diff --git a/.b4-config b/.b4-config new file mode 100644 index 00000000000..4b9b2fe290f --- /dev/null +++ b/.b4-config @@ -0,0 +1,14 @@ +# +# Common b4 settings that can be used to send patches to QEMU upstream. +# https://b4.docs.kernel.org/ +# + +[b4] + send-series-to = qemu-devel@nongnu.org + send-auto-to-cmd = echo + send-auto-cc-cmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback + am-perpatch-check-cmd = scripts/checkpatch.pl -q --terse --no-summary --mailback - + prep-perpatch-check-cmd = scripts/checkpatch.pl -q --terse --no-summary --mailback - + searchmask = https://lore.kernel.org/qemu-devel/?x=m&t=1&q=%s + linkmask = https://lore.kernel.org/qemu-devel/%s + linktrailermask = Message-ID: <%s> diff --git a/.editorconfig b/.editorconfig index 7303759ed7d..a04cb9054cb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -47,3 +47,16 @@ emacs_mode = glsl [*.json] indent_style = space emacs_mode = python + +# by default follow QEMU's style +[*.pl] +indent_style = space +indent_size = 4 +emacs_mode = perl + +# but user kernel "style" for imported scripts +[scripts/{kernel-doc,get_maintainer.pl,checkpatch.pl}] +indent_style = tab +indent_size = 8 +emacs_mode = perl + diff --git a/.gitattributes b/.gitattributes index a217cb7bfe9..9ce7a19581a 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 188a770799c..60a24a9d14d 100644 --- a/.gitlab-ci.d/base.yml +++ b/.gitlab-ci.d/base.yml @@ -24,6 +24,10 @@ 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 @@ -41,6 +45,10 @@ variables: - 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 @@ -61,14 +69,10 @@ variables: - 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_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 @@ -93,8 +97,8 @@ variables: 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_CI_UPSTREAM' + # Functional jobs can be manually started in forks + - if: '$QEMU_JOB_FUNCTIONAL && $QEMU_CI_FUNCTIONAL != "1" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM' when: manual allow_failure: true @@ -120,7 +124,7 @@ variables: when: manual # Jobs can run if any jobs they depend on were successful - - if: '$QEMU_JOB_SKIPPED && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/' + - 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 diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 4fbfeb66674..038c3c9540a 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -8,16 +8,23 @@ 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: - 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 - ccache --zero-stats + - section_start configure "Running configure" - ../configure --enable-werror --disable-docs --enable-fdt=system + --disable-debug-info ${TARGETS:+--target-list="$TARGETS"} $CONFIGURE_ARGS || { cat config.log meson-logs/meson-log.txt && exit 1; } @@ -25,11 +32,16 @@ then 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 @@ -44,22 +56,30 @@ exclude: - build/**/*.p - build/**/*.a.p - - build/**/*.fa.p - build/**/*.c.o - build/**/*.c.o.d - - build/**/*.fa .common_test_job_template: extends: .base_job_template stage: test image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG script: + - source scripts/ci/gitlab-ci-section + - section_start buildenv "Setting up to run tests" - scripts/git-submodule.sh update roms/SLOF - - meson subprojects download $(cd build/subprojects && echo *) + - 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" + # doctests need all the compilation artifacts + - $MAKE NINJA=":" MTESTARGS="--no-suite doc" $MAKE_CHECK_ARGS + - section_end test .native_test_job_template: extends: .common_test_job_template @@ -72,12 +92,12 @@ 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" @@ -86,21 +106,41 @@ 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: - - mkdir -p ~/.config/avocado - - echo "[datadir.paths]" > ~/.config/avocado/avocado.conf - - echo "cache_dirs = ['${CI_PROJECT_DIR}/avocado-cache']" - >> ~/.config/avocado/avocado.conf - - 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 ; - 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 + QEMU_JOB_FUNCTIONAL: 1 + +.wasm_build_job_template: + extends: .base_job_template + stage: build + image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG + before_script: + - source scripts/ci/gitlab-ci-section + - section_start setup "Pre-script setup" + - JOBS=$(expr $(nproc) + 1) + - section_end setup + script: + - du -sh .git + - mkdir build + - cd build + - section_start configure "Running configure" + - emconfigure ../configure --disable-docs + ${TARGETS:+--target-list="$TARGETS"} + $CONFIGURE_ARGS || + { cat config.log meson-logs/meson-log.txt && exit 1; } + - if test -n "$LD_JOBS"; + then + pyvenv/bin/meson configure . -Dbackend_max_links="$LD_JOBS" ; + fi || exit 1; + - section_end configure + - section_start build "Building QEMU" + - emmake make -j"$JOBS" + - section_end build diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index aee91015077..d888a600637 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -22,14 +22,14 @@ 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-functional build-system-ubuntu: extends: @@ -39,10 +39,9 @@ build-system-ubuntu: job: amd64-ubuntu2204-container variables: IMAGE: ubuntu2204 - CONFIGURE_ARGS: --enable-docs - TARGETS: alpha-softmmu cris-softmmu hppa-softmmu - microblazeel-softmmu mips64el-softmmu - MAKE_CHECK_ARGS: check-build + CONFIGURE_ARGS: --enable-docs --enable-rust + TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu + MAKE_CHECK_ARGS: check-build check-doc check-system-ubuntu: extends: .native_test_job_template @@ -53,14 +52,14 @@ check-system-ubuntu: 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: ubuntu2204 - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-functional build-system-debian: extends: @@ -69,10 +68,10 @@ build-system-debian: needs: job: amd64-debian-container variables: - IMAGE: debian-amd64 - CONFIGURE_ARGS: --with-coroutine=sigaltstack + IMAGE: debian + CONFIGURE_ARGS: --with-coroutine=sigaltstack --enable-rust TARGETS: arm-softmmu i386-softmmu riscv64-softmmu sh4eb-softmmu - sparc-softmmu xtensaeb-softmmu + sparc-softmmu xtensa-softmmu MAKE_CHECK_ARGS: check-build check-system-debian: @@ -81,17 +80,17 @@ 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-functional crash-test-debian: extends: .native_test_job_template @@ -99,7 +98,7 @@ crash-test-debian: - job: build-system-debian artifacts: true variables: - IMAGE: debian-amd64 + IMAGE: debian script: - cd build - make NINJA=":" check-venv @@ -113,10 +112,24 @@ build-system-fedora: job: amd64-fedora-container variables: IMAGE: fedora - CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs - 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 + MAKE_CHECK_ARGS: check-build check-doc + +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 check-doc + + allow_failure: true check-system-fedora: extends: .native_test_job_template @@ -127,14 +140,14 @@ 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-functional crash-test-fedora: extends: .native_test_job_template @@ -154,32 +167,107 @@ build-system-centos: - .native_build_job_template - .native_build_artifact_template needs: - job: amd64-centos8-container + job: amd64-centos9-container variables: - IMAGE: centos8 + IMAGE: centos9 CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-vfio-user-server --enable-modules --enable-trace-backends=dtrace --enable-docs TARGETS: ppc64-softmmu or1k-softmmu s390x-softmmu - x86_64-softmmu rx-softmmu sh4-softmmu nios2-softmmu + 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-previous/qemu-bundle + - build-previous/qemu-system-aarch64 + - build-previous/qemu-system-x86_64 + - build-previous/tests/qtest/migration-test + - build-previous/scripts + 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 + # Skip if this series contains the release bump commit. During the + # release process there might be a window of commits when the + # version tag is not yet present in the remote and git fetch would + # fail. + - if grep -q "\.0$" VERSION; then exit 0; fi + - 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: + # Skip for round release numbers, this job is only relevant for + # testing a development tree. + - if grep -q "\.0$" VERSION; then exit 0; fi + # Use the migration-tests from the older QEMU tree. This avoids + # testing an old QEMU against new features/tests that it is not + # compatible with. + - cd build-previous + # old to new + - QTEST_QEMU_BINARY_SRC=./qemu-system-${TARGET} + QTEST_QEMU_BINARY=../build/qemu-system-${TARGET} ./tests/qtest/migration-test + # new to old + - QTEST_QEMU_BINARY_DST=./qemu-system-${TARGET} + QTEST_QEMU_BINARY=../build/qemu-system-${TARGET} ./tests/qtest/migration-test + +# This job needs to be disabled until we can have an aarch64 CPU model that +# will both (1) support both KVM and TCG, and (2) provide a stable ABI. +# Currently only "-cpu max" can provide (1), however it doesn't guarantee +# (2). Mark this test skipped until later. +migration-compat-aarch64: + extends: .migration-compat-common + variables: + TARGET: aarch64 + QEMU_JOB_SKIPPED: 1 + +migration-compat-x86_64: + extends: .migration-compat-common + variables: + TARGET: x86_64 + check-system-centos: extends: .native_test_job_template needs: - 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-functional build-system-opensuse: extends: @@ -201,15 +289,44 @@ 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-functional +# +# 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-functional + QEMU_JOB_OPTIONAL: 1 + QEMU_TEST_FLAKY_TESTS: 1 # This jobs explicitly disable TCG (--disable-tcg), KVM is detected by # the configure script. The container doesn't contain Xen headers so @@ -221,9 +338,9 @@ 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 @@ -236,11 +353,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 @@ -249,6 +368,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: @@ -258,6 +378,18 @@ 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 build-user-hexagon: @@ -270,7 +402,9 @@ build-user-hexagon: 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: @@ -278,7 +412,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 @@ -298,8 +443,8 @@ 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 + 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 @@ -310,9 +455,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. @@ -322,8 +467,8 @@ clang-user: # Since slirp callbacks are used in QEMU Timers, we cannot use libslirp with # CFI builds, and thus have to disable it here. # -# Split in three sets of build/check/avocado to limit the execution time of each -# job +# Split in three sets of build/check/functional to limit the execution time +# of each job build-cfi-aarch64: extends: - .native_build_job_template @@ -353,14 +498,14 @@ 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-functional build-cfi-ppc64-s390x: extends: @@ -391,14 +536,14 @@ 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-functional build-cfi-x86_64: extends: @@ -425,14 +570,14 @@ 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-functional tsan-build: extends: .native_build_job_template @@ -443,6 +588,9 @@ tsan-build: 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 + # Remove when we switch to a distro with clang >= 18 + # https://github.com/google/sanitizers/issues/1716 + MAKE: setarch -R make # gcov is a GCC features gcov: @@ -484,12 +632,15 @@ build-oss-fuzz: - 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 @@ -498,10 +649,10 @@ 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 --disable-docs --disable-gtk --disable-vnc + - ../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" @@ -519,9 +670,9 @@ build-tci: build-without-defaults: extends: .native_build_job_template needs: - job: amd64-centos8-container + job: amd64-centos9-container variables: - IMAGE: centos8 + IMAGE: centos9 CONFIGURE_ARGS: --without-default-devices --without-default-features @@ -529,8 +680,7 @@ build-without-defaults: --disable-pie --disable-qom-cast-debug --disable-strip - TARGETS: avr-softmmu mips64-softmmu s390x-softmmu sh4-softmmu - sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user + --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: @@ -556,7 +706,7 @@ build-tools-and-docs-debian: # 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 @@ -576,7 +726,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:$QEMU_CI_CONTAINER_TAG + image: $CI_REGISTRY_IMAGE/qemu/debian:$QEMU_CI_CONTAINER_TAG stage: test needs: - job: build-tools-and-docs-debian @@ -584,7 +734,10 @@ 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 @@ -596,3 +749,49 @@ pages: - 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 + +build-wasm: + extends: .wasm_build_job_template + timeout: 2h + needs: + job: wasm-emsdk-cross-container + variables: + IMAGE: emsdk-wasm32-cross + CONFIGURE_ARGS: --static --disable-tools --enable-debug --enable-tcg-interpreter diff --git a/.gitlab-ci.d/check-dco.py b/.gitlab-ci.d/check-dco.py index 632c8bcce87..2fd56683dc6 100755 --- a/.gitlab-ci.d/check-dco.py +++ b/.gitlab-ci.d/check-dco.py @@ -19,10 +19,9 @@ 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", "--refetch", "check-dco", "master"]) ancestor = subprocess.check_output(["git", "merge-base", "check-dco/master", "HEAD"], @@ -79,7 +78,10 @@ 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 39e2b403c9e..be13e6f77d7 100755 --- a/.gitlab-ci.d/check-patch.py +++ b/.gitlab-ci.d/check-patch.py @@ -19,13 +19,12 @@ 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", "--refetch", "check-patch", "master"]) ancestor = subprocess.check_output(["git", "merge-base", "check-patch/master", "HEAD"], diff --git a/.gitlab-ci.d/check-units.py b/.gitlab-ci.d/check-units.py new file mode 100755 index 00000000000..268a4118d59 --- /dev/null +++ b/.gitlab-ci.d/check-units.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# +# check-units.py: check the number of compilation units and identify +# those that are rebuilt multiple times +# +# Copyright (C) 2025 Linaro Ltd. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from os import access, R_OK, path +from sys import argv, exit +import json +from collections import Counter + + +def extract_build_units(cc_path): + """ + Extract the build units and their counds from compile_commands.json file. + + Returns: + Hash table of ["unit"] = count + """ + + j = json.load(open(cc_path, 'r')) + files = [f['file'] for f in j] + build_units = Counter(files) + + return build_units + + +def analyse_units(build_units): + """ + Analyse the build units and report stats and the top 10 rebuilds + """ + + print(f"Total source files: {len(build_units.keys())}") + print(f"Total build units: {sum(units.values())}") + + # Create a sorted list by number of rebuilds + sorted_build_units = sorted(build_units.items(), + key=lambda item: item[1], + reverse=True) + + print("Most rebuilt units:") + for unit, count in sorted_build_units[:20]: + print(f" {unit} built {count} times") + + print("Least rebuilt units:") + for unit, count in sorted_build_units[-10:]: + print(f" {unit} built {count} times") + + +if __name__ == "__main__": + if len(argv) != 2: + script_name = path.basename(argv[0]) + print(f"Usage: {script_name} ") + exit(1) + + cc_path = argv[1] + if path.isfile(cc_path) and access(cc_path, R_OK): + units = extract_build_units(cc_path) + analyse_units(units) + exit(0) + else: + print(f"{cc_path} doesn't exist or isn't readable") + exit(1) diff --git a/.gitlab-ci.d/cirrus.yml b/.gitlab-ci.d/cirrus.yml index 41d64d66802..75b611418e7 100644 --- a/.gitlab-ci.d/cirrus.yml +++ b/.gitlab-ci.d/cirrus.yml @@ -13,98 +13,48 @@ .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: [] + allow_failure: + exit_codes: 3 + # 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: + - set -o allexport - source .gitlab-ci.d/cirrus/$NAME.vars - - 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|[@]CIRRUS_VM_INSTANCE_TYPE@|$CIRRUS_VM_INSTANCE_TYPE|g" - -e "s|[@]CIRRUS_VM_IMAGE_SELECTOR@|$CIRRUS_VM_IMAGE_SELECTOR|g" - -e "s|[@]CIRRUS_VM_IMAGE_NAME@|$CIRRUS_VM_IMAGE_NAME|g" - -e "s|[@]CIRRUS_VM_CPUS@|$CIRRUS_VM_CPUS|g" - -e "s|[@]CIRRUS_VM_RAM@|$CIRRUS_VM_RAM|g" - -e "s|[@]UPDATE_COMMAND@|$UPDATE_COMMAND|g" - -e "s|[@]INSTALL_COMMAND@|$INSTALL_COMMAND|g" - -e "s|[@]PATH@|$PATH_EXTRA${PATH_EXTRA:+:}\$PATH|g" - -e "s|[@]PKG_CONFIG_PATH@|$PKG_CONFIG_PATH|g" - -e "s|[@]PKGS@|$PKGS|g" - -e "s|[@]MAKE@|$MAKE|g" - -e "s|[@]PYTHON@|$PYTHON|g" - -e "s|[@]PIP3@|$PIP3|g" - -e "s|[@]PYPI_PKGS@|$PYPI_PKGS|g" - -e "s|[@]CONFIGURE_ARGS@|$CONFIGURE_ARGS|g" - -e "s|[@]TEST_TARGETS@|$TEST_TARGETS|g" - <.gitlab-ci.d/cirrus/build.yml >.gitlab-ci.d/cirrus/$NAME.yml + - set +o allexport + - cirrus-vars <.gitlab-ci.d/cirrus/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 -x64-freebsd-13-build: +x64-freebsd-14-build: extends: .cirrus_build_job variables: - NAME: freebsd-13 + NAME: freebsd-14 CIRRUS_VM_INSTANCE_TYPE: freebsd_instance CIRRUS_VM_IMAGE_SELECTOR: image_family - CIRRUS_VM_IMAGE_NAME: freebsd-13-2 + CIRRUS_VM_IMAGE_NAME: freebsd-14-2 CIRRUS_VM_CPUS: 8 CIRRUS_VM_RAM: 8G 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 -aarch64-macos-12-base-build: +aarch64-macos-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 a9444902ec8..41abd0b31ac 100644 --- a/.gitlab-ci.d/cirrus/build.yml +++ b/.gitlab-ci.d/cirrus/build.yml @@ -8,7 +8,7 @@ env: CI_REPOSITORY_URL: "@CI_REPOSITORY_URL@" CI_COMMIT_REF_NAME: "@CI_COMMIT_REF_NAME@" CI_COMMIT_SHA: "@CI_COMMIT_SHA@" - PATH: "@PATH@" + PATH: "@PATH_EXTRA@:$PATH" PKG_CONFIG_PATH: "@PKG_CONFIG_PATH@" PYTHON: "@PYTHON@" MAKE: "@MAKE@" @@ -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 diff --git a/.gitlab-ci.d/cirrus/freebsd-13.vars b/.gitlab-ci.d/cirrus/freebsd-13.vars deleted file mode 100644 index 3785afca36d..00000000000 --- 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 cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson mtools ncurses nettle ninja opencv pixman pkgconf png py39-numpy py39-pillow py39-pip py39-sphinx py39-sphinx_rtd_theme py39-tomli py39-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 xorriso zstd' -PYPI_PKGS='' -PYTHON='/usr/local/bin/python3' diff --git a/.gitlab-ci.d/cirrus/freebsd-14.vars b/.gitlab-ci.d/cirrus/freebsd-14.vars new file mode 100644 index 00000000000..19ca0d36638 --- /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 ccache4 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 vulkan-tools 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 a93881aa8b5..00000000000 --- 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 meson - 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 80eadaab296..00000000000 --- 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 mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy socat sparse spice-protocol tesseract usbredir vde vte3 xorriso zlib zstd' -PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli' -PYTHON='/opt/homebrew/bin/python3' diff --git a/.gitlab-ci.d/cirrus/macos-14.vars b/.gitlab-ci.d/cirrus/macos-14.vars new file mode 100644 index 00000000000..b039465f56f --- /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 vulkan-tools 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 08f8450fa16..5459447676c 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 e0d75d58246..8d3be53b75b 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,17 +10,17 @@ amd64-debian-user-cross-container: variables: NAME: debian-all-test-cross -arm64-debian-cross-container: +amd64-debian-legacy-cross-container: extends: .container_job_template stage: containers variables: - NAME: debian-arm64-cross + NAME: debian-legacy-test-cross -armel-debian-cross-container: +arm64-debian-cross-container: extends: .container_job_template stage: containers variables: - NAME: debian-armel-cross + NAME: debian-arm64-cross armhf-debian-cross-container: extends: .container_job_template @@ -40,23 +34,17 @@ hexagon-cross-container: variables: NAME: debian-hexagon-cross -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 @@ -64,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 @@ -91,36 +67,15 @@ ppc64el-debian-cross-container: riscv64-debian-cross-container: extends: .container_job_template stage: containers - # as we are currently based on 'sid/unstable' we may break so... - 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 - s390x-debian-cross-container: extends: .container_job_template stage: containers 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 @@ -132,22 +87,12 @@ 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: +win64-fedora-cross-container: extends: .container_job_template variables: - NAME: fedora-win32-cross + NAME: fedora-win64-cross -win64-fedora-cross-container: +wasm-emsdk-cross-container: extends: .container_job_template variables: - NAME: fedora-win64-cross + NAME: emsdk-wasm32-cross diff --git a/.gitlab-ci.d/containers.yml b/.gitlab-ci.d/containers.yml index 8637a13d860..db9b4d5e57f 100644 --- a/.gitlab-ci.d/containers.yml +++ b/.gitlab-ci.d/containers.yml @@ -11,7 +11,7 @@ amd64-debian-container: extends: .container_job_template stage: containers variables: - NAME: debian-amd64 + NAME: debian amd64-ubuntu2204-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 3e5f4d9cd8b..303943f818f 100644 --- a/.gitlab-ci.d/crossbuild-template.yml +++ b/.gitlab-ci.d/crossbuild-template.yml @@ -8,6 +8,12 @@ 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" @@ -16,18 +22,30 @@ - mkdir build - cd build - 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 cris-softmmu + --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. @@ -39,11 +57,14 @@ extends: .base_job_template stage: build image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG - timeout: 30m + 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" @@ -51,9 +72,19 @@ - export PATH="$CCACHE_WRAPPERSDIR:$PATH" - mkdir build - cd build + - 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 @@ -63,18 +94,31 @@ 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 + - 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 diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml index 84ff2f6d2bd..3f76c901ba8 100644 --- a/.gitlab-ci.d/crossbuilds.yml +++ b/.gitlab-ci.d/crossbuilds.yml @@ -1,13 +1,6 @@ include: - local: '/.gitlab-ci.d/crossbuild-template.yml' -cross-armel-user: - extends: .cross_user_build_job - needs: - job: armel-debian-cross-container - variables: - IMAGE: debian-armel-cross - cross-armhf-user: extends: .cross_user_build_job needs: @@ -37,28 +30,43 @@ cross-arm64-kvm-only: IMAGE: debian-arm64-cross EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-features -cross-i386-user: +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-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: +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 --disable-plugins - MAKE_CHECK_ARGS: check check-tcg + EXTRA_CONFIGURE_OPTS: --target-list=i386-softmmu,i386-linux-user,arm-softmmu,arm-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 @@ -110,12 +118,8 @@ cross-ppc64el-kvm-only: 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. cross-riscv64-system: extends: .cross_system_build_job - allow_failure: true needs: job: riscv64-debian-cross-container variables: @@ -123,7 +127,6 @@ cross-riscv64-system: cross-riscv64-user: extends: .cross_user_build_job - allow_failure: true needs: job: riscv64-debian-cross-container variables: @@ -159,29 +162,15 @@ 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 - EXTRA_CONFIGURE_OPTS: --enable-fdt=internal - CROSS_SKIP_TARGETS: alpha-softmmu avr-softmmu hppa-softmmu m68k-softmmu - microblazeel-softmmu mips64el-softmmu nios2-softmmu - artifacts: - when: on_success - paths: - - build/qemu-setup*.exe - cross-win64-system: extends: .cross_system_build_job needs: job: win64-fedora-cross-container variables: IMAGE: fedora-win64-cross - EXTRA_CONFIGURE_OPTS: --enable-fdt=internal + EXTRA_CONFIGURE_OPTS: --enable-fdt=internal --disable-plugins CROSS_SKIP_TARGETS: alpha-softmmu avr-softmmu hppa-softmmu - m68k-softmmu microblazeel-softmmu nios2-softmmu + m68k-softmmu microblazeel-softmmu or1k-softmmu rx-softmmu sh4eb-softmmu sparc64-softmmu tricore-softmmu xtensaeb-softmmu artifacts: diff --git a/.gitlab-ci.d/custom-runners.yml b/.gitlab-ci.d/custom-runners.yml index 8e5b9500f40..1aa3c60efe9 100644 --- a/.gitlab-ci.d/custom-runners.yml +++ b/.gitlab-ci.d/custom-runners.yml @@ -10,13 +10,14 @@ # 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 @@ -28,7 +29,6 @@ variables: 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 367424db781..00000000000 --- a/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml +++ /dev/null @@ -1,24 +0,0 @@ -# All centos-stream-8 jobs should run successfully in an environment -# setup by the scripts/ci/setup/stream/8/build-environment.yml task -# "Installation of extra packages to build QEMU" - -centos-stream-8-x86_64: - extends: .custom_runner_template - allow_failure: true - needs: [] - stage: build - 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" - 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 check-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 cdae6c52121..00000000000 --- a/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml +++ /dev/null @@ -1,130 +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: - extends: .custom_runner_template - 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 check-tcg - - make --output-sync -j`nproc` check - -ubuntu-20.04-s390x-all: - extends: .custom_runner_template - needs: [] - stage: build - tags: - - ubuntu_20.04 - - s390x - 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 - -ubuntu-20.04-s390x-alldbg: - extends: .custom_runner_template - 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 - -ubuntu-20.04-s390x-clang: - extends: .custom_runner_template - 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 - -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: - extends: .custom_runner_template - 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 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 b8a0d75162b..8727687e2b4 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml @@ -1,5 +1,5 @@ # All ubuntu-22.04 jobs should run successfully in an environment -# setup by the scripts/ci/setup/qemu/build-environment.yml task +# 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: 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 374b0956c3c..ca2f1404710 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml @@ -1,5 +1,5 @@ # All ubuntu-22.04 jobs should run successfully in an environment -# setup by the scripts/ci/setup/qemu/build-environment.yml task +# 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: @@ -103,7 +103,7 @@ ubuntu-22.04-aarch64-clang: script: - mkdir build - cd build - - ../configure --disable-libssh --cc=clang --cxx=clang++ --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 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 00000000000..ca374acb8c9 --- /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/opensbi.yml b/.gitlab-ci.d/opensbi.yml index fd293e6c317..42f137d624e 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 diff --git a/.gitlab-ci.d/static_checks.yml b/.gitlab-ci.d/static_checks.yml index ad9f426a52f..c3ed6de453d 100644 --- a/.gitlab-ci.d/static_checks.yml +++ b/.gitlab-ci.d/static_checks.yml @@ -46,3 +46,49 @@ check-python-tox: QEMU_JOB_OPTIONAL: 1 needs: job: python-container + +check-rust-tools-nightly: + extends: .base_job_template + stage: test + image: $CI_REGISTRY_IMAGE/qemu/fedora-rust-nightly:$QEMU_CI_CONTAINER_TAG + script: + - source scripts/ci/gitlab-ci-section + - section_start test "Running Rust code checks" + - cd build + - pyvenv/bin/meson devenv -w ../rust ${CARGO-cargo} fmt --check + - make clippy + - make rustdoc + - section_end test + variables: + GIT_DEPTH: 1 + allow_failure: true + needs: + - job: build-system-fedora-rust-nightly + artifacts: true + artifacts: + when: on_success + expire_in: 2 days + paths: + - rust/target/doc + +check-build-units: + extends: .base_job_template + stage: build + image: $CI_REGISTRY_IMAGE/qemu/debian:$QEMU_CI_CONTAINER_TAG + needs: + job: amd64-debian-container + before_script: + - source scripts/ci/gitlab-ci-section + - section_start setup "Install Tools" + - apt install --assume-yes --no-install-recommends jq + - section_end setup + script: + - mkdir build + - cd build + - section_start configure "Running configure" + - ../configure + - cd .. + - section_end configure + - section_start analyse "Analyse" + - .gitlab-ci.d/check-units.py build/compile_commands.json + - section_end analyse diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index 12a987cd719..45ed0c96fea 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -1,9 +1,7 @@ -.shared_msys2_builder: +msys2-64bit: extends: .base_job_template tags: - - shared-windows - - windows - - windows-1809 + - saas-windows-medium-amd64 cache: key: "$CI_JOB_NAME" paths: @@ -14,9 +12,14 @@ stage: build 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 @@ -72,35 +75,20 @@ - .\msys64\usr\bin\bash -lc "pacman -Sy --noconfirm --needed bison diffutils flex git grep make sed - $MINGW_TARGET-capstone - $MINGW_TARGET-ccache - $MINGW_TARGET-curl - $MINGW_TARGET-cyrus-sasl - $MINGW_TARGET-dtc - $MINGW_TARGET-gcc - $MINGW_TARGET-glib2 - $MINGW_TARGET-gnutls - $MINGW_TARGET-gtk3 - $MINGW_TARGET-libgcrypt - $MINGW_TARGET-libjpeg-turbo - $MINGW_TARGET-libnfs - $MINGW_TARGET-libpng - $MINGW_TARGET-libssh - $MINGW_TARGET-libtasn1 - $MINGW_TARGET-libusb - $MINGW_TARGET-lzo2 - $MINGW_TARGET-nettle - $MINGW_TARGET-ninja - $MINGW_TARGET-pixman - $MINGW_TARGET-pkgconf - $MINGW_TARGET-python - $MINGW_TARGET-SDL2 - $MINGW_TARGET-SDL2_image - $MINGW_TARGET-snappy - $MINGW_TARGET-spice - $MINGW_TARGET-usbredir - $MINGW_TARGET-zstd " + mingw-w64-x86_64-binutils + mingw-w64-x86_64-ccache + mingw-w64-x86_64-curl + mingw-w64-x86_64-gcc + mingw-w64-x86_64-glib2 + mingw-w64-x86_64-libnfs + mingw-w64-x86_64-libssh + mingw-w64-x86_64-ninja + mingw-w64-x86_64-pixman + mingw-w64-x86_64-pkgconf + mingw-w64-x86_64-python + 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:MSYS = 'winsymlinks:native' # Enable native Windows symlink - $env:CCACHE_BASEDIR = "$env:CI_PROJECT_DIR" @@ -111,30 +99,8 @@ - mkdir build - cd build - ..\msys64\usr\bin\bash -lc "ccache --zero-stats" - - ..\msys64\usr\bin\bash -lc "../configure --enable-fdt=system $CONFIGURE_ARGS" - - ..\msys64\usr\bin\bash -lc "make" + - ..\msys64\usr\bin\bash -lc "../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)" - -msys2-64bit: - extends: .shared_msys2_builder - variables: - MINGW_TARGET: mingw-w64-x86_64 - MSYSTEM: MINGW64 - # do not remove "--without-default-devices"! - # commit 9f8e6cad65a6 ("gitlab-ci: Speed up the msys2-64bit job by using --without-default-devices" - # changed to compile QEMU with the --without-default-devices switch - # for the msys2 64-bit job, due to the build could not complete within - CONFIGURE_ARGS: --target-list=x86_64-softmmu --without-default-devices -Ddebug=false -Doptimization=0 - # qTests don't run successfully with "--without-default-devices", - # so let's exclude the qtests from CI for now. - TEST_ARGS: --no-suite qtest - -msys2-32bit: - extends: .shared_msys2_builder - variables: - MINGW_TARGET: mingw-w64-i686 - MSYSTEM: MINGW32 - CONFIGURE_ARGS: --target-list=ppc64-softmmu -Ddebug=false -Doptimization=0 - TEST_ARGS: --no-suite qtest diff --git a/.mailmap b/.mailmap index 64ef9f4de6f..e7271852dc6 100644 --- a/.mailmap +++ b/.mailmap @@ -30,24 +30,45 @@ 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. +Akihiko Odaki +Akihiko Odaki Aleksandar Markovic Aleksandar Markovic Aleksandar Markovic @@ -56,6 +77,8 @@ Aleksandar Rikalo Alexander Graf Ani Sinha Anthony Liguori Anthony Liguori +Brian Cain +Brian Cain Christian Borntraeger Damien Hedde Filip Bozuta @@ -65,8 +88,13 @@ Greg Kurz Huacai Chen Huacai Chen James Hogan -Leif Lindholm -Leif Lindholm +Juan Quintela +Leif Lindholm +Leif Lindholm +Leif Lindholm +Luc Michel +Luc Michel +Luc Michel Radoslaw Biernacki Paul Brook Paul Burton @@ -77,7 +105,9 @@ Philippe Mathieu-Daudé Philippe Mathieu-Daudé Philippe Mathieu-Daudé Roman Bolshakov +Sriram Yagnaraman Stefan Brankovic +Stefan Weil Stefan Weil Taylor Simpson Yongbok Kim diff --git a/.readthedocs.yml b/.readthedocs.yml index 7fb7b8dd61a..0b262469ce6 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 b958eca5de1..0a634d7b4a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ os: linux -dist: focal +dist: jammy language: c compiler: - gcc @@ -7,13 +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 # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu @@ -34,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 @@ -81,45 +79,9 @@ after_script: jobs: include: - - name: "[aarch64] GCC check-tcg" - arch: arm64 - dist: focal - addons: - apt_packages: - - libaio-dev - - libattr1-dev - - libbrlapi-dev - - libcacard-dev - - libcap-ng-dev - - libfdt-dev - - libgcrypt20-dev - - libgnutls28-dev - - libgtk-3-dev - - libiscsi-dev - - liblttng-ust-dev - - libncurses5-dev - - libnfs-dev - - libpixman-1-dev - - libpng-dev - - librados-dev - - libsdl2-dev - - libseccomp-dev - - liburcu-dev - - libusb-1.0-0-dev - - libvdeplug-dev - - libvte-2.91-dev - - ninja-build - # Tests dependencies - - genisoimage - env: - - TEST_CMD="make check check-tcg V=1" - - CONFIG="--disable-containers --enable-fdt=system - --target-list=${MAIN_SOFTMMU_TARGETS} --cxx=/bin/false" - - UNRELIABLE=true - - - name: "[ppc64] GCC check-tcg" + - name: "[ppc64] Clang check-tcg" arch: ppc64le - dist: focal + compiler: clang addons: apt_packages: - libaio-dev @@ -145,6 +107,7 @@ jobs: - libvdeplug-dev - libvte-2.91-dev - ninja-build + - python3-tomli # Tests dependencies - genisoimage env: @@ -154,7 +117,6 @@ jobs: - name: "[s390x] GCC check-tcg" arch: s390x - dist: focal addons: apt_packages: - libaio-dev @@ -180,13 +142,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 --enable-fdt=system - --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=$? - | @@ -197,9 +159,9 @@ 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 @@ -220,17 +182,16 @@ jobs: - libsnappy-dev - libzstd-dev - nettle-dev - - xfslibs-dev - ninja-build + - python3-tomli # Tests dependencies - genisoimage env: - - CONFIG="--disable-containers --enable-fdt=system --audio-drv-list=sdl - --disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}" + - CONFIG="--disable-containers --audio-drv-list=sdl --disable-user + --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 @@ -239,13 +200,14 @@ jobs: - 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-10 + compiler: clang addons: apt_packages: - libaio-dev @@ -271,9 +233,8 @@ jobs: - libvdeplug-dev - libvte-2.91-dev - ninja-build - - clang-10 + - python3-tomli env: - TEST_CMD="make check-unit" - CONFIG="--disable-containers --disable-tcg --enable-kvm --disable-tools --enable-fdt=system --host-cc=clang --cxx=clang++" - - UNRELIABLE=true diff --git a/Kconfig b/Kconfig index fb6a24a2de8..63ca7f46df7 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 d763d892693..933425c74b4 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,12 @@ config FUZZ config VFIO_USER_SERVER_ALLOWED bool imply VFIO_USER_SERVER + +config HV_BALLOON_POSSIBLE + bool + +config HAVE_RUST + bool + +config MAC_PVG + bool diff --git a/MAINTAINERS b/MAINTAINERS index 00562f924f7..1b73b8b394a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -70,14 +70,16 @@ R: Daniel P. Berrangé R: Thomas Huth R: Markus Armbruster R: Philippe Mathieu-Daudé -R: Juan Quintela W: https://www.qemu.org/docs/master/devel/index.html S: Odd Fixes -F: docs/devel/style.rst +F: docs/devel/build-environment.rst F: docs/devel/code-of-conduct.rst +F: docs/devel/codebase.rst F: docs/devel/conflict-resolution.rst +F: docs/devel/style.rst F: docs/devel/submitting-a-patch.rst F: docs/devel/submitting-a-pull-request.rst +F: docs/glossary.rst Responsible Disclosure, Reporting Security Issues ------------------------------------------------- @@ -110,6 +112,7 @@ F: hw/intc/s390_flic.c F: hw/intc/s390_flic_kvm.c F: hw/s390x/ F: hw/vfio/ap.c +F: hw/s390x/ap-stub.c F: hw/vfio/ccw.c F: hw/watchdog/wdt_diag288.c F: include/hw/s390x/ @@ -119,7 +122,7 @@ F: pc-bios/s390-ccw.img F: target/s390x/ F: docs/system/target-s390x.rst F: docs/system/s390x/ -F: tests/migration/s390x/ +F: tests/qtest/migration/s390x/ K: ^Subject:.*(?i)s390x? L: qemu-s390x@nongnu.org @@ -131,16 +134,27 @@ 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) --------------------- Overall TCG CPUs M: Richard Henderson R: Paolo Bonzini S: Maintained -F: softmmu/cpus.c -F: softmmu/watchpoint.c -F: cpus-common.c -F: page-vary.c +F: system/watchpoint.c +F: page-vary-target.c F: page-vary-common.c F: accel/tcg/ F: accel/stubs/tcg-stub.c @@ -149,19 +163,17 @@ F: util/cacheflush.c F: scripts/decodetree.py F: docs/devel/decodetree.rst F: docs/devel/tcg* -F: include/exec/cpu*.h -F: include/exec/exec-all.h F: include/exec/tb-flush.h -F: include/exec/target_long.h F: include/exec/helper*.h F: include/exec/helper*.h.inc F: include/exec/helper-info.c.inc -F: include/sysemu/cpus.h -F: include/sysemu/tcg.h -F: include/hw/core/tcg-cpu-ops.h +F: include/exec/page-protection.h +F: include/system/tcg.h +F: include/accel/tcg/ F: host/include/*/host/cpuinfo.h F: util/cpuinfo-*.c F: include/tcg/ +F: tests/decode/ FPU emulation M: Aurelien Jarno @@ -200,7 +212,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/smmu* F: include/hw/arm/smmu* -F: tests/avocado/smmu.py +F: tests/functional/test_aarch64_smmu.py AVR TCG CPUs M: Michael Rolnik @@ -208,19 +220,10 @@ 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_*.py Hexagon TCG CPUs -M: Brian Cain +M: Brian Cain S: Supported F: target/hexagon/ X: target/hexagon/idef-parser/ @@ -231,6 +234,7 @@ F: disas/hexagon.c F: configs/targets/hexagon-linux-user/default.mak F: docker/dockerfiles/debian-hexagon-cross.docker F: gdb-xml/hexagon*.xml +T: git https://github.com/quic/qemu.git hex-next Hexagon idef-parser M: Alessandro Di Federico @@ -241,23 +245,25 @@ F: target/hexagon/gen_idef_parser_funcs.py HPPA (PA-RISC) TCG CPUs M: Richard Henderson +M: Helge Deller S: Maintained F: target/hppa/ F: disas/hppa.c +F: tests/tcg/hppa/ LoongArch TCG CPUs M: Song Gao -M: Xiaojuan Yang S: Maintained F: target/loongarch/ F: tests/tcg/loongarch64/ -F: tests/avocado/machine_loongarch.py +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 @@ -271,59 +277,61 @@ 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 -R: Chris Wulff -R: Marek Vasut -S: Orphan -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 -F: tests/tcg/nios2/ - OpenRISC TCG CPUs M: Stafford Horne S: Odd Fixes F: docs/system/openrisc/cpu-features.rst F: target/openrisc/ F: hw/openrisc/ +F: include/hw/openrisc/ F: tests/tcg/openrisc/ PowerPC TCG CPUs +M: Nicholas Piggin M: Daniel Henrique Barboza -R: Cédric Le Goater -R: David Gibson -R: Greg Kurz -R: Nicholas Piggin L: qemu-ppc@nongnu.org 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 -R: Weiwei Li +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: docs/specs/riscv-iommu.rst F: target/riscv/ +F: hw/char/riscv_htif.c F: hw/riscv/ +F: hw/intc/riscv* +F: include/hw/char/riscv_htif.h F: include/hw/riscv/ F: linux-user/host/riscv32/ F: linux-user/host/riscv64/ +F: common-user/host/riscv* +F: tests/functional/test_riscv* +F: tests/tcg/riscv64/ RISC-V XThead* extensions M: Christoph Muellner @@ -332,6 +340,8 @@ 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 @@ -339,6 +349,7 @@ L: qemu-riscv@nongnu.org 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 @@ -363,6 +374,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 @@ -373,6 +385,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 @@ -422,7 +435,7 @@ F: */*/kvm* F: accel/kvm/ F: accel/stubs/kvm-stub.c F: include/hw/kvm/ -F: include/sysemu/kvm*.h +F: include/system/kvm*.h F: scripts/kvm/kvm_flightrecorder ARM KVM CPUs @@ -435,13 +448,11 @@ MIPS KVM CPUs M: Huacai Chen S: Odd Fixes F: target/mips/kvm* -F: target/mips/sysemu/ +F: target/mips/system/ PPC KVM CPUs -M: Daniel Henrique Barboza -R: Cédric Le Goater -R: David Gibson -R: Greg Kurz +M: Nicholas Piggin +R: Daniel Henrique Barboza S: Odd Fixes F: target/ppc/kvm.c @@ -466,15 +477,16 @@ F: docs/system/i386/sgx.rst F: target/i386/kvm/ F: target/i386/sev* F: scripts/kvm/vmxcap +F: tests/functional/test_x86_64_hotplug_cpu.py Xen emulation on X86 KVM CPUs M: David Woodhouse M: Paul Durrant S: Supported -F: include/sysemu/kvm_xen.h +F: include/system/kvm_xen.h F: target/i386/kvm/xen* F: hw/i386/kvm/xen* -F: tests/avocado/xen_guest.py +F: tests/functional/test_x86_64_kvm_xen.py Guest CPU Cores (other accelerators) ------------------------------------ @@ -482,21 +494,29 @@ Overall M: Richard Henderson R: Paolo Bonzini S: Maintained +F: include/exec/cpu*.h +F: include/exec/target_long.h F: include/qemu/accel.h -F: include/sysemu/accel-*.h -F: include/hw/core/accel-cpu.h -F: accel/accel-*.c +F: include/system/accel-*.h +F: include/system/cpus.h +F: include/accel/accel-cpu*.h +F: accel/accel-*.? F: accel/Makefile.objs F: accel/stubs/Makefile.objs +F: cpu-common.c +F: cpu-target.c +F: system/cpus.c Apple Silicon HVF CPUs M: Alexander Graf S: Maintained F: target/arm/hvf/ +F: target/arm/hvf-stub.c X86 HVF CPUs M: Cameron Esfahani M: Roman Bolshakov +R: Phil Dennis-Jordan W: https://wiki.qemu.org/Features/HVF S: Maintained F: target/i386/hvf/ @@ -504,24 +524,35 @@ F: target/i386/hvf/ HVF M: Cameron Esfahani M: Roman Bolshakov +R: Phil Dennis-Jordan W: https://wiki.qemu.org/Features/HVF S: Maintained F: accel/hvf/ -F: include/sysemu/hvf.h -F: include/sysemu/hvf_int.h +F: accel/stubs/hvf-stub.c +F: include/system/hvf.h +F: include/system/hvf_int.h WHPX CPUs M: Sunil Muthuswamy S: Supported F: target/i386/whpx/ -F: include/sysemu/whpx.h +F: include/system/whpx.h + +X86 Instruction Emulator +M: Cameron Esfahani +M: Roman Bolshakov +R: Phil Dennis-Jordan +R: Wei Liu +S: Maintained +F: target/i386/emulate/ 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* @@ -539,16 +570,18 @@ F: hw/i386/xen/ F: hw/pci-host/xen_igd_pt.c F: include/hw/block/dataplane/xen* F: include/hw/xen/ -F: include/sysemu/xen.h -F: include/sysemu/xen-mapcache.h +F: include/system/xen.h +F: include/system/xen-mapcache.h F: stubs/xen-hw-stub.c +F: docs/system/arm/xenpvh.rst +F: docs/system/i386/xenpvh.rst Guest CPU Cores (NVMM) ---------------------- NetBSD Virtual Machine Monitor (NVMM) CPU support M: Reinoud Zandijk S: Maintained -F: include/sysemu/nvmm.h +F: include/system/nvmm.h F: target/i386/nvmm/ Hosts @@ -559,13 +592,14 @@ M: Cornelia Huck M: Paolo Bonzini S: Maintained F: linux-headers/ +F: include/standard-headers/ F: scripts/update-linux-headers.sh POSIX M: Paolo Bonzini S: Maintained F: os-posix.c -F: include/sysemu/os-posix.h +F: include/system/os-posix.h F: util/*posix*.c F: include/qemu/*posix*.h @@ -597,6 +631,15 @@ F: .gitlab-ci.d/cirrus/macos-* F: */*.m F: scripts/entitlement.sh +WebAssembly +M: Kohei Tokunaga +S: Maintained +F: include/system/os-wasm.h +F: os-wasm.c +F: util/coroutine-wasm.c +F: configs/meson/emscripten.txt +F: tests/docker/dockerfiles/emsdk-wasm32-cross.docker + Alpha Machines -------------- M: Richard Henderson @@ -604,6 +647,7 @@ S: Maintained F: hw/alpha/ F: hw/isa/smc37c669-superio.c F: tests/tcg/alpha/system/ +F: tests/functional/test_alpha_clipper.py ARM Machines ------------ @@ -614,10 +658,12 @@ 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 +F: tests/functional/test_arm_cubieboard.py Allwinner-h3 M: Niek Linnenbank @@ -627,6 +673,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 @@ -640,6 +687,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 @@ -670,7 +718,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 @@ -687,6 +735,24 @@ F: include/hw/timer/armv7m_systick.h F: include/hw/misc/armv7m_ras.h F: tests/qtest/test-arm-mptimer.c +Bananapi M2U +M: Peter Maydell +L: qemu-arm@nongnu.org +S: Odd Fixes +F: docs/system/arm/bananapi_m2u.rst +F: hw/*/allwinner-r40*.c +F: hw/arm/bananapi_m2u.c +F: include/hw/*/allwinner-r40*.h +F: tests/functional/test_arm_bpim2u.py + +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 @@ -694,6 +760,8 @@ L: qemu-arm@nongnu.org S: Odd Fixes F: hw/*/exynos* F: include/hw/*/exynos* +F: docs/system/arm/exynos.rst +F: tests/functional/test_arm_smdkc210.py Calxeda Highbank M: Rob Herring @@ -712,7 +780,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 @@ -723,14 +791,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 @@ -759,11 +819,11 @@ F: docs/system/arm/kzm.rst Integrator CP M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes 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 @@ -776,6 +836,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 @@ -789,13 +850,31 @@ 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 +MCIMX8MP-EVK / i.MX8MP +M: Bernhard Beschow +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/imx8mp-evk.c +F: hw/arm/fsl-imx8mp.c +F: hw/misc/imx8mp_*.c +F: hw/pci-host/fsl_imx8m_phy.c +F: hw/rtc/rs5c372.c +F: include/hw/arm/fsl-imx8mp.h +F: include/hw/misc/imx8mp_*.h +F: include/hw/pci-host/fsl_imx8m_phy.h +F: docs/system/arm/imx8mp-evk.rst +F: tests/functional/test_aarch64_imx8mp_evk.py +F: tests/qtest/rs5c372-test.c + +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 @@ -822,7 +901,7 @@ F: docs/system/arm/mps2.rst Musca M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/musca.c F: docs/system/arm/musca.rst @@ -842,37 +921,15 @@ M: Hao Wu L: qemu-arm@nongnu.org S: Supported 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: pc-bios/npcm8xx_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 +F: tests/functional/test_arm_quanta_gsj.py Raspberry Pi M: Peter Maydell @@ -882,40 +939,23 @@ 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 L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/realview* F: hw/cpu/realview_mpcore.c F: hw/intc/realview_gic.c F: include/hw/intc/realview_gic.h F: docs/system/arm/realview.rst - -PXA2XX -M: 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 +F: tests/functional/test_arm_realview.py SABRELITE / i.MX6 M: Peter Maydell @@ -936,13 +976,15 @@ F: include/hw/ssi/imx_spi.h SBSA-REF M: Radoslaw Biernacki M: Peter Maydell -R: Leif Lindholm -R: Marcin Juszkiewicz +R: Leif Lindholm L: qemu-arm@nongnu.org S: Maintained F: hw/arm/sbsa-ref.c +F: hw/misc/sbsa_ec.c +F: hw/watchdog/sbsa_gwdt.c +F: include/hw/watchdog/sbsa_gwdt.h F: docs/system/arm/sbsa.rst -F: tests/avocado/machine_aarch64_sbsaref.py +F: tests/functional/test_aarch64_*sbsaref*.py Sharp SL-5500 (Collie) PDA M: Peter Maydell @@ -950,15 +992,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 +S: Odd Fixes 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 +F: tests/functional/test_arm_stellaris.py + +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 @@ -970,14 +1031,16 @@ F: docs/system/arm/stm32.rst Versatile Express M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes 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 +S: Odd Fixes F: hw/*/versatile* F: hw/i2c/arm_sbcon_i2c.c F: include/hw/i2c/arm_sbcon_i2c.h @@ -991,7 +1054,10 @@ 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 +F: tests/functional/test_arm_virt.py Xilinx Zynq M: Edgar E. Iglesias @@ -1006,6 +1072,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 @@ -1019,9 +1086,11 @@ 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 +F: tests/functional/test_aarch64_xlnx_versal.py 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 @@ -1063,6 +1132,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 @@ -1107,23 +1178,28 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/msf2-som.c F: docs/system/arm/emcraft-sf2.rst +F: tests/functional/test_arm_emcraft_sf2.py 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: docs/system/arm/fby35.rst F: tests/*/*aspeed* +F: tests/*/*ast2700* F: hw/arm/fby35.c +F: pc-bios/ast27x0_bootrom.bin NRF51 M: Joel Stanley @@ -1135,8 +1211,14 @@ F: hw/*/microbit*.c F: include/hw/*/nrf51*.h F: include/hw/*/microbit*.h F: tests/qtest/microbit-test.c +F: tests/functional/test_arm_microbit.py F: docs/system/arm/nrf.rst +ARM PL011 Rust device +M: Manos Pitsidianakis +S: Maintained +F: rust/hw/char/pl011/ + AVR Machines ------------- @@ -1156,36 +1238,38 @@ Arduino 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 +F: tests/functional/test_avr_uno.py HP-PARISC Machines ------------------ -HP B160L +HP B160L, HP C3700 M: Richard Henderson -R: Helge Deller -S: Odd Fixes +M: Helge Deller +S: Maintained F: configs/devices/hppa-softmmu/default.mak +F: hw/char/diva-gsp.c +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/ +F: tests/functional/test_hppa_seabios.py 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 @@ -1193,7 +1277,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 @@ -1214,6 +1300,7 @@ F: hw/m68k/mcf_intc.c F: hw/char/mcf_uart.c F: hw/net/mcf_fec.c F: include/hw/m68k/mcf*.h +F: tests/functional/test_m68k_mcf5208evb.py NeXTcube M: Thomas Huth @@ -1221,6 +1308,7 @@ 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 @@ -1231,6 +1319,9 @@ 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 @@ -1240,6 +1331,10 @@ 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 @@ -1253,6 +1348,8 @@ 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 +F: tests/functional/test_m68k_tuxrun.py MicroBlaze Machines ------------------- @@ -1261,7 +1358,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 @@ -1279,26 +1376,29 @@ 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/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 @@ -1309,26 +1409,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: include/hw/pci-host/bonito.h -F: tests/avocado/machine_mips_fuloong2e.py +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 @@ -1341,25 +1443,25 @@ 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 +F: tests/functional/test_or1k_sim.py PowerPC Machines ---------------- -405 (ref405ep) -L: qemu-ppc@nongnu.org -S: Orphan -F: hw/ppc/ppc405_boards.c - 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/ @@ -1367,15 +1469,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 @@ -1388,6 +1494,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 @@ -1396,6 +1503,8 @@ 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 +F: tests/functional/test_ppc64_mac99.py Old World (g3beige) M: Mark Cave-Ayland @@ -1411,6 +1520,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 @@ -1427,13 +1537,11 @@ 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: Odd Fixes @@ -1441,6 +1549,10 @@ 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-* @@ -1448,12 +1560,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 -R: Nicholas Piggin L: qemu-ppc@nongnu.org S: Odd Fixes F: docs/system/ppc/powernv.rst @@ -1461,30 +1574,45 @@ 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: pc-bios/pnv-pnor.bin 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/dtb/canyonlands.dt[sb] F: pc-bios/u-boot-sam460ex-20100605.bin F: roms/u-boot-sam460ex +F: docs/system/ppc/amigang.rst +F: tests/functional/test_ppc_sam460ex.py pegasos2 M: BALATON Zoltan @@ -1495,14 +1623,19 @@ F: hw/pci-host/mv64361.c F: hw/pci-host/mv643xx.h F: include/hw/pci-host/mv64361.h +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 -R: Cédric Le Goater -R: Daniel Henrique Barboza -R: David Gibson -R: Greg Kurz L: qemu-ppc@nongnu.org -S: Maintained +S: Odd Fixes F: hw/ppc/spapr_vof* F: hw/ppc/vof* F: include/hw/ppc/vof* @@ -1521,9 +1654,9 @@ F: include/hw/riscv/opentitan.h F: include/hw/*/ibex_*.h Microchip PolarFire SoC Icicle Kit -M: Bin Meng L: qemu-riscv@nongnu.org S: Supported +F: docs/system/riscv/microchip-icicle-kit.rst F: hw/riscv/microchip_pfsoc.c F: hw/char/mchp_pfsoc_mmuart.c F: hw/misc/mchp_pfsoc_dmc.c @@ -1539,6 +1672,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 @@ -1546,13 +1680,19 @@ F: include/hw/char/shakti_uart.h SiFive Machines M: Alistair Francis -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 +AMD Microblaze-V Generic Board +M: Sai Pavan Boddu +S: Maintained +F: hw/riscv/microblaze-v-generic.c +F: docs/system/riscv/microblaze-v-generic.rst + RX Machines ----------- rx-gdbsim @@ -1560,7 +1700,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 ------------ @@ -1574,17 +1714,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 -------------- @@ -1602,6 +1734,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 @@ -1614,7 +1747,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 @@ -1624,13 +1758,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 ------------- @@ -1642,7 +1775,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 @@ -1697,6 +1830,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 @@ -1711,7 +1854,7 @@ 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 @@ -1721,8 +1864,14 @@ F: include/hw/southbridge/ich9.h F: include/hw/southbridge/piix.h 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_linux_initrd.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 @@ -1751,6 +1900,7 @@ 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 @@ -1766,14 +1916,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-system.c F: hw/core/machine-qmp-cmds.c F: hw/core/machine.c F: hw/core/machine-smp.c @@ -1781,14 +1942,23 @@ F: hw/core/null-machine.c F: hw/core/numa.c F: hw/cpu/cluster.c F: qapi/machine.json -F: qapi/machine-target.json +F: qapi/machine-common.json F: include/hw/boards.h F: include/hw/core/cpu.h F: include/hw/cpu/cluster.h -F: include/sysemu/numa.h +F: include/system/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 +TargetInfo API +M: Pierrick Bouvier +M: Philippe Mathieu-Daudé +S: Supported +F: include/qemu/target-info*.h +F: target-info*.c + Xtensa Machines --------------- sim @@ -1806,6 +1976,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 ------- @@ -1820,8 +1992,8 @@ F: tests/qtest/intel-hda-test.c F: tests/qtest/fuzz-sb16-test.c Xilinx CAN -M: Vikram Garhwal M: Francisco Iglesias +M: Vikram Garhwal S: Maintained F: hw/net/can/xlnx-* F: include/hw/net/xlnx-* @@ -1831,12 +2003,12 @@ 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: Odd Fixes -F: include/hw/ide.h F: include/hw/ide/ F: hw/ide/ F: hw/block/block.c @@ -1878,10 +2050,11 @@ F: include/hw/hyperv/vmbus*.h OMAP M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/*/omap* F: include/hw/arm/omap.h F: docs/system/arm/sx1.rst +F: tests/functional/test_arm_sx1.py IPack M: Alberto Garcia @@ -1900,6 +2073,7 @@ F: hw/pci-bridge/* F: qapi/pci.json F: docs/pci* F: docs/specs/*pci* +F: docs/system/sriov.rst PCIE DOE M: Huai-Cheng Kuo @@ -1947,13 +2121,13 @@ S: Supported F: hw/acpi/viot.c F: hw/acpi/viot.h -ACPI/AVOCADO/BIOSBITS +ACPI/FUNCTIONAL/BIOSBITS M: Ani Sinha M: Michael S. Tsirkin S: Supported -F: tests/avocado/acpi-bits/* -F: tests/avocado/acpi-bits.py -F: docs/devel/acpi-bits.rst +F: tests/functional/acpi-bits/* +F: tests/functional/test_acpi_bits.py +F: docs/devel/testing/acpi-bits.rst ACPI/HEST/GHES R: Dongjiu Geng @@ -1966,8 +2140,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 @@ -1978,6 +2155,7 @@ M: Marc-André Lureau R: Paolo Bonzini S: Odd Fixes F: hw/char/ +F: include/hw/char/ Network devices M: Jason Wang @@ -1985,6 +2163,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 @@ -2023,7 +2202,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* @@ -2034,8 +2213,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 @@ -2044,7 +2222,6 @@ 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 @@ -2054,9 +2231,18 @@ M: Alex Williamson M: Cédric Le Goater S: Supported F: hw/vfio/* +F: util/vfio-helpers.c F: include/hw/vfio/ +F: docs/devel/migration/vfio.rst +F: qapi/vfio.json + +vfio-igd +M: Alex Williamson +M: Cédric Le Goater +M: Tomita Moeko +S: Supported +F: hw/vfio/igd.c F: docs/igd-assign.txt -F: docs/devel/vfio-migration.rst vfio-ccw M: Eric Farman @@ -2081,16 +2267,34 @@ 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/system/iommufd.h +F: backends/host_iommu_device.c +F: include/system/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 -F: docs/interop/vhost-user.rst +F: docs/interop/vhost-user* +F: docs/system/devices/vhost-user* F: contrib/vhost-user-*/ -F: backends/vhost-user.c -F: include/sysemu/vhost-user-backend.h +F: backends/*vhost* +F: include/system/vhost-user-backend.h +F: include/hw/virtio/vhost* +F: include/*/vhost* F: subprojects/libvhost-user/ +F: block/export/vhost-user* +F: util/vhost-user-server.c vhost-shadow-virtqueue R: Eugenio Pérez @@ -2106,6 +2310,8 @@ 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 @@ -2114,30 +2320,23 @@ 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: include/sysemu/balloon.h +F: system/balloon.c +F: include/system/balloon.h +F: tests/qtest/virtio-balloon-test.c +F: tests/functional/test_virtio_balloon.py virtio-9p -M: Greg Kurz M: Christian Schoenebeck +R: Greg Kurz S: Maintained W: https://wiki.qemu.org/Documentation/9p F: hw/9pfs/ X: hw/9pfs/xen-9p* -X: hw/9pfs/9p-proxy* F: fsdev/ -X: fsdev/virtfs-proxy-helper.c F: tests/qtest/virtio-9p-test.c F: tests/qtest/libqos/virtio-9p* -T: git https://gitlab.com/gkurz/qemu.git 9p-next T: git https://github.com/cschoenebeck/qemu.git 9p.next -virtio-9p-proxy -F: hw/9pfs/9p-proxy* -F: fsdev/virtfs-proxy-helper.c -F: docs/tools/virtfs-proxy-helper.rst -S: Obsolete - virtio-blk M: Stefan Hajnoczi L: qemu-block@nongnu.org @@ -2147,6 +2346,7 @@ F: hw/block/virtio-blk.c F: hw/block/dataplane/* F: include/hw/virtio/virtio-blk-common.h F: tests/qtest/virtio-blk-test.c +F: tests/functional/test_x86_64_hotplug_blk.py T: git https://github.com/stefanha/qemu.git block virtio-ccw @@ -2160,18 +2360,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: Stefan Hajnoczi S: Supported F: hw/virtio/vhost-user-fs* F: include/hw/virtio/vhost-user-fs.h -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/* @@ -2196,10 +2404,26 @@ R: Amit Shah S: Supported F: hw/virtio/virtio-rng.c F: include/hw/virtio/virtio-rng.h -F: include/sysemu/rng*.h +F: include/system/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 @@ -2217,6 +2441,13 @@ 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 @@ -2234,6 +2465,9 @@ F: include/hw/virtio/virtio-crypto.h virtio based memory device M: David Hildenbrand S: Supported +F: hw/s390x/virtio-ccw-md.c +F: hw/s390x/virtio-ccw-md.h +F: hw/s390x/virtio-ccw-md-stubs.c F: hw/virtio/virtio-md-pci.c F: include/hw/virtio/virtio-md-pci.h F: stubs/virtio-md-pci.c @@ -2245,11 +2479,23 @@ W: https://virtio-mem.gitlab.io/ F: hw/virtio/virtio-mem.c F: hw/virtio/virtio-mem-pci.h F: hw/virtio/virtio-mem-pci.c +F: hw/s390x/virtio-ccw-mem.c +F: hw/s390x/virtio-ccw-mem.h 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/* @@ -2276,7 +2522,7 @@ F: tests/qtest/fuzz-megasas-test.c Network packet abstractions M: Dmitry Fleytman -R: Akihiko Odaki +R: Akihiko Odaki S: Maintained F: include/net/eth.h F: net/eth.c @@ -2286,9 +2532,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 @@ -2296,17 +2548,17 @@ 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 +R: Akihiko Odaki S: Maintained F: hw/net/e1000x* e1000e M: Dmitry Fleytman -R: Akihiko Odaki +R: Akihiko Odaki S: Maintained F: hw/net/e1000e* F: tests/qtest/fuzz-e1000e-test.c @@ -2314,12 +2566,12 @@ F: tests/qtest/e1000e-test.c F: tests/qtest/libqos/e1000e.* igb -M: Akihiko Odaki -R: Sriram Yagnaraman -S: Maintained +M: Akihiko Odaki +R: Sriram Yagnaraman +S: Odd Fixes F: docs/system/devices/igb.rst F: hw/net/igb* -F: tests/avocado/netdev-ethtool.py +F: tests/functional/test_netdev_ethtool.py F: tests/qtest/igb-test.c F: tests/qtest/libqos/igb.c @@ -2335,11 +2587,16 @@ 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 +S: Orphaned +F: hw/gpio/pcf8574.c +F: include/gpio/pcf8574.h + Generic Loader M: Alistair Francis S: Maintained @@ -2353,7 +2610,7 @@ M: Alex Bennée S: Maintained F: hw/core/guest-loader.c F: docs/system/guest-loader.rst -F: tests/avocado/boot_xen.py +F: tests/functional/test_aarch64_xen.py Intel Hexadecimal Object File Loader M: Su Hang @@ -2373,7 +2630,7 @@ 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 LED @@ -2405,6 +2662,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 @@ -2413,14 +2671,15 @@ 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 +F: tests/functional/test_aarch64_virt_gpu.py vhost-user-blk -M: Raphael Norwitz +M: Raphael Norwitz S: Maintained F: contrib/vhost-user-blk/ F: contrib/vhost-user-scsi/ @@ -2435,7 +2694,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 @@ -2454,13 +2712,27 @@ F: hw/display/edid* F: include/hw/display/edid.h F: qemu-edid.c +macOS PV Graphics (apple-gfx) +M: Phil Dennis-Jordan +S: Maintained +F: hw/display/apple-gfx* + 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 @@ -2475,12 +2747,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 @@ -2505,12 +2777,14 @@ F: include/hw/rx/ CAN bus subsystem and hardware M: Pavel Pisa -M: Vikram Garhwal +M: Francisco Iglesias +M: Vikram Garhwal 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 @@ -2554,7 +2828,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 @@ -2562,7 +2836,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 @@ -2582,6 +2856,35 @@ 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 + +ivshmem-flat +M: Gustavo Romero +S: Maintained +F: hw/misc/ivshmem-flat.c +F: include/hw/misc/ivshmem-flat.h +F: docs/system/devices/ivshmem-flat.rst + +UEFI variable service +M: Gerd Hoffmann +S: Maintained +F: hw/uefi/ +F: include/hw/uefi/ + +VMapple +M: Alexander Graf +M: Phil Dennis-Jordan +S: Maintained +F: hw/vmapple/* +F: include/hw/vmapple/* +F: docs/system/arm/vmapple.rst + Subsystems ---------- Overall Audio backends @@ -2590,7 +2893,7 @@ M: Marc-André Lureau S: Odd Fixes F: audio/ X: audio/alsaaudio.c -X: audio/coreaudio.c +X: audio/coreaudio.m X: audio/dsound* X: audio/jackaudio.c X: audio/ossaudio.c @@ -2610,9 +2913,9 @@ 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 +F: audio/coreaudio.m DSound Audio backend M: Gerd Hoffmann @@ -2658,7 +2961,7 @@ F: hw/block/ F: qapi/block*.json F: qapi/transaction.json F: include/block/ -F: include/sysemu/block-*.h +F: include/system/block-*.h F: qemu-img* F: docs/tools/qemu-img.rst F: qemu-io* @@ -2685,12 +2988,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: block/plug.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 @@ -2732,6 +3035,16 @@ F: include/qemu/co-shared-resource.h T: git https://gitlab.com/jsnow/qemu.git jobs T: git https://gitlab.com/vsementsov/qemu.git block +CheckPoint and Restart (CPR) +R: Steve Sistare +S: Supported +F: hw/vfio/cpr* +F: include/hw/vfio/vfio-cpr.h +F: include/migration/cpr.h +F: migration/cpr* +F: tests/qtest/migration/cpr* +F: docs/devel/migration/CPR.rst + Compute Express Link M: Jonathan Cameron R: Fan Ni @@ -2739,6 +3052,7 @@ S: Supported F: hw/cxl/ F: hw/mem/cxl_type3.c F: include/hw/cxl/ +F: qapi/cxl.json Dirty Bitmaps M: Eric Blake @@ -2777,6 +3091,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 @@ -2794,21 +3109,24 @@ Device Tree M: Alistair Francis R: David Gibson S: Maintained -F: softmmu/device_tree.c -F: include/sysemu/device_tree.h +F: system/device_tree.c +F: include/system/device_tree.h Dump S: Supported M: Marc-André Lureau +R: Ani Sinha F: dump/ F: hw/misc/vmcoreinfo.c F: include/hw/misc/vmcoreinfo.h F: include/qemu/win_dump_defs -F: include/sysemu/dump-arch.h -F: include/sysemu/dump.h +F: include/system/dump-arch.h +F: include/system/dump.h F: qapi/dump.json F: scripts/dump-guest-memory.py F: stubs/dump.c +F: docs/specs/vmcoreinfo.rst +F: tests/qtest/vmcoreinfo-test.c Error reporting M: Markus Armbruster @@ -2817,7 +3135,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 @@ -2834,9 +3152,10 @@ 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 @@ -2844,18 +3163,19 @@ M: Peter Xu M: David Hildenbrand R: Philippe Mathieu-Daudé S: Supported -F: include/exec/ioport.h +F: include/system/ioport.h F: include/exec/memop.h -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: include/exec/memory-internal.h +F: include/system/memory.h +F: include/system/ram_addr.h +F: include/system/ramblock.h +F: include/system/memory_mapping.h +F: system/dma-helpers.c +F: system/ioport.c +F: system/memory.c +F: system/memory_mapping.c +F: system/physmem.c +F: system/memory-internal.h +F: system/ram-block-attributes.c F: scripts/coccinelle/memory-region-housekeeping.cocci Memory devices @@ -2869,11 +3189,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 @@ -2883,7 +3203,6 @@ F: qapi/ui.json F: docs/spice-port-fqdn.txt Graphics -M: Gerd Hoffmann M: Marc-André Lureau S: Odd Fixes F: ui/ @@ -2891,11 +3210,12 @@ F: include/ui/ F: qapi/ui.json F: util/drm.c F: docs/devel/ui.rst +F: tests/functional/test_vnc.py Cocoa graphics M: Peter Maydell M: Philippe Mathieu-Daudé -R: Akihiko Odaki +R: Akihiko Odaki S: Odd Fixes F: ui/cocoa.m @@ -2903,27 +3223,28 @@ Main loop M: Paolo Bonzini S: Maintained F: include/qemu/main-loop.h -F: include/sysemu/runstate.h -F: include/sysemu/runstate-action.h +F: include/system/runstate.h +F: include/system/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* +F: util/qemu-timer*.c +F: system/vl.c +F: system/main.c +F: system/cpus.c +F: system/cpu-throttle.c +F: system/cpu-timers.c +F: system/runstate* F: qapi/run-state.json Read, Copy, Update (RCU) M: Paolo Bonzini S: Maintained -F: docs/devel/lockcnt.txt -F: docs/devel/rcu.txt +F: 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) @@ -2957,26 +3278,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: include/system/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: include/system/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 @@ -3008,8 +3333,6 @@ S: Supported F: qapi/ X: qapi/*.json F: include/qapi/ -X: include/qapi/qmp/ -F: include/qapi/qmp/dispatch.h F: tests/qapi-schema/ F: tests/unit/test-*-visitor.c F: tests/unit/test-qapi-*.c @@ -3026,14 +3349,14 @@ 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 M: Markus Armbruster S: Supported F: qobject/ -F: include/qapi/qmp/ -X: include/qapi/qmp/dispatch.h +F: include/qobject/ F: scripts/coccinelle/qobject.cocci F: tests/unit/check-qdict.c F: tests/unit/check-qjson.c @@ -3051,10 +3374,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 @@ -3082,7 +3406,7 @@ F: qapi/qom.json F: qapi/qdev.json F: scripts/coccinelle/qom-parent-type.cocci F: scripts/qom-cast-macro-clean-cocci-gen.py -F: softmmu/qdev-monitor.c +F: system/qdev-monitor.c F: stubs/qdev.c F: qom/ F: tests/unit/check-qom-interface.c @@ -3112,23 +3436,25 @@ 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/system/qtest.h F: accel/qtest/ F: tests/qtest/ -F: docs/devel/qgraph.rst -F: docs/devel/qtest.rst +F: docs/devel/testing/qgraph.rst +F: docs/devel/testing/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 @@ -3137,7 +3463,7 @@ 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 +F: docs/devel/testing/fuzzing.rst Register API M: Alistair Francis @@ -3146,6 +3472,18 @@ 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: tests/docker/test-rust +F: rust/ + SLIRP M: Samuel Thibault S: Maintained @@ -3155,8 +3493,9 @@ T: git https://people.debian.org/~sthibault/qemu.git slirp Stats S: Orphan -F: include/sysemu/stats.h +F: include/system/stats.h F: stats/ +F: qapi/stats.json Streams M: Edgar E. Iglesias @@ -3171,6 +3510,7 @@ F: stubs/ Tracing M: Stefan Hajnoczi +R: Mads Ynddal S: Maintained F: trace/ F: trace-events @@ -3183,27 +3523,37 @@ 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: softmmu/tpm* +F: system/tpm* F: hw/tpm/* F: include/hw/acpi/tpm.h -F: include/sysemu/tpm* +F: include/system/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/system/spdm-socket.h + Checkpatch S: Odd Fixes F: scripts/checkpatch.pl Migration -M: Juan Quintela -R: Peter Xu -R: Leonardo Bras +M: Peter Xu +M: Fabiano Rosas S: Maintained F: hw/core/vmstate-if.c F: include/hw/vmstate-if.h @@ -3211,21 +3561,38 @@ F: include/migration/ F: include/qemu/userfaultfd.h F: migration/ F: scripts/vmstate-static-checker.py +F: tests/functional/test_migration.py F: tests/vmstate-static-checker-data/ -F: tests/qtest/migration-test.c -F: docs/devel/migration.rst +F: tests/qtest/migration/ +F: tests/qtest/migration-* +F: docs/devel/migration/ F: qapi/migration.json -F: tests/migration/ +F: tests/migration-stress/ F: util/userfaultfd.c +X: migration/rdma* + +RDMA Migration +R: Li Zhijian +R: Peter Xu +S: Odd Fixes +F: migration/rdma* +F: scripts/rdma-migration-helper.sh Migration dirty limit and dirty page rate M: Hyman Huang S: Maintained -F: softmmu/dirtylimit.c -F: include/sysemu/dirtylimit.h +F: system/dirtylimit.c +F: include/system/dirtylimit.h F: migration/dirtyrate.c F: migration/dirtyrate.h -F: include/sysemu/dirtyrate.h +F: include/system/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 @@ -3245,8 +3612,8 @@ F: scripts/xml-preprocess* Seccomp M: Daniel P. Berrange S: Odd Fixes -F: softmmu/qemu-seccomp.c -F: include/sysemu/seccomp.h +F: system/qemu-seccomp.c +F: include/system/seccomp.h F: tests/unit/test-seccomp.c Cryptography @@ -3259,7 +3626,7 @@ 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 @@ -3341,7 +3708,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 @@ -3351,21 +3718,20 @@ F: net/filter-mirror.c F: tests/qtest/test-filter* Record/replay -M: Pavel Dovgalyuk R: Paolo Bonzini +R: Alex Bennée W: https://wiki.qemu.org/Features/record-replay -S: Supported +S: Odd Fixes F: replay/* F: block/blkreplay.c F: net/filter-replay.c F: include/exec/replay-core.h -F: include/sysemu/replay.h +F: include/system/replay.h 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 -F: tests/avocado/reverse_debugging.py +F: tests/functional/*reverse_debug*.py +F: tests/functional/*replay*.py F: qapi/replay.json IOVA Tree @@ -3376,9 +3742,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 @@ -3404,6 +3777,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 @@ -3426,19 +3808,22 @@ F: tests/uefi-test-tools/ 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 +F: tests/functional/test_intel_iommu.py +F: tests/qtest/intel-iommu-test.c AMD-Vi Emulation S: Orphan F: hw/i386/amd_iommu.? OpenSBI Firmware -M: Bin Meng +L: qemu-riscv@nongnu.org S: Supported F: pc-bios/opensbi-* F: .gitlab-ci.d/opensbi.yml @@ -3455,12 +3840,23 @@ 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/system/reset.h +F: hw/core/reset.c +F: hw/core/resettable.c +F: hw/core/resetcontainer.c + Usermode Emulation ------------------ Overall usermode emulation M: Riku Voipio S: Maintained F: accel/tcg/user-exec*.c +F: hw/core/cpu-user.c F: include/user/ F: common-user/ @@ -3481,7 +3877,7 @@ 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 +F: tests/functional/test_arm_bflt.py Tiny Code Generator (TCG) ------------------------- @@ -3493,14 +3889,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 @@ -3529,7 +3928,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/ @@ -3544,7 +3943,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 @@ -3574,7 +3973,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 @@ -3600,7 +3999,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 @@ -3616,14 +4015,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 @@ -3664,7 +4063,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 @@ -3683,6 +4082,7 @@ M: Stefan Hajnoczi L: qemu-block@nongnu.org S: Supported F: block/blkverify.c +F: docs/devel/testing/blkverify.rst bochs M: Stefan Hajnoczi @@ -3709,7 +4109,8 @@ L: qemu-block@nongnu.org S: Supported F: block/parallels.c F: block/parallels-ext.c -F: docs/interop/parallels.txt +F: docs/interop/parallels.rst +F: docs/interop/prl-xml.rst T: git https://src.openvz.org/scm/~den/qemu.git parallels qed @@ -3717,6 +4118,7 @@ M: Stefan Hajnoczi L: qemu-block@nongnu.org S: Supported F: block/qed.c +F: docs/interop/qed_spec.rst raw M: Kevin Wolf @@ -3745,7 +4147,7 @@ M: Hanna Reitz L: qemu-block@nongnu.org S: Supported F: block/qcow2* -F: docs/interop/qcow2.txt +F: docs/interop/qcow2.rst qcow M: Kevin Wolf @@ -3759,6 +4161,7 @@ M: Hanna Reitz L: qemu-block@nongnu.org S: Supported F: block/blkdebug.c +F: docs/devel/testing/blkdebug.rst vpc M: Kevin Wolf @@ -3813,16 +4216,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 @@ -3853,17 +4246,28 @@ F: hw/remote/proxy-memory-listener.c F: include/hw/remote/proxy-memory-listener.h F: hw/remote/iohub.c F: include/hw/remote/iohub.h -F: subprojects/libvfio-user F: hw/remote/vfio-user-obj.c F: include/hw/remote/vfio-user-obj.h F: hw/remote/iommu.c F: include/hw/remote/iommu.h +F: tests/functional/test_multiprocess.py + +VFIO-USER: +M: John Levon +M: Thanos Makatos +S: Supported +F: docs/interop/vfio-user.rst +F: docs/system/devices/vfio-user.rst +F: hw/vfio-user/* +F: include/hw/vfio-user/* +F: subprojects/libvfio-user EBPF: M: Jason Wang R: Andrew Melnychenko R: Yuri Benditovich S: Maintained +F: docs/devel/ebpf_rss.rst F: ebpf/* F: tools/ebpf/* @@ -3871,22 +4275,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/avocado/tuxrun_baselines.py +F: tests/functional/test_*_tuxrun.py F: scripts/archive-source.sh -F: docs/devel/testing.rst +F: docs/devel/testing/ci* +F: docs/devel/testing/main.rst W: https://gitlab.com/qemu-project/qemu/pipelines W: https://travis-ci.org/qemu/qemu @@ -3898,6 +4303,13 @@ 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é +R: Daniel P. Berrange +F: docs/devel/testing/functional.rst +F: tests/functional/qemu_test/ + Windows Hosted Continuous Integration M: Yonggang Luo S: Maintained @@ -3909,15 +4321,6 @@ R: Philippe Mathieu-Daudé S: Maintained F: tests/tcg/Makefile.target -Integration Testing with the Avocado framework -W: https://trello.com/b/6Qi1pxVn/avocado-qemu -R: Cleber Rosa -R: Philippe Mathieu-Daudé -R: Wainer dos Santos Moschetta -R: Beraldo Leal -S: Odd Fixes -F: tests/avocado/ - GitLab custom runner (Works On Arm Sponsored) M: Alex Bennée M: Philippe Mathieu-Daudé @@ -3939,7 +4342,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 @@ -3948,7 +4351,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 @@ -3987,12 +4389,24 @@ S: Orphan F: po/*.po Sphinx documentation configuration and build machinery +M: John Snow M: Peter Maydell S: Maintained 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 ------------- @@ -4005,3 +4419,8 @@ 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 bfc4b2c8e92..c92a3cf7857 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,8 @@ 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)/VERSION +config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/scripts/meson-buildoptions.sh \ + $(SRC_PATH)/pythondeps.toml $(SRC_PATH)/VERSION @echo config-host.mak is out-of-date, running configure @if test -f meson-private/coredata.dat; then \ ./config.status --skip-meson; \ @@ -141,8 +142,13 @@ 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)) @@ -181,11 +187,6 @@ SUBDIR_RULES=$(foreach t, all clean distclean, $(addsuffix /$(t), $(SUBDIRS))) $(SUBDIR_RULES): $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" $(notdir $@),) -ifneq ($(filter contrib/plugins, $(SUBDIRS)),) -.PHONY: plugins -plugins: contrib/plugins/all -endif - .PHONY: recurse-all recurse-clean recurse-all: $(addsuffix /all, $(SUBDIRS)) recurse-clean: $(addsuffix /clean, $(SUBDIRS)) @@ -202,13 +203,14 @@ clean: recurse-clean ! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-arm.a \ -exec rm {} + rm -f TAGS cscope.* *~ */*~ + @$(MAKE) -Ctests/qemu-iotests clean VERSION = $(shell cat $(SRC_PATH)/VERSION) -dist: qemu-$(VERSION).tar.bz2 +dist: qemu-$(VERSION).tar.xz -qemu-%.tar.bz2: - $(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)" +qemu-%.tar.xz: + $(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.xz,%,$@)" distclean: clean recurse-distclean -$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean -g || : @@ -283,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:' @@ -293,16 +302,14 @@ help: $(call print-help,cscope,Generate cscope index) $(call print-help,sparse,Run sparse on the QEMU source) @echo '' -ifneq ($(filter contrib/plugins, $(SUBDIRS)),) - @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) diff --git a/README.rst b/README.rst index 21df79ef437..b120a1f69e9 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/VERSION b/VERSION index ae3313c92b2..54e6ccf8546 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.1.50 +10.0.50 diff --git a/accel/Kconfig b/accel/Kconfig index a30cf2eb483..4263cab7227 100644 --- a/accel/Kconfig +++ b/accel/Kconfig @@ -16,3 +16,5 @@ config KVM config XEN bool select FSDEV_9P if VIRTFS + select PCI_EXPRESS_GENERIC_BRIDGE + select XEN_BUS diff --git a/accel/accel-blocker.c b/accel/accel-blocker.c index 1e7f423462d..51132d1b8a0 100644 --- a/accel/accel-blocker.c +++ b/accel/accel-blocker.c @@ -25,10 +25,11 @@ */ #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" +#include "system/accel-blocker.h" static QemuLockCnt accel_in_ioctl_lock; static QemuEvent accel_in_ioctl_event; @@ -41,7 +42,7 @@ void accel_blocker_init(void) void accel_ioctl_begin(void) { - if (likely(qemu_mutex_iothread_locked())) { + if (likely(bql_locked())) { return; } @@ -51,7 +52,7 @@ void accel_ioctl_begin(void) void accel_ioctl_end(void) { - if (likely(qemu_mutex_iothread_locked())) { + if (likely(bql_locked())) { return; } @@ -62,7 +63,7 @@ void accel_ioctl_end(void) void accel_cpu_ioctl_begin(CPUState *cpu) { - if (unlikely(qemu_mutex_iothread_locked())) { + if (unlikely(bql_locked())) { return; } @@ -72,7 +73,7 @@ void accel_cpu_ioctl_begin(CPUState *cpu) void accel_cpu_ioctl_end(CPUState *cpu) { - if (unlikely(qemu_mutex_iothread_locked())) { + if (unlikely(bql_locked())) { return; } @@ -105,7 +106,7 @@ void accel_ioctl_inhibit_begin(void) * We allow to inhibit only when holding the BQL, so we can identify * when an inhibitor wants to issue an ioctl easily. */ - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); /* Block further invocations of the ioctls outside the BQL. */ CPU_FOREACH(cpu) { diff --git a/accel/accel-common.c b/accel/accel-common.c index df72cc989a9..4894b98d64a 100644 --- a/accel/accel-common.c +++ b/accel/accel-common.c @@ -4,41 +4,14 @@ * 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. + * SPDX-License-Identifier: MIT */ #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), -}; +#include "qemu/target-info.h" +#include "accel/accel-cpu.h" +#include "accel-internal.h" /* Lookup AccelClass from opt_name. Returns NULL if not found */ AccelClass *accel_find(const char *opt_name) @@ -87,48 +60,65 @@ static void accel_init_cpu_interfaces(AccelClass *ac) const char *ac_name; /* AccelClass name */ char *acc_name; /* AccelCPUClass name */ ObjectClass *acc; /* AccelCPUClass */ + const char *cpu_resolving_type = target_cpu_type(); 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_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); + 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); + if (cpu->cc->accel_cpu && cpu->cc->accel_cpu->cpu_instance_init) { + cpu->cc->accel_cpu->cpu_instance_init(cpu); } } -bool accel_cpu_realizefn(CPUState *cpu, Error **errp) +bool accel_cpu_common_realize(CPUState *cpu, Error **errp) { - CPUClass *cc = CPU_GET_CLASS(cpu); + AccelState *accel = current_accel(); + AccelClass *acc = ACCEL_GET_CLASS(accel); + + /* target specific realization */ + if (cpu->cc->accel_cpu + && cpu->cc->accel_cpu->cpu_target_realize + && !cpu->cc->accel_cpu->cpu_target_realize(cpu, errp)) { + return false; + } - if (cc->accel_cpu && cc->accel_cpu->cpu_realizefn) { - return cc->accel_cpu->cpu_realizefn(cpu, errp); + /* 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(); @@ -139,17 +129,14 @@ int accel_supported_gdbstub_sstep_flags(void) return 0; } -static const TypeInfo accel_cpu_type = { - .name = TYPE_ACCEL_CPU, - .parent = TYPE_OBJECT, - .abstract = true, - .class_size = sizeof(AccelCPUClass), +static const TypeInfo accel_types[] = { + { + .name = TYPE_ACCEL, + .parent = TYPE_OBJECT, + .class_size = sizeof(AccelClass), + .instance_size = sizeof(AccelState), + .abstract = true, + }, }; -static void register_accel_types(void) -{ - type_register_static(&accel_type); - type_register_static(&accel_cpu_type); -} - -type_init(register_accel_types); +DEFINE_TYPES(accel_types) diff --git a/accel/accel-internal.h b/accel/accel-internal.h new file mode 100644 index 00000000000..d3a4422cbf7 --- /dev/null +++ b/accel/accel-internal.h @@ -0,0 +1,17 @@ +/* + * QEMU 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_INTERNAL_H +#define ACCEL_INTERNAL_H + +#include "qemu/accel.h" + +void accel_init_ops_interfaces(AccelClass *ac); + +#endif /* ACCEL_SYSTEM_H */ diff --git a/accel/accel-softmmu.c b/accel/accel-softmmu.c deleted file mode 100644 index 9c804ba9e3b..00000000000 --- 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 "qemu/error-report.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 5e192f1882e..00000000000 --- 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 00000000000..a0f562ae9ff --- /dev/null +++ b/accel/accel-system.c @@ -0,0 +1,105 @@ +/* + * 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 "system/accel-ops.h" +#include "system/cpus.h" +#include "qemu/error-report.h" +#include "accel-internal.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); + 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-target.c b/accel/accel-target.c new file mode 100644 index 00000000000..7fd392fbc4a --- /dev/null +++ b/accel/accel-target.c @@ -0,0 +1,41 @@ +/* + * 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 "accel/accel-cpu-target.h" + +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_cpu_type); +} + +type_init(register_accel_types); diff --git a/accel/accel-user.c b/accel/accel-user.c index 22b6a1a1a89..7d192306a65 100644 --- a/accel/accel-user.c +++ b/accel/accel-user.c @@ -9,6 +9,12 @@ #include "qemu/osdep.h" #include "qemu/accel.h" +#include "accel-internal.h" + +void accel_init_ops_interfaces(AccelClass *ac) +{ + /* nothing */ +} AccelState *current_accel(void) { diff --git a/accel/dummy-cpus.c b/accel/dummy-cpus.c index d6a1b8d0a27..867276144fa 100644 --- a/accel/dummy-cpus.c +++ b/accel/dummy-cpus.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qemu/rcu.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "qemu/guest-random.h" #include "qemu/main-loop.h" #include "hw/core/cpu.h" @@ -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 3c94c79747d..d60446b85b8 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -50,21 +50,18 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" -#include "exec/address-spaces.h" -#include "exec/exec-all.h" -#include "exec/gdbstub.h" -#include "sysemu/cpus.h" -#include "sysemu/hvf.h" -#include "sysemu/hvf_int.h" -#include "sysemu/runstate.h" +#include "system/address-spaces.h" +#include "gdbstub/enums.h" +#include "hw/boards.h" +#include "system/accel-ops.h" +#include "system/cpus.h" +#include "system/hvf.h" +#include "system/hvf_int.h" +#include "system/runstate.h" #include "qemu/guest-random.h" 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) @@ -204,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); } } @@ -221,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) @@ -323,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); @@ -348,7 +354,7 @@ static inline int hvf_gdbstub_sstep_flags(void) return SSTEP_ENABLE | SSTEP_NOIRQ; } -static void hvf_accel_class_init(ObjectClass *oc, void *data) +static void hvf_accel_class_init(ObjectClass *oc, const void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "HVF"; @@ -360,6 +366,7 @@ static void hvf_accel_class_init(ObjectClass *oc, void *data) static const TypeInfo hvf_accel_type = { .name = TYPE_HVF_ACCEL, .parent = TYPE_ACCEL, + .instance_size = sizeof(HVFState), .class_init = hvf_accel_class_init, }; @@ -400,9 +407,9 @@ static int hvf_init_vcpu(CPUState *cpu) r = hv_vcpu_create(&cpu->accel->fd, (hv_vcpu_exit_t **)&cpu->accel->exit, NULL); #else - r = hv_vcpu_create((hv_vcpuid_t *)&cpu->accel->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; @@ -424,11 +431,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); @@ -449,7 +455,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; } @@ -464,10 +470,6 @@ 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, @@ -576,7 +578,7 @@ static void hvf_remove_all_breakpoints(CPUState *cpu) } } -static void hvf_accel_ops_class_init(ObjectClass *oc, void *data) +static void hvf_accel_ops_class_init(ObjectClass *oc, const void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index db05b81be58..8c387fda24d 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -10,43 +10,34 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "sysemu/hvf.h" -#include "sysemu/hvf_int.h" +#include "system/hvf.h" +#include "system/hvf_int.h" +#include "hw/core/cpu.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; -#if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 - case HV_DENIED: - error_report("Error: HV_DENIED"); - break; -#endif - default: - error_report("Unknown Error"); - } + error_report("Error: %s = %s (0x%x, at %s:%u)", + exp, hvf_return_string(ret), ret, file, line); abort(); } @@ -68,8 +59,13 @@ int hvf_sw_breakpoints_active(CPUState *cpu) return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints); } -int hvf_update_guest_debug(CPUState *cpu) +static void do_hvf_update_guest_debug(CPUState *cpu, run_on_cpu_data arg) { hvf_arch_update_guest_debug(cpu); +} + +int hvf_update_guest_debug(CPUState *cpu) +{ + run_on_cpu(cpu, do_hvf_update_guest_debug, RUN_ON_CPU_NULL); return 0; } diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index 457eafa3802..e5c15449aa6 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -16,10 +16,11 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_int.h" -#include "sysemu/runstate.h" -#include "sysemu/cpus.h" +#include "system/accel-ops.h" +#include "system/kvm.h" +#include "system/kvm_int.h" +#include "system/runstate.h" +#include "system/cpus.h" #include "qemu/guest-random.h" #include "qapi/error.h" @@ -33,10 +34,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 +58,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 +67,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,17 +80,17 @@ 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 KVM_CAP_SET_GUEST_DEBUG +#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) +static void kvm_accel_ops_class_init(ObjectClass *oc, const void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); @@ -105,7 +102,7 @@ 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; diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index ff1578bb32b..d095d1b98f8 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -27,14 +27,15 @@ #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/s390x/adapter.h" -#include "exec/gdbstub.h" -#include "sysemu/kvm_int.h" -#include "sysemu/runstate.h" -#include "sysemu/cpus.h" -#include "sysemu/accel-blocker.h" +#include "gdbstub/enums.h" +#include "system/kvm_int.h" +#include "system/runstate.h" +#include "system/cpus.h" +#include "system/accel-blocker.h" #include "qemu/bswap.h" -#include "exec/memory.h" -#include "exec/ram_addr.h" +#include "exec/tswap.h" +#include "system/memory.h" +#include "system/ram_addr.h" #include "qemu/event_notifier.h" #include "qemu/main-loop.h" #include "trace.h" @@ -42,21 +43,26 @@ #include "qapi/visitor.h" #include "qapi/qapi-types-common.h" #include "qapi/qapi-visit-common.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "qemu/guest-random.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "kvm-cpus.h" -#include "sysemu/dirtylimit.h" +#include "system/dirtylimit.h" #include "qemu/range.h" #include "hw/boards.h" -#include "sysemu/stats.h" +#include "system/stats.h" /* This check must be after config-host.h is included */ #ifdef CONFIG_EVENTFD #include #endif +#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) +# define KVM_HAVE_MCE_INJECTION 1 +#endif + + /* KVM uses PAGE_SIZE in its definition of KVM_COALESCED_MMIO_MAX. We * need to use the real host PAGE_SIZE, as that's what KVM will use. */ @@ -69,15 +75,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; @@ -90,8 +91,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; @@ -99,18 +98,22 @@ 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; +bool kvm_pre_fault_memory_supported; +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 }; @@ -174,39 +177,106 @@ 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; } -/* Called with KVMMemoryListener.slots_lock held */ -static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) +unsigned int kvm_get_free_memslots(void) { + unsigned int used_slots = 0; KVMState *s = kvm_state; int i; - for (i = 0; i < s->nr_slots; i++) { - if (kml->slots[i].memory_size == 0) { - return &kml->slots[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 NULL; + return s->nr_slots_max - used_slots; } -bool kvm_has_free_slot(MachineState *ms) +/* Called with KVMMemoryListener.slots_lock held */ +static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) { - KVMState *s = KVM_STATE(ms->accelerator); - bool result; - KVMMemoryListener *kml = &s->memory_listener; + unsigned int n; + int i; - kvm_slots_lock(); - result = !!kvm_get_free_slot(kml); - kvm_slots_unlock(); + for (i = 0; i < kml->nr_slots_allocated; i++) { + if (kml->slots[i].memory_size == 0) { + return &kml->slots[i]; + } + } - return result; + /* + * 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; } /* Called with KVMMemoryListener.slots_lock held */ @@ -226,10 +296,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) { @@ -271,7 +340,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) { @@ -288,46 +357,151 @@ 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; +} + +static void kvm_reset_parked_vcpus(KVMState *s) +{ + struct KVMParkedVcpu *cpu; + + QLIST_FOREACH(cpu, &s->kvm_parked_vcpus, node) { + kvm_arch_reset_parked_vcpu(cpu->vcpu_id, cpu->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; + if (!s->guest_state_protected) { + 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) { @@ -337,7 +511,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; } @@ -353,10 +527,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; } @@ -369,44 +540,26 @@ 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_arch_pre_create_vcpu(cpu, errp); if (ret < 0) { - error_setg_errno(errp, -ret, "kvm_init_vcpu: kvm_get_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; + ret = kvm_create_vcpu(cpu); + if (ret < 0) { + error_setg_errno(errp, -ret, + "kvm_init_vcpu: kvm_create_vcpu failed (%lu)", + kvm_arch_vcpu_id(cpu)); + goto err; + } mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0); if (mmap_size < 0) { @@ -439,7 +592,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; } } @@ -471,6 +623,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; } @@ -813,7 +969,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. @@ -1004,7 +1160,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 || @@ -1101,13 +1257,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, - .priority = MEMORY_LISTENER_PRIORITY_MIN, -}; - int kvm_check_extension(KVMState *s, unsigned int extension) { int ret; @@ -1133,6 +1282,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; @@ -1147,7 +1301,7 @@ static void kvm_unpoison_all(void *param) QLIST_FOREACH_SAFE(page, &hwpoison_page_list, list, next_page) { QLIST_REMOVE(page, list); - qemu_ram_remap(page->ram_addr, TARGET_PAGE_SIZE); + qemu_ram_remap(page->ram_addr); g_free(page); } } @@ -1166,23 +1320,29 @@ 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 - /* The kernel expects ioeventfd values in HOST_BIG_ENDIAN - * endianness, but the memory core hands them in target endianness. - * For example, PPC is always treated as big-endian even if running - * on KVM and on PPC64LE. Correct here. - */ - switch (size) { - case 2: - val = bswap16(val); - break; - case 4: - val = bswap32(val); - break; + if (target_needs_bswap()) { + /* + * The kernel expects ioeventfd values in HOST_BIG_ENDIAN + * endianness, but the memory core hands them in target endianness. + * For example, PPC is always treated as big-endian even if running + * on KVM and on PPC64LE. Correct here, swapping back. + */ + switch (size) { + case 2: + val = bswap16(val); + break; + case 4: + val = bswap32(val); + break; + } } -#endif return val; } @@ -1249,43 +1409,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) { @@ -1306,6 +1429,36 @@ 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) @@ -1387,6 +1540,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, } start_addr += slot_size; size -= slot_size; + kml->nr_slots_used--; } while (size); return; } @@ -1401,6 +1555,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) { @@ -1408,10 +1565,21 @@ 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); } @@ -1440,18 +1608,14 @@ 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 void kvm_dirty_ring_reaper_init(KVMState *s) @@ -1641,12 +1805,8 @@ static void kvm_log_sync_global(MemoryListener *l, bool last_stage) /* 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); @@ -1761,12 +1921,9 @@ 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); @@ -1799,6 +1956,8 @@ 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 = MEMORY_LISTENER_PRIORITY_DEV_BACKEND, @@ -1840,7 +1999,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) { @@ -1852,12 +2011,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); } @@ -1879,8 +2032,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; @@ -1977,41 +2130,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) { @@ -2021,63 +2143,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) @@ -2119,12 +2195,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; } @@ -2204,69 +2285,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) @@ -2368,6 +2389,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); @@ -2408,7 +2434,7 @@ static int kvm_recommended_vcpus(KVMState *s) static int kvm_max_vcpus(KVMState *s) { - int ret = kvm_check_extension(s, KVM_CAP_MAX_VCPUS); + int ret = kvm_vm_check_extension(s, KVM_CAP_MAX_VCPUS); return (ret) ? ret : kvm_recommended_vcpus(s); } @@ -2426,7 +2452,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, @@ -2438,6 +2464,109 @@ uint32_t kvm_dirty_ring_size(void) return kvm_state->kvm_dirty_ring_size; } +static int do_kvm_create_vm(MachineState *ms, int type) +{ + KVMState *s; + int ret; + + s = KVM_STATE(ms->accelerator); + + do { + ret = kvm_ioctl(s, KVM_CREATE_VM, type); + } while (ret == -EINTR); + + if (ret < 0) { + error_report("ioctl(KVM_CREATE_VM) failed: %s", strerror(-ret)); + +#ifdef TARGET_S390X + if (ret == -EINVAL) { + 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) { + error_printf("PPC KVM module is not loaded. Try modprobe kvm_%s.\n", + (type == 2) ? "pr" : "hv"); + } +#endif + } + + return ret; +} + +static int find_kvm_machine_type(MachineState *ms) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + int type; + + 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; +} + +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 + */ + ret = kvm_dirty_ring_init(s); + if (ret < 0) { + return ret; + } + + /* + * KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 is not needed when dirty ring is + * enabled. More importantly, KVM_DIRTY_LOG_INITIALLY_SET will assume no + * page is wr-protected initially, which is against how kvm dirty ring is + * usage - kvm dirty ring requires all pages are wr-protected at the very + * beginning. Enabling this feature for dirty ring causes data corruption. + * + * TODO: Without KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 and kvm clear dirty log, + * we may expect a higher stall time when starting the migration. In the + * future we can enable KVM_CLEAR_DIRTY_LOG to work with dirty ring too: + * instead of clearing dirty bit, it can be a way to explicitly wr-protect + * guest pages. + */ + if (!s->kvm_dirty_ring_size) { + dirty_log_manual_caps = + kvm_check_extension(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2); + dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | + KVM_DIRTY_LOG_INITIALLY_SET); + s->manual_dirty_log_protect = dirty_log_manual_caps; + if (dirty_log_manual_caps) { + ret = kvm_vm_enable_cap(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2, 0, + dirty_log_manual_caps); + if (ret) { + warn_report("Trying to enable capability %"PRIu64" of " + "KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 but failed. " + "Falling back to the legacy mode. ", + dirty_log_manual_caps); + s->manual_dirty_log_protect = 0; + } + } + } + + return 0; +} + static int kvm_init(MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); @@ -2457,7 +2586,6 @@ static int kvm_init(MachineState *ms) const KVMCapabilityInfo *missing_cap; int ret; int type; - uint64_t dirty_log_manual_caps; qemu_mutex_init(&kml_slots_lock); @@ -2474,13 +2602,13 @@ static int kvm_init(MachineState *ms) s->sigmask_len = 8; accel_blocker_init(); -#ifdef KVM_CAP_SET_GUEST_DEBUG +#ifdef TARGET_KVM_HAVE_GUEST_DEBUG QTAILQ_INIT(&s->kvm_sw_breakpoints); #endif QLIST_INIT(&s->kvm_parked_vcpus); - s->fd = qemu_open_old("/dev/kvm", O_RDWR); + s->fd = qemu_open_old(s->device ?: "/dev/kvm", O_RDWR); if (s->fd == -1) { - fprintf(stderr, "Could not access KVM kernel module: %m\n"); + error_report("Could not access KVM kernel module: %m"); ret = -errno; goto err; } @@ -2490,78 +2618,43 @@ static int kvm_init(MachineState *ms) if (ret >= 0) { ret = -EINVAL; } - fprintf(stderr, "kvm version too old\n"); + error_report("kvm version too old"); goto err; } if (ret > KVM_API_VERSION) { ret = -EINVAL; - fprintf(stderr, "kvm version not supported\n"); + error_report("kvm version not supported"); goto err; } kvm_immediate_exit = kvm_check_extension(s, KVM_CAP_IMMEDIATE_EXIT); - s->nr_slots = kvm_check_extension(s, KVM_CAP_NR_MEMSLOTS); + s->nr_slots_max = 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); - } else { - type = kvm_arch_get_default_type(ms); + 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; } - do { - ret = kvm_ioctl(s, KVM_CREATE_VM, type); - } while (ret == -EINTR); - + ret = do_kvm_create_vm(ms, type); if (ret < 0) { - fprintf(stderr, "ioctl(KVM_CREATE_VM) failed: %d %s\n", -ret, - 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"); - } -#elif defined(TARGET_PPC) - if (ret == -EINVAL) { - fprintf(stderr, - "PPC KVM module is not loaded. Try modprobe kvm_%s.\n", - (type == 2) ? "pr" : "hv"); - } -#endif 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); @@ -2573,9 +2666,9 @@ static int kvm_init(MachineState *ms) 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); + 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); } } @@ -2589,8 +2682,8 @@ static int kvm_init(MachineState *ms) } if (missing_cap) { ret = -EINVAL; - fprintf(stderr, "kvm does not support %s\n%s", - missing_cap->name, upgrade_note); + error_report("kvm does not support %s", missing_cap->name); + error_printf("%s", upgrade_note); goto err; } @@ -2598,79 +2691,23 @@ static int kvm_init(MachineState *ms) s->coalesced_pio = s->coalesced_mmio && kvm_check_extension(s, KVM_CAP_COALESCED_PIO); - /* - * Enable KVM dirty ring if supported, otherwise fall back to - * dirty logging mode - */ - ret = kvm_dirty_ring_init(s); + ret = kvm_setup_dirty_ring(s); if (ret < 0) { goto err; } - /* - * KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 is not needed when dirty ring is - * enabled. More importantly, KVM_DIRTY_LOG_INITIALLY_SET will assume no - * page is wr-protected initially, which is against how kvm dirty ring is - * usage - kvm dirty ring requires all pages are wr-protected at the very - * beginning. Enabling this feature for dirty ring causes data corruption. - * - * TODO: Without KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 and kvm clear dirty log, - * we may expect a higher stall time when starting the migration. In the - * future we can enable KVM_CLEAR_DIRTY_LOG to work with dirty ring too: - * instead of clearing dirty bit, it can be a way to explicitly wr-protect - * guest pages. - */ - if (!s->kvm_dirty_ring_size) { - dirty_log_manual_caps = - kvm_check_extension(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2); - dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | - KVM_DIRTY_LOG_INITIALLY_SET); - s->manual_dirty_log_protect = dirty_log_manual_caps; - if (dirty_log_manual_caps) { - ret = kvm_vm_enable_cap(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2, 0, - dirty_log_manual_caps); - if (ret) { - warn_report("Trying to enable capability %"PRIu64" of " - "KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 but failed. " - "Falling back to the legacy mode. ", - dirty_log_manual_caps); - s->manual_dirty_log_protect = 0; - } - } - } - #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); @@ -2678,10 +2715,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 @@ -2690,7 +2724,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); @@ -2707,6 +2741,13 @@ 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); + kvm_pre_fault_memory_supported = kvm_vm_check_extension(s, KVM_CAP_PRE_FAULT_MEMORY); + 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; } @@ -2717,24 +2758,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); @@ -2787,16 +2820,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"); @@ -2843,46 +2874,86 @@ 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; } void kvm_cpu_synchronize_post_reset(CPUState *cpu) { run_on_cpu(cpu, do_kvm_cpu_synchronize_post_reset, RUN_ON_CPU_NULL); + + if (cpu == first_cpu) { + kvm_reset_parked_vcpus(kvm_state); + } } 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) @@ -2950,32 +3021,141 @@ 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 = -EINVAL; + + 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 ret; + } + + if (!size) { + return ret; + } + + 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 ret; + } + + 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); + + ret = ram_block_attributes_state_change(RAM_BLOCK_ATTRIBUTES(mr->rdm), + offset, size, to_private); + if (ret) { + error_report("Failed to notify the listener the state change of " + "(0x%"HWADDR_PRIx" + 0x%"HWADDR_PRIx") to %s", + start, size, to_private ? "private" : "shared"); + goto out_unref; + } + + 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 @@ -2995,39 +3175,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, @@ -3037,7 +3218,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, @@ -3047,11 +3227,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; @@ -3069,7 +3247,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 @@ -3081,11 +3259,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); @@ -3097,26 +3276,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); @@ -3127,7 +3317,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; @@ -3145,7 +3335,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; @@ -3165,7 +3355,7 @@ int kvm_vm_ioctl(KVMState *s, int type, ...) return ret; } -int kvm_vcpu_ioctl(CPUState *cpu, int type, ...) +int kvm_vcpu_ioctl(CPUState *cpu, unsigned long type, ...) { int ret; void *arg; @@ -3185,7 +3375,7 @@ int kvm_vcpu_ioctl(CPUState *cpu, int type, ...) return ret; } -int kvm_device_ioctl(int fd, int type, ...) +int kvm_device_ioctl(int fd, unsigned long type, ...) { int ret; void *arg; @@ -3266,29 +3456,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 @@ -3298,17 +3470,12 @@ 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 +#ifdef TARGET_KVM_HAVE_GUEST_DEBUG struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu, vaddr pc) { struct kvm_sw_breakpoint *bp; @@ -3468,7 +3635,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) { @@ -3751,6 +3918,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); @@ -3769,6 +3969,8 @@ static void kvm_accel_instance_init(Object *obj) s->xen_version = 0; s->xen_gnttab_max_frames = 64; s->xen_evtchn_max_pirq = 256; + s->device = NULL; + s->msr_energy.enable = false; } /** @@ -3782,7 +3984,7 @@ static int kvm_gdbstub_sstep_flags(void) return kvm_sstep_flags; } -static void kvm_accel_class_init(ObjectClass *oc, void *data) +static void kvm_accel_class_init(ObjectClass *oc, const void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "KVM"; @@ -3809,6 +4011,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); } @@ -3879,7 +4096,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; @@ -4227,3 +4444,30 @@ void query_stats_schemas_cb(StatsSchemaList **result, Error **errp) 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 ca40add32c3..688511151c8 100644 --- a/accel/kvm/kvm-cpus.h +++ b/accel/kvm/kvm-cpus.h @@ -10,8 +10,6 @@ #ifndef KVM_CPUS_H #define KVM_CPUS_H -#include "sysemu/cpus.h" - int kvm_init_vcpu(CPUState *cpu, Error **errp); int kvm_cpu_exec(CPUState *cpu); void kvm_destroy_vcpu(CPUState *cpu); @@ -22,5 +20,4 @@ bool kvm_supports_guest_debug(void); int kvm_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len); int kvm_remove_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len); void kvm_remove_all_breakpoints(CPUState *cpu); - #endif /* KVM_CPUS_H */ diff --git a/accel/kvm/trace-events b/accel/kvm/trace-events index 399aaeb0ec7..e43d18a8692 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 638a9a03ba9..52909314bfa 100644 --- a/accel/meson.build +++ b/accel/meson.build @@ -1,5 +1,6 @@ -specific_ss.add(files('accel-common.c', 'accel-blocker.c')) -system_ss.add(files('accel-softmmu.c')) +common_ss.add(files('accel-common.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') diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index f6056ac8361..92bed9264ce 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -18,18 +18,31 @@ #include "qemu/option.h" #include "qemu/config-file.h" #include "qemu/accel.h" -#include "sysemu/qtest.h" -#include "sysemu/cpus.h" +#include "system/accel-ops.h" +#include "system/qtest.h" +#include "system/cpus.h" #include "qemu/guest-random.h" #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; } -static void qtest_accel_class_init(ObjectClass *oc, void *data) +static void qtest_accel_class_init(ObjectClass *oc, const void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "QTest"; @@ -46,12 +59,13 @@ static const TypeInfo qtest_accel_type = { }; module_obj(TYPE_QTEST_ACCEL); -static void qtest_accel_ops_class_init(ObjectClass *oc, void *data) +static void qtest_accel_ops_class_init(ObjectClass *oc, const void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); 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/hvf-stub.c b/accel/stubs/hvf-stub.c new file mode 100644 index 00000000000..42eadc5ca92 --- /dev/null +++ b/accel/stubs/hvf-stub.c @@ -0,0 +1,12 @@ +/* + * HVF stubs for QEMU + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/hvf.h" + +bool hvf_allowed; diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index 235dc661bcc..ecfd7636f5f 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -11,23 +11,19 @@ */ #include "qemu/osdep.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/pci/msi.h" 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; -bool kvm_direct_msi_allowed; void kvm_flush_coalesced_mmio_buffer(void) { @@ -42,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; @@ -92,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) { @@ -109,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) @@ -133,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 6b0f200efe9..8ca1a4529e2 100644 --- a/accel/stubs/meson.build +++ b/accel/stubs/meson.build @@ -1,6 +1,7 @@ -sysemu_stubs_ss = ss.source_set() -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')) +system_stubs_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c')) -specific_ss.add_all(when: ['CONFIG_SYSTEM_ONLY'], 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 a9e7a2d5b44..3b76b8b17c1 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -11,34 +11,7 @@ */ #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, vaddr vaddr) -{ -} - -void tcg_flush_jmp_cache(CPUState *cpu) -{ -} - -int probe_access_flags(CPUArchState *env, vaddr addr, int size, - MMUAccessType access_type, int mmu_idx, - bool nonfault, void **phost, uintptr_t retaddr) -{ - g_assert_not_reached(); -} - -void *probe_access(CPUArchState *env, vaddr addr, int size, - MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) -{ - /* Handled by hardware accelerator. */ - g_assert_not_reached(); -} +#include "exec/cpu-common.h" G_NORETURN void cpu_loop_exit(CPUState *cpu) { diff --git a/accel/stubs/xen-stub.c b/accel/stubs/xen-stub.c index 7054965c480..cf929b644b7 100644 --- a/accel/stubs/xen-stub.c +++ b/accel/stubs/xen-stub.c @@ -6,7 +6,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/xen.h" +#include "system/xen.h" #include "qapi/qapi-commands-migration.h" bool xen_allowed; diff --git a/accel/tcg/atomic_common.c.inc b/accel/tcg/atomic_common.c.inc index 95a5c5ff12d..6056598c23d 100644 --- a/accel/tcg/atomic_common.c.inc +++ b/accel/tcg/atomic_common.c.inc @@ -14,9 +14,20 @@ */ 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); + } } /* diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index 84c08b1425b..08a475c10ca 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,11 +77,12 @@ # define END _le #endif -ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr addr, +ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr, ABI_TYPE cmpv, ABI_TYPE newv, MemOpIdx oi, uintptr_t retaddr) { - DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); DATA_TYPE ret; #if DATA_SIZE == 16 @@ -82,32 +91,48 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr 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 -ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE val, +ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) { - DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, 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, abi_ptr addr, \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ { \ DATA_TYPE *haddr, ret; \ - haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); \ + 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; \ } @@ -131,11 +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, abi_ptr addr, \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \ { \ XDATA_TYPE *haddr, cmp, old, new, val = xval; \ - haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); \ + haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, DATA_SIZE, retaddr); \ smp_mb(); \ cmp = qatomic_read__nocheck(haddr); \ do { \ @@ -143,7 +168,12 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr 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; \ } @@ -172,11 +202,12 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) # define END _be #endif -ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr addr, +ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr, ABI_TYPE cmpv, ABI_TYPE newv, MemOpIdx oi, uintptr_t retaddr) { - DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); DATA_TYPE ret; #if DATA_SIZE == 16 @@ -185,32 +216,48 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr 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 -ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE val, +ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) { - DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, 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, abi_ptr addr, \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ { \ DATA_TYPE *haddr, ret; \ - haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); \ + 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); \ } @@ -231,11 +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, abi_ptr addr, \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \ { \ XDATA_TYPE *haddr, ldo, ldn, old, new, val = xval; \ - haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, retaddr); \ + haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, DATA_SIZE, retaddr); \ smp_mb(); \ ldn = qatomic_read__nocheck(haddr); \ do { \ @@ -243,7 +290,12 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr 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; \ } @@ -277,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/backend-ldst.h b/accel/tcg/backend-ldst.h new file mode 100644 index 00000000000..9c3a407a5af --- /dev/null +++ b/accel/tcg/backend-ldst.h @@ -0,0 +1,41 @@ +/* + * Internal memory barrier helpers for QEMU (target agnostic) + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_BACKEND_LDST_H +#define ACCEL_TCG_BACKEND_LDST_H + +#include "tcg-target-mo.h" + +/** + * tcg_req_mo: + * @guest_mo: Guest default memory order + * @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. + */ +#define tcg_req_mo(guest_mo, type) \ + ((type) & guest_mo & ~TCG_TARGET_DEFAULT_MO) + +/** + * cpu_req_mo: + * @cpu: CPUState + * @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(cpu, type) \ + do { \ + if (tcg_req_mo(cpu->cc->tcg_ops->guest_default_memory_order, type)) { \ + smp_mb(); \ + } \ + } while (0) + +#endif diff --git a/accel/tcg/cpu-exec-common.c b/accel/tcg/cpu-exec-common.c index 7e35d7f4b58..c5c513f1e4a 100644 --- a/accel/tcg/cpu-exec-common.c +++ b/accel/tcg/cpu-exec-common.c @@ -18,14 +18,45 @@ */ #include "qemu/osdep.h" -#include "sysemu/cpus.h" -#include "sysemu/tcg.h" -#include "exec/exec-all.h" +#include "exec/log.h" +#include "system/tcg.h" #include "qemu/plugin.h" -#include "internal.h" +#include "internal-common.h" bool tcg_allowed; +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; + + /* + * Record gdb single-step. We should be exiting the TB by raising + * EXCP_DEBUG, but to simplify other tests, disable chaining too. + * + * For singlestep and -d nochain, suppress goto_tb so that + * we can log -d cpu,exec after every TB. + */ + if (unlikely(cpu->singlestep_enabled)) { + cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | CF_SINGLE_STEP | 1; + } 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; + } + + return cflags; +} + /* exit the current TB, but without causing any exception to be raised */ void cpu_loop_exit_noexc(CPUState *cpu) { @@ -36,7 +67,7 @@ void cpu_loop_exit_noexc(CPUState *cpu) 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); diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index e2c494e75ef..713bdb20564 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -21,28 +21,30 @@ #include "qemu/qemu-print.h" #include "qapi/error.h" #include "qapi/type-helpers.h" -#include "hw/core/tcg-cpu-ops.h" +#include "hw/core/cpu.h" +#include "accel/tcg/cpu-ops.h" +#include "accel/tcg/helper-retaddr.h" #include "trace.h" #include "disas/disas.h" -#include "exec/exec-all.h" +#include "exec/cpu-common.h" +#include "exec/cpu-interrupt.h" +#include "exec/page-protection.h" +#include "exec/mmap-lock.h" +#include "exec/translation-block.h" #include "tcg/tcg.h" #include "qemu/atomic.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 "exec/icount.h" #include "exec/replay-core.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" #include "exec/helper-proto-common.h" #include "tb-jmp-cache.h" #include "tb-hash.h" #include "tb-context.h" -#include "internal.h" +#include "tb-internal.h" +#include "internal-common.h" /* -icount align implementation. */ @@ -73,7 +75,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; @@ -124,7 +126,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; } @@ -146,35 +148,10 @@ static void init_delay_params(SyncClocks *sc, const CPUState *cpu) } #endif /* CONFIG USER ONLY */ -uint32_t curr_cflags(CPUState *cpu) -{ - uint32_t cflags = cpu->tcg_cflags; - - /* - * Record gdb single-step. We should be exiting the TB by raising - * EXCP_DEBUG, but to simplify other tests, disable chaining too. - * - * For singlestep and -d nochain, suppress goto_tb so that - * we can log -d cpu,exec after every TB. - */ - if (unlikely(cpu->singlestep_enabled)) { - cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | CF_SINGLE_STEP | 1; - } 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; - } - - return cflags; -} - struct tb_desc { - vaddr pc; - uint64_t cs_base; + TCGTBCPUState s; CPUArchState *env; tb_page_addr_t page_addr0; - uint32_t flags; - uint32_t cflags; }; static bool tb_lookup_cmp(const void *p, const void *d) @@ -182,11 +159,11 @@ static bool tb_lookup_cmp(const void *p, const void *d) const TranslationBlock *tb = p; const struct tb_desc *desc = d; - if ((tb_cflags(tb) & CF_PCREL || tb->pc == desc->pc) && + if ((tb_cflags(tb) & CF_PCREL || tb->pc == desc->s.pc) && tb_page_addr0(tb) == desc->page_addr0 && - tb->cs_base == desc->cs_base && - tb->flags == desc->flags && - tb_cflags(tb) == desc->cflags) { + tb->cs_base == desc->s.cs_base && + tb->flags == desc->s.flags && + tb_cflags(tb) == desc->s.cflags) { /* check next page if needed */ tb_page_addr_t tb_phys_page1 = tb_page_addr1(tb); if (tb_phys_page1 == -1) { @@ -204,7 +181,7 @@ static bool tb_lookup_cmp(const void *p, const void *d) * is different for the new TB. Therefore any exception raised * here by the faulting lookup is not premature. */ - virt_page1 = TARGET_PAGE_ALIGN(desc->pc); + virt_page1 = TARGET_PAGE_ALIGN(desc->s.pc); phys_page1 = get_page_addr_code(desc->env, virt_page1); if (tb_phys_page1 == phys_page1) { return true; @@ -214,81 +191,73 @@ static bool tb_lookup_cmp(const void *p, const void *d) return false; } -static TranslationBlock *tb_htable_lookup(CPUState *cpu, vaddr pc, - uint64_t cs_base, uint32_t flags, - uint32_t cflags) +static TranslationBlock *tb_htable_lookup(CPUState *cpu, TCGTBCPUState s) { tb_page_addr_t phys_pc; struct tb_desc desc; uint32_t h; - desc.env = cpu->env_ptr; - desc.cs_base = cs_base; - desc.flags = flags; - desc.cflags = cflags; - desc.pc = pc; - phys_pc = get_page_addr_code(desc.env, pc); + desc.s = s; + desc.env = cpu_env(cpu); + phys_pc = get_page_addr_code(desc.env, s.pc); if (phys_pc == -1) { return NULL; } desc.page_addr0 = phys_pc; - h = tb_hash_func(phys_pc, (cflags & CF_PCREL ? 0 : pc), - flags, cs_base, cflags); + h = tb_hash_func(phys_pc, (s.cflags & CF_PCREL ? 0 : s.pc), + s.flags, s.cs_base, s.cflags); return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp); } -/* Might cause an exception, so have a longjmp destination ready */ -static inline TranslationBlock *tb_lookup(CPUState *cpu, vaddr pc, - uint64_t cs_base, uint32_t flags, - uint32_t cflags) +/** + * tb_lookup: + * @cpu: CPU that will execute the returned translation block + * @pc: guest PC + * @cs_base: arch-specific value associated with translation block + * @flags: arch-specific translation block flags + * @cflags: CF_* flags + * + * Look up a translation block inside the QHT using @pc, @cs_base, @flags and + * @cflags. Uses @cpu's tb_jmp_cache. Might cause an exception, so have a + * longjmp destination ready. + * + * Returns: an existing translation block or NULL. + */ +static inline TranslationBlock *tb_lookup(CPUState *cpu, TCGTBCPUState s) { TranslationBlock *tb; CPUJumpCache *jc; uint32_t hash; /* we should never be trying to look up an INVALID tb */ - tcg_debug_assert(!(cflags & CF_INVALID)); + tcg_debug_assert(!(s.cflags & CF_INVALID)); - hash = tb_jmp_cache_hash_func(pc); + hash = tb_jmp_cache_hash_func(s.pc); jc = cpu->tb_jmp_cache; - if (cflags & CF_PCREL) { - /* Use acquire to ensure current load of pc from jc. */ - tb = qatomic_load_acquire(&jc->array[hash].tb); + tb = qatomic_read(&jc->array[hash].tb); + if (likely(tb && + jc->array[hash].pc == s.pc && + tb->cs_base == s.cs_base && + tb->flags == s.flags && + tb_cflags(tb) == s.cflags)) { + goto hit; + } - if (likely(tb && - jc->array[hash].pc == pc && - tb->cs_base == cs_base && - tb->flags == flags && - tb_cflags(tb) == cflags)) { - return tb; - } - tb = tb_htable_lookup(cpu, pc, cs_base, flags, cflags); - if (tb == NULL) { - return NULL; - } - jc->array[hash].pc = pc; - /* Ensure pc is written first. */ - qatomic_store_release(&jc->array[hash].tb, tb); - } else { - /* Use rcu_read to ensure current load of pc from *tb. */ - tb = qatomic_rcu_read(&jc->array[hash].tb); - - if (likely(tb && - tb->pc == pc && - tb->cs_base == cs_base && - tb->flags == flags && - tb_cflags(tb) == cflags)) { - return tb; - } - tb = tb_htable_lookup(cpu, pc, cs_base, flags, cflags); - if (tb == NULL) { - return NULL; - } - /* Use the pc value already stored in tb->pc. */ - qatomic_set(&jc->array[hash].tb, tb); + tb = tb_htable_lookup(cpu, s); + if (tb == NULL) { + return NULL; } + jc->array[hash].pc = s.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 == s.pc); return tb; } @@ -305,14 +274,11 @@ static void log_cpu_exec(vaddr pc, CPUState *cpu, if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) { FILE *logfile = qemu_log_trylock(); if (logfile) { - int flags = 0; + int flags = CPU_DUMP_CCOP; if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) { flags |= CPU_DUMP_FPU; } -#if defined(TARGET_I386) - flags |= CPU_DUMP_CCOP; -#endif if (qemu_loglevel_mask(CPU_LOG_TB_VPU)) { flags |= CPU_DUMP_VPU; } @@ -356,9 +322,9 @@ static bool check_for_breakpoints_slow(CPUState *cpu, vaddr 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 } @@ -384,7 +350,7 @@ static bool check_for_breakpoints_slow(CPUState *cpu, vaddr 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; } @@ -408,29 +374,45 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env) { CPUState *cpu = env_cpu(env); TranslationBlock *tb; - vaddr pc; - uint64_t cs_base; - uint32_t flags, cflags; - cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); + /* + * 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; + + TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu); + s.cflags = curr_cflags(cpu); - cflags = curr_cflags(cpu); - if (check_for_breakpoints(cpu, pc, &cflags)) { + if (check_for_breakpoints(cpu, s.pc, &s.cflags)) { cpu_loop_exit(cpu); } - tb = tb_lookup(cpu, pc, cs_base, flags, cflags); + tb = tb_lookup(cpu, s); if (tb == NULL) { return tcg_code_gen_epilogue; } if (qemu_loglevel_mask(CPU_LOG_TB_CPU | CPU_LOG_EXEC)) { - log_cpu_exec(pc, cpu, tb); + log_cpu_exec(s.pc, cpu, tb); } return tb->tc.ptr; } +/* Return the current PC from CPU, which may be cached in TB. */ +static vaddr log_pc(CPUState *cpu, const TranslationBlock *tb) +{ + if (tb_cflags(tb) & CF_PCREL) { + return cpu->cc->get_pc(cpu); + } else { + return tb->pc; + } +} + /* Execute a TB, and fix up the CPU state afterwards if necessary */ /* * Disable CFI checks. @@ -444,7 +426,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; @@ -454,8 +435,8 @@ 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 @@ -475,10 +456,11 @@ 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 { tcg_debug_assert(!(tb_cflags(last_tb) & CF_PCREL)); assert(cc->set_pc); @@ -510,19 +492,19 @@ 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); } } @@ -557,19 +539,15 @@ static void cpu_exec_longjmp_cleanup(CPUState *cpu) tcg_ctx->gen_tb = NULL; } #endif - if (qemu_mutex_iothread_locked()) { - qemu_mutex_unlock_iothread(); + if (bql_locked()) { + bql_unlock(); } assert_no_pages_locked(); } void cpu_exec_step_atomic(CPUState *cpu) { - CPUArchState *env = cpu->env_ptr; TranslationBlock *tb; - vaddr pc; - uint64_t cs_base; - uint32_t flags, cflags; int tb_exit; if (sigsetjmp(cpu->jmp_env, 0) == 0) { @@ -578,13 +556,13 @@ void cpu_exec_step_atomic(CPUState *cpu) g_assert(!cpu->running); cpu->running = true; - cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); + TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu); + s.cflags = curr_cflags(cpu); - cflags = curr_cflags(cpu); /* Execute in a serial context. */ - cflags &= ~CF_PARALLEL; + s.cflags &= ~CF_PARALLEL; /* After 1 insn, return and release the exclusive lock. */ - cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | 1; + s.cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | 1; /* * No need to check_for_breakpoints here. * We only arrive in cpu_exec_step_atomic after beginning execution @@ -592,16 +570,16 @@ void cpu_exec_step_atomic(CPUState *cpu) * Any breakpoint for this insn will have been recognized earlier. */ - tb = tb_lookup(cpu, pc, cs_base, flags, cflags); + tb = tb_lookup(cpu, s); if (tb == NULL) { mmap_lock(); - tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); + tb = tb_gen_code(cpu, s); mmap_unlock(); } cpu_exec_enter(cpu); /* execute the generated code */ - trace_exec_tb(tb, pc); + trace_exec_tb(tb, s.pc); cpu_tb_exec(cpu, tb, &tb_exit); cpu_exec_exit(cpu); } else { @@ -669,23 +647,16 @@ static inline void tb_add_jump(TranslationBlock *tb, int n, out_unlock_next: qemu_spin_unlock(&tb_next->jmp_lock); - return; } 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; } @@ -698,7 +669,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) { @@ -707,8 +678,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); } } @@ -717,7 +688,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; @@ -725,6 +696,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; @@ -733,62 +705,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; + /* + * If user mode only, we simulate a fake exception which will be + * handled outside the cpu execution loop. + */ + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; + if (tcg_ops->fake_user_interrupt) { + tcg_ops->fake_user_interrupt(cpu); + } + *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 (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) @@ -807,11 +776,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_set_mb(&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. */ @@ -820,7 +789,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) @@ -831,38 +800,27 @@ 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(); - return true; - } -#if defined(TARGET_I386) - else if (interrupt_request & CPU_INTERRUPT_INIT) { - X86CPU *x86_cpu = X86_CPU(cpu); - CPUArchState *env = &x86_cpu->env; - replay_interrupt(); - 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(); - 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 */ - /* The target hook has 3 exit conditions: - False when the interrupt isn't processed, - 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); - - if (cc->tcg_ops->cpu_exec_interrupt && - cc->tcg_ops->cpu_exec_interrupt(cpu, interrupt_request)) { - if (need_replay_interrupt(interrupt_request)) { + } else { + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; + + if (interrupt_request & CPU_INTERRUPT_RESET) { + replay_interrupt(); + tcg_ops->cpu_exec_reset(cpu); + bql_unlock(); + return true; + } + + /* + * The target hook has 3 exit conditions: + * False when the interrupt isn't processed, + * True when it is, and we should restart on a new TB, + * and via longjmp via cpu_loop_exit. + */ + if (tcg_ops->cpu_exec_interrupt(cpu, interrupt_request)) { + if (!tcg_ops->need_replay_interrupt || + tcg_ops->need_replay_interrupt(interrupt_request)) { replay_interrupt(); } /* @@ -872,7 +830,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; @@ -891,14 +849,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; @@ -913,8 +868,6 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, 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) { @@ -923,8 +876,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 @@ -941,8 +893,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; /* @@ -972,11 +924,8 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc) while (!cpu_handle_interrupt(cpu, &last_tb)) { TranslationBlock *tb; - vaddr pc; - uint64_t cs_base; - uint32_t flags, cflags; - - cpu_get_tb_cpu_state(cpu->env_ptr, &pc, &cs_base, &flags); + TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu); + s.cflags = cpu->cflags_next_tb; /* * When requested, use an exact setting for cflags for the next @@ -985,40 +934,33 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc) * have CF_INVALID set, -1 is a convenient invalid value that * does not require tcg headers for cpu_common_reset. */ - cflags = cpu->cflags_next_tb; - if (cflags == -1) { - cflags = curr_cflags(cpu); + if (s.cflags == -1) { + s.cflags = curr_cflags(cpu); } else { cpu->cflags_next_tb = -1; } - if (check_for_breakpoints(cpu, pc, &cflags)) { + if (check_for_breakpoints(cpu, s.pc, &s.cflags)) { break; } - tb = tb_lookup(cpu, pc, cs_base, flags, cflags); + tb = tb_lookup(cpu, s); if (tb == NULL) { CPUJumpCache *jc; uint32_t h; mmap_lock(); - tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); + tb = tb_gen_code(cpu, s); mmap_unlock(); /* * We add the TB in the virtual pc hash table * for the fast lookup */ - h = tb_jmp_cache_hash_func(pc); + h = tb_jmp_cache_hash_func(s.pc); jc = cpu->tb_jmp_cache; - if (cflags & CF_PCREL) { - jc->array[h].pc = pc; - /* Ensure pc is written first. */ - qatomic_store_release(&jc->array[h].tb, tb); - } else { - /* Use the pc value already stored in tb->pc. */ - qatomic_set(&jc->array[h].tb, tb); - } + jc->array[h].pc = s.pc; + qatomic_set(&jc->array[h].tb, tb); } #ifndef CONFIG_USER_ONLY @@ -1037,7 +979,7 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc) tb_add_jump(last_tb, tb_exit, tb); } - cpu_loop_exec_tb(cpu, tb, pc, &last_tb, &tb_exit); + cpu_loop_exec_tb(cpu, tb, s.pc, &last_tb, &tb_exit); /* Try to align the host and virtual clocks if the guest is in advance */ @@ -1069,7 +1011,7 @@ int cpu_exec(CPUState *cpu) return EXCP_HALTED; } - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); cpu_exec_enter(cpu); /* @@ -1083,18 +1025,26 @@ int cpu_exec(CPUState *cpu) ret = cpu_exec_setjmp(cpu, &sc); cpu_exec_exit(cpu); - rcu_read_unlock(); - return ret; } -void tcg_exec_realizefn(CPUState *cpu, Error **errp) +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 */ + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; +#ifndef CONFIG_USER_ONLY + assert(tcg_ops->cpu_exec_halt); + assert(tcg_ops->cpu_exec_interrupt); + assert(tcg_ops->cpu_exec_reset); + assert(tcg_ops->pointer_wrap); +#endif /* !CONFIG_USER_ONLY */ + assert(tcg_ops->translate_code); + assert(tcg_ops->get_tb_cpu_state); + assert(tcg_ops->mmu_index); + tcg_ops->initialize(); tcg_target_initialized = true; } @@ -1104,6 +1054,8 @@ 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 */ diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index c643d661909..87e14bde4f2 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -19,28 +19,39 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" -#include "hw/core/tcg-cpu-ops.h" -#include "exec/exec-all.h" -#include "exec/memory.h" -#include "exec/cpu_ldst.h" +#include "qemu/target-info.h" +#include "accel/tcg/cpu-ops.h" +#include "accel/tcg/iommu.h" +#include "accel/tcg/probe.h" +#include "exec/page-protection.h" +#include "system/memory.h" +#include "accel/tcg/cpu-ldst-common.h" +#include "accel/tcg/cpu-mmu-index.h" #include "exec/cputlb.h" -#include "exec/memory-internal.h" -#include "exec/ram_addr.h" +#include "exec/tb-flush.h" +#include "system/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-common.h" +#include "exec/tlb-flags.h" #include "qemu/atomic.h" #include "qemu/atomic128.h" -#include "exec/translate-all.h" +#include "tb-internal.h" #include "trace.h" #include "tb-hash.h" -#include "internal.h" +#include "tb-internal.h" +#include "tlb-bounds.h" +#include "internal-common.h" #ifdef CONFIG_PLUGIN #include "qemu/plugin-memory.h" #endif #include "tcg/tcg-ldst.h" -#include "tcg/oversized-guest.h" +#include "backend-ldst.h" + /* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */ /* #define DEBUG_TLB */ @@ -93,6 +104,43 @@ 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(uintptr_t)); + QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_write) != + MMU_DATA_STORE * sizeof(uintptr_t)); + QEMU_BUILD_BUG_ON(offsetof(CPUTLBEntry, addr_code) != + MMU_INST_FETCH * sizeof(uintptr_t)); + + const uintptr_t *ptr = &entry->addr_idx[access_type]; + /* ofs might correspond to .addr_write, so use qatomic_read */ + return qatomic_read(ptr); +} + +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) { @@ -240,11 +288,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); @@ -262,41 +310,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); @@ -322,26 +368,8 @@ static void flush_all_helper(CPUState *src, run_on_cpu_func fn, } } -void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) -{ - CPUState *cpu; - size_t full = 0, part = 0, elide = 0; - - CPU_FOREACH(cpu) { - CPUArchState *env = cpu->env_ptr; - - full += qatomic_read(&env_tlb(env)->c.full_flush_count); - part += qatomic_read(&env_tlb(env)->c.part_flush_count); - elide += qatomic_read(&env_tlb(env)->c.elide_flush_count); - } - *pfull = full; - *ppart = part; - *pelide = elide; -} - static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) { - CPUArchState *env = cpu->env_ptr; uint16_t asked = data.host_int; uint16_t all_dirty, work, to_clean; int64_t now = get_clock_realtime(); @@ -350,32 +378,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)); } } } @@ -384,12 +412,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) @@ -397,21 +422,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; @@ -470,43 +480,43 @@ static inline bool tlb_flush_entry_locked(CPUTLBEntry *tlb_entry, vaddr page) } /* Called with tlb_c.lock held */ -static void tlb_flush_vtlb_page_mask_locked(CPUArchState *env, int mmu_idx, +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, +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, vaddr page) +static void tlb_flush_page_locked(CPUState *cpu, int midx, vaddr page) { - vaddr lp_addr = env_tlb(env)->d[midx].large_page_addr; - vaddr 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 (%016" VADDR_PRIx "/%016" VADDR_PRIx ")\n", midx, lp_addr, lp_mask); - tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime()); + tlb_flush_one_mmuidx_locked(cpu, midx, get_clock_realtime()); } else { - if (tlb_flush_entry_locked(tlb_entry(env, midx, page), page)) { - tlb_n_used_entries_dec(env, midx); + if (tlb_flush_entry_locked(tlb_entry(cpu, midx, page), page)) { + tlb_n_used_entries_dec(cpu, midx); } - tlb_flush_vtlb_page_locked(env, midx, page); + tlb_flush_vtlb_page_locked(cpu, midx, page); } } @@ -523,20 +533,19 @@ static void tlb_flush_page_by_mmuidx_async_0(CPUState *cpu, vaddr addr, uint16_t idxmap) { - CPUArchState *env = cpu->env_ptr; int mmu_idx; assert_cpu_is_self(cpu); 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 @@ -594,28 +603,12 @@ void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t 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, vaddr addr) @@ -623,46 +616,6 @@ 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, vaddr addr, - uint16_t idxmap) -{ - tlb_debug("addr: %016" VADDR_PRIx " 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, vaddr addr) -{ - tlb_flush_page_by_mmuidx_all_cpus(src, addr, ALL_MMUIDX_BITS); -} - void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu, vaddr addr, uint16_t idxmap) @@ -709,12 +662,12 @@ 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, +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]; + CPUTLBDesc *d = &cpu->neg.tlb.d[midx]; + CPUTLBDescFast *f = &cpu->neg.tlb.f[midx]; vaddr mask = MAKE_64BIT_MASK(0, bits); /* @@ -731,7 +684,7 @@ static void tlb_flush_range_locked(CPUArchState *env, int midx, tlb_debug("forcing full flush midx %d (" "%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; } @@ -744,18 +697,18 @@ static void tlb_flush_range_locked(CPUArchState *env, int midx, tlb_debug("forcing full flush midx %d (" "%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 (vaddr i = 0; i < len; i += TARGET_PAGE_SIZE) { vaddr page = addr + i; - CPUTLBEntry *entry = tlb_entry(env, midx, page); + 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); } } @@ -769,7 +722,6 @@ 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); @@ -777,13 +729,13 @@ static void tlb_flush_range_by_mmuidx_async_0(CPUState *cpu, 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 @@ -819,60 +771,19 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, { TLBFlushRangeData d; - /* - * 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(cpu, addr, idxmap); - return; - } + assert_cpu_is_self(cpu); + /* If no page bits are significant, this devolves to tlb_flush. */ if (bits < TARGET_PAGE_BITS) { tlb_flush_by_mmuidx(cpu, idxmap); return; } - - /* This should already be page aligned */ - d.addr = addr & TARGET_PAGE_MASK; - d.len = len; - 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)); - } -} - -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, - vaddr addr, vaddr 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); + if (len <= TARGET_PAGE_SIZE && bits >= target_long_bits()) { + tlb_flush_page_by_mmuidx(cpu, addr, idxmap); return; } @@ -882,25 +793,13 @@ void tlb_flush_range_by_mmuidx_all_cpus(CPUState *src_cpu, 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); + tlb_flush_range_by_mmuidx_async_0(cpu, d); } -void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *src_cpu, - vaddr addr, uint16_t idxmap, - unsigned bits) +void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, + uint16_t idxmap, unsigned bits) { - tlb_flush_range_by_mmuidx_all_cpus(src_cpu, addr, TARGET_PAGE_SIZE, - idxmap, bits); + tlb_flush_range_by_mmuidx(cpu, addr, TARGET_PAGE_SIZE, idxmap, bits); } void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu, @@ -912,19 +811,19 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu, TLBFlushRangeData d, *p; CPUState *dst_cpu; + /* If no page bits are significant, this devolves to tlb_flush. */ + if (bits < TARGET_PAGE_BITS) { + tlb_flush_by_mmuidx_all_cpus_synced(src_cpu, idxmap); + return; + } /* * If all bits are significant, and len is small, * this devolves to tlb_flush_page. */ - if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) { + if (len <= TARGET_PAGE_SIZE && bits >= target_long_bits()) { tlb_flush_page_by_mmuidx_all_cpus_synced(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_synced(src_cpu, idxmap); - return; - } /* This should already be page aligned */ d.addr = addr & TARGET_PAGE_MASK; @@ -988,26 +887,17 @@ void tlb_unprotect_code(ram_addr_t ram_addr) * * Called with tlb_c.lock held. */ -static void tlb_reset_dirty_range_locked(CPUTLBEntry *tlb_entry, +static void tlb_reset_dirty_range_locked(CPUTLBEntryFull *full, CPUTLBEntry *ent, uintptr_t start, uintptr_t length) { - uintptr_t addr = tlb_entry->addr_write; - - if ((addr & (TLB_INVALID_MASK | TLB_MMIO | - TLB_DISCARD_WRITE | TLB_NOTDIRTY)) == 0) { - addr &= TARGET_PAGE_MASK; - addr += tlb_entry->addend; - if ((addr - start) < length) { -#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); -#endif + const uintptr_t addr = ent->addr_write; + int flags = addr | full->slow_flags[MMU_DATA_STORE]; + + flags &= TLB_INVALID_MASK | TLB_MMIO | TLB_DISCARD_WRITE | TLB_NOTDIRTY; + if (flags == 0) { + uintptr_t host = (addr & TARGET_PAGE_MASK) + ent->addend; + if ((host - start) < length) { + qatomic_set(&ent->addr_write, addr | TLB_NOTDIRTY); } } } @@ -1026,29 +916,28 @@ static inline void copy_tlb_helper_locked(CPUTLBEntry *d, const CPUTLBEntry *s) * We must take tlb_c.lock to avoid racing with another vCPU update. The only * thing actually updated is the target TLB entry ->addr_write flags. */ -void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length) +void tlb_reset_dirty(CPUState *cpu, uintptr_t start, uintptr_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++) { + CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx]; + CPUTLBDescFast *fast = &cpu->neg.tlb.f[mmu_idx]; + unsigned int n = tlb_n_entries(fast); unsigned int i; - unsigned int n = tlb_n_entries(&env_tlb(env)->f[mmu_idx]); for (i = 0; i < n; i++) { - tlb_reset_dirty_range_locked(&env_tlb(env)->f[mmu_idx].table[i], - start1, length); + tlb_reset_dirty_range_locked(&desc->fulltlb[i], &fast->table[i], + start, length); } for (i = 0; i < CPU_VTLB_SIZE; i++) { - tlb_reset_dirty_range_locked(&env_tlb(env)->d[mmu_idx].vtable[i], - start1, length); + tlb_reset_dirty_range_locked(&desc->vfulltlb[i], &desc->vtable[i], + start, length); } } - qemu_spin_unlock(&env_tlb(env)->c.lock); + qemu_spin_unlock(&cpu->neg.tlb.c.lock); } /* Called with tlb_c.lock held */ @@ -1062,34 +951,33 @@ static inline void tlb_set_dirty1_locked(CPUTLBEntry *tlb_entry, /* update the TLB corresponding to virtual page vaddr so that it is no longer dirty */ -void tlb_set_dirty(CPUState *cpu, vaddr addr) +static void tlb_set_dirty(CPUState *cpu, vaddr addr) { - CPUArchState *env = cpu->env_ptr; int mmu_idx; assert_cpu_is_self(cpu); addr &= TARGET_PAGE_MASK; - 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++) { - tlb_set_dirty1_locked(tlb_entry(env, mmu_idx, addr), addr); + 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], addr); + 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, +static void tlb_add_large_page(CPUState *cpu, int mmu_idx, vaddr addr, uint64_t size) { - vaddr lp_addr = env_tlb(env)->d[mmu_idx].large_page_addr; + vaddr lp_addr = cpu->neg.tlb.d[mmu_idx].large_page_addr; vaddr lp_mask = ~(size - 1); if (lp_addr == (vaddr)-1) { @@ -1099,13 +987,13 @@ static void tlb_add_large_page(CPUArchState *env, int mmu_idx, /* 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; + 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, @@ -1137,8 +1025,7 @@ static inline void tlb_set_compare(CPUTLBEntryFull *full, CPUTLBEntry *ent, void tlb_set_page_full(CPUState *cpu, int mmu_idx, vaddr addr, CPUTLBEntryFull *full) { - CPUArchState *env = cpu->env_ptr; - CPUTLB *tlb = env_tlb(env); + CPUTLB *tlb = &cpu->neg.tlb; CPUTLBDesc *desc = &tlb->d[mmu_idx]; MemoryRegionSection *section; unsigned int index, read_flags, write_flags; @@ -1155,7 +1042,7 @@ 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, addr, sz); + tlb_add_large_page(cpu, mmu_idx, addr, sz); } addr_page = addr & TARGET_PAGE_MASK; paddr_page = full->phys_addr & TARGET_PAGE_MASK; @@ -1170,14 +1057,11 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, " prot=%x idx=%d\n", addr, full->phys_addr, prot, mmu_idx); - read_flags = 0; + read_flags = full->tlb_fill_flags; if (full->lg_page_size < TARGET_PAGE_BITS) { /* Repeat the MMU check and TLB fill on every access. */ read_flags |= TLB_INVALID_MASK; } - if (full->attrs.byte_swap) { - read_flags |= TLB_BSWAP; - } is_ram = memory_region_is_ram(section->mr); is_romd = memory_region_is_romd(section->mr); @@ -1193,6 +1077,7 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, 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. @@ -1221,8 +1106,8 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, wp_flags = cpu_watchpoint_address_matches(cpu, addr_page, TARGET_PAGE_SIZE); - index = tlb_index(env, mmu_idx, addr_page); - te = tlb_entry(env, mmu_idx, addr_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 @@ -1237,7 +1122,7 @@ 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, addr_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 @@ -1250,21 +1135,23 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, /* 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) + * 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; full = &desc->fulltlb[index]; @@ -1293,13 +1180,13 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, 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, vaddr addr, hwaddr paddr, MemTxAttrs attrs, int prot, - int mmu_idx, uint64_t size) + int mmu_idx, vaddr size) { CPUTLBEntryFull full = { .phys_addr = paddr, @@ -1314,176 +1201,131 @@ void tlb_set_page_with_attrs(CPUState *cpu, vaddr addr, void tlb_set_page(CPUState *cpu, vaddr addr, hwaddr paddr, int prot, - int mmu_idx, uint64_t size) + int mmu_idx, vaddr size) { 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()). +/** + * tlb_hit_page: return true if page aligned @addr is a hit against the + * TLB entry @tlb_addr + * + * @addr: virtual address to test (must be page aligned) + * @tlb_addr: TLB entry address (a CPUTLBEntry addr_read/write/code value) */ -static void tlb_fill(CPUState *cpu, vaddr addr, int size, - MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) +static inline bool tlb_hit_page(uint64_t tlb_addr, vaddr addr) { - bool ok; - - /* - * 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); + return addr == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)); } -static inline void cpu_unaligned_access(CPUState *cpu, vaddr addr, - MMUAccessType access_type, - int mmu_idx, uintptr_t retaddr) +/** + * tlb_hit: return true if @addr is a hit against the TLB entry @tlb_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(uint64_t tlb_addr, vaddr addr) { - cpu->cc->tcg_ops->do_unaligned_access(cpu, addr, access_type, - mmu_idx, retaddr); + return tlb_hit_page(tlb_addr, addr & TARGET_PAGE_MASK); } -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) +/* + * 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 bool tlb_fill_align(CPUState *cpu, vaddr addr, MMUAccessType type, + int mmu_idx, MemOp memop, int size, + bool probe, uintptr_t ra) { - CPUClass *cc = CPU_GET_CLASS(cpu); + const TCGCPUOps *ops = cpu->cc->tcg_ops; + CPUTLBEntryFull full; - 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); + 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; } -/* - * 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 inline void cpu_unaligned_access(CPUState *cpu, vaddr addr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr) { -#ifdef CONFIG_PLUGIN - SavedIOTLB *saved = &cs->saved_iotlb; - saved->section = section; - saved->mr_offset = mr_offset; -#endif + cpu->cc->tcg_ops->do_unaligned_access(cpu, addr, access_type, + mmu_idx, retaddr); } -static uint64_t io_readx(CPUArchState *env, CPUTLBEntryFull *full, - int mmu_idx, vaddr addr, uintptr_t retaddr, - MMUAccessType access_type, MemOp op) +static MemoryRegionSection * +io_prepare(hwaddr *out_offset, CPUState *cpu, hwaddr xlat, + MemTxAttrs attrs, vaddr addr, uintptr_t retaddr) { - CPUState *cpu = env_cpu(env); - hwaddr mr_offset; MemoryRegionSection *section; - MemoryRegion *mr; - uint64_t val; - 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); } - /* - * 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); - - { - QEMU_IOTHREAD_LOCK_GUARD(); - 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); - } - return val; + *out_offset = mr_offset; + return section; } -static void io_writex(CPUArchState *env, CPUTLBEntryFull *full, - int mmu_idx, uint64_t val, vaddr addr, - uintptr_t retaddr, MemOp op) +static void io_failed(CPUState *cpu, CPUTLBEntryFull *full, vaddr addr, + unsigned size, MMUAccessType access_type, int mmu_idx, + MemTxResult response, uintptr_t retaddr) { - CPUState *cpu = env_cpu(env); - hwaddr mr_offset; - MemoryRegionSection *section; - MemoryRegion *mr; - MemTxResult r; + if (!cpu->ignore_memory_transaction_failures + && cpu->cc->tcg_ops->do_transaction_failed) { + hwaddr physaddr = full->phys_addr | (addr & ~TARGET_PAGE_MASK); - 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->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); - - { - QEMU_IOTHREAD_LOCK_GUARD(); - 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); + cpu->cc->tcg_ops->do_transaction_failed(cpu, physaddr, addr, size, + access_type, mmu_idx, + full->attrs, response, retaddr); } } /* 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, +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]; + 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,7 +1342,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)) { - tb_invalidate_phys_range_fast(ram_addr, size, retaddr); + tb_invalidate_phys_range_fast(cpu, ram_addr, size, retaddr); } /* @@ -1516,40 +1358,38 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size, } } -static int probe_access_internal(CPUArchState *env, vaddr 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, bool check_mem_cbs) { - uintptr_t index = tlb_index(env, mmu_idx, addr); - CPUTLBEntry *entry = tlb_entry(env, mmu_idx, 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); 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(env_cpu(env)); + bool force_mmio = check_mem_cbs && cpu_plugin_mem_cbs_enabled(cpu); CPUTLBEntryFull *full; if (!tlb_hit_page(tlb_addr, page_addr)) { - if (!victim_tlb_hit(env, mmu_idx, index, access_type, 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; } @@ -1557,13 +1397,12 @@ static int probe_access_internal(CPUArchState *env, vaddr addr, } flags &= tlb_addr; - *pfull = full = &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)) - || - (access_type != MMU_INST_FETCH && force_mmio)) { + if (unlikely(flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY | TLB_CHECK_ALIGNED)) + || (access_type != MMU_INST_FETCH && force_mmio)) { *phost = NULL; return TLB_MMIO; } @@ -1578,12 +1417,14 @@ int probe_access_full(CPUArchState *env, vaddr addr, int size, bool nonfault, void **phost, CPUTLBEntryFull **pfull, uintptr_t retaddr) { - int flags = probe_access_internal(env, addr, size, access_type, mmu_idx, - nonfault, phost, pfull, retaddr, true); + 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; } @@ -1601,12 +1442,13 @@ int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, phost = phost ? phost : &discard_phost; pfull = pfull ? pfull : &discard_tlb; - int flags = probe_access_internal(env, addr, size, access_type, mmu_idx, - true, phost, pfull, 0, false); + 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)) { - notdirty_write(env_cpu(env), addr, 1, *pfull, 0); + int dirtysize = size == 0 ? 1 : size; + notdirty_write(env_cpu(env), addr, dirtysize, *pfull, 0); flags &= ~TLB_NOTDIRTY; } @@ -1622,12 +1464,14 @@ int probe_access_flags(CPUArchState *env, vaddr addr, int size, g_assert(-(addr | TARGET_PAGE_MASK) >= size); - flags = probe_access_internal(env, addr, size, access_type, mmu_idx, - nonfault, phost, &full, retaddr, true); + 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)) { - notdirty_write(env_cpu(env), addr, 1, full, retaddr); + int dirtysize = size == 0 ? 1 : size; + notdirty_write(env_cpu(env), addr, dirtysize, full, retaddr); flags &= ~TLB_NOTDIRTY; } @@ -1643,8 +1487,9 @@ void *probe_access(CPUArchState *env, vaddr 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, true); + 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) { @@ -1662,21 +1507,21 @@ void *probe_access(CPUArchState *env, vaddr 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); } } return host; } -void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr, +void *tlb_vaddr_to_host(CPUArchState *env, vaddr addr, MMUAccessType access_type, int mmu_idx) { CPUTLBEntryFull *full; void *host; int flags; - flags = probe_access_internal(env, addr, 0, access_type, + 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. */ @@ -1699,8 +1544,8 @@ tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, CPUTLBEntryFull *full; void *p; - (void)probe_access_internal(env, addr, 1, MMU_INST_FETCH, - cpu_mmu_index(env, true), false, + (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; @@ -1726,45 +1571,40 @@ tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr 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, 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); - uint64_t tlb_addr = is_store ? tlb_addr_write(tlbe) : tlbe->addr_read; - - 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; + 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 (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->v.io.section = saved->section; - data->v.io.offset = saved->mr_offset; - return true; + data->mr = section->mr; + } else { + data->is_io = false; + data->mr = NULL; } + return true; } - #endif /* @@ -1788,23 +1628,24 @@ typedef struct MMULookupLocals { /** * mmu_lookup1: translate one page - * @env: cpu context + * @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 will longjmp out. Return true if the softmmu tlb for + * tlb_fill_align will longjmp out. Return true if the softmmu tlb for * @mmu_idx may have resized. */ -static bool mmu_lookup1(CPUArchState *env, MMULookupPageData *data, +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(env, mmu_idx, addr); - CPUTLBEntry *entry = tlb_entry(env, mmu_idx, 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; @@ -1812,20 +1653,40 @@ static bool mmu_lookup1(CPUArchState *env, MMULookupPageData *data, /* 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, access_type, + if (!victim_tlb_hit(cpu, mmu_idx, index, access_type, addr & TARGET_PAGE_MASK)) { - tlb_fill(env_cpu(env), addr, data->size, access_type, mmu_idx, ra); + tlb_fill_align(cpu, addr, access_type, mmu_idx, + memop, data->size, false, ra); maybe_resized = true; - index = tlb_index(env, mmu_idx, addr); - entry = tlb_entry(env, mmu_idx, addr); + 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 = &env_tlb(env)->d[mmu_idx].fulltlb[index]; + 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. */ @@ -1836,7 +1697,7 @@ static bool mmu_lookup1(CPUArchState *env, MMULookupPageData *data, /** * mmu_watch_or_dirty - * @env: cpu context + * @cpu: generic cpu state * @data: lookup parameters * @access_type: load/store/code * @ra: return address into tcg generated code, or 0 @@ -1844,7 +1705,7 @@ static bool mmu_lookup1(CPUArchState *env, MMULookupPageData *data, * Trigger watchpoints for @data.addr:@data.size; * record writes to protected clean pages. */ -static void mmu_watch_or_dirty(CPUArchState *env, MMULookupPageData *data, +static void mmu_watch_or_dirty(CPUState *cpu, MMULookupPageData *data, MMUAccessType access_type, uintptr_t ra) { CPUTLBEntryFull *full = data->full; @@ -1855,13 +1716,13 @@ static void mmu_watch_or_dirty(CPUArchState *env, MMULookupPageData *data, /* On watchpoint hit, this will longjmp out. */ if (flags & TLB_WATCHPOINT) { int wp = access_type == MMU_DATA_STORE ? BP_MEM_WRITE : BP_MEM_READ; - cpu_check_watchpoint(env_cpu(env), addr, size, full->attrs, wp, ra); + 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(env_cpu(env), addr, size, full, ra); + notdirty_write(cpu, addr, size, full, ra); flags &= ~TLB_NOTDIRTY; } data->flags = flags; @@ -1869,7 +1730,7 @@ static void mmu_watch_or_dirty(CPUArchState *env, MMULookupPageData *data, /** * mmu_lookup: translate page(s) - * @env: cpu context + * @cpu: generic cpu state * @addr: virtual address * @oi: combined mmu_idx and MemOp * @ra: return address into tcg generated code, or 0 @@ -1879,10 +1740,9 @@ static void mmu_watch_or_dirty(CPUArchState *env, MMULookupPageData *data, * 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(CPUArchState *env, vaddr addr, MemOpIdx oi, +static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi, uintptr_t ra, MMUAccessType type, MMULookupLocals *l) { - unsigned a_bits; bool crosspage; int flags; @@ -1891,12 +1751,6 @@ static bool mmu_lookup(CPUArchState *env, vaddr addr, MemOpIdx oi, tcg_debug_assert(l->mmu_idx < NB_MMU_MODES); - /* Handle CPU specific unaligned behaviour */ - a_bits = get_alignment_bits(l->memop); - if (addr & ((1 << a_bits) - 1)) { - cpu_unaligned_access(env_cpu(env), addr, type, l->mmu_idx, ra); - } - l->page[0].addr = addr; l->page[0].size = memop_size(l->memop); l->page[1].addr = (addr + l->page[0].size - 1) & TARGET_PAGE_MASK; @@ -1904,11 +1758,11 @@ static bool mmu_lookup(CPUArchState *env, vaddr addr, MemOpIdx oi, crosspage = (addr ^ l->page[1].addr) & TARGET_PAGE_MASK; if (likely(!crosspage)) { - mmu_lookup1(env, &l->page[0], l->mmu_idx, type, ra); + 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(env, &l->page[0], type, ra); + mmu_watch_or_dirty(cpu, &l->page[0], type, ra); } if (unlikely(flags & TLB_BSWAP)) { l->memop ^= MO_BSWAP; @@ -1919,20 +1773,23 @@ static bool mmu_lookup(CPUArchState *env, vaddr addr, MemOpIdx oi, l->page[1].size = l->page[0].size - size0; l->page[0].size = size0; + l->page[1].addr = cpu->cc->tcg_ops->pointer_wrap(cpu, l->mmu_idx, + l->page[1].addr, addr); + /* * Lookup both pages, recognizing exceptions from either. If the * second lookup potentially resized, refresh first CPUTLBEntryFull. */ - mmu_lookup1(env, &l->page[0], l->mmu_idx, type, ra); - if (mmu_lookup1(env, &l->page[1], l->mmu_idx, type, ra)) { - uintptr_t index = tlb_index(env, l->mmu_idx, addr); - l->page[0].full = &env_tlb(env)->d[l->mmu_idx].fulltlb[index]; + 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(env, &l->page[0], type, ra); - mmu_watch_or_dirty(env, &l->page[1], type, ra); + mmu_watch_or_dirty(cpu, &l->page[0], type, ra); + mmu_watch_or_dirty(cpu, &l->page[1], type, ra); } /* @@ -1950,51 +1807,36 @@ static bool mmu_lookup(CPUArchState *env, vaddr addr, MemOpIdx oi, * Probe for an atomic operation. Do not allow unaligned operations, * or io operations to proceed. Return the host address. */ -static void *atomic_mmu_lookup(CPUArchState *env, vaddr addr, MemOpIdx oi, +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; 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); - } - - /* 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. */ - goto stop_the_world; - } - - index = tlb_index(env, mmu_idx, addr); - tlbe = tlb_entry(env, mmu_idx, addr); + index = tlb_index(cpu, mmu_idx, addr); + tlbe = tlb_entry(cpu, mmu_idx, addr); /* Check TLB entry and enforce page permissions. */ tlb_addr = tlb_addr_write(tlbe); if (!tlb_hit(tlb_addr, addr)) { - if (!victim_tlb_hit(env, mmu_idx, index, MMU_DATA_STORE, + if (!victim_tlb_hit(cpu, mmu_idx, index, MMU_DATA_STORE, addr & TARGET_PAGE_MASK)) { - 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_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; } @@ -2006,17 +1848,38 @@ static void *atomic_mmu_lookup(CPUArchState *env, vaddr addr, MemOpIdx oi, * but addr_read will only be -1 if PAGE_READ was unset. */ if (unlikely(tlbe->addr_read == -1)) { - tlb_fill(env_cpu(env), addr, size, MMU_DATA_LOAD, mmu_idx, retaddr); + 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. But just in case, - * handle via stop-the-world. + * 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 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; } - /* Collect tlb flags for read. */ + + /* Finish collecting tlb flags for both read and write. */ + full = &cpu->neg.tlb.d[mmu_idx].fulltlb[index]; tlb_addr |= tlbe->addr_read; + tlb_addr &= TLB_FLAGS_MASK & ~TLB_FORCE_SLOW; + tlb_addr |= full->slow_flags[MMU_DATA_STORE]; + tlb_addr |= full->slow_flags[MMU_DATA_LOAD]; /* Notice an IO access or a needs-MMU-lookup access */ if (unlikely(tlb_addr & (TLB_MMIO | TLB_DISCARD_WRITE))) { @@ -2026,13 +1889,12 @@ static void *atomic_mmu_lookup(CPUArchState *env, vaddr addr, MemOpIdx oi, } hostaddr = (void *)((uintptr_t)addr + tlbe->addend); - full = &env_tlb(env)->d[mmu_idx].fulltlb[index]; if (unlikely(tlb_addr & TLB_NOTDIRTY)) { - notdirty_write(env_cpu(env), addr, size, full, retaddr); + notdirty_write(cpu, addr, size, full, retaddr); } - if (unlikely(tlb_addr & TLB_FORCE_SLOW)) { + if (unlikely(tlb_addr & TLB_WATCHPOINT)) { int wp_flags = 0; if (full->slow_flags[MMU_DATA_STORE] & TLB_WATCHPOINT) { @@ -2041,16 +1903,14 @@ static void *atomic_mmu_lookup(CPUArchState *env, vaddr addr, MemOpIdx oi, if (full->slow_flags[MMU_DATA_LOAD] & TLB_WATCHPOINT) { wp_flags |= BP_MEM_READ; } - if (wp_flags) { - cpu_check_watchpoint(env_cpu(env), addr, size, - full->attrs, wp_flags, retaddr); - } + cpu_check_watchpoint(cpu, addr, size, + full->attrs, wp_flags, retaddr); } return hostaddr; stop_the_world: - cpu_loop_exit_atomic(env_cpu(env), retaddr); + cpu_loop_exit_atomic(cpu, retaddr); } /* @@ -2072,59 +1932,96 @@ static void *atomic_mmu_lookup(CPUArchState *env, vaddr addr, MemOpIdx oi, /** * do_ld_mmio_beN: - * @env: cpu context + * @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: iothread lock held + * 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 do_ld_mmio_beN(CPUArchState *env, CPUTLBEntryFull *full, - uint64_t ret_be, vaddr addr, int size, - int mmu_idx, MMUAccessType type, uintptr_t ra) +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) { - uint64_t t; - - tcg_debug_assert(size > 0 && size <= 8); do { + MemOp this_mop; + unsigned this_size; + uint64_t val; + MemTxResult r; + /* Read aligned pieces up to 8 bytes. */ - switch ((size | (int)addr) & 7) { - case 1: - case 3: - case 5: - case 7: - t = io_readx(env, full, mmu_idx, addr, ra, type, MO_UB); - ret_be = (ret_be << 8) | t; - size -= 1; - addr += 1; - break; - case 2: - case 6: - t = io_readx(env, full, mmu_idx, addr, ra, type, MO_BEUW); - ret_be = (ret_be << 16) | t; - size -= 2; - addr += 2; - break; - case 4: - t = io_readx(env, full, mmu_idx, addr, ra, type, MO_BEUL); - ret_be = (ret_be << 32) | t; - size -= 4; - addr += 4; - break; - case 0: - return io_readx(env, full, mmu_idx, addr, ra, type, MO_BEUQ); - default: - qemu_build_not_reached(); + 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; } +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) +{ + 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 Int128 do_ld16_mmio_beN(CPUState *cpu, CPUTLBEntryFull *full, + uint64_t ret_be, vaddr addr, int size, + int mmu_idx, uintptr_t ra) +{ + 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); +} + /** * do_ld_bytes_beN * @p: translation parameters @@ -2219,11 +2116,11 @@ static uint64_t do_ld_whole_be4(MMULookupPageData *p, uint64_t ret_be) * 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(CPUArchState *env, uintptr_t ra, +static uint64_t do_ld_whole_be8(CPUState *cpu, uintptr_t ra, MMULookupPageData *p, uint64_t ret_be) { int o = p->addr & 7; - uint64_t x = load_atomic8_or_exit(env, ra, p->haddr - o); + uint64_t x = load_atomic8_or_exit(cpu, ra, p->haddr - o); x = cpu_to_be64(x); x <<= o * 8; @@ -2239,11 +2136,11 @@ static uint64_t do_ld_whole_be8(CPUArchState *env, uintptr_t ra, * 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(CPUArchState *env, uintptr_t ra, +static Int128 do_ld_whole_be16(CPUState *cpu, uintptr_t ra, MMULookupPageData *p, uint64_t ret_be) { int o = p->addr & 15; - Int128 x, y = load_atomic16_or_exit(env, ra, p->haddr - o); + Int128 x, y = load_atomic16_or_exit(cpu, ra, p->haddr - o); int size = p->size; if (!HOST_BIG_ENDIAN) { @@ -2259,7 +2156,7 @@ static Int128 do_ld_whole_be16(CPUArchState *env, uintptr_t ra, /* * Wrapper for the above. */ -static uint64_t do_ld_beN(CPUArchState *env, MMULookupPageData *p, +static uint64_t do_ld_beN(CPUState *cpu, MMULookupPageData *p, uint64_t ret_be, int mmu_idx, MMUAccessType type, MemOp mop, uintptr_t ra) { @@ -2267,8 +2164,7 @@ static uint64_t do_ld_beN(CPUArchState *env, MMULookupPageData *p, unsigned tmp, half_size; if (unlikely(p->flags & TLB_MMIO)) { - QEMU_IOTHREAD_LOCK_GUARD(); - return do_ld_mmio_beN(env, p->full, ret_be, p->addr, p->size, + return do_ld_mmio_beN(cpu, p->full, ret_be, p->addr, p->size, mmu_idx, type, ra); } @@ -2292,7 +2188,7 @@ static uint64_t do_ld_beN(CPUArchState *env, MMULookupPageData *p, if (!HAVE_al8_fast && p->size < 4) { return do_ld_whole_be4(p, ret_be); } else { - return do_ld_whole_be8(env, ra, p, ret_be); + return do_ld_whole_be8(cpu, ra, p, ret_be); } } /* fall through */ @@ -2310,7 +2206,7 @@ static uint64_t do_ld_beN(CPUArchState *env, MMULookupPageData *p, /* * Wrapper for the above, for 8 < size < 16. */ -static Int128 do_ld16_beN(CPUArchState *env, MMULookupPageData *p, +static Int128 do_ld16_beN(CPUState *cpu, MMULookupPageData *p, uint64_t a, int mmu_idx, MemOp mop, uintptr_t ra) { int size = p->size; @@ -2318,12 +2214,7 @@ static Int128 do_ld16_beN(CPUArchState *env, MMULookupPageData *p, MemOp atom; if (unlikely(p->flags & TLB_MMIO)) { - QEMU_IOTHREAD_LOCK_GUARD(); - a = do_ld_mmio_beN(env, p->full, a, p->addr, size - 8, - mmu_idx, MMU_DATA_LOAD, ra); - b = do_ld_mmio_beN(env, p->full, 0, p->addr + 8, 8, - mmu_idx, MMU_DATA_LOAD, ra); - return int128_make128(b, a); + return do_ld16_mmio_beN(cpu, p->full, a, p->addr, size, mmu_idx, ra); } /* @@ -2342,7 +2233,7 @@ static Int128 do_ld16_beN(CPUArchState *env, MMULookupPageData *p, case MO_ATOM_WITHIN16_PAIR: /* Since size > 8, this is the half that must be atomic. */ - return do_ld_whole_be16(env, ra, p, a); + return do_ld_whole_be16(cpu, ra, p, a); case MO_ATOM_IFALIGN_PAIR: /* @@ -2364,30 +2255,29 @@ static Int128 do_ld16_beN(CPUArchState *env, MMULookupPageData *p, return int128_make128(b, a); } -static uint8_t do_ld_1(CPUArchState *env, MMULookupPageData *p, int mmu_idx, +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 io_readx(env, p->full, mmu_idx, p->addr, ra, type, MO_UB); + 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(CPUArchState *env, MMULookupPageData *p, int mmu_idx, +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)) { - QEMU_IOTHREAD_LOCK_GUARD(); - ret = do_ld_mmio_beN(env, p->full, 0, p->addr, 2, mmu_idx, type, ra); + 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(env, ra, p->haddr, memop); + ret = load_atom_2(cpu, ra, p->haddr, memop); if (memop & MO_BSWAP) { ret = bswap16(ret); } @@ -2395,20 +2285,19 @@ static uint16_t do_ld_2(CPUArchState *env, MMULookupPageData *p, int mmu_idx, return ret; } -static uint32_t do_ld_4(CPUArchState *env, MMULookupPageData *p, int mmu_idx, +static uint32_t do_ld_4(CPUState *cpu, MMULookupPageData *p, int mmu_idx, MMUAccessType type, MemOp memop, uintptr_t ra) { uint32_t ret; if (unlikely(p->flags & TLB_MMIO)) { - QEMU_IOTHREAD_LOCK_GUARD(); - ret = do_ld_mmio_beN(env, p->full, 0, p->addr, 4, mmu_idx, type, ra); + 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(env, ra, p->haddr, memop); + ret = load_atom_4(cpu, ra, p->haddr, memop); if (memop & MO_BSWAP) { ret = bswap32(ret); } @@ -2416,20 +2305,19 @@ static uint32_t do_ld_4(CPUArchState *env, MMULookupPageData *p, int mmu_idx, return ret; } -static uint64_t do_ld_8(CPUArchState *env, MMULookupPageData *p, int mmu_idx, +static uint64_t do_ld_8(CPUState *cpu, MMULookupPageData *p, int mmu_idx, MMUAccessType type, MemOp memop, uintptr_t ra) { uint64_t ret; if (unlikely(p->flags & TLB_MMIO)) { - QEMU_IOTHREAD_LOCK_GUARD(); - ret = do_ld_mmio_beN(env, p->full, 0, p->addr, 8, mmu_idx, type, ra); + 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(env, ra, p->haddr, memop); + ret = load_atom_8(cpu, ra, p->haddr, memop); if (memop & MO_BSWAP) { ret = bswap64(ret); } @@ -2437,27 +2325,20 @@ static uint64_t do_ld_8(CPUArchState *env, MMULookupPageData *p, int mmu_idx, return ret; } -static uint8_t do_ld1_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, +static uint8_t do_ld1_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, uintptr_t ra, MMUAccessType access_type) { MMULookupLocals l; bool crosspage; - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); - crosspage = mmu_lookup(env, addr, oi, ra, access_type, &l); + cpu_req_mo(cpu, 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(env, &l.page[0], l.mmu_idx, access_type, ra); -} - -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, addr, oi, retaddr, MMU_DATA_LOAD); + return do_ld_1(cpu, &l.page[0], l.mmu_idx, access_type, ra); } -static uint16_t do_ld2_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, +static uint16_t do_ld2_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, uintptr_t ra, MMUAccessType access_type) { MMULookupLocals l; @@ -2465,14 +2346,14 @@ static uint16_t do_ld2_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uint16_t ret; uint8_t a, b; - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); - crosspage = mmu_lookup(env, addr, oi, ra, access_type, &l); + cpu_req_mo(cpu, 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(env, &l.page[0], l.mmu_idx, access_type, l.memop, ra); + return do_ld_2(cpu, &l.page[0], l.mmu_idx, access_type, l.memop, ra); } - a = do_ld_1(env, &l.page[0], l.mmu_idx, access_type, ra); - b = do_ld_1(env, &l.page[1], l.mmu_idx, access_type, 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); @@ -2482,93 +2363,49 @@ static uint16_t do_ld2_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, return ret; } -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, addr, oi, retaddr, MMU_DATA_LOAD); -} - -static uint32_t do_ld4_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, +static uint32_t do_ld4_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, uintptr_t ra, MMUAccessType access_type) { MMULookupLocals l; bool crosspage; uint32_t ret; - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); - crosspage = mmu_lookup(env, addr, oi, ra, access_type, &l); + cpu_req_mo(cpu, 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(env, &l.page[0], l.mmu_idx, access_type, l.memop, ra); + return do_ld_4(cpu, &l.page[0], l.mmu_idx, access_type, l.memop, ra); } - ret = do_ld_beN(env, &l.page[0], 0, l.mmu_idx, access_type, l.memop, ra); - ret = do_ld_beN(env, &l.page[1], ret, 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; } -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, addr, oi, retaddr, MMU_DATA_LOAD); -} - -static uint64_t do_ld8_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, +static uint64_t do_ld8_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, uintptr_t ra, MMUAccessType access_type) { MMULookupLocals l; bool crosspage; uint64_t ret; - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); - crosspage = mmu_lookup(env, addr, oi, ra, access_type, &l); + cpu_req_mo(cpu, 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(env, &l.page[0], l.mmu_idx, access_type, l.memop, ra); + return do_ld_8(cpu, &l.page[0], l.mmu_idx, access_type, l.memop, ra); } - ret = do_ld_beN(env, &l.page[0], 0, l.mmu_idx, access_type, l.memop, ra); - ret = do_ld_beN(env, &l.page[1], ret, 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 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, 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); -} - -static Int128 do_ld16_mmu(CPUArchState *env, vaddr addr, +static Int128 do_ld16_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, uintptr_t ra) { MMULookupLocals l; @@ -2577,22 +2414,18 @@ static Int128 do_ld16_mmu(CPUArchState *env, vaddr addr, Int128 ret; int first; - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); - crosspage = mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD, &l); + cpu_req_mo(cpu, 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)) { - QEMU_IOTHREAD_LOCK_GUARD(); - a = do_ld_mmio_beN(env, l.page[0].full, 0, addr, 8, - l.mmu_idx, MMU_DATA_LOAD, ra); - b = do_ld_mmio_beN(env, l.page[0].full, 0, addr + 8, 8, - l.mmu_idx, MMU_DATA_LOAD, ra); - ret = int128_make128(b, a); + 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(env, ra, l.page[0].haddr, l.memop); + ret = load_atom_16(cpu, ra, l.page[0].haddr, l.memop); if (l.memop & MO_BSWAP) { ret = bswap128(ret); } @@ -2604,8 +2437,8 @@ static Int128 do_ld16_mmu(CPUArchState *env, vaddr addr, if (first == 8) { MemOp mop8 = (l.memop & ~MO_SIZE) | MO_64; - a = do_ld_8(env, &l.page[0], l.mmu_idx, MMU_DATA_LOAD, mop8, ra); - b = do_ld_8(env, &l.page[1], l.mmu_idx, MMU_DATA_LOAD, mop8, ra); + 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 { @@ -2615,15 +2448,15 @@ static Int128 do_ld16_mmu(CPUArchState *env, vaddr addr, } if (first < 8) { - a = do_ld_beN(env, &l.page[0], 0, l.mmu_idx, + a = do_ld_beN(cpu, &l.page[0], 0, l.mmu_idx, MMU_DATA_LOAD, l.memop, ra); - ret = do_ld16_beN(env, &l.page[1], a, l.mmu_idx, l.memop, ra); + ret = do_ld16_beN(cpu, &l.page[1], a, l.mmu_idx, l.memop, ra); } else { - ret = do_ld16_beN(env, &l.page[0], 0, l.mmu_idx, l.memop, ra); + 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(env, &l.page[1], b, l.mmu_idx, + b = do_ld_beN(cpu, &l.page[1], b, l.mmu_idx, MMU_DATA_LOAD, l.memop, ra); ret = int128_make128(b, a); } @@ -2633,146 +2466,105 @@ static Int128 do_ld16_mmu(CPUArchState *env, vaddr addr, return ret; } -Int128 helper_ld16_mmu(CPUArchState *env, uint64_t addr, - uint32_t oi, uintptr_t retaddr) -{ - tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128); - return do_ld16_mmu(env, addr, oi, retaddr); -} - -Int128 helper_ld_i128(CPUArchState *env, uint64_t addr, uint32_t oi) -{ - return helper_ld16_mmu(env, addr, oi, GETPC()); -} - -/* - * Load helpers for cpu_ldst.h. - */ - -static void plugin_load_cb(CPUArchState *env, abi_ptr addr, MemOpIdx oi) -{ - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); -} - -uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra) -{ - uint8_t ret; - - tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_UB); - ret = do_ld1_mmu(env, addr, oi, ra, MMU_DATA_LOAD); - plugin_load_cb(env, addr, oi); - return ret; -} - -uint16_t cpu_ldw_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) -{ - uint16_t ret; - - tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16); - ret = do_ld2_mmu(env, addr, oi, ra, MMU_DATA_LOAD); - plugin_load_cb(env, addr, oi); - return ret; -} - -uint32_t cpu_ldl_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) -{ - uint32_t ret; - - tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32); - ret = do_ld4_mmu(env, addr, oi, ra, MMU_DATA_LOAD); - plugin_load_cb(env, addr, oi); - return ret; -} - -uint64_t cpu_ldq_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) -{ - uint64_t ret; - - tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64); - ret = do_ld8_mmu(env, addr, oi, ra, MMU_DATA_LOAD); - plugin_load_cb(env, addr, oi); - return ret; -} - -Int128 cpu_ld16_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) -{ - Int128 ret; - - tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128); - ret = do_ld16_mmu(env, addr, oi, ra); - plugin_load_cb(env, addr, oi); - return ret; -} - /* * Store Helpers */ /** * do_st_mmio_leN: - * @env: cpu context + * @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: iothread lock held + * 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 do_st_mmio_leN(CPUArchState *env, CPUTLBEntryFull *full, - uint64_t val_le, vaddr addr, int size, - int mmu_idx, uintptr_t ra) +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) { - tcg_debug_assert(size > 0 && size <= 8); - do { + MemOp this_mop; + unsigned this_size; + MemTxResult r; + /* Store aligned pieces up to 8 bytes. */ - switch ((size | (int)addr) & 7) { - case 1: - case 3: - case 5: - case 7: - io_writex(env, full, mmu_idx, val_le, addr, ra, MO_UB); - val_le >>= 8; - size -= 1; - addr += 1; - break; - case 2: - case 6: - io_writex(env, full, mmu_idx, val_le, addr, ra, MO_LEUW); - val_le >>= 16; - size -= 2; - addr += 2; - break; - case 4: - io_writex(env, full, mmu_idx, val_le, addr, ra, MO_LEUL); - val_le >>= 32; - size -= 4; - addr += 4; - break; - case 0: - io_writex(env, full, mmu_idx, val_le, addr, ra, MO_LEUQ); + this_mop = ctz32(size | (int)addr | 8); + this_size = 1 << this_mop; + this_mop |= MO_LE; + + 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); + } + if (this_size == 8) { return 0; - default: - qemu_build_not_reached(); } + + val_le >>= this_size * 8; + addr += this_size; + mr_offset += this_size; + size -= this_size; } while (size); return val_le; } +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) +{ + 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); +} + +static uint64_t do_st16_mmio_leN(CPUState *cpu, CPUTLBEntryFull *full, + Int128 val_le, vaddr addr, int size, + int mmu_idx, uintptr_t ra) +{ + MemoryRegionSection *section; + MemoryRegion *mr; + hwaddr mr_offset; + MemTxAttrs attrs; + + 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(); + 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); +} + /* * Wrapper for the above. */ -static uint64_t do_st_leN(CPUArchState *env, MMULookupPageData *p, +static uint64_t do_st_leN(CPUState *cpu, MMULookupPageData *p, uint64_t val_le, int mmu_idx, MemOp mop, uintptr_t ra) { @@ -2780,8 +2572,7 @@ static uint64_t do_st_leN(CPUArchState *env, MMULookupPageData *p, unsigned tmp, half_size; if (unlikely(p->flags & TLB_MMIO)) { - QEMU_IOTHREAD_LOCK_GUARD(); - return do_st_mmio_leN(env, p->full, val_le, p->addr, + 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); @@ -2809,7 +2600,7 @@ static uint64_t do_st_leN(CPUArchState *env, MMULookupPageData *p, } else if (HAVE_al8) { return store_whole_le8(p->haddr, p->size, val_le); } else { - cpu_loop_exit_atomic(env_cpu(env), ra); + cpu_loop_exit_atomic(cpu, ra); } } /* fall through */ @@ -2827,7 +2618,7 @@ static uint64_t do_st_leN(CPUArchState *env, MMULookupPageData *p, /* * Wrapper for the above, for 8 < size < 16. */ -static uint64_t do_st16_leN(CPUArchState *env, MMULookupPageData *p, +static uint64_t do_st16_leN(CPUState *cpu, MMULookupPageData *p, Int128 val_le, int mmu_idx, MemOp mop, uintptr_t ra) { @@ -2835,11 +2626,8 @@ static uint64_t do_st16_leN(CPUArchState *env, MMULookupPageData *p, MemOp atom; if (unlikely(p->flags & TLB_MMIO)) { - QEMU_IOTHREAD_LOCK_GUARD(); - do_st_mmio_leN(env, p->full, int128_getlo(val_le), - p->addr, 8, mmu_idx, ra); - return do_st_mmio_leN(env, p->full, int128_gethi(val_le), - p->addr + 8, size - 8, mmu_idx, ra); + 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); } @@ -2857,8 +2645,8 @@ static uint64_t do_st16_leN(CPUArchState *env, MMULookupPageData *p, case MO_ATOM_WITHIN16_PAIR: /* Since size > 8, this is the half that must be atomic. */ - if (!HAVE_ATOMIC128_RW) { - cpu_loop_exit_atomic(env_cpu(env), ra); + if (!HAVE_CMPXCHG128) { + cpu_loop_exit_atomic(cpu, ra); } return store_whole_le16(p->haddr, p->size, val_le); @@ -2879,11 +2667,11 @@ static uint64_t do_st16_leN(CPUArchState *env, MMULookupPageData *p, } } -static void do_st_1(CPUArchState *env, MMULookupPageData *p, uint8_t val, +static void do_st_1(CPUState *cpu, MMULookupPageData *p, uint8_t val, int mmu_idx, uintptr_t ra) { if (unlikely(p->flags & TLB_MMIO)) { - io_writex(env, p->full, mmu_idx, val, p->addr, ra, MO_UB); + do_st_mmio_leN(cpu, p->full, val, p->addr, 1, mmu_idx, ra); } else if (unlikely(p->flags & TLB_DISCARD_WRITE)) { /* nothing */ } else { @@ -2891,15 +2679,14 @@ static void do_st_1(CPUArchState *env, MMULookupPageData *p, uint8_t val, } } -static void do_st_2(CPUArchState *env, MMULookupPageData *p, uint16_t val, +static void do_st_2(CPUState *cpu, MMULookupPageData *p, uint16_t val, int mmu_idx, MemOp memop, uintptr_t ra) { if (unlikely(p->flags & TLB_MMIO)) { if ((memop & MO_BSWAP) != MO_LE) { val = bswap16(val); } - QEMU_IOTHREAD_LOCK_GUARD(); - do_st_mmio_leN(env, p->full, val, p->addr, 2, mmu_idx, ra); + do_st_mmio_leN(cpu, p->full, val, p->addr, 2, mmu_idx, ra); } else if (unlikely(p->flags & TLB_DISCARD_WRITE)) { /* nothing */ } else { @@ -2907,19 +2694,18 @@ static void do_st_2(CPUArchState *env, MMULookupPageData *p, uint16_t val, if (memop & MO_BSWAP) { val = bswap16(val); } - store_atom_2(env, ra, p->haddr, memop, val); + store_atom_2(cpu, ra, p->haddr, memop, val); } } -static void do_st_4(CPUArchState *env, MMULookupPageData *p, uint32_t val, +static void do_st_4(CPUState *cpu, MMULookupPageData *p, uint32_t val, int mmu_idx, MemOp memop, uintptr_t ra) { if (unlikely(p->flags & TLB_MMIO)) { if ((memop & MO_BSWAP) != MO_LE) { val = bswap32(val); } - QEMU_IOTHREAD_LOCK_GUARD(); - do_st_mmio_leN(env, p->full, val, p->addr, 4, mmu_idx, ra); + do_st_mmio_leN(cpu, p->full, val, p->addr, 4, mmu_idx, ra); } else if (unlikely(p->flags & TLB_DISCARD_WRITE)) { /* nothing */ } else { @@ -2927,19 +2713,18 @@ static void do_st_4(CPUArchState *env, MMULookupPageData *p, uint32_t val, if (memop & MO_BSWAP) { val = bswap32(val); } - store_atom_4(env, ra, p->haddr, memop, val); + store_atom_4(cpu, ra, p->haddr, memop, val); } } -static void do_st_8(CPUArchState *env, MMULookupPageData *p, uint64_t val, +static void do_st_8(CPUState *cpu, MMULookupPageData *p, uint64_t val, int mmu_idx, MemOp memop, uintptr_t ra) { if (unlikely(p->flags & TLB_MMIO)) { if ((memop & MO_BSWAP) != MO_LE) { val = bswap64(val); } - QEMU_IOTHREAD_LOCK_GUARD(); - do_st_mmio_leN(env, p->full, val, p->addr, 8, mmu_idx, ra); + do_st_mmio_leN(cpu, p->full, val, p->addr, 8, mmu_idx, ra); } else if (unlikely(p->flags & TLB_DISCARD_WRITE)) { /* nothing */ } else { @@ -2947,35 +2732,34 @@ static void do_st_8(CPUArchState *env, MMULookupPageData *p, uint64_t val, if (memop & MO_BSWAP) { val = bswap64(val); } - store_atom_8(env, ra, p->haddr, memop, val); + store_atom_8(cpu, ra, p->haddr, memop, val); } } -void helper_stb_mmu(CPUArchState *env, uint64_t addr, uint32_t val, - MemOpIdx oi, uintptr_t ra) +static void do_st1_mmu(CPUState *cpu, vaddr addr, uint8_t val, + MemOpIdx oi, uintptr_t ra) { MMULookupLocals l; bool crosspage; - tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_8); - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); - crosspage = mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE, &l); + cpu_req_mo(cpu, 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(env, &l.page[0], val, l.mmu_idx, ra); + do_st_1(cpu, &l.page[0], val, l.mmu_idx, ra); } -static void do_st2_mmu(CPUArchState *env, vaddr addr, uint16_t val, +static void do_st2_mmu(CPUState *cpu, vaddr addr, uint16_t val, MemOpIdx oi, uintptr_t ra) { MMULookupLocals l; bool crosspage; uint8_t a, b; - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); - crosspage = mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE, &l); + cpu_req_mo(cpu, 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(env, &l.page[0], val, l.mmu_idx, l.memop, ra); + do_st_2(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra); return; } @@ -2984,27 +2768,20 @@ static void do_st2_mmu(CPUArchState *env, vaddr addr, uint16_t val, } else { b = val, a = val >> 8; } - do_st_1(env, &l.page[0], a, l.mmu_idx, ra); - do_st_1(env, &l.page[1], b, l.mmu_idx, ra); + do_st_1(cpu, &l.page[0], a, l.mmu_idx, ra); + do_st_1(cpu, &l.page[1], b, l.mmu_idx, 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, addr, val, oi, retaddr); -} - -static void do_st4_mmu(CPUArchState *env, vaddr addr, uint32_t val, +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(env, addr, oi, ra, MMU_DATA_STORE, &l); + cpu_req_mo(cpu, 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(env, &l.page[0], val, l.mmu_idx, l.memop, ra); + do_st_4(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra); return; } @@ -3012,27 +2789,20 @@ static void do_st4_mmu(CPUArchState *env, vaddr addr, uint32_t val, if ((l.memop & MO_BSWAP) != MO_LE) { val = bswap32(val); } - val = do_st_leN(env, &l.page[0], val, l.mmu_idx, l.memop, ra); - (void) do_st_leN(env, &l.page[1], val, l.mmu_idx, l.memop, ra); -} - -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, addr, val, oi, retaddr); + 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(CPUArchState *env, vaddr addr, uint64_t val, +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(env, addr, oi, ra, MMU_DATA_STORE, &l); + cpu_req_mo(cpu, 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(env, &l.page[0], val, l.mmu_idx, l.memop, ra); + do_st_8(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra); return; } @@ -3040,18 +2810,11 @@ static void do_st8_mmu(CPUArchState *env, vaddr addr, uint64_t val, if ((l.memop & MO_BSWAP) != MO_LE) { val = bswap64(val); } - val = do_st_leN(env, &l.page[0], val, l.mmu_idx, l.memop, ra); - (void) do_st_leN(env, &l.page[1], val, l.mmu_idx, l.memop, ra); -} - -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, addr, val, oi, retaddr); + 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(CPUArchState *env, vaddr addr, Int128 val, +static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val, MemOpIdx oi, uintptr_t ra) { MMULookupLocals l; @@ -3059,18 +2822,14 @@ static void do_st16_mmu(CPUArchState *env, vaddr addr, Int128 val, uint64_t a, b; int first; - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); - crosspage = mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE, &l); + cpu_req_mo(cpu, 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); } - a = int128_getlo(val); - b = int128_gethi(val); - QEMU_IOTHREAD_LOCK_GUARD(); - do_st_mmio_leN(env, l.page[0].full, a, addr, 8, l.mmu_idx, ra); - do_st_mmio_leN(env, l.page[0].full, b, addr + 8, 8, l.mmu_idx, ra); + 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 { @@ -3078,7 +2837,7 @@ static void do_st16_mmu(CPUArchState *env, vaddr addr, Int128 val, if (l.memop & MO_BSWAP) { val = bswap128(val); } - store_atom_16(env, ra, l.page[0].haddr, l.memop, val); + store_atom_16(cpu, ra, l.page[0].haddr, l.memop, val); } return; } @@ -3095,8 +2854,8 @@ static void do_st16_mmu(CPUArchState *env, vaddr addr, Int128 val, } else { a = int128_getlo(val), b = int128_gethi(val); } - do_st_8(env, &l.page[0], a, l.mmu_idx, mop8, ra); - do_st_8(env, &l.page[1], b, l.mmu_idx, mop8, ra); + 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; } @@ -3104,75 +2863,15 @@ static void do_st16_mmu(CPUArchState *env, vaddr addr, Int128 val, val = bswap128(val); } if (first < 8) { - do_st_leN(env, &l.page[0], int128_getlo(val), l.mmu_idx, l.memop, ra); + 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(env, &l.page[1], val, l.mmu_idx, l.memop, ra); + do_st16_leN(cpu, &l.page[1], val, l.mmu_idx, l.memop, ra); } else { - b = do_st16_leN(env, &l.page[0], val, l.mmu_idx, l.memop, ra); - do_st_leN(env, &l.page[1], b, l.mmu_idx, l.memop, ra); + 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); } } -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, 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()); -} - -/* - * Store Helpers for cpu_ldst.h - */ - -static void plugin_store_cb(CPUArchState *env, abi_ptr addr, MemOpIdx oi) -{ - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); -} - -void cpu_stb_mmu(CPUArchState *env, abi_ptr addr, uint8_t val, - MemOpIdx oi, uintptr_t retaddr) -{ - helper_stb_mmu(env, addr, val, oi, retaddr); - plugin_store_cb(env, addr, oi); -} - -void cpu_stw_mmu(CPUArchState *env, abi_ptr addr, uint16_t val, - MemOpIdx oi, uintptr_t retaddr) -{ - tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16); - do_st2_mmu(env, addr, val, oi, retaddr); - plugin_store_cb(env, addr, oi); -} - -void cpu_stl_mmu(CPUArchState *env, abi_ptr addr, uint32_t val, - MemOpIdx oi, uintptr_t retaddr) -{ - tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32); - do_st4_mmu(env, addr, val, oi, retaddr); - plugin_store_cb(env, addr, oi); -} - -void cpu_stq_mmu(CPUArchState *env, abi_ptr addr, uint64_t val, - MemOpIdx oi, uintptr_t retaddr) -{ - tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64); - do_st8_mmu(env, addr, val, oi, retaddr); - plugin_store_cb(env, addr, oi); -} - -void cpu_st16_mmu(CPUArchState *env, abi_ptr addr, Int128 val, - MemOpIdx oi, uintptr_t retaddr) -{ - tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128); - do_st16_mmu(env, addr, val, oi, retaddr); - plugin_store_cb(env, addr, oi); -} - #include "ldst_common.c.inc" /* @@ -3208,50 +2907,45 @@ void cpu_st16_mmu(CPUArchState *env, abi_ptr addr, Int128 val, /* Code access functions. */ -uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr) -{ - MemOpIdx oi = make_memop_idx(MO_UB, cpu_mmu_index(env, true)); - return do_ld1_mmu(env, 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 do_ld2_mmu(env, addr, oi, 0, MMU_INST_FETCH); -} - -uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr) +uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr, + MemOpIdx oi, uintptr_t retaddr) { - MemOpIdx oi = make_memop_idx(MO_TEUL, cpu_mmu_index(env, true)); - return do_ld4_mmu(env, addr, oi, 0, MMU_INST_FETCH); + return do_ld1_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH); } -uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr) +uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr, + MemOpIdx oi, uintptr_t retaddr) { - MemOpIdx oi = make_memop_idx(MO_TEUQ, cpu_mmu_index(env, true)); - return do_ld8_mmu(env, addr, oi, 0, MMU_INST_FETCH); + return do_ld2_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH); } -uint8_t cpu_ldb_code_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t retaddr) +uint32_t cpu_ldl_code_mmu(CPUArchState *env, vaddr addr, + MemOpIdx oi, uintptr_t retaddr) { - return do_ld1_mmu(env, addr, oi, retaddr, MMU_INST_FETCH); + return do_ld4_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH); } -uint16_t cpu_ldw_code_mmu(CPUArchState *env, abi_ptr addr, +uint64_t cpu_ldq_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t retaddr) { - return do_ld2_mmu(env, addr, oi, retaddr, MMU_INST_FETCH); + return do_ld8_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) +/* + * Common pointer_wrap implementations. + */ + +/* + * To be used for strict alignment targets. + * Because no accesses are unaligned, no accesses wrap either. + */ +vaddr cpu_pointer_wrap_notreached(CPUState *cs, int idx, vaddr res, vaddr base) { - return do_ld4_mmu(env, addr, oi, retaddr, MMU_INST_FETCH); + g_assert_not_reached(); } -uint64_t cpu_ldq_code_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t retaddr) +/* To be used for strict 32-bit targets. */ +vaddr cpu_pointer_wrap_uint32(CPUState *cs, int idx, vaddr res, vaddr base) { - return do_ld8_mmu(env, addr, oi, retaddr, MMU_INST_FETCH); + return (uint32_t)res; } diff --git a/accel/tcg/icount-common.c b/accel/tcg/icount-common.c new file mode 100644 index 00000000000..d6471174a3a --- /dev/null +++ b/accel/tcg/icount-common.c @@ -0,0 +1,503 @@ +/* + * 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 "system/cpus.h" +#include "system/qtest.h" +#include "qemu/main-loop.h" +#include "qemu/option.h" +#include "qemu/seqlock.h" +#include "system/replay.h" +#include "system/runstate.h" +#include "hw/core/cpu.h" +#include "exec/icount.h" +#include "system/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 + +bool icount_align_option; + +/* 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 00000000000..1dbc45dd955 --- /dev/null +++ b/accel/tcg/internal-common.h @@ -0,0 +1,142 @@ +/* + * 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" +#include "exec/mmap-lock.h" +#include "accel/tcg/tb-cpu-state.h" + +extern int64_t max_delay; +extern int64_t max_advance; + +extern bool one_insn_per_tb; + +extern bool icount_align_option; + +/* + * 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, TCGTBCPUState s); +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); + +/** + * tlb_init - initialize a CPU's TLB + * @cpu: CPU whose TLB should be initialized + */ +void tlb_init(CPUState *cpu); +/** + * tlb_destroy - destroy a CPU's TLB + * @cpu: CPU whose TLB should be destroyed + */ +void tlb_destroy(CPUState *cpu); + +bool tcg_exec_realizefn(CPUState *cpu, Error **errp); +void tcg_exec_unrealizefn(CPUState *cpu); + +/* current cflags for hashing/comparison */ +uint32_t curr_cflags(CPUState *cpu); + +void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr); + +/** + * get_page_addr_code_hostp() + * @env: CPUArchState + * @addr: guest virtual address of guest code + * + * See get_page_addr_code() (full-system version) for documentation on the + * return value. + * + * Sets *@hostp (when @hostp is non-NULL) as follows. + * If the return value is -1, sets *@hostp to NULL. Otherwise, sets *@hostp + * to the host address where @addr's content is kept. + * + * Note: this function can trigger an exception. + */ +tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, + void **hostp); + +/** + * get_page_addr_code() + * @env: CPUArchState + * @addr: guest virtual address of guest code + * + * If we cannot translate and execute from the entire RAM page, or if + * the region is not backed by RAM, returns -1. Otherwise, returns the + * ram_addr_t corresponding to the guest code at @addr. + * + * Note: this function can trigger an exception. + */ +static inline tb_page_addr_t get_page_addr_code(CPUArchState *env, + vaddr addr) +{ + return get_page_addr_code_hostp(env, addr, NULL); +} + +/* + * 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 + +#ifndef CONFIG_USER_ONLY +G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); +#endif /* CONFIG_USER_ONLY */ + +void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); +void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr); + +#endif diff --git a/accel/tcg/internal.h b/accel/tcg/internal.h deleted file mode 100644 index e8cbbde5810..00000000000 --- a/accel/tcg/internal.h +++ /dev/null @@ -1,141 +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" -#include "exec/translate-all.h" - -/* - * Access to the various translations structures need to be serialised - * via locks for consistency. In user-mode emulation access to the - * memory related structures are protected with mmap_lock. - * In !user-mode we use per-page locks. - */ -#ifdef CONFIG_USER_ONLY -#define assert_memory_lock() tcg_debug_assert(have_mmap_lock()) -#else -#define assert_memory_lock() -#endif - -#if defined(CONFIG_SOFTMMU) && defined(CONFIG_DEBUG_TCG) -void assert_no_pages_locked(void); -#else -static inline void assert_no_pages_locked(void) { } -#endif - -#ifdef CONFIG_USER_ONLY -static inline void page_table_config_init(void) { } -#else -void page_table_config_init(void); -#endif - -#ifdef CONFIG_USER_ONLY -/* - * For user-only, page_protect sets the page read-only. - * Since most execution is already on read-only pages, and we'd need to - * account for other TBs on the same page, defer undoing any page protection - * until we receive the write fault. - */ -static inline void tb_lock_page0(tb_page_addr_t p0) -{ - page_protect(p0); -} - -static inline void tb_lock_page1(tb_page_addr_t p0, tb_page_addr_t p1) -{ - page_protect(p1); -} - -static inline void tb_unlock_page1(tb_page_addr_t p0, tb_page_addr_t p1) { } -static inline void tb_unlock_pages(TranslationBlock *tb) { } -#else -void tb_lock_page0(tb_page_addr_t); -void tb_lock_page1(tb_page_addr_t, tb_page_addr_t); -void tb_unlock_page1(tb_page_addr_t, tb_page_addr_t); -void tb_unlock_pages(TranslationBlock *); -#endif - -#ifdef CONFIG_SOFTMMU -void tb_invalidate_phys_range_fast(ram_addr_t ram_addr, - unsigned size, - uintptr_t retaddr); -G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); -#endif /* CONFIG_SOFTMMU */ - -TranslationBlock *tb_gen_code(CPUState *cpu, vaddr pc, - uint64_t cs_base, uint32_t flags, - int cflags); -void page_init(void); -void tb_htable_init(void); -void tb_reset_jump(TranslationBlock *tb, int n); -TranslationBlock *tb_link_page(TranslationBlock *tb); -bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc); -void cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, - uintptr_t host_pc); - -/* 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; - } -} - -/* - * Return true if CS is not running in parallel with other cpus, either - * because there are no other cpus or we are within an exclusive context. - */ -static inline bool cpu_in_serial_context(CPUState *cs) -{ - return !(cs->tcg_cflags & CF_PARALLEL) || cpu_in_exclusive_context(cs); -} - -extern int64_t max_delay; -extern int64_t max_advance; - -extern bool one_insn_per_tb; - -/** - * tcg_req_mo: - * @type: TCGBar - * - * Filter @type to the barrier that is required for the guest - * memory ordering vs the host memory ordering. A non-zero - * result indicates that some barrier is required. - * - * If TCG_GUEST_DEFAULT_MO is not defined, assume that the - * guest requires strict ordering. - * - * This is a macro so that it's constant even without optimization. - */ -#ifdef TCG_GUEST_DEFAULT_MO -# define tcg_req_mo(type) \ - ((type) & TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO) -#else -# define tcg_req_mo(type) ((type) & ~TCG_TARGET_DEFAULT_MO) -#endif - -/** - * cpu_req_mo: - * @type: TCGBar - * - * If tcg_req_mo indicates a barrier for @type is required - * for the guest memory model, issue a host memory barrier. - */ -#define cpu_req_mo(type) \ - do { \ - if (tcg_req_mo(type)) { \ - smp_mb(); \ - } \ - } while (0) - -#endif /* ACCEL_TCG_INTERNAL_H */ diff --git a/accel/tcg/ldst_atomicity.c.inc b/accel/tcg/ldst_atomicity.c.inc index 1b793e69358..c735add2615 100644 --- a/accel/tcg/ldst_atomicity.c.inc +++ b/accel/tcg/ldst_atomicity.c.inc @@ -9,8 +9,8 @@ * See the COPYING file in the top-level directory. */ -#include "host/load-extract-al16-al8.h" -#include "host/store-insert-al16.h" +#include "host/load-extract-al16-al8.h.inc" +#include "host/store-insert-al16.h.inc" #ifdef CONFIG_ATOMIC64 # define HAVE_al8 true @@ -26,7 +26,7 @@ * If the operation must be split into two operations to be * examined separately for atomicity, return -lg2. */ -static int required_atomicity(CPUArchState *env, uintptr_t p, MemOp memop) +static int required_atomicity(CPUState *cpu, uintptr_t p, MemOp memop) { MemOp atom = memop & MO_ATOM_MASK; MemOp size = memop & MO_SIZE; @@ -76,7 +76,7 @@ static int required_atomicity(CPUArchState *env, uintptr_t p, MemOp memop) /* * Examine the alignment of p to determine if there are subobjects * that must be aligned. Note that we only really need ctz4() -- - * any more sigificant bits are discarded by the immediately + * any more significant bits are discarded by the immediately * following comparison. */ tmp = ctz32(p); @@ -93,7 +93,7 @@ static int required_atomicity(CPUArchState *env, uintptr_t p, MemOp memop) * host atomicity in order to avoid racing. This reduction * avoids looping with cpu_loop_exit_atomic. */ - if (cpu_in_serial_context(env_cpu(env))) { + if (cpu_in_serial_context(cpu)) { return MO_8; } return atmax; @@ -139,14 +139,14 @@ static inline uint64_t load_atomic8(void *pv) /** * load_atomic8_or_exit: - * @env: cpu context + * @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(CPUArchState *env, uintptr_t ra, void *pv) +static uint64_t load_atomic8_or_exit(CPUState *cpu, uintptr_t ra, void *pv) { if (HAVE_al8) { return load_atomic8(pv); @@ -168,19 +168,20 @@ static uint64_t load_atomic8_or_exit(CPUArchState *env, uintptr_t ra, void *pv) #endif /* Ultimate fallback: re-execute in serial context. */ - cpu_loop_exit_atomic(env_cpu(env), ra); + trace_load_atom8_or_exit_fallback(ra); + cpu_loop_exit_atomic(cpu, ra); } /** * load_atomic16_or_exit: - * @env: cpu context + * @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(CPUArchState *env, uintptr_t ra, void *pv) +static Int128 load_atomic16_or_exit(CPUState *cpu, uintptr_t ra, void *pv) { Int128 *p = __builtin_assume_aligned(pv, 16); @@ -212,7 +213,8 @@ static Int128 load_atomic16_or_exit(CPUArchState *env, uintptr_t ra, void *pv) } /* Ultimate fallback: re-execute in serial context. */ - cpu_loop_exit_atomic(env_cpu(env), ra); + trace_load_atom16_or_exit_fallback(ra); + cpu_loop_exit_atomic(cpu, ra); } /** @@ -263,7 +265,7 @@ static uint64_t load_atom_extract_al8x2(void *pv) /** * load_atom_extract_al8_or_exit: - * @env: cpu context + * @cpu: generic cpu state * @ra: host unwind address * @pv: host address * @s: object size in bytes, @s <= 4. @@ -273,7 +275,7 @@ static uint64_t load_atom_extract_al8x2(void *pv) * 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(CPUArchState *env, uintptr_t ra, +static uint32_t load_atom_extract_al8_or_exit(CPUState *cpu, uintptr_t ra, void *pv, int s) { uintptr_t pi = (uintptr_t)pv; @@ -281,12 +283,12 @@ static uint32_t load_atom_extract_al8_or_exit(CPUArchState *env, uintptr_t ra, int shr = (HOST_BIG_ENDIAN ? 8 - s - o : o) * 8; pv = (void *)(pi & ~7); - return load_atomic8_or_exit(env, ra, pv) >> shr; + return load_atomic8_or_exit(cpu, ra, pv) >> shr; } /** * load_atom_extract_al16_or_exit: - * @env: cpu context + * @cpu: generic cpu state * @ra: host unwind address * @p: host address * @s: object size in bytes, @s <= 8. @@ -299,7 +301,7 @@ static uint32_t load_atom_extract_al8_or_exit(CPUArchState *env, uintptr_t ra, * * If this is not possible, longjmp out to restart serially. */ -static uint64_t load_atom_extract_al16_or_exit(CPUArchState *env, uintptr_t ra, +static uint64_t load_atom_extract_al16_or_exit(CPUState *cpu, uintptr_t ra, void *pv, int s) { uintptr_t pi = (uintptr_t)pv; @@ -312,7 +314,7 @@ static uint64_t load_atom_extract_al16_or_exit(CPUArchState *env, uintptr_t ra, * Provoke SIGBUS if possible otherwise. */ pv = (void *)(pi & ~7); - r = load_atomic16_or_exit(env, ra, pv); + r = load_atomic16_or_exit(cpu, ra, pv); r = int128_urshift(r, shr); return int128_getlo(r); @@ -394,7 +396,7 @@ static inline uint64_t load_atom_8_by_8_or_4(void *pv) * * Load 2 bytes from @p, honoring the atomicity of @memop. */ -static uint16_t load_atom_2(CPUArchState *env, uintptr_t ra, +static uint16_t load_atom_2(CPUState *cpu, uintptr_t ra, void *pv, MemOp memop) { uintptr_t pi = (uintptr_t)pv; @@ -410,7 +412,7 @@ static uint16_t load_atom_2(CPUArchState *env, uintptr_t ra, } } - atmax = required_atomicity(env, pi, memop); + atmax = required_atomicity(cpu, pi, memop); switch (atmax) { case MO_8: return lduw_he_p(pv); @@ -421,9 +423,9 @@ static uint16_t load_atom_2(CPUArchState *env, uintptr_t ra, return load_atomic4(pv - 1) >> 8; } if ((pi & 15) != 7) { - return load_atom_extract_al8_or_exit(env, ra, pv, 2); + return load_atom_extract_al8_or_exit(cpu, ra, pv, 2); } - return load_atom_extract_al16_or_exit(env, ra, pv, 2); + return load_atom_extract_al16_or_exit(cpu, ra, pv, 2); default: g_assert_not_reached(); } @@ -436,7 +438,7 @@ static uint16_t load_atom_2(CPUArchState *env, uintptr_t ra, * * Load 4 bytes from @p, honoring the atomicity of @memop. */ -static uint32_t load_atom_4(CPUArchState *env, uintptr_t ra, +static uint32_t load_atom_4(CPUState *cpu, uintptr_t ra, void *pv, MemOp memop) { uintptr_t pi = (uintptr_t)pv; @@ -452,7 +454,7 @@ static uint32_t load_atom_4(CPUArchState *env, uintptr_t ra, } } - atmax = required_atomicity(env, pi, memop); + atmax = required_atomicity(cpu, pi, memop); switch (atmax) { case MO_8: case MO_16: @@ -466,9 +468,9 @@ static uint32_t load_atom_4(CPUArchState *env, uintptr_t ra, return load_atom_extract_al4x2(pv); case MO_32: if (!(pi & 4)) { - return load_atom_extract_al8_or_exit(env, ra, pv, 4); + return load_atom_extract_al8_or_exit(cpu, ra, pv, 4); } - return load_atom_extract_al16_or_exit(env, ra, pv, 4); + return load_atom_extract_al16_or_exit(cpu, ra, pv, 4); default: g_assert_not_reached(); } @@ -481,7 +483,7 @@ static uint32_t load_atom_4(CPUArchState *env, uintptr_t ra, * * Load 8 bytes from @p, honoring the atomicity of @memop. */ -static uint64_t load_atom_8(CPUArchState *env, uintptr_t ra, +static uint64_t load_atom_8(CPUState *cpu, uintptr_t ra, void *pv, MemOp memop) { uintptr_t pi = (uintptr_t)pv; @@ -498,12 +500,12 @@ static uint64_t load_atom_8(CPUArchState *env, uintptr_t ra, return load_atom_extract_al16_or_al8(pv, 8); } - atmax = required_atomicity(env, pi, memop); + atmax = required_atomicity(cpu, pi, memop); if (atmax == MO_64) { if (!HAVE_al8 && (pi & 7) == 0) { - load_atomic8_or_exit(env, ra, pv); + load_atomic8_or_exit(cpu, ra, pv); } - return load_atom_extract_al16_or_exit(env, ra, pv, 8); + return load_atom_extract_al16_or_exit(cpu, ra, pv, 8); } if (HAVE_al8_fast) { return load_atom_extract_al8x2(pv); @@ -519,7 +521,8 @@ static uint64_t load_atom_8(CPUArchState *env, uintptr_t ra, if (HAVE_al8) { return load_atom_extract_al8x2(pv); } - cpu_loop_exit_atomic(env_cpu(env), ra); + trace_load_atom8_fallback(memop, ra); + cpu_loop_exit_atomic(cpu, ra); default: g_assert_not_reached(); } @@ -532,7 +535,7 @@ static uint64_t load_atom_8(CPUArchState *env, uintptr_t ra, * * Load 16 bytes from @p, honoring the atomicity of @memop. */ -static Int128 load_atom_16(CPUArchState *env, uintptr_t ra, +static Int128 load_atom_16(CPUState *cpu, uintptr_t ra, void *pv, MemOp memop) { uintptr_t pi = (uintptr_t)pv; @@ -548,7 +551,7 @@ static Int128 load_atom_16(CPUArchState *env, uintptr_t ra, return atomic16_read_ro(pv); } - atmax = required_atomicity(env, pi, memop); + atmax = required_atomicity(cpu, pi, memop); switch (atmax) { case MO_8: memcpy(&r, pv, 16); @@ -563,20 +566,22 @@ static Int128 load_atom_16(CPUArchState *env, uintptr_t ra, break; case MO_64: if (!HAVE_al8) { - cpu_loop_exit_atomic(env_cpu(env), ra); + 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) { - cpu_loop_exit_atomic(env_cpu(env), ra); + 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(env, ra, pv); + return load_atomic16_or_exit(cpu, ra, pv); default: g_assert_not_reached(); } @@ -825,7 +830,7 @@ static uint64_t store_whole_le16(void *pv, int size, Int128 val_le) int sh = o * 8; Int128 m, v; - qemu_build_assert(HAVE_ATOMIC128_RW); + qemu_build_assert(HAVE_CMPXCHG128); /* Like MAKE_64BIT_MASK(0, sz), but larger. */ if (sz <= 64) { @@ -857,7 +862,7 @@ static uint64_t store_whole_le16(void *pv, int size, Int128 val_le) * * Store 2 bytes to @p, honoring the atomicity of @memop. */ -static void store_atom_2(CPUArchState *env, uintptr_t ra, +static void store_atom_2(CPUState *cpu, uintptr_t ra, void *pv, MemOp memop, uint16_t val) { uintptr_t pi = (uintptr_t)pv; @@ -868,7 +873,7 @@ static void store_atom_2(CPUArchState *env, uintptr_t ra, return; } - atmax = required_atomicity(env, pi, memop); + atmax = required_atomicity(cpu, pi, memop); if (atmax == MO_8) { stw_he_p(pv, val); return; @@ -887,7 +892,7 @@ static void store_atom_2(CPUArchState *env, uintptr_t ra, return; } } else if ((pi & 15) == 7) { - if (HAVE_ATOMIC128_RW) { + 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); @@ -897,7 +902,8 @@ static void store_atom_2(CPUArchState *env, uintptr_t ra, g_assert_not_reached(); } - cpu_loop_exit_atomic(env_cpu(env), ra); + trace_store_atom2_fallback(memop, ra); + cpu_loop_exit_atomic(cpu, ra); } /** @@ -908,7 +914,7 @@ static void store_atom_2(CPUArchState *env, uintptr_t ra, * * Store 4 bytes to @p, honoring the atomicity of @memop. */ -static void store_atom_4(CPUArchState *env, uintptr_t ra, +static void store_atom_4(CPUState *cpu, uintptr_t ra, void *pv, MemOp memop, uint32_t val) { uintptr_t pi = (uintptr_t)pv; @@ -919,7 +925,7 @@ static void store_atom_4(CPUArchState *env, uintptr_t ra, return; } - atmax = required_atomicity(env, pi, memop); + atmax = required_atomicity(cpu, pi, memop); switch (atmax) { case MO_8: stl_he_p(pv, val); @@ -956,12 +962,13 @@ static void store_atom_4(CPUArchState *env, uintptr_t ra, return; } } else { - if (HAVE_ATOMIC128_RW) { + if (HAVE_CMPXCHG128) { store_whole_le16(pv, 4, int128_make64(cpu_to_le32(val))); return; } } - cpu_loop_exit_atomic(env_cpu(env), ra); + trace_store_atom4_fallback(memop, ra); + cpu_loop_exit_atomic(cpu, ra); default: g_assert_not_reached(); } @@ -975,7 +982,7 @@ static void store_atom_4(CPUArchState *env, uintptr_t ra, * * Store 8 bytes to @p, honoring the atomicity of @memop. */ -static void store_atom_8(CPUArchState *env, uintptr_t ra, +static void store_atom_8(CPUState *cpu, uintptr_t ra, void *pv, MemOp memop, uint64_t val) { uintptr_t pi = (uintptr_t)pv; @@ -986,7 +993,7 @@ static void store_atom_8(CPUArchState *env, uintptr_t ra, return; } - atmax = required_atomicity(env, pi, memop); + atmax = required_atomicity(cpu, pi, memop); switch (atmax) { case MO_8: stq_he_p(pv, val); @@ -1021,7 +1028,7 @@ static void store_atom_8(CPUArchState *env, uintptr_t ra, } break; case MO_64: - if (HAVE_ATOMIC128_RW) { + if (HAVE_CMPXCHG128) { store_whole_le16(pv, 8, int128_make64(cpu_to_le64(val))); return; } @@ -1029,7 +1036,8 @@ static void store_atom_8(CPUArchState *env, uintptr_t ra, default: g_assert_not_reached(); } - cpu_loop_exit_atomic(env_cpu(env), ra); + trace_store_atom8_fallback(memop, ra); + cpu_loop_exit_atomic(cpu, ra); } /** @@ -1040,7 +1048,7 @@ static void store_atom_8(CPUArchState *env, uintptr_t ra, * * Store 16 bytes to @p, honoring the atomicity of @memop. */ -static void store_atom_16(CPUArchState *env, uintptr_t ra, +static void store_atom_16(CPUState *cpu, uintptr_t ra, void *pv, MemOp memop, Int128 val) { uintptr_t pi = (uintptr_t)pv; @@ -1052,7 +1060,7 @@ static void store_atom_16(CPUArchState *env, uintptr_t ra, return; } - atmax = required_atomicity(env, pi, memop); + 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); @@ -1076,7 +1084,7 @@ static void store_atom_16(CPUArchState *env, uintptr_t ra, } break; case -MO_64: - if (HAVE_ATOMIC128_RW) { + if (HAVE_CMPXCHG128) { uint64_t val_le; int s2 = pi & 15; int s1 = 16 - s2; @@ -1103,13 +1111,10 @@ static void store_atom_16(CPUArchState *env, uintptr_t ra, } break; case MO_128: - if (HAVE_ATOMIC128_RW) { - atomic16_set(pv, val); - return; - } break; default: g_assert_not_reached(); } - cpu_loop_exit_atomic(env_cpu(env), ra); + 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 5f8144b33a3..57f3e061924 100644 --- a/accel/tcg/ldst_common.c.inc +++ b/accel/tcg/ldst_common.c.inc @@ -8,300 +8,238 @@ * 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 + */ -uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); - return cpu_ldb_mmu(env, addr, oi, ra); -} - -int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return (int8_t)cpu_ldub_mmuidx_ra(env, addr, mmu_idx, ra); -} - -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_mmu(env, addr, oi, ra); -} - -int cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return (int16_t)cpu_lduw_be_mmuidx_ra(env, addr, mmu_idx, ra); -} - -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_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_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_mmu(env, addr, oi, ra); -} - -int cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return (int16_t)cpu_lduw_le_mmuidx_ra(env, addr, mmu_idx, ra); -} - -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_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_mmu(env, addr, oi, ra); -} - -void cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); - cpu_stb_mmu(env, addr, val, oi, ra); -} - -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_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_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_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_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) +tcg_target_ulong helper_ldub_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) { - MemOpIdx oi = make_memop_idx(MO_LEUL | MO_UNALN, mmu_idx); - cpu_stl_mmu(env, addr, val, oi, ra); + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_8); + return do_ld1_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); } -void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, - int mmu_idx, uintptr_t ra) +tcg_target_ulong helper_lduw_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) { - MemOpIdx oi = make_memop_idx(MO_LEUQ | MO_UNALN, mmu_idx); - cpu_stq_mmu(env, addr, val, oi, ra); + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16); + return do_ld2_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); } -/*--------------------------*/ - -uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +tcg_target_ulong helper_ldul_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) { - return cpu_ldub_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32); + return do_ld4_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); } -int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +uint64_t helper_ldq_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) { - return (int8_t)cpu_ldub_data_ra(env, addr, ra); + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64); + return do_ld8_mmu(env_cpu(env), addr, oi, retaddr, MMU_DATA_LOAD); } -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); -} +/* + * 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. + */ -int cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +tcg_target_ulong helper_ldsb_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) { - return (int16_t)cpu_lduw_be_data_ra(env, addr, ra); + return (int8_t)helper_ldub_mmu(env, addr, oi, retaddr); } -uint32_t cpu_ldl_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +tcg_target_ulong helper_ldsw_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) { - return cpu_ldl_be_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); + return (int16_t)helper_lduw_mmu(env, addr, oi, retaddr); } -uint64_t cpu_ldq_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +tcg_target_ulong helper_ldsl_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) { - return cpu_ldq_be_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); + return (int32_t)helper_ldul_mmu(env, addr, oi, retaddr); } -uint32_t cpu_lduw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +Int128 helper_ld16_mmu(CPUArchState *env, uint64_t addr, + MemOpIdx oi, uintptr_t retaddr) { - return cpu_lduw_le_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128); + return do_ld16_mmu(env_cpu(env), addr, oi, retaddr); } -int cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +Int128 helper_ld_i128(CPUArchState *env, uint64_t addr, uint32_t oi) { - return (int16_t)cpu_lduw_le_data_ra(env, addr, ra); + return helper_ld16_mmu(env, addr, oi, GETPC()); } -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); -} +/* + * Store helpers for tcg-ldst.h + */ -uint64_t cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +void helper_stb_mmu(CPUArchState *env, uint64_t addr, uint32_t val, + MemOpIdx oi, uintptr_t ra) { - return cpu_ldq_le_mmuidx_ra(env, addr, cpu_mmu_index(env, false), ra); + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_8); + do_st1_mmu(env_cpu(env), addr, val, oi, ra); } -void cpu_stb_data_ra(CPUArchState *env, abi_ptr addr, - uint32_t val, uintptr_t ra) +void helper_stw_mmu(CPUArchState *env, uint64_t addr, uint32_t val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_stb_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16); + do_st2_mmu(env_cpu(env), addr, val, oi, retaddr); } -void cpu_stw_be_data_ra(CPUArchState *env, abi_ptr addr, - uint32_t val, uintptr_t ra) +void helper_stl_mmu(CPUArchState *env, uint64_t addr, uint32_t val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_stw_be_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32); + do_st4_mmu(env_cpu(env), addr, val, oi, retaddr); } -void cpu_stl_be_data_ra(CPUArchState *env, abi_ptr addr, - uint32_t val, uintptr_t ra) +void helper_stq_mmu(CPUArchState *env, uint64_t addr, uint64_t val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_stl_be_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64); + do_st8_mmu(env_cpu(env), addr, val, oi, retaddr); } -void cpu_stq_be_data_ra(CPUArchState *env, abi_ptr addr, - uint64_t val, uintptr_t ra) +void helper_st16_mmu(CPUArchState *env, uint64_t addr, Int128 val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_stq_be_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); + tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128); + do_st16_mmu(env_cpu(env), addr, val, oi, retaddr); } -void cpu_stw_le_data_ra(CPUArchState *env, abi_ptr addr, - uint32_t val, uintptr_t ra) +void helper_st_i128(CPUArchState *env, uint64_t addr, Int128 val, MemOpIdx oi) { - cpu_stw_le_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); + helper_st16_mmu(env, addr, val, oi, GETPC()); } -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); -} +/* + * Load helpers for cpu_ldst.h + */ -void cpu_stq_le_data_ra(CPUArchState *env, abi_ptr addr, - uint64_t val, uintptr_t ra) +static void plugin_load_cb(CPUArchState *env, vaddr addr, + uint64_t value_low, + uint64_t value_high, + MemOpIdx oi) { - cpu_stq_le_mmuidx_ra(env, addr, val, cpu_mmu_index(env, false), ra); + 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); + } } -/*--------------------------*/ - -uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr addr) +uint8_t cpu_ldb_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra) { - return cpu_ldub_data_ra(env, addr, 0); -} + uint8_t ret; -int cpu_ldsb_data(CPUArchState *env, abi_ptr addr) -{ - return (int8_t)cpu_ldub_data(env, addr); + 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; } -uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr addr) +uint16_t cpu_ldw_mmu(CPUArchState *env, vaddr addr, + MemOpIdx oi, uintptr_t ra) { - return cpu_lduw_be_data_ra(env, addr, 0); -} + uint16_t ret; -int cpu_ldsw_be_data(CPUArchState *env, abi_ptr addr) -{ - return (int16_t)cpu_lduw_be_data(env, addr); + 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_be_data(CPUArchState *env, abi_ptr addr) +uint32_t cpu_ldl_mmu(CPUArchState *env, vaddr addr, + MemOpIdx oi, uintptr_t ra) { - return cpu_ldl_be_data_ra(env, addr, 0); -} + uint32_t ret; -uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr addr) -{ - return cpu_ldq_be_data_ra(env, addr, 0); + 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; } -uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr addr) +uint64_t cpu_ldq_mmu(CPUArchState *env, vaddr addr, + MemOpIdx oi, uintptr_t ra) { - return cpu_lduw_le_data_ra(env, addr, 0); -} + uint64_t ret; -int cpu_ldsw_le_data(CPUArchState *env, abi_ptr addr) -{ - return (int16_t)cpu_lduw_le_data(env, addr); + 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; } -uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr addr) +Int128 cpu_ld16_mmu(CPUArchState *env, vaddr addr, + MemOpIdx oi, uintptr_t ra) { - return cpu_ldl_le_data_ra(env, addr, 0); -} + Int128 ret; -uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr addr) -{ - return cpu_ldq_le_data_ra(env, addr, 0); + 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; } -void cpu_stb_data(CPUArchState *env, abi_ptr addr, uint32_t val) -{ - cpu_stb_data_ra(env, addr, val, 0); -} +/* + * Store helpers for cpu_ldst.h + */ -void cpu_stw_be_data(CPUArchState *env, abi_ptr addr, uint32_t val) +static void plugin_store_cb(CPUArchState *env, vaddr addr, + uint64_t value_low, + uint64_t value_high, + MemOpIdx oi) { - cpu_stw_be_data_ra(env, addr, val, 0); + 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_stl_be_data(CPUArchState *env, abi_ptr addr, uint32_t val) +void cpu_stb_mmu(CPUArchState *env, vaddr addr, uint8_t val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_stl_be_data_ra(env, addr, val, 0); + helper_stb_mmu(env, addr, val, oi, retaddr); + plugin_store_cb(env, addr, val, 0, oi); } -void cpu_stq_be_data(CPUArchState *env, abi_ptr addr, uint64_t val) +void cpu_stw_mmu(CPUArchState *env, vaddr addr, uint16_t val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_stq_be_data_ra(env, addr, val, 0); + 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_stw_le_data(CPUArchState *env, abi_ptr addr, uint32_t val) +void cpu_stl_mmu(CPUArchState *env, vaddr addr, uint32_t val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_stw_le_data_ra(env, addr, val, 0); + 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_stl_le_data(CPUArchState *env, abi_ptr addr, uint32_t val) +void cpu_stq_mmu(CPUArchState *env, vaddr addr, uint64_t val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_stl_le_data_ra(env, addr, val, 0); + 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_stq_le_data(CPUArchState *env, abi_ptr addr, uint64_t val) +void cpu_st16_mmu(CPUArchState *env, vaddr addr, Int128 val, + MemOpIdx oi, uintptr_t retaddr) { - cpu_stq_le_data_ra(env, addr, val, 0); + 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); } diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 8ace7837079..575e92bb9e8 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -1,31 +1,38 @@ +if not have_tcg + subdir_done() +endif + tcg_ss = ss.source_set() + tcg_ss.add(files( - 'tcg-all.c', - 'cpu-exec-common.c', 'cpu-exec.c', - 'tb-maint.c', - 'tcg-runtime-gvec.c', + 'cpu-exec-common.c', 'tcg-runtime.c', + 'tcg-runtime-gvec.c', + 'tb-maint.c', + 'tcg-all.c', 'translate-all.c', 'translator.c', )) -tcg_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c')) -tcg_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_false: files('user-exec-stub.c')) if get_option('plugins') tcg_ss.add(files('plugin-gen.c')) endif -tcg_ss.add(when: libdw, if_true: files('debuginfo.c')) -tcg_ss.add(when: 'CONFIG_LINUX', if_true: files('perf.c')) -specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss) -specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( - 'cputlb.c', - 'monitor.c', +user_ss.add_all(tcg_ss) +system_ss.add_all(tcg_ss) + +user_ss.add(files( + 'user-exec.c', + 'user-exec-stub.c', )) -tcg_module_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( +system_ss.add(files( + 'cputlb.c', + 'icount-common.c', + 'monitor.c', 'tcg-accel-ops.c', - 'tcg-accel-ops-mttcg.c', 'tcg-accel-ops-icount.c', + 'tcg-accel-ops-mttcg.c', 'tcg-accel-ops-rr.c', + 'watchpoint.c', )) diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index d48de239997..1c182b6bfb5 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -8,15 +8,17 @@ #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 "system/cpu-timers.h" +#include "exec/icount.h" +#include "system/tcg.h" #include "tcg/tcg.h" -#include "internal.h" +#include "internal-common.h" +#include "tb-context.h" static void dump_drift_info(GString *buf) @@ -50,6 +52,153 @@ static void dump_accel_info(GString *buf) 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(""); @@ -66,6 +215,11 @@ HumanReadableText *qmp_x_query_jit(Error **errp) 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(""); diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 5c13615112f..c1da7538941 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -14,850 +14,444 @@ * 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 "cpu.h" +#include "qemu/plugin.h" +#include "qemu/log.h" #include "tcg/tcg.h" #include "tcg/tcg-temp-internal.h" -#include "tcg/tcg-op.h" -#include "exec/exec-all.h" +#include "tcg/tcg-op-common.h" #include "exec/plugin-gen.h" #include "exec/translator.h" -#include "exec/helper-proto-common.h" - -#define HELPER_H "accel/tcg/plugin-helpers.h" -#include "exec/helper-info.c.inc" -#undef HELPER_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. - */ +#include "exec/translation-block.h" enum plugin_gen_from { PLUGIN_GEN_FROM_TB, PLUGIN_GEN_FROM_INSN, - PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_AFTER_INSN, - PLUGIN_GEN_N_FROMS, -}; - -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, + PLUGIN_GEN_AFTER_TB, }; -/* - * 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 gen_empty_udata_cb(void) -{ - TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); - TCGv_ptr udata = tcg_temp_ebb_new_ptr(); - - tcg_gen_movi_ptr(udata, 0); - tcg_gen_ld_i32(cpu_index, cpu_env, - -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); - gen_helper_plugin_vcpu_udata_cb(cpu_index, udata); - - tcg_temp_free_ptr(udata); - tcg_temp_free_i32(cpu_index); -} - -/* - * 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) -{ - TCGv_i64 val = tcg_temp_ebb_new_i64(); - TCGv_ptr ptr = tcg_temp_ebb_new_ptr(); - - tcg_gen_movi_ptr(ptr, 0); - 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_temp_free_i64(val); -} - -static void gen_empty_mem_cb(TCGv_i64 addr, uint32_t info) -{ - TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); - TCGv_i32 meminfo = tcg_temp_ebb_new_i32(); - TCGv_ptr udata = tcg_temp_ebb_new_ptr(); - - tcg_gen_movi_i32(meminfo, info); - tcg_gen_movi_ptr(udata, 0); - tcg_gen_ld_i32(cpu_index, cpu_env, - -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); - - gen_helper_plugin_vcpu_mem_cb(cpu_index, meminfo, addr, udata); - - tcg_temp_free_ptr(udata); - tcg_temp_free_i32(meminfo); - tcg_temp_free_i32(cpu_index); -} - -/* - * 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 = tcg_temp_ebb_new_ptr(); - - tcg_gen_movi_ptr(ptr, 0); - 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) -{ - tcg_gen_plugin_cb_start(from, type, wr); -} - -static void gen_wrapped(enum plugin_gen_from from, - enum plugin_gen_cb type, void (*func)(void)) -{ - gen_plugin_cb_start(from, type, 0); - func(); - tcg_gen_plugin_cb_end(); -} - -static void plugin_gen_empty_callback(enum plugin_gen_from from) -{ - switch (from) { - case PLUGIN_GEN_AFTER_INSN: - gen_wrapped(from, PLUGIN_GEN_DISABLE_MEM_HELPER, - gen_empty_mem_helper); - 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); - break; - default: - g_assert_not_reached(); - } -} - -void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info) -{ - enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info); - - gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_CB_MEM, rw); - gen_empty_mem_cb(addr, info); - tcg_gen_plugin_cb_end(); - - gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_CB_INLINE, rw); - gen_empty_inline_cb(); - tcg_gen_plugin_cb_end(); -} - -static TCGOp *find_op(TCGOp *op, TCGOpcode opc) +/* called before finishing a TB with exit_tb, goto_tb or goto_ptr */ +void plugin_gen_disable_mem_helpers(void) { - while (op) { - if (op->opc == opc) { - return op; - } - op = QTAILQ_NEXT(op, link); + if (tcg_ctx->plugin_insn) { + tcg_gen_plugin_cb(PLUGIN_GEN_AFTER_TB); } - 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) -{ - TCGOp *old_op = QTAILQ_NEXT(*begin_op, link); - unsigned nargs = old_op->nargs; - - *begin_op = old_op; - op = tcg_op_insert_after(tcg_ctx, op, old_op->opc, nargs); - memcpy(op->args, old_op->args, sizeof(op->args[0]) * nargs); - - return op; -} - -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_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr) +static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb, + struct qemu_plugin_insn *insn) { - 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; -} + GArray *arr; + size_t len; -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); + /* + * 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; } - 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); + if (!insn->mem_cbs || !insn->mem_cbs->len) { + insn->mem_helper = false; + return; } - return op; -} + insn->mem_helper = true; + ptb->mem_helper = true; -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; -} + /* + * 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); -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; + tcg_gen_st_ptr(tcg_constant_ptr((intptr_t)arr), tcg_env, + offsetof(CPUState, neg.plugin_mem_cbs) - sizeof(CPUState)); } -static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *empty_func, - void *func, int *cb_idx) +static void gen_disable_mem_helper(void) { - TCGOp *old_op; - int func_idx; - - /* copy all ops until the call */ - do { - op = copy_op_nocheck(begin_op, op); - } while (op->opc != INDEX_op_call); - - /* fill in the op call */ - old_op = *begin_op; - TCGOP_CALLI(op) = TCGOP_CALLI(old_op); - TCGOP_CALLO(op) = TCGOP_CALLO(old_op); - tcg_debug_assert(op->life == 0); - - func_idx = TCGOP_CALLO(op) + TCGOP_CALLI(op); - *cb_idx = func_idx; - op->args[func_idx] = (uintptr_t)func; - - return op; + tcg_gen_st_ptr(tcg_constant_ptr(0), tcg_env, + offsetof(CPUState, neg.plugin_mem_cbs) - sizeof(CPUState)); } -/* - * 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 TCGv_i32 gen_cpu_index(void) { - /* 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 */ - if (*cb_idx == -1) { - op = copy_op(&begin_op, op, INDEX_op_ld_i32); - } else { - begin_op = QTAILQ_NEXT(begin_op, link); - tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32); + /* + * Optimize when we run with a single vcpu. All values using cpu_index, + * including scoreboard index, will be optimized out. + * User-mode calls tb_flush when setting this flag. In system-mode, all + * vcpus are created before generating code. + */ + if (!tcg_cflags_has(current_cpu, CF_PARALLEL)) { + return tcg_constant_i32(current_cpu->cpu_index); } - - /* call */ - op = copy_call(&begin_op, op, HELPER(plugin_vcpu_udata_cb), - cb->f.vcpu_udata, cb_idx); - - return op; + TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); + tcg_gen_ld_i32(cpu_index, tcg_env, + offsetof(CPUState, cpu_index) - sizeof(CPUState)); + return cpu_index; } -static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb, - TCGOp *begin_op, TCGOp *op, - int *unused) +static void gen_udata_cb(struct qemu_plugin_regular_cb *cb) { - /* 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; + 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 TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb, - TCGOp *begin_op, TCGOp *op, int *cb_idx) +static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry) { - 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); + TCGv_ptr ptr = tcg_temp_ebb_new_ptr(); - /* copy the ld_i32, but note that we only have to copy it once */ - if (*cb_idx == -1) { - op = copy_op(&begin_op, op, INDEX_op_ld_i32); - } else { - begin_op = QTAILQ_NEXT(begin_op, link); - tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32); - } + GArray *arr = entry.score->data; + char *base_ptr = arr->data + entry.offset; + size_t entry_size = g_array_get_element_size(arr); - if (type == PLUGIN_GEN_CB_MEM) { - /* call */ - op = copy_call(&begin_op, op, HELPER(plugin_vcpu_mem_cb), - cb->f.vcpu_udata, cb_idx); + 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; +} + +static TCGCond plugin_cond_to_tcgcond(enum qemu_plugin_cond cond) +{ + 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(); } - - 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) +static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb) { - TCGOp *end_op; - TCGOp *op; - int cb_idx = -1; - int i; - - if (!cbs || cbs->len == 0) { - rm_ops(begin_op); - return; - } + TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry); + TCGv_i64 val = tcg_temp_ebb_new_i64(); + TCGLabel *after_cb = gen_new_label(); - end_op = find_op(begin_op, INDEX_op_plugin_cb_end); - tcg_debug_assert(end_op); + /* Condition should be negated, as calling the cb is the "else" path */ + TCGCond cond = tcg_invert_cond(plugin_cond_to_tcgcond(cb->cond)); - 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); + tcg_gen_ld_i64(val, ptr, 0); + 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); - if (!ok(begin_op, cb)) { - continue; - } - op = inject(cb, begin_op, op, &cb_idx); - } - rm_ops_range(begin_op, end_op); + tcg_temp_free_i64(val); + tcg_temp_free_ptr(ptr); } -static void -inject_udata_cb(const GArray *cbs, TCGOp *begin_op) +static void gen_inline_add_u64_cb(struct qemu_plugin_inline_cb *cb) { - inject_cb_type(cbs, begin_op, append_udata_cb, op_ok); -} + TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry); + TCGv_i64 val = tcg_temp_ebb_new_i64(); -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); -} + tcg_gen_ld_i64(val, ptr, 0); + tcg_gen_addi_i64(val, val, cb->imm); + tcg_gen_st_i64(val, ptr, 0); -static void -inject_mem_cb(const GArray *cbs, TCGOp *begin_op) -{ - inject_cb_type(cbs, begin_op, append_mem_cb, op_rw); + tcg_temp_free_i64(val); + tcg_temp_free_ptr(ptr); } -/* 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) +static void gen_inline_store_u64_cb(struct qemu_plugin_inline_cb *cb) { - TCGOp *orig_op = begin_op; - TCGOp *end_op; - TCGOp *op; + TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry); + TCGv_i64 val = tcg_constant_i64(cb->imm); - 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); + tcg_gen_st_i64(val, ptr, 0); - rm_ops_range(orig_op, end_op); + tcg_temp_free_ptr(ptr); } -/* - * 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_tb *ptb, - struct qemu_plugin_insn *plugin_insn, - TCGOp *begin_op) +static void gen_mem_cb(struct qemu_plugin_regular_cb *cb, + qemu_plugin_meminfo_t meminfo, TCGv_i64 addr) { - 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; - } - ptb->mem_helper = true; - - 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); + 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_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); -} +static void inject_cb(struct qemu_plugin_dyn_cb *cb) -/* called before finishing a TB with exit_tb, goto_tb or goto_ptr */ -void plugin_gen_disable_mem_helpers(void) { - /* - * We could emit the clearing unconditionally and be done. However, this can - * be wasteful if for instance plugins don't track memory accesses, or if - * most TBs don't use helpers. Instead, emit the clearing iff the TB calls - * helpers that might access guest memory. - * - * Note: we do not reset plugin_tb->mem_helper here; a TB might have several - * exit points, and we want to emit the clearing from all of them. - */ - if (!tcg_ctx->plugin_tb->mem_helper) { - return; + switch (cb->type) { + case PLUGIN_CB_REGULAR: + gen_udata_cb(&cb->regular); + break; + 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(); } - tcg_gen_st_ptr(tcg_constant_ptr(NULL), cpu_env, - offsetof(CPUState, plugin_mem_cbs) - offsetof(ArchCPU, env)); -} - -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(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(ptb, insn, begin_op); } -static void plugin_gen_disable_mem_helper(struct qemu_plugin_tb *ptb, - TCGOp *begin_op, int insn_idx) +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) { - 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; - } + switch (cb->type) { + case PLUGIN_CB_MEM_REGULAR: + if (rw & cb->regular.rw) { + gen_mem_cb(&cb->regular, meminfo, addr); + } + break; + case PLUGIN_CB_INLINE_ADD_U64: + case PLUGIN_CB_INLINE_STORE_U64: + if (rw & cb->inline_insn.rw) { + inject_cb(cb); } - printf("op[%2i]: %s %s %s\n", i, tcg_op_defs[op->opc].name, name, type); - i++; + break; + default: + g_assert_not_reached(); } -#endif } static void plugin_gen_inject(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); + } + } + + /* + * 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(op, &tcg_ctx->ops, link) { + 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(); + + case PLUGIN_GEN_AFTER_INSN: + assert(insn != NULL); + if (insn->mem_helper) { + gen_disable_mem_helper(); } 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(); - } + 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_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(); + + 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_state->event_mask)) { + return false; + } - if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_mask)) { - struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; - int i; + tcg_ctx->plugin_db = db; + tcg_ctx->plugin_insn = NULL; + ptb = tcg_ctx->plugin_tb; - /* reset callbacks */ - for (i = 0; i < PLUGIN_N_CB_SUBTYPES; i++) { - if (ptb->cbs[i]) { - g_array_set_size(ptb->cbs[i], 0); - } + if (ptb) { + /* Reset callbacks */ + if (ptb->cbs) { + g_array_set_size(ptb->cbs, 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; ptb->mem_helper = false; - - plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB); + } else { + ptb = g_new0(struct qemu_plugin_tb, 1); + tcg_ctx->plugin_tb = ptb; + ptb->insns = g_ptr_array_new(); } - tcg_ctx->plugin_insn = NULL; - - return ret; + 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; - - 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; + struct qemu_plugin_insn *insn; + size_t n = db->num_insns; + vaddr pc; + + 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); } /* @@ -866,13 +460,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 8e685e06545..00000000000 --- 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 | TCG_CALL_PLUGIN, void, i32, ptr) -DEF_HELPER_FLAGS_4(plugin_vcpu_mem_cb, TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, void, i32, i32, i64, ptr) -#endif diff --git a/accel/tcg/tb-hash.h b/accel/tcg/tb-hash.h index a0c61f25cda..f7b159f04cb 100644 --- a/accel/tcg/tb-hash.h +++ b/accel/tcg/tb-hash.h @@ -20,8 +20,9 @@ #ifndef EXEC_TB_HASH_H #define EXEC_TB_HASH_H -#include "exec/cpu-defs.h" -#include "exec/exec-all.h" +#include "exec/vaddr.h" +#include "exec/target_page.h" +#include "exec/translation-block.h" #include "qemu/xxhash.h" #include "tb-jmp-cache.h" diff --git a/accel/tcg/tb-internal.h b/accel/tcg/tb-internal.h new file mode 100644 index 00000000000..40439f03c3a --- /dev/null +++ b/accel/tcg/tb-internal.h @@ -0,0 +1,55 @@ +/* + * TranslationBlock internal declarations (target specific) + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_TB_INTERNAL_TARGET_H +#define ACCEL_TCG_TB_INTERNAL_TARGET_H + +#include "exec/translation-block.h" + +/* + * The true return address will often point to a host insn that is part of + * the next translated guest insn. Adjust the address backward to point to + * the middle of the call insn. Subtracting one would do the job except for + * several compressed mode architectures (arm, mips) which set the low bit + * to indicate the compressed mode; subtracting two works around that. It + * is also the case that there are no host isas that contain a call insn + * smaller than 4 bytes, so we don't worry about special-casing this. + */ +#define GETPC_ADJ 2 + +void tb_lock_page0(tb_page_addr_t); + +#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_page1(tb_page_addr_t p0, tb_page_addr_t p1) +{ + tb_lock_page0(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_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(CPUState *cpu, ram_addr_t ram_addr, + unsigned size, uintptr_t retaddr); +#endif /* CONFIG_SOFTMMU */ + +bool tb_invalidate_phys_page_unwind(CPUState *cpu, tb_page_addr_t addr, + uintptr_t pc); + +#endif diff --git a/accel/tcg/tb-jmp-cache.h b/accel/tcg/tb-jmp-cache.h index bb424c8a05b..c3a505e394a 100644 --- a/accel/tcg/tb-jmp-cache.h +++ b/accel/tcg/tb-jmp-cache.h @@ -9,20 +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 CF_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; vaddr pc; } array[TB_JMP_CACHE_SIZE]; -}; +} CPUJumpCache; #endif /* ACCEL_TCG_TB_JMP_CACHE_H */ diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index 32ae8af61cd..0048316f99a 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -22,14 +22,21 @@ #include "qemu/qtree.h" #include "exec/cputlb.h" #include "exec/log.h" -#include "exec/exec-all.h" +#include "exec/page-protection.h" +#include "exec/mmap-lock.h" #include "exec/tb-flush.h" -#include "exec/translate-all.h" -#include "sysemu/tcg.h" +#include "exec/target_page.h" +#include "accel/tcg/cpu-ops.h" +#include "tb-internal.h" +#include "system/tcg.h" #include "tcg/tcg.h" #include "tb-hash.h" #include "tb-context.h" -#include "internal.h" +#include "tb-internal.h" +#include "internal-common.h" +#ifdef CONFIG_USER_ONLY +#include "user/page-protection.h" +#endif /* List iterators for lists of tagged pointers in TranslationBlock. */ @@ -150,11 +157,7 @@ static PageForEachNext foreach_tb_next(PageForEachNext tb, /* * 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 +#define L1_MAP_ADDR_SPACE_BITS HOST_LONG_BITS /* Size of the L2 (and L3, etc) page tables. */ #define V_L2_BITS 10 @@ -207,13 +210,12 @@ static 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--) { + for (int i = v_l2_levels; i > 0; i--) { void **p = qatomic_rcu_read(lp); if (p == NULL) { @@ -712,7 +714,7 @@ static void tb_record(TranslationBlock *tb) tb_page_addr_t paddr0 = tb_page_addr0(tb); tb_page_addr_t paddr1 = tb_page_addr1(tb); tb_page_addr_t pindex0 = paddr0 >> TARGET_PAGE_BITS; - tb_page_addr_t pindex1 = paddr0 >> TARGET_PAGE_BITS; + tb_page_addr_t pindex1 = paddr1 >> TARGET_PAGE_BITS; assert(paddr0 != -1); if (unlikely(paddr1 != -1) && pindex0 != pindex1) { @@ -744,7 +746,7 @@ static void tb_remove(TranslationBlock *tb) tb_page_addr_t paddr0 = tb_page_addr0(tb); tb_page_addr_t paddr1 = tb_page_addr1(tb); tb_page_addr_t pindex0 = paddr0 >> TARGET_PAGE_BITS; - tb_page_addr_t pindex1 = paddr0 >> TARGET_PAGE_BITS; + tb_page_addr_t pindex1 = paddr1 >> TARGET_PAGE_BITS; assert(paddr0 != -1); if (unlikely(paddr1 != -1) && pindex0 != pindex1) { @@ -1004,7 +1006,8 @@ TranslationBlock *tb_link_page(TranslationBlock *tb) * 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) +void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start, + tb_page_addr_t last) { TranslationBlock *tb; PageForEachNext n; @@ -1021,23 +1024,22 @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last) * 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_page(tb_page_addr_t addr) +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); + tb_invalidate_phys_range(NULL, 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). + * Returns true if the caller needs to abort execution of the current TB. */ -bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc) +bool tb_invalidate_phys_page_unwind(CPUState *cpu, tb_page_addr_t addr, + uintptr_t pc) { TranslationBlock *current_tb; bool current_tb_modified; @@ -1049,10 +1051,7 @@ bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc) * 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) { + if (!pc || !cpu || !cpu->cc->tcg_ops->precise_smc) { tb_invalidate_phys_page(addr); return false; } @@ -1075,15 +1074,14 @@ bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc) * the CPU state. */ current_tb_modified = true; - cpu_restore_state_from_tb(current_cpu, current_tb, pc); + cpu_restore_state_from_tb(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); + cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); return true; } return false; @@ -1092,23 +1090,28 @@ bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc) /* * @p must be non-NULL. * Call with all @pages locked. + * (@cpu, @retaddr) may be (NULL, 0) outside of a cpu context, + * in which case precise_smc need not be detected. */ static void -tb_invalidate_phys_page_range__locked(struct page_collection *pages, +tb_invalidate_phys_page_range__locked(CPUState *cpu, + struct page_collection *pages, PageDesc *p, tb_page_addr_t start, tb_page_addr_t last, uintptr_t retaddr) { TranslationBlock *tb; PageForEachNext n; -#ifdef TARGET_HAS_PRECISE_SMC bool current_tb_modified = false; - TranslationBlock *current_tb = retaddr ? tcg_tb_lookup(retaddr) : NULL; -#endif /* TARGET_HAS_PRECISE_SMC */ + TranslationBlock *current_tb = NULL; /* Range may not cross a page. */ tcg_debug_assert(((start ^ last) & TARGET_PAGE_MASK) == 0); + if (retaddr && cpu && cpu->cc->tcg_ops->precise_smc) { + current_tb = tcg_tb_lookup(retaddr); + } + /* * 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 @@ -1126,8 +1129,7 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages, tb_last = tb_start + (tb_last & ~TARGET_PAGE_MASK); } if (!(tb_last < start || tb_start > last)) { -#ifdef TARGET_HAS_PRECISE_SMC - if (current_tb == tb && + if (unlikely(current_tb == tb) && (tb_cflags(current_tb) & CF_COUNT_MASK) != 1) { /* * If we are modifying the current TB, we must stop @@ -1137,9 +1139,8 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages, * restore the CPU state. */ current_tb_modified = true; - cpu_restore_state_from_tb(current_cpu, current_tb, retaddr); + cpu_restore_state_from_tb(cpu, current_tb, retaddr); } -#endif /* TARGET_HAS_PRECISE_SMC */ tb_phys_invalidate__locked(tb); } } @@ -1149,37 +1150,13 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages, tlb_unprotect_code(start); } -#ifdef TARGET_HAS_PRECISE_SMC - if (current_tb_modified) { + if (unlikely(current_tb_modified)) { page_collection_unlock(pages); /* Force execution of one insn next time. */ - current_cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(current_cpu); + cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); mmap_unlock(); - cpu_loop_exit_noexc(current_cpu); - } -#endif -} - -/* - * Invalidate all TBs which intersect with the target physical - * address page @addr. - */ -void tb_invalidate_phys_page(tb_page_addr_t addr) -{ - struct page_collection *pages; - tb_page_addr_t start, last; - PageDesc *p; - - p = page_find(addr >> TARGET_PAGE_BITS); - if (p == NULL) { - return; + cpu_loop_exit_noexc(cpu); } - - start = addr & TARGET_PAGE_MASK; - last = addr | ~TARGET_PAGE_MASK; - pages = page_collection_lock(start, last); - tb_invalidate_phys_page_range__locked(pages, p, start, last, 0); - page_collection_unlock(pages); } /* @@ -1189,7 +1166,8 @@ void tb_invalidate_phys_page(tb_page_addr_t addr) * access: the virtual CPU will exit the current TB if code is modified inside * this TB. */ -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last) +void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start, + tb_page_addr_t last) { struct page_collection *pages; tb_page_addr_t index, index_last; @@ -1208,44 +1186,30 @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last) 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, + tb_invalidate_phys_page_range__locked(cpu, pages, pd, page_start, page_last, 0); } page_collection_unlock(pages); } -/* - * Call with all @pages in the range [@start, @start + len[ locked. - */ -static void tb_invalidate_phys_page_fast__locked(struct page_collection *pages, - tb_page_addr_t start, - unsigned len, uintptr_t ra) -{ - PageDesc *p; - - p = page_find(start >> TARGET_PAGE_BITS); - if (!p) { - return; - } - - assert_page_locked(p); - tb_invalidate_phys_page_range__locked(pages, p, start, start + len - 1, ra); -} - /* * len must be <= 8 and start must be a multiple of len. * Called via softmmu_template.h when code areas are written to with * iothread mutex not held. */ -void tb_invalidate_phys_range_fast(ram_addr_t ram_addr, - unsigned size, - uintptr_t retaddr) +void tb_invalidate_phys_range_fast(CPUState *cpu, ram_addr_t start, + unsigned len, uintptr_t ra) { - struct page_collection *pages; + PageDesc *p = page_find(start >> TARGET_PAGE_BITS); - 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); + if (p) { + ram_addr_t last = start + len - 1; + struct page_collection *pages = page_collection_lock(start, last); + + tb_invalidate_phys_page_range__locked(cpu, pages, p, + start, last, ra); + page_collection_unlock(pages); + } } #endif /* CONFIG_USER_ONLY */ diff --git a/accel/tcg/tcg-accel-ops-icount.c b/accel/tcg/tcg-accel-ops-icount.c index 3d2cfbbc977..d0f7b410fab 100644 --- a/accel/tcg/tcg-accel-ops-icount.c +++ b/accel/tcg/tcg-accel-ops-icount.c @@ -24,11 +24,11 @@ */ #include "qemu/osdep.h" -#include "sysemu/replay.h" -#include "sysemu/cpu-timers.h" +#include "system/replay.h" +#include "exec/icount.h" #include "qemu/main-loop.h" #include "qemu/guest-random.h" -#include "exec/exec-all.h" +#include "hw/core/cpu.h" #include "tcg-accel-ops.h" #include "tcg-accel-ops-icount.h" @@ -111,24 +111,24 @@ void icount_prepare_for_run(CPUState *cpu, int64_t cpu_budget) * 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); replay_mutex_lock(); cpu->icount_budget = MIN(icount_get_limit(), cpu_budget); insns_left = MIN(0xffff, cpu->icount_budget); - cpu_neg(cpu)->icount_decr.u16.low = insns_left; + cpu->neg.icount_decr.u16.low = insns_left; cpu->icount_extra = cpu->icount_budget - insns_left; 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(); } } @@ -138,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; @@ -153,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-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index b2762620079..dfcee30947e 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -24,15 +24,14 @@ */ #include "qemu/osdep.h" -#include "sysemu/tcg.h" -#include "sysemu/replay.h" -#include "sysemu/cpu-timers.h" +#include "system/tcg.h" +#include "system/replay.h" +#include "exec/icount.h" #include "qemu/main-loop.h" #include "qemu/notify.h" #include "qemu/guest-random.h" -#include "exec/exec-all.h" #include "hw/boards.h" -#include "tcg/tcg.h" +#include "tcg/startup.h" #include "tcg-accel-ops.h" #include "tcg-accel-ops-mttcg.h" @@ -76,11 +75,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,28 +90,23 @@ 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; @@ -123,8 +117,8 @@ static void *mttcg_cpu_thread_fn(void *arg) 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,10 +136,6 @@ 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); diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 2d523289a8d..6eec5c9eee9 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -25,14 +25,14 @@ #include "qemu/osdep.h" #include "qemu/lockable.h" -#include "sysemu/tcg.h" -#include "sysemu/replay.h" -#include "sysemu/cpu-timers.h" +#include "system/tcg.h" +#include "system/replay.h" +#include "exec/icount.h" #include "qemu/main-loop.h" #include "qemu/notify.h" #include "qemu/guest-random.h" -#include "exec/exec-all.h" -#include "tcg/tcg.h" +#include "exec/cpu-common.h" +#include "tcg/startup.h" #include "tcg-accel-ops.h" #include "tcg-accel-ops-rr.h" #include "tcg-accel-ops-icount.h" @@ -111,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(); @@ -131,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; } } @@ -188,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) { @@ -218,9 +218,9 @@ static void *rr_cpu_thread_fn(void *arg) /* Only used for icount_enabled() */ int64_t cpu_budget = 0; - qemu_mutex_unlock_iothread(); + bql_unlock(); replay_mutex_lock(); - qemu_mutex_lock_iothread(); + bql_lock(); if (icount_enabled()) { int cpu_count = rr_cpu_count(); @@ -254,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, 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) { @@ -302,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) @@ -317,24 +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; } 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 3973591508f..b24d6a75625 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -26,15 +26,21 @@ */ #include "qemu/osdep.h" -#include "sysemu/tcg.h" -#include "sysemu/replay.h" -#include "sysemu/cpu-timers.h" +#include "system/accel-ops.h" +#include "system/tcg.h" +#include "system/replay.h" +#include "exec/icount.h" #include "qemu/main-loop.h" #include "qemu/guest-random.h" #include "qemu/timer.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/hwaddr.h" -#include "exec/gdbstub.h" +#include "exec/tb-flush.h" +#include "exec/translation-block.h" +#include "exec/watchpoint.h" +#include "gdbstub/enums.h" + +#include "hw/core/cpu.h" #include "tcg-accel-ops.h" #include "tcg-accel-ops-mttcg.h" @@ -59,15 +65,15 @@ void tcg_cpu_init_cflags(CPUState *cpu, bool parallel) 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; assert(tcg_enabled()); @@ -77,10 +83,17 @@ int tcg_cpus_exec(CPUState *cpu) return ret; } +static void tcg_cpu_reset_hold(CPUState *cpu) +{ + tcg_flush_jmp_cache(cpu); + + tlb_flush(cpu); +} + /* mask must never be zero, except for A20 change call */ void tcg_handle_interrupt(CPUState *cpu, int mask) { - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); cpu->interrupt_request |= mask; @@ -91,7 +104,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); } } @@ -109,10 +122,9 @@ static inline int xlat_gdb_type(CPUState *cpu, int gdbtype) [GDB_WATCHPOINT_ACCESS] = BP_GDB | BP_MEM_ACCESS, }; - CPUClass *cc = CPU_GET_CLASS(cpu); int cputype = xlat[gdbtype]; - if (cc->gdb_stop_before_watchpoint) { + if (cpu->cc->gdb_stop_before_watchpoint) { cputype |= BP_STOP_BEFORE_ACCESS; } return cputype; @@ -205,13 +217,14 @@ 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; ops->remove_all_breakpoints = tcg_remove_all_breakpoints; } -static void tcg_accel_ops_class_init(ObjectClass *oc, void *data) +static void tcg_accel_ops_class_init(ObjectClass *oc, const void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); diff --git a/accel/tcg/tcg-accel-ops.h b/accel/tcg/tcg-accel-ops.h index f9bc6330e2d..6feeb3f3e9b 100644 --- a/accel/tcg/tcg-accel-ops.h +++ b/accel/tcg/tcg-accel-ops.h @@ -12,10 +12,10 @@ #ifndef TCG_ACCEL_OPS_H #define TCG_ACCEL_OPS_H -#include "sysemu/cpus.h" +#include "system/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 03dfd67e9ef..6e5dc333d59 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -24,26 +24,29 @@ */ #include "qemu/osdep.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" #include "exec/replay-core.h" -#include "sysemu/cpu-timers.h" -#include "tcg/tcg.h" -#include "tcg/oversized-guest.h" +#include "exec/icount.h" +#include "tcg/startup.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/accel.h" #include "qemu/atomic.h" +#include "qapi/qapi-types-common.h" #include "qapi/qapi-builtin-visit.h" #include "qemu/units.h" -#if !defined(CONFIG_USER_ONLY) +#include "qemu/target-info.h" +#ifndef CONFIG_USER_ONLY #include "hw/boards.h" #endif -#include "internal.h" +#include "accel/tcg/cpu-ops.h" +#include "internal-common.h" + struct TCGState { AccelState parent_obj; - bool mttcg_enabled; + OnOffAuto mttcg_enabled; bool one_insn_per_tb; int splitwx_enabled; unsigned long tb_size; @@ -55,40 +58,18 @@ typedef struct TCGState TCGState; DECLARE_INSTANCE_CHECKER(TCGState, TCG_STATE, TYPE_TCG_ACCEL) -/* - * We default to false if we know other options have been enabled - * which are currently incompatible with MTTCG. Otherwise when each - * guest (target) has been updated to support: - * - atomic instructions - * - memory ordering primitives (barriers) - * they can set the appropriate CONFIG flags in ${target}-softmmu.mak - * - * Once a guest architecture has been converted to the new primitives - * there is one remaining limitation to check: - * - The guest can't be oversized (e.g. 64 bit guest on 32 bit host) - */ - -static bool default_mttcg_enabled(void) +#ifndef CONFIG_USER_ONLY +bool qemu_tcg_mttcg_enabled(void) { - if (icount_enabled() || TCG_OVERSIZED_GUEST) { - return false; - } -#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 + TCGState *s = TCG_STATE(current_accel()); + return s->mttcg_enabled == ON_OFF_AUTO_ON; } +#endif /* !CONFIG_USER_ONLY */ static void tcg_accel_instance_init(Object *obj) { TCGState *s = TCG_STATE(obj); - s->mttcg_enabled = default_mttcg_enabled(); - /* If debugging enabled, default "auto on", otherwise off. */ #if defined(CONFIG_DEBUG_TCG) && !defined(CONFIG_USER_ONLY) s->splitwx_enabled = -1; @@ -97,31 +78,68 @@ static void tcg_accel_instance_init(Object *obj) #endif } -bool mttcg_enabled; bool one_insn_per_tb; static int tcg_init_machine(MachineState *ms) { TCGState *s = TCG_STATE(current_accel()); -#ifdef CONFIG_USER_ONLY - unsigned max_cpus = 1; -#else - unsigned max_cpus = ms->smp.max_cpus; + unsigned max_threads = 1; + +#ifndef CONFIG_USER_ONLY + CPUClass *cc = CPU_CLASS(object_class_by_name(target_cpu_type())); + bool mttcg_supported = cc->tcg_ops->mttcg_supported; + + switch (s->mttcg_enabled) { + case ON_OFF_AUTO_AUTO: + /* + * We default to false if we know other options have been enabled + * which are currently incompatible with MTTCG. Otherwise when each + * guest (target) has been updated to support: + * - atomic instructions + * - memory ordering primitives (barriers) + * they can set the appropriate CONFIG flags in ${target}-softmmu.mak + * + * Once a guest architecture has been converted to the new primitives + * there is one remaining limitation to check: + * - The guest can't be oversized (e.g. 64 bit guest on 32 bit host) + */ + if (mttcg_supported && !icount_enabled()) { + s->mttcg_enabled = ON_OFF_AUTO_ON; + max_threads = ms->smp.max_cpus; + } else { + s->mttcg_enabled = ON_OFF_AUTO_OFF; + } + break; + case ON_OFF_AUTO_ON: + if (!mttcg_supported) { + warn_report("Guest not yet converted to MTTCG - " + "you may get unexpected results"); + } + max_threads = ms->smp.max_cpus; + break; + case ON_OFF_AUTO_OFF: + break; + default: + g_assert_not_reached(); + } #endif tcg_allowed = true; - mttcg_enabled = s->mttcg_enabled; page_init(); tb_htable_init(); - tcg_init(s->tb_size * MiB, s->splitwx_enabled, max_cpus); + tcg_init(s->tb_size * MiB, s->splitwx_enabled, max_threads); #if defined(CONFIG_SOFTMMU) /* * 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 + +#ifdef CONFIG_USER_ONLY + qdev_create_fake_machine(); #endif return 0; @@ -131,7 +149,7 @@ static char *tcg_get_thread(Object *obj, Error **errp) { TCGState *s = TCG_STATE(obj); - return g_strdup(s->mttcg_enabled ? "multi" : "single"); + return g_strdup(s->mttcg_enabled == ON_OFF_AUTO_ON ? "multi" : "single"); } static void tcg_set_thread(Object *obj, const char *value, Error **errp) @@ -139,19 +157,13 @@ static void tcg_set_thread(Object *obj, const char *value, Error **errp) TCGState *s = TCG_STATE(obj); if (strcmp(value, "multi") == 0) { - if (TCG_OVERSIZED_GUEST) { - error_setg(errp, "No MTTCG when guest word size > hosts"); - } else if (icount_enabled()) { + if (icount_enabled()) { error_setg(errp, "No MTTCG when icount is enabled"); } else { -#ifndef TARGET_SUPPORTS_MTTCG - warn_report("Guest not yet converted to MTTCG - " - "you may get unexpected results"); -#endif - s->mttcg_enabled = true; + s->mttcg_enabled = ON_OFF_AUTO_ON; } } else if (strcmp(value, "single") == 0) { - s->mttcg_enabled = false; + s->mttcg_enabled = ON_OFF_AUTO_OFF; } else { error_setg(errp, "Invalid 'thread' setting %s", value); } @@ -222,11 +234,13 @@ static int tcg_gdbstub_supported_sstep_flags(void) } } -static void tcg_accel_class_init(ObjectClass *oc, void *data) +static void tcg_accel_class_init(ObjectClass *oc, const 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; diff --git a/accel/tcg/tcg-runtime-gvec.c b/accel/tcg/tcg-runtime-gvec.c index 6c99f952caa..ff927c5dd8d 100644 --- a/accel/tcg/tcg-runtime-gvec.c +++ b/accel/tcg/tcg-runtime-gvec.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "qemu/host-utils.h" -#include "cpu.h" #include "exec/helper-proto-common.h" #include "tcg/tcg-gvec-desc.h" @@ -1042,6 +1041,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 9fa539ad3d7..fa7ed9739c7 100644 --- a/accel/tcg/tcg-runtime.c +++ b/accel/tcg/tcg-runtime.c @@ -23,13 +23,9 @@ */ #include "qemu/osdep.h" #include "qemu/host-utils.h" -#include "cpu.h" +#include "exec/cpu-common.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" +#include "accel/tcg/getpc.h" #define HELPER_H "accel/tcg/tcg-runtime.h" #include "exec/helper-info.c.inc" diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h index 186899a2c7e..c23b5e66c46 100644 --- a/accel/tcg/tcg-runtime.h +++ b/accel/tcg/tcg-runtime.h @@ -297,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/tlb-bounds.h b/accel/tcg/tlb-bounds.h new file mode 100644 index 00000000000..f83d9ac9eed --- /dev/null +++ b/accel/tcg/tlb-bounds.h @@ -0,0 +1,13 @@ +/* + * softmmu size bounds + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_TLB_BOUNDS_H +#define ACCEL_TCG_TLB_BOUNDS_H + +#define CPU_TLB_DYN_MIN_BITS 6 +#define CPU_TLB_DYN_MAX_BITS (32 - TARGET_PAGE_BITS) +#define CPU_TLB_DYN_DEFAULT_BITS 8 + +#endif /* ACCEL_TCG_TLB_BOUNDS_H */ diff --git a/accel/tcg/trace-events b/accel/tcg/trace-events index 4e9b450520b..14f638810c6 100644 --- a/accel/tcg/trace-events +++ b/accel/tcg/trace-events @@ -12,3 +12,15 @@ 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 b2d4e22c17d..d468667b0dd 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -21,48 +21,21 @@ #include "trace.h" #include "disas/disas.h" -#include "exec/exec-all.h" #include "tcg/tcg.h" -#if defined(CONFIG_USER_ONLY) -#include "qemu.h" -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) -#include -#if __FreeBSD_version >= 700104 -#define HAVE_KINFO_GETVMMAP -#define sigqueue sigqueue_freebsd /* avoid redefinition */ -#include -#include -#define _KERNEL -#include -#undef _KERNEL -#undef sigqueue -#include -#endif -#endif -#else -#include "exec/ram_addr.h" -#endif - -#include "exec/cputlb.h" -#include "exec/translate-all.h" -#include "exec/translator.h" +#include "exec/mmap-lock.h" +#include "tb-internal.h" #include "exec/tb-flush.h" -#include "qemu/bitmap.h" -#include "qemu/qemu-print.h" -#include "qemu/main-loop.h" #include "qemu/cacheinfo.h" -#include "qemu/timer.h" +#include "qemu/target-info.h" #include "exec/log.h" -#include "sysemu/cpus.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/tcg.h" -#include "qapi/error.h" -#include "hw/core/tcg-cpu-ops.h" +#include "exec/icount.h" +#include "accel/tcg/cpu-ops.h" #include "tb-jmp-cache.h" #include "tb-hash.h" #include "tb-context.h" -#include "internal.h" -#include "perf.h" +#include "tb-internal.h" +#include "internal-common.h" +#include "tcg/perf.h" #include "tcg/insn-start-words.h" TBContext tb_ctx; @@ -104,7 +77,7 @@ static int64_t decode_sleb128(const uint8_t **pp) val |= (int64_t)(byte & 0x7f) << shift; shift += 7; } while (byte & 0x80); - if (shift < TARGET_LONG_BITS && (byte & 0x40)) { + if (shift < 64 && (byte & 0x40)) { val |= -(int64_t)1 << shift; } @@ -115,7 +88,7 @@ static int64_t decode_sleb128(const uint8_t **pp) /* Encode the data collected about the instructions while compiling TB. Place the data at BLOCK, and return the number of bytes consumed. - The logical table consists of TARGET_INSN_START_WORDS target_ulong's, + The logical table consists of INSN_START_WORDS uint64_t's, which come from the target's insn_start data, followed by a uintptr_t which comes from the host pc of the end of the code implementing the insn. @@ -135,13 +108,13 @@ static int encode_search(TranslationBlock *tb, uint8_t *block) for (i = 0, n = tb->icount; i < n; ++i) { uint64_t prev, curr; - for (j = 0; j < TARGET_INSN_START_WORDS; ++j) { + for (j = 0; j < INSN_START_WORDS; ++j) { if (i == 0) { prev = (!(tb_cflags(tb) & CF_PCREL) && j == 0 ? tb->pc : 0); } else { - prev = insn_data[(i - 1) * TARGET_INSN_START_WORDS + j]; + prev = insn_data[(i - 1) * INSN_START_WORDS + j]; } - curr = insn_data[i * TARGET_INSN_START_WORDS + j]; + curr = insn_data[i * INSN_START_WORDS + j]; p = encode_sleb128(p, curr - prev); } prev = (i == 0 ? 0 : insn_end_off[i - 1]); @@ -173,7 +146,7 @@ static int cpu_unwind_data_from_tb(TranslationBlock *tb, uintptr_t host_pc, return -1; } - memset(data, 0, sizeof(uint64_t) * TARGET_INSN_START_WORDS); + memset(data, 0, sizeof(uint64_t) * INSN_START_WORDS); if (!(tb_cflags(tb) & CF_PCREL)) { data[0] = tb->pc; } @@ -183,7 +156,7 @@ static int cpu_unwind_data_from_tb(TranslationBlock *tb, uintptr_t host_pc, * at which the end of the insn exceeds host_pc. */ for (i = 0; i < num_insns; ++i) { - for (j = 0; j < TARGET_INSN_START_WORDS; ++j) { + for (j = 0; j < INSN_START_WORDS; ++j) { data[j] += decode_sleb128(&p); } iter_pc += decode_sleb128(&p); @@ -201,7 +174,7 @@ static int cpu_unwind_data_from_tb(TranslationBlock *tb, uintptr_t host_pc, void cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, uintptr_t host_pc) { - uint64_t data[TARGET_INSN_START_WORDS]; + uint64_t data[INSN_START_WORDS]; int insns_left = cpu_unwind_data_from_tb(tb, host_pc, data); if (insns_left < 0) { @@ -214,7 +187,7 @@ 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); @@ -255,7 +228,6 @@ 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(); } @@ -274,8 +246,10 @@ 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); + CPUState *cs = env_cpu(env); + tcg_ctx->cpu = cs; + cs->cc->tcg_ops->translate_code(cs, tb, max_insns, pc, host_pc); + assert(tb->size != 0); tcg_ctx->cpu = NULL; *max_insns = tb->icount; @@ -284,11 +258,9 @@ static int setjmp_gen_code(CPUArchState *env, TranslationBlock *tb, } /* Called with mmap_lock held for user mode emulation. */ -TranslationBlock *tb_gen_code(CPUState *cpu, - vaddr pc, uint64_t cs_base, - uint32_t flags, int cflags) +TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s) { - CPUArchState *env = cpu->env_ptr; + CPUArchState *env = cpu_env(cpu); TranslationBlock *tb, *existing_tb; tb_page_addr_t phys_pc, phys_p2; tcg_insn_unit *gen_code_buf; @@ -299,14 +271,14 @@ TranslationBlock *tb_gen_code(CPUState *cpu, assert_memory_lock(); qemu_thread_jit_write(); - phys_pc = get_page_addr_code_hostp(env, pc, &host_pc); + phys_pc = get_page_addr_code_hostp(env, s.pc, &host_pc); if (phys_pc == -1) { /* Generate a one-shot TB with 1 insn in it */ - cflags = (cflags & ~CF_COUNT_MASK) | CF_LAST_IO | 1; + s.cflags = (s.cflags & ~CF_COUNT_MASK) | 1; } - max_insns = cflags & CF_COUNT_MASK; + max_insns = s.cflags & CF_COUNT_MASK; if (max_insns == 0) { max_insns = TCG_MAX_INSNS; } @@ -326,12 +298,12 @@ 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 (!(cflags & CF_PCREL)) { - tb->pc = pc; + if (!(s.cflags & CF_PCREL)) { + tb->pc = s.pc; } - tb->cs_base = cs_base; - tb->flags = flags; - tb->cflags = cflags; + tb->cs_base = s.cs_base; + tb->flags = s.flags; + tb->cflags = s.cflags; tb_set_page_addr0(tb, phys_pc); tb_set_page_addr1(tb, -1); if (phys_pc != -1) { @@ -339,32 +311,20 @@ TranslationBlock *tb_gen_code(CPUState *cpu, } 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; - tcg_ctx->tlb_fast_offset = - (int)offsetof(ArchCPU, neg.tlb.f) - (int)offsetof(ArchCPU, env); -#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 + tcg_ctx->addr_type = target_long_bits() == 32 ? TCG_TYPE_I32 : TCG_TYPE_I64; + tcg_ctx->guest_mo = cpu->cc->tcg_ops->guest_default_memory_order; restart_translate: - trace_translate_block(tb, pc, tb->tc.ptr); + trace_translate_block(tb, s.pc, tb->tc.ptr); - gen_code_size = setjmp_gen_code(env, tb, pc, host_pc, &max_insns, &ti); + gen_code_size = setjmp_gen_code(env, tb, s.pc, host_pc, &max_insns, &ti); if (unlikely(gen_code_size < 0)) { switch (gen_code_size) { case -1: /* * Overflow of code_gen_buffer, or the current slice of it. * - * TODO: We don't need to re-do gen_intermediate_code, nor + * TODO: We don't need to re-do tcg_ops->translate_code, nor * should we re-do the tcg optimization currently hidden * inside tcg_gen_code. All that should be required is to * flush the TBs, allocate a new TB, re-initialize it per @@ -434,10 +394,10 @@ TranslationBlock *tb_gen_code(CPUState *cpu, * 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)); + perf_report_code(s.pc, tb, tcg_splitwx_to_rx(gen_code_buf)); if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) && - qemu_log_in_addr_range(pc)) { + qemu_log_in_addr_range(s.pc)) { FILE *logfile = qemu_log_trylock(); if (logfile) { int code_size, data_size; @@ -459,7 +419,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, fprintf(logfile, "OUT: [size=%d]\n", gen_code_size); fprintf(logfile, " -- guest addr 0x%016" PRIx64 " + tb prologue\n", - tcg_ctx->gen_insn_data[insn * TARGET_INSN_START_WORDS]); + tcg_ctx->gen_insn_data[insn * INSN_START_WORDS]); chunk_start = tcg_ctx->gen_insn_end_off[insn]; disas(logfile, tb->tc.ptr, chunk_start); @@ -472,7 +432,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, size_t chunk_end = tcg_ctx->gen_insn_end_off[insn]; if (chunk_end > chunk_start) { fprintf(logfile, " -- guest addr 0x%016" PRIx64 "\n", - tcg_ctx->gen_insn_data[insn * TARGET_INSN_START_WORDS]); + tcg_ctx->gen_insn_data[insn * INSN_START_WORDS]); disas(logfile, tb->tc.ptr + chunk_start, chunk_end - chunk_start); chunk_start = chunk_end; @@ -529,23 +489,32 @@ TranslationBlock *tb_gen_code(CPUState *cpu, tb_reset_jump(tb, 1); } + /* + * Insert TB into the corresponding region tree before publishing it + * through QHT. Otherwise rewinding happened in the TB might fail to + * lookup itself using host PC. + */ + tcg_tb_insert(tb); + /* * If the TB is not associated with a physical RAM page then it must be - * a temporary one-insn TB, and we have nothing left to do. Return early - * before attempting to link to other TBs or add to the lookup table. + * a temporary one-insn TB. + * + * Such TBs must be added to region trees in order to make sure that + * restore_state_to_opc() - which on some architectures is not limited to + * rewinding, but also affects exception handling! - is called when such a + * TB causes an exception. + * + * At the same time, temporary one-insn TBs must be executed at most once, + * because subsequent reads from, e.g., I/O memory may return different + * values. So return early before attempting to link to other TBs or add + * to the QHT. */ if (tb_page_addr0(tb) == -1) { assert_no_pages_locked(); return tb; } - /* - * Insert TB into the corresponding region tree before publishing it - * through QHT. Otherwise rewinding happened in the TB might fail to - * lookup itself using host PC. - */ - tcg_tb_insert(tb); - /* * No explicit memory barrier is required -- tb_link_page() makes the * TB visible in a consistent state. @@ -580,16 +549,12 @@ 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; - 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); + CPUArchState *env = cpu_env(cpu); + TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu); + tb_page_addr_t addr = get_page_addr_code(env, s.pc); + if (addr != -1) { - tb_invalidate_phys_range(addr, addr); + tb_invalidate_phys_range(cpu, addr, addr); } } } @@ -620,10 +585,10 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) * to account for the re-execution of the branch. */ n = 1; - cc = CPU_GET_CLASS(cpu); + cc = cpu->cc; 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; } @@ -631,12 +596,13 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) * Exit the loop and potentially generate a new TB executing the * just the I/O insns. We also limit instrumentation to memory * operations only (which execute after completion) so we don't - * double instrument the instruction. + * double instrument the instruction. Also don't let an IRQ sneak + * in before we execute it. */ - cpu->cflags_next_tb = curr_cflags(cpu) | CF_MEMI_ONLY | CF_LAST_IO | n; + cpu->cflags_next_tb = curr_cflags(cpu) | CF_MEMI_ONLY | CF_NOIRQ | n; if (qemu_loglevel_mask(CPU_LOG_EXEC)) { - vaddr 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 %016" VADDR_PRIx "\n", pc); @@ -646,142 +612,6 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) 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_OFFSET_INVALID) { - tst->direct_jmp_count++; - if (tb->jmp_reset_offset[1] != TB_JMP_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); -} - #endif /* CONFIG_USER_ONLY */ /* @@ -801,11 +631,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 1a6a5448c8f..034f2f359ef 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -8,35 +8,28 @@ */ #include "qemu/osdep.h" +#include "qemu/bswap.h" #include "qemu/log.h" #include "qemu/error-report.h" -#include "exec/exec-all.h" +#include "accel/tcg/cpu-ldst-common.h" +#include "accel/tcg/cpu-mmu-index.h" +#include "exec/target_page.h" #include "exec/translator.h" #include "exec/plugin-gen.h" #include "tcg/tcg-op-common.h" -#include "internal.h" +#include "internal-common.h" +#include "disas/disas.h" +#include "tb-internal.h" -static void gen_io_start(void) +static void set_can_do_io(DisasContextBase *db, bool val) { - tcg_gen_st_i32(tcg_constant_i32(1), cpu_env, - offsetof(ArchCPU, parent_obj.can_do_io) - - offsetof(ArchCPU, env)); + QEMU_BUILD_BUG_ON(sizeof_field(CPUState, neg.can_do_io) != 1); + tcg_gen_st8_i32(tcg_constant_i32(val), tcg_env, + offsetof(CPUState, neg.can_do_io) - sizeof(CPUState)); } bool translator_io_start(DisasContextBase *db) { - uint32_t cflags = tb_cflags(db->tb); - - if (!(cflags & CF_USE_ICOUNT)) { - return false; - } - if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) { - /* Already started in translator_loop. */ - return true; - } - - gen_io_start(); - /* * Ensure that this instruction will be the last in the TB. * The target may override this to something more forceful. @@ -47,14 +40,17 @@ bool translator_io_start(DisasContextBase *db) return true; } -static TCGOp *gen_tb_start(uint32_t cflags) +static TCGOp *gen_tb_start(DisasContextBase *db, uint32_t cflags) { - TCGv_i32 count = tcg_temp_new_i32(); + TCGv_i32 count = NULL; TCGOp *icount_start_insn = NULL; - tcg_gen_ld_i32(count, cpu_env, - offsetof(ArchCPU, neg.icount_decr.u32) - - offsetof(ArchCPU, env)); + if ((cflags & CF_USE_ICOUNT) || !(cflags & CF_NOIRQ)) { + count = tcg_temp_new_i32(); + tcg_gen_ld_i32(count, tcg_env, + offsetof(CPUState, neg.icount_decr.u32) - + sizeof(CPUState)); + } if (cflags & CF_USE_ICOUNT) { /* @@ -81,19 +77,9 @@ static TCGOp *gen_tb_start(uint32_t cflags) } if (cflags & 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_gen_st16_i32(count, tcg_env, + offsetof(CPUState, neg.icount_decr.u16.low) - + sizeof(CPUState)); } return icount_start_insn; @@ -117,6 +103,11 @@ static void gen_tb_end(const TranslationBlock *tb, uint32_t cflags, } } +bool translator_is_same_page(const DisasContextBase *db, vaddr addr) +{ + return ((addr ^ db->pc_first) & TARGET_PAGE_MASK) == 0; +} + bool translator_use_goto_tb(DisasContextBase *db, vaddr dest) { /* Suppress goto_tb if requested. */ @@ -125,7 +116,7 @@ bool translator_use_goto_tb(DisasContextBase *db, vaddr dest) } /* Check for the dest on the same page as the start of the TB. */ - return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0; + return translator_is_same_page(db, dest); } void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, @@ -134,6 +125,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, { uint32_t cflags = tb_cflags(tb); TCGOp *icount_start_insn; + TCGOp *first_insn_start = NULL; bool plugin_enabled; /* Initialize DisasContext */ @@ -143,42 +135,45 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, db->is_jmp = DISAS_NEXT; db->num_insns = 0; db->max_insns = *max_insns; - db->singlestep_enabled = cflags & CF_SINGLE_STEP; + db->insn_start = NULL; + db->fake_insn = false; db->host_addr[0] = host_pc; db->host_addr[1] = NULL; + db->record_start = 0; + db->record_len = 0; + db->code_mmuidx = cpu_mmu_index(cpu, true); ops->init_disas_context(db, cpu); tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ /* Start translating. */ - icount_start_insn = gen_tb_start(cflags); + 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) { *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 @@ -210,172 +205,318 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, ops->tb_stop(db, cpu); 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; + if (plugin_enabled) { + plugin_gen_tb_end(cpu, db->num_insns); + } + 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); } } } -static void *translator_access(CPUArchState *env, DisasContextBase *db, - vaddr 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; - vaddr 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[1]; - base = TARGET_PAGE_ALIGN(db->pc_first); - if (host == NULL) { - tb_page_addr_t page0, old_page1, new_page1; - - new_page1 = get_page_addr_code_hostp(env, base, &db->host_addr[1]); - - /* - * If the second page is MMIO, treat as if the first page - * was MMIO as well, so that we do not cache the TB. - */ - if (unlikely(new_page1 == -1)) { - tb_unlock_pages(tb); - tb_set_page_addr0(tb, -1); - return NULL; - } + host = db->host_addr[0]; + base = db->pc_first; - /* - * 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]; + if (likely(((base ^ last) & TARGET_PAGE_MASK) == 0)) { + /* Entire read is from the first page. */ + goto do_read; + } + + if (unlikely(((base ^ pc) & TARGET_PAGE_MASK) == 0)) { + /* + * Read begins on the first page and extends to the second. + * The unaligned read is never atomic. + */ + 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; } - /* Use slow path when crossing pages. */ - if (is_same_page(db, pc)) { - return NULL; + /* + * If this is not the first time around, and page1 matches, + * then we already have the page locked. Alternately, we're + * not doing anything to prevent the PTE from changing, so + * we might wind up with a different page, requiring us to + * re-do the locking. + */ + old_page1 = tb_page_addr1(tb); + if (likely(new_page1 != old_page1)) { + page0 = tb_page_addr0(tb); + if (unlikely(old_page1 != -1)) { + tb_unlock_page1(page0, old_page1); + } + tb_set_page_addr1(tb, new_page1); + tb_lock_page1(page0, new_page1); } + host = db->host_addr[1]; } - tcg_debug_assert(pc >= base); - return host + (pc - base); + do_read: + /* + * Assume aligned reads should be atomic, if possible. + * We're not in a position to jump out with EXCP_ATOMIC. + */ + host += pc - base; + switch (len) { + case 2: + if (QEMU_IS_ALIGNED(pc, 2)) { + uint16_t t = qatomic_read((uint16_t *)host); + stw_he_p(dest, t); + return true; + } + break; + case 4: + if (QEMU_IS_ALIGNED(pc, 4)) { + uint32_t t = qatomic_read((uint32_t *)host); + stl_he_p(dest, t); + return true; + } + break; +#ifdef CONFIG_ATOMIC64 + case 8: + if (QEMU_IS_ALIGNED(pc, 8)) { + uint64_t t = qatomic_read__nocheck((uint64_t *)host); + stq_he_p(dest, t); + return true; + } + break; +#endif + } + /* Unaligned or partial read from the second page is not atomic. */ + memcpy(dest, host, len); + return true; } -static void plugin_insn_append(abi_ptr pc, const void *from, size_t size) +static void record_save(DisasContextBase *db, vaddr pc, + const void *from, int size) { -#ifdef CONFIG_PLUGIN - struct qemu_plugin_insn *insn = tcg_ctx->plugin_insn; - abi_ptr off; + int offset; - if (insn == NULL) { + /* Do not record probes before the start of TB. */ + if (pc < db->pc_first) { 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(); + + /* + * 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; } - insn->data = g_byte_array_append(insn->data, from, size); -#endif + 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; } -uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc) +bool translator_st(const DisasContextBase *db, void *dest, + vaddr addr, size_t len) { - uint8_t ret; - void *p = translator_access(env, db, pc, sizeof(ret)); + size_t offset, offset_end; - if (p) { - plugin_insn_append(pc, p, sizeof(ret)); - return ldub_p(p); + 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; + } + } + + /* 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; + } } - ret = cpu_ldub_code(env, pc); - plugin_insn_append(pc, &ret, sizeof(ret)); - return ret; + + /* 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; + } + 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 val; - if (p) { - plugin_insn_append(pc, p, sizeof(ret)); - return lduw_p(p); + if (!translator_ld(env, db, &val, pc, sizeof(val))) { + MemOpIdx oi = make_memop_idx(MO_UB, db->code_mmuidx); + val = cpu_ldb_code_mmu(env, pc, oi, 0); + record_save(db, pc, &val, sizeof(val)); } - ret = cpu_lduw_code(env, pc); - plug = tswap16(ret); - plugin_insn_append(pc, &plug, sizeof(ret)); - return ret; + return val; } -uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc) +uint16_t translator_lduw_end(CPUArchState *env, DisasContextBase *db, + vaddr pc, MemOp endian) { - uint32_t ret, plug; - void *p = translator_access(env, db, pc, sizeof(ret)); + uint16_t val; - if (p) { - plugin_insn_append(pc, p, sizeof(ret)); - return ldl_p(p); + if (!translator_ld(env, db, &val, pc, sizeof(val))) { + MemOpIdx oi = make_memop_idx(MO_UW, db->code_mmuidx); + val = cpu_ldw_code_mmu(env, pc, oi, 0); + record_save(db, pc, &val, sizeof(val)); } - ret = cpu_ldl_code(env, pc); - plug = tswap32(ret); - plugin_insn_append(pc, &plug, sizeof(ret)); - return ret; + if (endian & MO_BSWAP) { + val = bswap16(val); + } + return val; } -uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc) +uint32_t translator_ldl_end(CPUArchState *env, DisasContextBase *db, + vaddr pc, MemOp endian) { - uint64_t ret, plug; - void *p = translator_access(env, db, pc, sizeof(ret)); + uint32_t val; - if (p) { - plugin_insn_append(pc, p, sizeof(ret)); - return ldq_p(p); + if (!translator_ld(env, db, &val, pc, sizeof(val))) { + MemOpIdx oi = make_memop_idx(MO_UL, db->code_mmuidx); + val = cpu_ldl_code_mmu(env, pc, oi, 0); + record_save(db, pc, &val, sizeof(val)); + } + if (endian & MO_BSWAP) { + val = bswap32(val); + } + return val; +} + +uint64_t translator_ldq_end(CPUArchState *env, DisasContextBase *db, + vaddr pc, MemOp endian) +{ + uint64_t val; + + if (!translator_ld(env, db, &val, pc, sizeof(val))) { + MemOpIdx oi = make_memop_idx(MO_UQ, db->code_mmuidx); + val = cpu_ldq_code_mmu(env, pc, oi, 0); + record_save(db, pc, &val, sizeof(val)); + } + if (endian & MO_BSWAP) { + val = bswap64(val); } - ret = cpu_ldq_code(env, pc); - plug = tswap64(ret); - plugin_insn_append(pc, &plug, sizeof(ret)); - return ret; + return val; } -void translator_fake_ldb(uint8_t insn8, abi_ptr pc) +void translator_fake_ld(DisasContextBase *db, const void *data, size_t len) { - plugin_insn_append(pc, &insn8, sizeof(insn8)); + 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 874e1f1a20e..1d52f48226a 100644 --- a/accel/tcg/user-exec-stub.c +++ b/accel/tcg/user-exec-stub.c @@ -1,8 +1,7 @@ #include "qemu/osdep.h" #include "hw/core/cpu.h" #include "exec/replay-core.h" - -bool enable_cpu_pm = false; +#include "internal-common.h" void cpu_resume(CPUState *cpu) { @@ -16,6 +15,20 @@ void qemu_init_vcpu(CPUState *cpu) { } +void cpu_exec_reset_hold(CPUState *cpu) +{ +} + +/* User mode emulation does not support softmmu yet. */ + +void tlb_init(CPUState *cpu) +{ +} + +void tlb_destroy(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 ab48cb41e4c..f25d80e2dc2 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -17,24 +17,42 @@ * License along with this library; if not, see . */ #include "qemu/osdep.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #include "disas/disas.h" -#include "exec/exec-all.h" +#include "exec/vaddr.h" +#include "exec/tlb-flags.h" #include "tcg/tcg.h" #include "qemu/bitops.h" #include "qemu/rcu.h" -#include "exec/cpu_ldst.h" -#include "exec/translate-all.h" -#include "exec/helper-proto.h" +#include "accel/tcg/cpu-ldst-common.h" +#include "accel/tcg/helper-retaddr.h" +#include "accel/tcg/probe.h" +#include "user/cpu_loop.h" +#include "user/guest-host.h" +#include "qemu/main-loop.h" +#include "user/page-protection.h" +#include "exec/page-protection.h" +#include "exec/helper-proto-common.h" #include "qemu/atomic128.h" -#include "trace/trace-root.h" +#include "qemu/bswap.h" +#include "qemu/int128.h" +#include "trace.h" #include "tcg/tcg-ldst.h" -#include "internal.h" +#include "backend-ldst.h" +#include "internal-common.h" +#include "tb-internal.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. */ @@ -108,9 +126,9 @@ MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write) * guest, we'd end up in an infinite loop of retrying the faulting access. */ bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, - uintptr_t host_pc, abi_ptr guest_addr) + uintptr_t host_pc, vaddr guest_addr) { - switch (page_unprotect(guest_addr, host_pc)) { + switch (page_unprotect(cpu, guest_addr, host_pc)) { case 0: /* * Fault not caused by a page marked unwritable to protect @@ -144,7 +162,7 @@ typedef struct PageFlagsNode { static IntervalTreeRoot pageflags_root; -static PageFlagsNode *pageflags_find(target_ulong start, target_ulong last) +static PageFlagsNode *pageflags_find(vaddr start, vaddr last) { IntervalTreeNode *n; @@ -152,8 +170,7 @@ static PageFlagsNode *pageflags_find(target_ulong start, target_ulong last) return n ? container_of(n, PageFlagsNode, itree) : NULL; } -static PageFlagsNode *pageflags_next(PageFlagsNode *p, target_ulong start, - target_ulong last) +static PageFlagsNode *pageflags_next(PageFlagsNode *p, vaddr start, vaddr last) { IntervalTreeNode *n; @@ -182,13 +199,22 @@ int walk_memory_regions(void *priv, walk_memory_regions_fn fn) return rc; } -static int dump_region(void *priv, target_ulong start, - target_ulong end, unsigned long prot) +static int dump_region(void *opaque, vaddr start, vaddr end, int prot) { - FILE *f = (FILE *)priv; + FILE *f = opaque; + uint64_t mask; + int width; - fprintf(f, TARGET_FMT_lx"-"TARGET_FMT_lx" "TARGET_FMT_lx" %c%c%c\n", - start, end, end - start, + if (guest_addr_max <= UINT32_MAX) { + mask = UINT32_MAX, width = 8; + } else { + mask = UINT64_MAX, width = 16; + } + + fprintf(f, "%0*" PRIx64 "-%0*" PRIx64 " %0*" PRIx64 " %c%c%c\n", + width, start & mask, + width, end & mask, + width, (end - start) & mask, ((prot & PAGE_READ) ? 'r' : '-'), ((prot & PAGE_WRITE) ? 'w' : '-'), ((prot & PAGE_EXEC) ? 'x' : '-')); @@ -198,14 +224,14 @@ static int dump_region(void *priv, target_ulong start, /* dump memory mappings */ void page_dump(FILE *f) { - const int length = sizeof(target_ulong) * 2; + int width = guest_addr_max <= UINT32_MAX ? 8 : 16; fprintf(f, "%-*s %-*s %-*s %s\n", - length, "start", length, "end", length, "size", "prot"); + width, "start", width, "end", width, "size", "prot"); walk_memory_regions(f, dump_region); } -int page_get_flags(target_ulong address) +int page_get_flags(vaddr address) { PageFlagsNode *p = pageflags_find(address, address); @@ -228,7 +254,7 @@ int page_get_flags(target_ulong address) } /* 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) +static void pageflags_create(vaddr start, vaddr last, int flags) { PageFlagsNode *p = g_new(PageFlagsNode, 1); @@ -239,13 +265,13 @@ static void pageflags_create(target_ulong start, target_ulong last, int flags) } /* A subroutine of page_set_flags: remove everything in [start,last]. */ -static bool pageflags_unset(target_ulong start, target_ulong last) +static bool pageflags_unset(vaddr start, vaddr last) { bool inval_tb = false; while (true) { PageFlagsNode *p = pageflags_find(start, last); - target_ulong p_last; + vaddr p_last; if (!p) { break; @@ -284,8 +310,7 @@ static bool pageflags_unset(target_ulong start, target_ulong last) * 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) +static void pageflags_create_merge(vaddr start, vaddr last, int flags) { PageFlagsNode *next = NULL, *prev = NULL; @@ -336,11 +361,11 @@ static void pageflags_create_merge(target_ulong start, target_ulong last, #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, +static bool pageflags_set_clear(vaddr start, vaddr last, int set_flags, int clear_flags) { PageFlagsNode *p; - target_ulong p_start, p_last; + vaddr p_start, p_last; int p_flags, merge_flags; bool inval_tb = false; @@ -475,12 +500,7 @@ static bool pageflags_set_clear(target_ulong start, target_ulong last, return inval_tb; } -/* - * Modify the flags of a page and invalidate the code if necessary. - * The flag PAGE_WRITE_ORG is positioned automatically depending - * on PAGE_WRITE. The mmap_lock should already be held. - */ -void page_set_flags(target_ulong start, target_ulong last, int flags) +void page_set_flags(vaddr start, vaddr last, int flags) { bool reset = false; bool inval_tb = false; @@ -489,7 +509,7 @@ void page_set_flags(target_ulong start, target_ulong last, int flags) guest address space. If this assert fires, it probably indicates a missing call to h2g_valid. */ assert(start <= last); - assert(last <= GUEST_ADDR_MAX); + assert(last <= guest_addr_max); /* Only set PAGE_ANON with new mappings. */ assert(!(flags & PAGE_ANON) || (flags & PAGE_RESET)); assert_memory_lock(); @@ -516,13 +536,13 @@ void page_set_flags(target_ulong start, target_ulong last, int flags) ~(reset ? 0 : PAGE_STICKY)); } if (inval_tb) { - tb_invalidate_phys_range(start, last); + tb_invalidate_phys_range(NULL, start, last); } } -bool page_check_range(target_ulong start, target_ulong len, int flags) +bool page_check_range(vaddr start, vaddr len, int flags) { - target_ulong last; + vaddr last; int locked; /* tri-state: =0: unlocked, +1: global, -1: local */ bool ret; @@ -571,7 +591,7 @@ bool page_check_range(target_ulong start, target_ulong len, int flags) break; } /* Asking about writable, but has been protected: undo. */ - if (!page_unprotect(start, 0)) { + if (!page_unprotect(NULL, start, 0)) { ret = false; break; } @@ -598,20 +618,19 @@ bool page_check_range(target_ulong start, target_ulong len, int flags) return ret; } -bool page_check_range_empty(target_ulong start, target_ulong last) +bool page_check_range_empty(vaddr start, vaddr 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) +vaddr page_find_range_empty(vaddr min, vaddr max, vaddr len, vaddr align) { - target_ulong len_m1, align_m1; + vaddr len_m1, align_m1; assert(min <= max); - assert(max <= GUEST_ADDR_MAX); + assert(max <= guest_addr_max); assert(len != 0); assert(is_power_of_2(align)); assert_memory_lock(); @@ -646,20 +665,21 @@ target_ulong page_find_range_empty(target_ulong min, target_ulong max, } } -void page_protect(tb_page_addr_t address) +void tb_lock_page0(tb_page_addr_t address) { PageFlagsNode *p; - target_ulong start, last; + vaddr start, last; + int host_page_size = qemu_real_host_page_size(); int prot; assert_memory_lock(); - if (qemu_host_page_size <= TARGET_PAGE_SIZE) { + if (host_page_size <= TARGET_PAGE_SIZE) { start = address & TARGET_PAGE_MASK; last = start + TARGET_PAGE_SIZE - 1; } else { - start = address & qemu_host_page_mask; - last = start + qemu_host_page_size - 1; + start = address & -host_page_size; + last = start + host_page_size - 1; } p = pageflags_find(start, last); @@ -670,7 +690,7 @@ void page_protect(tb_page_addr_t address) if (unlikely(p->itree.last < last)) { /* More than one protection region covers the one host page. */ - assert(TARGET_PAGE_SIZE < qemu_host_page_size); + assert(TARGET_PAGE_SIZE < host_page_size); while ((p = pageflags_next(p, start, last)) != NULL) { prot |= p->flags; } @@ -678,7 +698,7 @@ void page_protect(tb_page_addr_t address) if (prot & PAGE_WRITE) { pageflags_set_clear(start, last, 0, PAGE_WRITE); - mprotect(g2h_untagged(start), qemu_host_page_size, + mprotect(g2h_untagged(start), last - start + 1, prot & (PAGE_READ | PAGE_EXEC) ? PROT_READ : PROT_NONE); } } @@ -690,11 +710,13 @@ void page_protect(tb_page_addr_t address) * immediately exited. (We can only return 2 if the 'pc' argument is * non-zero.) */ -int page_unprotect(target_ulong address, uintptr_t pc) +int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc) { PageFlagsNode *p; bool current_tb_invalidated; + assert((cpu == NULL) == (pc == 0)); + /* * Technically this isn't safe inside a signal handler. However we * know this only ever happens in a synchronous SEGV handler, so in @@ -717,29 +739,31 @@ int page_unprotect(target_ulong address, uintptr_t pc) * 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; + if (pc && cpu->cc->tcg_ops->precise_smc) { + TranslationBlock *current_tb = tcg_tb_lookup(pc); + if (current_tb) { + current_tb_invalidated = tb_cflags(current_tb) & CF_INVALID; + } } -#endif } else { - target_ulong start, len, i; + int host_page_size = qemu_real_host_page_size(); + vaddr start, len, i; int prot; - if (qemu_host_page_size <= TARGET_PAGE_SIZE) { + 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); + current_tb_invalidated = + tb_invalidate_phys_page_unwind(cpu, start, pc); } else { - start = address & qemu_host_page_mask; - len = qemu_host_page_size; + 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; + vaddr addr = start + i; p = pageflags_find(addr, addr); if (p) { @@ -755,13 +779,13 @@ int page_unprotect(target_ulong address, uintptr_t pc) * the corresponding translated code. */ current_tb_invalidated |= - tb_invalidate_phys_page_unwind(addr, pc); + tb_invalidate_phys_page_unwind(cpu, addr, pc); } } if (prot & PAGE_EXEC) { prot = (prot & ~PAGE_EXEC) | PAGE_READ; } - mprotect((void *)g2h_untagged(start), len, prot & PAGE_BITS); + mprotect((void *)g2h_untagged(start), len, prot & PAGE_RWX); } mmap_unlock(); @@ -793,7 +817,7 @@ static int probe_access_internal(CPUArchState *env, vaddr addr, if (guest_addr_valid_untagged(addr)) { int page_flags = page_get_flags(addr); if (page_flags & acc_flag) { - if ((acc_flag == PAGE_READ || acc_flag == PAGE_WRITE) + if (access_type != MMU_INST_FETCH && cpu_plugin_mem_cbs_enabled(env_cpu(env))) { return TLB_MMIO; } @@ -835,6 +859,12 @@ void *probe_access(CPUArchState *env, vaddr addr, int size, return size ? g2h(env_cpu(env), addr) : NULL; } +void *tlb_vaddr_to_host(CPUArchState *env, vaddr addr, + MMUAccessType access_type, int mmu_idx) +{ + return g2h(env_cpu(env), addr); +} + tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, void **hostp) { @@ -849,7 +879,6 @@ tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, return addr; } -#ifdef TARGET_PAGE_DATA_SIZE /* * 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%. @@ -861,14 +890,20 @@ tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, typedef struct TargetPageDataNode { struct rcu_head rcu; IntervalTreeNode itree; - char data[TPD_PAGES][TARGET_PAGE_DATA_SIZE] __attribute__((aligned)); + char data[] __attribute__((aligned)); } TargetPageDataNode; static IntervalTreeRoot targetdata_root; +static size_t target_page_data_size; -void page_reset_target_data(target_ulong start, target_ulong last) +void page_reset_target_data(vaddr start, vaddr last) { IntervalTreeNode *n, *next; + size_t size = target_page_data_size; + + if (likely(size == 0)) { + return; + } assert_memory_lock(); @@ -880,7 +915,7 @@ void page_reset_target_data(target_ulong start, target_ulong last) n != NULL; n = next, next = next ? interval_tree_iter_next(n, start, last) : NULL) { - target_ulong n_start, n_last, p_ofs, p_len; + vaddr n_start, n_last, p_ofs, p_len; TargetPageDataNode *t = container_of(n, TargetPageDataNode, itree); if (n->start >= start && n->last <= last) { @@ -899,15 +934,21 @@ void page_reset_target_data(target_ulong start, target_ulong last) n_last = MIN(last, n->last); p_len = (n_last + 1 - n_start) >> TARGET_PAGE_BITS; - memset(t->data[p_ofs], 0, p_len * TARGET_PAGE_DATA_SIZE); + memset(t->data + p_ofs * size, 0, p_len * size); } } -void *page_get_target_data(target_ulong address) +void *page_get_target_data(vaddr address, size_t size) { IntervalTreeNode *n; TargetPageDataNode *t; - target_ulong page, region; + vaddr page, region, p_ofs; + + /* Remember the size from the first call, and it should be constant. */ + if (unlikely(target_page_data_size != size)) { + assert(target_page_data_size == 0); + target_page_data_size = size; + } page = address & TARGET_PAGE_MASK; region = address & TBD_MASK; @@ -923,7 +964,7 @@ void *page_get_target_data(target_ulong address) mmap_lock(); n = interval_tree_iter_first(&targetdata_root, page, page); if (!n) { - t = g_new0(TargetPageDataNode, 1); + t = g_malloc0(sizeof(TargetPageDataNode) + TPD_PAGES * size); n = &t->itree; n->start = region; n->last = region | ~TBD_MASK; @@ -933,76 +974,132 @@ void *page_get_target_data(target_ulong address) } t = container_of(n, TargetPageDataNode, itree); - return t->data[(page - region) >> TARGET_PAGE_BITS]; + p_ofs = (page - region) >> TARGET_PAGE_BITS; + return t->data + p_ofs * size; } -#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. */ -static void *cpu_mmu_lookup(CPUArchState *env, vaddr addr, +static void *cpu_mmu_lookup(CPUState *cpu, vaddr addr, MemOp mop, uintptr_t ra, MMUAccessType type) { - 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; } -#include "ldst_atomicity.c.inc" - -static uint8_t do_ld1_mmu(CPUArchState *env, abi_ptr addr, - MemOp mop, uintptr_t ra) +/* physical memory access (slow version, mainly for debug) */ +int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, + void *ptr, size_t len, bool is_write) { - void *haddr; - uint8_t ret; + int flags; + vaddr l, page; + uint8_t *buf = ptr; + ssize_t written; + int ret = -1; + int fd = -1; + + mmap_lock(); + + 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) { + memcpy(g2h(cpu, addr), buf, 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. + */ + tb_invalidate_phys_range(NULL, addr, addr + l - 1); + written = pwrite(fd, buf, l, + (off_t)(uintptr_t)g2h_untagged(addr)); + if (written != l) { + goto out_close; + } + } + } else if (flags & PAGE_READ) { + memcpy(buf, g2h(cpu, addr), l); + } 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: + mmap_unlock(); - tcg_debug_assert((mop & MO_SIZE) == MO_8); - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); - haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_LOAD); - ret = ldub_p(haddr); - clear_helper_retaddr(); return ret; } -tcg_target_ulong helper_ldub_mmu(CPUArchState *env, uint64_t addr, - MemOpIdx oi, uintptr_t ra) -{ - return do_ld1_mmu(env, addr, get_memop(oi), ra); -} +#include "ldst_atomicity.c.inc" -tcg_target_ulong helper_ldsb_mmu(CPUArchState *env, uint64_t 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 (int8_t)do_ld1_mmu(env, addr, get_memop(oi), ra); -} + void *haddr; + uint8_t ret; -uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) -{ - uint8_t ret = do_ld1_mmu(env, addr, get_memop(oi), ra); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); + cpu_req_mo(cpu, 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(); return ret; } -static uint16_t do_ld2_mmu(CPUArchState *env, abi_ptr addr, - MemOp mop, 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); - tcg_debug_assert((mop & MO_SIZE) == MO_16); - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); - haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_LOAD); - ret = load_atom_2(env, ra, haddr, mop); + cpu_req_mo(cpu, 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(); if (mop & MO_BSWAP) { @@ -1011,36 +1108,16 @@ static uint16_t do_ld2_mmu(CPUArchState *env, abi_ptr addr, return ret; } -tcg_target_ulong helper_lduw_mmu(CPUArchState *env, uint64_t addr, - MemOpIdx oi, uintptr_t ra) -{ - return do_ld2_mmu(env, addr, get_memop(oi), ra); -} - -tcg_target_ulong helper_ldsw_mmu(CPUArchState *env, uint64_t addr, - MemOpIdx oi, uintptr_t ra) -{ - return (int16_t)do_ld2_mmu(env, addr, get_memop(oi), ra); -} - -uint16_t cpu_ldw_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) -{ - uint16_t ret = do_ld2_mmu(env, addr, get_memop(oi), ra); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); - return ret; -} - -static uint32_t do_ld4_mmu(CPUArchState *env, abi_ptr addr, - MemOp mop, 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); - tcg_debug_assert((mop & MO_SIZE) == MO_32); - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); - haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_LOAD); - ret = load_atom_4(env, ra, haddr, mop); + cpu_req_mo(cpu, 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(); if (mop & MO_BSWAP) { @@ -1049,36 +1126,16 @@ static uint32_t do_ld4_mmu(CPUArchState *env, abi_ptr addr, return ret; } -tcg_target_ulong helper_ldul_mmu(CPUArchState *env, uint64_t addr, - MemOpIdx oi, uintptr_t ra) -{ - return do_ld4_mmu(env, addr, get_memop(oi), ra); -} - -tcg_target_ulong helper_ldsl_mmu(CPUArchState *env, uint64_t addr, - MemOpIdx oi, uintptr_t ra) -{ - return (int32_t)do_ld4_mmu(env, addr, get_memop(oi), ra); -} - -uint32_t cpu_ldl_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) -{ - uint32_t ret = do_ld4_mmu(env, addr, get_memop(oi), ra); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); - return ret; -} - -static uint64_t do_ld8_mmu(CPUArchState *env, abi_ptr addr, - MemOp mop, 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); - tcg_debug_assert((mop & MO_SIZE) == MO_64); - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); - haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_LOAD); - ret = load_atom_8(env, ra, haddr, mop); + cpu_req_mo(cpu, 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(); if (mop & MO_BSWAP) { @@ -1087,30 +1144,17 @@ static uint64_t do_ld8_mmu(CPUArchState *env, abi_ptr addr, return ret; } -uint64_t helper_ldq_mmu(CPUArchState *env, uint64_t addr, - MemOpIdx oi, uintptr_t ra) -{ - return do_ld8_mmu(env, addr, get_memop(oi), ra); -} - -uint64_t cpu_ldq_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) -{ - uint64_t ret = do_ld8_mmu(env, addr, get_memop(oi), ra); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); - return ret; -} - -static Int128 do_ld16_mmu(CPUArchState *env, abi_ptr addr, - MemOp mop, uintptr_t ra) +static Int128 do_ld16_mmu(CPUState *cpu, vaddr addr, + MemOpIdx oi, uintptr_t ra) { void *haddr; Int128 ret; + MemOp mop = get_memop(oi); tcg_debug_assert((mop & MO_SIZE) == MO_128); - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); - haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_LOAD); - ret = load_atom_16(env, ra, haddr, mop); + cpu_req_mo(cpu, 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(); if (mop & MO_BSWAP) { @@ -1119,266 +1163,103 @@ static Int128 do_ld16_mmu(CPUArchState *env, abi_ptr addr, return ret; } -Int128 helper_ld16_mmu(CPUArchState *env, uint64_t addr, +static void do_st1_mmu(CPUState *cpu, vaddr addr, uint8_t val, MemOpIdx oi, uintptr_t ra) -{ - return do_ld16_mmu(env, addr, get_memop(oi), ra); -} - -Int128 helper_ld_i128(CPUArchState *env, uint64_t addr, MemOpIdx oi) -{ - return helper_ld16_mmu(env, addr, oi, GETPC()); -} - -Int128 cpu_ld16_mmu(CPUArchState *env, abi_ptr addr, - MemOpIdx oi, uintptr_t ra) -{ - Int128 ret = do_ld16_mmu(env, addr, get_memop(oi), ra); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); - return ret; -} - -static void do_st1_mmu(CPUArchState *env, abi_ptr addr, uint8_t val, - MemOp mop, uintptr_t ra) { void *haddr; - tcg_debug_assert((mop & MO_SIZE) == MO_8); - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); - haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_STORE); + cpu_req_mo(cpu, 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(); } -void helper_stb_mmu(CPUArchState *env, uint64_t addr, uint32_t val, - MemOpIdx oi, uintptr_t ra) -{ - do_st1_mmu(env, addr, val, get_memop(oi), ra); -} - -void cpu_stb_mmu(CPUArchState *env, abi_ptr addr, uint8_t val, - MemOpIdx oi, uintptr_t ra) -{ - do_st1_mmu(env, addr, val, get_memop(oi), ra); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); -} - -static void do_st2_mmu(CPUArchState *env, abi_ptr addr, uint16_t val, - MemOp mop, 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); - tcg_debug_assert((mop & MO_SIZE) == MO_16); - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); - haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_STORE); + cpu_req_mo(cpu, 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(env, ra, haddr, mop, val); + store_atom_2(cpu, ra, haddr, mop, val); clear_helper_retaddr(); } -void helper_stw_mmu(CPUArchState *env, uint64_t addr, uint32_t val, - MemOpIdx oi, uintptr_t ra) -{ - do_st2_mmu(env, addr, val, get_memop(oi), ra); -} - -void cpu_stw_mmu(CPUArchState *env, abi_ptr addr, uint16_t val, - MemOpIdx oi, uintptr_t ra) -{ - do_st2_mmu(env, addr, val, get_memop(oi), ra); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); -} - -static void do_st4_mmu(CPUArchState *env, abi_ptr addr, uint32_t val, - MemOp mop, 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); - tcg_debug_assert((mop & MO_SIZE) == MO_32); - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); - haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_STORE); + cpu_req_mo(cpu, 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(env, ra, haddr, mop, val); + store_atom_4(cpu, ra, haddr, mop, val); clear_helper_retaddr(); } -void helper_stl_mmu(CPUArchState *env, uint64_t addr, uint32_t val, - MemOpIdx oi, uintptr_t ra) -{ - do_st4_mmu(env, addr, val, get_memop(oi), ra); -} - -void cpu_stl_mmu(CPUArchState *env, abi_ptr addr, uint32_t val, - MemOpIdx oi, uintptr_t ra) -{ - do_st4_mmu(env, addr, val, get_memop(oi), ra); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); -} - -static void do_st8_mmu(CPUArchState *env, abi_ptr addr, uint64_t val, - MemOp mop, 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); - tcg_debug_assert((mop & MO_SIZE) == MO_64); - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); - haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_STORE); + cpu_req_mo(cpu, 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(env, ra, haddr, mop, val); + store_atom_8(cpu, ra, haddr, mop, val); clear_helper_retaddr(); } -void helper_stq_mmu(CPUArchState *env, uint64_t addr, uint64_t val, - MemOpIdx oi, uintptr_t ra) -{ - do_st8_mmu(env, addr, val, get_memop(oi), ra); -} - -void cpu_stq_mmu(CPUArchState *env, abi_ptr addr, uint64_t val, - MemOpIdx oi, uintptr_t ra) -{ - do_st8_mmu(env, addr, val, get_memop(oi), ra); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); -} - -static void do_st16_mmu(CPUArchState *env, abi_ptr addr, Int128 val, - MemOp mop, 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); - tcg_debug_assert((mop & MO_SIZE) == MO_128); - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); - haddr = cpu_mmu_lookup(env, addr, mop, ra, MMU_DATA_STORE); + cpu_req_mo(cpu, 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(env, ra, haddr, mop, val); - clear_helper_retaddr(); -} - -void helper_st16_mmu(CPUArchState *env, uint64_t addr, Int128 val, - MemOpIdx oi, uintptr_t ra) -{ - do_st16_mmu(env, addr, val, get_memop(oi), ra); -} - -void helper_st_i128(CPUArchState *env, uint64_t addr, Int128 val, MemOpIdx oi) -{ - helper_st16_mmu(env, addr, val, oi, GETPC()); -} - -void cpu_st16_mmu(CPUArchState *env, abi_ptr addr, - Int128 val, MemOpIdx oi, uintptr_t ra) -{ - do_st16_mmu(env, addr, val, get_memop(oi), ra); - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); -} - -uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr ptr) -{ - uint32_t ret; - - set_helper_retaddr(1); - ret = ldub_p(g2h_untagged(ptr)); + store_atom_16(cpu, ra, haddr, mop, val); clear_helper_retaddr(); - return ret; } -uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr ptr) -{ - uint32_t ret; - - set_helper_retaddr(1); - ret = lduw_p(g2h_untagged(ptr)); - clear_helper_retaddr(); - return ret; -} - -uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr ptr) -{ - uint32_t ret; - - set_helper_retaddr(1); - ret = ldl_p(g2h_untagged(ptr)); - clear_helper_retaddr(); - return ret; -} - -uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr) -{ - uint64_t ret; - - set_helper_retaddr(1); - ret = ldq_p(g2h_untagged(ptr)); - clear_helper_retaddr(); - return ret; -} - -uint8_t cpu_ldb_code_mmu(CPUArchState *env, abi_ptr addr, +uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra) { - void *haddr; - uint8_t ret; - - haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_INST_FETCH); - ret = ldub_p(haddr); - clear_helper_retaddr(); - return ret; + return do_ld1_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH); } -uint16_t cpu_ldw_code_mmu(CPUArchState *env, abi_ptr addr, +uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra) { - void *haddr; - uint16_t ret; - - haddr = cpu_mmu_lookup(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; + return do_ld2_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH); } -uint32_t cpu_ldl_code_mmu(CPUArchState *env, abi_ptr addr, +uint32_t cpu_ldl_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra) { - void *haddr; - uint32_t ret; - - haddr = cpu_mmu_lookup(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; + return do_ld4_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH); } -uint64_t cpu_ldq_code_mmu(CPUArchState *env, abi_ptr addr, +uint64_t cpu_ldq_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra) { - void *haddr; - uint64_t ret; - - haddr = cpu_mmu_lookup(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; + return do_ld8_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH); } #include "ldst_common.c.inc" @@ -1386,24 +1267,24 @@ uint64_t cpu_ldq_code_mmu(CPUArchState *env, abi_ptr addr, /* * Do not allow unaligned operations to proceed. Return the host address. */ -static void *atomic_mmu_lookup(CPUArchState *env, vaddr addr, MemOpIdx oi, +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))) { - cpu_loop_exit_sigbus(env_cpu(env), addr, MMU_DATA_STORE, 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; } diff --git a/accel/tcg/vcpu-state.h b/accel/tcg/vcpu-state.h new file mode 100644 index 00000000000..2e3464b5eef --- /dev/null +++ b/accel/tcg/vcpu-state.h @@ -0,0 +1,23 @@ +/* + * TaskState helpers for QEMU + * + * Copyright (c) 2023 Linaro Ltd. + * + * Authors: + * Philippe Mathieu-Daudé + * + * 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 00000000000..cfb37a49e72 --- /dev/null +++ b/accel/tcg/watchpoint.c @@ -0,0 +1,141 @@ +/* + * 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 "exec/breakpoint.h" +#include "exec/cpu-interrupt.h" +#include "exec/page-protection.h" +#include "exec/translation-block.h" +#include "system/tcg.h" +#include "system/replay.h" +#include "accel/tcg/cpu-ops.h" +#include "hw/core/cpu.h" +#include "internal-common.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) +{ + 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 (cpu->cc->tcg_ops->adjust_watchpoint_address) { + /* this is currently used only by ARM BE32 */ + addr = cpu->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 + && cpu->cc->tcg_ops->debug_check_watchpoint + && !cpu->cc->tcg_ops->debug_check_watchpoint(cpu, wp)) { + wp->flags &= ~BP_WATCHPOINT_HIT; + continue; + } + cpu->watchpoint_hit = wp; + + /* This call also restores vCPU state */ + tb_check_watchpoint(cpu, ra); + if (wp->flags & BP_STOP_BEFORE_ACCESS) { + cpu->exception_index = EXCP_DEBUG; + cpu_loop_exit(cpu); + } else { + /* Force execution of one insn next time. */ + cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); + 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 5ff0cb8bd9e..de52a8f882a 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -15,11 +15,13 @@ #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" -#include "sysemu/xen.h" -#include "sysemu/runstate.h" +#include "system/accel-ops.h" +#include "system/cpus.h" +#include "system/xen.h" +#include "system/runstate.h" #include "migration/misc.h" #include "migration/global_state.h" #include "hw/boards.h" @@ -114,7 +116,7 @@ static int xen_init(MachineState *ms) return 0; } -static void xen_accel_class_init(ObjectClass *oc, void *data) +static void xen_accel_class_init(ObjectClass *oc, const void *data) { AccelClass *ac = ACCEL_CLASS(oc); static GlobalProperty compat[] = { @@ -145,7 +147,7 @@ static const TypeInfo xen_accel_type = { .class_init = xen_accel_class_init, }; -static void xen_accel_ops_class_init(ObjectClass *oc, void *data) +static void xen_accel_ops_class_init(ObjectClass *oc, const void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 057571dd1e0..9b6c01c0ef5 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -899,12 +899,12 @@ static void alsa_enable_in(HWVoiceIn *hw, bool enable) static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo) { if (!apdo->has_try_poll) { - apdo->try_poll = true; + apdo->try_poll = false; apdo->has_try_poll = true; } } -static void *alsa_audio_init(Audiodev *dev) +static void *alsa_audio_init(Audiodev *dev, Error **errp) { AudiodevAlsaOptions *aopts; assert(dev->driver == AUDIODEV_DRIVER_ALSA); @@ -960,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 index 1237ce9e750..8774c09f185 100644 --- a/audio/audio-hmp-cmds.c +++ b/audio/audio-hmp-cmds.c @@ -26,7 +26,8 @@ #include "audio/audio.h" #include "monitor/hmp.h" #include "monitor/monitor.h" -#include "qapi/qmp/qdict.h" +#include "qapi/error.h" +#include "qobject/qdict.h" static QLIST_HEAD (capture_list_head, CaptureState) capture_head; @@ -65,10 +66,11 @@ void hmp_wavcapture(Monitor *mon, const QDict *qdict) 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); + Error *local_err = NULL; + AudioState *as = audio_state_by_name(audiodev, &local_err); if (!as) { - monitor_printf(mon, "Audiodev '%s' not found\n", audiodev); + error_report_err(local_err); return; } diff --git a/audio/audio.c b/audio/audio.c index 90c7c49d116..89f091bc882 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -32,13 +32,15 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-audio.h" #include "qapi/qapi-commands-audio.h" +#include "qobject/qdict.h" #include "qemu/cutils.h" +#include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/module.h" #include "qemu/help_option.h" -#include "sysemu/sysemu.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/replay.h" +#include "system/runstate.h" #include "ui/qemu-spice.h" #include "trace.h" @@ -61,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; @@ -99,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, @@ -111,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) { @@ -901,6 +905,14 @@ size_t AUD_read(SWVoiceIn *sw, void *buf, size_t size) int AUD_get_buffer_size_out(SWVoiceOut *sw) { + if (!sw) { + return 0; + } + + if (audio_get_pdo_out(sw->s->dev)->mixing_engine) { + return sw->resample_buf.size * sw->info.bytes_per_frame; + } + return sw->hw->samples * sw->hw->info.bytes_per_frame; } @@ -1553,9 +1565,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) { @@ -1567,13 +1581,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; } @@ -1653,6 +1669,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); @@ -1674,24 +1691,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; } /* @@ -1700,62 +1723,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); @@ -1767,56 +1744,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; @@ -1824,37 +1784,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) @@ -1876,10 +1850,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) { @@ -1900,10 +1872,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)); @@ -1922,7 +1892,8 @@ CaptureVoiceOut *AUD_add_capture( 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]; + hw->clip = mixeng_clip_float[hw->info.nchannels == 2] + [hw->info.swap_endianness]; } else { hw->clip = mixeng_clip [hw->info.nchannels == 2] @@ -1937,8 +1908,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) @@ -2184,17 +2156,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) @@ -2256,7 +2235,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) { @@ -2265,6 +2244,7 @@ AudioState *audio_state_by_name(const char *name) return s; } } + error_setg(errp, "audiodev '%s' not found", name); return NULL; } @@ -2303,17 +2283,19 @@ size_t audio_rate_peek_bytes(RateCtl *rate, struct audio_pcm_info *info) ticks = now - rate->start_ticks; bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND); frames = (bytes - rate->bytes_sent) / info->bytes_per_frame; - if (frames < 0 || frames > 65536) { - AUD_log(NULL, "Resetting rate control (%" PRId64 " frames)\n", frames); - audio_rate_start(rate); - frames = 0; - } + rate->peeked_frames = frames; - return frames * info->bytes_per_frame; + return frames < 0 ? 0 : frames * info->bytes_per_frame; } void audio_rate_add_bytes(RateCtl *rate, size_t bytes_used) { + if (rate->peeked_frames < 0 || rate->peeked_frames > 65536) { + AUD_log(NULL, "Resetting rate control (%" PRId64 " frames)\n", + rate->peeked_frames); + audio_rate_start(rate); + } + rate->bytes_sent += bytes_used; } diff --git a/audio/audio.h b/audio/audio.h index 01bdc567fb1..fcc22307bee 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 e57ff50155a..f78ca05f92f 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -140,13 +140,12 @@ 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, bool p2p); #endif struct audio_pcm_ops *pcm_ops; - int can_be_default; int max_voices_out; int max_voices_in; size_t voice_size_out; @@ -243,7 +242,6 @@ 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); @@ -257,6 +255,7 @@ const char *audio_application_name(void); typedef struct RateCtl { int64_t start_ticks; int64_t bytes_sent; + int64_t peeked_frames; } RateCtl; void audio_rate_start(RateCtl *rate); @@ -297,9 +296,6 @@ typedef struct AudiodevListEntry { } 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 dc72ba55e9a..00000000000 --- a/audio/audio_legacy.c +++ /dev/null @@ -1,591 +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) -{ - uint64_t ret; - if (parse_uint_full(str, 10, &ret) || 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) -{ - const char *val = getenv(env); - if (val) { - g_free(*dst); - *dst = g_strdup(val); - } -} - -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); - } -} - - -#if defined(CONFIG_AUDIO_ALSA) || defined(CONFIG_AUDIO_DSOUND) -static void get_millis_to_usecs(const char *env, uint32_t *dst, bool *has_dst) -{ - const char *val = getenv(env); - if (val) { - *dst = toui32(val) * 1000; - *has_dst = true; - } -} -#endif - -#if defined(CONFIG_AUDIO_ALSA) || defined(CONFIG_AUDIO_COREAUDIO) || \ - defined(CONFIG_AUDIO_PA) || defined(CONFIG_AUDIO_SDL) || \ - defined(CONFIG_AUDIO_DSOUND) || defined(CONFIG_AUDIO_OSS) -static uint32_t frames_to_usecs(uint32_t frames, - AudiodevPerDirectionOptions *pdo) -{ - uint32_t freq = pdo->has_frequency ? pdo->frequency : 44100; - return (frames * 1000000 + freq / 2) / freq; -} -#endif - -#ifdef CONFIG_AUDIO_COREAUDIO -static void get_frames_to_usecs(const char *env, uint32_t *dst, bool *has_dst, - AudiodevPerDirectionOptions *pdo) -{ - const char *val = getenv(env); - if (val) { - *dst = frames_to_usecs(toui32(val), pdo); - *has_dst = true; - } -} -#endif - -#if defined(CONFIG_AUDIO_PA) || defined(CONFIG_AUDIO_SDL) || \ - defined(CONFIG_AUDIO_DSOUND) || defined(CONFIG_AUDIO_OSS) -static uint32_t samples_to_usecs(uint32_t samples, - AudiodevPerDirectionOptions *pdo) -{ - uint32_t channels = pdo->has_channels ? pdo->channels : 2; - return frames_to_usecs(samples / channels, pdo); -} -#endif - -#if defined(CONFIG_AUDIO_PA) || defined(CONFIG_AUDIO_SDL) -static void get_samples_to_usecs(const char *env, uint32_t *dst, bool *has_dst, - AudiodevPerDirectionOptions *pdo) -{ - const char *val = getenv(env); - if (val) { - *dst = samples_to_usecs(toui32(val), pdo); - *has_dst = true; - } -} -#endif - -#if defined(CONFIG_AUDIO_DSOUND) || defined(CONFIG_AUDIO_OSS) -static uint32_t bytes_to_usecs(uint32_t bytes, AudiodevPerDirectionOptions *pdo) -{ - AudioFormat fmt = pdo->has_format ? pdo->format : AUDIO_FORMAT_S16; - 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; - } -} -#endif - -/* backend specific functions */ - -#ifdef CONFIG_AUDIO_ALSA -/* 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); - - 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); -} -#endif - -#ifdef CONFIG_AUDIO_COREAUDIO -/* 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); -} -#endif - -#ifdef CONFIG_AUDIO_DSOUND -/* 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); -} -#endif - -#ifdef CONFIG_AUDIO_OSS -/* 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); - - 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); -} -#endif - -#ifdef CONFIG_AUDIO_PA -/* pulseaudio */ -static void handle_pa_per_direction( - AudiodevPaPerDirectionOptions *ppdo, const char *env) -{ - get_str(env, &ppdo->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); -} -#endif - -#ifdef CONFIG_AUDIO_SDL -/* 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)); -} -#endif - -/* 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); -} - -/* 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) { -#ifdef CONFIG_AUDIO_ALSA - case AUDIODEV_DRIVER_ALSA: - handle_alsa(e->dev); - break; -#endif - -#ifdef CONFIG_AUDIO_COREAUDIO - case AUDIODEV_DRIVER_COREAUDIO: - handle_coreaudio(e->dev); - break; -#endif - -#ifdef CONFIG_AUDIO_DSOUND - case AUDIODEV_DRIVER_DSOUND: - handle_dsound(e->dev); - break; -#endif - -#ifdef CONFIG_AUDIO_OSS - case AUDIODEV_DRIVER_OSS: - handle_oss(e->dev); - break; -#endif - -#ifdef CONFIG_AUDIO_PA - case AUDIODEV_DRIVER_PA: - handle_pa(e->dev); - break; -#endif - -#ifdef CONFIG_AUDIO_SDL - case AUDIODEV_DRIVER_SDL: - handle_sdl(e->dev); - break; -#endif - - case AUDIODEV_DRIVER_WAV: - handle_wav(e->dev); - 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 dc0c74aa746..c29d79c4439 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); 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); @@ -167,9 +174,11 @@ static int glue (audio_pcm_sw_init_, TYPE) ( if (sw->info.is_float) { #ifdef DAC - sw->conv = mixeng_conv_float[sw->info.nchannels == 2]; + sw->conv = mixeng_conv_float[sw->info.nchannels == 2] + [sw->info.swap_endianness]; #else - sw->clip = mixeng_clip_float[sw->info.nchannels == 2]; + sw->clip = mixeng_clip_float[sw->info.nchannels == 2] + [sw->info.swap_endianness]; #endif } else { #ifdef DAC @@ -296,9 +305,11 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, if (hw->info.is_float) { #ifdef DAC - hw->clip = mixeng_clip_float[hw->info.nchannels == 2]; + hw->clip = mixeng_clip_float[hw->info.nchannels == 2] + [hw->info.swap_endianness]; #else - hw->conv = mixeng_conv_float[hw->info.nchannels == 2]; + hw->conv = mixeng_conv_float[hw->info.nchannels == 2] + [hw->info.swap_endianness]; #endif } else { #ifdef DAC diff --git a/audio/coreaudio.m b/audio/coreaudio.m index 4695291621a..cadd729d505 100644 --- a/audio/coreaudio.m +++ b/audio/coreaudio.m @@ -44,11 +44,6 @@ 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 @@ static ret_type glue(coreaudio_, name)args_decl \ #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 void coreaudio_audio_fini (void *opaque) .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 7a11fbfb420..b44fdd15111 100644 --- a/audio/dbusaudio.c +++ b/audio/dbusaudio.c @@ -43,9 +43,10 @@ #define DBUS_DISPLAY1_AUDIO_PATH DBUS_DISPLAY1_ROOT "/Audio" -#define DBUS_AUDIO_NSAMPLES 1024 /* could be configured? */ +#define DBUS_DEFAULT_AUDIO_NSAMPLES 480 typedef struct DBusAudio { + Audiodev *dev; GDBusObjectManagerServer *server; bool p2p; GDBusObjectSkeleton *audio; @@ -105,7 +106,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; @@ -151,6 +152,18 @@ dbus_init_out_listener(QemuDBusDisplay1AudioOutListener *listener, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } +static guint +dbus_audio_get_nsamples(DBusAudio *da) +{ + AudiodevDBusOptions *opts = &da->dev->u.dbus; + + if (opts->has_nsamples && opts->nsamples) { + return opts->nsamples; + } else { + return DBUS_DEFAULT_AUDIO_NSAMPLES; + } +} + static int dbus_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) { @@ -160,7 +173,7 @@ dbus_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) QemuDBusDisplay1AudioOutListener *listener = NULL; audio_pcm_init_info(&hw->info, as); - hw->samples = DBUS_AUDIO_NSAMPLES; + hw->samples = dbus_audio_get_nsamples(da); audio_rate_start(&vo->rate); g_hash_table_iter_init(&iter, da->out_listeners); @@ -274,7 +287,7 @@ dbus_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) QemuDBusDisplay1AudioInListener *listener = NULL; audio_pcm_init_info(&hw->info, as); - hw->samples = DBUS_AUDIO_NSAMPLES; + hw->samples = dbus_audio_get_nsamples(da); audio_rate_start(&vo->rate); g_hash_table_iter_init(&iter, da->in_listeners); @@ -395,10 +408,11 @@ 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); + da->dev = dev; da->out_listeners = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref); da->in_listeners = g_hash_table_new_full(g_str_hash, g_str_equal, @@ -524,11 +538,17 @@ dbus_audio_register_listener(AudioState *s, ); } + GDBusConnectionFlags flags = + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER; +#ifdef WIN32 + flags |= G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; +#endif + listener_conn = g_dbus_connection_new_sync( G_IO_STREAM(socket_conn), guid, - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER, + flags, NULL, NULL, &err); if (err) { error_report("Failed to setup peer connection: %s", err->message); @@ -646,6 +666,7 @@ dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p) "swapped-signal::handle-register-out-listener", dbus_audio_register_out_listener, s, NULL); + qemu_dbus_display1_audio_set_nsamples(da->iface, dbus_audio_get_nsamples(da)); g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio), G_DBUS_INTERFACE_SKELETON(da->iface)); @@ -676,7 +697,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 3fb67ec3eed..f3bb48d0073 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 5bdf3d7a78d..974a3caad34 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 df4d968c0fe..59f0a431d51 100644 --- a/audio/meson.build +++ b/audio/meson.build @@ -1,7 +1,6 @@ system_ss.add([spice_headers, files('audio.c')]) system_ss.add(files( 'audio-hmp-cmds.c', - 'audio_legacy.c', 'mixeng.c', 'noaudio.c', 'wavaudio.c', @@ -31,7 +30,8 @@ endforeach if dbus_display module_ss = ss.source_set() - module_ss.add(when: [gio, pixman], 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 69f6549224f..703ee5448fa 100644 --- a/audio/mixeng.c +++ b/audio/mixeng.c @@ -283,10 +283,15 @@ static const float float_scale_reciprocal = 1.f / ((int64_t)INT32_MAX + 1); #endif #endif +#define F32_TO_F32S(v) \ + bswap32((union { uint32_t i; float f; }){ .f = (v) }.i) +#define F32S_TO_F32(v) \ + ((union { uint32_t i; float f; }){ .i = bswap32(v) }.f) + static void conv_natural_float_to_mono(struct st_sample *dst, const void *src, int samples) { - float *in = (float *)src; + const float *in = src; while (samples--) { dst->r = dst->l = CONV_NATURAL_FLOAT(*in++); @@ -294,10 +299,21 @@ static void conv_natural_float_to_mono(struct st_sample *dst, const void *src, } } +static void conv_swap_float_to_mono(struct st_sample *dst, const void *src, + int samples) +{ + const uint32_t *in_f32s = src; + + while (samples--) { + dst->r = dst->l = CONV_NATURAL_FLOAT(F32S_TO_F32(*in_f32s++)); + dst++; + } +} + static void conv_natural_float_to_stereo(struct st_sample *dst, const void *src, int samples) { - float *in = (float *)src; + const float *in = src; while (samples--) { dst->l = CONV_NATURAL_FLOAT(*in++); @@ -306,15 +322,33 @@ static void conv_natural_float_to_stereo(struct st_sample *dst, const void *src, } } -t_sample *mixeng_conv_float[2] = { - conv_natural_float_to_mono, - conv_natural_float_to_stereo, +static void conv_swap_float_to_stereo(struct st_sample *dst, const void *src, + int samples) +{ + const uint32_t *in_f32s = src; + + while (samples--) { + dst->l = CONV_NATURAL_FLOAT(F32S_TO_F32(*in_f32s++)); + dst->r = CONV_NATURAL_FLOAT(F32S_TO_F32(*in_f32s++)); + dst++; + } +} + +t_sample *mixeng_conv_float[2][2] = { + { + conv_natural_float_to_mono, + conv_swap_float_to_mono, + }, + { + conv_natural_float_to_stereo, + conv_swap_float_to_stereo, + } }; static void clip_natural_float_from_mono(void *dst, const struct st_sample *src, int samples) { - float *out = (float *)dst; + float *out = dst; while (samples--) { *out++ = CLIP_NATURAL_FLOAT(src->l + src->r); @@ -322,10 +356,21 @@ static void clip_natural_float_from_mono(void *dst, const struct st_sample *src, } } +static void clip_swap_float_from_mono(void *dst, const struct st_sample *src, + int samples) +{ + uint32_t *out_f32s = dst; + + while (samples--) { + *out_f32s++ = F32_TO_F32S(CLIP_NATURAL_FLOAT(src->l + src->r)); + src++; + } +} + static void clip_natural_float_from_stereo( void *dst, const struct st_sample *src, int samples) { - float *out = (float *)dst; + float *out = dst; while (samples--) { *out++ = CLIP_NATURAL_FLOAT(src->l); @@ -334,9 +379,27 @@ static void clip_natural_float_from_stereo( } } -f_sample *mixeng_clip_float[2] = { - clip_natural_float_from_mono, - clip_natural_float_from_stereo, +static void clip_swap_float_from_stereo( + void *dst, const struct st_sample *src, int samples) +{ + uint32_t *out_f32s = dst; + + while (samples--) { + *out_f32s++ = F32_TO_F32S(CLIP_NATURAL_FLOAT(src->l)); + *out_f32s++ = F32_TO_F32S(CLIP_NATURAL_FLOAT(src->r)); + src++; + } +} + +f_sample *mixeng_clip_float[2][2] = { + { + clip_natural_float_from_mono, + clip_swap_float_from_mono, + }, + { + clip_natural_float_from_stereo, + clip_swap_float_from_stereo, + } }; void audio_sample_to_uint64(const void *samples, int pos, diff --git a/audio/mixeng.h b/audio/mixeng.h index a5f56d2c268..ead93ac2f78 100644 --- a/audio/mixeng.h +++ b/audio/mixeng.h @@ -42,9 +42,9 @@ typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); extern t_sample *mixeng_conv[2][2][2][3]; extern f_sample *mixeng_clip[2][2][2][3]; -/* indices: [stereo] */ -extern t_sample *mixeng_conv_float[2]; -extern f_sample *mixeng_clip_float[2]; +/* indices: [stereo][swap endianness] */ +extern t_sample *mixeng_conv_float[2][2]; +extern f_sample *mixeng_clip_float[2][2]; void *st_rate_start (int inrate, int outrate); void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf, diff --git a/audio/noaudio.c b/audio/noaudio.c index 4fdee5adecf..1b60d8518a4 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 e8d732b612c..c5858284a19 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" @@ -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,8 +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->dev ?: "/dev/dsp", R_OK | W_OK) < 0 || - access(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; @@ -779,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 529b39daacc..f3193b08c32 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 @@ -818,7 +818,7 @@ static void *qpa_conn_init(const char *server) 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; @@ -834,10 +834,12 @@ static void *qpa_audio_init(Audiodev *dev) 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; } } @@ -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 index b6a38738ee9..8e13b582865 100644 --- a/audio/pwaudio.c +++ b/audio/pwaudio.c @@ -11,8 +11,8 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "audio.h" -#include #include "qemu/error-report.h" +#include "qapi/error.h" #include #include #include @@ -736,7 +736,7 @@ static const struct pw_core_events core_events = { }; static void * -qpw_audio_init(Audiodev *dev) +qpw_audio_init(Audiodev *dev, Error **errp) { g_autofree pwaudio *pw = g_new0(pwaudio, 1); @@ -748,19 +748,19 @@ qpw_audio_init(Audiodev *dev) pw->dev = dev; pw->thread_loop = pw_thread_loop_new("PipeWire thread loop", NULL); if (pw->thread_loop == NULL) { - error_report("Could not create PipeWire loop: %s", g_strerror(errno)); + 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_report("Could not create PipeWire context: %s", g_strerror(errno)); + error_setg_errno(errp, errno, "Could not create PipeWire context"); goto fail; } if (pw_thread_loop_start(pw->thread_loop) < 0) { - error_report("Could not start PipeWire loop: %s", g_strerror(errno)); + error_setg_errno(errp, errno, "Could not start PipeWire loop"); goto fail; } @@ -769,12 +769,14 @@ qpw_audio_init(Audiodev *dev) 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) { @@ -786,7 +788,6 @@ qpw_audio_init(Audiodev *dev) return g_steal_pointer(&pw); fail: - AUD_log(AUDIO_CAP, "Failed to initialize PW context"); if (pw->thread_loop) { pw_thread_loop_stop(pw->thread_loop); } @@ -841,7 +842,6 @@ static struct audio_driver pw_audio_driver = { .init = qpw_audio_init, .fini = qpw_audio_fini, .pcm_ops = &qpw_pcm_ops, - .can_be_default = 1, .max_voices_out = INT_MAX, .max_voices_in = INT_MAX, .voice_size_out = sizeof(PWVoiceOut), diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c index 68a237b76b4..641357e5ee3 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 3fde01fdbd5..8eb35e1e538 100644 --- a/audio/sndioaudio.c +++ b/audio/sndioaudio.c @@ -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 d17ef1a25ef..7f02f7285cf 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 ab04f020ce8..7e3f1593c81 100644 --- a/audio/trace-events +++ b/audio/trace-events @@ -15,7 +15,7 @@ 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 diff --git a/audio/wavaudio.c b/audio/wavaudio.c index 6445a2cb90c..a8798a1c425 100644 --- a/audio/wavaudio.c +++ b/audio/wavaudio.c @@ -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/list.c b/authz/list.c index 0e17eed8974..17aa0efd80e 100644 --- a/authz/list.c +++ b/authz/list.c @@ -116,7 +116,7 @@ qauthz_list_finalize(Object *obj) static void -qauthz_list_class_init(ObjectClass *oc, void *data) +qauthz_list_class_init(ObjectClass *oc, const void *data) { QAuthZClass *authz = QAUTHZ_CLASS(oc); @@ -253,7 +253,7 @@ static const TypeInfo qauthz_list_info = { .instance_size = sizeof(QAuthZList), .instance_finalize = qauthz_list_finalize, .class_init = qauthz_list_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/authz/listfile.c b/authz/listfile.c index 45a60e987df..13741d5a722 100644 --- a/authz/listfile.c +++ b/authz/listfile.c @@ -28,8 +28,8 @@ #include "qemu/filemonitor.h" #include "qom/object_interfaces.h" #include "qapi/qapi-visit-authz.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qobject.h" +#include "qobject/qjson.h" +#include "qobject/qobject.h" #include "qapi/qobject-input-visitor.h" @@ -220,7 +220,7 @@ qauthz_list_file_finalize(Object *obj) static void -qauthz_list_file_class_init(ObjectClass *oc, void *data) +qauthz_list_file_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); QAuthZClass *authz = QAUTHZ_CLASS(oc); @@ -272,7 +272,7 @@ static const TypeInfo qauthz_list_file_info = { .instance_size = sizeof(QAuthZListFile), .instance_finalize = qauthz_list_file_finalize, .class_init = qauthz_list_file_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/authz/pamacct.c b/authz/pamacct.c index c862d9ff39b..c0ad67479a7 100644 --- a/authz/pamacct.c +++ b/authz/pamacct.c @@ -103,7 +103,7 @@ qauthz_pam_finalize(Object *obj) static void -qauthz_pam_class_init(ObjectClass *oc, void *data) +qauthz_pam_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); QAuthZClass *authz = QAUTHZ_CLASS(oc); @@ -136,7 +136,7 @@ static const TypeInfo qauthz_pam_info = { .instance_size = sizeof(QAuthZPAM), .instance_finalize = qauthz_pam_finalize, .class_init = qauthz_pam_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/authz/simple.c b/authz/simple.c index 0597dcd8ea5..f8f2b98518a 100644 --- a/authz/simple.c +++ b/authz/simple.c @@ -78,7 +78,7 @@ qauthz_simple_complete(UserCreatable *uc, Error **errp) static void -qauthz_simple_class_init(ObjectClass *oc, void *data) +qauthz_simple_class_init(ObjectClass *oc, const void *data) { QAuthZClass *authz = QAUTHZ_CLASS(oc); UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); @@ -111,7 +111,7 @@ static const TypeInfo qauthz_simple_info = { .instance_size = sizeof(QAuthZSimple), .instance_finalize = qauthz_simple_finalize, .class_init = qauthz_simple_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/backends/Kconfig b/backends/Kconfig index f35abc16092..d3dbe198680 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/confidential-guest-support.c b/backends/confidential-guest-support.c index 052fde8db04..8ff7bfa8570 100644 --- a/backends/confidential-guest-support.c +++ b/backends/confidential-guest-support.c @@ -13,14 +13,15 @@ #include "qemu/osdep.h" -#include "exec/confidential-guest-support.h" +#include "system/confidential-guest-support.h" OBJECT_DEFINE_ABSTRACT_TYPE(ConfidentialGuestSupport, confidential_guest_support, CONFIDENTIAL_GUEST_SUPPORT, OBJECT) -static void confidential_guest_support_class_init(ObjectClass *oc, void *data) +static void confidential_guest_support_class_init(ObjectClass *oc, + const void *data) { } diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c index 39d04552807..0414c01e06d 100644 --- a/backends/cryptodev-builtin.c +++ b/backends/cryptodev-builtin.c @@ -22,7 +22,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/cryptodev.h" +#include "system/cryptodev.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "standard-headers/linux/virtio_crypto.h" #include "crypto/cipher.h" @@ -63,11 +64,11 @@ static void cryptodev_builtin_init_akcipher(CryptoDevBackend *backend) { QCryptoAkCipherOptions opts; - opts.alg = QCRYPTO_AKCIPHER_ALG_RSA; - opts.u.rsa.padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW; + 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_AKCIPHER); + (1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_AKCIPHER); backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA; } } @@ -92,9 +93,9 @@ static void cryptodev_builtin_init( backend->conf.peers.ccs[0] = cc; backend->conf.crypto_services = - 1u << QCRYPTODEV_BACKEND_SERVICE_CIPHER | - 1u << QCRYPTODEV_BACKEND_SERVICE_HASH | - 1u << QCRYPTODEV_BACKEND_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; /* @@ -137,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; } @@ -168,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); @@ -199,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; } @@ -270,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", @@ -317,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; @@ -333,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: @@ -396,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; } @@ -427,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) { @@ -546,23 +549,23 @@ static int cryptodev_builtin_operation( CryptoDevBackendBuiltinSession *sess; CryptoDevBackendSymOpInfo *sym_op_info; CryptoDevBackendAsymOpInfo *asym_op_info; - QCryptodevBackendAlgType 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 == QCRYPTODEV_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 == QCRYPTODEV_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); @@ -605,7 +608,7 @@ static void cryptodev_builtin_cleanup( } static void -cryptodev_builtin_class_init(ObjectClass *oc, void *data) +cryptodev_builtin_class_init(ObjectClass *oc, const void *data) { CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc); diff --git a/backends/cryptodev-hmp-cmds.c b/backends/cryptodev-hmp-cmds.c index 4f7220bb133..01396d227c8 100644 --- a/backends/cryptodev-hmp-cmds.c +++ b/backends/cryptodev-hmp-cmds.c @@ -14,7 +14,7 @@ #include "monitor/hmp.h" #include "monitor/monitor.h" #include "qapi/qapi-commands-cryptodev.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" void hmp_info_cryptodev(Monitor *mon, const QDict *qdict) diff --git a/backends/cryptodev-lkcf.c b/backends/cryptodev-lkcf.c index 45aba1ff67b..bb7a81d5d06 100644 --- a/backends/cryptodev-lkcf.c +++ b/backends/cryptodev-lkcf.c @@ -30,7 +30,7 @@ #include "qemu/error-report.h" #include "qemu/queue.h" #include "qom/object.h" -#include "sysemu/cryptodev.h" +#include "system/cryptodev.h" #include "standard-headers/linux/virtio_crypto.h" #include @@ -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; } @@ -230,7 +230,7 @@ static void cryptodev_lkcf_init(CryptoDevBackend *backend, Error **errp) backend->conf.peers.ccs[0] = cc; backend->conf.crypto_services = - 1u << QCRYPTODEV_BACKEND_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, @@ -330,6 +330,8 @@ static void cryptodev_lkcf_execute_task(CryptoDevLKCFTask *task) cryptodev_lkcf_set_op_desc(&session->akcipher_opts, op_desc, sizeof(op_desc), &local_error) != 0) { error_report_err(local_error); + status = -VIRTIO_CRYPTO_ERR; + goto out; } else { key_id = add_key(KCTL_KEY_TYPE_PKEY, "lkcf-backend-priv-key", p8info, p8info_len, KCTL_KEY_RING); @@ -346,6 +348,7 @@ static void cryptodev_lkcf_execute_task(CryptoDevLKCFTask *task) session->key, session->keylen, &local_error); if (!akcipher) { + error_report_err(local_error); status = -VIRTIO_CRYPTO_ERR; goto out; } @@ -474,7 +477,7 @@ static int cryptodev_lkcf_operation( CryptoDevBackendLKCF *lkcf = CRYPTODEV_BACKEND_LKCF(backend); CryptoDevBackendLKCFSession *sess; - QCryptodevBackendAlgType algtype = op_info->algtype; + QCryptodevBackendAlgoType algtype = op_info->algtype; CryptoDevLKCFTask *task; if (op_info->session_id >= MAX_SESSIONS || @@ -485,7 +488,7 @@ static int cryptodev_lkcf_operation( } sess = lkcf->sess[op_info->session_id]; - if (algtype != QCRYPTODEV_BACKEND_ALG_ASYM) { + if (algtype != QCRYPTODEV_BACKEND_ALGO_TYPE_ASYM) { error_report("algtype not supported: %u", algtype); return -VIRTIO_CRYPTO_NOTSUPP; } @@ -518,7 +521,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) { @@ -534,11 +537,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: @@ -616,7 +619,7 @@ static int cryptodev_lkcf_close_session(CryptoDevBackend *backend, return 0; } -static void cryptodev_lkcf_class_init(ObjectClass *oc, void *data) +static void cryptodev_lkcf_class_init(ObjectClass *oc, const void *data) { CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc); diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c index c3283ba84a5..cb04e68b022 100644 --- a/backends/cryptodev-vhost-user.c +++ b/backends/cryptodev-vhost-user.c @@ -27,9 +27,9 @@ #include "qemu/error-report.h" #include "hw/virtio/vhost-user.h" #include "standard-headers/linux/virtio_crypto.h" -#include "sysemu/cryptodev-vhost.h" +#include "system/cryptodev-vhost.h" #include "chardev/char-fe.h" -#include "sysemu/cryptodev-vhost-user.h" +#include "system/cryptodev-vhost-user.h" #include "qom/object.h" @@ -221,9 +221,9 @@ static void cryptodev_vhost_user_init( cryptodev_vhost_user_event, NULL, s, NULL, true); backend->conf.crypto_services = - 1u << QCRYPTODEV_BACKEND_SERVICE_CIPHER | - 1u << QCRYPTODEV_BACKEND_SERVICE_HASH | - 1u << QCRYPTODEV_BACKEND_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; @@ -281,8 +281,7 @@ static int cryptodev_vhost_user_create_session( break; 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; } @@ -394,7 +393,7 @@ static void cryptodev_vhost_user_finalize(Object *obj) } static void -cryptodev_vhost_user_class_init(ObjectClass *oc, void *data) +cryptodev_vhost_user_class_init(ObjectClass *oc, const void *data) { CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc); diff --git a/backends/cryptodev-vhost.c b/backends/cryptodev-vhost.c index 93523732f39..943680a23af 100644 --- a/backends/cryptodev-vhost.c +++ b/backends/cryptodev-vhost.c @@ -24,13 +24,13 @@ #include "qemu/osdep.h" #include "hw/virtio/virtio-bus.h" -#include "sysemu/cryptodev-vhost.h" +#include "system/cryptodev-vhost.h" #ifdef CONFIG_VHOST_CRYPTO #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/virtio/virtio-crypto.h" -#include "sysemu/cryptodev-vhost-user.h" +#include "system/cryptodev-vhost-user.h" uint64_t cryptodev_vhost_get_max_queues( @@ -53,7 +53,7 @@ cryptodev_vhost_init( CryptoDevBackendVhost *crypto; Error *local_err = NULL; - crypto = g_new(CryptoDevBackendVhost, 1); + crypto = g_new0(CryptoDevBackendVhost, 1); crypto->dev.max_queues = 1; crypto->dev.nvqs = 1; crypto->dev.vqs = crypto->vqs; diff --git a/backends/cryptodev.c b/backends/cryptodev.c index 4d183f7237a..79f8882d3be 100644 --- a/backends/cryptodev.c +++ b/backends/cryptodev.c @@ -22,8 +22,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/cryptodev.h" -#include "sysemu/stats.h" +#include "system/cryptodev.h" +#include "system/stats.h" #include "qapi/error.h" #include "qapi/qapi-commands-cryptodev.h" #include "qapi/qapi-types-stats.h" @@ -74,7 +74,7 @@ static int qmp_query_cryptodev_foreach(Object *obj, void *data) backend = CRYPTODEV_BACKEND(obj); services = backend->conf.crypto_services; - for (i = 0; i < QCRYPTODEV_BACKEND_SERVICE__MAX; i++) { + for (i = 0; i < QCRYPTODEV_BACKEND_SERVICE_TYPE__MAX; i++) { if (services & (1 << i)) { QAPI_LIST_PREPEND(info->service, i); } @@ -97,7 +97,7 @@ static int qmp_query_cryptodev_foreach(Object *obj, void *data) QCryptodevInfoList *qmp_query_cryptodev(Error **errp) { QCryptodevInfoList *list = NULL; - Object *objs = container_get(object_get_root(), "/objects"); + Object *objs = object_get_container("objects"); object_child_foreach(objs, qmp_query_cryptodev_foreach, &list); @@ -185,10 +185,10 @@ static int cryptodev_backend_operation( static int cryptodev_backend_account(CryptoDevBackend *backend, CryptoDevBackendOpInfo *op_info) { - enum QCryptodevBackendAlgType algtype = op_info->algtype; + enum QCryptodevBackendAlgoType algtype = op_info->algtype; int len; - if (algtype == QCRYPTODEV_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; @@ -212,7 +212,7 @@ static int cryptodev_backend_account(CryptoDevBackend *backend, default: return -VIRTIO_CRYPTO_NOTSUPP; } - } else if (algtype == QCRYPTODEV_BACKEND_ALG_SYM) { + } else if (algtype == QCRYPTODEV_BACKEND_ALGO_TYPE_SYM) { CryptoDevBackendSymOpInfo *sym_op_info = op_info->u.sym_op_info; len = sym_op_info->src_len; @@ -252,10 +252,11 @@ static void cryptodev_backend_throttle_timer_cb(void *opaque) continue; } - throttle_account(&backend->ts, true, ret); + 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, true)) { + throttle_schedule_timer(&backend->ts, &backend->tt, + THROTTLE_WRITE)) { break; } } @@ -271,7 +272,7 @@ int cryptodev_backend_crypto_operation( goto do_account; } - if (throttle_schedule_timer(&backend->ts, &backend->tt, true) || + if (throttle_schedule_timer(&backend->ts, &backend->tt, THROTTLE_WRITE) || !QTAILQ_EMPTY(&backend->opinfos)) { QTAILQ_INSERT_TAIL(&backend->opinfos, op_info, next); return 0; @@ -283,7 +284,7 @@ int cryptodev_backend_crypto_operation( return ret; } - throttle_account(&backend->ts, true, ret); + throttle_account(&backend->ts, THROTTLE_WRITE, ret); return cryptodev_backend_operation(backend, op_info); } @@ -341,8 +342,7 @@ static void cryptodev_backend_set_throttle(CryptoDevBackend *backend, int field, if (!enabled) { throttle_init(&backend->ts); throttle_timers_init(&backend->tt, qemu_get_aio_context(), - QEMU_CLOCK_REALTIME, - cryptodev_backend_throttle_timer_cb, /* FIXME */ + QEMU_CLOCK_REALTIME, NULL, cryptodev_backend_throttle_timer_cb, backend); } @@ -398,6 +398,7 @@ static void cryptodev_backend_set_ops(Object *obj, Visitor *v, 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; @@ -406,19 +407,28 @@ cryptodev_backend_complete(UserCreatable *uc, Error **errp) QTAILQ_INIT(&backend->opinfos); value = backend->tc.buckets[THROTTLE_OPS_TOTAL].avg; cryptodev_backend_set_throttle(backend, THROTTLE_OPS_TOTAL, value, errp); + if (*errp) { + return; + } value = backend->tc.buckets[THROTTLE_BPS_TOTAL].avg; cryptodev_backend_set_throttle(backend, THROTTLE_BPS_TOTAL, value, errp); + if (*errp) { + return; + } if (bc->init) { bc->init(backend, errp); + if (*errp) { + return; + } } services = backend->conf.crypto_services; - if (services & (1 << QCRYPTODEV_BACKEND_SERVICE_CIPHER)) { + if (services & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_CIPHER)) { backend->sym_stat = g_new0(CryptodevBackendSymStat, 1); } - if (services & (1 << QCRYPTODEV_BACKEND_SERVICE_AKCIPHER)) { + if (services & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_AKCIPHER)) { backend->asym_stat = g_new0(CryptodevBackendAsymStat, 1); } } @@ -547,7 +557,7 @@ static void cryptodev_backend_stats_cb(StatsResultList **result, switch (target) { case STATS_TARGET_CRYPTODEV: { - Object *objs = container_get(object_get_root(), "/objects"); + Object *objs = object_get_container("objects"); StatsArgs stats_args; stats_args.result.stats = result; stats_args.names = names; @@ -598,7 +608,7 @@ static void cryptodev_backend_schemas_cb(StatsSchemaList **result, } static void -cryptodev_backend_class_init(ObjectClass *oc, void *data) +cryptodev_backend_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); @@ -631,7 +641,7 @@ static const TypeInfo cryptodev_backend_info = { .instance_finalize = cryptodev_backend_finalize, .class_size = sizeof(CryptoDevBackendClass), .class_init = cryptodev_backend_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/backends/dbus-vmstate.c b/backends/dbus-vmstate.c index 57369ec0f22..7d5b58b4c90 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"); } } @@ -486,7 +485,7 @@ dbus_vmstate_get_id(VMStateIf *vmif) } static void -dbus_vmstate_class_init(ObjectClass *oc, void *data) +dbus_vmstate_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); VMStateIfClass *vc = VMSTATE_IF_CLASS(oc); @@ -506,7 +505,7 @@ static const TypeInfo dbus_vmstate_info = { .instance_size = sizeof(DBusVMState), .instance_finalize = dbus_vmstate_finalize, .class_init = dbus_vmstate_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { TYPE_VMSTATE_IF }, { } diff --git a/backends/host_iommu_device.c b/backends/host_iommu_device.c new file mode 100644 index 00000000000..f6965e4027b --- /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 "system/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, const 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 4e162d6789e..ab20b182331 100644 --- a/backends/hostmem-epc.c +++ b/backends/hostmem-epc.c @@ -14,34 +14,31 @@ #include #include "qom/object_interfaces.h" #include "qapi/error.h" -#include "sysemu/hostmem.h" +#include "system/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); + ram_flags = (backend->share ? RAM_SHARED : RAM_PRIVATE) | RAM_PROTECTED; + 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) @@ -53,7 +50,7 @@ static void sgx_epc_backend_instance_init(Object *obj) m->dump = false; } -static void sgx_epc_backend_class_init(ObjectClass *oc, void *data) +static void sgx_epc_backend_class_init(ObjectClass *oc, const void *data) { HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c index b4335a80e6d..8e3219c0617 100644 --- a/backends/hostmem-file.c +++ b/backends/hostmem-file.c @@ -15,9 +15,11 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/madvise.h" -#include "sysemu/hostmem.h" +#include "system/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) @@ -31,38 +33,65 @@ struct HostMemoryBackendFile { 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 = backend->share ? RAM_SHARED : RAM_PRIVATE; + 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; ram_flags |= RAM_NAMED_FILE; - memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), name, - backend->size, fb->align, ram_flags, - fb->mem_path, fb->offset, fb->readonly, - errp); - g_free(name); + 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 } @@ -201,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); @@ -215,7 +270,7 @@ static void file_backend_unparent(Object *obj) } static void -file_backend_class_init(ObjectClass *oc, void *data) +file_backend_class_init(ObjectClass *oc, const void *data) { HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); @@ -243,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 3fc85c3db81..923239f9cf6 100644 --- a/backends/hostmem-memfd.c +++ b/backends/hostmem-memfd.c @@ -11,14 +11,13 @@ */ #include "qemu/osdep.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "qom/object_interfaces.h" #include "qemu/memfd.h" #include "qemu/module.h" #include "qapi/error.h" #include "qom/object.h" - -#define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd" +#include "migration/cpr.h" OBJECT_DECLARE_SIMPLE_TYPE(HostMemoryBackendMemfd, MEMORY_BACKEND_MEMFD) @@ -31,17 +30,21 @@ 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 = host_memory_backend_get_name(backend); + int fd = cpr_find_fd(name, 0); uint32_t ram_flags; - char *name; - int fd; if (!backend->size) { error_setg(errp, "can't create backend with size 0"); - return; + return false; + } + + if (fd >= 0) { + goto have_fd; } fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, @@ -49,15 +52,17 @@ 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; } + cpr_save_fd(name, 0, fd); - name = host_memory_backend_get_name(backend); - ram_flags = backend->share ? RAM_SHARED : 0; +have_fd: + backend->aligned = true; + ram_flags = backend->share ? RAM_SHARED : RAM_PRIVATE; 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 @@ -128,7 +133,7 @@ memfd_backend_instance_init(Object *obj) } static void -memfd_backend_class_init(ObjectClass *oc, void *data) +memfd_backend_class_init(ObjectClass *oc, const void *data) { HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); diff --git a/backends/hostmem-ram.c b/backends/hostmem-ram.c index b8e55cdbd0f..062b1abb116 100644 --- a/backends/hostmem-ram.c +++ b/backends/hostmem-ram.c @@ -11,32 +11,33 @@ */ #include "qemu/osdep.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "qapi/error.h" #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->share ? RAM_SHARED : RAM_PRIVATE; 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 -ram_backend_class_init(ObjectClass *oc, void *data) +ram_backend_class_init(ObjectClass *oc, const void *data) { HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); diff --git a/backends/hostmem-shm.c b/backends/hostmem-shm.c new file mode 100644 index 00000000000..f66211a2ec9 --- /dev/null +++ b/backends/hostmem-shm.c @@ -0,0 +1,92 @@ +/* + * 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 "system/hostmem.h" +#include "qapi/error.h" +#include "migration/cpr.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_autofree char *backend_name = host_memory_backend_get_name(backend); + uint32_t ram_flags; + int fd = cpr_find_fd(backend_name, 0); + + 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; + } + + if (fd >= 0) { + goto have_fd; + } + + fd = qemu_shm_alloc(backend->size, errp); + if (fd < 0) { + return false; + } + cpr_save_fd(backend_name, 0, fd); + +have_fd: + /* Let's do the same as memory-backend-ram,share=on would do. */ + 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, const 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 747e7838c03..35734d6f4d1 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "hw/boards.h" #include "qapi/error.h" #include "qapi/qapi-builtin-visit.h" @@ -20,6 +20,8 @@ #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 @@ -168,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) @@ -194,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) @@ -219,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) { @@ -237,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; @@ -279,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; } @@ -324,91 +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; - 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; - } + 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; + } - /* 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); + /* + * 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. - */ - mode = MPOL_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. + */ + mode = MPOL_PREFERRED_MANY; + } #endif - 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; - } + 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; } + } #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; - } - } + /* + * 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; } -out: - error_propagate(errp, local_err); } static bool @@ -481,7 +501,7 @@ host_memory_backend_set_use_canonical_path(Object *obj, bool value, } static void -host_memory_backend_class_init(ObjectClass *oc, void *data) +host_memory_backend_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); @@ -566,7 +586,7 @@ static const TypeInfo host_memory_backend_info = { .instance_size = sizeof(HostMemoryBackend), .instance_init = host_memory_backend_init, .instance_post_init = host_memory_backend_post_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/backends/iommufd.c b/backends/iommufd.c new file mode 100644 index 00000000000..c2c47abf7ed --- /dev/null +++ b/backends/iommufd.c @@ -0,0 +1,415 @@ +/* + * 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 "system/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-device.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, const 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?"); + } + } + 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; + } + 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; +} + +bool iommufd_backend_invalidate_cache(IOMMUFDBackend *be, uint32_t id, + uint32_t data_type, uint32_t entry_len, + uint32_t *entry_num, void *data, + Error **errp) +{ + int ret, fd = be->fd; + uint32_t total_entries = *entry_num; + struct iommu_hwpt_invalidate cache = { + .size = sizeof(cache), + .hwpt_id = id, + .data_type = data_type, + .entry_len = entry_len, + .entry_num = total_entries, + .data_uptr = (uintptr_t)data, + }; + + ret = ioctl(fd, IOMMU_HWPT_INVALIDATE, &cache); + trace_iommufd_backend_invalidate_cache(fd, id, data_type, entry_len, + total_entries, cache.entry_num, + (uintptr_t)data, ret ? errno : 0); + *entry_num = cache.entry_num; + + if (ret) { + error_setg_errno(errp, errno, "IOMMU_HWPT_INVALIDATE failed:" + " total %d entries, processed %d entries", + total_entries, cache.entry_num); + } else if (total_entries != cache.entry_num) { + error_setg(errp, "IOMMU_HWPT_INVALIDATE succeed but with unprocessed" + " entries: total %d entries, processed %d entries." + " Kernel BUG?!", total_entries, cache.entry_num); + return false; + } + + return !ret; +} + +bool host_iommu_device_iommufd_attach_hwpt(HostIOMMUDeviceIOMMUFD *idev, + uint32_t hwpt_id, Error **errp) +{ + HostIOMMUDeviceIOMMUFDClass *idevc = + HOST_IOMMU_DEVICE_IOMMUFD_GET_CLASS(idev); + + g_assert(idevc->attach_hwpt); + return idevc->attach_hwpt(idev, hwpt_id, errp); +} + +bool host_iommu_device_iommufd_detach_hwpt(HostIOMMUDeviceIOMMUFD *idev, + Error **errp) +{ + HostIOMMUDeviceIOMMUFDClass *idevc = + HOST_IOMMU_DEVICE_IOMMUFD_GET_CLASS(idev); + + g_assert(idevc->detach_hwpt); + return idevc->detach_hwpt(idev, errp); +} + +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, const 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 = (const InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } + }, { + .name = TYPE_HOST_IOMMU_DEVICE_IOMMUFD, + .parent = TYPE_HOST_IOMMU_DEVICE, + .instance_size = sizeof(HostIOMMUDeviceIOMMUFD), + .class_size = sizeof(HostIOMMUDeviceIOMMUFDClass), + .class_init = hiod_iommufd_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(types) diff --git a/backends/meson.build b/backends/meson.build index 914c7c4afb9..9b88d226851 100644 --- a/backends/meson.build +++ b/backends/meson.build @@ -10,9 +10,17 @@ system_ss.add([files( 'confidential-guest-support.c', ), numa]) -system_ss.add(when: 'CONFIG_POSIX', if_true: files('rng-random.c')) -system_ss.add(when: 'CONFIG_POSIX', if_true: files('hostmem-file.c')) -system_ss.add(when: 'CONFIG_LINUX', if_true: files('hostmem-memfd.c')) +if host_os != 'windows' + system_ss.add(files('rng-random.c')) + if host_os != 'emscripten' + system_ss.add(files('hostmem-file.c')) + system_ss.add([files('hostmem-shm.c'), rt]) + endif +endif +if host_os == 'linux' + system_ss.add(files('hostmem-memfd.c')) + system_ss.add(files('host_iommu_device.c')) +endif if keyutils.found() system_ss.add(keyutils, files('cryptodev-lkcf.c')) endif @@ -20,10 +28,13 @@ if have_vhost_user system_ss.add(when: 'CONFIG_VIRTIO', if_true: files('vhost-user.c')) endif 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 system_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('cryptodev-vhost-user.c')) endif 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-builtin.c b/backends/rng-builtin.c index f367eb665cf..41b7bfaa27f 100644 --- a/backends/rng-builtin.c +++ b/backends/rng-builtin.c @@ -6,11 +6,11 @@ */ #include "qemu/osdep.h" -#include "sysemu/rng.h" +#include "system/rng.h" #include "qemu/main-loop.h" #include "qemu/guest-random.h" #include "qom/object.h" -#include "sysemu/replay.h" +#include "system/replay.h" OBJECT_DECLARE_SIMPLE_TYPE(RngBuiltin, RNG_BUILTIN) @@ -55,7 +55,7 @@ static void rng_builtin_finalize(Object *obj) qemu_bh_delete(s->bh); } -static void rng_builtin_class_init(ObjectClass *klass, void *data) +static void rng_builtin_class_init(ObjectClass *klass, const void *data) { RngBackendClass *rbc = RNG_BACKEND_CLASS(klass); diff --git a/backends/rng-egd.c b/backends/rng-egd.c index 684c3cf3d61..9fd3393ede2 100644 --- a/backends/rng-egd.c +++ b/backends/rng-egd.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/rng.h" +#include "system/rng.h" #include "chardev/char-fe.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" @@ -143,7 +143,7 @@ static void rng_egd_finalize(Object *obj) g_free(s->chr_name); } -static void rng_egd_class_init(ObjectClass *klass, void *data) +static void rng_egd_class_init(ObjectClass *klass, const void *data) { RngBackendClass *rbc = RNG_BACKEND_CLASS(klass); diff --git a/backends/rng-random.c b/backends/rng-random.c index 80eb5be138c..820bf48c9b4 100644 --- a/backends/rng-random.c +++ b/backends/rng-random.c @@ -11,8 +11,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/rng-random.h" -#include "sysemu/rng.h" +#include "system/rng-random.h" +#include "system/rng.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/main-loop.h" @@ -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); } } @@ -124,7 +121,7 @@ static void rng_random_finalize(Object *obj) g_free(s->filename); } -static void rng_random_class_init(ObjectClass *klass, void *data) +static void rng_random_class_init(ObjectClass *klass, const void *data) { RngBackendClass *rbc = RNG_BACKEND_CLASS(klass); diff --git a/backends/rng.c b/backends/rng.c index 9bbd0c77b69..ab94dfea850 100644 --- a/backends/rng.c +++ b/backends/rng.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/rng.h" +#include "system/rng.h" #include "qapi/error.h" #include "qemu/module.h" #include "qom/object_interfaces.h" @@ -99,7 +99,7 @@ static void rng_backend_finalize(Object *obj) rng_backend_free_requests(s); } -static void rng_backend_class_init(ObjectClass *oc, void *data) +static void rng_backend_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); @@ -119,7 +119,7 @@ static const TypeInfo rng_backend_info = { .class_size = sizeof(RngBackendClass), .class_init = rng_backend_class_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/backends/spdm-socket.c b/backends/spdm-socket.c new file mode 100644 index 00000000000..2c709c68c87 --- /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 "system/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/tpm_backend.c b/backends/tpm/tpm_backend.c index 485a20b9e09..8cf80043ac5 100644 --- a/backends/tpm/tpm_backend.c +++ b/backends/tpm/tpm_backend.c @@ -13,9 +13,9 @@ */ #include "qemu/osdep.h" -#include "sysemu/tpm_backend.h" +#include "system/tpm_backend.h" #include "qapi/error.h" -#include "sysemu/tpm.h" +#include "system/tpm.h" #include "qemu/thread.h" #include "qemu/main-loop.h" #include "qemu/module.h" diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c index 402a2d6312a..4a234ab2c0b 100644 --- a/backends/tpm/tpm_emulator.c +++ b/backends/tpm/tpm_emulator.c @@ -32,9 +32,9 @@ #include "qemu/sockets.h" #include "qemu/lockable.h" #include "io/channel-socket.h" -#include "sysemu/runstate.h" -#include "sysemu/tpm_backend.h" -#include "sysemu/tpm_util.h" +#include "system/runstate.h" +#include "system/tpm_backend.h" +#include "system/tpm_util.h" #include "tpm_int.h" #include "tpm_ioctl.h" #include "migration/blocker.h" @@ -72,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; @@ -123,15 +123,17 @@ 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); + g_autofree uint8_t *buf = g_malloc(n); + memcpy(buf, &cmd_no, sizeof(cmd_no)); memcpy(buf + sizeof(cmd_no), msg, msg_len_in); @@ -140,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; } @@ -204,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; @@ -239,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); @@ -254,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 */ @@ -290,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; @@ -317,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; @@ -349,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; @@ -396,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)); @@ -437,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; @@ -466,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)); @@ -497,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) { @@ -527,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; } } @@ -560,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; @@ -707,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)); @@ -809,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; @@ -907,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; } @@ -942,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, @@ -978,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); } /* @@ -994,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) { @@ -1016,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); @@ -1031,7 +1056,7 @@ static void tpm_emulator_inst_finalize(Object *obj) vmstate_unregister(NULL, &vmstate_tpm_emulator, obj); } -static void tpm_emulator_class_init(ObjectClass *klass, void *data) +static void tpm_emulator_class_init(ObjectClass *klass, const void *data) { TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); diff --git a/backends/tpm/tpm_int.h b/backends/tpm/tpm_int.h index ba6109306e5..2319a1ce0c4 100644 --- a/backends/tpm/tpm_int.h +++ b/backends/tpm/tpm_int.h @@ -13,7 +13,7 @@ #define BACKENDS_TPM_INT_H #include "qemu/option.h" -#include "sysemu/tpm.h" +#include "system/tpm.h" #define TPM_STANDARD_CMDLINE_OPTS \ { \ diff --git a/backends/tpm/tpm_ioctl.h b/backends/tpm/tpm_ioctl.h index 1933ab68557..ee2dd15d35e 100644 --- a/backends/tpm/tpm_ioctl.h +++ b/backends/tpm/tpm_ioctl.h @@ -29,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 { @@ -242,7 +252,8 @@ struct ptm_lockstorage { } 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 179697a3a94..b7c7074c2aa 100644 --- a/backends/tpm/tpm_passthrough.c +++ b/backends/tpm/tpm_passthrough.c @@ -26,8 +26,8 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/sockets.h" -#include "sysemu/tpm_backend.h" -#include "sysemu/tpm_util.h" +#include "system/tpm_backend.h" +#include "system/tpm_util.h" #include "tpm_int.h" #include "qapi/clone-visitor.h" #include "qapi/qapi-visit-tpm.h" @@ -364,7 +364,7 @@ static void tpm_passthrough_inst_finalize(Object *obj) qapi_free_TPMPassthroughOptions(tpm_pt->options); } -static void tpm_passthrough_class_init(ObjectClass *klass, void *data) +static void tpm_passthrough_class_init(ObjectClass *klass, const void *data) { TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); diff --git a/backends/tpm/tpm_util.c b/backends/tpm/tpm_util.c index 1856589c3b7..f2d1739e336 100644 --- a/backends/tpm/tpm_util.c +++ b/backends/tpm/tpm_util.c @@ -21,13 +21,14 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "qemu/cutils.h" #include "qapi/error.h" #include "qapi/visitor.h" #include "tpm_int.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-properties.h" -#include "sysemu/tpm_backend.h" -#include "sysemu/tpm_util.h" +#include "system/tpm_backend.h" +#include "system/tpm_util.h" #include "trace.h" /* tpm backend property */ @@ -46,7 +47,7 @@ static void get_tpm(Object *obj, Visitor *v, const char *name, void *opaque, static void set_tpm(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; TPMBackend *s, **be = object_field_prop_ptr(obj, prop); char *str; @@ -66,7 +67,7 @@ static void set_tpm(Object *obj, Visitor *v, const char *name, void *opaque, static void release_tpm(Object *obj, const char *name, void *opaque) { - Property *prop = opaque; + const Property *prop = opaque; TPMBackend **be = object_field_prop_ptr(obj, prop); if (*be) { @@ -75,7 +76,7 @@ static void release_tpm(Object *obj, const char *name, void *opaque) } const PropertyInfo qdev_prop_tpm = { - .name = "str", + .type = "str", .description = "ID of a tpm to use as a backend", .get = get_tpm, .set = set_tpm, @@ -336,27 +337,23 @@ void tpm_sized_buffer_reset(TPMSizedBuffer *tsb) void tpm_util_show_buffer(const unsigned char *buffer, size_t buffer_size, const char *string) { - size_t len, i; - char *line_buffer, *p; + g_autoptr(GString) str = NULL; + size_t len, i, l; - 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 - * newline after every 16 chars and a final null terminator. - */ - line_buffer = g_malloc(len * 3 + (len / 16) + 1); - - for (i = 0, p = line_buffer; i < len; i++) { - if (i && !(i % 16)) { - p += sprintf(p, "\n"); + for (i = 0; i < len; i += l) { + if (str) { + g_string_append_c(str, '\n'); } - p += sprintf(p, "%.2X ", buffer[i]); + l = MIN(len, 16); + str = qemu_hexdump_line(str, buffer, l, 1, 0); } - trace_tpm_util_show_buffer(string, len, line_buffer); - g_free(line_buffer); + g_string_ascii_up(str); + trace_tpm_util_show_buffer_content(str->str); } diff --git a/backends/tpm/trace-events b/backends/tpm/trace-events index 1ecef42a074..05e30533ce2 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 652eb76a572..7278214ea51 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -5,3 +5,17 @@ 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)" +iommufd_backend_invalidate_cache(int iommufd, uint32_t id, uint32_t data_type, uint32_t entry_len, uint32_t entry_num, uint32_t done_num, uint64_t data_ptr, int ret) " iommufd=%d id=%u data_type=%u entry_len=%u entry_num=%u done_num=%u data_ptr=0x%"PRIx64" (%d)" diff --git a/backends/vhost-user.c b/backends/vhost-user.c index 94c6a82d526..42845329e72 100644 --- a/backends/vhost-user.c +++ b/backends/vhost-user.c @@ -15,8 +15,8 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qom/object_interfaces.h" -#include "sysemu/vhost-user-backend.h" -#include "sysemu/kvm.h" +#include "system/vhost-user-backend.h" +#include "system/kvm.h" #include "io/channel-command.h" #include "hw/virtio/virtio-bus.h" @@ -97,30 +97,28 @@ vhost_user_backend_start(VhostUserBackend *b) vhost_dev_disable_notifiers(&b->dev, b->vdev); } -void +int vhost_user_backend_stop(VhostUserBackend *b) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(b->vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - int ret = 0; + int ret; if (!b->started) { - return; + return 0; } - vhost_dev_stop(&b->dev, b->vdev, true); + ret = vhost_dev_stop(&b->dev, b->vdev, true); - if (k->set_guest_notifiers) { - ret = k->set_guest_notifiers(qbus->parent, - b->dev.nvqs, false); - if (ret < 0) { - error_report("vhost guest notifier cleanup failed: %d", ret); - } + if (k->set_guest_notifiers && + k->set_guest_notifiers(qbus->parent, b->dev.nvqs, false) < 0) { + error_report("vhost guest notifier cleanup failed: %d", ret); + return -1; } - assert(ret >= 0); vhost_dev_disable_notifiers(&b->dev, b->vdev); b->started = false; + return ret; } static void set_chardev(Object *obj, const char *value, Error **errp) @@ -163,7 +161,7 @@ static char *get_chardev(Object *obj, Error **errp) return NULL; } -static void vhost_user_backend_class_init(ObjectClass *oc, void *data) +static void vhost_user_backend_class_init(ObjectClass *oc, const void *data) { object_class_property_add_str(oc, "chardev", get_chardev, set_chardev); } diff --git a/block.c b/block.c index 8da89aaa623..bfd4340b24d 100644 --- a/block.c +++ b/block.c @@ -36,13 +36,13 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qnull.h" +#include "qobject/qstring.h" #include "qapi/qobject-output-visitor.h" #include "qapi/qapi-visit-block-core.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/notify.h" #include "qemu/option.h" #include "qemu/coroutine.h" @@ -86,14 +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 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, @@ -103,9 +106,9 @@ static void bdrv_reopen_abort(BDRVReopenState *reopen_state); static bool bdrv_backing_overridden(BlockDriverState *bs); -static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, - GHashTable *visited, Transaction *tran, - Error **errp); +static bool GRAPH_RDLOCK +bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, + GHashTable *visited, Transaction *tran, Error **errp); /* If non-zero, use only whitelisted block drivers */ static int use_bdrv_whitelist; @@ -277,8 +280,9 @@ bool bdrv_is_read_only(BlockDriverState *bs) return !(bs->open_flags & BDRV_O_RDWR); } -static 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(); @@ -369,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; @@ -530,9 +535,9 @@ typedef struct CreateCo { int coroutine_fn bdrv_co_create(BlockDriver *drv, const char *filename, QemuOpts *opts, Error **errp) { + ERRP_GUARD(); int ret; GLOBAL_STATE_CODE(); - ERRP_GUARD(); if (!drv->bdrv_co_create_opts) { error_setg(errp, "Driver '%s' does not support image creation", @@ -629,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; @@ -816,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); } @@ -916,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 @@ -1190,19 +1200,19 @@ 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); } -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, NULL, false); } -static void bdrv_child_cb_drained_end(BdrvChild *child) +static void GRAPH_RDLOCK bdrv_child_cb_drained_end(BdrvChild *child) { BlockDriverState *bs = child->opaque; bdrv_drained_end(bs); @@ -1216,9 +1226,10 @@ static int bdrv_child_cb_inactivate(BdrvChild *child) return 0; } -static bool bdrv_child_cb_change_aio_ctx(BdrvChild *child, AioContext *ctx, - GHashTable *visited, Transaction *tran, - Error **errp) +static bool GRAPH_RDLOCK +bdrv_child_cb_change_aio_ctx(BdrvChild *child, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp) { BlockDriverState *bs = child->opaque; return bdrv_change_aio_context(bs, ctx, visited, tran, errp); @@ -1248,7 +1259,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; @@ -1300,11 +1311,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) { @@ -1314,9 +1328,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"); } @@ -1470,10 +1498,14 @@ static void GRAPH_WRLOCK 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; } @@ -1542,6 +1574,10 @@ static void update_flags_from_options(int *flags, QemuOpts *opts) if (qemu_opt_get_bool_del(opts, BDRV_OPT_AUTO_READ_ONLY, false)) { *flags |= BDRV_O_AUTO_RDONLY; } + + if (!qemu_opt_get_bool_del(opts, BDRV_OPT_ACTIVE, true)) { + *flags |= BDRV_O_INACTIVE; + } } static void update_options_from_flags(QDict *options, int flags) @@ -1607,16 +1643,10 @@ static void bdrv_assign_node_name(BlockDriverState *bs, g_free(gen_node_name); } -/* - * The caller must always hold @bs AioContext lock, because this function calls - * bdrv_refresh_total_sectors() which polls when called from non-coroutine - * context. - */ static int no_coroutine_fn GRAPH_UNLOCKED bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, QDict *options, int open_flags, Error **errp) { - AioContext *ctx; Error *local_err = NULL; int i, ret; GLOBAL_STATE_CODE(); @@ -1630,10 +1660,8 @@ bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, 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; @@ -1664,21 +1692,15 @@ bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, bs->supported_read_flags |= BDRV_REQ_REGISTERED_BUF; bs->supported_write_flags |= BDRV_REQ_REGISTERED_BUF; - /* Get the context after .bdrv_open, it can change the context */ - ctx = bdrv_get_aio_context(bs); - aio_context_acquire(ctx); - ret = bdrv_refresh_total_sectors(bs, bs->total_sectors); if (ret < 0) { error_setg_errno(errp, -ret, "Could not refresh total sector count"); - aio_context_release(ctx); return ret; } bdrv_graph_rdlock_main_loop(); bdrv_refresh_limits(bs, NULL, &local_err); bdrv_graph_rdunlock_main_loop(); - aio_context_release(ctx); if (local_err) { error_propagate(errp, local_err); @@ -1698,10 +1720,16 @@ bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, return 0; open_failed: bs->drv = NULL; + + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); if (bs->file != NULL) { bdrv_unref_child(bs, bs->file); assert(!bs->file); } + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); + g_free(bs->opaque); bs->opaque = NULL; return ret; @@ -1778,6 +1806,11 @@ QemuOptsList bdrv_runtime_opts = { .type = QEMU_OPT_BOOL, .help = "Ignore flush requests", }, + { + .name = BDRV_OPT_ACTIVE, + .type = QEMU_OPT_BOOL, + .help = "Node is activated", + }, { .name = BDRV_OPT_READ_ONLY, .type = QEMU_OPT_BOOL, @@ -1843,9 +1876,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)) { @@ -1870,7 +1906,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 { /* @@ -1897,7 +1936,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; } @@ -1952,7 +1993,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; @@ -1968,6 +2009,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, static QDict *parse_json_filename(const char *filename, Error **errp) { + ERRP_GUARD(); QObject *options_obj; QDict *options; int ret; @@ -2026,7 +2068,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; @@ -2052,7 +2095,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) { @@ -2068,7 +2111,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"); @@ -2115,7 +2158,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; @@ -2201,7 +2243,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(); @@ -2226,11 +2269,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(); @@ -2254,8 +2298,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; @@ -2318,7 +2362,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; @@ -2331,7 +2375,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(); @@ -2346,9 +2390,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) { @@ -2374,20 +2422,22 @@ 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) { /* @@ -2404,6 +2454,7 @@ static void bdrv_replace_child_abort(void *opaque) } assert(s->child->quiesced_parent); bdrv_replace_child_noperm(s->child, s->old_bs); + bdrv_unref(new_bs); } @@ -2421,10 +2472,14 @@ static TransactionActionDrv bdrv_replace_child_drv = { * 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); @@ -2440,6 +2495,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 */ } @@ -2447,9 +2503,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; @@ -2522,9 +2582,13 @@ static int bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q, /* * @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 bdrv_do_refresh_perms(GSList *list, BlockReopenQueue *q, - Transaction *tran, Error **errp) +static int GRAPH_RDLOCK +bdrv_do_refresh_perms(GSList *list, BlockReopenQueue *q, Transaction *tran, + Error **errp) { int ret; BlockDriverState *bs; @@ -2550,9 +2614,13 @@ static int bdrv_do_refresh_perms(GSList *list, BlockReopenQueue *q, * @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 bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, - Transaction *tran, Error **errp) +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; @@ -2611,9 +2679,14 @@ char *bdrv_perm_names(uint64_t perm) } -/* @tran is allowed to be NULL. In this case no rollback is possible */ -static int bdrv_refresh_perms(BlockDriverState *bs, Transaction *tran, - 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 *local_tran = NULL; @@ -2857,10 +2930,10 @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm) * 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 and the caller must hold the AioContext lock for @new_bs. + * @child. */ -static void bdrv_replace_child_noperm(BdrvChild *child, - BlockDriverState *new_bs) +static void GRAPH_WRLOCK +bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs) { BlockDriverState *old_bs = child->bs; int new_bs_quiesce_counter; @@ -2895,8 +2968,6 @@ static void bdrv_replace_child_noperm(BdrvChild *child, assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)); } - /* TODO Pull this up into the callers to avoid polling here */ - bdrv_graph_wrlock(new_bs); if (old_bs) { if (child->klass->detach) { child->klass->detach(child); @@ -2912,7 +2983,6 @@ static void bdrv_replace_child_noperm(BdrvChild *child, child->klass->attach(child); } } - bdrv_graph_wrunlock(); /* * If the parent was drained through this BdrvChild previously, but new_bs @@ -2935,6 +3005,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); @@ -2947,16 +3019,19 @@ 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) { - bdrv_try_change_aio_context(bs, s->old_child_ctx, NULL, &error_abort); + bdrv_try_change_aio_context_locked(bs, s->old_child_ctx, NULL, + &error_abort); } if (bdrv_child_get_parent_aio_context(s->child) != s->old_parent_ctx) { @@ -2977,7 +3052,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); } @@ -2991,27 +3066,40 @@ 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. * - * The caller must hold the AioContext lock for @child_bs. Both @parent_bs and - * @child_bs can move to a different AioContext in this function. Callers must - * make sure that their AioContext locking is still correct after this. + * Both @parent_bs and @child_bs can move to a different AioContext in this + * function. + * + * All block nodes must be drained before this function is called until after + * the transaction is finalized. */ -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, *new_child_ctx; + AioContext *parent_ctx; AioContext *child_ctx = bdrv_get_aio_context(child_bs); assert(child_class->get_parent_desc); GLOBAL_STATE_CODE(); + if (bdrv_is_inactive(child_bs) && (perm & ~BLK_PERM_CONSISTENT_READ)) { + g_autofree char *perm_names = bdrv_perm_names(perm); + error_setg(errp, "Permission '%s' unavailable on inactive node", + perm_names); + return NULL; + } + new_child = g_new(BdrvChild, 1); *new_child = (BdrvChild) { .bs = NULL, @@ -3031,22 +3119,23 @@ static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs, parent_ctx = bdrv_child_get_parent_aio_context(new_child); if (child_ctx != parent_ctx) { Error *local_err = NULL; - int ret = bdrv_try_change_aio_context(child_bs, parent_ctx, NULL, - &local_err); + int ret = bdrv_try_change_aio_context_locked(child_bs, parent_ctx, NULL, + &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); } @@ -3057,12 +3146,6 @@ static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs, } } - new_child_ctx = bdrv_get_aio_context(child_bs); - if (new_child_ctx != child_ctx) { - aio_context_release(child_ctx); - aio_context_acquire(new_child_ctx); - } - bdrv_ref(child_bs); /* * Let every new BdrvChild start with a drained parent. Inserting the child @@ -3077,7 +3160,7 @@ static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs, * 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 + * will not run most of 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. */ @@ -3092,28 +3175,29 @@ static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs, }; tran_add(tran, &bdrv_attach_child_common_drv, s); - if (new_child_ctx != child_ctx) { - aio_context_release(new_child_ctx); - aio_context_acquire(child_ctx); - } - return new_child; } /* * Function doesn't update permissions, caller is responsible for this. * - * The caller must hold the AioContext lock for @child_bs. Both @parent_bs and - * @child_bs can move to a different AioContext in this function. Callers must - * make sure that their AioContext locking is still correct after 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. + * + * All block nodes must be drained before this function is called until after + * the transaction is finalized. */ -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; @@ -3125,6 +3209,11 @@ static BdrvChild *bdrv_attach_child_noperm(BlockDriverState *parent_bs, child_bs->node_name, child_name, parent_bs->node_name); return NULL; } + if (bdrv_is_inactive(child_bs) && !bdrv_is_inactive(parent_bs)) { + error_setg(errp, "Inactive '%s' can't be a %s child of active '%s'", + child_bs->node_name, child_name, parent_bs->node_name); + return NULL; + } bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm); bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL, @@ -3142,8 +3231,7 @@ static BdrvChild *bdrv_attach_child_noperm(BlockDriverState *parent_bs, * 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). + * All block nodes must be drained. */ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, const char *child_name, @@ -3171,7 +3259,7 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, out: tran_finalize(tran, ret); - bdrv_unref(child_bs); + bdrv_schedule_unref(child_bs); return ret < 0 ? NULL : child; } @@ -3184,8 +3272,7 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, * 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. + * All block nodes must be drained. */ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, BlockDriverState *child_bs, @@ -3215,12 +3302,16 @@ 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; } -/* Callers must ensure that child->frozen is false. */ +/* + * Callers must ensure that child->frozen is false. + * + * All block nodes must be drained. + */ void bdrv_root_unref_child(BdrvChild *child) { BlockDriverState *child_bs = child->bs; @@ -3241,11 +3332,11 @@ void bdrv_root_unref_child(BdrvChild *child) * 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_try_change_aio_context_locked(child_bs, qemu_get_aio_context(), + NULL, NULL); } - bdrv_unref(child_bs); + bdrv_schedule_unref(child_bs); } typedef struct BdrvSetInheritsFrom { @@ -3289,8 +3380,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; @@ -3314,7 +3406,11 @@ static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child, } } -/* Callers must ensure that child->frozen is false. */ +/* + * Callers must ensure that child->frozen is false. + * + * All block nodes must be drained. + */ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) { GLOBAL_STATE_CODE(); @@ -3327,7 +3423,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(); @@ -3368,16 +3465,25 @@ 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. * - * The caller must hold the AioContext lock for @child_bs. Both @parent_bs and - * @child_bs can move to a different AioContext in this function. Callers must - * make sure that their AioContext locking is still correct after 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. + * + * All block nodes must be drained before this function is called until after + * the transaction is finalized. */ -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); @@ -3428,6 +3534,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); } @@ -3454,26 +3561,17 @@ static int bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, } out: - bdrv_graph_rdlock_main_loop(); bdrv_refresh_limits(parent_bs, tran, NULL); - bdrv_graph_rdunlock_main_loop(); return 0; } /* - * The caller must hold the AioContext lock for @backing_hd. Both @bs and - * @backing_hd can move to a different AioContext in this function. Callers must - * make sure that their AioContext locking is still correct after this. + * Both @bs and @backing_hd can move to a different AioContext in this + * function. + * + * All block nodes must be drained. */ -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_drained(BlockDriverState *bs, BlockDriverState *backing_hd, Error **errp) @@ -3483,8 +3581,11 @@ int bdrv_set_backing_hd_drained(BlockDriverState *bs, GLOBAL_STATE_CODE(); 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; } @@ -3501,9 +3602,11 @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, int ret; GLOBAL_STATE_CODE(); - bdrv_drained_begin(bs); + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp); - bdrv_drained_end(bs); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); return ret; } @@ -3516,25 +3619,24 @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, * itself, all options starting with "${bdref_key}." are considered part of the * BlockdevRef. * - * The caller must hold the main AioContext lock. - * * TODO Can this be unified with bdrv_open_image()? */ 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; int ret = 0; bool implicit_backing = false; BlockDriverState *backing_hd; - AioContext *backing_hd_ctx; QDict *options; QDict *tmp_parent_options = NULL; Error *local_err = NULL; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (bs->backing != NULL) { goto free_exit; @@ -3598,7 +3700,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: "); @@ -3614,11 +3717,8 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, /* Hook up the backing file link; drop our reference, bs owns the * backing_hd reference now */ - backing_hd_ctx = bdrv_get_aio_context(backing_hd); - aio_context_acquire(backing_hd_ctx); ret = bdrv_set_backing_hd(bs, backing_hd, errp); bdrv_unref(backing_hd); - aio_context_release(backing_hd_ctx); if (ret < 0) { goto free_exit; @@ -3635,7 +3735,8 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, 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; @@ -3666,7 +3767,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; } @@ -3676,6 +3778,35 @@ bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, 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_drain_all_begin(); + bdrv_graph_wrlock(); + child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, + errp); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); + + return child; +} + /* * Opens a disk image whose options are given as BlockdevRef in another block * device's options. @@ -3690,9 +3821,7 @@ bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, * * The BlockdevRef will be removed from the options QDict. * - * The caller must hold the lock of the main AioContext and no other AioContext. - * @parent can move to a different AioContext in this function. Callers must - * make sure that their AioContext locking is still correct after this. + * @parent can move to a different AioContext in this function. */ BdrvChild *bdrv_open_child(const char *filename, QDict *options, const char *bdref_key, @@ -3701,33 +3830,17 @@ BdrvChild *bdrv_open_child(const char *filename, BdrvChildRole child_role, bool allow_none, Error **errp) { - BlockDriverState *bs; - BdrvChild *child; - AioContext *ctx; - - 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; - } - - ctx = bdrv_get_aio_context(bs); - aio_context_acquire(ctx); - child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, - errp); - aio_context_release(ctx); - - return child; + 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:). * - * The caller must hold the lock of the main AioContext and no other AioContext. - * @parent can move to a different AioContext in this function. Callers must - * make sure that their AioContext locking is still correct after this. + * @parent can move to a different AioContext in this function. */ int bdrv_open_file_child(const char *filename, QDict *options, const char *bdref_key, @@ -3740,8 +3853,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; } @@ -3786,7 +3899,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); @@ -3798,11 +3912,11 @@ 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; BlockDriverState *bs_snapshot = NULL; - AioContext *ctx = bdrv_get_aio_context(bs); int ret; GLOBAL_STATE_CODE(); @@ -3811,9 +3925,7 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, instead of opening 'filename' directly */ /* Get the required size from the image */ - aio_context_acquire(ctx); total_size = bdrv_getlength(bs); - aio_context_release(ctx); if (total_size < 0) { error_setg_errno(errp, -total_size, "Could not get image size"); @@ -3848,10 +3960,7 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, goto out; } - aio_context_acquire(ctx); ret = bdrv_append(bs_snapshot, bs, errp); - aio_context_release(ctx); - if (ret < 0) { bs_snapshot = NULL; goto out; @@ -3876,14 +3985,12 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, * The reference parameter may be used to specify an existing block device which * should be opened. If specified, neither options nor a filename may be given, * nor can an existing BDS be reused (that is, *pbs has to be NULL). - * - * The caller must always hold the main AioContext lock. */ static BlockDriverState * no_coroutine_fn bdrv_open_inherit(const char *filename, const char *reference, QDict *options, int flags, BlockDriverState *parent, const BdrvChildClass *child_class, BdrvChildRole child_role, - Error **errp) + bool parse_filename, Error **errp) { int ret; BlockBackend *file = NULL; @@ -3895,13 +4002,15 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, Error *local_err = NULL; QDict *snapshot_options = NULL; int snapshot_flags = 0; - AioContext *ctx = qemu_get_aio_context(); 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; qobject_unref(options); @@ -3929,9 +4038,11 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, } /* 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); @@ -3956,7 +4067,8 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, 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; } @@ -4025,7 +4137,7 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, 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; } @@ -4033,12 +4145,10 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, /* 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. */ - ctx = bdrv_get_aio_context(file_bs); - aio_context_acquire(ctx); + 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); - aio_context_release(ctx); if (local_err) { goto fail; @@ -4074,7 +4184,7 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, } /* 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); @@ -4085,13 +4195,8 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, goto fail; } - /* The AioContext could have changed during bdrv_open_common() */ - ctx = bdrv_get_aio_context(bs); - if (file) { - aio_context_acquire(ctx); blk_unref(file); - aio_context_release(ctx); file = NULL; } @@ -4149,16 +4254,13 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, * (snapshot_bs); thus, we have to drop the strong reference to bs * (which we obtained by calling bdrv_new()). bs will not be deleted, * though, because the overlay still has a reference to it. */ - aio_context_acquire(ctx); bdrv_unref(bs); - aio_context_release(ctx); bs = snapshot_bs; } return bs; fail: - aio_context_acquire(ctx); blk_unref(file); qobject_unref(snapshot_options); qobject_unref(bs->explicit_options); @@ -4167,28 +4269,24 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, bs->options = NULL; bs->explicit_options = NULL; bdrv_unref(bs); - aio_context_release(ctx); error_propagate(errp, local_err); return NULL; close_and_fail: - aio_context_acquire(ctx); bdrv_unref(bs); - aio_context_release(ctx); qobject_unref(snapshot_options); qobject_unref(options); error_propagate(errp, local_err); return NULL; } -/* The caller must always hold the main AioContext lock. */ BlockDriverState *bdrv_open(const char *filename, const char *reference, QDict *options, int flags, Error **errp) { 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 */ @@ -4241,8 +4339,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; @@ -4279,19 +4377,14 @@ 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 is drained here and undrained by bdrv_reopen_queue_free(). - * - * To be called with bs->aio_context locked. + * bs must be drained. */ -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); @@ -4303,7 +4396,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, GLOBAL_STATE_CODE(); - bdrv_drained_begin(bs); + assert(bs->quiesce_counter > 0); if (bs_queue == NULL) { bs_queue = g_new0(BlockReopenQueue, 1); @@ -4438,13 +4531,19 @@ 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(); + if (bs_queue == NULL) { + /* Paired with bdrv_drain_all_end() in bdrv_reopen_queue_free(). */ + bdrv_drain_all_begin(); + } + + GRAPH_RDLOCK_GUARD_MAINLOOP(); + return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, 0, false, NULL, 0, keep_old_opts); } @@ -4455,17 +4554,14 @@ void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue) if (bs_queue) { BlockReopenQueueEntry *bs_entry, *next; QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { - AioContext *ctx = bdrv_get_aio_context(bs_entry->state.bs); - - aio_context_acquire(ctx); - bdrv_drained_end(bs_entry->state.bs); - aio_context_release(ctx); - qobject_unref(bs_entry->state.explicit_options); qobject_unref(bs_entry->state.options); g_free(bs_entry); } g_free(bs_queue); + + /* Paired with bdrv_drain_all_begin() in bdrv_reopen_queue(). */ + bdrv_drain_all_end(); } } @@ -4492,7 +4588,6 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) { int ret = -1; BlockReopenQueueEntry *bs_entry, *next; - AioContext *ctx; Transaction *tran = tran_new(); g_autoptr(GSList) refresh_list = NULL; @@ -4501,10 +4596,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; @@ -4513,10 +4605,7 @@ 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; } @@ -4541,7 +4630,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; } @@ -4556,22 +4648,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); } } @@ -4579,13 +4667,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); } } @@ -4598,24 +4686,13 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) 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(); queue = bdrv_reopen_queue(NULL, bs, opts, keep_old_opts); - if (ctx != qemu_get_aio_context()) { - aio_context_release(ctx); - } - ret = bdrv_reopen_multiple(queue, errp); - - if (ctx != qemu_get_aio_context()) { - aio_context_acquire(ctx); - } - - return ret; + return bdrv_reopen_multiple(queue, errp); } int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, @@ -4645,25 +4722,29 @@ 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. * - * The caller must hold the AioContext lock of @reopen_state->bs. * @reopen_state->bs can move to a different AioContext in this function. - * Callers must make sure that their AioContext locking is still correct after - * this. + * + * All block nodes must be drained before this function is called until after + * the transaction is finalized. */ -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; - AioContext *ctx, *old_ctx; + bool has_child; int ret; GLOBAL_STATE_CODE(); @@ -4673,6 +4754,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 */ @@ -4682,11 +4765,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: @@ -4697,19 +4785,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; } } @@ -4720,7 +4812,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) { @@ -4729,22 +4822,28 @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, reopen_state->old_file_bs = old_child_bs; } - old_ctx = bdrv_get_aio_context(bs); - ctx = bdrv_get_aio_context(new_child_bs); - if (old_ctx != ctx) { - aio_context_release(old_ctx); - aio_context_acquire(ctx); + if (old_child_bs) { + bdrv_ref(old_child_bs); + assert(old_child_bs->quiesce_counter > 0); } + bdrv_graph_rdunlock_main_loop(); + bdrv_graph_wrlock(); + ret = bdrv_set_file_or_backing_noperm(bs, new_child_bs, is_backing, tran, errp); - if (old_ctx != ctx) { - aio_context_release(ctx); - aio_context_acquire(old_ctx); + bdrv_graph_wrunlock(); + + if (old_child_bs) { + bdrv_unref(old_child_bs); } return ret; + +out_rdlock: + bdrv_graph_rdunlock_main_loop(); + return ret; } /* @@ -4763,11 +4862,15 @@ 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 * - * The caller must hold the AioContext lock of @reopen_state->bs. + * After calling this function, the transaction @change_child_tran may only be + * completed while holding a writer lock for the graph. + * + * All block nodes must be drained before this function is called until after + * the transaction is finalized. */ -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; @@ -4829,7 +4932,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; @@ -4852,7 +4958,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); } @@ -4861,9 +4969,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; } @@ -4875,13 +4985,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 @@ -4909,6 +5022,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); @@ -4982,7 +5097,7 @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, * 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; @@ -4999,6 +5114,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); @@ -5020,9 +5137,7 @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state) qdict_del(bs->explicit_options, "backing"); qdict_del(bs->options, "backing"); - bdrv_graph_rdlock_main_loop(); bdrv_refresh_limits(bs, NULL, NULL); - bdrv_graph_rdunlock_main_loop(); bdrv_refresh_total_sectors(bs, bs->total_sectors); } @@ -5030,7 +5145,7 @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state) * 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; @@ -5065,12 +5180,17 @@ static void bdrv_close(BlockDriverState *bs) bs->drv = NULL; } + bdrv_drain_all_begin(); + 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(); + bdrv_drain_all_end(); + g_free(bs->opaque); bs->opaque = NULL; qatomic_set(&bs->copy_on_read, 0); @@ -5122,7 +5242,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; @@ -5211,45 +5331,47 @@ 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) { - BlockDriverState *bs = child->bs; - bdrv_drained_begin(bs); + assert(child->quiesced_parent); bdrv_replace_child_tran(child, NULL, tran); - bdrv_drained_end(bs); } tran_add(tran, &bdrv_remove_child_drv, child); } -static void undrain_on_clean_cb(void *opaque) -{ - bdrv_drained_end(opaque); -} - -static TransactionActionDrv undrain_on_clean = { - .clean = undrain_on_clean_cb, -}; - -static int bdrv_replace_node_noperm(BlockDriverState *from, - BlockDriverState *to, - bool auto_skip, Transaction *tran, - Error **errp) +/* + * 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 int GRAPH_WRLOCK +bdrv_replace_node_noperm(BlockDriverState *from, + BlockDriverState *to, + bool auto_skip, Transaction *tran, + Error **errp) { BdrvChild *c, *next; GLOBAL_STATE_CODE(); - bdrv_drained_begin(from); - bdrv_drained_begin(to); - tran_add(tran, &undrain_on_clean, from); - tran_add(tran, &undrain_on_clean, to); + assert(from->quiesce_counter); + assert(to->quiesce_counter); QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { assert(c->bs == from); @@ -5273,6 +5395,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. * @@ -5282,10 +5407,9 @@ 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(GSList) refresh_list = NULL; @@ -5294,6 +5418,10 @@ static int bdrv_replace_node_common(BlockDriverState *from, 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); @@ -5305,14 +5433,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 @@ -5325,6 +5445,7 @@ static int bdrv_replace_node_common(BlockDriverState *from, } if (detach_subchain) { + /* to_cow_parent is already drained because from is drained */ bdrv_remove_child(bdrv_filter_or_cow_child(to_cow_parent), tran); } @@ -5340,27 +5461,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; } /* @@ -5374,8 +5501,6 @@ int bdrv_drop_filter(BlockDriverState *bs, Error **errp) * child. * * This function does not create any image files. - * - * The caller must hold the AioContext lock for @bs_top. */ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, Error **errp) @@ -5383,13 +5508,15 @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, int ret; BdrvChild *child; Transaction *tran = tran_new(); - AioContext *old_context, *new_context = NULL; GLOBAL_STATE_CODE(); + bdrv_graph_rdlock_main_loop(); assert(!bs_new->backing); + bdrv_graph_rdunlock_main_loop(); - old_context = bdrv_get_aio_context(bs_top); + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); child = bdrv_attach_child_noperm(bs_new, bs_top, "backing", &child_of_bds, bdrv_backing_role(bs_new), @@ -5399,19 +5526,6 @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, goto out; } - /* - * bdrv_attach_child_noperm could change the AioContext of bs_top. - * bdrv_replace_node_noperm calls bdrv_drained_begin, so let's temporarily - * hold the new AioContext, since bdrv_drained_begin calls BDRV_POLL_WHILE - * that assumes the new lock is taken. - */ - new_context = bdrv_get_aio_context(bs_top); - - if (old_context != new_context) { - aio_context_release(old_context); - aio_context_acquire(new_context); - } - ret = bdrv_replace_node_noperm(bs_top, bs_new, true, tran, errp); if (ret < 0) { goto out; @@ -5421,14 +5535,9 @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, out: tran_finalize(tran, ret); - bdrv_graph_rdlock_main_loop(); bdrv_refresh_limits(bs_top, NULL, NULL); - bdrv_graph_rdunlock_main_loop(); - - if (new_context && old_context != new_context) { - aio_context_release(new_context); - aio_context_acquire(old_context); - } + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); return ret; } @@ -5447,6 +5556,7 @@ 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); @@ -5457,6 +5567,7 @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, tran_finalize(tran, ret); + bdrv_graph_wrunlock(); bdrv_drained_end(old_bs); bdrv_drained_end(new_bs); bdrv_unref(old_bs); @@ -5492,9 +5603,8 @@ static void bdrv_delete(BlockDriverState *bs) * 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 holds the AioContext lock for @bs. It must make sure that @bs - * stays in the same AioContext, i.e. @options must not refer to nodes in a - * different AioContext. + * 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) @@ -5522,12 +5632,8 @@ BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, GLOBAL_STATE_CODE(); - aio_context_release(ctx); - aio_context_acquire(qemu_get_aio_context()); new_node_bs = bdrv_new_open_driver_opts(drv, node_name, options, flags, errp); - aio_context_release(qemu_get_aio_context()); - aio_context_acquire(ctx); assert(bdrv_get_aio_context(bs) == ctx); options = NULL; /* bdrv_new_open_driver() eats options */ @@ -5536,9 +5642,19 @@ BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, 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: "); @@ -5584,13 +5700,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; @@ -5605,8 +5722,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; } @@ -5663,8 +5780,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; @@ -5774,7 +5892,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; @@ -5788,14 +5907,15 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, bdrv_ref(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 @@ -5827,6 +5947,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; @@ -5837,6 +5959,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) { /* @@ -5859,6 +5982,10 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, } ret = 0; + goto exit; + +exit_wrlock: + bdrv_graph_wrunlock(); exit: bdrv_drained_end(base); bdrv_unref(top); @@ -5908,7 +6035,7 @@ int64_t coroutine_fn bdrv_co_get_allocated_file_size(BlockDriverState *bs) 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), @@ -6077,12 +6204,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); } @@ -6150,6 +6277,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) { @@ -6266,7 +6394,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)) { @@ -6279,7 +6407,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); @@ -6288,7 +6416,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); @@ -6420,7 +6548,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(); @@ -6535,7 +6663,8 @@ void coroutine_fn bdrv_co_debug_event(BlockDriverState *bs, BlkdebugEvent 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) { @@ -6554,6 +6683,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); @@ -6565,6 +6696,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); @@ -6576,6 +6709,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); } @@ -6590,6 +6725,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); } @@ -6618,6 +6755,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; @@ -6729,6 +6867,10 @@ void bdrv_init_with_whitelist(void) bdrv_init(); } +bool bdrv_is_inactive(BlockDriverState *bs) { + return bs->open_flags & BDRV_O_INACTIVE; +} + int bdrv_activate(BlockDriverState *bs, Error **errp) { BdrvChild *child, *parent; @@ -6737,6 +6879,7 @@ int bdrv_activate(BlockDriverState *bs, Error **errp) BdrvDirtyBitmap *bm; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!bs->drv) { return -ENOMEDIUM; @@ -6828,14 +6971,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; @@ -6843,7 +6984,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(); @@ -6860,7 +7002,8 @@ 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, bool top_level) { BdrvChild *child, *parent; int ret; @@ -6868,6 +7011,8 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs) GLOBAL_STATE_CODE(); + assert(bs->quiesce_counter > 0); + if (!bs->drv) { return -ENOMEDIUM; } @@ -6878,7 +7023,14 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs) return 0; } - assert(!(bs->open_flags & BDRV_O_INACTIVE)); + /* + * Inactivating an already inactive node on user request is harmless, but if + * a child is already inactive before its parent, that's bad. + */ + if (bs->open_flags & BDRV_O_INACTIVE) { + assert(top_level); + return 0; + } /* Inactivate this node */ if (bs->drv->bdrv_inactivate) { @@ -6915,7 +7067,7 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs) /* Recursively inactivate children */ QLIST_FOREACH(child, &bs->children, next) { - ret = bdrv_inactivate_recurse(child->bs); + ret = bdrv_inactivate_recurse(child->bs, false); if (ret < 0) { return ret; } @@ -6924,23 +7076,43 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs) return 0; } +int bdrv_inactivate(BlockDriverState *bs, Error **errp) +{ + int ret; + + GLOBAL_STATE_CODE(); + + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); + + if (bdrv_has_bds_parent(bs, true)) { + error_setg(errp, "Node has active parent node"); + ret = -EPERM; + goto out; + } + + ret = bdrv_inactivate_recurse(bs, true); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to inactivate node"); + goto out; + } + +out: + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); + return ret; +} + 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); - } - } + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { /* Nodes with BDS parents are covered by recursion from the last @@ -6949,19 +7121,15 @@ int bdrv_inactivate_all(void) if (bdrv_has_bds_parent(bs, false)) { continue; } - ret = bdrv_inactivate_recurse(bs); + ret = bdrv_inactivate_recurse(bs, true); 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); + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); return ret; } @@ -7045,6 +7213,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; @@ -7054,6 +7245,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]); @@ -7119,10 +7311,6 @@ 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, @@ -7162,8 +7350,6 @@ void bdrv_img_create(const char *filename, const char *fmt, return; } - aio_context_acquire(qemu_get_aio_context()); - /* Create parameter list */ create_opts = qemu_opts_append(create_opts, drv->create_opts); create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); @@ -7281,7 +7467,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; } @@ -7313,7 +7502,6 @@ void bdrv_img_create(const char *filename, const char *fmt, qemu_opts_del(opts); qemu_opts_free(create_opts); error_propagate(errp, local_err); - aio_context_release(qemu_get_aio_context()); } AioContext *bdrv_get_aio_context(BlockDriverState *bs) @@ -7347,33 +7535,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); - } -} - static void bdrv_do_remove_aio_context_notifier(BdrvAioNotifier *ban) { GLOBAL_STATE_CODE(); @@ -7436,10 +7597,21 @@ typedef struct BdrvStateSetAioContext { BlockDriverState *bs; } BdrvStateSetAioContext; -static bool bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx, - GHashTable *visited, - Transaction *tran, - Error **errp) +/* + * Changes the AioContext of @child to @ctx and recursively for the associated + * block nodes and all their children and parents. Returns true if the change is + * possible and the transaction @tran can be continued. Returns false and sets + * @errp if not and the transaction must be aborted. + * + * @visited will accumulate all visited BdrvChild objects. The caller is + * responsible for freeing the list afterwards. + * + * Must be called with the affected block nodes drained. + */ +static bool GRAPH_RDLOCK +bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp) { GLOBAL_STATE_CODE(); if (g_hash_table_contains(visited, c)) { @@ -7464,6 +7636,17 @@ static bool bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx, return true; } +/* + * Changes the AioContext of @c->bs to @ctx and recursively for all its children + * and parents. Returns true if the change is possible and the transaction @tran + * can be continued. Returns false and sets @errp if not and the transaction + * must be aborted. + * + * @visited will accumulate all visited BdrvChild objects. The caller is + * responsible for freeing the list afterwards. + * + * Must be called with the affected block nodes drained. + */ bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, GHashTable *visited, Transaction *tran, Error **errp) @@ -7479,10 +7662,6 @@ bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, static void bdrv_set_aio_context_clean(void *opaque) { BdrvStateSetAioContext *state = (BdrvStateSetAioContext *) opaque; - BlockDriverState *bs = (BlockDriverState *) state->bs; - - /* Paired with bdrv_drained_begin in bdrv_change_aio_context() */ - bdrv_drained_end(bs); g_free(state); } @@ -7492,21 +7671,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); - /* - * 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); } @@ -7521,16 +7687,14 @@ 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. + * + * @bs must be drained. */ -static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, - GHashTable *visited, Transaction *tran, - Error **errp) +static bool GRAPH_RDLOCK +bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, + GHashTable *visited, Transaction *tran, Error **errp) { BdrvChild *c; BdrvStateSetAioContext *state; @@ -7559,8 +7723,7 @@ static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, .bs = bs, }; - /* Paired with bdrv_drained_end in bdrv_set_aio_context_clean() */ - bdrv_drained_begin(bs); + assert(bs->quiesce_counter > 0); tran_add(tran, &set_aio_context, state); @@ -7574,27 +7737,23 @@ 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. + * Called with the graph lock held. + * + * Called while all bs are drained. */ -int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, - BdrvChild *ignore_child, Error **errp) +int bdrv_try_change_aio_context_locked(BlockDriverState *bs, AioContext *ctx, + BdrvChild *ignore_child, Error **errp) { 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, building a linear list of callbacks to run if everything - * is successful (the transaction itself). + * Take care of checking that all nodes support changing AioContext, + * building a linear list of callbacks to run if everything is successful + * (the transaction itself). */ tran = tran_new(); visited = g_hash_table_new(NULL, NULL); @@ -7606,8 +7765,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). */ @@ -7617,35 +7776,31 @@ 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); + return 0; +} - if (qemu_get_aio_context() != ctx) { - aio_context_release(ctx); - } +/* + * Change bs's and recursively all of its parents' and children's AioContext + * to the given new context, returning an error if that isn't possible. + * + * If ignore_child is not NULL, that child (and its subgraph) will not + * be touched. + */ +int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, + BdrvChild *ignore_child, Error **errp) +{ + int ret; - /* Re-acquire the old AioContext, since the caller takes and releases it. */ - if (qemu_get_aio_context() != old_context) { - aio_context_acquire(old_context); - } + GLOBAL_STATE_CODE(); - return 0; + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); + ret = bdrv_try_change_aio_context_locked(bs, ctx, ignore_child, errp); + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); + + return ret; } void bdrv_add_aio_context_notifier(BlockDriverState *bs, @@ -7766,7 +7921,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(); @@ -7775,12 +7929,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 @@ -7793,12 +7943,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; } @@ -7897,7 +8044,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) { @@ -8031,7 +8178,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); @@ -8083,8 +8230,10 @@ char *bdrv_dirname(BlockDriverState *bs, Error **errp) } /* - * Hot add/remove a BDS's child. So the user can take a child offline when - * it is broken and take a new child online + * Hot add a BDS's child. Used in combination with bdrv_del_child, so the user + * can take a child offline when it is broken and take a new child online. + * + * All block nodes must be drained. */ void bdrv_add_child(BlockDriverState *parent_bs, BlockDriverState *child_bs, Error **errp) @@ -8124,6 +8273,12 @@ void bdrv_add_child(BlockDriverState *parent_bs, BlockDriverState *child_bs, parent_bs->drv->bdrv_add_child(parent_bs, child_bs, errp); } +/* + * Hot remove a BDS's child. Used in combination with bdrv_add_child, so the + * user can take a child offline when it is broken and take a new child online. + * + * All block nodes must be drained. + */ void bdrv_del_child(BlockDriverState *parent_bs, BdrvChild *child, Error **errp) { BdrvChild *tmp; @@ -8271,8 +8426,8 @@ BdrvChild *bdrv_primary_child(BlockDriverState *bs) return found; } -static BlockDriverState *bdrv_do_skip_filters(BlockDriverState *bs, - bool stop_on_explicit_filter) +static BlockDriverState * GRAPH_RDLOCK +bdrv_do_skip_filters(BlockDriverState *bs, bool stop_on_explicit_filter) { BdrvChild *c; diff --git a/block/accounting.c b/block/accounting.c index 2829745377a..3e46159569e 100644 --- a/block/accounting.c +++ b/block/accounting.c @@ -27,7 +27,7 @@ #include "block/accounting.h" #include "block/block_int.h" #include "qemu/timer.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" static QEMUClockType clock_type = QEMU_CLOCK_REALTIME; static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000; diff --git a/block/aio_task.c b/block/aio_task.c index 9bd17ea2c13..bb5c05f455d 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/backup.c b/block/backup.c index db3791f4d16..909027c17ae 100644 --- a/block/backup.c +++ b/block/backup.c @@ -23,7 +23,7 @@ #include "block/dirty-bitmap.h" #include "qapi/error.h" #include "qemu/cutils.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/bitmap.h" #include "qemu/error-report.h" @@ -356,11 +356,12 @@ 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, BlockdevOnError on_target_error, + OnCbwError on_cbw_error, int creation_flags, BlockCompletionFunc *cb, void *opaque, JobTxn *txn, Error **errp) @@ -384,31 +385,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); @@ -436,6 +439,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; @@ -443,6 +447,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; @@ -453,7 +458,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, on_cbw_error, errp); if (!cbw) { goto error; } @@ -492,8 +498,12 @@ 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_drain_all_begin(); + bdrv_graph_wrlock(); block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); return &job->common; @@ -506,4 +516,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 addad914b3f..c54aee0c84b 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -33,11 +33,11 @@ #include "qemu/module.h" #include "qemu/option.h" #include "qapi/qapi-visit-block-core.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" /* All APIs are thread-safe */ @@ -508,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 | @@ -520,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); @@ -530,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); @@ -539,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); @@ -549,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); @@ -558,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); @@ -568,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); @@ -746,13 +750,10 @@ blkdebug_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) 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, unsigned int mode, + int64_t offset, int64_t bytes, int64_t *pnum, + int64_t *map, BlockDriverState **file) { int err; @@ -973,7 +974,7 @@ blkdebug_co_getlength(BlockDriverState *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; @@ -1072,7 +1073,7 @@ 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, diff --git a/block/blkio.c b/block/blkio.c index 1dd495617cf..41426739847 100644 --- a/block/blkio.c +++ b/block/blkio.c @@ -11,14 +11,15 @@ #include "qemu/osdep.h" #include #include "block/block_int.h" -#include "exec/memory.h" +#include "system/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 "qobject/qdict.h" #include "qemu/module.h" -#include "sysemu/block-backend.h" -#include "exec/memory.h" /* for ram_block_discard_disable() */ +#include "system/block-backend.h" +#include "system/memory.h" /* for ram_block_discard_disable() */ #include "block/block-io.h" @@ -67,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; @@ -88,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; @@ -312,10 +316,10 @@ static void blkio_detach_aio_context(BlockDriverState *bs) } /* - * Called by blk_io_unplug() or immediately if not plugged. Called without - * blkio_lock. + * Called by defer_call_end() or immediately if not in a deferred section. + * Called without blkio_lock. */ -static void blkio_unplug_fn(void *opaque) +static void blkio_deferred_fn(void *opaque) { BDRVBlkioState *s = opaque; @@ -332,7 +336,7 @@ static void blkio_submit_io(BlockDriverState *bs) { BDRVBlkioState *s = bs->opaque; - blk_io_plug_call(blkio_unplug_fn, s); + defer_call(blkio_deferred_fn, s); } static int coroutine_fn @@ -709,7 +713,7 @@ static int blkio_virtio_blk_connect(BlockDriverState *bs, QDict *options, * for example will fail. * * In order to open the device read-only, we are using the `read-only` - * property of the libblkio driver in blkio_file_open(). + * property of the libblkio driver in blkio_open(). */ fd = qemu_open(path, O_RDWR, NULL); if (fd < 0) { @@ -787,8 +791,8 @@ static int blkio_virtio_blk_connect(BlockDriverState *bs, QDict *options, 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; @@ -895,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); @@ -1084,7 +1090,7 @@ static void blkio_refresh_limits(BlockDriverState *bs, Error **errp) */ #define BLKIO_DRIVER_COMMON \ .instance_size = sizeof(BDRVBlkioState), \ - .bdrv_file_open = blkio_file_open, \ + .bdrv_open = blkio_open, \ .bdrv_close = blkio_close, \ .bdrv_co_getlength = blkio_co_getlength, \ .bdrv_co_truncate = blkio_truncate, \ diff --git a/block/blklogwrites.c b/block/blklogwrites.c index 3ea7141cb5c..70ac76f4013 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. @@ -14,8 +14,8 @@ #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" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qemu/cutils.h" #include "qemu/module.h" #include "qemu/option.h" @@ -55,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 = { @@ -169,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) { @@ -231,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); @@ -251,8 +281,13 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, ret = 0; fail_log: if (ret < 0) { + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_unref_child(bs, s->log_file); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); s->log_file = NULL; + qemu_mutex_destroy(&s->mutex); } fail: qemu_opts_del(opts); @@ -263,8 +298,13 @@ static void blk_log_writes_close(BlockDriverState *bs) { BDRVBlkLogWritesState *s = bs->opaque; + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_unref_child(bs, s->log_file); s->log_file = NULL; + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); + qemu_mutex_destroy(&s->mutex); } static int64_t coroutine_fn GRAPH_RDLOCK @@ -291,7 +331,7 @@ 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; } @@ -324,38 +364,85 @@ 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; - - lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size, + /* + * 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; + + 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)); @@ -365,6 +452,13 @@ 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); } @@ -384,7 +478,7 @@ blk_log_writes_co_log(BlockDriverState *bs, uint64_t offset, uint64_t bytes, { 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, diff --git a/block/blkreplay.c b/block/blkreplay.c index 04f53eea417..16d8b12dd99 100644 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -13,7 +13,7 @@ #include "qemu/module.h" #include "block/block-io.h" #include "block/block_int.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "qapi/error.h" typedef struct Request { @@ -130,7 +130,13 @@ static int coroutine_fn GRAPH_RDLOCK 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 = { diff --git a/block/blkverify.c b/block/blkverify.c index 7326461f30e..3a71f7498c1 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -12,8 +12,8 @@ #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" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qemu/cutils.h" #include "qemu/module.h" #include "qemu/option.h" @@ -33,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 */ @@ -151,8 +151,12 @@ static void blkverify_close(BlockDriverState *bs) { BDRVBlkverifyState *s = bs->opaque; + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_unref_child(bs, s->test_file); s->test_file = NULL; + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); } static int64_t coroutine_fn GRAPH_RDLOCK @@ -168,8 +172,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); } @@ -178,13 +185,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) @@ -220,7 +230,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) { @@ -249,7 +259,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) { @@ -280,7 +290,7 @@ 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; @@ -313,7 +323,7 @@ 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_co_getlength = blkverify_co_getlength, diff --git a/block/block-backend.c b/block/block-backend.c index 4009ed5feda..68209bb2f7f 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -11,15 +11,15 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "block/block_int.h" #include "block/blockjob.h" #include "block/coroutines.h" #include "block/throttle-groups.h" #include "hw/qdev-core.h" -#include "sysemu/blockdev.h" -#include "sysemu/runstate.h" -#include "sysemu/replay.h" +#include "system/blockdev.h" +#include "system/runstate.h" +#include "system/replay.h" #include "qapi/error.h" #include "qapi/qapi-events-block.h" #include "qemu/id.h" @@ -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 */ @@ -103,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), }; @@ -121,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) @@ -135,9 +136,9 @@ static void blk_root_drained_end(BdrvChild *child); static void blk_root_change_media(BdrvChild *child, bool load); static void blk_root_resize(BdrvChild *child); -static bool blk_root_change_aio_ctx(BdrvChild *child, AioContext *ctx, - GHashTable *visited, Transaction *tran, - Error **errp); +static bool GRAPH_RDLOCK +blk_root_change_aio_ctx(BdrvChild *child, AioContext *ctx, GHashTable *visited, + Transaction *tran, Error **errp); static char *blk_root_get_parent_desc(BdrvChild *child) { @@ -186,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; @@ -207,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; @@ -226,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; @@ -252,14 +253,14 @@ static bool blk_can_inactivate(BlockBackend *blk) * guest. For block job BBs that satisfy this, we can just allow * it. This is the case for mirror job source, which is required * by libvirt non-shared block migration. */ - if (!(blk->perm & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED))) { + if (!(blk->perm & ~BLK_PERM_CONSISTENT_READ)) { return true; } 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; @@ -389,8 +390,6 @@ BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm) * Both sets of permissions can be changed later using blk_set_perm(). * * Return the new BlockBackend on success, null on failure. - * - * Callers must hold the AioContext lock of @bs. */ BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, Error **errp) @@ -415,8 +414,6 @@ BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm, * Just as with bdrv_open(), after having called this function the reference to * @options belongs to the block layer (even on failure). * - * Called without holding an AioContext lock. - * * TODO: Remove @filename and @flags; it should be possible to specify a whole * BDS tree just by specifying the @options QDict (or @reference, * alternatively). At the time of adding this function, this is not possible, @@ -428,7 +425,6 @@ BlockBackend *blk_new_open(const char *filename, const char *reference, { BlockBackend *blk; BlockDriverState *bs; - AioContext *ctx; uint64_t perm = 0; uint64_t shared = BLK_PERM_ALL; @@ -458,23 +454,18 @@ BlockBackend *blk_new_open(const char *filename, const char *reference, shared = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED; } - aio_context_acquire(qemu_get_aio_context()); bs = bdrv_open(filename, reference, options, flags, errp); - aio_context_release(qemu_get_aio_context()); if (!bs) { return NULL; } /* bdrv_open() could have moved bs to a different AioContext */ - ctx = bdrv_get_aio_context(bs); blk = blk_new(bdrv_get_aio_context(bs), perm, shared); blk->perm = perm; blk->shared_perm = shared; - aio_context_acquire(ctx); blk_insert_bs(blk, bs, errp); bdrv_unref(bs); - aio_context_release(ctx); if (!blk->root) { blk_unref(blk); @@ -576,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); } } @@ -612,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; @@ -633,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 @@ -677,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); @@ -779,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) { @@ -811,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; @@ -867,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. */ @@ -911,23 +888,41 @@ void blk_remove_bs(BlockBackend *blk) blk_drain(blk); root = blk->root; blk->root = NULL; + + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_root_unref_child(root); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); } /* * Associates a new BlockDriverState with @blk. - * - * Callers must hold the AioContext lock of @bs. */ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) { ThrottleGroupMember *tgm = &blk->public.throttle_group_member; + uint64_t perm, shared_perm; + GLOBAL_STATE_CODE(); bdrv_ref(bs); + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); + + if ((bs->open_flags & BDRV_O_INACTIVE) && blk_can_inactivate(blk)) { + blk->disable_perm = true; + perm = 0; + shared_perm = BLK_PERM_ALL; + } else { + perm = blk->perm; + shared_perm = blk->shared_perm; + } + blk->root = bdrv_root_attach_child(bs, "root", &child_root, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - blk->perm, blk->shared_perm, - blk, errp); + perm, shared_perm, blk, errp); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); if (blk->root == NULL) { return -EPERM; } @@ -953,8 +948,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(); @@ -972,6 +968,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(); @@ -1028,22 +1033,38 @@ 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) +/* + * The caller is responsible for releasing the value returned + * with g_free() after use. + */ +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(""); } +char *blk_get_attached_dev_id(BlockBackend *blk) +{ + return blk_get_attached_dev_id_or_path(blk, true); +} + +/* + * The caller is responsible for releasing the value returned + * with g_free() after use. + */ +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. @@ -1214,12 +1235,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(); @@ -1341,7 +1356,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, @@ -1415,7 +1430,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) { @@ -1533,7 +1548,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; } @@ -1545,16 +1560,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) @@ -1595,11 +1602,11 @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, acb->has_returned = false; co = qemu_coroutine_create(co_entry, acb); - aio_co_enter(blk_get_aio_context(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); } @@ -1901,11 +1908,11 @@ BlockAIOCB *blk_aio_zone_report(BlockBackend *blk, int64_t offset, acb->has_returned = false; co = qemu_coroutine_create(blk_aio_zone_report_entry, acb); - aio_co_enter(blk_get_aio_context(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); } @@ -1942,11 +1949,11 @@ BlockAIOCB *blk_aio_zone_mgmt(BlockBackend *blk, BlockZoneOp op, acb->has_returned = false; co = qemu_coroutine_create(blk_aio_zone_mgmt_entry, acb); - aio_co_enter(blk_get_aio_context(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); } @@ -1982,10 +1989,10 @@ BlockAIOCB *blk_aio_zone_append(BlockBackend *blk, int64_t *offset, acb->has_returned = false; co = qemu_coroutine_create(blk_aio_zone_append_entry, acb); - aio_co_enter(blk_get_aio_context(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); } @@ -2145,9 +2152,10 @@ static void send_qmp_error_event(BlockBackend *blk, { IoOperationType optype; BlockDriverState *bs = blk_bs(blk); + g_autofree char *path = blk_get_attached_dev_path(blk); optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; - qapi_event_send_block_io_error(blk_name(blk), + qapi_event_send_block_io_error(path, blk_name(blk), bs ? bdrv_get_node_name(bs) : NULL, optype, action, blk_iostatus_is_enabled(blk), error == ENOSPC, strerror(error)); @@ -2236,27 +2244,6 @@ 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; - } - - /* - * Migration code can call this function in coroutine context, so leave - * coroutine context if necessary. - */ - if (qemu_in_coroutine()) { - bdrv_co_activate(bs, errp); - } else { - bdrv_activate(bs, errp); - } -} - bool coroutine_fn blk_co_is_inserted(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); @@ -2374,70 +2361,23 @@ void *blk_blockalign(BlockBackend *blk, size_t size) return qemu_blockalign(blk ? blk_bs(blk) : NULL, size); } -bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp) -{ - BlockDriverState *bs = blk_bs(blk); - GLOBAL_STATE_CODE(); - - if (!bs) { - return false; - } - - 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; IO_CODE(); if (!blk) { return qemu_get_aio_context(); } - bs = blk_bs(blk); - if (bs) { - AioContext *ctx = bdrv_get_aio_context(blk_bs(blk)); - assert(ctx == blk->ctx); - } - - 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); + return qatomic_read(&blk->ctx); } int blk_set_aio_context(BlockBackend *blk, AioContext *new_context, @@ -2450,7 +2390,7 @@ int blk_set_aio_context(BlockBackend *blk, AioContext *new_context, GLOBAL_STATE_CODE(); if (!bs) { - blk->ctx = new_context; + qatomic_set(&blk->ctx, new_context); return 0; } @@ -2479,7 +2419,7 @@ static void blk_root_set_aio_ctx_commit(void *opaque) AioContext *new_context = s->new_ctx; ThrottleGroupMember *tgm = &blk->public.throttle_group_member; - blk->ctx = new_context; + qatomic_set(&blk->ctx, new_context); if (tgm->throttle_state) { throttle_group_detach_aio_context(tgm); throttle_group_attach_aio_context(tgm, new_context); @@ -2576,12 +2516,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); -} - BlockAcctStats *blk_get_stats(BlockBackend *blk) { IO_CODE(); @@ -2661,6 +2595,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; } @@ -2721,22 +2657,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; } @@ -2901,6 +2834,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 1c60368d72d..1826c2e1c7b 100644 --- a/block/block-copy.c +++ b/block/block-copy.c @@ -20,7 +20,7 @@ #include "block/block_int-io.h" #include "block/dirty-bitmap.h" #include "block/reqlist.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/units.h" #include "qemu/co-shared-resource.h" #include "qemu/coroutine.h" @@ -137,6 +137,7 @@ typedef struct BlockCopyState { CoMutex lock; int64_t in_flight_bytes; BlockCopyMethod method; + bool discard_source; BlockReqList reqs; QLIST_HEAD(, BlockCopyCallState) calls; /* @@ -309,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 @@ -323,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, " @@ -339,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(); @@ -355,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; @@ -392,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) { @@ -408,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); @@ -555,7 +584,7 @@ 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; WITH_GRAPH_RDLOCK_GUARD() { ret = block_copy_do_copy(s, t->req.offset, t->req.bytes, &method, @@ -579,6 +608,14 @@ 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; } diff --git a/block/block-ram-registrar.c b/block/block-ram-registrar.c index 25dbafa789c..fcda2b86afb 100644 --- a/block/block-ram-registrar.c +++ b/block/block-ram-registrar.c @@ -5,8 +5,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" -#include "sysemu/block-ram-registrar.h" +#include "system/block-backend.h" +#include "system/block-ram-registrar.h" #include "qapi/error.h" static void ram_block_added(RAMBlockNotifier *n, void *host, size_t size, diff --git a/block/bochs.c b/block/bochs.c index 66e7a58e5e3..b099fb52fe8 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -105,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; } @@ -116,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; diff --git a/block/cloop.c b/block/cloop.c index 835a0fe3da0..443af1444e8 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -67,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; } @@ -77,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) { diff --git a/block/commit.c b/block/commit.c index aa45beb0f0f..6c4b736ff80 100644 --- a/block/commit.c +++ b/block/commit.c @@ -15,12 +15,14 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "trace.h" +#include "block/block-common.h" +#include "block/coroutines.h" #include "block/block_int.h" #include "block/blockjob_int.h" #include "qapi/error.h" #include "qemu/ratelimit.h" #include "qemu/memalign.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" enum { /* @@ -42,14 +44,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. */ @@ -59,16 +64,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() */ @@ -90,8 +99,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); @@ -112,6 +128,84 @@ static void commit_clean(Job *job) blk_unref(s->top); } +static int commit_iteration(CommitBlockJob *s, int64_t offset, + int64_t *requested_bytes, void *buf) +{ + BlockErrorAction action; + int64_t bytes = *requested_bytes; + int ret = 0; + bool error_in_source = true; + + /* Copy if allocated above the base */ + WITH_GRAPH_RDLOCK_GUARD() { + ret = bdrv_co_common_block_status_above(blk_bs(s->top), + s->base_overlay, true, true, offset, COMMIT_BUFFER_SIZE, + &bytes, NULL, NULL, NULL); + } + + trace_commit_one_iteration(s, offset, bytes, ret); + + if (ret < 0) { + goto fail; + } + + if (ret & BDRV_BLOCK_ALLOCATED) { + if (ret & BDRV_BLOCK_ZERO) { + /* + * If the top (sub)clusters are smaller than the base + * (sub)clusters, this will not unmap unless the underlying device + * does some tracking of these requests. Ideally, we would find + * the maximal extent of the zero clusters. + */ + ret = blk_co_pwrite_zeroes(s->base, offset, bytes, + BDRV_REQ_MAY_UNMAP); + if (ret < 0) { + error_in_source = false; + goto fail; + } + } else { + assert(bytes < SIZE_MAX); + + ret = blk_co_pread(s->top, offset, bytes, buf, 0); + if (ret < 0) { + goto fail; + } + + ret = blk_co_pwrite(s->base, offset, bytes, buf, 0); + if (ret < 0) { + error_in_source = false; + goto fail; + } + } + + /* + * Whether zeroes actually end up on disk depends on the details of + * the underlying driver. Therefore, this might rate limit more than + * is necessary. + */ + block_job_ratelimit_processed_bytes(&s->common, bytes); + } + + /* Publish progress */ + + job_progress_update(&s->common.job, bytes); + + *requested_bytes = bytes; + + return 0; + +fail: + action = block_job_error_action(&s->common, s->on_error, + error_in_source, -ret); + if (action == BLOCK_ERROR_ACTION_REPORT) { + return ret; + } + + *requested_bytes = 0; + + return 0; +} + static int coroutine_fn commit_run(Job *job, Error **errp) { CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); @@ -142,9 +236,6 @@ static int coroutine_fn commit_run(Job *job, Error **errp) buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE); for (offset = 0; offset < len; offset += n) { - bool copy; - bool error_in_source = true; - /* Note that even when no rate limit is applied we need to yield * with no pending I/O here so that bdrv_drain_all() returns. */ @@ -152,38 +243,11 @@ static int coroutine_fn commit_run(Job *job, Error **errp) if (job_is_cancelled(&s->common.job)) { break; } - /* Copy if allocated above the base */ - 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) { - assert(n < SIZE_MAX); - - ret = blk_co_pread(s->top, offset, n, buf, 0); - if (ret >= 0) { - ret = blk_co_pwrite(s->base, offset, n, buf, 0); - if (ret < 0) { - error_in_source = false; - } - } - } - if (ret < 0) { - BlockErrorAction action = - block_job_error_action(&s->common, s->on_error, - error_in_source, -ret); - if (action == BLOCK_ERROR_ACTION_REPORT) { - return ret; - } else { - n = 0; - continue; - } - } - /* Publish progress */ - job_progress_update(&s->common.job, n); - if (copy) { - block_job_ratelimit_processed_bytes(&s->common, n); + ret = commit_iteration(s, offset, &n, buf); + + if (ret < 0) { + return ret; } } @@ -210,7 +274,7 @@ bdrv_commit_top_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, 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); @@ -242,6 +306,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; @@ -255,10 +320,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) { @@ -324,6 +392,8 @@ void commit_start(const char *job_id, BlockDriverState *bs, * this is the responsibility of the interface (i.e. whoever calls * commit_start()). */ + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); s->base_overlay = bdrv_find_overlay(top, base); assert(s->base_overlay); @@ -354,16 +424,23 @@ 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(); + bdrv_drain_all_end(); goto fail; } } if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) { + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); goto fail; } s->chain_frozen = true; ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); + if (ret < 0) { goto fail; } @@ -388,6 +465,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); @@ -396,7 +474,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); @@ -411,7 +491,11 @@ void commit_start(const char *job_id, BlockDriverState *bs, /* 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); } } @@ -434,6 +518,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 9a0e2b69d91..36d5d3ed9b0 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -24,9 +24,9 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qjson.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/cutils.h" #include "qapi/error.h" #include "block/block_int.h" @@ -43,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 @@ -65,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; @@ -203,7 +205,7 @@ static int coroutine_fn GRAPH_RDLOCK 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) { @@ -289,8 +291,8 @@ cbw_co_preadv_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes, } static int coroutine_fn GRAPH_RDLOCK -cbw_co_snapshot_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, int64_t bytes, +cbw_co_snapshot_block_status(BlockDriverState *bs, unsigned int mode, + int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { @@ -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. @@ -325,28 +327,40 @@ 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,12 +430,12 @@ static BlockdevOptions *cbw_parse_options(QDict *options, Error **errp) static int cbw_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { + ERRP_GUARD(); BDRVCopyBeforeWriteState *s = bs->opaque; BdrvDirtyBitmap *bitmap = NULL; int64_t cluster_size; g_autoptr(BlockdevOptions) full_opts = NULL; BlockdevOptionsCbw *opts; - AioContext *ctx; int ret; full_opts = cbw_parse_options(options, errp); @@ -433,15 +456,13 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - ctx = bdrv_get_aio_context(bs); - aio_context_acquire(ctx); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (opts->bitmap) { bitmap = block_dirty_bitmap_lookup(opts->bitmap->node, opts->bitmap->name, NULL, errp); if (!bitmap) { - ret = -EINVAL; - goto out; + return -EINVAL; } } s->on_cbw_error = opts->has_on_cbw_error ? opts->on_cbw_error : @@ -456,27 +477,28 @@ 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: "); - ret = -EINVAL; - goto out; + return -EINVAL; } cluster_size = block_copy_cluster_size(s->bcs); s->done_bitmap = bdrv_create_dirty_bitmap(bs, cluster_size, NULL, errp); if (!s->done_bitmap) { - ret = -EINVAL; - goto out; + return -EINVAL; } bdrv_disable_dirty_bitmap(s->done_bitmap); /* s->access_bitmap starts equal to bcs bitmap */ s->access_bitmap = bdrv_create_dirty_bitmap(bs, cluster_size, NULL, errp); if (!s->access_bitmap) { - ret = -EINVAL; - goto out; + return -EINVAL; } bdrv_disable_dirty_bitmap(s->access_bitmap); bdrv_dirty_bitmap_merge_internal(s->access_bitmap, @@ -485,11 +507,7 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, qemu_co_mutex_init(&s->lock); QLIST_INIT(&s->frozen_read_reqs); - - ret = 0; -out: - aio_context_release(ctx); - return ret; + return 0; } static void cbw_close(BlockDriverState *bs) @@ -530,12 +548,16 @@ static 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, + OnCbwError on_cbw_error, Error **errp) { 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(); @@ -547,8 +569,17 @@ 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)); + qdict_put_str(opts, "on-cbw-error", OnCbwError_str(on_cbw_error)); + + 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, BDRV_O_RDWR, errp); + 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 6e72bb25e91..eb93364e854 100644 --- a/block/copy-before-write.h +++ b/block/copy-before-write.h @@ -39,7 +39,10 @@ BlockDriverState *bdrv_cbw_append(BlockDriverState *source, BlockDriverState *target, const char *filter_node_name, + bool discard_source, + uint64_t min_cluster_size, BlockCopyState **bcs, + OnCbwError on_cbw_error, Error **errp); void bdrv_cbw_drop(BlockDriverState *bs); diff --git a/block/copy-on-read.c b/block/copy-on-read.c index b4d6b7efc30..accf1402f09 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -25,7 +25,7 @@ #include "block/block_int.h" #include "qemu/module.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "block/copy-on-read.h" @@ -35,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; @@ -44,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 | @@ -146,11 +150,11 @@ cor_co_preadv_part(BlockDriverState *bs, int64_t offset, int64_t bytes, 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; } @@ -227,13 +231,17 @@ cor_co_lock_medium(BlockDriverState *bs, bool 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); @@ -263,12 +271,15 @@ static BlockDriver bdrv_copy_on_read = { }; -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 1d8ad38c745..72f9b378eab 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 f3226682d6c..892646bb7aa 100644 --- a/block/coroutines.h +++ b/block/coroutines.h @@ -28,7 +28,7 @@ #include "block/block_int.h" /* For blk_bs() in generated block/block-gen.c */ -#include "sysemu/block-backend.h" +#include "system/block-backend.h" /* * I/O API functions. These functions are thread-safe. @@ -47,7 +47,7 @@ int coroutine_fn GRAPH_RDLOCK bdrv_co_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, bool include_base, - bool want_zero, + unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, @@ -78,7 +78,7 @@ int co_wrapper_mixed_bdrv_rdlock bdrv_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, bool include_base, - bool want_zero, + unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, diff --git a/block/crypto.c b/block/crypto.c index 6ee8d46d302..d4226cc68a4 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -22,7 +22,7 @@ #include "block/block_int.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "crypto/block.h" #include "qapi/opts-visitor.h" #include "qapi/qapi-visit-crypto.h" @@ -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; @@ -151,6 +162,48 @@ block_crypto_create_init_func(QCryptoBlock *block, size_t headerlen, 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", @@ -178,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 */ } }, }; @@ -256,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; @@ -263,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; @@ -289,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,7 +383,9 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, static int coroutine_fn GRAPH_UNLOCKED block_crypto_co_create_generic(BlockDriverState *bs, int64_t size, QCryptoBlockCreateOptions *opts, - PreallocMode prealloc, Error **errp) + PreallocMode prealloc, + unsigned int flags, + Error **errp) { int ret; BlockBackend *blk; @@ -334,7 +405,7 @@ block_crypto_co_create_generic(BlockDriverState *bs, int64_t size, data = (struct BlockCryptoCreateData) { .blk = blk, - .size = size, + .size = flags & QCRYPTO_BLOCK_CREATE_DETACHED ? 0 : size, .prealloc = prealloc, }; @@ -342,6 +413,7 @@ block_crypto_co_create_generic(BlockDriverState *bs, int64_t size, block_crypto_create_init_func, block_crypto_create_write_func, &data, + flags, errp); if (!crypto) { @@ -610,7 +682,7 @@ static BlockMeasureInfo *block_crypto_measure(QemuOpts *opts, 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); } @@ -619,7 +691,7 @@ 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); } @@ -628,21 +700,31 @@ 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_co_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), }; @@ -650,15 +732,52 @@ 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_co_unref(bs); + if (hdr_bs != NULL) { + bdrv_co_unref(hdr_bs); + } + + if (bs != NULL) { + bdrv_co_unref(bs); + } return ret; } @@ -672,6 +791,9 @@ block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename, 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; @@ -711,8 +833,13 @@ block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename, 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; } @@ -762,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; @@ -777,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; @@ -793,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; @@ -828,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, @@ -875,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, diff --git a/block/crypto.h b/block/crypto.h index 72e792c9af1..dc3d2d5ed91 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 0fc42d03d77..5467678024f 100644 --- a/block/curl.c +++ b/block/curl.c @@ -29,8 +29,8 @@ #include "qemu/option.h" #include "block/block-io.h" #include "block/block_int.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "crypto/secret.h" #include #include "qemu/cutils.h" @@ -210,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; } @@ -696,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; } @@ -1032,7 +1026,7 @@ 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_co_getlength = curl_co_getlength, @@ -1051,7 +1045,7 @@ 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_co_getlength = curl_co_getlength, @@ -1070,7 +1064,7 @@ 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_co_getlength = curl_co_getlength, @@ -1089,7 +1083,7 @@ 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_co_getlength = curl_co_getlength, diff --git a/block/dmg.c b/block/dmg.c index 06a0244a9c1..33dcb3a3498 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -70,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; @@ -84,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; @@ -321,8 +323,9 @@ static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds, 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; @@ -388,8 +391,9 @@ static int dmg_read_resource_fork(BlockDriverState *bs, DmgHeaderState *ds, 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; @@ -452,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; } @@ -461,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 diff --git a/block/export/export.c b/block/export/export.c index 10316b43c5a..f3bbf11070d 100644 --- a/block/export/export.c +++ b/block/export/export.c @@ -14,8 +14,8 @@ #include "qemu/osdep.h" #include "block/block.h" -#include "sysemu/block-backend.h" -#include "sysemu/iothread.h" +#include "system/block-backend.h" +#include "system/iothread.h" #include "block/export.h" #include "block/fuse.h" #include "block/nbd.h" @@ -75,6 +75,7 @@ static const BlockExportDriver *blk_exp_find_driver(BlockExportType type) BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) { bool fixed_iothread = export->has_fixed_iothread && export->fixed_iothread; + bool allow_inactive = export->has_allow_inactive && export->allow_inactive; const BlockExportDriver *drv; BlockExport *exp = NULL; BlockDriverState *bs; @@ -83,6 +84,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,7 +115,6 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) } ctx = bdrv_get_aio_context(bs); - aio_context_acquire(ctx); if (export->iothread) { IOThread *iothread; @@ -131,21 +133,32 @@ 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; } } - /* - * Block exports are used for non-shared storage migration. Make sure - * that BDRV_O_INACTIVE is cleared and the image is ready for write - * access since the export could be available before migration handover. - * ctx was acquired in the caller. - */ - bdrv_activate(bs, NULL); + bdrv_graph_rdlock_main_loop(); + if (allow_inactive) { + if (!drv->supports_inactive) { + error_setg(errp, "Export type does not support inactive exports"); + bdrv_graph_rdunlock_main_loop(); + goto fail; + } + } else { + /* + * Block exports are used for non-shared storage migration. Make sure + * that BDRV_O_INACTIVE is cleared and the image is ready for write + * access since the export could be available before migration handover. + */ + ret = bdrv_activate(bs, errp); + if (ret < 0) { + bdrv_graph_rdunlock_main_loop(); + goto fail; + } + } + bdrv_graph_rdunlock_main_loop(); perm = BLK_PERM_CONSISTENT_READ; if (export->writable) { @@ -157,6 +170,9 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) if (!fixed_iothread) { blk_set_allow_aio_context_change(blk, true); } + if (allow_inactive) { + blk_set_force_allow_inactivate(blk); + } ret = blk_insert_bs(blk, bs, errp); if (ret < 0) { @@ -187,8 +203,6 @@ 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: @@ -196,7 +210,6 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) blk_set_dev_ops(blk, NULL, NULL); blk_unref(blk); } - aio_context_release(ctx); if (exp) { g_free(exp->id); g_free(exp); @@ -214,9 +227,6 @@ void blk_exp_ref(BlockExport *exp) 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); @@ -226,8 +236,6 @@ static void blk_exp_delete_bh(void *opaque) qapi_event_send_block_export_deleted(exp->id); g_free(exp->id); g_free(exp); - - aio_context_release(aio_context); } void blk_exp_unref(BlockExport *exp) @@ -245,22 +253,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); @@ -268,9 +270,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); } /* diff --git a/block/export/fuse.c b/block/export/fuse.c index 3307b640896..465cc9891d6 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -28,7 +28,7 @@ #include "qapi/error.h" #include "qapi/qapi-commands-block.h" #include "qemu/main-loop.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include #include diff --git a/block/export/vduse-blk.c b/block/export/vduse-blk.c index 172f73cef44..bd852e538dc 100644 --- a/block/export/vduse-blk.c +++ b/block/export/vduse-blk.c @@ -273,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; @@ -297,10 +296,8 @@ 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; } } diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c index fe2cee3a78f..d9d2014d9b7 100644 --- a/block/export/vhost-user-blk-server.c +++ b/block/export/vhost-user-blk-server.c @@ -278,32 +278,33 @@ static void vu_blk_exp_resize(void *opaque) vu_config_change_msg(&vexp->vu_server.vu_dev); } -/* Called with vexp->export.ctx acquired */ 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); } -/* Called with vexp->export.blk AioContext acquired */ 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. - * - * Called with vexp->export.ctx acquired. + * 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 vhost_user_server_has_in_flight(&vexp->vu_server); + return server->co_trip || vhost_user_server_has_in_flight(server); } static const BlockDevOps vu_blk_dev_ops = { @@ -318,7 +319,6 @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, { 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; @@ -329,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; } diff --git a/block/export/virtio-blk-handler.h b/block/export/virtio-blk-handler.h index 150d44cff24..cca1544e9fe 100644 --- a/block/export/virtio-blk-handler.h +++ b/block/export/virtio-blk-handler.h @@ -13,7 +13,7 @@ #ifndef VIRTIO_BLK_HANDLER_H #define VIRTIO_BLK_HANDLER_H -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #define VIRTIO_BLK_SECTOR_BITS 9 #define VIRTIO_BLK_SECTOR_SIZE (1ULL << VIRTIO_BLK_SECTOR_BITS) diff --git a/block/file-posix.c b/block/file-posix.c index 4757914ac0f..9b5f08ccb24 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -36,11 +36,12 @@ #include "block/thread-pool.h" #include "qemu/iov.h" #include "block/raw-aio.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "scsi/pr-manager.h" #include "scsi/constants.h" +#include "scsi/utils.h" #if defined(__APPLE__) && (__MACH__) #include @@ -72,6 +73,7 @@ #include #endif #include +#include #include #include #include @@ -110,6 +112,10 @@ #include #endif +#ifdef EMSCRIPTEN +#include +#endif + /* OS X does not have O_DSYNC */ #ifndef O_DSYNC #ifdef O_SYNC @@ -134,6 +140,22 @@ #define RAW_LOCK_PERM_BASE 100 #define RAW_LOCK_SHARED_BASE 200 +/* + * Multiple retries are mostly meant for two separate scenarios: + * + * - DM_MPATH_PROBE_PATHS returns success, but before SG_IO completes, another + * path goes down. + * + * - DM_MPATH_PROBE_PATHS failed all paths in the current path group, so we have + * to send another SG_IO to switch to another path group to probe the paths in + * it. + * + * Even if each path is in a separate path group (path_grouping_policy set to + * failover), it's rare to have more than eight path groups - and even then + * pretty unlikely that only bad path groups would be chosen in eight retries. + */ +#define SG_IO_MAX_RETRIES 8 + typedef struct BDRVRawState { int fd; bool use_lock; @@ -159,8 +181,9 @@ 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; - int64_t *offset; /* offset of zone append operation */ + bool use_mpath:1; int page_cache_inconsistent; /* errno from fdatasync failure */ bool has_fallocate; bool needs_alignment; @@ -194,6 +217,7 @@ static int fd_open(BlockDriverState *bs) } static int64_t raw_getlength(BlockDriverState *bs); +static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs); typedef struct RawPosixAIOData { BlockDriverState *bs; @@ -713,17 +737,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) { @@ -734,14 +755,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."); @@ -790,17 +804,6 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, } #endif - if (S_ISBLK(st.st_mode)) { -#ifdef __linux__ - /* On Linux 3.10, BLKDISCARD leaves stale data in the page cache. Do - * not rely on the contents of discarded blocks unless using O_DIRECT. - * Same for BLKZEROOUT. - */ - if (!(bs->open_flags & BDRV_O_NOCACHE)) { - s->has_write_zeroes = false; - } -#endif - } #ifdef __FreeBSD__ if (S_ISCHR(st.st_mode)) { /* @@ -814,6 +817,13 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, #endif s->needs_alignment = raw_needs_alignment(bs); + bs->supported_write_flags = BDRV_REQ_FUA; + if (s->use_linux_aio && !laio_has_fua()) { + bs->supported_write_flags &= ~BDRV_REQ_FUA; + } else if (s->use_linux_io_uring && !luring_has_fua()) { + bs->supported_write_flags &= ~BDRV_REQ_FUA; + } + bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK; if (S_ISREG(st.st_mode)) { /* When extending regular files, we get zeros from the OS */ @@ -1053,8 +1063,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; @@ -1082,7 +1091,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; } @@ -1279,10 +1288,10 @@ static int get_sysfs_zoned_model(struct stat *st, BlockZoneModel *zoned) } #endif /* defined(CONFIG_BLKZONED) */ +#ifdef CONFIG_LINUX /* * 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; @@ -1302,6 +1311,30 @@ static long get_sysfs_long_val(struct stat *st, const char *attribute) } return ret; } + +/* + * Get a sysfs attribute value as a uint32_t. + */ +static int get_sysfs_u32_val(struct stat *st, const char *attribute, + uint32_t *u32) +{ + g_autofree char *str = NULL; + const char *end; + unsigned int 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_strtoui(str, &end, 10, &val); + if (ret == 0 && end && *end == '\0') { + *u32 = val; + } + return ret; +} #endif static int hdev_get_max_segments(int fd, struct stat *st) @@ -1321,6 +1354,23 @@ static int hdev_get_max_segments(int fd, struct stat *st) #endif } +/* + * Fills in *dalign with the discard alignment and returns 0 on success, + * -errno otherwise. + */ +static int hdev_get_pdiscard_alignment(struct stat *st, uint32_t *dalign) +{ +#ifdef CONFIG_LINUX + /* + * Note that Linux "discard_granularity" is QEMU "discard_alignment". Linux + * "discard_alignment" is something else. + */ + return get_sysfs_u32_val(st, "discard_granularity", dalign); +#else + return -ENOTSUP; +#endif +} + #if defined(CONFIG_BLKZONED) /* * If the reset_all flag is true, then the wps of zone whose state is @@ -1409,14 +1459,12 @@ static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st, Error **errp) { BDRVRawState *s = bs->opaque; - BlockZoneModel zoned; + BlockZoneModel zoned = BLK_Z_NONE; int ret; - bs->bl.zoned = BLK_Z_NONE; - ret = get_sysfs_zoned_model(st, &zoned); if (ret < 0 || zoned == BLK_Z_NONE) { - return; + goto no_zoned; } bs->bl.zoned = zoned; @@ -1437,10 +1485,10 @@ static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st, if (ret < 0) { error_setg_errno(errp, -ret, "Unable to read chunk_sectors " "sysfs attribute"); - return; + goto no_zoned; } else if (!ret) { error_setg(errp, "Read 0 from chunk_sectors sysfs attribute"); - return; + goto no_zoned; } bs->bl.zone_size = ret << BDRV_SECTOR_BITS; @@ -1448,10 +1496,10 @@ static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st, if (ret < 0) { error_setg_errno(errp, -ret, "Unable to read nr_zones " "sysfs attribute"); - return; + goto no_zoned; } else if (!ret) { error_setg(errp, "Read 0 from nr_zones sysfs attribute"); - return; + goto no_zoned; } bs->bl.nr_zones = ret; @@ -1472,10 +1520,15 @@ static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st, ret = get_zones_wp(bs, s->fd, 0, bs->bl.nr_zones, 0); if (ret < 0) { error_setg_errno(errp, -ret, "report wps failed"); - bs->wps = NULL; - return; + 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, @@ -1527,6 +1580,30 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) } } + if (S_ISBLK(st.st_mode)) { + uint32_t dalign = 0; + int ret; + + ret = hdev_get_pdiscard_alignment(&st, &dalign); + if (ret == 0 && dalign != 0) { + uint32_t ralign = bs->bl.request_alignment; + + /* Probably never happens, but handle it just in case */ + if (dalign < ralign && (ralign % dalign == 0)) { + dalign = ralign; + } + + /* The block layer requires a multiple of request_alignment */ + if (dalign % ralign != 0) { + error_setg(errp, "Invalid pdiscard_alignment limit %u is not a " + "multiple of request_alignment %u", dalign, ralign); + return; + } + + bs->bl.pdiscard_alignment = dalign; + } + } + raw_refresh_zoned_limits(bs, &st, errp); } @@ -2011,8 +2088,11 @@ static int handle_aiocb_write_zeroes_unmap(void *opaque) } #ifndef HAVE_COPY_FILE_RANGE -static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd, - off_t *out_off, size_t len, unsigned int flags) +#ifndef EMSCRIPTEN +static +#endif +ssize_t copy_file_range(int in_fd, off_t *in_off, int out_fd, + off_t *out_off, size_t len, unsigned int flags) { #ifdef __NR_copy_file_range return syscall(__NR_copy_file_range, in_fd, in_off, out_fd, @@ -2442,19 +2522,64 @@ static bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) return true; } -static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, int type) +#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, + int flags) { 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->wps) { + 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 && bs->bl.zone_size) { + if (type & QEMU_AIO_ZONE_APPEND) { int index = offset / bs->bl.zone_size; offset = bs->wps->wp[index]; } @@ -2470,15 +2595,15 @@ 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) { + } else if (raw_check_linux_io_uring(s)) { assert(qiov->size == bytes); - ret = luring_co_submit(bs, s->fd, offset, qiov, type); + ret = luring_co_submit(bs, s->fd, offset, qiov, type, flags); goto out; #endif #ifdef CONFIG_LINUX_AIO - } else if (s->use_linux_aio) { + } else if (raw_check_linux_aio(s)) { assert(qiov->size == bytes); - ret = laio_co_submit(s->fd, offset, qiov, type, + ret = laio_co_submit(s->fd, offset, qiov, type, flags, s->aio_max_batch); goto out; #endif @@ -2498,20 +2623,23 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset, assert(qiov->size == bytes); ret = raw_thread_pool_submit(handle_aiocb_rw, &acb); + if (ret == 0 && (flags & BDRV_REQ_FUA)) { + /* TODO Use pwritev2() instead if it's available */ + ret = raw_co_flush_to_disk(bs); + } goto out; /* Avoid the compiler err of unused label */ out: #if defined(CONFIG_BLKZONED) -{ - BlockZoneWps *wps = bs->wps; - if (ret == 0) { - if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) - && wps && bs->bl.zone_size) { + 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) { - *s->offset = *wp; - trace_zbd_zone_append_complete(bs, *s->offset + *offset_ptr = *wp; + trace_zbd_zone_append_complete(bs, *offset_ptr >> BDRV_SECTOR_BITS); } /* Advance the wp if needed */ @@ -2519,17 +2647,15 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset, *wp = offset + bytes; } } + } else { + /* + * write and append write are not allowed to cross zone boundaries + */ + update_zones_wp(bs, s->fd, offset, 1); } - } else { - if (type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) { - update_zones_wp(bs, s->fd, 0, 1); - } - } - if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) && wps) { qemu_co_mutex_unlock(&wps->colock); } -} #endif return ret; } @@ -2538,14 +2664,14 @@ 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, flags); } 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); + return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_WRITE, flags); } static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs) @@ -2566,37 +2692,16 @@ static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs) }; #ifdef CONFIG_LINUX_IO_URING - if (s->use_linux_io_uring) { - return luring_co_submit(bs, 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, 0); } #endif - return raw_thread_pool_submit(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, 0); } #endif + return raw_thread_pool_submit(handle_aiocb_flush, &acb); } static void raw_close(BlockDriverState *bs) @@ -3176,7 +3281,7 @@ static int find_allocation(BlockDriverState *bs, off_t start, * well exceed it. */ static int coroutine_fn raw_co_block_status(BlockDriverState *bs, - bool want_zero, + unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, @@ -3192,7 +3297,8 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs, return ret; } - if (!want_zero) { + if (!(mode & BDRV_WANT_ZERO)) { + /* There is no backing file - all bytes are allocated in this file. */ *pnum = bytes; *map = offset; *file = bs; @@ -3472,7 +3578,7 @@ static int coroutine_fn raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, len >> BDRV_SECTOR_BITS); ret = raw_thread_pool_submit(handle_aiocb_zone_mgmt, &acb); if (ret != 0) { - update_zones_wp(bs, s->fd, offset, i); + update_zones_wp(bs, s->fd, offset, nrz); error_report("ioctl %s failed %d", op_name, ret); return ret; } @@ -3508,8 +3614,6 @@ static int coroutine_fn raw_co_zone_append(BlockDriverState *bs, int64_t zone_size_mask = bs->bl.zone_size - 1; int64_t iov_len = 0; int64_t len = 0; - BDRVRawState *s = bs->opaque; - s->offset = offset; if (*offset & zone_size_mask) { error_report("sector offset %" PRId64 " is not aligned to zone size " @@ -3530,7 +3634,7 @@ static int coroutine_fn raw_co_zone_append(BlockDriverState *bs, } trace_zbd_zone_append(bs, *offset >> BDRV_SECTOR_BITS); - return raw_co_prw(bs, *offset, len, qiov, QEMU_AIO_ZONE_APPEND); + return raw_co_prw(bs, offset, len, qiov, QEMU_AIO_ZONE_APPEND, 0); } #endif @@ -3746,8 +3850,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) { @@ -3877,7 +3980,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, @@ -3897,7 +4000,6 @@ 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_attach_aio_context = raw_aio_attach_aio_context, .bdrv_co_truncate = raw_co_truncate, .bdrv_co_getlength = raw_co_getlength, @@ -3921,11 +4023,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; @@ -4175,15 +4272,105 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags, /* Since this does ioctl the device must be already opened */ bs->sg = hdev_is_sg(bs); + /* sg devices aren't even block devices and can't use dm-mpath */ + s->use_mpath = !bs->sg; + return ret; } #if defined(__linux__) +#if defined(DM_MPATH_PROBE_PATHS) +static bool coroutine_fn sgio_path_error(int ret, sg_io_hdr_t *io_hdr) +{ + if (ret < 0) { + switch (ret) { + case -ENODEV: + return true; + case -EAGAIN: + /* + * The device is probably suspended. This happens while the dm table + * is reloaded, e.g. because a path is added or removed. This is an + * operation that should complete within 1ms, so just wait a bit and + * retry. + * + * If the device was suspended for another reason, we'll wait and + * retry SG_IO_MAX_RETRIES times. This is a tolerable delay before + * we return an error and potentially stop the VM. + */ + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000); + return true; + default: + return false; + } + } + + if (io_hdr->host_status != SCSI_HOST_OK) { + return true; + } + + switch (io_hdr->status) { + case GOOD: + case CONDITION_GOOD: + case INTERMEDIATE_GOOD: + case INTERMEDIATE_C_GOOD: + case RESERVATION_CONFLICT: + case COMMAND_TERMINATED: + return false; + case CHECK_CONDITION: + return !scsi_sense_buf_is_guest_recoverable(io_hdr->sbp, + io_hdr->mx_sb_len); + default: + return true; + } +} + +static bool coroutine_fn hdev_co_ioctl_sgio_retry(RawPosixAIOData *acb, int ret) +{ + BDRVRawState *s = acb->bs->opaque; + RawPosixAIOData probe_acb; + + if (!s->use_mpath) { + return false; + } + + if (!sgio_path_error(ret, acb->ioctl.buf)) { + return false; + } + + probe_acb = (RawPosixAIOData) { + .bs = acb->bs, + .aio_type = QEMU_AIO_IOCTL, + .aio_fildes = s->fd, + .aio_offset = 0, + .ioctl = { + .buf = NULL, + .cmd = DM_MPATH_PROBE_PATHS, + }, + }; + + ret = raw_thread_pool_submit(handle_aiocb_ioctl, &probe_acb); + if (ret == -ENOTTY) { + s->use_mpath = false; + } else if (ret == -EAGAIN) { + /* The device might be suspended for a table reload, worth retrying */ + return true; + } + + return ret == 0; +} +#else +static bool coroutine_fn hdev_co_ioctl_sgio_retry(RawPosixAIOData *acb, int ret) +{ + return false; +} +#endif /* DM_MPATH_PROBE_PATHS */ + static int coroutine_fn hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) { BDRVRawState *s = bs->opaque; RawPosixAIOData acb; + int retries = SG_IO_MAX_RETRIES; int ret; ret = fd_open(bs); @@ -4211,7 +4398,11 @@ hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) }, }; - return raw_thread_pool_submit(handle_aiocb_ioctl, &acb); + do { + ret = raw_thread_pool_submit(handle_aiocb_ioctl, &acb); + } while (req == SG_IO && retries-- && hdev_co_ioctl_sgio_retry(&acb, ret)); + + return ret; } #endif /* linux */ @@ -4249,7 +4440,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, @@ -4267,7 +4458,6 @@ 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_attach_aio_context = raw_aio_attach_aio_context, .bdrv_co_truncate = raw_co_truncate, .bdrv_co_getlength = raw_co_getlength, @@ -4389,7 +4579,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, @@ -4403,7 +4593,6 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_co_pwritev = raw_co_pwritev, .bdrv_co_flush_to_disk = raw_co_flush_to_disk, .bdrv_refresh_limits = cdrom_refresh_limits, - .bdrv_attach_aio_context = raw_aio_attach_aio_context, .bdrv_co_truncate = raw_co_truncate, .bdrv_co_getlength = raw_co_getlength, @@ -4516,7 +4705,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, @@ -4529,7 +4718,6 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_co_pwritev = raw_co_pwritev, .bdrv_co_flush_to_disk = raw_co_flush_to_disk, .bdrv_refresh_limits = cdrom_refresh_limits, - .bdrv_attach_aio_context = raw_aio_attach_aio_context, .bdrv_co_truncate = raw_co_truncate, .bdrv_co_getlength = raw_co_getlength, diff --git a/block/file-win32.c b/block/file-win32.c index 48b790d9173..af9aea631cb 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -33,8 +33,8 @@ #include "trace.h" #include "block/thread-pool.h" #include "qemu/iov.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include #include @@ -746,7 +746,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, @@ -920,7 +920,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, diff --git a/block/filter-compress.c b/block/filter-compress.c index 320d9576fa1..9b68a2be645 100644 --- a/block/filter-compress.c +++ b/block/filter-compress.c @@ -36,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", @@ -97,7 +99,8 @@ compress_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t 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; diff --git a/block/gluster.c b/block/gluster.c index ad5fadbe793..89abd40f313 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -15,9 +15,8 @@ #include "block/block_int.h" #include "block/qdict.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" -#include "qemu/uri.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/option.h" @@ -289,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; @@ -349,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; } @@ -364,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, @@ -518,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; @@ -551,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 */ @@ -662,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; @@ -672,7 +666,6 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, error_propagate(errp, local_err); qapi_free_SocketAddress(gsconf); qemu_opts_del(opts); - g_free(str); qobject_unref(backing_options); errno = EINVAL; return -errno; @@ -813,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, @@ -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); @@ -975,8 +972,6 @@ static void qemu_gluster_reopen_commit(BDRVReopenState *state) g_free(state->opaque); state->opaque = NULL; - - return; } @@ -996,8 +991,6 @@ static void qemu_gluster_reopen_abort(BDRVReopenState *state) g_free(state->opaque); state->opaque = NULL; - - return; } #ifdef CONFIG_GLUSTERFS_ZEROFILL @@ -1468,7 +1461,7 @@ static int find_allocation(BlockDriverState *bs, off_t start, * (Based on raw_co_block_status() from file-posix.c.) */ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs, - bool want_zero, + unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, @@ -1485,7 +1478,7 @@ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs, return ret; } - if (!want_zero) { + if (!(mode & BDRV_WANT_ZERO)) { *pnum = bytes; *map = offset; *file = bs; @@ -1553,7 +1546,7 @@ 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, @@ -1582,7 +1575,7 @@ 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, @@ -1611,42 +1604,7 @@ static BlockDriver bdrv_gluster_unix = { .format_name = "gluster", .protocol_name = "gluster+unix", .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_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, - .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_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, @@ -1673,7 +1631,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 index f357a2c0b17..c81162b1473 100644 --- a/block/graph-lock.c +++ b/block/graph-lock.c @@ -106,25 +106,11 @@ static uint32_t reader_count(void) return rd; } -void bdrv_graph_wrlock(BlockDriverState *bs) +void no_coroutine_fn bdrv_graph_wrlock(void) { - AioContext *ctx = NULL; - GLOBAL_STATE_CODE(); assert(!qatomic_read(&has_writer)); - - /* - * Release only non-mainloop AioContext. The mainloop often relies on the - * BQL and doesn't lock the main AioContext before doing things. - */ - if (bs) { - ctx = bdrv_get_aio_context(bs); - if (ctx != qemu_get_aio_context()) { - aio_context_release(ctx); - } else { - ctx = NULL; - } - } + assert(!qemu_in_coroutine()); /* Make sure that constantly arriving new I/O doesn't cause starvation */ bdrv_drain_all_begin_nopoll(); @@ -154,26 +140,34 @@ void bdrv_graph_wrlock(BlockDriverState *bs) } while (reader_count() >= 1); bdrv_drain_all_end(); - - if (ctx) { - aio_context_acquire(bdrv_get_aio_context(bs)); - } } -void bdrv_graph_wrunlock(void) +void no_coroutine_fn bdrv_graph_wrunlock(void) { GLOBAL_STATE_CODE(); - QEMU_LOCK_GUARD(&aio_context_list_lock); 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); + } + /* - * No need for memory barriers, this works in pair with - * the slow path of rdlock() and both take the 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. */ - qatomic_store_release(&has_writer, 0); - - /* Wake up all coroutine that are waiting to read the graph */ - qemu_co_enter_all(&reader_queue, &aio_context_list_lock); + aio_bh_poll(qemu_get_aio_context()); } void coroutine_fn bdrv_graph_co_rdlock(void) diff --git a/block/io.c b/block/io.c index ba23a9bcd3c..ac5c7174f6b 100644 --- a/block/io.c +++ b/block/io.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "trace.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "block/aio-wait.h" #include "block/blockjob.h" #include "block/blockjob_int.h" @@ -37,18 +37,27 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" -#include "sysemu/replay.h" +#include "system/replay.h" +#include "qemu/units.h" /* 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); +/* Maximum read size for checking if data reads as zero, in bytes */ +#define MAX_ZERO_CHECK_BUFFER (128 * KiB) + +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) +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) { @@ -70,9 +79,12 @@ void bdrv_parent_drained_end_single(BdrvChild *c) } } -static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore) +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) { @@ -84,17 +96,22 @@ static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore) bool bdrv_parent_drained_poll_single(BdrvChild *c) { + IO_OR_GS_CODE(); + if (c->klass->drained_poll) { return c->klass->drained_poll(c); } return false; } -static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, - bool ignore_bds_parents) +static bool GRAPH_RDLOCK +bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, + bool ignore_bds_parents) { BdrvChild *c, *next; bool busy = false; + IO_OR_GS_CODE(); + assert_bdrv_graph_readable(); QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { if (c == ignore || (ignore_bds_parents && c->klass->parent_is_bds)) { @@ -114,6 +131,7 @@ void bdrv_parent_drained_begin_single(BdrvChild *c) 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); } } @@ -263,6 +281,9 @@ bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent, static bool bdrv_drain_poll_top_level(BlockDriverState *bs, BdrvChild *ignore_parent) { + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + return bdrv_drain_poll(bs, ignore_parent, false); } @@ -277,8 +298,6 @@ 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) { bdrv_do_drained_begin(bs, data->parent, data->poll); @@ -286,7 +305,6 @@ static void bdrv_co_drain_bh_cb(void *opaque) assert(!data->poll); bdrv_do_drained_end(bs, data->parent); } - aio_context_release(ctx); } else { assert(data->begin); bdrv_drain_all_begin(); @@ -303,8 +321,6 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, { 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(). */ @@ -323,17 +339,6 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *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(qemu_get_aio_context(), bdrv_co_drain_bh_cb, &data); @@ -341,11 +346,6 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, /* 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); - - /* Reacquire the AioContext of bs if we dropped it */ - if (ctx != co_ctx) { - aio_context_acquire(ctx); - } } static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent, @@ -362,6 +362,7 @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent, /* 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); @@ -387,7 +388,8 @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent) bdrv_do_drained_begin(bs, parent, false); } -void bdrv_drained_begin(BlockDriverState *bs) +void coroutine_mixed_fn +bdrv_drained_begin(BlockDriverState *bs) { IO_OR_GS_CODE(); bdrv_do_drained_begin(bs, NULL, true); @@ -407,12 +409,15 @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent) bdrv_co_yield_to_drain(bs, false, parent, false); return; } - assert(bs->quiesce_counter > 0); + + /* At this point, we should be always running in the main loop. */ GLOBAL_STATE_CODE(); + assert(bs->quiesce_counter > 0); /* Re-enable things in child-to-parent order */ old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter); if (old_quiesce_counter == 1) { + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (bs->drv && bs->drv->bdrv_drain_end) { bs->drv->bdrv_drain_end(bs); } @@ -436,6 +441,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) { @@ -449,15 +456,16 @@ static bool bdrv_drain_all_poll(void) { BlockDriverState *bs = NULL; bool result = false; + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); - /* 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. */ + /* + * 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, NULL, true); - aio_context_release(aio_context); } return result; @@ -498,15 +506,11 @@ void bdrv_drain_all_begin_nopoll(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); - - aio_context_acquire(aio_context); bdrv_do_drained_begin(bs, NULL, false); - aio_context_release(aio_context); } } -void bdrv_drain_all_begin(void) +void coroutine_mixed_fn bdrv_drain_all_begin(void) { BlockDriverState *bs = NULL; @@ -561,11 +565,7 @@ 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, NULL); - aio_context_release(aio_context); } assert(qemu_get_current_aio_context() == qemu_get_aio_context()); @@ -1061,6 +1061,10 @@ bdrv_driver_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, return -ENOMEDIUM; } + if (bs->open_flags & BDRV_O_NO_FLUSH) { + flags &= ~BDRV_REQ_FUA; + } + if ((flags & BDRV_REQ_FUA) && (~bs->supported_write_flags & BDRV_REQ_FUA)) { flags &= ~BDRV_REQ_FUA; @@ -1222,8 +1226,8 @@ bdrv_co_do_copy_on_readv(BdrvChild *child, int64_t offset, int64_t bytes, ret = 1; /* "already allocated", so nothing will be copied */ pnum = MIN(align_bytes, max_transfer); } else { - ret = bdrv_is_allocated(bs, align_offset, - MIN(align_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 @@ -1370,7 +1374,7 @@ bdrv_aligned_preadv(BdrvChild *child, BdrvTrackedRequest *req, /* 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; } @@ -1729,22 +1733,29 @@ static int bdrv_pad_request(BlockDriverState *bs, return 0; } - 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; + /* + * 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; } @@ -1858,6 +1869,11 @@ bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, 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); @@ -2002,7 +2018,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) { @@ -2311,10 +2327,6 @@ int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, trace_bdrv_co_pwrite_zeroes(child->bs, offset, bytes, flags); assert_bdrv_graph_readable(); - if (!(child->bs->open_flags & BDRV_O_UNMAP)) { - flags &= ~BDRV_REQ_MAY_UNMAP; - } - return bdrv_co_pwritev(child, offset, bytes, NULL, BDRV_REQ_ZERO_WRITE | flags); } @@ -2329,6 +2341,7 @@ int bdrv_flush_all(void) int result = 0; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); /* * bdrv queue is managed by record/replay, @@ -2340,15 +2353,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; @@ -2359,10 +2367,8 @@ int bdrv_flush_all(void) * Drivers not implementing the functionality are assumed to not support * backing files, hence all their sectors are reported as allocated. * - * If 'want_zero' is true, the caller is querying for mapping - * purposes, with a focus on valid BDRV_BLOCK_OFFSET_VALID, _DATA, and - * _ZERO where possible; otherwise, the result favors larger 'pnum', - * with a focus on accurate BDRV_BLOCK_ALLOCATED. + * 'mode' serves as a hint as to which results are favored; see the + * BDRV_WANT_* macros for details. * * If 'offset' is beyond the end of the disk image the return value is * BDRV_BLOCK_EOF and 'pnum' is set to 0. @@ -2382,9 +2388,9 @@ int bdrv_flush_all(void) * set to the host mapping and BDS corresponding to the guest offset. */ static int coroutine_fn GRAPH_RDLOCK -bdrv_co_block_status(BlockDriverState *bs, bool want_zero, - int64_t offset, int64_t bytes, - int64_t *pnum, int64_t *map, BlockDriverState **file) +bdrv_co_do_block_status(BlockDriverState *bs, unsigned int mode, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, BlockDriverState **file) { int64_t total_size; int64_t n; /* bytes */ @@ -2471,7 +2477,7 @@ bdrv_co_block_status(BlockDriverState *bs, bool want_zero, local_file = bs; local_map = aligned_offset; } else { - ret = bs->drv->bdrv_co_block_status(bs, want_zero, aligned_offset, + ret = bs->drv->bdrv_co_block_status(bs, mode, aligned_offset, aligned_bytes, pnum, &local_map, &local_file); @@ -2483,10 +2489,10 @@ bdrv_co_block_status(BlockDriverState *bs, bool want_zero, * the cache requires an RCU update, so double check here to avoid * such an update if possible. * - * Check want_zero, because we only want to update the cache when we + * Check mode, because we only want to update the cache when we * have accurate information about what is zero and what is data. */ - if (want_zero && + if (mode == BDRV_WANT_PRECISE && ret == (BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID) && QLIST_EMPTY(&bs->children)) { @@ -2543,8 +2549,8 @@ bdrv_co_block_status(BlockDriverState *bs, bool want_zero, 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, mode, local_map, + *pnum, pnum, &local_map, &local_file); goto out; } @@ -2555,7 +2561,7 @@ bdrv_co_block_status(BlockDriverState *bs, bool want_zero, if (!cow_bs) { ret |= BDRV_BLOCK_ZERO; - } else if (want_zero) { + } else if (mode == BDRV_WANT_PRECISE) { int64_t size2 = bdrv_co_getlength(cow_bs); if (size2 >= 0 && offset >= size2) { @@ -2564,15 +2570,15 @@ bdrv_co_block_status(BlockDriverState *bs, bool want_zero, } } - if (want_zero && ret & BDRV_BLOCK_RECURSE && + if (mode == BDRV_WANT_PRECISE && ret & BDRV_BLOCK_RECURSE && local_file && local_file != bs && (ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO) && (ret & BDRV_BLOCK_OFFSET_VALID)) { 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, mode, local_map, + *pnum, &file_pnum, NULL, NULL); if (ret2 >= 0) { /* Ignore errors. This is just providing extra information, it * is useful but not necessary. @@ -2591,6 +2597,16 @@ bdrv_co_block_status(BlockDriverState *bs, bool want_zero, 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: @@ -2612,7 +2628,7 @@ int coroutine_fn bdrv_co_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, bool include_base, - bool want_zero, + unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, @@ -2639,7 +2655,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, mode, offset, bytes, pnum, + map, file); ++*depth; if (ret < 0 || *pnum == 0 || ret & BDRV_BLOCK_ALLOCATED || bs == base) { return ret; @@ -2655,8 +2672,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, mode, offset, bytes, pnum, + map, file); ++*depth; if (ret < 0) { return ret; @@ -2718,25 +2735,18 @@ int coroutine_fn bdrv_co_block_status_above(BlockDriverState *bs, BlockDriverState **file) { IO_CODE(); - return bdrv_co_common_block_status_above(bs, base, false, true, offset, + return bdrv_co_common_block_status_above(bs, base, false, + BDRV_WANT_PRECISE, offset, bytes, pnum, map, file, NULL); } -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(BlockDriverState *bs, 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); -} - -int bdrv_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); } /* @@ -2744,82 +2754,105 @@ int bdrv_block_status(BlockDriverState *bs, int64_t offset, int64_t bytes, * by @offset and @bytes is known to read as zeroes. * Return 1 if that is the case, 0 otherwise and -errno on error. * This test is meant to be fast rather than accurate so returning 0 - * does not guarantee non-zero data. + * does not guarantee non-zero data; but a return of 1 is reliable. */ int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, int64_t bytes) { int ret; - int64_t pnum = bytes; + int64_t pnum; IO_CODE(); - if (!bytes) { - return 1; - } - - ret = bdrv_co_common_block_status_above(bs, NULL, false, false, offset, - bytes, &pnum, NULL, NULL, NULL); + while (bytes) { + ret = bdrv_co_common_block_status_above(bs, NULL, false, + BDRV_WANT_ZERO, offset, bytes, + &pnum, NULL, NULL, NULL); - if (ret < 0) { - return ret; + if (ret < 0) { + return ret; + } + if (!(ret & BDRV_BLOCK_ZERO)) { + return 0; + } + offset += pnum; + bytes -= pnum; } - return (pnum == bytes) && (ret & BDRV_BLOCK_ZERO); + return 1; } -int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset, - int64_t bytes, int64_t *pnum) +/* + * Check @bs (and its backing chain) to see if the entire image is known + * to read as zeroes. + * Return 1 if that is the case, 0 otherwise and -errno on error. + * This test is meant to be fast rather than accurate so returning 0 + * does not guarantee non-zero data; however, a return of 1 is reliable, + * and this function can report 1 in more cases than bdrv_co_is_zero_fast. + */ +int coroutine_fn bdrv_co_is_all_zeroes(BlockDriverState *bs) { int ret; - int64_t dummy; + int64_t pnum, bytes; + char *buf; + QEMUIOVector local_qiov; IO_CODE(); - ret = bdrv_co_common_block_status_above(bs, bs, true, false, offset, - bytes, pnum ? pnum : &dummy, NULL, - NULL, NULL); + bytes = bdrv_co_getlength(bs); + if (bytes < 0) { + return bytes; + } + + /* First probe - see if the entire image reads as zero */ + ret = bdrv_co_common_block_status_above(bs, NULL, false, BDRV_WANT_ZERO, + 0, bytes, &pnum, NULL, NULL, + NULL); if (ret < 0) { return ret; } - return !!(ret & BDRV_BLOCK_ALLOCATED); -} - -int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, - int64_t *pnum) -{ - int ret; - int64_t dummy; - IO_CODE(); + if (ret & BDRV_BLOCK_ZERO) { + return bdrv_co_is_zero_fast(bs, pnum, bytes - pnum); + } - ret = bdrv_common_block_status_above(bs, bs, true, false, offset, - bytes, pnum ? pnum : &dummy, NULL, - NULL, NULL); - if (ret < 0) { + /* + * Because of the way 'blockdev-create' works, raw files tend to + * be created with a non-sparse region at the front to make + * alignment probing easier. If the block starts with only a + * small allocated region, it is still worth the effort to see if + * the rest of the image is still sparse, coupled with manually + * reading the first region to see if it reads zero after all. + */ + if (pnum > MAX_ZERO_CHECK_BUFFER) { + return 0; + } + ret = bdrv_co_is_zero_fast(bs, pnum, bytes - pnum); + if (ret <= 0) { return ret; } - return !!(ret & BDRV_BLOCK_ALLOCATED); + /* Only the head of the image is unknown, and it's small. Read it. */ + buf = qemu_blockalign(bs, pnum); + qemu_iovec_init_buf(&local_qiov, buf, pnum); + ret = bdrv_driver_preadv(bs, 0, pnum, &local_qiov, 0, 0); + if (ret >= 0) { + ret = buffer_is_zero(buf, pnum); + } + qemu_vfree(buf); + return ret; } -/* See bdrv_is_allocated_above for documentation */ -int coroutine_fn bdrv_co_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(BlockDriverState *bs, int64_t offset, + int64_t bytes, int64_t *pnum) { - int depth; int ret; + int64_t dummy; IO_CODE(); - ret = bdrv_co_common_block_status_above(top, base, include_base, false, - offset, bytes, pnum, NULL, NULL, - &depth); + ret = bdrv_co_common_block_status_above(bs, bs, true, BDRV_WANT_ALLOCATED, + offset, bytes, pnum ? pnum : &dummy, + NULL, NULL, NULL); if (ret < 0) { return ret; } - - if (ret & BDRV_BLOCK_ALLOCATED) { - return depth; - } - return 0; + return !!(ret & BDRV_BLOCK_ALLOCATED); } /* @@ -2839,18 +2872,19 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top, * 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; IO_CODE(); - ret = bdrv_common_block_status_above(top, base, include_base, false, - offset, bytes, pnum, NULL, NULL, - &depth); + ret = bdrv_co_common_block_status_above(bs, base, include_base, + BDRV_WANT_ALLOCATED, + offset, bytes, pnum, NULL, NULL, + &depth); if (ret < 0) { return ret; } @@ -2950,25 +2984,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); } @@ -3140,18 +3167,19 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, return 0; } - if (!bs->drv->bdrv_co_pdiscard && !bs->drv->bdrv_aio_pdiscard) { + if (!bs->drv->bdrv_co_pdiscard) { return 0; } /* Invalidate the cached block-status data range if this discard overlaps */ bdrv_bsc_invalidate_range(bs, offset, bytes); - /* Discard is advisory, but some devices track and coalesce + /* + * Discard is advisory, but some devices track and coalesce * unaligned requests, so we must pass everything down rather than - * round here. Still, most devices will just silently ignore - * unaligned requests (by returning -ENOTSUP), so we must fragment - * the request accordingly. */ + * round here. Still, most devices reject unaligned requests with + * -EINVAL or -ENOTSUP, so we must fragment the request accordingly. + */ align = MAX(bs->bl.pdiscard_alignment, bs->bl.request_alignment); assert(align % bs->bl.request_alignment == 0); head = offset % align; @@ -3199,27 +3227,15 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, ret = -ENOMEDIUM; goto out; } - if (bs->drv->bdrv_co_pdiscard) { - ret = bs->drv->bdrv_co_pdiscard(bs, offset, num); - } else { - BlockAIOCB *acb; - CoroutineIOCompletion co = { - .coroutine = qemu_coroutine_self(), - }; - - acb = bs->drv->bdrv_aio_pdiscard(bs, offset, num, - bdrv_co_io_em_complete, &co); - if (acb == NULL) { - ret = -EIO; - goto out; + + ret = bs->drv->bdrv_co_pdiscard(bs, offset, num); + if (ret && ret != -ENOTSUP) { + if (ret == -EINVAL && (offset % align != 0 || num % align != 0)) { + /* Silently skip rejected unaligned head/tail requests */ } else { - qemu_coroutine_yield(); - ret = co.ret; + goto out; /* bail out */ } } - if (ret && ret != -ENOTSUP) { - goto out; - } offset += num; bytes -= num; @@ -3557,9 +3573,13 @@ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_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); @@ -3706,6 +3726,8 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, void bdrv_cancel_in_flight(BlockDriverState *bs) { GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!bs || !bs->drv) { return; } @@ -3741,8 +3763,8 @@ bdrv_co_preadv_snapshot(BdrvChild *child, int64_t offset, int64_t bytes, } int coroutine_fn -bdrv_co_snapshot_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, int64_t bytes, +bdrv_co_snapshot_block_status(BlockDriverState *bs, unsigned int mode, + int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { @@ -3760,7 +3782,7 @@ bdrv_co_snapshot_block_status(BlockDriverState *bs, } bdrv_inc_in_flight(bs); - ret = drv->bdrv_co_snapshot_block_status(bs, want_zero, offset, bytes, + ret = drv->bdrv_co_snapshot_block_status(bs, mode, offset, bytes, pnum, map, file); bdrv_dec_in_flight(bs); diff --git a/block/io_uring.c b/block/io_uring.c index 69d9820928a..dd4f304910b 100644 --- a/block/io_uring.c +++ b/block/io_uring.c @@ -15,8 +15,9 @@ #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 "system/block-backend.h" #include "trace.h" /* Only used for assertions. */ @@ -48,7 +49,7 @@ typedef struct LuringQueue { QSIMPLEQ_HEAD(, LuringAIOCB) submit_queue; } LuringQueue; -typedef struct LuringState { +struct LuringState { AioContext *aio_context; struct io_uring ring; @@ -57,7 +58,7 @@ typedef struct LuringState { LuringQueue io_q; QEMUBH *completion_bh; -} LuringState; +}; /** * luring_resubmit: @@ -101,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); @@ -124,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 @@ -216,7 +220,10 @@ static void luring_process_completions(LuringState *s) aio_co_wake(luringcb->co); } } + qemu_bh_cancel(s->completion_bh); + + defer_call_end(); } static int ioq_submit(LuringState *s) @@ -306,7 +313,7 @@ static void ioq_init(LuringQueue *io_q) io_q->blocked = false; } -static void luring_unplug_fn(void *opaque) +static void luring_deferred_fn(void *opaque) { LuringState *s = opaque; trace_luring_unplug_fn(s, s->io_q.blocked, s->io_q.in_queue, @@ -328,15 +335,24 @@ static void luring_unplug_fn(void *opaque) * */ static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s, - uint64_t offset, int type) + uint64_t offset, int type, BdrvRequestFlags flags) { int ret; struct io_uring_sqe *sqes = &luringcb->sqeq; switch (type) { case QEMU_AIO_WRITE: +#ifdef HAVE_IO_URING_PREP_WRITEV2 + { + int luring_flags = (flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0; + io_uring_prep_writev2(sqes, fd, luringcb->qiov->iov, + luringcb->qiov->niov, offset, luring_flags); + } +#else + assert(flags == 0); io_uring_prep_writev(sqes, fd, luringcb->qiov->iov, luringcb->qiov->niov, offset); +#endif break; case QEMU_AIO_ZONE_APPEND: io_uring_prep_writev(sqes, fd, luringcb->qiov->iov, @@ -367,13 +383,14 @@ static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s, return ret; } - blk_io_plug_call(luring_unplug_fn, s); + defer_call(luring_deferred_fn, s); } return 0; } int coroutine_fn luring_co_submit(BlockDriverState *bs, int fd, uint64_t offset, - QEMUIOVector *qiov, int type) + QEMUIOVector *qiov, int type, + BdrvRequestFlags flags) { int ret; AioContext *ctx = qemu_get_current_aio_context(); @@ -386,7 +403,7 @@ int coroutine_fn luring_co_submit(BlockDriverState *bs, int fd, uint64_t offset, }; trace_luring_co_submit(bs, s, &luringcb, fd, offset, qiov ? qiov->size : 0, type); - ret = luring_do_submit(fd, &luringcb, s, offset, type); + ret = luring_do_submit(fd, &luringcb, s, offset, type, flags); if (ret < 0) { return ret; @@ -425,7 +442,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; } @@ -441,3 +458,12 @@ void luring_cleanup(LuringState *s) trace_luring_cleanup_state(s); g_free(s); } + +bool luring_has_fua(void) +{ +#ifdef HAVE_IO_URING_PREP_WRITEV2 + return true; +#else + return false; +#endif +} diff --git a/block/iscsi.c b/block/iscsi.c index 5640c8b5657..15b96ee8800 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -28,7 +28,7 @@ #include #include #include -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qemu/bitops.h" @@ -41,11 +41,11 @@ #include "qemu/module.h" #include "qemu/option.h" #include "qemu/uuid.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "crypto/secret.h" #include "scsi/utils.h" #include "trace.h" @@ -694,9 +694,9 @@ iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, static int coroutine_fn iscsi_co_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, - int64_t bytes, int64_t *pnum, - int64_t *map, + unsigned int mode, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, BlockDriverState **file) { IscsiLun *iscsilun = bs->opaque; @@ -1925,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; } @@ -2427,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, @@ -2466,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, diff --git a/block/linux-aio.c b/block/linux-aio.c index 1a515032717..c200e7ad20f 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -14,8 +14,9 @@ #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" +#include "system/block-backend.h" /* Only used for assertions. */ #include "qemu/coroutine_int.h" @@ -204,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); @@ -230,6 +233,8 @@ static void qemu_laio_process_completions(LinuxAioState *s) * 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) @@ -286,7 +291,7 @@ static void ioq_submit(LinuxAioState *s) { int ret, len; struct qemu_laiocb *aiocb; - struct iocb *iocbs[MAX_EVENTS]; + QEMU_UNINITIALIZED struct iocb *iocbs[MAX_EVENTS]; QSIMPLEQ_HEAD(, qemu_laiocb) completed; do { @@ -353,7 +358,7 @@ static uint64_t laio_max_batch(LinuxAioState *s, uint64_t dev_max_batch) return max_batch; } -static void laio_unplug_fn(void *opaque) +static void laio_deferred_fn(void *opaque) { LinuxAioState *s = opaque; @@ -363,7 +368,8 @@ static void laio_unplug_fn(void *opaque) } static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, - int type, uint64_t dev_max_batch) + int type, BdrvRequestFlags flags, + uint64_t dev_max_batch) { LinuxAioState *s = laiocb->ctx; struct iocb *iocbs = &laiocb->iocb; @@ -371,7 +377,15 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, switch (type) { case QEMU_AIO_WRITE: +#ifdef HAVE_IO_PREP_PWRITEV2 + { + int laio_flags = (flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0; + io_prep_pwritev2(iocbs, fd, qiov->iov, qiov->niov, offset, laio_flags); + } +#else + assert(flags == 0); io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); +#endif break; case QEMU_AIO_ZONE_APPEND: io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); @@ -379,6 +393,9 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, 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", @@ -393,7 +410,7 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, if (s->io_q.in_queue >= laio_max_batch(s, dev_max_batch)) { ioq_submit(s); } else { - blk_io_plug_call(laio_unplug_fn, s); + defer_call(laio_deferred_fn, s); } } @@ -401,20 +418,21 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, } int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov, - int type, uint64_t dev_max_batch) + int type, BdrvRequestFlags flags, + 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, + .nbytes = qiov ? qiov->size : 0, .ctx = aio_get_linux_aio(ctx), .ret = -EINPROGRESS, .is_read = (type == QEMU_AIO_READ), .qiov = qiov, }; - ret = laio_do_submit(fd, &laiocb, offset, type, dev_max_batch); + ret = laio_do_submit(fd, &laiocb, offset, type, flags, dev_max_batch); if (ret < 0) { return ret; } @@ -481,3 +499,28 @@ 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; +} + +bool laio_has_fua(void) +{ +#ifdef HAVE_IO_PREP_PWRITEV2 + return true; +#else + return false; +#endif +} diff --git a/block/meson.build b/block/meson.build index f351b9d0d33..34b1b2a3063 100644 --- a/block/meson.build +++ b/block/meson.build @@ -21,7 +21,6 @@ block_ss.add(files( 'mirror.c', 'nbd.c', 'null.c', - 'plug.c', 'preallocate.c', 'progress_meter.c', 'qapi.c', @@ -40,7 +39,7 @@ block_ss.add(files( 'throttle.c', 'throttle-groups.c', 'write-threshold.c', -), zstd, zlib, gnutls) +), zstd, zlib) system_ss.add(when: 'CONFIG_TCG', if_true: files('blkreplay.c')) system_ss.add(files('block-ram-registrar.c')) @@ -89,10 +88,15 @@ 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 host_os == 'linux' + block_ss.add(files('nvme.c')) +endif if get_option('replication').allowed() block_ss.add(files('replication.c')) endif @@ -106,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')], @@ -115,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 @@ -150,8 +154,8 @@ block_gen_c = custom_target('block-gen.c', '../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', + '../include/system/block-backend-global-state.h', + '../include/system/block-backend-io.h', 'coroutines.h' ), command: [wrapper_py, '@OUTPUT@', '@INPUT@']) @@ -159,7 +163,7 @@ block_ss.add(block_gen_c) block_ss.add(files('stream.c')) -system_ss.add(files('qapi-sysemu.c')) +system_ss.add(files('qapi-system.c')) subdir('export') subdir('monitor') diff --git a/block/mirror.c b/block/mirror.c index aae4bebbb63..6e8caf4b49f 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -19,7 +19,7 @@ #include "block/blockjob_int.h" #include "block/block_int.h" #include "block/dirty-bitmap.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qemu/ratelimit.h" #include "qemu/bitmap.h" @@ -51,20 +51,29 @@ typedef struct MirrorBlockJob { BlockDriverState *to_replace; /* Used to block operations on the drive-mirror-replace target */ Error *replace_blocker; - bool is_none_mode; + MirrorSyncMode sync_mode; BlockMirrorBackingMode backing_mode; - /* Whether the target image requires explicit zero-initialization */ - bool zero_target; + /* Whether the target should be assumed to be already zero initialized */ + bool target_is_zero; + /* + * 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; size_t buf_size; int64_t bdev_length; unsigned long *cow_bitmap; + unsigned long *zero_bitmap; BdrvDirtyBitmap *dirty_bitmap; BdrvDirtyBitmapIter *dbi; uint8_t *buf; @@ -85,6 +94,7 @@ typedef struct MirrorBlockJob { int64_t active_write_bytes_in_flight; bool prepared; bool in_drain; + bool base_ro; } MirrorBlockJob; typedef struct MirrorBDSOpaque { @@ -99,9 +109,12 @@ struct MirrorOp { int64_t offset; uint64_t bytes; - /* The pointee is set by mirror_co_read(), mirror_co_zero(), and - * mirror_co_discard() before yielding for the first time */ + /* + * These pointers are set by mirror_co_read(), mirror_co_zero(), and + * mirror_co_discard() before yielding for the first time + */ int64_t *bytes_handled; + bool *io_skipped; bool is_pseudo_op; bool is_active_write; @@ -122,7 +135,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); @@ -340,7 +353,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; @@ -399,15 +412,34 @@ static void coroutine_fn mirror_co_read(void *opaque) static void coroutine_fn mirror_co_zero(void *opaque) { MirrorOp *op = opaque; - int ret; + bool write_needed = true; + int ret = 0; op->s->in_flight++; op->s->bytes_in_flight += op->bytes; *op->bytes_handled = op->bytes; op->is_in_flight = true; - ret = blk_co_pwrite_zeroes(op->s->target, op->offset, op->bytes, - op->s->unmap ? BDRV_REQ_MAY_UNMAP : 0); + if (op->s->zero_bitmap) { + unsigned long end = DIV_ROUND_UP(op->offset + op->bytes, + op->s->granularity); + assert(QEMU_IS_ALIGNED(op->offset, op->s->granularity)); + assert(QEMU_IS_ALIGNED(op->bytes, op->s->granularity) || + op->offset + op->bytes == op->s->bdev_length); + if (find_next_zero_bit(op->s->zero_bitmap, end, + op->offset / op->s->granularity) == end) { + write_needed = false; + *op->io_skipped = true; + } + } + if (write_needed) { + ret = blk_co_pwrite_zeroes(op->s->target, op->offset, op->bytes, + op->s->unmap ? BDRV_REQ_MAY_UNMAP : 0); + } + if (ret >= 0 && op->s->zero_bitmap) { + bitmap_set(op->s->zero_bitmap, op->offset / op->s->granularity, + DIV_ROUND_UP(op->bytes, op->s->granularity)); + } mirror_write_complete(op, ret); } @@ -426,29 +458,43 @@ static void coroutine_fn mirror_co_discard(void *opaque) } static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, - unsigned bytes, MirrorMethod mirror_method) + unsigned bytes, MirrorMethod mirror_method, + bool *io_skipped) { MirrorOp *op; Coroutine *co; int64_t bytes_handled = -1; + assert(QEMU_IS_ALIGNED(offset, s->granularity)); + assert(QEMU_IS_ALIGNED(bytes, s->granularity) || + offset + bytes == s->bdev_length); op = g_new(MirrorOp, 1); *op = (MirrorOp){ .s = s, .offset = offset, .bytes = bytes, .bytes_handled = &bytes_handled, + .io_skipped = io_skipped, }; qemu_co_queue_init(&op->waiting_requests); switch (mirror_method) { case MIRROR_METHOD_COPY: + if (s->zero_bitmap) { + bitmap_clear(s->zero_bitmap, offset / s->granularity, + DIV_ROUND_UP(bytes, s->granularity)); + } co = qemu_coroutine_create(mirror_co_read, op); break; case MIRROR_METHOD_ZERO: + /* s->zero_bitmap handled in mirror_co_zero */ co = qemu_coroutine_create(mirror_co_zero, op); break; case MIRROR_METHOD_DISCARD: + if (s->zero_bitmap) { + bitmap_clear(s->zero_bitmap, offset / s->granularity, + DIV_ROUND_UP(bytes, s->granularity)); + } co = qemu_coroutine_create(mirror_co_discard, op); break; default: @@ -471,9 +517,9 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, return bytes_handled; } -static void 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; /* At least the first dirty chunk is mirrored in one iteration. */ @@ -481,6 +527,10 @@ static void coroutine_fn mirror_iteration(MirrorBlockJob *s) 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) { @@ -552,16 +602,17 @@ static void 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; + bool io_skipped = false; MirrorMethod mirror_method = MIRROR_METHOD_COPY; assert(!(offset % s->granularity)); WITH_GRAPH_RDLOCK_GUARD() { - ret = bdrv_block_status_above(source, NULL, offset, - nb_chunks * s->granularity, - &io_bytes, NULL, NULL); + 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); @@ -598,8 +649,10 @@ static void coroutine_fn mirror_iteration(MirrorBlockJob *s) } io_bytes = mirror_clip_bytes(s, offset, io_bytes); - io_bytes = mirror_perform(s, offset, io_bytes, mirror_method); - if (mirror_method != MIRROR_METHOD_COPY && write_zeroes_ok) { + io_bytes = mirror_perform(s, offset, io_bytes, mirror_method, + &io_skipped); + if (io_skipped || + (mirror_method != MIRROR_METHOD_COPY && write_zeroes_ok)) { io_bytes_acct = 0; } else { io_bytes_acct = io_bytes; @@ -654,7 +707,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; @@ -669,7 +721,7 @@ static int mirror_exit_common(Job *job) } s->prepared = true; - aio_context_acquire(qemu_get_aio_context()); + bdrv_graph_rdlock_main_loop(); mirror_top_bs = s->mirror_top_bs; bs_opaque = mirror_top_bs->opaque; @@ -688,6 +740,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 @@ -701,13 +755,18 @@ 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 *backing; BlockDriverState *unfiltered_target = bdrv_skip_filters(target_bs); + backing = s->sync_mode == MIRROR_SYNC_MODE_NONE ? src : s->base; if (bdrv_cow_bs(unfiltered_target) != backing) { bdrv_set_backing_hd(unfiltered_target, backing, &local_err); if (local_err) { @@ -725,11 +784,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; @@ -742,15 +797,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. - * - * TODO Pull out the writer lock from bdrv_replace_node() to here */ - bdrv_graph_rdlock_main_loop(); + bdrv_graph_wrlock(); if (bdrv_recurse_can_replace(src, to_replace)) { bdrv_replace_node(to_replace, target_bs, &local_err); } else { @@ -759,8 +812,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_graph_rdunlock_main_loop(); - bdrv_drained_end(target_bs); + bdrv_graph_wrunlock(); + bdrv_drained_end(to_replace); if (local_err) { error_report_err(local_err); ret = -EPERM; @@ -771,11 +824,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 @@ -783,7 +832,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; @@ -793,8 +851,6 @@ static int mirror_exit_common(Job *job) bdrv_unref(mirror_top_bs); bdrv_unref(src); - aio_context_release(qemu_get_aio_context()); - return ret; } @@ -821,16 +877,59 @@ 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 = -EIO; int64_t count; + bool punch_holes = + target_bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && + bdrv_can_write_zeroes_with_unmap(target_bs); + int64_t bitmap_length = DIV_ROUND_UP(s->bdev_length, s->granularity); - if (s->zero_target) { - if (!bdrv_can_write_zeroes_with_unmap(target_bs)) { + /* Determine if the image is already zero, regardless of sync mode. */ + s->zero_bitmap = bitmap_new(bitmap_length); + bdrv_graph_co_rdlock(); + bs = s->mirror_top_bs->backing->bs; + if (s->target_is_zero) { + ret = 1; + } else { + ret = bdrv_co_is_all_zeroes(target_bs); + } + bdrv_graph_co_rdunlock(); + + /* Determine if a pre-zeroing pass is necessary. */ + if (ret < 0) { + return ret; + } else if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { + /* + * In TOP mode, there is no benefit to a pre-zeroing pass, but + * the zero bitmap can be set if the destination already reads + * as zero and we are not punching holes. + */ + if (ret > 0 && !punch_holes) { + bitmap_set(s->zero_bitmap, 0, bitmap_length); + } + } else if (ret == 0 || punch_holes) { + /* + * Here, we are in FULL mode; our goal is to avoid writing + * zeroes if the destination already reads as zero, except + * when we are trying to punch holes. This is possible if + * zeroing happened externally (ret > 0) or if we have a fast + * way to pre-zero the image (the dirty bitmap will be + * populated later by the non-zero portions, the same as for + * TOP mode). If pre-zeroing is not fast, or we need to visit + * the entire image in order to punch holes even in the + * non-allocated regions of the source, then just mark the + * entire image dirty and leave the zero bitmap clear at this + * point in time. Otherwise, it can be faster to pre-zero the + * image now, even if we re-write the allocated portions of + * the disk later, and the pre-zero pass will populate the + * zero bitmap. + */ + if (!bdrv_can_write_zeroes_with_unmap(target_bs) || punch_holes) { bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, s->bdev_length); return 0; } @@ -839,6 +938,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) for (offset = 0; offset < s->bdev_length; ) { int bytes = MIN(s->bdev_length - offset, QEMU_ALIGN_DOWN(INT_MAX, s->granularity)); + bool ignored; mirror_throttle(s); @@ -854,12 +954,15 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) continue; } - mirror_perform(s, offset, bytes, MIRROR_METHOD_ZERO); + mirror_perform(s, offset, bytes, MIRROR_METHOD_ZERO, &ignored); offset += bytes; } mirror_wait_for_all_io(s); s->initial_zeroing_ongoing = false; + } else { + /* In FULL mode, and image already reads as zero. */ + bitmap_set(s->zero_bitmap, 0, bitmap_length); } /* First part, loop on the sectors and initialize the dirty bitmap. */ @@ -875,8 +978,8 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) } WITH_GRAPH_RDLOCK_GUARD() { - ret = bdrv_is_allocated_above(bs, s->base_overlay, true, offset, - bytes, &count); + ret = bdrv_co_is_allocated_above(bs, s->base_overlay, true, offset, + bytes, &count); } if (ret < 0) { return ret; @@ -908,11 +1011,11 @@ static int coroutine_fn 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; @@ -920,6 +1023,10 @@ 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; } @@ -958,7 +1065,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); } @@ -980,13 +1087,13 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) } else { s->target_cluster_size = BDRV_SECTOR_SIZE; } - bdrv_graph_co_rdunlock(); if (backing_filename[0] && !bdrv_backing_chain_next(target_bs) && s->granularity < s->target_cluster_size) { s->buf_size = MAX(s->buf_size, s->target_cluster_size); 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) { @@ -997,7 +1104,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) mirror_free_init(s); s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - if (!s->is_none_mode) { + if (s->sync_mode != MIRROR_SYNC_MODE_NONE) { ret = mirror_dirty_init(s); if (ret < 0 || job_is_cancelled(&s->common.job)) { goto immediate_exit; @@ -1070,9 +1177,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 || @@ -1140,6 +1247,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) assert(s->in_flight == 0); qemu_vfree(s->buf); g_free(s->cow_bitmap); + g_free(s->zero_bitmap); g_free(s->in_flight_bitmap); bdrv_dirty_iter_free(s->dbi); @@ -1163,24 +1271,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; @@ -1242,6 +1343,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), @@ -1256,6 +1399,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 = { @@ -1281,7 +1426,8 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, { int ret; size_t qiov_offset = 0; - int64_t bitmap_offset, bitmap_end; + int64_t dirty_bitmap_offset, dirty_bitmap_end; + int64_t zero_bitmap_offset, zero_bitmap_end; if (!QEMU_IS_ALIGNED(offset, job->granularity) && bdrv_dirty_bitmap_get(job->dirty_bitmap, offset)) @@ -1325,31 +1471,54 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, } /* - * Tails are either clean or shrunk, so for bitmap resetting - * we safely align the range down. + * Tails are either clean or shrunk, so for dirty bitmap resetting + * we safely align the range narrower. But for zero bitmap, round + * range wider for checking or clearing, and narrower for setting. */ - bitmap_offset = QEMU_ALIGN_UP(offset, job->granularity); - bitmap_end = QEMU_ALIGN_DOWN(offset + bytes, job->granularity); - if (bitmap_offset < bitmap_end) { - bdrv_reset_dirty_bitmap(job->dirty_bitmap, bitmap_offset, - bitmap_end - bitmap_offset); + dirty_bitmap_offset = QEMU_ALIGN_UP(offset, job->granularity); + dirty_bitmap_end = QEMU_ALIGN_DOWN(offset + bytes, job->granularity); + if (dirty_bitmap_offset < dirty_bitmap_end) { + bdrv_reset_dirty_bitmap(job->dirty_bitmap, dirty_bitmap_offset, + dirty_bitmap_end - dirty_bitmap_offset); } + zero_bitmap_offset = offset / job->granularity; + zero_bitmap_end = DIV_ROUND_UP(offset + bytes, job->granularity); job_progress_increase_remaining(&job->common.job, bytes); job->active_write_bytes_in_flight += bytes; switch (method) { case MIRROR_METHOD_COPY: + if (job->zero_bitmap) { + bitmap_clear(job->zero_bitmap, zero_bitmap_offset, + zero_bitmap_end - zero_bitmap_offset); + } ret = blk_co_pwritev_part(job->target, offset, bytes, qiov, qiov_offset, flags); break; case MIRROR_METHOD_ZERO: + if (job->zero_bitmap) { + if (find_next_zero_bit(job->zero_bitmap, zero_bitmap_end, + zero_bitmap_offset) == zero_bitmap_end) { + ret = 0; + break; + } + } assert(!qiov); ret = blk_co_pwrite_zeroes(job->target, offset, bytes, flags); + if (job->zero_bitmap && ret >= 0) { + bitmap_set(job->zero_bitmap, dirty_bitmap_offset / job->granularity, + (dirty_bitmap_end - dirty_bitmap_offset) / + job->granularity); + } break; case MIRROR_METHOD_DISCARD: + if (job->zero_bitmap) { + bitmap_clear(job->zero_bitmap, zero_bitmap_offset, + zero_bitmap_end - zero_bitmap_offset); + } assert(!qiov); ret = blk_co_pdiscard(job->target, offset, bytes); break; @@ -1370,11 +1539,11 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, * at function start, and they must be still dirty, as we've locked * the region for in-flight op. */ - bitmap_offset = QEMU_ALIGN_DOWN(offset, job->granularity); - 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; + dirty_bitmap_offset = QEMU_ALIGN_DOWN(offset, job->granularity); + dirty_bitmap_end = QEMU_ALIGN_UP(offset + bytes, job->granularity); + bdrv_set_dirty_bitmap(job->dirty_bitmap, dirty_bitmap_offset, + dirty_bitmap_end - dirty_bitmap_offset); + qatomic_set(&job->actively_synced, false); action = mirror_error_action(job, false, -ret); if (action == BLOCK_ERROR_ACTION_REPORT) { @@ -1433,7 +1602,8 @@ static void coroutine_fn GRAPH_RDLOCK active_write_settle(MirrorOp *op) 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 && @@ -1459,21 +1629,21 @@ bdrv_mirror_top_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, return bdrv_co_preadv(bs->backing, offset, bytes, qiov, 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, - uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, - int flags) + 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); @@ -1496,6 +1666,11 @@ bdrv_mirror_top_do_write(BlockDriverState *bs, MirrorMethod method, 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; } @@ -1515,17 +1690,10 @@ 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 @@ -1542,8 +1710,8 @@ bdrv_mirror_top_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, 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); @@ -1566,18 +1734,20 @@ 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 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 @@ -1650,17 +1820,19 @@ static BlockJob *mirror_start_job( int creation_flags, BlockDriverState *target, const char *replaces, int64_t speed, uint32_t granularity, int64_t buf_size, + MirrorSyncMode sync_mode, BlockMirrorBackingMode backing_mode, - bool zero_target, + bool target_is_zero, BlockdevOnError on_source_error, BlockdevOnError on_target_error, bool unmap, BlockCompletionFunc *cb, void *opaque, const BlockJobDriver *driver, - bool is_none_mode, BlockDriverState *base, + BlockDriverState *base, bool auto_complete, const char *filter_node_name, bool is_mirror, MirrorCopyMode copy_mode, + bool base_ro, Error **errp) { MirrorBlockJob *s; @@ -1670,6 +1842,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); } @@ -1685,12 +1859,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 @@ -1739,6 +1916,7 @@ static BlockJob *mirror_start_job( 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 @@ -1773,14 +1951,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, @@ -1801,13 +1984,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->sync_mode = sync_mode; s->backing_mode = backing_mode; - s->zero_target = zero_target; - s->copy_mode = copy_mode; + s->target_is_zero = target_is_zero; + qatomic_set(&s->copy_mode, copy_mode); s->base = base; s->base_overlay = bdrv_find_overlay(bs, base); s->granularity = granularity; @@ -1816,20 +2000,29 @@ 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_drain_all_begin(); + 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(); + bdrv_drain_all_end(); goto fail; } @@ -1874,14 +2067,20 @@ 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(); + bdrv_drain_all_end(); goto fail; } } if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) { + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); goto fail; } } + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); QTAILQ_INIT(&s->ops_in_flight); @@ -1906,9 +2105,14 @@ static BlockJob *mirror_start_job( } 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); @@ -1920,13 +2124,12 @@ void mirror_start(const char *job_id, BlockDriverState *bs, int creation_flags, int64_t speed, uint32_t granularity, int64_t buf_size, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, - bool zero_target, + bool target_is_zero, BlockdevOnError on_source_error, BlockdevOnError on_target_error, bool unmap, const char *filter_node_name, MirrorCopyMode copy_mode, Error **errp) { - bool is_none_mode; BlockDriverState *base; GLOBAL_STATE_CODE(); @@ -1937,13 +2140,16 @@ void mirror_start(const char *job_id, BlockDriverState *bs, MirrorSyncMode_str(mode)); return; } - is_none_mode = mode == MIRROR_SYNC_MODE_NONE; + + bdrv_graph_rdlock_main_loop(); 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); + speed, granularity, buf_size, mode, backing_mode, + target_is_zero, on_source_error, on_target_error, unmap, + NULL, NULL, &mirror_job_driver, base, false, + filter_node_name, true, copy_mode, false, errp); } BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, @@ -1968,11 +2174,11 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, job = mirror_start_job( job_id, bs, creation_flags, base, NULL, speed, 0, 0, - MIRROR_LEAVE_BACKING_CHAIN, false, + MIRROR_SYNC_MODE_TOP, MIRROR_LEAVE_BACKING_CHAIN, false, on_error, on_error, true, cb, opaque, - &commit_active_job_driver, false, base, auto_complete, + &commit_active_job_driver, 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 55f778f5af5..a738e7bbf7a 100644 --- a/block/monitor/bitmap-qmp-cmds.c +++ b/block/monitor/bitmap-qmp-cmds.c @@ -95,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"); @@ -107,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: */ @@ -132,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) { @@ -145,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, @@ -157,7 +150,6 @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, { BlockDriverState *bs; BdrvDirtyBitmap *bitmap; - AioContext *aio_context; GLOBAL_STATE_CODE(); @@ -166,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; } @@ -190,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; } @@ -258,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 ca2599de44e..6919a49bf50 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -37,11 +37,11 @@ #include "qemu/osdep.h" #include "hw/boards.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" +#include "system/block-backend.h" +#include "system/blockdev.h" #include "qapi/qapi-commands-block.h" #include "qapi/qapi-commands-block-export.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/config-file.h" @@ -49,7 +49,7 @@ #include "qemu/sockets.h" #include "qemu/cutils.h" #include "qemu/error-report.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "monitor/monitor.h" #include "monitor/hmp.h" #include "block/nbd.h" @@ -141,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); @@ -165,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; } @@ -193,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) @@ -203,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) { @@ -216,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)); @@ -409,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, NBD_DEFAULT_HANDSHAKE_MAX_SECS, NULL, NULL, + NBD_DEFAULT_MAX_CONNECTIONS, &local_err); qapi_free_SocketAddress(addr); if (local_err != NULL) { goto exit; @@ -503,7 +497,7 @@ 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(device, device, base, NULL, NULL, NULL, + 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); @@ -554,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"); @@ -576,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); @@ -616,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); } @@ -645,11 +630,12 @@ static void print_block_info(Monitor *mon, BlockInfo *info, } if (inserted) { - monitor_printf(mon, ": %s (%s%s%s)\n", + monitor_printf(mon, ": %s (%s%s%s%s)\n", inserted->file, inserted->drv, inserted->ro ? ", read-only" : "", - inserted->encrypted ? ", encrypted" : ""); + inserted->encrypted ? ", encrypted" : "", + inserted->active ? "" : ", inactive"); } else { monitor_printf(mon, ": [not inserted]\n"); } @@ -843,7 +829,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", @@ -855,7 +841,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, @@ -876,7 +862,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; @@ -896,16 +881,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); @@ -916,9 +900,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); @@ -936,7 +918,6 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) } g_free(sn); } - aio_context_release(ctx); } if (no_snapshot) { diff --git a/block/nbd.c b/block/nbd.c index cc48580df70..d5a2b21c6d1 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -31,13 +31,12 @@ #include "qemu/osdep.h" #include "trace.h" -#include "qemu/uri.h" #include "qemu/option.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" #include "qapi/qapi-visit-sockets.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "qapi/clone-visitor.h" #include "block/qdict.h" @@ -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; @@ -339,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); @@ -416,7 +416,8 @@ static void coroutine_fn GRAPH_RDLOCK nbd_reconnect_attempt(BDRVNBDState *s) reconnect_delay_timer_del(s); } -static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t cookie) +static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t cookie, + Error **errp) { int ret; uint64_t ind = COOKIE_TO_INDEX(cookie), ind2; @@ -457,19 +458,25 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t cookie) /* 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, NULL); - if (ret <= 0) { - ret = ret ? ret : -EIO; + 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 = 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.cookie == cookie) { @@ -519,6 +526,7 @@ nbd_co_send_request(BlockDriverState *bs, NBDRequest *request, qemu_co_mutex_lock(&s->send_mutex); 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; @@ -830,6 +851,7 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( BDRVNBDState *s, uint64_t cookie, bool only_structured, int *request_ret, QEMUIOVector *qiov, void **payload, Error **errp) { + ERRP_GUARD(); int ret; int i = COOKIE_TO_INDEX(cookie); void *local_payload = NULL; @@ -840,9 +862,9 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( } *request_ret = 0; - ret = nbd_receive_replies(s, cookie); + 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); @@ -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) { @@ -1070,7 +1092,8 @@ nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t cookie, void *payload = NULL; Error *local_err = NULL; - NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, s->info.structured_reply, + NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, + s->info.mode >= NBD_MODE_STRUCTURED, qiov, &reply, &payload) { int ret; @@ -1115,7 +1138,7 @@ nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t cookie, static int coroutine_fn nbd_co_receive_blockstatus_reply(BDRVNBDState *s, uint64_t cookie, - uint64_t length, NBDExtent *extent, + uint64_t length, NBDExtent64 *extent, int *request_ret, Error **errp) { NBDReplyChunkIter iter; @@ -1128,11 +1151,17 @@ nbd_co_receive_blockstatus_reply(BDRVNBDState *s, uint64_t cookie, 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"); @@ -1140,9 +1169,9 @@ nbd_co_receive_blockstatus_reply(BDRVNBDState *s, uint64_t cookie, } 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); @@ -1302,10 +1331,11 @@ nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, 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)) { @@ -1352,10 +1382,11 @@ nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) 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) { @@ -1366,19 +1397,18 @@ nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) } 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) + BlockDriverState *bs, unsigned int mode, 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, }; @@ -1388,6 +1418,10 @@ static int coroutine_fn GRAPH_RDLOCK 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 @@ -1463,7 +1497,7 @@ static void nbd_yank(void *opaque) 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); @@ -1479,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++; } @@ -1510,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) @@ -1952,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; @@ -2105,7 +2146,7 @@ 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, @@ -2133,7 +2174,7 @@ 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, @@ -2161,7 +2202,7 @@ 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, diff --git a/block/nfs.c b/block/nfs.c index c24df49747d..0a7d38db095 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -38,12 +38,11 @@ #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 "system/replay.h" #include "qapi/qapi-visit-block-core.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include @@ -79,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; - } - - qp = query_params_parse(uri->query); - if (!qp) { - error_setg(errp, "could not parse query parameters"); - goto out; + return -EINVAL; } - 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); - - for (i = 0; i < qp->n; i++) { - uint64_t val; - if (!qp->p[i].value) { - error_setg(errp, "Value for NFS parameter expected: %s", - qp->p[i].name); - goto out; - } - if (parse_uint_full(qp->p[i].value, 0, &val)) { - 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; + qdict_put_str(options, "path", uri_path); + + 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) @@ -843,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; @@ -890,7 +888,7 @@ static BlockDriver bdrv_nfs = { #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 4808704ffd3..4e448d593d7 100644 --- a/block/null.c +++ b/block/null.c @@ -12,13 +12,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qemu/module.h" #include "qemu/option.h" #include "block/block-io.h" #include "block/block_int.h" -#include "sysemu/replay.h" +#include "system/replay.h" #define NULL_OPT_LATENCY "latency-ns" #define NULL_OPT_ZEROES "read-zeroes" @@ -77,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; @@ -227,9 +227,9 @@ static int null_reopen_prepare(BDRVReopenState *reopen_state, } static int coroutine_fn null_co_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, - int64_t bytes, int64_t *pnum, - int64_t *map, + unsigned int mode, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, BlockDriverState **file) { BDRVNullState *s = bs->opaque; @@ -283,7 +283,7 @@ 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_co_getlength = null_co_getlength, .bdrv_co_get_allocated_file_size = null_co_get_allocated_file_size, @@ -304,7 +304,7 @@ 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_co_getlength = null_co_getlength, .bdrv_co_get_allocated_file_size = null_co_get_allocated_file_size, diff --git a/block/nvme.c b/block/nvme.c index b6e95f0b7e6..8df53ee4cac 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -14,9 +14,11 @@ #include "qemu/osdep.h" #include #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" +#include "qemu/defer-call.h" #include "qemu/error-report.h" +#include "qemu/host-pci-mmio.h" #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/cutils.h" @@ -25,8 +27,8 @@ #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 "system/block-backend.h" +#include "system/replay.h" #include "trace.h" #include "block/nvme.h" @@ -59,7 +61,7 @@ typedef struct { uint8_t *queue; uint64_t iova; /* Hardware MMIO register */ - volatile uint32_t *doorbell; + uint32_t *doorbell; } NVMeQueue; typedef struct { @@ -99,7 +101,7 @@ struct BDRVNVMeState { QEMUVFIOState *vfio; void *bar0_wo_map; /* Memory mapped registers */ - volatile struct { + struct { uint32_t sq_tail; uint32_t cq_head; } *doorbells; @@ -167,6 +169,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; @@ -220,6 +223,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; @@ -289,7 +293,7 @@ static void nvme_kick(NVMeQueuePair *q) assert(!(q->sq.tail & 0xFF00)); /* Fence the write to submission queue entry before notifying the device. */ smp_wmb(); - *q->sq.doorbell = cpu_to_le32(q->sq.tail); + host_pci_stl_le_p(q->sq.doorbell, q->sq.tail); q->inflight += q->need_kick; q->need_kick = 0; } @@ -416,9 +420,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); @@ -437,7 +442,7 @@ static bool nvme_process_completion(NVMeQueuePair *q) if (progress) { /* Notify the device so it can post more completions. */ smp_mb_release(); - *q->cq.doorbell = cpu_to_le32(q->cq.head); + host_pci_stl_le_p(q->cq.doorbell, q->cq.head); nvme_wake_free_req_locked(q); } @@ -456,7 +461,7 @@ static void nvme_process_completion_bh(void *opaque) * so notify the device that it has space to fill in more completions now. */ smp_mb_release(); - *q->cq.doorbell = cpu_to_le32(q->cq.head); + host_pci_stl_le_p(q->cq.doorbell, q->cq.head); nvme_wake_free_req_locked(q); nvme_process_completion(q); @@ -476,7 +481,7 @@ static void nvme_trace_command(const NvmeCmd *cmd) } } -static void nvme_unplug_fn(void *opaque) +static void nvme_deferred_fn(void *opaque) { NVMeQueuePair *q = opaque; @@ -503,7 +508,7 @@ static void nvme_submit_command(NVMeQueuePair *q, NVMeRequest *req, q->need_kick++; qemu_mutex_unlock(&q->lock); - blk_io_plug_call(nvme_unplug_fn, q); + defer_call(nvme_deferred_fn, q); } static void nvme_admin_cmd_sync_cb(void *opaque, int ret) @@ -533,6 +538,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 { @@ -744,9 +750,10 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, int ret; uint64_t cap; uint32_t ver; + uint32_t cc; uint64_t timeout_ms; uint64_t deadline, now; - volatile NvmeBar *regs = NULL; + NvmeBar *regs = NULL; qemu_co_mutex_init(&s->dma_map_lock); qemu_co_queue_init(&s->dma_flush_queue); @@ -774,7 +781,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, /* Perform initialize sequence as described in NVMe spec "7.6.1 * Initialization". */ - cap = le64_to_cpu(regs->cap); + cap = host_pci_ldq_le_p(®s->cap); trace_nvme_controller_capability_raw(cap); trace_nvme_controller_capability("Maximum Queue Entries Supported", 1 + NVME_CAP_MQES(cap)); @@ -800,16 +807,17 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, bs->bl.request_alignment = s->page_size; timeout_ms = MIN(500 * NVME_CAP_TO(cap), 30000); - ver = le32_to_cpu(regs->vs); + ver = host_pci_ldl_le_p(®s->vs); trace_nvme_controller_spec_version(extract32(ver, 16, 16), extract32(ver, 8, 8), extract32(ver, 0, 8)); /* Reset device to get a clean state. */ - regs->cc = cpu_to_le32(le32_to_cpu(regs->cc) & 0xFE); + cc = host_pci_ldl_le_p(®s->cc); + host_pci_stl_le_p(®s->cc, cc & 0xFE); /* Wait for CSTS.RDY = 0. */ deadline = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + timeout_ms * SCALE_MS; - while (NVME_CSTS_RDY(le32_to_cpu(regs->csts))) { + while (NVME_CSTS_RDY(host_pci_ldl_le_p(®s->csts))) { if (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) > deadline) { error_setg(errp, "Timeout while waiting for device to reset (%" PRId64 " ms)", @@ -838,19 +846,21 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, s->queues[INDEX_ADMIN] = q; s->queue_count = 1; QEMU_BUILD_BUG_ON((NVME_QUEUE_SIZE - 1) & 0xF000); - regs->aqa = cpu_to_le32(((NVME_QUEUE_SIZE - 1) << AQA_ACQS_SHIFT) | - ((NVME_QUEUE_SIZE - 1) << AQA_ASQS_SHIFT)); - regs->asq = cpu_to_le64(q->sq.iova); - regs->acq = cpu_to_le64(q->cq.iova); + host_pci_stl_le_p(®s->aqa, + ((NVME_QUEUE_SIZE - 1) << AQA_ACQS_SHIFT) | + ((NVME_QUEUE_SIZE - 1) << AQA_ASQS_SHIFT)); + host_pci_stq_le_p(®s->asq, q->sq.iova); + host_pci_stq_le_p(®s->acq, q->cq.iova); /* After setting up all control registers we can enable device now. */ - regs->cc = cpu_to_le32((ctz32(NVME_CQ_ENTRY_BYTES) << CC_IOCQES_SHIFT) | - (ctz32(NVME_SQ_ENTRY_BYTES) << CC_IOSQES_SHIFT) | - CC_EN_MASK); + host_pci_stl_le_p(®s->cc, + (ctz32(NVME_CQ_ENTRY_BYTES) << CC_IOCQES_SHIFT) | + (ctz32(NVME_SQ_ENTRY_BYTES) << CC_IOSQES_SHIFT) | + CC_EN_MASK); /* Wait for CSTS.RDY = 1. */ now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); deadline = now + timeout_ms * SCALE_MS; - while (!NVME_CSTS_RDY(le32_to_cpu(regs->csts))) { + while (!NVME_CSTS_RDY(host_pci_ldl_le_p(®s->csts))) { if (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) > deadline) { error_setg(errp, "Timeout while waiting for device to start (%" PRId64 " ms)", @@ -884,7 +894,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, 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; } @@ -962,8 +972,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; @@ -1625,7 +1635,7 @@ 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_co_getlength = nvme_co_getlength, .bdrv_probe_blocksizes = nvme_probe_blocksizes, diff --git a/block/parallels-ext.c b/block/parallels-ext.c index 8a109f005ae..778b8f684ef 100644 --- a/block/parallels-ext.c +++ b/block/parallels-ext.c @@ -59,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; @@ -120,17 +119,16 @@ static int parallels_load_bitmap_data(BlockDriverState *bs, * @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)) { @@ -183,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; @@ -207,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 48c32d68216..3a375e2a8ab 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -33,10 +33,10 @@ #include "qapi/error.h" #include "block/block_int.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" #include "qemu/bswap.h" @@ -178,13 +178,82 @@ static void parallels_set_bat_entry(BDRVParallelsState *s, 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) { @@ -207,21 +276,21 @@ allocate_clusters(BlockDriverState *bs, int64_t sector_num, */ assert(idx < s->bat_size && idx + to_allocate <= s->bat_size); - space = to_allocate * s->tracks; - len = bdrv_co_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) { @@ -229,13 +298,42 @@ allocate_clusters(BlockDriverState *bs, int64_t sector_num, } } 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; + } + } } /* @@ -267,9 +365,18 @@ allocate_clusters(BlockDriverState *bs, int64_t sector_num, } } + 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++) { - parallels_set_bat_entry(s, idx + i, s->data_end / s->off_multiplier); - s->data_end += s->tracks; + 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; @@ -308,14 +415,10 @@ 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, unsigned int mode, + int64_t offset, int64_t bytes, int64_t *pnum, + int64_t *map, BlockDriverState **file) { BDRVParallelsState *s = bs->opaque; int count; @@ -430,6 +533,64 @@ parallels_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, return ret; } + +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) @@ -515,7 +676,17 @@ parallels_check_data_off(BlockDriverState *bs, BdrvCheckResult *res, 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++; } @@ -621,7 +792,7 @@ parallels_check_duplicate(BlockDriverState *bs, BdrvCheckResult *res, BDRVParallelsState *s = bs->opaque; int64_t host_off, host_sector, guest_sector; unsigned long *bitmap; - uint32_t i, bitmap_size, cluster_index, bat_entry; + uint32_t i, bitmap_size, bat_entry; int n, ret = 0; uint64_t *buf = NULL; bool fixed = false; @@ -655,10 +826,9 @@ parallels_check_duplicate(BlockDriverState *bs, BdrvCheckResult *res, continue; } - cluster_index = host_cluster_index(s, host_off); - assert(cluster_index < bitmap_size); - if (!test_bit(cluster_index, bitmap)) { - bitmap_set(bitmap, cluster_index, 1); + ret = mark_used(bs, bitmap, bitmap_size, host_off, 1); + assert(ret != -E2BIG); + if (ret == 0) { continue; } @@ -713,11 +883,13 @@ parallels_check_duplicate(BlockDriverState *bs, BdrvCheckResult *res, * consistent for the new allocated clusters too. * * Note, clusters allocated outside the current image are not - * considered, and the bitmap size doesn't change. + * considered, and the bitmap size doesn't change. This specifically + * means that -E2BIG is OK. */ - cluster_index = host_cluster_index(s, host_off); - if (cluster_index < bitmap_size) { - bitmap_set(bitmap, cluster_index, 1); + ret = mark_used(bs, bitmap, bitmap_size, host_off, 1); + if (ret == -EBUSY) { + res->check_errors++; + goto out_repair_bat; } fixed = true; @@ -1013,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), @@ -1025,6 +1197,44 @@ 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) { @@ -1033,16 +1243,20 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, int ret, size, i; int64_t file_nb_sectors, sector; uint32_t data_start; - QemuOpts *opts = NULL; - Error *local_err = NULL; - char *buf; - bool data_off_is_correct; + 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; @@ -1050,7 +1264,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, 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); @@ -1070,29 +1284,30 @@ 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; + } + if (le64_to_cpu(ph.ext_off) >= (INT64_MAX >> BDRV_SECTOR_BITS)) { + error_setg(errp, "Invalid image: Too big offset"); + 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; + return -ENOMEM; } ret = bdrv_pread(bs->file, 0, s->header_size, s->header, 0); @@ -1102,11 +1317,14 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, s->bat_bitmap = (uint32_t *)(s->header + 1); if (le32_to_cpu(ph.inuse) == HEADER_INUSE_MAGIC) { - s->header_unclean = true; + need_check = s->header_unclean = true; + } + + { + bool ok = parallels_test_data_off(s, file_nb_sectors, &data_start); + need_check = need_check || !ok; } - data_off_is_correct = parallels_test_data_off(s, file_nb_sectors, - &data_start); s->data_start = data_start; s->data_end = s->data_start; if (s->data_end < (s->header_size >> BDRV_SECTOR_BITS)) { @@ -1117,29 +1335,6 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, s->header_size = size; } - opts = qemu_opts_create(¶llels_runtime_opts, NULL, 0, errp); - if (!opts) { - goto fail_options; - } - - 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; - } - if (ph.ext_off) { if (flags & BDRV_O_RDWR) { /* @@ -1173,9 +1368,9 @@ 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_setg(errp, "Migration blocker error"); goto fail; } qemu_co_mutex_init(&s->lock); @@ -1186,6 +1381,15 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, 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 @@ -1195,33 +1399,29 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, return 0; } - /* - * Repair the image if it's dirty or - * out-of-image corruption was detected. - */ - if (s->data_end > file_nb_sectors || s->header_unclean - || !data_off_is_correct) { + /* 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); + 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. */ - error_free(s->migration_blocker); + parallels_free_used_bitmap(bs); + g_free(s->bat_dirty_bmap); qemu_vfree(s->header); return ret; @@ -1232,6 +1432,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); @@ -1241,31 +1443,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 4e53e9572d2..423b2ad7271 100644 --- a/block/parallels.h +++ b/block/parallels.h @@ -72,6 +72,9 @@ 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; @@ -87,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/plug.c b/block/plug.c deleted file mode 100644 index 98a155d2f42..00000000000 --- a/block/plug.c +++ /dev/null @@ -1,159 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Block I/O plugging - * - * Copyright Red Hat. - * - * This API defers a function call within a blk_io_plug()/blk_io_unplug() - * 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: - * - * blk_io_plug(); <-- start of plugged region - * ... - * blk_io_plug_call(my_func, my_obj); <-- deferred my_func(my_obj) call - * blk_io_plug_call(my_func, my_obj); <-- another - * blk_io_plug_call(my_func, my_obj); <-- another - * ... - * blk_io_unplug(); <-- end of plugged region, my_func(my_obj) is called once - * - * This code is actually generic and not tied to the block layer. If another - * subsystem needs this functionality, it could be renamed. - */ - -#include "qemu/osdep.h" -#include "qemu/coroutine-tls.h" -#include "qemu/notify.h" -#include "qemu/thread.h" -#include "sysemu/block-backend.h" - -/* A function call that has been deferred until unplug() */ -typedef struct { - void (*fn)(void *); - void *opaque; -} UnplugFn; - -/* Per-thread state */ -typedef struct { - unsigned count; /* how many times has plug() been called? */ - GArray *unplug_fns; /* functions to call at unplug time */ -} Plug; - -/* Use get_ptr_plug() to fetch this thread-local value */ -QEMU_DEFINE_STATIC_CO_TLS(Plug, plug); - -/* Called at thread cleanup time */ -static void blk_io_plug_atexit(Notifier *n, void *value) -{ - Plug *plug = get_ptr_plug(); - g_array_free(plug->unplug_fns, TRUE); -} - -/* This won't involve coroutines, so use __thread */ -static __thread Notifier blk_io_plug_atexit_notifier; - -/** - * blk_io_plug_call: - * @fn: a function pointer to be invoked - * @opaque: a user-defined argument to @fn() - * - * Call @fn(@opaque) immediately if not within a blk_io_plug()/blk_io_unplug() - * section. - * - * Otherwise defer the call until the end of the outermost - * blk_io_plug()/blk_io_unplug() section in this thread. If the same - * @fn/@opaque pair has already been deferred, it will only be called once upon - * blk_io_unplug() so that accumulated calls are batched into a single call. - * - * The caller must ensure that @opaque is not freed before @fn() is invoked. - */ -void blk_io_plug_call(void (*fn)(void *), void *opaque) -{ - Plug *plug = get_ptr_plug(); - - /* Call immediately if we're not plugged */ - if (plug->count == 0) { - fn(opaque); - return; - } - - GArray *array = plug->unplug_fns; - if (!array) { - array = g_array_new(FALSE, FALSE, sizeof(UnplugFn)); - plug->unplug_fns = array; - blk_io_plug_atexit_notifier.notify = blk_io_plug_atexit; - qemu_thread_atexit_add(&blk_io_plug_atexit_notifier); - } - - UnplugFn *fns = (UnplugFn *)array->data; - UnplugFn 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); -} - -/** - * blk_io_plug: Defer blk_io_plug_call() functions until blk_io_unplug() - * - * blk_io_plug/unplug are thread-local operations. This means that multiple - * threads can simultaneously call plug/unplug, but the caller must ensure that - * each unplug() is called in the same thread of the matching plug(). - * - * Nesting is supported. blk_io_plug_call() functions are only called at the - * outermost blk_io_unplug(). - */ -void blk_io_plug(void) -{ - Plug *plug = get_ptr_plug(); - - assert(plug->count < UINT32_MAX); - - plug->count++; -} - -/** - * blk_io_unplug: Run any pending blk_io_plug_call() functions - * - * There must have been a matching blk_io_plug() call in the same thread prior - * to this blk_io_unplug() call. - */ -void blk_io_unplug(void) -{ - Plug *plug = get_ptr_plug(); - - assert(plug->count > 0); - - if (--plug->count > 0) { - return; - } - - GArray *array = plug->unplug_fns; - if (!array) { - return; - } - - UnplugFn *fns = (UnplugFn *)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/block/preallocate.c b/block/preallocate.c index 3d0f6210039..d215bc5d6d0 100644 --- a/block/preallocate.c +++ b/block/preallocate.c @@ -75,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 = { @@ -137,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; } @@ -162,26 +173,46 @@ static int preallocate_open(BlockDriverState *bs, QDict *options, int flags, return 0; } -static void preallocate_close(BlockDriverState *bs) +static int GRAPH_RDLOCK +preallocate_truncate_to_real_size(BlockDriverState *bs, Error **errp) { - int ret; BDRVPreallocateState *s = bs->opaque; - - if (s->data_end < 0) { - return; - } + int ret; if (s->file_end < 0) { s->file_end = bdrv_getlength(bs->file->bs); if (s->file_end < 0) { - return; + error_setg_errno(errp, -s->file_end, "Failed to get file length"); + return s->file_end; } } if (s->data_end < s->file_end) { ret = bdrv_truncate(bs->file, s->data_end, true, PREALLOC_MODE_OFF, 0, NULL); - s->file_end = ret < 0 ? ret : s->data_end; + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to drop preallocation"); + s->file_end = ret; + return ret; + } + s->file_end = s->data_end; + } + + return 0; +} + +static void preallocate_close(BlockDriverState *bs) +{ + BDRVPreallocateState *s = bs->opaque; + + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + qemu_bh_cancel(s->drop_resize_bh); + qemu_bh_delete(s->drop_resize_bh); + + if (s->data_end >= 0) { + preallocate_truncate_to_real_size(bs, NULL); } } @@ -198,6 +229,10 @@ static int preallocate_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp) { PreallocateOpts *opts = g_new0(PreallocateOpts, 1); + int ret; + + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!preallocate_absorb_opts(opts, reopen_state->options, reopen_state->bs->file->bs, errp)) { @@ -205,6 +240,19 @@ static int preallocate_reopen_prepare(BDRVReopenState *reopen_state, return -EINVAL; } + /* + * Drop the preallocation already here if reopening read-only. The child + * might also be reopened read-only and then scheduling a BH during the + * permission update is too late. + */ + if ((reopen_state->flags & BDRV_O_RDWR) == 0) { + ret = preallocate_drop_resize(reopen_state->bs, errp); + if (ret < 0) { + g_free(opts); + return ret; + } + } + reopen_state->opaque = opts; return 0; @@ -246,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; @@ -462,58 +510,63 @@ preallocate_co_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 < 0) { + return 0; + } - 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; - } + /* + * 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); } } @@ -521,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; /* @@ -554,7 +613,6 @@ static 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, diff --git a/block/qapi-sysemu.c b/block/qapi-sysemu.c deleted file mode 100644 index ef07151892a..00000000000 --- a/block/qapi-sysemu.c +++ /dev/null @@ -1,583 +0,0 @@ -/* - * QMP command handlers specific to the system emulators - * - * Copyright (c) 2003-2008 Fabrice Bellard - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * later. See the COPYING file in the top-level directory. - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright (c) 2003-2008 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" - -#include "block/block_int.h" -#include "qapi/error.h" -#include "qapi/qapi-commands-block.h" -#include "qapi/qmp/qdict.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" - -static BlockBackend *qmp_get_blk(const char *blk_name, const char *qdev_id, - Error **errp) -{ - BlockBackend *blk; - - if (!blk_name == !qdev_id) { - error_setg(errp, "Need exactly one of 'device' and 'id'"); - return NULL; - } - - if (qdev_id) { - blk = blk_by_qdev_id(qdev_id, errp); - } else { - blk = blk_by_name(blk_name); - if (blk == NULL) { - error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, - "Device '%s' not found", blk_name); - } - } - - return blk; -} - -/* - * Attempt to open the tray of @device. - * If @force, ignore its tray lock. - * Else, if the tray is locked, don't open it, but ask the guest to open it. - * On error, store an error through @errp and return -errno. - * If @device does not exist, return -ENODEV. - * If it has no removable media, return -ENOTSUP. - * If it has no tray, return -ENOSYS. - * If the guest was asked to open the tray, return -EINPROGRESS. - * Else, return 0. - */ -static int do_open_tray(const char *blk_name, const char *qdev_id, - bool force, Error **errp) -{ - BlockBackend *blk; - const char *device = qdev_id ?: blk_name; - bool locked; - - blk = qmp_get_blk(blk_name, qdev_id, errp); - if (!blk) { - return -ENODEV; - } - - if (!blk_dev_has_removable_media(blk)) { - error_setg(errp, "Device '%s' is not removable", device); - return -ENOTSUP; - } - - if (!blk_dev_has_tray(blk)) { - error_setg(errp, "Device '%s' does not have a tray", device); - return -ENOSYS; - } - - if (blk_dev_is_tray_open(blk)) { - return 0; - } - - locked = blk_dev_is_medium_locked(blk); - if (locked) { - blk_dev_eject_request(blk, force); - } - - if (!locked || force) { - blk_dev_change_media_cb(blk, false, &error_abort); - } - - if (locked && !force) { - error_setg(errp, "Device '%s' is locked and force was not specified, " - "wait for tray to open and try again", device); - return -EINPROGRESS; - } - - return 0; -} - -void qmp_blockdev_open_tray(const char *device, - const char *id, - bool has_force, bool force, - Error **errp) -{ - Error *local_err = NULL; - int rc; - - if (!has_force) { - force = false; - } - rc = do_open_tray(device, id, force, &local_err); - if (rc && rc != -ENOSYS && rc != -EINPROGRESS) { - error_propagate(errp, local_err); - return; - } - error_free(local_err); -} - -void qmp_blockdev_close_tray(const char *device, - const char *id, - Error **errp) -{ - BlockBackend *blk; - Error *local_err = NULL; - - blk = qmp_get_blk(device, id, errp); - if (!blk) { - return; - } - - if (!blk_dev_has_removable_media(blk)) { - error_setg(errp, "Device '%s' is not removable", device ?: id); - return; - } - - if (!blk_dev_has_tray(blk)) { - /* Ignore this command on tray-less devices */ - return; - } - - if (!blk_dev_is_tray_open(blk)) { - return; - } - - blk_dev_change_media_cb(blk, true, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } -} - -static void blockdev_remove_medium(const char *device, const char *id, - Error **errp) -{ - BlockBackend *blk; - BlockDriverState *bs; - AioContext *aio_context; - bool has_attached_device; - - blk = qmp_get_blk(device, id, errp); - if (!blk) { - return; - } - - /* For BBs without a device, we can exchange the BDS tree at will */ - has_attached_device = blk_get_attached_dev(blk); - - if (has_attached_device && !blk_dev_has_removable_media(blk)) { - error_setg(errp, "Device '%s' is not removable", device ?: id); - return; - } - - if (has_attached_device && blk_dev_has_tray(blk) && - !blk_dev_is_tray_open(blk)) - { - error_setg(errp, "Tray of device '%s' is not open", device ?: id); - return; - } - - bs = blk_bs(blk); - if (!bs) { - return; - } - - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) { - goto out; - } - - blk_remove_bs(blk); - - if (!blk_dev_has_tray(blk)) { - /* For tray-less devices, blockdev-open-tray is a no-op (or may not be - * called at all); therefore, the medium needs to be ejected here. - * Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load - * value passed here (i.e. false). */ - blk_dev_change_media_cb(blk, false, &error_abort); - } - -out: - aio_context_release(aio_context); -} - -void qmp_blockdev_remove_medium(const char *id, Error **errp) -{ - blockdev_remove_medium(NULL, id, errp); -} - -static void qmp_blockdev_insert_anon_medium(BlockBackend *blk, - BlockDriverState *bs, Error **errp) -{ - Error *local_err = NULL; - bool has_device; - int ret; - - /* For BBs without a device, we can exchange the BDS tree at will */ - has_device = blk_get_attached_dev(blk); - - if (has_device && !blk_dev_has_removable_media(blk)) { - error_setg(errp, "Device is not removable"); - return; - } - - if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) { - error_setg(errp, "Tray of the device is not open"); - return; - } - - if (blk_bs(blk)) { - error_setg(errp, "There already is a medium in the device"); - return; - } - - ret = blk_insert_bs(blk, bs, errp); - if (ret < 0) { - return; - } - - if (!blk_dev_has_tray(blk)) { - /* For tray-less devices, blockdev-close-tray is a no-op (or may not be - * called at all); therefore, the medium needs to be pushed into the - * slot here. - * Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load - * value passed here (i.e. true). */ - blk_dev_change_media_cb(blk, true, &local_err); - if (local_err) { - error_propagate(errp, local_err); - blk_remove_bs(blk); - return; - } - } -} - -static void blockdev_insert_medium(const char *device, const char *id, - const char *node_name, Error **errp) -{ - BlockBackend *blk; - BlockDriverState *bs; - - blk = qmp_get_blk(device, id, errp); - if (!blk) { - return; - } - - bs = bdrv_find_node(node_name); - if (!bs) { - error_setg(errp, "Node '%s' not found", node_name); - return; - } - - if (bdrv_has_blk(bs)) { - error_setg(errp, "Node '%s' is already in use", node_name); - return; - } - - qmp_blockdev_insert_anon_medium(blk, bs, errp); -} - -void qmp_blockdev_insert_medium(const char *id, const char *node_name, - Error **errp) -{ - blockdev_insert_medium(NULL, id, node_name, errp); -} - -void qmp_blockdev_change_medium(const char *device, - const char *id, - const char *filename, - const char *format, - bool has_force, bool force, - bool has_read_only, - BlockdevChangeReadOnlyMode read_only, - Error **errp) -{ - BlockBackend *blk; - BlockDriverState *medium_bs = NULL; - int bdrv_flags; - bool detect_zeroes; - int rc; - QDict *options = NULL; - Error *err = NULL; - - blk = qmp_get_blk(device, id, errp); - if (!blk) { - goto fail; - } - - if (blk_bs(blk)) { - blk_update_root_state(blk); - } - - bdrv_flags = blk_get_open_flags_from_root_state(blk); - bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | - BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY); - - if (!has_read_only) { - read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN; - } - - switch (read_only) { - case BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN: - break; - - case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY: - bdrv_flags &= ~BDRV_O_RDWR; - break; - - case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE: - bdrv_flags |= BDRV_O_RDWR; - break; - - default: - abort(); - } - - options = qdict_new(); - detect_zeroes = blk_get_detect_zeroes_from_root_state(blk); - qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off"); - - if (format) { - qdict_put_str(options, "driver", format); - } - - aio_context_acquire(qemu_get_aio_context()); - medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp); - aio_context_release(qemu_get_aio_context()); - - if (!medium_bs) { - goto fail; - } - - rc = do_open_tray(device, id, force, &err); - if (rc && rc != -ENOSYS) { - error_propagate(errp, err); - goto fail; - } - error_free(err); - err = NULL; - - blockdev_remove_medium(device, id, &err); - if (err) { - error_propagate(errp, err); - goto fail; - } - - qmp_blockdev_insert_anon_medium(blk, medium_bs, &err); - if (err) { - error_propagate(errp, err); - goto fail; - } - - qmp_blockdev_close_tray(device, id, errp); - -fail: - /* If the medium has been inserted, the device has its own reference, so - * ours must be relinquished; and if it has not been inserted successfully, - * the reference must be relinquished anyway */ - bdrv_unref(medium_bs); -} - -void qmp_eject(const char *device, const char *id, - bool has_force, bool force, Error **errp) -{ - Error *local_err = NULL; - int rc; - - if (!has_force) { - force = false; - } - - rc = do_open_tray(device, id, force, &local_err); - if (rc && rc != -ENOSYS) { - error_propagate(errp, local_err); - return; - } - error_free(local_err); - - blockdev_remove_medium(device, id, errp); -} - -/* throttling disk I/O limits */ -void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp) -{ - ThrottleConfig cfg; - BlockDriverState *bs; - BlockBackend *blk; - AioContext *aio_context; - - blk = qmp_get_blk(arg->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; - } - - throttle_config_init(&cfg); - cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps; - cfg.buckets[THROTTLE_BPS_READ].avg = arg->bps_rd; - cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr; - - cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops; - cfg.buckets[THROTTLE_OPS_READ].avg = arg->iops_rd; - cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr; - - if (arg->has_bps_max) { - cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max; - } - if (arg->has_bps_rd_max) { - cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max; - } - if (arg->has_bps_wr_max) { - cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max; - } - if (arg->has_iops_max) { - cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max; - } - if (arg->has_iops_rd_max) { - cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max; - } - if (arg->has_iops_wr_max) { - cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max; - } - - if (arg->has_bps_max_length) { - cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length; - } - if (arg->has_bps_rd_max_length) { - cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length; - } - if (arg->has_bps_wr_max_length) { - cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length; - } - if (arg->has_iops_max_length) { - cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length; - } - if (arg->has_iops_rd_max_length) { - cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length; - } - if (arg->has_iops_wr_max_length) { - cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length; - } - - if (arg->has_iops_size) { - cfg.op_size = arg->iops_size; - } - - if (!throttle_is_valid(&cfg, errp)) { - goto out; - } - - if (throttle_enabled(&cfg)) { - /* Enable I/O limits if they're not enabled yet, otherwise - * just update the throttling group. */ - if (!blk_get_public(blk)->throttle_group_member.throttle_state) { - blk_io_limits_enable(blk, arg->group ?: arg->device ?: arg->id); - } else if (arg->group) { - blk_io_limits_update_group(blk, arg->group); - } - /* Set the new throttling configuration */ - blk_set_io_limits(blk, &cfg); - } else if (blk_get_public(blk)->throttle_group_member.throttle_state) { - /* If all throttling settings are set to 0, disable I/O limits */ - blk_io_limits_disable(blk); - } - -out: - aio_context_release(aio_context); -} - -void qmp_block_latency_histogram_set( - const char *id, - bool has_boundaries, uint64List *boundaries, - bool has_boundaries_read, uint64List *boundaries_read, - bool has_boundaries_write, uint64List *boundaries_write, - bool has_boundaries_append, uint64List *boundaries_append, - bool has_boundaries_flush, uint64List *boundaries_flush, - Error **errp) -{ - BlockBackend *blk = qmp_get_blk(NULL, id, errp); - BlockAcctStats *stats; - int ret; - - if (!blk) { - return; - } - - stats = blk_get_stats(blk); - - if (!has_boundaries && !has_boundaries_read && !has_boundaries_write && - !has_boundaries_flush) - { - block_latency_histograms_clear(stats); - return; - } - - if (has_boundaries || has_boundaries_read) { - ret = block_latency_histogram_set( - stats, BLOCK_ACCT_READ, - has_boundaries_read ? boundaries_read : boundaries); - if (ret) { - error_setg(errp, "Device '%s' set read boundaries fail", id); - return; - } - } - - if (has_boundaries || has_boundaries_write) { - ret = block_latency_histogram_set( - stats, BLOCK_ACCT_WRITE, - has_boundaries_write ? boundaries_write : boundaries); - if (ret) { - error_setg(errp, "Device '%s' set write boundaries fail", id); - return; - } - } - - if (has_boundaries || has_boundaries_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, - has_boundaries_flush ? boundaries_flush : boundaries); - if (ret) { - error_setg(errp, "Device '%s' set flush boundaries fail", id); - return; - } - } -} diff --git a/block/qapi-system.c b/block/qapi-system.c new file mode 100644 index 00000000000..54b7409b2b9 --- /dev/null +++ b/block/qapi-system.c @@ -0,0 +1,574 @@ +/* + * QMP command handlers specific to the system emulators + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + +#include "block/block_int.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-block.h" +#include "qobject/qdict.h" +#include "system/block-backend.h" +#include "system/blockdev.h" + +static BlockBackend *qmp_get_blk(const char *blk_name, const char *qdev_id, + Error **errp) +{ + BlockBackend *blk; + + if (!blk_name == !qdev_id) { + error_setg(errp, "Need exactly one of 'device' and 'id'"); + return NULL; + } + + if (qdev_id) { + blk = blk_by_qdev_id(qdev_id, errp); + } else { + blk = blk_by_name(blk_name); + if (blk == NULL) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", blk_name); + } + } + + return blk; +} + +/* + * Attempt to open the tray of @device. + * If @force, ignore its tray lock. + * Else, if the tray is locked, don't open it, but ask the guest to open it. + * On error, store an error through @errp and return -errno. + * If @device does not exist, return -ENODEV. + * If it has no removable media, return -ENOTSUP. + * If it has no tray, return -ENOSYS. + * If the guest was asked to open the tray, return -EINPROGRESS. + * Else, return 0. + */ +static int do_open_tray(const char *blk_name, const char *qdev_id, + bool force, Error **errp) +{ + BlockBackend *blk; + const char *device = qdev_id ?: blk_name; + bool locked; + + blk = qmp_get_blk(blk_name, qdev_id, errp); + if (!blk) { + return -ENODEV; + } + + if (!blk_dev_has_removable_media(blk)) { + error_setg(errp, "Device '%s' is not removable", device); + return -ENOTSUP; + } + + if (!blk_dev_has_tray(blk)) { + error_setg(errp, "Device '%s' does not have a tray", device); + return -ENOSYS; + } + + if (blk_dev_is_tray_open(blk)) { + return 0; + } + + locked = blk_dev_is_medium_locked(blk); + if (locked) { + blk_dev_eject_request(blk, force); + } + + if (!locked || force) { + blk_dev_change_media_cb(blk, false, &error_abort); + } + + if (locked && !force) { + error_setg(errp, "Device '%s' is locked and force was not specified, " + "wait for tray to open and try again", device); + return -EINPROGRESS; + } + + return 0; +} + +void qmp_blockdev_open_tray(const char *device, + const char *id, + bool has_force, bool force, + Error **errp) +{ + Error *local_err = NULL; + int rc; + + if (!has_force) { + force = false; + } + rc = do_open_tray(device, id, force, &local_err); + if (rc && rc != -ENOSYS && rc != -EINPROGRESS) { + error_propagate(errp, local_err); + return; + } + error_free(local_err); +} + +void qmp_blockdev_close_tray(const char *device, + const char *id, + Error **errp) +{ + BlockBackend *blk; + Error *local_err = NULL; + + blk = qmp_get_blk(device, id, errp); + if (!blk) { + return; + } + + if (!blk_dev_has_removable_media(blk)) { + error_setg(errp, "Device '%s' is not removable", device ?: id); + return; + } + + if (!blk_dev_has_tray(blk)) { + /* Ignore this command on tray-less devices */ + return; + } + + if (!blk_dev_is_tray_open(blk)) { + return; + } + + blk_dev_change_media_cb(blk, true, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } +} + +static void GRAPH_UNLOCKED +blockdev_remove_medium(const char *device, const char *id, Error **errp) +{ + BlockBackend *blk; + BlockDriverState *bs; + bool has_attached_device; + + GLOBAL_STATE_CODE(); + + blk = qmp_get_blk(device, id, errp); + if (!blk) { + return; + } + + /* For BBs without a device, we can exchange the BDS tree at will */ + has_attached_device = blk_get_attached_dev(blk); + + if (has_attached_device && !blk_dev_has_removable_media(blk)) { + error_setg(errp, "Device '%s' is not removable", device ?: id); + return; + } + + if (has_attached_device && blk_dev_has_tray(blk) && + !blk_dev_is_tray_open(blk)) + { + error_setg(errp, "Tray of device '%s' is not open", device ?: id); + return; + } + + bs = blk_bs(blk); + if (!bs) { + return; + } + + bdrv_graph_rdlock_main_loop(); + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) { + bdrv_graph_rdunlock_main_loop(); + return; + } + bdrv_graph_rdunlock_main_loop(); + + blk_remove_bs(blk); + + if (!blk_dev_has_tray(blk)) { + /* For tray-less devices, blockdev-open-tray is a no-op (or may not be + * called at all); therefore, the medium needs to be ejected here. + * Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load + * value passed here (i.e. false). */ + blk_dev_change_media_cb(blk, false, &error_abort); + } +} + +void qmp_blockdev_remove_medium(const char *id, Error **errp) +{ + blockdev_remove_medium(NULL, id, errp); +} + +static void qmp_blockdev_insert_anon_medium(BlockBackend *blk, + BlockDriverState *bs, Error **errp) +{ + Error *local_err = NULL; + bool has_device; + int ret; + + /* For BBs without a device, we can exchange the BDS tree at will */ + has_device = blk_get_attached_dev(blk); + + if (has_device && !blk_dev_has_removable_media(blk)) { + error_setg(errp, "Device is not removable"); + return; + } + + if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) { + error_setg(errp, "Tray of the device is not open"); + return; + } + + if (blk_bs(blk)) { + error_setg(errp, "There already is a medium in the device"); + return; + } + + ret = blk_insert_bs(blk, bs, errp); + if (ret < 0) { + return; + } + + if (!blk_dev_has_tray(blk)) { + /* For tray-less devices, blockdev-close-tray is a no-op (or may not be + * called at all); therefore, the medium needs to be pushed into the + * slot here. + * Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load + * value passed here (i.e. true). */ + blk_dev_change_media_cb(blk, true, &local_err); + if (local_err) { + error_propagate(errp, local_err); + blk_remove_bs(blk); + return; + } + } +} + +static void blockdev_insert_medium(const char *device, const char *id, + const char *node_name, Error **errp) +{ + BlockBackend *blk; + BlockDriverState *bs; + + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + blk = qmp_get_blk(device, id, errp); + if (!blk) { + return; + } + + bs = bdrv_find_node(node_name); + if (!bs) { + error_setg(errp, "Node '%s' not found", node_name); + return; + } + + if (bdrv_has_blk(bs)) { + error_setg(errp, "Node '%s' is already in use", node_name); + return; + } + + qmp_blockdev_insert_anon_medium(blk, bs, errp); +} + +void qmp_blockdev_insert_medium(const char *id, const char *node_name, + Error **errp) +{ + blockdev_insert_medium(NULL, id, node_name, errp); +} + +void qmp_blockdev_change_medium(const char *device, + const char *id, + const char *filename, + const char *format, + bool has_force, bool force, + bool has_read_only, + BlockdevChangeReadOnlyMode read_only, + Error **errp) +{ + BlockBackend *blk; + BlockDriverState *medium_bs = NULL; + int bdrv_flags; + bool detect_zeroes; + int rc; + QDict *options = NULL; + Error *err = NULL; + + blk = qmp_get_blk(device, id, errp); + if (!blk) { + goto fail; + } + + if (blk_bs(blk)) { + blk_update_root_state(blk); + } + + bdrv_flags = blk_get_open_flags_from_root_state(blk); + bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | + BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY); + + if (!has_read_only) { + read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN; + } + + switch (read_only) { + case BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN: + break; + + case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY: + bdrv_flags &= ~BDRV_O_RDWR; + break; + + case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE: + bdrv_flags |= BDRV_O_RDWR; + break; + + default: + abort(); + } + + options = qdict_new(); + detect_zeroes = blk_get_detect_zeroes_from_root_state(blk); + qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off"); + + if (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(device, id, force, &err); + if (rc && rc != -ENOSYS) { + error_propagate(errp, err); + goto fail; + } + error_free(err); + err = NULL; + + blockdev_remove_medium(device, id, &err); + if (err) { + error_propagate(errp, err); + goto fail; + } + + qmp_blockdev_insert_anon_medium(blk, medium_bs, &err); + if (err) { + error_propagate(errp, err); + goto fail; + } + + qmp_blockdev_close_tray(device, id, errp); + +fail: + /* If the medium has been inserted, the device has its own reference, so + * ours must be relinquished; and if it has not been inserted successfully, + * the reference must be relinquished anyway */ + bdrv_unref(medium_bs); +} + +void qmp_eject(const char *device, const char *id, + bool has_force, bool force, Error **errp) +{ + Error *local_err = NULL; + int rc; + + if (!has_force) { + force = false; + } + + rc = do_open_tray(device, id, force, &local_err); + if (rc && rc != -ENOSYS) { + error_propagate(errp, local_err); + return; + } + error_free(local_err); + + blockdev_remove_medium(device, id, errp); +} + +/* throttling disk I/O limits */ +void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp) +{ + ThrottleConfig cfg; + BlockDriverState *bs; + BlockBackend *blk; + + blk = qmp_get_blk(arg->device, arg->id, errp); + if (!blk) { + return; + } + + bs = blk_bs(blk); + if (!bs) { + error_setg(errp, "Device has no medium"); + return; + } + + throttle_config_init(&cfg); + cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps; + cfg.buckets[THROTTLE_BPS_READ].avg = arg->bps_rd; + cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr; + + cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops; + cfg.buckets[THROTTLE_OPS_READ].avg = arg->iops_rd; + cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr; + + if (arg->has_bps_max) { + cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max; + } + if (arg->has_bps_rd_max) { + cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max; + } + if (arg->has_bps_wr_max) { + cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max; + } + if (arg->has_iops_max) { + cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max; + } + if (arg->has_iops_rd_max) { + cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max; + } + if (arg->has_iops_wr_max) { + cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max; + } + + if (arg->has_bps_max_length) { + cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length; + } + if (arg->has_bps_rd_max_length) { + cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length; + } + if (arg->has_bps_wr_max_length) { + cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length; + } + if (arg->has_iops_max_length) { + cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length; + } + if (arg->has_iops_rd_max_length) { + cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length; + } + if (arg->has_iops_wr_max_length) { + cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length; + } + + if (arg->has_iops_size) { + cfg.op_size = arg->iops_size; + } + + if (!throttle_is_valid(&cfg, errp)) { + 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->group ?: arg->device ?: arg->id); + } else if (arg->group) { + blk_io_limits_update_group(blk, arg->group); + } + /* Set the new throttling configuration */ + blk_set_io_limits(blk, &cfg); + } else if (blk_get_public(blk)->throttle_group_member.throttle_state) { + /* If all throttling settings are set to 0, disable I/O limits */ + blk_io_limits_disable(blk); + } +} + +void qmp_block_latency_histogram_set( + const char *id, + bool has_boundaries, uint64List *boundaries, + bool has_boundaries_read, uint64List *boundaries_read, + bool has_boundaries_write, uint64List *boundaries_write, + bool has_boundaries_append, uint64List *boundaries_append, + bool has_boundaries_flush, uint64List *boundaries_flush, + Error **errp) +{ + BlockBackend *blk = qmp_get_blk(NULL, id, errp); + BlockAcctStats *stats; + int ret; + + if (!blk) { + return; + } + + stats = blk_get_stats(blk); + + if (!has_boundaries && !has_boundaries_read && !has_boundaries_write && + !has_boundaries_flush) + { + block_latency_histograms_clear(stats); + return; + } + + if (has_boundaries || has_boundaries_read) { + ret = block_latency_histogram_set( + stats, BLOCK_ACCT_READ, + has_boundaries_read ? boundaries_read : boundaries); + if (ret) { + error_setg(errp, "Device '%s' set read boundaries fail", id); + return; + } + } + + if (has_boundaries || has_boundaries_write) { + ret = block_latency_histogram_set( + stats, BLOCK_ACCT_WRITE, + has_boundaries_write ? boundaries_write : boundaries); + if (ret) { + error_setg(errp, "Device '%s' set write boundaries fail", id); + return; + } + } + + if (has_boundaries || has_boundaries_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, + has_boundaries_flush ? boundaries_flush : boundaries); + if (ret) { + error_setg(errp, "Device '%s' set flush boundaries fail", id); + return; + } + } +} diff --git a/block/qapi.c b/block/qapi.c index 1cbb0935ff0..2c50a6bf3b8 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -33,24 +33,24 @@ #include "qapi/qapi-commands-block-core.h" #include "qapi/qobject-output-visitor.h" #include "qapi/qapi-visit-block-core.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qemu/qemu-print.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, BlockDriverState *bs, bool flat, Error **errp) { + ERRP_GUARD(); ImageInfo **p_image_info; ImageInfo *backing_info; BlockDriverState *backing; BlockDeviceInfo *info; - ERRP_GUARD(); if (!bs->drv) { error_setg(errp, "Block device %s is ejected", bs->node_name); @@ -63,6 +63,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, info->file = g_strdup(bs->filename); info->ro = bdrv_is_read_only(bs); info->drv = g_strdup(bs->drv->format_name); + info->active = !bdrv_is_inactive(bs); info->encrypted = bs->encrypted; info->cache = g_new(BlockdevCacheInfo, 1); @@ -225,9 +226,8 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs, * Helper function for other query info functions. Store information about @bs * in @info, setting @errp on error. */ -static void bdrv_do_query_node_info(BlockDriverState *bs, - BlockNodeInfo *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; @@ -235,13 +235,11 @@ static void bdrv_do_query_node_info(BlockDriverState *bs, int ret; Error *err = NULL; - 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); @@ -266,7 +264,7 @@ static void bdrv_do_query_node_info(BlockDriverState *bs, info->format_specific = bdrv_get_specific_info(bs, &err); if (err) { error_propagate(errp, err); - goto out; + return; } backing_filename = bs->backing_file; if (backing_filename[0] != '\0') { @@ -301,11 +299,8 @@ static void bdrv_do_query_node_info(BlockDriverState *bs, break; default: error_propagate(errp, err); - goto out; + return; } - -out: - aio_context_release(bdrv_get_aio_context(bs)); } /** @@ -336,8 +331,8 @@ void bdrv_query_image_info(BlockDriverState *bs, bool skip_implicit_filters, Error **errp) { - ImageInfo *info; ERRP_GUARD(); + ImageInfo *info; info = g_new0(ImageInfo, 1); bdrv_do_query_node_info(bs, qapi_ImageInfo_base(info), errp); @@ -388,10 +383,10 @@ void bdrv_query_block_graph_info(BlockDriverState *bs, BlockGraphInfo **p_info, Error **errp) { + ERRP_GUARD(); BlockGraphInfo *info; BlockChildInfoList **children_list_tail; BdrvChild *c; - ERRP_GUARD(); info = g_new0(BlockGraphInfo, 1); bdrv_do_query_node_info(bs, qapi_BlockGraphInfo_base(info), errp); @@ -423,8 +418,8 @@ void bdrv_query_block_graph_info(BlockDriverState *bs, } /* @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); @@ -672,6 +667,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; @@ -708,15 +705,10 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, /* 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; @@ -724,7 +716,6 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, continue; } - aio_context_acquire(ctx); s = bdrv_query_bds_stats(blk_bs(blk), true); s->device = g_strdup(blk_name(blk)); @@ -736,7 +727,6 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, } bdrv_query_blk_stats(s->stats, blk); - aio_context_release(ctx); QAPI_LIST_APPEND(tail, s); } @@ -753,15 +743,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), @@ -770,8 +760,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, diff --git a/block/qcow.c b/block/qcow.c index 577bd70324d..8a3e7591a92 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -27,15 +27,15 @@ #include "qemu/error-report.h" #include "block/block_int.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/bswap.h" #include "qemu/cutils.h" #include "qemu/memalign.h" #include -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" #include "crypto/block.h" @@ -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); @@ -525,7 +530,7 @@ get_cluster_offset(BlockDriverState *bs, uint64_t offset, int allocate, } static int coroutine_fn GRAPH_RDLOCK -qcow_co_block_status(BlockDriverState *bs, bool want_zero, +qcow_co_block_status(BlockDriverState *bs, unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { @@ -549,7 +554,10 @@ qcow_co_block_status(BlockDriverState *bs, bool want_zero, 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; @@ -796,8 +804,7 @@ 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 GRAPH_UNLOCKED @@ -824,7 +831,7 @@ qcow_co_create(BlockdevCreateOptions *opts, Error **errp) } if (qcow_opts->encrypt && - qcow_opts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_QCOW) + qcow_opts->encrypt->format != QCRYPTO_BLOCK_FORMAT_QCOW) { error_setg(errp, "Unsupported encryption format"); return -EINVAL; @@ -878,7 +885,7 @@ qcow_co_create(BlockdevCreateOptions *opts, Error **errp) 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; @@ -1020,7 +1027,7 @@ qcow_co_create_opts(BlockDriver *drv, const char *filename, 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); diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 037fa2d4351..256ec998788 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -105,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; @@ -156,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); @@ -204,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; @@ -221,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; @@ -259,7 +260,8 @@ static int bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb, 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; @@ -550,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; @@ -730,8 +733,9 @@ qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, * 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; @@ -829,8 +833,9 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list, * 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; @@ -877,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; @@ -958,7 +963,7 @@ 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 GRAPH_RDLOCK +bool coroutine_fn qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated, Error **errp) { @@ -1271,9 +1276,9 @@ int coroutine_fn qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp /* 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; @@ -1370,7 +1375,8 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, * 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; @@ -1555,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)) { @@ -1604,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); @@ -1625,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; @@ -1705,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 01c67bdddc1..23d9588b08e 100644 --- a/block/qcow2-cache.c +++ b/block/qcow2-cache.c @@ -163,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; @@ -178,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; @@ -318,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 f4f6cd6ad02..ce8c0076b3b 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -207,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) * @@ -269,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; @@ -390,11 +391,10 @@ static int l2_allocate(BlockDriverState *bs, int l1_index) * 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; @@ -441,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; @@ -751,9 +752,9 @@ int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset, * * 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; @@ -1155,11 +1156,10 @@ void coroutine_fn qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m) * * Returns 0 on success, -errno on failure. */ -static int coroutine_fn 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); @@ -1329,7 +1329,8 @@ static int coroutine_fn calculate_l2_meta(BlockDriverState *bs, * 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: @@ -1360,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); @@ -1490,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; @@ -1600,10 +1601,9 @@ static int coroutine_fn handle_copied(BlockDriverState *bs, * function has been waiting for another request and the allocation must be * restarted, but the whole request should not be failed. */ -static int coroutine_fn 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; @@ -1658,9 +1658,9 @@ static int coroutine_fn do_alloc_cluster_offset(BlockDriverState *bs, * * -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; @@ -1898,9 +1898,9 @@ int coroutine_fn qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, * 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; @@ -1984,7 +1984,7 @@ static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, /* 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); @@ -2037,7 +2037,7 @@ int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, * all clusters in the same L2 slice) and returns the number of zeroed * clusters. */ -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, uint64_t nb_clusters, int flags) { @@ -2062,9 +2062,15 @@ 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 { @@ -2082,9 +2088,17 @@ 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); + } } } @@ -2093,7 +2107,7 @@ zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, return nb_clusters; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK zero_l2_subclusters(BlockDriverState *bs, uint64_t offset, unsigned nb_subclusters) { @@ -2231,11 +2245,12 @@ int coroutine_fn qcow2_subcluster_zeroize(BlockDriverState *bs, uint64_t offset, * 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 996d1217d08..0266542ceea 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -229,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; @@ -302,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; @@ -806,12 +807,9 @@ static void update_refcount_discard(BlockDriverState *bs, /* 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; @@ -967,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; @@ -2302,7 +2300,7 @@ 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 coroutine_fn +static void coroutine_fn GRAPH_RDLOCK compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix, bool *rebuild, int64_t *highest_cluster, @@ -3103,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; @@ -3166,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; @@ -3210,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; @@ -3545,8 +3547,8 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, return ret; } -static int64_t coroutine_fn 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); @@ -3565,7 +3567,7 @@ static int64_t coroutine_fn get_refblock_offset(BlockDriverState *bs, 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; diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 92e47978bf9..1e8dc48be1c 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -23,7 +23,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qcow2.h" #include "qemu/bswap.h" diff --git a/block/qcow2.c b/block/qcow2.c index b48cd9ce636..45451a7ee86 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -25,15 +25,15 @@ #include "qemu/osdep.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/main-loop.h" #include "qemu/module.h" #include "qcow2.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/qapi-events-block-core.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "trace.h" #include "qemu/option_int.h" #include "qemu/cutils.h" @@ -95,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; @@ -156,7 +157,7 @@ qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen, void *opaque, /* The graph lock must be held when called in coroutine context */ -static int coroutine_mixed_fn +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) @@ -320,7 +321,7 @@ 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; } @@ -536,7 +537,7 @@ int qcow2_mark_dirty(BlockDriverState *bs) * function when there are no pending requests, it does not guard against * concurrent requests dirtying the image. */ -static int qcow2_mark_clean(BlockDriverState *bs) +static int GRAPH_RDLOCK qcow2_mark_clean(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; @@ -570,7 +571,8 @@ int qcow2_mark_corrupt(BlockDriverState *bs) * Marks the image as consistent, i.e., unsets the corrupt bit, and flushes * before if necessary. */ -static int coroutine_fn qcow2_mark_consistent(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK +qcow2_mark_consistent(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; @@ -980,10 +982,9 @@ typedef struct Qcow2ReopenState { 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; @@ -1260,7 +1261,7 @@ static void qcow2_update_options_abort(BlockDriverState *bs, qapi_free_QCryptoBlockOpenOptions(r->crypto_opts); } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK qcow2_update_options(BlockDriverState *bs, QDict *options, int flags, Error **errp) { @@ -1635,7 +1636,22 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, 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 */ bdrv_graph_co_rdunlock(); s->data_file = bdrv_co_open_child(NULL, options, "data-file", bs, @@ -1700,13 +1716,12 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, 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; } - } else if (!(flags & BDRV_O_NO_IO)) { + } else { error_setg(errp, "Missing CRYPTO header for crypt method %d", s->crypt_method_header); ret = -EINVAL; @@ -1880,7 +1895,9 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, g_free(s->image_data_file); if (open_data_file && has_data_file(bs)) { bdrv_graph_co_rdunlock(); - bdrv_unref_child(bs, s->data_file); + bdrv_drain_all_begin(); + bdrv_co_unref_child(bs, s->data_file); + bdrv_drain_all_end(); bdrv_graph_co_rdlock(); s->data_file = NULL; } @@ -1961,7 +1978,7 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) { BDRVQcow2State *s = bs->opaque; - if (bs->encrypted) { + if (s->crypto) { /* Encryption works on a sector granularity */ bs->bl.request_alignment = qcrypto_block_get_sector_size(s->crypto); } @@ -1969,13 +1986,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; @@ -2025,6 +2046,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) { /* @@ -2038,6 +2061,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; @@ -2058,6 +2083,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 @@ -2116,9 +2143,9 @@ static void qcow2_join_options(QDict *options, QDict *old_options) } 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) +qcow2_co_block_status(BlockDriverState *bs, unsigned int mode, + int64_t offset, int64_t count, int64_t *pnum, + int64_t *map, BlockDriverState **file) { BDRVQcow2State *s = bs->opaque; uint64_t host_offset; @@ -2162,6 +2189,9 @@ qcow2_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, { status |= BDRV_BLOCK_RECURSE; } + if (type == QCOW2_SUBCLUSTER_COMPRESSED) { + status |= BDRV_BLOCK_COMPRESSED; + } return status; } @@ -2728,7 +2758,7 @@ qcow2_co_pwritev_part(BlockDriverState *bs, int64_t offset, int64_t bytes, return ret; } -static int qcow2_inactivate(BlockDriverState *bs) +static int GRAPH_RDLOCK qcow2_inactivate(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; int ret, result = 0; @@ -2763,7 +2793,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); @@ -2790,16 +2821,26 @@ 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_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_unref_child(bs, s->data_file); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); 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); } @@ -3137,8 +3178,9 @@ int qcow2_update_header(BlockDriverState *bs) 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; @@ -3176,10 +3218,10 @@ qcow2_set_up_encryption(BlockDriverState *bs, 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: @@ -3192,7 +3234,7 @@ 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; } @@ -3459,6 +3501,7 @@ static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version, static int coroutine_fn GRAPH_UNLOCKED qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) { + ERRP_GUARD(); BlockdevCreateOptionsQcow2 *qcow2_opts; QDict *options; @@ -3798,8 +3841,11 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) 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, @@ -3986,7 +4032,8 @@ qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts, } -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; @@ -4007,7 +4054,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); @@ -4071,8 +4118,8 @@ qcow2_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, 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; @@ -4255,6 +4302,7 @@ 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; @@ -4817,7 +4865,7 @@ qcow2_co_preadv_compressed(BlockDriverState *bs, 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; @@ -4968,7 +5016,7 @@ static int make_completely_empty(BlockDriverState *bs) 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; @@ -5012,7 +5060,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; @@ -5203,8 +5251,8 @@ qcow2_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) 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; @@ -5255,17 +5303,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; @@ -5283,7 +5331,8 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, 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; @@ -5361,7 +5410,7 @@ qcow2_co_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) 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); @@ -5397,9 +5446,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; @@ -5507,9 +5557,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; @@ -5635,11 +5686,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; @@ -5902,7 +5952,7 @@ static int coroutine_fn qcow2_co_amend(BlockDriverState *bs, 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; @@ -6094,64 +6144,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, - - .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_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_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, - - .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, - - .bdrv_detach_aio_context = qcow2_detach_aio_context, - .bdrv_attach_aio_context = qcow2_attach_aio_context, + .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_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_co_get_info = qcow2_co_get_info, + .bdrv_get_specific_info = qcow2_get_specific_info, + + .bdrv_co_save_vmstate = qcow2_co_save_vmstate, + .bdrv_co_load_vmstate = qcow2_co_load_vmstate, + + .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, + + .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_supports_persistent_dirty_bitmap = qcow2_supports_persistent_dirty_bitmap, diff --git a/block/qcow2.h b/block/qcow2.h index f789ce3ae0f..a9e3481c6e0 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -641,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); @@ -709,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; @@ -743,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, @@ -834,13 +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_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, @@ -851,165 +852,208 @@ int qcow2_validate_table(BlockDriverState *bs, uint64_t offset, 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 coroutine_fn qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, - int64_t nb_clusters); -int64_t coroutine_fn GRAPH_RDLOCK 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); +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); + +int GRAPH_RDLOCK +qcow2_update_snapshot_refcount(BlockDriverState *bs, int64_t l1_table_offset, + int l1_size, int addend); + +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); -void qcow2_process_discards(BlockDriverState *bs, int ret); +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 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 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 qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, - BlockDriverAmendStatusCB *status_cb, - void *cb_opaque, Error **errp); +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 qcow2_get_last_cluster(BlockDriverState *bs, int64_t size); + +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 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 qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); +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 GRAPH_RDLOCK +qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset, + unsigned int *bytes, uint64_t *host_offset, + QCow2SubclusterType *subcluster_type); + +int coroutine_fn GRAPH_RDLOCK +qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, + unsigned int *bytes, uint64_t *host_offset, + QCowL2Meta **m); + int coroutine_fn GRAPH_RDLOCK 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); +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 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); +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 qcow2_expand_zero_clusters(BlockDriverState *bs, - BlockDriverAmendStatusCB *status_cb, - void *cb_opaque); +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 coroutine_fn GRAPH_RDLOCK qcow2_read_snapshots(BlockDriverState *bs, Error **errp); -int qcow2_write_snapshots(BlockDriverState *bs); +int GRAPH_RDLOCK qcow2_write_snapshots(BlockDriverState *bs); int coroutine_fn GRAPH_RDLOCK 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_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 coroutine_fn +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 qcow2_get_bitmap_info_list(BlockDriverState *bs, - Qcow2BitmapInfoList **info_list, Error **errp); -int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); -int coroutine_fn 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); +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.c b/block/qed.c index b2604d9dad3..4a36fb39294 100644 --- a/block/qed.c +++ b/block/qed.c @@ -23,8 +23,8 @@ #include "qemu/memalign.h" #include "trace.h" #include "qed.h" -#include "sysemu/block-backend.h" -#include "qapi/qmp/qdict.h" +#include "system/block-backend.h" +#include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" @@ -353,6 +353,7 @@ static void bdrv_qed_detach_aio_context(BlockDriverState *bs) qed_cancel_need_check_timer(s); timer_free(s->need_check_timer); + s->need_check_timer = NULL; } static void bdrv_qed_attach_aio_context(BlockDriverState *bs, @@ -570,8 +571,8 @@ static void coroutine_fn bdrv_qed_open_entry(void *opaque) 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, @@ -612,7 +613,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; @@ -631,6 +632,14 @@ static void bdrv_qed_close(BlockDriverState *bs) qemu_vfree(s->l1_table); } +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) { @@ -824,9 +833,9 @@ bdrv_qed_co_create_opts(BlockDriver *drv, const char *filename, } 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) +bdrv_qed_co_block_status(BlockDriverState *bs, unsigned int mode, + 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); @@ -1138,7 +1147,7 @@ static int coroutine_fn GRAPH_RDLOCK qed_aio_write_cow(QEDAIOCB *acb) /** * 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) { @@ -1443,12 +1452,10 @@ bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, 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; @@ -1498,9 +1505,9 @@ bdrv_qed_co_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; @@ -1562,7 +1569,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)); @@ -1573,10 +1580,11 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs, 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); @@ -1636,34 +1644,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, - - .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_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_drain_begin = bdrv_qed_drain_begin, + .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_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 988654cb860..26d4bf038c7 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 diff --git a/block/quorum.c b/block/quorum.c index f28758cf2b8..cc3bc5f4e7d 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -23,10 +23,10 @@ #include "block/qdict.h" #include "qapi/error.h" #include "qapi/qapi-events-block.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" #include "crypto/hash.h" #define HASH_LENGTH 32 @@ -206,7 +206,7 @@ static void quorum_report_bad(QuorumOpType type, uint64_t offset, 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; @@ -393,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) { @@ -1037,12 +1037,16 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, close_exit: /* cleanup on error */ + bdrv_drain_all_begin(); + 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(); + bdrv_drain_all_end(); g_free(s->children); g_free(opened); exit: @@ -1055,15 +1059,19 @@ static void quorum_close(BlockDriverState *bs) BDRVQuorumState *s = bs->opaque; int i; + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); for (i = 0; i < s->num_children; i++) { bdrv_unref_child(bs, s->children[i]); } + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); 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; @@ -1089,8 +1097,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); @@ -1098,18 +1104,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]; @@ -1139,16 +1142,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, @@ -1229,7 +1230,7 @@ static void quorum_child_perm(BlockDriverState *bs, BdrvChild *c, * region contains zeroes, and BDRV_BLOCK_DATA otherwise. */ static int coroutine_fn GRAPH_RDLOCK -quorum_co_block_status(BlockDriverState *bs, bool want_zero, +quorum_co_block_status(BlockDriverState *bs, unsigned int mode, int64_t offset, int64_t count, int64_t *pnum, int64_t *map, BlockDriverState **file) { @@ -1241,7 +1242,7 @@ quorum_co_block_status(BlockDriverState *bs, bool want_zero, for (i = 0; i < s->num_children; i++) { int64_t bytes; ret = bdrv_co_common_block_status_above(s->children[i]->bs, NULL, false, - want_zero, offset, count, + mode, offset, count, &bytes, NULL, NULL, NULL); if (ret < 0) { quorum_report_bad(QUORUM_OP_TYPE_READ, offset, count, @@ -1311,7 +1312,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 a8bdee5279a..df16ac1ea25 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -95,9 +95,9 @@ static int raw_read_options(QDict *options, uint64_t *offset, bool *has_size, 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; @@ -111,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; } @@ -119,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; } @@ -145,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); @@ -279,11 +282,10 @@ raw_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, 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, unsigned int mode, + int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, + BlockDriverState **file) { BDRVRawState *s = bs->opaque; *pnum = bytes; @@ -397,7 +399,7 @@ raw_co_get_info(BlockDriverState *bs, BlockDriverInfo *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; @@ -452,7 +454,7 @@ raw_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) return bdrv_co_ioctl(bs->file->bs, req, buf); } -static int raw_has_zero_init(BlockDriverState *bs) +static int GRAPH_RDLOCK raw_has_zero_init(BlockDriverState *bs) { return bdrv_has_zero_init(bs->file->bs); } @@ -468,12 +470,13 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVRawState *s = bs->opaque; - AioContext *ctx; bool has_size; uint64_t offset, size; BdrvChildRole file_role; int ret; + GLOBAL_STATE_CODE(); + ret = raw_read_options(options, &offset, &has_size, &size, errp); if (ret < 0) { return ret; @@ -491,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; } @@ -516,11 +521,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, bs->file->bs->filename); } - ctx = bdrv_get_aio_context(bs); - aio_context_acquire(ctx); ret = raw_apply_options(bs, s, offset, has_size, size, errp); - aio_context_release(ctx); - if (ret < 0) { return ret; } @@ -541,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; @@ -558,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) { @@ -608,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); } diff --git a/block/rbd.c b/block/rbd.c index 978671411ec..951cd63f9ae 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -23,11 +23,11 @@ #include "block/qdict.h" #include "crypto/secret.h" #include "qemu/cutils.h" -#include "sysemu/replay.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qlist.h" +#include "system/replay.h" +#include "qobject/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qlist.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" @@ -254,7 +254,6 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options, done: g_free(buf); qobject_unref(keypairs); - return; } static int qemu_rbd_set_auth(rados_t cluster, BlockdevOptionsRbd *opts, @@ -367,11 +366,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; } @@ -1168,7 +1167,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; } @@ -1208,6 +1209,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", @@ -1290,7 +1293,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; } @@ -1500,9 +1503,9 @@ static int qemu_rbd_diff_iterate_cb(uint64_t offs, size_t len, } static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, - int64_t bytes, int64_t *pnum, - int64_t *map, + unsigned int mode, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, BlockDriverState **file) { BDRVRBDState *s = bs->opaque; @@ -1811,8 +1814,9 @@ 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, diff --git a/block/replication.c b/block/replication.c index ea4bf1aa801..08797188540 100644 --- a/block/replication.c +++ b/block/replication.c @@ -19,9 +19,9 @@ #include "block/blockjob.h" #include "block/block_int.h" #include "block/block_backup.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "block/replication.h" typedef enum { @@ -176,7 +176,6 @@ static void replication_child_perm(BlockDriverState *bs, BdrvChild *c, *nshared = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED; - return; } static int64_t coroutine_fn GRAPH_RDLOCK @@ -276,10 +275,10 @@ replication_co_writev(BlockDriverState *bs, int64_t sector_num, 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; } @@ -307,13 +306,16 @@ replication_co_writev(BlockDriverState *bs, int64_t sector_num, 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; @@ -325,6 +327,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); @@ -360,6 +363,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. @@ -387,14 +393,7 @@ 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); - } } } @@ -427,7 +426,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; @@ -454,12 +454,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 || @@ -469,20 +468,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; } @@ -490,26 +486,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); @@ -519,36 +517,40 @@ 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_drain_all_begin(); + 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(); + bdrv_drain_all_end(); return; } @@ -558,7 +560,8 @@ 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(); + bdrv_drain_all_end(); return; } @@ -570,30 +573,33 @@ 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(); + bdrv_drain_all_end(); 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(); + bdrv_drain_all_end(); 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, + BLOCKDEV_ON_ERROR_REPORT, + ON_CBW_ERROR_BREAK_GUEST_WRITE, + 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(); } @@ -604,18 +610,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) { @@ -624,38 +624,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) @@ -666,10 +656,15 @@ static void replication_done(void *opaque, int ret) if (ret == 0) { s->stage = BLOCK_REPLICATION_DONE; + bdrv_drain_all_begin(); + 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(); + bdrv_drain_all_end(); + s->error = 0; } else { s->stage = BLOCK_REPLICATION_FAILOVER_FAILED; @@ -680,12 +675,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) { @@ -694,13 +684,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; } @@ -716,29 +704,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[] = { diff --git a/block/reqlist.c b/block/reqlist.c index 08cb57cfa45..098e807378b 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 8d4e8932b8a..17ed2402db8 100644 --- a/block/snapshot-access.c +++ b/block/snapshot-access.c @@ -22,7 +22,7 @@ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/cutils.h" #include "block/block_int.h" @@ -41,11 +41,11 @@ snapshot_access_co_preadv_part(BlockDriverState *bs, static int coroutine_fn GRAPH_RDLOCK snapshot_access_co_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, + unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { - return bdrv_co_snapshot_block_status(bs->file->bs, want_zero, offset, + return bdrv_co_snapshot_block_status(bs->file->bs, mode, offset, bytes, pnum, map, file); } @@ -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; } diff --git a/block/snapshot.c b/block/snapshot.c index e22ac3eac63..28c9c43621c 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -27,11 +27,10 @@ #include "block/block_int.h" #include "block/qdict.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qemu/option.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" QemuOptsList internal_snapshot_opts = { .name = "snapshot", @@ -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,9 +291,14 @@ int bdrv_snapshot_goto(BlockDriverState *bs, } /* .bdrv_open() will re-attach it */ + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_unref_child(bs, fallback); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp); + memset(bs->opaque, 0, drv->instance_size); open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err); qobject_unref(options); if (open_ret < 0) { @@ -300,7 +315,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; } @@ -311,7 +329,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, /** * Delete an internal snapshot by @snapshot_id and @name. - * @bs: block device used in the operation + * @bs: block device used in the operation, must be drained * @snapshot_id: unique snapshot ID, or NULL * @name: snapshot name, or NULL * @errp: location to store error @@ -342,8 +360,11 @@ int bdrv_snapshot_delete(BlockDriverState *bs, GLOBAL_STATE_CODE(); + assert(bs->quiesce_counter > 0); + 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) { @@ -351,9 +372,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs, return -EINVAL; } - /* drain all pending i/o before deleting snapshot */ - bdrv_drained_begin(bs); - if (drv->bdrv_snapshot_delete) { ret = drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); } else if (fallback_bs) { @@ -365,17 +383,18 @@ int bdrv_snapshot_delete(BlockDriverState *bs, ret = -ENOTSUP; } - bdrv_drained_end(bs); return ret; } 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 +435,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 +481,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 +515,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 +529,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 +538,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 +547,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,40 +568,45 @@ int bdrv_all_delete_snapshot(const char *name, bool has_devices, strList *devices, Error **errp) { + ERRP_GUARD(); g_autoptr(GList) bdrvs = NULL; GList *iterbdrvs; + int ret = 0; GLOBAL_STATE_CODE(); - if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { - return -1; + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); + + ret = bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp); + if (ret < 0) { + goto out; } 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)); - return -1; + goto out; } iterbdrvs = iterbdrvs->next; } - return 0; +out: + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); + return ret; } @@ -589,29 +614,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 +662,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 +671,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 +702,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 +713,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 +722,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 +743,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 +752,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 2748253d4a3..70fe7cf86e5 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -37,11 +37,10 @@ #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" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "trace.h" @@ -181,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; - } - - qp = query_params_parse(uri->query); - if (!qp) { - error_setg(errp, "could not parse query parameters"); - goto err; + return -EINVAL; } - if(uri->user && strcmp(uri->user, "") != 0) { - qdict_put_str(options, "user", uri->user); + uri_user = g_uri_get_user(uri); + if (uri_user && !g_str_equal(uri_user, "")) { + qdict_put_str(options, "user", uri_user); } - qdict_put_str(options, "server.host", uri->server); + qdict_put_str(options, "server.host", uri_host); - 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); - - /* 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); + qdict_put_str(options, "path", uri_path); + + 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) @@ -359,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 @@ -371,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++; @@ -469,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: @@ -832,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; @@ -860,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; } @@ -1357,7 +1360,7 @@ 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, diff --git a/block/stream.c b/block/stream.c index e522bbdec54..f5441f27f46 100644 --- a/block/stream.c +++ b/block/stream.c @@ -16,9 +16,9 @@ #include "block/block_int.h" #include "block/blockjob_int.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/ratelimit.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "block/copy-on-read.h" enum { @@ -39,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; @@ -53,38 +54,59 @@ 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_set_backing_hd() requires that 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_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_drain_all_begin(); + 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_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. @@ -100,7 +122,10 @@ static int stream_prepare(Job *job) } out: - bdrv_drained_end(unfiltered_bs); + bdrv_drain_all_end(); + if (unfiltered_bs_cow) { + bdrv_unref(unfiltered_bs_cow); + } return ret; } @@ -128,18 +153,19 @@ 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; 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_co_getlength(s->target_bs); if (len < 0) { return len; @@ -149,7 +175,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp) 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. @@ -162,7 +188,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp) copy = false; WITH_GRAPH_RDLOCK_GUARD() { - ret = bdrv_is_allocated(unfiltered_bs, offset, STREAM_CHUNK, &n); + 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) { @@ -170,9 +196,9 @@ static int coroutine_fn stream_run(Job *job, Error **errp) * 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); + 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; @@ -225,6 +251,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, @@ -246,6 +273,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 @@ -262,7 +291,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; } /* @@ -282,10 +311,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); @@ -294,10 +322,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"); @@ -341,8 +371,12 @@ 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_drain_all_begin(); + bdrv_graph_wrlock(); if (block_job_add_bdrv(&s->common, "active node", bs, 0, basic_flags | BLK_PERM_WRITE, errp)) { + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); goto fail; } @@ -362,13 +396,18 @@ 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(); + bdrv_drain_all_end(); goto fail; } } + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); 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; @@ -388,4 +427,8 @@ void stream_start(const char *job_id, BlockDriverState *bs, 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 fb203c3ced4..66fdce9a90e 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -23,13 +23,13 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "block/throttle-groups.h" #include "qemu/throttle-options.h" #include "qemu/main-loop.h" #include "qemu/queue.h" #include "qemu/thread.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "qapi/error.h" #include "qapi/qapi-visit-block-core.h" #include "qom/object.h" @@ -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); } } } @@ -905,7 +908,6 @@ static void throttle_group_set_limits(Object *obj, Visitor *v, qemu_mutex_unlock(&tg->lock); qapi_free_ThrottleLimits(argp); error_propagate(errp, local_err); - return; } static void throttle_group_get_limits(Object *obj, Visitor *v, @@ -931,7 +933,8 @@ static bool throttle_group_can_be_deleted(UserCreatable *uc) return OBJECT(uc)->ref == 1; } -static void throttle_group_obj_class_init(ObjectClass *klass, void *class_data) +static void throttle_group_obj_class_init(ObjectClass *klass, + const void *class_data) { size_t i = 0; UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass); @@ -964,7 +967,7 @@ static const TypeInfo throttle_group_info = { .instance_size = sizeof(ThrottleGroup), .instance_init = throttle_group_obj_init, .instance_finalize = throttle_group_obj_finalize, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } }, diff --git a/block/throttle.c b/block/throttle.c index 3aaef18d4ed..97972d1f15e 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -84,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 | @@ -118,7 +121,7 @@ throttle_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, { 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); } @@ -128,7 +131,7 @@ 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); } @@ -138,7 +141,7 @@ 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); } @@ -147,7 +150,7 @@ 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); } diff --git a/block/trace-events b/block/trace-events index 6f121b76365..8e789e1f12f 100644 --- a/block/trace-events +++ b/block/trace-events @@ -166,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" diff --git a/block/vdi.c b/block/vdi.c index 6c35309e04a..3ddc62a5690 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 @@ -56,7 +58,7 @@ #include "qapi/qapi-visit-block-core.h" #include "block/block_int.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/bswap.h" @@ -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); @@ -383,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); @@ -438,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) { @@ -495,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; } @@ -518,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, unsigned int mode, + 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; @@ -634,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)) { @@ -700,7 +702,7 @@ vdi_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, /* 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); @@ -723,11 +725,11 @@ vdi_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, 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); } @@ -738,6 +740,7 @@ 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; @@ -986,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; diff --git a/block/vhdx-log.c b/block/vhdx-log.c index d8ed651b70e..4385a2d4f62 100644 --- a/block/vhdx-log.c +++ b/block/vhdx-log.c @@ -55,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; @@ -107,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; @@ -127,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; @@ -333,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; @@ -412,8 +414,9 @@ static int vhdx_log_read_desc(BlockDriverState *bs, BDRVVHDXState *s, * 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; @@ -484,8 +487,8 @@ static int vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc, * 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; @@ -584,9 +587,10 @@ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s, 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; @@ -663,8 +667,8 @@ static int vhdx_validate_log_entry(BlockDriverState *bs, BDRVVHDXState *s, /* 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; diff --git a/block/vhdx.c b/block/vhdx.c index a67edcc03ea..b2a4b813a0b 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -19,7 +19,7 @@ #include "qapi/error.h" #include "block/block_int.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/crc32c.h" @@ -29,7 +29,7 @@ #include "vhdx.h" #include "migration/blocker.h" #include "qemu/uuid.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" @@ -353,8 +353,9 @@ static int vhdx_write_header(BdrvChild *file, VHDXHeader *hdr, * * - 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 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, } -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 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s) * 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; @@ -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; } @@ -1693,7 +1699,7 @@ vhdx_create_new_metadata(BlockBackend *blk, uint64_t image_size, * Fixed images: default state of the BAT is fully populated, with * file offsets and state PAYLOAD_BLOCK_FULLY_PRESENT. */ -static int coroutine_fn +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, @@ -1706,6 +1712,7 @@ vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, uint64_t unused; int block_state; VHDXSectorInfo sinfo; + bool has_zero_init; assert(s->bat == NULL); @@ -1735,9 +1742,13 @@ 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) { @@ -1780,7 +1791,7 @@ vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, * to create the BAT itself, we will also cause the BAT to be * created. */ -static int coroutine_fn +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, @@ -2156,9 +2167,9 @@ vhdx_co_create_opts(BlockDriver *drv, const char *filename, * 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; @@ -2171,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; diff --git a/block/vhdx.h b/block/vhdx.h index 455a627a467..c6dd4d6040e 100644 --- a/block/vhdx.h +++ b/block/vhdx.h @@ -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,8 +411,9 @@ 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 coroutine_fn GRAPH_RDLOCK vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s, @@ -447,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 58ce290e9cd..89a72501209 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -26,9 +26,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "block/block_int.h" -#include "sysemu/block-backend.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.h" +#include "system/block-backend.h" +#include "qobject/qdict.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/option.h" @@ -272,6 +271,8 @@ static void vmdk_free_extents(BlockDriverState *bs) BDRVVmdkState *s = bs->opaque; VmdkExtent *e; + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); for (i = 0; i < s->num_extents; i++) { e = &s->extents[i]; g_free(e->l1_table); @@ -282,6 +283,9 @@ static void vmdk_free_extents(BlockDriverState *bs) bdrv_unref_child(bs, e->file); } } + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); + g_free(s->extents); } @@ -297,7 +301,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; @@ -347,29 +352,41 @@ vmdk_write_cid(BlockDriverState *bs, uint32_t cid) BDRVVmdkState *s = bs->opaque; int ret = 0; - desc = g_malloc0(DESC_SIZE); - tmp_desc = g_malloc0(DESC_SIZE); - ret = bdrv_co_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_co_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); @@ -377,7 +394,7 @@ vmdk_write_cid(BlockDriverState *bs, uint32_t cid) return ret; } -static int coroutine_fn 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; @@ -412,6 +429,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); @@ -448,6 +468,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; @@ -462,7 +485,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; @@ -575,8 +598,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; @@ -638,9 +661,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; @@ -794,9 +817,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; @@ -910,9 +933,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; @@ -1092,8 +1115,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; @@ -1120,9 +1144,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]; @@ -1140,6 +1166,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: * @@ -1220,7 +1248,13 @@ 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_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_unref_child(bs, extent_file); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); + bdrv_graph_rdlock_main_loop(); goto out; } extent->flat_start_offset = flat_offset << 9; @@ -1235,20 +1269,38 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, } g_free(buf); if (ret) { + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_unref_child(bs, extent_file); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); + 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_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_unref_child(bs, extent_file); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); + 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_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_unref_child(bs, extent_file); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); + bdrv_graph_rdlock_main_loop(); ret = -ENOTSUP; goto out; } @@ -1272,8 +1324,9 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, 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]; @@ -1309,6 +1362,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; @@ -1360,9 +1415,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; } @@ -1733,7 +1787,7 @@ static inline uint64_t vmdk_find_offset_in_cluster(VmdkExtent *extent, } static int coroutine_fn GRAPH_RDLOCK -vmdk_co_block_status(BlockDriverState *bs, bool want_zero, +vmdk_co_block_status(BlockDriverState *bs, unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { @@ -1770,6 +1824,8 @@ vmdk_co_block_status(BlockDriverState *bs, bool want_zero, if (extent->flat) { ret |= BDRV_BLOCK_RECURSE; } + } else { + ret |= BDRV_BLOCK_COMPRESSED; } *file = extent->file->bs; break; @@ -2231,12 +2287,12 @@ vmdk_init_extent(BlockBackend *blk, int64_t filesize, bool flat, bool compress, /* write all the data */ 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_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; } @@ -2256,7 +2312,7 @@ vmdk_init_extent(BlockBackend *blk, int64_t filesize, bool flat, bool compress, 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; } @@ -2268,7 +2324,8 @@ vmdk_init_extent(BlockBackend *blk, int64_t filesize, bool flat, bool compress, 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; @@ -2520,7 +2577,10 @@ vmdk_co_do_create(int64_t size, ret = -EINVAL; goto exit; } + + bdrv_graph_co_rdlock(); ret = vmdk_read_cid(blk_bs(backing), 0, &parent_cid); + bdrv_graph_co_rdunlock(); blk_co_unref(backing); if (ret) { error_setg(errp, "Failed to read parent CID"); @@ -2839,8 +2899,7 @@ 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 coroutine_fn GRAPH_RDLOCK @@ -2868,7 +2927,7 @@ vmdk_co_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; @@ -2885,7 +2944,7 @@ static int vmdk_has_zero_init(BlockDriverState *bs) return 1; } -static VmdkExtentInfo *vmdk_get_extent_info(VmdkExtent *extent) +static VmdkExtentInfo * GRAPH_RDLOCK vmdk_get_extent_info(VmdkExtent *extent) { VmdkExtentInfo *info = g_new0(VmdkExtentInfo, 1); @@ -2962,8 +3021,8 @@ vmdk_co_check(BlockDriverState *bs, BdrvCheckResult *result, BdrvCheckMode fix) 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; @@ -3018,8 +3077,9 @@ vmdk_co_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", diff --git a/block/vpc.c b/block/vpc.c index ceb87dd3d8e..801ff5793f8 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -27,14 +27,14 @@ #include "qapi/error.h" #include "block/block_int.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" #include "migration/blocker.h" #include "qemu/bswap.h" #include "qemu/uuid.h" #include "qemu/memalign.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" @@ -216,6 +216,39 @@ static void vpc_parse_options(BlockDriverState *bs, QemuOpts *opts, } } +/* + * Microsoft Virtual PC and Microsoft Hyper-V produce and read + * VHD image sizes differently. VPC will rely on CHS geometry, + * while Hyper-V and disk2vhd use the size specified in the footer. + * + * We use a couple of approaches to try and determine the correct method: + * look at the Creator App field, and look for images that have CHS + * geometry that is the maximum value. + * + * If the CHS geometry is the maximum CHS geometry, then we assume that + * the size is the footer->current_size to avoid truncation. Otherwise, + * we follow the table based on footer->creator_app: + * + * Known creator apps: + * 'vpc ' : CHS Virtual PC (uses disk geometry) + * 'qemu' : CHS QEMU (uses disk geometry) + * 'qem2' : current_size QEMU (uses current_size) + * 'win ' : current_size Hyper-V + * 'd2v ' : current_size Disk2vhd + * 'tap\0' : current_size XenServer + * 'CTXS' : current_size XenConverter + * 'wa\0\0': current_size Azure + * + * The user can override the table values via drive options, however + * even with an override we will still use current_size for images + * that have CHS geometry of the maximum size. + */ +static bool vpc_ignore_current_size(VHDFooter *footer) +{ + return !strncmp(footer->creator_app, "vpc ", 4) || + !strncmp(footer->creator_app, "qemu", 4); +} + static int vpc_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { @@ -238,6 +271,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; @@ -302,36 +337,8 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, bs->total_sectors = (int64_t) be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; - /* Microsoft Virtual PC and Microsoft Hyper-V produce and read - * VHD image sizes differently. VPC will rely on CHS geometry, - * while Hyper-V and disk2vhd use the size specified in the footer. - * - * We use a couple of approaches to try and determine the correct method: - * look at the Creator App field, and look for images that have CHS - * geometry that is the maximum value. - * - * If the CHS geometry is the maximum CHS geometry, then we assume that - * the size is the footer->current_size to avoid truncation. Otherwise, - * we follow the table based on footer->creator_app: - * - * Known creator apps: - * 'vpc ' : CHS Virtual PC (uses disk geometry) - * 'qemu' : CHS QEMU (uses disk geometry) - * 'qem2' : current_size QEMU (uses current_size) - * 'win ' : current_size Hyper-V - * 'd2v ' : current_size Disk2vhd - * 'tap\0' : current_size XenServer - * 'CTXS' : current_size XenConverter - * - * The user can override the table values via drive options, however - * even with an override we will still use current_size for images - * that have CHS geometry of the maximum size. - */ - use_chs = (!!strncmp(footer->creator_app, "win ", 4) && - !!strncmp(footer->creator_app, "qem2", 4) && - !!strncmp(footer->creator_app, "d2v ", 4) && - !!strncmp(footer->creator_app, "CTXS", 4) && - !!memcmp(footer->creator_app, "tap", 4)) || s->force_use_chs; + /* Use CHS or current_size to determine the image size. */ + use_chs = vpc_ignore_current_size(footer) || s->force_use_chs; if (!use_chs || bs->total_sectors == VHD_MAX_GEOMETRY || s->force_use_sz) { bs->total_sectors = be64_to_cpu(footer->current_size) / @@ -449,9 +456,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; } @@ -719,7 +726,7 @@ vpc_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, } static int coroutine_fn GRAPH_RDLOCK -vpc_co_block_status(BlockDriverState *bs, bool want_zero, +vpc_co_block_status(BlockDriverState *bs, unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) @@ -1168,7 +1175,7 @@ vpc_co_create_opts(BlockDriver *drv, const char *filename, } -static int vpc_has_zero_init(BlockDriverState *bs) +static int GRAPH_RDLOCK vpc_has_zero_init(BlockDriverState *bs) { BDRVVPCState *s = bs->opaque; @@ -1187,8 +1194,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 = { diff --git a/block/vvfat.c b/block/vvfat.c index 0ddc91fc096..814796d9185 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -34,8 +34,8 @@ #include "qemu/option.h" #include "qemu/bswap.h" #include "migration/blocker.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qemu/ctype.h" #include "qemu/cutils.h" #include "qemu/error-report.h" @@ -403,7 +403,6 @@ static direntry_t *create_long_filename(BDRVVVFATState *s, const char *filename) { int number_of_entries, i; glong length; - direntry_t *entry; gunichar2 *longname = g_utf8_to_utf16(filename, -1, NULL, &length, NULL); if (!longname) { @@ -414,24 +413,24 @@ static direntry_t *create_long_filename(BDRVVVFATState *s, const char *filename) number_of_entries = DIV_ROUND_UP(length * 2, 26); for(i=0;idirectory)); + direntry_t *entry=array_get_next(&(s->directory)); entry->attributes=0xf; entry->reserved[0]=0; entry->begin=0; entry->name[0]=(number_of_entries-i)|(i==0?0x40:0); } for(i=0;i<26*number_of_entries;i++) { + unsigned char *entry=array_get(&(s->directory),s->directory.next-1-(i/26)); int offset=(i%26); if(offset<10) offset=1+offset; else if(offset<22) offset=14+offset-10; else offset=28+offset-22; - entry=array_get(&(s->directory),s->directory.next-1-(i/26)); if (i >= 2 * length + 2) { - entry->name[offset] = 0xff; + entry[offset] = 0xff; } else if (i % 2 == 0) { - entry->name[offset] = longname[i / 2] & 0xff; + entry[offset] = longname[i / 2] & 0xff; } else { - entry->name[offset] = longname[i / 2] >> 8; + entry[offset] = longname[i / 2] >> 8; } } g_free(longname); @@ -777,7 +776,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,".."); @@ -857,7 +855,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)); } @@ -1145,6 +1143,8 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, QemuOpts *opts; int ret; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + #ifdef DEBUG vvv = s; #endif @@ -1267,9 +1267,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; } } @@ -1369,8 +1368,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; } @@ -1408,7 +1408,9 @@ static inline int read_cluster(BDRVVVFATState *s,int cluster_num) 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; @@ -1481,8 +1483,8 @@ vvfat_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sector 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; } @@ -1807,10 +1809,10 @@ cluster_was_modified(BDRVVVFATState *s, uint32_t cluster_num) } 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); } /* @@ -1878,7 +1880,6 @@ get_cluster_count_for_direntry(BDRVVVFATState* s, direntry_t* direntry, const ch 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; @@ -1929,8 +1930,9 @@ get_cluster_count_for_direntry(BDRVVVFATState* s, direntry_t* direntry, const ch (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; @@ -1939,14 +1941,9 @@ get_cluster_count_for_direntry(BDRVVVFATState* s, direntry_t* direntry, const ch 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)) { @@ -1962,24 +1959,24 @@ get_cluster_count_for_direntry(BDRVVVFATState* s, direntry_t* direntry, const ch * 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_co_pwrite(s->qcow, offset * BDRV_SECTOR_SIZE, + res = bdrv_co_pwrite(s->qcow, offs * BDRV_SECTOR_SIZE, BDRV_SECTOR_SIZE, s->cluster_buffer, 0); if (res < 0) { @@ -2404,7 +2401,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; } @@ -2467,8 +2464,9 @@ commit_direntries(BDRVVVFATState* s, int dir_index, int parent_mapping_index) 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; @@ -2524,8 +2522,9 @@ commit_one_file(BDRVVVFATState* s, int dir_index, uint32_t offset) 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) { @@ -2690,12 +2689,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; @@ -2716,8 +2715,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); } } @@ -2804,16 +2803,16 @@ static int coroutine_fn GRAPH_RDLOCK 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; } @@ -2833,8 +2832,9 @@ static int coroutine_fn GRAPH_RDLOCK 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; } @@ -3134,9 +3134,9 @@ vvfat_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, } static int coroutine_fn vvfat_co_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, - int64_t bytes, int64_t *n, - int64_t *map, + unsigned int mode, + int64_t offset, int64_t bytes, + int64_t *n, int64_t *map, BlockDriverState **file) { *n = bytes; @@ -3236,8 +3236,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 +3256,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/write-threshold.c b/block/write-threshold.c index 76d8885677e..56fe88de811 100644 --- a/block/write-threshold.c +++ b/block/write-threshold.c @@ -33,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) { @@ -41,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 213012435f4..1e3e634b87d 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -10,8 +10,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/blockdev.h" -#include "sysemu/block-backend.h" +#include "system/blockdev.h" +#include "system/block-backend.h" #include "hw/block/block.h" #include "qapi/error.h" #include "qapi/clone-visitor.h" @@ -21,12 +21,19 @@ #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; + uint32_t handshake_max_secs; QCryptoTLSCreds *tlscreds; char *tlsauthz; uint32_t max_connections; uint32_t connections; + QLIST_HEAD(, NBDConn) conns; } NBDServerData; static NBDServerData *nbd_server; @@ -51,6 +58,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 +75,55 @@ 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); + nbd_client_new(cioc, nbd_server->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)); } @@ -123,9 +162,9 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) } -void nbd_server_start(SocketAddress *addr, const char *tls_creds, - const char *tls_authz, uint32_t max_connections, - Error **errp) +void nbd_server_start(SocketAddress *addr, uint32_t handshake_max_secs, + const char *tls_creds, const char *tls_authz, + uint32_t max_connections, Error **errp) { if (nbd_server) { error_setg(errp, "NBD server already running"); @@ -134,6 +173,7 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds, nbd_server = g_new0(NBDServerData, 1); nbd_server->max_connections = max_connections; + nbd_server->handshake_max_secs = handshake_max_secs; nbd_server->listener = qio_net_listener_new(); qio_net_listener_set_name(nbd_server->listener, @@ -168,19 +208,36 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds, void nbd_server_start_options(NbdServerOptions *arg, Error **errp) { - nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz, - arg->max_connections, errp); + if (!arg->has_max_connections) { + arg->max_connections = NBD_DEFAULT_MAX_CONNECTIONS; + } + if (!arg->has_handshake_max_seconds) { + arg->handshake_max_seconds = NBD_DEFAULT_HANDSHAKE_MAX_SECS; + } + + nbd_server_start(arg->addr, arg->handshake_max_seconds, arg->tls_creds, + arg->tls_authz, arg->max_connections, errp); } -void qmp_nbd_server_start(SocketAddressLegacy *addr, +void qmp_nbd_server_start(bool has_handshake_max_secs, + uint32_t handshake_max_secs, const char *tls_creds, const char *tls_authz, bool has_max_connections, uint32_t max_connections, + SocketAddressLegacy *addr, Error **errp) { SocketAddress *addr_flat = socket_address_flatten(addr); - nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp); + if (!has_max_connections) { + max_connections = NBD_DEFAULT_MAX_CONNECTIONS; + } + if (!has_handshake_max_secs) { + handshake_max_secs = NBD_DEFAULT_HANDSHAKE_MAX_SECS; + } + + nbd_server_start(addr_flat, handshake_max_secs, tls_creds, tls_authz, + max_connections, errp); qapi_free_SocketAddress(addr_flat); } diff --git a/blockdev.c b/blockdev.c index e6eba61484a..2e7fda67801 100644 --- a/blockdev.c +++ b/blockdev.c @@ -31,8 +31,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" +#include "system/block-backend.h" +#include "system/blockdev.h" #include "hw/block/block.h" #include "block/blockjob.h" #include "block/dirty-bitmap.h" @@ -46,19 +46,19 @@ #include "qapi/qapi-commands-block.h" #include "qapi/qapi-commands-transaction.h" #include "qapi/qapi-visit-block-core.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qapi/qobject-output-visitor.h" -#include "sysemu/sysemu.h" -#include "sysemu/iothread.h" +#include "system/system.h" +#include "system/iothread.h" #include "block/block_int.h" #include "block/trace.h" -#include "sysemu/runstate.h" -#include "sysemu/replay.h" +#include "system/runstate.h" +#include "system/replay.h" #include "qemu/cutils.h" #include "qemu/help_option.h" #include "qemu/main-loop.h" @@ -255,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)) { @@ -662,7 +662,6 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, /* Takes the ownership of bs_opts */ BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp) { - BlockDriverState *bs; int bdrv_flags = 0; GLOBAL_STATE_CODE(); @@ -677,11 +676,7 @@ BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp) bdrv_flags |= BDRV_O_INACTIVE; } - aio_context_acquire(qemu_get_aio_context()); - bs = bdrv_open(NULL, NULL, bs_opts, bdrv_flags, errp); - aio_context_release(qemu_get_aio_context()); - - return bs; + return bdrv_open(NULL, NULL, bs_opts, bdrv_flags, errp); } void blockdev_close_all_bdrv_states(void) @@ -690,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); } } @@ -977,6 +968,15 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type, qemu_opt_set(devopts, "driver", "virtio-blk", &error_abort); qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), &error_abort); + } else if (type == IF_XEN) { + QemuOpts *devopts; + devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, + &error_abort); + qemu_opt_set(devopts, "driver", + (media == MEDIA_CDROM) ? "xen-cdrom" : "xen-disk", + &error_abort); + qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), + &error_abort); } filename = qemu_opt_get(legacy_opts, "file"); @@ -1039,7 +1039,8 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type, static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) { BlockDriverState *bs; - AioContext *aio_context; + + GRAPH_RDLOCK_GUARD_MAINLOOP(); bs = bdrv_lookup_bs(name, name, errp); if (bs == NULL) { @@ -1051,16 +1052,11 @@ static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) return NULL; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - if (!bdrv_is_inserted(bs)) { error_setg(errp, "Device has no medium"); bs = NULL; } - aio_context_release(aio_context); - return bs; } @@ -1130,49 +1126,49 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, Error **errp) { BlockDriverState *bs; - AioContext *aio_context; QEMUSnapshotInfo sn; Error *local_err = NULL; SnapshotInfo *info = NULL; int ret; + GLOBAL_STATE_CODE(); + + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); + bs = qmp_get_root_bs(device, errp); if (!bs) { - return NULL; + goto error; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); if (!id && !name) { error_setg(errp, "Name or id must be provided"); - goto out_aio_context; + goto error; } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) { - goto out_aio_context; + goto error; } 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; + goto error; } 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; + goto error; } bdrv_snapshot_delete(bs, id, name, &local_err); if (local_err) { error_propagate(errp, local_err); - goto out_aio_context; + goto error; } - aio_context_release(aio_context); - info = g_new0(SnapshotInfo, 1); info->id = g_strdup(sn.id_str); info->name = g_strdup(sn.name); @@ -1186,11 +1182,10 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, info->has_icount = true; } +error: + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); return info; - -out_aio_context: - aio_context_release(aio_context); - return NULL; } /* internal snapshot private data */ @@ -1213,14 +1208,16 @@ static void internal_snapshot_action(BlockdevSnapshotInternal *internal, Error *local_err = NULL; const char *device; const char *name; - BlockDriverState *bs; + BlockDriverState *bs, *check_bs; QEMUSnapshotInfo old_sn, *sn; bool ret; int64_t rt; InternalSnapshotState *state = g_new0(InternalSnapshotState, 1); - AioContext *aio_context; int ret1; + GLOBAL_STATE_CODE(); + bdrv_graph_rdlock_main_loop(); + tran_add(tran, &internal_snapshot_drv, state); device = internal->device; @@ -1228,36 +1225,48 @@ static void internal_snapshot_action(BlockdevSnapshotInternal *internal, bs = qmp_get_root_bs(device, errp); if (!bs) { + bdrv_graph_rdunlock_main_loop(); return; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - state->bs = bs; + /* Need to drain while unlocked. */ + bdrv_graph_rdunlock_main_loop(); /* Paired with .clean() */ bdrv_drained_begin(bs); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + /* Make sure the root bs did not change with the drain. */ + check_bs = qmp_get_root_bs(device, errp); + if (bs != check_bs) { + if (check_bs) { + error_setg(errp, "Block node of device '%s' unexpectedly changed", + device); + } /* else errp is already set */ + return; + } + 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 */ @@ -1265,12 +1274,12 @@ static void internal_snapshot_action(BlockdevSnapshotInternal *internal, &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 */ @@ -1291,14 +1300,11 @@ static void internal_snapshot_action(BlockdevSnapshotInternal *internal, 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(void *opaque) @@ -1306,15 +1312,16 @@ static void internal_snapshot_abort(void *opaque) InternalSnapshotState *state = opaque; BlockDriverState *bs = state->bs; QEMUSnapshotInfo *sn = &state->sn; - AioContext *aio_context; Error *local_error = NULL; + GLOBAL_STATE_CODE(); + if (!state->created) { return; } - aio_context = bdrv_get_aio_context(state->bs); - aio_context_acquire(aio_context); + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) { error_reportf_err(local_error, @@ -1323,25 +1330,19 @@ static void internal_snapshot_abort(void *opaque) sn->id_str, sn->name, bdrv_get_device_name(bs)); } - - aio_context_release(aio_context); + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); } static void internal_snapshot_clean(void *opaque) { g_autofree InternalSnapshotState *state = opaque; - AioContext *aio_context; 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 */ @@ -1375,8 +1376,11 @@ static void external_snapshot_action(TransactionAction *action, /* File name of the new image (for 'blockdev-snapshot-sync') */ const char *new_image_file; ExternalSnapshotState *state = g_new0(ExternalSnapshotState, 1); - AioContext *aio_context; uint64_t perm, shared; + BlockDriverState *check_bs; + + /* TODO We'll eventually have to take a writer lock in this function */ + bdrv_graph_rdlock_main_loop(); tran_add(tran, &external_snapshot_drv, state); @@ -1409,29 +1413,43 @@ static void external_snapshot_action(TransactionAction *action, state->old_bs = bdrv_lookup_bs(device, node_name, errp); if (!state->old_bs) { + bdrv_graph_rdunlock_main_loop(); return; } - aio_context = bdrv_get_aio_context(state->old_bs); - aio_context_acquire(aio_context); - + /* Need to drain while unlocked. */ + bdrv_graph_rdunlock_main_loop(); /* Paired with .clean() */ bdrv_drained_begin(state->old_bs); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + /* Make sure the associated bs did not change with the drain. */ + check_bs = bdrv_lookup_bs(device, node_name, errp); + if (state->old_bs != check_bs) { + if (check_bs) { + error_setg(errp, "Block node of device '%s' unexpectedly changed", + device); + } /* else errp is already set */ + return; + } 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; } } @@ -1443,13 +1461,13 @@ static void external_snapshot_action(TransactionAction *action, 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; @@ -1462,20 +1480,18 @@ static void external_snapshot_action(TransactionAction *action, 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); - aio_context_release(aio_context); bdrv_img_create(new_image_file, format, state->old_bs->filename, state->old_bs->drv->format_name, NULL, size, flags, false, &local_err); - aio_context_acquire(aio_context); if (local_err) { error_propagate(errp, local_err); - goto out; + return; } } @@ -1485,20 +1501,15 @@ static void external_snapshot_action(TransactionAction *action, } qdict_put_str(options, "driver", format); } - aio_context_release(aio_context); - aio_context_acquire(qemu_get_aio_context()); state->new_bs = bdrv_open(new_image_file, snapshot_ref, options, flags, errp); - aio_context_release(qemu_get_aio_context()); /* We will manually add the backing_hd field to the bs later */ if (!state->new_bs) { return; } - aio_context_acquire(aio_context); - /* * Allow attaching a backing file to an overlay that's already in use only * if the parents don't assume that they are already seeing a valid image. @@ -1507,41 +1518,50 @@ static void external_snapshot_action(TransactionAction *action, 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; + } + + /* + * Older QEMU versions have allowed adding an active parent node to an + * inactive child node. This is unsafe in the general case, but there is an + * important use case, which is taking a VM snapshot with migration to file + * and then adding an external snapshot while the VM is still stopped and + * images are inactive. Requiring the user to explicitly create the overlay + * as inactive would break compatibility, so just do it automatically here + * to keep this working. + */ + if (bdrv_is_inactive(state->old_bs) && !bdrv_is_inactive(state->new_bs)) { + ret = bdrv_inactivate(state->new_bs, errp); + if (ret < 0) { + 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(void *opaque) { ExternalSnapshotState *state = opaque; - AioContext *aio_context; - - aio_context = bdrv_get_aio_context(state->old_bs); - aio_context_acquire(aio_context); /* We don't need (or want) to use the transactional * bdrv_reopen_multiple() across all the entries at once, because we @@ -1549,8 +1569,6 @@ static void external_snapshot_commit(void *opaque) 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(void *opaque) @@ -1563,7 +1581,6 @@ static void external_snapshot_abort(void *opaque) 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 */ @@ -1576,21 +1593,18 @@ static void external_snapshot_abort(void *opaque) */ 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 */ } } } @@ -1598,19 +1612,13 @@ static void external_snapshot_abort(void *opaque) static void external_snapshot_clean(void *opaque) { g_autofree ExternalSnapshotState *state = opaque; - AioContext *aio_context; 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 { @@ -1642,7 +1650,6 @@ static void drive_backup_action(DriveBackup *backup, BlockDriverState *target_bs; BlockDriverState *source = NULL; AioContext *aio_context; - AioContext *old_context; const char *format; QDict *options; Error *local_err = NULL; @@ -1651,6 +1658,8 @@ static void drive_backup_action(DriveBackup *backup, bool set_backing_hd = false; int ret; + GLOBAL_STATE_CODE(); + tran_add(tran, &drive_backup_drv, state); if (!backup->has_mode) { @@ -1668,7 +1677,6 @@ static void drive_backup_action(DriveBackup *backup, } aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); state->bs = bs; /* Paired with .clean() */ @@ -1680,8 +1688,10 @@ static void drive_backup_action(DriveBackup *backup, } /* 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; @@ -1707,21 +1717,25 @@ static void drive_backup_action(DriveBackup *backup, 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(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_graph_rdunlock_main_loop(); + bdrv_img_create(backup->target, format, explicit_backing->filename, explicit_backing->drv->format_name, NULL, @@ -1734,7 +1748,7 @@ static void drive_backup_action(DriveBackup *backup, if (local_err) { error_propagate(errp, local_err); - goto out; + return; } options = qdict_new(); @@ -1743,30 +1757,18 @@ static void drive_backup_action(DriveBackup *backup, if (format) { qdict_put_str(options, "driver", format); } - aio_context_release(aio_context); - aio_context_acquire(qemu_get_aio_context()); target_bs = bdrv_open(backup->target, NULL, options, flags, errp); - aio_context_release(qemu_get_aio_context()); - if (!target_bs) { return; } - /* Honor bdrv_try_change_aio_context() context acquisition requirements. */ - 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) { 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; @@ -1779,22 +1781,14 @@ static void drive_backup_action(DriveBackup *backup, unref: bdrv_unref(target_bs); -out: - aio_context_release(aio_context); } static void drive_backup_commit(void *opaque) { DriveBackupState *state = opaque; - AioContext *aio_context; - - aio_context = bdrv_get_aio_context(state->bs); - aio_context_acquire(aio_context); assert(state->job); job_start(&state->job->job); - - aio_context_release(aio_context); } static void drive_backup_abort(void *opaque) @@ -1809,18 +1803,12 @@ static void drive_backup_abort(void *opaque) static void drive_backup_clean(void *opaque) { g_autofree DriveBackupState *state = opaque; - AioContext *aio_context; 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 { @@ -1845,7 +1833,6 @@ static void blockdev_backup_action(BlockdevBackup *backup, BlockDriverState *bs; BlockDriverState *target_bs; AioContext *aio_context; - AioContext *old_context; int ret; tran_add(tran, &blockdev_backup_drv, state); @@ -1862,17 +1849,12 @@ static void blockdev_backup_action(BlockdevBackup *backup, /* 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() */ @@ -1881,22 +1863,14 @@ static void blockdev_backup_action(BlockdevBackup *backup, state->job = do_backup_common(qapi_BlockdevBackup_base(backup), bs, target_bs, aio_context, block_job_txn, errp); - - aio_context_release(aio_context); } static void blockdev_backup_commit(void *opaque) { BlockdevBackupState *state = opaque; - AioContext *aio_context; - - aio_context = bdrv_get_aio_context(state->bs); - aio_context_acquire(aio_context); assert(state->job); job_start(&state->job->job); - - aio_context_release(aio_context); } static void blockdev_backup_abort(void *opaque) @@ -1911,18 +1885,12 @@ static void blockdev_backup_abort(void *opaque) static void blockdev_backup_clean(void *opaque) { g_autofree BlockdevBackupState *state = opaque; - AioContext *aio_context; 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 { @@ -2341,34 +2309,34 @@ void coroutine_fn qmp_block_resize(const char *device, const char *node_name, 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_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_co_unref(blk); - bdrv_co_unlock(bs); } 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, @@ -2384,6 +2352,8 @@ void qmp_block_stream(const char *job_id, const char *device, Error *local_err = NULL; int job_flags = JOB_DEFAULT; + GLOBAL_STATE_CODE(); + if (base && base_node) { error_setg(errp, "'base' and 'base-node' cannot be specified " "at the same time"); @@ -2402,6 +2372,10 @@ void qmp_block_stream(const char *job_id, const char *device, return; } + if (!has_backing_mask_protocol) { + backing_mask_protocol = false; + } + if (!has_on_error) { on_error = BLOCKDEV_ON_ERROR_REPORT; } @@ -2412,13 +2386,13 @@ void qmp_block_stream(const char *job_id, const char *device, } aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); + 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); } @@ -2426,35 +2400,36 @@ void qmp_block_stream(const char *job_id, const char *device, 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 (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); } @@ -2467,16 +2442,17 @@ void qmp_block_stream(const char *job_id, const char *device, 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 && backing_file) { error_setg(errp, "backing file specified, but streaming the " "entire chain"); - goto out; + return; } if (has_auto_finalize && !auto_finalize) { @@ -2487,17 +2463,19 @@ void qmp_block_stream(const char *job_id, const char *device, } 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(const char *job_id, const char *device, @@ -2506,6 +2484,8 @@ void qmp_block_commit(const char *job_id, const char *device, 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, const char *filter_node_name, @@ -2521,6 +2501,9 @@ void qmp_block_commit(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; } @@ -2533,6 +2516,9 @@ void qmp_block_commit(const char *job_id, const char *device, 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 @@ -2553,10 +2539,9 @@ void qmp_block_commit(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 */ @@ -2564,16 +2549,16 @@ void qmp_block_commit(const char *job_id, const char *device, if (top_node && top) { error_setg(errp, "'top-node' and 'top' are mutually exclusive"); - goto out; + 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 (top) { /* This strcmp() is just a shortcut, there is no need to @@ -2587,35 +2572,35 @@ void qmp_block_commit(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 (base_node && base) { error_setg(errp, "'base-node' and 'base' are mutually exclusive"); - goto out; + 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 (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; } } @@ -2625,14 +2610,14 @@ void qmp_block_commit(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; } /* @@ -2655,7 +2640,7 @@ void qmp_block_commit(const char *job_id, const char *device, error_setg(errp, "'backing-file' specified, but 'top' has a " "writer on it"); } - goto out; + return; } if (!job_id) { /* @@ -2671,19 +2656,17 @@ void qmp_block_commit(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(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 */ @@ -2697,6 +2680,7 @@ static BlockJob *do_backup_common(BackupCommon *backup, BdrvDirtyBitmap *bmap = NULL; BackupPerf perf = { .max_workers = 64 }; int job_flags = JOB_DEFAULT; + OnCbwError on_cbw_error = ON_CBW_ERROR_BREAK_GUEST_WRITE; if (!backup->has_speed) { backup->speed = 0; @@ -2727,6 +2711,9 @@ 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) || @@ -2798,13 +2785,18 @@ static BlockJob *do_backup_common(BackupCommon *backup, job_flags |= JOB_MANUAL_DISMISS; } + if (backup->has_on_cbw_error) { + on_cbw_error = backup->on_cbw_error; + } + 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, backup->on_target_error, + on_cbw_error, job_flags, NULL, NULL, txn, errp); return job; } @@ -2829,6 +2821,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); } @@ -2849,7 +2843,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, const char *replaces, enum MirrorSyncMode sync, BlockMirrorBackingMode backing_mode, - bool zero_target, + bool target_is_zero, bool has_speed, int64_t speed, bool has_granularity, uint32_t granularity, bool has_buf_size, int64_t buf_size, @@ -2916,10 +2910,6 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, return; } - if (!bdrv_backing_chain_next(bs) && sync == MIRROR_SYNC_MODE_TOP) { - sync = MIRROR_SYNC_MODE_FULL; - } - if (!replaces) { /* We want to mirror from @bs, but keep implicit filters on top */ unfiltered_bs = bdrv_skip_implicit_filters(bs); @@ -2930,7 +2920,6 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, if (replaces) { BlockDriverState *to_replace_bs; - AioContext *replace_aio_context; int64_t bs_size, replace_size; bs_size = bdrv_getlength(bs); @@ -2944,10 +2933,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, @@ -2964,11 +2950,10 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, /* pass the node name to replace to mirror start since it's loose coupling * and will allow to check whether the node still exist at mirror completion */ - mirror_start(job_id, bs, target, - 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); + mirror_start(job_id, bs, target, replaces, job_flags, + speed, granularity, buf_size, sync, backing_mode, + target_is_zero, on_source_error, on_target_error, unmap, + filter_node_name, copy_mode, errp); } void qmp_drive_mirror(DriveMirror *arg, Error **errp) @@ -2976,14 +2961,13 @@ 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; int flags; int64_t size; const char *format = arg->format; - bool zero_target; + bool target_is_zero; int ret; bs = qmp_get_root_bs(arg->device, errp); @@ -2992,12 +2976,13 @@ 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; @@ -3016,18 +3001,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->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; } } @@ -3048,16 +3034,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, @@ -3070,7 +3061,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) if (local_err) { error_propagate(errp, local_err); - goto out; + return; } options = qdict_new(); @@ -3080,41 +3071,30 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) if (format) { qdict_put_str(options, "driver", format); } - aio_context_release(aio_context); /* Mirroring takes care of copy-on-write using the source's backing * file. */ - aio_context_acquire(qemu_get_aio_context()); target_bs = bdrv_open(arg->target, NULL, options, flags, errp); - aio_context_release(qemu_get_aio_context()); - if (!target_bs) { return; } - zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL && - (arg->mode == NEW_IMAGE_MODE_EXISTING || - !bdrv_has_zero_init(target_bs))); - + bdrv_graph_rdlock_main_loop(); + target_is_zero = (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_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->job_id, bs, target_bs, arg->replaces, arg->sync, - backing_mode, zero_target, + backing_mode, target_is_zero, arg->has_speed, arg->speed, arg->has_granularity, arg->granularity, arg->has_buf_size, arg->buf_size, @@ -3127,8 +3107,6 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) arg->has_auto_dismiss, arg->auto_dismiss, errp); bdrv_unref(target_bs); -out: - aio_context_release(aio_context); } void qmp_blockdev_mirror(const char *job_id, @@ -3146,14 +3124,13 @@ void qmp_blockdev_mirror(const char *job_id, bool has_copy_mode, MirrorCopyMode copy_mode, bool has_auto_finalize, bool auto_finalize, bool has_auto_dismiss, bool auto_dismiss, + bool has_target_is_zero, bool target_is_zero, Error **errp) { BlockDriverState *bs; BlockDriverState *target_bs; AioContext *aio_context; - AioContext *old_context; BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN; - bool zero_target; int ret; bs = qmp_get_root_bs(device, errp); @@ -3166,25 +3143,17 @@ void qmp_blockdev_mirror(const char *job_id, return; } - 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(job_id, bs, target_bs, replaces, sync, backing_mode, - zero_target, has_speed, speed, + has_target_is_zero && target_is_zero, + has_speed, speed, has_granularity, granularity, has_buf_size, buf_size, has_on_source_error, on_source_error, @@ -3194,8 +3163,6 @@ void qmp_blockdev_mirror(const char *job_id, has_auto_finalize, auto_finalize, has_auto_dismiss, auto_dismiss, errp); -out: - aio_context_release(aio_context); } /* @@ -3338,13 +3305,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; @@ -3355,45 +3335,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; } } @@ -3411,9 +3391,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) @@ -3453,7 +3434,6 @@ void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) for (; reopen_list != NULL; reopen_list = reopen_list->next) { BlockdevOptions *options = reopen_list->value; BlockDriverState *bs; - AioContext *ctx; QObject *obj; Visitor *v; QDict *qdict; @@ -3481,12 +3461,7 @@ void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) qdict_flatten(qdict); - ctx = bdrv_get_aio_context(bs); - aio_context_acquire(ctx); - queue = bdrv_reopen_queue(queue, bs, qdict, false); - - aio_context_release(ctx); } /* Perform the reopen operation */ @@ -3499,10 +3474,10 @@ void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) 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) { @@ -3513,34 +3488,61 @@ 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); +void qmp_blockdev_set_active(const char *node_name, bool active, Error **errp) +{ + int ret; + + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + if (!node_name) { + if (active) { + bdrv_activate_all(errp); + } else { + ret = bdrv_inactivate_all(); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to inactivate all nodes"); + } + } + } else { + BlockDriverState *bs = bdrv_find_node(node_name); + if (!bs) { + error_setg(errp, "Failed to find node with node-name='%s'", + node_name); + return; + } + + if (active) { + bdrv_activate(bs, errp); + } else { + bdrv_inactivate(bs, errp); + } + } } -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; @@ -3559,9 +3561,12 @@ void qmp_x_blockdev_change(const char *parent, const char *child, BlockDriverState *parent_bs, *new_bs = NULL; BdrvChild *p_child; + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); + parent_bs = bdrv_lookup_bs(parent, parent, errp); if (!parent_bs) { - return; + goto out; } if (!child == !node) { @@ -3570,7 +3575,7 @@ void qmp_x_blockdev_change(const char *parent, const char *child, } else { error_setg(errp, "Either child or node must be specified"); } - return; + goto out; } if (child) { @@ -3578,7 +3583,7 @@ void qmp_x_blockdev_change(const char *parent, const char *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); } @@ -3587,10 +3592,14 @@ void qmp_x_blockdev_change(const char *parent, const char *child, 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(); + bdrv_drain_all_end(); } BlockJobInfoList *qmp_query_block_jobs(Error **errp) @@ -3621,14 +3630,16 @@ 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; + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); + bs = bdrv_find_node(node_name); if (!bs) { error_setg(errp, "Failed to find node with node-name='%s'", node_name); - return; + goto out; } /* Protects against accidents. */ @@ -3636,14 +3647,14 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, error_setg(errp, "Node %s is associated with a BlockBackend and could " "be in use (use force=true to override this check)", node_name); - return; + goto out; } if (iothread->type == QTYPE_QSTRING) { IOThread *obj = iothread_by_id(iothread->u.s); if (!obj) { error_setg(errp, "Cannot find iothread %s", iothread->u.s); - return; + goto out; } new_context = iothread_get_aio_context(obj); @@ -3651,12 +3662,11 @@ 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_locked(bs, new_context, NULL, errp); - bdrv_try_change_aio_context(bs, new_context, NULL, errp); - - aio_context_release(old_context); +out: + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); } QemuOptsList qemu_common_drive_opts = { diff --git a/blockjob.c b/blockjob.c index 25fe8e625d2..e68181a35b7 100644 --- a/blockjob.c +++ b/blockjob.c @@ -29,7 +29,7 @@ #include "block/blockjob_int.h" #include "block/block_int.h" #include "block/trace.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qapi/qapi-events-block-core.h" #include "qapi/qmp/qerror.h" @@ -144,9 +144,9 @@ static TransactionActionDrv change_child_job_context = { .clean = g_free, }; -static bool child_job_change_aio_ctx(BdrvChild *c, AioContext *ctx, - GHashTable *visited, Transaction *tran, - Error **errp) +static bool GRAPH_RDLOCK +child_job_change_aio_ctx(BdrvChild *c, AioContext *ctx, GHashTable *visited, + Transaction *tran, Error **errp) { BlockJob *job = c->opaque; BdrvStateChildJobContext *s; @@ -198,6 +198,8 @@ 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_drain_all_begin(); + bdrv_graph_wrlock(); while (job->nodes) { GSList *l = job->nodes; BdrvChild *c = l->data; @@ -209,6 +211,8 @@ void block_job_remove_all_bdrv(BlockJob *job) g_slist_free_1(l); } + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); } bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs) @@ -230,28 +234,12 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, Error **errp) { BdrvChild *c; - AioContext *ctx = bdrv_get_aio_context(bs); - bool need_context_ops; GLOBAL_STATE_CODE(); bdrv_ref(bs); - need_context_ops = ctx != job->job.aio_context; - - if (need_context_ops) { - if (job->job.aio_context != qemu_get_aio_context()) { - aio_context_release(job->job.aio_context); - } - aio_context_acquire(ctx); - } c = bdrv_root_attach_child(bs, name, &child_job, 0, perm, shared_perm, job, errp); - if (need_context_ops) { - aio_context_release(ctx); - if (job->job.aio_context != qemu_get_aio_context()) { - aio_context_acquire(job->job.aio_context); - } - } if (c == NULL) { return -EPERM; } @@ -326,6 +314,26 @@ static bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) return block_job_set_speed_locked(job, speed, errp); } +void block_job_change_locked(BlockJob *job, BlockJobChangeOptions *opts, + Error **errp) +{ + const BlockJobDriver *drv = block_job_driver(job); + + GLOBAL_STATE_CODE(); + + if (job_apply_verb_locked(&job->job, JOB_VERB_CHANGE, errp)) { + return; + } + + if (drv->change) { + job_unlock(); + drv->change(job, opts, errp); + job_lock(); + } else { + error_setg(errp, "Job type does not support change"); + } +} + void block_job_ratelimit_processed_bytes(BlockJob *job, uint64_t n) { IO_CODE(); @@ -354,6 +362,7 @@ 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(); @@ -366,7 +375,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; @@ -383,6 +392,11 @@ BlockJobInfo *block_job_query_locked(BlockJob *job, Error **errp) 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; } @@ -484,6 +498,9 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, int ret; GLOBAL_STATE_CODE(); + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); + if (job_id == NULL && !(flags & JOB_INTERNAL)) { job_id = bdrv_get_device_name(bs); } @@ -491,6 +508,8 @@ 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(); + bdrv_drain_all_end(); return NULL; } @@ -524,15 +543,17 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, goto fail; } - bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); - if (!block_job_set_speed(job, speed, errp)) { goto fail; } + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); return job; fail: + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); 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 00000000000..6bc73a798f3 --- /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 00000000000..702aeb7fc57 --- /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 00000000000..4815a56ae3c --- /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 00000000000..b2fa59efaf6 --- /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 00000000000..87fbf6d6775 --- /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 G_NORETURN 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 00000000000..cc87f475b3f --- /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 00000000000..b53302e7f7a --- /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 00000000000..b72ba7aa504 --- /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 00000000000..8cdd33b621d --- /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 00000000000..b003015daf4 --- /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 00000000000..4c911e605ac --- /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 00000000000..0c354919708 --- /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 00000000000..08ae913c42e --- /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/target_arch.h b/bsd-user/arm/target_arch.h index 561934bbd25..d80cb85c647 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.h b/bsd-user/arm/target_arch_cpu.h index 517d0087644..bc2eaa0bf4e 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -37,7 +37,7 @@ static inline void target_cpu_init(CPUARMState *env, } } -static inline void target_cpu_loop(CPUARMState *env) +static inline G_NORETURN void target_cpu_loop(CPUARMState *env) { int trapnr, si_signo, si_code; CPUState *cs = env_cpu(env); diff --git a/bsd-user/arm/target_arch_signal.h b/bsd-user/arm/target_arch_signal.h index 02b2b33e07a..10f96b8bfc9 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 3c00dc00567..6fa2c30b4de 100644 --- a/bsd-user/bsd-file.h +++ b/bsd-user/bsd-file.h @@ -641,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 00000000000..2ab1334b700 --- /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 00000000000..1be906c5914 --- /dev/null +++ b/bsd-user/bsd-mem.h @@ -0,0 +1,457 @@ +/* + * 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/mmap-lock.h" +#include "exec/page-protection.h" +#include "user/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 alignment; + abi_ulong mmap_start; + + alignment = 0; /* alignment above page size not required */ + mmap_start = mmap_find_vma(0, shm_info.shm_segsz, alignment); + + 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 00000000000..ca3c1bf94f4 --- /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 a1061bffb8f..8b1c2deea37 100644 --- a/bsd-user/bsd-proc.h +++ b/bsd-user/bsd-proc.h @@ -22,12 +22,16 @@ #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); @@ -35,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 baf2f63d2f1..3bca0cc9ede 100644 --- a/bsd-user/elfload.c +++ b/bsd-user/elfload.c @@ -44,7 +44,7 @@ static inline void memcpy_fromfs(void *to, const void *from, unsigned long n) memcpy(to, from, n); } -#ifdef BSWAP_NEEDED +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN static void bswap_ehdr(struct elfhdr *ehdr) { bswap16s(&ehdr->e_type); /* Object file type */ @@ -111,7 +111,7 @@ static void bswap_note(struct elf_note *en) bswap32s(&en->n_type); } -#else /* ! BSWAP_NEEDED */ +#else static void bswap_ehdr(struct elfhdr *ehdr) { } static void bswap_phdr(struct elf_phdr *phdr, int phnum) { } @@ -119,7 +119,7 @@ static void bswap_shdr(struct elf_shdr *shdr, int shnum) { } static void bswap_sym(struct elf_sym *sym) { } static void bswap_note(struct elf_note *en) { } -#endif /* ! BSWAP_NEEDED */ +#endif /* HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN */ #include "elfcore.c" @@ -383,7 +383,7 @@ static const char *lookup_symbolxx(struct syminfo *s, uint64_t 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; diff --git a/bsd-user/errno_defs.h b/bsd-user/errno_defs.h index f3e8ac34883..abe70119d90 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 f2f047cca31..8fd6c7cfb82 100644 --- a/bsd-user/freebsd/meson.build +++ b/bsd-user/freebsd/meson.build @@ -1,5 +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 00000000000..71145764a4d --- /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 00000000000..bf993f1b662 --- /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 00000000000..3003c8cb637 --- /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.h b/bsd-user/freebsd/os-stat.h index b20e2707745..3bdc66aa984 100644 --- a/bsd-user/freebsd/os-stat.h +++ b/bsd-user/freebsd/os-stat.h @@ -146,7 +146,7 @@ static inline abi_long do_freebsd_fstatat(abi_long arg1, abi_long arg2, return ret; } -/* undocummented nstat(char *path, struct nstat *ub) syscall */ +/* undocumented nstat(char *path, struct nstat *ub) syscall */ static abi_long do_freebsd11_nstat(abi_long arg1, abi_long arg2) { abi_long ret; @@ -162,7 +162,7 @@ static abi_long do_freebsd11_nstat(abi_long arg1, abi_long arg2) return ret; } -/* undocummented nfstat(int fd, struct nstat *sb) syscall */ +/* undocumented nfstat(int fd, struct nstat *sb) syscall */ static abi_long do_freebsd11_nfstat(abi_long arg1, abi_long arg2) { abi_long ret; @@ -175,7 +175,7 @@ static abi_long do_freebsd11_nfstat(abi_long arg1, abi_long arg2) return ret; } -/* undocummented nlstat(char *path, struct nstat *ub) syscall */ +/* undocumented nlstat(char *path, struct nstat *ub) syscall */ static abi_long do_freebsd11_nlstat(abi_long arg1, abi_long arg2) { abi_long ret; diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c index fa60df529ef..ca2f6fdb66e 100644 --- a/bsd-user/freebsd/os-syscall.c +++ b/bsd-user/freebsd/os-syscall.c @@ -33,11 +33,15 @@ #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 */ +/* 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); @@ -58,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. @@ -219,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. */ @@ -592,6 +795,108 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1, 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} diff --git a/bsd-user/freebsd/target_os_elf.h b/bsd-user/freebsd/target_os_elf.h index 9df17d56d8a..01124979f79 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 4573738752d..6c282d85021 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 05901332915..ac0ef22cd7a 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 f036a323435..1ca7b5ab173 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/target_arch_cpu.h b/bsd-user/i386/target_arch_cpu.h index 9bf2c4244b7..5d4c931decd 100644 --- a/bsd-user/i386/target_arch_cpu.h +++ b/bsd-user/i386/target_arch_cpu.h @@ -102,7 +102,7 @@ static inline void target_cpu_init(CPUX86State *env, env->segs[R_FS].selector = 0; } -static inline void target_cpu_loop(CPUX86State *env) +static inline G_NORETURN void target_cpu_loop(CPUX86State *env) { CPUState *cs = env_cpu(env); int trapnr; diff --git a/bsd-user/i386/target_arch_signal.h b/bsd-user/i386/target_arch_signal.h index 279dadc22c7..2c14153ab6b 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 f913cb55a72..7c0a059c3ba 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -35,8 +35,10 @@ #include "qemu/path.h" #include "qemu/help_option.h" #include "qemu/module.h" -#include "exec/exec-all.h" -#include "tcg/tcg.h" +#include "qemu/plugin.h" +#include "user/guest-base.h" +#include "user/page-protection.h" +#include "tcg/startup.h" #include "qemu/timer.h" #include "qemu/envlist.h" #include "qemu/cutils.h" @@ -45,11 +47,20 @@ #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" + +/* + * 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; /* @@ -68,29 +79,20 @@ 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)) -# define MAX_RESERVED_VA 0xfffffffful +# define MAX_RESERVED_VA(CPU) 0xfffffffful # else -# define MAX_RESERVED_VA ((1ul << TARGET_VIRT_ADDR_SPACE_BITS) - 1) +# 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 +unsigned long guest_addr_max; -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 */ @@ -104,35 +106,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) @@ -163,10 +171,13 @@ static void usage(void) " (use '-d help' for a list of log items)\n" "-D logfile write logs to 'logfile' (default stderr)\n" "-one-insn-per-tb run with one guest instruction per emulated TB\n" - "-singlestep deprecated synonym for -one-insn-per-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" +#ifdef CONFIG_PLUGIN + "-plugin [file=][,=]\n" +#endif "\n" "Environment variables:\n" "QEMU_STRACE Print system calls and arguments similar to the\n" @@ -217,6 +228,8 @@ static void init_task_state(TaskState *ts) }; } +static QemuPluginList plugins = QTAILQ_HEAD_INITIALIZER(plugins); + void gemu_log(const char *fmt, ...) { va_list ap; @@ -243,22 +256,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; @@ -279,6 +276,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(); @@ -286,7 +285,6 @@ int main(int argc, char **argv) usage(); } - save_proc_pathname(argv[0]); error_init(argv[0]); module_call_init(MODULE_INIT_TRACE); @@ -308,9 +306,13 @@ int main(int argc, char **argv) (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); + qemu_plugin_add_opts(); optind = 1; for (;;) { @@ -365,11 +367,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++]); @@ -378,10 +381,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")) { @@ -394,12 +394,22 @@ 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") || !strcmp(r, "one-insn-per-tb")) { + } 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")) { trace_opt_parse(optarg); +#ifdef CONFIG_PLUGIN + } else if (!strcmp(r, "plugin")) { + r = argv[optind++]; + qemu_plugin_opt_parse(r, &plugins); +#endif } else if (!strcmp(r, "0")) { argv0 = argv[optind++]; } else { @@ -407,6 +417,8 @@ int main(int argc, char **argv) } } + qemu_host_page_mask = -qemu_host_page_size; + /* init debug */ { int mask = 0; @@ -432,6 +444,7 @@ int main(int argc, char **argv) exit(1); } trace_init_file(); + qemu_plugin_load_list(&plugins, &error_fatal); /* Zero out regs */ memset(regs, 0, sizeof(struct target_pt_regs)); @@ -459,13 +472,55 @@ int main(int argc, char **argv) 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 (reserved_va != 0) { + guest_addr_max = reserved_va; + } else if (MIN(TARGET_VIRT_ADDR_SPACE_BITS, TARGET_ABI_BITS) <= 32) { + guest_addr_max = UINT32_MAX; + } else { + guest_addr_max = ~0ul; + } + if (getenv("QEMU_STRACE")) { do_strace = 1; } @@ -575,6 +630,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); @@ -586,13 +642,12 @@ 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); + gdbserver_start(gdbstub, &error_fatal); } cpu_loop(env); /* never exits */ diff --git a/bsd-user/meson.build b/bsd-user/meson.build index 5243122fc56..37b7cd6de87 100644 --- a/bsd-user/meson.build +++ b/bsd-user/meson.build @@ -7,16 +7,24 @@ 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', 'mmap.c', + 'plugin-api.c', 'signal.c', 'strace.c', '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 8e148a2ea3e..47e317517cb 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -17,6 +17,9 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" +#include "exec/mmap-lock.h" +#include "exec/page-protection.h" +#include "user/page-protection.h" #include "qemu.h" @@ -96,7 +99,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; @@ -107,7 +110,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; @@ -127,6 +130,40 @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot) 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 * @@ -174,7 +211,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) { @@ -189,7 +226,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; } @@ -239,8 +276,7 @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size, * It must be called with mmap_lock() held. * Return -1 if error. */ -static abi_ulong mmap_find_vma_aligned(abi_ulong start, abi_ulong size, - abi_ulong alignment) +abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong alignment) { void *ptr, *prev; abi_ulong addr; @@ -359,11 +395,6 @@ static abi_ulong mmap_find_vma_aligned(abi_ulong start, abi_ulong size, } } -abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size) -{ - return mmap_find_vma_aligned(start, size, 0); -} - /* NOTE: all the constants are the HOST ones */ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, int flags, int fd, off_t offset) @@ -453,13 +484,12 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, * before we truncate the length for mapping files below. */ if (!(flags & MAP_FIXED)) { + abi_ulong alignment; + host_len = len + offset - host_offset; host_len = HOST_PAGE_ALIGN(host_len); - if ((flags & MAP_ALIGNMENT_MASK) != 0) - start = mmap_find_vma_aligned(real_start, host_len, - (flags & MAP_ALIGNMENT_MASK) >> MAP_ALIGNMENT_SHIFT); - else - start = mmap_find_vma(real_start, host_len); + alignment = (flags & MAP_ALIGNMENT_MASK) >> MAP_ALIGNMENT_SHIFT; + start = mmap_find_vma(real_start, host_len, alignment); if (start == (abi_ulong)-1) { errno = ENOMEM; goto fail; @@ -564,7 +594,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)) { @@ -636,7 +666,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, 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; diff --git a/bsd-user/netbsd/target_os_elf.h b/bsd-user/netbsd/target_os_elf.h index 2f3cb208718..9de0f290c0c 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 6dca9c5a853..4cf5747dcd4 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/plugin-api.c b/bsd-user/plugin-api.c new file mode 100644 index 00000000000..6ccef7eaa04 --- /dev/null +++ b/bsd-user/plugin-api.c @@ -0,0 +1,15 @@ +/* + * QEMU Plugin API - bsd-user-mode only implementations + * + * Common user-mode only APIs are in plugins/api-user. These helpers + * are only specific to bsd-user. + * + * Copyright (C) 2017, Emilio G. Cota + * Copyright (C) 2019-2025, Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "common-user/plugin-api.c.inc" diff --git a/bsd-user/qemu-bsd.h b/bsd-user/qemu-bsd.h new file mode 100644 index 00000000000..ffc64bb244a --- /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 d3158bc2edd..93388e7c34e 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -17,18 +17,21 @@ #ifndef QEMU_H #define QEMU_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 "accel/tcg/cpu-ldst.h" -#include "exec/user/abitypes.h" +#include "user/abitypes.h" +#include "user/cpu_loop.h" +#include "user/page-protection.h" extern char **environ; -#include "exec/user/thunk.h" +#include "user/thunk.h" +#include "user/mmap.h" #include "target_arch.h" #include "syscall_defs.h" #include "target_syscall.h" @@ -36,9 +39,17 @@ extern char **environ; #include "target_os_signal.h" #include "target.h" #include "exec/gdbstub.h" -#include "qemu/clang-tsa.h" +#include "exec/page-protection.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, * it replicates in user space what would be certain task_struct fields in the @@ -70,7 +81,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; @@ -108,15 +119,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 @@ -175,11 +187,10 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1, abi_long arg5, abi_long arg6); void gemu_log(const char *fmt, ...) G_GNUC_PRINTF(1, 2); extern __thread CPUState *thread_cpu; -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" @@ -222,18 +233,8 @@ void print_taken_signal(int target_signum, const target_siginfo_t *tinfo); extern int do_strace; /* mmap.c */ -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, 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); int target_msync(abi_ulong start, abi_ulong len, int flags); -extern abi_ulong mmap_next_start; -abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size); -void TSA_NO_TSA mmap_fork_start(void); -void TSA_NO_TSA mmap_fork_end(int child); +void mmap_reserve(abi_ulong start, abi_ulong size); /* main.c */ extern char qemu_proc_pathname[]; @@ -249,6 +250,12 @@ 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); @@ -417,7 +424,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); @@ -441,7 +448,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 00000000000..10c940cd491 --- /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 00000000000..036ddd185e2 --- /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 00000000000..26ce07f343f --- /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 00000000000..44e25d2ddf5 --- /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 00000000000..ef92f004803 --- /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 G_NORETURN 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 00000000000..4eb915e61ec --- /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 00000000000..12b1c96b611 --- /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 00000000000..1a634b865b3 --- /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 00000000000..dfe50767390 --- /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 00000000000..9af42331b4d --- /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 00000000000..95cd0b6ad7e --- /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 00000000000..0f2486def1d --- /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 00000000000..e7e52313095 --- /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 6f90345bb2a..4e634e04a30 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -35,13 +35,13 @@ 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); void queue_signal(CPUArchState *env, int sig, int si_type, target_siginfo_t *info); void signal_init(void); -int target_to_host_signal(int sig); void target_to_host_sigset(sigset_t *d, const target_sigset_t *s); /* @@ -49,11 +49,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 4db85a3485e..dadcc037dc3 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -21,12 +21,20 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu.h" +#include "user/cpu_loop.h" +#include "exec/page-protection.h" +#include "user/page-protection.h" +#include "user/signal.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 "accel/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,8 +51,10 @@ static inline int sas_ss_flags(TaskState *ts, unsigned long sp) on_sig_stack(ts, sp) ? SS_ONSTACK : 0; } +int host_interrupt_signal = SIGRTMAX; + /* - * 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. */ @@ -241,7 +251,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 @@ -311,15 +321,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(). @@ -351,9 +367,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; @@ -415,7 +431,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); @@ -425,7 +441,6 @@ void queue_signal(CPUArchState *env, int sig, int si_type, ts->sync_signal.pending = sig; /* Signal that a new signal is pending. */ qatomic_set(&ts->signal_pending, 1); - return; } static int fatal_signal(int sig) @@ -457,21 +472,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; @@ -479,6 +492,12 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) uintptr_t pc = 0; bool sync_sig = false; + if (host_sig == host_interrupt_signal) { + ts->signal_pending = 1; + cpu_exit(thread_cpu); + return; + } + /* * Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special * handling wrt signal blocking and unwinding. @@ -580,7 +599,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; @@ -709,7 +728,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 */ @@ -719,14 +738,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 */ @@ -784,7 +796,7 @@ 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++) { __get_user(target_set.__bits[i], &ucontext->uc_sigmask.__bits[i]); @@ -834,7 +846,7 @@ long do_sigreturn(CPUArchState *env, abi_ulong addr) 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; @@ -848,12 +860,10 @@ void signal_init(void) act.sa_flags = SA_SIGINFO; for (i = 1; i <= TARGET_NSIG; i++) { -#ifdef CONFIG_GPROF - if (i == TARGET_SIGPROF) { + host_sig = target_to_host_signal(i); + if (host_sig == host_interrupt_signal) { 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; @@ -872,13 +882,14 @@ void signal_init(void) sigaction(host_sig, &act, NULL); } } + sigaction(host_interrupt_signal, &act, NULL); } 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; @@ -890,7 +901,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; @@ -967,7 +978,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); @@ -1019,10 +1030,10 @@ void process_pending_signals(CPUArchState *env) ts->in_sigsuspend = false; } -void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, +void cpu_loop_exit_sigsegv(CPUState *cpu, vaddr 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->cc->tcg_ops; if (tcg_ops->record_sigsegv) { tcg_ops->record_sigsegv(cpu, addr, access_type, maperr, ra); @@ -1035,10 +1046,10 @@ void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, cpu_loop_exit_restore(cpu, ra); } -void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, +void cpu_loop_exit_sigbus(CPUState *cpu, vaddr addr, MMUAccessType access_type, uintptr_t ra) { - const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + const TCGCPUOps *tcg_ops = cpu->cc->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 96499751eb0..6dc01d3be7e 100644 --- a/bsd-user/strace.c +++ b/bsd-user/strace.c @@ -22,6 +22,7 @@ #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 9c90616baae..52f84d5dd17 100644 --- a/bsd-user/syscall_defs.h +++ b/bsd-user/syscall_defs.h @@ -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 */ @@ -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 @@ -390,6 +425,52 @@ struct target_freebsd_flock { 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) \ { \ diff --git a/bsd-user/x86_64/target_arch_cpu.h b/bsd-user/x86_64/target_arch_cpu.h index 4094d61da1a..f82042e30af 100644 --- a/bsd-user/x86_64/target_arch_cpu.h +++ b/bsd-user/x86_64/target_arch_cpu.h @@ -110,7 +110,7 @@ static inline void target_cpu_init(CPUX86State *env, cpu_x86_load_seg(env, R_GS, 0); } -static inline void target_cpu_loop(CPUX86State *env) +static inline G_NORETURN void target_cpu_loop(CPUX86State *env) { CPUState *cs = env_cpu(env); int trapnr; diff --git a/bsd-user/x86_64/target_arch_signal.h b/bsd-user/x86_64/target_arch_signal.h index ca24bf1e7f7..f833ee66cef 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 52c28906d6d..7739bb2154b 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/chardev/baum.c b/chardev/baum.c index a1d9784d92d..f3e8cd27f06 100644 --- a/chardev/baum.c +++ b/chardev/baum.c @@ -668,7 +668,7 @@ static void baum_chr_open(Chardev *chr, qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum); } -static void char_braille_class_init(ObjectClass *oc, void *data) +static void char_braille_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-console.c b/chardev/char-console.c index 6c4ce5dbce7..7e1bf642ebd 100644 --- a/chardev/char-console.c +++ b/chardev/char-console.c @@ -34,7 +34,7 @@ static void qemu_chr_open_win_con(Chardev *chr, win_chr_set_file(chr, GetStdHandle(STD_OUTPUT_HANDLE), true); } -static void char_console_class_init(ObjectClass *oc, void *data) +static void char_console_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-fd.c b/chardev/char-fd.c index d2c49233598..6f03adf8725 100644 --- a/chardev/char-fd.c +++ b/chardev/char-fd.c @@ -50,7 +50,7 @@ static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) Chardev *chr = CHARDEV(opaque); FDChardev *s = FD_CHARDEV(opaque); int len; - uint8_t buf[CHR_READ_BUF_LEN]; + QEMU_UNINITIALIZED uint8_t buf[CHR_READ_BUF_LEN]; ssize_t ret; len = sizeof(buf); @@ -238,7 +238,7 @@ void qemu_chr_open_fd(Chardev *chr, } } -static void char_fd_class_init(ObjectClass *oc, void *data) +static void char_fd_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-fe.c b/chardev/char-fe.c index 7789f7be9c8..158a5f4f551 100644 --- a/chardev/char-fe.c +++ b/chardev/char-fe.c @@ -24,8 +24,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "chardev/char-fe.h" #include "chardev/char-io.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 263e6da5636..a9e8c5e0d7f 100644 --- a/chardev/char-file.c +++ b/chardev/char-file.c @@ -123,7 +123,7 @@ static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend, file->append = qemu_opt_get_bool(opts, "append", false); } -static void char_file_class_init(ObjectClass *oc, void *data) +static void char_file_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-hmp-cmds.c b/chardev/char-hmp-cmds.c index 287c2b1bcd8..8e9e1c1c021 100644 --- a/chardev/char-hmp-cmds.c +++ b/chardev/char-hmp-cmds.c @@ -19,7 +19,7 @@ #include "monitor/monitor.h" #include "qapi/error.h" #include "qapi/qapi-commands-char.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/config-file.h" #include "qemu/option.h" diff --git a/chardev/char-hub.c b/chardev/char-hub.c new file mode 100644 index 00000000000..16ffee2017a --- /dev/null +++ b/chardev/char-hub.c @@ -0,0 +1,301 @@ +/* + * QEMU Character Hub Device + * + * Author: Roman Penyaev + * + * 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/option.h" +#include "chardev/char.h" +#include "chardev-internal.h" + +/* + * Character hub device aggregates input from multiple backend devices + * and forwards it to a single frontend device. Additionally, hub + * device takes the output from the frontend device and sends it back + * to all the connected backend devices. + */ + +/* + * Write to all backends. Different backend devices accept data with + * various rate, so it is quite possible that one device returns less, + * then others. In this case we return minimum to the caller, + * expecting caller will repeat operation soon. When repeat happens + * send to the devices which consume data faster must be avoided + * for obvious reasons not to send data, which was already sent. + * Called with chr_write_lock held. + */ +static int hub_chr_write(Chardev *chr, const uint8_t *buf, int len) +{ + HubChardev *d = HUB_CHARDEV(chr); + int r, i, ret = len; + unsigned int written; + + /* Invalidate index on every write */ + d->be_eagain_ind = -1; + + for (i = 0; i < d->be_cnt; i++) { + if (!d->backends[i].be.chr->be_open) { + /* Skip closed backend */ + continue; + } + written = d->be_written[i] - d->be_min_written; + if (written) { + /* Written in the previous call so take into account */ + ret = MIN(written, ret); + continue; + } + r = qemu_chr_fe_write(&d->backends[i].be, buf, len); + if (r < 0) { + if (errno == EAGAIN) { + /* Set index and expect to be called soon on watch wake up */ + d->be_eagain_ind = i; + } + return r; + } + d->be_written[i] += r; + ret = MIN(r, ret); + } + d->be_min_written += ret; + + + return ret; +} + +static int hub_chr_can_read(void *opaque) +{ + HubCharBackend *backend = opaque; + CharBackend *fe = backend->hub->parent.be; + + if (fe && fe->chr_can_read) { + return fe->chr_can_read(fe->opaque); + } + + return 0; +} + +static void hub_chr_read(void *opaque, const uint8_t *buf, int size) +{ + HubCharBackend *backend = opaque; + CharBackend *fe = backend->hub->parent.be; + + if (fe && fe->chr_read) { + fe->chr_read(fe->opaque, buf, size); + } +} + +static void hub_chr_event(void *opaque, QEMUChrEvent event) +{ + HubCharBackend *backend = opaque; + HubChardev *d = backend->hub; + CharBackend *fe = d->parent.be; + + if (event == CHR_EVENT_OPENED) { + /* + * Catch up with what was already written while this backend + * was closed + */ + d->be_written[backend->be_ind] = d->be_min_written; + + if (d->be_event_opened_cnt++) { + /* Ignore subsequent open events from other backends */ + return; + } + } else if (event == CHR_EVENT_CLOSED) { + if (!d->be_event_opened_cnt) { + /* Don't go below zero. Probably assert is better */ + return; + } + if (--d->be_event_opened_cnt) { + /* Serve only the last one close event */ + return; + } + } + + if (fe && fe->chr_event) { + fe->chr_event(fe->opaque, event); + } +} + +static GSource *hub_chr_add_watch(Chardev *s, GIOCondition cond) +{ + HubChardev *d = HUB_CHARDEV(s); + Chardev *chr; + ChardevClass *cc; + + if (d->be_eagain_ind == -1) { + return NULL; + } + + assert(d->be_eagain_ind < d->be_cnt); + chr = qemu_chr_fe_get_driver(&d->backends[d->be_eagain_ind].be); + cc = CHARDEV_GET_CLASS(chr); + if (!cc->chr_add_watch) { + return NULL; + } + + return cc->chr_add_watch(chr, cond); +} + +static bool hub_chr_attach_chardev(HubChardev *d, Chardev *chr, + Error **errp) +{ + bool ret; + + if (d->be_cnt >= MAX_HUB) { + error_setg(errp, "hub: too many uses of chardevs '%s'" + " (maximum is " stringify(MAX_HUB) ")", + d->parent.label); + return false; + } + ret = qemu_chr_fe_init(&d->backends[d->be_cnt].be, chr, errp); + if (ret) { + d->backends[d->be_cnt].hub = d; + d->backends[d->be_cnt].be_ind = d->be_cnt; + d->be_cnt += 1; + } + + return ret; +} + +static void char_hub_finalize(Object *obj) +{ + HubChardev *d = HUB_CHARDEV(obj); + int i; + + for (i = 0; i < d->be_cnt; i++) { + qemu_chr_fe_deinit(&d->backends[i].be, false); + } +} + +static void hub_chr_update_read_handlers(Chardev *chr) +{ + HubChardev *d = HUB_CHARDEV(chr); + int i; + + for (i = 0; i < d->be_cnt; i++) { + qemu_chr_fe_set_handlers_full(&d->backends[i].be, + hub_chr_can_read, + hub_chr_read, + hub_chr_event, + NULL, + &d->backends[i], + chr->gcontext, true, false); + } +} + +static void qemu_chr_open_hub(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) +{ + ChardevHub *hub = backend->u.hub.data; + HubChardev *d = HUB_CHARDEV(chr); + strList *list = hub->chardevs; + + d->be_eagain_ind = -1; + + if (list == NULL) { + error_setg(errp, "hub: 'chardevs' list is not defined"); + return; + } + + while (list) { + Chardev *s; + + s = qemu_chr_find(list->value); + if (s == NULL) { + error_setg(errp, "hub: chardev can't be found by id '%s'", + list->value); + return; + } + if (CHARDEV_IS_HUB(s) || CHARDEV_IS_MUX(s)) { + error_setg(errp, "hub: multiplexers and hub devices can't be " + "stacked, check chardev '%s', chardev should not " + "be a hub device or have 'mux=on' enabled", + list->value); + return; + } + if (!hub_chr_attach_chardev(d, s, errp)) { + return; + } + list = list->next; + } + + /* Closed until an explicit event from backend */ + *be_opened = false; +} + +static void qemu_chr_parse_hub(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + ChardevHub *hub; + strList **tail; + int i; + + backend->type = CHARDEV_BACKEND_KIND_HUB; + hub = backend->u.hub.data = g_new0(ChardevHub, 1); + qemu_chr_parse_common(opts, qapi_ChardevHub_base(hub)); + + tail = &hub->chardevs; + + for (i = 0; i < MAX_HUB; i++) { + char optbuf[16]; + const char *dev; + + snprintf(optbuf, sizeof(optbuf), "chardevs.%u", i); + dev = qemu_opt_get(opts, optbuf); + if (!dev) { + break; + } + + QAPI_LIST_APPEND(tail, g_strdup(dev)); + } +} + +static void char_hub_class_init(ObjectClass *oc, const void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->parse = qemu_chr_parse_hub; + cc->open = qemu_chr_open_hub; + cc->chr_write = hub_chr_write; + cc->chr_add_watch = hub_chr_add_watch; + /* We handle events from backends only */ + cc->chr_be_event = NULL; + cc->chr_update_read_handler = hub_chr_update_read_handlers; +} + +static const TypeInfo char_hub_type_info = { + .name = TYPE_CHARDEV_HUB, + .parent = TYPE_CHARDEV, + .class_init = char_hub_class_init, + .instance_size = sizeof(HubChardev), + .instance_finalize = char_hub_finalize, +}; + +static void register_types(void) +{ + type_register_static(&char_hub_type_info); +} + +type_init(register_types); diff --git a/chardev/char-io.c b/chardev/char-io.c index 4451128cba5..3be17b51ca5 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 ee2d47b20d9..6b36290e2c4 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -26,8 +26,9 @@ #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 "system/block-backend.h" #include "qapi/qapi-commands-control.h" #include "chardev-internal.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); @@ -402,7 +447,7 @@ void resume_mux_open(void) chardev_options_parsed_cb, NULL); } -static void char_mux_class_init(ObjectClass *oc, void *data) +static void char_mux_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-null.c b/chardev/char-null.c index 1c6a2900f9b..89cb85da792 100644 --- a/chardev/char-null.c +++ b/chardev/char-null.c @@ -34,7 +34,7 @@ static void null_chr_open(Chardev *chr, *be_opened = false; } -static void char_null_class_init(ObjectClass *oc, void *data) +static void char_null_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-parallel.c b/chardev/char-parallel.c index a5164f975af..62a44b2f969 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,6 +238,7 @@ static void qemu_chr_open_pp_fd(Chardev *chr, } #endif +#ifdef HAVE_CHARDEV_PARALLEL static void qmp_chardev_open_parallel(Chardev *chr, ChardevBackend *backend, bool *be_opened, @@ -269,7 +270,7 @@ static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend, parallel->device = g_strdup(device); } -static void char_parallel_class_init(ObjectClass *oc, void *data) +static void char_parallel_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); @@ -306,3 +307,5 @@ static void register_types(void) } type_init(register_types); + +#endif /* HAVE_CHARDEV_PARALLEL */ diff --git a/chardev/char-pipe.c b/chardev/char-pipe.c index 5ad30bcc599..3d1b0ce2d2e 100644 --- a/chardev/char-pipe.c +++ b/chardev/char-pipe.c @@ -171,7 +171,7 @@ static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend, dev->device = g_strdup(device); } -static void char_pipe_class_init(ObjectClass *oc, void *data) +static void char_pipe_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-pty.c b/chardev/char-pty.c index 4e5deac18ae..674e9b3f144 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; @@ -106,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); + } + + /* + * 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 io_channel_send(s->ioc, buf, len); + + return len; } static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond) @@ -136,7 +154,7 @@ static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) Chardev *chr = CHARDEV(opaque); PtyChardev *s = PTY_CHARDEV(opaque); gsize len; - uint8_t buf[CHR_READ_BUF_LEN]; + QEMU_UNINITIALIZED uint8_t buf[CHR_READ_BUF_LEN]; ssize_t ret; len = sizeof(buf); @@ -163,6 +181,9 @@ static void pty_chr_state(Chardev *chr, int connected) if (!connected) { remove_fd_in_watch(chr); + if (s->connected) { + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); + } s->connected = 0; /* (re-)connect poll interval for idle guests: once per second. * We check more frequently in case the guests sends data to @@ -188,10 +209,15 @@ 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); - qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } #if defined HAVE_PTY_H @@ -314,6 +340,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) { @@ -338,12 +365,36 @@ static void char_pty_open(Chardev *chr, 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) +static void char_pty_class_init(ObjectClass *oc, const 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-ringbuf.c b/chardev/char-ringbuf.c index d40d21d3cf7..98aadb6acfb 100644 --- a/chardev/char-ringbuf.c +++ b/chardev/char-ringbuf.c @@ -223,7 +223,7 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend, } } -static void char_ringbuf_class_init(ObjectClass *oc, void *data) +static void char_ringbuf_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-serial.c b/chardev/char-serial.c index 4b0b83d5b45..0a68b4b4e0b 100644 --- a/chardev/char-serial.c +++ b/chardev/char-serial.c @@ -298,7 +298,7 @@ static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend, serial->device = g_strdup(device); } -static void char_serial_class_init(ObjectClass *oc, void *data) +static void char_serial_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 73947da188d..1e8313915b5 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, - 0, NULL); + 0, &err); } else { ret = qio_channel_readv_full(s->ioc, &iov, 1, NULL, NULL, - 0, 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); } } @@ -485,7 +497,7 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) { Chardev *chr = CHARDEV(opaque); SocketChardev *s = SOCKET_CHARDEV(opaque); - uint8_t buf[CHR_READ_BUF_LEN]; + QEMU_UNINITIALIZED uint8_t buf[CHR_READ_BUF_LEN]; int len, size; if ((s->state != TCP_CHARDEV_STATE_CONNECTED) || @@ -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; } @@ -558,9 +571,13 @@ static char *qemu_chr_compute_filename(SocketChardev *s) switch (ss->ss_family) { case AF_UNIX: - return g_strdup_printf("unix:%s%s", - ((struct sockaddr_un *)(ss))->sun_path, - s->is_listen ? ",server=on" : ""); + if (s->is_listen) { + return g_strdup_printf("unix:%s,server=on", + ((struct sockaddr_un *)(ss))->sun_path); + } else { + return g_strdup_printf("unix:%s", + ((struct sockaddr_un *)(ps))->sun_path); + } case AF_INET6: left = "["; right = "]"; @@ -597,6 +614,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 +685,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; } @@ -745,9 +781,9 @@ static void tcp_chr_websock_handshake(QIOTask *task, gpointer user_data) Error *err = NULL; if (qio_task_propagate_error(task, &err)) { - error_reportf_err(err, - "websock handshake of character device %s failed: ", - chr->label); + 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) { @@ -785,9 +821,9 @@ static void tcp_chr_tls_handshake(QIOTask *task, Error *err = NULL; if (qio_task_propagate_error(task, &err)) { - error_reportf_err(err, - "TLS handshake of character device %s failed: ", - chr->label); + 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) { @@ -806,19 +842,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; } @@ -1045,9 +1084,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; @@ -1232,13 +1271,13 @@ static int qmp_chardev_open_socket_server(Chardev *chr, 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 { @@ -1319,6 +1358,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; } @@ -1336,7 +1381,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; @@ -1408,7 +1453,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; } } @@ -1474,6 +1525,9 @@ 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_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->tls_authz = g_strdup(qemu_opt_get(opts, "tls-authz")); @@ -1504,7 +1558,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; @@ -1527,7 +1581,7 @@ char_socket_get_connected(Object *obj, Error **errp) return s->state == TCP_CHARDEV_STATE_CONNECTED; } -static void char_socket_class_init(ObjectClass *oc, void *data) +static void char_socket_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c index 3c648678ab1..48db8d2f30f 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"); @@ -132,7 +136,7 @@ static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend, stdio->signal = qemu_opt_get_bool(opts, "signal", true); } -static void char_stdio_class_init(ObjectClass *oc, void *data) +static void char_stdio_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-udp.c b/chardev/char-udp.c index 3d9a2d5e772..572fab0ad13 100644 --- a/chardev/char-udp.c +++ b/chardev/char-udp.c @@ -219,7 +219,7 @@ static void qmp_chardev_open_udp(Chardev *chr, *be_opened = false; } -static void char_udp_class_init(ObjectClass *oc, void *data) +static void char_udp_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-win-stdio.c b/chardev/char-win-stdio.c index 1a18999e78a..fb802a00b13 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, @@ -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); } @@ -251,7 +256,7 @@ static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len) return len - len1; } -static void char_win_stdio_class_init(ObjectClass *oc, void *data) +static void char_win_stdio_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-win.c b/chardev/char-win.c index d4fb44c4dcc..fef45e83aad 100644 --- a/chardev/char-win.c +++ b/chardev/char-win.c @@ -220,7 +220,7 @@ void win_chr_set_file(Chardev *chr, HANDLE file, bool keep_open) s->file = file; } -static void char_win_class_init(ObjectClass *oc, void *data) +static void char_win_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char.c b/chardev/char.c index 996a024c7a2..bbebd246c3a 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -33,7 +33,7 @@ #include "qapi/error.h" #include "qapi/qapi-commands-char.h" #include "qapi/qmp/qerror.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "qemu/help_option.h" #include "qemu/module.h" #include "qemu/option.h" @@ -48,7 +48,7 @@ Object *get_chardevs_root(void) { - return container_get(object_get_root(), "/chardevs"); + return object_get_container("chardevs"); } static void chr_be_event(Chardev *s, QEMUChrEvent event) @@ -171,6 +171,18 @@ int qemu_chr_write(Chardev *s, const uint8_t *buf, int len, bool write_all) return res; } + if (replay_mode == REPLAY_MODE_RECORD) { + /* + * When recording we don't want temporary conditions to + * perturb the result. By ensuring we write everything we can + * while recording we avoid playback being out of sync if it + * doesn't encounter the same temporary conditions (usually + * triggered by external programs not reading the chardev fast + * enough and pipes filling up). + */ + write_all = true; + } + res = qemu_chr_write_buffer(s, buf, len, &offset, write_all); if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) { @@ -283,7 +295,7 @@ static int null_chr_write(Chardev *chr, const uint8_t *buf, int len) return len; } -static void char_class_init(ObjectClass *oc, void *data) +static void char_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); @@ -321,7 +333,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; } @@ -416,6 +428,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) || @@ -518,7 +535,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; } @@ -603,11 +620,24 @@ 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 = qemu_opt_get(opts, "backend"); const char *id = qemu_opts_id(opts); @@ -645,11 +675,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); @@ -669,11 +699,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; @@ -681,14 +725,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; @@ -710,24 +762,20 @@ Chardev *qemu_chr_new_noreplay(const char *label, const char *filename, 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, @@ -750,7 +798,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); @@ -847,6 +895,9 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "reconnect", .type = QEMU_OPT_NUMBER, + },{ + .name = "reconnect-ms", + .type = QEMU_OPT_NUMBER, },{ .name = "telnet", .type = QEMU_OPT_BOOL, @@ -892,7 +943,26 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "chardev", .type = QEMU_OPT_STRING, + }, + /* + * Multiplexer options. Follows QAPI array syntax. + * See MAX_HUB macro to obtain array capacity. + */ + { + .name = "chardevs.0", + .type = QEMU_OPT_STRING, + },{ + .name = "chardevs.1", + .type = QEMU_OPT_STRING, + },{ + .name = "chardevs.2", + .type = QEMU_OPT_STRING, },{ + .name = "chardevs.3", + .type = QEMU_OPT_STRING, + }, + + { .name = "append", .type = QEMU_OPT_BOOL, },{ @@ -1055,8 +1125,8 @@ ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend, return NULL; } - if (CHARDEV_IS_MUX(chr)) { - error_setg(errp, "Mux device hotswap not supported yet"); + if (CHARDEV_IS_MUX(chr) || CHARDEV_IS_HUB(chr)) { + error_setg(errp, "For mux or hub device hotswap is not supported yet"); return NULL; } diff --git a/chardev/chardev-internal.h b/chardev/chardev-internal.h index 4e03af31476..9752dd75f71 100644 --- a/chardev/chardev-internal.h +++ b/chardev/chardev-internal.h @@ -29,38 +29,89 @@ #include "chardev/char-fe.h" #include "qom/object.h" +#define MAX_HUB 4 #define MAX_MUX 4 #define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */ #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1) struct MuxChardev { Chardev parent; + /* Linked frontends */ CharBackend *backends[MAX_MUX]; + /* Linked backend */ 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; +typedef struct HubChardev HubChardev; +typedef struct HubCharBackend HubCharBackend; + +/* + * Back-pointer on a hub, actual backend and its index in + * `hub->backends` array + */ +struct HubCharBackend { + HubChardev *hub; + CharBackend be; + unsigned int be_ind; +}; + +struct HubChardev { + Chardev parent; + /* Linked backends */ + HubCharBackend backends[MAX_HUB]; + /* + * Number of backends attached to this hub. Once attached, a + * backend can't be detached, so the counter is only increasing. + * To safely remove a backend, hub has to be removed first. + */ + unsigned int be_cnt; + /* + * Number of CHR_EVEN_OPENED events from all backends. Needed to + * send CHR_EVEN_CLOSED only when counter goes to zero. + */ + unsigned int be_event_opened_cnt; + /* + * Counters of written bytes from a single frontend device + * to multiple backend devices. + */ + unsigned int be_written[MAX_HUB]; + unsigned int be_min_written; + /* + * Index of a backend device which got EAGAIN on last write, + * -1 is invalid index. + */ + int be_eagain_ind; +}; +typedef struct HubChardev HubChardev; DECLARE_INSTANCE_CHECKER(MuxChardev, MUX_CHARDEV, TYPE_CHARDEV_MUX) -#define CHARDEV_IS_MUX(chr) \ +DECLARE_INSTANCE_CHECKER(HubChardev, HUB_CHARDEV, + TYPE_CHARDEV_HUB) + +#define CHARDEV_IS_MUX(chr) \ object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX) +#define CHARDEV_IS_HUB(chr) \ + object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_HUB) -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 6d56ad32fdb..56ee39ac0b0 100644 --- a/chardev/meson.build +++ b/chardev/meson.build @@ -3,6 +3,7 @@ chardev_ss.add(files( 'char-file.c', 'char-io.c', 'char-mux.c', + 'char-hub.c', 'char-null.c', 'char-pipe.c', 'char-ringbuf.c', @@ -12,21 +13,21 @@ chardev_ss.add(files( 'char-udp.c', 'char.c', )) -chardev_ss.add(when: 'CONFIG_POSIX', if_true: [files( - 'char-fd.c', - 'char-pty.c', -), util]) -if targetos in ['linux', 'gnu/kfreebsd', 'freebsd', 'dragonfly'] - chardev_ss.add(files('char-parallel.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.add(when: 'CONFIG_WIN32', if_true: files( - 'char-console.c', - 'char-win-stdio.c', - 'char-win.c', -)) - -chardev_ss = chardev_ss.apply(config_targetos, strict: false) +chardev_ss = chardev_ss.apply({}) system_ss.add(files( 'char-hmp-cmds.c', diff --git a/chardev/msmouse.c b/chardev/msmouse.c index ab8fe981d63..1a55755d397 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, @@ -267,7 +267,7 @@ static void msmouse_chr_open(Chardev *chr, fifo8_create(&mouse->outbuf, MSMOUSE_BUF_SZ); } -static void char_msmouse_class_init(ObjectClass *oc, void *data) +static void char_msmouse_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/spice.c b/chardev/spice.c index e843d961a78..db53b49da26 100644 --- a/chardev/spice.c +++ b/chardev/spice.c @@ -347,7 +347,7 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend, spiceport->fqdn = g_strdup(name); } -static void char_spice_class_init(ObjectClass *oc, void *data) +static void char_spice_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); @@ -366,7 +366,7 @@ static const TypeInfo char_spice_type_info = { }; module_obj(TYPE_CHARDEV_SPICE); -static void char_spicevmc_class_init(ObjectClass *oc, void *data) +static void char_spicevmc_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); @@ -382,7 +382,7 @@ static const TypeInfo char_spicevmc_type_info = { }; module_obj(TYPE_CHARDEV_SPICEVMC); -static void char_spiceport_class_init(ObjectClass *oc, void *data) +static void char_spiceport_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/testdev.c b/chardev/testdev.c index a92caca3c33..e91f4e8343a 100644 --- a/chardev/testdev.c +++ b/chardev/testdev.c @@ -110,7 +110,7 @@ static int testdev_chr_write(Chardev *chr, const uint8_t *buf, int len) return orig_len; } -static void char_testdev_class_init(ObjectClass *oc, void *data) +static void char_testdev_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/trace-events b/chardev/trace-events index 027107b0c10..7e97b8a988c 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 43bdf6b6083..0dc6ef08f59 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, @@ -342,7 +342,7 @@ static void wctablet_chr_open(Chardev *chr, &wctablet_handler); } -static void wctablet_chr_class_init(ObjectClass *oc, void *data) +static void wctablet_chr_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 00000000000..90161729838 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,3 @@ +doc-valid-idents = ["IrDA", "PrimeCell", ".."] +allow-mixed-uninlined-format-args = false +msrv = "1.77.0" diff --git a/common-user/host/riscv/safe-syscall.inc.S b/common-user/host/riscv/safe-syscall.inc.S index dfe83c300eb..c8b81e33d0b 100644 --- a/common-user/host/riscv/safe-syscall.inc.S +++ b/common-user/host/riscv/safe-syscall.inc.S @@ -69,11 +69,11 @@ safe_syscall_end: /* code path setting errno */ 0: neg a0, a0 - j safe_syscall_set_errno_tail + tail safe_syscall_set_errno_tail /* code path when we didn't execute the syscall */ 2: li a0, QEMU_ERESTARTSYS - j safe_syscall_set_errno_tail + tail safe_syscall_set_errno_tail .cfi_endproc .size safe_syscall_base, .-safe_syscall_base diff --git a/common-user/plugin-api.c.inc b/common-user/plugin-api.c.inc new file mode 100644 index 00000000000..5b8a1396b60 --- /dev/null +++ b/common-user/plugin-api.c.inc @@ -0,0 +1,43 @@ +/* + * QEMU Plugin API - *-user-mode only implementations + * + * Common user-mode only APIs are in plugins/api-user. These helpers + * are only specific to the *-user frontends. + * + * Copyright (C) 2017, Emilio G. Cota + * Copyright (C) 2019-2025, Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "qemu/plugin.h" +#include "qemu.h" + +/* + * Binary path, start and end locations. Host specific due to TaskState. + */ +const char *qemu_plugin_path_to_binary(void) +{ + TaskState *ts = get_task_state(current_cpu); + return g_strdup(ts->bprm->filename); +} + +uint64_t qemu_plugin_start_code(void) +{ + TaskState *ts = get_task_state(current_cpu); + return ts->info->start_code; +} + +uint64_t qemu_plugin_end_code(void) +{ + TaskState *ts = get_task_state(current_cpu); + return ts->info->end_code; +} + +uint64_t qemu_plugin_entry_code(void) +{ + TaskState *ts = get_task_state(current_cpu); + return ts->info->entry; +} diff --git a/configs/devices/aarch64-softmmu/default.mak b/configs/devices/aarch64-softmmu/default.mak index f82a04c27d1..ad8028cfd48 100644 --- a/configs/devices/aarch64-softmmu/default.mak +++ b/configs/devices/aarch64-softmmu/default.mak @@ -8,3 +8,5 @@ include ../arm-softmmu/default.mak # CONFIG_XLNX_ZYNQMP_ARM=n # CONFIG_XLNX_VERSAL=n # CONFIG_SBSA_REF=n +# CONFIG_NPCM8XX=n +CONFIG_VMAPPLE=n diff --git a/configs/devices/alpha-softmmu/default.mak b/configs/devices/alpha-softmmu/default.mak index d186fe8e9b1..3de6a9f5779 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 980c48a7d99..57ef1b8a702 100644 --- a/configs/devices/arm-softmmu/default.mak +++ b/configs/devices/arm-softmmu/default.mak @@ -1,9 +1,12 @@ # 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 +# 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. @@ -13,21 +16,16 @@ CONFIG_ARM_VIRT=y # CONFIG_INTEGRATOR=n # CONFIG_FSL_IMX31=n # CONFIG_MUSICPAL=n +# CONFIG_MPS3R=n # CONFIG_MUSCA=n -# CONFIG_CHEETAH=n # CONFIG_SX1=n -# CONFIG_NSERIES=n # CONFIG_STELLARIS=n # CONFIG_STM32VLDISCOVERY=n +# CONFIG_B_L475E_IOT01A=n # CONFIG_REALVIEW=n # CONFIG_VERSATILE=n # CONFIG_VEXPRESS=n # CONFIG_ZYNQ=n -# CONFIG_MAINSTONE=n -# CONFIG_GUMSTIX=n -# CONFIG_SPITZ=n -# CONFIG_TOSA=n -# CONFIG_Z2=n # CONFIG_NPCM7XX=n # CONFIG_COLLIE=n # CONFIG_ASPEED_SOC=n diff --git a/configs/devices/avr-softmmu/default.mak b/configs/devices/avr-softmmu/default.mak index 80218add98c..4207e7b3ce2 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 5932cf4d06f..00000000000 --- 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 b0364bb88f2..059510cdbb7 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 598c6646dfc..bc0479a7e0a 100644 --- a/configs/devices/i386-softmmu/default.mak +++ b/configs/devices/i386-softmmu/default.mak @@ -18,15 +18,16 @@ #CONFIG_QXL=n #CONFIG_SEV=n #CONFIG_SGA=n +#CONFIG_TDX=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 diff --git a/configs/devices/loongarch64-softmmu/default.mak b/configs/devices/loongarch64-softmmu/default.mak index 928bc117ef7..ffe705836fd 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 7f8619e4278..3ceda6b041b 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 db8c6e4bba3..78941064655 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 29f7f13816c..4c1086435bf 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 7da99327a77..b50107feafe 100644 --- a/configs/devices/mips-softmmu/common.mak +++ b/configs/devices/mips-softmmu/common.mak @@ -1,31 +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_I8257=y -CONFIG_IDE_ISA=y -CONFIG_PFLASH_CFI01=y -CONFIG_I8259=y -CONFIG_MC146818RTC=y -CONFIG_MIPS_CPS=y -CONFIG_MIPS_ITU=y -CONFIG_MALTA=y -CONFIG_PCNET_PCI=y -CONFIG_MIPSSIM=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 566672f3c22..1b8d7ced1c6 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 d5188f7ea58..9dce346c4fb 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 1bc4082ea99..00000000000 --- 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 89c39e31237..efe3bc278bc 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 a887f5438b8..460d15e676b 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 b90e5bf4558..e8ad2603133 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 d847bd5692e..c2cd86ce05f 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 bc69301fa4a..39ed3a0061a 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 df2b4e4f426..e7caebe1974 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 6d87bc8b4b0..340c1092922 100644 --- a/configs/devices/s390x-softmmu/default.mak +++ b/configs/devices/s390x-softmmu/default.mak @@ -9,6 +9,5 @@ #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 565e8b0b5df..efb401bfb17 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 ee852181151..87668fda5ea 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 e50030a229c..fa82f39a200 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 cb8fc286eb2..c7ab542244b 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 4fe1bf00c94..fbc3079a943 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/emscripten.txt b/configs/meson/emscripten.txt new file mode 100644 index 00000000000..4230e88005f --- /dev/null +++ b/configs/meson/emscripten.txt @@ -0,0 +1,8 @@ +[built-in options] +c_args = ['-pthread'] +cpp_args = ['-pthread'] +objc_args = ['-pthread'] +# -sPROXY_TO_PTHREAD link time flag always requires -pthread even during +# configuration so explicitly add the flag here. +c_link_args = ['-pthread','-sASYNCIFY=1','-sPROXY_TO_PTHREAD=1','-sFORCE_FILESYSTEM','-sALLOW_TABLE_GROWTH','-sTOTAL_MEMORY=2GB','-sWASM_BIGINT','-sEXPORT_ES6=1','-sASYNCIFY_IMPORTS=ffi_call_js','-sEXPORTED_RUNTIME_METHODS=addFunction,removeFunction,TTY,FS'] +cpp_link_args = ['-pthread','-sASYNCIFY=1','-sPROXY_TO_PTHREAD=1','-sFORCE_FILESYSTEM','-sALLOW_TABLE_GROWTH','-sTOTAL_MEMORY=2GB','-sWASM_BIGINT','-sEXPORT_ES6=1','-sASYNCIFY_IMPORTS=ffi_call_js','-sEXPORTED_RUNTIME_METHODS=addFunction,removeFunction,TTY,FS'] diff --git a/configs/meson/windows.txt b/configs/meson/windows.txt new file mode 100644 index 00000000000..55b192e71b1 --- /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 00000000000..f99c73377a9 --- /dev/null +++ b/configs/targets/aarch64-bsd-user.mak @@ -0,0 +1,4 @@ +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 +TARGET_LONG_BITS=64 diff --git a/configs/targets/aarch64-linux-user.mak b/configs/targets/aarch64-linux-user.mak index ba8bc5fe3fd..b779ac3b4a0 100644 --- a/configs/targets/aarch64-linux-user.mak +++ b/configs/targets/aarch64-linux-user.mak @@ -1,6 +1,9 @@ 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 +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 +TARGET_LONG_BITS=64 diff --git a/configs/targets/aarch64-softmmu.mak b/configs/targets/aarch64-softmmu.mak index b4338e95680..5dfeb35af90 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_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 +TARGET_LONG_BITS=64 diff --git a/configs/targets/aarch64_be-linux-user.mak b/configs/targets/aarch64_be-linux-user.mak index acb5620cdbf..ef9be02290f 100644 --- a/configs/targets/aarch64_be-linux-user.mak +++ b/configs/targets/aarch64_be-linux-user.mak @@ -1,7 +1,10 @@ TARGET_ARCH=aarch64 TARGET_BASE_ARCH=arm TARGET_BIG_ENDIAN=y -TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/aarch64-pauth.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 +TARGET_LONG_BITS=64 diff --git a/configs/targets/alpha-linux-user.mak b/configs/targets/alpha-linux-user.mak index f7d3fb4afa9..ef8e365b093 100644 --- a/configs/targets/alpha-linux-user.mak +++ b/configs/targets/alpha-linux-user.mak @@ -1,3 +1,4 @@ TARGET_ARCH=alpha TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl +TARGET_LONG_BITS=64 diff --git a/configs/targets/alpha-softmmu.mak b/configs/targets/alpha-softmmu.mak index 9dbe1607403..5275076e50d 100644 --- a/configs/targets/alpha-softmmu.mak +++ b/configs/targets/alpha-softmmu.mak @@ -1,2 +1,2 @@ TARGET_ARCH=alpha -TARGET_SUPPORTS_MTTCG=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/arm-bsd-user.mak b/configs/targets/arm-bsd-user.mak index cb143e6426a..472a4f9fb11 100644 --- a/configs/targets/arm-bsd-user.mak +++ b/configs/targets/arm-bsd-user.mak @@ -1,2 +1,3 @@ TARGET_ARCH=arm TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml +TARGET_LONG_BITS=32 diff --git a/configs/targets/arm-linux-user.mak b/configs/targets/arm-linux-user.mak index 7f5d65794c1..bf35ded7fea 100644 --- a/configs/targets/arm-linux-user.mak +++ b/configs/targets/arm-linux-user.mak @@ -5,3 +5,4 @@ TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml TARGET_HAS_BFLT=y CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/arm-softmmu.mak b/configs/targets/arm-softmmu.mak index 92c8349b964..6a5a8eda949 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 +TARGET_LONG_BITS=32 diff --git a/configs/targets/armeb-linux-user.mak b/configs/targets/armeb-linux-user.mak index 943d0d87bfd..35fa4d91b3c 100644 --- a/configs/targets/armeb-linux-user.mak +++ b/configs/targets/armeb-linux-user.mak @@ -6,3 +6,4 @@ TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml TARGET_HAS_BFLT=y CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/avr-softmmu.mak b/configs/targets/avr-softmmu.mak index e3f921c0199..b6157fc465d 100644 --- a/configs/targets/avr-softmmu.mak +++ b/configs/targets/avr-softmmu.mak @@ -1,2 +1,3 @@ TARGET_ARCH=avr TARGET_XML_FILES= gdb-xml/avr-cpu.xml +TARGET_LONG_BITS=32 diff --git a/configs/targets/cris-linux-user.mak b/configs/targets/cris-linux-user.mak deleted file mode 100644 index e483c420669..00000000000 --- 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 e483c420669..00000000000 --- 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 2765a4c5638..aec1a04d1b4 100644 --- a/configs/targets/hexagon-linux-user.mak +++ b/configs/targets/hexagon-linux-user.mak @@ -1,2 +1,5 @@ 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 +TARGET_LONG_BITS=32 diff --git a/configs/targets/hppa-linux-user.mak b/configs/targets/hppa-linux-user.mak index 361ea39d71e..59190f63358 100644 --- a/configs/targets/hppa-linux-user.mak +++ b/configs/targets/hppa-linux-user.mak @@ -1,4 +1,7 @@ TARGET_ARCH=hppa +TARGET_ABI32=y TARGET_SYSTBL_ABI=common,32 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y +# Compromise to ease maintenance vs system mode +TARGET_LONG_BITS=64 diff --git a/configs/targets/hppa-softmmu.mak b/configs/targets/hppa-softmmu.mak index a41662aa996..ea331107a08 100644 --- a/configs/targets/hppa-softmmu.mak +++ b/configs/targets/hppa-softmmu.mak @@ -1,3 +1,3 @@ TARGET_ARCH=hppa TARGET_BIG_ENDIAN=y -TARGET_SUPPORTS_MTTCG=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/i386-bsd-user.mak b/configs/targets/i386-bsd-user.mak index 0283bb62a07..70e098da491 100644 --- a/configs/targets/i386-bsd-user.mak +++ b/configs/targets/i386-bsd-user.mak @@ -1,2 +1,3 @@ TARGET_ARCH=i386 TARGET_XML_FILES= gdb-xml/i386-32bit.xml +TARGET_LONG_BITS=32 diff --git a/configs/targets/i386-linux-user.mak b/configs/targets/i386-linux-user.mak index 5b2546a4309..ea68a266fce 100644 --- a/configs/targets/i386-linux-user.mak +++ b/configs/targets/i386-linux-user.mak @@ -1,4 +1,5 @@ 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 +TARGET_LONG_BITS=32 diff --git a/configs/targets/i386-softmmu.mak b/configs/targets/i386-softmmu.mak index 6b3c99fc86c..e9d89e8ab41 100644 --- a/configs/targets/i386-softmmu.mak +++ b/configs/targets/i386-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=i386 -TARGET_SUPPORTS_MTTCG=y -TARGET_NEED_FDT=y +TARGET_KVM_HAVE_GUEST_DEBUG=y +TARGET_KVM_HAVE_RESET_PARKED_VCPU=y TARGET_XML_FILES= gdb-xml/i386-32bit.xml +TARGET_LONG_BITS=32 diff --git a/configs/targets/loongarch64-linux-user.mak b/configs/targets/loongarch64-linux-user.mak index 7d1b9640209..249a26a798c 100644 --- a/configs/targets/loongarch64-linux-user.mak +++ b/configs/targets/loongarch64-linux-user.mak @@ -1,3 +1,7 @@ # 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 +TARGET_LONG_BITS=64 diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak index f23780fdd89..fc44c54233d 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_SUPPORTS_MTTCG=y -TARGET_XML_FILES= gdb-xml/loongarch-base32.xml gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml +TARGET_KVM_HAVE_GUEST_DEBUG=y +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 +TARGET_LONG_BITS=64 diff --git a/configs/targets/m68k-linux-user.mak b/configs/targets/m68k-linux-user.mak index 579b5d299cc..2d9bae22707 100644 --- a/configs/targets/m68k-linux-user.mak +++ b/configs/targets/m68k-linux-user.mak @@ -4,3 +4,4 @@ TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/cf-core.xml gdb-xml/cf-fp.xml gdb-xml/m68k-core.xml gdb-xml/m68k-fp.xml TARGET_HAS_BFLT=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/m68k-softmmu.mak b/configs/targets/m68k-softmmu.mak index bbcd0bada69..bacc52e96a9 100644 --- a/configs/targets/m68k-softmmu.mak +++ b/configs/targets/m68k-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=m68k TARGET_BIG_ENDIAN=y TARGET_XML_FILES= gdb-xml/cf-core.xml gdb-xml/cf-fp.xml gdb-xml/m68k-core.xml gdb-xml/m68k-fp.xml +TARGET_LONG_BITS=32 diff --git a/configs/targets/microblaze-linux-user.mak b/configs/targets/microblaze-linux-user.mak index 0a2322c249b..37727797695 100644 --- a/configs/targets/microblaze-linux-user.mak +++ b/configs/targets/microblaze-linux-user.mak @@ -4,3 +4,4 @@ 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 +TARGET_LONG_BITS=32 diff --git a/configs/targets/microblaze-softmmu.mak b/configs/targets/microblaze-softmmu.mak index e84c0cc7283..bab7b498c24 100644 --- a/configs/targets/microblaze-softmmu.mak +++ b/configs/targets/microblaze-softmmu.mak @@ -1,5 +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 +TARGET_LONG_BITS=32 diff --git a/configs/targets/microblazeel-linux-user.mak b/configs/targets/microblazeel-linux-user.mak index 270743156a9..a51a05488d3 100644 --- a/configs/targets/microblazeel-linux-user.mak +++ b/configs/targets/microblazeel-linux-user.mak @@ -3,3 +3,4 @@ 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 +TARGET_LONG_BITS=32 diff --git a/configs/targets/microblazeel-softmmu.mak b/configs/targets/microblazeel-softmmu.mak index 9b688036bd3..8aee7ebc5cf 100644 --- a/configs/targets/microblazeel-softmmu.mak +++ b/configs/targets/microblazeel-softmmu.mak @@ -1,4 +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 +TARGET_LONG_BITS=32 diff --git a/configs/targets/mips-linux-user.mak b/configs/targets/mips-linux-user.mak index b4569a9893b..69bdc459b69 100644 --- a/configs/targets/mips-linux-user.mak +++ b/configs/targets/mips-linux-user.mak @@ -3,3 +3,4 @@ TARGET_ABI_MIPSO32=y TARGET_SYSTBL_ABI=o32 TARGET_SYSTBL=syscall_o32.tbl TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/mips-softmmu.mak b/configs/targets/mips-softmmu.mak index d34b4083fc4..c9588066b8d 100644 --- a/configs/targets/mips-softmmu.mak +++ b/configs/targets/mips-softmmu.mak @@ -1,3 +1,3 @@ TARGET_ARCH=mips TARGET_BIG_ENDIAN=y -TARGET_SUPPORTS_MTTCG=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/mips64-linux-user.mak b/configs/targets/mips64-linux-user.mak index d2ff509a115..04e82b3ab11 100644 --- a/configs/targets/mips64-linux-user.mak +++ b/configs/targets/mips64-linux-user.mak @@ -4,3 +4,4 @@ TARGET_BASE_ARCH=mips TARGET_SYSTBL_ABI=n64 TARGET_SYSTBL=syscall_n64.tbl TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/mips64-softmmu.mak b/configs/targets/mips64-softmmu.mak index 12d9483bf07..7202655fcac 100644 --- a/configs/targets/mips64-softmmu.mak +++ b/configs/targets/mips64-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/mips64el-linux-user.mak b/configs/targets/mips64el-linux-user.mak index f9efeec8ea1..27f41694265 100644 --- a/configs/targets/mips64el-linux-user.mak +++ b/configs/targets/mips64el-linux-user.mak @@ -3,3 +3,4 @@ TARGET_ABI_MIPSN64=y TARGET_BASE_ARCH=mips TARGET_SYSTBL_ABI=n64 TARGET_SYSTBL=syscall_n64.tbl +TARGET_LONG_BITS=64 diff --git a/configs/targets/mips64el-softmmu.mak b/configs/targets/mips64el-softmmu.mak index 8d9ab3ddc4b..3ebeadb29ea 100644 --- a/configs/targets/mips64el-softmmu.mak +++ b/configs/targets/mips64el-softmmu.mak @@ -1,3 +1,3 @@ TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips -TARGET_NEED_FDT=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/mipsel-linux-user.mak b/configs/targets/mipsel-linux-user.mak index e8d7241d310..8b7e86ab288 100644 --- a/configs/targets/mipsel-linux-user.mak +++ b/configs/targets/mipsel-linux-user.mak @@ -2,3 +2,4 @@ TARGET_ARCH=mips TARGET_ABI_MIPSO32=y TARGET_SYSTBL_ABI=o32 TARGET_SYSTBL=syscall_o32.tbl +TARGET_LONG_BITS=32 diff --git a/configs/targets/mipsel-softmmu.mak b/configs/targets/mipsel-softmmu.mak index 0829659fc26..90e09bdc3e5 100644 --- a/configs/targets/mipsel-softmmu.mak +++ b/configs/targets/mipsel-softmmu.mak @@ -1,2 +1,2 @@ TARGET_ARCH=mips -TARGET_SUPPORTS_MTTCG=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/mipsn32-linux-user.mak b/configs/targets/mipsn32-linux-user.mak index 206095da641..39ae214633f 100644 --- a/configs/targets/mipsn32-linux-user.mak +++ b/configs/targets/mipsn32-linux-user.mak @@ -5,3 +5,4 @@ TARGET_BASE_ARCH=mips TARGET_SYSTBL_ABI=n32 TARGET_SYSTBL=syscall_n32.tbl TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/mipsn32el-linux-user.mak b/configs/targets/mipsn32el-linux-user.mak index ca2a3ed7536..d9b61d69902 100644 --- a/configs/targets/mipsn32el-linux-user.mak +++ b/configs/targets/mipsn32el-linux-user.mak @@ -4,3 +4,4 @@ TARGET_ABI32=y TARGET_BASE_ARCH=mips TARGET_SYSTBL_ABI=n32 TARGET_SYSTBL=syscall_n32.tbl +TARGET_LONG_BITS=64 diff --git a/configs/targets/nios2-linux-user.mak b/configs/targets/nios2-linux-user.mak deleted file mode 100644 index 9a372f07177..00000000000 --- 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 c99ae3777eb..00000000000 --- a/configs/targets/nios2-softmmu.mak +++ /dev/null @@ -1,2 +0,0 @@ -TARGET_ARCH=nios2 -TARGET_NEED_FDT=y diff --git a/configs/targets/or1k-linux-user.mak b/configs/targets/or1k-linux-user.mak index 39558f77ecf..810567a98f9 100644 --- a/configs/targets/or1k-linux-user.mak +++ b/configs/targets/or1k-linux-user.mak @@ -1,2 +1,5 @@ TARGET_ARCH=openrisc TARGET_BIG_ENDIAN=y +TARGET_SYSTBL_ABI=common,32,or1k,time32,stat64,rlimit,renameat +TARGET_SYSTBL=syscall.tbl +TARGET_LONG_BITS=32 diff --git a/configs/targets/or1k-softmmu.mak b/configs/targets/or1k-softmmu.mak index 432f855a30a..0e47d9878b0 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 +TARGET_LONG_BITS=32 diff --git a/configs/targets/ppc-linux-user.mak b/configs/targets/ppc-linux-user.mak index cc0439a5285..970d04a5ba1 100644 --- a/configs/targets/ppc-linux-user.mak +++ b/configs/targets/ppc-linux-user.mak @@ -3,3 +3,4 @@ TARGET_SYSTBL_ABI=common,nospu,32 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=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_LONG_BITS=32 diff --git a/configs/targets/ppc-softmmu.mak b/configs/targets/ppc-softmmu.mak index 774440108f7..9bfa7df6c36 100644 --- a/configs/targets/ppc-softmmu.mak +++ b/configs/targets/ppc-softmmu.mak @@ -1,4 +1,5 @@ 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 +TARGET_LONG_BITS=32 diff --git a/configs/targets/ppc64-linux-user.mak b/configs/targets/ppc64-linux-user.mak index 4d81969f4a2..461f1c67d15 100644 --- a/configs/targets/ppc64-linux-user.mak +++ b/configs/targets/ppc64-linux-user.mak @@ -5,3 +5,4 @@ TARGET_SYSTBL_ABI=common,nospu,64 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=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 +TARGET_LONG_BITS=64 diff --git a/configs/targets/ppc64-softmmu.mak b/configs/targets/ppc64-softmmu.mak index ddf0c39617f..74572864b36 100644 --- a/configs/targets/ppc64-softmmu.mak +++ b/configs/targets/ppc64-softmmu.mak @@ -1,6 +1,8 @@ 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 +TARGET_LONG_BITS=64 diff --git a/configs/targets/ppc64le-linux-user.mak b/configs/targets/ppc64le-linux-user.mak index 426d5a28d66..cf9d8a400d9 100644 --- a/configs/targets/ppc64le-linux-user.mak +++ b/configs/targets/ppc64le-linux-user.mak @@ -4,3 +4,4 @@ TARGET_ABI_DIR=ppc TARGET_SYSTBL_ABI=common,nospu,64 TARGET_SYSTBL=syscall.tbl 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 +TARGET_LONG_BITS=64 diff --git a/configs/targets/riscv32-linux-user.mak b/configs/targets/riscv32-linux-user.mak index 9761618e67f..a0ef03c0c3f 100644 --- a/configs/targets/riscv32-linux-user.mak +++ b/configs/targets/riscv32-linux-user.mak @@ -4,3 +4,7 @@ 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 +TARGET_LONG_BITS=32 diff --git a/configs/targets/riscv32-softmmu.mak b/configs/targets/riscv32-softmmu.mak index d8b71cddcd4..db55275b868 100644 --- a/configs/targets/riscv32-softmmu.mak +++ b/configs/targets/riscv32-softmmu.mak @@ -1,5 +1,6 @@ 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 +TARGET_LONG_BITS=32 diff --git a/configs/targets/riscv64-bsd-user.mak b/configs/targets/riscv64-bsd-user.mak new file mode 100644 index 00000000000..c6348a79629 --- /dev/null +++ b/configs/targets/riscv64-bsd-user.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=riscv64 +TARGET_BASE_ARCH=riscv +TARGET_ABI_DIR=riscv +TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-virtual.xml +TARGET_LONG_BITS=64 diff --git a/configs/targets/riscv64-linux-user.mak b/configs/targets/riscv64-linux-user.mak index cfd1fd382f9..aac7568305d 100644 --- a/configs/targets/riscv64-linux-user.mak +++ b/configs/targets/riscv64-linux-user.mak @@ -4,3 +4,7 @@ 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 +TARGET_LONG_BITS=64 diff --git a/configs/targets/riscv64-softmmu.mak b/configs/targets/riscv64-softmmu.mak index 7c0e7eeb429..2bdd4a62cd2 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 +TARGET_LONG_BITS=64 diff --git a/configs/targets/rx-softmmu.mak b/configs/targets/rx-softmmu.mak index 0c458b2d07c..1c250a6450d 100644 --- a/configs/targets/rx-softmmu.mak +++ b/configs/targets/rx-softmmu.mak @@ -1,3 +1,5 @@ TARGET_ARCH=rx TARGET_XML_FILES= gdb-xml/rx-core.xml +# all boards require libfdt TARGET_NEED_FDT=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/s390x-linux-user.mak b/configs/targets/s390x-linux-user.mak index 24c04c85894..68c2f288724 100644 --- a/configs/targets/s390x-linux-user.mak +++ b/configs/targets/s390x-linux-user.mak @@ -3,3 +3,4 @@ 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-virt-kvm.xml gdb-xml/s390-gs.xml +TARGET_LONG_BITS=64 diff --git a/configs/targets/s390x-softmmu.mak b/configs/targets/s390x-softmmu.mak index 70d2f9f0ba0..76dd5de6584 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_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 +TARGET_LONG_BITS=64 diff --git a/configs/targets/sh4-linux-user.mak b/configs/targets/sh4-linux-user.mak index 99088875666..d58c5471b77 100644 --- a/configs/targets/sh4-linux-user.mak +++ b/configs/targets/sh4-linux-user.mak @@ -2,3 +2,4 @@ TARGET_ARCH=sh4 TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl TARGET_HAS_BFLT=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/sh4-softmmu.mak b/configs/targets/sh4-softmmu.mak index f9d62d91e4d..787d349b501 100644 --- a/configs/targets/sh4-softmmu.mak +++ b/configs/targets/sh4-softmmu.mak @@ -1 +1,2 @@ TARGET_ARCH=sh4 +TARGET_LONG_BITS=32 diff --git a/configs/targets/sh4eb-linux-user.mak b/configs/targets/sh4eb-linux-user.mak index 9db6b3609c2..99007f0f2d6 100644 --- a/configs/targets/sh4eb-linux-user.mak +++ b/configs/targets/sh4eb-linux-user.mak @@ -3,3 +3,4 @@ TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y TARGET_HAS_BFLT=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/sh4eb-softmmu.mak b/configs/targets/sh4eb-softmmu.mak index 226b1fc698a..cdea2c61c58 100644 --- a/configs/targets/sh4eb-softmmu.mak +++ b/configs/targets/sh4eb-softmmu.mak @@ -1,2 +1,3 @@ TARGET_ARCH=sh4 TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/sparc-linux-user.mak b/configs/targets/sparc-linux-user.mak index abcfb8fc624..4ff4b7287d2 100644 --- a/configs/targets/sparc-linux-user.mak +++ b/configs/targets/sparc-linux-user.mak @@ -2,3 +2,4 @@ TARGET_ARCH=sparc TARGET_SYSTBL_ABI=common,32 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/sparc-softmmu.mak b/configs/targets/sparc-softmmu.mak index 454eb354996..57801faf1fc 100644 --- a/configs/targets/sparc-softmmu.mak +++ b/configs/targets/sparc-softmmu.mak @@ -1,2 +1,3 @@ TARGET_ARCH=sparc TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/sparc32plus-linux-user.mak b/configs/targets/sparc32plus-linux-user.mak index 6cc8fa516b4..7a16934fd17 100644 --- a/configs/targets/sparc32plus-linux-user.mak +++ b/configs/targets/sparc32plus-linux-user.mak @@ -5,3 +5,4 @@ TARGET_ABI_DIR=sparc TARGET_SYSTBL_ABI=common,32 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/sparc64-linux-user.mak b/configs/targets/sparc64-linux-user.mak index 52f05ec0008..64ea04e3e2e 100644 --- a/configs/targets/sparc64-linux-user.mak +++ b/configs/targets/sparc64-linux-user.mak @@ -4,3 +4,4 @@ TARGET_ABI_DIR=sparc TARGET_SYSTBL_ABI=common,64 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/sparc64-softmmu.mak b/configs/targets/sparc64-softmmu.mak index d3f8a3b7109..2504e31ae33 100644 --- a/configs/targets/sparc64-softmmu.mak +++ b/configs/targets/sparc64-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=sparc64 TARGET_BASE_ARCH=sparc TARGET_BIG_ENDIAN=y +TARGET_LONG_BITS=64 diff --git a/configs/targets/tricore-softmmu.mak b/configs/targets/tricore-softmmu.mak index 96b10af8533..781ce49a62f 100644 --- a/configs/targets/tricore-softmmu.mak +++ b/configs/targets/tricore-softmmu.mak @@ -1 +1,2 @@ TARGET_ARCH=tricore +TARGET_LONG_BITS=32 diff --git a/configs/targets/x86_64-bsd-user.mak b/configs/targets/x86_64-bsd-user.mak index 799cd4acd41..d62d656f2c6 100644 --- a/configs/targets/x86_64-bsd-user.mak +++ b/configs/targets/x86_64-bsd-user.mak @@ -1,3 +1,4 @@ TARGET_ARCH=x86_64 TARGET_BASE_ARCH=i386 TARGET_XML_FILES= gdb-xml/i386-64bit.xml +TARGET_LONG_BITS=64 diff --git a/configs/targets/x86_64-linux-user.mak b/configs/targets/x86_64-linux-user.mak index 9ceefbb615a..b093ab5a167 100644 --- a/configs/targets/x86_64-linux-user.mak +++ b/configs/targets/x86_64-linux-user.mak @@ -2,4 +2,5 @@ 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 +TARGET_LONG_BITS=64 diff --git a/configs/targets/x86_64-softmmu.mak b/configs/targets/x86_64-softmmu.mak index 197817c9434..5619b2bc686 100644 --- a/configs/targets/x86_64-softmmu.mak +++ b/configs/targets/x86_64-softmmu.mak @@ -1,5 +1,6 @@ TARGET_ARCH=x86_64 TARGET_BASE_ARCH=i386 -TARGET_SUPPORTS_MTTCG=y -TARGET_NEED_FDT=y +TARGET_KVM_HAVE_GUEST_DEBUG=y +TARGET_KVM_HAVE_RESET_PARKED_VCPU=y TARGET_XML_FILES= gdb-xml/i386-64bit.xml +TARGET_LONG_BITS=64 diff --git a/configs/targets/xtensa-linux-user.mak b/configs/targets/xtensa-linux-user.mak index 420b30a68d9..cbec6e368af 100644 --- a/configs/targets/xtensa-linux-user.mak +++ b/configs/targets/xtensa-linux-user.mak @@ -2,3 +2,4 @@ TARGET_ARCH=xtensa TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl TARGET_HAS_BFLT=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/xtensa-softmmu.mak b/configs/targets/xtensa-softmmu.mak index f075557bfa9..2a9797338a6 100644 --- a/configs/targets/xtensa-softmmu.mak +++ b/configs/targets/xtensa-softmmu.mak @@ -1,2 +1,2 @@ TARGET_ARCH=xtensa -TARGET_SUPPORTS_MTTCG=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/xtensaeb-linux-user.mak b/configs/targets/xtensaeb-linux-user.mak index bce2d1d65d2..f455b1c7801 100644 --- a/configs/targets/xtensaeb-linux-user.mak +++ b/configs/targets/xtensaeb-linux-user.mak @@ -3,3 +3,4 @@ TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y TARGET_HAS_BFLT=y +TARGET_LONG_BITS=32 diff --git a/configs/targets/xtensaeb-softmmu.mak b/configs/targets/xtensaeb-softmmu.mak index b02e11b8200..5204729af8b 100644 --- a/configs/targets/xtensaeb-softmmu.mak +++ b/configs/targets/xtensaeb-softmmu.mak @@ -1,3 +1,3 @@ TARGET_ARCH=xtensa TARGET_BIG_ENDIAN=y -TARGET_SUPPORTS_MTTCG=y +TARGET_LONG_BITS=32 diff --git a/configure b/configure index e08127045d0..2b2b3d65979 100755 --- a/configure +++ b/configure @@ -13,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" @@ -41,12 +41,7 @@ then # 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" && \ @@ -54,8 +49,9 @@ endif then \ rm -rf build GNUmakefile ; \ fi -force: ; -.PHONY: force +%: build + @ +.PHONY: build GNUmakefile: ; EOF @@ -94,7 +90,7 @@ quote_sh() { printf "%s" "$1" | sed "s,','\\\\'',g; s,.*,'&'," } -print_error() { +error_exit() { (echo echo "ERROR: $1" while test -n "$2"; do @@ -102,10 +98,6 @@ print_error() { shift done echo) >&2 -} - -error_exit() { - print_error "$@" exit 1 } @@ -180,6 +172,7 @@ fi # some defaults, based on the host environment # default parameters +container_engine="auto" cpu="" cross_compile="no" cross_prefix="" @@ -214,6 +207,10 @@ for opt do ;; --objcc=*) objcc="$optarg" ;; + --rustc=*) RUSTC="$optarg" + ;; + --rustdoc=*) RUSTDOC="$optarg" + ;; --cpu=*) cpu="$optarg" ;; --extra-cflags=*) @@ -247,12 +244,9 @@ done default_cflags='-O2 -g' git_submodules_action="update" -git="git" docs="auto" EXESUF="" -prefix="/usr/local" -qemu_suffix="qemu" -softmmu="yes" +system="yes" linux_user="" bsd_user="" plugins="$default_feature" @@ -260,12 +254,12 @@ subdirs="" ninja="" python= download="enabled" -bindir="bin" skip_meson=no use_containers="yes" +rust="disabled" +rust_target_triple="" gdb_bin=$(command -v "gdb-multiarch" || command -v "gdb") gdb_arches="" -werror="" # Don't accept a target_list environment variable. unset target_list @@ -317,11 +311,12 @@ 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}" @@ -329,6 +324,9 @@ windmc="${WINDMC-${cross_prefix}windmc}" pkg_config="${PKG_CONFIG-${cross_prefix}pkg-config}" sdl2_config="${SDL2_CONFIG-${cross_prefix}sdl2-config}" +rustc="${RUSTC-rustc}" +rustdoc="${RUSTDOC-rustdoc}" + check_define() { cat > $TMPC <= 3.8. + # We require python >= 3.9. # NB: a True python conditional creates a non-zero return code (Failure) - "$1" -c 'import sys; sys.exit(sys.version_info < (3,8))' + "$1" -c 'import sys; sys.exit(sys.version_info < (3,9))' } 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.11 python3.10 python3.9 python3.8; do + for binary in python3 python python3.13 python3.12 python3.11 \ + python3.10 python3.9 ; do if has "$binary"; then python=$(command -v "$binary") if check_py_version "$python"; then @@ -596,16 +595,13 @@ do fi done -if test "$targetos" = "windows" ; then +if test "$host_os" = "windows" ; then EXESUF=".exe" - prefix="/qemu" - bindir="" - qemu_suffix="" fi meson_option_build_array() { printf '[' - (if test "$targetos" = windows; then + (if test "$host_os" = windows; then IFS=\; else IFS=: @@ -623,7 +619,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 "$@")" @@ -633,6 +632,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[^=]*=\(.*\)') @@ -641,8 +651,6 @@ for opt do ;; --version|-V) exec cat "$source_path/VERSION" ;; - --prefix=*) prefix="$optarg" - ;; --cross-prefix=*) ;; --cc=*) @@ -653,6 +661,10 @@ for opt do ;; --objcc=*) ;; + --rustc=*) + ;; + --rustdoc=*) + ;; --make=*) ;; --install=*) @@ -663,8 +675,6 @@ for opt do ;; --ninja=*) ninja="$optarg" ;; - --smbd=*) smbd="$optarg" - ;; --extra-cflags=*) ;; --extra-cxxflags=*) @@ -713,10 +723,6 @@ for opt do ;; --static) static="yes" ;; - --bindir=*) bindir="$optarg" - ;; - --with-suffix=*) qemu_suffix="$optarg" - ;; --host=*|--build=*|\ --disable-dependency-tracking|\ --sbindir=*|--sharedstatedir=*|\ @@ -736,13 +742,12 @@ for opt do default_cflags='-O0 -g' ;; --disable-tcg) tcg="disabled" - plugins="no" ;; --enable-tcg) tcg="enabled" ;; - --disable-system) softmmu="no" + --disable-system) system="no" ;; - --enable-system) softmmu="yes" + --enable-system) system="yes" ;; --disable-user) linux_user="no" ; @@ -761,25 +766,15 @@ for opt do ;; --disable-pie) pie="no" ;; - --enable-werror) werror="yes" + --enable-cfi) cfi=true ;; - --disable-werror) werror="no" - ;; - --enable-cfi) - cfi="true"; - meson_option_add -Db_lto=true - ;; - --disable-cfi) cfi="false" + --disable-cfi) cfi=false ;; --disable-download) download="disabled"; git_submodules_action=validate; ;; --enable-download) download="enabled"; git_submodules_action=update; ;; - --enable-plugins) if test "$targetos" = "windows"; then - error_exit "TCG plugins not currently supported on Windows platforms" - else - plugins="yes" - fi + --enable-plugins) plugins="yes" ;; --disable-plugins) plugins="no" ;; @@ -787,13 +782,21 @@ for opt do ;; --disable-containers) use_containers="no" ;; + --container-engine=*) container_engine="$optarg" + ;; + --rust-target-triple=*) rust_target_triple="$optarg" + ;; --gdb=*) gdb_bin="$optarg" ;; + --enable-rust) rust=enabled + ;; + --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_options="$meson_options $opt" + -D*) meson_option_add "$opt" ;; esac done @@ -803,11 +806,6 @@ then git_submodules_action="validate" fi -# test for any invalid configuration combinations -if test "$plugins" = "yes" -a "$tcg" = "disabled"; then - error_exit "Can't enable plugins on non-TCG builds" -fi - if ! test -f "$source_path/subprojects/keycodemapdb/README" \ && test "$download" = disabled then @@ -839,7 +837,7 @@ mak_wilds="" if [ -n "$host_arch" ] && [ -d "$source_path/common-user/host/$host_arch" ]; then if [ "$linux_user" != no ]; then - if [ "$targetos" = linux ]; then + if [ "$host_os" = linux ]; then linux_user=yes elif [ "$linux_user" = yes ]; then error_exit "linux-user not supported on this architecture" @@ -850,9 +848,9 @@ if [ -n "$host_arch" ] && [ -d "$source_path/common-user/host/$host_arch" ]; the fi if [ "$bsd_user" != no ]; then if [ "$bsd_user" = "" ]; then - test $targetos = freebsd && bsd_user=yes + test $host_os = freebsd && bsd_user=yes fi - if [ "$bsd_user" = yes ] && ! [ -d "$source_path/bsd-user/$targetos" ]; then + 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 @@ -864,7 +862,7 @@ else error_exit "user mode emulation not supported on this architecture" fi fi -if [ "$softmmu" = "yes" ]; then +if [ "$system" = "yes" ]; then mak_wilds="${mak_wilds} $source_path/configs/targets/*-softmmu.mak" fi @@ -883,7 +881,6 @@ 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/^/ /') @@ -897,6 +894,8 @@ Advanced options (experts only): 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] + --rustdoc=RUSTDOC use rustdoc binary RUSTDOC [$rustdoc] --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 @@ -906,21 +905,17 @@ Advanced options (experts only): --cross-prefix-ARCH=PREFIX cross compiler prefix when building ARCH guest test cases --python=PYTHON use specified python [$python] --ninja=NINJA use specified ninja [$ninja] - --smbd=SMBD use specified smbd [$smbd] --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 - --disable-werror disable compilation abort on warning --cpu=CPU Build for host CPU [$cpu] - --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 @@ -936,6 +931,13 @@ 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 @@ -944,7 +946,7 @@ then # 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.7 is required." \ + error_exit "Cannot use '$first_python', Python >= 3.9 is required." \ "Use --python=/path/to/python to specify a supported Python." else error_exit "Python not found. Use --python=/path/to/python" @@ -952,11 +954,11 @@ then fi if ! check_py_version "$python"; then - error_exit "Cannot use '$python', Python >= 3.8 is required." \ + error_exit "Cannot use '$python', Python >= 3.9 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" + " CentOS: dnf install python3.12" fi # Resolve PATH @@ -991,10 +993,6 @@ mkvenv="$python ${source_path}/python/scripts/mkvenv.py" # Finish preparing the virtual environment using vendored .whl files -if $python -c 'import sys; sys.exit(sys.version_info >= (3,11))'; then - $mkvenv ensure --dir "${source_path}/python/wheels" \ - 'tomli>=1.2.0' || exit 1 -fi $mkvenv ensuregroup --dir "${source_path}/python/wheels" \ ${source_path}/pythondeps.toml meson || exit 1 @@ -1007,14 +1005,14 @@ meson="$(cd pyvenv/bin; pwd)/meson" # Conditionally ensure Sphinx is installed. -mkvenv_flags="" -if test "$download" = "enabled" -a "$docs" = "enabled" ; then - mkvenv_flags="--online" +mkvenv_online_flag="" +if test "$download" = "enabled" ; then + mkvenv_online_flag=" --online" fi if test "$docs" != "disabled" ; then if ! $mkvenv ensuregroup \ - $mkvenv_flags \ + $(test "$docs" = "enabled" && echo "$mkvenv_online_flag") \ ${source_path}/pythondeps.toml docs; then if test "$docs" = "enabled" ; then @@ -1041,18 +1039,7 @@ if test -z "$ninja"; then fi 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 -e "$source_path/.git" && \ - { test "$targetos" = linux || test "$targetos" = "windows"; }; 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 @@ -1060,16 +1047,31 @@ if test "$targetos" = "bogus"; then error_exit "Unrecognized host OS (uname -s reports '$(uname -s)')" 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 +if test "$tcg" = "disabled" ; then + if test "$plugins" = "yes"; then + error_exit "Can't enable plugins on non-TCG builds" + fi + plugins="no" +fi if test "$static" = "yes" ; then if test "$plugins" = "yes"; then error_exit "static and plugins are mutually incompatible" - else - plugins="no" fi + plugins="no" fi -test "$plugins" = "" && plugins=yes -if test "$plugins" = "yes"; then - subdirs="$subdirs contrib/plugins" +if test "$plugins" != "no"; then + if has_meson_option "-Dtcg_interpreter=true"; then + plugins="no" + else + plugins=yes + fi fi cat > $TMPC << EOF @@ -1083,19 +1085,23 @@ static THREAD int tls_var; int main(void) { return tls_var; } EOF -if test "$static" = "yes"; then - if test "$pie" != "no" && compile_prog "-Werror -fPIE -DPIE" "-static-pie"; then - pie="yes" - elif test "$pie" = "yes"; then - error_exit "-static-pie not available due to missing toolchain support" +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 "$pie" != "no"; then + if test "$static" = "yes"; then + pie_ldflags=-static-pie else - pie="no" + pie_ldflags=-pie fi -elif test "$pie" != "no"; then - if compile_prog "-Werror -fPIE -DPIE" "-pie"; then + if compile_prog "-Werror -fPIE -DPIE" "$pie_ldflags"; then pie="yes" elif test "$pie" = "yes"; then - error_exit "PIE not available due to missing toolchain support" + error_exit "-static-pie not available due to missing toolchain support" else echo "Disabling PIE due to missing toolchain support" pie="no" @@ -1134,6 +1140,20 @@ if test "$tcg" = "auto"; then fi fi +######################################### +# 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 + ########################################## # big/little endian test cat > $TMPC << EOF @@ -1161,32 +1181,138 @@ EOF fi fi -######################################## -# check if ccache is interfering with -# semantic analysis of macros +########################################## +# detect rust triple + +meson_version=$($meson --version) +if test "$rust" != disabled && ! version_ge "$meson_version" 1.8.1; then + if test "$rust" = enabled; then + error_exit "Rust support needs Meson 1.8.1 or newer" + fi + echo "Rust needs Meson 1.8.1, disabling" 2>&1 + rust=disabled +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 + 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 + ;; -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; -} + 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 + ;; -if ! compile_object "-Werror"; then - ccache_cpp2=yes + 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 + ;; + esac + + # 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 + ;; + + 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 + ;; + + 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 + rust_target_triple=$rust_arch-$rust_machine-$rust_os${rust_osvariant:+-$rust_osvariant} + fi fi ########################################## @@ -1195,14 +1321,14 @@ fi 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=$($python "$source_path"/tests/docker/docker.py probe) + runc=$container fi fi @@ -1222,7 +1348,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"} @@ -1248,7 +1373,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=" @@ -1285,6 +1410,7 @@ probe_target_compiler() { got_cross_cc=no container_image= container_hosts= + container_cross_prefix= container_cross_cc= container_cross_ar= container_cross_as= @@ -1297,9 +1423,9 @@ probe_target_compiler() { 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 ;; @@ -1310,7 +1436,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 ;; @@ -1326,103 +1451,68 @@ 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- ;; x86_64) container_image=debian-amd64-cross - container_cross_prefix=x86_64-linux-gnu- ;; xtensa*) container_image=debian-xtensa-cross @@ -1431,6 +1521,9 @@ probe_target_compiler() { 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} @@ -1442,16 +1535,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 @@ -1610,22 +1706,26 @@ 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/data" +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 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 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 subdirs="$subdirs pc-bios/optionrom" config_mak=pc-bios/optionrom/config.mak @@ -1674,80 +1774,32 @@ echo >> $config_host_mak echo all: >> $config_host_mak -if test "$targetos" = "windows"; then - echo "QEMU_GA_MANUFACTURER=${QEMU_GA_MANUFACTURER-QEMU}" >> $config_host_mak - echo "QEMU_GA_DISTRO=${QEMU_GA_DISTRO-Linux}" >> $config_host_mak - echo "QEMU_GA_VERSION=${QEMU_GA_VERSION-$(cat "$source_path"/VERSION)}" >> $config_host_mak -fi - echo "SRC_PATH=$source_path" >> $config_host_mak echo "TARGET_DIRS=$target_list" >> $config_host_mak - -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 - gdb_arches=$($python "$source_path/scripts/probe-gdb-support.py" $gdb_bin) - 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 "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 "EXESUF=$EXESUF" >> $config_host_mak - -# use included Linux headers for KVM architectures -if test "$targetos" = "linux" && test -n "$linux_arch"; then - symlink "$source_path/linux-headers/asm-$linux_arch" linux-headers/asm -fi - -for target in $target_list; do - target_dir="$target" - target_name=$(echo $target | cut -d '-' -f 1)$EXESUF - 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 - -# contrib/plugins configuration -echo "# Automatically generated by configure - do not modify" > contrib/plugins/$config_host_mak -echo "SRC_PATH=$source_path/contrib/plugins" >> contrib/plugins/$config_host_mak -echo "PKG_CONFIG=${pkg_config}" >> contrib/plugins/$config_host_mak -echo "CC=$cc $CPU_CFLAGS" >> contrib/plugins/$config_host_mak -echo "CFLAGS=${CFLAGS-$default_cflags} $EXTRA_CFLAGS" >> contrib/plugins/$config_host_mak -if test "$targetos" = darwin; then - echo "CONFIG_DARWIN=y" >> contrib/plugins/$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 - -# 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= @@ -1756,7 +1808,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) @@ -1782,7 +1834,16 @@ for target in $target_list; do # will GDB work with these binaries? if test "${gdb_arches#*$arch}" != "$gdb_arches"; then - echo "HOST_GDB_SUPPORTS_ARCH=y" >> "$config_target_mak" + 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 @@ -1791,9 +1852,8 @@ for target in $target_list; do done if test "$tcg" = "enabled"; then - echo "TCG_TESTS_TARGETS=$tcg_tests_targets" >> config-host.mak + echo "TCG_TESTS_TARGETS=$tcg_tests_targets" >> $config_host_mak fi -) if test "$skip_meson" = no; then cross="config-meson.cross.new" @@ -1817,14 +1877,49 @@ 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 + echo "rustdoc = [$(meson_quote $rustdoc --target "$rust_target_triple")]" >> $cross + else + echo "rust = [$(meson_quote $rustc)]" >> $cross + echo "rustdoc = [$(meson_quote $rustdoc)]" >> $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)]" >> $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 @@ -1834,7 +1929,7 @@ if test "$skip_meson" = no; then echo "windmc = [$(meson_quote $windmc)]" >> $cross if test "$cross_compile" = "yes"; then echo "[host_machine]" >> $cross - echo "system = '$targetos'" >> $cross + echo "system = '$host_os'" >> $cross case "$cpu" in i386) echo "cpu_family = 'x86'" >> $cross @@ -1849,39 +1944,40 @@ if test "$skip_meson" = no; then else echo "endian = 'little'" >> $cross fi - cross_arg="--cross-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 - cross_arg="$cross_arg --native-file config-meson.native" - else - cross_arg="--native-file config-meson.cross" + 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 "$download" = "disabled" && meson_option_add "--wrap-mode=nodownload" - test "$bindir" != "bin" && meson_option_add "-Dbindir=$bindir" 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 "$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 "$plugins" = yes && meson_option_add "-Dplugins=true" - test "$qemu_suffix" != qemu && meson_option_add "-Dqemu_suffix=$qemu_suffix" - test "$smbd" != '' && meson_option_add "-Dsmbd=$smbd" test "$tcg" != enabled && meson_option_add "-Dtcg=$tcg" run_meson() { - NINJA=$ninja $meson setup --prefix "$prefix" "$@" $cross_arg "$PWD" "$source_path" + NINJA=$ninja $meson setup "$@" "$PWD" "$source_path" } eval run_meson $meson_options if test "$?" -ne 0 ; then @@ -1929,6 +2025,7 @@ 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 @@ -1958,3 +2055,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 0b04cba00e5..81295a11534 100644 --- a/contrib/elf2dmp/addrspace.c +++ b/contrib/elf2dmp/addrspace.c @@ -14,7 +14,7 @@ static struct pa_block *pa_space_find_block(struct pa_space *ps, uint64_t pa) 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; } } @@ -22,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); @@ -33,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); @@ -48,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) @@ -84,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) @@ -109,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) @@ -126,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) @@ -162,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; } @@ -176,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; } @@ -185,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; } @@ -204,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; @@ -216,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) { @@ -230,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 00b44c12180..2ad30a9da48 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 bd7650a7a27..21306b3fd4c 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 5c274925f7a..f65adb5d089 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 6d4d18501a3..d046a72ae67 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" @@ -20,6 +21,7 @@ #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 */ @@ -46,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 */ @@ -63,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; } @@ -78,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; } @@ -96,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; } @@ -119,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; } @@ -188,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; } /* @@ -208,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); } } @@ -222,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); @@ -244,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) { @@ -282,13 +312,17 @@ 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 += @@ -296,11 +330,16 @@ static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps, *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; @@ -310,10 +349,10 @@ static int fill_context(KDDEBUGGER_DATA64 *kdbg, 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 (!Prcb) { @@ -321,26 +360,24 @@ static int fill_context(KDDEBUGGER_DATA64 *kdbg, continue; } - if (va_space_rw(vs, Prcb + kdbg->OffsetPrcbContext, - &Context, sizeof(Context), 0)) { + 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 pe_get_data_dir_entry(uint64_t base, void *start_addr, int idx, - void *entry, size_t size, struct va_space *vs) +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"; @@ -353,40 +390,38 @@ static int pe_get_data_dir_entry(uint64_t base, void *start_addr, int idx, QEMU_BUILD_BUG_ON(sizeof(*dos_hdr) >= ELF2DMP_PAGE_SIZE); if (memcmp(&dos_hdr->e_magic, e_magic, sizeof(e_magic))) { - return 1; + return false; } - if (va_space_rw(vs, base + dos_hdr->e_lfanew, - &nt_hdrs, sizeof(nt_hdrs), 0)) { - return 1; + 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 1; + return false; } - if (va_space_rw(vs, - base + data_dir[idx].VirtualAddress, - entry, size, 0)) { - return 1; + 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 0; + return true; } -static int write_dump(struct pa_space *ps, - WinDumpHeader64 *hdr, const char *name) +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"); @@ -394,111 +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 bool pe_check_export_name(uint64_t base, void *start_addr, - struct va_space *vs) -{ - IMAGE_EXPORT_DIRECTORY export_dir; - const char *pe_name; - - if (pe_get_data_dir_entry(base, start_addr, IMAGE_FILE_EXPORT_DIRECTORY, - &export_dir, sizeof(export_dir), vs)) { - return false; - } - - pe_name = va_space_resolve(vs, base + export_dir.Name); - if (!pe_name) { - return false; - } - - return !strcmp(pe_name, PE_NAME); -} - -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 sign_rsds[4] = "RSDS"; IMAGE_DEBUG_DIRECTORY debug_dir; - OMFSignatureRSDS rsds; - char *pdb_name; - size_t pdb_name_sz; - size_t i; + char pdb_name[sizeof(PDB_NAME)]; - if (pe_get_data_dir_entry(base, start_addr, IMAGE_FILE_DEBUG_DIRECTORY, - &debug_dir, sizeof(debug_dir), vs)) { + 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 1; + 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; - } - - free(pdb_name); + return !strcmp(pdb_name, 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; @@ -515,39 +525,34 @@ int main(int argc, char *argv[]) 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)); @@ -562,7 +567,8 @@ int main(int argc, char *argv[]) } if (*(uint16_t *)nt_start_addr == 0x5a4d) { /* MZ */ - if (pe_check_export_name(KernBase, nt_start_addr, &vs)) { + printf("Checking candidate KernBase = 0x%016"PRIx64"\n", KernBase); + if (pe_check_pdb_name(KernBase, nt_start_addr, &vs, &rsds)) { kernel_found = true; break; } @@ -571,72 +577,59 @@ int main(int argc, char *argv[]) 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 adcfa7e154c..492aca4434c 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 4ea8925ee82..feddf1862f0 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/qemu_elf.c b/contrib/elf2dmp/qemu_elf.c index ebda60dcb8a..c9bad6e82cf 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(); + + if (uadd64_overflow(offset, phdr[0].p_memsz, &end_offset) || + end_offset > qe->size) { + end_offset = qe->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); - 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 (!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 #%zu: QEMU CPU state size %u doesn't match\n", - cpu_nr, state->size); + 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); - - qe->state = malloc(sizeof(*qe->state) * cpu_nr); - if (!qe->state) { - return 1; - } - - cpu_nr = 0; + printf("%u CPU states has been found\n", states->len); - 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; + qe->state_nr = states->len; + qe->state = (void *)g_ptr_array_free(states, FALSE); - 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 b2f0d9cbc9b..adc50238b46 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 3e31a062453..bf1dce03fde 100644 --- a/contrib/gitdm/domain-map +++ b/contrib/gitdm/domain-map @@ -12,15 +12,18 @@ 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 @@ -38,6 +41,7 @@ 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 diff --git a/contrib/ivshmem-client/meson.build b/contrib/ivshmem-client/meson.build index ce8dcca84dd..3c8b09af4bf 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/meson.build b/contrib/ivshmem-server/meson.build index c6c3c82e89f..1c8fea6594d 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 8ba78c7a329..00000000000 --- a/contrib/plugins/Makefile +++ /dev/null @@ -1,50 +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 -# programs that the main configure has already done for us. -# - -include config-host.mak - -TOP_SRC_PATH = $(SRC_PATH)/../.. - -VPATH += $(SRC_PATH) - -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). -PLUGIN_CFLAGS := $(shell $(PKG_CONFIG) --cflags glib-2.0) -PLUGIN_CFLAGS += -fPIC -Wall -PLUGIN_CFLAGS += -I$(TOP_SRC_PATH)/include/qemu - -all: $(SONAMES) - -%.o: %.c - $(CC) $(CFLAGS) $(PLUGIN_CFLAGS) -c -o $@ $< - -lib%.so: %.o -ifeq ($(CONFIG_DARWIN),y) - $(CC) -bundle -Wl,-undefined,dynamic_lookup -o $@ $^ $(LDLIBS) -else - $(CC) -shared -o $@ $^ $(LDLIBS) -endif - -clean: - rm -f *.o *.so *.d - rm -Rf .libs - -.PHONY: all clean -.SECONDARY: diff --git a/contrib/plugins/bbv.c b/contrib/plugins/bbv.c new file mode 100644 index 00000000000..b9da6f815e3 --- /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) + 1; + 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 4fca3edd07a..56508587d3a 100644 --- a/contrib/plugins/cache.c +++ b/contrib/plugins/cache.c @@ -208,7 +208,7 @@ static int fifo_get_first_block(Cache *cache, int set) static void fifo_update_on_miss(Cache *cache, int set, int blk_idx) { GQueue *q = cache->sets[set].fifo_queue; - g_queue_push_head(q, GINT_TO_POINTER(blk_idx)); + g_queue_push_head(q, (gpointer)(intptr_t) blk_idx); } static void fifo_destroy(Cache *cache) @@ -471,13 +471,8 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) n_insns = qemu_plugin_tb_n_insns(tb); for (i = 0; i < n_insns; i++) { struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); - uint64_t effective_addr; - - if (sys) { - effective_addr = (uint64_t) qemu_plugin_insn_haddr(insn); - } else { - effective_addr = (uint64_t) qemu_plugin_insn_vaddr(insn); - } + uint64_t effective_addr = sys ? (uintptr_t) qemu_plugin_insn_haddr(insn) : + qemu_plugin_insn_vaddr(insn); /* * Instructions might get translated multiple times, we do not create @@ -485,14 +480,13 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) * entry from the hash table and register it for the callback again. */ g_mutex_lock(&hashtable_lock); - data = g_hash_table_lookup(miss_ht, GUINT_TO_POINTER(effective_addr)); + data = g_hash_table_lookup(miss_ht, &effective_addr); if (data == NULL) { data = g_new0(InsnData, 1); data->disas_str = qemu_plugin_insn_disas(insn); data->symbol = qemu_plugin_insn_symbol(insn); data->addr = effective_addr; - g_hash_table_insert(miss_ht, GUINT_TO_POINTER(effective_addr), - (gpointer) data); + g_hash_table_insert(miss_ht, &data->addr, data); } g_mutex_unlock(&hashtable_lock); @@ -535,15 +529,13 @@ 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; - - l1_dmiss_rate = ((double) l1_dmisses) / (l1_daccess) * 100.0; - l1_imiss_rate = ((double) l1_imisses) / (l1_iaccess) * 100.0; + double l1_dmiss_rate = ((double) l1_dmisses) / (l1_daccess) * 100.0; + double l1_imiss_rate = ((double) l1_imisses) / (l1_iaccess) * 100.0; g_string_append_printf(line, "%-14" PRIu64 " %-12" PRIu64 " %9.4lf%%" " %-14" PRIu64 " %-12" PRIu64 " %9.4lf%%", @@ -554,13 +546,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; + 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"); @@ -584,7 +576,7 @@ static void sum_stats(void) } } -static int dcmp(gconstpointer a, gconstpointer b) +static int dcmp(gconstpointer a, gconstpointer b, gpointer d) { InsnData *insn_a = (InsnData *) a; InsnData *insn_b = (InsnData *) b; @@ -592,7 +584,7 @@ static int dcmp(gconstpointer a, gconstpointer b) return insn_a->l1_dmisses < insn_b->l1_dmisses ? 1 : -1; } -static int icmp(gconstpointer a, gconstpointer b) +static int icmp(gconstpointer a, gconstpointer b, gpointer d) { InsnData *insn_a = (InsnData *) a; InsnData *insn_b = (InsnData *) b; @@ -600,7 +592,7 @@ static int icmp(gconstpointer a, gconstpointer b) return insn_a->l1_imisses < insn_b->l1_imisses ? 1 : -1; } -static int l2_cmp(gconstpointer a, gconstpointer b) +static int l2_cmp(gconstpointer a, gconstpointer b, gpointer d) { InsnData *insn_a = (InsnData *) a; InsnData *insn_b = (InsnData *) b; @@ -611,7 +603,7 @@ static int l2_cmp(gconstpointer a, gconstpointer b) static void log_stats(void) { int i; - Cache *icache, *dcache, *l2_cache; + Cache *icache, *dcache, *l2_cache = NULL; g_autoptr(GString) rep = g_string_new("core #, data accesses, data misses," " dmiss rate, insn accesses," @@ -653,7 +645,7 @@ static void log_top_insns(void) InsnData *insn; miss_insns = g_hash_table_get_values(miss_ht); - miss_insns = g_list_sort(miss_insns, dcmp); + miss_insns = g_list_sort_with_data(miss_insns, dcmp, NULL); g_autoptr(GString) rep = g_string_new(""); g_string_append_printf(rep, "%s", "address, data misses, instruction\n"); @@ -667,7 +659,7 @@ static void log_top_insns(void) insn->l1_dmisses, insn->disas_str); } - miss_insns = g_list_sort(miss_insns, icmp); + miss_insns = g_list_sort_with_data(miss_insns, icmp, NULL); g_string_append_printf(rep, "%s", "\naddress, fetch misses, instruction\n"); for (curr = miss_insns, i = 0; curr && i < limit; i++, curr = curr->next) { @@ -684,7 +676,7 @@ static void log_top_insns(void) goto finish; } - miss_insns = g_list_sort(miss_insns, l2_cmp); + miss_insns = g_list_sort_with_data(miss_insns, l2_cmp, NULL); g_string_append_printf(rep, "%s", "\naddress, L2 misses, instruction\n"); for (curr = miss_insns, i = 0; curr && i < limit; i++, curr = curr->next) { @@ -769,7 +761,7 @@ 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]; @@ -855,7 +847,7 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); - miss_ht = g_hash_table_new_full(NULL, g_direct_equal, NULL, insn_free); + miss_ht = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, insn_free); return 0; } diff --git a/contrib/plugins/cflow.c b/contrib/plugins/cflow.c new file mode 100644 index 00000000000..b5e33f25f9b --- /dev/null +++ b/contrib/plugins/cflow.c @@ -0,0 +1,393 @@ +/* + * 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 current translated block */ + uint64_t tb_pc; + /* 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 tb_pc; +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, gpointer d) +{ + 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, gpointer d) +{ + 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, gpointer d) +{ + 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; + GCompareDataFunc 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_with_data(data, sort, NULL); + + 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(g_int64_hash, g_int64_equal); + state = qemu_plugin_scoreboard_new(sizeof(VCPUScoreBoard)); + + /* score board declarations */ + tb_pc = qemu_plugin_scoreboard_u64_in_struct(state, VCPUScoreBoard, tb_pc); + 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, &addr); + if (!node && create_if_not_found) { + node = create_node(addr); + g_hash_table_insert(nodes, &node->addr, 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 = qemu_plugin_u64_get(tb_pc, cpu_index); + + /* 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. + */ + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_STORE_U64, tb_pc, 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, NULL); + + /* + * 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/execlog.c b/contrib/plugins/execlog.c index 7129d526f81..d67d0107613 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; + + g_byte_array_set_size(reg->new, 0); + sz = qemu_plugin_read_register(reg->handle, reg->new); + g_assert(sz == reg->last->len); - /* Find or create vCPU in array */ - if (cpu_index >= last_exec->len) { - expand_last_exec(cpu_index); + 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,11 +450,8 @@ 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]; @@ -232,13 +460,22 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, 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 6b74d25fead..98404b68852 100644 --- a/contrib/plugins/hotblocks.c +++ b/contrib/plugins/hotblocks.c @@ -29,21 +29,45 @@ static guint64 limit = 20; * * The internals of the TCG are not exposed to plugins so we can only * get the starting PC for each block. We cheat this slightly by - * xor'ing the number of instructions to the hash to help + * checking the number of instructions as well to help * differentiate. */ 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; -static gint cmp_exec_count(gconstpointer a, gconstpointer b) +static gint cmp_exec_count(gconstpointer a, gconstpointer b, gpointer d) { 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 guint exec_count_hash(gconstpointer v) +{ + const ExecCount *e = v; + return e->start_addr ^ e->insns; +} + +static gboolean exec_count_equal(gconstpointer v1, gconstpointer v2) +{ + const ExecCount *ea = v1; + const ExecCount *eb = v2; + return (ea->start_addr == eb->start_addr) && + (ea->insns == eb->insns); +} + +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,45 +76,43 @@ 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); - it = g_list_sort(counts, cmp_exec_count); + it = g_list_sort_with_data(counts, cmp_exec_count, NULL); if (it) { g_string_append_printf(report, "pc, tcount, icount, ecount\n"); 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) { - hotblocks = g_hash_table_new(NULL, g_direct_equal); + hotblocks = g_hash_table_new(exec_count_hash, exec_count_equal); } 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); } /* @@ -103,10 +125,15 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) ExecCount *cnt; uint64_t pc = qemu_plugin_tb_vaddr(tb); size_t insns = qemu_plugin_tb_n_insns(tb); - uint64_t hash = pc ^ insns; g_mutex_lock(&lock); - cnt = (ExecCount *) g_hash_table_lookup(hotblocks, (gconstpointer) hash); + { + ExecCount e; + e.start_addr = pc; + e.insns = insns; + cnt = (ExecCount *) g_hash_table_lookup(hotblocks, &e); + } + if (cnt) { cnt->trans_count++; } else { @@ -114,18 +141,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; - g_hash_table_insert(hotblocks, (gpointer) hash, (gpointer) cnt); + cnt->exec_count = qemu_plugin_scoreboard_new(sizeof(uint64_t)); + g_hash_table_insert(hotblocks, cnt, 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); } } diff --git a/contrib/plugins/hotpages.c b/contrib/plugins/hotpages.c index 8316ae50c72..9d48ac969eb 100644 --- a/contrib/plugins/hotpages.c +++ b/contrib/plugins/hotpages.c @@ -48,7 +48,7 @@ typedef struct { static GMutex lock; static GHashTable *pages; -static gint cmp_access_count(gconstpointer a, gconstpointer b) +static gint cmp_access_count(gconstpointer a, gconstpointer b, gpointer d) { PageCounters *ea = (PageCounters *) a; PageCounters *eb = (PageCounters *) b; @@ -83,7 +83,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) if (counts && g_list_next(counts)) { GList *it; - it = g_list_sort(counts, cmp_access_count); + it = g_list_sort_with_data(counts, cmp_access_count, NULL); for (i = 0; i < limit && it->next; i++, it = it->next) { PageCounters *rec = (PageCounters *) it->data; @@ -103,7 +103,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) static void plugin_init(void) { page_mask = (page_size - 1); - pages = g_hash_table_new(NULL, g_direct_equal); + pages = g_hash_table_new(g_int64_hash, g_int64_equal); } static void vcpu_haddr(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, @@ -130,12 +130,12 @@ static void vcpu_haddr(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, page &= ~page_mask; g_mutex_lock(&lock); - count = (PageCounters *) g_hash_table_lookup(pages, GUINT_TO_POINTER(page)); + count = (PageCounters *) g_hash_table_lookup(pages, &page); if (!count) { count = g_new0(PageCounters, 1); count->page_address = page; - g_hash_table_insert(pages, GUINT_TO_POINTER(page), (gpointer) count); + g_hash_table_insert(pages, &count->page_address, count); } if (qemu_plugin_mem_is_store(meminfo)) { count->writes++; diff --git a/contrib/plugins/howvec.c b/contrib/plugins/howvec.c index 644a7856bb2..42bddb6566d 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; @@ -155,16 +155,19 @@ static ClassSelector class_tables[] = { static InsnClassExecCount *class_table; static int class_table_sz; -static gint cmp_exec_count(gconstpointer a, gconstpointer b) +static gint cmp_exec_count(gconstpointer a, gconstpointer b, gpointer d) { 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,11 +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) { + 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: @@ -203,7 +208,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) counts = g_hash_table_get_values(insns); if (counts && g_list_next(counts)) { g_string_append_printf(report, "Individual Instructions:\n"); - counts = g_list_sort(counts, cmp_exec_count); + counts = g_list_sort_with_data(counts, cmp_exec_count, NULL); for (i = 0; i < limit && g_list_next(counts); i++, counts = g_list_next(counts)) { @@ -212,7 +217,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) "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"); @@ -221,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); } @@ -232,15 +243,18 @@ 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; + /* if opcode is greater than 32 bits, we should refactor insn hash table. */ + G_STATIC_ASSERT(sizeof(opcode) == sizeof(uint32_t)); InsnClassExecCount *class = NULL; /* @@ -249,7 +263,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]; @@ -265,27 +279,29 @@ 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; g_mutex_lock(&lock); icount = (InsnExecCount *) g_hash_table_lookup(insns, - GUINT_TO_POINTER(opcode)); + (gpointer)(intptr_t) opcode); if (!icount) { icount = g_new0(InsnExecCount, 1); 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_hash_table_insert(insns, (gpointer)(intptr_t) opcode, icount); } g_mutex_unlock(&lock); - return &icount->count; + return icount->count.score; } default: g_assert_not_reached(); @@ -300,14 +316,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); @@ -322,6 +338,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]; diff --git a/contrib/plugins/hwprofile.c b/contrib/plugins/hwprofile.c index 739ac0c66b5..a9838ccc879 100644 --- a/contrib/plugins/hwprofile.c +++ b/contrib/plugins/hwprofile.c @@ -43,6 +43,8 @@ typedef struct { static GMutex lock; static GHashTable *devices; +static struct qemu_plugin_scoreboard *source_pc_scoreboard; +static qemu_plugin_u64 source_pc; /* track the access pattern to a piece of HW */ static bool pattern; @@ -69,7 +71,7 @@ static void plugin_init(void) devices = g_hash_table_new(NULL, NULL); } -static gint sort_cmp(gconstpointer a, gconstpointer b) +static gint sort_cmp(gconstpointer a, gconstpointer b, gpointer d) { DeviceCounts *ea = (DeviceCounts *) a; DeviceCounts *eb = (DeviceCounts *) b; @@ -77,7 +79,7 @@ static gint sort_cmp(gconstpointer a, gconstpointer b) eb->totals.reads + eb->totals.writes ? -1 : 1; } -static gint sort_loc(gconstpointer a, gconstpointer b) +static gint sort_loc(gconstpointer a, gconstpointer b, gpointer d) { IOLocationCounts *ea = (IOLocationCounts *) a; IOLocationCounts *eb = (IOLocationCounts *) b; @@ -124,13 +126,13 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) if (counts && g_list_next(counts)) { GList *it; - it = g_list_sort(counts, sort_cmp); + it = g_list_sort_with_data(counts, sort_cmp, NULL); while (it) { DeviceCounts *rec = (DeviceCounts *) it->data; if (rec->detail) { GList *accesses = g_hash_table_get_values(rec->detail); - GList *io_it = g_list_sort(accesses, sort_loc); + GList *io_it = g_list_sort_with_data(accesses, sort_loc, NULL); const char *prefix = pattern ? "off" : "pc"; g_string_append_printf(report, "%s @ 0x%"PRIx64"\n", rec->name, rec->base); @@ -159,7 +161,7 @@ static DeviceCounts *new_count(const char *name, uint64_t base) count->name = name; count->base = base; if (pattern || source) { - count->detail = g_hash_table_new(NULL, NULL); + count->detail = g_hash_table_new(g_int64_hash, g_int64_equal); } g_hash_table_insert(devices, (gpointer) name, count); return count; @@ -169,7 +171,7 @@ static IOLocationCounts *new_location(GHashTable *table, uint64_t off_or_pc) { IOLocationCounts *loc = g_new0(IOLocationCounts, 1); loc->off_or_pc = off_or_pc; - g_hash_table_insert(table, (gpointer) off_or_pc, loc); + g_hash_table_insert(table, &loc->off_or_pc, loc); return loc; } @@ -224,12 +226,12 @@ static void vcpu_haddr(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, /* either track offsets or source of access */ if (source) { - off = (uint64_t) udata; + off = qemu_plugin_u64_get(source_pc, cpu_index); } if (pattern || source) { IOLocationCounts *io_count = g_hash_table_lookup(counts->detail, - (gpointer) off); + &off); if (!io_count) { io_count = new_location(counts->detail, off); } @@ -247,10 +249,14 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) for (i = 0; i < n; i++) { struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); - gpointer udata = (gpointer) (source ? qemu_plugin_insn_vaddr(insn) : 0); + if (source) { + uint64_t pc = qemu_plugin_insn_vaddr(insn); + qemu_plugin_register_vcpu_mem_inline_per_vcpu( + insn, rw, QEMU_PLUGIN_INLINE_STORE_U64, + source_pc, pc); + } qemu_plugin_register_vcpu_mem_cb(insn, vcpu_haddr, - QEMU_PLUGIN_CB_NO_REGS, - rw, udata); + QEMU_PLUGIN_CB_NO_REGS, rw, NULL); } } @@ -306,10 +312,9 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, return -1; } - /* Just warn about overflow */ - if (info->system.smp_vcpus > 64 || - info->system.max_vcpus > 64) { - fprintf(stderr, "hwprofile: can only track up to 64 CPUs\n"); + if (source) { + source_pc_scoreboard = qemu_plugin_scoreboard_new(sizeof(uint64_t)); + source_pc = qemu_plugin_scoreboard_u64(source_pc_scoreboard); } plugin_init(); diff --git a/contrib/plugins/ips.c b/contrib/plugins/ips.c new file mode 100644 index 00000000000..f110c565bc4 --- /dev/null +++ b/contrib/plugins/ips.c @@ -0,0 +1,215 @@ +/* + * 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); +} + +typedef struct { + const char *suffix; + unsigned long multipler; +} ScaleEntry; + +/* a bit like units.h but not binary */ +static const ScaleEntry scales[] = { + { "k", 1000 }, + { "m", 1000 * 1000 }, + { "g", 1000 * 1000 * 1000 }, +}; + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, int argc, + char **argv) +{ + bool ipq_set = 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], "ips") == 0) { + char *endptr = NULL; + max_insn_per_second = g_ascii_strtoull(tokens[1], &endptr, 10); + if (!max_insn_per_second && errno) { + fprintf(stderr, "%s: couldn't parse %s (%s)\n", + __func__, tokens[1], g_strerror(errno)); + return -1; + } + + if (endptr && *endptr != 0) { + g_autofree gchar *lower = g_utf8_strdown(endptr, -1); + unsigned long scale = 0; + + for (int j = 0; j < G_N_ELEMENTS(scales); j++) { + if (g_strcmp0(lower, scales[j].suffix) == 0) { + scale = scales[j].multipler; + break; + } + } + + if (scale) { + max_insn_per_second *= scale; + } else { + fprintf(stderr, "bad suffix: %s\n", endptr); + return -1; + } + } + } else if (g_strcmp0(tokens[0], "ipq") == 0) { + max_insn_per_quantum = g_ascii_strtoull(tokens[1], NULL, 10); + + if (!max_insn_per_quantum) { + fprintf(stderr, "bad ipq value: %s\n", tokens[0]); + return -1; + } + ipq_set = true; + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + + vcpus = qemu_plugin_scoreboard_new(sizeof(vCPUTime)); + + if (!ipq_set) { + 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 682b11feb23..62981d4e098 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 }; @@ -134,10 +160,13 @@ static void report_divergance(ExecState *us, ExecState *them) /* Output short log entry of going out of sync... */ if (verbose || divrec.distance == 1 || diverged) { - g_string_printf(out, - "@ 0x%016" PRIx64 " vs 0x%016" PRIx64 + g_string_printf(out, "@ " + "0x%016" PRIx64 " (%" PRId64 ") vs " + "0x%016" PRIx64 " (%" PRId64 ")" " (%d/%d since last)\n", - us->pc, them->pc, g_slist_length(divergence_log), + us->pc, us->insn_count, + them->pc, them->insn_count, + g_slist_length(divergence_log), divrec.distance); qemu_plugin_outs(out->str); } @@ -146,10 +175,7 @@ static void report_divergance(ExecState *us, ExecState *them) int i; GSList *entry; - g_string_printf(out, - "Δ insn_count @ 0x%016" PRIx64 - " (%ld) vs 0x%016" PRIx64 " (%ld)\n", - us->pc, us->insn_count, them->pc, them->insn_count); + g_string_printf(out, "Δ too high, we have diverged, previous insns\n"); for (entry = log, i = 0; g_slist_next(entry) && i < 5; @@ -162,7 +188,7 @@ static void report_divergance(ExecState *us, ExecState *them) 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); } } @@ -245,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); @@ -254,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); @@ -287,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) { @@ -295,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"); @@ -336,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 00000000000..1876bc78438 --- /dev/null +++ b/contrib/plugins/meson.build @@ -0,0 +1,30 @@ +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: win32_qemu_plugin_api_link_flags, + 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: [python, '-c', '']) +endif + +plugin_modules += t diff --git a/contrib/plugins/stoptrigger.c b/contrib/plugins/stoptrigger.c new file mode 100644 index 00000000000..b3a6ed66a7b --- /dev/null +++ b/contrib/plugins/stoptrigger.c @@ -0,0 +1,157 @@ +/* + * 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; + uint64_t current_pc; +} InstructionsCount; +static struct qemu_plugin_scoreboard *insn_count_sb; +static qemu_plugin_u64 insn_count; +static qemu_plugin_u64 current_pc; + +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; + +typedef struct { + uint64_t exit_addr; + int exit_code; +} ExitInfo; + +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 = qemu_plugin_u64_get(current_pc, cpu_index); + 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) +{ + ExitInfo *ei = udata; + g_assert(ei); + char *msg = g_strdup_printf("0x%" PRIx64 " reached, exiting\n", ei->exit_addr); + exit_emulation(ei->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); + uint64_t insn_vaddr = 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_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_STORE_U64, current_pc, insn_vaddr); + 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, NULL); + } + + if (exit_on_address) { + ExitInfo *ei = g_hash_table_lookup(addrs_ht, &insn_vaddr); + if (ei) { + /* Exit triggered by address */ + qemu_plugin_register_vcpu_insn_exec_cb( + insn, exit_address_reached, QEMU_PLUGIN_CB_NO_REGS, ei); + } + } + } +} + +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_full(g_int64_hash, g_int64_equal, NULL, g_free); + + insn_count_sb = qemu_plugin_scoreboard_new(sizeof(InstructionsCount)); + insn_count = qemu_plugin_scoreboard_u64_in_struct( + insn_count_sb, InstructionsCount, insn_count); + current_pc = qemu_plugin_scoreboard_u64_in_struct( + insn_count_sb, InstructionsCount, current_pc); + + 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); + ExitInfo *ei = g_malloc(sizeof(ExitInfo)); + ei->exit_addr = g_ascii_strtoull(addr_tokens[0], NULL, 0); + ei->exit_code = 0; + if (addr_tokens[1]) { + ei->exit_code = g_ascii_strtoull(addr_tokens[1], NULL, 0); + } + g_hash_table_insert(addrs_ht, &ei->exit_addr, ei); + 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 00000000000..7534b2b8bf8 --- /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 771ca01e03f..00000000000 --- 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 36c9c896304..00000000000 --- 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 07a47229137..00000000000 --- 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 00000000000..8fd397bf79a --- /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 00000000000..183e8304d6e --- /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 dcb9e2ffcd0..ac1eece37a4 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 89e5f11a64e..6cc18a1c04f 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" @@ -194,8 +195,8 @@ 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 = buf; - uint64_t range[2] = { le64toh(desc->sector) << 9, - le32toh(desc->num_sectors) << 9 }; + 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 { @@ -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; } diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index aa304475a0a..bb41758e345 100644 --- a/contrib/vhost-user-gpu/vhost-user-gpu.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -834,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, @@ -842,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); } diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c index 1da6cc15882..51da0e3667f 100644 --- a/contrib/vhost-user-gpu/virgl.c +++ b/contrib/vhost-user-gpu/virgl.c @@ -327,7 +327,7 @@ virgl_get_resource_info_modifiers(uint32_t resource_id, #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 < 0) { + if (ret) { return ret; } @@ -335,7 +335,7 @@ virgl_get_resource_info_modifiers(uint32_t resource_id, *modifiers = info_ext.modifiers; #else ret = virgl_renderer_resource_get_info(resource_id, info); - if (ret < 0) { + if (ret) { return ret; } @@ -372,7 +372,7 @@ virgl_cmd_set_scanout(VuGpu *g, uint64_t modifiers = 0; ret = virgl_get_resource_info_modifiers(ss.resource_id, &info, &modifiers); - if (ret == -1) { + if (ret) { g_critical("%s: illegal resource specified %d\n", __func__, ss.resource_id); cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; @@ -401,7 +401,7 @@ virgl_cmd_set_scanout(VuGpu *g, if (g->use_modifiers) { /* - * The mesage uses all the fields set in dmabuf_scanout plus + * The message uses all the fields set in dmabuf_scanout plus * modifiers which is appended after VhostUserGpuDMABUFScanout. */ msg.request = VHOST_USER_GPU_DMABUF_SCANOUT2; diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h index 509b679f032..654c392fbbf 100644 --- a/contrib/vhost-user-gpu/vugpu.h +++ b/contrib/vhost-user-gpu/vugpu.h @@ -164,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) diff --git a/contrib/vhost-user-input/main.c b/contrib/vhost-user-input/main.c index 081230da548..f3362d41ac4 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 21a9ed4f15e..840d866594b 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 cc893f6f203..44be04853e4 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/contrib/vmapple/uuid.sh b/contrib/vmapple/uuid.sh new file mode 100755 index 00000000000..f5637221d23 --- /dev/null +++ b/contrib/vmapple/uuid.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# +# Used for converting a guest provisioned using Virtualization.framework +# for use with the QEMU 'vmapple' aarch64 machine type. +# +# Extracts the Machine UUID from Virtualization.framework VM JSON file. +# (as produced by 'macosvm', passed as command line argument) +# +# SPDX-License-Identifier: GPL-2.0-or-later + +plutil -extract machineId raw "$1" | base64 -d | plutil -extract ECID raw - + diff --git a/cpu-common.c b/cpu-common.c new file mode 100644 index 00000000000..ef5757d23bf --- /dev/null +++ b/cpu-common.c @@ -0,0 +1,454 @@ +/* + * 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 "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) +{ + CPUBreakpoint *bp; + + if (cpu->cc->gdb_adjust_breakpoint) { + pc = cpu->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) +{ + CPUBreakpoint *bp; + + if (cpu->cc->gdb_adjust_breakpoint) { + pc = cpu->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 00000000000..1c90a307593 --- /dev/null +++ b/cpu-target.c @@ -0,0 +1,93 @@ +/* + * 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 "cpu.h" +#include "system/accel-ops.h" +#include "system/cpus.h" +#include "exec/cpu-common.h" +#include "exec/tswap.h" +#include "exec/replay-core.h" +#include "exec/log.h" +#include "hw/core/cpu.h" +#include "trace/trace-root.h" + +/* Validate correct placement of CPUArchState. */ +QEMU_BUILD_BUG_ON(offsetof(ArchCPU, parent_obj) != 0); +QEMU_BUILD_BUG_ON(offsetof(ArchCPU, env) != sizeof(CPUState)); + +/* 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(); +} + +#undef target_big_endian +bool target_big_endian(void) +{ + return TARGET_BIG_ENDIAN; +} diff --git a/cpu.c b/cpu.c deleted file mode 100644 index 0769b0b1539..00000000000 --- a/cpu.c +++ /dev/null @@ -1,442 +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/cpus.h" -#include "sysemu/tcg.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" -#include "qemu/plugin.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 - - /* Call the plugin hook before clearing cpu->cpu_index in cpu_list_remove */ - if (tcg_enabled()) { - qemu_plugin_vcpu_exit_hook(cpu); - } - - cpu_list_remove(cpu); - /* - * Now that the vCPU has been removed from the RCU list, we can call - * tcg_exec_unrealizefn, which may free fields using call_rcu. - */ - if (tcg_enabled()) { - tcg_exec_unrealizefn(cpu); - } -} - -/* - * 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(void) -{ - /* 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(hwaddr 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 - -/* 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; - - 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) -{ - return TARGET_BIG_ENDIAN; -} - -const char *target_name(void) -{ - return TARGET_NAME; -} - -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 45c745ecf69..00000000000 --- a/cpus-common.c +++ /dev/null @@ -1,442 +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" -#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); -} - -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_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; - - 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 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_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/crypto/afalg.c b/crypto/afalg.c index 52a491dbb51..246d0679d43 100644 --- a/crypto/afalg.c +++ b/crypto/afalg.c @@ -66,13 +66,13 @@ qcrypto_afalg_socket_bind(const char *type, const char *name, 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); + afalg = g_new0(QCryptoAFAlgo, 1); /* initialize crypto API socket */ afalg->opfd = -1; afalg->tfmfd = qcrypto_afalg_socket_bind(type, name, errp); @@ -93,7 +93,7 @@ qcrypto_afalg_comm_alloc(const char *type, const char *name, return NULL; } -void qcrypto_afalg_comm_free(QCryptoAFAlg *afalg) +void qcrypto_afalg_comm_free(QCryptoAFAlgo *afalg) { if (!afalg) { return; diff --git a/crypto/afalgpriv.h b/crypto/afalgpriv.h index 5a2393f1b7e..3fdcc0f8316 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 b1a5a208995..b2e383aa678 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 abb1fb272e4..bcf030fdec4 100644 --- a/crypto/akcipher-gcrypt.c.inc +++ b/crypto/akcipher-gcrypt.c.inc @@ -26,14 +26,14 @@ #include "crypto/akcipher.h" #include "crypto/random.h" #include "qapi/error.h" -#include "sysemu/cryptodev.h" +#include "system/cryptodev.h" #include "rsakey.h" 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 02699e6e6d1..1d4bd6960e0 100644 --- a/crypto/akcipher-nettle.c.inc +++ b/crypto/akcipher-nettle.c.inc @@ -26,15 +26,15 @@ #include "crypto/akcipher.h" #include "crypto/random.h" #include "qapi/error.h" -#include "sysemu/cryptodev.h" +#include "system/cryptodev.h" #include "rsakey.h" 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 e4bbc6e5f1a..0a0576b7922 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 739f639bcf2..3b33e54f081 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.c b/crypto/block-luks.c index fb01ec38bbf..0926ad28f0c 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -33,6 +33,7 @@ #include "qemu/uuid.h" #include "qemu/bitmap.h" +#include "qemu/range.h" /* * Reference for the LUKS format implemented here is @@ -67,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); @@ -111,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; @@ -167,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 = @@ -183,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; } @@ -211,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) @@ -250,9 +262,9 @@ qcrypto_block_luks_has_format(const uint8_t *buf, * 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); @@ -262,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); @@ -318,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; } } @@ -457,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) { @@ -494,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"); @@ -543,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); @@ -558,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); @@ -645,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; @@ -1175,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; @@ -1203,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; } @@ -1248,7 +1262,6 @@ qcrypto_block_luks_open(QCryptoBlock *block, luks->cipher_mode, masterkey, luks->header.master_key_len, - n_threads, errp) < 0) { goto fail; } @@ -1257,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; @@ -1301,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; } } @@ -1369,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) { @@ -1396,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, @@ -1440,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; } @@ -1529,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; @@ -1833,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); @@ -1858,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; } @@ -1867,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 4d7cf36a8f5..054078b8950 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 7bb4b74a37c..96c83e60b9f 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; - - 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); + /* 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]; + } + } - 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; - block->n_free_ciphers++; + 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); + } - qemu_mutex_unlock(&block->mutex); + block->free_ciphers[block->n_free_ciphers] = cipher; + block->n_free_ciphers++; } 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 3c7ccea5040..edf0b3a3d9f 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 3df8fc54c05..4980d419c43 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 deleted file mode 100644 index b409089095c..00000000000 --- a/crypto/cipher-builtin.c.inc +++ /dev/null @@ -1,303 +0,0 @@ -/* - * QEMU Crypto cipher built-in algorithms - * - * Copyright (c) 2015 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 "crypto/aes.h" - -typedef struct QCryptoCipherBuiltinAESContext QCryptoCipherBuiltinAESContext; -struct QCryptoCipherBuiltinAESContext { - AES_KEY enc; - AES_KEY dec; -}; - -typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES; -struct QCryptoCipherBuiltinAES { - QCryptoCipher base; - QCryptoCipherBuiltinAESContext key; - uint8_t iv[AES_BLOCK_SIZE]; -}; - - -static inline bool qcrypto_length_check(size_t len, size_t blocksize, - Error **errp) -{ - if (unlikely(len & (blocksize - 1))) { - error_setg(errp, "Length %zu must be a multiple of block size %zu", - len, blocksize); - return false; - } - return true; -} - -static void qcrypto_cipher_ctx_free(QCryptoCipher *cipher) -{ - g_free(cipher); -} - -static int qcrypto_cipher_no_setiv(QCryptoCipher *cipher, - const uint8_t *iv, size_t niv, - Error **errp) -{ - error_setg(errp, "Setting IV is not supported"); - return -1; -} - -static void do_aes_encrypt_ecb(const void *vctx, - size_t len, - uint8_t *out, - const uint8_t *in) -{ - const QCryptoCipherBuiltinAESContext *ctx = vctx; - - /* We have already verified that len % AES_BLOCK_SIZE == 0. */ - while (len) { - AES_encrypt(in, out, &ctx->enc); - in += AES_BLOCK_SIZE; - out += AES_BLOCK_SIZE; - len -= AES_BLOCK_SIZE; - } -} - -static void do_aes_decrypt_ecb(const void *vctx, - size_t len, - uint8_t *out, - const uint8_t *in) -{ - const QCryptoCipherBuiltinAESContext *ctx = vctx; - - /* We have already verified that len % AES_BLOCK_SIZE == 0. */ - while (len) { - AES_decrypt(in, out, &ctx->dec); - in += AES_BLOCK_SIZE; - out += AES_BLOCK_SIZE; - len -= AES_BLOCK_SIZE; - } -} - -static void do_aes_encrypt_cbc(const AES_KEY *key, - size_t len, - uint8_t *out, - const uint8_t *in, - uint8_t *ivec) -{ - uint8_t tmp[AES_BLOCK_SIZE]; - size_t n; - - /* We have already verified that len % AES_BLOCK_SIZE == 0. */ - while (len) { - for (n = 0; n < AES_BLOCK_SIZE; ++n) { - tmp[n] = in[n] ^ ivec[n]; - } - AES_encrypt(tmp, out, key); - memcpy(ivec, out, AES_BLOCK_SIZE); - len -= AES_BLOCK_SIZE; - in += AES_BLOCK_SIZE; - out += AES_BLOCK_SIZE; - } -} - -static void do_aes_decrypt_cbc(const AES_KEY *key, - size_t len, - uint8_t *out, - const uint8_t *in, - uint8_t *ivec) -{ - uint8_t tmp[AES_BLOCK_SIZE]; - size_t n; - - /* We have already verified that len % AES_BLOCK_SIZE == 0. */ - while (len) { - memcpy(tmp, in, AES_BLOCK_SIZE); - AES_decrypt(in, out, key); - for (n = 0; n < AES_BLOCK_SIZE; ++n) { - out[n] ^= ivec[n]; - } - memcpy(ivec, tmp, AES_BLOCK_SIZE); - len -= AES_BLOCK_SIZE; - in += AES_BLOCK_SIZE; - out += AES_BLOCK_SIZE; - } -} - -static int qcrypto_cipher_aes_encrypt_ecb(QCryptoCipher *cipher, - const void *in, void *out, - size_t len, Error **errp) -{ - QCryptoCipherBuiltinAES *ctx - = container_of(cipher, QCryptoCipherBuiltinAES, base); - - if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { - return -1; - } - do_aes_encrypt_ecb(&ctx->key, len, out, in); - return 0; -} - -static int qcrypto_cipher_aes_decrypt_ecb(QCryptoCipher *cipher, - const void *in, void *out, - size_t len, Error **errp) -{ - QCryptoCipherBuiltinAES *ctx - = container_of(cipher, QCryptoCipherBuiltinAES, base); - - if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { - return -1; - } - do_aes_decrypt_ecb(&ctx->key, len, out, in); - return 0; -} - -static int qcrypto_cipher_aes_encrypt_cbc(QCryptoCipher *cipher, - const void *in, void *out, - size_t len, Error **errp) -{ - QCryptoCipherBuiltinAES *ctx - = container_of(cipher, QCryptoCipherBuiltinAES, base); - - if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { - return -1; - } - do_aes_encrypt_cbc(&ctx->key.enc, len, out, in, ctx->iv); - return 0; -} - -static int qcrypto_cipher_aes_decrypt_cbc(QCryptoCipher *cipher, - const void *in, void *out, - size_t len, Error **errp) -{ - QCryptoCipherBuiltinAES *ctx - = container_of(cipher, QCryptoCipherBuiltinAES, base); - - if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { - return -1; - } - do_aes_decrypt_cbc(&ctx->key.dec, len, out, in, ctx->iv); - return 0; -} - -static int qcrypto_cipher_aes_setiv(QCryptoCipher *cipher, const uint8_t *iv, - size_t niv, Error **errp) -{ - QCryptoCipherBuiltinAES *ctx - = container_of(cipher, QCryptoCipherBuiltinAES, base); - - if (niv != AES_BLOCK_SIZE) { - error_setg(errp, "IV must be %d bytes not %zu", - AES_BLOCK_SIZE, niv); - return -1; - } - - memcpy(ctx->iv, iv, AES_BLOCK_SIZE); - return 0; -} - -static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_ecb = { - .cipher_encrypt = qcrypto_cipher_aes_encrypt_ecb, - .cipher_decrypt = qcrypto_cipher_aes_decrypt_ecb, - .cipher_setiv = qcrypto_cipher_no_setiv, - .cipher_free = qcrypto_cipher_ctx_free, -}; - -static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_cbc = { - .cipher_encrypt = qcrypto_cipher_aes_encrypt_cbc, - .cipher_decrypt = qcrypto_cipher_aes_decrypt_cbc, - .cipher_setiv = qcrypto_cipher_aes_setiv, - .cipher_free = qcrypto_cipher_ctx_free, -}; - -bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, - QCryptoCipherMode mode) -{ - switch (alg) { - case QCRYPTO_CIPHER_ALG_AES_128: - case QCRYPTO_CIPHER_ALG_AES_192: - case QCRYPTO_CIPHER_ALG_AES_256: - switch (mode) { - case QCRYPTO_CIPHER_MODE_ECB: - case QCRYPTO_CIPHER_MODE_CBC: - return true; - default: - return false; - } - break; - default: - return false; - } -} - -static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, - QCryptoCipherMode mode, - const uint8_t *key, - size_t nkey, - Error **errp) -{ - if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) { - return NULL; - } - - switch (alg) { - case QCRYPTO_CIPHER_ALG_AES_128: - case QCRYPTO_CIPHER_ALG_AES_192: - case QCRYPTO_CIPHER_ALG_AES_256: - { - QCryptoCipherBuiltinAES *ctx; - const QCryptoCipherDriver *drv; - - switch (mode) { - case QCRYPTO_CIPHER_MODE_ECB: - drv = &qcrypto_cipher_aes_driver_ecb; - break; - case QCRYPTO_CIPHER_MODE_CBC: - drv = &qcrypto_cipher_aes_driver_cbc; - break; - default: - goto bad_mode; - } - - ctx = g_new0(QCryptoCipherBuiltinAES, 1); - ctx->base.driver = drv; - - if (AES_set_encrypt_key(key, nkey * 8, &ctx->key.enc)) { - error_setg(errp, "Failed to set encryption key"); - goto error; - } - if (AES_set_decrypt_key(key, nkey * 8, &ctx->key.dec)) { - error_setg(errp, "Failed to set decryption key"); - goto error; - } - - return &ctx->base; - - error: - g_free(ctx); - return NULL; - } - - default: - error_setg(errp, - "Unsupported cipher algorithm %s", - QCryptoCipherAlgorithm_str(alg)); - return NULL; - } - - bad_mode: - error_setg(errp, "Unsupported cipher mode %s", - QCryptoCipherMode_str(mode)); - return NULL; -} diff --git a/crypto/cipher-gcrypt.c.inc b/crypto/cipher-gcrypt.c.inc index a6a0117717f..12eb9ddb5a6 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 501e4e07a5b..b9450d48b0f 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 24cc61f87bf..ae913637721 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; + +static void sm4_encrypt_native(void *ctx, size_t length, + uint8_t *dst, const uint8_t *src) +{ + struct sm4_ctx *keys = ctx; + sm4_crypt(&keys[0], length, dst, src); +} + +static void sm4_decrypt_native(void *ctx, size_t length, + uint8_t *dst, const uint8_t *src) +{ + struct sm4_ctx *keys = ctx; + sm4_crypt(&keys[1], length, dst, src); +} + +DEFINE_ECB(qcrypto_nettle_sm4, + QCryptoNettleSm4, SM4_BLOCK_SIZE, + sm4_encrypt_native, sm4_decrypt_native) +#endif -bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, +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-stub.c.inc b/crypto/cipher-stub.c.inc new file mode 100644 index 00000000000..1b7ea81eac4 --- /dev/null +++ b/crypto/cipher-stub.c.inc @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * QEMU Crypto cipher impl stub + * + * Copyright (c) 2025 Red Hat, Inc. + * + */ + +bool qcrypto_cipher_supports(QCryptoCipherAlgo alg, + QCryptoCipherMode mode) +{ + return false; +} + +static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, + QCryptoCipherMode mode, + const uint8_t *key, + size_t nkey, + Error **errp) +{ + if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) { + return NULL; + } + + error_setg(errp, + "Unsupported cipher algorithm %s, no crypto library enabled in build", + QCryptoCipherAlgo_str(alg)); + return NULL; +} diff --git a/crypto/cipher.c b/crypto/cipher.c index 74b09a5b261..229710f76b4 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; } @@ -139,10 +145,10 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg, #elif defined CONFIG_GNUTLS_CRYPTO #include "cipher-gnutls.c.inc" #else -#include "cipher-builtin.c.inc" +#include "cipher-stub.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 396527857d6..64737ce9615 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 00000000000..9e3e61a77df --- /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 ebbecfc3fe1..81367524c3a 100644 --- a/crypto/der.c +++ b/crypto/der.c @@ -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 f4ba6da28a0..bcfa4a24952 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 SEQUENCE as OCTET STRING. - */ -void qcrypto_der_encode_octet_str_begin(QCryptoEncodeContext *ctx); - -/** - * qcrypto_der_encode_octet_str_end: - * @ctx: the encode context. - * - * Finish encoding a octet string, All fields between - * qcrypto_der_encode_octet_str_begin and qcrypto_der_encode_octet_str_end - * are encoded as an octet string. This is useful when we need to encode a - * encoded SEQUENCE as OCTET STRING. - */ -void qcrypto_der_encode_octet_str_end(QCryptoEncodeContext *ctx); - /** * qcrypto_der_encode_ctx_buffer_len: * @ctx: the encode context. diff --git a/crypto/hash-afalg.c b/crypto/hash-afalg.c index 3ebea392920..bd3fe3b4272 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; @@ -58,18 +59,18 @@ qcrypto_afalg_hash_format_name(QCryptoHashAlgorithm alg, if (is_hmac) { name = g_strdup_printf("hmac(%s)", alg_name); } else { - name = g_strdup_printf("%s", alg_name); + name = g_strdup(alg_name); } 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,89 +99,160 @@ 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 int -qcrypto_afalg_hash_hmac_bytesv(QCryptoAFAlg *hmac, - QCryptoHashAlgorithm alg, - const struct iovec *iov, - size_t niov, uint8_t **result, - size_t *resultlen, - Error **errp) +static +QCryptoHash *qcrypto_afalg_hash_new(QCryptoHashAlgo alg, 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); + /* Check if hash algorithm is supported */ + char *alg_name = qcrypto_afalg_hash_format_name(alg, false, NULL); + QCryptoHash *hash; - 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; + if (alg_name == NULL) { + error_setg(errp, "Unknown hash algorithm %d", alg); + return NULL; } - if (is_hmac) { - afalg = hmac; - } else { - afalg = qcrypto_afalg_hash_ctx_new(alg, errp); - if (!afalg) { - return -1; - } + 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(afalg->opfd, iov, niov, - 0, iov_size(iov, niov), true); + 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"); - goto out; + 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 = *resultlen; + 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"); - } else { - ret = 0; + return -1; } -out: - if (!is_hmac) { - qcrypto_afalg_comm_free(afalg); - } - return ret; + 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_bytesv(QCryptoHashAlgorithm alg, - const struct iovec *iov, - size_t niov, uint8_t **result, - size_t *resultlen, - Error **errp) +qcrypto_afalg_hash_hmac_bytesv(QCryptoAFAlgo *hmac, + QCryptoHashAlgo 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); + int ret = 0; + + 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); + } + + return ret; } static int @@ -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 829e48258d3..af61c4e75da 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; - - if (!qcrypto_hash_supports(alg)) { - error_setg(errp, - "Unknown hash algorithm %d", - alg); - return -1; - } + QCryptoHash *hash; + gcry_error_t ret; - ret = gcry_md_open(&md, qcrypto_hash_alg_map[alg], 0); + hash = g_new(QCryptoHash, 1); + hash->alg = alg; + hash->opaque = g_new(gcry_md_hd_t, 1); - 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; +} - for (i = 0; i < niov; i++) { - gcry_md_write(md, iov[i].iov_base, iov[i].iov_len); +static +void qcrypto_gcrypt_hash_free(QCryptoHash *hash) +{ + gcry_md_hd_t *ctx = hash->opaque; + + if (ctx) { + gcry_md_close(*ctx); + g_free(ctx); } - 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; + 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); } - 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; + + 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; } - digest = gcry_md_read(md, 0); - if (!digest) { + if (*result_len == 0) { + *result_len = ret; + *result = g_new(uint8_t, *result_len); + } else if (*result_len != ret) { error_setg(errp, - "No digest produced"); - goto error; + "Result buffer size %zu is smaller than hash %d", + *result_len, ret); + return -1; } - memcpy(*result, digest, *resultlen); - gcry_md_close(md); + /* Digest is freed by gcry_md_close(), copy it */ + digest = gcry_md_read(*ctx, 0); + memcpy(*result, digest, *result_len); return 0; - - error: - gcry_md_close(md); - return -1; } - 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 82de9db705c..809cef98aee 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]); + 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 (i = 0; i < niov; i++) { - g_checksum_update(cs, iov[i].iov_base, iov[i].iov_len); + for (int i = 0; i < niov; i++) { + g_checksum_update(ctx, iov[i].iov_base, iov[i].iov_len); } - ret = g_checksum_type_get_length(qcrypto_hash_alg_map[alg]); + 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, "%s", - "Unable to get hash length"); - goto error; + error_setg(errp, "Unable to get hash length"); + *result_len = 0; + return -1; } - if (*resultlen == 0) { - *resultlen = ret; - *result = g_new0(uint8_t, *resultlen); - } else if (*resultlen != ret) { + + 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", - *resultlen, ret); - goto error; + *result_len, ret); + return -1; } - g_checksum_get_digest(cs, *result, resultlen); - - g_checksum_free(cs); + g_checksum_get_digest(ctx, *result, result_len); return 0; - - error: - g_checksum_free(cs); - return -1; } - 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 17911ac5d1c..99fbe824ead 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; - - if (!qcrypto_hash_supports(alg)) { - error_setg(errp, - "Unknown hash algorithm %d", - alg); - return -1; - } + QCryptoHash *hash; + int ret; - 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; - } + hash = g_new(QCryptoHash, 1); + hash->alg = alg; + hash->opaque = g_new(gnutls_hash_hd_t, 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; +} + +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; + } } - gnutls_hash_deinit(hash, *result); 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 1ca1a410628..53f68301efb 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 +QCryptoHash *qcrypto_nettle_hash_new(QCryptoHashAlgo alg, Error **errp) +{ + QCryptoHash *hash; + + 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 int -qcrypto_nettle_hash_bytesv(QCryptoHashAlgorithm alg, - const struct iovec *iov, - size_t niov, - uint8_t **result, - size_t *resultlen, - Error **errp) +static +void qcrypto_nettle_hash_free(QCryptoHash *hash) { - size_t i; - union qcrypto_hash_ctx ctx; + union qcrypto_hash_ctx *ctx = hash->opaque; - if (!qcrypto_hash_supports(alg)) { - error_setg(errp, - "Unknown hash algorithm %d", - alg); - return -1; - } + g_free(ctx); + g_free(hash); +} - 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; - } +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); } - 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) { + 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 %zu", - *resultlen, qcrypto_hash_alg_map[alg].len); + "Result buffer size %zu is smaller than hash %d", + *result_len, ret); return -1; } - qcrypto_hash_alg_map[alg].result(&ctx, *resultlen, *result); + 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 b0f8228bdcb..7513769e42d 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; + g_autoptr(QCryptoHash) ctx = qcrypto_hash_new(alg, 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; } -#endif - return qcrypto_hash_lib_driver.hash_bytesv(alg, iov, niov, - result, resultlen, - errp); + 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 (!ctx) { + return -1; + } - if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) { + 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 cee26ccb47e..83b9256886e 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 0c6f9797114..5273086eb9a 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 509bbc74c2c..ea80c8d1b23 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 24db383322c..822995505cd 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 1ad6c4f2530..dd5b2ab7a19 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 4de7e8c9cbb..422e005182a 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 62dfe8257af..f339596bd9a 100644 --- a/crypto/hmacpriv.h +++ b/crypto/hmacpriv.h @@ -28,7 +28,7 @@ struct QCryptoHmacDriver { void (*hmac_free)(QCryptoHmac *hmac); }; -void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg, +void *qcrypto_hmac_ctx_new(QCryptoHashAlgo alg, const uint8_t *key, size_t nkey, Error **errp); extern QCryptoHmacDriver qcrypto_hmac_lib_driver; @@ -37,7 +37,7 @@ extern QCryptoHmacDriver qcrypto_hmac_lib_driver; #include "afalgpriv.h" -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); extern QCryptoHmacDriver qcrypto_hmac_afalg_driver; diff --git a/crypto/init.c b/crypto/init.c index fb7f1bff105..674d237fa91 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 12822f85198..6b7d24d8897 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 cecdbedfde4..e3388d30be1 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 5f03a30d342..735635de1f3 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 a8d8e64f4d4..e89b8b1c768 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 2dfbbd382c2..f34423f918b 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 d6293c25a13..3ef9c1b52c4 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 9c4622e4247..9f29d0eed71 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 8d198c152cf..2989fc0a40f 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; + size_t scaled = 0; uint64_t ret = -1; g_autofree uint8_t *out = g_new(uint8_t, nout); uint64_t iterations = (1 << 15); @@ -114,13 +131,27 @@ uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, delta_ms = end_ms - start_ms; - if (delta_ms > 500) { + /* + * For very small 'iterations' values, CPU (or crypto + * accelerator) might be fast enough that the scheduler + * hasn't incremented getrusage() data, or incremented + * it by a very small amount, resulting in delta_ms == 0. + * Once we've scaled 'iterations' x10, 5 times, we really + * should be seeing delta_ms != 0, so sanity check at + * that point. + */ + if (scaled > 5 && + 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; } else { iterations = (iterations * 1000 / delta_ms); } + scaled++; } iterations = iterations * 1000 / delta_ms; @@ -129,5 +160,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 aeeacc8f9bf..6337b84c54e 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 cc49872e78d..b7f34b02345 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.c b/crypto/secret.c index 44eaff16f60..61a4584286c 100644 --- a/crypto/secret.c +++ b/crypto/secret.c @@ -117,7 +117,7 @@ qcrypto_secret_finalize(Object *obj) } static void -qcrypto_secret_class_init(ObjectClass *oc, void *data) +qcrypto_secret_class_init(ObjectClass *oc, const void *data) { QCryptoSecretCommonClass *sic = QCRYPTO_SECRET_COMMON_CLASS(oc); sic->load_data = qcrypto_secret_load_data; diff --git a/crypto/secret_common.c b/crypto/secret_common.c index 3441c44ca89..a5ecb876aeb 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, @@ -272,15 +263,12 @@ qcrypto_secret_finalize(Object *obj) } static void -qcrypto_secret_class_init(ObjectClass *oc, void *data) +qcrypto_secret_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); 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, @@ -387,7 +375,7 @@ static const TypeInfo qcrypto_secret_info = { .class_size = sizeof(QCryptoSecretCommonClass), .class_init = qcrypto_secret_class_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/crypto/secret_keyring.c b/crypto/secret_keyring.c index 1b7edec84a0..78d7f09b3b9 100644 --- a/crypto/secret_keyring.c +++ b/crypto/secret_keyring.c @@ -103,7 +103,7 @@ qcrypto_secret_prop_get_key(Object *obj, Visitor *v, static void -qcrypto_secret_keyring_class_init(ObjectClass *oc, void *data) +qcrypto_secret_keyring_class_init(ObjectClass *oc, const void *data) { QCryptoSecretCommonClass *sic = QCRYPTO_SECRET_COMMON_CLASS(oc); sic->load_data = qcrypto_secret_keyring_load_data; diff --git a/crypto/tls-cipher-suites.c b/crypto/tls-cipher-suites.c index 5e4f5974645..d9b61d0c08a 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; @@ -103,7 +102,8 @@ static GByteArray *qcrypto_tls_cipher_suites_fw_cfg_gen_data(Object *obj, errp); } -static void qcrypto_tls_cipher_suites_class_init(ObjectClass *oc, void *data) +static void qcrypto_tls_cipher_suites_class_init(ObjectClass *oc, + const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); FWCfgDataGeneratorClass *fwgc = FW_CFG_DATA_GENERATOR_CLASS(oc); @@ -118,7 +118,7 @@ static const TypeInfo qcrypto_tls_cipher_suites_info = { .instance_size = sizeof(QCryptoTLSCipherSuites), .class_size = sizeof(QCryptoTLSCredsClass), .class_init = qcrypto_tls_cipher_suites_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { TYPE_FW_CFG_DATA_GENERATOR_INTERFACE }, { } diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c index 084ce0d51ae..9e59594d673 100644 --- a/crypto/tlscreds.c +++ b/crypto/tlscreds.c @@ -223,7 +223,7 @@ qcrypto_tls_creds_prop_get_endpoint(Object *obj, static void -qcrypto_tls_creds_class_init(ObjectClass *oc, void *data) +qcrypto_tls_creds_class_init(ObjectClass *oc, const void *data) { object_class_property_add_bool(oc, "verify-peer", qcrypto_tls_creds_prop_get_verify, diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c index c0d23a0ef3c..44af9e6c9af 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) { @@ -168,15 +137,11 @@ qcrypto_tls_creds_anon_finalize(Object *obj) static void -qcrypto_tls_creds_anon_class_init(ObjectClass *oc, void *data) +qcrypto_tls_creds_anon_class_init(ObjectClass *oc, const 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); } @@ -187,7 +152,7 @@ static const TypeInfo qcrypto_tls_creds_anon_info = { .instance_finalize = qcrypto_tls_creds_anon_finalize, .class_size = sizeof(QCryptoTLSCredsAnonClass), .class_init = qcrypto_tls_creds_anon_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c index 546cad1c5a4..5b68a6b7ba2 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 @@ -266,15 +236,12 @@ qcrypto_tls_creds_psk_prop_get_username(Object *obj, } static void -qcrypto_tls_creds_psk_class_init(ObjectClass *oc, void *data) +qcrypto_tls_creds_psk_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); 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); @@ -288,7 +255,7 @@ static const TypeInfo qcrypto_tls_creds_psk_info = { .instance_finalize = qcrypto_tls_creds_psk_finalize, .class_size = sizeof(QCryptoTLSCredsPSKClass), .class_init = qcrypto_tls_creds_psk_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c index d14313925dd..63a72fe47c8 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, @@ -829,7 +802,7 @@ qcrypto_tls_creds_x509_finalize(Object *obj) static void -qcrypto_tls_creds_x509_class_init(ObjectClass *oc, void *data) +qcrypto_tls_creds_x509_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); QCryptoTLSCredsClass *ctcc = QCRYPTO_TLS_CREDS_CLASS(oc); @@ -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); @@ -858,7 +828,7 @@ static const TypeInfo qcrypto_tls_creds_x509_info = { .instance_finalize = qcrypto_tls_creds_x509_finalize, .class_size = sizeof(QCryptoTLSCredsX509Class), .class_init = qcrypto_tls_creds_x509_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/crypto/tlssession.c b/crypto/tlssession.c index 1e98f44e0d1..6d8f8df6232 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,26 +505,29 @@ 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; @@ -505,35 +546,69 @@ qcrypto_tls_session_handshake(QCryptoTLSSession *session, Error **errp) { int ret = gnutls_handshake(session->handle); - if (ret == 0) { + if (!ret) { session->handshakeComplete = true; + return QCRYPTO_TLS_HANDSHAKE_COMPLETE; + } + + if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { + int direction = gnutls_record_get_direction(session->handle); + return direction ? QCRYPTO_TLS_HANDSHAKE_SENDING : + QCRYPTO_TLS_HANDSHAKE_RECVING; + } + + 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 { - if (ret == GNUTLS_E_INTERRUPTED || - ret == GNUTLS_E_AGAIN) { - ret = 1; - } else { - error_setg(errp, "TLS handshake failed: %s", - gnutls_strerror(ret)); - ret = -1; - } + error_setg(errp, "TLS handshake failed: %s", + gnutls_strerror(ret)); } - return ret; + error_free(session->rerr); + error_free(session->werr); + session->rerr = session->werr = NULL; + + return -1; } -QCryptoTLSSessionHandshakeStatus -qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) +int +qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp) { - if (session->handshakeComplete) { - return QCRYPTO_TLS_HANDSHAKE_COMPLETE; - } else if (gnutls_record_get_direction(session->handle) == 0) { - return QCRYPTO_TLS_HANDSHAKE_RECVING; + int ret; + + if (!session->handshakeComplete) { + return 0; + } + + ret = gnutls_bye(session->handle, GNUTLS_SHUT_WR); + + if (!ret) { + return QCRYPTO_TLS_BYE_COMPLETE; + } + + if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { + int direction = gnutls_record_get_direction(session->handle); + return direction ? QCRYPTO_TLS_BYE_SENDING : QCRYPTO_TLS_BYE_RECVING; + } + + if (session->rerr || session->werr) { + error_setg(errp, "TLS termination failed: %s: %s", gnutls_strerror(ret), + error_get_pretty(session->rerr ? + session->rerr : session->werr)); } else { - return QCRYPTO_TLS_HANDSHAKE_SENDING; + error_setg(errp, "TLS termination failed: %s", gnutls_strerror(ret)); } -} + error_free(session->rerr); + error_free(session->werr); + session->rerr = session->werr = NULL; + + return -1; +} int qcrypto_tls_session_get_key_size(QCryptoTLSSession *session, @@ -605,9 +680,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; } @@ -615,9 +691,11 @@ 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; } @@ -638,10 +716,10 @@ qcrypto_tls_session_handshake(QCryptoTLSSession *sess, } -QCryptoTLSSessionHandshakeStatus -qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess) +int +qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp) { - return QCRYPTO_TLS_HANDSHAKE_COMPLETE; + return QCRYPTO_TLS_BYE_COMPLETE; } diff --git a/crypto/x509-utils.c b/crypto/x509-utils.c new file mode 100644 index 00000000000..8bad00a51b8 --- /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/cris.c b/disas/cris.c deleted file mode 100644 index 0b0a3fb9165..00000000000 --- 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 00000000000..21c2f03430b --- /dev/null +++ b/disas/disas-common.c @@ -0,0 +1,99 @@ +/* + * 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 "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; + s->info.endian = BFD_ENDIAN_UNKNOWN; + + if (cpu->cc->disas_set_info) { + cpu->cc->disas_set_info(cpu, &s->info); + g_assert(s->info.endian != BFD_ENDIAN_UNKNOWN); + } +} + +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 00000000000..8146fafe804 --- /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 index 84a01f126f5..ed32e704cc0 100644 --- a/disas/disas-internal.h +++ b/disas/disas-internal.h @@ -14,8 +14,12 @@ typedef struct CPUDebug { 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 index 48ac492c6ca..9c693618c27 100644 --- a/disas/disas-mon.c +++ b/disas/disas-mon.c @@ -7,10 +7,23 @@ #include "qemu/osdep.h" #include "disas-internal.h" #include "disas/disas.h" -#include "exec/memory.h" +#include "system/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) @@ -34,9 +47,12 @@ void monitor_disas(Monitor *mon, CPUState *cpu, uint64_t pc, 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; diff --git a/disas/disas-target.c b/disas/disas-target.c new file mode 100644 index 00000000000..48f3a365dce --- /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/disas.c b/disas/disas.c deleted file mode 100644 index 0d2d06c2ecc..00000000000 --- a/disas/disas.c +++ /dev/null @@ -1,335 +0,0 @@ -/* General "disassemble this chunk" code. Used for debugging. */ -#include "qemu/osdep.h" -#include "disas/disas-internal.h" -#include "elf.h" -#include "qemu/qemu-print.h" -#include "disas/disas.h" -#include "disas/capstone.h" -#include "hw/core/cpu.h" -#include "exec/memory.h" - -/* 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; -} - -void disas_initialize_debug_target(CPUDebug *s, CPUState *cpu) -{ - initialize_debug(s); - - s->cpu = cpu; - s->info.read_memory_func = target_read_memory; - s->info.print_address_func = print_address; - if (target_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); - } -} - -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) -#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 target_disas(FILE *out, CPUState *cpu, uint64_t code, size_t size) -{ - uint64_t pc; - int count; - CPUDebug s; - - disas_initialize_debug_target(&s, cpu); - s.info.fprintf_func = fprintf; - s.info.stream = out; - s.info.buffer_vma = code; - s.info.buffer_length = size; - - 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; - } - } -} - -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; -} - -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); - - disas_initialize_debug_target(&s, cpu); - s.info.fprintf_func = disas_gstring_printf; - s.info.stream = (FILE *)ds; /* abuse this slot */ - s.info.buffer_vma = addr; - s.info.buffer_length = size; - 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, 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; - - 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(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/hppa.c b/disas/hppa.c index dcf9a47f348..2b58434966c 100644 --- a/disas/hppa.c +++ b/disas/hppa.c @@ -606,7 +606,7 @@ struct pa_opcode In the args field, the following characters are unused: - ' " - / 34 6789:; ' + ' " - / 34 678 :; ' '@ C M [\] ' '` e g } ' @@ -650,6 +650,7 @@ Also these: | 6 bit field length at 19,27:31 (fixed extract/deposit) A 13 bit immediate at 18 (to support the BREAK instruction) ^ like b, but describes a control register + 9 like b, but describes a diagnose register ! sar (cr11) register D 26 bit immediate at 31 (to support the DIAG instruction) $ 9 bit immediate at 28 (to support POPBTS) @@ -1322,13 +1323,19 @@ static const struct pa_opcode pa_opcodes[] = { "fdce", 0x040012c0, 0xfc00ffdf, "cZx(b)", pa10, 0}, { "fdce", 0x040012c0, 0xfc003fdf, "cZx(s,b)", pa10, 0}, { "fice", 0x040002c0, 0xfc001fdf, "cZx(S,b)", pa10, 0}, -{ "diag", 0x14000000, 0xfc000000, "D", pa10, 0}, { "idtlbt", 0x04001800, 0xfc00ffff, "x,b", pa20, FLAG_STRICT}, { "iitlbt", 0x04000800, 0xfc00ffff, "x,b", pa20, FLAG_STRICT}, +/* completely undocumented, but used by ODE, HP-UX and Linux: */ +{ "mfcpu_pcxu", 0x140008a0, 0xfc9fffe0, "9,t", pa20, 0}, /* PCXU: mfdiag */ +{ "mtcpu_pcxu", 0x14001840, 0xfc00ffff, "x,9", pa20, 0}, + /* These may be specific to certain versions of the PA. Joel claimed they were 72000 (7200?) specific. However, I'm almost certain the mtcpu/mfcpu were undocumented, but available in the older 700 machines. */ +{ "mfcpu_c", 0x14000600, 0xfc00ffff, "9,x", pa10, 0}, /* PCXL: for dr0 and dr8 only */ +{ "mfcpu_t", 0x14001400, 0xfc9fffe0, "9,t", pa10, 0}, /* PCXL: all dr except dr0 and dr8 */ +{ "mtcpu_pcxl", 0x14000240, 0xfc00ffff, "x,9", pa11, 0}, /* PCXL: mtcpu for dr0 and dr8 */ { "mtcpu", 0x14001600, 0xfc00ffff, "x,^", pa10, 0}, { "mfcpu", 0x14001A00, 0xfc00ffff, "^,x", pa10, 0}, { "tocen", 0x14403600, 0xffffffff, "", pa10, 0}, @@ -1336,6 +1343,9 @@ static const struct pa_opcode pa_opcodes[] = { "shdwgr", 0x14402600, 0xffffffff, "", pa10, 0}, { "grshdw", 0x14400620, 0xffffffff, "", pa10, 0}, +/* instead of showing D only, show all other registers too */ +{ "diag", 0x14000000, 0xfc000000, "D x,9,t", pa10, 0}, + /* gfw and gfr are not in the HP PA 1.1 manual, but they are in either the Timex FPU or the Mustang ERS (not sure which) manual. */ { "gfw", 0x04001680, 0xfc00ffdf, "cZx(b)", pa11, 0}, @@ -1609,6 +1619,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])) @@ -1797,6 +1811,12 @@ fput_creg (unsigned reg, disassemble_info *info) (*info->fprintf_func) (info->stream, "%s", control_reg[reg]); } +static void +fput_dreg (unsigned reg, disassemble_info *info) +{ + (*info->fprintf_func) (info->stream, "dr%d", reg); +} + /* Print constants with sign. */ static void @@ -1968,6 +1988,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]; @@ -1997,6 +2023,9 @@ print_insn_hppa (bfd_vma memaddr, disassemble_info *info) case '^': fput_creg (GET_FIELD (insn, 6, 10), info); break; + case '9': + fput_dreg (GET_FIELD (insn, 6, 10), info); + break; case 't': fput_reg (GET_FIELD (insn, 27, 31), info); break; @@ -2826,6 +2855,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 aefaecfbd6c..800b4145ac9 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 815523ab85e..bbfa1197835 100644 --- a/disas/meson.build +++ b/disas/meson.build @@ -1,11 +1,9 @@ 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', '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', 'riscv-xthead.c', @@ -15,7 +13,11 @@ 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'), capstone]) -common_ss.add(files('disas.c')) - +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 0b89b9c4faf..197327fae41 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 get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW) -#define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW) - -/* Local function prototypes. */ +#define PRIreg register_prefix "%ld" +#define PRIrfsl register_prefix "fsl%ld" +#define PRIpvr register_prefix "pvr%d" +#define PRIimm "%d" -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) -{ - char tmpstr[25]; - sprintf(tmpstr, "%s%d", register_prefix, (int)((instr & mask) >> low)); - return(strdup(tmpstr)); -} - -static char * -get_field_imm (long instr) -{ - 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)); -} +#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) -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)); -} +#define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW) +#define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW) -#if 0 -static char * -get_field_unsigned_imm (long instr) +static int get_field_special(long instr, const struct op_code_struct *op) { - char tmpstr[25]; - sprintf(tmpstr, "%d", (int)((instr & IMM_MASK) >> IMM_LOW)); - return(strdup(tmpstr)); + return ((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask; } -#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) +/* Returns NULL for PVR registers, which should be rendered differently. */ +static const char *get_special_name(int special) { - 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; - - info->bytes_per_chunk = 4; - - inst = read_insn_microblaze (memaddr, info, &op); - if (inst == 0) { - return -1; - } + 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; + + 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; + 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; + } + } } - 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; } - } - } - /* 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; - } + + 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; } - - /* Say how many bytes we consumed? */ - return 4; + return 4; } diff --git a/disas/nanomips.c b/disas/nanomips.c index a0253598dd6..db0c297b8dc 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 98ac07d72e9..00000000000 --- 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 00000000000..9859f234195 --- /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 index 99da679d16c..fcca326d1c3 100644 --- a/disas/riscv-xthead.c +++ b/disas/riscv-xthead.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "qemu/osdep.h" #include "disas/riscv.h" #include "disas/riscv-xthead.h" diff --git a/disas/riscv-xventana.c b/disas/riscv-xventana.c index a0224d1fb31..cd694f15f32 100644 --- a/disas/riscv-xventana.c +++ b/disas/riscv-xventana.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "qemu/osdep.h" #include "disas/riscv.h" #include "disas/riscv-xventana.h" diff --git a/disas/riscv.c b/disas/riscv.c index 3873a691576..85cd2a9c2ae 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -862,6 +862,128 @@ typedef enum { 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; /* register names */ @@ -1540,7 +1662,7 @@ const rv_opcode_data rvi_opcode_data[] = { { "aes32esi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, { "aes32dsmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, { "aes32dsi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, - { "aes64ks1i", rv_codec_k_rnum, rv_fmt_rd_rs1_rnum, NULL, 0, 0, 0 }, + { "aes64ks1i", rv_codec_k_rnum, rv_fmt_rd_rs1_rnum, NULL, 0, 0, 0 }, { "aes64ks2", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "aes64im", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, { "aes64esm", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, @@ -2008,6 +2130,130 @@ const rv_opcode_data rvi_opcode_data[] = { { "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 */ @@ -2025,6 +2271,7 @@ 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"; @@ -2102,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"; @@ -2116,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"; @@ -2128,9 +2438,11 @@ static const char *csr_name(int csrno) case 0x07a1: return "tdata1"; case 0x07a2: return "tdata2"; case 0x07a3: return "tdata3"; + case 0x07a4: return "tinfo"; case 0x07b0: return "dcsr"; case 0x07b1: return "dpc"; - case 0x07b2: return "dscratch"; + case 0x07b2: return "dscratch0"; + case 0x07b3: return "dscratch1"; case 0x0b00: return "mcycle"; case 0x0b01: return "mtime"; case 0x0b02: return "minstret"; @@ -2301,6 +2613,19 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 2: op = rv_op_c_li; break; case 3: + if (dec->cfg && 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; @@ -2389,7 +2714,7 @@ 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)) { + if (dec->cfg && dec->cfg->ext_zcmp && ((inst >> 12) & 0b01)) { switch ((inst >> 8) & 0b01111) { case 8: if (((inst >> 4) & 0b01111) >= 4) { @@ -2415,7 +2740,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } else { switch ((inst >> 10) & 0b011) { case 0: - if (!dec->cfg->ext_zcmt) { + if (dec->cfg && !dec->cfg->ext_zcmt) { break; } if (((inst >> 2) & 0xFF) >= 32) { @@ -2425,7 +2750,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 3: - if (!dec->cfg->ext_zcmp) { + if (dec->cfg && !dec->cfg->ext_zcmp) { break; } switch ((inst >> 5) & 0b011) { @@ -2631,7 +2956,13 @@ 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 && dec->cfg->ext_zicfilp && + (((inst >> 7) & 0b11111) == 0b00000)) { + op = rv_op_lpad; + } + break; case 6: switch ((inst >> 12) & 0b111) { case 0: op = rv_op_addiw; break; @@ -2732,9 +3063,13 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 11: 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; @@ -2756,24 +3091,45 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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; @@ -3054,12 +3410,12 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 89: - switch (((inst >> 12) & 0b111)) { + switch (((inst >> 12) & 0b111)) { case 0: op = rv_op_fmvp_d_x; break; } break; case 91: - switch (((inst >> 12) & 0b111)) { + switch (((inst >> 12) & 0b111)) { case 0: op = rv_op_fmvp_q_x; break; } break; @@ -3176,6 +3532,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 0: switch ((inst >> 26) & 0b111111) { case 0: op = rv_op_vadd_vv; break; + case 1: op = rv_op_vandn_vv; break; case 2: op = rv_op_vsub_vv; break; case 4: op = rv_op_vminu_vv; break; case 5: op = rv_op_vmin_vv; break; @@ -3198,6 +3555,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } 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; @@ -3226,6 +3585,7 @@ 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: @@ -3323,6 +3683,8 @@ 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) { case 0: if ((inst >> 25) & 1) op = rv_op_vmv_x_s; break; @@ -3338,6 +3700,12 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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: @@ -3406,6 +3774,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } 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; @@ -3437,11 +3806,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) { 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; @@ -3466,6 +3837,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } 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; @@ -3494,6 +3867,7 @@ 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: @@ -3554,6 +3928,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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: @@ -3643,6 +4019,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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: @@ -3681,11 +4059,89 @@ 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 && 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)) { @@ -4011,6 +4467,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; @@ -4086,6 +4548,11 @@ 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) @@ -4393,6 +4860,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); @@ -4400,7 +4873,7 @@ 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: @@ -4430,7 +4903,7 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa) break; case rv_codec_zcmt_jt: dec->imm = operand_tbl_index(inst); - break; + break; case rv_codec_fli: dec->rd = operand_rd(inst); dec->imm = operand_rs1(inst); @@ -4467,6 +4940,14 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa) 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; }; } @@ -4599,272 +5080,249 @@ static size_t inst_length(rv_inst inst) /* format instruction */ -static void append(char *s1, const char *s2, size_t n) -{ - 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) +static GString *format_inst(size_t tab, rv_decode *dec) { const rv_opcode_data *opcode_data = dec->opcode_data; - char tmp[64]; + 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); - break; case '-': - append(buf, "-", buflen); + 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, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rd] : - rv_freg_name_sym[dec->rd], - buflen); + if (dec->cfg && 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, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rs1] : - rv_freg_name_sym[dec->rs1], - buflen); + if (dec->cfg && 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, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rs2] : - rv_freg_name_sym[dec->rs2], - buflen); + if (dec->cfg && 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, dec->cfg->ext_zfinx ? rv_ireg_name_sym[dec->rs3] : - rv_freg_name_sym[dec->rs3], - buflen); + if (dec->cfg && 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': - snprintf(tmp, sizeof(tmp), "%d", dec->imm1); - append(buf, tmp, buflen); + 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, ' '); } - snprintf(tmp, sizeof(tmp), "# 0x%" PRIx64, - dec->pc + dec->imm); - append(buf, tmp, buflen); + g_string_append_printf(buf, "# 0x%" PRIx64, dec->pc + dec->imm); break; case 'U': fmt++; - snprintf(tmp, sizeof(tmp), "%d", dec->imm >> 12); - append(buf, tmp, buflen); + g_string_append_printf(buf, "%d", dec->imm >> 12); if (*fmt == 'o') { - while (strlen(buf) < tab * 2) { - append(buf, " ", buflen); + while (buf->len < tab * 2) { + g_string_append_c(buf, ' '); } - snprintf(tmp, sizeof(tmp), "# 0x%" PRIx64, - dec->pc + dec->imm); - append(buf, tmp, buflen); + g_string_append_printf(buf, "# 0x%" PRIx64, dec->pc + dec->imm); } 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: - snprintf(tmp, sizeof(tmp), "{ra}"); + g_string_append(buf, "{ra}"); break; case 5: - snprintf(tmp, sizeof(tmp), "{ra, s0}"); + g_string_append(buf, "{ra, s0}"); break; case 15: - snprintf(tmp, sizeof(tmp), "{ra, s0-s11}"); + g_string_append(buf, "{ra, s0-s11}"); break; default: - snprintf(tmp, sizeof(tmp), "{ra, s0-s%d}", dec->rlist - 5); + g_string_append_printf(buf, "{ra, s0-s%d}", dec->rlist - 5); break; } - append(buf, tmp, buflen); break; } case 'h': - append(buf, rv_fli_name_const[dec->imm], buflen); + g_string_append(buf, rv_fli_name_const[dec->imm]); break; default: break; } fmt++; } + + return buf; } /* lift instruction to pseudo-instruction */ @@ -4950,9 +5408,8 @@ 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, - RISCVCPUConfig *cfg) +static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, + RISCVCPUConfig *cfg) { rv_decode dec = { 0 }; dec.pc = pc; @@ -4984,7 +5441,8 @@ disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst, 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)) { + /* always_true_p don't dereference cfg */ + if (((i == 0) || cfg) && guard_func(cfg)) { dec.opcode_data = opcode_data; decode_func(&dec, isa); if (dec.op != rv_op_illegal) @@ -4999,7 +5457,7 @@ disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst, 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 " " @@ -5010,7 +5468,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; @@ -5034,24 +5491,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, - (RISCVCPUConfig *)info->target_info); - (*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 index 8abb578b515..d211700cb2f 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -7,7 +7,6 @@ #ifndef DISAS_RISCV_H #define DISAS_RISCV_H -#include "qemu/osdep.h" #include "target/riscv/cpu_cfg.h" /* types */ @@ -152,6 +151,7 @@ typedef enum { 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, @@ -166,6 +166,8 @@ typedef enum { rv_codec_r2_immhl, rv_codec_r2_imm2_imm5, rv_codec_fli, + rv_codec_lp, + rv_codec_cmop_ss, } rv_codec; /* structures */ @@ -223,11 +225,13 @@ enum { #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" @@ -274,6 +278,7 @@ enum { #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" @@ -289,7 +294,7 @@ enum { #define rv_fmt_fd_vs2 "O\t3,F" #define rv_fmt_vd_vm "O\tDm" #define rv_fmt_vsetvli "O\t0,1,v" -#define rv_fmt_vsetivli "O\t0,u,v" +#define rv_fmt_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" diff --git a/docs/COLO-FT.txt b/docs/COLO-FT.txt index 2e760a4aeec..2283a09c080 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 \ diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index f2a7aec56ff..8ecbd6b26f7 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -29,6 +29,9 @@ The `Repology`_ site is a useful resource to identify currently shipped versions of software in various operating systems, though it does not cover all distros listed below. +You can find how to install build dependencies for different systems on the +:ref:`setup-build-env` page. + Supported host architectures ---------------------------- @@ -40,8 +43,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 @@ -98,7 +101,7 @@ Python runtime 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. + As of QEMU |version|, the minimum supported version of Python is 3.9. Python build dependencies Some of QEMU's build dependencies are written in Python. Usually these @@ -107,18 +110,34 @@ Python build dependencies 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``. + + QEMU requires Rust 1.77.0. This is available on all supported platforms + with one exception, namely the ``mips64el`` architecture on Debian bookworm. + For all other architectures, Debian bookworm provides a new-enough Rust + compiler in the ``rustc-web`` package. + + Also, on Ubuntu 22.04 or 24.04 this requires the ``rustc-1.77`` + (or newer) package. The path to ``rustc`` and ``rustdoc`` must be + provided manually to the configure script. + 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 + Build components whose absence does not affect the ability to build QEMU + may not be available in distros, or may be too old for our requirements. + Many of these, such as additional modules for the functional 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 ------- @@ -139,6 +158,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 dc4da95329e..42037131de1 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -11,48 +11,25 @@ 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. -Prior to the 2.10.0 release there was no official policy on how -long features would be deprecated prior to their removal, nor -any documented list of which features were deprecated. Thus -any features deprecated prior to 2.10.0 will be treated as if -they were first deprecated in the 2.10.0 release. +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. What follows is a list of all features currently marked as deprecated. -Build options -------------- - -``gprof`` builds (since 8.0) -'''''''''''''''''''''''''''' - -The ``--enable-gprof`` configure setting relies on compiler -instrumentation to gather its data which can distort the generated -profile. As other non-instrumenting tools are available that give a -more holistic view of the system with non-instrumented binaries we are -deprecating the build option and no longer defend it in CI. The -``--enable-gcov`` build option remains for analysis test case -coverage. - 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. - Short-form boolean options (since 6.0) '''''''''''''''''''''''''''''''''''''' @@ -66,22 +43,6 @@ and will cause a warning. The replacement for the ``nodelay`` short-form boolean option is ``nodelay=on`` rather than ``delay=off``. -``-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) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -93,44 +54,42 @@ 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. -``-no-hpet`` (since 8.0) -'''''''''''''''''''''''' - -The HPET setting has been turned into a machine property. -Use ``-machine hpet=off`` instead. +``-smp`` (Unsupported "parameter=1" SMP configurations) (since 9.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -``-no-acpi`` (since 8.0) -'''''''''''''''''''''''' - -The ``-no-acpi`` setting has been turned into a machine property. -Use ``-machine acpi=off`` instead. - -``-async-teardown`` (since 8.1) -''''''''''''''''''''''''''''''' +Specified CPU topology parameters must be supported by the machine. -Use ``-run-with async-teardown=on`` instead. - -``-chroot`` (since 8.1) -''''''''''''''''''''''' +In the SMP configuration, users should provide the CPU topology parameters that +are supported by the target machine. -Use ``-run-with chroot=dir`` instead. +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. -``-singlestep`` (since 8.1) -''''''''''''''''''''''''''' +``-old-param`` option for booting Arm kernels via param_struct (since 10.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. +The ``-old-param`` command line option is specific to Arm targets: +it is used when directly booting a guest kernel to pass it the +command line and other information via the old ``param_struct`` ABI, +rather than the newer ATAGS or DTB mechanisms. This option was only +ever needed to support ancient kernels on some old board types +like the ``akita`` or ``terrier``; it has been deprecated in the +kernel since 2001. None of the board types QEMU supports need +``param_struct`` support, so this option has been deprecated and will +be removed in a future QEMU version. User-mode emulator command line arguments ----------------------------------------- -``-singlestep`` (since 8.1) -''''''''''''''''''''''''''' +``-p`` (since 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 -new ``-one-insn-per-tb`` option instead. +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 ------------------------------------ @@ -189,71 +148,66 @@ options are removed in favor of using explicit ``blockdev-create`` and ``blockdev-add`` calls. See :doc:`/interop/live-block-operations` for details. -Incorrectly typed ``device_add`` arguments (since 6.2) -'''''''''''''''''''''''''''''''''''''''''''''''''''''' +``query-migrationthreads`` (since 9.2) +'''''''''''''''''''''''''''''''''''''' -Due to shortcomings in the internal implementation of ``device_add``, QEMU -incorrectly accepts certain invalid arguments: Any object or list arguments are -silently ignored. Other argument types are not checked, but an implicit -conversion happens, so that e.g. string values can be assigned to integer -device properties or vice versa. +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. -This is a bug in QEMU that will be fixed in the future so that previously -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. +``block-job-pause`` (since 10.1) +'''''''''''''''''''''''''''''''' -``StatusInfo`` member ``singlestep`` (since 8.1) -'''''''''''''''''''''''''''''''''''''''''''''''' +Use ``job-pause`` instead. The only difference is that ``job-pause`` +always reports GenericError on failure when ``block-job-pause`` reports +DeviceNotActive when block-job is not found. -The ``singlestep`` member of the ``StatusInfo`` returned from the -``query-status`` command is deprecated. This member has a confusing -name and it never did what the documentation claimed or what its name -suggests. We do not believe that anybody is actually using the -information provided in this member. +``block-job-resume`` (since 10.1) +''''''''''''''''''''''''''''''''' -The information it reports is whether the TCG JIT is in "one -instruction per translated block" mode (which can be set on the -command line or via the HMP, but not via QMP). The information remains -available via the HMP 'info jit' command. +Use ``job-resume`` instead. The only difference is that ``job-resume`` +always reports GenericError on failure when ``block-job-resume`` reports +DeviceNotActive when block-job is not found. -QEMU Machine Protocol (QMP) events ----------------------------------- +``block-job-complete`` (since 10.1) +''''''''''''''''''''''''''''''''''' -``MEM_UNPLUG_ERROR`` (since 6.2) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''' +Use ``job-complete`` instead. The only difference is that ``job-complete`` +always reports GenericError on failure when ``block-job-complete`` reports +DeviceNotActive when block-job is not found. -Use the more generic event ``DEVICE_UNPLUG_GUEST_ERROR`` instead. +``block-job-dismiss`` (since 10.1) +'''''''''''''''''''''''''''''''''' -``vcpu`` trace events (since 8.1) -''''''''''''''''''''''''''''''''' +Use ``job-dismiss`` instead. -The ability to instrument QEMU helper functions with vCPU-aware trace -points was removed in 7.0. However QMP still exposed the vcpu -parameter. This argument has now been deprecated and the remaining -remaining trace points that used it are selected just by name. +``block-job-finalize`` (since 10.1) +''''''''''''''''''''''''''''''''''' -Human Monitor Protocol (HMP) commands -------------------------------------- +Use ``job-finalize`` instead. -``singlestep`` (since 8.1) -'''''''''''''''''''''''''' +``migrate`` argument ``detach`` (since 10.1) +'''''''''''''''''''''''''''''''''''''''''''' -The ``singlestep`` command has been replaced by the ``one-insn-per-tb`` -command, which has the same behaviour but a less misleading name. +This argument has always been ignored. 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. + +Likewise, the little endian variant of 32 bit MIPS is not supported by +Debian 13 ("Trixie") and newer. + +64 bit little endian MIPS is still a supported host architecture. System emulation on 32-bit x86 hosts (since 8.0) '''''''''''''''''''''''''''''''''''''''''''''''' @@ -265,10 +219,94 @@ 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. + +32-bit host operating systems (since 10.0) +'''''''''''''''''''''''''''''''''''''''''' + +Keeping 32-bit host support alive is a substantial burden for the +QEMU project. Thus QEMU will in future drop the support for all +32-bit host systems. + +linux-user mode CPUs +-------------------- + +iwMMXt emulation and the ``pxa`` CPUs (since 10.0) +'''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``pxa`` CPU family (``pxa250``, ``pxa255``, ``pxa260``, +``pxa261``, ``pxa262``, ``pxa270-a0``, ``pxa270-a1``, ``pxa270``, +``pxa270-b0``, ``pxa270-b1``, ``pxa270-c0``, ``pxa270-c5``) are no +longer used in system emulation, because all the machine types which +used these CPUs were removed in the QEMU 9.2 release. These CPUs can +now only be used in linux-user mode, and to do that you would have to +explicitly select one of these CPUs with the ``-cpu`` command line +option or the ``QEMU_CPU`` environment variable. + +We don't believe that anybody is using the iwMMXt emulation, and we do +not have any tests to validate it or any real hardware or similar +known-good implementation to test against. GCC is in the process of +dropping their support for iwMMXt codegen. These CPU types are +therefore deprecated in QEMU, and will be removed in a future release. + +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. + +PPC 405 CPUs (since 10.0) +''''''''''''''''''''''''' + +The PPC 405 CPU has no known users and the ``ref405ep`` machine was +removed in QEMU 10.0. Since the IBM POWER [8-11] processors uses an +embedded 405 for power management (OCC) and other internal tasks, it +is theoretically possible to use QEMU to model them. Let's keep the +CPU implementation for a while before removing all support. System emulator machines ------------------------ +Versioned machine types (aarch64, arm, i386, m68k, ppc64, s390x, x86_64) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +In accordance with our versioned machine type deprecation policy, all machine +types with version |VER_MACHINE_DEPRECATION_VERSION|, or older, have been +deprecated. + Arm ``virt`` machine ``dtb-kaslr-seed`` property (since 7.1) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -277,13 +315,48 @@ 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) -''''''''''''''''''''''''''''''''''''''''''''''''''''' +Mips ``mipssim`` machine (since 10.0) +''''''''''''''''''''''''''''''''''''' -These old machine types are quite neglected nowadays and thus might have -various pitfalls with regards to live migration. Use a newer machine type +Linux dropped support for this virtual machine type in kernel v3.7, and +there does not seem to be anybody around who is still using this board +in QEMU: Most former MIPS-related people are working on other architectures +in their everyday job nowadays, and we are also not aware of anybody still +using old binaries with this board (i.e. there is also no binary available +online to check that this board did not completely bitrot yet). It is +recommended to use another MIPS machine for future MIPS code development instead. +RISC-V default machine option (since 10.0) +'''''''''''''''''''''''''''''''''''''''''' + +RISC-V defines ``spike`` as the default machine if no machine option is +given in the command line. This happens because ``spike`` is the first +RISC-V machine implemented in QEMU and setting it as default was +convenient at that time. Now we have 7 riscv64 and 6 riscv32 machines +and having ``spike`` as a default is no longer justified. This default +will also promote situations where users think they're running ``virt`` +(the most used RISC-V machine type in 10.0) when in fact they're +running ``spike``. + +Removing the default machine option forces users to always set the machine +they want to use and avoids confusion. Existing users of the ``spike`` +machine must ensure that they're setting the ``spike`` machine in the +command line (``-M spike``). + + +System emulator binaries +------------------------ + +``qemu-system-microblazeel`` (since 10.1) +''''''''''''''''''''''''''''''''''''''''' + +The ``qemu-system-microblaze`` binary can emulate little-endian machines +now, too, so the separate binary ``qemu-system-microblazeel`` (with the +``el`` suffix) for little-endian targets is not required anymore. The +``petalogix-s3adsp1800`` machine can now be switched to little endian by +setting its ``endianness`` property to ``little``. + Backend options --------------- @@ -309,16 +382,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 nvme-ns,eui64-default=on|off`` (since 7.1) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -342,29 +405,35 @@ the addition of volatile memory support, it is now necessary to distinguish between persistent and volatile memory backends. As such, memdev is deprecated in favor of persistent-memdev. -``-fsdev proxy`` and ``-virtfs proxy`` (since 8.1) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The 9p ``proxy`` filesystem backend driver has been deprecated and will be -removed (along with its proxy helper daemon) in a future version of QEMU. Please -use ``-fsdev local`` or ``-virtfs local`` for using the 9p ``local`` filesystem -backend, or alternatively consider deploying virtiofsd instead. +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: -The 9p ``proxy`` backend was originally developed as an alternative to the 9p -``local`` backend. The idea was to enhance security by dispatching actual low -level filesystem operations from 9p server (QEMU process) over to a separate -process (the virtfs-proxy-helper binary). However this alternative never gained -momentum. The proxy backend is much slower than the local backend, hasn't seen -any development in years, and showed to be less secure, especially due to the -fact that its helper daemon must be run as root, whereas with the local backend -QEMU is typically run as unprivileged user and allows to tighten behaviour by -mapping permissions et al by using its 'mapped' security model option. +- "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" -Nowadays it would make sense to reimplement the ``proxy`` backend by using -QEMU's ``vhost`` feature, which would eliminate the high latency costs under -which the 9p ``proxy`` backend currently suffers. However as of to date nobody -has indicated plans for such kind of reimplementation unfortunately. +``-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 '''''''''''''''''''' @@ -399,6 +468,84 @@ Specifying the iSCSI password in plain text on the command line using the 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 specifying 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 specifying second granularity timeouts, +which is not enough for all types of use cases, use ``reconnect-ms`` instead. + +VFIO device options +''''''''''''''''''' + +``-device vfio-calxeda-xgmac`` (since 10.0) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The vfio-calxeda-xgmac device allows to assign a host Calxeda Highbank +10Gb XGMAC Ethernet controller device ("calxeda,hb-xgmac" compatibility +string) to a guest. Calxeda HW has been ewasted now and there is no point +keeping that device. + +``-device vfio-amd-xgbe`` (since 10.0) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The vfio-amd-xgbe device allows to assign a host AMD 10GbE controller +to a guest ("amd,xgbe-seattle-v1a" compatibility string). AMD "Seattle" +is not supported anymore and there is no point keeping that device. + +``-device vfio-platform`` (since 10.0) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The vfio-platform device allows to assign a host platform device +to a guest in a generic manner. Integrating a new device into +the vfio-platform infrastructure requires some adaptation at +both kernel and qemu level. No such attempt has been done for years +and the conclusion is that vfio-platform has not got any traction. +PCIe passthrough shall be the mainline solution. + +CPU device properties +''''''''''''''''''''' + +``pmu-num=n`` on RISC-V CPUs (since 8.2) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In order to support more flexible counter configurations this has been replaced +by a ``pmu-mask`` property. If set of counters is continuous then the mask can +be calculated with ``((2 ^ n) - 1) << 3``. The least significant three bits +must be left clear. + + +``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). + Backwards compatibility ----------------------- @@ -428,30 +575,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. -QEMU guest agent ----------------- - -``--blacklist`` command line option (since 7.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) -'''''''''''''''''''''''''''''''''''''''''''' +RISC-V "virt" board "riscv,delegate" DT property (since 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). +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. Migration --------- -``skipped`` MigrationStats field (since 8.1) -'''''''''''''''''''''''''''''''''''''''''''' +``fd:`` URI when used for file migration (since 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''' -``skipped`` field in Migration stats has been deprecated. It hasn't -been used for more than 10 years. +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. + +``zero-blocks`` capability (since 9.2) +'''''''''''''''''''''''''''''''''''''' +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 index 0ad0b86f0df..456d01d5b08 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -26,10 +26,6 @@ depending on the guest architecture. - :ref:`Yes` - No - 8 bit micro controller, often used in maker projects - * - Cris - - Yes - - Yes - - Embedded RISC chip developed by AXIS * - Hexagon - No - Yes @@ -42,7 +38,7 @@ depending on the guest architecture. - :ref:`Yes` - Yes - The ubiquitous desktop PC CPU architecture, 32 and 64 bit. - * - Loongarch + * - LoongArch - Yes - Yes - A MIPS-like 64bit RISC architecture developed in China @@ -58,10 +54,6 @@ depending on the guest architecture. - :ref:`Yes` - Yes - Venerable RISC architecture originally out of Stanford University - * - Nios2 - - Yes - - Yes - - 32 bit embedded soft-core by Altera * - OpenRISC - :ref:`Yes` - Yes @@ -99,9 +91,6 @@ depending on the guest architecture. - Yes - A configurable 32 bit soft core now owned by Cadence -A number of features are only available when running under -emulation including :ref:`Record/Replay` and :ref:`TCG Plugins`. - .. _Semihosting: Semihosting @@ -129,8 +118,9 @@ causing most hypervisors to trap and fault on them. .. warning:: Semihosting inherently bypasses any isolation there may be between the guest and the host. As a result a program using semihosting can - happily trash your host system. You should only ever run trusted - code with semihosting enabled. + 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 ~~~~~~~~~~~ @@ -179,12 +169,656 @@ for that architecture. * - MIPS - System - Unified Hosting Interface (MD01069) - * - Nios II - - System - - https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/nios2/nios2-semi.txt;hb=HEAD * - RISC-V - System and User-mode - - https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc + - https://github.com/riscv-non-isa/riscv-semihosting/blob/main/riscv-semihosting.adoc * - Xtensa - System - Tensilica ISS SIMCALL + +.. _tcg-plugins: + +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. + * - ipq=N + - Instructions per quantum. How many instructions before we re-calculate time. + The lower the number the more accurate time will be, but the less efficient the plugin. + Defaults to ips/10 + +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/removed-features.rst b/docs/about/removed-features.rst index c2043fd415d..d7c2113fc3e 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -162,6 +162,12 @@ specified with ``-mem-path`` can actually provide the guest RAM configured with The ``name`` parameter of the ``-net`` option was a synonym for the ``id`` parameter, which should now be used instead. +RISC-V firmware not booted by default (removed in 5.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''' + +QEMU 5.1 changes the default behaviour from ``-bios none`` to ``-bios default`` +for the RISC-V ``virt`` machine and ``sifive_u`` machine. + ``-numa node,mem=...`` (removed in 5.1) ''''''''''''''''''''''''''''''''''''''' @@ -324,12 +330,6 @@ devices. Drives the board doesn't pick up can no longer be used with This option was undocumented and not used in the field. Use ``-device usb-ccid`` instead. -RISC-V firmware not booted by default (removed in 5.1) -'''''''''''''''''''''''''''''''''''''''''''''''''''''' - -QEMU 5.1 changes the default behaviour from ``-bios none`` to ``-bios default`` -for the RISC-V ``virt`` machine and ``sifive_u`` machine. - ``-no-quit`` (removed in 7.0) ''''''''''''''''''''''''''''' @@ -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) ''''''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -403,13 +403,13 @@ Sound card devices should be created using ``-device`` or ``-audio``. The exception is ``pcspk`` which can be activated using ``-machine pcspk-audiodev=``. -``-watchdog`` (since 7.2) -''''''''''''''''''''''''' +``-watchdog`` (removed in 7.2) +'''''''''''''''''''''''''''''' Use ``-device`` instead. -Hexadecimal sizes with scaling multipliers (since 8.0) -'''''''''''''''''''''''''''''''''''''''''''''''''''''' +Hexadecimal sizes with scaling multipliers (removed in 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 @@ -436,6 +436,142 @@ 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`` (removed in 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`` (removed in 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`` (removed in 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. + +``-runas`` (removed in 10.0) +'''''''''''''''''''''''''''' + +Use ``-run-with user=..`` instead. + + +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 ------------------------------------ @@ -534,6 +670,82 @@ was superseded by ``sections``. 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. + +Incorrectly typed ``device_add`` arguments (since 9.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Due to shortcomings in the internal implementation of ``device_add``, +QEMU used to incorrectly accept certain invalid arguments. Any object +or list arguments were silently ignored. Other argument types were not +checked, but an implicit conversion happened, so that e.g. string +values could be assigned to integer device properties or vice versa. + +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 ------------------------------------- @@ -588,6 +800,82 @@ 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. + +32-bit hosts for 64-bit guests (removed in 10.0) +'''''''''''''''''''''''''''''''''''''''''''''''' + +In general, 32-bit hosts cannot support the memory space or atomicity +requirements of 64-bit guests. Prior to 10.0, QEMU attempted to +work around the atomicity issues in system mode by running all vCPUs +in a single thread context; in user mode atomicity was simply broken. +From 10.0, QEMU has disabled configuration of 64-bit guests on 32-bit hosts. + Guest Emulator ISAs ------------------- @@ -656,6 +944,27 @@ 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. + +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. + System accelerators ------------------- @@ -666,21 +975,28 @@ Userspace local APIC with KVM (x86, removed in 8.0) 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. +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. + System emulator machines ------------------------ +Versioned machine types (aarch64, arm, i386, m68k, ppc64, s390x, x86_64) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +In accordance with our versioned machine type deprecation policy, all machine +types with version |VER_MACHINE_DELETION_VERSION|, or older, have been +removed. + ``s390-virtio`` (removed in 2.6) '''''''''''''''''''''''''''''''' @@ -715,12 +1031,6 @@ 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) -''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -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. - Raspberry Pi ``raspi2`` and ``raspi3`` machines (removed in 6.2) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -740,6 +1050,56 @@ 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. + +Aspeed ``tacoma-bmc`` machine (removed in 10.0) +''''''''''''''''''''''''''''''''''''''''''''''' + +The ``tacoma-bmc`` machine was removed because it didn't bring much +compared to the ``rainier-bmc`` machine. Also, the ``tacoma-bmc`` was +a board used for bring up of the AST2600 SoC that never left the +labs. It can be easily replaced by the ``rainier-bmc`` machine, which +was the actual final product, or by the ``ast2600-evb`` with some +tweaks. + +ppc ``ref405ep`` machine (removed in 10.0) +'''''''''''''''''''''''''''''''''''''''''' + +This machine was removed because PPC 405 CPU have no known users, +firmware images are not available, OpenWRT dropped support in 2019, +U-Boot in 2017, and Linux in 2024. + +Big-Endian variants of ``petalogix-ml605`` and ``xlnx-zynqmp-pmu`` machines (removed in 10.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Both the MicroBlaze ``petalogix-ml605`` and ``xlnx-zynqmp-pmu`` machines +were added for little endian CPUs. Big endian support was never tested +and likely never worked. Starting with QEMU v10.1, the machines are now +only available as little-endian machines. + + linux-user mode CPUs -------------------- @@ -759,12 +1119,17 @@ 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 -------------------------- -TCG trace-events (since 6.2) -'''''''''''''''''''''''''''' +TCG trace-events (removed in 7.0) +''''''''''''''''''''''''''''''''' The ability to add new TCG trace points had bit rotted and as the feature can be replicated with TCG plugins it was removed. If @@ -808,6 +1173,10 @@ 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 ---------------- @@ -905,3 +1274,22 @@ 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 1fc38aed1b2..e712c883dba 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 e84a95e71ce..f892a6e1da3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,7 +29,6 @@ import os import sys import sphinx -from distutils.version import LooseVersion from sphinx.errors import ConfigError # The per-manual conf.py will set qemu_docdir for a single-manual build; @@ -54,15 +53,21 @@ # 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 # ones. -extensions = ['kerneldoc', 'qmp_lexer', 'hxtool', 'depfile', 'qapidoc'] +extensions = [ + 'depfile', + 'hxtool', + 'kerneldoc', + 'qapi_domain', + 'qapidoc', + 'qmp_lexer', +] if sphinx.version_info[:3] > (4, 0, 0): tags.add('sphinx4') @@ -89,7 +94,7 @@ # General information about the project. project = u'QEMU' -copyright = u'2023, The QEMU Project Developers' +copyright = u'2025, The QEMU Project Developers' author = u'The QEMU Project Developers' # The version info for the project you're documenting, acts as replacement for @@ -112,6 +117,32 @@ else: version = release = "unknown version" +bits = version.split(".") + +major = int(bits[0]) +minor = int(bits[1]) +micro = int(bits[2]) + +# Check for a dev snapshot, so we can adjust to next +# predicted release version. +# +# This assumes we do 3 releases per year, so must bump +# major if minor == 2 +if micro >= 50: + micro = 0 + if minor == 2: + major += 1 + minor = 0 + else: + minor += 1 + +# These thresholds must match the constants +# MACHINE_VER_DELETION_MAJOR & MACHINE_VER_DEPRECATION_MAJOR +# defined in include/hw/boards.h and the introductory text in +# docs/about/deprecated.rst +ver_machine_deprecation_version = "%d.%d.0" % (major - 3, minor) +ver_machine_deletion_version = "%d.%d.0" % (major - 6, minor) + # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # @@ -140,7 +171,18 @@ # environment variable is not set is for the benefit of readthedocs # style document building; our Makefile always sets the variable. confdir = os.getenv('CONFDIR', "/etc/qemu") -rst_epilog = ".. |CONFDIR| replace:: ``" + confdir + "``\n" + +vars = { + "CONFDIR": confdir, + "VER_MACHINE_DEPRECATION_VERSION": ver_machine_deprecation_version, + "VER_MACHINE_DELETION_VERSION": ver_machine_deletion_version, +} + +rst_epilog = "".join([ + ".. |" + key + "| replace:: ``" + vars[key] + "``\n" + for key in vars.keys() +]) + # We slurp in the defs.rst.inc and literally include it into rst_epilog, # because Sphinx's include:: directive doesn't work with absolute paths # and there isn't any one single relative path that will work for all @@ -148,6 +190,22 @@ with open(os.path.join(qemu_docdir, 'defs.rst.inc')) as f: rst_epilog += f.read() + +# Normally, the QAPI domain is picky about what field lists you use to +# describe a QAPI entity. If you'd like to use arbitrary additional +# fields in source documentation, add them here. +qapi_allowed_fields = { + "see also", +} + +# Due to a limitation in Sphinx, we need to know which indices to +# generate in advance. Adding a namespace here allows that generation. +qapi_namespaces = { + "QGA", + "QMP", + "QSD", +} + # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for @@ -165,11 +223,10 @@ # 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") @@ -189,7 +246,7 @@ ] html_context = { - "display_gitlab": True, + "source_url_prefix": "https://gitlab.com/qemu-project/qemu/-/blob/master/docs/", "gitlab_user": "qemu-project", "gitlab_repo": "qemu", "gitlab_version": "master", @@ -278,9 +335,6 @@ ('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), ] man_make_section_directory = False diff --git a/docs/config/q35-emulated.cfg b/docs/config/q35-emulated.cfg index c8806e6d362..b4bd7e858a9 100644 --- a/docs/config/q35-emulated.cfg +++ b/docs/config/q35-emulated.cfg @@ -288,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 148b5d2c5e4..820860aefe0 100644 --- a/docs/config/q35-virtio-graphical.cfg +++ b/docs/config/q35-virtio-graphical.cfg @@ -248,3 +248,7 @@ driver = "hda-duplex" bus = "sound.0" cad = "0" + audiodev = "audiodev0" + +[audiodev "audiodev0"] + driver = "none" # CHANGE ME diff --git a/docs/devel/acpi-bits.rst b/docs/devel/acpi-bits.rst deleted file mode 100644 index 9677b0098f4..00000000000 --- a/docs/devel/acpi-bits.rst +++ /dev/null @@ -1,144 +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. - You can also set **BITS_DEBUG=1** to turn on debug mode. It will enable - verbose logs and also retain the temporary work directory the test used for - you to inspect and run the specific commands manually. - - In order to run this test, please perform the following steps from the QEMU - build directory: - :: - - $ make check-venv (needed only the first time to create the venv) - $ ./pyvenv/bin/avocado run -t acpi tests/avocado - - The above will run all acpi avocado tests including this one. - In order to run the individual tests, perform the following: - :: - - $ ./pyvenv/bin/avocado run tests/avocado/acpi-bits.py --tap - - - The above will produce output in tap format. You can omit "--tap -" in the - end and it will produce output like the following: - :: - - $ ./pyvenv/bin/avocado run tests/avocado/acpi-bits.py - Fetching asset from tests/avocado/acpi-bits.py:AcpiBitsTest.test_acpi_smbios_bits - JOB ID : eab225724da7b64c012c65705dc2fa14ab1defef - JOB LOG : /home/anisinha/avocado/job-results/job-2022-10-10T17.58-eab2257/job.log - (1/1) tests/avocado/acpi-bits.py:AcpiBitsTest.test_acpi_smbios_bits: PASS (33.09 s) - RESULTS : PASS 1 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0 - JOB TIME : 39.22 s - - You can inspect the log file for more information about the run or in order - to diagnoze issues. If you pass V=1 in the environment, more diagnostic logs - would be found in the test log. - -* ``tests/avocado/acpi-bits/bits-config``: - - This location contains biosbits configuration files that determine how the - software runs the tests. - - ``bits-config.txt``: - This is the biosbits config file that determines what tests - or actions are performed by bits. The description of the config options are - provided in the file itself. - -* ``tests/avocado/acpi-bits/bits-tests``: - - This directory contains biosbits python based tests that are run from within - the biosbits environment in the spawned VM. New additions of test cases can - be made in the appropriate test file. For example, new acpi tests can go - into testacpi.py2 and one would call testsuite.add_test() to register the new - test so that it gets executed as a part of the ACPI tests. - It might be occasionally necessary to disable some subtests or add a new - test that belongs to a test suite not already present in this directory. To - do this, please clone the bits source from - https://gitlab.com/qemu-project/biosbits-bits/-/tree/qemu-bits. - Note that this is the "qemu-bits" branch and not the "bits" branch of the - repository. "qemu-bits" is the branch where we have made all the QEMU - specific enhancements and we must use the source from this branch only. - Copy the test suite/script that needs modification (addition of new tests - or disabling them) from python directory into this directory. For - example, in order to change cpuid related tests, copy the following - file into this directory and rename it with .py2 extension: - https://gitlab.com/qemu-project/biosbits-bits/-/blob/qemu-bits/python/testcpuid.py - Then make your additions and changes here. Therefore, the steps are: - - (a) Copy unmodified test script to this directory from bits source. - (b) Add a SPDX license header. - (c) Perform modifications to the test. - - Commits (a), (b) and (c) 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 ff9b5ee30c8..95c7b77c01e 100644 --- a/docs/devel/atomics.rst +++ b/docs/devel/atomics.rst @@ -119,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 @@ -204,7 +204,7 @@ They come in six kinds: before the second with respect to the other components of the system. 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[#]_. + 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 @@ -212,7 +212,7 @@ They come in six kinds: than actually loading the address itself, then it's a _control_ dependency and a full read barrier or better is required. -.. [#] The DEC Alpha is an exception, because ``smp_read_barrier_depends()`` +.. [#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. @@ -295,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. diff --git a/docs/devel/blkdebug.txt b/docs/devel/blkdebug.txt deleted file mode 100644 index 0b0c128d356..00000000000 --- 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 aca826c51cc..00000000000 --- 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/build-environment.rst b/docs/devel/build-environment.rst new file mode 100644 index 00000000000..661f6ea8504 --- /dev/null +++ b/docs/devel/build-environment.rst @@ -0,0 +1,118 @@ + +.. _setup-build-env: + +Setup build environment +======================= + +QEMU uses a lot of dependencies on the host system. glib2 is used everywhere in +the code base, and most of the other dependencies are optional. + +We present here simple instructions to enable native builds on most popular +systems. + +You can find additional instructions on `QEMU wiki `_: + +- `Linux `_ +- `MacOS `_ +- `Windows `_ +- `BSD `_ + +Note: Installing dependencies using your package manager build dependencies may +miss out on deps that have been newly introduced in qemu.git. In more, it misses +deps the distribution has decided to exclude. + +Linux +----- + +Fedora +++++++ + +:: + + sudo dnf update && sudo dnf builddep qemu + +Debian/Ubuntu ++++++++++++++ + +You first need to enable `Sources List `_. +Then, use apt to install dependencies: + +:: + + sudo apt update && sudo apt build-dep qemu + +MacOS +----- + +You first need to install `Homebrew `_. Then, use it to +install dependencies: + +:: + + brew update && brew install $(brew deps --include-build qemu) + +Windows +------- + +You first need to install `MSYS2 `_. +MSYS2 offers `different environments `_. +x86_64 environments are based on GCC, while aarch64 is based on Clang. + +We recommend to use MINGW64 for windows-x86_64 and CLANGARM64 for windows-aarch64 +(only available on windows-aarch64 hosts). + +Then, you can open a windows shell, and enter msys2 env using: + +:: + + c:/msys64/msys2_shell.cmd -defterm -here -no-start -mingw64 + # Replace -ucrt64 by -clangarm64 or -ucrt64 for other environments. + +MSYS2 package manager does not offer a built-in way to install build +dependencies. You can start with this list of packages using pacman: + +Note: Dependencies need to be installed again if you use a different MSYS2 +environment. + +:: + + # update MSYS2 itself, you need to reopen your shell at the end. + pacman -Syu + pacman -S \ + base-devel binutils bison diffutils flex git grep make sed \ + ${MINGW_PACKAGE_PREFIX}-toolchain \ + ${MINGW_PACKAGE_PREFIX}-glib2 \ + ${MINGW_PACKAGE_PREFIX}-gtk3 \ + ${MINGW_PACKAGE_PREFIX}-libnfs \ + ${MINGW_PACKAGE_PREFIX}-libssh \ + ${MINGW_PACKAGE_PREFIX}-ninja \ + ${MINGW_PACKAGE_PREFIX}-pixman \ + ${MINGW_PACKAGE_PREFIX}-pkgconf \ + ${MINGW_PACKAGE_PREFIX}-python \ + ${MINGW_PACKAGE_PREFIX}-SDL2 \ + ${MINGW_PACKAGE_PREFIX}-zstd + +If you want to install all dependencies, it's possible to use recipe used to +build QEMU in MSYS2 itself. + +:: + + pacman -S wget base-devel git + wget https://raw.githubusercontent.com/msys2/MINGW-packages/refs/heads/master/mingw-w64-qemu/PKGBUILD + # Some packages may be missing for your environment, installation will still + # be done though. + makepkg --syncdeps --nobuild PKGBUILD || true + +Build on windows-aarch64 +++++++++++++++++++++++++ + +When trying to cross compile meson for x86_64 using UCRT64 or MINGW64 env, +configure will run into an error because the cpu detected is not correct. + +Meson detects x86_64 processes emulated, so you need to manually set the cpu, +and force a cross compilation (with empty prefix). + +:: + + ./configure --cpu=x86_64 --cross-prefix= + diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index 0f990bb3e90..2c884197a20 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -122,10 +122,73 @@ functioning. These are performed using a few more helper functions: indicated by $TMPC. -Python virtual environments and the QEMU build system ------------------------------------------------------ +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`` or ``sphinx-build`` 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. + +.. [#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. + +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 +by QEMU's 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. -TBD Stage 2: Meson ============== @@ -168,14 +231,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 @@ -188,21 +247,6 @@ Target-independent emulator sourcesets: ``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 - system_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi.c'), - if_false: files('acpi-stub.c')) - - # This is required as well: - system_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, @@ -212,7 +256,7 @@ Target-dependent emulator sourcesets: Each emulator also includes sources for files in the ``hw/`` and ``target/`` subdirectories. The subdirectory used for each emulator comes from the target's definition of ``TARGET_BASE_ARCH`` or (if missing) - ``TARGET_ARCH``, as found in ``default-configs/targets/*.mak``. + ``TARGET_ARCH``, as found in ``configs/targets/*.mak``. Each subdirectory in ``hw/`` adds one sourceset to the ``hw_arch`` dictionary, for example:: @@ -225,14 +269,14 @@ 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_system_ss = ss.source_set() ... target_arch += {'arm': arm_ss} - target_softmmu_arch += {'arm': arm_system_ss} + target_system_arch += {'arm': arm_system_ss} Module sourcesets: There are two dictionaries for modules: ``modules`` is used for @@ -269,8 +313,8 @@ Utility sourcesets: The following files concur in the definition of which files are linked into each emulator: -``default-configs/devices/*.mak`` - The files under ``default-configs/devices/`` control the boards and devices +``configs/devices/*.mak`` + The files under ``configs/devices/`` control the boards and devices that are built into each QEMU system emulation targets. They merely contain a list of config variable definitions such as:: @@ -279,13 +323,13 @@ into each emulator: CONFIG_XLNX_VERSAL=y ``*/Kconfig`` - These files are processed together with ``default-configs/devices/*.mak`` and + These files are processed together with ``configs/devices/*.mak`` and describe the dependencies between various features, subsystems and device models. They are described in :ref:`kconfig` -``default-configs/targets/*.mak`` +``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. @@ -376,6 +420,15 @@ 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:: + + sphinx_build = find_program( + fs.parent(python.full_path()) / 'sphinx-build', + required: get_option('docs')) + Stage 3: Make ============= @@ -434,9 +487,13 @@ 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 - and the Avocado-based integration tests. + Rules for external test harnesses like the TCG tests. ``tests/docker/Makefile.include`` Rules for Docker tests. Like ``tests/Makefile.include``, this file is diff --git a/docs/devel/ci-definitions.rst.inc b/docs/devel/ci-definitions.rst.inc deleted file mode 100644 index 6d5c6fd9f20..00000000000 --- a/docs/devel/ci-definitions.rst.inc +++ /dev/null @@ -1,121 +0,0 @@ -Definition of terms -=================== - -This section defines the terms used in this document and correlates them with -what is currently used on QEMU. - -Automated tests ---------------- - -An automated test is written on a test framework using its generic test -functions/classes. The test framework can run the tests and report their -success or failure [1]_. - -An automated test has essentially three parts: - -1. The test initialization of the parameters, where the expected parameters, - like inputs and expected results, are set up; -2. The call to the code that should be tested; -3. An assertion, comparing the result from the previous call with the expected - result set during the initialization of the parameters. If the result - matches the expected result, the test has been successful; otherwise, it has - failed. - -Unit testing ------------- - -A unit test is responsible for exercising individual software components as a -unit, like interfaces, data structures, and functionality, uncovering errors -within the boundaries of a component. The verification effort is in the -smallest software unit and focuses on the internal processing logic and data -structures. A test case of unit tests should be designed to uncover errors due -to erroneous computations, incorrect comparisons, or improper control flow [2]_. - -On QEMU, unit testing is represented by the 'check-unit' target from 'make'. - -Functional testing ------------------- - -A functional test focuses on the functional requirement of the software. -Deriving sets of input conditions, the functional tests should fully exercise -all the functional requirements for a program. Functional testing is -complementary to other testing techniques, attempting to find errors like -incorrect or missing functions, interface errors, behavior errors, and -initialization and termination errors [3]_. - -On QEMU, functional testing is represented by the 'check-qtest' target from -'make'. - -System testing --------------- - -System tests ensure all application elements mesh properly while the overall -functionality and performance are achieved [4]_. Some or all system components -are integrated to create a complete system to be tested as a whole. System -testing ensures that components are compatible, interact correctly, and -transfer the right data at the right time across their interfaces. As system -testing focuses on interactions, use case-based testing is a practical approach -to system testing [5]_. Note that, in some cases, system testing may require -interaction with third-party software, like operating system images, databases, -networks, and so on. - -On QEMU, system testing is represented by the 'check-avocado' target from -'make'. - -Flaky tests ------------ - -A flaky test is defined as a test that exhibits both a passing and a failing -result with the same code on different runs. Some usual reasons for an -intermittent/flaky test are async wait, concurrency, and test order dependency -[6]_. - -Gating ------- - -A gate restricts the move of code from one stage to another on a -test/deployment pipeline. The step move is granted with approval. The approval -can be a manual intervention or a set of tests succeeding [7]_. - -On QEMU, the gating process happens during the pull request. The approval is -done by the project leader running its own set of tests. The pull request gets -merged when the tests succeed. - -Continuous Integration (CI) ---------------------------- - -Continuous integration (CI) requires the builds of the entire application and -the execution of a comprehensive set of automated tests every time there is a -need to commit any set of changes [8]_. The automated tests can be composed of -the unit, functional, system, and other tests. - -Keynotes about continuous integration (CI) [9]_: - -1. System tests may depend on external software (operating system images, - firmware, database, network). -2. It may take a long time to build and test. It may be impractical to build - the system being developed several times per day. -3. If the development platform is different from the target platform, it may - not be possible to run system tests in the developer’s private workspace. - There may be differences in hardware, operating system, or installed - software. Therefore, more time is required for testing the system. - -References ----------- - -.. [1] Sommerville, Ian (2016). Software Engineering. p. 233. -.. [2] Pressman, Roger S. & Maxim, Bruce R. (2020). Software Engineering, - A Practitioner’s Approach. p. 48, 376, 378, 381. -.. [3] Pressman, Roger S. & Maxim, Bruce R. (2020). Software Engineering, - A Practitioner’s Approach. p. 388. -.. [4] Pressman, Roger S. & Maxim, Bruce R. (2020). Software Engineering, - A Practitioner’s Approach. Software Engineering, p. 377. -.. [5] Sommerville, Ian (2016). Software Engineering. p. 59, 232, 240. -.. [6] Luo, Qingzhou, et al. An empirical analysis of flaky tests. - Proceedings of the 22nd ACM SIGSOFT International Symposium on - Foundations of Software Engineering. 2014. -.. [7] Humble, Jez & Farley, David (2010). Continuous Delivery: - Reliable Software Releases Through Build, Test, and Deployment, p. 122. -.. [8] Humble, Jez & Farley, David (2010). Continuous Delivery: - Reliable Software Releases Through Build, Test, and Deployment, p. 55. -.. [9] Sommerville, Ian (2016). Software Engineering. p. 743. diff --git a/docs/devel/ci.rst b/docs/devel/ci.rst deleted file mode 100644 index ed88a2010be..00000000000 --- a/docs/devel/ci.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _ci: - -== -CI -== - -Most of QEMU's CI is run on GitLab's infrastructure although a number -of other CI services are used for specialised purposes. The most up to -date information about them and their status can be found on the -`project wiki testing page `_. - -.. include:: ci-definitions.rst.inc -.. include:: ci-jobs.rst.inc -.. include:: ci-runners.rst.inc diff --git a/docs/devel/clocks.rst b/docs/devel/clocks.rst index 675fbeb6abe..3f744f2be1e 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/code-provenance.rst b/docs/devel/code-provenance.rst new file mode 100644 index 00000000000..b5aae2e2532 --- /dev/null +++ b/docs/devel/code-provenance.rst @@ -0,0 +1,338 @@ +.. _code-provenance: + +Code provenance +=============== + +Certifying patch submissions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The QEMU community **mandates** all contributors to certify provenance of +patch submissions they make to the project. To put it another way, +contributors must indicate that they are legally permitted to contribute to +the project. + +Certification is achieved with a low overhead by adding a single line to the +bottom of every git commit:: + + Signed-off-by: YOUR NAME + +The addition of this line asserts that the author of the patch is contributing +in accordance with the clauses specified in the +`Developer's Certificate of Origin `__: + +.. _dco: + + Developer's Certificate of Origin 1.1 + + By making a contribution to this project, I certify that: + + (a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + + (b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + + (c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + + (d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. + +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. + +It is generally expected that the name and email addresses used in one of the +``Signed-off-by`` lines, matches that of the git commit ``Author`` field. +It's okay if you subscribe or contribute to the list via more than one +address, but using multiple addresses in one commit just confuses +things. + +If the person sending the mail is not one of the patch authors, they are +nonetheless expected to add their own ``Signed-off-by`` to comply with the +DCO clause (c). + +Multiple authorship +~~~~~~~~~~~~~~~~~~~ + +It is not uncommon for a patch to have contributions from multiple authors. In +this scenario, git commits will usually be expected to have a ``Signed-off-by`` +line for each contributor involved in creation of the patch. Some edge cases: + + * The non-primary author's contributions were so trivial that they can be + considered not subject to copyright. In this case the secondary authors + need not include a ``Signed-off-by``. + + This case most commonly applies where QEMU reviewers give short snippets + of code as suggested fixes to a patch. The reviewers don't need to have + their own ``Signed-off-by`` added unless their code suggestion was + unusually large, but it is common to add ``Suggested-by`` as a credit + for non-trivial code. + + * Both contributors work for the same employer and the employer requires + copyright assignment. + + It can be said that in this case a ``Signed-off-by`` is indicating that + the person has permission to contribute from their employer who is the + copyright holder. It is nonetheless still preferable to include a + ``Signed-off-by`` for each contributor, as in some countries employees are + not able to assign copyright to their employer, and it also covers any + time invested outside working hours. + +When multiple ``Signed-off-by`` tags are present, they should be strictly kept +in order of authorship, from oldest to newest. + +Other commit tags +~~~~~~~~~~~~~~~~~ + +While the ``Signed-off-by`` tag is mandatory, there are a number of other tags +that are commonly used during QEMU development: + + * **``Reviewed-by``**: when a QEMU community member reviews a patch on the + mailing list, if they consider the patch acceptable, they should send an + email reply containing a ``Reviewed-by`` tag. Subsystem maintainers who + review a patch should add this even if they are also adding their + ``Signed-off-by`` to the same commit. + + * **``Acked-by``**: when a QEMU subsystem maintainer approves a patch that + touches their subsystem, but intends to allow a different maintainer to + queue it and send a pull request, they would send a mail containing a + ``Acked-by`` tag. Where a patch touches multiple subsystems, ``Acked-by`` + only implies review of the maintainers' own areas of responsibility. If a + maintainer wants to indicate they have done a full review they should use + a ``Reviewed-by`` tag. + + * **``Tested-by``**: when a QEMU community member has functionally tested the + behaviour of the patch in some manner, they should send an email reply + containing a ``Tested-by`` tag. + + * **``Reported-by``**: when a QEMU community member reports a problem via the + mailing list, or some other informal channel that is not the issue tracker, + it is good practice to credit them by including a ``Reported-by`` tag on + any patch fixing the issue. When the problem is reported via the GitLab + issue tracker, however, it is sufficient to just include a link to the + issue. + + * **``Suggested-by``**: when a reviewer or other 3rd party makes non-trivial + suggestions for how to change a patch, it is good practice to credit them + by including a ``Suggested-by`` tag. + +Subsystem maintainer requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When a subsystem maintainer accepts a patch from a contributor, in addition to +the normal code review points, they are expected to validate the presence of +suitable ``Signed-off-by`` tags. + +At the time they queue the patch in their subsystem tree, the maintainer +**must** also then add their own ``Signed-off-by`` to indicate that they have +done the aforementioned validation. This is in addition to any of their own +``Reviewed-by`` tags the subsystem maintainer may wish to include. + +When the maintainer modifies the patch after pulling into their tree, they +should record their contribution. This is typically done via a note in the +commit message, just prior to the maintainer's ``Signed-off-by``:: + + Signed-off-by: Cory Contributor + [Comment rephrased for clarity] + Signed-off-by: Mary Maintainer + + +Tools for adding ``Signed-off-by`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are a variety of ways tools can support adding ``Signed-off-by`` tags +for patches, avoiding the need for contributors to manually type in this +repetitive text each time. + +git commands +^^^^^^^^^^^^ + +When creating, or amending, a commit the ``-s`` flag to ``git commit`` will +append a suitable line matching the configured git author details. + +If preparing patches using the ``git format-patch`` tool, the ``-s`` flag can +be used to append a suitable line in the emails it creates, without modifying +the local commits. Alternatively to modify all the local commits on a branch:: + + git rebase master -x 'git commit --amend --no-edit -s' + +emacs +^^^^^ + +In the file ``$HOME/.emacs.d/abbrev_defs`` add: + +.. code:: elisp + + (define-abbrev-table 'global-abbrev-table + '( + ("8rev" "Reviewed-by: YOUR NAME " nil 1) + ("8ack" "Acked-by: YOUR NAME " nil 1) + ("8test" "Tested-by: YOUR NAME " nil 1) + ("8sob" "Signed-off-by: YOUR NAME " nil 1) + )) + +with this change, if you type (for example) ``8rev`` followed by ```` +or ```` it will expand to the whole phrase. + +vim +^^^ + +In the file ``$HOME/.vimrc`` add:: + + iabbrev 8rev Reviewed-by: YOUR NAME + iabbrev 8ack Acked-by: YOUR NAME + iabbrev 8test Tested-by: YOUR NAME + iabbrev 8sob Signed-off-by: YOUR NAME + +with this change, if you type (for example) ``8rev`` followed by ```` +or ```` it will expand to the whole phrase. + +Re-starting abandoned work +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For a variety of reasons there are some patches that get submitted to QEMU but +never merged. An unrelated contributor may decide (months or years later) to +continue working from the abandoned patch and re-submit it with extra changes. + +The general principles when picking up abandoned work are: + + * Continue to credit the original author for their work, by maintaining their + original ``Signed-off-by`` + * Indicate where the original patch was obtained from (mailing list, bug + tracker, author's git repo, etc) when sending it for review + * Acknowledge the extra work of the new contributor by including their + ``Signed-off-by`` in the patch in addition to the orignal author's + * Indicate who is responsible for what parts of the patch. This is typically + done via a note in the commit message, just prior to the new contributor's + ``Signed-off-by``:: + + Signed-off-by: Some Person + [Rebased and added support for 'foo'] + Signed-off-by: New Person + +In complicated cases, or if otherwise unsure, ask for advice on the project +mailing list. + +It is also recommended to attempt to contact the original author to let them +know you are interested in taking over their work, in case they still intended +to return to the work, or had any suggestions about the best way to continue. + +Inclusion of generated files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Files in patches contributed to QEMU are generally expected to be provided +only in the preferred format for making modifications. The implication of +this is that the output of code generators or compilers is usually not +appropriate to contribute to QEMU. + +For reasons of practicality there are some exceptions to this rule, where +generated code is permitted, provided it is also accompanied by the +corresponding preferred source format. This is done where it is impractical +to expect those building QEMU to run the code generation or compilation +process. A non-exhaustive list of examples is: + + * Images: where an bitmap image is created from a vector file it is common + to include the rendered bitmaps at desired resolution(s), since subtle + changes in the rasterization process / tools may affect quality. The + original vector file is expected to accompany any generated bitmaps. + + * Firmware: QEMU includes pre-compiled binary ROMs for a variety of guest + firmwares. When such binary ROMs are contributed, the corresponding source + must also be provided, either directly, or through a git submodule link. + + * Dockerfiles: the majority of the dockerfiles are automatically generated + from a canonical list of build dependencies maintained in tree, together + with the libvirt-ci git submodule link. The generated dockerfiles are + included in tree because it is desirable to be able to directly build + container images from a clean git checkout. + + * eBPF: QEMU includes some generated eBPF machine code, since the required + eBPF compilation tools are not broadly available on all targetted OS + distributions. The corresponding eBPF C code for the binary is also + provided. This is a time-limited exception until the eBPF toolchain is + sufficiently broadly available in distros. + +In all cases above, the existence of generated files must be acknowledged +and justified in the commit that introduces them. + +Tools which perform changes to existing code with deterministic algorithmic +manipulation, driven by user specified inputs, are not generally considered +to be "generators". + +For instance, using Coccinelle to convert code from one pattern to another +pattern, or fixing documentation typos with a spell checker, or transforming +code using sed / awk / etc, are not considered to be acts of code +generation. Where an automated manipulation is performed on code, however, +this should be declared in the commit message. + +At times contributors may use or create scripts/tools to generate an initial +boilerplate code template which is then filled in to produce the final patch. +The output of such a tool would still be considered the "preferred format", +since it is intended to be a foundation for further human authored changes. +Such tools are acceptable to use, provided there is clearly defined copyright +and licensing for their output. Note in particular the caveats applying to AI +content generators below. + +Use of AI content generators +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +TL;DR: + + **Current QEMU project policy is to DECLINE any contributions which are + believed to include or derive from AI generated content. This includes + ChatGPT, Claude, Copilot, Llama and similar tools.** + +The increasing prevalence of AI-assisted software development results in a +number of difficult legal questions and risks for software projects, including +QEMU. Of particular concern is content generated by `Large Language Models +`__ (LLMs). + +The QEMU community requires that contributors certify their patch submissions +are made in accordance with the rules of the `Developer's Certificate of +Origin (DCO) `. + +To satisfy the DCO, the patch contributor has to fully understand the +copyright and license status of content they are contributing to QEMU. With AI +content generators, the copyright and license status of the output is +ill-defined with no generally accepted, settled legal foundation. + +Where the training material is known, it is common for it to include large +volumes of material under restrictive licensing/copyright terms. Even where +the training material is all known to be under open source licenses, it is +likely to be under a variety of terms, not all of which will be compatible +with QEMU's licensing requirements. + +How contributors could comply with DCO terms (b) or (c) for the output of AI +content generators commonly available today is unclear. The QEMU project is +not willing or able to accept the legal risks of non-compliance. + +The QEMU project thus requires that contributors refrain from using AI content +generators on patches intended to be submitted to the project, and will +decline any contribution if use of AI is either known or suspected. + +This policy does not apply to other uses of AI, such as researching APIs or +algorithms, static analysis, or debugging, provided their output is not to be +included in contributions. + +Examples of tools impacted by this policy includes GitHub's CoPilot, OpenAI's +ChatGPT, Anthropic's Claude, and Meta's Code Llama, and code/content +generation agents which are built on top of such tools. + +This policy may evolve as AI tools mature and the legal situation is +clarifed. In the meanwhile, requests for exceptions to this policy will be +evaluated by the QEMU project on a case by case basis. To be granted an +exception, a contributor will need to demonstrate clarity of the license and +copyright status for the tool's output in relation to its training model and +code, to the satisfaction of the project maintainers. diff --git a/docs/devel/codebase.rst b/docs/devel/codebase.rst new file mode 100644 index 00000000000..2a3143787a6 --- /dev/null +++ b/docs/devel/codebase.rst @@ -0,0 +1,215 @@ +======== +Codebase +======== + +This section presents the various parts of QEMU and how the codebase is +organized. + +Beyond giving succinct descriptions, the goal is to offer links to various +parts of the documentation/codebase. + +Subsystems +---------- + +An exhaustive list of subsystems and associated files can be found in the +`MAINTAINERS `_ +file. + +Some of the main QEMU subsystems are: + +- `Accelerators` +- Block devices and `disk images` support +- `CI` and `Tests` +- `Devices` & Board models +- `Documentation ` +- `GDB support` +- :ref:`Migration` +- `Monitor` +- :ref:`QOM (QEMU Object Model)` +- `System mode` +- :ref:`TCG (Tiny Code Generator)` +- `User mode` (`Linux` & `BSD`) +- User Interfaces + +More documentation on QEMU subsystems can be found on :ref:`internal-subsystem` +page. + +The Grand tour +-------------- + +We present briefly here what every folder in the top directory of the codebase +contains. Hop on! + +The folder name links here will take you to that folder in our gitlab +repository. Other links will take you to more detailed documentation for that +subsystem, where we have it. Unfortunately not every subsystem has documentation +yet, so sometimes the source code is all you have. + +* `accel `_: + Infrastructure and architecture agnostic code related to the various + `accelerators ` supported by QEMU + (TCG, KVM, hvf, whpx, xen, nvmm). + Contains interfaces for operations that will be implemented per + `target `_. +* `audio `_: + Audio (host) support. +* `authz `_: + `QEMU Authorization framework`. +* `backends `_: + Various backends that are used to access resources on the host (e.g. for + random number generation, memory backing or cryptographic functions). +* `block `_: + Block devices and `image formats` implementation. +* `bsd-user `_: + `BSD User mode`. +* build: Where the code built goes by default. You can tell the QEMU build + system to put the built code anywhere else you like. +* `chardev `_: + Various backends used by char devices. +* `common-user `_: + User-mode assembly code for dealing with signals occurring during syscalls. +* `configs `_: + Makefiles defining configurations to build QEMU. +* `contrib `_: + Community contributed devices/plugins/tools. +* `crypto `_: + Cryptographic algorithms used in QEMU. +* `disas `_: + Disassembly functions used by QEMU target code. +* `docs `_: + QEMU Documentation. +* `dump `_: + Code to dump memory of a running VM. +* `ebpf `_: + eBPF program support in QEMU. `virtio-net RSS` uses it. +* `fpu `_: + Floating-point software emulation. +* `fsdev `_: + `VirtFS `_ support. +* `gdbstub `_: + `GDB ` support. +* `gdb-xml `_: + Set of XML files describing architectures and used by `gdbstub `. +* `host `_: + Various architecture specific header files (crypto, atomic, memory + operations). +* `linux-headers `_: + A subset of headers imported from Linux kernel and used for implementing + KVM support and user-mode. +* `linux-user `_: + `User mode ` implementation. Contains one folder per target + architecture. +* `.gitlab-ci.d `_: + `CI ` yaml and scripts. +* `include `_: + All headers associated to different subsystems in QEMU. The hierarchy used + mirrors source code organization and naming. +* `hw `_: + `Devices ` and boards emulation. Devices are categorized by + type/protocol/architecture and located in associated subfolder. +* `io `_: + QEMU `I/O channels `_. +* `libdecnumber `_: + Import of gcc library, used to implement decimal number arithmetic. +* `migration `__: + :ref:`Migration framework `. +* `monitor `_: + `Monitor ` implementation (HMP & QMP). +* `nbd `_: + QEMU NBD (Network Block Device) server. +* `net `_: + Network (host) support. +* `pc-bios `_: + Contains pre-built firmware binaries and boot images, ready to use in + QEMU without compilation. +* `plugins `_: + :ref:`TCG plugins ` core implementation. Plugins can be found in + `tests `__ + and `contrib `__ + folders. +* `po `_: + Translation files. +* `python `_: + Python part of our build/test system. +* `qapi `_: + `QAPI ` implementation. +* `qobject `_: + QEMU Object implementation. +* `qga `_: + QEMU `Guest agent ` implementation. +* `qom `_: + QEMU :ref:`Object model ` implementation, with monitor associated commands. +* `replay `_: + QEMU :ref:`Record/replay ` implementation. +* `roms `_: + Contains source code for various firmware and ROMs, which can be compiled if + custom or updated versions are needed. +* `rust `_: + Rust integration in QEMU. It contains the new interfaces defined and + associated devices using it. +* `scripts `_: + Collection of scripts used in build and test systems, and various + tools for QEMU codebase and execution traces. +* `scsi `_: + Code related to SCSI support, used by SCSI devices. +* `semihosting `_: + QEMU `Semihosting ` implementation. +* `stats `_: + `Monitor ` stats commands implementation. +* `storage-daemon `_: + QEMU `Storage daemon ` implementation. +* `stubs `_: + Various stubs (empty functions) used to compile QEMU with specific + configurations. +* `subprojects `_: + QEMU submodules used by QEMU build system. +* `system `_: + QEMU `system mode ` implementation (cpu, mmu, boot support). +* `target `_: + Contains code for all target architectures supported (one subfolder + per arch). For every architecture, you can find accelerator specific + implementations. +* `tcg `_: + :ref:`TCG ` related code. + Contains one subfolder per host supported architecture. +* `tests `_: + QEMU `test ` suite + + - `data `_: + Data for various tests. + - `decode `_: + Testsuite for :ref:`decodetree ` implementation. + - `docker `_: + Code and scripts to create `containers ` used in `CI `. + - `fp `_: + QEMU testsuite for soft float implementation. + - `functional `_: + `Functional tests ` (full VM boot). + - `lcitool `_: + Generate dockerfiles for CI containers. + - `migration `_: + Test scripts and data for :ref:`Migration framework `. + - `multiboot `_: + Test multiboot functionality for x86_64/i386. + - `qapi-schema `_: + Test scripts and data for `QAPI `. + - `qemu-iotests `_: + `Disk image and block tests `. + - `qtest `_: + `Device emulation testing `. + - `tcg `__: + `TCG related tests `. Contains code per architecture + (subfolder) and multiarch tests as well. + - `tsan `_: + `Suppressions ` for thread sanitizer. + - `uefi-test-tools `_: + Test tool for UEFI support. + - `unit `_: + QEMU `Unit tests `. +* `trace `_: + :ref:`Tracing framework `. Used to print information associated to various + events during execution. +* `ui `_: + QEMU User interfaces. +* `util `_: + Utility code used by other parts of QEMU. diff --git a/docs/devel/control-flow-integrity.rst b/docs/devel/control-flow-integrity.rst index e6b73a4fe1a..3d5702fa4cc 100644 --- a/docs/devel/control-flow-integrity.rst +++ b/docs/devel/control-flow-integrity.rst @@ -1,3 +1,5 @@ +.. _cfi: + ============================ Control-Flow Integrity (CFI) ============================ diff --git a/docs/devel/crypto.rst b/docs/devel/crypto.rst new file mode 100644 index 00000000000..39b1c910e7f --- /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 e3392aa7057..98ad33a4870 100644 --- a/docs/devel/decodetree.rst +++ b/docs/devel/decodetree.rst @@ -1,3 +1,5 @@ +.. _decodetree: + ======================== Decodetree Specification ======================== diff --git a/docs/devel/docs.rst b/docs/devel/docs.rst new file mode 100644 index 00000000000..a7768b53117 --- /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/ebpf_rss.rst b/docs/devel/ebpf_rss.rst index 4a68682b31a..ed5d33767bd 100644 --- a/docs/devel/ebpf_rss.rst +++ b/docs/devel/ebpf_rss.rst @@ -1,3 +1,5 @@ +.. _ebpf-rss: + =========================== eBPF RSS virtio-net support =========================== diff --git a/docs/devel/index-api.rst b/docs/devel/index-api.rst index 539ad29c215..1c487c152ab 100644 --- a/docs/devel/index-api.rst +++ b/docs/devel/index-api.rst @@ -9,8 +9,10 @@ generated from in-code annotations to function prototypes. bitops loads-stores + lockcnt memory modules + pci qom-api qdev-api ui diff --git a/docs/devel/index-build.rst b/docs/devel/index-build.rst index 57e8d39d985..3f3cb21b9b4 100644 --- a/docs/devel/index-build.rst +++ b/docs/devel/index-build.rst @@ -1,19 +1,16 @@ -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 + build-environment kconfig - testing - acpi-bits - qtest - ci + docs qapi-code-gen - fuzzing + qapi-domain control-flow-integrity diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst index e1a93df2639..7a0678cbdd3 100644 --- a/docs/devel/index-internals.rst +++ b/docs/devel/index-internals.rst @@ -1,3 +1,5 @@ +.. _internal-subsystem: + Internal Subsystem Information ------------------------------ @@ -8,14 +10,19 @@ 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 + uefi-vars + 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 362f97ee300..5807752d704 100644 --- a/docs/devel/index-process.rst +++ b/docs/devel/index-process.rst @@ -13,7 +13,9 @@ Notes about how to interact with the community and how and where to submit patch maintainers style submitting-a-patch + code-provenance trivial-patches stable-process submitting-a-pull-request secure-coding-practices + rust diff --git a/docs/devel/index.rst b/docs/devel/index.rst index abf60457c29..29f032d6a82 100644 --- a/docs/devel/index.rst +++ b/docs/devel/index.rst @@ -31,6 +31,8 @@ the :ref:`tcg_internals`. index-process index-build + testing/index index-api index-internals index-tcg + codebase diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst index 73f52de1067..493b76c4fbf 100644 --- a/docs/devel/kconfig.rst +++ b/docs/devel/kconfig.rst @@ -38,7 +38,7 @@ originated in the Linux kernel, though it was heavily simplified and the handling of dependencies is stricter in QEMU. Unlike Linux, there is no user interface to edit the configuration, which -is instead specified in per-target files under the ``default-configs/`` +is instead specified in per-target files under the ``configs/`` directory of the QEMU source tree. This is because, unlike Linux, configuration and dependencies can be treated as a black box when building QEMU; the default configuration that QEMU ships with should be okay in @@ -103,7 +103,7 @@ directives can be included: **default value**: ``default [if ]`` Default values are assigned to the config symbol if no other value was - set by the user via ``default-configs/*.mak`` files, and only if + set by the user via ``configs/*.mak`` files, and only if ``select`` or ``depends on`` directives do not force the value to true or false respectively. ```` can be ``y`` or ``n``; it cannot be an arbitrary Boolean expression. However, a condition for applying @@ -119,7 +119,7 @@ directives can be included: This is similar to ``select`` as it applies a lower limit of ``y`` to another symbol. However, the lower limit is only a default and the "implied" symbol's value may still be set to ``n`` from a - ``default-configs/*.mak`` files. The following two examples are + ``configs/*.mak`` files. The following two examples are equivalent:: config FOO @@ -146,7 +146,7 @@ declares its dependencies in different ways: bool Subsystems always default to false (they have no ``default`` directive) - and are never visible in ``default-configs/*.mak`` files. It's + and are never visible in ``configs/*.mak`` files. It's up to other symbols to ``select`` whatever subsystems they require. They sometimes have ``select`` directives to bring in other required @@ -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 ``configs/*.mak`` + for the target they apply to. **internal elements** @@ -241,18 +251,18 @@ declares its dependencies in different ways: Internal elements group code that is useful in several boards or devices. They are usually enabled with ``select`` and in turn select - other elements; they are never visible in ``default-configs/*.mak`` + other elements; they are never visible in ``configs/*.mak`` files, and often not even in the Makefile. Writing and modifying default configurations -------------------------------------------- In addition to the Kconfig files under hw/, each target also includes -a file called ``default-configs/TARGETNAME-softmmu.mak``. These files +a file called ``configs/TARGETNAME-softmmu.mak``. These files initialize some Kconfig variables to non-default values and provide the starting point to turn on devices and subsystems. -A file in ``default-configs/`` looks like the following example:: +A file in ``configs/`` looks like the following example:: # Default configuration for alpha-softmmu @@ -316,6 +326,6 @@ variable:: host_kconfig = \ (have_tpm ? ['CONFIG_TPM=y'] : []) + \ - (targetos == 'linux' ? ['CONFIG_LINUX=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 dab6dfa0acc..9471bac8599 100644 --- a/docs/devel/loads-stores.rst +++ b/docs/devel/loads-stores.rst @@ -63,12 +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`` ~~~~~~~~~~~~~~~~~~~~ @@ -95,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 @@ -121,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`` @@ -155,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`` ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -193,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`` ~~~~~~~~~~~~~~~~~~~~~ @@ -231,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`` ~~~~~~~~~~~~~~~~ @@ -296,7 +296,7 @@ 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`` @@ -325,7 +325,7 @@ store: ``helper_{size}_mmu(env, addr, val, opindex, retaddr)`` - ``l`` : 32 bits - ``q`` : 64 bits -Regexes for git grep +Regexes for git grep: - ``\`` - ``\`` @@ -382,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: - ``\`` - ``\`` - ``\`` @@ -400,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`` @@ -438,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: - ``\`` - ``\`` @@ -462,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`` @@ -497,7 +497,7 @@ make sure our existing code is doing things correctly. ``dma_memory_rw`` -Regexes for git grep +Regexes for git grep: - ``\`` - ``\`` - ``\`` @@ -538,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 00000000000..8b43578f6c7 --- /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 a3fb3bc5d8d..00000000000 --- 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 00000000000..94ec285c27a --- /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 5c907d901cd..88a613ed74f 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/memory.rst b/docs/devel/memory.rst index 69c5e3f914a..57fb2aec76e 100644 --- a/docs/devel/memory.rst +++ b/docs/devel/memory.rst @@ -369,4 +369,4 @@ callbacks are called: API Reference ------------- -.. kernel-doc:: include/exec/memory.h +.. kernel-doc:: include/system/memory.h diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst deleted file mode 100644 index c3e1400c0cc..00000000000 --- a/docs/devel/migration.rst +++ /dev/null @@ -1,919 +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 ``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) - -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 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 Recovery ------------------ - -Comparing to precopy, postcopy is special on error handlings. When any -error happens (in this case, mostly network errors), QEMU cannot easily -fail a migration because VM data resides in both source and destination -QEMU instances. On the other hand, when issue happens QEMU on both sides -will go into a paused state. It'll need a recovery phase to continue a -paused postcopy migration. - -The recovery phase normally contains a few steps: - - - When network issue occurs, both QEMU will go into PAUSED state - - - When the network is recovered (or a new network is provided), the admin - can setup the new channel for migration using QMP command - 'migrate-recover' on destination node, preparing for a resume. - - - On source host, the admin can continue the interrupted postcopy - migration using QMP command 'migrate' with resume=true flag set. - - - After the connection is re-established, QEMU will continue the postcopy - migration on both sides. - -During a paused postcopy migration, the VM can logically still continue -running, and it will not be impacted from any page access to pages that -were already migrated to destination VM before the interruption happens. -However, if any of the missing pages got accessed on destination VM, the VM -thread will be halted waiting for the page to be migrated, it means it can -be halted until the recovery is complete. - -The impact of accessing missing pages can be relevant to different -configurations of the guest. For example, when with async page fault -enabled, logically the guest can proactively schedule out the threads -accessing missing pages. - -Postcopy 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). - - - Paused - - Postcopy can run into a paused state (normally on both sides when - happens), where all threads will be temporarily halted mostly due to - network errors. When reaching paused state, migration will make sure - the qemu binary on both sides maintain the data without corrupting - the VM. To continue the migration, the admin needs to fix the - migration channel using the QMP command 'migrate-recover' on the - destination node, then resume the migration using QMP command 'migrate' - again on source node, with resume=true flag set. - - - End - - The listen thread can now quit, and perform the cleanup of migration - state, the migration is now complete. - -Source side page map --------------------- - -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 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. - -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 00000000000..7897873c86e --- /dev/null +++ b/docs/devel/migration/CPR.rst @@ -0,0 +1,327 @@ +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, +the cpr-reboot and cpr-transfer modes are available. + +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 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. + +cpr-transfer mode +----------------- + +This mode allows the user to transfer a guest to a new QEMU instance +on the same host with minimal guest pause time, by preserving guest +RAM in place, albeit with new virtual addresses in new QEMU. Devices +and their pinned memory pages will also be preserved in a future QEMU +release. + +The user starts new QEMU on the same host as old QEMU, with command- +line arguments to create the same machine, plus the ``-incoming`` +option for the main migration channel, like normal live migration. +In addition, the user adds a second -incoming option with channel +type ``cpr``. This CPR channel must support file descriptor transfer +with SCM_RIGHTS, i.e. it must be a UNIX domain socket. + +To initiate CPR, the user issues a migrate command to old QEMU, +adding a second migration channel of type ``cpr`` in the channels +argument. Old QEMU stops the VM, saves state to the migration +channels, and enters the postmigrate state. Execution resumes in +new QEMU. + +New QEMU reads the CPR channel before opening a monitor, hence +the CPR channel cannot be specified in the list of channels for a +migrate-incoming command. It may only be specified on the command +line. + +Usage +^^^^^ + +Memory backend objects must have the ``share=on`` attribute. + +The VM must be started with the ``-machine aux-ram-share=on`` +option. This causes implicit RAM blocks (those not described by +a memory-backend object) to be allocated by mmap'ing a memfd. +Examples include VGA and ROM. + +Outgoing: + * Set the migration mode parameter to ``cpr-transfer``. + * Issue the ``migrate`` command, containing a main channel and + a cpr channel. + +Incoming: + * Start new QEMU with two ``-incoming`` options. + * If the VM was running when the outgoing ``migrate`` command was + issued, then QEMU automatically resumes VM execution. + +Caveats +^^^^^^^ + +cpr-transfer mode may not be used with postcopy, background-snapshot, +or COLO. + +memory-backend-epc is not supported. + +The main incoming migration channel address cannot be a file type. + +If the main incoming channel address is an inet socket, then the port +cannot be 0 (meaning dynamically choose a port). + +When using ``-incoming defer``, you must issue the migrate command to +old QEMU before issuing any monitor commands to new QEMU, because new +QEMU blocks waiting to read from the cpr channel before starting its +monitor, and old QEMU does not write to the channel until the migrate +command is issued. However, new QEMU does not open and read the +main migration channel until you issue the migrate incoming command. + +Example 1: incoming channel +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In these examples, we simply restart the same version of QEMU, but +in a real scenario one would start new QEMU on the incoming side. +Note that new QEMU does not print the monitor prompt until old QEMU +has issued the migrate command. The outgoing side uses QMP because +HMP cannot specify a CPR channel. Some QMP responses are omitted for +brevity. + +:: + + Outgoing: Incoming: + + # qemu-kvm -qmp stdio + -object memory-backend-file,id=ram0,size=4G, + mem-path=/dev/shm/ram0,share=on -m 4G + -machine memory-backend=ram0 + -machine aux-ram-share=on + ... + # qemu-kvm -monitor stdio + -incoming tcp:0:44444 + -incoming '{"channel-type": "cpr", + "addr": { "transport": "socket", + "type": "unix", "path": "cpr.sock"}}' + ... + {"execute":"qmp_capabilities"} + + {"execute": "query-status"} + {"return": {"status": "running", + "running": true}} + + {"execute":"migrate-set-parameters", + "arguments":{"mode":"cpr-transfer"}} + + {"execute": "migrate", "arguments": { "channels": [ + {"channel-type": "main", + "addr": { "transport": "socket", "type": "inet", + "host": "0", "port": "44444" }}, + {"channel-type": "cpr", + "addr": { "transport": "socket", "type": "unix", + "path": "cpr.sock" }}]}} + + QEMU 10.0.50 monitor + (qemu) info status + VM status: running + + {"execute": "query-status"} + {"return": {"status": "postmigrate", + "running": false}} + +Example 2: incoming defer +^^^^^^^^^^^^^^^^^^^^^^^^^ + +This example uses ``-incoming defer`` to hot plug a device before +accepting the main migration channel. Again note you must issue the +migrate command to old QEMU before you can issue any monitor +commands to new QEMU. + + +:: + + Outgoing: Incoming: + + # qemu-kvm -monitor stdio + -object memory-backend-file,id=ram0,size=4G, + mem-path=/dev/shm/ram0,share=on -m 4G + -machine memory-backend=ram0 + -machine aux-ram-share=on + ... + # qemu-kvm -monitor stdio + -incoming defer + -incoming '{"channel-type": "cpr", + "addr": { "transport": "socket", + "type": "unix", "path": "cpr.sock"}}' + ... + {"execute":"qmp_capabilities"} + + {"execute": "device_add", + "arguments": {"driver": "pcie-root-port"}} + + {"execute":"migrate-set-parameters", + "arguments":{"mode":"cpr-transfer"}} + + {"execute": "migrate", "arguments": { "channels": [ + {"channel-type": "main", + "addr": { "transport": "socket", "type": "inet", + "host": "0", "port": "44444" }}, + {"channel-type": "cpr", + "addr": { "transport": "socket", "type": "unix", + "path": "cpr.sock" }}]}} + + QEMU 10.0.50 monitor + (qemu) info status + VM status: paused (inmigrate) + (qemu) device_add pcie-root-port + (qemu) migrate_incoming tcp:0:44444 + (qemu) info status + VM status: running + + {"execute": "query-status"} + {"return": {"status": "postmigrate", + "running": false}} + +Futures +^^^^^^^ + +cpr-transfer mode is based on a capability to transfer open file +descriptors from old to new QEMU. In the future, descriptors for +vfio, iommufd, vhost, and char devices could be transferred, +preserving those devices and their kernel state without interruption, +even if they do not explicitly support live migration. diff --git a/docs/devel/migration/best-practices.rst b/docs/devel/migration/best-practices.rst new file mode 100644 index 00000000000..d7c34a30149 --- /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 00000000000..ecb887e3184 --- /dev/null +++ b/docs/devel/migration/compatibility.rst @@ -0,0 +1,516 @@ +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 const 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 const 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), + }; + +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 00000000000..8f32329d5fd --- /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 00000000000..8f431d52f9d --- /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 00000000000..2aa294d6314 --- /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 00000000000..cdd4f4a6d7e --- /dev/null +++ b/docs/devel/migration/main.rst @@ -0,0 +1,604 @@ +.. _migration: + +=================== +Migration framework +=================== + +QEMU has code to load/save the state of the guest that it is running. +These are two complementary operations. Saving the state just does +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 00000000000..b08c2b433c4 --- /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 00000000000..82e7a848c63 --- /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 00000000000..862b3831647 --- /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 00000000000..990992d7865 --- /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 00000000000..64cadebd219 --- /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 00000000000..673e354754c --- /dev/null +++ b/docs/devel/migration/vfio.rst @@ -0,0 +1,249 @@ +===================== +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 ``switchover_start`` function that in the multifd mode starts a thread that + reassembles the multifd received data and loads it in-order into the device. + In the non-multifd mode this function is a NOP. + +* A ``save_state`` function to save the device config space if it is present + in the non-multifd mode. + In the multifd mode it just emits either a dummy EOS marker. + +* 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. + In the multifd mode it just emits a dummy EOS marker. + +* A ``save_live_complete_precopy_thread`` function that in the multifd mode + provides thread handler performing multifd device state transfer. + It sets the VFIO device to _STOP_COPY state, iteratively reads the data + from the VFIO device and queues it for multifd transmission until the vendor + driver indicates that no data remains. + After that, it saves the device config space and queues it for multifd + transfer too. + In the non-multifd mode this thread is a NOP. + +* A ``load_state`` function that loads the config section and the data + sections that are generated by the save functions above. + +* A ``load_state_buffer`` function that loads the device state and the device + config that arrived via multifd channels. + It's used only in the multifd mode. + +* ``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: in the non-multifd mode iterate in + .save_live_complete_precopy() until + pending data is 0 + In the multifd mode this iteration is done in + .save_live_complete_precopy_thread() instead. + | + (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 + transmitted via the main migration channel. + For data transmitted via multifd channels .load_state_buffer() is called + instead. + (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. + +Multifd +======= + +Starting from QEMU version 10.0 there's a possibility to transfer VFIO device +_STOP_COPY state via multifd channels. This helps reduce downtime - especially +with multiple VFIO devices or with devices having a large migration state. +As an additional benefit, setting the VFIO device to _STOP_COPY state and +saving its config space is also parallelized (run in a separate thread) in +such migration mode. + +The multifd VFIO device state transfer is controlled by +"x-migration-multifd-transfer" VFIO device property. This property defaults to +AUTO, which means that VFIO device state transfer via multifd channels is +attempted in configurations that otherwise support it. diff --git a/docs/devel/migration/virtio.rst b/docs/devel/migration/virtio.rst new file mode 100644 index 00000000000..611a18b8215 --- /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 e4801751f23..4ef539c0b0b 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 c9541a7b20a..da9a1530c9f 100644 --- a/docs/devel/multi-thread-tcg.rst +++ b/docs/devel/multi-thread-tcg.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. +.. _mttcg: + ================== Multi-threaded TCG ================== @@ -26,16 +28,15 @@ vCPU Scheduling We introduce a new running mode where each vCPU will run on its own user-space thread. This is enabled by default for all FE/BE combinations where the host memory model is able to accommodate the -guest (TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO is zero) and the -guest has had the required work done to support this safely -(TARGET_SUPPORTS_MTTCG). +guest (TCGCPUOps::guest_default_memory_order & ~TCG_TARGET_DEFAULT_MO is zero) +and the guest has had the required work done to support this safely +(TCGCPUOps::mttcg_supported). System emulation will fall back to the original round robin approach if: * forced by --accel tcg,thread=single * enabling --icount mode -* 64 bit guests on 32 bit hosts (TCG_OVERSIZED_GUEST) In the general case of running translated code there should be no inter-vCPU dependencies and all vCPUs should be able to run at full @@ -109,6 +110,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 +206,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 +223,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 00000000000..d1f3fc4510a --- /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 a3e949f6b3a..00000000000 --- a/docs/devel/multiple-iothreads.txt +++ /dev/null @@ -1,145 +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_bh_new_guarded() - create a BH with a device re-entrancy guard - * LEGACY qemu_aio_wait() - run an event loop iteration - -Since they implicitly work on the main loop they cannot be used in code that -runs in an IOThread. They might cause a crash or deadlock if called from an -IOThread since the QEMU global mutex is not held. - -Instead, use the AioContext functions directly (see include/block/aio.h): - * aio_set_fd_handler() - monitor a file descriptor - * aio_set_event_notifier() - monitor an event notifier - * aio_timer_new() - create a timer - * aio_bh_new() - create a BH - * aio_bh_new_guarded() - create a BH with a device re-entrancy guard - * aio_poll() - run an event loop iteration - -The qemu_bh_new_guarded/aio_bh_new_guarded APIs accept a "MemReentrancyGuard" -argument, which is used to check for and prevent re-entrancy problems. For -BHs associated with devices, the reentrancy-guard is contained in the -corresponding DeviceState and named "mem_reentrancy_guard". - -The AioContext can be obtained from the IOThread using -iothread_get_aio_context() or for the main loop using qemu_get_aio_context(). -Code that takes an AioContext argument works both in IOThreads or the main -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 00000000000..68739334f3c --- /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 7f78183cd48..231cc0fecf7 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -9,6 +9,7 @@ How to use the QAPI code generator This work is licensed under the terms of the GNU GPL, version 2 or later. See the COPYING file in the top-level directory. +.. _qapi: Introduction ============ @@ -167,6 +168,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 +185,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. @@ -223,7 +229,8 @@ These are of the form PREFIX_NAME, where PREFIX is derived from the enumeration type's name, and NAME from the value's name. For the example above, the generator maps 'MyEnum' to MY_ENUM and 'value1' to VALUE1, resulting in the enumeration constant MY_ENUM_VALUE1. The -optional 'prefix' member overrides PREFIX. +optional 'prefix' member overrides PREFIX. This is rarely necessary, +and should be used with restraint. The generated C enumeration constants have values 0, 1, ..., N-1 (in QAPI schema order), where N is the number of values. There is an @@ -594,7 +601,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. @@ -737,9 +744,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 @@ -757,8 +763,8 @@ Names beginning with ``x-`` used to signify "experimental". This convention has been replaced by special feature "unstable". Pragmas ``command-name-exceptions`` and ``member-name-exceptions`` let -you violate naming rules. Use for new code is strongly discouraged. See -`Pragma directives`_ for details. +you violate naming rules. Use for new code is strongly discouraged. +See `Pragma directives`_ for details. Downstream extensions @@ -895,7 +901,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:: # :: # @@ -969,7 +975,7 @@ 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. -Descriptions start with '\@name:'. The description text should be +Descriptions start with '\@name:'. The description text must be indented like this:: # @name: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed @@ -982,36 +988,94 @@ indented like this:: Extensions added after the definition was first released carry a "(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 second and subsequent lines of sections other than -"Example"/"Examples" should be indented like this:: +The second and subsequent lines of tagged sections must be indented +like this:: - # Note: Ut enim ad minim veniam, quis nostrud exercitation ullamco + # 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 rendered entirely -as literal fixed-width text. "TODO" sections are not rendered at all -(they are for developers, not users of QMP). In other sections, the -text is formatted, and rST markup can be used. +"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. An +elision is commonly ``...``, but it can also be or a pair of ``...`` +with text in between. + +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:: + # + # -> { "execute": "query-name" } + # <- { "return": { "name": "Fred" } } + +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. 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" } + # <- { "return": [ + # { + # "device": "ide0-hd0", + # ... + # } + # ... more ... + # ] } + # + # Above, lengthy output has been omitted for brevity. + +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. + + +Examples of complete definition documentation:: + ## # @BlockStats: # @@ -1020,11 +1084,11 @@ For example:: # @device: If the stats are for a virtual block device, the name # 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', @@ -1036,19 +1100,19 @@ 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) + # ... explain, explain ... + # (Since 2.3) # # Returns: A list of @BlockStats for each virtual block devices. # - # Since: 0.14.0 - # - # Example: + # Since: 0.14 # - # -> { "execute": "query-blockstats" } - # <- { - # ... lots of output ... - # } + # .. qmp-example:: # + # -> { "execute": "query-blockstats" } + # <- { + # ... + # } ## { 'command': 'query-blockstats', 'data': { '*query-nodes': 'bool' }, @@ -1082,8 +1146,10 @@ need to line up with each other, like this:: # or cache associativity unknown) # (since 5.0) -Section tags are case-sensitive and end with a colon. Good example:: +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):: @@ -1407,7 +1473,9 @@ As an example, we'll use the following schema, which describes a single complex user-defined type, along with command which takes a list of that type as a parameter, and returns a single element of that type. The user is responsible for writing the implementation of -qmp_my_command(); everything else is produced by the generator. :: +qmp_my_command(); everything else is produced by the generator. + +:: $ cat example-schema.json { 'struct': 'UserDefOne', @@ -1797,7 +1865,7 @@ Example:: #ifndef EXAMPLE_QAPI_INIT_COMMANDS_H #define EXAMPLE_QAPI_INIT_COMMANDS_H - #include "qapi/qmp/dispatch.h" + #include "qapi/qmp-registry.h" void example_qmp_init_marshal(QmpCommandList *cmds); @@ -1928,7 +1996,7 @@ Example:: #ifndef EXAMPLE_QAPI_INTROSPECT_H #define EXAMPLE_QAPI_INTROSPECT_H - #include "qapi/qmp/qlit.h" + #include "qobject/qlit.h" extern const QLitObject example_qmp_schema_qlit; diff --git a/docs/devel/qapi-domain.rst b/docs/devel/qapi-domain.rst new file mode 100644 index 00000000000..11238723c2d --- /dev/null +++ b/docs/devel/qapi-domain.rst @@ -0,0 +1,716 @@ +====================== +The Sphinx QAPI Domain +====================== + +An extension to the `rST syntax +`_ +in Sphinx is provided by the QAPI Domain, located in +``docs/sphinx/qapi_domain.py``. This extension is analogous to the +`Python Domain +`_ +included with Sphinx, but provides special directives and roles +speciically for annotating and documenting QAPI definitions +specifically. + +A `Domain +`_ +provides a set of special rST directives and cross-referencing roles to +Sphinx for understanding rST markup written to document a specific +language. By itself, this QAPI extension is only sufficient to parse rST +markup written by hand; the `autodoc +`_ +functionality is provided elsewhere, in ``docs/sphinx/qapidoc.py``, by +the "Transmogrifier". + +It is not expected that any developer nor documentation writer would +never need to write *nor* read these special rST forms. However, in the +event that something needs to be debugged, knowing the syntax of the +domain is quite handy. This reference may also be useful as a guide for +understanding the QAPI Domain extension code itself. Although most of +these forms will not be needed for documentation writing purposes, +understanding the cross-referencing syntax *will* be helpful when +writing rST documentation elsewhere, or for enriching the body of +QAPIDoc blocks themselves. + + +Concepts +======== + +The QAPI Domain itself provides no mechanisms for reading the QAPI +Schema or generating documentation from code that exists. It is merely +the rST syntax used to describe things. For instance, the Sphinx Python +domain adds syntax like ``:py:func:`` for describing Python functions in +documentation, but it's the autodoc module that is responsible for +reading Python code and generating such syntax. QAPI is analogous here: +qapidoc.py is responsible for reading the QAPI Schema and generating rST +syntax, and qapi_domain.py is responsible for translating that special +syntax and providing APIs for Sphinx internals. + +In other words: + +qapi_domain.py adds syntax like ``.. qapi:command::`` to Sphinx, and +qapidoc.py transforms the documentation in ``qapi/*.json`` into rST +using directives defined by the domain. + +Or even shorter: + +``:py:`` is to ``:qapi:`` as *autodoc* is to *qapidoc*. + + +Info Field Lists +================ + +`Field lists +`_ +are a standard syntax in reStructuredText. Sphinx `extends that syntax +`_ +to give certain field list entries special meaning and parsing to, for +example, add cross-references. The QAPI Domain takes advantage of this +field list extension to document things like Arguments, Members, Values, +and so on. + +The special parsing and handling of info field lists in Sphinx is provided by +three main classes; Field, GroupedField, and TypedField. The behavior +and formatting for each configured field list entry in the domain +changes depending on which class is used. + +Field: + * Creates an ungrouped field: i.e., each entry will create its own + section and they will not be combined. + * May *optionally* support an argument. + * May apply cross-reference roles to *either* the argument *or* the + content body, both, or neither. + +This is used primarily for entries which are not expected to be +repeated, i.e., items that may only show up at most once. The QAPI +domain uses this class for "Errors" section. + +GroupedField: + * Creates a grouped field: i.e. multiple adjacent entries will be + merged into one section, and the content will form a bulleted list. + * *Must* take an argument. + * May optionally apply a cross-reference role to the argument, but not + the body. + * Can be configured to remove the bulleted list if there is only a + single entry. + * All items will be generated with the form: "argument -- body" + +This is used for entries which are expected to be repeated, but aren't +expected to have two arguments, i.e. types without names, or names +without types. The QAPI domain uses this class for features, returns, +and enum values. + +TypedField: + * Creates a grouped, typed field. Multiple adjacent entres will be + merged into one section, and the content will form a bulleted list. + * *Must* take at least one argument, but supports up to two - + nominally, a name and a type. + * May optionally apply a cross-reference role to the type or the name + argument, but not the body. + * Can be configured to remove the bulleted list if there is only a + single entry. + * All items will be generated with the form "name (type) -- body" + +This is used for entries that are expected to be repeated and will have +a name, a type, and a description. The QAPI domain uses this class for +arguments, alternatives, and members. Wherever type names are referenced +below, They must be a valid, documented type that will be +cross-referenced in the HTML output; or one of the built-in JSON types +(string, number, int, boolean, null, value, q_empty). + + +``:feat:`` +---------- + +Document a feature attached to a QAPI definition. + +:availability: This field list is available in the body of Command, + Event, Enum, Object and Alternate directives. +:syntax: ``:feat name: Lorem ipsum, dolor sit amet...`` +:type: `sphinx.util.docfields.GroupedField + `_ + +Example:: + + .. qapi:object:: BlockdevOptionsVirtioBlkVhostVdpa + :since: 7.2 + :ifcond: CONFIG_BLKIO + + Driver specific block device options for the virtio-blk-vhost-vdpa + backend. + + :memb string path: path to the vhost-vdpa character device. + :feat fdset: Member ``path`` supports the special "/dev/fdset/N" path + (since 8.1) + + +``:arg:`` +--------- + +Document an argument to a QAPI command. + +:availability: This field list is only available in the body of the + Command directive. +:syntax: ``:arg type name: description`` +:type: `sphinx.util.docfields.TypedField + `_ + + +Example:: + + .. qapi:command:: job-pause + :since: 3.0 + + Pause an active job. + + 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. + + Cancelling a paused job automatically resumes it. + + :arg string id: The job identifier. + + +``:error:`` +----------- + +Document the error condition(s) of a QAPI command. + +:availability: This field list is only available in the body of the + Command directive. +:syntax: ``:error: Lorem ipsum dolor sit amet ...`` +:type: `sphinx.util.docfields.Field + `_ + +The format of the :errors: field list description is free-form rST. The +alternative spelling ":errors:" is also permitted, but strictly +analogous. + +Example:: + + .. qapi:command:: block-job-set-speed + :since: 1.1 + + Set maximum speed for a background block operation. + + This command can only be issued when there is an active block job. + + Throttling can be disabled by setting the speed to 0. + + :arg string 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. + :arg int speed: the maximum speed, in bytes per second, or 0 for + unlimited. Defaults to 0. + :error: + - If no background operation is active on this device, + DeviceNotActive + + +``:return:`` +------------- + +Document the return type(s) and value(s) of a QAPI command. + +:availability: This field list is only available in the body of the + Command directive. +:syntax: ``:return type: Lorem ipsum dolor sit amet ...`` +:type: `sphinx.util.docfields.GroupedField + `_ + + +Example:: + + .. qapi:command:: query-replay + :since: 5.2 + + Retrieve the record/replay information. It includes current + instruction count which may be used for ``replay-break`` and + ``replay-seek`` commands. + + :return ReplayInfo: record/replay information. + + .. qmp-example:: + + -> { "execute": "query-replay" } + <- { "return": { + "mode": "play", "filename": "log.rr", "icount": 220414 } + } + + +``:value:`` +----------- + +Document a possible value for a QAPI enum. + +:availability: This field list is only available in the body of the Enum + directive. +:syntax: ``:value name: Lorem ipsum, dolor sit amet ...`` +:type: `sphinx.util.docfields.GroupedField + `_ + +Example:: + + .. qapi:enum:: QapiErrorClass + :since: 1.2 + + QEMU error classes + + :value GenericError: this is used for errors that don't require a specific + error class. This should be the default case for most errors + :value CommandNotFound: the requested command has not been found + :value DeviceNotActive: a device has failed to be become active + :value DeviceNotFound: the requested device has not been found + :value KVMMissingCap: the requested operation can't be fulfilled because a + required KVM capability is missing + + +``:alt:`` +------------ + +Document a possible branch for a QAPI alternate. + +:availability: This field list is only available in the body of the + Alternate directive. +:syntax: ``:alt type name: Lorem ipsum, dolor sit amet ...`` +:type: `sphinx.util.docfields.TypedField + `_ + +As a limitation of Sphinx, we must document the "name" of the branch in +addition to the type, even though this information is not visible on the +wire in the QMP protocol format. This limitation *may* be lifted at a +future date. + +Example:: + + .. qapi:alternate:: StrOrNull + :since: 2.10 + + This is a string value or the explicit lack of a string (null + pointer in C). Intended for cases when 'optional absent' already + has a different meaning. + + :alt string s: the string value + :alt null n: no string value + + +``:memb:`` +---------- + +Document a member of an Event or Object. + +:availability: This field list is available in the body of Event or + Object directives. +:syntax: ``:memb type name: Lorem ipsum, dolor sit amet ...`` +:type: `sphinx.util.docfields.TypedField + `_ + +This is fundamentally the same as ``:arg:`` and ``:alt:``, but uses the +"Members" phrasing for Events and Objects (Structs and Unions). + +Example:: + + .. qapi:event:: JOB_STATUS_CHANGE + :since: 3.0 + + Emitted when a job transitions to a different status. + + :memb string id: The job identifier + :memb JobStatus status: The new job status + + +Arbitrary field lists +--------------------- + +Other field list names, while valid rST syntax, are prohibited inside of +QAPI directives to help prevent accidental misspellings of info field +list names. If you want to add a new arbitrary "non-value-added" field +list to QAPI documentation, you must add the field name to the allow +list in ``docs/conf.py`` + +For example:: + + qapi_allowed_fields = { + "see also", + } + +Will allow you to add arbitrary field lists in QAPI directives:: + + .. qapi:command:: x-fake-command + + :see also: Lorem ipsum, dolor sit amet ... + + +Cross-references +================ + +Cross-reference `roles +`_ +in the QAPI domain are modeled closely after the `Python +cross-referencing syntax +`_. + +QAPI definitions can be referenced using the standard `any +`_ +role cross-reference syntax, such as with ```query-blockstats```. In +the event that disambiguation is needed, cross-references can also be +written using a number of explicit cross-reference roles: + +* ``:qapi:mod:`block-core``` -- Reference a QAPI module. The link will + take you to the beginning of that section in the documentation. +* ``:qapi:cmd:`query-block``` -- Reference a QAPI command. +* ``:qapi:event:`JOB_STATUS_CHANGE``` -- Reference a QAPI event. +* ``:qapi:enum:`QapiErrorClass``` -- Reference a QAPI enum. +* ``:qapi:obj:`BlockdevOptionsVirtioBlkVhostVdpa`` -- Reference a QAPI + object (struct or union) +* ``:qapi:alt:`StrOrNull``` -- Reference a QAPI alternate. +* ``:qapi:type:`BlockDirtyInfo``` -- Reference *any* QAPI type; this + excludes modules, commands, and events. +* ``:qapi:any:`block-job-set-speed``` -- Reference absolutely any QAPI entity. + +Type arguments in info field lists are converted into references as if +you had used the ``:qapi:type:`` role. All of the special syntax below +applies to both info field lists and standalone explicit +cross-references. + + +Type decorations +---------------- + +Type names in references can be surrounded by brackets, like +``[typename]``, to indicate an array of that type. The cross-reference +will apply only to the type name between the brackets. For example; +``:qapi:type:`[Qcow2BitmapInfoFlags]``` renders to: +:qapi:type:`[QMP:Qcow2BitmapInfoFlags]` + +To indicate an optional argument/member in a field list, the type name +can be suffixed with ``?``. The cross-reference will be transformed to +"type, Optional" with the link applying only to the type name. For +example; ``:qapi:type:`BitmapSyncMode?``` renders to: +:qapi:type:`QMP:BitmapSyncMode?` + + +Namespaces +---------- + +Mimicking the `Python domain target specification syntax +`_, +QAPI allows you to specify the fully qualified path for a data +type. + +* A namespace can be explicitly provided; + e.g. ``:qapi:type:`QMP:BitmapSyncMode`` +* A module can be explicitly provided; + ``:qapi:type:`QMP:block-core.BitmapSyncMode``` will render to: + :qapi:type:`QMP:block-core.BitmapSyncMode` +* If you don't want to display the "fully qualified" name, it can be + prefixed with a tilde; ``:qapi:type:`~QMP:block-core.BitmapSyncMode``` + will render to: :qapi:type:`~QMP:block-core.BitmapSyncMode` + + +Target resolution +----------------- + +Any cross-reference to a QAPI type, whether using the ```any``` style of +reference or the more explicit ```:qapi:any:`target``` syntax, allows +for the presence or absence of either the namespace or module +information. + +When absent, their value will be inferred from context by the presence +of any ``qapi:namespace`` or ``qapi:module`` directives preceding the +cross-reference. + +If no results are found when using the inferred values, other +namespaces/modules will be searched as a last resort; but any explicitly +provided values must always match in order to succeed. + +This allows for efficient cross-referencing with a minimum of syntax in +the large majority of cases, but additional context or namespace markup +may be required outside of the QAPI reference documents when linking to +items that share a name across multiple documented QAPI schema. + + +Custom link text +---------------- + +The name of a cross-reference link can be explicitly overridden like +`most stock Sphinx references +`_ +using the ``custom text `` syntax. + +For example, ``:qapi:cmd:`Merge dirty bitmaps +``` will render as: :qapi:cmd:`Merge dirty +bitmaps ` + + +Directives +========== + +The QAPI domain adds a number of custom directives for documenting +various QAPI/QMP entities. The syntax is plain rST, and follows this +general format:: + + .. qapi:directive:: argument + :option: + :another-option: with an argument + + Content body, arbitrary rST is allowed here. + + +Sphinx standard options +----------------------- + +All QAPI directives inherit a number of `standard options +`_ +from Sphinx's ObjectDescription class. + +The dashed spellings of the below options were added in Sphinx 7.2, the +undashed spellings are currently retained as aliases, but will be +removed in a future version. + +* ``:no-index:`` and ``:noindex:`` -- Do not add this item into the + Index, and do not make it available for cross-referencing. +* ``no-index-entry:`` and ``:noindexentry:`` -- Do not add this item + into the Index, but allow it to be cross-referenced. +* ``no-contents-entry`` and ``:nocontentsentry:`` -- Exclude this item + from the Table of Contents. +* ``no-typesetting`` -- Create TOC, Index and cross-referencing + entities, but don't actually display the content. + + +QAPI standard options +--------------------- + +All QAPI directives -- *except* for namespace and module -- support +these common options. + +* ``:namespace: name`` -- This option allows you to override the + namespace association of a given definition. +* ``:module: modname`` -- Borrowed from the Python domain, this option allows + you to override the module association of a given definition. +* ``:since: x.y`` -- Allows the documenting of "Since" information, which is + displayed in the signature bar. +* ``:ifcond: CONDITION`` -- Allows the documenting of conditional availability + information, which is displayed in an eyecatch just below the + signature bar. +* ``:deprecated:`` -- Adds an eyecatch just below the signature bar that + advertises that this definition is deprecated and should be avoided. +* ``:unstable:`` -- Adds an eyecatch just below the signature bar that + advertises that this definition is unstable and should not be used in + production code. + + +qapi:namespace +-------------- + +The ``qapi:namespace`` directive marks the start of a QAPI namespace. It +does not take a content body, nor any options. All subsequent QAPI +directives are associated with the most recent namespace. This affects +the definition's "fully qualified name", allowing two different +namespaces to create an otherwise identically named definition. + +This directive also influences how reference resolution works for any +references that do not explicitly specify a namespace, so this directive +can be used to nudge references into preferring targets from within that +namespace. + +Example:: + + .. qapi:namespace:: QMP + + +This directive has no visible effect. + + +qapi:module +----------- + +The ``qapi:module`` directive marks the start of a QAPI module. It may have +a content body, but it can be omitted. All subsequent QAPI directives +are associated with the most recent module; this effects their "fully +qualified" name, but has no other effect. + +Example:: + + .. qapi:module:: block-core + + Welcome to the block-core module! + +Will be rendered as: + +.. qapi:module:: block-core + :noindex: + + Welcome to the block-core module! + + +qapi:command +------------ + +This directive documents a QMP command. It may use any of the standard +Sphinx or QAPI options, and the documentation body may contain +``:arg:``, ``:feat:``, ``:error:``, or ``:return:`` info field list +entries. + +Example:: + + .. qapi:command:: x-fake-command + :since: 42.0 + :unstable: + + This command is fake, so it can't hurt you! + + :arg int foo: Your favorite number. + :arg string? bar: Your favorite season. + :return [string]: A lovely computer-written poem for you. + + +Will be rendered as: + + .. qapi:command:: x-fake-command + :noindex: + :since: 42.0 + :unstable: + + This command is fake, so it can't hurt you! + + :arg int foo: Your favorite number. + :arg string? bar: Your favorite season. + :return [string]: A lovely computer-written poem for you. + + +qapi:event +---------- + +This directive documents a QMP event. It may use any of the standard +Sphinx or QAPI options, and the documentation body may contain +``:memb:`` or ``:feat:`` info field list entries. + +Example:: + + .. qapi:event:: COMPUTER_IS_RUINED + :since: 0.1 + :deprecated: + + This event is emitted when your computer is *extremely* ruined. + + :memb string reason: Diagnostics as to what caused your computer to + be ruined. + :feat sadness: When present, the diagnostic message will also + explain how sad the computer is as a result of your wrongdoings. + +Will be rendered as: + +.. qapi:event:: COMPUTER_IS_RUINED + :noindex: + :since: 0.1 + :deprecated: + + This event is emitted when your computer is *extremely* ruined. + + :memb string reason: Diagnostics as to what caused your computer to + be ruined. + :feat sadness: When present, the diagnostic message will also explain + how sad the computer is as a result of your wrongdoings. + + +qapi:enum +--------- + +This directive documents a QAPI enum. It may use any of the standard +Sphinx or QAPI options, and the documentation body may contain +``:value:`` or ``:feat:`` info field list entries. + +Example:: + + .. qapi:enum:: Mood + :ifcond: LIB_PERSONALITY + + This enum represents your virtual machine's current mood! + + :value Happy: Your VM is content and well-fed. + :value Hungry: Your VM needs food. + :value Melancholic: Your VM is experiencing existential angst. + :value Petulant: Your VM is throwing a temper tantrum. + +Will be rendered as: + +.. qapi:enum:: Mood + :noindex: + :ifcond: LIB_PERSONALITY + + This enum represents your virtual machine's current mood! + + :value Happy: Your VM is content and well-fed. + :value Hungry: Your VM needs food. + :value Melancholic: Your VM is experiencing existential angst. + :value Petulant: Your VM is throwing a temper tantrum. + + +qapi:object +----------- + +This directive documents a QAPI structure or union and represents a QMP +object. It may use any of the standard Sphinx or QAPI options, and the +documentation body may contain ``:memb:`` or ``:feat:`` info field list +entries. + +Example:: + + .. qapi:object:: BigBlobOfStuff + + This object has a bunch of disparate and unrelated things in it. + + :memb int Birthday: Your birthday, represented in seconds since the + UNIX epoch. + :memb [string] Fav-Foods: A list of your favorite foods. + :memb boolean? Bizarre-Docs: True if the documentation reference + should be strange. + +Will be rendered as: + +.. qapi:object:: BigBlobOfStuff + :noindex: + + This object has a bunch of disparate and unrelated things in it. + + :memb int Birthday: Your birthday, represented in seconds since the + UNIX epoch. + :memb [string] Fav-Foods: A list of your favorite foods. + :memb boolean? Bizarre-Docs: True if the documentation reference + should be strange. + + +qapi:alternate +-------------- + +This directive documents a QAPI alternate. It may use any of the +standard Sphinx or QAPI options, and the documentation body may contain +``:alt:`` or ``:feat:`` info field list entries. + +Example:: + + .. qapi:alternate:: ErrorCode + + This alternate represents an Error Code from the VM. + + :alt int ec: An error code, like the type you're used to. + :alt string em: An expletive-laced error message, if your + computer is feeling particularly cranky and tired of your + antics. + +Will be rendered as: + +.. qapi:alternate:: ErrorCode + :noindex: + + This alternate represents an Error Code from the VM. + + :alt int ec: An error code, like the type you're used to. + :alt string em: An expletive-laced error message, if your + computer is feeling particularly cranky and tired of your + antics. diff --git a/docs/devel/qom.rst b/docs/devel/qom.rst index 9918fac7f21..5870745ba27 100644 --- a/docs/devel/qom.rst +++ b/docs/devel/qom.rst @@ -147,7 +147,7 @@ to introduce an overridden virtual function: #include "qdev.h" - void my_device_class_init(ObjectClass *klass, void *class_data) + void my_device_class_init(ObjectClass *klass, const void *class_data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = my_device_reset; @@ -249,7 +249,7 @@ class, which someone might choose to change at some point. // do something } - static void my_class_init(ObjectClass *oc, void *data) + static void my_class_init(ObjectClass *oc, const void *data) { MyClass *mc = MY_CLASS(oc); @@ -279,7 +279,7 @@ class, which someone might choose to change at some point. // do something else here } - static void derived_class_init(ObjectClass *oc, void *data) + static void derived_class_init(ObjectClass *oc, const void *data) { MyClass *mc = MY_CLASS(oc); DerivedClass *dc = DERIVED_CLASS(oc); @@ -348,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: @@ -361,7 +363,7 @@ This is equivalent to the following: :caption: Expansion from defining a simple type static void my_device_finalize(Object *obj); - static void my_device_class_init(ObjectClass *oc, void *data); + static void my_device_class_init(ObjectClass *oc, const void *data); static void my_device_init(Object *obj); static const TypeInfo my_device_info = { @@ -370,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, }; @@ -385,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 }, diff --git a/docs/devel/rcu.rst b/docs/devel/rcu.rst new file mode 100644 index 00000000000..dd07c1d9195 --- /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 2e6cc607a17..00000000000 --- 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 0244be8b9c4..40f58d9d4fc 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 7cc6a6b3140..c02fe0a405c 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 @@ -112,6 +143,11 @@ The *exit* phase is executed only when the last reset operation ends. Therefore the object does not need to care how many of reset controllers it has and how many of them have started a reset. +DMA capable devices are expected to cancel all outstanding DMA operations +during either 'enter' or 'hold' phases. IOMMUs are expected to reset during +the 'exit' phase and this sequencing makes sure no outstanding DMA request +will fault. + Handling reset in a resettable object ------------------------------------- @@ -150,25 +186,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); @@ -180,25 +216,24 @@ in reset. ResettablePhases parent_phases; } MyDevClass; - static void mydev_class_init(ObjectClass *class, void *data) + static void mydev_class_init(ObjectClass *class, const void *data) { MyDevClass *myclass = MYDEV_CLASS(class); ResettableClass *rc = RESETTABLE_CLASS(class); - resettable_class_set_parent_reset_phases(rc, - mydev_reset_enter, - mydev_reset_hold, - mydev_reset_exit, - &myclass->parent_phases); + 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 +291,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 +324,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/rust.rst b/docs/devel/rust.rst new file mode 100644 index 00000000000..dc8c44109e1 --- /dev/null +++ b/docs/devel/rust.rst @@ -0,0 +1,478 @@ +.. |msrv| replace:: 1.63.0 + +Rust in QEMU +============ + +Rust in QEMU is a project to enable using the Rust programming language +to add new functionality to QEMU. + +Right now, the focus is on making it possible to write devices that inherit +from ``SysBusDevice`` in `*safe*`__ Rust. Later, it may become possible +to write other kinds of devices (e.g. PCI devices that can do DMA), +complete boards, or backends (e.g. block device formats). + +__ https://doc.rust-lang.org/nomicon/meet-safe-and-unsafe.html + +Building the Rust in QEMU code +------------------------------ + +The Rust in QEMU code is included in the emulators via Meson. Meson +invokes rustc directly, building static libraries that are then linked +together with the C code. This is completely automatic when you run +``make`` or ``ninja``. + +However, QEMU's build system also tries to be easy to use for people who +are accustomed to the more "normal" Cargo-based development workflow. +In particular: + +* the set of warnings and lints that are used to build QEMU always + comes from the ``rust/Cargo.toml`` workspace file + +* it is also possible to use ``cargo`` for common Rust-specific coding + tasks, in particular to invoke ``clippy``, ``rustfmt`` and ``rustdoc``. + +To this end, QEMU includes a ``build.rs`` build script that picks up +generated sources from QEMU's build directory and puts it in Cargo's +output directory (typically ``rust/target/``). A vanilla invocation +of Cargo will complain that it cannot find the generated sources, +which can be fixed in different ways: + +* by using Makefile targets, provided by Meson, that run ``clippy`` or + ``rustdoc``: + + make clippy + make rustdoc + +A target for ``rustfmt`` is also declared in ``rust/meson.build``: + + make rustfmt + +* by invoking ``cargo`` through the Meson `development environment`__ + feature:: + + pyvenv/bin/meson devenv -w ../rust cargo clippy --tests + pyvenv/bin/meson devenv -w ../rust cargo fmt + + If you are going to use ``cargo`` repeatedly, ``pyvenv/bin/meson devenv`` + will enter a shell where commands like ``cargo fmt`` just work. + +__ https://mesonbuild.com/Commands.html#devenv + +* by pointing the ``MESON_BUILD_ROOT`` to the top of your QEMU build + tree. This third method is useful if you are using ``rust-analyzer``; + you can set the environment variable through the + ``rust-analyzer.cargo.extraEnv`` setting. + +As shown above, you can use the ``--tests`` option as usual to operate on test +code. Note however that you cannot *build* or run tests via ``cargo``, because +they need support C code from QEMU that Cargo does not know about. Tests can +be run via ``meson test`` or ``make``:: + + make check-rust + +Note that doctests require all ``.o`` files from the build to be available. + +Supported tools +''''''''''''''' + +QEMU supports rustc version 1.77.0 and newer. Notably, the following features +are missing: + +* inline const expression (stable in 1.79.0), currently worked around with + associated constants in the ``FnCall`` trait. + +* associated constants have to be explicitly marked ``'static`` (`changed in + 1.81.0`__) + +* ``&raw`` (stable in 1.82.0). Use ``addr_of!`` and ``addr_of_mut!`` instead, + though hopefully the need for raw pointers will go down over time. + +* ``new_uninit`` (stable in 1.82.0). This is used internally by the ``pinned_init`` + crate, which is planned for inclusion in QEMU, but it can be easily patched + out. + +* referencing statics in constants (stable in 1.83.0). For now use a const + function; this is an important limitation for QEMU's migration stream + architecture (VMState). Right now, VMState lacks type safety because + it is hard to place the ``VMStateField`` definitions in traits. + +* NUL-terminated file names with ``#[track_caller]`` are scheduled for + inclusion as ``#![feature(location_file_nul)]``, but it will be a while + before QEMU can use them. For now, there is special code in + ``util/error.c`` to support non-NUL-terminated file names. + +* associated const equality would be nice to have for some users of + ``callbacks::FnCall``, but is still experimental. ``ASSERT_IS_SOME`` + replaces it. + +__ https://github.com/rust-lang/rust/pull/125258 + +QEMU also supports version 0.60.x of bindgen, which is missing option +``--generate-cstr``. This option requires version 0.66.x and will +be adopted as soon as supporting these older versions is not necessary +anymore. + +Writing Rust code in QEMU +------------------------- + +QEMU includes four crates: + +* ``qemu_api`` for bindings to C code and useful functionality + +* ``qemu_api_macros`` defines several procedural macros that are useful when + writing C code + +* ``pl011`` (under ``rust/hw/char/pl011``) and ``hpet`` (under ``rust/hw/timer/hpet``) + are sample devices that demonstrate ``qemu_api`` and ``qemu_api_macros``, and are + used to further develop them. These two crates are functional\ [#issues]_ replacements + for the ``hw/char/pl011.c`` and ``hw/timer/hpet.c`` files. + +.. [#issues] The ``pl011`` crate is synchronized with ``hw/char/pl011.c`` + as of commit 3e0f118f82. The ``hpet`` crate is synchronized as of + commit 1433e38cc8. Both are lacking tracing functionality. + +This section explains how to work with them. + +Status +'''''' + +Modules of ``qemu_api`` can be defined as: + +- *complete*: ready for use in new devices; if applicable, the API supports the + full functionality available in C + +- *stable*: ready for production use, the API is safe and should not undergo + major changes + +- *proof of concept*: the API is subject to change but allows working with safe + Rust + +- *initial*: the API is in its initial stages; it requires large amount of + unsafe code; it might have soundness or type-safety issues + +The status of the modules is as follows: + +================ ====================== +module status +================ ====================== +``assertions`` stable +``bitops`` complete +``callbacks`` complete +``cell`` stable +``errno`` complete +``error`` stable +``irq`` complete +``log`` proof of concept +``memory`` stable +``module`` complete +``qdev`` stable +``qom`` stable +``sysbus`` stable +``timer`` stable +``vmstate`` proof of concept +``zeroable`` stable +================ ====================== + +.. note:: + API stability is not a promise, if anything because the C APIs are not a stable + interface either. Also, ``unsafe`` interfaces may be replaced by safe interfaces + later. + +Naming convention +''''''''''''''''' + +C function names usually are prefixed according to the data type that they +apply to, for example ``timer_mod`` or ``sysbus_connect_irq``. Furthermore, +both function and structs sometimes have a ``qemu_`` or ``QEMU`` prefix. +Generally speaking, these are all removed in the corresponding Rust functions: +``QEMUTimer`` becomes ``timer::Timer``, ``timer_mod`` becomes ``Timer::modify``, +``sysbus_connect_irq`` becomes ``SysBusDeviceMethods::connect_irq``. + +Sometimes however a name appears multiple times in the QOM class hierarchy, +and the only difference is in the prefix. An example is ``qdev_realize`` and +``sysbus_realize``. In such cases, whenever a name is not unique in +the hierarchy, always add the prefix to the classes that are lower in +the hierarchy; for the top class, decide on a case by case basis. + +For example: + +========================== ========================================= +``device_cold_reset()`` ``DeviceMethods::cold_reset()`` +``pci_device_reset()`` ``PciDeviceMethods::pci_device_reset()`` +``pci_bridge_reset()`` ``PciBridgeMethods::pci_bridge_reset()`` +========================== ========================================= + +Here, the name is not exactly the same, but nevertheless ``PciDeviceMethods`` +adds the prefix to avoid confusion, because the functionality of +``device_cold_reset()`` and ``pci_device_reset()`` is subtly different. + +In this case, however, no prefix is needed: + +========================== ========================================= +``device_realize()`` ``DeviceMethods::realize()`` +``sysbus_realize()`` ``SysbusDeviceMethods::sysbus_realize()`` +``pci_realize()`` ``PciDeviceMethods::pci_realize()`` +========================== ========================================= + +Here, the lower classes do not add any functionality, and mostly +provide extra compile-time checking; the basic *realize* functionality +is the same for all devices. Therefore, ``DeviceMethods`` does not +add the prefix. + +Whenever a name is unique in the hierarchy, instead, you should +always remove the class name prefix. + +Common pitfalls +''''''''''''''' + +Rust has very strict rules with respect to how you get an exclusive (``&mut``) +reference; failure to respect those rules is a source of undefined behavior. +In particular, even if a value is loaded from a raw mutable pointer (``*mut``), +it *cannot* be casted to ``&mut`` unless the value was stored to the ``*mut`` +from a mutable reference. Furthermore, it is undefined behavior if any +shared reference was created between the store to the ``*mut`` and the load:: + + let mut p: u32 = 42; + let p_mut = &mut p; // 1 + let p_raw = p_mut as *mut u32; // 2 + + // p_raw keeps the mutable reference "alive" + + let p_shared = &p; // 3 + println!("access from &u32: {}", *p_shared); + + // Bring back the mutable reference, its lifetime overlaps + // with that of a shared reference. + let p_mut = unsafe { &mut *p_raw }; // 4 + println!("access from &mut 32: {}", *p_mut); + + println!("access from &u32: {}", *p_shared); // 5 + +These rules can be tested with `MIRI`__, for example. + +__ https://github.com/rust-lang/miri + +Almost all Rust code in QEMU will involve QOM objects, and pointers to these +objects are *shared*, for example because they are part of the QOM composition +tree. This creates exactly the above scenario: + +1. a QOM object is created + +2. a ``*mut`` is created, for example as the opaque value for a ``MemoryRegion`` + +3. the QOM object is placed in the composition tree + +4. a memory access dereferences the opaque value to a ``&mut`` + +5. but the shared reference is still present in the composition tree + +Because of this, QOM objects should almost always use ``&self`` instead +of ``&mut self``; access to internal fields must use *interior mutability* +to go from a shared reference to a ``&mut``. + +Whenever C code provides you with an opaque ``void *``, avoid converting it +to a Rust mutable reference, and use a shared reference instead. The +``qemu_api::cell`` module provides wrappers that can be used to tell the +Rust compiler about interior mutability, and optionally to enforce locking +rules for the "Big QEMU Lock". In the future, similar cell types might +also be provided for ``AioContext``-based locking as well. + +In particular, device code will usually rely on the ``BqlRefCell`` and +``BqlCell`` type to ensure that data is accessed correctly under the +"Big QEMU Lock". These cell types are also known to the ``vmstate`` +crate, which is able to "look inside" them when building an in-memory +representation of a ``struct``'s layout. Note that the same is not true +of a ``RefCell`` or ``Mutex``. + +Bindings code instead will usually use the ``Opaque`` type, which hides +the contents of the underlying struct and can be easily converted to +a raw pointer, for use in calls to C functions. It can be used for +example as follows:: + + #[repr(transparent)] + #[derive(Debug, qemu_api_macros::Wrapper)] + pub struct Object(Opaque); + +where the special ``derive`` macro provides useful methods such as +``from_raw``, ``as_ptr`, ``as_mut_ptr`` and ``raw_get``. The bindings will +then manually check for the big QEMU lock with assertions, which allows +the wrapper to be declared thread-safe:: + + unsafe impl Send for Object {} + unsafe impl Sync for Object {} + +Writing bindings to C code +'''''''''''''''''''''''''' + +Here are some things to keep in mind when working on the ``qemu_api`` crate. + +**Look at existing code** + Very often, similar idioms in C code correspond to similar tricks in + Rust bindings. If the C code uses ``offsetof``, look at qdev properties + or ``vmstate``. If the C code has a complex const struct, look at + ``MemoryRegion``. Reuse existing patterns for handling lifetimes; + for example use ``&T`` for QOM objects that do not need a reference + count (including those that can be embedded in other objects) and + ``Owned`` for those that need it. + +**Use the type system** + Bindings often will need access information that is specific to a type + (either a builtin one or a user-defined one) in order to pass it to C + functions. Put them in a trait and access it through generic parameters. + The ``vmstate`` module has examples of how to retrieve type information + for the fields of a Rust ``struct``. + +**Prefer unsafe traits to unsafe functions** + Unsafe traits are much easier to prove correct than unsafe functions. + They are an excellent place to store metadata that can later be accessed + by generic functions. C code usually places metadata in global variables; + in Rust, they can be stored in traits and then turned into ``static`` + variables. Often, unsafe traits can be generated by procedural macros. + +**Document limitations due to old Rust versions** + If you need to settle for an inferior solution because of the currently + supported set of Rust versions, document it in the source and in this + file. This ensures that it can be fixed when the minimum supported + version is bumped. + +**Keep locking in mind**. + When marking a type ``Sync``, be careful of whether it needs the big + QEMU lock. Use ``BqlCell`` and ``BqlRefCell`` for interior data, + or assert ``bql_locked()``. + +**Don't be afraid of complexity, but document and isolate it** + It's okay to be tricky; device code is written more often than bindings + code and it's important that it is idiomatic. However, you should strive + to isolate any tricks in a place (for example a ``struct``, a trait + or a macro) where it can be documented and tested. If needed, include + toy versions of the code in the documentation. + +Writing procedural macros +''''''''''''''''''''''''' + +By conventions, procedural macros are split in two functions, one +returning ``Result`` with the body of +the procedural macro, and the second returning ``proc_macro::TokenStream`` +which is the actual procedural macro. The former's name is the same as +the latter with the ``_or_error`` suffix. The code for the latter is more +or less fixed; it follows the following template, which is fixed apart +from the type after ``as`` in the invocation of ``parse_macro_input!``:: + + #[proc_macro_derive(Object)] + pub fn derive_object(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let expanded = derive_object_or_error(input).unwrap_or_else(Into::into); + + TokenStream::from(expanded) + } + +The ``qemu_api_macros`` crate has utility functions to examine a +``DeriveInput`` and perform common checks (e.g. looking for a struct +with named fields). These functions return ``Result<..., MacroError>`` +and can be used easily in the procedural macro function:: + + fn derive_object_or_error(input: DeriveInput) -> + Result + { + is_c_repr(&input, "#[derive(Object)]")?; + + let name = &input.ident; + let parent = &get_fields(&input, "#[derive(Object)]")?[0].ident; + ... + } + +Use procedural macros with care. They are mostly useful for two purposes: + +* Performing consistency checks; for example ``#[derive(Object)]`` checks + that the structure has ``#[repr[C])`` and that the type of the first field + is consistent with the ``ObjectType`` declaration. + +* Extracting information from Rust source code into traits, typically based + on types and attributes. For example, ``#[derive(TryInto)]`` builds an + implementation of ``TryFrom``, and it uses the ``#[repr(...)]`` attribute + as the ``TryFrom`` source and error types. + +Procedural macros can be hard to debug and test; if the code generation +exceeds a few lines of code, it may be worthwhile to delegate work to +"regular" declarative (``macro_rules!``) macros and write unit tests for +those instead. + + +Coding style +'''''''''''' + +Code should pass clippy and be formatted with rustfmt. + +Right now, only the nightly version of ``rustfmt`` is supported. This +might change in the future. While CI checks for correct formatting via +``cargo fmt --check``, maintainers can fix this for you when applying patches. + +It is expected that ``qemu_api`` provides full ``rustdoc`` documentation for +bindings that are in their final shape or close. + +Adding dependencies +------------------- + +Generally, the set of dependent crates is kept small. Think twice before +adding a new external crate, especially if it comes with a large set of +dependencies itself. Sometimes QEMU only needs a small subset of the +functionality; see for example QEMU's ``assertions`` module. + +On top of this recommendation, adding external crates to QEMU is a +slightly complicated process, mostly due to the need to teach Meson how +to build them. While Meson has initial support for parsing ``Cargo.lock`` +files, it is still highly experimental and is therefore not used. + +Therefore, external crates must be added as subprojects for Meson to +learn how to build them, as well as to the relevant ``Cargo.toml`` files. +The versions specified in ``rust/Cargo.lock`` must be the same as the +subprojects; note that the ``rust/`` directory forms a Cargo `workspace`__, +and therefore there is a single lock file for the whole build. + +__ https://doc.rust-lang.org/cargo/reference/workspaces.html#virtual-workspace + +Choose a version of the crate that works with QEMU's minimum supported +Rust version (|msrv|). + +Second, a new ``wrap`` file must be added to teach Meson how to download the +crate. The wrap file must be named ``NAME-SEMVER-rs.wrap``, where ``NAME`` +is the name of the crate and ``SEMVER`` is the version up to and including the +first non-zero number. For example, a crate with version ``0.2.3`` will use +``0.2`` for its ``SEMVER``, while a crate with version ``1.0.84`` will use ``1``. + +Third, the Meson rules to build the crate must be added at +``subprojects/NAME-SEMVER-rs/meson.build``. Generally this includes: + +* ``subproject`` and ``dependency`` lines for all dependent crates + +* a ``static_library`` or ``rust.proc_macro`` line to perform the actual build + +* ``declare_dependency`` and a ``meson.override_dependency`` lines to expose + the result to QEMU and to other subprojects + +Remember to add ``native: true`` to ``dependency``, ``static_library`` and +``meson.override_dependency`` for dependencies of procedural macros. +If a crate is needed in both procedural macros and QEMU binaries, everything +apart from ``subproject`` must be duplicated to build both native and +non-native versions of the crate. + +It's important to specify the right compiler options. These include: + +* the language edition (which can be found in the ``Cargo.toml`` file) + +* the ``--cfg`` (which have to be "reverse engineered" from the ``build.rs`` + file of the crate). + +* usually, a ``--cap-lints allow`` argument to hide warnings from rustc + or clippy. + +After every change to the ``meson.build`` file you have to update the patched +version with ``meson subprojects update --reset ``NAME-SEMVER-rs``. This might +be automated in the future. + +Also, after every change to the ``meson.build`` file it is strongly suggested to +do a dummy change to the ``.wrap`` file (for example adding a comment like +``# version 2``), which will help Meson notice that the subproject is out of date. + +As a last step, add the new subproject to ``scripts/archive-source.sh``, +``scripts/make-release`` and ``subprojects/.gitignore``. diff --git a/docs/devel/s390-cpu-topology.rst b/docs/devel/s390-cpu-topology.rst new file mode 100644 index 00000000000..48313b92d41 --- /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 2f68b500798..d025933808e 100644 --- a/docs/devel/style.rst +++ b/docs/devel/style.rst @@ -416,6 +416,26 @@ definitions instead of typedefs in headers and function prototypes; this avoids problems with duplicated typedefs and reduces the need to include headers from other headers. +Bitfields +--------- + +C bitfields can be a cause of non-portability issues, especially under windows +where `MSVC has a different way to lay them out than GCC +`_, or where +endianness matters. + +For this reason, we disallow usage of bitfields in packed structures and in any +structures which are supposed to exactly match a specific layout in guest +memory. Some existing code may use it, and we carefully ensured the layout was +the one expected. + +We also suggest avoiding bitfields even in structures where the exact +layout does not matter, unless you can show that they provide a significant +usability benefit. + +We encourage the usage of ``include/hw/registerfields.h`` as a safe replacement +for bitfields. + Reserved namespaces in C and POSIX ---------------------------------- diff --git a/docs/devel/submitting-a-patch.rst b/docs/devel/submitting-a-patch.rst index c641d948f16..f7917b899f6 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. @@ -235,6 +235,31 @@ to another list.) ``git send-email`` (`step-by-step setup guide works best for delivering the patch without mangling it, but attachments can be used as a last resort on a first-time submission. +.. _use_git_publish: + +Use git-publish +~~~~~~~~~~~~~~~ + +If you already configured git send-email, you can simply use `git-publish +`__ to send series. + +:: + + $ git checkout master -b my-feature + $ # work on new commits, add your 'Signed-off-by' lines to each + $ git publish + $ ... more work, rebase on master, ... + $ git publish # will send a v2 + +Each time you post a series, git-publish will create a local tag with the format +``-v`` to record the patch series. + +When sending patch emails, 'git publish' will consult the output of +'scripts/get_maintainers.pl' and automatically CC anyone listed as maintainers +of the affected code. Generally you should accept the suggested CC list, but +there may sometimes be scenarios where it is appropriate to cut it down (eg on +certain large tree-wide cleanups), or augment it with other interested people. + .. _if_you_cannot_send_patch_emails: If you cannot send patch emails @@ -252,10 +277,7 @@ patches to the QEMU mailing list by following these steps: #. Send your patches to the QEMU mailing list using the web-based ``git-send-email`` UI at https://git.sr.ht/~USERNAME/qemu/send-email -`This video -`__ -shows the web-based ``git-send-email`` workflow. Documentation is -available `here +Documentation for sourcehut is available `here `__. .. _cc_the_relevant_maintainer: @@ -322,23 +344,9 @@ Patch emails must include a ``Signed-off-by:`` line Your patches **must** include a Signed-off-by: line. This is a hard requirement because it's how you say "I'm legally okay to contribute -this and happy for it to go into QEMU". The process is modelled after -the `Linux kernel -`__ -policy. - -If you wrote the patch, make sure your "From:" and "Signed-off-by:" -lines use the same spelling. It's okay if you subscribe or contribute to -the list via more than one address, but using multiple addresses in one -commit just confuses things. If someone else wrote the patch, git will -include a "From:" line in the body of the email (different from your -envelope From:) that will give credit to the correct author; but again, -that author's Signed-off-by: line is mandatory, with the same spelling. - -There are various tooling options for automatically adding these tags -include using ``git commit -s`` or ``git format-patch -s``. For more -information see `SubmittingPatches 1.12 -`__. +this and happy for it to go into QEMU". For full guidance, read the +:ref:`code-provenance` documentation. + .. _include_a_meaningful_cover_letter: @@ -406,6 +414,20 @@ For more details on how QEMU's stable process works, refer to the .. _participating_in_code_review: +Retrieve an existing series +--------------------------- + +If you want to apply an existing series on top of your tree, you can simply use +`b4 `__. + +:: + + b4 shazam $msg-id + +The message id is related to the patch series that has been sent to the mailing +list. You need to retrieve the "Message-Id:" header from one of the patches. Any +of them can be used and b4 will apply the whole series. + Participating in Code Review ---------------------------- diff --git a/docs/devel/tcg-icount.rst b/docs/devel/tcg-icount.rst index 50c8e8dabc1..7df883446a7 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 index 8ae59ea02b7..f26b837a309 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -239,7 +239,7 @@ Jumps/Labels - | Jump to label. - * - brcond_i32/i64 *t0*, *t1*, *cond*, *label* + * - brcond *t0*, *t1*, *cond*, *label* - | Conditional jump if *t0* *cond* *t1* is true. *cond* can be: | @@ -253,104 +253,125 @@ Jumps/Labels | ``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* + * - add *t0*, *t1*, *t2* - | *t0* = *t1* + *t2* - * - sub_i32/i64 *t0*, *t1*, *t2* + * - sub *t0*, *t1*, *t2* - | *t0* = *t1* - *t2* - * - neg_i32/i64 *t0*, *t1* + * - neg *t0*, *t1* - | *t0* = -*t1* (two's complement) - * - mul_i32/i64 *t0*, *t1*, *t2* + * - mul *t0*, *t1*, *t2* - | *t0* = *t1* * *t2* - * - div_i32/i64 *t0*, *t1*, *t2* + * - divs *t0*, *t1*, *t2* - | *t0* = *t1* / *t2* (signed) | Undefined behavior if division by zero or overflow. - * - divu_i32/i64 *t0*, *t1*, *t2* + * - divu *t0*, *t1*, *t2* - | *t0* = *t1* / *t2* (unsigned) | Undefined behavior if division by zero. - * - rem_i32/i64 *t0*, *t1*, *t2* + * - rems *t0*, *t1*, *t2* - | *t0* = *t1* % *t2* (signed) | Undefined behavior if division by zero or overflow. - * - remu_i32/i64 *t0*, *t1*, *t2* + * - remu *t0*, *t1*, *t2* - | *t0* = *t1* % *t2* (unsigned) | Undefined behavior if division by zero. + * - divs2 *q*, *r*, *nl*, *nh*, *d* + + - | *q* = *nh:nl* / *d* (signed) + | *r* = *nh:nl* % *d* + | Undefined behaviour if division by zero, or the double-word + numerator divided by the single-word divisor does not fit + within the single-word quotient. The code generator will + pass *nh* as a simple sign-extension of *nl*, so the only + overflow should be *INT_MIN* / -1. + + * - divu2 *q*, *r*, *nl*, *nh*, *d* + + - | *q* = *nh:nl* / *d* (unsigned) + | *r* = *nh:nl* % *d* + | Undefined behaviour if division by zero, or the double-word + numerator divided by the single-word divisor does not fit + within the single-word quotient. The code generator will + pass 0 to *nh* to make a simple zero-extension of *nl*, + so overflow should never occur. Logical ------- .. list-table:: - * - and_i32/i64 *t0*, *t1*, *t2* + * - and *t0*, *t1*, *t2* - | *t0* = *t1* & *t2* - * - or_i32/i64 *t0*, *t1*, *t2* + * - or *t0*, *t1*, *t2* - | *t0* = *t1* | *t2* - * - xor_i32/i64 *t0*, *t1*, *t2* + * - xor *t0*, *t1*, *t2* - | *t0* = *t1* ^ *t2* - * - not_i32/i64 *t0*, *t1* + * - not *t0*, *t1* - | *t0* = ~\ *t1* - * - andc_i32/i64 *t0*, *t1*, *t2* + * - andc *t0*, *t1*, *t2* - | *t0* = *t1* & ~\ *t2* - * - eqv_i32/i64 *t0*, *t1*, *t2* + * - eqv *t0*, *t1*, *t2* - | *t0* = ~(*t1* ^ *t2*), or equivalently, *t0* = *t1* ^ ~\ *t2* - * - nand_i32/i64 *t0*, *t1*, *t2* + * - nand *t0*, *t1*, *t2* - | *t0* = ~(*t1* & *t2*) - * - nor_i32/i64 *t0*, *t1*, *t2* + * - nor *t0*, *t1*, *t2* - | *t0* = ~(*t1* | *t2*) - * - orc_i32/i64 *t0*, *t1*, *t2* + * - orc *t0*, *t1*, *t2* - | *t0* = *t1* | ~\ *t2* - * - clz_i32/i64 *t0*, *t1*, *t2* + * - clz *t0*, *t1*, *t2* - | *t0* = *t1* ? clz(*t1*) : *t2* - * - ctz_i32/i64 *t0*, *t1*, *t2* + * - ctz *t0*, *t1*, *t2* - | *t0* = *t1* ? ctz(*t1*) : *t2* - * - ctpop_i32/i64 *t0*, *t1* + * - ctpop *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``. + | The name *ctpop* is short for "count population", and matches + the function name used in ``include/qemu/host-utils.h``. Shifts/Rotates @@ -358,30 +379,30 @@ Shifts/Rotates .. list-table:: - * - shl_i32/i64 *t0*, *t1*, *t2* + * - shl *t0*, *t1*, *t2* - | *t0* = *t1* << *t2* - | Unspecified behavior if *t2* < 0 or *t2* >= 32 (resp 64) + | Unspecified behavior for negative or out-of-range shifts. - * - shr_i32/i64 *t0*, *t1*, *t2* + * - shr *t0*, *t1*, *t2* - | *t0* = *t1* >> *t2* (unsigned) - | Unspecified behavior if *t2* < 0 or *t2* >= 32 (resp 64) + | Unspecified behavior for negative or out-of-range shifts. - * - sar_i32/i64 *t0*, *t1*, *t2* + * - sar *t0*, *t1*, *t2* - | *t0* = *t1* >> *t2* (signed) - | Unspecified behavior if *t2* < 0 or *t2* >= 32 (resp 64) + | Unspecified behavior for negative or out-of-range shifts. - * - rotl_i32/i64 *t0*, *t1*, *t2* + * - rotl *t0*, *t1*, *t2* - | Rotation of *t2* bits to the left - | Unspecified behavior if *t2* < 0 or *t2* >= 32 (resp 64) + | Unspecified behavior for negative or out-of-range shifts. - * - rotr_i32/i64 *t0*, *t1*, *t2* + * - rotr *t0*, *t1*, *t2* - | Rotation of *t2* bits to the right. - | Unspecified behavior if *t2* < 0 or *t2* >= 32 (resp 64) + | Unspecified behavior for negative or out-of-range shifts. Misc @@ -389,26 +410,12 @@ Misc .. list-table:: - * - mov_i32/i64 *t0*, *t1* + * - mov *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* + | Move *t1* to *t0*. - 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* + * - bswap16 *t0*, *t1*, *flags* - | 16 bit byte swap on the low bits of a 32/64 bit input. | @@ -418,24 +425,24 @@ Misc | | 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 *t0*, *t1*, *flags* - * - bswap32_i32 *t0*, *t1*, *flags* + - | 32 bit byte swap. The flags are the same as for bswap16, except + they apply from bit 31 instead of bit 15. On TCG_TYPE_I32, the + flags should be zero. - bswap64_i64 *t0*, *t1*, *flags* + * - bswap64 *t0*, *t1*, *flags* - - | 32/64 bit byte swap. The flags are ignored, but still present - for consistency with the other bswap opcodes. + - | 64 bit byte swap. The flags are ignored, but still present + for consistency with the other bswap opcodes. For future + compatibility, the flags should be zero. * - 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 *dest*, *t1*, *t2*, *pos*, *len* - | Deposit *t2* as a bitfield into *t1*, placing the result in *dest*. | @@ -444,14 +451,16 @@ Misc | *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 + | For example, "deposit 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) + | + | on TCG_TYPE_I32. - * - extract_i32/i64 *dest*, *t1*, *pos*, *len* + * - extract *dest*, *t1*, *pos*, *len* - sextract_i32/i64 *dest*, *t1*, *pos*, *len* + sextract *dest*, *t1*, *pos*, *len* - | Extract a bitfield from *t1*, placing the result in *dest*. | @@ -460,16 +469,16 @@ Misc 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 + | For example, "sextract 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). + | (using an arithmetic right shift) on TCG_TYPE_I32. - * - extract2_i32/i64 *dest*, *t1*, *t2*, *pos* + * - extract2 *dest*, *t1*, *t2*, *pos* - - | For N = {32,64}, extract an N-bit quantity from the concatenation + - | For TCG_TYPE_I{N}, 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. @@ -492,19 +501,19 @@ Conditional moves .. list-table:: - * - setcond_i32/i64 *dest*, *t1*, *t2*, *cond* + * - setcond *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* + * - negsetcond *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* + * - movcond *dest*, *c1*, *c2*, *v1*, *v2*, *cond* - | *dest* = (*c1* *cond* *c2* ? *v1* : *v2*) | @@ -584,26 +593,79 @@ Multiword arithmetic support .. list-table:: - * - add2_i32/i64 *t0_low*, *t0_high*, *t1_low*, *t1_high*, *t2_low*, *t2_high* + * - addco *t0*, *t1*, *t2* + + - | Compute *t0* = *t1* + *t2* and in addition output to the + carry bit provided by the host architecture. + + * - addci *t0, *t1*, *t2* - sub2_i32/i64 *t0_low*, *t0_high*, *t1_low*, *t1_high*, *t2_low*, *t2_high* + - | Compute *t0* = *t1* + *t2* + *C*, where *C* is the + input carry bit provided by the host architecture. + The output carry bit need not be computed. - - | 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. + * - addcio *t0, *t1*, *t2* - * - mulu2_i32/i64 *t0_low*, *t0_high*, *t1*, *t2* + - | Compute *t0* = *t1* + *t2* + *C*, where *C* is the + input carry bit provided by the host architecture, + and also compute the output carry bit. + + * - addc1o *t0, *t1*, *t2* + + - | Compute *t0* = *t1* + *t2* + 1, and in addition output to the + carry bit provided by the host architecture. This is akin to + *addcio* with a fixed carry-in value of 1. + | This is intended to be used by the optimization pass, + intermediate to complete folding of the addition chain. + In some cases complete folding is not possible and this + opcode will remain until output. If this happens, the + code generator will use ``tcg_out_set_carry`` and then + the output routine for *addcio*. + + * - subbo *t0*, *t1*, *t2* + + - | Compute *t0* = *t1* - *t2* and in addition output to the + borrow bit provided by the host architecture. + | Depending on the host architecture, the carry bit may or may not be + identical to the borrow bit. Thus the addc\* and subb\* + opcodes must not be mixed. + + * - subbi *t0, *t1*, *t2* + + - | Compute *t0* = *t1* - *t2* - *B*, where *B* is the + input borrow bit provided by the host architecture. + The output borrow bit need not be computed. + + * - subbio *t0, *t1*, *t2* + + - | Compute *t0* = *t1* - *t2* - *B*, where *B* is the + input borrow bit provided by the host architecture, + and also compute the output borrow bit. + + * - subb1o *t0, *t1*, *t2* + + - | Compute *t0* = *t1* - *t2* - 1, and in addition output to the + borrow bit provided by the host architecture. This is akin to + *subbio* with a fixed borrow-in value of 1. + | This is intended to be used by the optimization pass, + intermediate to complete folding of the subtraction chain. + In some cases complete folding is not possible and this + opcode will remain until output. If this happens, the + code generator will use ``tcg_out_set_borrow`` and then + the output routine for *subbio*. + + * - mulu2 *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* + * - muls2 *t0_low*, *t0_high*, *t1*, *t2* - | Similar to mulu2, except the two inputs *t1* and *t2* are signed. - * - mulsh_i32/i64 *t0*, *t1*, *t2* + * - mulsh *t0*, *t1*, *t2* - muluh_i32/i64 *t0*, *t1*, *t2* + muluh *t0*, *t1*, *t2* - | Provide the high part of a signed or unsigned multiply, respectively. | @@ -682,8 +744,6 @@ QEMU specific operations 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, @@ -701,19 +761,14 @@ QEMU specific operations 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. +All of the vector ops have two parameters, ``TCGOP_TYPE`` & ``TCGOP_VECE``. +The former specifies the length of the vector as a TCGType; the latter +specifies the length of the element (if applicable) in log2 8-bit units. .. list-table:: @@ -727,7 +782,7 @@ E.g. VECL = 1 -> 64 << 1 -> v128, and VECE = 2 -> 1 << 2 -> i32. * - dup_vec *v0*, *r1* - - | Duplicate the low N bits of *r1* into VECL/VECE copies across *v0*. + - | Duplicate the low N bits of *r1* into TYPE/VECE copies across *v0*. * - dupi_vec *v0*, *c* @@ -736,7 +791,7 @@ E.g. VECL = 1 -> 64 << 1 -> v128, and VECE = 2 -> 1 << 2 -> i32. * - dup2_vec *v0*, *r1*, *r2* - - | Duplicate *r2*:*r1* into VECL/64 copies across *v0*. This opcode is + - | Duplicate *r2*:*r1* into TYPE/64 copies across *v0*. This opcode is only present for 32-bit hosts. * - add_vec *v0*, *v1*, *v2* @@ -808,7 +863,7 @@ E.g. VECL = 1 -> 64 << 1 -> v128, and VECE = 2 -> 1 << 2 -> i32. .. code-block:: c - for (i = 0; i < VECL/VECE; ++i) { + for (i = 0; i < TYPE/VECE; ++i) { v0[i] = v1[i] << s2; } @@ -830,7 +885,7 @@ E.g. VECL = 1 -> 64 << 1 -> v128, and VECE = 2 -> 1 << 2 -> i32. .. code-block:: c - for (i = 0; i < VECL/VECE; ++i) { + for (i = 0; i < TYPE/VECE; ++i) { v0[i] = v1[i] << v2[i]; } @@ -883,9 +938,9 @@ 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 32 bit target, all 64 bit operations are converted to 32 bits. +A few specific operations must be implemented to allow it +(see brcond2_i32, setcond2_i32). On a 64 bit target, the values are transferred between 32 and 64-bit registers using the following ops: @@ -926,7 +981,9 @@ 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. +used by each backend to indicate all registers. If ``TCG_REG_ZERO`` +is defined by the backend, the constraint '``z``' is defined generically +to map constant 0 to the hardware zero register. The movi_i32 and movi_i64 operations must accept any constants. diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst index 81dcd43a612..9463692c411 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -8,38 +8,6 @@ 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 --------------- @@ -93,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. @@ -112,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 --------- @@ -142,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/testing.rst b/docs/devel/testing.rst deleted file mode 100644 index 5d1fc0aa95f..00000000000 --- a/docs/devel/testing.rst +++ /dev/null @@ -1,1508 +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 - -``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. - - * 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. - -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 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.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() - -QEMU_TEST_FLAKY_TESTS -^^^^^^^^^^^^^^^^^^^^^ -Some tests are not working reliably and thus are disabled by default. -Set this environment variable to enable them. - -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 00000000000..9a4d716ebff --- /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/blkdebug.rst b/docs/devel/testing/blkdebug.rst new file mode 100644 index 00000000000..63887c9aa9c --- /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 00000000000..2a71778b5e3 --- /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-jobs.rst.inc b/docs/devel/testing/ci-jobs.rst.inc similarity index 85% rename from docs/devel/ci-jobs.rst.inc rename to docs/devel/testing/ci-jobs.rst.inc index 4c39cdb2d92..f1c541cc25b 100644 --- a/docs/devel/ci-jobs.rst.inc +++ b/docs/devel/testing/ci-jobs.rst.inc @@ -115,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. @@ -126,10 +126,10 @@ QEMU_JOB_PUBLISH The job is for publishing content after a branch has been merged into the upstream default branch. -QEMU_JOB_AVOCADO -~~~~~~~~~~~~~~~~ +QEMU_JOB_FUNCTIONAL +~~~~~~~~~~~~~~~~~~~ -The job runs the Avocado integration test suite +The job runs the functional test suite Contributor controlled runtime variables ---------------------------------------- @@ -147,15 +147,14 @@ 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 -~~~~~~~~~~~~~~~~~~~~~~~ -By default, tests using the Avocado framework are not run automatically in -the pipelines (because multiple artifacts have to be downloaded, and if -these artifacts are not already cached, downloading them make the jobs -reach the timeout limit). Set this variable to have the tests using the -Avocado framework run automatically. +QEMU_CI_FUNCTIONAL +~~~~~~~~~~~~~~~~~~ +By default, tests using the functional framework are not run automatically +in the pipelines (because multiple artifacts have to be downloaded, which +might cause a lot of network traffic). Set this variable to have the tests +using the functional framework run automatically. Other misc variables -------------------- @@ -182,13 +181,6 @@ 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 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 7817001fb28..67b23d37197 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/testing/ci.rst b/docs/devel/testing/ci.rst new file mode 100644 index 00000000000..e21d39db57a --- /dev/null +++ b/docs/devel/testing/ci.rst @@ -0,0 +1,34 @@ +.. _ci: + +Continuous Integration (CI) +=========================== + +Continuous integration (CI) requires the builds of the entire application and +the execution of a comprehensive set of automated tests every time there is a +need to commit any set of changes [1]_. The automated tests are composed +of unit, functional and other tests. + +Most of QEMU's CI is run on GitLab's infrastructure although a number +of other CI services are used for specialised purposes. The most up to +date information about them and their status can be found on the +`project wiki testing page `_. + +These tests are also used as gating tests before merging pull requests. +A gating test restricts the move of code from one stage to another on a +test/deployment pipeline. The step move is granted with approval. The approval +can be a manual intervention or a set of tests succeeding [2]_. + +On QEMU, the gating process happens during the pull request. The approval is +done by the project leader running its own set of tests. The pull request gets +merged when the tests succeed. + +.. include:: ci-jobs.rst.inc +.. include:: ci-runners.rst.inc + +References +---------- + +.. [1] Humble, Jez & Farley, David (2010). Continuous Delivery: + Reliable Software Releases Through Build, Test, and Deployment, p. 55. +.. [2] Humble, Jez & Farley, David (2010). Continuous Delivery: + Reliable Software Releases Through Build, Test, and Deployment, p. 122. diff --git a/docs/devel/testing/functional.rst b/docs/devel/testing/functional.rst new file mode 100644 index 00000000000..9e56dd1b118 --- /dev/null +++ b/docs/devel/testing/functional.rst @@ -0,0 +1,380 @@ +.. _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 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. + +Logging +------- + +The framework collects log files for each test in the build directory +in the following subfolder:: + + /tests/functional//../ + +There are usually three log files: + +* ``base.log`` contains the generic logging information that is written + by the calls to the logging functions in the test code (e.g. by calling + the ``self.log.info()`` or ``self.log.debug()`` functions). +* ``console.log`` contains the output of the serial console of the guest. +* ``default.log`` contains the output of QEMU. This file could be named + differently if the test chooses to use a different identifier for + the guest VM (e.g. when the test spins up multiple VMs). + +Introduction to writing tests +----------------------------- + +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`` instance 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 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 ``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:: + + from qemu_test import Asset + + ASSET_somename = Asset( + ('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. + +QEMU_TEST_ALLOW_SLOW +^^^^^^^^^^^^^^^^^^^^ +Tests that have a very long runtime and might run into timeout issues +e.g. if the QEMU binary has been compiled with debugging options enabled. +To avoid these timeout issues by default and to save some precious CPU +cycles during normal testing, such tests are disabled by default unless +the QEMU_TEST_ALLOW_SLOW environment variable has been set. + + +.. _unittest: https://docs.python.org/3/library/unittest.html diff --git a/docs/devel/fuzzing.rst b/docs/devel/testing/fuzzing.rst similarity index 97% rename from docs/devel/fuzzing.rst rename to docs/devel/testing/fuzzing.rst index 3bfcb33fc4b..c3ac084311b 100644 --- a/docs/devel/fuzzing.rst +++ b/docs/devel/testing/fuzzing.rst @@ -21,11 +21,12 @@ Building the fuzzers 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:: diff --git a/docs/devel/testing/index.rst b/docs/devel/testing/index.rst new file mode 100644 index 00000000000..ccc2fc6cbc1 --- /dev/null +++ b/docs/devel/testing/index.rst @@ -0,0 +1,17 @@ +Testing QEMU +------------ + +Details about how to test QEMU and how it is integrated into our CI +testing infrastructure. + +.. toctree:: + :maxdepth: 3 + + main + qtest + functional + 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 00000000000..6b18ed875cd --- /dev/null +++ b/docs/devel/testing/main.rst @@ -0,0 +1,1059 @@ +.. _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 functional 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 as an automated test into +the meson build system so can be run directly from the build tree, +for example:: + + [./pyvenv/bin/]meson test --suite qemu:softfloat + +will run just the softfloat tests. + +An automated test is written with one of the test frameworks using its +generic test functions/classes. The test framework can run the tests and +report their success or failure [1]_. + +An automated test has essentially three parts: + +1. The test initialization of the parameters, where the expected parameters, + like inputs and expected results, are set up; +2. The call to the code that should be tested; +3. An assertion, comparing the result from the previous call with the expected + result set during the initialization of the parameters. If the result + matches the expected result, the test has been successful; otherwise, it has + failed. + +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 +~~~~~~~~~~ + +A unit test is responsible for exercising individual software components as a +unit, like interfaces, data structures, and functionality, uncovering errors +within the boundaries of a component. The verification effort is in the +smallest software unit and focuses on the internal processing logic and data +structures. A test case of unit tests should be designed to uncover errors +due to erroneous computations, incorrect comparisons, or improper control +flow [2]_. + +In QEMU, unit tests can be invoked with ``make check-unit``. They 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-tests: + +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 +------------ + +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: + +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 +----------------------------- + +A functional test focuses on the functional requirement of the software, +attempting to find errors like incorrect functions, interface errors, +behavior errors, and initialization and termination errors [3]_. + +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. + +.. _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. + +Flaky tests +----------- + +A flaky test is defined as a test that exhibits both a passing and a failing +result with the same code on different runs. Some usual reasons for an +intermittent/flaky test are async wait, concurrency, and test order dependency +[4]_. + +In QEMU, tests that are identified to be flaky are normally disabled by +default. Set the QEMU_TEST_FLAKY_TESTS environment variable before running +the tests to enable them. + +References +---------- + +.. [1] Sommerville, Ian (2016). Software Engineering. p. 233. +.. [2] Pressman, Roger S. & Maxim, Bruce R. (2020). Software Engineering, + A Practitioner’s Approach. p. 48, 376, 378, 381. +.. [3] Pressman, Roger S. & Maxim, Bruce R. (2020). Software Engineering, + A Practitioner’s Approach. p. 388. +.. [4] Luo, Qingzhou, et al. An empirical analysis of flaky tests. + Proceedings of the 22nd ACM SIGSOFT International Symposium on + Foundations of Software Engineering. 2014. 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 0455aa06ab2..73ef7702b7b 100644 --- a/docs/devel/qtest.rst +++ b/docs/devel/testing/qtest.rst @@ -1,3 +1,5 @@ +.. _qtest: + ======================================== QTest Device Emulation Testing Framework ======================================== @@ -81,7 +83,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 d288480db11..043bed7fd0f 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/uefi-vars.rst b/docs/devel/uefi-vars.rst new file mode 100644 index 00000000000..0151a26a0a6 --- /dev/null +++ b/docs/devel/uefi-vars.rst @@ -0,0 +1,68 @@ +============== +UEFI variables +============== + +Guest UEFI variable management +============================== + +The traditional approach for UEFI Variable storage in qemu guests is +to work as close as possible to physical hardware. That means +providing pflash as storage and leaving the management of variables +and flash to the guest. + +Secure boot support comes with the requirement that the UEFI variable +storage must be protected against direct access by the OS. All update +requests must pass the sanity checks. (Parts of) the firmware must +run with a higher privilege level than the OS so this can be enforced +by the firmware. On x86 this has been implemented using System +Management Mode (SMM) in qemu and kvm, which again is the same +approach taken by physical hardware. Only privileged code running in +SMM mode is allowed to access flash storage. + +Communication with the firmware code running in SMM mode works by +serializing the requests to a shared buffer, then trapping into SMM +mode via SMI. The SMM code processes the request, stores the reply in +the same buffer and returns. + +Host UEFI variable service +========================== + +Instead of running the privileged code inside the guest we can run it +on the host. The serialization protocol can be reused. The +communication with the host uses a virtual device, which essentially +configures the shared buffer location and size, and traps to the host +to process the requests. + +The ``uefi-vars`` device implements the UEFI virtual device. It comes +in ``uefi-vars-x86`` and ``uefi-vars-sysbus`` flavours. The device +reimplements the handlers needed, specifically +``EfiSmmVariableProtocol`` and ``VarCheckPolicyLibMmiHandler``. It +also consumes events (``EfiEndOfDxeEventGroup``, +``EfiEventReadyToBoot`` and ``EfiEventExitBootServices``). + +The advantage of the approach is that we do not need a special +privilege level for the firmware to protect itself, i.e. it does not +depend on SMM emulation on x64, which allows the removal of a bunch of +complex code for SMM emulation from the linux kernel +(CONFIG_KVM_SMM=n). It also allows support for secure boot on arm +without implementing secure world (el3) emulation in kvm. + +Of course there are also downsides. The added device increases the +attack surface of the host, and we are adding some code duplication +because we have to reimplement some edk2 functionality in qemu. + +usage on x86_64 +--------------- + +.. code:: + + qemu-system-x86_64 \ + -device uefi-vars-x86,jsonfile=/path/to/vars.json + +usage on aarch64 +---------------- + +.. code:: + + qemu-system-aarch64 -M virt \ + -device uefi-vars-sysbus,jsonfile=/path/to/vars.json diff --git a/docs/devel/vfio-iommufd.rst b/docs/devel/vfio-iommufd.rst new file mode 100644 index 00000000000..3d1c11f175e --- /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 605fe60e969..00000000000 --- a/docs/devel/vfio-migration.rst +++ /dev/null @@ -1,208 +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 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/virtio-backends.rst b/docs/devel/virtio-backends.rst index 9ff092e7a04..ebddc3b9f51 100644 --- a/docs/devel/virtio-backends.rst +++ b/docs/devel/virtio-backends.rst @@ -101,13 +101,12 @@ manually instantiated: VirtIOBlock vdev; }; - static Property virtio_blk_pci_properties[] = { + static const Property virtio_blk_pci_properties[] = { DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -120,7 +119,7 @@ manually instantiated: qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } - static void virtio_blk_pci_class_init(ObjectClass *klass, void *data) + static void virtio_blk_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/docs/devel/virtio-migration.txt b/docs/devel/virtio-migration.txt deleted file mode 100644 index 98a6b0ffb57..00000000000 --- 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 2c11e716651..930da5cd068 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(const char *message, Error **errp) + void qmp_hello_world(const char *message, bool has_times, int64_t times, + Error **errp) { - if (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. @@ -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 - # - # QEMU alarm clock information. + # @OptionRomInfo: # - # @clock-name: The alarm clock method's name. + # @filename: option ROM image file name # - # @next-deadline: The time (in nanoseconds) the next alarm will fire. + # @bootindex: option ROM's bootindex # - # 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) - { - QemuAlarmClock *clock; - int64_t deadline; - - clock = g_malloc0(sizeof(*clock)); +It's time to implement the qmp_query_option_roms() function. Add to +monitor/qmp-cmds.c:: - deadline = qemu_next_alarm_deadline(); - if (deadline > 0) { - clock->has_next_deadline = true; - clock->next_deadline = deadline; + OptionRomInfoList *qmp_query_option_roms(Error **errp) + { + OptionRomInfoList *info_list = NULL; + OptionRomInfoList **tailp = &info_list; + OptionRomInfo *info; + + 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? Non-pointer optional - members have a 'has_TYPE_NAME' member that should be properly set +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 -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 -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); + 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); + } + monitor_printf(mon, "\n"); } - qapi_free_QemuAlarmClock(clock); + qapi_free_OptionRomInfoList(info_list); } -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, - }, +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 alarmclock" in the user monitor. +To test this, run QEMU and type "info option-roms" in the user monitor. -Returning Lists -~~~~~~~~~~~~~~~ +Writing a debugging aid returning unstructured text +--------------------------------------------------- -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. +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, such as +a query command that report aspects of QEMU's internal state that are +useful only to human operators. -This first step is to define a new type:: +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``:: ## - # @TimerAlarmMethod - # - # Timer alarm method information. + # @x-query-roms: # - # @method-name: The method's name. + # Query information on the registered ROMS # - # @current: true if this alarm method is currently in use, false otherwise + # Features: # - # 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 + # @unstable: This command is meant for debugging. # - # Returns information about available alarm methods. + # Returns: registered ROMs # - # Returns: a list of @TimerAlarmMethod for each method - # - # Since: 1.0 + # Since: 6.2 ## - { '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" - } - ] - } - -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; - } - - 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); - } - -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. - -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' } } - { '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/glossary.rst b/docs/glossary.rst new file mode 100644 index 00000000000..4fa044bfb6e --- /dev/null +++ b/docs/glossary.rst @@ -0,0 +1,280 @@ +.. _Glossary: + +-------- +Glossary +-------- + +This section of the manual presents brief definitions of acronyms and terms used +by QEMU developers. + +Accelerator +----------- + +A specific API used to accelerate execution of guest instructions. It can be +hardware-based, through a virtualization API provided by the host OS (kvm, hvf, +whpx, ...), or software-based (tcg). See this description of `supported +accelerators`. + +Board +----- + +Another name for :ref:`machine`. + +Block +----- + +Block drivers are the available `disk formats and front-ends +` available, and block devices `(see Block device section on +options page)` are using them to implement disks for a +virtual machine. + +CFI +--- + +Control Flow Integrity is a hardening technique used to prevent exploits +targeting QEMU by detecting unexpected branches during execution. QEMU `actively +supports` being compiled with CFI enabled. + +Device +------ + +In QEMU, a device is a piece of hardware visible to the guest. Examples include +UARTs, PCI controllers, PCI cards, VGA controllers, and many more. + +QEMU is able to emulate a CPU, and all the hardware interacting with it, +including `many devices`. When QEMU runs a virtual machine +using a hardware-based accelerator, it is responsible for emulating, using +software, all devices. + +EDK2 +---- + +EDK2, as known as `TianoCore `_, is an open source +implementation of UEFI standard. QEMU virtual machines that boot a UEFI firmware +usually use EDK2. + +gdbstub +------- + +QEMU implements a `gdb server `, allowing gdb to attach to it and +debug a running virtual machine, or a program in user-mode. This allows +debugging the guest code that is running inside QEMU. + +glib2 +----- + +`GLib2 `_ is one of the most important libraries we +are using through the codebase. It provides many data structures, macros, string +and thread utilities and portable functions across different OS. It's required +to build QEMU. + +Guest agent +----------- + +The `QEMU Guest Agent ` is a daemon intended to be run within virtual +machines. It provides various services to help QEMU to interact with it. + +.. _guest: + +Guest +----- + +Guest is the architecture of the virtual machine, which is emulated. +See also :ref:`host`. + +Sometimes this is called the :ref:`target` architecture, but that term +can be ambiguous. + +.. _host: + +Host +---- + +Host is the architecture on which QEMU is running on, which is native. +See also :ref:`guest`. + +Hypervisor +---------- + +The formal definition of an hypervisor is a program or API than can be used to +manage a virtual machine. QEMU is a virtualizer, that interacts with various +hypervisors. + +In the context of QEMU, an hypervisor is an API, provided by the Host OS, +allowing to execute virtual machines. Linux implementation is KVM (and supports +Xen as well). For MacOS, it's HVF. Windows defines WHPX. And NetBSD provides +NVMM. + +.. _machine: + +Machine +------- + +QEMU's system emulation models many different types of hardware. A machine model +(sometimes called a board model) is the model of a complete virtual system with +RAM, one or more CPUs, and various devices. It can be selected with the option +``-machine`` of qemu-system. Our machine models can be found on this `page +`. + +Migration +--------- + +QEMU can save and restore the execution of a virtual machine between different +host systems. This is provided by the :ref:`Migration framework`. + +NBD +--- + +The `QEMU Network Block Device server ` is a tool that can be used to +mount and access QEMU images, providing functionality similar to a loop device. + +Mailing List +------------ + +This is `where `_ all the +development happens! Changes are posted as series, that all developers can +review and share feedback for. + +For reporting issues, our `GitLab +`_ tracker is the best place. + +.. _softmmu: + +MMU / softmmu +------------- + +The Memory Management Unit is responsible for translating virtual addresses to +physical addresses and managing memory protection. QEMU system mode is named +"softmmu" precisely because it implements this in software, including a TLB +(Translation lookaside buffer), for the guest virtual machine. + +QEMU user-mode does not implement a full software MMU, but "simply" translates +virtual addresses by adding a specific offset, and relying on host MMU/OS +instead. + +Monitor / QMP / HMP +------------------- + +The `QEMU Monitor ` is a text interface which can be used to interact +with a running virtual machine. + +QMP stands for QEMU Monitor Protocol and is a json based interface. +HMP stands for Human Monitor Protocol and is a set of text commands available +for users who prefer natural language to json. + +MTTCG +----- + +Multiple CPU support was first implemented using a round-robin algorithm +running on a single thread. Later on, `Multi-threaded TCG ` was developed +to benefit from multiple cores to speed up execution. + +Plugins +------- + +`TCG Plugins ` is an API used to instrument guest code, in system +and user mode. The end goal is to have a similar set of functionality compared +to `DynamoRIO `_ or `valgrind `_. + +One key advantage of QEMU plugins is that they can be used to perform +architecture agnostic instrumentation. + +Patchew +------- + +`Patchew `_ is a website that tracks patches on the +Mailing List. + +PR +-- + +Once a series is reviewed and accepted by a subsystem maintainer, it will be +included in a PR (Pull Request) that the project maintainer will merge into QEMU +main branch, after running tests. + +The QEMU project doesn't currently expect most developers to directly submit +pull requests. + +QCOW2 +----- + +QEMU Copy On Write is a disk format developed by QEMU. It provides transparent +compression, automatic extension, and many other advantages over a raw image. + +qcow2 is the recommended format to use. + +QEMU +---- + +`QEMU (Quick Emulator) `_ is a generic and open source +machine emulator and virtualizer. + +QOM +--- + +:ref:`QEMU Object Model ` is an object oriented API used to define +various devices and hardware in the QEMU codebase. + +Record/replay +------------- + +:ref:`Record/replay ` is a feature of QEMU allowing to have a +deterministic and reproducible execution of a virtual machine. + +Rust +---- + +`A new programming language `_, memory safe by +default. There is a work in progress to integrate it in QEMU codebase for +various subsystems. + +System mode +----------- + +QEMU System mode 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 or Hypervisor.Framework to +allow the guest to run directly on the host CPU. + +QEMU System mode is called :ref:`softmmu ` as well. + +.. _target: + +Target +------ + +The term "target" can be ambiguous. In most places in QEMU it is used as a +synonym for :ref:`guest`. For example the code for emulating Arm CPUs is in +``target/arm/``. However in the :ref:`TCG subsystem ` "target" refers to the +architecture which QEMU is running on, i.e. the :ref:`host`. + +TCG +--- + +TCG is the QEMU `Tiny Code Generator `. It is the JIT (just-in-time) +compiler we use to emulate a guest CPU in software. + +It is one of the accelerators supported by QEMU, and supports a lot of +guest/host architectures. + +User mode +--------- + +QEMU User mode can launch processes compiled for one CPU on another CPU. In this +mode the CPU is always emulated. In this mode, QEMU translate system calls from +guest to host kernel. It is available for Linux and BSD. + +VirtIO +------ + +VirtIO is an open standard used to define and implement virtual devices with a +minimal overhead, defining a set of data structures and hypercalls (similar to +system calls, but targeting an hypervisor, which happens to be QEMU in our +case). It's designed to be more efficient than emulating a real device, by +minimizing the amount of interactions between a guest VM and its hypervisor. + +vhost-user +---------- + +`Vhost-user ` is an interface used to implement VirtIO devices +outside of QEMU itself. diff --git a/docs/igd-assign.txt b/docs/igd-assign.txt index e17bb50789a..af4e8391fc2 100644 --- a/docs/igd-assign.txt +++ b/docs/igd-assign.txt @@ -1,44 +1,70 @@ Intel Graphics Device (IGD) assignment with vfio-pci ==================================================== -IGD has two different modes for assignment using vfio-pci: - -1) Universal Pass-Through (UPT) mode: - - In this mode the IGD device is added as a *secondary* (ie. non-primary) - graphics device in combination with an emulated primary graphics device. - This mode *requires* guest driver support to remove the external - dependencies generally associated with IGD (see below). Those guest - drivers only support this mode for Broadwell and newer IGD, according to - Intel. Additionally, this mode by default, and as officially supported - by Intel, does not support direct video output. The intention is to use - this mode either to provide hardware acceleration to the emulated graphics - or to use this mode in combination with guest-based remote access software, - for example VNC (see below for optional output support). This mode - theoretically has no device specific handling dependencies on vfio-pci or - the VM firmware. - -2) "Legacy" mode: - - In this mode the IGD device is intended to be the primary and exclusive - graphics device in the VM[1], as such QEMU does not facilitate any sort - of remote graphics to the VM in this mode. A connected physical monitor - is the intended output device for IGD. This mode includes several - requirements and restrictions: - - * IGD must be given address 02.0 on the PCI root bus in the VM - * The host kernel must support vfio extensions for IGD (v4.6) - * vfio VGA support very likely needs to be enabled in the host kernel - * The VM firmware must support specific fw_cfg enablers for IGD - * The VM machine type must support a PCI host bridge at 00.0 (standard) - * The VM machine type must provide or allow to be created a special - ISA/LPC bridge device (vfio-pci-igd-lpc-bridge) on the root bus at - PCI address 1f.0. - * The IGD device must have a VGA ROM, either provided via the romfile - option or loaded automatically through vfio (standard). rombar=0 - will disable legacy mode support. - * Hotplug of the IGD device is not supported. - * The IGD device must be a SandyBridge or newer model device. +Using vfio-pci, we can passthrough Intel Graphics Device (IGD) to guest, either +serve as primary and exclusive graphics adapter, or used in combination with an +emulated primary graphics device, depending on the config and guest driver +support. However, IGD devices are not "clean" PCI devices, they use extra +memory regions other than BARs. Special handling is required to make them work +properly, including: + +* OpRegion for accessing Virtual BIOS Table (VBT) that contains display output + information. +* Data Stolen Memory (DSM) region used as VRAM at early stage (BIOS/UEFI) + +Certain guest software also depends on following conditions to work: +(*-Required by) + +| Condition | Linux | Windows | VBIOS | EFI GOP | +|---------------------------------------------|-------|---------|-------|---------| +| #1 IGD has a valid OpRegion containing VBT | * ^1 | * | * | * | +| #2 VID/DID of LPC bridge at 00:1f.0 matches | | | * | * | +| #3 IGD is assigned to BDF 00:02.0 | | | * | * | +| #4 IGD has VGA controller device class | | | * | * | +| #5 Host's VGA ranges are mapped to IGD | | | * | | +| #6 Guest has valid VBIOS or UEFI Option ROM | | | * | * | + +^1 Though i915 driver is able to mock a OpRegion, it is still recommended to + use the VBT copied from host OpRegion to prevent incorrect configuration. + +For #1, the "x-igd-opregion=on" option exposes a copy of host IGD OpRegion to +guest via fw_cfg, where guest firmware can set up guest OpRegion with it. + +For #2, "x-igd-lpc=on" option copies the IDs of host LPC bridge and host bridge +to guest. Currently this is only supported on i440fx machines as there is +already an ICH9 LPC bridge present on q35 machines, overwriting its IDs may +lead to unexpected behavior. + +For #3, "addr=2.0" assigns IGD to 00:02.0. + +For #4, the primary display must be set to IGD in host BIOS. + +For #5, "x-vga=on" enables guest access to standard VGA IO/MMIO ranges. + +For #6, ROM either provided via the ROM BAR or romfile= option is needed, this +Intel document [1] shows how to dump VBIOS to file. For UEFI Option ROM, see +"Guest firmware" section. + +QEMU also provides a "Legacy" mode that implicitly enables full functionality +on IGD, it is automatically enabled when +* IGD generation is 6 to 9 (Sandy Bridge to Comet Lake) +* Machine type is i440fx +* IGD is assigned to guest BDF 00:02.0 +* ROM BAR or romfile is present + +In "Legacy" mode, QEMU will automatically setup OpRegion, LPC bridge IDs and +VGA range access, which is equivalent to: + x-igd-opregion=on,x-igd-lpc=on,x-vga=on + +By default, "Legacy" mode won't fail, it continues on error. User can set +"x-igd-legacy-mode=on" to force enabling legacy mode, this also checks if the +conditions above for legacy mode is met, and if any error occurs, QEMU will +fail immediately. Users can also set "x-igd-legacy-mode=off" to disable legacy +mode. + +In legacy mode, as the guest VGA ranges are assigned to IGD device, all other +graphics devices should be removed, this can be done using "-nographic" or +"-vga none" or "-nodefaults", along with adding the device using vfio-pci. For either mode, depending on the host kernel, the i915 driver in the host may generate faults and errors upon re-binding to an IGD device after it @@ -73,31 +99,39 @@ DVI, or DisplayPort) may be unsupported in some use cases. In the author's experience, even DP to VGA adapters can be troublesome while adapters between digital formats work well. -Usage -===== -The intention is for IGD assignment to be transparent for users and thus for -management tools like libvirt. To make use of legacy mode, simply remove all -other graphics options and use "-nographic" and either "-vga none" or -"-nodefaults", along with adding the device using vfio-pci: - -device vfio-pci,host=00:02.0,id=hostdev0,bus=pci.0,addr=0x2 +Options +======= +* x-igd-opregion=[*on*|off] + Copy host IGD OpRegion and expose it to guest with fw_cfg + +* x-igd-lpc=[on|*off*] + Creates a dummy LPC bridge at 00:1f:0 with host VID/DID (i440fx only) + +* x-igd-legacy-mode=[on|off|*auto*] + Enable/Disable legacy mode + +* x-igd-gms=[hex, default 0] + Overriding DSM region size in GGC register, 0 means uses host value. + Use this only when the DSM size cannot be changed through the + 'DVMT Pre-Allocated' option in host BIOS. + -For UPT mode, retain the default emulated graphics and simply add the vfio-pci -device making use of any other bus address other than 02.0. libvirt will -default to assigning the device a UPT compatible address while legacy mode -users will need to manually edit the XML if using a tool like virt-manager -where the VM device address is not expressly specified. +Examples +======== +* Adding IGD with automatically legacy mode support + -device vfio-pci,host=00:02.0,id=hostdev0,addr=2.0 -An experimental vfio-pci option also exists to enable OpRegion, and thus -external monitor support, for UPT mode. This can be enabled by adding -"x-igd-opregion=on" to the vfio-pci device options for the IGD device. As -with legacy mode, this requires the host to support features introduced in -the v4.6 kernel. If Intel chooses to embrace this support, the option may -be made non-experimental in the future, opening it to libvirt support. +* Adding IGD with OpRegion and LPC ID hack, but without VGA ranges + (For UEFI guests) + -device vfio-pci,host=00:02.0,id=hostdev0,addr=2.0,x-igd-legacy-mode=off,x-igd-lpc=on,romfile=efi_oprom.rom -Developer ABI -============= -Legacy mode IGD support imposes two fw_cfg requirements on the VM firmware: + +Guest firmware +============== +Guest firmware is responsible for setting up OpRegion and Base of Data Stolen +Memory (BDSM) in guest address space. IGD passthrough support imposes two +fw_cfg requirements on the VM firmware: 1) "etc/igd-opregion" @@ -117,17 +151,117 @@ Legacy mode IGD support imposes two fw_cfg requirements on the VM firmware: Firmware must allocate a reserved memory below 4GB with required 1MB alignment equal to this size. Additionally the base address of this reserved region must be written to the dword BDSM register in PCI config - space of the IGD device at offset 0x5C. As this support is related to - running the IGD ROM, which has other dependencies on the device appearing - at guest address 00:02.0, it's expected that this fw_cfg file is only - relevant to a single PCI class VGA device with Intel vendor ID, appearing - at PCI bus address 00:02.0. + space of the IGD device at offset 0x5C (or 0xC0 for Gen 11+ devices using + 64-bit BDSM). As this support is related to running the IGD ROM, which + has other dependencies on the device appearing at guest address 00:02.0, + it's expected that this fw_cfg file is only relevant to a single PCI + class VGA device with Intel vendor ID, appearing at PCI bus address 00:02.0. + + Starting from Meteor Lake, IGD devices access stolen memory via its MMIO + BAR2 (LMEMBAR) and removed the BDSM register in config space. There is + no need for guest firmware to allocate data stolen memory in guest address + space and write it to BDSM register. Value of this fw_cfg file is 0 in + such case. + +Upstream Seabios has OpRegion and BDSM (pre-Gen11 device only) support. +However, the support is not accepted by upstream EDK2/OVMF. A recommended +solution is to create a virtual OpRom with following DXE drivers: + +* IgdAssignmentDxe: Set up OpRegion and BDSM according to fw_cfg (must) +* IntelGopDriver: Closed-source Intel GOP driver +* PlatformGopPolicy: Protocol required by IntelGopDriver + +IntelGopDriver and PlatformGopPolicy is only required when enabling GOP on IGD. + +The original IgdAssignmentDxe can be found at [3]. A Intel maintained version +with PlatformGopPolicy for industrial computing is at [4]. There is also an +unofficially maintained version with newer Gen11+ device support at [5]. +You need to build them with EDK2. + +For the IntelGopDriver, Intel never released it to public. You may contact +Intel support to get one as [4] said, if you are an Intel Premier Support +customer, or you can try extracting it from your host firmware using +"UEFI BIOS Updater"[6]. + +Once you got all the required DXE drivers, a Option ROM can be generated with +EfiRom utility in EDK2, using + EfiRom -f 0x8086 -i -o output.rom \ + -e IgdAssignmentDxe.efi PlatformGOPPolicy.efi IntelGopDriver.efi + + +Known issues +============ +When using OVMF as guest firmware, you may encounter the following warning: +warning: vfio_container_dma_map(0x55fab36ce610, 0x380010000000, 0x108000, 0x7fd336000000) = -22 (Invalid argument) + +Solution: +Set the host physical address bits to IOMMU address width using + -cpu host,host-phys-bits-limit= +Or in libvirt XML with + + + +The IOMMU address width can be determined with + echo $(( ((0x$(cat /sys/devices/virtual/iommu/dmar0/intel-iommu/cap) & 0x3F0000) >> 16) + 1 )) +Refer https://edk2.groups.io/g/devel/topic/patch_v1/102359124 for more details + + +Memory View +=========== +IGD has it own address space. To use system RAM as VRAM, a single-level page +table named Global Graphics Translation Table (GTT) is used for the address +translation. Each page table entry points a 4KB page. Illustration below shows +the translation flow on IGD with 64-bit GTT PTEs. + +(PTE_SIZE == 8) +-------------+---+ + | Address | V | V: Valid Bit + +-------------+---+ + | ... | | +IGD:0x01ae9010 0xd740| 0x70ffc000 | 1 | Mem:0x42ba3e010^ +-----------------------> 0xd748| 0x42ba3e000 | 1 +------------------> +(addr >> 12) * PTE_SIZE 0xd750| 0x42ba3f000 | 1 | + | ... | | + +-------------+---+ +^ The address may be remapped by IOMMU + +The memory region store GTT is called GTT Stolen Memory (GSM) it is located +right below the Data Stolen Memory (DSM). Accessing this region directly is +not allowed, any access will immediately freeze the whole system. The only way +to access it is through the second half of MMIO BAR0. + +The Data Stolen Memory is reserved by firmware, and acts as the VRAM in pre-OS +environments. In QEMU, guest firmware (Seabios/OVMF) is responsible for +reserving a continuous region and program its base address to BDSM register, +then let VBIOS/GOP driver initializing this region. Illustration below shows +how DSM is mapped. + + IGD Addr Space Host Addr Space Guest Addr Space + +-------------+ +-------------+ +-------------+ + | | | | | | + | | | | | | + | | +-------------+ +-------------+ + | | | Data Stolen | | Data Stolen | + | | | (Guest) | | (Guest) | + | | +------------>+-------------+<------->+-------------+<--Guest BDSM + | | | Passthrough | | EPT | | Emulated by QEMU +DSMSIZE+-------------+ | with IOMMU | | Mapping | | Programmed by guest FW + | | | | | | | + | | | | | | | + 0+-------------+--+ | | | | + | +-------------+ | | + | | Data Stolen | +-------------+ + | | (Host) | + +------------>+-------------+<--Host BDSM + Non- | | "real" one in HW + Passthrough | | Programmed by host FW + +-------------+ Footnotes ========= -[1] Nothing precludes adding additional emulated or assigned graphics devices - as non-primary, other than the combination typically not working. I only - intend to set user expectations, others are welcome to find working - combinations or fix whatever issues prevent this from working in the common - case. +[1] https://www.intel.com/content/www/us/en/docs/graphics-for-linux/developer-reference/1-0/dump-video-bios.html [2] # echo "vfio-pci" > /sys/bus/pci/devices/0000:00:02.0/driver_override +[3] https://web.archive.org/web/20240827012422/https://bugzilla.tianocore.org/show_bug.cgi?id=935 + Tianocore bugzilla was down since Jan 2025 :( +[4] https://eci.intel.com/docs/3.3/components/kvm-hypervisor.html, Patch 0001-0004 +[5] https://github.com/tomitamoeko/VfioIgdPkg +[6] https://winraid.level1techs.com/t/tool-guide-news-uefi-bios-updater-ubu/30357 diff --git a/docs/index.rst b/docs/index.rst index 0b9ee9901d9..5665de85cab 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,6 +3,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +.. _documentation-root: + ================================ Welcome to QEMU's documentation! ================================ @@ -18,3 +20,4 @@ Welcome to QEMU's documentation! interop/index specs/index devel/index + glossary diff --git a/docs/interop/bitmaps.rst b/docs/interop/bitmaps.rst index 1de46febdc5..7536f0ba5c3 100644 --- a/docs/interop/bitmaps.rst +++ b/docs/interop/bitmaps.rst @@ -97,7 +97,7 @@ time. - Persistent storage formats may impose their own requirements on bitmap names and namespaces. Presently, only qcow2 supports persistent bitmaps. See - docs/interop/qcow2.txt for more details on restrictions. Notably: + :doc:`qcow2` for more details on restrictions. Notably: - qcow2 bitmap names are limited to between 1 and 1023 bytes long. @@ -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 cc8f8691868..745d21d8223 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' ] } } ## @@ -191,15 +214,39 @@ # PL011 UART. @verbose-static is mutually exclusive # with @verbose-dynamic. # +# @host-uefi-vars: The firmware expects the host to provide an uefi +# variable store. qemu supports that via +# "uefi-vars-sysbus" (aarch64, riscv64, loongarch64) +# or "uefi-vars-x64" (x86_64) devices. The firmware +# will not use flash for nvram. When loading the +# firmware into flash the 'stateless' setup should be +# used. It is recommened to load the firmware into +# memory though. +# # Since: 3.0 ## { 'enum' : 'FirmwareFeature', 'data' : [ 'acpi-s3', 'acpi-s4', 'amd-sev', 'amd-sev-es', 'amd-sev-snp', 'intel-tdx', - 'enrolled-keys', 'requires-smm', 'secure-boot', + 'enrolled-keys', 'requires-smm', + 'secure-boot', 'host-uefi-vars', '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 +266,11 @@ ## { 'struct' : 'FirmwareFlashFile', 'data' : { 'filename' : 'str', - 'format' : 'BlockdevDriver' } } + 'format' : 'FirmwareFormat' } } ## -# @FirmwareFlashType: +# @FirmwareFlashMode: # # Describes how the firmware build handles code versus variable # persistence. @@ -433,205 +480,205 @@ # # Since: 3.0 # -# Examples: -# -# { -# "description": "SeaBIOS", -# "interface-types": [ -# "bios" -# ], -# "mapping": { -# "device": "memory", -# "filename": "/usr/share/seabios/bios-256k.bin" -# }, -# "targets": [ -# { -# "architecture": "i386", -# "machines": [ -# "pc-i440fx-*", -# "pc-q35-*" -# ] +# .. qmp-example:: +# +# { +# "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" -# ] -# } -# -# { -# "description": "OVMF with SB+SMM, empty varstore", -# "interface-types": [ -# "uefi" -# ], -# "mapping": { -# "device": "flash", -# "executable": { -# "filename": "/usr/share/OVMF/OVMF_CODE.secboot.fd", -# "format": "raw" +# "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" +# }, +# "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" -# ] -# } -# -# { -# "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" +# "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" +# }, +# "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" -# ] -# } -# -# { -# "description": "OVMF with SEV-ES support", -# "interface-types": [ -# "uefi" -# ], -# "mapping": { -# "device": "flash", -# "executable": { -# "filename": "/usr/share/OVMF/OVMF_CODE.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" +# ] +# } +# +# { +# "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" -# ] -# } -# -# { -# "description": "UEFI firmware for ARM64 virtual machines", -# "interface-types": [ -# "uefi" -# ], -# "mapping": { -# "device": "flash", -# "executable": { -# "filename": "/usr/share/AAVMF/AAVMF_CODE.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" +# ] +# } +# +# { +# "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": [ -# -# ], -# "tags": [ -# "-a AARCH64", -# "-p ArmVirtPkg/ArmVirtQemu.dsc", -# "-t GCC48", -# "-b DEBUG", -# "-D DEBUG_PRINT_ERROR_LEVEL=0x80000000" -# ] -# } +# "targets": [ +# { +# "architecture": "aarch64", +# "machines": [ +# "virt-*" +# ] +# } +# ], +# "features": [ +# +# ], +# "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 ed65395bfb2..d830c5c4104 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -14,12 +14,18 @@ are useful for making QEMU interoperate with other software. dbus-vmstate dbus-display live-block-operations + nbd + parallels + prl-xml + qcow2 + qed_spec pr-helper qmp-spec qemu-ga qemu-ga-ref qemu-qmp-ref qemu-storage-daemon-qmp-ref + vfio-user vhost-user vhost-user-gpu vhost-vdpa diff --git a/docs/interop/live-block-operations.rst b/docs/interop/live-block-operations.rst index 691429c7afe..6b549ede7c9 100644 --- a/docs/interop/live-block-operations.rst +++ b/docs/interop/live-block-operations.rst @@ -931,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 00000000000..de079d31fd8 --- /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 f5ca25174a6..00000000000 --- 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 00000000000..7b328a40c80 --- /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 bb3fadf3692..00000000000 --- 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 00000000000..5bb63bb93a4 --- /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 7031f8752cc..00000000000 --- 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.rst b/docs/interop/qcow2.rst new file mode 100644 index 00000000000..59485911073 --- /dev/null +++ b/docs/interop/qcow2.rst @@ -0,0 +1,937 @@ +======================= +Qcow2 Image File Format +======================= + +A ``qcow2`` image file is organized in units of constant size, which are called +(host) clusters. A cluster is the unit in which all allocations are done, +both for actual guest data and for image metadata. + +Likewise, the virtual disk as seen by the guest is divided into (guest) +clusters of the same size. + +All numbers in qcow2 are stored in Big Endian byte order. + +Header +------ + +The first cluster of a qcow2 image contains the file header:: + + Byte 0 - 3: magic + QCOW magic string ("QFI\xfb") + + 4 - 7: version + Version number (valid values are 2 and 3) + + 8 - 15: backing_file_offset + Offset into the image file at which the backing file name + is stored (NB: The string is not null terminated). 0 if the + image doesn't have a backing file. + + Note: backing files are incompatible with raw external data + files (auto-clear feature bit 1). + + 16 - 19: backing_file_size + Length of the backing file name in bytes. Must not be + longer than 1023 bytes. Undefined if the image doesn't have + a backing file. + + 20 - 23: cluster_bits + Number of bits that are used for addressing an offset + within a cluster (1 << cluster_bits is the cluster size). + Must not be less than 9 (i.e. 512 byte clusters). + + Note: QEMU as of today has an implementation limit of 2 MB + as the maximum cluster size and won't be able to open images + with larger cluster sizes. + + Note: if the image has Extended L2 Entries then cluster_bits + must be at least 14 (i.e. 16384 byte clusters). + + 24 - 31: size + Virtual disk size in bytes. + + Note: QEMU has an implementation limit of 32 MB as + the maximum L1 table size. With a 2 MB cluster + size, it is unable to populate a virtual cluster + beyond 2 EB (61 bits); with a 512 byte cluster + size, it is unable to populate a virtual size + larger than 128 GB (37 bits). Meanwhile, L1/L2 + table layouts limit an image to no more than 64 PB + (56 bits) of populated clusters, and an image may + hit other limits first (such as a file system's + maximum size). + + 32 - 35: crypt_method + 0 for no encryption + 1 for AES encryption + 2 for LUKS encryption + + 36 - 39: l1_size + Number of entries in the active L1 table + + 40 - 47: l1_table_offset + Offset into the image file at which the active L1 table + starts. Must be aligned to a cluster boundary. + + 48 - 55: refcount_table_offset + Offset into the image file at which the refcount table + starts. Must be aligned to a cluster boundary. + + 56 - 59: refcount_table_clusters + Number of clusters that the refcount table occupies + + 60 - 63: nb_snapshots + Number of snapshots contained in the image + + 64 - 71: snapshots_offset + Offset into the image file at which the snapshot table + starts. Must be aligned to a cluster boundary. + +For version 2, the header is exactly 72 bytes in length, and finishes here. +For version 3 or higher, the header length is at least 104 bytes, including +the next fields through ``header_length``. +:: + + 72 - 79: incompatible_features + Bitmask of incompatible features. An implementation must + fail to open an image if an unknown bit is set. + + Bit 0: Dirty bit. If this bit is set then refcounts + may be inconsistent, make sure to scan L1/L2 + tables to repair refcounts before accessing the + image. + + Bit 1: Corrupt bit. If this bit is set then any data + structure may be corrupt and the image must not + be written to (unless for regaining + consistency). + + Bit 2: External data file bit. If this bit is set, an + external data file is used. Guest clusters are + then stored in the external data file. For such + images, clusters in the external data file are + not refcounted. The offset field in the + Standard Cluster Descriptor must match the + guest offset and neither compressed clusters + nor internal snapshots are supported. + + An External Data File Name header extension may + be present if this bit is set. + + Bit 3: Compression type bit. If this bit is set, + a non-default compression is used for compressed + clusters. The compression_type field must be + present and not zero. + + Bit 4: Extended L2 Entries. If this bit is set then + L2 table entries use an extended format that + allows subcluster-based allocation. See the + Extended L2 Entries section for more details. + + Bits 5-63: Reserved (set to 0) + + 80 - 87: compatible_features + Bitmask of compatible features. An implementation can + safely ignore any unknown bits that are set. + + Bit 0: Lazy refcounts bit. If this bit is set then + lazy refcount updates can be used. This means + marking the image file dirty and postponing + refcount metadata updates. + + Bits 1-63: Reserved (set to 0) + + 88 - 95: autoclear_features + Bitmask of auto-clear features. An implementation may only + write to an image with unknown auto-clear features if it + clears the respective bits from this field first. + + Bit 0: Bitmaps extension bit + This bit indicates consistency for the bitmaps + extension data. + + It is an error if this bit is set without the + bitmaps extension present. + + If the bitmaps extension is present but this + bit is unset, the bitmaps extension data must be + considered inconsistent. + + Bit 1: Raw external data bit + If this bit is set, the external data file can + be read as a consistent standalone raw image + without looking at the qcow2 metadata. + + Setting this bit has a performance impact for + some operations on the image (e.g. writing + zeros requires writing to the data file instead + of only setting the zero flag in the L2 table + entry) and conflicts with backing files. + + This bit may only be set if the External Data + File bit (incompatible feature bit 1) is also + set. + + Bits 2-63: Reserved (set to 0) + + 96 - 99: refcount_order + Describes the width of a reference count block entry (width + in bits: refcount_bits = 1 << refcount_order). For version 2 + images, the order is always assumed to be 4 + (i.e. refcount_bits = 16). + This value may not exceed 6 (i.e. refcount_bits = 64). + + 100 - 103: header_length + Length of the header structure in bytes. For version 2 + images, the length is always assumed to be 72 bytes. + For version 3 it's at least 104 bytes and must be a multiple + of 8. + + +Additional fields (version 3 and higher) +---------------------------------------- + +In general, these fields are optional and may be safely ignored by the software, +as well as filled by zeros (which is equal to field absence), if software needs +to set field B, but does not care about field A which precedes B. More +formally, additional fields have the following compatibility rules: + +1. If the value of the additional field must not be ignored for correct + handling of the file, it will be accompanied by a corresponding incompatible + feature bit. + +2. If there are no unrecognized incompatible feature bits set, an unknown + additional field may be safely ignored other than preserving its value when + rewriting the image header. + +.. _ref_rules_3: + +3. An explicit value of 0 will have the same behavior as when the field is not + present*, if not altered by a specific incompatible bit. + +(*) A field is considered not present when ``header_length`` is less than or equal +to the field's offset. Also, all additional fields are not present for +version 2. + +:: + + 104: compression_type + + Defines the compression method used for compressed clusters. + All compressed clusters in an image use the same compression + type. + + If the incompatible bit "Compression type" is set: the field + must be present and non-zero (which means non-deflate + compression type). Otherwise, this field must not be present + or must be zero (which means deflate). + + Available compression type values: + - 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 +-------------- + +``header_length`` must be a multiple of 8, which means that if the end of the last +additional field is not aligned, some padding is needed. This padding must be +zeroed, so that if some existing (or future) additional field will fall into +the padding, it will be interpreted accordingly to point `[3.] <#ref_rules_3>`_ of the previous +paragraph, i.e. in the same manner as when this field is not present. + + +Header extensions +----------------- + +Directly after the image header, optional sections called header extensions can +be stored. Each extension has a structure like the following:: + + Byte 0 - 3: Header extension type: + 0x00000000 - End of the header extension area + 0xe2792aca - Backing file format name string + 0x6803f857 - Feature name table + 0x23852875 - Bitmaps extension + 0x0537be77 - Full disk encryption header pointer + 0x44415441 - External data file name string + other - Unknown header extension, can be safely + ignored + + 4 - 7: Length of the header extension data + + 8 - n: Header extension data + + n - m: Padding to round up the header extension size to the next + multiple of 8. + +Unless stated otherwise, each header extension type shall appear at most once +in the same image. + +If the image has a backing file then the backing file name should be stored in +the remaining space between the end of the header extension area and the end of +the first cluster. It is not allowed to store other data here, so that an +implementation can safely modify the header and add extensions without harming +data of compatible features that it doesn't support. Compatible features that +need space for additional data can use a header extension. + + +String header extensions +------------------------ + +Some header extensions (such as the backing file format name and the external +data file name) are just a single string. In this case, the header extension +length is the string length and the string is not ``\0`` terminated. (The header +extension padding can make it look like a string is ``\0`` terminated, but +neither is padding always necessary nor is there a guarantee that zero bytes +are used for padding.) + + +Feature name table +------------------ + +The feature name table is an optional header extension that contains the name +for features used by the image. It can be used by applications that don't know +the respective feature (e.g. because the feature was introduced only later) to +display a useful error message. + +The number of entries in the feature name table is determined by the length of +the header extension data. Each entry looks like this:: + + Byte 0: Type of feature (select feature bitmap) + 0: Incompatible feature + 1: Compatible feature + 2: Autoclear feature + + 1: Bit number within the selected feature bitmap (valid + values: 0-63) + + 2 - 47: Feature name (padded with zeros, but not necessarily null + terminated if it has full length) + + +Bitmaps extension +----------------- + +The bitmaps extension is an optional header extension. It provides the ability +to store bitmaps related to a virtual disk. For now, there is only one bitmap +type: the dirty tracking bitmap, which tracks virtual disk changes from some +point in time. + +The data of the extension should be considered consistent only if the +corresponding auto-clear feature bit is set, see ``autoclear_features`` above. + +The fields of the bitmaps extension are:: + + Byte 0 - 3: nb_bitmaps + The number of bitmaps contained in the image. Must be + greater than or equal to 1. + + Note: QEMU currently only supports up to 65535 bitmaps per + image. + + 4 - 7: Reserved, must be zero. + + 8 - 15: bitmap_directory_size + Size of the bitmap directory in bytes. It is the cumulative + size of all (nb_bitmaps) bitmap directory entries. + + 16 - 23: bitmap_directory_offset + Offset into the image file at which the bitmap directory + starts. Must be aligned to a cluster boundary. + +Full disk encryption header pointer +----------------------------------- + +The full disk encryption header must be present if, and only if, the +``crypt_method`` header requires metadata. Currently this is only true +of the ``LUKS`` crypt method. The header extension must be absent for +other methods. + +This header provides the offset at which the crypt method can store +its additional data, as well as the length of such data. +:: + + Byte 0 - 7: Offset into the image file at which the encryption + header starts in bytes. Must be aligned to a cluster + boundary. + Byte 8 - 15: Length of the written encryption header in bytes. + Note actual space allocated in the qcow2 file may + be larger than this value, since it will be rounded + to the nearest multiple of the cluster size. Any + unused bytes in the allocated space will be initialized + to 0. + +For the LUKS crypt method, the encryption header works as follows. + +The first 592 bytes of the header clusters will contain the LUKS +partition header. This is then followed by the key material data areas. +The size of the key material data areas is determined by the number of +stripes in the key slot and key size. Refer to the LUKS format +specification (``docs/on-disk-format.pdf`` in the cryptsetup source +package) for details of the LUKS partition header format. + +In the LUKS partition header, the ``payload-offset`` field will be +calculated as normal for the LUKS spec. ie the size of the LUKS +header, plus key material regions, plus padding, relative to the +start of the LUKS header. This offset value is not required to be +qcow2 cluster aligned. Its value is currently never used in the +context of qcow2, since the qcow2 file format itself defines where +the real payload offset is, but none the less a valid payload offset +should always be present. + +In the LUKS key slots header, the ``key-material-offset`` is relative +to the start of the LUKS header clusters in the qcow2 container, +not the start of the qcow2 file. + +Logically the layout looks like +:: + + +-----------------------------+ + | QCow2 header | + | QCow2 header extension X | + | QCow2 header extension FDE | + | QCow2 header extension ... | + | QCow2 header extension Z | + +-----------------------------+ + | ....other QCow2 tables.... | + . . + . . + +-----------------------------+ + | +-------------------------+ | + | | LUKS partition header | | + | +-------------------------+ | + | | LUKS key material 1 | | + | +-------------------------+ | + | | LUKS key material 2 | | + | +-------------------------+ | + | | LUKS key material ... | | + | +-------------------------+ | + | | LUKS key material 8 | | + | +-------------------------+ | + +-----------------------------+ + | QCow2 cluster payload | + . . + . . + . . + | | + +-----------------------------+ + +Data encryption +--------------- + +When an encryption method is requested in the header, the image payload +data must be encrypted/decrypted on every write/read. The image headers +and metadata are never encrypted. + +The algorithms used for encryption vary depending on the method + + - ``AES``: + + The AES cipher, in CBC mode, with 256 bit keys. + + Initialization vectors generated using plain64 method, with + the virtual disk sector as the input tweak. + + This format is no longer supported in QEMU system emulators, due + to a number of design flaws affecting its security. It is only + supported in the command line tools for the sake of back compatibility + and data liberation. + + - ``LUKS``: + + The algorithms are specified in the LUKS header. + + Initialization vectors generated using the method specified + in the LUKS header, with the physical disk sector as the + input tweak. + +Host cluster management +----------------------- + +qcow2 manages the allocation of host clusters by maintaining a reference count +for each host cluster. A refcount of 0 means that the cluster is free, 1 means +that it is used, and >= 2 means that it is used and any write access must +perform a COW (copy on write) operation. + +The refcounts are managed in a two-level table. The first level is called +refcount table and has a variable size (which is stored in the header). The +refcount table can cover multiple clusters, however it needs to be contiguous +in the image file. + +It contains pointers to the second level structures which are called refcount +blocks and are exactly one cluster in size. + +Although a large enough refcount table can reserve clusters past 64 PB +(56 bits) (assuming the underlying protocol can even be sized that +large), note that some qcow2 metadata such as L1/L2 tables must point +to clusters prior to that point. + +.. note:: + QEMU has an implementation limit of 8 MB as the maximum refcount + table size. With a 2 MB cluster size and a default refcount_order of + 4, it is unable to reference host resources beyond 2 EB (61 bits); in + the worst case, with a 512 cluster size and refcount_order of 6, it is + unable to access beyond 32 GB (35 bits). + +Given an offset into the image file, the refcount of its cluster can be +obtained as follows:: + + refcount_block_entries = (cluster_size * 8 / refcount_bits) + + refcount_block_index = (offset / cluster_size) % refcount_block_entries + refcount_table_index = (offset / cluster_size) / refcount_block_entries + + refcount_block = load_cluster(refcount_table[refcount_table_index]); + return refcount_block[refcount_block_index]; + +Refcount table entry:: + + Bit 0 - 8: Reserved (set to 0) + + 9 - 63: Bits 9-63 of the offset into the image file at which the + refcount block starts. Must be aligned to a cluster + boundary. + + If this is 0, the corresponding refcount block has not yet + been allocated. All refcounts managed by this refcount block + are 0. + +Refcount block entry ``(x = refcount_bits - 1)``:: + + Bit 0 - x: Reference count of the cluster. If refcount_bits implies a + sub-byte width, note that bit 0 means the least significant + bit in this context. + + +Cluster mapping +--------------- + +Just as for refcounts, qcow2 uses a two-level structure for the mapping of +guest clusters to host clusters. They are called L1 and L2 table. + +The L1 table has a variable size (stored in the header) and may use multiple +clusters, however it must be contiguous in the image file. L2 tables are +exactly one cluster in size. + +The L1 and L2 tables have implications on the maximum virtual file +size; for a given L1 table size, a larger cluster size is required for +the guest to have access to more space. Furthermore, a virtual +cluster must currently map to a host offset below 64 PB (56 bits) +(although this limit could be relaxed by putting reserved bits into +use). Additionally, as cluster size increases, the maximum host +offset for a compressed cluster is reduced (a 2M cluster size requires +compressed clusters to reside below 512 TB (49 bits), and this limit +cannot be relaxed without an incompatible layout change). + +Given an offset into the virtual disk, the offset into the image file can be +obtained as follows:: + + l2_entries = (cluster_size / sizeof(uint64_t)) [*] + + l2_index = (offset / cluster_size) % l2_entries + l1_index = (offset / cluster_size) / l2_entries + + l2_table = load_cluster(l1_table[l1_index]); + cluster_offset = l2_table[l2_index]; + + return cluster_offset + (offset % cluster_size) + + [*] this changes if Extended L2 Entries are enabled, see next section + +L1 table entry:: + + Bit 0 - 8: Reserved (set to 0) + + 9 - 55: Bits 9-55 of the offset into the image file at which the L2 + table starts. Must be aligned to a cluster boundary. If the + offset is 0, the L2 table and all clusters described by this + L2 table are unallocated. + + 56 - 62: Reserved (set to 0) + + 63: 0 for an L2 table that is unused or requires COW, 1 if its + refcount is exactly one. This information is only accurate + in the active L1 table. + +L2 table entry:: + + Bit 0 - 61: Cluster descriptor + + 62: 0 for standard clusters + 1 for compressed clusters + + 63: 0 for clusters that are unused, compressed or require COW. + 1 for standard clusters whose refcount is exactly one. + This information is only accurate in L2 tables + that are reachable from the active L1 table. + + With external data files, all guest clusters have an + implicit refcount of 1 (because of the fixed host = guest + mapping for guest cluster offsets), so this bit should be 1 + for all allocated clusters. + +Standard Cluster Descriptor:: + + Bit 0: If set to 1, the cluster reads as all zeros. The host + cluster offset can be used to describe a preallocation, + but it won't be used for reading data from this cluster, + nor is data read from the backing file if the cluster is + unallocated. + + With version 2 or with extended L2 entries (see the next + section), this is always 0. + + 1 - 8: Reserved (set to 0) + + 9 - 55: Bits 9-55 of host cluster offset. Must be aligned to a + cluster boundary. If the offset is 0 and bit 63 is clear, + the cluster is unallocated. The offset may only be 0 with + bit 63 set (indicating a host cluster offset of 0) when an + external data file is used. + + 56 - 61: Reserved (set to 0) + + +Compressed Clusters Descriptor ``(x = 62 - (cluster_bits - 8))``:: + + Bit 0 - x-1: Host cluster offset. This is usually _not_ aligned to a + cluster or sector boundary! If cluster_bits is + small enough that this field includes bits beyond + 55, those upper bits must be set to 0. + + x - 61: Number of additional 512-byte sectors used for the + compressed data, beyond the sector containing the offset + in the previous field. Some of these sectors may reside + in the next contiguous host cluster. + + Note that the compressed data does not necessarily occupy + all of the bytes in the final sector; rather, decompression + stops when it has produced a cluster of data. + + Another compressed cluster may map to the tail of the final + sector used by this compressed cluster. + +If a cluster is unallocated, read requests shall read the data from the backing +file (except if bit 0 in the Standard Cluster Descriptor is set). If there is +no backing file or the backing file is smaller than the image, they shall read +zeros for all parts that are not covered by the backing file. + +Extended L2 Entries +------------------- + +An image uses Extended L2 Entries if bit 4 is set on the incompatible_features +field of the header. + +In these images standard data clusters are divided into 32 subclusters of the +same size. They are contiguous and start from the beginning of the cluster. +Subclusters can be allocated independently and the L2 entry contains information +indicating the status of each one of them. Compressed data clusters don't have +subclusters so they are treated the same as in images without this feature. + +The size of an extended L2 entry is 128 bits so the number of entries per table +is calculated using this formula: + +.. code:: + + l2_entries = (cluster_size / (2 * sizeof(uint64_t))) + +The first 64 bits have the same format as the standard L2 table entry described +in the previous section, with the exception of bit 0 of the standard cluster +descriptor. + +The last 64 bits contain a subcluster allocation bitmap with this format: + +Subcluster Allocation Bitmap (for standard clusters):: + + Bit 0 - 31: Allocation status (one bit per subcluster) + + 1: the subcluster is allocated. In this case the + host cluster offset field must contain a valid + offset. + 0: the subcluster is not allocated. In this case + read requests shall go to the backing file or + return zeros if there is no backing file data. + + Bits are assigned starting from the least significant + one (i.e. bit x is used for subcluster x). + + 32 - 63 Subcluster reads as zeros (one bit per subcluster) + + 1: the subcluster reads as zeros. In this case the + allocation status bit must be unset. The host + cluster offset field may or may not be set. + 0: no effect. + + Bits are assigned starting from the least significant + one (i.e. bit x is used for subcluster x - 32). + +Subcluster Allocation Bitmap (for compressed clusters):: + + Bit 0 - 63: Reserved (set to 0) + Compressed clusters don't have subclusters, + so this field is not used. + +Snapshots +--------- + +qcow2 supports internal snapshots. Their basic principle of operation is to +switch the active L1 table, so that a different set of host clusters are +exposed to the guest. + +When creating a snapshot, the L1 table should be copied and the refcount of all +L2 tables and clusters reachable from this L1 table must be increased, so that +a write causes a COW and isn't visible in other snapshots. + +When loading a snapshot, bit 63 of all entries in the new active L1 table and +all L2 tables referenced by it must be reconstructed from the refcount table +as it doesn't need to be accurate in inactive L1 tables. + +A directory of all snapshots is stored in the snapshot table, a contiguous area +in the image file, whose starting offset and length are given by the header +fields snapshots_offset and nb_snapshots. The entries of the snapshot table +have variable length, depending on the length of ID, name and extra data. + +Snapshot table entry:: + + Byte 0 - 7: Offset into the image file at which the L1 table for the + snapshot starts. Must be aligned to a cluster boundary. + + 8 - 11: Number of entries in the L1 table of the snapshots + + 12 - 13: Length of the unique ID string describing the snapshot + + 14 - 15: Length of the name of the snapshot + + 16 - 19: Time at which the snapshot was taken in seconds since the + Epoch + + 20 - 23: Subsecond part of the time at which the snapshot was taken + in nanoseconds + + 24 - 31: Time that the guest was running until the snapshot was + taken in nanoseconds + + 32 - 35: Size of the VM state in bytes. 0 if no VM state is saved. + If there is VM state, it starts at the first cluster + described by first L1 table entry that doesn't describe a + regular guest cluster (i.e. VM state is stored like guest + disk content, except that it is stored at offsets that are + larger than the virtual disk presented to the guest) + + 36 - 39: Size of extra data in the table entry (used for future + extensions of the format) + + variable: Extra data for future extensions. Unknown fields must be + ignored. Currently defined are (offset relative to snapshot + table entry): + + Byte 40 - 47: Size of the VM state in bytes. 0 if no VM + state is saved. If this field is present, + the 32-bit value in bytes 32-35 is ignored. + + Byte 48 - 55: Virtual disk size of the snapshot in bytes + + Byte 56 - 63: icount value which corresponds to + the record/replay instruction count + when the snapshot was taken. Set to -1 + if icount was disabled + + Version 3 images must include extra data at least up to + byte 55. + + variable: Unique ID string for the snapshot (not null terminated) + + variable: Name of the snapshot (not null terminated) + + variable: Padding to round up the snapshot table entry size to the + next multiple of 8. + + +Bitmaps +------- + +As mentioned above, the bitmaps extension provides the ability to store bitmaps +related to a virtual disk. This section describes how these bitmaps are stored. + +All stored bitmaps are related to the virtual disk stored in the same image, so +each bitmap size is equal to the virtual disk size. + +Each bit of the bitmap is responsible for strictly defined range of the virtual +disk. For bit number bit_nr the corresponding range (in bytes) will be: + +.. code:: + + [bit_nr * bitmap_granularity .. (bit_nr + 1) * bitmap_granularity - 1] + +Granularity is a property of the concrete bitmap, see below. + + +Bitmap directory +---------------- + +Each bitmap saved in the image is described in a bitmap directory entry. The +bitmap directory is a contiguous area in the image file, whose starting offset +and length are given by the header extension fields ``bitmap_directory_offset`` and +``bitmap_directory_size``. The entries of the bitmap directory have variable +length, depending on the lengths of the bitmap name and extra data. + +Structure of a bitmap directory entry:: + + Byte 0 - 7: bitmap_table_offset + Offset into the image file at which the bitmap table + (described below) for the bitmap starts. Must be aligned to + a cluster boundary. + + 8 - 11: bitmap_table_size + Number of entries in the bitmap table of the bitmap. + + 12 - 15: flags + Bit + 0: in_use + The bitmap was not saved correctly and may be + inconsistent. Although the bitmap metadata is still + well-formed from a qcow2 perspective, the metadata + (such as the auto flag or bitmap size) or data + contents may be outdated. + + 1: auto + The bitmap must reflect all changes of the virtual + disk by any application that would write to this qcow2 + file (including writes, snapshot switching, etc.). The + type of this bitmap must be 'dirty tracking bitmap'. + + 2: extra_data_compatible + This flags is meaningful when the extra data is + unknown to the software (currently any extra data is + unknown to QEMU). + If it is set, the bitmap may be used as expected, extra + data must be left as is. + If it is not set, the bitmap must not be used, but + both it and its extra data be left as is. + + Bits 3 - 31 are reserved and must be 0. + + 16: type + This field describes the sort of the bitmap. + Values: + 1: Dirty tracking bitmap + + Values 0, 2 - 255 are reserved. + + 17: granularity_bits + Granularity bits. Valid values: 0 - 63. + + Note: QEMU currently supports only values 9 - 31. + + Granularity is calculated as + granularity = 1 << granularity_bits + + A bitmap's granularity is how many bytes of the image + accounts for one bit of the bitmap. + + 18 - 19: name_size + Size of the bitmap name. Must be non-zero. + + Note: QEMU currently doesn't support values greater than + 1023. + + 20 - 23: extra_data_size + Size of type-specific extra data. + + For now, as no extra data is defined, extra_data_size is + reserved and should be zero. If it is non-zero the + behavior is defined by extra_data_compatible flag. + + variable: extra_data + Extra data for the bitmap, occupying extra_data_size bytes. + Extra data must never contain references to clusters or in + some other way allocate additional clusters. + + variable: name + The name of the bitmap (not null terminated), occupying + name_size bytes. Must be unique among all bitmap names + within the bitmaps extension. + + variable: Padding to round up the bitmap directory entry size to the + next multiple of 8. All bytes of the padding must be zero. + + +Bitmap table +------------ + +Each bitmap is stored using a one-level structure (as opposed to two-level +structures like for refcounts and guest clusters mapping) for the mapping of +bitmap data to host clusters. This structure is called the bitmap table. + +Each bitmap table has a variable size (stored in the bitmap directory entry) +and may use multiple clusters, however, it must be contiguous in the image +file. + +Structure of a bitmap table entry:: + + Bit 0: Reserved and must be zero if bits 9 - 55 are non-zero. + If bits 9 - 55 are zero: + 0: Cluster should be read as all zeros. + 1: Cluster should be read as all ones. + + 1 - 8: Reserved and must be zero. + + 9 - 55: Bits 9 - 55 of the host cluster offset. Must be aligned to + a cluster boundary. If the offset is 0, the cluster is + unallocated; in that case, bit 0 determines how this + cluster should be treated during reads. + + 56 - 63: Reserved and must be zero. + + +Bitmap data +----------- + +As noted above, bitmap data is stored in separate clusters, described by the +bitmap table. Given an offset (in bytes) into the bitmap data, the offset into +the image file can be obtained as follows:: + + image_offset(bitmap_data_offset) = + bitmap_table[bitmap_data_offset / cluster_size] + + (bitmap_data_offset % cluster_size) + +This offset is not defined if bits 9 - 55 of bitmap table entry are zero (see +above). + +Given an offset byte_nr into the virtual disk and the bitmap's granularity, the +bit offset into the image file to the corresponding bit of the bitmap can be +calculated like this:: + + bit_offset(byte_nr) = + image_offset(byte_nr / granularity / 8) * 8 + + (byte_nr / granularity) % 8 + +If the size of the bitmap data is not a multiple of the cluster size then the +last cluster of the bitmap data contains some unused tail bits. These bits must +be zero. + + +Dirty tracking bitmaps +---------------------- + +Bitmaps with ``type`` field equal to one are dirty tracking bitmaps. + +When the virtual disk is in use dirty tracking bitmap may be ``enabled`` or +``disabled``. While the bitmap is ``enabled``, all writes to the virtual disk +should be reflected in the bitmap. A set bit in the bitmap means that the +corresponding range of the virtual disk (see above) was written to while the +bitmap was ``enabled``. An unset bit means that this range was not written to. + +The software doesn't have to sync the bitmap in the image file with its +representation in RAM after each write or metadata change. Flag ``in_use`` +should be set while the bitmap is not synced. + +In the image file the ``enabled`` state is reflected by the ``auto`` flag. If this +flag is set, the software must consider the bitmap as ``enabled`` and start +tracking virtual disk changes to this bitmap from the first write to the +virtual disk. If this flag is not set then the bitmap is disabled. diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt deleted file mode 100644 index 2c4618375ad..00000000000 --- a/docs/interop/qcow2.txt +++ /dev/null @@ -1,906 +0,0 @@ -== General == - -A qcow2 image file is organized in units of constant size, which are called -(host) clusters. A cluster is the unit in which all allocations are done, -both for actual guest data and for image metadata. - -Likewise, the virtual disk as seen by the guest is divided into (guest) -clusters of the same size. - -All numbers in qcow2 are stored in Big Endian byte order. - - -== Header == - -The first cluster of a qcow2 image contains the file header: - - Byte 0 - 3: magic - QCOW magic string ("QFI\xfb") - - 4 - 7: version - Version number (valid values are 2 and 3) - - 8 - 15: backing_file_offset - Offset into the image file at which the backing file name - is stored (NB: The string is not null terminated). 0 if the - image doesn't have a backing file. - - Note: backing files are incompatible with raw external data - files (auto-clear feature bit 1). - - 16 - 19: backing_file_size - Length of the backing file name in bytes. Must not be - longer than 1023 bytes. Undefined if the image doesn't have - a backing file. - - 20 - 23: cluster_bits - Number of bits that are used for addressing an offset - within a cluster (1 << cluster_bits is the cluster size). - Must not be less than 9 (i.e. 512 byte clusters). - - Note: qemu as of today has an implementation limit of 2 MB - as the maximum cluster size and won't be able to open images - with larger cluster sizes. - - Note: if the image has Extended L2 Entries then cluster_bits - must be at least 14 (i.e. 16384 byte clusters). - - 24 - 31: size - Virtual disk size in bytes. - - Note: qemu has an implementation limit of 32 MB as - the maximum L1 table size. With a 2 MB cluster - size, it is unable to populate a virtual cluster - beyond 2 EB (61 bits); with a 512 byte cluster - size, it is unable to populate a virtual size - larger than 128 GB (37 bits). Meanwhile, L1/L2 - table layouts limit an image to no more than 64 PB - (56 bits) of populated clusters, and an image may - hit other limits first (such as a file system's - maximum size). - - 32 - 35: crypt_method - 0 for no encryption - 1 for AES encryption - 2 for LUKS encryption - - 36 - 39: l1_size - Number of entries in the active L1 table - - 40 - 47: l1_table_offset - Offset into the image file at which the active L1 table - starts. Must be aligned to a cluster boundary. - - 48 - 55: refcount_table_offset - Offset into the image file at which the refcount table - starts. Must be aligned to a cluster boundary. - - 56 - 59: refcount_table_clusters - Number of clusters that the refcount table occupies - - 60 - 63: nb_snapshots - Number of snapshots contained in the image - - 64 - 71: snapshots_offset - Offset into the image file at which the snapshot table - starts. Must be aligned to a cluster boundary. - -For version 2, the header is exactly 72 bytes in length, and finishes here. -For version 3 or higher, the header length is at least 104 bytes, including -the next fields through header_length. - - 72 - 79: incompatible_features - Bitmask of incompatible features. An implementation must - fail to open an image if an unknown bit is set. - - Bit 0: Dirty bit. If this bit is set then refcounts - may be inconsistent, make sure to scan L1/L2 - tables to repair refcounts before accessing the - image. - - Bit 1: Corrupt bit. If this bit is set then any data - structure may be corrupt and the image must not - be written to (unless for regaining - consistency). - - Bit 2: External data file bit. If this bit is set, an - external data file is used. Guest clusters are - then stored in the external data file. For such - images, clusters in the external data file are - not refcounted. The offset field in the - Standard Cluster Descriptor must match the - guest offset and neither compressed clusters - nor internal snapshots are supported. - - An External Data File Name header extension may - be present if this bit is set. - - Bit 3: Compression type bit. If this bit is set, - a non-default compression is used for compressed - clusters. The compression_type field must be - present and not zero. - - Bit 4: Extended L2 Entries. If this bit is set then - L2 table entries use an extended format that - allows subcluster-based allocation. See the - Extended L2 Entries section for more details. - - Bits 5-63: Reserved (set to 0) - - 80 - 87: compatible_features - Bitmask of compatible features. An implementation can - safely ignore any unknown bits that are set. - - Bit 0: Lazy refcounts bit. If this bit is set then - lazy refcount updates can be used. This means - marking the image file dirty and postponing - refcount metadata updates. - - Bits 1-63: Reserved (set to 0) - - 88 - 95: autoclear_features - Bitmask of auto-clear features. An implementation may only - write to an image with unknown auto-clear features if it - clears the respective bits from this field first. - - Bit 0: Bitmaps extension bit - This bit indicates consistency for the bitmaps - extension data. - - It is an error if this bit is set without the - bitmaps extension present. - - If the bitmaps extension is present but this - bit is unset, the bitmaps extension data must be - considered inconsistent. - - Bit 1: Raw external data bit - If this bit is set, the external data file can - be read as a consistent standalone raw image - without looking at the qcow2 metadata. - - Setting this bit has a performance impact for - some operations on the image (e.g. writing - zeros requires writing to the data file instead - of only setting the zero flag in the L2 table - entry) and conflicts with backing files. - - This bit may only be set if the External Data - File bit (incompatible feature bit 1) is also - set. - - Bits 2-63: Reserved (set to 0) - - 96 - 99: refcount_order - Describes the width of a reference count block entry (width - in bits: refcount_bits = 1 << refcount_order). For version 2 - images, the order is always assumed to be 4 - (i.e. refcount_bits = 16). - This value may not exceed 6 (i.e. refcount_bits = 64). - - 100 - 103: header_length - Length of the header structure in bytes. For version 2 - images, the length is always assumed to be 72 bytes. - For version 3 it's at least 104 bytes and must be a multiple - of 8. - - -=== Additional fields (version 3 and higher) === - -In general, these fields are optional and may be safely ignored by the software, -as well as filled by zeros (which is equal to field absence), if software needs -to set field B, but does not care about field A which precedes B. More -formally, additional fields have the following compatibility rules: - -1. If the value of the additional field must not be ignored for correct -handling of the file, it will be accompanied by a corresponding incompatible -feature bit. - -2. If there are no unrecognized incompatible feature bits set, an unknown -additional field may be safely ignored other than preserving its value when -rewriting the image header. - -3. An explicit value of 0 will have the same behavior as when the field is not -present*, if not altered by a specific incompatible bit. - -*. A field is considered not present when header_length is less than or equal -to the field's offset. Also, all additional fields are not present for -version 2. - - 104: compression_type - - Defines the compression method used for compressed clusters. - All compressed clusters in an image use the same compression - type. - - If the incompatible bit "Compression type" is set: the field - must be present and non-zero (which means non-deflate - compression type). Otherwise, this field must not be present - or must be zero (which means deflate). - - Available compression type values: - 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 === - -@header_length must be a multiple of 8, which means that if the end of the last -additional field is not aligned, some padding is needed. This padding must be -zeroed, so that if some existing (or future) additional field will fall into -the padding, it will be interpreted accordingly to point [3.] of the previous -paragraph, i.e. in the same manner as when this field is not present. - - -=== Header extensions === - -Directly after the image header, optional sections called header extensions can -be stored. Each extension has a structure like the following: - - Byte 0 - 3: Header extension type: - 0x00000000 - End of the header extension area - 0xe2792aca - Backing file format name string - 0x6803f857 - Feature name table - 0x23852875 - Bitmaps extension - 0x0537be77 - Full disk encryption header pointer - 0x44415441 - External data file name string - other - Unknown header extension, can be safely - ignored - - 4 - 7: Length of the header extension data - - 8 - n: Header extension data - - n - m: Padding to round up the header extension size to the next - multiple of 8. - -Unless stated otherwise, each header extension type shall appear at most once -in the same image. - -If the image has a backing file then the backing file name should be stored in -the remaining space between the end of the header extension area and the end of -the first cluster. It is not allowed to store other data here, so that an -implementation can safely modify the header and add extensions without harming -data of compatible features that it doesn't support. Compatible features that -need space for additional data can use a header extension. - - -== String header extensions == - -Some header extensions (such as the backing file format name and the external -data file name) are just a single string. In this case, the header extension -length is the string length and the string is not '\0' terminated. (The header -extension padding can make it look like a string is '\0' terminated, but -neither is padding always necessary nor is there a guarantee that zero bytes -are used for padding.) - - -== Feature name table == - -The feature name table is an optional header extension that contains the name -for features used by the image. It can be used by applications that don't know -the respective feature (e.g. because the feature was introduced only later) to -display a useful error message. - -The number of entries in the feature name table is determined by the length of -the header extension data. Each entry look like this: - - Byte 0: Type of feature (select feature bitmap) - 0: Incompatible feature - 1: Compatible feature - 2: Autoclear feature - - 1: Bit number within the selected feature bitmap (valid - values: 0-63) - - 2 - 47: Feature name (padded with zeros, but not necessarily null - terminated if it has full length) - - -== Bitmaps extension == - -The bitmaps extension is an optional header extension. It provides the ability -to store bitmaps related to a virtual disk. For now, there is only one bitmap -type: the dirty tracking bitmap, which tracks virtual disk changes from some -point in time. - -The data of the extension should be considered consistent only if the -corresponding auto-clear feature bit is set, see autoclear_features above. - -The fields of the bitmaps extension are: - - Byte 0 - 3: nb_bitmaps - The number of bitmaps contained in the image. Must be - greater than or equal to 1. - - Note: QEMU currently only supports up to 65535 bitmaps per - image. - - 4 - 7: Reserved, must be zero. - - 8 - 15: bitmap_directory_size - Size of the bitmap directory in bytes. It is the cumulative - size of all (nb_bitmaps) bitmap directory entries. - - 16 - 23: bitmap_directory_offset - Offset into the image file at which the bitmap directory - starts. Must be aligned to a cluster boundary. - -== Full disk encryption header pointer == - -The full disk encryption header must be present if, and only if, the -'crypt_method' header requires metadata. Currently this is only true -of the 'LUKS' crypt method. The header extension must be absent for -other methods. - -This header provides the offset at which the crypt method can store -its additional data, as well as the length of such data. - - Byte 0 - 7: Offset into the image file at which the encryption - header starts in bytes. Must be aligned to a cluster - boundary. - Byte 8 - 15: Length of the written encryption header in bytes. - Note actual space allocated in the qcow2 file may - be larger than this value, since it will be rounded - to the nearest multiple of the cluster size. Any - unused bytes in the allocated space will be initialized - to 0. - -For the LUKS crypt method, the encryption header works as follows. - -The first 592 bytes of the header clusters will contain the LUKS -partition header. This is then followed by the key material data areas. -The size of the key material data areas is determined by the number of -stripes in the key slot and key size. Refer to the LUKS format -specification ('docs/on-disk-format.pdf' in the cryptsetup source -package) for details of the LUKS partition header format. - -In the LUKS partition header, the "payload-offset" field will be -calculated as normal for the LUKS spec. ie the size of the LUKS -header, plus key material regions, plus padding, relative to the -start of the LUKS header. This offset value is not required to be -qcow2 cluster aligned. Its value is currently never used in the -context of qcow2, since the qcow2 file format itself defines where -the real payload offset is, but none the less a valid payload offset -should always be present. - -In the LUKS key slots header, the "key-material-offset" is relative -to the start of the LUKS header clusters in the qcow2 container, -not the start of the qcow2 file. - -Logically the layout looks like - - +-----------------------------+ - | QCow2 header | - | QCow2 header extension X | - | QCow2 header extension FDE | - | QCow2 header extension ... | - | QCow2 header extension Z | - +-----------------------------+ - | ....other QCow2 tables.... | - . . - . . - +-----------------------------+ - | +-------------------------+ | - | | LUKS partition header | | - | +-------------------------+ | - | | LUKS key material 1 | | - | +-------------------------+ | - | | LUKS key material 2 | | - | +-------------------------+ | - | | LUKS key material ... | | - | +-------------------------+ | - | | LUKS key material 8 | | - | +-------------------------+ | - +-----------------------------+ - | QCow2 cluster payload | - . . - . . - . . - | | - +-----------------------------+ - -== Data encryption == - -When an encryption method is requested in the header, the image payload -data must be encrypted/decrypted on every write/read. The image headers -and metadata are never encrypted. - -The algorithms used for encryption vary depending on the method - - - AES: - - The AES cipher, in CBC mode, with 256 bit keys. - - Initialization vectors generated using plain64 method, with - the virtual disk sector as the input tweak. - - This format is no longer supported in QEMU system emulators, due - to a number of design flaws affecting its security. It is only - supported in the command line tools for the sake of back compatibility - and data liberation. - - - LUKS: - - The algorithms are specified in the LUKS header. - - Initialization vectors generated using the method specified - in the LUKS header, with the physical disk sector as the - input tweak. - -== Host cluster management == - -qcow2 manages the allocation of host clusters by maintaining a reference count -for each host cluster. A refcount of 0 means that the cluster is free, 1 means -that it is used, and >= 2 means that it is used and any write access must -perform a COW (copy on write) operation. - -The refcounts are managed in a two-level table. The first level is called -refcount table and has a variable size (which is stored in the header). The -refcount table can cover multiple clusters, however it needs to be contiguous -in the image file. - -It contains pointers to the second level structures which are called refcount -blocks and are exactly one cluster in size. - -Although a large enough refcount table can reserve clusters past 64 PB -(56 bits) (assuming the underlying protocol can even be sized that -large), note that some qcow2 metadata such as L1/L2 tables must point -to clusters prior to that point. - -Note: qemu has an implementation limit of 8 MB as the maximum refcount -table size. With a 2 MB cluster size and a default refcount_order of -4, it is unable to reference host resources beyond 2 EB (61 bits); in -the worst case, with a 512 cluster size and refcount_order of 6, it is -unable to access beyond 32 GB (35 bits). - -Given an offset into the image file, the refcount of its cluster can be -obtained as follows: - - refcount_block_entries = (cluster_size * 8 / refcount_bits) - - refcount_block_index = (offset / cluster_size) % refcount_block_entries - refcount_table_index = (offset / cluster_size) / refcount_block_entries - - refcount_block = load_cluster(refcount_table[refcount_table_index]); - return refcount_block[refcount_block_index]; - -Refcount table entry: - - Bit 0 - 8: Reserved (set to 0) - - 9 - 63: Bits 9-63 of the offset into the image file at which the - refcount block starts. Must be aligned to a cluster - boundary. - - If this is 0, the corresponding refcount block has not yet - been allocated. All refcounts managed by this refcount block - are 0. - -Refcount block entry (x = refcount_bits - 1): - - Bit 0 - x: Reference count of the cluster. If refcount_bits implies a - sub-byte width, note that bit 0 means the least significant - bit in this context. - - -== Cluster mapping == - -Just as for refcounts, qcow2 uses a two-level structure for the mapping of -guest clusters to host clusters. They are called L1 and L2 table. - -The L1 table has a variable size (stored in the header) and may use multiple -clusters, however it must be contiguous in the image file. L2 tables are -exactly one cluster in size. - -The L1 and L2 tables have implications on the maximum virtual file -size; for a given L1 table size, a larger cluster size is required for -the guest to have access to more space. Furthermore, a virtual -cluster must currently map to a host offset below 64 PB (56 bits) -(although this limit could be relaxed by putting reserved bits into -use). Additionally, as cluster size increases, the maximum host -offset for a compressed cluster is reduced (a 2M cluster size requires -compressed clusters to reside below 512 TB (49 bits), and this limit -cannot be relaxed without an incompatible layout change). - -Given an offset into the virtual disk, the offset into the image file can be -obtained as follows: - - l2_entries = (cluster_size / sizeof(uint64_t)) [*] - - l2_index = (offset / cluster_size) % l2_entries - l1_index = (offset / cluster_size) / l2_entries - - l2_table = load_cluster(l1_table[l1_index]); - cluster_offset = l2_table[l2_index]; - - return cluster_offset + (offset % cluster_size) - - [*] this changes if Extended L2 Entries are enabled, see next section - -L1 table entry: - - Bit 0 - 8: Reserved (set to 0) - - 9 - 55: Bits 9-55 of the offset into the image file at which the L2 - table starts. Must be aligned to a cluster boundary. If the - offset is 0, the L2 table and all clusters described by this - L2 table are unallocated. - - 56 - 62: Reserved (set to 0) - - 63: 0 for an L2 table that is unused or requires COW, 1 if its - refcount is exactly one. This information is only accurate - in the active L1 table. - -L2 table entry: - - Bit 0 - 61: Cluster descriptor - - 62: 0 for standard clusters - 1 for compressed clusters - - 63: 0 for clusters that are unused, compressed or require COW. - 1 for standard clusters whose refcount is exactly one. - This information is only accurate in L2 tables - that are reachable from the active L1 table. - - With external data files, all guest clusters have an - implicit refcount of 1 (because of the fixed host = guest - mapping for guest cluster offsets), so this bit should be 1 - for all allocated clusters. - -Standard Cluster Descriptor: - - Bit 0: If set to 1, the cluster reads as all zeros. The host - cluster offset can be used to describe a preallocation, - but it won't be used for reading data from this cluster, - nor is data read from the backing file if the cluster is - unallocated. - - With version 2 or with extended L2 entries (see the next - section), this is always 0. - - 1 - 8: Reserved (set to 0) - - 9 - 55: Bits 9-55 of host cluster offset. Must be aligned to a - cluster boundary. If the offset is 0 and bit 63 is clear, - the cluster is unallocated. The offset may only be 0 with - bit 63 set (indicating a host cluster offset of 0) when an - external data file is used. - - 56 - 61: Reserved (set to 0) - - -Compressed Clusters Descriptor (x = 62 - (cluster_bits - 8)): - - Bit 0 - x-1: Host cluster offset. This is usually _not_ aligned to a - cluster or sector boundary! If cluster_bits is - small enough that this field includes bits beyond - 55, those upper bits must be set to 0. - - x - 61: Number of additional 512-byte sectors used for the - compressed data, beyond the sector containing the offset - in the previous field. Some of these sectors may reside - in the next contiguous host cluster. - - Note that the compressed data does not necessarily occupy - all of the bytes in the final sector; rather, decompression - stops when it has produced a cluster of data. - - Another compressed cluster may map to the tail of the final - sector used by this compressed cluster. - -If a cluster is unallocated, read requests shall read the data from the backing -file (except if bit 0 in the Standard Cluster Descriptor is set). If there is -no backing file or the backing file is smaller than the image, they shall read -zeros for all parts that are not covered by the backing file. - -== Extended L2 Entries == - -An image uses Extended L2 Entries if bit 4 is set on the incompatible_features -field of the header. - -In these images standard data clusters are divided into 32 subclusters of the -same size. They are contiguous and start from the beginning of the cluster. -Subclusters can be allocated independently and the L2 entry contains information -indicating the status of each one of them. Compressed data clusters don't have -subclusters so they are treated the same as in images without this feature. - -The size of an extended L2 entry is 128 bits so the number of entries per table -is calculated using this formula: - - l2_entries = (cluster_size / (2 * sizeof(uint64_t))) - -The first 64 bits have the same format as the standard L2 table entry described -in the previous section, with the exception of bit 0 of the standard cluster -descriptor. - -The last 64 bits contain a subcluster allocation bitmap with this format: - -Subcluster Allocation Bitmap (for standard clusters): - - Bit 0 - 31: Allocation status (one bit per subcluster) - - 1: the subcluster is allocated. In this case the - host cluster offset field must contain a valid - offset. - 0: the subcluster is not allocated. In this case - read requests shall go to the backing file or - return zeros if there is no backing file data. - - Bits are assigned starting from the least significant - one (i.e. bit x is used for subcluster x). - - 32 - 63 Subcluster reads as zeros (one bit per subcluster) - - 1: the subcluster reads as zeros. In this case the - allocation status bit must be unset. The host - cluster offset field may or may not be set. - 0: no effect. - - Bits are assigned starting from the least significant - one (i.e. bit x is used for subcluster x - 32). - -Subcluster Allocation Bitmap (for compressed clusters): - - Bit 0 - 63: Reserved (set to 0) - Compressed clusters don't have subclusters, - so this field is not used. - -== Snapshots == - -qcow2 supports internal snapshots. Their basic principle of operation is to -switch the active L1 table, so that a different set of host clusters are -exposed to the guest. - -When creating a snapshot, the L1 table should be copied and the refcount of all -L2 tables and clusters reachable from this L1 table must be increased, so that -a write causes a COW and isn't visible in other snapshots. - -When loading a snapshot, bit 63 of all entries in the new active L1 table and -all L2 tables referenced by it must be reconstructed from the refcount table -as it doesn't need to be accurate in inactive L1 tables. - -A directory of all snapshots is stored in the snapshot table, a contiguous area -in the image file, whose starting offset and length are given by the header -fields snapshots_offset and nb_snapshots. The entries of the snapshot table -have variable length, depending on the length of ID, name and extra data. - -Snapshot table entry: - - Byte 0 - 7: Offset into the image file at which the L1 table for the - snapshot starts. Must be aligned to a cluster boundary. - - 8 - 11: Number of entries in the L1 table of the snapshots - - 12 - 13: Length of the unique ID string describing the snapshot - - 14 - 15: Length of the name of the snapshot - - 16 - 19: Time at which the snapshot was taken in seconds since the - Epoch - - 20 - 23: Subsecond part of the time at which the snapshot was taken - in nanoseconds - - 24 - 31: Time that the guest was running until the snapshot was - taken in nanoseconds - - 32 - 35: Size of the VM state in bytes. 0 if no VM state is saved. - If there is VM state, it starts at the first cluster - described by first L1 table entry that doesn't describe a - regular guest cluster (i.e. VM state is stored like guest - disk content, except that it is stored at offsets that are - larger than the virtual disk presented to the guest) - - 36 - 39: Size of extra data in the table entry (used for future - extensions of the format) - - variable: Extra data for future extensions. Unknown fields must be - ignored. Currently defined are (offset relative to snapshot - table entry): - - Byte 40 - 47: Size of the VM state in bytes. 0 if no VM - state is saved. If this field is present, - the 32-bit value in bytes 32-35 is ignored. - - Byte 48 - 55: Virtual disk size of the snapshot in bytes - - Byte 56 - 63: icount value which corresponds to - the record/replay instruction count - when the snapshot was taken. Set to -1 - if icount was disabled - - Version 3 images must include extra data at least up to - byte 55. - - variable: Unique ID string for the snapshot (not null terminated) - - variable: Name of the snapshot (not null terminated) - - variable: Padding to round up the snapshot table entry size to the - next multiple of 8. - - -== Bitmaps == - -As mentioned above, the bitmaps extension provides the ability to store bitmaps -related to a virtual disk. This section describes how these bitmaps are stored. - -All stored bitmaps are related to the virtual disk stored in the same image, so -each bitmap size is equal to the virtual disk size. - -Each bit of the bitmap is responsible for strictly defined range of the virtual -disk. For bit number bit_nr the corresponding range (in bytes) will be: - - [bit_nr * bitmap_granularity .. (bit_nr + 1) * bitmap_granularity - 1] - -Granularity is a property of the concrete bitmap, see below. - - -=== Bitmap directory === - -Each bitmap saved in the image is described in a bitmap directory entry. The -bitmap directory is a contiguous area in the image file, whose starting offset -and length are given by the header extension fields bitmap_directory_offset and -bitmap_directory_size. The entries of the bitmap directory have variable -length, depending on the lengths of the bitmap name and extra data. - -Structure of a bitmap directory entry: - - Byte 0 - 7: bitmap_table_offset - Offset into the image file at which the bitmap table - (described below) for the bitmap starts. Must be aligned to - a cluster boundary. - - 8 - 11: bitmap_table_size - Number of entries in the bitmap table of the bitmap. - - 12 - 15: flags - Bit - 0: in_use - The bitmap was not saved correctly and may be - inconsistent. Although the bitmap metadata is still - well-formed from a qcow2 perspective, the metadata - (such as the auto flag or bitmap size) or data - contents may be outdated. - - 1: auto - The bitmap must reflect all changes of the virtual - disk by any application that would write to this qcow2 - file (including writes, snapshot switching, etc.). The - type of this bitmap must be 'dirty tracking bitmap'. - - 2: extra_data_compatible - This flags is meaningful when the extra data is - unknown to the software (currently any extra data is - unknown to QEMU). - If it is set, the bitmap may be used as expected, extra - data must be left as is. - If it is not set, the bitmap must not be used, but - both it and its extra data be left as is. - - Bits 3 - 31 are reserved and must be 0. - - 16: type - This field describes the sort of the bitmap. - Values: - 1: Dirty tracking bitmap - - Values 0, 2 - 255 are reserved. - - 17: granularity_bits - Granularity bits. Valid values: 0 - 63. - - Note: QEMU currently supports only values 9 - 31. - - Granularity is calculated as - granularity = 1 << granularity_bits - - A bitmap's granularity is how many bytes of the image - accounts for one bit of the bitmap. - - 18 - 19: name_size - Size of the bitmap name. Must be non-zero. - - Note: QEMU currently doesn't support values greater than - 1023. - - 20 - 23: extra_data_size - Size of type-specific extra data. - - For now, as no extra data is defined, extra_data_size is - reserved and should be zero. If it is non-zero the - behavior is defined by extra_data_compatible flag. - - variable: extra_data - Extra data for the bitmap, occupying extra_data_size bytes. - Extra data must never contain references to clusters or in - some other way allocate additional clusters. - - variable: name - The name of the bitmap (not null terminated), occupying - name_size bytes. Must be unique among all bitmap names - within the bitmaps extension. - - variable: Padding to round up the bitmap directory entry size to the - next multiple of 8. All bytes of the padding must be zero. - - -=== Bitmap table === - -Each bitmap is stored using a one-level structure (as opposed to two-level -structures like for refcounts and guest clusters mapping) for the mapping of -bitmap data to host clusters. This structure is called the bitmap table. - -Each bitmap table has a variable size (stored in the bitmap directory entry) -and may use multiple clusters, however, it must be contiguous in the image -file. - -Structure of a bitmap table entry: - - Bit 0: Reserved and must be zero if bits 9 - 55 are non-zero. - If bits 9 - 55 are zero: - 0: Cluster should be read as all zeros. - 1: Cluster should be read as all ones. - - 1 - 8: Reserved and must be zero. - - 9 - 55: Bits 9 - 55 of the host cluster offset. Must be aligned to - a cluster boundary. If the offset is 0, the cluster is - unallocated; in that case, bit 0 determines how this - cluster should be treated during reads. - - 56 - 63: Reserved and must be zero. - - -=== Bitmap data === - -As noted above, bitmap data is stored in separate clusters, described by the -bitmap table. Given an offset (in bytes) into the bitmap data, the offset into -the image file can be obtained as follows: - - image_offset(bitmap_data_offset) = - bitmap_table[bitmap_data_offset / cluster_size] + - (bitmap_data_offset % cluster_size) - -This offset is not defined if bits 9 - 55 of bitmap table entry are zero (see -above). - -Given an offset byte_nr into the virtual disk and the bitmap's granularity, the -bit offset into the image file to the corresponding bit of the bitmap can be -calculated like this: - - bit_offset(byte_nr) = - image_offset(byte_nr / granularity / 8) * 8 + - (byte_nr / granularity) % 8 - -If the size of the bitmap data is not a multiple of the cluster size then the -last cluster of the bitmap data contains some unused tail bits. These bits must -be zero. - - -=== Dirty tracking bitmaps === - -Bitmaps with 'type' field equal to one are dirty tracking bitmaps. - -When the virtual disk is in use dirty tracking bitmap may be 'enabled' or -'disabled'. While the bitmap is 'enabled', all writes to the virtual disk -should be reflected in the bitmap. A set bit in the bitmap means that the -corresponding range of the virtual disk (see above) was written to while the -bitmap was 'enabled'. An unset bit means that this range was not written to. - -The software doesn't have to sync the bitmap in the image file with its -representation in RAM after each write or metadata change. Flag 'in_use' -should be set while the bitmap is not synced. - -In the image file the 'enabled' state is reflected by the 'auto' flag. If this -flag is set, the software must consider the bitmap as 'enabled' and start -tracking virtual disk changes to this bitmap from the first write to the -virtual disk. If this flag is not set then the bitmap is disabled. diff --git a/docs/interop/qed_spec.rst b/docs/interop/qed_spec.rst new file mode 100644 index 00000000000..cd6c7d90051 --- /dev/null +++ b/docs/interop/qed_spec.rst @@ -0,0 +1,219 @@ +=================================== +QED Image File Format Specification +=================================== + +The file format looks like this:: + + +----------+----------+----------+-----+ + | cluster0 | cluster1 | cluster2 | ... | + +----------+----------+----------+-----+ + +The first cluster begins with the ``header``. The header contains information +about where regular clusters start; this allows the header to be extensible and +store extra information about the image file. A regular cluster may be +a ``data cluster``, an ``L2``, or an ``L1 table``. L1 and L2 tables are composed +of one or more contiguous clusters. + +Normally the file size will be a multiple of the cluster size. If the file size +is not a multiple, extra information after the last cluster may not be preserved +if data is written. Legitimate extra information should use space between the header +and the first regular cluster. + +All fields are little-endian. + +Header +------ + +:: + + Header { + uint32_t magic; /* QED\0 */ + + uint32_t cluster_size; /* in bytes */ + uint32_t table_size; /* for L1 and L2 tables, in clusters */ + uint32_t header_size; /* in clusters */ + + uint64_t features; /* format feature bits */ + uint64_t compat_features; /* compat feature bits */ + uint64_t autoclear_features; /* self-resetting feature bits */ + + uint64_t l1_table_offset; /* in bytes */ + uint64_t image_size; /* total logical image size, in bytes */ + + /* if (features & QED_F_BACKING_FILE) */ + uint32_t backing_filename_offset; /* in bytes from start of header */ + uint32_t backing_filename_size; /* in bytes */ + } + +Field descriptions: +~~~~~~~~~~~~~~~~~~~ + +- ``cluster_size`` must be a power of 2 in range [2^12, 2^26]. +- ``table_size`` must be a power of 2 in range [1, 16]. +- ``header_size`` is the number of clusters used by the header and any additional + information stored before regular clusters. +- ``features``, ``compat_features``, and ``autoclear_features`` are file format + extension bitmaps. They work as follows: + + - An image with unknown ``features`` bits enabled must not be opened. File format + changes that are not backwards-compatible must use ``features`` bits. + - An image with unknown ``compat_features`` bits enabled can be opened safely. + The unknown features are simply ignored and represent backwards-compatible + changes to the file format. + - An image with unknown ``autoclear_features`` bits enable can be opened safely + after clearing the unknown bits. This allows for backwards-compatible changes + to the file format which degrade gracefully and can be re-enabled again by a + new program later. +- ``l1_table_offset`` is the offset of the first byte of the L1 table in the image + file and must be a multiple of ``cluster_size``. +- ``image_size`` is the block device size seen by the guest and must be a multiple + of 512 bytes. +- ``backing_filename_offset`` and ``backing_filename_size`` describe a string in + (byte offset, byte size) form. It is not NUL-terminated and has no alignment constraints. + The string must be stored within the first ``header_size`` clusters. The backing filename + may be an absolute path or relative to the image file. + +Feature bits: +~~~~~~~~~~~~~ + +- ``QED_F_BACKING_FILE = 0x01``. The image uses a backing file. +- ``QED_F_NEED_CHECK = 0x02``. The image needs a consistency check before use. +- ``QED_F_BACKING_FORMAT_NO_PROBE = 0x04``. The backing file is a raw disk image + and no file format autodetection should be attempted. This should be used to + ensure that raw backing files are never detected as an image format if they happen + to contain magic constants. + +There are currently no defined ``compat_features`` or ``autoclear_features`` bits. + +Fields predicated on a feature bit are only used when that feature is set. +The fields always take up header space, regardless of whether or not the feature +bit is set. + +Tables +------ + +Tables provide the translation from logical offsets in the block device to cluster +offsets in the file. + +:: + + #define TABLE_NOFFSETS (table_size * cluster_size / sizeof(uint64_t)) + + Table { + uint64_t offsets[TABLE_NOFFSETS]; + } + +The tables are organized as follows:: + + +----------+ + | L1 table | + +----------+ + ,------' | '------. + +----------+ | +----------+ + | L2 table | ... | L2 table | + +----------+ +----------+ + ,------' | '------. + +----------+ | +----------+ + | Data | ... | Data | + +----------+ +----------+ + +A table is made up of one or more contiguous clusters. The ``table_size`` header +field determines table size for an image file. For example, ``cluster_size=64 KB`` +and ``table_size=4`` results in 256 KB tables. + +The logical image size must be less than or equal to the maximum possible size of +clusters rooted by the L1 table: + +.. code:: + + header.image_size <= TABLE_NOFFSETS * TABLE_NOFFSETS * header.cluster_size + +L1, L2, and data cluster offsets must be aligned to ``header.cluster_size``. +The following offsets have special meanings: + +L2 table offsets +~~~~~~~~~~~~~~~~ + +- 0 - unallocated. The L2 table is not yet allocated. + +Data cluster offsets +~~~~~~~~~~~~~~~~~~~~ + +- 0 - unallocated. The data cluster is not yet allocated. +- 1 - zero. The data cluster contents are all zeroes and no cluster is allocated. + +Future format extensions may wish to store per-offset information. The least +significant 12 bits of an offset are reserved for this purpose and must be set +to zero. Image files with ``cluster_size`` > 2^12 will have more unused bits +which should also be zeroed. + +Unallocated L2 tables and data clusters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Reads to an unallocated area of the image file access the backing file. If there +is no backing file, then zeroes are produced. The backing file may be smaller +than the image file and reads of unallocated areas beyond the end of the backing +file produce zeroes. + +Writes to an unallocated area cause a new data clusters to be allocated, and a new +L2 table if that is also unallocated. The new data cluster is populated with data +from the backing file (or zeroes if no backing file) and the data being written. + +Zero data clusters +~~~~~~~~~~~~~~~~~~ + +Zero data clusters are a space-efficient way of storing zeroed regions of the image. + +Reads to a zero data cluster produce zeroes. + +.. note:: + The difference between an unallocated and a zero data cluster is that zero data + clusters stop the reading of contents from the backing file. + +Writes to a zero data cluster cause a new data cluster to be allocated. The new +data cluster is populated with zeroes and the data being written. + +Logical offset translation +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Logical offsets are translated into cluster offsets as follows:: + + table_bits table_bits cluster_bits + <--------> <--------> <---------------> + +----------+----------+-----------------+ + | L1 index | L2 index | byte offset | + +----------+----------+-----------------+ + + Structure of a logical offset + + offset_mask = ~(cluster_size - 1) # mask for the image file byte offset + + def logical_to_cluster_offset(l1_index, l2_index, byte_offset): + l2_offset = l1_table[l1_index] + l2_table = load_table(l2_offset) + cluster_offset = l2_table[l2_index] & offset_mask + return cluster_offset + byte_offset + +Consistency checking +-------------------- + +This section is informational and included to provide background on the use +of the ``QED_F_NEED_CHECK features`` bit. + +The ``QED_F_NEED_CHECK`` bit is used to mark an image as dirty before starting +an operation that could leave the image in an inconsistent state if interrupted +by a crash or power failure. A dirty image must be checked on open because its +metadata may not be consistent. + +Consistency check includes the following invariants: + +- Each cluster is referenced once and only once. It is an inconsistency to have + a cluster referenced more than once by L1 or L2 tables. A cluster has been leaked + if it has no references. +- Offsets must be within the image file size and must be ``cluster_size`` aligned. +- Table offsets must at least ``table_size`` * ``cluster_size`` bytes from the end + of the image file so that there is space for the entire table. + +The consistency check process starts from ``l1_table_offset`` and scans all L2 tables. +After the check completes with no other errors besides leaks, the ``QED_F_NEED_CHECK`` +bit can be cleared and the image can be accessed. diff --git a/docs/interop/qed_spec.txt b/docs/interop/qed_spec.txt deleted file mode 100644 index 7982e058b2c..00000000000 --- a/docs/interop/qed_spec.txt +++ /dev/null @@ -1,138 +0,0 @@ -=Specification= - -The file format looks like this: - - +----------+----------+----------+-----+ - | cluster0 | cluster1 | cluster2 | ... | - +----------+----------+----------+-----+ - -The first cluster begins with the '''header'''. The header contains information about where regular clusters start; this allows the header to be extensible and store extra information about the image file. A regular cluster may be a '''data cluster''', an '''L2''', or an '''L1 table'''. L1 and L2 tables are composed of one or more contiguous clusters. - -Normally the file size will be a multiple of the cluster size. If the file size is not a multiple, extra information after the last cluster may not be preserved if data is written. Legitimate extra information should use space between the header and the first regular cluster. - -All fields are little-endian. - -==Header== - Header { - uint32_t magic; /* QED\0 */ - - uint32_t cluster_size; /* in bytes */ - uint32_t table_size; /* for L1 and L2 tables, in clusters */ - uint32_t header_size; /* in clusters */ - - uint64_t features; /* format feature bits */ - uint64_t compat_features; /* compat feature bits */ - uint64_t autoclear_features; /* self-resetting feature bits */ - - uint64_t l1_table_offset; /* in bytes */ - uint64_t image_size; /* total logical image size, in bytes */ - - /* if (features & QED_F_BACKING_FILE) */ - uint32_t backing_filename_offset; /* in bytes from start of header */ - uint32_t backing_filename_size; /* in bytes */ - } - -Field descriptions: -* ''cluster_size'' must be a power of 2 in range [2^12, 2^26]. -* ''table_size'' must be a power of 2 in range [1, 16]. -* ''header_size'' is the number of clusters used by the header and any additional information stored before regular clusters. -* ''features'', ''compat_features'', and ''autoclear_features'' are file format extension bitmaps. They work as follows: -** An image with unknown ''features'' bits enabled must not be opened. File format changes that are not backwards-compatible must use ''features'' bits. -** An image with unknown ''compat_features'' bits enabled can be opened safely. The unknown features are simply ignored and represent backwards-compatible changes to the file format. -** An image with unknown ''autoclear_features'' bits enable can be opened safely after clearing the unknown bits. This allows for backwards-compatible changes to the file format which degrade gracefully and can be re-enabled again by a new program later. -* ''l1_table_offset'' is the offset of the first byte of the L1 table in the image file and must be a multiple of ''cluster_size''. -* ''image_size'' is the block device size seen by the guest and must be a multiple of 512 bytes. -* ''backing_filename_offset'' and ''backing_filename_size'' describe a string in (byte offset, byte size) form. It is not NUL-terminated and has no alignment constraints. The string must be stored within the first ''header_size'' clusters. The backing filename may be an absolute path or relative to the image file. - -Feature bits: -* QED_F_BACKING_FILE = 0x01. The image uses a backing file. -* QED_F_NEED_CHECK = 0x02. The image needs a consistency check before use. -* QED_F_BACKING_FORMAT_NO_PROBE = 0x04. The backing file is a raw disk image and no file format autodetection should be attempted. This should be used to ensure that raw backing files are never detected as an image format if they happen to contain magic constants. - -There are currently no defined ''compat_features'' or ''autoclear_features'' bits. - -Fields predicated on a feature bit are only used when that feature is set. The fields always take up header space, regardless of whether or not the feature bit is set. - -==Tables== - -Tables provide the translation from logical offsets in the block device to cluster offsets in the file. - - #define TABLE_NOFFSETS (table_size * cluster_size / sizeof(uint64_t)) - - Table { - uint64_t offsets[TABLE_NOFFSETS]; - } - -The tables are organized as follows: - - +----------+ - | L1 table | - +----------+ - ,------' | '------. - +----------+ | +----------+ - | L2 table | ... | L2 table | - +----------+ +----------+ - ,------' | '------. - +----------+ | +----------+ - | Data | ... | Data | - +----------+ +----------+ - -A table is made up of one or more contiguous clusters. The table_size header field determines table size for an image file. For example, cluster_size=64 KB and table_size=4 results in 256 KB tables. - -The logical image size must be less than or equal to the maximum possible size of clusters rooted by the L1 table: - header.image_size <= TABLE_NOFFSETS * TABLE_NOFFSETS * header.cluster_size - -L1, L2, and data cluster offsets must be aligned to header.cluster_size. The following offsets have special meanings: - -===L2 table offsets=== -* 0 - unallocated. The L2 table is not yet allocated. - -===Data cluster offsets=== -* 0 - unallocated. The data cluster is not yet allocated. -* 1 - zero. The data cluster contents are all zeroes and no cluster is allocated. - -Future format extensions may wish to store per-offset information. The least significant 12 bits of an offset are reserved for this purpose and must be set to zero. Image files with cluster_size > 2^12 will have more unused bits which should also be zeroed. - -===Unallocated L2 tables and data clusters=== -Reads to an unallocated area of the image file access the backing file. If there is no backing file, then zeroes are produced. The backing file may be smaller than the image file and reads of unallocated areas beyond the end of the backing file produce zeroes. - -Writes to an unallocated area cause a new data clusters to be allocated, and a new L2 table if that is also unallocated. The new data cluster is populated with data from the backing file (or zeroes if no backing file) and the data being written. - -===Zero data clusters=== -Zero data clusters are a space-efficient way of storing zeroed regions of the image. - -Reads to a zero data cluster produce zeroes. Note that the difference between an unallocated and a zero data cluster is that zero data clusters stop the reading of contents from the backing file. - -Writes to a zero data cluster cause a new data cluster to be allocated. The new data cluster is populated with zeroes and the data being written. - -===Logical offset translation=== -Logical offsets are translated into cluster offsets as follows: - - table_bits table_bits cluster_bits - <--------> <--------> <---------------> - +----------+----------+-----------------+ - | L1 index | L2 index | byte offset | - +----------+----------+-----------------+ - - Structure of a logical offset - - offset_mask = ~(cluster_size - 1) # mask for the image file byte offset - - def logical_to_cluster_offset(l1_index, l2_index, byte_offset): - l2_offset = l1_table[l1_index] - l2_table = load_table(l2_offset) - cluster_offset = l2_table[l2_index] & offset_mask - return cluster_offset + byte_offset - -==Consistency checking== - -This section is informational and included to provide background on the use of the QED_F_NEED_CHECK ''features'' bit. - -The QED_F_NEED_CHECK bit is used to mark an image as dirty before starting an operation that could leave the image in an inconsistent state if interrupted by a crash or power failure. A dirty image must be checked on open because its metadata may not be consistent. - -Consistency check includes the following invariants: -# Each cluster is referenced once and only once. It is an inconsistency to have a cluster referenced more than once by L1 or L2 tables. A cluster has been leaked if it has no references. -# Offsets must be within the image file size and must be ''cluster_size'' aligned. -# Table offsets must at least ''table_size'' * ''cluster_size'' bytes from the end of the image file so that there is space for the entire table. - -The consistency check process starts by from ''l1_table_offset'' and scans all L2 tables. After the check completes with no other errors besides leaks, the QED_F_NEED_CHECK bit can be cleared and the image can be accessed. diff --git a/docs/interop/qemu-ga-ref.rst b/docs/interop/qemu-ga-ref.rst index 032d4924552..25f6e24b03c 100644 --- a/docs/interop/qemu-ga-ref.rst +++ b/docs/interop/qemu-ga-ref.rst @@ -1,7 +1,6 @@ QEMU Guest Agent Protocol Reference =================================== -.. contents:: - :depth: 3 - .. qapi-doc:: qga/qapi-schema.json + :transmogrify: + :namespace: QGA diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst index 461c5a35ee1..d16cc1b9f07 100644 --- a/docs/interop/qemu-ga.rst +++ b/docs/interop/qemu-ga.rst @@ -1,3 +1,5 @@ +.. _qemu-ga: + QEMU Guest Agent ================ @@ -28,11 +30,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,13 +102,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 ``help`` to - list available RPCs). + Comma-separated list of RPCs to enable (no spaces, use ``--allow-rpcs=help`` + to list available RPCs). .. option:: -D, --dump-conf @@ -131,6 +152,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 f94614a0b2f..3bc1ca12b16 100644 --- a/docs/interop/qemu-qmp-ref.rst +++ b/docs/interop/qemu-qmp-ref.rst @@ -4,6 +4,8 @@ QEMU QMP Reference Manual ========================= .. contents:: - :depth: 3 + :local: .. qapi-doc:: qapi/qapi-schema.json + :transmogrify: + :namespace: QMP diff --git a/docs/interop/qemu-storage-daemon-qmp-ref.rst b/docs/interop/qemu-storage-daemon-qmp-ref.rst index 9fed68152f5..dc7bde262ae 100644 --- a/docs/interop/qemu-storage-daemon-qmp-ref.rst +++ b/docs/interop/qemu-storage-daemon-qmp-ref.rst @@ -2,6 +2,8 @@ QEMU Storage Daemon QMP Reference Manual ======================================== .. contents:: - :depth: 3 + :local: .. qapi-doc:: storage-daemon/qapi/qapi-schema.json + :transmogrify: + :namespace: QSD diff --git a/docs/interop/vfio-user.rst b/docs/interop/vfio-user.rst new file mode 100644 index 00000000000..0b06f026b0d --- /dev/null +++ b/docs/interop/vfio-user.rst @@ -0,0 +1,1520 @@ +.. include:: +.. SPDX-License-Identifier: GPL-2.0-or-later + +================================ +vfio-user Protocol Specification +================================ + +.. contents:: Table of Contents + +Introduction +============ +vfio-user is a protocol that allows a device to be emulated in a separate +process outside of a Virtual Machine Monitor (VMM). vfio-user devices consist +of a generic VFIO device type, living inside the VMM, which we call the client, +and the core device implementation, living outside the VMM, which we call the +server. + +The vfio-user specification is partly based on the +`Linux VFIO ioctl interface `_. + +VFIO is a mature and stable API, backed by an extensively used framework. The +existing VFIO client implementation in QEMU (``qemu/hw/vfio/``) can be largely +re-used, though there is nothing in this specification that requires that +particular implementation. None of the VFIO kernel modules are required for +supporting the protocol, on either the client or server side. Some source +definitions in VFIO are re-used for vfio-user. + +The main idea is to allow a virtual device to function in a separate process in +the same host over a UNIX domain socket. A UNIX domain socket (``AF_UNIX``) is +chosen because file descriptors can be trivially sent over it, which in turn +allows: + +* Sharing of client memory for DMA with the server. +* Sharing of server memory with the client for fast MMIO. +* Efficient sharing of eventfd's for triggering interrupts. + +Other socket types could be used which allow the server to run in a separate +guest in the same host (``AF_VSOCK``) or remotely (``AF_INET``). Theoretically +the underlying transport does not necessarily have to be a socket, however we do +not examine such alternatives. In this protocol version we focus on using a UNIX +domain socket and introduce basic support for the other two types of sockets +without considering performance implications. + +While passing of file descriptors is desirable for performance reasons, support +is not necessary for either the client or the server in order to implement the +protocol. There is always an in-band, message-passing fall back mechanism. + +Overview +======== + +VFIO is a framework that allows a physical device to be securely passed through +to a user space process; the device-specific kernel driver does not drive the +device at all. Typically, the user space process is a VMM and the device is +passed through to it in order to achieve high performance. VFIO provides an API +and the required functionality in the kernel. QEMU has adopted VFIO to allow a +guest to directly access physical devices, instead of emulating them in +software. + +vfio-user reuses the core VFIO concepts defined in its API, but implements them +as messages to be sent over a socket. It does not change the kernel-based VFIO +in any way, in fact none of the VFIO kernel modules need to be loaded to use +vfio-user. It is also possible for the client to concurrently use the current +kernel-based VFIO for one device, and vfio-user for another device. + +VFIO Device Model +----------------- + +A device under VFIO presents a standard interface to the user process. Many of +the VFIO operations in the existing interface use the ``ioctl()`` system call, and +references to the existing interface are called the ``ioctl()`` implementation in +this document. + +The following sections describe the set of messages that implement the vfio-user +interface over a socket. In many cases, the messages are analogous to data +structures used in the ``ioctl()`` implementation. Messages derived from the +``ioctl()`` will have a name derived from the ``ioctl()`` command name. E.g., the +``VFIO_DEVICE_GET_INFO`` ``ioctl()`` command becomes a +``VFIO_USER_DEVICE_GET_INFO`` message. The purpose of this reuse is to share as +much code as feasible with the ``ioctl()`` implementation``. + +Connection Initiation +^^^^^^^^^^^^^^^^^^^^^ + +After the client connects to the server, the initial client message is +``VFIO_USER_VERSION`` to propose a protocol version and set of capabilities to +apply to the session. The server replies with a compatible version and set of +capabilities it supports, or closes the connection if it cannot support the +advertised version. + +Device Information +^^^^^^^^^^^^^^^^^^ + +The client uses a ``VFIO_USER_DEVICE_GET_INFO`` message to query the server for +information about the device. This information includes: + +* The device type and whether it supports reset (``VFIO_DEVICE_FLAGS_``), +* the number of device regions, and +* the device presents to the client the number of interrupt types the device + supports. + +Region Information +^^^^^^^^^^^^^^^^^^ + +The client uses ``VFIO_USER_DEVICE_GET_REGION_INFO`` messages to query the +server for information about the device's regions. This information describes: + +* Read and write permissions, whether it can be memory mapped, and whether it + supports additional capabilities (``VFIO_REGION_INFO_CAP_``). +* Region index, size, and offset. + +When a device region can be mapped by the client, the server provides a file +descriptor which the client can ``mmap()``. The server is responsible for +polling for client updates to memory mapped regions. + +Region Capabilities +""""""""""""""""""" + +Some regions have additional capabilities that cannot be described adequately +by the region info data structure. These capabilities are returned in the +region info reply in a list similar to PCI capabilities in a PCI device's +configuration space. + +Sparse Regions +"""""""""""""" +A region can be memory-mappable in whole or in part. When only a subset of a +region can be mapped by the client, a ``VFIO_REGION_INFO_CAP_SPARSE_MMAP`` +capability is included in the region info reply. This capability describes +which portions can be mapped by the client. + +.. Note:: + For example, in a virtual NVMe controller, sparse regions can be used so + that accesses to the NVMe registers (found in the beginning of BAR0) are + trapped (an infrequent event), while allowing direct access to the doorbells + (an extremely frequent event as every I/O submission requires a write to + BAR0), found in the next page after the NVMe registers in BAR0. + +Device-Specific Regions +""""""""""""""""""""""" + +A device can define regions additional to the standard ones (e.g. PCI indexes +0-8). This is achieved by including a ``VFIO_REGION_INFO_CAP_TYPE`` capability +in the region info reply of a device-specific region. Such regions are reflected +in ``struct vfio_user_device_info.num_regions``. Thus, for PCI devices this +value can be equal to, or higher than, ``VFIO_PCI_NUM_REGIONS``. + +Region I/O via file descriptors +------------------------------- + +For unmapped regions, region I/O from the client is done via +``VFIO_USER_REGION_READ/WRITE``. As an optimization, ioeventfds or ioregionfds +may be configured for sub-regions of some regions. A client may request +information on these sub-regions via ``VFIO_USER_DEVICE_GET_REGION_IO_FDS``; by +configuring the returned file descriptors as ioeventfds or ioregionfds, the +server can be directly notified of I/O (for example, by KVM) without taking a +trip through the client. + +Interrupts +^^^^^^^^^^ + +The client uses ``VFIO_USER_DEVICE_GET_IRQ_INFO`` messages to query the server +for the device's interrupt types. The interrupt types are specific to the bus +the device is attached to, and the client is expected to know the capabilities +of each interrupt type. The server can signal an interrupt by directly injecting +interrupts into the guest via an event file descriptor. The client configures +how the server signals an interrupt with ``VFIO_USER_SET_IRQS`` messages. + +Device Read and Write +^^^^^^^^^^^^^^^^^^^^^ + +When the guest executes load or store operations to an unmapped device region, +the client forwards these operations to the server with +``VFIO_USER_REGION_READ`` or ``VFIO_USER_REGION_WRITE`` messages. The server +will reply with data from the device on read operations or an acknowledgement on +write operations. See `Read and Write Operations`_. + +Client memory access +-------------------- + +The client uses ``VFIO_USER_DMA_MAP`` and ``VFIO_USER_DMA_UNMAP`` messages to +inform the server of the valid DMA ranges that the server can access on behalf +of a device (typically, VM guest memory). DMA memory may be accessed by the +server via ``VFIO_USER_DMA_READ`` and ``VFIO_USER_DMA_WRITE`` messages over the +socket. In this case, the "DMA" part of the naming is a misnomer. + +Actual direct memory access of client memory from the server is possible if the +client provides file descriptors the server can ``mmap()``. Note that ``mmap()`` +privileges cannot be revoked by the client, therefore file descriptors should +only be exported in environments where the client trusts the server not to +corrupt guest memory. + +See `Read and Write Operations`_. + +Client/server interactions +========================== + +Socket +------ + +A server can serve: + +1) one or more clients, and/or +2) one or more virtual devices, belonging to one or more clients. + +The current protocol specification requires a dedicated socket per +client/server connection. It is a server-side implementation detail whether a +single server handles multiple virtual devices from the same or multiple +clients. The location of the socket is implementation-specific. Multiplexing +clients, devices, and servers over the same socket is not supported in this +version of the protocol. + +Authentication +-------------- + +For ``AF_UNIX``, we rely on OS mandatory access controls on the socket files, +therefore it is up to the management layer to set up the socket as required. +Socket types that span guests or hosts will require a proper authentication +mechanism. Defining that mechanism is deferred to a future version of the +protocol. + +Command Concurrency +------------------- + +A client may pipeline multiple commands without waiting for previous command +replies. The server will process commands in the order they are received. A +consequence of this is if a client issues a command with the *No_reply* bit, +then subsequently issues a command without *No_reply*, the older command will +have been processed before the reply to the younger command is sent by the +server. The client must be aware of the device's capability to process +concurrent commands if pipelining is used. For example, pipelining allows +multiple client threads to concurrently access device regions; the client must +ensure these accesses obey device semantics. + +An example is a frame buffer device, where the device may allow concurrent +access to different areas of video memory, but may have indeterminate behavior +if concurrent accesses are performed to command or status registers. + +Note that unrelated messages sent from the server to the client can appear in +between a client to server request/reply and vice versa. + +Implementers should be prepared for certain commands to exhibit potentially +unbounded latencies. For example, ``VFIO_USER_DEVICE_RESET`` may take an +arbitrarily long time to complete; clients should take care not to block +unnecessarily. + +Socket Disconnection Behavior +----------------------------- +The server and the client can disconnect from each other, either intentionally +or unexpectedly. Both the client and the server need to know how to handle such +events. + +Server Disconnection +^^^^^^^^^^^^^^^^^^^^ +A server disconnecting from the client may indicate that: + +1) A virtual device has been restarted, either intentionally (e.g. because of a + device update) or unintentionally (e.g. because of a crash). +2) A virtual device has been shut down with no intention to be restarted. + +It is impossible for the client to know whether or not a failure is +intermittent or innocuous and should be retried, therefore the client should +reset the VFIO device when it detects the socket has been disconnected. +Error recovery will be driven by the guest's device error handling +behavior. + +Client Disconnection +^^^^^^^^^^^^^^^^^^^^ +The client disconnecting from the server primarily means that the client +has exited. Currently, this means that the guest is shut down so the device is +no longer needed therefore the server can automatically exit. However, there +can be cases where a client disconnection should not result in a server exit: + +1) A single server serving multiple clients. +2) A multi-process QEMU upgrading itself step by step, which is not yet + implemented. + +Therefore in order for the protocol to be forward compatible, the server should +respond to a client disconnection as follows: + + - all client memory regions are unmapped and cleaned up (including closing any + passed file descriptors) + - all IRQ file descriptors passed from the old client are closed + - the device state should otherwise be retained + +The expectation is that when a client reconnects, it will re-establish IRQ and +client memory mappings. + +If anything happens to the client (such as qemu really did exit), the control +stack will know about it and can clean up resources accordingly. + +Security Considerations +----------------------- + +Speaking generally, vfio-user clients should not trust servers, and vice versa. +Standard tools and mechanisms should be used on both sides to validate input and +prevent against denial of service scenarios, buffer overflow, etc. + +Request Retry and Response Timeout +---------------------------------- +A failed command is a command that has been successfully sent and has been +responded to with an error code. Failure to send the command in the first place +(e.g. because the socket is disconnected) is a different type of error examined +earlier in the disconnect section. + +.. Note:: + QEMU's VFIO retries certain operations if they fail. While this makes sense + for real HW, we don't know for sure whether it makes sense for virtual + devices. + +Defining a retry and timeout scheme is deferred to a future version of the +protocol. + +Message sizes +------------- + +Some requests have an ``argsz`` field. In a request, it defines the maximum +expected reply payload size, which should be at least the size of the fixed +reply payload headers defined here. The *request* payload size is defined by the +usual ``msg_size`` field in the header, not the ``argsz`` field. + +In a reply, the server sets ``argsz`` field to the size needed for a full +payload size. This may be less than the requested maximum size. This may be +larger than the requested maximum size: in that case, the full payload is not +included in the reply, but the ``argsz`` field in the reply indicates the needed +size, allowing a client to allocate a larger buffer for holding the reply before +trying again. + +In addition, during negotiation (see `Version`_), the client and server may +each specify a ``max_data_xfer_size`` value; this defines the maximum data that +may be read or written via one of the ``VFIO_USER_DMA/REGION_READ/WRITE`` +messages; see `Read and Write Operations`_. + +Protocol Specification +====================== + +To distinguish from the base VFIO symbols, all vfio-user symbols are prefixed +with ``vfio_user`` or ``VFIO_USER``. In this revision, all data is in the +endianness of the host system, although this may be relaxed in future +revisions in cases where the client and server run on different hosts +with different endianness. + +Unless otherwise specified, all sizes should be presumed to be in bytes. + +.. _Commands: + +Commands +-------- +The following table lists the VFIO message command IDs, and whether the +message command is sent from the client or the server. + +====================================== ========= ================= +Name Command Request Direction +====================================== ========= ================= +``VFIO_USER_VERSION`` 1 client -> server +``VFIO_USER_DMA_MAP`` 2 client -> server +``VFIO_USER_DMA_UNMAP`` 3 client -> server +``VFIO_USER_DEVICE_GET_INFO`` 4 client -> server +``VFIO_USER_DEVICE_GET_REGION_INFO`` 5 client -> server +``VFIO_USER_DEVICE_GET_REGION_IO_FDS`` 6 client -> server +``VFIO_USER_DEVICE_GET_IRQ_INFO`` 7 client -> server +``VFIO_USER_DEVICE_SET_IRQS`` 8 client -> server +``VFIO_USER_REGION_READ`` 9 client -> server +``VFIO_USER_REGION_WRITE`` 10 client -> server +``VFIO_USER_DMA_READ`` 11 server -> client +``VFIO_USER_DMA_WRITE`` 12 server -> client +``VFIO_USER_DEVICE_RESET`` 13 client -> server +``VFIO_USER_REGION_WRITE_MULTI`` 15 client -> server +====================================== ========= ================= + +Header +------ + +All messages, both command messages and reply messages, are preceded by a +16-byte header that contains basic information about the message. The header is +followed by message-specific data described in the sections below. + ++----------------+--------+-------------+ +| Name | Offset | Size | ++================+========+=============+ +| Message ID | 0 | 2 | ++----------------+--------+-------------+ +| Command | 2 | 2 | ++----------------+--------+-------------+ +| Message size | 4 | 4 | ++----------------+--------+-------------+ +| Flags | 8 | 4 | ++----------------+--------+-------------+ +| | +-----+------------+ | +| | | Bit | Definition | | +| | +=====+============+ | +| | | 0-3 | Type | | +| | +-----+------------+ | +| | | 4 | No_reply | | +| | +-----+------------+ | +| | | 5 | Error | | +| | +-----+------------+ | ++----------------+--------+-------------+ +| Error | 12 | 4 | ++----------------+--------+-------------+ +| | 16 | variable | ++----------------+--------+-------------+ + +* *Message ID* identifies the message, and is echoed in the command's reply + message. Message IDs belong entirely to the sender, can be re-used (even + concurrently) and the receiver must not make any assumptions about their + uniqueness. +* *Command* specifies the command to be executed, listed in Commands_. It is + also set in the reply header. +* *Message size* contains the size of the entire message, including the header. +* *Flags* contains attributes of the message: + + * The *Type* bits indicate the message type. + + * *Command* (value 0x0) indicates a command message. + * *Reply* (value 0x1) indicates a reply message acknowledging a previous + command with the same message ID. + * *No_reply* in a command message indicates that no reply is needed for this + command. This is commonly used when multiple commands are sent, and only + the last needs acknowledgement. + * *Error* in a reply message indicates the command being acknowledged had + an error. In this case, the *Error* field will be valid. + +* *Error* in a reply message is an optional UNIX errno value. It may be zero + even if the Error bit is set in Flags. It is reserved in a command message. + +Each command message in Commands_ must be replied to with a reply message, +unless the message sets the *No_Reply* bit. The reply consists of the header +with the *Reply* bit set, plus any additional data. + +If an error occurs, the reply message must only include the reply header. + +As the header is standard in both requests and replies, it is not included in +the command-specific specifications below; each message definition should be +appended to the standard header, and the offsets are given from the end of the +standard header. + +``VFIO_USER_VERSION`` +--------------------- + +.. _Version: + +This is the initial message sent by the client after the socket connection is +established; the same format is used for the server's reply. + +Upon establishing a connection, the client must send a ``VFIO_USER_VERSION`` +message proposing a protocol version and a set of capabilities. The server +compares these with the versions and capabilities it supports and sends a +``VFIO_USER_VERSION`` reply according to the following rules. + +* The major version in the reply must be the same as proposed. If the client + does not support the proposed major, it closes the connection. +* The minor version in the reply must be equal to or less than the minor + version proposed. +* The capability list must be a subset of those proposed. If the server + requires a capability the client did not include, it closes the connection. + +The protocol major version will only change when incompatible protocol changes +are made, such as changing the message format. The minor version may change +when compatible changes are made, such as adding new messages or capabilities, +Both the client and server must support all minor versions less than the +maximum minor version it supports. E.g., an implementation that supports +version 1.3 must also support 1.0 through 1.2. + +When making a change to this specification, the protocol version number must +be included in the form "added in version X.Y" + +Request +^^^^^^^ + +============== ====== ==== +Name Offset Size +============== ====== ==== +version major 0 2 +version minor 2 2 +version data 4 variable (including terminating NUL). Optional. +============== ====== ==== + +The version data is an optional UTF-8 encoded JSON byte array with the following +format: + ++--------------+--------+-----------------------------------+ +| Name | Type | Description | ++==============+========+===================================+ +| capabilities | object | Contains common capabilities that | +| | | the sender supports. Optional. | ++--------------+--------+-----------------------------------+ + +Capabilities: + ++--------------------+---------+------------------------------------------------+ +| Name | Type | Description | ++====================+=========+================================================+ +| max_msg_fds | number | Maximum number of file descriptors that can be | +| | | received by the sender in one message. | +| | | Optional. If not specified then the receiver | +| | | must assume a value of ``1``. | ++--------------------+---------+------------------------------------------------+ +| max_data_xfer_size | number | Maximum ``count`` for data transfer messages; | +| | | see `Read and Write Operations`_. Optional, | +| | | with a default value of 1048576 bytes. | ++--------------------+---------+------------------------------------------------+ +| pgsizes | number | Page sizes supported in DMA map operations | +| | | or'ed together. Optional, with a default value | +| | | of supporting only 4k pages. | ++--------------------+---------+------------------------------------------------+ +| max_dma_maps | number | Maximum number DMA map windows that can be | +| | | valid simultaneously. Optional, with a | +| | | value of 65535 (64k-1). | ++--------------------+---------+------------------------------------------------+ +| migration | object | Migration capability parameters. If missing | +| | | then migration is not supported by the sender. | ++--------------------+---------+------------------------------------------------+ +| write_multiple | boolean | ``VFIO_USER_REGION_WRITE_MULTI`` messages | +| | | are supported if the value is ``true``. | ++--------------------+---------+------------------------------------------------+ + +The migration capability contains the following name/value pairs: + ++-----------------+--------+--------------------------------------------------+ +| Name | Type | Description | ++=================+========+==================================================+ +| pgsize | number | Page size of dirty pages bitmap. The smallest | +| | | between the client and the server is used. | ++-----------------+--------+--------------------------------------------------+ +| max_bitmap_size | number | Maximum bitmap size in ``VFIO_USER_DIRTY_PAGES`` | +| | | and ``VFIO_DMA_UNMAP`` messages. Optional, | +| | | with a default value of 256MB. | ++-----------------+--------+--------------------------------------------------+ + +Reply +^^^^^ + +The same message format is used in the server's reply with the semantics +described above. + +``VFIO_USER_DMA_MAP`` +--------------------- + +This command message is sent by the client to the server to inform it of the +memory regions the server can access. It must be sent before the server can +perform any DMA to the client. It is normally sent directly after the version +handshake is completed, but may also occur when memory is added to the client, +or if the client uses a vIOMMU. + +Request +^^^^^^^ + +The request payload for this message is a structure of the following format: + ++-------------+--------+-------------+ +| Name | Offset | Size | ++=============+========+=============+ +| argsz | 0 | 4 | ++-------------+--------+-------------+ +| flags | 4 | 4 | ++-------------+--------+-------------+ +| | +-----+------------+ | +| | | Bit | Definition | | +| | +=====+============+ | +| | | 0 | readable | | +| | +-----+------------+ | +| | | 1 | writeable | | +| | +-----+------------+ | ++-------------+--------+-------------+ +| offset | 8 | 8 | ++-------------+--------+-------------+ +| address | 16 | 8 | ++-------------+--------+-------------+ +| size | 24 | 8 | ++-------------+--------+-------------+ + +* *argsz* is the size of the above structure. Note there is no reply payload, + so this field differs from other message types. +* *flags* contains the following region attributes: + + * *readable* indicates that the region can be read from. + + * *writeable* indicates that the region can be written to. + +* *offset* is the file offset of the region with respect to the associated file + descriptor, or zero if the region is not mappable +* *address* is the base DMA address of the region. +* *size* is the size of the region. + +This structure is 32 bytes in size, so the message size is 16 + 32 bytes. + +If the DMA region being added can be directly mapped by the server, a file +descriptor must be sent as part of the message meta-data. The region can be +mapped via the mmap() system call. On ``AF_UNIX`` sockets, the file descriptor +must be passed as ``SCM_RIGHTS`` type ancillary data. Otherwise, if the DMA +region cannot be directly mapped by the server, no file descriptor must be sent +as part of the message meta-data and the DMA region can be accessed by the +server using ``VFIO_USER_DMA_READ`` and ``VFIO_USER_DMA_WRITE`` messages, +explained in `Read and Write Operations`_. A command to map over an existing +region must be failed by the server with ``EEXIST`` set in error field in the +reply. + +Reply +^^^^^ + +There is no payload in the reply message. + +``VFIO_USER_DMA_UNMAP`` +----------------------- + +This command message is sent by the client to the server to inform it that a +DMA region, previously made available via a ``VFIO_USER_DMA_MAP`` command +message, is no longer available for DMA. It typically occurs when memory is +subtracted from the client or if the client uses a vIOMMU. The DMA region is +described by the following structure: + +Request +^^^^^^^ + +The request payload for this message is a structure of the following format: + ++--------------+--------+------------------------+ +| Name | Offset | Size | ++==============+========+========================+ +| argsz | 0 | 4 | ++--------------+--------+------------------------+ +| flags | 4 | 4 | ++--------------+--------+------------------------+ +| address | 8 | 8 | ++--------------+--------+------------------------+ +| size | 16 | 8 | ++--------------+--------+------------------------+ + +* *argsz* is the maximum size of the reply payload. +* *flags* is unused in this version. +* *address* is the base DMA address of the DMA region. +* *size* is the size of the DMA region. + +The address and size of the DMA region being unmapped must match exactly a +previous mapping. + +Reply +^^^^^ + +Upon receiving a ``VFIO_USER_DMA_UNMAP`` command, if the file descriptor is +mapped then the server must release all references to that DMA region before +replying, which potentially includes in-flight DMA transactions. + +The server responds with the original DMA entry in the request. + + +``VFIO_USER_DEVICE_GET_INFO`` +----------------------------- + +This command message is sent by the client to the server to query for basic +information about the device. + +Request +^^^^^^^ + ++-------------+--------+--------------------------+ +| Name | Offset | Size | ++=============+========+==========================+ +| argsz | 0 | 4 | ++-------------+--------+--------------------------+ +| flags | 4 | 4 | ++-------------+--------+--------------------------+ +| | +-----+-------------------------+ | +| | | Bit | Definition | | +| | +=====+=========================+ | +| | | 0 | VFIO_DEVICE_FLAGS_RESET | | +| | +-----+-------------------------+ | +| | | 1 | VFIO_DEVICE_FLAGS_PCI | | +| | +-----+-------------------------+ | ++-------------+--------+--------------------------+ +| num_regions | 8 | 4 | ++-------------+--------+--------------------------+ +| num_irqs | 12 | 4 | ++-------------+--------+--------------------------+ + +* *argsz* is the maximum size of the reply payload +* all other fields must be zero. + +Reply +^^^^^ + ++-------------+--------+--------------------------+ +| Name | Offset | Size | ++=============+========+==========================+ +| argsz | 0 | 4 | ++-------------+--------+--------------------------+ +| flags | 4 | 4 | ++-------------+--------+--------------------------+ +| | +-----+-------------------------+ | +| | | Bit | Definition | | +| | +=====+=========================+ | +| | | 0 | VFIO_DEVICE_FLAGS_RESET | | +| | +-----+-------------------------+ | +| | | 1 | VFIO_DEVICE_FLAGS_PCI | | +| | +-----+-------------------------+ | ++-------------+--------+--------------------------+ +| num_regions | 8 | 4 | ++-------------+--------+--------------------------+ +| num_irqs | 12 | 4 | ++-------------+--------+--------------------------+ + +* *argsz* is the size required for the full reply payload (16 bytes today) +* *flags* contains the following device attributes. + + * ``VFIO_DEVICE_FLAGS_RESET`` indicates that the device supports the + ``VFIO_USER_DEVICE_RESET`` message. + * ``VFIO_DEVICE_FLAGS_PCI`` indicates that the device is a PCI device. + +* *num_regions* is the number of memory regions that the device exposes. +* *num_irqs* is the number of distinct interrupt types that the device supports. + +This version of the protocol only supports PCI devices. Additional devices may +be supported in future versions. + +``VFIO_USER_DEVICE_GET_REGION_INFO`` +------------------------------------ + +This command message is sent by the client to the server to query for +information about device regions. The VFIO region info structure is defined in +```` (``struct vfio_region_info``). + +Request +^^^^^^^ + ++------------+--------+------------------------------+ +| Name | Offset | Size | ++============+========+==============================+ +| argsz | 0 | 4 | ++------------+--------+------------------------------+ +| flags | 4 | 4 | ++------------+--------+------------------------------+ +| index | 8 | 4 | ++------------+--------+------------------------------+ +| cap_offset | 12 | 4 | ++------------+--------+------------------------------+ +| size | 16 | 8 | ++------------+--------+------------------------------+ +| offset | 24 | 8 | ++------------+--------+------------------------------+ + +* *argsz* the maximum size of the reply payload +* *index* is the index of memory region being queried, it is the only field + that is required to be set in the command message. +* all other fields must be zero. + +Reply +^^^^^ + ++------------+--------+------------------------------+ +| Name | Offset | Size | ++============+========+==============================+ +| argsz | 0 | 4 | ++------------+--------+------------------------------+ +| flags | 4 | 4 | ++------------+--------+------------------------------+ +| | +-----+-----------------------------+ | +| | | Bit | Definition | | +| | +=====+=============================+ | +| | | 0 | VFIO_REGION_INFO_FLAG_READ | | +| | +-----+-----------------------------+ | +| | | 1 | VFIO_REGION_INFO_FLAG_WRITE | | +| | +-----+-----------------------------+ | +| | | 2 | VFIO_REGION_INFO_FLAG_MMAP | | +| | +-----+-----------------------------+ | +| | | 3 | VFIO_REGION_INFO_FLAG_CAPS | | +| | +-----+-----------------------------+ | ++------------+--------+------------------------------+ ++------------+--------+------------------------------+ +| index | 8 | 4 | ++------------+--------+------------------------------+ +| cap_offset | 12 | 4 | ++------------+--------+------------------------------+ +| size | 16 | 8 | ++------------+--------+------------------------------+ +| offset | 24 | 8 | ++------------+--------+------------------------------+ + +* *argsz* is the size required for the full reply payload (region info structure + plus the size of any region capabilities) +* *flags* are attributes of the region: + + * ``VFIO_REGION_INFO_FLAG_READ`` allows client read access to the region. + * ``VFIO_REGION_INFO_FLAG_WRITE`` allows client write access to the region. + * ``VFIO_REGION_INFO_FLAG_MMAP`` specifies the client can mmap() the region. + When this flag is set, the reply will include a file descriptor in its + meta-data. On ``AF_UNIX`` sockets, the file descriptors will be passed as + ``SCM_RIGHTS`` type ancillary data. + * ``VFIO_REGION_INFO_FLAG_CAPS`` indicates additional capabilities found in the + reply. + +* *index* is the index of memory region being queried, it is the only field + that is required to be set in the command message. +* *cap_offset* describes where additional region capabilities can be found. + cap_offset is relative to the beginning of the VFIO region info structure. + The data structure it points is a VFIO cap header defined in + ````. +* *size* is the size of the region. +* *offset* is the offset that should be given to the mmap() system call for + regions with the MMAP attribute. It is also used as the base offset when + mapping a VFIO sparse mmap area, described below. + +VFIO region capabilities +"""""""""""""""""""""""" + +The VFIO region information can also include a capabilities list. This list is +similar to a PCI capability list - each entry has a common header that +identifies a capability and where the next capability in the list can be found. +The VFIO capability header format is defined in ```` (``struct +vfio_info_cap_header``). + +VFIO cap header format +"""""""""""""""""""""" + ++---------+--------+------+ +| Name | Offset | Size | ++=========+========+======+ +| id | 0 | 2 | ++---------+--------+------+ +| version | 2 | 2 | ++---------+--------+------+ +| next | 4 | 4 | ++---------+--------+------+ + +* *id* is the capability identity. +* *version* is a capability-specific version number. +* *next* specifies the offset of the next capability in the capability list. It + is relative to the beginning of the VFIO region info structure. + +VFIO sparse mmap cap header +""""""""""""""""""""""""""" + ++------------------+----------------------------------+ +| Name | Value | ++==================+==================================+ +| id | VFIO_REGION_INFO_CAP_SPARSE_MMAP | ++------------------+----------------------------------+ +| version | 0x1 | ++------------------+----------------------------------+ +| next | | ++------------------+----------------------------------+ +| sparse mmap info | VFIO region info sparse mmap | ++------------------+----------------------------------+ + +This capability is defined when only a subrange of the region supports +direct access by the client via mmap(). The VFIO sparse mmap area is defined in +```` (``struct vfio_region_sparse_mmap_area`` and ``struct +vfio_region_info_cap_sparse_mmap``). + +VFIO region info cap sparse mmap +"""""""""""""""""""""""""""""""" + ++----------+--------+------+ +| Name | Offset | Size | ++==========+========+======+ +| nr_areas | 0 | 4 | ++----------+--------+------+ +| reserved | 4 | 4 | ++----------+--------+------+ +| offset | 8 | 8 | ++----------+--------+------+ +| size | 16 | 8 | ++----------+--------+------+ +| ... | | | ++----------+--------+------+ + +* *nr_areas* is the number of sparse mmap areas in the region. +* *offset* and size describe a single area that can be mapped by the client. + There will be *nr_areas* pairs of offset and size. The offset will be added to + the base offset given in the ``VFIO_USER_DEVICE_GET_REGION_INFO`` to form the + offset argument of the subsequent mmap() call. + +The VFIO sparse mmap area is defined in ```` (``struct +vfio_region_info_cap_sparse_mmap``). + + +``VFIO_USER_DEVICE_GET_REGION_IO_FDS`` +-------------------------------------- + +Clients can access regions via ``VFIO_USER_REGION_READ/WRITE`` or, if provided, by +``mmap()`` of a file descriptor provided by the server. + +``VFIO_USER_DEVICE_GET_REGION_IO_FDS`` provides an alternative access mechanism via +file descriptors. This is an optional feature intended for performance +improvements where an underlying sub-system (such as KVM) supports communication +across such file descriptors to the vfio-user server, without needing to +round-trip through the client. + +The server returns an array of sub-regions for the requested region. Each +sub-region describes a span (offset and size) of a region, along with the +requested file descriptor notification mechanism to use. Each sub-region in the +response message may choose to use a different method, as defined below. The +two mechanisms supported in this specification are ioeventfds and ioregionfds. + +The server in addition returns a file descriptor in the ancillary data; clients +are expected to configure each sub-region's file descriptor with the requested +notification method. For example, a client could configure KVM with the +requested ioeventfd via a ``KVM_IOEVENTFD`` ``ioctl()``. + +Request +^^^^^^^ + ++-------------+--------+------+ +| Name | Offset | Size | ++=============+========+======+ +| argsz | 0 | 4 | ++-------------+--------+------+ +| flags | 4 | 4 | ++-------------+--------+------+ +| index | 8 | 4 | ++-------------+--------+------+ +| count | 12 | 4 | ++-------------+--------+------+ + +* *argsz* the maximum size of the reply payload +* *index* is the index of memory region being queried +* all other fields must be zero + +The client must set ``flags`` to zero and specify the region being queried in +the ``index``. + +Reply +^^^^^ + ++-------------+--------+------+ +| Name | Offset | Size | ++=============+========+======+ +| argsz | 0 | 4 | ++-------------+--------+------+ +| flags | 4 | 4 | ++-------------+--------+------+ +| index | 8 | 4 | ++-------------+--------+------+ +| count | 12 | 4 | ++-------------+--------+------+ +| sub-regions | 16 | ... | ++-------------+--------+------+ + +* *argsz* is the size of the region IO FD info structure plus the + total size of the sub-region array. Thus, each array entry "i" is at offset + i * ((argsz - 32) / count). Note that currently this is 40 bytes for both IO + FD types, but this is not to be relied on. As elsewhere, this indicates the + full reply payload size needed. +* *flags* must be zero +* *index* is the index of memory region being queried +* *count* is the number of sub-regions in the array +* *sub-regions* is the array of Sub-Region IO FD info structures + +The reply message will additionally include at least one file descriptor in the +ancillary data. Note that more than one sub-region may share the same file +descriptor. + +Note that it is the client's responsibility to verify the requested values (for +example, that the requested offset does not exceed the region's bounds). + +Each sub-region given in the response has one of two possible structures, +depending whether *type* is ``VFIO_USER_IO_FD_TYPE_IOEVENTFD`` or +``VFIO_USER_IO_FD_TYPE_IOREGIONFD``: + +Sub-Region IO FD info format (ioeventfd) +"""""""""""""""""""""""""""""""""""""""" + ++-----------+--------+------+ +| Name | Offset | Size | ++===========+========+======+ +| offset | 0 | 8 | ++-----------+--------+------+ +| size | 8 | 8 | ++-----------+--------+------+ +| fd_index | 16 | 4 | ++-----------+--------+------+ +| type | 20 | 4 | ++-----------+--------+------+ +| flags | 24 | 4 | ++-----------+--------+------+ +| padding | 28 | 4 | ++-----------+--------+------+ +| datamatch | 32 | 8 | ++-----------+--------+------+ + +* *offset* is the offset of the start of the sub-region within the region + requested ("physical address offset" for the region) +* *size* is the length of the sub-region. This may be zero if the access size is + not relevant, which may allow for optimizations +* *fd_index* is the index in the ancillary data of the FD to use for ioeventfd + notification; it may be shared. +* *type* is ``VFIO_USER_IO_FD_TYPE_IOEVENTFD`` +* *flags* is any of: + + * ``KVM_IOEVENTFD_FLAG_DATAMATCH`` + * ``KVM_IOEVENTFD_FLAG_PIO`` + * ``KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY`` (FIXME: makes sense?) + +* *datamatch* is the datamatch value if needed + +See https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt, *4.59 +KVM_IOEVENTFD* for further context on the ioeventfd-specific fields. + +Sub-Region IO FD info format (ioregionfd) +""""""""""""""""""""""""""""""""""""""""" + ++-----------+--------+------+ +| Name | Offset | Size | ++===========+========+======+ +| offset | 0 | 8 | ++-----------+--------+------+ +| size | 8 | 8 | ++-----------+--------+------+ +| fd_index | 16 | 4 | ++-----------+--------+------+ +| type | 20 | 4 | ++-----------+--------+------+ +| flags | 24 | 4 | ++-----------+--------+------+ +| padding | 28 | 4 | ++-----------+--------+------+ +| user_data | 32 | 8 | ++-----------+--------+------+ + +* *offset* is the offset of the start of the sub-region within the region + requested ("physical address offset" for the region) +* *size* is the length of the sub-region. This may be zero if the access size is + not relevant, which may allow for optimizations; ``KVM_IOREGION_POSTED_WRITES`` + must be set in *flags* in this case +* *fd_index* is the index in the ancillary data of the FD to use for ioregionfd + messages; it may be shared +* *type* is ``VFIO_USER_IO_FD_TYPE_IOREGIONFD`` +* *flags* is any of: + + * ``KVM_IOREGION_PIO`` + * ``KVM_IOREGION_POSTED_WRITES`` + +* *user_data* is an opaque value passed back to the server via a message on the + file descriptor + +For further information on the ioregionfd-specific fields, see: +https://lore.kernel.org/kvm/cover.1613828726.git.eafanasova@gmail.com/ + +(FIXME: update with final API docs.) + +``VFIO_USER_DEVICE_GET_IRQ_INFO`` +--------------------------------- + +This command message is sent by the client to the server to query for +information about device interrupt types. The VFIO IRQ info structure is +defined in ```` (``struct vfio_irq_info``). + +Request +^^^^^^^ + ++-------+--------+---------------------------+ +| Name | Offset | Size | ++=======+========+===========================+ +| argsz | 0 | 4 | ++-------+--------+---------------------------+ +| flags | 4 | 4 | ++-------+--------+---------------------------+ +| | +-----+--------------------------+ | +| | | Bit | Definition | | +| | +=====+==========================+ | +| | | 0 | VFIO_IRQ_INFO_EVENTFD | | +| | +-----+--------------------------+ | +| | | 1 | VFIO_IRQ_INFO_MASKABLE | | +| | +-----+--------------------------+ | +| | | 2 | VFIO_IRQ_INFO_AUTOMASKED | | +| | +-----+--------------------------+ | +| | | 3 | VFIO_IRQ_INFO_NORESIZE | | +| | +-----+--------------------------+ | ++-------+--------+---------------------------+ +| index | 8 | 4 | ++-------+--------+---------------------------+ +| count | 12 | 4 | ++-------+--------+---------------------------+ + +* *argsz* is the maximum size of the reply payload (16 bytes today) +* index is the index of IRQ type being queried (e.g. ``VFIO_PCI_MSIX_IRQ_INDEX``) +* all other fields must be zero + +Reply +^^^^^ + ++-------+--------+---------------------------+ +| Name | Offset | Size | ++=======+========+===========================+ +| argsz | 0 | 4 | ++-------+--------+---------------------------+ +| flags | 4 | 4 | ++-------+--------+---------------------------+ +| | +-----+--------------------------+ | +| | | Bit | Definition | | +| | +=====+==========================+ | +| | | 0 | VFIO_IRQ_INFO_EVENTFD | | +| | +-----+--------------------------+ | +| | | 1 | VFIO_IRQ_INFO_MASKABLE | | +| | +-----+--------------------------+ | +| | | 2 | VFIO_IRQ_INFO_AUTOMASKED | | +| | +-----+--------------------------+ | +| | | 3 | VFIO_IRQ_INFO_NORESIZE | | +| | +-----+--------------------------+ | ++-------+--------+---------------------------+ +| index | 8 | 4 | ++-------+--------+---------------------------+ +| count | 12 | 4 | ++-------+--------+---------------------------+ + +* *argsz* is the size required for the full reply payload (16 bytes today) +* *flags* defines IRQ attributes: + + * ``VFIO_IRQ_INFO_EVENTFD`` indicates the IRQ type can support server eventfd + signalling. + * ``VFIO_IRQ_INFO_MASKABLE`` indicates that the IRQ type supports the ``MASK`` + and ``UNMASK`` actions in a ``VFIO_USER_DEVICE_SET_IRQS`` message. + * ``VFIO_IRQ_INFO_AUTOMASKED`` indicates the IRQ type masks itself after being + triggered, and the client must send an ``UNMASK`` action to receive new + interrupts. + * ``VFIO_IRQ_INFO_NORESIZE`` indicates ``VFIO_USER_SET_IRQS`` operations setup + interrupts as a set, and new sub-indexes cannot be enabled without disabling + the entire type. +* index is the index of IRQ type being queried +* count describes the number of interrupts of the queried type. + +``VFIO_USER_DEVICE_SET_IRQS`` +----------------------------- + +This command message is sent by the client to the server to set actions for +device interrupt types. The VFIO IRQ set structure is defined in +```` (``struct vfio_irq_set``). + +Request +^^^^^^^ + ++-------+--------+------------------------------+ +| Name | Offset | Size | ++=======+========+==============================+ +| argsz | 0 | 4 | ++-------+--------+------------------------------+ +| flags | 4 | 4 | ++-------+--------+------------------------------+ +| | +-----+-----------------------------+ | +| | | Bit | Definition | | +| | +=====+=============================+ | +| | | 0 | VFIO_IRQ_SET_DATA_NONE | | +| | +-----+-----------------------------+ | +| | | 1 | VFIO_IRQ_SET_DATA_BOOL | | +| | +-----+-----------------------------+ | +| | | 2 | VFIO_IRQ_SET_DATA_EVENTFD | | +| | +-----+-----------------------------+ | +| | | 3 | VFIO_IRQ_SET_ACTION_MASK | | +| | +-----+-----------------------------+ | +| | | 4 | VFIO_IRQ_SET_ACTION_UNMASK | | +| | +-----+-----------------------------+ | +| | | 5 | VFIO_IRQ_SET_ACTION_TRIGGER | | +| | +-----+-----------------------------+ | ++-------+--------+------------------------------+ +| index | 8 | 4 | ++-------+--------+------------------------------+ +| start | 12 | 4 | ++-------+--------+------------------------------+ +| count | 16 | 4 | ++-------+--------+------------------------------+ +| data | 20 | variable | ++-------+--------+------------------------------+ + +* *argsz* is the size of the VFIO IRQ set request payload, including any *data* + field. Note there is no reply payload, so this field differs from other + message types. +* *flags* defines the action performed on the interrupt range. The ``DATA`` + flags describe the data field sent in the message; the ``ACTION`` flags + describe the action to be performed. The flags are mutually exclusive for + both sets. + + * ``VFIO_IRQ_SET_DATA_NONE`` indicates there is no data field in the command. + The action is performed unconditionally. + * ``VFIO_IRQ_SET_DATA_BOOL`` indicates the data field is an array of boolean + bytes. The action is performed if the corresponding boolean is true. + * ``VFIO_IRQ_SET_DATA_EVENTFD`` indicates an array of event file descriptors + was sent in the message meta-data. These descriptors will be signalled when + the action defined by the action flags occurs. In ``AF_UNIX`` sockets, the + descriptors are sent as ``SCM_RIGHTS`` type ancillary data. + If no file descriptors are provided, this de-assigns the specified + previously configured interrupts. + * ``VFIO_IRQ_SET_ACTION_MASK`` indicates a masking event. It can be used with + ``VFIO_IRQ_SET_DATA_BOOL`` or ``VFIO_IRQ_SET_DATA_NONE`` to mask an interrupt, + or with ``VFIO_IRQ_SET_DATA_EVENTFD`` to generate an event when the guest masks + the interrupt. + * ``VFIO_IRQ_SET_ACTION_UNMASK`` indicates an unmasking event. It can be used + with ``VFIO_IRQ_SET_DATA_BOOL`` or ``VFIO_IRQ_SET_DATA_NONE`` to unmask an + interrupt, or with ``VFIO_IRQ_SET_DATA_EVENTFD`` to generate an event when the + guest unmasks the interrupt. + * ``VFIO_IRQ_SET_ACTION_TRIGGER`` indicates a triggering event. It can be used + with ``VFIO_IRQ_SET_DATA_BOOL`` or ``VFIO_IRQ_SET_DATA_NONE`` to trigger an + interrupt, or with ``VFIO_IRQ_SET_DATA_EVENTFD`` to generate an event when the + server triggers the interrupt. + +* *index* is the index of IRQ type being setup. +* *start* is the start of the sub-index being set. +* *count* describes the number of sub-indexes being set. As a special case, a + count (and start) of 0, with data flags of ``VFIO_IRQ_SET_DATA_NONE`` disables + all interrupts of the index. +* *data* is an optional field included when the + ``VFIO_IRQ_SET_DATA_BOOL`` flag is present. It contains an array of booleans + that specify whether the action is to be performed on the corresponding + index. It's used when the action is only performed on a subset of the range + specified. + +Not all interrupt types support every combination of data and action flags. +The client must know the capabilities of the device and IRQ index before it +sends a ``VFIO_USER_DEVICE_SET_IRQ`` message. + +In typical operation, a specific IRQ may operate as follows: + +1. The client sends a ``VFIO_USER_DEVICE_SET_IRQ`` message with + ``flags=(VFIO_IRQ_SET_DATA_EVENTFD|VFIO_IRQ_SET_ACTION_TRIGGER)`` along + with an eventfd. This associates the IRQ with a particular eventfd on the + server side. + +#. The client may send a ``VFIO_USER_DEVICE_SET_IRQ`` message with + ``flags=(VFIO_IRQ_SET_DATA_EVENTFD|VFIO_IRQ_SET_ACTION_MASK/UNMASK)`` along + with another eventfd. This associates the given eventfd with the + mask/unmask state on the server side. + +#. The server may trigger the IRQ by writing 1 to the eventfd. + +#. The server may mask/unmask an IRQ which will write 1 to the corresponding + mask/unmask eventfd, if there is one. + +5. A client may trigger a device IRQ itself, by sending a + ``VFIO_USER_DEVICE_SET_IRQ`` message with + ``flags=(VFIO_IRQ_SET_DATA_NONE/BOOL|VFIO_IRQ_SET_ACTION_TRIGGER)``. + +6. A client may mask or unmask the IRQ, by sending a + ``VFIO_USER_DEVICE_SET_IRQ`` message with + ``flags=(VFIO_IRQ_SET_DATA_NONE/BOOL|VFIO_IRQ_SET_ACTION_MASK/UNMASK)``. + +Reply +^^^^^ + +There is no payload in the reply. + +.. _Read and Write Operations: + +Note that all of these operations must be supported by the client and/or server, +even if the corresponding memory or device region has been shared as mappable. + +The ``count`` field must not exceed the value of ``max_data_xfer_size`` of the +peer, for both reads and writes. + +``VFIO_USER_REGION_READ`` +------------------------- + +If a device region is not mappable, it's not directly accessible by the client +via ``mmap()`` of the underlying file descriptor. In this case, a client can +read from a device region with this message. + +Request +^^^^^^^ + ++--------+--------+----------+ +| Name | Offset | Size | ++========+========+==========+ +| offset | 0 | 8 | ++--------+--------+----------+ +| region | 8 | 4 | ++--------+--------+----------+ +| count | 12 | 4 | ++--------+--------+----------+ + +* *offset* into the region being accessed. +* *region* is the index of the region being accessed. +* *count* is the size of the data to be transferred. + +Reply +^^^^^ + ++--------+--------+----------+ +| Name | Offset | Size | ++========+========+==========+ +| offset | 0 | 8 | ++--------+--------+----------+ +| region | 8 | 4 | ++--------+--------+----------+ +| count | 12 | 4 | ++--------+--------+----------+ +| data | 16 | variable | ++--------+--------+----------+ + +* *offset* into the region accessed. +* *region* is the index of the region accessed. +* *count* is the size of the data transferred. +* *data* is the data that was read from the device region. + +``VFIO_USER_REGION_WRITE`` +-------------------------- + +If a device region is not mappable, it's not directly accessible by the client +via mmap() of the underlying fd. In this case, a client can write to a device +region with this message. + +Request +^^^^^^^ + ++--------+--------+----------+ +| Name | Offset | Size | ++========+========+==========+ +| offset | 0 | 8 | ++--------+--------+----------+ +| region | 8 | 4 | ++--------+--------+----------+ +| count | 12 | 4 | ++--------+--------+----------+ +| data | 16 | variable | ++--------+--------+----------+ + +* *offset* into the region being accessed. +* *region* is the index of the region being accessed. +* *count* is the size of the data to be transferred. +* *data* is the data to write + +Reply +^^^^^ + ++--------+--------+----------+ +| Name | Offset | Size | ++========+========+==========+ +| offset | 0 | 8 | ++--------+--------+----------+ +| region | 8 | 4 | ++--------+--------+----------+ +| count | 12 | 4 | ++--------+--------+----------+ + +* *offset* into the region accessed. +* *region* is the index of the region accessed. +* *count* is the size of the data transferred. + +``VFIO_USER_DMA_READ`` +----------------------- + +If the client has not shared mappable memory, the server can use this message to +read from guest memory. + +Request +^^^^^^^ + ++---------+--------+----------+ +| Name | Offset | Size | ++=========+========+==========+ +| address | 0 | 8 | ++---------+--------+----------+ +| count | 8 | 8 | ++---------+--------+----------+ + +* *address* is the client DMA memory address being accessed. This address must have + been previously exported to the server with a ``VFIO_USER_DMA_MAP`` message. +* *count* is the size of the data to be transferred. + +Reply +^^^^^ + ++---------+--------+----------+ +| Name | Offset | Size | ++=========+========+==========+ +| address | 0 | 8 | ++---------+--------+----------+ +| count | 8 | 8 | ++---------+--------+----------+ +| data | 16 | variable | ++---------+--------+----------+ + +* *address* is the client DMA memory address being accessed. +* *count* is the size of the data transferred. +* *data* is the data read. + +``VFIO_USER_DMA_WRITE`` +----------------------- + +If the client has not shared mappable memory, the server can use this message to +write to guest memory. + +Request +^^^^^^^ + ++---------+--------+----------+ +| Name | Offset | Size | ++=========+========+==========+ +| address | 0 | 8 | ++---------+--------+----------+ +| count | 8 | 8 | ++---------+--------+----------+ +| data | 16 | variable | ++---------+--------+----------+ + +* *address* is the client DMA memory address being accessed. This address must have + been previously exported to the server with a ``VFIO_USER_DMA_MAP`` message. +* *count* is the size of the data to be transferred. +* *data* is the data to write + +Reply +^^^^^ + ++---------+--------+----------+ +| Name | Offset | Size | ++=========+========+==========+ +| address | 0 | 8 | ++---------+--------+----------+ +| count | 8 | 4 | ++---------+--------+----------+ + +* *address* is the client DMA memory address being accessed. +* *count* is the size of the data transferred. + +``VFIO_USER_DEVICE_RESET`` +-------------------------- + +This command message is sent from the client to the server to reset the device. +Neither the request or reply have a payload. + +``VFIO_USER_REGION_WRITE_MULTI`` +-------------------------------- + +This message can be used to coalesce multiple device write operations +into a single messgage. It is only used as an optimization when the +outgoing message queue is relatively full. + +Request +^^^^^^^ + ++---------+--------+----------+ +| Name | Offset | Size | ++=========+========+==========+ +| wr_cnt | 0 | 8 | ++---------+--------+----------+ +| wrs | 8 | variable | ++---------+--------+----------+ + +* *wr_cnt* is the number of device writes coalesced in the message +* *wrs* is an array of device writes defined below + +Single Device Write Format +"""""""""""""""""""""""""" + ++--------+--------+----------+ +| Name | Offset | Size | ++========+========+==========+ +| offset | 0 | 8 | ++--------+--------+----------+ +| region | 8 | 4 | ++--------+--------+----------+ +| count | 12 | 4 | ++--------+--------+----------+ +| data | 16 | 8 | ++--------+--------+----------+ + +* *offset* into the region being accessed. +* *region* is the index of the region being accessed. +* *count* is the size of the data to be transferred. This format can + only describe writes of 8 bytes or less. +* *data* is the data to write. + +Reply +^^^^^ + ++---------+--------+----------+ +| Name | Offset | Size | ++=========+========+==========+ +| wr_cnt | 0 | 8 | ++---------+--------+----------+ + +* *wr_cnt* is the number of device writes completed. + + +Appendices +========== + +Unused VFIO ``ioctl()`` commands +-------------------------------- + +The following VFIO commands do not have an equivalent vfio-user command: + +* ``VFIO_GET_API_VERSION`` +* ``VFIO_CHECK_EXTENSION`` +* ``VFIO_SET_IOMMU`` +* ``VFIO_GROUP_GET_STATUS`` +* ``VFIO_GROUP_SET_CONTAINER`` +* ``VFIO_GROUP_UNSET_CONTAINER`` +* ``VFIO_GROUP_GET_DEVICE_FD`` +* ``VFIO_IOMMU_GET_INFO`` + +However, once support for live migration for VFIO devices is finalized some +of the above commands may have to be handled by the client in their +corresponding vfio-user form. This will be addressed in a future protocol +version. + +VFIO groups and containers +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The current VFIO implementation includes group and container idioms that +describe how a device relates to the host IOMMU. In the vfio-user +implementation, the IOMMU is implemented in SW by the client, and is not +visible to the server. The simplest idea would be that the client put each +device into its own group and container. + +Backend Program Conventions +--------------------------- + +vfio-user backend program conventions are based on the vhost-user ones. + +* The backend program must not daemonize itself. +* No assumptions must be made as to what access the backend program has on the + system. +* File descriptors 0, 1 and 2 must exist, must have regular + stdin/stdout/stderr semantics, and can be redirected. +* The backend program must honor the SIGTERM signal. +* The backend program must accept the following commands line options: + + * ``--socket-path=PATH``: path to UNIX domain socket, + * ``--fd=FDNUM``: file descriptor for UNIX domain socket, incompatible with + ``--socket-path`` +* The backend program must be accompanied with a JSON file stored under + ``/usr/share/vfio-user``. + +TODO add schema similar to docs/interop/vhost-user.json. diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst index 5a070adbc1a..2e50f2ddfad 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,6 +167,8 @@ A vring address description Note that a ring address is an IOVA if ``VIRTIO_F_IOMMU_PLATFORM`` has been negotiated. Otherwise it is a user address. +.. _memory_region_description: + Memory region description ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -143,7 +182,7 @@ Memory region description :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 @@ -153,7 +192,7 @@ fields at the end. | guest address | size | user address | mmap offset | xen mmap flags | domid | +---------------+------+--------------+-------------+----------------+-------+ -:xen mmap flags: 32-bit bit field +: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. @@ -174,7 +213,7 @@ Single memory region description :padding: 64-bit -A region is represented by Memory region description. +:region: region is represented by :ref:`Memory region description `. Multiple Memory regions description ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -187,7 +226,7 @@ Multiple Memory regions description :padding: 32-bit -A region is represented by Memory region description. +:regions: regions field contains 8 regions of type :ref:`Memory region description `. Log description ^^^^^^^^^^^^^^^ @@ -196,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) @@ -275,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 ----------- @@ -309,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`` @@ -334,6 +409,7 @@ in the ancillary data: * ``VHOST_USER_SET_VRING_ERR`` * ``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 @@ -364,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 ---------------------- @@ -480,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 @@ -492,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 ------------- @@ -825,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`` @@ -885,6 +1051,8 @@ Protocol features #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 ----------------------- @@ -1031,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 + + 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 + `. - Get the available vring base offset. + The request payload's *num* field is currently reserved and must be + set to 0. ``VHOST_USER_SET_VRING_KICK`` :id: 12 @@ -1440,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 ---------------------- @@ -1528,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/meson.build b/docs/meson.build index 9040f860ae1..3676f81c4d3 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -54,7 +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' : ''), 'qemu.1': 'man1', 'qemu-block-drivers.7': 'man7', 'qemu-cpu-models.7': 'man7' @@ -99,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/pci_expander_bridge.txt b/docs/pci_expander_bridge.txt index 36750273bb8..540191f5e04 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_sriov.txt b/docs/pcie_sriov.txt index a47aad0bfab..ab2142807f7 100644 --- a/docs/pcie_sriov.txt +++ b/docs/pcie_sriov.txt @@ -52,9 +52,11 @@ setting up a BAR for a VF. ... /* Add and initialize the SR/IOV capability */ - pcie_sriov_pf_init(d, 0x200, "your_virtual_dev", - vf_devid, initial_vfs, total_vfs, - fun_offset, stride); + if (!pcie_sriov_pf_init(d, 0x200, "your_virtual_dev", + vf_devid, initial_vfs, total_vfs, + fun_offset, stride, errp)) { + return; + } /* Set up individual VF BARs (parameters as for normal BARs) */ pcie_sriov_pf_init_vf_bar( ... ) diff --git a/docs/pvrdma.txt b/docs/pvrdma.txt deleted file mode 100644 index 5c122fe8181..00000000000 --- 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/qcow2-cache.txt b/docs/qcow2-cache.txt index 5f763aa6bbf..204a5741ad2 100644 --- a/docs/qcow2-cache.txt +++ b/docs/qcow2-cache.txt @@ -15,7 +15,7 @@ not a straightforward operation. This document attempts to give an overview of the L2 and refcount caches, and how to configure them. -Please refer to the docs/interop/qcow2.txt file for an in-depth +Please refer to the docs/interop/qcow2.rst file for an in-depth technical description of the qcow2 file format. diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000000..02583f209aa --- /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_hest_ghes.rst b/docs/specs/acpi_hest_ghes.rst index 68f1fbe0a4a..c3e9f8d9a70 100644 --- a/docs/specs/acpi_hest_ghes.rst +++ b/docs/specs/acpi_hest_ghes.rst @@ -67,8 +67,10 @@ Design Details (3) The address registers table contains N Error Block Address entries and N Read Ack Register entries. The size for each entry is 8-byte. The Error Status Data Block table contains N Error Status Data Block - entries. The size for each entry is 4096(0x1000) bytes. The total size - for the "etc/hardware_errors" fw_cfg blob is (N * 8 * 2 + N * 4096) bytes. + entries. The size for each entry is defined at the source code as + ACPI_GHES_MAX_RAW_DATA_LENGTH (currently 1024 bytes). The total size + for the "etc/hardware_errors" fw_cfg blob is + (N * 8 * 2 + N * ACPI_GHES_MAX_RAW_DATA_LENGTH) bytes. N is the number of the kinds of hardware error sources. (4) QEMU generates the ACPI linker/loader script for the firmware. The diff --git a/docs/specs/acpi_hw_reduced_hotplug.rst b/docs/specs/acpi_hw_reduced_hotplug.rst index 0bd3f9399fe..3acd6fcd8b8 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/aspeed-intc.rst b/docs/specs/aspeed-intc.rst new file mode 100644 index 00000000000..9cefd7f37f8 --- /dev/null +++ b/docs/specs/aspeed-intc.rst @@ -0,0 +1,136 @@ +=========================== +ASPEED Interrupt Controller +=========================== + +AST2700 +------- +There are a total of 480 interrupt sources in AST2700. Due to the limitation of +interrupt numbers of processors, the interrupts are merged every 32 sources for +interrupt numbers greater than 127. + +There are two levels of interrupt controllers, INTC (CPU Die) and INTCIO +(I/O Die). + +Interrupt Mapping +----------------- +- INTC: Handles interrupt sources 0 - 127 and integrates signals from INTCIO. +- INTCIO: Handles interrupt sources 128 - 319 independently. + +QEMU Support +------------ +Currently, only GIC 192 to 201 are supported, and their source interrupts are +from INTCIO and connected to INTC at input pin 0 and output pins 0 to 9 for +GIC 192-201. + +Design for GICINT 196 +--------------------- +The orgate has interrupt sources ranging from 0 to 31, with its output pin +connected to INTCIO "T0 GICINT_196". The output pin is then connected to INTC +"GIC_192_201" at bit 4, and its bit 4 output pin is connected to GIC 196. + +INTC GIC_192_201 Output Pin Mapping +----------------------------------- +The design of INTC GIC_192_201 have 10 output pins, mapped as following: + +==== ==== +Bit GIC +==== ==== +0 192 +1 193 +2 194 +3 195 +4 196 +5 197 +6 198 +7 199 +8 200 +9 201 +==== ==== + +AST2700 A0 +---------- +It has only one INTC controller, and currently, only GIC 128-136 is supported. +To support both AST2700 A1 and AST2700 A0, there are 10 OR gates in the INTC, +with gates 1 to 9 supporting GIC 128-136. + +Design for GICINT 132 +--------------------- +The orgate has interrupt sources ranging from 0 to 31, with its output pin +connected to INTC. The output pin is then connected to GIC 132. + +Block Diagram of GICINT 196 for AST2700 A1 and GICINT 132 for AST2700 A0 +------------------------------------------------------------------------ + +.. code-block:: + + |-------------------------------------------------------------------------------------------------------| + | AST2700 A1 Design | + | To GICINT196 | + | | + | ETH1 |-----------| |--------------------------| |--------------| | + | -------->|0 | | INTCIO | | orgates[0] | | + | ETH2 | 4| orgates[0]------>|inpin[0]-------->outpin[0]|------->| 0 | | + | -------->|1 5| orgates[1]------>|inpin[1]-------->outpin[1]|------->| 1 | | + | ETH3 | 6| orgates[2]------>|inpin[2]-------->outpin[2]|------->| 2 | | + | -------->|2 19| orgates[3]------>|inpin[3]-------->outpin[3]|------->| 3 OR[0:9] |-----| | + | UART0 | 20|-->orgates[4]------>|inpin[4]-------->outpin[4]|------->| 4 | | | + | -------->|7 21| orgates[5]------>|inpin[5]-------->outpin[5]|------->| 5 | | | + | UART1 | 22| orgates[6]------>|inpin[6]-------->outpin[6]|------->| 6 | | | + | -------->|8 23| orgates[7]------>|inpin[7]-------->outpin[7]|------->| 7 | | | + | UART2 | 24| orgates[8]------>|inpin[8]-------->outpin[8]|------->| 8 | | | + | -------->|9 25| orgates[9]------>|inpin[9]-------->outpin[9]|------->| 9 | | | + | UART3 | 26| |--------------------------| |--------------| | | + | ---------|10 27| | | + | UART5 | 28| | | + | -------->|11 29| | | + | UART6 | | | | + | -------->|12 30| |-----------------------------------------------------------------------| | + | UART7 | 31| | | + | -------->|13 | | | + | UART8 | OR[0:31] | | |------------------------------| |----------| | + | -------->|14 | | | INTC | | GIC | | + | UART9 | | | |inpin[0:0]--------->outpin[0] |---------->|192 | | + | -------->|15 | | |inpin[0:1]--------->outpin[1] |---------->|193 | | + | UART10 | | | |inpin[0:2]--------->outpin[2] |---------->|194 | | + | -------->|16 | | |inpin[0:3]--------->outpin[3] |---------->|195 | | + | UART11 | | |--------------> |inpin[0:4]--------->outpin[4] |---------->|196 | | + | -------->|17 | |inpin[0:5]--------->outpin[5] |---------->|197 | | + | UART12 | | |inpin[0:6]--------->outpin[6] |---------->|198 | | + | -------->|18 | |inpin[0:7]--------->outpin[7] |---------->|199 | | + | |-----------| |inpin[0:8]--------->outpin[8] |---------->|200 | | + | |inpin[0:9]--------->outpin[9] |---------->|201 | | + |-------------------------------------------------------------------------------------------------------| + |-------------------------------------------------------------------------------------------------------| + | ETH1 |-----------| orgates[1]------->|inpin[1]----------->outpin[10]|---------->|128 | | + | -------->|0 | orgates[2]------->|inpin[2]----------->outpin[11]|---------->|129 | | + | ETH2 | 4| orgates[3]------->|inpin[3]----------->outpin[12]|---------->|130 | | + | -------->|1 5| orgates[4]------->|inpin[4]----------->outpin[13]|---------->|131 | | + | ETH3 | 6|---->orgates[5]------->|inpin[5]----------->outpin[14]|---------->|132 | | + | -------->|2 19| orgates[6]------->|inpin[6]----------->outpin[15]|---------->|133 | | + | UART0 | 20| orgates[7]------->|inpin[7]----------->outpin[16]|---------->|134 | | + | -------->|7 21| orgates[8]------->|inpin[8]----------->outpin[17]|---------->|135 | | + | UART1 | 22| orgates[9]------->|inpin[9]----------->outpin[18]|---------->|136 | | + | -------->|8 23| |------------------------------| |----------| | + | UART2 | 24| | + | -------->|9 25| AST2700 A0 Design | + | UART3 | 26| | + | -------->|10 27| | + | UART5 | 28| | + | -------->|11 29| GICINT132 | + | UART6 | | | + | -------->|12 30| | + | UART7 | 31| | + | -------->|13 | | + | UART8 | OR[0:31] | | + | -------->|14 | | + | UART9 | | | + | -------->|15 | | + | UART10 | | | + | -------->|16 | | + | UART11 | | | + | -------->|17 | | + | UART12 | | | + | -------->|18 | | + | |-----------| | + | | + |-------------------------------------------------------------------------------------------------------| diff --git a/docs/specs/edu.rst b/docs/specs/edu.rst new file mode 100644 index 00000000000..ae72737dbb4 --- /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 08763108099..00000000000 --- 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 00000000000..af878225315 --- /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 5ad47a901c9..31ae31576b1 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 e58be38c41c..f19d73c9f6e 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -24,3 +24,18 @@ 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 + riscv-aia + aspeed-intc diff --git a/docs/specs/ivshmem-spec.rst b/docs/specs/ivshmem-spec.rst new file mode 100644 index 00000000000..2d8e80055b0 --- /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 1beb3a01ec3..00000000000 --- 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 index d6707fa069a..261b0f359fe 100644 --- a/docs/specs/pci-ids.rst +++ b/docs/specs/pci-ids.rst @@ -50,7 +50,7 @@ maintained as part of the virtio specification. by QEMU. 1af4:1110 - ivshmem device (shared memory, ``docs/specs/ivshmem-spec.txt``) + ivshmem device (:doc:`ivshmem-spec`) All other device IDs are reserved. @@ -77,13 +77,17 @@ PCI devices (other than virtio): 1b36:0008 PCIe host bridge 1b36:0009 - PCI Expander Bridge (-device pxb) + PCI Expander Bridge (``-device pxb``) 1b36:000a PCI-PCI bridge (multiseat) 1b36:000b - PCIe Expander Bridge (-device pxb-pcie) + 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 @@ -94,6 +98,8 @@ PCI devices (other than virtio): 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`. diff --git a/docs/specs/pvpanic.rst b/docs/specs/pvpanic.rst new file mode 100644 index 00000000000..61a80480edb --- /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 8afcde11cc8..00000000000 --- 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 00000000000..aaf0db9f91b --- /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-aia.rst b/docs/specs/riscv-aia.rst new file mode 100644 index 00000000000..8097e2f8974 --- /dev/null +++ b/docs/specs/riscv-aia.rst @@ -0,0 +1,83 @@ +.. _riscv-aia: + +RISC-V AIA support for RISC-V machines +====================================== + +AIA (Advanced Interrupt Architecture) support is implemented in the ``virt`` +RISC-V machine for TCG and KVM accelerators. + +The support consists of two main modes: + +- "aia=aplic": adds one or more APLIC (Advanced Platform Level Interrupt Controller) + devices +- "aia=aplic-imsic": adds one or more APLIC device and an IMSIC (Incoming MSI + Controller) device for each CPU + +From an user standpoint, these modes will behave the same regardless of the accelerator +used. From a developer standpoint the accelerator settings will change what it being +emulated in userspace versus what is being emulated by an in-kernel irqchip. + +When running TCG, all controllers are emulated in userspace, including machine mode +(m-mode) APLIC and IMSIC (when applicable). + +When running KVM: + +- no m-mode is provided, so there is no m-mode APLIC or IMSIC emulation regardless of + the AIA mode chosen +- with "aia=aplic", s-mode APLIC will be emulated by userspace +- with "aia=aplic-imsic" there are two possibilities. If no additional KVM option + is provided there will be no APLIC or IMSIC emulation in userspace, and the virtual + machine will use the provided in-kernel APLIC and IMSIC controllers. If the user + chooses to use the irqchip in split mode via "-accel kvm,kernel-irqchip=split", + s-mode APLIC will be emulated while using the s-mode IMSIC from the irqchip + +The following table summarizes how the AIA and accelerator options defines what +we will emulate in userspace: + + +.. list-table:: How AIA and accel options changes controller emulation + :widths: 25 25 25 25 25 25 25 + :header-rows: 1 + + * - Accel + - Accel props + - AIA type + - APLIC m-mode + - IMSIC m-mode + - APLIC s-mode + - IMSIC s-mode + * - tcg + - --- + - aplic + - emul + - n/a + - emul + - n/a + * - tcg + - --- + - aplic-imsic + - emul + - emul + - emul + - emul + * - kvm + - --- + - aplic + - n/a + - n/a + - emul + - n/a + * - kvm + - none + - aplic-imsic + - n/a + - n/a + - in-kernel + - in-kernel + * - kvm + - irqchip=split + - aplic-imsic + - n/a + - n/a + - emul + - in-kernel diff --git a/docs/specs/riscv-iommu.rst b/docs/specs/riscv-iommu.rst new file mode 100644 index 00000000000..991d376fdc2 --- /dev/null +++ b/docs/specs/riscv-iommu.rst @@ -0,0 +1,116 @@ +.. _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.0`_. + +The emulation includes a PCI reference device (riscv-iommu-pci) and a platform +bus device (riscv-iommu-sys) that QEMU RISC-V boards can use. The 'virt' +RISC-V machine is compatible with both devices. + +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.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 +- "hpm-counters": number of hardware performance counters available. Maximum value is 31. + Default value is 31. Use 0 (zero) to disable HPM support + +riscv-iommu-sys device +---------------------- + +This device implements the RISC-V IOMMU emulation as a platform bus device that +RISC-V boards can use. + +For the 'virt' board the device is disabled by default. To enable it use the +'iommu-sys' machine option: + +.. code-block:: bash + + $ qemu-system-riscv64 -M virt,iommu-sys=on (...) + +There is no options to configure the capabilities of this device in the 'virt' +board using the QEMU command line. The device is configured with the following +riscv-iommu options: + +- "ioatc-limit": default value (2Mb) +- "intremap": enabled +- "ats": enabled +- "off": on (DMA disabled) +- "s-stage": enabled +- "g-stage": enabled + +.. _iommu1.0.0: https://github.com/riscv-non-isa/riscv-iommu/releases/download/v1.0.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 00000000000..3a7fc6a7e0e --- /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 1857b317030..00000000000 --- 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 00000000000..f7de080ff0b --- /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 00000000000..992f429ced1 --- /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 18f75f1b302..00000000000 --- 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 efe124a1486..b630a351b4f 100644 --- a/docs/specs/tpm.rst +++ b/docs/specs/tpm.rst @@ -1,3 +1,5 @@ +.. _tpm-device: + =============== QEMU TPM Device =============== @@ -203,8 +205,8 @@ to be used with the passthrough backend or the swtpm backend. QEMU files related to TPM backends: - ``backends/tpm.c`` - - ``include/sysemu/tpm.h`` - - ``include/sysemu/tpm_backend.h`` + - ``include/system/tpm.h`` + - ``include/system/tpm_backend.h`` The QEMU TPM passthrough device ------------------------------- @@ -238,7 +240,7 @@ PCRs. QEMU files related to the TPM passthrough device: - ``backends/tpm/tpm_passthrough.c`` - ``backends/tpm/tpm_util.c`` - - ``include/sysemu/tpm_util.h`` + - ``include/system/tpm_util.h`` Command line to start QEMU with the TPM passthrough device using the host's @@ -299,7 +301,7 @@ command. QEMU files related to the TPM emulator device: - ``backends/tpm/tpm_emulator.c`` - ``backends/tpm/tpm_util.c`` - - ``include/sysemu/tpm_util.h`` + - ``include/system/tpm_util.h`` The following commands start the swtpm with a UnixIO control channel over a socket interface. They do not need to be run as root. @@ -334,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 \ diff --git a/docs/specs/virt-ctlr.rst b/docs/specs/virt-ctlr.rst new file mode 100644 index 00000000000..ad3edde82d2 --- /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 24d38084f7f..00000000000 --- 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 00000000000..6541aa116f7 --- /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 bcbca6fe47c..00000000000 --- 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 00000000000..9a3cefcd828 --- /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 80ff69f31cc..00000000000 --- 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 00000000000..b6f434a4187 --- /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 49affb2a423..00000000000 --- 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 c70ef951286..b225bf706f5 100644 --- a/docs/sphinx-static/theme_overrides.css +++ b/docs/sphinx-static/theme_overrides.css @@ -18,8 +18,8 @@ h1, h2, .rst-content .toctree-wrapper p.caption, h3, h4, h5, h6, legend { .rst-content dl:not(.docutils) dt { border-top: none; - border-left: solid 3px #ccc; - background-color: #f0f0f0; + border-left: solid 5px #bcc6d2; + background-color: #eaedf1; color: black; } @@ -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 @@ -159,3 +208,97 @@ div[class^="highlight"] pre { color: inherit; } } + +/* QAPI domain theming */ + +/* most content in a QAPI object definition should not eclipse about + 80ch, but nested field lists are explicitly exempt due to their + two-column nature */ +.qapi dd *:not(dl) { + max-width: 80ch; +} + +/* but the content column itself should still be less than ~80ch. */ +.qapi .field-list dd { + max-width: 80ch; +} + +.qapi-infopips { + margin-bottom: 1em; +} + +.qapi-infopip { + display: inline-block; + padding: 0em 0.5em 0em 0.5em; + margin: 0.25em; +} + +.qapi-deprecated,.qapi-unstable { + background-color: #fffef5; + border: solid #fff176 6px; + font-weight: bold; + padding: 8px; + border-radius: 15px; + margin: 5px; +} + +.qapi-unstable::before { + content: '🚧 '; +} + +.qapi-deprecated::before { + content: '⚠️ '; +} + +.qapi-ifcond::before { + /* gaze ye into the crystal ball to determine feature availability */ + content: '🔮 '; +} + +.qapi-ifcond { + background-color: #f9f5ff; + border: solid #dac2ff 6px; + padding: 8px; + border-radius: 15px; + margin: 5px; +} + +/* code blocks */ +.qapi div[class^="highlight"] { + width: fit-content; + background-color: #fffafd; + border: 2px solid #ffe1f3; +} + +/* note, warning, etc. */ +.qapi .admonition { + width: fit-content; +} + +/* pad the top of the field-list so the text doesn't start directly at + the top border; primarily for the field list labels, but adjust the + field bodies as well for parity. */ +dl.field-list > dt:first-of-type, dl.field-list > dd:first-of-type { + padding-top: 0.3em; +} + +dl.field-list > dt:last-of-type, dl.field-list > dd:last-of-type { + padding-bottom: 0.3em; +} + +/* pad the field list labels so they don't crash into the border */ +dl.field-list > dt { + padding-left: 0.5em; + padding-right: 0.5em; +} + +/* Add a little padding between field list sections */ +dl.field-list > dd:not(:last-child) { + padding-bottom: 1em; +} + +/* Sphinx 3.x: unresolved xrefs */ +.rst-content *:not(a) > code.xref { + font-weight: 400; + color: #333333; +} diff --git a/docs/sphinx/compat.py b/docs/sphinx/compat.py new file mode 100644 index 00000000000..9cf7fe006e4 --- /dev/null +++ b/docs/sphinx/compat.py @@ -0,0 +1,230 @@ +""" +Sphinx cross-version compatibility goop +""" + +import re +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Optional, + Type, +) + +from docutils import nodes +from docutils.nodes import Element, Node, Text +from docutils.statemachine import StringList + +import sphinx +from sphinx import addnodes, util +from sphinx.directives import ObjectDescription +from sphinx.environment import BuildEnvironment +from sphinx.roles import XRefRole +from sphinx.util import docfields +from sphinx.util.docutils import ( + ReferenceRole, + SphinxDirective, + switch_source_input, +) +from sphinx.util.typing import TextlikeNode + + +MAKE_XREF_WORKAROUND = sphinx.version_info[:3] < (4, 1, 0) + + +SpaceNode: Callable[[str], Node] +KeywordNode: Callable[[str, str], Node] + +if sphinx.version_info[:3] >= (4, 0, 0): + SpaceNode = addnodes.desc_sig_space + KeywordNode = addnodes.desc_sig_keyword +else: + SpaceNode = Text + KeywordNode = addnodes.desc_annotation + + +def nested_parse_with_titles( + directive: SphinxDirective, content_node: Element +) -> None: + """ + This helper preserves error parsing context across sphinx versions. + """ + + # necessary so that the child nodes get the right source/line set + content_node.document = directive.state.document + + try: + # Modern sphinx (6.2.0+) supports proper offsetting for + # nested parse error context management + util.nodes.nested_parse_with_titles( + directive.state, + directive.content, + content_node, + content_offset=directive.content_offset, + ) + except TypeError: + # No content_offset argument. Fall back to SSI method. + with switch_source_input(directive.state, directive.content): + util.nodes.nested_parse_with_titles( + directive.state, directive.content, content_node + ) + + +# ########################################### +# xref compatibility hacks for Sphinx < 4.1 # +# ########################################### + +# When we require >= Sphinx 4.1, the following function and the +# subsequent 3 compatibility classes can be removed. Anywhere in +# qapi_domain that uses one of these Compat* types can be switched to +# using the garden-variety lib-provided classes with no trickery. + + +def _compat_make_xref( # pylint: disable=unused-argument + self: sphinx.util.docfields.Field, + rolename: str, + domain: str, + target: str, + innernode: Type[TextlikeNode] = addnodes.literal_emphasis, + contnode: Optional[Node] = None, + env: Optional[BuildEnvironment] = None, + inliner: Any = None, + location: Any = None, +) -> Node: + """ + Compatibility workaround for Sphinx versions prior to 4.1.0. + + Older sphinx versions do not use the domain's XRefRole for parsing + and formatting cross-references, so we need to perform this magick + ourselves to avoid needing to write the parser/formatter in two + separate places. + + This workaround isn't brick-for-brick compatible with modern Sphinx + versions, because we do not have access to the parent directive's + state during this parsing like we do in more modern versions. + + It's no worse than what pre-Sphinx 4.1.0 does, so... oh well! + """ + + # Yes, this function is gross. Pre-4.1 support is a miracle. + # pylint: disable=too-many-locals + + assert env + # Note: Sphinx's own code ignores the type warning here, too. + if not rolename: + return contnode or innernode(target, target) # type: ignore[call-arg] + + # Get the role instance, but don't *execute it* - we lack the + # correct state to do so. Instead, we'll just use its public + # methods to do our reference formatting, and emulate the rest. + role = env.get_domain(domain).roles[rolename] + assert isinstance(role, XRefRole) + + # XRefRole features not supported by this compatibility shim; + # these were not supported in Sphinx 3.x either, so nothing of + # value is really lost. + assert not target.startswith("!") + assert not re.match(ReferenceRole.explicit_title_re, target) + assert not role.lowercase + assert not role.fix_parens + + # Code below based mostly on sphinx.roles.XRefRole; run() and + # create_xref_node() + options = { + "refdoc": env.docname, + "refdomain": domain, + "reftype": rolename, + "refexplicit": False, + "refwarn": role.warn_dangling, + } + refnode = role.nodeclass(target, **options) + title, target = role.process_link(env, refnode, False, target, target) + refnode["reftarget"] = target + classes = ["xref", domain, f"{domain}-{rolename}"] + refnode += role.innernodeclass(target, title, classes=classes) + + # This is the very gross part of the hack. Normally, + # result_nodes takes a document object to which we would pass + # self.inliner.document. Prior to Sphinx 4.1, we don't *have* an + # inliner to pass, so we have nothing to pass here. However, the + # actual implementation of role.result_nodes in this case + # doesn't actually use that argument, so this winds up being + # ... fine. Rest easy at night knowing this code only runs under + # old versions of Sphinx, so at least it won't change in the + # future on us and lead to surprising new failures. + # Gross, I know. + result_nodes, _messages = role.result_nodes( + None, # type: ignore + env, + refnode, + is_ref=True, + ) + return nodes.inline(target, "", *result_nodes) + + +class CompatField(docfields.Field): + if MAKE_XREF_WORKAROUND: + make_xref = _compat_make_xref + + +class CompatGroupedField(docfields.GroupedField): + if MAKE_XREF_WORKAROUND: + make_xref = _compat_make_xref + + +class CompatTypedField(docfields.TypedField): + if MAKE_XREF_WORKAROUND: + make_xref = _compat_make_xref + + +# ################################################################ +# Nested parsing error location fix for Sphinx 5.3.0 < x < 6.2.0 # +# ################################################################ + +# When we require Sphinx 4.x, the TYPE_CHECKING hack where we avoid +# subscripting ObjectDescription at runtime can be removed in favor of +# just always subscripting the class. + +# When we require Sphinx > 6.2.0, the rest of this compatibility hack +# can be dropped and QAPIObject can just inherit directly from +# ObjectDescription[Signature]. + +SOURCE_LOCATION_FIX = (5, 3, 0) <= sphinx.version_info[:3] < (6, 2, 0) + +Signature = str + + +if TYPE_CHECKING: + _BaseClass = ObjectDescription[Signature] +else: + _BaseClass = ObjectDescription + + +class ParserFix(_BaseClass): + + _temp_content: StringList + _temp_offset: int + _temp_node: Optional[addnodes.desc_content] + + def before_content(self) -> None: + # Work around a sphinx bug and parse the content ourselves. + self._temp_content = self.content + self._temp_offset = self.content_offset + self._temp_node = None + + if SOURCE_LOCATION_FIX: + self._temp_node = addnodes.desc_content() + self.state.nested_parse( + self.content, self.content_offset, self._temp_node + ) + # Sphinx will try to parse the content block itself, + # Give it nothingness to parse instead. + self.content = StringList() + self.content_offset = 0 + + def transform_content(self, content_node: addnodes.desc_content) -> None: + # Sphinx workaround: Inject our parsed content and restore state. + if self._temp_node: + content_node += self._temp_node.children + self.content = self._temp_content + self.content_offset = self._temp_offset diff --git a/docs/sphinx/depfile.py b/docs/sphinx/depfile.py index afdcbcec6e7..d3c774d28b1 100644 --- a/docs/sphinx/depfile.py +++ b/docs/sphinx/depfile.py @@ -19,7 +19,7 @@ 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(): @@ -31,6 +31,9 @@ def get_infiles(env): for path in Path(static_path).rglob('*'): yield str(path) + # also include kdoc script + yield str(env.config.kerneldoc_bin[1]) + def write_depfile(app, exception): if exception: diff --git a/docs/sphinx/hxtool.py b/docs/sphinx/hxtool.py index fb0649a3d5b..a84723be19e 100644 --- a/docs/sphinx/hxtool.py +++ b/docs/sphinx/hxtool.py @@ -24,16 +24,10 @@ 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 @@ def run(self): 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 @@ def run(self): # 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 72c403a7379..3aa972f2e89 100644 --- a/docs/sphinx/kerneldoc.py +++ b/docs/sphinx/kerneldoc.py @@ -38,20 +38,14 @@ 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""" @@ -111,8 +105,7 @@ def run(self): 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() @@ -122,8 +115,10 @@ def run(self): 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) @@ -149,22 +144,13 @@ def run(self): 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): diff --git a/docs/sphinx/kernellog.py b/docs/sphinx/kernellog.py deleted file mode 100644 index af924f51a7d..00000000000 --- 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/qapi_domain.py b/docs/sphinx/qapi_domain.py new file mode 100644 index 00000000000..ebc46a72c61 --- /dev/null +++ b/docs/sphinx/qapi_domain.py @@ -0,0 +1,1055 @@ +""" +QAPI domain extension. +""" + +# The best laid plans of mice and men, ... +# pylint: disable=too-many-lines + +from __future__ import annotations + +import re +import types +from typing import ( + TYPE_CHECKING, + List, + NamedTuple, + Tuple, + Type, + cast, +) + +from docutils import nodes +from docutils.parsers.rst import directives +from sphinx import addnodes +from sphinx.directives import ObjectDescription +from sphinx.domains import ( + Domain, + Index, + IndexEntry, + ObjType, +) +from sphinx.locale import _, __ +from sphinx.roles import XRefRole +from sphinx.util import logging +from sphinx.util.docutils import SphinxDirective +from sphinx.util.nodes import make_id, make_refnode + +from compat import ( + CompatField, + CompatGroupedField, + CompatTypedField, + KeywordNode, + ParserFix, + Signature, + SpaceNode, +) + + +if TYPE_CHECKING: + from typing import ( + AbstractSet, + Any, + Dict, + Iterable, + Optional, + Union, + ) + + from docutils.nodes import Element, Node + from sphinx.addnodes import desc_signature, pending_xref + from sphinx.application import Sphinx + from sphinx.builders import Builder + from sphinx.environment import BuildEnvironment + from sphinx.util.typing import OptionSpec + + +logger = logging.getLogger(__name__) + + +def _unpack_field( + field: nodes.Node, +) -> Tuple[nodes.field_name, nodes.field_body]: + """ + docutils helper: unpack a field node in a type-safe manner. + """ + assert isinstance(field, nodes.field) + assert len(field.children) == 2 + assert isinstance(field.children[0], nodes.field_name) + assert isinstance(field.children[1], nodes.field_body) + return (field.children[0], field.children[1]) + + +class ObjectEntry(NamedTuple): + docname: str + node_id: str + objtype: str + aliased: bool + + +class QAPIXRefRole(XRefRole): + + def process_link( + self, + env: BuildEnvironment, + refnode: Element, + has_explicit_title: bool, + title: str, + target: str, + ) -> tuple[str, str]: + refnode["qapi:namespace"] = env.ref_context.get("qapi:namespace") + refnode["qapi:module"] = env.ref_context.get("qapi:module") + + # Cross-references that begin with a tilde adjust the title to + # only show the reference without a leading module, even if one + # was provided. This is a Sphinx-standard syntax; give it + # priority over QAPI-specific type markup below. + hide_module = False + if target.startswith("~"): + hide_module = True + target = target[1:] + + # Type names that end with "?" are considered optional + # arguments and should be documented as such, but it's not + # part of the xref itself. + if target.endswith("?"): + refnode["qapi:optional"] = True + target = target[:-1] + + # Type names wrapped in brackets denote lists. strip the + # brackets and remember to add them back later. + if target.startswith("[") and target.endswith("]"): + refnode["qapi:array"] = True + target = target[1:-1] + + if has_explicit_title: + # Don't mess with the title at all if it was explicitly set. + # Explicit title syntax for references is e.g. + # :qapi:type:`target ` + # and this explicit title overrides everything else here. + return title, target + + title = target + if hide_module: + title = target.split(".")[-1] + + return title, target + + def result_nodes( + self, + document: nodes.document, + env: BuildEnvironment, + node: Element, + is_ref: bool, + ) -> Tuple[List[nodes.Node], List[nodes.system_message]]: + + # node here is the pending_xref node (or whatever nodeclass was + # configured at XRefRole class instantiation time). + results: List[nodes.Node] = [node] + + if node.get("qapi:array"): + results.insert(0, nodes.literal("[", "[")) + results.append(nodes.literal("]", "]")) + + if node.get("qapi:optional"): + results.append(nodes.Text(", ")) + results.append(nodes.emphasis("?", "optional")) + + return results, [] + + +class QAPIDescription(ParserFix): + """ + Generic QAPI description. + + This is meant to be an abstract class, not instantiated + directly. This class handles the abstract details of indexing, the + TOC, and reference targets for QAPI descriptions. + """ + + def handle_signature(self, sig: str, signode: desc_signature) -> Signature: + # pylint: disable=unused-argument + + # Do nothing. The return value here is the "name" of the entity + # being documented; for QAPI, this is the same as the + # "signature", which is just a name. + + # Normally this method must also populate signode with nodes to + # render the signature; here we do nothing instead - the + # subclasses will handle this. + return sig + + def get_index_text(self, name: Signature) -> Tuple[str, str]: + """Return the text for the index entry of the object.""" + + # NB: this is used for the global index, not the QAPI index. + return ("single", f"{name} (QMP {self.objtype})") + + def _get_context(self) -> Tuple[str, str]: + namespace = self.options.get( + "namespace", self.env.ref_context.get("qapi:namespace", "") + ) + modname = self.options.get( + "module", self.env.ref_context.get("qapi:module", "") + ) + + return namespace, modname + + def _get_fqn(self, name: Signature) -> str: + namespace, modname = self._get_context() + + # If we're documenting a module, don't include the module as + # part of the FQN; we ARE the module! + if self.objtype == "module": + modname = "" + + if modname: + name = f"{modname}.{name}" + if namespace: + name = f"{namespace}:{name}" + return name + + def add_target_and_index( + self, name: Signature, sig: str, signode: desc_signature + ) -> None: + # pylint: disable=unused-argument + + # name is the return value of handle_signature. + # sig is the original, raw text argument to handle_signature. + # For QAPI, these are identical, currently. + + assert self.objtype + + if not (fullname := signode.get("fullname", "")): + fullname = self._get_fqn(name) + + node_id = make_id( + self.env, self.state.document, self.objtype, fullname + ) + signode["ids"].append(node_id) + + self.state.document.note_explicit_target(signode) + domain = cast(QAPIDomain, self.env.get_domain("qapi")) + domain.note_object(fullname, self.objtype, node_id, location=signode) + + if "no-index-entry" not in self.options: + arity, indextext = self.get_index_text(name) + assert self.indexnode is not None + if indextext: + self.indexnode["entries"].append( + (arity, indextext, node_id, "", None) + ) + + @staticmethod + def split_fqn(name: str) -> Tuple[str, str, str]: + if ":" in name: + ns, name = name.split(":") + else: + ns = "" + + if "." in name: + module, name = name.split(".") + else: + module = "" + + return (ns, module, name) + + def _object_hierarchy_parts( + self, sig_node: desc_signature + ) -> Tuple[str, ...]: + if "fullname" not in sig_node: + return () + return self.split_fqn(sig_node["fullname"]) + + def _toc_entry_name(self, sig_node: desc_signature) -> str: + # This controls the name in the TOC and on the sidebar. + + # This is the return type of _object_hierarchy_parts(). + toc_parts = cast(Tuple[str, ...], sig_node.get("_toc_parts", ())) + if not toc_parts: + return "" + + config = self.env.app.config + namespace, modname, name = toc_parts + + if config.toc_object_entries_show_parents == "domain": + ret = name + if modname and modname != self.env.ref_context.get( + "qapi:module", "" + ): + ret = f"{modname}.{name}" + if namespace and namespace != self.env.ref_context.get( + "qapi:namespace", "" + ): + ret = f"{namespace}:{ret}" + return ret + if config.toc_object_entries_show_parents == "hide": + return name + if config.toc_object_entries_show_parents == "all": + return sig_node.get("fullname", name) + return "" + + +class QAPIObject(QAPIDescription): + """ + Description of a generic QAPI object. + + It's not used directly, but is instead subclassed by specific directives. + """ + + # Inherit some standard options from Sphinx's ObjectDescription + option_spec: OptionSpec = ( # type:ignore[misc] + ObjectDescription.option_spec.copy() + ) + option_spec.update( + { + # Context overrides: + "namespace": directives.unchanged, + "module": directives.unchanged, + # These are QAPI originals: + "since": directives.unchanged, + "ifcond": directives.unchanged, + "deprecated": directives.flag, + "unstable": directives.flag, + } + ) + + doc_field_types = [ + # :feat name: descr + CompatGroupedField( + "feature", + label=_("Features"), + names=("feat",), + can_collapse=False, + ), + ] + + def get_signature_prefix(self) -> List[nodes.Node]: + """Return a prefix to put before the object name in the signature.""" + assert self.objtype + return [ + KeywordNode("", self.objtype.title()), + SpaceNode(" "), + ] + + def get_signature_suffix(self) -> List[nodes.Node]: + """Return a suffix to put after the object name in the signature.""" + ret: List[nodes.Node] = [] + + if "since" in self.options: + ret += [ + SpaceNode(" "), + addnodes.desc_sig_element( + "", f"(Since: {self.options['since']})" + ), + ] + + return ret + + def handle_signature(self, sig: str, signode: desc_signature) -> Signature: + """ + Transform a QAPI definition name into RST nodes. + + This method was originally intended for handling function + signatures. In the QAPI domain, however, we only pass the + definition name as the directive argument and handle everything + else in the content body with field lists. + + As such, the only argument here is "sig", which is just the QAPI + definition name. + """ + # No module or domain info allowed in the signature! + assert ":" not in sig + assert "." not in sig + + namespace, modname = self._get_context() + signode["fullname"] = self._get_fqn(sig) + signode["namespace"] = namespace + signode["module"] = modname + + sig_prefix = self.get_signature_prefix() + if sig_prefix: + signode += addnodes.desc_annotation( + str(sig_prefix), "", *sig_prefix + ) + signode += addnodes.desc_name(sig, sig) + signode += self.get_signature_suffix() + + return sig + + def _add_infopips(self, contentnode: addnodes.desc_content) -> None: + # Add various eye-catches and things that go below the signature + # bar, but precede the user-defined content. + infopips = nodes.container() + infopips.attributes["classes"].append("qapi-infopips") + + def _add_pip( + source: str, content: Union[str, List[nodes.Node]], classname: str + ) -> None: + node = nodes.container(source) + if isinstance(content, str): + node.append(nodes.Text(content)) + else: + node.extend(content) + node.attributes["classes"].extend(["qapi-infopip", classname]) + infopips.append(node) + + if "deprecated" in self.options: + _add_pip( + ":deprecated:", + f"This {self.objtype} is deprecated.", + "qapi-deprecated", + ) + + if "unstable" in self.options: + _add_pip( + ":unstable:", + f"This {self.objtype} is unstable/experimental.", + "qapi-unstable", + ) + + if self.options.get("ifcond", ""): + ifcond = self.options["ifcond"] + _add_pip( + f":ifcond: {ifcond}", + [ + nodes.emphasis("", "Availability"), + nodes.Text(": "), + nodes.literal(ifcond, ifcond), + ], + "qapi-ifcond", + ) + + if infopips.children: + contentnode.insert(0, infopips) + + def _validate_field(self, field: nodes.field) -> None: + """Validate field lists in this QAPI Object Description.""" + name, _ = _unpack_field(field) + allowed_fields = set(self.env.app.config.qapi_allowed_fields) + + field_label = name.astext() + if field_label in allowed_fields: + # Explicitly allowed field list name, OK. + return + + try: + # split into field type and argument (if provided) + # e.g. `:arg type name: descr` is + # field_type = "arg", field_arg = "type name". + field_type, field_arg = field_label.split(None, 1) + except ValueError: + # No arguments provided + field_type = field_label + field_arg = "" + + typemap = self.get_field_type_map() + if field_type in typemap: + # This is a special docfield, yet-to-be-processed. Catch + # correct names, but incorrect arguments. This mismatch WILL + # cause Sphinx to render this field incorrectly (without a + # warning), which is never what we want. + typedesc = typemap[field_type][0] + if typedesc.has_arg != bool(field_arg): + msg = f"docfield field list type {field_type!r} " + if typedesc.has_arg: + msg += "requires an argument." + else: + msg += "takes no arguments." + logger.warning(msg, location=field) + else: + # This is unrecognized entirely. It's valid rST to use + # arbitrary fields, but let's ensure the documentation + # writer has done this intentionally. + valid = ", ".join(sorted(set(typemap) | allowed_fields)) + msg = ( + f"Unrecognized field list name {field_label!r}.\n" + f"Valid fields for qapi:{self.objtype} are: {valid}\n" + "\n" + "If this usage is intentional, please add it to " + "'qapi_allowed_fields' in docs/conf.py." + ) + logger.warning(msg, location=field) + + def transform_content(self, content_node: addnodes.desc_content) -> None: + # This hook runs after before_content and the nested parse, but + # before the DocFieldTransformer is executed. + super().transform_content(content_node) + + self._add_infopips(content_node) + + # Validate field lists. + for child in content_node: + if isinstance(child, nodes.field_list): + for field in child.children: + assert isinstance(field, nodes.field) + self._validate_field(field) + + +class SpecialTypedField(CompatTypedField): + def make_field(self, *args: Any, **kwargs: Any) -> nodes.field: + ret = super().make_field(*args, **kwargs) + + # Look for the characteristic " -- " text node that Sphinx + # inserts for each TypedField entry ... + for node in ret.traverse(lambda n: str(n) == " -- "): + par = node.parent + if par.children[0].astext() != "q_dummy": + continue + + # If the first node's text is q_dummy, this is a dummy + # field we want to strip down to just its contents. + del par.children[:-1] + + return ret + + +class QAPICommand(QAPIObject): + """Description of a QAPI Command.""" + + doc_field_types = QAPIObject.doc_field_types.copy() + doc_field_types.extend( + [ + # :arg TypeName ArgName: descr + SpecialTypedField( + "argument", + label=_("Arguments"), + names=("arg",), + typerolename="type", + can_collapse=False, + ), + # :error: descr + CompatField( + "error", + label=_("Errors"), + names=("error", "errors"), + has_arg=False, + ), + # :return TypeName: descr + CompatGroupedField( + "returnvalue", + label=_("Return"), + rolename="type", + names=("return",), + can_collapse=True, + ), + ] + ) + + +class QAPIEnum(QAPIObject): + """Description of a QAPI Enum.""" + + doc_field_types = QAPIObject.doc_field_types.copy() + doc_field_types.extend( + [ + # :value name: descr + CompatGroupedField( + "value", + label=_("Values"), + names=("value",), + can_collapse=False, + ) + ] + ) + + +class QAPIAlternate(QAPIObject): + """Description of a QAPI Alternate.""" + + doc_field_types = QAPIObject.doc_field_types.copy() + doc_field_types.extend( + [ + # :alt type name: descr + CompatTypedField( + "alternative", + label=_("Alternatives"), + names=("alt",), + typerolename="type", + can_collapse=False, + ), + ] + ) + + +class QAPIObjectWithMembers(QAPIObject): + """Base class for Events/Structs/Unions""" + + doc_field_types = QAPIObject.doc_field_types.copy() + doc_field_types.extend( + [ + # :member type name: descr + SpecialTypedField( + "member", + label=_("Members"), + names=("memb",), + typerolename="type", + can_collapse=False, + ), + ] + ) + + +class QAPIEvent(QAPIObjectWithMembers): + # pylint: disable=too-many-ancestors + """Description of a QAPI Event.""" + + +class QAPIJSONObject(QAPIObjectWithMembers): + # pylint: disable=too-many-ancestors + """Description of a QAPI Object: structs and unions.""" + + +class QAPIModule(QAPIDescription): + """ + Directive to mark description of a new module. + + This directive doesn't generate any special formatting, and is just + a pass-through for the content body. Named section titles are + allowed in the content body. + + Use this directive to create entries for the QAPI module in the + global index and the QAPI index; as well as to associate subsequent + definitions with the module they are defined in for purposes of + search and QAPI index organization. + + :arg: The name of the module. + :opt no-index: Don't add cross-reference targets or index entries. + :opt no-typesetting: Don't render the content body (but preserve any + cross-reference target IDs in the squelched output.) + + Example:: + + .. qapi:module:: block-core + :no-index: + :no-typesetting: + + Lorem ipsum, dolor sit amet ... + """ + + def run(self) -> List[Node]: + modname = self.arguments[0].strip() + self.env.ref_context["qapi:module"] = modname + ret = super().run() + + # ObjectDescription always creates a visible signature bar. We + # want module items to be "invisible", however. + + # Extract the content body of the directive: + assert isinstance(ret[-1], addnodes.desc) + desc_node = ret.pop(-1) + assert isinstance(desc_node.children[1], addnodes.desc_content) + ret.extend(desc_node.children[1].children) + + # Re-home node_ids so anchor refs still work: + node_ids: List[str] + if node_ids := [ + node_id + for el in desc_node.children[0].traverse(nodes.Element) + for node_id in cast(List[str], el.get("ids", ())) + ]: + target_node = nodes.target(ids=node_ids) + ret.insert(1, target_node) + + return ret + + +class QAPINamespace(SphinxDirective): + has_content = False + required_arguments = 1 + + def run(self) -> List[Node]: + namespace = self.arguments[0].strip() + self.env.ref_context["qapi:namespace"] = namespace + + return [] + + +class QAPIIndex(Index): + """ + Index subclass to provide the QAPI definition index. + """ + + # pylint: disable=too-few-public-methods + + name = "index" + localname = _("QAPI Index") + shortname = _("QAPI Index") + namespace = "" + + def generate( + self, + docnames: Optional[Iterable[str]] = None, + ) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]: + assert isinstance(self.domain, QAPIDomain) + content: Dict[str, List[IndexEntry]] = {} + collapse = False + + for objname, obj in self.domain.objects.items(): + if docnames and obj.docname not in docnames: + continue + + ns, _mod, name = QAPIDescription.split_fqn(objname) + + if self.namespace != ns: + continue + + # Add an alphabetical entry: + entries = content.setdefault(name[0].upper(), []) + entries.append( + IndexEntry( + name, 0, obj.docname, obj.node_id, obj.objtype, "", "" + ) + ) + + # Add a categorical entry: + category = obj.objtype.title() + "s" + entries = content.setdefault(category, []) + entries.append( + IndexEntry(name, 0, obj.docname, obj.node_id, "", "", "") + ) + + # Sort entries within each category alphabetically + for category in content: + content[category] = sorted(content[category]) + + # Sort the categories themselves; type names first, ABC entries last. + sorted_content = sorted( + content.items(), + key=lambda x: (len(x[0]) == 1, x[0]), + ) + return sorted_content, collapse + + +class QAPIDomain(Domain): + """QAPI language domain.""" + + name = "qapi" + label = "QAPI" + + # This table associates cross-reference object types (key) with an + # ObjType instance, which defines the valid cross-reference roles + # for each object type. + # + # e.g., the :qapi:type: cross-reference role can refer to enum, + # struct, union, or alternate objects; but :qapi:obj: can refer to + # anything. Each object also gets its own targeted cross-reference role. + object_types: Dict[str, ObjType] = { + "module": ObjType(_("module"), "mod", "any"), + "command": ObjType(_("command"), "cmd", "any"), + "event": ObjType(_("event"), "event", "any"), + "enum": ObjType(_("enum"), "enum", "type", "any"), + "object": ObjType(_("object"), "obj", "type", "any"), + "alternate": ObjType(_("alternate"), "alt", "type", "any"), + } + + # Each of these provides a rST directive, + # e.g. .. qapi:module:: block-core + directives = { + "namespace": QAPINamespace, + "module": QAPIModule, + "command": QAPICommand, + "event": QAPIEvent, + "enum": QAPIEnum, + "object": QAPIJSONObject, + "alternate": QAPIAlternate, + } + + # These are all cross-reference roles; e.g. + # :qapi:cmd:`query-block`. The keys correlate to the names used in + # the object_types table values above. + roles = { + "mod": QAPIXRefRole(), + "cmd": QAPIXRefRole(), + "event": QAPIXRefRole(), + "enum": QAPIXRefRole(), + "obj": QAPIXRefRole(), # specifically structs and unions. + "alt": QAPIXRefRole(), + # reference any data type (excludes modules, commands, events) + "type": QAPIXRefRole(), + "any": QAPIXRefRole(), # reference *any* type of QAPI object. + } + + # Moved into the data property at runtime; + # this is the internal index of reference-able objects. + initial_data: Dict[str, Dict[str, Tuple[Any]]] = { + "objects": {}, # fullname -> ObjectEntry + } + + # Index pages to generate; each entry is an Index class. + indices = [ + QAPIIndex, + ] + + @property + def objects(self) -> Dict[str, ObjectEntry]: + ret = self.data.setdefault("objects", {}) + return ret # type: ignore[no-any-return] + + def setup(self) -> None: + namespaces = set(self.env.app.config.qapi_namespaces) + for namespace in namespaces: + new_index: Type[QAPIIndex] = types.new_class( + f"{namespace}Index", bases=(QAPIIndex,) + ) + new_index.name = f"{namespace.lower()}-index" + new_index.localname = _(f"{namespace} Index") + new_index.shortname = _(f"{namespace} Index") + new_index.namespace = namespace + + self.indices.append(new_index) + + super().setup() + + def note_object( + self, + name: str, + objtype: str, + node_id: str, + aliased: bool = False, + location: Any = None, + ) -> None: + """Note a QAPI object for cross reference.""" + if name in self.objects: + other = self.objects[name] + if other.aliased and aliased is False: + # The original definition found. Override it! + pass + elif other.aliased is False and aliased: + # The original definition is already registered. + return + else: + # duplicated + logger.warning( + __( + "duplicate object description of %s, " + "other instance in %s, use :no-index: for one of them" + ), + name, + other.docname, + location=location, + ) + self.objects[name] = ObjectEntry( + self.env.docname, node_id, objtype, aliased + ) + + def clear_doc(self, docname: str) -> None: + for fullname, obj in list(self.objects.items()): + if obj.docname == docname: + del self.objects[fullname] + + def merge_domaindata( + self, docnames: AbstractSet[str], otherdata: Dict[str, Any] + ) -> None: + for fullname, obj in otherdata["objects"].items(): + if obj.docname in docnames: + # Sphinx's own python domain doesn't appear to bother to + # check for collisions. Assert they don't happen and + # we'll fix it if/when the case arises. + assert fullname not in self.objects, ( + "bug - collision on merge?" + f" {fullname=} {obj=} {self.objects[fullname]=}" + ) + self.objects[fullname] = obj + + def find_obj( + self, namespace: str, modname: str, name: str, typ: Optional[str] + ) -> List[Tuple[str, ObjectEntry]]: + """ + Find a QAPI object for "name", maybe using contextual information. + + Returns a list of (name, object entry) tuples. + + :param namespace: The current namespace context (if any!) under + which we are searching. + :param modname: The current module context (if any!) under + which we are searching. + :param name: The name of the x-ref to resolve; may or may not + include leading context. + :param type: The role name of the x-ref we're resolving, if + provided. This is absent for "any" role lookups. + """ + if not name: + return [] + + # ## + # what to search for + # ## + + parts = list(QAPIDescription.split_fqn(name)) + explicit = tuple(bool(x) for x in parts) + + # Fill in the blanks where possible: + if namespace and not parts[0]: + parts[0] = namespace + if modname and not parts[1]: + parts[1] = modname + + implicit_fqn = "" + if all(parts): + implicit_fqn = f"{parts[0]}:{parts[1]}.{parts[2]}" + + if typ is None: + # :any: lookup, search everything: + objtypes: List[str] = list(self.object_types) + else: + # type is specified and will be a role (e.g. obj, mod, cmd) + # convert this to eligible object types (e.g. command, module) + # using the QAPIDomain.object_types table. + objtypes = self.objtypes_for_role(typ, []) + + # ## + # search! + # ## + + def _search(needle: str) -> List[str]: + if ( + needle + and needle in self.objects + and self.objects[needle].objtype in objtypes + ): + return [needle] + return [] + + if found := _search(name): + # Exact match! + pass + elif found := _search(implicit_fqn): + # Exact match using contextual information to fill in the gaps. + pass + else: + # No exact hits, perform applicable fuzzy searches. + searches = [] + + esc = tuple(re.escape(s) for s in parts) + + # Try searching for ns:*.name or ns:name + if explicit[0] and not explicit[1]: + searches.append(f"^{esc[0]}:([^\\.]+\\.)?{esc[2]}$") + # Try searching for *:module.name or module.name + if explicit[1] and not explicit[0]: + searches.append(f"(^|:){esc[1]}\\.{esc[2]}$") + # Try searching for context-ns:*.name or context-ns:name + if parts[0] and not (explicit[0] or explicit[1]): + searches.append(f"^{esc[0]}:([^\\.]+\\.)?{esc[2]}$") + # Try searching for *:context-mod.name or context-mod.name + if parts[1] and not (explicit[0] or explicit[1]): + searches.append(f"(^|:){esc[1]}\\.{esc[2]}$") + # Try searching for *:name, *.name, or name + if not (explicit[0] or explicit[1]): + searches.append(f"(^|:|\\.){esc[2]}$") + + for search in searches: + if found := [ + oname + for oname in self.objects + if re.search(search, oname) + and self.objects[oname].objtype in objtypes + ]: + break + + matches = [(oname, self.objects[oname]) for oname in found] + if len(matches) > 1: + matches = [m for m in matches if not m[1].aliased] + return matches + + def resolve_xref( + self, + env: BuildEnvironment, + fromdocname: str, + builder: Builder, + typ: str, + target: str, + node: pending_xref, + contnode: Element, + ) -> nodes.reference | None: + namespace = node.get("qapi:namespace") + modname = node.get("qapi:module") + matches = self.find_obj(namespace, modname, target, typ) + + if not matches: + # Normally, we could pass warn_dangling=True to QAPIXRefRole(), + # but that will trigger on references to these built-in types, + # which we'd like to ignore instead. + + # Take care of that warning here instead, so long as the + # reference isn't to one of our built-in core types. + if target not in ( + "string", + "number", + "int", + "boolean", + "null", + "value", + "q_empty", + ): + logger.warning( + __("qapi:%s reference target not found: %r"), + typ, + target, + type="ref", + subtype="qapi", + location=node, + ) + return None + + if len(matches) > 1: + logger.warning( + __("more than one target found for cross-reference %r: %s"), + target, + ", ".join(match[0] for match in matches), + type="ref", + subtype="qapi", + location=node, + ) + + name, obj = matches[0] + return make_refnode( + builder, fromdocname, obj.docname, obj.node_id, contnode, name + ) + + def resolve_any_xref( + self, + env: BuildEnvironment, + fromdocname: str, + builder: Builder, + target: str, + node: pending_xref, + contnode: Element, + ) -> List[Tuple[str, nodes.reference]]: + results: List[Tuple[str, nodes.reference]] = [] + matches = self.find_obj( + node.get("qapi:namespace"), node.get("qapi:module"), target, None + ) + for name, obj in matches: + rolename = self.role_for_objtype(obj.objtype) + assert rolename is not None + role = f"qapi:{rolename}" + refnode = make_refnode( + builder, fromdocname, obj.docname, obj.node_id, contnode, name + ) + results.append((role, refnode)) + return results + + +def setup(app: Sphinx) -> Dict[str, Any]: + app.setup_extension("sphinx.directives") + app.add_config_value( + "qapi_allowed_fields", + set(), + "env", # Setting impacts parsing phase + types=set, + ) + app.add_config_value( + "qapi_namespaces", + set(), + "env", + types=set, + ) + app.add_domain(QAPIDomain) + + return { + "version": "1.0", + "env_version": 1, + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 8f3b9997a15..8011ac9efaf 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -2,6 +2,7 @@ # # QEMU qapidoc QAPI file parsing extension # +# Copyright (c) 2024-2025 Red Hat # Copyright (c) 2020 Linaro # # This work is licensed under the terms of the GNU GPLv2 or later. @@ -24,440 +25,438 @@ https://www.sphinx-doc.org/en/master/development/index.html """ +from __future__ import annotations + + +__version__ = "2.0" + +from contextlib import contextmanager import os +from pathlib import Path import re +import sys +from typing import TYPE_CHECKING from docutils import nodes -from docutils.statemachine import ViewList -from docutils.parsers.rst import directives, Directive +from docutils.parsers.rst import directives +from docutils.statemachine import StringList +from qapi.error import QAPIError +from qapi.parser import QAPIDoc +from qapi.schema import ( + QAPISchema, + QAPISchemaArrayType, + QAPISchemaCommand, + QAPISchemaDefinition, + QAPISchemaEnumMember, + QAPISchemaEvent, + QAPISchemaFeature, + QAPISchemaMember, + QAPISchemaObjectType, + QAPISchemaObjectTypeMember, + QAPISchemaType, + QAPISchemaVisitor, +) +from qapi.source import QAPISourceInfo +from sphinx import addnodes +from sphinx.directives.code import CodeBlock from sphinx.errors import ExtensionError +from sphinx.util import logging +from sphinx.util.docutils import SphinxDirective, switch_source_input from sphinx.util.nodes import nested_parse_with_titles -import sphinx -from qapi.gen import QAPISchemaVisitor -from qapi.error import QAPIError, QAPISemError -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 - - -__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 - - -class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): - """A QAPI schema visitor which generates docutils/Sphinx nodes - - This class builds up a tree of docutils/Sphinx nodes corresponding - to documentation for the various QAPI objects. To use it, first - create a QAPISchemaGenRSTVisitor object, and call its - visit_begin() method. Then you can call one of the two methods - 'freeform' (to add documentation for a freeform documentation - chunk) or 'symbol' (to add documentation for a QAPI symbol). These - will cause the visitor to build up the tree of document - nodes. Once you've added all the documentation via 'freeform' and - 'symbol' method calls, you can call 'get_document_nodes' to get - the final list of document nodes (in a form suitable for returning - from a Sphinx directive's 'run' method). - """ - def __init__(self, sphinx_directive): - self._cur_doc = None - self._sphinx_directive = sphinx_directive - self._top_node = nodes.section() - self._active_headings = [self._top_node] - - def _make_dlitem(self, term, defn): - """Return a dlitem node with the specified term and definition. - - term should be a list of Text and literal nodes. - defn should be one of: - - a string, which will be handed to _parse_text_into_node - - a list of Text and literal nodes, which will be put into - a paragraph node - """ - dlitem = nodes.definition_list_item() - dlterm = nodes.term('', '', *term) - dlitem += dlterm - if defn: - dldef = nodes.definition() - if isinstance(defn, list): - dldef += nodes.paragraph('', '', *defn) - else: - self._parse_text_into_node(defn, dldef) - dlitem += dldef - return dlitem - - def _make_section(self, title): - """Return a section node with optional title""" - section = nodes.section(ids=[self._sphinx_directive.new_serialno()]) - if title: - section += nodes.title(title, title) - return section - - def _nodes_for_ifcond(self, ifcond, with_if=True): - """Return list of Text, literal nodes for the ifcond - - Return a list which gives text like ' (If: condition)'. - If with_if is False, we don't return the "(If: " and ")". - """ - doc = ifcond.docgen() - if not doc: - return [] - doc = nodes.literal('', doc) - if not with_if: - return [doc] +from qapidoc_legacy import QAPISchemaGenRSTVisitor # type: ignore - nodelist = [nodes.Text(' ('), nodes.strong('', 'If: ')] - nodelist.append(doc) - nodelist.append(nodes.Text(')')) - return nodelist - def _nodes_for_one_member(self, member): - """Return list of Text, literal nodes for this member +if TYPE_CHECKING: + from typing import ( + Any, + Generator, + List, + Optional, + Sequence, + Union, + ) - Return a list of doctree nodes which give text like - 'name: type (optional) (If: ...)' suitable for use as the - 'term' part of a definition list item. - """ - term = [nodes.literal('', member.name)] - if member.type.doc_type(): - term.append(nodes.Text(': ')) - term.append(nodes.literal('', member.type.doc_type())) - if member.optional: - term.append(nodes.Text(' (optional)')) - if member.ifcond.is_present(): - term.extend(self._nodes_for_ifcond(member.ifcond)) - return term - - def _nodes_for_variant_when(self, variants, 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. - """ - term = [nodes.Text(' when '), - nodes.literal('', variants.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): - """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(', '))) - else: - defn = [nodes.Text('Not documented')] - - dlnode += self._make_dlitem(term, defn) - - if base: - dlnode += self._make_dlitem([nodes.Text('The members of '), - 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 not dlnode.children: - return [] - - section = self._make_section(what) - section += dlnode - return [section] - - def _nodes_for_enum_values(self, doc): - """Return list of doctree nodes for the table of enum values""" - seen_item = False - dlnode = nodes.definition_list() - for section in doc.args.values(): - termtext = [nodes.literal('', section.member.name)] - if section.member.ifcond.is_present(): - termtext.extend(self._nodes_for_ifcond(section.member.ifcond)) - # TODO drop fallbacks when undocumented members are outlawed - if section.text: - defn = section.text - else: - defn = [nodes.Text('Not documented')] - - dlnode += self._make_dlitem(termtext, defn) - seen_item = True - - if not seen_item: - return [] - - section = self._make_section('Values') - section += dlnode - return [section] - - def _nodes_for_arguments(self, doc, boxed_arg_type): - """Return list of doctree nodes for the arguments section""" - if boxed_arg_type: - 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)], - None) - section += dlnode - return [section] - - return self._nodes_for_members(doc, 'Arguments') - - def _nodes_for_features(self, doc): - """Return list of doctree nodes for the table of features""" - seen_item = False - dlnode = nodes.definition_list() - for section in doc.features.values(): - dlnode += self._make_dlitem([nodes.literal('', section.name)], - section.text) - seen_item = True - - if not seen_item: - return [] - - section = self._make_section('Features') - section += dlnode - return [section] - - def _nodes_for_example(self, exampletext): - """Return list of doctree nodes for a code example snippet""" - return [nodes.literal_block(exampletext, exampletext)] - - def _nodes_for_sections(self, doc): - """Return list of doctree nodes for additional sections""" - nodelist = [] - for section in doc.sections: - if section.name and section.name == 'TODO': - # Hide TODO: sections - continue - snode = self._make_section(section.name) - if section.name and section.name.startswith('Example'): - snode += self._nodes_for_example(section.text) - else: - self._parse_text_into_node(section.text, snode) - nodelist.append(snode) - return nodelist - - def _nodes_for_if_section(self, ifcond): - """Return list of doctree nodes for the "If" section""" - nodelist = [] - if ifcond.is_present(): - snode = self._make_section('If') - snode += nodes.paragraph( - '', '', *self._nodes_for_ifcond(ifcond, with_if=False) - ) - nodelist.append(snode) - return nodelist + from sphinx.application import Sphinx + from sphinx.util.typing import ExtensionMetadata - def _add_doc(self, typ, sections): - """Add documentation for a command/object/enum... - We assume we're documenting the thing defined in self._cur_doc. - typ is the type of thing being added ("Command", "Object", etc) +logger = logging.getLogger(__name__) - sections is a list of nodes for sections to add to the definition. - """ - doc = self._cur_doc - snode = nodes.section(ids=[self._sphinx_directive.new_serialno()]) - snode += nodes.title('', '', *[nodes.literal(doc.symbol, doc.symbol), - nodes.Text(' (' + typ + ')')]) - self._parse_text_into_node(doc.body.text, snode) - for s in sections: - if s is not None: - snode += s - self._add_node_to_current_heading(snode) - - def visit_enum_type(self, name, info, ifcond, features, members, prefix): - doc = self._cur_doc - self._add_doc('Enum', - self._nodes_for_enum_values(doc) - + self._nodes_for_features(doc) - + self._nodes_for_sections(doc) - + self._nodes_for_if_section(ifcond)) - - def visit_object_type(self, name, info, ifcond, features, - base, members, variants): - doc = self._cur_doc - if base and base.is_implicit(): - base = None - self._add_doc('Object', - self._nodes_for_members(doc, 'Members', base, variants) - + 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): - doc = self._cur_doc - self._add_doc('Alternate', - self._nodes_for_members(doc, 'Members') - + self._nodes_for_features(doc) - + self._nodes_for_sections(doc) - + self._nodes_for_if_section(ifcond)) - - def visit_command(self, name, info, ifcond, features, arg_type, - ret_type, gen, success_response, boxed, allow_oob, - 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_features(doc) - + self._nodes_for_sections(doc) - + self._nodes_for_if_section(ifcond)) - - 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_features(doc) - + self._nodes_for_sections(doc) - + self._nodes_for_if_section(ifcond)) - - def symbol(self, doc, entity): - """Add documentation for one symbol to the document tree - - This is the main entry point which causes us to add documentation - nodes for a symbol (which could be a 'command', 'object', 'event', - etc). We do this by calling 'visit' on the schema entity, which - will then call back into one of our visit_* methods, depending - on what kind of thing this symbol is. - """ - self._cur_doc = doc - entity.visit(self) - self._cur_doc = None +class Transmogrifier: + # pylint: disable=too-many-public-methods + + # Field names used for different entity types: + field_types = { + "enum": "value", + "struct": "memb", + "union": "memb", + "event": "memb", + "command": "arg", + "alternate": "alt", + } + + def __init__(self) -> None: + self._curr_ent: Optional[QAPISchemaDefinition] = None + self._result = StringList() + self.indent = 0 + + @property + def result(self) -> StringList: + return self._result + + @property + def entity(self) -> QAPISchemaDefinition: + assert self._curr_ent is not None + return self._curr_ent + + @property + def member_field_type(self) -> str: + return self.field_types[self.entity.meta] + + # General-purpose rST generation functions - def _start_new_heading(self, heading, level): - """Start a new heading at the specified heading level + def get_indent(self) -> str: + return " " * self.indent - Create a new section whose title is 'heading' and which is placed - in the docutils node tree as a child of the most recent level-1 - heading. Subsequent document sections (commands, freeform doc chunks, - etc) will be placed as children of this new heading section. + @contextmanager + def indented(self) -> Generator[None]: + self.indent += 1 + try: + yield + finally: + self.indent -= 1 + + def add_line_raw(self, line: str, source: str, *lineno: int) -> None: + """Append one line of generated reST to the output.""" + + # NB: Sphinx uses zero-indexed lines; subtract one. + lineno = tuple((n - 1 for n in lineno)) + + if line.strip(): + # not a blank line + self._result.append( + self.get_indent() + line.rstrip("\n"), source, *lineno + ) + else: + self._result.append("", source, *lineno) + + def add_line(self, content: str, info: QAPISourceInfo) -> None: + # NB: We *require* an info object; this works out OK because we + # don't document built-in objects that don't have + # one. Everything else should. + self.add_line_raw(content, info.fname, info.line) + + def add_lines( + self, + content: str, + info: QAPISourceInfo, + ) -> None: + lines = content.splitlines(True) + for i, line in enumerate(lines): + self.add_line_raw(line, info.fname, info.line + i) + + def ensure_blank_line(self) -> None: + # Empty document -- no blank line required. + if not self._result: + return + + # Last line isn't blank, add one. + if self._result[-1].strip(): # pylint: disable=no-member + fname, line = self._result.info(-1) + assert isinstance(line, int) + # New blank line is credited to one-after the current last line. + # +2: correct for zero/one index, then increment by one. + self.add_line_raw("", fname, line + 2) + + def add_field( + self, + kind: str, + name: str, + body: str, + info: QAPISourceInfo, + typ: Optional[str] = None, + ) -> None: + if typ: + text = f":{kind} {typ} {name}: {body}" + else: + text = f":{kind} {name}: {body}" + self.add_lines(text, info) + + def format_type( + self, ent: Union[QAPISchemaDefinition | QAPISchemaMember] + ) -> Optional[str]: + if isinstance(ent, (QAPISchemaEnumMember, QAPISchemaFeature)): + return None + + qapi_type = ent + optional = False + if isinstance(ent, QAPISchemaObjectTypeMember): + qapi_type = ent.type + optional = ent.optional + + if isinstance(qapi_type, QAPISchemaArrayType): + ret = f"[{qapi_type.element_type.doc_type()}]" + else: + assert isinstance(qapi_type, QAPISchemaType) + tmp = qapi_type.doc_type() + assert tmp + ret = tmp + if optional: + ret += "?" + + return ret + + def generate_field( + self, + kind: str, + member: QAPISchemaMember, + body: str, + info: QAPISourceInfo, + ) -> None: + typ = self.format_type(member) + self.add_field(kind, member.name, body, info, typ) + + # Transmogrification helpers + + def visit_paragraph(self, section: QAPIDoc.Section) -> None: + # Squelch empty paragraphs. + if not section.text: + return + + self.ensure_blank_line() + self.add_lines(section.text, section.info) + self.ensure_blank_line() + + def visit_member(self, section: QAPIDoc.ArgSection) -> None: + # FIXME: ifcond for members + # TODO: features for members (documented at entity-level, + # but sometimes defined per-member. Should we add such + # information to member descriptions when we can?) + assert section.member + self.generate_field( + self.member_field_type, + section.member, + # TODO drop fallbacks when undocumented members are outlawed + section.text if section.text else "Not documented", + section.info, + ) + + def visit_feature(self, section: QAPIDoc.ArgSection) -> None: + # FIXME - ifcond for features is not handled at all yet! + # Proposal: decorate the right-hand column with some graphical + # element to indicate conditional availability? + assert section.text # Guaranteed by parser.py + assert section.member + + self.generate_field("feat", section.member, section.text, section.info) + + def visit_returns(self, section: QAPIDoc.Section) -> None: + assert isinstance(self.entity, QAPISchemaCommand) + rtype = self.entity.ret_type + # q_empty can produce None, but we won't be documenting anything + # without an explicit return statement in the doc block, and we + # should not have any such explicit statements when there is no + # return value. + assert rtype + + typ = self.format_type(rtype) + assert typ + assert section.text + self.add_field("return", typ, section.text, section.info) + + def visit_errors(self, section: QAPIDoc.Section) -> None: + # FIXME: the formatting for errors may be inconsistent and may + # or may not require different newline placement to ensure + # proper rendering as a nested list. + self.add_lines(f":error:\n{section.text}", section.info) + + def preamble(self, ent: QAPISchemaDefinition) -> None: """ - if len(self._active_headings) < level: - raise QAPISemError(self._cur_doc.info, - 'Level %d subheading found outside a ' - 'level %d heading' - % (level, level - 1)) - snode = self._make_section(heading) - self._active_headings[level - 1] += snode - self._active_headings = self._active_headings[:level] - self._active_headings.append(snode) - - def _add_node_to_current_heading(self, node): - """Add the node to whatever the current active heading is""" - self._active_headings[-1] += node - - def freeform(self, doc): - """Add a piece of 'freeform' documentation to the document tree - - A 'freeform' document chunk doesn't relate to any particular - symbol (for instance, it could be an introduction). - - If the freeform document starts with a line of the form - '= Heading text', this is a section or subsection heading, with - the heading level indicated by the number of '=' signs. + Generate option lines for QAPI entity directives. """ + if ent.doc and ent.doc.since: + assert ent.doc.since.kind == QAPIDoc.Kind.SINCE + # Generated from the entity's docblock; info location is exact. + self.add_line(f":since: {ent.doc.since.text}", ent.doc.since.info) + + if ent.ifcond.is_present(): + doc = ent.ifcond.docgen() + assert ent.info + # Generated from entity definition; info location is approximate. + self.add_line(f":ifcond: {doc}", ent.info) + + # Hoist special features such as :deprecated: and :unstable: + # into the options block for the entity. If, in the future, new + # special features are added, qapi-domain will chirp about + # unrecognized options and fail until they are handled in + # qapi-domain. + for feat in ent.features: + if feat.is_special(): + # FIXME: handle ifcond if present. How to display that + # information is TBD. + # Generated from entity def; info location is approximate. + assert feat.info + self.add_line(f":{feat.name}:", feat.info) + + self.ensure_blank_line() + + def _insert_member_pointer(self, ent: QAPISchemaDefinition) -> None: + + def _get_target( + ent: QAPISchemaDefinition, + ) -> Optional[QAPISchemaDefinition]: + if isinstance(ent, (QAPISchemaCommand, QAPISchemaEvent)): + return ent.arg_type + if isinstance(ent, QAPISchemaObjectType): + return ent.base + return None + + target = _get_target(ent) + if target is not None and not target.is_implicit(): + assert ent.info + self.add_field( + self.member_field_type, + "q_dummy", + f"The members of :qapi:type:`{target.name}`.", + ent.info, + "q_dummy", + ) - # QAPIDoc documentation says free-form documentation blocks - # must have only a body section, nothing else. - assert not doc.sections - assert not doc.args - assert not doc.features - self._cur_doc = doc - - text = doc.body.text - if re.match(r'=+ ', text): - # Section/subsection heading (if present, will always be - # the first line of the block) - (heading, _, text) = text.partition('\n') - (leader, _, heading) = heading.partition(' ') - 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): - """Parse a chunk of QAPI-doc-format text into the node - - The doc comment can contain most inline rST markup, including - bulleted and enumerated lists. - As an extra permitted piece of markup, @var will be turned - into ``var``. - """ + if isinstance(ent, QAPISchemaObjectType) and ent.branches is not None: + for variant in ent.branches.variants: + if variant.type.name == "q_empty": + continue + assert ent.info + self.add_field( + self.member_field_type, + "q_dummy", + f" When ``{ent.branches.tag_member.name}`` is " + f"``{variant.name}``: " + f"The members of :qapi:type:`{variant.type.name}`.", + ent.info, + "q_dummy", + ) + + def visit_sections(self, ent: QAPISchemaDefinition) -> None: + sections = ent.doc.all_sections if ent.doc else [] + + # Determine the index location at which we should generate + # documentation for "The members of ..." pointers. This should + # go at the end of the members section(s) if any. Note that + # index 0 is assumed to be a plain intro section, even if it is + # empty; and that a members section if present will always + # immediately follow the opening PLAIN section. + gen_index = 1 + if len(sections) > 1: + while sections[gen_index].kind == QAPIDoc.Kind.MEMBER: + gen_index += 1 + if gen_index >= len(sections): + break + + # Add sections in source order: + for i, section in enumerate(sections): + # @var is translated to ``var``: + section.text = re.sub(r"@([\w-]+)", r"``\1``", section.text) + + if section.kind == QAPIDoc.Kind.PLAIN: + self.visit_paragraph(section) + elif section.kind == QAPIDoc.Kind.MEMBER: + assert isinstance(section, QAPIDoc.ArgSection) + self.visit_member(section) + elif section.kind == QAPIDoc.Kind.FEATURE: + assert isinstance(section, QAPIDoc.ArgSection) + self.visit_feature(section) + elif section.kind in (QAPIDoc.Kind.SINCE, QAPIDoc.Kind.TODO): + # Since is handled in preamble, TODO is skipped intentionally. + pass + elif section.kind == QAPIDoc.Kind.RETURNS: + self.visit_returns(section) + elif section.kind == QAPIDoc.Kind.ERRORS: + self.visit_errors(section) + else: + assert False + + # Generate "The members of ..." entries if necessary: + if i == gen_index - 1: + self._insert_member_pointer(ent) + + self.ensure_blank_line() + + # Transmogrification core methods + + def visit_module(self, path: str) -> None: + name = Path(path).stem + # module directives are credited to the first line of a module file. + self.add_line_raw(f".. qapi:module:: {name}", path, 1) + self.ensure_blank_line() + + def visit_freeform(self, doc: QAPIDoc) -> None: + # TODO: Once the old qapidoc transformer is deprecated, freeform + # sections can be updated to pure rST, and this transformed removed. + # + # For now, translate our micro-format into rST. Code adapted + # from Peter Maydell's freeform(). + + assert len(doc.all_sections) == 1, doc.all_sections + body = doc.all_sections[0] + text = body.text + info = doc.info + + if re.match(r"=+ ", text): + # Section/subsection heading (if present, will always be the + # first line of the block) + (heading, _, text) = text.partition("\n") + (leader, _, heading) = heading.partition(" ") + # Implicit +1 for heading in the containing .rst doc + level = len(leader) + 1 + + # https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#sections + markers = ' #*=_^"' + overline = level <= 2 + marker = markers[level] + + self.ensure_blank_line() + # This credits all 2 or 3 lines to the single source line. + if overline: + self.add_line(marker * len(heading), info) + self.add_line(heading, info) + self.add_line(marker * len(heading), info) + self.ensure_blank_line() + + # Eat blank line(s) and advance info + trimmed = text.lstrip("\n") + text = trimmed + info = info.next_line(len(text) - len(trimmed) + 1) + + self.add_lines(text, info) + self.ensure_blank_line() + + def visit_entity(self, ent: QAPISchemaDefinition) -> None: + assert ent.info - # Handle the "@var means ``var`` case - doctext = re.sub(r'@([\w-]+)', r'``\1``', doctext) - - rstlist = ViewList() - for line in doctext.splitlines(): - # The reported line number will always be that of the start line - # of the doc comment, rather than the actual location of the error. - # Being more precise would require overhaul of the QAPIDoc class - # to track lines more exactly within all the sub-parts of the doc - # comment, as well as counting lines here. - rstlist.append(line, self._cur_doc.info.fname, - self._cur_doc.info.line) - # Append a blank line -- in some cases rST syntax errors get - # attributed to the line after one with actual text, and if there - # isn't anything in the ViewList corresponding to that then Sphinx - # 1.6's AutodocReporter will then misidentify the source/line location - # in the error message (usually attributing it to the top-level - # .rst file rather than the offending .json file). The extra blank - # line won't affect the rendered output. - rstlist.append("", self._cur_doc.info.fname, self._cur_doc.info.line) - self._sphinx_directive.do_parse(rstlist, node) - - def get_document_nodes(self): - """Return the list of docutils nodes which make up the document""" - return self._top_node.children + try: + self._curr_ent = ent + + # Squish structs and unions together into an "object" directive. + meta = ent.meta + if meta in ("struct", "union"): + meta = "object" + + # This line gets credited to the start of the /definition/. + self.add_line(f".. qapi:{meta}:: {ent.name}", ent.info) + with self.indented(): + self.preamble(ent) + self.visit_sections(ent) + finally: + self._curr_ent = None + + def set_namespace(self, namespace: str, source: str, lineno: int) -> None: + self.add_line_raw( + f".. qapi:namespace:: {namespace}", source, lineno + 1 + ) + self.ensure_blank_line() class QAPISchemaGenDepVisitor(QAPISchemaVisitor): @@ -467,35 +466,145 @@ 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): + + def __init__(self, env: Any, qapidir: str) -> None: self._env = env self._qapidir = qapidir - def visit_module(self, name): + def visit_module(self, name: str) -> None: 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(SphinxDirective): + def run(self) -> Sequence[nodes.Node]: + raise NotImplementedError + + def do_parse(self, rstlist: StringList, node: nodes.Node) -> None: + """ + 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 + "qapifile": directives.unchanged_required, + "namespace": directives.unchanged, + "transmogrify": directives.flag, } has_content = False - def new_serialno(self): + def new_serialno(self) -> str: """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 transmogrify(self, schema: QAPISchema) -> nodes.Element: + logger.info("Transmogrifying QAPI to rST ...") + vis = Transmogrifier() + modules = set() + + if "namespace" in self.options: + vis.set_namespace( + self.options["namespace"], *self.get_source_info() + ) + + for doc in schema.docs: + module_source = doc.info.fname + if module_source not in modules: + vis.visit_module(module_source) + modules.add(module_source) + + if doc.symbol: + ent = schema.lookup_entity(doc.symbol) + assert isinstance(ent, QAPISchemaDefinition) + vis.visit_entity(ent) + else: + vis.visit_freeform(doc) + + logger.info("Transmogrification complete.") - def run(self): + contentnode = nodes.section() + content = vis.result + titles_allowed = True + + logger.info("Transmogrifier running nested parse ...") + with switch_source_input(self.state, content): + if titles_allowed: + node: nodes.Element = nodes.section() + node.document = self.state.document + nested_parse_with_titles(self.state, content, contentnode) + else: + node = nodes.paragraph() + node.document = self.state.document + self.state.nested_parse(content, 0, contentnode) + logger.info("Transmogrifier's nested parse completed.") + + if self.env.app.verbosity >= 2 or os.environ.get("DEBUG"): + argname = "_".join(Path(self.arguments[0]).parts) + name = Path(argname).stem + ".ir" + self.write_intermediate(content, name) + + sys.stdout.flush() + return contentnode + + def write_intermediate(self, content: StringList, filename: str) -> None: + logger.info( + "writing intermediate rST for '%s' to '%s'", + self.arguments[0], + filename, + ) + + srctree = Path(self.env.app.config.qapidoc_srctree).resolve() + outlines = [] + lcol_width = 0 + + for i, line in enumerate(content): + src, lineno = content.info(i) + srcpath = Path(src).resolve() + srcpath = srcpath.relative_to(srctree) + + lcol = f"{srcpath}:{lineno:04d}" + lcol_width = max(lcol_width, len(lcol)) + outlines.append((lcol, line)) + + with open(filename, "w", encoding="UTF-8") as outfile: + for lcol, rcol in outlines: + outfile.write(lcol.rjust(lcol_width)) + outfile.write(" |") + if rcol: + outfile.write(f" {rcol}") + outfile.write("\n") + + def legacy(self, schema: QAPISchema) -> nodes.Element: + vis = QAPISchemaGenRSTVisitor(self) + vis.visit_begin(schema) + for doc in schema.docs: + if doc.symbol: + vis.symbol(doc, schema.lookup_entity(doc.symbol)) + else: + vis.freeform(doc) + return vis.get_document_node() # type: ignore + + def run(self) -> Sequence[nodes.Node]: 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) + transmogrify = "transmogrify" in self.options try: schema = QAPISchema(qapifile) @@ -503,55 +612,123 @@ def run(self): # First tell Sphinx about all the schema files that the # output documentation depends on (including 'qapifile' itself) schema.visit(QAPISchemaGenDepVisitor(env, qapidir)) - - vis = QAPISchemaGenRSTVisitor(self) - vis.visit_begin(schema) - for doc in schema.docs: - if doc.symbol: - vis.symbol(doc, schema.lookup_entity(doc.symbol)) - else: - vis.freeform(doc) - return vis.get_document_nodes() 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 + if transmogrify: + contentnode = self.transmogrify(schema) + else: + contentnode = self.legacy(schema) - 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) + return contentnode.children + + +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 - - -def setup(app): - """ Register qapi-doc directive with Sphinx""" - app.add_config_value('qapidoc_srctree', None, 'env') - app.add_directive('qapi-doc', QAPIDocDirective) - - return dict( - version=__version__, - parallel_read_safe=True, - parallel_write_safe=True - ) + 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: nodes.Node) -> 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: Sphinx) -> ExtensionMetadata: + """Register qapi-doc directive with Sphinx""" + app.setup_extension("qapi_domain") + app.add_config_value("qapidoc_srctree", None, "env") + app.add_directive("qapi-doc", QAPIDocDirective) + app.add_directive("qmp-example", QMPExample) + + return { + "version": __version__, + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/sphinx/qapidoc_legacy.py b/docs/sphinx/qapidoc_legacy.py new file mode 100644 index 00000000000..13520f4c26b --- /dev/null +++ b/docs/sphinx/qapidoc_legacy.py @@ -0,0 +1,440 @@ +# coding=utf-8 +# type: ignore +# +# QEMU qapidoc QAPI file parsing extension +# +# Copyright (c) 2020 Linaro +# +# This work is licensed under the terms of the GNU GPLv2 or later. +# See the COPYING file in the top-level directory. + +""" +qapidoc is a Sphinx extension that implements the qapi-doc directive + +The purpose of this extension is to read the documentation comments +in QAPI schema files, and insert them all into the current document. + +It implements one new rST directive, "qapi-doc::". +Each qapi-doc:: directive takes one argument, which is the +pathname of the schema file to process, relative to the source tree. + +The docs/conf.py file must set the qapidoc_srctree config value to +the root of the QEMU source tree. + +The Sphinx documentation on writing extensions is at: +https://www.sphinx-doc.org/en/master/development/index.html +""" + +import re +import textwrap + +from docutils import nodes +from docutils.statemachine import ViewList +from qapi.error import QAPISemError +from qapi.gen import QAPISchemaVisitor +from qapi.parser import QAPIDoc + + +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:])) + + +class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): + """A QAPI schema visitor which generates docutils/Sphinx nodes + + This class builds up a tree of docutils/Sphinx nodes corresponding + to documentation for the various QAPI objects. To use it, first + create a QAPISchemaGenRSTVisitor object, and call its + visit_begin() method. Then you can call one of the two methods + 'freeform' (to add documentation for a freeform documentation + chunk) or 'symbol' (to add documentation for a QAPI symbol). These + will cause the visitor to build up the tree of document + nodes. Once you've added all the documentation via 'freeform' and + 'symbol' method calls, you can call 'get_document_nodes' to get + the final list of document nodes (in a form suitable for returning + from a Sphinx directive's 'run' method). + """ + def __init__(self, sphinx_directive): + self._cur_doc = None + self._sphinx_directive = sphinx_directive + self._top_node = nodes.section() + self._active_headings = [self._top_node] + + def _make_dlitem(self, term, defn): + """Return a dlitem node with the specified term and definition. + + term should be a list of Text and literal nodes. + defn should be one of: + - a string, which will be handed to _parse_text_into_node + - a list of Text and literal nodes, which will be put into + a paragraph node + """ + dlitem = nodes.definition_list_item() + dlterm = nodes.term('', '', *term) + dlitem += dlterm + if defn: + dldef = nodes.definition() + if isinstance(defn, list): + dldef += nodes.paragraph('', '', *defn) + else: + self._parse_text_into_node(defn, dldef) + dlitem += dldef + return dlitem + + def _make_section(self, title): + """Return a section node with optional title""" + section = nodes.section(ids=[self._sphinx_directive.new_serialno()]) + if title: + section += nodes.title(title, title) + return section + + def _nodes_for_ifcond(self, ifcond, with_if=True): + """Return list of Text, literal nodes for the ifcond + + Return a list which gives text like ' (If: condition)'. + If with_if is False, we don't return the "(If: " and ")". + """ + + doc = ifcond.docgen() + if not doc: + return [] + doc = nodes.literal('', doc) + if not with_if: + return [doc] + + nodelist = [nodes.Text(' ('), nodes.strong('', 'If: ')] + nodelist.append(doc) + nodelist.append(nodes.Text(')')) + return nodelist + + def _nodes_for_one_member(self, member): + """Return list of Text, literal nodes for this member + + Return a list of doctree nodes which give text like + 'name: type (optional) (If: ...)' suitable for use as the + 'term' part of a definition list item. + """ + term = [nodes.literal('', member.name)] + if member.type.doc_type(): + term.append(nodes.Text(': ')) + term.append(nodes.literal('', member.type.doc_type())) + if member.optional: + term.append(nodes.Text(' (optional)')) + if member.ifcond.is_present(): + term.extend(self._nodes_for_ifcond(member.ifcond)) + return term + + 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 'branches' part of a definition list. + """ + term = [nodes.Text(' when '), + 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, 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 = dedent(section.text) + else: + defn = [nodes.Text('Not documented')] + + dlnode += self._make_dlitem(term, defn) + + if base: + dlnode += self._make_dlitem([nodes.Text('The members of '), + nodes.literal('', base.doc_type())], + 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 [] + + section = self._make_section(what) + section += dlnode + return [section] + + def _nodes_for_enum_values(self, doc): + """Return list of doctree nodes for the table of enum values""" + seen_item = False + dlnode = nodes.definition_list() + for section in doc.args.values(): + termtext = [nodes.literal('', section.member.name)] + if section.member.ifcond.is_present(): + termtext.extend(self._nodes_for_ifcond(section.member.ifcond)) + # TODO drop fallbacks when undocumented members are outlawed + if section.text: + defn = dedent(section.text) + else: + defn = [nodes.Text('Not documented')] + + dlnode += self._make_dlitem(termtext, defn) + seen_item = True + + if not seen_item: + return [] + + section = self._make_section('Values') + section += dlnode + return [section] + + def _nodes_for_arguments(self, doc, arg_type): + """Return list of doctree nodes for the arguments section""" + 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('', arg_type.name)], + None) + section += dlnode + return [section] + + return self._nodes_for_members(doc, 'Arguments') + + def _nodes_for_features(self, doc): + """Return list of doctree nodes for the table of features""" + seen_item = False + dlnode = nodes.definition_list() + for section in doc.features.values(): + dlnode += self._make_dlitem( + [nodes.literal('', section.member.name)], dedent(section.text)) + seen_item = True + + if not seen_item: + return [] + + section = self._make_section('Features') + section += dlnode + return [section] + + def _nodes_for_sections(self, doc): + """Return list of doctree nodes for additional sections""" + nodelist = [] + for section in doc.sections: + if section.kind == QAPIDoc.Kind.TODO: + # Hide TODO: sections + continue + + if section.kind == QAPIDoc.Kind.PLAIN: + # 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.kind.name.title()) + self._parse_text_into_node(dedent(section.text), snode) + nodelist.append(snode) + return nodelist + + def _nodes_for_if_section(self, ifcond): + """Return list of doctree nodes for the "If" section""" + nodelist = [] + if ifcond.is_present(): + snode = self._make_section('If') + snode += nodes.paragraph( + '', '', *self._nodes_for_ifcond(ifcond, with_if=False) + ) + nodelist.append(snode) + return nodelist + + def _add_doc(self, typ, sections): + """Add documentation for a command/object/enum... + + We assume we're documenting the thing defined in self._cur_doc. + typ is the type of thing being added ("Command", "Object", etc) + + sections is a list of nodes for sections to add to the definition. + """ + + doc = self._cur_doc + snode = nodes.section(ids=[self._sphinx_directive.new_serialno()]) + snode += nodes.title('', '', *[nodes.literal(doc.symbol, doc.symbol), + nodes.Text(' (' + typ + ')')]) + self._parse_text_into_node(doc.body.text, snode) + for s in sections: + if s is not None: + snode += s + self._add_node_to_current_heading(snode) + + def visit_enum_type(self, name, info, ifcond, features, members, prefix): + doc = self._cur_doc + self._add_doc('Enum', + self._nodes_for_enum_values(doc) + + self._nodes_for_features(doc) + + self._nodes_for_sections(doc) + + self._nodes_for_if_section(ifcond)) + + def visit_object_type(self, name, info, ifcond, features, + 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, 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, + alternatives): + doc = self._cur_doc + self._add_doc('Alternate', + self._nodes_for_members(doc, 'Members') + + self._nodes_for_features(doc) + + self._nodes_for_sections(doc) + + self._nodes_for_if_section(ifcond)) + + def visit_command(self, name, info, ifcond, features, arg_type, + ret_type, gen, success_response, boxed, allow_oob, + allow_preconfig, coroutine): + doc = self._cur_doc + self._add_doc('Command', + self._nodes_for_arguments(doc, arg_type) + + self._nodes_for_features(doc) + + self._nodes_for_sections(doc) + + self._nodes_for_if_section(ifcond)) + + 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) + + self._nodes_for_features(doc) + + self._nodes_for_sections(doc) + + self._nodes_for_if_section(ifcond)) + + def symbol(self, doc, entity): + """Add documentation for one symbol to the document tree + + This is the main entry point which causes us to add documentation + nodes for a symbol (which could be a 'command', 'object', 'event', + etc). We do this by calling 'visit' on the schema entity, which + will then call back into one of our visit_* methods, depending + on what kind of thing this symbol is. + """ + self._cur_doc = doc + entity.visit(self) + self._cur_doc = None + + def _start_new_heading(self, heading, level): + """Start a new heading at the specified heading level + + Create a new section whose title is 'heading' and which is placed + in the docutils node tree as a child of the most recent level-1 + heading. Subsequent document sections (commands, freeform doc chunks, + etc) will be placed as children of this new heading section. + """ + if len(self._active_headings) < level: + raise QAPISemError(self._cur_doc.info, + 'Level %d subheading found outside a ' + 'level %d heading' + % (level, level - 1)) + snode = self._make_section(heading) + 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""" + self._active_headings[-1] += node + + def freeform(self, doc): + """Add a piece of 'freeform' documentation to the document tree + + A 'freeform' document chunk doesn't relate to any particular + symbol (for instance, it could be an introduction). + + If the freeform document starts with a line of the form + '= Heading text', this is a section or subsection heading, with + the heading level indicated by the number of '=' signs. + """ + + # QAPIDoc documentation says free-form documentation blocks + # must have only a body section, nothing else. + assert not doc.sections + assert not doc.args + assert not doc.features + self._cur_doc = doc + + text = doc.body.text + if re.match(r'=+ ', text): + # Section/subsection heading (if present, will always be + # the first line of the block) + (heading, _, text) = text.partition('\n') + (leader, _, heading) = heading.partition(' ') + node = self._start_new_heading(heading, len(leader)) + if text == '': + return + else: + node = nodes.container() + + self._parse_text_into_node(text, node) + self._cur_doc = None + + def _parse_text_into_node(self, doctext, node): + """Parse a chunk of QAPI-doc-format text into the node + + The doc comment can contain most inline rST markup, including + bulleted and enumerated lists. + As an extra permitted piece of markup, @var will be turned + into ``var``. + """ + + # Handle the "@var means ``var`` case + doctext = re.sub(r'@([\w-]+)', r'``\1``', doctext) + + rstlist = ViewList() + for line in doctext.splitlines(): + # The reported line number will always be that of the start line + # of the doc comment, rather than the actual location of the error. + # Being more precise would require overhaul of the QAPIDoc class + # to track lines more exactly within all the sub-parts of the doc + # comment, as well as counting lines here. + rstlist.append(line, self._cur_doc.info.fname, + self._cur_doc.info.line) + # Append a blank line -- in some cases rST syntax errors get + # attributed to the line after one with actual text, and if there + # isn't anything in the ViewList corresponding to that then Sphinx + # 1.6's AutodocReporter will then misidentify the source/line location + # in the error message (usually attributing it to the top-level + # .rst file rather than the offending .json file). The extra blank + # line won't affect the rendered output. + rstlist.append("", self._cur_doc.info.fname, self._cur_doc.info.line) + self._sphinx_directive.do_parse(rstlist, node) + + def get_document_node(self): + """Return the root docutils node which makes up the document""" + return self._top_node diff --git a/docs/sphinx/qmp_lexer.py b/docs/sphinx/qmp_lexer.py index a59de8a079c..7b3b808d124 100644 --- a/docs/sphinx/qmp_lexer.py +++ b/docs/sphinx/qmp_lexer.py @@ -24,7 +24,7 @@ class QMPExampleMarkersLexer(RegexLexer): 'root': [ (r'-> ', token.Generic.Prompt), (r'<- ', token.Generic.Prompt), - (r' ?\.{3} ?', token.Generic.Prompt), + (r'\.{3}( .* \.{3})?', token.Comment.Multiline), ] } diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index b2dea54eeda..43d27d83cb5 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -1,10 +1,10 @@ -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``, ``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 +AST2500 with an ARM1176JZS CPU (800MHz), the AST2600 with dual cores ARM Cortex-A7 CPUs (1.2GHz). The SoC comes with RAM, Gigabit ethernet, USB, SD/MMC, USB, SPI, I2C, @@ -14,7 +14,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 : @@ -30,7 +31,6 @@ AST2500 SoC based machines : AST2600 SoC based machines : - ``ast2600-evb`` Aspeed AST2600 Evaluation board (Cortex-A7) -- ``tacoma-bmc`` OpenPOWER Witherspoon POWER9 AST2600 BMC - ``rainier-bmc`` IBM Rainier POWER10 BMC - ``fuji-bmc`` Facebook Fuji BMC - ``bletchley-bmc`` Facebook Bletchley BMC @@ -66,6 +66,7 @@ Supported devices * eMMC Boot Controller (dummy) * PECI Controller (minimal) * I3C Controller + * Internal Bridge Controller (SLI dummy) Missing devices @@ -95,6 +96,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 @@ -104,14 +112,10 @@ To boot a kernel directly from a Linux build tree: -dtb arch/arm/boot/dts/aspeed-ast2600-evb.dtb \ -initrd rootfs.cpio -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 +Booting from a flash image +^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Options specific to Aspeed machines are : +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 @@ -122,10 +126,12 @@ Options specific to Aspeed machines are : * ``spi-model`` to change the default SPI Flash model. - * ``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 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 To use other flash models, for instance a different FMC chip and a bigger (64M) SPI for the ``ast2500-evb`` machine, run : @@ -157,6 +163,78 @@ 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 : @@ -164,6 +242,180 @@ under Linux), use : -M ast2500-evb,bmc-console=uart3 +Aspeed 2700 family boards (``ast2700-evb``) +================================================================== + +The QEMU Aspeed machines model BMCs of Aspeed evaluation boards. +They are based on different releases of the Aspeed SoC : +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. + +AST2700 SoC based machines : + +- ``ast2700-evb`` Aspeed AST2700 Evaluation board (Cortex-A35) +- ``ast2700fc`` Aspeed AST2700 Evaluation board (Cortex-A35 + Cortex-M4) + +Supported devices +----------------- + * Interrupt Controller + * Timer Controller + * RTC Controller + * I2C Controller + * System Control Unit (SCU) + * SRAM mapping + * X-DMA Controller (basic interface) + * Static Memory Controller (SMC or FMC) - Only SPI Flash support + * SPI Memory Controller + * USB 2.0 Controller + * SD/MMC storage controllers + * SDRAM controller (dummy interface for basic settings and training) + * Watchdog Controller + * GPIO Controller (Master only) + * UART + * Ethernet controllers + * Front LEDs (PCA9552 on I2C bus) + * LPC Peripheral Controller (a subset of subdevices are supported) + * Hash/Crypto Engine (HACE) - Hash support only. TODO: Crypto + * ADC + * eMMC Boot Controller (dummy) + * PECI Controller (minimal) + * I3C Controller + * Internal Bridge Controller (SLI dummy) + +Missing devices +--------------- + * PWM and Fan Controller + * Slave GPIO Controller + * Super I/O Controller + * PCI-Express 1 Controller + * Graphic Display Controller + * MCTP Controller + * Mailbox Controller + * Virtual UART + * eSPI Controller + +Boot options +------------ + +Images can be downloaded from the ASPEED Forked OpenBMC GitHub release repository : + + https://github.com/AspeedTech-BMC/openbmc/releases + +Booting the ast2700-evb machine +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Boot the AST2700 machine from the flash image. + +There are two supported methods for booting the AST2700 machine with a flash image: + +Manual boot using ``-device loader``: + +It causes all 4 CPU cores to start execution from address ``0x430000000``, which +corresponds to the BL31 image load address. + +.. 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 + +Boot using a virtual boot ROM (``-bios``): + +If users do not specify the ``-bios option``, QEMU will attempt to load the +default vbootrom image ``ast27x0_bootrom.bin`` from either the current working +directory or the ``pc-bios`` directory within the QEMU source tree. + +.. code-block:: bash + + $ qemu-system-aarch64 -M ast2700-evb \ + -drive file=image-bmc,format=raw,if=mtd \ + -nographic + +The ``-bios`` option allows users to specify a custom path for the vbootrom +image to be loaded during boot. This will load the vbootrom image from the +specified path in the ${HOME} directory. + +.. code-block:: bash + + -bios ${HOME}/ast27x0_bootrom.bin + +Booting the ast2700fc machine +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +AST2700 features four Cortex-A35 primary processors and two Cortex-M4 coprocessors. +**ast2700-evb** machine focuses on emulating the four Cortex-A35 primary processors, +**ast2700fc** machine extends **ast2700-evb** by adding support for the two Cortex-M4 coprocessors. + +Steps to boot the AST2700fc machine: + +1. Ensure you have the following AST2700A1 binaries available in a directory + + * u-boot-nodtb.bin + * u-boot.dtb + * bl31.bin + * optee/tee-raw.bin + * image-bmc + * zephyr-aspeed-ssp.elf (for SSP firmware, CPU 5) + * zephyr-aspeed-tsp.elf (for TSP firmware, CPU 6) + +2. Execute the following command to start ``ast2700fc`` machine: + +.. code-block:: bash + + IMGDIR=ast2700-default + UBOOT_SIZE=$(stat --format=%s -L ${IMGDIR}/u-boot-nodtb.bin) + + $ qemu-system-aarch64 -M ast2700fc \ + -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 \ + -drive file=${IMGDIR}/image-bmc,if=mtd,format=raw \ + -device loader,file=${IMGDIR}/zephyr-aspeed-ssp.elf,cpu-num=4 \ + -device loader,file=${IMGDIR}/zephyr-aspeed-tsp.elf,cpu-num=5 \ + -serial pty -serial pty -serial pty \ + -snapshot \ + -S -nographic + +After launching QEMU, serial devices will be automatically redirected. +Example output: + +.. code-block:: bash + + char device redirected to /dev/pts/55 (label serial0) + char device redirected to /dev/pts/56 (label serial1) + char device redirected to /dev/pts/57 (label serial2) + +- serial0: Console for the four Cortex-A35 primary processors. +- serial1 and serial2: Consoles for the two Cortex-M4 coprocessors. + +Use ``tio`` or another terminal emulator to connect to the consoles: + +.. code-block:: bash + + $ tio /dev/pts/55 + $ tio /dev/pts/56 + $ tio /dev/pts/57 + + Aspeed minibmc family boards (``ast1030-evb``) ================================================================== @@ -224,51 +476,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 00000000000..2adcc4b4c16 --- /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 index b09ba5c5486..03cc5618c38 100644 --- a/docs/system/arm/bananapi_m2u.rst +++ b/docs/system/arm/bananapi_m2u.rst @@ -22,7 +22,10 @@ The Banana Pi M2U machine supports the following devices: * EMAC ethernet * GMAC ethernet * Clock Control Unit + * SATA * TWI (I2C) + * USB 2.0 + * Hardware Watchdog Limitations """"""""""" @@ -31,9 +34,7 @@ Currently, Banana Pi M2U does *not* support the following features: - Graphical output via HDMI, GPU and/or the Display Engine - Audio output -- Hardware Watchdog - Real Time Clock -- USB 2.0 interfaces 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`` @@ -124,16 +125,15 @@ And then boot it. $ qemu-system-arm -M bpim2u -nographic -sd sd.img -Banana Pi M2U integration tests -""""""""""""""""""""""""""""""" +Banana Pi M2U functional tests +"""""""""""""""""""""""""""""" -The Banana Pi M2U machine has several integration tests included. +The Banana Pi M2U machine has several functional 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 + $ QEMU_TEST_ALLOW_LARGE_STORAGE=1 \ + pyvenv/bin/meson test --suite thorough func-arm-arm_bpim2u diff --git a/docs/system/arm/cpu-features.rst b/docs/system/arm/cpu-features.rst index a5fb929243c..37d5dfd15b3 100644 --- a/docs/system/arm/cpu-features.rst +++ b/docs/system/arm/cpu-features.rst @@ -219,8 +219,11 @@ Below is the list of TCG VCPU features and their descriptions. ``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 +``pauth-qarma5`` + When ``pauth`` is enabled, select the architected QARMA5 algorithm. + +Without ``pauth-impdef``, ``pauth-qarma3`` or ``pauth-qarma5`` enabled, +the QEMU impdef 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. diff --git a/docs/system/arm/cubieboard.rst b/docs/system/arm/cubieboard.rst index 58c4a2d3ea6..90d24c73a13 100644 --- a/docs/system/arm/cubieboard.rst +++ b/docs/system/arm/cubieboard.rst @@ -15,4 +15,5 @@ Emulated devices: - 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 3df936fc356..78c2fd2113c 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -3,48 +3,80 @@ 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_AFP (Alternate floating-point behavior) +- 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_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_ETS (Enhanced Translation Synchronization) +- 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) @@ -57,9 +89,17 @@ the following architecture extensions: - 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) @@ -67,8 +107,9 @@ the following architecture extensions: - 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 (Enhacements to 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) @@ -77,6 +118,7 @@ the following architecture extensions: - 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_RPRES (Increased precision of FRECPE and FRSQRTE) - FEAT_S2FWB (Stage 2 forced Write-Back) - FEAT_SB (Speculation Barrier) - FEAT_SEL2 (Secure EL2) @@ -90,8 +132,19 @@ 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) @@ -101,12 +154,12 @@ 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) +- FEAT_XS (XS attribute) 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 00000000000..86894bc02b7 --- /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 00000000000..e19274e75c8 --- /dev/null +++ b/docs/system/arm/fby35.rst @@ -0,0 +1,52 @@ +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. + +This machine model supports emulation of the boot from the CE0 flash device by +setting option ``execute-in-place``. When using this option, the CPU fetches +instructions to execute by reading CE0 and not from a preloaded ROM +initialized at machine init time. As a result, execution will be slower. diff --git a/docs/system/arm/gumstix.rst b/docs/system/arm/gumstix.rst deleted file mode 100644 index cb373139dcb..00000000000 --- 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/imx8mp-evk.rst b/docs/system/arm/imx8mp-evk.rst new file mode 100644 index 00000000000..b2f7d29ade5 --- /dev/null +++ b/docs/system/arm/imx8mp-evk.rst @@ -0,0 +1,62 @@ +NXP i.MX 8M Plus Evaluation Kit (``imx8mp-evk``) +================================================ + +The ``imx8mp-evk`` machine models the i.MX 8M Plus Evaluation Kit, based on an +i.MX 8M Plus SoC. + +Supported devices +----------------- + +The ``imx8mp-evk`` machine implements the following devices: + + * Up to 4 Cortex-A53 cores + * Generic Interrupt Controller (GICv3) + * 4 UARTs + * 3 USDHC Storage Controllers + * 1 Designware PCI Express Controller + * 1 Ethernet Controller + * 2 Designware USB 3 Controllers + * 5 GPIO Controllers + * 6 I2C Controllers + * 3 SPI Controllers + * 3 Watchdogs + * 6 General Purpose Timers + * Secure Non-Volatile Storage (SNVS) including an RTC + * Clock Tree + +Boot options +------------ + +The ``imx8mp-evk`` machine can start a Linux kernel directly using the standard +``-kernel`` functionality. + +Direct Linux Kernel Boot +'''''''''''''''''''''''' + +Probably the easiest way to get started with a whole Linux system on the machine +is to generate an image with Buildroot. Version 2024.11.1 is tested at the time +of writing and involves two steps. First run the following commands in the +toplevel directory of the Buildroot source tree: + +.. code-block:: bash + + $ make freescale_imx8mpevk_defconfig + $ make + +Once finished successfully there is an ``output/image`` subfolder. Navigate into +it and resize the SD card image to a power of two: + +.. code-block:: bash + + $ qemu-img resize sdcard.img 256M + +Now that everything is prepared the machine can be started as follows: + +.. code-block:: bash + + $ qemu-system-aarch64 -M imx8mp-evk -smp 4 -m 3G \ + -display none -serial null -serial stdio \ + -kernel Image \ + -dtb imx8mp-evk.dtb \ + -append "root=/dev/mmcblk2p2" \ + -drive file=sdcard.img,if=sd,bus=2,format=raw,id=mmcblk2 diff --git a/docs/system/arm/mainstone.rst b/docs/system/arm/mainstone.rst deleted file mode 100644 index 05310f42c7f..00000000000 --- 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 00000000000..8871138ab3e --- /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 00000000000..c5d35af1d44 --- /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 8a75beb3a08..a305935cc49 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 cd9edf5d88b..00000000000 --- 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 0424cae4b01..e4827fb43a1 100644 --- a/docs/system/arm/nuvoton.rst +++ b/docs/system/arm/nuvoton.rst @@ -1,12 +1,13 @@ -Nuvoton iBMC boards (``*-bmc``, ``npcm750-evb``, ``quanta-gsj``) -================================================================ +Nuvoton iBMC boards (``kudo-bmc``, ``mori-bmc``, ``npcm750-evb``, ``quanta-gbs-bmc``, ``quanta-gsj``, ``npcm845-evb``) +====================================================================================================================== -The `Nuvoton iBMC`_ chips (NPCM7xx) are a family of ARM-based SoCs that are +The `Nuvoton iBMC`_ chips are a family of Arm-based SoCs that are designed to be used as Baseboard Management Controllers (BMCs) in various -servers. They all feature one or two ARM Cortex-A9 CPU cores, as well as an -assortment of peripherals targeted for either Enterprise or Data Center / -Hyperscale applications. The former is a superset of the latter, so NPCM750 has -all the peripherals of NPCM730 and more. +servers. Currently there are two families: NPCM7XX series and +NPCM8XX series. NPCM7XX series feature one or two Arm Cortex-A9 CPU cores, +while NPCM8XX feature 4 Arm Cortex-A35 CPU cores. Both series contain a +different assortment of peripherals targeted for either Enterprise or Data +Center / Hyperscale applications. .. _Nuvoton iBMC: https://www.nuvoton.com/products/cloud-computing/ibmc/ @@ -27,6 +28,11 @@ There are also two more SoCs, NPCM710 and NPCM705, which are single-core variants of NPCM750 and NPCM730, respectively. These are currently not supported by QEMU. +The NPCM8xx SoC is the successor of the NPCM7xx SoC. It has 4 Cortex-A35 cores. +The following machines are based on this chip : + +- ``npcm845-evb`` Nuvoton NPCM845 Evaluation board + Supported devices ----------------- @@ -62,6 +68,8 @@ Missing devices * System Wake-up Control (SWC) * Shared memory (SHM) * eSPI slave interface + * Block-transfer interface (8XX only) + * Virtual UART (8XX only) * Ethernet controller (GMAC) * USB device (USBD) @@ -76,6 +84,11 @@ Missing devices * Video capture * Encoding compression engine * Security features + * I3C buses (8XX only) + * Temperature sensor interface (8XX only) + * Virtual UART (8XX only) + * Flash monitor (8XX only) + * JTAG master (8XX only) Boot options ------------ diff --git a/docs/system/arm/orangepi.rst b/docs/system/arm/orangepi.rst index 9afa54213b0..d81f6c3bfd2 100644 --- a/docs/system/arm/orangepi.rst +++ b/docs/system/arm/orangepi.rst @@ -119,7 +119,7 @@ Orange Pi PC images Note that the mainline kernel does not have a root filesystem. You may provide it with an official Orange Pi PC image from the official website: - http://www.orangepi.org/downloadresources/ + http://www.orangepi.org/html/serviceAndSupport/index.html Another possibility is to run an Armbian image for Orange Pi PC which can be downloaded from: @@ -213,7 +213,7 @@ including the Orange Pi PC. NetBSD 9.0 is known to work best for the Orange Pi P board and provides a fully working system with serial console, networking and storage. For the Orange Pi PC machine, get the 'evbarm-earmv7hf' based image from: - https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/evbarm-earmv7hf/binary/gzimg/armv7.img.gz + https://archive.netbsd.org/pub/NetBSD-archive/NetBSD-9.0/evbarm-earmv7hf/binary/gzimg/armv7.img.gz The image requires manually installing U-Boot in the image. Build U-Boot with the orangepi_pc_defconfig configuration as described in the previous section. @@ -252,14 +252,14 @@ and set the following environment variables before booting: Optionally you may save the environment variables to SD card with 'saveenv'. To continue booting simply give the 'boot' command and NetBSD boots. -Orange Pi PC integration tests -"""""""""""""""""""""""""""""" +Orange Pi PC functional tests +""""""""""""""""""""""""""""" -The Orange Pi PC machine has several integration tests included. +The Orange Pi PC machine has several functional tests included. To run the whole set of tests, build QEMU from source and simply -provide the following command: +provide the following command from the build directory: .. code-block:: bash - $ AVOCADO_ALLOW_LARGE_STORAGE=yes avocado --show=app,console run \ - -t machine:orangepi-pc tests/avocado/boot_linux_console.py + $ QEMU_TEST_ALLOW_LARGE_STORAGE=1 \ + pyvenv/bin/meson test --suite thorough func-arm-arm_orangepi diff --git a/docs/system/arm/palm.rst b/docs/system/arm/palm.rst deleted file mode 100644 index 47ff9b36d46..00000000000 --- 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 922fe375a67..44eec3f1c33 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 bca61608ff8..2bf3fc8d59d 100644 --- a/docs/system/arm/sbsa.rst +++ b/docs/system/arm/sbsa.rst @@ -1,12 +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. +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. @@ -35,16 +39,30 @@ 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 provided by the board model to the firmware is not intended -to be a complete compliant DT. It currently reports: +The devicetree reports: - CPUs - memory - platform version - GIC addresses + - NUMA node id for CPUs and memory + - CPU topology information Platform version '''''''''''''''' @@ -70,4 +88,7 @@ Platform version changes: GIC ITS information is present in devicetree. 0.3 - The USB controller is an XHCI device, not EHCI + 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 d7265b763d4..511e3eb9ac1 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,11 +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. @@ -34,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 --------------- @@ -51,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 3e3839e9231..38f29c73e71 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 e1697ac8f48..6a719b95863 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -1,3 +1,5 @@ +.. _arm-virt: + 'virt' generic virtual platform (``virt``) ========================================== @@ -19,6 +21,10 @@ of the 5.0 release and ``virt-5.0`` of the 5.1 release. Migration is not guaranteed to work between different QEMU releases for the non-versioned ``virt`` machine type. +VM migration is not guaranteed when using ``-cpu max``, as features +supported may change between QEMU versions. To ensure your VM can be +migrated, it is recommended to use another cpu model instead. + Supported devices """"""""""""""""" @@ -26,7 +32,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,6 +54,10 @@ 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) @@ -60,14 +70,28 @@ Supported guest CPU types: - ``cortex-a76`` (64-bit) - ``cortex-a710`` (64-bit) - ``a64fx`` (64-bit) -- ``host`` (with KVM only) +- ``host`` (with KVM and HVF only) - ``neoverse-n1`` (64-bit) - ``neoverse-v1`` (64-bit) -- ``max`` (same as ``host`` for KVM; best possible emulation with TCG) +- ``neoverse-n2`` (64-bit) +- ``max`` (same as ``host`` for KVM and HVF; 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 @@ -95,7 +119,13 @@ 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. @@ -114,6 +144,10 @@ highmem-mmio Set ``on``/``off`` to enable/disable the high memory region for PCI MMIO. The default is ``on``. +highmem-mmio-size + Set the high memory region size for PCI MMIO. Must be a power of 2 and + greater than or equal to the default size (512G). + gic-version Specify the version of the Generic Interrupt Controller (GIC) to provide. Valid values are: @@ -143,10 +177,18 @@ iommu ``smmuv3`` Create an SMMUv3 +default-bus-bypass-iommu + Set ``on``/``off`` to enable/disable `bypass_iommu + `_ + for default root bus. + ras Set ``on``/``off`` to enable/disable reporting host memory errors to a guest using ACPI and guest external abort exceptions. The default is off. +acpi + Set ``on``/``off``/``auto`` to enable/disable ACPI. + dtb-randomness Set ``on``/``off`` to pass random seeds via the guest DTB rng-seed and kaslr-seed nodes (in both "/chosen" and @@ -160,6 +202,14 @@ dtb-randomness dtb-kaslr-seed A deprecated synonym for dtb-randomness. +x-oem-id + Set string (up to 6 bytes) to override the default value of field OEMID in ACPI + table header. + +x-oem-table-id + Set string (up to 8 bytes) to override the default value of field OEM Table ID + in ACPI table header. + Linux guest kernel configuration """""""""""""""""""""""""""""""" diff --git a/docs/system/arm/vmapple.rst b/docs/system/arm/vmapple.rst new file mode 100644 index 00000000000..35c329ea5a8 --- /dev/null +++ b/docs/system/arm/vmapple.rst @@ -0,0 +1,65 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +VMApple machine emulation +======================================================================================== + +VMApple is the device model that the macOS built-in hypervisor called "Virtualization.framework" +exposes to Apple Silicon macOS guests. The "vmapple" machine model in QEMU implements the same +device model, but does not use any code from Virtualization.Framework. + +Prerequisites +------------- + +To run the vmapple machine model, you need to + + * Run on Apple Silicon + * Run on macOS 12.0 or above + * Have an already installed copy of a Virtualization.Framework macOS 12 virtual + machine. Note that newer versions than 12.x are currently NOT supported on + the guest side. I will assume that you installed it using the + `macosvm `__ CLI. + +First, we need to extract the UUID from the virtual machine that you installed. You can do this +by running the shell script in contrib/vmapple/uuid.sh on the macosvm.json file. + +.. code-block:: bash + :caption: uuid.sh script to extract the UUID from a macosvm.json file + + $ contrib/vmapple/uuid.sh "path/to/macosvm.json" + +Now we also need to trim the aux partition. It contains metadata that we can just discard: + +.. code-block:: bash + :caption: Command to trim the aux file + + $ dd if="aux.img" of="aux.img.trimmed" bs=$(( 0x4000 )) skip=1 + +How to run +---------- + +Then, we can launch QEMU with the Virtualization.Framework pre-boot environment and the readily +installed target disk images. I recommend to port forward the VM's ssh and vnc ports to the host +to get better interactive access into the target system: + +.. code-block:: bash + :caption: Example execution command line + + $ UUID="$(contrib/vmapple/uuid.sh 'macosvm.json')" + $ AVPBOOTER="/System/Library/Frameworks/Virtualization.framework/Resources/AVPBooter.vmapple2.bin" + $ AUX="aux.img.trimmed" + $ DISK="disk.img" + $ qemu-system-aarch64 \ + -serial mon:stdio \ + -m 4G \ + -accel hvf \ + -M vmapple,uuid="$UUID" \ + -bios "$AVPBOOTER" \ + -drive file="$AUX",if=pflash,format=raw \ + -drive file="$DISK",if=pflash,format=raw \ + -drive file="$AUX",if=none,id=aux,format=raw \ + -drive file="$DISK",if=none,id=root,format=raw \ + -device vmapple-virtio-blk-pci,variant=aux,drive=aux \ + -device vmapple-virtio-blk-pci,variant=root,drive=root \ + -netdev user,id=net0,ipv6=off,hostfwd=tcp::2222-:22,hostfwd=tcp::5901-:5900 \ + -device virtio-net-pci,netdev=net0 + diff --git a/docs/system/arm/xenpvh.rst b/docs/system/arm/xenpvh.rst index e1655c7ab87..430ac2c02e7 100644 --- a/docs/system/arm/xenpvh.rst +++ b/docs/system/arm/xenpvh.rst @@ -1,34 +1,39 @@ -XENPVH (``xenpvh``) +Xen Device Emulation Backend (``xenpvh``) ========================================= -This machine creates a IOREQ server to register/connect with Xen Hypervisor. -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. +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. -More information about TPM use and installing swtpm linux application can be -found at: docs/specs/tpm.rst. +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 & + --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 + -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 d2d1b266926..c5f35f28e4f 100644 --- a/docs/system/arm/xlnx-versal-virt.rst +++ b/docs/system/arm/xlnx-versal-virt.rst @@ -178,6 +178,9 @@ Run the following at the U-Boot prompt: fdt set /chosen/dom0 reg <0x00000000 0x40000000 0x0 0x03100000> booti 30000000 - 20000000 +It's possible to change the OSPI flash model emulated by using the machine model +option ``ospi-flash``. + BBRAM File Backend """""""""""""""""" BBRAM can have an optional file backend, which must be a seekable @@ -194,7 +197,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 """""""""""""""""" @@ -212,7 +215,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. diff --git a/docs/system/arm/xlnx-zcu102.rst b/docs/system/arm/xlnx-zcu102.rst new file mode 100644 index 00000000000..534cd1dc887 --- /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 00000000000..ade18a3fe13 --- /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 d2d5949e102..00000000000 --- 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 8b057f812f2..5e1b33ee22b 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/confidential-guest-support.rst b/docs/system/confidential-guest-support.rst index 0c490dbda2b..66129fbab64 100644 --- a/docs/system/confidential-guest-support.rst +++ b/docs/system/confidential-guest-support.rst @@ -38,6 +38,7 @@ Supported mechanisms Currently supported confidential guest mechanisms are: * AMD Secure Encrypted Virtualization (SEV) (see :doc:`i386/amd-memory-encryption`) +* Intel Trust Domain Extension (TDX) (see :doc:`i386/tdx`) * POWER Protected Execution Facility (PEF) (see :ref:`power-papr-protected-execution-facility-pef`) * s390x Protected Virtualization (PV) (see :doc:`s390x/protvirt`) diff --git a/docs/system/cpu-hotplug.rst b/docs/system/cpu-hotplug.rst index 015ce2b6ec3..cc50937c36c 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 }, { - "qom-path": "/machine/unattached/device[0]", - "type": "IvyBridge-IBRS-x86_64-cpu", - "vcpus-count": 1, "props": { - "socket-id": 0, "core-id": 0, + "socket-id": 0, "thread-id": 0 - } + }, + "qom-path": "/machine/unattached/device[0]", + "type": "IvyBridge-IBRS-x86_64-cpu", + "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 f3f3b60be10..38b9bae3102 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 7f6368f999b..6a770ca8351 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 @@ -71,6 +71,16 @@ mixture of host CPU models between machines, if live migration compatibility is required, use the newest CPU model that is compatible across all desired hosts. +``ClearwaterForest`` + Intel Xeon Processor (ClearwaterForest, 2025) + +``SierraForest``, ``SierraForest-v2`` + Intel Xeon Processor (SierraForest, 2024), SierraForest-v2 mitigates + the GDS and RFDS vulnerabilities with stepping 3. + +``GraniteRapids``, ``GraniteRapids-v2`` + Intel Xeon Processor (GraniteRapids, 2024) + ``Cascadelake-Server``, ``Cascadelake-Server-noTSX`` Intel Xeon Processor (Cascade Lake, 2019), with "stepping" levels 6 or 7 only. (The Cascade Lake Xeon processor with *stepping 5 is @@ -181,7 +191,7 @@ features are included if using "Host passthrough" or "Host model". CVE-2018-12127, [MSBDS] CVE-2018-12126). This is an MSR (Model-Specific Register) feature rather than a CPUID feature, - so it will not appear in the Linux ``/proc/cpuinfo`` in the host or + therefore it will not appear in the Linux ``/proc/cpuinfo`` in the host or guest. Instead, the host kernel uses it to populate the MDS vulnerability file in ``sysfs``. @@ -189,10 +199,10 @@ features are included if using "Host passthrough" or "Host model". affected} in the ``/sys/devices/system/cpu/vulnerabilities/mds`` file. ``taa-no`` - Recommended to inform that the guest that the host is ``not`` + Recommended to inform the guest that the host is ``not`` vulnerable to CVE-2019-11135, TSX Asynchronous Abort (TAA). - This too is an MSR feature, so it does not show up in the Linux + This is also an MSR feature, therefore it does not show up in the Linux ``/proc/cpuinfo`` in the host or guest. It should only be enabled for VMs if the host reports ``Not affected`` @@ -214,7 +224,7 @@ features are included if using "Host passthrough" or "Host model". By disabling TSX, KVM-based guests can avoid paying the price of mitigating TSX-based attacks. - Note that ``tsx-ctrl`` too is an MSR feature, so it does not show + Note that ``tsx-ctrl`` is also an MSR feature, therefore it does not show up in the Linux ``/proc/cpuinfo`` in the host or guest. To validate that Intel TSX is indeed disabled for the guest, there are @@ -223,6 +233,38 @@ features are included if using "Host passthrough" or "Host model". ``/sys/devices/system/cpu/vulnerabilities/tsx_async_abort`` file in the guest should report ``Mitigation: TSX disabled``. +``bhi-no`` + Recommended to inform the guest that the host is ``not`` + vulnerable to CVE-2022-0001, Branch History Injection (BHI). + + This is also an MSR feature, therefore it does not show up in the Linux + ``/proc/cpuinfo`` in the host or guest. + + It should only be enabled for VMs if the host reports + ``BHI: Not affected`` in the + ``/sys/devices/system/cpu/vulnerabilities/spectre_v2`` file. + +``gds-no`` + Recommended to inform the guest that the host is ``not`` + vulnerable to CVE-2022-40982, Gather Data Sampling (GDS). + + This is also an MSR feature, therefore it does not show up in the Linux + ``/proc/cpuinfo`` in the host or guest. + + It should only be enabled for VMs if the host reports ``Not affected`` + in the ``/sys/devices/system/cpu/vulnerabilities/gather_data_sampling`` + file. + +``rfds-no`` + Recommended to inform the guest that the host is ``not`` + vulnerable to CVE-2023-28746, Register File Data Sampling (RFDS). + + This is also an MSR feature, therefore it does not show up in the Linux + ``/proc/cpuinfo`` in the host or guest. + + It should only be enabled for VMs if the host reports ``Not affected`` + in the ``/sys/devices/system/cpu/vulnerabilities/reg_file_data_sampling`` + file. Preferred CPU models for AMD x86 hosts ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/system/device-emulation.rst b/docs/system/device-emulation.rst index 4491c4cbf76..911381643f1 100644 --- a/docs/system/device-emulation.rst +++ b/docs/system/device-emulation.rst @@ -85,13 +85,18 @@ Emulated Devices devices/can.rst devices/ccid.rst devices/cxl.rst + devices/vfio-user.rst devices/ivshmem.rst + devices/ivshmem-flat.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 diff --git a/docs/system/device-url-syntax.rst.inc b/docs/system/device-url-syntax.rst.inc index 7dbc525fa80..43b5c2596b7 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 0af3d9912a6..09121836fdb 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 cfa6186e483..7f3664963f3 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 f12011e2306..e307caf3f88 100644 --- a/docs/system/devices/cxl.rst +++ b/docs/system/devices/cxl.rst @@ -157,7 +157,7 @@ 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)----------------->| | __________ __________________________________ __________ | @@ -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 | |_______________| |_____________| |____________| |_____________| | | | | | | | | @@ -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 @@ -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___| | | @@ -308,12 +308,12 @@ A very simple setup with just one directly attached CXL Type 3 Persistent Memory -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,persistent-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,sn=0x1 \ -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-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-ram,id=vmem0,share=on,size=256M \ -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \ @@ -323,7 +323,7 @@ A very simple setup with just one directly attached CXL Type 3 Volatile Memory d The same volatile setup may optionally include an LSA region:: - 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-ram,id=vmem0,share=on,size=256M \ -object memory-backend-file,id=cxl-lsa0,share=on,mem-path=/tmp/lsa.raw,size=256M \ @@ -349,13 +349,13 @@ 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,persistent-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,sn=0x1 \ -device cxl-rp,port=1,bus=cxl.1,id=root_port14,chassis=0,slot=3 \ - -device cxl-type3,bus=root_port14,persistent-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,sn=0x2 \ -device cxl-rp,port=0,bus=cxl.2,id=root_port15,chassis=0,slot=5 \ - -device cxl-type3,bus=root_port15,persistent-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,sn=0x3 \ -device cxl-rp,port=1,bus=cxl.2,id=root_port16,chassis=0,slot=6 \ - -device cxl-type3,bus=root_port16,persistent-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,sn=0x4 \ -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:: @@ -375,13 +375,13 @@ 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,persistent-memdev=cxl-mem0,lsa=cxl-lsa0,id=cxl-pmem0 \ + -device cxl-type3,bus=swport0,persistent-memdev=cxl-mem0,lsa=cxl-lsa0,id=cxl-pmem0,sn=0x1 \ -device cxl-downstream,port=1,bus=us0,id=swport1,chassis=0,slot=5 \ - -device cxl-type3,bus=swport1,persistent-memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem1 \ + -device cxl-type3,bus=swport1,persistent-memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem1,sn=0x2 \ -device cxl-downstream,port=2,bus=us0,id=swport2,chassis=0,slot=6 \ - -device cxl-type3,bus=swport2,persistent-memdev=cxl-mem2,lsa=cxl-lsa2,id=cxl-pmem2 \ + -device cxl-type3,bus=swport2,persistent-memdev=cxl-mem2,lsa=cxl-lsa2,id=cxl-pmem2,sn=0x3 \ -device cxl-downstream,port=3,bus=us0,id=swport3,chassis=0,slot=7 \ - -device cxl-type3,bus=swport3,persistent-memdev=cxl-mem3,lsa=cxl-lsa3,id=cxl-pmem3 \ + -device cxl-type3,bus=swport3,persistent-memdev=cxl-mem3,lsa=cxl-lsa3,id=cxl-pmem3,sn=0x4 \ -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=4k Deprecations @@ -411,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 index 04e79dfe549..71f31cb1160 100644 --- a/docs/system/devices/igb.rst +++ b/docs/system/devices/igb.rst @@ -57,11 +57,12 @@ directory: 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: +functional test and can be run from the build directory with the following +command: .. code:: shell - make check-avocado AVOCADO_TESTS=tests/avocado/netdev-ethtool.py + pyvenv/bin/meson test --suite thorough func-x86_64-netdev_ethtool References ========== diff --git a/docs/system/devices/ivshmem-flat.rst b/docs/system/devices/ivshmem-flat.rst new file mode 100644 index 00000000000..1f97052804a --- /dev/null +++ b/docs/system/devices/ivshmem-flat.rst @@ -0,0 +1,33 @@ +Inter-VM Shared Memory Flat Device +---------------------------------- + +The ivshmem-flat device is meant to be used on machines that lack a PCI bus, +making them unsuitable for the use of the traditional ivshmem device modeled as +a PCI device. Machines like those with a Cortex-M MCU are good candidates to use +the ivshmem-flat device. Also, since the flat version maps the control and +status registers directly to the memory, it requires a quite tiny "device +driver" to interact with other VMs, which is useful in some RTOSes, like +Zephyr, which usually run on constrained resource targets. + +Similar to the ivshmem device, the ivshmem-flat device supports both peer +notification via HW interrupts and Inter-VM shared memory. This allows the +device to be used together with the traditional ivshmem, enabling communication +between, for instance, an aarch64 VM (using the traditional ivshmem device and +running Linux), and an arm VM (using the ivshmem-flat device and running Zephyr +instead). + +The ivshmem-flat device does not support the use of a ``memdev`` option (see +ivshmem.rst for more details). It relies on the ivshmem server to create and +distribute the proper shared memory file descriptor and the eventfd(s) to notify +(interrupt) the peers. Therefore, to use this device, it is always necessary to +have an ivshmem server up and running for proper device creation. + +Although the ivshmem-flat supports both peer notification (interrupts) and +shared memory, the interrupt mechanism is optional. If no input IRQ is +specified for the device it is disabled, preventing the VM from notifying or +being notified by other VMs (a warning will be displayed to the user to inform +the IRQ mechanism is disabled). The shared memory region is always present. + +The MMRs (INTRMASK, INTRSTATUS, IVPOSITION, and DOORBELL registers) offsets at +the MMR region, and their functions, follow the ivshmem spec, so they work +exactly as in the ivshmem PCI device (see ./specs/ivshmem-spec.txt). diff --git a/docs/system/devices/ivshmem.rst b/docs/system/devices/ivshmem.rst index e7aaf34c200..ce71e256630 100644 --- a/docs/system/devices/ivshmem.rst +++ b/docs/system/devices/ivshmem.rst @@ -33,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/net.rst b/docs/system/devices/net.rst index 2ab516d4b09..a3efbdcabd1 100644 --- a/docs/system/devices/net.rst +++ b/docs/system/devices/net.rst @@ -77,6 +77,106 @@ When using the ``'-netdev user,hostfwd=...'`` option, TCP or UDP connections can be redirected from the host to the guest. It allows for example to redirect X11, telnet or SSH connections. +Using passt as the user mode network stack +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +passt_ can be used as a simple replacement for SLIRP (``-net user``). +passt doesn't require any capability or privilege. passt has +better performance than ``-net user``, full IPv6 support and better security +as it's a daemon that is not executed in QEMU context. + +passt can be connected to QEMU either by using a socket +(``-netdev stream``) or using the vhost-user interface (``-netdev vhost-user``). +See `passt(1)`_ for more details on passt. + +.. _passt: https://passt.top/ +.. _passt(1): https://passt.top/builds/latest/web/passt.1.html + +To use socket based passt interface: +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Start passt as a daemon:: + + passt --socket ~/passt.socket + +If ``--socket`` is not provided, passt will print the path of the UNIX domain socket QEMU can connect to (``/tmp/passt_1.socket``, ``/tmp/passt_2.socket``, +...). Then you can connect your QEMU instance to passt: + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] -device virtio-net-pci,netdev=netdev0 -netdev stream,id=netdev0,server=off,addr.type=unix,addr.path=~/passt.socket + +Where ``~/passt.socket`` is the UNIX socket created by passt to +communicate with QEMU. + +To use vhost-based interface: +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Start passt with ``--vhost-user``:: + + passt --vhost-user --socket ~/passt.socket + +Then to connect QEMU: + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] -m $RAMSIZE -chardev socket,id=chr0,path=~/passt.socket -netdev vhost-user,id=netdev0,chardev=chr0 -device virtio-net,netdev=netdev0 -object memory-backend-memfd,id=memfd0,share=on,size=$RAMSIZE -numa node,memdev=memfd0 + +Where ``$RAMSIZE`` is the memory size of your VM ``-m`` and ``-object memory-backend-memfd,size=`` must match. + +Migration of passt: +^^^^^^^^^^^^^^^^^^^ + +When passt is connected to QEMU using the vhost-user interface it can +be migrated with QEMU and the network connections are not interrupted. + +As passt runs with no privileges, it relies on passt-repair to save and +load the TCP connections state, using the TCP_REPAIR socket option. +The passt-repair helper needs to have the CAP_NET_ADMIN capability, or run as root. If passt-repair is not available, TCP connections will not be preserved. + +Example of migration of a guest on the same host +________________________________________________ + +Before being able to run passt-repair, the CAP_NET_ADMIN capability must be set +on the file, run as root:: + + setcap cap_net_admin+eip ./passt-repair + +Start passt for the source side:: + + passt --vhost-user --socket ~/passt_src.socket --repair-path ~/passt-repair_src.socket + +Where ``~/passt-repair_src.socket`` is the UNIX socket created by passt to +communicate with passt-repair. The default value is the ``--socket`` path +appended with ``.repair``. + +Start passt-repair:: + + passt-repair ~/passt-repair_src.socket + +Start source side QEMU with a monitor to be able to send the migrate command: + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] [...VHOST USER OPTIONS...] -monitor stdio + +Start passt for the destination side:: + + passt --vhost-user --socket ~/passt_dst.socket --repair-path ~/passt-repair_dst.socket + +Start passt-repair:: + + passt-repair ~/passt-repair_dst.socket + +Start QEMU with the ``-incoming`` parameter: + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] [...VHOST USER OPTIONS...] -incoming tcp:localhost:4444 + +Then in the source guest monitor the migration can be started:: + + (qemu) migrate tcp:localhost:4444 + +A separate passt-repair instance must be started for every migration. In the case of a failed migration, passt-repair also needs to be restarted before trying +again. + Hubs ~~~~ diff --git a/docs/system/devices/nvme.rst b/docs/system/devices/nvme.rst index 4ea957baed1..6509b35fcb4 100644 --- a/docs/system/devices/nvme.rst +++ b/docs/system/devices/nvme.rst @@ -53,6 +53,13 @@ parameters. Vendor ID. Set this to ``on`` to revert to the unallocated Intel ID previously used. +``ocp`` (default: ``off``) + The Open Compute Project defines the Datacenter NVMe SSD Specification that + sits on top of NVMe. It describes additional commands and NVMe behaviors + specific for the Datacenter. When this option is ``on`` OCP features such as + the SMART / Health information extended log become available in the + controller. We emulate version 5 of this log page. + Additional Namespaces --------------------- @@ -81,6 +88,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. diff --git a/docs/system/devices/usb.rst b/docs/system/devices/usb.rst index a6ca7b0c375..dc694d23c2f 100644 --- a/docs/system/devices/usb.rst +++ b/docs/system/devices/usb.rst @@ -18,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 diff --git a/docs/system/devices/vfio-user.rst b/docs/system/devices/vfio-user.rst new file mode 100644 index 00000000000..b6dcaa5615e --- /dev/null +++ b/docs/system/devices/vfio-user.rst @@ -0,0 +1,26 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +========= +vfio-user +========= + +QEMU includes a ``vfio-user`` client. The ``vfio-user`` specification allows for +implementing (PCI) devices in userspace outside of QEMU; it is similar to +``vhost-user`` in this respect (see :doc:`vhost-user`), but can emulate arbitrary +PCI devices, not just ``virtio``. Whereas ``vfio`` is handled by the host +kernel, ``vfio-user``, while similar in implementation, is handled entirely in +userspace. + +For example, SPDK includes a virtual PCI NVMe controller implementation; by +setting up a ``vfio-user`` UNIX socket between QEMU and SPDK, a VM can send NVMe +I/O to the SPDK process. + +Presuming a suitable ``vfio-user`` server has opened a socket at +``/tmp/vfio-user.sock``, a device can be configured with for example: + +.. code-block:: console + +-device '{"driver": "vfio-user-pci","socket": {"path": "/tmp/vfio-user.sock", "type": "unix"}}' + +See `libvfio-user `_ for further +information. diff --git a/docs/system/devices/vhost-user-input.rst b/docs/system/devices/vhost-user-input.rst new file mode 100644 index 00000000000..118eb78101c --- /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 a145d4105c1..ead14053264 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 a80e95a48ae..35259d8ec7c 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 diff --git a/docs/system/devices/virtio-gpu.rst b/docs/system/devices/virtio-gpu.rst new file mode 100644 index 00000000000..b7eb0fc0e72 --- /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 00000000000..2a9187fd701 --- /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 9906991b841..d50470b135e 100644 --- a/docs/system/gdb.rst +++ b/docs/system/gdb.rst @@ -20,7 +20,7 @@ connection, use the ``-gdb dev`` option instead of ``-s``. See .. parsed-literal:: - |qemu_system| -s -S -kernel bzImage -hda rootdisk.img -append "root=/dev/hda" + |qemu_system| -s -S -kernel bzImage -drive file=rootdisk.img,format=raw -append "root=/dev/sda" QEMU will launch but will silently wait for gdb to connect. @@ -60,7 +60,7 @@ 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 @@ -68,6 +68,17 @@ 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 ============================ diff --git a/docs/system/i386/amd-memory-encryption.rst b/docs/system/i386/amd-memory-encryption.rst index dcf4add0e7b..748f5094baf 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 2505dc4c86e..1c1de77feb6 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 00000000000..7317f547dce --- /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 look like a `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:: + + $ vhost-device-vsock \ + --vm guest-cid=4,forward-cid=1,forward-listen=9001+9002,socket=/tmp/vhost4.socket + +__ https://github.com/rust-vmm/vhost-device/tree/main/vhost-device-vsock#using-the-vsock-backend + +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 0f0a73f7587..ab58b293923 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/tdx.rst b/docs/system/i386/tdx.rst new file mode 100644 index 00000000000..8131750b64b --- /dev/null +++ b/docs/system/i386/tdx.rst @@ -0,0 +1,161 @@ +Intel Trusted Domain eXtension (TDX) +==================================== + +Intel Trusted Domain eXtensions (TDX) refers to an Intel technology that extends +Virtual Machine Extensions (VMX) and Multi-Key Total Memory Encryption (MKTME) +with a new kind of virtual machine guest called a Trust Domain (TD). A TD runs +in a CPU mode that is designed to protect the confidentiality of its memory +contents and its CPU state from any other software, including the hosting +Virtual Machine Monitor (VMM), unless explicitly shared by the TD itself. + +Prerequisites +------------- + +To run TD, the physical machine needs to have TDX module loaded and initialized +while KVM hypervisor has TDX support and has TDX enabled. If those requirements +are met, the ``KVM_CAP_VM_TYPES`` will report the support of ``KVM_X86_TDX_VM``. + +Trust Domain Virtual Firmware (TDVF) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Trust Domain Virtual Firmware (TDVF) is required to provide TD services to boot +TD Guest OS. TDVF needs to be copied to guest private memory and measured before +the TD boots. + +KVM vcpu ioctl ``KVM_TDX_INIT_MEM_REGION`` can be used to populate the TDVF +content into its private memory. + +Since TDX doesn't support readonly memslot, TDVF cannot be mapped as pflash +device and it actually works as RAM. "-bios" option is chosen to load TDVF. + +OVMF is the opensource firmware that implements the TDVF support. Thus the +command line to specify and load TDVF is ``-bios OVMF.fd`` + +Feature Configuration +--------------------- + +Unlike non-TDX VM, the CPU features (enumerated by CPU or MSR) of a TD are not +under full control of VMM. VMM can only configure part of features of a TD on +``KVM_TDX_INIT_VM`` command of VM scope ``MEMORY_ENCRYPT_OP`` ioctl. + +The configurable features have three types: + +- Attributes: + - PKS (bit 30) controls whether Supervisor Protection Keys is exposed to TD, + which determines related CPUID bit and CR4 bit; + - PERFMON (bit 63) controls whether PMU is exposed to TD. + +- XSAVE related features (XFAM): + XFAM is a 64b mask, which has the same format as XCR0 or IA32_XSS MSR. It + determines the set of extended features available for use by the guest TD. + +- CPUID features: + Only some bits of some CPUID leaves are directly configurable by VMM. + +What features can be configured is reported via TDX capabilities. + +TDX capabilities +~~~~~~~~~~~~~~~~ + +The VM scope ``MEMORY_ENCRYPT_OP`` ioctl provides command ``KVM_TDX_CAPABILITIES`` +to get the TDX capabilities from KVM. It returns a data structure of +``struct kvm_tdx_capabilities``, which tells the supported configuration of +attributes, XFAM and CPUIDs. + +TD attributes +~~~~~~~~~~~~~ + +QEMU supports configuring raw 64-bit TD attributes directly via "attributes" +property of "tdx-guest" object. Note, it's users' responsibility to provide a +valid value because some bits may not supported by current QEMU or KVM yet. + +QEMU also supports the configuration of individual attribute bits that are +supported by it, via properties of "tdx-guest" object. +E.g., "sept-ve-disable" (bit 28). + +MSR based features +~~~~~~~~~~~~~~~~~~ + +Current KVM doesn't support MSR based feature (e.g., MSR_IA32_ARCH_CAPABILITIES) +configuration for TDX, and it's a future work to enable it in QEMU when KVM adds +support of it. + +Feature check +~~~~~~~~~~~~~ + +QEMU checks if the final (CPU) features, determined by given cpu model and +explicit feature adjustment of "+featureA/-featureB", can be supported or not. +It can produce feature not supported warning like + + "warning: host doesn't support requested feature: CPUID.07H:EBX.intel-pt [bit 25]" + +It can also produce warning like + + "warning: TDX forcibly sets the feature: CPUID.80000007H:EDX.invtsc [bit 8]" + +if the fixed-1 feature is requested to be disabled explicitly. This is newly +added to QEMU for TDX because TDX has fixed-1 features that are forcibly enabled +by TDX module and VMM cannot disable them. + +Launching a TD (TDX VM) +----------------------- + +To launch a TD, the necessary command line options are tdx-guest object and +split kernel-irqchip, as below: + +.. parsed-literal:: + + |qemu_system_x86| \\ + -accel kvm \\ + -cpu host \\ + -object tdx-guest,id=tdx0 \\ + -machine ...,confidential-guest-support=tdx0 \\ + -bios OVMF.fd \\ + +Restrictions +------------ + + - kernel-irqchip must be split; + + This is set by default for TDX guest if kernel-irqchip is left on its default + 'auto' setting. + + - No readonly support for private memory; + + - No SMM support: SMM support requires manipulating the guest register states + which is not allowed; + +Debugging +--------- + +Bit 0 of TD attributes, is DEBUG bit, which decides if the TD runs in off-TD +debug mode. When in off-TD debug mode, TD's VCPU state and private memory are +accessible via given SEAMCALLs. This requires KVM to expose APIs to invoke those +SEAMCALLs and corresonponding QEMU change. + +It's targeted as future work. + +TD attestation +-------------- + +In TD guest, the attestation process is used to verify the TDX guest +trustworthiness to other entities before provisioning secrets to the guest. + +TD attestation is initiated first by calling TDG.MR.REPORT inside TD to get the +REPORT. Then the REPORT data needs to be converted into a remotely verifiable +Quote by SGX Quoting Enclave (QE). + +It's a future work in QEMU to add support of TD attestation since it lacks +support in current KVM. + +Live Migration +-------------- + +Future work. + +References +---------- + +- `TDX Homepage `__ + +- `SGX QE `__ diff --git a/docs/system/i386/xen.rst b/docs/system/i386/xen.rst index f06765e88c8..46db5f34c1d 100644 --- a/docs/system/i386/xen.rst +++ b/docs/system/i386/xen.rst @@ -15,46 +15,24 @@ Setup ----- Xen mode is enabled by setting the ``xen-version`` property of the KVM -accelerator, for example for Xen 4.10: +accelerator, for example for Xen 4.17: .. parsed-literal:: - |qemu_system| --accel kvm,xen-version=0x4000a,kernel-irqchip=split + |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=0x4000a,kernel-irqchip=split --cpu host,+xen_vapic + |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. -The Xen platform device is enabled automatically for a Xen guest. This allows -a guest to unplug all emulated devices, in order to use Xen PV block and network -drivers instead. 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| -M pc --accel kvm,xen-version=0x4000a,kernel-irqchip=split \\ - -drive file=${GUEST_IMAGE},if=none,id=disk,file.locking=off -device xen-disk,drive=disk,vdev=xvda \\ - -drive file=${GUEST_IMAGE},index=2,media=disk,file.locking=off,if=ide - -It is necessary to use the pc machine type, as the q35 machine uses AHCI instead -of legacy IDE, and AHCI disks are not unplugged through the Xen PV unplug -mechanism. - -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. - Properties ---------- @@ -63,7 +41,10 @@ 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. + 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 @@ -83,8 +64,79 @@ The following properties exist on the KVM accelerator object: through simultaneous grants. For guests with large numbers of PV devices and high throughput, it may be desirable to increase this value. -OS requirements ---------------- +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 diff --git a/docs/system/i386/xenpvh.rst b/docs/system/i386/xenpvh.rst new file mode 100644 index 00000000000..354250f073f --- /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/images.rst b/docs/system/images.rst index d000bd6b6f1..a5551173c97 100644 --- a/docs/system/images.rst +++ b/docs/system/images.rst @@ -82,4 +82,6 @@ VM snapshots currently have the following known limitations: - A few device drivers still have incomplete snapshot support so their state is not saved or restored properly (in particular USB). +.. _block-drivers: + .. include:: qemu-block-drivers.rst.inc diff --git a/docs/system/index.rst b/docs/system/index.rst index 45bf1f19e75..718e9d3c56b 100644 --- a/docs/system/index.rst +++ b/docs/system/index.rst @@ -38,3 +38,5 @@ or Hypervisor.Framework. security multi-process confidential-guest-support + vm-templating + sriov diff --git a/docs/system/introduction.rst b/docs/system/introduction.rst index 51ac132d6cb..338d3745c3c 100644 --- a/docs/system/introduction.rst +++ b/docs/system/introduction.rst @@ -1,6 +1,8 @@ Introduction ============ +.. _Accelerators: + Virtualisation Accelerators --------------------------- @@ -167,7 +169,7 @@ would default to it anyway. .. code:: - -cpu max,pauth-impdef=on \ + -cpu max \ -smp 4 \ -accel tcg \ diff --git a/docs/system/invocation.rst b/docs/system/invocation.rst index 4ba38fc23d2..14b7db1c102 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.inc b/docs/system/keys.rst.inc index bd9b8e5f6f5..59966a3fe7c 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 5db2e560dc5..2328b4a73d5 100644 --- a/docs/system/linuxboot.rst +++ b/docs/system/linuxboot.rst @@ -11,7 +11,7 @@ The syntax is: .. parsed-literal:: - |qemu_system| -kernel bzImage -hda rootdisk.img -append "root=/dev/hda" + |qemu_system| -kernel bzImage -drive file=rootdisk.img,format=raw -append "root=/dev/sda" Use ``-kernel`` to provide the Linux kernel image and ``-append`` to give the kernel command line arguments. The ``-initrd`` option can be @@ -23,8 +23,8 @@ virtual serial port and the QEMU monitor to the console with the .. parsed-literal:: - |qemu_system| -kernel bzImage -hda rootdisk.img \ - -append "root=/dev/hda console=ttyS0" -nographic + |qemu_system| -kernel bzImage -drive file=rootdisk.img,format=raw \ + -append "root=/dev/sda console=ttyS0" -nographic Use Ctrl-a c to switch between the serial console and the monitor (see :ref:`GUI_keys`). diff --git a/docs/system/loongarch/virt.rst b/docs/system/loongarch/virt.rst index c37268b404d..78458784697 100644 --- a/docs/system/loongarch/virt.rst +++ b/docs/system/loongarch/virt.rst @@ -12,14 +12,15 @@ 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. + +* 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 -------------------- @@ -39,13 +40,7 @@ can be accessed by following steps. .. 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 + ./configure --target-list="loongarch64-softmmu" make -j8 (2) Set cross tools: @@ -53,9 +48,7 @@ can be accessed by following steps. .. 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 @@ -64,7 +57,7 @@ Note: You need get the latest cross-tools at https://github.com/loongson/build-t (3) Build BIOS: - See: https://github.com/tianocore/edk2-platforms/tree/master/Platform/Loongson/LoongArchQemuPkg#readme + 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 @@ -74,13 +67,9 @@ Note: To build the release version of the bios, set --buildtarget=RELEASE, .. 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. diff --git a/docs/system/ppc/amigang.rst b/docs/system/ppc/amigang.rst new file mode 100644 index 00000000000..21bb14ed099 --- /dev/null +++ b/docs/system/ppc/amigang.rst @@ -0,0 +1,162 @@ +========================================================= +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 + * 4 KiB NVRAM (use ``-drive if=mtd,format=raw,file=nvram.bin`` to keep contents persistent) + +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 + +If a firmware menu appears, select ``Boot sequence`` → ``Amiga Multiboot Options`` +and set ``Boot device 1`` to ``Onboard VIA IDE CDROM``. Then hit escape until +the main screen appears again, hit escape once more and from the exit menu that +appears select either ``Save settings and exit`` or ``Use settings for this +session only``. It may take a long time loading the kernel into memory but +eventually it boots and the installer becomes visible. The ``ati-vga`` RV100 +emulation is not complete yet so only frame buffer works, DRM and 3D is not +available. + +Genesi/bPlan Pegasos II (``pegasos2``) +====================================== + +The ``pegasos2`` machine emulates the Pegasos II sold by Genesi and +designed by bPlan. Its schematics are available at +https://www.powerdeveloper.org/platforms/pegasos/schematics. + +Emulated devices +---------------- + + * PowerPC 7457 CPU (can also use ``-cpu g3`` or ``750cxe``) + * Marvell MV64361 Discovery II north bridge + * VIA VT8231 south bridge + * PCI VGA compatible card (guests may need other card instead) + * PS/2 keyboard and mouse + +Firmware +-------- + +The Pegasos II board has an Open Firmware compliant ROM based on +SmartFirmware with some changes that are not open-sourced therefore +the ROM binary cannot be included in QEMU. An updater was available +from bPlan, it can be found in the `Internet Archive +`_. +The ROM image can be extracted from it with the following command: + +.. code-block:: bash + + $ tail -c +85581 up050404 | head -c 524288 > pegasos2.rom + +Running Linux +------------- + +The PowerPC version of Debian 8.11 supported Pegasos II. The BIOS +emulator in the firmware binary is unable to run QEMU‘s standard +vgabios so it needs to be disabled. To boot the system run: + +.. code-block:: bash + + $ qemu-system-ppc -machine pegasos2 -bios pegasos2.rom \ + -cdrom debian-8.11.0-powerpc-netinst.iso \ + -device VGA,romfile="" -serial stdio + +At the firmware ``ok`` prompt enter ``boot cd install/pegasos``. + +Alternatively, it is possible to boot the kernel directly without +firmware ROM using the QEMU built-in minimal Virtual Open Firmware +(VOF) emulation which is also supported on ``pegasos2``. For this, +extract the kernel ``install/powerpc/vmlinuz-chrp.initrd`` from the CD +image, then run: + +.. code-block:: bash + + $ qemu-system-ppc -machine pegasos2 -serial stdio \ + -kernel vmlinuz-chrp.initrd -append "---" \ + -cdrom debian-8.11.0-powerpc-netinst.iso + +aCube Sam460ex (``sam460ex``) +============================= + +The ``sam460ex`` machine emulates the Sam460ex board by aCube which is +based on the AMCC PowerPC 460EX SoC (that despite its name has a +PPC440 CPU core). + +Firmware +-------- + +The board has a firmware based on an older U-Boot version with +modifications to support booting AmigaOS 4. The firmware ROM is +included with QEMU. + +Emulated devices +---------------- + + * PowerPC 460EX SoC + * M41T80 serial RTC chip + * Silicon Motion SM501 display parts (identical to SM502 on real board) + * Silicon Image SiI3112 2 port SATA controller + * USB keyboard and mouse + +Running Linux +------------- + +The only Linux distro that supported Sam460ex out of box was CruxPPC +2.x. It can be booted by running: + +.. code-block:: bash + + $ qemu-system-ppc -machine sam460ex -serial stdio \ + -drive if=none,id=cd,format=raw,file=crux-ppc-2.7a.iso \ + -device ide-cd,drive=cd,bus=ide.1 + +There are some other kernels and instructions for booting other +distros on aCube's product page at +https://www.acube-systems.biz/index.php?page=hardware&pid=5 +but those are untested. diff --git a/docs/system/ppc/embedded.rst b/docs/system/ppc/embedded.rst index af3b3d9fa46..5cb7d98b450 100644 --- a/docs/system/ppc/embedded.rst +++ b/docs/system/ppc/embedded.rst @@ -4,6 +4,5 @@ Embedded family boards - ``bamboo`` bamboo - ``mpc8544ds`` mpc8544ds - ``ppce500`` generic paravirt e500 platform -- ``ref405ep`` ref405ep - ``sam460ex`` aCube Sam460ex - ``virtex-ml507`` Xilinx Virtex ML507 reference design diff --git a/docs/system/ppc/powermac.rst b/docs/system/ppc/powermac.rst index 04334ba2102..3eac81c491c 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 09f39658587..f3ec2cc69c0 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,6 +195,13 @@ Use a MTD drive to add a PNOR to the machine, and get a NVRAM : -drive file=./witherspoon.pnor,format=raw,if=mtd +If no mtd drive is provided, the powernv platform will create a default +PNOR device using a tiny formatted PNOR in pc-bios/pnv-pnor.bin opened +read-only (PNOR changes will be persistent across reboots but not across +invocations of QEMU). If no defaults are used, an erased 128MB PNOR is +provided (which skiboot will probably not recognize since it is not +formatted). + Maintainer contact information ------------------------------ diff --git a/docs/system/ppc/pseries.rst b/docs/system/ppc/pseries.rst index a876d897b6e..bbc51aa7fcd 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 105cb9679c3..384e95ba765 100644 --- a/docs/system/qemu-block-drivers.rst.inc +++ b/docs/system/qemu-block-drivers.rst.inc @@ -737,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 c47a4127582..3ade4ee45b5 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/replay.rst b/docs/system/replay.rst index ca7c17c63da..28e5772a2b3 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. diff --git a/docs/system/riscv/microblaze-v-generic.rst b/docs/system/riscv/microblaze-v-generic.rst new file mode 100644 index 00000000000..5606f88d570 --- /dev/null +++ b/docs/system/riscv/microblaze-v-generic.rst @@ -0,0 +1,42 @@ +Microblaze-V generic board (``amd-microblaze-v-generic``) +========================================================= +The AMD MicroBlaze™ V processor is a soft-core RISC-V processor IP for AMD +adaptive SoCs and FPGAs. The MicroBlaze™ V processor is based on the 32-bit (or +64-bit) RISC-V instruction set architecture (ISA) and contains interfaces +compatible with the classic MicroBlaze™ V processor (i.e it is a drop in +replacement for the classic MicroBlaze™ processor in existing RTL designs). +More information can be found in below document. + +https://docs.amd.com/r/en-US/ug1629-microblaze-v-user-guide/MicroBlaze-V-Architecture + +The MicroBlaze™ V generic board in QEMU has following supported devices: + + - timer + - uartlite + - uart16550 + - emaclite + - timer2 + - axi emac + - axi dma + +The MicroBlaze™ V core in QEMU has the following configuration: + + - RV32I base integer instruction set + - "Zicsr" Control and Status register instructions + - "Zifencei" instruction-fetch + - Extensions: m, a, f, c + +Running +""""""" +Below is an example command line for launching mainline U-boot +(xilinx_mbv32_defconfig) on the Microblaze-V generic board. + +.. code-block:: bash + + $ qemu-system-riscv32 -M amd-microblaze-v-generic \ + -display none \ + -device loader,addr=0x80000000,file=u-boot-spl.bin,cpu-num=0 \ + -device loader,addr=0x80200000,file=u-boot.img \ + -serial mon:stdio \ + -device loader,addr=0x83000000,file=system.dtb \ + -m 2g diff --git a/docs/system/riscv/microchip-icicle-kit.rst b/docs/system/riscv/microchip-icicle-kit.rst index 40798b1aae5..9809e94b84b 100644 --- a/docs/system/riscv/microchip-icicle-kit.rst +++ b/docs/system/riscv/microchip-icicle-kit.rst @@ -5,10 +5,10 @@ Microchip PolarFire SoC Icicle Kit integrates a PolarFire SoC, with one SiFive's E51 plus four U54 cores and many on-chip peripherals and an FPGA. For more details about Microchip PolarFire SoC, please see: -https://www.microsemi.com/product-directory/soc-fpgas/5498-polarfire-soc-fpga +https://www.microchip.com/en-us/products/fpgas-and-plds/system-on-chip-fpgas/polarfire-soc-fpgas The Icicle Kit board information can be found here: -https://www.microsemi.com/existing-parts/parts/152514 +https://www.microchip.com/en-us/development-tool/mpfs-icicle-kit-es Supported devices ----------------- @@ -26,95 +26,48 @@ The ``microchip-icicle-kit`` machine supports the following devices: * 2 GEM Ethernet controllers * 1 SDHC storage controller +The memory is set to 1537 MiB by default. A sanity check on RAM size is +performed in the machine init routine to prompt user to increase the RAM size +to > 1537 MiB when less than 1537 MiB RAM is detected. + Boot options ------------ -The ``microchip-icicle-kit`` machine can start using the standard -bios -functionality for loading its BIOS image, aka Hart Software Services (HSS_). -HSS loads the second stage bootloader U-Boot from an SD card. Then a kernel -can be loaded from U-Boot. It also supports direct kernel booting via the --kernel option along with the device tree blob via -dtb. When direct kernel -boot is used, the OpenSBI fw_dynamic BIOS image is used to boot a payload -like U-Boot or OS kernel directly. - -The user provided DTB should have the following requirements: - -* The /cpus node should contain at least one subnode for E51 and the number - of subnodes should match QEMU's ``-smp`` option -* The /memory reg size should match QEMU’s selected ram_size via ``-m`` -* Should contain a node for the CLINT device with a compatible string - "riscv,clint0" - -QEMU follows below truth table to select which payload to execute: - -===== ========== ========== ======= --bios -kernel -dtb payload -===== ========== ========== ======= - N N don't care HSS - Y don't care don't care HSS - N Y Y kernel -===== ========== ========== ======= - -The memory is set to 1537 MiB by default which is the minimum required high -memory size by HSS. A sanity check on ram size is performed in the machine -init routine to prompt user to increase the RAM size to > 1537 MiB when less -than 1537 MiB ram is detected. - -Running HSS ------------ - -HSS 2020.12 release is tested at the time of writing. To build an HSS image -that can be booted by the ``microchip-icicle-kit`` machine, type the following -in the HSS source tree: - -.. code-block:: bash - - $ export CROSS_COMPILE=riscv64-linux- - $ cp boards/mpfs-icicle-kit-es/def_config .config - $ make BOARD=mpfs-icicle-kit-es - -Download the official SD card image released by Microchip and prepare it for -QEMU usage: - -.. code-block:: bash - - $ wget ftp://ftpsoc.microsemi.com/outgoing/core-image-minimal-dev-icicle-kit-es-sd-20201009141623.rootfs.wic.gz - $ gunzip core-image-minimal-dev-icicle-kit-es-sd-20201009141623.rootfs.wic.gz - $ qemu-img resize core-image-minimal-dev-icicle-kit-es-sd-20201009141623.rootfs.wic 4G - -Then we can boot the machine by: - -.. code-block:: bash - - $ qemu-system-riscv64 -M microchip-icicle-kit -smp 5 \ - -bios path/to/hss.bin -sd path/to/sdcard.img \ - -nic user,model=cadence_gem \ - -nic tap,ifname=tap,model=cadence_gem,script=no \ - -display none -serial stdio \ - -chardev socket,id=serial1,path=serial1.sock,server=on,wait=on \ - -serial chardev:serial1 +The ``microchip-icicle-kit`` machine provides some options to run a firmware +(BIOS) or a kernel image. QEMU follows below truth table to select the +firmware: -With above command line, current terminal session will be used for the first -serial port. Open another terminal window, and use ``minicom`` to connect the -second serial port. +============= =========== ====================================== +-bios -kernel firmware +============= =========== ====================================== +none N this is an error +none Y the kernel image +NULL, default N hss.bin +NULL, default Y opensbi-riscv64-generic-fw_dynamic.bin +other don't care the BIOS image +============= =========== ====================================== -.. code-block:: bash +Direct Kernel Boot +------------------ - $ minicom -D unix\#serial1.sock +Use the ``-kernel`` option to directly run a kernel image. When a direct +kernel boot is requested, a device tree blob may be specified via the ``-dtb`` +option. Unlike other QEMU machines, this machine does not generate a device +tree for the kernel. It shall be provided by the user. The user provided DTB +should meet the following requirements: -HSS output is on the first serial port (stdio) and U-Boot outputs on the -second serial port. U-Boot will automatically load the Linux kernel from -the SD card image. +* The ``/cpus`` node should contain at least one subnode for E51 and the number + of subnodes should match QEMU's ``-smp`` option. -Direct Kernel Boot ------------------- +* The ``/memory`` reg size should match QEMU’s selected RAM size via the ``-m`` + option. -Sometimes we just want to test booting a new kernel, and transforming the -kernel image to the format required by the HSS bootflow is tedious. We can -use '-kernel' for direct kernel booting just like other RISC-V machines do. +* It should contain a node for the CLINT device with a compatible string + "riscv,clint0". -In this mode, the OpenSBI fw_dynamic BIOS image for 'generic' platform is -used to boot an S-mode payload like U-Boot or OS kernel directly. +When ``-bios`` is not specified or set to ``default``, the OpenSBI +``fw_dynamic`` BIOS image for the ``generic`` platform is used to boot an +S-mode payload like U-Boot or OS kernel directly. For example, the following commands show building a U-Boot image from U-Boot mainline v2021.07 for the Microchip Icicle Kit board: @@ -146,4 +99,13 @@ CAVEATS: ``u-boot.bin`` has to be used which does contain one. To use the ELF image, we need to change to CONFIG_OF_EMBED or CONFIG_OF_PRIOR_STAGE. +Running HSS +----------- + +The machine ``microchip-icicle-kit`` used to run the Hart Software Services +(HSS_), however, the HSS development progressed and the QEMU machine +implementation lacks behind. Currently, running the HSS no longer works. +There is missing support in the clock and memory controller devices. In +particular, reading from the SD card does not work. + .. _HSS: https://github.com/polarfire-soc/hart-software-services diff --git a/docs/system/riscv/sifive_u.rst b/docs/system/riscv/sifive_u.rst index 7b166567f97..8f55ae8e313 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 f9a2eac5443..60850970ce8 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 @@ -84,6 +84,25 @@ none``, as in 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 (...) + +It also has support for the riscv-iommu-sys platform device: + +.. code-block:: bash + + $ qemu-system-riscv64 -M virt,iommu-sys=on (...) + +Refer to :ref:`riscv-iommu` for more information on how the RISC-V IOMMU support +works. + Machine-specific options ------------------------ @@ -95,6 +114,11 @@ The following machine-specific options are supported: 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] This option allows selecting interrupt controller defined by the AIA @@ -105,12 +129,23 @@ The following machine-specific options are supported: MSIs. When not specified, this option is assumed to be "none" which selects SiFive PLIC to handle wired interrupts. + This option also interacts with '-accel kvm'. When using "aia=aplic-imsic" + with KVM, it is possible to set the use of the kernel irqchip in split mode + by using "-accel kvm,kernel-irqchip=split". In this case the ``virt`` machine + will emulate the APLIC controller instead of using the APLIC controller from + the irqchip. See :ref:`riscv-aia` for more details on all available AIA + modes. + - aia-guests=nnn The number of per-HART VS-level AIA IMSIC pages to be emulated for a guest having AIA IMSIC (i.e. "aia=aplic-imsic" selected). When not specified, the default number of per-HART VS-level AIA IMSIC pages is 0. +- iommu-sys=[on|off] + + Enables the riscv-iommu-sys platform device. Defaults to 'off'. + Running Linux kernel -------------------- diff --git a/docs/system/s390x/bootdevices.rst b/docs/system/s390x/bootdevices.rst index 1a7a18b43b0..97b3914785a 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 00000000000..d5b506ee5c9 --- /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/sriov.rst b/docs/system/sriov.rst new file mode 100644 index 00000000000..d12178f3c31 --- /dev/null +++ b/docs/system/sriov.rst @@ -0,0 +1,37 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Compsable SR-IOV device +======================= + +SR-IOV (Single Root I/O Virtualization) is an optional extended capability of a +PCI Express device. It allows a single physical function (PF) to appear as +multiple virtual functions (VFs) for the main purpose of eliminating software +overhead in I/O from virtual machines. + +There are devices with predefined SR-IOV configurations, but it is also possible +to compose an SR-IOV device yourself. Composing an SR-IOV device is currently +only supported by virtio-net-pci. + +Users can configure an SR-IOV-capable virtio-net device by adding +virtio-net-pci functions to a bus. Below is a command line example: + +.. code-block:: shell + + -netdev user,id=n -netdev user,id=o + -netdev user,id=p -netdev user,id=q + -device pcie-root-port,id=b + -device virtio-net-pci,bus=b,addr=0x0.0x3,netdev=q,sriov-pf=f + -device virtio-net-pci,bus=b,addr=0x0.0x2,netdev=p,sriov-pf=f + -device virtio-net-pci,bus=b,addr=0x0.0x1,netdev=o,sriov-pf=f + -device virtio-net-pci,bus=b,addr=0x0.0x0,netdev=n,id=f + +The VFs specify the paired PF with ``sriov-pf`` property. The PF must be +added after all VFs. It is the user's responsibility to ensure that VFs have +function numbers larger than one of the PF, and that the function numbers +have a consistent stride. Both the PF and VFs are ARI-capable so you can have +255 VFs at maximum. + +You may also need to perform additional steps to activate the SR-IOV feature on +your guest. For Linux, refer to [1]_. + +.. [1] https://docs.kernel.org/PCI/pci-iov-howto.html diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index 790ac1b8a2b..b96a05a9206 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 @@ -84,30 +80,34 @@ undocumented; you can get a complete list by running 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/imx8mp-evk arm/orangepi - arm/palm arm/raspi - arm/xscale arm/collie arm/sx1 arm/stellaris arm/stm32 arm/virt - arm/xlnx-versal-virt + arm/vmapple 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 7d1fffacbea..ae312b1c1e6 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 1b8a1f248ab..43b09c79d6b 100644 --- a/docs/system/target-i386.rst +++ b/docs/system/target-i386.rst @@ -14,8 +14,9 @@ Board-specific documentation .. toctree:: :maxdepth: 1 - i386/microvm i386/pc + i386/microvm + i386/nitro-enclave Architectural features ~~~~~~~~~~~~~~~~~~~~~~ @@ -26,9 +27,11 @@ Architectural features i386/cpu i386/hyperv i386/xen + i386/xenpvh i386/kvm-pv i386/sgx i386/amd-memory-encryption + i386/tdx OS requirements ~~~~~~~~~~~~~~~ diff --git a/docs/system/target-loongarch.rst b/docs/system/target-loongarch.rst new file mode 100644 index 00000000000..316c604b919 --- /dev/null +++ b/docs/system/target-loongarch.rst @@ -0,0 +1,19 @@ +.. _LoongArch-System-emulator: + +LoongArch System emulator +------------------------- + +QEMU can emulate loongArch 64 bit systems via the +``qemu-system-loongarch64`` binary. Only one machine type ``virt`` is +supported. + +When using KVM as accelerator, QEMU can emulate la464 cpu model. And when +using the default cpu model with TCG as accelerator, QEMU will emulate a +subset of la464 cpu features that should be enough to run distributions +built for the la464. + +Board-specific documentation +============================ + +.. toctree:: + loongarch/virt diff --git a/docs/system/target-mips.rst b/docs/system/target-mips.rst index 83239fb9dfd..9028c3b304d 100644 --- a/docs/system/target-mips.rst +++ b/docs/system/target-mips.rst @@ -112,5 +112,5 @@ https://mipsdistros.mips.com/LinuxDistro/nanomips/kernels/v4.15.18-432-gb2eb9a8b Start system emulation of Malta board with nanoMIPS I7200 CPU:: qemu-system-mipsel -cpu I7200 -kernel \ - -M malta -serial stdio -m -hda \ + -M malta -serial stdio -m -drive file=,format=raw \ -append "mem=256m@0x0 rw console=ttyS0 vga=cirrus vesa=0x111 root=/dev/sda" diff --git a/docs/system/target-ppc.rst b/docs/system/target-ppc.rst index 4f6eb93b177..87bf412ce5c 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 ba195f1518a..95457af130b 100644 --- a/docs/system/target-riscv.rst +++ b/docs/system/target-riscv.rst @@ -66,6 +66,7 @@ undocumented; you can get a complete list by running .. toctree:: :maxdepth: 1 + riscv/microblaze-v-generic riscv/microchip-icicle-kit riscv/shakti-c riscv/sifive_u diff --git a/docs/system/target-s390x.rst b/docs/system/target-s390x.rst index f6f11433c7e..94c981e732b 100644 --- a/docs/system/target-s390x.rst +++ b/docs/system/target-s390x.rst @@ -34,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 9ec8c90c147..4116cac493d 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 @@ -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/targets.rst b/docs/system/targets.rst index 224fadae71c..38e24188018 100644 --- a/docs/system/targets.rst +++ b/docs/system/targets.rst @@ -18,6 +18,7 @@ Contents: target-arm target-avr + target-loongarch target-m68k target-mips target-ppc diff --git a/docs/system/vm-templating.rst b/docs/system/vm-templating.rst new file mode 100644 index 00000000000..28905a1eeb6 --- /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 8e65ce0dfc7..1e88ae48cdc 100644 --- a/docs/tools/index.rst +++ b/docs/tools/index.rst @@ -15,4 +15,4 @@ command line utilities and other standalone programs. qemu-nbd qemu-pr-helper qemu-trace-stap - virtfs-proxy-helper + qemu-vmsr-helper diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index ca5a2773cfb..3653adb963e 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -406,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) @@ -667,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. @@ -694,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 diff --git a/docs/tools/qemu-nbd.rst b/docs/tools/qemu-nbd.rst index 329f44d9895..f82ea5fd77b 100644 --- a/docs/tools/qemu-nbd.rst +++ b/docs/tools/qemu-nbd.rst @@ -1,3 +1,5 @@ +.. _qemu-nbd: + ===================================== QEMU Disk Network Block Device Server ===================================== @@ -154,6 +156,11 @@ driver options if :option:`--image-opts` is specified. Set the NBD volume export description, as a human-readable string. +.. option:: --handshake-limit=N + + Set the timeout for a client to successfully complete its handshake + to N seconds (default 10), or 0 for no limit. + .. option:: -L, --list Connect as a client and list all details about the exports exposed by diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst index ea00149a63a..35ab2d78074 100644 --- a/docs/tools/qemu-storage-daemon.rst +++ b/docs/tools/qemu-storage-daemon.rst @@ -1,3 +1,5 @@ +.. _storage-daemon: + =================== QEMU Storage Daemon =================== diff --git a/docs/tools/qemu-vmsr-helper.rst b/docs/tools/qemu-vmsr-helper.rst new file mode 100644 index 00000000000..9ce10b9af9c --- /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 bd310ebb07b..00000000000 --- a/docs/tools/virtfs-proxy-helper.rst +++ /dev/null @@ -1,75 +0,0 @@ -QEMU 9p virtfs proxy filesystem helper -====================================== - -Synopsis --------- - -**virtfs-proxy-helper** [*OPTIONS*] - -Description ------------ - -NOTE: The 9p 'proxy' backend is deprecated (since QEMU 8.1) and will be -removed, along with this daemon, in a future version of QEMU! - -Pass-through security model in QEMU 9p server needs root privilege to do -few file operations (like chown, chmod to any mode/uid:gid). There are two -issues in pass-through security model: - -- 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/user/main.rst b/docs/user/main.rst index f4786353965..9a1c60448c5 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -1,3 +1,5 @@ +.. _user-mode: + QEMU User space emulator ======================== @@ -42,6 +44,8 @@ QEMU was conceived so that ultimately it can emulate itself. Although it is not very useful, it is an important test to show the power of the emulator. +.. _linux-user-mode: + Linux User space emulator ------------------------- @@ -50,7 +54,7 @@ Command line options :: - qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g port] [-B offset] [-R size] program [arguments...] + qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g endpoint] [-B offset] [-R size] program [arguments...] ``-h`` Print the help @@ -87,20 +91,24 @@ 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 endpoint`` + Wait gdb connection to a port (e.g., ``1234``) or a unix socket (e.g., + ``/tmp/qemu.sock``). + + If a unix socket path contains single ``%d`` placeholder (e.g., + ``/tmp/qemu-%d.sock``), it is replaced by the emulator PID, which is useful + when passing this option via the ``QEMU_GDB`` environment variable to a + multi-process application. -``-g port`` - Wait gdb connection to port + If the endpoint address is followed by ``,suspend=n`` (e.g., + ``1234,suspend=n``), then the emulated program starts without waiting for a + connection, which can be established at any later point in time. ``-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. -``-singlestep`` - This is a deprecated synonym for the ``-one-insn-per-tb`` option. - Environment variables: QEMU_STRACE @@ -136,10 +144,6 @@ Other binaries The binary format is detected automatically. -- user mode (Cris) - - * ``qemu-cris`` TODO. - - user mode (i386) * ``qemu-i386`` TODO. @@ -165,10 +169,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. @@ -189,6 +189,8 @@ Other binaries * ``qemu-sparc64`` can execute some Sparc64 (Sparc64 CPU, 64 bit ABI) and SPARC32PLUS binaries (Sparc64 CPU, 32 bit ABI). +.. _bsd-user-mode: + BSD User space emulator ----------------------- @@ -251,6 +253,3 @@ Debug options: 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. - -``-singlestep`` - This is a deprecated synonym for the ``-one-insn-per-tb`` option. diff --git a/dump/dump-hmp-cmds.c b/dump/dump-hmp-cmds.c index b038785fee3..21023db6fd8 100644 --- a/dump/dump-hmp-cmds.c +++ b/dump/dump-hmp-cmds.c @@ -10,7 +10,7 @@ #include "monitor/monitor.h" #include "qapi/error.h" #include "qapi/qapi-commands-dump.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) { @@ -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 d4ef713cd06..15bbcc0c619 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -17,9 +17,9 @@ #include "qemu/bswap.h" #include "exec/target_page.h" #include "monitor/monitor.h" -#include "sysemu/dump.h" -#include "sysemu/runstate.h" -#include "sysemu/cpus.h" +#include "system/dump.h" +#include "system/runstate.h" +#include "system/cpus.h" #include "qapi/error.h" #include "qapi/qapi-commands-dump.h" #include "qapi/qapi-events-dump.h" @@ -30,6 +30,7 @@ #include "migration/blocker.h" #include "hw/core/cpu.h" #include "win_dump.h" +#include "qemu/range.h" #include #ifdef CONFIG_LZO @@ -96,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; } @@ -570,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; } @@ -730,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; } @@ -809,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); @@ -824,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; } @@ -833,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; } @@ -849,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; } @@ -982,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; } @@ -1012,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; @@ -1027,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; @@ -1093,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; } @@ -1123,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; @@ -1139,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; @@ -1204,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; } @@ -1212,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; } @@ -1380,7 +1404,7 @@ static void write_dump_bitmap(DumpState *s, Error **errp) 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); @@ -1399,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; } @@ -1644,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; @@ -1665,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; @@ -1775,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(); @@ -1786,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) { @@ -1810,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; @@ -1839,12 +1845,6 @@ 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. @@ -1872,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); @@ -2061,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."); @@ -2085,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. @@ -2127,25 +2152,24 @@ void qmp_dump_guest_memory(bool paging, const char *file, return; } -#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; } @@ -2158,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; @@ -2168,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; @@ -2196,15 +2220,18 @@ 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 if (win_dump_available(NULL)) { diff --git a/dump/win_dump.c b/dump/win_dump.c index b7bfaff3794..3162e8bd487 100644 --- a/dump/win_dump.c +++ b/dump/win_dump.c @@ -9,10 +9,9 @@ */ #include "qemu/osdep.h" -#include "sysemu/dump.h" +#include "system/dump.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "qapi/qmp/qerror.h" #include "exec/cpu-defs.h" #include "hw/core/cpu.h" #include "qemu/win_dump_defs.h" @@ -52,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) { @@ -65,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; } @@ -459,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,8 +476,6 @@ void create_win_dump(DumpState *s, Error **errp) g_free(saved_ctx); out_cr3: first_x86_cpu->env.cr[3] = saved_cr3; - - return; } #else /* !TARGET_X86_64 */ diff --git a/dump/win_dump.h b/dump/win_dump.h index c9b49f87dc8..9d6cfa47c56 100644 --- a/dump/win_dump.h +++ b/dump/win_dump.h @@ -11,7 +11,7 @@ #ifndef WIN_DUMP_H #define WIN_DUMP_H -#include "sysemu/dump.h" +#include "system/dump.h" /* Check Windows dump availability for the current target */ bool win_dump_available(Error **errp); diff --git a/ebpf/ebpf.c b/ebpf/ebpf.c new file mode 100644 index 00000000000..2d73beb4796 --- /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 00000000000..378d4e9c706 --- /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 e71e229190d..d0e7f99fb91 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 cee658c158b..e793786c172 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); +} + +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; } -bool ebpf_rss_load(struct EBPFRSSContext *ctx) +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,91 +135,147 @@ 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, - struct EBPFRSSConfig *config) +bool ebpf_rss_load_fds(struct EBPFRSSContext *ctx, int program_fd, + int config_fd, int toeplitz_fd, int table_fd, + Error **errp) { - uint32_t map_key = 0; + if (ebpf_rss_is_loaded(ctx)) { + error_setg(errp, "eBPF program is already loaded"); + return false; + } - if (!ebpf_rss_is_loaded(ctx)) { + 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 (bpf_map_update_elem(ctx->map_configuration, - &map_key, config, 0) < 0) { + 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) +{ + 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 (!ebpf_rss_set_config(ctx, config)) { + if (config == NULL) { + error_setg(errp, "eBPF config table is NULL"); return false; } - - if (!ebpf_rss_set_indirections_table(ctx, indirections_table, - config->indirections_len)) { + 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_toepliz_key(ctx, toeplitz_key)) { + ebpf_rss_set_config(ctx, config); + + if (!ebpf_rss_set_indirections_table(ctx, indirections_table, + config->indirections_len, + errp)) { 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 bf3f2572c7c..86a5787789d 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 2f627d6c7d0..bff6156f518 100644 --- a/ebpf/meson.build +++ b/ebpf/meson.build @@ -1 +1 @@ -system_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 18eb2adb12c..647212e5dd0 100644 --- a/ebpf/rss.bpf.skeleton.h +++ b/ebpf/rss.bpf.skeleton.h @@ -165,7 +165,7 @@ rss_bpf__create_skeleton(struct rss_bpf *obj) s->progs[0].prog = &obj->progs.tun_rss_steering_prog; s->progs[0].link = &obj->links.tun_rss_steering_prog; - s->data = (void *)rss_bpf__elf_bytes(&s->data_sz); + s->data = rss_bpf__elf_bytes(&s->data_sz); obj->skeleton = s; return 0; @@ -176,333 +176,313 @@ rss_bpf__create_skeleton(struct rss_bpf *obj) static inline const void *rss_bpf__elf_bytes(size_t *sz) { - *sz = 20440; - return (const void *)"\ + 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\x98\x4c\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x0d\0\ -\x01\0\xbf\x19\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x63\x1a\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\x08\0\0\0\0\0\0\ -\x18\0\0\0\xff\xff\xff\xff\0\0\0\0\0\0\0\0\x15\x06\x67\x02\0\0\0\0\xbf\x87\0\0\ -\0\0\0\0\x15\x07\x65\x02\0\0\0\0\x71\x61\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\ -\0\x5e\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\x15\x09\x4d\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\x42\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\x32\x02\0\ -\0\0\0\x69\xa1\xd0\xff\0\0\0\0\x15\x01\x30\x02\0\0\0\0\x7b\x7a\x38\xff\0\0\0\0\ -\x7b\x9a\x40\xff\0\0\0\0\x15\x01\x55\0\x86\xdd\0\0\x55\x01\x39\0\x08\0\0\0\xb7\ -\x07\0\0\x01\0\0\0\x73\x7a\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\x40\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\x1c\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\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\ -\x73\x7a\x5e\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\x48\xff\0\0\0\0\xbf\x91\0\0\0\0\0\0\x57\x01\0\0\xff\0\0\ -\0\x15\x01\x19\0\0\0\0\0\x57\x07\0\0\xff\0\0\0\x55\x07\x17\0\0\0\0\0\x57\x09\0\ -\0\xff\0\0\0\x15\x09\x5a\x01\x11\0\0\0\x55\x09\x14\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\x40\xff\0\0\0\0\x79\xa2\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\xf7\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\xdb\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\x5b\xff\0\0\0\0\x79\xa0\x38\xff\0\0\0\0\x15\x02\x0c\x01\0\0\0\0\xbf\ -\x12\0\0\0\0\0\0\x57\x02\0\0\x02\0\0\0\x15\x02\x09\x01\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\x6b\x01\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\xb7\x01\0\0\x28\0\0\0\x7b\x1a\x48\ -\xff\0\0\0\0\xbf\x91\0\0\0\0\0\0\xb7\x02\0\0\0\0\0\0\xb7\x04\0\0\x28\0\0\0\xb7\ -\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\ -\x55\0\xfe\0\0\0\0\0\x79\xa1\xe0\xff\0\0\0\0\x63\x1a\x6c\xff\0\0\0\0\x77\x01\0\ -\0\x20\0\0\0\x63\x1a\x70\xff\0\0\0\0\x79\xa1\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\x13\x01\x3c\0\0\0\xb7\x01\0\0\x01\0\0\ -\0\x6f\x91\0\0\0\0\0\0\x18\x02\0\0\x01\0\0\0\0\0\0\0\0\x18\0\x1c\x5f\x21\0\0\0\ -\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\x0c\x01\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\x48\xff\0\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\x18\x07\0\0\x01\0\0\0\0\0\ -\0\0\0\x18\0\x1c\xb7\x02\0\0\0\0\0\0\x7b\x8a\x28\xff\0\0\0\0\x7b\x2a\x30\xff\0\ -\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xfe\xff\xff\xff\x79\xa1\x40\xff\0\0\0\0\ -\x79\xa2\x48\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\ -\x91\x01\0\0\0\0\xbf\x91\0\0\0\0\0\0\x15\x01\x26\0\x3c\0\0\0\x15\x01\x5f\0\x2c\ -\0\0\0\x55\x01\x60\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\xa7\x40\xff\0\0\0\0\xbf\x71\0\ -\0\0\0\0\0\x79\xa2\x48\xff\0\0\0\0\xb7\x04\0\0\x04\0\0\0\xb7\x05\0\0\x01\0\0\0\ -\x85\0\0\0\x44\0\0\0\xbf\x01\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\ -\0\0\0\x55\x01\x06\x01\0\0\0\0\x71\xa1\xfa\xff\0\0\0\0\x55\x01\x11\0\x02\0\0\0\ -\x71\xa1\xf9\xff\0\0\0\0\x55\x01\x0f\0\x02\0\0\0\x71\xa1\xfb\xff\0\0\0\0\x55\ -\x01\x0d\0\x01\0\0\0\x79\xa2\x48\xff\0\0\0\0\x07\x02\0\0\x08\0\0\0\xbf\x71\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\xbf\x01\0\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\ -\0\0\0\x55\x01\xf5\0\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5d\xff\0\0\0\0\x18\ -\x07\0\0\x01\0\0\0\0\0\0\0\0\x18\0\x1c\x05\0\x3c\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\x13\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\x04\0\0\0\0\0\x79\ -\xa8\x28\xff\0\0\0\0\x18\x07\0\0\x01\0\0\0\0\0\0\0\0\x18\0\x1c\x05\0\x25\0\0\0\ -\0\0\xbf\x89\0\0\0\0\0\0\x79\xa1\x48\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\x40\xff\0\0\0\0\xbf\x92\0\0\0\0\ -\0\0\xb7\x04\0\0\x02\0\0\0\xb7\x05\0\0\x01\0\0\0\x85\0\0\0\x44\0\0\0\xbf\x01\0\ -\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x55\x01\x79\0\0\0\0\0\ -\x71\xa2\xf8\xff\0\0\0\0\x55\x02\x0e\0\xc9\0\0\0\x07\x09\0\0\x02\0\0\0\x79\xa1\ -\x40\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\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\x6c\0\0\0\0\0\xb7\x01\0\0\x01\0\0\0\ -\x73\x1a\x5c\xff\0\0\0\0\x05\0\xde\xff\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x15\x02\ -\xcd\xff\0\0\0\0\x71\xa1\xf9\xff\0\0\0\0\x07\x01\0\0\x02\0\0\0\x05\0\xca\xff\0\ -\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5e\xff\0\0\0\0\x71\xa1\xff\xff\0\0\0\0\ -\x67\x01\0\0\x03\0\0\0\x79\xa2\x48\xff\0\0\0\0\x0f\x12\0\0\0\0\0\0\x07\x02\0\0\ -\x08\0\0\0\x7b\x2a\x48\xff\0\0\0\0\x71\xa9\xfe\xff\0\0\0\0\x79\xa2\x30\xff\0\0\ -\0\0\x25\x09\x0c\0\x3c\0\0\0\xb7\x01\0\0\x01\0\0\0\x6f\x91\0\0\0\0\0\0\x5f\x71\ -\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\x07\0\0\0\0\0\x07\x02\0\0\x01\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\x55\x01\x7d\ -\xff\x0b\0\0\0\x71\xa7\x5e\xff\0\0\0\0\x05\0\x09\xff\0\0\0\0\x15\x09\xf8\xff\ -\x87\0\0\0\x05\0\xfc\xff\0\0\0\0\x71\xa1\x59\xff\0\0\0\0\x79\xa0\x38\xff\0\0\0\ -\0\x15\x01\x13\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\x5b\xff\0\0\0\0\x15\x02\x42\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\ -\x02\0\0\x10\0\0\0\x15\x02\x3f\0\0\0\0\0\x57\x01\0\0\x80\0\0\0\xb7\x02\0\0\x10\ -\0\0\0\xb7\x03\0\0\x10\0\0\0\x15\x01\x01\0\0\0\0\0\xb7\x03\0\0\x30\0\0\0\x71\ -\xa4\x5d\xff\0\0\0\0\x15\x04\x01\0\0\0\0\0\xbf\x32\0\0\0\0\0\0\xbf\xa3\0\0\0\0\ -\0\0\x07\x03\0\0\x64\xff\xff\xff\xbf\x34\0\0\0\0\0\0\x15\x01\x02\0\0\0\0\0\xbf\ -\xa4\0\0\0\0\0\0\x07\x04\0\0\x84\xff\xff\xff\x71\xa5\x5c\xff\0\0\0\0\xbf\x31\0\ -\0\0\0\0\0\x15\x05\x01\0\0\0\0\0\xbf\x41\0\0\0\0\0\0\x61\x14\x04\0\0\0\0\0\x67\ -\x04\0\0\x20\0\0\0\x61\x15\0\0\0\0\0\0\x4f\x54\0\0\0\0\0\0\x7b\x4a\xa8\xff\0\0\ -\0\0\x61\x14\x08\0\0\0\0\0\x61\x11\x0c\0\0\0\0\0\x67\x01\0\0\x20\0\0\0\x4f\x41\ -\0\0\0\0\0\0\x7b\x1a\xb0\xff\0\0\0\0\x0f\x23\0\0\0\0\0\0\x61\x31\0\0\0\0\0\0\ -\x61\x32\x04\0\0\0\0\0\x61\x34\x08\0\0\0\0\0\x61\x33\x0c\0\0\0\0\0\x69\xa5\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\x6b\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\xf1\xfe\0\0\0\0\x57\x01\0\0\x01\0\0\ -\0\x15\x01\xd0\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\x5e\0\0\0\0\0\xb7\x09\0\0\ -\x3c\0\0\0\x79\xa8\x28\xff\0\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x15\ -\0\xac\xff\0\0\0\0\x05\0\xc5\0\0\0\0\0\x71\xa2\x5a\xff\0\0\0\0\x15\x02\x26\0\0\ -\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x20\0\0\0\x15\x02\x23\0\0\0\0\0\x57\x01\ -\0\0\0\x01\0\0\xb7\x02\0\0\x10\0\0\0\xb7\x03\0\0\x10\0\0\0\x15\x01\x01\0\0\0\0\ -\0\xb7\x03\0\0\x30\0\0\0\x71\xa4\x5d\xff\0\0\0\0\x15\x04\x01\0\0\0\0\0\xbf\x32\ -\0\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x64\xff\xff\xff\xbf\x34\0\0\0\0\0\ -\0\x15\x01\x02\0\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\x84\xff\xff\xff\x71\ -\xa5\x5c\xff\0\0\0\0\xbf\x31\0\0\0\0\0\0\x15\x05\xbd\xff\0\0\0\0\x05\0\xbb\xff\ -\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\ -\x40\xff\0\0\0\0\x79\xa2\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\xa0\0\ -\0\0\0\0\x05\0\xa8\xfe\0\0\0\0\x15\x09\xf3\xfe\x87\0\0\0\x05\0\x83\xff\0\0\0\0\ -\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x08\0\0\0\x15\x02\x9a\0\0\0\0\0\x57\x01\0\0\ -\x40\0\0\0\xb7\x02\0\0\x0c\0\0\0\xb7\x03\0\0\x0c\0\0\0\x15\x01\x01\0\0\0\0\0\ -\xb7\x03\0\0\x2c\0\0\0\x71\xa4\x5c\xff\0\0\0\0\x15\x04\x01\0\0\0\0\0\xbf\x32\0\ -\0\0\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\x58\xff\xff\xff\x0f\x23\0\0\0\0\0\0\ -\x61\x32\x04\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\x61\x34\0\0\0\0\0\0\x4f\x42\0\0\0\ -\0\0\0\x7b\x2a\xa8\xff\0\0\0\0\x61\x32\x08\0\0\0\0\0\x61\x33\x0c\0\0\0\0\0\x67\ -\x03\0\0\x20\0\0\0\x4f\x23\0\0\0\0\0\0\x7b\x3a\xb0\xff\0\0\0\0\x71\xa2\x5d\xff\ -\0\0\0\0\x15\x02\x0c\0\0\0\0\0\x15\x01\x0b\0\0\0\0\0\x61\xa1\xa0\xff\0\0\0\0\ -\x67\x01\0\0\x20\0\0\0\x61\xa2\x9c\xff\0\0\0\0\x4f\x21\0\0\0\0\0\0\x7b\x1a\xc0\ -\xff\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\x05\0\x0a\0\0\0\0\0\xb7\x09\0\0\x2b\0\0\0\x05\0\xae\xff\0\0\0\0\x61\xa1\ -\x80\xff\0\0\0\0\x67\x01\0\0\x20\0\0\0\x61\xa2\x7c\xff\0\0\0\0\x4f\x21\0\0\0\0\ -\0\0\x7b\x1a\xc0\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\xb7\x02\0\0\0\ -\0\0\0\x07\x08\0\0\x04\0\0\0\x61\x03\0\0\0\0\0\0\xb7\x05\0\0\0\0\0\0\xbf\xa1\0\ -\0\0\0\0\0\x07\x01\0\0\xa8\xff\xff\xff\x0f\x21\0\0\0\0\0\0\x71\x14\0\0\0\0\0\0\ -\xbf\x41\0\0\0\0\0\0\x67\x01\0\0\x38\0\0\0\xc7\x01\0\0\x3f\0\0\0\x5f\x31\0\0\0\ -\0\0\0\xaf\x51\0\0\0\0\0\0\xbf\x85\0\0\0\0\0\0\x0f\x25\0\0\0\0\0\0\x71\x55\0\0\ -\0\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x07\0\0\0\x4f\x03\ -\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x39\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\ -\x30\0\0\0\0\0\0\xaf\x01\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x06\0\0\0\ -\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\ -\0\0\x67\0\0\0\x3a\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x01\0\0\0\ -\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x05\0\0\0\x57\0\0\0\ -\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3b\0\0\0\xc7\0\0\ -\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x01\0\0\0\0\0\0\x67\x03\0\0\x01\0\0\0\xbf\ -\x50\0\0\0\0\0\0\x77\0\0\0\x04\0\0\0\x57\0\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\ -\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3c\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\ -\0\xaf\x01\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x03\0\0\0\x57\0\0\0\x01\0\ -\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\ -\x3d\0\0\0\xc7\0\0\0\x3f\0\0\0\x5f\x30\0\0\0\0\0\0\xaf\x01\0\0\0\0\0\0\xbf\x50\ -\0\0\0\0\0\0\x77\0\0\0\x02\0\0\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\ -\x03\0\0\0\0\0\0\xbf\x40\0\0\0\0\0\0\x67\0\0\0\x3e\0\0\0\xc7\0\0\0\x3f\0\0\0\ -\x5f\x30\0\0\0\0\0\0\xaf\x01\0\0\0\0\0\0\xbf\x50\0\0\0\0\0\0\x77\0\0\0\x01\0\0\ -\0\x57\0\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x03\0\0\0\0\0\0\x57\x04\0\0\ -\x01\0\0\0\x87\x04\0\0\0\0\0\0\x5f\x34\0\0\0\0\0\0\xaf\x41\0\0\0\0\0\0\x57\x05\ -\0\0\x01\0\0\0\x67\x03\0\0\x01\0\0\0\x4f\x53\0\0\0\0\0\0\x07\x02\0\0\x01\0\0\0\ -\xbf\x15\0\0\0\0\0\0\x15\x02\x01\0\x24\0\0\0\x05\0\xa9\xff\0\0\0\0\xbf\x12\0\0\ -\0\0\0\0\x67\x02\0\0\x20\0\0\0\x77\x02\0\0\x20\0\0\0\x15\x02\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\x42\0\0\0\0\0\0\x2f\x42\0\0\0\0\0\0\x1f\x21\0\0\0\0\0\0\x63\x1a\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\x05\0\0\0\0\0\x71\x61\x08\0\0\0\0\ -\0\x71\x60\x09\0\0\0\0\0\x67\0\0\0\x08\0\0\0\x4f\x10\0\0\0\0\0\0\x95\0\0\0\0\0\ -\0\0\x69\0\0\0\0\0\0\0\x05\0\xfd\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\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\x47\x50\x4c\x20\x76\x32\0\0\x9f\xeb\x01\0\x18\0\0\0\0\0\0\0\x10\x05\0\0\x10\ -\x05\0\0\x65\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\x04\0\0\x04\x20\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\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\0\0\0\x0e\x0b\0\0\0\x01\0\0\ -\0\0\0\0\0\0\0\0\x02\x0e\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\x04\0\0\x04\x20\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\x0d\0\0\0\x80\0\0\0\x32\0\0\0\x09\0\0\0\xc0\0\0\ -\0\x59\0\0\0\0\0\0\x0e\x0f\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x12\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\x04\0\0\x04\x20\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\x11\0\0\0\xc0\0\0\0\x72\0\0\0\0\0\0\x0e\x13\0\0\0\x01\0\0\ -\0\0\0\0\0\0\0\0\x02\x16\0\0\0\x90\0\0\0\x22\0\0\x04\xc0\0\0\0\x9a\0\0\0\x17\0\ -\0\0\0\0\0\0\x9e\0\0\0\x17\0\0\0\x20\0\0\0\xa7\0\0\0\x17\0\0\0\x40\0\0\0\xac\0\ -\0\0\x17\0\0\0\x60\0\0\0\xba\0\0\0\x17\0\0\0\x80\0\0\0\xc3\0\0\0\x17\0\0\0\xa0\ -\0\0\0\xd0\0\0\0\x17\0\0\0\xc0\0\0\0\xd9\0\0\0\x17\0\0\0\xe0\0\0\0\xe4\0\0\0\ -\x17\0\0\0\0\x01\0\0\xed\0\0\0\x17\0\0\0\x20\x01\0\0\xfd\0\0\0\x17\0\0\0\x40\ -\x01\0\0\x05\x01\0\0\x17\0\0\0\x60\x01\0\0\x0e\x01\0\0\x19\0\0\0\x80\x01\0\0\ -\x11\x01\0\0\x17\0\0\0\x20\x02\0\0\x16\x01\0\0\x17\0\0\0\x40\x02\0\0\x21\x01\0\ -\0\x17\0\0\0\x60\x02\0\0\x26\x01\0\0\x17\0\0\0\x80\x02\0\0\x2f\x01\0\0\x17\0\0\ -\0\xa0\x02\0\0\x37\x01\0\0\x17\0\0\0\xc0\x02\0\0\x3e\x01\0\0\x17\0\0\0\xe0\x02\ -\0\0\x49\x01\0\0\x17\0\0\0\0\x03\0\0\x53\x01\0\0\x1a\0\0\0\x20\x03\0\0\x5e\x01\ -\0\0\x1a\0\0\0\xa0\x03\0\0\x68\x01\0\0\x17\0\0\0\x20\x04\0\0\x74\x01\0\0\x17\0\ -\0\0\x40\x04\0\0\x7f\x01\0\0\x17\0\0\0\x60\x04\0\0\0\0\0\0\x1b\0\0\0\x80\x04\0\ -\0\x89\x01\0\0\x1d\0\0\0\xc0\x04\0\0\x90\x01\0\0\x17\0\0\0\0\x05\0\0\x99\x01\0\ -\0\x17\0\0\0\x20\x05\0\0\0\0\0\0\x1f\0\0\0\x40\x05\0\0\xa2\x01\0\0\x17\0\0\0\ -\x80\x05\0\0\xab\x01\0\0\x21\0\0\0\xa0\x05\0\0\xb7\x01\0\0\x1d\0\0\0\xc0\x05\0\ -\0\xc0\x01\0\0\0\0\0\x08\x18\0\0\0\xc6\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\x17\0\0\0\x04\0\0\0\x05\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\ -\0\x17\0\0\0\x04\0\0\0\x04\0\0\0\0\0\0\0\x01\0\0\x05\x08\0\0\0\xd3\x01\0\0\x1c\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\x2a\0\0\0\xdd\x01\0\0\0\0\0\x08\x1e\0\0\0\xe3\ -\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\xf6\x01\0\ -\0\x20\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\x2b\0\0\0\xf9\x01\0\0\0\0\0\x08\x22\0\0\ -\0\xfe\x01\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\x0c\ -\x02\0\0\x15\0\0\0\x10\x02\0\0\x01\0\0\x0c\x23\0\0\0\x32\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\x25\0\0\0\x04\0\0\0\x07\0\0\0\x37\ -\x11\0\0\0\0\0\x0e\x26\0\0\0\x01\0\0\0\x40\x11\0\0\x03\0\0\x0f\0\0\0\0\x0c\0\0\ -\0\0\0\0\0\x20\0\0\0\x10\0\0\0\0\0\0\0\x20\0\0\0\x14\0\0\0\0\0\0\0\x20\0\0\0\ -\x46\x11\0\0\x01\0\0\x0f\0\0\0\0\x27\0\0\0\0\0\0\0\x07\0\0\0\x4e\x11\0\0\0\0\0\ -\x07\0\0\0\0\x5c\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\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\x74\x75\x6e\x5f\x72\ -\x73\x73\x5f\x73\x74\x65\x65\x72\x69\x6e\x67\0\x2f\x68\x6f\x6d\x65\x2f\x73\x68\ -\x72\x65\x65\x73\x68\x2f\x63\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\x69\x70\x2e\x66\x72\x61\x67\x5f\ -\x6f\x66\x66\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\ -\x6e\x5f\x73\x72\x63\x20\x3d\x20\x69\x70\x2e\x73\x61\x64\x64\x72\x3b\0\x20\x20\ -\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x6e\x5f\x64\x73\x74\x20\ -\x3d\x20\x69\x70\x2e\x64\x61\x64\x64\x72\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\ -\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\x20\x69\x70\x2e\x70\x72\ -\x6f\x74\x6f\x63\x6f\x6c\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x6c\x34\x5f\x6f\ -\x66\x66\x73\x65\x74\x20\x3d\x20\x69\x70\x2e\x69\x68\x6c\x20\x2a\x20\x34\x3b\0\ -\x20\x20\x20\x20\x69\x66\x20\x28\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\ -\x20\x21\x3d\x20\x30\x20\x26\x26\x20\x21\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\ -\x66\x72\x61\x67\x6d\x65\x6e\x74\x65\x64\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\ -\x20\x20\x69\x66\x20\x28\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x20\x3d\ -\x3d\x20\x49\x50\x50\x52\x4f\x54\x4f\x5f\x54\x43\x50\x29\x20\x7b\0\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x74\ -\x63\x70\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x73\x74\x72\x75\x63\x74\x20\x74\x63\x70\x68\x64\x72\x20\x74\x63\x70\x20\x3d\ -\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\ -\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\ -\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\x6c\x34\ -\x5f\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x74\x63\x70\x2c\x20\x73\x69\x7a\x65\ -\x6f\x66\x28\x74\x63\x70\x29\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x69\x66\x20\x28\x65\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\x69\x66\x20\x28\ -\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x69\x70\x76\x34\ -\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x70\x61\x63\x6b\ -\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x74\x63\x70\x20\x26\x26\0\x20\x20\ +\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\ @@ -530,442 +510,457 @@ static inline const void *rss_bpf__elf_bytes(size_t *sz) \x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\ \x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\x20\ \x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x65\x78\x74\x5f\x72\x74\ -\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x28\x65\ -\x78\x74\x5f\x72\x74\x2e\x74\x79\x70\x65\x20\x3d\x3d\x20\x49\x50\x56\x36\x5f\ -\x53\x52\x43\x52\x54\x5f\x54\x59\x50\x45\x5f\x32\x29\x20\x26\x26\0\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2a\x6c\ -\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x20\x6f\x66\x66\x73\x65\x74\x6f\x66\ -\x28\x73\x74\x72\x75\x63\x74\x20\x72\x74\x32\x5f\x68\x64\x72\x2c\x20\x61\x64\ -\x64\x72\x29\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\x64\ -\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\x62\ -\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\ -\x20\x28\x65\x72\x72\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ +\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\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\ +\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\ -\x20\x20\x20\x20\x2a\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x20\x6f\x70\ -\x74\x5f\x6f\x66\x66\x73\x65\x74\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\ -\x73\x6b\x62\x5f\x6c\x6f\x61\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\ -\x74\x69\x76\x65\x28\x73\x6b\x62\x2c\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x65\x72\x72\x29\x20\ -\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x69\x70\x76\x36\x5f\x65\x78\x74\ -\x5f\x73\x72\x63\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x69\x6e\x66\x6f\x2d\x3e\x69\x73\x5f\x66\x72\x61\x67\x6d\x65\x6e\x74\ -\x65\x64\x20\x3d\x20\x74\x72\x75\x65\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x2a\ -\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x20\x2b\x3d\x20\x28\x65\x78\x74\x5f\x68\ -\x64\x72\x2e\x68\x64\x72\x6c\x65\x6e\x20\x2b\x20\x31\x29\x20\x2a\x20\x38\x3b\0\ -\x20\x20\x20\x20\x20\x20\x20\x20\x2a\x6c\x34\x5f\x70\x72\x6f\x74\x6f\x63\x6f\ -\x6c\x20\x3d\x20\x65\x78\x74\x5f\x68\x64\x72\x2e\x6e\x65\x78\x74\x68\x64\x72\ -\x3b\0\x20\x20\x20\x20\x66\x6f\x72\x20\x28\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\ -\x69\x6e\x74\x20\x69\x20\x3d\x20\x30\x3b\x20\x69\x20\x3c\x20\x49\x50\x36\x5f\ -\x45\x58\x54\x45\x4e\x53\x49\x4f\x4e\x53\x5f\x43\x4f\x55\x4e\x54\x3b\x20\x2b\ -\x2b\x69\x29\x20\x7b\0\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\x69\x66\x20\ -\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x69\x70\x76\ -\x36\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\ -\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x69\x70\x76\ -\x36\x5f\x65\x78\x74\x5f\x64\x73\x74\x20\x26\x26\0\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x69\x66\x20\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\ -\x6f\x2e\x69\x73\x5f\x69\x70\x76\x36\x5f\x65\x78\x74\x5f\x73\x72\x63\x20\x26\ -\x26\0\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\x69\x66\x20\ -\x28\x70\x61\x63\x6b\x65\x74\x5f\x69\x6e\x66\x6f\x2e\x69\x73\x5f\x75\x64\x70\ -\x20\x26\x26\0\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\x69\ -\x66\x20\x28\x63\x6f\x6e\x66\x69\x67\x2d\x3e\x68\x61\x73\x68\x5f\x74\x79\x70\ -\x65\x73\x20\x26\x20\x56\x49\x52\x54\x49\x4f\x5f\x4e\x45\x54\x5f\x52\x53\x53\ -\x5f\x48\x41\x53\x48\x5f\x54\x59\x50\x45\x5f\x49\x50\x76\x34\x29\x20\x7b\0\x20\ -\x20\x20\x20\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x5f\x6d\x65\x6d\x63\x70\x79\ -\x28\x26\x72\x73\x73\x5f\x69\x6e\x70\x75\x74\x5b\x2a\x62\x79\x74\x65\x73\x5f\ -\x77\x72\x69\x74\x74\x65\x6e\x5d\x2c\x20\x70\x74\x72\x2c\x20\x73\x69\x7a\x65\ -\x29\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x6e\x66\x6f\x2d\ -\x3e\x69\x73\x5f\x75\x64\x70\x20\x3d\x20\x31\x3b\0\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x73\x74\x72\x75\x63\x74\x20\x75\x64\x70\x68\x64\x72\x20\ -\x75\x64\x70\x20\x3d\x20\x7b\x7d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x73\x6b\x62\x5f\x6c\x6f\x61\ -\x64\x5f\x62\x79\x74\x65\x73\x5f\x72\x65\x6c\x61\x74\x69\x76\x65\x28\x73\x6b\ -\x62\x2c\x20\x6c\x34\x5f\x6f\x66\x66\x73\x65\x74\x2c\x20\x26\x75\x64\x70\x2c\ -\x20\x73\x69\x7a\x65\x6f\x66\x28\x75\x64\x70\x29\x2c\0\x20\x20\x20\x20\x20\x20\ -\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\x69\x66\x20\x28\x63\x6f\x6e\x66\x69\x67\ -\x2d\x3e\x68\x61\x73\x68\x5f\x74\x79\x70\x65\x73\x20\x26\x20\x56\x49\x52\x54\ -\x49\x4f\x5f\x4e\x45\x54\x5f\x52\x53\x53\x5f\x48\x41\x53\x48\x5f\x54\x59\x50\ -\x45\x5f\x49\x50\x76\x36\x29\x20\x7b\0\x20\x20\x20\x20\x66\x6f\x72\x20\x28\x62\ -\x79\x74\x65\x20\x3d\x20\x30\x3b\x20\x62\x79\x74\x65\x20\x3c\x20\x48\x41\x53\ -\x48\x5f\x43\x41\x4c\x43\x55\x4c\x41\x54\x49\x4f\x4e\x5f\x42\x55\x46\x46\x45\ -\x52\x5f\x53\x49\x5a\x45\x3b\x20\x62\x79\x74\x65\x2b\x2b\x29\x20\x7b\0\x20\x20\ -\x20\x20\x5f\x5f\x75\x33\x32\x20\x6c\x65\x66\x74\x6d\x6f\x73\x74\x5f\x33\x32\ -\x5f\x62\x69\x74\x73\x20\x3d\x20\x6b\x65\x79\x2d\x3e\x6c\x65\x66\x74\x6d\x6f\ -\x73\x74\x5f\x33\x32\x5f\x62\x69\x74\x73\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\ -\x5f\x5f\x75\x38\x20\x69\x6e\x70\x75\x74\x5f\x62\x79\x74\x65\x20\x3d\x20\x69\ -\x6e\x70\x75\x74\x5b\x62\x79\x74\x65\x5d\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x69\x66\x20\x28\x69\x6e\x70\x75\x74\x5f\x62\x79\x74\x65\x20\ -\x26\x20\x28\x31\x20\x3c\x3c\x20\x37\x29\x29\x20\x7b\0\x20\x20\x20\x20\x20\x20\ -\x20\x20\x5f\x5f\x75\x38\x20\x6b\x65\x79\x5f\x62\x79\x74\x65\x20\x3d\x20\x6b\ -\x65\x79\x2d\x3e\x6e\x65\x78\x74\x5f\x62\x79\x74\x65\x5b\x62\x79\x74\x65\x5d\ -\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x28\x6c\x65\x66\x74\x6d\x6f\x73\x74\x5f\x33\x32\x5f\x62\x69\x74\x73\ -\x20\x3c\x3c\x20\x31\x29\x20\x7c\x20\x28\x28\x6b\x65\x79\x5f\x62\x79\x74\x65\ -\x20\x26\x20\x28\x31\x20\x3c\x3c\x20\x37\x29\x29\x20\x3e\x3e\x20\x37\x29\x3b\0\ -\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x68\x61\x73\x68\x29\x20\x7b\0\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x5f\x5f\x75\x33\x32\x20\x74\ -\x61\x62\x6c\x65\x5f\x69\x64\x78\x20\x3d\x20\x68\x61\x73\x68\x20\x25\x20\x63\ -\x6f\x6e\x66\x69\x67\x2d\x3e\x69\x6e\x64\x69\x72\x65\x63\x74\x69\x6f\x6e\x73\ -\x5f\x6c\x65\x6e\x3b\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x71\x75\ -\x65\x75\x65\x20\x3d\x20\x62\x70\x66\x5f\x6d\x61\x70\x5f\x6c\x6f\x6f\x6b\x75\ -\x70\x5f\x65\x6c\x65\x6d\x28\x26\x74\x61\x70\x5f\x72\x73\x73\x5f\x6d\x61\x70\ -\x5f\x69\x6e\x64\x69\x72\x65\x63\x74\x69\x6f\x6e\x5f\x74\x61\x62\x6c\x65\x2c\0\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x71\x75\x65\ -\x75\x65\x29\x20\x7b\0\x7d\0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x72\x65\x74\x75\x72\x6e\x20\x2a\x71\x75\x65\x75\x65\x3b\0\x63\ -\x68\x61\x72\0\x5f\x6c\x69\x63\x65\x6e\x73\x65\0\x2e\x6d\x61\x70\x73\0\x6c\x69\ -\x63\x65\x6e\x73\x65\0\x62\x70\x66\x5f\x66\x6c\x6f\x77\x5f\x6b\x65\x79\x73\0\ -\x62\x70\x66\x5f\x73\x6f\x63\x6b\0\0\0\0\x9f\xeb\x01\0\x20\0\0\0\0\0\0\0\x14\0\ -\0\0\x14\0\0\0\x6c\x0c\0\0\x80\x0c\0\0\0\0\0\0\x08\0\0\0\x26\x02\0\0\x01\0\0\0\ -\0\0\0\0\x24\0\0\0\x10\0\0\0\x26\x02\0\0\xc6\0\0\0\0\0\0\0\x37\x02\0\0\x61\x02\ -\0\0\0\x50\x08\0\x10\0\0\0\x37\x02\0\0\x92\x02\0\0\x0b\x68\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\xa5\x02\0\0\x0e\x74\x08\0\x50\0\ -\0\0\x37\x02\0\0\xea\x02\0\0\x0b\x78\x08\0\x88\0\0\0\x37\x02\0\0\x2a\x03\0\0\ -\x10\x80\x08\0\x90\0\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x98\0\0\0\x37\x02\0\0\x2a\ -\x03\0\0\x10\x80\x08\0\xa0\0\0\0\x37\x02\0\0\x43\x03\0\0\x16\x84\x08\0\xa8\0\0\ -\0\x37\x02\0\0\x43\x03\0\0\x0d\x84\x08\0\xc0\0\0\0\x37\x02\0\0\x64\x03\0\0\x0a\ -\xfc\x05\0\xe8\0\0\0\x37\x02\0\0\x9b\x03\0\0\x1f\x0c\x06\0\x38\x01\0\0\x37\x02\ -\0\0\xcb\x03\0\0\x0f\xa0\x04\0\x40\x01\0\0\x37\x02\0\0\xe4\x03\0\0\x0c\x20\x04\ -\0\x50\x01\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x58\x01\0\0\x37\x02\0\0\xf8\x03\0\0\ -\x0b\x2c\x04\0\x90\x01\0\0\x37\x02\0\0\x3e\x04\0\0\x09\x34\x04\0\xa0\x01\0\0\ -\x37\x02\0\0\x4d\x04\0\0\x0d\x44\x04\0\xb8\x01\0\0\x37\x02\0\0\x4d\x04\0\0\x05\ -\x44\x04\0\xd8\x01\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\xe0\x01\0\0\x37\x02\0\0\x6b\ -\x04\0\0\x0f\x58\x04\0\x10\x02\0\0\x37\x02\0\0\x3e\x04\0\0\x09\x70\x04\0\x18\ -\x02\0\0\x37\x02\0\0\xb5\x04\0\0\x0c\x80\x04\0\x20\x02\0\0\x37\x02\0\0\xc5\x04\ -\0\0\x09\xbc\x04\0\x50\x02\0\0\x37\x02\0\0\xe1\x04\0\0\x17\xd4\x04\0\x60\x02\0\ -\0\x37\x02\0\0\xfc\x04\0\0\x16\xdc\x04\0\x80\x02\0\0\x37\x02\0\0\xe1\x04\0\0\ -\x17\xd4\x04\0\x88\x02\0\0\x37\x02\0\0\x1a\x05\0\0\x0f\xe0\x04\0\xc0\x02\0\0\ -\x37\x02\0\0\x5d\x05\0\0\x0d\xe8\x04\0\xc8\x02\0\0\x37\x02\0\0\x70\x05\0\0\x24\ -\0\x05\0\xd0\x02\0\0\x37\x02\0\0\x70\x05\0\0\x20\0\x05\0\xe0\x02\0\0\x37\x02\0\ -\0\x9d\x05\0\0\x1b\xf8\x04\0\xe8\x02\0\0\x37\x02\0\0\x9d\x05\0\0\x16\xf8\x04\0\ -\xf0\x02\0\0\x37\x02\0\0\xbe\x05\0\0\x1b\xfc\x04\0\xf8\x02\0\0\x37\x02\0\0\xbe\ -\x05\0\0\x16\xfc\x04\0\0\x03\0\0\x37\x02\0\0\xdf\x05\0\0\x1a\x08\x05\0\x08\x03\ -\0\0\x37\x02\0\0\x70\x05\0\0\x1d\0\x05\0\x10\x03\0\0\x37\x02\0\0\x02\x06\0\0\ -\x18\x0c\x05\0\x18\x03\0\0\x37\x02\0\0\x02\x06\0\0\x1c\x0c\x05\0\x30\x03\0\0\ -\x37\x02\0\0\x22\x06\0\0\x15\x68\x05\0\x40\x03\0\0\x37\x02\0\0\x22\x06\0\0\x1a\ -\x68\x05\0\x58\x03\0\0\x37\x02\0\0\x56\x06\0\0\x0d\x6c\x05\0\x78\x03\0\0\x37\ -\x02\0\0\x80\x06\0\0\x1a\x70\x05\0\x88\x03\0\0\x37\x02\0\0\x9e\x06\0\0\x1b\x78\ -\x05\0\xa8\x03\0\0\x37\x02\0\0\x80\x06\0\0\x1a\x70\x05\0\xb0\x03\0\0\x37\x02\0\ -\0\xc2\x06\0\0\x13\x7c\x05\0\xe8\x03\0\0\x37\x02\0\0\x13\x07\0\0\x11\x84\x05\0\ -\xf0\x03\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x10\x04\0\0\x37\x02\0\0\x2a\x07\0\0\ -\x15\x28\x06\0\x18\x04\0\0\x37\x02\0\0\x2a\x07\0\0\x09\x28\x06\0\x20\x04\0\0\ -\x37\x02\0\0\0\0\0\0\0\0\0\0\x70\x04\0\0\x37\x02\0\0\x49\x07\0\0\x19\x2c\x06\0\ -\x80\x04\0\0\x37\x02\0\0\x49\x07\0\0\x20\x2c\x06\0\xa0\x04\0\0\x37\x02\0\0\0\0\ -\0\0\0\0\0\0\xf0\x04\0\0\x37\x02\0\0\x6b\x07\0\0\x17\x14\x05\0\0\x05\0\0\x37\ -\x02\0\0\x86\x07\0\0\x18\x1c\x05\0\x30\x05\0\0\x37\x02\0\0\x6b\x07\0\0\x17\x14\ -\x05\0\x48\x05\0\0\x37\x02\0\0\xa7\x07\0\0\x0f\x20\x05\0\x80\x05\0\0\x37\x02\0\ -\0\x5d\x05\0\0\x0d\x28\x05\0\x88\x05\0\0\x37\x02\0\0\xec\x07\0\0\x1d\x38\x05\0\ -\xc8\x05\0\0\x37\x02\0\0\x0f\x08\0\0\x1d\x3c\x05\0\x08\x06\0\0\x37\x02\0\0\x32\ -\x08\0\0\x1b\x44\x05\0\x10\x06\0\0\x37\x02\0\0\x55\x08\0\0\x05\x30\x02\0\x58\ -\x06\0\0\x37\x02\0\0\x6d\x08\0\0\x19\xb8\x02\0\xd0\x06\0\0\x37\x02\0\0\0\0\0\0\ -\0\0\0\0\xd8\x06\0\0\x37\x02\0\0\x93\x08\0\0\x0f\xc8\x02\0\x10\x07\0\0\x37\x02\ -\0\0\x5d\x05\0\0\x0d\xd0\x02\0\x20\x07\0\0\x37\x02\0\0\xd8\x08\0\0\x0d\xe0\x02\ -\0\x40\x07\0\0\x37\x02\0\0\x07\x09\0\0\x20\xe4\x02\0\x68\x07\0\0\x37\x02\0\0\ -\x33\x09\0\0\x13\xec\x02\0\xa8\x07\0\0\x37\x02\0\0\x13\x07\0\0\x11\xf4\x02\0\ -\xb0\x07\0\0\x37\x02\0\0\x7b\x09\0\0\x19\x04\x03\0\xb8\x07\0\0\x37\x02\0\0\x7b\ -\x09\0\0\x34\x04\x03\0\xe0\x07\0\0\x37\x02\0\0\xb1\x09\0\0\x15\x18\x03\0\xf0\ -\x07\0\0\x37\x02\0\0\xf2\x09\0\0\x17\x14\x03\0\x30\x08\0\0\x37\x02\0\0\x29\x0a\ -\0\0\x15\x24\x03\0\x38\x08\0\0\x37\x02\0\0\x44\x0a\0\0\x27\x34\x03\0\x70\x08\0\ -\0\x37\x02\0\0\x6f\x0a\0\0\x27\x50\x03\0\x80\x08\0\0\x37\x02\0\0\x9f\x0a\0\0\ -\x1c\xb4\x03\0\x88\x08\0\0\x37\x02\0\0\xdb\x0a\0\0\x20\xc0\x03\0\x98\x08\0\0\ -\x37\x02\0\0\xdb\x0a\0\0\x2f\xc0\x03\0\xa0\x08\0\0\x37\x02\0\0\xdb\x0a\0\0\x36\ -\xc0\x03\0\xa8\x08\0\0\x37\x02\0\0\xdb\x0a\0\0\x15\xc0\x03\0\x18\x09\0\0\x37\ -\x02\0\0\x17\x0b\0\0\x43\x64\x03\0\x38\x09\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x40\ -\x09\0\0\x37\x02\0\0\x17\x0b\0\0\x17\x64\x03\0\x80\x09\0\0\x37\x02\0\0\x29\x0a\ -\0\0\x15\x6c\x03\0\x88\x09\0\0\x37\x02\0\0\x67\x0b\0\0\x19\x7c\x03\0\x90\x09\0\ -\0\x37\x02\0\0\x67\x0b\0\0\x15\x7c\x03\0\x98\x09\0\0\x37\x02\0\0\x97\x0b\0\0\ -\x19\x84\x03\0\xa0\x09\0\0\x37\x02\0\0\xc7\x0b\0\0\x1b\x80\x03\0\xe8\x09\0\0\ -\x37\x02\0\0\x02\x0c\0\0\x19\x94\x03\0\xf0\x09\0\0\x37\x02\0\0\x21\x0c\0\0\x2b\ -\xa4\x03\0\x10\x0a\0\0\x37\x02\0\0\x9f\x0a\0\0\x1f\xb4\x03\0\x30\x0a\0\0\x37\ -\x02\0\0\x50\x0c\0\0\x21\xd4\x03\0\x40\x0a\0\0\x37\x02\0\0\x78\x0c\0\0\x20\xe4\ -\x03\0\x48\x0a\0\0\x37\x02\0\0\x78\x0c\0\0\x2c\xe4\x03\0\x60\x0a\0\0\x37\x02\0\ -\0\x78\x0c\0\0\x14\xe4\x03\0\x70\x0a\0\0\x37\x02\0\0\xa8\x0c\0\0\x20\xe0\x03\0\ -\x80\x0a\0\0\x37\x02\0\0\x55\x08\0\0\x05\x30\x02\0\xb0\x0a\0\0\x37\x02\0\0\xd0\ -\x0c\0\0\x38\xc0\x02\0\xd0\x0a\0\0\x37\x02\0\0\xd0\x0c\0\0\x05\xc0\x02\0\xe8\ -\x0a\0\0\x37\x02\0\0\x55\x08\0\0\x05\x30\x02\0\xf8\x0a\0\0\x37\x02\0\0\x0e\x0d\ -\0\0\x1c\xc4\x06\0\x08\x0b\0\0\x37\x02\0\0\x0e\x0d\0\0\x10\xc4\x06\0\x10\x0b\0\ -\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x60\x0b\0\0\x37\x02\0\0\x49\x07\0\0\x19\xc8\x06\ -\0\x68\x0b\0\0\x37\x02\0\0\x49\x07\0\0\x20\xc8\x06\0\xa0\x0b\0\0\x37\x02\0\0\ -\x34\x0d\0\0\x2d\0\x07\0\xb0\x0b\0\0\x37\x02\0\0\x34\x0d\0\0\x1d\0\x07\0\xb8\ -\x0b\0\0\x37\x02\0\0\x34\x0d\0\0\x2d\0\x07\0\xc8\x0b\0\0\x37\x02\0\0\x63\x0d\0\ -\0\x2d\xd4\x06\0\xf8\x0b\0\0\x37\x02\0\0\x63\x0d\0\0\x1d\xd4\x06\0\x08\x0c\0\0\ -\x37\x02\0\0\x63\x0d\0\0\x2d\xd4\x06\0\x18\x0c\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\ -\xe8\x0c\0\0\x37\x02\0\0\x92\x0d\0\0\x20\x68\x06\0\xf0\x0c\0\0\x37\x02\0\0\x92\ -\x0d\0\0\x27\x68\x06\0\x18\x0d\0\0\x37\x02\0\0\xbb\x0d\0\0\x27\xa4\x06\0\x20\ -\x0d\0\0\x37\x02\0\0\xbb\x0d\0\0\x14\xa4\x06\0\x28\x0d\0\0\x37\x02\0\0\x04\x0e\ -\0\0\x05\x98\x01\0\x38\x0d\0\0\x37\x02\0\0\x04\x0e\0\0\x05\x98\x01\0\x60\x0d\0\ -\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x70\x0d\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x80\x0d\ -\0\0\x37\x02\0\0\x92\x0d\0\0\x20\x44\x07\0\x88\x0d\0\0\x37\x02\0\0\x92\x0d\0\0\ -\x27\x44\x07\0\xc0\x0d\0\0\x37\x02\0\0\x34\x0d\0\0\x2d\x7c\x07\0\xd0\x0d\0\0\ -\x37\x02\0\0\x34\x0d\0\0\x1d\x7c\x07\0\xd8\x0d\0\0\x37\x02\0\0\x34\x0d\0\0\x2d\ -\x7c\x07\0\xe8\x0d\0\0\x37\x02\0\0\x63\x0d\0\0\x2d\x50\x07\0\x18\x0e\0\0\x37\ -\x02\0\0\x63\x0d\0\0\x1d\x50\x07\0\x28\x0e\0\0\x37\x02\0\0\x63\x0d\0\0\x2d\x50\ -\x07\0\x40\x0e\0\0\x37\x02\0\0\x41\x0e\0\0\x1a\xa0\x05\0\x50\x0e\0\0\x37\x02\0\ -\0\x5f\x0e\0\0\x1b\xa8\x05\0\x60\x0e\0\0\x37\x02\0\0\x41\x0e\0\0\x1a\xa0\x05\0\ -\x68\x0e\0\0\x37\x02\0\0\x83\x0e\0\0\x13\xac\x05\0\xa0\x0e\0\0\x37\x02\0\0\x13\ -\x07\0\0\x11\xb4\x05\0\xb0\x0e\0\0\x37\x02\0\0\x55\x08\0\0\x05\x30\x02\0\xc0\ -\x0e\0\0\x37\x02\0\0\xd4\x0e\0\0\x27\xc8\x07\0\xd0\x0e\0\0\x37\x02\0\0\xd4\x0e\ -\0\0\x14\xc8\x07\0\xf0\x0e\0\0\x37\x02\0\0\x63\x0d\0\0\x2d\xcc\x07\0\0\x0f\0\0\ -\x37\x02\0\0\x63\x0d\0\0\x1d\xcc\x07\0\x08\x0f\0\0\x37\x02\0\0\x63\x0d\0\0\x2d\ -\xcc\x07\0\x30\x0f\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x80\x0f\0\0\x37\x02\0\0\x34\ -\x0d\0\0\x1d\xf8\x07\0\x88\x0f\0\0\x37\x02\0\0\x34\x0d\0\0\x2d\xf8\x07\0\x98\ -\x0f\0\0\x37\x02\0\0\x04\x0e\0\0\x05\x98\x01\0\xf0\x0f\0\0\x37\x02\0\0\x04\x0e\ -\0\0\x05\x98\x01\0\x30\x10\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x48\x10\0\0\x37\x02\ -\0\0\x1d\x0f\0\0\x05\xd0\x01\0\x50\x10\0\0\x37\x02\0\0\x5f\x0f\0\0\x23\xc4\x01\ -\0\x68\x10\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x70\x10\0\0\x37\x02\0\0\x93\x0f\0\0\ -\x1b\xd4\x01\0\x90\x10\0\0\x37\x02\0\0\xba\x0f\0\0\x11\xe8\x01\0\xa8\x10\0\0\ -\x37\x02\0\0\xe3\x0f\0\0\x19\xd8\x01\0\xc0\x10\0\0\x37\x02\0\0\x11\x10\0\0\x27\ -\xfc\x01\0\xc8\x10\0\0\x37\x02\0\0\x11\x10\0\0\x46\xfc\x01\0\xd8\x10\0\0\x37\ -\x02\0\0\x11\x10\0\0\x2d\xfc\x01\0\xe0\x10\0\0\x37\x02\0\0\xba\x0f\0\0\x11\xe8\ -\x01\0\x08\x11\0\0\x37\x02\0\0\x11\x10\0\0\x46\xfc\x01\0\x20\x11\0\0\x37\x02\0\ -\0\x11\x10\0\0\x27\xfc\x01\0\x28\x11\0\0\x37\x02\0\0\x11\x10\0\0\x2d\xfc\x01\0\ -\x30\x11\0\0\x37\x02\0\0\xba\x0f\0\0\x11\xe8\x01\0\x58\x11\0\0\x37\x02\0\0\x11\ -\x10\0\0\x27\xfc\x01\0\x60\x11\0\0\x37\x02\0\0\x11\x10\0\0\x46\xfc\x01\0\x78\ -\x11\0\0\x37\x02\0\0\x11\x10\0\0\x2d\xfc\x01\0\x80\x11\0\0\x37\x02\0\0\xba\x0f\ -\0\0\x11\xe8\x01\0\xa8\x11\0\0\x37\x02\0\0\x11\x10\0\0\x27\xfc\x01\0\xb0\x11\0\ -\0\x37\x02\0\0\x11\x10\0\0\x46\xfc\x01\0\xc8\x11\0\0\x37\x02\0\0\x11\x10\0\0\ -\x2d\xfc\x01\0\xd0\x11\0\0\x37\x02\0\0\xba\x0f\0\0\x11\xe8\x01\0\xf8\x11\0\0\ -\x37\x02\0\0\x11\x10\0\0\x46\xfc\x01\0\x10\x12\0\0\x37\x02\0\0\x11\x10\0\0\x27\ -\xfc\x01\0\x18\x12\0\0\x37\x02\0\0\x11\x10\0\0\x2d\xfc\x01\0\x20\x12\0\0\x37\ -\x02\0\0\xba\x0f\0\0\x11\xe8\x01\0\x48\x12\0\0\x37\x02\0\0\x11\x10\0\0\x46\xfc\ -\x01\0\x60\x12\0\0\x37\x02\0\0\x11\x10\0\0\x27\xfc\x01\0\x68\x12\0\0\x37\x02\0\ -\0\x11\x10\0\0\x2d\xfc\x01\0\x70\x12\0\0\x37\x02\0\0\xba\x0f\0\0\x11\xe8\x01\0\ -\x98\x12\0\0\x37\x02\0\0\x11\x10\0\0\x46\xfc\x01\0\xb0\x12\0\0\x37\x02\0\0\x11\ -\x10\0\0\x27\xfc\x01\0\xb8\x12\0\0\x37\x02\0\0\x11\x10\0\0\x2d\xfc\x01\0\xc0\ -\x12\0\0\x37\x02\0\0\xba\x0f\0\0\x11\xe8\x01\0\xe0\x12\0\0\x37\x02\0\0\x11\x10\ -\0\0\x46\xfc\x01\0\xe8\x12\0\0\x37\x02\0\0\x11\x10\0\0\x27\xfc\x01\0\xf0\x12\0\ -\0\x37\x02\0\0\x11\x10\0\0\x2d\xfc\x01\0\xf8\x12\0\0\x37\x02\0\0\x1d\x0f\0\0\ -\x3d\xd0\x01\0\x08\x13\0\0\x37\x02\0\0\x1d\x0f\0\0\x05\xd0\x01\0\x18\x13\0\0\ -\x37\x02\0\0\x5d\x10\0\0\x0d\x98\x08\0\x30\x13\0\0\x37\x02\0\0\x5d\x10\0\0\x0d\ -\x98\x08\0\x38\x13\0\0\x37\x02\0\0\x71\x10\0\0\x2e\x9c\x08\0\x58\x13\0\0\x37\ -\x02\0\0\x71\x10\0\0\x24\x9c\x08\0\x70\x13\0\0\x37\x02\0\0\x71\x10\0\0\x13\x9c\ -\x08\0\x80\x13\0\0\x37\x02\0\0\x71\x10\0\0\x2e\x9c\x08\0\x88\x13\0\0\x37\x02\0\ -\0\xb0\x10\0\0\x15\xa8\x08\0\xa0\x13\0\0\x37\x02\0\0\xf8\x10\0\0\x11\xb4\x08\0\ -\xa8\x13\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\xc8\x13\0\0\x37\x02\0\0\x11\x11\0\0\ -\x01\xd8\x08\0\xd0\x13\0\0\x37\x02\0\0\x13\x11\0\0\x18\xb8\x08\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\xde\0\0\0\0\0\x03\0\xc8\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x7a\x01\0\0\0\ -\0\x03\0\xb8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\0\0\0\0\0\x03\0\xa8\x13\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\xc7\0\0\0\0\0\x03\0\xd0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\x2c\x02\0\0\0\0\x03\0\x20\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf7\0\0\0\0\0\x03\0\ -\xe8\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1c\x02\0\0\0\0\x03\0\x10\x04\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\x28\x01\0\0\0\0\x03\0\xe0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf3\ -\x01\0\0\0\0\x03\0\x30\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xeb\x01\0\0\0\0\x03\0\ -\x38\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x44\x02\0\0\0\0\x03\0\xf0\x03\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\xe3\x01\0\0\0\0\x03\0\xf8\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x18\ -\x01\0\0\0\0\x03\0\xe8\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x30\x01\0\0\0\0\x03\0\ -\xa0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa9\x01\0\0\0\0\x03\0\x40\x10\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\x51\x01\0\0\0\0\x03\0\x78\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x5c\ -\x02\0\0\0\0\x03\0\xb0\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\x02\0\0\0\0\x03\0\ -\x50\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc2\x01\0\0\0\0\x03\0\xc0\x06\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\x69\x01\0\0\0\0\x03\0\x20\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\ -\x01\0\0\0\0\x03\0\x60\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x38\x01\0\0\0\0\x03\0\ -\x30\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x20\x01\0\0\0\0\x03\0\x40\x0a\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\xba\x01\0\0\0\0\x03\0\xe0\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa1\ -\x01\0\0\0\0\x03\0\x48\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\x01\0\0\0\0\x03\0\ -\x18\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xfb\x01\0\0\0\0\x03\0\x80\x08\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\x99\x01\0\0\0\0\x03\0\xf8\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x59\ -\x01\0\0\0\0\x03\0\x50\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x54\x02\0\0\0\0\x03\0\ -\x08\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xef\0\0\0\0\0\x03\0\xe8\x0a\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\x4c\x02\0\0\0\0\x03\0\xb0\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x24\ -\x02\0\0\0\0\x03\0\xd8\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x89\x01\0\0\0\0\x03\0\ -\x80\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\x01\0\0\0\0\x03\0\xb0\x0b\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\xd6\0\0\0\0\0\x03\0\xc8\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x14\ -\x02\0\0\0\0\x03\0\xf8\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb2\x01\0\0\0\0\x03\0\ -\x18\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xdb\x01\0\0\0\0\x03\0\x10\x0c\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\x3c\x02\0\0\0\0\x03\0\x18\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x91\ -\x01\0\0\0\0\x03\0\x60\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81\x01\0\0\0\0\x03\0\ -\xc0\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe7\0\0\0\0\0\x03\0\xd0\x0d\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\x34\x02\0\0\0\0\x03\0\xe8\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd3\ -\x01\0\0\0\0\x03\0\x18\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\x01\0\0\0\0\x03\0\0\ -\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xce\0\0\0\0\0\x03\0\x18\x0f\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\x0b\x02\0\0\0\0\x03\0\xf0\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xca\x01\0\ -\0\0\0\x03\0\x30\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x71\x01\0\0\0\0\x03\0\x60\x10\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x48\x01\0\0\0\0\x03\0\x18\x13\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\x64\x02\0\0\0\0\x03\0\xd0\x13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x4e\0\0\0\ -\x12\0\x03\0\0\0\0\0\0\0\0\0\xe0\x13\0\0\0\0\0\0\x33\0\0\0\x11\0\x05\0\0\0\0\0\ -\0\0\0\0\x20\0\0\0\0\0\0\0\x01\0\0\0\x11\0\x05\0\x20\0\0\0\0\0\0\0\x20\0\0\0\0\ -\0\0\0\x90\0\0\0\x11\0\x05\0\x40\0\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\x87\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\x37\0\0\ -\0\x50\0\0\0\0\0\0\0\x01\0\0\0\x38\0\0\0\x88\x13\0\0\0\0\0\0\x01\0\0\0\x39\0\0\ -\0\xd8\x04\0\0\0\0\0\0\x04\0\0\0\x37\0\0\0\xe4\x04\0\0\0\0\0\0\x04\0\0\0\x38\0\ -\0\0\xf0\x04\0\0\0\0\0\0\x04\0\0\0\x39\0\0\0\x08\x05\0\0\0\0\0\0\x04\0\0\0\x3a\ -\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\x40\x41\x42\x43\x44\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\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\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\x31\x30\x39\0\x4c\ -\x42\x42\x30\x5f\x38\x38\0\x4c\x42\x42\x30\x5f\x34\x38\0\x4c\x42\x42\x30\x5f\ -\x31\x38\0\x4c\x42\x42\x30\x5f\x31\x30\x38\0\x4c\x42\x42\x30\x5f\x39\x37\0\x4c\ -\x42\x42\x30\x5f\x37\x37\0\x4c\x42\x42\x30\x5f\x36\x37\0\x4c\x42\x42\x30\x5f\ -\x34\x37\0\x4c\x42\x42\x30\x5f\x31\x37\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\x31\ -\x30\x36\0\x4c\x42\x42\x30\x5f\x35\x35\0\x4c\x42\x42\x30\x5f\x34\x35\0\x4c\x42\ -\x42\x30\x5f\x33\x35\0\x4c\x42\x42\x30\x5f\x32\x35\0\x4c\x42\x42\x30\x5f\x31\ -\x30\x35\0\x4c\x42\x42\x30\x5f\x34\0\x4c\x42\x42\x30\x5f\x39\x34\0\x4c\x42\x42\ -\x30\x5f\x38\x34\0\x4c\x42\x42\x30\x5f\x35\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\x38\x33\0\x4c\x42\x42\x30\x5f\x35\x33\0\x4c\x42\x42\x30\x5f\x32\x33\0\ -\x4c\x42\x42\x30\x5f\x31\x30\x33\0\x4c\x42\x42\x30\x5f\x39\x32\0\x4c\x42\x42\ -\x30\x5f\x38\x32\0\x4c\x42\x42\x30\x5f\x37\x32\0\x4c\x42\x42\x30\x5f\x36\x32\0\ -\x4c\x42\x42\x30\x5f\x35\x32\0\x4c\x42\x42\x30\x5f\x34\x32\0\x4c\x42\x42\x30\ -\x5f\x32\x32\0\x4c\x42\x42\x30\x5f\x31\x30\x32\0\x4c\x42\x42\x30\x5f\x38\x31\0\ -\x4c\x42\x42\x30\x5f\x36\x31\0\x4c\x42\x42\x30\x5f\x35\x31\0\x4c\x42\x42\x30\ -\x5f\x31\x31\0\x4c\x42\x42\x30\x5f\x39\x30\0\x4c\x42\x42\x30\x5f\x37\x30\0\x4c\ -\x42\x42\x30\x5f\x36\x30\0\x4c\x42\x42\x30\x5f\x35\x30\0\x4c\x42\x42\x30\x5f\ -\x34\x30\0\x4c\x42\x42\x30\x5f\x32\x30\0\x4c\x42\x42\x30\x5f\x31\x31\x30\0\0\0\ +\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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xae\0\0\0\x03\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x25\x4a\0\0\0\0\0\0\x6d\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\x68\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\xe0\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\x64\0\0\0\x09\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\x3d\0\0\ -\0\0\0\0\x30\0\0\0\0\0\0\0\x0c\0\0\0\x03\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\ -\0\0\x2d\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x20\x14\0\0\0\0\0\0\ -\x60\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\x88\0\0\0\ -\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x80\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\xc2\0\0\0\x01\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x88\x14\0\0\0\0\0\0\x8d\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\xbe\0\0\0\x09\0\0\0\x40\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\x70\x3d\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\x0c\0\0\0\x07\0\0\0\x08\ -\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x24\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\x18\x2b\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\xb0\ -\x3d\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\x79\0\0\0\x03\x4c\xff\x6f\0\0\0\x80\0\0\0\0\0\0\0\0\0\0\0\0\x20\ -\x4a\0\0\0\0\0\0\x05\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\xb6\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb8\x37\0\0\0\0\0\0\ -\x88\x05\0\0\0\0\0\0\x01\0\0\0\x36\0\0\0\x08\0\0\0\0\0\0\0\x18\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 diff --git a/ebpf/trace-events b/ebpf/trace-events index b3ad1a35f2e..bf3d9b64510 100644 --- a/ebpf/trace-events +++ b/ebpf/trace-events @@ -1,4 +1,10 @@ # 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/event-loop-base.c b/event-loop-base.c index d5be4dc6fcf..8ca143bea43 100644 --- a/event-loop-base.c +++ b/event-loop-base.c @@ -15,7 +15,7 @@ #include "qom/object_interfaces.h" #include "qapi/error.h" #include "block/thread-pool.h" -#include "sysemu/event-loop-base.h" +#include "system/event-loop-base.h" typedef struct { const char *name; @@ -73,8 +73,6 @@ static void event_loop_base_set_param(Object *obj, Visitor *v, if (bc->update_params) { bc->update_params(base, errp); } - - return; } static void event_loop_base_complete(UserCreatable *uc, Error **errp) @@ -99,7 +97,8 @@ static bool event_loop_base_can_be_deleted(UserCreatable *uc) return true; } -static void event_loop_base_class_init(ObjectClass *klass, void *class_data) +static void event_loop_base_class_init(ObjectClass *klass, + const void *class_data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass); ucc->complete = event_loop_base_complete; @@ -127,7 +126,7 @@ static const TypeInfo event_loop_base_info = { .class_size = sizeof(EventLoopBaseClass), .class_init = event_loop_base_class_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/fpu/meson.build b/fpu/meson.build index 1a9992ded56..646c76f0c69 100644 --- a/fpu/meson.build +++ b/fpu/meson.build @@ -1 +1 @@ -specific_ss.add(when: 'CONFIG_TCG', if_true: files('softfloat.c')) +common_ss.add(when: 'CONFIG_TCG', if_true: files('softfloat.c')) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 527e15e6abb..171bfd06e3a 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -39,65 +39,152 @@ static void partsN(return_nan)(FloatPartsN *a, float_status *s) static FloatPartsN *partsN(pick_nan)(FloatPartsN *a, FloatPartsN *b, float_status *s) { + bool have_snan = false; + FloatPartsN *ret; + int cmp; + if (is_snan(a->cls) || is_snan(b->cls)) { float_raise(float_flag_invalid | float_flag_invalid_snan, s); + have_snan = true; } if (s->default_nan_mode) { parts_default_nan(a, s); - } else { - int cmp = frac_cmp(a, b); - if (cmp == 0) { - cmp = a->sign < b->sign; - } + return a; + } - if (pickNaN(a->cls, b->cls, cmp > 0, s)) { - a = b; + switch (s->float_2nan_prop_rule) { + case float_2nan_prop_s_ab: + if (have_snan) { + ret = is_snan(a->cls) ? a : b; + break; + } + /* fall through */ + case float_2nan_prop_ab: + ret = is_nan(a->cls) ? a : b; + break; + case float_2nan_prop_s_ba: + if (have_snan) { + ret = is_snan(b->cls) ? b : a; + break; } + /* fall through */ + case float_2nan_prop_ba: + ret = is_nan(b->cls) ? b : a; + 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)) { - parts_silence_nan(a, s); + if (!is_snan(b->cls)) { + ret = is_qnan(b->cls) ? b : a; + break; + } + } else if (is_qnan(a->cls)) { + if (is_snan(b->cls) || !is_qnan(b->cls)) { + ret = a; + break; + } + } else { + ret = b; + break; + } + cmp = frac_cmp(a, b); + if (cmp == 0) { + cmp = a->sign < b->sign; } + ret = cmp > 0 ? a : b; + break; + default: + g_assert_not_reached(); } - return a; + + if (is_snan(ret->cls)) { + parts_silence_nan(ret, s); + } + return ret; } static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, FloatPartsN *c, float_status *s, int ab_mask, int abc_mask) { - int which; + bool infzero = (ab_mask == float_cmask_infzero); + bool have_snan = (abc_mask & float_cmask_snan); + FloatPartsN *ret; - if (unlikely(abc_mask & float_cmask_snan)) { + if (unlikely(have_snan)) { float_raise(float_flag_invalid | float_flag_invalid_snan, s); } - which = pickNaNMulAdd(a->cls, b->cls, c->cls, - ab_mask == float_cmask_infzero, s); + if (infzero && + !(s->float_infzeronan_rule & float_infzeronan_suppress_invalid)) { + /* This is (0 * inf) + NaN or (inf * 0) + NaN */ + float_raise(float_flag_invalid | float_flag_invalid_imz, s); + } - if (s->default_nan_mode || which == 3) { + if (s->default_nan_mode) { /* - * Note that this check is after pickNaNMulAdd so that function - * has an opportunity to set the Invalid flag for infzero. + * 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. */ - parts_default_nan(a, s); - return a; + goto default_nan; + } else if (infzero) { + /* + * Inf * 0 + NaN -- some implementations return the + * default NaN here, and some return the input NaN. + */ + switch (s->float_infzeronan_rule & ~float_infzeronan_suppress_invalid) { + case float_infzeronan_dnan_never: + break; + case float_infzeronan_dnan_always: + goto default_nan; + case float_infzeronan_dnan_if_qnan: + if (is_qnan(c->cls)) { + goto default_nan; + } + break; + default: + g_assert_not_reached(); + } + ret = c; + } else { + FloatPartsN *val[R_3NAN_1ST_MASK + 1] = { a, b, c }; + Float3NaNPropRule rule = s->float_3nan_prop_rule; + + assert(rule != float_3nan_prop_none); + if (have_snan && (rule & R_3NAN_SNAN_MASK)) { + /* We have at least one SNaN input and should prefer it */ + do { + ret = val[rule & R_3NAN_1ST_MASK]; + rule >>= R_3NAN_1ST_LENGTH; + } while (!is_snan(ret->cls)); + } else { + do { + ret = val[rule & R_3NAN_1ST_MASK]; + rule >>= R_3NAN_1ST_LENGTH; + } while (!is_nan(ret->cls)); + } } - switch (which) { - case 0: - break; - case 1: - a = b; - break; - case 2: - a = c; - break; - default: - g_assert_not_reached(); - } - if (is_snan(a->cls)) { - parts_silence_nan(a, s); + if (is_snan(ret->cls)) { + parts_silence_nan(ret, s); } + return ret; + + default_nan: + parts_default_nan(a, s); return a; } @@ -108,17 +195,37 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, static void partsN(canonicalize)(FloatPartsN *p, float_status *status, const FloatFmt *fmt) { + /* + * It's target-dependent how to handle the case of exponent 0 + * and Integer bit set. Intel calls these "pseudodenormals", + * and treats them as if the integer bit was 0, and never + * produces them on output. This is the default behaviour for QEMU. + * For m68k, the integer bit is considered validly part of the + * input value when the exponent is 0, and may be 0 or 1, + * giving extra range. They may also be generated as outputs. + * (The m68k manual actually calls these values part of the + * normalized number range, not the denormalized number range, + * but that distinction is not important for us, because + * m68k doesn't care about the input_denormal_used status flag.) + * floatx80_pseudo_denormal_valid selects the m68k behaviour, + * which changes both how we canonicalize such a value and + * how we uncanonicalize results. + */ + bool has_pseudo_denormals = fmt->has_explicit_bit && + (status->floatx80_behaviour & floatx80_pseudo_denormal_valid); + if (unlikely(p->exp == 0)) { if (likely(frac_eqz(p))) { p->cls = float_class_zero; } else if (status->flush_inputs_to_zero) { - float_raise(float_flag_input_denormal, status); + float_raise(float_flag_input_denormal_flushed, status); p->cls = float_class_zero; frac_clear(p); } else { int shift = frac_normalize(p); - p->cls = float_class_normal; - p->exp = fmt->frac_shift - fmt->exp_bias - shift + 1; + p->cls = float_class_denormal; + p->exp = fmt->frac_shift - fmt->exp_bias + - shift + !has_pseudo_denormals; } } else if (likely(p->exp < fmt->exp_max) || fmt->arm_althp) { p->cls = float_class_normal; @@ -154,6 +261,9 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, int exp, flags = 0; switch (s->float_rounding_mode) { + case float_round_nearest_even_max: + overflow_norm = true; + /* fall through */ case float_round_nearest_even: if (N > 64 && frac_lsb == 0) { inc = ((p->frac_hi & 1) || (p->frac_lo & round_mask) != frac_lsbm1 @@ -243,20 +353,23 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, p->frac_lo &= ~round_mask; } frac_shr(p, frac_shift); - } else if (s->flush_to_zero) { - flags |= float_flag_output_denormal; + } else if (s->flush_to_zero && + s->ftz_detection == float_ftz_before_rounding) { + flags |= float_flag_output_denormal_flushed; p->cls = float_class_zero; exp = 0; frac_clear(p); } else { bool is_tiny = s->tininess_before_rounding || exp < 0; + bool has_pseudo_denormals = fmt->has_explicit_bit && + (s->floatx80_behaviour & floatx80_pseudo_denormal_valid); if (!is_tiny) { FloatPartsN discard; is_tiny = !frac_addi(&discard, p, inc); } - frac_shrjam(p, 1 - exp); + frac_shrjam(p, !has_pseudo_denormals - exp); if (p->frac_lo & round_mask) { /* Need to recompute round-to-even/round-to-odd. */ @@ -287,14 +400,22 @@ 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) && !has_pseudo_denormals; frac_shr(p, frac_shift); - if (is_tiny && (flags & float_flag_inexact)) { - flags |= float_flag_underflow; - } - if (exp == 0 && frac_eqz(p)) { - p->cls = float_class_zero; + if (is_tiny) { + if (s->flush_to_zero) { + assert(s->ftz_detection == float_ftz_after_rounding); + flags |= float_flag_output_denormal_flushed; + p->cls = float_class_zero; + exp = 0; + frac_clear(p); + } else if (flags & float_flag_inexact) { + flags |= float_flag_underflow; + } + if (exp == 0 && frac_eqz(p)) { + p->cls = float_class_zero; + } } } p->exp = exp; @@ -304,7 +425,7 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, static void partsN(uncanon)(FloatPartsN *p, float_status *s, const FloatFmt *fmt) { - if (likely(p->cls == float_class_normal)) { + if (likely(is_anynorm(p->cls))) { parts_uncanon_normal(p, s, fmt); } else { switch (p->cls) { @@ -342,9 +463,18 @@ static FloatPartsN *partsN(addsub)(FloatPartsN *a, FloatPartsN *b, bool b_sign = b->sign ^ subtract; int ab_mask = float_cmask(a->cls) | float_cmask(b->cls); + /* + * For addition and subtraction, we will consume an + * input denormal unless the other input is a NaN. + */ + if ((ab_mask & (float_cmask_denormal | float_cmask_anynan)) == + float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } + if (a->sign != b_sign) { /* Subtraction */ - if (likely(ab_mask == float_cmask_normal)) { + if (likely(cmask_is_only_normals(ab_mask))) { if (parts_sub_normal(a, b)) { return a; } @@ -377,7 +507,7 @@ static FloatPartsN *partsN(addsub)(FloatPartsN *a, FloatPartsN *b, } } else { /* Addition */ - if (likely(ab_mask == float_cmask_normal)) { + if (likely(cmask_is_only_normals(ab_mask))) { parts_add_normal(a, b); return a; } @@ -397,12 +527,12 @@ static FloatPartsN *partsN(addsub)(FloatPartsN *a, FloatPartsN *b, } if (b->cls == float_class_zero) { - g_assert(a->cls == float_class_normal); + g_assert(is_anynorm(a->cls)); return a; } g_assert(a->cls == float_class_zero); - g_assert(b->cls == float_class_normal); + g_assert(is_anynorm(b->cls)); return_b: b->sign = b_sign; return b; @@ -422,9 +552,13 @@ static FloatPartsN *partsN(mul)(FloatPartsN *a, FloatPartsN *b, int ab_mask = float_cmask(a->cls) | float_cmask(b->cls); bool sign = a->sign ^ b->sign; - if (likely(ab_mask == float_cmask_normal)) { + if (likely(cmask_is_only_normals(ab_mask))) { FloatPartsW tmp; + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } + frac_mulw(&tmp, a, b); frac_truncjam(a, &tmp); @@ -450,6 +584,10 @@ static FloatPartsN *partsN(mul)(FloatPartsN *a, FloatPartsN *b, } /* Multiply by 0 or Inf */ + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } + if (ab_mask & float_cmask_inf) { a->cls = float_class_inf; a->sign = sign; @@ -475,8 +613,9 @@ static FloatPartsN *partsN(mul)(FloatPartsN *a, FloatPartsN *b, * Requires A and C extracted into a double-sized structure to provide the * extra space for the widening multiply. */ -static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b, - FloatPartsN *c, int flags, float_status *s) +static FloatPartsN *partsN(muladd_scalbn)(FloatPartsN *a, FloatPartsN *b, + FloatPartsN *c, int scale, + int flags, float_status *s) { int ab_mask, abc_mask; FloatPartsW p_widen, c_widen; @@ -504,7 +643,7 @@ static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b, a->sign ^= 1; } - if (unlikely(ab_mask != float_cmask_normal)) { + if (unlikely(!cmask_is_only_normals(ab_mask))) { if (unlikely(ab_mask == float_cmask_infzero)) { float_raise(float_flag_invalid | float_flag_invalid_imz, s); goto d_nan; @@ -519,12 +658,14 @@ static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b, } g_assert(ab_mask & float_cmask_zero); - if (c->cls == float_class_normal) { + if (is_anynorm(c->cls)) { *a = *c; goto return_normal; } if (c->cls == float_class_zero) { - if (a->sign != c->sign) { + if (flags & float_muladd_suppress_add_product_zero) { + a->sign = c->sign; + } else if (a->sign != c->sign) { goto return_sub_zero; } goto return_zero; @@ -565,13 +706,21 @@ static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b, a->exp = p_widen.exp; return_normal: - if (flags & float_muladd_halve_result) { - a->exp -= 1; - } + a->exp += scale; finish_sign: if (flags & float_muladd_negate_result) { a->sign ^= 1; } + + /* + * All result types except for "return the default NaN + * because this is an Invalid Operation" go through here; + * this matches the set of cases where we consumed a + * denormal input. + */ + if (abc_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } return a; return_sub_zero: @@ -600,7 +749,10 @@ static FloatPartsN *partsN(div)(FloatPartsN *a, FloatPartsN *b, int ab_mask = float_cmask(a->cls) | float_cmask(b->cls); bool sign = a->sign ^ b->sign; - if (likely(ab_mask == float_cmask_normal)) { + if (likely(cmask_is_only_normals(ab_mask))) { + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } a->sign = sign; a->exp -= b->exp + frac_div(a, b); return a; @@ -621,6 +773,10 @@ static FloatPartsN *partsN(div)(FloatPartsN *a, FloatPartsN *b, return parts_pick_nan(a, b, s); } + if ((ab_mask & float_cmask_denormal) && b->cls != float_class_zero) { + float_raise(float_flag_input_denormal_used, s); + } + a->sign = sign; /* Inf / X */ @@ -658,7 +814,10 @@ static FloatPartsN *partsN(modrem)(FloatPartsN *a, FloatPartsN *b, { int ab_mask = float_cmask(a->cls) | float_cmask(b->cls); - if (likely(ab_mask == float_cmask_normal)) { + if (likely(cmask_is_only_normals(ab_mask))) { + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } frac_modrem(a, b, mod_quot); return a; } @@ -679,6 +838,10 @@ static FloatPartsN *partsN(modrem)(FloatPartsN *a, FloatPartsN *b, return a; } + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } + /* N % Inf; 0 % N */ g_assert(b->cls == float_class_inf || a->cls == float_class_zero); return a; @@ -708,6 +871,12 @@ static void partsN(sqrt)(FloatPartsN *a, float_status *status, if (unlikely(a->cls != float_class_normal)) { switch (a->cls) { + case float_class_denormal: + if (!a->sign) { + /* -ve denormal will be InvalidOperation */ + float_raise(float_flag_input_denormal_used, status); + } + break; case float_class_snan: case float_class_qnan: parts_return_nan(a, status); @@ -1038,6 +1207,7 @@ static void partsN(round_to_int)(FloatPartsN *a, FloatRoundMode rmode, case float_class_inf: break; case float_class_normal: + case float_class_denormal: if (parts_round_to_int_normal(a, rmode, scale, fmt->frac_size)) { float_raise(float_flag_inexact, s); } @@ -1082,6 +1252,7 @@ static int64_t partsN(float_to_sint)(FloatPartsN *p, FloatRoundMode rmode, return 0; case float_class_normal: + case float_class_denormal: /* TODO: N - 2 is frac_size for rounding; could use input fmt. */ if (parts_round_to_int_normal(p, rmode, scale, N - 2)) { flags = float_flag_inexact; @@ -1149,6 +1320,7 @@ static uint64_t partsN(float_to_uint)(FloatPartsN *p, FloatRoundMode rmode, return 0; case float_class_normal: + case float_class_denormal: /* TODO: N - 2 is frac_size for rounding; could use input fmt. */ if (parts_round_to_int_normal(p, rmode, scale, N - 2)) { flags = float_flag_inexact; @@ -1212,6 +1384,7 @@ static int64_t partsN(float_to_sint_modulo)(FloatPartsN *p, return 0; case float_class_normal: + case float_class_denormal: /* 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; @@ -1333,6 +1506,9 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b, if ((flags & (minmax_isnum | minmax_isnumber)) && !(ab_mask & float_cmask_snan) && (ab_mask & ~float_cmask_qnan)) { + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } return is_nan(a->cls) ? b : a; } @@ -1357,12 +1533,17 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b, return parts_pick_nan(a, b, s); } + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } + a_exp = a->exp; b_exp = b->exp; - if (unlikely(ab_mask != float_cmask_normal)) { + if (unlikely(!cmask_is_only_normals(ab_mask))) { switch (a->cls) { case float_class_normal: + case float_class_denormal: break; case float_class_inf: a_exp = INT16_MAX; @@ -1372,10 +1553,10 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b, break; default: g_assert_not_reached(); - break; } switch (b->cls) { case float_class_normal: + case float_class_denormal: break; case float_class_inf: b_exp = INT16_MAX; @@ -1385,7 +1566,6 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b, break; default: g_assert_not_reached(); - break; } } @@ -1423,9 +1603,13 @@ static FloatRelation partsN(compare)(FloatPartsN *a, FloatPartsN *b, { int ab_mask = float_cmask(a->cls) | float_cmask(b->cls); - if (likely(ab_mask == float_cmask_normal)) { + if (likely(cmask_is_only_normals(ab_mask))) { FloatRelation cmp; + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } + if (a->sign != b->sign) { goto a_sign; } @@ -1451,6 +1635,10 @@ static FloatRelation partsN(compare)(FloatPartsN *a, FloatPartsN *b, return float_relation_unordered; } + if (ab_mask & float_cmask_denormal) { + float_raise(float_flag_input_denormal_used, s); + } + if (ab_mask & float_cmask_zero) { if (ab_mask == float_cmask_zero) { return float_relation_equal; @@ -1490,6 +1678,9 @@ static void partsN(scalbn)(FloatPartsN *a, int n, float_status *s) case float_class_zero: case float_class_inf: break; + case float_class_denormal: + float_raise(float_flag_input_denormal_used, s); + /* fall through */ case float_class_normal: a->exp += MIN(MAX(n, -0x10000), 0x10000); break; @@ -1509,6 +1700,12 @@ static void partsN(log2)(FloatPartsN *a, float_status *s, const FloatFmt *fmt) if (unlikely(a->cls != float_class_normal)) { switch (a->cls) { + case float_class_denormal: + if (!a->sign) { + /* -ve denormal will be InvalidOperation */ + float_raise(float_flag_input_denormal_used, s); + } + break; case float_class_snan: case float_class_qnan: parts_return_nan(a, s); @@ -1525,9 +1722,8 @@ static void partsN(log2)(FloatPartsN *a, float_status *s, const FloatFmt *fmt) } return; default: - break; + g_assert_not_reached(); } - g_assert_not_reached(); } if (unlikely(a->sign)) { goto d_nan; diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 1610472cfc4..ba4fa08b7be 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -85,11 +85,7 @@ this code that are retained. */ static inline bool no_signaling_nans(float_status *status) { -#if defined(TARGET_XTENSA) return status->no_signaling_nans; -#else - return false; -#endif } /* Define how the architecture discriminates signaling NaNs. @@ -97,17 +93,10 @@ static inline bool no_signaling_nans(float_status *status) * In IEEE 754-1985 this was implementation defined, but in IEEE 754-2008 * the msb must be zero. MIPS is (so far) unique in supporting both the * 2008 revision and backward compatibility with their original choice. - * Thus for MIPS we must make the choice at runtime. */ static inline bool snan_bit_is_one(float_status *status) { -#if defined(TARGET_MIPS) return status->snan_bit_is_one; -#elif defined(TARGET_HPPA) || defined(TARGET_SH4) - return 1; -#else - return 0; -#endif } /*---------------------------------------------------------------------------- @@ -133,35 +122,17 @@ static void parts64_default_nan(FloatParts64 *p, float_status *status) { bool sign = 0; uint64_t frac; + uint8_t dnan_pattern = status->default_nan_pattern; -#if defined(TARGET_SPARC) || defined(TARGET_M68K) - /* !snan_bit_is_one, set all bits */ - frac = (1ULL << DECOMPOSED_BINARY_POINT) - 1; -#elif defined(TARGET_I386) || defined(TARGET_X86_64) \ - || defined(TARGET_MICROBLAZE) - /* !snan_bit_is_one, set sign and msb */ - frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1); - sign = 1; -#elif defined(TARGET_HPPA) - /* snan_bit_is_one, set msb-1. */ - frac = 1ULL << (DECOMPOSED_BINARY_POINT - 2); -#elif defined(TARGET_HEXAGON) - sign = 1; - frac = ~0ULL; -#else + assert(dnan_pattern != 0); + + sign = dnan_pattern >> 7; /* - * 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. + * Place default_nan_pattern [6:0] into bits [62:56], + * and replecate bit [0] down into [55:0] */ - if (snan_bit_is_one(status)) { - /* set all bits other than msb */ - frac = (1ULL << (DECOMPOSED_BINARY_POINT - 1)) - 1; - } else { - /* set msb */ - frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1); - } -#endif + frac = deposit64(0, DECOMPOSED_BINARY_POINT - 7, 7, dnan_pattern); + frac = deposit64(frac, 0, DECOMPOSED_BINARY_POINT - 7, -(dnan_pattern & 1)); *p = (FloatParts64) { .cls = float_class_qnan, @@ -227,17 +198,17 @@ static void parts128_silence_nan(FloatParts128 *p, float_status *status) floatx80 floatx80_default_nan(float_status *status) { floatx80 r; + /* + * Extrapolate from the choices made by parts64_default_nan to fill + * in the floatx80 format. We assume that floatx80's explicit + * integer bit is always set (this is true for i386 and m68k, + * which are the only real users of this format). + */ + FloatParts64 p64; + parts64_default_nan(&p64, status); - /* None of the targets that have snan_bit_is_one use floatx80. */ - assert(!snan_bit_is_one(status)); -#if defined(TARGET_M68K) - r.low = UINT64_C(0xFFFFFFFFFFFFFFFF); - r.high = 0x7FFF; -#else - /* X86 */ - r.low = UINT64_C(0xC000000000000000); - r.high = 0xFFFF; -#endif + r.high = 0x7FFF | (p64.sign << 15); + r.low = (1ULL << DECOMPOSED_BINARY_POINT) | p64.frac; return r; } @@ -245,15 +216,15 @@ floatx80 floatx80_default_nan(float_status *status) | The pattern for a default generated extended double-precision inf. *----------------------------------------------------------------------------*/ -#define floatx80_infinity_high 0x7FFF -#if defined(TARGET_M68K) -#define floatx80_infinity_low UINT64_C(0x0000000000000000) -#else -#define floatx80_infinity_low UINT64_C(0x8000000000000000) -#endif - -const floatx80 floatx80_infinity - = make_floatx80_init(floatx80_infinity_high, floatx80_infinity_low); +floatx80 floatx80_default_inf(bool zSign, float_status *status) +{ + /* + * Whether the Integer bit is set in the default Infinity is + * target dependent. + */ + bool z = status->floatx80_behaviour & floatx80_default_inf_int_bit_is_zero; + return packFloatx80(zSign, 0x7fff, z ? 0 : (1ULL << 63)); +} /*---------------------------------------------------------------------------- | Returns 1 if the half-precision floating-point value `a' is a quiet @@ -370,300 +341,6 @@ bool float32_is_signaling_nan(float32 a_, float_status *status) } } -/*---------------------------------------------------------------------------- -| Select which NaN to propagate for a two-input operation. -| IEEE754 doesn't specify all the details of this, so the -| algorithm is target-specific. -| The routine is passed various bits of information about the -| two NaNs and should return 0 to select NaN a and 1 for NaN b. -| Note that signalling NaNs are always squashed to quiet NaNs -| by the caller, by calling floatXX_silence_nan() before -| returning them. -| -| aIsLargerSignificand is only valid if both a and b are NaNs -| of some kind, and is true if a has the larger significand, -| or if both a and b have the same significand but a is -| positive but b is negative. It is only needed for the x87 -| tie-break rule. -*----------------------------------------------------------------------------*/ - -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. - */ - if (status->use_first_nan) { - if (is_nan(a_cls)) { - return 0; - } else { - return 1; - } - } else { - 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; - } else { - return aIsLargerSignificand ? 0 : 1; - } - } else { - return 1; - } -#endif -} - -/*---------------------------------------------------------------------------- -| Select which NaN to propagate for a three-input operation. -| For the moment we assume that no CPU needs the 'larger significand' -| information. -| Return values : 0 : a; 1 : b; 2 : c; 3 : default-NaN -*----------------------------------------------------------------------------*/ -static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, - bool infzero, float_status *status) -{ -#if defined(TARGET_ARM) - /* For ARM, the (inf,zero,qnan) case sets InvalidOp and returns - * the default NaN - */ - if (infzero && is_qnan(c_cls)) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 3; - } - - /* This looks different from the ARM ARM pseudocode, because the ARM ARM - * puts the operands to a fused mac operation (a*b)+c in the order c,a,b. - */ - if (is_snan(c_cls)) { - return 2; - } else if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_qnan(c_cls)) { - return 2; - } else if (is_qnan(a_cls)) { - return 0; - } else { - return 1; - } -#elif defined(TARGET_MIPS) - if (snan_bit_is_one(status)) { - /* - * For MIPS systems that conform to IEEE754-1985, the (inf,zero,nan) - * case sets InvalidOp and returns the default NaN - */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 3; - } - /* Prefer sNaN over qNaN, in the a, b, c order. */ - 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; - } - } else { - /* - * For MIPS systems that conform to IEEE754-2008, the (inf,zero,nan) - * case sets InvalidOp and returns the input value 'c' - */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 2; - } - /* Prefer sNaN over qNaN, in the c, a, b order. */ - if (is_snan(c_cls)) { - return 2; - } else if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_qnan(c_cls)) { - return 2; - } else if (is_qnan(a_cls)) { - return 0; - } else { - return 1; - } - } -#elif defined(TARGET_LOONGARCH64) - /* - * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan) - * case sets InvalidOp and returns the input value 'c' - */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 2; - } - /* Prefer sNaN over qNaN, in the c, a, b order. */ - if (is_snan(c_cls)) { - return 2; - } else if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_qnan(c_cls)) { - return 2; - } else if (is_qnan(a_cls)) { - return 0; - } else { - return 1; - } -#elif defined(TARGET_PPC) - /* For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer - * to return an input NaN if we have one (ie c) rather than generating - * a default NaN - */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 2; - } - - /* If fRA is a NaN return it; otherwise if fRB is a NaN return it; - * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB - */ - if (is_nan(a_cls)) { - return 0; - } else if (is_nan(c_cls)) { - return 2; - } else { - return 1; - } -#elif defined(TARGET_RISCV) - /* For RISC-V, InvalidOp is set when multiplicands are Inf and zero */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - } - return 3; /* default NaN */ -#elif defined(TARGET_XTENSA) - /* - * For Xtensa, the (inf,zero,nan) case sets InvalidOp and returns - * an input NaN if we have one (ie c). - */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 2; - } - if (status->use_first_nan) { - if (is_nan(a_cls)) { - return 0; - } else if (is_nan(b_cls)) { - return 1; - } else { - return 2; - } - } else { - if (is_nan(c_cls)) { - return 2; - } else if (is_nan(b_cls)) { - return 1; - } else { - return 0; - } - } -#else - /* A default implementation: prefer a to b to c. - * This is unlikely to actually match any real implementation. - */ - if (is_nan(a_cls)) { - return 0; - } else if (is_nan(b_cls)) { - return 1; - } else { - return 2; - } -#endif -} - /*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. @@ -767,58 +444,6 @@ floatx80 floatx80_silence_nan(floatx80 a, float_status *status) return a; } -/*---------------------------------------------------------------------------- -| Takes two extended double-precision floating-point values `a' and `b', one -| of which is a NaN, and returns the appropriate NaN result. If either `a' or -| `b' is a signaling NaN, the invalid exception is raised. -*----------------------------------------------------------------------------*/ - -floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, float_status *status) -{ - bool aIsLargerSignificand; - FloatClass a_cls, b_cls; - - /* This is not complete, but is good enough for pickNaN. */ - a_cls = (!floatx80_is_any_nan(a) - ? float_class_normal - : floatx80_is_signaling_nan(a, status) - ? float_class_snan - : float_class_qnan); - b_cls = (!floatx80_is_any_nan(b) - ? float_class_normal - : floatx80_is_signaling_nan(b, status) - ? float_class_snan - : float_class_qnan); - - if (is_snan(a_cls) || is_snan(b_cls)) { - float_raise(float_flag_invalid, status); - } - - if (status->default_nan_mode) { - return floatx80_default_nan(status); - } - - if (a.low < b.low) { - aIsLargerSignificand = 0; - } else if (b.low < a.low) { - aIsLargerSignificand = 1; - } else { - aIsLargerSignificand = (a.high < b.high) ? 1 : 0; - } - - if (pickNaN(a_cls, b_cls, aIsLargerSignificand, status)) { - if (is_snan(b_cls)) { - return floatx80_silence_nan(b, status); - } - return b; - } else { - if (is_snan(a_cls)) { - return floatx80_silence_nan(a, status); - } - return a; - } -} - /*---------------------------------------------------------------------------- | Returns 1 if the quadruple-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 0cc130ae9b2..34c962d6bd9 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -79,9 +79,6 @@ this code that are retained. * version 2 or later. See the COPYING file in the top-level directory. */ -/* softfloat (and in particular the code in softfloat-specialize.h) is - * target-dependent and needs the TARGET_* macros. - */ #include "qemu/osdep.h" #include #include "qemu/bitops.h" @@ -132,7 +129,7 @@ this code that are retained. if (unlikely(soft_t ## _is_denormal(*a))) { \ *a = soft_t ## _set_sign(soft_t ## _zero, \ soft_t ## _is_neg(*a)); \ - float_raise(float_flag_input_denormal, s); \ + float_raise(float_flag_input_denormal_flushed, s); \ } \ } @@ -220,11 +217,9 @@ GEN_INPUT_FLUSH3(float64_input_flush3, float64) * the use of hardfloat, since hardfloat relies on the inexact flag being * already set. */ -#if defined(TARGET_PPC) || defined(__FAST_MATH__) # if defined(__FAST_MATH__) # warning disabling hardfloat due to -ffast-math: hardfloat requires an exact \ IEEE implementation -# endif # define QEMU_NO_HARDFLOAT 1 # define QEMU_SOFTFLOAT_ATTR QEMU_FLATTEN #else @@ -404,12 +399,16 @@ float64_gen2(float64 xa, float64 xb, float_status *s, /* * Classify a floating point number. Everything above float_class_qnan * is a NaN so cls >= float_class_qnan is any NaN. + * + * Note that we canonicalize denormals, so most code should treat + * class_normal and class_denormal identically. */ typedef enum __attribute__ ((__packed__)) { float_class_unclassified, float_class_zero, float_class_normal, + float_class_denormal, /* input was a non-squashed denormal */ float_class_inf, float_class_qnan, /* all NaNs from here */ float_class_snan, @@ -420,12 +419,14 @@ typedef enum __attribute__ ((__packed__)) { enum { float_cmask_zero = float_cmask(float_class_zero), float_cmask_normal = float_cmask(float_class_normal), + float_cmask_denormal = float_cmask(float_class_denormal), float_cmask_inf = float_cmask(float_class_inf), float_cmask_qnan = float_cmask(float_class_qnan), float_cmask_snan = float_cmask(float_class_snan), float_cmask_infzero = float_cmask_zero | float_cmask_inf, float_cmask_anynan = float_cmask_qnan | float_cmask_snan, + float_cmask_anynorm = float_cmask_normal | float_cmask_denormal, }; /* Flags for parts_minmax. */ @@ -459,6 +460,20 @@ static inline __attribute__((unused)) bool is_qnan(FloatClass c) return c == float_class_qnan; } +/* + * Return true if the float_cmask has only normals in it + * (including input denormals that were canonicalized) + */ +static inline bool cmask_is_only_normals(int cmask) +{ + return !(cmask & ~float_cmask_anynorm); +} + +static inline bool is_anynorm(FloatClass c) +{ + return float_cmask(c) & float_cmask_anynorm; +} + /* * Structure holding all of the decomposed parts of a float. * The exponent is unbiased and the fraction is normalized. @@ -517,6 +532,8 @@ typedef struct { * round_mask: bits below lsb which must be rounded * The following optional modifiers are available: * arm_althp: handle ARM Alternative Half Precision + * has_explicit_bit: has an explicit integer bit; this affects whether + * the float_status floatx80_behaviour handling applies */ typedef struct { int exp_size; @@ -526,6 +543,7 @@ typedef struct { int frac_size; int frac_shift; bool arm_althp; + bool has_explicit_bit; uint64_t round_mask; } FloatFmt; @@ -576,7 +594,10 @@ 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), + .has_explicit_bit = true, + }, }; /* Unpack a float to parts, but do not canonicalize. */ @@ -782,15 +803,15 @@ static FloatParts128 *parts128_mul(FloatParts128 *a, FloatParts128 *b, #define parts_mul(A, B, S) \ PARTS_GENERIC_64_128(mul, A)(A, B, S) -static FloatParts64 *parts64_muladd(FloatParts64 *a, FloatParts64 *b, - FloatParts64 *c, int flags, - float_status *s); -static FloatParts128 *parts128_muladd(FloatParts128 *a, FloatParts128 *b, - FloatParts128 *c, int flags, - float_status *s); +static FloatParts64 *parts64_muladd_scalbn(FloatParts64 *a, FloatParts64 *b, + FloatParts64 *c, int scale, + int flags, float_status *s); +static FloatParts128 *parts128_muladd_scalbn(FloatParts128 *a, FloatParts128 *b, + FloatParts128 *c, int scale, + int flags, float_status *s); -#define parts_muladd(A, B, C, Z, S) \ - PARTS_GENERIC_64_128(muladd, A)(A, B, C, Z, S) +#define parts_muladd_scalbn(A, B, C, Z, Y, S) \ + PARTS_GENERIC_64_128(muladd_scalbn, A)(A, B, C, Z, Y, S) static FloatParts64 *parts64_div(FloatParts64 *a, FloatParts64 *b, float_status *s); @@ -1722,6 +1743,7 @@ static float64 float64r32_round_pack_canonical(FloatParts64 *p, */ switch (p->cls) { case float_class_normal: + case float_class_denormal: if (unlikely(p->exp == 0)) { /* * The result is denormal for float32, but can be represented @@ -1782,7 +1804,7 @@ static bool floatx80_unpack_canonical(FloatParts128 *p, floatx80 f, g_assert_not_reached(); } - if (unlikely(floatx80_invalid_encoding(f))) { + if (unlikely(floatx80_invalid_encoding(f, s))) { float_raise(float_flag_invalid, s); return false; } @@ -1810,6 +1832,7 @@ static floatx80 floatx80_round_pack_canonical(FloatParts128 *p, switch (p->cls) { case float_class_normal: + case float_class_denormal: if (s->floatx80_rounding_precision == floatx80_precision_x) { parts_uncanon_normal(p, s, fmt); frac = p->frac_hi; @@ -1831,7 +1854,8 @@ static floatx80 floatx80_round_pack_canonical(FloatParts128 *p, case float_class_inf: /* x86 and m68k differ in the setting of the integer bit. */ - frac = floatx80_infinity_low; + frac = s->floatx80_behaviour & floatx80_default_inf_int_bit_is_zero ? + 0 : (1ULL << 63); exp = fmt->exp_max; break; @@ -2205,43 +2229,50 @@ floatx80_mul(floatx80 a, floatx80 b, float_status *status) * Fused multiply-add */ -float16 QEMU_FLATTEN float16_muladd(float16 a, float16 b, float16 c, - int flags, float_status *status) +float16 QEMU_FLATTEN +float16_muladd_scalbn(float16 a, float16 b, float16 c, + int scale, int flags, float_status *status) { FloatParts64 pa, pb, pc, *pr; float16_unpack_canonical(&pa, a, status); float16_unpack_canonical(&pb, b, status); float16_unpack_canonical(&pc, c, status); - pr = parts_muladd(&pa, &pb, &pc, flags, status); + pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status); return float16_round_pack_canonical(pr, status); } -static float32 QEMU_SOFTFLOAT_ATTR -soft_f32_muladd(float32 a, float32 b, float32 c, int flags, - float_status *status) +float16 float16_muladd(float16 a, float16 b, float16 c, + int flags, float_status *status) +{ + return float16_muladd_scalbn(a, b, c, 0, flags, status); +} + +float32 QEMU_SOFTFLOAT_ATTR +float32_muladd_scalbn(float32 a, float32 b, float32 c, + int scale, int flags, float_status *status) { FloatParts64 pa, pb, pc, *pr; float32_unpack_canonical(&pa, a, status); float32_unpack_canonical(&pb, b, status); float32_unpack_canonical(&pc, c, status); - pr = parts_muladd(&pa, &pb, &pc, flags, status); + pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status); return float32_round_pack_canonical(pr, status); } -static float64 QEMU_SOFTFLOAT_ATTR -soft_f64_muladd(float64 a, float64 b, float64 c, int flags, - float_status *status) +float64 QEMU_SOFTFLOAT_ATTR +float64_muladd_scalbn(float64 a, float64 b, float64 c, + int scale, int flags, float_status *status) { FloatParts64 pa, pb, pc, *pr; float64_unpack_canonical(&pa, a, status); float64_unpack_canonical(&pb, b, status); float64_unpack_canonical(&pc, c, status); - pr = parts_muladd(&pa, &pb, &pc, flags, status); + pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status); return float64_round_pack_canonical(pr, status); } @@ -2260,7 +2291,7 @@ float32_muladd(float32 xa, float32 xb, float32 xc, int flags, float_status *s) if (unlikely(!can_use_fpu(s))) { goto soft; } - if (unlikely(flags & float_muladd_halve_result)) { + if (unlikely(flags & float_muladd_suppress_add_product_zero)) { goto soft; } @@ -2316,7 +2347,7 @@ float32_muladd(float32 xa, float32 xb, float32 xc, int flags, float_status *s) return ur.s; soft: - return soft_f32_muladd(ua.s, ub.s, uc.s, flags, s); + return float32_muladd_scalbn(ua.s, ub.s, uc.s, 0, flags, s); } float64 QEMU_FLATTEN @@ -2331,9 +2362,6 @@ float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s) if (unlikely(!can_use_fpu(s))) { goto soft; } - if (unlikely(flags & float_muladd_halve_result)) { - goto soft; - } float64_input_flush3(&ua.s, &ub.s, &uc.s, s); if (unlikely(!f64_is_zon3(ua, ub, uc))) { @@ -2387,7 +2415,7 @@ float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s) return ur.s; soft: - return soft_f64_muladd(ua.s, ub.s, uc.s, flags, s); + return float64_muladd_scalbn(ua.s, ub.s, uc.s, 0, flags, s); } float64 float64r32_muladd(float64 a, float64 b, float64 c, @@ -2398,7 +2426,7 @@ float64 float64r32_muladd(float64 a, float64 b, float64 c, float64_unpack_canonical(&pa, a, status); float64_unpack_canonical(&pb, b, status); float64_unpack_canonical(&pc, c, status); - pr = parts_muladd(&pa, &pb, &pc, flags, status); + pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status); return float64r32_round_pack_canonical(pr, status); } @@ -2411,7 +2439,7 @@ bfloat16 QEMU_FLATTEN bfloat16_muladd(bfloat16 a, bfloat16 b, bfloat16 c, bfloat16_unpack_canonical(&pa, a, status); bfloat16_unpack_canonical(&pb, b, status); bfloat16_unpack_canonical(&pc, c, status); - pr = parts_muladd(&pa, &pb, &pc, flags, status); + pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status); return bfloat16_round_pack_canonical(pr, status); } @@ -2424,7 +2452,7 @@ float128 QEMU_FLATTEN float128_muladd(float128 a, float128 b, float128 c, float128_unpack_canonical(&pa, a, status); float128_unpack_canonical(&pb, b, status); float128_unpack_canonical(&pc, c, status); - pr = parts_muladd(&pa, &pb, &pc, flags, status); + pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status); return float128_round_pack_canonical(pr, status); } @@ -2685,6 +2713,9 @@ static void parts_float_to_ahp(FloatParts64 *a, float_status *s) float16_params_ahp.frac_size + 1); break; + case float_class_denormal: + float_raise(float_flag_input_denormal_used, s); + break; case float_class_normal: case float_class_zero: break; @@ -2699,6 +2730,9 @@ static void parts64_float_to_float(FloatParts64 *a, float_status *s) if (is_nan(a->cls)) { parts_return_nan(a, s); } + if (a->cls == float_class_denormal) { + float_raise(float_flag_input_denormal_used, s); + } } static void parts128_float_to_float(FloatParts128 *a, float_status *s) @@ -2706,6 +2740,9 @@ static void parts128_float_to_float(FloatParts128 *a, float_status *s) if (is_nan(a->cls)) { parts_return_nan(a, s); } + if (a->cls == float_class_denormal) { + float_raise(float_flag_input_denormal_used, s); + } } #define parts_float_to_float(P, S) \ @@ -2718,12 +2755,21 @@ static void parts_float_to_float_narrow(FloatParts64 *a, FloatParts128 *b, a->sign = b->sign; a->exp = b->exp; - if (a->cls == float_class_normal) { + switch (a->cls) { + case float_class_denormal: + float_raise(float_flag_input_denormal_used, s); + /* fall through */ + case float_class_normal: frac_truncjam(a, b); - } else if (is_nan(a->cls)) { + break; + case float_class_snan: + case float_class_qnan: /* Discard the low bits of the NaN. */ a->frac = b->frac_hi; parts_return_nan(a, s); + break; + default: + break; } } @@ -2738,6 +2784,9 @@ static void parts_float_to_float_widen(FloatParts128 *a, FloatParts64 *b, if (is_nan(a->cls)) { parts_return_nan(a, s); } + if (a->cls == float_class_denormal) { + float_raise(float_flag_input_denormal_used, s); + } } float32 float16_to_float32(float16 a, bool ieee, float_status *s) @@ -3126,6 +3175,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) { @@ -3198,6 +3256,7 @@ static Int128 float128_to_int128_scalbn(float128 a, FloatRoundMode rmode, return int128_zero(); case float_class_normal: + case float_class_denormal: if (parts_round_to_int_normal(&p, rmode, scale, 128 - 2)) { flags = float_flag_inexact; } @@ -3392,6 +3451,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); @@ -3407,6 +3471,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); @@ -3534,6 +3603,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) { @@ -3606,6 +3684,7 @@ static Int128 float128_to_uint128_scalbn(float128 a, FloatRoundMode rmode, return int128_zero(); case float_class_normal: + case float_class_denormal: if (parts_round_to_int_normal(&p, rmode, scale, 128 - 2)) { flags = float_flag_inexact; if (p.cls == float_class_zero) { @@ -3759,6 +3838,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); @@ -3774,6 +3858,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); @@ -3929,6 +4018,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); @@ -3944,6 +4038,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 = { }; @@ -4139,6 +4238,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); @@ -4154,6 +4258,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; @@ -4317,7 +4426,11 @@ float32_hs_compare(float32 xa, float32 xb, float_status *s, bool is_quiet) goto soft; } - float32_input_flush2(&ua.s, &ub.s, s); + if (unlikely(float32_is_denormal(ua.s) || float32_is_denormal(ub.s))) { + /* We may need to set the input_denormal_used flag */ + goto soft; + } + if (isgreaterequal(ua.h, ub.h)) { if (isgreater(ua.h, ub.h)) { return float_relation_greater; @@ -4367,7 +4480,11 @@ float64_hs_compare(float64 xa, float64 xb, float_status *s, bool is_quiet) goto soft; } - float64_input_flush2(&ua.s, &ub.s, s); + if (unlikely(float64_is_denormal(ua.s) || float64_is_denormal(ub.s))) { + /* We may need to set the input_denormal_used flag */ + goto soft; + } + if (isgreaterequal(ua.h, ub.h)) { if (isgreater(ua.h, ub.h)) { return float_relation_greater; @@ -4779,7 +4896,7 @@ float128 float128_silence_nan(float128 a, float_status *status) static bool parts_squash_denormal(FloatParts64 p, float_status *status) { if (p.exp == 0 && p.frac != 0) { - float_raise(float_flag_input_denormal, status); + float_raise(float_flag_input_denormal_flushed, status); return true; } @@ -4855,6 +4972,25 @@ void normalizeFloatx80Subnormal(uint64_t aSig, int32_t *zExpPtr, *zExpPtr = 1 - shiftCount; } +/*---------------------------------------------------------------------------- +| Takes two extended double-precision floating-point values `a' and `b', one +| of which is a NaN, and returns the appropriate NaN result. If either `a' or +| `b' is a signaling NaN, the invalid exception is raised. +*----------------------------------------------------------------------------*/ + +floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, float_status *status) +{ + FloatParts128 pa, pb, *pr; + + if (!floatx80_unpack_canonical(&pa, a, status) || + !floatx80_unpack_canonical(&pb, b, status)) { + return floatx80_default_nan(status); + } + + pr = parts_pick_nan(&pa, &pb, status); + return floatx80_round_pack_canonical(pr, status); +} + /*---------------------------------------------------------------------------- | Takes an abstract floating-point value having sign `zSign', exponent `zExp', | and extended significand formed by the concatenation of `zSig0' and `zSig1', @@ -4929,7 +5065,7 @@ floatx80 roundAndPackFloatx80(FloatX80RoundPrec roundingPrecision, bool zSign, } if ( zExp <= 0 ) { if (status->flush_to_zero) { - float_raise(float_flag_output_denormal, status); + float_raise(float_flag_output_denormal_flushed, status); return packFloatx80(zSign, 0, 0); } isTiny = status->tininess_before_rounding @@ -5003,9 +5139,7 @@ floatx80 roundAndPackFloatx80(FloatX80RoundPrec roundingPrecision, bool zSign, ) { return packFloatx80( zSign, 0x7FFE, ~ roundMask ); } - return packFloatx80(zSign, - floatx80_infinity_high, - floatx80_infinity_low); + return floatx80_default_inf(zSign, status); } if ( zExp <= 0 ) { isTiny = status->tininess_before_rounding @@ -5143,6 +5277,8 @@ float32 float32_exp2(float32 a, float_status *status) float32_unpack_canonical(&xp, a, status); if (unlikely(xp.cls != float_class_normal)) { switch (xp.cls) { + case float_class_denormal: + break; case float_class_snan: case float_class_qnan: parts_return_nan(&xp, status); @@ -5152,9 +5288,8 @@ float32 float32_exp2(float32 a, float_status *status) case float_class_zero: return float32_one; default: - break; + g_assert_not_reached(); } - g_assert_not_reached(); } float_raise(float_flag_inexact, status); @@ -5165,8 +5300,9 @@ float32 float32_exp2(float32 a, float_status *status) float64_unpack_canonical(&rp, float64_one, status); for (i = 0 ; i < 15 ; i++) { + float64_unpack_canonical(&tp, float32_exp2_coefficients[i], status); - rp = *parts_muladd(&tp, &xnp, &rp, 0, status); + rp = *parts_muladd_scalbn(&tp, &xnp, &rp, 0, 0, status); xnp = *parts_mul(&xnp, &xp, status); } diff --git a/fsdev/9p-iov-marshal.c b/fsdev/9p-iov-marshal.c index a1c9beddd2e..0c5a1a0fa21 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/file-op-9p.h b/fsdev/file-op-9p.h index 4997677460e..b9dae8c84c2 100644 --- a/fsdev/file-op-9p.h +++ b/fsdev/file-op-9p.h @@ -129,6 +129,8 @@ struct FileOperations { int (*chown)(FsContext *, V9fsPath *, FsCred *); int (*mknod)(FsContext *, V9fsPath *, const char *, FsCred *); int (*utimensat)(FsContext *, V9fsPath *, const struct timespec *); + int (*futimens)(FsContext *ctx, int fid_type, V9fsFidOpenState *fs, + const struct timespec *times); int (*remove)(FsContext *, const char *); int (*symlink)(FsContext *, const char *, V9fsPath *, const char *, FsCred *); @@ -152,6 +154,8 @@ struct FileOperations { int (*fstat)(FsContext *, int, V9fsFidOpenState *, struct stat *); int (*rename)(FsContext *, const char *, const char *); int (*truncate)(FsContext *, V9fsPath *, off_t); + int (*ftruncate)(FsContext *ctx, int fid_type, V9fsFidOpenState *fs, + off_t size); int (*fsync)(FsContext *, int, V9fsFidOpenState *, int); int (*statfs)(FsContext *s, V9fsPath *path, struct statfs *stbuf); ssize_t (*lgetxattr)(FsContext *, V9fsPath *, @@ -164,6 +168,7 @@ struct FileOperations { int (*renameat)(FsContext *ctx, V9fsPath *olddir, const char *old_name, V9fsPath *newdir, const char *new_name); int (*unlinkat)(FsContext *ctx, V9fsPath *dir, const char *name, int flags); + bool (*has_valid_file_handle)(int fid_type, V9fsFidOpenState *fs); }; #endif diff --git a/fsdev/meson.build b/fsdev/meson.build index 1bec0659245..c751d8cb622 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')) -system_ss.add_all(when: 'CONFIG_LINUX', if_true: fsdev_ss) -system_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/qemu-fsdev-throttle.c b/fsdev/qemu-fsdev-throttle.c index 5c83a1cc09c..d912da906dc 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 a21aecddc73..daa8ca2494d 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 f5c953a7105..57877dad0a7 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, @@ -133,14 +122,6 @@ int qemu_fsdev_add(QemuOpts *opts, Error **errp) } if (fsdriver) { - if (strncmp(fsdriver, "proxy", 5) == 0) { - warn_report( - "'-fsdev proxy' and '-virtfs proxy' are deprecated, use " - "'local' instead of 'proxy, or consider deploying virtiofsd " - "as alternative to 9p" - ); - } - for (i = 0; i < ARRAY_SIZE(FsDrivers); i++) { if (strcmp(FsDrivers[i].name, fsdriver) == 0) { break; diff --git a/fsdev/qemu-fsdev.h b/fsdev/qemu-fsdev.h index 52a53977701..731f1406a81 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 144aaf585ad..00000000000 --- a/fsdev/virtfs-proxy-helper.c +++ /dev/null @@ -1,1193 +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. - */ - -/* - * NOTE: The 9p 'proxy' backend is deprecated (since QEMU 8.1) and will be - * removed in a future version of QEMU! - */ - -#include "qemu/osdep.h" -#include -#include -#include -#include -#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 - - fprintf(stderr, "NOTE: The 9p 'proxy' backend is deprecated (since " - "QEMU 8.1) and will be removed in a future version of " - "QEMU!\n"); - - prog_name = g_path_get_basename(argv[0]); - - is_daemon = true; - 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-core.xml b/gdb-xml/aarch64-core.xml index e1e9dc3f913..b8046510b9a 100644 --- a/gdb-xml/aarch64-core.xml +++ b/gdb-xml/aarch64-core.xml @@ -1,5 +1,5 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/aarch64-mte.xml b/gdb-xml/aarch64-mte.xml new file mode 100644 index 00000000000..4b70b4f17a6 --- /dev/null +++ b/gdb-xml/aarch64-mte.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/gdb-xml/arm-neon.xml b/gdb-xml/arm-neon.xml index 9dce0a996fc..d61f6b8549d 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 index e181163cff0..b94378112a0 100644 --- a/gdb-xml/hexagon-core.xml +++ b/gdb-xml/hexagon-core.xml @@ -1,6 +1,6 @@ + + + + + diff --git a/gdb-xml/i386-64bit-linux.xml b/gdb-xml/i386-64bit-linux.xml new file mode 100644 index 00000000000..0f26990d2f7 --- /dev/null +++ b/gdb-xml/i386-64bit-linux.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/gdb-xml/loongarch-lasx.xml b/gdb-xml/loongarch-lasx.xml new file mode 100644 index 00000000000..753b982c651 --- /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 00000000000..51af1c6fd55 --- /dev/null +++ b/gdb-xml/loongarch-lsx.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 349d348c7b5..def0b7e8776 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -20,7 +20,7 @@ * 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" @@ -28,18 +28,22 @@ #include "qemu/cutils.h" #include "qemu/module.h" #include "qemu/error-report.h" +#include "qemu/target-info.h" #include "trace.h" #include "exec/gdbstub.h" +#include "gdbstub/commands.h" #include "gdbstub/syscalls.h" #ifdef CONFIG_USER_ONLY +#include "accel/tcg/vcpu-state.h" #include "gdbstub/user.h" #else #include "hw/cpu/cluster.h" #include "hw/boards.h" #endif +#include "hw/core/cpu.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" +#include "system/hw_accel.h" +#include "system/runstate.h" #include "exec/replay-core.h" #include "exec/hwaddr.h" @@ -47,11 +51,9 @@ 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; GDBState gdbserver_state; @@ -349,16 +351,11 @@ static CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid) } } -bool gdb_has_xml(void) -{ - return !!gdb_get_cpu_process(gdbserver_state.g_cpu)->target_xml; -} - static const char *get_feature_xml(const char *p, const char **newp, GDBProcess *process) { CPUState *cpu = gdb_get_first_cpu_in_process(process); - CPUClass *cc = CPU_GET_CLASS(cpu); + GDBRegisterState *r; size_t len; /* @@ -372,47 +369,39 @@ static const char *get_feature_xml(const char *p, const char **newp, /* Is it the main target xml? */ if (strncmp(p, "target.xml", len) == 0) { if (!process->target_xml) { - GDBRegisterState *r; - GString *xml = g_string_new(""); - - g_string_append(xml, - "" - ""); - - if (cc->gdb_arch_name) { - g_autofree gchar *arch = cc->gdb_arch_name(cpu); - g_string_append_printf(xml, - "%s", - arch); + g_autoptr(GPtrArray) xml = g_ptr_array_new_with_free_func(g_free); + + g_ptr_array_add( + xml, + g_strdup("" + "" + "")); + + if (cpu->cc->gdb_arch_name) { + g_ptr_array_add( + xml, + g_markup_printf_escaped("%s", + cpu->cc->gdb_arch_name(cpu))); } - g_string_append(xml, "gdb_core_xml_file); - g_string_append(xml, "\"/>"); - for (r = cpu->gdb_regs; r; r = r->next) { - g_string_append(xml, "xml); - g_string_append(xml, "\"/>"); + 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)); } - g_string_append(xml, ""); + g_ptr_array_add(xml, g_strdup("")); + g_ptr_array_add(xml, NULL); - process->target_xml = g_string_free(xml, false); - return process->target_xml; - } - } - /* Is it dynamically generated by the target? */ - if (cc->gdb_get_dynamic_xml) { - g_autofree char *xmlname = g_strndup(p, len); - const char *xml = cc->gdb_get_dynamic_xml(cpu, xmlname); - if (xml) { - return xml; + process->target_xml = g_strjoinv(NULL, (void *)xml->pdata); } + return process->target_xml; } - /* Is it one of the encoded gdb-xml/ files? */ - for (int i = 0; xml_builtin[i][0]; i++) { - const char *name = xml_builtin[i][0]; - if ((strncmp(name, p, len) == 0) && - strlen(name) == len) { - return xml_builtin[i][1]; + /* 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; } } @@ -420,19 +409,127 @@ static const char *get_feature_xml(const char *p, const char **newp, 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); + if (reg < cpu->cc->gdb_num_core_regs) { + return cpu->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; @@ -440,57 +537,113 @@ 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); + if (reg < cpu->cc->gdb_num_core_regs) { + return cpu->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; } +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); +} + +static const char *gdb_get_core_xml_file(CPUState *cpu) +{ + CPUClass *cc = cpu->cc; + + /* + * The CPU class can provide the XML filename via a method, + * or as a simple fixed string field. + */ + if (cc->gdb_get_core_xml_file) { + return cc->gdb_get_core_xml_file(cpu); + } + return cc->gdb_core_xml_file; +} + +void gdb_init_cpu(CPUState *cpu) +{ + CPUClass *cc = cpu->cc; + const GDBFeature *feature; + const char *xmlfile = gdb_get_core_xml_file(cpu); + + cpu->gdb_regs = g_array_new(false, false, sizeof(GDBRegisterState)); + + if (xmlfile) { + feature = gdb_find_static_feature(xmlfile); + 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 = gdb_get_first_cpu_in_process(p); @@ -601,7 +754,7 @@ static int gdb_handle_vcont(const char *p) /* * 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, - * respectivelly + * respectively */ int target_count = 0; CPUState *last_target = NULL; @@ -794,60 +947,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 - * - * allow_stop_reply -> true iff the gdbstub can respond to this command with a - * "stop reply" packet. The list of commands that accept such response is - * defined at the GDB Remote Serial Protocol documentation. see: - * https://sourceware.org/gdb/onlinedocs/gdb/Stop-Reply-Packets.html#Stop-Reply-Packets. - * - * schema definitions: - * Each schema parameter entry consists of 2 chars, - * 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; - bool allow_stop_reply; -} GdbCmdParseEntry; - static inline int startswith(const char *string, const char *pattern) { return !strncmp(string, pattern, strlen(pattern)); } -static int process_string_cmd(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)) || @@ -858,16 +975,20 @@ static int process_string_cmd(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, NULL); - return 0; + cmd->handler(params, user_ctx); + return true; } - return -1; + return false; } static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) @@ -881,7 +1002,7 @@ 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(data, cmd, 1)) { + if (!process_string_cmd(data, cmd, 1)) { gdb_put_packet(""); } } @@ -897,9 +1018,15 @@ static void handle_detach(GArray *params, void *user_ctx) 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; @@ -929,13 +1056,13 @@ static void handle_thread_alive(GArray *params, void *user_ctx) return; } - if (get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { + 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) { gdb_put_packet("E22"); return; @@ -947,7 +1074,7 @@ static void handle_thread_alive(GArray *params, void *user_ctx) 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; @@ -963,7 +1090,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); @@ -975,6 +1102,7 @@ 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) { @@ -982,18 +1110,24 @@ static void handle_set_thread(GArray *params, void *user_ctx) return; } - if (get_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) { + 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) { + 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) { gdb_put_packet("E22"); return; @@ -1003,7 +1137,7 @@ 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; gdb_put_packet("OK"); @@ -1028,9 +1162,9 @@ static void handle_insert_bp(GArray *params, void *user_ctx) } 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) { gdb_put_packet("OK"); return; @@ -1052,9 +1186,9 @@ static void handle_remove_bp(GArray *params, void *user_ctx) } 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) { gdb_put_packet("OK"); return; @@ -1081,20 +1215,15 @@ static void handle_set_reg(GArray *params, void *user_ctx) { int reg_size; - if (!gdb_get_cpu_process(gdbserver_state.g_cpu)->target_xml) { - gdb_put_packet(""); - return; - } - if (params->len != 2) { gdb_put_packet("E22"); return; } - reg_size = strlen(get_param(params, 1)->data) / 2; - gdb_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); + gdb_get_cmd_param(params, 0)->val_ull); gdb_put_packet("OK"); } @@ -1102,11 +1231,6 @@ static void handle_get_reg(GArray *params, void *user_ctx) { int reg_size; - if (!gdb_get_cpu_process(gdbserver_state.g_cpu)->target_xml) { - gdb_put_packet(""); - return; - } - if (!params->len) { gdb_put_packet("E14"); return; @@ -1114,7 +1238,7 @@ static void handle_get_reg(GArray *params, void *user_ctx) 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) { gdb_put_packet("E14"); return; @@ -1135,16 +1259,16 @@ static void handle_write_mem(GArray *params, void *user_ctx) } /* gdb_hextomem() reads 2*len bytes */ - if (get_param(params, 1)->val_ull > - strlen(get_param(params, 2)->data) / 2) { + if (gdb_get_cmd_param(params, 1)->val_ull > + strlen(gdb_get_cmd_param(params, 2)->data) / 2) { gdb_put_packet("E22"); return; } - gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 2)->data, - get_param(params, 1)->val_ull); + 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, - get_param(params, 0)->val_ull, + gdb_get_cmd_param(params, 0)->val_ull, gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len, true)) { gdb_put_packet("E14"); @@ -1162,16 +1286,16 @@ static void handle_read_mem(GArray *params, void *user_ctx) } /* gdb_memtohex() doubles the required space */ - if (get_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) { + 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 (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, - get_param(params, 0)->val_ull, + gdb_get_cmd_param(params, 0)->val_ull, gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len, false)) { gdb_put_packet("E14"); @@ -1195,8 +1319,8 @@ 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; - gdb_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 (reg_id = 0; reg_id < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0; @@ -1220,8 +1344,8 @@ static void handle_read_all_regs(GArray *params, void *user_ctx) len += gdb_read_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf, reg_id); + g_assert(len == gdbserver_state.mem_buf->len); } - g_assert(len == gdbserver_state.mem_buf->len); gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len); gdb_put_strbuf(); @@ -1231,7 +1355,7 @@ static void handle_read_all_regs(GArray *params, void *user_ctx) static void handle_step(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); } cpu_single_step(gdbserver_state.c_cpu, gdbserver_state.sstep_flags); @@ -1244,7 +1368,7 @@ static void handle_backward(GArray *params, void *user_ctx) 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(); @@ -1279,7 +1403,7 @@ 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)) { gdb_put_packet("E22"); } else if (res) { @@ -1297,7 +1421,7 @@ 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; } @@ -1327,7 +1451,7 @@ static void handle_v_kill(GArray *params, void *user_ctx) 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[] = { @@ -1335,26 +1459,26 @@ 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 /* @@ -1364,25 +1488,25 @@ static const GdbCmdParseEntry gdb_v_commands_table[] = { { .handler = gdb_handle_v_file_open, .cmd = "File:open:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s,L,L0" }, { .handler = gdb_handle_v_file_close, .cmd = "File:close:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l0" }, { .handler = gdb_handle_v_file_pread, .cmd = "File:pread:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l,L,L0" }, { .handler = gdb_handle_v_file_readlink, .cmd = "File:readlink:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }, #endif @@ -1394,9 +1518,9 @@ static void handle_v_commands(GArray *params, void *user_ctx) return; } - if (process_string_cmd(get_param(params, 0)->data, - gdb_v_commands_table, - ARRAY_SIZE(gdb_v_commands_table))) { + if (!process_string_cmd(gdb_get_cmd_param(params, 0)->data, + gdb_v_commands_table, + ARRAY_SIZE(gdb_v_commands_table))) { gdb_put_packet(""); } } @@ -1426,7 +1550,7 @@ 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) { gdb_put_packet("E22"); @@ -1474,6 +1598,18 @@ static void handle_query_threads(GArray *params, void *user_ctx) gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu); } +static void handle_query_gdb_server_version(GArray *params, void *user_ctx) +{ +#if defined(CONFIG_USER_ONLY) + g_string_printf(gdbserver_state.str_buf, "name:qemu-%s;version:%s;", + target_name(), QEMU_VERSION); +#else + g_string_printf(gdbserver_state.str_buf, "name:qemu-system-%s;version:%s;", + target_name(), QEMU_VERSION); +#endif + gdb_put_strbuf(); +} + static void handle_query_first_threads(GArray *params, void *user_ctx) { gdbserver_state.query_cpu = gdb_first_attached_cpu(); @@ -1486,13 +1622,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) { + 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; } @@ -1516,13 +1652,27 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx) gdb_put_strbuf(); } -static void handle_query_supported(GArray *params, void *user_ctx) + +static char **extra_query_flags; + +void gdb_extend_qsupported_features(char *qflags) { - CPUClass *cc; + 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); + } +} +static void handle_query_supported(GArray *params, void *user_ctx) +{ g_string_printf(gdbserver_state.str_buf, "PacketSize=%x", MAX_PACKET_LENGTH); - cc = CPU_GET_CLASS(first_cpu); - if (cc->gdb_core_xml_file) { + if (gdb_get_core_xml_file(first_cpu)) { g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+"); } @@ -1533,26 +1683,42 @@ static void handle_query_supported(GArray *params, void *user_ctx) #if defined(CONFIG_USER_ONLY) #if defined(CONFIG_LINUX) - if (gdbserver_state.c_cpu->opaque) { + 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+"); + + 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) { GDBProcess *process; - CPUClass *cc; unsigned long len, total_len, addr; const char *xml; const char *p; @@ -1563,21 +1729,20 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx) } process = gdb_get_cpu_process(gdbserver_state.g_cpu); - cc = CPU_GET_CLASS(gdbserver_state.g_cpu); - if (!cc->gdb_core_xml_file) { + if (!gdb_get_core_xml_file(gdbserver_state.g_cpu)) { gdb_put_packet(""); return; } - p = get_param(params, 0)->data; + p = gdb_get_cmd_param(params, 0)->data; xml = get_feature_xml(p, &p, process); if (!xml) { 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) { gdb_put_packet("E00"); @@ -1622,11 +1787,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, @@ -1636,6 +1855,10 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { .handler = handle_query_threads, .cmd = "sThreadInfo", }, + { + .handler = handle_query_gdb_server_version, + .cmd = "GDBServerVersion", + }, { .handler = handle_query_first_threads, .cmd = "fThreadInfo", @@ -1643,7 +1866,7 @@ 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 @@ -1655,14 +1878,14 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { { .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" }, { @@ -1673,7 +1896,7 @@ 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) @@ -1681,21 +1904,27 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { { .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 = 1, + .cmd_startswith = true, .schema = "l:l,l0" }, #endif { .handler = gdb_handle_query_attached, .cmd = "Attached:", - .cmd_startswith = 1 + .cmd_startswith = true }, { .handler = gdb_handle_query_attached, @@ -1713,60 +1942,100 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { #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 = 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(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(get_param(params, 0)->data, + if (process_string_cmd(data, gdb_gen_query_table, ARRAY_SIZE(gdb_gen_query_table))) { - gdb_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(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(get_param(params, 0)->data, + if (process_string_cmd(data, gdb_gen_set_table, ARRAY_SIZE(gdb_gen_set_table))) { - gdb_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) @@ -1801,7 +2070,7 @@ static int gdb_handle_packet(const char *line_buf) 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; @@ -1812,7 +2081,7 @@ 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" }; @@ -1824,7 +2093,7 @@ 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" }; @@ -1836,7 +2105,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; @@ -1846,13 +2115,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; @@ -1863,7 +2133,7 @@ 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" }; @@ -1875,7 +2145,7 @@ 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" }; @@ -1887,7 +2157,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry file_io_cmd_desc = { .handler = gdb_handle_file_io, .cmd = "F", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "L,L,o0" }; cmd_parser = &file_io_cmd_desc; @@ -1898,7 +2168,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; } @@ -1908,7 +2178,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; @@ -1919,7 +2189,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; @@ -1930,7 +2200,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; @@ -1941,7 +2211,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; @@ -1952,7 +2222,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; @@ -1963,7 +2233,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; @@ -1974,7 +2244,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; @@ -1985,7 +2255,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; @@ -1996,7 +2266,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; @@ -2007,7 +2277,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; @@ -2018,7 +2288,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; diff --git a/gdbstub/internals.h b/gdbstub/internals.h index fee243081ff..bf5a5c63029 100644 --- a/gdbstub/internals.h +++ b/gdbstub/internals.h @@ -24,6 +24,7 @@ enum { 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 @@ -32,8 +33,6 @@ enum { typedef struct GDBProcess { uint32_t pid; bool attached; - - /* If gdb sends qXfer:features:read:target.xml this will be populated */ char *target_xml; } GDBProcess; @@ -103,20 +102,18 @@ static inline int tohex(int v) } /* - * Connection helpers for both softmmu and user backends + * Connection helpers for both system and user backends */ void gdb_put_strbuf(void); -int gdb_put_packet(const char *buf); int gdb_put_packet_binary(const char *buf, int len, bool dump); -void gdb_hextomem(GByteArray *mem, const char *buf, int len); void gdb_memtohex(GString *buf, const uint8_t *mem, int len); void gdb_memtox(GString *buf, const char *mem, int len); void gdb_read_byte(uint8_t ch); /* * Packet acknowledgement - we handle this slightly differently - * between user and softmmu mode, mainly to deal with the differences + * 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 @@ -126,7 +123,7 @@ void gdb_read_byte(uint8_t ch); * gdb_got_immediate_ack() - check ok to continue * * Returns true to continue, false to re-transmit for user only, the - * softmmu stub always returns true. + * system stub always returns true. */ bool gdb_got_immediate_ack(void); /* utility helpers */ @@ -136,11 +133,12 @@ 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); /* softmmu, stub for user */ +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 softmmu, specialised for user-mode */ +/* signal mapping, common for system, specialised for user-mode */ int gdb_signal_to_target(int sig); int gdb_target_signal_to_gdb(int sig); @@ -157,50 +155,34 @@ void gdb_continue(void); int gdb_continue_partial(char *newstates); /* - * Helpers with separate softmmu and user implementations + * Helpers with separate system and user implementations */ void gdb_put_buffer(const uint8_t *buf, int len); /* - * Command handlers - either specialised or softmmu or user only + * Command handlers - either specialised or system or user only */ void gdb_init_gdbserver_state(void); -typedef enum GDBThreadIdKind { - GDB_ONE_THREAD = 0, - GDB_ALL_THREADS, /* One process, all threads */ - GDB_ALL_PROCESSES, - GDB_READ_THREAD_ERR -} GDBThreadIdKind; - -typedef union GdbCmdVariant { - const char *data; - uint8_t opcode; - unsigned long val_ul; - unsigned long long val_ull; - struct { - GDBThreadIdKind kind; - uint32_t pid; - uint32_t tid; - } thread_id; -} GdbCmdVariant; - -#define get_param(p, i) (&g_array_index(p, GdbCmdVariant, i)) - -void gdb_handle_query_rcmd(GArray *params, void *user_ctx); /* softmmu */ +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 *user_ctx); /* both */ +void gdb_handle_query_attached(GArray *params, void *ctx); /* both */ -/* softmmu only */ -void gdb_handle_query_qemu_phy_mem_mode(GArray *params, void *user_ctx); -void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *user_ctx); +/* 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); @@ -208,11 +190,11 @@ bool gdb_handled_syscall(void); void gdb_disable_syscalls(void); void gdb_syscall_reset(void); -/* user/softmmu specific syscall handling */ +/* user/system specific syscall handling */ void gdb_syscall_handling(const char *syscall_packet); /* - * Break/Watch point support - there is an implementation for softmmu + * Break/Watch point support - there is an implementation for system * and user mode. */ bool gdb_supports_guest_debug(void); @@ -229,7 +211,7 @@ void gdb_breakpoint_remove_all(CPUState *cs); * @is_write: is it a write operation * * This function is specialised depending on the mode we are running - * in. For softmmu guests we can switch the interpretation of the + * 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, diff --git a/gdbstub/meson.build b/gdbstub/meson.build index 9500b9dc4e6..15c666f5752 100644 --- a/gdbstub/meson.build +++ b/gdbstub/meson.build @@ -1,39 +1,21 @@ # # 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. # -# 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', 'softmmu.c')) - -gdb_user_ss = gdb_user_ss.apply(config_targetos, strict: false) -gdb_system_ss = gdb_system_ss.apply(config_targetos, strict: false) - -libgdb_user = static_library('gdb_user', - gdb_user_ss.sources() + genh, - name_suffix: 'fa', - c_args: '-DCONFIG_USER_ONLY', - build_by_default: have_user) - -libgdb_softmmu = static_library('gdb_softmmu', - gdb_system_ss.sources() + genh, - name_suffix: 'fa', - build_by_default: have_system) - -gdb_user = declare_dependency(link_whole: libgdb_user) -user_ss.add(gdb_user) -gdb_softmmu = declare_dependency(link_whole: libgdb_softmmu) -system_ss.add(gdb_softmmu) - -common_ss.add(files('syscalls.c')) +user_ss.add(files( + 'gdbstub.c', + 'syscalls.c', + 'user.c' +)) + +system_ss.add(files( + 'gdbstub.c', + 'syscalls.c', + 'system.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 9f0b8b54977..00000000000 --- a/gdbstub/softmmu.c +++ /dev/null @@ -1,658 +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) 2003-2005 Fabrice Bellard - * Copyright (c) 2022 Linaro Ltd - * - * SPDX-License-Identifier: LGPL-2.0+ - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "qemu/cutils.h" -#include "exec/gdbstub.h" -#include "gdbstub/syscalls.h" -#include "exec/hwaddr.h" -#include "exec/tb-flush.h" -#include "sysemu/cpus.h" -#include "sysemu/runstate.h" -#include "sysemu/replay.h" -#include "hw/core/cpu.h" -#include "hw/cpu/cluster.h" -#include "hw/boards.h" -#include "chardev/char.h" -#include "chardev/char-fe.h" -#include "monitor/monitor.h" -#include "trace.h" -#include "internals.h" - -/* System emulation specific state */ -typedef struct { - CharBackend chr; - Chardev *mon_chr; -} GDBSystemState; - -GDBSystemState gdbserver_system_state; - -static void reset_gdbserver_state(void) -{ - g_free(gdbserver_state.processes); - gdbserver_state.processes = NULL; - gdbserver_state.process_num = 0; - gdbserver_state.allow_stop_reply = false; -} - -/* - * Return the GDB index for a given vCPU state. - * - * In system mode GDB numbers CPUs from 1 as 0 is reserved as an "any - * cpu" index. - */ -int gdb_get_cpu_index(CPUState *cpu) -{ - return cpu->cpu_index + 1; -} - -/* - * We check the status of the last message in the chardev receive code - */ -bool gdb_got_immediate_ack(void) -{ - return true; -} - -/* - * GDB Connection management. For system emulation we do all of this - * via our existing Chardev infrastructure which allows us to support - * network and unix sockets. - */ - -void gdb_put_buffer(const uint8_t *buf, int len) -{ - /* - * XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks - */ - qemu_chr_fe_write_all(&gdbserver_system_state.chr, buf, len); -} - -static void gdb_chr_event(void *opaque, QEMUChrEvent event) -{ - int i; - GDBState *s = (GDBState *) opaque; - - switch (event) { - case CHR_EVENT_OPENED: - /* Start with first process attached, others detached */ - for (i = 0; i < s->process_num; i++) { - s->processes[i].attached = !i; - } - - s->c_cpu = gdb_first_attached_cpu(); - s->g_cpu = s->c_cpu; - - vm_stop(RUN_STATE_PAUSED); - replay_gdb_attached(); - break; - default: - break; - } -} - -/* - * In softmmu 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_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: - 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[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); - } - - 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); -} - -/* - * 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 *user_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 *user_ctx) -{ - if (!params->len) { - gdb_put_packet("E22"); - return; - } - - if (!get_param(params, 0)->val_ul) { - phy_memory_mode = 0; - } else { - phy_memory_mode = 1; - } - gdb_put_packet("OK"); -} - -void gdb_handle_query_rcmd(GArray *params, void *user_ctx) -{ - const guint8 zero = 0; - int len; - - if (!params->len) { - gdb_put_packet("E22"); - return; - } - - len = strlen(get_param(params, 0)->data); - if (len % 2) { - gdb_put_packet("E01"); - return; - } - - g_assert(gdbserver_state.mem_buf->len == 0); - len = len / 2; - gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len); - g_byte_array_append(gdbserver_state.mem_buf, &zero, 1); - qemu_chr_be_write(gdbserver_system_state.mon_chr, - gdbserver_state.mem_buf->data, - gdbserver_state.mem_buf->len); - gdb_put_packet("OK"); -} - -/* - * Execution state helpers - */ - -void gdb_handle_query_attached(GArray *params, void *user_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/syscalls.c b/gdbstub/syscalls.c index 02e3a8f74c6..e855df21aba 100644 --- a/gdbstub/syscalls.c +++ b/gdbstub/syscalls.c @@ -7,15 +7,16 @@ * Copyright (c) 2003-2005 Fabrice Bellard * Copyright (c) 2023 Linaro Ltd * - * SPDX-License-Identifier: LGPL-2.0+ + * 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 "system/runstate.h" #include "gdbstub/user.h" #include "gdbstub/syscalls.h" +#include "gdbstub/commands.h" #include "trace.h" #include "internals.h" @@ -154,9 +155,9 @@ void gdb_handle_file_io(GArray *params, void *user_ctx) uint64_t ret; int err; - ret = get_param(params, 0)->val_ull; + ret = gdb_get_cmd_param(params, 0)->val_ull; if (params->len >= 2) { - err = get_param(params, 1)->val_ull; + err = gdb_get_cmd_param(params, 1)->val_ull; } else { err = 0; } @@ -196,7 +197,7 @@ void gdb_handle_file_io(GArray *params, void *user_ctx) gdbserver_syscall_state.current_syscall_cb = NULL; } - if (params->len >= 3 && get_param(params, 2)->opcode == (uint8_t)'C') { + if (params->len >= 3 && gdb_get_cmd_param(params, 2)->opcode == (uint8_t)'C') { gdb_put_packet("T02"); return; } diff --git a/gdbstub/system.c b/gdbstub/system.c new file mode 100644 index 00000000000..8a32d8e1a1d --- /dev/null +++ b/gdbstub/system.c @@ -0,0 +1,668 @@ +/* + * 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 "system/accel-ops.h" +#include "system/cpus.h" +#include "system/runstate.h" +#include "system/replay.h" +#include "system/tcg.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(); + } + if (tcg_enabled()) { + 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, const 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); +} + +bool gdbserver_start(const char *device, Error **errp) +{ + Chardev *chr = NULL; + Chardev *mon_chr; + g_autoptr(GString) cs = g_string_new(device); + + if (!first_cpu) { + error_setg(errp, "gdbstub: meaningless to attach gdb to a " + "machine without any CPU."); + return false; + } + + if (!gdb_supports_guest_debug()) { + error_setg(errp, "gdbstub: current accelerator doesn't " + "support guest debugging"); + return false; + } + + if (cs->len == 0) { + error_setg(errp, "gdbstub: missing connection string"); + return false; + } + + 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) { + error_setg(errp, "gdbstub: couldn't create chardev"); + return false; + } + } + + 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 true; +} + +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) +{ + if (phy_memory_mode) { + if (is_write) { + cpu_physical_memory_write(addr, buf, len); + } else { + cpu_physical_memory_read(addr, buf, len); + } + return 0; + } + + if (cpu->cc->memory_rw_debug) { + return cpu->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 7bc79a73c42..4fd126a38c1 100644 --- a/gdbstub/trace-events +++ b/gdbstub/trace-events @@ -28,5 +28,5 @@ gdbstub_err_checksum_invalid(uint8_t ch) "got invalid command checksum digit: 0x 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" -# softmmu.c +# 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 index 6e21c3161c2..43231e695e8 100644 --- a/gdbstub/user-target.c +++ b/gdbstub/user-target.c @@ -4,11 +4,12 @@ * Copyright (c) 2003-2005 Fabrice Bellard * Copyright (c) 2022 Linaro Ltd * - * SPDX-License-Identifier: LGPL-2.0+ + * 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 @@ -204,7 +205,7 @@ int gdb_target_signal_to_gdb(int sig) int gdb_get_cpu_index(CPUState *cpu) { - TaskState *ts = (TaskState *) cpu->opaque; + TaskState *ts = get_task_state(cpu); return ts ? ts->ts_tid : -1; } @@ -216,7 +217,7 @@ void gdb_handle_query_offsets(GArray *params, void *user_ctx) { TaskState *ts; - ts = gdbserver_state.c_cpu->opaque; + 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 @@ -232,10 +233,8 @@ void gdb_handle_query_offsets(GArray *params, void *user_ctx) 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); + if (cpu->cc->memory_rw_debug) { + return cpu->cc->memory_rw_debug(cpu, addr, buf, len, is_write); } return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); } @@ -250,9 +249,9 @@ void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx) return; } - offset = get_param(params, 0)->val_ul; - len = get_param(params, 1)->val_ul; - ts = gdbserver_state.c_cpu->opaque; + 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; @@ -288,7 +287,7 @@ void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx) static const char *get_filename_param(GArray *params, int i) { - const char *hex_filename = get_param(params, i)->data; + 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); @@ -306,29 +305,29 @@ static void hostio_reply_with_data(const void *buf, size_t n) void gdb_handle_v_file_open(GArray *params, void *user_ctx) { const char *filename = get_filename_param(params, 0); - uint64_t flags = get_param(params, 1)->val_ull; - uint64_t mode = get_param(params, 2)->val_ull; + 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(gdbserver_state.g_cpu->env_ptr, 0, filename, + 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); + g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); } else { - g_string_printf(gdbserver_state.str_buf, "F%d", fd); + g_string_printf(gdbserver_state.str_buf, "F%x", fd); } gdb_put_strbuf(); } void gdb_handle_v_file_close(GArray *params, void *user_ctx) { - int fd = get_param(params, 0)->val_ul; + 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); + g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); gdb_put_strbuf(); return; } @@ -338,9 +337,9 @@ void gdb_handle_v_file_close(GArray *params, void *user_ctx) void gdb_handle_v_file_pread(GArray *params, void *user_ctx) { - int fd = get_param(params, 0)->val_ul; - size_t count = get_param(params, 1)->val_ull; - off_t offset = get_param(params, 2)->val_ull; + 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); @@ -351,7 +350,7 @@ void gdb_handle_v_file_pread(GArray *params, void *user_ctx) ssize_t n = pread(fd, buf, bufsiz, offset); if (n < 0) { - g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); + g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); gdb_put_strbuf(); return; } @@ -374,7 +373,7 @@ void gdb_handle_v_file_readlink(GArray *params, void *user_ctx) ssize_t n = readlink(filename, buf, BUFSIZ); #endif if (n < 0) { - g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); + g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); gdb_put_strbuf(); return; } @@ -383,9 +382,9 @@ void gdb_handle_v_file_readlink(GArray *params, void *user_ctx) void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx) { - uint32_t pid = get_param(params, 0)->val_ul; - uint32_t offset = get_param(params, 1)->val_ul; - uint32_t length = get_param(params, 2)->val_ul; + 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) { @@ -399,7 +398,7 @@ void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx) return; } - TaskState *ts = cpu->opaque; + TaskState *ts = get_task_state(cpu); if (!ts || !ts->bprm || !ts->bprm->filename) { gdb_put_packet("E00"); return; @@ -418,3 +417,8 @@ void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx) 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 7ab6e5d975e..67403e5a252 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -6,26 +6,101 @@ * Copyright (c) 2003-2005 Fabrice Bellard * Copyright (c) 2022 Linaro Ltd * - * SPDX-License-Identifier: LGPL-2.0+ + * 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 "qapi/error.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 "user/signal.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; @@ -113,9 +188,16 @@ void gdb_exit(int 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) +int gdb_handlesig(CPUState *cpu, int sig, const char *reason, void *siginfo, + int siginfo_len) { char buf[256]; int n; @@ -124,6 +206,18 @@ int gdb_handlesig(CPUState *cpu, int sig) 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); @@ -135,6 +229,9 @@ int gdb_handlesig(CPUState *cpu, int sig) "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; } @@ -219,33 +316,20 @@ static bool gdb_accept_socket(int gdb_fd) return true; } -static int gdbserver_open_socket(const char *path) +static int gdbserver_open_socket(const char *path, Error **errp) { - struct sockaddr_un sockaddr = {}; - int fd, ret; - - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { - perror("create socket"); - return -1; + g_autoptr(GString) buf = g_string_new(""); + char *pid_placeholder; + + pid_placeholder = strstr(path, "%d"); + if (pid_placeholder != NULL) { + g_string_append_len(buf, path, pid_placeholder - path); + g_string_append_printf(buf, "%d", qemu_get_thread_id()); + g_string_append(buf, pid_placeholder + 2); + path = buf->str; } - 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; + return unix_listen(path, errp); } static bool gdb_accept_tcp(int gdb_fd) @@ -277,14 +361,14 @@ static bool gdb_accept_tcp(int gdb_fd) return true; } -static int gdbserver_open_port(int port) +static int gdbserver_open_port(int port, Error **errp) { struct sockaddr_in sockaddr; int fd, ret; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { - perror("socket"); + error_setg_errno(errp, errno, "Failed to create socket"); return -1; } qemu_set_cloexec(fd); @@ -296,13 +380,13 @@ static int gdbserver_open_port(int port) sockaddr.sin_addr.s_addr = 0; ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); if (ret < 0) { - perror("bind"); + error_setg_errno(errp, errno, "Failed to bind socket"); close(fd); return -1; } ret = listen(fd, 1); if (ret < 0) { - perror("listen"); + error_setg_errno(errp, errno, "Failed to listen to socket"); close(fd); return -1; } @@ -310,43 +394,310 @@ static int gdbserver_open_port(int port) return fd; } -int gdbserver_start(const char *port_or_path) +static bool gdbserver_accept(int port, int gdb_fd, const char *path) { - int port = g_ascii_strtoull(port_or_path, NULL, 10); - int gdb_fd; + bool ret; if (port > 0) { - gdb_fd = gdbserver_open_port(port); + ret = gdb_accept_tcp(gdb_fd); } else { - gdb_fd = gdbserver_open_socket(port_or_path); + ret = gdb_accept_socket(gdb_fd); + if (ret) { + gdbserver_user_state.socket_path = g_strdup(path); + } } - if (gdb_fd < 0) { - return -1; + if (!ret) { + close(gdb_fd); + } + + return ret; +} + +struct { + int port; + int gdb_fd; + char *path; +} gdbserver_args; + +static void do_gdb_handlesig(CPUState *cs, run_on_cpu_data arg) +{ + int sig; + + sig = target_to_host_signal(gdb_handlesig(cs, 0, NULL, NULL, 0)); + if (sig >= 1 && sig < NSIG) { + qemu_kill_thread(gdb_get_cpu_index(cs), sig); + } +} + +static void *gdbserver_accept_thread(void *arg) +{ + if (gdbserver_accept(gdbserver_args.port, gdbserver_args.gdb_fd, + gdbserver_args.path)) { + CPUState *cs = first_cpu; + + async_safe_run_on_cpu(cs, do_gdb_handlesig, RUN_ON_CPU_NULL); + qemu_kill_thread(gdb_get_cpu_index(cs), host_interrupt_signal); + } + + g_free(gdbserver_args.path); + gdbserver_args.path = NULL; + + return NULL; +} + +#define USAGE "\nUsage: -g {port|path}[,suspend={y|n}]" + +bool gdbserver_start(const char *args, Error **errp) +{ + g_auto(GStrv) argv = g_strsplit(args, ",", 0); + const char *port_or_path = NULL; + bool suspend = true; + int gdb_fd, port; + GStrv arg; + + for (arg = argv; *arg; arg++) { + g_auto(GStrv) tokens = g_strsplit(*arg, "=", 2); + + if (g_strcmp0(tokens[0], "suspend") == 0) { + if (tokens[1] == NULL) { + error_setg(errp, + "gdbstub: missing \"suspend\" option value" USAGE); + return false; + } else if (!qapi_bool_parse(tokens[0], tokens[1], + &suspend, errp)) { + return false; + } + } else { + if (port_or_path) { + error_setg(errp, "gdbstub: unknown option \"%s\"" USAGE, *arg); + return false; + } + port_or_path = *arg; + } + } + if (!port_or_path) { + error_setg(errp, "gdbstub: port or path not specified" USAGE); + return false; } - 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; + port = g_ascii_strtoull(port_or_path, NULL, 10); + if (port > 0) { + gdb_fd = gdbserver_open_port(port, errp); + } else { + gdb_fd = gdbserver_open_socket(port_or_path, errp); + } + if (gdb_fd < 0) { + return false; } - /* gone wrong */ - close(gdb_fd); - return -1; + if (suspend) { + if (gdbserver_accept(port, gdb_fd, port_or_path)) { + gdb_handlesig(first_cpu, 0, NULL, NULL, 0); + return true; + } else { + error_setg(errp, "gdbstub: failed to accept connection"); + return false; + } + } else { + QemuThread thread; + + gdbserver_args.port = port; + gdbserver_args.gdb_fd = gdb_fd; + gdbserver_args.path = g_strdup(port_or_path); + qemu_thread_create(&thread, "gdb-accept", + &gdbserver_accept_thread, NULL, + QEMU_THREAD_DETACHED); + return true; + } } -/* Disable gdb stub for child processes. */ -void gdbserver_fork(CPUState *cpu) +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_breakpoint_remove_all(cpu, BP_GDB); - /* no cpu_watchpoint_remove_all for user-mode */ + 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; } /* @@ -392,11 +743,8 @@ int gdb_continue_partial(char *newstates) 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); + if (cpu->cc->memory_rw_debug) { + return cpu->cc->memory_rw_debug(cpu, addr, buf, len, is_write); } return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); } @@ -491,5 +839,117 @@ void gdb_breakpoint_remove_all(CPUState *cs) void gdb_syscall_handling(const char *syscall_packet) { gdb_put_packet(syscall_packet); - gdb_handlesig(gdbserver_state.c_cpu, 0); + 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/hmp-commands-info.hx b/hmp-commands-info.hx index f5b37eb74ab..639a450ee51 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 = "", @@ -488,9 +475,9 @@ ERST { .name = "migrate", - .args_type = "", - .params = "", - .help = "show migration status", + .args_type = "all:-a", + .params = "[-a]", + .help = "show migration status (-a: all, dump all status)", .cmd = hmp_info_migrate, }, @@ -540,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, }, @@ -905,7 +892,7 @@ ERST }, SRST - ``stats`` + ``info stats`` Show runtime-collected statistics ERST diff --git a/hmp-commands.hx b/hmp-commands.hx index 2cbd0f77a02..06746f0afc3 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. @@ -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", @@ -396,19 +398,6 @@ SRST If called with option off, the emulation returns to normal mode. ERST - { - .name = "singlestep", - .args_type = "option:s?", - .params = "[on|off]", - .help = "deprecated synonym for one-insn-per-tb", - .cmd = hmp_one_insn_per_tb, - }, - -SRST -``singlestep [off]`` - This is a deprecated synonym for the one-insn-per-tb command. -ERST - { .name = "stop|s", .args_type = "", @@ -920,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 { @@ -1085,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" @@ -1115,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 @@ -1296,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 @@ -1415,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, diff --git a/host/include/aarch64/host/atomic128-cas.h b/host/include/aarch64/host/atomic128-cas.h index 58630107bcc..991da4ef543 100644 --- a/host/include/aarch64/host/atomic128-cas.h +++ b/host/include/aarch64/host/atomic128-cas.h @@ -13,7 +13,7 @@ /* 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" +#include "host/include/generic/host/atomic128-cas.h.inc" #else static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) { diff --git a/host/include/aarch64/host/atomic128-ldst.h b/host/include/aarch64/host/atomic128-ldst.h.inc similarity index 100% rename from host/include/aarch64/host/atomic128-ldst.h rename to host/include/aarch64/host/atomic128-ldst.h.inc diff --git a/host/include/aarch64/host/bufferiszero.c.inc b/host/include/aarch64/host/bufferiszero.c.inc new file mode 100644 index 00000000000..947ee7ca1f6 --- /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 index 769626b0988..fe671534e40 100644 --- a/host/include/aarch64/host/cpuinfo.h +++ b/host/include/aarch64/host/cpuinfo.h @@ -10,6 +10,8 @@ #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; diff --git a/host/include/aarch64/host/crypto/clmul.h b/host/include/aarch64/host/crypto/clmul.h new file mode 100644 index 00000000000..bb516d8b2f8 --- /dev/null +++ b/host/include/aarch64/host/crypto/clmul.h @@ -0,0 +1,41 @@ +/* + * AArch64 specific clmul acceleration. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef AARCH64_HOST_CRYPTO_CLMUL_H +#define AARCH64_HOST_CRYPTO_CLMUL_H + +#include "host/cpuinfo.h" +#include + +/* + * 64x64->128 pmull is available with FEAT_PMULL. + * Both FEAT_AES and FEAT_PMULL are covered under the same macro. + */ +#ifdef __ARM_FEATURE_AES +# define HAVE_CLMUL_ACCEL true +#else +# define HAVE_CLMUL_ACCEL likely(cpuinfo & CPUINFO_PMULL) +#endif +#if !defined(__ARM_FEATURE_AES) && defined(CONFIG_ARM_AES_BUILTIN) +# define ATTR_CLMUL_ACCEL __attribute__((target("+crypto"))) +#else +# define ATTR_CLMUL_ACCEL +#endif + +static inline Int128 ATTR_CLMUL_ACCEL +clmul_64_accel(uint64_t n, uint64_t m) +{ + union { poly128_t v; Int128 s; } u; + +#ifdef CONFIG_ARM_AES_BUILTIN + u.v = vmull_p64((poly64_t)n, (poly64_t)m); +#else + asm(".arch_extension aes\n\t" + "pmull %0.1q, %1.1d, %2.1d" : "=w"(u.v) : "w"(n), "w"(m)); +#endif + return u.s; +} + +#endif /* AARCH64_HOST_CRYPTO_CLMUL_H */ diff --git a/host/include/aarch64/host/load-extract-al16-al8.h b/host/include/aarch64/host/load-extract-al16-al8.h.inc similarity index 100% rename from host/include/aarch64/host/load-extract-al16-al8.h rename to host/include/aarch64/host/load-extract-al16-al8.h.inc diff --git a/host/include/aarch64/host/store-insert-al16.h b/host/include/aarch64/host/store-insert-al16.h.inc similarity index 100% rename from host/include/aarch64/host/store-insert-al16.h rename to host/include/aarch64/host/store-insert-al16.h.inc diff --git a/host/include/generic/host/atomic128-cas.h b/host/include/generic/host/atomic128-cas.h deleted file mode 100644 index 991d3da0820..00000000000 --- a/host/include/generic/host/atomic128-cas.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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) -{ - __int128_t *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-cas.h.inc b/host/include/generic/host/atomic128-cas.h.inc new file mode 100644 index 00000000000..6b40cc22710 --- /dev/null +++ b/host/include/generic/host/atomic128-cas.h.inc @@ -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 deleted file mode 100644 index 80fff0643a6..00000000000 --- a/host/include/generic/host/atomic128-ldst.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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) -{ - __int128_t *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/atomic128-ldst.h.inc b/host/include/generic/host/atomic128-ldst.h.inc new file mode 100644 index 00000000000..691e6a85313 --- /dev/null +++ b/host/include/generic/host/atomic128-ldst.h.inc @@ -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 00000000000..ea0875c24a4 --- /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/crypto/clmul.h b/host/include/generic/host/crypto/clmul.h new file mode 100644 index 00000000000..915bfb88d36 --- /dev/null +++ b/host/include/generic/host/crypto/clmul.h @@ -0,0 +1,15 @@ +/* + * No host specific carry-less multiply acceleration. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef GENERIC_HOST_CRYPTO_CLMUL_H +#define GENERIC_HOST_CRYPTO_CLMUL_H + +#define HAVE_CLMUL_ACCEL false +#define ATTR_CLMUL_ACCEL + +Int128 clmul_64_accel(uint64_t, uint64_t) + QEMU_ERROR("unsupported accel"); + +#endif /* GENERIC_HOST_CRYPTO_CLMUL_H */ diff --git a/host/include/generic/host/load-extract-al16-al8.h b/host/include/generic/host/load-extract-al16-al8.h.inc similarity index 100% rename from host/include/generic/host/load-extract-al16-al8.h rename to host/include/generic/host/load-extract-al16-al8.h.inc diff --git a/host/include/generic/host/store-insert-al16.h b/host/include/generic/host/store-insert-al16.h.inc similarity index 100% rename from host/include/generic/host/store-insert-al16.h rename to host/include/generic/host/store-insert-al16.h.inc diff --git a/host/include/i386/host/bufferiszero.c.inc b/host/include/i386/host/bufferiszero.c.inc new file mode 100644 index 00000000000..74ae98580f6 --- /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 index 073d0a426f3..9541a64da61 100644 --- a/host/include/i386/host/cpuinfo.h +++ b/host/include/i386/host/cpuinfo.h @@ -1,6 +1,6 @@ /* * SPDX-License-Identifier: GPL-2.0-or-later - * Host specific cpu indentification for x86. + * Host specific cpu identification for x86. */ #ifndef HOST_CPUINFO_H @@ -9,14 +9,13 @@ /* Digested version of */ #define CPUINFO_ALWAYS (1u << 0) /* so cpuinfo is nonzero */ -#define CPUINFO_CMOV (1u << 1) +#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_SSE4 (1u << 8) #define CPUINFO_AVX1 (1u << 9) #define CPUINFO_AVX2 (1u << 10) #define CPUINFO_AVX512F (1u << 11) @@ -27,6 +26,7 @@ #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; diff --git a/host/include/i386/host/crypto/clmul.h b/host/include/i386/host/crypto/clmul.h new file mode 100644 index 00000000000..dc3c814797b --- /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.inc b/host/include/loongarch64/host/atomic128-ldst.h.inc new file mode 100644 index 00000000000..754d2143f0c --- /dev/null +++ b/host/include/loongarch64/host/atomic128-ldst.h.inc @@ -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 00000000000..bb2598fdc3f --- /dev/null +++ b/host/include/loongarch64/host/bufferiszero.c.inc @@ -0,0 +1,145 @@ +/* + * 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 00000000000..d7bf27501d8 --- /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 00000000000..9528521e7d7 --- /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 00000000000..919fd8d7440 --- /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 index 29ee7f9ef81..38b8eabe2a8 100644 --- a/host/include/ppc/host/cpuinfo.h +++ b/host/include/ppc/host/cpuinfo.h @@ -1,6 +1,6 @@ /* * SPDX-License-Identifier: GPL-2.0-or-later - * Host specific cpu indentification for ppc. + * Host specific cpu identification for ppc. */ #ifndef HOST_CPUINFO_H diff --git a/host/include/riscv/host/cpuinfo.h b/host/include/riscv/host/cpuinfo.h new file mode 100644 index 00000000000..b2b53dbf627 --- /dev/null +++ b/host/include/riscv/host/cpuinfo.h @@ -0,0 +1,26 @@ +/* + * 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_ZBS (1u << 3) +#define CPUINFO_ZICOND (1u << 4) +#define CPUINFO_ZVE64X (1u << 5) + +/* 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 deleted file mode 100644 index 8d6f909d3c9..00000000000 --- a/host/include/x86_64/host/atomic128-ldst.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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/atomic128-ldst.h.inc b/host/include/x86_64/host/atomic128-ldst.h.inc new file mode 100644 index 00000000000..4c698e3246f --- /dev/null +++ b/host/include/x86_64/host/atomic128-ldst.h.inc @@ -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.inc" +#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 00000000000..1d3f1fd6f51 --- /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/crypto/clmul.h b/host/include/x86_64/host/crypto/clmul.h new file mode 100644 index 00000000000..f25eced416e --- /dev/null +++ b/host/include/x86_64/host/crypto/clmul.h @@ -0,0 +1 @@ +#include "host/include/i386/host/crypto/clmul.h" diff --git a/host/include/x86_64/host/load-extract-al16-al8.h b/host/include/x86_64/host/load-extract-al16-al8.h deleted file mode 100644 index baa506b7b5b..00000000000 --- a/host/include/x86_64/host/load-extract-al16-al8.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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/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 00000000000..b837c378684 --- /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.inc" + +/** + * 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 1b1f3b9ec81..31e216227cb 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -766,16 +766,19 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, return err; } -static int local_fstat(FsContext *fs_ctx, int fid_type, - V9fsFidOpenState *fs, struct stat *stbuf) +static int local_fid_fd(int fid_type, V9fsFidOpenState *fs) { - int err, fd; - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir.stream); + return dirfd(fs->dir.stream); } else { - fd = fs->fd; + return fs->fd; } +} + +static int local_fstat(FsContext *fs_ctx, int fid_type, + V9fsFidOpenState *fs, struct stat *stbuf) +{ + int err, fd = local_fid_fd(fid_type, fs); err = fstat(fd, stbuf); if (err) { @@ -1039,6 +1042,14 @@ static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) return ret; } +static int local_ftruncate(FsContext *ctx, int fid_type, V9fsFidOpenState *fs, + off_t size) +{ + int fd = local_fid_fd(fid_type, fs); + + return ftruncate(fd, size); +} + static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) { char *dirpath = g_path_get_dirname(fs_path->data); @@ -1089,6 +1100,14 @@ static int local_utimensat(FsContext *s, V9fsPath *fs_path, return ret; } +static int local_futimens(FsContext *s, int fid_type, V9fsFidOpenState *fs, + const struct timespec *times) +{ + int fd = local_fid_fd(fid_type, fs); + + return qemu_futimens(fd, times); +} + static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name, int flags) { @@ -1167,13 +1186,7 @@ static int local_remove(FsContext *ctx, const char *path) static int local_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; - } + int fd = local_fid_fd(fid_type, fs); if (datasync) { return qemu_fdatasync(fd); @@ -1538,6 +1551,9 @@ static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) "[remap|forbid|warn]\n"); return -1; } + } else { + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; + fse->export_flags |= V9FS_REMAP_INODES; } if (!path) { @@ -1572,6 +1588,13 @@ static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) return 0; } +static bool local_has_valid_file_handle(int fid_type, V9fsFidOpenState *fs) +{ + return + (fid_type == P9_FID_FILE && fs->fd != -1) || + (fid_type == P9_FID_DIR && fs->dir.stream != NULL); +} + FileOperations local_ops = { .parse_opts = local_parse_opts, .init = local_init, @@ -1609,4 +1632,7 @@ FileOperations local_ops = { .name_to_path = local_name_to_path, .renameat = local_renameat, .unlinkat = local_unlinkat, + .has_valid_file_handle = local_has_valid_file_handle, + .ftruncate = local_ftruncate, + .futimens = local_futimens, }; diff --git a/hw/9pfs/9p-proxy.c b/hw/9pfs/9p-proxy.c deleted file mode 100644 index 7aac49ad4ad..00000000000 --- a/hw/9pfs/9p-proxy.c +++ /dev/null @@ -1,1279 +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 - */ - -/* - * NOTE: The 9p 'proxy' backend is deprecated (since QEMU 8.1) and will be - * removed in a future version of QEMU! - */ - -#include "qemu/osdep.h" -#include -#include -#include "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 specified. - */ - 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 9be4718d3e2..00000000000 --- a/hw/9pfs/9p-proxy.h +++ /dev/null @@ -1,101 +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. - */ - -/* - * NOTE: The 9p 'proxy' backend is deprecated (since QEMU 8.1) and will be - * removed in a future version of QEMU! - */ - -#ifndef QEMU_9P_PROXY_H -#define QEMU_9P_PROXY_H - -#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 0ac79a500b4..9cd18842242 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c @@ -24,7 +24,7 @@ #include "qemu/rcu.h" #include "qemu/rcu_queue.h" #include "qemu/cutils.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" /* Root node for synth file system */ static V9fsSynthNode synth_root = { @@ -356,6 +356,13 @@ static int synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset) return -1; } +static int synth_ftruncate(FsContext *ctx, int fid_type, V9fsFidOpenState *fs, + off_t size) +{ + errno = ENOSYS; + return -1; +} + static int synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp) { errno = EPERM; @@ -417,6 +424,13 @@ static int synth_utimensat(FsContext *fs_ctx, V9fsPath *path, return 0; } +static int synth_futimens(FsContext *fs_ctx, int fid_type, V9fsFidOpenState *fs, + const struct timespec *buf) +{ + errno = ENOSYS; + return -1; +} + static int synth_remove(FsContext *ctx, const char *path) { errno = EPERM; @@ -615,6 +629,11 @@ static int synth_init(FsContext *ctx, Error **errp) return 0; } +static bool synth_has_valid_file_handle(int fid_type, V9fsFidOpenState *fs) +{ + return false; +} + FileOperations synth_ops = { .init = synth_init, .lstat = synth_lstat, @@ -650,4 +669,7 @@ FileOperations synth_ops = { .name_to_path = synth_name_to_path, .renameat = synth_renameat, .unlinkat = synth_unlinkat, + .has_valid_file_handle = synth_has_valid_file_handle, + .ftruncate = synth_ftruncate, + .futimens = synth_futimens, }; diff --git a/hw/9pfs/9p-util-generic.c b/hw/9pfs/9p-util-generic.c new file mode 100644 index 00000000000..4c1e9c887d0 --- /dev/null +++ b/hw/9pfs/9p-util-generic.c @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "9p-util.h" +#include + +char *qemu_open_flags_tostr(int flags) +{ + int acc = flags & O_ACCMODE; + return g_strconcat( + (acc == O_WRONLY) ? "WRONLY" : (acc == O_RDONLY) ? "RDONLY" : "RDWR", + (flags & O_CREAT) ? "|CREAT" : "", + (flags & O_EXCL) ? "|EXCL" : "", + (flags & O_NOCTTY) ? "|NOCTTY" : "", + (flags & O_TRUNC) ? "|TRUNC" : "", + (flags & O_APPEND) ? "|APPEND" : "", + (flags & O_NONBLOCK) ? "|NONBLOCK" : "", + (flags & O_DSYNC) ? "|DSYNC" : "", + #ifdef O_DIRECT + (flags & O_DIRECT) ? "|DIRECT" : "", + #endif + (flags & O_LARGEFILE) ? "|LARGEFILE" : "", + (flags & O_DIRECTORY) ? "|DIRECTORY" : "", + (flags & O_NOFOLLOW) ? "|NOFOLLOW" : "", + #ifdef O_NOATIME + (flags & O_NOATIME) ? "|NOATIME" : "", + #endif + #ifdef O_CLOEXEC + (flags & O_CLOEXEC) ? "|CLOEXEC" : "", + #endif + #ifdef __O_SYNC + (flags & __O_SYNC) ? "|SYNC" : "", + #else + ((flags & O_SYNC) == O_SYNC) ? "|SYNC" : "", + #endif + #ifdef O_PATH + (flags & O_PATH) ? "|PATH" : "", + #endif + #ifdef __O_TMPFILE + (flags & __O_TMPFILE) ? "|TMPFILE" : "", + #elif defined(O_TMPFILE) + ((flags & O_TMPFILE) == O_TMPFILE) ? "|TMPFILE" : "", + #endif + /* O_NDELAY is usually just an alias of O_NONBLOCK */ + #if defined(O_NDELAY) && O_NDELAY != O_NONBLOCK + (flags & O_NDELAY) ? "|NDELAY" : "", + #endif + NULL /* always last (required NULL termination) */ + ); +} diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index 51c94b01162..a1924fe3f05 100644 --- a/hw/9pfs/9p-util.h +++ b/hw/9pfs/9p-util.h @@ -103,6 +103,7 @@ static inline int errno_to_dotl(int err) { #define qemu_renameat renameat #define qemu_utimensat utimensat #define qemu_unlinkat unlinkat +#define qemu_futimens futimens static inline void close_preserve_errno(int fd) { @@ -177,20 +178,27 @@ static inline int openat_file(int dirfd, const char *name, int flags, return -1; } - if (close_if_special_file(fd) < 0) { - return -1; - } - - serrno = errno; - /* O_NONBLOCK was only needed to open the file. Let's drop it. We don't - * do that with O_PATH since fcntl(F_SETFL) isn't supported, and openat() - * ignored it anyway. - */ + /* Only if O_PATH is not set ... */ if (!(flags & O_PATH_9P_UTIL)) { + /* + * Prevent I/O on special files (device files, etc.) on host side, + * however it is safe and required to allow opening them with O_PATH, + * as this is limited to (required) path based operations only. + */ + if (close_if_special_file(fd) < 0) { + return -1; + } + + serrno = errno; + /* + * O_NONBLOCK was only needed to open the file. Let's drop it. We don't + * do that with O_PATH since fcntl(F_SETFL) isn't supported, and + * openat() ignored it anyway. + */ ret = fcntl(fd, F_SETFL, flags); assert(!ret); + errno = serrno; } - errno = serrno; return fd; } @@ -260,4 +268,10 @@ int pthread_fchdir_np(int fd) __attribute__((weak_import)); #endif int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev); +/* + * Returns a newly allocated string presentation of open() flags, intended + * for debugging (tracing) purposes only. + */ +char *qemu_open_flags_tostr(int flags); + #endif diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 323f042e655..8b001b91120 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -406,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); } @@ -438,16 +434,24 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu) V9fsFidState *f; GHashTableIter iter; gpointer fid; + int err; + int nclosed = 0; + + /* prevent multiple coroutines running this function simultaniously */ + if (s->reclaiming) { + return; + } + s->reclaiming = true; g_hash_table_iter_init(&iter, s->fids); QSLIST_HEAD(, V9fsFidState) reclaim_list = QSLIST_HEAD_INITIALIZER(reclaim_list); + /* Pick FIDs to be closed, collect them on reclaim_list. */ while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &f)) { /* - * Unlink fids cannot be reclaimed. Check - * for them and skip them. Also skip fids + * Unlinked fids cannot be reclaimed, skip those, and also skip fids * currently being operated on. */ if (f->ref || f->flags & FID_NON_RECLAIMABLE) { @@ -497,23 +501,42 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu) } } /* - * Now close the fid in reclaim list. Free them if they - * are already clunked. + * Close the picked FIDs altogether on a background I/O driver thread. Do + * this all at once to keep latency (i.e. amount of thread hops between main + * thread <-> fs driver background thread) as low as possible. */ + v9fs_co_run_in_worker({ + QSLIST_FOREACH(f, &reclaim_list, reclaim_next) { + err = (f->fid_type == P9_FID_DIR) ? + s->ops->closedir(&s->ctx, &f->fs_reclaim) : + s->ops->close(&s->ctx, &f->fs_reclaim); + + /* 'man 2 close' suggests to ignore close() errors except of EBADF */ + if (unlikely(err && errno == EBADF)) { + /* + * unexpected case as FIDs were picked above by having a valid + * file descriptor + */ + error_report("9pfs: v9fs_reclaim_fd() WARNING: close() failed with EBADF"); + } else { + /* total_open_fd must only be mutated on main thread */ + nclosed++; + } + } + }); + total_open_fd -= nclosed; + /* Free the closed FIDs. */ while (!QSLIST_EMPTY(&reclaim_list)) { f = QSLIST_FIRST(&reclaim_list); QSLIST_REMOVE(&reclaim_list, f, V9fsFidState, reclaim_next); - if (f->fid_type == P9_FID_FILE) { - v9fs_co_close(pdu, &f->fs_reclaim); - } else if (f->fid_type == P9_FID_DIR) { - v9fs_co_closedir(pdu, &f->fs_reclaim); - } /* * Now drop the fid reference, free it * if clunked. */ put_fid(pdu, f); } + + s->reclaiming = false; } /* @@ -1505,10 +1528,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; } @@ -1580,6 +1601,11 @@ static void coroutine_fn v9fs_stat(void *opaque) pdu_complete(pdu, err); } +static bool fid_has_valid_file_handle(V9fsState *s, V9fsFidState *fidp) +{ + return s->ops->has_valid_file_handle(fidp->fid_type, &fidp->fs); +} + static void coroutine_fn v9fs_getattr(void *opaque) { int32_t fid; @@ -1602,11 +1628,11 @@ 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 (fid_has_valid_file_handle(pdu->s, fidp)) { + retval = v9fs_co_fstat(pdu, fidp, &stbuf); + } else { + retval = v9fs_co_lstat(pdu, &fidp->path, &stbuf); + } if (retval < 0) { goto out; } @@ -1709,7 +1735,11 @@ static void coroutine_fn v9fs_setattr(void *opaque) } else { times[1].tv_nsec = UTIME_OMIT; } - err = v9fs_co_utimensat(pdu, &fidp->path, times); + if (fid_has_valid_file_handle(pdu->s, fidp)) { + err = v9fs_co_futimens(pdu, fidp, times); + } else { + err = v9fs_co_utimensat(pdu, &fidp->path, times); + } if (err < 0) { goto out; } @@ -1734,7 +1764,11 @@ static void coroutine_fn v9fs_setattr(void *opaque) } } if (v9iattr.valid & (P9_ATTR_SIZE)) { - err = v9fs_co_truncate(pdu, &fidp->path, v9iattr.size); + if (fid_has_valid_file_handle(pdu->s, fidp)) { + err = v9fs_co_ftruncate(pdu, fidp, v9iattr.size); + } else { + err = v9fs_co_truncate(pdu, &fidp->path, v9iattr.size); + } if (err < 0) { goto out; } @@ -1778,6 +1812,21 @@ static bool same_stat_id(const struct stat *a, const struct stat *b) return a->st_dev == b->st_dev && a->st_ino == b->st_ino; } +/* + * Returns a (newly allocated) comma-separated string presentation of the + * passed array for logging (tracing) purpose for trace event "v9fs_walk". + * + * It is caller's responsibility to free the returned string. + */ +static char *trace_v9fs_walk_wnames(V9fsString *wnames, size_t nwnames) +{ + g_autofree char **arr = g_malloc0_n(nwnames + 1, sizeof(char *)); + for (size_t i = 0; i < nwnames; ++i) { + arr[i] = wnames[i].data; + } + return g_strjoinv(", ", arr); +} + static void coroutine_fn v9fs_walk(void *opaque) { int name_idx, nwalked; @@ -1791,6 +1840,7 @@ static void coroutine_fn v9fs_walk(void *opaque) size_t offset = 7; int32_t fid, newfid; P9ARRAY_REF(V9fsString) wnames = NULL; + g_autofree char *trace_wnames = NULL; V9fsFidState *fidp; V9fsFidState *newfidp = NULL; V9fsPDU *pdu = opaque; @@ -1804,11 +1854,9 @@ static void coroutine_fn v9fs_walk(void *opaque) } offset += err; - trace_v9fs_walk(pdu->tag, pdu->id, fid, newfid, nwnames); - if (nwnames > P9_MAXWELEM) { err = -EINVAL; - goto out_nofid; + goto out_nofid_nownames; } if (nwnames) { P9ARRAY_NEW(V9fsString, wnames, nwnames); @@ -1818,15 +1866,23 @@ static void coroutine_fn v9fs_walk(void *opaque) for (i = 0; i < nwnames; i++) { err = pdu_unmarshal(pdu, offset, "s", &wnames[i]); if (err < 0) { - goto out_nofid; + goto out_nofid_nownames; } if (name_is_illegal(wnames[i].data)) { err = -ENOENT; - goto out_nofid; + goto out_nofid_nownames; } offset += err; } + if (trace_event_get_state_backends(TRACE_V9FS_WALK)) { + trace_wnames = trace_v9fs_walk_wnames(wnames, nwnames); + trace_v9fs_walk(pdu->tag, pdu->id, fid, newfid, nwnames, + trace_wnames); + } + } else { + trace_v9fs_walk(pdu->tag, pdu->id, fid, newfid, nwnames, ""); } + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; @@ -1961,7 +2017,11 @@ static void coroutine_fn v9fs_walk(void *opaque) } v9fs_path_free(&dpath); v9fs_path_free(&path); + goto out_pdu_complete; +out_nofid_nownames: + trace_v9fs_walk(pdu->tag, pdu->id, fid, newfid, nwnames, ""); out_nofid: +out_pdu_complete: pdu_complete(pdu, err); } @@ -1986,6 +2046,7 @@ static void coroutine_fn v9fs_open(void *opaque) V9fsFidState *fidp; V9fsPDU *pdu = opaque; V9fsState *s = pdu->s; + g_autofree char *trace_oflags = NULL; if (s->proto_version == V9FS_PROTO_2000L) { err = pdu_unmarshal(pdu, offset, "dd", &fid, &mode); @@ -1997,7 +2058,13 @@ static void coroutine_fn v9fs_open(void *opaque) if (err < 0) { goto out_nofid; } - trace_v9fs_open(pdu->tag, pdu->id, fid, mode); + if (trace_event_get_state_backends(TRACE_V9FS_OPEN)) { + trace_oflags = qemu_open_flags_tostr( + (s->proto_version == V9FS_PROTO_2000L) ? + dotl_to_open_flags(mode) : omode_to_uflags(mode) + ); + trace_v9fs_open(pdu->tag, pdu->id, fid, mode, trace_oflags); + } fidp = get_fid(pdu, fid); if (fidp == NULL) { @@ -2593,6 +2660,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; @@ -4290,6 +4362,8 @@ int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t, s->ctx.fst = &fse->fst; fsdev_throttle_init(s->ctx.fst); + s->reclaiming = false; + rc = 0; out: if (rc) { diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index a6f59abccbf..259ad32ed18 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -280,7 +280,6 @@ struct V9fsFidState { uid_t uid; int ref; bool clunked; - QSIMPLEQ_ENTRY(V9fsFidState) next; QSLIST_ENTRY(V9fsFidState) reclaim_next; }; @@ -363,6 +362,7 @@ struct V9fsState { uint64_t qp_ndevices; /* Amount of entries in qpd_table. */ uint16_t qp_affix_next; uint64_t qp_fullpath_next; + bool reclaiming; }; /* 9p2000.L open flags */ diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c index 2068a4779d4..bce7dd96e91 100644 --- a/hw/9pfs/codir.c +++ b/hw/9pfs/codir.c @@ -20,6 +20,7 @@ #include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" #include "qemu/main-loop.h" +#include "qemu/error-report.h" #include "coth.h" #include "9p-xattr.h" #include "9p-util.h" @@ -353,7 +354,11 @@ int coroutine_fn v9fs_co_closedir(V9fsPDU *pdu, V9fsFidOpenState *fs) err = -errno; } }); - if (!err) { + /* 'man 2 close' suggests to ignore close() errors except of EBADF */ + if (unlikely(err && errno == EBADF)) { + /* unexpected case as we should have checked for a valid file handle */ + error_report("9pfs: WARNING: v9fs_co_closedir() failed with EBADF"); + } else { total_open_fd--; } return err; diff --git a/hw/9pfs/cofile.c b/hw/9pfs/cofile.c index 9c5344039eb..6e775c8e412 100644 --- a/hw/9pfs/cofile.c +++ b/hw/9pfs/cofile.c @@ -20,6 +20,7 @@ #include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" #include "qemu/main-loop.h" +#include "qemu/error-report.h" #include "coth.h" int coroutine_fn v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode, @@ -197,7 +198,11 @@ int coroutine_fn v9fs_co_close(V9fsPDU *pdu, V9fsFidOpenState *fs) err = -errno; } }); - if (!err) { + /* 'man 2 close' suggests to ignore close() errors except of EBADF */ + if (unlikely(err && errno == EBADF)) { + /* unexpected case as we should have checked for a valid file handle */ + error_report("9pfs: WARNING: v9fs_co_close() failed with EBADF"); + } else { total_open_fd--; } return err; @@ -252,7 +257,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); @@ -272,7 +277,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 67e3ae5c5cc..12fa8c9fe9c 100644 --- a/hw/9pfs/cofs.c +++ b/hw/9pfs/cofs.c @@ -139,6 +139,25 @@ int coroutine_fn v9fs_co_utimensat(V9fsPDU *pdu, V9fsPath *path, return err; } +int coroutine_fn v9fs_co_futimens(V9fsPDU *pdu, V9fsFidState *fidp, + struct timespec times[2]) +{ + int err; + V9fsState *s = pdu->s; + + if (v9fs_request_cancelled(pdu)) { + return -EINTR; + } + v9fs_co_run_in_worker( + { + err = s->ops->futimens(&s->ctx, fidp->fid_type, &fidp->fs, times); + if (err < 0) { + err = -errno; + } + }); + return err; +} + int coroutine_fn v9fs_co_chown(V9fsPDU *pdu, V9fsPath *path, uid_t uid, gid_t gid) { @@ -184,6 +203,24 @@ int coroutine_fn v9fs_co_truncate(V9fsPDU *pdu, V9fsPath *path, off_t size) return err; } +int coroutine_fn v9fs_co_ftruncate(V9fsPDU *pdu, V9fsFidState *fidp, off_t size) +{ + int err; + V9fsState *s = pdu->s; + + if (v9fs_request_cancelled(pdu)) { + return -EINTR; + } + v9fs_co_run_in_worker( + { + err = s->ops->ftruncate(&s->ctx, fidp->fid_type, &fidp->fs, size); + if (err < 0) { + err = -errno; + } + }); + return err; +} + int coroutine_fn v9fs_co_mknod(V9fsPDU *pdu, V9fsFidState *fidp, V9fsString *name, uid_t uid, gid_t gid, dev_t dev, mode_t mode, struct stat *stbuf) diff --git a/hw/9pfs/coth.h b/hw/9pfs/coth.h index 2c54249b357..7906fa7782d 100644 --- a/hw/9pfs/coth.h +++ b/hw/9pfs/coth.h @@ -71,8 +71,12 @@ int coroutine_fn v9fs_co_statfs(V9fsPDU *, V9fsPath *, struct statfs *); int coroutine_fn v9fs_co_lstat(V9fsPDU *, V9fsPath *, struct stat *); int coroutine_fn v9fs_co_chmod(V9fsPDU *, V9fsPath *, mode_t); int coroutine_fn v9fs_co_utimensat(V9fsPDU *, V9fsPath *, struct timespec [2]); +int coroutine_fn v9fs_co_futimens(V9fsPDU *pdu, V9fsFidState *fidp, + struct timespec times[2]); int coroutine_fn v9fs_co_chown(V9fsPDU *, V9fsPath *, uid_t, gid_t); int coroutine_fn v9fs_co_truncate(V9fsPDU *, V9fsPath *, off_t); +int coroutine_fn v9fs_co_ftruncate(V9fsPDU *pdu, V9fsFidState *fidp, + off_t size); int coroutine_fn v9fs_co_llistxattr(V9fsPDU *, V9fsPath *, void *, size_t); int coroutine_fn v9fs_co_lgetxattr(V9fsPDU *, V9fsPath *, V9fsString *, void *, size_t); diff --git a/hw/9pfs/meson.build b/hw/9pfs/meson.build index 2944ea63c38..d35d4f44ffa 100644 --- a/hw/9pfs/meson.build +++ b/hw/9pfs/meson.build @@ -2,8 +2,8 @@ fs_ss = ss.source_set() fs_ss.add(files( '9p-local.c', '9p-posix-acl.c', - '9p-proxy.c', '9p-synth.c', + '9p-util-generic.c', '9p-xattr-user.c', '9p-xattr.c', '9p.c', @@ -13,8 +13,11 @@ 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')) +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) diff --git a/hw/9pfs/trace-events b/hw/9pfs/trace-events index a12e55c165d..0e0fc37261b 100644 --- a/hw/9pfs/trace-events +++ b/hw/9pfs/trace-events @@ -11,9 +11,9 @@ v9fs_stat(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d" v9fs_stat_return(uint16_t tag, uint8_t id, int32_t mode, int32_t atime, int32_t mtime, int64_t length) "tag %d id %d stat={mode %d atime %d mtime %d length %"PRId64"}" v9fs_getattr(uint16_t tag, uint8_t id, int32_t fid, uint64_t request_mask) "tag %d id %d fid %d request_mask %"PRIu64 v9fs_getattr_return(uint16_t tag, uint8_t id, uint64_t result_mask, uint32_t mode, uint32_t uid, uint32_t gid) "tag %d id %d getattr={result_mask %"PRId64" mode %u uid %u gid %u}" -v9fs_walk(uint16_t tag, uint8_t id, int32_t fid, int32_t newfid, uint16_t nwnames) "tag %d id %d fid %d newfid %d nwnames %d" +v9fs_walk(uint16_t tag, uint8_t id, int32_t fid, int32_t newfid, uint16_t nwnames, const char* wnames) "tag=%d id=%d fid=%d newfid=%d nwnames=%d wnames={%s}" v9fs_walk_return(uint16_t tag, uint8_t id, uint16_t nwnames, void* qids) "tag %d id %d nwnames %d qids %p" -v9fs_open(uint16_t tag, uint8_t id, int32_t fid, int32_t mode) "tag %d id %d fid %d mode %d" +v9fs_open(uint16_t tag, uint8_t id, int32_t fid, int32_t mode, const char* oflags) "tag=%d id=%d fid=%d mode=%d(%s)" v9fs_open_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path, int iounit) "tag %u id %u qid={type %u version %u path %"PRIu64"} iounit %d" v9fs_lcreate(uint16_t tag, uint8_t id, int32_t dfid, int32_t flags, int32_t mode, uint32_t gid) "tag %d id %d dfid %d flags %d mode %d gid %u" v9fs_lcreate_return(uint16_t tag, uint8_t id, uint8_t type, uint32_t version, uint64_t path, int32_t iounit) "tag %u id %u qid={type %u version %u path %"PRIu64"} iounit %d" diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 5f522e68e9f..81b91e47c63 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -26,7 +26,7 @@ #include "hw/virtio/virtio-access.h" #include "qemu/iov.h" #include "qemu/module.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" static void virtio_9p_push_and_notify(V9fsPDU *pdu) { @@ -237,19 +237,18 @@ 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() }, }; -static Property virtio_9p_properties[] = { +static const Property virtio_9p_properties[] = { DEFINE_PROP_STRING("mount_tag", V9fsVirtioState, state.fsconf.tag), DEFINE_PROP_STRING("fsdev", V9fsVirtioState, state.fsconf.fsdev_id), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_9p_class_init(ObjectClass *klass, void *data) +static void virtio_9p_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index 4aa9c8c736d..79359d911a7 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -513,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, @@ -522,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 9ca7b38c31f..9e6c789ae7e 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,25 +37,27 @@ source smbios/Kconfig source ssi/Kconfig source timer/Kconfig source tpm/Kconfig +source uefi/Kconfig source ufs/Kconfig source usb/Kconfig source virtio/Kconfig source vfio/Kconfig +source vfio-user/Kconfig +source vmapple/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 e07d3204eb3..1d4e9f0845c 100644 --- a/hw/acpi/Kconfig +++ b/hw/acpi/Kconfig @@ -60,6 +60,11 @@ config ACPI_VMGENID default y depends on PC +config ACPI_VMCLOCK + bool + default y + depends on PC + config ACPI_VIOT bool depends on ACPI diff --git a/hw/acpi/acpi-cpu-hotplug-stub.c b/hw/acpi/acpi-cpu-hotplug-stub.c index 3fc4b14c260..9872dd55e43 100644 --- a/hw/acpi/acpi-cpu-hotplug-stub.c +++ b/hw/acpi/acpi-cpu-hotplug-stub.c @@ -10,41 +10,39 @@ void acpi_switch_to_modern_cphp(AcpiCpuHotplug *gpe_cpu, CPUHotplugState *cpuhp_state, uint16_t io_port) { - return; } void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, AcpiCpuHotplug *gpe_cpu, uint16_t base) { - return; +} + +void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner, + CPUHotplugState *state, hwaddr base_addr) +{ } void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list) { - return; } void acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, CPUHotplugState *cpu_st, DeviceState *dev, Error **errp) { - return; } void legacy_acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, AcpiCpuHotplug *g, DeviceState *dev, Error **errp) { - return; } void acpi_cpu_unplug_cb(CPUHotplugState *cpu_st, DeviceState *dev, Error **errp) { - return; } void acpi_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, CPUHotplugState *cpu_st, DeviceState *dev, Error **errp) { - return; } diff --git a/hw/acpi/acpi-mem-hotplug-stub.c b/hw/acpi/acpi-mem-hotplug-stub.c index 73a076a2657..7ad0fdcdf26 100644 --- a/hw/acpi/acpi-mem-hotplug-stub.c +++ b/hw/acpi/acpi-mem-hotplug-stub.c @@ -7,29 +7,24 @@ const VMStateDescription vmstate_memory_hotplug; void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner, MemHotplugState *state, hwaddr io_base) { - return; } void acpi_memory_ospm_status(MemHotplugState *mem_st, ACPIOSTInfoList ***list) { - return; } void acpi_memory_plug_cb(HotplugHandler *hotplug_dev, MemHotplugState *mem_st, DeviceState *dev, Error **errp) { - return; } void acpi_memory_unplug_cb(MemHotplugState *mem_st, DeviceState *dev, Error **errp) { - return; } void acpi_memory_unplug_request_cb(HotplugHandler *hotplug_dev, MemHotplugState *mem_st, DeviceState *dev, Error **errp) { - return; } diff --git a/hw/acpi/acpi-nvdimm-stub.c b/hw/acpi/acpi-nvdimm-stub.c index 8baff9be6f4..65f491d6535 100644 --- a/hw/acpi/acpi-nvdimm-stub.c +++ b/hw/acpi/acpi-nvdimm-stub.c @@ -4,5 +4,4 @@ void nvdimm_acpi_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev) { - return; } diff --git a/hw/acpi/acpi-pci-hotplug-stub.c b/hw/acpi/acpi-pci-hotplug-stub.c index dcee3ad7a1b..b7bc6e40a1e 100644 --- a/hw/acpi/acpi-pci-hotplug-stub.c +++ b/hw/acpi/acpi-pci-hotplug-stub.c @@ -7,40 +7,34 @@ const VMStateDescription vmstate_acpi_pcihp_pci_status; void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, MemoryRegion *address_space_io, uint16_t io_base) { - return; } void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) { - return; } void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - return; } void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) { - return; } void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) { - return; } void acpi_pcihp_reset(AcpiPciHpState *s) { - return; } -bool acpi_pcihp_is_hotpluggbale_bus(AcpiPciHpState *s, BusState *bus) +bool acpi_pcihp_is_hotpluggable_bus(AcpiPciHpState *s, BusState *bus) { return true; } diff --git a/hw/acpi/acpi-stub.c b/hw/acpi/acpi-stub.c index e268ce9b1a9..fd0b62fad9e 100644 --- a/hw/acpi/acpi-stub.c +++ b/hw/acpi/acpi-stub.c @@ -21,7 +21,15 @@ #include "qemu/osdep.h" #include "hw/acpi/acpi.h" +char unsigned *acpi_tables; +size_t acpi_tables_len; + void acpi_table_add(const QemuOpts *opts, Error **errp) { g_assert_not_reached(); } + +bool acpi_builtin(void) +{ + return false; +} diff --git a/hw/acpi/acpi-x86-stub.c b/hw/acpi/acpi-x86-stub.c index d0d399d26b8..9662a594adb 100644 --- a/hw/acpi/acpi-x86-stub.c +++ b/hw/acpi/acpi-x86-stub.c @@ -1,12 +1,6 @@ #include "qemu/osdep.h" -#include "hw/i386/pc.h" #include "hw/i386/acpi-build.h" -void pc_madt_cpu_entry(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/aml-build.c b/hw/acpi/aml-build.c index ea331a20d13..f8f93a9f66c 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -24,7 +24,7 @@ #include "hw/acpi/aml-build.h" #include "qemu/bswap.h" #include "qemu/bitops.h" -#include "sysemu/numa.h" +#include "system/numa.h" #include "hw/boards.h" #include "hw/acpi/tpm.h" #include "hw/pci/pci_host.h" @@ -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,71 @@ 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, const char *name) +{ + 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); + if (rev < 4) { + /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); + } else { + /* UartClkFreq */ + build_append_int_noprefix(table_data, f->uart_clk_freq, 4); + /* PreciseBaudrate */ + build_append_int_noprefix(table_data, f->precise_baudrate, 4); + /* NameSpaceStringLength */ + build_append_int_noprefix(table_data, f->namespace_string_length, 2); + /* NameSpaceStringOffset */ + build_append_int_noprefix(table_data, f->namespace_string_offset, 2); + /* NamespaceString[] */ + g_array_append_vals(table_data, name, f->namespace_string_length); + } + acpi_table_end(linker, &table); +} /* * ACPI spec, Revision 6.3 * 5.2.29 Processor Properties Topology Table (PPTT) @@ -2159,7 +2306,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 00b1e79a301..58f8964e130 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -31,7 +31,8 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/option.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" +#include "trace.h" struct acpi_table_header { uint16_t _length; /* our length, not actual part of the hdr */ @@ -77,6 +78,11 @@ static void acpi_register_config(void) opts_init(acpi_register_config); +bool acpi_builtin(void) +{ + return true; +} + static int acpi_checksum(const uint8_t *data, int len) { int sum, i; @@ -688,9 +694,11 @@ void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val) cur = acpi_gpe_ioport_get_ptr(ar, addr); if (addr < ar->gpe.len / 2) { + trace_acpi_gpe_sts_ioport_writeb(addr, val); /* GPE_STS */ *cur = (*cur) & ~val; } else if (addr < ar->gpe.len) { + trace_acpi_gpe_en_ioport_writeb(addr - (ar->gpe.len / 2), val); /* GPE_EN */ *cur = val; } else { @@ -709,6 +717,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 19c154d78fb..6f1ae79edbf 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" +#include "system/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 @@ -235,8 +235,8 @@ void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner, static AcpiCpuStatus *get_cpu_status(CPUHotplugState *cpu_st, DeviceState *dev) { - CPUClass *k = CPU_GET_CLASS(dev); - uint64_t cpu_arch_id = k->get_arch_id(CPU(dev)); + CPUState *cpu = CPU(dev); + uint64_t cpu_arch_id = cpu->cc->get_arch_id(cpu); int i; for (i = 0; i < cpu_st->dev_count; i++) { @@ -296,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), @@ -309,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, @@ -327,6 +327,7 @@ const VMStateDescription vmstate_cpu_hotplug = { #define CPU_EJECT_METHOD "CEJ0" #define CPU_OST_METHOD "COST" #define CPU_ADDED_LIST "CNEW" +#define CPU_EJ_LIST "CEJL" #define CPU_ENABLED "CPEN" #define CPU_SELECTOR "CSEL" @@ -338,9 +339,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; @@ -353,8 +355,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); cpu_ctrl_dev = aml_device("%s", cphp_res_path); { @@ -366,14 +366,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, @@ -481,7 +489,6 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, method = aml_method(CPU_SCAN_METHOD, 0, AML_SERIALIZED); { const uint8_t max_cpus_per_pass = 255; - Aml *else_ctx; Aml *while_ctx, *while_ctx2; Aml *has_event = aml_local(0); Aml *dev_chk = aml_int(1); @@ -492,6 +499,8 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, Aml *uid = aml_local(3); Aml *has_job = aml_local(4); Aml *new_cpus = aml_name(CPU_ADDED_LIST); + Aml *ej_cpus = aml_name(CPU_EJ_LIST); + Aml *num_ej_cpus = aml_local(5); aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); @@ -506,6 +515,8 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, */ aml_append(method, aml_name_decl(CPU_ADDED_LIST, aml_package(max_cpus_per_pass))); + aml_append(method, aml_name_decl(CPU_EJ_LIST, + aml_package(max_cpus_per_pass))); aml_append(method, aml_store(zero, uid)); aml_append(method, aml_store(one, has_job)); @@ -520,6 +531,7 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(while_ctx2, aml_store(one, has_event)); aml_append(while_ctx2, aml_store(zero, num_added_cpus)); + aml_append(while_ctx2, aml_store(zero, num_ej_cpus)); /* * Scan CPUs, till there are CPUs with events or @@ -552,8 +564,10 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, * if CPU_ADDED_LIST is full, exit inner loop and process * collected CPUs */ - ifctx = aml_if( - aml_equal(num_added_cpus, aml_int(max_cpus_per_pass))); + ifctx = aml_if(aml_lor( + aml_equal(num_added_cpus, aml_int(max_cpus_per_pass)), + aml_equal(num_ej_cpus, aml_int(max_cpus_per_pass)) + )); { aml_append(ifctx, aml_store(one, has_job)); aml_append(ifctx, aml_break()); @@ -570,16 +584,16 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(ifctx, aml_store(one, has_event)); } aml_append(while_ctx, ifctx); - else_ctx = aml_else(); + ifctx = aml_if(aml_equal(rm_evt, one)); { - aml_append(ifctx, - aml_call2(CPU_NOTIFY_METHOD, uid, eject_req)); - aml_append(ifctx, aml_store(one, rm_evt)); + /* cache to be removed CPUs to Notify later */ + aml_append(ifctx, aml_store(uid, + aml_index(ej_cpus, num_ej_cpus))); + aml_append(ifctx, aml_increment(num_ej_cpus)); aml_append(ifctx, aml_store(one, has_event)); } - aml_append(else_ctx, ifctx); - aml_append(while_ctx, else_ctx); + aml_append(while_ctx, ifctx); aml_append(while_ctx, aml_increment(uid)); } aml_append(while_ctx2, while_ctx); @@ -613,6 +627,24 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(while_ctx, aml_increment(cpu_idx)); } aml_append(while_ctx2, while_ctx); + + /* + * Notify OSPM about to be removed CPUs and clear remove flag + */ + aml_append(while_ctx2, aml_store(zero, cpu_idx)); + while_ctx = aml_while(aml_lless(cpu_idx, num_ej_cpus)); + { + aml_append(while_ctx, + aml_store(aml_derefof(aml_index(ej_cpus, cpu_idx)), + uid)); + aml_append(while_ctx, + aml_call2(CPU_NOTIFY_METHOD, uid, eject_req)); + aml_append(while_ctx, aml_store(uid, cpu_selector)); + aml_append(while_ctx, aml_store(one, rm_evt)); + aml_append(while_ctx, aml_increment(cpu_idx)); + } + aml_append(while_ctx2, while_ctx); + /* * If another batch is needed, then it will resume scanning * exactly at -- and not after -- the last CPU that's currently @@ -664,9 +696,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(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 ff14c3f4106..aa0e1e3efa5 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,32 +59,43 @@ 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; - cpu_id = k->get_arch_id(cpu); + cpu_id = cpu->cc->get_arch_id(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 +103,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 +276,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 +310,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 +331,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 92b46bc9323..9cd7905ea25 100644 --- a/hw/acpi/cxl.c +++ b/hw/acpi/cxl.c @@ -30,6 +30,75 @@ #include "qapi/error.h" #include "qemu/uuid.h" +void build_cxl_dsm_method(Aml *dev) +{ + 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); diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c index 35007d80173..099cabb7ab7 100644 --- a/hw/acpi/erst.c +++ b/hw/acpi/erst.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/qdev-core.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qom/object.h" #include "hw/pci/pci_device.h" #include "qom/object_interfaces.h" @@ -23,8 +23,8 @@ #include "hw/acpi/acpi-defs.h" #include "hw/acpi/aml-build.h" #include "hw/acpi/bios-linker-loader.h" -#include "exec/address-spaces.h" -#include "sysemu/hostmem.h" +#include "system/address-spaces.h" +#include "system/hostmem.h" #include "hw/acpi/erst.h" #include "trace.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); @@ -1001,15 +1011,14 @@ static void erst_reset(DeviceState *dev) trace_acpi_erst_reset_out(le32_to_cpu(s->header->record_count)); } -static Property erst_properties[] = { +static const Property erst_properties[] = { DEFINE_PROP_LINK(ACPI_ERST_MEMDEV_PROP, ERSTDeviceState, hostmem, TYPE_MEMORY_BACKEND, HostMemoryBackend *), DEFINE_PROP_UINT32(ACPI_ERST_RECORD_SIZE_PROP, ERSTDeviceState, default_record_size, ERST_RECORD_SIZE), - DEFINE_PROP_END_OF_LIST(), }; -static void erst_class_init(ObjectClass *klass, void *data) +static void erst_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1020,7 +1029,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; @@ -1035,7 +1044,7 @@ static const TypeInfo erst_type_info = { .parent = TYPE_PCI_DEVICE, .class_init = erst_class_init, .instance_size = sizeof(ERSTDeviceState), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } } diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index a3d31631fe0..7a62f8d5bca 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -19,12 +19,13 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qemu/error-report.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" 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); @@ -303,26 +316,43 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) qemu_irq_pulse(s->irq); } -static Property acpi_ged_properties[] = { +static const Property acpi_ged_properties[] = { DEFINE_PROP_UINT32("ged-event", AcpiGedState, ged_event_bitmap, 0), - DEFINE_PROP_END_OF_LIST(), }; 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,8 +362,8 @@ static const VMStateDescription vmstate_ghes = { .name = "acpi-ghes", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT64(ghes_addr_le, AcpiGhesState), + .fields = (const VMStateField[]) { + VMSTATE_UINT64(hw_error_le, AcpiGhesState), VMSTATE_END_OF_LIST() }, }; @@ -341,7 +371,7 @@ static const VMStateDescription vmstate_ghes = { static bool ghes_needed(void *opaque) { AcpiGedState *s = opaque; - return s->ghes_state.ghes_addr_le; + return s->ghes_state.hw_error_le; } static const VMStateDescription vmstate_ghes_state = { @@ -349,7 +379,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 +390,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); @@ -391,18 +458,18 @@ static void acpi_ged_initfn(Object *obj) * container for memory hotplug IO and expose it as GED sysbus * MMIO so that boards can map it separately. */ - memory_region_init(&s->container_memhp, OBJECT(dev), "memhp container", - MEMORY_HOTPLUG_IO_LEN); - sysbus_init_mmio(sbd, &s->container_memhp); - acpi_memory_hotplug_init(&s->container_memhp, OBJECT(dev), - &s->memhp_state, 0); + memory_region_init(&s->container_memhp, OBJECT(dev), "memhp container", + MEMORY_HOTPLUG_IO_LEN); + sysbus_init_mmio(sbd, &s->container_memhp); + acpi_memory_hotplug_init(&s->container_memhp, OBJECT(dev), + &s->memhp_state, 0); memory_region_init_io(&ged_st->regs, obj, &ged_regs_ops, ged_st, TYPE_ACPI_GED "-regs", ACPI_GED_REG_COUNT); sysbus_init_mmio(sbd, &ged_st->regs); } -static void acpi_ged_class_init(ObjectClass *class, void *data) +static void acpi_ged_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class); @@ -411,6 +478,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; @@ -426,7 +494,7 @@ static const TypeInfo acpi_ged_info = { .instance_size = sizeof(AcpiGedState), .instance_init = acpi_ged_initfn, .class_init = acpi_ged_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, { } diff --git a/hw/acpi/ghes-stub.c b/hw/acpi/ghes-stub.c index c315de1802d..7cec1812dad 100644 --- a/hw/acpi/ghes-stub.c +++ b/hw/acpi/ghes-stub.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "hw/acpi/ghes.h" -int acpi_ghes_record_errors(uint8_t source_id, uint64_t physical_address) +int acpi_ghes_memory_errors(uint16_t source_id, uint64_t physical_address) { return -1; } diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c index e9511d9b8f7..b85bb48195a 100644 --- a/hw/acpi/ghes.c +++ b/hw/acpi/ghes.c @@ -28,15 +28,12 @@ #include "hw/nvram/fw_cfg.h" #include "qemu/uuid.h" -#define ACPI_GHES_ERRORS_FW_CFG_FILE "etc/hardware_errors" -#define ACPI_GHES_DATA_ADDR_FW_CFG_FILE "etc/hardware_errors_addr" +#define ACPI_HW_ERROR_FW_CFG_FILE "etc/hardware_errors" +#define ACPI_HW_ERROR_ADDR_FW_CFG_FILE "etc/hardware_errors_addr" /* The max size in bytes for one error block */ #define ACPI_GHES_MAX_RAW_DATA_LENGTH (1 * KiB) -/* Now only support ARMv8 SEA notification type error source */ -#define ACPI_GHES_ERROR_SOURCE_COUNT 1 - /* Generic Hardware Error Source version 2 */ #define ACPI_GHES_SOURCE_GENERIC_ERROR_V2 10 @@ -184,51 +181,24 @@ static void acpi_ghes_build_append_mem_cper(GArray *table, build_append_int_noprefix(table, 0, 7); } -static int acpi_ghes_record_mem_error(uint64_t error_block_address, - uint64_t error_physical_addr) +static void +ghes_gen_err_data_uncorrectable_recoverable(GArray *block, + const uint8_t *section_type, + int data_length) { - GArray *block; - - /* Memory Error Section Type */ - const uint8_t uefi_cper_mem_sec[] = - UUID_LE(0xA5BC1114, 0x6F64, 0x4EDE, 0xB8, 0x63, 0x3E, 0x83, \ - 0xED, 0x7C, 0x83, 0xB1); - /* invalid fru id: ACPI 4.0: 17.3.2.6.1 Generic Error Data, * Table 17-13 Generic Error Data Entry */ QemuUUID fru_id = {}; - uint32_t data_length; - - block = g_array_new(false, true /* clear */, 1); - - /* This is the length if adding a new generic error data entry*/ - data_length = ACPI_GHES_DATA_LENGTH + ACPI_GHES_MEM_CPER_LENGTH; - /* - * It should not run out of the preallocated memory if adding a new generic - * error data entry - */ - assert((data_length + ACPI_GHES_GESB_SIZE) <= - ACPI_GHES_MAX_RAW_DATA_LENGTH); /* Build the new generic error status block header */ acpi_ghes_generic_error_status(block, ACPI_GEBS_UNCORRECTABLE, 0, 0, data_length, ACPI_CPER_SEV_RECOVERABLE); /* Build this new generic error data entry header */ - acpi_ghes_generic_error_data(block, uefi_cper_mem_sec, + acpi_ghes_generic_error_data(block, section_type, ACPI_CPER_SEV_RECOVERABLE, 0, 0, ACPI_GHES_MEM_CPER_LENGTH, fru_id, 0); - - /* Build the memory section CPER for above new generic error data entry */ - acpi_ghes_build_append_mem_cper(block, error_physical_addr); - - /* Write the generic error data entry into guest memory */ - cpu_physical_memory_write(error_block_address, block->data, block->len); - - g_array_free(block, true); - - return 0; } /* @@ -236,7 +206,7 @@ static int acpi_ghes_record_mem_error(uint64_t error_block_address, * Initialize "etc/hardware_errors" and "etc/hardware_errors_addr" fw_cfg blobs. * See docs/specs/acpi_hest_ghes.rst for blobs format. */ -void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker) +static void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker) { int i, error_status_block_offset; @@ -264,7 +234,7 @@ void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker) ACPI_GHES_MAX_RAW_DATA_LENGTH * ACPI_GHES_ERROR_SOURCE_COUNT); /* Tell guest firmware to place hardware_errors blob into RAM */ - bios_linker_loader_alloc(linker, ACPI_GHES_ERRORS_FW_CFG_FILE, + bios_linker_loader_alloc(linker, ACPI_HW_ERROR_FW_CFG_FILE, hardware_errors, sizeof(uint64_t), false); for (i = 0; i < ACPI_GHES_ERROR_SOURCE_COUNT; i++) { @@ -273,23 +243,31 @@ void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker) * corresponding "Generic Error Status Block" */ bios_linker_loader_add_pointer(linker, - ACPI_GHES_ERRORS_FW_CFG_FILE, sizeof(uint64_t) * i, - sizeof(uint64_t), ACPI_GHES_ERRORS_FW_CFG_FILE, - error_status_block_offset + i * ACPI_GHES_MAX_RAW_DATA_LENGTH); + ACPI_HW_ERROR_FW_CFG_FILE, + sizeof(uint64_t) * i, + sizeof(uint64_t), + ACPI_HW_ERROR_FW_CFG_FILE, + error_status_block_offset + + i * ACPI_GHES_MAX_RAW_DATA_LENGTH); } /* * tell firmware to write hardware_errors GPA into * hardware_errors_addr fw_cfg, once the former has been initialized. */ - bios_linker_loader_write_pointer(linker, ACPI_GHES_DATA_ADDR_FW_CFG_FILE, - 0, sizeof(uint64_t), ACPI_GHES_ERRORS_FW_CFG_FILE, 0); + bios_linker_loader_write_pointer(linker, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, 0, + sizeof(uint64_t), + ACPI_HW_ERROR_FW_CFG_FILE, 0); } /* Build Generic Hardware Error Source version 2 (GHESv2) */ -static void build_ghes_v2(GArray *table_data, int source_id, BIOSLinker *linker) +static void build_ghes_v2(GArray *table_data, + BIOSLinker *linker, + enum AcpiGhesNotifyType notify, + uint16_t source_id) { uint64_t address_offset; + /* * Type: * Generic Hardware Error Source version 2(GHESv2 - Type 10) @@ -316,21 +294,13 @@ static void build_ghes_v2(GArray *table_data, int source_id, BIOSLinker *linker) build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 0x40, 0, 4 /* QWord access */, 0); bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, - address_offset + GAS_ADDR_OFFSET, sizeof(uint64_t), - ACPI_GHES_ERRORS_FW_CFG_FILE, source_id * sizeof(uint64_t)); + address_offset + GAS_ADDR_OFFSET, + sizeof(uint64_t), + ACPI_HW_ERROR_FW_CFG_FILE, + source_id * sizeof(uint64_t)); - switch (source_id) { - case ACPI_HEST_SRC_ID_SEA: - /* - * Notification Structure - * Now only enable ARMv8 SEA notification type - */ - build_ghes_hw_error_notification(table_data, ACPI_GHES_NOTIFY_SEA); - break; - default: - error_report("Not support this error source"); - abort(); - } + /* Notification Structure */ + build_ghes_hw_error_notification(table_data, notify); /* Error Status Block Length */ build_append_int_noprefix(table_data, ACPI_GHES_MAX_RAW_DATA_LENGTH, 4); @@ -344,9 +314,11 @@ static void build_ghes_v2(GArray *table_data, int source_id, BIOSLinker *linker) build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 0x40, 0, 4 /* QWord access */, 0); bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, - address_offset + GAS_ADDR_OFFSET, - sizeof(uint64_t), ACPI_GHES_ERRORS_FW_CFG_FILE, - (ACPI_GHES_ERROR_SOURCE_COUNT + source_id) * sizeof(uint64_t)); + address_offset + GAS_ADDR_OFFSET, + sizeof(uint64_t), + ACPI_HW_ERROR_FW_CFG_FILE, + (ACPI_GHES_ERROR_SOURCE_COUNT + source_id) + * sizeof(uint64_t)); /* * Read Ack Preserve field @@ -359,17 +331,21 @@ static void build_ghes_v2(GArray *table_data, int source_id, BIOSLinker *linker) } /* Build Hardware Error Source Table */ -void acpi_build_hest(GArray *table_data, BIOSLinker *linker, +void acpi_build_hest(GArray *table_data, GArray *hardware_errors, + BIOSLinker *linker, const char *oem_id, const char *oem_table_id) { AcpiTable table = { .sig = "HEST", .rev = 1, .oem_id = oem_id, .oem_table_id = oem_table_id }; + build_ghes_error_table(hardware_errors, linker); + acpi_table_begin(&table, table_data); /* Error Source Count */ build_append_int_noprefix(table_data, ACPI_GHES_ERROR_SOURCE_COUNT, 4); - build_ghes_v2(table_data, ACPI_HEST_SRC_ID_SEA, linker); + build_ghes_v2(table_data, linker, + ACPI_GHES_NOTIFY_SEA, ACPI_HEST_SRC_ID_SEA); acpi_table_end(linker, &table); } @@ -378,70 +354,130 @@ void acpi_ghes_add_fw_cfg(AcpiGhesState *ags, FWCfgState *s, GArray *hardware_error) { /* Create a read-only fw_cfg file for GHES */ - fw_cfg_add_file(s, ACPI_GHES_ERRORS_FW_CFG_FILE, hardware_error->data, + fw_cfg_add_file(s, ACPI_HW_ERROR_FW_CFG_FILE, hardware_error->data, hardware_error->len); /* Create a read-write fw_cfg file for Address */ - fw_cfg_add_file_callback(s, ACPI_GHES_DATA_ADDR_FW_CFG_FILE, NULL, NULL, - NULL, &(ags->ghes_addr_le), sizeof(ags->ghes_addr_le), false); + fw_cfg_add_file_callback(s, ACPI_HW_ERROR_ADDR_FW_CFG_FILE, NULL, NULL, + NULL, &(ags->hw_error_le), sizeof(ags->hw_error_le), false); ags->present = true; } -int acpi_ghes_record_errors(uint8_t source_id, uint64_t physical_address) +static void get_hw_error_offsets(uint64_t ghes_addr, + uint64_t *cper_addr, + uint64_t *read_ack_register_addr) +{ + if (!ghes_addr) { + return; + } + + /* + * non-HEST version supports only one source, so no need to change + * the start offset based on the source ID. Also, we can't validate + * the source ID, as it is stored inside the HEST table. + */ + + cpu_physical_memory_read(ghes_addr, cper_addr, + sizeof(*cper_addr)); + + *cper_addr = le64_to_cpu(*cper_addr); + + /* + * As the current version supports only one source, the ack offset is + * just sizeof(uint64_t). + */ + *read_ack_register_addr = ghes_addr + sizeof(uint64_t); +} + +static void ghes_record_cper_errors(const void *cper, size_t len, + uint16_t source_id, Error **errp) { - uint64_t error_block_addr, read_ack_register_addr, read_ack_register = 0; - uint64_t start_addr; - bool ret = -1; + uint64_t cper_addr = 0, read_ack_register_addr = 0, read_ack_register; AcpiGedState *acpi_ged_state; AcpiGhesState *ags; - assert(source_id < ACPI_HEST_SRC_ID_RESERVED); + if (len > ACPI_GHES_MAX_RAW_DATA_LENGTH) { + error_setg(errp, "GHES CPER record is too big: %zd", len); + return; + } acpi_ged_state = ACPI_GED(object_resolve_path_type("", TYPE_ACPI_GED, NULL)); - g_assert(acpi_ged_state); + if (!acpi_ged_state) { + error_setg(errp, "Can't find ACPI_GED object"); + return; + } ags = &acpi_ged_state->ghes_state; - start_addr = le64_to_cpu(ags->ghes_addr_le); + assert(ACPI_GHES_ERROR_SOURCE_COUNT == 1); + get_hw_error_offsets(le64_to_cpu(ags->hw_error_le), + &cper_addr, &read_ack_register_addr); + + if (!cper_addr) { + error_setg(errp, "can not find Generic Error Status Block"); + return; + } + + cpu_physical_memory_read(read_ack_register_addr, + &read_ack_register, sizeof(read_ack_register)); + + /* zero means OSPM does not acknowledge the error */ + if (!read_ack_register) { + error_setg(errp, + "OSPM does not acknowledge previous error," + " so can not record CPER for current error anymore"); + return; + } + + read_ack_register = cpu_to_le64(0); + /* + * Clear the Read Ack Register, OSPM will write 1 to this register when + * it acknowledges the error. + */ + cpu_physical_memory_write(read_ack_register_addr, + &read_ack_register, sizeof(uint64_t)); + + /* Write the generic error data entry into guest memory */ + cpu_physical_memory_write(cper_addr, cper, len); +} - if (physical_address) { +int acpi_ghes_memory_errors(uint16_t source_id, uint64_t physical_address) +{ + /* Memory Error Section Type */ + const uint8_t guid[] = + UUID_LE(0xA5BC1114, 0x6F64, 0x4EDE, 0xB8, 0x63, 0x3E, 0x83, \ + 0xED, 0x7C, 0x83, 0xB1); + Error *errp = NULL; + int data_length; + GArray *block; - if (source_id < ACPI_HEST_SRC_ID_RESERVED) { - start_addr += source_id * sizeof(uint64_t); - } + block = g_array_new(false, true /* clear */, 1); - cpu_physical_memory_read(start_addr, &error_block_addr, - sizeof(error_block_addr)); + data_length = ACPI_GHES_DATA_LENGTH + ACPI_GHES_MEM_CPER_LENGTH; + /* + * It should not run out of the preallocated memory if adding a new generic + * error data entry + */ + assert((data_length + ACPI_GHES_GESB_SIZE) <= + ACPI_GHES_MAX_RAW_DATA_LENGTH); - error_block_addr = le64_to_cpu(error_block_addr); + ghes_gen_err_data_uncorrectable_recoverable(block, guid, data_length); - read_ack_register_addr = start_addr + - ACPI_GHES_ERROR_SOURCE_COUNT * sizeof(uint64_t); + /* Build the memory section CPER for above new generic error data entry */ + acpi_ghes_build_append_mem_cper(block, physical_address); - cpu_physical_memory_read(read_ack_register_addr, - &read_ack_register, sizeof(read_ack_register)); + /* Report the error */ + ghes_record_cper_errors(block->data, block->len, source_id, &errp); - /* zero means OSPM does not acknowledge the error */ - if (!read_ack_register) { - error_report("OSPM does not acknowledge previous error," - " so can not record CPER for current error anymore"); - } else if (error_block_addr) { - read_ack_register = cpu_to_le64(0); - /* - * Clear the Read Ack Register, OSPM will write it to 1 when - * it acknowledges this error. - */ - cpu_physical_memory_write(read_ack_register_addr, - &read_ack_register, sizeof(uint64_t)); + g_array_free(block, true); - ret = acpi_ghes_record_mem_error(error_block_addr, - physical_address); - } else - error_report("can not find Generic Error Status Block"); + if (errp) { + error_report_err(errp); + return -1; } - return ret; + return 0; } bool acpi_ghes_present(void) diff --git a/hw/acpi/hmat.c b/hw/acpi/hmat.c index 3a6d51282aa..ca7b183d9eb 100644 --- a/hw/acpi/hmat.c +++ b/hw/acpi/hmat.c @@ -26,7 +26,8 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "sysemu/numa.h" +#include "system/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 b57f0e7e802..362b05e2ddf 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 "system/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 25e2c7243e0..967b67485ee 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -31,24 +31,16 @@ #include "migration/vmstate.h" #include "qemu/timer.h" #include "hw/core/cpu.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/reset.h" +#include "system/runstate.h" #include "hw/acpi/acpi.h" #include "hw/acpi/ich9_tco.h" +#include "hw/acpi/ich9_timer.h" #include "hw/southbridge/ich9.h" #include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" -//#define DEBUG - -#ifdef DEBUG -#define ICH9_DEBUG(fmt, ...) \ -do { printf("%s "fmt, __func__, ## __VA_ARGS__); } while (0) -#else -#define ICH9_DEBUG(fmt, ...) do { } while (0) -#endif - static void ich9_pm_update_sci_fn(ACPIREGS *regs) { ICH9LPCPMRegs *pm = container_of(regs, ICH9LPCPMRegs, acpi_regs); @@ -108,6 +100,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; } } @@ -122,8 +126,6 @@ static const MemoryRegionOps ich9_smi_ops = { void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base) { - ICH9_DEBUG("to 0x%x\n", pm_io_base); - assert((pm_io_base & ICH9_PMIO_MASK) == 0); pm->pm_io_base = pm_io_base; @@ -153,18 +155,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 +176,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 +203,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() } @@ -226,7 +221,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 +234,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 +246,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, @@ -293,6 +288,8 @@ static void pm_powerdown_req(Notifier *n, void *opaque) 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), @@ -312,6 +309,14 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq) "acpi-smi", 8); memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi); + if (pm->swsmi_timer_enabled) { + ich9_pm_swsmi_timer_init(pm); + } + + if (pm->periodic_timer_enabled) { + ich9_pm_periodic_timer_init(pm); + } + if (pm->enable_tco) { acpi_pm_tco_init(&pm->tco_regs, &pm->io); } @@ -335,11 +340,9 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq) 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, @@ -351,21 +354,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); @@ -445,9 +433,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); @@ -478,12 +463,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) && @@ -527,8 +507,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); @@ -563,8 +542,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,7 +559,7 @@ 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); + return acpi_pcihp_is_hotpluggable_bus(&lpc->pm.acpi_pci_hotplug, bus); } void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list) diff --git a/hw/acpi/ich9_tco.c b/hw/acpi/ich9_tco.c index 1540f4fd461..6300db65b72 100644 --- a/hw/acpi/ich9_tco.c +++ b/hw/acpi/ich9_tco.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "hw/southbridge/ich9.h" #include "migration/vmstate.h" @@ -254,7 +254,7 @@ const VMStateDescription vmstate_tco_io_sts = { .name = "tco io device status", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(tco.rld, TCOIORegs), VMSTATE_UINT8(tco.din, TCOIORegs), VMSTATE_UINT8(tco.dout, TCOIORegs), diff --git a/hw/acpi/ich9_timer.c b/hw/acpi/ich9_timer.c new file mode 100644 index 00000000000..5b1c9101566 --- /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/ipmi.c b/hw/acpi/ipmi.c index a20e57d465c..39f8f2f6d68 100644 --- a/hw/acpi/ipmi.c +++ b/hw/acpi/ipmi.c @@ -55,7 +55,8 @@ static Aml *aml_ipmi_crs(IPMIFwInfo *info) abort(); } - if (info->interrupt_number) { + /* Should PCI interrupts also be appended? */ + if (info->irq_source == IPMI_ISA_IRQ && info->interrupt_number) { aml_append(crs, aml_irq_no_flags(info->interrupt_number)); } diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index d926f4f77dd..9b974b7274a 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" @@ -177,14 +178,6 @@ 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->canonical_path); error_free(local_err); @@ -316,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), @@ -329,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 fc1b952379a..73f02b96912 100644 --- a/hw/acpi/meson.build +++ b/hw/acpi/meson.build @@ -15,6 +15,7 @@ acpi_ss.add(when: 'CONFIG_ACPI_NVDIMM', if_false: files('acpi-nvdimm-stub.c')) acpi_ss.add(when: 'CONFIG_ACPI_PCI', if_true: files('pci.c')) acpi_ss.add(when: 'CONFIG_ACPI_CXL', if_true: files('cxl.c'), if_false: files('cxl-stub.c')) acpi_ss.add(when: 'CONFIG_ACPI_VMGENID', if_true: files('vmgenid.c')) +acpi_ss.add(when: 'CONFIG_ACPI_VMCLOCK', if_true: files('vmclock.c')) acpi_ss.add(when: 'CONFIG_ACPI_HW_REDUCED', if_true: files('generic_event_device.c')) 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')) @@ -23,7 +24,7 @@ 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_ICH9', if_true: files('ich9.c', 'ich9_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')) @@ -33,9 +34,4 @@ endif 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(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', 'pci-bridge-stub.c')) system_ss.add(files('acpi-qmp-cmds.c')) diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index a3b25a92f3f..9ba90806f26 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.c b/hw/acpi/pci.c index 20b70dcd815..d511a850295 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, const 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, const 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 cdd6f775a14..aac90013d4a 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -371,7 +371,7 @@ 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) +bool acpi_pcihp_is_hotpluggable_bus(AcpiPciHpState *s, BusState *bus) { Object *o = OBJECT(bus->parent); @@ -496,8 +496,7 @@ static const MemoryRegionOps acpi_pcihp_io_ops = { }; void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, - MemoryRegion *address_space_io, - uint16_t io_base) + MemoryRegion *io, uint16_t io_base) { s->io_len = ACPI_PCIHP_SIZE; s->io_base = io_base; @@ -506,7 +505,7 @@ void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, 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); @@ -518,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 63d2113b865..d98b80df6dd 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -20,7 +20,6 @@ */ #include "qemu/osdep.h" -#include "hw/i386/pc.h" #include "hw/irq.h" #include "hw/isa/apm.h" #include "hw/i2c/pm_smbus.h" @@ -29,9 +28,9 @@ #include "hw/acpi/acpi.h" #include "hw/acpi/pcihp.h" #include "hw/acpi/piix4.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" -#include "sysemu/xen.h" +#include "system/runstate.h" +#include "system/system.h" +#include "system/xen.h" #include "qapi/error.h" #include "qemu/range.h" #include "hw/acpi/cpu_hotplug.h" @@ -43,7 +42,6 @@ #include "hw/acpi/acpi_dev_interface.h" #include "migration/vmstate.h" #include "hw/core/cpu.h" -#include "trace.h" #include "qom/object.h" #define GPE_BASE 0xafe0 @@ -149,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() @@ -160,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() @@ -191,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() } @@ -216,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() } @@ -249,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), @@ -271,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 @@ -408,7 +406,7 @@ 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); + return acpi_pcihp_is_hotpluggable_bus(&s->acpi_pci_hotplug, bus); } static void piix4_pm_machine_ready(Notifier *n, void *opaque) @@ -518,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; } @@ -527,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); } @@ -606,7 +602,7 @@ static void piix4_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev) acpi_send_gpe_event(&s->ar, s->irq, ev); } -static Property piix4_pm_properties[] = { +static const Property piix4_pm_properties[] = { DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0), DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0), DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0), @@ -621,10 +617,9 @@ static Property piix4_pm_properties[] = { DEFINE_PROP_BOOL("smm-enabled", PIIX4PMState, smm_enabled, false), DEFINE_PROP_BOOL("x-not-migrate-acpi-index", PIIX4PMState, not_migrate_acpi_index, false), - DEFINE_PROP_END_OF_LIST(), }; -static void piix4_pm_class_init(ObjectClass *klass, void *data) +static void piix4_pm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -637,7 +632,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); @@ -654,7 +649,6 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data) 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 = { @@ -663,7 +657,7 @@ static const TypeInfo piix4_pm_info = { .instance_init = piix4_pm_init, .instance_size = sizeof(PIIX4PMState), .class_init = piix4_pm_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index 78e0a8670e9..edc93e703c4 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,10 +54,6 @@ 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" diff --git a/hw/acpi/vmclock.c b/hw/acpi/vmclock.c new file mode 100644 index 00000000000..c582c0c1f83 --- /dev/null +++ b/hw/acpi/vmclock.c @@ -0,0 +1,179 @@ +/* + * Virtual Machine Clock Device + * + * Copyright © 2024 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 "qemu/module.h" +#include "hw/i386/e820_memory_layout.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/aml-build.h" +#include "hw/acpi/vmclock.h" +#include "hw/nvram/fw_cfg.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "migration/vmstate.h" +#include "system/reset.h" + +#include "standard-headers/linux/vmclock-abi.h" + +void vmclock_build_acpi(VmclockState *vms, GArray *table_data, + BIOSLinker *linker, const char *oem_id) +{ + Aml *ssdt, *dev, *scope, *crs; + AcpiTable table = { .sig = "SSDT", .rev = 1, + .oem_id = oem_id, .oem_table_id = "VMCLOCK" }; + + /* Put VMCLOCK into a separate SSDT table */ + acpi_table_begin(&table, table_data); + ssdt = init_aml_allocator(); + + scope = aml_scope("\\_SB"); + dev = aml_device("VCLK"); + aml_append(dev, aml_name_decl("_HID", aml_string("AMZNC10C"))); + aml_append(dev, aml_name_decl("_CID", aml_string("VMCLOCK"))); + aml_append(dev, aml_name_decl("_DDN", aml_string("VMCLOCK"))); + + /* Simple status method */ + aml_append(dev, aml_name_decl("_STA", aml_int(0xf))); + + crs = aml_resource_template(); + aml_append(crs, aml_qword_memory(AML_POS_DECODE, + AML_MIN_FIXED, AML_MAX_FIXED, + AML_CACHEABLE, AML_READ_ONLY, + 0xffffffffffffffffULL, + vms->physaddr, + vms->physaddr + VMCLOCK_SIZE - 1, + 0, VMCLOCK_SIZE)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); + aml_append(ssdt, scope); + + g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len); + acpi_table_end(linker, &table); + free_aml_allocator(); +} + +static void vmclock_update_guest(VmclockState *vms) +{ + uint64_t disruption_marker; + uint32_t seq_count; + + if (!vms->clk) { + return; + } + + seq_count = le32_to_cpu(vms->clk->seq_count) | 1; + vms->clk->seq_count = cpu_to_le32(seq_count); + /* These barriers pair with read barriers in the guest */ + smp_wmb(); + + disruption_marker = le64_to_cpu(vms->clk->disruption_marker); + disruption_marker++; + vms->clk->disruption_marker = cpu_to_le64(disruption_marker); + + /* These barriers pair with read barriers in the guest */ + smp_wmb(); + vms->clk->seq_count = cpu_to_le32(seq_count + 1); +} + +/* + * After restoring an image, we need to update the guest memory to notify + * it of clock disruption. + */ +static int vmclock_post_load(void *opaque, int version_id) +{ + VmclockState *vms = opaque; + + vmclock_update_guest(vms); + return 0; +} + +static const VMStateDescription vmstate_vmclock = { + .name = "vmclock", + .version_id = 1, + .minimum_version_id = 1, + .post_load = vmclock_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(physaddr, VmclockState), + VMSTATE_END_OF_LIST() + }, +}; + +static void vmclock_handle_reset(void *opaque) +{ + VmclockState *vms = VMCLOCK(opaque); + + if (!memory_region_is_mapped(&vms->clk_page)) { + memory_region_add_subregion_overlap(get_system_memory(), + vms->physaddr, + &vms->clk_page, 0); + } +} + +static void vmclock_realize(DeviceState *dev, Error **errp) +{ + VmclockState *vms = VMCLOCK(dev); + + /* + * Given that this function is executing, there is at least one VMCLOCK + * device. Check if there are several. + */ + if (!find_vmclock_dev()) { + error_setg(errp, "at most one %s device is permitted", TYPE_VMCLOCK); + return; + } + + vms->physaddr = VMCLOCK_ADDR; + + e820_add_entry(vms->physaddr, VMCLOCK_SIZE, E820_RESERVED); + + memory_region_init_ram(&vms->clk_page, OBJECT(dev), "vmclock_page", + VMCLOCK_SIZE, &error_abort); + memory_region_set_enabled(&vms->clk_page, true); + vms->clk = memory_region_get_ram_ptr(&vms->clk_page); + memset(vms->clk, 0, VMCLOCK_SIZE); + + vms->clk->magic = cpu_to_le32(VMCLOCK_MAGIC); + vms->clk->size = cpu_to_le16(VMCLOCK_SIZE); + vms->clk->version = cpu_to_le16(1); + + /* These are all zero and thus default, but be explicit */ + vms->clk->clock_status = VMCLOCK_STATUS_UNKNOWN; + vms->clk->counter_id = VMCLOCK_COUNTER_INVALID; + + qemu_register_reset(vmclock_handle_reset, vms); + + vmclock_update_guest(vms); +} + +static void vmclock_device_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_vmclock; + dc->realize = vmclock_realize; + dc->hotpluggable = false; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static const TypeInfo vmclock_device_info = { + .name = TYPE_VMCLOCK, + .parent = TYPE_DEVICE, + .instance_size = sizeof(VmclockState), + .class_init = vmclock_device_class_init, +}; + +static void vmclock_register_types(void) +{ + type_register_static(&vmclock_device_info); +} + +type_init(vmclock_register_types) diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c index a39315c1b35..fac3d6d97e7 100644 --- a/hw/acpi/vmgenid.c +++ b/hw/acpi/vmgenid.c @@ -20,7 +20,7 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" -#include "sysemu/reset.h" +#include "system/reset.h" void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid, BIOSLinker *linker, const char *oem_id) @@ -178,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() }, @@ -214,12 +214,11 @@ static void vmgenid_realize(DeviceState *dev, Error **errp) vmgenid_update_guest(vms); } -static Property vmgenid_device_properties[] = { +static const Property vmgenid_device_properties[] = { DEFINE_PROP_UUID(VMGENID_GUID, VmGenIdState, guid), - DEFINE_PROP_END_OF_LIST(), }; -static void vmgenid_device_class_init(ObjectClass *klass, void *data) +static void vmgenid_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/adc/Kconfig b/hw/adc/Kconfig index a825bd3d343..25d2229fb83 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 0d296631295..3e820cae1e3 100644 --- a/hw/adc/aspeed_adc.c +++ b/hw/adc/aspeed_adc.c @@ -280,24 +280,23 @@ 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(), } }; -static Property aspeed_adc_engine_properties[] = { +static const Property aspeed_adc_engine_properties[] = { DEFINE_PROP_UINT32("engine-id", AspeedADCEngineState, engine_id, 0), DEFINE_PROP_UINT32("nr-channels", AspeedADCEngineState, nr_channels, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void aspeed_adc_engine_class_init(ObjectClass *klass, void *data) +static void aspeed_adc_engine_class_init(ObjectClass *klass, const 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; @@ -370,7 +369,7 @@ static void aspeed_adc_realize(DeviceState *dev, Error **errp) } } -static void aspeed_adc_class_init(ObjectClass *klass, void *data) +static void aspeed_adc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedADCClass *aac = ASPEED_ADC_CLASS(klass); @@ -380,7 +379,7 @@ static void aspeed_adc_class_init(ObjectClass *klass, void *data) aac->nr_engines = 1; } -static void aspeed_2600_adc_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_adc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedADCClass *aac = ASPEED_ADC_CLASS(klass); @@ -389,7 +388,7 @@ static void aspeed_2600_adc_class_init(ObjectClass *klass, void *data) aac->nr_engines = 2; } -static void aspeed_1030_adc_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_adc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedADCClass *aac = ASPEED_ADC_CLASS(klass); @@ -398,6 +397,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, const 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 +438,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 +452,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 e8bf4cccd44..00000000000 --- 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 a4f85b7d468..7f7acc16196 100644 --- a/hw/adc/meson.build +++ b/hw/adc/meson.build @@ -2,4 +2,3 @@ system_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c')) system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_adc.c')) system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c')) system_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq-xadc.c')) -system_ss.add(when: 'CONFIG_MAX111X', if_true: files('max111x.c')) diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c index bc6f3f55e64..ddb219d4562 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), @@ -267,12 +267,11 @@ static const VMStateDescription vmstate_npcm7xx_adc = { }, }; -static Property npcm7xx_timer_properties[] = { +static const Property npcm7xx_timer_properties[] = { DEFINE_PROP_UINT32("iref", NPCM7xxADCState, iref, NPCM7XX_ADC_DEFAULT_IREF), - DEFINE_PROP_END_OF_LIST(), }; -static void npcm7xx_adc_class_init(ObjectClass *klass, void *data) +static void npcm7xx_adc_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/adc/stm32f2xx_adc.c b/hw/adc/stm32f2xx_adc.c index 01a0b14e69d..a490ae640d9 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), @@ -284,11 +284,11 @@ static void stm32f2xx_adc_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } -static void stm32f2xx_adc_class_init(ObjectClass *klass, void *data) +static void stm32f2xx_adc_class_init(ObjectClass *klass, const 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 032e19cbd0a..748a51ba78b 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), @@ -281,12 +281,12 @@ static const VMStateDescription vmstate_zynq_xadc = { } }; -static void zynq_xadc_class_init(ObjectClass *klass, void *data) +static void zynq_xadc_class_init(ObjectClass *klass, const 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 9af650c94ec..7f3455ce1e1 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/dp264.c b/hw/alpha/dp264.c index 03495e1e606..19562b59674 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -8,6 +8,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/target_page.h" #include "elf.h" #include "hw/loader.h" #include "alpha_sys.h" @@ -124,9 +125,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, mc->default_nic, NULL); - } + pci_init_nic_devices(pci_bus, mc->default_nic); /* Super I/O */ isa_create_simple(isa_bus, TYPE_SMC37C669_SUPERIO); @@ -146,7 +145,7 @@ static void clipper_init(MachineState *machine) } size = load_elf(palcode_filename, NULL, cpu_alpha_superpage_to_phys, NULL, &palcode_entry, NULL, NULL, NULL, - 0, EM_ALPHA, 0, 0); + ELFDATA2LSB, EM_ALPHA, 0, 0); if (size < 0) { error_report("could not load palcode '%s'", palcode_filename); exit(1); @@ -165,7 +164,7 @@ static void clipper_init(MachineState *machine) size = load_elf(kernel_filename, NULL, cpu_alpha_superpage_to_phys, NULL, &kernel_entry, &kernel_low, NULL, NULL, - 0, EM_ALPHA, 0, 0); + ELFDATA2LSB, EM_ALPHA, 0, 0); if (size < 0) { error_report("could not load kernel '%s'", kernel_filename); exit(1); diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index 49a80550c54..4c56f981d71 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "qemu/units.h" +#include "exec/cpu-interrupt.h" #include "qapi/error.h" #include "hw/pci/pci_host.h" #include "cpu.h" @@ -738,6 +739,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 +902,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, @@ -930,7 +935,7 @@ static const TypeInfo typhoon_pcihost_info = { }; static void typhoon_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 7e683484405..f543d944c30 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -1,5 +1,8 @@ config ARM_VIRT bool + default y + depends on ARM + depends on TCG || KVM || HVF imply PCI_DEVICES imply TEST_DEVICES imply VFIO_AMD_XGBE @@ -8,10 +11,12 @@ config ARM_VIRT 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 @@ -33,13 +38,6 @@ config ARM_VIRT select ACPI_CXL select ACPI_HMAT -config CHEETAH - bool - default y - depends on TCG && ARM - select OMAP - select TSC210X - config CUBIEBOARD bool default y @@ -73,7 +71,7 @@ config HIGHBANK depends on TCG && ARM select A9MPCORE select A15MPCORE - select AHCI + select AHCI_SYSBUS select ARM_TIMER # sp804 select ARM_V7M select PL011 # UART @@ -97,20 +95,17 @@ config INTEGRATOR select PL181 # display select SMC91C111 -config MAINSTONE +config MPS3R bool default y depends on TCG && ARM - select PXA2XX - select PFLASH_CFI01 - select SMC91C111 config MUSCA bool default y depends on TCG && ARM select ARMSSE - select PL011 + select PL011 # UART select PL031 select SPLIT_IRQ select UNIMP @@ -127,7 +122,7 @@ config MUSICPAL select MARVELL_88W8618 select PTIMER select PFLASH_CFI02 - select SERIAL + select SERIAL_MM select WM8750 config NETDUINO2 @@ -148,79 +143,13 @@ config OLIMEX_STM32_H405 depends on TCG && ARM select STM32F405_SOC -config NSERIES - bool - default y - depends on TCG && ARM - select OMAP - select TMP105 # temperature 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 - 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 - default y - depends on TCG && ARM - select PFLASH_CFI01 - select SMC91C111 - select PXA2XX - -config TOSA - bool - default y - depends on TCG && ARM - select ZAURUS # scoop - select MICRODRIVE - select PXA2XX - select LED - -config SPITZ - bool - default y - depends on TCG && ARM - 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 - default y - depends on TCG && ARM - select PFLASH_CFI01 - select WM8750 - select PL011 # UART - select PXA2XX + select SERIAL_MM config REALVIEW bool @@ -239,7 +168,7 @@ config REALVIEW select WM8750 # audio codec select LSI_SCSI_PCI select PCI - select PL011 # UART + select PL011 # UART select PL031 # RTC select PL041 # audio codec select PL050 # keyboard/mouse @@ -250,14 +179,15 @@ config REALVIEW select PL310 # cache controller select ARM_SBCON_I2C select DS1338 # I2C RTC+NVRAM - select USB_OHCI + select USB_OHCI_SYSBUS config SBSA_REF bool default y depends on TCG && AARCH64 imply PCI_DEVICES - select AHCI + select DEVICE_TREE + select AHCI_SYSBUS select ARM_SMMUV3 select GPIO_KEY select PCI_EXPRESS @@ -269,6 +199,8 @@ config SBSA_REF select USB_XHCI_SYSBUS select WDT_SBSA select BOCHS_DISPLAY + select IDE_BUS + select IDE_DEV config SABRELITE bool @@ -291,7 +223,7 @@ config STELLARIS select SSD0303 # OLED display select SSD0323 # OLED display select SSI_SD - select STELLARIS_INPUT + select STELLARIS_GAMEPAD select STELLARIS_ENET # ethernet select STELLARIS_GPTM # general purpose timer module select UNIMP @@ -304,14 +236,15 @@ config STM32VLDISCOVERY 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 @@ -331,12 +264,13 @@ 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 @@ -357,10 +291,11 @@ config ZYNQ select A9MPCORE select CADENCE # UART select PFLASH_CFI02 + select PL310 # cache controller select PL330 select SDHCI select SSI_M25P80 - select USB_EHCI_SYSBUS + select USB_CHIPIDEA select XILINX # UART select XILINX_AXI select XILINX_SPI @@ -376,7 +311,7 @@ config ARM_V7M config ALLWINNER_A10 bool - select AHCI + select AHCI_SYSBUS select ALLWINNER_A10_PIT select ALLWINNER_A10_PIC select ALLWINNER_A10_CCM @@ -384,9 +319,11 @@ config ALLWINNER_A10 select ALLWINNER_WDT select ALLWINNER_EMAC select ALLWINNER_I2C + select ALLWINNER_A10_SPI select AXP2XX_PMU - select SERIAL + select SERIAL_MM select UNIMP + select USB_OHCI_SYSBUS config ALLWINNER_H3 bool @@ -396,24 +333,28 @@ config ALLWINNER_H3 select ALLWINNER_SUN8I_EMAC select ALLWINNER_I2C select ALLWINNER_WDT - select SERIAL + 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_SYSBUS select ALLWINNER_SRAMC select ALLWINNER_A10_PIT + select ALLWINNER_WDT select AXP2XX_PMU - select SERIAL + select SERIAL_MM select ARM_TIMER select ARM_GIC select UNIMP + select USB_OHCI_SYSBUS + select USB_EHCI_SYSBUS select SD config RASPI @@ -424,6 +365,8 @@ config RASPI select PL011 # UART select SDHCI select USB_DWC2 + select BCM2835_SPI + select BCM2835_I2C config STM32F100_SOC bool @@ -445,24 +388,45 @@ config STM32F405_SOC bool select ARM_V7M select OR_IRQ + select STM32_RCC select STM32F4XX_SYSCFG select STM32F4XX_EXTI -config XLNX_ZYNQMP_ARM +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 AHCI_SYSBUS 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 @@ -472,7 +436,9 @@ config XLNX_VERSAL default y depends on TCG && AARCH64 select ARM_GIC - select PL011 + select CPU_CLUSTER + select DEVICE_TREE + select PL011 # UART select CADENCE select VIRTIO_MMIO select UNIMP @@ -482,12 +448,15 @@ config XLNX_VERSAL 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 @@ -496,10 +465,24 @@ 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 NPCM8XX + bool + default y + depends on TCG && AARCH64 + select ARM_GIC + select SMBUS + select PL310 # cache controller + select NPCM7XX + select SERIAL + select SSI + select UNIMP + config FSL_IMX25 bool @@ -509,6 +492,7 @@ config FSL_IMX25 select IMX select IMX_FEC select IMX_I2C + select USB_CHIPIDEA select WDT_IMX2 select SDHCI @@ -517,7 +501,7 @@ config FSL_IMX31 default y depends on TCG && ARM imply I2C_DEVICES - select SERIAL + select SERIAL_MM select IMX select IMX_I2C select WDT_IMX2 @@ -525,6 +509,7 @@ config FSL_IMX31 config FSL_IMX6 bool + imply PCIE_DEVICES imply I2C_DEVICES select A9MPCORE select IMX @@ -532,7 +517,11 @@ config FSL_IMX6 select IMX_I2C select IMX_USBPHY select WDT_IMX2 + select PL310 # cache controller + select PCI_EXPRESS_DESIGNWARE select SDHCI + select USB_CHIPIDEA + select OR_IRQ config ASPEED_SOC bool @@ -543,7 +532,7 @@ config ASPEED_SOC select I2C select DPS310 select PCA9552 - select SERIAL + select SERIAL_MM select SMBUS_EEPROM select PCA954X select SSI @@ -555,6 +544,8 @@ config ASPEED_SOC select LED select PMBUS select MAX31785 + select FSI_APB2OPB_ASPEED + select AT24C config MPS2 bool @@ -588,7 +579,33 @@ config FSL_IMX7 select WDT_IMX2 select PCI_EXPRESS_DESIGNWARE select SDHCI + select OR_IRQ + select UNIMP + select USB_CHIPIDEA + +config FSL_IMX8MP + bool + imply I2C_DEVICES + imply PCI_DEVICES + select ARM_GIC + select FSL_IMX8MP_ANALOG + select FSL_IMX8MP_CCM + select IMX + select IMX_FEC + select IMX_I2C + select OR_IRQ + select PCI_EXPRESS_DESIGNWARE + select PCI_EXPRESS_FSL_IMX8M_PHY + select SDHCI select UNIMP + select USB_DWC3 + select WDT_IMX2 + +config FSL_IMX8MP_EVK + bool + default y + depends on TCG && AARCH64 + select FSL_IMX8MP config ARM_SMMUV3 bool @@ -604,6 +621,7 @@ config FSL_IMX6UL select IMX_I2C select WDT_IMX2 select SDHCI + select USB_CHIPIDEA select UNIMP config MICROBIT @@ -630,30 +648,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 @@ -664,6 +662,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 b0ea3f7f662..dc910d4177b 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -17,15 +17,17 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "qemu/module.h" -#include "hw/char/serial.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 "system/system.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 @@ -34,6 +36,7 @@ #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 @@ -48,9 +51,8 @@ void allwinner_a10_bootrom_setup(AwA10State *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-a10.bootrom", buffer, rom_size, @@ -79,15 +81,12 @@ static void aw_a10_init(Object *obj) object_initialize_child(obj, "i2c0", &s->i2c0, TYPE_AW_I2C); - if (machine_usb(current_machine)) { - int i; + object_initialize_child(obj, "spi0", &s->spi0, TYPE_AW_A10_SPI); - for (i = 0; i < AW_A10_NUM_USB; i++) { - object_initialize_child(obj, "ehci[*]", &s->ehci[i], - TYPE_PLATFORM_EHCI); - object_initialize_child(obj, "ohci[*]", &s->ohci[i], - TYPE_SYSBUS_OHCI); - } + for (size_t i = 0; i < AW_A10_NUM_USB; i++) { + object_initialize_child(obj, "ehci[*]", &s->ehci[i], + TYPE_PLATFORM_EHCI); + object_initialize_child(obj, "ohci[*]", &s->ohci[i], TYPE_SYSBUS_OHCI); } object_initialize_child(obj, "mmc0", &s->mmc0, TYPE_AW_SDHOST_SUN4I); @@ -142,11 +141,7 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) sysbus_realize(SYS_BUS_DEVICE(&s->dramc), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 0, AW_A10_DRAMC_BASE); - /* 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]); - } + qemu_configure_nic_device(DEVICE(&s->emac), true, NULL); if (!sysbus_realize(SYS_BUS_DEVICE(&s->emac), errp)) { return; } @@ -163,30 +158,26 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) /* FIXME use a qdev chardev prop instead of serial_hd() */ serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, qdev_get_gpio_in(dev, 1), - 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); - - if (machine_usb(current_machine)) { - int i; - - for (i = 0; i < AW_A10_NUM_USB; i++) { - 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_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)); - } + 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); + + for (size_t i = 0; i < AW_A10_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, + 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)); } /* SD/MMC */ @@ -207,12 +198,17 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) 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) +static void aw_a10_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index f05afddf7e0..edffc21dd88 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 "system/system.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[] = { @@ -180,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, @@ -369,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); @@ -411,19 +408,19 @@ static void allwinner_h3_realize(DeviceState *dev, Error **errp) /* UART0. For future clocktree API: All UARTS are connected to APB2_CLK. */ serial_mm_init(get_system_memory(), s->memmap[AW_H3_DEV_UART0], 2, qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART0), - 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); /* UART1 */ serial_mm_init(get_system_memory(), s->memmap[AW_H3_DEV_UART1], 2, qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART1), - 115200, serial_hd(1), DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(1), DEVICE_LITTLE_ENDIAN); /* UART2 */ serial_mm_init(get_system_memory(), s->memmap[AW_H3_DEV_UART2], 2, qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART2), - 115200, serial_hd(2), DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(2), DEVICE_LITTLE_ENDIAN); /* UART3 */ serial_mm_init(get_system_memory(), s->memmap[AW_H3_DEV_UART3], 2, qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART3), - 115200, serial_hd(3), DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(3), DEVICE_LITTLE_ENDIAN); /* DRAMC */ sysbus_realize(SYS_BUS_DEVICE(&s->dramc), &error_fatal); @@ -469,7 +466,7 @@ static void allwinner_h3_realize(DeviceState *dev, Error **errp) } } -static void allwinner_h3_class_init(ObjectClass *oc, void *data) +static void allwinner_h3_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index 7d29eb224f6..0bf700865c2 100644 --- a/hw/arm/allwinner-r40.c +++ b/hw/arm/allwinner-r40.c @@ -23,15 +23,18 @@ #include "qemu/bswap.h" #include "qemu/module.h" #include "qemu/units.h" +#include "hw/boards.h" #include "hw/qdev-core.h" #include "hw/sysbus.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "hw/misc/unimp.h" #include "hw/usb/hcd-ehci.h" #include "hw/loader.h" -#include "sysemu/sysemu.h" +#include "system/system.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[] = { @@ -45,8 +48,14 @@ const hwaddr allwinner_r40_memmap[] = { [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, @@ -88,10 +97,9 @@ static struct AwR40Unimplemented r40_unimplemented[] = { { "usb0-host", 0x01c14000, 4 * KiB }, { "crypto", 0x01c15000, 4 * KiB }, { "spi2", 0x01c17000, 4 * KiB }, - { "sata", 0x01c18000, 4 * KiB }, - { "usb1-host", 0x01c19000, 4 * KiB }, + { "usb1-phy", 0x01c19800, 2 * KiB }, { "sid", 0x01c1b000, 4 * KiB }, - { "usb2-host", 0x01c1c000, 4 * KiB }, + { "usb2-phy", 0x01c1c800, 2 * KiB }, { "cs1", 0x01c1d000, 4 * KiB }, { "spi3", 0x01c1f000, 4 * KiB }, { "rtc", 0x01c20400, 1 * KiB }, @@ -181,6 +189,11 @@ enum { 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, }; @@ -218,9 +231,8 @@ bool allwinner_r40_bootrom_setup(AwR40State *s, BlockBackend *blk, int unit) struct boot_file_head *head = (struct boot_file_head *)buffer; if (blk_pread(blk, 8 * KiB, rom_size, buffer, 0) < 0) { - error_setg(&error_fatal, "%s: failed to read BlockBackend data", - __func__); - return false; + error_report("%s: failed to read BlockBackend data", __func__); + exit(1); } /* we only check the magic string here. */ @@ -269,6 +281,8 @@ static void allwinner_r40_init(Object *obj) 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++) { @@ -276,6 +290,15 @@ static void allwinner_r40_init(Object *obj) 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); @@ -294,12 +317,10 @@ static void allwinner_r40_init(Object *obj) static void allwinner_r40_realize(DeviceState *dev, Error **errp) { - const char *r40_nic_models[] = { "gmac", "emac", NULL }; AwR40State *s = AW_R40(dev); - unsigned i; /* CPUs */ - for (i = 0; i < AW_R40_NUM_CPUS; i++) { + for (unsigned i = 0; i < AW_R40_NUM_CPUS; i++) { /* * Disable secondary CPUs. Guest EL3 firmware will start @@ -335,7 +356,7 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp) * maintenance interrupt signal to the appropriate GIC PPI inputs, * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. */ - for (i = 0; i < AW_R40_NUM_CPUS; i++) { + 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; @@ -408,6 +429,40 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp) 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), @@ -437,7 +492,7 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp) 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); + 115200, serial_hd(i), DEVICE_LITTLE_ENDIAN); } /* I2C */ @@ -455,31 +510,8 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 2, s->memmap[AW_R40_DEV_DRAMPHY]); - /* nic support gmac and emac */ - for (int i = 0; i < ARRAY_SIZE(r40_nic_models) - 1; i++) { - NICInfo *nic = &nd_table[i]; - - if (!nic->used) { - continue; - } - if (qemu_show_nic_models(nic->model, r40_nic_models)) { - exit(0); - } - - switch (qemu_find_nic_model(nic, r40_nic_models, r40_nic_models[0])) { - case 0: /* gmac */ - qdev_set_nic_properties(DEVICE(&s->gmac), nic); - break; - case 1: /* emac */ - qdev_set_nic_properties(DEVICE(&s->emac), nic); - break; - default: - exit(1); - break; - } - } - /* 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); @@ -488,20 +520,26 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp) 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 (i = 0; i < ARRAY_SIZE(r40_unimplemented); i++) { + 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) +static void allwinner_r40_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 11cd08b6c1e..50ab7f48105 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -72,12 +72,13 @@ struct ARMSSEInfo { bool has_cpu_pwrctrl; bool has_sse_counter; bool has_tcms; - Property *props; + uint8_t props_count; + const Property *props; const ARMSSEDeviceInfo *devinfo; const bool *irq_is_common; }; -static Property iotkit_properties[] = { +static const Property iotkit_properties[] = { DEFINE_PROP_LINK("memory", ARMSSE, board_memory, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64), @@ -87,10 +88,9 @@ static Property iotkit_properties[] = { 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() }; -static Property sse200_properties[] = { +static const Property sse200_properties[] = { DEFINE_PROP_LINK("memory", ARMSSE, board_memory, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64), @@ -104,10 +104,9 @@ static Property sse200_properties[] = { 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() }; -static Property sse300_properties[] = { +static const Property sse300_properties[] = { DEFINE_PROP_LINK("memory", ARMSSE, board_memory, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64), @@ -117,7 +116,6 @@ static Property sse300_properties[] = { 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() }; static const ARMSSEDeviceInfo iotkit_devices[] = { @@ -528,6 +526,7 @@ static const ARMSSEInfo armsse_variants[] = { .has_sse_counter = false, .has_tcms = false, .props = iotkit_properties, + .props_count = ARRAY_SIZE(iotkit_properties), .devinfo = iotkit_devices, .irq_is_common = sse200_irq_is_common, }, @@ -549,6 +548,7 @@ static const ARMSSEInfo armsse_variants[] = { .has_sse_counter = false, .has_tcms = false, .props = sse200_properties, + .props_count = ARRAY_SIZE(sse200_properties), .devinfo = sse200_devices, .irq_is_common = sse200_irq_is_common, }, @@ -570,6 +570,7 @@ static const ARMSSEInfo armsse_variants[] = { .has_sse_counter = true, .has_tcms = true, .props = sse300_properties, + .props_count = ARRAY_SIZE(sse300_properties), .devinfo = sse300_devices, .irq_is_common = sse300_irq_is_common, }, @@ -1022,10 +1023,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)) { @@ -1468,7 +1467,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); @@ -1484,7 +1482,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); @@ -1499,7 +1496,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)) { @@ -1512,7 +1508,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; @@ -1605,7 +1600,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)) { @@ -1617,22 +1612,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 @@ -1681,7 +1676,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), @@ -1696,7 +1691,7 @@ static void armsse_reset(DeviceState *dev) s->nsccfg = 0; } -static void armsse_class_init(ObjectClass *klass, void *data) +static void armsse_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(klass); @@ -1705,8 +1700,8 @@ 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_props_n(dc, info->props, info->props_count); + device_class_set_legacy_reset(dc, armsse_reset); iic->check = armsse_idau_check; asc->info = info; } @@ -1718,7 +1713,7 @@ static const TypeInfo armsse_info = { .class_size = sizeof(ARMSSEClass), .instance_init = armsse_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_IDAU_INTERFACE }, { } } @@ -1735,9 +1730,9 @@ static void armsse_register_types(void) .name = armsse_variants[i].name, .parent = TYPE_ARM_SSE, .class_init = armsse_class_init, - .class_data = (void *)&armsse_variants[i], + .class_data = &armsse_variants[i], }; - type_register(&ti); + type_register_static(&ti); } } diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index bf173b10b8b..cea3eb49ee5 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -16,11 +16,14 @@ #include "hw/qdev-properties.h" #include "hw/qdev-clock.h" #include "elf.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "qemu/error-report.h" #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. */ @@ -137,7 +140,7 @@ static MemTxResult v7m_sysreg_ns_write(void *opaque, hwaddr addr, /* S accesses to the alias act like NS accesses to the real region */ attrs.secure = 0; return memory_region_dispatch_write(mr, addr, value, - size_memop(size) | MO_TE, attrs); + size_memop(size) | MO_LE, attrs); } else { /* NS attrs are RAZ/WI for privileged, and BusFault for user */ if (attrs.user) { @@ -157,7 +160,7 @@ static MemTxResult v7m_sysreg_ns_read(void *opaque, hwaddr addr, /* S accesses to the alias act like NS accesses to the real region */ attrs.secure = 0; return memory_region_dispatch_read(mr, addr, data, - size_memop(size) | MO_TE, attrs); + size_memop(size) | MO_LE, attrs); } else { /* NS attrs are RAZ/WI for privileged, and BusFault for user */ if (attrs.user) { @@ -171,7 +174,7 @@ static MemTxResult v7m_sysreg_ns_read(void *opaque, hwaddr addr, static const MemoryRegionOps v7m_sysreg_ns_ops = { .read_with_attrs = v7m_sysreg_ns_read, .write_with_attrs = v7m_sysreg_ns_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static MemTxResult v7m_systick_write(void *opaque, hwaddr addr, @@ -184,7 +187,7 @@ static MemTxResult v7m_systick_write(void *opaque, hwaddr addr, /* Direct the access to the correct systick */ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0); return memory_region_dispatch_write(mr, addr, value, - size_memop(size) | MO_TE, attrs); + size_memop(size) | MO_LE, attrs); } static MemTxResult v7m_systick_read(void *opaque, hwaddr addr, @@ -196,14 +199,14 @@ static MemTxResult v7m_systick_read(void *opaque, hwaddr addr, /* Direct the access to the correct systick */ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0); - return memory_region_dispatch_read(mr, addr, data, size_memop(size) | MO_TE, - attrs); + return memory_region_dispatch_read(mr, addr, data, + size_memop(size) | MO_LE, attrs); } static const MemoryRegionOps v7m_systick_ops = { .read_with_attrs = v7m_systick_read, .write_with_attrs = v7m_systick_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; /* @@ -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,8 @@ 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 @@ -517,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)) { @@ -537,7 +538,7 @@ static void armv7m_realize(DeviceState *dev, Error **errp) } } -static Property armv7m_properties[] = { +static const Property armv7m_properties[] = { DEFINE_PROP_STRING("cpu-type", ARMv7MState, cpu_type), DEFINE_PROP_LINK("memory", ARMv7MState, board_memory, TYPE_MEMORY_REGION, MemoryRegion *), @@ -551,21 +552,20 @@ static Property armv7m_properties[] = { 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(), }; 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() } }; -static void armv7m_class_init(ObjectClass *klass, void *data) +static void armv7m_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -608,7 +608,7 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, if (kernel_filename) { image_size = load_elf_as(kernel_filename, NULL, NULL, NULL, &entry, NULL, NULL, - NULL, 0, EM_ARM, 1, 0, as); + NULL, ELFDATA2LSB, EM_ARM, 1, 0, as); if (image_size < 0) { image_size = load_image_targphys_as(kernel_filename, mem_base, mem_size, as); @@ -630,14 +630,13 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, qemu_register_reset(armv7m_reset, cpu); } -static Property bitband_properties[] = { +static const Property bitband_properties[] = { DEFINE_PROP_UINT32("base", BitBandState, base, 0), DEFINE_PROP_LINK("source-memory", BitBandState, source_memory, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; -static void bitband_class_init(ObjectClass *klass, void *data) +static void bitband_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index f8ba67531a0..d0b333646e9 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -18,18 +18,19 @@ #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" -#include "sysemu/block-backend.h" -#include "sysemu/reset.h" +#include "system/block-backend.h" +#include "system/reset.h" #include "hw/loader.h" #include "qemu/error-report.h" +#include "qemu/datadir.h" #include "qemu/units.h" #include "hw/qdev-clock.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static struct arm_boot_info aspeed_board_binfo = { .board_id = -1, /* device-tree-only board */ @@ -40,12 +41,13 @@ 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 */ @@ -178,12 +180,16 @@ struct AspeedMachineState { #define AST2600_EVB_HW_STRAP1 0x000000C0 #define AST2600_EVB_HW_STRAP2 0x00000003 -/* Tacoma hardware value */ -#define TACOMA_BMC_HW_STRAP1 0x00000000 -#define TACOMA_BMC_HW_STRAP2 0x00000040 +#ifdef TARGET_AARCH64 +/* AST2700 evb hardware value */ +/* SCU HW Strap1 */ +#define AST2700_EVB_HW_STRAP1 0x00000800 +/* SCUIO HW Strap1 */ +#define AST2700_EVB_HW_STRAP2 0x00000700 +#endif /* 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 */ @@ -259,7 +265,8 @@ static void write_boot_rom(BlockBackend *blk, hwaddr addr, size_t rom_size, 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); @@ -288,13 +295,42 @@ static void write_boot_rom(BlockBackend *blk, hwaddr addr, size_t rom_size, static void aspeed_install_boot_rom(AspeedMachineState *bmc, BlockBackend *blk, uint64_t rom_size) { - AspeedSoCState *soc = &bmc->soc; + 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, ASPEED_SOC_SPI_BOOT_ADDR, rom_size, &error_abort); + write_boot_rom(blk, sc->memmap[ASPEED_DEV_SPI_BOOT], + rom_size, &error_abort); +} + +#define VBOOTROM_FILE_NAME "ast27x0_bootrom.bin" + +/* + * This function locates the vbootrom image file specified via the command line + * using the -bios option. It loads the specified image into the vbootrom + * memory region and handles errors if the file cannot be found or loaded. + */ +static void aspeed_load_vbootrom(AspeedMachineState *bmc, const char *bios_name, + Error **errp) +{ + g_autofree char *filename = NULL; + AspeedSoCState *soc = bmc->soc; + int ret; + + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!filename) { + error_setg(errp, "Could not find vbootrom image '%s'", bios_name); + return; + } + + ret = load_image_mr(filename, &soc->vbootrom); + if (ret < 0) { + error_setg(errp, "Failed to load vbootrom image '%s'", bios_name); + return; + } } void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, @@ -319,14 +355,30 @@ void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, } } -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, @@ -337,16 +389,16 @@ 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, uart_chosen, serial_hd(0)); - for (int i = 1, uart = ASPEED_DEV_UART1; i < sc->uarts_num; i++, uart++) { + for (int i = 1, uart = sc->uarts_base; i < sc->uarts_num; uart++) { if (uart == uart_chosen) { continue; } - aspeed_soc_uart_set_chr(s, uart, serial_hd(i)); + aspeed_soc_uart_set_chr(s, uart, serial_hd(i++)); } } @@ -356,52 +408,61 @@ static void aspeed_machine_init(MachineState *machine) AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(machine); AspeedSoCClass *sc; int i; - NICInfo *nd = &nd_table[0]; + const char *bios_name = NULL; + 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 (amc->sdhci_wp_inverted) { + for (i = 0; i < bmc->soc->sdhci.num_slots; i++) { + object_property_set_bool(OBJECT(&bmc->soc->sdhci.slots[i]), + "wp-inverted", true, &error_abort); + } + } if (machine->kernel_filename) { /* * When booting with a -kernel command line there is no u-boot * 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); if (defaults_enabled()) { - aspeed_board_init_flashes(&bmc->soc.fmc, + aspeed_board_init_flashes(&bmc->soc->fmc, bmc->fmc_model ? bmc->fmc_model : amc->fmc_model, amc->num_cs, 0); - aspeed_board_init_flashes(&bmc->soc.spi[0], + aspeed_board_init_flashes(&bmc->soc->spi[0], bmc->spi_model ? bmc->spi_model : amc->spi_model, 1, amc->num_cs); } @@ -426,37 +487,48 @@ 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 && defaults_enabled(); 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 && defaults_enabled()) { + 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); + DeviceState *dev = ssi_get_cs(bmc->soc->fmc.spi, 0); BlockBackend *fmc0 = dev ? m25p80_get_blk(dev) : NULL; - if (fmc0) { - uint64_t rom_size = memory_region_size(&bmc->soc.spi_boot); + 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); } } + if (amc->vbootrom) { + bios_name = machine->firmware ?: VBOOTROM_FILE_NAME; + aspeed_load_vbootrom(bmc, bios_name, &error_abort); + } + arm_load_kernel(ARM_CPU(first_cpu), machine, &aspeed_board_binfo); } 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, @@ -473,7 +545,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 @@ -505,7 +577,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, @@ -518,7 +590,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, @@ -531,7 +603,7 @@ static void ast2600_evb_i2c_init(AspeedMachineState *bmc) static void yosemitev2_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + 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, @@ -545,16 +617,18 @@ static void yosemitev2_bmc_i2c_init(AspeedMachineState *bmc) 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; + 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, @@ -573,7 +647,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); @@ -627,7 +701,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; @@ -656,8 +730,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, @@ -672,7 +748,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), @@ -708,7 +784,7 @@ static void g220a_bmc_i2c_init(AspeedMachineState *bmc) static void fp5280g2_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; I2CSlave *i2c_mux; /* The at24c256 */ @@ -735,7 +811,7 @@ 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; at24c_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 0), 0x51, 32 * KiB); @@ -852,7 +928,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++) { @@ -930,7 +1006,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)) { @@ -976,7 +1052,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++) { @@ -1008,14 +1084,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 */ @@ -1057,7 +1133,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) @@ -1094,7 +1173,7 @@ static char *aspeed_get_bmc_console(Object *obj, Error **errp) 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", uart_chosen - ASPEED_DEV_UART1 + 1); + return g_strdup_printf("uart%d", aspeed_uart_index(uart_chosen)); } static void aspeed_set_bmc_console(Object *obj, const char *value, Error **errp) @@ -1103,6 +1182,8 @@ static void aspeed_set_bmc_console(Object *obj, const char *value, Error **errp) 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"); @@ -1110,11 +1191,12 @@ static void aspeed_set_bmc_console(Object *obj, const char *value, Error **errp) } /* The number of UART depends on the SoC */ - if (val < 1 || val > sc->uarts_num) { - error_setg(errp, "\"uart\" should be in range [1 - %d]", sc->uarts_num); + 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 = ASPEED_DEV_UART1 + val - 1; + bmc->uart_chosen = val + ASPEED_DEV_UART0; } static void aspeed_machine_class_props_init(ObjectClass *oc) @@ -1140,13 +1222,46 @@ 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) +{ + 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) { - AspeedSoCClass *sc = ASPEED_SOC_CLASS(object_class_by_name(soc_name)); - return sc->num_cpus; + 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) +static void aspeed_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1162,7 +1277,8 @@ static void aspeed_machine_class_init(ObjectClass *oc, void *data) aspeed_machine_class_props_init(oc); } -static void aspeed_machine_palmetto_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_palmetto_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1174,12 +1290,13 @@ static void aspeed_machine_palmetto_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx25l25635f"; amc->num_cs = 1; amc->i2c_init = palmetto_bmc_i2c_init; + mc->auto_create_sdcard = true; 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) +static void aspeed_machine_quanta_q71l_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1191,13 +1308,13 @@ static void aspeed_machine_quanta_q71l_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx25l25635e"; amc->num_cs = 1; amc->i2c_init = quanta_q71l_bmc_i2c_init; + mc->auto_create_sdcard = true; 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, - void *data) + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1210,11 +1327,13 @@ static void aspeed_machine_supermicrox11_bmc_class_init(ObjectClass *oc, amc->num_cs = 1; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = palmetto_bmc_i2c_init; + mc->auto_create_sdcard = true; 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) + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1227,12 +1346,13 @@ static void aspeed_machine_supermicro_x11spi_bmc_class_init(ObjectClass *oc, amc->num_cs = 1; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = palmetto_bmc_i2c_init; + mc->auto_create_sdcard = true; 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_ast2500_evb_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1244,12 +1364,13 @@ static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx25l25635f"; amc->num_cs = 1; amc->i2c_init = ast2500_evb_i2c_init; + mc->auto_create_sdcard = true; 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) +static void aspeed_machine_yosemitev2_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1262,12 +1383,13 @@ static void aspeed_machine_yosemitev2_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx25l25635e"; amc->num_cs = 2; amc->i2c_init = yosemitev2_bmc_i2c_init; + mc->auto_create_sdcard = true; 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_romulus_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_romulus_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1279,12 +1401,13 @@ static void aspeed_machine_romulus_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = romulus_bmc_i2c_init; + mc->auto_create_sdcard = true; 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) +static void aspeed_machine_tiogapass_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1297,13 +1420,13 @@ static void aspeed_machine_tiogapass_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx25l25635e"; amc->num_cs = 2; amc->i2c_init = tiogapass_bmc_i2c_init; + mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); - aspeed_soc_num_cpus(amc->soc_name); + aspeed_machine_class_init_cpus_defaults(mc); }; -static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1315,12 +1438,13 @@ static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = sonorapass_bmc_i2c_init; + mc->auto_create_sdcard = true; 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) +static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1332,12 +1456,13 @@ static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, void *data) amc->spi_model = "mx66l1g45g"; amc->num_cs = 2; amc->i2c_init = witherspoon_bmc_i2c_init; + mc->auto_create_sdcard = true; 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) +static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1351,32 +1476,15 @@ static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, void *data) amc->num_cs = 1; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON | ASPEED_MAC2_ON | ASPEED_MAC3_ON; + amc->sdhci_wp_inverted = true; amc->i2c_init = ast2600_evb_i2c_init; + mc->auto_create_sdcard = true; 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) -{ - MachineClass *mc = MACHINE_CLASS(oc); - AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); - - mc->desc = "OpenPOWER Tacoma BMC (Cortex-A7)"; - amc->soc_name = "ast2600-a3"; - amc->hw_strap1 = TACOMA_BMC_HW_STRAP1; - amc->hw_strap2 = TACOMA_BMC_HW_STRAP2; - amc->fmc_model = "mx66l1g45g"; - amc->spi_model = "mx66l1g45g"; - amc->num_cs = 2; - amc->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); -}; - -static void aspeed_machine_g220a_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_g220a_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1389,12 +1497,13 @@ static void aspeed_machine_g220a_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = g220a_bmc_i2c_init; + mc->auto_create_sdcard = true; 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) +static void aspeed_machine_fp5280g2_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1407,12 +1516,12 @@ static void aspeed_machine_fp5280g2_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; amc->i2c_init = fp5280g2_bmc_i2c_init; + mc->auto_create_sdcard = true; 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) +static void aspeed_machine_rainier_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1426,14 +1535,15 @@ static void aspeed_machine_rainier_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = rainier_bmc_i2c_init; + mc->auto_create_sdcard = true; 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); }; #define FUJI_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB) -static void aspeed_machine_fuji_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_fuji_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1448,14 +1558,15 @@ static void aspeed_machine_fuji_class_init(ObjectClass *oc, void *data) amc->macs_mask = ASPEED_MAC3_ON; amc->i2c_init = fuji_bmc_i2c_init; amc->uart_default = ASPEED_DEV_UART1; + mc->auto_create_sdcard = true; mc->default_ram_size = FUJI_BMC_RAM_SIZE; - mc->default_cpus = mc->min_cpus = mc->max_cpus = - aspeed_soc_num_cpus(amc->soc_name); + aspeed_machine_class_init_cpus_defaults(mc); }; #define BLETCHLEY_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB) -static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_bletchley_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1469,17 +1580,17 @@ static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON; amc->i2c_init = bletchley_bmc_i2c_init; + mc->auto_create_sdcard = true; 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); @@ -1500,7 +1611,7 @@ static void fby35_reset(MachineState *state, ShutdownCause reason) object_property_set_bool(OBJECT(gpio), "gpioB5", false, &error_fatal); } -static void aspeed_machine_fby35_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_fby35_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1511,8 +1622,10 @@ static void aspeed_machine_fby35_class_init(ObjectClass *oc, void *data) amc->num_cs = 2; amc->macs_mask = ASPEED_MAC3_ON; amc->i2c_init = fby35_i2c_init; + mc->auto_create_sdcard = true; /* 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) @@ -1528,26 +1641,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); - - aspeed_board_init_flashes(&bmc->soc.fmc, - bmc->fmc_model ? bmc->fmc_model : amc->fmc_model, - amc->num_cs, - 0); + qdev_realize(DEVICE(bmc->soc), NULL, &error_abort); - 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)); + 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[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); @@ -1561,7 +1678,7 @@ 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 Group 1 by default */ uint8_t *eeprom_buf = g_malloc0(32 * 1024); @@ -1572,7 +1689,7 @@ static void ast1030_evb_i2c_init(AspeedMachineState *bmc) } static void aspeed_minibmc_machine_ast1030_evb_class_init(ObjectClass *oc, - void *data) + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1584,15 +1701,71 @@ 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_ast2700a0_evb_class_init(ObjectClass *oc, + const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->alias = "ast2700-evb"; + mc->desc = "Aspeed AST2700 A0 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; + amc->vbootrom = true; + mc->auto_create_sdcard = true; + mc->default_ram_size = 1 * GiB; + aspeed_machine_class_init_cpus_defaults(mc); +} + +static void aspeed_machine_ast2700a1_evb_class_init(ObjectClass *oc, + const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Aspeed AST2700 A1 EVB (Cortex-A35)"; + amc->soc_name = "ast2700-a1"; + 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; + amc->vbootrom = true; + mc->auto_create_sdcard = true; + 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) + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1606,13 +1779,13 @@ static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = qcom_dc_scm_bmc_i2c_init; + mc->auto_create_sdcard = true; 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, - void *data) + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1626,9 +1799,9 @@ static void aspeed_machine_qcom_firework_class_init(ObjectClass *oc, amc->num_cs = 2; amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = qcom_dc_scm_firework_i2c_init; + mc->auto_create_sdcard = true; 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[] = { @@ -1668,10 +1841,6 @@ static const TypeInfo aspeed_machine_types[] = { .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, @@ -1716,6 +1885,16 @@ 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("ast2700a0-evb"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_ast2700a0_evb_class_init, + }, { + .name = MACHINE_TYPE_NAME("ast2700a1-evb"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_ast2700a1_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 649b3b13c13..e6e1ee63c1c 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -11,8 +11,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "exec/address-spaces.h" -#include "sysemu/sysemu.h" +#include "system/address-spaces.h" +#include "system/system.h" #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" #include "hw/arm/aspeed_soc.h" @@ -101,24 +101,26 @@ static const int aspeed_soc_ast1030_irqmap[] = { 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]; char typename[64]; int i; - if (sscanf(sc->name, "%7s", socname) != 1) { + if (sscanf(object_get_typename(obj), "%7s", socname) != 1) { 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); @@ -185,6 +187,7 @@ static void aspeed_soc_ast1030_init(Object *obj) 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; @@ -206,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); @@ -249,7 +252,7 @@ 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); @@ -261,7 +264,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, 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->armv7m), + 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); @@ -290,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 */ @@ -412,15 +415,20 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) sc->memmap[ASPEED_DEV_JTAG1], 0x20); } -static void aspeed_soc_ast1030_class_init(ObjectClass *klass, void *data) +static void aspeed_soc_ast1030_class_init(ObjectClass *klass, const 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"); /* TODO cortex-m4f */ + sc->valid_cpu_types = valid_cpu_types; sc->silicon_rev = AST1030_A1_SILICON_REV; sc->sram_size = 0xc0000; sc->secsram_size = 0x40000; /* 256 * KiB */ @@ -429,24 +437,25 @@ static void aspeed_soc_ast1030_class_init(ObjectClass *klass, void *data) 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 00000000000..c7b0f21887b --- /dev/null +++ b/hw/arm/aspeed_ast2400.c @@ -0,0 +1,579 @@ +/* + * 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 "system/system.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(object_get_typename(obj), "%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); + + snprintf(typename, sizeof(typename), "aspeed.sdhci-%s", socname); + object_initialize_child(obj, "sdc", &s->sdhci, typename); + + 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, const 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->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, const 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->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 a8b3a8065a1..d12707f0abe 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -15,13 +15,14 @@ #include "qemu/error-report.h" #include "hw/i2c/aspeed_i2c.h" #include "net/net.h" -#include "sysemu/sysemu.h" +#include "system/system.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] = ASPEED_SOC_SPI_BOOT_ADDR, + [ASPEED_DEV_SPI_BOOT] = 0x00000000, [ASPEED_DEV_SRAM] = 0x10000000, [ASPEED_DEV_DPMCU] = 0x18000000, /* 0x16000000 0x17FFFFFF : AHB BUS do LPC Bus bridge */ @@ -75,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, }; @@ -132,30 +135,35 @@ 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; char socname[8]; char typename[64]; - if (sscanf(sc->name, "%7s", socname) != 1) { + if (sscanf(object_get_typename(obj), "%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); + object_initialize_child(obj, "cpu[*]", &a->cpu[i], + aspeed_soc_cpu_type(sc)); } snprintf(typename, sizeof(typename), "aspeed.scu-%s", socname); @@ -169,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); @@ -228,8 +236,8 @@ static void aspeed_soc_ast2600_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.gpio-%s-1_8v", socname); object_initialize_child(obj, "gpio_1_8v", &s->gpio_1_8v, typename); - object_initialize_child(obj, "sd-controller", &s->sdhci, - TYPE_ASPEED_SDHCI); + snprintf(typename, sizeof(typename), "aspeed.sdhci-%s", socname); + object_initialize_child(obj, "sd-controller", &s->sdhci, typename); object_property_set_int(OBJECT(&s->sdhci), "num-slots", 2, &error_abort); @@ -239,8 +247,7 @@ static void aspeed_soc_ast2600_init(Object *obj) &s->sdhci.slots[i], TYPE_SYSBUS_SDHCI); } - object_initialize_child(obj, "emmc-controller", &s->emmc, - TYPE_ASPEED_SDHCI); + object_initialize_child(obj, "emmc-controller", &s->emmc, typename); object_property_set_int(OBJECT(&s->emmc), "num-slots", 1, &error_abort); @@ -262,6 +269,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); + } } /* @@ -277,9 +288,9 @@ 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; @@ -306,39 +317,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_bool(OBJECT(&s->cpu[i]), "vfp-d32", false, + object_property_set_bool(OBJECT(&a->cpu[i]), "vfp-d32", false, &error_abort); - object_property_set_link(OBJECT(&s->cpu[i]), "memory", + 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); @@ -351,10 +362,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, @@ -388,7 +398,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); } @@ -413,8 +423,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); } @@ -530,7 +540,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) 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]); + 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)); @@ -579,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 */ @@ -611,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); } @@ -622,17 +633,40 @@ 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 void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data) +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, const 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; @@ -640,24 +674,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 void aspeed_soc_register_types(void) -{ - type_register_static(&aspeed_soc_ast2600_type_info); +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, + }, }; -type_init(aspeed_soc_register_types) +DEFINE_TYPES(aspeed_soc_ast2600_types) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c new file mode 100644 index 00000000000..7087be4288b --- /dev/null +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -0,0 +1,200 @@ +/* + * ASPEED SoC 2700 family + * + * Copyright (C) 2025 ASPEED Technology Inc. + * + * This code is licensed under the 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 "qemu/units.h" +#include "qapi/error.h" +#include "system/block-backend.h" +#include "system/system.h" +#include "hw/arm/aspeed.h" +#include "hw/boards.h" +#include "hw/qdev-clock.h" +#include "hw/arm/aspeed_soc.h" +#include "hw/loader.h" +#include "hw/arm/boot.h" +#include "hw/block/flash.h" + + +#define TYPE_AST2700A1FC MACHINE_TYPE_NAME("ast2700fc") +OBJECT_DECLARE_SIMPLE_TYPE(Ast2700FCState, AST2700A1FC); + +static struct arm_boot_info ast2700fc_board_info = { + .board_id = -1, /* device-tree-only board */ +}; + +struct Ast2700FCState { + MachineState parent_obj; + + MemoryRegion ca35_memory; + MemoryRegion ca35_dram; + MemoryRegion ssp_memory; + MemoryRegion tsp_memory; + + Clock *ssp_sysclk; + Clock *tsp_sysclk; + + Aspeed27x0SoCState ca35; + Aspeed27x0SSPSoCState ssp; + Aspeed27x0TSPSoCState tsp; + + bool mmio_exec; +}; + +#define AST2700FC_BMC_RAM_SIZE (1 * GiB) +#define AST2700FC_CM4_DRAM_SIZE (32 * MiB) + +#define AST2700FC_HW_STRAP1 0x000000C0 +#define AST2700FC_HW_STRAP2 0x00000003 +#define AST2700FC_FMC_MODEL "w25q01jvq" +#define AST2700FC_SPI_MODEL "w25q512jv" + +static void ast2700fc_ca35_init(MachineState *machine) +{ + Ast2700FCState *s = AST2700A1FC(machine); + AspeedSoCState *soc; + AspeedSoCClass *sc; + + object_initialize_child(OBJECT(s), "ca35", &s->ca35, "ast2700-a1"); + soc = ASPEED_SOC(&s->ca35); + sc = ASPEED_SOC_GET_CLASS(soc); + + memory_region_init(&s->ca35_memory, OBJECT(&s->ca35), "ca35-memory", + UINT64_MAX); + memory_region_add_subregion(get_system_memory(), 0, &s->ca35_memory); + + if (!memory_region_init_ram(&s->ca35_dram, OBJECT(&s->ca35), "ca35-dram", + AST2700FC_BMC_RAM_SIZE, &error_abort)) { + return; + } + if (!object_property_set_link(OBJECT(&s->ca35), "memory", + OBJECT(&s->ca35_memory), + &error_abort)) { + return; + }; + if (!object_property_set_link(OBJECT(&s->ca35), "dram", + OBJECT(&s->ca35_dram), &error_abort)) { + return; + } + if (!object_property_set_int(OBJECT(&s->ca35), "ram-size", + AST2700FC_BMC_RAM_SIZE, &error_abort)) { + return; + } + + for (int i = 0; i < sc->macs_num; i++) { + if (!qemu_configure_nic_device(DEVICE(&soc->ftgmac100[i]), + true, NULL)) { + break; + } + } + if (!object_property_set_int(OBJECT(&s->ca35), "hw-strap1", + AST2700FC_HW_STRAP1, &error_abort)) { + return; + } + if (!object_property_set_int(OBJECT(&s->ca35), "hw-strap2", + AST2700FC_HW_STRAP2, &error_abort)) { + return; + } + aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART12, serial_hd(0)); + if (!qdev_realize(DEVICE(&s->ca35), NULL, &error_abort)) { + return; + } + + /* + * AST2700 EVB has a LM75 temperature sensor on I2C bus 0 at address 0x4d. + */ + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 0), "tmp105", 0x4d); + + aspeed_board_init_flashes(&soc->fmc, AST2700FC_FMC_MODEL, 2, 0); + aspeed_board_init_flashes(&soc->spi[0], AST2700FC_SPI_MODEL, 1, 2); + + ast2700fc_board_info.ram_size = machine->ram_size; + ast2700fc_board_info.loader_start = sc->memmap[ASPEED_DEV_SDRAM]; + + arm_load_kernel(ARM_CPU(first_cpu), machine, &ast2700fc_board_info); +} + +static void ast2700fc_ssp_init(MachineState *machine) +{ + AspeedSoCState *soc; + Ast2700FCState *s = AST2700A1FC(machine); + s->ssp_sysclk = clock_new(OBJECT(s), "SSP_SYSCLK"); + clock_set_hz(s->ssp_sysclk, 200000000ULL); + + object_initialize_child(OBJECT(s), "ssp", &s->ssp, TYPE_ASPEED27X0SSP_SOC); + memory_region_init(&s->ssp_memory, OBJECT(&s->ssp), "ssp-memory", + UINT64_MAX); + + qdev_connect_clock_in(DEVICE(&s->ssp), "sysclk", s->ssp_sysclk); + if (!object_property_set_link(OBJECT(&s->ssp), "memory", + OBJECT(&s->ssp_memory), &error_abort)) { + return; + } + + soc = ASPEED_SOC(&s->ssp); + aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART4, serial_hd(1)); + if (!qdev_realize(DEVICE(&s->ssp), NULL, &error_abort)) { + return; + } +} + +static void ast2700fc_tsp_init(MachineState *machine) +{ + AspeedSoCState *soc; + Ast2700FCState *s = AST2700A1FC(machine); + s->tsp_sysclk = clock_new(OBJECT(s), "TSP_SYSCLK"); + clock_set_hz(s->tsp_sysclk, 200000000ULL); + + object_initialize_child(OBJECT(s), "tsp", &s->tsp, TYPE_ASPEED27X0TSP_SOC); + memory_region_init(&s->tsp_memory, OBJECT(&s->tsp), "tsp-memory", + UINT64_MAX); + + qdev_connect_clock_in(DEVICE(&s->tsp), "sysclk", s->tsp_sysclk); + if (!object_property_set_link(OBJECT(&s->tsp), "memory", + OBJECT(&s->tsp_memory), &error_abort)) { + return; + } + + soc = ASPEED_SOC(&s->tsp); + aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART7, serial_hd(2)); + if (!qdev_realize(DEVICE(&s->tsp), NULL, &error_abort)) { + return; + } +} + +static void ast2700fc_init(MachineState *machine) +{ + ast2700fc_ca35_init(machine); + ast2700fc_ssp_init(machine); + ast2700fc_tsp_init(machine); +} + +static void ast2700fc_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->alias = "ast2700fc"; + mc->desc = "ast2700 full core support"; + mc->init = ast2700fc_init; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->min_cpus = mc->max_cpus = mc->default_cpus = 6; +} + +static const TypeInfo ast2700fc_types[] = { + { + .name = MACHINE_TYPE_NAME("ast2700fc"), + .parent = TYPE_MACHINE, + .class_init = ast2700fc_class_init, + .instance_size = sizeof(Ast2700FCState), + }, +}; + +DEFINE_TYPES(ast2700fc_types) diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c new file mode 100644 index 00000000000..80ec5996c1d --- /dev/null +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -0,0 +1,294 @@ +/* + * ASPEED Ast27x0 SSP SoC + * + * Copyright (C) 2025 ASPEED Technology Inc. + * + * This code is licensed under the 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 "qapi/error.h" +#include "hw/qdev-clock.h" +#include "hw/misc/unimp.h" +#include "hw/arm/aspeed_soc.h" + +#define AST2700_SSP_RAM_SIZE (32 * MiB) + +static const hwaddr aspeed_soc_ast27x0ssp_memmap[] = { + [ASPEED_DEV_SRAM] = 0x00000000, + [ASPEED_DEV_INTC] = 0x72100000, + [ASPEED_DEV_SCU] = 0x72C02000, + [ASPEED_DEV_SCUIO] = 0x74C02000, + [ASPEED_DEV_UART0] = 0x74C33000, + [ASPEED_DEV_UART1] = 0x74C33100, + [ASPEED_DEV_UART2] = 0x74C33200, + [ASPEED_DEV_UART3] = 0x74C33300, + [ASPEED_DEV_UART4] = 0x72C1A000, + [ASPEED_DEV_INTCIO] = 0x74C18000, + [ASPEED_DEV_IPC0] = 0x72C1C000, + [ASPEED_DEV_IPC1] = 0x74C39000, + [ASPEED_DEV_UART5] = 0x74C33400, + [ASPEED_DEV_UART6] = 0x74C33500, + [ASPEED_DEV_UART7] = 0x74C33600, + [ASPEED_DEV_UART8] = 0x74C33700, + [ASPEED_DEV_UART9] = 0x74C33800, + [ASPEED_DEV_UART10] = 0x74C33900, + [ASPEED_DEV_UART11] = 0x74C33A00, + [ASPEED_DEV_UART12] = 0x74C33B00, + [ASPEED_DEV_TIMER1] = 0x72C10000, +}; + +static const int aspeed_soc_ast27x0ssp_irqmap[] = { + [ASPEED_DEV_SCU] = 12, + [ASPEED_DEV_UART0] = 164, + [ASPEED_DEV_UART1] = 164, + [ASPEED_DEV_UART2] = 164, + [ASPEED_DEV_UART3] = 164, + [ASPEED_DEV_UART4] = 8, + [ASPEED_DEV_UART5] = 164, + [ASPEED_DEV_UART6] = 164, + [ASPEED_DEV_UART7] = 164, + [ASPEED_DEV_UART8] = 164, + [ASPEED_DEV_UART9] = 164, + [ASPEED_DEV_UART10] = 164, + [ASPEED_DEV_UART11] = 164, + [ASPEED_DEV_UART12] = 164, + [ASPEED_DEV_TIMER1] = 16, +}; + +/* SSPINT 164 */ +static const int ast2700_ssp132_ssp164_intcmap[] = { + [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, +}; + +struct nvic_intc_irq_info { + int irq; + int intc_idx; + int orgate_idx; + const int *ptr; +}; + +static struct nvic_intc_irq_info ast2700_ssp_intcmap[] = { + {160, 1, 0, NULL}, + {161, 1, 1, NULL}, + {162, 1, 2, NULL}, + {163, 1, 3, NULL}, + {164, 1, 4, ast2700_ssp132_ssp164_intcmap}, + {165, 1, 5, NULL}, + {166, 1, 6, NULL}, + {167, 1, 7, NULL}, + {168, 1, 8, NULL}, + {169, 1, 9, NULL}, + {128, 0, 1, NULL}, + {129, 0, 2, NULL}, + {130, 0, 3, NULL}, + {131, 0, 4, NULL}, + {132, 0, 5, ast2700_ssp132_ssp164_intcmap}, + {133, 0, 6, NULL}, + {134, 0, 7, NULL}, + {135, 0, 8, NULL}, + {136, 0, 9, NULL}, +}; + +static qemu_irq aspeed_soc_ast27x0ssp_get_irq(AspeedSoCState *s, int dev) +{ + Aspeed27x0SSPSoCState *a = ASPEED27X0SSP_SOC(s); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + + int or_idx; + int idx; + int i; + + for (i = 0; i < ARRAY_SIZE(ast2700_ssp_intcmap); i++) { + if (sc->irqmap[dev] == ast2700_ssp_intcmap[i].irq) { + assert(ast2700_ssp_intcmap[i].ptr); + or_idx = ast2700_ssp_intcmap[i].orgate_idx; + idx = ast2700_ssp_intcmap[i].intc_idx; + return qdev_get_gpio_in(DEVICE(&a->intc[idx].orgates[or_idx]), + ast2700_ssp_intcmap[i].ptr[dev]); + } + } + + return qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[dev]); +} + +static void aspeed_soc_ast27x0ssp_init(Object *obj) +{ + Aspeed27x0SSPSoCState *a = ASPEED27X0SSP_SOC(obj); + AspeedSoCState *s = ASPEED_SOC(obj); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int i; + + object_initialize_child(obj, "armv7m", &a->armv7m, TYPE_ARMV7M); + object_initialize_child(obj, "scu", &s->scu, TYPE_ASPEED_2700_SCU); + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); + qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", sc->silicon_rev); + + for (i = 0; i < sc->uarts_num; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM); + } + + object_initialize_child(obj, "intc0", &a->intc[0], + TYPE_ASPEED_2700SSP_INTC); + object_initialize_child(obj, "intc1", &a->intc[1], + TYPE_ASPEED_2700SSP_INTCIO); + + object_initialize_child(obj, "timerctrl", &s->timerctrl, + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "ipc0", &a->ipc[0], + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "ipc1", &a->ipc[1], + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "scuio", &a->scuio, + TYPE_UNIMPLEMENTED_DEVICE); +} + +static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) +{ + Aspeed27x0SSPSoCState *a = ASPEED27X0SSP_SOC(dev_soc); + AspeedSoCState *s = ASPEED_SOC(dev_soc); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + DeviceState *armv7m; + g_autofree char *sram_name = NULL; + int i; + + if (!clock_has_source(s->sysclk)) { + error_setg(errp, "sysclk clock must be wired up by the board code"); + return; + } + + /* AST27X0 SSP Core */ + armv7m = DEVICE(&a->armv7m); + qdev_prop_set_uint32(armv7m, "num-irq", 256); + 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(&a->armv7m), "memory", + OBJECT(s->memory), &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&a->armv7m), &error_abort); + + sram_name = g_strdup_printf("aspeed.dram.%d", + CPU(a->armv7m.cpu)->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]); + + /* INTC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[0]), errp)) { + return; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[0]), 0, + sc->memmap[ASPEED_DEV_INTC]); + + /* INTCIO */ + if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[1]), errp)) { + return; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[1]), 0, + sc->memmap[ASPEED_DEV_INTCIO]); + + /* irq source orgates -> INTC0 */ + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[0])->num_inpins; i++) { + qdev_connect_gpio_out(DEVICE(&a->intc[0].orgates[i]), 0, + qdev_get_gpio_in(DEVICE(&a->intc[0]), i)); + } + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[0])->num_outpins; i++) { + assert(i < ARRAY_SIZE(ast2700_ssp_intcmap)); + sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc[0]), i, + qdev_get_gpio_in(DEVICE(&a->armv7m), + ast2700_ssp_intcmap[i].irq)); + } + /* irq source orgates -> INTCIO */ + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[1])->num_inpins; i++) { + qdev_connect_gpio_out(DEVICE(&a->intc[1].orgates[i]), 0, + qdev_get_gpio_in(DEVICE(&a->intc[1]), i)); + } + /* INTCIO -> INTC */ + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[1])->num_outpins; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc[1]), i, + qdev_get_gpio_in(DEVICE(&a->intc[0].orgates[0]), i)); + } + /* UART */ + if (!aspeed_soc_uart_realize(s, errp)) { + return; + } + + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->timerctrl), + "aspeed.timerctrl", + sc->memmap[ASPEED_DEV_TIMER1], 0x200); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->ipc[0]), + "aspeed.ipc0", + sc->memmap[ASPEED_DEV_IPC0], 0x1000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->ipc[1]), + "aspeed.ipc1", + sc->memmap[ASPEED_DEV_IPC1], 0x1000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->scuio), + "aspeed.scuio", + sc->memmap[ASPEED_DEV_SCUIO], 0x1000); +} + +static void aspeed_soc_ast27x0ssp_class_init(ObjectClass *klass, const 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_ast27x0ssp_realize; + + sc->valid_cpu_types = valid_cpu_types; + sc->silicon_rev = AST2700_A1_SILICON_REV; + sc->sram_size = AST2700_SSP_RAM_SIZE; + sc->spis_num = 0; + sc->ehcis_num = 0; + sc->wdts_num = 0; + sc->macs_num = 0; + sc->uarts_num = 13; + sc->uarts_base = ASPEED_DEV_UART0; + sc->irqmap = aspeed_soc_ast27x0ssp_irqmap; + sc->memmap = aspeed_soc_ast27x0ssp_memmap; + sc->num_cpus = 1; + sc->get_irq = aspeed_soc_ast27x0ssp_get_irq; +} + +static const TypeInfo aspeed_soc_ast27x0ssp_types[] = { + { + .name = TYPE_ASPEED27X0SSP_SOC, + .parent = TYPE_ASPEED_SOC, + .instance_size = sizeof(Aspeed27x0SSPSoCState), + .instance_init = aspeed_soc_ast27x0ssp_init, + .class_init = aspeed_soc_ast27x0ssp_class_init, + }, +}; + +DEFINE_TYPES(aspeed_soc_ast27x0ssp_types) diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c new file mode 100644 index 00000000000..4e0efaef07c --- /dev/null +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -0,0 +1,294 @@ +/* + * ASPEED Ast27x0 TSP SoC + * + * Copyright (C) 2025 ASPEED Technology Inc. + * + * This code is licensed under the 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 "qapi/error.h" +#include "hw/qdev-clock.h" +#include "hw/misc/unimp.h" +#include "hw/arm/aspeed_soc.h" + +#define AST2700_TSP_RAM_SIZE (32 * MiB) + +static const hwaddr aspeed_soc_ast27x0tsp_memmap[] = { + [ASPEED_DEV_SRAM] = 0x00000000, + [ASPEED_DEV_INTC] = 0x72100000, + [ASPEED_DEV_SCU] = 0x72C02000, + [ASPEED_DEV_SCUIO] = 0x74C02000, + [ASPEED_DEV_UART0] = 0x74C33000, + [ASPEED_DEV_UART1] = 0x74C33100, + [ASPEED_DEV_UART2] = 0x74C33200, + [ASPEED_DEV_UART3] = 0x74C33300, + [ASPEED_DEV_UART4] = 0x72C1A000, + [ASPEED_DEV_INTCIO] = 0x74C18000, + [ASPEED_DEV_IPC0] = 0x72C1C000, + [ASPEED_DEV_IPC1] = 0x74C39000, + [ASPEED_DEV_UART5] = 0x74C33400, + [ASPEED_DEV_UART6] = 0x74C33500, + [ASPEED_DEV_UART7] = 0x74C33600, + [ASPEED_DEV_UART8] = 0x74C33700, + [ASPEED_DEV_UART9] = 0x74C33800, + [ASPEED_DEV_UART10] = 0x74C33900, + [ASPEED_DEV_UART11] = 0x74C33A00, + [ASPEED_DEV_UART12] = 0x74C33B00, + [ASPEED_DEV_TIMER1] = 0x72C10000, +}; + +static const int aspeed_soc_ast27x0tsp_irqmap[] = { + [ASPEED_DEV_SCU] = 12, + [ASPEED_DEV_UART0] = 164, + [ASPEED_DEV_UART1] = 164, + [ASPEED_DEV_UART2] = 164, + [ASPEED_DEV_UART3] = 164, + [ASPEED_DEV_UART4] = 8, + [ASPEED_DEV_UART5] = 164, + [ASPEED_DEV_UART6] = 164, + [ASPEED_DEV_UART7] = 164, + [ASPEED_DEV_UART8] = 164, + [ASPEED_DEV_UART9] = 164, + [ASPEED_DEV_UART10] = 164, + [ASPEED_DEV_UART11] = 164, + [ASPEED_DEV_UART12] = 164, + [ASPEED_DEV_TIMER1] = 16, +}; + +/* TSPINT 164 */ +static const int ast2700_tsp132_tsp164_intcmap[] = { + [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, +}; + +struct nvic_intc_irq_info { + int irq; + int intc_idx; + int orgate_idx; + const int *ptr; +}; + +static struct nvic_intc_irq_info ast2700_tsp_intcmap[] = { + {160, 1, 0, NULL}, + {161, 1, 1, NULL}, + {162, 1, 2, NULL}, + {163, 1, 3, NULL}, + {164, 1, 4, ast2700_tsp132_tsp164_intcmap}, + {165, 1, 5, NULL}, + {166, 1, 6, NULL}, + {167, 1, 7, NULL}, + {168, 1, 8, NULL}, + {169, 1, 9, NULL}, + {128, 0, 1, NULL}, + {129, 0, 2, NULL}, + {130, 0, 3, NULL}, + {131, 0, 4, NULL}, + {132, 0, 5, ast2700_tsp132_tsp164_intcmap}, + {133, 0, 6, NULL}, + {134, 0, 7, NULL}, + {135, 0, 8, NULL}, + {136, 0, 9, NULL}, +}; + +static qemu_irq aspeed_soc_ast27x0tsp_get_irq(AspeedSoCState *s, int dev) +{ + Aspeed27x0TSPSoCState *a = ASPEED27X0TSP_SOC(s); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + + int or_idx; + int idx; + int i; + + for (i = 0; i < ARRAY_SIZE(ast2700_tsp_intcmap); i++) { + if (sc->irqmap[dev] == ast2700_tsp_intcmap[i].irq) { + assert(ast2700_tsp_intcmap[i].ptr); + or_idx = ast2700_tsp_intcmap[i].orgate_idx; + idx = ast2700_tsp_intcmap[i].intc_idx; + return qdev_get_gpio_in(DEVICE(&a->intc[idx].orgates[or_idx]), + ast2700_tsp_intcmap[i].ptr[dev]); + } + } + + return qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[dev]); +} + +static void aspeed_soc_ast27x0tsp_init(Object *obj) +{ + Aspeed27x0TSPSoCState *a = ASPEED27X0TSP_SOC(obj); + AspeedSoCState *s = ASPEED_SOC(obj); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int i; + + object_initialize_child(obj, "armv7m", &a->armv7m, TYPE_ARMV7M); + object_initialize_child(obj, "scu", &s->scu, TYPE_ASPEED_2700_SCU); + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); + qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", sc->silicon_rev); + + for (i = 0; i < sc->uarts_num; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM); + } + + object_initialize_child(obj, "intc0", &a->intc[0], + TYPE_ASPEED_2700TSP_INTC); + object_initialize_child(obj, "intc1", &a->intc[1], + TYPE_ASPEED_2700TSP_INTCIO); + + object_initialize_child(obj, "timerctrl", &s->timerctrl, + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "ipc0", &a->ipc[0], + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "ipc1", &a->ipc[1], + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "scuio", &a->scuio, + TYPE_UNIMPLEMENTED_DEVICE); +} + +static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) +{ + Aspeed27x0TSPSoCState *a = ASPEED27X0TSP_SOC(dev_soc); + AspeedSoCState *s = ASPEED_SOC(dev_soc); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + DeviceState *armv7m; + g_autofree char *sram_name = NULL; + int i; + + if (!clock_has_source(s->sysclk)) { + error_setg(errp, "sysclk clock must be wired up by the board code"); + return; + } + + /* AST27X0 TSP Core */ + armv7m = DEVICE(&a->armv7m); + qdev_prop_set_uint32(armv7m, "num-irq", 256); + 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(&a->armv7m), "memory", + OBJECT(s->memory), &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&a->armv7m), &error_abort); + + sram_name = g_strdup_printf("aspeed.dram.%d", + CPU(a->armv7m.cpu)->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]); + + /* INTC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[0]), errp)) { + return; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[0]), 0, + sc->memmap[ASPEED_DEV_INTC]); + + /* INTCIO */ + if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[1]), errp)) { + return; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[1]), 0, + sc->memmap[ASPEED_DEV_INTCIO]); + + /* irq source orgates -> INTC */ + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[0])->num_inpins; i++) { + qdev_connect_gpio_out(DEVICE(&a->intc[0].orgates[i]), 0, + qdev_get_gpio_in(DEVICE(&a->intc[0]), i)); + } + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[0])->num_outpins; i++) { + assert(i < ARRAY_SIZE(ast2700_tsp_intcmap)); + sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc[0]), i, + qdev_get_gpio_in(DEVICE(&a->armv7m), + ast2700_tsp_intcmap[i].irq)); + } + /* irq source orgates -> INTC */ + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[1])->num_inpins; i++) { + qdev_connect_gpio_out(DEVICE(&a->intc[1].orgates[i]), 0, + qdev_get_gpio_in(DEVICE(&a->intc[1]), i)); + } + /* INTCIO -> INTC */ + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[1])->num_outpins; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc[1]), i, + qdev_get_gpio_in(DEVICE(&a->intc[0].orgates[0]), i)); + } + /* UART */ + if (!aspeed_soc_uart_realize(s, errp)) { + return; + } + + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->timerctrl), + "aspeed.timerctrl", + sc->memmap[ASPEED_DEV_TIMER1], 0x200); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->ipc[0]), + "aspeed.ipc0", + sc->memmap[ASPEED_DEV_IPC0], 0x1000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->ipc[1]), + "aspeed.ipc1", + sc->memmap[ASPEED_DEV_IPC1], 0x1000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->scuio), + "aspeed.scuio", + sc->memmap[ASPEED_DEV_SCUIO], 0x1000); +} + +static void aspeed_soc_ast27x0tsp_class_init(ObjectClass *klass, const 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_ast27x0tsp_realize; + + sc->valid_cpu_types = valid_cpu_types; + sc->silicon_rev = AST2700_A1_SILICON_REV; + sc->sram_size = AST2700_TSP_RAM_SIZE; + sc->spis_num = 0; + sc->ehcis_num = 0; + sc->wdts_num = 0; + sc->macs_num = 0; + sc->uarts_num = 13; + sc->uarts_base = ASPEED_DEV_UART0; + sc->irqmap = aspeed_soc_ast27x0tsp_irqmap; + sc->memmap = aspeed_soc_ast27x0tsp_memmap; + sc->num_cpus = 1; + sc->get_irq = aspeed_soc_ast27x0tsp_get_irq; +} + +static const TypeInfo aspeed_soc_ast27x0tsp_types[] = { + { + .name = TYPE_ASPEED27X0TSP_SOC, + .parent = TYPE_ASPEED_SOC, + .instance_size = sizeof(Aspeed27x0TSPSoCState), + .instance_init = aspeed_soc_ast27x0tsp_init, + .class_init = aspeed_soc_ast27x0tsp_class_init, + }, +}; + +DEFINE_TYPES(aspeed_soc_ast27x0tsp_types) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c new file mode 100644 index 00000000000..6aa3841b691 --- /dev/null +++ b/hw/arm/aspeed_ast27x0.c @@ -0,0 +1,1037 @@ +/* + * 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 "system/system.h" +#include "hw/intc/arm_gicv3.h" +#include "qobject/qlist.h" +#include "qemu/log.h" + +#define AST2700_SOC_IO_SIZE 0x00FE0000 +#define AST2700_SOC_IOMEM_SIZE 0x01000000 +#define AST2700_SOC_DPMCU_SIZE 0x00040000 +#define AST2700_SOC_LTPI_SIZE 0x01000000 + +static const hwaddr aspeed_soc_ast2700_memmap[] = { + [ASPEED_DEV_VBOOTROM] = 0x00000000, + [ASPEED_DEV_IOMEM] = 0x00020000, + [ASPEED_DEV_SRAM] = 0x10000000, + [ASPEED_DEV_DPMCU] = 0x11000000, + [ASPEED_DEV_IOMEM0] = 0x12000000, + [ASPEED_DEV_EHCI1] = 0x12061000, + [ASPEED_DEV_EHCI2] = 0x12063000, + [ASPEED_DEV_HACE] = 0x12070000, + [ASPEED_DEV_EMMC] = 0x12090000, + [ASPEED_DEV_INTC] = 0x12100000, + [ASPEED_GIC_DIST] = 0x12200000, + [ASPEED_GIC_REDIST] = 0x12280000, + [ASPEED_DEV_SDMC] = 0x12C00000, + [ASPEED_DEV_SCU] = 0x12C02000, + [ASPEED_DEV_RTC] = 0x12C0F000, + [ASPEED_DEV_TIMER1] = 0x12C10000, + [ASPEED_DEV_SLI] = 0x12C17000, + [ASPEED_DEV_UART4] = 0x12C1A000, + [ASPEED_DEV_IOMEM1] = 0x14000000, + [ASPEED_DEV_FMC] = 0x14000000, + [ASPEED_DEV_SPI0] = 0x14010000, + [ASPEED_DEV_SPI1] = 0x14020000, + [ASPEED_DEV_SPI2] = 0x14030000, + [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_SDHCI] = 0x14080000, + [ASPEED_DEV_EHCI3] = 0x14121000, + [ASPEED_DEV_EHCI4] = 0x14123000, + [ASPEED_DEV_ADC] = 0x14C00000, + [ASPEED_DEV_SCUIO] = 0x14C02000, + [ASPEED_DEV_GPIO] = 0x14C0B000, + [ASPEED_DEV_I2C] = 0x14C0F000, + [ASPEED_DEV_INTCIO] = 0x14C18000, + [ASPEED_DEV_SLIIO] = 0x14C1E000, + [ASPEED_DEV_VUART] = 0x14C30000, + [ASPEED_DEV_UART0] = 0x14C33000, + [ASPEED_DEV_UART1] = 0x14C33100, + [ASPEED_DEV_UART2] = 0x14C33200, + [ASPEED_DEV_UART3] = 0x14C33300, + [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_SPI_BOOT] = 0x100000000, + [ASPEED_DEV_LTPI] = 0x300000000, + [ASPEED_DEV_SDRAM] = 0x400000000, +}; + +#define AST2700_MAX_IRQ 256 + +/* Shared Peripheral Interrupt values below are offset by -32 from datasheet */ +static const int aspeed_soc_ast2700a0_irqmap[] = { + [ASPEED_DEV_SDMC] = 0, + [ASPEED_DEV_HACE] = 4, + [ASPEED_DEV_XDMA] = 5, + [ASPEED_DEV_UART4] = 8, + [ASPEED_DEV_SCU] = 12, + [ASPEED_DEV_RTC] = 13, + [ASPEED_DEV_EMMC] = 15, + [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_DP] = 28, + [ASPEED_DEV_EHCI1] = 33, + [ASPEED_DEV_EHCI2] = 37, + [ASPEED_DEV_LPC] = 128, + [ASPEED_DEV_IBT] = 128, + [ASPEED_DEV_KCS] = 128, + [ASPEED_DEV_ADC] = 130, + [ASPEED_DEV_GPIO] = 130, + [ASPEED_DEV_I2C] = 130, + [ASPEED_DEV_FMC] = 131, + [ASPEED_DEV_WDT] = 131, + [ASPEED_DEV_PWM] = 131, + [ASPEED_DEV_I3C] = 131, + [ASPEED_DEV_UART0] = 132, + [ASPEED_DEV_UART1] = 132, + [ASPEED_DEV_UART2] = 132, + [ASPEED_DEV_UART3] = 132, + [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_ETH1] = 132, + [ASPEED_DEV_ETH2] = 132, + [ASPEED_DEV_ETH3] = 132, + [ASPEED_DEV_PECI] = 133, + [ASPEED_DEV_SDHCI] = 133, +}; + +static const int aspeed_soc_ast2700a1_irqmap[] = { + [ASPEED_DEV_SDMC] = 0, + [ASPEED_DEV_HACE] = 4, + [ASPEED_DEV_XDMA] = 5, + [ASPEED_DEV_UART4] = 8, + [ASPEED_DEV_SCU] = 12, + [ASPEED_DEV_RTC] = 13, + [ASPEED_DEV_EMMC] = 15, + [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_DP] = 28, + [ASPEED_DEV_EHCI1] = 33, + [ASPEED_DEV_EHCI2] = 37, + [ASPEED_DEV_LPC] = 192, + [ASPEED_DEV_IBT] = 192, + [ASPEED_DEV_KCS] = 192, + [ASPEED_DEV_I2C] = 194, + [ASPEED_DEV_ADC] = 194, + [ASPEED_DEV_GPIO] = 194, + [ASPEED_DEV_FMC] = 195, + [ASPEED_DEV_WDT] = 195, + [ASPEED_DEV_PWM] = 195, + [ASPEED_DEV_I3C] = 195, + [ASPEED_DEV_UART0] = 196, + [ASPEED_DEV_UART1] = 196, + [ASPEED_DEV_UART2] = 196, + [ASPEED_DEV_UART3] = 196, + [ASPEED_DEV_UART5] = 196, + [ASPEED_DEV_UART6] = 196, + [ASPEED_DEV_UART7] = 196, + [ASPEED_DEV_UART8] = 196, + [ASPEED_DEV_UART9] = 196, + [ASPEED_DEV_UART10] = 196, + [ASPEED_DEV_UART11] = 196, + [ASPEED_DEV_UART12] = 196, + [ASPEED_DEV_ETH1] = 196, + [ASPEED_DEV_ETH2] = 196, + [ASPEED_DEV_ETH3] = 196, + [ASPEED_DEV_PECI] = 197, + [ASPEED_DEV_SDHCI] = 197, +}; + +/* GICINT 128 */ +/* GICINT 192 */ +static const int ast2700_gic128_gic192_intcmap[] = { + [ASPEED_DEV_LPC] = 0, + [ASPEED_DEV_IBT] = 2, + [ASPEED_DEV_KCS] = 4, +}; + +/* GICINT 129 */ +/* GICINT 193 */ + +/* GICINT 130 */ +/* GICINT 194 */ +static const int ast2700_gic130_gic194_intcmap[] = { + [ASPEED_DEV_I2C] = 0, + [ASPEED_DEV_ADC] = 16, + [ASPEED_DEV_GPIO] = 18, +}; + +/* GICINT 131 */ +/* GICINT 195 */ +static const int ast2700_gic131_gic195_intcmap[] = { + [ASPEED_DEV_I3C] = 0, + [ASPEED_DEV_WDT] = 16, + [ASPEED_DEV_FMC] = 25, + [ASPEED_DEV_PWM] = 29, +}; + +/* GICINT 132 */ +/* GICINT 196 */ +static const int ast2700_gic132_gic196_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, + [ASPEED_DEV_EHCI3] = 28, + [ASPEED_DEV_EHCI4] = 29, +}; + +/* GICINT 133 */ +/* GICINT 197 */ +static const int ast2700_gic133_gic197_intcmap[] = { + [ASPEED_DEV_SDHCI] = 1, + [ASPEED_DEV_PECI] = 4, +}; + +/* GICINT 128 ~ 136 */ +/* GICINT 192 ~ 201 */ +struct gic_intc_irq_info { + int irq; + int intc_idx; + int orgate_idx; + const int *ptr; +}; + +static const struct gic_intc_irq_info ast2700_gic_intcmap[] = { + {192, 1, 0, ast2700_gic128_gic192_intcmap}, + {193, 1, 1, NULL}, + {194, 1, 2, ast2700_gic130_gic194_intcmap}, + {195, 1, 3, ast2700_gic131_gic195_intcmap}, + {196, 1, 4, ast2700_gic132_gic196_intcmap}, + {197, 1, 5, ast2700_gic133_gic197_intcmap}, + {198, 1, 6, NULL}, + {199, 1, 7, NULL}, + {200, 1, 8, NULL}, + {201, 1, 9, NULL}, + {128, 0, 1, ast2700_gic128_gic192_intcmap}, + {129, 0, 2, NULL}, + {130, 0, 3, ast2700_gic130_gic194_intcmap}, + {131, 0, 4, ast2700_gic131_gic195_intcmap}, + {132, 0, 5, ast2700_gic132_gic196_intcmap}, + {133, 0, 6, ast2700_gic133_gic197_intcmap}, + {134, 0, 7, NULL}, + {135, 0, 8, NULL}, + {136, 0, 9, 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 or_idx; + int idx; + int i; + + for (i = 0; i < ARRAY_SIZE(ast2700_gic_intcmap); i++) { + if (sc->irqmap[dev] == ast2700_gic_intcmap[i].irq) { + assert(ast2700_gic_intcmap[i].ptr); + or_idx = ast2700_gic_intcmap[i].orgate_idx; + idx = ast2700_gic_intcmap[i].intc_idx; + return qdev_get_gpio_in(DEVICE(&a->intc[idx].orgates[or_idx]), + 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 or_idx; + int idx; + int i; + + for (i = 0; i < ARRAY_SIZE(ast2700_gic_intcmap); i++) { + if (sc->irqmap[dev] == ast2700_gic_intcmap[i].irq) { + assert(ast2700_gic_intcmap[i].ptr); + or_idx = ast2700_gic_intcmap[i].orgate_idx; + idx = ast2700_gic_intcmap[i].intc_idx; + return qdev_get_gpio_in(DEVICE(&a->intc[idx].orgates[or_idx]), + ast2700_gic_intcmap[i].ptr[dev] + index); + } + } + + /* + * Invalid OR gate index, device IRQ should be between 128 to 136 + * and 192 to 201. + */ + 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". + */ + address_space_stl_le(&s->dram_as, addr % ram_size, data, + MEMTXATTRS_UNSPECIFIED, &result); + + 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, + .impl.min_access_size = 4, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +/* + * 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(object_get_typename(obj), "%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-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); + /* + * There is one hw-strap1 register in the SCU (CPU DIE) and another + * hw-strap1 register in the SCUIO (IO DIE). To reuse the current design + * of hw-strap, hw-strap1 is assigned to the SCU and sets the value in the + * SCU hw-strap1 register, while hw-strap2 is assigned to the SCUIO and + * sets the value in the SCUIO hw-strap1 register. + */ + object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scuio), + "hw-strap1"); + + 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); + } + + 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); + + 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[0], TYPE_ASPEED_2700_INTC); + object_initialize_child(obj, "intcio", &a->intc[1], + TYPE_ASPEED_2700_INTCIO); + + 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); + + snprintf(typename, sizeof(typename), "aspeed.sdhci-%s", socname); + object_initialize_child(obj, "sd-controller", &s->sdhci, typename); + object_property_set_int(OBJECT(&s->sdhci), "num-slots", 1, &error_abort); + + /* Init sd card slot class here so that they're under the correct parent */ + object_initialize_child(obj, "sd-controller.sdhci", + &s->sdhci.slots[0], TYPE_SYSBUS_SDHCI); + + object_initialize_child(obj, "emmc-controller", &s->emmc, typename); + object_property_set_int(OBJECT(&s->emmc), "num-slots", 1, &error_abort); + + object_initialize_child(obj, "emmc-controller.sdhci", &s->emmc.slots[0], + TYPE_SYSBUS_SDHCI); + + snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname); + object_initialize_child(obj, "timerctrl", &s->timerctrl, typename); + + snprintf(typename, sizeof(typename), "aspeed.hace-%s", socname); + object_initialize_child(obj, "hace", &s->hace, typename); + object_initialize_child(obj, "dpmcu", &s->dpmcu, + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "ltpi", &s->ltpi, + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "iomem", &s->iomem, + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "iomem0", &s->iomem0, + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "iomem1", &s->iomem1, + TYPE_UNIMPLEMENTED_DEVICE); +} + +/* + * 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; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->gic), 0, + sc->memmap[ASPEED_GIC_DIST]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->gic), 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)); + sysbus_connect_irq(gicbusdev, i + 4 * sc->num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_NMI)); + sysbus_connect_irq(gicbusdev, i + 5 * sc->num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VINMI)); + } + + 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[0]); + AspeedINTCClass *icio = ASPEED_INTC_GET_CLASS(&a->intc[1]); + g_autofree char *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[0]), errp)) { + return; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[0]), 0, + sc->memmap[ASPEED_DEV_INTC]); + + /* INTCIO */ + if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[1]), errp)) { + return; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[1]), 0, + sc->memmap[ASPEED_DEV_INTCIO]); + + /* irq sources -> orgates -> INTC */ + for (i = 0; i < ic->num_inpins; i++) { + qdev_connect_gpio_out(DEVICE(&a->intc[0].orgates[i]), 0, + qdev_get_gpio_in(DEVICE(&a->intc[0]), i)); + } + + /* INTC -> GIC192 - GIC201 */ + /* INTC -> GIC128 - GIC136 */ + for (i = 0; i < ic->num_outpins; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc[0]), i, + qdev_get_gpio_in(DEVICE(&a->gic), + ast2700_gic_intcmap[i].irq)); + } + + /* irq source -> orgates -> INTCIO */ + for (i = 0; i < icio->num_inpins; i++) { + qdev_connect_gpio_out(DEVICE(&a->intc[1].orgates[i]), 0, + qdev_get_gpio_in(DEVICE(&a->intc[1]), i)); + } + + /* INTCIO -> INTC */ + for (i = 0; i < icio->num_outpins; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc[1]), i, + qdev_get_gpio_in(DEVICE(&a->intc[0].orgates[0]), i)); + } + + /* SRAM */ + name = g_strdup_printf("aspeed.sram.%d", CPU(&a->cpu[0])->cpu_index); + if (!memory_region_init_ram(&s->sram, OBJECT(s), name, sc->sram_size, + errp)) { + return; + } + memory_region_add_subregion(s->memory, + sc->memmap[ASPEED_DEV_SRAM], &s->sram); + + /* VBOOTROM */ + if (!memory_region_init_ram(&s->vbootrom, OBJECT(s), "aspeed.vbootrom", + 0x20000, errp)) { + return; + } + memory_region_add_subregion(s->memory, + sc->memmap[ASPEED_DEV_VBOOTROM], &s->vbootrom); + + /* 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); + } + + /* 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 + * 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. + * + * For AST2700 A0: + * I2C bus interrupts are connected to the OR gate from bit 0 to bit + * 15, and the OR gate output pin is connected to the input pin of + * GICINT130 of INTC (CPU Die). Then, the output pin is connected to + * the GIC. + * + * For AST2700 A1: + * I2C bus interrupts are connected to the OR gate from bit 0 to bit + * 15, and the OR gate output pin is connected to the input pin of + * GICINT194 of INTCIO (IO Die). Then, the output pin is connected + * to the INTC (CPU Die) input pin, and its output pin is connected + * to the GIC. + * + * I2C bus 0 is connected to the OR gate at bit 0. + * I2C bus 15 is connected to the OR gate 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)); + + /* 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)); + + /* eMMC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->emmc), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->emmc), 0, + sc->memmap[ASPEED_DEV_EMMC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_EMMC)); + + /* 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++) { + irq = aspeed_soc_get_irq(s, ASPEED_DEV_TIMER1 + i); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); + } + + /* 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)); + + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->dpmcu), + "aspeed.dpmcu", + sc->memmap[ASPEED_DEV_DPMCU], + AST2700_SOC_DPMCU_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->ltpi), + "aspeed.ltpi", + sc->memmap[ASPEED_DEV_LTPI], + AST2700_SOC_LTPI_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem), + "aspeed.io", + sc->memmap[ASPEED_DEV_IOMEM], + AST2700_SOC_IO_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem0), + "aspeed.iomem0", + sc->memmap[ASPEED_DEV_IOMEM0], + AST2700_SOC_IOMEM_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem1), + "aspeed.iomem1", + sc->memmap[ASPEED_DEV_IOMEM1], + AST2700_SOC_IOMEM_SIZE); +} + +static void aspeed_soc_ast2700a0_class_init(ObjectClass *oc, const 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->valid_cpu_types = valid_cpu_types; + sc->silicon_rev = AST2700_A0_SILICON_REV; + sc->sram_size = 0x20000; + sc->spis_num = 3; + sc->ehcis_num = 2; + 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_ast2700a0_irqmap; + sc->memmap = aspeed_soc_ast2700_memmap; + sc->get_irq = aspeed_soc_ast2700_get_irq; +} + +static void aspeed_soc_ast2700a1_class_init(ObjectClass *oc, const 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->valid_cpu_types = valid_cpu_types; + sc->silicon_rev = AST2700_A1_SILICON_REV; + sc->sram_size = 0x20000; + sc->spis_num = 3; + sc->ehcis_num = 4; + sc->wdts_num = 8; + sc->macs_num = 3; + sc->uarts_num = 13; + sc->num_cpus = 4; + sc->uarts_base = ASPEED_DEV_UART0; + sc->irqmap = aspeed_soc_ast2700a1_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_ast2700a0_class_init, + }, + { + .name = "ast2700-a1", + .parent = TYPE_ASPEED27X0_SOC, + .instance_init = aspeed_soc_ast2700_init, + .class_init = aspeed_soc_ast2700a1_class_init, + }, +}; + +DEFINE_TYPES(aspeed_soc_ast27x0_types) diff --git a/hw/arm/aspeed_eeprom.c b/hw/arm/aspeed_eeprom.c index ace5266cec9..daa3d329d10 100644 --- a/hw/arm/aspeed_eeprom.c +++ b/hw/arm/aspeed_eeprom.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: GPL-2.0-only */ +#include "qemu/osdep.h" #include "aspeed_eeprom.h" /* Tiogapass BMC FRU */ diff --git a/hw/arm/aspeed_eeprom.h b/hw/arm/aspeed_eeprom.h index bbf9e54365b..f08c16ef506 100644 --- a/hw/arm/aspeed_eeprom.h +++ b/hw/arm/aspeed_eeprom.h @@ -7,7 +7,6 @@ #ifndef ASPEED_EEPROM_H #define ASPEED_EEPROM_H -#include "qemu/osdep.h" extern const uint8_t tiogapass_bmc_fruid[]; extern const size_t tiogapass_bmc_fruid_len; diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c deleted file mode 100644 index bf22258de95..00000000000 --- a/hw/arm/aspeed_soc.c +++ /dev/null @@ -1,683 +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_SPI_BOOT] = ASPEED_SOC_SPI_BOOT_ADDR, - [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] = ASPEED_SOC_SPI_BOOT_ADDR, - [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; - - /* 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(&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)); - - /* 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 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 00000000000..1c4ac93a0ff --- /dev/null +++ b/hw/arm/aspeed_soc_common.c @@ -0,0 +1,170 @@ +/* + * 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 const 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 *), +}; + +static void aspeed_soc_class_init(ObjectClass *oc, const 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 00000000000..34ed2e0851b --- /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(s->soc.armv7m.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, const 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 index 74121d89660..b750a575f72 100644 --- a/hw/arm/bananapi_m2u.c +++ b/hw/arm/bananapi_m2u.c @@ -19,13 +19,14 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "exec/address-spaces.h" +#include "system/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; @@ -70,12 +71,6 @@ static void bpim2u_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); - } - r40 = AW_R40(object_new(TYPE_AW_R40)); object_property_add_child(OBJECT(machine), "soc", OBJECT(r40)); object_unref(OBJECT(r40)); @@ -127,19 +122,26 @@ static void bpim2u_init(MachineState *machine) 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(ARM_CPU(first_cpu), machine, &bpim2u_binfo); + 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"; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("bpim2u", bpim2u_machine_init) diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 0233038b957..8a1e72dfab2 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -15,7 +15,7 @@ #include "hw/arm/bcm2835_peripherals.h" #include "hw/misc/bcm2835_mbox_defs.h" #include "hw/arm/raspi_platform.h" -#include "sysemu/sysemu.h" +#include "system/system.h" /* Peripheral base address on the VC (GPU) system bus */ #define BCM2835_VC_PERI_BASE 0x7e000000 @@ -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,10 +111,15 @@ 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); @@ -97,9 +132,8 @@ static void bcm2835_peripherals_init(Object *obj) 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); @@ -110,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); @@ -144,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); @@ -256,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; } @@ -270,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; @@ -281,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: @@ -311,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)) { @@ -336,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)); @@ -402,40 +471,79 @@ 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); create_unimp(s, &s->sdramc, "bcm2835-sdramc", SDRAMC_OFFSET, 0x100); } -static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data) +static void bcm2835_peripherals_class_init(ObjectClass *oc, const 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 166dc896c09..cd61ba15054 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -15,26 +15,16 @@ #include "hw/arm/bcm2836.h" #include "hw/arm/raspi_platform.h" #include "hw/sysbus.h" +#include "target/arm/cpu-qom.h" +#include "target/arm/gtimer.h" -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; -}; - -static Property bcm2836_enabled_cores_property = - DEFINE_PROP_UINT32("enabled-cpus", BCM283XState, enabled_cpus, 0); +static const Property bcm2836_enabled_cores_property = + 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++) { @@ -50,6 +40,11 @@ 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); @@ -59,114 +54,116 @@ static void bcm2836_init(Object *obj) "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, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -174,10 +171,10 @@ static void bcm283x_class_init(ObjectClass *oc, void *data) dc->user_creatable = false; } -static void bcm2835_class_init(ObjectClass *oc, void *data) +static void bcm2835_class_init(ObjectClass *oc, const 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; @@ -185,10 +182,10 @@ static void bcm2835_class_init(ObjectClass *oc, void *data) dc->realize = bcm2835_realize; }; -static void bcm2836_class_init(ObjectClass *oc, void *data) +static void bcm2836_class_init(ObjectClass *oc, const 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; @@ -199,10 +196,10 @@ static void bcm2836_class_init(ObjectClass *oc, void *data) }; #ifdef TARGET_AARCH64 -static void bcm2837_class_init(ObjectClass *oc, void *data) +static void bcm2837_class_init(ObjectClass *oc, const 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; @@ -230,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 00000000000..22aa754613c --- /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, const 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 00000000000..812b5b8480c --- /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, const 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 720f22531a6..becd827af13 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -14,15 +14,18 @@ #include #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 "cpu.h" +#include "exec/target_page.h" +#include "system/kvm.h" +#include "system/tcg.h" +#include "system/system.h" +#include "system/memory.h" +#include "system/numa.h" #include "hw/boards.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/loader.h" #include "elf.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "qemu/config-file.h" #include "qemu/option.h" #include "qemu/units.h" @@ -347,13 +350,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 */ @@ -432,13 +435,12 @@ static int fdt_add_memory_node(void *fdt, uint32_t acells, hwaddr mem_base, return ret; } -static void fdt_add_psci_node(void *fdt) +static void fdt_add_psci_node(void *fdt, ARMCPU *armcpu) { uint32_t cpu_suspend_fn; uint32_t cpu_off_fn; uint32_t cpu_on_fn; uint32_t migrate_fn; - ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0)); const char *psci_method; int64_t psci_conduit; int rc; @@ -512,7 +514,8 @@ static void fdt_add_psci_node(void *fdt) } int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, - hwaddr addr_limit, AddressSpace *as, MachineState *ms) + hwaddr addr_limit, AddressSpace *as, MachineState *ms, + ARMCPU *cpu) { void *fdt = NULL; int size, rc, n = 0; @@ -524,7 +527,7 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, if (binfo->dtb_filename) { char *filename; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename); + filename = qemu_find_file(QEMU_FILE_TYPE_DTB, binfo->dtb_filename); if (!filename) { fprintf(stderr, "Couldn't open dtb file %s\n", binfo->dtb_filename); goto fail; @@ -655,14 +658,12 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, } } - fdt_add_psci_node(fdt); + fdt_add_psci_node(fdt, cpu); if (binfo->modify_dtb) { binfo->modify_dtb(binfo, fdt); } - qemu_fdt_dumpdtb(fdt, size); - /* Put the DTB into the memory map as a ROM image: this will ensure * the DTB is copied again upon reset, even if addr points into RAM. */ @@ -722,81 +723,36 @@ 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); - } - - 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; - } - } + int target_el = arm_feature(env, ARM_FEATURE_EL2) ? 2 : 1; - 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 && cpu == info->primary_cpu))) { + /* Start this CPU in Secure SVC */ + target_el = 3; + } } - if (cs == first_cpu) { + arm_emulate_firmware_reset(cs, target_el); + + if (cpu == info->primary_cpu) { AddressSpace *as = arm_boot_address_space(cpu, info); cpu_set_pc(cs, info->loader_start); @@ -843,24 +799,28 @@ static ssize_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, Elf64_Ehdr h64; } elf_header; int data_swab = 0; - bool big_endian; - ssize_t ret = -1; + int elf_data_order; + 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) { - big_endian = elf_header.h64.e_ident[EI_DATA] == ELFDATA2MSB; - info->endianness = big_endian ? ARM_ENDIANNESS_BE8 - : ARM_ENDIANNESS_LE; + elf_data_order = elf_header.h64.e_ident[EI_DATA]; + info->endianness = elf_data_order == ELFDATA2MSB ? ARM_ENDIANNESS_BE8 + : ARM_ENDIANNESS_LE; } else { - big_endian = elf_header.h32.e_ident[EI_DATA] == ELFDATA2MSB; - if (big_endian) { + elf_data_order = elf_header.h32.e_ident[EI_DATA]; + if (elf_data_order == ELFDATA2MSB) { if (bswap32(elf_header.h32.e_flags) & EF_ARM_BE8) { info->endianness = ARM_ENDIANNESS_BE8; } else { @@ -880,10 +840,12 @@ static ssize_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, } ret = load_elf_as(info->kernel_filename, NULL, NULL, NULL, - pentry, lowaddr, highaddr, NULL, big_endian, elf_machine, - 1, data_swab, as); + pentry, lowaddr, highaddr, NULL, elf_data_order, + elf_machine, 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); } @@ -896,7 +858,7 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base, hwaddr kernel_load_offset = KERNEL64_LOAD_ADDR; uint64_t kernel_size = 0; uint8_t *buffer; - int size; + ssize_t size; /* On aarch64, it's the bootloader's job to uncompress the kernel. */ size = load_image_gzipped_buffer(filename, LOAD_IMAGE_MAX_GUNZIP_BYTES, @@ -1277,6 +1239,9 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) info->dtb_filename = ms->dtb; info->dtb_limit = 0; + /* We assume the CPU passed as argument is the primary CPU. */ + info->primary_cpu = cpu; + /* Load the kernel. */ if (!info->kernel_filename || info->firmware_loaded) { arm_setup_firmware_boot(cpu, info); @@ -1326,12 +1291,8 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) object_property_set_int(cpuobj, "psci-conduit", info->psci_conduit, &error_abort); - /* - * Secondary CPUs start in PSCI powered-down state. Like the - * code in do_cpu_reset(), we assume first_cpu is the primary - * CPU. - */ - if (cs != first_cpu) { + /* Secondary CPUs start in PSCI powered-down state. */ + if (ARM_CPU(cs) != info->primary_cpu) { object_property_set_bool(cpuobj, "start-powered-off", true, &error_abort); } @@ -1366,7 +1327,8 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) * decided whether to enable PSCI and set the psci-conduit CPU properties. */ if (!info->skip_dtb_autoload && have_dtb(info)) { - if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as, ms) < 0) { + if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, + as, ms, cpu) < 0) { exit(1); } } diff --git a/hw/arm/collie.c b/hw/arm/collie.c index a0ad1b8dc7e..93bb190f1f9 100644 --- a/hw/arm/collie.c +++ b/hw/arm/collie.c @@ -16,8 +16,7 @@ #include "strongarm.h" #include "hw/arm/boot.h" #include "hw/block/flash.h" -#include "exec/address-spaces.h" -#include "cpu.h" +#include "system/address-spaces.h" #include "qom/object.h" #include "qemu/error-report.h" @@ -70,7 +69,7 @@ static void collie_init(MachineState *machine) arm_load_kernel(cms->sa1110->cpu, machine, &collie_binfo); } -static void collie_machine_class_init(ObjectClass *oc, void *data) +static void collie_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c index 8c7fa91529e..d665d4edd97 100644 --- a/hw/arm/cubieboard.c +++ b/hw/arm/cubieboard.c @@ -21,6 +21,7 @@ #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 = { @@ -51,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)); @@ -113,14 +108,21 @@ 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; mc->units_per_default_bus = 1; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "cubieboard.ram"; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("cubieboard", cubieboard_machine_init) diff --git a/hw/arm/digic.c b/hw/arm/digic.c index 6df55479773..d831bc974d2 100644 --- a/hw/arm/digic.c +++ b/hw/arm/digic.c @@ -25,7 +25,7 @@ #include "qemu/module.h" #include "hw/arm/digic.h" #include "hw/qdev-properties.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #define DIGIC4_TIMER_BASE(n) (0xc0210000 + (n) * 0x100) @@ -79,7 +79,7 @@ static void digic_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(sbd, 0, DIGIC_UART_BASE); } -static void digic_class_init(ObjectClass *oc, void *data) +static void digic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c index 4093af09cb2..466b8b84c0e 100644 --- a/hw/arm/digic_boards.c +++ b/hw/arm/digic_boards.c @@ -31,7 +31,7 @@ #include "hw/arm/digic.h" #include "hw/block/flash.h" #include "hw/loader.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "qemu/units.h" #include "qemu/cutils.h" @@ -80,7 +80,7 @@ static void digic4_board_init(MachineState *machine, DigicBoard *board) static void digic_load_rom(DigicState *s, hwaddr addr, hwaddr max_size, const char *filename) { - target_long rom_size; + ssize_t rom_size; if (qtest_enabled()) { /* qtest runs no code so don't attempt a ROM load which diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index de39fb0ece8..76001ff0dfc 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -23,11 +23,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "exec/tswap.h" #include "cpu.h" #include "hw/cpu/a9mpcore.h" #include "hw/irq.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" +#include "system/blockdev.h" +#include "system/system.h" #include "hw/sysbus.h" #include "hw/arm/boot.h" #include "hw/loader.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 @@ -101,6 +103,8 @@ #define EXYNOS4210_PL330_BASE1_ADDR 0x12690000 #define EXYNOS4210_PL330_BASE2_ADDR 0x12850000 +#define GIC_EXT_IRQS 64 /* FIXME: verify for this SoC */ + enum ExtGicId { EXT_GIC_ID_MDMA_LCD0 = 66, EXT_GIC_ID_PDMA0, @@ -392,7 +396,8 @@ static void exynos4210_init_board_irqs(Exynos4210State *s) } if (irq_id) { qdev_connect_gpio_out(splitter, splitin, - qdev_get_gpio_in(extgicdev, irq_id - 32)); + qdev_get_gpio_in(extgicdev, + irq_id - GIC_INTERNAL)); } } for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) { @@ -419,7 +424,8 @@ static void exynos4210_init_board_irqs(Exynos4210State *s) s->irq_table[n] = qdev_get_gpio_in(splitter, 0); qdev_connect_gpio_out(splitter, 0, qdev_get_gpio_in(intcdev, n)); qdev_connect_gpio_out(splitter, 1, - qdev_get_gpio_in(extgicdev, irq_id - 32)); + qdev_get_gpio_in(extgicdev, + irq_id - GIC_INTERNAL)); } else { s->irq_table[n] = qdev_get_gpio_in(intcdev, n); } @@ -456,7 +462,6 @@ static uint64_t exynos4210_chipid_and_omr_read(void *opaque, hwaddr offset, static void exynos4210_chipid_and_omr_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { - return; } static const MemoryRegionOps exynos4210_chipid_and_omr_ops = { @@ -554,6 +559,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. */ @@ -583,6 +589,8 @@ static void exynos4210_realize(DeviceState *socdev, Error **errp) /* Private memory region and Internal GIC */ qdev_prop_set_uint32(DEVICE(&s->a9mpcore), "num-cpu", EXYNOS4210_NCPUS); + qdev_prop_set_uint32(DEVICE(&s->a9mpcore), "num-irq", + GIC_EXT_IRQS + GIC_INTERNAL); busdev = SYS_BUS_DEVICE(&s->a9mpcore); sysbus_realize(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR); @@ -766,11 +774,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)]); @@ -830,7 +842,7 @@ static void exynos4210_init(Object *obj) TYPE_EXYNOS4210_COMBINER); } -static void exynos4210_class_init(ObjectClass *klass, void *data) +static void exynos4210_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c index ef5bcbc212c..73049741312 100644 --- a/hw/arm/exynos4_boards.c +++ b/hw/arm/exynos4_boards.c @@ -28,12 +28,13 @@ #include "hw/sysbus.h" #include "net/net.h" #include "hw/arm/boot.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/arm/exynos4210.h" #include "hw/net/lan9118.h" #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,19 +146,26 @@ 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 void nuri_class_init(ObjectClass *oc, void *data) +static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a9"), + NULL +}; + +static void nuri_class_init(ObjectClass *oc, const 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; mc->ignore_memory_transaction_failures = true; + mc->auto_create_sdcard = true; } static const TypeInfo nuri_type = { @@ -167,16 +174,18 @@ static const TypeInfo nuri_type = { .class_init = nuri_class_init, }; -static void smdkc210_class_init(ObjectClass *oc, void *data) +static void smdkc210_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); 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; mc->ignore_memory_transaction_failures = true; + mc->auto_create_sdcard = true; } static const TypeInfo smdkc210_type = { diff --git a/hw/arm/fby35.c b/hw/arm/fby35.c index f2ff6c1abfd..c14fc2efe9b 100644 --- a/hw/arm/fby35.c +++ b/hw/arm/fby35.c @@ -8,8 +8,8 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" +#include "system/system.h" +#include "system/block-backend.h" #include "hw/boards.h" #include "hw/qdev-clock.h" #include "hw/arm/aspeed_soc.h" @@ -27,8 +27,8 @@ struct Fby35State { MemoryRegion bic_memory; Clock *bic_sysclk; - AspeedSoCState bmc; - AspeedSoCState bic; + Aspeed2600SoCState bmc; + Aspeed10x0SoCState bic; bool mmio_exec; }; @@ -70,10 +70,14 @@ static void fby35_bmc_write_boot_rom(DriveInfo *dinfo, MemoryRegion *mr, static void fby35_bmc_init(Fby35State *s) { + AspeedSoCState *soc; + object_initialize_child(OBJECT(s), "bmc", &s->bmc, "ast2600-a3"); + soc = ASPEED_SOC(&s->bmc); memory_region_init(&s->bmc_memory, OBJECT(&s->bmc), "bmc-memory", UINT64_MAX); + memory_region_add_subregion(get_system_memory(), 0, &s->bmc_memory); memory_region_init_ram(&s->bmc_dram, OBJECT(&s->bmc), "bmc-dram", FBY35_BMC_RAM_SIZE, &error_abort); @@ -87,22 +91,21 @@ 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 (!s->mmio_exec) { DriveInfo *mtd0 = drive_get(IF_MTD, 0, 0); if (mtd0) { - AspeedSoCState *bmc = &s->bmc; - uint64_t rom_size = memory_region_size(&bmc->spi_boot); + uint64_t rom_size = memory_region_size(&soc->spi_boot); memory_region_init_rom(&s->bmc_boot_rom, NULL, "aspeed.boot_rom", rom_size, &error_abort); - memory_region_add_subregion_overlap(&bmc->spi_boot_container, 0, + 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, @@ -114,10 +117,13 @@ static void fby35_bmc_init(Fby35State *s) 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); @@ -125,12 +131,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) @@ -157,7 +163,7 @@ static void fby35_instance_init(Object *obj) FBY35(obj)->mmio_exec = false; } -static void fby35_class_init(ObjectClass *oc, void *data) +static void fby35_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -165,6 +171,7 @@ static void fby35_class_init(ObjectClass *oc, void *data) mc->init = fby35_init; mc->no_floppy = 1; mc->no_cdrom = 1; + mc->auto_create_sdcard = true; mc->min_cpus = mc->max_cpus = mc->default_cpus = 3; object_class_property_add_bool(oc, "execute-in-place", diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 24c43745903..7aad6359ea8 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -25,9 +25,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx25.h" -#include "sysemu/sysemu.h" +#include "system/system.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; @@ -242,8 +243,6 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) &error_abort); object_property_set_uint(OBJECT(&s->esdhc[i]), "capareg", IMX25_ESDHC_CAPABILITIES, &error_abort); - object_property_set_uint(OBJECT(&s->esdhc[i]), "vendor", - SDHCI_VENDOR_IMX, &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->esdhc[i]), errp)) { return; } @@ -280,28 +279,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, @@ -314,12 +307,11 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) &s->iram_alias); } -static Property fsl_imx25_properties[] = { +static const Property fsl_imx25_properties[] = { DEFINE_PROP_UINT32("fec-phy-num", FslIMX25State, phy_num, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void fsl_imx25_class_init(ObjectClass *oc, void *data) +static void fsl_imx25_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index def27bb9136..e9f70ad94b8 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -22,10 +22,11 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx31.h" -#include "sysemu/sysemu.h" -#include "exec/address-spaces.h" +#include "system/system.h" +#include "system/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, @@ -224,7 +218,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) &s->iram_alias); } -static void fsl_imx31_class_init(ObjectClass *oc, void *data) +static void fsl_imx31_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 4fa7f0b95ed..f3a60022d84 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -22,13 +22,15 @@ #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" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "chardev/char.h" #include "qemu/error-report.h" #include "qemu/module.h" +#include "target/arm/cpu-qom.h" #define IMX6_ESDHC_CAPABILITIES 0x057834b4 @@ -102,6 +104,10 @@ 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); + object_initialize_child(obj, "pcie4-msi-irq", &s->pcie4_msi_irq, + TYPE_OR_IRQ); } static void fsl_imx6_realize(DeviceState *dev, Error **errp) @@ -109,8 +115,10 @@ 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; + DeviceState *mpcore = DEVICE(&s->a9mpcore); + DeviceState *gic; if (smp_cpus > FSL_IMX6_NUM_CPUS) { error_setg(errp, "%s: Only %d CPUs are supported (%d requested)", @@ -137,24 +145,27 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) } } - object_property_set_int(OBJECT(&s->a9mpcore), "num-cpu", smp_cpus, - &error_abort); + object_property_set_int(OBJECT(mpcore), "num-cpu", smp_cpus, &error_abort); - object_property_set_int(OBJECT(&s->a9mpcore), "num-irq", + object_property_set_int(OBJECT(mpcore), "num-irq", FSL_IMX6_MAX_IRQ + GIC_INTERNAL, &error_abort); - if (!sysbus_realize(SYS_BUS_DEVICE(&s->a9mpcore), errp)) { + if (!sysbus_realize(SYS_BUS_DEVICE(mpcore), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->a9mpcore), 0, FSL_IMX6_A9MPCORE_ADDR); + sysbus_mmio_map(SYS_BUS_DEVICE(mpcore), 0, FSL_IMX6_A9MPCORE_ADDR); + gic = mpcore; for (i = 0; i < smp_cpus; i++) { - sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i, + sysbus_connect_irq(SYS_BUS_DEVICE(gic), i, qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ)); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i + smp_cpus, + sysbus_connect_irq(SYS_BUS_DEVICE(gic), i + smp_cpus, 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; } @@ -186,8 +197,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr); sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - serial_table[i].irq)); + qdev_get_gpio_in(gic, serial_table[i].irq)); } s->gpt.ccm = IMX_CCM(&s->ccm); @@ -198,8 +208,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt), 0, FSL_IMX6_GPT_ADDR); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - FSL_IMX6_GPT_IRQ)); + qdev_get_gpio_in(gic, FSL_IMX6_GPT_IRQ)); /* Initialize all EPIT timers */ for (i = 0; i < FSL_IMX6_NUM_EPITS; i++) { @@ -219,8 +228,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->epit[i]), 0, epit_table[i].addr); sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - epit_table[i].irq)); + qdev_get_gpio_in(gic, epit_table[i].irq)); } /* Initialize all I2C */ @@ -240,8 +248,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr); sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - i2c_table[i].irq)); + qdev_get_gpio_in(gic, i2c_table[i].irq)); } /* Initialize all GPIOs */ @@ -298,11 +305,9 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - gpio_table[i].irq_low)); + qdev_get_gpio_in(gic, gpio_table[i].irq_low)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - gpio_table[i].irq_high)); + qdev_get_gpio_in(gic, gpio_table[i].irq_high)); } /* Initialize all SDHC */ @@ -322,15 +327,12 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) &error_abort); object_property_set_uint(OBJECT(&s->esdhc[i]), "capareg", IMX6_ESDHC_CAPABILITIES, &error_abort); - object_property_set_uint(OBJECT(&s->esdhc[i]), "vendor", - SDHCI_VENDOR_IMX, &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->esdhc[i]), errp)) { return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->esdhc[i]), 0, esdhc_table[i].addr); sysbus_connect_irq(SYS_BUS_DEVICE(&s->esdhc[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - esdhc_table[i].irq)); + qdev_get_gpio_in(gic, esdhc_table[i].irq)); } /* USB */ @@ -351,8 +353,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, FSL_IMX6_USBOH3_USB_ADDR + i * 0x200); sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - FSL_IMX6_USBn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6_USBn_IRQ[i])); } /* Initialize all ECSPI */ @@ -375,22 +376,20 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_table[i].addr); sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - spi_table[i].irq)); + qdev_get_gpio_in(gic, 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; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->eth), 0, FSL_IMX6_ENET_ADDR); sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - FSL_IMX6_ENET_MAC_IRQ)); + qdev_get_gpio_in(gic, FSL_IMX6_ENET_MAC_IRQ)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth), 1, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - FSL_IMX6_ENET_MAC_1588_IRQ)); + qdev_get_gpio_in(gic, FSL_IMX6_ENET_MAC_1588_IRQ)); /* * SNVS @@ -417,35 +416,58 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, FSL_IMX6_WDOGn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a9mpcore), - FSL_IMX6_WDOGn_IRQ[i])); + qdev_get_gpio_in(gic, 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); + + object_property_set_int(OBJECT(&s->pcie4_msi_irq), "num-lines", 2, + &error_abort); + qdev_realize(DEVICE(&s->pcie4_msi_irq), NULL, &error_abort); + + irq = qdev_get_gpio_in(DEVICE(&s->a9mpcore), FSL_IMX6_PCIE4_MSI_IRQ); + qdev_connect_gpio_out(DEVICE(&s->pcie4_msi_irq), 0, irq); + + 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->pcie4_msi_irq), 0); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, irq); + irq = qdev_get_gpio_in(DEVICE(&s->pcie4_msi_irq), 1); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 4, 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, @@ -458,12 +480,11 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) &s->ocram_alias); } -static Property fsl_imx6_properties[] = { +static const Property fsl_imx6_properties[] = { DEFINE_PROP_UINT32("fec-phy-num", FslIMX6State, phy_num, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void fsl_imx6_class_init(ObjectClass *oc, void *data) +static void fsl_imx6_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index e37b69a5e16..883c7fc534f 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -22,9 +22,10 @@ #include "hw/misc/unimp.h" #include "hw/usb/imx-usb-phy.h" #include "hw/boards.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/error-report.h" #include "qemu/module.h" +#include "target/arm/cpu-qom.h" #define NAME_SIZE 20 @@ -156,10 +157,12 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) { MachineState *ms = MACHINE(qdev_get_machine()); FslIMX6ULState *s = FSL_IMX6UL(dev); + DeviceState *mpcore = DEVICE(&s->a7mpcore); int i; char name[NAME_SIZE]; - SysBusDevice *sbd; - DeviceState *d; + DeviceState *gic; + SysBusDevice *gicsbd; + DeviceState *cpu; if (ms->smp.cpus > 1) { error_setg(errp, "%s: Only a single CPU is supported (%d requested)", @@ -172,19 +175,19 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) /* * A7MPCORE */ - object_property_set_int(OBJECT(&s->a7mpcore), "num-cpu", 1, &error_abort); - object_property_set_int(OBJECT(&s->a7mpcore), "num-irq", + object_property_set_int(OBJECT(mpcore), "num-cpu", 1, &error_abort); + object_property_set_int(OBJECT(mpcore), "num-irq", FSL_IMX6UL_MAX_IRQ + GIC_INTERNAL, &error_abort); - sysbus_realize(SYS_BUS_DEVICE(&s->a7mpcore), &error_abort); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->a7mpcore), 0, FSL_IMX6UL_A7MPCORE_ADDR); + sysbus_realize(SYS_BUS_DEVICE(mpcore), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(mpcore), 0, FSL_IMX6UL_A7MPCORE_ADDR); - sbd = SYS_BUS_DEVICE(&s->a7mpcore); - d = DEVICE(&s->cpu); - - sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(d, ARM_CPU_IRQ)); - sysbus_connect_irq(sbd, 1, qdev_get_gpio_in(d, ARM_CPU_FIQ)); - sysbus_connect_irq(sbd, 2, qdev_get_gpio_in(d, ARM_CPU_VIRQ)); - sysbus_connect_irq(sbd, 3, qdev_get_gpio_in(d, ARM_CPU_VFIQ)); + gic = mpcore; + gicsbd = SYS_BUS_DEVICE(gic); + cpu = DEVICE(&s->cpu); + sysbus_connect_irq(gicsbd, 0, qdev_get_gpio_in(cpu, ARM_CPU_IRQ)); + sysbus_connect_irq(gicsbd, 1, qdev_get_gpio_in(cpu, ARM_CPU_FIQ)); + sysbus_connect_irq(gicsbd, 2, qdev_get_gpio_in(cpu, ARM_CPU_VIRQ)); + sysbus_connect_irq(gicsbd, 3, qdev_get_gpio_in(cpu, ARM_CPU_VFIQ)); /* * A7MPCORE DAP @@ -192,6 +195,36 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) create_unimplemented_device("a7mpcore-dap", FSL_IMX6UL_A7MPCORE_DAP_ADDR, FSL_IMX6UL_A7MPCORE_DAP_SIZE); + /* + * 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 */ @@ -213,8 +246,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_GPTn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_GPTn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_GPTn_IRQ[i])); } /* @@ -238,8 +270,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_EPITn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_EPITn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_EPITn_IRQ[i])); } /* @@ -276,12 +307,10 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_GPIOn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_GPIOn_LOW_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_GPIOn_LOW_IRQ[i])); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_GPIOn_HIGH_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_GPIOn_HIGH_IRQ[i])); } /* @@ -335,8 +364,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_SPIn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_SPIn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_SPIn_IRQ[i])); } /* @@ -361,8 +389,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, FSL_IMX6UL_I2Cn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_I2Cn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_I2Cn_IRQ[i])); } /* @@ -399,8 +426,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_UARTn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_UARTn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_UARTn_IRQ[i])); } /* @@ -442,19 +468,17 @@ 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, FSL_IMX6UL_ENETn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_ENETn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_ENETn_IRQ[i])); sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 1, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_ENETn_TIMER_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_ENETn_TIMER_IRQ[i])); } /* @@ -490,8 +514,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, 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])); + qdev_get_gpio_in(gic, FSL_IMX6UL_USBn_IRQ[i])); } /* @@ -508,16 +531,13 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_USDHC2_IRQ, }; - object_property_set_uint(OBJECT(&s->usdhc[i]), "vendor", - SDHCI_VENDOR_IMX, &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->usdhc[i]), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0, FSL_IMX6UL_USDHCn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_USDHCn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_USDHCn_IRQ[i])); } /* @@ -549,8 +569,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, FSL_IMX6UL_WDOGn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX6UL_WDOGn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX6UL_WDOGn_IRQ[i])); } /* @@ -687,17 +706,16 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_OCRAM_ALIAS_ADDR, &s->ocram_alias); } -static Property fsl_imx6ul_properties[] = { +static const 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(), }; -static void fsl_imx6ul_class_init(ObjectClass *oc, void *data) +static void fsl_imx6ul_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index 474cfdc87c6..02f7602077f 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -23,9 +23,10 @@ #include "hw/arm/fsl-imx7.h" #include "hw/misc/unimp.h" #include "hw/boards.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/error-report.h" #include "qemu/module.h" +#include "target/arm/cpu-qom.h" #define NAME_SIZE 20 @@ -149,6 +150,8 @@ static void fsl_imx7_init(Object *obj) * PCIE */ object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); + object_initialize_child(obj, "pcie4-msi-irq", &s->pcie4_msi_irq, + TYPE_OR_IRQ); /* * USBs @@ -163,7 +166,8 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) { MachineState *ms = MACHINE(qdev_get_machine()); FslIMX7State *s = FSL_IMX7(dev); - Object *o; + DeviceState *mpcore = DEVICE(&s->a7mpcore); + DeviceState *gic; int i; qemu_irq irq; char name[NAME_SIZE]; @@ -179,7 +183,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) * CPUs */ for (i = 0; i < smp_cpus; i++) { - o = OBJECT(&s->cpu[i]); + Object *o = OBJECT(&s->cpu[i]); /* On uniprocessor, the CBAR is set to 0 */ if (smp_cpus > 1) { @@ -202,16 +206,15 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) /* * A7MPCORE */ - object_property_set_int(OBJECT(&s->a7mpcore), "num-cpu", smp_cpus, - &error_abort); - object_property_set_int(OBJECT(&s->a7mpcore), "num-irq", + object_property_set_int(OBJECT(mpcore), "num-cpu", smp_cpus, &error_abort); + object_property_set_int(OBJECT(mpcore), "num-irq", FSL_IMX7_MAX_IRQ + GIC_INTERNAL, &error_abort); + sysbus_realize(SYS_BUS_DEVICE(mpcore), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(mpcore), 0, FSL_IMX7_A7MPCORE_ADDR); - sysbus_realize(SYS_BUS_DEVICE(&s->a7mpcore), &error_abort); - sysbus_mmio_map(SYS_BUS_DEVICE(&s->a7mpcore), 0, FSL_IMX7_A7MPCORE_ADDR); - + gic = mpcore; for (i = 0; i < smp_cpus; i++) { - SysBusDevice *sbd = SYS_BUS_DEVICE(&s->a7mpcore); + SysBusDevice *sbd = SYS_BUS_DEVICE(gic); DeviceState *d = DEVICE(qemu_get_cpu(i)); irq = qdev_get_gpio_in(d, ARM_CPU_IRQ); @@ -252,8 +255,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) 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])); + qdev_get_gpio_in(gic, FSL_IMX7_GPTn_IRQ[i])); } /* @@ -295,12 +297,10 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) 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])); + qdev_get_gpio_in(gic, 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])); + qdev_get_gpio_in(gic, FSL_IMX7_GPIOn_HIGH_IRQ[i])); } /* @@ -352,8 +352,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, FSL_IMX7_SPIn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX7_SPIn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX7_SPIn_IRQ[i])); } /* @@ -378,8 +377,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, FSL_IMX7_I2Cn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX7_I2Cn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX7_I2Cn_IRQ[i])); } /* @@ -413,7 +411,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, FSL_IMX7_UARTn_ADDR[i]); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_UARTn_IRQ[i]); + irq = qdev_get_gpio_in(gic, FSL_IMX7_UARTn_IRQ[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, irq); } @@ -446,14 +444,14 @@ 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]); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 0)); + irq = qdev_get_gpio_in(gic, FSL_IMX7_ENET_IRQ(i, 0)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 0, irq); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 3)); + irq = qdev_get_gpio_in(gic, FSL_IMX7_ENET_IRQ(i, 3)); sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 1, irq); } @@ -473,14 +471,12 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) FSL_IMX7_USDHC3_IRQ, }; - object_property_set_uint(OBJECT(&s->usdhc[i]), "vendor", - SDHCI_VENDOR_IMX, &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->usdhc[i]), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0, FSL_IMX7_USDHCn_ADDR[i]); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USDHCn_IRQ[i]); + irq = qdev_get_gpio_in(gic, FSL_IMX7_USDHCn_IRQ[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, irq); } @@ -519,8 +515,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, FSL_IMX7_WDOGn_ADDR[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[i]), 0, - qdev_get_gpio_in(DEVICE(&s->a7mpcore), - FSL_IMX7_WDOGn_IRQ[i])); + qdev_get_gpio_in(gic, FSL_IMX7_WDOGn_IRQ[i])); } /* @@ -596,14 +591,23 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_realize(SYS_BUS_DEVICE(&s->pcie), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, FSL_IMX7_PCIE_REG_ADDR); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTA_IRQ); + object_property_set_int(OBJECT(&s->pcie4_msi_irq), "num-lines", 2, + &error_abort); + qdev_realize(DEVICE(&s->pcie4_msi_irq), NULL, &error_abort); + + irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTD_MSI_IRQ); + qdev_connect_gpio_out(DEVICE(&s->pcie4_msi_irq), 0, irq); + + irq = qdev_get_gpio_in(gic, FSL_IMX7_PCI_INTA_IRQ); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, irq); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTB_IRQ); + irq = qdev_get_gpio_in(gic, FSL_IMX7_PCI_INTB_IRQ); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, irq); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTC_IRQ); + irq = qdev_get_gpio_in(gic, FSL_IMX7_PCI_INTC_IRQ); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, irq); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTD_IRQ); + irq = qdev_get_gpio_in(DEVICE(&s->pcie4_msi_irq), 0); sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, irq); + irq = qdev_get_gpio_in(DEVICE(&s->pcie4_msi_irq), 1); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 4, irq); /* * USBs @@ -631,7 +635,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, FSL_IMX7_USBn_ADDR[i]); - irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USBn_IRQ[i]); + irq = qdev_get_gpio_in(gic, FSL_IMX7_USBn_IRQ[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, irq); snprintf(name, NAME_SIZE, "usbmisc%d", i); @@ -735,17 +739,16 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) &s->caam); } -static Property fsl_imx7_properties[] = { +static const 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(), }; -static void fsl_imx7_class_init(ObjectClass *oc, void *data) +static void fsl_imx7_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c new file mode 100644 index 00000000000..23e662c16ca --- /dev/null +++ b/hw/arm/fsl-imx8mp.c @@ -0,0 +1,712 @@ +/* + * i.MX 8M Plus SoC Implementation + * + * Based on hw/arm/fsl-imx6.c + * + * Copyright (c) 2024, Bernhard Beschow + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/address-spaces.h" +#include "hw/arm/bsa.h" +#include "hw/arm/fsl-imx8mp.h" +#include "hw/intc/arm_gicv3.h" +#include "hw/misc/unimp.h" +#include "hw/boards.h" +#include "system/system.h" +#include "target/arm/cpu-qom.h" +#include "qapi/error.h" +#include "qobject/qlist.h" + +static const struct { + hwaddr addr; + size_t size; + const char *name; +} fsl_imx8mp_memmap[] = { + [FSL_IMX8MP_RAM] = { FSL_IMX8MP_RAM_START, FSL_IMX8MP_RAM_SIZE_MAX, "ram" }, + [FSL_IMX8MP_DDR_PHY_BROADCAST] = { 0x3dc00000, 4 * MiB, "ddr_phy_broadcast" }, + [FSL_IMX8MP_DDR_PERF_MON] = { 0x3d800000, 4 * MiB, "ddr_perf_mon" }, + [FSL_IMX8MP_DDR_CTL] = { 0x3d400000, 4 * MiB, "ddr_ctl" }, + [FSL_IMX8MP_DDR_BLK_CTRL] = { 0x3d000000, 1 * MiB, "ddr_blk_ctrl" }, + [FSL_IMX8MP_DDR_PHY] = { 0x3c000000, 16 * MiB, "ddr_phy" }, + [FSL_IMX8MP_AUDIO_DSP] = { 0x3b000000, 16 * MiB, "audio_dsp" }, + [FSL_IMX8MP_GIC_DIST] = { 0x38800000, 512 * KiB, "gic_dist" }, + [FSL_IMX8MP_GIC_REDIST] = { 0x38880000, 512 * KiB, "gic_redist" }, + [FSL_IMX8MP_NPU] = { 0x38500000, 2 * MiB, "npu" }, + [FSL_IMX8MP_VPU] = { 0x38340000, 2 * MiB, "vpu" }, + [FSL_IMX8MP_VPU_BLK_CTRL] = { 0x38330000, 2 * MiB, "vpu_blk_ctrl" }, + [FSL_IMX8MP_VPU_VC8000E_ENCODER] = { 0x38320000, 2 * MiB, "vpu_vc8000e_encoder" }, + [FSL_IMX8MP_VPU_G2_DECODER] = { 0x38310000, 2 * MiB, "vpu_g2_decoder" }, + [FSL_IMX8MP_VPU_G1_DECODER] = { 0x38300000, 2 * MiB, "vpu_g1_decoder" }, + [FSL_IMX8MP_USB2_GLUE] = { 0x382f0000, 0x100, "usb2_glue" }, + [FSL_IMX8MP_USB2_OTG] = { 0x3820cc00, 0x100, "usb2_otg" }, + [FSL_IMX8MP_USB2_DEV] = { 0x3820c700, 0x500, "usb2_dev" }, + [FSL_IMX8MP_USB2] = { 0x38200000, 0xc700, "usb2" }, + [FSL_IMX8MP_USB1_GLUE] = { 0x381f0000, 0x100, "usb1_glue" }, + [FSL_IMX8MP_USB1_OTG] = { 0x3810cc00, 0x100, "usb1_otg" }, + [FSL_IMX8MP_USB1_DEV] = { 0x3810c700, 0x500, "usb1_dev" }, + [FSL_IMX8MP_USB1] = { 0x38100000, 0xc700, "usb1" }, + [FSL_IMX8MP_GPU2D] = { 0x38008000, 32 * KiB, "gpu2d" }, + [FSL_IMX8MP_GPU3D] = { 0x38000000, 32 * KiB, "gpu3d" }, + [FSL_IMX8MP_QSPI1_RX_BUFFER] = { 0x34000000, 32 * MiB, "qspi1_rx_buffer" }, + [FSL_IMX8MP_PCIE1] = { 0x33800000, 4 * MiB, "pcie1" }, + [FSL_IMX8MP_QSPI1_TX_BUFFER] = { 0x33008000, 32 * KiB, "qspi1_tx_buffer" }, + [FSL_IMX8MP_APBH_DMA] = { 0x33000000, 32 * KiB, "apbh_dma" }, + + /* AIPS-5 Begin */ + [FSL_IMX8MP_MU_3_B] = { 0x30e90000, 64 * KiB, "mu_3_b" }, + [FSL_IMX8MP_MU_3_A] = { 0x30e80000, 64 * KiB, "mu_3_a" }, + [FSL_IMX8MP_MU_2_B] = { 0x30e70000, 64 * KiB, "mu_2_b" }, + [FSL_IMX8MP_MU_2_A] = { 0x30e60000, 64 * KiB, "mu_2_a" }, + [FSL_IMX8MP_EDMA_CHANNELS] = { 0x30e40000, 128 * KiB, "edma_channels" }, + [FSL_IMX8MP_EDMA_MANAGEMENT_PAGE] = { 0x30e30000, 64 * KiB, "edma_management_page" }, + [FSL_IMX8MP_AUDIO_BLK_CTRL] = { 0x30e20000, 64 * KiB, "audio_blk_ctrl" }, + [FSL_IMX8MP_SDMA2] = { 0x30e10000, 64 * KiB, "sdma2" }, + [FSL_IMX8MP_SDMA3] = { 0x30e00000, 64 * KiB, "sdma3" }, + [FSL_IMX8MP_AIPS5_CONFIGURATION] = { 0x30df0000, 64 * KiB, "aips5_configuration" }, + [FSL_IMX8MP_SPBA2] = { 0x30cf0000, 64 * KiB, "spba2" }, + [FSL_IMX8MP_AUDIO_XCVR_RX] = { 0x30cc0000, 64 * KiB, "audio_xcvr_rx" }, + [FSL_IMX8MP_HDMI_TX_AUDLNK_MSTR] = { 0x30cb0000, 64 * KiB, "hdmi_tx_audlnk_mstr" }, + [FSL_IMX8MP_PDM] = { 0x30ca0000, 64 * KiB, "pdm" }, + [FSL_IMX8MP_ASRC] = { 0x30c90000, 64 * KiB, "asrc" }, + [FSL_IMX8MP_SAI7] = { 0x30c80000, 64 * KiB, "sai7" }, + [FSL_IMX8MP_SAI6] = { 0x30c60000, 64 * KiB, "sai6" }, + [FSL_IMX8MP_SAI5] = { 0x30c50000, 64 * KiB, "sai5" }, + [FSL_IMX8MP_SAI3] = { 0x30c30000, 64 * KiB, "sai3" }, + [FSL_IMX8MP_SAI2] = { 0x30c20000, 64 * KiB, "sai2" }, + [FSL_IMX8MP_SAI1] = { 0x30c10000, 64 * KiB, "sai1" }, + /* AIPS-5 End */ + + /* AIPS-4 Begin */ + [FSL_IMX8MP_HDMI_TX] = { 0x32fc0000, 128 * KiB, "hdmi_tx" }, + [FSL_IMX8MP_TZASC] = { 0x32f80000, 64 * KiB, "tzasc" }, + [FSL_IMX8MP_HSIO_BLK_CTL] = { 0x32f10000, 64 * KiB, "hsio_blk_ctl" }, + [FSL_IMX8MP_PCIE_PHY1] = { 0x32f00000, 64 * KiB, "pcie_phy1" }, + [FSL_IMX8MP_MEDIA_BLK_CTL] = { 0x32ec0000, 64 * KiB, "media_blk_ctl" }, + [FSL_IMX8MP_LCDIF2] = { 0x32e90000, 64 * KiB, "lcdif2" }, + [FSL_IMX8MP_LCDIF1] = { 0x32e80000, 64 * KiB, "lcdif1" }, + [FSL_IMX8MP_MIPI_DSI1] = { 0x32e60000, 64 * KiB, "mipi_dsi1" }, + [FSL_IMX8MP_MIPI_CSI2] = { 0x32e50000, 64 * KiB, "mipi_csi2" }, + [FSL_IMX8MP_MIPI_CSI1] = { 0x32e40000, 64 * KiB, "mipi_csi1" }, + [FSL_IMX8MP_IPS_DEWARP] = { 0x32e30000, 64 * KiB, "ips_dewarp" }, + [FSL_IMX8MP_ISP2] = { 0x32e20000, 64 * KiB, "isp2" }, + [FSL_IMX8MP_ISP1] = { 0x32e10000, 64 * KiB, "isp1" }, + [FSL_IMX8MP_ISI] = { 0x32e00000, 64 * KiB, "isi" }, + [FSL_IMX8MP_AIPS4_CONFIGURATION] = { 0x32df0000, 64 * KiB, "aips4_configuration" }, + /* AIPS-4 End */ + + [FSL_IMX8MP_INTERCONNECT] = { 0x32700000, 1 * MiB, "interconnect" }, + + /* AIPS-3 Begin */ + [FSL_IMX8MP_ENET2_TSN] = { 0x30bf0000, 64 * KiB, "enet2_tsn" }, + [FSL_IMX8MP_ENET1] = { 0x30be0000, 64 * KiB, "enet1" }, + [FSL_IMX8MP_SDMA1] = { 0x30bd0000, 64 * KiB, "sdma1" }, + [FSL_IMX8MP_QSPI] = { 0x30bb0000, 64 * KiB, "qspi" }, + [FSL_IMX8MP_USDHC3] = { 0x30b60000, 64 * KiB, "usdhc3" }, + [FSL_IMX8MP_USDHC2] = { 0x30b50000, 64 * KiB, "usdhc2" }, + [FSL_IMX8MP_USDHC1] = { 0x30b40000, 64 * KiB, "usdhc1" }, + [FSL_IMX8MP_I2C6] = { 0x30ae0000, 64 * KiB, "i2c6" }, + [FSL_IMX8MP_I2C5] = { 0x30ad0000, 64 * KiB, "i2c5" }, + [FSL_IMX8MP_SEMAPHORE_HS] = { 0x30ac0000, 64 * KiB, "semaphore_hs" }, + [FSL_IMX8MP_MU_1_B] = { 0x30ab0000, 64 * KiB, "mu_1_b" }, + [FSL_IMX8MP_MU_1_A] = { 0x30aa0000, 64 * KiB, "mu_1_a" }, + [FSL_IMX8MP_AUD_IRQ_STEER] = { 0x30a80000, 64 * KiB, "aud_irq_steer" }, + [FSL_IMX8MP_UART4] = { 0x30a60000, 64 * KiB, "uart4" }, + [FSL_IMX8MP_I2C4] = { 0x30a50000, 64 * KiB, "i2c4" }, + [FSL_IMX8MP_I2C3] = { 0x30a40000, 64 * KiB, "i2c3" }, + [FSL_IMX8MP_I2C2] = { 0x30a30000, 64 * KiB, "i2c2" }, + [FSL_IMX8MP_I2C1] = { 0x30a20000, 64 * KiB, "i2c1" }, + [FSL_IMX8MP_AIPS3_CONFIGURATION] = { 0x309f0000, 64 * KiB, "aips3_configuration" }, + [FSL_IMX8MP_CAAM] = { 0x30900000, 256 * KiB, "caam" }, + [FSL_IMX8MP_SPBA1] = { 0x308f0000, 64 * KiB, "spba1" }, + [FSL_IMX8MP_FLEXCAN2] = { 0x308d0000, 64 * KiB, "flexcan2" }, + [FSL_IMX8MP_FLEXCAN1] = { 0x308c0000, 64 * KiB, "flexcan1" }, + [FSL_IMX8MP_UART2] = { 0x30890000, 64 * KiB, "uart2" }, + [FSL_IMX8MP_UART3] = { 0x30880000, 64 * KiB, "uart3" }, + [FSL_IMX8MP_UART1] = { 0x30860000, 64 * KiB, "uart1" }, + [FSL_IMX8MP_ECSPI3] = { 0x30840000, 64 * KiB, "ecspi3" }, + [FSL_IMX8MP_ECSPI2] = { 0x30830000, 64 * KiB, "ecspi2" }, + [FSL_IMX8MP_ECSPI1] = { 0x30820000, 64 * KiB, "ecspi1" }, + /* AIPS-3 End */ + + /* AIPS-2 Begin */ + [FSL_IMX8MP_QOSC] = { 0x307f0000, 64 * KiB, "qosc" }, + [FSL_IMX8MP_PERFMON2] = { 0x307d0000, 64 * KiB, "perfmon2" }, + [FSL_IMX8MP_PERFMON1] = { 0x307c0000, 64 * KiB, "perfmon1" }, + [FSL_IMX8MP_GPT4] = { 0x30700000, 64 * KiB, "gpt4" }, + [FSL_IMX8MP_GPT5] = { 0x306f0000, 64 * KiB, "gpt5" }, + [FSL_IMX8MP_GPT6] = { 0x306e0000, 64 * KiB, "gpt6" }, + [FSL_IMX8MP_SYSCNT_CTRL] = { 0x306c0000, 64 * KiB, "syscnt_ctrl" }, + [FSL_IMX8MP_SYSCNT_CMP] = { 0x306b0000, 64 * KiB, "syscnt_cmp" }, + [FSL_IMX8MP_SYSCNT_RD] = { 0x306a0000, 64 * KiB, "syscnt_rd" }, + [FSL_IMX8MP_PWM4] = { 0x30690000, 64 * KiB, "pwm4" }, + [FSL_IMX8MP_PWM3] = { 0x30680000, 64 * KiB, "pwm3" }, + [FSL_IMX8MP_PWM2] = { 0x30670000, 64 * KiB, "pwm2" }, + [FSL_IMX8MP_PWM1] = { 0x30660000, 64 * KiB, "pwm1" }, + [FSL_IMX8MP_AIPS2_CONFIGURATION] = { 0x305f0000, 64 * KiB, "aips2_configuration" }, + /* AIPS-2 End */ + + /* AIPS-1 Begin */ + [FSL_IMX8MP_CSU] = { 0x303e0000, 64 * KiB, "csu" }, + [FSL_IMX8MP_RDC] = { 0x303d0000, 64 * KiB, "rdc" }, + [FSL_IMX8MP_SEMAPHORE2] = { 0x303c0000, 64 * KiB, "semaphore2" }, + [FSL_IMX8MP_SEMAPHORE1] = { 0x303b0000, 64 * KiB, "semaphore1" }, + [FSL_IMX8MP_GPC] = { 0x303a0000, 64 * KiB, "gpc" }, + [FSL_IMX8MP_SRC] = { 0x30390000, 64 * KiB, "src" }, + [FSL_IMX8MP_CCM] = { 0x30380000, 64 * KiB, "ccm" }, + [FSL_IMX8MP_SNVS_HP] = { 0x30370000, 64 * KiB, "snvs_hp" }, + [FSL_IMX8MP_ANA_PLL] = { 0x30360000, 64 * KiB, "ana_pll" }, + [FSL_IMX8MP_OCOTP_CTRL] = { 0x30350000, 64 * KiB, "ocotp_ctrl" }, + [FSL_IMX8MP_IOMUXC_GPR] = { 0x30340000, 64 * KiB, "iomuxc_gpr" }, + [FSL_IMX8MP_IOMUXC] = { 0x30330000, 64 * KiB, "iomuxc" }, + [FSL_IMX8MP_GPT3] = { 0x302f0000, 64 * KiB, "gpt3" }, + [FSL_IMX8MP_GPT2] = { 0x302e0000, 64 * KiB, "gpt2" }, + [FSL_IMX8MP_GPT1] = { 0x302d0000, 64 * KiB, "gpt1" }, + [FSL_IMX8MP_WDOG3] = { 0x302a0000, 64 * KiB, "wdog3" }, + [FSL_IMX8MP_WDOG2] = { 0x30290000, 64 * KiB, "wdog2" }, + [FSL_IMX8MP_WDOG1] = { 0x30280000, 64 * KiB, "wdog1" }, + [FSL_IMX8MP_ANA_OSC] = { 0x30270000, 64 * KiB, "ana_osc" }, + [FSL_IMX8MP_ANA_TSENSOR] = { 0x30260000, 64 * KiB, "ana_tsensor" }, + [FSL_IMX8MP_GPIO5] = { 0x30240000, 64 * KiB, "gpio5" }, + [FSL_IMX8MP_GPIO4] = { 0x30230000, 64 * KiB, "gpio4" }, + [FSL_IMX8MP_GPIO3] = { 0x30220000, 64 * KiB, "gpio3" }, + [FSL_IMX8MP_GPIO2] = { 0x30210000, 64 * KiB, "gpio2" }, + [FSL_IMX8MP_GPIO1] = { 0x30200000, 64 * KiB, "gpio1" }, + [FSL_IMX8MP_AIPS1_CONFIGURATION] = { 0x301f0000, 64 * KiB, "aips1_configuration" }, + /* AIPS-1 End */ + + [FSL_IMX8MP_A53_DAP] = { 0x28000000, 16 * MiB, "a53_dap" }, + [FSL_IMX8MP_PCIE1_MEM] = { 0x18000000, 128 * MiB, "pcie1_mem" }, + [FSL_IMX8MP_QSPI_MEM] = { 0x08000000, 256 * MiB, "qspi_mem" }, + [FSL_IMX8MP_OCRAM] = { 0x00900000, 576 * KiB, "ocram" }, + [FSL_IMX8MP_TCM_DTCM] = { 0x00800000, 128 * KiB, "tcm_dtcm" }, + [FSL_IMX8MP_TCM_ITCM] = { 0x007e0000, 128 * KiB, "tcm_itcm" }, + [FSL_IMX8MP_OCRAM_S] = { 0x00180000, 36 * KiB, "ocram_s" }, + [FSL_IMX8MP_CAAM_MEM] = { 0x00100000, 32 * KiB, "caam_mem" }, + [FSL_IMX8MP_BOOT_ROM_PROTECTED] = { 0x0003f000, 4 * KiB, "boot_rom_protected" }, + [FSL_IMX8MP_BOOT_ROM] = { 0x00000000, 252 * KiB, "boot_rom" }, +}; + +static void fsl_imx8mp_init(Object *obj) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + FslImx8mpState *s = FSL_IMX8MP(obj); + int i; + + for (i = 0; i < MIN(ms->smp.cpus, FSL_IMX8MP_NUM_CPUS); i++) { + g_autofree char *name = g_strdup_printf("cpu%d", i); + object_initialize_child(obj, name, &s->cpu[i], + ARM_CPU_TYPE_NAME("cortex-a53")); + } + + object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GICV3); + + object_initialize_child(obj, "ccm", &s->ccm, TYPE_IMX8MP_CCM); + + object_initialize_child(obj, "analog", &s->analog, TYPE_IMX8MP_ANALOG); + + object_initialize_child(obj, "snvs", &s->snvs, TYPE_IMX7_SNVS); + + for (i = 0; i < FSL_IMX8MP_NUM_UARTS; i++) { + g_autofree char *name = g_strdup_printf("uart%d", i + 1); + object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); + } + + for (i = 0; i < FSL_IMX8MP_NUM_GPTS; i++) { + g_autofree char *name = g_strdup_printf("gpt%d", i + 1); + object_initialize_child(obj, name, &s->gpt[i], TYPE_IMX8MP_GPT); + } + object_initialize_child(obj, "gpt5-gpt6-irq", &s->gpt5_gpt6_irq, + TYPE_OR_IRQ); + + for (i = 0; i < FSL_IMX8MP_NUM_I2CS; i++) { + g_autofree char *name = g_strdup_printf("i2c%d", i + 1); + object_initialize_child(obj, name, &s->i2c[i], TYPE_IMX_I2C); + } + + for (i = 0; i < FSL_IMX8MP_NUM_GPIOS; i++) { + g_autofree char *name = g_strdup_printf("gpio%d", i + 1); + object_initialize_child(obj, name, &s->gpio[i], TYPE_IMX_GPIO); + } + + for (i = 0; i < FSL_IMX8MP_NUM_USDHCS; i++) { + g_autofree char *name = g_strdup_printf("usdhc%d", i + 1); + object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); + } + + for (i = 0; i < FSL_IMX8MP_NUM_USBS; i++) { + g_autofree char *name = g_strdup_printf("usb%d", i); + object_initialize_child(obj, name, &s->usb[i], TYPE_USB_DWC3); + } + + for (i = 0; i < FSL_IMX8MP_NUM_ECSPIS; i++) { + g_autofree char *name = g_strdup_printf("spi%d", i + 1); + object_initialize_child(obj, name, &s->spi[i], TYPE_IMX_SPI); + } + + for (i = 0; i < FSL_IMX8MP_NUM_WDTS; i++) { + g_autofree char *name = g_strdup_printf("wdt%d", i); + object_initialize_child(obj, name, &s->wdt[i], TYPE_IMX2_WDT); + } + + object_initialize_child(obj, "eth0", &s->enet, TYPE_IMX_ENET); + + object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); + object_initialize_child(obj, "pcie_phy", &s->pcie_phy, + TYPE_FSL_IMX8M_PCIE_PHY); +} + +static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + FslImx8mpState *s = FSL_IMX8MP(dev); + DeviceState *gicdev = DEVICE(&s->gic); + int i; + + if (ms->smp.cpus > FSL_IMX8MP_NUM_CPUS) { + error_setg(errp, "%s: Only %d CPUs are supported (%d requested)", + TYPE_FSL_IMX8MP, FSL_IMX8MP_NUM_CPUS, ms->smp.cpus); + return; + } + + /* CPUs */ + for (i = 0; i < ms->smp.cpus; i++) { + /* On uniprocessor, the CBAR is set to 0 */ + if (ms->smp.cpus > 1) { + object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar", + fsl_imx8mp_memmap[FSL_IMX8MP_GIC_DIST].addr, + &error_abort); + } + + /* + * CNTFID0 base frequency in Hz of system counter + */ + object_property_set_int(OBJECT(&s->cpu[i]), "cntfrq", 8000000, + &error_abort); + + if (i) { + /* + * Secondary CPUs start in powered-down state (and can be + * powered up via the SRC system reset controller) + */ + object_property_set_bool(OBJECT(&s->cpu[i]), "start-powered-off", + true, &error_abort); + } + + if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { + return; + } + } + + /* GIC */ + { + SysBusDevice *gicsbd = SYS_BUS_DEVICE(&s->gic); + QList *redist_region_count; + + qdev_prop_set_uint32(gicdev, "num-cpu", ms->smp.cpus); + qdev_prop_set_uint32(gicdev, "num-irq", + FSL_IMX8MP_NUM_IRQS + GIC_INTERNAL); + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, ms->smp.cpus); + qdev_prop_set_array(gicdev, "redist-region-count", redist_region_count); + object_property_set_link(OBJECT(&s->gic), "sysmem", + OBJECT(get_system_memory()), &error_fatal); + if (!sysbus_realize(gicsbd, errp)) { + return; + } + sysbus_mmio_map(gicsbd, 0, fsl_imx8mp_memmap[FSL_IMX8MP_GIC_DIST].addr); + sysbus_mmio_map(gicsbd, 1, fsl_imx8mp_memmap[FSL_IMX8MP_GIC_REDIST].addr); + + /* + * 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 interrupt outputs to the CPU's inputs. + */ + for (i = 0; i < ms->smp.cpus; i++) { + DeviceState *cpudev = DEVICE(&s->cpu[i]); + int intidbase = FSL_IMX8MP_NUM_IRQS + i * GIC_INTERNAL; + qemu_irq irq; + + /* + * Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs. + */ + static const int timer_irqs[] = { + [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, + }; + + for (int j = 0; j < ARRAY_SIZE(timer_irqs); j++) { + irq = qdev_get_gpio_in(gicdev, intidbase + timer_irqs[j]); + qdev_connect_gpio_out(cpudev, j, irq); + } + + irq = qdev_get_gpio_in(gicdev, intidbase + ARCH_GIC_MAINT_IRQ); + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", + 0, irq); + + irq = qdev_get_gpio_in(gicdev, intidbase + VIRTUAL_PMU_IRQ); + qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, irq); + + sysbus_connect_irq(gicsbd, i, + qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(gicsbd, i + ms->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + } + } + + /* CCM */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ccm), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_CCM].addr); + + /* Analog */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->analog), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->analog), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_ANA_PLL].addr); + + /* UARTs */ + for (i = 0; i < FSL_IMX8MP_NUM_UARTS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } serial_table[FSL_IMX8MP_NUM_UARTS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_UART1].addr, FSL_IMX8MP_UART1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_UART2].addr, FSL_IMX8MP_UART2_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_UART3].addr, FSL_IMX8MP_UART3_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_UART4].addr, FSL_IMX8MP_UART4_IRQ }, + }; + + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + qdev_get_gpio_in(gicdev, serial_table[i].irq)); + } + + /* GPTs */ + object_property_set_int(OBJECT(&s->gpt5_gpt6_irq), "num-lines", 2, + &error_abort); + if (!qdev_realize(DEVICE(&s->gpt5_gpt6_irq), NULL, errp)) { + return; + } + + qdev_connect_gpio_out(DEVICE(&s->gpt5_gpt6_irq), 0, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_GPT5_GPT6_IRQ)); + + for (i = 0; i < FSL_IMX8MP_NUM_GPTS; i++) { + hwaddr gpt_addrs[FSL_IMX8MP_NUM_GPTS] = { + fsl_imx8mp_memmap[FSL_IMX8MP_GPT1].addr, + fsl_imx8mp_memmap[FSL_IMX8MP_GPT2].addr, + fsl_imx8mp_memmap[FSL_IMX8MP_GPT3].addr, + fsl_imx8mp_memmap[FSL_IMX8MP_GPT4].addr, + fsl_imx8mp_memmap[FSL_IMX8MP_GPT5].addr, + fsl_imx8mp_memmap[FSL_IMX8MP_GPT6].addr, + }; + + s->gpt[i].ccm = IMX_CCM(&s->ccm); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpt[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, gpt_addrs[i]); + + if (i < FSL_IMX8MP_NUM_GPTS - 2) { + static const unsigned int gpt_irqs[FSL_IMX8MP_NUM_GPTS - 2] = { + FSL_IMX8MP_GPT1_IRQ, + FSL_IMX8MP_GPT2_IRQ, + FSL_IMX8MP_GPT3_IRQ, + FSL_IMX8MP_GPT4_IRQ, + }; + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, + qdev_get_gpio_in(gicdev, gpt_irqs[i])); + } else { + int irq = i - FSL_IMX8MP_NUM_GPTS + 2; + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, + qdev_get_gpio_in(DEVICE(&s->gpt5_gpt6_irq), irq)); + } + } + + /* I2Cs */ + for (i = 0; i < FSL_IMX8MP_NUM_I2CS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } i2c_table[FSL_IMX8MP_NUM_I2CS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C1].addr, FSL_IMX8MP_I2C1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C2].addr, FSL_IMX8MP_I2C2_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C3].addr, FSL_IMX8MP_I2C3_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C4].addr, FSL_IMX8MP_I2C4_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C5].addr, FSL_IMX8MP_I2C5_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C6].addr, FSL_IMX8MP_I2C6_IRQ }, + }; + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, + qdev_get_gpio_in(gicdev, i2c_table[i].irq)); + } + + /* GPIOs */ + for (i = 0; i < FSL_IMX8MP_NUM_GPIOS; i++) { + struct { + hwaddr addr; + unsigned int irq_low; + unsigned int irq_high; + } gpio_table[FSL_IMX8MP_NUM_GPIOS] = { + { + fsl_imx8mp_memmap[FSL_IMX8MP_GPIO1].addr, + FSL_IMX8MP_GPIO1_LOW_IRQ, + FSL_IMX8MP_GPIO1_HIGH_IRQ + }, + { + fsl_imx8mp_memmap[FSL_IMX8MP_GPIO2].addr, + FSL_IMX8MP_GPIO2_LOW_IRQ, + FSL_IMX8MP_GPIO2_HIGH_IRQ + }, + { + fsl_imx8mp_memmap[FSL_IMX8MP_GPIO3].addr, + FSL_IMX8MP_GPIO3_LOW_IRQ, + FSL_IMX8MP_GPIO3_HIGH_IRQ + }, + { + fsl_imx8mp_memmap[FSL_IMX8MP_GPIO4].addr, + FSL_IMX8MP_GPIO4_LOW_IRQ, + FSL_IMX8MP_GPIO4_HIGH_IRQ + }, + { + fsl_imx8mp_memmap[FSL_IMX8MP_GPIO5].addr, + FSL_IMX8MP_GPIO5_LOW_IRQ, + FSL_IMX8MP_GPIO5_HIGH_IRQ + }, + }; + + object_property_set_bool(OBJECT(&s->gpio[i]), "has-edge-sel", true, + &error_abort); + object_property_set_bool(OBJECT(&s->gpio[i]), "has-upper-pin-irq", + true, &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(gicdev, gpio_table[i].irq_low)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1, + qdev_get_gpio_in(gicdev, gpio_table[i].irq_high)); + } + + /* USDHCs */ + for (i = 0; i < FSL_IMX8MP_NUM_USDHCS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } usdhc_table[FSL_IMX8MP_NUM_USDHCS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_USDHC1].addr, FSL_IMX8MP_USDHC1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_USDHC2].addr, FSL_IMX8MP_USDHC2_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_USDHC3].addr, FSL_IMX8MP_USDHC3_IRQ }, + }; + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->usdhc[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0, usdhc_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, + qdev_get_gpio_in(gicdev, usdhc_table[i].irq)); + } + + /* USBs */ + for (i = 0; i < FSL_IMX8MP_NUM_USBS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } usb_table[FSL_IMX8MP_NUM_USBS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_USB1].addr, FSL_IMX8MP_USB1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_USB2].addr, FSL_IMX8MP_USB2_IRQ }, + }; + + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "p2", 1); + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "p3", 1); + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "slots", 2); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->usb[i]), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, usb_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i].sysbus_xhci), 0, + qdev_get_gpio_in(gicdev, usb_table[i].irq)); + } + + /* ECSPIs */ + for (i = 0; i < FSL_IMX8MP_NUM_ECSPIS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } spi_table[FSL_IMX8MP_NUM_ECSPIS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_ECSPI1].addr, FSL_IMX8MP_ECSPI1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_ECSPI2].addr, FSL_IMX8MP_ECSPI2_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_ECSPI3].addr, FSL_IMX8MP_ECSPI3_IRQ }, + }; + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, + qdev_get_gpio_in(gicdev, spi_table[i].irq)); + } + + /* ENET1 */ + object_property_set_uint(OBJECT(&s->enet), "phy-num", s->phy_num, + &error_abort); + object_property_set_uint(OBJECT(&s->enet), "tx-ring-num", 3, &error_abort); + qemu_configure_nic_device(DEVICE(&s->enet), true, NULL); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->enet), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->enet), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_ENET1].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->enet), 0, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_ENET1_MAC_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->enet), 1, + qdev_get_gpio_in(gicdev, FSL_IMX6_ENET1_MAC_1588_IRQ)); + + /* SNVS */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->snvs), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_SNVS_HP].addr); + + /* Watchdogs */ + for (i = 0; i < FSL_IMX8MP_NUM_WDTS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } wdog_table[FSL_IMX8MP_NUM_WDTS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_WDOG1].addr, FSL_IMX8MP_WDOG1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_WDOG2].addr, FSL_IMX8MP_WDOG2_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_WDOG3].addr, FSL_IMX8MP_WDOG3_IRQ }, + }; + + object_property_set_bool(OBJECT(&s->wdt[i]), "pretimeout-support", + true, &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, wdog_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[i]), 0, + qdev_get_gpio_in(gicdev, wdog_table[i].irq)); + } + + /* PCIe */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_PCIE1].addr); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_PCI_INTA_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_PCI_INTB_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_PCI_INTC_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_PCI_INTD_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 4, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_PCI_MSI_IRQ)); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie_phy), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie_phy), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_PCIE_PHY1].addr); + + /* On-Chip RAM */ + if (!memory_region_init_ram(&s->ocram, NULL, "imx8mp.ocram", + fsl_imx8mp_memmap[FSL_IMX8MP_OCRAM].size, + errp)) { + return; + } + memory_region_add_subregion(get_system_memory(), + fsl_imx8mp_memmap[FSL_IMX8MP_OCRAM].addr, + &s->ocram); + + /* Unimplemented devices */ + for (i = 0; i < ARRAY_SIZE(fsl_imx8mp_memmap); i++) { + switch (i) { + case FSL_IMX8MP_ANA_PLL: + case FSL_IMX8MP_CCM: + case FSL_IMX8MP_GIC_DIST: + case FSL_IMX8MP_GIC_REDIST: + case FSL_IMX8MP_GPIO1 ... FSL_IMX8MP_GPIO5: + case FSL_IMX8MP_ECSPI1 ... FSL_IMX8MP_ECSPI3: + case FSL_IMX8MP_ENET1: + case FSL_IMX8MP_I2C1 ... FSL_IMX8MP_I2C6: + case FSL_IMX8MP_OCRAM: + case FSL_IMX8MP_PCIE1: + case FSL_IMX8MP_PCIE_PHY1: + case FSL_IMX8MP_RAM: + case FSL_IMX8MP_SNVS_HP: + case FSL_IMX8MP_UART1 ... FSL_IMX8MP_UART4: + case FSL_IMX8MP_USB1 ... FSL_IMX8MP_USB2: + case FSL_IMX8MP_USDHC1 ... FSL_IMX8MP_USDHC3: + case FSL_IMX8MP_WDOG1 ... FSL_IMX8MP_WDOG3: + /* device implemented and treated above */ + break; + + default: + create_unimplemented_device(fsl_imx8mp_memmap[i].name, + fsl_imx8mp_memmap[i].addr, + fsl_imx8mp_memmap[i].size); + break; + } + } +} + +static const Property fsl_imx8mp_properties[] = { + DEFINE_PROP_UINT32("fec1-phy-num", FslImx8mpState, phy_num, 0), + DEFINE_PROP_BOOL("fec1-phy-connected", FslImx8mpState, phy_connected, true), +}; + +static void fsl_imx8mp_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + device_class_set_props(dc, fsl_imx8mp_properties); + dc->realize = fsl_imx8mp_realize; + + dc->desc = "i.MX 8M Plus SoC"; +} + +static const TypeInfo fsl_imx8mp_types[] = { + { + .name = TYPE_FSL_IMX8MP, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(FslImx8mpState), + .instance_init = fsl_imx8mp_init, + .class_init = fsl_imx8mp_class_init, + }, +}; + +DEFINE_TYPES(fsl_imx8mp_types) diff --git a/hw/arm/gumstix.c b/hw/arm/gumstix.c deleted file mode 100644 index 2ca4140c9fc..00000000000 --- a/hw/arm/gumstix.c +++ /dev/null @@ -1,142 +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/units.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" - -#define CONNEX_FLASH_SIZE (16 * MiB) -#define CONNEX_RAM_SIZE (64 * MiB) - -#define VERDEX_FLASH_SIZE (32 * MiB) -#define VERDEX_RAM_SIZE (256 * MiB) - -#define FLASH_SECTOR_SIZE (128 * KiB) - -static void connex_init(MachineState *machine) -{ - PXA2xxState *cpu; - DriveInfo *dinfo; - - cpu = pxa255_init(CONNEX_RAM_SIZE); - - 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); - } - - /* Numonyx RC28F128J3F75 */ - pflash_cfi01_register(0x00000000, "connext.rom", CONNEX_FLASH_SIZE, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - FLASH_SECTOR_SIZE, 2, 0, 0, 0, 0, 0); - - /* Interrupt line of NIC is connected to GPIO line 36 */ - smc91c111_init(&nd_table[0], 0x04000300, - qdev_get_gpio_in(cpu->gpio, 36)); -} - -static void verdex_init(MachineState *machine) -{ - PXA2xxState *cpu; - DriveInfo *dinfo; - - cpu = pxa270_init(VERDEX_RAM_SIZE, 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); - } - - /* Micron RC28F256P30TFA */ - pflash_cfi01_register(0x00000000, "verdex.rom", VERDEX_FLASH_SIZE, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - FLASH_SECTOR_SIZE, 2, 0, 0, 0, 0, 0); - - /* Interrupt line of NIC is connected to GPIO line 99 */ - smc91c111_init(&nd_table[0], 0x04000300, - qdev_get_gpio_in(cpu->gpio, 99)); -} - -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 Pro XL6P COMs (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 f12aacea6b8..3ae26ebebdc 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -25,17 +25,18 @@ #include "hw/arm/boot.h" #include "hw/loader.h" #include "net/net.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.h" #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 @@ -44,7 +45,7 @@ #define MVBAR_ADDR 0x200 #define BOARD_SETUP_ADDR (MVBAR_ADDR + 8 * sizeof(uint32_t)) -#define NIRQ_GIC 160 +#define GIC_EXT_IRQS 128 /* EnergyCore ECX-1000 & ECX-2000 */ /* Board init. */ @@ -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(), }, @@ -138,13 +139,13 @@ static void highbank_regs_init(Object *obj) sysbus_init_mmio(dev, &s->iomem); } -static void highbank_regs_class_init(ObjectClass *klass, void *data) +static void highbank_regs_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 = { @@ -179,7 +180,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) { DeviceState *dev = NULL; SysBusDevice *busdev; - qemu_irq pic[128]; + qemu_irq pic[GIC_EXT_IRQS]; int n; unsigned int smp_cpus = machine->smp.cpus; qemu_irq cpu_irq[4]; @@ -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); @@ -258,7 +260,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) break; } qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); - qdev_prop_set_uint32(dev, "num-irq", NIRQ_GIC); + qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL); busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); @@ -269,7 +271,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) sysbus_connect_irq(busdev, n + 3 * smp_cpus, cpu_vfiq[n]); } - for (n = 0; n < 128; n++) { + for (n = 0; n < GIC_EXT_IRQS; n++) { pic[n] = qdev_get_gpio_in(dev, n); } @@ -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]); @@ -341,12 +341,17 @@ static void midway_init(MachineState *machine) calxeda_init(machine, CALXEDA_MIDWAY); } -static void highbank_class_init(ObjectClass *oc, void *data) +static void highbank_class_init(ObjectClass *oc, const 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; @@ -360,12 +365,17 @@ static const TypeInfo highbank_type = { .class_init = highbank_class_init, }; -static void midway_class_init(ObjectClass *oc, void *data) +static void midway_class_init(ObjectClass *oc, const 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 b4f7f4e8a7f..e95ea5e4e18 100644 --- a/hw/arm/imx25_pdk.c +++ b/hw/arm/imx25_pdk.c @@ -27,9 +27,10 @@ #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" +#include "system/qtest.h" #include "hw/i2c/i2c.h" #include "qemu/cutils.h" @@ -146,6 +147,7 @@ static void imx25_pdk_machine_init(MachineClass *mc) mc->init = imx25_pdk_init; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "imx25.ram"; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("imx25-pdk", imx25_pdk_machine_init) diff --git a/hw/arm/imx8mp-evk.c b/hw/arm/imx8mp-evk.c new file mode 100644 index 00000000000..b3082fa60d8 --- /dev/null +++ b/hw/arm/imx8mp-evk.c @@ -0,0 +1,103 @@ +/* + * NXP i.MX 8M Plus Evaluation Kit System Emulation + * + * Copyright (c) 2024, Bernhard Beschow + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/arm/fsl-imx8mp.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "system/qtest.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include + +static void imx8mp_evk_modify_dtb(const struct arm_boot_info *info, void *fdt) +{ + int i, offset; + + /* Temporarily disable following nodes until they are implemented */ + const char *nodes_to_remove[] = { + "nxp,imx8mp-fspi", + }; + + for (i = 0; i < ARRAY_SIZE(nodes_to_remove); i++) { + const char *dev_str = nodes_to_remove[i]; + + offset = fdt_node_offset_by_compatible(fdt, -1, dev_str); + while (offset >= 0) { + fdt_nop_node(fdt, offset); + offset = fdt_node_offset_by_compatible(fdt, offset, dev_str); + } + } + + /* Remove cpu-idle-states property from CPU nodes */ + offset = fdt_node_offset_by_compatible(fdt, -1, "arm,cortex-a53"); + while (offset >= 0) { + fdt_nop_property(fdt, offset, "cpu-idle-states"); + offset = fdt_node_offset_by_compatible(fdt, offset, "arm,cortex-a53"); + } +} + +static void imx8mp_evk_init(MachineState *machine) +{ + static struct arm_boot_info boot_info; + FslImx8mpState *s; + + if (machine->ram_size > FSL_IMX8MP_RAM_SIZE_MAX) { + error_report("RAM size " RAM_ADDR_FMT " above max supported (%08" PRIx64 ")", + machine->ram_size, FSL_IMX8MP_RAM_SIZE_MAX); + exit(1); + } + + boot_info = (struct arm_boot_info) { + .loader_start = FSL_IMX8MP_RAM_START, + .board_id = -1, + .ram_size = machine->ram_size, + .psci_conduit = QEMU_PSCI_CONDUIT_SMC, + .modify_dtb = imx8mp_evk_modify_dtb, + }; + + s = FSL_IMX8MP(object_new(TYPE_FSL_IMX8MP)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(s)); + object_property_set_uint(OBJECT(s), "fec1-phy-num", 1, &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal); + + memory_region_add_subregion(get_system_memory(), FSL_IMX8MP_RAM_START, + machine->ram); + + for (int i = 0; i < FSL_IMX8MP_NUM_USDHCS; i++) { + BusState *bus; + DeviceState *carddev; + BlockBackend *blk; + DriveInfo *di = drive_get(IF_SD, i, 0); + + if (!di) { + continue; + } + + blk = blk_by_legacy_dinfo(di); + bus = qdev_get_child_bus(DEVICE(&s->usdhc[i]), "sd-bus"); + 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 (!qtest_enabled()) { + arm_load_kernel(&s->cpu[0], machine, &boot_info); + } +} + +static void imx8mp_evk_machine_init(MachineClass *mc) +{ + mc->desc = "NXP i.MX 8M Plus EVK Board"; + mc->init = imx8mp_evk_init; + mc->max_cpus = FSL_IMX8MP_NUM_CPUS; + mc->default_ram_id = "imx8mp-evk.ram"; +} +DEFINE_MACHINE("imx8mp-evk", imx8mp_evk_machine_init) diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index b109ece3ae0..b1d8fbd470a 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" @@ -17,9 +16,9 @@ #include "hw/misc/arm_integrator_debug.h" #include "hw/net/smc91c111.h" #include "net/net.h" -#include "exec/address-spaces.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/address-spaces.h" +#include "system/runstate.h" +#include "system/system.h" #include "qemu/log.h" #include "qemu/error-report.h" #include "hw/char/pl011.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,16 +688,18 @@ 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"; + mc->auto_create_sdcard = true; + + machine_add_audiodev_property(mc); } DEFINE_MACHINE("integratorcp", integratorcp_machine_init) -static Property core_properties[] = { +static const Property core_properties[] = { DEFINE_PROP_UINT32("memsz", IntegratorCMState, memsz, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void core_class_init(ObjectClass *klass, void *data) +static void core_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -696,14 +708,14 @@ static void core_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_integratorcm; } -static void icp_pic_class_init(ObjectClass *klass, void *data) +static void icp_pic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_icp_pic; } -static void icp_control_class_init(ObjectClass *klass, void *data) +static void icp_control_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index b1b281c9acb..362c1454099 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -16,14 +16,15 @@ #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 "system/address-spaces.h" #include "net/net.h" #include "hw/net/lan9118.h" -#include "hw/char/serial.h" -#include "sysemu/qtest.h" -#include "sysemu/sysemu.h" +#include "hw/char/serial-mm.h" +#include "system/qtest.h" +#include "system/system.h" #include "qemu/cutils.h" /* Memory map for Kzm Emulation Baseboard: @@ -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 68329c46178..00000000000 --- a/hw/arm/mainstone.c +++ /dev/null @@ -1,176 +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/units.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_SIZE (64 * MiB) -#define MAINSTONE_ROM_SIZE (8 * MiB) -#define MAINSTONE_FLASH_SIZE (32 * MiB) - -static struct arm_boot_info mainstone_binfo = { - .loader_start = PXA2XX_SDRAM_BASE, - .ram_size = MAINSTONE_RAM_SIZE, -}; - -#define FLASH_SECTOR_SIZE (256 * KiB) - -static void mainstone_common_init(MachineState *machine, - enum mainstone_model_e model, int arm_id) -{ - 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(mainstone_binfo.ram_size, machine->cpu_type); - memory_region_init_rom(rom, NULL, "mainstone.rom", MAINSTONE_ROM_SIZE, - &error_fatal); - memory_region_add_subregion(get_system_memory(), 0x00000000, rom); - - /* There are two 32MiB flash devices on the board */ - for (i = 0; i < 2; i ++) { - dinfo = drive_get(IF_PFLASH, 0, i); - pflash_cfi01_register(mainstone_flash_base[i], - i ? "mainstone.flash1" : "mainstone.flash0", - MAINSTONE_FLASH_SIZE, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - FLASH_SECTOR_SIZE, 4, 0, 0, 0, 0, 0); - } - - mst_irq = sysbus_create_simple("mainstone-fpga", MST_FPGA_PHYS, - 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(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 3ac1e2ea9b4..86982cb0772 100644 --- a/hw/arm/mcimx6ul-evk.c +++ b/hw/arm/mcimx6ul-evk.c @@ -13,10 +13,11 @@ #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" -#include "sysemu/qtest.h" +#include "system/qtest.h" static void mcimx6ul_evk_init(MachineState *machine) { @@ -73,5 +74,6 @@ static void mcimx6ul_evk_machine_init(MachineClass *mc) mc->init = mcimx6ul_evk_init; mc->max_cpus = FSL_IMX6UL_NUM_CPUS; mc->default_ram_id = "mcimx6ul-evk.ram"; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("mcimx6ul-evk", mcimx6ul_evk_machine_init) diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c index d1778122b64..33119610113 100644 --- a/hw/arm/mcimx7d-sabre.c +++ b/hw/arm/mcimx7d-sabre.c @@ -15,10 +15,11 @@ #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" -#include "sysemu/qtest.h" +#include "system/qtest.h" static void mcimx7d_sabre_init(MachineState *machine) { @@ -73,5 +74,6 @@ static void mcimx7d_sabre_machine_init(MachineClass *mc) mc->init = mcimx7d_sabre_init; mc->max_cpus = FSL_IMX7_NUM_CPUS; mc->default_ram_id = "mcimx7d-sabre.ram"; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("mcimx7d-sabre", mcimx7d_sabre_machine_init) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 11eb9112f88..d90be8f4c94 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -1,74 +1,85 @@ arm_ss = ss.source_set() -arm_ss.add(files('boot.c'), fdt) +arm_common_ss = ss.source_set() 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_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_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_Z2', if_true: files('z2.c')) -arm_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview.c')) +arm_common_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic_boards.c')) +arm_common_ss.add(when: 'CONFIG_EMCRAFT_SF2', if_true: files('msf2-som.c')) +arm_common_ss.add(when: 'CONFIG_HIGHBANK', if_true: files('highbank.c')) +arm_common_ss.add(when: 'CONFIG_INTEGRATOR', if_true: files('integratorcp.c')) +arm_common_ss.add(when: 'CONFIG_MICROBIT', if_true: files('microbit.c')) +arm_common_ss.add(when: 'CONFIG_MPS3R', if_true: files('mps3r.c')) +arm_common_ss.add(when: 'CONFIG_MUSICPAL', if_true: [files('musicpal.c')]) +arm_common_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: files('netduinoplus2.c')) +arm_common_ss.add(when: 'CONFIG_OLIMEX_STM32_H405', if_true: files('olimex-stm32-h405.c')) +arm_common_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c', 'npcm7xx_boards.c')) +arm_common_ss.add(when: 'CONFIG_NPCM8XX', if_true: files('npcm8xx.c', 'npcm8xx_boards.c')) +arm_common_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_common_ss.add(when: 'CONFIG_STELLARIS', if_true: files('stellaris.c')) +arm_common_ss.add(when: 'CONFIG_STM32VLDISCOVERY', if_true: files('stm32vldiscovery.c')) +arm_common_ss.add(when: 'CONFIG_ZYNQ', if_true: files('xilinx_zynq.c')) +arm_common_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_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_ALLWINNER_R40', if_true: files('allwinner-r40.c', 'bananapi_m2u.c')) +arm_common_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m.c')) +arm_common_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210.c')) +arm_common_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic.c')) +arm_common_ss.add(when: 'CONFIG_OMAP', if_true: files('omap1.c')) +arm_common_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('allwinner-a10.c', 'cubieboard.c')) +arm_common_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3.c', 'orangepi.c')) +arm_common_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_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_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_common_ss.add(when: ['CONFIG_RASPI', 'TARGET_AARCH64'], if_true: files('bcm2838.c', 'raspi4b.c')) +arm_common_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: files('stm32f100_soc.c')) +arm_common_ss.add(when: 'CONFIG_STM32F205_SOC', if_true: files('stm32f205_soc.c')) +arm_common_ss.add(when: 'CONFIG_STM32F405_SOC', if_true: files('stm32f405_soc.c')) +arm_common_ss.add(when: 'CONFIG_B_L475E_IOT01A', if_true: files('b-l475e-iot01a.c')) +arm_common_ss.add(when: 'CONFIG_STM32L4X5_SOC', if_true: files('stm32l4x5_soc.c')) +arm_common_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp.c', 'xlnx-zcu102.c')) +arm_common_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal.c', 'xlnx-versal-virt.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX25', if_true: files('fsl-imx25.c', 'imx25_pdk.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX31', if_true: files('fsl-imx31.c', 'kzm.c')) +arm_common_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_ast27x0-ssp.c', + 'aspeed_ast27x0-tsp.c', 'aspeed_ast10x0.c', 'aspeed_eeprom.c', 'fby35.c')) -arm_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2.c')) -arm_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2-tz.c')) -arm_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-soc.c')) -arm_ss.add(when: 'CONFIG_MUSCA', if_true: files('musca.c')) -arm_ss.add(when: 'CONFIG_ARMSSE', if_true: files('armsse.c')) -arm_ss.add(when: 'CONFIG_FSL_IMX7', if_true: files('fsl-imx7.c', 'mcimx7d-sabre.c')) -arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c')) -arm_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c')) -arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c')) -arm_ss.add(when: 'CONFIG_XEN', if_true: files('xen_arm.c')) -arm_ss.add_all(xen_ss) +arm_common_ss.add(when: ['CONFIG_ASPEED_SOC', 'TARGET_AARCH64'], if_true: files( + 'aspeed_ast27x0.c', + 'aspeed_ast27x0-fc.c',)) +arm_common_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2.c')) +arm_common_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2-tz.c')) +arm_common_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-soc.c')) +arm_common_ss.add(when: 'CONFIG_MUSCA', if_true: files('musca.c')) +arm_common_ss.add(when: 'CONFIG_ARMSSE', if_true: files('armsse.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX7', if_true: files('fsl-imx7.c', 'mcimx7d-sabre.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX8MP', if_true: files('fsl-imx8mp.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX8MP_EVK', if_true: files('imx8mp-evk.c')) +arm_common_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c')) +arm_common_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_EXYNOS4', if_true: files('exynos4_boards.c')) -system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c')) -system_ss.add(when: 'CONFIG_TOSA', if_true: files('tosa.c')) +arm_common_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c')) +arm_common_ss.add(when: 'CONFIG_COLLIE', if_true: files('collie.c')) +arm_common_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4_boards.c')) +arm_common_ss.add(when: 'CONFIG_NETDUINO2', if_true: files('netduino2.c')) +arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c')) +arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2838_peripherals.c')) +arm_common_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.c')) +arm_common_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c')) +arm_common_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c')) +arm_common_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c')) + +arm_common_ss.add(files('boot.c')) hw_arch += {'arm': arm_ss} +hw_common_arch += {'arm': arm_common_ss} diff --git a/hw/arm/microbit.c b/hw/arm/microbit.c index 50df3620882..525443fdb97 100644 --- a/hw/arm/microbit.c +++ b/hw/arm/microbit.c @@ -12,8 +12,8 @@ #include "qapi/error.h" #include "hw/boards.h" #include "hw/arm/boot.h" -#include "sysemu/sysemu.h" -#include "exec/address-spaces.h" +#include "system/system.h" +#include "system/address-spaces.h" #include "hw/arm/nrf51_soc.h" #include "hw/i2c/microbit_i2c.h" @@ -56,11 +56,11 @@ static void microbit_init(MachineState *machine) memory_region_add_subregion_overlap(&s->nrf51.container, NRF51_TWI_BASE, mr, -1); - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + armv7m_load_kernel(s->nrf51.armv7m.cpu, machine->kernel_filename, 0, s->nrf51.flash_size); } -static void microbit_machine_class_init(ObjectClass *oc, void *data) +static void microbit_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index eae3639da23..5dd87cc0281 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -48,14 +48,15 @@ #include "qemu/units.h" #include "qemu/cutils.h" #include "qapi/error.h" +#include "qobject/qlist.h" #include "qemu/error-report.h" #include "hw/arm/boot.h" #include "hw/arm/armv7m.h" #include "hw/or-irq.h" #include "hw/boards.h" -#include "exec/address-spaces.h" -#include "sysemu/sysemu.h" -#include "sysemu/reset.h" +#include "system/address-spaces.h" +#include "system/system.h" +#include "system/reset.h" #include "hw/misc/unimp.h" #include "hw/char/cmsdk-apb-uart.h" #include "hw/timer/cmsdk-apb-timer.h" @@ -434,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]; @@ -446,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])); @@ -461,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); @@ -469,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); } @@ -499,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); @@ -524,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); @@ -533,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); @@ -809,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); @@ -1217,7 +1211,7 @@ static void mps2tz_common_init(MachineState *machine) mms->remap_irq); } - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + armv7m_load_kernel(mms->iotkit.armv7m[0].cpu, machine->kernel_filename, 0, boot_ram_size(mms)); } @@ -1260,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); @@ -1270,10 +1264,10 @@ 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) +static void mps2tz_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(oc); @@ -1310,10 +1304,14 @@ static void mps2tz_set_default_ram_info(MPS2TZMachineClass *mmc) g_assert_not_reached(); } -static void mps2tz_an505_class_init(ObjectClass *oc, void *data) +static void mps2tz_an505_class_init(ObjectClass *oc, const 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; @@ -1321,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; @@ -1339,10 +1338,14 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data) mps2tz_set_default_ram_info(mmc); } -static void mps2tz_an521_class_init(ObjectClass *oc, void *data) +static void mps2tz_an521_class_init(ObjectClass *oc, const 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; @@ -1350,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; @@ -1368,10 +1372,14 @@ static void mps2tz_an521_class_init(ObjectClass *oc, void *data) mps2tz_set_default_ram_info(mmc); } -static void mps3tz_an524_class_init(ObjectClass *oc, void *data) +static void mps3tz_an524_class_init(ObjectClass *oc, const 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; @@ -1379,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; @@ -1402,10 +1411,14 @@ static void mps3tz_an524_class_init(ObjectClass *oc, void *data) "are BRAM (default) and QSPI."); } -static void mps3tz_an547_class_init(ObjectClass *oc, void *data) +static void mps3tz_an547_class_init(ObjectClass *oc, const 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; @@ -1413,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 */ @@ -1439,7 +1453,7 @@ static const TypeInfo mps2tz_info = { .instance_size = sizeof(MPS2TZMachineState), .class_size = sizeof(MPS2TZMachineClass), .class_init = mps2tz_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_IDAU_INTERFACE }, { } }, diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index d92fd60684c..bd378e360b0 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -33,8 +33,8 @@ #include "hw/arm/armv7m.h" #include "hw/or-irq.h" #include "hw/boards.h" -#include "exec/address-spaces.h" -#include "sysemu/sysemu.h" +#include "system/address-spaces.h" +#include "system/system.h" #include "hw/qdev-properties.h" #include "hw/misc/unimp.h" #include "hw/char/cmsdk-apb-uart.h" @@ -48,6 +48,7 @@ #include "net/net.h" #include "hw/watchdog/cmsdk-apb-watchdog.h" #include "hw/qdev-clock.h" +#include "qobject/qlist.h" #include "qom/object.h" typedef enum MPS2FPGAType { @@ -138,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); @@ -228,7 +224,11 @@ static void mps2_common_init(MachineState *machine) switch (mmc->fpga_type) { case FPGA_AN385: case FPGA_AN386: + qdev_prop_set_uint32(armv7m, "num-irq", 32); + break; case FPGA_AN500: + /* The AN500 configures its Cortex-M7 with 16 MPU regions */ + qdev_prop_set_uint32(armv7m, "mpu-ns-regions", 16); qdev_prop_set_uint32(armv7m, "num-irq", 32); break; case FPGA_AN511: @@ -402,10 +402,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", @@ -458,15 +460,15 @@ static void mps2_common_init(MachineState *machine) /* In hardware this is a LAN9220; the LAN9118 is software compatible * except that it doesn't support the checksum-offload feature. */ - lan9118_init(&nd_table[0], mmc->ethernet_base, + lan9118_init(mmc->ethernet_base, qdev_get_gpio_in(armv7m, mmc->fpga_type == FPGA_AN511 ? 47 : 13)); - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + armv7m_load_kernel(mms->armv7m.cpu, machine->kernel_filename, 0, 0x400000); } -static void mps2_class_init(ObjectClass *oc, void *data) +static void mps2_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -476,56 +478,76 @@ static void mps2_class_init(ObjectClass *oc, void *data) mc->default_ram_id = "mps.ram"; } -static void mps2_an385_class_init(ObjectClass *oc, void *data) +static void mps2_an385_class_init(ObjectClass *oc, const 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; mmc->has_block_ram = true; } -static void mps2_an386_class_init(ObjectClass *oc, void *data) +static void mps2_an386_class_init(ObjectClass *oc, const 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; mmc->has_block_ram = true; } -static void mps2_an500_class_init(ObjectClass *oc, void *data) +static void mps2_an500_class_init(ObjectClass *oc, const 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; mmc->has_block_ram = false; } -static void mps2_an511_class_init(ObjectClass *oc, void *data) +static void mps2_an511_class_init(ObjectClass *oc, const 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 00000000000..48c73acc62e --- /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 "qobject/qlist.h" +#include "system/address-spaces.h" +#include "cpu.h" +#include "system/system.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, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = mps3r_common_init; +} + +static void mps3r_an536_class_init(ObjectClass *oc, const 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 b5fe9f364d5..c5e9c7175a4 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -25,12 +25,12 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "exec/address-spaces.h" -#include "hw/char/serial.h" +#include "system/address-spaces.h" +#include "hw/char/serial-mm.h" #include "hw/arm/msf2-soc.h" #include "hw/misc/unimp.h" #include "hw/qdev-clock.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #define MSF2_TIMER_BASE 0x40004000 #define MSF2_SYSREG_BASE 0x40038000 @@ -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)) { @@ -226,12 +222,11 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("usb", 0x40043000, 0x1000); } -static Property m2sxxx_soc_properties[] = { +static const 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, @@ -239,10 +234,9 @@ static Property m2sxxx_soc_properties[] = { /* default divisors in Libero GUI */ DEFINE_PROP_UINT8("apb0div", MSF2State, apb0div, 2), DEFINE_PROP_UINT8("apb1div", MSF2State, apb1div, 2), - DEFINE_PROP_END_OF_LIST(), }; -static void m2sxxx_soc_class_init(ObjectClass *klass, void *data) +static void m2sxxx_soc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c index 7b3106c790c..29c76c68605 100644 --- a/hw/arm/msf2-som.c +++ b/hw/arm/msf2-som.c @@ -33,7 +33,7 @@ #include "hw/qdev-properties.h" #include "hw/arm/boot.h" #include "hw/qdev-clock.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/arm/msf2-soc.h" #define DDR_BASE_ADDRESS 0xA0000000 @@ -47,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; @@ -55,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); @@ -100,15 +92,20 @@ static void emcraft_sf2_s2s010_init(MachineState *machine) cs_line = qdev_get_gpio_in_named(spi_flash, SSI_GPIO_CS, 0); sysbus_connect_irq(SYS_BUS_DEVICE(&soc->spi[0]), 1, cs_line); - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + armv7m_load_kernel(soc->armv7m.cpu, machine->kernel_filename, 0, soc->envm_size); } static void emcraft_sf2_machine_init(MachineClass *mc) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m3"), + NULL + }; + mc->desc = "SmartFusion2 SOM kit from Emcraft (M2S010)"; mc->init = emcraft_sf2_s2s010_init; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); + mc->valid_cpu_types = valid_cpu_types; } DEFINE_MACHINE("emcraft-sf2", emcraft_sf2_machine_init) diff --git a/hw/arm/musca.c b/hw/arm/musca.c index 6eeee57c9dd..250b3b5bf84 100644 --- a/hw/arm/musca.c +++ b/hw/arm/musca.c @@ -22,8 +22,8 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "exec/address-spaces.h" -#include "sysemu/sysemu.h" +#include "system/address-spaces.h" +#include "system/system.h" #include "hw/arm/boot.h" #include "hw/arm/armsse.h" #include "hw/boards.h" @@ -355,7 +355,6 @@ static void musca_init(MachineState *machine) { MuscaMachineState *mms = MUSCA_MACHINE(machine); MuscaMachineClass *mmc = MUSCA_MACHINE_GET_CLASS(mms); - MachineClass *mc = MACHINE_GET_CLASS(machine); MemoryRegion *system_memory = get_system_memory(); DeviceState *ssedev; DeviceState *dev_splitter; @@ -366,12 +365,6 @@ static void musca_init(MachineState *machine) assert(mmc->num_irqs <= MUSCA_NUMIRQ_MAX); assert(mmc->num_mpcs <= MUSCA_MPC_MAX); - if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) { - error_report("This board can only be used with CPU %s", - mc->default_cpu_type); - exit(1); - } - mms->sysclk = clock_new(OBJECT(machine), "SYSCLK"); clock_set_hz(mms->sysclk, SYSCLK_FRQ); mms->s32kclk = clock_new(OBJECT(machine), "S32KCLK"); @@ -597,22 +590,26 @@ static void musca_init(MachineState *machine) "cfg_sec_resp", 0)); } - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + armv7m_load_kernel(mms->sse.armv7m[0].cpu, machine->kernel_filename, 0, 0x2000000); } -static void musca_class_init(ObjectClass *oc, void *data) +static void musca_class_init(ObjectClass *oc, const 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; } -static void musca_a_class_init(ObjectClass *oc, void *data) +static void musca_a_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc); @@ -626,7 +623,7 @@ static void musca_a_class_init(ObjectClass *oc, void *data) mmc->num_mpcs = ARRAY_SIZE(a_mpc_info); } -static void musca_b1_class_init(ObjectClass *oc, void *data) +static void musca_b1_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc); diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index dc4e43e0ee2..329b162eb20 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -12,14 +12,13 @@ #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 "system/system.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" @@ -30,15 +29,16 @@ #include "hw/irq.h" #include "hw/or-irq.h" #include "hw/audio/wm8750.h" -#include "sysemu/block-backend.h" -#include "sysemu/runstate.h" -#include "sysemu/dma.h" +#include "system/block-backend.h" +#include "system/runstate.h" +#include "system/dma.h" #include "ui/pixel_ops.h" #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 @@ -275,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), @@ -286,7 +286,7 @@ static const VMStateDescription musicpal_lcd_vmsd = { } }; -static void musicpal_lcd_class_init(ObjectClass *klass, void *data) +static void musicpal_lcd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -400,18 +400,18 @@ 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() } }; -static void mv88w8618_pic_class_init(ObjectClass *klass, void *data) +static void mv88w8618_pic_class_init(ObjectClass *klass, const 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; } @@ -583,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() @@ -594,18 +594,18 @@ 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() } }; -static void mv88w8618_pit_class_init(ObjectClass *klass, void *data) +static void mv88w8618_pit_class_init(ObjectClass *klass, const 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; } @@ -681,13 +681,13 @@ 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() } }; -static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data) +static void mv88w8618_flashcfg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1015,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,11 +1026,11 @@ static const VMStateDescription musicpal_gpio_vmsd = { } }; -static void musicpal_gpio_class_init(ObjectClass *klass, void *data) +static void musicpal_gpio_class_init(ObjectClass *klass, const 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; } @@ -1043,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) @@ -1074,67 +1060,66 @@ struct musicpal_key_state { SysBusDevice parent_obj; /*< public >*/ - 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; - if (s->kbd_extended) { - switch (keycode & KEY_CODE) { - case KEYCODE_UP: - event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV; - break; + case Q_KEY_CODE_DOWN: + event = MP_KEY_WHEEL_NAV; + break; - case KEYCODE_DOWN: - event = MP_KEY_WHEEL_NAV; - break; + case Q_KEY_CODE_LEFT: + event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV; + break; - case KEYCODE_LEFT: - event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV; - break; + case Q_KEY_CODE_RIGHT: + event = MP_KEY_WHEEL_VOL; + 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)) { + 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; + } + + /* + * 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); @@ -1143,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) @@ -1162,30 +1145,38 @@ static void musicpal_key_init(Object *obj) DeviceState *dev = DEVICE(sbd); musicpal_key_state *s = MUSICPAL_KEY(dev); - 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() } }; -static void musicpal_key_class_init(ObjectClass *klass, void *data) +static void musicpal_key_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &musicpal_key_vmsd; + dc->realize = musicpal_key_realize; } static const TypeInfo musicpal_key_info = { @@ -1247,7 +1238,7 @@ static void musicpal_init(MachineState *machine) qdev_get_gpio_in(pic, MP_TIMER4_IRQ), NULL); /* Logically OR both UART IRQs together */ - uart_orgate = DEVICE(object_new(TYPE_OR_IRQ)); + uart_orgate = qdev_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(uart_orgate, 0, @@ -1286,9 +1277,8 @@ static void musicpal_init(MachineState *machine) } 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); @@ -1326,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), @@ -1347,11 +1342,13 @@ 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) -static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data) +static void mv88w8618_wlan_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/netduino2.c b/hw/arm/netduino2.c index 83753d53a3f..df793c77fe1 100644 --- a/hw/arm/netduino2.c +++ b/hw/arm/netduino2.c @@ -44,18 +44,24 @@ static void netduino2_init(MachineState *machine) clock_set_hz(sysclk, SYSCLK_FRQ); dev = qdev_new(TYPE_STM32F205_SOC); - qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3")); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + armv7m_load_kernel(STM32F205_SOC(dev)->armv7m.cpu, machine->kernel_filename, 0, FLASH_SIZE); } static void netduino2_machine_init(MachineClass *mc) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m3"), + NULL + }; + mc->desc = "Netduino 2 Machine (Cortex-M3)"; mc->init = netduino2_init; + mc->valid_cpu_types = valid_cpu_types; mc->ignore_memory_transaction_failures = true; } diff --git a/hw/arm/netduinoplus2.c b/hw/arm/netduinoplus2.c index 515c0816054..81b6334cf72 100644 --- a/hw/arm/netduinoplus2.c +++ b/hw/arm/netduinoplus2.c @@ -44,19 +44,25 @@ static void netduinoplus2_init(MachineState *machine) clock_set_hz(sysclk, SYSCLK_FRQ); dev = qdev_new(TYPE_STM32F405_SOC); - qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4")); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - armv7m_load_kernel(ARM_CPU(first_cpu), + armv7m_load_kernel(STM32F405_SOC(dev)->armv7m.cpu, machine->kernel_filename, 0, FLASH_SIZE); } static void netduinoplus2_machine_init(MachineClass *mc) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m4"), + NULL + }; + mc->desc = "Netduino Plus 2 Machine (Cortex-M4)"; mc->init = netduinoplus2_init; + mc->valid_cpu_types = valid_cpu_types; } DEFINE_MACHINE("netduinoplus2", netduinoplus2_machine_init) diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index 15ff21d0472..2f30c49df55 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 "system/system.h" +#include "target/arm/cpu-qom.h" /* * This covers the whole MMIO space. We'll use this to catch any MMIO accesses @@ -83,8 +85,10 @@ 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, @@ -228,6 +232,12 @@ static const hwaddr npcm7xx_pspi_addr[] = { 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; @@ -282,17 +292,21 @@ static const struct { hwaddr regs_addr; int cs_count; const hwaddr *flash_addr; + size_t flash_size; } npcm7xx_fiu[] = { { .name = "fiu0", .regs_addr = 0xfb000000, .cs_count = ARRAY_SIZE(npcm7xx_fiu0_flash_addr), .flash_addr = npcm7xx_fiu0_flash_addr, + .flash_size = 128 * MiB, + }, { .name = "fiu3", .regs_addr = 0xc0000000, .cs_count = ARRAY_SIZE(npcm7xx_fiu3_flash_addr), .flash_addr = npcm7xx_fiu3_flash_addr, + .flash_size = 128 * MiB, }, }; @@ -377,7 +391,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)); } @@ -456,6 +470,10 @@ static void npcm7xx_init(Object *obj) 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); } @@ -473,9 +491,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, @@ -655,8 +670,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. @@ -664,12 +680,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 @@ -687,6 +704,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. @@ -698,6 +739,8 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) object_property_set_int(OBJECT(sbd), "cs-count", npcm7xx_fiu[i].cs_count, &error_abort); + object_property_set_int(OBJECT(sbd), "flash-size", + npcm7xx_fiu[i].flash_size, &error_abort); sysbus_realize(sbd, &error_abort); sysbus_mmio_map(sbd, 0, npcm7xx_fiu[i].regs_addr); @@ -749,8 +792,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) create_unimplemented_device("npcm7xx.siox[2]", 0xf0102000, 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); @@ -775,13 +816,12 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) create_unimplemented_device("npcm7xx.spix", 0xfb001000, 4 * KiB); } -static Property npcm7xx_properties[] = { +static const Property npcm7xx_properties[] = { DEFINE_PROP_LINK("dram-mr", NPCM7xxState, dram, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; -static void npcm7xx_class_init(ObjectClass *oc, void *data) +static void npcm7xx_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -790,7 +830,7 @@ static void npcm7xx_class_init(ObjectClass *oc, void *data) device_class_set_props(dc, npcm7xx_properties); } -static void npcm730_class_init(ObjectClass *oc, void *data) +static void npcm730_class_init(ObjectClass *oc, const void *data) { NPCM7xxClass *nc = NPCM7XX_CLASS(oc); @@ -799,7 +839,7 @@ static void npcm730_class_init(ObjectClass *oc, void *data) nc->num_cpus = 2; } -static void npcm750_class_init(ObjectClass *oc, void *data) +static void npcm750_class_init(ObjectClass *oc, const void *data) { NPCM7xxClass *nc = NPCM7XX_CLASS(oc); diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c index 2aef579aacc..465a0e5acec 100644 --- a/hw/arm/npcm7xx_boards.c +++ b/hw/arm/npcm7xx_boards.c @@ -27,9 +27,9 @@ #include "qapi/error.h" #include "qemu/datadir.h" #include "qemu/units.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" +#include "system/blockdev.h" +#include "system/system.h" +#include "system/block-backend.h" #include "qemu/error-report.h" @@ -121,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); @@ -460,22 +453,26 @@ static void npcm7xx_set_soc_type(NPCM7xxMachineClass *nmc, const char *type) mc->default_cpus = mc->min_cpus = mc->max_cpus = sc->num_cpus; } -static void npcm7xx_machine_class_init(ObjectClass *oc, void *data) +static void npcm7xx_machine_class_init(ObjectClass *oc, const 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; } /* * Schematics: * https://github.com/Nuvoton-Israel/nuvoton-info/blob/master/npcm7xx-poleg/evaluation-board/board_deliverables/NPCM750x_EB_ver.A1.1_COMPLETE.pdf */ -static void npcm750_evb_machine_class_init(ObjectClass *oc, void *data) +static void npcm750_evb_machine_class_init(ObjectClass *oc, const void *data) { NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); @@ -484,10 +481,11 @@ static void npcm750_evb_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Nuvoton NPCM750 Evaluation Board (Cortex-A9)"; mc->init = npcm750_evb_init; + mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; }; -static void gsj_machine_class_init(ObjectClass *oc, void *data) +static void gsj_machine_class_init(ObjectClass *oc, const void *data) { NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); @@ -496,10 +494,11 @@ static void gsj_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Quanta GSJ (Cortex-A9)"; mc->init = quanta_gsj_init; + mc->auto_create_sdcard = true; mc->default_ram_size = 512 * MiB; }; -static void gbs_bmc_machine_class_init(ObjectClass *oc, void *data) +static void gbs_bmc_machine_class_init(ObjectClass *oc, const void *data) { NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); @@ -508,10 +507,11 @@ static void gbs_bmc_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Quanta GBS (Cortex-A9)"; mc->init = quanta_gbs_init; + mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; } -static void kudo_bmc_machine_class_init(ObjectClass *oc, void *data) +static void kudo_bmc_machine_class_init(ObjectClass *oc, const void *data) { NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); @@ -520,10 +520,11 @@ static void kudo_bmc_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Kudo BMC (Cortex-A9)"; mc->init = kudo_bmc_init; + mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; }; -static void mori_bmc_machine_class_init(ObjectClass *oc, void *data) +static void mori_bmc_machine_class_init(ObjectClass *oc, const void *data) { NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); @@ -532,6 +533,7 @@ static void mori_bmc_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Mori BMC (Cortex-A9)"; mc->init = mori_bmc_init; + mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; } diff --git a/hw/arm/npcm8xx.c b/hw/arm/npcm8xx.c new file mode 100644 index 00000000000..a276fea6985 --- /dev/null +++ b/hw/arm/npcm8xx.c @@ -0,0 +1,859 @@ +/* + * Nuvoton NPCM8xx SoC family. + * + * Copyright 2022 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/boards.h" +#include "hw/arm/boot.h" +#include "hw/arm/bsa.h" +#include "hw/arm/npcm8xx.h" +#include "hw/char/serial-mm.h" +#include "hw/intc/arm_gic.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/units.h" +#include "system/system.h" + +/* + * This covers the whole MMIO space. We'll use this to catch any MMIO accesses + * that aren't handled by a device. + */ +#define NPCM8XX_MMIO_BA 0x80000000 +#define NPCM8XX_MMIO_SZ 0x7ffd0000 + +/* OTP fuse array */ +#define NPCM8XX_OTP_BA 0xf0189000 + +/* GIC Distributor */ +#define NPCM8XX_GICD_BA 0xdfff9000 +#define NPCM8XX_GICC_BA 0xdfffa000 + +/* Core system modules. */ +#define NPCM8XX_CPUP_BA 0xf03fe000 +#define NPCM8XX_GCR_BA 0xf0800000 +#define NPCM8XX_CLK_BA 0xf0801000 +#define NPCM8XX_MC_BA 0xf0824000 +#define NPCM8XX_RNG_BA 0xf000b000 + +/* ADC Module */ +#define NPCM8XX_ADC_BA 0xf000c000 + +/* Internal AHB SRAM */ +#define NPCM8XX_RAM3_BA 0xc0008000 +#define NPCM8XX_RAM3_SZ (4 * KiB) + +/* Memory blocks at the end of the address space */ +#define NPCM8XX_RAM2_BA 0xfffb0000 +#define NPCM8XX_RAM2_SZ (256 * KiB) +#define NPCM8XX_ROM_BA 0xffff0100 +#define NPCM8XX_ROM_SZ (64 * KiB) + +/* SDHCI Modules */ +#define NPCM8XX_MMC_BA 0xf0842000 + +/* PCS Module */ +#define NPCM8XX_PCS_BA 0xf0780000 + +/* PSPI Modules */ +#define NPCM8XX_PSPI_BA 0xf0201000 + +/* Run PLL1 at 1600 MHz */ +#define NPCM8XX_PLLCON1_FIXUP_VAL 0x00402101 +/* Run the CPU from PLL1 and UART from PLL2 */ +#define NPCM8XX_CLKSEL_FIXUP_VAL 0x004aaba9 + +/* Clock configuration values to be fixed up when bypassing bootloader */ + +/* + * Interrupt lines going into the GIC. This does not include internal Cortex-A35 + * interrupts. + */ +enum NPCM8xxInterrupt { + NPCM8XX_ADC_IRQ = 0, + NPCM8XX_PECI_IRQ = 6, + NPCM8XX_KCS_HIB_IRQ = 9, + NPCM8XX_GMAC1_IRQ = 14, + NPCM8XX_GMAC2_IRQ, + NPCM8XX_GMAC3_IRQ, + NPCM8XX_GMAC4_IRQ, + NPCM8XX_MMC_IRQ = 26, + NPCM8XX_PSPI_IRQ = 28, + NPCM8XX_TIMER0_IRQ = 32, /* Timer Module 0 */ + NPCM8XX_TIMER1_IRQ, + NPCM8XX_TIMER2_IRQ, + NPCM8XX_TIMER3_IRQ, + NPCM8XX_TIMER4_IRQ, + NPCM8XX_TIMER5_IRQ, /* Timer Module 1 */ + NPCM8XX_TIMER6_IRQ, + NPCM8XX_TIMER7_IRQ, + NPCM8XX_TIMER8_IRQ, + NPCM8XX_TIMER9_IRQ, + NPCM8XX_TIMER10_IRQ, /* Timer Module 2 */ + NPCM8XX_TIMER11_IRQ, + NPCM8XX_TIMER12_IRQ, + NPCM8XX_TIMER13_IRQ, + NPCM8XX_TIMER14_IRQ, + NPCM8XX_WDG0_IRQ = 47, /* Timer Module 0 Watchdog */ + NPCM8XX_WDG1_IRQ, /* Timer Module 1 Watchdog */ + NPCM8XX_WDG2_IRQ, /* Timer Module 2 Watchdog */ + NPCM8XX_EHCI1_IRQ = 61, + NPCM8XX_OHCI1_IRQ, + NPCM8XX_EHCI2_IRQ, + NPCM8XX_OHCI2_IRQ, + NPCM8XX_PWM0_IRQ = 93, /* PWM module 0 */ + NPCM8XX_PWM1_IRQ, /* PWM module 1 */ + NPCM8XX_MFT0_IRQ = 96, /* MFT module 0 */ + NPCM8XX_MFT1_IRQ, /* MFT module 1 */ + NPCM8XX_MFT2_IRQ, /* MFT module 2 */ + NPCM8XX_MFT3_IRQ, /* MFT module 3 */ + NPCM8XX_MFT4_IRQ, /* MFT module 4 */ + NPCM8XX_MFT5_IRQ, /* MFT module 5 */ + NPCM8XX_MFT6_IRQ, /* MFT module 6 */ + NPCM8XX_MFT7_IRQ, /* MFT module 7 */ + NPCM8XX_PCI_MBOX1_IRQ = 105, + NPCM8XX_PCI_MBOX2_IRQ, + NPCM8XX_GPIO0_IRQ = 116, + NPCM8XX_GPIO1_IRQ, + NPCM8XX_GPIO2_IRQ, + NPCM8XX_GPIO3_IRQ, + NPCM8XX_GPIO4_IRQ, + NPCM8XX_GPIO5_IRQ, + NPCM8XX_GPIO6_IRQ, + NPCM8XX_GPIO7_IRQ, + NPCM8XX_SMBUS0_IRQ = 128, + NPCM8XX_SMBUS1_IRQ, + NPCM8XX_SMBUS2_IRQ, + NPCM8XX_SMBUS3_IRQ, + NPCM8XX_SMBUS4_IRQ, + NPCM8XX_SMBUS5_IRQ, + NPCM8XX_SMBUS6_IRQ, + NPCM8XX_SMBUS7_IRQ, + NPCM8XX_SMBUS8_IRQ, + NPCM8XX_SMBUS9_IRQ, + NPCM8XX_SMBUS10_IRQ, + NPCM8XX_SMBUS11_IRQ, + NPCM8XX_SMBUS12_IRQ, + NPCM8XX_SMBUS13_IRQ, + NPCM8XX_SMBUS14_IRQ, + NPCM8XX_SMBUS15_IRQ, + NPCM8XX_SMBUS16_IRQ, + NPCM8XX_SMBUS17_IRQ, + NPCM8XX_SMBUS18_IRQ, + NPCM8XX_SMBUS19_IRQ, + NPCM8XX_SMBUS20_IRQ, + NPCM8XX_SMBUS21_IRQ, + NPCM8XX_SMBUS22_IRQ, + NPCM8XX_SMBUS23_IRQ, + NPCM8XX_SMBUS24_IRQ, + NPCM8XX_SMBUS25_IRQ, + NPCM8XX_SMBUS26_IRQ, + NPCM8XX_UART0_IRQ = 192, + NPCM8XX_UART1_IRQ, + NPCM8XX_UART2_IRQ, + NPCM8XX_UART3_IRQ, + NPCM8XX_UART4_IRQ, + NPCM8XX_UART5_IRQ, + NPCM8XX_UART6_IRQ, +}; + +/* Total number of GIC interrupts, including internal Cortex-A35 interrupts. */ +#define NPCM8XX_NUM_IRQ (288) +#define NPCM8XX_PPI_BASE(cpu) \ + ((NPCM8XX_NUM_IRQ - GIC_INTERNAL) + (cpu) * GIC_INTERNAL) + +/* Register base address for each Timer Module */ +static const hwaddr npcm8xx_tim_addr[] = { + 0xf0008000, + 0xf0009000, + 0xf000a000, +}; + +/* Register base address for each 16550 UART */ +static const hwaddr npcm8xx_uart_addr[] = { + 0xf0000000, + 0xf0001000, + 0xf0002000, + 0xf0003000, + 0xf0004000, + 0xf0005000, + 0xf0006000, +}; + +/* Direct memory-mapped access to SPI0 CS0-1. */ +static const hwaddr npcm8xx_fiu0_flash_addr[] = { + 0x80000000, /* CS0 */ + 0x88000000, /* CS1 */ +}; + +/* Direct memory-mapped access to SPI1 CS0-3. */ +static const hwaddr npcm8xx_fiu1_flash_addr[] = { + 0x90000000, /* CS0 */ + 0x91000000, /* CS1 */ + 0x92000000, /* CS2 */ + 0x93000000, /* CS3 */ +}; + +/* Direct memory-mapped access to SPI3 CS0-3. */ +static const hwaddr npcm8xx_fiu3_flash_addr[] = { + 0xa0000000, /* CS0 */ + 0xa8000000, /* CS1 */ + 0xb0000000, /* CS2 */ + 0xb8000000, /* CS3 */ +}; + +/* Register base address for each PWM Module */ +static const hwaddr npcm8xx_pwm_addr[] = { + 0xf0103000, + 0xf0104000, + 0xf0105000, +}; + +/* Register base address for each MFT Module */ +static const hwaddr npcm8xx_mft_addr[] = { + 0xf0180000, + 0xf0181000, + 0xf0182000, + 0xf0183000, + 0xf0184000, + 0xf0185000, + 0xf0186000, + 0xf0187000, +}; + +/* Direct memory-mapped access to each SMBus Module. */ +static const hwaddr npcm8xx_smbus_addr[] = { + 0xf0080000, + 0xf0081000, + 0xf0082000, + 0xf0083000, + 0xf0084000, + 0xf0085000, + 0xf0086000, + 0xf0087000, + 0xf0088000, + 0xf0089000, + 0xf008a000, + 0xf008b000, + 0xf008c000, + 0xf008d000, + 0xf008e000, + 0xf008f000, + 0xfff00000, + 0xfff01000, + 0xfff02000, + 0xfff03000, + 0xfff04000, + 0xfff05000, + 0xfff06000, + 0xfff07000, + 0xfff08000, + 0xfff09000, + 0xfff0a000, +}; + +/* Register base address for each GMAC Module */ +static const hwaddr npcm8xx_gmac_addr[] = { + 0xf0802000, + 0xf0804000, + 0xf0806000, + 0xf0808000, +}; + +/* Register base address for each USB host EHCI registers */ +static const hwaddr npcm8xx_ehci_addr[] = { + 0xf0828100, + 0xf082a100, +}; + +/* Register base address for each USB host OHCI registers */ +static const hwaddr npcm8xx_ohci_addr[] = { + 0xf0829000, + 0xf082b000, +}; + +static const struct { + hwaddr regs_addr; + uint32_t reset_pu; + uint32_t reset_pd; + uint32_t reset_osrc; + uint32_t reset_odsc; +} npcm8xx_gpio[] = { + { + .regs_addr = 0xf0010000, + .reset_pu = 0x00000300, + .reset_pd = 0x000f0000, + }, { + .regs_addr = 0xf0011000, + .reset_pu = 0xe0fefe01, + .reset_pd = 0x07000000, + }, { + .regs_addr = 0xf0012000, + .reset_pu = 0xc00fffff, + .reset_pd = 0x3ff00000, + }, { + .regs_addr = 0xf0013000, + .reset_pd = 0x00003000, + }, { + .regs_addr = 0xf0014000, + .reset_pu = 0xffff0000, + }, { + .regs_addr = 0xf0015000, + .reset_pu = 0xff8387fe, + .reset_pd = 0x007c0001, + .reset_osrc = 0x08000000, + }, { + .regs_addr = 0xf0016000, + .reset_pu = 0x00000801, + .reset_pd = 0x00000302, + }, { + .regs_addr = 0xf0017000, + .reset_pu = 0x000002ff, + .reset_pd = 0x00000c00, + }, +}; + +static const struct { + const char *name; + hwaddr regs_addr; + int cs_count; + const hwaddr *flash_addr; + size_t flash_size; +} npcm8xx_fiu[] = { + { + .name = "fiu0", + .regs_addr = 0xfb000000, + .cs_count = ARRAY_SIZE(npcm8xx_fiu0_flash_addr), + .flash_addr = npcm8xx_fiu0_flash_addr, + .flash_size = 128 * MiB, + }, + { + .name = "fiu1", + .regs_addr = 0xfb002000, + .cs_count = ARRAY_SIZE(npcm8xx_fiu1_flash_addr), + .flash_addr = npcm8xx_fiu1_flash_addr, + .flash_size = 16 * MiB, + }, { + .name = "fiu3", + .regs_addr = 0xc0000000, + .cs_count = ARRAY_SIZE(npcm8xx_fiu3_flash_addr), + .flash_addr = npcm8xx_fiu3_flash_addr, + .flash_size = 128 * MiB, + }, +}; + +static struct arm_boot_info npcm8xx_binfo = { + .loader_start = NPCM8XX_LOADER_START, + .smp_loader_start = NPCM8XX_SMP_LOADER_START, + .smp_bootreg_addr = NPCM8XX_SMP_BOOTREG_ADDR, + .gic_cpu_if_addr = NPCM8XX_GICC_BA, + .secure_boot = false, + .board_id = -1, + .board_setup_addr = NPCM8XX_BOARD_SETUP_ADDR, + .psci_conduit = QEMU_PSCI_CONDUIT_SMC, +}; + +void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc) +{ + npcm8xx_binfo.ram_size = machine->ram_size; + + arm_load_kernel(&soc->cpu[0], machine, &npcm8xx_binfo); +} + +static void npcm8xx_init_fuses(NPCM8xxState *s) +{ + NPCM8xxClass *nc = NPCM8XX_GET_CLASS(s); + uint32_t value; + + /* + * The initial mask of disabled modules indicates the chip derivative (e.g. + * NPCM750 or NPCM730). + */ + value = cpu_to_le32(nc->disabled_modules); + npcm7xx_otp_array_write(&s->fuse_array, &value, NPCM7XX_FUSE_DERIVATIVE, + sizeof(value)); +} + +static void npcm8xx_write_adc_calibration(NPCM8xxState *s) +{ + /* Both ADC and the fuse array must have realized. */ + QEMU_BUILD_BUG_ON(sizeof(s->adc.calibration_r_values) != 4); + npcm7xx_otp_array_write(&s->fuse_array, s->adc.calibration_r_values, + NPCM7XX_FUSE_ADC_CALIB, sizeof(s->adc.calibration_r_values)); +} + +static qemu_irq npcm8xx_irq(NPCM8xxState *s, int n) +{ + return qdev_get_gpio_in(DEVICE(&s->gic), n); +} + +static void npcm8xx_init(Object *obj) +{ + NPCM8xxState *s = NPCM8XX(obj); + int i; + + object_initialize_child(obj, "cpu-cluster", &s->cpu_cluster, + TYPE_CPU_CLUSTER); + for (i = 0; i < NPCM8XX_MAX_NUM_CPUS; i++) { + object_initialize_child(OBJECT(&s->cpu_cluster), "cpu[*]", &s->cpu[i], + ARM_CPU_TYPE_NAME("cortex-a35")); + } + object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GIC); + object_initialize_child(obj, "gcr", &s->gcr, TYPE_NPCM8XX_GCR); + object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr), + "power-on-straps"); + object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM8XX_CLK); + object_initialize_child(obj, "otp", &s->fuse_array, + TYPE_NPCM7XX_FUSE_ARRAY); + object_initialize_child(obj, "mc", &s->mc, TYPE_NPCM7XX_MC); + object_initialize_child(obj, "rng", &s->rng, TYPE_NPCM7XX_RNG); + object_initialize_child(obj, "adc", &s->adc, TYPE_NPCM7XX_ADC); + + for (i = 0; i < ARRAY_SIZE(s->tim); i++) { + object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER); + } + + for (i = 0; i < ARRAY_SIZE(s->gpio); i++) { + object_initialize_child(obj, "gpio[*]", &s->gpio[i], TYPE_NPCM7XX_GPIO); + } + + + for (i = 0; i < ARRAY_SIZE(s->smbus); i++) { + object_initialize_child(obj, "smbus[*]", &s->smbus[i], + TYPE_NPCM7XX_SMBUS); + DEVICE(&s->smbus[i])->id = g_strdup_printf("smbus[%d]", i); + } + + for (i = 0; i < ARRAY_SIZE(s->ehci); i++) { + object_initialize_child(obj, "ehci[*]", &s->ehci[i], TYPE_NPCM7XX_EHCI); + } + for (i = 0; i < ARRAY_SIZE(s->ohci); i++) { + object_initialize_child(obj, "ohci[*]", &s->ohci[i], TYPE_SYSBUS_OHCI); + } + + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_fiu) != ARRAY_SIZE(s->fiu)); + for (i = 0; i < ARRAY_SIZE(s->fiu); i++) { + object_initialize_child(obj, npcm8xx_fiu[i].name, &s->fiu[i], + TYPE_NPCM7XX_FIU); + } + + for (i = 0; i < ARRAY_SIZE(s->pwm); i++) { + object_initialize_child(obj, "pwm[*]", &s->pwm[i], TYPE_NPCM7XX_PWM); + } + + for (i = 0; i < ARRAY_SIZE(s->mft); i++) { + object_initialize_child(obj, "mft[*]", &s->mft[i], TYPE_NPCM7XX_MFT); + } + + for (i = 0; i < ARRAY_SIZE(s->gmac); i++) { + object_initialize_child(obj, "gmac[*]", &s->gmac[i], TYPE_NPCM_GMAC); + } + object_initialize_child(obj, "pcs", &s->pcs, TYPE_NPCM_PCS); + + object_initialize_child(obj, "mmc", &s->mmc, TYPE_NPCM7XX_SDHCI); + object_initialize_child(obj, "pspi", &s->pspi, TYPE_NPCM_PSPI); +} + +static void npcm8xx_realize(DeviceState *dev, Error **errp) +{ + NPCM8xxState *s = NPCM8XX(dev); + NPCM8xxClass *nc = NPCM8XX_GET_CLASS(s); + int i; + + if (memory_region_size(s->dram) > NPCM8XX_DRAM_SZ) { + error_setg(errp, "%s: NPCM8xx cannot address more than %" PRIu64 + " MiB of DRAM", __func__, NPCM8XX_DRAM_SZ / MiB); + return; + } + + /* CPUs */ + for (i = 0; i < nc->num_cpus; i++) { + object_property_set_int(OBJECT(&s->cpu[i]), "mp-affinity", + arm_build_mp_affinity(i, NPCM8XX_MAX_NUM_CPUS), + &error_abort); + object_property_set_bool(OBJECT(&s->cpu[i]), "reset-hivecs", true, + &error_abort); + object_property_set_int(OBJECT(&s->cpu[i]), "core-count", + nc->num_cpus, &error_abort); + + /* Disable security extensions. */ + object_property_set_bool(OBJECT(&s->cpu[i]), "has_el3", false, + &error_abort); + + if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { + return; + } + } + + /* ARM GIC for Cortex A35. Can only fail if we pass bad parameters here. */ + object_property_set_uint(OBJECT(&s->gic), "num-cpu", nc->num_cpus, errp); + object_property_set_uint(OBJECT(&s->gic), "num-irq", NPCM8XX_NUM_IRQ, errp); + object_property_set_uint(OBJECT(&s->gic), "revision", 2, errp); + object_property_set_bool(OBJECT(&s->gic), "has-security-extensions", true, + errp); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gic), errp)) { + return; + } + for (i = 0; i < nc->num_cpus; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i, + qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + nc->num_cpus, + qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + nc->num_cpus * 2, + qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_VIRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + nc->num_cpus * 3, + qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_VFIQ)); + + qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_PHYS, + qdev_get_gpio_in(DEVICE(&s->gic), + NPCM8XX_PPI_BASE(i) + ARCH_TIMER_NS_EL1_IRQ)); + qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_VIRT, + qdev_get_gpio_in(DEVICE(&s->gic), + NPCM8XX_PPI_BASE(i) + ARCH_TIMER_VIRT_IRQ)); + qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_HYP, + qdev_get_gpio_in(DEVICE(&s->gic), + NPCM8XX_PPI_BASE(i) + ARCH_TIMER_NS_EL2_IRQ)); + qdev_connect_gpio_out(DEVICE(&s->cpu[i]), GTIMER_SEC, + qdev_get_gpio_in(DEVICE(&s->gic), + NPCM8XX_PPI_BASE(i) + ARCH_TIMER_S_EL1_IRQ)); + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0, NPCM8XX_GICD_BA); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1, NPCM8XX_GICC_BA); + + /* CPU cluster */ + qdev_prop_set_uint32(DEVICE(&s->cpu_cluster), "cluster-id", 0); + qdev_realize(DEVICE(&s->cpu_cluster), NULL, &error_fatal); + + /* System Global Control Registers (GCR). Can fail due to user input. */ + object_property_set_int(OBJECT(&s->gcr), "disabled-modules", + nc->disabled_modules, &error_abort); + object_property_add_const_link(OBJECT(&s->gcr), "dram-mr", OBJECT(s->dram)); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gcr), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gcr), 0, NPCM8XX_GCR_BA); + + /* Clock Control Registers (CLK). Cannot fail. */ + sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, NPCM8XX_CLK_BA); + + /* OTP fuse strap array. Cannot fail. */ + sysbus_realize(SYS_BUS_DEVICE(&s->fuse_array), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->fuse_array), 0, NPCM8XX_OTP_BA); + npcm8xx_init_fuses(s); + + /* Fake Memory Controller (MC). Cannot fail. */ + sysbus_realize(SYS_BUS_DEVICE(&s->mc), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->mc), 0, NPCM8XX_MC_BA); + + /* ADC Modules. Cannot fail. */ + qdev_connect_clock_in(DEVICE(&s->adc), "clock", qdev_get_clock_out( + DEVICE(&s->clk), "adc-clock")); + sysbus_realize(SYS_BUS_DEVICE(&s->adc), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->adc), 0, NPCM8XX_ADC_BA); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, + npcm8xx_irq(s, NPCM8XX_ADC_IRQ)); + npcm8xx_write_adc_calibration(s); + + /* Timer Modules (TIM). Cannot fail. */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_tim_addr) != ARRAY_SIZE(s->tim)); + for (i = 0; i < ARRAY_SIZE(s->tim); i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->tim[i]); + int first_irq; + int j; + + /* Connect the timer clock. */ + qdev_connect_clock_in(DEVICE(&s->tim[i]), "clock", qdev_get_clock_out( + DEVICE(&s->clk), "timer-clock")); + + sysbus_realize(sbd, &error_abort); + sysbus_mmio_map(sbd, 0, npcm8xx_tim_addr[i]); + + first_irq = NPCM8XX_TIMER0_IRQ + i * NPCM7XX_TIMERS_PER_CTRL; + for (j = 0; j < NPCM7XX_TIMERS_PER_CTRL; j++) { + qemu_irq irq = npcm8xx_irq(s, first_irq + j); + sysbus_connect_irq(sbd, j, irq); + } + + /* IRQ for watchdogs */ + sysbus_connect_irq(sbd, NPCM7XX_TIMERS_PER_CTRL, + npcm8xx_irq(s, NPCM8XX_WDG0_IRQ + i)); + /* GPIO that connects clk module with watchdog */ + qdev_connect_gpio_out_named(DEVICE(&s->tim[i]), + NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 0, + qdev_get_gpio_in_named(DEVICE(&s->clk), + NPCM7XX_WATCHDOG_RESET_GPIO_IN, i)); + } + + /* UART0..6 (16550 compatible) */ + for (i = 0; i < ARRAY_SIZE(npcm8xx_uart_addr); i++) { + serial_mm_init(get_system_memory(), npcm8xx_uart_addr[i], 2, + npcm8xx_irq(s, NPCM8XX_UART0_IRQ + i), 115200, + serial_hd(i), DEVICE_LITTLE_ENDIAN); + } + + /* Random Number Generator. Cannot fail. */ + sysbus_realize(SYS_BUS_DEVICE(&s->rng), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rng), 0, NPCM8XX_RNG_BA); + + /* GPIO modules. Cannot fail. */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_gpio) != ARRAY_SIZE(s->gpio)); + for (i = 0; i < ARRAY_SIZE(s->gpio); i++) { + Object *obj = OBJECT(&s->gpio[i]); + + object_property_set_uint(obj, "reset-pullup", + npcm8xx_gpio[i].reset_pu, &error_abort); + object_property_set_uint(obj, "reset-pulldown", + npcm8xx_gpio[i].reset_pd, &error_abort); + object_property_set_uint(obj, "reset-osrc", + npcm8xx_gpio[i].reset_osrc, &error_abort); + object_property_set_uint(obj, "reset-odsc", + npcm8xx_gpio[i].reset_odsc, &error_abort); + sysbus_realize(SYS_BUS_DEVICE(obj), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(obj), 0, npcm8xx_gpio[i].regs_addr); + sysbus_connect_irq(SYS_BUS_DEVICE(obj), 0, + npcm8xx_irq(s, NPCM8XX_GPIO0_IRQ + i)); + } + + /* SMBus modules. Cannot fail. */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_smbus_addr) != ARRAY_SIZE(s->smbus)); + for (i = 0; i < ARRAY_SIZE(s->smbus); i++) { + Object *obj = OBJECT(&s->smbus[i]); + + sysbus_realize(SYS_BUS_DEVICE(obj), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(obj), 0, npcm8xx_smbus_addr[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(obj), 0, + npcm8xx_irq(s, NPCM8XX_SMBUS0_IRQ + i)); + } + + /* USB Host */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->ohci) != ARRAY_SIZE(s->ehci)); + for (i = 0; i < ARRAY_SIZE(s->ehci); i++) { + object_property_set_bool(OBJECT(&s->ehci[i]), "companion-enable", true, + &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ehci[i]), 0, npcm8xx_ehci_addr[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, + npcm8xx_irq(s, NPCM8XX_EHCI1_IRQ + 2 * i)); + } + for (i = 0; i < ARRAY_SIZE(s->ohci); i++) { + object_property_set_str(OBJECT(&s->ohci[i]), "masterbus", "usb-bus.0", + &error_abort); + object_property_set_uint(OBJECT(&s->ohci[i]), "num-ports", 1, + &error_abort); + object_property_set_uint(OBJECT(&s->ohci[i]), "firstport", i, + &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&s->ohci[i]), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ohci[i]), 0, npcm8xx_ohci_addr[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ohci[i]), 0, + npcm8xx_irq(s, NPCM8XX_OHCI1_IRQ + 2 * i)); + } + + /* PWM Modules. Cannot fail. */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_pwm_addr) != ARRAY_SIZE(s->pwm)); + for (i = 0; i < ARRAY_SIZE(s->pwm); i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->pwm[i]); + + qdev_connect_clock_in(DEVICE(&s->pwm[i]), "clock", qdev_get_clock_out( + DEVICE(&s->clk), "apb3-clock")); + sysbus_realize(sbd, &error_abort); + sysbus_mmio_map(sbd, 0, npcm8xx_pwm_addr[i]); + sysbus_connect_irq(sbd, i, npcm8xx_irq(s, NPCM8XX_PWM0_IRQ + i)); + } + + /* MFT Modules. Cannot fail. */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_mft_addr) != ARRAY_SIZE(s->mft)); + for (i = 0; i < ARRAY_SIZE(s->mft); i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->mft[i]); + + qdev_connect_clock_in(DEVICE(&s->mft[i]), "clock-in", + qdev_get_clock_out(DEVICE(&s->clk), + "apb4-clock")); + sysbus_realize(sbd, &error_abort); + sysbus_mmio_map(sbd, 0, npcm8xx_mft_addr[i]); + sysbus_connect_irq(sbd, 0, npcm8xx_irq(s, NPCM8XX_MFT0_IRQ + i)); + } + + /* + * GMAC Modules. Cannot fail. + */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_gmac_addr) != ARRAY_SIZE(s->gmac)); + for (i = 0; i < ARRAY_SIZE(s->gmac); i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->gmac[i]); + + /* This is used to make sure that the NIC can create the device */ + 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, npcm8xx_gmac_addr[i]); + /* + * 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, npcm8xx_irq(s, NPCM8XX_GMAC1_IRQ + i)); + } + /* + * GMAC Physical Coding Sublayer(PCS) Module. Cannot fail. + */ + sysbus_realize(SYS_BUS_DEVICE(&s->pcs), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcs), 0, NPCM8XX_PCS_BA); + + /* + * Flash Interface Unit (FIU). Can fail if incorrect number of chip selects + * specified, but this is a programming error. + */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_fiu) != ARRAY_SIZE(s->fiu)); + for (i = 0; i < ARRAY_SIZE(s->fiu); i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->fiu[i]); + int j; + + object_property_set_int(OBJECT(sbd), "cs-count", + npcm8xx_fiu[i].cs_count, &error_abort); + object_property_set_int(OBJECT(sbd), "flash-size", + npcm8xx_fiu[i].flash_size, &error_abort); + sysbus_realize(sbd, &error_abort); + + sysbus_mmio_map(sbd, 0, npcm8xx_fiu[i].regs_addr); + for (j = 0; j < npcm8xx_fiu[i].cs_count; j++) { + sysbus_mmio_map(sbd, j + 1, npcm8xx_fiu[i].flash_addr[j]); + } + } + + /* RAM2 (SRAM) */ + memory_region_init_ram(&s->sram, OBJECT(dev), "ram2", + NPCM8XX_RAM2_SZ, &error_abort); + memory_region_add_subregion(get_system_memory(), NPCM8XX_RAM2_BA, &s->sram); + + /* RAM3 (SRAM) */ + memory_region_init_ram(&s->ram3, OBJECT(dev), "ram3", + NPCM8XX_RAM3_SZ, &error_abort); + memory_region_add_subregion(get_system_memory(), NPCM8XX_RAM3_BA, &s->ram3); + + /* Internal ROM */ + memory_region_init_rom(&s->irom, OBJECT(dev), "irom", NPCM8XX_ROM_SZ, + &error_abort); + memory_region_add_subregion(get_system_memory(), NPCM8XX_ROM_BA, &s->irom); + + /* SDHCI */ + sysbus_realize(SYS_BUS_DEVICE(&s->mmc), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc), 0, NPCM8XX_MMC_BA); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc), 0, + npcm8xx_irq(s, NPCM8XX_MMC_IRQ)); + + /* PSPI */ + sysbus_realize(SYS_BUS_DEVICE(&s->pspi), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pspi), 0, NPCM8XX_PSPI_BA); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pspi), 0, + npcm8xx_irq(s, NPCM8XX_PSPI_IRQ)); + + create_unimplemented_device("npcm8xx.shm", 0xc0001000, 4 * KiB); + create_unimplemented_device("npcm8xx.gicextra", 0xdfffa000, 24 * KiB); + create_unimplemented_device("npcm8xx.vdmx", 0xe0800000, 4 * KiB); + create_unimplemented_device("npcm8xx.pcierc", 0xe1000000, 64 * KiB); + create_unimplemented_device("npcm8xx.rootc", 0xe8000000, 128 * MiB); + create_unimplemented_device("npcm8xx.kcs", 0xf0007000, 4 * KiB); + create_unimplemented_device("npcm8xx.gfxi", 0xf000e000, 4 * KiB); + create_unimplemented_device("npcm8xx.fsw", 0xf000f000, 4 * KiB); + create_unimplemented_device("npcm8xx.bt", 0xf0030000, 4 * KiB); + create_unimplemented_device("npcm8xx.espi", 0xf009f000, 4 * KiB); + create_unimplemented_device("npcm8xx.peci", 0xf0100000, 4 * KiB); + create_unimplemented_device("npcm8xx.siox[1]", 0xf0101000, 4 * KiB); + create_unimplemented_device("npcm8xx.siox[2]", 0xf0102000, 4 * KiB); + create_unimplemented_device("npcm8xx.tmps", 0xf0188000, 4 * KiB); + create_unimplemented_device("npcm8xx.viru1", 0xf0204000, 4 * KiB); + create_unimplemented_device("npcm8xx.viru2", 0xf0205000, 4 * KiB); + create_unimplemented_device("npcm8xx.jtm1", 0xf0208000, 4 * KiB); + create_unimplemented_device("npcm8xx.jtm2", 0xf0209000, 4 * KiB); + create_unimplemented_device("npcm8xx.flm0", 0xf0210000, 4 * KiB); + create_unimplemented_device("npcm8xx.flm1", 0xf0211000, 4 * KiB); + create_unimplemented_device("npcm8xx.flm2", 0xf0212000, 4 * KiB); + create_unimplemented_device("npcm8xx.flm3", 0xf0213000, 4 * KiB); + create_unimplemented_device("npcm8xx.ahbpci", 0xf0400000, 1 * MiB); + create_unimplemented_device("npcm8xx.dap", 0xf0500000, 960 * KiB); + create_unimplemented_device("npcm8xx.mcphy", 0xf05f0000, 64 * KiB); + create_unimplemented_device("npcm8xx.tsgen", 0xf07fc000, 8 * KiB); + create_unimplemented_device("npcm8xx.copctl", 0xf080c000, 4 * KiB); + create_unimplemented_device("npcm8xx.tipctl", 0xf080d000, 4 * KiB); + create_unimplemented_device("npcm8xx.rst", 0xf080e000, 4 * KiB); + create_unimplemented_device("npcm8xx.vcd", 0xf0810000, 64 * KiB); + create_unimplemented_device("npcm8xx.ece", 0xf0820000, 8 * KiB); + create_unimplemented_device("npcm8xx.vdma", 0xf0822000, 8 * KiB); + create_unimplemented_device("npcm8xx.usbd[0]", 0xf0830000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[1]", 0xf0831000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[2]", 0xf0832000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[3]", 0xf0833000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[4]", 0xf0834000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[5]", 0xf0835000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[6]", 0xf0836000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[7]", 0xf0837000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[8]", 0xf0838000, 4 * KiB); + create_unimplemented_device("npcm8xx.usbd[9]", 0xf0839000, 4 * KiB); + create_unimplemented_device("npcm8xx.pci_mbox1", 0xf0848000, 64 * KiB); + create_unimplemented_device("npcm8xx.gdma0", 0xf0850000, 4 * KiB); + create_unimplemented_device("npcm8xx.gdma1", 0xf0851000, 4 * KiB); + create_unimplemented_device("npcm8xx.gdma2", 0xf0852000, 4 * KiB); + create_unimplemented_device("npcm8xx.aes", 0xf0858000, 4 * KiB); + create_unimplemented_device("npcm8xx.des", 0xf0859000, 4 * KiB); + create_unimplemented_device("npcm8xx.sha", 0xf085a000, 4 * KiB); + create_unimplemented_device("npcm8xx.pci_mbox2", 0xf0868000, 64 * KiB); + create_unimplemented_device("npcm8xx.i3c0", 0xfff10000, 4 * KiB); + create_unimplemented_device("npcm8xx.i3c1", 0xfff11000, 4 * KiB); + create_unimplemented_device("npcm8xx.i3c2", 0xfff12000, 4 * KiB); + create_unimplemented_device("npcm8xx.i3c3", 0xfff13000, 4 * KiB); + create_unimplemented_device("npcm8xx.i3c4", 0xfff14000, 4 * KiB); + create_unimplemented_device("npcm8xx.i3c5", 0xfff15000, 4 * KiB); + create_unimplemented_device("npcm8xx.spixcs0", 0xf8000000, 16 * MiB); + create_unimplemented_device("npcm8xx.spixcs1", 0xf9000000, 16 * MiB); + create_unimplemented_device("npcm8xx.spix", 0xfb001000, 4 * KiB); + create_unimplemented_device("npcm8xx.vect", 0xffff0000, 256); +} + +static const Property npcm8xx_properties[] = { + DEFINE_PROP_LINK("dram-mr", NPCM8xxState, dram, TYPE_MEMORY_REGION, + MemoryRegion *), +}; + +static void npcm8xx_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + NPCM8xxClass *nc = NPCM8XX_CLASS(oc); + + dc->realize = npcm8xx_realize; + dc->user_creatable = false; + nc->disabled_modules = 0x00000000; + nc->num_cpus = NPCM8XX_MAX_NUM_CPUS; + device_class_set_props(dc, npcm8xx_properties); +} + +static const TypeInfo npcm8xx_soc_types[] = { + { + .name = TYPE_NPCM8XX, + .parent = TYPE_DEVICE, + .instance_size = sizeof(NPCM8xxState), + .instance_init = npcm8xx_init, + .class_size = sizeof(NPCM8xxClass), + .class_init = npcm8xx_class_init, + }, +}; + +DEFINE_TYPES(npcm8xx_soc_types); diff --git a/hw/arm/npcm8xx_boards.c b/hw/arm/npcm8xx_boards.c new file mode 100644 index 00000000000..3bf3e1f8f16 --- /dev/null +++ b/hw/arm/npcm8xx_boards.c @@ -0,0 +1,254 @@ +/* + * Machine definitions for boards featuring an NPCM8xx SoC. + * + * Copyright 2021 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 "chardev/char.h" +#include "hw/boards.h" +#include "hw/arm/npcm8xx.h" +#include "hw/core/cpu.h" +#include "hw/loader.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/datadir.h" +#include "qemu/units.h" + +#define NPCM845_EVB_POWER_ON_STRAPS 0x000017ff + +static const char npcm8xx_default_bootrom[] = "npcm8xx_bootrom.bin"; + +static void npcm8xx_load_bootrom(MachineState *machine, NPCM8xxState *soc) +{ + const char *bios_name = machine->firmware ?: npcm8xx_default_bootrom; + g_autofree char *filename = NULL; + int ret; + + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!filename) { + error_report("Could not find ROM image '%s'", bios_name); + if (!machine->kernel_filename) { + /* We can't boot without a bootrom or a kernel image. */ + exit(1); + } + return; + } + ret = load_image_mr(filename, machine->ram); + if (ret < 0) { + error_report("Failed to load ROM image '%s'", filename); + exit(1); + } +} + +static void npcm8xx_connect_flash(NPCM7xxFIUState *fiu, int cs_no, + const char *flash_type, DriveInfo *dinfo) +{ + DeviceState *flash; + qemu_irq flash_cs; + + flash = qdev_new(flash_type); + if (dinfo) { + qdev_prop_set_drive(flash, "drive", blk_by_legacy_dinfo(dinfo)); + } + qdev_realize_and_unref(flash, BUS(fiu->spi), &error_fatal); + + flash_cs = qdev_get_gpio_in_named(flash, SSI_GPIO_CS, 0); + qdev_connect_gpio_out_named(DEVICE(fiu), "cs", cs_no, flash_cs); +} + +static void npcm8xx_connect_dram(NPCM8xxState *soc, MemoryRegion *dram) +{ + memory_region_add_subregion(get_system_memory(), NPCM8XX_DRAM_BA, dram); + + object_property_set_link(OBJECT(soc), "dram-mr", OBJECT(dram), + &error_abort); +} + +static NPCM8xxState *npcm8xx_create_soc(MachineState *machine, + uint32_t hw_straps) +{ + NPCM8xxMachineClass *nmc = NPCM8XX_MACHINE_GET_CLASS(machine); + Object *obj; + + 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); + + return NPCM8XX(obj); +} + +static I2CBus *npcm8xx_i2c_get_bus(NPCM8xxState *soc, uint32_t num) +{ + g_assert(num < ARRAY_SIZE(soc->smbus)); + return I2C_BUS(qdev_get_child_bus(DEVICE(&soc->smbus[num]), "i2c-bus")); +} + +static void npcm8xx_init_pwm_splitter(NPCM8xxMachine *machine, + NPCM8xxState *soc, const int *fan_counts) +{ + SplitIRQ *splitters = machine->fan_splitter; + + /* + * PWM 0~3 belong to module 0 output 0~3. + * PWM 4~7 belong to module 1 output 0~3. + */ + for (int i = 0; i < NPCM8XX_NR_PWM_MODULES; ++i) { + for (int j = 0; j < NPCM7XX_PWM_PER_MODULE; ++j) { + int splitter_no = i * NPCM7XX_PWM_PER_MODULE + j; + DeviceState *splitter; + + if (fan_counts[splitter_no] < 1) { + continue; + } + object_initialize_child(OBJECT(machine), "fan-splitter[*]", + &splitters[splitter_no], TYPE_SPLIT_IRQ); + splitter = DEVICE(&splitters[splitter_no]); + qdev_prop_set_uint16(splitter, "num-lines", + fan_counts[splitter_no]); + qdev_realize(splitter, NULL, &error_abort); + qdev_connect_gpio_out_named(DEVICE(&soc->pwm[i]), "duty-gpio-out", + j, qdev_get_gpio_in(splitter, 0)); + } + } +} + +static void npcm8xx_connect_pwm_fan(NPCM8xxState *soc, SplitIRQ *splitter, + int fan_no, int output_no) +{ + DeviceState *fan; + int fan_input; + qemu_irq fan_duty_gpio; + + g_assert(fan_no >= 0 && fan_no <= NPCM7XX_MFT_MAX_FAN_INPUT); + /* + * Fan 0~1 belong to module 0 input 0~1. + * Fan 2~3 belong to module 1 input 0~1. + * ... + * Fan 14~15 belong to module 7 input 0~1. + * Fan 16~17 belong to module 0 input 2~3. + * Fan 18~19 belong to module 1 input 2~3. + */ + if (fan_no < 16) { + fan = DEVICE(&soc->mft[fan_no / 2]); + fan_input = fan_no % 2; + } else { + fan = DEVICE(&soc->mft[(fan_no - 16) / 2]); + fan_input = fan_no % 2 + 2; + } + + /* Connect the Fan to PWM module */ + fan_duty_gpio = qdev_get_gpio_in_named(fan, "duty", fan_input); + qdev_connect_gpio_out(DEVICE(splitter), output_no, fan_duty_gpio); +} + +static void npcm845_evb_i2c_init(NPCM8xxState *soc) +{ + /* tmp100 temperature sensor on SVB, tmp105 is compatible */ + i2c_slave_create_simple(npcm8xx_i2c_get_bus(soc, 6), "tmp105", 0x48); +} + +static void npcm845_evb_fan_init(NPCM8xxMachine *machine, NPCM8xxState *soc) +{ + SplitIRQ *splitter = machine->fan_splitter; + static const int fan_counts[] = {2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0}; + + npcm8xx_init_pwm_splitter(machine, soc, fan_counts); + npcm8xx_connect_pwm_fan(soc, &splitter[0], 0x00, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[0], 0x01, 1); + npcm8xx_connect_pwm_fan(soc, &splitter[1], 0x02, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[1], 0x03, 1); + npcm8xx_connect_pwm_fan(soc, &splitter[2], 0x04, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[2], 0x05, 1); + npcm8xx_connect_pwm_fan(soc, &splitter[3], 0x06, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[3], 0x07, 1); + npcm8xx_connect_pwm_fan(soc, &splitter[4], 0x08, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[4], 0x09, 1); + npcm8xx_connect_pwm_fan(soc, &splitter[5], 0x0a, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[5], 0x0b, 1); + npcm8xx_connect_pwm_fan(soc, &splitter[6], 0x0c, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[6], 0x0d, 1); + npcm8xx_connect_pwm_fan(soc, &splitter[7], 0x0e, 0); + npcm8xx_connect_pwm_fan(soc, &splitter[7], 0x0f, 1); +} + +static void npcm845_evb_init(MachineState *machine) +{ + NPCM8xxState *soc; + + soc = npcm8xx_create_soc(machine, NPCM845_EVB_POWER_ON_STRAPS); + npcm8xx_connect_dram(soc, machine->ram); + qdev_realize(DEVICE(soc), NULL, &error_fatal); + + npcm8xx_load_bootrom(machine, soc); + npcm8xx_connect_flash(&soc->fiu[0], 0, "w25q256", drive_get(IF_MTD, 0, 0)); + npcm845_evb_i2c_init(soc); + npcm845_evb_fan_init(NPCM8XX_MACHINE(machine), soc); + npcm8xx_load_kernel(machine, soc); +} + +static void npcm8xx_set_soc_type(NPCM8xxMachineClass *nmc, const char *type) +{ + NPCM8xxClass *sc = NPCM8XX_CLASS(object_class_by_name(type)); + MachineClass *mc = MACHINE_CLASS(nmc); + + nmc->soc_type = type; + mc->default_cpus = mc->min_cpus = mc->max_cpus = sc->num_cpus; +} + +static void npcm8xx_machine_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a35"), + NULL + }; + + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->no_parallel = 1; + mc->default_ram_id = "ram"; + mc->valid_cpu_types = valid_cpu_types; +} + +static void npcm845_evb_machine_class_init(ObjectClass *oc, const void *data) +{ + NPCM8xxMachineClass *nmc = NPCM8XX_MACHINE_CLASS(oc); + MachineClass *mc = MACHINE_CLASS(oc); + + npcm8xx_set_soc_type(nmc, TYPE_NPCM8XX); + + mc->desc = "Nuvoton NPCM845 Evaluation Board (Cortex-A35)"; + mc->init = npcm845_evb_init; + mc->default_ram_size = 1 * GiB; +}; + +static const TypeInfo npcm8xx_machine_types[] = { + { + .name = TYPE_NPCM8XX_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(NPCM8xxMachine), + .class_size = sizeof(NPCM8xxMachineClass), + .class_init = npcm8xx_machine_class_init, + .abstract = true, + }, { + .name = MACHINE_TYPE_NAME("npcm845-evb"), + .parent = TYPE_NPCM8XX_MACHINE, + .class_init = npcm845_evb_machine_class_init, + }, +}; + +DEFINE_TYPES(npcm8xx_machine_types) diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index 34da0d62f00..d8cc3214ed6 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; @@ -77,25 +76,23 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) } /* This clock doesn't need migration because it is fixed-frequency */ clock_set_hz(s->sysclk, HCLK_FRQ); - qdev_connect_clock_in(DEVICE(&s->cpu), "cpuclk", s->sysclk); + qdev_connect_clock_in(DEVICE(&s->armv7m), "cpuclk", s->sysclk); /* * This SoC has no systick device, so don't connect refclk. * TODO: model the lack of systick (currently the armv7m object * will always provide one). */ - object_property_set_link(OBJECT(&s->cpu), "memory", OBJECT(&s->container), + object_property_set_link(OBJECT(&s->armv7m), "memory", OBJECT(&s->container), &error_abort); - if (!sysbus_realize(SYS_BUS_DEVICE(&s->cpu), errp)) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { return; } 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); @@ -107,7 +104,7 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->uart), 0); memory_region_add_subregion_overlap(&s->container, NRF51_UART_BASE, mr, 0); sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart), 0, - qdev_get_gpio_in(DEVICE(&s->cpu), + qdev_get_gpio_in(DEVICE(&s->armv7m), BASE_TO_IRQ(NRF51_UART_BASE))); /* RNG */ @@ -118,7 +115,7 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0); memory_region_add_subregion_overlap(&s->container, NRF51_RNG_BASE, mr, 0); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rng), 0, - qdev_get_gpio_in(DEVICE(&s->cpu), + qdev_get_gpio_in(DEVICE(&s->armv7m), BASE_TO_IRQ(NRF51_RNG_BASE))); /* UICR, FICR, NVMC, FLASH */ @@ -164,7 +161,7 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer[i]), 0, base_addr); sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer[i]), 0, - qdev_get_gpio_in(DEVICE(&s->cpu), + qdev_get_gpio_in(DEVICE(&s->armv7m), BASE_TO_IRQ(base_addr))); } @@ -188,10 +185,10 @@ static void nrf51_soc_init(Object *obj) memory_region_init(&s->container, obj, "nrf51-container", UINT64_MAX); - object_initialize_child(OBJECT(s), "armv6m", &s->cpu, TYPE_ARMV7M); - qdev_prop_set_string(DEVICE(&s->cpu), "cpu-type", + object_initialize_child(OBJECT(s), "armv6m", &s->armv7m, TYPE_ARMV7M); + qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type", ARM_CPU_TYPE_NAME("cortex-m0")); - qdev_prop_set_uint32(DEVICE(&s->cpu), "num-irq", 32); + qdev_prop_set_uint32(DEVICE(&s->armv7m), "num-irq", 32); object_initialize_child(obj, "uart", &s->uart, TYPE_NRF51_UART); object_property_add_alias(obj, "serial0", OBJECT(&s->uart), "chardev"); @@ -211,16 +208,15 @@ static void nrf51_soc_init(Object *obj) s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); } -static Property nrf51_soc_properties[] = { +static const Property nrf51_soc_properties[] = { DEFINE_PROP_LINK("memory", NRF51State, board_memory, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT32("sram-size", NRF51State, sram_size, NRF51822_SRAM_SIZE), DEFINE_PROP_UINT32("flash-size", NRF51State, flash_size, NRF51822_FLASH_SIZE), - DEFINE_PROP_END_OF_LIST(), }; -static void nrf51_soc_class_init(ObjectClass *klass, void *data) +static void nrf51_soc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c deleted file mode 100644 index 9e49e9e1776..00000000000 --- 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" -#include "qemu/error-report.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 const MouseTransformInfo n800_pointercal = { - .x = 800, - .y = 480, - .a = { 14560, -68, -3455208, -39, -9621, 35152972, 65536 }, -}; - -static const 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 const 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 const 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 const 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, - }, - { /* end of list */ } -}, 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, - }, - { /* end of list */ } -}; - -static const 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" }, - { /* end of list */ } -}, n810_part_info[] = { - { 0x00000000, 0x00020000, 0x3, "bootloader" }, - { 0x00020000, 0x00060000, 0x0, "config" }, - { 0x00080000, 0x00220000, 0x0, "kernel" }, - { 0x002a0000, 0x00400000, 0x0, "initfs" }, - { 0x006a0000, 0x0f960000, 0x0, "rootfs" }, - { /* end of list */ } -}; - -static const uint8_t n8x0_bd_addr[6] = { N8X0_BD_ADDR }; - -static int n8x0_atag_setup(void *p, int model) -{ - uint8_t *b; - uint16_t *w; - uint32_t *l; - const struct omap_gpiosw_info_s *gpiosw; - const 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 index 3aa61c91b75..1f15620f9fd 100644 --- a/hw/arm/olimex-stm32-h405.c +++ b/hw/arm/olimex-stm32-h405.c @@ -47,20 +47,25 @@ static void olimex_stm32_h405_init(MachineState *machine) clock_set_hz(sysclk, SYSCLK_FRQ); dev = qdev_new(TYPE_STM32F405_SOC); - qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4")); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - armv7m_load_kernel(ARM_CPU(first_cpu), + armv7m_load_kernel(STM32F405_SOC(dev)->armv7m.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->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m4"); + mc->valid_cpu_types = valid_cpu_types; /* SRAM pre-allocated as part of the SoC instantiation */ mc->default_ram_size = 0; diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index d5438156ee9..74458fb7c69 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -23,23 +23,26 @@ #include "qemu/main-loop.h" #include "qapi/error.h" #include "cpu.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/hw.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/arm/boot.h" #include "hw/arm/omap.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" +#include "hw/sd/sd.h" +#include "system/blockdev.h" +#include "system/system.h" #include "hw/arm/soc_dma.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/rtc.h" +#include "system/qtest.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/rtc.h" #include "qemu/range.h" #include "hw/sysbus.h" #include "qemu/cutils.h" #include "qemu/bcd.h" +#include "target/arm/cpu-qom.h" +#include "trace.h" static inline void omap_log_badwidth(const char *funcname, hwaddr addr, int sz) { @@ -141,7 +144,7 @@ static inline void omap_timer_update(struct omap_mpu_timer_s *timer) int64_t expires; if (timer->enable && timer->st && timer->rate) { - timer->val = timer->reset_val; /* Should skip this on clk enable */ + timer->val = timer->reset_val; /* Should skip this on clk enable */ expires = muldiv64((uint64_t) timer->val << (timer->ptv + 1), NANOSECONDS_PER_SECOND, timer->rate); @@ -209,13 +212,13 @@ static uint64_t omap_mpu_timer_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* CNTL_TIMER */ + case 0x00: /* CNTL_TIMER */ return (s->enable << 5) | (s->ptv << 2) | (s->ar << 1) | s->st; - case 0x04: /* LOAD_TIM */ + case 0x04: /* LOAD_TIM */ break; - case 0x08: /* READ_TIM */ + case 0x08: /* READ_TIM */ return omap_timer_read(s); } @@ -234,7 +237,7 @@ static void omap_mpu_timer_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* CNTL_TIMER */ + case 0x00: /* CNTL_TIMER */ omap_timer_sync(s); s->enable = (value >> 5) & 1; s->ptv = (value >> 2) & 7; @@ -243,11 +246,11 @@ static void omap_mpu_timer_write(void *opaque, hwaddr addr, omap_timer_update(s); return; - case 0x04: /* LOAD_TIM */ + case 0x04: /* LOAD_TIM */ s->reset_val = value; return; - case 0x08: /* READ_TIM */ + case 0x08: /* READ_TIM */ OMAP_RO_REG(addr); break; @@ -315,14 +318,14 @@ static uint64_t omap_wd_timer_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* CNTL_TIMER */ + case 0x00: /* CNTL_TIMER */ return (s->timer.ptv << 9) | (s->timer.ar << 8) | (s->timer.st << 7) | (s->free << 1); - case 0x04: /* READ_TIMER */ + case 0x04: /* READ_TIMER */ return omap_timer_read(&s->timer); - case 0x08: /* TIMER_MODE */ + case 0x08: /* TIMER_MODE */ return s->mode << 15; } @@ -341,7 +344,7 @@ static void omap_wd_timer_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* CNTL_TIMER */ + case 0x00: /* CNTL_TIMER */ omap_timer_sync(&s->timer); s->timer.ptv = (value >> 9) & 7; s->timer.ar = (value >> 8) & 1; @@ -350,11 +353,11 @@ static void omap_wd_timer_write(void *opaque, hwaddr addr, omap_timer_update(&s->timer); break; - case 0x04: /* LOAD_TIMER */ + case 0x04: /* LOAD_TIMER */ s->timer.reset_val = value & 0xffff; break; - case 0x08: /* TIMER_MODE */ + case 0x08: /* TIMER_MODE */ if (!s->mode && ((value >> 15) & 1)) omap_clk_get(s->timer.clk); s->mode |= (value >> 15) & 1; @@ -439,13 +442,13 @@ static uint64_t omap_os_timer_read(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* TVR */ + case 0x00: /* TVR */ return s->timer.reset_val; - case 0x04: /* TCR */ + case 0x04: /* TCR */ return omap_timer_read(&s->timer); - case 0x08: /* CR */ + case 0x08: /* CR */ return (s->timer.ar << 3) | (s->timer.it_ena << 2) | s->timer.st; default: @@ -467,15 +470,15 @@ static void omap_os_timer_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* TVR */ + case 0x00: /* TVR */ s->timer.reset_val = value & 0x00ffffff; break; - case 0x04: /* TCR */ + case 0x04: /* TCR */ OMAP_RO_REG(addr); break; - case 0x08: /* CR */ + case 0x08: /* CR */ s->timer.ar = (value >> 3) & 1; s->timer.it_ena = (value >> 2) & 1; if (s->timer.st != (value & 1) || (value & 2)) { @@ -540,34 +543,34 @@ static uint64_t omap_ulpd_pm_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x14: /* IT_STATUS */ + case 0x14: /* IT_STATUS */ ret = s->ulpd_pm_regs[addr >> 2]; s->ulpd_pm_regs[addr >> 2] = 0; qemu_irq_lower(qdev_get_gpio_in(s->ih[1], OMAP_INT_GAUGE_32K)); return ret; - case 0x18: /* Reserved */ - case 0x1c: /* Reserved */ - case 0x20: /* Reserved */ - case 0x28: /* Reserved */ - case 0x2c: /* Reserved */ + case 0x18: /* Reserved */ + case 0x1c: /* Reserved */ + case 0x20: /* Reserved */ + case 0x28: /* Reserved */ + case 0x2c: /* Reserved */ OMAP_BAD_REG(addr); /* fall through */ - case 0x00: /* COUNTER_32_LSB */ - case 0x04: /* COUNTER_32_MSB */ - case 0x08: /* COUNTER_HIGH_FREQ_LSB */ - case 0x0c: /* COUNTER_HIGH_FREQ_MSB */ - case 0x10: /* GAUGING_CTRL */ - case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */ - case 0x30: /* CLOCK_CTRL */ - case 0x34: /* SOFT_REQ */ - case 0x38: /* COUNTER_32_FIQ */ - case 0x3c: /* DPLL_CTRL */ - case 0x40: /* STATUS_REQ */ + case 0x00: /* COUNTER_32_LSB */ + case 0x04: /* COUNTER_32_MSB */ + case 0x08: /* COUNTER_HIGH_FREQ_LSB */ + case 0x0c: /* COUNTER_HIGH_FREQ_MSB */ + case 0x10: /* GAUGING_CTRL */ + case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */ + case 0x30: /* CLOCK_CTRL */ + case 0x34: /* SOFT_REQ */ + case 0x38: /* COUNTER_32_FIQ */ + case 0x3c: /* DPLL_CTRL */ + case 0x40: /* STATUS_REQ */ /* XXX: check clk::usecount state for every clock */ - case 0x48: /* LOCL_TIME */ - case 0x4c: /* APLL_CTRL */ - case 0x50: /* POWER_CTRL */ + case 0x48: /* LOCL_TIME */ + case 0x4c: /* APLL_CTRL */ + case 0x50: /* POWER_CTRL */ return s->ulpd_pm_regs[addr >> 2]; } @@ -578,22 +581,22 @@ static uint64_t omap_ulpd_pm_read(void *opaque, hwaddr addr, static inline void omap_ulpd_clk_update(struct omap_mpu_state_s *s, uint16_t diff, uint16_t value) { - if (diff & (1 << 4)) /* USB_MCLK_EN */ + if (diff & (1 << 4)) /* USB_MCLK_EN */ omap_clk_onoff(omap_findclk(s, "usb_clk0"), (value >> 4) & 1); - if (diff & (1 << 5)) /* DIS_USB_PVCI_CLK */ + if (diff & (1 << 5)) /* DIS_USB_PVCI_CLK */ omap_clk_onoff(omap_findclk(s, "usb_w2fc_ck"), (~value >> 5) & 1); } static inline void omap_ulpd_req_update(struct omap_mpu_state_s *s, uint16_t diff, uint16_t value) { - if (diff & (1 << 0)) /* SOFT_DPLL_REQ */ + if (diff & (1 << 0)) /* SOFT_DPLL_REQ */ omap_clk_canidle(omap_findclk(s, "dpll4"), (~value >> 0) & 1); - if (diff & (1 << 1)) /* SOFT_COM_REQ */ + if (diff & (1 << 1)) /* SOFT_COM_REQ */ omap_clk_canidle(omap_findclk(s, "com_mclk_out"), (~value >> 1) & 1); - if (diff & (1 << 2)) /* SOFT_SDW_REQ */ + if (diff & (1 << 2)) /* SOFT_SDW_REQ */ omap_clk_canidle(omap_findclk(s, "bt_mclk_out"), (~value >> 2) & 1); - if (diff & (1 << 3)) /* SOFT_USB_REQ */ + if (diff & (1 << 3)) /* SOFT_USB_REQ */ omap_clk_canidle(omap_findclk(s, "usb_clk0"), (~value >> 3) & 1); } @@ -612,16 +615,16 @@ static void omap_ulpd_pm_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* COUNTER_32_LSB */ - case 0x04: /* COUNTER_32_MSB */ - case 0x08: /* COUNTER_HIGH_FREQ_LSB */ - case 0x0c: /* COUNTER_HIGH_FREQ_MSB */ - case 0x14: /* IT_STATUS */ - case 0x40: /* STATUS_REQ */ + case 0x00: /* COUNTER_32_LSB */ + case 0x04: /* COUNTER_32_MSB */ + case 0x08: /* COUNTER_HIGH_FREQ_LSB */ + case 0x0c: /* COUNTER_HIGH_FREQ_MSB */ + case 0x14: /* IT_STATUS */ + case 0x40: /* STATUS_REQ */ OMAP_RO_REG(addr); break; - case 0x10: /* GAUGING_CTRL */ + case 0x10: /* GAUGING_CTRL */ /* Bits 0 and 1 seem to be confused in the OMAP 310 TRM */ if ((s->ulpd_pm_regs[addr >> 2] ^ value) & 1) { now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); @@ -635,50 +638,50 @@ static void omap_ulpd_pm_write(void *opaque, hwaddr addr, ticks = muldiv64(now, 32768, NANOSECONDS_PER_SECOND); s->ulpd_pm_regs[0x00 >> 2] = (ticks >> 0) & 0xffff; s->ulpd_pm_regs[0x04 >> 2] = (ticks >> 16) & 0xffff; - if (ticks >> 32) /* OVERFLOW_32K */ + if (ticks >> 32) /* OVERFLOW_32K */ s->ulpd_pm_regs[0x14 >> 2] |= 1 << 2; /* High frequency ticks */ ticks = muldiv64(now, 12000000, NANOSECONDS_PER_SECOND); s->ulpd_pm_regs[0x08 >> 2] = (ticks >> 0) & 0xffff; s->ulpd_pm_regs[0x0c >> 2] = (ticks >> 16) & 0xffff; - if (ticks >> 32) /* OVERFLOW_HI_FREQ */ + if (ticks >> 32) /* OVERFLOW_HI_FREQ */ s->ulpd_pm_regs[0x14 >> 2] |= 1 << 1; - s->ulpd_pm_regs[0x14 >> 2] |= 1 << 0; /* IT_GAUGING */ + s->ulpd_pm_regs[0x14 >> 2] |= 1 << 0; /* IT_GAUGING */ qemu_irq_raise(qdev_get_gpio_in(s->ih[1], OMAP_INT_GAUGE_32K)); } } s->ulpd_pm_regs[addr >> 2] = value; break; - case 0x18: /* Reserved */ - case 0x1c: /* Reserved */ - case 0x20: /* Reserved */ - case 0x28: /* Reserved */ - case 0x2c: /* Reserved */ + case 0x18: /* Reserved */ + case 0x1c: /* Reserved */ + case 0x20: /* Reserved */ + case 0x28: /* Reserved */ + case 0x2c: /* Reserved */ OMAP_BAD_REG(addr); /* fall through */ - case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */ - case 0x38: /* COUNTER_32_FIQ */ - case 0x48: /* LOCL_TIME */ - case 0x50: /* POWER_CTRL */ + case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */ + case 0x38: /* COUNTER_32_FIQ */ + case 0x48: /* LOCL_TIME */ + case 0x50: /* POWER_CTRL */ s->ulpd_pm_regs[addr >> 2] = value; break; - case 0x30: /* CLOCK_CTRL */ + case 0x30: /* CLOCK_CTRL */ diff = s->ulpd_pm_regs[addr >> 2] ^ value; s->ulpd_pm_regs[addr >> 2] = value & 0x3f; omap_ulpd_clk_update(s, diff, value); break; - case 0x34: /* SOFT_REQ */ + case 0x34: /* SOFT_REQ */ diff = s->ulpd_pm_regs[addr >> 2] ^ value; s->ulpd_pm_regs[addr >> 2] = value & 0x1f; omap_ulpd_req_update(s, diff, value); break; - case 0x3c: /* DPLL_CTRL */ + case 0x3c: /* DPLL_CTRL */ /* XXX: OMAP310 TRM claims bit 3 is PLL_ENABLE, and bit 4 is * omitted altogether, probably a typo. */ /* This register has identical semantics with DPLL(1:3) control @@ -686,11 +689,11 @@ static void omap_ulpd_pm_write(void *opaque, hwaddr addr, diff = s->ulpd_pm_regs[addr >> 2] & value; s->ulpd_pm_regs[addr >> 2] = value & 0x2fff; if (diff & (0x3ff << 2)) { - if (value & (1 << 4)) { /* PLL_ENABLE */ - div = ((value >> 5) & 3) + 1; /* PLL_DIV */ - mult = MIN((value >> 7) & 0x1f, 1); /* PLL_MULT */ + if (value & (1 << 4)) { /* PLL_ENABLE */ + div = ((value >> 5) & 3) + 1; /* PLL_DIV */ + mult = MIN((value >> 7) & 0x1f, 1); /* PLL_MULT */ } else { - div = bypass_div[((value >> 2) & 3)]; /* BYPASS_DIV */ + div = bypass_div[((value >> 2) & 3)]; /* BYPASS_DIV */ mult = 1; } omap_clk_setrate(omap_findclk(s, "dpll4"), div, mult); @@ -705,10 +708,10 @@ static void omap_ulpd_pm_write(void *opaque, hwaddr addr, s->ulpd_pm_regs[addr >> 2] |= 2; break; - case 0x4c: /* APLL_CTRL */ + case 0x4c: /* APLL_CTRL */ diff = s->ulpd_pm_regs[addr >> 2] & value; s->ulpd_pm_regs[addr >> 2] = value & 0xf; - if (diff & (1 << 0)) /* APLL_NDPLL_SWITCH */ + if (diff & (1 << 0)) /* APLL_NDPLL_SWITCH */ omap_clk_reparent(omap_findclk(s, "ck_48m"), omap_findclk(s, (value & (1 << 0)) ? "apll" : "dpll4")); break; @@ -772,43 +775,43 @@ static uint64_t omap_pin_cfg_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* FUNC_MUX_CTRL_0 */ - case 0x04: /* FUNC_MUX_CTRL_1 */ - case 0x08: /* FUNC_MUX_CTRL_2 */ + case 0x00: /* FUNC_MUX_CTRL_0 */ + case 0x04: /* FUNC_MUX_CTRL_1 */ + case 0x08: /* FUNC_MUX_CTRL_2 */ return s->func_mux_ctrl[addr >> 2]; - case 0x0c: /* COMP_MODE_CTRL_0 */ + case 0x0c: /* COMP_MODE_CTRL_0 */ return s->comp_mode_ctrl[0]; - case 0x10: /* FUNC_MUX_CTRL_3 */ - case 0x14: /* FUNC_MUX_CTRL_4 */ - case 0x18: /* FUNC_MUX_CTRL_5 */ - case 0x1c: /* FUNC_MUX_CTRL_6 */ - case 0x20: /* FUNC_MUX_CTRL_7 */ - case 0x24: /* FUNC_MUX_CTRL_8 */ - case 0x28: /* FUNC_MUX_CTRL_9 */ - case 0x2c: /* FUNC_MUX_CTRL_A */ - case 0x30: /* FUNC_MUX_CTRL_B */ - case 0x34: /* FUNC_MUX_CTRL_C */ - case 0x38: /* FUNC_MUX_CTRL_D */ + case 0x10: /* FUNC_MUX_CTRL_3 */ + case 0x14: /* FUNC_MUX_CTRL_4 */ + case 0x18: /* FUNC_MUX_CTRL_5 */ + case 0x1c: /* FUNC_MUX_CTRL_6 */ + case 0x20: /* FUNC_MUX_CTRL_7 */ + case 0x24: /* FUNC_MUX_CTRL_8 */ + case 0x28: /* FUNC_MUX_CTRL_9 */ + case 0x2c: /* FUNC_MUX_CTRL_A */ + case 0x30: /* FUNC_MUX_CTRL_B */ + case 0x34: /* FUNC_MUX_CTRL_C */ + case 0x38: /* FUNC_MUX_CTRL_D */ return s->func_mux_ctrl[(addr >> 2) - 1]; - case 0x40: /* PULL_DWN_CTRL_0 */ - case 0x44: /* PULL_DWN_CTRL_1 */ - case 0x48: /* PULL_DWN_CTRL_2 */ - case 0x4c: /* PULL_DWN_CTRL_3 */ + case 0x40: /* PULL_DWN_CTRL_0 */ + case 0x44: /* PULL_DWN_CTRL_1 */ + case 0x48: /* PULL_DWN_CTRL_2 */ + case 0x4c: /* PULL_DWN_CTRL_3 */ return s->pull_dwn_ctrl[(addr & 0xf) >> 2]; - case 0x50: /* GATE_INH_CTRL_0 */ + case 0x50: /* GATE_INH_CTRL_0 */ return s->gate_inh_ctrl[0]; - case 0x60: /* VOLTAGE_CTRL_0 */ + case 0x60: /* VOLTAGE_CTRL_0 */ return s->voltage_ctrl[0]; - case 0x70: /* TEST_DBG_CTRL_0 */ + case 0x70: /* TEST_DBG_CTRL_0 */ return s->test_dbg_ctrl[0]; - case 0x80: /* MOD_CONF_CTRL_0 */ + case 0x80: /* MOD_CONF_CTRL_0 */ return s->mod_conf_ctrl[0]; } @@ -820,10 +823,10 @@ static inline void omap_pin_funcmux0_update(struct omap_mpu_state_s *s, uint32_t diff, uint32_t value) { if (s->compat1509) { - if (diff & (1 << 9)) /* BLUETOOTH */ + if (diff & (1 << 9)) /* BLUETOOTH */ omap_clk_onoff(omap_findclk(s, "bt_mclk_out"), (~value >> 9) & 1); - if (diff & (1 << 7)) /* USB.CLKO */ + if (diff & (1 << 7)) /* USB.CLKO */ omap_clk_onoff(omap_findclk(s, "usb.clko"), (value >> 7) & 1); } @@ -853,23 +856,23 @@ static inline void omap_pin_modconf1_update(struct omap_mpu_state_s *s, omap_findclk(s, ((value >> 31) & 1) ? "ck_48m" : "armper_ck")); } - if (diff & (1 << 30)) /* CONF_MOD_UART2_CLK_MODE_R */ + if (diff & (1 << 30)) /* CONF_MOD_UART2_CLK_MODE_R */ omap_clk_reparent(omap_findclk(s, "uart2_ck"), omap_findclk(s, ((value >> 30) & 1) ? "ck_48m" : "armper_ck")); - if (diff & (1 << 29)) /* CONF_MOD_UART1_CLK_MODE_R */ + if (diff & (1 << 29)) /* CONF_MOD_UART1_CLK_MODE_R */ omap_clk_reparent(omap_findclk(s, "uart1_ck"), omap_findclk(s, ((value >> 29) & 1) ? "ck_48m" : "armper_ck")); - if (diff & (1 << 23)) /* CONF_MOD_MMC_SD_CLK_REQ_R */ + if (diff & (1 << 23)) /* CONF_MOD_MMC_SD_CLK_REQ_R */ omap_clk_reparent(omap_findclk(s, "mmc_ck"), omap_findclk(s, ((value >> 23) & 1) ? "ck_48m" : "armper_ck")); - if (diff & (1 << 12)) /* CONF_MOD_COM_MCLK_12_48_S */ + if (diff & (1 << 12)) /* CONF_MOD_COM_MCLK_12_48_S */ omap_clk_reparent(omap_findclk(s, "com_mclk_out"), omap_findclk(s, ((value >> 12) & 1) ? "ck_48m" : "armper_ck")); - if (diff & (1 << 9)) /* CONF_MOD_USB_HOST_HHC_UHO */ + if (diff & (1 << 9)) /* CONF_MOD_USB_HOST_HHC_UHO */ omap_clk_onoff(omap_findclk(s, "usb_hhc_ck"), (value >> 9) & 1); } @@ -885,63 +888,63 @@ static void omap_pin_cfg_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* FUNC_MUX_CTRL_0 */ + case 0x00: /* FUNC_MUX_CTRL_0 */ diff = s->func_mux_ctrl[addr >> 2] ^ value; s->func_mux_ctrl[addr >> 2] = value; omap_pin_funcmux0_update(s, diff, value); return; - case 0x04: /* FUNC_MUX_CTRL_1 */ + case 0x04: /* FUNC_MUX_CTRL_1 */ diff = s->func_mux_ctrl[addr >> 2] ^ value; s->func_mux_ctrl[addr >> 2] = value; omap_pin_funcmux1_update(s, diff, value); return; - case 0x08: /* FUNC_MUX_CTRL_2 */ + case 0x08: /* FUNC_MUX_CTRL_2 */ s->func_mux_ctrl[addr >> 2] = value; return; - case 0x0c: /* COMP_MODE_CTRL_0 */ + case 0x0c: /* COMP_MODE_CTRL_0 */ s->comp_mode_ctrl[0] = value; s->compat1509 = (value != 0x0000eaef); omap_pin_funcmux0_update(s, ~0, s->func_mux_ctrl[0]); omap_pin_funcmux1_update(s, ~0, s->func_mux_ctrl[1]); return; - case 0x10: /* FUNC_MUX_CTRL_3 */ - case 0x14: /* FUNC_MUX_CTRL_4 */ - case 0x18: /* FUNC_MUX_CTRL_5 */ - case 0x1c: /* FUNC_MUX_CTRL_6 */ - case 0x20: /* FUNC_MUX_CTRL_7 */ - case 0x24: /* FUNC_MUX_CTRL_8 */ - case 0x28: /* FUNC_MUX_CTRL_9 */ - case 0x2c: /* FUNC_MUX_CTRL_A */ - case 0x30: /* FUNC_MUX_CTRL_B */ - case 0x34: /* FUNC_MUX_CTRL_C */ - case 0x38: /* FUNC_MUX_CTRL_D */ + case 0x10: /* FUNC_MUX_CTRL_3 */ + case 0x14: /* FUNC_MUX_CTRL_4 */ + case 0x18: /* FUNC_MUX_CTRL_5 */ + case 0x1c: /* FUNC_MUX_CTRL_6 */ + case 0x20: /* FUNC_MUX_CTRL_7 */ + case 0x24: /* FUNC_MUX_CTRL_8 */ + case 0x28: /* FUNC_MUX_CTRL_9 */ + case 0x2c: /* FUNC_MUX_CTRL_A */ + case 0x30: /* FUNC_MUX_CTRL_B */ + case 0x34: /* FUNC_MUX_CTRL_C */ + case 0x38: /* FUNC_MUX_CTRL_D */ s->func_mux_ctrl[(addr >> 2) - 1] = value; return; - case 0x40: /* PULL_DWN_CTRL_0 */ - case 0x44: /* PULL_DWN_CTRL_1 */ - case 0x48: /* PULL_DWN_CTRL_2 */ - case 0x4c: /* PULL_DWN_CTRL_3 */ + case 0x40: /* PULL_DWN_CTRL_0 */ + case 0x44: /* PULL_DWN_CTRL_1 */ + case 0x48: /* PULL_DWN_CTRL_2 */ + case 0x4c: /* PULL_DWN_CTRL_3 */ s->pull_dwn_ctrl[(addr & 0xf) >> 2] = value; return; - case 0x50: /* GATE_INH_CTRL_0 */ + case 0x50: /* GATE_INH_CTRL_0 */ s->gate_inh_ctrl[0] = value; return; - case 0x60: /* VOLTAGE_CTRL_0 */ + case 0x60: /* VOLTAGE_CTRL_0 */ s->voltage_ctrl[0] = value; return; - case 0x70: /* TEST_DBG_CTRL_0 */ + case 0x70: /* TEST_DBG_CTRL_0 */ s->test_dbg_ctrl[0] = value; return; - case 0x80: /* MOD_CONF_CTRL_0 */ + case 0x80: /* MOD_CONF_CTRL_0 */ diff = s->mod_conf_ctrl[0] ^ value; s->mod_conf_ctrl[0] = value; omap_pin_modconf1_update(s, diff, value); @@ -995,17 +998,17 @@ static uint64_t omap_id_read(void *opaque, hwaddr addr, } switch (addr) { - case 0xfffe1800: /* DIE_ID_LSB */ + case 0xfffe1800: /* DIE_ID_LSB */ return 0xc9581f0e; - case 0xfffe1804: /* DIE_ID_MSB */ + case 0xfffe1804: /* DIE_ID_MSB */ return 0xa8858bfa; - case 0xfffe2000: /* PRODUCT_ID_LSB */ + case 0xfffe2000: /* PRODUCT_ID_LSB */ return 0x00aaaafc; - case 0xfffe2004: /* PRODUCT_ID_MSB */ + case 0xfffe2004: /* PRODUCT_ID_MSB */ return 0xcafeb574; - case 0xfffed400: /* JTAG_ID_LSB */ + case 0xfffed400: /* JTAG_ID_LSB */ switch (s->mpu_model) { case omap310: return 0x03310315; @@ -1016,7 +1019,7 @@ static uint64_t omap_id_read(void *opaque, hwaddr addr, } break; - case 0xfffed404: /* JTAG_ID_MSB */ + case 0xfffed404: /* JTAG_ID_MSB */ switch (s->mpu_model) { case omap310: return 0xfb57402f; @@ -1077,22 +1080,22 @@ static uint64_t omap_mpui_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* CTRL */ + case 0x00: /* CTRL */ return s->mpui_ctrl; - case 0x04: /* DEBUG_ADDR */ + case 0x04: /* DEBUG_ADDR */ return 0x01ffffff; - case 0x08: /* DEBUG_DATA */ + case 0x08: /* DEBUG_DATA */ return 0xffffffff; - case 0x0c: /* DEBUG_FLAG */ + case 0x0c: /* DEBUG_FLAG */ return 0x00000800; - case 0x10: /* STATUS */ + case 0x10: /* STATUS */ return 0x00000000; /* Not in OMAP310 */ - case 0x14: /* DSP_STATUS */ - case 0x18: /* DSP_BOOT_CONFIG */ + case 0x14: /* DSP_STATUS */ + case 0x18: /* DSP_BOOT_CONFIG */ return 0x00000000; - case 0x1c: /* DSP_MPUI_CONFIG */ + case 0x1c: /* DSP_MPUI_CONFIG */ return 0x0000ffff; } @@ -1111,20 +1114,20 @@ static void omap_mpui_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* CTRL */ + case 0x00: /* CTRL */ s->mpui_ctrl = value & 0x007fffff; break; - case 0x04: /* DEBUG_ADDR */ - case 0x08: /* DEBUG_DATA */ - case 0x0c: /* DEBUG_FLAG */ - case 0x10: /* STATUS */ + case 0x04: /* DEBUG_ADDR */ + case 0x08: /* DEBUG_DATA */ + case 0x0c: /* DEBUG_FLAG */ + case 0x10: /* STATUS */ /* Not in OMAP310 */ - case 0x14: /* DSP_STATUS */ + case 0x14: /* DSP_STATUS */ OMAP_RO_REG(addr); break; - case 0x18: /* DSP_BOOT_CONFIG */ - case 0x1c: /* DSP_MPUI_CONFIG */ + case 0x18: /* DSP_BOOT_CONFIG */ + case 0x1c: /* DSP_MPUI_CONFIG */ break; default: @@ -1175,19 +1178,19 @@ static uint64_t omap_tipb_bridge_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* TIPB_CNTL */ + case 0x00: /* TIPB_CNTL */ return s->control; - case 0x04: /* TIPB_BUS_ALLOC */ + case 0x04: /* TIPB_BUS_ALLOC */ return s->alloc; - case 0x08: /* MPU_TIPB_CNTL */ + case 0x08: /* MPU_TIPB_CNTL */ return s->buffer; - case 0x0c: /* ENHANCED_TIPB_CNTL */ + case 0x0c: /* ENHANCED_TIPB_CNTL */ return s->enh_control; - case 0x10: /* ADDRESS_DBG */ - case 0x14: /* DATA_DEBUG_LOW */ - case 0x18: /* DATA_DEBUG_HIGH */ + case 0x10: /* ADDRESS_DBG */ + case 0x14: /* DATA_DEBUG_LOW */ + case 0x18: /* DATA_DEBUG_HIGH */ return 0xffff; - case 0x1c: /* DEBUG_CNTR_SIG */ + case 0x1c: /* DEBUG_CNTR_SIG */ return 0x00f8; } @@ -1206,27 +1209,27 @@ static void omap_tipb_bridge_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* TIPB_CNTL */ + case 0x00: /* TIPB_CNTL */ s->control = value & 0xffff; break; - case 0x04: /* TIPB_BUS_ALLOC */ + case 0x04: /* TIPB_BUS_ALLOC */ s->alloc = value & 0x003f; break; - case 0x08: /* MPU_TIPB_CNTL */ + case 0x08: /* MPU_TIPB_CNTL */ s->buffer = value & 0x0003; break; - case 0x0c: /* ENHANCED_TIPB_CNTL */ + case 0x0c: /* ENHANCED_TIPB_CNTL */ s->width_intr = !(value & 2); s->enh_control = value & 0x000f; break; - case 0x10: /* ADDRESS_DBG */ - case 0x14: /* DATA_DEBUG_LOW */ - case 0x18: /* DATA_DEBUG_HIGH */ - case 0x1c: /* DEBUG_CNTR_SIG */ + case 0x10: /* ADDRESS_DBG */ + case 0x14: /* DATA_DEBUG_LOW */ + case 0x18: /* DATA_DEBUG_HIGH */ + case 0x1c: /* DEBUG_CNTR_SIG */ OMAP_RO_REG(addr); break; @@ -1277,23 +1280,23 @@ static uint64_t omap_tcmi_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* IMIF_PRIO */ - case 0x04: /* EMIFS_PRIO */ - case 0x08: /* EMIFF_PRIO */ - case 0x0c: /* EMIFS_CONFIG */ - case 0x10: /* EMIFS_CS0_CONFIG */ - case 0x14: /* EMIFS_CS1_CONFIG */ - case 0x18: /* EMIFS_CS2_CONFIG */ - case 0x1c: /* EMIFS_CS3_CONFIG */ - case 0x24: /* EMIFF_MRS */ - case 0x28: /* TIMEOUT1 */ - case 0x2c: /* TIMEOUT2 */ - case 0x30: /* TIMEOUT3 */ - case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */ - case 0x40: /* EMIFS_CFG_DYN_WAIT */ + case 0x00: /* IMIF_PRIO */ + case 0x04: /* EMIFS_PRIO */ + case 0x08: /* EMIFF_PRIO */ + case 0x0c: /* EMIFS_CONFIG */ + case 0x10: /* EMIFS_CS0_CONFIG */ + case 0x14: /* EMIFS_CS1_CONFIG */ + case 0x18: /* EMIFS_CS2_CONFIG */ + case 0x1c: /* EMIFS_CS3_CONFIG */ + case 0x24: /* EMIFF_MRS */ + case 0x28: /* TIMEOUT1 */ + case 0x2c: /* TIMEOUT2 */ + case 0x30: /* TIMEOUT3 */ + case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */ + case 0x40: /* EMIFS_CFG_DYN_WAIT */ return s->tcmi_regs[addr >> 2]; - case 0x20: /* EMIFF_SDRAM_CONFIG */ + case 0x20: /* EMIFF_SDRAM_CONFIG */ ret = s->tcmi_regs[addr >> 2]; s->tcmi_regs[addr >> 2] &= ~1; /* XXX: Clear SLRF on SDRAM access */ /* XXX: We can try using the VGA_DIRTY flag for this */ @@ -1315,23 +1318,23 @@ static void omap_tcmi_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* IMIF_PRIO */ - case 0x04: /* EMIFS_PRIO */ - case 0x08: /* EMIFF_PRIO */ - case 0x10: /* EMIFS_CS0_CONFIG */ - case 0x14: /* EMIFS_CS1_CONFIG */ - case 0x18: /* EMIFS_CS2_CONFIG */ - case 0x1c: /* EMIFS_CS3_CONFIG */ - case 0x20: /* EMIFF_SDRAM_CONFIG */ - case 0x24: /* EMIFF_MRS */ - case 0x28: /* TIMEOUT1 */ - case 0x2c: /* TIMEOUT2 */ - case 0x30: /* TIMEOUT3 */ - case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */ - case 0x40: /* EMIFS_CFG_DYN_WAIT */ + case 0x00: /* IMIF_PRIO */ + case 0x04: /* EMIFS_PRIO */ + case 0x08: /* EMIFF_PRIO */ + case 0x10: /* EMIFS_CS0_CONFIG */ + case 0x14: /* EMIFS_CS1_CONFIG */ + case 0x18: /* EMIFS_CS2_CONFIG */ + case 0x1c: /* EMIFS_CS3_CONFIG */ + case 0x20: /* EMIFF_SDRAM_CONFIG */ + case 0x24: /* EMIFF_MRS */ + case 0x28: /* TIMEOUT1 */ + case 0x2c: /* TIMEOUT2 */ + case 0x30: /* TIMEOUT3 */ + case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */ + case 0x40: /* EMIFS_CFG_DYN_WAIT */ s->tcmi_regs[addr >> 2] = value; break; - case 0x0c: /* EMIFS_CONFIG */ + case 0x0c: /* EMIFS_CONFIG */ s->tcmi_regs[addr >> 2] = (value & 0xf) | (1 << 4); break; @@ -1390,7 +1393,7 @@ static uint64_t omap_dpll_read(void *opaque, hwaddr addr, return omap_badwidth_read16(opaque, addr); } - if (addr == 0x00) /* CTL_REG */ + if (addr == 0x00) /* CTL_REG */ return s->mode; OMAP_BAD_REG(addr); @@ -1410,16 +1413,16 @@ static void omap_dpll_write(void *opaque, hwaddr addr, return; } - if (addr == 0x00) { /* CTL_REG */ + if (addr == 0x00) { /* CTL_REG */ /* See omap_ulpd_pm_write() too */ diff = s->mode & value; s->mode = value & 0x2fff; if (diff & (0x3ff << 2)) { - if (value & (1 << 4)) { /* PLL_ENABLE */ - div = ((value >> 5) & 3) + 1; /* PLL_DIV */ - mult = MIN((value >> 7) & 0x1f, 1); /* PLL_MULT */ + if (value & (1 << 4)) { /* PLL_ENABLE */ + div = ((value >> 5) & 3) + 1; /* PLL_DIV */ + mult = MIN((value >> 7) & 0x1f, 1); /* PLL_MULT */ } else { - div = bypass_div[((value >> 2) & 3)]; /* BYPASS_DIV */ + div = bypass_div[((value >> 2) & 3)]; /* BYPASS_DIV */ mult = 1; } omap_clk_setrate(s->dpll, div, mult); @@ -1471,31 +1474,31 @@ static uint64_t omap_clkm_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* ARM_CKCTL */ + case 0x00: /* ARM_CKCTL */ return s->clkm.arm_ckctl; - case 0x04: /* ARM_IDLECT1 */ + case 0x04: /* ARM_IDLECT1 */ return s->clkm.arm_idlect1; - case 0x08: /* ARM_IDLECT2 */ + case 0x08: /* ARM_IDLECT2 */ return s->clkm.arm_idlect2; - case 0x0c: /* ARM_EWUPCT */ + case 0x0c: /* ARM_EWUPCT */ return s->clkm.arm_ewupct; - case 0x10: /* ARM_RSTCT1 */ + case 0x10: /* ARM_RSTCT1 */ return s->clkm.arm_rstct1; - case 0x14: /* ARM_RSTCT2 */ + case 0x14: /* ARM_RSTCT2 */ return s->clkm.arm_rstct2; - case 0x18: /* ARM_SYSST */ + case 0x18: /* ARM_SYSST */ return (s->clkm.clocking_scheme << 11) | s->clkm.cold_start; - case 0x1c: /* ARM_CKOUT1 */ + case 0x1c: /* ARM_CKOUT1 */ return s->clkm.arm_ckout1; - case 0x20: /* ARM_CKOUT2 */ + case 0x20: /* ARM_CKOUT2 */ break; } @@ -1508,7 +1511,7 @@ static inline void omap_clkm_ckctl_update(struct omap_mpu_state_s *s, { omap_clk clk; - if (diff & (1 << 14)) { /* ARM_INTHCK_SEL */ + if (diff & (1 << 14)) { /* ARM_INTHCK_SEL */ if (value & (1 << 14)) /* Reserved */; else { @@ -1516,7 +1519,7 @@ static inline void omap_clkm_ckctl_update(struct omap_mpu_state_s *s, omap_clk_reparent(clk, omap_findclk(s, "tc_ck")); } } - if (diff & (1 << 12)) { /* ARM_TIMXO */ + if (diff & (1 << 12)) { /* ARM_TIMXO */ clk = omap_findclk(s, "armtim_ck"); if (value & (1 << 12)) omap_clk_reparent(clk, omap_findclk(s, "clkin")); @@ -1524,27 +1527,27 @@ static inline void omap_clkm_ckctl_update(struct omap_mpu_state_s *s, omap_clk_reparent(clk, omap_findclk(s, "ck_gen1")); } /* XXX: en_dspck */ - if (diff & (3 << 10)) { /* DSPMMUDIV */ + if (diff & (3 << 10)) { /* DSPMMUDIV */ clk = omap_findclk(s, "dspmmu_ck"); omap_clk_setrate(clk, 1 << ((value >> 10) & 3), 1); } - if (diff & (3 << 8)) { /* TCDIV */ + if (diff & (3 << 8)) { /* TCDIV */ clk = omap_findclk(s, "tc_ck"); omap_clk_setrate(clk, 1 << ((value >> 8) & 3), 1); } - if (diff & (3 << 6)) { /* DSPDIV */ + if (diff & (3 << 6)) { /* DSPDIV */ clk = omap_findclk(s, "dsp_ck"); omap_clk_setrate(clk, 1 << ((value >> 6) & 3), 1); } - if (diff & (3 << 4)) { /* ARMDIV */ + if (diff & (3 << 4)) { /* ARMDIV */ clk = omap_findclk(s, "arm_ck"); omap_clk_setrate(clk, 1 << ((value >> 4) & 3), 1); } - if (diff & (3 << 2)) { /* LCDDIV */ + if (diff & (3 << 2)) { /* LCDDIV */ clk = omap_findclk(s, "lcd_ck"); omap_clk_setrate(clk, 1 << ((value >> 2) & 3), 1); } - if (diff & (3 << 0)) { /* PERDIV */ + if (diff & (3 << 0)) { /* PERDIV */ clk = omap_findclk(s, "armper_ck"); omap_clk_setrate(clk, 1 << ((value >> 0) & 3), 1); } @@ -1563,25 +1566,25 @@ static inline void omap_clkm_idlect1_update(struct omap_mpu_state_s *s, qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } -#define SET_CANIDLE(clock, bit) \ - if (diff & (1 << bit)) { \ - clk = omap_findclk(s, clock); \ - omap_clk_canidle(clk, (value >> bit) & 1); \ +#define SET_CANIDLE(clock, bit) \ + if (diff & (1 << bit)) { \ + clk = omap_findclk(s, clock); \ + omap_clk_canidle(clk, (value >> bit) & 1); \ } - SET_CANIDLE("mpuwd_ck", 0) /* IDLWDT_ARM */ - SET_CANIDLE("armxor_ck", 1) /* IDLXORP_ARM */ - SET_CANIDLE("mpuper_ck", 2) /* IDLPER_ARM */ - SET_CANIDLE("lcd_ck", 3) /* IDLLCD_ARM */ - SET_CANIDLE("lb_ck", 4) /* IDLLB_ARM */ - SET_CANIDLE("hsab_ck", 5) /* IDLHSAB_ARM */ - SET_CANIDLE("tipb_ck", 6) /* IDLIF_ARM */ - SET_CANIDLE("dma_ck", 6) /* IDLIF_ARM */ - SET_CANIDLE("tc_ck", 6) /* IDLIF_ARM */ - SET_CANIDLE("dpll1", 7) /* IDLDPLL_ARM */ - SET_CANIDLE("dpll2", 7) /* IDLDPLL_ARM */ - SET_CANIDLE("dpll3", 7) /* IDLDPLL_ARM */ - SET_CANIDLE("mpui_ck", 8) /* IDLAPI_ARM */ - SET_CANIDLE("armtim_ck", 9) /* IDLTIM_ARM */ + SET_CANIDLE("mpuwd_ck", 0) /* IDLWDT_ARM */ + SET_CANIDLE("armxor_ck", 1) /* IDLXORP_ARM */ + SET_CANIDLE("mpuper_ck", 2) /* IDLPER_ARM */ + SET_CANIDLE("lcd_ck", 3) /* IDLLCD_ARM */ + SET_CANIDLE("lb_ck", 4) /* IDLLB_ARM */ + SET_CANIDLE("hsab_ck", 5) /* IDLHSAB_ARM */ + SET_CANIDLE("tipb_ck", 6) /* IDLIF_ARM */ + SET_CANIDLE("dma_ck", 6) /* IDLIF_ARM */ + SET_CANIDLE("tc_ck", 6) /* IDLIF_ARM */ + SET_CANIDLE("dpll1", 7) /* IDLDPLL_ARM */ + SET_CANIDLE("dpll2", 7) /* IDLDPLL_ARM */ + SET_CANIDLE("dpll3", 7) /* IDLDPLL_ARM */ + SET_CANIDLE("mpui_ck", 8) /* IDLAPI_ARM */ + SET_CANIDLE("armtim_ck", 9) /* IDLTIM_ARM */ } static inline void omap_clkm_idlect2_update(struct omap_mpu_state_s *s, @@ -1589,22 +1592,22 @@ static inline void omap_clkm_idlect2_update(struct omap_mpu_state_s *s, { omap_clk clk; -#define SET_ONOFF(clock, bit) \ - if (diff & (1 << bit)) { \ - clk = omap_findclk(s, clock); \ - omap_clk_onoff(clk, (value >> bit) & 1); \ +#define SET_ONOFF(clock, bit) \ + if (diff & (1 << bit)) { \ + clk = omap_findclk(s, clock); \ + omap_clk_onoff(clk, (value >> bit) & 1); \ } - SET_ONOFF("mpuwd_ck", 0) /* EN_WDTCK */ - SET_ONOFF("armxor_ck", 1) /* EN_XORPCK */ - SET_ONOFF("mpuper_ck", 2) /* EN_PERCK */ - SET_ONOFF("lcd_ck", 3) /* EN_LCDCK */ - SET_ONOFF("lb_ck", 4) /* EN_LBCK */ - SET_ONOFF("hsab_ck", 5) /* EN_HSABCK */ - SET_ONOFF("mpui_ck", 6) /* EN_APICK */ - SET_ONOFF("armtim_ck", 7) /* EN_TIMCK */ - SET_CANIDLE("dma_ck", 8) /* DMACK_REQ */ - SET_ONOFF("arm_gpio_ck", 9) /* EN_GPIOCK */ - SET_ONOFF("lbfree_ck", 10) /* EN_LBFREECK */ + SET_ONOFF("mpuwd_ck", 0) /* EN_WDTCK */ + SET_ONOFF("armxor_ck", 1) /* EN_XORPCK */ + SET_ONOFF("mpuper_ck", 2) /* EN_PERCK */ + SET_ONOFF("lcd_ck", 3) /* EN_LCDCK */ + SET_ONOFF("lb_ck", 4) /* EN_LBCK */ + SET_ONOFF("hsab_ck", 5) /* EN_HSABCK */ + SET_ONOFF("mpui_ck", 6) /* EN_APICK */ + SET_ONOFF("armtim_ck", 7) /* EN_TIMCK */ + SET_CANIDLE("dma_ck", 8) /* DMACK_REQ */ + SET_ONOFF("arm_gpio_ck", 9) /* EN_GPIOCK */ + SET_ONOFF("lbfree_ck", 10) /* EN_LBFREECK */ } static inline void omap_clkm_ckout1_update(struct omap_mpu_state_s *s, @@ -1612,7 +1615,7 @@ static inline void omap_clkm_ckout1_update(struct omap_mpu_state_s *s, { omap_clk clk; - if (diff & (3 << 4)) { /* TCLKOUT */ + if (diff & (3 << 4)) { /* TCLKOUT */ clk = omap_findclk(s, "tclk_out"); switch ((value >> 4) & 3) { case 1: @@ -1627,7 +1630,7 @@ static inline void omap_clkm_ckout1_update(struct omap_mpu_state_s *s, omap_clk_onoff(clk, 0); } } - if (diff & (3 << 2)) { /* DCLKOUT */ + if (diff & (3 << 2)) { /* DCLKOUT */ clk = omap_findclk(s, "dclk_out"); switch ((value >> 2) & 3) { case 0: @@ -1644,7 +1647,7 @@ static inline void omap_clkm_ckout1_update(struct omap_mpu_state_s *s, break; } } - if (diff & (3 << 0)) { /* ACLKOUT */ + if (diff & (3 << 0)) { /* ACLKOUT */ clk = omap_findclk(s, "aclk_out"); switch ((value >> 0) & 3) { case 1: @@ -1682,66 +1685,66 @@ static void omap_clkm_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* ARM_CKCTL */ + case 0x00: /* ARM_CKCTL */ diff = s->clkm.arm_ckctl ^ value; s->clkm.arm_ckctl = value & 0x7fff; omap_clkm_ckctl_update(s, diff, value); return; - case 0x04: /* ARM_IDLECT1 */ + case 0x04: /* ARM_IDLECT1 */ diff = s->clkm.arm_idlect1 ^ value; s->clkm.arm_idlect1 = value & 0x0fff; omap_clkm_idlect1_update(s, diff, value); return; - case 0x08: /* ARM_IDLECT2 */ + case 0x08: /* ARM_IDLECT2 */ diff = s->clkm.arm_idlect2 ^ value; s->clkm.arm_idlect2 = value & 0x07ff; omap_clkm_idlect2_update(s, diff, value); return; - case 0x0c: /* ARM_EWUPCT */ + case 0x0c: /* ARM_EWUPCT */ s->clkm.arm_ewupct = value & 0x003f; return; - case 0x10: /* ARM_RSTCT1 */ + case 0x10: /* ARM_RSTCT1 */ diff = s->clkm.arm_rstct1 ^ value; s->clkm.arm_rstct1 = value & 0x0007; if (value & 9) { qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); s->clkm.cold_start = 0xa; } - if (diff & ~value & 4) { /* DSP_RST */ + if (diff & ~value & 4) { /* DSP_RST */ omap_mpui_reset(s); omap_tipb_bridge_reset(s->private_tipb); omap_tipb_bridge_reset(s->public_tipb); } - if (diff & 2) { /* DSP_EN */ + if (diff & 2) { /* DSP_EN */ clk = omap_findclk(s, "dsp_ck"); omap_clk_canidle(clk, (~value >> 1) & 1); } return; - case 0x14: /* ARM_RSTCT2 */ + case 0x14: /* ARM_RSTCT2 */ s->clkm.arm_rstct2 = value & 0x0001; return; - case 0x18: /* ARM_SYSST */ + case 0x18: /* ARM_SYSST */ if ((s->clkm.clocking_scheme ^ (value >> 11)) & 7) { s->clkm.clocking_scheme = (value >> 11) & 7; - printf("%s: clocking scheme set to %s\n", __func__, + trace_omap1_pwl_clocking_scheme( clkschemename[s->clkm.clocking_scheme]); } s->clkm.cold_start &= value & 0x3f; return; - case 0x1c: /* ARM_CKOUT1 */ + case 0x1c: /* ARM_CKOUT1 */ diff = s->clkm.arm_ckout1 ^ value; s->clkm.arm_ckout1 = value & 0x003f; omap_clkm_ckout1_update(s, diff, value); return; - case 0x20: /* ARM_CKOUT2 */ + case 0x20: /* ARM_CKOUT2 */ default: OMAP_BAD_REG(addr); } @@ -1764,16 +1767,16 @@ static uint64_t omap_clkdsp_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x04: /* DSP_IDLECT1 */ + case 0x04: /* DSP_IDLECT1 */ return s->clkm.dsp_idlect1; - case 0x08: /* DSP_IDLECT2 */ + case 0x08: /* DSP_IDLECT2 */ return s->clkm.dsp_idlect2; - case 0x14: /* DSP_RSTCT2 */ + case 0x14: /* DSP_RSTCT2 */ return s->clkm.dsp_rstct2; - case 0x18: /* DSP_SYSST */ + case 0x18: /* DSP_SYSST */ return (s->clkm.clocking_scheme << 11) | s->clkm.cold_start | (cpu->halted << 6); /* Quite useless... */ } @@ -1787,7 +1790,7 @@ static inline void omap_clkdsp_idlect1_update(struct omap_mpu_state_s *s, { omap_clk clk; - SET_CANIDLE("dspxor_ck", 1); /* IDLXORP_DSP */ + SET_CANIDLE("dspxor_ck", 1); /* IDLXORP_DSP */ } static inline void omap_clkdsp_idlect2_update(struct omap_mpu_state_s *s, @@ -1795,7 +1798,7 @@ static inline void omap_clkdsp_idlect2_update(struct omap_mpu_state_s *s, { omap_clk clk; - SET_ONOFF("dspxor_ck", 1); /* EN_XORPCK */ + SET_ONOFF("dspxor_ck", 1); /* EN_XORPCK */ } static void omap_clkdsp_write(void *opaque, hwaddr addr, @@ -1810,23 +1813,23 @@ static void omap_clkdsp_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x04: /* DSP_IDLECT1 */ + case 0x04: /* DSP_IDLECT1 */ diff = s->clkm.dsp_idlect1 ^ value; s->clkm.dsp_idlect1 = value & 0x01f7; omap_clkdsp_idlect1_update(s, diff, value); break; - case 0x08: /* DSP_IDLECT2 */ + case 0x08: /* DSP_IDLECT2 */ s->clkm.dsp_idlect2 = value & 0x0037; diff = s->clkm.dsp_idlect1 ^ value; omap_clkdsp_idlect2_update(s, diff, value); break; - case 0x14: /* DSP_RSTCT2 */ + case 0x14: /* DSP_RSTCT2 */ s->clkm.dsp_rstct2 = value & 0x0001; break; - case 0x18: /* DSP_SYSST */ + case 0x18: /* DSP_SYSST */ s->clkm.cold_start &= value & 0x3f; break; @@ -1925,8 +1928,8 @@ static void omap_mpuio_set(void *opaque, int line, int level) qemu_irq_raise(s->irq); /* TODO: wakeup */ } - if ((s->event & (1 << 0)) && /* SET_GPIO_EVENT_MODE */ - (s->event >> 1) == line) /* PIN_SELECT */ + if ((s->event & (1 << 0)) && /* SET_GPIO_EVENT_MODE */ + (s->event >> 1) == line) /* PIN_SELECT */ s->latch = s->inputs; } } @@ -1956,47 +1959,47 @@ static uint64_t omap_mpuio_read(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* INPUT_LATCH */ + case 0x00: /* INPUT_LATCH */ return s->inputs; - case 0x04: /* OUTPUT_REG */ + case 0x04: /* OUTPUT_REG */ return s->outputs; - case 0x08: /* IO_CNTL */ + case 0x08: /* IO_CNTL */ return s->dir; - case 0x10: /* KBR_LATCH */ + case 0x10: /* KBR_LATCH */ return s->row_latch; - case 0x14: /* KBC_REG */ + case 0x14: /* KBC_REG */ return s->cols; - case 0x18: /* GPIO_EVENT_MODE_REG */ + case 0x18: /* GPIO_EVENT_MODE_REG */ return s->event; - case 0x1c: /* GPIO_INT_EDGE_REG */ + case 0x1c: /* GPIO_INT_EDGE_REG */ return s->edge; - case 0x20: /* KBD_INT */ + case 0x20: /* KBD_INT */ return (~s->row_latch & 0x1f) && !s->kbd_mask; - case 0x24: /* GPIO_INT */ + case 0x24: /* GPIO_INT */ ret = s->ints; s->ints &= s->mask; if (ret) qemu_irq_lower(s->irq); return ret; - case 0x28: /* KBD_MASKIT */ + case 0x28: /* KBD_MASKIT */ return s->kbd_mask; - case 0x2c: /* GPIO_MASKIT */ + case 0x2c: /* GPIO_MASKIT */ return s->mask; - case 0x30: /* GPIO_DEBOUNCING_REG */ + case 0x30: /* GPIO_DEBOUNCING_REG */ return s->debounce; - case 0x34: /* GPIO_LATCH_REG */ + case 0x34: /* GPIO_LATCH_REG */ return s->latch; } @@ -2018,7 +2021,7 @@ static void omap_mpuio_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x04: /* OUTPUT_REG */ + case 0x04: /* OUTPUT_REG */ diff = (s->outputs ^ value) & ~s->dir; s->outputs = value; while ((ln = ctz32(diff)) != 32) { @@ -2028,7 +2031,7 @@ static void omap_mpuio_write(void *opaque, hwaddr addr, } break; - case 0x08: /* IO_CNTL */ + case 0x08: /* IO_CNTL */ diff = s->outputs & (s->dir ^ value); s->dir = value; @@ -2040,37 +2043,37 @@ static void omap_mpuio_write(void *opaque, hwaddr addr, } break; - case 0x14: /* KBC_REG */ + case 0x14: /* KBC_REG */ s->cols = value; omap_mpuio_kbd_update(s); break; - case 0x18: /* GPIO_EVENT_MODE_REG */ + case 0x18: /* GPIO_EVENT_MODE_REG */ s->event = value & 0x1f; break; - case 0x1c: /* GPIO_INT_EDGE_REG */ + case 0x1c: /* GPIO_INT_EDGE_REG */ s->edge = value; break; - case 0x28: /* KBD_MASKIT */ + case 0x28: /* KBD_MASKIT */ s->kbd_mask = value & 1; omap_mpuio_kbd_update(s); break; - case 0x2c: /* GPIO_MASKIT */ + case 0x2c: /* GPIO_MASKIT */ s->mask = value; break; - case 0x30: /* GPIO_DEBOUNCING_REG */ + case 0x30: /* GPIO_DEBOUNCING_REG */ s->debounce = value & 0x1ff; break; - case 0x00: /* INPUT_LATCH */ - case 0x10: /* KBR_LATCH */ - case 0x20: /* KBD_INT */ - case 0x24: /* GPIO_INT */ - case 0x34: /* GPIO_LATCH_REG */ + case 0x00: /* INPUT_LATCH */ + case 0x10: /* KBR_LATCH */ + case 0x20: /* KBD_INT */ + case 0x24: /* GPIO_INT */ + case 0x34: /* GPIO_LATCH_REG */ OMAP_RO_REG(addr); return; @@ -2169,30 +2172,28 @@ 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]; + int chipselect = (s->control >> 10) & 3; /* INDEX */ - 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))); - s->control &= ~(1 << 14); /* CSRB */ + if ((s->control >> 5) & 0x1f) { /* NB_BITS_WR */ + 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); - s->control |= 1 << 15; /* RDRB */ + if ((s->control >> 0) & 0x1f) { /* NB_BITS_RD */ + 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? */ } @@ -2208,22 +2209,22 @@ static uint64_t omap_uwire_read(void *opaque, hwaddr addr, unsigned size) } switch (offset) { - case 0x00: /* RDR */ - s->control &= ~(1 << 15); /* RDRB */ + case 0x00: /* RDR */ + s->control &= ~(1 << 15); /* RDRB */ return s->rxbuf; - case 0x04: /* CSR */ + case 0x04: /* CSR */ return s->control; - case 0x08: /* SR1 */ + case 0x08: /* SR1 */ return s->setup[0]; - case 0x0c: /* SR2 */ + case 0x0c: /* SR2 */ return s->setup[1]; - case 0x10: /* SR3 */ + case 0x10: /* SR3 */ return s->setup[2]; - case 0x14: /* SR4 */ + case 0x14: /* SR4 */ return s->setup[3]; - case 0x18: /* SR5 */ + case 0x18: /* SR5 */ return s->setup[4]; } @@ -2243,39 +2244,39 @@ static void omap_uwire_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* TDR */ - s->txbuf = value; /* TD */ - if ((s->setup[4] & (1 << 2)) && /* AUTO_TX_EN */ - ((s->setup[4] & (1 << 3)) || /* CS_TOGGLE_TX_EN */ - (s->control & (1 << 12)))) { /* CS_CMD */ - s->control |= 1 << 14; /* CSRB */ + case 0x00: /* TDR */ + s->txbuf = value; /* TD */ + if ((s->setup[4] & (1 << 2)) && /* AUTO_TX_EN */ + ((s->setup[4] & (1 << 3)) || /* CS_TOGGLE_TX_EN */ + (s->control & (1 << 12)))) { /* CS_CMD */ + s->control |= 1 << 14; /* CSRB */ omap_uwire_transfer_start(s); } break; - case 0x04: /* CSR */ + case 0x04: /* CSR */ s->control = value & 0x1fff; - if (value & (1 << 13)) /* START */ + if (value & (1 << 13)) /* START */ omap_uwire_transfer_start(s); break; - case 0x08: /* SR1 */ + case 0x08: /* SR1 */ s->setup[0] = value & 0x003f; break; - case 0x0c: /* SR2 */ + case 0x0c: /* SR2 */ s->setup[1] = value & 0x0fc0; break; - case 0x10: /* SR3 */ + case 0x10: /* SR3 */ s->setup[2] = value & 0x0003; break; - case 0x14: /* SR4 */ + case 0x14: /* SR4 */ s->setup[3] = value & 0x0001; break; - case 0x18: /* SR5 */ + case 0x18: /* SR5 */ s->setup[4] = value & 0x000f; break; @@ -2320,17 +2321,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; @@ -2346,7 +2336,7 @@ static void omap_pwl_update(struct omap_pwl_s *s) if (output != s->output) { s->output = output; - printf("%s: Backlight now at %i/256\n", __func__, output); + trace_omap1_pwl_backlight(output); } } @@ -2360,9 +2350,9 @@ static uint64_t omap_pwl_read(void *opaque, hwaddr addr, unsigned size) } switch (offset) { - case 0x00: /* PWL_LEVEL */ + case 0x00: /* PWL_LEVEL */ return s->level; - case 0x04: /* PWL_CTRL */ + case 0x04: /* PWL_CTRL */ return s->enable; } OMAP_BAD_REG(addr); @@ -2381,11 +2371,11 @@ static void omap_pwl_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* PWL_LEVEL */ + case 0x00: /* PWL_LEVEL */ s->level = value; omap_pwl_update(s); break; - case 0x04: /* PWL_CTRL */ + case 0x04: /* PWL_CTRL */ s->enable = value & 1; omap_pwl_update(s); break; @@ -2453,11 +2443,11 @@ static uint64_t omap_pwt_read(void *opaque, hwaddr addr, unsigned size) } switch (offset) { - case 0x00: /* FRC */ + case 0x00: /* FRC */ return s->frc; - case 0x04: /* VCR */ + case 0x04: /* VCR */ return s->vrc; - case 0x08: /* GCR */ + case 0x08: /* GCR */ return s->gcr; } OMAP_BAD_REG(addr); @@ -2476,13 +2466,13 @@ static void omap_pwt_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* FRC */ + case 0x00: /* FRC */ s->frc = value & 0x3f; break; - case 0x04: /* VRC */ + case 0x04: /* VRC */ if ((value ^ s->vrc) & 1) { - if (value & 1) - printf("%s: %iHz buzz on\n", __func__, (int) + if (value & 1) { + trace_omap1_pwt_buzz( /* 1.5 MHz from a 12-MHz or 13-MHz PWT_CLK */ ((omap_clk_getrate(s->clk) >> 3) / /* Pre-multiplexer divider */ @@ -2498,12 +2488,13 @@ static void omap_pwt_write(void *opaque, hwaddr addr, /* 80/127 divider */ ((value & (1 << 5)) ? 80 : 127) / (107 * 55 * 63 * 127))); - else - printf("%s: silence!\n", __func__); + } else { + trace_omap1_pwt_silence(); + } } s->vrc = value & 0x7f; break; - case 0x08: /* GCR */ + case 0x08: /* GCR */ s->gcr = value & 3; break; default: @@ -2570,8 +2561,9 @@ static void omap_rtc_interrupts_update(struct omap_rtc_s *s) static void omap_rtc_alarm_update(struct omap_rtc_s *s) { s->alarm_ti = mktimegm(&s->alarm_tm); - if (s->alarm_ti == -1) - printf("%s: conversion failed\n", __func__); + if (s->alarm_ti == -1) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: conversion failed\n", __func__); + } } static uint64_t omap_rtc_read(void *opaque, hwaddr addr, unsigned size) @@ -2585,69 +2577,69 @@ static uint64_t omap_rtc_read(void *opaque, hwaddr addr, unsigned size) } switch (offset) { - case 0x00: /* SECONDS_REG */ + case 0x00: /* SECONDS_REG */ return to_bcd(s->current_tm.tm_sec); - case 0x04: /* MINUTES_REG */ + case 0x04: /* MINUTES_REG */ return to_bcd(s->current_tm.tm_min); - case 0x08: /* HOURS_REG */ + case 0x08: /* HOURS_REG */ if (s->pm_am) return ((s->current_tm.tm_hour > 11) << 7) | to_bcd(((s->current_tm.tm_hour - 1) % 12) + 1); else return to_bcd(s->current_tm.tm_hour); - case 0x0c: /* DAYS_REG */ + case 0x0c: /* DAYS_REG */ return to_bcd(s->current_tm.tm_mday); - case 0x10: /* MONTHS_REG */ + case 0x10: /* MONTHS_REG */ return to_bcd(s->current_tm.tm_mon + 1); - case 0x14: /* YEARS_REG */ + case 0x14: /* YEARS_REG */ return to_bcd(s->current_tm.tm_year % 100); - case 0x18: /* WEEK_REG */ + case 0x18: /* WEEK_REG */ return s->current_tm.tm_wday; - case 0x20: /* ALARM_SECONDS_REG */ + case 0x20: /* ALARM_SECONDS_REG */ return to_bcd(s->alarm_tm.tm_sec); - case 0x24: /* ALARM_MINUTES_REG */ + case 0x24: /* ALARM_MINUTES_REG */ return to_bcd(s->alarm_tm.tm_min); - case 0x28: /* ALARM_HOURS_REG */ + case 0x28: /* ALARM_HOURS_REG */ if (s->pm_am) return ((s->alarm_tm.tm_hour > 11) << 7) | to_bcd(((s->alarm_tm.tm_hour - 1) % 12) + 1); else return to_bcd(s->alarm_tm.tm_hour); - case 0x2c: /* ALARM_DAYS_REG */ + case 0x2c: /* ALARM_DAYS_REG */ return to_bcd(s->alarm_tm.tm_mday); - case 0x30: /* ALARM_MONTHS_REG */ + case 0x30: /* ALARM_MONTHS_REG */ return to_bcd(s->alarm_tm.tm_mon + 1); - case 0x34: /* ALARM_YEARS_REG */ + case 0x34: /* ALARM_YEARS_REG */ return to_bcd(s->alarm_tm.tm_year % 100); - case 0x40: /* RTC_CTRL_REG */ + case 0x40: /* RTC_CTRL_REG */ return (s->pm_am << 3) | (s->auto_comp << 2) | (s->round << 1) | s->running; - case 0x44: /* RTC_STATUS_REG */ + case 0x44: /* RTC_STATUS_REG */ i = s->status; s->status &= ~0x3d; return i; - case 0x48: /* RTC_INTERRUPTS_REG */ + case 0x48: /* RTC_INTERRUPTS_REG */ return s->interrupts; - case 0x4c: /* RTC_COMP_LSB_REG */ + case 0x4c: /* RTC_COMP_LSB_REG */ return ((uint16_t) s->comp_reg) & 0xff; - case 0x50: /* RTC_COMP_MSB_REG */ + case 0x50: /* RTC_COMP_MSB_REG */ return ((uint16_t) s->comp_reg) >> 8; } @@ -2669,26 +2661,17 @@ static void omap_rtc_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* SECONDS_REG */ -#ifdef ALMDEBUG - printf("RTC SEC_REG <-- %02x\n", value); -#endif + case 0x00: /* SECONDS_REG */ s->ti -= s->current_tm.tm_sec; s->ti += from_bcd(value); return; - case 0x04: /* MINUTES_REG */ -#ifdef ALMDEBUG - printf("RTC MIN_REG <-- %02x\n", value); -#endif + case 0x04: /* MINUTES_REG */ s->ti -= s->current_tm.tm_min * 60; s->ti += from_bcd(value) * 60; return; - case 0x08: /* HOURS_REG */ -#ifdef ALMDEBUG - printf("RTC HRS_REG <-- %02x\n", value); -#endif + case 0x08: /* HOURS_REG */ s->ti -= s->current_tm.tm_hour * 3600; if (s->pm_am) { s->ti += (from_bcd(value & 0x3f) & 12) * 3600; @@ -2697,18 +2680,12 @@ static void omap_rtc_write(void *opaque, hwaddr addr, s->ti += from_bcd(value & 0x3f) * 3600; return; - case 0x0c: /* DAYS_REG */ -#ifdef ALMDEBUG - printf("RTC DAY_REG <-- %02x\n", value); -#endif + case 0x0c: /* DAYS_REG */ s->ti -= s->current_tm.tm_mday * 86400; s->ti += from_bcd(value) * 86400; return; - case 0x10: /* MONTHS_REG */ -#ifdef ALMDEBUG - printf("RTC MTH_REG <-- %02x\n", value); -#endif + case 0x10: /* MONTHS_REG */ memcpy(&new_tm, &s->current_tm, sizeof(new_tm)); new_tm.tm_mon = from_bcd(value); ti[0] = mktimegm(&s->current_tm); @@ -2724,10 +2701,7 @@ static void omap_rtc_write(void *opaque, hwaddr addr, } return; - case 0x14: /* YEARS_REG */ -#ifdef ALMDEBUG - printf("RTC YRS_REG <-- %02x\n", value); -#endif + case 0x14: /* YEARS_REG */ memcpy(&new_tm, &s->current_tm, sizeof(new_tm)); new_tm.tm_year += from_bcd(value) - (new_tm.tm_year % 100); ti[0] = mktimegm(&s->current_tm); @@ -2743,29 +2717,20 @@ static void omap_rtc_write(void *opaque, hwaddr addr, } return; - case 0x18: /* WEEK_REG */ - return; /* Ignored */ + case 0x18: /* WEEK_REG */ + return; /* Ignored */ - case 0x20: /* ALARM_SECONDS_REG */ -#ifdef ALMDEBUG - printf("ALM SEC_REG <-- %02x\n", value); -#endif + case 0x20: /* ALARM_SECONDS_REG */ s->alarm_tm.tm_sec = from_bcd(value); omap_rtc_alarm_update(s); return; - case 0x24: /* ALARM_MINUTES_REG */ -#ifdef ALMDEBUG - printf("ALM MIN_REG <-- %02x\n", value); -#endif + case 0x24: /* ALARM_MINUTES_REG */ s->alarm_tm.tm_min = from_bcd(value); omap_rtc_alarm_update(s); return; - case 0x28: /* ALARM_HOURS_REG */ -#ifdef ALMDEBUG - printf("ALM HRS_REG <-- %02x\n", value); -#endif + case 0x28: /* ALARM_HOURS_REG */ if (s->pm_am) s->alarm_tm.tm_hour = ((from_bcd(value & 0x3f)) % 12) + @@ -2775,34 +2740,22 @@ static void omap_rtc_write(void *opaque, hwaddr addr, omap_rtc_alarm_update(s); return; - case 0x2c: /* ALARM_DAYS_REG */ -#ifdef ALMDEBUG - printf("ALM DAY_REG <-- %02x\n", value); -#endif + case 0x2c: /* ALARM_DAYS_REG */ s->alarm_tm.tm_mday = from_bcd(value); omap_rtc_alarm_update(s); return; - case 0x30: /* ALARM_MONTHS_REG */ -#ifdef ALMDEBUG - printf("ALM MON_REG <-- %02x\n", value); -#endif + case 0x30: /* ALARM_MONTHS_REG */ s->alarm_tm.tm_mon = from_bcd(value); omap_rtc_alarm_update(s); return; - case 0x34: /* ALARM_YEARS_REG */ -#ifdef ALMDEBUG - printf("ALM YRS_REG <-- %02x\n", value); -#endif + case 0x34: /* ALARM_YEARS_REG */ s->alarm_tm.tm_year = from_bcd(value); omap_rtc_alarm_update(s); return; - case 0x40: /* RTC_CTRL_REG */ -#ifdef ALMDEBUG - printf("RTC CONTROL <-- %02x\n", value); -#endif + case 0x40: /* RTC_CTRL_REG */ s->pm_am = (value >> 3) & 1; s->auto_comp = (value >> 2) & 1; s->round = (value >> 1) & 1; @@ -2811,33 +2764,21 @@ static void omap_rtc_write(void *opaque, hwaddr addr, s->status |= s->running << 1; return; - case 0x44: /* RTC_STATUS_REG */ -#ifdef ALMDEBUG - printf("RTC STATUSL <-- %02x\n", value); -#endif + case 0x44: /* RTC_STATUS_REG */ s->status &= ~((value & 0xc0) ^ 0x80); omap_rtc_interrupts_update(s); return; - case 0x48: /* RTC_INTERRUPTS_REG */ -#ifdef ALMDEBUG - printf("RTC INTRS <-- %02x\n", value); -#endif + case 0x48: /* RTC_INTERRUPTS_REG */ s->interrupts = value; return; - case 0x4c: /* RTC_COMP_LSB_REG */ -#ifdef ALMDEBUG - printf("RTC COMPLSB <-- %02x\n", value); -#endif + case 0x4c: /* RTC_COMP_LSB_REG */ s->comp_reg &= 0xff00; s->comp_reg |= 0x00ff & value; return; - case 0x50: /* RTC_COMP_MSB_REG */ -#ifdef ALMDEBUG - printf("RTC COMPMSB <-- %02x\n", value); -#endif + case 0x50: /* RTC_COMP_MSB_REG */ s->comp_reg &= 0x00ff; s->comp_reg |= 0xff00 & (value << 8); return; @@ -2988,12 +2929,12 @@ static void omap_mcbsp_intr_update(struct omap_mcbsp_s *s) { int irq; - switch ((s->spcr[0] >> 4) & 3) { /* RINTM */ + switch ((s->spcr[0] >> 4) & 3) { /* RINTM */ case 0: - irq = (s->spcr[0] >> 1) & 1; /* RRDY */ + irq = (s->spcr[0] >> 1) & 1; /* RRDY */ break; case 3: - irq = (s->spcr[0] >> 3) & 1; /* RSYNCERR */ + irq = (s->spcr[0] >> 3) & 1; /* RSYNCERR */ break; default: irq = 0; @@ -3003,12 +2944,12 @@ static void omap_mcbsp_intr_update(struct omap_mcbsp_s *s) if (irq) qemu_irq_pulse(s->rxirq); - switch ((s->spcr[1] >> 4) & 3) { /* XINTM */ + switch ((s->spcr[1] >> 4) & 3) { /* XINTM */ case 0: - irq = (s->spcr[1] >> 1) & 1; /* XRDY */ + irq = (s->spcr[1] >> 1) & 1; /* XRDY */ break; case 3: - irq = (s->spcr[1] >> 3) & 1; /* XSYNCERR */ + irq = (s->spcr[1] >> 3) & 1; /* XSYNCERR */ break; default: irq = 0; @@ -3021,9 +2962,9 @@ static void omap_mcbsp_intr_update(struct omap_mcbsp_s *s) static void omap_mcbsp_rx_newdata(struct omap_mcbsp_s *s) { - if ((s->spcr[0] >> 1) & 1) /* RRDY */ - s->spcr[0] |= 1 << 2; /* RFULL */ - s->spcr[0] |= 1 << 1; /* RRDY */ + if ((s->spcr[0] >> 1) & 1) /* RRDY */ + s->spcr[0] |= 1 << 2; /* RFULL */ + s->spcr[0] |= 1 << 1; /* RRDY */ qemu_irq_raise(s->rxdrq); omap_mcbsp_intr_update(s); } @@ -3035,8 +2976,9 @@ static void omap_mcbsp_source_tick(void *opaque) if (!s->rx_rate) return; - if (s->rx_req) - printf("%s: Rx FIFO overrun\n", __func__); + if (s->rx_req) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Rx FIFO overrun\n", __func__); + } s->rx_req = s->rx_rate << bps[(s->rcr[0] >> 5) & 7]; @@ -3062,14 +3004,14 @@ static void omap_mcbsp_rx_stop(struct omap_mcbsp_s *s) static void omap_mcbsp_rx_done(struct omap_mcbsp_s *s) { - s->spcr[0] &= ~(1 << 1); /* RRDY */ + s->spcr[0] &= ~(1 << 1); /* RRDY */ qemu_irq_lower(s->rxdrq); omap_mcbsp_intr_update(s); } static void omap_mcbsp_tx_newdata(struct omap_mcbsp_s *s) { - s->spcr[1] |= 1 << 1; /* XRDY */ + s->spcr[1] |= 1 << 1; /* XRDY */ qemu_irq_raise(s->txdrq); omap_mcbsp_intr_update(s); } @@ -3081,8 +3023,9 @@ static void omap_mcbsp_sink_tick(void *opaque) if (!s->tx_rate) return; - if (s->tx_req) - printf("%s: Tx FIFO underrun\n", __func__); + if (s->tx_req) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Tx FIFO underrun\n", __func__); + } s->tx_req = s->tx_rate << bps[(s->xcr[0] >> 5) & 7]; @@ -3103,7 +3046,7 @@ static void omap_mcbsp_tx_start(struct omap_mcbsp_s *s) static void omap_mcbsp_tx_done(struct omap_mcbsp_s *s) { - s->spcr[1] &= ~(1 << 1); /* XRDY */ + s->spcr[1] &= ~(1 << 1); /* XRDY */ qemu_irq_lower(s->txdrq); omap_mcbsp_intr_update(s); if (s->codec && s->codec->cts) @@ -3121,27 +3064,27 @@ static void omap_mcbsp_req_update(struct omap_mcbsp_s *s) { int prev_rx_rate, prev_tx_rate; int rx_rate = 0, tx_rate = 0; - int cpu_rate = 1500000; /* XXX */ + int cpu_rate = 1500000; /* XXX */ /* TODO: check CLKSTP bit */ - if (s->spcr[1] & (1 << 6)) { /* GRST */ - if (s->spcr[0] & (1 << 0)) { /* RRST */ - if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ - (s->pcr & (1 << 8))) { /* CLKRM */ - if (~s->pcr & (1 << 7)) /* SCLKME */ + if (s->spcr[1] & (1 << 6)) { /* GRST */ + if (s->spcr[0] & (1 << 0)) { /* RRST */ + if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ + (s->pcr & (1 << 8))) { /* CLKRM */ + if (~s->pcr & (1 << 7)) /* SCLKME */ rx_rate = cpu_rate / - ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ + ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ } else if (s->codec) rx_rate = s->codec->rx_rate; } - if (s->spcr[1] & (1 << 0)) { /* XRST */ - if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ - (s->pcr & (1 << 9))) { /* CLKXM */ - if (~s->pcr & (1 << 7)) /* SCLKME */ + if (s->spcr[1] & (1 << 0)) { /* XRST */ + if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ + (s->pcr & (1 << 9))) { /* CLKXM */ + if (~s->pcr & (1 << 7)) /* SCLKME */ tx_rate = cpu_rate / - ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ + ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ } else if (s->codec) tx_rate = s->codec->tx_rate; @@ -3178,13 +3121,13 @@ static uint64_t omap_mcbsp_read(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* DRR2 */ - if (((s->rcr[0] >> 5) & 7) < 3) /* RWDLEN1 */ + case 0x00: /* DRR2 */ + if (((s->rcr[0] >> 5) & 7) < 3) /* RWDLEN1 */ return 0x0000; /* Fall through. */ - case 0x02: /* DRR1 */ + case 0x02: /* DRR1 */ if (s->rx_req < 2) { - printf("%s: Rx FIFO underrun\n", __func__); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Rx FIFO underrun\n", __func__); omap_mcbsp_rx_done(s); } else { s->tx_req -= 2; @@ -3200,63 +3143,63 @@ static uint64_t omap_mcbsp_read(void *opaque, hwaddr addr, } return 0x0000; - case 0x04: /* DXR2 */ - case 0x06: /* DXR1 */ + case 0x04: /* DXR2 */ + case 0x06: /* DXR1 */ return 0x0000; - case 0x08: /* SPCR2 */ + case 0x08: /* SPCR2 */ return s->spcr[1]; - case 0x0a: /* SPCR1 */ + case 0x0a: /* SPCR1 */ return s->spcr[0]; - case 0x0c: /* RCR2 */ + case 0x0c: /* RCR2 */ return s->rcr[1]; - case 0x0e: /* RCR1 */ + case 0x0e: /* RCR1 */ return s->rcr[0]; - case 0x10: /* XCR2 */ + case 0x10: /* XCR2 */ return s->xcr[1]; - case 0x12: /* XCR1 */ + case 0x12: /* XCR1 */ return s->xcr[0]; - case 0x14: /* SRGR2 */ + case 0x14: /* SRGR2 */ return s->srgr[1]; - case 0x16: /* SRGR1 */ + case 0x16: /* SRGR1 */ return s->srgr[0]; - case 0x18: /* MCR2 */ + case 0x18: /* MCR2 */ return s->mcr[1]; - case 0x1a: /* MCR1 */ + case 0x1a: /* MCR1 */ return s->mcr[0]; - case 0x1c: /* RCERA */ + case 0x1c: /* RCERA */ return s->rcer[0]; - case 0x1e: /* RCERB */ + case 0x1e: /* RCERB */ return s->rcer[1]; - case 0x20: /* XCERA */ + case 0x20: /* XCERA */ return s->xcer[0]; - case 0x22: /* XCERB */ + case 0x22: /* XCERB */ return s->xcer[1]; - case 0x24: /* PCR0 */ + case 0x24: /* PCR0 */ return s->pcr; - case 0x26: /* RCERC */ + case 0x26: /* RCERC */ return s->rcer[2]; - case 0x28: /* RCERD */ + case 0x28: /* RCERD */ return s->rcer[3]; - case 0x2a: /* XCERC */ + case 0x2a: /* XCERC */ return s->xcer[2]; - case 0x2c: /* XCERD */ + case 0x2c: /* XCERD */ return s->xcer[3]; - case 0x2e: /* RCERE */ + case 0x2e: /* RCERE */ return s->rcer[4]; - case 0x30: /* RCERF */ + case 0x30: /* RCERF */ return s->rcer[5]; - case 0x32: /* XCERE */ + case 0x32: /* XCERE */ return s->xcer[4]; - case 0x34: /* XCERF */ + case 0x34: /* XCERF */ return s->xcer[5]; - case 0x36: /* RCERG */ + case 0x36: /* RCERG */ return s->rcer[6]; - case 0x38: /* RCERH */ + case 0x38: /* RCERH */ return s->rcer[7]; - case 0x3a: /* XCERG */ + case 0x3a: /* XCERG */ return s->xcer[6]; - case 0x3c: /* XCERH */ + case 0x3c: /* XCERH */ return s->xcer[7]; } @@ -3271,16 +3214,16 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr, int offset = addr & OMAP_MPUI_REG_MASK; switch (offset) { - case 0x00: /* DRR2 */ - case 0x02: /* DRR1 */ + case 0x00: /* DRR2 */ + case 0x02: /* DRR1 */ OMAP_RO_REG(addr); return; - case 0x04: /* DXR2 */ - if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */ + case 0x04: /* DXR2 */ + if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */ return; /* Fall through. */ - case 0x06: /* DXR1 */ + case 0x06: /* DXR1 */ if (s->tx_req > 1) { s->tx_req -= 2; if (s->codec && s->codec->cts) { @@ -3289,24 +3232,28 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr, } if (s->tx_req < 2) omap_mcbsp_tx_done(s); - } else - printf("%s: Tx FIFO overrun\n", __func__); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Tx FIFO overrun\n", __func__); + } return; - case 0x08: /* SPCR2 */ + case 0x08: /* SPCR2 */ s->spcr[1] &= 0x0002; s->spcr[1] |= 0x03f9 & value; - s->spcr[1] |= 0x0004 & (value << 2); /* XEMPTY := XRST */ - if (~value & 1) /* XRST */ + s->spcr[1] |= 0x0004 & (value << 2); /* XEMPTY := XRST */ + if (~value & 1) /* XRST */ s->spcr[1] &= ~6; omap_mcbsp_req_update(s); return; - case 0x0a: /* SPCR1 */ + case 0x0a: /* SPCR1 */ s->spcr[0] &= 0x0006; s->spcr[0] |= 0xf8f9 & value; - if (value & (1 << 15)) /* DLB */ - printf("%s: Digital Loopback mode enable attempt\n", __func__); - if (~value & 1) { /* RRST */ + if (value & (1 << 15)) { /* DLB */ + qemu_log_mask(LOG_UNIMP, + "%s: Digital Loopback mode enable attempt\n", + __func__); + } + if (~value & 1) { /* RRST */ s->spcr[0] &= ~6; s->rx_req = 0; omap_mcbsp_rx_done(s); @@ -3314,85 +3261,91 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr, omap_mcbsp_req_update(s); return; - case 0x0c: /* RCR2 */ + case 0x0c: /* RCR2 */ s->rcr[1] = value & 0xffff; return; - case 0x0e: /* RCR1 */ + case 0x0e: /* RCR1 */ s->rcr[0] = value & 0x7fe0; return; - case 0x10: /* XCR2 */ + case 0x10: /* XCR2 */ s->xcr[1] = value & 0xffff; return; - case 0x12: /* XCR1 */ + case 0x12: /* XCR1 */ s->xcr[0] = value & 0x7fe0; return; - case 0x14: /* SRGR2 */ + case 0x14: /* SRGR2 */ s->srgr[1] = value & 0xffff; omap_mcbsp_req_update(s); return; - case 0x16: /* SRGR1 */ + case 0x16: /* SRGR1 */ s->srgr[0] = value & 0xffff; omap_mcbsp_req_update(s); return; - case 0x18: /* MCR2 */ + case 0x18: /* MCR2 */ s->mcr[1] = value & 0x03e3; - if (value & 3) /* XMCM */ - printf("%s: Tx channel selection mode enable attempt\n", __func__); + if (value & 3) { /* XMCM */ + qemu_log_mask(LOG_UNIMP, + "%s: Tx channel selection mode enable attempt\n", + __func__); + } return; - case 0x1a: /* MCR1 */ + case 0x1a: /* MCR1 */ s->mcr[0] = value & 0x03e1; - if (value & 1) /* RMCM */ - printf("%s: Rx channel selection mode enable attempt\n", __func__); + if (value & 1) { /* RMCM */ + qemu_log_mask(LOG_UNIMP, + "%s: Rx channel selection mode enable attempt\n", + __func__); + } return; - case 0x1c: /* RCERA */ + case 0x1c: /* RCERA */ s->rcer[0] = value & 0xffff; return; - case 0x1e: /* RCERB */ + case 0x1e: /* RCERB */ s->rcer[1] = value & 0xffff; return; - case 0x20: /* XCERA */ + case 0x20: /* XCERA */ s->xcer[0] = value & 0xffff; return; - case 0x22: /* XCERB */ + case 0x22: /* XCERB */ s->xcer[1] = value & 0xffff; return; - case 0x24: /* PCR0 */ + case 0x24: /* PCR0 */ s->pcr = value & 0x7faf; return; - case 0x26: /* RCERC */ + case 0x26: /* RCERC */ s->rcer[2] = value & 0xffff; return; - case 0x28: /* RCERD */ + case 0x28: /* RCERD */ s->rcer[3] = value & 0xffff; return; - case 0x2a: /* XCERC */ + case 0x2a: /* XCERC */ s->xcer[2] = value & 0xffff; return; - case 0x2c: /* XCERD */ + case 0x2c: /* XCERD */ s->xcer[3] = value & 0xffff; return; - case 0x2e: /* RCERE */ + case 0x2e: /* RCERE */ s->rcer[4] = value & 0xffff; return; - case 0x30: /* RCERF */ + case 0x30: /* RCERF */ s->rcer[5] = value & 0xffff; return; - case 0x32: /* XCERE */ + case 0x32: /* XCERE */ s->xcer[4] = value & 0xffff; return; - case 0x34: /* XCERF */ + case 0x34: /* XCERF */ s->xcer[5] = value & 0xffff; return; - case 0x36: /* RCERG */ + case 0x36: /* RCERG */ s->rcer[6] = value & 0xffff; return; - case 0x38: /* RCERH */ + case 0x38: /* RCERH */ s->rcer[7] = value & 0xffff; return; - case 0x3a: /* XCERG */ + case 0x3a: /* XCERG */ s->xcer[6] = value & 0xffff; return; - case 0x3c: /* XCERH */ + case 0x3c: /* XCERH */ s->xcer[7] = value & 0xffff; return; } @@ -3406,8 +3359,8 @@ static void omap_mcbsp_writew(void *opaque, hwaddr addr, struct omap_mcbsp_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; - if (offset == 0x04) { /* DXR */ - if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */ + if (offset == 0x04) { /* DXR */ + if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */ return; if (s->tx_req > 3) { s->tx_req -= 4; @@ -3423,8 +3376,9 @@ static void omap_mcbsp_writew(void *opaque, hwaddr addr, } if (s->tx_req < 4) omap_mcbsp_tx_done(s); - } else - printf("%s: Tx FIFO overrun\n", __func__); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Tx FIFO overrun\n", __func__); + } return; } @@ -3542,7 +3496,7 @@ static void omap_lpg_tick(void *opaque) timer_mod(s->tm, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + s->on); s->cycle = !s->cycle; - printf("%s: LED is %s\n", __func__, s->cycle ? "on" : "off"); + trace_omap1_lpg_led(s->cycle ? "on" : "off"); } static void omap_lpg_update(struct omap_lpg_s *s) @@ -3550,23 +3504,23 @@ static void omap_lpg_update(struct omap_lpg_s *s) int64_t on, period = 1, ticks = 1000; static const int per[8] = { 1, 2, 4, 8, 12, 16, 20, 24 }; - if (~s->control & (1 << 6)) /* LPGRES */ + if (~s->control & (1 << 6)) /* LPGRES */ on = 0; - else if (s->control & (1 << 7)) /* PERM_ON */ + else if (s->control & (1 << 7)) /* PERM_ON */ on = period; else { - period = muldiv64(ticks, per[s->control & 7], /* PERCTRL */ + period = muldiv64(ticks, per[s->control & 7], /* PERCTRL */ 256 / 32); on = (s->clk && s->power) ? muldiv64(ticks, - per[(s->control >> 3) & 7], 256) : 0; /* ONCTRL */ + per[(s->control >> 3) & 7], 256) : 0; /* ONCTRL */ } timer_del(s->tm); - if (on == period && s->on < s->period) - printf("%s: LED is on\n", __func__); - else if (on == 0 && s->on) - printf("%s: LED is off\n", __func__); - else if (on && (on != s->on || period != s->period)) { + if (on == period && s->on < s->period) { + trace_omap1_lpg_led("on"); + } else if (on == 0 && s->on) { + trace_omap1_lpg_led("off"); + } else if (on && (on != s->on || period != s->period)) { s->cycle = 0; s->on = on; s->period = period; @@ -3596,10 +3550,10 @@ static uint64_t omap_lpg_read(void *opaque, hwaddr addr, unsigned size) } switch (offset) { - case 0x00: /* LCR */ + case 0x00: /* LCR */ return s->control; - case 0x04: /* PMR */ + case 0x04: /* PMR */ return s->power; } @@ -3619,14 +3573,14 @@ static void omap_lpg_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* LCR */ - if (~value & (1 << 6)) /* LPGRES */ + case 0x00: /* LCR */ + if (~value & (1 << 6)) /* LPGRES */ omap_lpg_reset(s); s->control = value & 0xff; omap_lpg_update(s); return; - case 0x04: /* PMR */ + case 0x04: /* PMR */ s->power = value & 0x01; omap_lpg_update(s); return; @@ -3676,7 +3630,7 @@ static uint64_t omap_mpui_io_read(void *opaque, hwaddr addr, return omap_badwidth_read16(opaque, addr); } - if (addr == OMAP_MPUI_BASE) /* CMR */ + if (addr == OMAP_MPUI_BASE) /* CMR */ return 0xfe4d; OMAP_BAD_REG(addr); @@ -3728,7 +3682,6 @@ static void omap1_mpu_reset(void *opaque) omap_uart_reset(mpu->uart[0]); omap_uart_reset(mpu->uart[1]); omap_uart_reset(mpu->uart[2]); - omap_mmc_reset(mpu->mmc); omap_mpuio_reset(mpu->mpuio); omap_uwire_reset(mpu->microwire); omap_pwl_reset(mpu->pwl); @@ -3750,25 +3703,25 @@ static const struct omap_map_s { const char *name; } omap15xx_dsp_mm[] = { /* Strobe 0 */ - { 0xe1010000, 0xfffb0000, 0x800, "UART1 BT" }, /* CS0 */ - { 0xe1010800, 0xfffb0800, 0x800, "UART2 COM" }, /* CS1 */ - { 0xe1011800, 0xfffb1800, 0x800, "McBSP1 audio" }, /* CS3 */ - { 0xe1012000, 0xfffb2000, 0x800, "MCSI2 communication" }, /* CS4 */ - { 0xe1012800, 0xfffb2800, 0x800, "MCSI1 BT u-Law" }, /* CS5 */ - { 0xe1013000, 0xfffb3000, 0x800, "uWire" }, /* CS6 */ - { 0xe1013800, 0xfffb3800, 0x800, "I^2C" }, /* CS7 */ - { 0xe1014000, 0xfffb4000, 0x800, "USB W2FC" }, /* CS8 */ - { 0xe1014800, 0xfffb4800, 0x800, "RTC" }, /* CS9 */ - { 0xe1015000, 0xfffb5000, 0x800, "MPUIO" }, /* CS10 */ - { 0xe1015800, 0xfffb5800, 0x800, "PWL" }, /* CS11 */ - { 0xe1016000, 0xfffb6000, 0x800, "PWT" }, /* CS12 */ - { 0xe1017000, 0xfffb7000, 0x800, "McBSP3" }, /* CS14 */ - { 0xe1017800, 0xfffb7800, 0x800, "MMC" }, /* CS15 */ - { 0xe1019000, 0xfffb9000, 0x800, "32-kHz timer" }, /* CS18 */ - { 0xe1019800, 0xfffb9800, 0x800, "UART3" }, /* CS19 */ - { 0xe101c800, 0xfffbc800, 0x800, "TIPB switches" }, /* CS25 */ + { 0xe1010000, 0xfffb0000, 0x800, "UART1 BT" }, /* CS0 */ + { 0xe1010800, 0xfffb0800, 0x800, "UART2 COM" }, /* CS1 */ + { 0xe1011800, 0xfffb1800, 0x800, "McBSP1 audio" }, /* CS3 */ + { 0xe1012000, 0xfffb2000, 0x800, "MCSI2 communication" }, /* CS4 */ + { 0xe1012800, 0xfffb2800, 0x800, "MCSI1 BT u-Law" }, /* CS5 */ + { 0xe1013000, 0xfffb3000, 0x800, "uWire" }, /* CS6 */ + { 0xe1013800, 0xfffb3800, 0x800, "I^2C" }, /* CS7 */ + { 0xe1014000, 0xfffb4000, 0x800, "USB W2FC" }, /* CS8 */ + { 0xe1014800, 0xfffb4800, 0x800, "RTC" }, /* CS9 */ + { 0xe1015000, 0xfffb5000, 0x800, "MPUIO" }, /* CS10 */ + { 0xe1015800, 0xfffb5800, 0x800, "PWL" }, /* CS11 */ + { 0xe1016000, 0xfffb6000, 0x800, "PWT" }, /* CS12 */ + { 0xe1017000, 0xfffb7000, 0x800, "McBSP3" }, /* CS14 */ + { 0xe1017800, 0xfffb7800, 0x800, "MMC" }, /* CS15 */ + { 0xe1019000, 0xfffb9000, 0x800, "32-kHz timer" }, /* CS18 */ + { 0xe1019800, 0xfffb9800, 0x800, "UART3" }, /* CS19 */ + { 0xe101c800, 0xfffbc800, 0x800, "TIPB switches" }, /* CS25 */ /* Strobe 1 */ - { 0xe101e000, 0xfffce000, 0x800, "GPIOs" }, /* CS28 */ + { 0xe101e000, 0xfffce000, 0x800, "GPIOs" }, /* CS28 */ { 0 } }; @@ -3993,11 +3946,25 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *dram, if (!dinfo && !qtest_enabled()) { warn_report("missing SecureDigital device"); } - s->mmc = omap_mmc_init(0xfffb7800, system_memory, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - qdev_get_gpio_in(s->ih[1], OMAP_INT_OQN), - &s->drq[OMAP_DMA_MMC_TX], - omap_findclk(s, "mmc_ck")); + + s->mmc = qdev_new(TYPE_OMAP_MMC); + sysbus_realize_and_unref(SYS_BUS_DEVICE(s->mmc), &error_fatal); + omap_mmc_set_clk(s->mmc, omap_findclk(s, "mmc_ck")); + + memory_region_add_subregion(system_memory, 0xfffb7800, + sysbus_mmio_get_region(SYS_BUS_DEVICE(s->mmc), 0)); + qdev_connect_gpio_out_named(s->mmc, "dma-tx", 0, s->drq[OMAP_DMA_MMC_TX]); + qdev_connect_gpio_out_named(s->mmc, "dma-rx", 0, s->drq[OMAP_DMA_MMC_RX]); + sysbus_connect_irq(SYS_BUS_DEVICE(s->mmc), 0, + qdev_get_gpio_in(s->ih[1], OMAP_INT_OQN)); + + if (dinfo) { + DeviceState *card = qdev_new(TYPE_SD_CARD); + qdev_prop_set_drive_err(card, "drive", blk_by_legacy_dinfo(dinfo), + &error_fatal); + qdev_realize_and_unref(card, qdev_get_child_bus(s->mmc, "sd-bus"), + &error_fatal); + } s->mpuio = omap_mpuio_init(system_memory, 0xfffb5000, qdev_get_gpio_in(s->ih[1], OMAP_INT_KEYBOARD), @@ -4058,18 +4025,18 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *dram, 0xfffbd800, omap_findclk(s, "clk32-kHz")); /* 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 - * Camera Interface fffb6800 - fffb6fff - * USB Host fffba000 - fffba7ff - * FAC fffba800 - fffbafff - * HDQ/1-Wire fffbc000 - fffbc7ff - * TIPB switches fffbc800 - fffbcfff - * Mailbox fffcf000 - fffcf7ff - * Local bus IF fffec100 - fffec1ff - * Local bus MMU fffec200 - fffec2ff - * DSP MMU fffed200 - fffed2ff + * MCSI2 Comm fffb2000 - fffb27ff (not mapped on OMAP310) + * MCSI1 Bluetooth fffb2800 - fffb2fff (not mapped on OMAP310) + * USB W2FC fffb4000 - fffb47ff + * Camera Interface fffb6800 - fffb6fff + * USB Host fffba000 - fffba7ff + * FAC fffba800 - fffbafff + * HDQ/1-Wire fffbc000 - fffbc7ff + * TIPB switches fffbc800 - fffbcfff + * Mailbox fffcf000 - fffcf7ff + * Local bus IF fffec100 - fffec1ff + * Local bus MMU fffec200 - fffec2ff + * DSP MMU fffed200 - fffed2ff */ omap_setup_dsp_mapping(system_memory, omap15xx_dsp_mm); diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c deleted file mode 100644 index d5a2ae7af6e..00000000000 --- a/hw/arm/omap2.c +++ /dev/null @@ -1,2710 +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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 (including those not currently 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 4bf1579f8c1..5d4a31b7aed 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -1,7 +1,7 @@ /* omap_sx1.c Support for the Siemens SX1 smartphone emulation. * * Copyright (C) 2008 - * Jean-Christophe PLAGNIOL-VILLARD + * Jean-Christophe PLAGNIOL-VILLARD * Copyright (C) 2007 Vladimir Ananiev * * based on PalmOne's (TM) PDAs support (palm.c) @@ -33,9 +33,8 @@ #include "hw/boards.h" #include "hw/arm/boot.h" #include "hw/block/flash.h" -#include "sysemu/qtest.h" -#include "exec/address-spaces.h" -#include "cpu.h" +#include "system/qtest.h" +#include "system/address-spaces.h" #include "qemu/cutils.h" #include "qemu/error-report.h" @@ -77,10 +76,6 @@ static uint64_t static_read(void *opaque, hwaddr offset, static void static_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { -#ifdef SPY - printf("%s: value %" PRIx64 " %u bytes written at 0x%x\n", - __func__, value, size, (int)offset); -#endif } static const MemoryRegionOps static_ops = { @@ -207,7 +202,7 @@ static void sx1_init_v2(MachineState *machine) sx1_init(machine, 2); } -static void sx1_machine_v2_class_init(ObjectClass *oc, void *data) +static void sx1_machine_v2_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -217,6 +212,7 @@ static void sx1_machine_v2_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = ARM_CPU_TYPE_NAME("ti925t"); mc->default_ram_size = SDRAM_SIZE; mc->default_ram_id = "omap1.dram"; + mc->auto_create_sdcard = true; } static const TypeInfo sx1_machine_v2_type = { @@ -225,7 +221,7 @@ static const TypeInfo sx1_machine_v2_type = { .class_init = sx1_machine_v2_class_init, }; -static void sx1_machine_v1_class_init(ObjectClass *oc, void *data) +static void sx1_machine_v1_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -235,6 +231,7 @@ static void sx1_machine_v1_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = ARM_CPU_TYPE_NAME("ti925t"); mc->default_ram_size = SDRAM_SIZE; mc->default_ram_id = "omap1.dram"; + mc->auto_create_sdcard = true; } static const TypeInfo sx1_machine_v1_type = { diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index 10653361ed5..e0956880d11 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -19,12 +19,13 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "exec/address-spaces.h" +#include "system/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; @@ -48,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)); @@ -105,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; @@ -118,8 +118,10 @@ 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"; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("orangepi-pc", orangepi_machine_init) diff --git a/hw/arm/palm.c b/hw/arm/palm.c deleted file mode 100644 index 17c11ac4cec..00000000000 --- a/hw/arm/palm.c +++ /dev/null @@ -1,322 +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" -#include "qemu/error-report.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 = 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 07d5dd8691f..00000000000 --- a/hw/arm/pxa2xx.c +++ /dev/null @@ -1,2388 +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 "exec/address-spaces.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(unsigned int sdram_size, const char *cpu_type) -{ - MemoryRegion *address_space = get_system_memory(); - 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(unsigned int sdram_size) -{ - MemoryRegion *address_space = get_system_memory(); - 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 e7c3d99224a..00000000000 --- 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 47132ab982b..00000000000 --- 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 cc4c4ec9bfc..9d9af63d654 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,19 +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); - object_property_set_str(OBJECT(&s->soc), "command-line", + object_property_set_str(OBJECT(soc), "command-line", machine->kernel_cmdline, &error_abort); - qdev_realize(DEVICE(&s->soc), NULL, &error_fatal); + 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); @@ -300,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; @@ -322,50 +330,62 @@ static void raspi_machine_class_common_init(MachineClass *mc, mc->default_ram_id = "ram"; }; -static void raspi0_machine_class_init(ObjectClass *oc, void *data) +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, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); - RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); + mc->auto_create_sdcard = true; 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) +static void raspi1ap_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); - RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); + mc->auto_create_sdcard = true; 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) +static void raspi2b_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); - RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); + mc->auto_create_sdcard = true; 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) +static void raspi3ap_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); - RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); + mc->auto_create_sdcard = true; 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) +static void raspi3b_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); - RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); + mc->auto_create_sdcard = true; rmc->board_rev = 0xa02082; - raspi_machine_class_common_init(mc, rmc->board_rev); + raspi_machine_class_init(mc, rmc->board_rev); }; #endif /* TARGET_AARCH64 */ @@ -394,9 +414,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 00000000000..20082d52667 --- /dev/null +++ b/hw/arm/raspi4b.c @@ -0,0 +1,137 @@ +/* + * 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 "system/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, const 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->auto_create_sdcard = true; + 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 a5aa2f046ae..5c9050490b4 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -19,7 +19,7 @@ #include "hw/pci/pci.h" #include "hw/qdev-core.h" #include "net/net.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/i2c/i2c.h" #include "qemu/error-report.h" @@ -29,10 +29,14 @@ #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 +#define GIC_EXT_IRQS 64 /* Realview PBX-A9 development board */ + /* Board init. */ static struct arm_boot_info realview_binfo = { @@ -83,12 +87,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; @@ -185,7 +187,12 @@ static void realview_init(MachineState *machine, sysbus_mmio_map(SYS_BUS_DEVICE(sysctl), 0, 0x10000000); if (is_mpcore) { - dev = qdev_new(is_pb ? TYPE_A9MPCORE_PRIV : "realview_mpcore"); + if (is_pb) { + dev = qdev_new(TYPE_A9MPCORE_PRIV); + qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL); + } else { + dev = qdev_new("realview_mpcore"); + } qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); @@ -201,12 +208,15 @@ static void realview_init(MachineState *machine, /* For now just create the nIRQ GIC, and ignore the others. */ dev = sysbus_create_simple(TYPE_REALVIEW_GIC, gic_addr, cpu_irq[0]); } - for (n = 0; n < 64; n++) { + for (n = 0; n < GIC_EXT_IRQS; n++) { pic[n] = qdev_get_gpio_in(dev, n); } 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 +245,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,24 +306,19 @@ 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]); } } + 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 +390,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) @@ -403,7 +413,7 @@ static void realview_pbx_a9_init(MachineState *machine) realview_init(machine, BOARD_PBX_A9); } -static void realview_eb_class_init(ObjectClass *oc, void *data) +static void realview_eb_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -412,6 +422,9 @@ 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"); + mc->auto_create_sdcard = true; + + machine_add_audiodev_property(mc); } static const TypeInfo realview_eb_type = { @@ -420,7 +433,7 @@ static const TypeInfo realview_eb_type = { .class_init = realview_eb_class_init, }; -static void realview_eb_mpcore_class_init(ObjectClass *oc, void *data) +static void realview_eb_mpcore_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -430,6 +443,9 @@ 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"); + mc->auto_create_sdcard = true; + + machine_add_audiodev_property(mc); } static const TypeInfo realview_eb_mpcore_type = { @@ -438,7 +454,7 @@ static const TypeInfo realview_eb_mpcore_type = { .class_init = realview_eb_mpcore_class_init, }; -static void realview_pb_a8_class_init(ObjectClass *oc, void *data) +static void realview_pb_a8_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -446,6 +462,9 @@ 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"); + mc->auto_create_sdcard = true; + + machine_add_audiodev_property(mc); } static const TypeInfo realview_pb_a8_type = { @@ -454,7 +473,7 @@ static const TypeInfo realview_pb_a8_type = { .class_init = realview_pb_a8_class_init, }; -static void realview_pbx_a9_class_init(ObjectClass *oc, void *data) +static void realview_pbx_a9_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -463,6 +482,9 @@ 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"); + mc->auto_create_sdcard = true; + + 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 41191245b81..df60d47c6fd 100644 --- a/hw/arm/sabrelite.c +++ b/hw/arm/sabrelite.c @@ -13,10 +13,11 @@ #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" -#include "sysemu/qtest.h" +#include "system/qtest.h" static struct arm_boot_info sabrelite_binfo = { /* DDR memory start */ @@ -109,6 +110,7 @@ static void sabrelite_machine_init(MachineClass *mc) mc->max_cpus = FSL_IMX6_NUM_CPUS; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "sabrelite.ram"; + mc->auto_create_sdcard = true; } DEFINE_MACHINE("sabrelite", sabrelite_machine_init) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index bc89eb48062..deae5cf9861 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 @@ -22,20 +23,21 @@ #include "qapi/error.h" #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 "system/device_tree.h" +#include "system/kvm.h" +#include "system/numa.h" +#include "system/runstate.h" +#include "system/system.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" @@ -46,7 +48,10 @@ #include "hw/char/pl011.h" #include "hw/watchdog/sbsa_gwdt.h" #include "net/net.h" +#include "qobject/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) @@ -55,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, @@ -149,51 +156,28 @@ static const int sbsa_ref_irqmap[] = { [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("neoverse-n1"), - ARM_CPU_TYPE_NAME("neoverse-v1"), - ARM_CPU_TYPE_NAME("max"), -}; - -static bool cpu_type_valid(const char *cpu) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(valid_cpus); i++) { - if (strcmp(cpu, valid_cpus[i]) == 0) { - return true; - } - } - return false; -} - static uint64_t sbsa_ref_cpu_mp_affinity(SBSAMachineState *sms, int idx) { uint8_t clustersz = ARM_DEFAULT_CPUS_PER_CLUSTER; - return arm_cpu_mp_affinity(idx, clustersz); + return arm_build_mp_affinity(idx, clustersz); } static void sbsa_fdt_add_gic_node(SBSAMachineState *sms) { - char *nodename; + const char *intc_nodename = "/intc"; + const char *its_nodename = "/intc/its"; - nodename = g_strdup_printf("/intc"); - qemu_fdt_add_subnode(sms->fdt, nodename); - qemu_fdt_setprop_sized_cells(sms->fdt, nodename, "reg", + 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); - nodename = g_strdup_printf("/intc/its"); - qemu_fdt_add_subnode(sms->fdt, nodename); - qemu_fdt_setprop_sized_cells(sms->fdt, nodename, "reg", + 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); - - g_free(nodename); } /* @@ -232,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", 3); + 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); @@ -289,6 +273,14 @@ 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); } @@ -441,6 +433,7 @@ static void create_gic(SBSAMachineState *sms, MemoryRegion *mem) SysBusDevice *gicbusdev; const char *gictype; uint32_t redist0_capacity, redist0_count; + QList *redist_region_count; int i; gictype = gicv3_class_name(); @@ -459,8 +452,9 @@ static void create_gic(SBSAMachineState *sms, MemoryRegion *mem) 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); @@ -478,7 +472,7 @@ static void create_gic(SBSAMachineState *sms, MemoryRegion *mem) */ for (i = 0; i < smp_cpus; i++) { DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); - int ppibase = NUM_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS; + int intidbase = NUM_IRQS + i * GIC_INTERNAL; int irq; /* * Mapping from the output timer irq lines from the CPU to the @@ -489,19 +483,25 @@ static void create_gic(SBSAMachineState *sms, 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, + [GTIMER_S_EL2_PHYS] = ARCH_TIMER_S_EL2_IRQ, + [GTIMER_S_EL2_VIRT] = ARCH_TIMER_S_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)); @@ -546,6 +546,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); @@ -586,8 +587,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); @@ -596,14 +595,8 @@ 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_bus_create_drive(&ahci->dev[i].port, 0, hd[i]); - } + ahci_ide_create_devs(&sysahci->ahci, hd); } static void create_xhci(const SBSAMachineState *sms) @@ -627,6 +620,7 @@ static void create_smmu(const SBSAMachineState *sms, PCIBus *bus) 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); @@ -681,18 +675,15 @@ static void create_pcie(SBSAMachineState *sms) /* Map IO port space */ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio); - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, qdev_get_gpio_in(sms->gic, irq + i)); gpex_set_irq_num(GPEX_HOST(dev), i, irq + i); } pci = PCI_HOST_BRIDGE(dev); - if (pci->bus) { - for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], pci->bus, mc->default_nic, NULL); - } - } + + pci_init_nic_devices(pci->bus, mc->default_nic); pci_create_simple(pci->bus, -1, "bochs-display"); @@ -730,11 +721,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); @@ -799,6 +785,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); @@ -892,13 +880,23 @@ static void sbsa_ref_instance_init(Object *obj) sbsa_flash_create(sms); } -static void sbsa_ref_class_init(ObjectClass *oc, void *data) +static void sbsa_ref_class_init(ObjectClass *oc, const 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("neoverse-n1"); + 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; @@ -908,6 +906,7 @@ static void sbsa_ref_class_init(ObjectClass *oc, void *data) 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; diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index f35ae9aa22c..f39b99e5269 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -57,7 +57,7 @@ static gboolean smmu_iotlb_key_equal(gconstpointer v1, gconstpointer v2) (k1->vmid == k2->vmid); } -SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint16_t vmid, 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, .vmid = vmid, .iova = iova, @@ -66,8 +66,10 @@ SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint16_t vmid, uint64_t iova, 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; @@ -88,6 +90,36 @@ SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg, } 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++; @@ -127,24 +159,35 @@ void smmu_iotlb_inv_all(SMMUState *s) 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_vmid(gpointer key, gpointer value, gpointer user_data) { - uint16_t vmid = *(uint16_t *)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) { @@ -163,6 +206,46 @@ static gboolean smmu_hash_remove_by_asid_vmid_iova(gpointer key, gpointer value, ((entry->iova & ~info->mask) == info->iova); } +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); +} + +static gboolean +smmu_hash_remove_by_sid_range(gpointer key, gpointer value, gpointer user_data) +{ + SMMUDevice *sdev = (SMMUDevice *)key; + uint32_t sid = smmu_get_sid(sdev); + SMMUSIDRange *sid_range = (SMMUSIDRange *)user_data; + + if (sid < sid_range->start || sid > sid_range->end) { + return false; + } + trace_smmu_config_cache_inv(sid); + return true; +} + +void smmu_configs_inv_sid_range(SMMUState *s, SMMUSIDRange sid_range) +{ + trace_smmu_configs_inv_sid_range(sid_range.start, sid_range.end); + g_hash_table_foreach_remove(s->configs, smmu_hash_remove_by_sid_range, + &sid_range); +} + 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) { @@ -191,18 +274,57 @@ void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, &info); } -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) +{ + 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) { - trace_smmu_iotlb_inv_asid(asid); - g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid, &asid); + 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); } -inline void smmu_iotlb_inv_vmid(SMMUState *s, uint16_t vmid) +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 */ /** @@ -286,8 +408,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_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 @@ -299,12 +454,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_s1(SMMUTransCfg *cfg, +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; @@ -318,7 +473,8 @@ static int smmu_ptw_64_s1(SMMUTransCfg *cfg, inputsize = 64 - tt->tsz; level = 4 - (inputsize - 4) / stride; indexmask = VMSA_IDXMSK(inputsize, stride, level); - baseaddr = extract64(tt->ttb, 0, 48); + + baseaddr = extract64(tt->ttb, 0, cfg->oas); baseaddr &= ~indexmask; while (level < VMSA_LEVELS) { @@ -349,6 +505,11 @@ static int smmu_ptw_64_s1(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)) { @@ -364,16 +525,38 @@ static int smmu_ptw_64_s1(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; @@ -381,7 +564,7 @@ static int smmu_ptw_64_s1(SMMUTransCfg *cfg, info->type = SMMU_PTW_ERR_TRANSLATION; error: - info->stage = 1; + info->stage = SMMU_STAGE_1; tlbe->entry.perm = IOMMU_NONE; return -EINVAL; } @@ -404,7 +587,7 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg, dma_addr_t ipa, IOMMUAccessFlags perm, SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) { - const int stage = 2; + 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; @@ -415,8 +598,8 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg, * Get the ttb from concatenated structure. * The offset is the idx * size of each ttb(number of ptes * (sizeof(pte)) */ - uint64_t baseaddr = extract64(cfg->s2cfg.vttb, 0, 48) + (1 << stride) * - idx * sizeof(uint64_t); + 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; @@ -427,7 +610,7 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg, */ if (ipa >= (1ULL << inputsize)) { info->type = SMMU_PTW_ERR_TRANSLATION; - goto error; + goto error_ipa; } while (level < VMSA_LEVELS) { @@ -473,13 +656,13 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg, */ if (!PTE_AF(pte) && !cfg->s2cfg.affd) { info->type = SMMU_PTW_ERR_ACCESS; - goto error; + goto error_ipa; } s2ap = PTE_AP(pte); if (is_permission_fault_s2(s2ap, perm)) { info->type = SMMU_PTW_ERR_PERMISSION; - goto error; + goto error_ipa; } /* @@ -488,28 +671,53 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg, */ if (gpa >= (1ULL << cfg->s2cfg.eff_ps)) { info->type = SMMU_PTW_ERR_ADDR_SIZE; - goto error; + goto error_ipa; } tlbe->entry.translated_addr = gpa; tlbe->entry.iova = ipa & ~mask; tlbe->entry.addr_mask = mask; - tlbe->entry.perm = s2ap; + 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 = 2; + 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; +} + /** * 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 @@ -518,12 +726,16 @@ static int smmu_ptw_64_s2(SMMUTransCfg *cfg, * * return 0 on success */ -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->stage == 1) { - return smmu_ptw_64_s1(cfg, iova, perm, tlbe, info); - } else if (cfg->stage == 2) { + 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) { /* * If bypassing stage 1(or unimplemented), the input address is passed * directly to stage 2 as IPA. If the input address of a transaction @@ -532,7 +744,7 @@ int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, */ if (iova >= (1ULL << cfg->oas)) { info->type = SMMU_PTW_ERR_ADDR_SIZE; - info->stage = 1; + info->stage = SMMU_STAGE_1; tlbe->entry.perm = IOMMU_NONE; return -EINVAL; } @@ -540,7 +752,72 @@ int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, return smmu_ptw_64_s2(cfg, iova, perm, tlbe, info); } - g_assert_not_reached(); + /* 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; } /** @@ -605,20 +882,20 @@ 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; } @@ -661,28 +938,34 @@ 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_hold(Object *obj) +/* + * Make sure the IOMMU is reset in 'exit' phase after + * all outstanding DMA requests have been quiesced during + * the 'enter' or 'hold' reset phases + */ +static void smmu_base_reset_exit(Object *obj, ResetType type) { 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); } -static Property smmu_dev_properties[] = { +static const Property smmu_dev_properties[] = { DEFINE_PROP_UINT8("bus_num", SMMUState, bus_num, 0), 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) +static void smmu_base_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -691,7 +974,7 @@ static void smmu_base_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, smmu_dev_properties); device_class_set_parent_realize(dc, smmu_base_realize, &sbc->parent_realize); - rc->phases.hold = smmu_base_reset_hold; + rc->phases.exit = smmu_base_reset_exit; } static const TypeInfo smmu_base_info = { diff --git a/hw/arm/smmu-internal.h b/hw/arm/smmu-internal.h index 843bebb185d..d143d296f34 100644 --- a/hw/arm/smmu-internal.h +++ b/hw/arm/smmu-internal.h @@ -141,9 +141,4 @@ typedef struct SMMUIOTLBPageInvInfo { uint64_t mask; } SMMUIOTLBPageInvInfo; -typedef struct SMMUSIDRange { - uint32_t start; - uint32_t end; -} SMMUSIDRange; - #endif diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index 6d1c1edab7b..b6b7399347f 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,6 +32,12 @@ 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) @@ -38,33 +45,71 @@ REG32(IDR0, 0x0) 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 @@ -328,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) @@ -533,21 +575,13 @@ typedef struct CD { #define STE_S2S(x) extract32((x)->word[5], 25, 1) #define STE_S2R(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_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) { @@ -565,34 +599,18 @@ static inline int oas2bits(int oas_field) case 5: return 48; } - return -1; -} -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); + g_assert_not_reached(); } -#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) @@ -600,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 1e9be8e89af..ab679723533 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -25,6 +25,7 @@ #include "hw/qdev-core.h" #include "hw/pci/pci.h" #include "cpu.h" +#include "exec/target_page.h" #include "trace.h" #include "qemu/log.h" #include "qemu/error-report.h" @@ -34,8 +35,10 @@ #include "smmuv3-internal.h" #include "smmu-internal.h" -#define PTW_RECORD_FAULT(cfg) (((cfg)->stage == 1) ? (cfg)->record_faults : \ - (cfg)->s2cfg.record_faults) +#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 @@ -259,6 +262,9 @@ static void smmuv3_init_regs(SMMUv3State *s) /* 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); } @@ -278,15 +284,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; @@ -332,14 +342,35 @@ static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, STE *buf, } +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, 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); @@ -347,7 +378,7 @@ static int smmu_get_cd(SMMUv3State *s, STE *ste, uint32_t ssid, qemu_log_mask(LOG_GUEST_ERROR, "Cannot fetch pte at address=0x%"PRIx64"\n", addr); event->type = SMMU_EVT_F_CD_FETCH; - event->u.f_ste_fetch.addr = addr; + event->u.f_cd_fetch.addr = addr; return -EINVAL; } for (i = 0; i < ARRAY_SIZE(buf->word); i++) { @@ -372,10 +403,10 @@ static bool s2t0sz_valid(SMMUTransCfg *cfg) } if (cfg->s2cfg.granule_sz == 16) { - return (cfg->s2cfg.tsz >= 64 - oas2bits(SMMU_IDR5_OAS)); + return (cfg->s2cfg.tsz >= 64 - cfg->s2cfg.eff_ps); } - return (cfg->s2cfg.tsz >= MAX(64 - oas2bits(SMMU_IDR5_OAS), 16)); + return (cfg->s2cfg.tsz >= MAX(64 - cfg->s2cfg.eff_ps, 16)); } /* @@ -396,9 +427,10 @@ static bool s2_pgtable_config_valid(uint8_t sl0, uint8_t t0sz, uint8_t gran) return nr_concat <= VMSA_MAX_S2_CONCAT; } -static int decode_ste_s2_cfg(SMMUTransCfg *cfg, STE *ste) +static int decode_ste_s2_cfg(SMMUv3State *s, SMMUTransCfg *cfg, + STE *ste) { - cfg->stage = 2; + uint8_t oas = FIELD_EX32(s->idr[5], IDR5, OAS); if (STE_S2AA64(ste) == 0x0) { qemu_log_mask(LOG_UNIMP, @@ -432,7 +464,15 @@ static int decode_ste_s2_cfg(SMMUTransCfg *cfg, STE *ste) } /* For AA64, The effective S2PS size is capped to the OAS. */ - cfg->s2cfg.eff_ps = oas2bits(MIN(STE_S2PS(ste), SMMU_IDR5_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. @@ -482,11 +522,33 @@ static int decode_ste_s2_cfg(SMMUTransCfg *cfg, STE *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)) { @@ -498,13 +560,9 @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, config = STE_CONFIG(ste); - if (STE_CFG_ABORT(config)) { - cfg->aborted = true; - return 0; - } + decode_ste_config(cfg, config); - if (STE_CFG_BYPASS(config)) { - cfg->bypassed = true; + if (cfg->aborted || cfg->bypassed) { return 0; } @@ -534,8 +592,8 @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, * Stage-1 OAS defaults to OAS even if not enabled as it would be used * in input address check for stage-2. */ - cfg->oas = oas2bits(SMMU_IDR5_OAS); - ret = decode_ste_s2_cfg(cfg, ste); + cfg->oas = oas2bits(oas); + ret = decode_ste_s2_cfg(s, cfg, ste); if (ret) { goto bad_ste; } @@ -654,10 +712,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; @@ -674,12 +736,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); @@ -705,11 +767,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); } @@ -757,16 +844,16 @@ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg, return ret; } - if (cfg->aborted || cfg->bypassed || (cfg->stage == 2)) { + 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); } /** @@ -817,10 +904,137 @@ static void smmuv3_flush_config(SMMUDevice *sdev) SMMUv3State *s = sdev->smmu; SMMUState *bc = &s->smmu_state; - trace_smmuv3_config_cache_inv(smmu_get_sid(sdev)); + trace_smmu_config_cache_inv(smmu_get_sid(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) { @@ -830,12 +1044,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, @@ -844,11 +1053,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, .addr_mask = ~(hwaddr)0, .perm = IOMMU_NONE, }; - /* - * Combined attributes used for TLB lookup, as only one stage is supported, - * it will hold attributes based on the enabled stage. - */ - SMMUTransTableInfo tt_combined; + SMMUTLBEntry *cached_entry = NULL; qemu_mutex_lock(&s->mutex); @@ -877,116 +1082,19 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, goto epilogue; } - if (cfg->stage == 1) { - /* Select stage1 translation table. */ - tt = select_tt(cfg, addr); - if (!tt) { - if (cfg->record_faults) { - event.type = SMMU_EVT_F_TRANSLATION; - event.u.f_translation.addr = addr; - event.u.f_translation.rnw = flag & 0x1; - } - status = SMMU_TRANS_ERROR; - goto epilogue; - } - tt_combined.granule_sz = tt->granule_sz; - tt_combined.tsz = tt->tsz; - - } else { - /* Stage2. */ - tt_combined.granule_sz = cfg->s2cfg.granule_sz; - tt_combined.tsz = cfg->s2cfg.tsz; - } - /* - * TLB lookup looks for granule and input size for a translation stage, - * as only one stage is supported right now, choose the right values - * from the configuration. - */ - page_mask = (1ULL << tt_combined.granule_sz) - 1; - aligned_addr = addr & ~page_mask; - - cached_entry = smmu_iotlb_lookup(bs, cfg, &tt_combined, aligned_addr); - if (cached_entry) { - if ((flag & IOMMU_WO) && !(cached_entry->entry.perm & IOMMU_WO)) { - status = SMMU_TRANS_ERROR; - /* - * We know that the TLB only contains either stage-1 or stage-2 as - * nesting is not supported. So it is sufficient to check the - * translation stage to know the TLB stage for now. - */ - event.u.f_walk_eabt.s2 = (cfg->stage == 2); - if (PTW_RECORD_FAULT(cfg)) { - event.type = SMMU_EVT_F_PERMISSION; - event.u.f_permission.addr = addr; - event.u.f_permission.rnw = flag & 0x1; - } - } else { - status = SMMU_TRANS_SUCCESS; - } - goto epilogue; - } - - cached_entry = g_new0(SMMUTLBEntry, 1); - - if (smmu_ptw(cfg, aligned_addr, flag, cached_entry, &ptw_info)) { - /* All faults from PTW has S2 field. */ - event.u.f_walk_eabt.s2 = (ptw_info.stage == 2); - g_free(cached_entry); - switch (ptw_info.type) { - case SMMU_PTW_ERR_WALK_EABT: - 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 (PTW_RECORD_FAULT(cfg)) { - event.type = SMMU_EVT_F_TRANSLATION; - event.u.f_translation.addr = addr; - event.u.f_translation.rnw = flag & 0x1; - } - break; - case SMMU_PTW_ERR_ADDR_SIZE: - if (PTW_RECORD_FAULT(cfg)) { - event.type = SMMU_EVT_F_ADDR_SIZE; - event.u.f_addr_size.addr = addr; - event.u.f_addr_size.rnw = flag & 0x1; - } - break; - case SMMU_PTW_ERR_ACCESS: - if (PTW_RECORD_FAULT(cfg)) { - event.type = SMMU_EVT_F_ACCESS; - event.u.f_access.addr = addr; - event.u.f_access.rnw = flag & 0x1; - } - break; - case SMMU_PTW_ERR_PERMISSION: - if (PTW_RECORD_FAULT(cfg)) { - 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; @@ -1006,6 +1114,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, 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)); @@ -1027,27 +1136,38 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, * @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, int vmid, dma_addr_t iova, uint8_t tg, - uint64_t num_pages) + 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; - SMMUv3State *s = sdev->smmu; + + 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) { - SMMUEventInfo event = {.inval_ste_allowed = true}; - SMMUTransCfg *cfg = smmuv3_get_config(sdev, &event); SMMUTransTableInfo *tt; - if (!cfg) { - return; - } - if (asid >= 0 && cfg->asid != asid) { return; } @@ -1056,7 +1176,7 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, return; } - if (STAGE1_SUPPORTED(s)) { + if (stage == SMMU_STAGE_1) { tt = select_tt(cfg, iova); if (!tt) { return; @@ -1082,7 +1202,7 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, /* 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) + uint64_t num_pages, int stage) { SMMUDevice *sdev; @@ -1091,15 +1211,15 @@ static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, int vmid, IOMMUNotifier *n; trace_smmuv3_inv_notifiers_iova(mr->parent_obj.name, asid, vmid, - iova, tg, num_pages); + iova, tg, num_pages, stage); IOMMU_NOTIFIER_FOREACH(n, mr) { - smmuv3_notify_iova(mr, n, asid, vmid, iova, tg, num_pages); + smmuv3_notify_iova(mr, n, asid, vmid, iova, tg, num_pages, stage); } } } -static void smmuv3_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); @@ -1124,9 +1244,13 @@ static void smmuv3_range_inval(SMMUState *s, Cmd *cmd) } if (!tg) { - trace_smmuv3_range_inval(vmid, asid, addr, tg, 1, ttl, leaf); - smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, 1); - smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, 1, ttl); + 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; } @@ -1142,27 +1266,18 @@ static void smmuv3_range_inval(SMMUState *s, Cmd *cmd) uint64_t mask = dma_aligned_pow2_mask(addr, end, 64); num_pages = (mask + 1) >> granule; - trace_smmuv3_range_inval(vmid, asid, addr, tg, num_pages, ttl, leaf); - smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, num_pages); - smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, num_pages, ttl); + 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; } } -static gboolean -smmuv3_invalidate_ste(gpointer key, gpointer value, gpointer user_data) -{ - SMMUDevice *sdev = (SMMUDevice *)key; - uint32_t sid = smmu_get_sid(sdev); - SMMUSIDRange *sid_range = (SMMUSIDRange *)user_data; - - if (sid < sid_range->start || sid > sid_range->end) { - return false; - } - trace_smmuv3_config_cache_inv(sid); - return true; -} - static int smmuv3_cmdq_consume(SMMUv3State *s) { SMMUState *bs = ARM_SMMU(s); @@ -1213,20 +1328,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; @@ -1247,53 +1360,74 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) sid_range.end = sid_range.start + mask; trace_smmuv3_cmdq_cfgi_ste_range(sid_range.start, sid_range.end); - g_hash_table_foreach_remove(bs->configs, smmuv3_invalidate_ste, - &sid_range); + smmu_configs_inv_sid_range(bs, sid_range); break; } case SMMU_CMD_CFGI_CD: 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; @@ -1303,11 +1437,11 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) cmd_error = SMMU_CERROR_ILL; break; } - smmuv3_range_inval(bs, &cmd); + smmuv3_range_inval(bs, &cmd, SMMU_STAGE_1); break; case SMMU_CMD_TLBI_S12_VMALL: { - uint16_t vmid = CMD_VMID(&cmd); + int vmid = CMD_VMID(&cmd); if (!STAGE2_SUPPORTED(s)) { cmd_error = SMMU_CERROR_ILL; @@ -1328,7 +1462,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) * As currently only either s1 or s2 are supported * we can reuse same function for s2. */ - smmuv3_range_inval(bs, &cmd); + smmuv3_range_inval(bs, &cmd, SMMU_STAGE_2); break; case SMMU_CMD_TLBI_EL3_ALL: case SMMU_CMD_TLBI_EL3_VA: @@ -1722,13 +1856,19 @@ static void smmu_init_irq(SMMUv3State *s, SysBusDevice *dev) } } -static void smmu_reset_hold(Object *obj) +/* + * Make sure the IOMMU is reset in 'exit' phase after + * all outstanding DMA requests have been quiesced during + * the 'enter' or 'hold' reset phases + */ +static void smmu_reset_exit(Object *obj, ResetType type) { SMMUv3State *s = ARM_SMMUV3(obj); SMMUv3Class *c = ARM_SMMUV3_GET_CLASS(s); - if (c->parent_phases.hold) { - c->parent_phases.hold(obj); + trace_smmu_reset_exit(); + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); } smmuv3_init_regs(s); @@ -1764,7 +1904,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), @@ -1786,7 +1926,7 @@ static const VMStateDescription vmstate_gbpa = { .version_id = 1, .minimum_version_id = 1, .needed = smmuv3_gbpa_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(gbpa, SMMUv3State), VMSTATE_END_OF_LIST() } @@ -1797,7 +1937,7 @@ static const VMStateDescription vmstate_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), @@ -1822,21 +1962,21 @@ static const VMStateDescription vmstate_smmuv3 = { VMSTATE_END_OF_LIST(), }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_gbpa, NULL } }; -static Property smmuv3_properties[] = { +static const 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) @@ -1844,17 +1984,17 @@ static void smmuv3_instance_init(Object *obj) /* Nothing much to do here as of now */ } -static void smmuv3_class_init(ObjectClass *klass, void *data) +static void smmuv3_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); SMMUv3Class *c = ARM_SMMUV3_CLASS(klass); dc->vmsd = &vmstate_smmuv3; - resettable_class_set_parent_phases(rc, NULL, smmu_reset_hold, NULL, + resettable_class_set_parent_phases(rc, NULL, NULL, smmu_reset_exit, &c->parent_phases); - c->parent_realize = dc->realize; - dc->realize = smmu_realize; + device_class_set_parent_realize(dc, smmu_realize, + &c->parent_realize); device_class_set_props(dc, smmuv3_properties); } @@ -1891,7 +2031,7 @@ static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu, } static void smmuv3_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); @@ -1916,8 +2056,8 @@ static const TypeInfo smmuv3_iommu_memory_region_info = { static void smmuv3_register_types(void) { - type_register(&smmuv3_type_info); - type_register(&smmuv3_iommu_memory_region_info); + type_register_static(&smmuv3_type_info); + type_register_static(&smmuv3_iommu_memory_region_info); } type_init(smmuv3_register_types) diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c deleted file mode 100644 index f732fe0acf9..00000000000 --- a/hw/arm/spitz.c +++ /dev/null @@ -1,1277 +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 *rom = g_new(MemoryRegion, 1); - - /* Setup CPU & memory */ - mpu = pxa270_init(spitz_binfo.ram_size, machine->cpu_type); - sms->mpu = mpu; - - sl_flash_register(mpu, (model == spitz) ? FLASH_128M : FLASH_1024M); - - memory_region_init_rom(rom, NULL, "spitz.rom", SPITZ_ROM, &error_fatal); - memory_region_add_subregion(get_system_memory(), 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 aa5b0ddfaa5..031ea3a24e7 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" +#include "qemu/bitops.h" #include "qapi/error.h" #include "hw/core/split-irq.h" #include "hw/sysbus.h" @@ -19,11 +20,11 @@ #include "net/net.h" #include "hw/boards.h" #include "qemu/log.h" -#include "exec/address-spaces.h" -#include "sysemu/sysemu.h" +#include "system/address-spaces.h" +#include "system/system.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 +32,8 @@ #include "hw/timer/stellaris-gptm.h" #include "hw/qdev-clock.h" #include "qom/object.h" +#include "qobject/qlist.h" +#include "ui/input.h" #define GPIO_A 0 #define GPIO_B 1 @@ -45,6 +48,32 @@ #define BP_GAMEPAD 0x04 #define NUM_IRQ_LINES 64 +#define NUM_PRIO_BITS 3 + +#define NUM_GPIO 7 +#define NUM_UART 4 +#define NUM_GPTM 4 +#define NUM_I2C 2 + +/* + * See Stellaris Data Sheet chapter 5.2.5 "System Control", + * Register 13 .. 17: Device Capabilities 0 .. 4 (DC0 .. DC4). + */ +#define DC1_WDT 3 +#define DC1_HIB 6 +#define DC1_MPU 7 +#define DC1_ADC 16 +#define DC1_PWM 20 +#define DC2_UART(n) (n) +#define DC2_SSI 4 +#define DC2_QEI(n) (8 + n) +#define DC2_I2C(n) (12 + 2 * n) +#define DC2_GPTM(n) (16 + n) +#define DC2_COMP(n) (24 + n) +#define DC4_GPIO(n) (n) +#define DC4_EMAC 28 + +#define DEV_CAP(_dc, _cap) extract32(board->dc##_dc, DC##_dc##_##_cap, 1) typedef const struct { const char *name; @@ -98,7 +127,7 @@ static void ssys_update(ssys_state *s) qemu_set_irq(s->irq, (s->int_status & s->int_mask) != 0); } -static uint32_t pllcfg_sandstorm[16] = { +static const uint32_t pllcfg_sandstorm[16] = { 0x31c0, /* 1 Mhz */ 0x1ae0, /* 1.8432 Mhz */ 0x18c0, /* 2 Mhz */ @@ -117,7 +146,7 @@ static uint32_t pllcfg_sandstorm[16] = { 0x585b /* 8.192 Mhz */ }; -static uint32_t pllcfg_fury[16] = { +static const uint32_t pllcfg_fury[16] = { 0x3200, /* 1 Mhz */ 0x1b20, /* 1.8432 Mhz */ 0x1900, /* 2 Mhz */ @@ -391,7 +420,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 +428,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 +446,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), @@ -435,7 +464,7 @@ static const VMStateDescription vmstate_stellaris_sys = { } }; -static Property stellaris_sys_properties[] = { +static const Property stellaris_sys_properties[] = { DEFINE_PROP_UINT32("user0", ssys_state, user0, 0), DEFINE_PROP_UINT32("user1", ssys_state, user1, 0), DEFINE_PROP_UINT32("did0", ssys_state, did0, 0), @@ -445,7 +474,6 @@ static Property stellaris_sys_properties[] = { DEFINE_PROP_UINT32("dc2", ssys_state, dc2, 0), DEFINE_PROP_UINT32("dc3", ssys_state, dc3, 0), DEFINE_PROP_UINT32("dc4", ssys_state, dc4, 0), - DEFINE_PROP_END_OF_LIST() }; static void stellaris_sys_instance_init(Object *obj) @@ -459,7 +487,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 +635,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 +654,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 +673,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 +699,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, @@ -770,8 +812,9 @@ static void stellaris_adc_trigger(void *opaque, int irq, int level) } } -static void stellaris_adc_reset(StellarisADCState *s) +static void stellaris_adc_reset_hold(Object *obj, ResetType type) { + StellarisADCState *s = STELLARIS_ADC(obj); int n; for (n = 0; n < 4; n++) { @@ -899,7 +942,7 @@ static const VMStateDescription vmstate_stellaris_adc = { .name = "stellaris_adc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(actss, StellarisADCState), VMSTATE_UINT32(ris, StellarisADCState), VMSTATE_UINT32(im, StellarisADCState), @@ -943,12 +986,11 @@ 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); } /* Board init. */ -static stellaris_board_info stellaris_boards[] = { +static const stellaris_board_info stellaris_boards[] = { { "LM3S811EVB", 0, 0x0032000e, @@ -973,19 +1015,20 @@ static stellaris_board_info stellaris_boards[] = { static void stellaris_init(MachineState *ms, stellaris_board_info *board) { - static const int uart_irq[] = {5, 6, 33, 34}; - static const int timer_irq[] = {19, 21, 23, 35}; - static const uint32_t gpio_addr[7] = + static const int uart_irq[NUM_UART] = {5, 6, 33, 34}; + static const int timer_irq[NUM_GPTM] = {19, 21, 23, 35}; + static const uint32_t gpio_addr[NUM_GPIO] = { 0x40004000, 0x40005000, 0x40006000, 0x40007000, 0x40024000, 0x40025000, 0x40026000}; - static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31}; + static const int gpio_irq[NUM_GPIO] = {0, 1, 2, 3, 4, 30, 31}; + static const uint32_t i2c_addr[NUM_I2C] = {0x40020000, 0x40021000}; + static const int i2c_irq[NUM_I2C] = {8, 37}; /* Memory map of SoC devices, from * Stellaris LM3S6965 Microcontroller Data Sheet (rev I) * http://www.ti.com/lit/ds/symlink/lm3s6965.pdf * * 40000000 wdtimer - * 40002000 i2c (unimplemented) * 40004000 GPIO * 40005000 GPIO * 40006000 GPIO @@ -1014,18 +1057,20 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) * 400fe000 system control */ - DeviceState *gpio_dev[7], *nvic; - qemu_irq gpio_in[7][8]; - qemu_irq gpio_out[7][8]; + Object *soc_container; + DeviceState *gpio_dev[NUM_GPIO], *armv7m, *nvic; + qemu_irq gpio_in[NUM_GPIO][8]; + qemu_irq gpio_out[NUM_GPIO][8]; qemu_irq adc; int sram_size; int flash_size; - I2CBus *i2c; + DeviceState *i2c_dev[NUM_I2C] = { }; DeviceState *dev; 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); @@ -1034,6 +1079,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(TYPE_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); @@ -1048,12 +1096,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); @@ -1063,23 +1122,26 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) qdev_prop_set_uint32(ssys_dev, "dc4", board->dc4); sysbus_realize_and_unref(SYS_BUS_DEVICE(ssys_dev), &error_fatal); - nvic = qdev_new(TYPE_ARMV7M); - qdev_prop_set_uint32(nvic, "num-irq", NUM_IRQ_LINES); - qdev_prop_set_string(nvic, "cpu-type", ms->cpu_type); - qdev_prop_set_bit(nvic, "enable-bitband", true); - qdev_connect_clock_in(nvic, "cpuclk", + armv7m = qdev_new(TYPE_ARMV7M); + object_property_add_child(soc_container, "v7m", OBJECT(armv7m)); + qdev_prop_set_uint32(armv7m, "num-irq", NUM_IRQ_LINES); + qdev_prop_set_uint8(armv7m, "num-prio-bits", NUM_PRIO_BITS); + qdev_prop_set_string(armv7m, "cpu-type", ms->cpu_type); + qdev_prop_set_bit(armv7m, "enable-bitband", true); + qdev_connect_clock_in(armv7m, "cpuclk", qdev_get_clock_out(ssys_dev, "SYSCLK")); /* This SoC does not connect the systick reference clock */ - object_property_set_link(OBJECT(nvic), "memory", + object_property_set_link(OBJECT(armv7m), "memory", OBJECT(get_system_memory()), &error_abort); /* This will exit with an error if the user passed us a bad cpu_type */ - sysbus_realize_and_unref(SYS_BUS_DEVICE(nvic), &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(armv7m), &error_fatal); + nvic = armv7m; /* Now we can wire up the IRQ and MMIO of the system registers */ sysbus_mmio_map(SYS_BUS_DEVICE(ssys_dev), 0, 0x400fe000); sysbus_connect_irq(SYS_BUS_DEVICE(ssys_dev), 0, qdev_get_gpio_in(nvic, 28)); - if (board->dc1 & (1 << 16)) { + if (DEV_CAP(1, ADC)) { dev = sysbus_create_varargs(TYPE_STELLARIS_ADC, 0x40038000, qdev_get_gpio_in(nvic, 14), qdev_get_gpio_in(nvic, 15), @@ -1090,12 +1152,13 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) } else { adc = NULL; } - for (i = 0; i < 4; i++) { - if (board->dc2 & (0x10000 << i)) { + for (i = 0; i < NUM_GPTM; i++) { + if (DEV_CAP(2, GPTM(i))) { SysBusDevice *sbd; 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); @@ -1107,9 +1170,9 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) } } - if (board->dc1 & (1 << 3)) { /* watchdog present */ + if (DEV_CAP(1, WDT)) { 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")); @@ -1123,8 +1186,8 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) } - for (i = 0; i < 7; i++) { - if (board->dc4 & (1 << i)) { + for (i = 0; i < NUM_GPIO; i++) { + if (DEV_CAP(4, GPIO(i))) { gpio_dev[i] = sysbus_create_simple("pl061_luminary", gpio_addr[i], qdev_get_gpio_in(nvic, gpio_irq[i])); @@ -1135,20 +1198,25 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) } } - if (board->dc2 & (1 << 12)) { - dev = sysbus_create_simple(TYPE_STELLARIS_I2C, 0x40020000, - qdev_get_gpio_in(nvic, 8)); - i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); - if (board->peripherals & BP_OLED_I2C) { - i2c_slave_create_simple(i2c, "ssd0303", 0x3d); + for (i = 0; i < NUM_I2C; i++) { + if (DEV_CAP(2, I2C(i))) { + i2c_dev[i] = sysbus_create_simple(TYPE_STELLARIS_I2C, i2c_addr[i], + qdev_get_gpio_in(nvic, + i2c_irq[i])); } } + if (board->peripherals & BP_OLED_I2C) { + I2CBus *bus = (I2CBus *)qdev_get_child_bus(i2c_dev[0], "i2c"); + + i2c_slave_create_simple(bus, "ssd0303", 0x3d); + } - for (i = 0; i < 4; i++) { - if (board->dc2 & (1 << i)) { + for (i = 0; i < NUM_UART; i++) { + if (DEV_CAP(2, UART(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); @@ -1156,7 +1224,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(nvic, uart_irq[i])); } } - if (board->dc2 & (1 << 4)) { + if (DEV_CAP(2, SSI)) { dev = sysbus_create_simple("pl022", 0x40008000, qdev_get_gpio_in(nvic, 7)); if (board->peripherals & BP_OLED_SSI) { @@ -1242,10 +1310,13 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) &error_fatal); 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( @@ -1262,28 +1333,47 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) qemu_irq_raise(gpio_out[GPIO_D][0]); } } - if (board->dc4 & (1 << 28)) { + if (DEV_CAP(4, EMAC)) { 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 }; - - 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 */ - - stellaris_gamepad_init(5, gpad_irq, gpad_keycode); + 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 = 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); + + 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)) { @@ -1298,8 +1388,6 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) /* Add dummy regions for the devices we don't implement yet, * so guest accesses don't cause unlogged crashes. */ - create_unimplemented_device("i2c-0", 0x40002000, 0x1000); - create_unimplemented_device("i2c-2", 0x40021000, 0x1000); create_unimplemented_device("PWM", 0x40028000, 0x1000); create_unimplemented_device("QEI-0", 0x4002c000, 0x1000); create_unimplemented_device("QEI-1", 0x4002d000, 0x1000); @@ -1307,7 +1395,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) create_unimplemented_device("hibernation", 0x400fc000, 0x1000); create_unimplemented_device("flash-control", 0x400fd000, 0x1000); - armv7m_load_kernel(ARM_CPU(first_cpu), ms->kernel_filename, 0, flash_size); + armv7m_load_kernel(ARMV7M(armv7m)->cpu, ms->kernel_filename, 0, flash_size); } /* FIXME: Figure out how to generate these from stellaris_boards. */ @@ -1321,7 +1409,11 @@ static void lm3s6965evb_init(MachineState *machine) stellaris_init(machine, &stellaris_boards[1]); } -static void lm3s811evb_class_init(ObjectClass *oc, void *data) +/* + * Stellaris LM3S811 Evaluation Board Schematics: + * https://www.ti.com/lit/ug/symlink/spmu030.pdf + */ +static void lm3s811evb_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1337,7 +1429,11 @@ static const TypeInfo lm3s811evb_type = { .class_init = lm3s811evb_class_init, }; -static void lm3s6965evb_class_init(ObjectClass *oc, void *data) +/* + * Stellaris: LM3S6965 Evaluation Board Schematics: + * https://www.ti.com/lit/ug/symlink/spmu029.pdf + */ +static void lm3s6965evb_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1345,6 +1441,7 @@ static void lm3s6965evb_class_init(ObjectClass *oc, void *data) mc->init = lm3s6965evb_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); + mc->auto_create_sdcard = true; } static const TypeInfo lm3s6965evb_type = { @@ -1361,10 +1458,14 @@ static void stellaris_machine_init(void) type_init(stellaris_machine_init) -static void stellaris_i2c_class_init(ObjectClass *klass, void *data) +static void stellaris_i2c_class_init(ObjectClass *klass, const 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; } @@ -1376,10 +1477,12 @@ static const TypeInfo stellaris_i2c_info = { .class_init = stellaris_i2c_class_init, }; -static void stellaris_adc_class_init(ObjectClass *klass, void *data) +static void stellaris_adc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + rc->phases.hold = stellaris_adc_reset_hold; dc->vmsd = &vmstate_stellaris_adc; } @@ -1391,7 +1494,7 @@ static const TypeInfo stellaris_adc_info = { .class_init = stellaris_adc_class_init, }; -static void stellaris_sys_class_init(ObjectClass *klass, void *data) +static void stellaris_sys_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/arm/stm32f100_soc.c b/hw/arm/stm32f100_soc.c index f7b344ba9fb..0702d51cc3e 100644 --- a/hw/arm/stm32f100_soc.c +++ b/hw/arm/stm32f100_soc.c @@ -27,12 +27,12 @@ #include "qapi/error.h" #include "qemu/module.h" #include "hw/arm/boot.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/arm/stm32f100_soc.h" #include "hw/qdev-properties.h" #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" -#include "sysemu/sysemu.h" +#include "system/system.h" /* stm32f100_soc implementation is derived from stm32f205_soc */ @@ -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) +static void stm32f100_soc_class_init(ObjectClass *klass, const 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 c6b75a381d9..229af7fb108 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -26,11 +26,11 @@ #include "qapi/error.h" #include "qemu/module.h" #include "hw/arm/boot.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/arm/stm32f205_soc.h" #include "hw/qdev-properties.h" #include "hw/qdev-clock.h" -#include "sysemu/sysemu.h" +#include "system/system.h" /* At the moment only Timer 2 to 5 are modelled */ static const uint32_t timer_addr[STM_NUM_TIMERS] = { 0x40000000, 0x40000400, @@ -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) +static void stm32f205_soc_class_init(ObjectClass *klass, const 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 cef23d7ee41..c8684e2b4c5 100644 --- a/hw/arm/stm32f405_soc.c +++ b/hw/arm/stm32f405_soc.c @@ -24,12 +24,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "exec/address-spaces.h" -#include "sysemu/sysemu.h" +#include "system/address-spaces.h" +#include "system/system.h" #include "hw/arm/stm32f405_soc.h" #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++) { @@ -149,7 +152,8 @@ static void stm32f405_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-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); @@ -159,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)) { @@ -275,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); @@ -287,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) +static void stm32f405_soc_class_init(ObjectClass *klass, const 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 00000000000..64da5559c07 --- /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 "system/address-spaces.h" +#include "system/system.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, const 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, const void *data) +{ + Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); + + ssc->flash_size = 256 * KiB; +} + +static void stm32l4x5xe_soc_class_init(ObjectClass *oc, const void *data) +{ + Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); + + ssc->flash_size = 512 * KiB; +} + +static void stm32l4x5xg_soc_class_init(ObjectClass *oc, const 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 67675e952fc..e6c1f5b8d7d 100644 --- a/hw/arm/stm32vldiscovery.c +++ b/hw/arm/stm32vldiscovery.c @@ -47,19 +47,25 @@ static void stm32vldiscovery_init(MachineState *machine) clock_set_hz(sysclk, SYSCLK_FRQ); dev = qdev_new(TYPE_STM32F100_SOC); - qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3")); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - armv7m_load_kernel(ARM_CPU(first_cpu), + armv7m_load_kernel(STM32F100_SOC(dev)->armv7m.cpu, machine->kernel_filename, 0, FLASH_SIZE); } static void stm32vldiscovery_machine_init(MachineClass *mc) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m3"), + NULL + }; + mc->desc = "ST STM32VLDISCOVERY (Cortex-M3)"; mc->init = stm32vldiscovery_init; + mc->valid_cpu_types = valid_cpu_types; } DEFINE_MACHINE("stm32vldiscovery", stm32vldiscovery_machine_init) diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index cc73145053a..229c98ddd90 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" @@ -39,15 +38,15 @@ #include "hw/arm/boot.h" #include "chardev/char-fe.h" #include "chardev/char-serial.h" -#include "sysemu/sysemu.h" -#include "sysemu/rtc.h" +#include "system/system.h" +#include "system/rtc.h" #include "hw/ssi/ssi.h" #include "qapi/error.h" #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" HWADDR_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" HWADDR_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), @@ -220,7 +215,7 @@ static const VMStateDescription vmstate_strongarm_pic_regs = { }, }; -static void strongarm_pic_class_init(ObjectClass *klass, void *data) +static void strongarm_pic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -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" HWADDR_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" HWADDR_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), @@ -449,7 +448,8 @@ static const VMStateDescription vmstate_strongarm_rtc_regs = { }, }; -static void strongarm_rtc_sysbus_class_init(ObjectClass *klass, void *data) +static void strongarm_rtc_sysbus_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -556,12 +556,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 +581,9 @@ static uint64_t strongarm_gpio_read(void *opaque, hwaddr offset, return s->status; default: - printf("%s: Bad offset 0x" HWADDR_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 +628,9 @@ static void strongarm_gpio_write(void *opaque, hwaddr offset, break; default: - printf("%s: Bad offset 0x" HWADDR_FMT_plx "\n", __func__, offset); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad write offset 0x"HWADDR_FMT_plx"\n", + __func__, offset); } } @@ -677,7 +681,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), @@ -690,7 +694,7 @@ static const VMStateDescription vmstate_strongarm_gpio_regs = { }, }; -static void strongarm_gpio_class_init(ObjectClass *klass, void *data) +static void strongarm_gpio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -782,7 +786,9 @@ static uint64_t strongarm_ppc_read(void *opaque, hwaddr offset, return s->ppfr | ~0x7f001; default: - printf("%s: Bad offset 0x" HWADDR_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 +823,9 @@ static void strongarm_ppc_write(void *opaque, hwaddr offset, break; default: - printf("%s: Bad offset 0x" HWADDR_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 +854,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), @@ -858,7 +866,7 @@ static const VMStateDescription vmstate_strongarm_ppc_regs = { }, }; -static void strongarm_ppc_class_init(ObjectClass *klass, void *data) +static void strongarm_ppc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1029,8 +1037,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 +1177,9 @@ static uint64_t strongarm_uart_read(void *opaque, hwaddr addr, return s->utsr1; default: - printf("%s: Bad register 0x" HWADDR_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 +1236,9 @@ static void strongarm_uart_write(void *opaque, hwaddr addr, break; default: - printf("%s: Bad register 0x" HWADDR_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 +1317,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), @@ -1316,17 +1333,16 @@ static const VMStateDescription vmstate_strongarm_uart_regs = { }, }; -static Property strongarm_uart_properties[] = { +static const Property strongarm_uart_properties[] = { DEFINE_PROP_CHR("chardev", StrongARMUARTState, chr), - DEFINE_PROP_END_OF_LIST(), }; -static void strongarm_uart_class_init(ObjectClass *klass, void *data) +static void strongarm_uart_class_init(ObjectClass *klass, const 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" HWADDR_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" HWADDR_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), @@ -1568,12 +1590,12 @@ static const VMStateDescription vmstate_strongarm_ssp_regs = { }, }; -static void strongarm_ssp_class_init(ObjectClass *klass, void *data) +static void strongarm_ssp_class_init(ObjectClass *klass, const 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/strongarm.h b/hw/arm/strongarm.h index 192821f6aab..b11b3a3379f 100644 --- a/hw/arm/strongarm.h +++ b/hw/arm/strongarm.h @@ -1,7 +1,7 @@ #ifndef STRONGARM_H #define STRONGARM_H -#include "exec/memory.h" +#include "system/memory.h" #include "target/arm/cpu-qom.h" #define SA_CS0 0x00000000 diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c deleted file mode 100644 index 3ca2e4459ca..00000000000 --- 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(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 cdc1ea06a81..f3386bd7ae1 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -1,5 +1,12 @@ # See docs/devel/tracing.rst for syntax documentation. +# omap1.c +omap1_pwl_clocking_scheme(const char *scheme) "omap1 CLKM: clocking scheme set to %s" +omap1_pwl_backlight(int output) "omap1 PWL: backlight now at %d/256" +omap1_pwt_buzz(int freq) "omap1 PWT: %dHz buzz on" +omap1_pwt_silence(void) "omap1 PWT: buzzer silenced" +omap1_lpg_led(const char *onoff) "omap1 LPG: LED is %s" + # virt-acpi-build.c virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out." @@ -11,13 +18,16 @@ smmu_ptw_page_pte(int stage, int level, uint64_t iova, uint64_t baseaddr, uint6 smmu_ptw_block_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t iova, uint64_t gpa, int bsize_mb) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" iova=0x%"PRIx64" block address = 0x%"PRIx64" block size = %d MiB" smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte) "baseaddr=0x%"PRIx64" index=0x%x, pteaddr=0x%"PRIx64", pte=0x%"PRIx64 smmu_iotlb_inv_all(void) "IOTLB invalidate all" -smmu_iotlb_inv_asid(uint16_t asid) "IOTLB invalidate asid=%d" -smmu_iotlb_inv_vmid(uint16_t vmid) "IOTLB invalidate vmid=%d" -smmu_iotlb_inv_iova(uint16_t asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64 +smmu_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_configs_inv_sid_range(uint32_t start, uint32_t end) "Config cache INV SID range from 0x%x to 0x%x" +smmu_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x" smmu_inv_notifiers_mr(const char *name) "iommu mr=%s" -smmu_iotlb_lookup_hit(uint16_t asid, uint16_t vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" -smmu_iotlb_lookup_miss(uint16_t asid, uint16_t vmid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS asid=%d vmid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" -smmu_iotlb_insert(uint16_t asid, uint16_t vmid, uint64_t addr, uint8_t tg, uint8_t level) "IOTLB ++ asid=%d vmid=%d addr=0x%"PRIx64" tg=%d level=%d" +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)" @@ -37,7 +47,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" @@ -46,12 +56,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_range_inval(int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf) "vmid=%d asid=%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d" -smmuv3_cmdq_tlbi_nh(void) "" -smmuv3_cmdq_tlbi_nh_asid(uint16_t asid) "asid=%d" -smmuv3_cmdq_tlbi_s12_vmid(uint16_t vmid) "vmid=%d" -smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x" +smmuv3_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_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, uint16_t vmid, uint64_t iova, uint8_t tg, uint64_t num_pages) "iommu mr=%s asid=%d vmid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64 +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" +smmu_reset_exit(void) "" + +# 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 05b9462a5b7..5cf1a70d10d 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -9,13 +9,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "cpu.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "hw/arm/boot.h" #include "hw/net/smc91c111.h" #include "net/net.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/pci/pci.h" #include "hw/i2c/i2c.h" #include "hw/i2c/arm_sbcon_i2c.h" @@ -26,6 +25,9 @@ #include "hw/char/pl011.h" #include "hw/sd/sd.h" #include "qom/object.h" +#include "audio/audio.h" +#include "target/arm/cpu-qom.h" +#include "qemu/log.h" #define VERSATILE_FLASH_ADDR 0x34000000 #define VERSATILE_FLASH_SIZE (64 * 1024 * 1024) @@ -51,7 +53,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), @@ -109,7 +111,8 @@ static uint64_t vpb_sic_read(void *opaque, hwaddr offset, case 8: /* PICENABLE */ return s->pic_enable; default: - printf ("vpb_sic_read: Bad register offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "vpb_sic_read: Bad register offset 0x%x\n", (int)offset); return 0; } } @@ -143,7 +146,8 @@ static void vpb_sic_write(void *opaque, hwaddr offset, vpb_sic_update_pic(s); break; default: - printf ("vpb_sic_write: Bad register offset 0x%x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, + "vpb_sic_write: Bad register offset 0x%x\n", (int)offset); return; } vpb_sic_update(s); @@ -191,10 +195,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 +264,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 +302,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)); @@ -343,6 +346,9 @@ static void versatile_init(MachineState *machine, int board_id) /* 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]); @@ -406,7 +412,7 @@ static void vab_init(MachineState *machine) versatile_init(machine, 0x25e); } -static void versatilepb_class_init(ObjectClass *oc, void *data) +static void versatilepb_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -416,6 +422,9 @@ 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"; + mc->auto_create_sdcard = true; + + machine_add_audiodev_property(mc); } static const TypeInfo versatilepb_type = { @@ -424,7 +433,7 @@ static const TypeInfo versatilepb_type = { .class_init = versatilepb_class_init, }; -static void versatileab_class_init(ObjectClass *oc, void *data) +static void versatileab_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -434,6 +443,9 @@ 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"; + mc->auto_create_sdcard = true; + + machine_add_audiodev_property(mc); } static const TypeInfo versatileab_type = { @@ -450,7 +462,7 @@ static void versatile_machine_init(void) type_init(versatile_machine_init) -static void vpb_sic_class_init(ObjectClass *klass, void *data) +static void vpb_sic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 56abadd9b8b..35f8d05ea17 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -24,18 +24,17 @@ #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" #include "hw/net/lan9118.h" #include "hw/i2c/i2c.h" #include "net/net.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/block/flash.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "qemu/error-report.h" #include #include "hw/char/pl011.h" @@ -43,12 +42,17 @@ #include "hw/cpu/a15mpcore.h" #include "hw/i2c/arm_sbcon_i2c.h" #include "hw/sd/sd.h" +#include "qobject/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) #define VEXPRESS_FLASH_SECT_SIZE (256 * 1024) +#define GIC_EXT_IRQS 64 /* Versatile Express A9 development board */ + /* Number of virtio transports to create (0..8; limited by * number of available IRQ lines). */ @@ -176,7 +180,6 @@ struct VexpressMachineState { MemoryRegion vram; MemoryRegion sram; MemoryRegion flashalias; - MemoryRegion lowram; MemoryRegion a15sram; bool secure; bool virt; @@ -240,6 +243,7 @@ static void init_cpus(MachineState *ms, const char *cpu_type, */ dev = qdev_new(privdev); qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); + qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL); busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, periphbase); @@ -250,7 +254,7 @@ static void init_cpus(MachineState *ms, const char *cpu_type, * external interrupts starting from 32 (because there * are internal interrupts 0..31). */ - for (n = 0; n < 64; n++) { + for (n = 0; n < GIC_EXT_IRQS; n++) { pic[n] = qdev_get_gpio_in(dev, n); } @@ -275,7 +279,7 @@ static void a9_daughterboard_init(VexpressMachineState *vms, { MachineState *machine = MACHINE(vms); MemoryRegion *sysmem = get_system_memory(); - ram_addr_t low_ram_size; + DeviceState *dev; if (ram_size > 0x40000000) { /* 1GB is the maximum the address space permits */ @@ -283,17 +287,11 @@ static void a9_daughterboard_init(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(&vms->lowram, NULL, "vexpress.lowmem", - machine->ram, 0, low_ram_size); - memory_region_add_subregion(sysmem, 0x0, &vms->lowram); memory_region_add_subregion(sysmem, 0x60000000, machine->ram); /* 0x1e000000 A9MPCore (SCU) private memory region */ @@ -303,7 +301,12 @@ static void a9_daughterboard_init(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 */ @@ -543,7 +546,7 @@ static void vexpress_common_init(MachineState *machine) VexpressMachineClass *vmc = VEXPRESS_MACHINE_GET_CLASS(machine); VEDBoardInfo *daughterboard = vmc->daughterboard; DeviceState *dev, *sysctl, *pl041; - qemu_irq pic[64]; + qemu_irq pic[GIC_EXT_IRQS]; uint32_t sys_id; DriveInfo *dinfo; PFlashCFI01 *pflash0; @@ -551,6 +554,7 @@ static void vexpress_common_init(MachineState *machine) ram_addr_t vram_size, sram_size; MemoryRegion *sysmem = get_system_memory(); const hwaddr *map = daughterboard->motherboard_map; + QList *db_voltage, *db_clock; int i; daughterboard->init(vms, machine->ram_size, machine->cpu_type, pic); @@ -591,20 +595,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]); @@ -613,6 +616,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]); @@ -653,7 +659,12 @@ 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", @@ -682,8 +693,8 @@ static void vexpress_common_init(MachineState *machine) 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 */ @@ -766,7 +777,7 @@ static void vexpress_a9_instance_init(Object *obj) vms->virt = false; } -static void vexpress_class_init(ObjectClass *oc, void *data) +static void vexpress_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -776,6 +787,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", @@ -783,24 +795,34 @@ static void vexpress_class_init(ObjectClass *oc, void *data) "Security Extensions (TrustZone)"); } -static void vexpress_a9_class_init(ObjectClass *oc, void *data) +static void vexpress_a9_class_init(ObjectClass *oc, const 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; + mc->auto_create_sdcard = true; vmc->daughterboard = &a9_daughterboard; } -static void vexpress_a15_class_init(ObjectClass *oc, void *data) +static void vexpress_a15_class_init(ObjectClass *oc, const 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; + mc->auto_create_sdcard = true; vmc->daughterboard = &a15_daughterboard; diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 6b674231c27..7e8e0f0298d 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -32,10 +32,9 @@ #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" @@ -52,12 +51,14 @@ #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 "system/numa.h" +#include "system/reset.h" +#include "system/tpm.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 @@ -77,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, @@ -94,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; @@ -133,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) { @@ -193,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)); @@ -248,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 */ @@ -308,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; @@ -345,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); } @@ -405,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 */ @@ -454,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); @@ -471,48 +437,38 @@ 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 }; - - 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); + 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, + }; + /* + * Passing NULL as the SPCR Table for Revision 2 doesn't support + * NameSpaceString. + */ + build_spcr(table_data, linker, &serial, 2, vms->oem_id, vms->oem_table_id, + NULL); } /* @@ -558,6 +514,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); } @@ -573,22 +531,19 @@ 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) { - VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); /* * Table 5-117 Flag Definitions * set only "Timer interrupt Mode" and assume "Timer Interrupt * polarity" bit as '0: Interrupt is Active high' */ - 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, + const uint32_t irqflags = 0; /* Interrupt is Level triggered */ + AcpiTable table = { .sig = "GTDT", .rev = 3, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; acpi_table_begin(&table, table_data); @@ -601,21 +556,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 */ @@ -624,7 +579,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); } @@ -673,12 +636,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); @@ -704,7 +667,6 @@ static void build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { int i; - VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); const MemMapEntry *memmap = vms->memmap; AcpiTable table = { .sig = "APIC", .rev = 4, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; @@ -729,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; @@ -760,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 */ @@ -775,7 +737,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) memmap[VIRT_HIGH_GIC_REDIST2].size); } - if (its_class_name() && !vmc->no_its) { + if (its_class_name()) { /* * ACPI spec, Revision 6.0 Errata A * (original 6.0 definition has invalid Length) @@ -811,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, }; @@ -859,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, @@ -969,16 +936,15 @@ 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); if (vms->ras) { - build_ghes_error_table(tables->hardware_errors, tables->linker); acpi_add_table(table_offsets, tables_blob); - acpi_build_hest(tables_blob, tables->linker, vms->oem_id, - vms->oem_table_id); + acpi_build_hest(tables_blob, tables->hardware_errors, tables->linker, + vms->oem_id, vms->oem_table_id); } if (ms->numa_state->num_nodes > 0) { @@ -1003,7 +969,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) vms->oem_table_id); } - if (its_class_name() && !vmc->no_its) { + if (its_class_name()) { acpi_add_table(table_offsets, tables_blob); build_iort(tables_blob, tables->linker, vms); } @@ -1047,7 +1013,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); @@ -1100,7 +1066,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 8ad78b23c24..99fde5836c9 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -42,17 +42,18 @@ #include "hw/vfio/vfio-amd-xgbe.h" #include "hw/display/ramfb.h" #include "net/net.h" -#include "sysemu/device_tree.h" -#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 "system/device_tree.h" +#include "system/numa.h" +#include "system/runstate.h" +#include "system/tpm.h" +#include "system/tcg.h" +#include "system/kvm.h" +#include "system/hvf.h" +#include "system/qtest.h" #include "hw/loader.h" #include "qapi/error.h" #include "qemu/bitops.h" +#include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "hw/pci-host/gpex.h" @@ -66,47 +67,74 @@ #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 "qobject/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 "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/uefi/var-service-api.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, \ + const 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), \ + static const TypeInfo MACHINE_VER_SYM(info, virt, __VA_ARGS__) = \ + { \ + .name = MACHINE_VER_TYPE_NAME("virt", __VA_ARGS__), \ .parent = TYPE_VIRT_MACHINE, \ - .class_init = virt_##major##_##minor##_class_init, \ + .class_init = MACHINE_VER_SYM(class_init, virt, __VA_ARGS__), \ }; \ - static void machvirt_machine_##major##_##minor##_init(void) \ + static void MACHINE_VER_SYM(register, virt, __VA_ARGS__)(void) \ { \ - type_register_static(&machvirt_##major##_##minor##_info); \ + 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 */ @@ -144,11 +172,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 }, @@ -166,6 +194,10 @@ static const MemMapEntry base_memmap[] = { [VIRT_MEM] = { GiB, LEGACY_RAMLIMIT_BYTES }, }; +/* Update the docs for highmem-mmio-size when changing this default */ +#define DEFAULT_HIGH_PCIE_MMIO_SIZE_GB 512 +#define DEFAULT_HIGH_PCIE_MMIO_SIZE (DEFAULT_HIGH_PCIE_MMIO_SIZE_GB * GiB) + /* * Highmem IO Regions: This memory map is floating, located after the RAM. * Each MemMapEntry base (GPA) will be dynamically computed, depending on the @@ -181,21 +213,24 @@ static const MemMapEntry base_memmap[] = { * 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. + * + * Note that the highmem-mmio-size property will update the high PCIE MMIO size + * field in this array. */ static MemMapEntry extended_memmap[] = { /* Additional 64 MB redist region (can contain up to 512 redistributors) */ [VIRT_HIGH_GIC_REDIST2] = { 0x0, 64 * MiB }, [VIRT_HIGH_PCIE_ECAM] = { 0x0, 256 * MiB }, /* Second PCIe window */ - [VIRT_HIGH_PCIE_MMIO] = { 0x0, 512 * GiB }, + [VIRT_HIGH_PCIE_MMIO] = { 0x0, DEFAULT_HIGH_PCIE_MMIO_SIZE }, }; 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 */ @@ -203,37 +238,6 @@ static const int a15irqmap[] = { [VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */ }; -static const char *valid_cpus[] = { -#ifdef CONFIG_TCG - ARM_CPU_TYPE_NAME("cortex-a7"), - ARM_CPU_TYPE_NAME("cortex-a15"), - 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"), -#endif - ARM_CPU_TYPE_NAME("cortex-a53"), - ARM_CPU_TYPE_NAME("cortex-a57"), - 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 { @@ -248,6 +252,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); @@ -267,6 +285,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) { @@ -280,6 +309,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 @@ -339,14 +370,9 @@ static void fdt_add_timer_nodes(const VirtMachineState *vms) * the correct information. */ ARMCPU *armcpu; - VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); uint32_t irqflags = GIC_FDT_IRQ_FLAGS_LEVEL_HI; MachineState *ms = MACHINE(vms); - if (vmc->claim_edge_triggered_timers) { - irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI; - } - if (vms->gic_version == VIRT_GIC_VERSION_2) { irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, GIC_FDT_IRQ_PPI_CPU_WIDTH, @@ -365,11 +391,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) @@ -396,7 +440,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; } @@ -423,10 +467,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) { @@ -570,7 +614,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 { @@ -594,7 +639,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); } } @@ -626,7 +672,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); } } @@ -647,13 +694,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; } @@ -691,10 +737,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, @@ -705,6 +751,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); @@ -747,14 +807,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) { @@ -763,20 +832,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); @@ -796,12 +862,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. */ @@ -810,27 +876,30 @@ 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, + [GTIMER_S_EL2_PHYS] = ARCH_TIMER_S_EL2_IRQ, + [GTIMER_S_EL2_VIRT] = ARCH_TIMER_S_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)); @@ -840,6 +909,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); @@ -852,7 +928,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; @@ -885,9 +961,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"); @@ -931,7 +1011,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); } } @@ -940,7 +1020,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"); @@ -951,7 +1032,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 @@ -1334,6 +1415,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]; @@ -1350,6 +1432,9 @@ static void create_smmu(const VirtMachineState *vms, 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); @@ -1402,9 +1487,12 @@ static void create_virtio_iommu_dt_bindings(VirtMachineState *vms) qemu_fdt_setprop_cell(ms->fdt, node, "phandle", vms->iommu_phandle); g_free(node); - qemu_fdt_setprop_cells(ms->fdt, vms->pciehb_nodename, "iommu-map", - 0x0, vms->iommu_phandle, 0x0, bdf, - bdf + 1, vms->iommu_phandle, bdf + 1, 0xffff - bdf); + if (!vms->default_bus_bypass_iommu) { + qemu_fdt_setprop_cells(ms->fdt, vms->pciehb_nodename, "iommu-map", + 0x0, vms->iommu_phandle, 0x0, bdf, + bdf + 1, vms->iommu_phandle, bdf + 1, + 0xffff - bdf); + } } static void create_pcie(VirtMachineState *vms) @@ -1468,7 +1556,7 @@ static void create_pcie(VirtMachineState *vms) /* Map IO port space */ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio); - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, qdev_get_gpio_in(vms->gic, irq + i)); gpex_set_irq_num(GPEX_HOST(dev), i, irq + i); @@ -1478,9 +1566,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++) { - pci_nic_init_nofail(&nd_table[i], pci->bus, mc->default_nic, NULL); - } + pci_init_nic_devices(pci->bus, mc->default_nic); } nodename = vms->pciehb_nodename = g_strdup_printf("/pcie@%" PRIx64, base); @@ -1529,8 +1615,10 @@ static void create_pcie(VirtMachineState *vms) switch (vms->iommu) { case VIRT_IOMMU_SMMUV3: create_smmu(vms, vms->bus); - qemu_fdt_setprop_cells(ms->fdt, nodename, "iommu-map", - 0x0, vms->iommu_phandle, 0x0, 0x10000); + if (!vms->default_bus_bypass_iommu) { + qemu_fdt_setprop_cells(ms->fdt, nodename, "iommu-map", + 0x0, vms->iommu_phandle, 0x0, 0x10000); + } break; default: g_assert_not_reached(); @@ -1616,7 +1704,6 @@ 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; @@ -1626,15 +1713,13 @@ static void virt_build_smbios(VirtMachineState *vms) product = "KVM Virtual Machine"; } - smbios_set_defaults("QEMU", product, - vmc->smbios_old_sys_ver ? "1.0" : mc->name, false, - true, SMBIOS_ENTRY_POINT_TYPE_64); + smbios_set_defaults("QEMU", product, mc->name); /* 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, &mem_array, 1, + smbios_get_tables(ms, SMBIOS_ENTRY_POINT_TYPE_64, &mem_array, 1, &smbios_tables, &smbios_tables_len, &smbios_anchor, &smbios_anchor_len, &error_fatal); @@ -1670,11 +1755,12 @@ void virt_machine_done(Notifier *notifier, void *data) vms->memmap[VIRT_PLATFORM_BUS].size, vms->irqmap[VIRT_PLATFORM_BUS]); } - if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as, ms) < 0) { + if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as, ms, cpu) < 0) { exit(1); } - fw_cfg_add_extra_pci_roots(vms->bus, vms->fw_cfg); + pci_bus_add_fw_cfg_extra_pci_roots(vms->fw_cfg, vms->bus, + &error_abort); virt_acpi_setup(vms); virt_build_smbios(vms); @@ -1682,25 +1768,19 @@ void virt_machine_done(Notifier *notifier, void *data) static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx) { - uint8_t clustersz = ARM_DEFAULT_CPUS_PER_CLUSTER; - VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); + uint8_t clustersz; - if (!vmc->disallow_affinity_adjustment) { - /* Adjust MPIDR like 64-bit KVM hosts, which incorporate the - * GIC's target-list limitations. 32-bit KVM hosts currently - * always create clusters of 4 CPUs, but that is expected to - * change when they gain support for gicv3. When KVM is enabled - * it will override the changes we make here, therefore our - * purposes are to make TCG consistent (with 64-bit KVM hosts) - * and to improve SGI efficiency. - */ - if (vms->gic_version == VIRT_GIC_VERSION_2) { - clustersz = GIC_TARGETLIST_BITS; - } else { - clustersz = GICV3_TARGETLIST_BITS; - } + /* + * Adjust MPIDR to make TCG consistent (with 64-bit KVM hosts) + * and to improve SGI efficiency. + */ + if (vms->gic_version == VIRT_GIC_VERSION_2) { + clustersz = GIC_TARGETLIST_BITS; + } else { + 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, @@ -1795,8 +1875,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) { @@ -1990,13 +2070,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 { @@ -2031,16 +2112,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; @@ -2140,7 +2217,7 @@ static void machvirt_init(MachineState *machine) 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", current_accel_name()); @@ -2188,10 +2265,6 @@ static void machvirt_init(MachineState *machine) object_property_set_bool(cpuobj, "kvm-steal-time", false, NULL); } - if (vmc->no_pmu && object_property_find(cpuobj, "pmu")) { - object_property_set_bool(cpuobj, "pmu", false, NULL); - } - if (vmc->no_tcg_lpa2 && object_property_find(cpuobj, "lpa2")) { object_property_set_bool(cpuobj, "lpa2", false, NULL); } @@ -2210,45 +2283,62 @@ 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"); - exit(1); + 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); + } } - tag_sysmem = g_new(MemoryRegion, 1); - memory_region_init(tag_sysmem, OBJECT(machine), - "tag-memory", UINT64_MAX / 32); - + object_property_set_link(cpuobj, "tag-memory", + OBJECT(tag_sysmem), &error_abort); 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, "secure-tag-memory", + OBJECT(secure_tag_sysmem), + &error_abort); } - } - - 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); + } + 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); @@ -2263,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) { @@ -2429,6 +2549,40 @@ static void virt_set_highmem_mmio(Object *obj, bool value, Error **errp) vms->highmem_mmio = value; } +static void virt_get_highmem_mmio_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + uint64_t size = extended_memmap[VIRT_HIGH_PCIE_MMIO].size; + + visit_type_size(v, name, &size, errp); +} + +static void virt_set_highmem_mmio_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + uint64_t size; + + if (!visit_type_size(v, name, &size, errp)) { + return; + } + + if (!is_power_of_2(size)) { + error_setg(errp, "highmem-mmio-size is not a power of 2"); + return; + } + + if (size < DEFAULT_HIGH_PCIE_MMIO_SIZE) { + char *sz = size_to_str(DEFAULT_HIGH_PCIE_MMIO_SIZE); + error_setg(errp, "highmem-mmio-size cannot be set to a lower value " + "than the default (%s)", sz); + g_free(sz); + return; + } + + extended_memmap[VIRT_HIGH_PCIE_MMIO].size = size; +} static bool virt_get_its(Object *obj, Error **errp) { @@ -2713,7 +2867,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, @@ -2744,6 +2898,7 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, 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) { @@ -2770,9 +2925,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); } } @@ -2924,10 +3079,69 @@ static int virt_kvm_type(MachineState *ms, const char *type_str) return fixed_ipa ? 0 : requested_pa_size; } -static void virt_machine_class_init(ObjectClass *oc, void *data) +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, const 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. @@ -2939,6 +3153,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_AMD_XGBE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_PLATFORM); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif @@ -2954,8 +3169,10 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) #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; @@ -3024,6 +3241,14 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) "Set on/off to enable/disable high " "memory region for PCI MMIO"); + object_class_property_add(oc, "highmem-mmio-size", "size", + virt_get_highmem_mmio_size, + virt_set_highmem_mmio_size, + NULL, NULL); + object_class_property_set_description(oc, "highmem-mmio-size", + "Set the high memory region size " + "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", @@ -3111,21 +3336,17 @@ static void virt_instance_init(Object *obj) vms->highmem_compact = !vmc->no_highmem_compact; vms->gic_version = VIRT_GIC_VERSION_NOSEL; - vms->highmem_ecam = !vmc->no_highmem_ecam; + vms->highmem_ecam = true; vms->highmem_mmio = true; vms->highmem_redists = true; - if (vmc->no_its) { - vms->its = false; - } else { - /* Default allows ITS instantiation */ - vms->its = true; + /* Default allows ITS instantiation */ + vms->its = true; - if (vmc->no_tcg_its) { - vms->tcg_its = false; - } else { - vms->tcg_its = true; - } + if (vmc->no_tcg_its) { + vms->tcg_its = false; + } else { + vms->tcg_its = true; } /* Default disallows iommu instantiation */ @@ -3159,7 +3380,7 @@ static const TypeInfo virt_machine_info = { .class_size = sizeof(VirtMachineClass), .class_init = virt_machine_class_init, .instance_init = virt_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } }, @@ -3171,10 +3392,58 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); +static void virt_machine_10_1_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(10, 1) + +static void virt_machine_10_0_options(MachineClass *mc) +{ + virt_machine_10_1_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_0, hw_compat_10_0_len); +} +DEFINE_VIRT_MACHINE(10, 0) + +static void virt_machine_9_2_options(MachineClass *mc) +{ + virt_machine_10_0_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_2, hw_compat_9_2_len); +} +DEFINE_VIRT_MACHINE(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_AS_LATEST(8, 2) +DEFINE_VIRT_MACHINE(8, 2) static void virt_machine_8_1_options(MachineClass *mc) { @@ -3298,99 +3567,3 @@ static void virt_machine_4_1_options(MachineClass *mc) mc->auto_enable_numa_with_memhp = false; } DEFINE_VIRT_MACHINE(4, 1) - -static void virt_machine_4_0_options(MachineClass *mc) -{ - virt_machine_4_1_options(mc); - compat_props_add(mc->compat_props, hw_compat_4_0, hw_compat_4_0_len); -} -DEFINE_VIRT_MACHINE(4, 0) - -static void virt_machine_3_1_options(MachineClass *mc) -{ - virt_machine_4_0_options(mc); - compat_props_add(mc->compat_props, hw_compat_3_1, hw_compat_3_1_len); -} -DEFINE_VIRT_MACHINE(3, 1) - -static void virt_machine_3_0_options(MachineClass *mc) -{ - virt_machine_3_1_options(mc); - compat_props_add(mc->compat_props, hw_compat_3_0, hw_compat_3_0_len); -} -DEFINE_VIRT_MACHINE(3, 0) - -static void virt_machine_2_12_options(MachineClass *mc) -{ - VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); - - virt_machine_3_0_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_12, hw_compat_2_12_len); - vmc->no_highmem_ecam = true; - mc->max_cpus = 255; -} -DEFINE_VIRT_MACHINE(2, 12) - -static void virt_machine_2_11_options(MachineClass *mc) -{ - VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); - - virt_machine_2_12_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_11, hw_compat_2_11_len); - vmc->smbios_old_sys_ver = true; -} -DEFINE_VIRT_MACHINE(2, 11) - -static void virt_machine_2_10_options(MachineClass *mc) -{ - virt_machine_2_11_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_10, hw_compat_2_10_len); - /* before 2.11 we never faulted accesses to bad addresses */ - mc->ignore_memory_transaction_failures = true; -} -DEFINE_VIRT_MACHINE(2, 10) - -static void virt_machine_2_9_options(MachineClass *mc) -{ - virt_machine_2_10_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_9, hw_compat_2_9_len); -} -DEFINE_VIRT_MACHINE(2, 9) - -static void virt_machine_2_8_options(MachineClass *mc) -{ - VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); - - virt_machine_2_9_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_8, hw_compat_2_8_len); - /* For 2.8 and earlier we falsely claimed in the DT that - * our timers were edge-triggered, not level-triggered. - */ - vmc->claim_edge_triggered_timers = true; -} -DEFINE_VIRT_MACHINE(2, 8) - -static void virt_machine_2_7_options(MachineClass *mc) -{ - VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); - - virt_machine_2_8_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_7, hw_compat_2_7_len); - /* ITS was introduced with 2.8 */ - vmc->no_its = true; - /* Stick with 1K pages for migration compatibility */ - mc->minimum_page_bits = 0; -} -DEFINE_VIRT_MACHINE(2, 7) - -static void virt_machine_2_6_options(MachineClass *mc) -{ - VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); - - virt_machine_2_7_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_6, hw_compat_2_6_len); - vmc->disallow_affinity_adjustment = true; - /* Disable PMU for 2.6 as PMU support was first introduced in 2.7 */ - vmc->no_pmu = true; -} -DEFINE_VIRT_MACHINE(2, 6) diff --git a/hw/arm/xen-pvh.c b/hw/arm/xen-pvh.c new file mode 100644 index 00000000000..4b26bcff7a5 --- /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 "system/system.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, const 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 00000000000..6a830435533 --- /dev/null +++ b/hw/arm/xen-stubs.c @@ -0,0 +1,30 @@ +/* + * Stubs for unimplemented Xen functions for ARM. + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" +#include "qapi/qapi-commands-migration.h" +#include "system/xen.h" +#include "hw/hw.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); +} + +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/xen_arm.c b/hw/arm/xen_arm.c deleted file mode 100644 index f83b983ec58..00000000000 --- a/hw/arm/xen_arm.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * QEMU ARM Xen PVH Machine - * - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qapi/qapi-commands-migration.h" -#include "qapi/visitor.h" -#include "hw/boards.h" -#include "hw/irq.h" -#include "hw/sysbus.h" -#include "sysemu/block-backend.h" -#include "sysemu/tpm_backend.h" -#include "sysemu/sysemu.h" -#include "hw/xen/xen-hvm-common.h" -#include "sysemu/tpm.h" -#include "hw/xen/arch_hvm.h" - -#define TYPE_XEN_ARM MACHINE_TYPE_NAME("xenpvh") -OBJECT_DECLARE_SIMPLE_TYPE(XenArmState, XEN_ARM) - -static const MemoryListener xen_memory_listener = { - .region_add = xen_region_add, - .region_del = xen_region_del, - .log_start = NULL, - .log_stop = NULL, - .log_sync = NULL, - .log_global_start = NULL, - .log_global_stop = NULL, - .priority = MEMORY_LISTENER_PRIORITY_ACCEL, -}; - -struct XenArmState { - /*< private >*/ - MachineState parent; - - XenIOState *state; - - struct { - uint64_t tpm_base_addr; - } cfg; -}; - -static MemoryRegion ram_lo, ram_hi; - -/* - * VIRTIO_MMIO_DEV_SIZE is imported from tools/libs/light/libxl_arm.c under Xen - * repository. - * - * Origin: git://xenbits.xen.org/xen.git 2128143c114c - */ -#define VIRTIO_MMIO_DEV_SIZE 0x200 - -#define NR_VIRTIO_MMIO_DEVICES \ - (GUEST_VIRTIO_MMIO_SPI_LAST - GUEST_VIRTIO_MMIO_SPI_FIRST) - -static void xen_set_irq(void *opaque, int irq, int level) -{ - xendevicemodel_set_irq_level(xen_dmod, xen_domid, irq, level); -} - -static void xen_create_virtio_mmio_devices(XenArmState *xam) -{ - int i; - - for (i = 0; i < NR_VIRTIO_MMIO_DEVICES; i++) { - hwaddr base = GUEST_VIRTIO_MMIO_BASE + i * VIRTIO_MMIO_DEV_SIZE; - qemu_irq irq = qemu_allocate_irq(xen_set_irq, NULL, - GUEST_VIRTIO_MMIO_SPI_FIRST + i); - - sysbus_create_simple("virtio-mmio", base, irq); - - DPRINTF("Created virtio-mmio device %d: irq %d base 0x%lx\n", - i, GUEST_VIRTIO_MMIO_SPI_FIRST + i, base); - } -} - -static void xen_init_ram(MachineState *machine) -{ - MemoryRegion *sysmem = get_system_memory(); - ram_addr_t block_len, ram_size[GUEST_RAM_BANKS]; - - if (machine->ram_size <= GUEST_RAM0_SIZE) { - ram_size[0] = machine->ram_size; - ram_size[1] = 0; - block_len = GUEST_RAM0_BASE + ram_size[0]; - } else { - ram_size[0] = GUEST_RAM0_SIZE; - ram_size[1] = machine->ram_size - GUEST_RAM0_SIZE; - block_len = GUEST_RAM1_BASE + ram_size[1]; - } - - memory_region_init_ram(&ram_memory, NULL, "xen.ram", block_len, - &error_fatal); - - memory_region_init_alias(&ram_lo, NULL, "xen.ram.lo", &ram_memory, - GUEST_RAM0_BASE, ram_size[0]); - memory_region_add_subregion(sysmem, GUEST_RAM0_BASE, &ram_lo); - DPRINTF("Initialized region xen.ram.lo: base 0x%llx size 0x%lx\n", - GUEST_RAM0_BASE, ram_size[0]); - - if (ram_size[1] > 0) { - memory_region_init_alias(&ram_hi, NULL, "xen.ram.hi", &ram_memory, - GUEST_RAM1_BASE, ram_size[1]); - memory_region_add_subregion(sysmem, GUEST_RAM1_BASE, &ram_hi); - DPRINTF("Initialized region xen.ram.hi: base 0x%llx size 0x%lx\n", - GUEST_RAM1_BASE, ram_size[1]); - } -} - -void arch_handle_ioreq(XenIOState *state, ioreq_t *req) -{ - hw_error("Invalid ioreq type 0x%x\n", req->type); - - return; -} - -void arch_xen_set_memory(XenIOState *state, MemoryRegionSection *section, - bool add) -{ -} - -void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) -{ -} - -void qmp_xen_set_global_dirty_log(bool enable, Error **errp) -{ -} - -#ifdef CONFIG_TPM -static void xen_enable_tpm(XenArmState *xam) -{ - Error *errp = NULL; - DeviceState *dev; - SysBusDevice *busdev; - - TPMBackend *be = qemu_find_tpm_be("tpm0"); - if (be == NULL) { - DPRINTF("Couldn't fine the backend for tpm0\n"); - return; - } - dev = qdev_new(TYPE_TPM_TIS_SYSBUS); - object_property_set_link(OBJECT(dev), "tpmdev", OBJECT(be), &errp); - object_property_set_str(OBJECT(dev), "tpmdev", be->id, &errp); - busdev = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(busdev, &error_fatal); - sysbus_mmio_map(busdev, 0, xam->cfg.tpm_base_addr); - - DPRINTF("Connected tpmdev at address 0x%lx\n", xam->cfg.tpm_base_addr); -} -#endif - -static void xen_arm_init(MachineState *machine) -{ - XenArmState *xam = XEN_ARM(machine); - - xam->state = g_new0(XenIOState, 1); - - if (machine->ram_size == 0) { - DPRINTF("ram_size not specified. QEMU machine started without IOREQ" - "(no emulated devices including Virtio)\n"); - return; - } - - xen_init_ram(machine); - - xen_register_ioreq(xam->state, machine->smp.cpus, &xen_memory_listener); - - xen_create_virtio_mmio_devices(xam); - -#ifdef CONFIG_TPM - if (xam->cfg.tpm_base_addr) { - xen_enable_tpm(xam); - } else { - DPRINTF("tpm-base-addr is not provided. TPM will not be enabled\n"); - } -#endif -} - -#ifdef CONFIG_TPM -static void xen_arm_get_tpm_base_addr(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - XenArmState *xam = XEN_ARM(obj); - uint64_t value = xam->cfg.tpm_base_addr; - - visit_type_uint64(v, name, &value, errp); -} - -static void xen_arm_set_tpm_base_addr(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - XenArmState *xam = XEN_ARM(obj); - uint64_t value; - - if (!visit_type_uint64(v, name, &value, errp)) { - return; - } - - xam->cfg.tpm_base_addr = value; -} -#endif - -static void xen_arm_machine_class_init(ObjectClass *oc, void *data) -{ - - MachineClass *mc = MACHINE_CLASS(oc); - mc->desc = "Xen Para-virtualized PC"; - mc->init = xen_arm_init; - mc->max_cpus = 1; - mc->default_machine_opts = "accel=xen"; - /* Set explicitly here to make sure that real ram_size is passed */ - mc->default_ram_size = 0; - -#ifdef CONFIG_TPM - object_class_property_add(oc, "tpm-base-addr", "uint64_t", - xen_arm_get_tpm_base_addr, - xen_arm_set_tpm_base_addr, - NULL, NULL); - object_class_property_set_description(oc, "tpm-base-addr", - "Set Base address for TPM device."); - - machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); -#endif -} - -static const TypeInfo xen_arm_machine_type = { - .name = TYPE_XEN_ARM, - .parent = TYPE_MACHINE, - .class_init = xen_arm_machine_class_init, - .instance_size = sizeof(XenArmState), -}; - -static void xen_arm_machine_register_types(void) -{ - type_register_static(&xen_arm_machine_type); -} - -type_init(xen_arm_machine_register_types) diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 8dc2ea83a93..0372cd0ac46 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -18,11 +18,10 @@ #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" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/block/flash.h" #include "hw/loader.h" @@ -35,8 +34,12 @@ #include "hw/net/cadence_gem.h" #include "hw/cpu/a9mpcore.h" #include "hw/qdev-clock.h" -#include "sysemu/reset.h" +#include "hw/misc/unimp.h" +#include "system/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) @@ -51,11 +54,11 @@ OBJECT_DECLARE_SIMPLE_TYPE(ZynqMachineState, ZYNQ_MACHINE) #define FLASH_SIZE (64 * 1024 * 1024) #define FLASH_SECTOR_SIZE (128 * 1024) -#define IRQ_OFFSET 32 /* pic interrupts start from index 32 */ - #define MPCORE_PERIPHBASE 0xF8F00000 #define ZYNQ_BOARD_MIDR 0x413FC090 +#define GIC_EXT_IRQS 64 /* Zynq 7000 SoC */ + static const int dma_irqs[8] = { 46, 47, 48, 49, 72, 73, 74, 75 }; @@ -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); @@ -175,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]; + qemu_irq pic[GIC_EXT_IRQS]; int n; + unsigned int smp_cpus = machine->smp.cpus; /* max 2GB ram */ if (machine->ram_size > 2 * GiB) { @@ -192,21 +217,18 @@ 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); - 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); + zynq_machine->cpu[n] = ARM_CPU(cpuobj); + } /* DDR remapped to address zero. */ memory_region_add_subregion(address_space_mem, 0, machine->ram); @@ -235,27 +257,37 @@ 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); + qdev_prop_set_uint32(dev, "num-irq", GIC_EXT_IRQS + GIC_INTERNAL); 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++) { + for (n = 0; n < GIC_EXT_IRQS; n++) { pic[n] = qdev_get_gpio_in(dev, n); } - n = zynq_init_spi_flashes(0xE0006000, pic[58 - IRQ_OFFSET], false, 0); - n = zynq_init_spi_flashes(0xE0007000, pic[81 - IRQ_OFFSET], false, n); - n = zynq_init_spi_flashes(0xE000D000, pic[51 - IRQ_OFFSET], true, n); + n = zynq_init_spi_flashes(0xE0006000, pic[58 - GIC_INTERNAL], false, 0); + n = zynq_init_spi_flashes(0xE0007000, pic[81 - GIC_INTERNAL], false, n); + n = zynq_init_spi_flashes(0xE000D000, pic[51 - GIC_INTERNAL], true, n); - sysbus_create_simple(TYPE_CHIPIDEA, 0xE0002000, pic[53 - IRQ_OFFSET]); - sysbus_create_simple(TYPE_CHIPIDEA, 0xE0003000, pic[76 - IRQ_OFFSET]); + sysbus_create_simple(TYPE_CHIPIDEA, 0xE0002000, pic[53 - GIC_INTERNAL]); + sysbus_create_simple(TYPE_CHIPIDEA, 0xE0003000, pic[76 - GIC_INTERNAL]); dev = qdev_new(TYPE_CADENCE_UART); busdev = SYS_BUS_DEVICE(dev); @@ -264,7 +296,7 @@ static void zynq_init(MachineState *machine) qdev_get_clock_out(slcr, "uart0_ref_clk")); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, 0xE0000000); - sysbus_connect_irq(busdev, 0, pic[59 - IRQ_OFFSET]); + sysbus_connect_irq(busdev, 0, pic[59 - GIC_INTERNAL]); dev = qdev_new(TYPE_CADENCE_UART); busdev = SYS_BUS_DEVICE(dev); qdev_prop_set_chr(dev, "chardev", serial_hd(1)); @@ -272,15 +304,15 @@ static void zynq_init(MachineState *machine) qdev_get_clock_out(slcr, "uart1_ref_clk")); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, 0xE0001000); - sysbus_connect_irq(busdev, 0, pic[82 - IRQ_OFFSET]); + sysbus_connect_irq(busdev, 0, pic[82 - GIC_INTERNAL]); sysbus_create_varargs("cadence_ttc", 0xF8001000, - pic[42-IRQ_OFFSET], pic[43-IRQ_OFFSET], pic[44-IRQ_OFFSET], NULL); + pic[42-GIC_INTERNAL], pic[43-GIC_INTERNAL], pic[44-GIC_INTERNAL], NULL); sysbus_create_varargs("cadence_ttc", 0xF8002000, - pic[69-IRQ_OFFSET], pic[70-IRQ_OFFSET], pic[71-IRQ_OFFSET], NULL); + pic[69-GIC_INTERNAL], pic[70-GIC_INTERNAL], pic[71-GIC_INTERNAL], 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 - GIC_INTERNAL]); + gem_init(0xE000C000, pic[77 - GIC_INTERNAL]); for (n = 0; n < 2; n++) { int hci_irq = n ? 79 : 56; @@ -299,7 +331,7 @@ static void zynq_init(MachineState *machine) qdev_prop_set_uint64(dev, "capareg", ZYNQ_SDHCI_CAPABILITIES); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, hci_addr); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[hci_irq - IRQ_OFFSET]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[hci_irq - GIC_INTERNAL]); di = drive_get(IF_SD, 0, n); blk = di ? blk_by_legacy_dinfo(di) : NULL; @@ -312,7 +344,7 @@ static void zynq_init(MachineState *machine) dev = qdev_new(TYPE_ZYNQ_XADC); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xF8007100); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[39-IRQ_OFFSET]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[39-GIC_INTERNAL]); dev = qdev_new("pl330"); object_property_set_link(OBJECT(dev), "memory", @@ -332,36 +364,115 @@ static void zynq_init(MachineState *machine) busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, 0xF8003000); - sysbus_connect_irq(busdev, 0, pic[45-IRQ_OFFSET]); /* abort irq line */ + sysbus_connect_irq(busdev, 0, pic[45-GIC_INTERNAL]); /* abort irq line */ for (n = 0; n < ARRAY_SIZE(dma_irqs); ++n) { /* event irqs */ - sysbus_connect_irq(busdev, n + 1, pic[dma_irqs[n] - IRQ_OFFSET]); + sysbus_connect_irq(busdev, n + 1, pic[dma_irqs[n] - GIC_INTERNAL]); } dev = qdev_new("xlnx.ps7-dev-cfg"); busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); - sysbus_connect_irq(busdev, 0, pic[40 - IRQ_OFFSET]); + sysbus_connect_irq(busdev, 0, pic[40 - GIC_INTERNAL]); 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 void zynq_machine_class_init(ObjectClass *oc, const void *data) { + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a9"), + NULL + }; MachineClass *mc = MACHINE_CLASS(oc); - mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9"; + ObjectProperty *prop; + mc->desc = "Xilinx Zynq 7000 Platform Baseboard for Cortex-A9"; mc->init = zynq_init; - mc->max_cpus = 1; - mc->no_sdcard = 1; + mc->max_cpus = ZYNQ_MAX_CPUS; 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 88c561ff632..adadbb72902 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -12,13 +12,15 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "sysemu/device_tree.h" +#include "system/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") @@ -48,6 +50,7 @@ struct VersalVirt { struct { bool secure; } cfg; + char *ospi_model; }; static void fdt_create(VersalVirt *s) @@ -106,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"); } @@ -636,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); @@ -730,12 +750,25 @@ 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); @@ -768,7 +801,14 @@ static void versal_virt_machine_instance_init(Object *obj) 0); } -static void versal_virt_machine_class_init(ObjectClass *oc, void *data) +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, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -778,7 +818,12 @@ static void versal_virt_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = XLNX_VERSAL_NR_ACPUS + XLNX_VERSAL_NR_RCPUS; mc->default_cpus = XLNX_VERSAL_NR_ACPUS + XLNX_VERSAL_NR_RCPUS; mc->no_cdrom = true; + mc->auto_create_sdcard = 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 = { @@ -787,6 +832,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 fa556d8764b..a42b9e7140b 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -12,16 +12,17 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" +#include "qobject/qlist.h" #include "qemu/module.h" #include "hw/sysbus.h" #include "net/net.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" +#include "system/system.h" #include "hw/arm/boot.h" -#include "kvm_arm.h" #include "hw/misc/unimp.h" #include "hw/arm/xlnx-versal.h" #include "qemu/log.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") @@ -69,6 +70,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; @@ -79,8 +81,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); @@ -249,21 +254,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); @@ -271,7 +280,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); } } @@ -373,6 +383,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); @@ -909,6 +934,7 @@ static void versal_realize(DeviceState *dev, Error **errp) 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); @@ -940,17 +966,16 @@ static void versal_init(Object *obj) "mr-rpu-ps-alias", &s->mr_ps, 0, UINT64_MAX); } -static Property versal_properties[] = { +static const 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() }; -static void versal_class_init(ObjectClass *klass, void *data) +static void versal_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index 21483f75fd9..14b6641a713 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 "system/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, @@ -261,7 +267,7 @@ static void xlnx_zcu102_machine_instance_init(Object *obj) 0); } -static void xlnx_zcu102_machine_class_init(ObjectClass *oc, void *data) +static void xlnx_zcu102_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -274,7 +280,9 @@ static void xlnx_zcu102_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = XLNX_ZYNQMP_NUM_APU_CPUS + XLNX_ZYNQMP_NUM_RPU_CPUS; mc->default_cpus = XLNX_ZYNQMP_NUM_APU_CPUS; mc->default_ram_id = "ddr-ram"; + mc->auto_create_sdcard = true; + 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 5905a330151..ec96a46eec3 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -22,9 +22,9 @@ #include "hw/intc/arm_gic_common.h" #include "hw/misc/unimp.h" #include "hw/boards.h" -#include "sysemu/kvm.h" -#include "sysemu/sysemu.h" -#include "kvm_arm.h" +#include "system/system.h" +#include "target/arm/cpu-qom.h" +#include "target/arm/gtimer.h" #define GIC_NUM_SPI_INTR 160 @@ -392,6 +392,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 +618,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++) { @@ -684,16 +687,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) * - SDIO Specification Version 3.0 * - eMMC Specification Version 4.51 */ - if (!object_property_set_uint(sdhci, "sd-spec-version", 3, errp)) { - return; - } - if (!object_property_set_uint(sdhci, "capareg", SDHCI_CAPABILITIES, - errp)) { - return; - } - if (!object_property_set_uint(sdhci, "uhs", UHS_I, errp)) { - return; - } + object_property_set_uint(sdhci, "sd-spec-version", 3, &error_abort); + object_property_set_uint(sdhci, "capareg", SDHCI_CAPABILITIES, + &error_abort); + object_property_set_uint(sdhci, "uhs", UHS_I, &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(sdhci), errp)) { return; } @@ -758,14 +755,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) xlnx_zynqmp_create_unimp_mmio(s); for (i = 0; i < XLNX_ZYNQMP_NUM_GDMA_CH; i++) { - if (!object_property_set_uint(OBJECT(&s->gdma[i]), "bus-width", 128, - errp)) { - return; - } - if (!object_property_set_link(OBJECT(&s->gdma[i]), "dma", - OBJECT(system_memory), errp)) { - return; - } + object_property_set_uint(OBJECT(&s->gdma[i]), "bus-width", 128, + &error_abort); + object_property_set_link(OBJECT(&s->gdma[i]), "dma", + OBJECT(system_memory), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->gdma[i]), errp)) { return; } @@ -806,10 +799,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->qspi_dma), 0, qdev_get_gpio_in(DEVICE(&s->qspi_irq_orgate), 0)); - if (!object_property_set_link(OBJECT(&s->qspi), "stream-connected-dma", - OBJECT(&s->qspi_dma), errp)) { - return; - } + object_property_set_link(OBJECT(&s->qspi), "stream-connected-dma", + OBJECT(&s->qspi_dma), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->qspi), errp)) { return; } @@ -828,10 +819,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } for (i = 0; i < XLNX_ZYNQMP_NUM_USB; i++) { - if (!object_property_set_link(OBJECT(&s->usb[i].sysbus_xhci), "dma", - OBJECT(system_memory), errp)) { - return; - } + object_property_set_link(OBJECT(&s->usb[i].sysbus_xhci), "dma", + OBJECT(system_memory), &error_abort); qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "intrs", 4); qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "slots", 2); @@ -852,7 +841,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } } -static Property xlnx_zynqmp_props[] = { +static const Property xlnx_zynqmp_props[] = { DEFINE_PROP_STRING("boot-cpu", XlnxZynqMPState, boot_cpu), DEFINE_PROP_BOOL("secure", XlnxZynqMPState, secure, false), DEFINE_PROP_BOOL("virtualization", XlnxZynqMPState, virt, false), @@ -862,10 +851,9 @@ static Property xlnx_zynqmp_props[] = { CanBusState *), DEFINE_PROP_LINK("canbus1", XlnxZynqMPState, canbus[1], TYPE_CAN_BUS, CanBusState *), - DEFINE_PROP_END_OF_LIST() }; -static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data) +static void xlnx_zynqmp_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/z2.c b/hw/arm/z2.c deleted file mode 100644 index dc25304290a..00000000000 --- a/hw/arm/z2.c +++ /dev/null @@ -1,353 +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 "qemu/units.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, -}; - -#define FLASH_SECTOR_SIZE (64 * KiB) - -static void z2_init(MachineState *machine) -{ - PXA2xxState *mpu; - DriveInfo *dinfo; - void *z2_lcd; - I2CBus *bus; - DeviceState *wm; - - /* Setup CPU & memory */ - mpu = pxa270_init(z2_binfo.ram_size, machine->cpu_type); - - dinfo = drive_get(IF_PFLASH, 0, 0); - pflash_cfi01_register(Z2_FLASH_BASE, "z2.flash0", Z2_FLASH_SIZE, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - FLASH_SECTOR_SIZE, 4, 0, 0, 0, 0, 0); - - /* setup keypad */ - pxa27x_register_keypad(mpu->kp, map, 0x100); - - /* 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 e76c69ca7e7..daf060e1be0 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 c2a5ce062a1..eb7a847080d 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -24,7 +24,7 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qom/object.h" #include "ac97.h" @@ -886,7 +886,7 @@ static void nabm_writel(void *opaque, uint32_t addr, uint32_t val) static int write_audio(AC97LinkState *s, AC97BusMasterRegs *r, int max, int *stop) { - uint8_t tmpbuf[4096]; + QEMU_UNINITIALIZED uint8_t tmpbuf[4096]; uint32_t addr = r->bd.addr; uint32_t temp = r->picb << 1; uint32_t written = 0; @@ -959,7 +959,7 @@ static void write_bup(AC97LinkState *s, int elapsed) static int read_audio(AC97LinkState *s, AC97BusMasterRegs *r, int max, int *stop) { - uint8_t tmpbuf[4096]; + QEMU_UNINITIALIZED uint8_t tmpbuf[4096]; uint32_t addr = r->bd.addr; uint32_t temp = r->picb << 1; uint32_t nread = 0; @@ -1094,7 +1094,7 @@ static const VMStateDescription vmstate_ac97_bm_regs = { .name = "ac97_bm_regs", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(bdbar, AC97BusMasterRegs), VMSTATE_UINT8(civ, AC97BusMasterRegs), VMSTATE_UINT8(lvi, AC97BusMasterRegs), @@ -1142,7 +1142,7 @@ static const VMStateDescription vmstate_ac97 = { .version_id = 3, .minimum_version_id = 2, .post_load = ac97_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, AC97LinkState), VMSTATE_UINT32(glob_cnt, AC97LinkState), VMSTATE_UINT32(glob_sta, AC97LinkState), @@ -1273,6 +1273,10 @@ static void ac97_realize(PCIDevice *dev, Error **errp) AC97LinkState *s = AC97(dev); uint8_t *c = s->dev.config; + if (!AUD_register_card ("ac97", &s->card, errp)) { + return; + } + /* TODO: no need to override */ c[PCI_COMMAND] = 0x00; /* pcicmd pci command rw, ro */ c[PCI_COMMAND + 1] = 0x00; @@ -1306,7 +1310,7 @@ static void ac97_realize(PCIDevice *dev, Error **errp) "ac97-nabm", 256); pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_nam); pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_nabm); - AUD_register_card("ac97", &s->card); + ac97_on_reset(DEVICE(s)); } @@ -1320,12 +1324,11 @@ static void ac97_exit(PCIDevice *dev) AUD_remove_card(&s->card); } -static Property ac97_properties[] = { +static const Property ac97_properties[] = { DEFINE_AUDIO_PROPERTIES(AC97LinkState, card), - DEFINE_PROP_END_OF_LIST(), }; -static void ac97_class_init(ObjectClass *klass, void *data) +static void ac97_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1340,7 +1343,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_reset; + device_class_set_legacy_reset(dc, ac97_on_reset); } static const TypeInfo ac97_info = { @@ -1348,7 +1351,7 @@ static const TypeInfo ac97_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(AC97LinkState), .class_init = ac97_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c index 5f979b1487d..1f29a7e319d 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, @@ -295,14 +297,13 @@ static void adlib_realizefn (DeviceState *dev, Error **errp) portio_list_add (&s->port_list, isa_address_space_io(&s->parent_obj), 0); } -static Property adlib_properties[] = { +static const Property adlib_properties[] = { DEFINE_AUDIO_PROPERTIES(AdlibState, card), DEFINE_PROP_UINT32 ("iobase", AdlibState, port, 0x220), DEFINE_PROP_UINT32 ("freq", AdlibState, freq, 44100), - DEFINE_PROP_END_OF_LIST (), }; -static void adlib_class_initfn (ObjectClass *klass, void *data) +static void adlib_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS (klass); diff --git a/hw/audio/asc.c b/hw/audio/asc.c new file mode 100644 index 00000000000..edd42d6d91c --- /dev/null +++ b/hw/audio/asc.c @@ -0,0 +1,732 @@ +/* + * 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 "qapi/error.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; + } +} + +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); + if (!s->voice) { + AUD_remove_card(&s->card); + error_setg(errp, "Initializing audio stream failed"); + return; + } + + 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_malloc(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 const Property asc_properties[] = { + DEFINE_AUDIO_PROPERTIES(ASCState, card), + DEFINE_PROP_UINT8("asctype", ASCState, type, ASC_TYPE_ASC), +}; + +static void asc_class_init(ObjectClass *oc, const 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 aefc3edea18..97cceb44d86 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() @@ -160,11 +160,11 @@ static void cs4231_init(Object *obj) sysbus_init_irq(dev, &s->irq); } -static void cs4231_class_init(ObjectClass *klass, void *data) +static void cs4231_class_init(ObjectClass *klass, const 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 5c6d6437320..6dfff202ff9 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -528,7 +528,7 @@ static int cs_write_audio (CSState *s, int nchan, int dma_pos, int dma_len, int len) { int temp, net; - uint8_t tmpbuf[4096]; + QEMU_UNINITIALIZED uint8_t tmpbuf[4096]; IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); temp = len; @@ -547,7 +547,7 @@ static int cs_write_audio (CSState *s, int nchan, int dma_pos, copied = k->read_memory(s->isa_dma, nchan, tmpbuf, dma_pos, to_copy); if (s->tab) { int i; - int16_t linbuf[4096]; + QEMU_UNINITIALIZED int16_t linbuf[4096]; for (i = 0; i < copied; ++i) linbuf[i] = s->tab[tmpbuf[i]]; @@ -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), @@ -678,29 +678,34 @@ static void cs4231a_realizefn (DeviceState *dev, Error **errp) return; } + if (!AUD_register_card ("cs4231a", &s->card, errp)) { + return; + } + + if (s->irq >= ISA_NUM_IRQS) { + error_setg(errp, "Invalid IRQ %d (max %d)", s->irq, ISA_NUM_IRQS - 1); + 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[] = { +static const Property cs4231a_properties[] = { DEFINE_AUDIO_PROPERTIES(CSState, card), DEFINE_PROP_UINT32 ("iobase", CSState, port, 0x534), DEFINE_PROP_UINT32 ("irq", CSState, irq, 9), DEFINE_PROP_UINT32 ("dma", CSState, dma, 3), - DEFINE_PROP_END_OF_LIST (), }; -static void cs4231a_class_initfn (ObjectClass *klass, void *data) +static void cs4231a_class_initfn(ObjectClass *klass, const 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 4f738a0ad88..a6a32a6348b 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_device.h" #include "migration/vmstate.h" +#include "qemu/cutils.h" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qom/object.h" +#include "trace.h" /* Missing stuff: SCTRL_P[12](END|ST)INC @@ -164,97 +165,85 @@ 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]; - - 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); + if (DEBUG_ES1370) { + char buf[1024]; + + 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]; - - 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'; - } + if (DEBUG_ES1370) { + static const char *fmt_names[] = {"8M", "8S", "16M", "16S"}; + char buf[1024]; + + buf[0] = '\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 - -#ifndef SILENT_ES1370 -#define lwarn(...) AUD_log ("es1370: warning", __VA_ARGS__) -#else -#define lwarn(...) -#endif +#define lwarn(...) \ +do { \ + if (VERBOSE_ES1370) { \ + AUD_log("es1370: warning", __VA_ARGS__); \ + } \ +} while (0) #define TYPE_ES1370 "ES1370" OBJECT_DECLARE_SIMPLE_TYPE(ES1370State, ES1370) @@ -320,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); @@ -344,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; } @@ -411,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; @@ -435,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, @@ -456,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); } } @@ -470,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; } @@ -502,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: @@ -515,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: @@ -534,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; } } @@ -573,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: @@ -593,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: @@ -613,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]; + QEMU_UNINITIALIZED uint8_t tmpbuf[4096]; + size_t to_transfer; uint32_t addr = d->frame_addr; int sc = d->scount & 0xffff; int csc = d->scount >> 16; @@ -648,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]; @@ -732,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) { @@ -784,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), @@ -806,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; @@ -828,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), @@ -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); } @@ -885,12 +868,11 @@ static void es1370_exit(PCIDevice *dev) AUD_remove_card(&s->card); } -static Property es1370_properties[] = { +static const Property es1370_properties[] = { DEFINE_AUDIO_PROPERTIES(ES1370State, card), - DEFINE_PROP_END_OF_LIST(), }; -static void es1370_class_init (ObjectClass *klass, void *data) +static void es1370_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS (klass); PCIDeviceClass *k = PCI_DEVICE_CLASS (klass); @@ -905,7 +887,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); } @@ -914,7 +896,7 @@ static const TypeInfo es1370_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof (ES1370State), .class_init = es1370_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/audio/gus.c b/hw/audio/gus.c index 787345ce543..c36df0240fe 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -183,7 +183,7 @@ static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) { GUSState *s = opaque; IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); - char tmpbuf[4096]; + QEMU_UNINITIALIZED char tmpbuf[4096]; int pos = dma_pos, mode, left = dma_len - dma_pos; ldebug ("read DMA %#x %d\n", dma_pos, dma_len); @@ -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), @@ -241,14 +241,16 @@ static void gus_realizefn (DeviceState *dev, Error **errp) IsaDmaClass *k; struct audsettings as; + 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; @@ -288,16 +290,15 @@ static void gus_realizefn (DeviceState *dev, Error **errp) AUD_set_active_out (s->voice, 1); } -static Property gus_properties[] = { +static const Property gus_properties[] = { DEFINE_AUDIO_PROPERTIES(GUSState, card), DEFINE_PROP_UINT32 ("freq", GUSState, freq, 44100), DEFINE_PROP_UINT32 ("iobase", GUSState, port, 0x240), DEFINE_PROP_UINT32 ("irq", GUSState, emu.gusirq, 7), DEFINE_PROP_UINT32 ("dma", GUSState, emu.gusdma, 3), - DEFINE_PROP_END_OF_LIST (), }; -static void gus_class_initfn (ObjectClass *klass, void *data) +static void gus_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS (klass); diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index c51d8ba6177..66edad280f2 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" @@ -189,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) @@ -222,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) { @@ -286,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; + + if (uptime <= 0) { + /* wanted_wpos <= 0 */ + goto out_timer; + } - int64_t wanted_wpos = hda_bytes_per_second(st) * (now - buft_start) - / NANOSECONDS_PER_SECOND; + wanted_wpos = muldiv64(uptime, hda_bytes_per_second(st), + NANOSECONDS_PER_SECOND); wanted_wpos &= -4; /* IMPORTANT! clip to frames */ if (wanted_wpos <= wpos) { @@ -474,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; } @@ -484,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; } @@ -675,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; @@ -683,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); @@ -708,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); @@ -718,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) @@ -733,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 { @@ -795,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), @@ -808,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), @@ -820,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 } @@ -830,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), @@ -840,48 +857,50 @@ static const VMStateDescription vmstate_hda_audio = { } }; -static Property hda_audio_properties[] = { +static const Property hda_audio_properties[] = { DEFINE_AUDIO_PROPERTIES(HDAAudioState, card), DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true), DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer, true), - 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) +static void hda_audio_base_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); @@ -890,7 +909,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); } @@ -903,7 +922,7 @@ static const TypeInfo hda_audio_info = { .abstract = true, }; -static void hda_audio_output_class_init(ObjectClass *klass, void *data) +static void hda_audio_output_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); @@ -918,7 +937,7 @@ static const TypeInfo hda_audio_output_info = { .class_init = hda_audio_output_class_init, }; -static void hda_audio_duplex_class_init(ObjectClass *klass, void *data) +static void hda_audio_duplex_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); @@ -933,7 +952,7 @@ static const TypeInfo hda_audio_duplex_info = { .class_init = hda_audio_duplex_class_init, }; -static void hda_audio_micro_class_init(ObjectClass *klass, void *data) +static void hda_audio_micro_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index b9ed231fe84..b256c8ccea1 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -30,16 +30,15 @@ #include "intel-hda.h" #include "migration/vmstate.h" #include "intel-hda-defs.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qapi/error.h" #include "qom/object.h" /* --------------------------------------------------------------------- */ /* hda bus */ -static Property hda_props[] = { +static const Property hda_props[] = { DEFINE_PROP_UINT32("cad", HDACodecDevice, cad, -1), - DEFINE_PROP_END_OF_LIST() }; static const TypeInfo hda_codec_bus_info = { @@ -71,9 +70,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 +1157,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 +1173,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 */ @@ -1217,14 +1214,13 @@ static const VMStateDescription vmstate_intel_hda = { } }; -static Property intel_hda_properties[] = { +static const Property intel_hda_properties[] = { DEFINE_PROP_UINT32("debug", IntelHDAState, debug, 0), DEFINE_PROP_ON_OFF_AUTO("msi", IntelHDAState, msi, ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("old_msi_addr", IntelHDAState, old_msi_addr, false), - DEFINE_PROP_END_OF_LIST(), }; -static void intel_hda_class_init(ObjectClass *klass, void *data) +static void intel_hda_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1233,12 +1229,12 @@ 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); } -static void intel_hda_class_init_ich6(ObjectClass *klass, void *data) +static void intel_hda_class_init_ich6(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1249,7 +1245,7 @@ static void intel_hda_class_init_ich6(ObjectClass *klass, void *data) dc->desc = "Intel HD Audio Controller (ich6)"; } -static void intel_hda_class_init_ich9(ObjectClass *klass, void *data) +static void intel_hda_class_init_ich9(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1266,7 +1262,7 @@ static const TypeInfo intel_hda_info = { .instance_size = sizeof(IntelHDAState), .class_init = intel_hda_class_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -1284,7 +1280,7 @@ static const TypeInfo intel_hda_info_ich9 = { .class_init = intel_hda_class_init_ich9, }; -static void hda_codec_device_class_init(ObjectClass *klass, void *data) +static void hda_codec_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->realize = hda_codec_dev_realize; diff --git a/hw/audio/intel-hda.h b/hw/audio/intel-hda.h index f78c1833e34..8d710eee5d6 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 32b1481b561..a4a77c8dc6a 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 aba9bb5b077..61c3ab12dd3 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 e6c09bdb8e3..c5c79d083a4 100644 --- a/hw/audio/marvell_88w8618.c +++ b/hw/audio/marvell_88w8618.c @@ -66,7 +66,7 @@ static void mv88w8618_audio_callback(void *opaque, int free_out, int free_in) { mv88w8618_audio_state *s = opaque; int16_t *codec_buffer; - int8_t buf[4096]; + QEMU_UNINITIALIZED int8_t buf[4096]; int8_t *mem_buffer; int pos, block_size; @@ -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), @@ -287,12 +287,12 @@ static const VMStateDescription mv88w8618_audio_vmsd = { } }; -static void mv88w8618_audio_class_init(ObjectClass *klass, void *data) +static void mv88w8618_audio_class_init(ObjectClass *klass, const 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 d0fda1009ed..29909744490 100644 --- a/hw/audio/meson.build +++ b/hw/audio/meson.build @@ -1,6 +1,7 @@ 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')) @@ -12,3 +13,5 @@ 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 daf92a4ce11..a419161b5b1 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,21 +208,20 @@ 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() } }; -static Property pcspk_properties[] = { +static const Property pcspk_properties[] = { DEFINE_AUDIO_PROPERTIES(PCSpkState, card), DEFINE_PROP_UINT32("iobase", PCSpkState, iobase, 0x61), DEFINE_PROP_BOOL("migrate", PCSpkState, migrate, true), - DEFINE_PROP_END_OF_LIST(), }; -static void pcspk_class_initfn(ObjectClass *klass, void *data) +static void pcspk_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/audio/pl041.c b/hw/audio/pl041.c index 03acd4fe344..5d9d6c1178b 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), @@ -625,21 +625,20 @@ static const VMStateDescription vmstate_pl041 = { } }; -static Property pl041_device_properties[] = { +static const Property pl041_device_properties[] = { DEFINE_AUDIO_PROPERTIES(PL041State, codec.card), /* Non-compact FIFO depth property */ DEFINE_PROP_UINT32("nc_fifo_depth", PL041State, fifo_depth, DEFAULT_FIFO_DEPTH), - DEFINE_PROP_END_OF_LIST(), }; -static void pl041_device_class_init(ObjectClass *klass, void *data) +static void pl041_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 535ccccdc98..bac64118fef 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -1181,7 +1181,7 @@ static int write_audio (SB16State *s, int nchan, int dma_pos, IsaDma *isa_dma = nchan == s->dma ? s->isa_dma : s->isa_hdma; IsaDmaClass *k = ISADMA_GET_CLASS(isa_dma); int temp, net; - uint8_t tmpbuf[4096]; + QEMU_UNINITIALIZED uint8_t tmpbuf[4096]; temp = len; net = 0; @@ -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), @@ -1402,6 +1402,10 @@ static void sb16_realizefn (DeviceState *dev, Error **errp) SB16State *s = SB16 (dev); IsaDmaClass *k; + 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) { @@ -1434,21 +1438,18 @@ 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[] = { +static const Property sb16_properties[] = { DEFINE_AUDIO_PROPERTIES(SB16State, card), DEFINE_PROP_UINT32 ("version", SB16State, ver, 0x0405), /* 4.5 */ DEFINE_PROP_UINT32 ("iobase", SB16State, port, 0x220), DEFINE_PROP_UINT32 ("irq", SB16State, irq, 5), DEFINE_PROP_UINT32 ("dma", SB16State, dma, 1), DEFINE_PROP_UINT32 ("dma16", SB16State, hdma, 5), - DEFINE_PROP_END_OF_LIST (), }; -static void sb16_class_initfn (ObjectClass *klass, void *data) +static void sb16_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS (klass); diff --git a/hw/audio/soundhw.c b/hw/audio/soundhw.c index 94d9463e42d..d18fd9fa051 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 4dec48a4fd5..b8ef5727678 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -6,6 +6,17 @@ 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" @@ -17,3 +28,32 @@ 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 676254b7a40..d5231e1cf22 100644 --- a/hw/audio/via-ac97.c +++ b/hw/audio/via-ac97.c @@ -175,7 +175,7 @@ static void out_cb(void *opaque, int avail) ViaAC97SGDChannel *c = &s->aur; int temp, to_copy, copied; bool stop = false; - uint8_t tmpbuf[4096]; + QEMU_UNINITIALIZED uint8_t tmpbuf[4096]; if (c->stat & STAT_PAUSED) { return; @@ -211,14 +211,14 @@ static void out_cb(void *opaque, int avail) AUD_set_active_out(s->vo, 0); } if (c->type & STAT_EOL) { - pci_set_irq(&s->dev, 1); + 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) { - pci_set_irq(&s->dev, 1); + via_isa_set_irq(&s->dev, 0, 1); } } if (CLEN_IS_STOP(c)) { @@ -305,13 +305,13 @@ static void sgd_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) if (val & STAT_EOL) { s->aur.stat &= ~(STAT_EOL | STAT_PAUSED); if (s->aur.type & STAT_EOL) { - pci_set_irq(&s->dev, 0); + 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) { - pci_set_irq(&s->dev, 0); + via_isa_set_irq(&s->dev, 0, 0); } } break; @@ -426,6 +426,10 @@ static void via_ac97_realize(PCIDevice *pci_dev, Error **errp) 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 @@ -445,8 +449,6 @@ static void via_ac97_realize(PCIDevice *pci_dev, Error **errp) 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); - - AUD_register_card ("via-ac97", &s->card); } static void via_ac97_exit(PCIDevice *dev) @@ -457,12 +459,11 @@ static void via_ac97_exit(PCIDevice *dev) AUD_remove_card(&s->card); } -static Property via_ac97_properties[] = { +static const Property via_ac97_properties[] = { DEFINE_AUDIO_PROPERTIES(ViaAC97State, card), - DEFINE_PROP_END_OF_LIST(), }; -static void via_ac97_class_init(ObjectClass *klass, void *data) +static void via_ac97_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -476,7 +477,7 @@ static void via_ac97_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, via_ac97_properties); set_bit(DEVICE_CATEGORY_SOUND, dc->categories); dc->desc = "VIA AC97"; - dc->reset = via_ac97_reset; + device_class_set_legacy_reset(dc, via_ac97_reset); /* Reason: Part of a south bridge chip */ dc->user_creatable = false; } @@ -486,7 +487,7 @@ static const TypeInfo via_ac97_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(ViaAC97State), .class_init = via_ac97_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -500,7 +501,7 @@ static void via_mc97_realize(PCIDevice *pci_dev, Error **errp) pci_set_long(pci_dev->config + PCI_INTERRUPT_PIN, 0x03); } -static void via_mc97_class_init(ObjectClass *klass, void *data) +static void via_mc97_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -521,7 +522,7 @@ static const TypeInfo via_mc97_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = via_mc97_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const 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 00000000000..9eb00073920 --- /dev/null +++ b/hw/audio/virtio-snd-pci.c @@ -0,0 +1,94 @@ +/* + * 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 const 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), +}; + +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, const 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 00000000000..eca3319e59f --- /dev/null +++ b/hw/audio/virtio-snd.c @@ -0,0 +1,1392 @@ +/* + * 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 "system/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 const 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), +}; + +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, const 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 b5722b37c36..2846b55fe2a 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)); } @@ -703,12 +706,11 @@ void wm8750_set_bclk_in(void *opaque, int new_hz) wm8750_clk_update(s, 1); } -static Property wm8750_properties[] = { +static const Property wm8750_properties[] = { DEFINE_AUDIO_PROPERTIES(WM8750State, card), - DEFINE_PROP_END_OF_LIST(), }; -static void wm8750_class_init(ObjectClass *klass, void *data) +static void wm8750_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); diff --git a/hw/avr/Kconfig b/hw/avr/Kconfig index d31298c3cce..b29937be414 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/arduino.c b/hw/avr/arduino.c index 48ef478346e..e166ca18e11 100644 --- a/hw/avr/arduino.c +++ b/hw/avr/arduino.c @@ -56,7 +56,7 @@ static void arduino_machine_init(MachineState *machine) } } -static void arduino_machine_class_init(ObjectClass *oc, void *data) +static void arduino_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -69,7 +69,7 @@ static void arduino_machine_class_init(ObjectClass *oc, void *data) mc->no_parallel = 1; } -static void arduino_duemilanove_class_init(ObjectClass *oc, void *data) +static void arduino_duemilanove_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc); @@ -84,7 +84,7 @@ static void arduino_duemilanove_class_init(ObjectClass *oc, void *data) amc->xtal_hz = 16 * 1000 * 1000; }; -static void arduino_uno_class_init(ObjectClass *oc, void *data) +static void arduino_uno_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc); @@ -99,7 +99,7 @@ static void arduino_uno_class_init(ObjectClass *oc, void *data) amc->xtal_hz = 16 * 1000 * 1000; }; -static void arduino_mega_class_init(ObjectClass *oc, void *data) +static void arduino_mega_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc); @@ -114,7 +114,7 @@ static void arduino_mega_class_init(ObjectClass *oc, void *data) amc->xtal_hz = 16 * 1000 * 1000; }; -static void arduino_mega2560_class_init(ObjectClass *oc, void *data) +static void arduino_mega2560_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc); diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c index a34803e6427..95b6da5e343 100644 --- a/hw/avr/atmega.c +++ b/hw/avr/atmega.c @@ -12,13 +12,15 @@ #include "qemu/module.h" #include "qemu/units.h" #include "qapi/error.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" -#include "sysemu/sysemu.h" +#include "exec/target_page.h" +#include "system/memory.h" +#include "system/address-spaces.h" +#include "system/system.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "qom/object.h" #include "hw/misc/unimp.h" +#include "migration/vmstate.h" #include "atmega.h" enum AtmegaPeripheral { @@ -224,8 +226,6 @@ static void atmega_realize(DeviceState *dev, Error **errp) char *devname; size_t i; - assert(mc->io_size <= 0x200); - if (!s->xtal_freq_hz) { error_setg(errp, "\"xtal-frequency-hz\" property must be provided."); return; @@ -233,14 +233,44 @@ 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); - /* SRAM */ - memory_region_init_ram(&s->sram, OBJECT(dev), "sram", mc->sram_size, - &error_abort); - memory_region_add_subregion(get_system_memory(), - OFFSET_DATA + mc->io_size, &s->sram); + /* + * SRAM + * + * Softmmu is not able mix i/o and ram on the same page. + * Therefore in all cases, the first page exclusively contains i/o. + * + * If the MCU's i/o region matches the page size, then we can simply + * allocate all ram starting at the second page. Otherwise, we must + * allocate some ram as i/o to complete the first page. + */ + assert(mc->io_size == 0x100 || mc->io_size == 0x200); + if (mc->io_size >= TARGET_PAGE_SIZE) { + memory_region_init_ram(&s->sram, OBJECT(dev), "sram", mc->sram_size, + &error_abort); + memory_region_add_subregion(get_system_memory(), + OFFSET_DATA + mc->io_size, &s->sram); + } else { + int sram_io_size = TARGET_PAGE_SIZE - mc->io_size; + void *sram_io_mem = g_malloc0(sram_io_size); + + memory_region_init_ram_device_ptr(&s->sram_io, OBJECT(dev), "sram-as-io", + sram_io_size, sram_io_mem); + memory_region_add_subregion(get_system_memory(), + OFFSET_DATA + mc->io_size, &s->sram_io); + vmstate_register_ram(&s->sram_io, dev); + + memory_region_init_ram(&s->sram, OBJECT(dev), "sram", + mc->sram_size - sram_io_size, &error_abort); + memory_region_add_subregion(get_system_memory(), + OFFSET_DATA + TARGET_PAGE_SIZE, &s->sram); + } /* Flash */ memory_region_init_rom(&s->flash, OBJECT(dev), @@ -351,13 +381,12 @@ static void atmega_realize(DeviceState *dev, Error **errp) create_unimplemented_device("avr-eeprom", OFFSET_DATA + 0x03f, 3); } -static Property atmega_props[] = { +static const Property atmega_props[] = { DEFINE_PROP_UINT64("xtal-frequency-hz", AtmegaMcuState, xtal_freq_hz, 0), - DEFINE_PROP_END_OF_LIST() }; -static void atmega_class_init(ObjectClass *oc, void *data) +static void atmega_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -367,7 +396,7 @@ static void atmega_class_init(ObjectClass *oc, void *data) dc->user_creatable = false; } -static void atmega168_class_init(ObjectClass *oc, void *data) +static void atmega168_class_init(ObjectClass *oc, const void *data) { AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); @@ -382,7 +411,7 @@ static void atmega168_class_init(ObjectClass *oc, void *data) amc->dev = dev168_328; }; -static void atmega328_class_init(ObjectClass *oc, void *data) +static void atmega328_class_init(ObjectClass *oc, const void *data) { AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); @@ -397,7 +426,7 @@ static void atmega328_class_init(ObjectClass *oc, void *data) amc->dev = dev168_328; }; -static void atmega1280_class_init(ObjectClass *oc, void *data) +static void atmega1280_class_init(ObjectClass *oc, const void *data) { AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); @@ -412,7 +441,7 @@ static void atmega1280_class_init(ObjectClass *oc, void *data) amc->dev = dev1280_2560; }; -static void atmega2560_class_init(ObjectClass *oc, void *data) +static void atmega2560_class_init(ObjectClass *oc, const void *data) { AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); diff --git a/hw/avr/atmega.h b/hw/avr/atmega.h index a99ee15c7e1..9ac46782312 100644 --- a/hw/avr/atmega.h +++ b/hw/avr/atmega.h @@ -41,6 +41,7 @@ struct AtmegaMcuState { MemoryRegion flash; MemoryRegion eeprom; MemoryRegion sram; + MemoryRegion sram_io; DeviceState *io; AVRMaskState pwr[POWER_MAX]; AVRUsartState usart[USART_MAX]; diff --git a/hw/avr/boot.c b/hw/avr/boot.c index 617f3a144c8..e5a29c7218e 100644 --- a/hw/avr/boot.c +++ b/hw/avr/boot.c @@ -71,11 +71,9 @@ bool avr_load_firmware(AVRCPU *cpu, MachineState *ms, return false; } - bytes_loaded = load_elf_ram_sym(filename, - NULL, NULL, NULL, - &entry, NULL, NULL, - &e_flags, 0, EM_AVR, 0, 0, - NULL, true, NULL); + bytes_loaded = load_elf_as(filename, NULL, NULL, NULL, + &entry, NULL, NULL, + &e_flags, ELFDATA2LSB, EM_AVR, 0, 0, NULL); if (bytes_loaded >= 0) { /* If ELF file is provided, determine CPU type reading ELF e_flags. */ const char *elf_cpu = avr_elf_e_flags_to_cpu_type(e_flags); diff --git a/hw/block/Kconfig b/hw/block/Kconfig index 9e8f28f9824..737dbcdb3e9 100644 --- a/hw/block/Kconfig +++ b/hw/block/Kconfig @@ -13,24 +13,12 @@ config FDC_SYSBUS config SSI_M25P80 bool -config NAND - bool - config PFLASH_CFI01 bool 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 9f52ee6e728..2e10611d95a 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -9,9 +9,10 @@ #include "qemu/osdep.h" #include "block/block_int-common.h" -#include "sysemu/blockdev.h" -#include "sysemu/block-backend.h" +#include "system/blockdev.h" +#include "system/block-backend.h" #include "hw/block/block.h" +#include "migration/cpr.h" #include "qapi/error.h" #include "qapi/qapi-types-block.h" @@ -30,7 +31,7 @@ static int blk_pread_nonzeroes(BlockBackend *blk, hwaddr size, void *buf) BlockDriverState *bs = blk_bs(blk); for (;;) { - bytes = MIN(size - offset, BDRV_REQUEST_MAX_SECTORS); + bytes = MIN(size - offset, BDRV_REQUEST_MAX_BYTES); if (bytes <= 0) { return 0; } @@ -54,29 +55,34 @@ static int blk_pread_nonzeroes(BlockBackend *blk, hwaddr size, void *buf) * 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; + + if (cpr_is_incoming()) { + return true; + } 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; } @@ -89,7 +95,11 @@ bool blk_check_size_and_read_all(BlockBackend *blk, void *buf, hwaddr size, assert(size <= BDRV_REQUEST_MAX_BYTES); 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 025b3b061b6..11a5eba2f4c 100644 --- a/hw/block/dataplane/meson.build +++ b/hw/block/dataplane/meson.build @@ -1,2 +1 @@ -system_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.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 38fc3e75071..00000000000 --- 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 240cc598348..00000000000 --- 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 da36fcfd0b5..00000000000 --- a/hw/block/dataplane/virtio-blk.c +++ /dev/null @@ -1,380 +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-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_guarded(s->ctx, notify_guest_bh, s, - &DEVICE(vdev)->mem_reentrancy_guard); - 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(); - - 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; - } - - /* 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)); - } - - /* - * These fields must be visible to the IOThread when it processes the - * virtqueue, otherwise it will think dataplane has not started yet. - * - * Make sure ->dataplane_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->starting = false; - vblk->dataplane_started = true; - smp_wmb(); /* paired with aio_notify_accept() on the read side */ - - /* Get this show started by hooking up our callbacks */ - if (!blk_in_drain(s->conf->conf.blk)) { - 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: - vblk->dataplane_disabled = true; - s->starting = false; - 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); - EventNotifier *host_notifier = virtio_queue_get_host_notifier(vq); - - virtio_queue_aio_detach_host_notifier(vq, s->ctx); - - /* - * 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: 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); - - if (!blk_in_drain(s->conf->conf.blk)) { - aio_wait_bh_oneshot(s->ctx, virtio_blk_data_plane_stop_bh, s); - } - - /* - * 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 ->dataplane_started to false before draining so that host notifiers - * are not detached/attached anymore. - */ - vblk->dataplane_started = false; - - aio_context_acquire(s->ctx); - - /* 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); - - aio_context_release(s->ctx); - - 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); - - s->stopping = false; -} diff --git a/hw/block/dataplane/virtio-blk.h b/hw/block/dataplane/virtio-blk.h deleted file mode 100644 index 5e18bb99aeb..00000000000 --- 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 3b6f2b0aa21..48c2e315f31 100644 --- a/hw/block/dataplane/xen-block.c +++ b/hw/block/dataplane/xen-block.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/defer-call.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/memalign.h" @@ -26,8 +27,8 @@ #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 "system/block-backend.h" +#include "system/iothread.h" #include "xen-block.h" typedef struct XenBlockRequest { @@ -259,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 ? @@ -272,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) { @@ -317,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, @@ -509,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 @@ -537,7 +533,7 @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane) * is below us. */ if (inflight_atstart > IO_PLUG_THRESHOLD) { - blk_io_plug(); + defer_call_begin(); } while (rc != rp) { /* pull request from ring */ @@ -577,12 +573,12 @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane) if (inflight_atstart > IO_PLUG_THRESHOLD && batched >= inflight_atstart) { - blk_io_unplug(); + defer_call_end(); } xen_block_do_aio(request); if (inflight_atstart > IO_PLUG_THRESHOLD) { if (batched >= inflight_atstart) { - blk_io_plug(); + defer_call_begin(); batched = 0; } else { batched++; @@ -590,7 +586,7 @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane) } } if (inflight_atstart > IO_PLUG_THRESHOLD) { - blk_io_unplug(); + defer_call_end(); } return done_something; @@ -600,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) @@ -702,10 +696,8 @@ void xen_block_dataplane_stop(XenBlockDataPlane *dataplane) xen_block_dataplane_detach(dataplane); } - aio_context_acquire(dataplane->ctx); /* 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 @@ -751,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; @@ -835,11 +826,8 @@ 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); if (!blk_in_drain(dataplane->blk)) { xen_block_dataplane_attach(dataplane); diff --git a/hw/block/dataplane/xen-block.h b/hw/block/dataplane/xen-block.h index 7b8e9df09f7..eb70327db38 100644 --- a/hw/block/dataplane/xen-block.h +++ b/hw/block/dataplane/xen-block.h @@ -10,7 +10,7 @@ #include "hw/block/block.h" #include "hw/xen/xen-bus.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" typedef struct XenBlockDataPlane XenBlockDataPlane; diff --git a/hw/block/ecc.c b/hw/block/ecc.c deleted file mode 100644 index 6e0d63842c1..00000000000 --- 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 036392e9fc1..e219623dc7a 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 7ec075e470a..6d1790e0e61 100644 --- a/hw/block/fdc-isa.c +++ b/hw/block/fdc-isa.c @@ -39,9 +39,10 @@ #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" #include "hw/block/block.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" +#include "system/block-backend.h" +#include "system/blockdev.h" +#include "system/system.h" +#include "system/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; }; @@ -91,7 +93,7 @@ static void isabus_fdc_realize(DeviceState *dev, Error **errp) 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"); @@ -145,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)--; } @@ -190,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; @@ -259,13 +277,13 @@ 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() } }; -static Property isa_fdc_properties[] = { +static const Property isa_fdc_properties[] = { DEFINE_PROP_UINT32("iobase", FDCtrlISABus, iobase, 0x3f0), DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6), DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2), @@ -278,10 +296,9 @@ static Property isa_fdc_properties[] = { DEFINE_PROP_SIGNED("fallback", FDCtrlISABus, state.fallback, FLOPPY_DRIVE_TYPE_288, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_END_OF_LIST(), }; -static void isabus_fdc_class_init(ObjectClass *klass, void *data) +static void isabus_fdc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); @@ -289,7 +306,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); @@ -314,7 +331,7 @@ static const TypeInfo isa_fdc_info = { .instance_size = sizeof(FDCtrlISABus), .class_init = isabus_fdc_class_init, .instance_init = isabus_fdc_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, diff --git a/hw/block/fdc-sysbus.c b/hw/block/fdc-sysbus.c index 86ea51d0034..956860ab298 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 "system/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,18 +170,18 @@ 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() } }; -static void sysbus_fdc_common_class_init(ObjectClass *klass, void *data) +static void sysbus_fdc_common_class_init(ObjectClass *klass, const 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); } @@ -194,7 +196,7 @@ static const TypeInfo sysbus_fdc_common_typeinfo = { .class_size = sizeof(FDCtrlSysBusClass), }; -static Property sysbus_fdc_properties[] = { +static const Property sysbus_fdc_properties[] = { DEFINE_PROP_SIGNED("fdtypeA", FDCtrlSysBus, state.qdev_for_drives[0].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), @@ -204,10 +206,9 @@ static Property sysbus_fdc_properties[] = { DEFINE_PROP_SIGNED("fallback", FDCtrlSysBus, state.fallback, FLOPPY_DRIVE_TYPE_144, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_END_OF_LIST(), }; -static void sysbus_fdc_class_init(ObjectClass *klass, void *data) +static void sysbus_fdc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -221,17 +222,16 @@ static const TypeInfo sysbus_fdc_typeinfo = { .class_init = sysbus_fdc_class_init, }; -static Property sun4m_fdc_properties[] = { +static const Property sun4m_fdc_properties[] = { DEFINE_PROP_SIGNED("fdtype", FDCtrlSysBus, state.qdev_for_drives[0].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), DEFINE_PROP_SIGNED("fallback", FDCtrlSysBus, state.fallback, FLOPPY_DRIVE_TYPE_144, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_END_OF_LIST(), }; -static void sun4m_fdc_class_init(ObjectClass *klass, void *data) +static void sun4m_fdc_class_init(ObjectClass *klass, const void *data) { FDCtrlSysBusClass *sbdc = SYSBUS_FDC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/block/fdc.c b/hw/block/fdc.c index d7cc4d3ec19..d0f08c7be54 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -39,9 +39,9 @@ #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" #include "hw/block/block.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" +#include "system/block-backend.h" +#include "system/blockdev.h" +#include "system/system.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/module.h" @@ -454,13 +454,12 @@ struct FloppyDrive { FloppyDriveType type; }; -static Property floppy_drive_properties[] = { +static const Property floppy_drive_properties[] = { DEFINE_PROP_UINT32("unit", FloppyDrive, unit, -1), DEFINE_BLOCK_PROPERTIES(FloppyDrive, conf), DEFINE_PROP_SIGNED("drive-type", FloppyDrive, type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_END_OF_LIST(), }; static void floppy_drive_realize(DeviceState *qdev, Error **errp) @@ -554,7 +553,7 @@ static void floppy_drive_realize(DeviceState *qdev, Error **errp) fd_revalidate(drive); } -static void floppy_drive_class_init(ObjectClass *klass, void *data) +static void floppy_drive_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->realize = floppy_drive_realize; @@ -854,7 +853,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 +863,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 +881,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 +898,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 +976,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 +994,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 +1012,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 +1025,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 +1056,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 dae13ab14d7..f3939e73f42 100644 --- a/hw/block/hd-geometry.c +++ b/hw/block/hd-geometry.c @@ -31,7 +31,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/qapi-types-block.h" #include "qemu/bswap.h" #include "hw/block/block.h" @@ -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 afc3fdf4d60..a5336d92ff9 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -23,7 +23,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "hw/block/block.h" #include "hw/block/flash.h" #include "hw/qdev-properties.h" @@ -61,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). */ @@ -168,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 @@ -189,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) }, @@ -266,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) }, @@ -347,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 { @@ -413,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,10 +528,9 @@ struct Flash { struct M25P80Class { SSIPeripheralClass parent_class; - FlashPartInfo *pi; + const FlashPartInfo *pi; }; -#define TYPE_M25P80 "m25p80-generic" OBJECT_DECLARE_TYPE(Flash, M25P80Class, M25P80) static inline Manufacturer get_man(Flash *s) @@ -542,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. */ } @@ -796,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; } @@ -803,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]; @@ -1252,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) { @@ -1429,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; } @@ -1617,7 +1684,8 @@ static void m25p80_realize(SSIPeripheral *ss, Error **errp) trace_m25p80_binding(s); s->storage = blk_blockalign(s->blk, s->size); - if (!blk_check_size_and_read_all(s->blk, s->storage, s->size, errp)) { + if (!blk_check_size_and_read_all(s->blk, DEVICE(s), + s->storage, s->size, errp)) { return; } } else { @@ -1652,7 +1720,7 @@ static int m25p80_pre_save(void *opaque) return 0; } -static Property m25p80_properties[] = { +static const Property m25p80_properties[] = { /* This is default value for Micron flash */ DEFINE_PROP_BOOL("write-enable", Flash, write_enable, false), DEFINE_PROP_UINT32("nonvolatile-cfg", Flash, nonvolatile_cfg, 0x8FFF), @@ -1661,7 +1729,6 @@ static Property m25p80_properties[] = { DEFINE_PROP_UINT8("spansion-cr3nv", Flash, spansion_cr3nv, 0x2), DEFINE_PROP_UINT8("spansion-cr4nv", Flash, spansion_cr4nv, 0x10), DEFINE_PROP_DRIVE("drive", Flash, blk), - DEFINE_PROP_END_OF_LIST(), }; static int m25p80_pre_load(void *opaque) @@ -1684,7 +1751,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() } @@ -1702,7 +1769,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() } @@ -1720,7 +1787,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() @@ -1743,7 +1810,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), @@ -1759,7 +1826,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), @@ -1781,7 +1848,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, @@ -1790,7 +1857,7 @@ static const VMStateDescription vmstate_m25p80 = { } }; -static void m25p80_class_init(ObjectClass *klass, void *data) +static void m25p80_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); @@ -1802,8 +1869,10 @@ 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); + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); mc->pi = data; + dc->desc = "Serial Flash"; } static const TypeInfo m25p80_info = { @@ -1820,13 +1889,13 @@ 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, - .class_data = (void *)&known_devices[i], + .class_data = &known_devices[i], }; - type_register(&ti); + type_register_static(&ti); } } diff --git a/hw/block/m25p80_sfdp.c b/hw/block/m25p80_sfdp.c index b33811a4f5e..a03a291a09b 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 @@ -331,6 +404,42 @@ static const uint8_t sfdp_w25q01jvq[] = { }; 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) */ diff --git a/hw/block/m25p80_sfdp.h b/hw/block/m25p80_sfdp.h index 011a880f66a..35785686a0e 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,7 @@ 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); diff --git a/hw/block/meson.build b/hw/block/meson.build index 8aa4dc3893c..655704471a5 100644 --- a/hw/block/meson.build +++ b/hw/block/meson.build @@ -3,19 +3,15 @@ system_ss.add(files( 'cdrom.c', 'hd-geometry.c' )) -system_ss.add(when: 'CONFIG_ECC', if_true: files('ecc.c')) system_ss.add(when: 'CONFIG_FDC', if_true: files('fdc.c')) system_ss.add(when: 'CONFIG_FDC_ISA', if_true: files('fdc-isa.c')) system_ss.add(when: 'CONFIG_FDC_SYSBUS', if_true: files('fdc-sysbus.c')) -system_ss.add(when: 'CONFIG_NAND', if_true: files('nand.c')) -system_ss.add(when: 'CONFIG_ONENAND', if_true: files('onenand.c')) system_ss.add(when: 'CONFIG_PFLASH_CFI01', if_true: files('pflash_cfi01.c')) system_ss.add(when: 'CONFIG_PFLASH_CFI02', if_true: files('pflash_cfi02.c')) system_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80.c')) system_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80_sfdp.c')) system_ss.add(when: 'CONFIG_SWIM', if_true: files('swim.c')) system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-block.c')) -system_ss.add(when: 'CONFIG_TC58128', if_true: files('tc58128.c')) specific_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c', '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 deleted file mode 100644 index 9c1b89cfa66..00000000000 --- a/hw/block/nand.c +++ /dev/null @@ -1,815 +0,0 @@ -/* - * Flash NAND memory emulation. Based on "16M x 8 Bit NAND Flash - * Memory" datasheet for the KM29U128AT / K9F2808U0A chips from - * Samsung Electronic. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * Support for additional features based on "MT29F2G16ABCWP 2Gx16" - * datasheet from Micron Technology and "NAND02G-B2C" datasheet - * from ST Microelectronics. - * - * 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. - */ - -#ifndef NAND_IO - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/qdev-properties.h" -#include "hw/qdev-properties-system.h" -#include "hw/block/flash.h" -#include "sysemu/block-backend.h" -#include "migration/vmstate.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#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_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 MAX_PAGE 0x800 -# define MAX_OOB 0x40 - -typedef struct NANDFlashState NANDFlashState; -struct NANDFlashState { - DeviceState parent_obj; - - uint8_t manf_id, chip_id; - uint8_t buswidth; /* in BYTES */ - int size, pages; - int page_shift, oob_shift, erase_shift, addr_shift; - uint8_t *storage; - BlockBackend *blk; - int mem_oob; - - uint8_t cle, ale, ce, wp, gnd; - - uint8_t io[MAX_PAGE + MAX_OOB + 0x400]; - uint8_t *ioaddr; - int iolen; - - uint32_t cmd; - uint64_t addr; - int addrlen; - int status; - int offset; - - void (*blk_write)(NANDFlashState *s); - void (*blk_erase)(NANDFlashState *s); - void (*blk_load)(NANDFlashState *s, uint64_t addr, int offset); - - uint32_t ioaddr_vmstate; -}; - -#define TYPE_NAND "nand" - -OBJECT_DECLARE_SIMPLE_TYPE(NANDFlashState, NAND) - -static void mem_and(uint8_t *dest, const uint8_t *src, size_t n) -{ - /* Like memcpy() but we logical-AND the data into the destination */ - int i; - for (i = 0; i < n; i++) { - dest[i] &= src[i]; - } -} - -# 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 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 -# include "nand.c" -# 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 */ -static const struct { - int size; - int width; - int page_shift; - int erase_shift; - uint32_t options; -} 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 }, - - [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 }, - - [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 }, - - [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) - - /* 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 }, - - /* 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 }, - - /* 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 }, - - /* 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 }, - - /* 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 }, - - /* 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 }, -}; - -static void nand_reset(DeviceState *dev) -{ - NANDFlashState *s = NAND(dev); - s->cmd = NAND_CMD_READ0; - s->addr = 0; - s->addrlen = 0; - s->iolen = 0; - s->offset = 0; - s->status &= NAND_IOSTATUS_UNPROTCT; - s->status |= NAND_IOSTATUS_READY; -} - -static inline void nand_pushio_byte(NANDFlashState *s, uint8_t value) -{ - s->ioaddr[s->iolen++] = value; - for (value = s->buswidth; --value;) { - s->ioaddr[s->iolen++] = 0; - } -} - -static void nand_command(NANDFlashState *s) -{ - unsigned int offset; - switch (s->cmd) { - case NAND_CMD_READ0: - s->iolen = 0; - break; - - case NAND_CMD_READID: - s->ioaddr = s->io; - s->iolen = 0; - nand_pushio_byte(s, s->manf_id); - nand_pushio_byte(s, s->chip_id); - nand_pushio_byte(s, 'Q'); /* Don't-care byte (often 0xa5) */ - if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { - /* Page Size, Block Size, Spare Size; bit 6 indicates - * 8 vs 16 bit width NAND. - */ - nand_pushio_byte(s, (s->buswidth == 2) ? 0x55 : 0x15); - } else { - nand_pushio_byte(s, 0xc0); /* Multi-plane */ - } - break; - - case NAND_CMD_RANDOMREAD2: - 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; - break; - - case NAND_CMD_RESET: - nand_reset(DEVICE(s)); - break; - - case NAND_CMD_PAGEPROGRAM1: - s->ioaddr = s->io; - s->iolen = 0; - break; - - case NAND_CMD_PAGEPROGRAM2: - if (s->wp) { - s->blk_write(s); - } - break; - - case NAND_CMD_BLOCKERASE1: - break; - - case NAND_CMD_BLOCKERASE2: - s->addr &= (1ull << s->addrlen * 8) - 1; - s->addr <<= nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP ? - 16 : 8; - - if (s->wp) { - s->blk_erase(s); - } - break; - - case NAND_CMD_READSTATUS: - s->ioaddr = s->io; - s->iolen = 0; - nand_pushio_byte(s, s->status); - break; - - default: - printf("%s: Unknown NAND command 0x%02x\n", __func__, s->cmd); - } -} - -static int nand_pre_save(void *opaque) -{ - NANDFlashState *s = NAND(opaque); - - s->ioaddr_vmstate = s->ioaddr - s->io; - - return 0; -} - -static int nand_post_load(void *opaque, int version_id) -{ - NANDFlashState *s = NAND(opaque); - - if (s->ioaddr_vmstate > sizeof(s->io)) { - return -EINVAL; - } - s->ioaddr = s->io + s->ioaddr_vmstate; - - return 0; -} - -static const VMStateDescription vmstate_nand = { - .name = "nand", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = nand_pre_save, - .post_load = nand_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(cle, NANDFlashState), - VMSTATE_UINT8(ale, NANDFlashState), - VMSTATE_UINT8(ce, NANDFlashState), - VMSTATE_UINT8(wp, NANDFlashState), - VMSTATE_UINT8(gnd, NANDFlashState), - VMSTATE_BUFFER(io, NANDFlashState), - VMSTATE_UINT32(ioaddr_vmstate, NANDFlashState), - VMSTATE_INT32(iolen, NANDFlashState), - VMSTATE_UINT32(cmd, NANDFlashState), - VMSTATE_UINT64(addr, NANDFlashState), - VMSTATE_INT32(addrlen, NANDFlashState), - VMSTATE_INT32(status, NANDFlashState), - VMSTATE_INT32(offset, NANDFlashState), - /* XXX: do we want to save s->storage too? */ - VMSTATE_END_OF_LIST() - } -}; - -static void nand_realize(DeviceState *dev, Error **errp) -{ - int pagesize; - NANDFlashState *s = NAND(dev); - int ret; - - - s->buswidth = nand_flash_ids[s->chip_id].width >> 3; - s->size = nand_flash_ids[s->chip_id].size << 20; - if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { - s->page_shift = 11; - s->erase_shift = 6; - } else { - s->page_shift = nand_flash_ids[s->chip_id].page_shift; - s->erase_shift = nand_flash_ids[s->chip_id].erase_shift; - } - - switch (1 << s->page_shift) { - case 256: - nand_init_256(s); - break; - case 512: - nand_init_512(s); - break; - case 2048: - nand_init_2048(s); - break; - default: - error_setg(errp, "Unsupported NAND block size %#x", - 1 << s->page_shift); - return; - } - - pagesize = 1 << s->oob_shift; - s->mem_oob = 1; - if (s->blk) { - if (!blk_supports_write_perm(s->blk)) { - error_setg(errp, "Can't use a read-only drive"); - return; - } - ret = blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, - BLK_PERM_ALL, errp); - if (ret < 0) { - return; - } - if (blk_getlength(s->blk) >= - (s->pages << s->page_shift) + (s->pages << s->oob_shift)) { - pagesize = 0; - s->mem_oob = 0; - } - } else { - pagesize += 1 << s->page_shift; - } - if (pagesize) { - s->storage = (uint8_t *) memset(g_malloc(s->pages * pagesize), - 0xff, s->pages * pagesize); - } - /* Give s->ioaddr a sane value in case we save state before it is used. */ - s->ioaddr = s->io; -} - -static Property nand_properties[] = { - DEFINE_PROP_UINT8("manufacturer_id", NANDFlashState, manf_id, 0), - DEFINE_PROP_UINT8("chip_id", NANDFlashState, chip_id, 0), - DEFINE_PROP_DRIVE("drive", NANDFlashState, blk), - DEFINE_PROP_END_OF_LIST(), -}; - -static void nand_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = nand_realize; - dc->reset = nand_reset; - dc->vmsd = &vmstate_nand; - device_class_set_props(dc, nand_properties); - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo nand_info = { - .name = TYPE_NAND, - .parent = TYPE_DEVICE, - .instance_size = sizeof(NANDFlashState), - .class_init = nand_class_init, -}; - -static void nand_register_types(void) -{ - type_register_static(&nand_info); -} - -/* - * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins. Chip - * outputs are R/B and eight I/O pins. - * - * CE, WP and R/B are active low. - */ -void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale, - uint8_t ce, uint8_t wp, uint8_t gnd) -{ - NANDFlashState *s = NAND(dev); - - s->cle = cle; - s->ale = ale; - s->ce = ce; - s->wp = wp; - s->gnd = gnd; - if (wp) { - s->status |= NAND_IOSTATUS_UNPROTCT; - } else { - s->status &= ~NAND_IOSTATUS_UNPROTCT; - } -} - -void nand_getpins(DeviceState *dev, int *rb) -{ - *rb = 1; -} - -void nand_setio(DeviceState *dev, uint32_t value) -{ - int i; - NANDFlashState *s = NAND(dev); - - if (!s->ce && s->cle) { - if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { - if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2) - return; - if (value == NAND_CMD_RANDOMREAD1) { - s->addr &= ~((1 << s->addr_shift) - 1); - s->addrlen = 0; - return; - } - } - if (value == NAND_CMD_READ0) { - s->offset = 0; - } else if (value == NAND_CMD_READ1) { - s->offset = 0x100; - value = NAND_CMD_READ0; - } else if (value == NAND_CMD_READ2) { - s->offset = 1 << s->page_shift; - value = NAND_CMD_READ0; - } - - s->cmd = value; - - if (s->cmd == NAND_CMD_READSTATUS || - s->cmd == NAND_CMD_PAGEPROGRAM2 || - s->cmd == NAND_CMD_BLOCKERASE1 || - s->cmd == NAND_CMD_BLOCKERASE2 || - s->cmd == NAND_CMD_NOSERIALREAD2 || - s->cmd == NAND_CMD_RANDOMREAD2 || - s->cmd == NAND_CMD_RESET) { - nand_command(s); - } - - if (s->cmd != NAND_CMD_RANDOMREAD2) { - s->addrlen = 0; - } - } - - if (s->ale) { - unsigned int shift = s->addrlen * 8; - uint64_t mask = ~(0xffull << shift); - uint64_t v = (uint64_t)value << shift; - - s->addr = (s->addr & mask) | v; - s->addrlen ++; - - switch (s->addrlen) { - case 1: - if (s->cmd == NAND_CMD_READID) { - nand_command(s); - } - break; - case 2: /* fix cache address as a byte address */ - s->addr <<= (s->buswidth - 1); - break; - case 3: - if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - (s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) { - nand_command(s); - } - break; - case 4: - if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - nand_flash_ids[s->chip_id].size < 256 && /* 1Gb or less */ - (s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) { - nand_command(s); - } - break; - case 5: - if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - nand_flash_ids[s->chip_id].size >= 256 && /* 2Gb or more */ - (s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) { - nand_command(s); - } - break; - default: - break; - } - } - - if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) { - if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift)) { - for (i = s->buswidth; i--; value >>= 8) { - s->io[s->iolen ++] = (uint8_t) (value & 0xff); - } - } - } else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) { - if ((s->addr & ((1 << s->addr_shift) - 1)) < - (1 << s->page_shift) + (1 << s->oob_shift)) { - for (i = s->buswidth; i--; s->addr++, value >>= 8) { - s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = - (uint8_t) (value & 0xff); - } - } - } -} - -uint32_t nand_getio(DeviceState *dev) -{ - int offset; - uint32_t x = 0; - NANDFlashState *s = NAND(dev); - - /* Allow sequential reading */ - 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; - } - - if (s->ce || s->iolen <= 0) { - return 0; - } - - for (offset = s->buswidth; offset--;) { - x |= s->ioaddr[offset] << (offset << 3); - } - /* after receiving READ STATUS command all subsequent reads will - * return the status register value until another command is issued - */ - if (s->cmd != NAND_CMD_READSTATUS) { - s->addr += s->buswidth; - s->ioaddr += s->buswidth; - s->iolen -= s->buswidth; - } - return x; -} - -uint32_t nand_getbuswidth(DeviceState *dev) -{ - NANDFlashState *s = (NANDFlashState *) dev; - return s->buswidth << 3; -} - -DeviceState *nand_init(BlockBackend *blk, int manf_id, int chip_id) -{ - DeviceState *dev; - - if (nand_flash_ids[chip_id].size == 0) { - hw_error("%s: Unsupported NAND chip ID.\n", __func__); - } - dev = qdev_new(TYPE_NAND); - qdev_prop_set_uint8(dev, "manufacturer_id", manf_id); - qdev_prop_set_uint8(dev, "chip_id", chip_id); - if (blk) { - qdev_prop_set_drive_err(dev, "drive", blk, &error_fatal); - } - - qdev_realize(dev, NULL, &error_fatal); - return dev; -} - -type_init(nand_register_types) - -#else - -/* Program a single page */ -static void glue(nand_blk_write_, NAND_PAGE_SIZE)(NANDFlashState *s) -{ - uint64_t off, page, sector, soff; - uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200]; - if (PAGE(s->addr) >= s->pages) - return; - - if (!s->blk) { - mem_and(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) + - s->offset, s->io, s->iolen); - } else if (s->mem_oob) { - sector = SECTOR(s->addr); - off = (s->addr & PAGE_MASK) + s->offset; - soff = SECTOR_OFFSET(s->addr); - if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS, - PAGE_SECTORS << BDRV_SECTOR_BITS, iobuf, 0) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); - return; - } - - mem_and(iobuf + (soff | off), s->io, MIN(s->iolen, NAND_PAGE_SIZE - off)); - if (off + s->iolen > NAND_PAGE_SIZE) { - page = PAGE(s->addr); - mem_and(s->storage + (page << OOB_SHIFT), s->io + NAND_PAGE_SIZE - off, - MIN(OOB_SIZE, off + s->iolen - NAND_PAGE_SIZE)); - } - - if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS, - PAGE_SECTORS << BDRV_SECTOR_BITS, iobuf, 0) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); - } - } else { - off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset; - sector = off >> 9; - soff = off & 0x1ff; - if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS, - (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, iobuf, 0) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); - return; - } - - mem_and(iobuf + soff, s->io, s->iolen); - - if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS, - (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, iobuf, 0) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); - } - } - s->offset = 0; -} - -/* Erase a single block */ -static void glue(nand_blk_erase_, NAND_PAGE_SIZE)(NANDFlashState *s) -{ - uint64_t i, page, addr; - uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, }; - addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1); - - if (PAGE(addr) >= s->pages) { - return; - } - - if (!s->blk) { - memset(s->storage + PAGE_START(addr), - 0xff, (NAND_PAGE_SIZE + OOB_SIZE) << s->erase_shift); - } else if (s->mem_oob) { - memset(s->storage + (PAGE(addr) << OOB_SHIFT), - 0xff, OOB_SIZE << s->erase_shift); - i = SECTOR(addr); - page = SECTOR(addr + (1 << (ADDR_SHIFT + s->erase_shift))); - for (; i < page; i ++) - if (blk_pwrite(s->blk, i << BDRV_SECTOR_BITS, - BDRV_SECTOR_SIZE, iobuf, 0) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, i); - } - } else { - addr = PAGE_START(addr); - page = addr >> 9; - if (blk_pread(s->blk, page << BDRV_SECTOR_BITS, - BDRV_SECTOR_SIZE, iobuf, 0) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, page); - } - memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1); - if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS, - BDRV_SECTOR_SIZE, iobuf, 0) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, page); - } - - memset(iobuf, 0xff, 0x200); - i = (addr & ~0x1ff) + 0x200; - for (addr += ((NAND_PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200; - i < addr; i += 0x200) { - if (blk_pwrite(s->blk, i, BDRV_SECTOR_SIZE, iobuf, 0) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", - __func__, i >> 9); - } - } - - page = i >> 9; - if (blk_pread(s->blk, page << BDRV_SECTOR_BITS, - BDRV_SECTOR_SIZE, iobuf, 0) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, page); - } - memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1); - if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS, - BDRV_SECTOR_SIZE, iobuf, 0) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, page); - } - } -} - -static void glue(nand_blk_load_, NAND_PAGE_SIZE)(NANDFlashState *s, - uint64_t addr, int offset) -{ - if (PAGE(addr) >= s->pages) { - return; - } - - if (s->blk) { - if (s->mem_oob) { - if (blk_pread(s->blk, SECTOR(addr) << BDRV_SECTOR_BITS, - PAGE_SECTORS << BDRV_SECTOR_BITS, s->io, 0) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", - __func__, SECTOR(addr)); - } - memcpy(s->io + SECTOR_OFFSET(s->addr) + NAND_PAGE_SIZE, - s->storage + (PAGE(s->addr) << OOB_SHIFT), - OOB_SIZE); - s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset; - } else { - if (blk_pread(s->blk, PAGE_START(addr), - (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, s->io, 0) - < 0) { - printf("%s: read error in sector %" PRIu64 "\n", - __func__, PAGE_START(addr) >> 9); - } - s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset; - } - } else { - memcpy(s->io, s->storage + PAGE_START(s->addr) + - offset, NAND_PAGE_SIZE + OOB_SIZE - offset); - s->ioaddr = s->io; - } -} - -static void glue(nand_init_, NAND_PAGE_SIZE)(NANDFlashState *s) -{ - s->oob_shift = PAGE_SHIFT - 5; - s->pages = s->size >> PAGE_SHIFT; - s->addr_shift = ADDR_SHIFT; - - s->blk_erase = glue(nand_blk_erase_, NAND_PAGE_SIZE); - s->blk_write = glue(nand_blk_write_, NAND_PAGE_SIZE); - s->blk_load = glue(nand_blk_load_, NAND_PAGE_SIZE); -} - -# undef NAND_PAGE_SIZE -# undef PAGE_SHIFT -# undef PAGE_SECTORS -# undef ADDR_SHIFT -#endif /* NAND_IO */ diff --git a/hw/block/onenand.c b/hw/block/onenand.c deleted file mode 100644 index 50d3d1c9856..00000000000 --- 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 3c066e34058..168101d8dfe 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -41,18 +41,17 @@ #include "hw/block/flash.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/bitops.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" -#include "sysemu/blockdev.h" -#include "sysemu/runstate.h" +#include "system/blockdev.h" +#include "system/runstate.h" #include "trace.h" #define PFLASH_BE 0 @@ -80,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 } }; @@ -225,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; @@ -400,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; - } - 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; + 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; + 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, @@ -494,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 */ @@ -548,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; @@ -581,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 */ @@ -595,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--; @@ -618,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; @@ -842,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; } @@ -865,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) @@ -884,14 +891,16 @@ 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[] = { +static const Property pflash_cfi01_properties[] = { DEFINE_PROP_DRIVE("drive", PFlashCFI01, blk), /* 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), @@ -923,34 +932,29 @@ static Property pflash_cfi01_properties[] = { DEFINE_PROP_STRING("name", PFlashCFI01, name), DEFINE_PROP_BOOL("old-multiple-chip-handling", PFlashCFI01, old_multiple_chip_handling, false), - DEFINE_PROP_END_OF_LIST(), }; -static void pflash_cfi01_class_init(ObjectClass *klass, void *data) +static void pflash_cfi01_class_init(ObjectClass *klass, const 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 2a99b286b07..3244b699b98 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -41,7 +41,7 @@ #include "qemu/error-report.h" #include "qemu/bitmap.h" #include "qemu/timer.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/host-utils.h" #include "qemu/module.h" #include "hw/sysbus.h" @@ -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; @@ -937,7 +937,7 @@ static void pflash_cfi02_reset(DeviceState *dev) pflash_reset_state_machine(pfl); } -static Property pflash_cfi02_properties[] = { +static const Property pflash_cfi02_properties[] = { DEFINE_PROP_DRIVE("drive", PFlashCFI02, blk), DEFINE_PROP_UINT32("num-blocks", PFlashCFI02, uniform_nb_blocs, 0), DEFINE_PROP_UINT32("sector-length", PFlashCFI02, uniform_sector_len, 0), @@ -959,7 +959,6 @@ static Property pflash_cfi02_properties[] = { DEFINE_PROP_UINT16("unlock-addr0", PFlashCFI02, unlock_addr0, 0), DEFINE_PROP_UINT16("unlock-addr1", PFlashCFI02, unlock_addr1, 0), DEFINE_PROP_STRING("name", PFlashCFI02, name), - DEFINE_PROP_END_OF_LIST(), }; static void pflash_cfi02_unrealize(DeviceState *dev) @@ -969,12 +968,12 @@ static void pflash_cfi02_unrealize(DeviceState *dev) g_free(pfl->sector_erase_map); } -static void pflash_cfi02_class_init(ObjectClass *klass, void *data) +static void pflash_cfi02_class_init(ObjectClass *klass, const 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 333da08ce09..ad047362f88 100644 --- a/hw/block/swim.c +++ b/hw/block/swim.c @@ -13,31 +13,36 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qapi/error.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #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) { } @@ -148,10 +166,9 @@ static const BlockDevOps swim_block_ops = { .change_media_cb = swim_change_cb, }; -static Property swim_drive_properties[] = { +static const Property swim_drive_properties[] = { DEFINE_PROP_INT32("unit", SWIMDrive, unit, -1), DEFINE_BLOCK_PROPERTIES(SWIMDrive, conf), - DEFINE_PROP_END_OF_LIST(), }; static void swim_drive_realize(DeviceState *qdev, Error **errp) @@ -236,7 +253,7 @@ static void swim_drive_realize(DeviceState *qdev, Error **errp) blk_set_dev_ops(drive->blk, &swim_block_ops, drive); } -static void swim_drive_class_init(ObjectClass *klass, void *data) +static void swim_drive_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->realize = swim_drive_realize; @@ -259,102 +276,158 @@ 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; - reg >>= REG_SHIFT; + addr >>= 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; + /* 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 reg, unsigned size) +static uint64_t iwmctrl_read(void *opaque, hwaddr addr, unsigned size) { SWIMCtrl *swimctrl = opaque; + uint8_t latch, reg, value; - reg >>= REG_SHIFT; + 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->regs[reg >> 1] = reg & 1; + switch (reg) { + case IWM_READALLONES: + value = 0xff; + break; + default: + value = 0; + break; + } - return 0; + trace_swim_iwmctrl_read(reg, iwm_reg_names[reg], size, value); + return value; } -static void swimctrl_write(void *opaque, hwaddr reg, uint64_t value, - unsigned size) +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; - 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: swimctrl->swim_phase = 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 +440,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 +475,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 +491,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 +506,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 +524,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,18 +544,18 @@ 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() } }; -static void sysbus_swim_class_init(ObjectClass *oc, void *data) +static void sysbus_swim_class_init(ObjectClass *oc, const 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 d350126b274..00000000000 --- 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 34be8b9135f..cc9a9f24603 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" @@ -90,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 eecf3f7a818..0eebbcd80d8 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -29,10 +29,8 @@ #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" - -#define REALIZE_CONNECTION_RETRIES 3 +#include "system/system.h" +#include "system/runstate.h" static const int user_feature_bits[] = { VIRTIO_BLK_F_SIZE_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 }; @@ -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; } @@ -197,7 +204,7 @@ static int vhost_user_blk_start(VirtIODevice *vdev, Error **errp) return ret; } -static void vhost_user_blk_stop(VirtIODevice *vdev) +static int vhost_user_blk_stop(VirtIODevice *vdev) { VHostUserBlk *s = VHOST_USER_BLK(vdev); BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); @@ -205,26 +212,26 @@ static void vhost_user_blk_stop(VirtIODevice *vdev) int ret; if (!s->started_vu) { - return; + return 0; } s->started_vu = false; if (!k->set_guest_notifiers) { - return; + return 0; } - vhost_dev_stop(&s->dev, vdev, true); + ret = vhost_dev_stop(&s->dev, vdev, true); - ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); - if (ret < 0) { + if (k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false) < 0) { error_report("vhost guest notifier cleanup failed: %d", ret); - return; + return -1; } vhost_dev_disable_notifiers(&s->dev, vdev); + return ret; } -static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status) +static int vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status) { VHostUserBlk *s = VHOST_USER_BLK(vdev); bool should_start = virtio_device_should_start(vdev, status); @@ -232,11 +239,11 @@ static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status) int ret; if (!s->connected) { - return; + return -1; } if (vhost_dev_is_started(&s->dev) == should_start) { - return; + return 0; } if (should_start) { @@ -246,9 +253,12 @@ static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status) qemu_chr_fe_disconnect(&s->chardev); } } else { - vhost_user_blk_stop(vdev); + ret = vhost_user_blk_stop(vdev); + if (ret < 0) { + return ret; + } } - + return 0; } static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev, @@ -328,7 +338,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 +354,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 +370,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 +378,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 +414,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 +432,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 +491,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,13 +567,13 @@ 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() }, }; -static Property vhost_user_blk_properties[] = { +static const Property vhost_user_blk_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserBlk, chardev), DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues, VHOST_USER_BLK_AUTO_NUM_QUEUES), @@ -575,16 +584,16 @@ static Property vhost_user_blk_properties[] = { VIRTIO_BLK_F_DISCARD, true), DEFINE_PROP_BIT64("write-zeroes", VHostUserBlk, parent_obj.host_features, VIRTIO_BLK_F_WRITE_ZEROES, true), - DEFINE_PROP_END_OF_LIST(), }; -static void vhost_user_blk_class_init(ObjectClass *klass, void *data) +static void vhost_user_blk_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 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.c b/hw/block/virtio-blk.c index 39e7f23fab3..9bab2716c14 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -12,6 +12,7 @@ */ #include "qemu/osdep.h" +#include "qemu/defer-call.h" #include "qapi/error.h" #include "qemu/iov.h" #include "qemu/module.h" @@ -21,22 +22,24 @@ #include "trace.h" #include "hw/block/block.h" #include "hw/qdev-properties.h" -#include "sysemu/blockdev.h" -#include "sysemu/block-ram-registrar.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/blockdev.h" +#include "system/block-ram-registrar.h" +#include "system/system.h" +#include "system/runstate.h" #include "hw/virtio/virtio-blk.h" -#include "dataplane/virtio-blk.h" #include "scsi/constants.h" #ifdef __linux__ # include #endif #include "hw/virtio/virtio-bus.h" #include "migration/qemu-file-types.h" +#include "hw/virtio/iothread-vq-mapping.h" #include "hw/virtio/virtio-access.h" #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) { @@ -48,12 +51,7 @@ static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq, req->mr_next = NULL; } -static void virtio_blk_free_request(VirtIOBlockReq *req) -{ - g_free(req); -} - -static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) +void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) { VirtIOBlock *s = req->dev; VirtIODevice *vdev = VIRTIO_DEVICE(s); @@ -64,8 +62,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); } @@ -81,14 +79,17 @@ 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) { block_acct_failed(blk_get_stats(s->blk), &req->acct); } - virtio_blk_free_request(req); + g_free(req); } blk_error_action(s->blk, action, is_read, error); @@ -101,7 +102,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; @@ -132,9 +132,8 @@ static void virtio_blk_rw_complete(void *opaque, int ret) virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); block_acct_done(blk_get_stats(s->blk), &req->acct); - virtio_blk_free_request(req); + g_free(req); } - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); } static void virtio_blk_flush_complete(void *opaque, int ret) @@ -142,19 +141,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)); + g_free(req); } static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret) @@ -164,76 +157,17 @@ 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); if (is_write_zeroes) { 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); + g_free(req); } -#endif - static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s, VirtQueue *vq) { VirtIOBlockReq *req = virtqueue_pop(vq, sizeof(VirtIOBlockReq)); @@ -244,20 +178,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. @@ -273,95 +201,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); + g_free(req); } static inline void submit_requests(VirtIOBlock *s, MultiReqBuffer *mrb, @@ -668,13 +517,15 @@ static void virtio_blk_zone_report_complete(void *opaque, int ret) { ZoneCmdData *data = opaque; VirtIOBlockReq *req = data->req; - VirtIOBlock *s = req->dev; 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) { @@ -682,9 +533,6 @@ static void virtio_blk_zone_report_complete(void *opaque, int ret) goto out; } - struct virtio_blk_zone_report zrp_hdr = (struct virtio_blk_zone_report) { - .nr_zones = cpu_to_le64(nz), - }; 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)); @@ -759,10 +607,8 @@ static void virtio_blk_zone_report_complete(void *opaque, int ret) } out: - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); virtio_blk_req_complete(req, err_status); - virtio_blk_free_request(req); - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); + g_free(req); g_free(data->zone_report_data.zones); g_free(data); } @@ -782,7 +628,8 @@ static void virtio_blk_handle_zone_report(VirtIOBlockReq *req, sizeof(struct virtio_blk_zone_report) + sizeof(struct virtio_blk_zone_descriptor)) { virtio_error(vdev, "in buffer too small for zone report"); - return; + err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD; + goto out; } /* start byte offset of the zone report */ @@ -810,7 +657,7 @@ static void virtio_blk_handle_zone_report(VirtIOBlockReq *req, return; out: virtio_blk_req_complete(req, err_status); - virtio_blk_free_request(req); + g_free(req); } static void virtio_blk_zone_mgmt_complete(void *opaque, int ret) @@ -825,10 +672,8 @@ static void virtio_blk_zone_mgmt_complete(void *opaque, int ret) err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD; } - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); virtio_blk_req_complete(req, err_status); - virtio_blk_free_request(req); - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); + g_free(req); } static int virtio_blk_handle_zone_mgmt(VirtIOBlockReq *req, BlockZoneOp op) @@ -851,7 +696,7 @@ static int virtio_blk_handle_zone_mgmt(VirtIOBlockReq *req, BlockZoneOp op) } else { if (bs->bl.zone_size > capacity - offset) { /* The zoned device allows the last smaller zone. */ - len = capacity - bs->bl.zone_size * (bs->bl.nr_zones - 1); + len = capacity - bs->bl.zone_size * (bs->bl.nr_zones - 1ull); } else { len = bs->bl.zone_size; } @@ -870,7 +715,7 @@ static int virtio_blk_handle_zone_mgmt(VirtIOBlockReq *req, BlockZoneOp op) return 0; out: virtio_blk_req_complete(req, err_status); - virtio_blk_free_request(req); + g_free(req); return err_status; } @@ -878,7 +723,6 @@ static void virtio_blk_zone_append_complete(void *opaque, int ret) { ZoneCmdData *data = opaque; VirtIOBlockReq *req = data->req; - VirtIOBlock *s = req->dev; VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); int64_t append_sector, n; uint8_t err_status = VIRTIO_BLK_S_OK; @@ -901,10 +745,8 @@ static void virtio_blk_zone_append_complete(void *opaque, int ret) trace_virtio_blk_zone_append_complete(vdev, req, append_sector, ret); out: - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); virtio_blk_req_complete(req, err_status); - virtio_blk_free_request(req); - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); + g_free(req); g_free(data); } @@ -919,13 +761,14 @@ static int virtio_blk_handle_zone_append(VirtIOBlockReq *req, 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; } - ZoneCmdData *data = g_malloc(sizeof(ZoneCmdData)); + data = g_malloc(sizeof(ZoneCmdData)); data->req = req; data->in_iov = in_iov; data->in_num = in_num; @@ -940,10 +783,8 @@ static int virtio_blk_handle_zone_append(VirtIOBlockReq *req, return 0; out: - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); virtio_blk_req_complete(req, err_status); - virtio_blk_free_request(req); - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); + g_free(req); return err_status; } @@ -1010,7 +851,7 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); block_acct_invalid(blk_get_stats(s->blk), is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ); - virtio_blk_free_request(req); + g_free(req); return 0; } @@ -1066,7 +907,7 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) VIRTIO_BLK_ID_BYTES)); iov_from_buf(in_iov, in_num, 0, serial, size); virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); - virtio_blk_free_request(req); + g_free(req); break; } case VIRTIO_BLK_T_ZONE_APPEND & ~VIRTIO_BLK_T_OUT: @@ -1098,7 +939,7 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) if (unlikely(!(type & VIRTIO_BLK_T_OUT) || out_len > sizeof(dwz_hdr))) { virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); - virtio_blk_free_request(req); + g_free(req); return 0; } @@ -1115,14 +956,24 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) is_write_zeroes); if (err_status != VIRTIO_BLK_S_OK) { virtio_blk_req_complete(req, err_status); - virtio_blk_free_request(req); + g_free(req); } break; } default: - virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); - virtio_blk_free_request(req); + { + /* + * Give subclasses a chance to handle unknown requests. This way the + * class lookup is not in the hot path. + */ + VirtIOBlkClass *vbk = VIRTIO_BLK_GET_CLASS(s); + if (!vbk->handle_unknown_request || + !vbk->handle_unknown_request(req, mrb, type)) { + virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); + g_free(req); + } + } } return 0; } @@ -1133,8 +984,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(); + defer_call_begin(); do { if (suppress_notifications) { @@ -1144,7 +994,7 @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) while ((req = virtio_blk_get_request(s, vq))) { if (virtio_blk_handle_request(req, &mrb)) { virtqueue_detach_element(req->vq, &req->elem, 0); - virtio_blk_free_request(req); + g_free(req); break; } } @@ -1158,36 +1008,33 @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) virtio_blk_submit_multireq(s, &mrb); } - blk_io_unplug(); - 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); } static void virtio_blk_dma_restart_bh(void *opaque) { - VirtIOBlock *s = opaque; + VirtIOBlockReq *req = opaque; + VirtIOBlock *s = req->dev; /* we're called with at least one request */ - VirtIOBlockReq *req = s->rq; 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)) { @@ -1197,7 +1044,7 @@ static void virtio_blk_dma_restart_bh(void *opaque) while (req) { next = req->next; virtqueue_detach_element(req->vq, &req->elem, 0); - virtio_blk_free_request(req); + g_free(req); req = next; } break; @@ -1211,48 +1058,79 @@ static void virtio_blk_dma_restart_bh(void *opaque) /* Paired with inc in virtio_blk_dma_restart_cb() */ 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_cb(void *opaque, bool running, RunState state) { VirtIOBlock *s = opaque; + uint16_t num_queues = s->conf.num_queues; + g_autofree VirtIOBlockReq **vq_rq = NULL; + VirtIOBlockReq *rq = NULL; if (!running) { return; } - /* Paired with dec in virtio_blk_dma_restart_bh() */ - blk_inc_in_flight(s->conf.conf.blk); + /* 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; + } - aio_bh_schedule_oneshot(blk_get_aio_context(s->conf.conf.blk), - virtio_blk_dma_restart_bh, s); + 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); + + 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; - aio_context_release(ctx); + /* No other threads can access req->vq here */ + virtqueue_detach_element(req->vq, &req->elem, 0); + + g_free(req); + } + } - assert(!s->dataplane_started); blk_set_enable_write_cache(s->blk, s->original_wce); } @@ -1267,10 +1145,6 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) uint64_t capacity; int64_t length; int blk_size = conf->logical_block_size; - AioContext *ctx; - - ctx = blk_get_aio_context(s->blk); - aio_context_acquire(ctx); blk_get_geometry(s->blk, &capacity); memset(&blkcfg, 0, sizeof(blkcfg)); @@ -1294,7 +1168,6 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) * per track (cylinder). */ length = blk_getlength(s->blk); - aio_context_release(ctx); if (length > 0 && length / conf->heads / conf->secs % blk_size) { blkcfg.geometry.sectors = conf->secs & ~s->sector_mask; } else { @@ -1361,9 +1234,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, @@ -1378,13 +1249,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); } @@ -1403,16 +1270,16 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, return features; } -static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) +static int 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)) { - return; + return 0; } /* A guest that supports VIRTIO_BLK_F_CONFIG_WCE must be able to send @@ -1431,29 +1298,32 @@ 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)); } + return 0; } 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); } @@ -1479,8 +1349,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; @@ -1499,44 +1372,50 @@ 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); } -/* Suspend virtqueue ioeventfd processing during drain */ -static void virtio_blk_drained_begin(void *opaque) +static void virtio_blk_ioeventfd_detach(VirtIOBlock *s) { - VirtIOBlock *s = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(opaque); - AioContext *ctx = blk_get_aio_context(s->conf.conf.blk); + VirtIODevice *vdev = VIRTIO_DEVICE(s); - if (!s->dataplane || !s->dataplane_started) { - return; + 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_detach_host_notifier(vq, ctx); + virtio_queue_aio_attach_host_notifier(vq, s->vq_aio_context[i]); } } -/* Resume virtqueue ioeventfd processing after drain */ -static void virtio_blk_drained_end(void *opaque) +/* Suspend virtqueue ioeventfd processing during drain */ +static void virtio_blk_drained_begin(void *opaque) { VirtIOBlock *s = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(opaque); - AioContext *ctx = blk_get_aio_context(s->conf.conf.blk); - if (!s->dataplane || !s->dataplane_started) { - return; + if (s->ioeventfd_started) { + virtio_blk_ioeventfd_detach(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, ctx); +/* 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); } } @@ -1546,11 +1425,277 @@ static const BlockDevOps virtio_block_ops = { .drained_end = virtio_blk_drained_end, }; +/* 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; + } + } + + s->vq_aio_context = g_new(AioContext *, conf->num_queues); + + if (conf->iothread_vq_mapping_list) { + if (!iothread_vq_mapping_apply(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) { + iothread_vq_mapping_cleanup(conf->iothread_vq_mapping_list); + } + + 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; @@ -1596,7 +1741,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) return; } - BlockDriverState *bs = blk_bs(conf->conf.blk); + 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) { @@ -1627,6 +1772,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; @@ -1635,7 +1782,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++) { @@ -1650,7 +1803,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) * 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); + qdev_add_vm_change_state_handler(dev, virtio_blk_dma_restart_cb, NULL, s); blk_ram_registrar_init(&s->blk_ram_registrar, s->blk); blk_set_dev_ops(s->blk, &virtio_block_ops, s); @@ -1672,12 +1825,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); @@ -1697,23 +1850,19 @@ 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() }, }; -static Property virtio_blk_properties[] = { +static const Property virtio_blk_properties[] = { DEFINE_BLOCK_PROPERTIES(VirtIOBlock, conf.conf), DEFINE_BLOCK_ERROR_PROPERTIES(VirtIOBlock, conf.conf), DEFINE_BLOCK_CHS_PROPERTIES(VirtIOBlock, conf.conf), 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, @@ -1722,6 +1871,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, @@ -1734,10 +1885,9 @@ static Property virtio_blk_properties[] = { conf.max_write_zeroes_sectors, BDRV_REQUEST_MAX_SECTORS), DEFINE_PROP_BOOL("x-enable-wce-if-config-wce", VirtIOBlock, conf.x_enable_wce_if_config_wce, true), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_blk_class_init(ObjectClass *klass, void *data) +static void virtio_blk_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); @@ -1754,8 +1904,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 = { @@ -1764,6 +1914,7 @@ static const TypeInfo virtio_blk_info = { .instance_size = sizeof(VirtIOBlock), .instance_init = virtio_blk_instance_init, .class_init = virtio_blk_class_init, + .class_size = sizeof(VirtIOBlkClass), }; static void virtio_register_types(void) diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 3906b9058bc..74de897c798 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -16,24 +16,148 @@ #include "qapi/qapi-visit-block-core.h" #include "qapi/qobject-input-visitor.h" #include "qapi/visitor.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qom/object_interfaces.h" #include "hw/block/xen_blkif.h" #include "hw/qdev-properties.h" #include "hw/xen/xen-block.h" #include "hw/xen/xen-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/block-backend.h" -#include "sysemu/iothread.h" +#include "system/blockdev.h" +#include "system/block-backend.h" +#include "system/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); } @@ -115,9 +239,14 @@ static void xen_block_connect(XenDevice *xendev, Error **errp) return; } - if (xen_device_frontend_scanf(xendev, "protocol", "%ms", - &str) != 1) { - protocol = BLKIF_PROTOCOL_NATIVE; + str = xen_device_frontend_read(xendev, "protocol"); + if (!str) { + /* 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; @@ -279,6 +408,8 @@ static void xen_block_realize(XenDevice *xendev, Error **errp) } xen_device_backend_printf(xendev, "info", "%u", blockdev->info); + xen_device_backend_printf(xendev, "mode", + (blockdev->info & VDISK_READONLY) ? "r" : "w"); xen_device_frontend_printf(xendev, "virtual-device", "%lu", vdev->number); @@ -357,7 +488,7 @@ static char *disk_to_vbd_name(unsigned int disk) static void xen_block_get_vdev(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; XenBlockVdev *vdev = object_field_prop_ptr(obj, prop); char *str; @@ -369,7 +500,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) ? @@ -377,8 +508,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: @@ -417,7 +548,7 @@ static int vbd_name_to_disk(const char *name, const char **endp, static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; XenBlockVdev *vdev = object_field_prop_ptr(obj, prop); char *str, *p; const char *end; @@ -478,10 +609,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; @@ -491,10 +622,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; @@ -503,7 +635,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; } @@ -529,14 +662,14 @@ static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name, * * https://xenbits.xen.org/docs/unstable/man/xen-vbd-interface.7.html */ -const PropertyInfo xen_block_prop_vdev = { - .name = "str", - .description = "Virtual Disk specifier: d*p*/xvd*/hd*/sd*", +static const PropertyInfo xen_block_prop_vdev = { + .type = "str", + .description = "Virtual Disk specifier (d*p*/xvd*/hd*/sd*)", .get = xen_block_get_vdev, .set = xen_block_set_vdev, }; -static Property xen_block_props[] = { +static const Property xen_block_props[] = { DEFINE_PROP("vdev", XenBlockDevice, props.vdev, xen_block_prop_vdev, XenBlockVdev), DEFINE_BLOCK_PROPERTIES(XenBlockDevice, props.conf), @@ -544,10 +677,9 @@ static Property xen_block_props[] = { props.max_ring_page_order, 4), DEFINE_PROP_LINK("iothread", XenBlockDevice, props.iothread, TYPE_IOTHREAD, IOThread *), - DEFINE_PROP_END_OF_LIST() }; -static void xen_block_class_init(ObjectClass *class, void *data) +static void xen_block_class_init(ObjectClass *class, const void *data) { DeviceClass *dev_class = DEVICE_CLASS(class); XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class); @@ -592,7 +724,7 @@ static void xen_disk_realize(XenBlockDevice *blockdev, Error **errp) blockdev->info = blk_supports_write_perm(conf->blk) ? 0 : VDISK_READONLY; } -static void xen_disk_class_init(ObjectClass *class, void *data) +static void xen_disk_class_init(ObjectClass *class, const void *data) { DeviceClass *dev_class = DEVICE_CLASS(class); XenBlockDeviceClass *blockdev_class = XEN_BLOCK_DEVICE_CLASS(class); @@ -639,7 +771,7 @@ static void xen_cdrom_realize(XenBlockDevice *blockdev, Error **errp) blockdev->info = VDISK_READONLY | VDISK_CDROM; } -static void xen_cdrom_class_init(ObjectClass *class, void *data) +static void xen_cdrom_class_init(ObjectClass *class, const void *data) { DeviceClass *dev_class = DEVICE_CLASS(class); XenBlockDeviceClass *blockdev_class = XEN_BLOCK_DEVICE_CLASS(class); diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 6b6cf2fc1df..9d517f3e287 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -11,6 +11,12 @@ config PARALLEL config PL011 bool + # The PL011 has both a Rust and a C implementation + select PL011_C if !HAVE_RUST + select X_PL011_RUST if HAVE_RUST + +config PL011_C + bool config SERIAL bool @@ -21,6 +27,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 +51,9 @@ config VIRTIO_SERIAL config STM32F2XX_USART bool +config STM32L4X5_USART + bool + config CMSDK_APB_UART bool @@ -59,6 +72,9 @@ config RENESAS_SCI config AVR_USART bool +config DIVA_GSP + bool + config MCHP_PFSOC_MMUART bool select SERIAL @@ -71,3 +87,8 @@ config GOLDFISH_TTY config SHAKTI_UART bool + +config IP_OCTAL_232 + bool + default y + depends on IPACK diff --git a/hw/char/avr_usart.c b/hw/char/avr_usart.c index 5bcf9db0b78..fae15217e9f 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(); } } @@ -259,9 +259,8 @@ static const MemoryRegionOps avr_usart_ops = { .impl = {.min_access_size = 1, .max_access_size = 1} }; -static Property avr_usart_properties[] = { +static const Property avr_usart_properties[] = { DEFINE_PROP_CHR("chardev", AVRUsartState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void avr_usart_pr(void *opaque, int irq, int level) @@ -296,11 +295,11 @@ static void avr_usart_realize(DeviceState *dev, Error **errp) avr_usart_reset(dev); } -static void avr_usart_class_init(ObjectClass *klass, void *data) +static void avr_usart_class_init(ObjectClass *klass, const 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 96410b1ff86..2b397f2ff39 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -98,7 +98,7 @@ static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size) * interrupts are active, besides that this cannot occur. At * present, we choose to prioritise the rx interrupt, since * the tx fifo is always empty. */ - if (s->read_count != 0) { + if ((s->iir & RX_INT) && s->read_count != 0) { res |= 0x4; } else { res |= 0x2; @@ -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; @@ -221,7 +221,7 @@ static int bcm2835_aux_can_receive(void *opaque) { BCM2835AuxState *s = opaque; - return s->read_count < BCM2835_AUX_RX_FIFO_LEN; + return BCM2835_AUX_RX_FIFO_LEN - s->read_count; } static void bcm2835_aux_put_fifo(void *opaque, uint8_t value) @@ -243,7 +243,9 @@ static void bcm2835_aux_put_fifo(void *opaque, uint8_t value) static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size) { - bcm2835_aux_put_fifo(opaque, *buf); + for (int i = 0; i < size; i++) { + bcm2835_aux_put_fifo(opaque, buf[i]); + } } static const MemoryRegionOps bcm2835_aux_ops = { @@ -260,7 +262,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), @@ -290,12 +292,11 @@ static void bcm2835_aux_realize(DeviceState *dev, Error **errp) bcm2835_aux_receive, NULL, NULL, s, NULL, true); } -static Property bcm2835_aux_props[] = { +static const Property bcm2835_aux_props[] = { DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr), - DEFINE_PROP_END_OF_LIST(), }; -static void bcm2835_aux_class_init(ObjectClass *oc, void *data) +static void bcm2835_aux_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index eff0304a18f..0dfa356b6d0 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -525,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); @@ -575,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; } @@ -602,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), @@ -617,12 +617,11 @@ static const VMStateDescription vmstate_cadence_uart = { }, }; -static Property cadence_uart_properties[] = { +static const Property cadence_uart_properties[] = { DEFINE_PROP_CHR("chardev", CadenceUARTState, chr), - DEFINE_PROP_END_OF_LIST(), }; -static void cadence_uart_class_init(ObjectClass *klass, void *data) +static void cadence_uart_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/char/cmsdk-apb-uart.c b/hw/char/cmsdk-apb-uart.c index d466cd93de4..32090f3516f 100644 --- a/hw/char/cmsdk-apb-uart.c +++ b/hw/char/cmsdk-apb-uart.c @@ -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), @@ -377,19 +377,18 @@ static const VMStateDescription cmsdk_apb_uart_vmstate = { } }; -static Property cmsdk_apb_uart_properties[] = { +static const Property cmsdk_apb_uart_properties[] = { DEFINE_PROP_CHR("chardev", CMSDKAPBUART, chr), DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBUART, pclk_frq, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void cmsdk_apb_uart_class_init(ObjectClass *klass, void *data) +static void cmsdk_apb_uart_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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/debugcon.c b/hw/char/debugcon.c index fdb04fee093..bf44aaf9e4a 100644 --- a/hw/char/debugcon.c +++ b/hw/char/debugcon.c @@ -114,14 +114,13 @@ static void debugcon_isa_realizefn(DeviceState *dev, Error **errp) isa->iobase, &s->io); } -static Property debugcon_isa_properties[] = { +static const Property debugcon_isa_properties[] = { DEFINE_PROP_UINT32("iobase", ISADebugconState, iobase, 0xe9), DEFINE_PROP_CHR("chardev", ISADebugconState, state.chr), DEFINE_PROP_UINT32("readback", ISADebugconState, state.readback, 0xe9), - DEFINE_PROP_END_OF_LIST(), }; -static void debugcon_isa_class_initfn(ObjectClass *klass, void *data) +static void debugcon_isa_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c index 51d4e7db52f..0f6af51bb7b 100644 --- a/hw/char/digic-uart.c +++ b/hw/char/digic-uart.c @@ -165,24 +165,23 @@ 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() } }; -static Property digic_uart_properties[] = { +static const Property digic_uart_properties[] = { DEFINE_PROP_CHR("chardev", DigicUartState, chr), - DEFINE_PROP_END_OF_LIST(), }; -static void digic_uart_class_init(ObjectClass *klass, void *data) +static void digic_uart_class_init(ObjectClass *klass, const 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/diva-gsp.c b/hw/char/diva-gsp.c new file mode 100644 index 00000000000..e1f0713cb79 --- /dev/null +++ b/hw/char/diva-gsp.c @@ -0,0 +1,295 @@ +/* + * HP Diva GSP controller + * + * The Diva PCI boards are Remote Management cards for PA-RISC machines. + * They come with built-in 16550A multi UARTs for serial consoles + * and a mailbox-like memory area for hardware auto-reboot functionality. + * GSP stands for "Guardian Service Processor". Later products were marketed + * "Management Processor" (MP). + * + * Diva cards are multifunctional cards. The first part, the aux port, + * is on physical machines not useable but we still try to mimic it here. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Helge Deller + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/char/serial.h" +#include "hw/irq.h" +#include "hw/pci/pci_device.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "migration/vmstate.h" + +#define PCI_DEVICE_ID_HP_DIVA 0x1048 +/* various DIVA GSP cards: */ +#define PCI_DEVICE_ID_HP_DIVA_TOSCA1 0x1049 +#define PCI_DEVICE_ID_HP_DIVA_TOSCA2 0x104A +#define PCI_DEVICE_ID_HP_DIVA_MAESTRO 0x104B +#define PCI_DEVICE_ID_HP_REO_IOC 0x10f1 +#define PCI_DEVICE_ID_HP_DIVA_HALFDOME 0x1223 +#define PCI_DEVICE_ID_HP_DIVA_KEYSTONE 0x1226 +#define PCI_DEVICE_ID_HP_DIVA_POWERBAR 0x1227 +#define PCI_DEVICE_ID_HP_DIVA_EVEREST 0x1282 +#define PCI_DEVICE_ID_HP_DIVA_AUX 0x1290 +#define PCI_DEVICE_ID_HP_DIVA_RMP3 0x1301 +#define PCI_DEVICE_ID_HP_DIVA_HURRICANE 0x132a + + +#define PCI_SERIAL_MAX_PORTS 4 + +typedef struct PCIDivaSerialState { + PCIDevice dev; + MemoryRegion membar; /* for serial ports */ + MemoryRegion mailboxbar; /* for hardware mailbox */ + uint32_t subvendor; + uint32_t ports; + char *name[PCI_SERIAL_MAX_PORTS]; + SerialState state[PCI_SERIAL_MAX_PORTS]; + uint32_t level[PCI_SERIAL_MAX_PORTS]; + qemu_irq *irqs; + bool disable; +} PCIDivaSerialState; + +static void diva_pci_exit(PCIDevice *dev) +{ + PCIDivaSerialState *pci = DO_UPCAST(PCIDivaSerialState, dev, dev); + SerialState *s; + int i; + + for (i = 0; i < pci->ports; i++) { + s = pci->state + i; + qdev_unrealize(DEVICE(s)); + memory_region_del_subregion(&pci->membar, &s->io); + g_free(pci->name[i]); + } + qemu_free_irqs(pci->irqs, pci->ports); +} + +static void multi_serial_irq_mux(void *opaque, int n, int level) +{ + PCIDivaSerialState *pci = opaque; + int i, pending = 0; + + pci->level[n] = level; + for (i = 0; i < pci->ports; i++) { + if (pci->level[i]) { + pending = 1; + } + } + pci_set_irq(&pci->dev, pending); +} + +struct diva_info { + unsigned int nports:4; /* number of serial ports */ + unsigned int omask:12; /* offset mask: BIT(1) -> offset 8 */ +}; + +static struct diva_info diva_get_diva_info(PCIDeviceClass *pc) +{ + switch (pc->subsystem_id) { + case PCI_DEVICE_ID_HP_DIVA_POWERBAR: + case PCI_DEVICE_ID_HP_DIVA_HURRICANE: + return (struct diva_info) { .nports = 1, + .omask = BIT(0) }; + case PCI_DEVICE_ID_HP_DIVA_TOSCA2: + return (struct diva_info) { .nports = 2, + .omask = BIT(0) | BIT(1) }; + case PCI_DEVICE_ID_HP_DIVA_TOSCA1: + case PCI_DEVICE_ID_HP_DIVA_HALFDOME: + case PCI_DEVICE_ID_HP_DIVA_KEYSTONE: + return (struct diva_info) { .nports = 3, + .omask = BIT(0) | BIT(1) | BIT(2) }; + case PCI_DEVICE_ID_HP_DIVA_EVEREST: /* e.g. in rp3410 */ + return (struct diva_info) { .nports = 3, + .omask = BIT(0) | BIT(2) | BIT(7) }; + case PCI_DEVICE_ID_HP_DIVA_MAESTRO: + return (struct diva_info) { .nports = 4, + .omask = BIT(0) | BIT(1) | BIT(2) | BIT(7) }; + } + g_assert_not_reached(); +} + + +static void diva_pci_realize(PCIDevice *dev, Error **errp) +{ + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); + PCIDivaSerialState *pci = DO_UPCAST(PCIDivaSerialState, dev, dev); + SerialState *s; + struct diva_info di = diva_get_diva_info(pc); + size_t i, offset = 0; + size_t portmask = di.omask; + + pci->dev.config[PCI_CLASS_PROG] = 2; /* 16550 compatible */ + pci->dev.config[PCI_INTERRUPT_PIN] = 1; + memory_region_init(&pci->membar, OBJECT(pci), "serial_ports", 4096); + pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &pci->membar); + pci->irqs = qemu_allocate_irqs(multi_serial_irq_mux, pci, di.nports); + + for (i = 0; i < di.nports; i++) { + s = pci->state + i; + if (!qdev_realize(DEVICE(s), NULL, errp)) { + diva_pci_exit(dev); + return; + } + s->irq = pci->irqs[i]; + pci->name[i] = g_strdup_printf("uart #%zu", i + 1); + memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s, + pci->name[i], 8); + + /* calculate offset of given port based on bitmask */ + while ((portmask & BIT(0)) == 0) { + offset += 8; + portmask >>= 1; + } + memory_region_add_subregion(&pci->membar, offset, &s->io); + offset += 8; + portmask >>= 1; + pci->ports++; + } + + /* mailbox bar */ + memory_region_init(&pci->mailboxbar, OBJECT(pci), "mailbox", 128 * KiB); + pci_register_bar(&pci->dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_PREFETCH, &pci->mailboxbar); +} + +static const VMStateDescription vmstate_pci_diva = { + .name = "pci-diva-serial", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_PCI_DEVICE(dev, PCIDivaSerialState), + VMSTATE_STRUCT_ARRAY(state, PCIDivaSerialState, PCI_SERIAL_MAX_PORTS, + 0, vmstate_serial, SerialState), + VMSTATE_UINT32_ARRAY(level, PCIDivaSerialState, PCI_SERIAL_MAX_PORTS), + VMSTATE_BOOL(disable, PCIDivaSerialState), + VMSTATE_END_OF_LIST() + } +}; + +static const Property diva_serial_properties[] = { + DEFINE_PROP_BOOL("disable", PCIDivaSerialState, disable, false), + DEFINE_PROP_CHR("chardev1", PCIDivaSerialState, state[0].chr), + DEFINE_PROP_CHR("chardev2", PCIDivaSerialState, state[1].chr), + DEFINE_PROP_CHR("chardev3", PCIDivaSerialState, state[2].chr), + DEFINE_PROP_CHR("chardev4", PCIDivaSerialState, state[3].chr), + DEFINE_PROP_UINT32("subvendor", PCIDivaSerialState, subvendor, + PCI_DEVICE_ID_HP_DIVA_TOSCA1), +}; + +static void diva_serial_class_initfn(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); + pc->realize = diva_pci_realize; + pc->exit = diva_pci_exit; + pc->vendor_id = PCI_VENDOR_ID_HP; + pc->device_id = PCI_DEVICE_ID_HP_DIVA; + pc->subsystem_vendor_id = PCI_VENDOR_ID_HP; + pc->subsystem_id = PCI_DEVICE_ID_HP_DIVA_TOSCA1; + pc->revision = 3; + pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL; + dc->vmsd = &vmstate_pci_diva; + device_class_set_props(dc, diva_serial_properties); + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static void diva_serial_init(Object *o) +{ + PCIDevice *dev = PCI_DEVICE(o); + PCIDivaSerialState *pms = DO_UPCAST(PCIDivaSerialState, dev, dev); + struct diva_info di = diva_get_diva_info(PCI_DEVICE_GET_CLASS(dev)); + size_t i; + + for (i = 0; i < di.nports; i++) { + object_initialize_child(o, "serial[*]", &pms->state[i], TYPE_SERIAL); + } +} + + +/* Diva-aux is the driver for portion 0 of the multifunction PCI device */ + +struct DivaAuxState { + PCIDevice dev; + MemoryRegion mem; + qemu_irq irq; +}; + +#define TYPE_DIVA_AUX "diva-aux" +OBJECT_DECLARE_SIMPLE_TYPE(DivaAuxState, DIVA_AUX) + +static void diva_aux_realize(PCIDevice *dev, Error **errp) +{ + DivaAuxState *pci = DO_UPCAST(DivaAuxState, dev, dev); + + pci->dev.config[PCI_CLASS_PROG] = 0x02; + pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; + pci->irq = pci_allocate_irq(&pci->dev); + + memory_region_init(&pci->mem, OBJECT(pci), "mem", 16); + pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &pci->mem); +} + +static void diva_aux_exit(PCIDevice *dev) +{ + DivaAuxState *pci = DO_UPCAST(DivaAuxState, dev, dev); + qemu_free_irq(pci->irq); +} + +static void diva_aux_class_initfn(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); + pc->realize = diva_aux_realize; + pc->exit = diva_aux_exit; + pc->vendor_id = PCI_VENDOR_ID_HP; + pc->device_id = PCI_DEVICE_ID_HP_DIVA_AUX; + pc->subsystem_vendor_id = PCI_VENDOR_ID_HP; + pc->subsystem_id = 0x1291; + pc->revision = 1; + pc->class_id = PCI_CLASS_COMMUNICATION_MULTISERIAL; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->user_creatable = false; +} + +static void diva_aux_init(Object *o) +{ +} + +static const TypeInfo diva_aux_info = { + .name = TYPE_DIVA_AUX, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(DivaAuxState), + .instance_init = diva_aux_init, + .class_init = diva_aux_class_initfn, + .interfaces = (const InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + + + +static const TypeInfo diva_serial_pci_info = { + .name = "diva-gsp", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDivaSerialState), + .instance_init = diva_serial_init, + .class_init = diva_serial_class_initfn, + .interfaces = (const InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + +static void diva_pci_register_type(void) +{ + type_register_static(&diva_serial_pci_info); + type_register_static(&diva_aux_info); +} + +type_init(diva_pci_register_type) diff --git a/hw/char/escc.c b/hw/char/escc.c index 4be66053c14..afe4ca483e7 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -287,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); } @@ -766,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), @@ -785,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() @@ -845,7 +846,7 @@ 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, @@ -952,53 +953,96 @@ static void handle_kbd_command(ESCCChannelState *s, int val) } } -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; - int ch; + 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; - trace_escc_sunmouse_event(dx, dy, buttons_state); - ch = 0x80 | 0x7; /* protocol start byte, no buttons pressed */ + 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; - if (buttons_state & MOUSE_EVENT_LBUTTON) { - ch ^= 0x4; - } - if (buttons_state & MOUSE_EVENT_MBUTTON) { - ch ^= 0x2; + default: + /* keep gcc happy */ + break; } - if (buttons_state & MOUSE_EVENT_RBUTTON) { - ch ^= 0x1; +} + +static void sunmouse_sync(DeviceState *dev) +{ + ESCCChannelState *s = (ESCCChannelState *)dev; + int ch; + + 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 */ + 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); @@ -1036,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]), @@ -1045,7 +1089,7 @@ static void escc_realize(DeviceState *dev, Error **errp) } } -static Property escc_properties[] = { +static const Property escc_properties[] = { DEFINE_PROP_UINT32("frequency", ESCCState, frequency, 0), DEFINE_PROP_UINT32("it_shift", ESCCState, it_shift, 0), DEFINE_PROP_BOOL("bit_swap", ESCCState, bit_swap, false), @@ -1055,14 +1099,13 @@ static Property escc_properties[] = { 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(), }; -static void escc_class_init(ObjectClass *klass, void *data) +static void escc_class_init(ObjectClass *klass, const 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 8d6422dae41..00000000000 --- 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 " HWADDR_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 " HWADDR_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 7b7c56b6ef4..6521b4cedd9 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, @@ -704,20 +704,19 @@ static void exynos4210_uart_realize(DeviceState *dev, Error **errp) NULL, s, NULL, true); } -static Property exynos4210_uart_properties[] = { +static const Property exynos4210_uart_properties[] = { DEFINE_PROP_CHR("chardev", Exynos4210UartState, chr), DEFINE_PROP_UINT32("channel", Exynos4210UartState, channel, 0), DEFINE_PROP_UINT32("rx-size", Exynos4210UartState, rx.size, 16), DEFINE_PROP_UINT32("tx-size", Exynos4210UartState, tx.size, 16), - DEFINE_PROP_END_OF_LIST(), }; -static void exynos4210_uart_class_init(ObjectClass *klass, void *data) +static void exynos4210_uart_class_init(ObjectClass *klass, const 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 20b77885c18..a37408adae5 100644 --- a/hw/char/goldfish_tty.c +++ b/hw/char/goldfish_tty.c @@ -15,7 +15,8 @@ #include "chardev/char-fe.h" #include "qemu/log.h" #include "trace.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" +#include "system/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), @@ -241,9 +241,8 @@ static const VMStateDescription vmstate_goldfish_tty = { } }; -static Property goldfish_tty_properties[] = { +static const Property goldfish_tty_properties[] = { DEFINE_PROP_CHR("chardev", GoldfishTTYState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void goldfish_tty_instance_init(Object *obj) @@ -257,12 +256,12 @@ static void goldfish_tty_instance_init(Object *obj) sysbus_init_irq(dev, &s->irq); } -static void goldfish_tty_class_init(ObjectClass *oc, void *data) +static void goldfish_tty_class_init(ObjectClass *oc, const 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 82ff40a530a..81c26e33899 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" @@ -275,17 +277,16 @@ static void grlib_apbuart_reset(DeviceState *d) uart->current = 0; } -static Property grlib_apbuart_properties[] = { +static const Property grlib_apbuart_properties[] = { DEFINE_PROP_CHR("chrdev", UART, chr), - DEFINE_PROP_END_OF_LIST(), }; -static void grlib_apbuart_class_init(ObjectClass *klass, void *data) +static void grlib_apbuart_class_init(ObjectClass *klass, const 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 51708c08363..d6f0d18c777 100644 --- a/hw/char/ibex_uart.c +++ b/hw/char/ibex_uart.c @@ -488,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), @@ -508,9 +508,8 @@ static const VMStateDescription vmstate_ibex_uart = { } }; -static Property ibex_uart_properties[] = { +static const Property ibex_uart_properties[] = { DEFINE_PROP_CHR("chardev", IbexUartState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void ibex_uart_init(Object *obj) @@ -543,11 +542,11 @@ static void ibex_uart_realize(DeviceState *dev, Error **errp) s, NULL, true); } -static void ibex_uart_class_init(ObjectClass *klass, void *data) +static void ibex_uart_class_init(ObjectClass *klass, const 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 1b75a895881..509b0141d05 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -26,6 +26,8 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" +#include "qemu/fifo32.h" +#include "trace.h" #ifndef DEBUG_IMX_UART #define DEBUG_IMX_UART 0 @@ -41,10 +43,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 +74,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 +85,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_WKEN | 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 +160,10 @@ static void imx_serial_reset(IMXSerialState *s) s->ucr3 = 0x700; s->ubmr = 0; s->ubrc = 4; - s->readbuff = URXD_ERR; + s->ufcr = BIT(11) | BIT(0); + + fifo32_reset(&s->rx_fifo); + timer_del(&s->ageing_timer); } static void imx_serial_reset_at_boot(DeviceState *dev) @@ -112,7 +173,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,65 +186,91 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset, unsigned size) { IMXSerialState *s = (IMXSerialState *)opaque; - uint32_t c; - - DPRINTF("read(offset=0x%" HWADDR_PRIx ")\n", offset); + Chardev *chr = qemu_chr_fe_get_driver(&s->chr); + uint32_t c, rx_used; + uint8_t rxtl = s->ufcr & TL_MASK; + uint64_t value; 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; + value = c; + break; case 0x20: /* UCR1 */ - return s->ucr1; + value = s->ucr1; + break; case 0x21: /* UCR2 */ - return s->ucr2; + value = s->ucr2; + break; case 0x25: /* USR1 */ - return s->usr1; + value = s->usr1; + break; case 0x26: /* USR2 */ - return s->usr2; + value = s->usr2; + break; case 0x2A: /* BRM Modulator */ - return s->ubmr; + value = s->ubmr; + break; case 0x2B: /* Baud Rate Count */ - return s->ubrc; + value = s->ubrc; + break; case 0x2d: /* Test register */ - return s->uts1; + value = s->uts1; + break; case 0x24: /* UFCR */ - return s->ufcr; + value = s->ufcr; + break; case 0x2c: - return s->onems; + value = s->onems; + break; case 0x22: /* UCR3 */ - return s->ucr3; + value = s->ucr3; + break; case 0x23: /* UCR4 */ - return s->ucr4; + value = s->ucr4; + break; case 0x29: /* BRM Incremental */ - return 0x0; /* TODO */ + value = 0x0; /* TODO */ + break; default: qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset); - return 0; + value = 0; + break; } + + trace_imx_serial_read(chr ? chr->label : "NODEV", offset, value); + + return value; } static void imx_serial_write(void *opaque, hwaddr offset, @@ -193,8 +280,7 @@ static void imx_serial_write(void *opaque, hwaddr offset, Chardev *chr = qemu_chr_fe_get_driver(&s->chr); unsigned char ch; - DPRINTF("write(offset=0x%" HWADDR_PRIx ", value = 0x%x) to %s\n", - offset, (unsigned int)value, chr ? chr->label : "NODEV"); + trace_imx_serial_write(chr ? chr->label : "NODEV", offset, value); switch (offset >> 2) { case 0x10: /* UTXD */ @@ -300,22 +386,30 @@ 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_free(&s->rx_fifo) : 0; } static void imx_put_data(void *opaque, uint32_t value) { IMXSerialState *s = (IMXSerialState *)opaque; + Chardev *chr = qemu_chr_fe_get_driver(&s->chr); + uint8_t rxtl = s->ufcr & TL_MASK; - DPRINTF("received char\n"); + trace_imx_serial_put_data(chr ? chr->label : "NODEV", value); - s->usr1 |= USR1_RRDY; + imx_serial_rx_fifo_push(s, value); + if (fifo32_num_used(&s->rx_fifo) >= rxtl) { + s->usr1 |= USR1_RRDY; + } s->usr2 |= USR2_RDR; s->uts1 &= ~UTS1_RXEMPTY; - s->readbuff = value; if (value & URXD_BRK) { s->usr2 |= USR2_BRCD; } + + imx_serial_rx_fifo_ageing_timer_restart(s); + imx_update(s); } @@ -324,7 +418,10 @@ 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); + + for (int i = 0; i < size; i++) { + imx_put_data(opaque, buf[i]); + } } static void imx_event(void *opaque, QEMUChrEvent event) @@ -345,6 +442,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, @@ -362,18 +463,17 @@ static void imx_serial_init(Object *obj) sysbus_init_irq(sbd, &s->irq); } -static Property imx_serial_properties[] = { +static const Property imx_serial_properties[] = { DEFINE_PROP_CHR("chardev", IMXSerialState, chr), - DEFINE_PROP_END_OF_LIST(), }; -static void imx_serial_class_init(ObjectClass *klass, void *data) +static void imx_serial_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 3311e0872c2..752c6c818ab 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), @@ -184,9 +184,9 @@ static void update_irq(IPOctalState *dev, unsigned block) unsigned intno = block / 2; if ((blk0->isr & blk0->imr) || (blk1->isr & blk1->imr)) { - qemu_irq_raise(idev->irq[intno]); + qemu_irq_raise(&idev->irq[intno]); } else { - qemu_irq_lower(idev->irq[intno]); + qemu_irq_lower(&idev->irq[intno]); } } @@ -558,7 +558,7 @@ static void ipoctal_realize(DeviceState *dev, Error **errp) } } -static Property ipoctal_properties[] = { +static const Property ipoctal_properties[] = { DEFINE_PROP_CHR("chardev0", IPOctalState, ch[0].dev), DEFINE_PROP_CHR("chardev1", IPOctalState, ch[1].dev), DEFINE_PROP_CHR("chardev2", IPOctalState, ch[2].dev), @@ -567,10 +567,9 @@ static Property ipoctal_properties[] = { DEFINE_PROP_CHR("chardev5", IPOctalState, ch[5].dev), DEFINE_PROP_CHR("chardev6", IPOctalState, ch[6].dev), DEFINE_PROP_CHR("chardev7", IPOctalState, ch[7].dev), - DEFINE_PROP_END_OF_LIST(), }; -static void ipoctal_class_init(ObjectClass *klass, void *data) +static void ipoctal_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); IPackDeviceClass *ic = IPACK_DEVICE_CLASS(klass); diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index 6fa4ac502cb..87bfcbebdc5 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -17,6 +17,8 @@ #include "chardev/char-fe.h" #include "qom/object.h" +#define FIFO_DEPTH 4 + struct mcf_uart_state { SysBusDevice parent_obj; @@ -27,7 +29,7 @@ struct mcf_uart_state { uint8_t imr; uint8_t bg1; uint8_t bg2; - uint8_t fifo[4]; + uint8_t fifo[FIFO_DEPTH]; uint8_t tb; int current_mr; int fifo_len; @@ -247,14 +249,16 @@ static void mcf_uart_reset(DeviceState *dev) static void mcf_uart_push_byte(mcf_uart_state *s, uint8_t data) { /* Break events overwrite the last byte if the fifo is full. */ - if (s->fifo_len == 4) + if (s->fifo_len == FIFO_DEPTH) { s->fifo_len--; + } s->fifo[s->fifo_len] = data; s->fifo_len++; s->sr |= MCF_UART_RxRDY; - if (s->fifo_len == 4) + if (s->fifo_len == FIFO_DEPTH) { s->sr |= MCF_UART_FFULL; + } mcf_uart_update(s); } @@ -277,14 +281,16 @@ static int mcf_uart_can_receive(void *opaque) { mcf_uart_state *s = (mcf_uart_state *)opaque; - return s->rx_enabled && (s->sr & MCF_UART_FFULL) == 0; + return s->rx_enabled ? FIFO_DEPTH - s->fifo_len : 0; } static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size) { mcf_uart_state *s = (mcf_uart_state *)opaque; - mcf_uart_push_byte(s, buf[0]); + for (int i = 0; i < size; i++) { + mcf_uart_push_byte(s, buf[i]); + } } static const MemoryRegionOps mcf_uart_ops = { @@ -312,17 +318,16 @@ static void mcf_uart_realize(DeviceState *dev, Error **errp) mcf_uart_event, NULL, s, NULL, true); } -static Property mcf_uart_properties[] = { +static const Property mcf_uart_properties[] = { DEFINE_PROP_CHR("chardev", mcf_uart_state, chr), - DEFINE_PROP_END_OF_LIST(), }; -static void mcf_uart_class_init(ObjectClass *oc, void *data) +static void mcf_uart_class_init(ObjectClass *oc, const 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 +347,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 22f3e78eb9e..6149f9d2047 100644 --- a/hw/char/mchp_pfsoc_mmuart.c +++ b/hw/char/mchp_pfsoc_mmuart.c @@ -114,19 +114,19 @@ 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() } }; -static void mchp_pfsoc_mmuart_class_init(ObjectClass *oc, void *data) +static void mchp_pfsoc_mmuart_class_init(ObjectClass *oc, const 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 006d20f1e25..4e439da8b9e 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -1,25 +1,26 @@ system_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_uart.c')) system_ss.add(when: 'CONFIG_CMSDK_APB_UART', if_true: files('cmsdk-apb-uart.c')) system_ss.add(when: 'CONFIG_ESCC', if_true: files('escc.c')) -system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_ser.c')) system_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_apbuart.c')) system_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_uart.c')) system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_serial.c')) -system_ss.add(when: 'CONFIG_IPACK', if_true: files('ipoctal232.c')) +system_ss.add(when: 'CONFIG_IP_OCTAL_232', 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_PL011_C', 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')) +system_ss.add(when: 'CONFIG_DIVA_GSP', if_true: files('diva-gsp.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')) @@ -31,6 +32,7 @@ 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')) diff --git a/hw/char/nrf51_uart.c b/hw/char/nrf51_uart.c index dfe2276d711..41d423446f3 100644 --- a/hw/char/nrf51_uart.c +++ b/hw/char/nrf51_uart.c @@ -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), @@ -304,16 +304,15 @@ static const VMStateDescription nrf51_uart_vmstate = { } }; -static Property nrf51_uart_properties[] = { +static const Property nrf51_uart_properties[] = { DEFINE_PROP_CHR("chardev", NRF51UARTState, chr), - DEFINE_PROP_END_OF_LIST(), }; -static void nrf51_uart_class_init(ObjectClass *klass, void *data) +static void nrf51_uart_class_init(ObjectClass *klass, const 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 6848bddb4e2..8cbf6ce803e 100644 --- a/hw/char/omap_uart.c +++ b/hw/char/omap_uart.c @@ -20,15 +20,14 @@ #include "qemu/osdep.h" #include "chardev/char.h" #include "hw/arm/omap.h" -#include "hw/char/serial.h" -#include "exec/address-spaces.h" +#include "hw/char/serial-mm.h" +#include "system/address-spaces.h" /* UARTs */ 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,117 +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 = 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 = 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; -} diff --git a/hw/char/parallel-isa.c b/hw/char/parallel-isa.c index ab0f879998d..b6dfb6cc31a 100644 --- a/hw/char/parallel-isa.c +++ b/hw/char/parallel-isa.c @@ -10,7 +10,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/isa/isa.h" #include "hw/qdev-properties.h" #include "hw/char/parallel-isa.h" @@ -41,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 147c900f0d6..8732e4e9f96 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -33,8 +33,8 @@ #include "migration/vmstate.h" #include "hw/char/parallel-isa.h" #include "hw/char/parallel.h" -#include "sysemu/reset.h" -#include "sysemu/sysemu.h" +#include "system/reset.h" +#include "system/system.h" #include "trace.h" #include "qom/object.h" @@ -478,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), @@ -532,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]), @@ -603,15 +603,14 @@ bool parallel_mm_init(MemoryRegion *address_space, return true; } -static Property parallel_isa_properties[] = { +static const Property parallel_isa_properties[] = { DEFINE_PROP_UINT32("index", ISAParallelState, index, -1), DEFINE_PROP_UINT32("iobase", ISAParallelState, iobase, -1), DEFINE_PROP_UINT32("irq", ISAParallelState, isairq, 7), DEFINE_PROP_CHR("chardev", ISAParallelState, state.chr), - DEFINE_PROP_END_OF_LIST(), }; -static void parallel_isa_class_initfn(ObjectClass *klass, void *data) +static void parallel_isa_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); @@ -628,7 +627,7 @@ static const TypeInfo parallel_isa_info = { .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(ISAParallelState), .class_init = parallel_isa_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 58edeb9ddb6..01335d9437d 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -49,10 +49,14 @@ DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr) } /* 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) @@ -76,6 +80,22 @@ DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr) #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_RXE (1 << 9) +#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] = @@ -121,6 +141,11 @@ 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; @@ -132,41 +157,127 @@ static inline unsigned pl011_get_fifo_depth(PL011State *s) return pl011_is_fifo_enabled(s) ? PL011_FIFO_DEPTH : 1; } -static inline void pl011_reset_fifo(PL011State *s) +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 | PL011_FLAG_TXFF); - s->flags |= PL011_FLAG_RXFE | PL011_FLAG_TXFE; + 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, pipe_depth); + 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; + unsigned fifo_depth = pl011_get_fifo_depth(s); + + 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) & (fifo_depth - 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, fifo_depth); + 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--; - 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); - r = c; + r = pl011_read_rxdata(s); break; case 1: /* UARTRSR */ r = s->rsr; @@ -251,6 +362,54 @@ 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) { @@ -261,13 +420,8 @@ static void pl011_write(void *opaque, hwaddr 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 |= INT_TX; - pl011_update(s); + pl011_write_txdata(s, ch); break; case 1: /* UARTRSR/UARTECR */ s->rsr = 0; @@ -279,29 +433,32 @@ static void pl011_write(void *opaque, hwaddr offset, 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) & LCR_FEN) { - pl011_reset_fifo(s); + pl011_reset_rx_fifo(s); + pl011_reset_tx_fifo(s); } 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; @@ -330,44 +487,44 @@ static void pl011_write(void *opaque, hwaddr offset, static int pl011_can_receive(void *opaque) { PL011State *s = (PL011State *)opaque; - int r; - - r = s->read_count < pl011_get_fifo_depth(s); - trace_pl011_can_receive(s->lcr, s->read_count, r); - return r; + unsigned fifo_depth = pl011_get_fifo_depth(s); + unsigned fifo_available = fifo_depth - s->read_count; + + /* + * In theory we should check the UART and RX enable bits here and + * return 0 if they are not set (so the guest can't receive data + * until you have enabled the UART). In practice we suspect there + * is at least some guest code out there which has been tested only + * on QEMU and which never bothers to enable the UART because we + * historically never enforced that. So we effectively keep the + * UART continuously enabled regardless of the enable bits. + */ + + trace_pl011_can_receive(s->lcr, s->read_count, fifo_depth, fifo_available); + return fifo_available; } -static void pl011_put_fifo(void *opaque, uint32_t value) +static void pl011_receive(void *opaque, const uint8_t *buf, int size) { - 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_put_fifo(value, s->read_count); - if (s->read_count == pipe_depth) { - trace_pl011_put_fifo_full(); - s->flags |= PL011_FLAG_RXFF; - } - if (s->read_count == s->read_trigger) { - s->int_level |= INT_RX; - pl011_update(s); + trace_pl011_receive(size); + /* + * In loopback mode, the RX input signal is internally disconnected + * from the entire receiving logics; thus, all inputs are ignored, + * and BREAK detection on RX input signal is also not performed. + */ + if (pl011_loopback_enabled(opaque)) { + return; } -} -static void pl011_receive(void *opaque, const uint8_t *buf, int size) -{ - pl011_put_fifo(opaque, *buf); + for (int i = 0; i < size; i++) { + pl011_fifo_rx_put(opaque, buf[i]); + } } static void pl011_event(void *opaque, QEMUChrEvent event) { - if (event == CHR_EVENT_BREAK) { - pl011_put_fifo(opaque, DR_BE); + if (event == CHR_EVENT_BREAK && !pl011_loopback_enabled(opaque)) { + pl011_fifo_rx_put(opaque, DR_BE); } } @@ -398,7 +555,7 @@ 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() } @@ -425,6 +582,9 @@ static int pl011_post_load(void *opaque, int version_id) s->read_pos = 0; } + s->ibrd &= IBRD_MASK; + s->fbrd &= FBRD_MASK; + return 0; } @@ -433,8 +593,8 @@ static const VMStateDescription vmstate_pl011 = { .version_id = 2, .minimum_version_id = 2, .post_load = pl011_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(readbuff, PL011State), + .fields = (const VMStateField[]) { + VMSTATE_UNUSED(sizeof(uint32_t)), VMSTATE_UINT32(flags, PL011State), VMSTATE_UINT32(lcr, PL011State), VMSTATE_UINT32(rsr, PL011State), @@ -452,16 +612,15 @@ 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 } }; -static Property pl011_properties[] = { +static const Property pl011_properties[] = { DEFINE_PROP_CHR("chardev", PL011State, chr), DEFINE_PROP_BOOL("migrate-clk", PL011State, migrate_clk, true), - DEFINE_PROP_END_OF_LIST(), }; static void pl011_init(Object *obj) @@ -506,15 +665,16 @@ static void pl011_reset(DeviceState *dev) s->ifl = 0x12; s->cr = 0x300; s->flags = 0; - pl011_reset_fifo(s); + pl011_reset_rx_fifo(s); + pl011_reset_tx_fifo(s); } -static void pl011_class_init(ObjectClass *oc, void *data) +static void pl011_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = pl011_realize; - dc->reset = pl011_reset; + 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 1c634672905..b9d0ed1c89b 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), @@ -319,19 +319,18 @@ static const VMStateDescription vmstate_rsci = { } }; -static Property rsci_properties[] = { +static const Property rsci_properties[] = { DEFINE_PROP_UINT64("input-freq", RSCIState, input_freq, 0), DEFINE_PROP_CHR("chardev", RSCIState, chr), - DEFINE_PROP_END_OF_LIST(), }; -static void rsci_class_init(ObjectClass *klass, void *data) +static void rsci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 40de6b8b775..c884be5d757 100644 --- a/hw/char/riscv_htif.c +++ b/hw/char/riscv_htif.c @@ -24,22 +24,15 @@ #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 "system/address-spaces.h" #include "exec/tswap.h" -#include "sysemu/dma.h" - -#define RISCV_DEBUG_HTIF 0 -#define HTIF_DEBUG(fmt, ...) \ - do { \ - if (RISCV_DEBUG_HTIF) { \ - qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\ - } \ - } while (0) +#include "system/dma.h" +#include "system/runstate.h" +#include "trace.h" #define HTIF_DEV_SHIFT 56 #define HTIF_CMD_SHIFT 48 @@ -159,8 +152,7 @@ static void htif_handle_tohost_write(HTIFState *s, uint64_t val_written) uint64_t payload = val_written & 0xFFFFFFFFFFFFULL; int resp = 0; - HTIF_DEBUG("mtohost write: device: %d cmd: %d what: %02" PRIx64 - " -payload: %016" PRIx64 "\n", device, cmd, payload & 0xFF, payload); + trace_htif_uart_write_to_host(device, cmd, payload); /* * Currently, there is a fixed mapping of devices: @@ -206,16 +198,22 @@ static void htif_handle_tohost_write(HTIFState *s, uint64_t val_written) g_free(sig_data); } - exit(exit_code); + qemu_system_shutdown_request_with_code( + SHUTDOWN_CAUSE_GUEST_SHUTDOWN, exit_code); + return; } else { 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) { + if (le64_to_cpu(syscall[0]) == PK_SYS_WRITE && + le64_to_cpu(syscall[1]) == HTIF_DEV_CONSOLE && + le64_to_cpu(syscall[3]) == HTIF_CONSOLE_CMD_PUTC) { uint8_t ch; - cpu_physical_memory_read(tswap64(syscall[2]), &ch, 1); - qemu_chr_fe_write(&s->chr, &ch, 1); + cpu_physical_memory_read(le64_to_cpu(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, @@ -234,15 +232,18 @@ static void htif_handle_tohost_write(HTIFState *s, uint64_t val_written) return; } else if (cmd == HTIF_CONSOLE_CMD_PUTC) { uint8_t ch = (uint8_t)payload; - qemu_chr_fe_write(&s->chr, &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("HTIF device %d: unknown command\n", device); } } else { qemu_log("HTIF unknown device or command\n"); - HTIF_DEBUG("device: %d cmd: %d what: %02" PRIx64 - " payload: %016" PRIx64, device, cmd, payload & 0xFF, payload); + trace_htif_uart_unknown_device_command(device, cmd, payload); } /* * Latest bbl does not set fromhost to 0 if there is a value in tohost. @@ -314,6 +315,11 @@ static void htif_mm_write(void *opaque, hwaddr addr, static const MemoryRegionOps htif_mm_ops = { .read = htif_mm_read, .write = htif_mm_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, }; HTIFState *htif_mm_init(MemoryRegion *address_space, Chardev *chr, diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index b9e9b2d4535..3e40d5e4342 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -214,7 +214,7 @@ static int process_mdb(SCLPEvent *event, MDBO *mdbo) { int rc; int len; - uint8_t buffer[SIZE_BUFFER]; + QEMU_UNINITIALIZED uint8_t buffer[SIZE_BUFFER]; len = be16_to_cpu(mdbo->length); len -= sizeof(mdbo->length) + sizeof(mdbo->type) @@ -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), @@ -333,20 +333,19 @@ static void console_reset(DeviceState *dev) scon->write_errors = 0; } -static Property console_properties[] = { +static const Property console_properties[] = { DEFINE_PROP_CHR("chardev", SCLPConsoleLM, chr), DEFINE_PROP_UINT32("write_errors", SCLPConsoleLM, write_errors, 0), DEFINE_PROP_BOOL("echo", SCLPConsoleLM, echo, true), - DEFINE_PROP_END_OF_LIST(), }; -static void console_class_init(ObjectClass *klass, void *data) +static void console_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 c36b5722224..95e3045178e 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), @@ -251,18 +251,17 @@ static void console_reset(DeviceState *dev) scon->notify = false; } -static Property console_properties[] = { +static const Property console_properties[] = { DEFINE_PROP_CHR("chardev", SCLPConsole, chr), - DEFINE_PROP_END_OF_LIST(), }; -static void console_class_init(ObjectClass *klass, void *data) +static void console_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 141a6cb1684..0ea59a3d5c2 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -26,9 +26,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" -#include "sysemu/sysemu.h" +#include "system/system.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,20 +107,19 @@ 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() } }; -static Property serial_isa_properties[] = { +static const Property serial_isa_properties[] = { DEFINE_PROP_UINT32("index", ISASerialState, index, -1), DEFINE_PROP_UINT32("iobase", ISASerialState, iobase, -1), DEFINE_PROP_UINT32("irq", ISASerialState, isairq, -1), - DEFINE_PROP_END_OF_LIST(), }; -static void serial_isa_class_initfn(ObjectClass *klass, void *data) +static void serial_isa_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); @@ -146,7 +146,7 @@ static const TypeInfo serial_isa_info = { .instance_size = sizeof(ISASerialState), .instance_init = serial_isa_initfn, .class_init = serial_isa_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, @@ -184,3 +184,17 @@ void serial_hds_isa_init(ISABus *bus, int from, int to) } } } + +void isa_serial_set_iobase(ISADevice *serial, hwaddr iobase) +{ + ISASerialState *s = ISA_SERIAL(serial); + + serial->ioport_id = iobase; + s->iobase = iobase; + memory_region_set_address(&s->state.io, s->iobase); +} + +void isa_serial_set_enabled(ISADevice *serial, bool enabled) +{ + memory_region_set_enabled(&ISA_SERIAL(serial)->state.io, enabled); +} diff --git a/hw/char/serial-mm.c b/hw/char/serial-mm.c new file mode 100644 index 00000000000..13aba780ec5 --- /dev/null +++ b/hw/char/serial-mm.c @@ -0,0 +1,156 @@ +/* + * 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 const 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), +}; + +static void serial_mm_class_init(ObjectClass *oc, const 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 5d65c534cb5..13df272691a 100644 --- a/hw/char/serial-pci-multi.c +++ b/hw/char/serial-pci-multi.c @@ -45,8 +45,7 @@ typedef struct PCIMultiSerialState { char *name[PCI_SERIAL_MAX_PORTS]; SerialState state[PCI_SERIAL_MAX_PORTS]; uint32_t level[PCI_SERIAL_MAX_PORTS]; - qemu_irq *irqs; - uint8_t prog_if; + IRQState irqs[PCI_SERIAL_MAX_PORTS]; } PCIMultiSerialState; static void multi_serial_pci_exit(PCIDevice *dev) @@ -61,7 +60,6 @@ static void multi_serial_pci_exit(PCIDevice *dev) memory_region_del_subregion(&pci->iobar, &s->io); g_free(pci->name[i]); } - qemu_free_irqs(pci->irqs, pci->ports); } static void multi_serial_irq_mux(void *opaque, int n, int level) @@ -98,11 +96,10 @@ static void multi_serial_pci_realize(PCIDevice *dev, Error **errp) SerialState *s; size_t i, nports = multi_serial_get_port_count(pc); - pci->dev.config[PCI_CLASS_PROG] = pci->prog_if; - pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; + pci->dev.config[PCI_CLASS_PROG] = 2; /* 16550 compatible */ + pci->dev.config[PCI_INTERRUPT_PIN] = 1; memory_region_init(&pci->iobar, OBJECT(pci), "multiserial", 8 * nports); pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->iobar); - pci->irqs = qemu_allocate_irqs(multi_serial_irq_mux, pci, nports); for (i = 0; i < nports; i++) { s = pci->state + i; @@ -110,7 +107,7 @@ static void multi_serial_pci_realize(PCIDevice *dev, Error **errp) multi_serial_pci_exit(dev); return; } - s->irq = pci->irqs[i]; + s->irq = &pci->irqs[i]; pci->name[i] = g_strdup_printf("uart #%zu", i + 1); memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s, pci->name[i], 8); @@ -123,7 +120,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), @@ -132,23 +129,20 @@ static const VMStateDescription vmstate_pci_multi_serial = { } }; -static Property multi_2x_serial_pci_properties[] = { +static const Property multi_2x_serial_pci_properties[] = { DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].chr), DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr), - DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02), - DEFINE_PROP_END_OF_LIST(), }; -static Property multi_4x_serial_pci_properties[] = { +static const Property multi_4x_serial_pci_properties[] = { DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].chr), DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr), DEFINE_PROP_CHR("chardev3", PCIMultiSerialState, state[2].chr), DEFINE_PROP_CHR("chardev4", PCIMultiSerialState, state[3].chr), - DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02), - DEFINE_PROP_END_OF_LIST(), }; -static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, void *data) +static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); @@ -163,7 +157,8 @@ static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } -static void multi_4x_serial_pci_class_initfn(ObjectClass *klass, void *data) +static void multi_4x_serial_pci_class_initfn(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); @@ -185,6 +180,7 @@ static void multi_serial_init(Object *o) size_t i, nports = multi_serial_get_port_count(PCI_DEVICE_GET_CLASS(dev)); for (i = 0; i < nports; i++) { + qemu_init_irq(&pms->irqs[i], multi_serial_irq_mux, pms, i); object_initialize_child(o, "serial[*]", &pms->state[i], TYPE_SERIAL); } } @@ -195,7 +191,7 @@ static const TypeInfo multi_2x_serial_pci_info = { .instance_size = sizeof(PCIMultiSerialState), .instance_init = multi_serial_init, .class_init = multi_2x_serial_pci_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -207,7 +203,7 @@ static const TypeInfo multi_4x_serial_pci_info = { .instance_size = sizeof(PCIMultiSerialState), .instance_init = multi_serial_init, .class_init = multi_4x_serial_pci_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c index 087da3059a4..46efabc4cbe 100644 --- a/hw/char/serial-pci.c +++ b/hw/char/serial-pci.c @@ -38,7 +38,6 @@ struct PCISerialState { PCIDevice dev; SerialState state; - uint8_t prog_if; }; #define TYPE_PCI_SERIAL "pci-serial" @@ -53,8 +52,8 @@ static void serial_pci_realize(PCIDevice *dev, Error **errp) return; } - pci->dev.config[PCI_CLASS_PROG] = pci->prog_if; - pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; + pci->dev.config[PCI_CLASS_PROG] = 2; /* 16550 compatible */ + pci->dev.config[PCI_INTERRUPT_PIN] = 1; s->irq = pci_allocate_irq(&pci->dev); memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s, "serial", 8); @@ -74,19 +73,14 @@ 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() } }; -static Property serial_pci_properties[] = { - DEFINE_PROP_UINT8("prog_if", PCISerialState, prog_if, 0x02), - DEFINE_PROP_END_OF_LIST(), -}; - -static void serial_pci_class_initfn(ObjectClass *klass, void *data) +static void serial_pci_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); @@ -97,7 +91,6 @@ static void serial_pci_class_initfn(ObjectClass *klass, void *data) pc->revision = 1; pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL; dc->vmsd = &vmstate_pci_serial; - device_class_set_props(dc, serial_pci_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } @@ -116,7 +109,7 @@ static const TypeInfo serial_pci_info = { .instance_size = sizeof(PCISerialState), .instance_init = serial_pci_init, .class_init = serial_pci_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/char/serial.c b/hw/char/serial.c index f3094f860ff..03fec3fe75b 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -31,8 +31,8 @@ #include "chardev/char-serial.h" #include "qapi/error.h" #include "qemu/timer.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/reset.h" +#include "system/runstate.h" #include "qemu/error-report.h" #include "trace.h" #include "hw/qdev-properties.h" @@ -54,7 +54,7 @@ #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 */ /* @@ -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, @@ -971,14 +964,13 @@ const MemoryRegionOps serial_io_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static Property serial_properties[] = { +static const Property serial_properties[] = { DEFINE_PROP_CHR("chardev", SerialState, chr), DEFINE_PROP_UINT32("baudbase", SerialState, baudbase, 115200), DEFINE_PROP_BOOL("wakeup", SerialState, wakeup, false), - DEFINE_PROP_END_OF_LIST(), }; -static void serial_class_init(ObjectClass *klass, void* data) +static void serial_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -996,135 +988,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 355886ee3a1..30447fa018a 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -78,10 +78,6 @@ struct SHSerialState { qemu_irq bri; }; -typedef struct {} SHSerialStateClass; - -OBJECT_DEFINE_TYPE(SHSerialState, sh_serial, SH_SERIAL, SYS_BUS_DEVICE) - static void sh_serial_clear_fifo(SHSerialState *s) { memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH); @@ -320,7 +316,7 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs, static int sh_serial_can_receive(SHSerialState *s) { - return s->scr & (1 << 4); + return s->scr & (1 << 4) ? SH_RX_FIFO_LENGTH - s->rx_head : 0; } static void sh_serial_receive_break(SHSerialState *s) @@ -353,22 +349,20 @@ static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) if (s->feat & SH_SERIAL_FEAT_SCIF) { int i; for (i = 0; i < size; i++) { - if (s->rx_cnt < SH_RX_FIFO_LENGTH) { - s->rx_fifo[s->rx_head++] = buf[i]; - if (s->rx_head == SH_RX_FIFO_LENGTH) { - s->rx_head = 0; - } - s->rx_cnt++; - if (s->rx_cnt >= s->rtrg) { - s->flags |= SH_SERIAL_FLAG_RDF; - if (s->scr & (1 << 6) && s->rxi) { - timer_del(&s->fifo_timeout_timer); - qemu_set_irq(s->rxi, 1); - } - } else { - timer_mod(&s->fifo_timeout_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 15 * s->etu); + s->rx_fifo[s->rx_head++] = buf[i]; + if (s->rx_head == SH_RX_FIFO_LENGTH) { + s->rx_head = 0; + } + s->rx_cnt++; + if (s->rx_cnt >= s->rtrg) { + s->flags |= SH_SERIAL_FLAG_RDF; + if (s->scr & (1 << 6) && s->rxi) { + timer_del(&s->fifo_timeout_timer); + qemu_set_irq(s->rxi, 1); } + } else { + timer_mod(&s->fifo_timeout_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 15 * s->etu); } } } else { @@ -436,30 +430,37 @@ static void sh_serial_realize(DeviceState *d, Error **errp) s->etu = NANOSECONDS_PER_SECOND / 9600; } -static void sh_serial_finalize(Object *obj) +static void sh_serial_unrealize(DeviceState *dev) { - SHSerialState *s = SH_SERIAL(obj); + SHSerialState *s = SH_SERIAL(dev); timer_del(&s->fifo_timeout_timer); } -static void sh_serial_init(Object *obj) -{ -} - -static Property sh_serial_properties[] = { +static const Property sh_serial_properties[] = { DEFINE_PROP_CHR("chardev", SHSerialState, chr), DEFINE_PROP_UINT8("features", SHSerialState, feat, 0), - DEFINE_PROP_END_OF_LIST() }; -static void sh_serial_class_init(ObjectClass *oc, void *data) +static void sh_serial_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); device_class_set_props(dc, sh_serial_properties); dc->realize = sh_serial_realize; - dc->reset = sh_serial_reset; + dc->unrealize = sh_serial_unrealize; + device_class_set_legacy_reset(dc, sh_serial_reset); /* Reason: part of SuperH CPU/SoC, needs to be wired up */ dc->user_creatable = false; } + +static const TypeInfo sh_serial_types[] = { + { + .name = TYPE_SH_SERIAL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SHSerialState), + .class_init = sh_serial_class_init, + }, +}; + +DEFINE_TYPES(sh_serial_types) diff --git a/hw/char/shakti_uart.c b/hw/char/shakti_uart.c index 98b142c7df8..6e216edb0fc 100644 --- a/hw/char/shakti_uart.c +++ b/hw/char/shakti_uart.c @@ -157,15 +157,14 @@ static void shakti_uart_instance_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &sus->mmio); } -static Property shakti_uart_properties[] = { +static const Property shakti_uart_properties[] = { DEFINE_PROP_CHR("chardev", ShaktiUartState, chr), - DEFINE_PROP_END_OF_LIST(), }; -static void shakti_uart_class_init(ObjectClass *klass, void *data) +static void shakti_uart_class_init(ObjectClass *klass, const 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 f2684e57bcc..0fc89e76d1f 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, @@ -177,9 +251,25 @@ static int sifive_uart_be_change(void *opaque) return 0; } -static Property sifive_uart_properties[] = { +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_reset(&s->tx_fifo); +} + +static const Property sifive_uart_properties[] = { DEFINE_PROP_CHR("chardev", SiFiveUARTState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void sifive_uart_init(Object *obj) @@ -197,24 +287,27 @@ static void sifive_uart_realize(DeviceState *dev, Error **errp) { SiFiveUARTState *s = SIFIVE_UART(dev); - qemu_chr_fe_set_handlers(&s->chr, sifive_uart_can_rx, sifive_uart_rx, - sifive_uart_event, sifive_uart_be_change, s, - NULL, true); + fifo8_create(&s->tx_fifo, SIFIVE_UART_TX_FIFO_SIZE); + + s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL, + fifo_trigger_update, s); + + if (qemu_chr_fe_backend_connected(&s->chr)) { + qemu_chr_fe_set_handlers(&s->chr, sifive_uart_can_rx, sifive_uart_rx, + sifive_uart_event, sifive_uart_be_change, s, + NULL, true); + } } -static void sifive_uart_reset_enter(Object *obj, ResetType type) +static void sifive_uart_unrealize(DeviceState *dev) { - SiFiveUARTState *s = SIFIVE_UART(obj); - s->ie = 0; - s->ip = 0; - s->txctrl = 0; - s->rxctrl = 0; - s->div = 0; - s->rx_fifo_len = 0; + SiFiveUARTState *s = SIFIVE_UART(dev); + + fifo8_destroy(&s->tx_fifo); } -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 +315,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,17 +326,21 @@ 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() }, }; -static void sifive_uart_class_init(ObjectClass *oc, void *data) +static void sifive_uart_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); ResettableClass *rc = RESETTABLE_CLASS(oc); dc->realize = sifive_uart_realize; + dc->unrealize = sifive_uart_unrealize; dc->vmsd = &vmstate_sifive_uart; rc->phases.enter = sifive_uart_reset_enter; rc->phases.hold = sifive_uart_reset_hold; diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index 91eae1a5988..fc8ea604f8d 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -163,17 +163,16 @@ void spapr_vty_create(SpaprVioBus *bus, Chardev *chardev) qdev_realize_and_unref(dev, &bus->bus, &error_fatal); } -static Property spapr_vty_properties[] = { +static const Property spapr_vty_properties[] = { DEFINE_SPAPR_PROPERTIES(SpaprVioVty, sdev), DEFINE_PROP_CHR("chardev", SpaprVioVty, chardev), - DEFINE_PROP_END_OF_LIST(), }; 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), @@ -183,7 +182,7 @@ static const VMStateDescription vmstate_spapr_vty = { }, }; -static void spapr_vty_class_init(ObjectClass *klass, void *data) +static void spapr_vty_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index fde67f4f03f..45c30643a74 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -30,17 +30,7 @@ #include "qemu/log.h" #include "qemu/module.h" -#ifndef STM_USART_ERR_DEBUG -#define STM_USART_ERR_DEBUG 0 -#endif - -#define DB_PRINT_L(lvl, fmt, args...) do { \ - if (STM_USART_ERR_DEBUG >= lvl) { \ - qemu_log("%s: " fmt, __func__, ## args); \ - } \ -} while (0) - -#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) +#include "trace.h" static int stm32f2xx_usart_can_receive(void *opaque) { @@ -53,24 +43,34 @@ 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; + DeviceState *d = DEVICE(s); if (!(s->usart_cr1 & USART_CR1_UE && s->usart_cr1 & USART_CR1_RE)) { /* USART not enabled - drop the chars */ - DB_PRINT("Dropping the chars\n"); + trace_stm32f2xx_usart_drop(d->id); return; } 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); + trace_stm32f2xx_usart_receive(d->id, *buf); } static void stm32f2xx_usart_reset(DeviceState *dev) @@ -85,56 +85,62 @@ 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, unsigned int size) { STM32F2XXUsartState *s = opaque; - uint64_t retvalue; - - DB_PRINT("Read 0x%"HWADDR_PRIx"\n", addr); + DeviceState *d = DEVICE(s); + uint64_t retvalue = 0; switch (addr) { case USART_SR: retvalue = s->usart_sr; qemu_chr_fe_accept_input(&s->chr); - return retvalue; + break; case USART_DR: - DB_PRINT("Value: 0x%" PRIx32 ", %c\n", s->usart_dr, (char) s->usart_dr); retvalue = s->usart_dr & 0x3FF; s->usart_sr &= ~USART_SR_RXNE; qemu_chr_fe_accept_input(&s->chr); - qemu_set_irq(s->irq, 0); - return retvalue; + stm32f2xx_update_irq(s); + break; case USART_BRR: - return s->usart_brr; + retvalue = s->usart_brr; + break; case USART_CR1: - return s->usart_cr1; + retvalue = s->usart_cr1; + break; case USART_CR2: - return s->usart_cr2; + retvalue = s->usart_cr2; + break; case USART_CR3: - return s->usart_cr3; + retvalue = s->usart_cr3; + break; case USART_GTPR: - return s->usart_gtpr; + retvalue = s->usart_gtpr; + break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); return 0; } - return 0; + trace_stm32f2xx_usart_read(d->id, size, addr, retvalue); + + return retvalue; } static void stm32f2xx_usart_write(void *opaque, hwaddr addr, uint64_t val64, unsigned int size) { STM32F2XXUsartState *s = opaque; + DeviceState *d = DEVICE(s); uint32_t value = val64; unsigned char ch; - DB_PRINT("Write 0x%" PRIx32 ", 0x%"HWADDR_PRIx"\n", value, addr); + trace_stm32f2xx_usart_write(d->id, size, addr, val64); switch (addr) { case USART_SR: @@ -145,9 +151,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 +165,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 +173,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; @@ -194,9 +196,8 @@ static const MemoryRegionOps stm32f2xx_usart_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static Property stm32f2xx_usart_properties[] = { +static const Property stm32f2xx_usart_properties[] = { DEFINE_PROP_CHR("chardev", STM32F2XXUsartState, chr), - DEFINE_PROP_END_OF_LIST(), }; static void stm32f2xx_usart_init(Object *obj) @@ -219,11 +220,11 @@ static void stm32f2xx_usart_realize(DeviceState *dev, Error **errp) s, NULL, true); } -static void stm32f2xx_usart_class_init(ObjectClass *klass, void *data) +static void stm32f2xx_usart_class_init(ObjectClass *klass, const 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 00000000000..afbe4bab29d --- /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 const Property stm32l4x5_usart_base_properties[] = { + DEFINE_PROP_CHR("chardev", Stm32l4x5UsartBaseState, chr), +}; + +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, + const 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, const void *data) +{ + Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); + + subc->type = STM32L4x5_USART; +} + +static void stm32l4x5_uart_class_init(ObjectClass *oc, const void *data) +{ + Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); + + subc->type = STM32L4x5_UART; +} + +static void stm32l4x5_lpuart_class_init(ObjectClass *oc, const 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/terminal3270.c b/hw/char/terminal3270.c index 82e85fac2e6..d950c172921 100644 --- a/hw/char/terminal3270.c +++ b/hw/char/terminal3270.c @@ -283,9 +283,8 @@ static int write_payload_3270(EmulatedCcw3270Device *dev, uint8_t cmd) return (retval <= 0) ? 0 : get_cds(t)->count; } -static Property terminal_properties[] = { +static const Property terminal_properties[] = { DEFINE_PROP_CHR("chardev", Terminal3270, chr), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription terminal3270_vmstate = { @@ -293,7 +292,7 @@ static const VMStateDescription terminal3270_vmstate = { .unmigratable = 1, }; -static void terminal_class_init(ObjectClass *klass, void *data) +static void terminal_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); EmulatedCcw3270Class *ck = EMULATED_CCW_3270_CLASS(klass); diff --git a/hw/char/trace-events b/hw/char/trace-events index babf4d35eae..05a33036c12 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -52,15 +52,21 @@ escc_sunkbd_event_out(int ch) "Translated keycode 0x%2.2x" escc_kbd_command(int val) "Command %d" escc_sunmouse_event(int dx, int dy, int buttons_state) "dx=%d dy=%d buttons=0x%01x" +# imx_serial.c +imx_serial_read(const char *chrname, uint64_t addr, uint64_t value) "%s:[0x%03" PRIu64 "] -> 0x%08" PRIx64 +imx_serial_write(const char *chrname, uint64_t addr, uint64_t value) "%s:[0x%03" PRIu64 "] <- 0x%08" PRIx64 +imx_serial_put_data(const char *chrname, uint32_t value) "%s: 0x%" PRIx32 + # pl011.c pl011_irq_state(int level) "irq state %d" 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_read_fifo(unsigned rx_fifo_used, size_t rx_fifo_depth) "RX FIFO read, used %u/%zu" 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_can_receive(uint32_t lcr, unsigned rx_fifo_used, size_t rx_fifo_depth, unsigned rx_fifo_available) "LCR 0x%02x, RX FIFO used %u/%zu, can_receive %u chars" +pl011_fifo_rx_put(uint32_t c, unsigned read_count, size_t rx_fifo_depth) "RX FIFO push char [0x%02x] %d/%zu depth used" +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 ")" +pl011_receive(int size) "recv %d chars" # cmsdk-apb-uart.c cmsdk_apb_uart_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" @@ -105,3 +111,33 @@ 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" + +# stm32f2xx_usart.c +stm32f2xx_usart_read(char *id, unsigned size, uint64_t ofs, uint64_t val) " %s size %d ofs 0x%02" PRIx64 " -> 0x%02" PRIx64 +stm32f2xx_usart_write(char *id, unsigned size, uint64_t ofs, uint64_t val) "%s size %d ofs 0x%02" PRIx64 " <- 0x%02" PRIx64 +stm32f2xx_usart_drop(char *id) " %s dropping the chars" +stm32f2xx_usart_receive(char *id, uint8_t chr) " %s receiving '%c'" + +# riscv_htif.c +htif_uart_write_to_host(uint8_t device, uint8_t cmd, uint64_t payload) "device: %u cmd: %02u payload: %016" PRIx64 +htif_uart_unknown_device_command(uint8_t device, uint8_t cmd, uint64_t payload) "device: %u cmd: %02u payload: %016" PRIx64 diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c index dbe0b28e601..0932a3572b7 100644 --- a/hw/char/virtio-console.c +++ b/hw/char/virtio-console.c @@ -261,7 +261,7 @@ static void virtconsole_unrealize(DeviceState *dev) } } -static void virtconsole_class_init(ObjectClass *klass, void *data) +static void virtconsole_class_init(ObjectClass *klass, const void *data) { VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass); @@ -274,12 +274,11 @@ static const TypeInfo virtconsole_info = { .class_init = virtconsole_class_init, }; -static Property virtserialport_properties[] = { +static const Property virtserialport_properties[] = { DEFINE_PROP_CHR("chardev", VirtConsole, chr), - DEFINE_PROP_END_OF_LIST(), }; -static void virtserialport_class_init(ObjectClass *klass, void *data) +static void virtserialport_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass); diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index dd619f0731e..673c50f0be0 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -622,7 +622,7 @@ static void guest_reset(VirtIOSerial *vser) } } -static void set_status(VirtIODevice *vdev, uint8_t status) +static int set_status(VirtIODevice *vdev, uint8_t status) { VirtIOSerial *vser; VirtIOSerialPort *port; @@ -650,6 +650,7 @@ static void set_status(VirtIODevice *vdev, uint8_t status) vsc->enable_backend(port, vdev->vm_running); } } + return 0; } static void vser_reset(VirtIODevice *vdev) @@ -835,13 +836,12 @@ static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f, static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); -static Property virtser_props[] = { +static const Property virtser_props[] = { DEFINE_PROP_UINT32("nr", VirtIOSerialPort, id, VIRTIO_CONSOLE_BAD_ID), DEFINE_PROP_STRING("name", VirtIOSerialPort, name), - DEFINE_PROP_END_OF_LIST() }; -static void virtser_bus_class_init(ObjectClass *klass, void *data) +static void virtser_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); k->print_dev = virtser_bus_dev_print; @@ -985,8 +985,7 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp) return; } - port->bh = qemu_bh_new_guarded(flush_queued_data_bh, port, - &dev->mem_reentrancy_guard); + port->bh = virtio_bh_new_guarded(dev, flush_queued_data_bh, port); port->elem = NULL; } @@ -1094,7 +1093,7 @@ static void virtio_serial_device_realize(DeviceState *dev, Error **errp) QLIST_INSERT_HEAD(&vserdevices.devices, vser, next); } -static void virtio_serial_port_class_init(ObjectClass *klass, void *data) +static void virtio_serial_port_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); @@ -1148,21 +1147,20 @@ 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() }, }; -static Property virtio_serial_properties[] = { +static const Property virtio_serial_properties[] = { DEFINE_PROP_UINT32("max_ports", VirtIOSerial, serial.max_virtserial_ports, 31), DEFINE_PROP_BIT64("emergency-write", VirtIOSerial, host_features, VIRTIO_CONSOLE_F_EMERG_WRITE, true), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_serial_class_init(ObjectClass *klass, void *data) +static void virtio_serial_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); @@ -1191,7 +1189,7 @@ static const TypeInfo virtio_device_info = { .parent = TYPE_VIRTIO_DEVICE, .instance_size = sizeof(VirtIOSerial), .class_init = virtio_serial_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index 810dae3f44e..9c34a554bfa 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 "system/system.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; + +#define TYPE_XEN_CONSOLE_DEVICE "xen-console" +OBJECT_DECLARE_SIMPLE_TYPE(XenConsole, XEN_CONSOLE_DEVICE) -static void buffer_append(struct XenConsole *con) +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,174 +173,482 @@ 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 store_con_info(struct XenConsole *con) +static bool con_event(void *_xendev) { - Chardev *cs = qemu_chr_fe_get_driver(&con->chr); - char *pts = NULL; - char *dom_path; - g_autoptr(GString) path = NULL; + XenConsole *con = XEN_CONSOLE_DEVICE(_xendev); + bool done_something; - /* Only continue if we're talking to a pty. */ - if (!CHARDEV_IS_PTY(cs)) { - return 0; + if (xen_device_backend_get_state(&con->xendev) != XenbusStateConnected) { + return false; } - pts = cs->filename + 4; - dom_path = qemu_xen_xs_get_domain_path(xenstore, xen_domid); - if (!dom_path) { - return 0; + done_something = buffer_append(con); + + if (con->buffer.size - con->buffer.consumed) { + done_something |= xencons_send(con); } + return done_something; +} - path = g_string_new(dom_path); - free(dom_path); +/* -------------------------------------------------------------------- */ - if (con->xendev.dev) { - g_string_append_printf(path, "/device/console/%d", con->xendev.dev); - } else { - g_string_append(path, "/console"); +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; } - g_string_append(path, "/tty"); - if (xenstore_write_str(con->console, path->str, pts)) { - fprintf(stderr, "xenstore_write_str for '%s' fail", path->str); - return -1; + con->event_channel = xen_device_bind_event_channel(xendev, port, + con_event, + con, + errp); + if (!con->event_channel) { + return false; } - return 0; + + 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); + return true; } -static int con_init(struct XenLegacyDevice *xendev) +static void xen_console_disconnect(XenDevice *xendev, Error **errp) { - struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - char *type, *dom, label[32]; - int ret = 0; - const char *output; - - /* setup */ - dom = qemu_xen_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); + XenConsole *con = XEN_CONSOLE_DEVICE(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); + } } - 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 (con->sring) { + if (!con->dev && xen_mode != XEN_EMULATE) { + qemu_xen_foreignmem_unmap(con->sring, 1); + } else { + xen_device_unmap_grant_refs(xendev, con->sring, + &con->ring_ref, 1, errp); + } + con->sring = NULL; } +} + +static void xen_console_frontend_changed(XenDevice *xendev, + enum xenbus_state frontend_state, + Error **errp) +{ + ERRP_GUARD(); + enum xenbus_state backend_state = xen_device_backend_get_state(xendev); + + switch (frontend_state) { + case XenbusStateInitialised: + case XenbusStateConnected: + if (backend_state == XenbusStateConnected) { + break; + } - output = xenstore_read_str(con->console, "output"); + xen_console_disconnect(xendev, errp); + if (*errp) { + break; + } - /* 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); + if (!xen_console_connect(xendev, errp)) { + xen_device_backend_set_state(xendev, XenbusStateClosing); + break; } - } 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); + + 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); + + if (con->dev == -1) { + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + int idx = (xen_mode == XEN_EMULATE) ? 0 : 1; + Error *local_err = NULL; + char *value; + + /* Theoretically we could go up to INT_MAX here but that's overkill */ + while (idx < 100) { + if (!idx) { + value = xs_node_read(xenbus->xsh, XBT_NULL, NULL, &local_err, + "/local/domain/%u/console", + xendev->frontend_id); + } else { + value = xs_node_read(xenbus->xsh, XBT_NULL, NULL, &local_err, + "/local/domain/%u/device/console/%u", + xendev->frontend_id, idx); + } + if (!value) { + if (errno == ENOENT) { + con->dev = idx; + error_free(local_err); + goto found; + } + error_propagate(errp, local_err); + 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); - store_con_info(con); + trace_xen_console_unrealize(con->dev); -out: - g_free(type); - return ret; + /* Disconnect from the frontend in case this has not already happened */ + xen_console_disconnect(xendev, NULL); + + qemu_chr_fe_deinit(&con->chr, false); } -static int con_initialise(struct XenLegacyDevice *xendev) +static void xen_console_realize(XenDevice *xendev, Error **errp) { - 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) - con->buffer.max_capacity = limit; + 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 (!xendev->dev) { - xen_pfn_t mfn = con->ring_ref; - con->sring = qemu_xen_foreignmem_map(con->xendev.dom, NULL, - PROT_READ | PROT_WRITE, - 1, &mfn, NULL); + 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 { - con->sring = xen_be_map_grant_ref(xendev, con->ring_ref, - PROT_READ | PROT_WRITE); + return g_strdup_printf("/local/domain/%u/device/console/%u", dom_id, + dev); } - if (!con->sring) - return -1; +} - xen_be_bind_evtchn(&con->xendev); - 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; +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 void con_disconnect(struct XenLegacyDevice *xendev) + +static const Property xen_console_properties[] = { + DEFINE_PROP_CHR("chardev", XenConsole, chr), + DEFINE_PROP_INT32("idx", XenConsole, dev, -1), +}; + +static void xen_console_class_init(ObjectClass *class, const void *data) { - struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + 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); +} - qemu_chr_fe_deinit(&con->chr, false); - xen_pv_unbind_evtchn(&con->xendev); +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, +}; - if (con->sring) { - if (!xendev->dev) { - qemu_xen_foreignmem_unmap(con->sring, 1); +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; + } + + type = xs_node_read(xsh, XBT_NULL, NULL, errp, "%s/%s", fe, "type"); + if (!type) { + 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); + + output = xs_node_read(xsh, XBT_NULL, NULL, errp, "%s/%s", fe, "output"); + if (output) { + /* + * 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 (errno != ENOENT) { + error_prepend(errp, "console: No valid chardev found: "); + goto fail; + } else { + error_free(*errp); + *errp = NULL; + + if (number) { + cd = serial_hd(number); + if (!cd) { + error_setg(errp, "console: No serial device #%ld found", + number); + goto fail; + } } else { - xen_be_unmap_grant_ref(xendev, con->sring, con->ring_ref); + /* 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; + } } - con->sring = NULL; } + + 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 con_event(struct XenLegacyDevice *xendev) +static void xen_console_device_destroy(XenBackendInstance *backend, + Error **errp) { - struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + ERRP_GUARD(); + XenDevice *xendev = xen_backend_get_device(backend); + XenConsole *con = XEN_CONSOLE_DEVICE(xendev); - buffer_append(con); - if (con->buffer.size - con->buffer.consumed) - xencons_send(con); -} + trace_xen_console_device_destroy(con->dev); -/* -------------------------------------------------------------------- */ + object_unparent(OBJECT(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, +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 180bb97202c..8008171eea9 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 "qapi/error.h" #include "hw/char/xilinx_uartlite.h" #include "hw/irq.h" #include "hw/qdev-properties.h" @@ -57,6 +58,7 @@ struct XilinxUARTLite { SysBusDevice parent_obj; + EndianMode model_endianness; MemoryRegion mmio; CharBackend chr; qemu_irq irq; @@ -166,19 +168,22 @@ uart_write(void *opaque, hwaddr addr, uart_update_irq(s); } -static const MemoryRegionOps uart_ops = { - .read = uart_read, - .write = uart_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 4 - } +static const MemoryRegionOps uart_ops[2] = { + [0 ... 1] = { + .read = uart_read, + .write = uart_write, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, + }, + [0].endianness = DEVICE_LITTLE_ENDIAN, + [1].endianness = DEVICE_BIG_ENDIAN, }; -static Property xilinx_uartlite_properties[] = { +static const Property xilinx_uartlite_properties[] = { + DEFINE_PROP_ENDIAN_NODEFAULT("endianness", XilinxUARTLite, model_endianness), DEFINE_PROP_CHR("chardev", XilinxUARTLite, chr), - DEFINE_PROP_END_OF_LIST(), }; static void uart_rx(void *opaque, const uint8_t *buf, int size) @@ -215,6 +220,15 @@ static void xilinx_uartlite_realize(DeviceState *dev, Error **errp) { XilinxUARTLite *s = XILINX_UARTLITE(dev); + if (s->model_endianness == ENDIAN_MODE_UNSPECIFIED) { + error_setg(errp, TYPE_XILINX_UARTLITE " property 'endianness'" + " must be set to 'big' or 'little'"); + return; + } + + memory_region_init_io(&s->mmio, OBJECT(dev), + &uart_ops[s->model_endianness == ENDIAN_MODE_BIG], + s, "xlnx.xps-uartlite", R_MAX * 4); qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event, NULL, s, NULL, true); } @@ -224,17 +238,14 @@ static void xilinx_uartlite_init(Object *obj) XilinxUARTLite *s = XILINX_UARTLITE(obj); sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); - - memory_region_init_io(&s->mmio, obj, &uart_ops, s, - "xlnx.xps-uartlite", R_MAX * 4); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } -static void xilinx_uartlite_class_init(ObjectClass *klass, void *data) +static void xilinx_uartlite_class_init(ObjectClass *klass, const 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 9397503656d..d1bdf765ee8 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 c7831b5293b..bddfc22d388 100644 --- a/hw/core/bus.c +++ b/hw/core/bus.c @@ -232,58 +232,7 @@ 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) +static void bus_class_init(ObjectClass *class, const void *data) { BusClass *bc = BUS_CLASS(class); ResettableClass *rc = RESETTABLE_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) @@ -327,7 +260,7 @@ static const TypeInfo bus_info = { .instance_init = qbus_initfn, .instance_finalize = qbus_finalize, .class_init = bus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_RESETTABLE_INTERFACE }, { } }, diff --git a/hw/core/clock-vmstate.c b/hw/core/clock-vmstate.c index 7eccb6d4eaa..e831fc596f8 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 d82e44cd1aa..9c906761e19 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 "system/qtest.h" #include "hw/clock.h" #include "trace.h" @@ -42,16 +44,12 @@ Clock *clock_new(Object *parent, const char *name) void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque, unsigned int events) { + assert(OBJECT(clk)->parent); clk->callback = cb; clk->callback_opaque = opaque; clk->callback_events = events; } -void clock_clear_callback(Clock *clk) -{ - clock_set_callback(clk, NULL, NULL, 0); -} - bool clock_set(Clock *clk, uint64_t period) { if (clk->period == period) { @@ -108,7 +106,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,14 +140,39 @@ 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_unparent(Object *obj) +{ + /* + * Callback are registered by the parent, which might die anytime after + * it's unparented the children. Avoid having a callback to a deleted + * object in case the clock is still referenced somewhere else (eg: by + * a clock output). + */ + clock_set_callback(CLOCK(obj), NULL, NULL, 0); } static void clock_initfn(Object *obj) @@ -161,6 +183,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) @@ -179,11 +206,17 @@ static void clock_finalizefn(Object *obj) g_free(clk->canonical_path); } +static void clock_class_init(ObjectClass *klass, const void *data) +{ + klass->unparent = clock_unparent; +} + static const TypeInfo clock_info = { .name = TYPE_CLOCK, .parent = TYPE_OBJECT, .instance_size = sizeof(Clock), .instance_init = clock_initfn, + .class_init = clock_class_init, .instance_finalize = clock_finalizefn, }; diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index ced66c2b342..39e674aca21 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -21,28 +21,29 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/core/cpu.h" -#include "sysemu/hw_accel.h" -#include "qemu/notify.h" +#include "system/hw_accel.h" #include "qemu/log.h" #include "qemu/main-loop.h" -#include "exec/log.h" -#include "exec/cpu-common.h" +#include "qemu/lockcnt.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" -#include "sysemu/tcg.h" +#include "qemu/target-info.h" +#include "exec/log.h" +#include "exec/gdbstub.h" +#include "system/tcg.h" #include "hw/boards.h" #include "hw/qdev-properties.h" #include "trace.h" +#ifdef CONFIG_PLUGIN #include "qemu/plugin.h" +#endif CPUState *cpu_by_arch_id(int64_t id) { CPUState *cpu; CPU_FOREACH(cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->get_arch_id(cpu) == id) { + if (cpu->cc->get_arch_id(cpu) == id) { return cpu; } } @@ -70,14 +71,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 +87,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) @@ -101,11 +102,9 @@ static int cpu_common_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg) void cpu_dump_state(CPUState *cpu, FILE *f, int flags) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->dump_state) { + if (cpu->cc->dump_state) { cpu_synchronize_state(cpu); - cc->dump_state(cpu, f, flags); + cpu->cc->dump_state(cpu, f, flags); } } @@ -116,43 +115,59 @@ void cpu_reset(CPUState *cpu) trace_cpu_reset(cpu->cpu_index); } -static void cpu_common_reset_hold(Object *obj) +static void cpu_common_reset_hold(Object *obj, ResetType type) { CPUState *cpu = CPU(obj); - CPUClass *cc = CPU_GET_CLASS(cpu); if (qemu_loglevel_mask(CPU_LOG_RESET)) { qemu_log("CPU Reset (CPU %d)\n", cpu->cpu_index); - log_cpu_state(cpu, cc->reset_dump_flags); + log_cpu_state(cpu, cpu->cc->reset_dump_flags); } cpu->interrupt_request = 0; 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) +ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model) { - return false; + ObjectClass *oc; + CPUClass *cc; + + 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; } -ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model) +char *cpu_model_from_type(const char *typename) { - CPUClass *cc = CPU_CLASS(object_class_by_name(typename)); + g_autofree char *suffix = g_strdup_printf("-%s", target_cpu_type()); + + if (!object_class_by_name(typename)) { + return NULL; + } + + if (g_str_has_suffix(typename, suffix)) { + return g_strndup(typename, strlen(typename) - strlen(suffix)); + } - assert(cpu_model && cc->class_by_name); - return cc->class_by_name(cpu_model); + return g_strdup(typename); } static void cpu_common_parse_features(const char *typename, char *features, @@ -186,6 +201,49 @@ static void cpu_common_parse_features(const char *typename, char *features, } } +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(target_cpu_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; +} + +bool cpu_exec_realizefn(CPUState *cpu, Error **errp) +{ + if (!accel_cpu_common_realize(cpu, errp)) { + return false; + } + + gdb_init_cpu(cpu); + + /* Wait until cpu initialization complete before exposing cpu. */ + cpu_list_add(cpu); + + cpu_vmstate_register(cpu); + + return true; +} + static void cpu_common_realizefn(DeviceState *dev, Error **errp) { CPUState *cpu = CPU(dev); @@ -216,23 +274,50 @@ 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 */ cpu_exec_unrealizefn(cpu); } +void cpu_exec_unrealizefn(CPUState *cpu) +{ + cpu_vmstate_unregister(cpu); + + cpu_list_remove(cpu); + /* + * Now that the vCPU has been removed from the RCU list, we can call + * accel_cpu_common_unrealize, which may free fields using call_rcu. + */ + accel_cpu_common_unrealize(cpu); +} + static void cpu_common_initfn(Object *obj) { CPUState *cpu = CPU(obj); - CPUClass *cc = CPU_GET_CLASS(obj); + + cpu_exec_class_post_init(CPU_GET_CLASS(obj)); + + /* cache the cpu class for the hotpath */ + cpu->cc = CPU_GET_CLASS(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 */ - cpu->nr_cores = 1; + cpu->as = NULL; + cpu->num_ases = 0; + /* user-mode doesn't have configurable SMP topology */ + /* the default value is changed by qemu_init_vcpu() for system-mode */ 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); @@ -241,14 +326,39 @@ static void cpu_common_initfn(Object *obj) QTAILQ_INIT(&cpu->watchpoints); 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) @@ -256,7 +366,7 @@ 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, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -264,7 +374,6 @@ static void cpu_class_init(ObjectClass *klass, void *data) k->parse_features = cpu_common_parse_features; k->get_arch_id = cpu_common_get_arch_id; - k->has_work = cpu_common_has_work; k->gdb_read_register = cpu_common_gdb_read_register; k->gdb_write_register = cpu_common_gdb_write_register; set_bit(DEVICE_CATEGORY_CPU, dc->categories); @@ -287,7 +396,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) @@ -296,3 +405,32 @@ static void cpu_register_types(void) } type_init(cpu_register_types) + +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); + } +} + +void list_cpus(void) +{ + CPUClass *cc = CPU_CLASS(object_class_by_name(target_cpu_type())); + + if (cc->list_cpus) { + cc->list_cpus(); + } else { + 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); + } +} diff --git a/hw/core/cpu-sysemu.c b/hw/core/cpu-sysemu.c deleted file mode 100644 index 5eaf2e79e66..00000000000 --- a/hw/core/cpu-sysemu.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * QEMU CPU model (system emulation specific) - * - * Copyright (c) 2012-2014 SUSE LINUX Products 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 - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/core/cpu.h" -#include "hw/core/sysemu-cpu-ops.h" - -bool cpu_paging_enabled(const CPUState *cpu) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->sysemu_ops->get_paging_enabled) { - return cc->sysemu_ops->get_paging_enabled(cpu); - } - - return false; -} - -void 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; - } - - error_setg(errp, "Obtaining memory mappings is unsupported on this CPU."); -} - -hwaddr cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, - MemTxAttrs *attrs) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->sysemu_ops->get_phys_page_attrs_debug) { - return cc->sysemu_ops->get_phys_page_attrs_debug(cpu, addr, attrs); - } - /* Fallback for CPUs which don't implement the _attrs_ hook */ - *attrs = MEMTXATTRS_UNSPECIFIED; - return cc->sysemu_ops->get_phys_page_debug(cpu, addr); -} - -hwaddr cpu_get_phys_page_debug(CPUState *cpu, vaddr addr) -{ - MemTxAttrs attrs = {}; - - return cpu_get_phys_page_attrs_debug(cpu, addr, &attrs); -} - -int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs) -{ - int ret = 0; - - if (cpu->cc->sysemu_ops->asidx_from_attrs) { - ret = cpu->cc->sysemu_ops->asidx_from_attrs(cpu, attrs); - assert(ret < cpu->num_ases && ret >= 0); - } - return ret; -} - -int cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, - void *opaque) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (!cc->sysemu_ops->write_elf32_qemunote) { - return 0; - } - return (*cc->sysemu_ops->write_elf32_qemunote)(f, cpu, opaque); -} - -int cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cpu, - int cpuid, void *opaque) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (!cc->sysemu_ops->write_elf32_note) { - return -1; - } - return (*cc->sysemu_ops->write_elf32_note)(f, cpu, cpuid, opaque); -} - -int cpu_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cpu, - void *opaque) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (!cc->sysemu_ops->write_elf64_qemunote) { - return 0; - } - return (*cc->sysemu_ops->write_elf64_qemunote)(f, cpu, opaque); -} - -int cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, - int cpuid, void *opaque) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (!cc->sysemu_ops->write_elf64_note) { - return -1; - } - return (*cc->sysemu_ops->write_elf64_note)(f, cpu, cpuid, opaque); -} - -bool cpu_virtio_is_big_endian(CPUState *cpu) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->sysemu_ops->virtio_is_big_endian) { - return cc->sysemu_ops->virtio_is_big_endian(cpu); - } - return target_words_bigendian(); -} - -GuestPanicInformation *cpu_get_crash_info(CPUState *cpu) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - GuestPanicInformation *res = NULL; - - if (cc->sysemu_ops->get_crash_info) { - res = cc->sysemu_ops->get_crash_info(cpu); - } - return res; -} diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c new file mode 100644 index 00000000000..3c84176a0c5 --- /dev/null +++ b/hw/core/cpu-system.c @@ -0,0 +1,305 @@ +/* + * QEMU CPU model (system specific) + * + * Copyright (c) 2012-2014 SUSE LINUX Products 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 + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "system/address-spaces.h" +#include "exec/cputlb.h" +#include "system/memory.h" +#include "exec/tb-flush.h" +#include "exec/tswap.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "hw/core/sysemu-cpu-ops.h" +#include "migration/vmstate.h" +#include "system/tcg.h" + +bool cpu_has_work(CPUState *cpu) +{ + return cpu->cc->sysemu_ops->has_work(cpu); +} + +bool cpu_paging_enabled(const CPUState *cpu) +{ + if (cpu->cc->sysemu_ops->get_paging_enabled) { + return cpu->cc->sysemu_ops->get_paging_enabled(cpu); + } + + return false; +} + +bool cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, + Error **errp) +{ + if (cpu->cc->sysemu_ops->get_memory_mapping) { + return cpu->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, + MemTxAttrs *attrs) +{ + hwaddr paddr; + + if (cpu->cc->sysemu_ops->get_phys_page_attrs_debug) { + paddr = cpu->cc->sysemu_ops->get_phys_page_attrs_debug(cpu, addr, + attrs); + } else { + /* Fallback for CPUs which don't implement the _attrs_ hook */ + *attrs = MEMTXATTRS_UNSPECIFIED; + paddr = cpu->cc->sysemu_ops->get_phys_page_debug(cpu, addr); + } + /* Indicate that this is a debug access. */ + attrs->debug = 1; + return paddr; +} + +hwaddr cpu_get_phys_page_debug(CPUState *cpu, vaddr addr) +{ + MemTxAttrs attrs = {}; + + return cpu_get_phys_page_attrs_debug(cpu, addr, &attrs); +} + +int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs) +{ + int ret = 0; + + if (cpu->cc->sysemu_ops->asidx_from_attrs) { + ret = cpu->cc->sysemu_ops->asidx_from_attrs(cpu, attrs); + assert(ret < cpu->num_ases && ret >= 0); + } + return ret; +} + +int cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, + void *opaque) +{ + if (!cpu->cc->sysemu_ops->write_elf32_qemunote) { + return 0; + } + return (*cpu->cc->sysemu_ops->write_elf32_qemunote)(f, cpu, opaque); +} + +int cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cpu, + int cpuid, void *opaque) +{ + if (!cpu->cc->sysemu_ops->write_elf32_note) { + return -1; + } + return (*cpu->cc->sysemu_ops->write_elf32_note)(f, cpu, cpuid, opaque); +} + +int cpu_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cpu, + void *opaque) +{ + if (!cpu->cc->sysemu_ops->write_elf64_qemunote) { + return 0; + } + return (*cpu->cc->sysemu_ops->write_elf64_qemunote)(f, cpu, opaque); +} + +int cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, + int cpuid, void *opaque) +{ + if (!cpu->cc->sysemu_ops->write_elf64_note) { + return -1; + } + return (*cpu->cc->sysemu_ops->write_elf64_note)(f, cpu, cpuid, opaque); +} + +bool cpu_virtio_is_big_endian(CPUState *cpu) +{ + if (cpu->cc->sysemu_ops->virtio_is_big_endian) { + return cpu->cc->sysemu_ops->virtio_is_big_endian(cpu); + } + return target_big_endian(); +} + +GuestPanicInformation *cpu_get_crash_info(CPUState *cpu) +{ + GuestPanicInformation *res = NULL; + + if (cpu->cc->sysemu_ops->get_crash_info) { + res = cpu->cc->sysemu_ops->get_crash_info(cpu); + } + return res; +} + +static const Property cpu_system_props[] = { + /* + * 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 *), +}; + +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); + + /* + * 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); + + device_class_set_props(dc, cpu_system_props); +} + +void cpu_exec_class_post_init(CPUClass *cc) +{ + /* Check mandatory SysemuCPUOps handlers */ + g_assert(cc->sysemu_ops->has_work); +} + +void cpu_exec_initfn(CPUState *cpu) +{ + cpu->memory = get_system_memory(); + object_ref(OBJECT(cpu->memory)); +} + +static int cpu_common_post_load(void *opaque, int version_id) +{ + if (tcg_enabled()) { + 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 + } +}; + +void cpu_vmstate_register(CPUState *cpu) +{ + 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); + } +} + +void cpu_vmstate_unregister(CPUState *cpu) +{ + if (cpu->cc->sysemu_ops->legacy_vmsd != NULL) { + vmstate_unregister(NULL, cpu->cc->sysemu_ops->legacy_vmsd, cpu); + } + if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { + vmstate_unregister(NULL, &vmstate_cpu_common, cpu); + } +} diff --git a/hw/core/cpu-user.c b/hw/core/cpu-user.c new file mode 100644 index 00000000000..7176791851b --- /dev/null +++ b/hw/core/cpu-user.c @@ -0,0 +1,49 @@ +/* + * QEMU CPU model (user specific) + * + * Copyright (c) Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "hw/core/cpu.h" +#include "migration/vmstate.h" + +static const Property cpu_user_props[] = { + /* + * 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), +}; + +void cpu_class_init_props(DeviceClass *dc) +{ + device_class_set_props(dc, cpu_user_props); +} + +void cpu_exec_class_post_init(CPUClass *cc) +{ + /* nothing to do */ +} + +void cpu_exec_initfn(CPUState *cpu) +{ + /* nothing to do */ +} + +void cpu_vmstate_register(CPUState *cpu) +{ + assert(qdev_get_vmsd(DEVICE(cpu)) == NULL || + qdev_get_vmsd(DEVICE(cpu))->unmigratable); +} + +void cpu_vmstate_unregister(CPUState *cpu) +{ + /* nothing to do */ +} diff --git a/hw/core/eif.c b/hw/core/eif.c new file mode 100644 index 00000000000..513caec6b49 --- /dev/null +++ b/hw/core/eif.c @@ -0,0 +1,709 @@ +/* + * 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, + QCryptoHash *hash0, QCryptoHash *hash1, + uint32_t *crc, Error **errp) +{ + size_t got; + FILE *tmp_file = NULL; + uint8_t *kernel = g_try_malloc(size); + if (!kernel) { + error_setg(errp, "Out of memory reading kernel section"); + goto cleanup; + } + + *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); + if (qcrypto_hash_update(hash0, (char *)kernel, size, errp) != 0 || + qcrypto_hash_update(hash1, (char *)kernel, size, errp) != 0) { + goto cleanup; + } + g_free(kernel); + fclose(tmp_file); + + return true; + + cleanup: + safe_fclose(tmp_file); + + safe_unlink(*kernel_path); + g_free(*kernel_path); + *kernel_path = NULL; + + g_free(kernel); + return false; +} + +static bool read_eif_cmdline(FILE *f, uint64_t size, char *cmdline, + QCryptoHash *hash0, QCryptoHash *hash1, + 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); + if (qcrypto_hash_update(hash0, cmdline, size, errp) != 0 || + qcrypto_hash_update(hash1, cmdline, size, errp) != 0) { + return false; + } + return true; +} + +static bool read_eif_ramdisk(FILE *eif, FILE *initrd, uint64_t size, + QCryptoHash *hash0, QCryptoHash *h, uint32_t *crc, + Error **errp) +{ + size_t got; + bool ret = false; + uint8_t *ramdisk = g_try_malloc(size); + if (!ramdisk) { + error_setg(errp, "Out of memory reading initrd section"); + goto cleanup; + } + + got = fread(ramdisk, 1, size, eif); + if ((uint64_t) got != size) { + error_setg(errp, "Failed to read EIF ramdisk section data"); + goto cleanup; + } + + got = fwrite(ramdisk, 1, size, initrd); + if ((uint64_t) got != size) { + error_setg(errp, "Failed to write EIF ramdisk data to temporary file"); + goto cleanup; + } + + *crc = crc32(*crc, ramdisk, size); + if (qcrypto_hash_update(hash0, (char *)ramdisk, size, errp) != 0 || + qcrypto_hash_update(h, (char *)ramdisk, size, errp) != 0) { + goto cleanup; + } + ret = true; + + cleanup: + g_free(ramdisk); + return ret; +} + +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_hash(QCryptoHash *h, uint8_t *hash, Error **errp) +{ + size_t hash_len = QCRYPTO_HASH_DIGEST_LEN_SHA384; + return qcrypto_hash_finalize_bytes(h, &hash, &hash_len, errp) == 0; +} + +/* + * 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_hash, uint8_t *bootstrap_hash, + uint8_t *app_hash, uint8_t *fingerprint_hash, + 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 */ + g_autoptr(QCryptoHash) hash0 = NULL; + /* kernel + boot ramdisk + cmdline SHA384 hash */ + g_autoptr(QCryptoHash) hash1 = NULL; + /* application ramdisk(s) SHA384 hash */ + g_autoptr(QCryptoHash) hash2 = NULL; + + *signature_found = false; + *kernel_path = *initrd_path = *cmdline = NULL; + + hash0 = qcrypto_hash_new(QCRYPTO_HASH_ALGO_SHA384, errp); + if (!hash0) { + goto cleanup; + } + hash1 = qcrypto_hash_new(QCRYPTO_HASH_ALGO_SHA384, errp); + if (!hash1) { + goto cleanup; + } + hash2 = qcrypto_hash_new(QCRYPTO_HASH_ALGO_SHA384, errp); + if (!hash2) { + goto cleanup; + } + + 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; + } + + if (!read_eif_kernel(f, hdr.section_size, kernel_path, hash0, + hash1, &crc, errp)) { + goto cleanup; + } + + break; + case EIF_SECTION_CMDLINE: + { + uint64_t size; + 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, hash0, hash1, &crc, + errp)) { + goto cleanup; + } + (*cmdline)[size] = '\0'; + + break; + } + case EIF_SECTION_RAMDISK: + { + QCryptoHash *h = hash2; + if (!seen_sections[EIF_SECTION_RAMDISK]) { + /* + * If this is the first time we are seeing a ramdisk section, + * we need to: + * 1) hash it into bootstrap (hash1) instead of app (hash2) + * along with image (hash0) + * 2) create the initrd temporary file. + */ + h = hash1; + 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; + } + } + + if (!read_eif_ramdisk(f, initrd_path_f, hdr.section_size, hash0, h, + &crc, errp)) { + goto cleanup; + } + + break; + } + case EIF_SECTION_SIGNATURE: + *signature_found = true; + if (!get_signature_fingerprint_sha384(f, hdr.section_size, + fingerprint_hash, &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; + } + + if (!read_eif_ramdisk(machine_initrd_f, initrd_path_f, + machine_initrd_size, hash0, hash2, &crc, errp)) { + goto cleanup; + } + } + + if (!get_SHA384_hash(hash0, image_hash, errp)) { + goto cleanup; + } + if (!get_SHA384_hash(hash1, bootstrap_hash, errp)) { + goto cleanup; + } + if (!get_SHA384_hash(hash2, app_hash, errp)) { + goto cleanup; + } + + fclose(f); + fclose(initrd_path_f); + safe_fclose(machine_initrd_f); + return true; + + cleanup: + 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 00000000000..fed3cb55140 --- /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 4f4d77908da..e72bbde2a23 100644 --- a/hw/core/generic-loader.c +++ b/hw/core/generic-loader.c @@ -24,16 +24,15 @@ * 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 "sysemu/dma.h" -#include "sysemu/reset.h" +#include "system/dma.h" +#include "system/reset.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/qdev-properties.h" @@ -48,11 +47,8 @@ static void generic_loader_reset(void *opaque) GenericLoaderState *s = GENERIC_LOADER(opaque); if (s->set_pc) { - CPUClass *cc = CPU_GET_CLASS(s->cpu); cpu_reset(s->cpu); - if (cc) { - cc->set_pc(s->cpu, s->addr); - } + cpu_set_pc(s->cpu, s->addr); } if (s->data_len) { @@ -66,7 +62,6 @@ static void generic_loader_realize(DeviceState *dev, Error **errp) { GenericLoaderState *s = GENERIC_LOADER(dev); hwaddr entry; - int big_endian; ssize_t size = 0; s->set_pc = false; @@ -134,14 +129,12 @@ static void generic_loader_realize(DeviceState *dev, Error **errp) s->cpu = first_cpu; } - big_endian = target_words_bigendian(); - if (s->file) { AddressSpace *as = s->cpu ? s->cpu->as : NULL; if (!s->force_raw) { size = load_elf_as(s->file, NULL, NULL, NULL, &entry, NULL, NULL, - NULL, big_endian, 0, 0, 0, as); + NULL, ELFDATANONE, 0, 0, 0, as); if (size < 0) { size = load_uimage_as(s->file, &entry, NULL, NULL, NULL, NULL, @@ -166,7 +159,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 { @@ -179,7 +172,7 @@ static void generic_loader_unrealize(DeviceState *dev) qemu_unregister_reset(generic_loader_reset, dev); } -static Property generic_loader_props[] = { +static const Property generic_loader_props[] = { DEFINE_PROP_UINT64("addr", GenericLoaderState, addr, 0), DEFINE_PROP_UINT64("data", GenericLoaderState, data, 0), DEFINE_PROP_UINT8("data-len", GenericLoaderState, data_len, 0), @@ -187,10 +180,9 @@ static Property generic_loader_props[] = { DEFINE_PROP_UINT32("cpu-num", GenericLoaderState, cpu_num, CPU_NONE), DEFINE_PROP_BOOL("force-raw", GenericLoaderState, force_raw, false), DEFINE_PROP_STRING("file", GenericLoaderState, file), - DEFINE_PROP_END_OF_LIST(), }; -static void generic_loader_class_init(ObjectClass *klass, void *data) +static void generic_loader_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/core/gpio.c b/hw/core/gpio.c index 80d07a6ec99..6e32a8eec61 100644 --- a/hw/core/gpio.c +++ b/hw/core/gpio.c @@ -121,8 +121,7 @@ void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n, name ? name : "unnamed-gpio-out", n); if (input_pin && !OBJECT(input_pin)->parent) { /* We need a name for object_property_set_link to work */ - object_property_add_child(container_get(qdev_get_machine(), - "/unattached"), + object_property_add_child(machine_get_container("unattached"), "non-qdev-gpio[*]", OBJECT(input_pin)); } object_property_set_link(OBJECT(dev), propname, diff --git a/hw/core/guest-loader.c b/hw/core/guest-loader.c index 391c875a297..3db89d7a2e1 100644 --- a/hw/core/guest-loader.c +++ b/hw/core/guest-loader.c @@ -26,13 +26,13 @@ #include "qemu/osdep.h" #include "hw/core/cpu.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/loader.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/module.h" #include "guest-loader.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/boards.h" /* @@ -111,15 +111,14 @@ static void guest_loader_realize(DeviceState *dev, Error **errp) loader_insert_platform_data(s, size, errp); } -static Property guest_loader_props[] = { +static const Property guest_loader_props[] = { DEFINE_PROP_UINT64("addr", GuestLoaderState, addr, 0), DEFINE_PROP_STRING("kernel", GuestLoaderState, kernel), DEFINE_PROP_STRING("bootargs", GuestLoaderState, args), DEFINE_PROP_STRING("initrd", GuestLoaderState, initrd), - DEFINE_PROP_END_OF_LIST(), }; -static void guest_loader_class_init(ObjectClass *klass, void *data) +static void guest_loader_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/core/irq.c b/hw/core/irq.c index 3f14e2dda74..6dd8d47bd6e 100644 --- a/hw/core/irq.c +++ b/hw/core/irq.c @@ -26,16 +26,6 @@ #include "hw/irq.h" #include "qom/object.h" -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) { if (!irq) @@ -44,6 +34,29 @@ 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); +} + +void qemu_init_irqs(IRQState irq[], size_t count, + qemu_irq_handler handler, void *opaque) +{ + for (size_t i = 0; i < count; i++) { + qemu_init_irq(&irq[i], handler, opaque, i); + } +} + qemu_irq *qemu_extend_irqs(qemu_irq *old, int n_old, qemu_irq_handler handler, void *opaque, int n) { @@ -67,13 +80,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) { - 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; } diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c index b7c7b3ba94d..2dea485ae06 100644 --- a/hw/core/loader-fit.c +++ b/hw/core/loader-fit.c @@ -20,20 +20,20 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/units.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/loader.h" #include "hw/loader-fit.h" #include "qemu/cutils.h" #include "qemu/error-report.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include #include #define FIT_LOADER_MAX_PATH (128) -static const void *fit_load_image_alloc(const void *itb, const char *name, - int *poff, size_t *psz, Error **errp) +static void *fit_load_image_alloc(const void *itb, const char *name, + int *poff, size_t *psz, Error **errp) { const void *data; const char *comp; @@ -80,11 +80,11 @@ static const void *fit_load_image_alloc(const void *itb, const char *name, return NULL; } - data = g_realloc(uncomp_data, uncomp_len); + uncomp_data = g_realloc(uncomp_data, uncomp_len); if (psz) { *psz = uncomp_len; } - return data; + return uncomp_data; } error_setg(errp, "unknown compression '%s'", comp); @@ -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; @@ -176,12 +177,12 @@ static int fit_load_kernel(const struct fit_loader *ldr, const void *itb, 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) + hwaddr kernel_end, void **pfdt, Error **errp) { + ERRP_GUARD(); Error *err = NULL; const char *name; - const void *data; - const void *load_data; + void *data; hwaddr load_addr; int img_off; size_t sz; @@ -192,7 +193,7 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, return 0; } - load_data = data = fit_load_image_alloc(itb, name, &img_off, &sz, errp); + data = fit_load_image_alloc(itb, name, &img_off, &sz, errp); if (!data) { error_prepend(errp, "unable to load FDT image from FIT: "); return -EINVAL; @@ -209,19 +210,23 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, } if (ldr->fdt_filter) { - load_data = ldr->fdt_filter(opaque, data, match_data, &load_addr); + void *filtered_data; + + filtered_data = ldr->fdt_filter(opaque, data, match_data, &load_addr); + if (filtered_data != data) { + g_free(data); + data = filtered_data; + } } load_addr = ldr->addr_to_phys(opaque, load_addr); - sz = fdt_totalsize(load_data); - rom_add_blob_fixed(name, load_data, sz, load_addr); + sz = fdt_totalsize(data); + rom_add_blob_fixed(name, data, sz, load_addr); - ret = 0; + *pfdt = data; + return 0; out: g_free((void *) data); - if (data != load_data) { - g_free((void *) load_data); - } return ret; } @@ -257,7 +262,8 @@ static bool fit_cfg_compatible(const void *itb, int cfg, const char *compat) return ret; } -int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque) +int load_fit(const struct fit_loader *ldr, const char *filename, + void **pfdt, void *opaque) { Error *err = NULL; const struct fit_loader_match *match; @@ -265,7 +271,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); @@ -321,7 +327,7 @@ int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque) goto out; } - ret = fit_load_fdt(ldr, itb, cfg_off, opaque, match_data, kernel_end, + ret = fit_load_fdt(ldr, itb, cfg_off, opaque, match_data, kernel_end, pfdt, &err); if (ret) { error_report_err(err); diff --git a/hw/core/loader.c b/hw/core/loader.c index 4dd5a71fb79..e7056ba4bd3 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -51,18 +51,19 @@ #include "trace.h" #include "hw/hw.h" #include "disas/disas.h" +#include "migration/cpr.h" #include "migration/vmstate.h" #include "monitor/monitor.h" -#include "sysemu/reset.h" -#include "sysemu/sysemu.h" +#include "system/reset.h" +#include "system/system.h" #include "uboot_image.h" #include "hw/loader.h" #include "hw/nvram/fw_cfg.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/boards.h" #include "qemu/cutils.h" -#include "sysemu/runstate.h" -#include "accel/tcg/debuginfo.h" +#include "system/runstate.h" +#include "tcg/debuginfo.h" #include @@ -144,7 +145,7 @@ ssize_t load_image_mr(const char *filename, MemoryRegion *mr) { ssize_t size; - if (!memory_access_is_direct(mr, false)) { + if (!memory_access_is_direct(mr, false, MEMTXATTRS_UNSPECIFIED)) { /* Can only load an image into RAM or ROM */ return -1; } @@ -225,7 +226,7 @@ static void bswap_ahdr(struct exec *e) ssize_t load_aout(const char *filename, hwaddr addr, int max_sz, - int bswap_needed, hwaddr target_page_size) + bool big_endian, hwaddr target_page_size) { int fd; ssize_t size, ret; @@ -240,7 +241,7 @@ ssize_t load_aout(const char *filename, hwaddr addr, int max_sz, if (size < 0) goto fail; - if (bswap_needed) { + if (big_endian != HOST_BIG_ENDIAN) { bswap_ahdr(&e); } @@ -305,7 +306,7 @@ static void *load_at(int fd, off_t offset, size_t size) #define elf_word uint32_t #define elf_sword int32_t #define bswapSZs bswap32s -#include "hw/elf_ops.h" +#include "hw/elf_ops.h.inc" #undef elfhdr #undef elf_phdr @@ -327,7 +328,7 @@ static void *load_at(int fd, off_t offset, size_t size) #define elf_sword int64_t #define bswapSZs bswap64s #define SZ 64 -#include "hw/elf_ops.h" +#include "hw/elf_ops.h.inc" const char *load_elf_strerror(ssize_t error) { @@ -409,11 +410,11 @@ ssize_t load_elf(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, + uint64_t *highaddr, uint32_t *pflags, int elf_data_order, int elf_machine, int clear_lsb, int data_swab) { return load_elf_as(filename, elf_note_fn, translate_fn, translate_opaque, - pentry, lowaddr, highaddr, pflags, big_endian, + pentry, lowaddr, highaddr, pflags, elf_data_order, elf_machine, clear_lsb, data_swab, NULL); } @@ -422,29 +423,15 @@ ssize_t load_elf_as(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, + uint64_t *highaddr, uint32_t *pflags, int elf_data_order, int elf_machine, int clear_lsb, int data_swab, AddressSpace *as) -{ - return load_elf_ram(filename, elf_note_fn, translate_fn, translate_opaque, - pentry, lowaddr, highaddr, pflags, big_endian, - elf_machine, clear_lsb, data_swab, as, true); -} - -/* return < 0 if error, otherwise the number of bytes loaded in memory */ -ssize_t load_elf_ram(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, - int big_endian, int elf_machine, int clear_lsb, - int data_swab, AddressSpace *as, bool load_rom) { return load_elf_ram_sym(filename, elf_note_fn, translate_fn, translate_opaque, - pentry, lowaddr, highaddr, pflags, big_endian, + pentry, lowaddr, highaddr, pflags, elf_data_order, elf_machine, clear_lsb, data_swab, as, - load_rom, NULL); + true, NULL); } /* return < 0 if error, otherwise the number of bytes loaded in memory */ @@ -453,11 +440,12 @@ ssize_t load_elf_ram_sym(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, - uint32_t *pflags, int big_endian, int elf_machine, + uint32_t *pflags, int elf_data_order, int elf_machine, int clear_lsb, int data_swab, AddressSpace *as, bool load_rom, symbol_fn_t sym_cb) { - int fd, data_order, target_data_order, must_swab; + const int host_data_order = HOST_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB; + int fd, must_swab; ssize_t ret = ELF_LOAD_FAILED; uint8_t e_ident[EI_NIDENT]; @@ -475,23 +463,14 @@ ssize_t load_elf_ram_sym(const char *filename, ret = ELF_LOAD_NOT_ELF; goto fail; } -#if HOST_BIG_ENDIAN - data_order = ELFDATA2MSB; -#else - data_order = ELFDATA2LSB; -#endif - must_swab = data_order != e_ident[EI_DATA]; - if (big_endian) { - target_data_order = ELFDATA2MSB; - } else { - target_data_order = ELFDATA2LSB; - } - if (target_data_order != e_ident[EI_DATA]) { + if (elf_data_order != ELFDATANONE && elf_data_order != e_ident[EI_DATA]) { ret = ELF_LOAD_WRONG_ENDIAN; goto fail; } + must_swab = host_data_order != e_ident[EI_DATA]; + lseek(fd, 0, SEEK_SET); if (e_ident[EI_CLASS] == ELFCLASS64) { ret = load_elf64(filename, fd, elf_note_fn, @@ -505,7 +484,7 @@ ssize_t load_elf_ram_sym(const char *filename, clear_lsb, data_swab, as, load_rom, sym_cb); } - if (ret != ELF_LOAD_FAILED) { + if (ret > 0) { debuginfo_report_elf(filename, fd, 0); } @@ -558,7 +537,7 @@ static void zfree(void *x, void *addr) 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; @@ -610,6 +589,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; @@ -844,19 +824,6 @@ 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); - } - return bytes; -} /* The PE/COFF MS-DOS stub magic number */ #define EFI_PE_MSDOS_MAGIC "MZ" @@ -898,11 +865,11 @@ struct linux_efi_zboot_header { * * 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) +ssize_t unpack_efi_zboot_image(uint8_t **buffer, ssize_t *size) { const struct linux_efi_zboot_header *header; uint8_t *data = NULL; - int ploff, plsize; + ssize_t ploff, plsize; ssize_t bytes; /* ignore if this is too small to be a EFI zboot image */ @@ -1063,20 +1030,22 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name, bool ro) vmstate_register_ram_global(rom->mr); data = memory_region_get_ram_ptr(rom->mr); - memcpy(data, rom->data, rom->datasize); + if (!cpr_is_incoming()) { + memcpy(data, rom->data, rom->datasize); + } return data; } 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) { @@ -1094,10 +1063,10 @@ ssize_t rom_add_file(const char *file, const char *fw_dir, rom->path = g_strdup(file); } - fd = open(rom->path, O_RDONLY | O_BINARY); - 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; } @@ -1106,23 +1075,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; @@ -1139,7 +1093,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; @@ -1159,9 +1113,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; } @@ -1382,20 +1333,6 @@ void rom_set_fw(FWCfgState *f) fw_cfg = f; } -void rom_set_order_override(int order) -{ - if (!fw_cfg) - return; - fw_cfg_set_order_override(fw_cfg, order); -} - -void rom_reset_order_override(void) -{ - if (!fw_cfg) - return; - fw_cfg_reset_order_override(fw_cfg); -} - void rom_transaction_begin(void) { Rom *rom; @@ -1459,7 +1396,7 @@ typedef struct RomSec { * work, but this way saves a little work later by avoiding * dealing with "gaps" of 0 length. */ -static gint sort_secs(gconstpointer a, gconstpointer b) +static gint sort_secs(gconstpointer a, gconstpointer b, gpointer d) { RomSec *ra = (RomSec *) a; RomSec *rb = (RomSec *) b; @@ -1512,7 +1449,7 @@ RomGap rom_find_largest_gap_between(hwaddr base, size_t size) /* sentinel */ secs = add_romsec_to_list(secs, base + size, 1); - secs = g_list_sort(secs, sort_secs); + secs = g_list_sort_with_data(secs, sort_secs, NULL); for (it = g_list_first(secs); it; it = g_list_next(it)) { cand = (RomSec *) it->data; diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index c3e55ef9e9c..c6325cdcaaa 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -19,10 +19,10 @@ #include "qapi/error.h" #include "qapi/qapi-builtin-visit.h" #include "qapi/qapi-commands-machine.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/string-output-visitor.h" #include "qemu/error-report.h" -#include "sysemu/numa.h" +#include "system/numa.h" #include "hw/boards.h" void hmp_info_cpus(Monitor *mon, const QDict *qdict) @@ -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); } @@ -247,6 +257,7 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) MemoryDeviceInfo *value; PCDIMMDeviceInfo *di; SgxEPCDeviceInfo *se; + HvBalloonDeviceInfo *hi; for (info = info_list; info; info = info->next) { value = info->value; @@ -304,6 +315,20 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) 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(); } diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 3860a50c3b7..d82043e1c68 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -12,21 +12,21 @@ #include "hw/boards.h" #include "hw/intc/intc.h" #include "hw/mem/memory-device.h" -#include "hw/rdma/rdma.h" #include "qapi/error.h" #include "qapi/qapi-builtin-visit.h" #include "qapi/qapi-commands-machine.h" -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" #include "qapi/qobject-input-visitor.h" #include "qapi/type-helpers.h" -#include "qemu/main-loop.h" #include "qemu/uuid.h" +#include "qemu/target-info.h" #include "qom/qom-qobject.h" -#include "sysemu/hostmem.h" -#include "sysemu/hw_accel.h" -#include "sysemu/numa.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/hostmem.h" +#include "system/hw_accel.h" +#include "system/numa.h" +#include "system/runstate.h" +#include "system/system.h" +#include "hw/s390x/storage-keys.h" /* * fast means: we NEVER interrupt vCPU threads to retrieve @@ -66,13 +66,15 @@ 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; for (el = machines; el; el = el->next) { MachineClass *mc = el->data; + const char *default_cpu_type = machine_class_default_cpu_type(mc); MachineInfo *info; info = g_malloc0(sizeof(*info)); @@ -91,13 +93,33 @@ MachineInfoList *qmp_query_machines(Error **errp) 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); + if (default_cpu_type) { + info->default_cpu_type = g_strdup(default_cpu_type); } if (mc->default_ram_id) { info->default_ram_id = g_strdup(mc->default_ram_id); } + 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); } @@ -113,9 +135,9 @@ CurrentMachineParams *qmp_query_current_machine(Error **errp) return params; } -TargetInfo *qmp_query_target(Error **errp) +QemuTargetInfo *qmp_query_target(Error **errp) { - TargetInfo *info = g_malloc0(sizeof(*info)); + QemuTargetInfo *info = g_malloc0(sizeof(*info)); info->arch = qapi_enum_parse(&SysEmuTarget_lookup, target_name(), -1, &error_abort); @@ -292,37 +314,6 @@ MemoryInfo *qmp_query_memory_size_summary(Error **errp) return mem_info; } -static int qmp_x_query_rdma_foreach(Object *obj, void *opaque) -{ - RdmaProvider *rdma; - RdmaProviderClass *k; - GString *buf = opaque; - - if (object_dynamic_cast(obj, INTERFACE_RDMA_PROVIDER)) { - rdma = RDMA_PROVIDER(obj); - k = RDMA_PROVIDER_GET_CLASS(obj); - if (k->format_statistics) { - k->format_statistics(rdma, buf); - } else { - g_string_append_printf(buf, - "RDMA statistics not available for %s.\n", - object_get_typename(obj)); - } - } - - return 0; -} - -HumanReadableText *qmp_x_query_rdma(Error **errp) -{ - g_autoptr(GString) buf = g_string_new(""); - - object_child_foreach_recursive(object_get_root(), - qmp_x_query_rdma_foreach, buf); - - return human_readable_text_from_str(buf); -} - HumanReadableText *qmp_x_query_ramblock(Error **errp) { g_autoptr(GString) buf = ram_block_format(); @@ -373,6 +364,35 @@ HumanReadableText *qmp_x_query_irq(Error **errp) 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; @@ -389,3 +409,16 @@ GuidInfo *qmp_query_vm_generation_id(Error **errp) info->guid = qemu_uuid_unparse_strdup(&vms->guid); return info; } + +void qmp_dump_skeys(const char *filename, Error **errp) +{ + ObjectClass *mc = object_get_class(qdev_get_machine()); + ObjectClass *oc = object_class_dynamic_cast(mc, TYPE_DUMP_SKEYS_INTERFACE); + + if (!oc) { + error_setg(errp, "Storage keys information not available" + " for this architecture"); + return; + } + DUMP_SKEYS_INTERFACE_CLASS(oc)->qmp_dump_skeys(filename, errp); +} diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c index 0f4d9b6f7a9..0be0ac044c2 100644 --- a/hw/core/machine-smp.c +++ b/hw/core/machine-smp.c @@ -33,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) { @@ -43,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); @@ -75,43 +87,79 @@ 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"); + 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; } - if (!mc->smp_props.clusters_supported && clusters > 1) { - error_setg(errp, "clusters not supported by this machine's CPU topology"); + modules = modules > 0 ? modules : 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; - clusters = clusters > 0 ? clusters : 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) { @@ -126,36 +174,51 @@ 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; @@ -163,7 +226,7 @@ void machine_parse_smp_config(MachineState *ms, 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: " @@ -198,12 +261,148 @@ void machine_parse_smp_config(MachineState *ms, } } +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 (props->topology == CPU_TOPOLOGY_LEVEL_THREAD) { + error_setg(errp, + "%s level cache not supported by this machine", + CpuTopologyLevel_str(props->topology)); + return false; + } + + if (!machine_check_topo_support(ms, props->topology, errp)) { + return false; + } + } + + mc->smp_props.has_caches = true; + return true; +} + unsigned int machine_topo_get_cores_per_socket(const MachineState *ms) { - return ms->smp.cores * ms->smp.clusters * ms->smp.dies; + 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 da699cf4e14..e869821b224 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -11,35 +11,81 @@ */ #include "qemu/osdep.h" -#include "qemu/option.h" -#include "qemu/accel.h" -#include "sysemu/replay.h" #include "qemu/units.h" +#include "qemu/accel.h" +#include "system/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 "qapi/qapi-commands-machine.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 "system/cpus.h" +#include "system/system.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/xen.h" +#include "system/qtest.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 "system/confidential-guest-support.h" #include "hw/virtio/virtio-pci.h" +#include "hw/virtio/virtio-net.h" +#include "hw/virtio/virtio-iommu.h" +#include "audio/audio.h" + +GlobalProperty hw_compat_10_0[] = { + { "scsi-hd", "dpofua", "off" }, +}; +const size_t hw_compat_10_0_len = G_N_ELEMENTS(hw_compat_10_0); + +GlobalProperty hw_compat_9_2[] = { + { "arm-cpu", "backcompat-pauth-default-use-qarma5", "true"}, + { "virtio-balloon-pci", "vectors", "0" }, + { "virtio-balloon-pci-transitional", "vectors", "0" }, + { "virtio-balloon-pci-non-transitional", "vectors", "0" }, + { "virtio-mem-pci", "vectors", "0" }, + { "migration", "multifd-clean-tls-termination", "false" }, + { "migration", "send-switchover-start", "off"}, + { "vfio-pci", "x-migration-multifd-transfer", "off" }, +}; +const size_t hw_compat_9_2_len = G_N_ELEMENTS(hw_compat_9_2); -GlobalProperty hw_compat_8_1[] = {}; +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[] = { @@ -96,6 +142,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); @@ -177,7 +224,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" }, @@ -239,53 +285,6 @@ GlobalProperty hw_compat_2_6[] = { }; const size_t hw_compat_2_6_len = G_N_ELEMENTS(hw_compat_2_6); -GlobalProperty hw_compat_2_5[] = { - { "isa-fdc", "fallback", "144" }, - { "pvscsi", "x-old-pci-configuration", "on" }, - { "pvscsi", "x-disable-pcie", "on" }, - { "vmxnet3", "x-old-msi-offsets", "on" }, - { "vmxnet3", "x-disable-pcie", "on" }, -}; -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" }, - { "fw_cfg_mem", "dma_enabled", "off" }, - { "fw_cfg_io", "dma_enabled", "off" } -}; -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) @@ -303,6 +302,21 @@ static void machine_set_kernel(Object *obj, const char *value, Error **errp) ms->kernel_filename = g_strdup(value); } +static char *machine_get_shim(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->shim_filename); +} + +static void machine_set_shim(Object *obj, const char *value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + g_free(ms->shim_filename); + ms->shim_filename = g_strdup(value); +} + static char *machine_get_initrd(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -413,6 +427,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; } @@ -427,9 +445,29 @@ 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; } +#ifdef CONFIG_POSIX +static bool machine_get_aux_ram_share(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return ms->aux_ram_share; +} + +static void machine_set_aux_ram_share(Object *obj, bool value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->aux_ram_share = value; +} +#endif + static bool machine_get_usb(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -591,11 +629,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 " @@ -682,6 +728,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; @@ -692,7 +758,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); @@ -702,7 +768,7 @@ HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine) cpu = machine->possible_cpus->cpus[i].cpu; if (cpu) { - 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); } @@ -767,6 +833,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; @@ -791,6 +862,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; @@ -843,9 +919,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, @@ -869,6 +948,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) { @@ -927,24 +1040,81 @@ static void machine_set_boot(Object *obj, Visitor *v, const char *name, qapi_free_BootConfiguration(config); } -static void machine_class_init(ObjectClass *oc, void *data) +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, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); /* 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", "Linux kernel image file"); + object_class_property_add_str(oc, "shim", + machine_get_shim, machine_set_shim); + object_class_property_set_description(oc, "shim", + "shim.efi file"); + object_class_property_add_str(oc, "initrd", machine_get_initrd, machine_set_initrd); object_class_property_set_description(oc, "initrd", @@ -977,6 +1147,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); @@ -998,6 +1173,12 @@ static void machine_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, "mem-merge", "Enable/disable memory merge support"); +#ifdef CONFIG_POSIX + object_class_property_add_bool(oc, "aux-ram-share", + machine_get_aux_ram_share, + machine_set_aux_ram_share); +#endif + object_class_property_add_bool(oc, "usb", machine_get_usb, machine_set_usb); object_class_property_set_description(oc, "usb", @@ -1046,7 +1227,7 @@ static void machine_class_init(ObjectClass *oc, void *data) "Memory size configuration"); } -static void machine_class_base_init(ObjectClass *oc, void *data) +static void machine_class_base_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->max_cpus = mc->max_cpus ?: 1; @@ -1067,19 +1248,14 @@ static void machine_initfn(Object *obj) MachineState *ms = MACHINE(obj); MachineClass *mc = MACHINE_GET_CLASS(obj); - container_get(obj, "/peripheral"); - 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); @@ -1108,12 +1284,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 }); } @@ -1132,6 +1316,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) @@ -1154,6 +1339,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); @@ -1172,6 +1362,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, ", "); @@ -1274,7 +1470,7 @@ static void validate_cpu_cluster_to_numa_boundary(MachineState *ms) const CPUArchId *cpus = possible_cpus->cpus; int i, j; - if (state->num_nodes <= 1 || possible_cpus->len <= 1) { + if (qtest_enabled() || state->num_nodes <= 1 || possible_cpus->len <= 1) { return; } @@ -1320,44 +1516,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; + if (mc->valid_cpu_types && !mc->valid_cpu_types[1]) { + /* Only a single CPU type allowed: use it as default. */ + return mc->valid_cpu_types[0]; + } + return mc->default_cpu_type; +} - 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; +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; } } - 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; + + /* 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); } - r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp); -out: - object_unref(obj); - return r; + 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 @@ -1383,13 +1609,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; } } @@ -1408,41 +1640,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) { @@ -1486,6 +1686,22 @@ void qemu_remove_machine_init_done_notifier(Notifier *notify) notifier_remove(notify); } +static void handle_machine_dumpdtb(MachineState *ms) +{ + if (!ms->dumpdtb) { + return; + } +#ifdef CONFIG_FDT + qmp_dumpdtb(ms->dumpdtb, &error_fatal); + exit(0); +#else + error_report("This machine doesn't have an FDT"); + error_printf("(this machine type definitely doesn't use FDT, and " + "this QEMU doesn't have FDT support compiled in)\n"); + exit(1); +#endif +} + void qdev_machine_creation_done(void) { cpu_synchronize_all_post_init(); @@ -1505,17 +1721,22 @@ 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); + /* + * If the user used -machine dumpdtb=file.dtb to request that we + * dump the DTB to a file, do it now, and exit. + */ + handle_machine_dumpdtb(current_machine); + if (rom_check_and_register_reset() != 0) { exit(1); } diff --git a/hw/core/meson.build b/hw/core/meson.build index 67dad04de55..b5a545a0edd 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,22 +11,12 @@ 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')) system_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c')) system_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c')) -system_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c')) +system_ss.add(when: 'CONFIG_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')) @@ -34,11 +24,13 @@ 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]) system_ss.add(files( - 'cpu-sysemu.c', + 'cpu-system.c', 'fw-path-provider.c', 'gpio.c', + 'hotplug.c', 'loader.c', 'machine-hmp-cmds.c', 'machine-qmp-cmds.c', @@ -47,8 +39,14 @@ system_ss.add(files( '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', )) +user_ss.add(files( + 'cpu-user.c', + 'qdev-user.c', +)) diff --git a/hw/core/null-machine.c b/hw/core/null-machine.c index f586a4bef54..a6e477a2d88 100644 --- a/hw/core/null-machine.c +++ b/hw/core/null-machine.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "hw/boards.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/core/cpu.h" static void machine_none_init(MachineState *mch) @@ -53,7 +53,6 @@ static void machine_none_machine_init(MachineClass *mc) mc->no_parallel = 1; mc->no_floppy = 1; mc->no_cdrom = 1; - mc->no_sdcard = 1; } DEFINE_MACHINE("none", machine_none_machine_init) diff --git a/hw/core/numa.c b/hw/core/numa.c index f08956ddb0f..218576f7455 100644 --- a/hw/core/numa.c +++ b/hw/core/numa.c @@ -24,19 +24,17 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "sysemu/hostmem.h" -#include "sysemu/numa.h" +#include "system/hostmem.h" +#include "system/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" #include "qapi/qapi-visit-machine.h" -#include "sysemu/qtest.h" +#include "system/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" @@ -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); diff --git a/hw/core/or-irq.c b/hw/core/or-irq.c index 1df4bc05a7c..3942c709939 100644 --- a/hw/core/or-irq.c +++ b/hw/core/or-irq.c @@ -94,7 +94,7 @@ static const VMStateDescription vmstate_or_irq_extras = { .version_id = 1, .minimum_version_id = 1, .needed = vmstate_extras_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VARRAY_UINT16_UNSAFE(levels, OrIRQState, num_lines, 0, vmstate_info_bool, bool), VMSTATE_END_OF_LIST(), @@ -105,26 +105,25 @@ static const VMStateDescription vmstate_or_irq = { .name = TYPE_OR_IRQ, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .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[] = { +static const Property or_irq_properties[] = { DEFINE_PROP_UINT16("num-lines", OrIRQState, num_lines, 1), - DEFINE_PROP_END_OF_LIST(), }; -static void or_irq_class_init(ObjectClass *klass, void *data) +static void or_irq_class_init(ObjectClass *klass, const 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; diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c index b8487b26b67..6950063de4f 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); } } @@ -201,13 +204,12 @@ static void platform_bus_realize(DeviceState *dev, Error **errp) plaform_bus_refresh_irqs(pbus); } -static Property platform_bus_properties[] = { +static const Property platform_bus_properties[] = { DEFINE_PROP_UINT32("num_irqs", PlatformBusDevice, num_irqs, 0), DEFINE_PROP_UINT32("mmio_size", PlatformBusDevice, mmio_size, 0), - DEFINE_PROP_END_OF_LIST() }; -static void platform_bus_class_init(ObjectClass *klass, void *data) +static void platform_bus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index e03165febf1..0aeb10fb53e 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -11,8 +11,8 @@ #include "migration/vmstate.h" #include "qemu/host-utils.h" #include "exec/replay-core.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/qtest.h" +#include "exec/icount.h" +#include "system/qtest.h" #include "block/aio.h" #include "hw/clock.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 82799577f3e..dacafa4e036 100644 --- a/hw/core/qdev-clock.c +++ b/hw/core/qdev-clock.c @@ -22,7 +22,7 @@ * Add a new clock in a device */ static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name, - bool output, Clock *clk) + bool alias, bool output, Clock *clk) { NamedClockList *ncl; @@ -38,39 +38,8 @@ static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name, */ ncl = g_new0(NamedClockList, 1); ncl->name = g_strdup(name); + ncl->alias = alias; ncl->output = output; - ncl->alias = (clk != NULL); - - /* - * Trying to create a clock whose name clashes with some other - * clock or property is a bug in the caller and we will abort(). - */ - if (clk == NULL) { - clk = CLOCK(object_new(TYPE_CLOCK)); - object_property_add_child(OBJECT(dev), name, OBJECT(clk)); - if (output) { - /* - * Remove object_new()'s initial reference. - * Note that for inputs, the reference created by object_new() - * will be deleted in qdev_finalize_clocklist(). - */ - object_unref(OBJECT(clk)); - } - } else { - object_property_add_link(OBJECT(dev), name, - object_get_typename(OBJECT(clk)), - (Object **) &ncl->clock, - NULL, OBJ_PROP_LINK_STRONG); - /* - * Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk - * object reference count gets decremented on property deletion. - * However object_property_add_link does not increment it since it - * doesn't know the linked object. Increment it here to ensure the - * aliased clock stays alive during this device life-time. - */ - object_ref(OBJECT(clk)); - } - ncl->clock = clk; QLIST_INSERT_HEAD(&dev->clocks, ncl, node); @@ -84,14 +53,11 @@ void qdev_finalize_clocklist(DeviceState *dev) QLIST_FOREACH_SAFE(ncl, &dev->clocks, node, ncl_next) { QLIST_REMOVE(ncl, node); - if (!ncl->output && !ncl->alias) { + if (!ncl->alias) { /* * We kept a reference on the input clock to ensure it lives up to - * this point so we can safely remove the callback. - * It avoids having a callback to a deleted object if ncl->clock - * is still referenced somewhere else (eg: by a clock output). + * this point; it is used by the monitor to show the frequency. */ - clock_clear_callback(ncl->clock); object_unref(OBJECT(ncl->clock)); } g_free(ncl->name); @@ -101,29 +67,25 @@ void qdev_finalize_clocklist(DeviceState *dev) Clock *qdev_init_clock_out(DeviceState *dev, const char *name) { - NamedClockList *ncl; - - assert(name); - - ncl = qdev_init_clocklist(dev, name, true, NULL); + Clock *clk = CLOCK(object_new(TYPE_CLOCK)); + object_property_add_child(OBJECT(dev), name, OBJECT(clk)); - return ncl->clock; + qdev_init_clocklist(dev, name, false, true, clk); + return clk; } Clock *qdev_init_clock_in(DeviceState *dev, const char *name, ClockCallback *callback, void *opaque, unsigned int events) { - NamedClockList *ncl; - - assert(name); - - ncl = qdev_init_clocklist(dev, name, false, NULL); + Clock *clk = CLOCK(object_new(TYPE_CLOCK)); + object_property_add_child(OBJECT(dev), name, OBJECT(clk)); + qdev_init_clocklist(dev, name, false, false, clk); if (callback) { - clock_set_callback(ncl->clock, callback, opaque, events); + clock_set_callback(clk, callback, opaque, events); } - return ncl->clock; + return clk; } void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks) @@ -194,15 +156,25 @@ Clock *qdev_get_clock_out(DeviceState *dev, const char *name) Clock *qdev_alias_clock(DeviceState *dev, const char *name, DeviceState *alias_dev, const char *alias_name) { - NamedClockList *ncl; - - assert(name && alias_name); + NamedClockList *ncl = qdev_get_clocklist(dev, name); + Clock *clk = ncl->clock; - ncl = qdev_get_clocklist(dev, name); + ncl = qdev_init_clocklist(alias_dev, alias_name, true, ncl->output, clk); - qdev_init_clocklist(alias_dev, alias_name, ncl->output, ncl->clock); + object_property_add_link(OBJECT(alias_dev), alias_name, + TYPE_CLOCK, + (Object **) &ncl->clock, + NULL, OBJ_PROP_LINK_STRONG); + /* + * Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk + * object reference count gets decremented on property deletion. + * However object_property_add_link does not increment it since it + * doesn't know the linked object. Increment it here to ensure the + * aliased clock stays alive during this device life-time. + */ + object_ref(OBJECT(clk)); - return ncl->clock; + return clk; } void qdev_connect_clock_in(DeviceState *dev, const char *name, Clock *source) diff --git a/hw/core/qdev-hotplug.c b/hw/core/qdev-hotplug.c index d495d0e9c70..ff176dc1bb3 100644 --- a/hw/core/qdev-hotplug.c +++ b/hw/core/qdev-hotplug.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "hw/qdev-core.h" #include "hw/boards.h" +#include "qapi/error.h" HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev) { @@ -30,12 +31,48 @@ HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev) return NULL; } -bool qdev_hotplug_allowed(DeviceState *dev, Error **errp) +static bool qdev_hotplug_unplug_allowed_common(DeviceState *dev, BusState *bus, + Error **errp) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + if (!dc->hotpluggable) { + error_setg(errp, "Device '%s' does not support hotplugging", + object_get_typename(OBJECT(dev))); + return false; + } + + if (bus) { + if (!qbus_is_hotpluggable(bus)) { + error_setg(errp, "Bus '%s' does not support hotplugging", + bus->name); + return false; + } + } else { + if (!qdev_get_machine_hotplug_handler(dev)) { + /* + * No bus, no machine hotplug handler --> device is not hotpluggable + */ + error_setg(errp, + "Device '%s' can not be hotplugged on this machine", + object_get_typename(OBJECT(dev))); + return false; + } + } + + return true; +} + +bool qdev_hotplug_allowed(DeviceState *dev, BusState *bus, Error **errp) { MachineState *machine; MachineClass *mc; Object *m_obj = qdev_get_machine(); + if (!qdev_hotplug_unplug_allowed_common(dev, bus, errp)) { + return false; + } + if (object_dynamic_cast(m_obj, TYPE_MACHINE)) { machine = MACHINE(m_obj); mc = MACHINE_GET_CLASS(machine); @@ -47,6 +84,12 @@ bool qdev_hotplug_allowed(DeviceState *dev, Error **errp) return true; } +bool qdev_hotunplug_allowed(DeviceState *dev, Error **errp) +{ + return !qdev_unplug_blocked(dev, errp) && + qdev_hotplug_unplug_allowed_common(dev, dev->parent_bus, errp); +} + HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev) { if (dev->parent_bus) { diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 6d5d43eda22..24e145d8700 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" @@ -28,8 +29,8 @@ #include "audio/audio.h" #include "chardev/char-fe.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" +#include "system/block-backend.h" +#include "system/blockdev.h" #include "net/net.h" #include "hw/pci/pci.h" #include "hw/pci/pcie.h" @@ -57,13 +58,39 @@ 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 --- */ static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; void **ptr = object_field_prop_ptr(obj, prop); const char *value; char *p; @@ -89,7 +116,7 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name, void *opaque, bool iothread, Error **errp) { DeviceState *dev = DEVICE(obj); - Property *prop = opaque; + const Property *prop = opaque; void **ptr = object_field_prop_ptr(obj, prop); char *str; BlockBackend *blk; @@ -107,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,11 +145,10 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name, if (ctx != bdrv_get_aio_context(bs)) { error_setg(errp, "Different aio context is not supported for new " "node"); + return; } - aio_context_acquire(ctx); blk_replace_bs(blk, bs, errp); - aio_context_release(ctx); return; } @@ -148,10 +174,7 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name, 0, BLK_PERM_ALL); blk_created = true; - aio_context_acquire(ctx); ret = blk_insert_bs(blk, bs, errp); - aio_context_release(ctx); - if (ret < 0) { goto fail; } @@ -203,21 +226,17 @@ static void set_drive_iothread(Object *obj, Visitor *v, const char *name, static void release_drive(Object *obj, const char *name, void *opaque) { DeviceState *dev = DEVICE(obj); - Property *prop = opaque; + const Property *prop = 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); } } const PropertyInfo qdev_prop_drive = { - .name = "str", + .type = "str", .description = "Node name or ID of a block device to use as a backend", .realized_set_allowed = true, .get = get_drive, @@ -226,7 +245,7 @@ const PropertyInfo qdev_prop_drive = { }; const PropertyInfo qdev_prop_drive_iothread = { - .name = "str", + .type = "str", .description = "Node name or ID of a block device to use as a backend", .realized_set_allowed = true, .get = get_drive, @@ -250,7 +269,8 @@ 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) { - Property *prop = opaque; + ERRP_GUARD(); + const Property *prop = opaque; CharBackend *be = object_field_prop_ptr(obj, prop); Chardev *s; char *str; @@ -286,14 +306,14 @@ static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, static void release_chr(Object *obj, const char *name, void *opaque) { - Property *prop = opaque; + const Property *prop = opaque; CharBackend *be = object_field_prop_ptr(obj, prop); qemu_chr_fe_deinit(be, false); } const PropertyInfo qdev_prop_chr = { - .name = "str", + .type = "str", .description = "ID of a chardev to use as a backend", .get = get_chr, .set = set_chr, @@ -310,7 +330,7 @@ const PropertyInfo qdev_prop_chr = { static void get_mac(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; MACAddr *mac = object_field_prop_ptr(obj, prop); char buffer[2 * 6 + 5 + 1]; char *p = buffer; @@ -325,7 +345,7 @@ static void get_mac(Object *obj, Visitor *v, const char *name, void *opaque, static void set_mac(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; MACAddr *mac = object_field_prop_ptr(obj, prop); int i, pos; char *str; @@ -367,7 +387,7 @@ static void set_mac(Object *obj, Visitor *v, const char *name, void *opaque, } const PropertyInfo qdev_prop_macaddr = { - .name = "str", + .type = "str", .description = "Ethernet 6-byte MAC Address, example: 52:54:00:12:34:56", .get = get_mac, .set = set_mac, @@ -387,7 +407,7 @@ void qdev_prop_set_macaddr(DeviceState *dev, const char *name, static void get_netdev(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; NICPeers *peers_ptr = object_field_prop_ptr(obj, prop); char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : ""); @@ -398,7 +418,7 @@ static void get_netdev(Object *obj, Visitor *v, const char *name, static void set_netdev(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; NICPeers *peers_ptr = object_field_prop_ptr(obj, prop); NetClientState **ncs = peers_ptr->ncs; NetClientState *peers[MAX_QUEUE_NUM]; @@ -450,12 +470,12 @@ 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); } const PropertyInfo qdev_prop_netdev = { - .name = "str", + .type = "str", .description = "ID of a netdev to use as a backend", .get = get_netdev, .set = set_netdev, @@ -466,7 +486,7 @@ const PropertyInfo qdev_prop_netdev = { static void get_audiodev(Object *obj, Visitor *v, const char* name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; QEMUSoundCard *card = object_field_prop_ptr(obj, prop); char *p = g_strdup(audio_get_id(card)); @@ -477,31 +497,23 @@ static void get_audiodev(Object *obj, Visitor *v, const char* name, static void set_audiodev(Object *obj, Visitor *v, const char* name, void *opaque, Error **errp) { - Property *prop = opaque; + const 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 = { - .name = "str", + .type = "str", .description = "ID of an audiodev to use as a backend", /* release done on shutdown */ .get = get_audiodev, @@ -567,7 +579,7 @@ static void qdev_propinfo_set_losttickpolicy(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int *ptr = object_field_prop_ptr(obj, prop); int value; @@ -591,7 +603,8 @@ static void qdev_propinfo_set_losttickpolicy(Object *obj, Visitor *v, QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int)); const PropertyInfo qdev_prop_losttickpolicy = { - .name = "LostTickPolicy", + .type = "LostTickPolicy", + .description = "Policy for handling lost ticks (discard/delay/slew)", .enum_table = &LostTickPolicy_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_losttickpolicy, @@ -603,25 +616,21 @@ 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; + const 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; } const PropertyInfo qdev_prop_blocksize = { - .name = "size", + .type = "size", .description = "A power of two between " MIN_BLOCK_SIZE_STR " and " MAX_BLOCK_SIZE_STR, .get = qdev_propinfo_get_size32, @@ -634,9 +643,8 @@ const PropertyInfo qdev_prop_blocksize = { QEMU_BUILD_BUG_ON(sizeof(BlockdevOnError) != sizeof(int)); const PropertyInfo qdev_prop_blockdev_on_error = { - .name = "BlockdevOnError", - .description = "Error handling policy, " - "report/ignore/enospc/stop/auto", + .type = "BlockdevOnError", + .description = "Error handling policy (report/ignore/enospc/stop/auto)", .enum_table = &BlockdevOnError_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -648,9 +656,9 @@ const PropertyInfo qdev_prop_blockdev_on_error = { QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int)); const PropertyInfo qdev_prop_bios_chs_trans = { - .name = "BiosAtaTranslation", - .description = "Logical CHS translation algorithm, " - "auto/none/lba/large/rechs", + .type = "BiosAtaTranslation", + .description = "Logical CHS translation algorithm " + " (auto/none/lba/large/rechs)", .enum_table = &BiosAtaTranslation_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -660,9 +668,8 @@ const PropertyInfo qdev_prop_bios_chs_trans = { /* --- FDC default drive types */ const PropertyInfo qdev_prop_fdc_drive_type = { - .name = "FdcDriveType", - .description = "FDC drive type, " - "144/288/120/none/auto", + .type = "FloppyDriveType", + .description = "Floppy drive type (144/288/120/none/auto)", .enum_table = &FloppyDriveType_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -672,15 +679,50 @@ const PropertyInfo qdev_prop_fdc_drive_type = { /* --- MultiFDCompression --- */ const PropertyInfo qdev_prop_multifd_compression = { - .name = "MultiFDCompression", - .description = "multifd_compression values, " - "none/zlib/zstd", + .type = "MultiFDCompression", + .description = "multifd_compression values" + " (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 = { + .type = "MigMode", + .description = "Migration mode (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 = { + .type = "GranuleMode", + .description = "Granule page size (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 = { + .type = "ZeroPageDetection", + .description = "Zero page detection (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 --- */ /* @@ -692,14 +734,14 @@ const PropertyInfo qdev_prop_multifd_compression = { static void get_reserved_region(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; ReservedRegion *rr = object_field_prop_ptr(obj, prop); char buffer[64]; char *p = buffer; 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); @@ -708,9 +750,10 @@ static void get_reserved_region(Object *obj, Visitor *v, const char *name, static void set_reserved_region(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; ReservedRegion *rr = object_field_prop_ptr(obj, prop); const char *endptr; + uint64_t lob, upb; char *str; int ret; @@ -718,7 +761,7 @@ static void set_reserved_region(Object *obj, Visitor *v, const char *name, 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); @@ -728,7 +771,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); @@ -738,6 +781,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'" @@ -749,11 +794,10 @@ static void set_reserved_region(Object *obj, Visitor *v, const char *name, error_setg(errp, "reserved region fields must be separated with ':'"); out: g_free(str); - return; } const PropertyInfo qdev_prop_reserved_region = { - .name = "reserved_region", + .type = "str", .description = "Reserved Region, example: 0xFEE00000:0xFEEFFFFF:0", .get = get_reserved_region, .set = set_reserved_region, @@ -767,43 +811,61 @@ const PropertyInfo qdev_prop_reserved_region = { static void set_pci_devfn(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const 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_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; - if (!visit_type_str(v, name, &str, NULL)) { + 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; - 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; + default: + error_setg(errp, "Invalid parameter type for '%s', expected int or str", + name ? name : "null"); + goto out; } - *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, +static int print_pci_devfn(Object *obj, const Property *prop, char *dest, size_t len) { int32_t *ptr = object_field_prop_ptr(obj, prop); @@ -816,7 +878,7 @@ static int print_pci_devfn(Object *obj, Property *prop, char *dest, } const PropertyInfo qdev_prop_pci_devfn = { - .name = "int32", + .type = "str", .description = "Slot and optional function number, example: 06.0 or 06", .print = print_pci_devfn, .get = qdev_propinfo_get_int32, @@ -829,7 +891,7 @@ const PropertyInfo qdev_prop_pci_devfn = { static void get_pci_host_devaddr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIHostDeviceAddress *addr = object_field_prop_ptr(obj, prop); char buffer[] = "ffff:ff:ff.f"; char *p = buffer; @@ -855,7 +917,7 @@ static void get_pci_host_devaddr(Object *obj, Visitor *v, const char *name, static void set_pci_host_devaddr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIHostDeviceAddress *addr = object_field_prop_ptr(obj, prop); char *str, *p; char *e; @@ -922,8 +984,8 @@ static void set_pci_host_devaddr(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_pci_host_devaddr = { - .name = "str", - .description = "Address (bus/device/function) of " + .type = "str", + .description = "Address (bus:device.function) of " "the host device, example: 04:10.0", .get = get_pci_host_devaddr, .set = set_pci_host_devaddr, @@ -932,7 +994,7 @@ const PropertyInfo qdev_prop_pci_host_devaddr = { /* --- OffAutoPCIBAR off/auto/bar0/bar1/bar2/bar3/bar4/bar5 --- */ const PropertyInfo qdev_prop_off_auto_pcibar = { - .name = "OffAutoPCIBAR", + .type = "OffAutoPCIBAR", .description = "off/auto/bar0/bar1/bar2/bar3/bar4/bar5", .enum_table = &OffAutoPCIBAR_lookup, .get = qdev_propinfo_get_enum, @@ -940,12 +1002,12 @@ 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) { - Property *prop = opaque; + const Property *prop = opaque; PCIExpLinkSpeed *p = object_field_prop_ptr(obj, prop); int speed; @@ -962,6 +1024,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(); @@ -973,7 +1041,7 @@ static void get_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, static void set_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIExpLinkSpeed *p = object_field_prop_ptr(obj, prop); int speed; @@ -995,6 +1063,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(); @@ -1002,8 +1076,8 @@ 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", + .type = "PCIELinkSpeed", + .description = "2_5/5/8/16/32/64", .enum_table = &PCIELinkSpeed_lookup, .get = get_prop_pcielinkspeed, .set = set_prop_pcielinkspeed, @@ -1015,7 +1089,7 @@ const PropertyInfo qdev_prop_pcie_link_speed = { static void get_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIExpLinkWidth *p = object_field_prop_ptr(obj, prop); int width; @@ -1052,7 +1126,7 @@ static void get_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, static void set_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIExpLinkWidth *p = object_field_prop_ptr(obj, prop); int width; @@ -1090,7 +1164,7 @@ static void set_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_pcie_link_width = { - .name = "PCIELinkWidth", + .type = "PCIELinkWidth", .description = "1/2/4/8/12/16/32", .enum_table = &PCIELinkWidth_lookup, .get = get_prop_pcielinkwidth, @@ -1103,9 +1177,9 @@ const PropertyInfo qdev_prop_pcie_link_width = { static void get_uuid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const 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); @@ -1118,7 +1192,7 @@ static void get_uuid(Object *obj, Visitor *v, const char *name, void *opaque, static void set_uuid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; QemuUUID *uuid = object_field_prop_ptr(obj, prop); char *str; @@ -1140,10 +1214,88 @@ static void set_default_uuid_auto(ObjectProperty *op, const Property *prop) } const PropertyInfo qdev_prop_uuid = { - .name = "str", + .type = "str", .description = "UUID (aka GUID) or \"" UUID_VALUE_AUTO "\" for random value (default)", .get = get_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 = { + .type = "S390CpuEntitlement", + .description = "auto/low/medium/high (default medium)", + .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 = { + .type = "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, +}; + +/* --- Endian modes */ + +const PropertyInfo qdev_prop_endian_mode = { + .type = "EndianMode", + .description = "Endian mode, big/little/unspecified", + .enum_table = &EndianMode_lookup, + .get = qdev_propinfo_get_enum, + .set = qdev_propinfo_set_enum, + .set_default_value = qdev_propinfo_set_default_value_enum, +}; + +const PropertyInfo qdev_prop_vmapple_virtio_blk_variant = { + .type = "VMAppleVirtioBlkVariant", + .description = "unspecified/root/aux", + .enum_table = &VMAppleVirtioBlkVariant_lookup, + .get = qdev_propinfo_get_enum, + .set = qdev_propinfo_set_enum, + .set_default_value = qdev_propinfo_set_default_value_enum, +}; diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 357b8761b54..147b3ffd16e 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 "qobject/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) @@ -50,7 +51,7 @@ void qdev_prop_allow_set_link_before_realize(const Object *obj, } } -void *object_field_prop_ptr(Object *obj, Property *prop) +void *object_field_prop_ptr(Object *obj, const Property *prop) { void *ptr = obj; ptr += prop->offset; @@ -60,7 +61,7 @@ void *object_field_prop_ptr(Object *obj, Property *prop) static void field_prop_get(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; return prop->info->get(obj, v, name, opaque, errp); } @@ -77,7 +78,7 @@ static ObjectPropertyAccessor *field_prop_getter(const PropertyInfo *info) static void field_prop_set(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; if (!qdev_prop_allow_set(obj, name, prop->info, errp)) { return; @@ -99,7 +100,7 @@ static ObjectPropertyAccessor *field_prop_setter(const PropertyInfo *info) void qdev_propinfo_get_enum(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int *ptr = object_field_prop_ptr(obj, prop); visit_type_enum(v, name, ptr, prop->info->enum_table, errp); @@ -108,7 +109,7 @@ void qdev_propinfo_get_enum(Object *obj, Visitor *v, const char *name, void qdev_propinfo_set_enum(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int *ptr = object_field_prop_ptr(obj, prop); visit_type_enum(v, name, ptr, prop->info->enum_table, errp); @@ -121,22 +122,15 @@ void qdev_propinfo_set_default_value_enum(ObjectProperty *op, qapi_enum_lookup(prop->info->enum_table, prop->defval.i)); } -const PropertyInfo qdev_prop_enum = { - .name = "enum", - .get = qdev_propinfo_get_enum, - .set = qdev_propinfo_set_enum, - .set_default_value = qdev_propinfo_set_default_value_enum, -}; - /* Bit */ -static uint32_t qdev_get_prop_mask(Property *prop) +static uint32_t qdev_get_prop_mask(const Property *prop) { assert(prop->info == &qdev_prop_bit); return 0x1 << prop->bitnr; } -static void bit_prop_set(Object *obj, Property *props, bool val) +static void bit_prop_set(Object *obj, const Property *props, bool val) { uint32_t *p = object_field_prop_ptr(obj, props); uint32_t mask = qdev_get_prop_mask(props); @@ -150,7 +144,7 @@ static void bit_prop_set(Object *obj, Property *props, bool val) static void prop_get_bit(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *p = object_field_prop_ptr(obj, prop); bool value = (*p & qdev_get_prop_mask(prop)) != 0; @@ -160,7 +154,7 @@ static void prop_get_bit(Object *obj, Visitor *v, const char *name, static void prop_set_bit(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; bool value; if (!visit_type_bool(v, name, &value, errp)) { @@ -175,7 +169,7 @@ static void set_default_value_bool(ObjectProperty *op, const Property *prop) } const PropertyInfo qdev_prop_bit = { - .name = "bool", + .type = "bool", .description = "on/off", .get = prop_get_bit, .set = prop_set_bit, @@ -184,13 +178,13 @@ const PropertyInfo qdev_prop_bit = { /* Bit64 */ -static uint64_t qdev_get_prop_mask64(Property *prop) +static uint64_t qdev_get_prop_mask64(const Property *prop) { assert(prop->info == &qdev_prop_bit64); return 0x1ull << prop->bitnr; } -static void bit64_prop_set(Object *obj, Property *props, bool val) +static void bit64_prop_set(Object *obj, const Property *props, bool val) { uint64_t *p = object_field_prop_ptr(obj, props); uint64_t mask = qdev_get_prop_mask64(props); @@ -204,7 +198,7 @@ static void bit64_prop_set(Object *obj, Property *props, bool val) static void prop_get_bit64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *p = object_field_prop_ptr(obj, prop); bool value = (*p & qdev_get_prop_mask64(prop)) != 0; @@ -214,7 +208,7 @@ static void prop_get_bit64(Object *obj, Visitor *v, const char *name, static void prop_set_bit64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; bool value; if (!visit_type_bool(v, name, &value, errp)) { @@ -224,7 +218,7 @@ static void prop_set_bit64(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_bit64 = { - .name = "bool", + .type = "bool", .description = "on/off", .get = prop_get_bit64, .set = prop_set_bit64, @@ -236,7 +230,7 @@ const PropertyInfo qdev_prop_bit64 = { static void get_bool(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; bool *ptr = object_field_prop_ptr(obj, prop); visit_type_bool(v, name, ptr, errp); @@ -245,14 +239,15 @@ static void get_bool(Object *obj, Visitor *v, const char *name, void *opaque, static void set_bool(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; bool *ptr = object_field_prop_ptr(obj, prop); visit_type_bool(v, name, ptr, errp); } const PropertyInfo qdev_prop_bool = { - .name = "bool", + .type = "bool", + .description = "on/off", .get = get_bool, .set = set_bool, .set_default_value = set_default_value_bool, @@ -263,7 +258,7 @@ const PropertyInfo qdev_prop_bool = { static void get_uint8(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint8_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint8(v, name, ptr, errp); @@ -272,7 +267,7 @@ static void get_uint8(Object *obj, Visitor *v, const char *name, void *opaque, static void set_uint8(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint8_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint8(v, name, ptr, errp); @@ -291,7 +286,7 @@ void qdev_propinfo_set_default_value_uint(ObjectProperty *op, } const PropertyInfo qdev_prop_uint8 = { - .name = "uint8", + .type = "uint8", .get = get_uint8, .set = set_uint8, .set_default_value = qdev_propinfo_set_default_value_uint, @@ -302,7 +297,7 @@ const PropertyInfo qdev_prop_uint8 = { static void get_uint16(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint16_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint16(v, name, ptr, errp); @@ -311,14 +306,14 @@ static void get_uint16(Object *obj, Visitor *v, const char *name, static void set_uint16(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint16_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint16(v, name, ptr, errp); } const PropertyInfo qdev_prop_uint16 = { - .name = "uint16", + .type = "uint16", .get = get_uint16, .set = set_uint16, .set_default_value = qdev_propinfo_set_default_value_uint, @@ -329,7 +324,7 @@ const PropertyInfo qdev_prop_uint16 = { static void get_uint32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint32(v, name, ptr, errp); @@ -338,7 +333,7 @@ static void get_uint32(Object *obj, Visitor *v, const char *name, static void set_uint32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint32(v, name, ptr, errp); @@ -347,7 +342,7 @@ static void set_uint32(Object *obj, Visitor *v, const char *name, void qdev_propinfo_get_int32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int32_t *ptr = object_field_prop_ptr(obj, prop); visit_type_int32(v, name, ptr, errp); @@ -356,21 +351,21 @@ void qdev_propinfo_get_int32(Object *obj, Visitor *v, const char *name, static void set_int32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int32_t *ptr = object_field_prop_ptr(obj, prop); visit_type_int32(v, name, ptr, errp); } const PropertyInfo qdev_prop_uint32 = { - .name = "uint32", + .type = "uint32", .get = get_uint32, .set = set_uint32, .set_default_value = qdev_propinfo_set_default_value_uint, }; const PropertyInfo qdev_prop_int32 = { - .name = "int32", + .type = "int32", .get = qdev_propinfo_get_int32, .set = set_int32, .set_default_value = qdev_propinfo_set_default_value_int, @@ -381,7 +376,7 @@ const PropertyInfo qdev_prop_int32 = { static void get_uint64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint64(v, name, ptr, errp); @@ -390,7 +385,7 @@ static void get_uint64(Object *obj, Visitor *v, const char *name, static void set_uint64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint64(v, name, ptr, errp); @@ -399,7 +394,7 @@ static void set_uint64(Object *obj, Visitor *v, const char *name, static void get_int64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_int64(v, name, ptr, errp); @@ -408,21 +403,21 @@ static void get_int64(Object *obj, Visitor *v, const char *name, static void set_int64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_int64(v, name, ptr, errp); } const PropertyInfo qdev_prop_uint64 = { - .name = "uint64", + .type = "uint64", .get = get_uint64, .set = set_uint64, .set_default_value = qdev_propinfo_set_default_value_uint, }; const PropertyInfo qdev_prop_int64 = { - .name = "int64", + .type = "int64", .get = get_int64, .set = set_int64, .set_default_value = qdev_propinfo_set_default_value_int, @@ -431,7 +426,7 @@ const PropertyInfo qdev_prop_int64 = { static void set_uint64_checkmask(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint64(v, name, ptr, errp); @@ -442,23 +437,60 @@ static void set_uint64_checkmask(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_uint64_checkmask = { - .name = "uint64", + .type = "uint64", .get = get_uint64, .set = set_uint64_checkmask, }; +/* --- pointer-size integer --- */ + +static void get_usize(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + const Property *prop = opaque; + +#if HOST_LONG_BITS == 32 + uint32_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint32(v, name, ptr, errp); +#else + uint64_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint64(v, name, ptr, errp); +#endif +} + +static void set_usize(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + const Property *prop = opaque; + +#if HOST_LONG_BITS == 32 + uint32_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint32(v, name, ptr, errp); +#else + uint64_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint64(v, name, ptr, errp); +#endif +} + +const PropertyInfo qdev_prop_usize = { + .type = "usize", + .get = get_usize, + .set = set_usize, + .set_default_value = qdev_propinfo_set_default_value_uint, +}; + /* --- string --- */ static void release_string(Object *obj, const char *name, void *opaque) { - Property *prop = opaque; + const Property *prop = opaque; g_free(*(char **)object_field_prop_ptr(obj, prop)); } static void get_string(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; char **ptr = object_field_prop_ptr(obj, prop); if (!*ptr) { @@ -472,7 +504,7 @@ static void get_string(Object *obj, Visitor *v, const char *name, static void set_string(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; char **ptr = object_field_prop_ptr(obj, prop); char *str; @@ -484,7 +516,7 @@ static void set_string(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_string = { - .name = "str", + .type = "str", .release = release_string, .get = get_string, .set = set_string, @@ -493,7 +525,7 @@ const PropertyInfo qdev_prop_string = { /* --- on/off/auto --- */ const PropertyInfo qdev_prop_on_off_auto = { - .name = "OnOffAuto", + .type = "OnOffAuto", .description = "on/off/auto", .enum_table = &OnOffAuto_lookup, .get = qdev_propinfo_get_enum, @@ -506,7 +538,7 @@ const PropertyInfo qdev_prop_on_off_auto = { void qdev_propinfo_get_size32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); uint64_t value = *ptr; @@ -516,7 +548,7 @@ void qdev_propinfo_get_size32(Object *obj, Visitor *v, const char *name, static void set_size32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); uint64_t value; @@ -536,7 +568,7 @@ static void set_size32(Object *obj, Visitor *v, const char *name, void *opaque, } const PropertyInfo qdev_prop_size32 = { - .name = "size", + .type = "size", .get = qdev_propinfo_get_size32, .set = set_size32, .set_default_value = qdev_propinfo_set_default_value_uint, @@ -544,125 +576,230 @@ 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 ArrayElementList ArrayElementList; + +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. */ -typedef struct { - struct Property prop; - char *propname; - ObjectPropertyRelease *release; -} ArrayElementProperty; - -/* 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. +static Property array_elem_prop(Object *obj, const Property *parent_prop, + const char *name, char *elem) +{ + 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, + }; +} + +/* + * 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 array_element_release(Object *obj, const char *name, void *opaque) +static void release_prop_array(Object *obj, const char *name, void *opaque) { - ArrayElementProperty *p = opaque; - if (p->release) { - p->release(obj, name, opaque); + const Property *prop = opaque; + uint32_t *alenptr = object_field_prop_ptr(obj, prop); + void **arrayptr = (void *)obj + prop->arrayoffset; + 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; } - g_free(p->propname); - g_free(p); } -static void set_prop_arraylen(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +/* + * 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) { - /* 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; + ERRP_GUARD(); + const Property *prop = opaque; uint32_t *alenptr = object_field_prop_ptr(obj, prop); void **arrayptr = (void *)obj + prop->arrayoffset; - void *eltptr; - const char *arrayname; - int i; + 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)) { + + if (!visit_start_list(v, name, (GenericList **) &list, size, errp)) { return; } - if (!*alenptr) { + + /* Read the whole input into a temporary list */ + elem = list; + while (elem) { + Property elem_prop; + + 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; } - /* DEFINE_PROP_ARRAY guarantees that name should start with this prefix; - * strip it off so we can get the name of the array itself. + /* + * Now that we know how big the array has to be, move the data over to a + * linear array and free the temporary list. */ - assert(strncmp(name, PROP_ARRAY_LEN_PREFIX, - strlen(PROP_ARRAY_LEN_PREFIX)) == 0); - arrayname = name + strlen(PROP_ARRAY_LEN_PREFIX); + *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); + } +} - /* Note that it is the responsibility of the individual device's deinit - * to free the array proper. - */ - *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); +static void get_prop_array(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ERRP_GUARD(); + const 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); } } -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 default_prop_array(ObjectProperty *op, const Property *prop) +{ + object_property_set_default_list(op); +} + +const PropertyInfo qdev_prop_array = { + .type = "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(DeviceClass *cls, const char *name) { - if (!props) { - return NULL; - } - while (props->name) { - if (strcmp(props->name, name) == 0) { - return props; + for (int i = 0, n = cls->props_count_; i < n; ++i) { + const Property *prop = &cls->props_[i]; + if (strcmp(prop->name, name) == 0) { + return prop; } - props++; } 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)); do { - prop = qdev_prop_walk(DEVICE_CLASS(class)->props_, name); + prop = qdev_prop_walk(DEVICE_CLASS(class), name); if (prop) { return prop; } @@ -682,7 +819,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 +868,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 +876,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; @@ -815,7 +959,7 @@ void qdev_prop_set_globals(DeviceState *dev) static void get_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_size(v, name, ptr, errp); @@ -824,14 +968,14 @@ static void get_size(Object *obj, Visitor *v, const char *name, void *opaque, static void set_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_size(v, name, ptr, errp); } const PropertyInfo qdev_prop_size = { - .name = "size", + .type = "size", .get = get_size, .set = set_size, .set_default_value = qdev_propinfo_set_default_value_uint, @@ -840,7 +984,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, @@ -849,22 +993,22 @@ static ObjectProperty *create_link_property(ObjectClass *oc, const char *name, } const PropertyInfo qdev_prop_link = { - .name = "link", + .type = "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; assert(!prop->info->create); - op = object_property_add(obj, prop->name, prop->info->name, + op = object_property_add(obj, prop->name, prop->info->type, 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 +1022,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; @@ -887,11 +1031,11 @@ static void qdev_class_add_property(DeviceClass *klass, const char *name, op = prop->info->create(oc, name, prop); } else { op = object_class_property_add(oc, - name, prop->info->name, + name, prop->info->type, 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); @@ -907,7 +1051,7 @@ static void qdev_get_legacy_property(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; char buffer[1024]; char *ptr = buffer; @@ -930,7 +1074,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,15 +1086,21 @@ 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_n(DeviceClass *dc, const Property *props, size_t n) { - Property *prop; + /* We used a hole in DeviceClass because that's still a lot. */ + assert(n <= UINT16_MAX); + assert(n != 0); dc->props_ = props; - for (prop = props; prop && prop->name; prop++) { + dc->props_count_ = n; + + for (size_t i = 0; i < n; ++i) { + const Property *prop = &props[i]; + assert(prop->name); qdev_class_add_legacy_property(dc, prop); qdev_class_add_property(dc, prop->name, prop); } @@ -959,16 +1109,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-user.c b/hw/core/qdev-user.c new file mode 100644 index 00000000000..3d421d8f4e5 --- /dev/null +++ b/hw/core/qdev-user.c @@ -0,0 +1,19 @@ +/* + * QDev helpers specific to user emulation. + * + * Copyright 2025 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "qom/object.h" +#include "hw/qdev-core.h" + +void qdev_create_fake_machine(void) +{ + Object *fake_machine_obj; + + fake_machine_obj = object_property_add_new_container(object_get_root(), + "machine"); + object_property_add_new_container(fake_machine_obj, "unattached"); +} diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 43d863b0c5b..f6002261768 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -28,8 +28,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qapi-events-qdev.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.h" +#include "qobject/qdict.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 @@ -479,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; } @@ -491,8 +476,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp) if (!obj->parent) { gchar *name = g_strdup_printf("device[%d]", unattached_count++); - object_property_add_child(container_get(qdev_get_machine(), - "/unattached"), + object_property_add_child(machine_get_container("unattached"), name, obj); unattached_parent = true; g_free(name); @@ -706,11 +690,10 @@ static void device_finalize(Object *obj) dev->canonical_path = NULL; } - qobject_unref(dev->opts); g_free(dev->id); } -static void device_class_base_init(ObjectClass *class, void *data) +static void device_class_base_init(ObjectClass *class, const void *data) { DeviceClass *klass = DEVICE_CLASS(class); @@ -718,6 +701,7 @@ static void device_class_base_init(ObjectClass *class, void *data) * so do not propagate them to the subclasses. */ klass->props_ = NULL; + klass->props_count_ = 0; } static void device_unparent(Object *obj) @@ -747,58 +731,7 @@ 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) +static void device_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); VMStateIfClass *vc = VMSTATE_IF_CLASS(class); @@ -819,20 +752,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); @@ -844,12 +769,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, @@ -873,12 +816,36 @@ Object *qdev_get_machine(void) static Object *dev; if (dev == NULL) { - dev = container_get(object_get_root(), "/machine"); + dev = object_resolve_path_component(object_get_root(), "machine"); + /* + * Any call to this function before machine is created is treated + * as a programming error as of now. + */ + assert(dev); } return dev; } +Object *machine_get_container(const char *name) +{ + Object *container, *machine; + + machine = qdev_get_machine(); + container = object_resolve_path_component(machine, name); + assert(object_dynamic_cast(container, TYPE_CONTAINER)); + + return container; +} + +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) @@ -903,7 +870,7 @@ static const TypeInfo device_type_info = { .class_init = device_class_init, .abstract = true, .class_size = sizeof(DeviceClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_VMSTATE_IF }, { TYPE_RESETTABLE_INTERFACE }, { } diff --git a/hw/core/register.c b/hw/core/register.c index 95b0150c0aa..8f63d9f227c 100644 --- a/hw/core/register.c +++ b/hw/core/register.c @@ -319,7 +319,7 @@ void register_finalize_block(RegisterInfoArray *r_array) g_free(r_array); } -static void register_class_init(ObjectClass *oc, void *data) +static void register_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/core/reset.c b/hw/core/reset.c index d3263b613e6..65f82fa43d9 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 "system/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; + + if (!root_reset_container) { + root_reset_container = + RESETTABLE_CONTAINER(object_new(TYPE_RESETTABLE_CONTAINER)); + } + return root_reset_container; +} -typedef struct QEMUResetEntry { - QTAILQ_ENTRY(QEMUResetEntry) entry; +/* + * 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; +}; + +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, const void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); -static QTAILQ_HEAD(, QEMUResetEntry) reset_handlers = - QTAILQ_HEAD_INITIALIZER(reset_handlers); + 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); } -void qemu_unregister_reset(QEMUResetHandler *func, void *opaque) +typedef struct FindLegacyInfo { + QEMUResetHandler *func; + void *opaque; + LegacyReset *lr; +} FindLegacyInfo; + +static void find_legacy_reset_cb(Object *obj, void *opaque, ResetType type) { - QEMUResetEntry *re; - - QTAILQ_FOREACH(re, &reset_handlers, entry) { - if (re->func == func && re->opaque == opaque) { - QTAILQ_REMOVE(&reset_handlers, re, entry); - g_free(re); - return; - } + 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; } } -void qemu_devices_reset(ShutdownCause reason) +static LegacyReset *find_legacy_reset(QEMUResetHandler *func, void *opaque) { - 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); + /* + * 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) +{ + Object *obj = OBJECT(find_legacy_reset(func, opaque)); + + if (obj) { + qemu_unregister_resettable(obj); + object_unref(obj); } } +void qemu_register_resettable(Object *obj) +{ + 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 00000000000..5ff17002e75 --- /dev/null +++ b/hw/core/resetcontainer.c @@ -0,0 +1,78 @@ +/* + * 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, + const 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 c3df75c6ba8..5cdb4a4f8d3 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/split-irq.c b/hw/core/split-irq.c index 3b90af2e8f9..f8b48750c5b 100644 --- a/hw/core/split-irq.c +++ b/hw/core/split-irq.c @@ -59,12 +59,11 @@ static void split_irq_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(dev, s->out_irq, s->num_lines); } -static Property split_irq_properties[] = { +static const Property split_irq_properties[] = { DEFINE_PROP_UINT16("num-lines", SplitIRQ, num_lines, 1), - DEFINE_PROP_END_OF_LIST(), }; -static void split_irq_class_init(ObjectClass *klass, void *data) +static void split_irq_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c index eebcd28f9a3..c339a27875c 100644 --- a/hw/core/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -29,13 +29,15 @@ #endif #include "hw/core/sysbus-fdt.h" #include "qemu/error-report.h" -#include "sysemu/device_tree.h" -#include "sysemu/tpm.h" +#include "system/device_tree.h" +#include "system/tpm.h" #include "hw/platform-bus.h" #include "hw/vfio/vfio-platform.h" #include "hw/vfio/vfio-calxeda-xgmac.h" #include "hw/vfio/vfio-amd-xgbe.h" +#include "hw/vfio/vfio-region.h" #include "hw/display/ramfb.h" +#include "hw/uefi/var-service-api.h" #include "hw/arm/fdt.h" /* @@ -471,6 +473,28 @@ static int add_tpm_tis_fdt_node(SysBusDevice *sbdev, void *opaque) } #endif +static int add_uefi_vars_node(SysBusDevice *sbdev, void *opaque) +{ + PlatformBusFDTData *data = opaque; + PlatformBusDevice *pbus = data->pbus; + const char *parent_node = data->pbus_node_name; + void *fdt = data->fdt; + uint64_t mmio_base; + char *nodename; + + mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); + nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, + UEFI_VARS_FDT_NODE, mmio_base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, + "compatible", UEFI_VARS_FDT_COMPAT); + qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", + 1, mmio_base, + 1, UEFI_VARS_REGS_SIZE); + g_free(nodename); + return 0; +} + static int no_fdt_node(SysBusDevice *sbdev, void *opaque) { return 0; @@ -495,6 +519,7 @@ static const BindingEntry bindings[] = { TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node), #endif TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node), + TYPE_BINDING(TYPE_UEFI_VARS_SYSBUS, add_uefi_vars_node), TYPE_BINDING("", NULL), /* last element */ }; diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index 35f902b582b..e71367adfb1 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -19,10 +19,9 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qemu/module.h" #include "hw/sysbus.h" #include "monitor/monitor.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent); static char *sysbus_get_fw_dev_path(DeviceState *dev); @@ -65,14 +64,14 @@ void foreach_dynamic_sysbus_device(FindSysbusDeviceFunc *func, void *opaque) }; /* Loop through all sysbus devices that were spawned outside the machine */ - container = container_get(qdev_get_machine(), "/peripheral"); + container = machine_get_container("peripheral"); find_sysbus_device(container, &find); - container = container_get(qdev_get_machine(), "/peripheral-anon"); + container = machine_get_container("peripheral-anon"); find_sysbus_device(container, &find); } -static void system_bus_class_init(ObjectClass *klass, void *data) +static void system_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); @@ -80,13 +79,6 @@ static void system_bus_class_init(ObjectClass *klass, void *data) k->get_fw_dev_path = sysbus_get_fw_dev_path; } -static const TypeInfo system_bus_info = { - .name = TYPE_SYSTEM_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(BusState), - .class_init = system_bus_class_init, -}; - /* Check whether an IRQ source exists */ bool sysbus_has_irq(SysBusDevice *dev, int n) { @@ -154,16 +146,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); @@ -298,18 +280,7 @@ 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) +static void sysbus_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->realize = sysbus_device_realize; @@ -327,15 +298,6 @@ static void sysbus_device_class_init(ObjectClass *klass, void *data) k->user_creatable = false; } -static const TypeInfo sysbus_device_type_info = { - .name = TYPE_SYS_BUS_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(SysBusDevice), - .abstract = true, - .class_size = sizeof(SysBusDeviceClass), - .class_init = sysbus_device_class_init, -}; - static BusState *main_system_bus; static void main_system_bus_create(void) @@ -344,8 +306,8 @@ static void main_system_bus_create(void) * assign main_system_bus before qbus_init() * in order to make "if (bus != sysbus_get_default())" work */ - main_system_bus = g_malloc0(system_bus_info.instance_size); - qbus_init(main_system_bus, system_bus_info.instance_size, + main_system_bus = g_new0(BusState, 1); + qbus_init(main_system_bus, sizeof(BusState), TYPE_SYSTEM_BUS, NULL, "main-system-bus"); OBJECT(main_system_bus)->free = g_free; } @@ -358,10 +320,36 @@ BusState *sysbus_get_default(void) return main_system_bus; } -static void sysbus_register_types(void) +static void dynamic_sysbus_device_class_init(ObjectClass *klass, + const void *data) { - type_register_static(&system_bus_info); - type_register_static(&sysbus_device_type_info); + DeviceClass *k = DEVICE_CLASS(klass); + + k->user_creatable = true; + k->hotpluggable = false; } -type_init(sysbus_register_types) +static const TypeInfo sysbus_types[] = { + { + .name = TYPE_SYSTEM_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(BusState), + .class_init = system_bus_class_init, + }, + { + .name = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SysBusDevice), + .abstract = true, + .class_size = sizeof(SysBusDeviceClass), + .class_init = sysbus_device_class_init, + }, + { + .name = TYPE_DYNAMIC_SYS_BUS_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .class_init = dynamic_sysbus_device_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(sysbus_types) diff --git a/hw/core/uboot_image.h b/hw/core/uboot_image.h index 18ac293359d..e4dcfb08f0c 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 8e2639224e7..99c642b5587 100644 --- a/hw/core/vm-change-state-handler.c +++ b/hw/core/vm-change-state-handler.c @@ -17,7 +17,7 @@ #include "qemu/osdep.h" #include "hw/qdev-core.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" static int qdev_get_dev_tree_depth(DeviceState *dev) { @@ -40,6 +40,7 @@ static int qdev_get_dev_tree_depth(DeviceState *dev) * qdev_add_vm_change_state_handler: * @dev: the device that owns this handler * @cb: the callback function to be invoked + * @cb_ret: the callback function with return value to be invoked * @opaque: user data passed to the callback function * * This function works like qemu_add_vm_change_state_handler() except callbacks @@ -50,25 +51,30 @@ static int qdev_get_dev_tree_depth(DeviceState *dev) * controller's callback is invoked before the children on its bus when the VM * starts running. The order is reversed when the VM stops running. * + * Note that the parameter `cb` and `cb_ret` are mutually exclusive. + * * Returns: an entry to be freed with qemu_del_vm_change_state_handler() */ VMChangeStateEntry *qdev_add_vm_change_state_handler(DeviceState *dev, VMChangeStateHandler *cb, + VMChangeStateHandlerWithRet *cb_ret, void *opaque) { - return qdev_add_vm_change_state_handler_full(dev, cb, NULL, opaque); + assert(!cb || !cb_ret); + return qdev_add_vm_change_state_handler_full(dev, cb, NULL, cb_ret, opaque); } /* * Exactly like qdev_add_vm_change_state_handler() but passes a prepare_cb - * argument too. + * and the cb_ret arguments too. */ VMChangeStateEntry *qdev_add_vm_change_state_handler_full( - DeviceState *dev, VMChangeStateHandler *cb, - VMChangeStateHandler *prepare_cb, void *opaque) + DeviceState *dev, VMChangeStateHandler *cb, VMChangeStateHandler *prepare_cb, + VMChangeStateHandlerWithRet *cb_ret, void *opaque) { int depth = qdev_get_dev_tree_depth(dev); - return qemu_add_vm_change_state_handler_prio_full(cb, prepare_cb, opaque, - depth); + assert(!cb || !cb_ret); + return qemu_add_vm_change_state_handler_prio_full(cb, prepare_cb, cb_ret, + opaque, depth); } diff --git a/hw/cpu/Kconfig b/hw/cpu/Kconfig index 1767d028ac2..baff478e1be 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 774ca9987a1..bd36dd94d4a 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -24,8 +24,9 @@ #include "hw/cpu/a15mpcore.h" #include "hw/irq.h" #include "hw/qdev-properties.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_arm.h" +#include "target/arm/gtimer.h" static void a15mp_priv_set_irq(void *opaque, int irq, int level) { @@ -57,6 +58,11 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) bool has_el2 = false; Object *cpuobj; + if (s->num_irq < 32 || s->num_irq > 256) { + error_setg(errp, "Property 'num-irq' must be between 32 and 256"); + return; + } + gicdev = DEVICE(&s->gic); qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu); qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq); @@ -143,38 +149,35 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) } } -static Property a15mp_priv_properties[] = { +static const Property a15mp_priv_properties[] = { DEFINE_PROP_UINT32("num-cpu", A15MPPrivState, num_cpu, 1), - /* The Cortex-A15MP may have anything from 0 to 224 external interrupt - * IRQ lines (with another 32 internal). We default to 128+32, which - * is the number provided by the Cortex-A15MP test chip in the - * Versatile Express A15 development board. - * Other boards may differ and should set this property appropriately. + /* + * The Cortex-A15MP may have anything from 0 to 224 external interrupt + * lines, plus always 32 internal IRQs. This property sets the total + * of internal + external, so the valid range is from 32 to 256. + * The board model must set this to whatever the configuration + * used for the CPU on that board or SoC is. */ - DEFINE_PROP_UINT32("num-irq", A15MPPrivState, num_irq, 160), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_UINT32("num-irq", A15MPPrivState, num_irq, 0), }; -static void a15mp_priv_class_init(ObjectClass *klass, void *data) +static void a15mp_priv_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 = { - .name = TYPE_A15MPCORE_PRIV, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(A15MPPrivState), - .instance_init = a15mp_priv_initfn, - .class_init = a15mp_priv_class_init, +static const TypeInfo a15mp_types[] = { + { + .name = TYPE_A15MPCORE_PRIV, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(A15MPPrivState), + .instance_init = a15mp_priv_initfn, + .class_init = a15mp_priv_class_init, + }, }; -static void a15mp_register_types(void) -{ - type_register_static(&a15mp_priv_info); -} - -type_init(a15mp_register_types) +DEFINE_TYPES(a15mp_types) diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index d03f57e579b..64bebbd19cb 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 @@ -56,6 +56,11 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) CPUState *cpu0; Object *cpuobj; + if (s->num_irq < 32 || s->num_irq > 256) { + error_setg(errp, "Property 'num-irq' must be between 32 and 256"); + return; + } + cpu0 = qemu_get_cpu(0); cpuobj = OBJECT(cpu0); if (strcmp(object_get_typename(cpuobj), ARM_CPU_TYPE_NAME("cortex-a9"))) { @@ -158,19 +163,19 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) } } -static Property a9mp_priv_properties[] = { +static const Property a9mp_priv_properties[] = { DEFINE_PROP_UINT32("num-cpu", A9MPPrivState, num_cpu, 1), - /* The Cortex-A9MP may have anything from 0 to 224 external interrupt - * IRQ lines (with another 32 internal). We default to 64+32, which - * is the number provided by the Cortex-A9MP test chip in the - * Realview PBX-A9 and Versatile Express A9 development boards. - * Other boards may differ and should set this property appropriately. + /* + * The Cortex-A9MP may have anything from 0 to 224 external interrupt + * lines, plus always 32 internal IRQs. This property sets the total + * of internal + external, so the valid range is from 32 to 256. + * The board model must set this to whatever the configuration + * used for the CPU on that board or SoC is. */ - DEFINE_PROP_UINT32("num-irq", A9MPPrivState, num_irq, 96), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_UINT32("num-irq", A9MPPrivState, num_irq, 0), }; -static void a9mp_priv_class_init(ObjectClass *klass, void *data) +static void a9mp_priv_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -178,17 +183,14 @@ static void a9mp_priv_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, a9mp_priv_properties); } -static const TypeInfo a9mp_priv_info = { - .name = TYPE_A9MPCORE_PRIV, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(A9MPPrivState), - .instance_init = a9mp_priv_initfn, - .class_init = a9mp_priv_class_init, +static const TypeInfo a9mp_types[] = { + { + .name = TYPE_A9MPCORE_PRIV, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(A9MPPrivState), + .instance_init = a9mp_priv_initfn, + .class_init = a9mp_priv_class_init, + }, }; -static void a9mp_register_types(void) -{ - type_register_static(&a9mp_priv_info); -} - -type_init(a9mp_register_types) +DEFINE_TYPES(a9mp_types) diff --git a/hw/cpu/arm11mpcore.c b/hw/cpu/arm11mpcore.c index 89c4e35143a..01772e7f77b 100644 --- a/hw/cpu/arm11mpcore.c +++ b/hw/cpu/arm11mpcore.c @@ -131,7 +131,7 @@ static void mpcore_priv_initfn(Object *obj) object_initialize_child(obj, "wdtimer", &s->wdtimer, TYPE_ARM_MPTIMER); } -static Property mpcore_priv_properties[] = { +static const Property mpcore_priv_properties[] = { DEFINE_PROP_UINT32("num-cpu", ARM11MPCorePriveState, num_cpu, 1), /* The ARM11 MPCORE TRM says the on-chip controller may have * anything from 0 to 224 external interrupt IRQ lines (with another @@ -142,10 +142,9 @@ static Property mpcore_priv_properties[] = { * has more IRQ lines than the kernel expects. */ DEFINE_PROP_UINT32("num-irq", ARM11MPCorePriveState, num_irq, 64), - DEFINE_PROP_END_OF_LIST(), }; -static void mpcore_priv_class_init(ObjectClass *klass, void *data) +static void mpcore_priv_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -153,17 +152,14 @@ static void mpcore_priv_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, mpcore_priv_properties); } -static const TypeInfo mpcore_priv_info = { - .name = TYPE_ARM11MPCORE_PRIV, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ARM11MPCorePriveState), - .instance_init = mpcore_priv_initfn, - .class_init = mpcore_priv_class_init, +static const TypeInfo arm11mp_types[] = { + { + .name = TYPE_ARM11MPCORE_PRIV, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ARM11MPCorePriveState), + .instance_init = mpcore_priv_initfn, + .class_init = mpcore_priv_class_init, + }, }; -static void arm11mpcore_register_types(void) -{ - type_register_static(&mpcore_priv_info); -} - -type_init(arm11mpcore_register_types) +DEFINE_TYPES(arm11mp_types) diff --git a/hw/cpu/cluster.c b/hw/cpu/cluster.c index e444b7c29d1..ef3b3d1e940 100644 --- a/hw/cpu/cluster.c +++ b/hw/cpu/cluster.c @@ -19,16 +19,14 @@ */ #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[] = { +static const Property cpu_cluster_properties[] = { DEFINE_PROP_UINT32("cluster-id", CPUClusterState, cluster_id, 0), - DEFINE_PROP_END_OF_LIST() }; typedef struct CallbackData { @@ -74,7 +72,7 @@ static void cpu_cluster_realize(DeviceState *dev, Error **errp) assert(cbdata.cpu_count > 0); } -static void cpu_cluster_class_init(ObjectClass *klass, void *data) +static void cpu_cluster_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/cpu/core.c b/hw/cpu/core.c index 98760751557..5cb2e9a7f54 100644 --- a/hw/cpu/core.c +++ b/hw/cpu/core.c @@ -8,12 +8,11 @@ */ #include "qemu/osdep.h" + +#include "hw/boards.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 "qapi/visitor.h" static void core_prop_get_core_id(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -78,7 +77,7 @@ static void cpu_core_instance_init(Object *obj) } } -static void cpu_core_class_init(ObjectClass *oc, void *data) +static void cpu_core_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/cpu/meson.build b/hw/cpu/meson.build index 6d319947ca0..9d36bf8ae2c 100644 --- a/hw/cpu/meson.build +++ b/hw/cpu/meson.build @@ -1,6 +1,7 @@ -system_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')) system_ss.add(when: 'CONFIG_ARM11MPCORE', if_true: files('arm11mpcore.c')) system_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_A9MPCORE', if_true: files('a9mpcore.c')) specific_ss.add(when: 'CONFIG_A15MPCORE', if_true: files('a15mpcore.c')) diff --git a/hw/cpu/realview_mpcore.c b/hw/cpu/realview_mpcore.c index 72c792eef1a..099b71a9ef2 100644 --- a/hw/cpu/realview_mpcore.c +++ b/hw/cpu/realview_mpcore.c @@ -14,7 +14,6 @@ #include "hw/cpu/arm11mpcore.h" #include "hw/intc/realview_gic.h" #include "hw/irq.h" -#include "hw/qdev-properties.h" #include "qom/object.h" #define TYPE_REALVIEW_MPCORE_RIRQ "realview_mpcore" @@ -68,7 +67,6 @@ static void realview_mpcore_realize(DeviceState *dev, Error **errp) int n; int i; - qdev_prop_set_uint32(priv, "num-cpu", s->num_cpu); if (!sysbus_realize(SYS_BUS_DEVICE(&s->priv), errp)) { return; } @@ -100,6 +98,7 @@ static void mpcore_rirq_init(Object *obj) int i; object_initialize_child(obj, "a11priv", &s->priv, TYPE_ARM11MPCORE_PRIV); + object_property_add_alias(obj, "num-cpu", OBJECT(&s->priv), "num-cpu"); privbusdev = SYS_BUS_DEVICE(&s->priv); sysbus_init_mmio(sbd, sysbus_mmio_get_region(privbusdev, 0)); @@ -108,30 +107,21 @@ static void mpcore_rirq_init(Object *obj) } } -static Property mpcore_rirq_properties[] = { - DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void mpcore_rirq_class_init(ObjectClass *klass, void *data) +static void mpcore_rirq_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = realview_mpcore_realize; - device_class_set_props(dc, mpcore_rirq_properties); } -static const TypeInfo mpcore_rirq_info = { - .name = TYPE_REALVIEW_MPCORE_RIRQ, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(mpcore_rirq_state), - .instance_init = mpcore_rirq_init, - .class_init = mpcore_rirq_class_init, +static const TypeInfo realview_mpcore_types[] = { + { + .name = TYPE_REALVIEW_MPCORE_RIRQ, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(mpcore_rirq_state), + .instance_init = mpcore_rirq_init, + .class_init = mpcore_rirq_class_init, + }, }; -static void realview_mpcore_register_types(void) -{ - type_register_static(&mpcore_rirq_info); -} - -type_init(realview_mpcore_register_types) +DEFINE_TYPES(realview_mpcore_types) diff --git a/hw/cris/Kconfig b/hw/cris/Kconfig deleted file mode 100644 index 884ad2cbc0d..00000000000 --- 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 d82050d927d..00000000000 --- 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 9fa09cfd83c..00000000000 --- 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 9f1e0e340c9..00000000000 --- 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 dc808a4e0f1..00000000000 --- 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 d246d6885b2..959a55518e6 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,12 +109,13 @@ 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 char *buf = NULL; + g_autofree uint8_t *buf = NULL; uint8_t sum = 0; int num_ent; int i = 0, ent = 1; @@ -121,11 +128,11 @@ static void ct3_load_cdat(CDATObject *cdat, Error **errp) &file_size, &error)) { error_setg(errp, "CDAT: File read failed: %s", error->message); g_error_free(error); - return; + return false; } if (file_size < sizeof(CDATTableHeader)) { error_setg(errp, "CDAT: File too short"); - return; + return false; } i = sizeof(CDATTableHeader); num_ent = 1; @@ -133,19 +140,19 @@ static void ct3_load_cdat(CDATObject *cdat, Error **errp) hdr = (CDATSubHeader *)(buf + i); if (i + sizeof(CDATSubHeader) > file_size) { error_setg(errp, "CDAT: Truncated table"); - return; + return false; } cdat_len_check(hdr, errp); i += hdr->length; if (i > file_size) { error_setg(errp, "CDAT: Truncated table"); - return; + return false; } num_ent++; } if (i != file_size) { error_setg(errp, "CDAT: File length mismatch"); - return; + return false; } cdat_st = g_new0(CDATEntry, num_ent); @@ -165,7 +172,7 @@ static void ct3_load_cdat(CDATObject *cdat, Error **errp) cdat_st[ent].base = hdr; cdat_st[ent].length = hdr->length; - while (buf + i < (char *)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 += buf[i++]; } @@ -179,16 +186,17 @@ 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); } } diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c index 378f1082ce9..473895948b3 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(); } } @@ -42,6 +98,9 @@ static void dumb_hdm_handler(CXLComponentState *cxl_cstate, hwaddr offset, 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; @@ -66,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) { - qemu_log_mask(LOG_UNIMP, - "CXL 8 byte cache mem registers not implemented\n"); + 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; } - 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); + case 8: + qemu_log_mask(LOG_UNIMP, + "CXL 8 byte cache mem registers not implemented\n"); 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 @@ -126,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); @@ -161,7 +232,8 @@ static void ras_init_common(uint32_t *reg_state, uint32_t *write_msk) static void hdm_init_common(uint32_t *reg_state, uint32_t *write_msk, enum reg_type type) { - int decoder_count = 1; + int decoder_count = CXL_HDM_DECODER_COUNT; + int hdm_inc = R_CXL_HDM_DECODER1_BASE_LO - R_CXL_HDM_DECODER0_BASE_LO; int i; ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, DECODER_COUNT, @@ -169,36 +241,53 @@ 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); + if (type == CXL2_TYPE3_DEVICE) { + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 3_6_12_WAY, 1); + ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 16_WAY, 1); + } else { + 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: @@ -213,6 +302,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; @@ -224,12 +314,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], \ @@ -242,24 +332,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 } @@ -319,26 +421,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; @@ -366,7 +477,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; } @@ -375,6 +486,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) { @@ -392,6 +504,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 517f06d8690..e150d744576 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,10 +32,13 @@ 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(); } } @@ -59,7 +62,17 @@ static uint64_t dev_reg_read(void *opaque, hwaddr offset, unsigned size) 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: @@ -69,6 +82,29 @@ 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]; + int bgop; + + qemu_mutex_lock(&cci->bg.lock); + bgop = !(cci->bg.complete_pct == 100 || cci->bg.aborted); + + status_reg = FIELD_DP64(status_reg, CXL_DEV_MAILBOX_STS, BG_OP, + bgop); + cxl_dstate->mbox_reg_state64[offset / size] = status_reg; + qemu_mutex_unlock(&cci->bg.lock); + } return cxl_dstate->mbox_reg_state64[offset / size]; default: g_assert_not_reached(); @@ -101,8 +137,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; @@ -120,7 +155,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); @@ -140,18 +185,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; - - retval = FIELD_DP64(retval, CXL_MEM_DEV_STS, MEDIA_STATUS, 1); - retval = FIELD_DP64(retval, CXL_MEM_DEV_STS, MBOX_READY, 1); + CXLDeviceState *cxl_dstate = opaque; - return retval; + return cxl_dstate->memdev_status; } static void ro_reg_write(void *opaque, hwaddr offset, uint64_t value, @@ -220,7 +304,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", @@ -230,7 +315,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", @@ -271,36 +356,79 @@ static void device_reg_init_common(CXLDeviceState *cxl_dstate) } } -static void mailbox_reg_init_common(CXLDeviceState *cxl_dstate) +static void mailbox_reg_init_common(CXLDeviceState *cxl_dstate, int msi_n) { - /* 2048 payload size, with no interrupt or background support */ + /* 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) { } +static void memdev_reg_init_common(CXLDeviceState *cxl_dstate) +{ + 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, int msi_n) +{ + 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_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_STATUS_VERSION); + device_reg_init_common(cxl_dstate); -void cxl_device_register_init_common(CXLDeviceState *cxl_dstate) + cxl_device_cap_init(cxl_dstate, MAILBOX, 2, CXL_DEV_MAILBOX_VERSION); + mailbox_reg_init_common(cxl_dstate, msi_n); + + cxl_device_cap_init(cxl_dstate, MEMORY_DEVICE, 0x4000, + CXL_MEM_DEV_STATUS_VERSION); + memdev_reg_init_common(cxl_dstate); + + cxl_initialize_mailbox_t3(&ct3d->cci, DEVICE(ct3d), + CXL_MAILBOX_MAX_PAYLOAD_SIZE); +} + +void cxl_device_register_init_swcci(CSWMBCCIDev *sw, int msi_n) { - uint64_t *cap_hdrs = cxl_dstate->caps_reg_state64; + 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_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, 2); device_reg_init_common(cxl_dstate); cxl_device_cap_init(cxl_dstate, MAILBOX, 2, 1); - mailbox_reg_init_common(cxl_dstate); + mailbox_reg_init_common(cxl_dstate, msi_n); cxl_device_cap_init(cxl_dstate, MEMORY_DEVICE, 0x4000, 1); memdev_reg_init_common(cxl_dstate); - - cxl_initialize_mailbox(cxl_dstate); } uint64_t cxl_device_get_timestamp(CXLDeviceState *cxl_dstate) diff --git a/hw/cxl/cxl-events.c b/hw/cxl/cxl-events.c index d161d574563..12dee2e4675 100644 --- a/hw/cxl/cxl-events.c +++ b/hw/cxl/cxl-events.c @@ -7,11 +7,9 @@ * COPYING file in the top-level directory. */ -#include - #include "qemu/osdep.h" + #include "qemu/bswap.h" -#include "qemu/typedefs.h" #include "qemu/error-report.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" @@ -141,9 +139,22 @@ bool cxl_event_insert(CXLDeviceState *cxlds, CXLEventLogType log_type, 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, - uint16_t *len) + size_t *len) { CXLEventLog *log; CXLEvent *entry; @@ -170,8 +181,10 @@ CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl, 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->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); @@ -180,7 +193,8 @@ CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl, return CXL_MBOX_SUCCESS; } -CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds, CXLClearEventPayload *pl) +CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds, + CXLClearEventPayload *pl) { CXLEventLog *log; uint8_t log_type; @@ -197,13 +211,13 @@ CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds, CXLClearEventPayload * QEMU_LOCK_GUARD(&log->lock); /* - * Must itterate the queue twice. + * 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 3.0 8.2.9.2.3 + * -- 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++) { diff --git a/hw/cxl/cxl-host.c b/hw/cxl/cxl-host.c index 034c7805b3e..e0101631744 100644 --- a/hw/cxl/cxl-host.c +++ b/hw/cxl/cxl-host.c @@ -10,7 +10,7 @@ #include "qemu/bitmap.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "hw/boards.h" #include "qapi/qapi-visit-machine.h" @@ -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,12 +40,6 @@ 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 be a multiple of 256MiB"); @@ -64,10 +59,14 @@ 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)); - - return; } void cxl_fmws_link_targets(CXLState *cxl_state, Error **errp) @@ -97,33 +96,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; - - ctrl = cache_mem[R_CXL_HDM_DECODER0_CTRL]; - if (!FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, COMMITTED)) { - return false; - } - - 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); + 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; + + 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; + } - 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); + 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; } - return true; + return found; } static PCIDevice *cxl_cfmws_find_device(CXLFixedWindow *fw, hwaddr addr) @@ -289,7 +313,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 02f9b5a8702..299f232f262 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -7,16 +7,32 @@ * COPYING file in the top-level directory. */ +#include + #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 "system/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 +#define CXL_ALERTS_LIFE_USED_WARN_THRESH (1 << 0) +#define CXL_ALERTS_OVER_TEMP_WARN_THRESH (1 << 1) +#define CXL_ALERTS_UNDER_TEMP_WARN_THRESH (1 << 2) +#define CXL_ALERTS_COR_VMEM_ERR_WARN_THRESH (1 << 3) +#define CXL_ALERTS_COR_PMEM_ERR_WARN_THRESH (1 << 4) /* * How to add a new command, example. The command set FOO, with cmd BAR. @@ -39,11 +55,17 @@ * 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 + #define GET_RESPONSE_MSG_LIMIT 0x3 + #define SET_RESPONSE_MSG_LIMIT 0x4 + #define BACKGROUND_OPERATION_ABORT 0x5 EVENTS = 0x01, #define GET_RECORDS 0x0 #define CLEAR_RECORDS 0x1 @@ -51,39 +73,184 @@ 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 + HEALTH_INFO_ALERTS = 0x42, + #define GET_ALERT_CONFIG 0x1 + #define SET_ALERT_CONFIG 0x2 + SANITIZE = 0x44, + #define OVERWRITE 0x0 + #define SECURE_ERASE 0x1 + #define MEDIA_OPERATIONS 0x2 + 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 }; -struct cxl_cmd; -typedef CXLRetCode (*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; -}; +/* 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; + +/* 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); -static CXLRetCode cmd_events_get_records(struct cxl_cmd *cmd, - CXLDeviceState *cxlds, - uint16_t *len) + 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; @@ -92,10 +259,9 @@ static CXLRetCode cmd_events_get_records(struct cxl_cmd *cmd, return CXL_MBOX_INVALID_INPUT; } - log_type = *((uint8_t *)cmd->payload); + log_type = payload_in[0]; - pl = (CXLGetEventPayload *)cmd->payload; - memset(pl, 0, sizeof(*pl)); + pl = (CXLGetEventPayload *)payload_out; max_recs = (cxlds->payload_size - CXL_EVENT_PAYLOAD_HDR_SIZE) / CXL_EVENT_RECORD_SIZE; @@ -103,29 +269,42 @@ static CXLRetCode cmd_events_get_records(struct cxl_cmd *cmd, max_recs = 0xFFFF; } - return cxl_event_get_records(cxlds, pl, log_type, max_recs, len); + return cxl_event_get_records(cxlds, pl, log_type, max_recs, len_out); } -static CXLRetCode cmd_events_clear_records(struct cxl_cmd *cmd, - CXLDeviceState *cxlds, - uint16_t *len) +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 *)cmd->payload; - *len = 0; + 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(struct cxl_cmd *cmd, - CXLDeviceState *cxlds, - uint16_t *len) +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 *)cmd->payload; - memset(policy, 0, sizeof(*policy)); + policy = (CXLEventInterruptPolicy *)payload_out; log = &cxlds->event_logs[CXL_EVENT_TYPE_INFO]; if (log->irq_enabled) { @@ -153,22 +332,26 @@ static CXLRetCode cmd_events_get_interrupt_policy(struct cxl_cmd *cmd, policy->dyn_cap_settings = CXL_INT_MSI_MSIX; } - *len = sizeof(*policy); + *len_out = sizeof(*policy); return CXL_MBOX_SUCCESS; } -static CXLRetCode cmd_events_set_interrupt_policy(struct cxl_cmd *cmd, - CXLDeviceState *cxlds, - uint16_t *len) +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 < CXL_EVENT_INT_SETTING_MIN_LEN) { + if (len_in < CXL_EVENT_INT_SETTING_MIN_LEN) { return CXL_MBOX_INVALID_PAYLOAD_LENGTH; } - policy = (CXLEventInterruptPolicy *)cmd->payload; + policy = (CXLEventInterruptPolicy *)payload_in; log = &cxlds->event_logs[CXL_EVENT_TYPE_INFO]; log->irq_enabled = (policy->info_settings & CXL_EVENT_INT_MODE_MASK) == @@ -187,7 +370,7 @@ static CXLRetCode cmd_events_set_interrupt_policy(struct cxl_cmd *cmd, CXL_INT_MSI_MSIX; /* DCD is optional */ - if (*len < sizeof(*policy)) { + if (len_in < sizeof(*policy)) { return CXL_MBOX_SUCCESS; } @@ -195,15 +378,372 @@ static CXLRetCode cmd_events_set_interrupt_policy(struct cxl_cmd *cmd, log->irq_enabled = (policy->dyn_cap_settings & CXL_EVENT_INT_MODE_MASK) == CXL_INT_MSI_MSIX; - *len = sizeof(*policy); + *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 */ + } + + is_identify->max_message_size = (uint8_t)log2(cci->payload_max); + *len_out = sizeof(*is_identify); + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.1 section 8.2.9.1.3: Get Response Message Limit (Opcode 0003h) */ +static CXLRetCode cmd_get_response_msg_limit(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 rsp_limit; + } QEMU_PACKED *get_rsp_msg_limit = (void *)payload_out; + QEMU_BUILD_BUG_ON(sizeof(*get_rsp_msg_limit) != 1); + + get_rsp_msg_limit->rsp_limit = (uint8_t)log2(cci->payload_max); + + *len_out = sizeof(*get_rsp_msg_limit); + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.1 section 8.2.9.1.4: Set Response Message Limit (Opcode 0004h) */ +static CXLRetCode cmd_set_response_msg_limit(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 rsp_limit; + } QEMU_PACKED *in = (void *)payload_in; + QEMU_BUILD_BUG_ON(sizeof(*in) != 1); + struct { + uint8_t rsp_limit; + } QEMU_PACKED *out = (void *)payload_out; + QEMU_BUILD_BUG_ON(sizeof(*out) != 1); + + if (in->rsp_limit < 8 || in->rsp_limit > 10) { + return CXL_MBOX_INVALID_INPUT; + } + + cci->payload_max = 1 << in->rsp_limit; + out->rsp_limit = in->rsp_limit; + + *len_out = sizeof(*out); + 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; +} + +/* + * CXL r3.1 Section 8.2.9.1.5: + * Request Abort Background Operation (Opcode 0005h) + */ +static CXLRetCode cmd_infostat_bg_op_abort(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + int bg_set = cci->bg.opcode >> 8; + int bg_cmd = cci->bg.opcode & 0xff; + const struct cxl_cmd *bg_c = &cci->cxl_cmd_set[bg_set][bg_cmd]; + + if (!(bg_c->effect & CXL_MBOX_BACKGROUND_OPERATION_ABORT)) { + return CXL_MBOX_REQUEST_ABORT_NOTSUP; + } + + qemu_mutex_lock(&cci->bg.lock); + if (cci->bg.runtime) { + /* operation is near complete, let it finish */ + if (cci->bg.complete_pct < 85) { + timer_del(cci->bg.timer); + cci->bg.ret_code = CXL_MBOX_ABORTED; + cci->bg.starttime = 0; + cci->bg.runtime = 0; + cci->bg.aborted = true; + } + } + qemu_mutex_unlock(&cci->bg.lock); + return CXL_MBOX_SUCCESS; } -/* 8.2.9.2.1 */ -static CXLRetCode cmd_firmware_update_get_info(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +#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; @@ -216,60 +756,253 @@ static CXLRetCode cmd_firmware_update_get_info(struct cxl_cmd *cmd, } QEMU_PACKED *fw_info; QEMU_BUILD_BUG_ON(sizeof(*fw_info) != 0x50); - if ((cxl_dstate->vmem_size < CXL_CAPACITY_MULTIPLIER) || - (cxl_dstate->pmem_size < CXL_CAPACITY_MULTIPLIER)) { + 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 = 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 */ + + 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"); + } + + *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; + } - 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"); + 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; + } - *len = sizeof(*fw_info); return CXL_MBOX_SUCCESS; } -/* 8.2.9.3.1 */ -static CXLRetCode cmd_timestamp_get(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +/* CXL r3.1 Section 8.2.9.4.1: Get Timestamp (Opcode 0300h) */ +static CXLRetCode cmd_timestamp_get(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) { + CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; uint64_t final_time = cxl_device_get_timestamp(cxl_dstate); - stq_le_p(cmd->payload, final_time); - *len = 8; + stq_le_p(payload_out, final_time); + *len_out = 8; return CXL_MBOX_SUCCESS; } -/* 8.2.9.3.2 */ -static CXLRetCode cmd_timestamp_set(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +/* CXL r3.1 Section 8.2.9.4.2: Set Timestamp (Opcode 0301h) */ +static CXLRetCode cmd_timestamp_set(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) { + CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; + cxl_dstate->timestamp.set = true; cxl_dstate->timestamp.last_set = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - cxl_dstate->timestamp.host_set = le64_to_cpu(*(uint64_t *)cmd->payload); + cxl_dstate->timestamp.host_set = le64_to_cpu(*(uint64_t *)payload_in); - *len = 0; + *len_out = 0; return CXL_MBOX_SUCCESS; } -/* CXL 3.0 8.2.9.5.2.1 Command Effects Log (CEL) */ +/* 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 CXLRetCode 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; @@ -278,484 +1011,2611 @@ static CXLRetCode 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 CXLRetCode cmd_logs_get_log(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +/* CXL r3.1 Section 8.2.9.5.2: Get Log (Opcode 0401h) */ +static CXLRetCode cmd_logs_get_log(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) { struct { QemuUUID uuid; uint32_t offset; uint32_t length; - } QEMU_PACKED QEMU_ALIGNED(16) *get_log = (void *)cmd->payload; + } QEMU_PACKED QEMU_ALIGNED(16) *get_log; + + get_log = (void *)payload_in; + + 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_INVALID_LOG; + } /* - * 8.2.9.4.2 - * The device shall return Invalid Parameter if the Offset or Length + * CXL r3.1 Section 8.2.9.5.2: Get Log (Opcode 0401h) + * The device shall return Invalid Input if the Offset or Length * fields attempt to access beyond the size of the log as reported by Get - * Supported Logs. - * - * XXX: Spec is wrong, "Invalid Parameter" isn't a thing. - * XXX: Spec doesn't address incorrect UUID incorrectness. + * Supported Log. * - * 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. + * 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 (get_log->offset + get_log->length > cxl_dstate->payload_size) { + if ((uint64_t)get_log->offset + get_log->length >= sizeof(cci->cel_log)) { return CXL_MBOX_INVALID_INPUT; } - if (!qemu_uuid_is_equal(&get_log->uuid, &cel_uuid)) { - return CXL_MBOX_UNSUPPORTED; - } - /* 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 CXLRetCode cmd_identify_memory_device(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) -{ - struct { - char fw_revision[0x10]; - uint64_t total_capacity; - uint64_t volatile_capacity; - uint64_t persistent_capacity; - uint64_t partition_align; - uint16_t info_event_log_size; - uint16_t warning_event_log_size; - uint16_t failure_event_log_size; - uint16_t fatal_event_log_size; - uint32_t lsa_size; - uint8_t poison_list_max_mer[3]; - uint16_t inject_poison_limit; - uint8_t poison_caps; - uint8_t qos_telemetry_caps; - } QEMU_PACKED *id; - QEMU_BUILD_BUG_ON(sizeof(*id) != 0x43); +/* 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; - CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); - CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); +/* + * 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; - if ((!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER)) || - (!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER))) { - return CXL_MBOX_INTERNAL_ERROR; - } +/* + * 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 +}; - id = (void *)cmd->payload; - memset(id, 0, sizeof(*id)); +/* 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) - snprintf(id->fw_revision, 0x10, "BWFW VERSION %02d", 0); +/* 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) +}; - stq_le_p(&id->total_capacity, cxl_dstate->mem_size / CXL_CAPACITY_MULTIPLIER); - stq_le_p(&id->persistent_capacity, cxl_dstate->pmem_size / CXL_CAPACITY_MULTIPLIER); - stq_le_p(&id->volatile_capacity, cxl_dstate->vmem_size / CXL_CAPACITY_MULTIPLIER); - stl_le_p(&id->lsa_size, cvc->get_lsa_size(ct3d)); - /* 256 poison records */ - st24_le_p(id->poison_list_max_mer, 256); - /* No limit - so limited by main poison record limit */ - stw_le_p(&id->inject_poison_limit, 0); +typedef struct CXLMemPatrolScrubSetFeature { + CXLSetFeatureInHeader hdr; + CXLMemPatrolScrubWriteAttrs feat_data; +} QEMU_PACKED QEMU_ALIGNED(16) CXLMemPatrolScrubSetFeature; - *len = sizeof(*id); - return CXL_MBOX_SUCCESS; -} +/* + * 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) +}; -static CXLRetCode cmd_ccls_get_partition_info(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +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 { - uint64_t active_vmem; - uint64_t active_pmem; - uint64_t next_vmem; - uint64_t next_pmem; - } QEMU_PACKED *part_info = (void *)cmd->payload; - QEMU_BUILD_BUG_ON(sizeof(*part_info) != 0x20); + uint32_t count; + uint16_t start_index; + uint16_t reserved; + } QEMU_PACKED QEMU_ALIGNED(16) * get_feats_in = (void *)payload_in; - if ((!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER)) || - (!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER))) { - return CXL_MBOX_INTERNAL_ERROR; + 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; } - stq_le_p(&part_info->active_vmem, cxl_dstate->vmem_size / CXL_CAPACITY_MULTIPLIER); - /* + 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]; + uint64_t total_capacity; + uint64_t volatile_capacity; + uint64_t persistent_capacity; + uint64_t partition_align; + uint16_t info_event_log_size; + uint16_t warning_event_log_size; + uint16_t failure_event_log_size; + uint16_t fatal_event_log_size; + uint32_t lsa_size; + uint8_t poison_list_max_mer[3]; + 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) != 0x45); + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); + CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; + + 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 *)payload_out; + + snprintf(id->fw_revision, 0x10, "BWFW VERSION %02d", 0); + + 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_out = sizeof(*id); + return CXL_MBOX_SUCCESS; +} + +/* 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 *)payload_out; + QEMU_BUILD_BUG_ON(sizeof(*part_info) != 0x20); + CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); + + 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; + } + + 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->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 CXLRetCode 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 CXLRetCode 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, len_in, set_lsa_payload->offset); + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.2 Section 8.2.10.9.3.2 Get Alert Configuration (Opcode 4201h) */ +static CXLRetCode cmd_get_alert_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); + CXLAlertConfig *out = (CXLAlertConfig *)payload_out; + + memcpy(out, &ct3d->alert_config, sizeof(ct3d->alert_config)); + *len_out = sizeof(ct3d->alert_config); + + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.2 Section 8.2.10.9.3.3 Set Alert Configuration (Opcode 4202h) */ +static CXLRetCode cmd_set_alert_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); + CXLAlertConfig *alert_config = &ct3d->alert_config; + struct { + uint8_t valid_alert_actions; + uint8_t enable_alert_actions; + uint8_t life_used_warn_thresh; + uint8_t rsvd; + uint16_t over_temp_warn_thresh; + uint16_t under_temp_warn_thresh; + uint16_t cor_vmem_err_warn_thresh; + uint16_t cor_pmem_err_warn_thresh; + } QEMU_PACKED *in = (void *)payload_in; + + if (in->valid_alert_actions & CXL_ALERTS_LIFE_USED_WARN_THRESH) { + /* + * CXL r3.2 Table 8-149 The life used warning threshold shall be + * less than the life used critical alert value. + */ + if (in->life_used_warn_thresh >= + alert_config->life_used_crit_alert_thresh) { + return CXL_MBOX_INVALID_INPUT; + } + alert_config->life_used_warn_thresh = in->life_used_warn_thresh; + alert_config->enable_alerts |= CXL_ALERTS_LIFE_USED_WARN_THRESH; + } + + if (in->valid_alert_actions & CXL_ALERTS_OVER_TEMP_WARN_THRESH) { + /* + * CXL r3.2 Table 8-149 The Device Over-Temperature Warning Threshold + * shall be less than the the Device Over-Temperature Critical + * Alert Threshold. + */ + if (in->over_temp_warn_thresh >= + alert_config->over_temp_crit_alert_thresh) { + return CXL_MBOX_INVALID_INPUT; + } + alert_config->over_temp_warn_thresh = in->over_temp_warn_thresh; + alert_config->enable_alerts |= CXL_ALERTS_OVER_TEMP_WARN_THRESH; + } + + if (in->valid_alert_actions & CXL_ALERTS_UNDER_TEMP_WARN_THRESH) { + /* + * CXL r3.2 Table 8-149 The Device Under-Temperature Warning Threshold + * shall be higher than the the Device Under-Temperature Critical + * Alert Threshold. + */ + if (in->under_temp_warn_thresh <= + alert_config->under_temp_crit_alert_thresh) { + return CXL_MBOX_INVALID_INPUT; + } + alert_config->under_temp_warn_thresh = in->under_temp_warn_thresh; + alert_config->enable_alerts |= CXL_ALERTS_UNDER_TEMP_WARN_THRESH; + } + + if (in->valid_alert_actions & CXL_ALERTS_COR_VMEM_ERR_WARN_THRESH) { + alert_config->cor_vmem_err_warn_thresh = in->cor_vmem_err_warn_thresh; + alert_config->enable_alerts |= CXL_ALERTS_COR_VMEM_ERR_WARN_THRESH; + } - cvc->set_lsa(ct3d, set_lsa_payload->data, plen, set_lsa_payload->offset); + if (in->valid_alert_actions & CXL_ALERTS_COR_PMEM_ERR_WARN_THRESH) { + alert_config->cor_pmem_err_warn_thresh = in->cor_pmem_err_warn_thresh; + alert_config->enable_alerts |= CXL_ALERTS_COR_PMEM_ERR_WARN_THRESH; + } return CXL_MBOX_SUCCESS; } +/* Perform the actual device zeroing */ +static void __do_sanitization(CXLType3Dev *ct3d) +{ + MemoryRegion *mr; + + if (ct3d->hostvmem) { + mr = host_memory_backend_get_memory(ct3d->hostvmem); + if (mr) { + void *hostmem = memory_region_get_ram_ptr(mr); + memset(hostmem, 0, memory_region_size(mr)); + } + } + + if (ct3d->hostpmem) { + mr = host_memory_backend_get_memory(ct3d->hostpmem); + if (mr) { + void *hostmem = memory_region_get_ram_ptr(mr); + memset(hostmem, 0, memory_region_size(mr)); + } + } + if (ct3d->lsa) { + mr = host_memory_backend_get_memory(ct3d->lsa); + if (mr) { + void *lsa = memory_region_get_ram_ptr(mr); + memset(lsa, 0, memory_region_size(mr)); + } + } + cxl_discard_all_event_records(&ct3d->cxl_dstate); +} + +static int get_sanitize_duration(uint64_t total_mem) +{ + int secs = 0; + + 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 */ + } + + return secs; +} + /* - * 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. + * 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_media_get_poison_list(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len) +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) { - struct get_poison_list_pl { - uint64_t pa; - uint64_t length; - } QEMU_PACKED; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + uint64_t total_mem; /* in Mb */ + int secs; - struct get_poison_list_out_pl { - uint8_t flags; - uint8_t rsvd1; - uint64_t overflow_timestamp; - uint16_t count; - uint8_t rsvd2[0x14]; + total_mem = (ct3d->cxl_dstate.vmem_size + ct3d->cxl_dstate.pmem_size) >> 20; + secs = get_sanitize_duration(total_mem); + + /* 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; +} + +struct dpa_range_list_entry { + uint64_t starting_dpa; + uint64_t length; +} QEMU_PACKED; + +struct CXLSanitizeInfo { + uint32_t dpa_range_count; + uint8_t fill_value; + struct dpa_range_list_entry dpa_range_list[]; +} QEMU_PACKED; + +static uint64_t get_vmr_size(CXLType3Dev *ct3d, MemoryRegion **vmr) +{ + MemoryRegion *mr; + if (ct3d->hostvmem) { + mr = host_memory_backend_get_memory(ct3d->hostvmem); + if (vmr) { + *vmr = mr; + } + return memory_region_size(mr); + } + return 0; +} + +static uint64_t get_pmr_size(CXLType3Dev *ct3d, MemoryRegion **pmr) +{ + MemoryRegion *mr; + if (ct3d->hostpmem) { + mr = host_memory_backend_get_memory(ct3d->hostpmem); + if (pmr) { + *pmr = mr; + } + return memory_region_size(mr); + } + return 0; +} + +static uint64_t get_dc_size(CXLType3Dev *ct3d, MemoryRegion **dc_mr) +{ + MemoryRegion *mr; + if (ct3d->dc.host_dc) { + mr = host_memory_backend_get_memory(ct3d->dc.host_dc); + if (dc_mr) { + *dc_mr = mr; + } + return memory_region_size(mr); + } + return 0; +} + +static int validate_dpa_addr(CXLType3Dev *ct3d, uint64_t dpa_addr, + size_t length) +{ + uint64_t vmr_size, pmr_size, dc_size; + + if ((dpa_addr % CXL_CACHE_LINE_SIZE) || + (length % CXL_CACHE_LINE_SIZE) || + (length <= 0)) { + return -EINVAL; + } + + vmr_size = get_vmr_size(ct3d, NULL); + pmr_size = get_pmr_size(ct3d, NULL); + dc_size = get_dc_size(ct3d, NULL); + + if (dpa_addr + length > vmr_size + pmr_size + dc_size) { + return -EINVAL; + } + + if (dpa_addr > vmr_size + pmr_size) { + if (!ct3_test_region_block_backed(ct3d, dpa_addr, length)) { + return -ENODEV; + } + } + + return 0; +} + +static int sanitize_range(CXLType3Dev *ct3d, uint64_t dpa_addr, size_t length, + uint8_t fill_value) +{ + + uint64_t vmr_size, pmr_size; + AddressSpace *as = NULL; + MemTxAttrs mem_attrs = {}; + + vmr_size = get_vmr_size(ct3d, NULL); + pmr_size = get_pmr_size(ct3d, NULL); + + if (dpa_addr < vmr_size) { + as = &ct3d->hostvmem_as; + } else if (dpa_addr < vmr_size + pmr_size) { + as = &ct3d->hostpmem_as; + } else { + if (!ct3_test_region_block_backed(ct3d, dpa_addr, length)) { + return -ENODEV; + } + as = &ct3d->dc.host_dc_as; + } + + return address_space_set(as, dpa_addr, fill_value, length, mem_attrs); +} + +/* Perform the actual device zeroing */ +static void __do_sanitize(CXLType3Dev *ct3d) +{ + struct CXLSanitizeInfo *san_info = ct3d->media_op_sanitize; + int dpa_range_count = san_info->dpa_range_count; + int rc = 0; + int i; + + for (i = 0; i < dpa_range_count; i++) { + rc = sanitize_range(ct3d, san_info->dpa_range_list[i].starting_dpa, + san_info->dpa_range_list[i].length, + san_info->fill_value); + if (rc) { + goto exit; + } + } +exit: + g_free(ct3d->media_op_sanitize); + ct3d->media_op_sanitize = NULL; + return; +} + +enum { + MEDIA_OP_CLASS_GENERAL = 0x0, + #define MEDIA_OP_GEN_SUBC_DISCOVERY 0x0 + MEDIA_OP_CLASS_SANITIZE = 0x1, + #define MEDIA_OP_SAN_SUBC_SANITIZE 0x0 + #define MEDIA_OP_SAN_SUBC_ZERO 0x1 +}; + +struct media_op_supported_list_entry { + uint8_t media_op_class; + uint8_t media_op_subclass; +}; + +struct media_op_discovery_out_pl { + uint64_t dpa_range_granularity; + uint16_t total_supported_operations; + uint16_t num_of_supported_operations; + struct media_op_supported_list_entry entry[]; +} QEMU_PACKED; + +static const struct media_op_supported_list_entry media_op_matrix[] = { + { MEDIA_OP_CLASS_GENERAL, MEDIA_OP_GEN_SUBC_DISCOVERY }, + { MEDIA_OP_CLASS_SANITIZE, MEDIA_OP_SAN_SUBC_SANITIZE }, + { MEDIA_OP_CLASS_SANITIZE, MEDIA_OP_SAN_SUBC_ZERO }, +}; + +static CXLRetCode media_operations_discovery(uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out) +{ + struct { + uint8_t media_operation_class; + uint8_t media_operation_subclass; + uint8_t rsvd[2]; + uint32_t dpa_range_count; struct { - uint64_t addr; - uint32_t length; - uint32_t resv; - } QEMU_PACKED records[]; - } QEMU_PACKED; + uint16_t start_index; + uint16_t num_ops; + } discovery_osa; + } QEMU_PACKED *media_op_in_disc_pl = (void *)payload_in; + struct media_op_discovery_out_pl *media_out_pl = + (struct media_op_discovery_out_pl *)payload_out; + int num_ops, start_index, i; + int count = 0; + + if (len_in < sizeof(*media_op_in_disc_pl)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } - struct get_poison_list_pl *in = (void *)cmd->payload; - struct get_poison_list_out_pl *out = (void *)cmd->payload; - CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); - uint16_t record_count = 0, i = 0; - uint64_t query_start, query_length; - CXLPoisonList *poison_list = &ct3d->poison_list; + num_ops = media_op_in_disc_pl->discovery_osa.num_ops; + start_index = media_op_in_disc_pl->discovery_osa.start_index; + + /* + * As per spec CXL r3.2 8.2.10.9.5.3 dpa_range_count should be zero and + * start index should not exceed the total number of entries for discovery + * sub class command. + */ + if (media_op_in_disc_pl->dpa_range_count || + start_index > ARRAY_SIZE(media_op_matrix)) { + return CXL_MBOX_INVALID_INPUT; + } + + media_out_pl->dpa_range_granularity = CXL_CACHE_LINE_SIZE; + media_out_pl->total_supported_operations = + ARRAY_SIZE(media_op_matrix); + if (num_ops > 0) { + for (i = start_index; i < start_index + num_ops; i++) { + media_out_pl->entry[count].media_op_class = + media_op_matrix[i].media_op_class; + media_out_pl->entry[count].media_op_subclass = + media_op_matrix[i].media_op_subclass; + count++; + if (count == num_ops) { + break; + } + } + } + + media_out_pl->num_of_supported_operations = count; + *len_out = sizeof(*media_out_pl) + count * sizeof(*media_out_pl->entry); + return CXL_MBOX_SUCCESS; +} + +static CXLRetCode media_operations_sanitize(CXLType3Dev *ct3d, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + uint8_t fill_value, + CXLCCI *cci) +{ + struct media_operations_sanitize { + uint8_t media_operation_class; + uint8_t media_operation_subclass; + uint8_t rsvd[2]; + uint32_t dpa_range_count; + struct dpa_range_list_entry dpa_range_list[]; + } QEMU_PACKED *media_op_in_sanitize_pl = (void *)payload_in; + uint32_t dpa_range_count = media_op_in_sanitize_pl->dpa_range_count; + uint64_t total_mem = 0; + size_t dpa_range_list_size; + int secs = 0, i; + + if (dpa_range_count == 0) { + return CXL_MBOX_SUCCESS; + } + + dpa_range_list_size = dpa_range_count * sizeof(struct dpa_range_list_entry); + if (len_in < (sizeof(*media_op_in_sanitize_pl) + dpa_range_list_size)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + + for (i = 0; i < dpa_range_count; i++) { + uint64_t start_dpa = + media_op_in_sanitize_pl->dpa_range_list[i].starting_dpa; + uint64_t length = media_op_in_sanitize_pl->dpa_range_list[i].length; + + if (validate_dpa_addr(ct3d, start_dpa, length)) { + return CXL_MBOX_INVALID_INPUT; + } + total_mem += length; + } + ct3d->media_op_sanitize = g_malloc0(sizeof(struct CXLSanitizeInfo) + + dpa_range_list_size); + + ct3d->media_op_sanitize->dpa_range_count = dpa_range_count; + ct3d->media_op_sanitize->fill_value = fill_value; + memcpy(ct3d->media_op_sanitize->dpa_range_list, + media_op_in_sanitize_pl->dpa_range_list, + dpa_range_list_size); + secs = get_sanitize_duration(total_mem >> 20); + + /* EBUSY other bg cmds as of now */ + cci->bg.runtime = secs * 1000UL; + *len_out = 0; + /* + * media op sanitize is targeted so no need to disable media or + * clear event logs + */ + return CXL_MBOX_BG_STARTED; +} + +static CXLRetCode cmd_media_operations(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 media_operation_class; + uint8_t media_operation_subclass; + uint8_t rsvd[2]; + uint32_t dpa_range_count; + } QEMU_PACKED *media_op_in_common_pl = (void *)payload_in; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + uint8_t media_op_cl = 0; + uint8_t media_op_subclass = 0; + + if (len_in < sizeof(*media_op_in_common_pl)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + + media_op_cl = media_op_in_common_pl->media_operation_class; + media_op_subclass = media_op_in_common_pl->media_operation_subclass; + + switch (media_op_cl) { + case MEDIA_OP_CLASS_GENERAL: + if (media_op_subclass != MEDIA_OP_GEN_SUBC_DISCOVERY) { + return CXL_MBOX_UNSUPPORTED; + } + + return media_operations_discovery(payload_in, len_in, payload_out, + len_out); + case MEDIA_OP_CLASS_SANITIZE: + switch (media_op_subclass) { + case MEDIA_OP_SAN_SUBC_SANITIZE: + return media_operations_sanitize(ct3d, payload_in, len_in, + payload_out, len_out, 0xF, + cci); + case MEDIA_OP_SAN_SUBC_ZERO: + return media_operations_sanitize(ct3d, payload_in, len_in, + payload_out, len_out, 0, + cci); + default: + return CXL_MBOX_UNSUPPORTED; + } + default: + return CXL_MBOX_UNSUPPORTED; + } +} + +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 alignemnt required */ - if (query_start & 0x3f) { - return CXL_MBOX_INVALID_INPUT; + 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); } - query_length = ldq_le_p(&in->length) * CXL_CACHE_LINE_SIZE; - QLIST_FOREACH(ent, poison_list, node) { - /* Check for no overlap */ - if (ent->start >= query_start + query_length || - ent->start + ent->length <= query_start) { - continue; + 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; } - record_count++; - } - out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); - assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE); - memset(out, 0, out_pl_len); - QLIST_FOREACH(ent, poison_list, node) { - uint64_t start, stop; + 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); + } - /* Check for no overlap */ - if (ent->start >= query_start + query_length || - ent->start + ent->length <= query_start) { - continue; + 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; } - /* 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); + /* 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; + } + } } - stw_le_p(&out->count, record_count); - *len = out_pl_len; return CXL_MBOX_SUCCESS; } -static CXLRetCode cmd_media_inject_poison(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len_unused) +/* + * 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) { - CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); - CXLPoisonList *poison_list = &ct3d->poison_list; - CXLPoison *ent; - struct inject_poison_pl { - uint64_t dpa; - }; - struct inject_poison_pl *in = (void *)cmd->payload; - uint64_t dpa = ldq_le_p(&in->dpa); - CXLPoison *p; + 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; + } - QLIST_FOREACH(ent, poison_list, node) { - if (dpa >= ent->start && - dpa + CXL_CACHE_LINE_SIZE <= ent->start + ent->length) { - return CXL_MBOX_SUCCESS; - } + if (in->num_entries_updated == 0) { + cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); + return CXL_MBOX_SUCCESS; } - if (ct3d->poison_list_cnt == CXL_POISON_LIST_LIMIT) { - return CXL_MBOX_INJECT_POISON_LIMIT; + if (len_in < + sizeof(*in) + sizeof(*in->updated_entries) * in->num_entries_updated) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; } - p = g_new0(CXLPoison, 1); - p->length = CXL_CACHE_LINE_SIZE; - p->start = dpa; - p->type = CXL_POISON_TYPE_INJECTED; + /* 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; + } - /* - * 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++; + 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; } -static CXLRetCode cmd_media_clear_poison(struct cxl_cmd *cmd, - CXLDeviceState *cxl_dstate, - uint16_t *len_unused) +/* + * Copy extent list from src to dst + * Return value: number of extents copied + */ +static uint32_t copy_extent_list(CXLDCExtentList *dst, + const CXLDCExtentList *src) { - CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, 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; + uint32_t cnt = 0; + CXLDCExtent *ent; - struct clear_poison_pl *in = (void *)cmd->payload; + if (!dst || !src) { + return 0; + } - dpa = ldq_le_p(&in->dpa); - if (dpa + CXL_CACHE_LINE_SIZE > cxl_dstate->mem_size) { - return CXL_MBOX_INVALID_PA; + 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; +} - /* 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; +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; } - } - 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; + /* 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; + } + } } } - if (!ent) { - return CXL_MBOX_SUCCESS; +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; } - QLIST_REMOVE(ent, node); - ct3d->poison_list_cnt--; - - if (dpa > ent->start) { - CXLPoison *frag; - /* Cannot overflow as replacing existing entry */ + return ret; +} - frag = g_new0(CXLPoison, 1); +/* + * 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; + } - frag->start = ent->start; - frag->length = dpa - ent->start; - frag->type = ent->type; + if (in->num_entries_updated == 0) { + return CXL_MBOX_INVALID_INPUT; + } - QLIST_INSERT_HEAD(poison_list, frag, node); - ct3d->poison_list_cnt++; + if (len_in < + sizeof(*in) + sizeof(*in->updated_entries) * in->num_entries_updated) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; } - if (dpa + CXL_CACHE_LINE_SIZE < ent->start + ent->length) { - CXLPoison *frag; + ret = cxl_detect_malformed_extent_list(ct3d, in); + if (ret != CXL_MBOX_SUCCESS) { + return ret; + } - if (ct3d->poison_list_cnt == CXL_POISON_LIST_LIMIT) { - cxl_set_poison_list_overflowed(ct3d); - } else { - frag = g_new0(CXLPoison, 1); + ret = cxl_dc_extent_release_dry_run(ct3d, in, &updated_list, + &updated_list_size); + if (ret != CXL_MBOX_SUCCESS) { + return ret; + } - 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++; - } + /* + * 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); } - /* Any fragments have been added, free original entry */ - g_free(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; } -#define IMMEDIATE_CONFIG_CHANGE (1 << 1) -#define IMMEDIATE_DATA_CHANGE (1 << 2) -#define IMMEDIATE_POLICY_CHANGE (1 << 3) -#define IMMEDIATE_LOG_CHANGE (1 << 4) - -static struct cxl_cmd cxl_cmd_set[256][256] = { +static const struct cxl_cmd cxl_cmd_set[256][256] = { + [INFOSTAT][BACKGROUND_OPERATION_ABORT] = { "BACKGROUND_OPERATION_ABORT", + cmd_infostat_bg_op_abort, 0, 0 }, [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 }, [EVENTS][SET_INTERRUPT_POLICY] = { "EVENTS_SET_INTERRUPT_POLICY", cmd_events_set_interrupt_policy, - ~0, IMMEDIATE_CONFIG_CHANGE }, + ~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 | CXL_MBOX_BACKGROUND_OPERATION_ABORT }, + [FIRMWARE_UPDATE][ACTIVATE] = { "FIRMWARE_UPDATE_ACTIVATE", + cmd_firmware_update_activate, 2, + CXL_MBOX_BACKGROUND_OPERATION | CXL_MBOX_BACKGROUND_OPERATION_ABORT }, [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 }, + [HEALTH_INFO_ALERTS][GET_ALERT_CONFIG] = { + "HEALTH_INFO_ALERTS_GET_ALERT_CONFIG", + cmd_get_alert_config, 0, 0 }, + [HEALTH_INFO_ALERTS][SET_ALERT_CONFIG] = { + "HEALTH_INFO_ALERTS_SET_ALERT_CONFIG", + cmd_set_alert_config, 12, CXL_MBOX_IMMEDIATE_POLICY_CHANGE }, + [SANITIZE][OVERWRITE] = { "SANITIZE_OVERWRITE", cmd_sanitize_overwrite, 0, + (CXL_MBOX_IMMEDIATE_DATA_CHANGE | + CXL_MBOX_SECURITY_STATE_CHANGE | + CXL_MBOX_BACKGROUND_OPERATION | + CXL_MBOX_BACKGROUND_OPERATION_ABORT)}, + [SANITIZE][MEDIA_OPERATIONS] = { "MEDIA_OPERATIONS", cmd_media_operations, + ~0, + (CXL_MBOX_IMMEDIATE_DATA_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 | CXL_MBOX_BACKGROUND_OPERATION_ABORT)}, + [MEDIA_AND_POISON][GET_SCAN_MEDIA_RESULTS] = { + "MEDIA_AND_POISON_GET_SCAN_MEDIA_RESULTS", + cmd_media_get_scan_media_results, 0, 0 }, +}; + +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 }, +}; + +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 }, + [INFOSTAT][BACKGROUND_OPERATION_ABORT] = { "BACKGROUND_OPERATION_ABORT", + cmd_infostat_bg_op_abort, 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 }, }; -void cxl_process_mailbox(CXLDeviceState *cxl_dstate) +/* + * 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) { - uint16_t ret = CXL_MBOX_SUCCESS; - struct cxl_cmd *cxl_cmd; - uint64_t status_reg; + int ret; + const struct cxl_cmd *cxl_cmd; opcode_handler h; - uint64_t command_reg = cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD]; + CXLDeviceState *cxl_dstate; - 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]; + *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; + } + + if (len_in != cxl_cmd->in && cxl_cmd->in != ~0) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + + /* Only one bg command at a time */ + if ((cxl_cmd->effect & CXL_MBOX_BACKGROUND_OPERATION) && + cci->bg.runtime > 0) { + return CXL_MBOX_BUSY; + } + + /* 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; + + 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; + } + } + } + + 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.aborted = false; + 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, total_time; + + qemu_mutex_lock(&cci->bg.lock); + + now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); + total_time = cci->bg.starttime + cci->bg.runtime; + + 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 0x4402: /* Media Operations sanitize */ + { + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + __do_sanitize(ct3d); + } + 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); } - /* Set the return code */ - status_reg = FIELD_DP64(0, CXL_DEV_MAILBOX_STS, ERRNO, ret); + 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); - /* 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); + cci->bg.starttime = 0; + /* registers are updated, allow new bg-capable cmds */ + cci->bg.runtime = 0; - cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD] = command_reg; - cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_STS] = status_reg; + 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); + } + } - /* Tell the host we're done */ - ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CTRL, - DOORBELL, 0); + qemu_mutex_unlock(&cci->bg.lock); } -void cxl_initialize_mailbox(CXLDeviceState *cxl_dstate) +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 (cxl_cmd_set[set][cmd].handler) { - struct cxl_cmd *c = &cxl_cmd_set[set][cmd]; + if (cci->cxl_cmd_set[set][cmd].handler) { + const struct cxl_cmd *c = &cci->cxl_cmd_set[set][cmd]; struct cel_log *log = - &cxl_dstate->cel_log[cxl_dstate->cel_size]; + &cci->cel_log[cci->cel_size]; log->opcode = (set << 8) | cmd; log->effect = c->effect; - cxl_dstate->cel_size++; + cci->cel_size++; + } + } + } +} + +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.aborted = false; + cci->bg.timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, + bg_timercb, cci); + qemu_mutex_init(&cci->bg.lock); + + memset(&cci->fw, 0, sizeof(cci->fw)); + cci->fw.active_slot = 1; + cci->fw.slot[cci->fw.active_slot - 1] = true; + cci->initialized = true; +} + +void cxl_destroy_cci(CXLCCI *cci) +{ + qemu_mutex_destroy(&cci->bg.lock); + cci->initialized = false; +} + +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}, + [INFOSTAT][GET_RESPONSE_MSG_LIMIT] = { "GET_RESPONSE_MSG_LIMIT", + cmd_get_response_msg_limit, 0, 0 }, + [INFOSTAT][SET_RESPONSE_MSG_LIMIT] = { "SET_RESPONSE_MSG_LIMIT", + cmd_set_response_msg_limit, 1, 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 e261ff38816..3e375f61a98 100644 --- a/hw/cxl/meson.build +++ b/hw/cxl/meson.build @@ -6,9 +6,8 @@ system_ss.add(when: 'CONFIG_CXL', 'cxl-host.c', 'cxl-cdat.c', 'cxl-events.c', + 'switch-mailbox-cci.c', ), if_false: files( 'cxl-host-stubs.c', )) - -system_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 00000000000..223f2204331 --- /dev/null +++ b/hw/cxl/switch-mailbox-cci.c @@ -0,0 +1,112 @@ +/* + * 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" + +#define CXL_SWCCI_MSIX_MBOX 3 + +static void cswmbcci_reset(DeviceState *dev) +{ + CSWMBCCIDev *cswmb = CXL_SWITCH_MAILBOX_CCI(dev); + cxl_device_register_init_swcci(cswmb, CXL_SWCCI_MSIX_MBOX); +} + +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 const Property cxl_switch_cci_props[] = { + DEFINE_PROP_LINK("target", CSWMBCCIDev, + target, TYPE_CXL_USP, PCIDevice *), +}; + +static void cswmbcci_class_init(ObjectClass *oc, const 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 = (const 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 7b3da68d1c0..1e95ab28ef4 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 && PC_PCI + 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,20 @@ 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 + +config MAC_PVG_MMIO + bool + depends on MAC_PVG && AARCH64 + +config MAC_PVG_PCI + bool + depends on MAC_PVG && PCI + default y if PCI_DEVICES diff --git a/hw/display/apple-gfx-mmio.m b/hw/display/apple-gfx-mmio.m new file mode 100644 index 00000000000..b0b6e2993eb --- /dev/null +++ b/hw/display/apple-gfx-mmio.m @@ -0,0 +1,285 @@ +/* + * QEMU Apple ParavirtualizedGraphics.framework device, MMIO (arm64) variant + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * ParavirtualizedGraphics.framework is a set of libraries that macOS provides + * which implements 3d graphics passthrough to the host as well as a + * proprietary guest communication channel to drive it. This device model + * implements support to drive that library from within QEMU as an MMIO-based + * system device for macOS on arm64 VMs. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "block/aio-wait.h" +#include "hw/sysbus.h" +#include "hw/irq.h" +#include "apple-gfx.h" +#include "trace.h" + +#import + +OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXMMIOState, APPLE_GFX_MMIO) + +/* + * ParavirtualizedGraphics.Framework only ships header files for the PCI + * variant which does not include IOSFC descriptors and host devices. We add + * their definitions here so that we can also work with the ARM version. + */ +typedef bool(^IOSFCRaiseInterrupt)(uint32_t vector); +typedef bool(^IOSFCUnmapMemory)(void *, void *, void *, void *, void *, void *); +typedef bool(^IOSFCMapMemory)(uint64_t phys, uint64_t len, bool ro, void **va, + void *, void *); + +@interface PGDeviceDescriptor (IOSurfaceMapper) +@property (readwrite, nonatomic) bool usingIOSurfaceMapper; +@end + +@interface PGIOSurfaceHostDeviceDescriptor : NSObject +-(PGIOSurfaceHostDeviceDescriptor *)init; +@property (readwrite, nonatomic, copy, nullable) IOSFCMapMemory mapMemory; +@property (readwrite, nonatomic, copy, nullable) IOSFCUnmapMemory unmapMemory; +@property (readwrite, nonatomic, copy, nullable) IOSFCRaiseInterrupt raiseInterrupt; +@end + +@interface PGIOSurfaceHostDevice : NSObject +-(instancetype)initWithDescriptor:(PGIOSurfaceHostDeviceDescriptor *)desc; +-(uint32_t)mmioReadAtOffset:(size_t)offset; +-(void)mmioWriteAtOffset:(size_t)offset value:(uint32_t)value; +@end + +struct AppleGFXMapSurfaceMemoryJob; +struct AppleGFXMMIOState { + SysBusDevice parent_obj; + + AppleGFXState common; + + qemu_irq irq_gfx; + qemu_irq irq_iosfc; + MemoryRegion iomem_iosfc; + PGIOSurfaceHostDevice *pgiosfc; +}; + +typedef struct AppleGFXMMIOJob { + AppleGFXMMIOState *state; + uint64_t offset; + uint64_t value; + bool completed; +} AppleGFXMMIOJob; + +static void iosfc_do_read(void *opaque) +{ + AppleGFXMMIOJob *job = opaque; + job->value = [job->state->pgiosfc mmioReadAtOffset:job->offset]; + qatomic_set(&job->completed, true); + aio_wait_kick(); +} + +static uint64_t iosfc_read(void *opaque, hwaddr offset, unsigned size) +{ + AppleGFXMMIOJob job = { + .state = opaque, + .offset = offset, + .completed = false, + }; + dispatch_queue_t queue = + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + dispatch_async_f(queue, &job, iosfc_do_read); + AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed)); + + trace_apple_gfx_mmio_iosfc_read(offset, job.value); + return job.value; +} + +static void iosfc_do_write(void *opaque) +{ + AppleGFXMMIOJob *job = opaque; + [job->state->pgiosfc mmioWriteAtOffset:job->offset value:job->value]; + qatomic_set(&job->completed, true); + aio_wait_kick(); +} + +static void iosfc_write(void *opaque, hwaddr offset, uint64_t val, + unsigned size) +{ + AppleGFXMMIOJob job = { + .state = opaque, + .offset = offset, + .value = val, + .completed = false, + }; + dispatch_queue_t queue = + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + dispatch_async_f(queue, &job, iosfc_do_write); + AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed)); + + trace_apple_gfx_mmio_iosfc_write(offset, val); +} + +static const MemoryRegionOps apple_iosfc_ops = { + .read = iosfc_read, + .write = iosfc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 8, + }, +}; + +static void raise_irq_bh(void *opaque) +{ + qemu_irq *irq = opaque; + + qemu_irq_pulse(*irq); +} + +static void *apple_gfx_mmio_map_surface_memory(uint64_t guest_physical_address, + uint64_t length, bool read_only) +{ + void *mem; + MemoryRegion *region = NULL; + + RCU_READ_LOCK_GUARD(); + mem = apple_gfx_host_ptr_for_gpa_range(guest_physical_address, + length, read_only, ®ion); + if (mem) { + memory_region_ref(region); + } + return mem; +} + +static bool apple_gfx_mmio_unmap_surface_memory(void *ptr) +{ + MemoryRegion *region; + ram_addr_t offset = 0; + + RCU_READ_LOCK_GUARD(); + region = memory_region_from_host(ptr, &offset); + if (!region) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: memory at %p to be unmapped not found.\n", + __func__, ptr); + return false; + } + + trace_apple_gfx_iosfc_unmap_memory_region(ptr, region); + memory_region_unref(region); + return true; +} + +static PGIOSurfaceHostDevice *apple_gfx_prepare_iosurface_host_device( + AppleGFXMMIOState *s) +{ + PGIOSurfaceHostDeviceDescriptor *iosfc_desc = + [PGIOSurfaceHostDeviceDescriptor new]; + PGIOSurfaceHostDevice *iosfc_host_dev; + + iosfc_desc.mapMemory = + ^bool(uint64_t phys, uint64_t len, bool ro, void **va, void *e, void *f) { + *va = apple_gfx_mmio_map_surface_memory(phys, len, ro); + + trace_apple_gfx_iosfc_map_memory(phys, len, ro, va, e, f, *va); + + return *va != NULL; + }; + + iosfc_desc.unmapMemory = + ^bool(void *va, void *b, void *c, void *d, void *e, void *f) { + return apple_gfx_mmio_unmap_surface_memory(va); + }; + + iosfc_desc.raiseInterrupt = ^bool(uint32_t vector) { + trace_apple_gfx_iosfc_raise_irq(vector); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + raise_irq_bh, &s->irq_iosfc); + return true; + }; + + iosfc_host_dev = + [[PGIOSurfaceHostDevice alloc] initWithDescriptor:iosfc_desc]; + [iosfc_desc release]; + return iosfc_host_dev; +} + +static void apple_gfx_mmio_realize(DeviceState *dev, Error **errp) +{ + @autoreleasepool { + AppleGFXMMIOState *s = APPLE_GFX_MMIO(dev); + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; + + desc.raiseInterrupt = ^(uint32_t vector) { + trace_apple_gfx_raise_irq(vector); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + raise_irq_bh, &s->irq_gfx); + }; + + desc.usingIOSurfaceMapper = true; + s->pgiosfc = apple_gfx_prepare_iosurface_host_device(s); + + if (!apple_gfx_common_realize(&s->common, dev, desc, errp)) { + [s->pgiosfc release]; + s->pgiosfc = nil; + } + + [desc release]; + desc = nil; + } +} + +static void apple_gfx_mmio_init(Object *obj) +{ + AppleGFXMMIOState *s = APPLE_GFX_MMIO(obj); + + apple_gfx_common_init(obj, &s->common, TYPE_APPLE_GFX_MMIO); + + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->common.iomem_gfx); + memory_region_init_io(&s->iomem_iosfc, obj, &apple_iosfc_ops, s, + TYPE_APPLE_GFX_MMIO, 0x10000); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem_iosfc); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq_gfx); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq_iosfc); +} + +static void apple_gfx_mmio_reset(Object *obj, ResetType type) +{ + AppleGFXMMIOState *s = APPLE_GFX_MMIO(obj); + [s->common.pgdev reset]; +} + +static const Property apple_gfx_mmio_properties[] = { + DEFINE_PROP_ARRAY("display-modes", AppleGFXMMIOState, + common.num_display_modes, common.display_modes, + qdev_prop_apple_gfx_display_mode, AppleGFXDisplayMode), +}; + +static void apple_gfx_mmio_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = apple_gfx_mmio_reset; + dc->hotpluggable = false; + dc->realize = apple_gfx_mmio_realize; + + device_class_set_props(dc, apple_gfx_mmio_properties); +} + +static const TypeInfo apple_gfx_mmio_types[] = { + { + .name = TYPE_APPLE_GFX_MMIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AppleGFXMMIOState), + .class_init = apple_gfx_mmio_class_init, + .instance_init = apple_gfx_mmio_init, + } +}; +DEFINE_TYPES(apple_gfx_mmio_types) diff --git a/hw/display/apple-gfx-pci.m b/hw/display/apple-gfx-pci.m new file mode 100644 index 00000000000..b0694f4cb85 --- /dev/null +++ b/hw/display/apple-gfx-pci.m @@ -0,0 +1,157 @@ +/* + * QEMU Apple ParavirtualizedGraphics.framework device, PCI variant + * + * Copyright © 2023-2024 Phil Dennis-Jordan + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * ParavirtualizedGraphics.framework is a set of libraries that macOS provides + * which implements 3d graphics passthrough to the host as well as a + * proprietary guest communication channel to drive it. This device model + * implements support to drive that library from within QEMU as a PCI device + * aimed primarily at x86-64 macOS VMs. + */ + +#include "qemu/osdep.h" +#include "hw/pci/pci_device.h" +#include "hw/pci/msi.h" +#include "apple-gfx.h" +#include "trace.h" + +#import + +OBJECT_DECLARE_SIMPLE_TYPE(AppleGFXPCIState, APPLE_GFX_PCI) + +struct AppleGFXPCIState { + PCIDevice parent_obj; + + AppleGFXState common; +}; + +static const char *apple_gfx_pci_option_rom_path = NULL; + +static void apple_gfx_init_option_rom_path(void) +{ + NSURL *option_rom_url = PGCopyOptionROMURL(); + const char *option_rom_path = option_rom_url.fileSystemRepresentation; + apple_gfx_pci_option_rom_path = g_strdup(option_rom_path); + [option_rom_url release]; +} + +static void apple_gfx_pci_init(Object *obj) +{ + AppleGFXPCIState *s = APPLE_GFX_PCI(obj); + + if (!apple_gfx_pci_option_rom_path) { + /* + * The following is done on device not class init to avoid running + * ObjC code before fork() in -daemonize mode. + */ + PCIDeviceClass *pci = PCI_DEVICE_CLASS(object_get_class(obj)); + apple_gfx_init_option_rom_path(); + pci->romfile = apple_gfx_pci_option_rom_path; + } + + apple_gfx_common_init(obj, &s->common, TYPE_APPLE_GFX_PCI); +} + +typedef struct AppleGFXPCIInterruptJob { + PCIDevice *device; + uint32_t vector; +} AppleGFXPCIInterruptJob; + +static void apple_gfx_pci_raise_interrupt(void *opaque) +{ + AppleGFXPCIInterruptJob *job = opaque; + + if (msi_enabled(job->device)) { + msi_notify(job->device, job->vector); + } + g_free(job); +} + +static void apple_gfx_pci_interrupt(PCIDevice *dev, uint32_t vector) +{ + AppleGFXPCIInterruptJob *job; + + trace_apple_gfx_raise_irq(vector); + job = g_malloc0(sizeof(*job)); + job->device = dev; + job->vector = vector; + aio_bh_schedule_oneshot(qemu_get_aio_context(), + apple_gfx_pci_raise_interrupt, job); +} + +static void apple_gfx_pci_realize(PCIDevice *dev, Error **errp) +{ + AppleGFXPCIState *s = APPLE_GFX_PCI(dev); + int ret; + + pci_register_bar(dev, PG_PCI_BAR_MMIO, + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->common.iomem_gfx); + + ret = msi_init(dev, 0x0 /* config offset; 0 = find space */, + PG_PCI_MAX_MSI_VECTORS, true /* msi64bit */, + false /* msi_per_vector_mask */, errp); + if (ret != 0) { + return; + } + + @autoreleasepool { + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; + desc.raiseInterrupt = ^(uint32_t vector) { + apple_gfx_pci_interrupt(dev, vector); + }; + + apple_gfx_common_realize(&s->common, DEVICE(dev), desc, errp); + [desc release]; + desc = nil; + } +} + +static void apple_gfx_pci_reset(Object *obj, ResetType type) +{ + AppleGFXPCIState *s = APPLE_GFX_PCI(obj); + [s->common.pgdev reset]; +} + +static const Property apple_gfx_pci_properties[] = { + DEFINE_PROP_ARRAY("display-modes", AppleGFXPCIState, + common.num_display_modes, common.display_modes, + qdev_prop_apple_gfx_display_mode, AppleGFXDisplayMode), +}; + +static void apple_gfx_pci_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *pci = PCI_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = apple_gfx_pci_reset; + dc->desc = "macOS Paravirtualized Graphics PCI Display Controller"; + dc->hotpluggable = false; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + + pci->vendor_id = PG_PCI_VENDOR_ID; + pci->device_id = PG_PCI_DEVICE_ID; + pci->class_id = PCI_CLASS_DISPLAY_OTHER; + pci->realize = apple_gfx_pci_realize; + + device_class_set_props(dc, apple_gfx_pci_properties); +} + +static const TypeInfo apple_gfx_pci_types[] = { + { + .name = TYPE_APPLE_GFX_PCI, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(AppleGFXPCIState), + .class_init = apple_gfx_pci_class_init, + .instance_init = apple_gfx_pci_init, + .interfaces = (const InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { }, + }, + } +}; +DEFINE_TYPES(apple_gfx_pci_types) + diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h new file mode 100644 index 00000000000..a8b1d1efc09 --- /dev/null +++ b/hw/display/apple-gfx.h @@ -0,0 +1,74 @@ +/* + * Data structures and functions shared between variants of the macOS + * ParavirtualizedGraphics.framework based apple-gfx display adapter. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_APPLE_GFX_H +#define QEMU_APPLE_GFX_H + +#include "qemu/queue.h" +#include "system/memory.h" +#include "hw/qdev-properties.h" +#include "ui/surface.h" + +#define TYPE_APPLE_GFX_MMIO "apple-gfx-mmio" +#define TYPE_APPLE_GFX_PCI "apple-gfx-pci" + +@class PGDeviceDescriptor; +@protocol PGDevice; +@protocol PGDisplay; +@protocol MTLDevice; +@protocol MTLTexture; +@protocol MTLCommandQueue; + +typedef QTAILQ_HEAD(, PGTask_s) PGTaskList; + +typedef struct AppleGFXDisplayMode { + uint16_t width_px; + uint16_t height_px; + uint16_t refresh_rate_hz; +} AppleGFXDisplayMode; + +typedef struct AppleGFXState { + /* Initialised on init/realize() */ + MemoryRegion iomem_gfx; + id pgdev; + id pgdisp; + QemuConsole *con; + id mtl; + id mtl_queue; + AppleGFXDisplayMode *display_modes; + uint32_t num_display_modes; + + /* List `tasks` is protected by task_mutex */ + QemuMutex task_mutex; + PGTaskList tasks; + + /* Mutable state (BQL protected) */ + QEMUCursor *cursor; + DisplaySurface *surface; + id texture; + int8_t pending_frames; /* # guest frames in the rendering pipeline */ + bool gfx_update_requested; /* QEMU display system wants a new frame */ + bool new_frame_ready; /* Guest has rendered a frame, ready to be used */ + bool using_managed_texture_storage; + uint32_t rendering_frame_width; + uint32_t rendering_frame_height; + + /* Mutable state (atomic) */ + bool cursor_show; +} AppleGFXState; + +void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name); +bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev, + PGDeviceDescriptor *desc, Error **errp); +void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical, + uint64_t length, bool read_only, + MemoryRegion **mapping_in_region); + +extern const PropertyInfo qdev_prop_apple_gfx_display_mode; + +#endif + diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m new file mode 100644 index 00000000000..174d56ae05b --- /dev/null +++ b/hw/display/apple-gfx.m @@ -0,0 +1,880 @@ +/* + * QEMU Apple ParavirtualizedGraphics.framework device + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * ParavirtualizedGraphics.framework is a set of libraries that macOS provides + * which implements 3d graphics passthrough to the host as well as a + * proprietary guest communication channel to drive it. This device model + * implements support to drive that library from within QEMU. + */ + +#include "qemu/osdep.h" +#include "qemu/lockable.h" +#include "qemu/cutils.h" +#include "qemu/log.h" +#include "qapi/visitor.h" +#include "qapi/error.h" +#include "block/aio-wait.h" +#include "system/address-spaces.h" +#include "system/dma.h" +#include "migration/blocker.h" +#include "ui/console.h" +#include "apple-gfx.h" +#include "trace.h" + +#include +#include +#include + +#import + +static const AppleGFXDisplayMode apple_gfx_default_modes[] = { + { 1920, 1080, 60 }, + { 1440, 1080, 60 }, + { 1280, 1024, 60 }, +}; + +static Error *apple_gfx_mig_blocker; +static uint32_t next_pgdisplay_serial_num = 1; + +static dispatch_queue_t get_background_queue(void) +{ + return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); +} + +/* ------ PGTask and task operations: new/destroy/map/unmap ------ */ + +/* + * This implements the type declared in + * which is opaque from the framework's point of view. It is used in callbacks + * in the form of its typedef PGTask_t, which also already exists in the + * framework headers. + * + * A "task" in PVG terminology represents a host-virtual contiguous address + * range which is reserved in a large chunk on task creation. The mapMemory + * callback then requests ranges of guest system memory (identified by their + * GPA) to be mapped into subranges of this reserved address space. + * This type of operation isn't well-supported by QEMU's memory subsystem, + * but it is fortunately trivial to achieve with Darwin's mach_vm_remap() call, + * which allows us to refer to the same backing memory via multiple virtual + * address ranges. The Mach VM APIs are therefore used throughout for managing + * task memory. + */ +struct PGTask_s { + QTAILQ_ENTRY(PGTask_s) node; + AppleGFXState *s; + mach_vm_address_t address; + uint64_t len; + /* + * All unique MemoryRegions for which a mapping has been created in this + * task, and on which we have thus called memory_region_ref(). There are + * usually very few regions of system RAM in total, so we expect this array + * to be very short. Therefore, no need for sorting or fancy search + * algorithms, linear search will do. + * Protected by AppleGFXState's task_mutex. + */ + GPtrArray *mapped_regions; +}; + +static PGTask_t *apple_gfx_new_task(AppleGFXState *s, uint64_t len) +{ + mach_vm_address_t task_mem; + PGTask_t *task; + kern_return_t r; + + r = mach_vm_allocate(mach_task_self(), &task_mem, len, VM_FLAGS_ANYWHERE); + if (r != KERN_SUCCESS) { + return NULL; + } + + task = g_new0(PGTask_t, 1); + task->s = s; + task->address = task_mem; + task->len = len; + task->mapped_regions = g_ptr_array_sized_new(2 /* Usually enough */); + + QEMU_LOCK_GUARD(&s->task_mutex); + QTAILQ_INSERT_TAIL(&s->tasks, task, node); + + return task; +} + +static void apple_gfx_destroy_task(AppleGFXState *s, PGTask_t *task) +{ + GPtrArray *regions = task->mapped_regions; + MemoryRegion *region; + size_t i; + + for (i = 0; i < regions->len; ++i) { + region = g_ptr_array_index(regions, i); + memory_region_unref(region); + } + g_ptr_array_unref(regions); + + mach_vm_deallocate(mach_task_self(), task->address, task->len); + + QEMU_LOCK_GUARD(&s->task_mutex); + QTAILQ_REMOVE(&s->tasks, task, node); + g_free(task); +} + +void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical, + uint64_t length, bool read_only, + MemoryRegion **mapping_in_region) +{ + MemoryRegion *ram_region; + char *host_ptr; + hwaddr ram_region_offset = 0; + hwaddr ram_region_length = length; + + ram_region = address_space_translate(&address_space_memory, + guest_physical, + &ram_region_offset, + &ram_region_length, !read_only, + MEMTXATTRS_UNSPECIFIED); + + if (!ram_region || ram_region_length < length || + !memory_access_is_direct(ram_region, !read_only, + MEMTXATTRS_UNSPECIFIED)) { + return NULL; + } + + host_ptr = memory_region_get_ram_ptr(ram_region); + if (!host_ptr) { + return NULL; + } + host_ptr += ram_region_offset; + *mapping_in_region = ram_region; + return host_ptr; +} + +static bool apple_gfx_task_map_memory(AppleGFXState *s, PGTask_t *task, + uint64_t virtual_offset, + PGPhysicalMemoryRange_t *ranges, + uint32_t range_count, bool read_only) +{ + kern_return_t r; + void *source_ptr; + mach_vm_address_t target; + vm_prot_t cur_protection, max_protection; + bool success = true; + MemoryRegion *region; + + RCU_READ_LOCK_GUARD(); + QEMU_LOCK_GUARD(&s->task_mutex); + + trace_apple_gfx_map_memory(task, range_count, virtual_offset, read_only); + for (int i = 0; i < range_count; i++) { + PGPhysicalMemoryRange_t *range = &ranges[i]; + + target = task->address + virtual_offset; + virtual_offset += range->physicalLength; + + trace_apple_gfx_map_memory_range(i, range->physicalAddress, + range->physicalLength); + + region = NULL; + source_ptr = apple_gfx_host_ptr_for_gpa_range(range->physicalAddress, + range->physicalLength, + read_only, ®ion); + if (!source_ptr) { + success = false; + continue; + } + + if (!g_ptr_array_find(task->mapped_regions, region, NULL)) { + g_ptr_array_add(task->mapped_regions, region); + memory_region_ref(region); + } + + cur_protection = 0; + max_protection = 0; + /* Map guest RAM at range->physicalAddress into PG task memory range */ + r = mach_vm_remap(mach_task_self(), + &target, range->physicalLength, vm_page_size - 1, + VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, + mach_task_self(), (mach_vm_address_t)source_ptr, + false /* shared mapping, no copy */, + &cur_protection, &max_protection, + VM_INHERIT_COPY); + trace_apple_gfx_remap(r, source_ptr, target); + g_assert(r == KERN_SUCCESS); + } + + return success; +} + +static void apple_gfx_task_unmap_memory(AppleGFXState *s, PGTask_t *task, + uint64_t virtual_offset, uint64_t length) +{ + kern_return_t r; + mach_vm_address_t range_address; + + trace_apple_gfx_unmap_memory(task, virtual_offset, length); + + /* + * Replace task memory range with fresh 0 pages, undoing the mapping + * from guest RAM. + */ + range_address = task->address + virtual_offset; + r = mach_vm_allocate(mach_task_self(), &range_address, length, + VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE); + g_assert(r == KERN_SUCCESS); +} + +/* ------ Rendering and frame management ------ */ + +static void apple_gfx_render_frame_completed_bh(void *opaque); + +static void apple_gfx_render_new_frame(AppleGFXState *s) +{ + bool managed_texture = s->using_managed_texture_storage; + uint32_t width = surface_width(s->surface); + uint32_t height = surface_height(s->surface); + MTLRegion region = MTLRegionMake2D(0, 0, width, height); + id command_buffer = [s->mtl_queue commandBuffer]; + id texture = s->texture; + + assert(bql_locked()); + [texture retain]; + [command_buffer retain]; + + s->rendering_frame_width = width; + s->rendering_frame_height = height; + + dispatch_async(get_background_queue(), ^{ + /* + * This is not safe to call from the BQL/BH due to PVG-internal locks + * causing deadlocks. + */ + bool r = [s->pgdisp encodeCurrentFrameToCommandBuffer:command_buffer + texture:texture + region:region]; + if (!r) { + [texture release]; + [command_buffer release]; + qemu_log_mask(LOG_GUEST_ERROR, + "%s: encodeCurrentFrameToCommandBuffer:texture:region: " + "failed\n", __func__); + bql_lock(); + --s->pending_frames; + if (s->pending_frames > 0) { + apple_gfx_render_new_frame(s); + } + bql_unlock(); + return; + } + + if (managed_texture) { + /* "Managed" textures exist in both VRAM and RAM and must be synced. */ + id blit = [command_buffer blitCommandEncoder]; + [blit synchronizeResource:texture]; + [blit endEncoding]; + } + [texture release]; + [command_buffer addCompletedHandler: + ^(id cb) + { + aio_bh_schedule_oneshot(qemu_get_aio_context(), + apple_gfx_render_frame_completed_bh, s); + }]; + [command_buffer commit]; + [command_buffer release]; + }); +} + +static void copy_mtl_texture_to_surface_mem(id texture, void *vram) +{ + /* + * TODO: Skip this entirely on a pure Metal or headless/guest-only + * rendering path, else use a blit command encoder? Needs careful + * (double?) buffering design. + */ + size_t width = texture.width, height = texture.height; + MTLRegion region = MTLRegionMake2D(0, 0, width, height); + [texture getBytes:vram + bytesPerRow:(width * 4) + bytesPerImage:(width * height * 4) + fromRegion:region + mipmapLevel:0 + slice:0]; +} + +static void apple_gfx_render_frame_completed_bh(void *opaque) +{ + AppleGFXState *s = opaque; + + @autoreleasepool { + --s->pending_frames; + assert(s->pending_frames >= 0); + + /* Only update display if mode hasn't changed since we started rendering. */ + if (s->rendering_frame_width == surface_width(s->surface) && + s->rendering_frame_height == surface_height(s->surface)) { + copy_mtl_texture_to_surface_mem(s->texture, surface_data(s->surface)); + if (s->gfx_update_requested) { + s->gfx_update_requested = false; + dpy_gfx_update_full(s->con); + graphic_hw_update_done(s->con); + s->new_frame_ready = false; + } else { + s->new_frame_ready = true; + } + } + if (s->pending_frames > 0) { + apple_gfx_render_new_frame(s); + } + } +} + +static void apple_gfx_fb_update_display(void *opaque) +{ + AppleGFXState *s = opaque; + + assert(bql_locked()); + if (s->new_frame_ready) { + dpy_gfx_update_full(s->con); + s->new_frame_ready = false; + graphic_hw_update_done(s->con); + } else if (s->pending_frames > 0) { + s->gfx_update_requested = true; + } else { + graphic_hw_update_done(s->con); + } +} + +static const GraphicHwOps apple_gfx_fb_ops = { + .gfx_update = apple_gfx_fb_update_display, + .gfx_update_async = true, +}; + +/* ------ Mouse cursor and display mode setting ------ */ + +static void set_mode(AppleGFXState *s, uint32_t width, uint32_t height) +{ + MTLTextureDescriptor *textureDescriptor; + + if (s->surface && + width == surface_width(s->surface) && + height == surface_height(s->surface)) { + return; + } + + [s->texture release]; + + s->surface = qemu_create_displaysurface(width, height); + + @autoreleasepool { + textureDescriptor = + [MTLTextureDescriptor + texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm + width:width + height:height + mipmapped:NO]; + textureDescriptor.usage = s->pgdisp.minimumTextureUsage; + s->texture = [s->mtl newTextureWithDescriptor:textureDescriptor]; + s->using_managed_texture_storage = + (s->texture.storageMode == MTLStorageModeManaged); + } + + dpy_gfx_replace_surface(s->con, s->surface); +} + +static void update_cursor(AppleGFXState *s) +{ + assert(bql_locked()); + dpy_mouse_set(s->con, s->pgdisp.cursorPosition.x, + s->pgdisp.cursorPosition.y, qatomic_read(&s->cursor_show)); +} + +static void update_cursor_bh(void *opaque) +{ + AppleGFXState *s = opaque; + update_cursor(s); +} + +typedef struct AppleGFXSetCursorGlyphJob { + AppleGFXState *s; + NSBitmapImageRep *glyph; + PGDisplayCoord_t hotspot; +} AppleGFXSetCursorGlyphJob; + +static void set_cursor_glyph(void *opaque) +{ + AppleGFXSetCursorGlyphJob *job = opaque; + AppleGFXState *s = job->s; + NSBitmapImageRep *glyph = job->glyph; + uint32_t bpp = glyph.bitsPerPixel; + size_t width = glyph.pixelsWide; + size_t height = glyph.pixelsHigh; + size_t padding_bytes_per_row = glyph.bytesPerRow - width * 4; + const uint8_t* px_data = glyph.bitmapData; + + trace_apple_gfx_cursor_set(bpp, width, height); + + if (s->cursor) { + cursor_unref(s->cursor); + s->cursor = NULL; + } + + if (bpp == 32) { /* Shouldn't be anything else, but just to be safe... */ + s->cursor = cursor_alloc(width, height); + s->cursor->hot_x = job->hotspot.x; + s->cursor->hot_y = job->hotspot.y; + + uint32_t *dest_px = s->cursor->data; + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + /* + * NSBitmapImageRep's red & blue channels are swapped + * compared to QEMUCursor's. + */ + *dest_px = + (px_data[0] << 16u) | + (px_data[1] << 8u) | + (px_data[2] << 0u) | + (px_data[3] << 24u); + ++dest_px; + px_data += 4; + } + px_data += padding_bytes_per_row; + } + dpy_cursor_define(s->con, s->cursor); + update_cursor(s); + } + [glyph release]; + + g_free(job); +} + +/* ------ DMA (device reading system memory) ------ */ + +typedef struct AppleGFXReadMemoryJob { + QemuEvent event; + hwaddr physical_address; + uint64_t length; + void *dst; + bool success; +} AppleGFXReadMemoryJob; + +static void apple_gfx_do_read_memory(void *opaque) +{ + AppleGFXReadMemoryJob *job = opaque; + MemTxResult r; + + r = dma_memory_read(&address_space_memory, job->physical_address, + job->dst, job->length, MEMTXATTRS_UNSPECIFIED); + job->success = (r == MEMTX_OK); + + qemu_event_set(&job->event); +} + +static bool apple_gfx_read_memory(AppleGFXState *s, hwaddr physical_address, + uint64_t length, void *dst) +{ + AppleGFXReadMemoryJob job = { + .physical_address = physical_address, .length = length, .dst = dst + }; + + trace_apple_gfx_read_memory(physical_address, length, dst); + + /* Performing DMA requires BQL, so do it in a BH. */ + qemu_event_init(&job.event, 0); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + apple_gfx_do_read_memory, &job); + qemu_event_wait(&job.event); + qemu_event_destroy(&job.event); + return job.success; +} + +/* ------ Memory-mapped device I/O operations ------ */ + +typedef struct AppleGFXIOJob { + AppleGFXState *state; + uint64_t offset; + uint64_t value; + bool completed; +} AppleGFXIOJob; + +static void apple_gfx_do_read(void *opaque) +{ + AppleGFXIOJob *job = opaque; + job->value = [job->state->pgdev mmioReadAtOffset:job->offset]; + qatomic_set(&job->completed, true); + aio_wait_kick(); +} + +static uint64_t apple_gfx_read(void *opaque, hwaddr offset, unsigned size) +{ + AppleGFXIOJob job = { + .state = opaque, + .offset = offset, + .completed = false, + }; + dispatch_queue_t queue = get_background_queue(); + + dispatch_async_f(queue, &job, apple_gfx_do_read); + AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed)); + + trace_apple_gfx_read(offset, job.value); + return job.value; +} + +static void apple_gfx_do_write(void *opaque) +{ + AppleGFXIOJob *job = opaque; + [job->state->pgdev mmioWriteAtOffset:job->offset value:job->value]; + qatomic_set(&job->completed, true); + aio_wait_kick(); +} + +static void apple_gfx_write(void *opaque, hwaddr offset, uint64_t val, + unsigned size) +{ + /* + * The methods mmioReadAtOffset: and especially mmioWriteAtOffset: can + * trigger synchronous operations on other dispatch queues, which in turn + * may call back out on one or more of the callback blocks. For this reason, + * and as we are holding the BQL, we invoke the I/O methods on a pool + * thread and handle AIO tasks while we wait. Any work in the callbacks + * requiring the BQL will in turn schedule BHs which this thread will + * process while waiting. + */ + AppleGFXIOJob job = { + .state = opaque, + .offset = offset, + .value = val, + .completed = false, + }; + dispatch_queue_t queue = get_background_queue(); + + dispatch_async_f(queue, &job, apple_gfx_do_write); + AIO_WAIT_WHILE(NULL, !qatomic_read(&job.completed)); + + trace_apple_gfx_write(offset, val); +} + +static const MemoryRegionOps apple_gfx_ops = { + .read = apple_gfx_read, + .write = apple_gfx_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static size_t apple_gfx_get_default_mmio_range_size(void) +{ + size_t mmio_range_size; + @autoreleasepool { + PGDeviceDescriptor *desc = [PGDeviceDescriptor new]; + mmio_range_size = desc.mmioLength; + [desc release]; + } + return mmio_range_size; +} + +/* ------ Initialisation and startup ------ */ + +void apple_gfx_common_init(Object *obj, AppleGFXState *s, const char* obj_name) +{ + size_t mmio_range_size = apple_gfx_get_default_mmio_range_size(); + + trace_apple_gfx_common_init(obj_name, mmio_range_size); + memory_region_init_io(&s->iomem_gfx, obj, &apple_gfx_ops, s, obj_name, + mmio_range_size); + + /* TODO: PVG framework supports serialising device state: integrate it! */ +} + +static void apple_gfx_register_task_mapping_handlers(AppleGFXState *s, + PGDeviceDescriptor *desc) +{ + desc.createTask = ^(uint64_t vmSize, void * _Nullable * _Nonnull baseAddress) { + PGTask_t *task = apple_gfx_new_task(s, vmSize); + *baseAddress = (void *)task->address; + trace_apple_gfx_create_task(vmSize, *baseAddress); + return task; + }; + + desc.destroyTask = ^(PGTask_t * _Nonnull task) { + trace_apple_gfx_destroy_task(task, task->mapped_regions->len); + + apple_gfx_destroy_task(s, task); + }; + + desc.mapMemory = ^bool(PGTask_t * _Nonnull task, uint32_t range_count, + uint64_t virtual_offset, bool read_only, + PGPhysicalMemoryRange_t * _Nonnull ranges) { + return apple_gfx_task_map_memory(s, task, virtual_offset, + ranges, range_count, read_only); + }; + + desc.unmapMemory = ^bool(PGTask_t * _Nonnull task, uint64_t virtual_offset, + uint64_t length) { + apple_gfx_task_unmap_memory(s, task, virtual_offset, length); + return true; + }; + + desc.readMemory = ^bool(uint64_t physical_address, uint64_t length, + void * _Nonnull dst) { + return apple_gfx_read_memory(s, physical_address, length, dst); + }; +} + +static void new_frame_handler_bh(void *opaque) +{ + AppleGFXState *s = opaque; + + /* Drop frames if guest gets too far ahead. */ + if (s->pending_frames >= 2) { + return; + } + ++s->pending_frames; + if (s->pending_frames > 1) { + return; + } + + @autoreleasepool { + apple_gfx_render_new_frame(s); + } +} + +static PGDisplayDescriptor *apple_gfx_prepare_display_descriptor(AppleGFXState *s) +{ + PGDisplayDescriptor *disp_desc = [PGDisplayDescriptor new]; + + disp_desc.name = @"QEMU display"; + disp_desc.sizeInMillimeters = NSMakeSize(400., 300.); /* A 20" display */ + disp_desc.queue = dispatch_get_main_queue(); + disp_desc.newFrameEventHandler = ^(void) { + trace_apple_gfx_new_frame(); + aio_bh_schedule_oneshot(qemu_get_aio_context(), new_frame_handler_bh, s); + }; + disp_desc.modeChangeHandler = ^(PGDisplayCoord_t sizeInPixels, + OSType pixelFormat) { + trace_apple_gfx_mode_change(sizeInPixels.x, sizeInPixels.y); + + BQL_LOCK_GUARD(); + set_mode(s, sizeInPixels.x, sizeInPixels.y); + }; + disp_desc.cursorGlyphHandler = ^(NSBitmapImageRep *glyph, + PGDisplayCoord_t hotspot) { + AppleGFXSetCursorGlyphJob *job = g_malloc0(sizeof(*job)); + job->s = s; + job->glyph = glyph; + job->hotspot = hotspot; + [glyph retain]; + aio_bh_schedule_oneshot(qemu_get_aio_context(), + set_cursor_glyph, job); + }; + disp_desc.cursorShowHandler = ^(BOOL show) { + trace_apple_gfx_cursor_show(show); + qatomic_set(&s->cursor_show, show); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + update_cursor_bh, s); + }; + disp_desc.cursorMoveHandler = ^(void) { + trace_apple_gfx_cursor_move(); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + update_cursor_bh, s); + }; + + return disp_desc; +} + +static NSArray *apple_gfx_create_display_mode_array( + const AppleGFXDisplayMode display_modes[], uint32_t display_mode_count) +{ + PGDisplayMode *mode_obj; + NSMutableArray *mode_array = + [[NSMutableArray alloc] initWithCapacity:display_mode_count]; + + for (unsigned i = 0; i < display_mode_count; i++) { + const AppleGFXDisplayMode *mode = &display_modes[i]; + trace_apple_gfx_display_mode(i, mode->width_px, mode->height_px); + PGDisplayCoord_t mode_size = { mode->width_px, mode->height_px }; + + mode_obj = + [[PGDisplayMode alloc] initWithSizeInPixels:mode_size + refreshRateInHz:mode->refresh_rate_hz]; + [mode_array addObject:mode_obj]; + [mode_obj release]; + } + + return mode_array; +} + +static id copy_suitable_metal_device(void) +{ + id dev = nil; + NSArray> *devs = MTLCopyAllDevices(); + + /* Prefer a unified memory GPU. Failing that, pick a non-removable GPU. */ + for (size_t i = 0; i < devs.count; ++i) { + if (devs[i].hasUnifiedMemory) { + dev = devs[i]; + break; + } + if (!devs[i].removable) { + dev = devs[i]; + } + } + + if (dev != nil) { + [dev retain]; + } else { + dev = MTLCreateSystemDefaultDevice(); + } + [devs release]; + + return dev; +} + +bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev, + PGDeviceDescriptor *desc, Error **errp) +{ + PGDisplayDescriptor *disp_desc; + const AppleGFXDisplayMode *display_modes = apple_gfx_default_modes; + uint32_t num_display_modes = ARRAY_SIZE(apple_gfx_default_modes); + NSArray *mode_array; + + if (apple_gfx_mig_blocker == NULL) { + error_setg(&apple_gfx_mig_blocker, + "Migration state blocked by apple-gfx display device"); + if (migrate_add_blocker(&apple_gfx_mig_blocker, errp) < 0) { + return false; + } + } + + qemu_mutex_init(&s->task_mutex); + QTAILQ_INIT(&s->tasks); + s->mtl = copy_suitable_metal_device(); + s->mtl_queue = [s->mtl newCommandQueue]; + + desc.device = s->mtl; + + apple_gfx_register_task_mapping_handlers(s, desc); + + s->cursor_show = true; + + s->pgdev = PGNewDeviceWithDescriptor(desc); + + disp_desc = apple_gfx_prepare_display_descriptor(s); + /* + * Although the framework does, this integration currently does not support + * multiple virtual displays connected to a single PV graphics device. + * It is however possible to create + * more than one instance of the device, each with one display. The macOS + * guest will ignore these displays if they share the same serial number, + * so ensure each instance gets a unique one. + */ + s->pgdisp = [s->pgdev newDisplayWithDescriptor:disp_desc + port:0 + serialNum:next_pgdisplay_serial_num++]; + [disp_desc release]; + + if (s->display_modes != NULL && s->num_display_modes > 0) { + trace_apple_gfx_common_realize_modes_property(s->num_display_modes); + display_modes = s->display_modes; + num_display_modes = s->num_display_modes; + } + s->pgdisp.modeList = mode_array = + apple_gfx_create_display_mode_array(display_modes, num_display_modes); + [mode_array release]; + + s->con = graphic_console_init(dev, 0, &apple_gfx_fb_ops, s); + return true; +} + +/* ------ Display mode list device property ------ */ + +static void apple_gfx_get_display_mode(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + AppleGFXDisplayMode *mode = object_field_prop_ptr(obj, prop); + /* 3 uint16s (max 5 digits) + 2 separator characters + nul. */ + char buffer[5 * 3 + 2 + 1]; + char *pos = buffer; + + int rc = snprintf(buffer, sizeof(buffer), + "%"PRIu16"x%"PRIu16"@%"PRIu16, + mode->width_px, mode->height_px, + mode->refresh_rate_hz); + assert(rc < sizeof(buffer)); + + visit_type_str(v, name, &pos, errp); +} + +static void apple_gfx_set_display_mode(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + AppleGFXDisplayMode *mode = object_field_prop_ptr(obj, prop); + const char *endptr; + g_autofree char *str = NULL; + int ret; + int val; + + if (!visit_type_str(v, name, &str, errp)) { + return; + } + + endptr = str; + + ret = qemu_strtoi(endptr, &endptr, 10, &val); + if (ret || val > UINT16_MAX || val <= 0) { + error_setg(errp, "width in '%s' must be a decimal integer number" + " of pixels in the range 1..65535", name); + return; + } + mode->width_px = val; + if (*endptr != 'x') { + goto separator_error; + } + + ret = qemu_strtoi(endptr + 1, &endptr, 10, &val); + if (ret || val > UINT16_MAX || val <= 0) { + error_setg(errp, "height in '%s' must be a decimal integer number" + " of pixels in the range 1..65535", name); + return; + } + mode->height_px = val; + if (*endptr != '@') { + goto separator_error; + } + + ret = qemu_strtoi(endptr + 1, &endptr, 10, &val); + if (ret || val > UINT16_MAX || val <= 0) { + error_setg(errp, "refresh rate in '%s'" + " must be a positive decimal integer (Hertz)", name); + return; + } + mode->refresh_rate_hz = val; + return; + +separator_error: + error_setg(errp, + "Each display mode takes the format 'x@'"); +} + +const PropertyInfo qdev_prop_apple_gfx_display_mode = { + .type = "display_mode", + .description = + "Display mode in pixels and Hertz, as x@ " + "Example: 3840x2160@60", + .get = apple_gfx_get_display_mode, + .set = apple_gfx_set_display_mode, +}; diff --git a/hw/display/artist.c b/hw/display/artist.c index fde050c882b..3fafc8a222b 100644 --- a/hw/display/artist.c +++ b/hw/display/artist.c @@ -48,6 +48,7 @@ struct ARTISTState { struct vram_buffer vram_buffer[16]; + bool disable; uint16_t width; uint16_t height; uint16_t depth; @@ -1211,8 +1212,8 @@ static uint64_t artist_reg_read(void *opaque, hwaddr addr, unsigned size) break; case 0x380004: - /* 0x02000000 Buserror */ - val = 0x6dc20006; + /* magic number detected by SeaBIOS-hppa */ + val = s->disable ? 0 : 0x6dc20006; break; default: @@ -1432,10 +1433,10 @@ static int vmstate_artist_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_artist = { .name = "artist", - .version_id = 2, + .version_id = 3, .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), @@ -1470,28 +1471,29 @@ static const VMStateDescription vmstate_artist = { VMSTATE_UINT32(font_write1, ARTISTState), VMSTATE_UINT32(font_write2, ARTISTState), VMSTATE_UINT32(font_write_pos_y, ARTISTState), + VMSTATE_BOOL(disable, ARTISTState), VMSTATE_END_OF_LIST() } }; -static Property artist_properties[] = { +static const Property artist_properties[] = { DEFINE_PROP_UINT16("width", ARTISTState, width, 1280), DEFINE_PROP_UINT16("height", ARTISTState, height, 1024), DEFINE_PROP_UINT16("depth", ARTISTState, depth, 8), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_BOOL("disable", ARTISTState, disable, false), }; static void artist_reset(DeviceState *qdev) { } -static void artist_class_init(ObjectClass *klass, void *data) +static void artist_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 6e38e005022..7de27732cdc 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; @@ -1008,21 +1039,22 @@ static void ati_vga_exit(PCIDevice *dev) graphic_console_close(s->vga.con); } -static Property ati_vga_properties[] = { +static const Property ati_vga_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", ATIVGAState, vga.vram_size_mb, 16), DEFINE_PROP_STRING("model", ATIVGAState, model), 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), - DEFINE_PROP_END_OF_LIST() + /* this is a debug option, prefer PROP_UINT over PROP_BIT for simplicity */ + DEFINE_PROP_UINT8("x-pixman", ATIVGAState, use_pixman, DEFAULT_X_PIXMAN), }; -static void ati_vga_class_init(ObjectClass *klass, void *data) +static void ati_vga_class_init(ObjectClass *klass, const 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,12 +1067,19 @@ 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, - .interfaces = (InterfaceInfo[]) { + .instance_init = ati_vga_init, + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index 7d786653e87..309bb5ccb6c 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 bd0ecd48c7a..3ffa7f35df9 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 e8d3c7af752..f5a47b82b08 100644 --- a/hw/display/ati_int.h +++ b/hw/display/ati_int.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 d6282b2ef21..d7127748ffe 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 a05277674f2..820e67ac8bb 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), @@ -429,7 +429,7 @@ static void bcm2835_fb_realize(DeviceState *dev, Error **errp) qemu_console_resize(s->con, s->config.xres, s->config.yres); } -static Property bcm2835_fb_props[] = { +static const Property bcm2835_fb_props[] = { DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/ DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size, DEFAULT_VCRAM_SIZE), @@ -440,16 +440,15 @@ static Property bcm2835_fb_props[] = { initial_config.pixo, 1), /* 1=RGB, 0=BGR */ DEFINE_PROP_UINT32("alpha", BCM2835FBState, initial_config.alpha, 2), /* alpha ignored */ - DEFINE_PROP_END_OF_LIST() }; -static void bcm2835_fb_class_init(ObjectClass *klass, void *data) +static void bcm2835_fb_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 030abbe7d4d..00000000000 --- 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 9138e98c3b6..ad2821c9745 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -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), @@ -345,14 +345,13 @@ static void bochs_display_exit(PCIDevice *dev) graphic_console_close(s->con); } -static Property bochs_display_properties[] = { +static const Property bochs_display_properties[] = { DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * MiB), DEFINE_PROP_BOOL("edid", BochsDisplayState, enable_edid, true), DEFINE_EDID_PROPERTIES(BochsDisplayState, edid_info), - DEFINE_PROP_END_OF_LIST(), }; -static void bochs_display_class_init(ObjectClass *klass, void *data) +static void bochs_display_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -375,7 +374,7 @@ static const TypeInfo bochs_display_type_info = { .instance_size = sizeof(BochsDisplayState), .instance_init = bochs_display_init, .class_init = bochs_display_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, diff --git a/hw/display/cg3.c b/hw/display/cg3.c index 2e9656ae1c3..daeef152174 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), @@ -361,20 +361,19 @@ static void cg3_reset(DeviceState *d) qemu_irq_lower(s->irq); } -static Property cg3_properties[] = { +static const Property cg3_properties[] = { DEFINE_PROP_UINT32("vram-size", CG3State, vram_size, -1), DEFINE_PROP_UINT16("width", CG3State, width, -1), DEFINE_PROP_UINT16("height", CG3State, height, -1), DEFINE_PROP_UINT16("depth", CG3State, depth, -1), - DEFINE_PROP_END_OF_LIST(), }; -static void cg3_class_init(ObjectClass *klass, void *data) +static void cg3_class_init(ObjectClass *klass, const 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 b80f98b6c4c..ef08694626d 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -36,13 +36,14 @@ #include "qemu/module.h" #include "qemu/units.h" #include "qemu/log.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "qapi/error.h" #include "trace.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) @@ -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; @@ -2982,17 +2982,16 @@ static void pci_cirrus_vga_realize(PCIDevice *dev, Error **errp) } } -static Property pci_vga_cirrus_properties[] = { +static const Property pci_vga_cirrus_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", struct PCICirrusVGAState, cirrus_vga.vga.vram_size_mb, 4), DEFINE_PROP_BOOL("blitter", struct PCICirrusVGAState, cirrus_vga.enable_blitter, true), DEFINE_PROP_BOOL("global-vmstate", struct PCICirrusVGAState, cirrus_vga.vga.global_vmstate, false), - DEFINE_PROP_END_OF_LIST(), }; -static void cirrus_vga_class_init(ObjectClass *klass, void *data) +static void cirrus_vga_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -3014,7 +3013,7 @@ static const TypeInfo cirrus_vga_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCICirrusVGAState), .class_init = cirrus_vga_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/display/cirrus_vga_isa.c b/hw/display/cirrus_vga_isa.c index 84be51670ed..4b55c48eff8 100644 --- a/hw/display/cirrus_vga_isa.c +++ b/hw/display/cirrus_vga_isa.c @@ -69,15 +69,14 @@ static void isa_cirrus_vga_realizefn(DeviceState *dev, Error **errp) /* FIXME not qdev yet */ } -static Property isa_cirrus_vga_properties[] = { +static const Property isa_cirrus_vga_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", struct ISACirrusVGAState, cirrus_vga.vga.vram_size_mb, 4), DEFINE_PROP_BOOL("blitter", struct ISACirrusVGAState, cirrus_vga.enable_blitter, true), - DEFINE_PROP_END_OF_LIST(), }; -static void isa_cirrus_vga_class_init(ObjectClass *klass, void *data) +static void isa_cirrus_vga_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/display/dm163.c b/hw/display/dm163.c new file mode 100644 index 00000000000..f8340d8275e --- /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, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->desc = "DM163 8x3-channel constant current LED driver"; + 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 64463654a1a..a157dc64e76 100644 --- a/hw/display/dpcd.c +++ b/hw/display/dpcd.c @@ -135,17 +135,17 @@ 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() } }; -static void dpcd_class_init(ObjectClass *oc, void *data) +static void dpcd_class_init(ObjectClass *oc, const 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/edid-region.c b/hw/display/edid-region.c index 675429dc18b..f1596fba9a9 100644 --- a/hw/display/edid-region.c +++ b/hw/display/edid-region.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/display/edid.h" static uint64_t edid_region_read(void *ptr, hwaddr addr, unsigned size) diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 34a960a9765..c61e0280a7c 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,11 @@ static const GraphicHwOps exynos4210_fimd_ops = { .gfx_update = exynos4210_fimd_update, }; +static const Property exynos4210_fimd_properties[] = { + DEFINE_PROP_LINK("framebuffer-memory", Exynos4210fimdState, fbmem, + TYPE_MEMORY_REGION, MemoryRegion *), +}; + static void exynos4210_fimd_init(Object *obj) { Exynos4210fimdState *s = EXYNOS4210_FIMD(obj); @@ -1944,16 +1950,22 @@ 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); } -static void exynos4210_fimd_class_init(ObjectClass *klass, void *data) +static void exynos4210_fimd_class_init(ObjectClass *klass, const 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/framebuffer.h b/hw/display/framebuffer.h index 38fa0dcec61..29a828ce7a3 100644 --- a/hw/display/framebuffer.h +++ b/hw/display/framebuffer.h @@ -1,7 +1,7 @@ #ifndef QEMU_FRAMEBUFFER_H #define QEMU_FRAMEBUFFER_H -#include "exec/memory.h" +#include "system/memory.h" /* Framebuffer device helper routines. */ diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index 2903cab82d8..a6ddc21d3e4 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -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), @@ -512,29 +512,28 @@ static void g364fb_sysbus_reset(DeviceState *d) g364fb_reset(&s->g364); } -static Property g364fb_sysbus_properties[] = { +static const Property g364fb_sysbus_properties[] = { DEFINE_PROP_UINT32("vram_size", G364SysBusState, g364.vram_size, 8 * MiB), - DEFINE_PROP_END_OF_LIST(), }; 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() } }; -static void g364fb_sysbus_class_init(ObjectClass *klass, void *data) +static void g364fb_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 146489518c7..2adfc1a1472 100644 --- a/hw/display/i2c-ddc.c +++ b/hw/display/i2c-ddc.c @@ -88,24 +88,23 @@ 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() } }; -static Property i2c_ddc_properties[] = { +static const Property i2c_ddc_properties[] = { DEFINE_EDID_PROPERTIES(I2CDDCState, edid_info), - DEFINE_PROP_END_OF_LIST(), }; -static void i2c_ddc_class_init(ObjectClass *oc, void *data) +static void i2c_ddc_class_init(ObjectClass *oc, const 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 dd5f4696c4f..90e82b58be2 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() } @@ -294,13 +294,13 @@ static void jazz_led_reset(DeviceState *d) qemu_console_resize(s->con, 60, 80); } -static void jazz_led_class_init(ObjectClass *klass, void *data) +static void jazz_led_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 2f8e0165664..574d667173c 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 @@ -383,7 +383,6 @@ static void macfb_sense_write(MacfbState *s, uint32_t val) s->regs[DAFB_MODE_SENSE >> 2] = val; trace_macfb_sense_write(val); - return; } static void macfb_update_mode(MacfbState *s) @@ -537,6 +536,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 +587,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 +626,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 +713,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); @@ -754,56 +757,54 @@ static void macfb_nubus_reset(DeviceState *d) macfb_reset(&s->macfb); } -static Property macfb_sysbus_properties[] = { +static const Property macfb_sysbus_properties[] = { DEFINE_PROP_UINT32("width", MacfbSysBusState, macfb.width, 640), DEFINE_PROP_UINT32("height", MacfbSysBusState, macfb.height, 480), DEFINE_PROP_UINT8("depth", MacfbSysBusState, macfb.depth, 8), DEFINE_PROP_UINT8("display", MacfbSysBusState, macfb.type, MACFB_DISPLAY_VGA), - DEFINE_PROP_END_OF_LIST(), }; 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() } }; -static Property macfb_nubus_properties[] = { +static const Property macfb_nubus_properties[] = { DEFINE_PROP_UINT32("width", MacfbNubusState, macfb.width, 640), DEFINE_PROP_UINT32("height", MacfbNubusState, macfb.height, 480), DEFINE_PROP_UINT8("depth", MacfbNubusState, macfb.depth, 8), DEFINE_PROP_UINT8("display", MacfbNubusState, macfb.type, MACFB_DISPLAY_VGA), - DEFINE_PROP_END_OF_LIST(), }; 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() } }; -static void macfb_sysbus_class_init(ObjectClass *klass, void *data) +static void macfb_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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); } -static void macfb_nubus_class_init(ObjectClass *klass, void *data) +static void macfb_nubus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); MacfbNubusDeviceClass *ndc = NUBUS_MACFB_CLASS(klass); @@ -813,7 +814,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 413ba4ab249..90e6c041bdb 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -3,7 +3,7 @@ hw_display_modules = {} 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')) -system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('ramfb.c')) +system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('ramfb.c'), if_false: files('ramfb-stubs.c')) system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('ramfb-standalone.c')) system_ss.add(when: 'CONFIG_VGA_CIRRUS', if_true: files('cirrus_vga.c')) @@ -22,13 +22,9 @@ 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')) -system_ss.add(when: 'CONFIG_BLIZZARD', if_true: files('blizzard.c')) system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_fimd.c')) system_ss.add(when: 'CONFIG_FRAMEBUFFER', if_true: files('framebuffer.c')) -system_ss.add(when: 'CONFIG_ZAURUS', if_true: files('tc6393xb.c')) -system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dss.c')) -system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_lcd.c')) system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_fb.c')) system_ss.add(when: 'CONFIG_SM501', if_true: files('sm501.c')) system_ss.add(when: 'CONFIG_TCX', if_true: files('tcx.c')) @@ -37,6 +33,8 @@ system_ss.add(when: 'CONFIG_MACFB', if_true: files('macfb.c')) system_ss.add(when: 'CONFIG_NEXTCUBE', if_true: files('next-fb.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 @@ -57,19 +55,24 @@ if config_all_devices.has_key('CONFIG_QXL') endif system_ss.add(when: 'CONFIG_DPCD', if_true: files('dpcd.c')) -system_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx_dp.c')) +system_ss.add(when: 'CONFIG_XLNX_DISPLAYPORT', if_true: files('xlnx_dp.c')) system_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c')) -system_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]) +system_ss.add(when: [pvg, 'CONFIG_MAC_PVG_PCI'], if_true: [files('apple-gfx.m', 'apple-gfx-pci.m')]) +system_ss.add(when: [pvg, 'CONFIG_MAC_PVG_MMIO'], if_true: [files('apple-gfx.m', 'apple-gfx-mmio.m')]) 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} @@ -79,6 +82,13 @@ if config_all_devices.has_key('CONFIG_VIRTIO_GPU') 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') @@ -95,6 +105,12 @@ if config_all_devices.has_key('CONFIG_VIRTIO_PCI') 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') @@ -107,15 +123,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 system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_lcdc.c')) -system_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-vga-stub.c')) modules += { 'hw-display': hw_display_modules } diff --git a/hw/display/next-fb.c b/hw/display/next-fb.c index 8446ff3c00e..ec81b766a7e 100644 --- a/hw/display/next-fb.c +++ b/hw/display/next-fb.c @@ -119,7 +119,7 @@ static void nextfb_realize(DeviceState *dev, Error **errp) qemu_console_resize(s->con, s->cols, s->rows); } -static void nextfb_class_init(ObjectClass *oc, void *data) +static void nextfb_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/display/omap_dss.c b/hw/display/omap_dss.c deleted file mode 100644 index f33fc7606d3..00000000000 --- a/hw/display/omap_dss.c +++ /dev/null @@ -1,1093 +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 = 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 = 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 = 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 = 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 = 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 = 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/pl110.c b/hw/display/pl110.c index 4bf15c1da51..09c3c59e0ed 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,21 @@ static const GraphicHwOps pl110_gfx_ops = { .gfx_update = pl110_update_display, }; +static const Property pl110_properties[] = { + DEFINE_PROP_LINK("framebuffer-memory", PL110State, fbmem, + TYPE_MEMORY_REGION, MemoryRegion *), +}; + 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); @@ -570,13 +580,14 @@ static void pl111_init(Object *obj) s->version = VERSION_PL111; } -static void pl110_class_init(ObjectClass *klass, void *data) +static void pl110_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 eb83d882222..00000000000 --- 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 ec99ec887a6..eda6d3de37c 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "qxl.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "trace.h" static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) @@ -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, @@ -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); diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 7bb00d68f57..18f482ca7f7 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -29,7 +29,8 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "hw/qdev-properties.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" +#include "migration/cpr.h" #include "migration/vmstate.h" #include "trace.h" @@ -50,7 +51,7 @@ #undef ALIGN #define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1)) -#define PIXEL_SIZE 0.2936875 //1280x1024 is 14.8" x 11.9" +#define PIXEL_SIZE 0.2936875 /* 1280x1024 is 14.8" x 11.9" */ #define QXL_MODE(_x, _y, _b, _o) \ { .x_res = _x, \ @@ -333,6 +334,10 @@ static void init_qxl_rom(PCIQXLDevice *d) uint32_t fb; int i, n; + if (cpr_is_incoming()) { + goto skip_init; + } + memset(rom, 0, d->rom_size); rom->magic = cpu_to_le32(QXL_ROM_MAGIC); @@ -390,6 +395,7 @@ static void init_qxl_rom(PCIQXLDevice *d) sizeof(rom->client_monitors_config)); } +skip_init: d->shadow_rom = *rom; d->rom = rom; d->modes = modes; @@ -403,6 +409,9 @@ static void init_qxl_ram(PCIQXLDevice *d) buf = d->vga.vram_ptr; d->ram = (QXLRam *)(buf + le32_to_cpu(d->shadow_rom.ram_header_offset)); + if (cpr_is_incoming()) { + return; + } d->ram->magic = cpu_to_le32(QXL_RAM_MAGIC); d->ram->int_pending = cpu_to_le32(0); d->ram->int_mask = cpu_to_le32(0); @@ -539,6 +548,10 @@ static void interface_set_compression_level(QXLInstance *sin, int level) trace_qxl_interface_set_compression_level(qxl->id, level); qxl->shadow_rom.compression_level = cpu_to_le32(level); + if (cpr_is_incoming()) { + assert(qxl->rom->compression_level == cpu_to_le32(level)); + return; + } qxl->rom->compression_level = cpu_to_le32(level); qxl_rom_set_dirty(qxl); } @@ -997,7 +1010,8 @@ static void interface_set_client_capabilities(QXLInstance *sin, } if (runstate_check(RUN_STATE_INMIGRATE) || - runstate_check(RUN_STATE_POSTMIGRATE)) { + runstate_check(RUN_STATE_POSTMIGRATE) || + cpr_is_incoming()) { return; } @@ -1200,6 +1214,10 @@ static void qxl_reset_state(PCIQXLDevice *d) { QXLRom *rom = d->rom; + if (cpr_is_incoming()) { + return; + } + qxl_check_state(d); d->shadow_rom.update_id = cpu_to_le32(0); *rom = d->shadow_rom; @@ -1301,8 +1319,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; @@ -1370,8 +1388,11 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, memslot.virt_start = virt_start + (guest_start - pci_start); memslot.virt_end = virt_start + (guest_end - pci_start); memslot.addr_delta = memslot.virt_start - delta; - memslot.generation = d->rom->slot_generation = 0; - qxl_rom_set_dirty(d); + if (!cpr_is_incoming()) { + d->rom->slot_generation = 0; + qxl_rom_set_dirty(d); + } + memslot.generation = d->rom->slot_generation; qemu_spice_add_memslot(&d->ssd, &memslot, async); d->guest_slots[slot_id].mr = mr; @@ -2388,7 +2409,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), @@ -2400,7 +2421,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), @@ -2419,7 +2440,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() }, @@ -2432,7 +2453,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), @@ -2452,13 +2473,13 @@ 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 } }; -static Property qxl_properties[] = { +static const Property qxl_properties[] = { DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * MiB), DEFINE_PROP_UINT64("vram_size", PCIQXLDevice, vram32_size, 64 * MiB), DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, @@ -2475,10 +2496,9 @@ static Property qxl_properties[] = { DEFINE_PROP_UINT32("xres", PCIQXLDevice, xres, 0), DEFINE_PROP_UINT32("yres", PCIQXLDevice, yres, 0), DEFINE_PROP_BOOL("global-vmstate", PCIQXLDevice, vga.global_vmstate, false), - DEFINE_PROP_END_OF_LIST(), }; -static void qxl_pci_class_init(ObjectClass *klass, void *data) +static void qxl_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -2486,7 +2506,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); } @@ -2497,13 +2517,13 @@ static const TypeInfo qxl_pci_type_info = { .instance_size = sizeof(PCIQXLDevice), .abstract = true, .class_init = qxl_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -static void qxl_primary_class_init(ObjectClass *klass, void *data) +static void qxl_primary_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -2523,7 +2543,7 @@ static const TypeInfo qxl_primary_info = { module_obj("qxl-vga"); module_kconfig(QXL); -static void qxl_secondary_class_init(ObjectClass *klass, void *data) +static void qxl_secondary_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/display/qxl.h b/hw/display/qxl.h index fdac14edade..e0a85a5ca49 100644 --- a/hw/display/qxl.h +++ b/hw/display/qxl.h @@ -159,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 8c0094397f5..08f2d5db4ec 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,19 +42,42 @@ static void ramfb_realizefn(DeviceState *dev, Error **errp) ramfb->state = ramfb_setup(errp); } -static void ramfb_class_initfn(ObjectClass *klass, void *data) +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 const Property ramfb_properties[] = { + DEFINE_PROP_BOOL("x-migrate", RAMFBStandaloneState, migrate, true), +}; + +static void ramfb_class_initfn(ObjectClass *klass, const 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 = { .name = TYPE_RAMFB_DEVICE, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_DYNAMIC_SYS_BUS_DEVICE, .instance_size = sizeof(RAMFBStandaloneState), .class_init = ramfb_class_initfn, }; diff --git a/hw/display/ramfb-stubs.c b/hw/display/ramfb-stubs.c new file mode 100644 index 00000000000..cf64733b10c --- /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 79b9754a582..8c0f907673d 100644 --- a/hw/display/ramfb.c +++ b/hw/display/ramfb.c @@ -17,7 +17,7 @@ #include "hw/display/ramfb.h" #include "hw/display/bochs-vbe.h" /* for limits */ #include "ui/console.h" -#include "sysemu/reset.h" +#include "system/reset.h" struct QEMU_PACKED RAMFBCfg { uint64_t addr; @@ -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 664fd4046d8..d00d3e9fc54 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), @@ -167,7 +167,7 @@ static void sii9022_realize(DeviceState *dev, Error **errp) i2c_slave_create_simple(bus, TYPE_I2CDDC, 0x50); } -static void sii9022_class_init(ObjectClass *klass, void *data) +static void sii9022_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); @@ -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 0eecd007017..6d2f18684c3 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -29,7 +29,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "hw/usb/hcd-ohci.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "ui/console.h" #include "hw/sysbus.h" #include "migration/vmstate.h" @@ -438,6 +438,12 @@ #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[] = { [0] = 4 * MiB, @@ -730,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; @@ -828,9 +833,11 @@ static void sm501_2d_operation(SM501State *s) de = db + (width + (height - 1) * dst_pitch) * bypp; overlap = (db < se && sb < de); } +#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)) { @@ -860,7 +867,9 @@ static void sm501_2d_operation(SM501State *s) dst_pitch * bypp / sizeof(uint32_t), 8 * bypp, 8 * bypp, src_x, src_y, dst_x, dst_y, width, height); - } else { + } else +#endif + { fallback = true; } if (fallback) { @@ -894,20 +903,23 @@ static void sm501_2d_operation(SM501State *s) color = cpu_to_le16(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)) { - /* 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); + 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: @@ -1878,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 */ @@ -1922,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), @@ -2036,10 +2054,10 @@ static void sm501_realize_sysbus(DeviceState *dev, Error **errp) /* TODO : chain irq to IRL */ } -static Property sm501_sysbus_properties[] = { +static const Property sm501_sysbus_properties[] = { DEFINE_PROP_UINT32("vram-size", SM501SysBusState, vram_size, 0), - DEFINE_PROP_UINT8("x-pixman", SM501SysBusState, state.use_pixman, 7), - DEFINE_PROP_END_OF_LIST(), + /* this a debug option, prefer PROP_UINT over PROP_BIT for simplicity */ + DEFINE_PROP_UINT8("x-pixman", SM501SysBusState, state.use_pixman, DEFAULT_X_PIXMAN), }; static void sm501_reset_sysbus(DeviceState *dev) @@ -2052,14 +2070,14 @@ 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() } }; -static void sm501_sysbus_class_init(ObjectClass *klass, void *data) +static void sm501_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -2067,7 +2085,7 @@ 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; } @@ -2124,10 +2142,9 @@ static void sm501_realize_pci(PCIDevice *dev, Error **errp) &s->state.mmio_region); } -static Property sm501_pci_properties[] = { +static const Property sm501_pci_properties[] = { DEFINE_PROP_UINT32("vram-size", SM501PCIState, vram_size, 64 * MiB), - DEFINE_PROP_UINT8("x-pixman", SM501PCIState, state.use_pixman, 7), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_UINT8("x-pixman", SM501PCIState, state.use_pixman, DEFAULT_X_PIXMAN), }; static void sm501_reset_pci(DeviceState *dev) @@ -2142,7 +2159,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), @@ -2150,7 +2167,7 @@ static const VMStateDescription vmstate_sm501_pci = { } }; -static void sm501_pci_class_init(ObjectClass *klass, void *data) +static void sm501_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -2162,7 +2179,7 @@ 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; } @@ -2179,7 +2196,7 @@ static const TypeInfo sm501_pci_info = { .instance_size = sizeof(SM501PCIState), .class_init = sm501_pci_class_init, .instance_init = sm501_pci_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c index 32b32a3044e..87781438cd5 100644 --- a/hw/display/ssd0303.c +++ b/hw/display/ssd0303.c @@ -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), @@ -311,7 +311,7 @@ static void ssd0303_realize(DeviceState *dev, Error **errp) qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY); } -static void ssd0303_class_init(ObjectClass *klass, void *data) +static void ssd0303_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c index 09b1bbed0a4..af5ff4fecdc 100644 --- a/hw/display/ssd0323.c +++ b/hw/display/ssd0323.c @@ -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), @@ -361,7 +361,7 @@ static void ssd0323_realize(SSIPeripheral *d, Error **errp) qdev_init_gpio_in(dev, ssd0323_cd, 1); } -static void ssd0323_class_init(ObjectClass *klass, void *data) +static void ssd0323_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c deleted file mode 100644 index c7beba453b0..00000000000 --- 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 1b27b64f6d1..4853c5e1424 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), @@ -729,7 +729,6 @@ static uint64_t tcx_dummy_readl(void *opaque, hwaddr addr, static void tcx_dummy_writel(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - return; } static const MemoryRegionOps tcx_dummy_ops = { @@ -879,20 +878,19 @@ static void tcx_realizefn(DeviceState *dev, Error **errp) qemu_console_resize(s->con, s->width, s->height); } -static Property tcx_properties[] = { +static const Property tcx_properties[] = { DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1), DEFINE_PROP_UINT16("width", TCXState, width, -1), DEFINE_PROP_UINT16("height", TCXState, height, -1), DEFINE_PROP_UINT16("depth", TCXState, depth, -1), - DEFINE_PROP_END_OF_LIST(), }; -static void tcx_class_init(ObjectClass *klass, void *data) +static void tcx_class_init(ObjectClass *klass, const 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 2336a0ca157..52786e6e184 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -53,6 +53,9 @@ 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_io_write_vga(int qid, const char *mode, uint32_t addr, uint32_t val) "%d %s addr=%u val=%u" @@ -177,3 +180,47 @@ 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" + +# apple-gfx.m +apple_gfx_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 +apple_gfx_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 +apple_gfx_create_task(uint32_t vm_size, void *va) "vm_size=0x%x base_addr=%p" +apple_gfx_destroy_task(void *task, unsigned int num_mapped_regions) "task=%p, task->mapped_regions->len=%u" +apple_gfx_map_memory(void *task, uint32_t range_count, uint64_t virtual_offset, uint32_t read_only) "task=%p range_count=0x%x virtual_offset=0x%"PRIx64" read_only=%d" +apple_gfx_map_memory_range(uint32_t i, uint64_t phys_addr, uint64_t phys_len) "[%d] phys_addr=0x%"PRIx64" phys_len=0x%"PRIx64 +apple_gfx_remap(uint64_t retval, void *source_ptr, uint64_t target) "retval=%"PRId64" source=%p target=0x%"PRIx64 +apple_gfx_unmap_memory(void *task, uint64_t virtual_offset, uint64_t length) "task=%p virtual_offset=0x%"PRIx64" length=0x%"PRIx64 +apple_gfx_read_memory(uint64_t phys_address, uint64_t length, void *dst) "phys_addr=0x%"PRIx64" length=0x%"PRIx64" dest=%p" +apple_gfx_raise_irq(uint32_t vector) "vector=0x%x" +apple_gfx_new_frame(void) "" +apple_gfx_mode_change(uint64_t x, uint64_t y) "x=%"PRId64" y=%"PRId64 +apple_gfx_cursor_set(uint32_t bpp, uint64_t width, uint64_t height) "bpp=%d width=%"PRId64" height=0x%"PRId64 +apple_gfx_cursor_show(uint32_t show) "show=%d" +apple_gfx_cursor_move(void) "" +apple_gfx_common_init(const char *device_name, size_t mmio_size) "device: %s; MMIO size: %zu bytes" +apple_gfx_common_realize_modes_property(uint32_t num_modes) "using %u modes supplied by 'display-modes' device property" +apple_gfx_display_mode(uint32_t mode_idx, uint16_t width_px, uint16_t height_px) "mode %2"PRIu32": %4"PRIu16"x%4"PRIu16 + +# apple-gfx-mmio.m +apple_gfx_mmio_iosfc_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 +apple_gfx_mmio_iosfc_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 +apple_gfx_iosfc_map_memory(uint64_t phys, uint64_t len, uint32_t ro, void *va, void *e, void *f, void* va_result) "phys=0x%"PRIx64" len=0x%"PRIx64" ro=%d va=%p e=%p f=%p -> *va=%p" +apple_gfx_iosfc_map_memory_new_region(size_t i, void *region, uint64_t start, uint64_t end) "index=%zu, region=%p, 0x%"PRIx64"-0x%"PRIx64 +apple_gfx_iosfc_unmap_memory(void *a, void *b, void *c, void *d, void *e, void *f) "a=%p b=%p c=%p d=%p e=%p f=%p" +apple_gfx_iosfc_unmap_memory_region(void* mem, void *region) "unmapping @ %p from memory region %p" +apple_gfx_iosfc_raise_irq(uint32_t vector) "vector=0x%x" + diff --git a/hw/display/vga-helpers.h b/hw/display/vga-helpers.h index 10e9cfd40a0..2029b61791b 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 2a5437d8037..3618913b3b7 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 * @@ -88,17 +88,16 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp) rom_add_vga(VGABIOS_FILENAME); } -static Property vga_isa_properties[] = { +static const Property vga_isa_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", ISAVGAState, state.vram_size_mb, 8), - DEFINE_PROP_END_OF_LIST(), }; -static void vga_isa_class_initfn(ObjectClass *klass, void *data) +static void vga_isa_class_initfn(ObjectClass *klass, const 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 cd2c46776dc..33263856b76 100644 --- a/hw/display/vga-mmio.c +++ b/hw/display/vga-mmio.c @@ -111,18 +111,17 @@ static void vga_mmio_realizefn(DeviceState *dev, Error **errp) s->vga.con = graphic_console_init(dev, 0, s->vga.hw_ops, &s->vga); } -static Property vga_mmio_properties[] = { +static const Property vga_mmio_properties[] = { DEFINE_PROP_UINT8("it_shift", VGAMmioState, it_shift, 0), DEFINE_PROP_UINT32("vgamem_mb", VGAMmioState, vga.vram_size_mb, 8), - DEFINE_PROP_END_OF_LIST(), }; -static void vga_mmio_class_initfn(ObjectClass *klass, void *data) +static void vga_mmio_class_initfn(ObjectClass *klass, const 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 b351b8f299d..b81f7fd2d0f 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 * @@ -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() @@ -330,7 +330,7 @@ static void pci_secondary_vga_reset(DeviceState *dev) vga_common_reset(&d->vga); } -static Property vga_pci_properties[] = { +static const Property vga_pci_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", PCIVGAState, vga.vram_size_mb, 16), DEFINE_PROP_BIT("mmio", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_MMIO, true), DEFINE_PROP_BIT("qemu-extended-regs", @@ -339,20 +339,18 @@ static Property vga_pci_properties[] = { PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_EDID, true), DEFINE_EDID_PROPERTIES(PCIVGAState, edid_info), DEFINE_PROP_BOOL("global-vmstate", PCIVGAState, vga.global_vmstate, false), - DEFINE_PROP_END_OF_LIST(), }; -static Property secondary_pci_properties[] = { +static const Property secondary_pci_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", PCIVGAState, vga.vram_size_mb, 16), DEFINE_PROP_BIT("qemu-extended-regs", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_QEXT, true), DEFINE_PROP_BIT("edid", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_EDID, true), DEFINE_EDID_PROPERTIES(PCIVGAState, edid_info), - DEFINE_PROP_END_OF_LIST(), }; -static void vga_pci_class_init(ObjectClass *klass, void *data) +static void vga_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -371,14 +369,14 @@ static const TypeInfo vga_pci_type_info = { .instance_size = sizeof(PCIVGAState), .abstract = true, .class_init = vga_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { TYPE_ACPI_DEV_AML_IF }, { }, }, }; -static void vga_class_init(ObjectClass *klass, void *data) +static void vga_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -394,7 +392,7 @@ static void vga_class_init(ObjectClass *klass, void *data) vga_get_big_endian_fb, vga_set_big_endian_fb); } -static void secondary_class_init(ObjectClass *klass, void *data) +static void secondary_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -403,7 +401,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 37557c3442a..20475ebbd31 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -24,9 +24,9 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "qapi/error.h" -#include "hw/core/cpu.h" +#include "exec/tswap.h" #include "hw/display/vga.h" #include "hw/i386/x86.h" #include "hw/pci/pci.h" @@ -47,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) * @@ -90,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]; @@ -836,45 +815,62 @@ 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]; + plane = s->gr[VGA_GFX_PLANE_READ]; + } - 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; - } + 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" HWADDR_FMT_plx "] = 0x%02x\n", addr, val); @@ -903,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" HWADDR_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 { + if ((sr(s, VGA_SEQ_MEMORY_MODE) & VGA_SR04_SEQ_MODE) == 0) { + mask &= (addr & 1) ? 0x0a : 0x05; } - } 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; + if (s->gr[VGA_GFX_MISC] & VGA_GR06_CHAIN_ODD_EVEN) { + addr &= ~1; + } + } + + /* 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: odd/even: [0x" HWADDR_FMT_plx "]\n", addr); + 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); } - } 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)); + return; + } - bit_mask = s->gr[VGA_GFX_BIT_MASK] & val; - val = mask16[s->gr[VGA_GFX_SR_VALUE]]; - break; - } + /* 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)); - /* 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; - } + bit_mask = s->gr[VGA_GFX_BIT_MASK] & val; + val = mask16[s->gr[VGA_GFX_SR_VALUE]]; + 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] */ - 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); + /* 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); + 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)); - } + 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" @@ -1073,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; @@ -1219,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) { @@ -1258,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) { @@ -1272,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; @@ -1282,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; @@ -1362,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; @@ -1475,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; @@ -1492,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); @@ -1537,15 +1521,86 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) } 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 (depth) { + default: + case 0: + full_update |= update_palette256(s); + v = VGA_DRAW_LINE8D2; + bits = 4; + break; + case 8: + full_update |= update_palette256(s); + v = VGA_DRAW_LINE8; + bits = 8; + break; + case 15: + v = s->big_endian_fb ? VGA_DRAW_LINE15_BE : VGA_DRAW_LINE15_LE; + bits = 16; + break; + case 16: + v = s->big_endian_fb ? VGA_DRAW_LINE16_BE : VGA_DRAW_LINE16_LE; + bits = 16; + break; + case 24: + v = s->big_endian_fb ? VGA_DRAW_LINE24_BE : VGA_DRAW_LINE24_LE; + bits = 24; + break; + case 32: + v = s->big_endian_fb ? VGA_DRAW_LINE32_BE : VGA_DRAW_LINE32_LE; + bits = 32; + 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 @@ -1554,40 +1609,43 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) */ format = qemu_default_pixman_format(depth, !byteswap); if (format) { - share_surface = dpy_gfx_check_format(s->con, format) - && !s->force_shadow && !force_shadow; + allocate_surface = !dpy_gfx_check_format(s->con, format) + || s->force_shadow || force_shadow; } else { - share_surface = false; + allocate_surface = true; } - if (s->line_offset != s->last_line_offset || + if (s->params.line_offset != s->last_line_offset || disp_width != s->last_width || height != s->last_height || s->last_depth != depth || s->last_byteswap != byteswap || - share_surface != is_buffer_shared(surface)) { + 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->line_offset; + 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->start_addr * 4) - && is_buffer_shared(surface)) { + 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 (share_surface) { + if (!allocate_surface) { surface = qemu_create_displaysurface_from(disp_width, - height, format, s->line_offset, - s->vram_ptr + (s->start_addr * 4)); + 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); @@ -1595,76 +1653,24 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) } } - if (shift_control == 0) { - full_update |= update_palette16(s); - if (sr(s, VGA_SEQ_CLOCK_MODE) & 8) { - 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) { - v = VGA_DRAW_LINE2D2; - } else { - v = VGA_DRAW_LINE2; - } - bits = 4; - } else { - switch(s->get_bpp(s)) { - default: - case 0: - full_update |= update_palette256(s); - v = VGA_DRAW_LINE8D2; - bits = 4; - break; - case 8: - full_update |= update_palette256(s); - v = VGA_DRAW_LINE8; - bits = 8; - break; - case 15: - v = s->big_endian_fb ? VGA_DRAW_LINE15_BE : VGA_DRAW_LINE15_LE; - bits = 16; - break; - case 16: - v = s->big_endian_fb ? VGA_DRAW_LINE16_BE : VGA_DRAW_LINE16_LE; - bits = 16; - break; - case 24: - v = s->big_endian_fb ? VGA_DRAW_LINE24_BE : VGA_DRAW_LINE24_LE; - bits = 24; - break; - case 32: - v = s->big_endian_fb ? VGA_DRAW_LINE32_BE : VGA_DRAW_LINE32_LE; - bits = 32; - break; - } - } 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); @@ -1701,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); } @@ -1717,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) { @@ -1748,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++) { @@ -1841,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; @@ -1854,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)); @@ -1965,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) { @@ -1981,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) { @@ -2106,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() } @@ -2117,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), @@ -2149,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 } @@ -2226,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) { @@ -2246,7 +2264,8 @@ bool vga_common_init(VGACommonState *s, Object *obj, Error **errp) * into a device attribute set by the machine/platform to remove * all target endian dependencies from this file. */ - s->default_endian_fb = target_words_bigendian(); + s->default_endian_fb = target_big_endian(); + s->big_endian_fb = s->default_endian_fb; vga_dirty_log_start(s); diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index 7cf0d11201a..747b5cc6cf8 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -25,8 +25,9 @@ #ifndef HW_VGA_INT_H #define HW_VGA_INT_H -#include "exec/ioport.h" -#include "exec/memory.h" +#include "ui/console.h" +#include "system/ioport.h" +#include "system/memory.h" #include "hw/display/bochs-vbe.h" #include "hw/acpi/acpi_aml_interface.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 7fdba34b9b1..40e673f164d 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 1150521d9db..9fc6bbcd2ce 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -18,6 +18,7 @@ #include "chardev/char-fe.h" #include "qapi/error.h" #include "migration/blocker.h" +#include "standard-headers/drm/drm_fourcc.h" typedef enum VhostUserGpuRequest { VHOST_USER_GPU_NONE = 0, @@ -249,6 +250,9 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) case VHOST_USER_GPU_DMABUF_SCANOUT: { VhostUserGpuDMABUFScanout *m = &msg->payload.dmabuf_scanout; int fd = qemu_chr_fe_get_msgfd(&g->vhost_chr); + uint32_t offset = 0; + uint32_t stride = m->fd_stride; + uint64_t modifier = DRM_FORMAT_MOD_INVALID; QemuDmaBuf *dmabuf; if (m->scanout_id >= g->parent_obj.conf.max_outputs) { @@ -261,30 +265,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; - dmabuf->modifier = m2->modifier; + modifier = m2->modifier; } + dmabuf = qemu_dmabuf_new(m->width, m->height, + &offset, &stride, 0, 0, + m->fd_width, m->fd_height, + m->fd_drm_fourcc, modifier, + &fd, 1, 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: { @@ -307,6 +315,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; @@ -334,6 +343,7 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) } break; } +#endif default: g_warning("unhandled message %d %d", msg->request, msg->size); } @@ -383,7 +393,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 || @@ -506,7 +516,7 @@ vhost_user_gpu_set_config(VirtIODevice *vdev, } } -static void +static int vhost_user_gpu_set_status(VirtIODevice *vdev, uint8_t val) { VhostUserGPU *g = VHOST_USER_GPU(vdev); @@ -515,18 +525,24 @@ vhost_user_gpu_set_status(VirtIODevice *vdev, uint8_t val) if (val & VIRTIO_CONFIG_S_DRIVER_OK && vdev->vm_running) { if (!vhost_user_gpu_do_set_socket(g, &err)) { error_report_err(err); - return; + return 0; } vhost_user_backend_start(g->vhost); } else { + int ret; + /* unblock any wait and stop processing */ if (g->vhost_gpu_fd != -1) { vhost_user_gpu_update_blocked(g, true); qemu_chr_fe_deinit(&g->vhost_chr, true); g->vhost_gpu_fd = -1; } - vhost_user_backend_stop(g->vhost); + ret = vhost_user_backend_stop(g->vhost); + if (ret < 0) { + return ret; + } } + return 0; } static bool @@ -624,6 +640,14 @@ vhost_user_gpu_device_realize(DeviceState *qdev, Error **errp) error_report("EDID requested but the backend doesn't support it."); g->parent_obj.conf.flags &= ~(1 << VIRTIO_GPU_FLAG_EDID_ENABLED); } + if (virtio_has_feature(g->vhost->dev.features, + VIRTIO_GPU_F_RESOURCE_UUID)) { + g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_RESOURCE_UUID_ENABLED; + } + if (virtio_has_feature(g->vhost->dev.features, + VIRTIO_GPU_F_RESOURCE_UUID)) { + g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_RESOURCE_UUID_ENABLED; + } if (!virtio_gpu_base_device_realize(qdev, NULL, NULL, errp)) { return; @@ -635,16 +659,15 @@ 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[] = { +static const Property vhost_user_gpu_properties[] = { VIRTIO_GPU_BASE_PROPERTIES(VhostUserGPU, parent_obj.conf), - DEFINE_PROP_END_OF_LIST(), }; static void -vhost_user_gpu_class_init(ObjectClass *klass, void *data) +vhost_user_gpu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/display/virtio-dmabuf.c b/hw/display/virtio-dmabuf.c new file mode 100644 index 00000000000..3dba4577ca7 --- /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 ca1fb7b16fc..9eb806b71f6 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -110,7 +110,6 @@ static void virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) /* send event to guest */ virtio_gpu_notify_event(g, VIRTIO_GPU_EVENT_DISPLAY); - return; } static void @@ -184,8 +183,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; } } @@ -223,7 +221,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)) { @@ -232,6 +231,12 @@ 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); + } + if (virtio_gpu_resource_uuid_enabled(g->conf)) { + features |= (1 << VIRTIO_GPU_F_RESOURCE_UUID); + } return features; } @@ -248,15 +253,16 @@ 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 -virtio_gpu_base_class_init(ObjectClass *klass, void *data) +virtio_gpu_base_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c index e06be60dfbf..c06a078fb36 100644 --- a/hw/display/virtio-gpu-gl.c +++ b/hw/display/virtio-gpu-gl.c @@ -16,7 +16,7 @@ #include "qemu/module.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-gpu.h" #include "hw/virtio/virtio-gpu-bswap.h" @@ -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,24 +134,55 @@ 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); } -static Property virtio_gpu_gl_properties[] = { +static const Property virtio_gpu_gl_properties[] = { DEFINE_PROP_BIT("stats", VirtIOGPU, parent_obj.conf.flags, VIRTIO_GPU_FLAG_STATS_ENABLED, false), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_BIT("venus", VirtIOGPU, parent_obj.conf.flags, + VIRTIO_GPU_FLAG_VENUS_ENABLED, false), }; -static void virtio_gpu_gl_class_init(ObjectClass *klass, void *data) +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, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); @@ -149,6 +195,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 +217,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 00000000000..5fdff37f2c1 --- /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 = (const 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 93f214ff581..c0d71b6254c 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -21,9 +21,8 @@ #include "hw/virtio/virtio-gpu-pci.h" #include "qom/object.h" -static Property virtio_gpu_pci_base_properties[] = { +static const Property virtio_gpu_pci_base_properties[] = { DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_gpu_pci_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -33,6 +32,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; @@ -44,7 +57,7 @@ static void virtio_gpu_pci_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp) } } -static void virtio_gpu_pci_base_class_init(ObjectClass *klass, void *data) +static void virtio_gpu_pci_base_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c new file mode 100644 index 00000000000..ed5ae52acbe --- /dev/null +++ b/hw/display/virtio-gpu-rutabaga.c @@ -0,0 +1,1142 @@ +/* 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 const 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), +}; + +static void virtio_gpu_rutabaga_class_init(ObjectClass *klass, const 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 d51184d6582..d804f321aa3 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -19,12 +19,13 @@ #include "hw/virtio/virtio-gpu.h" #include "hw/virtio/virtio-gpu-pixman.h" #include "trace.h" -#include "exec/ramblock.h" -#include "sysemu/hostmem.h" +#include "system/ramblock.h" +#include "system/hostmem.h" #include #include #include "qemu/memfd.h" #include "standard-headers/linux/udmabuf.h" +#include "standard-headers/drm/drm_fourcc.h" static void virtio_gpu_create_udmabuf(struct virtio_gpu_simple_resource *res) { @@ -162,7 +163,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); } @@ -175,23 +177,19 @@ static VGPUDMABuf struct virtio_gpu_rect *r) { VGPUDMABuf *dmabuf; + uint32_t offset = 0; if (res->dmabuf_fd < 0) { return NULL; } dmabuf = g_new0(VGPUDMABuf, 1); - dmabuf->buf.width = r->width; - dmabuf->buf.height = r->height; - dmabuf->buf.stride = fb->stride; - dmabuf->buf.x = r->x; - dmabuf->buf.y = r->y; - dmabuf->buf.backing_width = fb->width; - dmabuf->buf.backing_height = fb->height; - dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format); - dmabuf->buf.fd = res->dmabuf_fd; - dmabuf->buf.allow_fences = true; - dmabuf->buf.draw_submitted = false; + dmabuf->buf = qemu_dmabuf_new(r->width, r->height, + &offset, &fb->stride, + r->x, r->y, fb->width, fb->height, + qemu_pixman_to_drm_format(fb->format), + DRM_FORMAT_MOD_INVALID, &res->dmabuf_fd, + 1, true, false); dmabuf->scanout_id = scanout_id; QTAILQ_INSERT_HEAD(&g->dmabuf.bufs, dmabuf, next); @@ -206,6 +204,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) { @@ -216,11 +215,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.width, - new_primary->buf.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 8bb7a2c21fe..145a0b38795 100644 --- a/hw/display/virtio-gpu-virgl.c +++ b/hw/display/virtio-gpu-virgl.c @@ -17,11 +17,31 @@ #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 +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) @@ -30,16 +50,179 @@ virgl_get_egl_display(G_GNUC_UNUSED void *cookie) } #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; @@ -59,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; @@ -79,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); @@ -95,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, @@ -106,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, @@ -171,7 +418,7 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, struct virgl_renderer_resource_info info; void *d3d_tex2d = NULL; -#ifdef HAVE_VIRGL_D3D_INFO_EXT +#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); @@ -181,7 +428,7 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, memset(&info, 0, sizeof(info)); ret = virgl_renderer_resource_get_info(ss.resource_id, &info); #endif - if (ret == -1) { + if (ret) { qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n", __func__, ss.resource_id); @@ -375,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; - 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; + + 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 { - 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)); @@ -417,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(); @@ -461,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 */ @@ -483,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) { @@ -525,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); } } } @@ -574,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", @@ -588,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); } } @@ -626,6 +1095,7 @@ int virtio_gpu_virgl_init(VirtIOGPU *g) { int ret; uint32_t flags = 0; + VirtIOGPUGL *gl = VIRTIO_GPU_GL(g); #if VIRGL_RENDERER_CALLBACKS_VERSION >= 4 if (qemu_egl_display) { @@ -638,6 +1108,11 @@ int virtio_gpu_virgl_init(VirtIOGPU *g) 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) { @@ -645,23 +1120,55 @@ int virtio_gpu_virgl_init(VirtIOGPU *g) 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) +{ + g_array_append_val(capset_ids, capset_id); +} + +GArray *virtio_gpu_virgl_get_capsets(VirtIOGPU *g) { - uint32_t capset2_max_ver, capset2_max_size; + 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, - &capset2_max_ver, - &capset2_max_size); + &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 capset2_max_ver ? 2 : 1; + return capset_ids; } diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 93857ad523f..0a1a625b0ea 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -14,11 +14,12 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/iov.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "ui/console.h" +#include "ui/rect.h" #include "trace.h" -#include "sysemu/dma.h" -#include "sysemu/sysemu.h" +#include "system/dma.h" +#include "system/system.h" #include "hw/virtio/virtio.h" #include "migration/qemu-file-types.h" #include "hw/virtio/virtio-gpu.h" @@ -27,21 +28,18 @@ #include "hw/virtio/virtio-bus.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, @@ -112,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; @@ -242,16 +239,6 @@ static uint32_t calc_image_hostmem(pixman_format_code_t pformat, return height * stride; } -#ifdef WIN32 -static void -win32_pixman_image_destroy(pixman_image_t *image, void *data) -{ - HANDLE handle = data; - - qemu_win32_map_free(pixman_image_get_data(image), handle, &error_warn); -} -#endif - static void virtio_gpu_resource_create_2d(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { @@ -298,28 +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) { - void *bits = NULL; -#ifdef WIN32 - bits = qemu_win32_map_alloc(res->hostmem, &res->handle, &error_warn); - if (!bits) { + 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; } -#endif - res->image = pixman_image_create_bits( - pformat, - c2d.width, - c2d.height, - bits, c2d.height ? res->hostmem / c2d.height : 0); -#ifdef WIN32 - if (res->image) { - pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle); - } -#endif } -#ifdef WIN32 end: -#endif if (!res->image) { qemu_log_mask(LOG_GUEST_ERROR, "%s: resource creation failed %d %d %d\n", @@ -383,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; @@ -405,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; @@ -441,7 +421,11 @@ 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, @@ -507,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; @@ -569,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) @@ -604,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; @@ -624,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, @@ -652,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; @@ -660,11 +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); + virtio_gpu_update_scanout(g, scanout_id, res, fb, r); } else { *error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return false; } - return; + return true; } data = res->blob; @@ -691,20 +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; - } -#ifdef WIN32 - qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, fb->offset); -#endif + 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, @@ -748,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); @@ -780,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; } @@ -904,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; @@ -1061,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++; @@ -1073,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); @@ -1093,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); } } } @@ -1132,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) @@ -1173,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), @@ -1187,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() }, }; @@ -1194,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), @@ -1217,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); @@ -1234,14 +1249,41 @@ static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size, return vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL); } +static bool virtio_gpu_load_restore_mapping(VirtIOGPU *g, + struct virtio_gpu_simple_resource *res) +{ + int i; + + for (i = 0; i < res->iov_cnt; i++) { + hwaddr len = res->iov[i].iov_len; + res->iov[i].iov_base = + dma_memory_map(VIRTIO_DEVICE(g)->dma_as, res->addrs[i], &len, + DMA_DIRECTION_TO_DEVICE, MEMTXATTRS_UNSPECIFIED); + + if (!res->iov[i].iov_base || len != res->iov[i].iov_len) { + /* Clean up the half-a-mapping we just created... */ + if (res->iov[i].iov_base) { + dma_memory_unmap(VIRTIO_DEVICE(g)->dma_as, res->iov[i].iov_base, + len, DMA_DIRECTION_TO_DEVICE, 0); + } + /* ...and the mappings for previous loop iterations */ + res->iov_cnt = i; + virtio_gpu_cleanup_mapping(g, res); + return false; + } + } + + QTAILQ_INSERT_HEAD(&g->reslist, res, next); + g->hostmem += res->hostmem; + return true; +} + static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, const VMStateField *field) { VirtIOGPU *g = opaque; struct virtio_gpu_simple_resource *res; - struct virtio_gpu_scanout *scanout; uint32_t resource_id, pformat; - void *bits = NULL; int i; g->hostmem = 0; @@ -1268,24 +1310,17 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, } res->hostmem = calc_image_hostmem(pformat, res->width, res->height); -#ifdef WIN32 - bits = qemu_win32_map_alloc(res->hostmem, &res->handle, &error_warn); - if (!bits) { - g_free(res); - return -EINVAL; - } -#endif - res->image = pixman_image_create_bits( - pformat, - res->width, res->height, - bits, res->height ? res->hostmem / res->height : 0); - if (!res->image) { + 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; } -#ifdef WIN32 - pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle); -#endif res->addrs = g_new(uint64_t, res->iov_cnt); res->iov = g_new(struct iovec, res->iov_cnt); @@ -1298,59 +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++) { - /* FIXME: should take scanout.r.{x,y} into account */ 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); } -#ifdef WIN32 - qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0); -#endif - dpy_gfx_replace_surface(scanout->con, scanout->ds); dpy_gfx_update_full(scanout->con); if (scanout->cursor.resource_id) { update_cursor(g, &scanout->cursor); @@ -1367,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, @@ -1387,10 +1514,8 @@ 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_guarded(virtio_gpu_ctrl_bh, g, - &qdev->mem_reentrancy_guard); - g->cursor_bh = qemu_bh_new_guarded(virtio_gpu_cursor_bh, g, - &qdev->mem_reentrancy_guard); + 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); @@ -1412,11 +1537,24 @@ static void virtio_gpu_device_unrealize(DeviceState *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) { - virtio_gpu_resource_destroy(g, res); + 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++) { @@ -1436,10 +1574,10 @@ void virtio_gpu_reset(VirtIODevice *vdev) g->reset_finished = false; qemu_bh_schedule(g->reset_bh); while (!g->reset_finished) { - qemu_cond_wait_iothread(&g->reset_cond); + qemu_cond_wait_bql(&g->reset_cond); } } else { - virtio_gpu_reset_bh(g); + aio_bh_call(g->reset_bh); } while (!QTAILQ_EMPTY(&g->cmdq)) { @@ -1478,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 @@ -1490,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", @@ -1503,18 +1667,24 @@ 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[] = { +static const Property virtio_gpu_properties[] = { VIRTIO_GPU_BASE_PROPERTIES(VirtIOGPU, parent_obj.conf), DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf_max_hostmem, 256 * MiB), DEFINE_PROP_BIT("blob", VirtIOGPU, parent_obj.conf.flags, VIRTIO_GPU_FLAG_BLOB_ENABLED, false), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_SIZE("hostmem", VirtIOGPU, parent_obj.conf.hostmem, 0), + DEFINE_PROP_UINT8("x-scanout-vmstate-version", VirtIOGPU, scanout_vmstate_version, 2), }; -static void virtio_gpu_class_init(ObjectClass *klass, void *data) +static void virtio_gpu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); @@ -1524,6 +1694,7 @@ 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; diff --git a/hw/display/virtio-vga-rutabaga.c b/hw/display/virtio-vga-rutabaga.c new file mode 100644 index 00000000000..a7bef6da248 --- /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 e6fb0aa876c..40e60f70fcd 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,14 +180,14 @@ static void virtio_vga_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp) } } -static void virtio_vga_base_reset_hold(Object *obj) +static void virtio_vga_base_reset_hold(Object *obj, ResetType type) { VirtIOVGABaseClass *klass = VIRTIO_VGA_BASE_GET_CLASS(obj); VirtIOVGABase *vvga = VIRTIO_VGA_BASE(obj); /* reset virtio-gpu */ if (klass->parent_phases.hold) { - klass->parent_phases.hold(obj); + klass->parent_phases.hold(obj, type); } /* reset vga */ @@ -194,12 +209,11 @@ static void virtio_vga_set_big_endian_fb(Object *obj, bool value, Error **errp) d->vga.big_endian_fb = value; } -static Property virtio_vga_base_properties[] = { +static const Property virtio_vga_base_properties[] = { DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_vga_base_class_init(ObjectClass *klass, void *data) +static void virtio_vga_base_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 09591fbd39f..bc1a8ed4665 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -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)) { @@ -618,7 +618,7 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s) uint32_t cmd, colour; int args, len, maxloop = 1024; int x, y, dx, dy, width, height; - struct vmsvga_cursor_definition_s cursor; + QEMU_UNINITIALIZED struct vmsvga_cursor_definition_s cursor; uint32_t cmd_start; len = vmsvga_fifo_length(s); @@ -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; } @@ -1334,15 +1332,14 @@ static void pci_vmsvga_realize(PCIDevice *dev, Error **errp) &s->chip.fifo_ram); } -static Property vga_vmware_properties[] = { +static const Property vga_vmware_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", struct pci_vmsvga_state_s, chip.vga.vram_size_mb, 16), DEFINE_PROP_BOOL("global-vmstate", struct pci_vmsvga_state_s, chip.vga.global_vmstate, false), - DEFINE_PROP_END_OF_LIST(), }; -static void vmsvga_class_init(ObjectClass *klass, void *data) +static void vmsvga_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1354,7 +1351,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; @@ -1366,7 +1363,7 @@ static const TypeInfo vmsvga_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(struct pci_vmsvga_state_s), .class_init = vmsvga_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index 0074a9b6f81..22822fecea3 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -29,6 +29,7 @@ #include "ui/input.h" #include "ui/console.h" +#include "system/system.h" #include "hw/xen/xen-legacy-backend.h" #include "hw/xen/interface/io/fbif.h" @@ -321,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, @@ -637,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) { @@ -755,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; } @@ -972,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, @@ -981,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, @@ -995,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 43c7dd8e9cd..7c980ee6423 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, @@ -1302,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); @@ -1320,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", @@ -1385,13 +1387,18 @@ static void xlnx_dp_reset(DeviceState *dev) xlnx_dp_update_irq(s); } -static void xlnx_dp_class_init(ObjectClass *oc, void *data) +static const Property xlnx_dp_device_properties[] = { + DEFINE_AUDIO_PROPERTIES(XlnxDPState, aud_card), +}; + +static void xlnx_dp_class_init(ObjectClass *oc, const 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 5e9306110dc..a2771ddcb52 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), @@ -385,12 +385,12 @@ static void bcm2835_dma_realize(DeviceState *dev, Error **errp) bcm2835_dma_reset(dev); } -static void bcm2835_dma_class_init(ObjectClass *klass, void *data) +static void bcm2835_dma_class_init(ObjectClass *klass, const 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 9c0003de51d..00000000000 --- a/hw/dma/etraxfs_dma.c +++ /dev/null @@ -1,781 +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 "exec/memory.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=" HWADDR_FMT_plx "\n", __func__, c, addr)); - cpu_physical_memory_read(addr, &ctrl->channels[c].current_d, - sizeof(ctrl->channels[c].current_d)); - - D(dump_d(c, &ctrl->channels[c].current_d)); - ctrl->channels[c].regs[RW_DATA] = addr; -} - -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=" HWADDR_FMT_plx "\n", __func__, c, addr)); - D(dump_d(c, &ctrl->channels[c].current_d)); - cpu_physical_memory_write(addr, &ctrl->channels[c].current_c, - sizeof(ctrl->channels[c].current_c)); -} - -static void channel_store_d(struct fs_dma_ctrl *ctrl, int c) -{ - hwaddr addr = channel_reg(ctrl, c, RW_SAVED_DATA); - - /* Encode and store. FIXME: handle endianness. */ - D(printf("%s ch=%d addr=" HWADDR_FMT_plx "\n", __func__, c, addr)); - cpu_physical_memory_write(addr, &ctrl->channels[c].current_d, - sizeof(ctrl->channels[c].current_d)); -} - -static inline void channel_stop(struct fs_dma_ctrl *ctrl, int c) -{ - /* FIXME: */ -} - -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=" HWADDR_FMT_plx "\n", addr); - return 0; -} - -static uint64_t -dma_read(void *opaque, hwaddr addr, unsigned int size) -{ - struct fs_dma_ctrl *ctrl = opaque; - int c; - uint32_t r = 0; - - 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=" HWADDR_FMT_plx "\n", - __func__, c, addr)); - break; - } - return r; -} - -static void -dma_winvalid (void *opaque, hwaddr addr, uint32_t value) -{ - hw_error("Unsupported short waccess. reg=" HWADDR_FMT_plx "\n", addr); -} - -static void -dma_update_state(struct fs_dma_ctrl *ctrl, int c) -{ - if (ctrl->channels[c].regs[RW_CFG] & 2) - ctrl->channels[c].state = STOPPED; - if (!(ctrl->channels[c].regs[RW_CFG] & 1)) - ctrl->channels[c].state = RST; -} - -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 " HWADDR_FMT_plx "\n", - __func__, c, addr)); - break; - } -} - -static const MemoryRegionOps dma_ops = { - .read = dma_read, - .write = dma_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 4 - } -}; - -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 63734c22c9d..e226eda6d15 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() }, @@ -129,7 +129,7 @@ static void i82374_realize(DeviceState *dev, Error **errp) 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"); @@ -139,18 +139,19 @@ static void i82374_realize(DeviceState *dev, Error **errp) memset(s->commands, 0, sizeof(s->commands)); } -static Property i82374_properties[] = { +static const Property i82374_properties[] = { DEFINE_PROP_UINT32("iobase", I82374State, iobase, 0x400), - DEFINE_PROP_END_OF_LIST() }; -static void i82374_class_init(ObjectClass *klass, void *data) +static void i82374_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = i82374_realize; dc->vmsd = &vmstate_i82374; device_class_set_props(dc, i82374_properties); + dc->desc = "Intel 82374 DMA controller"; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static const TypeInfo i82374_info = { diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c index de5f696919c..2463952ada2 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), @@ -585,21 +585,20 @@ static void i8257_realize(DeviceState *dev, Error **errp) d->dma_bh = qemu_bh_new(i8257_dma_run, d); } -static Property i8257_properties[] = { +static const Property i8257_properties[] = { DEFINE_PROP_INT32("base", I8257State, base, 0x00), DEFINE_PROP_INT32("page-base", I8257State, page_base, 0x80), DEFINE_PROP_INT32("pageh-base", I8257State, pageh_base, 0x480), DEFINE_PROP_INT32("dshift", I8257State, dshift, 0), - DEFINE_PROP_END_OF_LIST() }; -static void i8257_class_init(ObjectClass *klass, void *data) +static void i8257_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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); @@ -619,7 +618,7 @@ static const TypeInfo i8257_info = { .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(I8257State), .class_init = i8257_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ISADMA }, { } } @@ -632,12 +631,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 +646,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 a96c1be2c8d..cc7810beb84 100644 --- a/hw/dma/meson.build +++ b/hw/dma/meson.build @@ -5,12 +5,10 @@ system_ss.add(when: 'CONFIG_I82374', if_true: files('i82374.c')) system_ss.add(when: 'CONFIG_I8257', if_true: files('i8257.c')) system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('xilinx_axidma.c')) system_ss.add(when: 'CONFIG_ZYNQ_DEVCFG', if_true: files('xlnx-zynq-devcfg.c')) -system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_dma.c')) system_ss.add(when: 'CONFIG_STP2000', if_true: files('sparc32_dma.c')) system_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx_dpdma.c')) system_ss.add(when: 'CONFIG_XLNX_ZDMA', if_true: files('xlnx-zdma.c')) system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dma.c', 'soc_dma.c')) -system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_dma.c')) system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c')) system_ss.add(when: 'CONFIG_SIFIVE_PDMA', if_true: files('sifive_pdma.c')) system_ss.add(when: 'CONFIG_XLNX_CSU_DMA', if_true: files('xlnx_csu_dma.c')) diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index c6e35ba4b80..101f91f4a33 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -131,9 +131,9 @@ struct omap_dma_s { #define LAST_FRAME_INTR (1 << 4) #define END_BLOCK_INTR (1 << 5) #define SYNC (1 << 6) -#define END_PKT_INTR (1 << 7) -#define TRANS_ERR_INTR (1 << 8) -#define MISALIGN_INTR (1 << 11) +#define END_PKT_INTR (1 << 7) +#define TRANS_ERR_INTR (1 << 8) +#define MISALIGN_INTR (1 << 11) static inline void omap_dma_interrupts_update(struct omap_dma_s *s) { @@ -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; @@ -526,12 +526,12 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) /* Check all the conditions that terminate the transfer starting * with those that can occur the soonest. */ -#define INTR_CHECK(cond, id, nelements) \ - if (cond) { \ - elements[id] = nelements; \ - if (elements[id] < min_elems) \ - min_elems = elements[id]; \ - } else \ +#define INTR_CHECK(cond, id, nelements) \ + if (cond) { \ + elements[id] = nelements; \ + if (elements[id] < min_elems) \ + min_elems = elements[id]; \ + } else \ elements[id] = INT_MAX; /* Elements */ @@ -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; @@ -747,7 +740,7 @@ static int omap_dma_ch_reg_read(struct omap_dma_s *s, struct omap_dma_channel_s *ch, int reg, uint16_t *value) { switch (reg) { - case 0x00: /* SYS_DMA_CSDP_CH0 */ + case 0x00: /* SYS_DMA_CSDP_CH0 */ *value = (ch->burst[1] << 14) | (ch->pack[1] << 13) | (ch->port[1] << 9) | @@ -757,9 +750,9 @@ static int omap_dma_ch_reg_read(struct omap_dma_s *s, (ch->data_type >> 1); break; - case 0x02: /* SYS_DMA_CCR_CH0 */ + case 0x02: /* SYS_DMA_CCR_CH0 */ if (s->model <= omap_dma_3_1) - *value = 0 << 10; /* FIFO_FLUSH reads as 0 */ + *value = 0 << 10; /* FIFO_FLUSH reads as 0 */ else *value = ch->omap_3_1_compatible_disable << 10; *value |= (ch->mode[1] << 14) | @@ -772,11 +765,11 @@ static int omap_dma_ch_reg_read(struct omap_dma_s *s, (ch->fs << 5) | ch->sync; break; - case 0x04: /* SYS_DMA_CICR_CH0 */ + case 0x04: /* SYS_DMA_CICR_CH0 */ *value = ch->interrupts; break; - case 0x06: /* SYS_DMA_CSR_CH0 */ + case 0x06: /* SYS_DMA_CSR_CH0 */ *value = ch->status; ch->status &= SYNC; if (!ch->omap_3_1_compatible_disable && ch->sibling) { @@ -786,77 +779,77 @@ static int omap_dma_ch_reg_read(struct omap_dma_s *s, qemu_irq_lower(ch->irq); break; - case 0x08: /* SYS_DMA_CSSA_L_CH0 */ + case 0x08: /* SYS_DMA_CSSA_L_CH0 */ *value = ch->addr[0] & 0x0000ffff; break; - case 0x0a: /* SYS_DMA_CSSA_U_CH0 */ + case 0x0a: /* SYS_DMA_CSSA_U_CH0 */ *value = ch->addr[0] >> 16; break; - case 0x0c: /* SYS_DMA_CDSA_L_CH0 */ + case 0x0c: /* SYS_DMA_CDSA_L_CH0 */ *value = ch->addr[1] & 0x0000ffff; break; - case 0x0e: /* SYS_DMA_CDSA_U_CH0 */ + case 0x0e: /* SYS_DMA_CDSA_U_CH0 */ *value = ch->addr[1] >> 16; break; - case 0x10: /* SYS_DMA_CEN_CH0 */ + case 0x10: /* SYS_DMA_CEN_CH0 */ *value = ch->elements; break; - case 0x12: /* SYS_DMA_CFN_CH0 */ + case 0x12: /* SYS_DMA_CFN_CH0 */ *value = ch->frames; break; - case 0x14: /* SYS_DMA_CFI_CH0 */ + case 0x14: /* SYS_DMA_CFI_CH0 */ *value = ch->frame_index[0]; break; - case 0x16: /* SYS_DMA_CEI_CH0 */ + case 0x16: /* SYS_DMA_CEI_CH0 */ *value = ch->element_index[0]; break; - case 0x18: /* SYS_DMA_CPC_CH0 or DMA_CSAC */ + case 0x18: /* SYS_DMA_CPC_CH0 or DMA_CSAC */ if (ch->omap_3_1_compatible_disable) - *value = ch->active_set.src & 0xffff; /* CSAC */ + *value = ch->active_set.src & 0xffff; /* CSAC */ else *value = ch->cpc; break; - case 0x1a: /* DMA_CDAC */ - *value = ch->active_set.dest & 0xffff; /* CDAC */ + case 0x1a: /* DMA_CDAC */ + *value = ch->active_set.dest & 0xffff; /* CDAC */ break; - case 0x1c: /* DMA_CDEI */ + case 0x1c: /* DMA_CDEI */ *value = ch->element_index[1]; break; - case 0x1e: /* DMA_CDFI */ + case 0x1e: /* DMA_CDFI */ *value = ch->frame_index[1]; break; - case 0x20: /* DMA_COLOR_L */ + case 0x20: /* DMA_COLOR_L */ *value = ch->color & 0xffff; break; - case 0x22: /* DMA_COLOR_U */ + case 0x22: /* DMA_COLOR_U */ *value = ch->color >> 16; break; - case 0x24: /* DMA_CCR2 */ + case 0x24: /* DMA_CCR2 */ *value = (ch->bs << 2) | (ch->transparent_copy << 1) | ch->constant_fill; break; - case 0x28: /* DMA_CLNK_CTRL */ + case 0x28: /* DMA_CLNK_CTRL */ *value = (ch->link_enabled << 15) | (ch->link_next_ch & 0xf); break; - case 0x2a: /* DMA_LCH_CTRL */ + case 0x2a: /* DMA_LCH_CTRL */ *value = (ch->interleave_disabled << 15) | ch->type; break; @@ -871,7 +864,7 @@ static int omap_dma_ch_reg_write(struct omap_dma_s *s, struct omap_dma_channel_s *ch, int reg, uint16_t value) { switch (reg) { - case 0x00: /* SYS_DMA_CSDP_CH0 */ + case 0x00: /* SYS_DMA_CSDP_CH0 */ ch->burst[1] = (value & 0xc000) >> 14; ch->pack[1] = (value & 0x2000) >> 13; ch->port[1] = (enum omap_dma_port) ((value & 0x1e00) >> 9); @@ -894,7 +887,7 @@ static int omap_dma_ch_reg_write(struct omap_dma_s *s, } break; - case 0x02: /* SYS_DMA_CCR_CH0 */ + case 0x02: /* SYS_DMA_CCR_CH0 */ ch->mode[1] = (omap_dma_addressing_t) ((value & 0xc000) >> 14); ch->mode[0] = (omap_dma_addressing_t) ((value & 0x3000) >> 12); ch->end_prog = (value & 0x0800) >> 11; @@ -916,88 +909,88 @@ static int omap_dma_ch_reg_write(struct omap_dma_s *s, break; - case 0x04: /* SYS_DMA_CICR_CH0 */ + case 0x04: /* SYS_DMA_CICR_CH0 */ ch->interrupts = value & 0x3f; break; - case 0x06: /* SYS_DMA_CSR_CH0 */ + case 0x06: /* SYS_DMA_CSR_CH0 */ OMAP_RO_REG((hwaddr) reg); break; - case 0x08: /* SYS_DMA_CSSA_L_CH0 */ + case 0x08: /* SYS_DMA_CSSA_L_CH0 */ ch->addr[0] &= 0xffff0000; ch->addr[0] |= value; break; - case 0x0a: /* SYS_DMA_CSSA_U_CH0 */ + case 0x0a: /* SYS_DMA_CSSA_U_CH0 */ ch->addr[0] &= 0x0000ffff; ch->addr[0] |= (uint32_t) value << 16; break; - case 0x0c: /* SYS_DMA_CDSA_L_CH0 */ + case 0x0c: /* SYS_DMA_CDSA_L_CH0 */ ch->addr[1] &= 0xffff0000; ch->addr[1] |= value; break; - case 0x0e: /* SYS_DMA_CDSA_U_CH0 */ + case 0x0e: /* SYS_DMA_CDSA_U_CH0 */ ch->addr[1] &= 0x0000ffff; ch->addr[1] |= (uint32_t) value << 16; break; - case 0x10: /* SYS_DMA_CEN_CH0 */ + case 0x10: /* SYS_DMA_CEN_CH0 */ ch->elements = value; break; - case 0x12: /* SYS_DMA_CFN_CH0 */ + case 0x12: /* SYS_DMA_CFN_CH0 */ ch->frames = value; break; - case 0x14: /* SYS_DMA_CFI_CH0 */ + case 0x14: /* SYS_DMA_CFI_CH0 */ ch->frame_index[0] = (int16_t) value; break; - case 0x16: /* SYS_DMA_CEI_CH0 */ + case 0x16: /* SYS_DMA_CEI_CH0 */ ch->element_index[0] = (int16_t) value; break; - case 0x18: /* SYS_DMA_CPC_CH0 or DMA_CSAC */ + case 0x18: /* SYS_DMA_CPC_CH0 or DMA_CSAC */ OMAP_RO_REG((hwaddr) reg); break; - case 0x1c: /* DMA_CDEI */ + case 0x1c: /* DMA_CDEI */ ch->element_index[1] = (int16_t) value; break; - case 0x1e: /* DMA_CDFI */ + case 0x1e: /* DMA_CDFI */ ch->frame_index[1] = (int16_t) value; break; - case 0x20: /* DMA_COLOR_L */ + case 0x20: /* DMA_COLOR_L */ ch->color &= 0xffff0000; ch->color |= value; break; - case 0x22: /* DMA_COLOR_U */ + case 0x22: /* DMA_COLOR_U */ ch->color &= 0xffff; ch->color |= (uint32_t)value << 16; break; - case 0x24: /* DMA_CCR2 */ + case 0x24: /* DMA_CCR2 */ ch->bs = (value >> 2) & 0x1; ch->transparent_copy = (value >> 1) & 0x1; ch->constant_fill = value & 0x1; break; - case 0x28: /* DMA_CLNK_CTRL */ + case 0x28: /* DMA_CLNK_CTRL */ ch->link_enabled = (value >> 15) & 0x1; - if (value & (1 << 14)) { /* Stop_Lnk */ + if (value & (1 << 14)) { /* Stop_Lnk */ ch->link_enabled = 0; omap_dma_disable_channel(s, ch); } ch->link_next_ch = value & 0x1f; break; - case 0x2a: /* DMA_LCH_CTRL */ + case 0x2a: /* DMA_LCH_CTRL */ ch->interleave_disabled = (value >> 15) & 0x1; ch->type = value & 0xf; break; @@ -1012,7 +1005,7 @@ static int omap_dma_3_2_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, uint16_t value) { switch (offset) { - case 0xbc0: /* DMA_LCD_CSDP */ + case 0xbc0: /* DMA_LCD_CSDP */ s->brust_f2 = (value >> 14) & 0x3; s->pack_f2 = (value >> 13) & 0x1; s->data_type_f2 = (1 << ((value >> 11) & 0x3)); @@ -1021,7 +1014,7 @@ static int omap_dma_3_2_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, s->data_type_f1 = (1 << ((value >> 0) & 0x3)); break; - case 0xbc2: /* DMA_LCD_CCR */ + case 0xbc2: /* DMA_LCD_CCR */ s->mode_f2 = (value >> 14) & 0x3; s->mode_f1 = (value >> 12) & 0x3; s->end_prog = (value >> 11) & 0x1; @@ -1033,7 +1026,7 @@ static int omap_dma_3_2_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, s->bs = (value >> 4) & 0x1; break; - case 0xbc4: /* DMA_LCD_CTRL */ + case 0xbc4: /* DMA_LCD_CTRL */ s->dst = (value >> 8) & 0x1; s->src = ((value >> 6) & 0x3) << 1; s->condition = 0; @@ -1042,91 +1035,91 @@ static int omap_dma_3_2_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, s->dual = value & 1; break; - case 0xbc8: /* TOP_B1_L */ + case 0xbc8: /* TOP_B1_L */ s->src_f1_top &= 0xffff0000; s->src_f1_top |= 0x0000ffff & value; break; - case 0xbca: /* TOP_B1_U */ + case 0xbca: /* TOP_B1_U */ s->src_f1_top &= 0x0000ffff; s->src_f1_top |= (uint32_t)value << 16; break; - case 0xbcc: /* BOT_B1_L */ + case 0xbcc: /* BOT_B1_L */ s->src_f1_bottom &= 0xffff0000; s->src_f1_bottom |= 0x0000ffff & value; break; - case 0xbce: /* BOT_B1_U */ + case 0xbce: /* BOT_B1_U */ s->src_f1_bottom &= 0x0000ffff; s->src_f1_bottom |= (uint32_t) value << 16; break; - case 0xbd0: /* TOP_B2_L */ + case 0xbd0: /* TOP_B2_L */ s->src_f2_top &= 0xffff0000; s->src_f2_top |= 0x0000ffff & value; break; - case 0xbd2: /* TOP_B2_U */ + case 0xbd2: /* TOP_B2_U */ s->src_f2_top &= 0x0000ffff; s->src_f2_top |= (uint32_t) value << 16; break; - case 0xbd4: /* BOT_B2_L */ + case 0xbd4: /* BOT_B2_L */ s->src_f2_bottom &= 0xffff0000; s->src_f2_bottom |= 0x0000ffff & value; break; - case 0xbd6: /* BOT_B2_U */ + case 0xbd6: /* BOT_B2_U */ s->src_f2_bottom &= 0x0000ffff; s->src_f2_bottom |= (uint32_t) value << 16; break; - case 0xbd8: /* DMA_LCD_SRC_EI_B1 */ + case 0xbd8: /* DMA_LCD_SRC_EI_B1 */ s->element_index_f1 = value; break; - case 0xbda: /* DMA_LCD_SRC_FI_B1_L */ + case 0xbda: /* DMA_LCD_SRC_FI_B1_L */ s->frame_index_f1 &= 0xffff0000; s->frame_index_f1 |= 0x0000ffff & value; break; - case 0xbf4: /* DMA_LCD_SRC_FI_B1_U */ + case 0xbf4: /* DMA_LCD_SRC_FI_B1_U */ s->frame_index_f1 &= 0x0000ffff; s->frame_index_f1 |= (uint32_t) value << 16; break; - case 0xbdc: /* DMA_LCD_SRC_EI_B2 */ + case 0xbdc: /* DMA_LCD_SRC_EI_B2 */ s->element_index_f2 = value; break; - case 0xbde: /* DMA_LCD_SRC_FI_B2_L */ + case 0xbde: /* DMA_LCD_SRC_FI_B2_L */ s->frame_index_f2 &= 0xffff0000; s->frame_index_f2 |= 0x0000ffff & value; break; - case 0xbf6: /* DMA_LCD_SRC_FI_B2_U */ + case 0xbf6: /* DMA_LCD_SRC_FI_B2_U */ s->frame_index_f2 &= 0x0000ffff; s->frame_index_f2 |= (uint32_t) value << 16; break; - case 0xbe0: /* DMA_LCD_SRC_EN_B1 */ + case 0xbe0: /* DMA_LCD_SRC_EN_B1 */ s->elements_f1 = value; break; - case 0xbe4: /* DMA_LCD_SRC_FN_B1 */ + case 0xbe4: /* DMA_LCD_SRC_FN_B1 */ s->frames_f1 = value; break; - case 0xbe2: /* DMA_LCD_SRC_EN_B2 */ + case 0xbe2: /* DMA_LCD_SRC_EN_B2 */ s->elements_f2 = value; break; - case 0xbe6: /* DMA_LCD_SRC_FN_B2 */ + case 0xbe6: /* DMA_LCD_SRC_FN_B2 */ s->frames_f2 = value; break; - case 0xbea: /* DMA_LCD_LCH_CTRL */ + case 0xbea: /* DMA_LCD_LCH_CTRL */ s->lch_type = value & 0xf; break; @@ -1140,7 +1133,7 @@ static int omap_dma_3_2_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, uint16_t *ret) { switch (offset) { - case 0xbc0: /* DMA_LCD_CSDP */ + case 0xbc0: /* DMA_LCD_CSDP */ *ret = (s->brust_f2 << 14) | (s->pack_f2 << 13) | ((s->data_type_f2 >> 1) << 11) | @@ -1149,7 +1142,7 @@ static int omap_dma_3_2_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, ((s->data_type_f1 >> 1) << 0); break; - case 0xbc2: /* DMA_LCD_CCR */ + case 0xbc2: /* DMA_LCD_CCR */ *ret = (s->mode_f2 << 14) | (s->mode_f1 << 12) | (s->end_prog << 11) | @@ -1161,7 +1154,7 @@ static int omap_dma_3_2_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, (s->bs << 4); break; - case 0xbc4: /* DMA_LCD_CTRL */ + case 0xbc4: /* DMA_LCD_CTRL */ qemu_irq_lower(s->irq); *ret = (s->dst << 8) | ((s->src & 0x6) << 5) | @@ -1170,79 +1163,79 @@ static int omap_dma_3_2_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, s->dual; break; - case 0xbc8: /* TOP_B1_L */ + case 0xbc8: /* TOP_B1_L */ *ret = s->src_f1_top & 0xffff; break; - case 0xbca: /* TOP_B1_U */ + case 0xbca: /* TOP_B1_U */ *ret = s->src_f1_top >> 16; break; - case 0xbcc: /* BOT_B1_L */ + case 0xbcc: /* BOT_B1_L */ *ret = s->src_f1_bottom & 0xffff; break; - case 0xbce: /* BOT_B1_U */ + case 0xbce: /* BOT_B1_U */ *ret = s->src_f1_bottom >> 16; break; - case 0xbd0: /* TOP_B2_L */ + case 0xbd0: /* TOP_B2_L */ *ret = s->src_f2_top & 0xffff; break; - case 0xbd2: /* TOP_B2_U */ + case 0xbd2: /* TOP_B2_U */ *ret = s->src_f2_top >> 16; break; - case 0xbd4: /* BOT_B2_L */ + case 0xbd4: /* BOT_B2_L */ *ret = s->src_f2_bottom & 0xffff; break; - case 0xbd6: /* BOT_B2_U */ + case 0xbd6: /* BOT_B2_U */ *ret = s->src_f2_bottom >> 16; break; - case 0xbd8: /* DMA_LCD_SRC_EI_B1 */ + case 0xbd8: /* DMA_LCD_SRC_EI_B1 */ *ret = s->element_index_f1; break; - case 0xbda: /* DMA_LCD_SRC_FI_B1_L */ + case 0xbda: /* DMA_LCD_SRC_FI_B1_L */ *ret = s->frame_index_f1 & 0xffff; break; - case 0xbf4: /* DMA_LCD_SRC_FI_B1_U */ + case 0xbf4: /* DMA_LCD_SRC_FI_B1_U */ *ret = s->frame_index_f1 >> 16; break; - case 0xbdc: /* DMA_LCD_SRC_EI_B2 */ + case 0xbdc: /* DMA_LCD_SRC_EI_B2 */ *ret = s->element_index_f2; break; - case 0xbde: /* DMA_LCD_SRC_FI_B2_L */ + case 0xbde: /* DMA_LCD_SRC_FI_B2_L */ *ret = s->frame_index_f2 & 0xffff; break; - case 0xbf6: /* DMA_LCD_SRC_FI_B2_U */ + case 0xbf6: /* DMA_LCD_SRC_FI_B2_U */ *ret = s->frame_index_f2 >> 16; break; - case 0xbe0: /* DMA_LCD_SRC_EN_B1 */ + case 0xbe0: /* DMA_LCD_SRC_EN_B1 */ *ret = s->elements_f1; break; - case 0xbe4: /* DMA_LCD_SRC_FN_B1 */ + case 0xbe4: /* DMA_LCD_SRC_FN_B1 */ *ret = s->frames_f1; break; - case 0xbe2: /* DMA_LCD_SRC_EN_B2 */ + case 0xbe2: /* DMA_LCD_SRC_EN_B2 */ *ret = s->elements_f2; break; - case 0xbe6: /* DMA_LCD_SRC_FN_B2 */ + case 0xbe6: /* DMA_LCD_SRC_FN_B2 */ *ret = s->frames_f2; break; - case 0xbea: /* DMA_LCD_LCH_CTRL */ + case 0xbea: /* DMA_LCD_LCH_CTRL */ *ret = s->lch_type; break; @@ -1256,7 +1249,7 @@ static int omap_dma_3_1_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, uint16_t value) { switch (offset) { - case 0x300: /* SYS_DMA_LCD_CTRL */ + case 0x300: /* SYS_DMA_LCD_CTRL */ s->src = (value & 0x40) ? imif : emiff; s->condition = 0; /* Assume no bus errors and thus no BUS_ERROR irq bits. */ @@ -1264,42 +1257,42 @@ static int omap_dma_3_1_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, s->dual = value & 1; break; - case 0x302: /* SYS_DMA_LCD_TOP_F1_L */ + case 0x302: /* SYS_DMA_LCD_TOP_F1_L */ s->src_f1_top &= 0xffff0000; s->src_f1_top |= 0x0000ffff & value; break; - case 0x304: /* SYS_DMA_LCD_TOP_F1_U */ + case 0x304: /* SYS_DMA_LCD_TOP_F1_U */ s->src_f1_top &= 0x0000ffff; s->src_f1_top |= (uint32_t)value << 16; break; - case 0x306: /* SYS_DMA_LCD_BOT_F1_L */ + case 0x306: /* SYS_DMA_LCD_BOT_F1_L */ s->src_f1_bottom &= 0xffff0000; s->src_f1_bottom |= 0x0000ffff & value; break; - case 0x308: /* SYS_DMA_LCD_BOT_F1_U */ + case 0x308: /* SYS_DMA_LCD_BOT_F1_U */ s->src_f1_bottom &= 0x0000ffff; s->src_f1_bottom |= (uint32_t)value << 16; break; - case 0x30a: /* SYS_DMA_LCD_TOP_F2_L */ + case 0x30a: /* SYS_DMA_LCD_TOP_F2_L */ s->src_f2_top &= 0xffff0000; s->src_f2_top |= 0x0000ffff & value; break; - case 0x30c: /* SYS_DMA_LCD_TOP_F2_U */ + case 0x30c: /* SYS_DMA_LCD_TOP_F2_U */ s->src_f2_top &= 0x0000ffff; s->src_f2_top |= (uint32_t)value << 16; break; - case 0x30e: /* SYS_DMA_LCD_BOT_F2_L */ + case 0x30e: /* SYS_DMA_LCD_BOT_F2_L */ s->src_f2_bottom &= 0xffff0000; s->src_f2_bottom |= 0x0000ffff & value; break; - case 0x310: /* SYS_DMA_LCD_BOT_F2_U */ + case 0x310: /* SYS_DMA_LCD_BOT_F2_U */ s->src_f2_bottom &= 0x0000ffff; s->src_f2_bottom |= (uint32_t)value << 16; break; @@ -1316,7 +1309,7 @@ static int omap_dma_3_1_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, int i; switch (offset) { - case 0x300: /* SYS_DMA_LCD_CTRL */ + case 0x300: /* SYS_DMA_LCD_CTRL */ i = s->condition; s->condition = 0; qemu_irq_lower(s->irq); @@ -1324,35 +1317,35 @@ static int omap_dma_3_1_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, (s->interrupts << 1) | s->dual; break; - case 0x302: /* SYS_DMA_LCD_TOP_F1_L */ + case 0x302: /* SYS_DMA_LCD_TOP_F1_L */ *ret = s->src_f1_top & 0xffff; break; - case 0x304: /* SYS_DMA_LCD_TOP_F1_U */ + case 0x304: /* SYS_DMA_LCD_TOP_F1_U */ *ret = s->src_f1_top >> 16; break; - case 0x306: /* SYS_DMA_LCD_BOT_F1_L */ + case 0x306: /* SYS_DMA_LCD_BOT_F1_L */ *ret = s->src_f1_bottom & 0xffff; break; - case 0x308: /* SYS_DMA_LCD_BOT_F1_U */ + case 0x308: /* SYS_DMA_LCD_BOT_F1_U */ *ret = s->src_f1_bottom >> 16; break; - case 0x30a: /* SYS_DMA_LCD_TOP_F2_L */ + case 0x30a: /* SYS_DMA_LCD_TOP_F2_L */ *ret = s->src_f2_top & 0xffff; break; - case 0x30c: /* SYS_DMA_LCD_TOP_F2_U */ + case 0x30c: /* SYS_DMA_LCD_TOP_F2_U */ *ret = s->src_f2_top >> 16; break; - case 0x30e: /* SYS_DMA_LCD_BOT_F2_L */ + case 0x30e: /* SYS_DMA_LCD_BOT_F2_L */ *ret = s->src_f2_bottom & 0xffff; break; - case 0x310: /* SYS_DMA_LCD_BOT_F2_U */ + case 0x310: /* SYS_DMA_LCD_BOT_F2_U */ *ret = s->src_f2_bottom >> 16; break; @@ -1365,18 +1358,18 @@ static int omap_dma_3_1_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, static int omap_dma_sys_write(struct omap_dma_s *s, int offset, uint16_t value) { switch (offset) { - case 0x400: /* SYS_DMA_GCR */ + case 0x400: /* SYS_DMA_GCR */ s->gcr = value; break; - case 0x404: /* DMA_GSCR */ + case 0x404: /* DMA_GSCR */ if (value & 0x8) omap_dma_disable_3_1_mapping(s); else omap_dma_enable_3_1_mapping(s); break; - case 0x408: /* DMA_GRST */ + case 0x408: /* DMA_GRST */ if (value & 0x1) omap_dma_reset(s->dma); break; @@ -1391,57 +1384,57 @@ static int omap_dma_sys_read(struct omap_dma_s *s, int offset, uint16_t *ret) { switch (offset) { - case 0x400: /* SYS_DMA_GCR */ + case 0x400: /* SYS_DMA_GCR */ *ret = s->gcr; break; - case 0x404: /* DMA_GSCR */ + case 0x404: /* DMA_GSCR */ *ret = s->omap_3_1_mapping_disabled << 3; break; - case 0x408: /* DMA_GRST */ + case 0x408: /* DMA_GRST */ *ret = 0; break; - case 0x442: /* DMA_HW_ID */ - case 0x444: /* DMA_PCh2_ID */ - case 0x446: /* DMA_PCh0_ID */ - case 0x448: /* DMA_PCh1_ID */ - case 0x44a: /* DMA_PChG_ID */ - case 0x44c: /* DMA_PChD_ID */ + case 0x442: /* DMA_HW_ID */ + case 0x444: /* DMA_PCh2_ID */ + case 0x446: /* DMA_PCh0_ID */ + case 0x448: /* DMA_PCh1_ID */ + case 0x44a: /* DMA_PChG_ID */ + case 0x44c: /* DMA_PChD_ID */ *ret = 1; break; - case 0x44e: /* DMA_CAPS_0_U */ + case 0x44e: /* DMA_CAPS_0_U */ *ret = (s->caps[0] >> 16) & 0xffff; break; - case 0x450: /* DMA_CAPS_0_L */ + case 0x450: /* DMA_CAPS_0_L */ *ret = (s->caps[0] >> 0) & 0xffff; break; - case 0x452: /* DMA_CAPS_1_U */ + case 0x452: /* DMA_CAPS_1_U */ *ret = (s->caps[1] >> 16) & 0xffff; break; - case 0x454: /* DMA_CAPS_1_L */ + case 0x454: /* DMA_CAPS_1_L */ *ret = (s->caps[1] >> 0) & 0xffff; break; - case 0x456: /* DMA_CAPS_2 */ + case 0x456: /* DMA_CAPS_2 */ *ret = s->caps[2]; break; - case 0x458: /* DMA_CAPS_3 */ + case 0x458: /* DMA_CAPS_3 */ *ret = s->caps[3]; break; - case 0x45a: /* DMA_CAPS_4 */ + case 0x45a: /* DMA_CAPS_4 */ *ret = s->caps[4]; break; - case 0x460: /* DMA_PCh2_SR */ - case 0x480: /* DMA_PCh0_SR */ - case 0x482: /* DMA_PCh1_SR */ - case 0x4c0: /* DMA_PChD_SR_0 */ + case 0x460: /* DMA_PCh2_SR */ + case 0x480: /* DMA_PCh0_SR */ + case 0x482: /* DMA_PCh1_SR */ + case 0x4c0: /* DMA_PChD_SR_0 */ qemu_log_mask(LOG_UNIMP, "%s: Physical Channel Status Registers not implemented\n", __func__); @@ -1587,41 +1580,40 @@ 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 */ - (1 << 18); /* Transparent BLT Capability */ + (1 << 19) | /* Constant Fill Capability */ + (1 << 18); /* Transparent BLT Capability */ s->caps[1] = - (1 << 1); /* 1-bit palettized capability (DMA 3.2 only) */ + (1 << 1); /* 1-bit palettized capability (DMA 3.2 only) */ s->caps[2] = - (1 << 8) | /* SEPARATE_SRC_AND_DST_INDEX_CPBLTY */ - (1 << 7) | /* DST_DOUBLE_INDEX_ADRS_CPBLTY */ - (1 << 6) | /* DST_SINGLE_INDEX_ADRS_CPBLTY */ - (1 << 5) | /* DST_POST_INCRMNT_ADRS_CPBLTY */ - (1 << 4) | /* DST_CONST_ADRS_CPBLTY */ - (1 << 3) | /* SRC_DOUBLE_INDEX_ADRS_CPBLTY */ - (1 << 2) | /* SRC_SINGLE_INDEX_ADRS_CPBLTY */ - (1 << 1) | /* SRC_POST_INCRMNT_ADRS_CPBLTY */ - (1 << 0); /* SRC_CONST_ADRS_CPBLTY */ + (1 << 8) | /* SEPARATE_SRC_AND_DST_INDEX_CPBLTY */ + (1 << 7) | /* DST_DOUBLE_INDEX_ADRS_CPBLTY */ + (1 << 6) | /* DST_SINGLE_INDEX_ADRS_CPBLTY */ + (1 << 5) | /* DST_POST_INCRMNT_ADRS_CPBLTY */ + (1 << 4) | /* DST_CONST_ADRS_CPBLTY */ + (1 << 3) | /* SRC_DOUBLE_INDEX_ADRS_CPBLTY */ + (1 << 2) | /* SRC_SINGLE_INDEX_ADRS_CPBLTY */ + (1 << 1) | /* SRC_POST_INCRMNT_ADRS_CPBLTY */ + (1 << 0); /* SRC_CONST_ADRS_CPBLTY */ s->caps[3] = - (1 << 6) | /* BLOCK_SYNCHR_CPBLTY (DMA 4 only) */ - (1 << 7) | /* PKT_SYNCHR_CPBLTY (DMA 4 only) */ - (1 << 5) | /* CHANNEL_CHAINING_CPBLTY */ - (1 << 4) | /* LCh_INTERLEAVE_CPBLTY */ - (1 << 3) | /* AUTOINIT_REPEAT_CPBLTY (DMA 3.2 only) */ - (1 << 2) | /* AUTOINIT_ENDPROG_CPBLTY (DMA 3.2 only) */ - (1 << 1) | /* FRAME_SYNCHR_CPBLTY */ - (1 << 0); /* ELMNT_SYNCHR_CPBLTY */ + (1 << 6) | /* BLOCK_SYNCHR_CPBLTY (DMA 4 only) */ + (1 << 7) | /* PKT_SYNCHR_CPBLTY (DMA 4 only) */ + (1 << 5) | /* CHANNEL_CHAINING_CPBLTY */ + (1 << 4) | /* LCh_INTERLEAVE_CPBLTY */ + (1 << 3) | /* AUTOINIT_REPEAT_CPBLTY (DMA 3.2 only) */ + (1 << 2) | /* AUTOINIT_ENDPROG_CPBLTY (DMA 3.2 only) */ + (1 << 1) | /* FRAME_SYNCHR_CPBLTY */ + (1 << 0); /* ELMNT_SYNCHR_CPBLTY */ s->caps[4] = - (1 << 7) | /* PKT_INTERRUPT_CPBLTY (DMA 4 only) */ - (1 << 6) | /* SYNC_STATUS_CPBLTY */ - (1 << 5) | /* BLOCK_INTERRUPT_CPBLTY */ - (1 << 4) | /* LAST_FRAME_INTERRUPT_CPBLTY */ - (1 << 3) | /* FRAME_INTERRUPT_CPBLTY */ - (1 << 2) | /* HALF_FRAME_INTERRUPT_CPBLTY */ - (1 << 1) | /* EVENT_DROP_INTERRUPT_CPBLTY */ - (1 << 0); /* TIMEOUT_INTERRUPT_CPBLTY (DMA 3.2 only) */ + (1 << 7) | /* PKT_INTERRUPT_CPBLTY (DMA 4 only) */ + (1 << 6) | /* SYNC_STATUS_CPBLTY */ + (1 << 5) | /* BLOCK_INTERRUPT_CPBLTY */ + (1 << 4) | /* LAST_FRAME_INTERRUPT_CPBLTY */ + (1 << 3) | /* FRAME_INTERRUPT_CPBLTY */ + (1 << 2) | /* HALF_FRAME_INTERRUPT_CPBLTY */ + (1 << 1) | /* EVENT_DROP_INTERRUPT_CPBLTY */ + (1 << 0); /* TIMEOUT_INTERRUPT_CPBLTY (DMA 3.2 only) */ break; } } @@ -1678,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 = 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 = 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 2627307cc85..277d9343223 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), @@ -408,20 +408,19 @@ static void pl081_init(Object *obj) s->nchannels = 2; } -static Property pl080_properties[] = { +static const Property pl080_properties[] = { DEFINE_PROP_LINK("downstream", PL080State, downstream, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; -static void pl080_class_init(ObjectClass *oc, void *data) +static void pl080_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); 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 e7e67dd8b6a..a570bb08ec4 100644 --- a/hw/dma/pl330.c +++ b/hw/dma/pl330.c @@ -15,13 +15,14 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/timer.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/log.h" #include "qemu/module.h" #include "trace.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); } } @@ -1653,7 +1646,7 @@ static void pl330_realize(DeviceState *dev, Error **errp) pl330_fifo_init(&s->fifo, s->data_width / 4 * s->data_buffer_dep); } -static Property pl330_properties[] = { +static const Property pl330_properties[] = { /* CR0 */ DEFINE_PROP_UINT32("num_chnls", PL330State, num_chnls, 8), DEFINE_PROP_UINT8("num_periph_req", PL330State, num_periph_req, 4), @@ -1676,16 +1669,14 @@ static Property pl330_properties[] = { DEFINE_PROP_LINK("memory", PL330State, mem_mr, TYPE_MEMORY_REGION, MemoryRegion *), - - DEFINE_PROP_END_OF_LIST(), }; -static void pl330_class_init(ObjectClass *klass, void *data) +static void pl330_class_init(ObjectClass *klass, const 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 fa896f7edf7..00000000000 --- 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 aa1d323a36b..b6ed1d46433 100644 --- a/hw/dma/rc4030.c +++ b/hw/dma/rc4030.c @@ -32,7 +32,7 @@ #include "qemu/timer.h" #include "qemu/log.h" #include "qemu/module.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "trace.h" #include "qom/object.h" @@ -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), @@ -701,13 +701,13 @@ static void rc4030_unrealize(DeviceState *dev) object_unparent(OBJECT(&s->dma_mr)); } -static void rc4030_class_init(ObjectClass *klass, void *class_data) +static void rc4030_class_init(ObjectClass *klass, const void *class_data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = rc4030_realize; dc->unrealize = rc4030_unrealize; - dc->reset = rc4030_reset; + device_class_set_legacy_reset(dc, rc4030_reset); dc->vmsd = &vmstate_rc4030; } @@ -720,7 +720,7 @@ static const TypeInfo rc4030_info = { }; static void rc4030_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/dma/sifive_pdma.c b/hw/dma/sifive_pdma.c index 1dd88f3479d..48de3a24785 100644 --- a/hw/dma/sifive_pdma.c +++ b/hw/dma/sifive_pdma.c @@ -28,7 +28,7 @@ #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/dma/sifive_pdma.h" #define DMA_CONTROL 0x000 @@ -152,7 +152,6 @@ static void sifive_pdma_run(SiFivePDMAState *s, int ch) error: s->chan[ch].state = DMA_CHAN_STATE_ERROR; s->chan[ch].control |= CONTROL_ERR; - return; } static inline void sifive_pdma_update_irq(SiFivePDMAState *s, int ch) @@ -465,7 +464,7 @@ static void sifive_pdma_realize(DeviceState *dev, Error **errp) } } -static void sifive_pdma_class_init(ObjectClass *klass, void *data) +static void sifive_pdma_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index 3a430057f54..d5c52b804f8 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 0ef13c5e9a8..60c23b69e5c 100644 --- a/hw/dma/sparc32_dma.c +++ b/hw/dma/sparc32_dma.c @@ -32,7 +32,7 @@ #include "hw/sparc/sun4m_iommu.h" #include "hw/sysbus.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qapi/error.h" #include "qemu/module.h" #include "trace.h" @@ -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() } @@ -274,11 +274,11 @@ static void sparc32_dma_device_init(Object *obj) qdev_init_gpio_out(dev, s->gpio, 2); } -static void sparc32_dma_device_class_init(ObjectClass *klass, void *data) +static void sparc32_dma_device_class_init(ObjectClass *klass, const 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; } @@ -316,7 +316,8 @@ static void sparc32_espdma_device_realize(DeviceState *dev, Error **errp) sysbus_realize(SYS_BUS_DEVICE(sysbus), &error_fatal); } -static void sparc32_espdma_device_class_init(ObjectClass *klass, void *data) +static void sparc32_espdma_device_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -351,7 +352,8 @@ static void sparc32_ledma_device_realize(DeviceState *dev, Error **errp) sysbus_realize(SYS_BUS_DEVICE(lance), &error_fatal); } -static void sparc32_ledma_device_class_init(ObjectClass *klass, void *data) +static void sparc32_ledma_device_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -426,7 +428,7 @@ static void sparc32_dma_init(Object *obj) TYPE_SPARC32_LEDMA_DEVICE); } -static void sparc32_dma_class_init(ObjectClass *klass, void *data) +static void sparc32_dma_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/dma/trace-events b/hw/dma/trace-events index 3c47df54e4d..4c09790f9af 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 12c90267df6..2020399fd59 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -33,9 +33,10 @@ #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "system/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; @@ -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); @@ -590,21 +611,22 @@ static void xilinx_axidma_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static Property axidma_properties[] = { +static const Property axidma_properties[] = { DEFINE_PROP_UINT32("freqhz", XilinxAXIDMA, freqhz, 50000000), DEFINE_PROP_LINK("axistream-connected", XilinxAXIDMA, tx_data_dev, TYPE_STREAM_SINK, StreamSink *), DEFINE_PROP_LINK("axistream-control-connected", XilinxAXIDMA, tx_control_dev, TYPE_STREAM_SINK, StreamSink *), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_LINK("dma", XilinxAXIDMA, dma_mr, + TYPE_MEMORY_REGION, MemoryRegion *), }; -static void axidma_class_init(ObjectClass *klass, void *data) +static void axidma_class_init(ObjectClass *klass, const 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); } @@ -617,7 +639,8 @@ static StreamSinkClass xilinx_axidma_control_stream_class = { .push = xilinx_axidma_control_stream_push, }; -static void xilinx_axidma_stream_class_init(ObjectClass *klass, void *data) +static void xilinx_axidma_stream_class_init(ObjectClass *klass, + const void *data) { StreamSinkClass *ssc = STREAM_SINK_CLASS(klass); @@ -639,7 +662,7 @@ static const TypeInfo xilinx_axidma_data_stream_info = { .instance_size = sizeof(XilinxAXIDMAStreamSink), .class_init = xilinx_axidma_stream_class_init, .class_data = &xilinx_axidma_data_stream_class, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_STREAM_SINK }, { } } @@ -651,7 +674,7 @@ static const TypeInfo xilinx_axidma_control_stream_info = { .instance_size = sizeof(XilinxAXIDMAStreamSink), .class_init = xilinx_axidma_stream_class_init, .class_data = &xilinx_axidma_control_stream_class, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_STREAM_SINK }, { } } diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index 4eb7f66e9f8..0c075e7d0d1 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), @@ -815,16 +810,17 @@ static const VMStateDescription vmstate_zdma = { } }; -static Property zdma_props[] = { +static const Property zdma_props[] = { DEFINE_PROP_UINT32("bus-width", XlnxZDMA, cfg.bus_width, 64), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_LINK("dma", XlnxZDMA, dma_mr, + TYPE_MEMORY_REGION, MemoryRegion *), }; -static void zdma_class_init(ObjectClass *klass, void *data) +static void zdma_class_init(ObjectClass *klass, const 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 f5ad1a0d22c..26845713ee5 100644 --- a/hw/dma/xlnx-zynq-devcfg.c +++ b/hw/dma/xlnx-zynq-devcfg.c @@ -29,7 +29,7 @@ #include "hw/irq.h" #include "migration/vmstate.h" #include "qemu/bitops.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/log.h" #include "qemu/module.h" @@ -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, @@ -380,11 +380,11 @@ static void xlnx_zynq_devcfg_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static void xlnx_zynq_devcfg_class_init(ObjectClass *klass, void *data) +static void xlnx_zynq_devcfg_class_init(ObjectClass *klass, const 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 88002698a15..d8c7da1a501 100644 --- a/hw/dma/xlnx_csu_dma.c +++ b/hw/dma/xlnx_csu_dma.c @@ -25,7 +25,7 @@ #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/ptimer.h" #include "hw/stream.h" #include "hw/register.h" @@ -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 */ @@ -287,7 +287,7 @@ static uint32_t xlnx_csu_dma_advance(XlnxCSUDMA *s, uint32_t len) static void xlnx_csu_dma_src_notify(void *opaque) { XlnxCSUDMA *s = XLNX_CSU_DMA(opaque); - unsigned char buf[4 * 1024]; + QEMU_UNINITIALIZED unsigned char buf[4 * 1024]; size_t rlen = 0; ptimer_transaction_begin(s->src_timer); @@ -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), @@ -687,7 +691,7 @@ static const VMStateDescription vmstate_xlnx_csu_dma = { } }; -static Property xlnx_csu_dma_properties[] = { +static const Property xlnx_csu_dma_properties[] = { /* * Ref PG021, Stream Data Width: * Data width in bits of the AXI S2MM AXI4-Stream Data bus. @@ -702,16 +706,19 @@ static Property xlnx_csu_dma_properties[] = { * which channel the device is connected to. */ DEFINE_PROP_BOOL("is-dst", XlnxCSUDMA, is_dst, true), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_LINK("stream-connected-dma", XlnxCSUDMA, tx_dev, + TYPE_STREAM_SINK, StreamSink *), + DEFINE_PROP_LINK("dma", XlnxCSUDMA, dma_mr, + TYPE_MEMORY_REGION, MemoryRegion *), }; -static void xlnx_csu_dma_class_init(ObjectClass *klass, void *data) +static void xlnx_csu_dma_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 +735,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 = { @@ -746,7 +744,7 @@ static const TypeInfo xlnx_csu_dma_info = { .class_init = xlnx_csu_dma_class_init, .class_size = sizeof(XlnxCSUDMAClass), .instance_init = xlnx_csu_dma_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_STREAM_SINK }, { } } diff --git a/hw/dma/xlnx_dpdma.c b/hw/dma/xlnx_dpdma.c index dd66be5265d..3d88ccc8da9 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), @@ -593,12 +593,12 @@ static void xlnx_dpdma_reset(DeviceState *dev) } } -static void xlnx_dpdma_class_init(ObjectClass *oc, void *data) +static void xlnx_dpdma_class_init(ObjectClass *oc, const 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 00000000000..9cee657a0f0 --- /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 00000000000..172ba16b0c0 --- /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, const 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 00000000000..e2145c5934b --- /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, const 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 00000000000..083a5507abc --- /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, const 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 00000000000..6c52d5e745e --- /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, const 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 00000000000..8ec7f5fd780 --- /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, const 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 00000000000..a18a076552f --- /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 00000000000..9e286d08d3c --- /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 00000000000..ee67c7fb04d --- /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 d2cf3accc88..a209294c20c 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,21 @@ config GPIO_PWR config SIFIVE_GPIO bool + +config STM32L4X5_GPIO + bool + +config PCA9552 + bool + depends on I2C + +config PCA9554 + bool + depends on I2C + +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 1e267dd4820..609a556908f 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; @@ -755,7 +800,6 @@ static void aspeed_gpio_write_index_mode(void *opaque, hwaddr offset, return; } aspeed_gpio_update(s, set, set->data_value, UINT32_MAX); - return; } static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, @@ -785,6 +829,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%" @@ -877,7 +927,6 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, return; } aspeed_gpio_update(s, set, set->data_value, UINT32_MAX); - return; } static int get_set_idx(AspeedGPIOState *s, const char *group, int *group_idx) @@ -951,7 +1000,314 @@ 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); +} + +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; + } +} + +/* 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 +1353,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 +1371,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 +1408,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 +1441,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 +1464,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, @@ -1099,17 +1473,17 @@ static const VMStateDescription vmstate_aspeed_gpio = { } }; -static void aspeed_gpio_class_init(ObjectClass *klass, void *data) +static void aspeed_gpio_class_init(ObjectClass *klass, const 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; } -static void aspeed_gpio_ast2400_class_init(ObjectClass *klass, void *data) +static void aspeed_gpio_ast2400_class_init(ObjectClass *klass, const void *data) { AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass); @@ -1117,9 +1491,12 @@ 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) +static void aspeed_gpio_2500_class_init(ObjectClass *klass, const void *data) { AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass); @@ -1127,9 +1504,13 @@ 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) +static void aspeed_gpio_ast2600_3_3v_class_init(ObjectClass *klass, + const void *data) { AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass); @@ -1137,9 +1518,13 @@ 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) +static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, + const void *data) { AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass); @@ -1147,9 +1532,12 @@ 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) +static void aspeed_gpio_1030_class_init(ObjectClass *klass, const void *data) { AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass); @@ -1157,6 +1545,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, const 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 +1606,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 +1621,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 c995bba1d9f..dfb5d5cb578 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), @@ -319,13 +319,13 @@ static void bcm2835_gpio_realize(DeviceState *dev, Error **errp) s->sdbus_sdhost = SD_BUS(obj); } -static void bcm2835_gpio_class_init(ObjectClass *klass, void *data) +static void bcm2835_gpio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 00000000000..1069e7811bb --- /dev/null +++ b/hw/gpio/bcm2838_gpio.c @@ -0,0 +1,389 @@ +/* + * 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); + } +} + +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, const 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 74f61383562..40c028bed9e 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() } @@ -85,13 +85,13 @@ static void gpio_key_realize(DeviceState *dev, Error **errp) s->timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, gpio_key_timer_expired, s); } -static void gpio_key_class_init(ObjectClass *klass, void *data) +static void gpio_key_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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/gpio_pwr.c b/hw/gpio/gpio_pwr.c index dbaf1c70c88..2d14f8b344c 100644 --- a/hw/gpio/gpio_pwr.c +++ b/hw/gpio/gpio_pwr.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #define TYPE_GPIOPWR "gpio-pwr" OBJECT_DECLARE_SIMPLE_TYPE(GPIO_PWR_State, GPIOPWR) diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c index c7f98b7bb15..450ece45482 100644 --- a/hw/gpio/imx_gpio.c +++ b/hw/gpio/imx_gpio.c @@ -24,6 +24,7 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" +#include "trace.h" #ifndef DEBUG_IMX_GPIO #define DEBUG_IMX_GPIO 0 @@ -34,14 +35,6 @@ typedef enum IMXGPIOLevel { IMX_GPIO_LEVEL_HIGH = 1, } IMXGPIOLevel; -#define DPRINTF(fmt, args...) \ - do { \ - if (DEBUG_IMX_GPIO) { \ - fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_GPIO, \ - __func__, ##args); \ - } \ - } while (0) - static const char *imx_gpio_reg_name(uint32_t reg) { switch (reg) { @@ -79,7 +72,7 @@ static void imx_gpio_update_int(IMXGPIOState *s) static void imx_gpio_set_int_line(IMXGPIOState *s, int line, IMXGPIOLevel level) { /* if this signal isn't configured as an input signal, nothing to do */ - if (!extract32(s->gdir, line, 1)) { + if (extract32(s->gdir, line, 1)) { return; } @@ -111,6 +104,8 @@ static void imx_gpio_set(void *opaque, int line, int level) IMXGPIOState *s = IMX_GPIO(opaque); IMXGPIOLevel imx_level = level ? IMX_GPIO_LEVEL_HIGH : IMX_GPIO_LEVEL_LOW; + trace_imx_gpio_set(DEVICE(s)->canonical_path, line, imx_level); + imx_gpio_set_int_line(s, line, imx_level); /* this is an input signal, so set PSR */ @@ -200,7 +195,8 @@ static uint64_t imx_gpio_read(void *opaque, hwaddr offset, unsigned size) break; } - DPRINTF("(%s) = 0x%" PRIx32 "\n", imx_gpio_reg_name(offset), reg_value); + trace_imx_gpio_read(DEVICE(s)->canonical_path, imx_gpio_reg_name(offset), + reg_value); return reg_value; } @@ -210,8 +206,8 @@ static void imx_gpio_write(void *opaque, hwaddr offset, uint64_t value, { IMXGPIOState *s = IMX_GPIO(opaque); - DPRINTF("(%s, value = 0x%" PRIx32 ")\n", imx_gpio_reg_name(offset), - (uint32_t)value); + trace_imx_gpio_write(DEVICE(s)->canonical_path, imx_gpio_reg_name(offset), + value); switch (offset) { case DR_ADDR: @@ -261,8 +257,6 @@ static void imx_gpio_write(void *opaque, hwaddr offset, uint64_t value, HWADDR_PRIx "\n", TYPE_IMX_GPIO, __func__, offset); break; } - - return; } static const MemoryRegionOps imx_gpio_ops = { @@ -277,7 +271,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), @@ -290,11 +284,10 @@ static const VMStateDescription vmstate_imx_gpio = { } }; -static Property imx_gpio_properties[] = { +static const Property imx_gpio_properties[] = { DEFINE_PROP_BOOL("has-edge-sel", IMXGPIOState, has_edge_sel, true), DEFINE_PROP_BOOL("has-upper-pin-irq", IMXGPIOState, has_upper_pin_irq, false), - DEFINE_PROP_END_OF_LIST(), }; static void imx_gpio_reset(DeviceState *dev) @@ -328,12 +321,12 @@ static void imx_gpio_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); } -static void imx_gpio_class_init(ObjectClass *klass, void *data) +static void imx_gpio_class_init(ObjectClass *klass, const 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 4470cfe9856..00000000000 --- a/hw/gpio/max7310.c +++ /dev/null @@ -1,217 +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) -{ - MAX7310State *s = MAX7310(dev); - - qdev_init_gpio_in(dev, max7310_gpio_set, ARRAY_SIZE(s->handler)); - qdev_init_gpio_out(dev, s->handler, ARRAY_SIZE(s->handler)); -} - -static void max7310_class_init(ObjectClass *klass, void *data) -{ - 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 066ea96480f..74840619c01 100644 --- a/hw/gpio/meson.build +++ b/hw/gpio/meson.build @@ -1,14 +1,20 @@ system_ss.add(when: 'CONFIG_GPIO_KEY', if_true: files('gpio_key.c')) system_ss.add(when: 'CONFIG_GPIO_MPC8XXX', if_true: files('mpc8xxx.c')) system_ss.add(when: 'CONFIG_GPIO_PWR', if_true: files('gpio_pwr.c')) -system_ss.add(when: 'CONFIG_MAX7310', if_true: files('max7310.c')) +system_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c')) +system_ss.add(when: 'CONFIG_PCA9554', if_true: files('pca9554.c')) system_ss.add(when: 'CONFIG_PL061', if_true: files('pl061.c')) -system_ss.add(when: 'CONFIG_ZAURUS', if_true: files('zaurus.c')) +system_ss.add(when: 'CONFIG_ZAURUS_SCOOP', if_true: files('zaurus.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')) +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 cb42acb6da8..257497af584 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), @@ -200,25 +199,22 @@ static void mpc8xxx_gpio_initfn(Object *obj) qdev_init_gpio_out(dev, s->out, 32); } -static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data) +static void mpc8xxx_gpio_class_init(ObjectClass *klass, const 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 3376901ab13..66f8256a7a7 100644 --- a/hw/gpio/npcm7xx_gpio.c +++ b/hw/gpio/npcm7xx_gpio.c @@ -220,8 +220,6 @@ static void npcm7xx_gpio_regs_write(void *opaque, hwaddr addr, uint64_t v, return; } - diff = s->regs[reg] ^ value; - switch (reg) { case NPCM7XX_GPIO_TLOCK1: case NPCM7XX_GPIO_TLOCK2: @@ -242,6 +240,7 @@ static void npcm7xx_gpio_regs_write(void *opaque, hwaddr addr, uint64_t v, case NPCM7XX_GPIO_PU: case NPCM7XX_GPIO_PD: case NPCM7XX_GPIO_IEM: + diff = s->regs[reg] ^ value; s->regs[reg] = value; npcm7xx_gpio_update_pins(s, diff); break; @@ -352,7 +351,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 +376,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), @@ -386,7 +385,7 @@ static const VMStateDescription vmstate_npcm7xx_gpio = { }, }; -static Property npcm7xx_gpio_properties[] = { +static const Property npcm7xx_gpio_properties[] = { /* Bit n set => pin n has pullup enabled by default. */ DEFINE_PROP_UINT32("reset-pullup", NPCM7xxGPIOState, reset_pu, 0), /* Bit n set => pin n has pulldown enabled by default. */ @@ -395,10 +394,9 @@ static Property npcm7xx_gpio_properties[] = { DEFINE_PROP_UINT32("reset-osrc", NPCM7xxGPIOState, reset_osrc, 0), /* Bit n set => pin n has high drive strength by default. */ DEFINE_PROP_UINT32("reset-odsc", NPCM7xxGPIOState, reset_odsc, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void npcm7xx_gpio_class_init(ObjectClass *klass, void *data) +static void npcm7xx_gpio_class_init(ObjectClass *klass, const void *data) { ResettableClass *reset = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/gpio/nrf51_gpio.c b/hw/gpio/nrf51_gpio.c index 08396c69a4b..d94c0c47da2 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; @@ -280,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), @@ -305,12 +304,12 @@ static void nrf51_gpio_init(Object *obj) qdev_init_gpio_out_named(DEVICE(s), &s->detect, "detect", 1); } -static void nrf51_gpio_class_init(ObjectClass *klass, void *data) +static void nrf51_gpio_class_init(ObjectClass *klass, const 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 a3341d70f16..f27806b774a 100644 --- a/hw/gpio/omap_gpio.c +++ b/hw/gpio/omap_gpio.c @@ -80,25 +80,25 @@ static uint64_t omap_gpio_read(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* DATA_INPUT */ + case 0x00: /* DATA_INPUT */ return s->inputs & s->pins; - case 0x04: /* DATA_OUTPUT */ + case 0x04: /* DATA_OUTPUT */ return s->outputs; - case 0x08: /* DIRECTION_CONTROL */ + case 0x08: /* DIRECTION_CONTROL */ return s->dir; - case 0x0c: /* INTERRUPT_CONTROL */ + case 0x0c: /* INTERRUPT_CONTROL */ return s->edge; - case 0x10: /* INTERRUPT_MASK */ + case 0x10: /* INTERRUPT_MASK */ return s->mask; - case 0x14: /* INTERRUPT_STATUS */ + case 0x14: /* INTERRUPT_STATUS */ return s->ints; - case 0x18: /* PIN_CONTROL (not in OMAP310) */ + case 0x18: /* PIN_CONTROL (not in OMAP310) */ OMAP_BAD_REG(addr); return s->pins; } @@ -121,11 +121,11 @@ static void omap_gpio_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* DATA_INPUT */ + case 0x00: /* DATA_INPUT */ OMAP_RO_REG(addr); return; - case 0x04: /* DATA_OUTPUT */ + case 0x04: /* DATA_OUTPUT */ diff = (s->outputs ^ value) & ~s->dir; s->outputs = value; while ((ln = ctz32(diff)) != 32) { @@ -135,7 +135,7 @@ static void omap_gpio_write(void *opaque, hwaddr addr, } break; - case 0x08: /* DIRECTION_CONTROL */ + case 0x08: /* DIRECTION_CONTROL */ diff = s->outputs & (s->dir ^ value); s->dir = value; @@ -147,21 +147,21 @@ static void omap_gpio_write(void *opaque, hwaddr addr, } break; - case 0x0c: /* INTERRUPT_CONTROL */ + case 0x0c: /* INTERRUPT_CONTROL */ s->edge = value; break; - case 0x10: /* INTERRUPT_MASK */ + case 0x10: /* INTERRUPT_MASK */ s->mask = value; break; - case 0x14: /* INTERRUPT_STATUS */ + case 0x14: /* INTERRUPT_STATUS */ s->ints &= ~value; if (!s->ints) qemu_irq_lower(s->irq); break; - case 0x18: /* PIN_CONTROL (not in OMAP310 TRM) */ + case 0x18: /* PIN_CONTROL (not in OMAP310 TRM) */ OMAP_BAD_REG(addr); s->pins = value; break; @@ -190,408 +190,6 @@ 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 Omap2GpioState { - 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) -{ - Omap2GpioState *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 = 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 = 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) { Omap1GpioState *s = OMAP1_GPIO(dev); @@ -599,81 +197,6 @@ static void omap_gpif_reset(DeviceState *dev) omap_gpio_reset(&s->omap1); } -static void omap2_gpif_reset(DeviceState *dev) -{ - Omap2GpioState *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) -{ - Omap2GpioState *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) -{ - Omap2GpioState *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); @@ -697,67 +220,21 @@ static void omap_gpio_realize(DeviceState *dev, Error **errp) } } -static void omap2_gpio_realize(DeviceState *dev, Error **errp) -{ - Omap2GpioState *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(Omap1GpioState *gpio, omap_clk clk) { gpio->clk = clk; } -static Property omap_gpio_properties[] = { +static const Property omap_gpio_properties[] = { DEFINE_PROP_INT32("mpu_model", Omap1GpioState, mpu_model, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void omap_gpio_class_init(ObjectClass *klass, void *data) +static void omap_gpio_class_init(ObjectClass *klass, const 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; @@ -771,44 +248,9 @@ static const TypeInfo omap_gpio_info = { .class_init = omap_gpio_class_init, }; -void omap2_gpio_set_iclk(Omap2GpioState *gpio, omap_clk clk) -{ - gpio->iclk = clk; -} - -void omap2_gpio_set_fclk(Omap2GpioState *gpio, uint8_t i, omap_clk clk) -{ - assert(i <= 5); - gpio->fclk[i] = clk; -} - -static Property omap2_gpio_properties[] = { - DEFINE_PROP_INT32("mpu_model", Omap2GpioState, 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(Omap2GpioState), - .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 83% rename from hw/misc/pca9552.c rename to hw/gpio/pca9552.c index fff19e369a3..1e10238b2e0 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"}; @@ -71,7 +76,7 @@ static void pca955x_display_pins_status(PCA955xState *s, return; } if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_STATUS)) { - char *buf = g_newa(char, k->pin_count + 1); + char buf[PCA955X_PIN_COUNT_MAX + 1]; for (i = 0; i < k->pin_count; i++) { if (extract32(pins_status, i, 1)) { @@ -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,15 +424,15 @@ 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[] = { +static const Property pca955x_properties[] = { DEFINE_PROP_STRING("description", PCA955xState, description), - DEFINE_PROP_END_OF_LIST(), }; -static void pca955x_class_init(ObjectClass *klass, void *data) +static void pca955x_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); @@ -411,12 +454,12 @@ static const TypeInfo pca955x_info = { .abstract = true, }; -static void pca9552_class_init(ObjectClass *oc, void *data) +static void pca9552_class_init(ObjectClass *oc, const 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 00000000000..de3f883aee9 --- /dev/null +++ b/hw/gpio/pca9554.c @@ -0,0 +1,324 @@ +/* + * 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); + + return pca9554_read(s, s->pointer & 0x3); +} + +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 const Property pca9554_properties[] = { + DEFINE_PROP_STRING("description", PCA9554State, description), +}; + +static void pca9554_class_init(ObjectClass *klass, const 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 00000000000..274b44bb616 --- /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, const 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 899be861cc5..1acca3f2f80 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), @@ -443,7 +443,6 @@ static void pl061_write(void *opaque, hwaddr offset, return; } pl061_update(s); - return; } static void pl061_enter_reset(Object *obj, ResetType type) @@ -484,7 +483,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; @@ -562,13 +561,12 @@ static void pl061_realize(DeviceState *dev, Error **errp) } } -static Property pl061_props[] = { +static const Property pl061_props[] = { DEFINE_PROP_UINT32("pullups", PL061State, pullups, 0xff), DEFINE_PROP_UINT32("pulldowns", PL061State, pulldowns, 0x0), - DEFINE_PROP_END_OF_LIST() }; -static void pl061_class_init(ObjectClass *klass, void *data) +static void pl061_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/gpio/sifive_gpio.c b/hw/gpio/sifive_gpio.c index 78bf29e996f..5831647b4d0 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), @@ -349,9 +349,8 @@ static const VMStateDescription vmstate_sifive_gpio = { } }; -static Property sifive_gpio_properties[] = { +static const Property sifive_gpio_properties[] = { DEFINE_PROP_UINT32("ngpio", SIFIVEGPIOState, ngpio, SIFIVE_GPIO_PINS), - DEFINE_PROP_END_OF_LIST(), }; static void sifive_gpio_realize(DeviceState *dev, Error **errp) @@ -371,14 +370,14 @@ static void sifive_gpio_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(DEVICE(s), s->output, s->ngpio); } -static void sifive_gpio_class_init(ObjectClass *klass, void *data) +static void sifive_gpio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 00000000000..414ce830390 --- /dev/null +++ b/hw/gpio/stm32l4x5_gpio.c @@ -0,0 +1,478 @@ +/* + * 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 const 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), +}; + +static void stm32l4x5_gpio_class_init(ObjectClass *klass, const 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 9736b362ac1..cea896b28f6 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -1,5 +1,10 @@ # See docs/devel/tracing.rst for syntax documentation. +# imx_gpio.c +imx_gpio_read(const char *id, const char *reg, uint32_t value) "%s:[%s] -> 0x%" PRIx32 +imx_gpio_write(const char *id, const char *reg, uint32_t value) "%s:[%s] <- 0x%" PRIx32 +imx_gpio_set(const char *id, int line, int level) "%s:[%d] <- %d" + # npcm7xx_gpio.c npcm7xx_gpio_read(const char *id, uint64_t offset, uint64_t value) " %s offset: 0x%04" PRIx64 " value 0x%08" PRIx64 npcm7xx_gpio_write(const char *id, uint64_t offset, uint64_t value) "%s offset: 0x%04" PRIx64 " value 0x%08" PRIx64 @@ -13,6 +18,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 +40,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 7cf52a50412..b8d27f59738 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 - -static inline void scoop_gpio_handler_update(ScoopInfo *s) { +#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) +{ 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), @@ -240,7 +243,7 @@ static const VMStateDescription vmstate_scoop_regs = { }, }; -static void scoop_sysbus_class_init(ObjectClass *klass, void *data) +static void scoop_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -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 5dd8b5b21e2..cab21045de9 100644 --- a/hw/hppa/Kconfig +++ b/hw/hppa/Kconfig @@ -1,11 +1,17 @@ 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 DIVA_GSP select ISA_BUS select I8259 select IDE_CMD646 diff --git a/hw/hppa/hppa_hardware.h b/hw/hppa/hppa_hardware.h index a5ac3dd0fd1..21c777cba65 100644 --- a/hw/hppa/hppa_hardware.h +++ b/hw/hppa/hppa_hardware.h @@ -6,6 +6,11 @@ #define FIRMWARE_START 0xf0000000 #define FIRMWARE_END 0xf0800000 +#define FIRMWARE_HIGH 0xfffffff0 /* upper 32-bits of 64-bit firmware address */ + +#define RAM_MAP_HIGH 0x0100000000 /* memory above 3.75 GB is mapped here */ + +#define MEM_PDC_ENTRY 0x4800 /* PDC entry address */ #define DEVICE_HPA_LEN 0x00100000 @@ -28,16 +33,23 @@ #define CPU_HPA 0xfffb0000 #define MEMORY_HPA 0xfffff000 -#define PCI_HPA DINO_HPA /* PCI bus */ #define IDE_HPA 0xf9000000 /* Boot disc controller */ +#define ASTRO_HPA 0xfed00000 +#define ELROY0_HPA 0xfed30000 +#define ELROY2_HPA 0xfed32000 +#define ELROY8_HPA 0xfed38000 +#define ELROYc_HPA 0xfed3c000 +#define ASTRO_MEMORY_HPA 0xfed10200 + +#define SCSI_HPA 0xf1040000 /* emulated SCSI, needs to be in f region */ /* offsets to DINO HPA: */ #define DINO_PCI_ADDR 0x064 #define DINO_CONFIG_DATA 0x068 #define DINO_IO_DATA 0x06c -#define PORT_PCI_CMD (PCI_HPA + DINO_PCI_ADDR) -#define PORT_PCI_DATA (PCI_HPA + DINO_CONFIG_DATA) +#define PORT_PCI_CMD hppa_port_pci_cmd +#define PORT_PCI_DATA hppa_port_pci_data #define FW_CFG_IO_BASE 0xfffa0000 @@ -47,7 +59,24 @@ #define HPPA_MAX_CPUS 16 /* max. number of SMP CPUs */ #define CPU_CLOCK_MHZ 250 /* emulate a 250 MHz CPU */ +#define CR_PSW_DEFAULT 6 /* used by SeaBIOS & QEMU for default PSW */ #define CPU_HPA_CR_REG 7 /* store CPU HPA in cr7 (SeaBIOS internal) */ #define PIM_STORAGE_SIZE 600 /* storage size of pdc_pim_toc_struct (64bit) */ +#define ASTRO_BUS_MODULE 0x0a /* C3700: 0x0a, others maybe 0 ? */ + +/* ASTRO Memory and I/O regions */ +#define ASTRO_BASE_HPA 0xfffed00000 +#define ELROY0_BASE_HPA 0xfffed30000 /* ELROY0_HPA */ + +#define ROPES_PER_IOC 8 /* per Ike half or Pluto/Astro */ + +#define LMMIO_DIRECT0_BASE 0x300 +#define LMMIO_DIRECT0_MASK 0x308 +#define LMMIO_DIRECT0_ROUTE 0x310 + +/* space register hashing */ +#define HPPA64_DIAG_SPHASH_ENABLE 0x200 /* DIAG_SPHASH_ENAB (bit 54) */ +#define HPPA64_PDC_CACHE_RET_SPID_VAL 0xfe0 /* PDC return value on 64-bit CPU */ + #endif diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 866e11d2080..dacedc5409c 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" @@ -9,18 +11,23 @@ #include "elf.h" #include "hw/loader.h" #include "qemu/error-report.h" -#include "sysemu/reset.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "exec/target_page.h" +#include "system/reset.h" +#include "system/system.h" +#include "system/qtest.h" +#include "system/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" @@ -29,16 +36,17 @@ #include "net/net.h" #include "qemu/log.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); @@ -81,7 +89,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; @@ -90,25 +98,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_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; @@ -118,40 +191,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)); + + 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(HPA_POWER_BUTTON); - fw_cfg_add_file(fw_cfg, "/etc/power-button-addr", - g_memdup(&val, sizeof(val)), sizeof(val)); + val = cpu_to_le64(CPU_HPA + 24); + fw_cfg_add_file(fw_cfg, "/etc/hppa/DebugOutputPort", + g_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)); + pci_bus_add_fw_cfg_extra_pci_roots(fw_cfg, pci_bus, &error_abort); + return fw_cfg; } @@ -177,173 +268,203 @@ 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) { - 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); - DeviceState *dev, *dino_dev, *lasi_dev; - PCIBus *pci_bus; - ISABus *isa_bus; - 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; + TranslateFn *translate; + MemoryRegion *cpu_region; + uint64_t ram_max; /* Create CPUs. */ - for (i = 0; i < smp_cpus; i++) { - char *name = g_strdup_printf("cpu%ld-io-eir", i); + for (unsigned int i = 0; i < smp_cpus; i++) { cpu[i] = HPPA_CPU(cpu_create(machine->cpu_type)); + } + + /* Initialize memory */ + if (hppa_is_pa20(&cpu[0]->env)) { + translate = translate_pa20; + ram_max = 256 * GiB; /* like HP rp8440 */ + } else { + translate = translate_pa10; + ram_max = FIRMWARE_START; /* 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, CPU_HPA + i * 0x1000, + memory_region_add_subregion(addr_space, + translate(NULL, CPU_HPA + i * 0x1000), cpu_region); - g_free(name); } + /* 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 > 3 * GiB) { - error_report("RAM size is currently restricted to 3GB"); - exit(EXIT_FAILURE); + if (machine->ram_size > ram_max) { + info_report("Max RAM size limited to %" PRIu64 " MB", ram_max / MiB); + machine->ram_size = ram_max; + } + if (machine->ram_size <= FIRMWARE_START) { + /* contiguous memory up to 3.75 GB RAM */ + memory_region_add_subregion_overlap(addr_space, 0, machine->ram, -1); + } else { + /* non-contiguous: Memory above 3.75 GB is mapped at RAM_MAP_HIGH */ + MemoryRegion *mem_region; + mem_region = g_new(MemoryRegion, 2); + memory_region_init_alias(&mem_region[0], &addr_space->parent_obj, + "LowMem", machine->ram, 0, FIRMWARE_START); + memory_region_init_alias(&mem_region[1], &addr_space->parent_obj, + "HighMem", machine->ram, FIRMWARE_START, + machine->ram_size - FIRMWARE_START); + memory_region_add_subregion_overlap(addr_space, 0, &mem_region[0], -1); + memory_region_add_subregion_overlap(addr_space, RAM_MAP_HIGH, + &mem_region[1], -1); } - 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]); + return translate; +} - /* fw_cfg configuration interface */ - create_fw_cfg(machine); +/* + * 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; + 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; + SysBusDevice *s; /* 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) { - vga_interface_created = true; 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); + bool disabled = object_property_get_bool(OBJECT(dev), "disable", NULL); + if (!disabled) { + sysbus_realize_and_unref(s, &error_fatal); + vga_interface_created = true; + 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, mc->default_nic, NULL); + pci_init_nic_devices(pci_bus, mc->default_nic); + + /* BMC board: HP Diva GSP */ + dev = qdev_new("diva-gsp"); + if (!object_property_get_bool(OBJECT(dev), "disable", NULL)) { + pci_dev = pci_new_multifunction(PCI_DEVFN(2, 0), "diva-gsp"); + if (!lasi_dev) { + /* bind default keyboard/serial to Diva card */ + qdev_prop_set_chr(DEVICE(pci_dev), "chardev1", serial_hd(0)); + qdev_prop_set_chr(DEVICE(pci_dev), "chardev2", serial_hd(1)); + qdev_prop_set_chr(DEVICE(pci_dev), "chardev3", serial_hd(2)); + qdev_prop_set_chr(DEVICE(pci_dev), "chardev4", serial_hd(3)); } + 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); - } - - size = load_elf(firmware_filename, NULL, NULL, NULL, - &firmware_entry, &firmware_low, &firmware_high, NULL, - true, EM_PARISC, 0, 0); + 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); + } - /* 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; + size = load_elf(firmware_filename, NULL, translate, NULL, + &firmware_entry, &firmware_low, &firmware_high, NULL, + ELFDATA2MSB, EM_PARISC, 0, 0); - 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 < FIRMWARE_START || firmware_high >= FIRMWARE_END) { - error_report("Firmware overlaps with memory or IO space"); - 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); } - 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); + ELFDATA2MSB, 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); @@ -355,8 +476,8 @@ static void machine_hppa_init(MachineState *machine) kernel_low, kernel_high, kernel_entry, size / KiB); if (kernel_cmdline) { - cpu[0]->env.gr[24] = 0x4000; - pstrcpy_targphys("cmdline", cpu[0]->env.gr[24], + cpu[0]->env.cmdline_or_bootorder = 0x4000; + pstrcpy_targphys("cmdline", cpu[0]->env.cmdline_or_bootorder, TARGET_PAGE_SIZE, kernel_cmdline); } @@ -386,40 +507,143 @@ static void machine_hppa_init(MachineState *machine) } load_image_targphys(initrd_filename, initrd_base, initrd_size); - cpu[0]->env.gr[23] = initrd_base; - cpu[0]->env.gr[22] = initrd_base + initrd_size; + cpu[0]->env.initrd_base = initrd_base; + cpu[0]->env.initrd_end = initrd_base + initrd_size; } } if (!kernel_entry) { /* When booting via firmware, tell firmware if we want interactive - * mode (kernel_entry=1), and to boot from CD (gr[24]='d') - * or hard disc * (gr[24]='c'). + * mode (kernel_entry=1), and to boot from CD (cmdline_or_bootorder='d') + * or hard disc (cmdline_or_bootorder='c'). */ kernel_entry = machine->boot_config.has_menu ? machine->boot_config.menu : 0; - cpu[0]->env.gr[24] = machine->boot_config.order[0]; + cpu[0]->env.cmdline_or_bootorder = machine->boot_config.order[0]; } - /* We jump to the firmware entry routine and pass the - * various parameters in registers. After firmware initialization, - * firmware will start the Linux kernel with ramdisk and cmdline. - */ - cpu[0]->env.gr[26] = machine->ram_size; - cpu[0]->env.gr[25] = kernel_entry; + /* Keep initial kernel_entry for first boot */ + cpu[0]->env.kernel_entry = kernel_entry; +} - /* tell firmware how many SMP CPUs to present in inventory table */ - cpu[0]->env.gr[21] = smp_cpus; +/* + * 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; - /* tell firmware fw_cfg port */ - cpu[0]->env.gr[19] = FW_CFG_IO_BASE; + /* Create CPUs and RAM. */ + translate = machine_HP_common_init_cpus(machine); + + if (hppa_is_pa20(&cpu[0]->env)) { + error_report("The HP B160L workstation requires a 32-bit " + "CPU. Use '-machine C3700' instead."); + exit(1); + } + + /* Init Lasi chip */ + lasi_dev = DEVICE(lasi_init()); + memory_region_add_subregion(addr_space, translate(NULL, LASI_HPA), + sysbus_mmio_get_region( + SYS_BUS_DEVICE(lasi_dev), 0)); + + /* Init Dino (PCI host bus chip). */ + dino_dev = DEVICE(dino_init(addr_space)); + memory_region_add_subregion(addr_space, translate(NULL, DINO_HPA), + sysbus_mmio_get_region( + SYS_BUS_DEVICE(dino_dev), 0)); + pci_bus = PCI_BUS(qdev_get_child_bus(dino_dev, "pci")); + assert(pci_bus); + + /* Create ISA bus, needed for PS/2 kbd/mouse port emulation */ + isa_bus = hppa_isa_bus(translate(NULL, IDE_HPA)); + assert(isa_bus); + + /* Serial ports: Lasi and Dino use a 7.272727 MHz clock. */ + serial_mm_init(addr_space, translate(NULL, LASI_UART_HPA + 0x800), 0, + qdev_get_gpio_in(lasi_dev, LASI_IRQ_UART_HPA), 7272727 / 16, + serial_hd(0), DEVICE_BIG_ENDIAN); + + serial_mm_init(addr_space, translate(NULL, DINO_UART_HPA + 0x800), 0, + qdev_get_gpio_in(dino_dev, DINO_IRQ_RS232INT), 7272727 / 16, + serial_hd(1), DEVICE_BIG_ENDIAN); + + /* Parallel port */ + parallel_mm_init(addr_space, translate(NULL, LASI_LPT_HPA + 0x800), 0, + qdev_get_gpio_in(lasi_dev, LASI_IRQ_LAN_HPA), + parallel_hds[0]); + + /* PS/2 Keyboard/Mouse */ + dev = qdev_new(TYPE_LASIPS2); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, + qdev_get_gpio_in(lasi_dev, LASI_IRQ_PS2KBD_HPA)); + memory_region_add_subregion(addr_space, + translate(NULL, LASI_PS2KBD_HPA), + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), + 0)); + memory_region_add_subregion(addr_space, + translate(NULL, LASI_PS2KBD_HPA + 0x100), + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), + 1)); + + /* Add SCSI discs, NICs, graphics & load firmware */ + machine_HP_common_init_tail(machine, pci_bus, translate); +} + +static AstroState *astro_init(void) +{ + DeviceState *dev; + + dev = qdev_new(TYPE_ASTRO_CHIP); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + return ASTRO_CHIP(dev); +} + +/* + * Create HP C3700 workstation + */ +static void machine_HP_C3700_init(MachineState *machine) +{ + PCIBus *pci_bus; + AstroState *astro; + DeviceState *astro_dev; + MemoryRegion *addr_space = get_system_memory(); + TranslateFn *translate; + + /* Create CPUs and RAM. */ + translate = machine_HP_common_init_cpus(machine); + + if (!hppa_is_pa20(&cpu[0]->env)) { + error_report("The HP C3000 workstation requires a 64-bit CPU. " + "Use '-machine B160L' instead."); + exit(1); + } + + /* Init Astro and the Elroys (PCI host bus chips). */ + astro = astro_init(); + astro_dev = DEVICE(astro); + memory_region_add_subregion(addr_space, translate(NULL, ASTRO_HPA), + sysbus_mmio_get_region( + SYS_BUS_DEVICE(astro_dev), 0)); + pci_bus = PCI_BUS(qdev_get_child_bus(DEVICE(astro->elroy[0]), "pci")); + assert(pci_bus); + + /* Add SCSI discs, NICs, graphics & load firmware */ + machine_HP_common_init_tail(machine, pci_bus, translate); } -static void hppa_machine_reset(MachineState *ms, ShutdownCause reason) +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 @@ -427,26 +651,27 @@ static void hppa_machine_reset(MachineState *ms, ShutdownCause reason) for (i = 0; i < smp_cpus; i++) { CPUState *cs = CPU(cpu[i]); + /* reset CPU */ + resettable_reset(OBJECT(cs), RESET_TYPE_COLD); + cpu_set_pc(cs, firmware_entry); cpu[i]->env.psw = PSW_Q; cpu[i]->env.gr[5] = CPU_HPA + i * 0x1000; - - cs->exception_index = -1; - cs->halted = 0; - } - - /* already initialized by machine_hppa_init()? */ - if (cpu[0]->env.gr[26] == ms->ram_size) { - return; } cpu[0]->env.gr[26] = ms->ram_size; - cpu[0]->env.gr[25] = 0; /* no firmware boot menu */ - cpu[0]->env.gr[24] = 'c'; - /* gr22/gr23 unused, no initrd while reboot. */ + cpu[0]->env.gr[25] = cpu[0]->env.kernel_entry; + cpu[0]->env.gr[24] = cpu[0]->env.cmdline_or_bootorder; + cpu[0]->env.gr[23] = cpu[0]->env.initrd_base; + cpu[0]->env.gr[22] = cpu[0]->env.initrd_end; cpu[0]->env.gr[21] = smp_cpus; - /* tell firmware fw_cfg port */ cpu[0]->env.gr[19] = FW_CFG_IO_BASE; + + /* reset static fields to avoid starting Linux kernel & initrd on reboot */ + cpu[0]->env.kernel_entry = 0; + cpu[0]->env.initrd_base = 0; + cpu[0]->env.initrd_end = 0; + cpu[0]->env.cmdline_or_bootorder = 'c'; } static void hppa_nmi(NMIState *n, int cpu_index, Error **errp) @@ -458,14 +683,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, const 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; @@ -479,11 +709,47 @@ static void hppa_machine_init_class_init(ObjectClass *oc, void *data) 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 = HP_B160L_machine_init_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_NMI }, + { } + }, +}; + +static void HP_C3700_machine_init_class_init(ObjectClass *oc, const 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 = hppa_machine_init_class_init, - .interfaces = (InterfaceInfo[]) { + .class_init = HP_C3700_machine_init_class_init, + .interfaces = (const InterfaceInfo[]) { { TYPE_NMI }, { } }, @@ -491,7 +757,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 fcf65903bd0..41dd827c841 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 00000000000..ee53a28a266 --- /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 00000000000..1505a395cf7 --- /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 00000000000..b1f19d77daf --- /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 "system/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 00000000000..dfb14852f42 --- /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 00000000000..333772b86d2 --- /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 00000000000..a47412d4a8a --- /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 00000000000..6dbcb2d9a29 --- /dev/null +++ b/hw/hyperv/hv-balloon.c @@ -0,0 +1,1768 @@ +/* + * 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 "system/address-spaces.h" +#include "exec/cpu-common.h" +#include "system/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 "qobject/qdict.h" +#include "qapi/visitor.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "qemu/timer.h" +#include "system/balloon.h" +#include "system/hostmem.h" +#include "system/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 + */ + +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_SIMPLE_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 const 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), +}; + +static void hv_balloon_class_init(ObjectClass *klass, const 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 57b402b9561..e4d0688dbfa 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -11,8 +11,11 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "qapi/error.h" -#include "exec/address-spaces.h" -#include "sysemu/kvm.h" +#include "system/address-spaces.h" +#include "system/memory.h" +#include "exec/target_page.h" +#include "linux/kvm.h" +#include "system/kvm.h" #include "qemu/bitops.h" #include "qemu/error-report.h" #include "qemu/lockable.h" @@ -21,6 +24,8 @@ #include "qemu/rcu_queue.h" #include "hw/hyperv/hyperv.h" #include "qom/object.h" +#include "target/i386/kvm/hyperv-proto.h" +#include "exec/target_page.h" struct SynICState { DeviceState parent_obj; @@ -129,12 +134,12 @@ static void synic_reset(DeviceState *dev) assert(QLIST_EMPTY(&synic->sint_routes)); } -static void synic_class_init(ObjectClass *klass, void *data) +static void synic_class_init(ObjectClass *klass, const 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 +374,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 +977,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 9a56ddf83fe..2d4a63693b8 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) @@ -304,7 +303,7 @@ static void hv_test_dev_realizefn(DeviceState *d, Error **errp) memory_region_add_subregion(io, 0x3000, &dev->sint_control); } -static void hv_test_dev_class_init(ObjectClass *klass, void *data) +static void hv_test_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/hyperv/meson.build b/hw/hyperv/meson.build index b43f119ea56..d1cf781f049 100644 --- a/hw/hyperv/meson.build +++ b/hw/hyperv/meson.build @@ -1,4 +1,6 @@ -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')) +system_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c')) +system_ss.add(when: 'CONFIG_HYPERV_TESTDEV', if_true: files('hyperv_testdev.c')) +system_ss.add(when: 'CONFIG_VMBUS', if_true: files('vmbus.c')) +system_ss.add(when: 'CONFIG_SYNDBG', if_true: files('syndbg.c')) +system_ss.add(when: 'CONFIG_HV_BALLOON', if_true: files('hv-balloon.c', 'hv-balloon-page_range_tree.c', 'hv-balloon-our_range_memslots.c')) +system_ss.add(when: 'CONFIG_HV_BALLOON', if_false: files('hv-balloon-stub.c')) diff --git a/hw/hyperv/syndbg.c b/hw/hyperv/syndbg.c index 065e12fb1ed..ac7e15f6f1d 100644 --- a/hw/hyperv/syndbg.c +++ b/hw/hyperv/syndbg.c @@ -10,11 +10,12 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/sockets.h" +#include "qemu/units.h" #include "qapi/error.h" #include "migration/vmstate.h" #include "hw/qdev-properties.h" #include "hw/loader.h" -#include "cpu.h" +#include "exec/target_page.h" #include "hw/hyperv/hyperv.h" #include "hw/hyperv/vmbus-bridge.h" #include "hw/hyperv/hyperv-proto.h" @@ -183,12 +184,15 @@ static bool create_udp_pkt(HvSynDbg *syndbg, void *pkt, uint32_t pkt_len, return true; } +#define MSG_BUFSZ (4 * KiB) + static uint16_t handle_recv_msg(HvSynDbg *syndbg, uint64_t outgpa, uint32_t count, bool is_raw, uint32_t options, uint64_t timeout, uint32_t *retrieved_count) { uint16_t ret; - uint8_t data_buf[TARGET_PAGE_SIZE - UDP_PKT_HEADER_SIZE]; + g_assert(MSG_BUFSZ >= qemu_target_page_size()); + QEMU_UNINITIALIZED uint8_t data_buf[MSG_BUFSZ]; hwaddr out_len; void *out_data; ssize_t recv_byte_count; @@ -201,7 +205,7 @@ static uint16_t handle_recv_msg(HvSynDbg *syndbg, uint64_t outgpa, recv_byte_count = 0; } else { recv_byte_count = recv(syndbg->socket, data_buf, - MIN(sizeof(data_buf), count), MSG_WAITALL); + MIN(MSG_BUFSZ, count), MSG_WAITALL); if (recv_byte_count == -1) { return HV_STATUS_INVALID_PARAMETER; } @@ -366,14 +370,13 @@ static const VMStateDescription vmstate_hv_syndbg = { .unmigratable = 1, }; -static Property hv_syndbg_properties[] = { +static const Property hv_syndbg_properties[] = { DEFINE_PROP_STRING("host_ip", HvSynDbg, host_ip), DEFINE_PROP_UINT16("host_port", HvSynDbg, host_port, 50000), DEFINE_PROP_BOOL("use_hcalls", HvSynDbg, use_hcalls, false), - DEFINE_PROP_END_OF_LIST(), }; -static void hv_syndbg_class_init(ObjectClass *klass, void *data) +static void hv_syndbg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/hyperv/trace-events b/hw/hyperv/trace-events index b4c35ca8e37..7963c215b1c 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 271289f902f..961406cdd6a 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +#include "exec/target_page.h" #include "qapi/error.h" #include "migration/vmstate.h" #include "hw/qdev-properties.h" @@ -18,7 +19,7 @@ #include "hw/hyperv/vmbus.h" #include "hw/hyperv/vmbus-bridge.h" #include "hw/sysbus.h" -#include "cpu.h" +#include "exec/target_page.h" #include "trace.h" enum { @@ -526,7 +527,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 +1490,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), @@ -1874,7 +1875,7 @@ static void send_create_gpadl(VMBus *vmbus) } } - assert(false); + g_assert_not_reached(); } static bool complete_create_gpadl(VMBus *vmbus) @@ -1889,8 +1890,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 +1931,7 @@ static void send_teardown_gpadl(VMBus *vmbus) } } - assert(false); + g_assert_not_reached(); } static bool complete_teardown_gpadl(VMBus *vmbus) @@ -1946,8 +1946,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 +1995,7 @@ static void send_open_channel(VMBus *vmbus) } } - assert(false); + g_assert_not_reached(); } static bool complete_open_channel(VMBus *vmbus) @@ -2020,8 +2019,7 @@ static bool complete_open_channel(VMBus *vmbus) } } - assert(false); - return false; + g_assert_not_reached(); } static void vdev_reset_on_close(VMBusDevice *vdev) @@ -2076,7 +2074,6 @@ static void send_unload(VMBus *vmbus) qemu_mutex_unlock(&vmbus->rx_queue_lock); post_msg(vmbus, &msg, sizeof(msg)); - return; } static bool complete_unload(VMBus *vmbus) @@ -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)); @@ -2349,20 +2346,19 @@ static void vmbus_dev_unrealize(DeviceState *dev) free_channels(vdev); } -static Property vmbus_dev_props[] = { +static const Property vmbus_dev_props[] = { DEFINE_PROP_UUID("instanceid", VMBusDevice, instanceid), - DEFINE_PROP_END_OF_LIST() }; -static void vmbus_dev_class_init(ObjectClass *klass, void *data) +static void vmbus_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *kdev = DEVICE_CLASS(klass); device_class_set_props(kdev, vmbus_dev_props); 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 +2376,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, @@ -2453,9 +2449,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) @@ -2467,21 +2463,22 @@ 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); } -static void vmbus_class_init(ObjectClass *klass, void *data) +static void vmbus_class_init(ObjectClass *klass, const 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) @@ -2549,7 +2546,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 @@ -2572,7 +2569,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, @@ -2589,7 +2586,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), @@ -2598,7 +2595,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 } @@ -2630,6 +2627,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")); } @@ -2643,18 +2646,17 @@ 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() }, }; -static Property vmbus_bridge_props[] = { +static const Property vmbus_bridge_props[] = { DEFINE_PROP_UINT8("irq", VMBusBridge, irq, 7), - DEFINE_PROP_END_OF_LIST() }; -static void vmbus_bridge_class_init(ObjectClass *klass, void *data) +static void vmbus_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); SysBusDeviceClass *sk = SYS_BUS_DEVICE_CLASS(klass); diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig index 14886b35dac..596a7a3165a 100644 --- a/hw/i2c/Kconfig +++ b/hw/i2c/Kconfig @@ -45,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 index 9e8efa1d63f..fe887e1c6a9 100644 --- a/hw/i2c/allwinner-i2c.c +++ b/hw/i2c/allwinner-i2c.c @@ -170,7 +170,7 @@ 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) +static void allwinner_i2c_reset_hold(Object *obj, ResetType type) { AWI2CState *s = AW_I2C(obj); @@ -385,8 +385,7 @@ static void allwinner_i2c_write(void *opaque, hwaddr offset, break; case TWI_SRST_REG: if (((value & TWI_SRST_MASK) == 0) && (s->srst & TWI_SRST_MASK)) { - /* Perform reset */ - allwinner_i2c_reset_hold(OBJECT(s)); + device_cold_reset(DEVICE(s)); } s->srst = value & TWI_SRST_MASK; break; @@ -408,14 +407,14 @@ static const MemoryRegionOps allwinner_i2c_ops = { .write = allwinner_i2c_write, .valid.min_access_size = 1, .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static const VMStateDescription allwinner_i2c_vmstate = { .name = TYPE_AW_I2C, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(addr, AWI2CState), VMSTATE_UINT8(xaddr, AWI2CState), VMSTATE_UINT8(data, AWI2CState), @@ -439,7 +438,7 @@ static void allwinner_i2c_realize(DeviceState *dev, Error **errp) s->bus = i2c_init_bus(dev, "i2c"); } -static void allwinner_i2c_class_init(ObjectClass *klass, void *data) +static void allwinner_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index 7275d40749a..83fb906bdc7 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,18 +225,18 @@ 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; } @@ -291,7 +306,6 @@ 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_SIZE) + 1; @@ -312,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); @@ -324,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)) { @@ -637,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; @@ -722,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); @@ -812,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: @@ -907,7 +978,7 @@ 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; @@ -915,26 +986,61 @@ static uint64_t aspeed_i2c_pool_read(void *opaque, hwaddr offset, int i; for (i = 0; i < size; i++) { - ret |= (uint64_t) s->pool[offset + i] << (8 * i); + ret |= (uint64_t) s->share_pool[offset + i] << (8 * i); } return ret; } -static void aspeed_i2c_pool_write(void *opaque, hwaddr offset, +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); + } + + return ret; +} + +static void aspeed_i2c_bus_pool_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + AspeedI2CBus *s = opaque; + int i; + for (i = 0; i < size; i++) { s->pool[offset + i] = (value >> (8 * i)) & 0xFF; } } -static const MemoryRegionOps aspeed_i2c_pool_ops = { - .read = aspeed_i2c_pool_read, - .write = aspeed_i2c_pool_write, +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, @@ -944,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() } }; @@ -996,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 @@ -1004,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) { @@ -1012,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++) { @@ -1034,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) { @@ -1053,18 +1258,17 @@ static void aspeed_i2c_realize(DeviceState *dev, Error **errp) } } -static Property aspeed_i2c_properties[] = { +static const Property aspeed_i2c_properties[] = { DEFINE_PROP_LINK("dram", AspeedI2CState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; -static void aspeed_i2c_class_init(ObjectClass *klass, void *data) +static void aspeed_i2c_class_init(ObjectClass *klass, const 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"; @@ -1091,8 +1295,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); @@ -1158,10 +1363,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); @@ -1185,7 +1390,8 @@ static void aspeed_i2c_bus_slave_send_async(I2CSlave *slave, uint8_t data) aspeed_i2c_bus_raise_interrupt(bus); } -static void aspeed_i2c_bus_slave_class_init(ObjectClass *klass, void *data) +static void aspeed_i2c_bus_slave_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); @@ -1216,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"); @@ -1233,22 +1440,25 @@ 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[] = { +static const Property aspeed_i2c_bus_properties[] = { DEFINE_PROP_UINT8("bus-id", AspeedI2CBus, id, 0), DEFINE_PROP_LINK("controller", AspeedI2CBus, controller, TYPE_ASPEED_I2C, AspeedI2CState *), - DEFINE_PROP_END_OF_LIST(), }; -static void aspeed_i2c_bus_class_init(ObjectClass *klass, void *data) +static void aspeed_i2c_bus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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); } @@ -1267,13 +1477,14 @@ 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)]; } -static void aspeed_2400_i2c_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); @@ -1284,9 +1495,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 = { @@ -1302,10 +1515,10 @@ 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) +static void aspeed_2500_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); @@ -1316,11 +1529,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 = { @@ -1334,12 +1548,7 @@ 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) +static void aspeed_2600_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); @@ -1350,10 +1559,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 = { @@ -1362,7 +1572,7 @@ static const TypeInfo aspeed_2600_i2c_info = { .class_init = aspeed_2600_i2c_class_init, }; -static void aspeed_1030_i2c_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); @@ -1373,10 +1583,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 = { @@ -1385,6 +1596,33 @@ static const TypeInfo aspeed_1030_i2c_info = { .class_init = aspeed_1030_i2c_class_init, }; +static void aspeed_2700_i2c_class_init(ObjectClass *klass, const 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); @@ -1394,6 +1632,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 00000000000..be11cca2a93 --- /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, const 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 de5f5aacf54..e020f314e2a 100644 --- a/hw/i2c/bitbang_i2c.c +++ b/hw/i2c/bitbang_i2c.c @@ -222,7 +222,7 @@ static void gpio_i2c_init(Object *obj) qdev_init_gpio_out(dev, &s->out, 1); } -static void gpio_i2c_class_init(ObjectClass *klass, void *data) +static void gpio_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i2c/core.c b/hw/i2c/core.c index bed594fe599..4b6345b5889 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -18,9 +18,8 @@ #define I2C_BROADCAST 0x00 -static Property i2c_props[] = { +static const Property i2c_props[] = { DEFINE_PROP_UINT8("address", struct I2CSlave, address, 0), - DEFINE_PROP_END_OF_LIST(), }; static const TypeInfo i2c_bus_info = { @@ -50,7 +49,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 +63,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; } @@ -359,7 +358,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() } @@ -402,7 +401,7 @@ static bool i2c_slave_match(I2CSlave *candidate, uint8_t address, return false; } -static void i2c_slave_class_init(ObjectClass *klass, void *data) +static void i2c_slave_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); diff --git a/hw/i2c/exynos4210_i2c.c b/hw/i2c/exynos4210_i2c.c index b65a7d0222e..9d0c1cdaa8a 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), @@ -309,12 +309,12 @@ static void exynos4210_i2c_init(Object *obj) s->bus = i2c_init_bus(dev, "i2c"); } -static void exynos4210_i2c_class_init(ObjectClass *klass, void *data) +static void exynos4210_i2c_class_init(ObjectClass *klass, const 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 db5db956a6b..a8ef640cd25 100644 --- a/hw/i2c/i2c_mux_pca954x.c +++ b/hw/i2c/i2c_mux_pca954x.c @@ -172,13 +172,13 @@ I2CBus *pca954x_i2c_get_bus(I2CSlave *mux, uint8_t channel) return pca954x->bus[channel]; } -static void pca9546_class_init(ObjectClass *klass, void *data) +static void pca9546_class_init(ObjectClass *klass, const void *data) { Pca954xClass *s = PCA954X_CLASS(klass); s->nchans = PCA9546_CHANNEL_COUNT; } -static void pca9548_class_init(ObjectClass *klass, void *data) +static void pca9548_class_init(ObjectClass *klass, const void *data) { Pca954xClass *s = PCA954X_CLASS(klass); s->nchans = PCA9548_CHANNEL_COUNT; @@ -211,12 +211,11 @@ static void pca954x_init(Object *obj) } } -static Property pca954x_props[] = { +static const Property pca954x_props[] = { DEFINE_PROP_STRING("name", Pca954xState, name), - DEFINE_PROP_END_OF_LIST() }; -static void pca954x_class_init(ObjectClass *klass, void *data) +static void pca954x_class_init(ObjectClass *klass, const void *data) { I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c index 9792583fea7..d26177c85df 100644 --- a/hw/i2c/imx_i2c.c +++ b/hw/i2c/imx_i2c.c @@ -25,18 +25,7 @@ #include "hw/i2c/i2c.h" #include "qemu/log.h" #include "qemu/module.h" - -#ifndef DEBUG_IMX_I2C -#define DEBUG_IMX_I2C 0 -#endif - -#define DPRINTF(fmt, args...) \ - do { \ - if (DEBUG_IMX_I2C) { \ - fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_I2C, \ - __func__, ##args); \ - } \ - } while (0) +#include "trace.h" static const char *imx_i2c_get_regname(unsigned offset) { @@ -90,13 +79,12 @@ static void imx_i2c_reset(DeviceState *dev) static inline void imx_i2c_raise_interrupt(IMXI2CState *s) { - /* - * raise an interrupt if the device is enabled and it is configured - * to generate some interrupts. - */ - if (imx_i2c_is_enabled(s) && imx_i2c_interrupt_is_enabled(s)) { + if (imx_i2c_is_enabled(s)) { s->i2sr |= I2SR_IIF; - qemu_irq_raise(s->irq); + + if (imx_i2c_interrupt_is_enabled(s)) { + qemu_irq_raise(s->irq); + } } } @@ -152,8 +140,8 @@ static uint64_t imx_i2c_read(void *opaque, hwaddr offset, break; } - DPRINTF("read %s [0x%" HWADDR_PRIx "] -> 0x%02x\n", - imx_i2c_get_regname(offset), offset, value); + trace_imx_i2c_read(DEVICE(s)->canonical_path, imx_i2c_get_regname(offset), + offset, value); return (uint64_t)value; } @@ -163,8 +151,8 @@ static void imx_i2c_write(void *opaque, hwaddr offset, { IMXI2CState *s = IMX_I2C(opaque); - DPRINTF("write %s [0x%" HWADDR_PRIx "] <- 0x%02x\n", - imx_i2c_get_regname(offset), offset, (int)value); + trace_imx_i2c_read(DEVICE(s)->canonical_path, imx_i2c_get_regname(offset), + offset, value); value &= 0xff; @@ -285,7 +273,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), @@ -308,12 +296,12 @@ static void imx_i2c_realize(DeviceState *dev, Error **errp) s->bus = i2c_init_bus(dev, NULL); } -static void imx_i2c_class_init(ObjectClass *klass, void *data) +static void imx_i2c_class_init(ObjectClass *klass, const 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 b58bc167dbc..c459adcb596 100644 --- a/hw/i2c/meson.build +++ b/hw/i2c/meson.build @@ -17,4 +17,5 @@ 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')) +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 e92f9f84ea8..2291d6370e2 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() @@ -105,12 +105,12 @@ static void microbit_i2c_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); } -static void microbit_i2c_class_init(ObjectClass *klass, void *data) +static void microbit_i2c_class_init(ObjectClass *klass, const 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 219c5484028..25f91b7bc82 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 " HWADDR_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 " HWADDR_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,31 +329,28 @@ 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"); } -static void mpc_i2c_class_init(ObjectClass *klass, void *data) +static void mpc_i2c_class_init(ObjectClass *klass, const 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 e7e0ba66fe7..179852a4fd9 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), @@ -1075,7 +1075,7 @@ static const VMStateDescription vmstate_npcm7xx_smbus = { }, }; -static void npcm7xx_smbus_class_init(ObjectClass *klass, void *data) +static void npcm7xx_smbus_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i2c/omap_i2c.c b/hw/i2c/omap_i2c.c index e5d205dda5a..751bf748fd9 100644 --- a/hw/i2c/omap_i2c.c +++ b/hw/i2c/omap_i2c.c @@ -55,16 +55,16 @@ struct OMAPI2CState { uint16_t test; }; -#define OMAP2_INTR_REV 0x34 -#define OMAP2_GC_REV 0x34 +#define OMAP2_INTR_REV 0x34 +#define OMAP2_GC_REV 0x34 static void omap_i2c_interrupts_update(OMAPI2CState *s) { qemu_set_irq(s->irq, s->stat & s->mask); - if ((s->dma >> 15) & 1) /* RDMA_EN */ - qemu_set_irq(s->drq[0], (s->stat >> 3) & 1); /* RRDY */ - if ((s->dma >> 7) & 1) /* XDMA_EN */ - qemu_set_irq(s->drq[1], (s->stat >> 4) & 1); /* XRDY */ + if ((s->dma >> 15) & 1) /* RDMA_EN */ + qemu_set_irq(s->drq[0], (s->stat >> 3) & 1); /* RRDY */ + if ((s->dma >> 7) & 1) /* XDMA_EN */ + qemu_set_irq(s->drq[1], (s->stat >> 4) & 1); /* XRDY */ } static void omap_i2c_fifo_run(OMAPI2CState *s) @@ -74,25 +74,25 @@ static void omap_i2c_fifo_run(OMAPI2CState *s) if (!i2c_bus_busy(s->bus)) return; - if ((s->control >> 2) & 1) { /* RM */ - if ((s->control >> 1) & 1) { /* STP */ + if ((s->control >> 2) & 1) { /* RM */ + if ((s->control >> 1) & 1) { /* STP */ i2c_end_transfer(s->bus); - s->control &= ~(1 << 1); /* STP */ + s->control &= ~(1 << 1); /* STP */ s->count_cur = s->count; s->txlen = 0; - } else if ((s->control >> 9) & 1) { /* TRX */ + } else if ((s->control >> 9) & 1) { /* TRX */ while (ack && s->txlen) ack = (i2c_send(s->bus, (s->fifo >> ((-- s->txlen) << 3)) & 0xff) >= 0); - s->stat |= 1 << 4; /* XRDY */ + s->stat |= 1 << 4; /* XRDY */ } else { while (s->rxlen < 4) s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3); - s->stat |= 1 << 3; /* RRDY */ + s->stat |= 1 << 3; /* RRDY */ } } else { - if ((s->control >> 9) & 1) { /* TRX */ + if ((s->control >> 9) & 1) { /* TRX */ while (ack && s->count_cur && s->txlen) { ack = (i2c_send(s->bus, (s->fifo >> ((-- s->txlen) << 3)) & @@ -100,12 +100,12 @@ static void omap_i2c_fifo_run(OMAPI2CState *s) s->count_cur --; } if (ack && s->count_cur) - s->stat |= 1 << 4; /* XRDY */ + s->stat |= 1 << 4; /* XRDY */ else - s->stat &= ~(1 << 4); /* XRDY */ + s->stat &= ~(1 << 4); /* XRDY */ if (!s->count_cur) { - s->stat |= 1 << 2; /* ARDY */ - s->control &= ~(1 << 10); /* MST */ + s->stat |= 1 << 2; /* ARDY */ + s->control &= ~(1 << 10); /* MST */ } } else { while (s->count_cur && s->rxlen < 4) { @@ -113,26 +113,26 @@ static void omap_i2c_fifo_run(OMAPI2CState *s) s->count_cur --; } if (s->rxlen) - s->stat |= 1 << 3; /* RRDY */ + s->stat |= 1 << 3; /* RRDY */ else - s->stat &= ~(1 << 3); /* RRDY */ + s->stat &= ~(1 << 3); /* RRDY */ } if (!s->count_cur) { - if ((s->control >> 1) & 1) { /* STP */ + if ((s->control >> 1) & 1) { /* STP */ i2c_end_transfer(s->bus); - s->control &= ~(1 << 1); /* STP */ + s->control &= ~(1 << 1); /* STP */ s->count_cur = s->count; s->txlen = 0; } else { - s->stat |= 1 << 2; /* ARDY */ - s->control &= ~(1 << 10); /* MST */ + s->stat |= 1 << 2; /* ARDY */ + s->control &= ~(1 << 10); /* MST */ } } } - s->stat |= (!ack) << 1; /* NACK */ + s->stat |= (!ack) << 1; /* NACK */ if (!ack) - s->control &= ~(1 << 1); /* STP */ + s->control &= ~(1 << 1); /* STP */ } static void omap_i2c_reset(DeviceState *dev) @@ -163,16 +163,16 @@ static uint32_t omap_i2c_read(void *opaque, hwaddr addr) uint16_t ret; switch (offset) { - case 0x00: /* I2C_REV */ - return s->revision; /* REV */ + case 0x00: /* I2C_REV */ + return s->revision; /* REV */ - case 0x04: /* I2C_IE */ + case 0x04: /* I2C_IE */ return s->mask; - case 0x08: /* I2C_STAT */ + case 0x08: /* I2C_STAT */ return s->stat | (i2c_bus_busy(s->bus) << 12); - case 0x0c: /* I2C_IV */ + case 0x0c: /* I2C_IV */ if (s->revision >= OMAP2_INTR_REV) break; ret = ctz32(s->stat & s->mask); @@ -185,18 +185,18 @@ static uint32_t omap_i2c_read(void *opaque, hwaddr addr) omap_i2c_interrupts_update(s); return ret; - case 0x10: /* I2C_SYSS */ - return (s->control >> 15) & 1; /* I2C_EN */ + case 0x10: /* I2C_SYSS */ + return (s->control >> 15) & 1; /* I2C_EN */ - case 0x14: /* I2C_BUF */ + case 0x14: /* I2C_BUF */ return s->dma; - case 0x18: /* I2C_CNT */ - return s->count_cur; /* DCOUNT */ + case 0x18: /* I2C_CNT */ + return s->count_cur; /* DCOUNT */ - case 0x1c: /* I2C_DATA */ + case 0x1c: /* I2C_DATA */ ret = 0; - if (s->control & (1 << 14)) { /* BE */ + if (s->control & (1 << 14)) { /* BE */ ret |= ((s->fifo >> 0) & 0xff) << 8; ret |= ((s->fifo >> 8) & 0xff) << 0; } else { @@ -204,7 +204,7 @@ static uint32_t omap_i2c_read(void *opaque, hwaddr addr) ret |= ((s->fifo >> 0) & 0xff) << 0; } if (s->rxlen == 1) { - s->stat |= 1 << 15; /* SBD */ + s->stat |= 1 << 15; /* SBD */ s->rxlen = 0; } else if (s->rxlen > 1) { if (s->rxlen > 2) @@ -214,41 +214,41 @@ static uint32_t omap_i2c_read(void *opaque, hwaddr addr) /* XXX: remote access (qualifier) error - what's that? */ } if (!s->rxlen) { - s->stat &= ~(1 << 3); /* RRDY */ - if (((s->control >> 10) & 1) && /* MST */ - ((~s->control >> 9) & 1)) { /* TRX */ - s->stat |= 1 << 2; /* ARDY */ - s->control &= ~(1 << 10); /* MST */ + s->stat &= ~(1 << 3); /* RRDY */ + if (((s->control >> 10) & 1) && /* MST */ + ((~s->control >> 9) & 1)) { /* TRX */ + s->stat |= 1 << 2; /* ARDY */ + s->control &= ~(1 << 10); /* MST */ } } - s->stat &= ~(1 << 11); /* ROVR */ + s->stat &= ~(1 << 11); /* ROVR */ omap_i2c_fifo_run(s); omap_i2c_interrupts_update(s); return ret; - case 0x20: /* I2C_SYSC */ + case 0x20: /* I2C_SYSC */ return 0; - case 0x24: /* I2C_CON */ + case 0x24: /* I2C_CON */ return s->control; - case 0x28: /* I2C_OA */ + case 0x28: /* I2C_OA */ return s->addr[0]; - case 0x2c: /* I2C_SA */ + case 0x2c: /* I2C_SA */ return s->addr[1]; - case 0x30: /* I2C_PSC */ + case 0x30: /* I2C_PSC */ return s->divider; - case 0x34: /* I2C_SCLL */ + case 0x34: /* I2C_SCLL */ return s->times[0]; - case 0x38: /* I2C_SCLH */ + case 0x38: /* I2C_SCLH */ return s->times[1]; - case 0x3c: /* I2C_SYSTEST */ - if (s->test & (1 << 15)) { /* ST_EN */ + case 0x3c: /* I2C_SYSTEST */ + if (s->test & (1 << 15)) { /* ST_EN */ s->test ^= 0xa; return s->test; } else @@ -267,17 +267,17 @@ static void omap_i2c_write(void *opaque, hwaddr addr, int nack; switch (offset) { - case 0x00: /* I2C_REV */ - case 0x0c: /* I2C_IV */ - case 0x10: /* I2C_SYSS */ + case 0x00: /* I2C_REV */ + case 0x0c: /* I2C_IV */ + case 0x10: /* I2C_SYSS */ OMAP_RO_REG(addr); return; - case 0x04: /* I2C_IE */ + case 0x04: /* I2C_IE */ s->mask = value & (s->revision < OMAP2_GC_REV ? 0x1f : 0x3f); break; - case 0x08: /* I2C_STAT */ + case 0x08: /* I2C_STAT */ if (s->revision < OMAP2_INTR_REV) { OMAP_RO_REG(addr); return; @@ -288,40 +288,40 @@ static void omap_i2c_write(void *opaque, hwaddr addr, omap_i2c_interrupts_update(s); break; - case 0x14: /* I2C_BUF */ + case 0x14: /* I2C_BUF */ s->dma = value & 0x8080; - if (value & (1 << 15)) /* RDMA_EN */ - s->mask &= ~(1 << 3); /* RRDY_IE */ - if (value & (1 << 7)) /* XDMA_EN */ - s->mask &= ~(1 << 4); /* XRDY_IE */ + if (value & (1 << 15)) /* RDMA_EN */ + s->mask &= ~(1 << 3); /* RRDY_IE */ + if (value & (1 << 7)) /* XDMA_EN */ + s->mask &= ~(1 << 4); /* XRDY_IE */ break; - case 0x18: /* I2C_CNT */ - s->count = value; /* DCOUNT */ + case 0x18: /* I2C_CNT */ + s->count = value; /* DCOUNT */ break; - case 0x1c: /* I2C_DATA */ + case 0x1c: /* I2C_DATA */ if (s->txlen > 2) { /* XXX: remote access (qualifier) error - what's that? */ break; } s->fifo <<= 16; s->txlen += 2; - if (s->control & (1 << 14)) { /* BE */ + if (s->control & (1 << 14)) { /* BE */ s->fifo |= ((value >> 8) & 0xff) << 8; s->fifo |= ((value >> 0) & 0xff) << 0; } else { s->fifo |= ((value >> 0) & 0xff) << 8; s->fifo |= ((value >> 8) & 0xff) << 0; } - s->stat &= ~(1 << 10); /* XUDF */ + s->stat &= ~(1 << 10); /* XUDF */ if (s->txlen > 2) - s->stat &= ~(1 << 4); /* XRDY */ + s->stat &= ~(1 << 4); /* XRDY */ omap_i2c_fifo_run(s); omap_i2c_interrupts_update(s); break; - case 0x20: /* I2C_SYSC */ + case 0x20: /* I2C_SYSC */ if (s->revision < OMAP2_INTR_REV) { OMAP_BAD_REG(addr); return; @@ -332,9 +332,9 @@ static void omap_i2c_write(void *opaque, hwaddr addr, } break; - case 0x24: /* I2C_CON */ + case 0x24: /* I2C_CON */ s->control = value & 0xcf87; - if (~value & (1 << 15)) { /* I2C_EN */ + if (~value & (1 << 15)) { /* I2C_EN */ if (s->revision < OMAP2_INTR_REV) { omap_i2c_reset(DEVICE(s)); } @@ -351,14 +351,14 @@ static void omap_i2c_write(void *opaque, hwaddr addr, __func__); break; } - if ((value & (1 << 15)) && value & (1 << 0)) { /* STT */ - nack = !!i2c_start_transfer(s->bus, s->addr[1], /* SA */ - (~value >> 9) & 1); /* TRX */ - s->stat |= nack << 1; /* NACK */ - s->control &= ~(1 << 0); /* STT */ + if ((value & (1 << 15)) && value & (1 << 0)) { /* STT */ + nack = !!i2c_start_transfer(s->bus, s->addr[1], /* SA */ + (~value >> 9) & 1); /* TRX */ + s->stat |= nack << 1; /* NACK */ + s->control &= ~(1 << 0); /* STT */ s->fifo = 0; if (nack) - s->control &= ~(1 << 1); /* STP */ + s->control &= ~(1 << 1); /* STP */ else { s->count_cur = s->count; omap_i2c_fifo_run(s); @@ -367,34 +367,34 @@ static void omap_i2c_write(void *opaque, hwaddr addr, } break; - case 0x28: /* I2C_OA */ + case 0x28: /* I2C_OA */ s->addr[0] = value & 0x3ff; break; - case 0x2c: /* I2C_SA */ + case 0x2c: /* I2C_SA */ s->addr[1] = value & 0x3ff; break; - case 0x30: /* I2C_PSC */ + case 0x30: /* I2C_PSC */ s->divider = value; break; - case 0x34: /* I2C_SCLL */ + case 0x34: /* I2C_SCLL */ s->times[0] = value; break; - case 0x38: /* I2C_SCLH */ + case 0x38: /* I2C_SCLH */ s->times[1] = value; break; - case 0x3c: /* I2C_SYSTEST */ + case 0x3c: /* I2C_SYSTEST */ s->test = value & 0xf80f; - if (value & (1 << 11)) /* SBB */ + if (value & (1 << 11)) /* SBB */ if (s->revision >= OMAP2_INTR_REV) { s->stat |= 0x3f; omap_i2c_interrupts_update(s); } - if (value & (1 << 15)) { /* ST_EN */ + if (value & (1 << 15)) { /* ST_EN */ qemu_log_mask(LOG_UNIMP, "%s: System Test not supported\n", __func__); } @@ -413,7 +413,7 @@ static void omap_i2c_writeb(void *opaque, hwaddr addr, int offset = addr & OMAP_MPUI_REG_MASK; switch (offset) { - case 0x1c: /* I2C_DATA */ + case 0x1c: /* I2C_DATA */ if (s->txlen > 2) { /* XXX: remote access (qualifier) error - what's that? */ break; @@ -421,9 +421,9 @@ static void omap_i2c_writeb(void *opaque, hwaddr addr, s->fifo <<= 8; s->txlen += 1; s->fifo |= value & 0xff; - s->stat &= ~(1 << 10); /* XUDF */ + s->stat &= ~(1 << 10); /* XUDF */ if (s->txlen > 2) - s->stat &= ~(1 << 4); /* XRDY */ + s->stat &= ~(1 << 4); /* XRDY */ omap_i2c_fifo_run(s); omap_i2c_interrupts_update(s); break; @@ -511,17 +511,16 @@ void omap_i2c_set_fclk(OMAPI2CState *i2c, omap_clk clk) i2c->fclk = clk; } -static Property omap_i2c_properties[] = { +static const Property omap_i2c_properties[] = { DEFINE_PROP_UINT8("revision", OMAPI2CState, revision, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void omap_i2c_class_init(ObjectClass *klass, void *data) +static void omap_i2c_class_init(ObjectClass *klass, const 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 9ad6a47739b..4e685fd26e7 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -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; @@ -213,7 +205,6 @@ static void smb_transaction(PMSMBus *s) error: s->smb_stat |= STS_DEV_ERR; - return; } static void smb_transaction_start(PMSMBus *s) @@ -258,8 +249,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 +269,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 +277,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 +419,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 +454,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 cef51663d0e..853dc4b4342 100644 --- a/hw/i2c/pmbus_device.c +++ b/hw/i2c/pmbus_device.c @@ -102,7 +102,6 @@ void pmbus_send_string(PMBusDevice *pmdev, const char *data) } 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; @@ -112,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) { @@ -472,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); @@ -782,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); @@ -814,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); @@ -854,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); @@ -1096,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; } @@ -1115,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) { @@ -1277,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); @@ -1582,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 */ @@ -1669,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), @@ -1685,7 +1902,7 @@ static void pmbus_device_finalize(Object *obj) g_free(pmdev->pages); } -static void pmbus_device_class_init(ObjectClass *klass, void *data) +static void pmbus_device_class_init(ObjectClass *klass, const void *data) { SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass); diff --git a/hw/i2c/ppc4xx_i2c.c b/hw/i2c/ppc4xx_i2c.c index 75d50f15158..09d4c49d657 100644 --- a/hw/i2c/ppc4xx_i2c.c +++ b/hw/i2c/ppc4xx_i2c.c @@ -354,11 +354,11 @@ static void ppc4xx_i2c_init(Object *o) bitbang_i2c_init(&s->bitbang, s->bus); } -static void ppc4xx_i2c_class_init(ObjectClass *klass, void *data) +static void ppc4xx_i2c_class_init(ObjectClass *klass, const 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 12c5741f388..0a1088fbb0a 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), @@ -137,13 +137,13 @@ static void smbus_eeprom_realize(DeviceState *dev, Error **errp) } } -static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data) +static void smbus_eeprom_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 18d40e93c10..956c9b59bbc 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -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, @@ -118,7 +118,7 @@ static void build_ich9_smb_aml(AcpiDevAmlIf *adev, Aml *scope) qbus_build_aml(bus, scope); } -static void ich9_smb_class_init(ObjectClass *klass, void *data) +static void ich9_smb_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -145,7 +145,7 @@ static const TypeInfo ich9_smb_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(ICH9SMBState), .class_init = ich9_smb_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { TYPE_ACPI_DEV_AML_IF }, { }, diff --git a/hw/i2c/smbus_slave.c b/hw/i2c/smbus_slave.c index 2ef2c7c5f69..cfb61c879e0 100644 --- a/hw/i2c/smbus_slave.c +++ b/hw/i2c/smbus_slave.c @@ -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 { @@ -197,7 +201,7 @@ static int smbus_i2c_send(I2CSlave *s, uint8_t data) return 0; } -static void smbus_device_class_init(ObjectClass *klass, void *data) +static void smbus_device_class_init(ObjectClass *klass, const void *data) { I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); @@ -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 d7b1e25858b..1ad0e95c0e6 100644 --- a/hw/i2c/trace-events +++ b/hw/i2c/trace-events @@ -15,6 +15,12 @@ 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 @@ -29,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" @@ -45,3 +56,8 @@ npcm7xx_smbus_recv_fifo(const char *id, uint8_t received, uint8_t expected) "%s pca954x_write_bytes(uint8_t value) "PCA954X write data: 0x%02x" pca954x_read_data(uint8_t value) "PCA954X read data: 0x%02x" + +# imx_i2c.c + +imx_i2c_read(const char *id, const char *reg, uint64_t ofs, uint64_t value) "%s:[%s (0x%" PRIx64 ")] -> 0x%02" PRIx64 +imx_i2c_write(const char *id, const char *reg, uint64_t ofs, uint64_t value) "%s:[%s (0x%" PRIx64 ")] <- 0x%02" PRIx64 diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 9051083c1e7..eb65bda6e07 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -10,6 +10,11 @@ config SGX bool depends on KVM +config TDX + bool + select X86_FW_OVMF + depends on KVM + config PC bool imply APPLESMC @@ -26,13 +31,14 @@ config PC imply QXL imply SEV imply SGX + imply TDX 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 @@ -43,8 +49,10 @@ config PC select SERIAL_ISA select ACPI_PCI select ACPI_VMGENID + select ACPI_VMCLOCK select VIRTIO_PMEM_SUPPORTED select VIRTIO_MEM_SUPPORTED + select HV_BALLOON_SUPPORTED config PC_PCI bool @@ -65,6 +73,8 @@ config PC_ACPI config I440FX bool + default y + depends on I386 imply E1000_PCI imply VMPORT imply VMMOUSE @@ -72,14 +82,16 @@ config I440FX select PC_PCI select PC_ACPI 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 @@ -90,11 +102,14 @@ config ISAPC 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 @@ -106,6 +121,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 @@ -118,6 +136,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 @@ -140,4 +168,4 @@ config VMMOUSE config XEN_EMU bool default y - depends on KVM && (I386 || X86_64) + depends on KVM && I386 diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index bb12b0ad430..61851cc8407 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -22,7 +22,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qnum.h" #include "acpi-build.h" #include "acpi-common.h" #include "qemu/bitmap.h" @@ -40,23 +40,23 @@ #include "hw/acpi/acpi_aml_interface.h" #include "hw/input/i8042.h" #include "hw/acpi/memory_hotplug.h" -#include "sysemu/tpm.h" +#include "system/tpm.h" #include "hw/acpi/tpm.h" #include "hw/acpi/vmgenid.h" +#include "hw/acpi/vmclock.h" #include "hw/acpi/erst.h" #include "hw/acpi/piix4.h" -#include "sysemu/tpm_backend.h" +#include "system/tpm_backend.h" #include "hw/rtc/mc146818rtc_regs.h" #include "migration/vmstate.h" #include "hw/mem/memory-device.h" #include "hw/mem/nvdimm.h" -#include "sysemu/numa.h" -#include "sysemu/reset.h" +#include "system/numa.h" +#include "system/reset.h" #include "hw/hyperv/vmbus-bridge.h" /* Supported chipsets: */ #include "hw/southbridge/ich9.h" -#include "hw/southbridge/piix.h" #include "hw/acpi/pcihp.h" #include "hw/i386/fw_cfg.h" #include "hw/i386/pc.h" @@ -85,7 +85,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 @@ -140,7 +139,7 @@ static void init_common_fadt_data(MachineState *ms, Object *o, /* * "ICH9-LPC" or "PIIX4_PM" has "smm-compat" property to keep the old * behavior for compatibility irrelevant to smm_enabled, which doesn't - * comforms to ACPI spec. + * conform to the ACPI spec. */ bool smm_enabled = object_property_get_bool(o, "smm-compat", NULL) ? true : x86_machine_is_smm_enabled(x86ms); @@ -193,21 +192,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; @@ -242,10 +230,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) { @@ -605,8 +589,8 @@ void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus) } } -static bool build_append_notfication_callback(Aml *parent_scope, - const PCIBus *bus) +static bool build_append_notification_callback(Aml *parent_scope, + const PCIBus *bus) { Aml *method; PCIBus *sec; @@ -620,7 +604,7 @@ static bool build_append_notfication_callback(Aml *parent_scope, continue; } nr_notifiers = nr_notifiers + - build_append_notfication_callback(br_scope, sec); + build_append_notification_callback(br_scope, sec); /* * add new child scope to parent * and keep track of bus that have PCNT, @@ -671,6 +655,7 @@ static Aml *aml_pci_pdsm(void) Aml *acpi_index = aml_local(2); Aml *zero = aml_int(0); Aml *one = aml_int(1); + Aml *not_supp = aml_int(0xFFFFFFFF); Aml *func = aml_arg(2); Aml *params = aml_arg(4); Aml *bnum = aml_derefof(aml_index(params, aml_int(0))); @@ -695,7 +680,7 @@ static Aml *aml_pci_pdsm(void) */ ifctx1 = aml_if(aml_lnot( aml_or(aml_equal(acpi_index, zero), - aml_equal(acpi_index, aml_int(0xFFFFFFFF)), NULL) + aml_equal(acpi_index, not_supp), NULL) )); { /* have supported functions */ @@ -721,18 +706,30 @@ static Aml *aml_pci_pdsm(void) { Aml *pkg = aml_package(2); - aml_append(pkg, zero); - /* - * optional, if not impl. should return null string - */ - aml_append(pkg, aml_string("%s", "")); - aml_append(ifctx, aml_store(pkg, ret)); - aml_append(ifctx, aml_store(aml_call2("AIDX", bnum, sunum), acpi_index)); + aml_append(ifctx, aml_store(pkg, ret)); /* - * update acpi-index to actual value + * Windows calls func=7 without checking if it's available, + * as workaround Microsoft has suggested to return invalid for func7 + * Package, so return 2 elements package but only initialize elements + * when acpi_index is supported and leave them uninitialized, which + * leads elements to being Uninitialized ObjectType and should trip + * Windows into discarding result as an unexpected and prevent setting + * bogus 'PCI Label' on the device. */ - aml_append(ifctx, aml_store(acpi_index, aml_index(ret, zero))); + ifctx1 = aml_if(aml_lnot(aml_lor( + aml_equal(acpi_index, zero), aml_equal(acpi_index, not_supp) + ))); + { + aml_append(ifctx1, aml_store(acpi_index, aml_index(ret, zero))); + /* + * optional, if not impl. should return null string + */ + aml_append(ifctx1, aml_store(aml_string("%s", ""), + aml_index(ret, one))); + } + aml_append(ifctx, ifctx1); + aml_append(ifctx, aml_return(ret)); } @@ -740,120 +737,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)); - - /* 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); - - /* 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); + assert(nroutes < 256); + rt_pkg = aml_package(nroutes); + + for (pin = 0; pin < nroutes; pin++) { + Aml *pkg = aml_package(4); + int slot = pin >> 2; + + 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; } @@ -1420,8 +1342,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); @@ -1432,8 +1355,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; @@ -1495,14 +1419,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)); @@ -1549,8 +1473,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) { @@ -1560,11 +1485,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)) { @@ -1575,6 +1501,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)) { @@ -1582,15 +1510,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(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"))); @@ -1783,14 +1711,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); - if (object_property_find(OBJECT(bus), ACPI_PCIHP_PROP_BSEL)) { - build_append_pcihp_slots(scope, bus); + 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, scope); + aml_append(sb_scope, ascope); } } @@ -1842,10 +1770,10 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, bool has_pcnt; Object *pci_host = acpi_get_i386_pci_host(); - PCIBus *bus = PCI_HOST_BRIDGE(pci_host)->bus; + PCIBus *b = PCI_HOST_BRIDGE(pci_host)->bus; scope = aml_scope("\\_SB.PCI0"); - has_pcnt = build_append_notfication_callback(scope, bus); + has_pcnt = build_append_notification_callback(scope, b); if (has_pcnt) { aml_append(dsdt, scope); } @@ -2060,6 +1988,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 @@ -2079,7 +2009,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) @@ -2337,30 +2267,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 @@ -2380,56 +2303,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. + * for type IO-APIC + * Refer to spec - Table 95: IVHD device entry type codes + * + * Linux IOMMU driver checks for the special IVHD device (type IO-APIC). + * See Linux kernel commit 'c2ff5cf5294bcbd7fa50f7d860e90a66db7e5059' */ if (x86_iommu_ir_supported(x86_iommu_get_default())) { - ivhd_table_len += 8; + build_append_int_noprefix(ivhd_blob, + (0x1ull << 56) | /* type IOAPIC */ + (IOAPIC_SB_DEVID << 40) | /* IOAPIC devid */ + 0x48, /* special device */ + 8); } + /* IVHD definition - type 10h */ + build_append_int_noprefix(table_data, 0x10, 1); + /* virtualization flags */ + build_append_int_noprefix(table_data, + (1UL << 0) | /* HtTunEn */ + (1UL << 4) | /* iotblSup */ + (1UL << 6) | /* PrefSup */ + (1UL << 7), /* PPRSup */ + 1); + /* IVHD length */ - build_append_int_noprefix(table_data, ivhd_table_len, 2); + build_append_int_noprefix(table_data, ivhd_blob->len + 24, 2); /* DeviceID */ build_append_int_noprefix(table_data, - object_property_get_int(OBJECT(&s->pci), "addr", + object_property_get_int(OBJECT(s->pci), "addr", &error_abort), 2); /* Capability offset */ - build_append_int_noprefix(table_data, s->pci.capab_offset, 2); + build_append_int_noprefix(table_data, s->pci->capab_offset, 2); /* IOMMU base address */ - build_append_int_noprefix(table_data, s->mmio.addr, 8); + 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, - (48UL << 30) | /* HATS */ - (48UL << 28) | /* GATS */ - (1UL << 2) | /* GTSup */ - (1UL << 6), /* GASup */ - 4); + (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); - /* - * Add a special IVHD device type. - * Refer to spec - Table 95: IVHD device entry type codes - * - * Linux IOMMU driver checks for the special IVHD device (type IO-APIC). - * See Linux kernel commit 'c2ff5cf5294bcbd7fa50f7d860e90a66db7e5059' - */ - if (x86_iommu_ir_supported(x86_iommu_get_default())) { - build_append_int_noprefix(table_data, - (0x1ull << 56) | /* type IOAPIC */ - (IOAPIC_SB_DEVID << 40) | /* IOAPIC devid */ - 0x48, /* special device */ - 8); - } + g_array_free(ivhd_blob, TRUE); acpi_table_end(linker, &table); } @@ -2439,7 +2400,6 @@ struct AcpiBuildState { MemoryRegion *table_mr; /* Is table patched? */ uint8_t patched; - void *rsdp; MemoryRegion *rsdp_mr; MemoryRegion *linker_mr; } AcpiBuildState; @@ -2475,20 +2435,18 @@ 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; + Object *vmgenid_dev, *vmclock_dev; char *oem_id; char *oem_table_id; @@ -2531,24 +2489,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 { @@ -2569,6 +2519,13 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) tables->vmgenid, tables->linker, x86ms->oem_id); } + vmclock_dev = find_vmclock_dev(); + if (vmclock_dev) { + acpi_add_table(table_offsets, tables_blob); + vmclock_build_acpi(VMCLOCK(vmclock_dev), tables_blob, tables->linker, + x86ms->oem_id); + } + if (misc.has_hpet) { acpi_add_table(table_offsets, tables_blob); build_hpet(tables_blob, tables->linker, x86ms->oem_id, @@ -2656,16 +2613,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 @@ -2675,49 +2622,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) && - !pcmc->resizable_acpi_blob) { - /* Should happen only with PCI bridges and -M pc-i440fx-2.0. */ - warn_report("ACPI table size %u exceeds %d bytes," - " migration may not work", - tables_blob->len, legacy_table_size); - error_printf("Try removing CPUs, NUMA nodes, memory slots" - " or PCI bridges."); - } - 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) && - !pcmc->resizable_acpi_blob) { - /* As of QEMU 2.1, this fires with 160 VCPUs and 255 memory slots. */ - warn_report("ACPI table size %u exceeds %d bytes," - " migration may not work", - tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2); - error_printf("Try removing CPUs, NUMA nodes, memory slots" - " or PCI bridges."); - } - 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); @@ -2755,11 +2662,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); @@ -2775,7 +2678,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() }, @@ -2784,7 +2687,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; @@ -2846,25 +2748,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-build.h b/hw/i386/acpi-build.h index 0dce155c8cc..275ec058a12 100644 --- a/hw/i386/acpi-build.h +++ b/hw/i386/acpi-build.h @@ -5,7 +5,7 @@ extern const struct AcpiGenericAddress x86_nvdimm_acpi_dsmio; -/* PCI Hot-plug registers bases. See docs/spec/acpi_pci_hotplug.txt */ +/* PCI Hot-plug registers' base. See docs/specs/acpi_pci_hotplug.rst */ #define ACPI_PCIHP_SEJ_BASE 0x8 #define ACPI_PCIHP_BNMR_BASE 0x10 diff --git a/hw/i386/acpi-common.c b/hw/i386/acpi-common.c index 8a0932fe84e..7bd08067a73 100644 --- a/hw/i386/acpi-common.c +++ b/hw/i386/acpi-common.c @@ -23,11 +23,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "exec/memory.h" +#include "system/memory.h" #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" @@ -94,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 = 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(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; } @@ -123,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 a68825acf50..e305aaac158 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 a075360d852..bc6571778c3 100644 --- a/hw/i386/acpi-microvm.c +++ b/hw/i386/acpi-microvm.c @@ -24,7 +24,7 @@ #include "qemu/cutils.h" #include "qapi/error.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/acpi/acpi.h" #include "hw/acpi/acpi_aml_interface.h" #include "hw/acpi/aml-build.h" @@ -37,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" @@ -55,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); @@ -77,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); } } } @@ -214,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 00000000000..d62a3732e60 --- /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 9c773044384..963aa2450c9 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) @@ -154,11 +167,11 @@ static void amdvi_generate_msi_interrupt(AMDVIState *s) { MSIMessage msg = {}; MemTxAttrs attrs = { - .requester_id = pci_requester_id(&s->pci.dev) + .requester_id = pci_requester_id(&s->pci->dev) }; - if (msi_enabled(&s->pci.dev)) { - msg = msi_get_message(&s->pci.dev, 0); + if (msi_enabled(&s->pci->dev)) { + msg = msi_get_message(&s->pci->dev, 0); address_space_stl_le(&address_space_memory, msg.address, msg.data, attrs, NULL); } @@ -226,7 +239,7 @@ static void amdvi_page_fault(AMDVIState *s, uint16_t devid, info |= AMDVI_EVENT_IOPF_I | AMDVI_EVENT_IOPF; amdvi_encode_event(evt, devid, addr, info); amdvi_log_event(s, evt); - pci_word_test_and_set_mask(s->pci.dev.config + PCI_STATUS, + pci_word_test_and_set_mask(s->pci->dev.config + PCI_STATUS, PCI_STATUS_SIG_TARGET_ABORT); } /* @@ -243,7 +256,7 @@ static void amdvi_log_devtab_error(AMDVIState *s, uint16_t devid, amdvi_encode_event(evt, devid, devtab, info); amdvi_log_event(s, evt); - pci_word_test_and_set_mask(s->pci.dev.config + PCI_STATUS, + pci_word_test_and_set_mask(s->pci->dev.config + PCI_STATUS, PCI_STATUS_SIG_TARGET_ABORT); } /* log an event trying to access command buffer @@ -256,10 +269,10 @@ static void amdvi_log_command_error(AMDVIState *s, hwaddr addr) amdvi_encode_event(evt, 0, addr, info); amdvi_log_event(s, evt); - pci_word_test_and_set_mask(s->pci.dev.config + PCI_STATUS, + 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, @@ -297,7 +310,7 @@ static void amdvi_log_pagetab_error(AMDVIState *s, uint16_t devid, info |= AMDVI_EVENT_PAGE_TAB_HW_ERROR; amdvi_encode_event(evt, devid, addr, info); amdvi_log_event(s, evt); - pci_word_test_and_set_mask(s->pci.dev.config + PCI_STATUS, + pci_word_test_and_set_mask(s->pci->dev.config + PCI_STATUS, PCI_STATUS_SIG_TARGET_ABORT); } @@ -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; } @@ -1284,15 +1309,15 @@ static int amdvi_int_remap_msi(AMDVIState *iommu, ret = -AMDVI_IR_ERR; break; case AMDVI_IOAPIC_INT_TYPE_NMI: - pass = dte[3] & AMDVI_DEV_NMI_PASS_MASK; + pass = dte[2] & AMDVI_DEV_NMI_PASS_MASK; trace_amdvi_ir_delivery_mode("nmi"); break; case AMDVI_IOAPIC_INT_TYPE_INIT: - pass = dte[3] & AMDVI_DEV_INT_PASS_MASK; + pass = dte[2] & AMDVI_DEV_INT_PASS_MASK; trace_amdvi_ir_delivery_mode("init"); break; case AMDVI_IOAPIC_INT_TYPE_EINT: - pass = dte[3] & AMDVI_DEV_EINT_PASS_MASK; + pass = dte[2] & AMDVI_DEV_EINT_PASS_MASK; trace_amdvi_ir_delivery_mode("eint"); break; default: @@ -1425,13 +1450,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 +1466,35 @@ 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); + + memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, false); + memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu), 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,8 +1546,9 @@ 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); } @@ -1544,9 +1585,9 @@ static void amdvi_pci_realize(PCIDevice *pdev, Error **errp) /* reset AMDVI specific capabilities, all r/o */ 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)); + AMDVI_BASE_ADDR & MAKE_64BIT_MASK(14, 18)); pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_BAR_HIGH, - (AMDVI_BASE_ADDR & ~(0xffff)) >> 16); + AMDVI_BASE_ADDR >> 32); pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_RANGE, 0xff000000); pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_MISC, 0); @@ -1558,81 +1599,173 @@ static void amdvi_sysbus_reset(DeviceState *dev) { AMDVIState *s = AMD_IOMMU_DEVICE(dev); - msi_reset(&s->pci.dev); + msi_reset(&s->pci->dev); amdvi_init(s); } +static const VMStateDescription vmstate_amdvi_sysbus_migratable = { + .name = "amd-iommu", + .version_id = 1, + .minimum_version_id = 1, + .priority = MIG_PRI_IOMMU, + .fields = (VMStateField[]) { + /* Updated in amdvi_handle_control_write() */ + VMSTATE_BOOL(enabled, AMDVIState), + VMSTATE_BOOL(ga_enabled, AMDVIState), + VMSTATE_BOOL(ats_enabled, AMDVIState), + VMSTATE_BOOL(cmdbuf_enabled, AMDVIState), + VMSTATE_BOOL(completion_wait_intr, AMDVIState), + VMSTATE_BOOL(evtlog_enabled, AMDVIState), + VMSTATE_BOOL(evtlog_intr, AMDVIState), + /* Updated in amdvi_handle_devtab_write() */ + VMSTATE_UINT64(devtab, AMDVIState), + VMSTATE_UINT64(devtab_len, AMDVIState), + /* Updated in amdvi_handle_cmdbase_write() */ + VMSTATE_UINT64(cmdbuf, AMDVIState), + VMSTATE_UINT64(cmdbuf_len, AMDVIState), + /* Updated in amdvi_handle_cmdhead_write() */ + VMSTATE_UINT32(cmdbuf_head, AMDVIState), + /* Updated in amdvi_handle_cmdtail_write() */ + VMSTATE_UINT32(cmdbuf_tail, AMDVIState), + /* Updated in amdvi_handle_evtbase_write() */ + VMSTATE_UINT64(evtlog, AMDVIState), + VMSTATE_UINT32(evtlog_len, AMDVIState), + /* Updated in amdvi_handle_evthead_write() */ + VMSTATE_UINT32(evtlog_head, AMDVIState), + /* Updated in amdvi_handle_evttail_write() */ + VMSTATE_UINT32(evtlog_tail, AMDVIState), + /* Updated in amdvi_handle_pprbase_write() */ + VMSTATE_UINT64(ppr_log, AMDVIState), + VMSTATE_UINT32(pprlog_len, AMDVIState), + /* Updated in amdvi_handle_pprhead_write() */ + VMSTATE_UINT32(pprlog_head, AMDVIState), + /* Updated in amdvi_handle_tailhead_write() */ + VMSTATE_UINT32(pprlog_tail, AMDVIState), + /* MMIO registers */ + VMSTATE_UINT8_ARRAY(mmior, AMDVIState, AMDVI_MMIO_SIZE), + VMSTATE_UINT8_ARRAY(romask, AMDVIState, AMDVI_MMIO_SIZE), + VMSTATE_UINT8_ARRAY(w1cmask, AMDVIState, AMDVI_MMIO_SIZE), + VMSTATE_END_OF_LIST() + } +}; + static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) { + DeviceClass *dc = (DeviceClass *) object_get_class(OBJECT(dev)); 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); + if (s->pci_id) { + PCIDevice *pdev = NULL; + int ret = pci_qdev_find_device(s->pci_id, &pdev); - /* This device should take care of IOMMU PCI properties */ - if (!qdev_realize(DEVICE(&s->pci), &bus->qbus, errp)) { - return; + if (ret) { + error_report("Cannot find PCI device '%s'", s->pci_id); + return; + } + + if (!object_dynamic_cast(OBJECT(pdev), TYPE_AMD_IOMMU_PCI)) { + error_report("Device '%s' must be an AMDVI-PCI device type", s->pci_id); + return; + } + + s->pci = AMD_IOMMU_PCI(pdev); + dc->vmsd = &vmstate_amdvi_sysbus_migratable; + } else { + s->pci = AMD_IOMMU_PCI(object_new(TYPE_AMD_IOMMU_PCI)); + /* This device should take care of IOMMU PCI properties */ + if (!qdev_realize(DEVICE(s->pci), &bus->qbus, errp)) { + return; + } } + s->iotlb = g_hash_table_new_full(amdvi_uint64_hash, + amdvi_uint64_equal, g_free, g_free); + /* 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); + + /* 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); + + if (kvm_enabled() && x86ms->apic_id_limit > 255 && !s->xtsup) { + error_report("AMD IOMMU with x2APIC configuration requires xtsup=on"); + exit(EXIT_FAILURE); + } + + if (s->xtsup) { + if (kvm_irqchip_is_split() && !kvm_enable_x2apic()) { + error_report("AMD IOMMU xtsup=on requires x2APIC support on " + "the KVM side"); + exit(EXIT_FAILURE); + } + } - 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); + pci_setup_iommu(bus, &amdvi_iommu_ops, s); amdvi_init(s); } +static const Property amdvi_properties[] = { + DEFINE_PROP_BOOL("xtsup", AMDVIState, xtsup, false), + DEFINE_PROP_STRING("pci-id", AMDVIState, pci_id), +}; + static const VMStateDescription vmstate_amdvi_sysbus = { .name = "amd-iommu", .unmigratable = 1 }; -static void amdvi_sysbus_instance_init(Object *klass) -{ - AMDVIState *s = AMD_IOMMU_DEVICE(klass); - - object_initialize(&s->pci, sizeof(s->pci), TYPE_AMD_IOMMU_PCI); -} - -static void amdvi_sysbus_class_init(ObjectClass *klass, void *data) +static void amdvi_sysbus_class_init(ObjectClass *klass, const 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; dc_class->int_remap = amdvi_int_remap; - /* Supported by the pc-q35-* machine types */ - 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 = { .name = TYPE_AMD_IOMMU_DEVICE, .parent = TYPE_X86_IOMMU_DEVICE, .instance_size = sizeof(AMDVIState), - .instance_init = amdvi_sysbus_instance_init, .class_init = amdvi_sysbus_class_init }; -static void amdvi_pci_class_init(ObjectClass *klass, void *data) +static void amdvi_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->vendor_id = PCI_VENDOR_ID_AMD; + k->device_id = 0x1419; k->class_id = 0x0806; k->realize = amdvi_pci_realize; @@ -1645,13 +1778,14 @@ static const TypeInfo amdvi_pci = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(AMDVIPCIState), .class_init = amdvi_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -static void amdvi_iommu_memory_region_class_init(ObjectClass *klass, void *data) +static void amdvi_iommu_memory_region_class_init(ObjectClass *klass, + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 6da893ee571..5672bdef890 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) @@ -185,7 +187,7 @@ AMDVI_CAPAB_FLAG_HTTUNNEL | AMDVI_CAPAB_EFR_SUP) /* AMDVI default address */ -#define AMDVI_BASE_ADDR 0xfed80000 +#define AMDVI_BASE_ADDR 0xfed80000ULL /* page management constants */ #define AMDVI_PAGE_SHIFT 12 @@ -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; }; @@ -314,7 +315,8 @@ struct AMDVIPCIState { struct AMDVIState { X86IOMMUState iommu; /* IOMMU bus device */ - AMDVIPCIState pci; /* IOMMU PCI device */ + AMDVIPCIState *pci; /* IOMMU PCI device */ + char *pci_id; /* ID of AMDVI-PCI device, if user created */ uint32_t version; @@ -327,7 +329,7 @@ struct AMDVIState { bool excl_enabled; hwaddr devtab; /* base address device table */ - size_t devtab_len; /* device table length */ + uint64_t devtab_len; /* device table length */ hwaddr cmdbuf; /* command buffer base address */ uint64_t cmdbuf_len; /* command buffer length */ @@ -352,7 +354,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 */ @@ -366,6 +371,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 06970ac44a2..3e848fb69cd 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 7c239aa033f..b50acfa2019 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 7362daa45a6..5c0bcd5f8a9 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -13,7 +13,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/numa.h" +#include "system/numa.h" #include "hw/acpi/acpi.h" #include "hw/acpi/aml-build.h" #include "hw/firmware/smbios.h" @@ -26,7 +26,9 @@ #include CONFIG_DEVICES #include "target/i386/cpu.h" -struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX}; +#if !defined(CONFIG_HPET) +struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX}; +#endif const char *fw_cfg_arch_key_name(uint16_t key) { @@ -48,27 +50,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)) { @@ -77,7 +101,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); @@ -121,16 +145,13 @@ FWCfgState *fw_cfg_arch_create(MachineState *ms, */ fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, apic_id_limit); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, ms->ram_size); -#ifdef CONFIG_ACPI - fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, - acpi_tables, acpi_tables_len); -#endif + if (acpi_builtin()) { + fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, + acpi_tables, acpi_tables_len); + } 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)); + fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_fw_cfg, sizeof(hpet_fw_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 * hold the amount of memory. @@ -191,6 +212,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) { /* @@ -217,3 +239,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 86ca7c1c0cb..e560fd7be82 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 e26fb02a2ef..00000000000 --- 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 c9961ef752c..69d72ad35cc 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -32,9 +32,9 @@ #include "hw/i386/apic-msidef.h" #include "hw/i386/x86-iommu.h" #include "hw/pci-host/q35.h" -#include "sysemu/kvm.h" -#include "sysemu/dma.h" -#include "sysemu/sysemu.h" +#include "system/kvm.h" +#include "system/dma.h" +#include "system/system.h" #include "hw/i386/apic_internal.h" #include "kvm/kvm_i386.h" #include "migration/vmstate.h" @@ -48,11 +48,14 @@ /* pe operations */ #define VTD_PE_GET_TYPE(pe) ((pe)->val[0] & VTD_SM_PASID_ENTRY_PGTT) -#define VTD_PE_GET_LEVEL(pe) (2 + (((pe)->val[0] >> 2) & VTD_SM_PASID_ENTRY_AW)) +#define VTD_PE_GET_FL_LEVEL(pe) \ + (4 + (((pe)->val[2] >> 2) & VTD_SM_PASID_ENTRY_FLPM)) +#define VTD_PE_GET_SL_LEVEL(pe) \ + (2 + (((pe)->val[0] >> 2) & VTD_SM_PASID_ENTRY_AW)) /* * 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,6 +64,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_as_raw_key { + uint16_t sid; + uint32_t pasid; +}; + struct vtd_iotlb_key { uint64_t gfn; uint32_t pasid; @@ -250,6 +264,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) { @@ -259,15 +292,15 @@ static gboolean vtd_hash_remove_by_domain(gpointer key, gpointer value, } /* The shift of an addr for a certain level of paging structure */ -static inline uint32_t vtd_slpt_level_shift(uint32_t level) +static inline uint32_t vtd_pt_level_shift(uint32_t level) { assert(level != 0); - return VTD_PAGE_SHIFT_4K + (level - 1) * VTD_SL_LEVEL_BITS; + return VTD_PAGE_SHIFT_4K + (level - 1) * VTD_LEVEL_BITS; } -static inline uint64_t vtd_slpt_level_page_mask(uint32_t level) +static inline uint64_t vtd_pt_level_page_mask(uint32_t level) { - return ~((1ULL << vtd_slpt_level_shift(level)) - 1); + return ~((1ULL << vtd_pt_level_shift(level)) - 1); } static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value, @@ -277,9 +310,43 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value, VTDIOTLBPageInvInfo *info = (VTDIOTLBPageInvInfo *)user_data; uint64_t gfn = (info->addr >> VTD_PAGE_SHIFT_4K) & info->mask; uint64_t gfn_tlb = (info->addr & entry->mask) >> VTD_PAGE_SHIFT_4K; - return (entry->domain_id == info->domain_id) && - (((entry->gfn & info->mask) == gfn) || - (entry->gfn == gfn_tlb)); + + if (entry->domain_id != info->domain_id) { + return false; + } + + /* + * According to spec, IOTLB entries caching first-stage (PGTT=001b) or + * nested (PGTT=011b) mapping associated with specified domain-id are + * invalidated. Nested isn't supported yet, so only need to check 001b. + */ + if (entry->pgtt == VTD_SM_PASID_ENTRY_FLT) { + return true; + } + + return (entry->gfn & info->mask) == gfn || entry->gfn == gfn_tlb; +} + +static gboolean vtd_hash_remove_by_page_piotlb(gpointer key, gpointer value, + gpointer user_data) +{ + VTDIOTLBEntry *entry = (VTDIOTLBEntry *)value; + VTDIOTLBPageInvInfo *info = (VTDIOTLBPageInvInfo *)user_data; + uint64_t gfn = (info->addr >> VTD_PAGE_SHIFT_4K) & info->mask; + uint64_t gfn_tlb = (info->addr & entry->mask) >> VTD_PAGE_SHIFT_4K; + + /* + * According to spec, PASID-based-IOTLB Invalidation in page granularity + * doesn't invalidate IOTLB entries caching second-stage (PGTT=010b) + * or pass-through (PGTT=100b) mappings. Nested isn't supported yet, + * so only need to check first-stage (PGTT=001b) mappings. + */ + if (entry->pgtt != VTD_SM_PASID_ENTRY_FLT) { + return false; + } + + return entry->domain_id == info->domain_id && entry->pasid == info->pasid && + ((entry->gfn & info->mask) == gfn || entry->gfn == gfn_tlb); } /* Reset all the gen of VTDAddressSpace to zero and set the gen of @@ -324,7 +391,7 @@ static void vtd_reset_caches(IntelIOMMUState *s) static uint64_t vtd_get_iotlb_gfn(hwaddr addr, uint32_t level) { - return (addr & vtd_slpt_level_page_mask(level)) >> VTD_PAGE_SHIFT_4K; + return (addr & vtd_pt_level_page_mask(level)) >> VTD_PAGE_SHIFT_4K; } /* Must be called with IOMMU lock held */ @@ -333,9 +400,9 @@ 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++) { + for (level = VTD_PT_LEVEL; level < VTD_PML4_LEVEL; level++) { key.gfn = vtd_get_iotlb_gfn(addr, level); key.level = level; key.sid = source_id; @@ -352,15 +419,15 @@ static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id, /* Must be with IOMMU lock held */ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, - uint16_t domain_id, hwaddr addr, uint64_t slpte, + uint16_t domain_id, hwaddr addr, uint64_t pte, uint8_t access_flags, uint32_t level, - uint32_t pasid) + uint32_t pasid, uint8_t pgtt) { VTDIOTLBEntry *entry = g_malloc(sizeof(*entry)); struct vtd_iotlb_key *key = g_malloc(sizeof(*key)); uint64_t gfn = vtd_get_iotlb_gfn(addr, level); - trace_vtd_iotlb_page_update(source_id, addr, slpte, domain_id); + trace_vtd_iotlb_page_update(source_id, addr, pte, domain_id); if (g_hash_table_size(s->iotlb) >= VTD_IOTLB_MAX_SIZE) { trace_vtd_iotlb_reset("iotlb exceeds size limit"); vtd_reset_iotlb_locked(s); @@ -368,10 +435,11 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, entry->gfn = gfn; entry->domain_id = domain_id; - entry->slpte = slpte; + entry->pte = pte; entry->access_flags = access_flags; - entry->mask = vtd_slpt_level_page_mask(level); + entry->mask = vtd_pt_level_page_mask(level); entry->pasid = pasid; + entry->pgtt = pgtt; key->gfn = gfn; key->sid = source_id; @@ -469,21 +537,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); @@ -509,17 +568,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"); @@ -539,8 +592,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, " @@ -565,6 +617,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. */ @@ -667,32 +753,32 @@ static inline dma_addr_t vtd_ce_get_slpt_base(VTDContextEntry *ce) return ce->lo & VTD_CONTEXT_ENTRY_SLPTPTR; } -static inline uint64_t vtd_get_slpte_addr(uint64_t slpte, uint8_t aw) +static inline uint64_t vtd_get_pte_addr(uint64_t pte, uint8_t aw) { - return slpte & VTD_SL_PT_BASE_ADDR_MASK(aw); + return pte & VTD_PT_BASE_ADDR_MASK(aw); } /* Whether the pte indicates the address of the page frame */ -static inline bool vtd_is_last_slpte(uint64_t slpte, uint32_t level) +static inline bool vtd_is_last_pte(uint64_t pte, uint32_t level) { - return level == VTD_SL_PT_LEVEL || (slpte & VTD_SL_PT_PAGE_SIZE_MASK); + return level == VTD_PT_LEVEL || (pte & VTD_PT_PAGE_SIZE_MASK); } -/* Get the content of a spte located in @base_addr[@index] */ -static uint64_t vtd_get_slpte(dma_addr_t base_addr, uint32_t index) +/* Get the content of a pte located in @base_addr[@index] */ +static uint64_t vtd_get_pte(dma_addr_t base_addr, uint32_t index) { - uint64_t slpte; + uint64_t pte; - assert(index < VTD_SL_PT_ENTRY_NR); + assert(index < VTD_PT_ENTRY_NR); if (dma_memory_read(&address_space_memory, - base_addr + index * sizeof(slpte), - &slpte, sizeof(slpte), MEMTXATTRS_UNSPECIFIED)) { - slpte = (uint64_t)-1; - return slpte; + base_addr + index * sizeof(pte), + &pte, sizeof(pte), MEMTXATTRS_UNSPECIFIED)) { + pte = (uint64_t)-1; + return pte; } - slpte = le64_to_cpu(slpte); - return slpte; + pte = le64_to_cpu(pte); + return pte; } /* Given an iova and the level of paging structure, return the offset @@ -700,36 +786,39 @@ static uint64_t vtd_get_slpte(dma_addr_t base_addr, uint32_t index) */ static inline uint32_t vtd_iova_level_offset(uint64_t iova, uint32_t level) { - return (iova >> vtd_slpt_level_shift(level)) & - ((1ULL << VTD_SL_LEVEL_BITS) - 1); + return (iova >> vtd_pt_level_shift(level)) & + ((1ULL << VTD_LEVEL_BITS) - 1); } /* Check Capability Register to see if the @level of page-table is supported */ -static inline bool vtd_is_level_supported(IntelIOMMUState *s, uint32_t level) +static inline bool vtd_is_sl_level_supported(IntelIOMMUState *s, uint32_t level) { return VTD_CAP_SAGAW_MASK & s->cap & (1ULL << (level - 2 + VTD_CAP_SAGAW_SHIFT)); } +static inline bool vtd_is_fl_level_supported(IntelIOMMUState *s, uint32_t level) +{ + return level == VTD_PML4_LEVEL; +} + /* Return true if check passed, otherwise false */ -static inline bool vtd_pe_type_check(X86IOMMUState *x86_iommu, - VTDPASIDEntry *pe) +static inline bool vtd_pe_type_check(IntelIOMMUState *s, VTDPASIDEntry *pe) { switch (VTD_PE_GET_TYPE(pe)) { case VTD_SM_PASID_ENTRY_FLT: + return !!(s->ecap & VTD_ECAP_FLTS); case VTD_SM_PASID_ENTRY_SLT: + return !!(s->ecap & VTD_ECAP_SLTS); case VTD_SM_PASID_ENTRY_NESTED: - break; + /* Not support NESTED page table type yet */ + return false; case VTD_SM_PASID_ENTRY_PT: - if (!x86_iommu->pt_supported) { - return false; - } - break; + return !!(s->ecap & VTD_ECAP_PT); default: /* Unknown type */ return false; } - return true; } static inline bool vtd_pdire_present(VTDPASIDDirEntry *pdire) @@ -753,7 +842,7 @@ static int vtd_get_pdire_from_pdir_table(dma_addr_t pasid_dir_base, addr = pasid_dir_base + index * entry_size; if (dma_memory_read(&address_space_memory, addr, pdire, entry_size, MEMTXATTRS_UNSPECIFIED)) { - return -VTD_FR_PASID_TABLE_INV; + return -VTD_FR_PASID_DIR_ACCESS_ERR; } pdire->val = le64_to_cpu(pdire->val); @@ -771,28 +860,35 @@ static int vtd_get_pe_in_pasid_leaf_table(IntelIOMMUState *s, dma_addr_t addr, VTDPASIDEntry *pe) { + uint8_t pgtt; uint32_t index; dma_addr_t entry_size; - X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); index = VTD_PASID_TABLE_INDEX(pasid); entry_size = VTD_PASID_ENTRY_SIZE; addr = addr + index * entry_size; if (dma_memory_read(&address_space_memory, addr, pe, entry_size, MEMTXATTRS_UNSPECIFIED)) { - return -VTD_FR_PASID_TABLE_INV; + return -VTD_FR_PASID_TABLE_ACCESS_ERR; } 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)) { - return -VTD_FR_PASID_TABLE_INV; + if (!vtd_pe_type_check(s, pe)) { + return -VTD_FR_PASID_TABLE_ENTRY_INV; + } + + pgtt = VTD_PE_GET_TYPE(pe); + if (pgtt == VTD_SM_PASID_ENTRY_SLT && + !vtd_is_sl_level_supported(s, VTD_PE_GET_SL_LEVEL(pe))) { + return -VTD_FR_PASID_TABLE_ENTRY_INV; } - if (!vtd_is_level_supported(s, VTD_PE_GET_LEVEL(pe))) { - return -VTD_FR_PASID_TABLE_INV; + if (pgtt == VTD_SM_PASID_ENTRY_FLT && + !vtd_is_fl_level_supported(s, VTD_PE_GET_FL_LEVEL(pe))) { + return -VTD_FR_PASID_TABLE_ENTRY_INV; } return 0; @@ -833,7 +929,7 @@ static int vtd_get_pe_from_pasid_table(IntelIOMMUState *s, } if (!vtd_pdire_present(&pdire)) { - return -VTD_FR_PASID_TABLE_INV; + return -VTD_FR_PASID_DIR_ENTRY_P; } ret = vtd_get_pe_from_pdire(s, pasid, &pdire, pe); @@ -842,7 +938,7 @@ static int vtd_get_pe_from_pasid_table(IntelIOMMUState *s, } if (!vtd_pe_present(pe)) { - return -VTD_FR_PASID_TABLE_INV; + return -VTD_FR_PASID_ENTRY_P; } return 0; @@ -895,7 +991,7 @@ static int vtd_ce_get_pasid_fpd(IntelIOMMUState *s, } if (!vtd_pdire_present(&pdire)) { - return -VTD_FR_PASID_TABLE_INV; + return -VTD_FR_PASID_DIR_ENTRY_P; } /* @@ -930,7 +1026,11 @@ static uint32_t vtd_get_iova_level(IntelIOMMUState *s, if (s->root_scalable) { vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid); - return VTD_PE_GET_LEVEL(&pe); + if (s->flts) { + return VTD_PE_GET_FL_LEVEL(&pe); + } else { + return VTD_PE_GET_SL_LEVEL(&pe); + } } return vtd_ce_get_level(ce); @@ -998,9 +1098,9 @@ static inline uint64_t vtd_iova_limit(IntelIOMMUState *s, } /* Return true if IOVA passes range check, otherwise false. */ -static inline bool vtd_iova_range_check(IntelIOMMUState *s, - uint64_t iova, VTDContextEntry *ce, - uint8_t aw, uint32_t pasid) +static inline bool vtd_iova_sl_range_check(IntelIOMMUState *s, + uint64_t iova, VTDContextEntry *ce, + uint8_t aw, uint32_t pasid) { /* * Check if @iova is above 2^X-1, where X is the minimum of MGAW @@ -1017,7 +1117,11 @@ static dma_addr_t vtd_get_iova_pgtbl_base(IntelIOMMUState *s, if (s->root_scalable) { vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid); - return pe.val[0] & VTD_SM_PASID_ENTRY_SLPTPTR; + if (s->flts) { + return pe.val[2] & VTD_SM_PASID_ENTRY_FLPTPTR; + } else { + return pe.val[0] & VTD_SM_PASID_ENTRY_SLPTPTR; + } } return vtd_ce_get_slpt_base(ce); @@ -1027,18 +1131,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_sl_level_supported. + */ + assert(level < VTD_SPTE_RSVD_LEN); + /* + * Zero level doesn't exist. The smallest level is VTD_PT_LEVEL=1 and + * checked by vtd_is_last_pte(). + */ + assert(level); - if ((level == VTD_SL_PD_LEVEL || level == VTD_SL_PDP_LEVEL) && - (slpte & VTD_SL_PT_PAGE_SIZE_MASK)) { + if ((level == VTD_PD_LEVEL || level == VTD_PDP_LEVEL) && + (slpte & VTD_PT_PAGE_SIZE_MASK)) { /* large page */ rsvd_mask = vtd_spte_rsvd_large[level]; + } else { + rsvd_mask = vtd_spte_rsvd[level]; } return slpte & rsvd_mask; @@ -1058,9 +1179,8 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, uint32_t offset; uint64_t slpte; uint64_t access_right_check; - uint64_t xlat, size; - if (!vtd_iova_range_check(s, iova, ce, aw_bits, pasid)) { + if (!vtd_iova_sl_range_check(s, iova, ce, aw_bits, pasid)) { error_report_once("%s: detected IOVA overflow (iova=0x%" PRIx64 "," "pasid=0x%" PRIx32 ")", __func__, iova, pasid); return -VTD_FR_ADDR_BEYOND_MGAW; @@ -1071,7 +1191,7 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, while (true) { offset = vtd_iova_level_offset(iova, level); - slpte = vtd_get_slpte(addr, offset); + slpte = vtd_get_pte(addr, offset); if (slpte == (uint64_t)-1) { error_report_once("%s: detected read error on DMAR slpte " @@ -1102,40 +1222,19 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, return -VTD_FR_PAGING_ENTRY_RSVD; } - if (vtd_is_last_slpte(slpte, level)) { + if (vtd_is_last_pte(slpte, level)) { *slptep = slpte; *slpte_level = level; break; } - addr = vtd_get_slpte_addr(slpte, aw_bits); + addr = vtd_get_pte_addr(slpte, aw_bits); level--; } - xlat = vtd_get_slpte_addr(*slptep, aw_bits); - size = ~vtd_slpt_level_page_mask(level) + 1; - - /* - * From VT-d spec 3.14: Untranslated requests and translation - * requests that result in an address in the interrupt range will be - * blocked with condition code LGN.4 or SGN.8. - */ - if ((xlat > VTD_INTERRUPT_ADDR_LAST || - xlat + size - 1 < VTD_INTERRUPT_ADDR_FIRST)) { - return 0; - } else { - error_report_once("%s: xlat address is in interrupt range " - "(iova=0x%" PRIx64 ", level=0x%" PRIx32 ", " - "slpte=0x%" PRIx64 ", write=%d, " - "xlat=0x%" PRIx64 ", size=0x%" PRIx64 ", " - "pasid=0x%" PRIx32 ")", - __func__, iova, level, slpte, is_write, - xlat, size, pasid); - return s->scalable_mode ? -VTD_FR_SM_INTERRUPT_ADDR : - -VTD_FR_INTERRUPT_ADDR; - } + return 0; } -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 @@ -1263,14 +1362,14 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, trace_vtd_page_walk_level(addr, level, start, end); - subpage_size = 1ULL << vtd_slpt_level_shift(level); - subpage_mask = vtd_slpt_level_page_mask(level); + subpage_size = 1ULL << vtd_pt_level_shift(level); + subpage_mask = vtd_pt_level_page_mask(level); while (iova < end) { iova_next = (iova & subpage_mask) + subpage_size; offset = vtd_iova_level_offset(iova, level); - slpte = vtd_get_slpte(addr, offset); + slpte = vtd_get_pte(addr, offset); if (slpte == (uint64_t)-1) { trace_vtd_page_walk_skip_read(iova, iova_next); @@ -1293,12 +1392,12 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, */ entry_valid = read_cur | write_cur; - if (!vtd_is_last_slpte(slpte, level) && entry_valid) { + if (!vtd_is_last_pte(slpte, level) && entry_valid) { /* * This is a valid PDE (or even bigger than PDE). We need * to walk one further level. */ - ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, info->aw), + ret = vtd_page_walk_level(vtd_get_pte_addr(slpte, info->aw), iova, MIN(iova_next, end), level - 1, read_cur, write_cur, info); } else { @@ -1315,7 +1414,7 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, event.entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); event.entry.addr_mask = ~subpage_mask; /* NOTE: this is only meaningful if entry_valid == true */ - event.entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw); + event.entry.translated_addr = vtd_get_pte_addr(slpte, info->aw); event.type = event.entry.perm ? IOMMU_NOTIFIER_MAP : IOMMU_NOTIFIER_UNMAP; ret = vtd_page_walk_one(&event, info); @@ -1349,11 +1448,11 @@ static int vtd_page_walk(IntelIOMMUState *s, VTDContextEntry *ce, dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce, pasid); uint32_t level = vtd_get_iova_level(s, ce, pasid); - if (!vtd_iova_range_check(s, start, ce, info->aw, pasid)) { + if (!vtd_iova_sl_range_check(s, start, ce, info->aw, pasid)) { return -VTD_FR_ADDR_BEYOND_MGAW; } - if (!vtd_iova_range_check(s, end, ce, info->aw, pasid)) { + if (!vtd_iova_sl_range_check(s, end, ce, info->aw, pasid)) { /* Fix end so that it reaches the maximum */ end = vtd_iova_limit(s, ce, info->aw, pasid); } @@ -1468,7 +1567,7 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, /* Check if the programming of context-entry is valid */ if (!s->root_scalable && - !vtd_is_level_supported(s, vtd_ce_get_level(ce))) { + !vtd_is_sl_level_supported(s, vtd_ce_get_level(ce))) { error_report_once("%s: invalid context entry: hi=%"PRIx64 ", lo=%"PRIx64" (level %d not supported)", __func__, ce->hi, ce->lo, @@ -1498,7 +1597,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); @@ -1629,8 +1728,6 @@ static bool vtd_as_pt_enabled(VTDAddressSpace *as) 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(); assert(as); @@ -1647,9 +1744,7 @@ static bool vtd_switch_address_space(VTDAddressSpace *as) * from vtd_pt_enable_fast_path(). However the memory APIs need * it. We'd better make sure we have had it already, or, take it. */ - if (take_bql) { - qemu_mutex_lock_iothread(); - } + BQL_LOCK_GUARD(); /* Turn off first then on the other */ if (use_iommu) { @@ -1694,7 +1789,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); @@ -1702,10 +1797,6 @@ static bool vtd_switch_address_space(VTDAddressSpace *as) memory_region_set_enabled(&as->iommu_ir_fault, false); } - if (take_bql) { - qemu_mutex_unlock_iothread(); - } - return use_iommu; } @@ -1735,8 +1826,20 @@ static const bool vtd_qualified_faults[] = { [VTD_FR_ROOT_ENTRY_RSVD] = false, [VTD_FR_PAGING_ENTRY_RSVD] = true, [VTD_FR_CONTEXT_ENTRY_TT] = true, - [VTD_FR_PASID_TABLE_INV] = false, + [VTD_FR_PASID_DIR_ACCESS_ERR] = false, + [VTD_FR_PASID_DIR_ENTRY_P] = true, + [VTD_FR_PASID_TABLE_ACCESS_ERR] = false, + [VTD_FR_PASID_ENTRY_P] = true, + [VTD_FR_PASID_TABLE_ENTRY_INV] = true, + [VTD_FR_FS_PAGING_ENTRY_INV] = true, + [VTD_FR_FS_PAGING_ENTRY_P] = true, + [VTD_FR_FS_PAGING_ENTRY_RSVD] = true, + [VTD_FR_PASID_ENTRY_FSPTPTR_INV] = true, + [VTD_FR_FS_NON_CANONICAL] = true, + [VTD_FR_FS_PAGING_ENTRY_US] = true, + [VTD_FR_SM_WRITE] = true, [VTD_FR_SM_INTERRUPT_ADDR] = true, + [VTD_FR_FS_BIT_UPDATE_FAILED] = true, [VTD_FR_MAX] = false, }; @@ -1754,29 +1857,32 @@ static inline bool vtd_is_interrupt_addr(hwaddr addr) return VTD_INTERRUPT_ADDR_FIRST <= addr && addr <= VTD_INTERRUPT_ADDR_LAST; } -static gboolean vtd_find_as_by_sid(gpointer key, gpointer value, - gpointer user_data) +static gboolean vtd_find_as_by_sid_and_pasid(gpointer key, gpointer value, + gpointer user_data) { struct vtd_as_key *as_key = (struct vtd_as_key *)key; - uint16_t target_sid = *(uint16_t *)user_data; + struct vtd_as_raw_key *target = (struct vtd_as_raw_key *)user_data; uint16_t sid = PCI_BUILD_BDF(pci_bus_num(as_key->bus), as_key->devfn); - return sid == target_sid; + + return (as_key->pasid == target->pasid) && (sid == target->sid); } -static VTDAddressSpace *vtd_get_as_by_sid(IntelIOMMUState *s, uint16_t sid) +static VTDAddressSpace *vtd_get_as_by_sid_and_pasid(IntelIOMMUState *s, + uint16_t sid, + uint32_t pasid) { - uint8_t bus_num = PCI_BUS_NUM(sid); - VTDAddressSpace *vtd_as = s->vtd_as_cache[bus_num]; - - if (vtd_as && - (sid == PCI_BUILD_BDF(pci_bus_num(vtd_as->bus), vtd_as->devfn))) { - return vtd_as; - } + struct vtd_as_raw_key key = { + .sid = sid, + .pasid = pasid + }; - vtd_as = g_hash_table_find(s->vtd_address_spaces, vtd_find_as_by_sid, &sid); - s->vtd_as_cache[bus_num] = vtd_as; + return g_hash_table_find(s->vtd_address_spaces, + vtd_find_as_by_sid_and_pasid, &key); +} - return vtd_as; +static VTDAddressSpace *vtd_get_as_by_sid(IntelIOMMUState *s, uint16_t sid) +{ + return vtd_get_as_by_sid_and_pasid(s, sid, PCI_NO_PASID); } static void vtd_pt_enable_fast_path(IntelIOMMUState *s, uint16_t source_id) @@ -1798,6 +1904,157 @@ static void vtd_pt_enable_fast_path(IntelIOMMUState *s, uint16_t source_id) trace_vtd_pt_enable_fast_path(source_id, success); } +/* + * Rsvd field masks for fpte: + * vtd_fpte_rsvd 4k pages + * vtd_fpte_rsvd_large large pages + * + * We support only 4-level page tables. + */ +#define VTD_FPTE_RSVD_LEN 5 +static uint64_t vtd_fpte_rsvd[VTD_FPTE_RSVD_LEN]; +static uint64_t vtd_fpte_rsvd_large[VTD_FPTE_RSVD_LEN]; + +static bool vtd_flpte_nonzero_rsvd(uint64_t flpte, uint32_t level) +{ + uint64_t rsvd_mask; + + /* + * We should have caught a guest-mis-programmed level earlier, + * via vtd_is_fl_level_supported. + */ + assert(level < VTD_FPTE_RSVD_LEN); + /* + * Zero level doesn't exist. The smallest level is VTD_PT_LEVEL=1 and + * checked by vtd_is_last_pte(). + */ + assert(level); + + if ((level == VTD_PD_LEVEL || level == VTD_PDP_LEVEL) && + (flpte & VTD_PT_PAGE_SIZE_MASK)) { + /* large page */ + rsvd_mask = vtd_fpte_rsvd_large[level]; + } else { + rsvd_mask = vtd_fpte_rsvd[level]; + } + + return flpte & rsvd_mask; +} + +static inline bool vtd_flpte_present(uint64_t flpte) +{ + return !!(flpte & VTD_FL_P); +} + +/* Return true if IOVA is canonical, otherwise false. */ +static bool vtd_iova_fl_check_canonical(IntelIOMMUState *s, uint64_t iova, + VTDContextEntry *ce, uint32_t pasid) +{ + uint64_t iova_limit = vtd_iova_limit(s, ce, s->aw_bits, pasid); + uint64_t upper_bits_mask = ~(iova_limit - 1); + uint64_t upper_bits = iova & upper_bits_mask; + bool msb = ((iova & (iova_limit >> 1)) != 0); + + if (msb) { + return upper_bits == upper_bits_mask; + } else { + return !upper_bits; + } +} + +static MemTxResult vtd_set_flag_in_pte(dma_addr_t base_addr, uint32_t index, + uint64_t pte, uint64_t flag) +{ + if (pte & flag) { + return MEMTX_OK; + } + pte |= flag; + pte = cpu_to_le64(pte); + return dma_memory_write(&address_space_memory, + base_addr + index * sizeof(pte), + &pte, sizeof(pte), + MEMTXATTRS_UNSPECIFIED); +} + +/* + * Given the @iova, get relevant @flptep. @flpte_level will be the last level + * of the translation, can be used for deciding the size of large page. + */ +static int vtd_iova_to_flpte(IntelIOMMUState *s, VTDContextEntry *ce, + uint64_t iova, bool is_write, + uint64_t *flptep, uint32_t *flpte_level, + bool *reads, bool *writes, uint8_t aw_bits, + uint32_t pasid) +{ + dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce, pasid); + uint32_t level = vtd_get_iova_level(s, ce, pasid); + uint32_t offset; + uint64_t flpte, flag_ad = VTD_FL_A; + + if (!vtd_iova_fl_check_canonical(s, iova, ce, pasid)) { + error_report_once("%s: detected non canonical IOVA (iova=0x%" PRIx64 "," + "pasid=0x%" PRIx32 ")", __func__, iova, pasid); + return -VTD_FR_FS_NON_CANONICAL; + } + + while (true) { + offset = vtd_iova_level_offset(iova, level); + flpte = vtd_get_pte(addr, offset); + + if (flpte == (uint64_t)-1) { + if (level == vtd_get_iova_level(s, ce, pasid)) { + /* Invalid programming of pasid-entry */ + return -VTD_FR_PASID_ENTRY_FSPTPTR_INV; + } else { + return -VTD_FR_FS_PAGING_ENTRY_INV; + } + } + + if (!vtd_flpte_present(flpte)) { + *reads = false; + *writes = false; + return -VTD_FR_FS_PAGING_ENTRY_P; + } + + /* No emulated device supports supervisor privilege request yet */ + if (!(flpte & VTD_FL_US)) { + *reads = false; + *writes = false; + return -VTD_FR_FS_PAGING_ENTRY_US; + } + + *reads = true; + *writes = (*writes) && (flpte & VTD_FL_RW); + if (is_write && !(flpte & VTD_FL_RW)) { + return -VTD_FR_SM_WRITE; + } + if (vtd_flpte_nonzero_rsvd(flpte, level)) { + error_report_once("%s: detected flpte reserved non-zero " + "iova=0x%" PRIx64 ", level=0x%" PRIx32 + "flpte=0x%" PRIx64 ", pasid=0x%" PRIX32 ")", + __func__, iova, level, flpte, pasid); + return -VTD_FR_FS_PAGING_ENTRY_RSVD; + } + + if (vtd_is_last_pte(flpte, level) && is_write) { + flag_ad |= VTD_FL_D; + } + + if (vtd_set_flag_in_pte(addr, offset, flpte, flag_ad) != MEMTX_OK) { + return -VTD_FR_FS_BIT_UPDATE_FAILED; + } + + if (vtd_is_last_pte(flpte, level)) { + *flptep = flpte; + *flpte_level = level; + return 0; + } + + addr = vtd_get_pte_addr(flpte, aw_bits); + level--; + } +} + static void vtd_report_fault(IntelIOMMUState *s, int err, bool is_fpd_set, uint16_t source_id, @@ -1834,16 +2091,17 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, VTDContextEntry ce; uint8_t bus_num = pci_bus_num(bus); VTDContextCacheEntry *cc_entry; - uint64_t slpte, page_mask; + uint64_t pte, page_mask; uint32_t level, pasid = vtd_as->pasid; uint16_t source_id = PCI_BUILD_BDF(bus_num, devfn); int ret_fr; bool is_fpd_set = false; bool reads = true; bool writes = true; - uint8_t access_flags; + uint8_t access_flags, pgtt; bool rid2pasid = (pasid == PCI_NO_PASID) && s->root_scalable; VTDIOTLBEntry *iotlb_entry; + uint64_t xlat, size; /* * We have standalone memory region for interrupt addresses, we @@ -1855,13 +2113,13 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, cc_entry = &vtd_as->context_cache_entry; - /* Try to fetch slpte form IOTLB, we don't need RID2PASID logic */ + /* Try to fetch pte from IOTLB, we don't need RID2PASID logic */ if (!rid2pasid) { iotlb_entry = vtd_lookup_iotlb(s, source_id, pasid, addr); if (iotlb_entry) { - trace_vtd_iotlb_page_hit(source_id, addr, iotlb_entry->slpte, + trace_vtd_iotlb_page_hit(source_id, addr, iotlb_entry->pte, iotlb_entry->domain_id); - slpte = iotlb_entry->slpte; + pte = iotlb_entry->pte; access_flags = iotlb_entry->access_flags; page_mask = iotlb_entry->mask; goto out; @@ -1933,35 +2191,65 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, return true; } - /* Try to fetch slpte form IOTLB for RID2PASID slow path */ + /* Try to fetch pte from IOTLB for RID2PASID slow path */ if (rid2pasid) { iotlb_entry = vtd_lookup_iotlb(s, source_id, pasid, addr); if (iotlb_entry) { - trace_vtd_iotlb_page_hit(source_id, addr, iotlb_entry->slpte, + trace_vtd_iotlb_page_hit(source_id, addr, iotlb_entry->pte, iotlb_entry->domain_id); - slpte = iotlb_entry->slpte; + pte = iotlb_entry->pte; access_flags = iotlb_entry->access_flags; page_mask = iotlb_entry->mask; goto out; } } - ret_fr = vtd_iova_to_slpte(s, &ce, addr, is_write, &slpte, &level, - &reads, &writes, s->aw_bits, pasid); + if (s->flts && s->root_scalable) { + ret_fr = vtd_iova_to_flpte(s, &ce, addr, is_write, &pte, &level, + &reads, &writes, s->aw_bits, pasid); + pgtt = VTD_SM_PASID_ENTRY_FLT; + } else { + ret_fr = vtd_iova_to_slpte(s, &ce, addr, is_write, &pte, &level, + &reads, &writes, s->aw_bits, pasid); + pgtt = VTD_SM_PASID_ENTRY_SLT; + } + if (!ret_fr) { + xlat = vtd_get_pte_addr(pte, s->aw_bits); + size = ~vtd_pt_level_page_mask(level) + 1; + + /* + * Per VT-d spec 4.1 section 3.15: Untranslated requests and translation + * requests that result in an address in the interrupt range will be + * blocked with condition code LGN.4 or SGN.8. + */ + if ((xlat <= VTD_INTERRUPT_ADDR_LAST && + xlat + size - 1 >= VTD_INTERRUPT_ADDR_FIRST)) { + error_report_once("%s: xlat address is in interrupt range " + "(iova=0x%" PRIx64 ", level=0x%" PRIx32 ", " + "pte=0x%" PRIx64 ", write=%d, " + "xlat=0x%" PRIx64 ", size=0x%" PRIx64 ", " + "pasid=0x%" PRIx32 ")", + __func__, addr, level, pte, is_write, + xlat, size, pasid); + ret_fr = s->scalable_mode ? -VTD_FR_SM_INTERRUPT_ADDR : + -VTD_FR_INTERRUPT_ADDR; + } + } + if (ret_fr) { vtd_report_fault(s, -ret_fr, is_fpd_set, source_id, addr, is_write, pasid != PCI_NO_PASID, pasid); goto error; } - page_mask = vtd_slpt_level_page_mask(level); + page_mask = vtd_pt_level_page_mask(level); access_flags = IOMMU_ACCESS_FLAG(reads, writes); vtd_update_iotlb(s, source_id, vtd_get_domain_id(s, &ce, pasid), - addr, slpte, access_flags, level, pasid); + addr, pte, access_flags, level, pasid, pgtt); out: vtd_iommu_unlock(s); entry->iova = addr & page_mask; - entry->translated_addr = vtd_get_slpte_addr(slpte, s->aw_bits) & page_mask; + entry->translated_addr = vtd_get_pte_addr(pte, s->aw_bits) & page_mask; entry->addr_mask = ~page_mask; entry->perm = access_flags; return true; @@ -2155,8 +2443,13 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) } } +/* + * There is no pasid field in iotlb invalidation descriptor, so PCI_NO_PASID + * is passed as parameter. Piotlb invalidation supports pasid, pasid in its + * descriptor is passed which should not be PCI_NO_PASID. + */ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, - uint16_t domain_id, hwaddr addr, + uint16_t domain_id, hwaddr addr, uint8_t am, uint32_t pasid) { VTDAddressSpace *vtd_as; @@ -2165,26 +2458,44 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, hwaddr size = (1 << am) * VTD_PAGE_SIZE; QLIST_FOREACH(vtd_as, &(s->vtd_as_with_notifiers), next) { - if (pasid != PCI_NO_PASID && pasid != vtd_as->pasid) { - continue; - } ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce); if (!ret && domain_id == vtd_get_domain_id(s, &ce, vtd_as->pasid)) { + uint32_t rid2pasid = PCI_NO_PASID; + + if (s->root_scalable) { + rid2pasid = VTD_CE_GET_RID2PASID(&ce); + } + + /* + * In legacy mode, vtd_as->pasid == pasid is always true. + * In scalable mode, for vtd address space backing a PCI + * device without pasid, needs to compare pasid with + * rid2pasid of this device. + */ + if (!(vtd_as->pasid == pasid || + (vtd_as->pasid == PCI_NO_PASID && pasid == rid2pasid))) { + continue; + } + if (vtd_as_has_map_notifier(vtd_as)) { /* - * As long as we have MAP notifications registered in - * any of our IOMMU notifiers, we need to sync the - * shadow page table. + * When stage-1 translation is off, as long as we have MAP + * notifications registered in any of our IOMMU notifiers, + * we need to sync the shadow page table. Otherwise VFIO + * device attaches to nested page table instead of shadow + * page table, so no need to sync. */ - vtd_sync_shadow_page_table_range(vtd_as, &ce, addr, size); + if (!s->flts || !s->root_scalable) { + vtd_sync_shadow_page_table_range(vtd_as, &ce, addr, size); + } } else { /* * For UNMAP-only notifiers, we don't need to walk the * 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, @@ -2472,15 +2783,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 >> @@ -2514,13 +2861,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( @@ -2550,12 +2898,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; } @@ -2593,51 +2940,132 @@ static bool vtd_process_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) return true; } -static bool vtd_process_inv_iec_desc(IntelIOMMUState *s, - VTDInvDesc *inv_desc) +static gboolean vtd_hash_remove_by_pasid(gpointer key, gpointer value, + gpointer user_data) { - trace_vtd_inv_desc_iec(inv_desc->iec.granularity, - inv_desc->iec.index, - inv_desc->iec.index_mask); + VTDIOTLBEntry *entry = (VTDIOTLBEntry *)value; + VTDIOTLBPageInvInfo *info = (VTDIOTLBPageInvInfo *)user_data; - vtd_iec_notify_all(s, !inv_desc->iec.granularity, - inv_desc->iec.index, - inv_desc->iec.index_mask); - return true; + return ((entry->domain_id == info->domain_id) && + (entry->pasid == info->pasid)); } -static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, - VTDInvDesc *inv_desc) +static void vtd_piotlb_pasid_invalidate(IntelIOMMUState *s, + uint16_t domain_id, uint32_t pasid) { - VTDAddressSpace *vtd_dev_as; - IOMMUTLBEvent event; - hwaddr addr; - uint64_t sz; - uint16_t sid; - bool size; + VTDIOTLBPageInvInfo info; + VTDAddressSpace *vtd_as; + VTDContextEntry ce; - 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); + info.domain_id = domain_id; + info.pasid = pasid; + + vtd_iommu_lock(s); + g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_pasid, + &info); + vtd_iommu_unlock(s); + + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { + if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce) && + domain_id == vtd_get_domain_id(s, &ce, vtd_as->pasid)) { + uint32_t rid2pasid = VTD_CE_GET_RID2PASID(&ce); + + if ((vtd_as->pasid != PCI_NO_PASID || pasid != rid2pasid) && + vtd_as->pasid != pasid) { + continue; + } + + if (!s->flts || !vtd_as_has_map_notifier(vtd_as)) { + vtd_address_space_sync(vtd_as); + } + } + } +} - 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); +static void vtd_piotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, + uint32_t pasid, hwaddr addr, uint8_t am) +{ + VTDIOTLBPageInvInfo info; + + info.domain_id = domain_id; + info.pasid = pasid; + info.addr = addr; + info.mask = ~((1 << am) - 1); + + vtd_iommu_lock(s); + g_hash_table_foreach_remove(s->iotlb, + vtd_hash_remove_by_page_piotlb, &info); + vtd_iommu_unlock(s); + + vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am, pasid); +} + +static bool vtd_process_piotlb_desc(IntelIOMMUState *s, + VTDInvDesc *inv_desc) +{ + uint16_t domain_id; + uint32_t pasid; + hwaddr addr; + uint8_t am; + uint64_t mask[4] = {VTD_INV_DESC_PIOTLB_RSVD_VAL0, + VTD_INV_DESC_PIOTLB_RSVD_VAL1, + VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE}; + + if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, true, + __func__, "piotlb inv")) { 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; + domain_id = VTD_INV_DESC_PIOTLB_DID(inv_desc->val[0]); + pasid = VTD_INV_DESC_PIOTLB_PASID(inv_desc->val[0]); + switch (inv_desc->val[0] & VTD_INV_DESC_PIOTLB_G) { + case VTD_INV_DESC_PIOTLB_ALL_IN_PASID: + vtd_piotlb_pasid_invalidate(s, domain_id, pasid); + break; + + case VTD_INV_DESC_PIOTLB_PSI_IN_PASID: + am = VTD_INV_DESC_PIOTLB_AM(inv_desc->val[1]); + addr = (hwaddr) VTD_INV_DESC_PIOTLB_ADDR(inv_desc->val[1]); + vtd_piotlb_page_invalidate(s, domain_id, pasid, addr, am); + break; + + default: + error_report_once("%s: invalid piotlb inv desc: hi=0x%"PRIx64 + ", lo=0x%"PRIx64" (type mismatch: 0x%llx)", + __func__, inv_desc->val[1], inv_desc->val[0], + inv_desc->val[0] & VTD_INV_DESC_IOTLB_G); + return false; + } + return true; +} + +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; } - /* According to ATS spec table 2.4: + trace_vtd_inv_desc_iec(inv_desc->iec.granularity, + inv_desc->iec.index, + inv_desc->iec.index_mask); + + vtd_iec_notify_all(s, !inv_desc->iec.granularity, + inv_desc->iec.index, + inv_desc->iec.index_mask); + return true; +} + +static void do_invalidate_device_tlb(VTDAddressSpace *vtd_dev_as, + bool size, hwaddr addr) +{ + /* + * 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 @@ -2645,6 +3073,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); @@ -2659,6 +3091,81 @@ 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_piotlb_desc(IntelIOMMUState *s, + VTDInvDesc *inv_desc) +{ + uint16_t sid; + VTDAddressSpace *vtd_dev_as; + bool size; + bool global; + hwaddr addr; + uint32_t pasid; + uint64_t mask[4] = {VTD_INV_DESC_PASID_DEVICE_IOTLB_RSVD_VAL0, + VTD_INV_DESC_PASID_DEVICE_IOTLB_RSVD_VAL1, + VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE}; + + if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, true, + __func__, "device piotlb inv")) { + return false; + } + + global = VTD_INV_DESC_PASID_DEVICE_IOTLB_GLOBAL(inv_desc->hi); + size = VTD_INV_DESC_PASID_DEVICE_IOTLB_SIZE(inv_desc->hi); + addr = VTD_INV_DESC_PASID_DEVICE_IOTLB_ADDR(inv_desc->hi); + sid = VTD_INV_DESC_PASID_DEVICE_IOTLB_SID(inv_desc->lo); + if (global) { + QLIST_FOREACH(vtd_dev_as, &s->vtd_as_with_notifiers, next) { + if ((vtd_dev_as->pasid != PCI_NO_PASID) && + (PCI_BUILD_BDF(pci_bus_num(vtd_dev_as->bus), + vtd_dev_as->devfn) == sid)) { + do_invalidate_device_tlb(vtd_dev_as, size, addr); + } + } + } else { + pasid = VTD_INV_DESC_PASID_DEVICE_IOTLB_PASID(inv_desc->lo); + vtd_dev_as = vtd_get_as_by_sid_and_pasid(s, sid, pasid); + if (!vtd_dev_as) { + return true; + } + + do_invalidate_device_tlb(vtd_dev_as, size, addr); + } + + return true; +} + +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; @@ -2675,7 +3182,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; @@ -2694,15 +3201,11 @@ 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: + trace_vtd_inv_desc("p-iotlb", inv_desc.val[1], inv_desc.val[0]); + if (!vtd_process_piotlb_desc(s, &inv_desc)) { + return false; + } break; case VTD_INV_DESC_WAIT: @@ -2719,6 +3222,13 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) } break; + case VTD_INV_DESC_DEV_PIOTLB: + trace_vtd_inv_desc("device-piotlb", inv_desc.hi, inv_desc.lo); + if (!vtd_process_device_piotlb_desc(s, &inv_desc)) { + return false; + } + break; + case VTD_INV_DESC_DEVICE: trace_vtd_inv_desc("device", inv_desc.hi, inv_desc.lo); if (!vtd_process_device_iotlb_desc(s, &inv_desc)) { @@ -2726,6 +3236,16 @@ 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: + if (s->scalable_mode) { + break; + } + /* fallthrough */ default: error_report_once("%s: invalid inv desc: hi=%"PRIx64", lo=%"PRIx64 " (unknown type)", __func__, inv_desc.hi, @@ -2778,6 +3298,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); @@ -2878,7 +3399,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); } @@ -3254,7 +3777,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), @@ -3288,7 +3811,7 @@ static const MemoryRegionOps vtd_mem_ops = { }, }; -static Property vtd_properties[] = { +static const Property vtd_properties[] = { DEFINE_PROP_UINT32("version", IntelIOMMUState, version, 0), DEFINE_PROP_ON_OFF_AUTO("eim", IntelIOMMUState, intr_eim, ON_OFF_AUTO_AUTO), @@ -3297,16 +3820,19 @@ static Property vtd_properties[] = { VTD_HOST_ADDRESS_WIDTH), DEFINE_PROP_BOOL("caching-mode", IntelIOMMUState, caching_mode, FALSE), DEFINE_PROP_BOOL("x-scalable-mode", IntelIOMMUState, scalable_mode, FALSE), + DEFINE_PROP_BOOL("x-flts", IntelIOMMUState, flts, FALSE), DEFINE_PROP_BOOL("snoop-control", IntelIOMMUState, snoop_control, false), DEFINE_PROP_BOOL("x-pasid-mode", IntelIOMMUState, pasid, false), DEFINE_PROP_BOOL("dma-drain", IntelIOMMUState, dma_drain, true), DEFINE_PROP_BOOL("dma-translation", IntelIOMMUState, dma_translation, true), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_BOOL("stale-tm", IntelIOMMUState, stale_tm, false), + DEFINE_PROP_BOOL("fs1gp", IntelIOMMUState, fs1gp, true), }; /* 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}; @@ -3317,7 +3843,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); @@ -3325,7 +3854,10 @@ 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; } entry->data[0] = le64_to_cpu(entry->data[0]); @@ -3333,11 +3865,24 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, 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, entry->data[1], entry->data[0]); - return -VTD_FR_IR_ENTRY_P; + 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 || @@ -3345,7 +3890,10 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, error_report_once("%s: detected non-zero reserved IRTE " "(index=%u, high=0x%" PRIx64 ", low=0x%" PRIx64 ")", __func__, index, entry->data[1], entry->data[0]); - return -VTD_FR_IR_IRTE_RSVD; + if (do_fault) { + vtd_report_ir_fault(iommu, sid, VTD_FR_IR_IRTE_RSVD, index); + } + return false; } if (sid != X86_IOMMU_SID_INVALID) { @@ -3361,7 +3909,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; @@ -3373,7 +3924,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; @@ -3382,23 +3936,24 @@ 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; @@ -3417,16 +3972,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 = {}; @@ -3443,14 +3997,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. */ @@ -3469,9 +4029,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) { @@ -3481,7 +4040,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; @@ -3521,7 +4083,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, @@ -3547,9 +4109,8 @@ 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; } @@ -3644,9 +4205,30 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, VTDAddressSpace *vtd_dev_as; char name[128]; + vtd_iommu_lock(s); vtd_dev_as = g_hash_table_lookup(s->vtd_address_spaces, &key); + vtd_iommu_unlock(s); + if (!vtd_dev_as) { - struct vtd_as_key *new_key = g_malloc(sizeof(*new_key)); + struct vtd_as_key *new_key; + /* Slow path */ + + /* + * memory_region_add_subregion_overlap requires the bql, + * make sure we own it. + */ + BQL_LOCK_GUARD(); + vtd_iommu_lock(s); + + /* Check again as we released the lock for a moment */ + vtd_dev_as = g_hash_table_lookup(s->vtd_address_spaces, &key); + if (vtd_dev_as) { + vtd_iommu_unlock(s); + return vtd_dev_as; + } + + /* Still nothing, allocate a new address space */ + new_key = g_malloc(sizeof(*new_key)); new_key->bus = bus; new_key->devfn = devfn; @@ -3737,14 +4319,103 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, vtd_switch_address_space(vtd_dev_as); g_hash_table_insert(s->vtd_address_spaces, new_key, vtd_dev_as); + + vtd_iommu_unlock(s); } 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; + } + + if (!s->flts) { + /* All checks requested by VTD stage-2 translation pass */ + return true; + } + + error_setg(errp, "host device is uncompatible with stage-1 translation"); + return false; +} + +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; @@ -3765,7 +4436,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; @@ -3793,10 +4464,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 - 1; /* Inclusive */ + map.size = total - 1; /* Inclusive */ iova_tree_remove(as->iova_tree, map); } @@ -3818,7 +4489,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; @@ -3859,34 +4530,12 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn), PCI_FUNC(vtd_as->devfn)); } - - 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); @@ -3903,27 +4552,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) { @@ -3945,7 +4573,12 @@ static void vtd_init(IntelIOMMUState *s) } /* TODO: read cap/ecap from host to decide which cap to be exposed. */ - if (s->scalable_mode) { + if (s->flts) { + s->ecap |= VTD_ECAP_SMTS | VTD_ECAP_FLTS; + if (s->fs1gp) { + s->cap |= VTD_CAP_FS1GP; + } + } else if (s->scalable_mode) { s->ecap |= VTD_ECAP_SMTS | VTD_ECAP_SRS | VTD_ECAP_SLTS; } @@ -3956,6 +4589,68 @@ 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); + + /* + * Rsvd field masks for fpte + */ + vtd_fpte_rsvd[0] = ~0ULL; + vtd_fpte_rsvd[1] = VTD_FPTE_PAGE_L1_RSVD_MASK(s->aw_bits); + vtd_fpte_rsvd[2] = VTD_FPTE_PAGE_L2_RSVD_MASK(s->aw_bits); + vtd_fpte_rsvd[3] = VTD_FPTE_PAGE_L3_RSVD_MASK(s->aw_bits); + vtd_fpte_rsvd[4] = VTD_FPTE_PAGE_L4_RSVD_MASK(s->aw_bits); + + vtd_fpte_rsvd_large[2] = VTD_FPTE_LPAGE_L2_RSVD_MASK(s->aw_bits); + vtd_fpte_rsvd_large[3] = VTD_FPTE_LPAGE_L3_RSVD_MASK(s->aw_bits); + + 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); @@ -4015,10 +4710,11 @@ static void vtd_init(IntelIOMMUState *s) /* Should not reset address_spaces when reset because devices will still use * the address space they got at first (won't ask the bus again). */ -static void vtd_reset(DeviceState *dev) +static void vtd_reset_exit(Object *obj, ResetType type) { - IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev); + IntelIOMMUState *s = INTEL_IOMMU_DEVICE(obj); + trace_vtd_reset_exit(); vtd_init(s); vtd_address_space_refresh_all(s); } @@ -4034,6 +4730,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); @@ -4049,25 +4751,33 @@ 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_enabled() && !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; } } - /* Currently only address widths supported are 39 and 48 bits */ - if ((s->aw_bits != VTD_HOST_AW_39BIT) && - (s->aw_bits != VTD_HOST_AW_48BIT)) { - error_setg(errp, "Supported values for aw-bits are: %d, %d", + if (!s->scalable_mode && s->flts) { + error_setg(errp, "x-flts is only available in scalable mode"); + return false; + } + + if (!s->flts && s->aw_bits != VTD_HOST_AW_39BIT && + s->aw_bits != VTD_HOST_AW_48BIT) { + error_setg(errp, "%s: supported values for aw-bits are: %d, %d", + s->scalable_mode ? "Scalable mode(flts=off)" : "Legacy mode", VTD_HOST_AW_39BIT, VTD_HOST_AW_48BIT); return false; } + if (s->flts && s->aw_bits != VTD_HOST_AW_48BIT) { + error_setg(errp, + "Scalable mode(flts=on): supported value for aw-bits is: %d", + VTD_HOST_AW_48BIT); + return false; + } + if (s->scalable_mode && !s->dma_drain) { error_setg(errp, "Need to set dma_drain for scalable mode"); return false; @@ -4112,7 +4822,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); @@ -4134,6 +4844,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", @@ -4148,34 +4860,36 @@ 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); } -static void vtd_class_init(ObjectClass *klass, void *data) +static void vtd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); X86IOMMUClass *x86_class = X86_IOMMU_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); - dc->reset = vtd_reset; + /* + * Use 'exit' reset phase to make sure all DMA requests + * have been quiesced during 'enter' or 'hold' phase + */ + rc->phases.exit = vtd_reset_exit; dc->vmsd = &vtd_vmstate; device_class_set_props(dc, vtd_properties); dc->hotpluggable = false; x86_class->realize = vtd_realize; x86_class->int_remap = vtd_int_remap; - /* Supported by the pc-q35-* machine types */ - dc->user_creatable = true; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->desc = "Intel IOMMU (VT-d) DMA Remapping device"; } @@ -4188,7 +4902,7 @@ static const TypeInfo vtd_info = { }; static void vtd_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index e1450c5cfed..e8b211e8b04 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -195,6 +195,7 @@ #define VTD_ECAP_PASID (1ULL << 40) #define VTD_ECAP_SMTS (1ULL << 43) #define VTD_ECAP_SLTS (1ULL << 46) +#define VTD_ECAP_FLTS (1ULL << 47) /* CAP_REG */ /* (offset >> 4) << 24 */ @@ -211,6 +212,7 @@ #define VTD_CAP_SLLPS ((1ULL << 34) | (1ULL << 35)) #define VTD_CAP_DRAIN_WRITE (1ULL << 54) #define VTD_CAP_DRAIN_READ (1ULL << 55) +#define VTD_CAP_FS1GP (1ULL << 56) #define VTD_CAP_DRAIN (VTD_CAP_DRAIN_READ | VTD_CAP_DRAIN_WRITE) #define VTD_CAP_CM (1ULL << 7) #define VTD_PASID_ID_SHIFT 20 @@ -264,10 +266,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 { @@ -310,10 +313,28 @@ typedef enum VTDFaultReason { * request while disabled */ VTD_FR_IR_SID_ERR = 0x26, /* Invalid Source-ID */ - VTD_FR_PASID_TABLE_INV = 0x58, /*Invalid PASID table entry */ + /* PASID directory entry access failure */ + VTD_FR_PASID_DIR_ACCESS_ERR = 0x50, + /* The Present(P) field of pasid directory entry is 0 */ + VTD_FR_PASID_DIR_ENTRY_P = 0x51, + VTD_FR_PASID_TABLE_ACCESS_ERR = 0x58, /* PASID table entry access failure */ + /* The Present(P) field of pasid table entry is 0 */ + VTD_FR_PASID_ENTRY_P = 0x59, + VTD_FR_PASID_TABLE_ENTRY_INV = 0x5b, /*Invalid PASID table entry */ + + /* Fail to access a first-level paging entry (not FS_PML4E) */ + VTD_FR_FS_PAGING_ENTRY_INV = 0x70, + VTD_FR_FS_PAGING_ENTRY_P = 0x71, + /* Non-zero reserved field in present first-stage paging entry */ + VTD_FR_FS_PAGING_ENTRY_RSVD = 0x72, + VTD_FR_PASID_ENTRY_FSPTPTR_INV = 0x73, /* Invalid FSPTPTR in PASID entry */ + VTD_FR_FS_NON_CANONICAL = 0x80, /* SNG.1 : Address for FS not canonical.*/ + VTD_FR_FS_PAGING_ENTRY_US = 0x81, /* Privilege violation */ + VTD_FR_SM_WRITE = 0x85, /* No write permission */ /* Output address in the interrupt address range for scalable mode */ VTD_FR_SM_INTERRUPT_ADDR = 0x87, + VTD_FR_FS_BIT_UPDATE_FAILED = 0x91, /* SFS.10 */ VTD_FR_MAX, /* Guard */ } VTDFaultReason; @@ -355,7 +376,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 @@ -364,6 +387,7 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_INV_DESC_WAIT 0x5 /* Invalidation Wait Descriptor */ #define VTD_INV_DESC_PIOTLB 0x6 /* PASID-IOTLB Invalidate Desc */ #define VTD_INV_DESC_PC 0x7 /* PASID-cache Invalidate Desc */ +#define VTD_INV_DESC_DEV_PIOTLB 0x8 /* PASID-based-DIOTLB inv_desc*/ #define VTD_INV_DESC_NONE 0 /* Not an Invalidate Descriptor */ /* Masks for Invalidation Wait Descriptor*/ @@ -371,7 +395,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 */ @@ -382,7 +406,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) @@ -392,26 +416,34 @@ 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) -#define VTD_INV_DESC_IOTLB_PASID(val) (((val) >> 32) & VTD_PASID_ID_MASK) -#define VTD_INV_DESC_IOTLB_PASID_RSVD_LO 0xfff00000000001c0ULL -#define VTD_INV_DESC_IOTLB_PASID_RSVD_HI 0xf80ULL /* Mask for Device IOTLB Invalidate Descriptor */ #define VTD_INV_DESC_DEVICE_IOTLB_ADDR(val) ((val) & 0xfffffffffffff000ULL) #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 + +/* Masks for PASID based Device IOTLB Invalidate Descriptor */ +#define VTD_INV_DESC_PASID_DEVICE_IOTLB_ADDR(val) ((val) & \ + 0xfffffffffffff000ULL) +#define VTD_INV_DESC_PASID_DEVICE_IOTLB_SIZE(val) ((val >> 11) & 0x1) +#define VTD_INV_DESC_PASID_DEVICE_IOTLB_GLOBAL(val) ((val) & 0x1) +#define VTD_INV_DESC_PASID_DEVICE_IOTLB_SID(val) (((val) >> 16) & 0xffffULL) +#define VTD_INV_DESC_PASID_DEVICE_IOTLB_PASID(val) ((val >> 32) & 0xfffffULL) +#define VTD_INV_DESC_PASID_DEVICE_IOTLB_RSVD_VAL0 0xfff000000000f000ULL +#define VTD_INV_DESC_PASID_DEVICE_IOTLB_RSVD_VAL1 0x7feULL /* 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) \ @@ -421,21 +453,49 @@ 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)) +/* Rsvd field masks for fpte */ +#define VTD_FS_UPPER_IGNORED 0xfff0000000000000ULL +#define VTD_FPTE_PAGE_L1_RSVD_MASK(aw) \ + (~(VTD_HAW_MASK(aw) | VTD_FS_UPPER_IGNORED)) +#define VTD_FPTE_PAGE_L2_RSVD_MASK(aw) \ + (~(VTD_HAW_MASK(aw) | VTD_FS_UPPER_IGNORED)) +#define VTD_FPTE_PAGE_L3_RSVD_MASK(aw) \ + (~(VTD_HAW_MASK(aw) | VTD_FS_UPPER_IGNORED)) +#define VTD_FPTE_PAGE_L4_RSVD_MASK(aw) \ + (0x80ULL | ~(VTD_HAW_MASK(aw) | VTD_FS_UPPER_IGNORED)) + +#define VTD_FPTE_LPAGE_L2_RSVD_MASK(aw) \ + (0x1fe000ULL | ~(VTD_HAW_MASK(aw) | VTD_FS_UPPER_IGNORED)) +#define VTD_FPTE_LPAGE_L3_RSVD_MASK(aw) \ + (0x3fffe000ULL | ~(VTD_HAW_MASK(aw) | VTD_FS_UPPER_IGNORED)) + +/* Masks for PIOTLB Invalidate Descriptor */ +#define VTD_INV_DESC_PIOTLB_G (3ULL << 4) +#define VTD_INV_DESC_PIOTLB_ALL_IN_PASID (2ULL << 4) +#define VTD_INV_DESC_PIOTLB_PSI_IN_PASID (3ULL << 4) +#define VTD_INV_DESC_PIOTLB_DID(val) (((val) >> 16) & VTD_DOMAIN_ID_MASK) +#define VTD_INV_DESC_PIOTLB_PASID(val) (((val) >> 32) & 0xfffffULL) +#define VTD_INV_DESC_PIOTLB_AM(val) ((val) & 0x3fULL) +#define VTD_INV_DESC_PIOTLB_IH(val) (((val) >> 6) & 0x1) +#define VTD_INV_DESC_PIOTLB_ADDR(val) ((val) & ~0xfffULL) +#define VTD_INV_DESC_PIOTLB_RSVD_VAL0 0xfff000000000f1c0ULL +#define VTD_INV_DESC_PIOTLB_RSVD_VAL1 0xf80ULL + /* Information about page-selective IOTLB invalidate */ struct VTDIOTLBPageInvInfo { uint16_t domain_id; uint32_t pasid; uint64_t addr; - uint8_t mask; + uint64_t mask; }; typedef struct VTDIOTLBPageInvInfo VTDIOTLBPageInvInfo; @@ -513,27 +573,38 @@ typedef struct VTDRootEntry VTDRootEntry; #define VTD_SM_PASID_ENTRY_AW 7ULL /* Adjusted guest-address-width */ #define VTD_SM_PASID_ENTRY_DID(val) ((val) & VTD_DOMAIN_ID_MASK) +#define VTD_SM_PASID_ENTRY_FLPM 3ULL +#define VTD_SM_PASID_ENTRY_FLPTPTR (~0xfffULL) + +/* First Level Paging Structure */ +/* Masks for First Level Paging Entry */ +#define VTD_FL_P 1ULL +#define VTD_FL_RW (1ULL << 1) +#define VTD_FL_US (1ULL << 2) +#define VTD_FL_A (1ULL << 5) +#define VTD_FL_D (1ULL << 6) + /* Second Level Page Translation Pointer*/ #define VTD_SM_PASID_ENTRY_SLPTPTR (~0xfffULL) -/* Paging Structure common */ -#define VTD_SL_PT_PAGE_SIZE_MASK (1ULL << 7) -/* Bits to decide the offset for each level */ -#define VTD_SL_LEVEL_BITS 9 - /* Second Level Paging Structure */ -#define VTD_SL_PML4_LEVEL 4 -#define VTD_SL_PDP_LEVEL 3 -#define VTD_SL_PD_LEVEL 2 -#define VTD_SL_PT_LEVEL 1 -#define VTD_SL_PT_ENTRY_NR 512 - /* Masks for Second Level Paging Entry */ #define VTD_SL_RW_MASK 3ULL #define VTD_SL_R 1ULL #define VTD_SL_W (1ULL << 1) -#define VTD_SL_PT_BASE_ADDR_MASK(aw) (~(VTD_PAGE_SIZE - 1) & VTD_HAW_MASK(aw)) #define VTD_SL_IGN_COM 0xbff0000000000000ULL #define VTD_SL_TM (1ULL << 62) +/* Common for both First Level and Second Level */ +#define VTD_PML4_LEVEL 4 +#define VTD_PDP_LEVEL 3 +#define VTD_PD_LEVEL 2 +#define VTD_PT_LEVEL 1 +#define VTD_PT_ENTRY_NR 512 +#define VTD_PT_PAGE_SIZE_MASK (1ULL << 7) +#define VTD_PT_BASE_ADDR_MASK(aw) (~(VTD_PAGE_SIZE - 1) & VTD_HAW_MASK(aw)) + +/* Bits to decide the offset for each level */ +#define VTD_LEVEL_BITS 9 + #endif diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c index 1e89ca0899c..1be9bfe36e9 100644 --- a/hw/i386/kvm/apic.c +++ b/hw/i386/kvm/apic.c @@ -14,9 +14,10 @@ #include "qemu/module.h" #include "hw/i386/apic_internal.h" #include "hw/pci/msi.h" -#include "sysemu/hw_accel.h" -#include "sysemu/kvm.h" +#include "system/hw_accel.h" +#include "system/kvm.h" #include "kvm/kvm_i386.h" +#include "kvm/tdx.h" static inline void kvm_apic_set_reg(struct kvm_lapic_state *kapic, int reg_id, uint32_t val) @@ -95,9 +96,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) @@ -140,6 +142,10 @@ static void kvm_apic_put(CPUState *cs, run_on_cpu_data data) struct kvm_lapic_state kapic; int ret; + if (is_tdx_vm()) { + return; + } + kvm_put_apicbase(s->cpu, s->apicbase); kvm_put_apic_state(s, &kapic); @@ -213,7 +219,7 @@ static void kvm_apic_mem_write(void *opaque, hwaddr addr, static const MemoryRegionOps kvm_apic_io_ops = { .read = kvm_apic_mem_read, .write = kvm_apic_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void kvm_apic_reset(APICCommonState *s) @@ -239,7 +245,7 @@ static void kvm_apic_unrealize(DeviceState *dev) { } -static void kvm_apic_class_init(ObjectClass *klass, void *data) +static void kvm_apic_class_init(ObjectClass *klass, const void *data) { APICCommonClass *k = APIC_COMMON_CLASS(klass); diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 34348a3324c..f56382717f7 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -16,9 +16,9 @@ #include "qemu/osdep.h" #include "qemu/host-utils.h" #include "qemu/module.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" -#include "sysemu/hw_accel.h" +#include "system/kvm.h" +#include "system/runstate.h" +#include "system/hw_accel.h" #include "kvm/kvm_i386.h" #include "migration/vmstate.h" #include "hw/sysbus.h" @@ -27,7 +27,6 @@ #include "qapi/error.h" #include -#include "standard-headers/asm-x86/kvm_para.h" #include "qom/object.h" #define TYPE_KVM_CLOCK "kvmclock" @@ -66,7 +65,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 +244,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,23 +294,22 @@ 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 } }; -static Property kvmclock_properties[] = { +static const Property kvmclock_properties[] = { DEFINE_PROP_BOOL("x-mach-use-reliable-get-clock", KVMClockState, mach_use_reliable_get_clock, true), - DEFINE_PROP_END_OF_LIST(), }; -static void kvmclock_class_init(ObjectClass *klass, void *data) +static void kvmclock_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -333,13 +331,9 @@ void kvmclock_create(bool create_always) X86CPU *cpu = X86_CPU(first_cpu); assert(kvm_enabled()); - if (!kvm_has_adjust_clock()) { - return; - } - if (create_always || - cpu->env.features[FEAT_KVM] & ((1ULL << KVM_FEATURE_CLOCKSOURCE) | - (1ULL << KVM_FEATURE_CLOCKSOURCE2))) { + cpu->env.features[FEAT_KVM] & (CPUID_KVM_CLOCK | + CPUID_KVM_CLOCK2)) { sysbus_create_simple(TYPE_KVM_CLOCK, -1, NULL); } } diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index a649b2b7caf..14b78f30a83 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -29,11 +29,11 @@ #include "qapi/error.h" #include "qemu/module.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/timer/i8254.h" #include "hw/timer/i8254_internal.h" #include "hw/qdev-properties-system.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "target/i386/kvm/kvm_i386.h" #include "qom/object.h" @@ -97,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]; @@ -170,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(); } @@ -261,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,13 +287,12 @@ static void kvm_pit_realizefn(DeviceState *dev, Error **errp) kpc->parent_realize(dev, errp); } -static Property kvm_pit_properties[] = { +static const Property kvm_pit_properties[] = { DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", KVMPITState, lost_tick_policy, LOST_TICK_POLICY_DELAY), - DEFINE_PROP_END_OF_LIST(), }; -static void kvm_pit_class_init(ObjectClass *klass, void *data) +static void kvm_pit_class_init(ObjectClass *klass, const void *data) { KVMPITClass *kpc = KVM_PIT_CLASS(klass); PITCommonClass *k = PIT_COMMON_CLASS(klass); @@ -317,7 +302,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 3ca0e1ff036..8a72d6e4dd3 100644 --- a/hw/i386/kvm/i8259.c +++ b/hw/i386/kvm/i8259.c @@ -16,7 +16,7 @@ #include "qemu/module.h" #include "hw/intc/kvm_irqcount.h" #include "hw/irq.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qom/object.h" #define TYPE_KVM_I8259 "kvm-i8259" @@ -139,13 +139,13 @@ qemu_irq *kvm_i8259_init(ISABus *bus) return qemu_allocate_irqs(kvm_pic_set_irq, NULL, ISA_NUM_IRQS); } -static void kvm_i8259_class_init(ObjectClass *klass, void *data) +static void kvm_i8259_class_init(ObjectClass *klass, const void *data) { KVMPICClass *kpc = KVM_PIC_CLASS(klass); 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 409d0c8c766..693ee978a12 100644 --- a/hw/i386/kvm/ioapic.c +++ b/hw/i386/kvm/ioapic.c @@ -15,7 +15,7 @@ #include "hw/qdev-properties.h" #include "hw/intc/ioapic_internal.h" #include "hw/intc/kvm_irqcount.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm/kvm_i386.h" /* PC Utility function */ @@ -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) { @@ -133,12 +133,11 @@ static void kvm_ioapic_realize(DeviceState *dev, Error **errp) qdev_init_gpio_in(dev, kvm_ioapic_set_irq, IOAPIC_NUM_PINS); } -static Property kvm_ioapic_properties[] = { +static const Property kvm_ioapic_properties[] = { DEFINE_PROP_UINT32("gsi_base", KVMIOAPICState, kvm_gsi_base, 0), - DEFINE_PROP_END_OF_LIST() }; -static void kvm_ioapic_class_init(ObjectClass *klass, void *data) +static void kvm_ioapic_class_init(ObjectClass *klass, const void *data) { IOAPICCommonClass *k = IOAPIC_COMMON_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -146,7 +145,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 ab143d6474b..a4a2e23c06e 100644 --- a/hw/i386/kvm/meson.build +++ b/hw/i386/kvm/meson.build @@ -9,6 +9,7 @@ i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files( 'xen_evtchn.c', 'xen_gnttab.c', 'xen_xenstore.c', + 'xen_primary_console.c', 'xenstore_impl.c', )) diff --git a/hw/i386/kvm/trace-events b/hw/i386/kvm/trace-events index e4c82de6f3e..67bf7f174ed 100644 --- a/hw/i386/kvm/trace-events +++ b/hw/i386/kvm/trace-events @@ -18,3 +18,5 @@ 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/xen-stubs.c b/hw/i386/kvm/xen-stubs.c index ae406e0b02f..ce73119ee7a 100644 --- a/hw/i386/kvm/xen-stubs.c +++ b/hw/i386/kvm/xen-stubs.c @@ -12,9 +12,9 @@ #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) @@ -30,15 +30,10 @@ bool xen_evtchn_deliver_pirq_msi(uint64_t address, uint32_t data) return false; } -#ifdef TARGET_I386 -EvtchnInfoList *qmp_xen_event_list(Error **errp) +void xen_primary_console_create(void) { - error_setg(errp, "Xen event channel emulation not enabled"); - return NULL; } -void qmp_xen_event_inject(uint32_t port, Error **errp) +void xen_primary_console_set_be_port(uint16_t port) { - 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 index a731738411b..dd566c49679 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -19,11 +19,11 @@ #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 "qapi/qapi-commands-misc-i386.h" +#include "qobject/qdict.h" #include "qom/object.h" #include "exec/target_page.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "migration/vmstate.h" #include "trace.h" @@ -41,8 +41,8 @@ #include "xen_overlay.h" #include "xen_xenstore.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_xen.h" +#include "system/kvm.h" +#include "system/kvm_xen.h" #include #include @@ -58,7 +58,15 @@ OBJECT_DECLARE_SIMPLE_TYPE(XenEvtchnState, XEN_EVTCHN) typedef struct XenEvtchnPort { uint32_t vcpu; /* Xen/ACPI vcpu_id */ uint16_t type; /* EVTCHNSTAT_xxxx */ - uint16_t type_val; /* pirq# / virq# / remote port according to type */ + 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 */ @@ -105,14 +113,6 @@ struct xenevtchn_handle { int fd; }; -/* - * For unbound/interdomain ports there are only two possible remote - * domains; self and QEMU. Use a single high bit in type_val for that, - * and the low bits for the remote port number (or 0 for unbound). - */ -#define PORT_INFO_TYPEVAL_REMOTE_QEMU 0x8000 -#define PORT_INFO_TYPEVAL_REMOTE_PORT_MASK 0x7FFF - /* * 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 @@ -140,6 +140,8 @@ struct XenEvtchnState { uint64_t callback_param; bool evtchn_in_kernel; + bool setting_callback_gsi; + int extern_gsi_level; uint32_t callback_gsi; QEMUBH *gsi_bh; @@ -210,16 +212,16 @@ static int xen_evtchn_post_load(void *opaque, int version_id) XenEvtchnPort *p = &s->port_table[i]; if (p->type == EVTCHNSTAT_pirq) { - assert(p->type_val); - assert(p->type_val < s->nr_pirqs); + 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->type_val].gsi = IRQ_UNBOUND; - s->pirq[p->type_val].port = i; + s->pirq[p->u.pirq].gsi = IRQ_UNBOUND; + s->pirq[p->u.pirq].port = i; } } /* Rebuild s->pirq[].gsi mapping */ @@ -240,10 +242,10 @@ static const VMStateDescription xen_evtchn_port_vmstate = { .name = "xen_evtchn_port", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(vcpu, XenEvtchnPort), VMSTATE_UINT16(type, XenEvtchnPort), - VMSTATE_UINT16(type_val, XenEvtchnPort), + VMSTATE_UINT16(u.val, XenEvtchnPort), VMSTATE_END_OF_LIST() } }; @@ -255,7 +257,7 @@ static const VMStateDescription xen_evtchn_vmstate = { .needed = xen_evtchn_is_needed, .pre_load = xen_evtchn_pre_load, .post_load = xen_evtchn_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(callback_param, XenEvtchnState), VMSTATE_UINT32(nr_ports, XenEvtchnState), VMSTATE_STRUCT_VARRAY_UINT32(port_table, XenEvtchnState, nr_ports, 1, @@ -269,7 +271,7 @@ static const VMStateDescription xen_evtchn_vmstate = { } }; -static void xen_evtchn_class_init(ObjectClass *klass, void *data) +static void xen_evtchn_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -371,7 +373,7 @@ static int set_callback_pci_intx(XenEvtchnState *s, uint64_t param) return 0; } - pdev = pci_find_device(pcms->bus, bus, devfn); + pdev = pci_find_device(pcms->pcibus, bus, devfn); if (!pdev) { return 0; } @@ -425,15 +427,28 @@ void xen_evtchn_set_callback_level(int level) * effect immediately. That just leaves interdomain loopback as the case * which uses the BH. */ - if (!qemu_mutex_iothread_locked()) { + 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 */ + /* + * Ugly, but since we hold the BQL we can set this flag so that + * xen_evtchn_set_gsi() can tell the difference between this code + * setting the GSI, and an external device (PCI INTx) doing so. + */ + s->setting_callback_gsi = true; + /* Do not deassert the line if an external device is asserting it. */ + qemu_set_irq(s->callback_gsis[s->callback_gsi], + level || s->extern_gsi_level); + s->setting_callback_gsi = false; + + /* + * If the callback GSI is the only one asserted, ensure the status + * is polled for deassertion in kvm_arch_post_run(). + */ + if (level && !s->extern_gsi_level) { kvm_xen_set_callback_asserted(); } } @@ -459,7 +474,7 @@ int xen_evtchn_set_callback_param(uint64_t param) * 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(qemu_mutex_iothread_locked()); + assert(bql_locked()); qemu_mutex_lock(&s->port_lock); switch (type) { @@ -490,6 +505,12 @@ int xen_evtchn_set_callback_param(uint64_t param) 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) == @@ -599,14 +620,13 @@ static void unbind_backend_ports(XenEvtchnState *s) for (i = 1; i < s->nr_ports; i++) { p = &s->port_table[i]; - if (p->type == EVTCHNSTAT_interdomain && - (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU)) { - evtchn_port_t be_port = p->type_val & PORT_INFO_TYPEVAL_REMOTE_PORT_MASK; + 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->type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU; + p->u.interdomain.port = 0; /* Leave the backend port open and unbound too. */ if (kvm_xen_has_cap(EVTCHN_SEND)) { @@ -644,30 +664,22 @@ int xen_evtchn_status_op(struct evtchn_status *status) switch (p->type) { case EVTCHNSTAT_unbound: - if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) { - status->u.unbound.dom = DOMID_QEMU; - } else { - status->u.unbound.dom = xen_domid; - } + status->u.unbound.dom = p->u.interdomain.to_qemu ? DOMID_QEMU + : xen_domid; break; case EVTCHNSTAT_interdomain: - if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) { - status->u.interdomain.dom = DOMID_QEMU; - } else { - status->u.interdomain.dom = xen_domid; - } - - status->u.interdomain.port = p->type_val & - PORT_INFO_TYPEVAL_REMOTE_PORT_MASK; + 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->type_val; + status->u.pirq = p->u.pirq; break; case EVTCHNSTAT_virq: - status->u.virq = p->type_val; + status->u.virq = p->u.virq; break; } @@ -983,7 +995,7 @@ static int clear_port_pending(XenEvtchnState *s, evtchn_port_t port) static void free_port(XenEvtchnState *s, evtchn_port_t port) { s->port_table[port].type = EVTCHNSTAT_closed; - s->port_table[port].type_val = 0; + s->port_table[port].u.val = 0; s->port_table[port].vcpu = 0; if (s->nr_ports == port + 1) { @@ -1006,7 +1018,7 @@ static int allocate_port(XenEvtchnState *s, uint32_t vcpu, uint16_t type, if (s->port_table[p].type == EVTCHNSTAT_closed) { s->port_table[p].vcpu = vcpu; s->port_table[p].type = type; - s->port_table[p].type_val = val; + s->port_table[p].u.val = val; *port = p; @@ -1040,22 +1052,22 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port, XenEvtchnPort *p = &s->port_table[port]; /* Because it *might* be a PIRQ port */ - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); switch (p->type) { case EVTCHNSTAT_closed: return -ENOENT; case EVTCHNSTAT_pirq: - s->pirq[p->type_val].port = 0; - if (s->pirq[p->type_val].is_translated) { + 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->type_val) ? 0 : p->vcpu, - p->type_val, 0); + kvm_xen_set_vcpu_virq(virq_is_global(p->u.virq) ? 0 : p->vcpu, + p->u.virq, 0); break; case EVTCHNSTAT_ipi: @@ -1065,8 +1077,8 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port, break; case EVTCHNSTAT_interdomain: - if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) { - uint16_t be_port = p->type_val & ~PORT_INFO_TYPEVAL_REMOTE_QEMU; + 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)) { @@ -1076,14 +1088,15 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port, } } else { /* Loopback interdomain */ - XenEvtchnPort *rp = &s->port_table[p->type_val]; - if (!valid_port(p->type_val) || rp->type_val != port || + 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->type_val = 0; + rp->u.interdomain.port = 0; } } break; @@ -1099,14 +1112,14 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port, int xen_evtchn_soft_reset(void) { XenEvtchnState *s = xen_evtchn_singleton; - bool flush_kvm_routes; + bool flush_kvm_routes = false; int i; if (!s) { return -ENOTSUP; } - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); qemu_mutex_lock(&s->port_lock); @@ -1129,6 +1142,7 @@ int xen_evtchn_reset_op(struct evtchn_reset *reset) return -ESRCH; } + BQL_LOCK_GUARD(); return xen_evtchn_soft_reset(); } @@ -1146,7 +1160,7 @@ int xen_evtchn_close_op(struct evtchn_close *close) return -EINVAL; } - QEMU_IOTHREAD_LOCK_GUARD(); + BQL_LOCK_GUARD(); qemu_mutex_lock(&s->port_lock); ret = close_port(s, close->port, &flush_kvm_routes); @@ -1207,7 +1221,7 @@ int xen_evtchn_bind_vcpu_op(struct evtchn_bind_vcpu *vcpu) if (p->type == EVTCHNSTAT_interdomain || p->type == EVTCHNSTAT_unbound || p->type == EVTCHNSTAT_pirq || - (p->type == EVTCHNSTAT_virq && virq_is_global(p->type_val))) { + (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. @@ -1273,7 +1287,7 @@ int xen_evtchn_bind_pirq_op(struct evtchn_bind_pirq *pirq) return -EINVAL; } - QEMU_IOTHREAD_LOCK_GUARD(); + BQL_LOCK_GUARD(); if (s->pirq[pirq->pirq].port) { return -EBUSY; @@ -1352,19 +1366,15 @@ int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi) int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain) { XenEvtchnState *s = xen_evtchn_singleton; - uint16_t type_val; int ret; if (!s) { return -ENOTSUP; } - if (interdomain->remote_dom == DOMID_QEMU) { - type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU; - } else if (interdomain->remote_dom == DOMID_SELF || - interdomain->remote_dom == xen_domid) { - type_val = 0; - } else { + if (interdomain->remote_dom != DOMID_QEMU && + interdomain->remote_dom != DOMID_SELF && + interdomain->remote_dom != xen_domid) { return -ESRCH; } @@ -1375,8 +1385,8 @@ int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain) qemu_mutex_lock(&s->port_lock); /* The newly allocated port starts out as unbound */ - ret = allocate_port(s, 0, EVTCHNSTAT_unbound, type_val, - &interdomain->local_port); + ret = allocate_port(s, 0, EVTCHNSTAT_unbound, 0, &interdomain->local_port); + if (ret) { goto out; } @@ -1401,7 +1411,8 @@ int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain) assign_kernel_eventfd(lp->type, xc->guest_port, xc->fd); } lp->type = EVTCHNSTAT_interdomain; - lp->type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU | interdomain->remote_port; + lp->u.interdomain.to_qemu = 1; + lp->u.interdomain.port = interdomain->remote_port; ret = 0; } else { /* Loopback */ @@ -1409,19 +1420,18 @@ int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain) 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 (as indicated by rp->type_val - * being zero, not PORT_INFO_TYPEVAL_REMOTE_QEMU), and must *not* be - * the port that was just allocated for the local end. + * 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->type_val == 0) { + rp->type == EVTCHNSTAT_unbound && !rp->u.interdomain.to_qemu) { rp->type = EVTCHNSTAT_interdomain; - rp->type_val = interdomain->local_port; + rp->u.interdomain.port = interdomain->local_port; lp->type = EVTCHNSTAT_interdomain; - lp->type_val = interdomain->remote_port; + lp->u.interdomain.port = interdomain->remote_port; } else { ret = -EINVAL; } @@ -1440,7 +1450,6 @@ int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain) int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc) { XenEvtchnState *s = xen_evtchn_singleton; - uint16_t type_val; int ret; if (!s) { @@ -1451,18 +1460,20 @@ int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc) return -ESRCH; } - if (alloc->remote_dom == DOMID_QEMU) { - type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU; - } else if (alloc->remote_dom == DOMID_SELF || - alloc->remote_dom == xen_domid) { - type_val = 0; - } else { + 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, type_val, &alloc->port); + 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); @@ -1489,12 +1500,12 @@ int xen_evtchn_send_op(struct evtchn_send *send) switch (p->type) { case EVTCHNSTAT_interdomain: - if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) { + 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->type_val & ~PORT_INFO_TYPEVAL_REMOTE_QEMU; + uint16_t be_port = p->u.interdomain.port; struct xenevtchn_handle *xc = s->be_handles[be_port]; if (xc) { eventfd_write(xc->fd, 1); @@ -1504,7 +1515,7 @@ int xen_evtchn_send_op(struct evtchn_send *send) } } else { /* Loopback interdomain ports; just a complex IPI */ - set_port_pending(s, p->type_val); + set_port_pending(s, p->u.interdomain.port); } break; @@ -1546,8 +1557,7 @@ int xen_evtchn_set_port(uint16_t port) /* QEMU has no business sending to anything but these */ if (p->type == EVTCHNSTAT_virq || - (p->type == EVTCHNSTAT_interdomain && - (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU))) { + (p->type == EVTCHNSTAT_interdomain && p->u.interdomain.to_qemu)) { set_port_pending(s, port); ret = 0; } @@ -1601,28 +1611,47 @@ static int allocate_pirq(XenEvtchnState *s, int type, int gsi) return pirq; } -bool xen_evtchn_set_gsi(int gsi, int level) +bool xen_evtchn_set_gsi(int gsi, int *level) { XenEvtchnState *s = xen_evtchn_singleton; int pirq; - assert(qemu_mutex_iothread_locked()); + 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. + * For the callback_gsi we need to implement a logical OR of the event + * channel GSI and the external input (e.g. from PCI INTx), because + * QEMU itself doesn't support shared level interrupts via demux or + * resamplers. */ if (gsi && gsi == s->callback_gsi) { + /* Remember the external state of the GSI pin (e.g. from PCI INTx) */ + if (!s->setting_callback_gsi) { + s->extern_gsi_level = *level; + + /* + * Don't allow the external device to deassert the line if the + * eveht channel GSI should still be asserted. + */ + if (!s->extern_gsi_level) { + struct vcpu_info *vi = kvm_xen_get_vcpu_info_hva(0); + if (vi && vi->evtchn_upcall_pending) { + /* Need to poll for deassertion */ + kvm_xen_set_callback_asserted(); + *level = 1; + } + } + } + + /* + * The event channel GSI cannot be routed to PIRQ, as that would make + * no sense. It could also deadlock on s->port_lock, if we proceed. + * So bail out now. + */ return false; } @@ -1633,7 +1662,7 @@ bool xen_evtchn_set_gsi(int gsi, int level) return false; } - if (level) { + if (*level) { int port = s->pirq[pirq].port; s->pirq_gsi_set |= (1U << gsi); @@ -1717,7 +1746,7 @@ void xen_evtchn_snoop_msi(PCIDevice *dev, bool is_msix, unsigned int vector, return; } - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); pirq = msi_pirq_target(addr, data); @@ -1754,7 +1783,7 @@ int xen_evtchn_translate_pirq_msi(struct kvm_irq_routing_entry *route, return 1; /* Not a PIRQ */ } - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); pirq = msi_pirq_target(address, data); if (!pirq || pirq >= s->nr_pirqs) { @@ -1801,7 +1830,7 @@ bool xen_evtchn_deliver_pirq_msi(uint64_t address, uint32_t data) return false; } - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); pirq = msi_pirq_target(address, data); if (!pirq || pirq >= s->nr_pirqs) { @@ -1829,7 +1858,7 @@ int xen_physdev_map_pirq(struct physdev_map_pirq *map) return -ENOTSUP; } - QEMU_IOTHREAD_LOCK_GUARD(); + BQL_LOCK_GUARD(); QEMU_LOCK_GUARD(&s->port_lock); if (map->domid != DOMID_SELF && map->domid != xen_domid) { @@ -1889,7 +1918,7 @@ int xen_physdev_unmap_pirq(struct physdev_unmap_pirq *unmap) return -EINVAL; } - QEMU_IOTHREAD_LOCK_GUARD(); + BQL_LOCK_GUARD(); qemu_mutex_lock(&s->port_lock); if (!pirq_inuse(s, pirq)) { @@ -1929,7 +1958,7 @@ int xen_physdev_eoi_pirq(struct physdev_eoi *eoi) return -ENOTSUP; } - QEMU_IOTHREAD_LOCK_GUARD(); + BQL_LOCK_GUARD(); QEMU_LOCK_GUARD(&s->port_lock); if (!pirq_inuse(s, pirq)) { @@ -1961,7 +1990,7 @@ int xen_physdev_query_pirq(struct physdev_irq_status_query *query) return -ENOTSUP; } - QEMU_IOTHREAD_LOCK_GUARD(); + BQL_LOCK_GUARD(); QEMU_LOCK_GUARD(&s->port_lock); if (!pirq_inuse(s, pirq)) { @@ -2057,7 +2086,7 @@ int xen_be_evtchn_bind_interdomain(struct xenevtchn_handle *xc, uint32_t domid, switch (gp->type) { case EVTCHNSTAT_interdomain: /* Allow rebinding after migration, preserve port # if possible */ - be_port = gp->type_val & ~PORT_INFO_TYPEVAL_REMOTE_QEMU; + be_port = gp->u.interdomain.port; assert(be_port != 0); if (!s->be_handles[be_port]) { s->be_handles[be_port] = xc; @@ -2078,7 +2107,8 @@ int xen_be_evtchn_bind_interdomain(struct xenevtchn_handle *xc, uint32_t domid, } gp->type = EVTCHNSTAT_interdomain; - gp->type_val = be_port | PORT_INFO_TYPEVAL_REMOTE_QEMU; + 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); @@ -2123,7 +2153,7 @@ int xen_be_evtchn_unbind(struct xenevtchn_handle *xc, evtchn_port_t port) /* This should never *not* be true */ if (gp->type == EVTCHNSTAT_interdomain) { gp->type = EVTCHNSTAT_unbound; - gp->type_val = PORT_INFO_TYPEVAL_REMOTE_QEMU; + gp->u.interdomain.port = 0; } if (kvm_xen_has_cap(EVTCHN_SEND)) { @@ -2277,11 +2307,11 @@ EvtchnInfoList *qmp_xen_event_list(Error **errp) info->type = p->type; if (p->type == EVTCHNSTAT_interdomain) { - info->remote_domain = g_strdup((p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) ? + info->remote_domain = g_strdup(p->u.interdomain.to_qemu ? "qemu" : "loopback"); - info->target = p->type_val & PORT_INFO_TYPEVAL_REMOTE_PORT_MASK; + info->target = p->u.interdomain.port; } else { - info->target = p->type_val; + info->target = p->u.val; /* pirq# or virq# */ } info->vcpu = p->vcpu; info->pending = test_bit(i, pending); diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index b740acfc0d7..0521ebc0922 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -23,7 +23,7 @@ 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); +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); diff --git a/hw/i386/kvm/xen_gnttab.c b/hw/i386/kvm/xen_gnttab.c index 21c30e36591..4b9e272c5eb 100644 --- a/hw/i386/kvm/xen_gnttab.c +++ b/hw/i386/kvm/xen_gnttab.c @@ -17,7 +17,7 @@ #include "qapi/error.h" #include "qom/object.h" #include "exec/target_page.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "migration/vmstate.h" #include "hw/sysbus.h" @@ -25,9 +25,10 @@ #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 "system/kvm.h" +#include "system/kvm_xen.h" #include "hw/xen/interface/memory.h" #include "hw/xen/interface/grant_table.h" @@ -126,7 +127,7 @@ static const VMStateDescription xen_gnttab_vmstate = { .minimum_version_id = 1, .needed = xen_gnttab_is_needed, .post_load = xen_gnttab_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(nr_frames, XenGnttabState), VMSTATE_VARRAY_UINT32(gnt_frame_gpas, XenGnttabState, nr_frames, 0, vmstate_info_uint64, uint64_t), @@ -134,7 +135,7 @@ static const VMStateDescription xen_gnttab_vmstate = { } }; -static void xen_gnttab_class_init(ObjectClass *klass, void *data) +static void xen_gnttab_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -175,7 +176,7 @@ int xen_gnttab_map_page(uint64_t idx, uint64_t gfn) return -EINVAL; } - QEMU_IOTHREAD_LOCK_GUARD(); + BQL_LOCK_GUARD(); QEMU_LOCK_GUARD(&s->gnt_lock); xen_overlay_do_map_page(&s->gnt_aliases[idx], gpa); @@ -537,11 +538,13 @@ int xen_gnttab_reset(void) 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); - memset(s->map_track, 0, s->max_frames * ENTRIES_PER_FRAME_V1); + 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_overlay.c b/hw/i386/kvm/xen_overlay.c index 39fda1b72c3..3cb73619371 100644 --- a/hw/i386/kvm/xen_overlay.c +++ b/hw/i386/kvm/xen_overlay.c @@ -16,15 +16,15 @@ #include "qapi/error.h" #include "qom/object.h" #include "exec/target_page.h" -#include "exec/address-spaces.h" +#include "system/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 "system/kvm.h" +#include "system/kvm_xen.h" #include #include "hw/xen/interface/memory.h" @@ -139,7 +139,7 @@ static const VMStateDescription xen_overlay_vmstate = { .needed = xen_overlay_is_needed, .pre_save = xen_overlay_pre_save, .post_load = xen_overlay_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(shinfo_gpa, XenOverlayState), VMSTATE_BOOL(long_mode, XenOverlayState), VMSTATE_END_OF_LIST() @@ -151,11 +151,11 @@ static void xen_overlay_reset(DeviceState *dev) kvm_xen_soft_reset(); } -static void xen_overlay_class_init(ObjectClass *klass, void *data) +static void xen_overlay_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = xen_overlay_reset; + device_class_set_legacy_reset(dc, xen_overlay_reset); dc->realize = xen_overlay_realize; dc->vmsd = &xen_overlay_vmstate; } @@ -194,7 +194,7 @@ int xen_overlay_map_shinfo_page(uint64_t gpa) return -ENOENT; } - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); if (s->shinfo_gpa) { /* If removing shinfo page, turn the kernel magic off first */ diff --git a/hw/i386/kvm/xen_primary_console.c b/hw/i386/kvm/xen_primary_console.c new file mode 100644 index 00000000000..6e9d6417c3e --- /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 "system/kvm.h" +#include "system/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, const 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 00000000000..7e2989ea0d4 --- /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 index 133d89e953d..42955cccd90 100644 --- a/hw/i386/kvm/xen_xenstore.c +++ b/hw/i386/kvm/xen_xenstore.c @@ -25,10 +25,11 @@ #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 "system/kvm.h" +#include "system/kvm_xen.h" #include "trace.h" @@ -208,7 +209,6 @@ 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 @@ -230,8 +230,7 @@ static int xen_xenstore_post_load(void *opaque, int ver) s->impl_state = NULL; s->impl_state_size = 0; - ret = xs_impl_deserialize(s->impl, save, xen_domid, fire_watch_cb, s); - return ret; + return xs_impl_deserialize(s->impl, save, xen_domid, fire_watch_cb, s); } static const VMStateDescription xen_xenstore_vmstate = { @@ -242,7 +241,7 @@ static const VMStateDescription xen_xenstore_vmstate = { .needed = xen_xenstore_is_needed, .pre_save = xen_xenstore_pre_save, .post_load = xen_xenstore_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(req_data, XenXenstoreState, sizeof_field(XenXenstoreState, req_data)), VMSTATE_UINT8_ARRAY(rsp_data, XenXenstoreState, @@ -260,7 +259,7 @@ static const VMStateDescription xen_xenstore_vmstate = { } }; -static void xen_xenstore_class_init(ObjectClass *klass, void *data) +static void xen_xenstore_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -331,7 +330,7 @@ static void xs_error(XenXenstoreState *s, unsigned int id, const char *errstr = NULL; for (unsigned int i = 0; i < ARRAY_SIZE(xsd_errors); i++) { - struct xsd_errors *xsd_error = &xsd_errors[i]; + const struct xsd_errors *xsd_error = &xsd_errors[i]; if (xsd_error->errnum == errnum) { errstr = xsd_error->errstring; @@ -531,6 +530,10 @@ static void xs_read(XenXenstoreState *s, unsigned int req_id, return; } + if (!len) { + return; + } + memcpy(&rsp_data[rsp->len], data->data, len); rsp->len += len; } @@ -1156,7 +1159,7 @@ static unsigned int copy_to_ring(XenXenstoreState *s, uint8_t *ptr, /* * This matches the barrier in copy_to_ring() (or the guest's - * equivalent) betweem writing the data to the ring and updating + * 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 @@ -1340,7 +1343,7 @@ static void fire_watch_cb(void *opaque, const char *path, const char *token) { XenXenstoreState *s = opaque; - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); /* * If there's a response pending, we obviously can't scribble over @@ -1357,10 +1360,12 @@ static void fire_watch_cb(void *opaque, const char *path, const char *token) } else { deliver_watch(s, path, token); /* - * If the message was queued because there was already ring activity, - * no need to wake the guest. But if not, we need to send the evtchn. + * Attempt to queue the message into the actual ring, and send + * the event channel notification if any bytes are copied. */ - xen_be_evtchn_notify(s->eh, s->be_port); + if (s->rsp_pending && put_rsp(s) > 0) { + xen_be_evtchn_notify(s->eh, s->be_port); + } } } @@ -1432,6 +1437,8 @@ static void alloc_guest_port(XenXenstoreState *s) int xen_xenstore_reset(void) { XenXenstoreState *s = xen_xenstore_singleton; + int console_port; + GList *perms; int err; if (!s) { @@ -1459,6 +1466,24 @@ int xen_xenstore_reset(void) } 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 diff --git a/hw/i386/kvm/xenstore_impl.c b/hw/i386/kvm/xenstore_impl.c index d9732b567e1..1d134a68664 100644 --- a/hw/i386/kvm/xenstore_impl.c +++ b/hw/i386/kvm/xenstore_impl.c @@ -1436,7 +1436,7 @@ static void save_node(gpointer key, gpointer value, gpointer opaque) /* * 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 transation in which it exists. Which + * it is the tx_id of the transaction in which it exists. Which * may be the root tx. */ if (n->serialized_tx != XBT_NULL) { diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c deleted file mode 100644 index 43f8a8f679e..00000000000 --- 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 cfdbfdcbcb2..7896f348cff 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,19 +24,19 @@ 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', 'port92.c')) i386_ss.add(when: 'CONFIG_X86_FW_OVMF', if_true: files('pc_sysfw_ovmf.c'), if_false: files('pc_sysfw_ovmf-stubs.c')) +i386_ss.add(when: 'CONFIG_TDX', if_true: files('tdvf.c', 'tdvf-hob.c')) subdir('kvm') subdir('xen') i386_ss.add_all(xenpv_ss) -i386_ss.add_all(xen_ss) hw_arch += {'i386': i386_ss} diff --git a/hw/i386/microvm-dt.c b/hw/i386/microvm-dt.c index b3049e4f9f2..cb27dfd732e 100644 --- a/hw/i386/microvm-dt.c +++ b/hw/i386/microvm-dt.c @@ -33,8 +33,8 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qapi/error.h" -#include "sysemu/device_tree.h" -#include "hw/char/serial.h" +#include "system/device_tree.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 8deeb62774d..e0daf0d4fc3 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -22,11 +22,11 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "qapi/qapi-visit-common.h" -#include "sysemu/sysemu.h" -#include "sysemu/cpus.h" -#include "sysemu/numa.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/cpus.h" +#include "system/numa.h" +#include "system/reset.h" +#include "system/runstate.h" #include "acpi-microvm.h" #include "microvm-dt.h" @@ -39,7 +39,7 @@ #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" @@ -139,7 +139,7 @@ static void create_gpex(MicrovmMachineState *mms) mms->gpex.mmio64.base, mmio64_alias); } - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, x86ms->gsi[mms->gpex.irq + i]); } @@ -175,7 +175,7 @@ static void microvm_devices_init(MicrovmMachineState *mms) &error_abort); 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); } @@ -204,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); } @@ -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) { @@ -452,11 +451,44 @@ static HotplugHandler *microvm_get_hotplug_handler(MachineState *machine, return NULL; } +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) +{ + MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState, + powerdown_req); + X86MachineState *x86ms = X86_MACHINE(mms); + + if (x86ms->acpi_dev) { + Object *obj = OBJECT(x86ms->acpi_dev); + AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj); + adevc->send_event(ACPI_DEVICE_IF(x86ms->acpi_dev), + ACPI_POWER_DOWN_STATUS); + } +} + static void microvm_machine_state_init(MachineState *machine) { MicrovmMachineState *mms = MICROVM_MACHINE(machine); X86MachineState *x86ms = X86_MACHINE(machine); + /* State */ + mms->kernel_cmdline_fixed = false; + + mms->machine_done.notify = microvm_machine_done; + qemu_add_machine_init_done_notifier(&mms->machine_done); + mms->powerdown_req.notify = microvm_powerdown_req; + qemu_register_powerdown_notifier(&mms->powerdown_req); + microvm_memory_init(mms); x86_cpus_init(x86ms, CPU_VERSION_LATEST); @@ -464,7 +496,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; @@ -477,7 +509,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); @@ -582,29 +614,6 @@ static void microvm_machine_set_auto_kernel_cmdline(Object *obj, bool value, mms->auto_kernel_cmdline = value; } -static void microvm_machine_done(Notifier *notifier, void *data) -{ - MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState, - machine_done); - - acpi_setup_microvm(mms); - dt_setup_microvm(mms); -} - -static void microvm_powerdown_req(Notifier *notifier, void *data) -{ - MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState, - powerdown_req); - X86MachineState *x86ms = X86_MACHINE(mms); - - if (x86ms->acpi_dev) { - Object *obj = OBJECT(x86ms->acpi_dev); - AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj); - adevc->send_event(ACPI_DEVICE_IF(x86ms->acpi_dev), - ACPI_POWER_DOWN_STATUS); - } -} - static void microvm_machine_initfn(Object *obj) { MicrovmMachineState *mms = MICROVM_MACHINE(obj); @@ -616,14 +625,6 @@ static void microvm_machine_initfn(Object *obj) mms->isa_serial = true; mms->option_roms = true; mms->auto_kernel_cmdline = true; - - /* State */ - mms->kernel_cmdline_fixed = false; - - mms->machine_done.notify = microvm_machine_done; - qemu_add_machine_init_done_notifier(&mms->machine_done); - mms->powerdown_req.notify = microvm_powerdown_req; - qemu_register_powerdown_notifier(&mms->powerdown_req); } GlobalProperty microvm_properties[] = { @@ -634,12 +635,15 @@ GlobalProperty microvm_properties[] = { { "pcie-root-port", "io-reserve", "0" }, }; -static void microvm_class_init(ObjectClass *oc, void *data) +static void microvm_class_init(ObjectClass *oc, const 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"; @@ -722,7 +726,7 @@ static const TypeInfo microvm_machine_info = { .instance_init = microvm_machine_initfn, .class_size = sizeof(MicrovmMachineClass), .class_init = microvm_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } }, diff --git a/hw/i386/monitor.c b/hw/i386/monitor.c new file mode 100644 index 00000000000..79df96562f6 --- /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 "qobject/qdict.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-i386.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 3332712ab35..6e6b96bc345 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -29,7 +29,8 @@ #include "multiboot.h" #include "hw/loader.h" #include "elf.h" -#include "sysemu/sysemu.h" +#include "exec/target_page.h" +#include "system/system.h" #include "qemu/error-report.h" /* Show multiboot debug output */ @@ -133,9 +134,9 @@ 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: "HWADDR_FMT_plx" - "HWADDR_FMT_plx, s->mb_mods_count, start, end); @@ -168,9 +169,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) { @@ -202,8 +203,8 @@ int load_multiboot(X86MachineState *x86ms, } kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, - &elf_low, &elf_high, NULL, 0, I386_ELF_MACHINE, - 0, 0); + &elf_low, &elf_high, NULL, + ELFDATA2LSB, I386_ELF_MACHINE, 0, 0); if (kernel_size < 0) { error_report("Error while loading elf kernel"); exit(1); @@ -223,11 +224,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 +240,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) { @@ -364,22 +365,21 @@ 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 = "HWADDR_FMT_plx, mbs.mb_buf_phys); diff --git a/hw/i386/nitro_enclave.c b/hw/i386/nitro_enclave.c new file mode 100644 index 00000000000..5ee50f3b858 --- /dev/null +++ b/hw/i386/nitro_enclave.c @@ -0,0 +1,353 @@ +/* + * 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 "system/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_hash, + QCRYPTO_HASH_DIGEST_LEN_SHA384); + /* PCR1 */ + ne_state->vnsm->extend_pcr(ne_state->vnsm, 1, ne_state->bootstrap_hash, + QCRYPTO_HASH_DIGEST_LEN_SHA384); + /* PCR2 */ + ne_state->vnsm->extend_pcr(ne_state->vnsm, 2, ne_state->app_hash, + 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_hash, + 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_hash, nems->bootstrap_hash, + nems->app_hash, nems->fingerprint_hash, + &(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); +} + +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, const 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 54838c0c411..b2116335752 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -24,80 +24,47 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "hw/i386/x86.h" +#include "exec/target_page.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/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/internal.h" -#include "hw/ide/isa.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 "system/cpus.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/intc/ioapic.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 "system/system.h" +#include "system/xen.h" +#include "system/reset.h" #include "kvm/kvm_i386.h" +#include "kvm/tdx.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 "qobject/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-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 "sysemu/replay.h" -#include "target/i386/cpu.h" +#include "hw/mem/memory-device.h" #include "e820_memory_layout.h" -#include "fw_cfg.h" #include "trace.h" +#include "sev.h" #include CONFIG_DEVICES #ifdef CONFIG_XEN_EMU @@ -114,6 +81,32 @@ { "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_10_0[] = {}; +const size_t pc_compat_10_0_len = G_N_ELEMENTS(pc_compat_10_0); + +GlobalProperty pc_compat_9_2[] = {}; +const size_t pc_compat_9_2_len = G_N_ELEMENTS(pc_compat_9_2); + +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" }, + { TYPE_INTEL_IOMMU_DEVICE, "aw-bits", "39" }, +}; +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); @@ -267,151 +260,15 @@ GlobalProperty pc_compat_2_6[] = { }; const size_t pc_compat_2_6_len = G_N_ELEMENTS(pc_compat_2_6); -GlobalProperty pc_compat_2_5[] = {}; -const size_t pc_compat_2_5_len = G_N_ELEMENTS(pc_compat_2_5); - -GlobalProperty pc_compat_2_4[] = { - PC_CPU_MODEL_IDS("2.4.0") - { "Haswell-" TYPE_X86_CPU, "abm", "off" }, - { "Haswell-noTSX-" TYPE_X86_CPU, "abm", "off" }, - { "Broadwell-" TYPE_X86_CPU, "abm", "off" }, - { "Broadwell-noTSX-" TYPE_X86_CPU, "abm", "off" }, - { "host" "-" TYPE_X86_CPU, "host-cache-info", "on" }, - { TYPE_X86_CPU, "check", "off" }, - { "qemu64" "-" TYPE_X86_CPU, "sse4a", "on" }, - { "qemu64" "-" TYPE_X86_CPU, "abm", "on" }, - { "qemu64" "-" TYPE_X86_CPU, "popcnt", "on" }, - { "qemu32" "-" TYPE_X86_CPU, "popcnt", "on" }, - { "Opteron_G2" "-" TYPE_X86_CPU, "rdtscp", "on" }, - { "Opteron_G3" "-" TYPE_X86_CPU, "rdtscp", "on" }, - { "Opteron_G4" "-" TYPE_X86_CPU, "rdtscp", "on" }, - { "Opteron_G5" "-" TYPE_X86_CPU, "rdtscp", "on", } -}; -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) { @@ -436,7 +293,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) { @@ -486,8 +343,8 @@ static int boot_device2nibble(char boot_device) return 0; } -static void set_boot_dev(MC146818RtcState *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, }; @@ -507,26 +364,32 @@ static void set_boot_dev(MC146818RtcState *s, const char *boot_device, } } mc146818rtc_set_cmos_data(s, 0x3d, (bds[1] << 4) | bds[0]); - mc146818rtc_set_cmos_data(s, 0x38, (bds[2] << 4) | (fd_bootchk ? 0x0 : 0x1)); + 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(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]); mc146818rtc_set_cmos_data(rtc_state, 0x10, val); @@ -552,11 +415,6 @@ static void pc_cmos_init_floppy(MC146818RtcState *rtc_state, ISADevice *floppy) mc146818rtc_set_cmos_data(rtc_state, REG_EQUIPMENT_BYTE, val); } -typedef struct pc_cmos_init_late_arg { - MC146818RtcState *rtc_state; - BusState *idebus[2]; -} pc_cmos_init_late_arg; - typedef struct check_fdc_state { ISADevice *floppy; bool multiple; @@ -589,7 +447,7 @@ static int check_fdc(Object *obj, void *opaque) } static const char * const fdc_container_path[] = { - "/unattached", "/peripheral", "/peripheral-anon" + "unattached", "peripheral", "peripheral-anon" }; /* @@ -603,7 +461,7 @@ static ISADevice *pc_find_fdc0(void) CheckFdcState state = { 0 }; for (i = 0; i < ARRAY_SIZE(fdc_container_path); i++) { - container = container_get(qdev_get_machine(), fdc_container_path[i]); + container = machine_get_container(fdc_container_path[i]); object_child_foreach(container, check_fdc, &state); } @@ -617,23 +475,25 @@ 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; - MC146818RtcState *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; } @@ -645,10 +505,11 @@ 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); } @@ -657,18 +518,6 @@ static void pc_cmos_init_late(void *opaque) 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 *rtc) -{ - int val; - static pc_cmos_init_late_arg arg; - X86MachineState *x86ms = X86_MACHINE(pcms); - MC146818RtcState *s = MC146818_RTC(rtc); - /* various important CMOS locations needed by PC/Bochs bios */ /* memory size */ @@ -704,26 +553,10 @@ void pc_cmos_init(PCMachineState *pcms, mc146818rtc_set_cmos_data(s, 0x5c, val >> 8); mc146818rtc_set_cmos_data(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); - val = 0; val |= 0x02; /* FPU is there */ val |= 0x04; /* PS/2 mouse installed */ mc146818rtc_set_cmos_data(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); } static void handle_a20_line_change(void *opaque, int irq, int level) @@ -741,15 +574,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) @@ -768,7 +605,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) { @@ -778,24 +615,19 @@ 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); + pci_bus_add_fw_cfg_extra_pci_roots(x86ms->fw_cfg, pcms->pcibus, + &error_abort); 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 */ @@ -816,12 +648,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") || @@ -854,7 +686,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; @@ -862,10 +693,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; @@ -874,10 +703,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 { @@ -907,13 +738,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); + + 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 don't have hole64 thus return max CPU address */ - if (cpu->phys_bits <= 32) { + /* 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; } /* @@ -1098,16 +955,23 @@ void pc_memory_init(PCMachineState *pcms, /* Initialize PC system firmware */ 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 (!is_tdx_vm()) { + option_rom_mr = g_malloc(sizeof(*option_rom_mr)); + 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, + option_rom_mr, + 1); } - memory_region_add_subregion_overlap(rom_memory, - PC_ROM_MIN_VGA, - option_rom_mr, - 1); fw_cfg = fw_cfg_arch_create(machine, x86ms->boot_cpus, x86ms->apic_id_limit); @@ -1116,23 +980,20 @@ void pc_memory_init(PCMachineState *pcms, if (machine->device_memory) { uint64_t *val = g_malloc(sizeof(*val)); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); - uint64_t res_mem_end = machine->device_memory->base; - - if (!pcmc->broken_reserved_end) { - res_mem_end += memory_region_size(&machine->device_memory->mr); - } + uint64_t res_mem_end; if (pcms->cxl_devices_state.is_enabled) { res_mem_end = cxl_resv_end; + } else { + res_mem_end = machine->device_memory->base + + memory_region_size(&machine->device_memory->mr); } *val = cpu_to_le64(ROUND_UP(res_mem_end, 1 * GiB)); fw_cfg_add_file(fw_cfg, "etc/reserved-memory-end", val, sizeof(*val)); } 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++) { @@ -1163,9 +1024,7 @@ uint64_t pc_pci_hole64_start(void) hole64_start = pc_get_cxl_range_end(pcms); } else if (pcmc->has_reserved_memory && (ms->ram_size < ms->maxram_size)) { pc_get_device_memory_range(pcms, &hole64_start, &size); - if (!pcmc->broken_reserved_end) { - hole64_start += size; - } + hole64_start += size; } else { hole64_start = pc_above_4g_end(pcms); } @@ -1177,7 +1036,6 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus) { DeviceState *dev = NULL; - rom_set_order_override(FW_CFG_ORDER_OVERRIDE_VGA); if (pci_bus) { PCIDevice *pcidev = pci_vga_init(pci_bus); dev = pcidev ? &pcidev->qdev : NULL; @@ -1185,14 +1043,14 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus) ISADevice *isadev = isa_vga_init(isa_bus); dev = isadev ? DEVICE(isadev) : NULL; } - rom_reset_order_override(); + return dev; } static const MemoryRegionOps ioport80_io_ops = { .write = ioport80_write, .read = ioport80_read, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .impl = { .min_access_size = 1, .max_access_size = 1, @@ -1202,7 +1060,7 @@ static const MemoryRegionOps ioport80_io_ops = { static const MemoryRegionOps ioportF0_io_ops = { .write = ioportF0_write, .read = ioportF0_read, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .impl = { .min_access_size = 1, .max_access_size = 1, @@ -1210,12 +1068,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); @@ -1225,14 +1083,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; } @@ -1251,7 +1115,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); @@ -1267,7 +1132,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); @@ -1281,21 +1145,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); @@ -1311,16 +1173,11 @@ void pc_basic_device_init(struct PCMachineState *pcms, 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); - } - if (rtc_irq) { + /* overwrite connection created by south bridge */ qdev_connect_gpio_out(DEVICE(rtc_state), 0, rtc_irq); - } else { - uint32_t irq = object_property_get_uint(OBJECT(rtc_state), - "irq", - &error_fatal); - isa_connect_gpio_out(rtc_state, 0, irq); } + object_property_add_alias(OBJECT(pcms), "rtc-time", OBJECT(rtc_state), "date"); @@ -1330,15 +1187,16 @@ void pc_basic_device_init(struct PCMachineState *pcms, xen_evtchn_create(IOAPIC_NUM_PINS, gsi); xen_gnttab_create(); xen_xenstore_create(); - if (pcms->bus) { - pci_create_simple(pcms->bus, -1, "xen-platform"); + if (pcms->pcibus) { + pci_create_simple(pcms->pcibus, -1, "xen-platform"); } xen_bus_init(); - xen_be_init(); } #endif - qemu_register_boot_set(pc_boot_set, rtc_state); + 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)) { @@ -1351,31 +1209,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); + + pcms->machine_done.notify = pc_machine_done; + qemu_add_machine_init_done_notifier(&pcms->machine_done); } void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus) { MachineClass *mc = MACHINE_CLASS(pcmc); - int i; + 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 : mc->default_nic; + while ((nd = qemu_find_nic_info(TYPE_ISA_NE2000, default_is_ne2k, NULL))) { + pc_init_ne2k_isa(isa_bus, nd, &error_fatal); + } - if (g_str_equal(model, "ne2k_isa")) { - pc_init_ne2k_isa(isa_bus, nd); - } else { - pci_nic_init_nofail(nd, pci_bus, model, NULL); - } + /* Anything remaining should be a PCI NIC */ + if (pci_bus) { + pci_init_nic_devices(pci_bus, mc->default_nic); } - rom_reset_order_override(); } void pc_i8259_create(ISABus *isa_bus, qemu_irq *i8259_irqs) @@ -1400,16 +1265,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. */ @@ -1430,8 +1292,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, @@ -1457,7 +1318,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. */ @@ -1494,6 +1355,20 @@ static void pc_memory_unplug(HotplugHandler *hotplug_dev, error_propagate(errp, local_err); } +static void pc_hv_balloon_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + /* 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_hv_balloon_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + memory_device_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev)); +} + static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -1507,10 +1382,11 @@ static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, /* 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); } @@ -1524,6 +1400,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); } } @@ -1536,6 +1414,8 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev, x86_cpu_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); } } @@ -1577,6 +1457,7 @@ static HotplugHandler *pc_get_hotplug_handler(MachineState *machine, object_dynamic_cast(OBJECT(dev), TYPE_CPU) || 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); } @@ -1601,6 +1482,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); @@ -1746,16 +1641,16 @@ static void pc_machine_set_max_fw_size(Object *obj, Visitor *v, } /* - * 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); @@ -1778,6 +1673,7 @@ static void pc_machine_initfn(Object *obj) #endif /* CONFIG_VMPORT */ pcms->max_ram_below_4g = 0; /* use default */ 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 = pcmc->has_acpi_build; @@ -1785,29 +1681,27 @@ static void pc_machine_initfn(Object *obj) pcms->sata_enabled = true; pcms->i8042_enabled = true; pcms->max_fw_size = 8 * MiB; -#ifdef CONFIG_HPET +#if defined(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); -} - -int pc_machine_kvm_type(MachineState *machine, const char *kvm_type) -{ - return 0; + if (pcmc->pci_enabled) { + cxl_machine_init(obj, &pcms->cxl_devices_state); + } } -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. @@ -1822,7 +1716,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(); } @@ -1845,34 +1739,26 @@ static bool pc_hotplug_allowed(MachineState *ms, DeviceState *dev, Error **errp) return true; } -static void pc_machine_class_init(ObjectClass *oc, void *data) +static void pc_machine_class_init(ObjectClass *oc, const 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; - pcmc->resizable_acpi_blob = true; + x86mc->apic_xrupt_override = true; assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = pc_get_hotplug_handler; mc->hotplug_allowed = pc_hotplug_allowed; - 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; @@ -1888,8 +1774,13 @@ 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->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L1D] = true; + mc->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L1I] = true; + mc->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L2] = true; + mc->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L3] = true; mc->default_ram_id = "pc.ram"; - pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_64; + 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, @@ -1920,6 +1811,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, @@ -1936,6 +1829,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 = { @@ -1946,7 +1843,7 @@ static const TypeInfo pc_machine_info = { .instance_init = pc_machine_initfn, .class_size = sizeof(PCMachineClass), .class_init = pc_machine_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } }, diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 8321f36f97f..ea7572e7831 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -36,42 +36,39 @@ #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 "system/kvm.h" #include "hw/i386/kvm/clock.h" #include "hw/sysbus.h" #include "hw/i2c/smbus_eeprom.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/acpi/acpi.h" -#include "hw/acpi/piix4.h" -#include "hw/usb/hcd-uhci.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/xen.h" +#include "system/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/numa.h" +#include "system/runstate.h" +#include "system/numa.h" #include "hw/hyperv/vmbus-bridge.h" #include "hw/mem/nvdimm.h" +#include "hw/uefi/var-service-api.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 @@ -95,33 +92,27 @@ static void piix_intx_routing_notifier_xen(PCIDevice *dev) { int i; - /* Scan for updates to PCI link routes (0x60-0x63). */ + /* Scan for updates to PCI link routes. */ for (i = 0; i < PIIX_NUM_PIRQS; i++) { - uint8_t v = dev->config_read(dev, PIIX_PIRQCA + i, 1); - if (v & 0x80) { - v = 0; - } - v &= 0xf; + 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 = NULL; + 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 = NULL; MemoryRegion *rom_memory = system_memory; @@ -193,18 +184,16 @@ static void pc_init1(MachineState *machine, pc_machine_init_sgx_epc(pcms); x86_cpus_init(x86ms, pcmc->default_cpu_version); - if (kvm_enabled() && pcmc->kvmclock_enabled) { + if (kvm_enabled()) { kvmclock_create(pcmc->kvmclock_create_always); } if (pcmc->pci_enabled) { - Object *phb; - pci_memory = g_new(MemoryRegion, 1); memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); rom_memory = pci_memory; - phb = OBJECT(qdev_new(host_type)); + 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); @@ -222,28 +211,16 @@ static void pc_init1(MachineState *machine, &error_fatal); sysbus_realize_and_unref(SYS_BUS_DEVICE(phb), &error_fatal); - pci_bus = PCI_BUS(qdev_get_child_bus(DEVICE(phb), "pci.0")); - pci_bus_map_irqs(pci_bus, + 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); - pcms->bus = pci_bus; hole64_size = object_property_get_uint(phb, PCI_HOST_PROP_PCI_HOLE64_SIZE, &error_abort); } - 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", mc->desc, - 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, hole64_size); @@ -261,10 +238,29 @@ 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; - - pci_dev = pci_create_simple_multifunction(pci_bus, -1, TYPE_PIIX3_DEVICE); + DeviceState *dev; + size_t i; + + 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); if (xen_enabled()) { pci_device_set_intx_routing_notifier( @@ -276,65 +272,58 @@ static void pc_init1(MachineState *machine, * connected to the IOAPIC directly. * These additional routes can be discovered through ACPI. */ - pci_bus_irqs(pci_bus, xen_intx_set_irq, pci_dev, + pci_bus_irqs(pcms->pcibus, xen_intx_set_irq, pci_dev, XEN_IOAPIC_NUM_PIRQS); } - 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")); - rtc_state = ISA_DEVICE(object_resolve_path_component(OBJECT(pci_dev), - "rtc")); + 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 { + uint32_t irq; + isa_bus = isa_bus_new(NULL, system_memory, system_io, &error_abort); + isa_bus_register_input_irqs(isa_bus, x86ms->gsi); - rtc_state = isa_new(TYPE_MC146818_RTC); - qdev_prop_set_int32(DEVICE(rtc_state), "base_year", 2000); - isa_realize_and_unref(rtc_state, isa_bus, &error_fatal); + 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); + irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", + &error_fatal); + isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); - i8257_dma_init(isa_bus, 0); + i8257_dma_init(OBJECT(machine), isa_bus, 0); pcms->hpet_enabled = false; } - isa_bus_register_input_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); - - if (pcmc->pci_enabled) { - PCIDevice *dev; + pc_nic_init(pcmc, isa_bus, pcms->pcibus); - 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; @@ -350,27 +339,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, TYPE_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. */ @@ -382,7 +358,7 @@ static void pc_init1(MachineState *machine, object_property_allow_set_link, OBJ_PROP_LINK_STRONG); object_property_set_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, - OBJECT(piix4_pm), &error_abort); + piix4_pm, &error_abort); } if (machine->nvdimms_state->is_enabled) { @@ -392,62 +368,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) -{ - X86MachineState *x86ms = X86_MACHINE(machine); - if (kvm_enabled()) { - x86ms->smm = ON_OFF_AUTO_OFF; - } -} +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 void pc_compat_2_2_fn(MachineState *machine) +static int pc_get_south_bridge(Object *obj, Error **errp) { - pc_compat_2_3_fn(machine); -} + PCMachineState *pcms = PC_MACHINE(obj); + int i; -static void pc_compat_2_1_fn(MachineState *machine) -{ - pc_compat_2_2_fn(machine); - x86_cpu_change_kvm_default("svm", NULL); -} + for (i = 0; i < PCSouthBridgeOption_lookup.size; i++) { + if (g_strcmp0(PCSouthBridgeOption_lookup.array[i], + pcms->south_bridge) == 0) { + return i; + } + } -static void pc_compat_2_0_fn(MachineState *machine) -{ - pc_compat_2_1_fn(machine); + error_setg(errp, "Invalid south bridge value set"); + return 0; } -static void pc_compat_1_7_fn(MachineState *machine) +static void pc_set_south_bridge(Object *obj, int value, Error **errp) { - pc_compat_2_0_fn(machine); - x86_cpu_change_kvm_default("x2apic", NULL); -} + PCMachineState *pcms = PC_MACHINE(obj); -static void pc_compat_1_6_fn(MachineState *machine) -{ - pc_compat_1_7_fn(machine); -} + if (value < 0) { + error_setg(errp, "Value can't be negative"); + return; + } -static void pc_compat_1_5_fn(MachineState *machine) -{ - pc_compat_1_6_fn(machine); -} + if (value >= PCSouthBridgeOption_lookup.size) { + error_setg(errp, "Value too big"); + return; + } -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 @@ -457,9 +428,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) @@ -472,26 +441,27 @@ 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, false, NULL, major, minor); + +#define DEFINE_I440FX_MACHINE_AS_LATEST(major, minor) \ + DEFINE_PC_VER_MACHINE(pc_i440fx, "pc-i440fx", pc_i440fx_init, true, "pc", major, minor); static void pc_i440fx_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + ObjectClass *oc = OBJECT_CLASS(m); + pcmc->default_south_bridge = TYPE_PIIX3_DEVICE; pcmc->pci_root_uid = 0; pcmc->default_cpu_version = 1; @@ -500,38 +470,100 @@ static void pc_i440fx_machine_options(MachineClass *m) 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); + machine_class_allow_dynamic_sysbus_dev(m, TYPE_UEFI_VARS_X64); + + 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_8_2_machine_options(MachineClass *m) +static void pc_i440fx_machine_10_1_options(MachineClass *m) { pc_i440fx_machine_options(m); - m->alias = "pc"; - m->is_default = true; } -DEFINE_I440FX_MACHINE(v8_2, "pc-i440fx-8.2", NULL, - pc_i440fx_8_2_machine_options); +DEFINE_I440FX_MACHINE_AS_LATEST(10, 1); + +static void pc_i440fx_machine_10_0_options(MachineClass *m) +{ + pc_i440fx_machine_10_1_options(m); + compat_props_add(m->compat_props, hw_compat_10_0, hw_compat_10_0_len); + compat_props_add(m->compat_props, pc_compat_10_0, pc_compat_10_0_len); +} + +DEFINE_I440FX_MACHINE(10, 0); + +static void pc_i440fx_machine_9_2_options(MachineClass *m) +{ + pc_i440fx_machine_10_0_options(m); + compat_props_add(m->compat_props, hw_compat_9_2, hw_compat_9_2_len); + compat_props_add(m->compat_props, pc_compat_9_2, pc_compat_9_2_len); +} + +DEFINE_I440FX_MACHINE(9, 2); + +static void pc_i440fx_machine_9_1_options(MachineClass *m) +{ + pc_i440fx_machine_9_2_options(m); + 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_8_1_machine_options(MachineClass *m) +static void pc_i440fx_machine_8_1_options(MachineClass *m) { - pc_i440fx_8_2_machine_options(m); - m->alias = NULL; - m->is_default = false; + 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(v8_1, "pc-i440fx-8.1", NULL, - pc_i440fx_8_1_machine_options); +DEFINE_I440FX_MACHINE(8, 1); -static void pc_i440fx_8_0_machine_options(MachineClass *m) +static void pc_i440fx_machine_8_0_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_8_1_machine_options(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); @@ -539,394 +571,217 @@ static void pc_i440fx_8_0_machine_options(MachineClass *m) pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_32; } -DEFINE_I440FX_MACHINE(v8_0, "pc-i440fx-8.0", NULL, - pc_i440fx_8_0_machine_options); +DEFINE_I440FX_MACHINE(8, 0); -static void pc_i440fx_7_2_machine_options(MachineClass *m) +static void pc_i440fx_machine_7_2_options(MachineClass *m) { - pc_i440fx_8_0_machine_options(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(v7_2, "pc-i440fx-7.2", NULL, - pc_i440fx_7_2_machine_options); +DEFINE_I440FX_MACHINE(7, 2) -static void pc_i440fx_7_1_machine_options(MachineClass *m) +static void pc_i440fx_machine_7_1_options(MachineClass *m) { - pc_i440fx_7_2_machine_options(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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + pc_i440fx_machine_4_0_options(m); m->smbus_no_migration_support = true; 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); - -static void pc_i440fx_2_5_machine_options(MachineClass *m) -{ - X86MachineClass *x86mc = X86_MACHINE_CLASS(m); - - pc_i440fx_2_6_machine_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); - -static void pc_i440fx_2_4_machine_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_i440fx_2_5_machine_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; - pcmc->resizable_acpi_blob = 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, 6); #ifdef CONFIG_ISAPC static void isapc_machine_options(MachineClass *m) @@ -944,6 +799,7 @@ static void isapc_machine_options(MachineClass *m) pcmc->has_reserved_memory = false; 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); } @@ -952,20 +808,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; @@ -973,5 +829,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 2dd1158b708..33211b1876f 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -30,12 +30,13 @@ #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 "system/tcg.h" +#include "system/kvm.h" #include "hw/i386/kvm/clock.h" #include "hw/pci-host/q35.h" #include "hw/pci/pcie_port.h" @@ -44,25 +45,32 @@ #include "hw/i386/pc.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 "system/numa.h" #include "hw/hyperv/vmbus-bridge.h" #include "hw/mem/nvdimm.h" +#include "hw/uefi/var-service-api.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; @@ -123,19 +131,14 @@ static void pc_q35_init(MachineState *machine) PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); X86MachineState *x86ms = X86_MACHINE(machine); Object *phb; - PCIBus *host_bus; 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 *pci_memory = g_new(MemoryRegion, 1); GSIState *gsi_state; ISABus *isa_bus; int i; - PCIDevice *ahci; ram_addr_t lowmem; DriveInfo *hd[MAX_SATA_PORTS]; MachineClass *mc = MACHINE_GET_CLASS(machine); @@ -143,6 +146,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). @@ -189,37 +194,16 @@ static void pc_q35_init(MachineState *machine) 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 = system_memory; - } - - pc_guest_info_init(pcms); - - if (pcmc->smbios_defaults) { - /* These values are guest ABI, do not change */ - smbios_set_defaults("QEMU", mc->desc, - mc->name, pcmc->smbios_legacy_mode, - pcmc->smbios_uuid_encoded, - pcms->smbios_entry_point_type); - } - /* create pci host bus */ phb = OBJECT(qdev_new(TYPE_Q35_HOST_DEVICE)); - if (pcmc->pci_enabled) { - pci_hole64_size = object_property_get_uint(phb, - 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, system_memory, rom_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(OBJECT(machine), "q35", phb); object_property_set_link(phb, PCI_HOST_PROP_RAM_MEM, @@ -236,20 +220,28 @@ static void pc_q35_init(MachineState *machine) 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 */ - host_bus = PCI_BUS(qdev_get_child_bus(DEVICE(phb), "pcie.0")); - pcms->bus = host_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_new_multifunction(PCI_DEVFN(ICH9_LPC_DEV, ICH9_LPC_FUNC), TYPE_ICH9_LPC_DEVICE); - qdev_prop_set_bit(DEVICE(lpc), "smm-enabled", + lpc_dev = DEVICE(lpc); + qdev_prop_set_bit(lpc_dev, "smm-enabled", x86_machine_is_smm_enabled(x86ms)); - pci_realize_and_unref(lpc, host_bus, &error_fatal); + 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); - rtc_state = ISA_DEVICE(object_resolve_path_component(OBJECT(lpc), "rtc")); + 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, @@ -273,61 +265,49 @@ static void pc_q35_init(MachineState *machine) "true", true); } - /* irq lines */ - gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); - - lpc_dev = DEVICE(lpc); - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - qdev_connect_gpio_out_named(lpc_dev, ICH9_GPIO_GSI, i, x86ms->gsi[i]); - } 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); 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), "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 = 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. */ - smb = pci_create_simple_multifunction(host_bus, + smb = pci_create_simple_multifunction(pcms->pcibus, PCI_DEVFN(ICH9_SMB_DEV, ICH9_SMB_FUNC), TYPE_ICH9_SMB_DEVICE); @@ -336,11 +316,9 @@ static void pc_q35_init(MachineState *machine) 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, @@ -349,17 +327,14 @@ 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, false, NULL, major, minor); + +#define DEFINE_Q35_MACHINE_AS_LATEST(major, minor) \ + DEFINE_PC_VER_MACHINE(pc_q35, "pc-q35", pc_q35_init, false, "q35", major, minor); +#define DEFINE_Q35_MACHINE_BUGFIX(major, minor, micro) \ + DEFINE_PC_VER_MACHINE(pc_q35, "pc-q35", pc_q35_init, false, NULL, major, minor, micro); static void pc_q35_machine_options(MachineClass *m) { @@ -375,39 +350,92 @@ static void pc_q35_machine_options(MachineClass *m) m->default_nic = "e1000e"; m->default_kernel_irqchip_split = false; m->no_floppy = 1; - m->max_cpus = 1024; + 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); + machine_class_allow_dynamic_sysbus_dev(m, TYPE_UEFI_VARS_X64); + compat_props_add(m->compat_props, + pc_q35_compat_defaults, pc_q35_compat_defaults_len); } -static void pc_q35_8_2_machine_options(MachineClass *m) +static void pc_q35_machine_10_1_options(MachineClass *m) { pc_q35_machine_options(m); - m->alias = "q35"; } -DEFINE_Q35_MACHINE(v8_2, "pc-q35-8.2", NULL, - pc_q35_8_2_machine_options); +DEFINE_Q35_MACHINE_AS_LATEST(10, 1); + +static void pc_q35_machine_10_0_options(MachineClass *m) +{ + pc_q35_machine_10_1_options(m); + compat_props_add(m->compat_props, hw_compat_10_0, hw_compat_10_0_len); + compat_props_add(m->compat_props, pc_compat_10_0, pc_compat_10_0_len); +} + +DEFINE_Q35_MACHINE(10, 0); + +static void pc_q35_machine_9_2_options(MachineClass *m) +{ + pc_q35_machine_10_0_options(m); + compat_props_add(m->compat_props, hw_compat_9_2, hw_compat_9_2_len); + compat_props_add(m->compat_props, pc_compat_9_2, pc_compat_9_2_len); +} + +DEFINE_Q35_MACHINE(9, 2); + +static void pc_q35_machine_9_1_options(MachineClass *m) +{ + pc_q35_machine_9_2_options(m); + 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_8_1_machine_options(MachineClass *m) +static void pc_q35_machine_9_0_options(MachineClass *m) { - pc_q35_8_2_machine_options(m); - m->alias = NULL; + 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(v8_1, "pc-q35-8.1", NULL, - pc_q35_8_1_machine_options); +DEFINE_Q35_MACHINE(8, 1); -static void pc_q35_8_0_machine_options(MachineClass *m) +static void pc_q35_machine_8_0_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_8_1_machine_options(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); @@ -416,132 +444,120 @@ static void pc_q35_8_0_machine_options(MachineClass *m) m->max_cpus = 288; } -DEFINE_Q35_MACHINE(v8_0, "pc-q35-8.0", NULL, - pc_q35_8_0_machine_options); +DEFINE_Q35_MACHINE(8, 0); -static void pc_q35_7_2_machine_options(MachineClass *m) +static void pc_q35_machine_7_2_options(MachineClass *m) { - pc_q35_8_0_machine_options(m); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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 @@ -552,24 +568,22 @@ 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; /* 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; pcmc->pvh_enabled = false; @@ -577,121 +591,84 @@ static void pc_q35_3_1_machine_options(MachineClass *m) 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) { - pc_q35_2_12_machine_options(m); + 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); - -static void pc_q35_2_5_machine_options(MachineClass *m) -{ - X86MachineClass *x86mc = X86_MACHINE_CLASS(m); - - pc_q35_2_6_machine_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); - -static void pc_q35_2_4_machine_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_q35_2_5_machine_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, 6); diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index c8d9e71b889..821396c16e9 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/units.h" @@ -35,17 +35,16 @@ #include "hw/loader.h" #include "hw/qdev-properties.h" #include "hw/block/flash.h" -#include "sysemu/kvm.h" -#include "sev.h" +#include "system/kvm.h" +#include "target/i386/sev.h" +#include "kvm/tdx.h" #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 +52,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 +71,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 +112,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 +142,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 +156,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 +187,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 +220,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 +241,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 +259,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 +270,22 @@ 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); + } else if (is_tdx_vm()) { + ret = tdx_parse_tdvf(ptr, size); + if (ret) { + error_report("failed to parse TDVF for TDX VM"); + exit(1); + } } } diff --git a/hw/i386/pc_sysfw_ovmf.c b/hw/i386/pc_sysfw_ovmf.c index 07a4c267faa..da947c3ca41 100644 --- a/hw/i386/pc_sysfw_ovmf.c +++ b/hw/i386/pc_sysfw_ovmf.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "hw/i386/pc.h" +#include "exec/target_page.h" #include "cpu.h" #define OVMF_TABLE_FOOTER_GUID "96b582de-1fb2-45f7-baea-a366c55a082d" diff --git a/hw/i386/port92.c b/hw/i386/port92.c index e1379a4f980..39b6f3178fe 100644 --- a/hw/i386/port92.c +++ b/hw/i386/port92.c @@ -7,9 +7,10 @@ */ #include "qemu/osdep.h" -#include "sysemu/runstate.h" +#include "system/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() } @@ -96,12 +97,12 @@ static void port92_realizefn(DeviceState *dev, Error **errp) isa_register_ioport(isadev, &s->io, 0x92); } -static void port92_class_initfn(ObjectClass *klass, void *data) +static void port92_class_initfn(ObjectClass *klass, const 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-epc.c b/hw/i386/sgx-epc.c index d664829d354..2b3b2823b5f 100644 --- a/hw/i386/sgx-epc.c +++ b/hw/i386/sgx-epc.c @@ -17,14 +17,13 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "target/i386/cpu.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" -static Property sgx_epc_properties[] = { +static const Property sgx_epc_properties[] = { DEFINE_PROP_UINT64(SGX_EPC_ADDR_PROP, SGXEPCDevice, addr, 0), DEFINE_PROP_UINT32(SGX_EPC_NUMA_NODE_PROP, SGXEPCDevice, node, 0), DEFINE_PROP_LINK(SGX_EPC_MEMDEV_PROP, SGXEPCDevice, hostmem, TYPE_MEMORY_BACKEND_EPC, HostMemoryBackendEpc *), - DEFINE_PROP_END_OF_LIST(), }; static void sgx_epc_get_size(Object *obj, Visitor *v, const char *name, @@ -148,7 +147,7 @@ static void sgx_epc_md_fill_device_info(const MemoryDeviceState *md, info->type = MEMORY_DEVICE_INFO_KIND_SGX_EPC; } -static void sgx_epc_class_init(ObjectClass *oc, void *data) +static void sgx_epc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc); @@ -174,7 +173,7 @@ static const TypeInfo sgx_epc_info = { .instance_init = sgx_epc_init, .class_init = sgx_epc_class_init, .class_size = sizeof(DeviceClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_MEMORY_DEVICE }, { } }, diff --git a/hw/i386/sgx-stub.c b/hw/i386/sgx-stub.c index 26833eb233c..d295e54d239 100644 --- a/hw/i386/sgx-stub.c +++ b/hw/i386/sgx-stub.c @@ -3,20 +3,20 @@ #include "monitor/hmp-target.h" #include "hw/i386/pc.h" #include "hw/i386/sgx-epc.h" +#include "qapi/qapi-commands-misc-i386.h" #include "qapi/error.h" -#include "qapi/qapi-commands-misc-target.h" void sgx_epc_build_srat(GArray *table_data) { } -SGXInfo *qmp_query_sgx(Error **errp) +SgxInfo *qmp_query_sgx(Error **errp) { error_setg(errp, "SGX support is not compiled in"); return NULL; } -SGXInfo *qmp_query_sgx_capabilities(Error **errp) +SgxInfo *qmp_query_sgx_capabilities(Error **errp) { error_setg(errp, "SGX support is not compiled in"); return NULL; @@ -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 70305547d4a..e2801546ad6 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -19,10 +19,10 @@ #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" -#include "sysemu/reset.h" +#include "qapi/qapi-commands-misc-i386.h" +#include "system/address-spaces.h" +#include "system/hw_accel.h" +#include "system/reset.h" #include #include "hw/acpi/aml-build.h" @@ -84,10 +84,10 @@ 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(void) +static SgxEpcSectionList *sgx_calc_host_epc_sections(void) { - SGXEPCSectionList *head = NULL, **tail = &head; - SGXEPCSection *section; + SgxEpcSectionList *head = NULL, **tail = &head; + SgxEpcSection *section; uint32_t i, type; uint32_t eax, ebx, ecx, edx; uint32_t j = 0; @@ -104,7 +104,7 @@ static SGXEPCSectionList *sgx_calc_host_epc_sections(void) break; } - section = g_new0(SGXEPCSection, 1); + section = g_new0(SgxEpcSection, 1); section->node = j++; section->size = sgx_calc_section_metric(ecx, edx); QAPI_LIST_APPEND(tail, section); @@ -153,18 +153,20 @@ static void sgx_epc_reset(void *opaque) } } -SGXInfo *qmp_query_sgx_capabilities(Error **errp) +SgxInfo *qmp_query_sgx_capabilities(Error **errp) { - SGXInfo *info = NULL; + SgxInfo *info = NULL; uint32_t eax, ebx, ecx, edx; + 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; } - info = g_new0(SGXInfo, 1); + info = g_new0(SgxInfo, 1); host_cpuid(0x7, 0, &eax, &ebx, &ecx, &edx); info->sgx = ebx & (1U << 2) ? true : false; @@ -181,17 +183,17 @@ SGXInfo *qmp_query_sgx_capabilities(Error **errp) return info; } -static SGXEPCSectionList *sgx_get_epc_sections_list(void) +static SgxEpcSectionList *sgx_get_epc_sections_list(void) { GSList *device_list = sgx_epc_get_device_list(); - SGXEPCSectionList *head = NULL, **tail = &head; - SGXEPCSection *section; + SgxEpcSectionList *head = NULL, **tail = &head; + SgxEpcSection *section; for (; device_list; device_list = device_list->next) { DeviceState *dev = device_list->data; Object *obj = OBJECT(dev); - section = g_new0(SGXEPCSection, 1); + section = g_new0(SgxEpcSection, 1); section->node = object_property_get_uint(obj, SGX_EPC_NUMA_NODE_PROP, &error_abort); section->size = object_property_get_uint(obj, SGX_EPC_SIZE_PROP, @@ -203,9 +205,9 @@ static SGXEPCSectionList *sgx_get_epc_sections_list(void) return head; } -SGXInfo *qmp_query_sgx(Error **errp) +SgxInfo *qmp_query_sgx(Error **errp) { - SGXInfo *info = NULL; + SgxInfo *info = NULL; X86MachineState *x86ms; PCMachineState *pcms = (PCMachineState *)object_dynamic_cast(qdev_get_machine(), @@ -221,7 +223,7 @@ SGXInfo *qmp_query_sgx(Error **errp) return NULL; } - info = g_new0(SGXInfo, 1); + info = g_new0(SgxInfo, 1); info->sgx = true; info->sgx1 = true; @@ -235,8 +237,8 @@ SGXInfo *qmp_query_sgx(Error **errp) void hmp_info_sgx(Monitor *mon, const QDict *qdict) { Error *err = NULL; - SGXEPCSectionList *section_list, *section; - g_autoptr(SGXInfo) info = qmp_query_sgx(&err); + SgxEpcSectionList *section_list, *section; + g_autoptr(SgxInfo) info = qmp_query_sgx(&err); uint64_t size = 0; if (err) { @@ -264,12 +266,22 @@ void hmp_info_sgx(Monitor *mon, const QDict *qdict) 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; } @@ -286,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) { @@ -300,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/tdvf-hob.c b/hw/i386/tdvf-hob.c new file mode 100644 index 00000000000..782b3d15787 --- /dev/null +++ b/hw/i386/tdvf-hob.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2025 Intel Corporation + * Author: Isaku Yamahata + * + * Xiaoyao Li + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "standard-headers/uefi/uefi.h" +#include "hw/pci/pcie_host.h" +#include "tdvf-hob.h" + +typedef struct TdvfHob { + hwaddr hob_addr; + void *ptr; + int size; + + /* working area */ + void *current; + void *end; +} TdvfHob; + +static uint64_t tdvf_current_guest_addr(const TdvfHob *hob) +{ + return hob->hob_addr + (hob->current - hob->ptr); +} + +static void tdvf_align(TdvfHob *hob, size_t align) +{ + hob->current = QEMU_ALIGN_PTR_UP(hob->current, align); +} + +static void *tdvf_get_area(TdvfHob *hob, uint64_t size) +{ + void *ret; + + if (hob->current + size > hob->end) { + error_report("TD_HOB overrun, size = 0x%" PRIx64, size); + exit(1); + } + + ret = hob->current; + hob->current += size; + tdvf_align(hob, 8); + return ret; +} + +static void tdvf_hob_add_memory_resources(TdxGuest *tdx, TdvfHob *hob) +{ + EFI_HOB_RESOURCE_DESCRIPTOR *region; + EFI_RESOURCE_ATTRIBUTE_TYPE attr; + EFI_RESOURCE_TYPE resource_type; + + TdxRamEntry *e; + int i; + + for (i = 0; i < tdx->nr_ram_entries; i++) { + e = &tdx->ram_entries[i]; + + if (e->type == TDX_RAM_UNACCEPTED) { + resource_type = EFI_RESOURCE_MEMORY_UNACCEPTED; + attr = EFI_RESOURCE_ATTRIBUTE_TDVF_UNACCEPTED; + } else if (e->type == TDX_RAM_ADDED) { + resource_type = EFI_RESOURCE_SYSTEM_MEMORY; + attr = EFI_RESOURCE_ATTRIBUTE_TDVF_PRIVATE; + } else { + error_report("unknown TDX_RAM_ENTRY type %d", e->type); + exit(1); + } + + region = tdvf_get_area(hob, sizeof(*region)); + *region = (EFI_HOB_RESOURCE_DESCRIPTOR) { + .Header = { + .HobType = EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, + .HobLength = cpu_to_le16(sizeof(*region)), + .Reserved = cpu_to_le32(0), + }, + .Owner = EFI_HOB_OWNER_ZERO, + .ResourceType = cpu_to_le32(resource_type), + .ResourceAttribute = cpu_to_le32(attr), + .PhysicalStart = cpu_to_le64(e->address), + .ResourceLength = cpu_to_le64(e->length), + }; + } +} + +void tdvf_hob_create(TdxGuest *tdx, TdxFirmwareEntry *td_hob) +{ + TdvfHob hob = { + .hob_addr = td_hob->address, + .size = td_hob->size, + .ptr = td_hob->mem_ptr, + + .current = td_hob->mem_ptr, + .end = td_hob->mem_ptr + td_hob->size, + }; + + EFI_HOB_GENERIC_HEADER *last_hob; + EFI_HOB_HANDOFF_INFO_TABLE *hit; + + /* Note, Efi{Free}Memory{Bottom,Top} are ignored, leave 'em zeroed. */ + hit = tdvf_get_area(&hob, sizeof(*hit)); + *hit = (EFI_HOB_HANDOFF_INFO_TABLE) { + .Header = { + .HobType = EFI_HOB_TYPE_HANDOFF, + .HobLength = cpu_to_le16(sizeof(*hit)), + .Reserved = cpu_to_le32(0), + }, + .Version = cpu_to_le32(EFI_HOB_HANDOFF_TABLE_VERSION), + .BootMode = cpu_to_le32(0), + .EfiMemoryTop = cpu_to_le64(0), + .EfiMemoryBottom = cpu_to_le64(0), + .EfiFreeMemoryTop = cpu_to_le64(0), + .EfiFreeMemoryBottom = cpu_to_le64(0), + .EfiEndOfHobList = cpu_to_le64(0), /* initialized later */ + }; + + tdvf_hob_add_memory_resources(tdx, &hob); + + last_hob = tdvf_get_area(&hob, sizeof(*last_hob)); + *last_hob = (EFI_HOB_GENERIC_HEADER) { + .HobType = EFI_HOB_TYPE_END_OF_HOB_LIST, + .HobLength = cpu_to_le16(sizeof(*last_hob)), + .Reserved = cpu_to_le32(0), + }; + hit->EfiEndOfHobList = tdvf_current_guest_addr(&hob); +} diff --git a/hw/i386/tdvf-hob.h b/hw/i386/tdvf-hob.h new file mode 100644 index 00000000000..4fc6a3740a5 --- /dev/null +++ b/hw/i386/tdvf-hob.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef HW_I386_TD_HOB_H +#define HW_I386_TD_HOB_H + +#include "hw/i386/tdvf.h" +#include "target/i386/kvm/tdx.h" + +void tdvf_hob_create(TdxGuest *tdx, TdxFirmwareEntry *td_hob); + +#define EFI_RESOURCE_ATTRIBUTE_TDVF_PRIVATE \ + (EFI_RESOURCE_ATTRIBUTE_PRESENT | \ + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \ + EFI_RESOURCE_ATTRIBUTE_TESTED) + +#define EFI_RESOURCE_ATTRIBUTE_TDVF_UNACCEPTED \ + (EFI_RESOURCE_ATTRIBUTE_PRESENT | \ + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \ + EFI_RESOURCE_ATTRIBUTE_TESTED) + +#define EFI_RESOURCE_ATTRIBUTE_TDVF_MMIO \ + (EFI_RESOURCE_ATTRIBUTE_PRESENT | \ + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \ + EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE) + +#endif diff --git a/hw/i386/tdvf.c b/hw/i386/tdvf.c new file mode 100644 index 00000000000..645d9d1294b --- /dev/null +++ b/hw/i386/tdvf.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2025 Intel Corporation + * Author: Isaku Yamahata + * + * Xiaoyao Li + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" + +#include "hw/i386/pc.h" +#include "hw/i386/tdvf.h" +#include "system/kvm.h" + +#define TDX_METADATA_OFFSET_GUID "e47a6535-984a-4798-865e-4685a7bf8ec2" +#define TDX_METADATA_VERSION 1 +#define TDVF_SIGNATURE 0x46564454 /* TDVF as little endian */ +#define TDVF_ALIGNMENT 4096 + +/* + * the raw structs read from TDVF keeps the name convention in + * TDVF Design Guide spec. + */ +typedef struct { + uint32_t DataOffset; + uint32_t RawDataSize; + uint64_t MemoryAddress; + uint64_t MemoryDataSize; + uint32_t Type; + uint32_t Attributes; +} TdvfSectionEntry; + +typedef struct { + uint32_t Signature; + uint32_t Length; + uint32_t Version; + uint32_t NumberOfSectionEntries; + TdvfSectionEntry SectionEntries[]; +} TdvfMetadata; + +struct tdx_metadata_offset { + uint32_t offset; +}; + +static TdvfMetadata *tdvf_get_metadata(void *flash_ptr, int size) +{ + TdvfMetadata *metadata; + uint32_t offset = 0; + uint8_t *data; + + if ((uint32_t) size != size) { + return NULL; + } + + if (pc_system_ovmf_table_find(TDX_METADATA_OFFSET_GUID, &data, NULL)) { + offset = size - le32_to_cpu(((struct tdx_metadata_offset *)data)->offset); + + if (offset + sizeof(*metadata) > size) { + return NULL; + } + } else { + error_report("Cannot find TDX_METADATA_OFFSET_GUID"); + return NULL; + } + + metadata = flash_ptr + offset; + + /* Finally, verify the signature to determine if this is a TDVF image. */ + metadata->Signature = le32_to_cpu(metadata->Signature); + if (metadata->Signature != TDVF_SIGNATURE) { + error_report("Invalid TDVF signature in metadata!"); + return NULL; + } + + /* Sanity check that the TDVF doesn't overlap its own metadata. */ + metadata->Length = le32_to_cpu(metadata->Length); + if (offset + metadata->Length > size) { + return NULL; + } + + /* Only version 1 is supported/defined. */ + metadata->Version = le32_to_cpu(metadata->Version); + if (metadata->Version != TDX_METADATA_VERSION) { + return NULL; + } + + return metadata; +} + +static int tdvf_parse_and_check_section_entry(const TdvfSectionEntry *src, + TdxFirmwareEntry *entry) +{ + entry->data_offset = le32_to_cpu(src->DataOffset); + entry->data_len = le32_to_cpu(src->RawDataSize); + entry->address = le64_to_cpu(src->MemoryAddress); + entry->size = le64_to_cpu(src->MemoryDataSize); + entry->type = le32_to_cpu(src->Type); + entry->attributes = le32_to_cpu(src->Attributes); + + /* sanity check */ + if (entry->size < entry->data_len) { + error_report("Broken metadata RawDataSize 0x%x MemoryDataSize 0x%"PRIx64, + entry->data_len, entry->size); + return -1; + } + if (!QEMU_IS_ALIGNED(entry->address, TDVF_ALIGNMENT)) { + error_report("MemoryAddress 0x%"PRIx64" not page aligned", entry->address); + return -1; + } + if (!QEMU_IS_ALIGNED(entry->size, TDVF_ALIGNMENT)) { + error_report("MemoryDataSize 0x%"PRIx64" not page aligned", entry->size); + return -1; + } + + switch (entry->type) { + case TDVF_SECTION_TYPE_BFV: + case TDVF_SECTION_TYPE_CFV: + /* The sections that must be copied from firmware image to TD memory */ + if (entry->data_len == 0) { + error_report("%d section with RawDataSize == 0", entry->type); + return -1; + } + break; + case TDVF_SECTION_TYPE_TD_HOB: + case TDVF_SECTION_TYPE_TEMP_MEM: + /* The sections that no need to be copied from firmware image */ + if (entry->data_len != 0) { + error_report("%d section with RawDataSize 0x%x != 0", + entry->type, entry->data_len); + return -1; + } + break; + default: + error_report("TDVF contains unsupported section type %d", entry->type); + return -1; + } + + return 0; +} + +int tdvf_parse_metadata(TdxFirmware *fw, void *flash_ptr, int size) +{ + g_autofree TdvfSectionEntry *sections = NULL; + TdvfMetadata *metadata; + ssize_t entries_size; + int i; + + metadata = tdvf_get_metadata(flash_ptr, size); + if (!metadata) { + return -EINVAL; + } + + /* load and parse metadata entries */ + fw->nr_entries = le32_to_cpu(metadata->NumberOfSectionEntries); + if (fw->nr_entries < 2) { + error_report("Invalid number of fw entries (%u) in TDVF Metadata", + fw->nr_entries); + return -EINVAL; + } + + entries_size = fw->nr_entries * sizeof(TdvfSectionEntry); + if (metadata->Length != sizeof(*metadata) + entries_size) { + error_report("TDVF metadata len (0x%x) mismatch, expected (0x%x)", + metadata->Length, + (uint32_t)(sizeof(*metadata) + entries_size)); + return -EINVAL; + } + + fw->entries = g_new(TdxFirmwareEntry, fw->nr_entries); + sections = g_new(TdvfSectionEntry, fw->nr_entries); + + memcpy(sections, (void *)metadata + sizeof(*metadata), entries_size); + + for (i = 0; i < fw->nr_entries; i++) { + if (tdvf_parse_and_check_section_entry(§ions[i], &fw->entries[i])) { + goto err; + } + } + + fw->mem_ptr = flash_ptr; + return 0; + +err: + fw->entries = 0; + g_free(fw->entries); + return -EINVAL; +} diff --git a/hw/i386/trace-events b/hw/i386/trace-events index 53c02d7ac85..ac9e1a10aa4 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -68,6 +68,7 @@ vtd_frr_new(int index, uint64_t hi, uint64_t lo) "index %d high 0x%"PRIx64" low vtd_warn_invalid_qi_tail(uint16_t tail) "tail 0x%"PRIx16 vtd_warn_ir_vector(uint16_t sid, int index, int vec, int target) "sid 0x%"PRIx16" index %d vec %d (should be: %d)" vtd_warn_ir_trigger(uint16_t sid, int index, int trig, int target) "sid 0x%"PRIx16" index %d trigger %d (should be: %d)" +vtd_reset_exit(void) "" # amd_iommu.c amdvi_evntlog_fail(uint64_t addr, uint32_t head) "error: fail to write at addr 0x%"PRIx64" + offset 0x%"PRIx32 diff --git a/hw/i386/vapic.c b/hw/i386/vapic.c new file mode 100644 index 00000000000..0c1c92c4793 --- /dev/null +++ b/hw/i386/vapic.c @@ -0,0 +1,871 @@ +/* + * 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 "exec/target_page.h" +#include "system/system.h" +#include "system/cpus.h" +#include "system/hw_accel.h" +#include "system/kvm.h" +#include "system/runstate.h" +#include "system/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_LITTLE_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, const 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 91320afa2f6..3896159b055 100644 --- a/hw/i386/vmmouse.c +++ b/hw/i386/vmmouse.c @@ -277,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), @@ -317,17 +317,16 @@ static void vmmouse_realizefn(DeviceState *dev, Error **errp) vmport_register(VMPORT_CMD_VMMOUSE_DATA, vmmouse_ioport_read, s); } -static Property vmmouse_properties[] = { +static const Property vmmouse_properties[] = { DEFINE_PROP_LINK("i8042", VMMouseState, i8042, TYPE_I8042, ISAKBDState *), - DEFINE_PROP_END_OF_LIST(), }; -static void vmmouse_class_initfn(ObjectClass *klass, void *data) +static void vmmouse_class_initfn(ObjectClass *klass, const 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/vmport.c b/hw/i386/vmport.c index 7cc75dbc6da..6d93457c526 100644 --- a/hw/i386/vmport.c +++ b/hw/i386/vmport.c @@ -33,9 +33,9 @@ #include "hw/i386/vmport.h" #include "hw/qdev-properties.h" #include "hw/boards.h" -#include "sysemu/sysemu.h" -#include "sysemu/hw_accel.h" -#include "sysemu/qtest.h" +#include "system/system.h" +#include "system/hw_accel.h" +#include "system/qtest.h" #include "qemu/log.h" #include "trace.h" #include "qom/object.h" @@ -252,7 +252,7 @@ static void vmport_realizefn(DeviceState *dev, Error **errp) } } -static Property vmport_properties[] = { +static const Property vmport_properties[] = { /* Used to enforce compatibility for migration */ DEFINE_PROP_BIT("x-read-set-eax", VMPortState, compat_flags, VMPORT_COMPAT_READ_SET_EAX_BIT, true), @@ -284,11 +284,9 @@ static Property vmport_properties[] = { * 5 - ACE 1.x (Deprecated) */ DEFINE_PROP_UINT8("vmware-vmx-type", VMPortState, vmware_vmx_type, 2), - - DEFINE_PROP_END_OF_LIST(), }; -static void vmport_class_initfn(ObjectClass *klass, void *data) +static void vmport_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c new file mode 100644 index 00000000000..b1b5f11e739 --- /dev/null +++ b/hw/i386/x86-common.c @@ -0,0 +1,1081 @@ +/* + * 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 "system/numa.h" +#include "system/system.h" +#include "system/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" +#include "kvm/tdx.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); + X86CPUTopoInfo *topo_info = &env->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) { + set_bit(CPU_TOPOLOGY_LEVEL_MODULE, env->avail_cpu_topo); + } + + if (ms->smp.dies > 1) { + 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 > (ms->smp.cores - 1)) { + error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u", + cpu->core_id, ms->smp.cores - 1); + return; + } + if (cpu->thread_id < 0) { + error_setg(errp, "CPU thread-id is not set"); + return; + } else if (cpu->thread_id > (ms->smp.threads - 1)) { + error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u", + cpu->thread_id, ms->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; + bool bypass_ioapic = false; + + trace_x86_gsi_interrupt(n, level); + +#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. + * + * Additionally, this hook allows the Xen event channel GSI to + * work around QEMU's lack of support for shared level interrupts, + * by keeping track of the externally driven state of the pin and + * implementing a logical OR with the state of the evtchn GSI. + */ + if (xen_mode == XEN_EMULATE) { + bypass_ioapic = xen_evtchn_set_gsi(n, &level); + } +#endif + + 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: + if (!bypass_ioapic) { + 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, + ELFDATA2LSB, 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); + } + + 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); + } + fseek(f, 0, SEEK_SET); + 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() && protocol > 0) { + 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 - setup_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, + kernel + setup_size, kernel_size - setup_size); + sev_load_ctx.kernel_data = (char *)kernel + setup_size; + sev_load_ctx.kernel_size = kernel_size - setup_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; + + /* kernel without setup header patches */ + fw_cfg_add_file(fw_cfg, "etc/boot/kernel", kernel, kernel_size); + + if (machine->shim_filename) { + GMappedFile *mapped_file; + GError *gerr = NULL; + + mapped_file = g_mapped_file_new(machine->shim_filename, false, &gerr); + if (!mapped_file) { + fprintf(stderr, "qemu: error reading shim %s: %s\n", + machine->shim_filename, gerr->message); + exit(1); + } + + fw_cfg_add_file(fw_cfg, "etc/boot/shim", + g_mapped_file_get_contents(mapped_file), + g_mapped_file_get_length(mapped_file)); + } + + 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); + if (is_tdx_vm()) { + tdx_set_tdvf_region(&x86ms->bios); + } + } else { + memory_region_init_ram(&x86ms->bios, NULL, "pc.bios", + bios_size, &error_fatal); + } + if (sev_enabled() || is_tdx_vm()) { + /* + * 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 00000000000..c876e6709e0 --- /dev/null +++ b/hw/i386/x86-cpu.c @@ -0,0 +1,97 @@ +/* + * 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 "system/whpx.h" +#include "system/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 "system/kvm.h" + +/* TSC handling */ +uint64_t cpu_get_tsc(CPUX86State *env) +{ + return cpus_get_elapsed_ticks(); +} + +/* 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 726e9e1d16e..d34a6849f4a 100644 --- a/hw/i386/x86-iommu.c +++ b/hw/i386/x86-iommu.c @@ -25,7 +25,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "trace.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" void x86_iommu_iec_register_notifier(X86IOMMUState *iommu, iec_notify_fn fn, void *data) @@ -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; @@ -125,15 +125,14 @@ static void x86_iommu_realize(DeviceState *dev, Error **errp) } } -static Property x86_iommu_properties[] = { +static const Property x86_iommu_properties[] = { DEFINE_PROP_ON_OFF_AUTO("intremap", X86IOMMUState, intr_supported, ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("device-iotlb", X86IOMMUState, dt_supported, false), DEFINE_PROP_BOOL("pt", X86IOMMUState, pt_supported, true), - DEFINE_PROP_END_OF_LIST(), }; -static void x86_iommu_class_init(ObjectClass *klass, void *data) +static void x86_iommu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = x86_iommu_realize; @@ -147,7 +146,7 @@ bool x86_iommu_ir_supported(X86IOMMUState *s) static const TypeInfo x86_iommu_info = { .name = TYPE_X86_IOMMU_DEVICE, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_DYNAMIC_SYS_BUS_DEVICE, .instance_size = sizeof(X86IOMMUState), .class_init = x86_iommu_class_init, .class_size = sizeof(X86IOMMUClass), diff --git a/hw/i386/x86.c b/hw/i386/x86.c index f034df8bf62..f80533df1c5 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -22,57 +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/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 "system/qtest.h" +#include "system/numa.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" -#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 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; } @@ -94,345 +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? With KVM, that requires - * both in-kernel lapic and X2APIC userspace API. - */ - if (x86ms->apic_id_limit > 255 && kvm_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 *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. - */ -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_enabled() && !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); @@ -442,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); @@ -456,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; @@ -493,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; @@ -509,690 +155,12 @@ 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) -{ - return cpus_get_elapsed_ticks(); -} - -/* 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); - } - } -} - -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: -#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, 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; + cpu_interrupt(cs, CPU_INTERRUPT_NMI); } } - 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) @@ -1274,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); } @@ -1379,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,7 +372,7 @@ static void x86_machine_initfn(Object *obj) x86ms->above_4g_mem_start = 4 * GiB; } -static void x86_machine_class_init(ObjectClass *oc, void *data) +static void x86_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); X86MachineClass *x86mc = X86_MACHINE_CLASS(oc); @@ -1403,7 +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; - x86mc->save_tsc_khz = true; + mc->kvm_type = x86_kvm_type; x86mc->fwcfg_dma_enabled = true; nc->nmi_monitor_handler = x86_nmi; @@ -1471,7 +449,7 @@ static const TypeInfo x86_machine_info = { .instance_init = x86_machine_initfn, .class_size = sizeof(X86MachineClass), .class_init = x86_machine_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NMI }, { } }, diff --git a/hw/i386/xen/meson.build b/hw/i386/xen/meson.build index 3dc4c4f106e..c73c62b8e39 100644 --- a/hw/i386/xen/meson.build +++ b/hw/i386/xen/meson.build @@ -1,8 +1,11 @@ i386_ss.add(when: 'CONFIG_XEN', if_true: files( - 'xen-hvm.c', 'xen_apic.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/xen-hvm.c b/hw/i386/xen/xen-hvm.c index f42621e6742..ceb2242aa71 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -10,10 +10,12 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/qapi-commands-migration.h" #include "trace.h" +#include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/irq.h" #include "hw/i386/apic-msidef.h" @@ -23,6 +25,11 @@ #include "hw/xen/xen-hvm-common.h" #include "hw/xen/arch_hvm.h" #include +#include "exec/target_page.h" +#include "target/i386/cpu.h" +#include "system/runstate.h" +#include "system/xen-mapcache.h" +#include "system/xen.h" static MemoryRegion ram_640k, ram_lo, ram_hi; static MemoryRegion *framebuffer; @@ -149,12 +156,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. @@ -163,22 +170,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); } } -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)) { @@ -188,9 +196,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) { @@ -245,6 +254,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; @@ -252,7 +264,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) { @@ -292,9 +304,9 @@ static int xen_add_to_physmap(XenIOState *state, 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) { @@ -308,8 +320,8 @@ static int xen_add_to_physmap(XenIOState *state, } 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)); @@ -321,11 +333,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; } @@ -336,9 +351,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) { @@ -367,13 +382,16 @@ 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; @@ -387,7 +405,7 @@ static void xen_sync_dirty_bitmap(XenIOState *state, return; } - rc = xen_track_dirty_vram(xen_domid, start_addr >> TARGET_PAGE_BITS, + rc = xen_track_dirty_vram(xen_domid, start_addr >> target_page_bits, npages, dirty_bitmap); if (rc < 0) { #ifndef ENODATA @@ -408,8 +426,7 @@ static void xen_sync_dirty_bitmap(XenIOState *state, 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); }; } } @@ -446,11 +463,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) @@ -572,6 +590,26 @@ static void xen_wakeup_notifier(Notifier *notifier, void *data) xc_set_hvm_param(xen_xc, xen_domid, HVM_PARAM_ACPI_S_STATE, 0); } +static bool xen_check_stubdomain(struct xs_handle *xsh) +{ + 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; + + val = xs_read(xsh, 0, dm_path, NULL); + if (val) { + if (sscanf(val, "%d", &dm_domid) == 1) { + is_stubdom = dm_domid != 0; + } + free(val); + } + + g_free(dm_path); + return is_stubdom; +} + void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) { MachineState *ms = MACHINE(pcms); @@ -582,7 +620,11 @@ void xen_hvm_init_pc(PCMachineState *pcms, MemoryRegion **ram_memory) state = g_new0(XenIOState, 1); - xen_register_ioreq(state, max_cpus, &xen_memory_listener); + xen_register_ioreq(state, max_cpus, + HVM_IOREQSRV_BUFIOREQ_ATOMIC, + &xen_memory_listener); + + xen_is_stubdomain = xen_check_stubdomain(state->xenstore); QLIST_INIT(&xen_physmap); xen_read_physmap(state); @@ -629,17 +671,21 @@ void xen_register_framebuffer(MemoryRegion *mr) 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) { @@ -653,7 +699,7 @@ 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); } @@ -662,6 +708,9 @@ void qmp_xen_set_global_dirty_log(bool enable, Error **errp) 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); @@ -677,8 +726,8 @@ void arch_xen_set_memory(XenIOState *state, MemoryRegionSection *section, trace_xen_client_set_memory(start_addr, size, log_dirty); - start_addr &= TARGET_PAGE_MASK; - size = TARGET_PAGE_ALIGN(size); + start_addr &= page_mask; + size = ROUND_UP(size, page_size); if (add) { if (!memory_region_is_rom(section->mr)) { @@ -687,8 +736,8 @@ void arch_xen_set_memory(XenIOState *state, MemoryRegionSection *section, } else { mem_type = HVMMEM_ram_ro; if (xen_set_mem_type(xen_domid, mem_type, - start_addr >> TARGET_PAGE_BITS, - size >> TARGET_PAGE_BITS)) { + start_addr >> target_page_bits, + size >> target_page_bits)) { DPRINTF("xen_set_mem_type error, addr: "HWADDR_FMT_plx"\n", start_addr); } @@ -709,6 +758,4 @@ void arch_handle_ioreq(XenIOState *state, ioreq_t *req) default: hw_error("Invalid ioreq type 0x%x\n", req->type); } - - return; } diff --git a/hw/i386/xen/xen-pvh.c b/hw/i386/xen/xen-pvh.c new file mode 100644 index 00000000000..067f73e9773 --- /dev/null +++ b/hw/i386/xen/xen-pvh.c @@ -0,0 +1,125 @@ +/* + * 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 "system/system.h" +#include "hw/xen/arch_hvm.h" +#include +#include "hw/xen/xen-pvh-common.h" +#include "target/i386/cpu.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, const 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 7c7a60b166e..f30398fa4ae 100644 --- a/hw/i386/xen/xen_apic.c +++ b/hw/i386/xen/xen_apic.c @@ -36,7 +36,7 @@ static void xen_apic_mem_write(void *opaque, hwaddr addr, static const MemoryRegionOps xen_apic_io_ops = { .read = xen_apic_mem_read, .write = xen_apic_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void xen_apic_realize(DeviceState *dev, Error **errp) @@ -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) @@ -75,7 +76,7 @@ static void xen_send_msi(MSIMessage *msi) xen_hvm_inject_msi(msi->address, msi->data); } -static void xen_apic_class_init(ObjectClass *klass, void *data) +static void xen_apic_class_init(ObjectClass *klass, const void *data) { APICCommonClass *k = APIC_COMMON_CLASS(klass); diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index 17457ff3de1..c8b852be0cc 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -30,8 +30,8 @@ #include "migration/vmstate.h" #include "net/net.h" #include "trace.h" -#include "sysemu/xen.h" -#include "sysemu/block-backend.h" +#include "system/xen.h" +#include "system/block-backend.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "qom/object.h" @@ -140,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); } @@ -164,39 +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(PCIDevice *d, bool aux) +struct ide_unplug_state { + bool aux; + int nr_unplugged; +}; + +static int ide_dev_unplug(DeviceState *dev, void *_st) { - DeviceState *dev = DEVICE(d); - PCIIDEState *pci_ide; - int i; + struct ide_unplug_state *st = _st; IDEDevice *idedev; IDEBus *idebus; BlockBackend *blk; + int unit; - pci_ide = PCI_IDE(dev); + idedev = IDE_DEVICE(object_dynamic_cast(OBJECT(dev), "ide-hd")); + if (!idedev) { + return 0; + } - for (i = aux ? 1 : 0; i < 4; i++) { - idebus = &pci_ide->bus[i / 2]; - blk = idebus->ifs[i % 2].blk; + idebus = IDE_BUS(qdev_get_parent_bus(dev)); - if (blk && idebus->ifs[i % 2].drive_kind != IDE_CD) { - if (!(i % 2)) { - idedev = idebus->master; - } else { - idedev = idebus->slave; - } + unit = (idedev == idebus->slave); + assert(unit || idedev == idebus->master); - blk_drain(blk); - blk_flush(blk); + if (st->aux && !unit && !strcmp(BUS(idebus)->name, "ide.0")) { + return 0; + } - blk_detach_dev(blk, DEVICE(idedev)); - idebus->ifs[i % 2].blk = NULL; - idedev->conf.blk = NULL; - monitor_remove_blk(blk); - blk_unref(blk); - } + 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); } - pci_device_reset(d); } static void unplug_disks(PCIBus *b, PCIDevice *d, void *opaque) @@ -211,6 +237,7 @@ static void unplug_disks(PCIBus *b, PCIDevice *d, void *opaque) switch (pci_get_word(d->config + PCI_CLASS_DEVICE)) { case PCI_CLASS_STORAGE_IDE: + case PCI_CLASS_STORAGE_SATA: pci_xen_ide_unplug(d, aux); break; @@ -487,7 +514,7 @@ static void platform_mmio_write(void *opaque, hwaddr addr, static const MemoryRegionOps platform_mmio_handler = { .read = &platform_mmio_read, .write = &platform_mmio_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void platform_mmio_setup(PCIXenPlatformState *d) @@ -510,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() @@ -554,7 +581,7 @@ static void platform_reset(DeviceState *dev) platform_fixed_ioport_reset(s); } -static void xen_platform_class_init(ObjectClass *klass, void *data) +static void xen_platform_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -568,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; } @@ -577,7 +604,7 @@ static const TypeInfo xen_platform_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIXenPlatformState), .class_init = xen_platform_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/i386/xen/xen_pvdevice.c b/hw/i386/xen/xen_pvdevice.c index e62e06622b0..87a974ae5a0 100644 --- a/hw/i386/xen/xen_pvdevice.c +++ b/hw/i386/xen/xen_pvdevice.c @@ -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() } @@ -115,15 +115,14 @@ static void xen_pv_realize(PCIDevice *pci_dev, Error **errp) &d->mmio); } -static Property xen_pv_props[] = { +static const Property xen_pv_props[] = { DEFINE_PROP_UINT16("vendor-id", XenPVDevice, vendor_id, PCI_VENDOR_ID_XEN), DEFINE_PROP_UINT16("device-id", XenPVDevice, device_id, 0xffff), DEFINE_PROP_UINT8("revision", XenPVDevice, revision, 0x01), DEFINE_PROP_UINT32("size", XenPVDevice, size, 0x400000), - DEFINE_PROP_END_OF_LIST() }; -static void xen_pv_class_init(ObjectClass *klass, void *data) +static void xen_pv_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -140,7 +139,7 @@ static const TypeInfo xen_pv_type_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(XenPVDevice), .class_init = xen_pv_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/ide/Kconfig b/hw/ide/Kconfig index dd85fa3619f..b55507b836e 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 @@ -53,7 +54,14 @@ config AHCI_ICH9 depends on PCI select AHCI +config AHCI_SYSBUS + bool + select AHCI + 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 227e747ba72..bc7a116a89a 100644 --- a/hw/ide/ahci-allwinner.c +++ b/hw/ide/ahci-allwinner.c @@ -18,10 +18,9 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "sysemu/dma.h" -#include "hw/ide/internal.h" +#include "system/dma.h" #include "migration/vmstate.h" -#include "ahci_internal.h" +#include "hw/ide/ahci-sysbus.h" #include "trace.h" @@ -97,14 +96,14 @@ 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() } }; -static void allwinner_ahci_class_init(ObjectClass *klass, void *data) +static void allwinner_ahci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ide/ahci-internal.h b/hw/ide/ahci-internal.h new file mode 100644 index 00000000000..a318f36811c --- /dev/null +++ b/hw/ide/ahci-internal.h @@ -0,0 +1,385 @@ +/* + * 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 "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-sysbus.c b/hw/ide/ahci-sysbus.c new file mode 100644 index 00000000000..210818d0479 --- /dev/null +++ b/hw/ide/ahci-sysbus.c @@ -0,0 +1,90 @@ +/* + * QEMU AHCI Emulation (MMIO-mapped devices) + * + * 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 . + * + */ + +#include "qemu/osdep.h" +#include "system/address-spaces.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" + +#include "hw/ide/ahci-sysbus.h" +#include "ahci-internal.h" + +static const VMStateDescription vmstate_sysbus_ahci = { + .name = "sysbus-ahci", + .fields = (const VMStateField[]) { + VMSTATE_AHCI(ahci, SysbusAHCIState), + VMSTATE_END_OF_LIST() + }, +}; + +static void sysbus_ahci_reset(DeviceState *dev) +{ + SysbusAHCIState *s = SYSBUS_AHCI(dev); + + ahci_reset(&s->ahci); +} + +static void sysbus_ahci_init(Object *obj) +{ + SysbusAHCIState *s = SYSBUS_AHCI(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + ahci_init(&s->ahci, DEVICE(obj)); + + sysbus_init_mmio(sbd, &s->ahci.mem); + sysbus_init_irq(sbd, &s->ahci.irq); +} + +static void sysbus_ahci_realize(DeviceState *dev, Error **errp) +{ + SysbusAHCIState *s = SYSBUS_AHCI(dev); + + ahci_realize(&s->ahci, dev, &address_space_memory); +} + +static const Property sysbus_ahci_properties[] = { + DEFINE_PROP_UINT32("num-ports", SysbusAHCIState, ahci.ports, 1), +}; + +static void sysbus_ahci_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = sysbus_ahci_realize; + dc->vmsd = &vmstate_sysbus_ahci; + device_class_set_props(dc, sysbus_ahci_properties); + device_class_set_legacy_reset(dc, sysbus_ahci_reset); + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); +} + +static const TypeInfo sysbus_ahci_types[] = { + { + .name = TYPE_SYSBUS_AHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SysbusAHCIState), + .instance_init = sysbus_ahci_init, + .class_init = sysbus_ahci_class_init, + }, +}; + +DEFINE_TYPES(sysbus_ahci_types) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index d0a774bc171..1303c21cb70 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -23,20 +23,15 @@ #include "qemu/osdep.h" #include "hw/irq.h" -#include "hw/pci/msi.h" -#include "hw/pci/pci.h" -#include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/main-loop.h" -#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 "system/block-backend.h" +#include "system/dma.h" +#include "ahci-internal.h" +#include "ide-internal.h" #include "trace.h" @@ -177,34 +172,6 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset) return val; } -static void ahci_irq_raise(AHCIState *s) -{ - DeviceState *dev_state = s->container; - PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), - TYPE_PCI_DEVICE); - - trace_ahci_irq_raise(s); - - if (pci_dev && msi_enabled(pci_dev)) { - msi_notify(pci_dev, 0); - } else { - qemu_irq_raise(s->irq); - } -} - -static void ahci_irq_lower(AHCIState *s) -{ - DeviceState *dev_state = s->container; - PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), - TYPE_PCI_DEVICE); - - trace_ahci_irq_lower(s); - - if (!pci_dev || !msi_enabled(pci_dev)) { - qemu_irq_lower(s->irq); - } -} - static void ahci_check_irq(AHCIState *s) { int i; @@ -220,9 +187,11 @@ static void ahci_check_irq(AHCIState *s) trace_ahci_check_irq(s, old_irq, s->control_regs.irqstatus); if (s->control_regs.irqstatus && (s->control_regs.ghc & HOST_CTL_IRQ_EN)) { - ahci_irq_raise(s); + trace_ahci_irq_raise(s); + qemu_irq_raise(s->irq); } else { - ahci_irq_lower(s); + trace_ahci_irq_lower(s); + qemu_irq_lower(s->irq); } } @@ -623,9 +592,13 @@ static void ahci_init_d2h(AHCIDevice *ad) return; } + /* + * 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) | @@ -663,6 +636,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; @@ -897,11 +871,10 @@ static bool ahci_write_fis_d2h(AHCIDevice *ad, bool d2h_fis_i) 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); - } - - if (d2h_fis_i) { + } else if (d2h_fis_i) { ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_DHRS); } @@ -942,7 +915,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); @@ -970,6 +942,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]); @@ -1243,10 +1217,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; @@ -1581,7 +1575,6 @@ static const IDEDMAOps ahci_dma_ops = { void ahci_init(AHCIState *s, DeviceState *qdev) { - s->container = qdev; /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */ memory_region_init_io(&s->mem, OBJECT(qdev), &ahci_mem_ops, s, "ahci", AHCI_MEM_BAR_SIZE); @@ -1589,14 +1582,14 @@ 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++) { @@ -1622,9 +1615,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)); } @@ -1663,7 +1654,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), @@ -1678,7 +1669,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), @@ -1795,8 +1786,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), @@ -1804,87 +1795,13 @@ 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[]) { - VMSTATE_AHCI(ahci, SysbusAHCIState), - VMSTATE_END_OF_LIST() - }, -}; - -static void sysbus_ahci_reset(DeviceState *dev) +void ahci_ide_create_devs(AHCIState *ahci, DriveInfo **hd) { - SysbusAHCIState *s = SYSBUS_AHCI(dev); - - ahci_reset(&s->ahci); -} - -static void sysbus_ahci_init(Object *obj) -{ - SysbusAHCIState *s = SYSBUS_AHCI(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - ahci_init(&s->ahci, DEVICE(obj)); - - sysbus_init_mmio(sbd, &s->ahci.mem); - sysbus_init_irq(sbd, &s->ahci.irq); -} - -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); -} - -static Property sysbus_ahci_properties[] = { - DEFINE_PROP_UINT32("num-ports", SysbusAHCIState, num_ports, 1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void sysbus_ahci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = sysbus_ahci_realize; - dc->vmsd = &vmstate_sysbus_ahci; - device_class_set_props(dc, sysbus_ahci_properties); - dc->reset = sysbus_ahci_reset; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo sysbus_ahci_info = { - .name = TYPE_SYSBUS_AHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysbusAHCIState), - .instance_init = sysbus_ahci_init, - .class_init = sysbus_ahci_class_init, -}; - -static void sysbus_ahci_register_types(void) -{ - type_register_static(&sysbus_ahci_info); -} - -type_init(sysbus_ahci_register_types) - -int32_t ahci_get_num_ports(PCIDevice *dev) -{ - 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++) { @@ -1893,5 +1810,4 @@ void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd) } 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 c244bbd8be3..00000000000 --- a/hw/ide/ahci_internal.h +++ /dev/null @@ -1,394 +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_device.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; -}; - -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 dcc39df9a44..a42b7485218 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -24,10 +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 "system/block-backend.h" #include "scsi/constants.h" +#include "ide-internal.h" #include "trace.h" #define ATAPI_SECTOR_BITS (2 + BDRV_SECTOR_BITS) @@ -264,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 */ @@ -1309,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 00000000000..f87cd413b68 --- /dev/null +++ b/hw/ide/cf.c @@ -0,0 +1,57 @@ +/* + * 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 const 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), +}; + +static void ide_cf_class_init(ObjectClass *klass, const 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 c0bcfa44143..2a59516a9dd 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -29,10 +29,11 @@ #include "migration/vmstate.h" #include "qemu/module.h" #include "hw/isa/isa.h" -#include "sysemu/dma.h" -#include "sysemu/reset.h" +#include "system/dma.h" +#include "system/reset.h" #include "hw/ide/pci.h" +#include "ide-internal.h" #include "trace.h" /* CMD646 specific */ @@ -312,17 +313,16 @@ static void pci_cmd646_ide_exitfn(PCIDevice *dev) } } -static Property cmd646_ide_properties[] = { +static const Property cmd646_ide_properties[] = { DEFINE_PROP_UINT32("secondary", PCIIDEState, secondary, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void cmd646_ide_class_init(ObjectClass *klass, void *data) +static void cmd646_ide_class_init(ObjectClass *klass, const 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 b5e0dcd29b2..b14983ec54f 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -32,16 +32,16 @@ #include "qemu/timer.h" #include "qemu/hw-version.h" #include "qemu/memalign.h" -#include "sysemu/sysemu.h" -#include "sysemu/blockdev.h" -#include "sysemu/dma.h" +#include "system/system.h" +#include "system/blockdev.h" +#include "system/dma.h" #include "hw/block/block.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qemu/cutils.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" -#include "hw/ide/internal.h" +#include "system/replay.h" +#include "system/runstate.h" +#include "ide-internal.h" #include "trace.h" /* These values were based on a Seagate ST3500418AS but have been modified @@ -81,6 +81,18 @@ static const char *IDE_DMA_CMD_str(enum ide_dma_cmd enval) static void ide_dummy_transfer_stop(IDEState *s); +const MemoryRegionPortio ide_portio_list[] = { + { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write }, + { 0, 1, 2, .read = ide_data_readw, .write = ide_data_writew }, + { 0, 1, 4, .read = ide_data_readl, .write = ide_data_writel }, + PORTIO_END_OF_LIST(), +}; + +const MemoryRegionPortio ide_portio2_list[] = { + { 0, 1, 1, .read = ide_status_read, .write = ide_ctrl_write }, + PORTIO_END_OF_LIST(), +}; + static void padstr(char *str, const char *src, int len) { int i, v; @@ -956,8 +968,7 @@ static void ide_dma_cb(void *opaque, int ret) BDRV_SECTOR_SIZE, ide_dma_cb, s); break; case IDE_DMA_TRIM: - s->bus->dma->aiocb = dma_blk_io(blk_get_aio_context(s->blk), - &s->sg, offset, BDRV_SECTOR_SIZE, + s->bus->dma->aiocb = dma_blk_io(&s->sg, offset, BDRV_SECTOR_SIZE, ide_issue_trim, s, ide_dma_cb, s, DMA_DIRECTION_TO_DEVICE); break; @@ -1047,7 +1058,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 @@ -1611,11 +1622,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; } @@ -2515,19 +2539,19 @@ static void ide_dummy_transfer_stop(IDEState *s) void ide_bus_reset(IDEBus *bus) { - bus->unit = 0; - bus->cmd = 0; - ide_reset(&bus->ifs[0]); - ide_reset(&bus->ifs[1]); - ide_clear_hob(bus); - - /* pending async DMA */ + /* pending async DMA - needs the IDEState before it is reset */ if (bus->dma->aiocb) { trace_ide_bus_reset_aio(); blk_aio_cancel(bus->dma->aiocb); bus->dma->aiocb = NULL; } + bus->unit = 0; + bus->cmd = 0; + ide_reset(&bus->ifs[0]); + ide_reset(&bus->ifs[1]); + ide_clear_hob(bus); + /* reset dma provider too */ if (bus->dma->ops->reset) { bus->dma->ops->reset(bus->dma); @@ -2577,24 +2601,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; @@ -2602,26 +2623,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: @@ -2636,14 +2657,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; } @@ -2906,7 +2927,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() @@ -2918,7 +2939,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() @@ -2932,7 +2953,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), @@ -2950,7 +2971,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), @@ -2973,7 +2994,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, @@ -2986,7 +3007,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), @@ -2999,12 +3020,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 d61faab5323..4cade0d1219 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -61,15 +61,15 @@ */ #include "qemu/osdep.h" -#include "hw/irq.h" #include "hw/pci/msi.h" #include "hw/pci/pci.h" #include "migration/vmstate.h" #include "qemu/module.h" #include "hw/isa/isa.h" -#include "sysemu/dma.h" +#include "system/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 @@ -83,13 +83,26 @@ 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() }, }; +static void pci_ich9_ahci_update_irq(void *opaque, int irq_num, int level) +{ + PCIDevice *pci_dev = opaque; + + if (msi_enabled(pci_dev)) { + if (level) { + msi_notify(pci_dev, 0); + } + } else { + pci_set_irq(pci_dev, level); + } +} + static void pci_ich9_reset(DeviceState *dev) { AHCIPCIState *d = ICH9_AHCI(dev); @@ -99,20 +112,23 @@ 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); + qemu_init_irq(&d->irq, pci_ich9_ahci_update_irq, d, 0); ahci_init(&d->ahci, DEVICE(obj)); + d->ahci.irq = &d->irq; } 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); @@ -123,8 +139,6 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp) /* XXX Software should program this register */ dev->config[0x90] = 1 << 6; /* Address Map Register - AHCI mode */ - d->ahci.irq = pci_allocate_irq(dev); - pci_register_bar(dev, ICH9_IDP_BAR, PCI_BASE_ADDRESS_SPACE_IO, &d->ahci.idp); pci_register_bar(dev, ICH9_MEM_BAR, PCI_BASE_ADDRESS_SPACE_MEMORY, @@ -154,15 +168,14 @@ 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); ahci_uninit(&d->ahci); - qemu_free_irq(d->ahci.irq); } -static void ich_ahci_class_init(ObjectClass *klass, void *data) +static void ich_ahci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -174,7 +187,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); } @@ -184,7 +197,7 @@ static const TypeInfo ich_ahci_info = { .instance_size = sizeof(AHCIPCIState), .instance_init = pci_ich9_ahci_init, .class_init = ich_ahci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/ide/ide-bus.c b/hw/ide/ide-bus.c new file mode 100644 index 00000000000..b24e4d17b48 --- /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 "system/block-backend.h" +#include "system/blockdev.h" +#include "system/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, const 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 00000000000..5d478588c61 --- /dev/null +++ b/hw/ide/ide-dev.c @@ -0,0 +1,267 @@ +/* + * 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 "system/block-backend.h" +#include "system/blockdev.h" +#include "system/system.h" +#include "qapi/visitor.h" +#include "ide-internal.h" + +static const Property ide_props[] = { + DEFINE_PROP_UINT32("unit", IDEDevice, unit, -1), + DEFINE_PROP_BOOL("win2k-install-hack", IDEDevice, win2k_install_hack, false), +}; + +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; + } + + 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 const 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), +}; + +static void ide_hd_class_init(ObjectClass *klass, const 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 const Property ide_cd_properties[] = { + DEFINE_IDE_DEV_PROPERTIES(), +}; + +static void ide_cd_class_init(ObjectClass *klass, const 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, const 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 00000000000..0d64805da20 --- /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 e2ecc6230cd..a2f457f0bd0 100644 --- a/hw/ide/ioport.c +++ b/hw/ide/ioport.c @@ -25,21 +25,9 @@ #include "qemu/osdep.h" #include "hw/isa/isa.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 95053e026f1..5f418413c18 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -29,11 +29,11 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/ide/isa.h" -#include "hw/ide/internal.h" #include "qom/object.h" +#include "ide-internal.h" /***********************************************************/ /* ISA IDE definitions */ @@ -58,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() @@ -73,7 +73,7 @@ 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); ide_bus_init_output_irq(&s->bus, isa_get_irq(isadev, s->irqnum)); - vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_isa, s); + vmstate_register_any(VMSTATE_IF(dev), &vmstate_ide_isa, s); ide_bus_register_restart_cb(&s->bus); } @@ -101,20 +101,19 @@ ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int irqnum, return isadev; } -static Property isa_ide_properties[] = { +static const Property isa_ide_properties[] = { DEFINE_PROP_UINT32("iobase", ISAIDEState, iobase, 0x1f0), DEFINE_PROP_UINT32("iobase2", ISAIDEState, iobase2, 0x3f6), DEFINE_PROP_UINT32("irq", ISAIDEState, irqnum, 14), - DEFINE_PROP_END_OF_LIST(), }; -static void isa_ide_class_initfn(ObjectClass *klass, void *data) +static void isa_ide_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 dca1cc9efc1..c23bf32d2b1 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -30,10 +30,10 @@ #include "migration/vmstate.h" #include "qemu/module.h" #include "hw/misc/macio/macio.h" -#include "sysemu/block-backend.h" -#include "sysemu/dma.h" +#include "system/block-backend.h" +#include "system/dma.h" -#include "hw/ide/internal.h" +#include "ide-internal.h" /* debug MACIO */ // #define DEBUG_MACIO @@ -119,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 { @@ -190,8 +187,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) pmac_ide_transfer_cb, io); break; case IDE_DMA_TRIM: - s->bus->dma->aiocb = dma_blk_io(blk_get_aio_context(s->blk), &s->sg, - offset, 0x1, ide_issue_trim, s, + s->bus->dma->aiocb = dma_blk_io(&s->sg, offset, 0x1, ide_issue_trim, s, pmac_ide_transfer_cb, io, DMA_DIRECTION_TO_DEVICE); break; @@ -202,9 +198,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); @@ -361,7 +354,7 @@ static const VMStateDescription vmstate_pmac = { .name = "ide", .version_id = 5, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_IDE_BUS(bus, MACIOIDEState), VMSTATE_IDE_DRIVES(bus.ifs, MACIOIDEState), VMSTATE_BOOL(dma_active, MACIOIDEState), @@ -420,7 +413,8 @@ static void macio_ide_realizefn(DeviceState *dev, Error **errp) { MACIOIDEState *s = MACIO_IDE(dev); - ide_bus_init_output_irq(&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; @@ -456,26 +450,25 @@ 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, qdev_prop_allow_set_link_before_realize, 0); } -static Property macio_ide_properties[] = { +static const Property macio_ide_properties[] = { DEFINE_PROP_UINT32("channel", MACIOIDEState, channel, 0), DEFINE_PROP_UINT32("addr", MACIOIDEState, addr, -1), - DEFINE_PROP_END_OF_LIST(), }; -static void macio_ide_class_init(ObjectClass *oc, void *data) +static void macio_ide_class_init(ObjectClass *oc, const 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); @@ -508,7 +501,8 @@ void macio_ide_init_drives(MACIOIDEState *s, DriveInfo **hd_table) 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 e050eef9423..ddd70660400 100644 --- a/hw/ide/meson.build +++ b/hw/ide/meson.build @@ -1,14 +1,16 @@ 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_AHCI_SYSBUS', if_true: files('ahci-sysbus.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_QDEV', if_true: files('qdev.c')) system_ss.add(when: 'CONFIG_IDE_SII3112', if_true: files('sii3112.c')) system_ss.add(when: 'CONFIG_IDE_VIA', if_true: files('via.c')) -system_ss.add(when: 'CONFIG_MICRODRIVE', if_true: files('microdrive.c')) diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c deleted file mode 100644 index 981cfbd97fd..00000000000 --- a/hw/ide/microdrive.c +++ /dev/null @@ -1,644 +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/irq.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 = ide_bus_active_if(&s->bus); - if (ifs->blk) { - return ifs->status; - } else { - return 0; - } - case 0xf: /* Device Address */ - ifs = ide_bus_active_if(&s->bus); - return 0xc2 | ((~ifs->select << 2) & 0x3c); - default: - return ide_ioport_read(&s->bus, at); - } - - 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_bus_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_bus_init_output_irq(&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 3aeacab3bb2..699874db785 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -27,11 +27,11 @@ #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/ide/mmio.h" -#include "hw/ide/internal.h" #include "hw/qdev-properties.h" +#include "ide-internal.h" /***********************************************************/ /* MMIO based ide port @@ -110,7 +110,7 @@ static const VMStateDescription vmstate_ide_mmio = { .name = "mmio-ide", .version_id = 3, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_IDE_BUS(bus, MMIOIDEState), VMSTATE_IDE_DRIVES(bus.ifs, MMIOIDEState), VMSTATE_END_OF_LIST() @@ -141,17 +141,16 @@ static void mmio_ide_initfn(Object *obj) sysbus_init_irq(d, &s->irq); } -static Property mmio_ide_properties[] = { +static const Property mmio_ide_properties[] = { DEFINE_PROP_UINT32("shift", MMIOIDEState, shift, 0), - DEFINE_PROP_END_OF_LIST() }; -static void mmio_ide_class_init(ObjectClass *oc, void *data) +static void mmio_ide_class_init(ObjectClass *oc, const 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; } diff --git a/hw/ide/pci.c b/hw/ide/pci.c index a25b3525374..1e50bb9e483 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -27,10 +27,11 @@ #include "hw/irq.h" #include "hw/pci/pci.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/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 @@ -104,6 +105,90 @@ 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); @@ -152,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; @@ -181,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 */ @@ -417,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), @@ -431,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() } @@ -442,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), @@ -451,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 @@ -478,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), @@ -543,7 +625,7 @@ static const TypeInfo pci_ide_type_info = { .instance_size = sizeof(PCIIDEState), .instance_init = pci_ide_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/ide/piix.c b/hw/ide/piix.c index 4e5e12935f5..a0f2709c697 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -32,6 +32,7 @@ #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) @@ -177,12 +178,12 @@ static void pci_piix_ide_exitfn(PCIDevice *dev) } /* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */ -static void piix3_ide_class_init(ObjectClass *klass, void *data) +static void piix3_ide_class_init(ObjectClass *klass, const 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; @@ -200,12 +201,12 @@ static const TypeInfo piix3_ide_info = { }; /* NOTE: for the PIIX4, the IRQs and IOports are hardcoded */ -static void piix4_ide_class_init(ObjectClass *klass, void *data) +static void piix4_ide_class_init(ObjectClass *klass, const 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; diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c deleted file mode 100644 index 1b3b4da01df..00000000000 --- a/hw/ide/qdev.c +++ /dev/null @@ -1,403 +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_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; -} - -/* --------------------------------- */ - -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; - } - - 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); -} - -static void ide_cf_realize(IDEDevice *dev, Error **errp) -{ - ide_dev_initfn(dev, IDE_CFATA, 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 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_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_cf_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 63dc4a0494f..9b28c691fd7 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) @@ -289,7 +290,7 @@ static void sii3112_pci_realize(PCIDevice *dev, Error **errp) } } -static void sii3112_pci_class_init(ObjectClass *klass, void *data) +static void sii3112_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pd = PCI_DEVICE_CLASS(klass); @@ -299,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/via.c b/hw/ide/via.c index fff23803a6e..dedc2674c00 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -28,10 +28,12 @@ #include "hw/pci/pci.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "qemu/range.h" +#include "system/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, @@ -128,16 +130,14 @@ static void via_ide_reset(DeviceState *dev) 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); @@ -159,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); @@ -166,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; @@ -211,16 +245,18 @@ static void via_ide_exitfn(PCIDevice *dev) } } -static void via_ide_class_init(ObjectClass *klass, void *data) +static void via_ide_class_init(ObjectClass *klass, const 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 55865bb3869..a116cb82dff 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 a9088c910cc..507557deecc 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, @@ -375,7 +375,7 @@ static void adb_kbd_initfn(Object *obj) d->devaddr = ADB_DEVID_KEYBOARD; } -static void adb_kbd_class_init(ObjectClass *oc, void *data) +static void adb_kbd_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc); @@ -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 e6b341f0280..373ef3f953c 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) +#define ADB_MOUSE_BUTTON_LEFT 0x01 +#define ADB_MOUSE_BUTTON_RIGHT 0x02 + +static void adb_mouse_handle_event(DeviceState *dev, QemuConsole *src, + InputEvent *evt) { - MouseState *s = opaque; + 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; - s->dx += dx1; - s->dy += dy1; - s->dz += dz1; - s->buttons_state = buttons_state; + 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) @@ -246,7 +287,7 @@ static void adb_mouse_initfn(Object *obj) d->devaddr = ADB_DEVID_MOUSE; } -static void adb_mouse_class_init(ObjectClass *oc, void *data) +static void adb_mouse_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc); @@ -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 214ae6f42b3..bcb11edca35 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -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) @@ -259,13 +259,14 @@ static void adb_bus_unrealize(BusState *qbus) vmstate_unregister(NULL, &vmstate_adb_bus, adb_bus); } -static void adb_bus_class_init(ObjectClass *klass, void *data) +static void adb_bus_class_init(ObjectClass *klass, const 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() @@ -298,7 +299,7 @@ static void adb_device_realizefn(DeviceState *dev, Error **errp) bus->devices[bus->nb_devices++] = d; } -static void adb_device_class_init(ObjectClass *oc, void *data) +static void adb_device_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/input/ads7846.c b/hw/input/ads7846.c deleted file mode 100644 index dc0998ac794..00000000000 --- 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 e7ecebdf8f8..76bedc18443 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 ea7c07a2ba4..de625723c78 100644 --- a/hw/input/lasips2.c +++ b/hw/input/lasips2.c @@ -29,7 +29,7 @@ #include "hw/input/lasips2.h" #include "exec/hwaddr.h" #include "trace.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "migration/vmstate.h" #include "hw/irq.h" #include "qapi/error.h" @@ -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), @@ -306,7 +306,7 @@ static void lasips2_init(Object *obj) "lasips2-port-input-irq", 2); } -static void lasips2_class_init(ObjectClass *klass, void *data) +static void lasips2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -347,10 +347,15 @@ static void lasips2_port_init(Object *obj) "ps2-input-irq", 1); } -static void lasips2_port_class_init(ObjectClass *klass, void *data) +static void lasips2_port_class_init(ObjectClass *klass, const 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; } @@ -392,11 +397,16 @@ static void lasips2_kbd_port_init(Object *obj) lp->lasips2 = container_of(s, LASIPS2State, kbd_port); } -static void lasips2_kbd_port_class_init(ObjectClass *klass, void *data) +static void lasips2_kbd_port_class_init(ObjectClass *klass, const 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); } @@ -437,7 +447,7 @@ static void lasips2_mouse_port_init(Object *obj) lp->lasips2 = container_of(s, LASIPS2State, mouse_port); } -static void lasips2_mouse_port_class_init(ObjectClass *klass, void *data) +static void lasips2_mouse_port_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); LASIPS2PortDeviceClass *lpdc = LASIPS2_PORT_CLASS(klass); diff --git a/hw/input/lm832x.c b/hw/input/lm832x.c deleted file mode 100644 index 19a646d9bb4..00000000000 --- 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 c0d44821800..90a214962c7 100644 --- a/hw/input/meson.build +++ b/hw/input/meson.build @@ -1,18 +1,12 @@ system_ss.add(files('hid.c')) system_ss.add(when: 'CONFIG_ADB', if_true: files('adb.c', 'adb-mouse.c', 'adb-kbd.c')) -system_ss.add(when: 'CONFIG_ADS7846', if_true: files('ads7846.c')) -system_ss.add(when: 'CONFIG_LM832X', if_true: files('lm832x.c')) system_ss.add(when: 'CONFIG_PCKBD', if_true: files('pckbd.c')) system_ss.add(when: 'CONFIG_PL050', if_true: files('pl050.c')) system_ss.add(when: 'CONFIG_PS2', if_true: files('ps2.c')) -system_ss.add(when: 'CONFIG_STELLARIS_INPUT', if_true: files('stellaris_input.c')) -system_ss.add(when: 'CONFIG_TSC2005', if_true: files('tsc2005.c')) +system_ss.add(when: 'CONFIG_STELLARIS_GAMEPAD', if_true: files('stellaris_gamepad.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')) -system_ss.add(when: 'CONFIG_VHOST_USER_INPUT', if_true: files('vhost-user-input.c')) -system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_keypad.c')) -system_ss.add(when: 'CONFIG_TSC210X', if_true: files('tsc210x.c')) system_ss.add(when: 'CONFIG_LASIPS2', if_true: files('lasips2.c')) diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index b92b63bedca..71f5f976e9c 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -34,8 +34,8 @@ #include "hw/irq.h" #include "hw/input/i8042.h" #include "hw/qdev-properties.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/reset.h" +#include "system/runstate.h" #include "trace.h" @@ -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 @@ -735,28 +735,27 @@ static void i8042_mmio_init(Object *obj) "ps2-mouse-input-irq", 1); } -static Property i8042_mmio_properties[] = { +static const Property i8042_mmio_properties[] = { DEFINE_PROP_UINT64("mask", MMIOKBDState, kbd.mask, UINT64_MAX), DEFINE_PROP_UINT32("size", MMIOKBDState, size, -1), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_kbd_mmio = { .name = "pckbd-mmio", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(kbd, MMIOKBDState, 0, vmstate_kbd, KBDState), VMSTATE_END_OF_LIST() } }; -static void i8042_mmio_class_init(ObjectClass *klass, void *data) +static void i8042_mmio_class_init(ObjectClass *klass, const 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 +776,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() } @@ -938,21 +932,20 @@ static void i8042_build_aml(AcpiDevAmlIf *adev, Aml *scope) aml_append(scope, mou); } -static Property i8042_properties[] = { +static const Property i8042_properties[] = { DEFINE_PROP_BOOL("extended-state", ISAKBDState, kbd.extended_state, true), DEFINE_PROP_BOOL("kbd-throttle", ISAKBDState, kbd_throttle, false), DEFINE_PROP_UINT8("kbd-irq", ISAKBDState, kbd_irq, 1), DEFINE_PROP_UINT8("mouse-irq", ISAKBDState, mouse_irq, 12), - DEFINE_PROP_END_OF_LIST(), }; -static void i8042_class_initfn(ObjectClass *klass, void *data) +static void i8042_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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; @@ -965,7 +958,7 @@ static const TypeInfo i8042_info = { .instance_size = sizeof(ISAKBDState), .instance_init = i8042_initfn, .class_init = i8042_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, diff --git a/hw/input/pl050.c b/hw/input/pl050.c index ec5e19285e3..c5f4a3fa843 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), @@ -203,7 +203,7 @@ static void pl050_mouse_init(Object *obj) object_initialize_child(obj, "mouse", &s->mouse, TYPE_PS2_MOUSE_DEVICE); } -static void pl050_kbd_class_init(ObjectClass *oc, void *data) +static void pl050_kbd_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PL050DeviceClass *pdc = PL050_CLASS(oc); @@ -220,7 +220,7 @@ static const TypeInfo pl050_kbd_info = { .class_init = pl050_kbd_class_init, }; -static void pl050_mouse_class_init(ObjectClass *oc, void *data) +static void pl050_mouse_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PL050DeviceClass *pdc = PL050_CLASS(oc); @@ -249,7 +249,7 @@ static void pl050_init(Object *obj) qdev_init_gpio_in_named(DEVICE(obj), pl050_set_irq, "ps2-input-irq", 1); } -static void pl050_class_init(ObjectClass *oc, void *data) +static void pl050_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 45af76a8378..7f7b1fce2e5 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -30,8 +30,8 @@ #include "migration/vmstate.h" #include "ui/console.h" #include "ui/input.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/reset.h" +#include "system/runstate.h" #include "qapi/error.h" #include "trace.h" @@ -1007,7 +1007,7 @@ void ps2_write_mouse(PS2MouseState *s, int val) } } -static void ps2_reset_hold(Object *obj) +static void ps2_reset_hold(Object *obj, ResetType type) { PS2State *s = PS2_DEVICE(obj); @@ -1015,7 +1015,7 @@ static void ps2_reset_hold(Object *obj) ps2_reset_queue(s); } -static void ps2_reset_exit(Object *obj) +static void ps2_reset_exit(Object *obj, ResetType type) { PS2State *s = PS2_DEVICE(obj); @@ -1048,7 +1048,7 @@ static void ps2_common_post_load(PS2State *s) q->cwptr = ccount ? (q->rptr + ccount) & (PS2_BUFFER_SIZE - 1) : -1; } -static void ps2_kbd_reset_hold(Object *obj) +static void ps2_kbd_reset_hold(Object *obj, ResetType type) { PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(obj); PS2KbdState *s = PS2_KBD_DEVICE(obj); @@ -1056,7 +1056,7 @@ static void ps2_kbd_reset_hold(Object *obj) trace_ps2_kbd_reset(s); if (ps2dc->parent_phases.hold) { - ps2dc->parent_phases.hold(obj); + ps2dc->parent_phases.hold(obj, type); } s->scan_enabled = 1; @@ -1065,7 +1065,7 @@ static void ps2_kbd_reset_hold(Object *obj) s->modifiers = 0; } -static void ps2_mouse_reset_hold(Object *obj) +static void ps2_mouse_reset_hold(Object *obj, ResetType type) { PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(obj); PS2MouseState *s = PS2_MOUSE_DEVICE(obj); @@ -1073,7 +1073,7 @@ static void ps2_mouse_reset_hold(Object *obj) trace_ps2_mouse_reset(s); if (ps2dc->parent_phases.hold) { - ps2dc->parent_phases.hold(obj); + ps2dc->parent_phases.hold(obj, type); } s->mouse_status = 0; @@ -1093,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), @@ -1124,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() } @@ -1141,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() } @@ -1158,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() } @@ -1183,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), @@ -1191,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, @@ -1214,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), @@ -1231,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, @@ -1242,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, @@ -1254,7 +1254,7 @@ static void ps2_mouse_realize(DeviceState *dev, Error **errp) qemu_input_handler_register(dev, &ps2_mouse_handler); } -static void ps2_kbd_class_init(ObjectClass *klass, void *data) +static void ps2_kbd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -1273,7 +1273,7 @@ static const TypeInfo ps2_kbd_info = { .class_init = ps2_kbd_class_init }; -static void ps2_mouse_class_init(ObjectClass *klass, void *data) +static void ps2_mouse_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -1299,7 +1299,7 @@ static void ps2_init(Object *obj) qdev_init_gpio_out(DEVICE(obj), &s->irq, 1); } -static void ps2_class_init(ObjectClass *klass, void *data) +static void ps2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/input/pxa2xx_keypad.c b/hw/input/pxa2xx_keypad.c deleted file mode 100644 index 3dd03e8c9f4..00000000000 --- 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 00000000000..fec1161c9c1 --- /dev/null +++ b/hw/input/stellaris_gamepad.c @@ -0,0 +1,106 @@ +/* + * 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 const Property stellaris_gamepad_properties[] = { + DEFINE_PROP_ARRAY("keycodes", StellarisGamepad, num_buttons, + keycodes, qdev_prop_uint32, uint32_t), +}; + +static void stellaris_gamepad_class_init(ObjectClass *klass, const 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 e6ee5e11f1b..00000000000 --- 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 29001a827d9..1484625565b 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 555b6771738..00000000000 --- 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, const 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 7eae5989f76..00000000000 --- a/hw/input/tsc210x.c +++ /dev/null @@ -1,1256 +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 "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, const 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 4ee3542106e..00000000000 --- 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_FRONTEND); - 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 7053ad72d4d..d986c3c16e3 100644 --- a/hw/input/virtio-input-hid.c +++ b/hw/input/virtio-input-hid.c @@ -237,13 +237,12 @@ static void virtio_input_hid_handle_status(VirtIOInput *vinput, } } -static Property virtio_input_hid_properties[] = { +static const Property virtio_input_hid_properties[] = { DEFINE_PROP_STRING("display", VirtIOInputHID, display), DEFINE_PROP_UINT32("head", VirtIOInputHID, head, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_input_hid_class_init(ObjectClass *klass, void *data) +static void virtio_input_hid_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass); @@ -265,7 +264,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, @@ -322,7 +321,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, @@ -380,12 +379,11 @@ static struct virtio_input_config virtio_mouse_config_v2[] = { { /* end of list */ }, }; -static Property virtio_mouse_properties[] = { +static const Property virtio_mouse_properties[] = { DEFINE_PROP_BOOL("wheel-axis", VirtIOInputHID, wheel_axis, true), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_mouse_class_init(ObjectClass *klass, void *data) +static void virtio_mouse_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -416,7 +414,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, @@ -505,12 +503,11 @@ static struct virtio_input_config virtio_tablet_config_v2[] = { { /* end of list */ }, }; -static Property virtio_tablet_properties[] = { +static const Property virtio_tablet_properties[] = { DEFINE_PROP_BOOL("wheel-axis", VirtIOInputHID, wheel_axis, true), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_tablet_class_init(ObjectClass *klass, void *data) +static void virtio_tablet_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -541,7 +538,7 @@ static const TypeInfo virtio_tablet_info = { /* ----------------------------------------------------------------- */ -static QemuInputHandler virtio_multitouch_handler = { +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, diff --git a/hw/input/virtio-input-host.c b/hw/input/virtio-input-host.c index fea7139382a..bbfee9d3b9f 100644 --- a/hw/input/virtio-input-host.c +++ b/hw/input/virtio-input-host.c @@ -178,7 +178,6 @@ static void virtio_input_host_realize(DeviceState *dev, Error **errp) err_close: close(vih->fd); vih->fd = -1; - return; } static void virtio_input_host_unrealize(DeviceState *dev) @@ -221,12 +220,11 @@ static const VMStateDescription vmstate_virtio_input_host = { .unmigratable = 1, }; -static Property virtio_input_host_properties[] = { +static const Property virtio_input_host_properties[] = { DEFINE_PROP_STRING("evdev", VirtIOInputHost, evdev), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_input_host_class_init(ObjectClass *klass, void *data) +static void virtio_input_host_class_init(ObjectClass *klass, const void *data) { VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/input/virtio-input.c b/hw/input/virtio-input.c index 5b5398b3cac..a3f554f2110 100644 --- a/hw/input/virtio-input.c +++ b/hw/input/virtio-input.c @@ -189,7 +189,7 @@ static uint64_t virtio_input_get_features(VirtIODevice *vdev, uint64_t f, return f; } -static void virtio_input_set_status(VirtIODevice *vdev, uint8_t val) +static int virtio_input_set_status(VirtIODevice *vdev, uint8_t val) { VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev); VirtIOInput *vinput = VIRTIO_INPUT(vdev); @@ -202,6 +202,7 @@ static void virtio_input_set_status(VirtIODevice *vdev, uint8_t val) } } } + return 0; } static void virtio_input_reset(VirtIODevice *vdev) @@ -293,19 +294,18 @@ 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() }, .post_load = virtio_input_post_load, }; -static Property virtio_input_properties[] = { +static const Property virtio_input_properties[] = { DEFINE_PROP_STRING("serial", VirtIOInput, serial), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_input_class_init(ObjectClass *klass, void *data) +static void virtio_input_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index 97d550b06b7..7547528f2c2 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_GICV3_TCG +config ARM_GIC + bool + select ARM_GICV3 if TCG + select ARM_GIC_KVM if KVM + select MSI_NONBROKEN + +config ARM_GICV3 bool - default y - depends on ARM_GIC && TCG + depends on ARM_GIC config ARM_GIC_KVM bool - default y depends on ARM_GIC && KVM config XICS @@ -87,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 d0bf8d545ba..04097341557 100644 --- a/hw/intc/allwinner-a10-pic.c +++ b/hw/intc/allwinner-a10-pic.c @@ -135,14 +135,14 @@ static void aw_a10_pic_write(void *opaque, hwaddr offset, uint64_t value, static const MemoryRegionOps aw_a10_pic_ops = { .read = aw_a10_pic_read, .write = aw_a10_pic_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; 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), @@ -187,11 +187,11 @@ static void aw_a10_pic_reset(DeviceState *d) } } -static void aw_a10_pic_class_init(ObjectClass *klass, void *data) +static void aw_a10_pic_class_init(ObjectClass *klass, const 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 ac3d47d2318..bcb103560c7 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -26,20 +26,19 @@ #include "hw/intc/kvm_irqcount.h" #include "hw/pci/msi.h" #include "qemu/host-utils.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "trace.h" #include "hw/i386/apic-msidef.h" #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 */ @@ -49,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) @@ -199,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) {\ @@ -226,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; @@ -276,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); @@ -288,16 +300,82 @@ 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) { - s->apicbase = (val & 0xfffff000) | + /* 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 & MSR_IA32_APICBASE_BASE) | (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE)); - /* if disabled, cannot be enabled again */ if (!(val & MSR_IA32_APICBASE_ENABLE)) { s->apicbase &= ~MSR_IA32_APICBASE_ENABLE; 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) @@ -435,57 +513,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]; - int i; + if (is_x2apic_mode(&apic->parent_obj)) { + return apic->initial_apic_id == dest; + } else { + return apic->id == (uint8_t)dest; + } +} - if (apic && apic->id == dest) - return dest; /* shortcut in case apic->id == local_apics[dest]->id */ +static void apic_find_dest(uint32_t *deliver_bitmask, uint32_t dest) +{ + APICCommonState *apic = NULL; + int i; - 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); + } + } + } } } } @@ -509,29 +653,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; } @@ -636,27 +787,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); @@ -679,10 +829,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; @@ -718,17 +877,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; @@ -737,38 +935,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: @@ -788,9 +973,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: @@ -802,13 +995,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: @@ -837,10 +1044,70 @@ static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val, s->count_shift = (v + 1) & 7; } break; + 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; - break; + 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) @@ -871,12 +1138,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."); @@ -893,7 +1154,16 @@ static void apic_realize(DeviceState *dev, Error **errp) 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; } @@ -903,10 +1173,10 @@ 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) +static void apic_class_init(ObjectClass *klass, const void *data) { APICCommonClass *k = APIC_COMMON_CLASS(klass); diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 68ad30e2f5d..37a7a7019d3 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -28,27 +28,26 @@ #include "hw/intc/kvm_irqcount.h" #include "trace.h" #include "hw/boards.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "migration/vmstate.h" 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) @@ -63,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; @@ -257,6 +269,7 @@ static const VMStateDescription vmstate_apic_common; static void apic_common_realize(DeviceState *dev, Error **errp) { + ERRP_GUARD(); APICCommonState *s = APIC_COMMON(dev); APICCommonClass *info; static DeviceState *vapic; @@ -267,6 +280,9 @@ 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 && @@ -283,6 +299,10 @@ static void apic_common_realize(DeviceState *dev, Error **errp) } vmstate_register_with_alias_id(NULL, instance_id, &vmstate_apic_common, s, -1, 0, NULL); + + /* APIC LDR in x2APIC mode */ + s->extended_log_dest = ((s->initial_apic_id >> 4) << 16) | + (1 << (s->initial_apic_id & 0xf)); } static void apic_common_unrealize(DeviceState *dev) @@ -345,7 +365,7 @@ static const VMStateDescription vmstate_apic_common_sipi = { .version_id = 1, .minimum_version_id = 1, .needed = apic_common_sipi_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(sipi_vector, APICCommonState), VMSTATE_INT32(wait_for_sipi, APICCommonState), VMSTATE_END_OF_LIST() @@ -359,7 +379,7 @@ static const VMStateDescription vmstate_apic_common = { .pre_load = apic_pre_load, .pre_save = apic_dispatch_pre_save, .post_load = apic_dispatch_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(apicbase, APICCommonState), VMSTATE_UINT8(id, APICCommonState), VMSTATE_UINT8(arb_id, APICCommonState), @@ -382,19 +402,18 @@ 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 } }; -static Property apic_properties_common[] = { +static const Property apic_properties_common[] = { DEFINE_PROP_UINT8("version", APICCommonState, version, 0x14), DEFINE_PROP_BIT("vapic", APICCommonState, vapic_control, VAPIC_ENABLE_BIT, true), DEFINE_PROP_BOOL("legacy-instance-id", APICCommonState, legacy_instance_id, false), - DEFINE_PROP_END_OF_LIST(), }; static void apic_common_get_id(Object *obj, Visitor *v, const char *name, @@ -413,6 +432,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); @@ -423,6 +443,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; } @@ -437,11 +466,11 @@ static void apic_common_initfn(Object *obj) apic_common_set_id, NULL, NULL); } -static void apic_common_class_init(ObjectClass *klass, void *data) +static void apic_common_class_init(ObjectClass *klass, const 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 074cf50af25..899f1333633 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -27,8 +27,8 @@ #include "qemu/log.h" #include "qemu/module.h" #include "trace.h" -#include "sysemu/kvm.h" -#include "sysemu/qtest.h" +#include "system/kvm.h" +#include "system/qtest.h" /* #define DEBUG_GIC */ @@ -59,7 +59,7 @@ static const uint8_t gic_id_gicv2[] = { static inline int gic_get_current_cpu(GICState *s) { if (!qtest_enabled() && s->num_cpu > 1) { - return current_cpu->cpu_index; + return current_cpu->cpu_index - s->first_cpu_index; } return 0; } @@ -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) { @@ -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; } @@ -2147,7 +2162,7 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) } -static void arm_gic_class_init(ObjectClass *klass, void *data) +static void arm_gic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ARMGICClass *agc = ARM_GIC_CLASS(klass); diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 7c28504ace8..ed5be056452 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -26,7 +26,7 @@ #include "hw/arm/linux-boot-if.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" static int gic_pre_save(void *opaque) { @@ -62,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), @@ -79,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), @@ -104,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, @@ -122,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 } @@ -263,7 +263,7 @@ static inline void arm_gic_common_reset_irq_state(GICState *s, int cidx, } } -static void arm_gic_common_reset_hold(Object *obj) +static void arm_gic_common_reset_hold(Object *obj, ResetType type) { GICState *s = ARM_GIC_COMMON(obj); int i, j; @@ -348,8 +348,9 @@ static void arm_gic_common_linux_init(ARMLinuxBootIf *obj, } } -static Property arm_gic_common_properties[] = { +static const Property arm_gic_common_properties[] = { DEFINE_PROP_UINT32("num-cpu", GICState, num_cpu, 1), + DEFINE_PROP_UINT32("first-cpu-index", GICState, first_cpu_index, 0), DEFINE_PROP_UINT32("num-irq", GICState, num_irq, 32), /* Revision can be 1 or 2 for GIC architecture specification * versions 1 or 2, or 0 to indicate the legacy 11MPCore GIC. @@ -360,10 +361,9 @@ static Property arm_gic_common_properties[] = { /* True if the GIC should implement the virtualization extensions */ DEFINE_PROP_BOOL("has-virtualization-extensions", GICState, virt_extn, 0), DEFINE_PROP_UINT32("num-priority-bits", GICState, n_prio_bits, 8), - DEFINE_PROP_END_OF_LIST(), }; -static void arm_gic_common_class_init(ObjectClass *klass, void *data) +static void arm_gic_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -383,7 +383,7 @@ static const TypeInfo arm_gic_common_type = { .class_size = sizeof(ARMGICCommonClass), .class_init = arm_gic_common_class_init, .abstract = true, - .interfaces = (InterfaceInfo []) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ARM_LINUX_BOOT_IF }, { }, }, diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index 1d588946bce..1e9232f47c8 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -23,7 +23,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "migration/blocker.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_arm.h" #include "gic_internal.h" #include "vgic_common.h" @@ -473,13 +473,13 @@ static void kvm_arm_gic_get(GICState *s) } } -static void kvm_arm_gic_reset_hold(Object *obj) +static void kvm_arm_gic_reset_hold(Object *obj, ResetType type) { GICState *s = ARM_GIC_COMMON(obj); KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); if (kgc->parent_phases.hold) { - kgc->parent_phases.hold(obj); + kgc->parent_phases.hold(obj, type); } if (kvm_arm_gic_can_save_restore(s)) { @@ -516,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; } } @@ -548,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; } @@ -592,7 +584,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) } } -static void kvm_arm_gic_class_init(ObjectClass *klass, void *data) +static void kvm_arm_gic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/intc/arm_gicv2m.c b/hw/intc/arm_gicv2m.c index d564b857eba..cef06882216 100644 --- a/hw/intc/arm_gicv2m.c +++ b/hw/intc/arm_gicv2m.c @@ -31,7 +31,7 @@ #include "hw/irq.h" #include "hw/pci/msi.h" #include "hw/qdev-properties.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" @@ -170,13 +170,12 @@ static void gicv2m_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static Property gicv2m_properties[] = { +static const Property gicv2m_properties[] = { DEFINE_PROP_UINT32("base-spi", ARMGICv2mState, base_spi, 0), DEFINE_PROP_UINT32("num-spi", ARMGICv2mState, num_spi, 64), - DEFINE_PROP_END_OF_LIST(), }; -static void gicv2m_class_init(ObjectClass *klass, void *data) +static void gicv2m_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c index 0b8f79a1227..6059ce926a6 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 @@ -403,7 +452,7 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) gicv3_init_cpuif(s); } -static void arm_gicv3_class_init(ObjectClass *klass, void *data) +static void arm_gicv3_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass); diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 2ebf880eada..1cee68193ca 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -31,7 +31,7 @@ #include "migration/vmstate.h" #include "gicv3_internal.h" #include "hw/arm/linux-boot-if.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" static void gicv3_gicd_no_migration_shift_bug_post_load(GICv3State *cs) @@ -105,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), @@ -139,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() } @@ -157,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), @@ -192,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 } }; @@ -232,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, @@ -246,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), @@ -264,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 } }; @@ -299,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); @@ -451,7 +495,7 @@ static void arm_gicv3_finalize(Object *obj) g_free(s->redist_region_count); } -static void arm_gicv3_common_reset_hold(Object *obj) +static void arm_gicv3_common_reset_hold(Object *obj, ResetType type) { GICv3State *s = ARM_GICV3_COMMON(obj); int i; @@ -492,8 +536,11 @@ static void arm_gicv3_common_reset_hold(Object *obj) 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. @@ -558,11 +605,12 @@ static void arm_gic_common_linux_init(ARMLinuxBootIf *obj, } } -static Property arm_gicv3_common_properties[] = { +static const Property arm_gicv3_common_properties[] = { DEFINE_PROP_UINT32("num-cpu", GICv3State, num_cpu, 1), 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 @@ -573,10 +621,9 @@ static Property arm_gicv3_common_properties[] = { redist_region_count, qdev_prop_uint32, uint32_t), DEFINE_PROP_LINK("sysmem", GICv3State, dma, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; -static void arm_gicv3_common_class_init(ObjectClass *klass, void *data) +static void arm_gicv3_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -597,7 +644,7 @@ static const TypeInfo arm_gicv3_common_type = { .class_init = arm_gicv3_common_class_init, .instance_finalize = arm_gicv3_finalize, .abstract = true, - .interfaces = (InterfaceInfo []) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ARM_LINUX_BOOT_IF }, { }, }, diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index d07b13eb270..4b4cf091570 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -21,8 +21,10 @@ #include "hw/irq.h" #include "cpu.h" #include "target/arm/cpregs.h" -#include "sysemu/tcg.h" -#include "sysemu/qtest.h" +#include "target/arm/cpu-features.h" +#include "target/arm/internals.h" +#include "system/tcg.h" +#include "system/qtest.h" /* * Special case return value from hppvi_index(); must be larger than @@ -146,7 +148,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) @@ -157,6 +159,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]; @@ -191,6 +197,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 */ @@ -199,6 +206,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) { @@ -217,10 +225,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; } } @@ -289,6 +299,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)) { @@ -301,10 +312,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; } @@ -326,6 +338,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; } @@ -465,6 +482,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, @@ -482,9 +500,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; } @@ -494,6 +520,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) @@ -550,10 +577,13 @@ 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; } static uint64_t icv_bpr_read(CPUARMState *env, const ARMCPRegInfo *ri) @@ -697,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; @@ -736,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) @@ -753,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); } @@ -763,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)) { @@ -772,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; @@ -795,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) { /* @@ -803,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) @@ -832,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]; @@ -898,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; } @@ -923,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; } @@ -931,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); @@ -969,6 +1087,8 @@ void gicv3_cpuif_update(GICv3CPUState *cs) if (isfiq) { fiqlevel = 1; + } else if (cs->hppi.nmi) { + nmilevel = 1; } else { irqlevel = 1; } @@ -978,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) @@ -1044,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); @@ -1067,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; } @@ -1104,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; } @@ -1159,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)) { @@ -1172,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 @@ -1205,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; @@ -1239,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]); @@ -1329,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 @@ -1351,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. @@ -1410,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); @@ -1422,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. @@ -1434,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); - - if (thisgrp == grp && lr_gprio == dropprio) { - if (!icv_eoi_split(env, cs)) { - /* Priority drop and deactivate not split: deactivate irq now */ + bool thisnmi = lr & ICH_LR_EL2_NMI; + + 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); } } @@ -1684,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); } @@ -1774,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); @@ -1794,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; } @@ -2081,7 +2291,7 @@ static CPAccessResult gicv3_irqfiq_access(CPUARMState *env, r = CP_ACCESS_TRAP_EL3; break; case 3: - if (!is_a64(env) && !arm_is_el3_or_mon(env)) { + if (!arm_is_el3_or_mon(env)) { r = CP_ACCESS_TRAP_EL3; } break; @@ -2090,9 +2300,6 @@ static CPAccessResult gicv3_irqfiq_access(CPUARMState *env, } } - if (r == CP_ACCESS_TRAP_EL3 && !arm_el_is_aa64(env, 3)) { - r = CP_ACCESS_TRAP; - } return r; } @@ -2146,7 +2353,7 @@ static CPAccessResult gicv3_fiq_access(CPUARMState *env, r = CP_ACCESS_TRAP_EL3; break; case 3: - if (!is_a64(env) && !arm_is_el3_or_mon(env)) { + if (!arm_is_el3_or_mon(env)) { r = CP_ACCESS_TRAP_EL3; } break; @@ -2155,9 +2362,6 @@ static CPAccessResult gicv3_fiq_access(CPUARMState *env, } } - if (r == CP_ACCESS_TRAP_EL3 && !arm_el_is_aa64(env, 3)) { - r = CP_ACCESS_TRAP; - } return r; } @@ -2185,7 +2389,7 @@ static CPAccessResult gicv3_irq_access(CPUARMState *env, r = CP_ACCESS_TRAP_EL3; break; case 3: - if (!is_a64(env) && !arm_is_el3_or_mon(env)) { + if (!arm_is_el3_or_mon(env)) { r = CP_ACCESS_TRAP_EL3; } break; @@ -2194,9 +2398,6 @@ static CPAccessResult gicv3_irq_access(CPUARMState *env, } } - if (r == CP_ACCESS_TRAP_EL3 && !arm_el_is_aa64(env, 3)) { - r = CP_ACCESS_TRAP; - } return r; } @@ -2473,6 +2674,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); @@ -2494,7 +2704,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); } @@ -2611,6 +2825,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); } @@ -2675,6 +2894,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, @@ -2682,6 +2902,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, @@ -2689,6 +2910,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, @@ -2720,6 +2942,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, @@ -2730,6 +2953,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, @@ -2737,6 +2961,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, @@ -2747,6 +2972,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, @@ -2754,6 +2980,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, @@ -2761,6 +2988,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, @@ -2768,6 +2996,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, @@ -2819,6 +3048,19 @@ void gicv3_init_cpuif(GICv3State *s) */ 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 @@ -2889,6 +3131,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, diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c index 35e850685c0..d8207acb22c 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,6 +412,7 @@ 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 == (((max SPI IntID + 1) / 32) - 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; @@ -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; diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 5f552b4d37f..577b4454057 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -465,7 +465,7 @@ static ItsCmdResult lookup_vte(GICv3ITSState *s, const char *who, static ItsCmdResult process_its_cmd_phys(GICv3ITSState *s, const ITEntry *ite, int irqlevel) { - CTEntry cte; + CTEntry cte = {}; ItsCmdResult cmdres; cmdres = lookup_cte(s, __func__, ite->icid, &cte); @@ -479,7 +479,7 @@ static ItsCmdResult process_its_cmd_phys(GICv3ITSState *s, const ITEntry *ite, static ItsCmdResult process_its_cmd_virt(GICv3ITSState *s, const ITEntry *ite, int irqlevel) { - VTEntry vte; + VTEntry vte = {}; ItsCmdResult cmdres; cmdres = lookup_vte(s, __func__, ite->vpeid, &vte); @@ -514,8 +514,8 @@ static ItsCmdResult process_its_cmd_virt(GICv3ITSState *s, const ITEntry *ite, static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, uint32_t eventid, ItsCmdType cmd) { - DTEntry dte; - ITEntry ite; + DTEntry dte = {}; + ITEntry ite = {}; ItsCmdResult cmdres; int irqlevel; @@ -545,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; } @@ -583,8 +583,8 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, uint32_t pIntid = 0; uint64_t num_eventids; uint16_t icid = 0; - DTEntry dte; - ITEntry ite; + DTEntry dte = {}; + ITEntry ite = {}; devid = (cmdpkt[0] & DEVID_MASK) >> DEVID_SHIFT; eventid = cmdpkt[1] & EVENTID_MASK; @@ -651,8 +651,8 @@ static ItsCmdResult process_vmapti(GICv3ITSState *s, const uint64_t *cmdpkt, { uint32_t devid, eventid, vintid, doorbell, vpeid; uint32_t num_eventids; - DTEntry dte; - ITEntry ite; + DTEntry dte = {}; + ITEntry ite = {}; if (!its_feature_virtual(s)) { return CMD_CONTINUE; @@ -761,7 +761,7 @@ static bool update_cte(GICv3ITSState *s, uint16_t icid, const CTEntry *cte) static ItsCmdResult process_mapc(GICv3ITSState *s, const uint64_t *cmdpkt) { uint16_t icid; - CTEntry cte; + CTEntry cte = {}; icid = cmdpkt[2] & ICID_MASK; cte.valid = cmdpkt[2] & CMD_FIELD_VALID_MASK; @@ -822,7 +822,7 @@ static bool update_dte(GICv3ITSState *s, uint32_t devid, const DTEntry *dte) static ItsCmdResult process_mapd(GICv3ITSState *s, const uint64_t *cmdpkt) { uint32_t devid; - DTEntry dte; + DTEntry dte = {}; devid = (cmdpkt[0] & DEVID_MASK) >> DEVID_SHIFT; dte.size = cmdpkt[1] & SIZE_MASK; @@ -886,9 +886,9 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) { uint32_t devid, eventid; uint16_t new_icid; - DTEntry dte; - CTEntry old_cte, new_cte; - ITEntry old_ite; + DTEntry dte = {}; + CTEntry old_cte = {}, new_cte = {}; + ITEntry old_ite = {}; ItsCmdResult cmdres; devid = FIELD_EX64(cmdpkt[0], MOVI_0, DEVICEID); @@ -965,7 +965,7 @@ static bool update_vte(GICv3ITSState *s, uint32_t vpeid, const VTEntry *vte) static ItsCmdResult process_vmapp(GICv3ITSState *s, const uint64_t *cmdpkt) { - VTEntry vte; + VTEntry vte = {}; uint32_t vpeid; if (!its_feature_virtual(s)) { @@ -1030,7 +1030,7 @@ static void vmovp_callback(gpointer data, gpointer opaque) */ GICv3ITSState *s = data; VmovpCallbackData *cbdata = opaque; - VTEntry vte; + VTEntry vte = {}; ItsCmdResult cmdres; cmdres = lookup_vte(s, __func__, cbdata->vpeid, &vte); @@ -1085,9 +1085,9 @@ static ItsCmdResult process_vmovi(GICv3ITSState *s, const uint64_t *cmdpkt) { uint32_t devid, eventid, vpeid, doorbell; bool doorbell_valid; - DTEntry dte; - ITEntry ite; - VTEntry old_vte, new_vte; + DTEntry dte = {}; + ITEntry ite = {}; + VTEntry old_vte = {}, new_vte = {}; ItsCmdResult cmdres; if (!its_feature_virtual(s)) { @@ -1186,10 +1186,10 @@ static ItsCmdResult process_vinvall(GICv3ITSState *s, const uint64_t *cmdpkt) static ItsCmdResult process_inv(GICv3ITSState *s, const uint64_t *cmdpkt) { uint32_t devid, eventid; - ITEntry ite; - DTEntry dte; - CTEntry cte; - VTEntry vte; + ITEntry ite = {}; + DTEntry dte = {}; + CTEntry cte = {}; + VTEntry vte = {}; ItsCmdResult cmdres; devid = FIELD_EX64(cmdpkt[0], INV_0, DEVICEID); @@ -1950,13 +1950,13 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp) } } -static void gicv3_its_reset_hold(Object *obj) +static void gicv3_its_reset_hold(Object *obj, ResetType type) { GICv3ITSState *s = ARM_GICV3_ITS_COMMON(obj); GICv3ITSClass *c = ARM_GICV3_ITS_GET_CLASS(s); if (c->parent_phases.hold) { - c->parent_phases.hold(obj); + c->parent_phases.hold(obj, type); } /* Quiescent bit reset to 1 */ @@ -2002,13 +2002,12 @@ static void gicv3_its_post_load(GICv3ITSState *s) } } -static Property gicv3_its_props[] = { +static const Property gicv3_its_props[] = { DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "arm-gicv3", GICv3State *), - DEFINE_PROP_END_OF_LIST(), }; -static void gicv3_its_class_init(ObjectClass *klass, void *data) +static void gicv3_its_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c index abaf77057e1..e946e3fb87b 100644 --- a/hw/intc/arm_gicv3_its_common.c +++ b/hw/intc/arm_gicv3_its_common.c @@ -24,7 +24,7 @@ #include "hw/intc/arm_gicv3_its_common.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" static int gicv3_its_pre_save(void *opaque) { @@ -54,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), @@ -123,7 +123,7 @@ void gicv3_its_init_mmio(GICv3ITSState *s, const MemoryRegionOps *ops, msi_nonbroken = true; } -static void gicv3_its_common_reset_hold(Object *obj) +static void gicv3_its_common_reset_hold(Object *obj, ResetType type) { GICv3ITSState *s = ARM_GICV3_ITS_COMMON(obj); @@ -135,7 +135,7 @@ static void gicv3_its_common_reset_hold(Object *obj) memset(&s->baser, 0, sizeof(s->baser)); } -static void gicv3_its_common_class_init(ObjectClass *klass, void *data) +static void gicv3_its_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -163,8 +163,7 @@ type_init(gicv3_its_common_register_types) 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; + 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 7eda9fb86ea..9812d508597 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -21,10 +21,11 @@ #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" -#include "sysemu/kvm.h" +#include "system/runstate.h" +#include "system/kvm.h" #include "kvm_arm.h" #include "migration/blocker.h" #include "qom/object.h" @@ -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,14 +197,14 @@ static void kvm_arm_its_post_load(GICv3ITSState *s) GITS_CTLR, &s->ctlr, true, &error_abort); } -static void kvm_arm_its_reset_hold(Object *obj) +static void kvm_arm_its_reset_hold(Object *obj, ResetType type) { GICv3ITSState *s = ARM_GICV3_ITS_COMMON(obj); KVMARMITSClass *c = KVM_ARM_ITS_GET_CLASS(s); int i; if (c->parent_phases.hold) { - c->parent_phases.hold(obj); + c->parent_phases.hold(obj, type); } if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, @@ -234,13 +234,12 @@ static void kvm_arm_its_reset_hold(Object *obj) } } -static Property kvm_arm_its_props[] = { +static const Property kvm_arm_its_props[] = { DEFINE_PROP_LINK("parent-gicv3", GICv3ITSState, gicv3, "kvm-arm-gicv3", GICv3State *), - DEFINE_PROP_END_OF_LIST(), }; -static void kvm_arm_its_class_init(ObjectClass *klass, void *data) +static void kvm_arm_its_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 72ad916d3db..3be3bf6c28d 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -24,8 +24,8 @@ #include "hw/intc/arm_gicv3_common.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" +#include "system/kvm.h" +#include "system/runstate.h" #include "kvm_arm.h" #include "gicv3_internal.h" #include "vgic_common.h" @@ -703,7 +703,7 @@ 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_hold(Object *obj) +static void kvm_arm_gicv3_reset_hold(Object *obj, ResetType type) { GICv3State *s = ARM_GICV3_COMMON(obj); KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s); @@ -711,7 +711,7 @@ static void kvm_arm_gicv3_reset_hold(Object *obj) DPRINTF("Reset\n"); if (kgc->parent_phases.hold) { - kgc->parent_phases.hold(obj); + kgc->parent_phases.hold(obj, type); } if (s->migration_blocker) { @@ -805,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++) { @@ -878,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; } } @@ -889,7 +893,7 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) } } -static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data) +static void kvm_arm_gicv3_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index 8153525849a..90b238fac0b 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: { @@ -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; diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 03b6b8c986e..83ff74f899f 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -18,10 +18,11 @@ #include "hw/intc/armv7m_nvic.h" #include "hw/irq.h" #include "hw/qdev-properties.h" -#include "sysemu/tcg.h" -#include "sysemu/runstate.h" +#include "system/tcg.h" +#include "system/runstate.h" #include "target/arm/cpu.h" -#include "exec/exec-all.h" +#include "target/arm/cpu-features.h" +#include "exec/cputlb.h" #include "exec/memop.h" #include "qemu/log.h" #include "qemu/module.h" @@ -2497,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), @@ -2542,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), @@ -2556,22 +2557,26 @@ 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 } }; -static Property props_nvic[] = { +static const Property props_nvic[] = { /* Number of external IRQ lines (so excluding the 16 internal exceptions) */ DEFINE_PROP_UINT32("num-irq", NVICState, num_irq, 64), - DEFINE_PROP_END_OF_LIST() + /* + * 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), }; static void armv7m_nvic_reset(DeviceState *dev) @@ -2684,7 +2689,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 @@ -2709,13 +2730,13 @@ static void armv7m_nvic_instance_init(Object *obj) qdev_init_gpio_in_named(dev, nvic_nmi_trigger, "NMI", 1); } -static void armv7m_nvic_class_init(ObjectClass *klass, void *data) +static void armv7m_nvic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 00000000000..5cd786dee6c --- /dev/null +++ b/hw/intc/aspeed_intc.c @@ -0,0 +1,1108 @@ +/* + * 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 + * + * values below are offset by - 0x1000 from datasheet + * because its memory region is start at 0x1000 + * + */ +REG32(GICINT128_EN, 0x000) +REG32(GICINT128_STATUS, 0x004) +REG32(GICINT129_EN, 0x100) +REG32(GICINT129_STATUS, 0x104) +REG32(GICINT130_EN, 0x200) +REG32(GICINT130_STATUS, 0x204) +REG32(GICINT131_EN, 0x300) +REG32(GICINT131_STATUS, 0x304) +REG32(GICINT132_EN, 0x400) +REG32(GICINT132_STATUS, 0x404) +REG32(GICINT133_EN, 0x500) +REG32(GICINT133_STATUS, 0x504) +REG32(GICINT134_EN, 0x600) +REG32(GICINT134_STATUS, 0x604) +REG32(GICINT135_EN, 0x700) +REG32(GICINT135_STATUS, 0x704) +REG32(GICINT136_EN, 0x800) +REG32(GICINT136_STATUS, 0x804) +REG32(GICINT192_201_EN, 0xB00) +REG32(GICINT192_201_STATUS, 0xB04) + +/* + * INTCIO Registers + * + * values below are offset by - 0x100 from datasheet + * because its memory region is start at 0x100 + * + */ +REG32(GICINT192_EN, 0x00) +REG32(GICINT192_STATUS, 0x04) +REG32(GICINT193_EN, 0x10) +REG32(GICINT193_STATUS, 0x14) +REG32(GICINT194_EN, 0x20) +REG32(GICINT194_STATUS, 0x24) +REG32(GICINT195_EN, 0x30) +REG32(GICINT195_STATUS, 0x34) +REG32(GICINT196_EN, 0x40) +REG32(GICINT196_STATUS, 0x44) +REG32(GICINT197_EN, 0x50) +REG32(GICINT197_STATUS, 0x54) + +/* + * SSP INTC Registers + */ +REG32(SSPINT128_EN, 0x2000) +REG32(SSPINT128_STATUS, 0x2004) +REG32(SSPINT129_EN, 0x2100) +REG32(SSPINT129_STATUS, 0x2104) +REG32(SSPINT130_EN, 0x2200) +REG32(SSPINT130_STATUS, 0x2204) +REG32(SSPINT131_EN, 0x2300) +REG32(SSPINT131_STATUS, 0x2304) +REG32(SSPINT132_EN, 0x2400) +REG32(SSPINT132_STATUS, 0x2404) +REG32(SSPINT133_EN, 0x2500) +REG32(SSPINT133_STATUS, 0x2504) +REG32(SSPINT134_EN, 0x2600) +REG32(SSPINT134_STATUS, 0x2604) +REG32(SSPINT135_EN, 0x2700) +REG32(SSPINT135_STATUS, 0x2704) +REG32(SSPINT136_EN, 0x2800) +REG32(SSPINT136_STATUS, 0x2804) +REG32(SSPINT137_EN, 0x2900) +REG32(SSPINT137_STATUS, 0x2904) +REG32(SSPINT138_EN, 0x2A00) +REG32(SSPINT138_STATUS, 0x2A04) +REG32(SSPINT160_169_EN, 0x2B00) +REG32(SSPINT160_169_STATUS, 0x2B04) + +/* + * SSP INTCIO Registers + */ +REG32(SSPINT160_EN, 0x180) +REG32(SSPINT160_STATUS, 0x184) +REG32(SSPINT161_EN, 0x190) +REG32(SSPINT161_STATUS, 0x194) +REG32(SSPINT162_EN, 0x1A0) +REG32(SSPINT162_STATUS, 0x1A4) +REG32(SSPINT163_EN, 0x1B0) +REG32(SSPINT163_STATUS, 0x1B4) +REG32(SSPINT164_EN, 0x1C0) +REG32(SSPINT164_STATUS, 0x1C4) +REG32(SSPINT165_EN, 0x1D0) +REG32(SSPINT165_STATUS, 0x1D4) + +/* + * TSP INTC Registers + */ +REG32(TSPINT128_EN, 0x3000) +REG32(TSPINT128_STATUS, 0x3004) +REG32(TSPINT129_EN, 0x3100) +REG32(TSPINT129_STATUS, 0x3104) +REG32(TSPINT130_EN, 0x3200) +REG32(TSPINT130_STATUS, 0x3204) +REG32(TSPINT131_EN, 0x3300) +REG32(TSPINT131_STATUS, 0x3304) +REG32(TSPINT132_EN, 0x3400) +REG32(TSPINT132_STATUS, 0x3404) +REG32(TSPINT133_EN, 0x3500) +REG32(TSPINT133_STATUS, 0x3504) +REG32(TSPINT134_EN, 0x3600) +REG32(TSPINT134_STATUS, 0x3604) +REG32(TSPINT135_EN, 0x3700) +REG32(TSPINT135_STATUS, 0x3704) +REG32(TSPINT136_EN, 0x3800) +REG32(TSPINT136_STATUS, 0x3804) +REG32(TSPINT137_EN, 0x3900) +REG32(TSPINT137_STATUS, 0x3904) +REG32(TSPINT138_EN, 0x3A00) +REG32(TSPINT138_STATUS, 0x3A04) +REG32(TSPINT160_169_EN, 0x3B00) +REG32(TSPINT160_169_STATUS, 0x3B04) + +/* + * TSP INTCIO Registers + */ + +REG32(TSPINT160_EN, 0x200) +REG32(TSPINT160_STATUS, 0x204) +REG32(TSPINT161_EN, 0x210) +REG32(TSPINT161_STATUS, 0x214) +REG32(TSPINT162_EN, 0x220) +REG32(TSPINT162_STATUS, 0x224) +REG32(TSPINT163_EN, 0x230) +REG32(TSPINT163_STATUS, 0x234) +REG32(TSPINT164_EN, 0x240) +REG32(TSPINT164_STATUS, 0x244) +REG32(TSPINT165_EN, 0x250) +REG32(TSPINT165_STATUS, 0x254) + +static const AspeedINTCIRQ *aspeed_intc_get_irq(AspeedINTCClass *aic, + uint32_t reg) +{ + int i; + + for (i = 0; i < aic->irq_table_count; i++) { + if (aic->irq_table[i].enable_reg == reg || + aic->irq_table[i].status_reg == reg) { + return &aic->irq_table[i]; + } + } + + /* + * Invalid reg. + */ + g_assert_not_reached(); +} + +/* + * Update the state of an interrupt controller pin by setting + * the specified output pin to the given level. + * The input pin index should be between 0 and the number of input pins. + * The output pin index should be between 0 and the number of output pins. + */ +static void aspeed_intc_update(AspeedINTCState *s, int inpin_idx, + int outpin_idx, int level) +{ + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const char *name = object_get_typename(OBJECT(s)); + + assert((outpin_idx < aic->num_outpins) && (inpin_idx < aic->num_inpins)); + + trace_aspeed_intc_update_irq(name, inpin_idx, outpin_idx, level); + qemu_set_irq(s->output_pins[outpin_idx], level); +} + +static void aspeed_intc_set_irq_handler(AspeedINTCState *s, + const AspeedINTCIRQ *intc_irq, + uint32_t select) +{ + const char *name = object_get_typename(OBJECT(s)); + uint32_t status_reg; + int outpin_idx; + int inpin_idx; + + status_reg = intc_irq->status_reg; + outpin_idx = intc_irq->outpin_idx; + inpin_idx = intc_irq->inpin_idx; + + if ((s->mask[inpin_idx] & select) || (s->regs[status_reg] & select)) { + /* + * 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[inpin_idx] |= select; + trace_aspeed_intc_pending_irq(name, inpin_idx, s->pending[inpin_idx]); + } else { + /* + * notify firmware which source interrupt are coming + * by setting status register + */ + s->regs[status_reg] = select; + trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx, + s->regs[status_reg]); + aspeed_intc_update(s, inpin_idx, outpin_idx, 1); + } +} + +static void aspeed_intc_set_irq_handler_multi_outpins(AspeedINTCState *s, + const AspeedINTCIRQ *intc_irq, uint32_t select) +{ + const char *name = object_get_typename(OBJECT(s)); + uint32_t status_reg; + int num_outpins; + int outpin_idx; + int inpin_idx; + int i; + + num_outpins = intc_irq->num_outpins; + status_reg = intc_irq->status_reg; + outpin_idx = intc_irq->outpin_idx; + inpin_idx = intc_irq->inpin_idx; + + for (i = 0; i < num_outpins; i++) { + if (select & BIT(i)) { + if (s->mask[inpin_idx] & BIT(i) || + s->regs[status_reg] & BIT(i)) { + /* + * a. mask bit is not 0 means in ISR mode sources interrupt + * routine are executing. + * b. status bit is not 0 means previous source interrupt + * does not be executed, yet. + * + * save source interrupt to pending bit. + */ + s->pending[inpin_idx] |= BIT(i); + trace_aspeed_intc_pending_irq(name, inpin_idx, + s->pending[inpin_idx]); + } else { + /* + * notify firmware which source interrupt are coming + * by setting status bit + */ + s->regs[status_reg] |= BIT(i); + trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx + i, + s->regs[status_reg]); + aspeed_intc_update(s, inpin_idx, outpin_idx + i, 1); + } + } + } +} + +/* + * GICINT192_201 maps 1:10 to input IRQ 0 and output IRQs 0 to 9. + * GICINT128 to GICINT136 map 1:1 to input IRQs 1 to 9 and output + * IRQs 10 to 18. The value of input IRQ should be between 0 and + * the number of input pins. + */ +static void aspeed_intc_set_irq(void *opaque, int irq, int level) +{ + AspeedINTCState *s = (AspeedINTCState *)opaque; + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const char *name = object_get_typename(OBJECT(s)); + const AspeedINTCIRQ *intc_irq; + uint32_t select = 0; + uint32_t enable; + int num_outpins; + int inpin_idx; + int i; + + assert(irq < aic->num_inpins); + + intc_irq = &aic->irq_table[irq]; + num_outpins = intc_irq->num_outpins; + inpin_idx = intc_irq->inpin_idx; + trace_aspeed_intc_set_irq(name, inpin_idx, level); + enable = s->enable[inpin_idx]; + + if (!level) { + return; + } + + for (i = 0; i < aic->num_lines; i++) { + if (s->orgates[inpin_idx].levels[i]) { + if (enable & BIT(i)) { + select |= BIT(i); + } + } + } + + if (!select) { + return; + } + + trace_aspeed_intc_select(name, select); + if (num_outpins > 1) { + aspeed_intc_set_irq_handler_multi_outpins(s, intc_irq, select); + } else { + aspeed_intc_set_irq_handler(s, intc_irq, select); + } +} + +static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, + uint64_t data) +{ + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const char *name = object_get_typename(OBJECT(s)); + const AspeedINTCIRQ *intc_irq; + uint32_t reg = offset >> 2; + uint32_t old_enable; + uint32_t change; + int inpin_idx; + + intc_irq = aspeed_intc_get_irq(aic, reg); + inpin_idx = intc_irq->inpin_idx; + + assert(inpin_idx < aic->num_inpins); + + /* + * The enable registers are used to enable source interrupts. + * They also handle masking and unmasking of source interrupts + * during the execution of the source ISR. + */ + + /* disable all source interrupt */ + if (!data && !s->enable[inpin_idx]) { + s->regs[reg] = data; + return; + } + + old_enable = s->enable[inpin_idx]; + s->enable[inpin_idx] |= data; + + /* enable new source interrupt */ + if (old_enable != s->enable[inpin_idx]) { + trace_aspeed_intc_enable(name, s->enable[inpin_idx]); + s->regs[reg] = data; + return; + } + + /* mask and unmask source interrupt */ + change = s->regs[reg] ^ data; + if (change & data) { + s->mask[inpin_idx] &= ~change; + trace_aspeed_intc_unmask(name, change, s->mask[inpin_idx]); + } else { + s->mask[inpin_idx] |= change; + trace_aspeed_intc_mask(name, change, s->mask[inpin_idx]); + } + + s->regs[reg] = data; +} + +static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, + uint64_t data) +{ + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const char *name = object_get_typename(OBJECT(s)); + const AspeedINTCIRQ *intc_irq; + uint32_t reg = offset >> 2; + int outpin_idx; + int inpin_idx; + + if (!data) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__); + return; + } + + intc_irq = aspeed_intc_get_irq(aic, reg); + outpin_idx = intc_irq->outpin_idx; + inpin_idx = intc_irq->inpin_idx; + + assert(inpin_idx < aic->num_inpins); + + /* clear status */ + s->regs[reg] &= ~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[reg]) { + trace_aspeed_intc_all_isr_done(name, inpin_idx); + if (s->pending[inpin_idx]) { + /* + * handle pending source interrupt + * notify firmware which source interrupt are pending + * by setting status register + */ + s->regs[reg] = s->pending[inpin_idx]; + s->pending[inpin_idx] = 0; + trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx, + s->regs[reg]); + aspeed_intc_update(s, inpin_idx, outpin_idx, 1); + } else { + /* clear irq */ + trace_aspeed_intc_clear_irq(name, inpin_idx, outpin_idx, 0); + aspeed_intc_update(s, inpin_idx, outpin_idx, 0); + } + } +} + +static void aspeed_intc_status_handler_multi_outpins(AspeedINTCState *s, + hwaddr offset, uint64_t data) +{ + const char *name = object_get_typename(OBJECT(s)); + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const AspeedINTCIRQ *intc_irq; + uint32_t reg = offset >> 2; + int num_outpins; + int outpin_idx; + int inpin_idx; + int i; + + if (!data) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__); + return; + } + + intc_irq = aspeed_intc_get_irq(aic, reg); + num_outpins = intc_irq->num_outpins; + outpin_idx = intc_irq->outpin_idx; + inpin_idx = intc_irq->inpin_idx; + assert(inpin_idx < aic->num_inpins); + + /* clear status */ + s->regs[reg] &= ~data; + + /* + * The 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; + } + + for (i = 0; i < num_outpins; i++) { + /* All source ISR executions are done from a specific bit */ + if (data & BIT(i)) { + trace_aspeed_intc_all_isr_done_bit(name, inpin_idx, i); + if (s->pending[inpin_idx] & BIT(i)) { + /* + * Handle pending source interrupt. + * Notify firmware which source interrupt is pending + * by setting the status bit. + */ + s->regs[reg] |= BIT(i); + s->pending[inpin_idx] &= ~BIT(i); + trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx + i, + s->regs[reg]); + aspeed_intc_update(s, inpin_idx, outpin_idx + i, 1); + } else { + /* clear irq for the specific bit */ + trace_aspeed_intc_clear_irq(name, inpin_idx, outpin_idx + i, 0); + aspeed_intc_update(s, inpin_idx, outpin_idx + i, 0); + } + } + } +} + +static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + uint32_t value = 0; + + value = s->regs[reg]; + trace_aspeed_intc_read(name, offset, size, value); + + return value; +} + +static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + + trace_aspeed_intc_write(name, offset, size, data); + + switch (reg) { + 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: + case R_GICINT192_201_EN: + aspeed_intc_enable_handler(s, offset, 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: + aspeed_intc_status_handler(s, offset, data); + break; + case R_GICINT192_201_STATUS: + aspeed_intc_status_handler_multi_outpins(s, offset, data); + break; + default: + s->regs[reg] = data; + break; + } +} + +static void aspeed_ssp_intc_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + + trace_aspeed_intc_write(name, offset, size, data); + + switch (reg) { + case R_SSPINT128_EN: + case R_SSPINT129_EN: + case R_SSPINT130_EN: + case R_SSPINT131_EN: + case R_SSPINT132_EN: + case R_SSPINT133_EN: + case R_SSPINT134_EN: + case R_SSPINT135_EN: + case R_SSPINT136_EN: + case R_SSPINT160_169_EN: + aspeed_intc_enable_handler(s, offset, data); + break; + case R_SSPINT128_STATUS: + case R_SSPINT129_STATUS: + case R_SSPINT130_STATUS: + case R_SSPINT131_STATUS: + case R_SSPINT132_STATUS: + case R_SSPINT133_STATUS: + case R_SSPINT134_STATUS: + case R_SSPINT135_STATUS: + case R_SSPINT136_STATUS: + aspeed_intc_status_handler(s, offset, data); + break; + case R_SSPINT160_169_STATUS: + aspeed_intc_status_handler_multi_outpins(s, offset, data); + break; + default: + s->regs[reg] = data; + break; + } +} + +static void aspeed_tsp_intc_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + + trace_aspeed_intc_write(name, offset, size, data); + + switch (reg) { + case R_TSPINT128_EN: + case R_TSPINT129_EN: + case R_TSPINT130_EN: + case R_TSPINT131_EN: + case R_TSPINT132_EN: + case R_TSPINT133_EN: + case R_TSPINT134_EN: + case R_TSPINT135_EN: + case R_TSPINT136_EN: + case R_TSPINT160_169_EN: + aspeed_intc_enable_handler(s, offset, data); + break; + case R_TSPINT128_STATUS: + case R_TSPINT129_STATUS: + case R_TSPINT130_STATUS: + case R_TSPINT131_STATUS: + case R_TSPINT132_STATUS: + case R_TSPINT133_STATUS: + case R_TSPINT134_STATUS: + case R_TSPINT135_STATUS: + case R_TSPINT136_STATUS: + aspeed_intc_status_handler(s, offset, data); + break; + case R_TSPINT160_169_STATUS: + aspeed_intc_status_handler_multi_outpins(s, offset, data); + break; + default: + s->regs[reg] = data; + break; + } +} + +static uint64_t aspeed_intcio_read(void *opaque, hwaddr offset, + unsigned int size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + uint32_t value = 0; + + value = s->regs[reg]; + trace_aspeed_intc_read(name, offset, size, value); + + return value; +} + +static void aspeed_intcio_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + + trace_aspeed_intc_write(name, offset, size, data); + + switch (reg) { + case R_GICINT192_EN: + case R_GICINT193_EN: + case R_GICINT194_EN: + case R_GICINT195_EN: + case R_GICINT196_EN: + case R_GICINT197_EN: + aspeed_intc_enable_handler(s, offset, data); + break; + case R_GICINT192_STATUS: + case R_GICINT193_STATUS: + case R_GICINT194_STATUS: + case R_GICINT195_STATUS: + case R_GICINT196_STATUS: + case R_GICINT197_STATUS: + aspeed_intc_status_handler(s, offset, data); + break; + default: + s->regs[reg] = data; + break; + } +} + +static void aspeed_ssp_intcio_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + + trace_aspeed_intc_write(name, offset, size, data); + + switch (reg) { + case R_SSPINT160_EN: + case R_SSPINT161_EN: + case R_SSPINT162_EN: + case R_SSPINT163_EN: + case R_SSPINT164_EN: + case R_SSPINT165_EN: + aspeed_intc_enable_handler(s, offset, data); + break; + case R_SSPINT160_STATUS: + case R_SSPINT161_STATUS: + case R_SSPINT162_STATUS: + case R_SSPINT163_STATUS: + case R_SSPINT164_STATUS: + case R_SSPINT165_STATUS: + aspeed_intc_status_handler(s, offset, data); + break; + default: + s->regs[reg] = data; + break; + } +} + +static void aspeed_tsp_intcio_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + + trace_aspeed_intc_write(name, offset, size, data); + + switch (reg) { + case R_TSPINT160_EN: + case R_TSPINT161_EN: + case R_TSPINT162_EN: + case R_TSPINT163_EN: + case R_TSPINT164_EN: + case R_TSPINT165_EN: + aspeed_intc_enable_handler(s, offset, data); + break; + case R_TSPINT160_STATUS: + case R_TSPINT161_STATUS: + case R_TSPINT162_STATUS: + case R_TSPINT163_STATUS: + case R_TSPINT164_STATUS: + case R_TSPINT165_STATUS: + aspeed_intc_status_handler(s, offset, data); + break; + default: + s->regs[reg] = data; + break; + } +} + +static const MemoryRegionOps aspeed_intc_ops = { + .read = aspeed_intc_read, + .write = aspeed_intc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static const MemoryRegionOps aspeed_intcio_ops = { + .read = aspeed_intcio_read, + .write = aspeed_intcio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static const MemoryRegionOps aspeed_ssp_intc_ops = { + .read = aspeed_intc_read, + .write = aspeed_ssp_intc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static const MemoryRegionOps aspeed_ssp_intcio_ops = { + .read = aspeed_intcio_read, + .write = aspeed_ssp_intcio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static const MemoryRegionOps aspeed_tsp_intc_ops = { + .read = aspeed_intc_read, + .write = aspeed_tsp_intc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static const MemoryRegionOps aspeed_tsp_intcio_ops = { + .read = aspeed_intcio_read, + .write = aspeed_tsp_intcio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .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_inpins <= ASPEED_INTC_MAX_INPINS); + for (i = 0; i < aic->num_inpins; 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); + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + + memset(s->regs, 0, aic->nr_regs << 2); + 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(&s->iomem_container, OBJECT(s), + TYPE_ASPEED_INTC ".container", aic->mem_size); + + sysbus_init_mmio(sbd, &s->iomem_container); + + s->regs = g_new(uint32_t, aic->nr_regs); + memory_region_init_io(&s->iomem, OBJECT(s), aic->reg_ops, s, + TYPE_ASPEED_INTC ".regs", aic->nr_regs << 2); + + memory_region_add_subregion(&s->iomem_container, aic->reg_offset, + &s->iomem); + + qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_inpins); + + for (i = 0; i < aic->num_inpins; i++) { + if (!qdev_realize(DEVICE(&s->orgates[i]), NULL, errp)) { + return; + } + } + + for (i = 0; i < aic->num_outpins; i++) { + sysbus_init_irq(sbd, &s->output_pins[i]); + } +} + +static void aspeed_intc_unrealize(DeviceState *dev) +{ + AspeedINTCState *s = ASPEED_INTC(dev); + + g_free(s->regs); + s->regs = NULL; +} + +static void aspeed_intc_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); + + dc->desc = "ASPEED INTC Controller"; + dc->realize = aspeed_intc_realize; + dc->unrealize = aspeed_intc_unrealize; + device_class_set_legacy_reset(dc, aspeed_intc_reset); + dc->vmsd = NULL; + + aic->reg_ops = &aspeed_intc_ops; +} + +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 AspeedINTCIRQ aspeed_2700_intc_irqs[ASPEED_INTC_MAX_INPINS] = { + {0, 0, 10, R_GICINT192_201_EN, R_GICINT192_201_STATUS}, + {1, 10, 1, R_GICINT128_EN, R_GICINT128_STATUS}, + {2, 11, 1, R_GICINT129_EN, R_GICINT129_STATUS}, + {3, 12, 1, R_GICINT130_EN, R_GICINT130_STATUS}, + {4, 13, 1, R_GICINT131_EN, R_GICINT131_STATUS}, + {5, 14, 1, R_GICINT132_EN, R_GICINT132_STATUS}, + {6, 15, 1, R_GICINT133_EN, R_GICINT133_STATUS}, + {7, 16, 1, R_GICINT134_EN, R_GICINT134_STATUS}, + {8, 17, 1, R_GICINT135_EN, R_GICINT135_STATUS}, + {9, 18, 1, R_GICINT136_EN, R_GICINT136_STATUS}, +}; + +static void aspeed_2700_intc_class_init(ObjectClass *klass, const 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_inpins = 10; + aic->num_outpins = 19; + aic->mem_size = 0x4000; + aic->nr_regs = 0xB08 >> 2; + aic->reg_offset = 0x1000; + aic->irq_table = aspeed_2700_intc_irqs; + aic->irq_table_count = ARRAY_SIZE(aspeed_2700_intc_irqs); +} + +static const TypeInfo aspeed_2700_intc_info = { + .name = TYPE_ASPEED_2700_INTC, + .parent = TYPE_ASPEED_INTC, + .class_init = aspeed_2700_intc_class_init, +}; + +static AspeedINTCIRQ aspeed_2700_intcio_irqs[ASPEED_INTC_MAX_INPINS] = { + {0, 0, 1, R_GICINT192_EN, R_GICINT192_STATUS}, + {1, 1, 1, R_GICINT193_EN, R_GICINT193_STATUS}, + {2, 2, 1, R_GICINT194_EN, R_GICINT194_STATUS}, + {3, 3, 1, R_GICINT195_EN, R_GICINT195_STATUS}, + {4, 4, 1, R_GICINT196_EN, R_GICINT196_STATUS}, + {5, 5, 1, R_GICINT197_EN, R_GICINT197_STATUS}, +}; + +static void aspeed_2700_intcio_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); + + dc->desc = "ASPEED 2700 INTC IO Controller"; + aic->num_lines = 32; + aic->num_inpins = 6; + aic->num_outpins = 6; + aic->mem_size = 0x400; + aic->nr_regs = 0x58 >> 2; + aic->reg_offset = 0x100; + aic->reg_ops = &aspeed_intcio_ops; + aic->irq_table = aspeed_2700_intcio_irqs; + aic->irq_table_count = ARRAY_SIZE(aspeed_2700_intcio_irqs); +} + +static const TypeInfo aspeed_2700_intcio_info = { + .name = TYPE_ASPEED_2700_INTCIO, + .parent = TYPE_ASPEED_INTC, + .class_init = aspeed_2700_intcio_class_init, +}; + +static AspeedINTCIRQ aspeed_2700ssp_intc_irqs[ASPEED_INTC_MAX_INPINS] = { + {0, 0, 10, R_SSPINT160_169_EN, R_SSPINT160_169_STATUS}, + {1, 10, 1, R_SSPINT128_EN, R_SSPINT128_STATUS}, + {2, 11, 1, R_SSPINT129_EN, R_SSPINT129_STATUS}, + {3, 12, 1, R_SSPINT130_EN, R_SSPINT130_STATUS}, + {4, 13, 1, R_SSPINT131_EN, R_SSPINT131_STATUS}, + {5, 14, 1, R_SSPINT132_EN, R_SSPINT132_STATUS}, + {6, 15, 1, R_SSPINT133_EN, R_SSPINT133_STATUS}, + {7, 16, 1, R_SSPINT134_EN, R_SSPINT134_STATUS}, + {8, 17, 1, R_SSPINT135_EN, R_SSPINT135_STATUS}, + {9, 18, 1, R_SSPINT136_EN, R_SSPINT136_STATUS}, +}; + +static void aspeed_2700ssp_intc_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); + + dc->desc = "ASPEED 2700 SSP INTC Controller"; + aic->num_lines = 32; + aic->num_inpins = 10; + aic->num_outpins = 19; + aic->mem_size = 0x4000; + aic->nr_regs = 0x2B08 >> 2; + aic->reg_offset = 0x0; + aic->reg_ops = &aspeed_ssp_intc_ops; + aic->irq_table = aspeed_2700ssp_intc_irqs; + aic->irq_table_count = ARRAY_SIZE(aspeed_2700ssp_intc_irqs); +} + +static const TypeInfo aspeed_2700ssp_intc_info = { + .name = TYPE_ASPEED_2700SSP_INTC, + .parent = TYPE_ASPEED_INTC, + .class_init = aspeed_2700ssp_intc_class_init, +}; + +static AspeedINTCIRQ aspeed_2700ssp_intcio_irqs[ASPEED_INTC_MAX_INPINS] = { + {0, 0, 1, R_SSPINT160_EN, R_SSPINT160_STATUS}, + {1, 1, 1, R_SSPINT161_EN, R_SSPINT161_STATUS}, + {2, 2, 1, R_SSPINT162_EN, R_SSPINT162_STATUS}, + {3, 3, 1, R_SSPINT163_EN, R_SSPINT163_STATUS}, + {4, 4, 1, R_SSPINT164_EN, R_SSPINT164_STATUS}, + {5, 5, 1, R_SSPINT165_EN, R_SSPINT165_STATUS}, +}; + +static void aspeed_2700ssp_intcio_class_init(ObjectClass *klass, + const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); + + dc->desc = "ASPEED 2700 SSP INTC IO Controller"; + aic->num_lines = 32; + aic->num_inpins = 6; + aic->num_outpins = 6; + aic->mem_size = 0x400; + aic->nr_regs = 0x1d8 >> 2; + aic->reg_offset = 0; + aic->reg_ops = &aspeed_ssp_intcio_ops; + aic->irq_table = aspeed_2700ssp_intcio_irqs; + aic->irq_table_count = ARRAY_SIZE(aspeed_2700ssp_intcio_irqs); +} + +static const TypeInfo aspeed_2700ssp_intcio_info = { + .name = TYPE_ASPEED_2700SSP_INTCIO, + .parent = TYPE_ASPEED_INTC, + .class_init = aspeed_2700ssp_intcio_class_init, +}; + +static AspeedINTCIRQ aspeed_2700tsp_intc_irqs[ASPEED_INTC_MAX_INPINS] = { + {0, 0, 10, R_TSPINT160_169_EN, R_TSPINT160_169_STATUS}, + {1, 10, 1, R_TSPINT128_EN, R_TSPINT128_STATUS}, + {2, 11, 1, R_TSPINT129_EN, R_TSPINT129_STATUS}, + {3, 12, 1, R_TSPINT130_EN, R_TSPINT130_STATUS}, + {4, 13, 1, R_TSPINT131_EN, R_TSPINT131_STATUS}, + {5, 14, 1, R_TSPINT132_EN, R_TSPINT132_STATUS}, + {6, 15, 1, R_TSPINT133_EN, R_TSPINT133_STATUS}, + {7, 16, 1, R_TSPINT134_EN, R_TSPINT134_STATUS}, + {8, 17, 1, R_TSPINT135_EN, R_TSPINT135_STATUS}, + {9, 18, 1, R_TSPINT136_EN, R_TSPINT136_STATUS}, +}; + +static void aspeed_2700tsp_intc_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); + + dc->desc = "ASPEED 2700 TSP INTC Controller"; + aic->num_lines = 32; + aic->num_inpins = 10; + aic->num_outpins = 19; + aic->mem_size = 0x4000; + aic->nr_regs = 0x3B08 >> 2; + aic->reg_offset = 0; + aic->reg_ops = &aspeed_tsp_intc_ops; + aic->irq_table = aspeed_2700tsp_intc_irqs; + aic->irq_table_count = ARRAY_SIZE(aspeed_2700tsp_intc_irqs); +} + +static const TypeInfo aspeed_2700tsp_intc_info = { + .name = TYPE_ASPEED_2700TSP_INTC, + .parent = TYPE_ASPEED_INTC, + .class_init = aspeed_2700tsp_intc_class_init, +}; + +static AspeedINTCIRQ aspeed_2700tsp_intcio_irqs[ASPEED_INTC_MAX_INPINS] = { + {0, 0, 1, R_TSPINT160_EN, R_TSPINT160_STATUS}, + {1, 1, 1, R_TSPINT161_EN, R_TSPINT161_STATUS}, + {2, 2, 1, R_TSPINT162_EN, R_TSPINT162_STATUS}, + {3, 3, 1, R_TSPINT163_EN, R_TSPINT163_STATUS}, + {4, 4, 1, R_TSPINT164_EN, R_TSPINT164_STATUS}, + {5, 5, 1, R_TSPINT165_EN, R_TSPINT165_STATUS}, +}; + +static void aspeed_2700tsp_intcio_class_init(ObjectClass *klass, + const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); + + dc->desc = "ASPEED 2700 TSP INTC IO Controller"; + aic->num_lines = 32; + aic->num_inpins = 6; + aic->num_outpins = 6; + aic->mem_size = 0x400; + aic->nr_regs = 0x258 >> 2; + aic->reg_offset = 0x0; + aic->reg_ops = &aspeed_tsp_intcio_ops; + aic->irq_table = aspeed_2700tsp_intcio_irqs; + aic->irq_table_count = ARRAY_SIZE(aspeed_2700tsp_intcio_irqs); +} + +static const TypeInfo aspeed_2700tsp_intcio_info = { + .name = TYPE_ASPEED_2700TSP_INTCIO, + .parent = TYPE_ASPEED_INTC, + .class_init = aspeed_2700tsp_intcio_class_init, +}; + +static void aspeed_intc_register_types(void) +{ + type_register_static(&aspeed_intc_info); + type_register_static(&aspeed_2700_intc_info); + type_register_static(&aspeed_2700_intcio_info); + type_register_static(&aspeed_2700ssp_intc_info); + type_register_static(&aspeed_2700ssp_intcio_info); + type_register_static(&aspeed_2700tsp_intc_info); + type_register_static(&aspeed_2700tsp_intcio_info); +} + +type_init(aspeed_intc_register_types); diff --git a/hw/intc/aspeed_vic.c b/hw/intc/aspeed_vic.c index 5ba06c52628..7120088454c 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), @@ -339,11 +339,11 @@ static const VMStateDescription vmstate_aspeed_vic = { } }; -static void aspeed_vic_class_init(ObjectClass *klass, void *data) +static void aspeed_vic_class_init(ObjectClass *klass, const 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 4513fad16f2..55e0a5a503b 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), @@ -219,11 +219,11 @@ static const VMStateDescription vmstate_bcm2835_ic = { } }; -static void bcm2835_ic_class_init(ObjectClass *klass, void *data) +static void bcm2835_ic_class_init(ObjectClass *klass, const 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 b0589df1881..1c028536696 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), @@ -384,11 +384,11 @@ static const VMStateDescription vmstate_bcm2836_control = { } }; -static void bcm2836_control_class_init(ObjectClass *klass, void *data) +static void bcm2836_control_class_init(ObjectClass *klass, const 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 bd37d1cca04..00000000000 --- 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 4ba448fdb19..ebbe23436f4 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, @@ -325,16 +325,15 @@ static void exynos4210_combiner_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static Property exynos4210_combiner_properties[] = { +static const Property exynos4210_combiner_properties[] = { DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void exynos4210_combiner_class_init(ObjectClass *klass, void *data) +static void exynos4210_combiner_class_init(ObjectClass *klass, const 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/exynos4210_gic.c b/hw/intc/exynos4210_gic.c index fcca85c6c69..7e2d79d00cd 100644 --- a/hw/intc/exynos4210_gic.c +++ b/hw/intc/exynos4210_gic.c @@ -111,12 +111,11 @@ static void exynos4210_gic_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->dist_container); } -static Property exynos4210_gic_properties[] = { +static const Property exynos4210_gic_properties[] = { DEFINE_PROP_UINT32("num-cpu", Exynos4210GicState, num_cpu, 1), - DEFINE_PROP_END_OF_LIST(), }; -static void exynos4210_gic_class_init(ObjectClass *klass, void *data) +static void exynos4210_gic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 8d29b40ca10..8ddbf554c69 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 29d5cdc1b69..bc9f518fe86 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 dfd53275f69..2359861785d 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() @@ -181,17 +181,16 @@ static void goldfish_pic_instance_init(Object *obj) qdev_init_gpio_in(DEVICE(obj), goldfish_irq_request, GOLDFISH_PIC_IRQ_NB); } -static Property goldfish_pic_properties[] = { +static const Property goldfish_pic_properties[] = { DEFINE_PROP_UINT8("index", GoldfishPICState, idx, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void goldfish_pic_class_init(ObjectClass *oc, void *data) +static void goldfish_pic_class_init(ObjectClass *oc, const 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; @@ -205,7 +204,7 @@ static const TypeInfo goldfish_pic_info = { .class_init = goldfish_pic_class_init, .instance_init = goldfish_pic_instance_init, .instance_size = sizeof(GoldfishPICState), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_INTERRUPT_STATS_PROVIDER }, { } }, diff --git a/hw/intc/grlib_irqmp.c b/hw/intc/grlib_irqmp.c index 3bfe2544b7c..e0f26466ba3 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]; - - level0 = pend & ~state->level; - level1 = pend & state->level; + 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; - trace_grlib_irqmp_check_irqs(state->pending, state->force[0], - state->mask[0], level1, level0); + trace_grlib_irqmp_check_irqs(state->pending, state->force[i], + state->mask[i], 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,55 @@ 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 void grlib_irqmp_class_init(ObjectClass *klass, void *data) +static const Property grlib_irqmp_properties[] = { + DEFINE_PROP_UINT32("ncpus", IRQMP, ncpus, 1), +}; + +static void grlib_irqmp_class_init(ObjectClass *klass, const 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 13048a27354..447e8c25d8d 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() @@ -184,11 +184,11 @@ static void heathrow_init(Object *obj) sysbus_init_mmio(sbd, &s->mem); } -static void heathrow_class_init(ObjectClass *oc, void *data) +static void heathrow_class_init(ObjectClass *oc, const 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 bbae2d87f4b..b6f96bf208c 100644 --- a/hw/intc/i8259.c +++ b/hw/intc/i8259.c @@ -32,10 +32,7 @@ #include "trace.h" #include "qom/object.h" -/* debug PIC */ -//#define DEBUG_PIC - -//#define DEBUG_IRQ_LATENCY +/*#define DEBUG_IRQ_LATENCY*/ #define TYPE_I8259 "isa-i8259" typedef struct PICClass PICClass; @@ -436,13 +433,13 @@ qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq_in) return irq_set; } -static void i8259_class_init(ObjectClass *klass, void *data) +static void i8259_class_init(ObjectClass *klass, const void *data) { PICClass *k = PIC_CLASS(klass); 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 c931dc2d07c..602e44c8eaf 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]; @@ -132,16 +131,17 @@ 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) @@ -156,7 +156,7 @@ static const VMStateDescription vmstate_pic_ltim = { .version_id = 1, .minimum_version_id = 1, .needed = ltim_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(ltim, PICCommonState), VMSTATE_END_OF_LIST() } @@ -168,7 +168,7 @@ static const VMStateDescription vmstate_pic_common = { .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), @@ -187,21 +187,20 @@ static const VMStateDescription vmstate_pic_common = { VMSTATE_UINT8(elcr, PICCommonState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_pic_ltim, NULL } }; -static Property pic_properties_common[] = { +static const Property pic_properties_common[] = { DEFINE_PROP_UINT32("iobase", PICCommonState, iobase, -1), DEFINE_PROP_UINT32("elcr_addr", PICCommonState, elcr_addr, -1), DEFINE_PROP_UINT8("elcr_mask", PICCommonState, elcr_mask, -1), DEFINE_PROP_BIT("master", PICCommonState, master, 0, false), - DEFINE_PROP_END_OF_LIST(), }; -static void pic_common_class_init(ObjectClass *klass, void *data) +static void pic_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(klass); @@ -227,7 +226,7 @@ static const TypeInfo pic_common_type = { .class_size = sizeof(PICCommonClass), .class_init = pic_common_class_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_INTERRUPT_STATS_PROVIDER }, { } }, diff --git a/hw/intc/imx_avic.c b/hw/intc/imx_avic.c index 63fc602a1a9..09c3bfac0b6 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), @@ -341,12 +341,12 @@ static void imx_avic_init(Object *obj) } -static void imx_avic_class_init(ObjectClass *klass, void *data) +static void imx_avic_class_init(ObjectClass *klass, const 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 237d5f97eba..58d286c1cfc 100644 --- a/hw/intc/imx_gpcv2.c +++ b/hw/intc/imx_gpcv2.c @@ -96,17 +96,17 @@ 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() }, }; -static void imx_gpcv2_class_init(ObjectClass *klass, void *data) +static void imx_gpcv2_class_init(ObjectClass *klass, const 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 00000000000..4dcd86248da --- /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 716ffc8bbbd..133bef852d1 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -30,8 +30,8 @@ #include "hw/intc/ioapic_internal.h" #include "hw/pci/msi.h" #include "hw/qdev-properties.h" -#include "sysemu/kvm.h" -#include "sysemu/sysemu.h" +#include "system/kvm.h" +#include "system/system.h" #include "hw/i386/apic-msidef.h" #include "hw/i386/x86-iommu.h" #include "trace.h" @@ -476,12 +476,11 @@ static void ioapic_unrealize(DeviceState *dev) timer_free(s->delayed_ioapic_service_timer); } -static Property ioapic_properties[] = { +static const Property ioapic_properties[] = { DEFINE_PROP_UINT8("version", IOAPICCommonState, version, IOAPIC_VER_DEF), - DEFINE_PROP_END_OF_LIST(), }; -static void ioapic_class_init(ObjectClass *klass, void *data) +static void ioapic_class_init(ObjectClass *klass, const void *data) { IOAPICCommonClass *k = IOAPIC_COMMON_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -493,7 +492,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 b05f436dac2..fce3486e519 100644 --- a/hw/intc/ioapic_common.c +++ b/hw/intc/ioapic_common.c @@ -23,7 +23,6 @@ #include "qapi/error.h" #include "qemu/module.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "hw/intc/intc.h" #include "hw/intc/ioapic.h" #include "hw/intc/ioapic_internal.h" @@ -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 */ @@ -192,7 +197,7 @@ static const VMStateDescription vmstate_ioapic_common = { } }; -static void ioapic_common_class_init(ObjectClass *klass, void *data) +static void ioapic_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(klass); @@ -210,7 +215,7 @@ static const TypeInfo ioapic_common_type = { .class_size = sizeof(IOAPICCommonClass), .class_init = ioapic_common_class_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_INTERRUPT_STATS_PROVIDER }, { } }, diff --git a/hw/intc/ioapic_internal.h b/hw/intc/ioapic_internal.h index 37b8565539b..51205767f44 100644 --- a/hw/intc/ioapic_internal.h +++ b/hw/intc/ioapic_internal.h @@ -22,7 +22,7 @@ #ifndef HW_INTC_IOAPIC_INTERNAL_H #define HW_INTC_IOAPIC_INTERNAL_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/intc/ioapic.h" #include "hw/sysbus.h" #include "qemu/notify.h" diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index af754606438..8b8ac6b187b 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -8,17 +8,33 @@ #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" -#include "hw/qdev-properties.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" +#include "system/kvm.h" #include "hw/intc/loongarch_extioi.h" -#include "migration/vmstate.h" #include "trace.h" +static int extioi_get_index_from_archid(LoongArchExtIOICommonState *s, + uint64_t arch_id) +{ + int i; + + for (i = 0; i < s->num_cpu; i++) { + if (s->cpu[i].arch_id == arch_id) { + break; + } + } + + if ((i < s->num_cpu) && s->cpu[i].cpu) { + return i; + } -static void extioi_update_irq(LoongArchExtIOI *s, int irq, int level) + return -1; +} + +static void extioi_update_irq(LoongArchExtIOICommonState *s, int irq, int level) { int ipnum, cpu, found, irq_index, irq_mask; @@ -32,38 +48,33 @@ 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) { - LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(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); } @@ -71,7 +82,7 @@ static void extioi_setirq(void *opaque, int irq, int level) static MemTxResult extioi_readw(void *opaque, hwaddr addr, uint64_t *data, unsigned size, MemTxAttrs attrs) { - LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); unsigned long offset = addr & 0xffff; uint32_t index, cpu; @@ -96,7 +107,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; @@ -110,7 +121,7 @@ static MemTxResult extioi_readw(void *opaque, hwaddr addr, uint64_t *data, return MEMTX_OK; } -static inline void extioi_enable_irq(LoongArchExtIOI *s, int index,\ +static inline void extioi_enable_irq(LoongArchExtIOICommonState *s, int index,\ uint32_t mask, int level) { uint32_t val; @@ -129,12 +140,74 @@ static inline void extioi_enable_irq(LoongArchExtIOI *s, int index,\ } } +static inline void extioi_update_sw_coremap(LoongArchExtIOICommonState *s, + int irq, uint64_t val, bool notify) +{ + int i, cpu, cpuid; + + /* + * loongarch only support little endian, + * so we paresd the value with little endian. + */ + val = cpu_to_le64(val); + + for (i = 0; i < 4; i++) { + cpuid = val & 0xff; + val = val >> 8; + + if (!(s->status & BIT(EXTIOI_ENABLE_CPU_ENCODE))) { + cpuid = ctz32(cpuid); + cpuid = (cpuid >= 4) ? 0 : cpuid; + } + + cpu = extioi_get_index_from_archid(s, cpuid); + if (cpu < 0) { + continue; + } + + 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(LoongArchExtIOICommonState *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; + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); + int cpu, index, old_data, irq; uint32_t offset; trace_loongarch_extioi_writew(addr, val); @@ -152,20 +225,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 +249,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 +264,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,70 +283,175 @@ static const MemoryRegionOps extioi_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static const VMStateDescription vmstate_loongarch_extioi = { - .name = TYPE_LOONGARCH_EXTIOI, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOI, EXTIOI_IRQS_GROUP_COUNT), - VMSTATE_UINT32_2DARRAY(coreisr, LoongArchExtIOI, EXTIOI_CPUS, - 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_END_OF_LIST() +static MemTxResult extioi_virt_readw(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(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) +{ + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(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_instance_init(Object *obj) +static void loongarch_extioi_realize(DeviceState *dev, Error **errp) { - SysBusDevice *dev = SYS_BUS_DEVICE(obj); - LoongArchExtIOI *s = LOONGARCH_EXTIOI(obj); - int i, cpu, pin; + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(dev); + LoongArchExtIOIClass *lec = LOONGARCH_EXTIOI_GET_CLASS(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Error *local_err = NULL; + int i; + + lec->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } - for (i = 0; i < EXTIOI_IRQS; i++) { - sysbus_init_irq(dev, &s->irq[i]); + if (s->features & BIT(EXTIOI_HAS_VIRT_EXTENSION)) { + s->features |= EXTIOI_VIRT_HAS_FEATURES; + } else { + s->status |= BIT(EXTIOI_ENABLE); } - qdev_init_gpio_in(DEVICE(obj), extioi_setirq, EXTIOI_IRQS); + if (kvm_irqchip_in_kernel()) { + kvm_extioi_realize(dev, errp); + } else { + for (i = 0; i < EXTIOI_IRQS; i++) { + sysbus_init_irq(sbd, &s->irq[i]); + } - for (cpu = 0; cpu < EXTIOI_CPUS; cpu++) { - memory_region_init_io(&s->extioi_iocsr_mem[cpu], OBJECT(s), &extioi_ops, - s, "extioi_iocsr", 0x900); - sysbus_init_mmio(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); + 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); } } - memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops, - s, "extioi_system_mem", 0x900); - sysbus_init_mmio(dev, &s->extioi_system_mem); } -static void loongarch_extioi_class_init(ObjectClass *klass, void *data) +static void loongarch_extioi_unrealize(DeviceState *dev) { - DeviceClass *dc = DEVICE_CLASS(klass); + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(dev); - dc->vmsd = &vmstate_loongarch_extioi; + g_free(s->cpu); } -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, -}; +static void loongarch_extioi_reset_hold(Object *obj, ResetType type) +{ + LoongArchExtIOIClass *lec = LOONGARCH_EXTIOI_GET_CLASS(obj); -static void loongarch_extioi_register_types(void) + if (lec->parent_phases.hold) { + lec->parent_phases.hold(obj, type); + } + + if (kvm_irqchip_in_kernel()) { + kvm_extioi_put(obj, 0); + } +} + +static int vmstate_extioi_pre_save(void *opaque) +{ + if (kvm_irqchip_in_kernel()) { + return kvm_extioi_get(opaque); + } + + return 0; +} + +static int vmstate_extioi_post_load(void *opaque, int version_id) +{ + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); + int i, start_irq; + + if (kvm_irqchip_in_kernel()) { + return kvm_extioi_put(opaque, version_id); + } + + 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 void loongarch_extioi_class_init(ObjectClass *klass, const void *data) { - type_register_static(&loongarch_extioi_info); + DeviceClass *dc = DEVICE_CLASS(klass); + LoongArchExtIOIClass *lec = LOONGARCH_EXTIOI_CLASS(klass); + LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + device_class_set_parent_realize(dc, loongarch_extioi_realize, + &lec->parent_realize); + device_class_set_parent_unrealize(dc, loongarch_extioi_unrealize, + &lec->parent_unrealize); + resettable_class_set_parent_phases(rc, NULL, loongarch_extioi_reset_hold, + NULL, &lec->parent_phases); + lecc->pre_save = vmstate_extioi_pre_save; + lecc->post_load = vmstate_extioi_post_load; } -type_init(loongarch_extioi_register_types) +static const TypeInfo loongarch_extioi_types[] = { + { + .name = TYPE_LOONGARCH_EXTIOI, + .parent = TYPE_LOONGARCH_EXTIOI_COMMON, + .instance_size = sizeof(LoongArchExtIOIState), + .class_size = sizeof(LoongArchExtIOIClass), + .class_init = loongarch_extioi_class_init, + } +}; + +DEFINE_TYPES(loongarch_extioi_types) diff --git a/hw/intc/loongarch_extioi_common.c b/hw/intc/loongarch_extioi_common.c new file mode 100644 index 00000000000..4a904b3bc15 --- /dev/null +++ b/hw/intc/loongarch_extioi_common.c @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Loongson extioi interrupt controller emulation + * Copyright (C) 2024 Loongson Technology Corporation Limited + */ +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/intc/loongarch_extioi_common.h" +#include "migration/vmstate.h" +#include "target/loongarch/cpu.h" + +static ExtIOICore *loongarch_extioi_get_cpu(LoongArchExtIOICommonState *s, + DeviceState *dev) +{ + CPUClass *k = CPU_GET_CLASS(dev); + uint64_t arch_id = k->get_arch_id(CPU(dev)); + int i; + + for (i = 0; i < s->num_cpu; i++) { + if (s->cpu[i].arch_id == arch_id) { + return &s->cpu[i]; + } + } + + return NULL; +} + +static void loongarch_extioi_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(hotplug_dev); + Object *obj = OBJECT(dev); + ExtIOICore *core; + int pin, index; + + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch extioi: Invalid %s device type", + object_get_typename(obj)); + return; + } + + core = loongarch_extioi_get_cpu(s, dev); + if (!core) { + return; + } + + core->cpu = CPU(dev); + index = core - s->cpu; + + /* + * connect extioi irq to the cpu irq + * cpu_pin[LS3A_INTC_IP + 2 : 2] <= intc_pin[LS3A_INTC_IP : 0] + */ + for (pin = 0; pin < LS3A_INTC_IP; pin++) { + qdev_connect_gpio_out(DEVICE(s), index * LS3A_INTC_IP + pin, + qdev_get_gpio_in(dev, pin + 2)); + } +} + +static void loongarch_extioi_cpu_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(hotplug_dev); + Object *obj = OBJECT(dev); + ExtIOICore *core; + + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch extioi: Invalid %s device type", + object_get_typename(obj)); + return; + } + + core = loongarch_extioi_get_cpu(s, dev); + if (!core) { + return; + } + + core->cpu = NULL; +} + +static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) +{ + LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)dev; + MachineState *machine = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(machine); + const CPUArchIdList *id_list; + int i, pin; + + assert(mc->possible_cpu_arch_ids); + id_list = mc->possible_cpu_arch_ids(machine); + s->num_cpu = id_list->len; + 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++) { + s->cpu[i].arch_id = id_list->cpus[i].arch_id; + s->cpu[i].cpu = CPU(id_list->cpus[i].cpu); + + for (pin = 0; pin < LS3A_INTC_IP; pin++) { + qdev_init_gpio_out(dev, &s->cpu[i].parent_irq[pin], 1); + } + } +} + +static void loongarch_extioi_common_reset_hold(Object *obj, ResetType type) +{ + LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_GET_CLASS(obj); + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(obj); + ExtIOICore *core; + int i; + + if (lecc->parent_phases.hold) { + lecc->parent_phases.hold(obj, type); + } + + /* Clear HW registers for the board */ + memset(s->nodetype, 0, sizeof(s->nodetype)); + memset(s->bounce, 0, sizeof(s->bounce)); + memset(s->isr, 0, sizeof(s->isr)); + memset(s->enable, 0, sizeof(s->enable)); + memset(s->ipmap, 0, sizeof(s->ipmap)); + memset(s->coremap, 0, sizeof(s->coremap)); + memset(s->sw_pending, 0, sizeof(s->sw_pending)); + memset(s->sw_ipmap, 0, sizeof(s->sw_ipmap)); + memset(s->sw_coremap, 0, sizeof(s->sw_coremap)); + + for (i = 0; i < s->num_cpu; i++) { + core = s->cpu + i; + /* EXTIOI with targeted CPU available however not present */ + if (!core->cpu) { + continue; + } + + /* Clear HW registers for CPUs */ + memset(core->coreisr, 0, sizeof(core->coreisr)); + memset(core->sw_isr, 0, sizeof(core->sw_isr)); + } + + s->status = 0; +} + +static int loongarch_extioi_common_pre_save(void *opaque) +{ + LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)opaque; + LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_GET_CLASS(s); + + if (lecc->pre_save) { + return lecc->pre_save(s); + } + + return 0; +} + +static int loongarch_extioi_common_post_load(void *opaque, int version_id) +{ + LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)opaque; + LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_GET_CLASS(s); + + if (lecc->post_load) { + return lecc->post_load(s, version_id); + } + + return 0; +} + +static const VMStateDescription vmstate_extioi_core = { + .name = "extioi-core", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(coreisr, ExtIOICore, EXTIOI_IRQS_GROUP_COUNT), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_loongarch_extioi = { + .name = "loongarch.extioi", + .version_id = 3, + .minimum_version_id = 3, + .pre_save = loongarch_extioi_common_pre_save, + .post_load = loongarch_extioi_common_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOICommonState, + EXTIOI_IRQS_GROUP_COUNT), + VMSTATE_UINT32_ARRAY(nodetype, LoongArchExtIOICommonState, + EXTIOI_IRQS_NODETYPE_COUNT / 2), + VMSTATE_UINT32_ARRAY(enable, LoongArchExtIOICommonState, + EXTIOI_IRQS / 32), + VMSTATE_UINT32_ARRAY(isr, LoongArchExtIOICommonState, + EXTIOI_IRQS / 32), + VMSTATE_UINT32_ARRAY(ipmap, LoongArchExtIOICommonState, + EXTIOI_IRQS_IPMAP_SIZE / 4), + VMSTATE_UINT32_ARRAY(coremap, LoongArchExtIOICommonState, + EXTIOI_IRQS / 4), + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchExtIOICommonState, + num_cpu, vmstate_extioi_core, ExtIOICore), + VMSTATE_UINT32(features, LoongArchExtIOICommonState), + VMSTATE_UINT32(status, LoongArchExtIOICommonState), + VMSTATE_END_OF_LIST() + } +}; + +static const Property extioi_properties[] = { + DEFINE_PROP_BIT("has-virtualization-extension", LoongArchExtIOICommonState, + features, EXTIOI_HAS_VIRT_EXTENSION, 0), +}; + +static void loongarch_extioi_common_class_init(ObjectClass *klass, + const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + device_class_set_parent_realize(dc, loongarch_extioi_common_realize, + &lecc->parent_realize); + resettable_class_set_parent_phases(rc, NULL, + loongarch_extioi_common_reset_hold, + NULL, &lecc->parent_phases); + device_class_set_props(dc, extioi_properties); + dc->vmsd = &vmstate_loongarch_extioi; + hc->plug = loongarch_extioi_cpu_plug; + hc->unplug = loongarch_extioi_cpu_unplug; +} + +static const TypeInfo loongarch_extioi_common_types[] = { + { + .name = TYPE_LOONGARCH_EXTIOI_COMMON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LoongArchExtIOICommonState), + .class_size = sizeof(LoongArchExtIOICommonClass), + .class_init = loongarch_extioi_common_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, + .abstract = true, + } +}; + +DEFINE_TYPES(loongarch_extioi_common_types) diff --git a/hw/intc/loongarch_extioi_kvm.c b/hw/intc/loongarch_extioi_kvm.c new file mode 100644 index 00000000000..0133540c45d --- /dev/null +++ b/hw/intc/loongarch_extioi_kvm.c @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch EXTIOI interrupt kvm support + * + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qemu/typedefs.h" +#include "hw/intc/loongarch_extioi.h" +#include "linux/kvm.h" +#include "qapi/error.h" +#include "system/kvm.h" + +static void kvm_extioi_access_reg(int fd, uint64_t addr, void *val, bool write) +{ + kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS, + addr, val, write, &error_abort); +} + +static void kvm_extioi_access_sw_state(int fd, uint64_t addr, + void *val, bool write) +{ + kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_SW_STATUS, + addr, val, write, &error_abort); +} + +static void kvm_extioi_access_sw_status(void *opaque, bool write) +{ + LoongArchExtIOICommonState *lecs = LOONGARCH_EXTIOI_COMMON(opaque); + LoongArchExtIOIState *les = LOONGARCH_EXTIOI(opaque); + int addr; + + addr = KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_STATE; + kvm_extioi_access_sw_state(les->dev_fd, addr, &lecs->status, write); +} + +static void kvm_extioi_access_regs(void *opaque, bool write) +{ + LoongArchExtIOICommonState *lecs = LOONGARCH_EXTIOI_COMMON(opaque); + LoongArchExtIOIState *les = LOONGARCH_EXTIOI(opaque); + int fd = les->dev_fd; + int addr, offset, cpu; + + for (addr = EXTIOI_NODETYPE_START; addr < EXTIOI_NODETYPE_END; addr += 4) { + offset = (addr - EXTIOI_NODETYPE_START) / 4; + kvm_extioi_access_reg(fd, addr, &lecs->nodetype[offset], write); + } + + for (addr = EXTIOI_IPMAP_START; addr < EXTIOI_IPMAP_END; addr += 4) { + offset = (addr - EXTIOI_IPMAP_START) / 4; + kvm_extioi_access_reg(fd, addr, &lecs->ipmap[offset], write); + } + + for (addr = EXTIOI_ENABLE_START; addr < EXTIOI_ENABLE_END; addr += 4) { + offset = (addr - EXTIOI_ENABLE_START) / 4; + kvm_extioi_access_reg(fd, addr, &lecs->enable[offset], write); + } + + for (addr = EXTIOI_BOUNCE_START; addr < EXTIOI_BOUNCE_END; addr += 4) { + offset = (addr - EXTIOI_BOUNCE_START) / 4; + kvm_extioi_access_reg(fd, addr, &lecs->bounce[offset], write); + } + + for (addr = EXTIOI_ISR_START; addr < EXTIOI_ISR_END; addr += 4) { + offset = (addr - EXTIOI_ISR_START) / 4; + kvm_extioi_access_reg(fd, addr, &lecs->isr[offset], write); + } + + for (addr = EXTIOI_COREMAP_START; addr < EXTIOI_COREMAP_END; addr += 4) { + offset = (addr - EXTIOI_COREMAP_START) / 4; + kvm_extioi_access_reg(fd, addr, &lecs->coremap[offset], write); + } + + for (cpu = 0; cpu < lecs->num_cpu; cpu++) { + for (addr = EXTIOI_COREISR_START; + addr < EXTIOI_COREISR_END; addr += 4) { + offset = (addr - EXTIOI_COREISR_START) / 4; + kvm_extioi_access_reg(fd, (cpu << 16) | addr, + &lecs->cpu[cpu].coreisr[offset], write); + } + } +} + +int kvm_extioi_get(void *opaque) +{ + kvm_extioi_access_regs(opaque, false); + kvm_extioi_access_sw_status(opaque, false); + return 0; +} + +int kvm_extioi_put(void *opaque, int version_id) +{ + LoongArchExtIOIState *les = LOONGARCH_EXTIOI(opaque); + int fd = les->dev_fd; + + if (fd == 0) { + return 0; + } + + kvm_extioi_access_regs(opaque, true); + kvm_extioi_access_sw_status(opaque, true); + kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, + KVM_DEV_LOONGARCH_EXTIOI_CTRL_LOAD_FINISHED, + NULL, true, &error_abort); + return 0; +} + +void kvm_extioi_realize(DeviceState *dev, Error **errp) +{ + LoongArchExtIOICommonState *lecs = LOONGARCH_EXTIOI_COMMON(dev); + LoongArchExtIOIState *les = LOONGARCH_EXTIOI(dev); + int ret; + + ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_LOONGARCH_EIOINTC, false); + if (ret < 0) { + fprintf(stderr, "create KVM_LOONGARCH_EIOINTC failed: %s\n", + strerror(-ret)); + abort(); + } + + les->dev_fd = ret; + ret = kvm_device_access(les->dev_fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, + KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_NUM_CPU, + &lecs->num_cpu, true, NULL); + if (ret < 0) { + fprintf(stderr, "KVM_LOONGARCH_EXTIOI_INIT_NUM_CPU failed: %s\n", + strerror(-ret)); + abort(); + } + + ret = kvm_device_access(les->dev_fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, + KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_FEATURE, + &lecs->features, true, NULL); + if (ret < 0) { + fprintf(stderr, "KVM_LOONGARCH_EXTIOI_INIT_FEATURE failed: %s\n", + strerror(-ret)); + abort(); + } +} diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 67858b521c6..fc8005c9444 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -1,313 +1,230 @@ /* 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/intc/loongarch_ipi.h" -#include "hw/irq.h" +#include "qemu/error-report.h" +#include "hw/boards.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 "hw/intc/loongarch_ipi.h" +#include "hw/qdev-properties.h" +#include "system/kvm.h" +#include "target/loongarch/cpu.h" -static void loongarch_ipi_writel(void *, hwaddr, uint64_t, unsigned); +static AddressSpace *get_iocsr_as(CPUState *cpu) +{ + return LOONGARCH_CPU(cpu)->env.address_space_iocsr; +} -static uint64_t loongarch_ipi_readl(void *opaque, hwaddr addr, unsigned size) +static int loongarch_ipi_cmp(const void *a, const void *b) { - 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; - } + IPICore *ipi_a = (IPICore *)a; + IPICore *ipi_b = (IPICore *)b; - trace_loongarch_ipi_read(size, (uint64_t)addr, ret); - return ret; + return ipi_a->arch_id - ipi_b->arch_id; } -static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr) +static int loongarch_cpu_by_arch_id(LoongsonIPICommonState *lics, + int64_t arch_id, int *index, CPUState **pcs) { - int i, mask = 0, data = 0; - - /* - * 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); - } + IPICore ipi, *found; + + ipi.arch_id = arch_id; + found = bsearch(&ipi, lics->cpu, lics->num_cpu, sizeof(IPICore), + loongarch_ipi_cmp); + if (found && found->cpu) { + if (index) { + *index = found - lics->cpu; } - } - data &= mask; - data |= (val >> 32) & ~mask; - address_space_stl(&env->address_space_iocsr, addr, - data, MEMTXATTRS_UNSPECIFIED, NULL); -} + if (pcs) { + *pcs = found->cpu; + } -static int archid_cmp(const void *a, const void *b) -{ - CPUArchId *archid_a = (CPUArchId *)a; - CPUArchId *archid_b = (CPUArchId *)b; + return MEMTX_OK; + } - return archid_a->arch_id - archid_b->arch_id; + return MEMTX_ERROR; } -static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id) +static IPICore *loongarch_ipi_get_cpu(LoongsonIPICommonState *lics, + DeviceState *dev) { - CPUArchId apic_id, *found_cpu; + CPUClass *k = CPU_GET_CLASS(dev); + uint64_t arch_id = k->get_arch_id(CPU(dev)); + int i; - 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); + for (i = 0; i < lics->num_cpu; i++) { + if (lics->cpu[i].arch_id == arch_id) { + return &lics->cpu[i]; + } + } - return found_cpu; + return NULL; } -static CPUState *ipi_getcpu(int arch_id) +static void loongarch_ipi_realize(DeviceState *dev, Error **errp) { + LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(dev); + LoongarchIPIClass *lic = LOONGARCH_IPI_GET_CLASS(dev); MachineState *machine = MACHINE(qdev_get_machine()); - CPUArchId *archid; + MachineClass *mc = MACHINE_GET_CLASS(machine); + const CPUArchIdList *id_list; + Error *local_err = NULL; + int i; + + lic->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + assert(mc->possible_cpu_arch_ids); + id_list = mc->possible_cpu_arch_ids(machine); + lics->num_cpu = id_list->len; + lics->cpu = g_new0(IPICore, lics->num_cpu); + for (i = 0; i < lics->num_cpu; i++) { + lics->cpu[i].arch_id = id_list->cpus[i].arch_id; + lics->cpu[i].cpu = CPU(id_list->cpus[i].cpu); + lics->cpu[i].ipi = lics; + qdev_init_gpio_out(dev, &lics->cpu[i].irq, 1); + } - archid = find_cpu_by_archid(machine, arch_id); - return CPU(archid->cpu); + if (kvm_irqchip_in_kernel()) { + kvm_ipi_realize(dev, errp); + } } -static void ipi_send(uint64_t val) +static void loongarch_ipi_reset_hold(Object *obj, ResetType type) { - uint32_t cpuid; - uint8_t vector; - CPUState *cs; - LoongArchCPU *cpu; - LoongArchIPI *s; - - cpuid = extract32(val, 16, 10); - if (cpuid >= LOONGARCH_MAX_CPUS) { - trace_loongarch_ipi_unsupported_cpuid("IOCSR_IPI_SEND", cpuid); - return; + int i; + LoongarchIPIClass *lic = LOONGARCH_IPI_GET_CLASS(obj); + LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(obj); + IPICore *core; + + if (lic->parent_phases.hold) { + lic->parent_phases.hold(obj, type); } - /* IPI status vector */ - vector = extract8(val, 0, 5); + for (i = 0; i < lics->num_cpu; i++) { + core = lics->cpu + i; + /* IPI with targeted CPU available however not present */ + if (!core->cpu) { + continue; + } - cs = ipi_getcpu(cpuid); - cpu = LOONGARCH_CPU(cs); - s = LOONGARCH_IPI(cpu->env.ipistate); - loongarch_ipi_writel(&s->ipi_core, CORE_SET_OFF, BIT(vector), 4); + core->status = 0; + core->en = 0; + core->set = 0; + core->clear = 0; + memset(core->buf, 0, sizeof(core->buf)); + } + + if (kvm_irqchip_in_kernel()) { + kvm_ipi_put(obj, 0); + } } -static void mail_send(uint64_t val) +static void loongarch_ipi_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { - uint32_t cpuid; - hwaddr addr; - CPULoongArchState *env; - CPUState *cs; - LoongArchCPU *cpu; - - cpuid = extract32(val, 16, 10); - if (cpuid >= LOONGARCH_MAX_CPUS) { - trace_loongarch_ipi_unsupported_cpuid("IOCSR_MAIL_SEND", cpuid); + LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev); + Object *obj = OBJECT(dev); + IPICore *core; + int index; + + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch extioi: Invalid %s device type", + object_get_typename(obj)); return; } - addr = 0x1020 + (val & 0x1c); - cs = ipi_getcpu(cpuid); - cpu = LOONGARCH_CPU(cs); - env = &cpu->env; - send_ipi_data(env, val, addr); -} - -static void any_send(uint64_t val) -{ - uint32_t cpuid; - hwaddr addr; - CPULoongArchState *env; - CPUState *cs; - LoongArchCPU *cpu; - - cpuid = extract32(val, 16, 10); - if (cpuid >= LOONGARCH_MAX_CPUS) { - trace_loongarch_ipi_unsupported_cpuid("IOCSR_ANY_SEND", cpuid); + core = loongarch_ipi_get_cpu(lics, dev); + if (!core) { return; } - addr = val & 0xffff; - cs = ipi_getcpu(cpuid); - cpu = LOONGARCH_CPU(cs); - env = &cpu->env; - send_ipi_data(env, val, addr); + core->cpu = CPU(dev); + index = core - lics->cpu; + + /* connect ipi irq to cpu irq */ + qdev_connect_gpio_out(DEVICE(lics), index, qdev_get_gpio_in(dev, IRQ_IPI)); } -static void loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, - unsigned size) +static void loongarch_ipi_cpu_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { - 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; - } -} + LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(hotplug_dev); + Object *obj = OBJECT(dev); + IPICore *core; -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, -}; + if (!object_dynamic_cast(obj, TYPE_LOONGARCH_CPU)) { + warn_report("LoongArch extioi: Invalid %s device type", + object_get_typename(obj)); + return; + } -/* 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; + core = loongarch_ipi_get_cpu(lics, dev); + if (!core) { + return; } -} -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, -}; + core->cpu = NULL; +} -static void loongarch_ipi_init(Object *obj) +static int loongarch_ipi_pre_save(void *opaque) { - LoongArchIPI *s = LOONGARCH_IPI(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - memory_region_init_io(&s->ipi_iocsr_mem, obj, &loongarch_ipi_ops, - &s->ipi_core, "loongarch_ipi_iocsr", 0x48); - - /* loongarch_ipi_iocsr performs re-entrant IO through ipi_send */ - s->ipi_iocsr_mem.disable_reentrancy_guard = true; - - sysbus_init_mmio(sbd, &s->ipi_iocsr_mem); + if (kvm_irqchip_in_kernel()) { + return kvm_ipi_get(opaque); + } - memory_region_init_io(&s->ipi64_iocsr_mem, obj, &loongarch_ipi64_ops, - &s->ipi_core, "loongarch_ipi64_iocsr", 0x118); - sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); - qdev_init_gpio_out(DEVICE(obj), &s->ipi_core.irq, 1); + return 0; } -static const VMStateDescription vmstate_ipi_core = { - .name = "ipi-single", - .version_id = 2, - .minimum_version_id = 2, - .fields = (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 int loongarch_ipi_post_load(void *opaque, int version_id) +{ + if (kvm_irqchip_in_kernel()) { + return kvm_ipi_put(opaque, version_id); } -}; -static const VMStateDescription vmstate_loongarch_ipi = { - .name = TYPE_LOONGARCH_IPI, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(ipi_core, LoongArchIPI, 0, vmstate_ipi_core, IPICore), - VMSTATE_END_OF_LIST() - } -}; + return 0; +} -static void loongarch_ipi_class_init(ObjectClass *klass, void *data) +static void loongarch_ipi_class_init(ObjectClass *klass, const void *data) { + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); + LoongarchIPIClass *lic = LOONGARCH_IPI_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - dc->vmsd = &vmstate_loongarch_ipi; + device_class_set_parent_realize(dc, loongarch_ipi_realize, + &lic->parent_realize); + resettable_class_set_parent_phases(rc, NULL, loongarch_ipi_reset_hold, + NULL, &lic->parent_phases); + licc->get_iocsr_as = get_iocsr_as; + licc->cpu_by_arch_id = loongarch_cpu_by_arch_id; + hc->plug = loongarch_ipi_cpu_plug; + hc->unplug = loongarch_ipi_cpu_unplug; + licc->pre_save = loongarch_ipi_pre_save; + licc->post_load = loongarch_ipi_post_load; } -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, + .instance_size = sizeof(LoongarchIPIState), + .class_size = sizeof(LoongarchIPIClass), + .class_init = loongarch_ipi_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, + } }; -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_ipi_kvm.c b/hw/intc/loongarch_ipi_kvm.c new file mode 100644 index 00000000000..4cb3acc921f --- /dev/null +++ b/hw/intc/loongarch_ipi_kvm.c @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch IPI interrupt KVM support + * + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/intc/loongarch_ipi.h" +#include "system/kvm.h" +#include "target/loongarch/cpu.h" + +static void kvm_ipi_access_reg(int fd, uint64_t addr, uint32_t *val, bool write) +{ + kvm_device_access(fd, KVM_DEV_LOONGARCH_IPI_GRP_REGS, + addr, val, write, &error_abort); +} + +static void kvm_ipi_access_regs(void *opaque, bool write) +{ + LoongsonIPICommonState *ipi = (LoongsonIPICommonState *)opaque; + LoongarchIPIState *lis = LOONGARCH_IPI(opaque); + IPICore *core; + uint64_t attr; + int cpu, fd = lis->dev_fd; + + if (fd == 0) { + return; + } + + for (cpu = 0; cpu < ipi->num_cpu; cpu++) { + core = &ipi->cpu[cpu]; + attr = (cpu << 16) | CORE_STATUS_OFF; + kvm_ipi_access_reg(fd, attr, &core->status, write); + + attr = (cpu << 16) | CORE_EN_OFF; + kvm_ipi_access_reg(fd, attr, &core->en, write); + + attr = (cpu << 16) | CORE_SET_OFF; + kvm_ipi_access_reg(fd, attr, &core->set, write); + + attr = (cpu << 16) | CORE_CLEAR_OFF; + kvm_ipi_access_reg(fd, attr, &core->clear, write); + + attr = (cpu << 16) | CORE_BUF_20; + kvm_ipi_access_reg(fd, attr, &core->buf[0], write); + + attr = (cpu << 16) | CORE_BUF_28; + kvm_ipi_access_reg(fd, attr, &core->buf[2], write); + + attr = (cpu << 16) | CORE_BUF_30; + kvm_ipi_access_reg(fd, attr, &core->buf[4], write); + + attr = (cpu << 16) | CORE_BUF_38; + kvm_ipi_access_reg(fd, attr, &core->buf[6], write); + } +} + +int kvm_ipi_get(void *opaque) +{ + kvm_ipi_access_regs(opaque, false); + return 0; +} + +int kvm_ipi_put(void *opaque, int version_id) +{ + kvm_ipi_access_regs(opaque, true); + return 0; +} + +void kvm_ipi_realize(DeviceState *dev, Error **errp) +{ + LoongarchIPIState *lis = LOONGARCH_IPI(dev); + int ret; + + ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_LOONGARCH_IPI, false); + if (ret < 0) { + fprintf(stderr, "IPI KVM_CREATE_DEVICE failed: %s\n", + strerror(-ret)); + abort(); + } + + lis->dev_fd = ret; +} diff --git a/hw/intc/loongarch_pch_msi.c b/hw/intc/loongarch_pch_msi.c index ecf3ed0267e..f6d163158da 100644 --- a/hw/intc/loongarch_pch_msi.c +++ b/hw/intc/loongarch_pch_msi.c @@ -13,6 +13,7 @@ #include "hw/pci/msi.h" #include "hw/misc/unimp.h" #include "migration/vmstate.h" +#include "system/kvm.h" #include "trace.h" static uint64_t loongarch_msi_mem_read(void *opaque, hwaddr addr, unsigned size) @@ -26,6 +27,15 @@ static void loongarch_msi_mem_write(void *opaque, hwaddr addr, LoongArchPCHMSI *s = (LoongArchPCHMSI *)opaque; int irq_num; + if (kvm_irqchip_in_kernel()) { + MSIMessage msg; + + msg.address = addr; + msg.data = val; + kvm_irqchip_send_msi(kvm_state, msg); + return; + } + /* * vector number is irq number from upper extioi intc * need subtract irq base to get msi vector offset @@ -42,13 +52,6 @@ static const MemoryRegionOps loongarch_pch_msi_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void pch_msi_irq_handler(void *opaque, int irq, int level) -{ - LoongArchPCHMSI *s = LOONGARCH_PCH_MSI(opaque); - - 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); @@ -59,9 +62,7 @@ static void loongarch_pch_msi_realize(DeviceState *dev, Error **errp) } 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) @@ -83,13 +84,12 @@ static void loongarch_pch_msi_init(Object *obj) } -static Property loongarch_msi_properties[] = { +static const Property loongarch_msi_properties[] = { DEFINE_PROP_UINT32("msi_irq_base", LoongArchPCHMSI, irq_base, 0), DEFINE_PROP_UINT32("msi_irq_num", LoongArchPCHMSI, irq_num, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void loongarch_pch_msi_class_init(ObjectClass *klass, void *data) +static void loongarch_pch_msi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 6aa4cadfa4a..c4b242dbf41 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -7,17 +7,15 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" -#include "hw/sysbus.h" -#include "hw/loongarch/virt.h" -#include "hw/pci-host/ls7a.h" +#include "qemu/log.h" #include "hw/irq.h" #include "hw/intc/loongarch_pch_pic.h" -#include "hw/qdev-properties.h" -#include "migration/vmstate.h" +#include "system/kvm.h" #include "trace.h" #include "qapi/error.h" -static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level) +static void pch_pic_update_irq(LoongArchPICCommonState *s, uint64_t mask, + int level) { uint64_t val; int irq; @@ -45,12 +43,17 @@ static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level) static void pch_pic_irq_handler(void *opaque, int irq, int level) { - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); uint64_t mask = 1ULL << irq; assert(irq < s->irq_num); trace_loongarch_pch_pic_irq_handler(irq, level); + if (kvm_irqchip_in_kernel()) { + kvm_set_irq(kvm_state, irq, !!level); + return; + } + if (s->intedge & mask) { /* Edge triggered */ if (level) { @@ -75,389 +78,266 @@ static void pch_pic_irq_handler(void *opaque, int irq, int level) pch_pic_update_irq(s, mask, level); } -static uint64_t loongarch_pch_pic_low_readw(void *opaque, hwaddr addr, - unsigned size) +static uint64_t pch_pic_read(void *opaque, hwaddr addr, uint64_t field_mask) { - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); uint64_t val = 0; - uint32_t offset = addr & 0xfff; + uint32_t offset; - switch (offset) { - case PCH_PIC_INT_ID_LO: - val = PCH_PIC_INT_ID_VAL; + offset = addr & 7; + addr -= offset; + switch (addr) { + case PCH_PIC_INT_ID: + val = cpu_to_le64(s->id.data); break; - case PCH_PIC_INT_ID_HI: - /* - * 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); + case PCH_PIC_INT_MASK: + val = s->int_mask; break; - case PCH_PIC_INT_MASK_LO: - val = (uint32_t)s->int_mask; + case PCH_PIC_INT_EDGE: + val = s->intedge; break; - case PCH_PIC_INT_MASK_HI: - val = s->int_mask >> 32; + case PCH_PIC_HTMSI_EN: + val = s->htmsi_en; break; - case PCH_PIC_INT_EDGE_LO: - val = (uint32_t)s->intedge; + case PCH_PIC_AUTO_CTRL0: + case PCH_PIC_AUTO_CTRL1: + /* PCH PIC connect to EXTIOI always, discard auto_ctrl access */ break; - case PCH_PIC_INT_EDGE_HI: - val = s->intedge >> 32; + case PCH_PIC_INT_STATUS: + val = s->intisr & (~s->int_mask); break; - case PCH_PIC_HTMSI_EN_LO: - val = (uint32_t)s->htmsi_en; + case PCH_PIC_INT_POL: + val = s->int_polarity; break; - case PCH_PIC_HTMSI_EN_HI: - val = s->htmsi_en >> 32; + case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END: + val = *(uint64_t *)(s->htmsi_vector + addr - PCH_PIC_HTMSI_VEC); break; - case PCH_PIC_AUTO_CTRL0_LO: - case PCH_PIC_AUTO_CTRL0_HI: - case PCH_PIC_AUTO_CTRL1_LO: - case PCH_PIC_AUTO_CTRL1_HI: + case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END: + val = *(uint64_t *)(s->route_entry + addr - PCH_PIC_ROUTE_ENTRY); break; default: + qemu_log_mask(LOG_GUEST_ERROR, + "pch_pic_read: Bad address 0x%"PRIx64"\n", addr); break; } - trace_loongarch_pch_pic_low_readw(size, addr, val); - return val; + return (val >> (offset * 8)) & field_mask; } -static uint64_t get_writew_val(uint64_t value, uint32_t target, bool hi) +static void pch_pic_write(void *opaque, hwaddr addr, uint64_t value, + uint64_t field_mask) { - uint64_t mask = 0xffffffff00000000; - uint64_t data = target; - - return hi ? (value & ~mask) | (data << 32) : (value & mask) | data; -} - -static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); - uint32_t offset, old_valid, data = (uint32_t)value; - uint64_t old, int_mask; - offset = addr & 0xfff; - - trace_loongarch_pch_pic_low_writew(size, addr, data); - - switch (offset) { - case PCH_PIC_INT_MASK_LO: - old = s->int_mask; - s->int_mask = get_writew_val(old, data, 0); - old_valid = (uint32_t)old; - if (old_valid & ~data) { - pch_pic_update_irq(s, (old_valid & ~data), 1); - } - if (~old_valid & data) { - pch_pic_update_irq(s, (~old_valid & data), 0); - } - break; - case PCH_PIC_INT_MASK_HI: + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); + uint32_t offset; + uint64_t old, mask, data, *ptemp; + + offset = addr & 7; + addr -= offset; + mask = field_mask << (offset * 8); + data = (value & field_mask) << (offset * 8); + switch (addr) { + case PCH_PIC_INT_MASK: old = s->int_mask; - s->int_mask = get_writew_val(old, data, 1); - old_valid = (uint32_t)(old >> 32); - int_mask = old_valid & ~data; - if (int_mask) { - pch_pic_update_irq(s, int_mask << 32, 1); + s->int_mask = (old & ~mask) | data; + if (old & ~data) { + pch_pic_update_irq(s, old & ~data, 1); } - int_mask = ~old_valid & data; - if (int_mask) { - pch_pic_update_irq(s, int_mask << 32, 0); + + if (~old & data) { + pch_pic_update_irq(s, ~old & data, 0); } break; - case PCH_PIC_INT_EDGE_LO: - s->intedge = get_writew_val(s->intedge, data, 0); - break; - case PCH_PIC_INT_EDGE_HI: - s->intedge = get_writew_val(s->intedge, data, 1); + case PCH_PIC_INT_EDGE: + s->intedge = (s->intedge & ~mask) | data; break; - case PCH_PIC_INT_CLEAR_LO: + case PCH_PIC_INT_CLEAR: if (s->intedge & data) { - s->intirr &= (~data); + s->intirr &= ~data; pch_pic_update_irq(s, data, 0); - s->intisr &= (~data); + s->intisr &= ~data; } break; - case PCH_PIC_INT_CLEAR_HI: - value <<= 32; - if (s->intedge & value) { - s->intirr &= (~value); - pch_pic_update_irq(s, value, 0); - s->intisr &= (~value); - } + case PCH_PIC_HTMSI_EN: + s->htmsi_en = (s->htmsi_en & ~mask) | data; break; - case PCH_PIC_HTMSI_EN_LO: - s->htmsi_en = get_writew_val(s->htmsi_en, data, 0); + case PCH_PIC_AUTO_CTRL0: + case PCH_PIC_AUTO_CTRL1: + /* Discard auto_ctrl access */ break; - case PCH_PIC_HTMSI_EN_HI: - s->htmsi_en = get_writew_val(s->htmsi_en, data, 1); + case PCH_PIC_INT_POL: + s->int_polarity = (s->int_polarity & ~mask) | data; break; - case PCH_PIC_AUTO_CTRL0_LO: - case PCH_PIC_AUTO_CTRL0_HI: - case PCH_PIC_AUTO_CTRL1_LO: - case PCH_PIC_AUTO_CTRL1_HI: + case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END: + ptemp = (uint64_t *)(s->htmsi_vector + addr - PCH_PIC_HTMSI_VEC); + *ptemp = (*ptemp & ~mask) | data; + break; + case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END: + ptemp = (uint64_t *)(s->route_entry + addr - PCH_PIC_ROUTE_ENTRY); + *ptemp = (*ptemp & ~mask) | data; break; default: + qemu_log_mask(LOG_GUEST_ERROR, + "pch_pic_write: Bad address 0x%"PRIx64"\n", addr); break; } } -static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr, - unsigned size) +static uint64_t loongarch_pch_pic_read(void *opaque, hwaddr addr, + unsigned size) { - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); uint64_t val = 0; - uint32_t offset = addr & 0xfff; - switch (offset) { - case STATUS_LO_START: - val = (uint32_t)(s->intisr & (~s->int_mask)); + switch (size) { + case 1: + val = pch_pic_read(opaque, addr, UCHAR_MAX); break; - case STATUS_HI_START: - val = (s->intisr & (~s->int_mask)) >> 32; + case 2: + val = pch_pic_read(opaque, addr, USHRT_MAX); break; - case POL_LO_START: - val = (uint32_t)s->int_polarity; + case 4: + val = pch_pic_read(opaque, addr, UINT_MAX); break; - case POL_HI_START: - val = s->int_polarity >> 32; + case 8: + val = pch_pic_read(opaque, addr, UINT64_MAX); break; default: + qemu_log_mask(LOG_GUEST_ERROR, + "loongarch_pch_pic_read: Bad size %d\n", size); break; } - trace_loongarch_pch_pic_high_readw(size, addr, val); + trace_loongarch_pch_pic_read(size, addr, val); return val; } -static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr, - uint64_t value, unsigned size) +static void loongarch_pch_pic_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) { - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); - uint32_t offset, data = (uint32_t)value; - offset = addr & 0xfff; - - trace_loongarch_pch_pic_high_writew(size, addr, data); + trace_loongarch_pch_pic_write(size, addr, value); - switch (offset) { - case STATUS_LO_START: - s->intisr = get_writew_val(s->intisr, data, 0); + switch (size) { + case 1: + pch_pic_write(opaque, addr, value, UCHAR_MAX); break; - case STATUS_HI_START: - s->intisr = get_writew_val(s->intisr, data, 1); + case 2: + pch_pic_write(opaque, addr, value, USHRT_MAX); break; - case POL_LO_START: - s->int_polarity = get_writew_val(s->int_polarity, data, 0); break; - case POL_HI_START: - s->int_polarity = get_writew_val(s->int_polarity, data, 1); - break; - default: - break; - } -} - -static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr, - unsigned size) -{ - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); - uint64_t val = 0; - uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET; - int64_t offset_tmp; - - switch (offset) { - case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END: - offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; - if (offset_tmp >= 0 && offset_tmp < 64) { - val = s->htmsi_vector[offset_tmp]; - } - break; - case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END: - offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET; - if (offset_tmp >= 0 && offset_tmp < 64) { - val = s->route_entry[offset_tmp]; - } - break; - default: - break; - } - - trace_loongarch_pch_pic_readb(size, addr, val); - return val; -} - -static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); - int32_t offset_tmp; - uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET; - - trace_loongarch_pch_pic_writeb(size, addr, data); - - switch (offset) { - case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END: - offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; - if (offset_tmp >= 0 && offset_tmp < 64) { - s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff); - } + case 4: + pch_pic_write(opaque, addr, value, UINT_MAX); break; - case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END: - offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET; - if (offset_tmp >= 0 && offset_tmp < 64) { - s->route_entry[offset_tmp] = (uint8_t)(data & 0xff); - } + case 8: + pch_pic_write(opaque, addr, value, UINT64_MAX); break; default: + qemu_log_mask(LOG_GUEST_ERROR, + "loongarch_pch_pic_write: Bad size %d\n", size); break; } } -static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops = { - .read = loongarch_pch_pic_low_readw, - .write = loongarch_pch_pic_low_writew, - .valid = { - .min_access_size = 4, - .max_access_size = 8, - }, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops = { - .read = loongarch_pch_pic_high_readw, - .write = loongarch_pch_pic_high_writew, - .valid = { - .min_access_size = 4, - .max_access_size = 8, - }, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps loongarch_pch_pic_reg8_ops = { - .read = loongarch_pch_pic_readb, - .write = loongarch_pch_pic_writeb, +static const MemoryRegionOps loongarch_pch_pic_ops = { + .read = loongarch_pch_pic_read, + .write = loongarch_pch_pic_write, .valid = { .min_access_size = 1, - .max_access_size = 1, + .max_access_size = 8, + /* + * PCH PIC 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. + */ + .unaligned = false, }, .impl = { .min_access_size = 1, - .max_access_size = 1, + .max_access_size = 8, }, .endianness = DEVICE_LITTLE_ENDIAN, }; -static void loongarch_pch_pic_reset(DeviceState *d) +static void loongarch_pic_reset_hold(Object *obj, ResetType type) { - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(d); - int i; - - s->int_mask = -1; - s->htmsi_en = 0x0; - s->intedge = 0x0; - s->intclr = 0x0; - s->auto_crtl0 = 0x0; - s->auto_crtl1 = 0x0; - for (i = 0; i < 64; i++) { - s->route_entry[i] = 0x1; - s->htmsi_vector[i] = 0x0; + LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(obj); + + if (lpc->parent_phases.hold) { + lpc->parent_phases.hold(obj, type); + } + + if (kvm_irqchip_in_kernel()) { + kvm_pic_put(obj, 0); } - s->intirr = 0x0; - s->intisr = 0x0; - s->last_intirr = 0x0; - s->int_polarity = 0x0; } -static void loongarch_pch_pic_realize(DeviceState *dev, Error **errp) +static void loongarch_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'"); + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(dev); + LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Error *local_err = NULL; + + lpc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } qdev_init_gpio_out(dev, s->parent_irq, s->irq_num); qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num); + + if (kvm_irqchip_in_kernel()) { + kvm_pic_realize(dev, errp); + } else { + memory_region_init_io(&s->iomem, OBJECT(dev), + &loongarch_pch_pic_ops, + s, TYPE_LOONGARCH_PIC, VIRT_PCH_REG_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + } } -static void loongarch_pch_pic_init(Object *obj) +static int loongarch_pic_pre_save(LoongArchPICCommonState *opaque) { - LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - memory_region_init_io(&s->iomem32_low, obj, - &loongarch_pch_pic_reg32_low_ops, - s, PCH_PIC_NAME(.reg32_part1), 0x100); - memory_region_init_io(&s->iomem8, obj, &loongarch_pch_pic_reg8_ops, - s, PCH_PIC_NAME(.reg8), 0x2a0); - memory_region_init_io(&s->iomem32_high, obj, - &loongarch_pch_pic_reg32_high_ops, - s, PCH_PIC_NAME(.reg32_part2), 0xc60); - sysbus_init_mmio(sbd, &s->iomem32_low); - sysbus_init_mmio(sbd, &s->iomem8); - sysbus_init_mmio(sbd, &s->iomem32_high); + if (kvm_irqchip_in_kernel()) { + return kvm_pic_get(opaque); + } + return 0; } -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[]) { - VMSTATE_UINT64(int_mask, LoongArchPCHPIC), - VMSTATE_UINT64(htmsi_en, LoongArchPCHPIC), - VMSTATE_UINT64(intedge, LoongArchPCHPIC), - VMSTATE_UINT64(intclr, LoongArchPCHPIC), - VMSTATE_UINT64(auto_crtl0, LoongArchPCHPIC), - VMSTATE_UINT64(auto_crtl1, LoongArchPCHPIC), - VMSTATE_UINT8_ARRAY(route_entry, LoongArchPCHPIC, 64), - VMSTATE_UINT8_ARRAY(htmsi_vector, LoongArchPCHPIC, 64), - VMSTATE_UINT64(last_intirr, LoongArchPCHPIC), - VMSTATE_UINT64(intirr, LoongArchPCHPIC), - VMSTATE_UINT64(intisr, LoongArchPCHPIC), - VMSTATE_UINT64(int_polarity, LoongArchPCHPIC), - VMSTATE_END_OF_LIST() +static int loongarch_pic_post_load(LoongArchPICCommonState *opaque, + int version_id) +{ + if (kvm_irqchip_in_kernel()) { + return kvm_pic_put(opaque, version_id); } -}; -static void loongarch_pch_pic_class_init(ObjectClass *klass, void *data) + return 0; +} + +static void loongarch_pic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = loongarch_pch_pic_realize; - dc->reset = loongarch_pch_pic_reset; - dc->vmsd = &vmstate_loongarch_pch_pic; - device_class_set_props(dc, loongarch_pch_pic_properties); + LoongarchPICClass *lpc = LOONGARCH_PIC_CLASS(klass); + LoongArchPICCommonClass *lpcc = LOONGARCH_PIC_COMMON_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + resettable_class_set_parent_phases(rc, NULL, loongarch_pic_reset_hold, + NULL, &lpc->parent_phases); + device_class_set_parent_realize(dc, loongarch_pic_realize, + &lpc->parent_realize); + lpcc->pre_save = loongarch_pic_pre_save; + lpcc->post_load = loongarch_pic_post_load; } -static const TypeInfo loongarch_pch_pic_info = { - .name = TYPE_LOONGARCH_PCH_PIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(LoongArchPCHPIC), - .instance_init = loongarch_pch_pic_init, - .class_init = loongarch_pch_pic_class_init, +static const TypeInfo loongarch_pic_types[] = { + { + .name = TYPE_LOONGARCH_PIC, + .parent = TYPE_LOONGARCH_PIC_COMMON, + .instance_size = sizeof(LoongarchPICState), + .class_size = sizeof(LoongarchPICClass), + .class_init = loongarch_pic_class_init, + } }; -static void loongarch_pch_pic_register_types(void) -{ - type_register_static(&loongarch_pch_pic_info); -} - -type_init(loongarch_pch_pic_register_types) +DEFINE_TYPES(loongarch_pic_types) diff --git a/hw/intc/loongarch_pic_common.c b/hw/intc/loongarch_pic_common.c new file mode 100644 index 00000000000..de170501cf2 --- /dev/null +++ b/hw/intc/loongarch_pic_common.c @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU Loongson 7A1000 I/O interrupt controller. + * Copyright (C) 2024 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/intc/loongarch_pic_common.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" + +static int loongarch_pic_pre_save(void *opaque) +{ + LoongArchPICCommonState *s = (LoongArchPICCommonState *)opaque; + LoongArchPICCommonClass *lpcc = LOONGARCH_PIC_COMMON_GET_CLASS(s); + + if (lpcc->pre_save) { + return lpcc->pre_save(s); + } + + return 0; +} + +static int loongarch_pic_post_load(void *opaque, int version_id) +{ + LoongArchPICCommonState *s = (LoongArchPICCommonState *)opaque; + LoongArchPICCommonClass *lpcc = LOONGARCH_PIC_COMMON_GET_CLASS(s); + + if (lpcc->post_load) { + return lpcc->post_load(s, version_id); + } + + return 0; +} + +static void loongarch_pic_common_realize(DeviceState *dev, Error **errp) +{ + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(dev); + + if (!s->irq_num || s->irq_num > VIRT_PCH_PIC_IRQ_NUM) { + error_setg(errp, "Invalid 'pic_irq_num'"); + return; + } +} + +static void loongarch_pic_common_reset_hold(Object *obj, ResetType type) +{ + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(obj); + int i; + + /* + * With Loongson 7A1000 user manual + * Chapter 5.2 "Description of Interrupt-related Registers" + * + * Interrupt controller identification register 1 + * Bit 24-31 Interrupt Controller ID + * Interrupt controller identification register 2 + * Bit 0-7 Interrupt Controller version number + * Bit 16-23 The number of interrupt sources supported + */ + s->id.desc.id = PCH_PIC_INT_ID_VAL; + s->id.desc.version = PCH_PIC_INT_ID_VER; + s->id.desc.irq_num = s->irq_num - 1; + s->int_mask = UINT64_MAX; + s->htmsi_en = 0x0; + s->intedge = 0x0; + s->intclr = 0x0; + s->auto_crtl0 = 0x0; + s->auto_crtl1 = 0x0; + for (i = 0; i < 64; i++) { + s->route_entry[i] = 0x1; + s->htmsi_vector[i] = 0x0; + } + s->intirr = 0x0; + s->intisr = 0x0; + s->last_intirr = 0x0; + s->int_polarity = 0x0; +} + +static const Property loongarch_pic_common_properties[] = { + DEFINE_PROP_UINT32("pch_pic_irq_num", LoongArchPICCommonState, irq_num, 0), +}; + +static const VMStateDescription vmstate_loongarch_pic_common = { + .name = "loongarch_pch_pic", + .version_id = 1, + .minimum_version_id = 1, + .pre_save = loongarch_pic_pre_save, + .post_load = loongarch_pic_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(int_mask, LoongArchPICCommonState), + VMSTATE_UINT64(htmsi_en, LoongArchPICCommonState), + VMSTATE_UINT64(intedge, LoongArchPICCommonState), + VMSTATE_UINT64(intclr, LoongArchPICCommonState), + VMSTATE_UINT64(auto_crtl0, LoongArchPICCommonState), + VMSTATE_UINT64(auto_crtl1, LoongArchPICCommonState), + VMSTATE_UINT8_ARRAY(route_entry, LoongArchPICCommonState, 64), + VMSTATE_UINT8_ARRAY(htmsi_vector, LoongArchPICCommonState, 64), + VMSTATE_UINT64(last_intirr, LoongArchPICCommonState), + VMSTATE_UINT64(intirr, LoongArchPICCommonState), + VMSTATE_UINT64(intisr, LoongArchPICCommonState), + VMSTATE_UINT64(int_polarity, LoongArchPICCommonState), + VMSTATE_END_OF_LIST() + } +}; + +static void loongarch_pic_common_class_init(ObjectClass *klass, + const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + LoongArchPICCommonClass *lpcc = LOONGARCH_PIC_COMMON_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + device_class_set_parent_realize(dc, loongarch_pic_common_realize, + &lpcc->parent_realize); + resettable_class_set_parent_phases(rc, NULL, + loongarch_pic_common_reset_hold, + NULL, &lpcc->parent_phases); + device_class_set_props(dc, loongarch_pic_common_properties); + dc->vmsd = &vmstate_loongarch_pic_common; +} + +static const TypeInfo loongarch_pic_common_types[] = { + { + .name = TYPE_LOONGARCH_PIC_COMMON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LoongArchPICCommonState), + .class_size = sizeof(LoongArchPICCommonClass), + .class_init = loongarch_pic_common_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(loongarch_pic_common_types) diff --git a/hw/intc/loongarch_pic_kvm.c b/hw/intc/loongarch_pic_kvm.c new file mode 100644 index 00000000000..dd504ec6a6f --- /dev/null +++ b/hw/intc/loongarch_pic_kvm.c @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch kvm pch pic interrupt support + * + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "hw/intc/loongarch_pch_pic.h" +#include "hw/loongarch/virt.h" +#include "hw/pci-host/ls7a.h" +#include "system/kvm.h" + +static void kvm_pch_pic_access_reg(int fd, uint64_t addr, void *val, bool write) +{ + kvm_device_access(fd, KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS, + addr, val, write, &error_abort); +} + +static void kvm_pch_pic_access(void *opaque, bool write) +{ + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); + LoongarchPICState *lps = LOONGARCH_PIC(opaque); + int fd = lps->dev_fd; + int addr, offset; + + if (fd == 0) { + return; + } + + kvm_pch_pic_access_reg(fd, PCH_PIC_INT_MASK, &s->int_mask, write); + kvm_pch_pic_access_reg(fd, PCH_PIC_HTMSI_EN, &s->htmsi_en, write); + kvm_pch_pic_access_reg(fd, PCH_PIC_INT_EDGE, &s->intedge, write); + kvm_pch_pic_access_reg(fd, PCH_PIC_AUTO_CTRL0, &s->auto_crtl0, write); + kvm_pch_pic_access_reg(fd, PCH_PIC_AUTO_CTRL1, &s->auto_crtl1, write); + + for (addr = PCH_PIC_ROUTE_ENTRY; + addr < PCH_PIC_ROUTE_ENTRY_END; addr++) { + offset = addr - PCH_PIC_ROUTE_ENTRY; + kvm_pch_pic_access_reg(fd, addr, &s->route_entry[offset], write); + } + + for (addr = PCH_PIC_HTMSI_VEC; addr < PCH_PIC_HTMSI_VEC_END; addr++) { + offset = addr - PCH_PIC_HTMSI_VEC; + kvm_pch_pic_access_reg(fd, addr, &s->htmsi_vector[offset], write); + } + + kvm_pch_pic_access_reg(fd, PCH_PIC_INT_REQUEST, &s->intirr, write); + kvm_pch_pic_access_reg(fd, PCH_PIC_INT_STATUS, &s->intisr, write); + kvm_pch_pic_access_reg(fd, PCH_PIC_INT_POL, &s->int_polarity, write); +} + +int kvm_pic_get(void *opaque) +{ + kvm_pch_pic_access(opaque, false); + return 0; +} + +int kvm_pic_put(void *opaque, int version_id) +{ + kvm_pch_pic_access(opaque, true); + return 0; +} + +void kvm_pic_realize(DeviceState *dev, Error **errp) +{ + LoongarchPICState *lps = LOONGARCH_PIC(dev); + uint64_t pch_pic_base = VIRT_PCH_REG_BASE; + int ret; + + ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_LOONGARCH_PCHPIC, false); + if (ret < 0) { + fprintf(stderr, "Create KVM_LOONGARCH_PCHPIC failed: %s\n", + strerror(-ret)); + abort(); + } + + lps->dev_fd = ret; + ret = kvm_device_access(lps->dev_fd, KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL, + KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT, + &pch_pic_base, true, NULL); + if (ret < 0) { + fprintf(stderr, "KVM_LOONGARCH_PCH_PIC_INIT failed: %s\n", + strerror(-ret)); + abort(); + } +} diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c new file mode 100644 index 00000000000..fbc73e8b001 --- /dev/null +++ b/hw/intc/loongson_ipi.c @@ -0,0 +1,129 @@ +/* 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 "hw/qdev-properties.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 int loongson_cpu_by_arch_id(LoongsonIPICommonState *lics, + int64_t arch_id, int *index, CPUState **pcs) +{ + CPUState *cs; + + cs = cpu_by_arch_id(arch_id); + if (cs == NULL) { + return MEMTX_ERROR; + } + + if (index) { + *index = cs->cpu_index; + } + + if (pcs) { + *pcs = cs; + } + + return MEMTX_OK; +} + +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; + int i; + + lic->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (sc->num_cpu == 0) { + error_setg(errp, "num-cpu must be at least 1"); + return; + } + + sc->cpu = g_new0(IPICore, sc->num_cpu); + for (i = 0; i < sc->num_cpu; i++) { + sc->cpu[i].ipi = sc; + qdev_init_gpio_out(dev, &sc->cpu[i].irq, 1); + } + + s->ipi_mmio_mem = g_new0(MemoryRegion, sc->num_cpu); + for (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 const Property loongson_ipi_properties[] = { + DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1), +}; + +static void loongson_ipi_class_init(ObjectClass *klass, const 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); + device_class_set_props(dc, loongson_ipi_properties); + licc->get_iocsr_as = get_iocsr_as; + licc->cpu_by_arch_id = loongson_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 00000000000..8cd78d48589 --- /dev/null +++ b/hw/intc/loongson_ipi_common.c @@ -0,0 +1,362 @@ +/* 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 "qemu/log.h" +#include "migration/vmstate.h" +#include "system/kvm.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; + int cpu, ret; + + cpuid = extract32(val, 16, 10); + ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs); + if (ret != MEMTX_OK) { + return MEMTX_DECODE_ERROR; + } + + /* override requester_id */ + addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c); + attrs.requester_id = cpu; + 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; + int cpu, ret; + + cpuid = extract32(val, 16, 10); + ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs); + if (ret != MEMTX_OK) { + return MEMTX_DECODE_ERROR; + } + + /* override requester_id */ + addr = val & 0xffff; + attrs.requester_id = cpu; + 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; + int cpu, ret; + + 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); + ret = licc->cpu_by_arch_id(ipi, cpuid, &cpu, &cs); + if (ret != MEMTX_OK || cpu >= ipi->num_cpu) { + return MEMTX_DECODE_ERROR; + } + loongson_ipi_core_writel(&ipi->cpu[cpu], 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); + + if (kvm_irqchip_in_kernel()) { + 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); +} + +static void loongson_ipi_common_unrealize(DeviceState *dev) +{ + LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev); + + g_free(s->cpu); +} + +static int loongson_ipi_common_pre_save(void *opaque) +{ + IPICore *ipicore = (IPICore *)opaque; + LoongsonIPICommonState *s = ipicore->ipi; + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(s); + + if (licc->pre_save) { + return licc->pre_save(s); + } + + return 0; +} + +static int loongson_ipi_common_post_load(void *opaque, int version_id) +{ + IPICore *ipicore = (IPICore *)opaque; + LoongsonIPICommonState *s = ipicore->ipi; + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(s); + + if (licc->post_load) { + return licc->post_load(s, version_id); + } + + return 0; +} + +static const VMStateDescription vmstate_ipi_core = { + .name = "ipi-single", + .version_id = 2, + .minimum_version_id = 2, + .pre_save = loongson_ipi_common_pre_save, + .post_load = loongson_ipi_common_post_load, + .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 void loongson_ipi_common_class_init(ObjectClass *klass, const 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); + 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 cc11b544cb4..c10fb97a063 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 0c515e4ecb7..2532322618a 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,26 @@ 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 void m68k_irqc_class_init(ObjectClass *oc, void *data) +static const Property m68k_irqc_properties[] = { + DEFINE_PROP_LINK("m68k-cpu", M68KIRQCState, cpu, + TYPE_M68K_CPU, ArchCPU *), +}; + +static void m68k_irqc_class_init(ObjectClass *oc, const 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; @@ -104,7 +110,7 @@ static const TypeInfo m68k_irqc_type_info = { .instance_size = sizeof(M68KIRQCState), .instance_init = m68k_irqc_instance_init, .class_init = m68k_irqc_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NMI }, { TYPE_INTERRUPT_STATS_PROVIDER }, { } diff --git a/hw/intc/meson.build b/hw/intc/meson.build index ed355941d17..3137521a4ad 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -6,7 +6,7 @@ system_ss.add(when: 'CONFIG_ARM_GIC', if_true: files( 'arm_gicv3_common.c', 'arm_gicv3_its_common.c', )) -system_ss.add(when: 'CONFIG_ARM_GICV3_TCG', if_true: files( +system_ss.add(when: 'CONFIG_ARM_GICV3', if_true: files( 'arm_gicv3.c', 'arm_gicv3_dist.c', 'arm_gicv3_its.c', @@ -14,13 +14,13 @@ system_ss.add(when: 'CONFIG_ARM_GICV3_TCG', if_true: files( )) system_ss.add(when: 'CONFIG_ALLWINNER_A10_PIC', if_true: files('allwinner-a10-pic.c')) system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_vic.c')) -system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_pic.c')) +system_ss.add(when: 'CONFIG_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')) +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')) @@ -39,7 +39,7 @@ endif 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_GICV3', 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')) @@ -68,8 +68,15 @@ 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_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_KVM', 'CONFIG_LOONGARCH_IPI'], + if_true: files('loongarch_ipi_kvm.c')) +specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c', 'loongarch_pic_common.c')) +specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_LOONGARCH_PCH_PIC'], + if_true: files('loongarch_pic_kvm.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c')) -specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c')) +specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c', 'loongarch_extioi_common.c')) +specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_LOONGARCH_EXTIOI'], + if_true: files('loongarch_extioi_kvm.c')) diff --git a/hw/intc/mips_gic.c b/hw/intc/mips_gic.c index 4bdc3b1bd14..0c50ba41f62 100644 --- a/hw/intc/mips_gic.c +++ b/hw/intc/mips_gic.c @@ -14,9 +14,9 @@ #include "qemu/module.h" #include "qapi/error.h" #include "hw/sysbus.h" -#include "exec/memory.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" +#include "system/memory.h" +#include "system/kvm.h" +#include "system/reset.h" #include "kvm_mips.h" #include "hw/intc/mips_gic.h" #include "hw/irq.h" @@ -255,7 +255,6 @@ static void gic_write_vp(MIPSGICState *gic, uint32_t vp_index, hwaddr addr, return; bad_offset: qemu_log_mask(LOG_GUEST_ERROR, "Wrong GIC offset at 0x%" PRIx64 "\n", addr); - return; } static void gic_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) @@ -423,7 +422,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, @@ -438,13 +437,12 @@ static void mips_gic_realize(DeviceState *dev, Error **errp) } } -static Property mips_gic_properties[] = { +static const Property mips_gic_properties[] = { DEFINE_PROP_UINT32("num-vp", MIPSGICState, num_vps, 1), DEFINE_PROP_UINT32("num-irq", MIPSGICState, num_irq, 256), - DEFINE_PROP_END_OF_LIST(), }; -static void mips_gic_class_init(ObjectClass *klass, void *data) +static void mips_gic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/nios2_vic.c b/hw/intc/nios2_vic.c deleted file mode 100644 index cf63212a886..00000000000 --- 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 647bf324a8e..c61158bddd3 100644 --- a/hw/intc/omap_intc.c +++ b/hw/intc/omap_intc.c @@ -50,8 +50,6 @@ struct OMAPIntcState { int level_only; uint32_t size; - uint8_t revision; - /* state */ uint32_t new_agr[2]; int sir_intr[2]; @@ -68,7 +66,7 @@ static void omap_inth_sir_update(OMAPIntcState *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) { @@ -104,8 +102,8 @@ static inline void omap_inth_update(OMAPIntcState *s, int is_fiq) } } -#define INT_FALLING_EDGE 0 -#define INT_LOW_LEVEL 1 +#define INT_FALLING_EDGE 0 +#define INT_LOW_LEVEL 1 static void omap_set_intr(void *opaque, int irq, int req) { @@ -133,26 +131,6 @@ 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) -{ - OMAPIntcState *ih = 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) { @@ -164,13 +142,13 @@ static uint64_t omap_inth_read(void *opaque, hwaddr addr, offset &= 0xff; switch (offset) { - case 0x00: /* ITR */ + case 0x00: /* ITR */ return bank->irqs; - case 0x04: /* MIR */ + case 0x04: /* MIR */ return bank->mask; - case 0x10: /* SIR_IRQ_CODE */ + case 0x10: /* SIR_IRQ_CODE */ case 0x14: /* SIR_FIQ_CODE */ if (bank_no != 0) break; @@ -181,49 +159,49 @@ static uint64_t omap_inth_read(void *opaque, hwaddr addr, bank->irqs &= ~(1 << i); return line_no; - case 0x18: /* CONTROL_REG */ + case 0x18: /* CONTROL_REG */ if (bank_no != 0) break; return 0; - case 0x1c: /* ILR0 */ - case 0x20: /* ILR1 */ - case 0x24: /* ILR2 */ - case 0x28: /* ILR3 */ - case 0x2c: /* ILR4 */ - case 0x30: /* ILR5 */ - case 0x34: /* ILR6 */ - case 0x38: /* ILR7 */ - case 0x3c: /* ILR8 */ - case 0x40: /* ILR9 */ - case 0x44: /* ILR10 */ - case 0x48: /* ILR11 */ - case 0x4c: /* ILR12 */ - case 0x50: /* ILR13 */ - case 0x54: /* ILR14 */ - case 0x58: /* ILR15 */ - case 0x5c: /* ILR16 */ - case 0x60: /* ILR17 */ - case 0x64: /* ILR18 */ - case 0x68: /* ILR19 */ - case 0x6c: /* ILR20 */ - case 0x70: /* ILR21 */ - case 0x74: /* ILR22 */ - case 0x78: /* ILR23 */ - case 0x7c: /* ILR24 */ - case 0x80: /* ILR25 */ - case 0x84: /* ILR26 */ - case 0x88: /* ILR27 */ - case 0x8c: /* ILR28 */ - case 0x90: /* ILR29 */ - case 0x94: /* ILR30 */ - case 0x98: /* ILR31 */ + case 0x1c: /* ILR0 */ + case 0x20: /* ILR1 */ + case 0x24: /* ILR2 */ + case 0x28: /* ILR3 */ + case 0x2c: /* ILR4 */ + case 0x30: /* ILR5 */ + case 0x34: /* ILR6 */ + case 0x38: /* ILR7 */ + case 0x3c: /* ILR8 */ + case 0x40: /* ILR9 */ + case 0x44: /* ILR10 */ + case 0x48: /* ILR11 */ + case 0x4c: /* ILR12 */ + case 0x50: /* ILR13 */ + case 0x54: /* ILR14 */ + case 0x58: /* ILR15 */ + case 0x5c: /* ILR16 */ + case 0x60: /* ILR17 */ + case 0x64: /* ILR18 */ + case 0x68: /* ILR19 */ + case 0x6c: /* ILR20 */ + case 0x70: /* ILR21 */ + case 0x74: /* ILR22 */ + case 0x78: /* ILR23 */ + case 0x7c: /* ILR24 */ + case 0x80: /* ILR25 */ + case 0x84: /* ILR26 */ + case 0x88: /* ILR27 */ + case 0x8c: /* ILR28 */ + case 0x90: /* ILR29 */ + case 0x94: /* ILR30 */ + case 0x98: /* ILR31 */ i = (offset - 0x1c) >> 2; return (bank->priority[i] << 2) | (((bank->sens_edge >> i) & 1) << 1) | ((bank->fiq >> i) & 1); - case 0x9c: /* ISR */ + case 0x9c: /* ISR */ return 0x00000000; } @@ -241,24 +219,24 @@ static void omap_inth_write(void *opaque, hwaddr addr, offset &= 0xff; switch (offset) { - case 0x00: /* ITR */ + case 0x00: /* ITR */ /* Important: ignore the clearing if the IRQ is level-triggered and the input bit is 1 */ bank->irqs &= value | (bank->inputs & bank->sens_edge); return; - case 0x04: /* MIR */ + case 0x04: /* MIR */ bank->mask = value; omap_inth_update(s, 0); omap_inth_update(s, 1); return; - case 0x10: /* SIR_IRQ_CODE */ - case 0x14: /* SIR_FIQ_CODE */ + case 0x10: /* SIR_IRQ_CODE */ + case 0x14: /* SIR_FIQ_CODE */ OMAP_RO_REG(addr); break; - case 0x18: /* CONTROL_REG */ + case 0x18: /* CONTROL_REG */ if (bank_no != 0) break; if (value & 2) { @@ -273,38 +251,38 @@ static void omap_inth_write(void *opaque, hwaddr addr, } return; - case 0x1c: /* ILR0 */ - case 0x20: /* ILR1 */ - case 0x24: /* ILR2 */ - case 0x28: /* ILR3 */ - case 0x2c: /* ILR4 */ - case 0x30: /* ILR5 */ - case 0x34: /* ILR6 */ - case 0x38: /* ILR7 */ - case 0x3c: /* ILR8 */ - case 0x40: /* ILR9 */ - case 0x44: /* ILR10 */ - case 0x48: /* ILR11 */ - case 0x4c: /* ILR12 */ - case 0x50: /* ILR13 */ - case 0x54: /* ILR14 */ - case 0x58: /* ILR15 */ - case 0x5c: /* ILR16 */ - case 0x60: /* ILR17 */ - case 0x64: /* ILR18 */ - case 0x68: /* ILR19 */ - case 0x6c: /* ILR20 */ - case 0x70: /* ILR21 */ - case 0x74: /* ILR22 */ - case 0x78: /* ILR23 */ - case 0x7c: /* ILR24 */ - case 0x80: /* ILR25 */ - case 0x84: /* ILR26 */ - case 0x88: /* ILR27 */ - case 0x8c: /* ILR28 */ - case 0x90: /* ILR29 */ - case 0x94: /* ILR30 */ - case 0x98: /* ILR31 */ + case 0x1c: /* ILR0 */ + case 0x20: /* ILR1 */ + case 0x24: /* ILR2 */ + case 0x28: /* ILR3 */ + case 0x2c: /* ILR4 */ + case 0x30: /* ILR5 */ + case 0x34: /* ILR6 */ + case 0x38: /* ILR7 */ + case 0x3c: /* ILR8 */ + case 0x40: /* ILR9 */ + case 0x44: /* ILR10 */ + case 0x48: /* ILR11 */ + case 0x4c: /* ILR12 */ + case 0x50: /* ILR13 */ + case 0x54: /* ILR14 */ + case 0x58: /* ILR15 */ + case 0x5c: /* ILR16 */ + case 0x60: /* ILR17 */ + case 0x64: /* ILR18 */ + case 0x68: /* ILR19 */ + case 0x6c: /* ILR20 */ + case 0x70: /* ILR21 */ + case 0x74: /* ILR22 */ + case 0x78: /* ILR23 */ + case 0x7c: /* ILR24 */ + case 0x80: /* ILR25 */ + case 0x84: /* ILR26 */ + case 0x88: /* ILR27 */ + case 0x8c: /* ILR28 */ + case 0x90: /* ILR29 */ + case 0x94: /* ILR30 */ + case 0x98: /* ILR31 */ i = (offset - 0x1c) >> 2; bank->priority[i] = (value >> 2) & 0x1f; bank->sens_edge &= ~(1 << i); @@ -313,7 +291,7 @@ static void omap_inth_write(void *opaque, hwaddr addr, bank->fiq |= (value & 1) << i; return; - case 0x9c: /* ISR */ + case 0x9c: /* ISR */ for (i = 0; i < 32; i ++) if (value & (1 << i)) { omap_set_intr(s, 32 * bank_no + i, 1); @@ -397,16 +375,15 @@ void omap_intc_set_fclk(OMAPIntcState *intc, omap_clk clk) intc->fclk = clk; } -static Property omap_intc_properties[] = { +static const Property omap_intc_properties[] = { DEFINE_PROP_UINT32("size", OMAPIntcState, size, 0x100), - DEFINE_PROP_END_OF_LIST(), }; -static void omap_intc_class_init(ObjectClass *klass, void *data) +static void omap_intc_class_init(ObjectClass *klass, const 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 +391,16 @@ static void omap_intc_class_init(ObjectClass *klass, void *data) } static const TypeInfo omap_intc_info = { - .name = "omap-intc", - .parent = TYPE_OMAP_INTC, - .instance_init = omap_intc_init, - .class_init = omap_intc_class_init, -}; - -static uint64_t omap2_inth_read(void *opaque, hwaddr addr, - unsigned size) -{ - OMAPIntcState *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) -{ - OMAPIntcState *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); - OMAPIntcState *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) -{ - OMAPIntcState *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", OMAPIntcState, - 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(OMAPIntcState), - .abstract = true, + .instance_init = omap_intc_init, + .class_init = omap_intc_class_init, }; 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 1f10314807d..047c367478d 100644 --- a/hw/intc/ompic.c +++ b/hw/intc/ompic.c @@ -13,7 +13,7 @@ #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "migration/vmstate.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qom/object.h" #define TYPE_OR1K_OMPIC "or1k-ompic" @@ -128,16 +128,15 @@ static void or1k_ompic_realize(DeviceState *dev, Error **errp) } } -static Property or1k_ompic_properties[] = { +static const Property or1k_ompic_properties[] = { DEFINE_PROP_UINT32("num-cpus", OR1KOMPICState, num_cpus, 1), - DEFINE_PROP_END_OF_LIST(), }; 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 +147,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), @@ -156,7 +155,7 @@ static const VMStateDescription vmstate_or1k_ompic = { } }; -static void or1k_ompic_class_init(ObjectClass *klass, void *data) +static void or1k_ompic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index c757adbe538..87733eb7c30 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; } @@ -1611,19 +1606,18 @@ static void openpic_realize(DeviceState *dev, Error **errp) qdev_init_gpio_in(dev, openpic_set_irq, opp->max_irq); } -static Property openpic_properties[] = { +static const Property openpic_properties[] = { DEFINE_PROP_UINT32("model", OpenPICState, model, OPENPIC_MODEL_FSL_MPIC_20), DEFINE_PROP_UINT32("nb_cpus", OpenPICState, nb_cpus, 1), - DEFINE_PROP_END_OF_LIST(), }; -static void openpic_class_init(ObjectClass *oc, void *data) +static void openpic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); 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 557dd0c2bf5..673ea9ca055 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -30,7 +30,7 @@ #include "hw/pci/msi.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" @@ -262,19 +262,18 @@ int kvm_openpic_connect_vcpu(DeviceState *d, CPUState *cs) kvm_arch_vcpu_id(cs)); } -static Property kvm_openpic_properties[] = { +static const Property kvm_openpic_properties[] = { DEFINE_PROP_UINT32("model", KVMOpenPICState, model, OPENPIC_MODEL_FSL_MPIC_20), - DEFINE_PROP_END_OF_LIST(), }; -static void kvm_openpic_class_init(ObjectClass *oc, void *data) +static void kvm_openpic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); 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 cd88443601e..838c21c4a02 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), @@ -273,11 +273,11 @@ static const VMStateDescription vmstate_pl190 = { } }; -static void pl190_class_init(ObjectClass *klass, void *data) +static void pl190_class_init(ObjectClass *klass, const 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 9b10e905195..935c0e4742f 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -1,10 +1,9 @@ /* * QEMU PowerPC XIVE interrupt controller model * - * Copyright (c) 2017-2019, IBM Corporation. + * Copyright (c) 2017-2024, IBM Corporation. * - * This code is licensed under the 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" @@ -12,10 +11,9 @@ #include "qemu/module.h" #include "qapi/error.h" #include "target/ppc/cpu.h" -#include "sysemu/cpus.h" -#include "sysemu/dma.h" -#include "sysemu/reset.h" -#include "monitor/monitor.h" +#include "system/cpus.h" +#include "system/dma.h" +#include "system/reset.h" #include "hw/ppc/fdt.h" #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_chip.h" @@ -210,7 +208,7 @@ static uint64_t pnv_xive_vst_addr_remote(PnvXive *xive, uint32_t type, return 0; } - remote_addr |= idx << xive->pc_shift; + remote_addr |= ((uint64_t)idx) << xive->pc_shift; vst_addr = address_space_ldq_be(&address_space_memory, remote_addr, MEMTXATTRS_UNSPECIFIED, &result); @@ -474,7 +472,7 @@ static bool pnv_xive_is_cpu_enabled(PnvXive *xive, PowerPCCPU *cpu) static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match) { PnvXive *xive = PNV_XIVE(xptr); @@ -501,7 +499,8 @@ static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format, * Check the thread context CAM lines and record matches. */ ring = xive_presenter_tctx_match(xptr, tctx, format, nvt_blk, - nvt_idx, cam_ignore, logic_serv); + nvt_idx, cam_ignore, + logic_serv); /* * Save the context and follow on to catch duplicates, that we * don't support yet. @@ -988,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)) { @@ -1831,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); @@ -1840,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); @@ -1858,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); } } } @@ -2059,17 +2059,16 @@ static int pnv_xive_dt_xscom(PnvXScomInterface *dev, void *fdt, return 0; } -static Property pnv_xive_properties[] = { +static const Property pnv_xive_properties[] = { DEFINE_PROP_UINT64("ic-bar", PnvXive, ic_base, 0), DEFINE_PROP_UINT64("vc-bar", PnvXive, vc_base, 0), DEFINE_PROP_UINT64("pc-bar", PnvXive, pc_base, 0), DEFINE_PROP_UINT64("tm-bar", PnvXive, tm_base, 0), /* The PnvChip id identifies the XIVE interrupt controller. */ DEFINE_PROP_LINK("chip", PnvXive, chip, TYPE_PNV_CHIP, PnvChip *), - DEFINE_PROP_END_OF_LIST(), }; -static void pnv_xive_class_init(ObjectClass *klass, void *data) +static void pnv_xive_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); @@ -2107,7 +2106,7 @@ static const TypeInfo pnv_xive_info = { .instance_size = sizeof(PnvXive), .class_init = pnv_xive_class_init, .class_size = sizeof(PnvXiveClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 4b8d0a5d812..ec8b0c68f1a 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -1,19 +1,17 @@ /* * QEMU PowerPC XIVE2 interrupt controller model (POWER10) * - * Copyright (c) 2019-2022, IBM Corporation. + * Copyright (c) 2019-2024, IBM Corporation. * - * This code is licensed under the 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 "qemu/log.h" #include "qapi/error.h" #include "target/ppc/cpu.h" -#include "sysemu/cpus.h" -#include "sysemu/dma.h" -#include "monitor/monitor.h" +#include "system/cpus.h" +#include "system/dma.h" #include "hw/ppc/fdt.h" #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_chip.h" @@ -25,7 +23,8 @@ #include "hw/ppc/xive2_regs.h" #include "hw/ppc/ppc.h" #include "hw/qdev-properties.h" -#include "sysemu/reset.h" +#include "system/reset.h" +#include "system/qtest.h" #include @@ -33,6 +32,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) */ @@ -46,16 +55,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 @@ -207,6 +216,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) { @@ -220,6 +243,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) { @@ -228,6 +256,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); } @@ -330,40 +364,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]); } } @@ -380,40 +489,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; + + 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, VST_NVP, blk, idx, nxc_watch)) { - xive2_error(xive, "VST: no NVP entry %x/%x !?", blk, idx); + 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]); } } @@ -463,7 +624,7 @@ static bool pnv_xive2_is_cpu_enabled(PnvXive2 *xive, PowerPCCPU *cpu) static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match) { PnvXive2 *xive = PNV_XIVE2(xptr); @@ -494,25 +655,38 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, logic_serv); } else { ring = xive2_presenter_tctx_match(xptr, tctx, format, nvt_blk, - nvt_idx, cam_ignore, - logic_serv); + nvt_idx, crowd, cam_ignore, + logic_serv); } - /* - * Save the context and follow on to catch duplicates, - * that we don't support yet. - */ if (ring != -1) { - if (match->tctx) { + /* + * For VP-specific match, finding more than one is a + * problem. For group notification, it's possible. + */ + if (!cam_ignore && match->tctx) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a " "thread context NVT %x/%x\n", nvt_blk, nvt_idx); - return false; + /* Should set a FIR if we ever model it */ + return -1; + } + /* + * For a group notification, we need to know if the + * match is precluded first by checking the current + * thread priority. If the interrupt can be delivered, + * we always notify the first match (for now). + */ + if (cam_ignore && + xive2_tm_irq_precluded(tctx, ring, priority)) { + match->precluded = true; + } else { + if (!match->tctx) { + match->ring = ring; + match->tctx = tctx; + } + count++; } - - match->ring = ring; - match->tctx = tctx; - count++; } } } @@ -531,6 +705,47 @@ static uint32_t pnv_xive2_presenter_get_config(XivePresenter *xptr) return cfg; } +static int pnv_xive2_broadcast(XivePresenter *xptr, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool ignore, uint8_t priority) +{ + PnvXive2 *xive = PNV_XIVE2(xptr); + PnvChip *chip = xive->chip; + int i, j; + bool gen1_tima_os = + xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS; + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pc = chip->cores[i]; + CPUCore *cc = CPU_CORE(pc); + + for (j = 0; j < cc->nr_threads; j++) { + PowerPCCPU *cpu = pc->threads[j]; + XiveTCTX *tctx; + int ring; + + if (!pnv_xive2_is_cpu_enabled(xive, cpu)) { + continue; + } + + tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc); + + if (gen1_tima_os) { + ring = xive_presenter_tctx_match(xptr, tctx, 0, nvt_blk, + nvt_idx, ignore, 0); + } else { + ring = xive2_presenter_tctx_match(xptr, tctx, 0, nvt_blk, + nvt_idx, crowd, ignore, 0); + } + + if (ring != -1) { + xive2_tm_set_lsmfb(tctx, ring, priority); + } + } + } + return 0; +} + static uint8_t pnv_xive2_get_block_id(Xive2Router *xrtr) { return pnv_xive2_block_id(PNV_XIVE2(xrtr)); @@ -582,6 +797,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: @@ -642,6 +858,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)); @@ -657,6 +876,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)); @@ -681,13 +903,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) { @@ -722,6 +941,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 */ @@ -965,12 +1194,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) { /* @@ -1001,24 +1288,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; @@ -1064,6 +1371,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) { /* @@ -1072,7 +1380,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; /* @@ -1084,6 +1392,10 @@ 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; @@ -1096,19 +1408,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; @@ -1117,6 +1446,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 */ @@ -1158,12 +1491,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) { /* @@ -1174,24 +1538,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; @@ -1215,36 +1599,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: */ @@ -1252,6 +1666,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 */ @@ -1548,13 +1966,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) @@ -1566,22 +1990,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); } } @@ -1728,21 +2202,40 @@ static const MemoryRegionOps pnv_xive2_tm_ops = { }, }; -static uint64_t pnv_xive2_nvc_read(void *opaque, hwaddr offset, +static uint64_t pnv_xive2_nvc_read(void *opaque, hwaddr addr, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); + XivePresenter *xptr = XIVE_PRESENTER(xive); + uint32_t page = addr >> xive->nvpg_shift; + uint16_t op = addr & 0xFFF; + uint8_t blk = pnv_xive2_block_id(xive); - xive2_error(xive, "NVC: invalid read @%"HWADDR_PRIx, offset); - return -1; + if (size != 2) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvc load size %d\n", + size); + return -1; + } + + return xive2_presenter_nvgc_backlog_op(xptr, true, blk, page, op, 1); } -static void pnv_xive2_nvc_write(void *opaque, hwaddr offset, +static void pnv_xive2_nvc_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); + XivePresenter *xptr = XIVE_PRESENTER(xive); + uint32_t page = addr >> xive->nvc_shift; + uint16_t op = addr & 0xFFF; + uint8_t blk = pnv_xive2_block_id(xive); + + if (size != 1) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvc write size %d\n", + size); + return; + } - xive2_error(xive, "NVC: invalid write @%"HWADDR_PRIx, offset); + (void)xive2_presenter_nvgc_backlog_op(xptr, true, blk, page, op, val); } static const MemoryRegionOps pnv_xive2_nvc_ops = { @@ -1750,30 +2243,63 @@ static const MemoryRegionOps pnv_xive2_nvc_ops = { .write = pnv_xive2_nvc_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, }, }; -static uint64_t pnv_xive2_nvpg_read(void *opaque, hwaddr offset, +static uint64_t pnv_xive2_nvpg_read(void *opaque, hwaddr addr, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); + XivePresenter *xptr = XIVE_PRESENTER(xive); + uint32_t page = addr >> xive->nvpg_shift; + uint16_t op = addr & 0xFFF; + uint32_t index = page >> 1; + uint8_t blk = pnv_xive2_block_id(xive); - xive2_error(xive, "NVPG: invalid read @%"HWADDR_PRIx, offset); - return -1; + if (size != 2) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvpg load size %d\n", + size); + return -1; + } + + if (page % 2) { + /* odd page - NVG */ + return xive2_presenter_nvgc_backlog_op(xptr, false, blk, index, op, 1); + } else { + /* even page - NVP */ + return xive2_presenter_nvp_backlog_op(xptr, blk, index, op); + } } -static void pnv_xive2_nvpg_write(void *opaque, hwaddr offset, +static void pnv_xive2_nvpg_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); + XivePresenter *xptr = XIVE_PRESENTER(xive); + uint32_t page = addr >> xive->nvpg_shift; + uint16_t op = addr & 0xFFF; + uint32_t index = page >> 1; + uint8_t blk = pnv_xive2_block_id(xive); - xive2_error(xive, "NVPG: invalid write @%"HWADDR_PRIx, offset); + if (size != 1) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvpg write size %d\n", + size); + return; + } + + if (page % 2) { + /* odd page - NVG */ + (void)xive2_presenter_nvgc_backlog_op(xptr, false, blk, index, op, val); + } else { + /* even page - NVP */ + (void)xive2_presenter_nvp_backlog_op(xptr, blk, index, op); + } } static const MemoryRegionOps pnv_xive2_nvpg_ops = { @@ -1781,11 +2307,11 @@ static const MemoryRegionOps pnv_xive2_nvpg_ops = { .write = pnv_xive2_nvpg_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, }, }; @@ -1815,6 +2341,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; @@ -1927,7 +2459,7 @@ static void pnv_xive2_realize(DeviceState *dev, Error **errp) qemu_register_reset(pnv_xive2_reset, dev); } -static Property pnv_xive2_properties[] = { +static const Property pnv_xive2_properties[] = { DEFINE_PROP_UINT64("ic-bar", PnvXive2, ic_base, 0), DEFINE_PROP_UINT64("esb-bar", PnvXive2, esb_base, 0), DEFINE_PROP_UINT64("end-bar", PnvXive2, end_base, 0), @@ -1939,7 +2471,6 @@ static Property pnv_xive2_properties[] = { DEFINE_PROP_UINT64("config", PnvXive2, config, PNV_XIVE2_CONFIGURATION), DEFINE_PROP_LINK("chip", PnvXive2, chip, TYPE_PNV_CHIP, PnvChip *), - DEFINE_PROP_END_OF_LIST(), }; static void pnv_xive2_instance_init(Object *obj) @@ -1974,7 +2505,7 @@ static int pnv_xive2_dt_xscom(PnvXScomInterface *dev, void *fdt, return 0; } -static void pnv_xive2_class_init(ObjectClass *klass, void *data) +static void pnv_xive2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); @@ -1997,6 +2528,8 @@ 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; @@ -2004,6 +2537,7 @@ static void pnv_xive2_class_init(ObjectClass *klass, void *data) xpc->match_nvt = pnv_xive2_match_nvt; xpc->get_config = pnv_xive2_presenter_get_config; + xpc->broadcast = pnv_xive2_broadcast; }; static const TypeInfo pnv_xive2_info = { @@ -2013,7 +2547,7 @@ static const TypeInfo pnv_xive2_info = { .instance_size = sizeof(PnvXive2), .class_init = pnv_xive2_class_init, .class_size = sizeof(PnvXive2Class), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } @@ -2026,33 +2560,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. @@ -2104,7 +2611,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); @@ -2114,42 +2621,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 7165dc87045..e8b87b3d2c1 100644 --- a/hw/intc/pnv_xive2_regs.h +++ b/hw/intc/pnv_xive2_regs.h @@ -232,6 +232,10 @@ #define VC_ESBC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(32, 35) #define VC_ESBC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(36, 63) /* 28-bit */ +/* ESBC 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 @@ -250,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 */ @@ -270,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 @@ -283,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 @@ -302,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 */ @@ -358,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 @@ -381,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 */ diff --git a/hw/intc/ppc-uic.c b/hw/intc/ppc-uic.c index dcf5de5d43c..bc4dc90ade1 100644 --- a/hw/intc/ppc-uic.c +++ b/hw/intc/ppc-uic.c @@ -259,17 +259,16 @@ static void ppc_uic_realize(DeviceState *dev, Error **errp) qdev_init_gpio_in(dev, ppcuic_set_irq, UIC_MAX_IRQ); } -static Property ppc_uic_properties[] = { +static const Property ppc_uic_properties[] = { DEFINE_PROP_UINT32("dcr-base", PPCUIC, dcr_base, 0xc0), DEFINE_PROP_BOOL("use-vectors", PPCUIC, use_vectors, true), - DEFINE_PROP_END_OF_LIST() }; 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), @@ -282,11 +281,11 @@ static const VMStateDescription ppc_uic_vmstate = { }, }; -static void ppc_uic_class_init(ObjectClass *klass, void *data) +static void ppc_uic_class_init(ObjectClass *klass, const 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/realview_gic.c b/hw/intc/realview_gic.c index 9b12116b2af..63e25c2a781 100644 --- a/hw/intc/realview_gic.c +++ b/hw/intc/realview_gic.c @@ -63,7 +63,7 @@ static void realview_gic_init(Object *obj) qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", 1); } -static void realview_gic_class_init(ObjectClass *oc, void *data) +static void realview_gic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c index 25cf7a5d9d8..b0139f03f59 100644 --- a/hw/intc/riscv_aclint.c +++ b/hw/intc/riscv_aclint.c @@ -131,7 +131,7 @@ static uint64_t riscv_aclint_mtimer_read(void *opaque, hwaddr addr, size_t hartid = mtimer->hartid_base + ((addr - mtimer->timecmp_base) >> 3); CPUState *cpu = cpu_by_arch_id(hartid); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; if (!env) { qemu_log_mask(LOG_GUEST_ERROR, "aclint-mtimer: invalid hartid: %zu", hartid); @@ -174,7 +174,7 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr, size_t hartid = mtimer->hartid_base + ((addr - mtimer->timecmp_base) >> 3); CPUState *cpu = cpu_by_arch_id(hartid); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; if (!env) { qemu_log_mask(LOG_GUEST_ERROR, "aclint-mtimer: invalid hartid: %zu", hartid); @@ -233,7 +233,7 @@ 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 = cpu_by_arch_id(mtimer->hartid_base + i); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; if (!env) { continue; } @@ -262,7 +262,7 @@ static const MemoryRegionOps riscv_aclint_mtimer_ops = { } }; -static Property riscv_aclint_mtimer_properties[] = { +static const Property riscv_aclint_mtimer_properties[] = { DEFINE_PROP_UINT32("hartid-base", RISCVAclintMTimerState, hartid_base, 0), DEFINE_PROP_UINT32("num-harts", RISCVAclintMTimerState, num_harts, 1), @@ -274,7 +274,6 @@ static Property riscv_aclint_mtimer_properties[] = { aperture_size, RISCV_ACLINT_DEFAULT_MTIMER_SIZE), DEFINE_PROP_UINT32("timebase-freq", RISCVAclintMTimerState, timebase_freq, 0), - DEFINE_PROP_END_OF_LIST(), }; static void riscv_aclint_mtimer_realize(DeviceState *dev, Error **errp) @@ -321,7 +320,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), @@ -329,7 +328,7 @@ static const VMStateDescription vmstate_riscv_mtimer = { } }; -static void riscv_aclint_mtimer_class_init(ObjectClass *klass, void *data) +static void riscv_aclint_mtimer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = riscv_aclint_mtimer_realize; @@ -375,7 +374,7 @@ DeviceState *riscv_aclint_mtimer_create(hwaddr addr, hwaddr size, for (i = 0; i < num_harts; 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); @@ -409,7 +408,7 @@ 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 = cpu_by_arch_id(hartid); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; if (!env) { qemu_log_mask(LOG_GUEST_ERROR, "aclint-swi: invalid hartid: %zu", hartid); @@ -432,7 +431,7 @@ 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 = cpu_by_arch_id(hartid); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; if (!env) { qemu_log_mask(LOG_GUEST_ERROR, "aclint-swi: invalid hartid: %zu", hartid); @@ -462,11 +461,10 @@ static const MemoryRegionOps riscv_aclint_swi_ops = { } }; -static Property riscv_aclint_swi_properties[] = { +static const Property riscv_aclint_swi_properties[] = { DEFINE_PROP_UINT32("hartid-base", RISCVAclintSwiState, hartid_base, 0), DEFINE_PROP_UINT32("num-harts", RISCVAclintSwiState, num_harts, 1), DEFINE_PROP_UINT32("sswi", RISCVAclintSwiState, sswi, false), - DEFINE_PROP_END_OF_LIST(), }; static void riscv_aclint_swi_realize(DeviceState *dev, Error **errp) @@ -511,7 +509,7 @@ static void riscv_aclint_swi_reset_enter(Object *obj, ResetType type) } } -static void riscv_aclint_swi_class_init(ObjectClass *klass, void *data) +static void riscv_aclint_swi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = riscv_aclint_swi_realize; diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index 99aae8ccbeb..8bcd9f4697a 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -22,7 +22,7 @@ #include "qemu/module.h" #include "qemu/error-report.h" #include "qemu/bswap.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/sysbus.h" #include "hw/pci/msi.h" #include "hw/boards.h" @@ -30,9 +30,10 @@ #include "hw/intc/riscv_aplic.h" #include "hw/irq.h" #include "target/riscv/cpu.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" -#include "kvm_riscv.h" +#include "system/system.h" +#include "system/kvm.h" +#include "system/tcg.h" +#include "kvm/kvm_riscv.h" #include "migration/vmstate.h" #define APLIC_MAX_IDC (1UL << 14) @@ -154,23 +155,76 @@ * KVM AIA only supports APLIC MSI, fallback to QEMU emulation if we want to use * APLIC Wired. */ -static bool is_kvm_aia(bool msimode) +bool riscv_is_kvm_aia_aplic_imsic(bool msimode) { return kvm_irqchip_in_kernel() && msimode; } +bool riscv_use_emulated_aplic(bool msimode) +{ +#ifdef CONFIG_KVM + if (tcg_enabled()) { + return true; + } + + if (!riscv_is_kvm_aia_aplic_imsic(msimode)) { + return true; + } + + return kvm_kernel_irqchip_split(); +#else + return true; +#endif +} + +void riscv_aplic_set_kvm_msicfgaddr(RISCVAPLICState *aplic, hwaddr addr) +{ +#ifdef CONFIG_KVM + if (riscv_use_emulated_aplic(aplic->msimode)) { + addr >>= APLIC_xMSICFGADDR_PPN_SHIFT; + aplic->kvm_msicfgaddr = extract64(addr, 0, 32); + aplic->kvm_msicfgaddrH = extract64(addr, 32, 32) & + APLIC_xMSICFGADDRH_VALID_MASK; + } +#endif +} + +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; @@ -218,13 +272,29 @@ 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) { + return; + } + if (aplic->msimode && !pending) { + goto noskip_write_pending; + } + 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; + } + } + +noskip_write_pending: riscv_aplic_set_pending_raw(aplic, irq, pending); } @@ -323,21 +393,29 @@ static void riscv_aplic_msi_send(RISCVAPLICState *aplic, uint32_t lhxs, lhxw, hhxs, hhxw, group_idx, msicfgaddr, msicfgaddrH; aplic_m = aplic; - while (aplic_m && !aplic_m->mmode) { - aplic_m = aplic_m->parent; - } - if (!aplic_m) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: m-level APLIC not found\n", - __func__); - return; + + if (!aplic->kvm_splitmode) { + while (aplic_m && !aplic_m->mmode) { + aplic_m = aplic_m->parent; + } + if (!aplic_m) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: m-level APLIC not found\n", + __func__); + return; + } } - if (aplic->mmode) { - msicfgaddr = aplic_m->mmsicfgaddr; - msicfgaddrH = aplic_m->mmsicfgaddrH; + if (aplic->kvm_splitmode) { + msicfgaddr = aplic->kvm_msicfgaddr; + msicfgaddrH = ((uint64_t)aplic->kvm_msicfgaddrH << 32); } else { - msicfgaddr = aplic_m->smsicfgaddr; - msicfgaddrH = aplic_m->smsicfgaddrH; + if (aplic->mmode) { + msicfgaddr = aplic_m->mmsicfgaddr; + msicfgaddrH = aplic_m->mmsicfgaddrH; + } else { + msicfgaddr = aplic_m->smsicfgaddr; + msicfgaddrH = aplic_m->smsicfgaddrH; + } } lhxs = (msicfgaddrH >> APLIC_xMSICFGADDRH_LHXS_SHIFT) & @@ -350,7 +428,6 @@ static void riscv_aplic_msi_send(RISCVAPLICState *aplic, APLIC_xMSICFGADDRH_HHXW_MASK; group_idx = hart_idx >> lhxw; - hart_idx &= APLIC_xMSICFGADDR_PPN_LHX_MASK(lhxw); addr = msicfgaddr; addr |= ((uint64_t)(msicfgaddrH & APLIC_xMSICFGADDRH_BAPPN_MASK)) << 32; @@ -463,6 +540,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; } @@ -676,6 +754,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)) { @@ -812,7 +894,27 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) uint32_t i; RISCVAPLICState *aplic = RISCV_APLIC(dev); - if (!is_kvm_aia(aplic->msimode)) { + if (riscv_use_emulated_aplic(aplic->msimode)) { + /* Create output IRQ lines for non-MSI mode */ + if (!aplic->msimode) { + /* Claim the CPU interrupt to be triggered by this APLIC */ + for (i = 0; i < aplic->num_harts; i++) { + RISCVCPU *cpu; + + 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", + (aplic->mmode) ? "MEIP" : "SEIP"); + exit(1); + } + } + + aplic->external_irqs = g_malloc(sizeof(qemu_irq) * + aplic->num_harts); + qdev_init_gpio_out(dev, aplic->external_irqs, aplic->num_harts); + } + 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); @@ -829,6 +931,10 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) 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); + + if (kvm_enabled()) { + aplic->kvm_splitmode = true; + } } /* @@ -836,34 +942,17 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp) * have IRQ lines delegated by their parent APLIC. */ if (!aplic->parent) { - if (kvm_enabled() && is_kvm_aia(aplic->msimode)) { + if (kvm_enabled() && !riscv_use_emulated_aplic(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 */ - if (!aplic->msimode) { - aplic->external_irqs = g_malloc(sizeof(qemu_irq) * aplic->num_harts); - qdev_init_gpio_out(dev, aplic->external_irqs, aplic->num_harts); - - /* Claim the CPU interrupt to be triggered by this APLIC */ - for (i = 0; i < aplic->num_harts; 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", - (aplic->mmode) ? "MEIP" : "SEIP"); - exit(1); - } - } - } - msi_nonbroken = true; } -static Property riscv_aplic_properties[] = { +static const Property riscv_aplic_properties[] = { DEFINE_PROP_UINT32("aperture-size", RISCVAPLICState, aperture_size, 0), DEFINE_PROP_UINT32("hartid-base", RISCVAPLICState, hartid_base, 0), DEFINE_PROP_UINT32("num-harts", RISCVAPLICState, num_harts, 0), @@ -871,20 +960,21 @@ static Property riscv_aplic_properties[] = { DEFINE_PROP_UINT32("num-irqs", RISCVAPLICState, num_irqs, 0), DEFINE_PROP_BOOL("msimode", RISCVAPLICState, msimode, 0), DEFINE_PROP_BOOL("mmode", RISCVAPLICState, mmode, 0), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_riscv_aplic = { .name = "riscv_aplic", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { VMSTATE_UINT32(domaincfg, RISCVAPLICState), VMSTATE_UINT32(mmsicfgaddr, RISCVAPLICState), VMSTATE_UINT32(mmsicfgaddrH, RISCVAPLICState), VMSTATE_UINT32(smsicfgaddr, RISCVAPLICState), VMSTATE_UINT32(smsicfgaddrH, RISCVAPLICState), VMSTATE_UINT32(genmsi, RISCVAPLICState), + VMSTATE_UINT32(kvm_msicfgaddr, RISCVAPLICState), + VMSTATE_UINT32(kvm_msicfgaddrH, RISCVAPLICState), VMSTATE_VARRAY_UINT32(sourcecfg, RISCVAPLICState, num_irqs, 0, vmstate_info_uint32, uint32_t), @@ -907,7 +997,7 @@ static const VMStateDescription vmstate_riscv_aplic = { } }; -static void riscv_aplic_class_init(ObjectClass *klass, void *data) +static void riscv_aplic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -974,23 +1064,23 @@ 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); - - if (!is_kvm_aia(msimode)) { - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); - } - if (parent) { riscv_aplic_add_child(parent, dev); } - if (!msimode) { - for (i = 0; i < num_harts; i++) { - CPUState *cpu = cpu_by_arch_id(hartid_base + i); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + if (riscv_use_emulated_aplic(msimode)) { + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); + + if (!msimode) { + for (i = 0; i < num_harts; i++) { + CPUState *cpu = cpu_by_arch_id(hartid_base + i); - qdev_connect_gpio_out_named(dev, NULL, i, - qdev_get_gpio_in(DEVICE(cpu), + qdev_connect_gpio_out_named(dev, NULL, i, + qdev_get_gpio_in(DEVICE(cpu), (mmode) ? IRQ_M_EXT : IRQ_S_EXT)); + } } } diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c index 760dbddcf7f..21699881675 100644 --- a/hw/intc/riscv_imsic.c +++ b/hw/intc/riscv_imsic.c @@ -22,7 +22,7 @@ #include "qemu/module.h" #include "qemu/error-report.h" #include "qemu/bswap.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/sysbus.h" #include "hw/pci/msi.h" #include "hw/boards.h" @@ -31,8 +31,8 @@ #include "hw/irq.h" #include "target/riscv/cpu.h" #include "target/riscv/cpu_bits.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" +#include "system/system.h" +#include "system/kvm.h" #include "migration/vmstate.h" #define IMSIC_MMIO_PAGE_LE 0x00 @@ -55,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; } @@ -66,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); } } @@ -125,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; } @@ -139,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; @@ -157,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++) { @@ -172,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; } } @@ -302,14 +316,14 @@ static void riscv_imsic_write(void *opaque, hwaddr addr, uint64_t value, 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: @@ -333,9 +347,21 @@ static void riscv_imsic_realize(DeviceState *dev, Error **errp) RISCVIMSICState *imsic = RISCV_IMSIC(dev); RISCVCPU *rcpu = RISCV_CPU(cpu_by_arch_id(imsic->hartid)); CPUState *cpu = cpu_by_arch_id(imsic->hartid); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; + CPURISCVState *env = cpu ? cpu_env(cpu) : NULL; + + /* Claim the CPU interrupt to be triggered by this IMSIC */ + if (riscv_cpu_claim_interrupts(rcpu, + (imsic->mmode) ? MIP_MEIP : MIP_SEIP) < 0) { + error_setg(errp, "%s already claimed", + (imsic->mmode) ? "MEIP" : "SEIP"); + return; + } if (!kvm_irqchip_in_kernel()) { + /* Create output IRQ lines */ + imsic->external_irqs = g_malloc(sizeof(qemu_irq) * imsic->num_pages); + qdev_init_gpio_out(dev, imsic->external_irqs, imsic->num_pages); + 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); @@ -347,18 +373,6 @@ static void riscv_imsic_realize(DeviceState *dev, Error **errp) IMSIC_MMIO_SIZE(imsic->num_pages)); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &imsic->mmio); - /* Claim the CPU interrupt to be triggered by this IMSIC */ - if (riscv_cpu_claim_interrupts(rcpu, - (imsic->mmode) ? MIP_MEIP : MIP_SEIP) < 0) { - error_setg(errp, "%s already claimed", - (imsic->mmode) ? "MEIP" : "SEIP"); - return; - } - - /* Create output IRQ lines */ - imsic->external_irqs = g_malloc(sizeof(qemu_irq) * imsic->num_pages); - qdev_init_gpio_out(dev, imsic->external_irqs, imsic->num_pages); - /* Force select AIA feature and setup CSR read-modify-write callback */ if (env) { if (!imsic->mmode) { @@ -367,26 +381,28 @@ static void riscv_imsic_realize(DeviceState *dev, Error **errp) } else { rcpu->cfg.ext_smaia = true; } - riscv_cpu_set_aia_ireg_rmw_fn(env, (imsic->mmode) ? PRV_M : PRV_S, - riscv_imsic_rmw, imsic); + + if (!kvm_irqchip_in_kernel()) { + riscv_cpu_set_aia_ireg_rmw_fn(env, (imsic->mmode) ? PRV_M : PRV_S, + riscv_imsic_rmw, imsic); + } } msi_nonbroken = true; } -static Property riscv_imsic_properties[] = { +static const Property riscv_imsic_properties[] = { DEFINE_PROP_BOOL("mmode", RISCVIMSICState, mmode, 0), DEFINE_PROP_UINT32("hartid", RISCVIMSICState, hartid, 0), DEFINE_PROP_UINT32("num-pages", RISCVIMSICState, num_pages, 0), DEFINE_PROP_UINT32("num-irqs", RISCVIMSICState, num_irqs, 0), - DEFINE_PROP_END_OF_LIST(), }; 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), @@ -400,7 +416,7 @@ static const VMStateDescription vmstate_riscv_imsic = { } }; -static void riscv_imsic_class_init(ObjectClass *klass, void *data) +static void riscv_imsic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -451,15 +467,17 @@ DeviceState *riscv_imsic_create(hwaddr addr, uint32_t hartid, bool mmode, sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); - for (i = 0; i < num_pages; i++) { - if (!i) { - qdev_connect_gpio_out_named(dev, NULL, i, - qdev_get_gpio_in(DEVICE(cpu), + if (!kvm_irqchip_in_kernel()) { + for (i = 0; i < num_pages; i++) { + if (!i) { + qdev_connect_gpio_out_named(dev, NULL, i, + qdev_get_gpio_in(DEVICE(cpu), (mmode) ? IRQ_M_EXT : IRQ_S_EXT)); - } else { - qdev_connect_gpio_out_named(dev, NULL, i, - qdev_get_gpio_in(DEVICE(cpu), + } else { + qdev_connect_gpio_out_named(dev, NULL, i, + qdev_get_gpio_in(DEVICE(cpu), IRQ_LOCAL_MAX + i - 1)); + } } } diff --git a/hw/intc/rx_icu.c b/hw/intc/rx_icu.c index e5c01807b9a..f8615527b75 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), @@ -361,15 +361,14 @@ static const VMStateDescription vmstate_rxicu = { } }; -static Property rxicu_properties[] = { +static const Property rxicu_properties[] = { DEFINE_PROP_ARRAY("ipr-map", RXICUState, nr_irqs, map, qdev_prop_uint8, uint8_t), DEFINE_PROP_ARRAY("trigger-level", RXICUState, nr_sense, init_sense, qdev_prop_uint8, uint8_t), - DEFINE_PROP_END_OF_LIST(), }; -static void rxicu_class_init(ObjectClass *klass, void *data) +static void rxicu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 74e02858d43..8f4c9fd52e8 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,18 @@ static void qemu_s390_flic_instance_init(Object *obj) } } -static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) +static const Property qemu_s390_flic_properties[] = { + DEFINE_PROP_BOOL("migrate-all-state", QEMUS390FLICState, + migrate_all_state, true), +}; + +static void qemu_s390_flic_class_init(ObjectClass *oc, const 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; @@ -402,31 +470,17 @@ static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) fsc->inject_crw_mchk = qemu_s390_inject_crw_mchk; } -static Property s390_flic_common_properties[] = { - DEFINE_PROP_UINT32("adapter_routes_max_batch", S390FLICState, - adapter_routes_max_batch, ADAPTER_ROUTES_MAX_GSI), - DEFINE_PROP_END_OF_LIST(), -}; - static void s390_flic_common_realize(DeviceState *dev, Error **errp) { S390FLICState *fs = S390_FLIC_COMMON(dev); - uint32_t max_batch = fs->adapter_routes_max_batch; - - if (max_batch > ADAPTER_ROUTES_MAX_GSI) { - error_setg(errp, "flic property adapter_routes_max_batch too big" - " (%d > %d)", max_batch, ADAPTER_ROUTES_MAX_GSI); - return; - } fs->ais_supported = s390_has_feat(S390_FEAT_ADAPTER_INT_SUPPRESSION); } -static void s390_flic_class_init(ObjectClass *oc, void *data) +static void s390_flic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - device_class_set_props(dc, s390_flic_common_properties); dc->realize = s390_flic_common_realize; } @@ -455,17 +509,11 @@ static void qemu_s390_flic_register_types(void) type_init(qemu_s390_flic_register_types) -static bool adapter_info_so_needed(void *opaque) -{ - return css_migration_enabled(); -} - const VMStateDescription vmstate_adapter_info_so = { .name = "s390_adapter_info/summary_offset", .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 +523,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 +532,7 @@ const VMStateDescription vmstate_adapter_info = { */ VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_adapter_info_so, NULL } @@ -495,7 +543,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 28364b22d65..f833a3996a6 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -16,7 +16,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qapi/error.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/s390x/s390_flic.h" #include "hw/s390x/adapter.h" #include "hw/s390x/css.h" @@ -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) { @@ -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 } @@ -642,15 +670,16 @@ static void kvm_s390_flic_reset(DeviceState *dev) flic_enable_pfault(flic); } -static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) +static void kvm_s390_flic_class_init(ObjectClass *oc, const 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 5522ede2cf8..3160b216fdc 100644 --- a/hw/intc/sifive_plic.c +++ b/hw/intc/sifive_plic.c @@ -30,7 +30,7 @@ #include "target/riscv/cpu.h" #include "migration/vmstate.h" #include "hw/irq.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" static bool addr_between(uint32_t addr, uint32_t base, uint32_t num) { @@ -189,8 +189,13 @@ static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value, if (addr_between(addr, plic->priority_base, plic->num_sources << 2)) { uint32_t irq = (addr - plic->priority_base) >> 2; - - if (((plic->num_priorities + 1) & plic->num_priorities) == 0) { + 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 @@ -349,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) @@ -406,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), @@ -423,7 +430,7 @@ static const VMStateDescription vmstate_sifive_plic = { } }; -static Property sifive_plic_properties[] = { +static const Property sifive_plic_properties[] = { DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config), DEFINE_PROP_UINT32("hartid-base", SiFivePLICState, hartid_base, 0), /* number of interrupt sources including interrupt source 0 */ @@ -437,14 +444,13 @@ static Property sifive_plic_properties[] = { DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0), DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0), DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void sifive_plic_class_init(ObjectClass *klass, void *data) +static void sifive_plic_class_init(ObjectClass *klass, const 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; diff --git a/hw/intc/slavio_intctl.c b/hw/intc/slavio_intctl.c index f7e59ba6432..00b80bb177c 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) @@ -442,12 +441,12 @@ static void slavio_intctl_init(Object *obj) } } -static void slavio_intctl_class_init(ObjectClass *klass, void *data) +static void slavio_intctl_class_init(ObjectClass *klass, const 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; @@ -461,7 +460,7 @@ static const TypeInfo slavio_intctl_info = { .instance_size = sizeof(SLAVIO_INTCTLState), .instance_init = slavio_intctl_init, .class_init = slavio_intctl_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_INTERRUPT_STATS_PROVIDER }, { } }, diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 8bcab2846c1..440edb97d8d 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -1,10 +1,9 @@ /* * QEMU PowerPC sPAPR XIVE interrupt controller model * - * Copyright (c) 2017-2018, IBM Corporation. + * Copyright (c) 2017-2024, IBM Corporation. * - * This code is licensed under the 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" @@ -13,10 +12,9 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "target/ppc/cpu.h" -#include "sysemu/cpus.h" -#include "sysemu/reset.h" +#include "system/cpus.h" +#include "system/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 +25,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 +130,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 +140,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 +154,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 +169,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 +179,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 +195,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 +315,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 +326,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 +344,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, @@ -431,7 +430,8 @@ static int spapr_xive_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk, static int spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, + uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match) { CPUState *cs; @@ -522,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), @@ -539,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() }, @@ -577,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), @@ -627,13 +627,12 @@ static void spapr_xive_free_irq(SpaprInterruptController *intc, int lisn) xive->eat[lisn].w &= cpu_to_be64(~EAS_VALID); } -static Property spapr_xive_properties[] = { +static const Property spapr_xive_properties[] = { DEFINE_PROP_UINT32("nr-irqs", SpaprXive, nr_irqs, 0), DEFINE_PROP_UINT32("nr-ends", SpaprXive, nr_ends, 0), DEFINE_PROP_UINT64("vc-base", SpaprXive, vc_base, SPAPR_XIVE_VC_BASE), DEFINE_PROP_UINT64("tm-base", SpaprXive, tm_base, SPAPR_XIVE_TM_BASE), DEFINE_PROP_UINT8("hv-prio", SpaprXive, hv_prio, 7), - DEFINE_PROP_END_OF_LIST(), }; static int spapr_xive_cpu_intc_create(SpaprInterruptController *intc, @@ -699,7 +698,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; @@ -707,10 +706,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, @@ -811,7 +809,7 @@ static bool spapr_xive_in_kernel_xptr(const XivePresenter *xptr) return spapr_xive_in_kernel(SPAPR_XIVE(xptr)); } -static void spapr_xive_class_init(ObjectClass *klass, void *data) +static void spapr_xive_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); XiveRouterClass *xrc = XIVE_ROUTER_CLASS(klass); @@ -858,7 +856,7 @@ static const TypeInfo spapr_xive_info = { .instance_size = sizeof(SpaprXive), .class_init = spapr_xive_class_init, .class_size = sizeof(SpaprXiveClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_SPAPR_INTC }, { } }, diff --git a/hw/intc/spapr_xive_kvm.c b/hw/intc/spapr_xive_kvm.c index 61fe7bd2d32..26d30b41c15 100644 --- a/hw/intc/spapr_xive_kvm.c +++ b/hw/intc/spapr_xive_kvm.c @@ -12,9 +12,9 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "target/ppc/cpu.h" -#include "sysemu/cpus.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" +#include "system/cpus.h" +#include "system/kvm.h" +#include "system/runstate.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_cpu_core.h" #include "hw/ppc/spapr_xive.h" @@ -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 36ff71f9475..334aa6a97ba 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -14,8 +14,8 @@ cpu_get_apic_base(uint64_t val) "0x%016"PRIx64 # 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" @@ -79,6 +79,20 @@ 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(const char *s, uint64_t offset, unsigned size, uint32_t value) "%s: From 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_intc_write(const char *s, uint64_t offset, unsigned size, uint32_t data) "%s: To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_intc_set_irq(const char *s, int inpin_idx, int level) "%s: Set IRQ %d: %d" +aspeed_intc_clear_irq(const char *s, int inpin_idx, int outpin_idx, int level) "%s: Clear IRQ %d-%d: %d" +aspeed_intc_update_irq(const char *s, int inpin_idx, int outpin_idx, int level) "%s: Update IRQ: %d-%d: %d" +aspeed_intc_pending_irq(const char *s, int inpin_idx, uint32_t value) "%s: Pending IRQ: %d: 0x%x" +aspeed_intc_trigger_irq(const char *s, int inpin_idx, int outpin_idx, uint32_t value) "%s: Trigger IRQ: %d-%d: 0x%x" +aspeed_intc_all_isr_done(const char *s, int inpin_idx) "%s: All source ISR execution are done: %d" +aspeed_intc_enable(const char *s, uint32_t value) "%s: Enable: 0x%x" +aspeed_intc_select(const char *s, uint32_t value) "%s: Select: 0x%x" +aspeed_intc_mask(const char *s, uint32_t change, uint32_t value) "%s: Mask: 0x%x: 0x%x" +aspeed_intc_unmask(const char *s, uint32_t change, uint32_t value) "%s: UnMask: 0x%x: 0x%x" +aspeed_intc_all_isr_done_bit(const char *s, int inpin_idx, int bit) "%s: All source ISR execution are done from specific bit: %d-%d" # arm_gic.c gic_enable_irq(int irq) "irq %d enabled" @@ -116,6 +130,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 @@ -151,6 +166,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" @@ -267,9 +283,13 @@ xive_router_end_notify(uint8_t end_blk, uint32_t end_idx, uint32_t end_data) "EN 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(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_presenter_notify(uint8_t nvt_blk, uint32_t nvt_idx, uint8_t ring, uint8_t group_level) "found NVT 0x%x/0x%x ring=0x%x group_level=%d" xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x/0x%x @0x%"PRIx64 +# xive2.c +xive_nvp_backlog_op(uint8_t blk, uint32_t idx, uint8_t op, uint8_t priority, uint8_t rc) "NVP 0x%x/0x%x operation=%d priority=%d rc=%d" +xive_nvgc_backlog_op(bool c, uint8_t blk, uint32_t idx, uint8_t op, uint8_t priority, uint32_t rc) "NVGC crowd=%d 0x%x/0x%x operation=%d priority=%d rc=%d" + # pnv_xive.c pnv_xive_ic_hw_trigger(uint64_t addr, uint64_t val) "@0x%"PRIx64" val=0x%"PRIx64 @@ -289,19 +309,13 @@ 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 -loongarch_ipi_unsupported_cpuid(const char *s, uint32_t cpuid) "%s unsupported cpuid 0x%" PRIx32 - +# 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 -loongarch_pch_pic_low_writew(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64 -loongarch_pch_pic_high_readw(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64 -loongarch_pch_pic_high_writew(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64 -loongarch_pch_pic_readb(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64 -loongarch_pch_pic_writeb(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64 +loongarch_pch_pic_read(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64 +loongarch_pch_pic_write(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64 # loongarch_pch_msi.c loongarch_msi_set_irq(int irq_num) "set msi irq %d" diff --git a/hw/intc/xics.c b/hw/intc/xics.c index c7f8abd71e4..d9a199e8834 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 "system/kvm.h" +#include "system/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) @@ -346,14 +344,13 @@ static void icp_unrealize(DeviceState *dev) vmstate_unregister(NULL, &vmstate_icp_server, icp); } -static Property icp_properties[] = { +static const Property icp_properties[] = { DEFINE_PROP_LINK(ICP_PROP_XICS, ICPState, xics, TYPE_XICS_FABRIC, XICSFabric *), DEFINE_PROP_LINK(ICP_PROP_CPU, ICPState, cs, TYPE_CPU, CPUState *), - DEFINE_PROP_END_OF_LIST(), }; -static void icp_class_init(ObjectClass *klass, void *data) +static void icp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -564,7 +561,7 @@ static void ics_reset_irq(ICSIRQState *irq) irq->saved_priority = 0xff; } -static void ics_reset_hold(Object *obj) +static void ics_reset_hold(Object *obj, ResetType type) { ICSState *ics = ICS(obj); g_autofree uint8_t *flags = g_malloc(ics->nr_irqs); @@ -651,7 +648,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 +664,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), @@ -678,14 +675,13 @@ static const VMStateDescription vmstate_ics = { }, }; -static Property ics_properties[] = { +static const Property ics_properties[] = { DEFINE_PROP_UINT32("nr-irqs", ICSState, nr_irqs, 0), DEFINE_PROP_LINK(ICS_PROP_XICS, ICSState, xics, TYPE_XICS_FABRIC, XICSFabric *), - DEFINE_PROP_END_OF_LIST(), }; -static void ics_class_init(ObjectClass *klass, void *data) +static void ics_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 9719d98a179..ee72969f5f1 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -28,7 +28,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "trace.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_cpu_core.h" #include "hw/ppc/xics.h" diff --git a/hw/intc/xics_pnv.c b/hw/intc/xics_pnv.c index 753c067f172..ff602d9a346 100644 --- a/hw/intc/xics_pnv.c +++ b/hw/intc/xics_pnv.c @@ -176,7 +176,7 @@ static void pnv_icp_realize(DeviceState *dev, Error **errp) icp, "icp-thread", 0x1000); } -static void pnv_icp_class_init(ObjectClass *klass, void *data) +static void pnv_icp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ICPStateClass *icpc = ICP_CLASS(klass); diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index 37b2d99977a..7663596a4a4 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) @@ -437,7 +436,7 @@ static void xics_spapr_deactivate(SpaprInterruptController *intc) } } -static void ics_spapr_class_init(ObjectClass *klass, void *data) +static void ics_spapr_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ICSStateClass *isc = ICS_CLASS(klass); @@ -462,7 +461,7 @@ static const TypeInfo ics_spapr_info = { .name = TYPE_ICS_SPAPR, .parent = TYPE_ICS, .class_init = ics_spapr_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_SPAPR_INTC }, { } }, diff --git a/hw/intc/xilinx_intc.c b/hw/intc/xilinx_intc.c index 6e5012e66eb..5257ad54b17 100644 --- a/hw/intc/xilinx_intc.c +++ b/hw/intc/xilinx_intc.c @@ -3,6 +3,9 @@ * * Copyright (c) 2009 Edgar E. Iglesias. * + * https://docs.amd.com/v/u/en-US/xps_intc + * DS572: LogiCORE IP XPS Interrupt Controller (v2.01a) + * * 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 @@ -23,10 +26,12 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "qemu/module.h" #include "hw/irq.h" #include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" #include "qom/object.h" #define D(x) @@ -49,6 +54,7 @@ struct XpsIntc { SysBusDevice parent_obj; + EndianMode model_endianness; MemoryRegion mmio; qemu_irq parent_irq; @@ -140,14 +146,28 @@ static void pic_write(void *opaque, hwaddr addr, update_irq(p); } -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 const MemoryRegionOps pic_ops[2] = { + [0 ... 1] = { + .read = pic_read, + .write = pic_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + /* + * All XPS INTC registers are accessed through the PLB interface. + * The base address for these registers is provided by the + * configuration parameter, C_BASEADDR. Each register is 32 bits + * although some bits may be unused and is accessed on a 4-byte + * boundary offset from the base address. + */ + .min_access_size = 4, + .max_access_size = 4, + }, + }, + [0].endianness = DEVICE_LITTLE_ENDIAN, + [1].endianness = DEVICE_BIG_ENDIAN, }; static void irq_handler(void *opaque, int irq, int level) @@ -170,21 +190,35 @@ static void xilinx_intc_init(Object *obj) qdev_init_gpio_in(DEVICE(obj), irq_handler, 32); sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio); +} - memory_region_init_io(&p->mmio, obj, &pic_ops, p, "xlnx.xps-intc", +static void xilinx_intc_realize(DeviceState *dev, Error **errp) +{ + XpsIntc *p = XILINX_INTC(dev); + + if (p->model_endianness == ENDIAN_MODE_UNSPECIFIED) { + error_setg(errp, TYPE_XILINX_INTC " property 'endianness'" + " must be set to 'big' or 'little'"); + return; + } + + memory_region_init_io(&p->mmio, OBJECT(dev), + &pic_ops[p->model_endianness == ENDIAN_MODE_BIG], + p, "xlnx.xps-intc", R_MAX * 4); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio); } -static Property xilinx_intc_properties[] = { +static const Property xilinx_intc_properties[] = { + DEFINE_PROP_ENDIAN_NODEFAULT("endianness", XpsIntc, model_endianness), DEFINE_PROP_UINT32("kind-of-intr", XpsIntc, c_kind_of_intr, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void xilinx_intc_class_init(ObjectClass *klass, void *data) +static void xilinx_intc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = xilinx_intc_realize; device_class_set_props(dc, xilinx_intc_properties); } diff --git a/hw/intc/xive.c b/hw/intc/xive.c index df3ee0496fe..27b473e4d76 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -3,8 +3,7 @@ * * Copyright (c) 2017-2018, IBM Corporation. * - * This code is licensed under the 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" @@ -12,12 +11,11 @@ #include "qemu/module.h" #include "qapi/error.h" #include "target/ppc/cpu.h" -#include "sysemu/cpus.h" -#include "sysemu/dma.h" -#include "sysemu/reset.h" +#include "system/cpus.h" +#include "system/dma.h" +#include "system/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" @@ -28,28 +26,6 @@ * XIVE Thread Interrupt Management context */ -/* - * Convert an Interrupt Pending Buffer (IPB) register to a Pending - * Interrupt Priority Register (PIPR), which contains the priority of - * the most favored pending notification. - */ -static uint8_t ipb_to_pipr(uint8_t ibp) -{ - return ibp ? clz32((uint32_t)ibp << 24) : 0xff; -} - -static uint8_t exception_mask(uint8_t ring) -{ - switch (ring) { - case TM_QW1_OS: - return TM_QW1_NSR_EO; - case TM_QW3_HV_PHYS: - return TM_QW3_NSR_HE; - default: - g_assert_not_reached(); - } -} - static qemu_irq xive_tctx_output(XiveTCTX *tctx, uint8_t ring) { switch (ring) { @@ -69,66 +45,88 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) { uint8_t *regs = &tctx->regs[ring]; uint8_t nsr = regs[TM_NSR]; - uint8_t mask = exception_mask(ring); qemu_irq_lower(xive_tctx_output(tctx, ring)); - if (regs[TM_NSR] & mask) { + if (regs[TM_NSR] != 0) { 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]); + /* + * If the interrupt was for a specific VP, reset the pending + * buffer bit, otherwise clear the logical server indicator + */ + if (regs[TM_NSR] & TM_NSR_GRP_LVL) { + regs[TM_NSR] &= ~TM_NSR_GRP_LVL; + } else { + alt_regs[TM_IPB] &= ~xive_priority_to_ipb(cppr); + } - /* Drop Exception bit */ - regs[TM_NSR] &= ~mask; + /* Drop the exception bit and any group/crowd */ + regs[TM_NSR] = 0; - 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) +void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level) { + /* 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; + regs[TM_NSR] = TM_QW1_NSR_EO | (group_level & 0x3F); + break; + case TM_QW2_HV_POOL: + alt_regs[TM_NSR] = (TM_QW3_NSR_HE_POOL << 6) | (group_level & 0x3F); break; case TM_QW3_HV_PHYS: - regs[TM_NSR] |= (TM_QW3_NSR_HE_PHYS << 6); + regs[TM_NSR] = (TM_QW3_NSR_HE_PHYS << 6) | (group_level & 0x3F); break; default: 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], @@ -140,18 +138,57 @@ 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 = xive_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 = xive_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, 0); } -void xive_tctx_ipb_update(XiveTCTX *tctx, uint8_t ring, uint8_t ipb) -{ +void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, + uint8_t group_level) + { + /* 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]; - regs[TM_IPB] |= ipb; - regs[TM_PIPR] = ipb_to_pipr(regs[TM_IPB]); - xive_tctx_notify(tctx, ring); -} + if (group_level == 0) { + /* VP-specific */ + regs[TM_IPB] |= xive_priority_to_ipb(priority); + alt_regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); + } else { + /* VP-group */ + alt_regs[TM_PIPR] = xive_priority_to_pipr(priority); + } + xive_tctx_notify(tctx, ring, group_level); + } /* * XIVE Thread Interrupt Management Area (TIMA) @@ -180,6 +217,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) { @@ -208,14 +256,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 */ }; @@ -342,14 +390,27 @@ 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 + * Adjust the PIPR to allow a CPU to process event queues of other * priorities during one physical interrupt cycle. */ static void xive_tm_set_os_pending(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size) { - xive_tctx_ipb_update(tctx, TM_QW1_OS, xive_priority_to_ipb(value & 0xff)); + xive_tctx_pipr_update(tctx, TM_QW1_OS, value & 0xff, 0); } static void xive_os_cam_decode(uint32_t cam, uint8_t *nvt_blk, @@ -401,7 +462,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; } @@ -427,16 +488,20 @@ static void xive_tctx_need_resend(XiveRouter *xrtr, XiveTCTX *tctx, /* Reset the NVT value */ nvt.w4 = xive_set_field32(NVT_W4_IPB, nvt.w4, 0); xive_router_write_nvt(xrtr, nvt_blk, nvt_idx, &nvt, 4); + + uint8_t *regs = &tctx->regs[TM_QW1_OS]; + regs[TM_IPB] |= ipb; } + /* - * Always call xive_tctx_ipb_update(). Even if there were no + * Always call xive_tctx_pipr_update(). Even if there were no * escalation triggered, there could be a pending interrupt which * was saved when the context was pulled and that we need to take * into account by recalculating the PIPR (which is not * saved/restored). * It will also raise the External interrupt signal if needed. */ - xive_tctx_ipb_update(tctx, TM_QW1_OS, ipb); + xive_tctx_pipr_update(tctx, TM_QW1_OS, 0xFF, 0); /* fxb */ } /* @@ -489,20 +554,34 @@ 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 xive2_tm_operations[] = { @@ -510,20 +589,50 @@ static const XiveTmOp xive2_tm_operations[] = { * MMIOs below 2K : raw values and special operations without side * effects */ - { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive_tm_set_os_cppr, NULL }, - { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, xive2_tm_push_os_ctx, NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive_tm_set_hv_cppr, NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push, NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, NULL, xive_tm_vt_poll }, + { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive2_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, xive2_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, 4, NULL, xive2_tm_pull_os_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, NULL, xive2_tm_pull_os_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, NULL, xive_tm_ack_hv_reg }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, NULL, xive_tm_pull_pool_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, NULL, xive_tm_pull_pool_ctx }, + { 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, @@ -669,7 +778,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; @@ -693,13 +802,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); } } @@ -712,15 +828,19 @@ 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 * CPPR is first set. */ tctx->regs[TM_QW1_OS + TM_PIPR] = - ipb_to_pipr(tctx->regs[TM_QW1_OS + TM_IPB]); + xive_ipb_to_pipr(tctx->regs[TM_QW1_OS + TM_IPB]); tctx->regs[TM_QW3_HV_PHYS + TM_PIPR] = - ipb_to_pipr(tctx->regs[TM_QW3_HV_PHYS + TM_IPB]); + xive_ipb_to_pipr(tctx->regs[TM_QW3_HV_PHYS + TM_IPB]); } static void xive_tctx_realize(DeviceState *dev, Error **errp) @@ -798,20 +918,19 @@ 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() }, }; -static Property xive_tctx_properties[] = { +static const Property xive_tctx_properties[] = { DEFINE_PROP_LINK("cpu", XiveTCTX, cs, TYPE_CPU, CPUState *), DEFINE_PROP_LINK("presenter", XiveTCTX, xptr, TYPE_XIVE_PRESENTER, XivePresenter *), - DEFINE_PROP_END_OF_LIST(), }; -static void xive_tctx_class_init(ObjectClass *klass, void *data) +static void xive_tctx_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1207,22 +1326,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' : ' '); } } @@ -1238,7 +1355,7 @@ static void xive_source_reset(void *dev) 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); @@ -1271,7 +1388,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() @@ -1282,7 +1399,7 @@ static const VMStateDescription vmstate_xive_source = { * The default XIVE interrupt source setting for the ESB MMIOs is two * 64k pages without Store EOI, to be in sync with KVM. */ -static Property xive_source_properties[] = { +static const 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), @@ -1293,10 +1410,9 @@ static Property xive_source_properties[] = { 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(), }; -static void xive_source_class_init(ObjectClass *klass, void *data) +static void xive_source_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1322,7 +1438,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); @@ -1333,7 +1449,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); @@ -1345,14 +1461,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); @@ -1371,26 +1487,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) @@ -1419,8 +1536,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; @@ -1431,15 +1547,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)); } /* @@ -1539,6 +1655,75 @@ static uint32_t xive_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) return xive_nvt_cam_line(blk, 1 << 7 | (pir & 0x7f)); } +uint32_t xive_get_vpgroup_size(uint32_t nvp_index) +{ + /* + * Group size is a power of 2. The position of the first 0 + * (starting with the least significant bits) in the NVP index + * gives the size of the group. + */ + int first_zero = cto32(nvp_index); + if (first_zero >= 31) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid group index 0x%08x", + nvp_index); + return 0; + } + + return 1U << (first_zero + 1); +} + +static uint8_t xive_get_group_level(bool crowd, bool ignore, + uint32_t nvp_blk, uint32_t nvp_index) +{ + int first_zero; + uint8_t level; + + if (!ignore) { + g_assert(!crowd); + return 0; + } + + first_zero = cto32(nvp_index); + if (first_zero >= 31) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid group index 0x%08x", + nvp_index); + return 0; + } + + level = (first_zero + 1) & 0b1111; + if (crowd) { + uint32_t blk; + + /* crowd level is bit position of first 0 from the right in nvp_blk */ + first_zero = cto32(nvp_blk); + if (first_zero >= 31) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid crowd block 0x%08x", + nvp_blk); + return 0; + } + blk = first_zero + 1; + + /* + * Supported crowd sizes are 2^1, 2^2, and 2^4. 2^3 is not supported. + * HW will encode level 4 as the value 3. See xive2_pgofnext(). + */ + switch (blk) { + case 1: + case 2: + break; + case 4: + blk = 3; + break; + default: + g_assert_not_reached(); + } + + /* Crowd level bits reside in upper 2 bits of the 6 bit group level */ + level |= blk << 4; + } + return level; +} + /* * The thread context register words are in big-endian format. */ @@ -1605,31 +1790,41 @@ int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, /* * This is our simple Xive Presenter Engine model. It is merged in the * Router as it does not require an extra object. - * - * 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 - * 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 - * involves 2 or 3 exchanges on the PowerBus that the model does not - * support. - * - * The parameters represent what is sent on the PowerBus */ bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, - uint32_t logic_serv) + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, bool *precluded) { XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xfb); - XiveTCTXMatch match = { .tctx = NULL, .ring = 0 }; + XiveTCTXMatch match = { .tctx = NULL, .ring = 0, .precluded = false }; + uint8_t group_level; int count; /* - * Ask the machine to scan the interrupt controllers for a match + * Ask the machine to scan the interrupt controllers for a match. + * + * For VP-specific notification, we expect at most one match and + * one call to the presenters is all we need (abbreviated notify + * sequence documented by the architecture). + * + * For VP-group notification, match_nvt() is the equivalent of the + * "histogram" and "poll" commands sent to the power bus to the + * presenters. 'count' could be more than one, but we always + * select the first match for now. 'precluded' tells if (at least) + * one thread matches but can't take the interrupt now because + * it's running at a more favored priority. We return the + * information to the router so that it can take appropriate + * actions (backlog, escalation, broadcast, etc...) + * + * If we were to implement a better way of dispatching the + * interrupt in case of multiple matches (instead of the first + * match), we would need a heuristic to elect a thread (for + * example, the hardware keeps track of an 'age' in the TIMA) and + * a new command to the presenters (the equivalent of the "assign" + * power bus command in the documented full notify sequence. */ - count = xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, cam_ignore, + count = xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, crowd, cam_ignore, priority, logic_serv, &match); if (count < 0) { return false; @@ -1637,9 +1832,11 @@ bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, /* handle CPU exception delivery */ if (count) { - trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring); - xive_tctx_ipb_update(match.tctx, match.ring, - xive_priority_to_ipb(priority)); + group_level = xive_get_group_level(crowd, cam_ignore, nvt_blk, nvt_idx); + trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring, group_level); + xive_tctx_pipr_update(match.tctx, match.ring, priority, group_level); + } else { + *precluded = match.precluded; } return !!count; @@ -1679,7 +1876,7 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas) uint8_t nvt_blk; uint32_t nvt_idx; XiveNVT nvt; - bool found; + bool found, precluded; uint8_t end_blk = xive_get_field64(EAS_END_BLOCK, eas->w); uint32_t end_idx = xive_get_field64(EAS_END_INDEX, eas->w); @@ -1760,10 +1957,12 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas) } found = xive_presenter_notify(xrtr->xfb, format, nvt_blk, nvt_idx, + false /* crowd */, xive_get_field32(END_W7_F0_IGNORE, end.w7), priority, - xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7)); - + xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7), + &precluded); + /* we don't support VP-group notification on P9, so precluded is not used */ /* TODO: Auto EOI. */ if (found) { @@ -1881,13 +2080,12 @@ void xive_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) xive_router_end_notify_handler(xrtr, &eas); } -static Property xive_router_properties[] = { +static const Property xive_router_properties[] = { DEFINE_PROP_LINK("xive-fabric", XiveRouter, xfb, TYPE_XIVE_FABRIC, XiveFabric *), - DEFINE_PROP_END_OF_LIST(), }; -static void xive_router_class_init(ObjectClass *klass, void *data) +static void xive_router_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); XiveNotifierClass *xnc = XIVE_NOTIFIER_CLASS(klass); @@ -1910,24 +2108,24 @@ static const TypeInfo xive_router_info = { .instance_size = sizeof(XiveRouter), .class_size = sizeof(XiveRouterClass), .class_init = xive_router_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XIVE_NOTIFIER }, { TYPE_XIVE_PRESENTER }, { } } }; -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)); } /* @@ -2049,15 +2247,14 @@ static void xive_end_source_realize(DeviceState *dev, Error **errp) (1ull << (xsrc->esb_shift + 1)) * xsrc->nr_ends); } -static Property xive_end_source_properties[] = { +static const Property xive_end_source_properties[] = { DEFINE_PROP_UINT32("nr-ends", XiveENDSource, nr_ends, 0), DEFINE_PROP_UINT32("shift", XiveENDSource, esb_shift, XIVE_ESB_64K), DEFINE_PROP_LINK("xive", XiveENDSource, xrtr, TYPE_XIVE_ROUTER, XiveRouter *), - DEFINE_PROP_END_OF_LIST(), }; -static void xive_end_source_class_init(ObjectClass *klass, void *data) +static void xive_end_source_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index c37ef25d44a..a08cf906d0e 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1,10 +1,9 @@ /* * QEMU PowerPC XIVE2 interrupt controller model (POWER10) * - * Copyright (c) 2019-2022, IBM Corporation.. + * Copyright (c) 2019-2024, IBM Corporation.. * - * This code is licensed under the 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" @@ -12,13 +11,13 @@ #include "qemu/module.h" #include "qapi/error.h" #include "target/ppc/cpu.h" -#include "sysemu/cpus.h" -#include "sysemu/dma.h" +#include "system/cpus.h" +#include "system/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" +#include "trace.h" uint32_t xive2_router_get_config(Xive2Router *xrtr) { @@ -27,21 +26,169 @@ 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. NVGC is 32-bytes long, so 24-bytes from + * w2, which fits 8 priorities * 24-bits per priority. + */ + ptr = (uint8_t *)&nvgc->w2 + priority * 3; + for (i = 0; i < 3; i++, ptr++) { + val = (val << 8) + *ptr; + } + return val; +} + +static void xive2_nvgc_set_backlog(Xive2Nvgc *nvgc, uint8_t priority, + uint32_t val) +{ + uint8_t *ptr, i; + uint32_t shift; + + if (priority > 7) { + return; + } + + if (val > 0xFFFFFF) { + val = 0xFFFFFF; + } + /* + * 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++) { + shift = 8 * (2 - i); + *ptr = (val >> shift) & 0xFF; + } +} + +uint64_t xive2_presenter_nvgc_backlog_op(XivePresenter *xptr, + bool crowd, + uint8_t blk, uint32_t idx, + uint16_t offset, uint16_t val) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint8_t priority = GETFIELD(NVx_BACKLOG_PRIO, offset); + uint8_t op = GETFIELD(NVx_BACKLOG_OP, offset); + Xive2Nvgc nvgc; + uint32_t count, old_count; + + if (xive2_router_get_nvgc(xrtr, crowd, blk, idx, &nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No %s %x/%x\n", + crowd ? "NVC" : "NVG", blk, idx); + return -1; + } + if (!xive2_nvgc_is_valid(&nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVG %x/%x\n", blk, idx); + return -1; + } + + old_count = xive2_nvgc_get_backlog(&nvgc, priority); + count = old_count; + /* + * op: + * 0b00 => increment + * 0b01 => decrement + * 0b1- => read + */ + if (op == 0b00 || op == 0b01) { + if (op == 0b00) { + count += val; + } else { + if (count > val) { + count -= val; + } else { + count = 0; + } + } + xive2_nvgc_set_backlog(&nvgc, priority, count); + xive2_router_write_nvgc(xrtr, crowd, blk, idx, &nvgc); + } + trace_xive_nvgc_backlog_op(crowd, blk, idx, op, priority, old_count); + return old_count; +} + +uint64_t xive2_presenter_nvp_backlog_op(XivePresenter *xptr, + uint8_t blk, uint32_t idx, + uint16_t offset) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint8_t priority = GETFIELD(NVx_BACKLOG_PRIO, offset); + uint8_t op = GETFIELD(NVx_BACKLOG_OP, offset); + Xive2Nvp nvp; + uint8_t ipb, old_ipb, rc; + + if (xive2_router_get_nvp(xrtr, blk, idx, &nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVP %x/%x\n", blk, idx); + return -1; + } + if (!xive2_nvp_is_valid(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVP %x/%x\n", blk, idx); + return -1; + } + + old_ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2); + ipb = old_ipb; + /* + * op: + * 0b00 => set priority bit + * 0b01 => reset priority bit + * 0b1- => read + */ + if (op == 0b00 || op == 0b01) { + if (op == 0b00) { + ipb |= xive_priority_to_ipb(priority); + } else { + ipb &= ~xive_priority_to_ipb(priority); + } + nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, ipb); + xive2_router_write_nvp(xrtr, blk, idx, &nvp, 2); + } + rc = !!(old_ipb & xive_priority_to_ipb(priority)); + trace_xive_nvp_backlog_op(blk, idx, op, priority, rc); + return rc; +} + +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 +199,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 +211,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); @@ -79,8 +226,8 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, Monitor *mon) uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3); uint32_t qentries = 1 << (qsize + 10); - uint32_t nvp_blk = xive_get_field32(END2_W6_VP_BLOCK, end->w6); - uint32_t nvp_idx = xive_get_field32(END2_W6_VP_OFFSET, end->w6); + uint32_t nvx_blk = xive_get_field32(END2_W6_VP_BLOCK, end->w6); + uint32_t nvx_idx = xive_get_field32(END2_W6_VP_OFFSET, end->w6); uint8_t priority = xive_get_field32(END2_W7_F0_PRIORITY, end->w7); uint8_t pq; @@ -90,33 +237,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, nvx_blk, nvx_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 +278,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) @@ -167,6 +367,115 @@ static void xive2_end_enqueue(Xive2End *end, uint32_t data) end->w1 = xive_set_field32(END2_W1_PAGE_OFF, end->w1, qindex); } +static void xive2_pgofnext(uint8_t *nvgc_blk, uint32_t *nvgc_idx, + uint8_t next_level) +{ + uint32_t mask, next_idx; + uint8_t next_blk; + + /* + * Adjust the block and index of a VP for the next group/crowd + * size (PGofFirst/PGofNext field in the NVP and NVGC structures). + * + * The 6-bit group level is split into a 2-bit crowd and 4-bit + * group levels. Encoding is similar. However, we don't support + * crowd size of 8. So a crowd level of 0b11 is bumped to a crowd + * size of 16. + */ + next_blk = NVx_CROWD_LVL(next_level); + if (next_blk == 3) { + next_blk = 4; + } + mask = (1 << next_blk) - 1; + *nvgc_blk &= ~mask; + *nvgc_blk |= mask >> 1; + + next_idx = NVx_GROUP_LVL(next_level); + mask = (1 << next_idx) - 1; + *nvgc_idx &= ~mask; + *nvgc_idx |= mask >> 1; +} + +/* + * Scan the group chain and return the highest priority and group + * level of pending group interrupts. + */ +static uint8_t xive2_presenter_backlog_scan(XivePresenter *xptr, + uint8_t nvx_blk, uint32_t nvx_idx, + uint8_t first_group, + uint8_t *out_level) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint32_t nvgc_idx; + uint32_t current_level, count; + uint8_t nvgc_blk, prio; + Xive2Nvgc nvgc; + + for (prio = 0; prio <= XIVE_PRIORITY_MAX; prio++) { + current_level = first_group & 0x3F; + nvgc_blk = nvx_blk; + nvgc_idx = nvx_idx; + + while (current_level) { + xive2_pgofnext(&nvgc_blk, &nvgc_idx, current_level); + + if (xive2_router_get_nvgc(xrtr, NVx_CROWD_LVL(current_level), + nvgc_blk, nvgc_idx, &nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVGC %x/%x\n", + nvgc_blk, nvgc_idx); + return 0xFF; + } + if (!xive2_nvgc_is_valid(&nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVGC %x/%x\n", + nvgc_blk, nvgc_idx); + return 0xFF; + } + + count = xive2_nvgc_get_backlog(&nvgc, prio); + if (count) { + *out_level = current_level; + return prio; + } + current_level = xive_get_field32(NVGC2_W0_PGONEXT, nvgc.w0) & 0x3F; + } + } + return 0xFF; +} + +static void xive2_presenter_backlog_decr(XivePresenter *xptr, + uint8_t nvx_blk, uint32_t nvx_idx, + uint8_t group_prio, + uint8_t group_level) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint32_t nvgc_idx, count; + uint8_t nvgc_blk; + Xive2Nvgc nvgc; + + nvgc_blk = nvx_blk; + nvgc_idx = nvx_idx; + xive2_pgofnext(&nvgc_blk, &nvgc_idx, group_level); + + if (xive2_router_get_nvgc(xrtr, NVx_CROWD_LVL(group_level), + nvgc_blk, nvgc_idx, &nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVGC %x/%x\n", + nvgc_blk, nvgc_idx); + return; + } + if (!xive2_nvgc_is_valid(&nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid NVGC %x/%x\n", + nvgc_blk, nvgc_idx); + return; + } + count = xive2_nvgc_get_backlog(&nvgc, group_prio); + if (!count) { + return; + } + xive2_nvgc_set_backlog(&nvgc, group_prio, count - 1); + xive2_router_write_nvgc(xrtr, NVx_CROWD_LVL(group_level), + nvgc_blk, nvgc_idx, &nvgc); +} + /* * XIVE Thread Interrupt Management Area (TIMA) - Gen2 mode * @@ -182,13 +491,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", @@ -224,7 +534,19 @@ static void xive2_tctx_save_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, regs[TM_IPB]); nvp.w2 = xive_set_field32(NVP2_W2_CPPR, nvp.w2, regs[TM_CPPR]); - nvp.w2 = xive_set_field32(NVP2_W2_LSMFB, nvp.w2, regs[TM_LSMFB]); + if (nvp.w0 & NVP2_W0_L) { + /* + * Typically not used. If LSMFB is restored with 0, it will + * force a backlog rescan + */ + nvp.w2 = xive_set_field32(NVP2_W2_LSMFB, nvp.w2, regs[TM_LSMFB]); + } + if (nvp.w0 & NVP2_W0_G) { + nvp.w2 = xive_set_field32(NVP2_W2_LGS, nvp.w2, regs[TM_LGS]); + } + if (nvp.w0 & NVP2_W0_T) { + nvp.w2 = xive_set_field32(NVP2_W2_T, nvp.w2, regs[TM_T]); + } xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2); nvp.w1 = xive_set_field32(NVP2_W1_CO, nvp.w1, 0); @@ -233,44 +555,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); } -uint64_t xive2_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, - hwaddr offset, unsigned size) +/* + * 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); - uint32_t qw1w2 = xive_tctx_word2(&tctx->regs[TM_QW1_OS]); - uint32_t qw1w2_new; - uint32_t cam = be32_to_cpu(qw1w2); + 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; - bool vo; + uint8_t cur_ring; + bool valid; bool do_save; - xive2_os_cam_decode(cam, &nvp_blk, &nvp_idx, &vo, &do_save); + xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &valid, &do_save); - if (!vo) { + if (!valid) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid NVP %x/%x !?\n", nvp_blk, nvp_idx); } - /* Invalidate CAM line */ - qw1w2_new = xive_set_field32(TM2_QW1W2_VO, qw1w2, 0); - memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1w2_new, 4); + /* 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_os_ctx(xrtr, tctx, nvp_blk, nvp_idx); + 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 hw_cam, nvp_idx, xive2_cfg, reserved; + uint8_t nvp_blk; + Xive2Nvp nvp; + uint64_t phys_addr; + MemTxResult result; + + hw_cam = xive2_tctx_hw_cam_line(xptr, tctx); + nvp_blk = xive2_nvp_blk(hw_cam); + nvp_idx = xive2_nvp_idx(hw_cam); + + 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; } - xive_tctx_reset_os_signal(tctx); - return qw1w2; + if (!xive2_nvp_is_valid(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + 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, @@ -292,7 +760,9 @@ static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, nvp, 2); tctx->regs[TM_QW1_OS + TM_CPPR] = cppr; - /* we don't model LSMFB */ + tctx->regs[TM_QW1_OS + TM_LSMFB] = xive_get_field32(NVP2_W2_LSMFB, nvp->w2); + tctx->regs[TM_QW1_OS + TM_LGS] = xive_get_field32(NVP2_W2_LGS, nvp->w2); + tctx->regs[TM_QW1_OS + TM_T] = xive_get_field32(NVP2_W2_T, nvp->w2); nvp->w1 = xive_set_field32(NVP2_W1_CO, nvp->w1, 1); nvp->w1 = xive_set_field32(NVP2_W1_CO_THRID_VALID, nvp->w1, 1); @@ -315,8 +785,15 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t nvp_blk, uint32_t nvp_idx, bool do_restore) { - Xive2Nvp nvp; + XivePresenter *xptr = XIVE_PRESENTER(xrtr); uint8_t ipb; + uint8_t backlog_level; + uint8_t group_level; + uint8_t first_group; + uint8_t backlog_prio; + uint8_t group_prio; + uint8_t *regs = &tctx->regs[TM_QW1_OS]; + Xive2Nvp nvp; /* * Grab the associated thread interrupt context registers in the @@ -345,15 +822,29 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, 0); xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2); } + regs[TM_IPB] |= ipb; + backlog_prio = xive_ipb_to_pipr(ipb); + backlog_level = 0; + + first_group = xive_get_field32(NVP2_W0_PGOFIRST, nvp.w0); + if (first_group && regs[TM_LSMFB] < backlog_prio) { + group_prio = xive2_presenter_backlog_scan(xptr, nvp_blk, nvp_idx, + first_group, &group_level); + regs[TM_LSMFB] = group_prio; + if (regs[TM_LGS] && group_prio < backlog_prio) { + /* VP can take a group interrupt */ + xive2_presenter_backlog_decr(xptr, nvp_blk, nvp_idx, + group_prio, group_level); + backlog_prio = group_prio; + backlog_level = group_level; + } + } + /* - * Always call xive_tctx_ipb_update(). Even if there were no - * escalation triggered, there could be a pending interrupt which - * was saved when the context was pulled and that we need to take - * into account by recalculating the PIPR (which is not - * saved/restored). - * It will also raise the External interrupt signal if needed. + * Compute the PIPR based on the restored state. + * It will raise the External interrupt signal if needed. */ - xive_tctx_ipb_update(tctx, TM_QW1_OS, ipb); + xive_tctx_pipr_update(tctx, TM_QW1_OS, backlog_prio, backlog_level); } /* @@ -362,17 +853,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 +886,185 @@ void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, } } +static int xive2_tctx_get_nvp_indexes(XiveTCTX *tctx, uint8_t ring, + uint32_t *nvp_blk, uint32_t *nvp_idx) +{ + uint32_t w2, cam; + + w2 = xive_tctx_word2(&tctx->regs[ring]); + switch (ring) { + case TM_QW1_OS: + if (!(be32_to_cpu(w2) & TM2_QW1W2_VO)) { + return -1; + } + cam = xive_get_field32(TM2_QW1W2_OS_CAM, w2); + break; + case TM_QW2_HV_POOL: + if (!(be32_to_cpu(w2) & TM2_QW2W2_VP)) { + return -1; + } + cam = xive_get_field32(TM2_QW2W2_POOL_CAM, w2); + break; + case TM_QW3_HV_PHYS: + if (!(be32_to_cpu(w2) & TM2_QW3W2_VT)) { + return -1; + } + cam = xive2_tctx_hw_cam_line(tctx->xptr, tctx); + break; + default: + return -1; + } + *nvp_blk = xive2_nvp_blk(cam); + *nvp_idx = xive2_nvp_idx(cam); + return 0; +} + +static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) +{ + uint8_t *regs = &tctx->regs[ring]; + Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr); + uint8_t old_cppr, backlog_prio, first_group, group_level = 0; + uint8_t pipr_min, lsmfb_min, ring_min; + bool group_enabled; + uint32_t nvp_blk, nvp_idx; + Xive2Nvp nvp; + int rc; + + trace_xive_tctx_set_cppr(tctx->cs->cpu_index, ring, + regs[TM_IPB], regs[TM_PIPR], + cppr, regs[TM_NSR]); + + if (cppr > XIVE_PRIORITY_MAX) { + cppr = 0xff; + } + + old_cppr = regs[TM_CPPR]; + regs[TM_CPPR] = cppr; + + /* + * Recompute the PIPR based on local pending interrupts. It will + * be adjusted below if needed in case of pending group interrupts. + */ + pipr_min = xive_ipb_to_pipr(regs[TM_IPB]); + group_enabled = !!regs[TM_LGS]; + lsmfb_min = (group_enabled) ? regs[TM_LSMFB] : 0xff; + ring_min = ring; + + /* PHYS updates also depend on POOL values */ + if (ring == TM_QW3_HV_PHYS) { + uint8_t *pregs = &tctx->regs[TM_QW2_HV_POOL]; + + /* POOL values only matter if POOL ctx is valid */ + if (pregs[TM_WORD2] & 0x80) { + + uint8_t pool_pipr = xive_ipb_to_pipr(pregs[TM_IPB]); + uint8_t pool_lsmfb = pregs[TM_LSMFB]; + + /* + * Determine highest priority interrupt and + * remember which ring has it. + */ + if (pool_pipr < pipr_min) { + pipr_min = pool_pipr; + if (pool_pipr < lsmfb_min) { + ring_min = TM_QW2_HV_POOL; + } + } + + /* Values needed for group priority calculation */ + if (pregs[TM_LGS] && (pool_lsmfb < lsmfb_min)) { + group_enabled = true; + lsmfb_min = pool_lsmfb; + if (lsmfb_min < pipr_min) { + ring_min = TM_QW2_HV_POOL; + } + } + } + } + regs[TM_PIPR] = pipr_min; + + rc = xive2_tctx_get_nvp_indexes(tctx, ring_min, &nvp_blk, &nvp_idx); + if (rc) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: set CPPR on invalid context\n"); + return; + } + + if (cppr < old_cppr) { + /* + * FIXME: check if there's a group interrupt being presented + * and if the new cppr prevents it. If so, then the group + * interrupt needs to be re-added to the backlog and + * re-triggered (see re-trigger END info in the NVGC + * structure) + */ + } + + if (group_enabled && + lsmfb_min < cppr && + lsmfb_min < regs[TM_PIPR]) { + /* + * Thread has seen a group interrupt with a higher priority + * than the new cppr or pending local interrupt. Check the + * backlog + */ + 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; + } + + if (!xive2_nvp_is_valid(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + first_group = xive_get_field32(NVP2_W0_PGOFIRST, nvp.w0); + if (!first_group) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + backlog_prio = xive2_presenter_backlog_scan(tctx->xptr, + nvp_blk, nvp_idx, + first_group, &group_level); + tctx->regs[ring_min + TM_LSMFB] = backlog_prio; + if (backlog_prio != 0xFF) { + xive2_presenter_backlog_decr(tctx->xptr, nvp_blk, nvp_idx, + backlog_prio, group_level); + regs[TM_PIPR] = backlog_prio; + } + } + /* CPPR has changed, check if we need to raise a pending exception */ + xive_tctx_notify(tctx, ring_min, group_level); +} + +void xive2_tm_set_hv_cppr(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive2_tctx_set_cppr(tctx, TM_QW3_HV_PHYS, value & 0xff); +} + +void xive2_tm_set_os_cppr(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive2_tctx_set_cppr(tctx, TM_QW1_OS, value & 0xff); +} + +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 +1127,63 @@ 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); +} + +static bool xive2_vp_match_mask(uint32_t cam1, uint32_t cam2, + uint32_t vp_mask) +{ + return (cam1 & vp_mask) == (cam2 & vp_mask); +} + +static uint8_t xive2_get_vp_block_mask(uint32_t nvt_blk, bool crowd) +{ + uint8_t block_mask = 0b1111; + + /* 3 supported crowd sizes: 2, 4, 16 */ + if (crowd) { + uint32_t size = xive_get_vpgroup_size(nvt_blk); + + if (size != 2 && size != 4 && size != 16) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid crowd size of %d", + size); + return block_mask; + } + block_mask &= ~(size - 1); + } + return block_mask; +} + +static uint32_t xive2_get_vp_index_mask(uint32_t nvt_index, bool cam_ignore) +{ + uint32_t index_mask = 0xFFFFFF; /* 24 bits */ + + if (cam_ignore) { + uint32_t size = xive_get_vpgroup_size(nvt_index); + + if (size < 2) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid group size of %d", + size); + return index_mask; + } + index_mask &= ~(size - 1); + } + return index_mask; } /* @@ -476,7 +1192,8 @@ static uint32_t xive2_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint32_t logic_serv) + bool crowd, bool cam_ignore, + uint32_t logic_serv) { uint32_t cam = xive2_nvp_cam_line(nvt_blk, nvt_idx); uint32_t qw3w2 = xive_tctx_word2(&tctx->regs[TM_QW3_HV_PHYS]); @@ -484,44 +1201,51 @@ int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, uint32_t qw1w2 = xive_tctx_word2(&tctx->regs[TM_QW1_OS]); uint32_t qw0w2 = xive_tctx_word2(&tctx->regs[TM_QW0_USER]); - /* - * TODO (PowerNV): ignore mode. The low order bits of the NVT - * identifier are ignored in the "CAM" match. - */ + uint32_t index_mask, vp_mask; + uint8_t block_mask; if (format == 0) { - if (cam_ignore == true) { - /* - * F=0 & i=1: Logical server notification (bits ignored at - * the end of the NVT identifier) - */ - qemu_log_mask(LOG_UNIMP, "XIVE: no support for LS NVT %x/%x\n", - nvt_blk, nvt_idx); - return -1; - } + /* + * i=0: Specific NVT notification + * i=1: VP-group notification (bits ignored at the end of the + * NVT identifier) + */ + block_mask = xive2_get_vp_block_mask(nvt_blk, crowd); + index_mask = xive2_get_vp_index_mask(nvt_idx, cam_ignore); + vp_mask = xive2_nvp_cam_line(block_mask, index_mask); - /* F=0 & i=0: Specific NVT notification */ + /* For VP-group notifications, threads with LGS=0 are excluded */ /* PHYS ring */ if ((be32_to_cpu(qw3w2) & TM2_QW3W2_VT) && - cam == xive2_tctx_hw_cam_line(xptr, tctx)) { + !(cam_ignore && tctx->regs[TM_QW3_HV_PHYS + TM_LGS] == 0) && + xive2_vp_match_mask(cam, + xive2_tctx_hw_cam_line(xptr, tctx), + vp_mask)) { return TM_QW3_HV_PHYS; } /* HV POOL ring */ if ((be32_to_cpu(qw2w2) & TM2_QW2W2_VP) && - cam == xive_get_field32(TM2_QW2W2_POOL_CAM, qw2w2)) { + !(cam_ignore && tctx->regs[TM_QW2_HV_POOL + TM_LGS] == 0) && + xive2_vp_match_mask(cam, + xive_get_field32(TM2_QW2W2_POOL_CAM, qw2w2), + vp_mask)) { return TM_QW2_HV_POOL; } /* OS ring */ if ((be32_to_cpu(qw1w2) & TM2_QW1W2_VO) && - cam == xive_get_field32(TM2_QW1W2_OS_CAM, qw1w2)) { + !(cam_ignore && tctx->regs[TM_QW1_OS + TM_LGS] == 0) && + xive2_vp_match_mask(cam, + xive_get_field32(TM2_QW1W2_OS_CAM, qw1w2), + vp_mask)) { return TM_QW1_OS; } } else { /* F=1 : User level Event-Based Branch (EBB) notification */ + /* FIXME: what if cam_ignore and LGS = 0 ? */ /* USER ring */ if ((be32_to_cpu(qw1w2) & TM2_QW1W2_VO) && (cam == xive_get_field32(TM2_QW1W2_OS_CAM, qw1w2)) && @@ -533,6 +1257,37 @@ int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, return -1; } +bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority) +{ + /* 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]; + + /* + * The xive2_presenter_tctx_match() above tells if there's a match + * but for VP-group notification, we still need to look at the + * priority to know if the thread can take the interrupt now or if + * it is precluded. + */ + if (priority < alt_regs[TM_CPPR]) { + return false; + } + return true; +} + +void xive2_tm_set_lsmfb(XiveTCTX *tctx, int ring, uint8_t priority) +{ + uint8_t *regs = &tctx->regs[ring]; + + /* + * Called by the router during a VP-group notification when the + * thread matches but can't take the interrupt because it's + * already running at a more favored priority. It then stores the + * new interrupt priority in the LSMFB field. + */ + regs[TM_LSMFB] = priority; +} + static void xive2_router_realize(DeviceState *dev, Error **errp) { Xive2Router *xrtr = XIVE2_ROUTER(dev); @@ -542,7 +1297,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, @@ -572,10 +1327,9 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, Xive2End end; uint8_t priority; uint8_t format; - bool found; - Xive2Nvp nvp; - uint8_t nvp_blk; - uint32_t nvp_idx; + bool found, precluded; + uint8_t nvx_blk; + uint32_t nvx_idx; /* END cache lookup */ if (xive2_router_get_end(xrtr, end_blk, end_idx, &end)) { @@ -590,6 +1344,12 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, return; } + if (xive2_end_is_crowd(&end) && !xive2_end_is_ignore(&end)) { + qemu_log_mask(LOG_GUEST_ERROR, + "XIVE: invalid END, 'crowd' bit requires 'ignore' bit\n"); + return; + } + if (xive2_end_is_enqueue(&end)) { xive2_end_enqueue(&end, end_data); /* Enqueuing event data modifies the EQ toggle and index */ @@ -621,7 +1381,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 */ @@ -634,26 +1394,14 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, /* * Follows IVPE notification */ - nvp_blk = xive_get_field32(END2_W6_VP_BLOCK, end.w6); - nvp_idx = xive_get_field32(END2_W6_VP_OFFSET, end.w6); - - /* NVP cache lookup */ - 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; - } + nvx_blk = xive_get_field32(END2_W6_VP_BLOCK, end.w6); + nvx_idx = xive_get_field32(END2_W6_VP_OFFSET, end.w6); - if (!xive2_nvp_is_valid(&nvp)) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVP %x/%x is invalid\n", - nvp_blk, nvp_idx); - return; - } - - found = xive_presenter_notify(xrtr->xfb, format, nvp_blk, nvp_idx, - xive_get_field32(END2_W6_IGNORE, end.w7), + found = xive_presenter_notify(xrtr->xfb, format, nvx_blk, nvx_idx, + xive2_end_is_crowd(&end), xive2_end_is_ignore(&end), priority, - xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7)); + xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7), + &precluded); /* TODO: Auto EOI. */ @@ -664,10 +1412,9 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, /* * If no matching NVP is dispatched on a HW thread : * - specific VP: update the NVP structure if backlog is activated - * - logical server : forward request to IVPE (not supported) + * - VP-group: update the backlog counter for that priority in the NVG */ if (xive2_end_is_backlog(&end)) { - uint8_t ipb; if (format == 1) { qemu_log_mask(LOG_GUEST_ERROR, @@ -676,19 +1423,82 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, return; } - /* - * Record the IPB in the associated NVP structure for later - * use. The presenter will resend the interrupt when the vCPU - * is dispatched again on a HW thread. - */ - ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2) | - xive_priority_to_ipb(priority); - nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, ipb); - xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2); + if (!xive2_end_is_ignore(&end)) { + uint8_t ipb; + Xive2Nvp nvp; - /* - * On HW, follows a "Broadcast Backlog" to IVPEs - */ + /* NVP cache lookup */ + if (xive2_router_get_nvp(xrtr, nvx_blk, nvx_idx, &nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: no NVP %x/%x\n", + nvx_blk, nvx_idx); + return; + } + + if (!xive2_nvp_is_valid(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVP %x/%x is invalid\n", + nvx_blk, nvx_idx); + return; + } + + /* + * Record the IPB in the associated NVP structure for later + * use. The presenter will resend the interrupt when the vCPU + * is dispatched again on a HW thread. + */ + ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2) | + xive_priority_to_ipb(priority); + nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, ipb); + xive2_router_write_nvp(xrtr, nvx_blk, nvx_idx, &nvp, 2); + } else { + Xive2Nvgc nvgc; + uint32_t backlog; + bool crowd; + + crowd = xive2_end_is_crowd(&end); + + /* + * For groups and crowds, the per-priority backlog + * counters are stored in the NVG/NVC structures + */ + if (xive2_router_get_nvgc(xrtr, crowd, + nvx_blk, nvx_idx, &nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: no %s %x/%x\n", + crowd ? "NVC" : "NVG", nvx_blk, nvx_idx); + return; + } + + if (!xive2_nvgc_is_valid(&nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVG %x/%x is invalid\n", + nvx_blk, nvx_idx); + return; + } + + /* + * Increment the backlog counter for that priority. + * We only call broadcast the first time the counter is + * incremented. broadcast will set the LSMFB field of the TIMA of + * relevant threads so that they know an interrupt is pending. + */ + backlog = xive2_nvgc_get_backlog(&nvgc, priority) + 1; + xive2_nvgc_set_backlog(&nvgc, priority, backlog); + xive2_router_write_nvgc(xrtr, crowd, nvx_blk, nvx_idx, &nvgc); + + if (backlog == 1) { + XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xrtr->xfb); + xfc->broadcast(xrtr->xfb, nvx_blk, nvx_idx, + xive2_end_is_crowd(&end), + xive2_end_is_ignore(&end), + priority); + + if (!xive2_end_is_precluded_escalation(&end)) { + /* + * The interrupt will be picked up when the + * matching thread lowers its priority level + */ + return; + } + } + } } do_escalation: @@ -702,7 +1512,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, /* * 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 */ @@ -775,13 +1585,12 @@ void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) xive_get_field64(EAS2_END_DATA, eas.w)); } -static Property xive2_router_properties[] = { +static const Property xive2_router_properties[] = { DEFINE_PROP_LINK("xive-fabric", Xive2Router, xfb, TYPE_XIVE_FABRIC, XiveFabric *), - DEFINE_PROP_END_OF_LIST(), }; -static void xive2_router_class_init(ObjectClass *klass, void *data) +static void xive2_router_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); XiveNotifierClass *xnc = XIVE_NOTIFIER_CLASS(klass); @@ -800,7 +1609,7 @@ static const TypeInfo xive2_router_info = { .instance_size = sizeof(Xive2Router), .class_size = sizeof(Xive2RouterClass), .class_init = xive2_router_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XIVE_NOTIFIER }, { TYPE_XIVE_PRESENTER }, { } @@ -989,15 +1798,14 @@ static void xive2_end_source_realize(DeviceState *dev, Error **errp) (1ull << (xsrc->esb_shift + 1)) * xsrc->nr_ends); } -static Property xive2_end_source_properties[] = { +static const Property xive2_end_source_properties[] = { DEFINE_PROP_UINT32("nr-ends", Xive2EndSource, nr_ends, 0), DEFINE_PROP_UINT32("shift", Xive2EndSource, esb_shift, XIVE_ESB_64K), DEFINE_PROP_LINK("xive", Xive2EndSource, xrtr, TYPE_XIVE2_ROUTER, Xive2Router *), - DEFINE_PROP_END_OF_LIST(), }; -static void xive2_end_source_class_init(ObjectClass *klass, void *data) +static void xive2_end_source_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/xlnx-pmu-iomod-intc.c b/hw/intc/xlnx-pmu-iomod-intc.c index acaa1c3e6f7..9200585e325 100644 --- a/hw/intc/xlnx-pmu-iomod-intc.c +++ b/hw/intc/xlnx-pmu-iomod-intc.c @@ -474,11 +474,10 @@ static const MemoryRegionOps xlnx_pmu_io_intc_ops = { }, }; -static Property xlnx_pmu_io_intc_properties[] = { +static const Property xlnx_pmu_io_intc_properties[] = { DEFINE_PROP_UINT32("intc-intr-size", XlnxPMUIOIntc, cfg.intr_size, 0), DEFINE_PROP_UINT32("intc-level-edge", XlnxPMUIOIntc, cfg.level_edge, 0), DEFINE_PROP_UINT32("intc-positive", XlnxPMUIOIntc, cfg.positive, 0), - DEFINE_PROP_END_OF_LIST(), }; static void xlnx_pmu_io_intc_realize(DeviceState *dev, Error **errp) @@ -526,17 +525,17 @@ 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(), } }; -static void xlnx_pmu_io_intc_class_init(ObjectClass *klass, void *data) +static void xlnx_pmu_io_intc_class_init(ObjectClass *klass, const 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 adc11790141..610cd0e3164 100644 --- a/hw/intc/xlnx-zynqmp-ipi.c +++ b/hw/intc/xlnx-zynqmp-ipi.c @@ -349,17 +349,17 @@ 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(), } }; -static void xlnx_zynqmp_ipi_class_init(ObjectClass *klass, void *data) +static void xlnx_zynqmp_ipi_class_init(ObjectClass *klass, const 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/Kconfig b/hw/ipack/Kconfig index f8da24a62be..28d668727c8 100644 --- a/hw/ipack/Kconfig +++ b/hw/ipack/Kconfig @@ -1,4 +1,8 @@ config IPACK bool + +config TPCI200 + bool + select IPACK default y if PCI_DEVICES depends on PCI diff --git a/hw/ipack/ipack.c b/hw/ipack/ipack.c index ae20f36da68..ab602bff734 100644 --- a/hw/ipack/ipack.c +++ b/hw/ipack/ipack.c @@ -55,30 +55,26 @@ static void ipack_device_realize(DeviceState *dev, Error **errp) } bus->free_slot = idev->slot + 1; - idev->irq = qemu_allocate_irqs(bus->set_irq, idev, 2); + qemu_init_irqs(idev->irq, ARRAY_SIZE(idev->irq), bus->set_irq, idev); k->realize(dev, errp); } static void ipack_device_unrealize(DeviceState *dev) { - IPackDevice *idev = IPACK_DEVICE(dev); IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev); if (k->unrealize) { k->unrealize(dev); return; } - - qemu_free_irqs(idev->irq, 2); } -static Property ipack_device_props[] = { +static const Property ipack_device_props[] = { DEFINE_PROP_INT32("slot", IPackDevice, slot, -1), - DEFINE_PROP_END_OF_LIST() }; -static void ipack_device_class_init(ObjectClass *klass, void *data) +static void ipack_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); @@ -93,7 +89,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 26567f1068e..e4805228926 100644 --- a/hw/ipack/meson.build +++ b/hw/ipack/meson.build @@ -1 +1,2 @@ -system_ss.add(when: 'CONFIG_IPACK', if_true: files('ipack.c', 'tpci200.c')) +system_ss.add(when: 'CONFIG_IPACK', if_true: files('ipack.c')) +system_ss.add(when: 'CONFIG_TPCI200', if_true: files('tpci200.c')) diff --git a/hw/ipack/tpci200.c b/hw/ipack/tpci200.c index 6b3edbf0176..40b30517c75 100644 --- a/hw/ipack/tpci200.c +++ b/hw/ipack/tpci200.c @@ -275,11 +275,11 @@ static void tpci200_write_las0(void *opaque, hwaddr addr, uint64_t val, if (ip != NULL) { if (val & STATUS_INT(i, 0)) { DPRINTF("Clear IP %c INT0# status\n", 'A' + i); - qemu_irq_lower(ip->irq[0]); + qemu_irq_lower(&ip->irq[0]); } if (val & STATUS_INT(i, 1)) { DPRINTF("Clear IP %c INT1# status\n", 'A' + i); - qemu_irq_lower(ip->irq[1]); + qemu_irq_lower(&ip->irq[1]); } } @@ -344,7 +344,7 @@ static uint64_t tpci200_read_las1(void *opaque, hwaddr addr, unsigned size) bool int_set = s->status & STATUS_INT(ip_n, intno); bool int_edge_sensitive = s->ctrl[ip_n] & CTRL_INT_EDGE(intno); if (int_set && !int_edge_sensitive) { - qemu_irq_lower(ip->irq[intno]); + qemu_irq_lower(&ip->irq[intno]); } } @@ -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), @@ -629,7 +629,7 @@ static const VMStateDescription vmstate_tpci200 = { } }; -static void tpci200_class_init(ObjectClass *klass, void *data) +static void tpci200_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -650,7 +650,7 @@ static const TypeInfo tpci200_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(TPCI200State), .class_init = tpci200_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c index bbb07b151e9..b91e487e1b8 100644 --- a/hw/ipmi/ipmi.c +++ b/hw/ipmi/ipmi.c @@ -26,7 +26,7 @@ #include "hw/ipmi/ipmi.h" #include "hw/qdev-properties.h" #include "qom/object_interfaces.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "qapi/error.h" #include "qemu/module.h" #include "hw/nmi.h" @@ -78,7 +78,7 @@ static int ipmi_do_hw_op(IPMIInterface *s, enum ipmi_op op, int checkonly) } } -static void ipmi_interface_class_init(ObjectClass *class, void *data) +static void ipmi_interface_class_init(ObjectClass *class, const void *data) { IPMIInterfaceClass *ik = IPMI_INTERFACE_CLASS(class); @@ -108,12 +108,11 @@ void ipmi_bmc_find_and_link(Object *obj, Object **bmc) OBJ_PROP_LINK_STRONG); } -static Property ipmi_bmc_properties[] = { +static const Property ipmi_bmc_properties[] = { DEFINE_PROP_UINT8("slave_addr", IPMIBmc, slave_addr, 0x20), - DEFINE_PROP_END_OF_LIST(), }; -static void bmc_class_init(ObjectClass *oc, void *data) +static void bmc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index acf2bab35fa..9f1ba7b2f87 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -142,7 +142,6 @@ static void continue_send(IPMIBmcExtern *ibe) qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4000000000ULL); } } - return; } static void extern_timeout(void *opaque) @@ -214,7 +213,7 @@ static void ipmi_bmc_extern_handle_command(IPMIBmc *b, rsp[2] = err; ibe->waiting_rsp = false; k->handle_rsp(s, msg_id, rsp, 3); - goto out; + return; } addchar(ibe, msg_id); @@ -229,9 +228,6 @@ static void ipmi_bmc_extern_handle_command(IPMIBmc *b, /* Start the transmit */ continue_send(ibe); - - out: - return; } static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op) @@ -301,7 +297,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 +449,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 +475,31 @@ 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); +} + 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) @@ -514,12 +509,11 @@ static void ipmi_bmc_extern_finalize(Object *obj) timer_free(ibe->extern_timer); } -static Property ipmi_bmc_extern_properties[] = { +static const Property ipmi_bmc_extern_properties[] = { DEFINE_PROP_CHR("chardev", IPMIBmcExtern, chr), - DEFINE_PROP_END_OF_LIST(), }; -static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data) +static void ipmi_bmc_extern_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); IPMIBmcClass *bk = IPMI_BMC_CLASS(oc); @@ -528,6 +522,7 @@ static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data) bk->handle_reset = ipmi_bmc_extern_handle_reset; dc->hotpluggable = false; dc->realize = ipmi_bmc_extern_realize; + dc->vmsd = &vmstate_ipmi_bmc_extern; device_class_set_props(dc, ipmi_bmc_extern_properties); } diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 905e091094b..04e1dcd0e76 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -23,7 +23,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/timer.h" #include "hw/ipmi/ipmi.h" #include "qemu/error-report.h" @@ -70,6 +70,7 @@ #define IPMI_CMD_GET_MSG 0x33 #define IPMI_CMD_SEND_MSG 0x34 #define IPMI_CMD_READ_EVT_MSG_BUF 0x35 +#define IPMI_CMD_GET_CHANNEL_INFO 0x42 #define IPMI_NETFN_STORAGE 0x0a @@ -234,6 +235,7 @@ struct IPMIBmcSim { #define IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(s) \ (IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE & (s)->msg_flags) +#define IPMI_BMC_GLOBAL_ENABLES_SUPPORTED 0x0f #define IPMI_BMC_RCV_MSG_QUEUE_INT_BIT 0 #define IPMI_BMC_EVBUF_FULL_INT_BIT 1 #define IPMI_BMC_EVENT_MSG_BUF_BIT 2 @@ -463,14 +465,12 @@ void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log) } if (ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL) { - goto out; + return; } memcpy(ibs->evtbuf, evt, 16); ibs->msg_flags |= IPMI_BMC_MSG_FLAG_EVT_BUF_FULL; k->set_atn(s, 1, attn_irq_enabled(ibs)); - out: - return; } static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert, uint8_t evd1, uint8_t evd2, uint8_t evd3) @@ -513,7 +513,8 @@ static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert, static void sensor_set_discrete_bit(IPMIBmcSim *ibs, unsigned int sensor, unsigned int bit, unsigned int val, - uint8_t evd1, uint8_t evd2, uint8_t evd3) + uint8_t evd1, uint8_t evd2, uint8_t evd3, + bool do_log) { IPMISensor *sens; uint16_t mask; @@ -533,7 +534,7 @@ static void sensor_set_discrete_bit(IPMIBmcSim *ibs, unsigned int sensor, return; /* Already asserted */ } sens->assert_states |= mask & sens->assert_suppt; - if (sens->assert_enable & mask & sens->assert_states) { + if (do_log && (sens->assert_enable & mask & sens->assert_states)) { /* Send an event on assert */ gen_event(ibs, sensor, 0, evd1, evd2, evd3); } @@ -543,7 +544,7 @@ static void sensor_set_discrete_bit(IPMIBmcSim *ibs, unsigned int sensor, return; /* Already deasserted */ } sens->deassert_states |= mask & sens->deassert_suppt; - if (sens->deassert_enable & mask & sens->deassert_states) { + if (do_log && (sens->deassert_enable & mask & sens->deassert_states)) { /* Send an event on deassert */ gen_event(ibs, sensor, 1, evd1, evd2, evd3); } @@ -699,6 +700,7 @@ static void ipmi_sim_handle_timeout(IPMIBmcSim *ibs) { IPMIInterface *s = ibs->parent.intf; IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + bool do_log = !IPMI_BMC_WATCHDOG_GET_DONT_LOG(ibs); if (!ibs->watchdog_running) { goto out; @@ -710,14 +712,16 @@ static void ipmi_sim_handle_timeout(IPMIBmcSim *ibs) ibs->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK; k->do_hw_op(s, IPMI_SEND_NMI, 0); sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 8, 1, - 0xc8, (2 << 4) | 0xf, 0xff); + 0xc8, (2 << 4) | 0xf, 0xff, + do_log); break; case IPMI_BMC_WATCHDOG_PRE_MSG_INT: ibs->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK; k->set_atn(s, 1, attn_irq_enabled(ibs)); sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 8, 1, - 0xc8, (3 << 4) | 0xf, 0xff); + 0xc8, (3 << 4) | 0xf, 0xff, + do_log); break; default: @@ -737,24 +741,28 @@ static void ipmi_sim_handle_timeout(IPMIBmcSim *ibs) switch (IPMI_BMC_WATCHDOG_GET_ACTION(ibs)) { case IPMI_BMC_WATCHDOG_ACTION_NONE: sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 0, 1, - 0xc0, ibs->watchdog_use & 0xf, 0xff); + 0xc0, ibs->watchdog_use & 0xf, 0xff, + do_log); break; case IPMI_BMC_WATCHDOG_ACTION_RESET: sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 1, 1, - 0xc1, ibs->watchdog_use & 0xf, 0xff); + 0xc1, ibs->watchdog_use & 0xf, 0xff, + do_log); k->do_hw_op(s, IPMI_RESET_CHASSIS, 0); break; case IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN: sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 2, 1, - 0xc2, ibs->watchdog_use & 0xf, 0xff); + 0xc2, ibs->watchdog_use & 0xf, 0xff, + do_log); k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0); break; case IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE: sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 2, 1, - 0xc3, ibs->watchdog_use & 0xf, 0xff); + 0xc3, ibs->watchdog_use & 0xf, 0xff, + do_log); k->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 0); break; } @@ -925,7 +933,14 @@ static void set_bmc_global_enables(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, RspBuffer *rsp) { - set_global_enables(ibs, cmd[2]); + uint8_t val = cmd[2]; + + if (val & ~IPMI_BMC_GLOBAL_ENABLES_SUPPORTED) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + set_global_enables(ibs, val); } static void get_bmc_global_enables(IPMIBmcSim *ibs, @@ -980,7 +995,7 @@ static void get_msg(IPMIBmcSim *ibs, if (QTAILQ_EMPTY(&ibs->rcvbufs)) { rsp_buffer_set_error(rsp, 0x80); /* Queue empty */ - goto out; + return; } rsp_buffer_push(rsp, 0); /* Channel 0 */ msg = QTAILQ_FIRST(&ibs->rcvbufs); @@ -995,9 +1010,6 @@ static void get_msg(IPMIBmcSim *ibs, ibs->msg_flags &= ~IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE; k->set_atn(s, attn_set(ibs), attn_irq_enabled(ibs)); } - -out: - return; } static unsigned char @@ -1020,8 +1032,8 @@ static void send_msg(IPMIBmcSim *ibs, uint8_t *buf; uint8_t netfn, rqLun, rsLun, rqSeq; - if (cmd[2] != 0) { - /* We only handle channel 0 with no options */ + if (cmd[2] != IPMI_CHANNEL_IPMB) { + /* We only handle channel 0h (IPMB) with no options */ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); return; } @@ -1219,6 +1231,68 @@ static void get_watchdog_timer(IPMIBmcSim *ibs, } } +static void get_channel_info(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + IPMIFwInfo info = {}; + uint8_t ch = cmd[2] & 0x0f; + + /* Only define channel 0h (IPMB) and Fh (system interface) */ + + if (ch == 0x0e) { /* "This channel" */ + ch = IPMI_CHANNEL_SYSTEM; + } + rsp_buffer_push(rsp, ch); + + if (ch != IPMI_CHANNEL_IPMB && ch != IPMI_CHANNEL_SYSTEM) { + /* Not a supported channel */ + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + if (k->get_fwinfo) { + k->get_fwinfo(s, &info); + } + + if (ch == IPMI_CHANNEL_IPMB) { + rsp_buffer_push(rsp, IPMI_CHANNEL_MEDIUM_IPMB); + rsp_buffer_push(rsp, IPMI_CHANNEL_PROTOCOL_IPMB); + } else { /* IPMI_CHANNEL_SYSTEM */ + rsp_buffer_push(rsp, IPMI_CHANNEL_MEDIUM_SYSTEM); + rsp_buffer_push(rsp, info.ipmi_channel_protocol); + } + + rsp_buffer_push(rsp, 0x00); /* Session-less */ + + /* IPMI Enterprise Number for Vendor ID */ + rsp_buffer_push(rsp, 0xf2); + rsp_buffer_push(rsp, 0x1b); + rsp_buffer_push(rsp, 0x00); + + if (ch == IPMI_CHANNEL_SYSTEM) { + uint8_t irq; + + if (info.irq_source == IPMI_ISA_IRQ) { + irq = info.interrupt_number; + } else if (info.irq_source == IPMI_PCI_IRQ) { + irq = 0x10 + info.interrupt_number; + } else { + irq = 0xff; /* no interrupt / unspecified */ + } + + /* Both interrupts use the same irq number */ + rsp_buffer_push(rsp, irq); + rsp_buffer_push(rsp, irq); + } else { + /* Reserved */ + rsp_buffer_push(rsp, 0x00); + rsp_buffer_push(rsp, 0x00); + } +} + static void get_sdr_rep_info(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, RspBuffer *rsp) @@ -2015,6 +2089,7 @@ static const IPMICmdHandler app_cmds[] = { [IPMI_CMD_RESET_WATCHDOG_TIMER] = { reset_watchdog_timer }, [IPMI_CMD_SET_WATCHDOG_TIMER] = { set_watchdog_timer, 8 }, [IPMI_CMD_GET_WATCHDOG_TIMER] = { get_watchdog_timer }, + [IPMI_CMD_GET_CHANNEL_INFO] = { get_channel_info, 3 }, }; static const IPMINetfn app_netfn = { .cmd_nums = ARRAY_SIZE(app_cmds), @@ -2103,7 +2178,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), @@ -2187,11 +2262,9 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp) register_cmds(ibs); ibs->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ipmi_timeout, ibs); - - vmstate_register(NULL, 0, &vmstate_ipmi_sim, ibs); } -static Property ipmi_sim_properties[] = { +static const Property ipmi_sim_properties[] = { DEFINE_PROP_UINT16("fruareasize", IPMIBmcSim, fru.areasize, 1024), DEFINE_PROP_STRING("frudatafile", IPMIBmcSim, fru.filename), DEFINE_PROP_STRING("sdrfile", IPMIBmcSim, sdr_filename), @@ -2203,16 +2276,16 @@ static Property ipmi_sim_properties[] = { DEFINE_PROP_UINT32("mfg_id", IPMIBmcSim, mfg_id, 0), DEFINE_PROP_UINT16("product_id", IPMIBmcSim, product_id, 0), DEFINE_PROP_UUID_NODEFAULT("guid", IPMIBmcSim, uuid), - DEFINE_PROP_END_OF_LIST(), }; -static void ipmi_sim_class_init(ObjectClass *oc, void *data) +static void ipmi_sim_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); IPMIBmcClass *bk = IPMI_BMC_CLASS(oc); dc->hotpluggable = false; dc->realize = ipmi_sim_realize; + dc->vmsd = &vmstate_ipmi_sim; device_class_set_props(dc, ipmi_sim_properties); bk->handle_command = ipmi_sim_handle_command; } diff --git a/hw/ipmi/ipmi_bt.c b/hw/ipmi/ipmi_bt.c index 22f94fb98da..e01d02fe528 100644 --- a/hw/ipmi/ipmi_bt.c +++ b/hw/ipmi/ipmi_bt.c @@ -98,14 +98,14 @@ static void ipmi_bt_handle_event(IPMIInterface *ii) IPMIBT *ib = iic->get_backend_data(ii); if (ib->inlen < 4) { - goto out; + return; } /* Note that overruns are handled by handle_command */ if (ib->inmsg[0] != (ib->inlen - 1)) { /* Length mismatch, just ignore. */ IPMI_BT_SET_BBUSY(ib->control_reg, 1); ib->inlen = 0; - goto out; + return; } if ((ib->inmsg[1] == (IPMI_NETFN_APP << 2)) && (ib->inmsg[3] == IPMI_CMD_GET_BT_INTF_CAP)) { @@ -136,7 +136,7 @@ static void ipmi_bt_handle_event(IPMIInterface *ii) IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); ipmi_bt_raise_irq(ib); } - goto out; + return; } ib->waiting_seq = ib->inmsg[2]; ib->inmsg[2] = ib->inmsg[1]; @@ -145,8 +145,6 @@ static void ipmi_bt_handle_event(IPMIInterface *ii) bk->handle_command(ib->bmc, ib->inmsg + 2, ib->inlen - 2, sizeof(ib->inmsg), ib->waiting_rsp); } - out: - return; } static void ipmi_bt_handle_rsp(IPMIInterface *ii, uint8_t msg_id, @@ -396,7 +394,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), @@ -419,6 +417,8 @@ void ipmi_bt_get_fwinfo(struct IPMIBT *ib, IPMIFwInfo *info) info->interface_type = IPMI_SMBIOS_BT; info->ipmi_spec_major_revision = 2; info->ipmi_spec_minor_revision = 0; + /* BT System Interface Format, IPMI v1.5 */ + info->ipmi_channel_protocol = IPMI_CHANNEL_PROTOCOL_BT_15; info->base_address = ib->io_base; info->register_length = ib->io_length; info->register_spacing = 1; diff --git a/hw/ipmi/ipmi_kcs.c b/hw/ipmi/ipmi_kcs.c index a77612946a5..d5cfe6c0689 100644 --- a/hw/ipmi/ipmi_kcs.c +++ b/hw/ipmi/ipmi_kcs.c @@ -168,7 +168,7 @@ static void ipmi_kcs_handle_event(IPMIInterface *ii) ik->outpos = 0; bk->handle_command(ik->bmc, ik->inmsg, ik->inlen, sizeof(ik->inmsg), ik->waiting_rsp); - goto out_noibf; + return; } else if (ik->cmd_reg == IPMI_KCS_WRITE_END_CMD) { ik->cmd_reg = -1; ik->write_end = 1; @@ -197,8 +197,6 @@ static void ipmi_kcs_handle_event(IPMIInterface *ii) ik->cmd_reg = -1; ik->data_in_reg = -1; IPMI_KCS_SET_IBF(ik->status_reg, 0); - out_noibf: - return; } static void ipmi_kcs_handle_rsp(IPMIInterface *ii, uint8_t msg_id, @@ -379,7 +377,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 */ @@ -405,6 +403,7 @@ void ipmi_kcs_get_fwinfo(IPMIKCS *ik, IPMIFwInfo *info) info->interface_type = IPMI_SMBIOS_KCS; info->ipmi_spec_major_revision = 2; info->ipmi_spec_minor_revision = 0; + info->ipmi_channel_protocol = IPMI_CHANNEL_PROTOCOL_KCS; info->base_address = ik->io_base; info->i2c_slave_address = ik->bmc->slave_addr; info->register_length = ik->io_length; diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index a83e7243d6c..0ad91ccf689 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -49,6 +49,7 @@ static void isa_ipmi_bt_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info) ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii); ipmi_bt_get_fwinfo(&iib->bt, info); + info->irq_source = IPMI_ISA_IRQ; info->interrupt_number = iib->isairq; info->i2c_slave_address = iib->bt.bmc->slave_addr; info->uuid = iib->uuid; @@ -68,6 +69,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; @@ -104,28 +120,11 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) 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() - } -}; - 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) @@ -135,19 +134,19 @@ static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii) return &iib->bt; } -static Property ipmi_isa_properties[] = { +static const Property ipmi_isa_properties[] = { DEFINE_PROP_UINT32("ioport", ISAIPMIBTDevice, bt.io_base, 0xe4), DEFINE_PROP_INT32("irq", ISAIPMIBTDevice, isairq, 5), - DEFINE_PROP_END_OF_LIST(), }; -static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data) +static void isa_ipmi_bt_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(oc); dc->realize = isa_ipmi_bt_realize; + dc->vmsd = &vmstate_ISAIPMIBTDevice; device_class_set_props(dc, ipmi_isa_properties); iic->get_backend_data = isa_ipmi_bt_get_backend_data; @@ -162,7 +161,7 @@ static const TypeInfo isa_ipmi_bt_info = { .instance_size = sizeof(ISAIPMIBTDevice), .instance_init = isa_ipmi_bt_init, .class_init = isa_ipmi_bt_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_IPMI_INTERFACE }, { TYPE_ACPI_DEV_AML_IF }, { } diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index b2ed70b9da5..418d234e0f1 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -49,6 +49,7 @@ static void isa_ipmi_kcs_get_fwinfo(IPMIInterface *ii, IPMIFwInfo *info) ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii); ipmi_kcs_get_fwinfo(&iik->kcs, info); + info->irq_source = IPMI_ISA_IRQ; info->interrupt_number = iik->isairq; info->uuid = iik->uuid; } @@ -67,6 +68,28 @@ 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; +} + +/* + * Version 1 had an incorrect name, it clashed with the BT IPMI + * device, so receive it, but transmit a different version. + */ +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; @@ -103,36 +126,11 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp) 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 - * IPMI device, so receive it, but transmit a different - * version. - */ - vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik); } static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii) @@ -142,19 +140,19 @@ static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii) return &iik->kcs; } -static Property ipmi_isa_properties[] = { +static const Property ipmi_isa_properties[] = { DEFINE_PROP_UINT32("ioport", ISAIPMIKCSDevice, kcs.io_base, 0xca2), DEFINE_PROP_INT32("irq", ISAIPMIKCSDevice, isairq, 5), - DEFINE_PROP_END_OF_LIST(), }; -static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data) +static void isa_ipmi_kcs_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(oc); dc->realize = ipmi_isa_realize; + dc->vmsd = &vmstate_ISAIPMIKCSDevice; device_class_set_props(dc, ipmi_isa_properties); iic->get_backend_data = isa_ipmi_kcs_get_backend_data; @@ -169,7 +167,7 @@ static const TypeInfo isa_ipmi_kcs_info = { .instance_size = sizeof(ISAIPMIKCSDevice), .instance_init = isa_ipmi_kcs_init, .class_init = isa_ipmi_kcs_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_IPMI_INTERFACE }, { TYPE_ACPI_DEV_AML_IF }, { } diff --git a/hw/ipmi/pci_ipmi_bt.c b/hw/ipmi/pci_ipmi_bt.c index 633931b8257..905101dcafd 100644 --- a/hw/ipmi/pci_ipmi_bt.c +++ b/hw/ipmi/pci_ipmi_bt.c @@ -38,56 +38,67 @@ struct PCIIPMIBTDevice { uint32_t uuid; }; -static void pci_ipmi_raise_irq(IPMIBT *ik) +static void pci_ipmi_bt_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info) { - PCIIPMIBTDevice *pik = ik->opaque; + PCIIPMIBTDevice *pib = PCI_IPMI_BT(ii); - pci_set_irq(&pik->dev, true); + ipmi_bt_get_fwinfo(&pib->bt, info); + info->irq_source = IPMI_PCI_IRQ; + info->interrupt_number = pci_intx(&pib->dev); + info->i2c_slave_address = pib->bt.bmc->slave_addr; + info->uuid = pib->uuid; } -static void pci_ipmi_lower_irq(IPMIBT *ik) +static void pci_ipmi_raise_irq(IPMIBT *ib) { - PCIIPMIBTDevice *pik = ik->opaque; + PCIIPMIBTDevice *pib = ib->opaque; - pci_set_irq(&pik->dev, false); + pci_set_irq(&pib->dev, true); +} + +static void pci_ipmi_lower_irq(IPMIBT *ib) +{ + PCIIPMIBTDevice *pib = ib->opaque; + + pci_set_irq(&pib->dev, false); } static void pci_ipmi_bt_realize(PCIDevice *pd, Error **errp) { Error *err = NULL; - PCIIPMIBTDevice *pik = PCI_IPMI_BT(pd); + PCIIPMIBTDevice *pib = PCI_IPMI_BT(pd); IPMIInterface *ii = IPMI_INTERFACE(pd); IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); - if (!pik->bt.bmc) { + if (!pib->bt.bmc) { error_setg(errp, "IPMI device requires a bmc attribute to be set"); return; } - pik->uuid = ipmi_next_uuid(); + pib->uuid = ipmi_next_uuid(); - pik->bt.bmc->intf = ii; - pik->bt.opaque = pik; + pib->bt.bmc->intf = ii; + pib->bt.opaque = pib; pci_config_set_prog_interface(pd->config, 0x02); /* BT */ pci_config_set_interrupt_pin(pd->config, 0x01); - pik->bt.use_irq = 1; - pik->bt.raise_irq = pci_ipmi_raise_irq; - pik->bt.lower_irq = pci_ipmi_lower_irq; + pib->bt.use_irq = 1; + pib->bt.raise_irq = pci_ipmi_raise_irq; + pib->bt.lower_irq = pci_ipmi_lower_irq; iic->init(ii, 8, &err); if (err) { error_propagate(errp, err); return; } - pci_register_bar(pd, 0, PCI_BASE_ADDRESS_SPACE_IO, &pik->bt.io); + pci_register_bar(pd, 0, PCI_BASE_ADDRESS_SPACE_IO, &pib->bt.io); } 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() @@ -96,19 +107,19 @@ const VMStateDescription vmstate_PCIIPMIBTDevice = { static void pci_ipmi_bt_instance_init(Object *obj) { - PCIIPMIBTDevice *pik = PCI_IPMI_BT(obj); + PCIIPMIBTDevice *pib = PCI_IPMI_BT(obj); - ipmi_bmc_find_and_link(obj, (Object **) &pik->bt.bmc); + ipmi_bmc_find_and_link(obj, (Object **) &pib->bt.bmc); } static void *pci_ipmi_bt_get_backend_data(IPMIInterface *ii) { - PCIIPMIBTDevice *pik = PCI_IPMI_BT(ii); + PCIIPMIBTDevice *pib = PCI_IPMI_BT(ii); - return &pik->bt; + return &pib->bt; } -static void pci_ipmi_bt_class_init(ObjectClass *oc, void *data) +static void pci_ipmi_bt_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); @@ -125,6 +136,7 @@ static void pci_ipmi_bt_class_init(ObjectClass *oc, void *data) iic->get_backend_data = pci_ipmi_bt_get_backend_data; ipmi_bt_class_init(iic); + iic->get_fwinfo = pci_ipmi_bt_get_fwinfo; } static const TypeInfo pci_ipmi_bt_info = { @@ -133,7 +145,7 @@ static const TypeInfo pci_ipmi_bt_info = { .instance_size = sizeof(PCIIPMIBTDevice), .instance_init = pci_ipmi_bt_instance_init, .class_init = pci_ipmi_bt_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_IPMI_INTERFACE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } diff --git a/hw/ipmi/pci_ipmi_kcs.c b/hw/ipmi/pci_ipmi_kcs.c index 1a581413c26..4d6cde826ec 100644 --- a/hw/ipmi/pci_ipmi_kcs.c +++ b/hw/ipmi/pci_ipmi_kcs.c @@ -38,6 +38,16 @@ struct PCIIPMIKCSDevice { uint32_t uuid; }; +static void pci_ipmi_kcs_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info) +{ + PCIIPMIKCSDevice *pik = PCI_IPMI_KCS(ii); + + ipmi_kcs_get_fwinfo(&pik->kcs, info); + info->irq_source = IPMI_PCI_IRQ; + info->interrupt_number = pci_intx(&pik->dev); + info->uuid = pik->uuid; +} + static void pci_ipmi_raise_irq(IPMIKCS *ik) { PCIIPMIKCSDevice *pik = ik->opaque; @@ -87,7 +97,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() @@ -108,7 +118,7 @@ static void *pci_ipmi_kcs_get_backend_data(IPMIInterface *ii) return &pik->kcs; } -static void pci_ipmi_kcs_class_init(ObjectClass *oc, void *data) +static void pci_ipmi_kcs_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); @@ -125,6 +135,7 @@ static void pci_ipmi_kcs_class_init(ObjectClass *oc, void *data) iic->get_backend_data = pci_ipmi_kcs_get_backend_data; ipmi_kcs_class_init(iic); + iic->get_fwinfo = pci_ipmi_kcs_get_fwinfo; } static const TypeInfo pci_ipmi_kcs_info = { @@ -133,7 +144,7 @@ static const TypeInfo pci_ipmi_kcs_info = { .instance_size = sizeof(PCIIPMIKCSDevice), .instance_init = pci_ipmi_kcs_instance_init, .class_init = pci_ipmi_kcs_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_IPMI_INTERFACE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } diff --git a/hw/ipmi/smbus_ipmi.c b/hw/ipmi/smbus_ipmi.c index d0991ab7f93..78c332de54d 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), @@ -351,7 +351,7 @@ static void smbus_ipmi_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info) info->uuid = sid->uuid; } -static void smbus_ipmi_class_init(ObjectClass *oc, void *data) +static void smbus_ipmi_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); @@ -376,7 +376,7 @@ static const TypeInfo smbus_ipmi_info = { .instance_size = sizeof(SMBusIPMIDevice), .instance_init = smbus_ipmi_init, .class_init = smbus_ipmi_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_IPMI_INTERFACE }, { TYPE_ACPI_DEV_AML_IF }, { } diff --git a/hw/isa/Kconfig b/hw/isa/Kconfig index c10cbc5fc19..73c6470805c 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,18 +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 - select MC146818RTC - -config PIIX4 +config PIIX bool # For historical reasons, SuperIO devices are created in the board # for PIIX4. @@ -52,11 +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 @@ -64,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 diff --git a/hw/isa/apm.c b/hw/isa/apm.c index dfe9020d30b..e34edb864cd 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 00000000000..c2a38f04b11 --- /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, const 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 63e08572089..06e8f0ce3e0 100644 --- a/hw/isa/i82378.c +++ b/hw/isa/i82378.c @@ -40,7 +40,7 @@ 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() }, @@ -67,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, @@ -102,7 +103,11 @@ static void i82378_realize(PCIDevice *pci, Error **errp) 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"); @@ -117,7 +122,7 @@ static void i82378_init(Object *obj) qdev_init_gpio_in(dev, i82378_request_pic_irq, 16); } -static void i82378_class_init(ObjectClass *klass, void *data) +static void i82378_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -137,7 +142,7 @@ static const TypeInfo i82378_type_info = { .instance_size = sizeof(I82378State), .instance_init = i82378_init, .class_init = i82378_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index a289eccfb19..6c9802eb7ac 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -22,14 +22,14 @@ #include "qemu/module.h" #include "qapi/error.h" #include "hw/sysbus.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/isa/isa.h" static ISABus *isabus; static char *isabus_get_fw_dev_path(DeviceState *dev); -static void isa_bus_class_init(ObjectClass *klass, void *data) +static void isa_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); @@ -52,18 +52,25 @@ 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; } @@ -198,7 +205,7 @@ ISADevice *isa_vga_init(ISABus *bus) } } -static void isabus_bridge_class_init(ObjectClass *klass, void *data) +static void isabus_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -213,7 +220,7 @@ static const TypeInfo isabus_bridge_info = { .class_init = isabus_bridge_class_init, }; -static void isa_device_class_init(ObjectClass *klass, void *data) +static void isa_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->bus_type = TYPE_ISA_BUS; diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c index 7dbfc374da3..2853485977c 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -14,7 +14,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qapi/error.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "chardev/char.h" #include "hw/char/parallel.h" #include "hw/block/fdc.h" @@ -22,7 +22,7 @@ #include "hw/qdev-properties.h" #include "hw/input/i8042.h" #include "hw/char/parallel-isa.h" -#include "hw/char/serial.h" +#include "hw/char/serial-isa.h" #include "trace.h" static void isa_superio_realize(DeviceState *dev, Error **errp) @@ -116,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) { @@ -170,7 +172,7 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) } } -static void isa_superio_class_init(ObjectClass *oc, void *data) +static void isa_superio_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -185,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 9c47a2f6c7e..304dffac322 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -41,13 +41,13 @@ #include "hw/isa/apm.h" #include "hw/pci/pci.h" #include "hw/southbridge/ich9.h" -#include "hw/i386/pc.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" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.h" #include "hw/core/cpu.h" #include "hw/nvram/fw_cfg.h" #include "qemu/cutils.h" @@ -182,7 +182,6 @@ static uint64_t ich9_cc_read(void *opaque, hwaddr addr, } /* IRQ routing */ -/* */ static void ich9_lpc_rout(uint8_t pirq_rout, int *pic_irq, int *pic_dis) { *pic_irq = pirq_rout & ICH9_LPC_PIRQ_ROUT_MASK; @@ -304,6 +303,21 @@ static 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); } @@ -517,6 +531,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 +683,9 @@ static void ich9_lpc_initfn(Object *obj) 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,9 +702,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))) { @@ -719,17 +745,17 @@ 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, IOAPIC_NUM_PINS); - isa_bus_register_input_irqs(isa_bus, lpc->gsi); - i8257_dma_init(isa_bus, 0); + i8257_dma_init(OBJECT(d), isa_bus, 0); /* RTC */ qdev_prop_set_int32(DEVICE(&lpc->rtc), "base_year", 2000); if (!qdev_realize(DEVICE(&lpc->rtc), BUS(isa_bus), errp)) { return; } + irq = object_property_get_uint(OBJECT(&lpc->rtc), "irq", &error_fatal); + isa_connect_gpio_out(ISA_DEVICE(&lpc->rtc), 0, irq); pci_bus_irqs(pci_bus, ich9_lpc_set_irq, d, ICH9_LPC_NB_PIRQS); pci_bus_map_irqs(pci_bus, ich9_lpc_map_irq); @@ -750,7 +776,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() } @@ -770,7 +796,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), @@ -784,7 +810,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), @@ -792,14 +818,14 @@ 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 } }; -static Property ich9_lpc_properties[] = { +static const Property ich9_lpc_properties[] = { 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), @@ -809,7 +835,10 @@ static Property ich9_lpc_properties[] = { 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_END_OF_LIST(), + 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), }; static void ich9_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev) @@ -845,7 +874,7 @@ static void build_ich9_isa_aml(AcpiDevAmlIf *adev, Aml *scope) qbus_build_aml(bus, scope); } -static void ich9_lpc_class_init(ObjectClass *klass, void *data) +static void ich9_lpc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -854,7 +883,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); @@ -876,7 +905,6 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data) 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; } @@ -886,7 +914,7 @@ static const TypeInfo ich9_lpc_info = { .instance_size = sizeof(ICH9LPCState), .instance_init = ich9_lpc_initfn, .class_init = ich9_lpc_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, diff --git a/hw/isa/meson.build b/hw/isa/meson.build index b855e812762..32192822170 100644 --- a/hw/isa/meson.build +++ b/hw/isa/meson.build @@ -1,10 +1,10 @@ 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_PIIX3', if_true: files('piix3.c')) -system_ss.add(when: 'CONFIG_PIIX4', if_true: files('piix4.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')) diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c index 8d7b8d3db2b..388da8f5900 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), @@ -327,21 +327,20 @@ static const VMStateDescription vmstate_pc87312 = { } }; -static Property pc87312_properties[] = { +static const Property pc87312_properties[] = { DEFINE_PROP_UINT16("iobase", PC87312State, iobase, 0x398), DEFINE_PROP_UINT8("config", PC87312State, config, 1), - DEFINE_PROP_END_OF_LIST() }; -static void pc87312_class_init(ObjectClass *klass, void *data) +static void pc87312_class_init(ObjectClass *klass, const 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 00000000000..52c14d3cd5b --- /dev/null +++ b/hw/isa/piix.c @@ -0,0 +1,521 @@ +/* + * 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 "system/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 const 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), +}; + +static void pci_piix_class_init(ObjectClass *klass, const 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 = (const 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, const 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, const 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 117024e450b..00000000000 --- a/hw/isa/piix3.c +++ /dev/null @@ -1,389 +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/qdev-properties.h" -#include "hw/isa/isa.h" -#include "sysemu/runstate.h" -#include "migration/vmstate.h" -#include "hw/acpi/acpi_aml_interface.h" - -static void piix3_set_irq_pic(PIIX3State *piix3, int pic_irq) -{ - qemu_set_irq(piix3->pic[pic_irq], - !!(piix3->pic_levels & - (((1ULL << PIIX_NUM_PIRQS) - 1) << - (pic_irq * PIIX_NUM_PIRQS)))); -} - -static void piix3_set_irq_level_internal(PIIX3State *piix3, int pirq, int level) -{ - int pic_irq; - uint64_t mask; - - pic_irq = piix3->dev.config[PIIX_PIRQCA + pirq]; - if (pic_irq >= PIIX_NUM_PIC_IRQS) { - return; - } - - mask = 1ULL << ((pic_irq * PIIX_NUM_PIRQS) + pirq); - piix3->pic_levels &= ~mask; - piix3->pic_levels |= mask * !!level; -} - -static void piix3_set_irq_level(PIIX3State *piix3, int pirq, int level) -{ - int pic_irq; - - pic_irq = piix3->dev.config[PIIX_PIRQCA + pirq]; - if (pic_irq >= PIIX_NUM_PIC_IRQS) { - return; - } - - piix3_set_irq_level_internal(piix3, pirq, level); - - piix3_set_irq_pic(piix3, pic_irq); -} - -static void piix3_set_irq(void *opaque, int pirq, int level) -{ - PIIX3State *piix3 = opaque; - piix3_set_irq_level(piix3, pirq, level); -} - -static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pin) -{ - PIIX3State *piix3 = opaque; - int irq = piix3->dev.config[PIIX_PIRQCA + pin]; - PCIINTxRoute route; - - if (irq < PIIX_NUM_PIC_IRQS) { - route.mode = PCI_INTX_ENABLED; - route.irq = irq; - } else { - route.mode = PCI_INTX_DISABLED; - route.irq = -1; - } - return route; -} - -/* irq routing is changed. so rebuild bitmap */ -static void piix3_update_irq_levels(PIIX3State *piix3) -{ - PCIBus *bus = pci_get_bus(&piix3->dev); - int pirq; - - piix3->pic_levels = 0; - for (pirq = 0; pirq < PIIX_NUM_PIRQS; pirq++) { - piix3_set_irq_level(piix3, pirq, pci_bus_get_irq_level(bus, pirq)); - } -} - -static void piix3_write_config(PCIDevice *dev, - uint32_t address, uint32_t val, int len) -{ - pci_default_write_config(dev, address, val, len); - if (ranges_overlap(address, len, PIIX_PIRQCA, 4)) { - PIIX3State *piix3 = PIIX3_PCI_DEVICE(dev); - int pic_irq; - - pci_bus_fire_intx_routing_notifier(pci_get_bus(&piix3->dev)); - piix3_update_irq_levels(piix3); - for (pic_irq = 0; pic_irq < PIIX_NUM_PIC_IRQS; pic_irq++) { - piix3_set_irq_pic(piix3, pic_irq); - } - } -} - -static void piix3_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); - - /* RTC */ - qdev_prop_set_int32(DEVICE(&d->rtc), "base_year", 2000); - if (!qdev_realize(DEVICE(&d->rtc), BUS(isa_bus), errp)) { - return; - } -} - -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_piix3_init(Object *obj) -{ - PIIX3State *d = PIIX3_PCI_DEVICE(obj); - - object_initialize_child(obj, "rtc", &d->rtc, TYPE_MC146818_RTC); -} - -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); - - k->config_write = piix3_write_config; - 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), - .instance_init = pci_piix3_init, - .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, 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->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_register_types(void) -{ - type_register_static(&piix3_pci_type_info); - type_register_static(&piix3_info); -} - -type_init(piix3_register_types) diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c deleted file mode 100644 index e0b149f8eb8..00000000000 --- a/hw/isa/piix4.c +++ /dev/null @@ -1,302 +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; - - MC146818RtcState 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 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] = 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->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_register_input_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, 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, TYPE_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 18287741cb4..0ec63f520c5 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; @@ -73,26 +58,23 @@ static unsigned int get_fdc_dma(ISASuperIODevice *sio, uint8_t index) return 2; } -static void smc37c669_class_init(ObjectClass *klass, void *data) +static void smc37c669_class_init(ObjectClass *klass, const void *data) { ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); 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/vt82c686.c b/hw/isa/vt82c686.c index 57bdfb4e78c..337958617a4 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), @@ -217,11 +220,11 @@ typedef struct via_pm_init_info { uint16_t device_id; } ViaPMInitInfo; -static void via_pm_class_init(ObjectClass *klass, void *data) +static void via_pm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - ViaPMInitInfo *info = data; + const ViaPMInitInfo *info = data; k->realize = via_pm_realize; k->config_write = pm_write_config; @@ -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; @@ -240,7 +243,7 @@ static const TypeInfo via_pm_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(ViaPMState), .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -256,7 +259,7 @@ static const TypeInfo vt82c686b_pm_info = { .name = TYPE_VT82C686B_PM, .parent = TYPE_VIA_PM, .class_init = via_pm_class_init, - .class_data = (void *)&vt82c686b_pm_init_info, + .class_data = &vt82c686b_pm_init_info, }; static const ViaPMInitInfo vt8231_pm_init_info = { @@ -269,7 +272,7 @@ static const TypeInfo vt8231_pm_info = { .name = TYPE_VT8231_PM, .parent = TYPE_VIA_PM, .class_init = via_pm_class_init, - .class_data = (void *)&vt8231_pm_init_info, + .class_data = &vt8231_pm_init_info, }; @@ -323,13 +326,24 @@ static uint64_t via_superio_cfg_read(void *opaque, hwaddr addr, unsigned size) return val; } -static void via_superio_class_init(ObjectClass *klass, void *data) +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, const 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); @@ -419,12 +456,12 @@ static void vt82c686b_superio_init(Object *obj) VIA_SUPERIO(obj)->io_ops = &vt82c686b_superio_cfg_ops; } -static void vt82c686b_superio_class_init(ObjectClass *klass, void *data) +static void vt82c686b_superio_class_init(ObjectClass *klass, const 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) +static void vt8231_superio_class_init(ObjectClass *klass, const 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,8 +592,11 @@ OBJECT_DECLARE_SIMPLE_TYPE(ViaISAState, VIA_ISA) struct ViaISAState { PCIDevice dev; + + IRQState i8259_irq; qemu_irq cpu_intr; qemu_irq *isa_irqs_in; + uint16_t irq_state[ISA_NUM_IRQS]; ViaSuperIOState via_sio; MC146818RtcState rtc; PCIIDEState ide; @@ -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() } @@ -586,21 +634,15 @@ static const TypeInfo via_isa_info = { .instance_size = sizeof(ViaISAState), .instance_init = via_isa_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -static void via_isa_request_i8259_irq(void *opaque, int irq, int level) +static int via_isa_get_pci_irq(const ViaISAState *s, int pin) { - ViaISAState *s = opaque; - qemu_set_irq(s->cpu_intr, level); -} - -static int via_isa_get_pci_irq(const ViaISAState *s, int irq_num) -{ - switch (irq_num) { + switch (pin) { case 0: return s->dev.config[0x55] >> 4; case 1: @@ -613,29 +655,61 @@ static int via_isa_get_pci_irq(const ViaISAState *s, int irq_num) return 0; } -static void via_isa_set_pci_irq(void *opaque, int irq_num, int level) +void via_isa_set_irq(PCIDevice *d, int pin, int level) { - ViaISAState *s = opaque; - PCIBus *bus = pci_get_bus(&s->dev); - int i, pic_level, pic_irq = via_isa_get_pci_irq(s, irq_num); + 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; - /* IRQ 0: disabled, IRQ 2,8,13: reserved */ - if (!pic_irq) { + 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; } - if (unlikely(pic_irq == 2 || pic_irq == 8 || pic_irq == 13)) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid ISA IRQ routing"); + /* 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]); +} - /* The pic level is the logical OR of all the PCI irqs mapped to it. */ - pic_level = 0; - for (i = 0; i < PCI_NUM_PINS; i++) { - if (pic_irq == via_isa_get_pci_irq(s, i)) { - pic_level |= pci_bus_get_irq_level(bus, i); - } - } - /* Now we change the pic irq level according to the via irq mappings. */ - qemu_set_irq(s->isa_irqs_in[pic_irq], pic_level); +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) +{ + ViaISAState *s = opaque; + qemu_set_irq(s->cpu_intr, level); } static void via_isa_realize(PCIDevice *d, Error **errp) @@ -643,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); @@ -656,12 +730,10 @@ static void via_isa_realize(PCIDevice *d, Error **errp) return; } - s->isa_irqs_in = i8259_init(isa_bus, *isa_irq); + 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); - - qdev_init_gpio_in_named(dev, via_isa_set_pci_irq, "pirq", PCI_NUM_PINS); + i8257_dma_init(OBJECT(d), isa_bus, 0); /* RTC */ qdev_prop_set_int32(DEVICE(&s->rtc), "base_year", 2000); @@ -761,7 +833,7 @@ static void vt82c686b_init(Object *obj) object_initialize_child(obj, "pm", &s->pm, TYPE_VT82C686B_PM); } -static void vt82c686b_class_init(ObjectClass *klass, void *data) +static void vt82c686b_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -772,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 */ @@ -826,7 +898,7 @@ static void vt8231_init(Object *obj) object_initialize_child(obj, "pm", &s->pm, TYPE_VT8231_PM); } -static void vt8231_class_init(ObjectClass *klass, void *data) +static void vt8231_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -837,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 1e7c5b43c5e..bb2838b7b53 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 @@ -16,6 +17,7 @@ config LOONGARCH_VIRT select LOONGARCH_EXTIOI select LS7A_RTC select SMBIOS + select ACPI_CPU_HOTPLUG select ACPI_PCI select ACPI_HW_REDUCED select FW_CFG_DMA diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c deleted file mode 100644 index ae292fc5432..00000000000 --- a/hw/loongarch/acpi-build.c +++ /dev/null @@ -1,616 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Support for generating ACPI tables and passing them to Guests - * - * Copyright (C) 2021 Loongson Technology Corporation Limited - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "qemu/bitmap.h" -#include "hw/pci/pci.h" -#include "hw/core/cpu.h" -#include "target/loongarch/cpu.h" -#include "hw/acpi/acpi-defs.h" -#include "hw/acpi/acpi.h" -#include "hw/nvram/fw_cfg.h" -#include "hw/acpi/bios-linker-loader.h" -#include "migration/vmstate.h" -#include "hw/mem/memory-device.h" -#include "sysemu/reset.h" - -/* Supported chipsets: */ -#include "hw/pci-host/ls7a.h" -#include "hw/loongarch/virt.h" - -#include "hw/acpi/utils.h" -#include "hw/acpi/pci.h" - -#include "qom/qom-qobject.h" - -#include "hw/acpi/generic_event_device.h" -#include "hw/pci-host/gpex.h" -#include "sysemu/tpm.h" -#include "hw/platform-bus.h" -#include "hw/acpi/aml-build.h" -#include "hw/acpi/hmat.h" - -#define ACPI_BUILD_ALIGN_SIZE 0x1000 -#define ACPI_BUILD_TABLE_SIZE 0x20000 - -#ifdef DEBUG_ACPI_BUILD -#define ACPI_BUILD_DPRINTF(fmt, ...) \ - do {printf("ACPI_BUILD: " fmt, ## __VA_ARGS__); } while (0) -#else -#define ACPI_BUILD_DPRINTF(fmt, ...) -#endif - -/* build FADT */ -static void init_common_fadt_data(AcpiFadtData *data) -{ - AcpiFadtData fadt = { - /* ACPI 5.0: 4.1 Hardware-Reduced ACPI */ - .rev = 5, - .flags = ((1 << ACPI_FADT_F_HW_REDUCED_ACPI) | - (1 << ACPI_FADT_F_RESET_REG_SUP)), - - /* ACPI 5.0: 4.8.3.7 Sleep Control and Status Registers */ - .sleep_ctl = { - .space_id = AML_AS_SYSTEM_MEMORY, - .bit_width = 8, - .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_CTL, - }, - .sleep_sts = { - .space_id = AML_AS_SYSTEM_MEMORY, - .bit_width = 8, - .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_STS, - }, - - /* ACPI 5.0: 4.8.3.6 Reset Register */ - .reset_reg = { - .space_id = AML_AS_SYSTEM_MEMORY, - .bit_width = 8, - .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_RESET, - }, - .reset_val = ACPI_GED_RESET_VALUE, - }; - *data = fadt; -} - -static void acpi_align_size(GArray *blob, unsigned align) -{ - /* - * Align size to multiple of given size. This reduces the chance - * we need to change size in the future (breaking cross version migration). - */ - g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align)); -} - -/* build FACS */ -static void -build_facs(GArray *table_data) -{ - const char *sig = "FACS"; - const uint8_t reserved[40] = {}; - - g_array_append_vals(table_data, sig, 4); /* Signature */ - build_append_int_noprefix(table_data, 64, 4); /* Length */ - build_append_int_noprefix(table_data, 0, 4); /* Hardware Signature */ - build_append_int_noprefix(table_data, 0, 4); /* Firmware Waking Vector */ - build_append_int_noprefix(table_data, 0, 4); /* Global Lock */ - build_append_int_noprefix(table_data, 0, 4); /* Flags */ - g_array_append_vals(table_data, reserved, 40); /* Reserved */ -} - -/* build MADT */ -static void -build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams) -{ - MachineState *ms = MACHINE(lams); - MachineClass *mc = MACHINE_GET_CLASS(ms); - const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); - int i, arch_id; - AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lams->oem_id, - .oem_table_id = lams->oem_table_id }; - - acpi_table_begin(&table, table_data); - - /* Local APIC Address */ - build_append_int_noprefix(table_data, 0, 4); - build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */ - - for (i = 0; i < arch_ids->len; i++) { - /* Processor Core Interrupt Controller Structure */ - arch_id = arch_ids->cpus[i].arch_id; - - build_append_int_noprefix(table_data, 17, 1); /* Type */ - build_append_int_noprefix(table_data, 15, 1); /* Length */ - build_append_int_noprefix(table_data, 1, 1); /* Version */ - build_append_int_noprefix(table_data, i, 4); /* ACPI Processor ID */ - build_append_int_noprefix(table_data, arch_id, 4); /* Core ID */ - build_append_int_noprefix(table_data, 1, 4); /* Flags */ - } - - /* Extend I/O Interrupt Controller Structure */ - build_append_int_noprefix(table_data, 20, 1); /* Type */ - build_append_int_noprefix(table_data, 13, 1); /* Length */ - build_append_int_noprefix(table_data, 1, 1); /* Version */ - build_append_int_noprefix(table_data, 3, 1); /* Cascade */ - build_append_int_noprefix(table_data, 0, 1); /* Node */ - build_append_int_noprefix(table_data, 0xffff, 8); /* Node map */ - - /* MSI Interrupt Controller Structure */ - build_append_int_noprefix(table_data, 21, 1); /* Type */ - build_append_int_noprefix(table_data, 19, 1); /* Length */ - build_append_int_noprefix(table_data, 1, 1); /* Version */ - build_append_int_noprefix(table_data, VIRT_PCH_MSI_ADDR_LOW, 8);/* Address */ - build_append_int_noprefix(table_data, 0x40, 4); /* Start */ - build_append_int_noprefix(table_data, 0xc0, 4); /* Count */ - - /* Bridge I/O Interrupt Controller Structure */ - build_append_int_noprefix(table_data, 22, 1); /* Type */ - build_append_int_noprefix(table_data, 17, 1); /* Length */ - build_append_int_noprefix(table_data, 1, 1); /* Version */ - build_append_int_noprefix(table_data, VIRT_PCH_REG_BASE, 8);/* Address */ - build_append_int_noprefix(table_data, 0x1000, 2); /* Size */ - build_append_int_noprefix(table_data, 0, 2); /* Id */ - build_append_int_noprefix(table_data, 0x40, 2); /* Base */ - - acpi_table_end(linker, &table); -} - -/* build SRAT */ -static void -build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) -{ - int i, arch_id, node_id; - uint64_t mem_len, mem_base; - int nb_numa_nodes = machine->numa_state->num_nodes; - LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); - MachineClass *mc = MACHINE_GET_CLASS(lams); - const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine); - AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lams->oem_id, - .oem_table_id = lams->oem_table_id }; - - acpi_table_begin(&table, table_data); - build_append_int_noprefix(table_data, 1, 4); /* Reserved */ - build_append_int_noprefix(table_data, 0, 8); /* Reserved */ - - for (i = 0; i < arch_ids->len; ++i) { - arch_id = arch_ids->cpus[i].arch_id; - node_id = arch_ids->cpus[i].props.node_id; - - /* Processor Local APIC/SAPIC Affinity Structure */ - build_append_int_noprefix(table_data, 0, 1); /* Type */ - build_append_int_noprefix(table_data, 16, 1); /* Length */ - /* Proximity Domain [7:0] */ - build_append_int_noprefix(table_data, node_id, 1); - build_append_int_noprefix(table_data, arch_id, 1); /* APIC ID */ - /* Flags, Table 5-36 */ - build_append_int_noprefix(table_data, 1, 4); - build_append_int_noprefix(table_data, 0, 1); /* Local SAPIC EID */ - /* Proximity Domain [31:8] */ - build_append_int_noprefix(table_data, 0, 3); - build_append_int_noprefix(table_data, 0, 4); /* Reserved */ - } - - /* Node0 */ - build_srat_memory(table_data, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, - 0, MEM_AFFINITY_ENABLED); - mem_base = VIRT_HIGHMEM_BASE; - if (!nb_numa_nodes) { - mem_len = machine->ram_size - VIRT_LOWMEM_SIZE; - } else { - mem_len = machine->numa_state->nodes[0].node_mem - VIRT_LOWMEM_SIZE; - } - if (mem_len) - build_srat_memory(table_data, mem_base, mem_len, 0, MEM_AFFINITY_ENABLED); - - /* Node1 - Nodemax */ - if (nb_numa_nodes) { - mem_base += mem_len; - for (i = 1; i < nb_numa_nodes; ++i) { - if (machine->numa_state->nodes[i].node_mem > 0) { - build_srat_memory(table_data, mem_base, - machine->numa_state->nodes[i].node_mem, i, - MEM_AFFINITY_ENABLED); - mem_base += machine->numa_state->nodes[i].node_mem; - } - } - } - - if (machine->device_memory) { - build_srat_memory(table_data, machine->device_memory->base, - memory_region_size(&machine->device_memory->mr), - nb_numa_nodes - 1, - MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); - } - - acpi_table_end(linker, &table); -} - -typedef -struct AcpiBuildState { - /* Copy of table in RAM (for patching). */ - MemoryRegion *table_mr; - /* Is table patched? */ - uint8_t patched; - void *rsdp; - MemoryRegion *rsdp_mr; - MemoryRegion *linker_mr; -} AcpiBuildState; - -static void build_uart_device_aml(Aml *table) -{ - Aml *dev; - Aml *crs; - Aml *pkg0, *pkg1, *pkg2; - uint32_t uart_irq = VIRT_UART_IRQ; - - Aml *scope = aml_scope("_SB"); - dev = aml_device("COMA"); - aml_append(dev, aml_name_decl("_HID", aml_string("PNP0501"))); - aml_append(dev, aml_name_decl("_UID", aml_int(0))); - aml_append(dev, aml_name_decl("_CCA", aml_int(1))); - crs = aml_resource_template(); - aml_append(crs, - aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, - AML_NON_CACHEABLE, AML_READ_WRITE, - 0, VIRT_UART_BASE, VIRT_UART_BASE + VIRT_UART_SIZE - 1, - 0, VIRT_UART_SIZE)); - aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, - AML_SHARED, &uart_irq, 1)); - aml_append(dev, aml_name_decl("_CRS", crs)); - pkg0 = aml_package(0x2); - aml_append(pkg0, aml_int(0x05F5E100)); - aml_append(pkg0, aml_string("clock-frenquency")); - pkg1 = aml_package(0x1); - aml_append(pkg1, pkg0); - pkg2 = aml_package(0x2); - aml_append(pkg2, aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301")); - aml_append(pkg2, pkg1); - aml_append(dev, aml_name_decl("_DSD", pkg2)); - aml_append(scope, dev); - aml_append(table, scope); -} - -static void -build_la_ged_aml(Aml *dsdt, MachineState *machine) -{ - uint32_t event; - LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); - - build_ged_aml(dsdt, "\\_SB."GED_DEVICE, - HOTPLUG_HANDLER(lams->acpi_ged), - VIRT_SCI_IRQ, AML_SYSTEM_MEMORY, - VIRT_GED_EVT_ADDR); - event = object_property_get_uint(OBJECT(lams->acpi_ged), - "ged-event", &error_abort); - if (event & ACPI_GED_MEM_HOTPLUG_EVT) { - build_memory_hotplug_aml(dsdt, machine->ram_slots, "\\_SB", NULL, - AML_SYSTEM_MEMORY, - VIRT_GED_MEM_ADDR); - } - acpi_dsdt_add_power_button(dsdt); -} - -static void build_pci_device_aml(Aml *scope, LoongArchMachineState *lams) -{ - struct GPEXConfig cfg = { - .mmio64.base = VIRT_PCI_MEM_BASE, - .mmio64.size = VIRT_PCI_MEM_SIZE, - .pio.base = VIRT_PCI_IO_BASE, - .pio.size = VIRT_PCI_IO_SIZE, - .ecam.base = VIRT_PCI_CFG_BASE, - .ecam.size = VIRT_PCI_CFG_SIZE, - .irq = VIRT_GSI_BASE + VIRT_DEVICE_IRQS, - .bus = lams->pci_bus, - }; - - acpi_dsdt_add_gpex(scope, &cfg); -} - -static void build_flash_aml(Aml *scope, LoongArchMachineState *lams) -{ - Aml *dev, *crs; - - hwaddr flash_base = VIRT_FLASH_BASE; - hwaddr flash_size = VIRT_FLASH_SIZE; - - 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(flash_base, flash_size, AML_READ_WRITE)); - aml_append(dev, aml_name_decl("_CRS", crs)); - aml_append(scope, dev); -} - -#ifdef CONFIG_TPM -static void acpi_dsdt_add_tpm(Aml *scope, LoongArchMachineState *vms) -{ - PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); - hwaddr pbus_base = VIRT_PLATFORM_BUS_BASEADDRESS; - SysBusDevice *sbdev = SYS_BUS_DEVICE(tpm_find()); - MemoryRegion *sbdev_mr; - hwaddr tpm_base; - - if (!sbdev) { - return; - } - - tpm_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); - assert(tpm_base != -1); - - tpm_base += pbus_base; - - sbdev_mr = sysbus_mmio_get_region(sbdev, 0); - - Aml *dev = aml_device("TPM0"); - aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101"))); - aml_append(dev, aml_name_decl("_STR", aml_string("TPM 2.0 Device"))); - aml_append(dev, aml_name_decl("_UID", aml_int(0))); - - Aml *crs = aml_resource_template(); - aml_append(crs, - aml_memory32_fixed(tpm_base, - (uint32_t)memory_region_size(sbdev_mr), - AML_READ_WRITE)); - aml_append(dev, aml_name_decl("_CRS", crs)); - aml_append(scope, dev); -} -#endif - -/* build DSDT */ -static void -build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) -{ - Aml *dsdt, *scope, *pkg; - LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); - AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lams->oem_id, - .oem_table_id = lams->oem_table_id }; - - acpi_table_begin(&table, table_data); - dsdt = init_aml_allocator(); - build_uart_device_aml(dsdt); - build_pci_device_aml(dsdt, lams); - build_la_ged_aml(dsdt, machine); - build_flash_aml(dsdt, lams); -#ifdef CONFIG_TPM - acpi_dsdt_add_tpm(dsdt, lams); -#endif - /* System State Package */ - scope = aml_scope("\\"); - pkg = aml_package(4); - aml_append(pkg, aml_int(ACPI_GED_SLP_TYP_S5)); - aml_append(pkg, aml_int(0)); /* ignored */ - aml_append(pkg, aml_int(0)); /* reserved */ - aml_append(pkg, aml_int(0)); /* reserved */ - aml_append(scope, aml_name_decl("_S5", pkg)); - aml_append(dsdt, scope); - /* Copy AML table into ACPI tables blob and patch header there */ - g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); - acpi_table_end(linker, &table); - free_aml_allocator(); -} - -static void acpi_build(AcpiBuildTables *tables, MachineState *machine) -{ - LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); - GArray *table_offsets; - AcpiFadtData fadt_data; - unsigned facs, rsdt, dsdt; - uint8_t *u; - GArray *tables_blob = tables->table_data; - - init_common_fadt_data(&fadt_data); - - table_offsets = g_array_new(false, true, sizeof(uint32_t)); - ACPI_BUILD_DPRINTF("init ACPI tables\n"); - - bios_linker_loader_alloc(tables->linker, - ACPI_BUILD_TABLE_FILE, tables_blob, - 64, false); - - /* - * FACS is pointed to by FADT. - * We place it first since it's the only table that has alignment - * requirements. - */ - facs = tables_blob->len; - build_facs(tables_blob); - - /* DSDT is pointed to by FADT */ - dsdt = tables_blob->len; - build_dsdt(tables_blob, tables->linker, machine); - - /* ACPI tables pointed to by RSDT */ - acpi_add_table(table_offsets, tables_blob); - fadt_data.facs_tbl_offset = &facs; - fadt_data.dsdt_tbl_offset = &dsdt; - fadt_data.xdsdt_tbl_offset = &dsdt; - build_fadt(tables_blob, tables->linker, &fadt_data, - lams->oem_id, lams->oem_table_id); - - acpi_add_table(table_offsets, tables_blob); - build_madt(tables_blob, tables->linker, lams); - - acpi_add_table(table_offsets, tables_blob); - build_pptt(tables_blob, tables->linker, machine, - lams->oem_id, lams->oem_table_id); - - acpi_add_table(table_offsets, tables_blob); - build_srat(tables_blob, tables->linker, machine); - - if (machine->numa_state->num_nodes) { - if (machine->numa_state->have_numa_distance) { - acpi_add_table(table_offsets, tables_blob); - build_slit(tables_blob, tables->linker, machine, lams->oem_id, - lams->oem_table_id); - } - if (machine->numa_state->hmat_enabled) { - acpi_add_table(table_offsets, tables_blob); - build_hmat(tables_blob, tables->linker, machine->numa_state, - lams->oem_id, lams->oem_table_id); - } - } - - acpi_add_table(table_offsets, tables_blob); - { - AcpiMcfgInfo mcfg = { - .base = cpu_to_le64(VIRT_PCI_CFG_BASE), - .size = cpu_to_le64(VIRT_PCI_CFG_SIZE), - }; - build_mcfg(tables_blob, tables->linker, &mcfg, lams->oem_id, - lams->oem_table_id); - } - -#ifdef CONFIG_TPM - /* TPM info */ - if (tpm_get_version(tpm_find()) == TPM_VERSION_2_0) { - acpi_add_table(table_offsets, tables_blob); - build_tpm2(tables_blob, tables->linker, - tables->tcpalog, lams->oem_id, - lams->oem_table_id); - } -#endif - /* Add tables supplied by user (if any) */ - for (u = acpi_table_first(); u; u = acpi_table_next(u)) { - unsigned len = acpi_table_len(u); - - acpi_add_table(table_offsets, tables_blob); - g_array_append_vals(tables_blob, u, len); - } - - /* RSDT is pointed to by RSDP */ - rsdt = tables_blob->len; - build_rsdt(tables_blob, tables->linker, table_offsets, - lams->oem_id, lams->oem_table_id); - - /* RSDP is in FSEG memory, so allocate it separately */ - { - AcpiRsdpData rsdp_data = { - .revision = 0, - .oem_id = lams->oem_id, - .xsdt_tbl_offset = NULL, - .rsdt_tbl_offset = &rsdt, - }; - build_rsdp(tables->rsdp, tables->linker, &rsdp_data); - } - - /* - * The align size is 128, warn if 64k is not enough therefore - * the align size could be resized. - */ - if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) { - warn_report("ACPI table size %u exceeds %d bytes," - " migration may not work", - tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2); - error_printf("Try removing CPUs, NUMA nodes, memory slots" - " or PCI bridges."); - } - - acpi_align_size(tables->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE); - - /* Cleanup memory that's no longer used. */ - g_array_free(table_offsets, true); -} - -static void acpi_ram_update(MemoryRegion *mr, GArray *data) -{ - uint32_t size = acpi_data_len(data); - - /* - * Make sure RAM size is correct - in case it got changed - * e.g. by migration - */ - memory_region_ram_resize(mr, size, &error_abort); - - memcpy(memory_region_get_ram_ptr(mr), data->data, size); - memory_region_set_dirty(mr, 0, size); -} - -static void acpi_build_update(void *build_opaque) -{ - AcpiBuildState *build_state = build_opaque; - AcpiBuildTables tables; - - /* No state to update or already patched? Nothing to do. */ - if (!build_state || build_state->patched) { - return; - } - build_state->patched = 1; - - acpi_build_tables_init(&tables); - - acpi_build(&tables, MACHINE(qdev_get_machine())); - - acpi_ram_update(build_state->table_mr, tables.table_data); - acpi_ram_update(build_state->rsdp_mr, tables.rsdp); - acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob); - - acpi_build_tables_cleanup(&tables, true); -} - -static void acpi_build_reset(void *build_opaque) -{ - AcpiBuildState *build_state = build_opaque; - build_state->patched = 0; -} - -static const VMStateDescription vmstate_acpi_build = { - .name = "acpi_build", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(patched, AcpiBuildState), - VMSTATE_END_OF_LIST() - }, -}; - -void loongarch_acpi_setup(LoongArchMachineState *lams) -{ - AcpiBuildTables tables; - AcpiBuildState *build_state; - - if (!lams->fw_cfg) { - ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n"); - return; - } - - if (!loongarch_is_acpi_enabled(lams)) { - ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n"); - return; - } - - build_state = g_malloc0(sizeof *build_state); - - acpi_build_tables_init(&tables); - acpi_build(&tables, MACHINE(lams)); - - /* Now expose it all to Guest */ - build_state->table_mr = acpi_add_rom_blob(acpi_build_update, - build_state, tables.table_data, - ACPI_BUILD_TABLE_FILE); - assert(build_state->table_mr != NULL); - - build_state->linker_mr = - acpi_add_rom_blob(acpi_build_update, build_state, - tables.linker->cmd_blob, ACPI_BUILD_LOADER_FILE); - - build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update, - build_state, tables.rsdp, - ACPI_BUILD_RSDP_FILE); - - qemu_register_reset(acpi_build_reset, build_state); - acpi_build_reset(build_state); - vmstate_register(NULL, 0, &vmstate_acpi_build, build_state); - - /* - * Cleanup tables but don't free the memory: we track it - * in build_state. - */ - acpi_build_tables_cleanup(&tables, false); -} diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c new file mode 100644 index 00000000000..14d6c52d4e4 --- /dev/null +++ b/hw/loongarch/boot.c @@ -0,0 +1,448 @@ +/* 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 "system/reset.h" +#include "system/qtest.h" + +/* + * Linux Image Format + * https://docs.kernel.org/arch/loongarch/booting.html + */ +#define LINUX_PE_MAGIC 0x818223cd +#define MZ_MAGIC 0x5a4d /* "MZ" */ + +struct loongarch_linux_hdr { + uint32_t mz_magic; + uint32_t res0; + uint64_t kernel_entry; + uint64_t kernel_size; + uint64_t load_offset; + uint64_t res1; + uint64_t res2; + uint64_t res3; + uint32_t linux_pe_magic; + uint32_t pe_header_offset; +} QEMU_PACKED; + +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(MachineState *ms, + 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; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); + struct memmap_entry *memmap_table; + unsigned int memmap_entries; + + /* 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); + memmap_table = lvms->memmap_table; + memmap_entries = lvms->memmap_entries; + 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 loongarch_boot_info *info, + 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 = info->initrd_addr; + initrd_table->size = info->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(MachineState *ms, + struct loongarch_boot_info *info, void *p, void *start) +{ + void *bp_tables_start; + struct efi_system_table *systab = p; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); + + 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(ms, systab, p, start); + p += ROUND_UP(sizeof(struct efi_boot_memmap) + + sizeof(efi_memory_desc_t) * lvms->memmap_entries, 64 * KiB); + init_efi_initrd_table(info, 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_loongarch_linux_image(const char *filename, + uint64_t *kernel_entry, + uint64_t *kernel_low, + uint64_t *kernel_high) +{ + gsize len; + ssize_t size; + uint8_t *buffer; + struct loongarch_linux_hdr *hdr; + + /* Load as raw file otherwise */ + if (!g_file_get_contents(filename, (char **)&buffer, &len, NULL)) { + 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; + } + + hdr = (struct loongarch_linux_hdr *)buffer; + + if (extract32(le32_to_cpu(hdr->mz_magic), 0, 16) != MZ_MAGIC || + le32_to_cpu(hdr->linux_pe_magic) != LINUX_PE_MAGIC) { + g_free(buffer); + return -1; + } + + /* Early kernel versions may have those fields in virtual address */ + *kernel_entry = extract64(le64_to_cpu(hdr->kernel_entry), + 0, TARGET_PHYS_ADDR_SPACE_BITS); + *kernel_low = extract64(le64_to_cpu(hdr->load_offset), + 0, TARGET_PHYS_ADDR_SPACE_BITS); + *kernel_high = *kernel_low + size; + + rom_add_blob_fixed(filename, buffer, size, *kernel_low); + + g_free(buffer); + + return size; +} + +static ram_addr_t alloc_initrd_memory(struct loongarch_boot_info *info, + uint64_t advice_start, ssize_t rd_size) +{ + hwaddr base, ram_size, gap, low_end; + ram_addr_t initrd_end, initrd_start; + + base = VIRT_LOWMEM_BASE; + gap = VIRT_LOWMEM_SIZE; + initrd_start = advice_start; + initrd_end = initrd_start + rd_size; + + ram_size = info->ram_size; + low_end = base + MIN(ram_size, gap); + if (initrd_end <= low_end) { + return initrd_start; + } + + if (ram_size <= gap) { + error_report("The low memory too small for initial ram disk '%s'," + "You need to expand the ram", + info->initrd_filename); + exit(1); + } + + /* + * Try to load initrd in the high memory + */ + ram_size -= gap; + initrd_start = VIRT_HIGHMEM_BASE; + if (rd_size <= ram_size) { + return initrd_start; + } + + error_report("The high memory too small for initial ram disk '%s'," + "You need to expand the ram", + info->initrd_filename); + exit(1); +} + +static int64_t load_kernel_info(struct loongarch_boot_info *info) +{ + uint64_t kernel_entry, kernel_low, kernel_high, initrd_offset = 0; + ssize_t kernel_size, initrd_size; + + kernel_size = load_elf(info->kernel_filename, NULL, + cpu_loongarch_virt_to_phys, NULL, + &kernel_entry, &kernel_low, + &kernel_high, NULL, ELFDATA2LSB, + EM_LOONGARCH, 1, 0); + kernel_entry = cpu_loongarch_virt_to_phys(NULL, kernel_entry); + if (kernel_size < 0) { + kernel_size = load_loongarch_linux_image(info->kernel_filename, + &kernel_entry, &kernel_low, + &kernel_high); + } + + 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); + initrd_offset = alloc_initrd_memory(info, initrd_offset, + initrd_size); + initrd_size = load_image_targphys(info->initrd_filename, + initrd_offset, initrd_size); + } + + if (initrd_size == (target_ulong)-1) { + error_report("could not load initial ram disk '%s'", + info->initrd_filename); + exit(1); + } + + info->initrd_addr = initrd_offset; + info->initrd_size = initrd_size; + } + + 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(MachineState *ms, + struct loongarch_boot_info *info, void *p) +{ + void *start = p; + + init_cmdline(info, p, start); + p += COMMAND_LINE_SIZE; + + init_systab(ms, info, p, start); +} + +static void loongarch_direct_kernel_boot(MachineState *ms, + 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(ms, 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(ms, info); + } +} diff --git a/hw/loongarch/fw_cfg.c b/hw/loongarch/fw_cfg.c index f15a17416c4..493563669e5 100644 --- a/hw/loongarch/fw_cfg.c +++ b/hw/loongarch/fw_cfg.c @@ -9,7 +9,7 @@ #include "hw/loongarch/fw_cfg.h" #include "hw/loongarch/virt.h" #include "hw/nvram/fw_cfg.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static void fw_cfg_boot_set(void *opaque, const char *boot_device, Error **errp) @@ -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 7c0de4db4ad..27ee68286ed 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 c0421502aba..d494d1e2838 100644 --- a/hw/loongarch/meson.build +++ b/hw/loongarch/meson.build @@ -1,8 +1,11 @@ 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]) -loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-build.c')) +common_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('fw_cfg.c')) +loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files( + 'virt-fdt-build.c', + 'virt.c')) +loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) hw_arch += {'loongarch': loongarch_ss} diff --git a/hw/loongarch/virt-acpi-build.c b/hw/loongarch/virt-acpi-build.c new file mode 100644 index 00000000000..2cd2d9d8424 --- /dev/null +++ b/hw/loongarch/virt-acpi-build.c @@ -0,0 +1,742 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Support for generating ACPI tables and passing them to Guests + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/bitmap.h" +#include "hw/pci/pci.h" +#include "hw/core/cpu.h" +#include "target/loongarch/cpu.h" +#include "hw/acpi/acpi-defs.h" +#include "hw/acpi/acpi.h" +#include "hw/nvram/fw_cfg.h" +#include "hw/acpi/bios-linker-loader.h" +#include "migration/vmstate.h" +#include "hw/mem/memory-device.h" +#include "system/reset.h" + +/* Supported chipsets: */ +#include "hw/pci-host/ls7a.h" +#include "hw/loongarch/virt.h" + +#include "hw/acpi/utils.h" +#include "hw/acpi/pci.h" + +#include "qom/qom-qobject.h" + +#include "hw/acpi/generic_event_device.h" +#include "hw/pci-host/gpex.h" +#include "system/system.h" +#include "system/tpm.h" +#include "hw/platform-bus.h" +#include "hw/acpi/aml-build.h" +#include "hw/acpi/hmat.h" + +#define ACPI_BUILD_ALIGN_SIZE 0x1000 +#define ACPI_BUILD_TABLE_SIZE 0x20000 + +#ifdef DEBUG_ACPI_BUILD +#define ACPI_BUILD_DPRINTF(fmt, ...) \ + do {printf("ACPI_BUILD: " fmt, ## __VA_ARGS__); } while (0) +#else +#define ACPI_BUILD_DPRINTF(fmt, ...) +#endif + +static void virt_madt_cpu_entry(int uid, + const CPUArchIdList *apic_ids, + GArray *entry, bool force_enabled) +{ + uint32_t flags, apic_id = apic_ids->cpus[uid].arch_id; + + flags = apic_ids->cpus[uid].cpu || force_enabled ? 1 /* Enabled */ : 0; + + /* Rev 1.0b, Table 5-13 Processor Local APIC Structure */ + build_append_int_noprefix(entry, 0, 1); /* Type */ + build_append_int_noprefix(entry, 8, 1); /* Length */ + build_append_int_noprefix(entry, uid, 1); /* ACPI Processor ID */ + build_append_int_noprefix(entry, apic_id, 1); /* APIC ID */ + build_append_int_noprefix(entry, flags, 4); /* Flags */ +} + +/* build FADT */ +static void init_common_fadt_data(AcpiFadtData *data) +{ + AcpiFadtData fadt = { + /* ACPI 5.0: 4.1 Hardware-Reduced ACPI */ + .rev = 5, + .flags = ((1 << ACPI_FADT_F_HW_REDUCED_ACPI) | + (1 << ACPI_FADT_F_RESET_REG_SUP)), + + /* ACPI 5.0: 4.8.3.7 Sleep Control and Status Registers */ + .sleep_ctl = { + .space_id = AML_AS_SYSTEM_MEMORY, + .bit_width = 8, + .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_CTL, + }, + .sleep_sts = { + .space_id = AML_AS_SYSTEM_MEMORY, + .bit_width = 8, + .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_STS, + }, + + /* ACPI 5.0: 4.8.3.6 Reset Register */ + .reset_reg = { + .space_id = AML_AS_SYSTEM_MEMORY, + .bit_width = 8, + .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_RESET, + }, + .reset_val = ACPI_GED_RESET_VALUE, + }; + *data = fadt; +} + +static void acpi_align_size(GArray *blob, unsigned align) +{ + /* + * Align size to multiple of given size. This reduces the chance + * we need to change size in the future (breaking cross version migration). + */ + g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align)); +} + +/* build FACS */ +static void +build_facs(GArray *table_data) +{ + const char *sig = "FACS"; + const uint8_t reserved[40] = {}; + + g_array_append_vals(table_data, sig, 4); /* Signature */ + build_append_int_noprefix(table_data, 64, 4); /* Length */ + build_append_int_noprefix(table_data, 0, 4); /* Hardware Signature */ + build_append_int_noprefix(table_data, 0, 4); /* Firmware Waking Vector */ + build_append_int_noprefix(table_data, 0, 4); /* Global Lock */ + build_append_int_noprefix(table_data, 0, 4); /* Flags */ + g_array_append_vals(table_data, reserved, 40); /* Reserved */ +} + +/* build MADT */ +static void +build_madt(GArray *table_data, BIOSLinker *linker, + LoongArchVirtMachineState *lvms) +{ + MachineState *ms = MACHINE(lvms); + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); + int i, arch_id, flags; + AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lvms->oem_id, + .oem_table_id = lvms->oem_table_id }; + + acpi_table_begin(&table, table_data); + + /* Local APIC Address */ + build_append_int_noprefix(table_data, 0, 4); + build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */ + + for (i = 0; i < arch_ids->len; i++) { + /* Processor Core Interrupt Controller Structure */ + arch_id = arch_ids->cpus[i].arch_id; + flags = arch_ids->cpus[i].cpu ? 1 : 0; + build_append_int_noprefix(table_data, 17, 1); /* Type */ + build_append_int_noprefix(table_data, 15, 1); /* Length */ + build_append_int_noprefix(table_data, 1, 1); /* Version */ + build_append_int_noprefix(table_data, i, 4); /* ACPI Processor ID */ + build_append_int_noprefix(table_data, arch_id, 4); /* Core ID */ + build_append_int_noprefix(table_data, flags, 4); /* Flags */ + } + + /* Extend I/O Interrupt Controller Structure */ + build_append_int_noprefix(table_data, 20, 1); /* Type */ + build_append_int_noprefix(table_data, 13, 1); /* Length */ + build_append_int_noprefix(table_data, 1, 1); /* Version */ + build_append_int_noprefix(table_data, 3, 1); /* Cascade */ + build_append_int_noprefix(table_data, 0, 1); /* Node */ + build_append_int_noprefix(table_data, 0xffff, 8); /* Node map */ + + /* MSI Interrupt Controller Structure */ + build_append_int_noprefix(table_data, 21, 1); /* Type */ + build_append_int_noprefix(table_data, 19, 1); /* Length */ + build_append_int_noprefix(table_data, 1, 1); /* Version */ + build_append_int_noprefix(table_data, VIRT_PCH_MSI_ADDR_LOW, 8);/* Address */ + build_append_int_noprefix(table_data, 0x40, 4); /* Start */ + build_append_int_noprefix(table_data, 0xc0, 4); /* Count */ + + /* Bridge I/O Interrupt Controller Structure */ + build_append_int_noprefix(table_data, 22, 1); /* Type */ + build_append_int_noprefix(table_data, 17, 1); /* Length */ + build_append_int_noprefix(table_data, 1, 1); /* Version */ + build_append_int_noprefix(table_data, VIRT_PCH_REG_BASE, 8);/* Address */ + build_append_int_noprefix(table_data, 0x1000, 2); /* Size */ + build_append_int_noprefix(table_data, 0, 2); /* Id */ + build_append_int_noprefix(table_data, 0x40, 2); /* Base */ + + acpi_table_end(linker, &table); +} + +/* build SRAT */ +static void +build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) +{ + int i, arch_id, node_id; + 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 < arch_ids->len; ++i) { + arch_id = arch_ids->cpus[i].arch_id; + node_id = arch_ids->cpus[i].props.node_id; + + /* Processor Local APIC/SAPIC Affinity Structure */ + build_append_int_noprefix(table_data, 0, 1); /* Type */ + build_append_int_noprefix(table_data, 16, 1); /* Length */ + /* Proximity Domain [7:0] */ + build_append_int_noprefix(table_data, node_id, 1); + build_append_int_noprefix(table_data, arch_id, 1); /* APIC ID */ + /* Flags, Table 5-36 */ + build_append_int_noprefix(table_data, 1, 4); + build_append_int_noprefix(table_data, 0, 1); /* Local SAPIC EID */ + /* Proximity Domain [31:8] */ + build_append_int_noprefix(table_data, 0, 3); + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + } + + base = VIRT_LOWMEM_BASE; + gap = VIRT_LOWMEM_SIZE; + numa_info = machine->numa_state->nodes; + nodes = nb_numa_nodes; + if (!nodes) { + nodes = 1; + } + + for (i = 0; i < nodes; i++) { + if (nb_numa_nodes) { + len = numa_info[i].node_mem; + } else { + len = machine->ram_size; + } + + /* + * 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); + /* + * Passing NULL as the SPCR Table for Revision 2 doesn't support + * NameSpaceString. + */ + build_spcr(table_data, linker, &serial, 2, lvms->oem_id, + lvms->oem_table_id, NULL); +} + +typedef +struct AcpiBuildState { + /* Copy of table in RAM (for patching). */ + MemoryRegion *table_mr; + /* Is table patched? */ + uint8_t patched; + void *rsdp; + MemoryRegion *rsdp_mr; + MemoryRegion *linker_mr; +} AcpiBuildState; + +static void build_uart_device_aml(Aml *table, int index) +{ + Aml *dev; + Aml *crs; + Aml *pkg0, *pkg1, *pkg2; + Aml *scope; + uint32_t uart_irq; + uint64_t base; + + 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(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, 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)); + aml_append(dev, aml_name_decl("_CRS", crs)); + pkg0 = aml_package(0x2); + aml_append(pkg0, aml_int(0x05F5E100)); + aml_append(pkg0, aml_string("clock-frenquency")); + pkg1 = aml_package(0x1); + aml_append(pkg1, pkg0); + pkg2 = aml_package(0x2); + aml_append(pkg2, aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301")); + aml_append(pkg2, pkg1); + aml_append(dev, aml_name_decl("_DSD", pkg2)); + aml_append(scope, dev); + aml_append(table, scope); +} + +static void +build_la_ged_aml(Aml *dsdt, MachineState *machine) +{ + uint32_t event; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); + CPUHotplugFeatures opts; + + build_ged_aml(dsdt, "\\_SB."GED_DEVICE, + HOTPLUG_HANDLER(lvms->acpi_ged), + VIRT_SCI_IRQ, AML_SYSTEM_MEMORY, + VIRT_GED_EVT_ADDR); + 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); + } + + if (event & ACPI_GED_CPU_HOTPLUG_EVT) { + opts.acpi_1_compatible = false; + opts.has_legacy_cphp = false; + opts.fw_unplugs_cpu = false; + opts.smi_path = NULL; + + build_cpus_aml(dsdt, machine, opts, virt_madt_cpu_entry, + VIRT_GED_CPUHP_ADDR, "\\_SB", + AML_GED_EVT_CPU_SCAN_METHOD, AML_SYSTEM_MEMORY); + } + + acpi_dsdt_add_power_button(dsdt); +} + +static void build_pci_device_aml(Aml *scope, LoongArchVirtMachineState *lvms) +{ + struct GPEXConfig cfg = { + .mmio64.base = VIRT_PCI_MEM_BASE, + .mmio64.size = VIRT_PCI_MEM_SIZE, + .pio.base = VIRT_PCI_IO_BASE, + .pio.size = VIRT_PCI_IO_SIZE, + .ecam.base = VIRT_PCI_CFG_BASE, + .ecam.size = VIRT_PCI_CFG_SIZE, + .irq = VIRT_GSI_BASE + VIRT_DEVICE_IRQS, + .bus = 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, LoongArchVirtMachineState *vms) +{ + PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); + hwaddr pbus_base = VIRT_PLATFORM_BUS_BASEADDRESS; + SysBusDevice *sbdev = SYS_BUS_DEVICE(tpm_find()); + MemoryRegion *sbdev_mr; + hwaddr tpm_base; + + if (!sbdev) { + return; + } + + tpm_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); + assert(tpm_base != -1); + + tpm_base += pbus_base; + + sbdev_mr = sysbus_mmio_get_region(sbdev, 0); + + Aml *dev = aml_device("TPM0"); + aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101"))); + aml_append(dev, aml_name_decl("_STR", aml_string("TPM 2.0 Device"))); + aml_append(dev, aml_name_decl("_UID", aml_int(0))); + + Aml *crs = aml_resource_template(); + aml_append(crs, + aml_memory32_fixed(tpm_base, + (uint32_t)memory_region_size(sbdev_mr), + AML_READ_WRITE)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); +} +#endif + +/* build DSDT */ +static void +build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) +{ + int i; + Aml *dsdt, *scope, *pkg; + 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(); + 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, lvms); +#endif + /* System State Package */ + scope = aml_scope("\\"); + pkg = aml_package(4); + aml_append(pkg, aml_int(ACPI_GED_SLP_TYP_S5)); + aml_append(pkg, aml_int(0)); /* ignored */ + aml_append(pkg, aml_int(0)); /* reserved */ + aml_append(pkg, aml_int(0)); /* reserved */ + aml_append(scope, aml_name_decl("_S5", pkg)); + aml_append(dsdt, scope); + /* Copy AML table into ACPI tables blob and patch header there */ + g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); + acpi_table_end(linker, &table); + free_aml_allocator(); +} + +static void acpi_build(AcpiBuildTables *tables, MachineState *machine) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); + GArray *table_offsets; + AcpiFadtData fadt_data; + unsigned facs, xsdt, dsdt; + uint8_t *u; + GArray *tables_blob = tables->table_data; + + init_common_fadt_data(&fadt_data); + + table_offsets = g_array_new(false, true, sizeof(uint32_t)); + ACPI_BUILD_DPRINTF("init ACPI tables\n"); + + bios_linker_loader_alloc(tables->linker, + ACPI_BUILD_TABLE_FILE, tables_blob, + 64, false); + + /* + * FACS is pointed to by FADT. + * We place it first since it's the only table that has alignment + * requirements. + */ + facs = tables_blob->len; + build_facs(tables_blob); + + /* DSDT is pointed to by FADT */ + dsdt = tables_blob->len; + build_dsdt(tables_blob, tables->linker, machine); + + /* ACPI tables pointed to by RSDT */ + acpi_add_table(table_offsets, tables_blob); + fadt_data.facs_tbl_offset = &facs; + fadt_data.dsdt_tbl_offset = &dsdt; + fadt_data.xdsdt_tbl_offset = &dsdt; + build_fadt(tables_blob, tables->linker, &fadt_data, + lvms->oem_id, lvms->oem_table_id); + + acpi_add_table(table_offsets, tables_blob); + 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); + { + AcpiMcfgInfo mcfg = { + .base = VIRT_PCI_CFG_BASE, + .size = VIRT_PCI_CFG_SIZE, + }; + build_mcfg(tables_blob, tables->linker, &mcfg, lvms->oem_id, + lvms->oem_table_id); + } + +#ifdef CONFIG_TPM + /* TPM info */ + if (tpm_get_version(tpm_find()) == TPM_VERSION_2_0) { + acpi_add_table(table_offsets, tables_blob); + build_tpm2(tables_blob, tables->linker, + tables->tcpalog, lvms->oem_id, + lvms->oem_table_id); + } +#endif + /* Add tables supplied by user (if any) */ + for (u = acpi_table_first(); u; u = acpi_table_next(u)) { + unsigned len = acpi_table_len(u); + + acpi_add_table(table_offsets, tables_blob); + g_array_append_vals(tables_blob, u, len); + } + + /* RSDT is pointed to by RSDP */ + xsdt = tables_blob->len; + build_xsdt(tables_blob, tables->linker, table_offsets, + lvms->oem_id, lvms->oem_table_id); + + /* RSDP is in FSEG memory, so allocate it separately */ + { + AcpiRsdpData rsdp_data = { + .revision = 2, + .oem_id = lvms->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 CPUs, NUMA nodes, memory slots" + " or PCI bridges.\n"); + } + + acpi_align_size(tables->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE); + + /* Cleanup memory that's no longer used. */ + g_array_free(table_offsets, true); +} + +static void acpi_ram_update(MemoryRegion *mr, GArray *data) +{ + uint32_t size = acpi_data_len(data); + + /* + * Make sure RAM size is correct - in case it got changed + * e.g. by migration + */ + memory_region_ram_resize(mr, size, &error_abort); + + memcpy(memory_region_get_ram_ptr(mr), data->data, size); + memory_region_set_dirty(mr, 0, size); +} + +static void acpi_build_update(void *build_opaque) +{ + AcpiBuildState *build_state = build_opaque; + AcpiBuildTables tables; + + /* No state to update or already patched? Nothing to do. */ + if (!build_state || build_state->patched) { + return; + } + build_state->patched = 1; + + acpi_build_tables_init(&tables); + + acpi_build(&tables, MACHINE(qdev_get_machine())); + + acpi_ram_update(build_state->table_mr, tables.table_data); + acpi_ram_update(build_state->rsdp_mr, tables.rsdp); + acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob); + + acpi_build_tables_cleanup(&tables, true); +} + +static void acpi_build_reset(void *build_opaque) +{ + AcpiBuildState *build_state = build_opaque; + build_state->patched = 0; +} + +static const VMStateDescription vmstate_acpi_build = { + .name = "acpi_build", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT8(patched, AcpiBuildState), + VMSTATE_END_OF_LIST() + }, +}; + +static bool virt_is_acpi_enabled(LoongArchVirtMachineState *lvms) +{ + if (lvms->acpi == ON_OFF_AUTO_OFF) { + return false; + } + return true; +} + +void virt_acpi_setup(LoongArchVirtMachineState *lvms) +{ + AcpiBuildTables tables; + AcpiBuildState *build_state; + + if (!lvms->fw_cfg) { + ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n"); + return; + } + + if (!virt_is_acpi_enabled(lvms)) { + ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n"); + return; + } + + build_state = g_malloc0(sizeof *build_state); + + acpi_build_tables_init(&tables); + acpi_build(&tables, MACHINE(lvms)); + + /* Now expose it all to Guest */ + build_state->table_mr = acpi_add_rom_blob(acpi_build_update, + build_state, tables.table_data, + ACPI_BUILD_TABLE_FILE); + assert(build_state->table_mr != NULL); + + build_state->linker_mr = + acpi_add_rom_blob(acpi_build_update, build_state, + tables.linker->cmd_blob, ACPI_BUILD_LOADER_FILE); + + build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update, + build_state, tables.rsdp, + ACPI_BUILD_RSDP_FILE); + + 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); + + /* + * Cleanup tables but don't free the memory: we track it + * in build_state. + */ + acpi_build_tables_cleanup(&tables, false); +} diff --git a/hw/loongarch/virt-fdt-build.c b/hw/loongarch/virt-fdt-build.c new file mode 100644 index 00000000000..728ce466996 --- /dev/null +++ b/hw/loongarch/virt-fdt-build.c @@ -0,0 +1,534 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2025 Loongson Technology Corporation Limited + */ +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include +#include "hw/acpi/generic_event_device.h" +#include "hw/core/sysbus-fdt.h" +#include "hw/intc/loongarch_extioi.h" +#include "hw/loader.h" +#include "hw/loongarch/virt.h" +#include "hw/pci-host/gpex.h" +#include "hw/pci-host/ls7a.h" +#include "system/device_tree.h" +#include "system/reset.h" +#include "target/loongarch/cpu.h" + +static void create_fdt(LoongArchVirtMachineState *lvms) +{ + MachineState *ms = MACHINE(lvms); + uint8_t rng_seed[32]; + + ms->fdt = create_device_tree(&lvms->fdt_size); + if (!ms->fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + /* Header */ + qemu_fdt_setprop_string(ms->fdt, "/", "compatible", + "linux,dummy-loongson3"); + qemu_fdt_setprop_cell(ms->fdt, "/", "#address-cells", 0x2); + qemu_fdt_setprop_cell(ms->fdt, "/", "#size-cells", 0x2); + qemu_fdt_add_subnode(ms->fdt, "/chosen"); + + /* 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 LoongArchVirtMachineState *lvms) +{ + int num; + MachineState *ms = MACHINE(lvms); + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *possible_cpus; + LoongArchCPU *cpu; + CPUState *cs; + char *nodename, *map_path; + + qemu_fdt_add_subnode(ms->fdt, "/cpus"); + qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1); + qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0); + + /* cpu nodes */ + possible_cpus = mc->possible_cpu_arch_ids(ms); + for (num = 0; num < possible_cpus->len; num++) { + cs = possible_cpus->cpus[num].cpu; + if (cs == NULL) { + continue; + } + + nodename = g_strdup_printf("/cpus/cpu@%d", num); + cpu = LOONGARCH_CPU(cs); + + 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 (possible_cpus->cpus[num].props.has_node_id) { + qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", + possible_cpus->cpus[num].props.node_id); + } + qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", + qemu_fdt_alloc_phandle(ms->fdt)); + g_free(nodename); + } + + /*cpu map */ + qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map"); + for (num = 0; num < possible_cpus->len; num++) { + cs = possible_cpus->cpus[num].cpu; + if (cs == NULL) { + continue; + } + + nodename = g_strdup_printf("/cpus/cpu@%d", num); + if (ms->smp.threads > 1) { + map_path = g_strdup_printf( + "/cpus/cpu-map/socket%d/core%d/thread%d", + num / (ms->smp.cores * ms->smp.threads), + (num / ms->smp.threads) % ms->smp.cores, + num % ms->smp.threads); + } else { + map_path = g_strdup_printf( + "/cpus/cpu-map/socket%d/core%d", + num / ms->smp.cores, + num % ms->smp.cores); + } + qemu_fdt_add_path(ms->fdt, map_path); + qemu_fdt_setprop_phandle(ms->fdt, map_path, "cpu", nodename); + + g_free(map_path); + g_free(nodename); + } +} + +static void fdt_add_memory_node(MachineState *ms, + uint64_t base, uint64_t size, int node_id) +{ + char *nodename = g_strdup_printf("/memory@%" PRIx64, base); + + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", base >> 32, base, + size >> 32, size); + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "memory"); + + if (ms->numa_state && ms->numa_state->num_nodes) { + qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", node_id); + } + + g_free(nodename); +} + +static void fdt_add_memory_nodes(MachineState *ms) +{ + 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 fdt_add_fw_cfg_node(const LoongArchVirtMachineState *lvms) +{ + char *nodename; + hwaddr base = VIRT_FWCFG_BASE; + const MachineState *ms = MACHINE(lvms); + + nodename = g_strdup_printf("/fw_cfg@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, + "compatible", "qemu,fw-cfg-mmio"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", + 2, base, 2, 0x18); + qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); + g_free(nodename); +} + +static void fdt_add_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_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_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[PCI_NUM_PINS * PCI_NUM_PINS * 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 < PCI_NUM_PINS; dev++) { + int devfn = dev * 0x8; + + for (pin = 0; pin < PCI_NUM_PINS; pin++) { + int irq_nr = 16 + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS); + 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, + PCI_NUM_PINS * PCI_NUM_PINS * + 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; + hwaddr size_mmio = VIRT_PCI_MEM_SIZE; + hwaddr base_pio = VIRT_PCI_IO_BASE; + hwaddr size_pio = VIRT_PCI_IO_SIZE; + hwaddr base_pcie = VIRT_PCI_CFG_BASE; + hwaddr size_pcie = VIRT_PCI_CFG_SIZE; + hwaddr base = base_pcie; + const MachineState *ms = MACHINE(lvms); + + nodename = g_strdup_printf("/pcie@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, + "compatible", "pci-host-ecam-generic"); + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "pci"); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 3); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 2); + qemu_fdt_setprop_cell(ms->fdt, nodename, "linux,pci-domain", 0); + qemu_fdt_setprop_cells(ms->fdt, nodename, "bus-range", 0, + PCIE_MMCFG_BUS(VIRT_PCI_CFG_SIZE - 1)); + qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", + 2, base_pcie, 2, size_pcie); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "ranges", + 1, FDT_PCI_RANGE_IOPORT, 2, VIRT_PCI_IO_OFFSET, + 2, base_pio, 2, size_pio, + 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, + 2, base_mmio, 2, size_mmio); + 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_uart_node(LoongArchVirtMachineState *lvms, + uint32_t *pch_pic_phandle, hwaddr base, + int irq, bool chosen) +{ + char *nodename; + hwaddr size = VIRT_UART_SIZE; + 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); + 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 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(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_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_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); +} + +void virt_fdt_setup(LoongArchVirtMachineState *lvms) +{ + MachineState *machine = MACHINE(lvms); + uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle, pch_msi_phandle; + int i; + + create_fdt(lvms); + fdt_add_cpu_nodes(lvms); + fdt_add_memory_nodes(machine); + fdt_add_fw_cfg_node(lvms); + fdt_add_flash_node(lvms); + + /* Add cpu interrupt-controller */ + fdt_add_cpuic_node(lvms, &cpuintc_phandle); + /* Add Extend I/O Interrupt Controller node */ + fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle); + /* Add PCH PIC node */ + fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle); + /* Add PCH MSI node */ + fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle); + /* 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; + fdt_add_uart_node(lvms, &pch_pic_phandle, base, irq, i == 0); + } + + fdt_add_rtc_node(lvms, &pch_pic_phandle); + fdt_add_ged_reset(lvms); + platform_bus_add_all_fdt_nodes(machine->fdt, "/platic", + VIRT_PLATFORM_BUS_BASEADDRESS, + VIRT_PLATFORM_BUS_SIZE, + VIRT_PLATFORM_BUS_IRQ); + + /* + * Since lowmem region starts from 0 and Linux kernel legacy start address + * at 2 MiB, FDT base address is located at 1 MiB to avoid NULL pointer + * access. FDT size limit with 1 MiB. + * Put the FDT into the memory map as a ROM image: this will ensure + * the FDT is copied again upon reset, even if addr points into RAM. + */ + 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)); +} diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 2629128aeda..b15ada20787 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -8,15 +8,18 @@ #include "qemu/units.h" #include "qemu/datadir.h" #include "qapi/error.h" +#include "exec/target_page.h" #include "hw/boards.h" -#include "hw/char/serial.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "sysemu/runstate.h" -#include "sysemu/reset.h" -#include "sysemu/rtc.h" +#include "hw/char/serial-mm.h" +#include "system/kvm.h" +#include "system/tcg.h" +#include "system/system.h" +#include "system/qtest.h" +#include "system/runstate.h" +#include "system/reset.h" +#include "system/rtc.h" #include "hw/loongarch/virt.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/irq.h" #include "net/net.h" #include "hw/loader.h" @@ -31,23 +34,39 @@ #include "hw/loongarch/fw_cfg.h" #include "target/loongarch/cpu.h" #include "hw/firmware/smbios.h" -#include "hw/acpi/aml-build.h" #include "qapi/qapi-visit-common.h" #include "hw/acpi/generic_event_device.h" #include "hw/mem/nvdimm.h" -#include "sysemu/device_tree.h" -#include -#include "hw/core/sysbus-fdt.h" #include "hw/platform-bus.h" #include "hw/display/ramfb.h" +#include "hw/uefi/var-service-api.h" #include "hw/mem/pc-dimm.h" -#include "sysemu/tpm.h" -#include "sysemu/block-backend.h" +#include "system/tpm.h" +#include "system/block-backend.h" #include "hw/block/flash.h" +#include "hw/virtio/virtio-iommu.h" #include "qemu/error-report.h" +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); -static void virt_flash_create(LoongArchMachineState *lams) + 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); @@ -59,306 +78,109 @@ static void virt_flash_create(LoongArchMachineState *lams) 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", "virt.flash"); - object_property_add_child(OBJECT(lams), "virt.flash", OBJECT(dev)); - object_property_add_alias(OBJECT(lams), "pflash", + 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"); - - lams->flash = PFLASH_CFI01(dev); -} - -static void virt_flash_map(LoongArchMachineState *lams, - MemoryRegion *sysmem) -{ - PFlashCFI01 *flash = lams->flash; - DeviceState *dev = DEVICE(flash); - hwaddr base = VIRT_FLASH_BASE; - hwaddr size = VIRT_FLASH_SIZE; - - assert(QEMU_IS_ALIGNED(size, VIRT_FLASH_SECTOR_SIZE)); - assert(size / VIRT_FLASH_SECTOR_SIZE <= UINT32_MAX); - - qdev_prop_set_uint32(dev, "num-blocks", 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)); - + return PFLASH_CFI01(dev); } -static void fdt_add_flash_node(LoongArchMachineState *lams) +static void virt_flash_create(LoongArchVirtMachineState *lvms) { - MachineState *ms = MACHINE(lams); - char *nodename; - - hwaddr flash_base = VIRT_FLASH_BASE; - hwaddr flash_size = VIRT_FLASH_SIZE; - - nodename = g_strdup_printf("/flash@%" PRIx64, flash_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, flash_base, 2, flash_size); - qemu_fdt_setprop_cell(ms->fdt, nodename, "bank-width", 4); - g_free(nodename); + lvms->flash[0] = virt_flash_create1(lvms, "virt.flash0", "pflash0"); + lvms->flash[1] = virt_flash_create1(lvms, "virt.flash1", "pflash1"); } -static void fdt_add_rtc_node(LoongArchMachineState *lams) +static void virt_flash_map1(PFlashCFI01 *flash, + hwaddr base, hwaddr size, + MemoryRegion *sysmem) { - char *nodename; - hwaddr base = VIRT_RTC_REG_BASE; - hwaddr size = VIRT_RTC_LEN; - MachineState *ms = MACHINE(lams); - - nodename = g_strdup_printf("/rtc@%" PRIx64, base); - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "loongson,ls7a-rtc"); - qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); - g_free(nodename); -} - -static void fdt_add_uart_node(LoongArchMachineState *lams) -{ - char *nodename; - hwaddr base = VIRT_UART_BASE; - hwaddr size = VIRT_UART_SIZE; - MachineState *ms = MACHINE(lams); - - nodename = g_strdup_printf("/serial@%" PRIx64, base); - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "ns16550a"); - qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, base, 0x0, size); - qemu_fdt_setprop_cell(ms->fdt, nodename, "clock-frequency", 100000000); - qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); - g_free(nodename); -} - -static void create_fdt(LoongArchMachineState *lams) -{ - MachineState *ms = MACHINE(lams); - - ms->fdt = create_device_tree(&lams->fdt_size); - if (!ms->fdt) { - error_report("create_device_tree() failed"); - exit(1); - } - - /* Header */ - qemu_fdt_setprop_string(ms->fdt, "/", "compatible", - "linux,dummy-loongson3"); - qemu_fdt_setprop_cell(ms->fdt, "/", "#address-cells", 0x2); - qemu_fdt_setprop_cell(ms->fdt, "/", "#size-cells", 0x2); - qemu_fdt_add_subnode(ms->fdt, "/chosen"); -} - -static void fdt_add_cpu_nodes(const LoongArchMachineState *lams) -{ - int num; - const MachineState *ms = MACHINE(lams); - int smp_cpus = ms->smp.cpus; - - qemu_fdt_add_subnode(ms->fdt, "/cpus"); - qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1); - qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0); - - /* cpu nodes */ - for (num = smp_cpus - 1; num >= 0; num--) { - char *nodename = g_strdup_printf("/cpus/cpu@%d", num); - LoongArchCPU *cpu = LOONGARCH_CPU(qemu_get_cpu(num)); - CPUState *cs = CPU(cpu); - - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu"); - qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", - cpu->dtb_compatible); - if (ms->possible_cpus->cpus[cs->cpu_index].props.has_node_id) { - qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", - ms->possible_cpus->cpus[cs->cpu_index].props.node_id); - } - qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num); - qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", - qemu_fdt_alloc_phandle(ms->fdt)); - g_free(nodename); - } - - /*cpu map */ - qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map"); - - for (num = smp_cpus - 1; num >= 0; num--) { - char *cpu_path = g_strdup_printf("/cpus/cpu@%d", num); - char *map_path; - - if (ms->smp.threads > 1) { - map_path = g_strdup_printf( - "/cpus/cpu-map/socket%d/core%d/thread%d", - num / (ms->smp.cores * ms->smp.threads), - (num / ms->smp.threads) % ms->smp.cores, - num % ms->smp.threads); - } else { - map_path = g_strdup_printf( - "/cpus/cpu-map/socket%d/core%d", - num / ms->smp.cores, - num % ms->smp.cores); - } - qemu_fdt_add_path(ms->fdt, map_path); - qemu_fdt_setprop_phandle(ms->fdt, map_path, "cpu", cpu_path); + DeviceState *dev = DEVICE(flash); + BlockBackend *blk; + hwaddr real_size = size; - g_free(map_path); - g_free(cpu_path); + blk = pflash_cfi01_get_blk(flash); + if (blk) { + real_size = blk_getlength(blk); + assert(real_size && real_size <= size); } -} - -static void fdt_add_fw_cfg_node(const LoongArchMachineState *lams) -{ - char *nodename; - hwaddr base = VIRT_FWCFG_BASE; - const MachineState *ms = MACHINE(lams); - - nodename = g_strdup_printf("/fw_cfg@%" PRIx64, base); - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_string(ms->fdt, nodename, - "compatible", "qemu,fw-cfg-mmio"); - qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", - 2, base, 2, 0x18); - qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); - g_free(nodename); -} -static void fdt_add_pcie_node(const LoongArchMachineState *lams) -{ - char *nodename; - hwaddr base_mmio = VIRT_PCI_MEM_BASE; - hwaddr size_mmio = VIRT_PCI_MEM_SIZE; - hwaddr base_pio = VIRT_PCI_IO_BASE; - hwaddr size_pio = VIRT_PCI_IO_SIZE; - hwaddr base_pcie = VIRT_PCI_CFG_BASE; - hwaddr size_pcie = VIRT_PCI_CFG_SIZE; - hwaddr base = base_pcie; - - const MachineState *ms = MACHINE(lams); - - nodename = g_strdup_printf("/pcie@%" PRIx64, base); - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_string(ms->fdt, nodename, - "compatible", "pci-host-ecam-generic"); - qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "pci"); - qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 3); - qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 2); - qemu_fdt_setprop_cell(ms->fdt, nodename, "linux,pci-domain", 0); - qemu_fdt_setprop_cells(ms->fdt, nodename, "bus-range", 0, - PCIE_MMCFG_BUS(VIRT_PCI_CFG_SIZE - 1)); - qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); - qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", - 2, base_pcie, 2, size_pcie); - qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "ranges", - 1, FDT_PCI_RANGE_IOPORT, 2, VIRT_PCI_IO_OFFSET, - 2, base_pio, 2, size_pio, - 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, - 2, base_mmio, 2, size_mmio); - g_free(nodename); -} + assert(QEMU_IS_ALIGNED(real_size, VIRT_FLASH_SECTOR_SIZE)); + assert(real_size / VIRT_FLASH_SECTOR_SIZE <= UINT32_MAX); -static void fdt_add_irqchip_node(LoongArchMachineState *lams) -{ - MachineState *ms = MACHINE(lams); - char *nodename; - uint32_t irqchip_phandle; - - irqchip_phandle = qemu_fdt_alloc_phandle(ms->fdt); - qemu_fdt_setprop_cell(ms->fdt, "/", "interrupt-parent", irqchip_phandle); - - nodename = g_strdup_printf("/intc@%lx", VIRT_IOAPIC_REG_BASE); - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 3); - qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 0x2); - qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 0x2); - qemu_fdt_setprop(ms->fdt, nodename, "ranges", NULL, 0); - - qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", - "loongarch,ls7a"); - - qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", - 2, VIRT_IOAPIC_REG_BASE, - 2, PCH_PIC_ROUTE_ENTRY_OFFSET); - - qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", irqchip_phandle); - g_free(nodename); + 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 fdt_add_memory_node(MachineState *ms, - uint64_t base, uint64_t size, int node_id) +static void virt_flash_map(LoongArchVirtMachineState *lvms, + MemoryRegion *sysmem) { - char *nodename = g_strdup_printf("/memory@%" PRIx64, base); - - qemu_fdt_add_subnode(ms->fdt, nodename); - qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 2, base, 2, size); - qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "memory"); + PFlashCFI01 *flash0 = lvms->flash[0]; + PFlashCFI01 *flash1 = lvms->flash[1]; - if (ms->numa_state && ms->numa_state->num_nodes) { - qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", node_id); - } - - g_free(nodename); + virt_flash_map1(flash0, VIRT_FLASH0_BASE, VIRT_FLASH0_SIZE, sysmem); + virt_flash_map1(flash1, VIRT_FLASH1_BASE, VIRT_FLASH1_SIZE, sysmem); } -#define PM_BASE 0x10080000 -#define PM_SIZE 0x100 -#define PM_CTRL 0x10 - -static void virt_build_smbios(LoongArchMachineState *lams) +static void virt_build_smbios(LoongArchVirtMachineState *lvms) { - MachineState *ms = MACHINE(lams); - MachineClass *mc = MACHINE_GET_CLASS(lams); + 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); + if (kvm_enabled()) { + product = "KVM Virtual Machine"; + } - smbios_get_tables(ms, NULL, 0, &smbios_tables, &smbios_tables_len, + smbios_set_defaults("QEMU", product, mc->name); + + 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); + virt_acpi_setup(lvms); + virt_fdt_setup(lvms); } static void virt_powerdown_req(Notifier *notifier, void *opaque) { - LoongArchMachineState *s = container_of(notifier, - LoongArchMachineState, powerdown_notifier); + LoongArchVirtMachineState *s; + s = container_of(notifier, LoongArchVirtMachineState, powerdown_notifier); acpi_send_event(s->acpi_ged, ACPI_POWER_DOWN_STATUS); } -struct memmap_entry { - uint64_t address; - uint64_t length; - uint32_t type; - uint32_t reserved; -}; - -static struct memmap_entry *memmap_table; -static unsigned memmap_entries; - -static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) +static void memmap_add_entry(MachineState *ms, uint64_t address, + uint64_t length, uint32_t type) { + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); + struct memmap_entry *memmap_table; + unsigned int memmap_entries; + + memmap_table = lvms->memmap_table; + memmap_entries = lvms->memmap_entries; /* Ensure there are no duplicate entries. */ for (unsigned i = 0; i < memmap_entries; i++) { assert(memmap_table[i].address != address); @@ -371,89 +193,29 @@ static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) memmap_table[memmap_entries].type = cpu_to_le32(type); memmap_table[memmap_entries].reserved = 0; memmap_entries++; + lvms->memmap_table = memmap_table; + lvms->memmap_entries = 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 & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS); -} - -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); + MachineClass *mc = MACHINE_GET_CLASS(lvms); uint32_t event = ACPI_GED_PWR_DOWN_EVT; if (ms->ram_slots) { event |= ACPI_GED_MEM_HOTPLUG_EVT; } + + if (mc->has_hotpluggable_cpus) { + event |= ACPI_GED_CPU_HOTPLUG_EVT; + } + dev = qdev_new(TYPE_ACPI_GED); qdev_prop_set_uint32(dev, "ged-event", event); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); /* ged event */ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, VIRT_GED_EVT_ADDR); @@ -462,9 +224,12 @@ static DeviceState *create_acpi_ged(DeviceState *pch_pic, LoongArchMachineState /* ged regs used for reset and power down */ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, VIRT_GED_REG_ADDR); + if (mc->has_hotpluggable_cpus) { + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 3, VIRT_GED_CPUHP_ADDR); + } + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(pch_pic, VIRT_SCI_IRQ - VIRT_GSI_BASE)); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); return dev; } @@ -493,21 +258,22 @@ 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) { - MachineClass *mc = MACHINE_GET_CLASS(lams); + 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); @@ -533,22 +299,26 @@ static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState * memory_region_add_subregion(get_system_memory(), VIRT_PCI_IO_BASE, pio_alias); - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { sysbus_connect_irq(d, i, qdev_get_gpio_in(pch_pic, 16 + i)); gpex_set_irq_num(GPEX_HOST(gpex_dev), i, 16 + i); } - serial_mm_init(get_system_memory(), VIRT_UART_BASE, 0, - qdev_get_gpio_in(pch_pic, - VIRT_UART_IRQ - VIRT_GSI_BASE), - 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); - fdt_add_uart_node(lams); + /* + * 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); + } /* Network init */ - for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], pci_bus, mc->default_nic, NULL); - } + pci_init_nic_devices(pci_bus, mc->default_nic); /* * There are some invalid guest memory access. @@ -558,113 +328,106 @@ static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState * sysbus_create_simple("ls7a_rtc", VIRT_RTC_REG_BASE, qdev_get_gpio_in(pch_pic, VIRT_RTC_IRQ - VIRT_GSI_BASE)); - fdt_add_rtc_node(lams); - 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 virt_cpu_irq_init(LoongArchVirtMachineState *lvms) +{ + int num; + MachineState *ms = MACHINE(lvms); + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *possible_cpus; + CPUState *cs; + + /* cpu nodes */ + possible_cpus = mc->possible_cpu_arch_ids(ms); + for (num = 0; num < possible_cpus->len; num++) { + cs = possible_cpus->cpus[num].cpu; + if (cs == NULL) { + continue; + } + + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), DEVICE(cs), + &error_abort); + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), DEVICE(cs), + &error_abort); + } } -static void loongarch_irq_init(LoongArchMachineState *lams) +static void virt_irq_init(LoongArchVirtMachineState *lvms) { - MachineState *ms = MACHINE(lams); - DeviceState *pch_pic, *pch_msi, *cpudev; + DeviceState *pch_pic, *pch_msi; DeviceState *ipi, *extioi; SysBusDevice *d; - LoongArchCPU *lacpu; - CPULoongArchState *env; - CPUState *cpu_state; - int cpu, pin, i, start, num; - - extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); - sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); + int i, start, num; /* - * 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 | + * +--------+ +---------+ +---------+ */ - 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); - - ipi = qdev_new(TYPE_LOONGARCH_IPI); - sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); - /* connect ipi irq to cpu irq */ - qdev_connect_gpio_out(ipi, 0, 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), - 0)); - memory_region_add_subregion(&env->system_iocsr, MAIL_SEND_ADDR, - sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), - 1)); - /* - * extioi iocsr memory region - * only one extioi is added on loongarch virt machine - * external device interrupt can only be routed to cpu 0-3 - */ - if (cpu < EXTIOI_CPUS) - memory_region_add_subregion(&env->system_iocsr, APIC_BASE, - sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), - cpu)); - env->ipistate = ipi; - } + /* Create IPI device */ + ipi = qdev_new(TYPE_LOONGARCH_IPI); + lvms->ipi = ipi; + sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); - /* - * connect ext irq to the cpu irq - * cpu_pin[9:2] <= intc_pin[7:0] - */ - for (cpu = 0; cpu < MIN(ms->smp.cpus, EXTIOI_CPUS); cpu++) { - cpudev = DEVICE(qemu_get_cpu(cpu)); - for (pin = 0; pin < LS3A_INTC_IP; pin++) { - qdev_connect_gpio_out(extioi, (cpu * 8 + pin), - qdev_get_gpio_in(cpudev, pin + 2)); - } + /* Create EXTIOI device */ + extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); + lvms->extioi = extioi; + 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); - pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC); + virt_cpu_irq_init(lvms); + pch_pic = qdev_new(TYPE_LOONGARCH_PIC); num = VIRT_PCH_PIC_IRQ_NUM; qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num); d = SYS_BUS_DEVICE(pch_pic); sysbus_realize_and_unref(d, &error_fatal); - memory_region_add_subregion(get_system_memory(), VIRT_IOAPIC_REG_BASE, - sysbus_mmio_get_region(d, 0)); - memory_region_add_subregion(get_system_memory(), - VIRT_IOAPIC_REG_BASE + PCH_PIC_ROUTE_ENTRY_OFFSET, - sysbus_mmio_get_region(d, 1)); - memory_region_add_subregion(get_system_memory(), - VIRT_IOAPIC_REG_BASE + PCH_PIC_INT_STATUS_LO, - sysbus_mmio_get_region(d, 2)); - - /* Connect pch_pic irqs to extioi */ - for (int i = 0; i < num; i++) { - qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i)); - } pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI); start = num; @@ -674,24 +437,72 @@ static void loongarch_irq_init(LoongArchMachineState *lams) d = SYS_BUS_DEVICE(pch_msi); sysbus_realize_and_unref(d, &error_fatal); sysbus_mmio_map(d, 0, VIRT_PCH_MSI_ADDR_LOW); - for (i = 0; i < num; i++) { - /* Connect pch_msi irqs to extioi */ - qdev_connect_gpio_out(DEVICE(d), i, - qdev_get_gpio_in(extioi, i + start)); - } - loongarch_devices_init(pch_pic, lams); + if (kvm_irqchip_in_kernel()) { + kvm_loongarch_init_irq_routing(); + } else { + /* 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)); + + /* EXTIOI iocsr memory region */ + 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)); + } + + /* PCH_PIC memory region */ + memory_region_add_subregion(get_system_memory(), VIRT_IOAPIC_REG_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(pch_pic), 0)); + + /* Connect pch_pic irqs to extioi */ + for (i = 0; i < VIRT_PCH_PIC_IRQ_NUM; i++) { + qdev_connect_gpio_out(DEVICE(pch_pic), i, + qdev_get_gpio_in(extioi, i)); + } + + for (i = VIRT_PCH_PIC_IRQ_NUM; i < EXTIOI_IRQS; i++) { + /* Connect pch_msi irqs to extioi */ + qdev_connect_gpio_out(DEVICE(pch_msi), i - VIRT_PCH_PIC_IRQ_NUM, + qdev_get_gpio_in(extioi, i)); + } + } + virt_devices_init(pch_pic, lvms); } -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()); - lams->bios_loaded = false; + pflash_blk0 = pflash_cfi01_get_blk(lvms->flash[0]); - virt_flash_map(lams, get_system_memory()); + 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; + } if (filename) { bios_name = qemu_find_file(QEMU_FILE_TYPE_BIOS, filename); @@ -700,162 +511,235 @@ 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); + lvms->bios_loaded = true; + } +} + +static MemTxResult virt_iocsr_misc_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(opaque); + uint64_t features; - 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; + switch (addr) { + case MISC_FUNC_REG: + if (kvm_irqchip_in_kernel()) { + return MEMTX_OK; + } + + 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 reset_load_elf(void *opaque) +static MemTxResult virt_iocsr_misc_read(void *opaque, hwaddr addr, + uint64_t *data, + unsigned size, MemTxAttrs attrs) { - LoongArchCPU *cpu = opaque; - CPULoongArchState *env = &cpu->env; + 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 (kvm_irqchip_in_kernel()) { + return MEMTX_OK; + } - cpu_reset(CPU(cpu)); - if (env->load_elf) { - cpu_set_pc(CPU(cpu), env->elf_address); + 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 void fw_cfg_add_kernel_info(FWCfgState *fw_cfg) +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) { - /* - * Expose the kernel, the command line, and the initrd in fw_cfg. - * We don't process them here at all, it's all left to the - * firmware. - */ - load_image_to_fw_cfg(fw_cfg, - FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA, - loaderparams.kernel_filename, - false); - - if (loaderparams.initrd_filename) { - load_image_to_fw_cfg(fw_cfg, - FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, - loaderparams.initrd_filename, false); + 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; } - 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); + /* add fw_cfg memory map of node0 */ + if (nb_numa_nodes) { + size = numa_info[0].node_mem; + } else { + size = ram_size; } -} -static void loongarch_firmware_boot(LoongArchMachineState *lams) -{ - fw_cfg_add_kernel_info(lams->fw_cfg); -} + if (size >= gap) { + memmap_add_entry(ms, base, gap, 1); + size -= gap; + base = VIRT_HIGHMEM_BASE; + } -static void loongarch_direct_kernel_boot(LoongArchMachineState *lams) -{ - MachineState *machine = MACHINE(lams); - int64_t kernel_addr = 0; - LoongArchCPU *lacpu; - int i; + if (size) { + memmap_add_entry(ms, base, size, 1); + base += size; + } - 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; - } + 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(ms, 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(ms, base, size, 1); } } -static void loongarch_init(MachineState *machine) +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, phyAddr = 0; MemoryRegion *address_space_mem = get_system_memory(); - LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); - int nb_numa_nodes = machine->numa_state->num_nodes; - NodeInfo *numa_info = machine->numa_state->nodes; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); int i; - hwaddr fdt_base; - const CPUArchIdList *possible_cpus; + hwaddr base, size, ram_size = machine->ram_size; MachineClass *mc = MACHINE_GET_CLASS(machine); - CPUState *cpu; - char *ramName = NULL; + Object *cpuobj; if (!cpu_model) { cpu_model = LOONGARCH_CPU_TYPE_NAME("la464"); } - if (ram_size < 1 * GiB) { - error_report("ram_size must be greater than 1G."); - exit(1); - } - create_fdt(lams); - /* Init CPUs */ + /* 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); - 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 = OBJECT(cpu); - lacpu = LOONGARCH_CPU(cpu); - lacpu->phy_id = machine->possible_cpus->cpus[i].arch_id; + /* Init CPUs */ + mc->possible_cpu_arch_ids(machine); + for (i = 0; i < machine->smp.cpus; i++) { + cpuobj = object_new(machine->cpu_type); + if (cpuobj == NULL) { + error_report("Fail to create object with type %s ", + machine->cpu_type); + exit(EXIT_FAILURE); + } + qdev_realize_and_unref(DEVICE(cpuobj), NULL, &error_fatal); } - fdt_add_cpu_nodes(lams); + fw_cfg_add_memory(machine); /* Node0 memory */ - memmap_add_entry(VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 1); - fdt_add_memory_node(machine, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 0); - memory_region_init_alias(&lams->lowmem, NULL, "loongarch.node0.lowram", - machine->ram, offset, VIRT_LOWMEM_SIZE); - memory_region_add_subregion(address_space_mem, phyAddr, &lams->lowmem); - - offset += VIRT_LOWMEM_SIZE; - if (nb_numa_nodes > 0) { - assert(numa_info[0].node_mem > VIRT_LOWMEM_SIZE); - highram_size = numa_info[0].node_mem - VIRT_LOWMEM_SIZE; - } else { - highram_size = ram_size - VIRT_LOWMEM_SIZE; + size = ram_size; + base = VIRT_LOWMEM_BASE; + if (size > VIRT_LOWMEM_SIZE) { + size = VIRT_LOWMEM_SIZE; } - phyAddr = VIRT_HIGHMEM_BASE; - memmap_add_entry(phyAddr, highram_size, 1); - fdt_add_memory_node(machine, phyAddr, highram_size, 0); - memory_region_init_alias(&lams->highmem, NULL, "loongarch.node0.highram", - machine->ram, offset, highram_size); - memory_region_add_subregion(address_space_mem, phyAddr, &lams->highmem); - - /* Node1 - Nodemax memory */ - offset += highram_size; - phyAddr += highram_size; - - for (i = 1; i < nb_numa_nodes; i++) { - MemoryRegion *nodemem = g_new(MemoryRegion, 1); - ramName = g_strdup_printf("loongarch.node%d.ram", i); - memory_region_init_alias(nodemem, NULL, ramName, machine->ram, - offset, numa_info[i].node_mem); - memory_region_add_subregion(address_space_mem, phyAddr, nodemem); - memmap_add_entry(phyAddr, numa_info[i].node_mem, 1); - fdt_add_memory_node(machine, phyAddr, numa_info[i].node_mem, i); - offset += numa_info[i].node_mem; - phyAddr += numa_info[i].node_mem; + + 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; } /* initialize device memory address space */ if (machine->ram_size < machine->maxram_size) { ram_addr_t device_mem_size = machine->maxram_size - machine->ram_size; - hwaddr device_mem_base; if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) { error_report("unsupported amount of memory slots: %"PRIu64, @@ -869,105 +753,292 @@ static void loongarch_init(MachineState *machine) "%d bytes", TARGET_PAGE_SIZE); exit(EXIT_FAILURE); } - /* device memory base is the top of high memory address. */ - device_mem_base = ROUND_UP(VIRT_HIGHMEM_BASE + highram_size, 1 * GiB); - machine_memory_devices_init(machine, device_mem_base, device_mem_size); + 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", - memmap_table, - sizeof(struct memmap_entry) * (memmap_entries)); + 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", + lvms->memmap_table, + sizeof(struct memmap_entry) * lvms->memmap_entries); + } + + /* Initialize the IO interrupt subsystem */ + virt_irq_init(lvms); + 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); + + lvms->bootinfo.ram_size = ram_size; + loongarch_load_kernel(machine, &lvms->bootinfo); +} + +static void virt_get_acpi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + OnOffAuto acpi = lvms->acpi; + + visit_type_OnOffAuto(v, name, &acpi, errp); +} + +static void virt_set_acpi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &lvms->acpi, errp); +} + +static char *virt_get_oem_id(Object *obj, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + + return g_strdup(lvms->oem_id); +} + +static void virt_set_oem_id(Object *obj, const char *value, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + size_t len = strlen(value); + + if (len > 6) { + error_setg(errp, + "User specified oem-id value is bigger than 6 bytes in size"); + return; + } + + strncpy(lvms->oem_id, value, 6); +} + +static char *virt_get_oem_table_id(Object *obj, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + + return g_strdup(lvms->oem_table_id); +} + +static void virt_set_oem_table_id(Object *obj, const char *value, + Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + size_t len = strlen(value); + + if (len > 8) { + error_setg(errp, + "User specified oem-table-id value is bigger than 8 bytes in size"); + return; } - 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); + strncpy(lvms->oem_table_id, value, 8); +} + +static void virt_initfn(Object *obj) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + + 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 void virt_get_topo_from_index(MachineState *ms, + LoongArchCPUTopo *topo, int index) +{ + topo->socket_id = index / (ms->smp.cores * ms->smp.threads); + topo->core_id = index / ms->smp.threads % ms->smp.cores; + topo->thread_id = index % ms->smp.threads; +} + +static unsigned int topo_align_up(unsigned int count) +{ + g_assert(count >= 1); + count -= 1; + return BIT(count ? 32 - clz32(count) : 0); +} + +/* + * LoongArch Reference Manual Vol1, Chapter 7.4.12 CPU Identity + * For CPU architecture, bit0 .. bit8 is valid for CPU id, max cpuid is 512 + * However for IPI/Eiointc interrupt controller, max supported cpu id for + * irq routingis 256 + * + * Here max cpu id is 256 for virt machine + */ +static int virt_get_arch_id_from_topo(MachineState *ms, LoongArchCPUTopo *topo) +{ + int arch_id, threads, cores, sockets; + + threads = topo_align_up(ms->smp.threads); + cores = topo_align_up(ms->smp.cores); + sockets = topo_align_up(ms->smp.sockets); + if ((threads * cores * sockets) > 256) { + error_report("Exceeding max cpuid 256 with sockets[%d] cores[%d]" + " threads[%d]", ms->smp.sockets, ms->smp.cores, + ms->smp.threads); + exit(1); + } + + arch_id = topo->thread_id + topo->core_id * threads; + arch_id += topo->socket_id * threads * cores; + return arch_id; +} + +/* Find cpu slot in machine->possible_cpus by arch_id */ +static CPUArchId *virt_find_cpu_slot(MachineState *ms, int arch_id) +{ + int n; + for (n = 0; n < ms->possible_cpus->len; n++) { + if (ms->possible_cpus->cpus[n].arch_id == arch_id) { + return &ms->possible_cpus->cpus[n]; } } - fdt_add_flash_node(lams); - /* register reset function */ - for (i = 0; i < machine->smp.cpus; i++) { - lacpu = LOONGARCH_CPU(qemu_get_cpu(i)); - qemu_register_reset(reset_load_elf, lacpu); + + return NULL; +} + +/* Find cpu slot for cold-plut CPU object where cpu is NULL */ +static CPUArchId *virt_find_empty_cpu_slot(MachineState *ms) +{ + int n; + for (n = 0; n < ms->possible_cpus->len; n++) { + if (ms->possible_cpus->cpus[n].cpu == NULL) { + return &ms->possible_cpus->cpus[n]; + } } - /* Initialize the IO interrupt subsystem */ - loongarch_irq_init(lams); - fdt_add_irqchip_node(lams); - platform_bus_add_all_fdt_nodes(machine->fdt, "/intc", - VIRT_PLATFORM_BUS_BASEADDRESS, - VIRT_PLATFORM_BUS_SIZE, - VIRT_PLATFORM_BUS_IRQ); - lams->machine_done.notify = virt_machine_done; - qemu_add_machine_init_done_notifier(&lams->machine_done); - /* connect powerdown request */ - lams->powerdown_notifier.notify = virt_powerdown_req; - qemu_register_powerdown_notifier(&lams->powerdown_notifier); - fdt_add_pcie_node(lams); - /* - * Since lowmem region starts from 0 and Linux kernel legacy start address - * at 2 MiB, FDT base address is located at 1 MiB to avoid NULL pointer - * access. FDT size limit with 1 MiB. - * Put the FDT into the memory map as a ROM image: this will ensure - * the FDT is copied again upon reset, even if addr points into RAM. - */ - fdt_base = 1 * MiB; - qemu_fdt_dumpdtb(machine->fdt, lams->fdt_size); - rom_add_blob_fixed("fdt", machine->fdt, lams->fdt_size, fdt_base); + return NULL; } -bool loongarch_is_acpi_enabled(LoongArchMachineState *lams) +static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { - if (lams->acpi == ON_OFF_AUTO_OFF) { - return false; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + MachineState *ms = MACHINE(OBJECT(hotplug_dev)); + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + CPUState *cs = CPU(dev); + CPUArchId *cpu_slot; + LoongArchCPUTopo topo; + int arch_id; + + if (lvms->acpi_ged) { + if ((cpu->thread_id < 0) || (cpu->thread_id >= ms->smp.threads)) { + error_setg(errp, + "Invalid thread-id %u specified, must be in range 1:%u", + cpu->thread_id, ms->smp.threads - 1); + return; + } + + if ((cpu->core_id < 0) || (cpu->core_id >= ms->smp.cores)) { + error_setg(errp, + "Invalid core-id %u specified, must be in range 1:%u", + cpu->core_id, ms->smp.cores - 1); + return; + } + + if ((cpu->socket_id < 0) || (cpu->socket_id >= ms->smp.sockets)) { + error_setg(errp, + "Invalid socket-id %u specified, must be in range 1:%u", + cpu->socket_id, ms->smp.sockets - 1); + return; + } + + topo.socket_id = cpu->socket_id; + topo.core_id = cpu->core_id; + topo.thread_id = cpu->thread_id; + arch_id = virt_get_arch_id_from_topo(ms, &topo); + cpu_slot = virt_find_cpu_slot(ms, arch_id); + if (CPU(cpu_slot->cpu)) { + error_setg(errp, + "cpu(id%d=%d:%d:%d) with arch-id %" PRIu64 " exists", + cs->cpu_index, cpu->socket_id, cpu->core_id, + cpu->thread_id, cpu_slot->arch_id); + return; + } + } else { + /* For cold-add cpu, find empty cpu slot */ + cpu_slot = virt_find_empty_cpu_slot(ms); + topo.socket_id = cpu_slot->props.socket_id; + topo.core_id = cpu_slot->props.core_id; + topo.thread_id = cpu_slot->props.thread_id; + object_property_set_int(OBJECT(dev), "socket-id", topo.socket_id, NULL); + object_property_set_int(OBJECT(dev), "core-id", topo.core_id, NULL); + object_property_set_int(OBJECT(dev), "thread-id", topo.thread_id, NULL); } - return true; + + cpu->env.address_space_iocsr = &lvms->as_iocsr; + cpu->phy_id = cpu_slot->arch_id; + cs->cpu_index = cpu_slot - ms->possible_cpus->cpus; + numa_cpu_pre_plug(cpu_slot, dev, errp); } -static void loongarch_get_acpi(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static void virt_cpu_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); - OnOffAuto acpi = lams->acpi; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + CPUState *cs = CPU(dev); + + if (cs->cpu_index == 0) { + error_setg(errp, "hot-unplug of boot cpu(id%d=%d:%d:%d) not supported", + cs->cpu_index, cpu->socket_id, + cpu->core_id, cpu->thread_id); + return; + } - visit_type_OnOffAuto(v, name, &acpi, errp); + hotplug_handler_unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev, errp); } -static void loongarch_set_acpi(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static void virt_cpu_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); + CPUArchId *cpu_slot; + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); - visit_type_OnOffAuto(v, name, &lams->acpi, errp); + /* Notify ipi and extioi irqchip to remove interrupt routing to CPU */ + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->ipi), dev, &error_abort); + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->extioi), dev, &error_abort); + + /* Notify acpi ged CPU removed */ + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &error_abort); + + cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); + cpu_slot->cpu = NULL; } -static void loongarch_machine_initfn(Object *obj) +static void virt_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); + CPUArchId *cpu_slot; + LoongArchCPU *cpu = LOONGARCH_CPU(dev); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + + if (lvms->ipi) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), dev, &error_abort); + } - lams->acpi = ON_OFF_AUTO_AUTO; - lams->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); - lams->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); - virt_flash_create(lams); + if (lvms->extioi) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), dev, &error_abort); + } + + if (lvms->acpi_ged) { + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, + &error_abort); + } + + cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); + cpu_slot->cpu = CPU(dev); } static bool memhp_type_supported(DeviceState *dev) @@ -980,85 +1051,96 @@ 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)) { virt_mem_pre_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_pre_plug(hotplug_dev, dev, errp); } } static void virt_mem_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); + 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)) { virt_mem_unplug_request(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_unplug_request(hotplug_dev, dev, errp); } } 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)) { virt_mem_unplug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_unplug(hotplug_dev, dev, errp); } } 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); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { + virt_cpu_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_LOONGARCH_CPU) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) || memhp_type_supported(dev)) { return HOTPLUG_HANDLER(machine); } @@ -1067,8 +1149,9 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) { - int n; + int n, arch_id; unsigned int max_cpus = ms->smp.max_cpus; + LoongArchCPUTopo topo; if (ms->possible_cpus) { assert(ms->possible_cpus->len == max_cpus); @@ -1079,23 +1162,23 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) sizeof(CPUArchId) * max_cpus); ms->possible_cpus->len = max_cpus; for (n = 0; n < ms->possible_cpus->len; n++) { + virt_get_topo_from_index(ms, &topo, n); + arch_id = virt_get_arch_id_from_topo(ms, &topo); ms->possible_cpus->cpus[n].type = ms->cpu_type; - ms->possible_cpus->cpus[n].arch_id = n; - + ms->possible_cpus->cpus[n].arch_id = arch_id; + ms->possible_cpus->cpus[n].vcpus_count = 1; 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.socket_id = topo.socket_id; 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.core_id = topo.core_id; ms->possible_cpus->cpus[n].props.has_thread_id = true; - ms->possible_cpus->cpus[n].props.thread_id = n % ms->smp.threads; + ms->possible_cpus->cpus[n].props.thread_id = topo.thread_id; } return ms->possible_cpus; } -static CpuInstanceProperties -virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index) +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); @@ -1106,27 +1189,25 @@ virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index) static int64_t virt_get_default_cpu_node_id(const MachineState *ms, int idx) { - int64_t nidx = 0; + int64_t socket_id; if (ms->numa_state->num_nodes) { - nidx = idx / (ms->smp.cpus / ms->numa_state->num_nodes); - if (ms->numa_state->num_nodes <= nidx) { - nidx = ms->numa_state->num_nodes - 1; - } + socket_id = ms->possible_cpus->cpus[idx].props.socket_id; + return socket_id % ms->numa_state->num_nodes; + } else { + return 0; } - return nidx; } -static void loongarch_class_init(ObjectClass *oc, void *data) +static void virt_class_init(ObjectClass *oc, const 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->desc = "QEMU LoongArch Virtual Machine"; mc->max_cpus = LOONGARCH_MAX_CPUS; mc->is_default = 1; mc->default_kernel_irqchip_split = false; @@ -1139,36 +1220,59 @@ static void loongarch_class_init(ObjectClass *oc, void *data) mc->numa_mem_supported = true; mc->auto_enable_numa_with_memhp = true; mc->auto_enable_numa_with_memdev = true; - mc->get_hotplug_handler = virt_machine_get_hotplug_handler; + mc->has_hotpluggable_cpus = true; + mc->get_hotplug_handler = virt_get_hotplug_handler; mc->default_nic = "virtio-net-pci"; - hc->plug = loongarch_machine_device_plug_cb; - hc->pre_plug = virt_machine_device_pre_plug; - hc->unplug_request = virt_machine_device_unplug_request; - hc->unplug = virt_machine_device_unplug; + 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); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif + object_class_property_add_str(oc, "x-oem-id", + virt_get_oem_id, + virt_set_oem_id); + object_class_property_set_description(oc, "x-oem-id", + "Override the default value of field OEMID " + "in ACPI table header." + "The string may be up to 6 bytes in size"); + + + object_class_property_add_str(oc, "x-oem-table-id", + virt_get_oem_table_id, + virt_set_oem_table_id); + object_class_property_set_description(oc, "x-oem-table-id", + "Override the default value of field OEM Table ID " + "in ACPI table header." + "The string may be up to 8 bytes in size"); } -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, - .interfaces = (InterfaceInfo[]) { + .instance_size = sizeof(LoongArchVirtMachineState), + .class_init = virt_class_init, + .instance_init = virt_initfn, + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } }, } }; -DEFINE_TYPES(loongarch_machine_types) +DEFINE_TYPES(virt_machine_types) diff --git a/hw/m68k/Kconfig b/hw/m68k/Kconfig index f839f8a0306..aff769b30f1 100644 --- a/hw/m68k/Kconfig +++ b/hw/m68k/Kconfig @@ -1,20 +1,29 @@ 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 + select EMPTY_SLOT config Q800 bool + default y + depends on M68K select MAC_VIA select NUBUS select MACFB @@ -23,9 +32,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 11ae4c9795b..d97399b882b 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -14,18 +14,20 @@ #include "hw/loader.h" #include "elf.h" #include "qemu/error-report.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #define KERNEL_LOAD_ADDR 0x10000 #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) { @@ -72,7 +74,7 @@ static void an5206_init(MachineState *machine) } kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, - NULL, NULL, NULL, 1, EM_68K, 0, 0); + NULL, NULL, NULL, ELFDATA2MSB, EM_68K, 0, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, diff --git a/hw/m68k/bootinfo.h b/hw/m68k/bootinfo.h index a3d37e3c809..0b3e7c4ea01 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 2ab1b4f0593..a25e782403f 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -10,12 +10,13 @@ #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" #include "qemu/timer.h" #include "hw/ptimer.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/sysbus.h" /* General purpose timer module. */ @@ -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); @@ -585,7 +582,7 @@ static const MemoryRegionOps m5206_mbar_ops = { .write = m5206_mbar_writefn, .valid.min_access_size = 1, .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, }; static void mcf5206_mbar_realize(DeviceState *dev, Error **errp) @@ -597,21 +594,26 @@ 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 void mcf5206_mbar_class_init(ObjectClass *oc, void *data) +static const Property mcf5206_mbar_properties[] = { + DEFINE_PROP_LINK("m68k-cpu", m5206_mbar_state, cpu, + TYPE_M68K_CPU, M68kCPU *), +}; + +static void mcf5206_mbar_class_init(ObjectClass *oc, const 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 be1033f84f8..75cc076f787 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" @@ -18,8 +26,8 @@ #include "hw/m68k/mcf_fec.h" #include "qemu/timer.h" #include "hw/ptimer.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" +#include "system/system.h" +#include "system/qtest.h" #include "net/net.h" #include "hw/boards.h" #include "hw/loader.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; @@ -145,7 +155,7 @@ static uint64_t m5208_timer_read(void *opaque, hwaddr addr, static const MemoryRegionOps m5208_timer_ops = { .read = m5208_timer_read, .write = m5208_timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, }; static uint64_t m5208_sys_read(void *opaque, hwaddr addr, @@ -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; } } @@ -182,15 +192,53 @@ static void m5208_sys_write(void *opaque, hwaddr addr, static const MemoryRegionOps m5208_sys_ops = { .read = m5208_sys_read, .write = m5208_sys_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, +}; + +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_BIG_ENDIAN, }; -static void mcf5208_sys_init(MemoryRegion *address_space, qemu_irq *pic) +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. */ @@ -331,7 +372,7 @@ static void mcf5208evb_init(MachineState *machine) } kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, - NULL, NULL, NULL, 1, EM_68K, 0, 0); + NULL, NULL, NULL, ELFDATA2MSB, EM_68K, 0, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, diff --git a/hw/m68k/mcf_intc.c b/hw/m68k/mcf_intc.c index 4cd30188c05..e3055b841e8 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" @@ -165,7 +166,7 @@ static void mcf_intc_reset(DeviceState *dev) static const MemoryRegionOps mcf_intc_ops = { .read = mcf_intc_read, .write = mcf_intc_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, }; static void mcf_intc_instance_init(Object *obj) @@ -173,14 +174,21 @@ 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 void mcf_intc_class_init(ObjectClass *oc, void *data) +static const Property mcf_intc_properties[] = { + DEFINE_PROP_LINK("m68k-cpu", mcf_intc_state, cpu, + TYPE_M68K_CPU, M68kCPU *), +}; + +static void mcf_intc_class_init(ObjectClass *oc, const 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 +211,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/next-cube.c b/hw/m68k/next-cube.c index 5d244b3b956..957644b2d1e 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -2,6 +2,7 @@ * NeXT Cube System Driver * * Copyright (c) 2011 Bryce Lanham + * Copyright (c) 2024 Mark Cave-Ayland * * This code is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published @@ -11,8 +12,9 @@ #include "qemu/osdep.h" #include "exec/hwaddr.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" +#include "exec/cpu-interrupt.h" +#include "system/system.h" +#include "system/qtest.h" #include "hw/irq.h" #include "hw/m68k/next-cube.h" #include "hw/boards.h" @@ -22,6 +24,7 @@ #include "qom/object.h" #include "hw/char/escc.h" /* ZILOG 8530 Serial Emulation */ #include "hw/block/fdc.h" +#include "hw/misc/empty_slot.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -37,43 +40,43 @@ #define DPRINTF(fmt, ...) do { } while (0) #endif -#define TYPE_NEXT_MACHINE MACHINE_TYPE_NAME("next-cube") -OBJECT_DECLARE_SIMPLE_TYPE(NeXTState, NEXT_MACHINE) - #define ENTRY 0x0100001e #define RAM_SIZE 0x4000000 #define ROM_FILE "Rev_2.5_v66.bin" -typedef struct next_dma { - uint32_t csr; - - uint32_t saved_next; - uint32_t saved_limit; - uint32_t saved_start; - uint32_t saved_stop; - uint32_t next; - uint32_t limit; - uint32_t start; - uint32_t stop; +#define TYPE_NEXT_RTC "next-rtc" +OBJECT_DECLARE_SIMPLE_TYPE(NeXTRTC, NEXT_RTC) - uint32_t next_initbuf; - uint32_t size; -} next_dma; +struct NeXTRTC { + SysBusDevice parent_obj; -typedef struct NextRtc { + int8_t phase; uint8_t ram[32]; uint8_t command; uint8_t value; uint8_t status; uint8_t control; uint8_t retval; -} NextRtc; -struct NeXTState { - MachineState parent; + qemu_irq data_out_irq; + qemu_irq power_irq; +}; - next_dma dma[10]; +#define TYPE_NEXT_SCSI "next-scsi" +OBJECT_DECLARE_SIMPLE_TYPE(NeXTSCSI, NEXT_SCSI) + +/* NeXT SCSI Controller */ +struct NeXTSCSI { + SysBusDevice parent_obj; + + MemoryRegion scsi_mem; + + SysBusESPState sysbus_esp; + + MemoryRegion scsi_csr_mem; + uint8_t scsi_csr_1; + uint8_t scsi_csr_2; }; #define TYPE_NEXT_PC "next-pc" @@ -85,17 +88,61 @@ struct NeXTPC { M68kCPU *cpu; + MemoryRegion floppy_mem; + MemoryRegion timer_mem; + MemoryRegion dummyen_mem; MemoryRegion mmiomem; MemoryRegion scrmem; 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; + + NeXTSCSI next_scsi; - NextRtc rtc; + qemu_irq scsi_reset; + qemu_irq scsi_dma; + + ESCCState escc; + + NeXTRTC rtc; + qemu_irq rtc_data_irq; + qemu_irq rtc_cmd_reset_irq; +}; + +typedef struct next_dma { + uint32_t csr; + + uint32_t saved_next; + uint32_t saved_limit; + uint32_t saved_start; + uint32_t saved_stop; + + uint32_t next; + uint32_t limit; + uint32_t start; + uint32_t stop; + + uint32_t next_initbuf; + uint32_t size; +} next_dma; + +#define TYPE_NEXT_MACHINE MACHINE_TYPE_NAME("next-cube") +OBJECT_DECLARE_SIMPLE_TYPE(NeXTState, NEXT_MACHINE) + +struct NeXTState { + MachineState parent; + + MemoryRegion rom; + MemoryRegion rom2; + MemoryRegion dmamem; + MemoryRegion bmapm1; + MemoryRegion bmapm2; + + next_dma dma[10]; }; /* Thanks to NeXT forums for this */ @@ -118,332 +165,122 @@ 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; } } +} - if (scr2_2 & 0x1) { - /* DPRINTF("RTC %x phase %i\n", scr2_2, phase); */ - if (phase == -1) { - 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) { - rtc->command = (rtc->command << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); - } - if (phase >= 8 && 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))) { - scr2_2 |= SCR2_RTDATA; - } - - rtc->retval = (rtc->retval << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); - } - /* read the status 0x30 */ - if (rtc->command == 0x30) { - scr2_2 = scr2_2 & (~SCR2_RTDATA); - /* for now status = 0x98 (new rtc + FTU) */ - if (rtc->status & (0x80 >> (phase - 8))) { - scr2_2 |= SCR2_RTDATA; - } - - rtc->retval = (rtc->retval << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); - } - /* read the status 0x31 */ - if (rtc->command == 0x31) { - scr2_2 = scr2_2 & (~SCR2_RTDATA); - if (rtc->control & (0x80 >> (phase - 8))) { - scr2_2 |= SCR2_RTDATA; - } - rtc->retval = (rtc->retval << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); - } - - if ((rtc->command >= 0x20) && (rtc->command <= 0x2F)) { - scr2_2 = scr2_2 & (~SCR2_RTDATA); - /* for now 0x00 */ - time_t time_h = time(NULL); - struct tm *info = localtime(&time_h); - int ret = 0; - - switch (rtc->command) { - case 0x20: - ret = SCR2_TOBCD(info->tm_sec); - break; - case 0x21: - ret = SCR2_TOBCD(info->tm_min); - break; - case 0x22: - ret = SCR2_TOBCD(info->tm_hour); - break; - case 0x24: - ret = SCR2_TOBCD(info->tm_mday); - break; - case 0x25: - ret = SCR2_TOBCD((info->tm_mon + 1)); - break; - case 0x26: - ret = SCR2_TOBCD((info->tm_year - 100)); - break; - - } - - if (ret & (0x80 >> (phase - 8))) { - scr2_2 |= SCR2_RTDATA; - } - rtc->retval = (rtc->retval << 1) | - ((scr2_2 & SCR2_RTDATA) ? 1 : 0); - } +static void next_scr2_rtc_update(NeXTPC *s) +{ + uint8_t old_scr2_rtc, scr2_rtc; - } + old_scr2_rtc = extract32(s->old_scr2, 8, 8); + scr2_rtc = extract32(s->scr2, 8, 8); - phase++; - if (phase == 16) { - if (rtc->command >= 0x80 && rtc->command <= 0x9F) { - rtc->ram[rtc->command - 0x80] = rtc->value; - } - /* write to x30 register */ - if (rtc->command == 0xB1) { - /* clear FTU */ - if (rtc->value & 0x04) { - rtc->status = rtc->status & (~0x18); - s->int_status = s->int_status & (~0x04); - } - } + if (scr2_rtc & 0x1) { + /* DPRINTF("RTC %x phase %i\n", scr2_2, rtc->phase); */ + /* If we are in going down clock... do something */ + if (((old_scr2_rtc & SCR2_RTCLK) != (scr2_rtc & SCR2_RTCLK)) && + ((scr2_rtc & SCR2_RTCLK) == 0)) { + if (scr2_rtc & SCR2_RTDATA) { + qemu_irq_raise(s->rtc_data_irq); + } else { + qemu_irq_lower(s->rtc_data_irq); } } } else { /* else end or abort */ - phase = -1; - rtc->command = 0; - rtc->value = 0; - } - s->scr2 = val & 0xFFFF00FF; - s->scr2 |= scr2_2 << 8; - old_scr2 = scr2_2; -} - -static uint32_t mmio_readb(NeXTPC *s, hwaddr addr) -{ - 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; - - 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; + qemu_irq_raise(s->rtc_cmd_reset_irq); } } -static uint32_t mmio_readw(NeXTPC *s, hwaddr addr) +static uint64_t next_mmio_read(void *opaque, hwaddr addr, unsigned size) { - switch (addr) { - default: - DPRINTF("MMIO Read W @ %"HWADDR_PRIx"\n", addr); - return 0x0; - } -} + NeXTPC *s = NEXT_PC(opaque); + uint64_t val; -static uint32_t mmio_readl(NeXTPC *s, hwaddr addr) -{ switch (addr) { - case 0x7000: + case 0x2000: /* 0x2007000 */ /* DPRINTF("Read INT status: %x\n", s->int_status); */ - return s->int_status; + val = s->int_status; + break; - case 0x7800: + case 0x2800: /* 0x2007800 */ DPRINTF("MMIO Read INT mask: %x\n", s->int_mask); - return s->int_mask; + val = s->int_mask; + break; - case 0xc000: - return s->scr1; + case 0x7000 ... 0x7003: /* 0x200c000 */ + val = extract32(s->scr1, (4 - (addr - 0x7000) - size) << 3, + size << 3); + break; - case 0xd000: - return s->scr2; + case 0x8000 ... 0x8003: /* 0x200d000 */ + val = extract32(s->scr2, (4 - (addr - 0x8000) - size) << 3, + size << 3); + break; 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 = 0; + DPRINTF("MMIO Read @ 0x%"HWADDR_PRIx" size %d\n", addr, size); break; - default: - DPRINTF("MMIO Write B @ %x with %x\n", (unsigned int)addr, val); } + 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); + case 0x2000: /* 0x2007000 */ + 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); + + case 0x2800: /* 0x2007800 */ + 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); - break; - case 0xd000: - nextscr2_write(s, val, 4); - break; - - default: - DPRINTF("MMIO Write l @ %x with %x\n", (unsigned int)addr, val); - } -} - -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); + case 0x7000 ... 0x7003: /* 0x200c000 */ + DPRINTF("SCR1 Write: %x\n", (unsigned int)val); + s->scr1 = deposit32(s->scr1, (4 - (addr - 0x7000) - size) << 3, + size << 3, val); break; - case 4: - mmio_writel(s, addr, value); + + case 0x8000 ... 0x8003: /* 0x200d000 */ + s->scr2 = deposit32(s->scr2, (4 - (addr - 0x8000) - size) << 3, + size << 3, val); + next_scr2_led_update(s); + next_scr2_rtc_update(s); + s->old_scr2 = s->scr2; break; + default: - g_assert_not_reached(); + DPRINTF("MMIO Write @ 0x%"HWADDR_PRIx " with 0x%x size %u\n", addr, + (unsigned int)val, size); } } -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 @@ -451,138 +288,6 @@ 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) -{ - switch (addr) { - case 0x14108: - DPRINTF("FDCSR Write: %x\n", value); - - if (value == 0x0) { - /* qemu_irq_raise(s->fd_irq[0]); */ - } - break; - case 0x14020: /* SCSI Control Register */ - if (value & 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) { - DPRINTF("SCSICSR Enable\n"); - /* - * qemu_irq_raise(s->scsi_dma); - * s->scsi_csr_1 = 0xc0; - * s->scsi_csr_1 |= 0x1; - * qemu_irq_pulse(s->scsi_dma); - */ - } - /* - * else - * s->scsi_csr_1 &= ~SCSICSR_ENABLE; - */ - - if (value & 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); */ - - } - if (value & SCSICSR_DMADIR) { - DPRINTF("SCSICSR DMAdir\n"); - } - if (value & SCSICSR_CPUDMA) { - DPRINTF("SCSICSR CPUDMA\n"); - /* qemu_irq_raise(s->scsi_dma); */ - - s->int_status |= 0x4000000; - } else { - s->int_status &= ~(0x4000000); - } - if (value & SCSICSR_INTMASK) { - DPRINTF("SCSICSR INTMASK\n"); - /* - * int_mask &= ~0x1000; - * s->scsi_csr_1 |= value; - * s->scsi_csr_1 &= ~SCSICSR_INTMASK; - * if (s->scsi_queued) { - * s->scsi_queued = 0; - * next_irq(s, NEXT_SCSI_I, level); - * } - */ - } else { - /* int_mask |= 0x1000; */ - } - if (value & 0x80) { - /* int_mask |= 0x1000; */ - /* s->scsi_csr_1 |= 0x80; */ - } - DPRINTF("SCSICSR Write: %x\n", value); - /* s->scsi_csr_1 = value; */ - return; - /* Hardware timer latch - not implemented yet */ - case 0x1a000: - default: - DPRINTF("BMAP Write B @ %x with %x\n", (unsigned int)addr, value); - } -} - -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, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - #define NEXTDMA_SCSI(x) (0x10 + x) #define NEXTDMA_FD(x) (0x10 + x) #define NEXTDMA_ENTX(x) (0x110 + x) @@ -595,59 +300,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"); */ @@ -656,76 +365,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) @@ -828,56 +558,649 @@ static void next_irq(void *opaque, int number, int level) } } -static void next_escc_init(DeviceState *pcdev) +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) { - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_new(TYPE_ESCC); - qdev_prop_set_uint32(dev, "disabled", 0); - qdev_prop_set_uint32(dev, "frequency", 9600 * 384); - qdev_prop_set_uint32(dev, "it_shift", 0); - qdev_prop_set_bit(dev, "bit_swap", true); - qdev_prop_set_chr(dev, "chrB", serial_hd(1)); - qdev_prop_set_chr(dev, "chrA", serial_hd(0)); - qdev_prop_set_uint32(dev, "chnBtype", escc_serial); - qdev_prop_set_uint32(dev, "chnAtype", escc_serial); - - s = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(s, &error_fatal); - sysbus_connect_irq(s, 0, qdev_get_gpio_in(pcdev, NEXT_SCC_I)); - sysbus_connect_irq(s, 1, qdev_get_gpio_in(pcdev, NEXT_SCC_DMA_I)); - sysbus_mmio_map(s, 0, 0x2118000); + DPRINTF("SCSI READ: %x\n", len); + abort(); } -static void next_pc_reset(DeviceState *dev) +static void nextscsi_write(void *opaque, uint8_t *buf, int size) { - NeXTPC *s = NEXT_PC(dev); + DPRINTF("SCSI WRITE: %i\n", size); + nextdma_write(opaque, buf, size, NEXTDMA_SCSI); +} + +static void next_scsi_csr_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + NeXTSCSI *s = NEXT_SCSI(opaque); + NeXTPC *pc = NEXT_PC(container_of(s, NeXTPC, next_scsi)); + + switch (addr) { + case 0: + 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); */ + } + + if (val & SCSICSR_ENABLE) { + DPRINTF("SCSICSR Enable\n"); + /* + * qemu_irq_raise(s->scsi_dma); + * s->scsi_csr_1 = 0xc0; + * s->scsi_csr_1 |= 0x1; + * qemu_irq_pulse(s->scsi_dma); + */ + } + /* + * else + * s->scsi_csr_1 &= ~SCSICSR_ENABLE; + */ + + if (val & SCSICSR_RESET) { + DPRINTF("SCSICSR Reset\n"); + /* I think this should set DMADIR. CPUDMA and INTMASK to 0 */ + qemu_irq_raise(pc->scsi_reset); + s->scsi_csr_1 &= ~(SCSICSR_INTMASK | 0x80 | 0x1); + qemu_irq_lower(pc->scsi_reset); + } + if (val & SCSICSR_DMADIR) { + DPRINTF("SCSICSR DMAdir\n"); + } + if (val & SCSICSR_CPUDMA) { + DPRINTF("SCSICSR CPUDMA\n"); + /* qemu_irq_raise(s->scsi_dma); */ + pc->int_status |= 0x4000000; + } else { + /* fprintf(stderr,"SCSICSR CPUDMA disabled\n"); */ + pc->int_status &= ~(0x4000000); + /* qemu_irq_lower(s->scsi_dma); */ + } + if (val & SCSICSR_INTMASK) { + DPRINTF("SCSICSR INTMASK\n"); + /* + * int_mask &= ~0x1000; + * s->scsi_csr_1 |= val; + * s->scsi_csr_1 &= ~SCSICSR_INTMASK; + * if (s->scsi_queued) { + * s->scsi_queued = 0; + * next_irq(s, NEXT_SCSI_I, level); + * } + */ + } else { + /* int_mask |= 0x1000; */ + } + if (val & 0x80) { + /* int_mask |= 0x1000; */ + /* s->scsi_csr_1 |= 0x80; */ + } + DPRINTF("SCSICSR1 Write: %"PRIx64 "\n", val); + s->scsi_csr_1 = val; + break; + + case 1: + DPRINTF("SCSICSR2 Write: %"PRIx64 "\n", val); + s->scsi_csr_2 = val; + break; + + default: + g_assert_not_reached(); + } +} + +static uint64_t next_scsi_csr_read(void *opaque, hwaddr addr, unsigned size) +{ + NeXTSCSI *s = NEXT_SCSI(opaque); + uint64_t val; + + switch (addr) { + case 0: + DPRINTF("SCSI 4020 STATUS READ %X\n", s->scsi_csr_1); + val = s->scsi_csr_1; + break; + + case 1: + DPRINTF("SCSI 4021 STATUS READ %X\n", s->scsi_csr_2); + val = s->scsi_csr_2; + break; + + default: + g_assert_not_reached(); + } + + return val; +} + +static const MemoryRegionOps next_scsi_csr_ops = { + .read = next_scsi_csr_read, + .write = next_scsi_csr_write, + .valid.min_access_size = 1, + .valid.max_access_size = 1, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void next_scsi_init(Object *obj) +{ + NeXTSCSI *s = NEXT_SCSI(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + object_initialize_child(obj, "esp", &s->sysbus_esp, TYPE_SYSBUS_ESP); + + memory_region_init_io(&s->scsi_csr_mem, obj, &next_scsi_csr_ops, + s, "csrs", 2); + + memory_region_init(&s->scsi_mem, obj, "next.scsi", 0x40); + sysbus_init_mmio(sbd, &s->scsi_mem); +} + +static void next_scsi_realize(DeviceState *dev, Error **errp) +{ + NeXTSCSI *s = NEXT_SCSI(dev); + SysBusESPState *sysbus_esp; + SysBusDevice *sbd; + ESPState *esp; + NeXTPC *pcdev; + + pcdev = NEXT_PC(container_of(s, NeXTPC, next_scsi)); + + /* ESP */ + sysbus_esp = SYSBUS_ESP(&s->sysbus_esp); + 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; + sbd = SYS_BUS_DEVICE(sysbus_esp); + if (!sysbus_realize(sbd, errp)) { + return; + } + memory_region_add_subregion(&s->scsi_mem, 0x0, + sysbus_mmio_get_region(sbd, 0)); + + /* SCSI CSRs */ + memory_region_add_subregion(&s->scsi_mem, 0x20, &s->scsi_csr_mem); + + scsi_bus_legacy_handle_cmdline(&s->sysbus_esp.esp.bus); +} + +static const VMStateDescription next_scsi_vmstate = { + .name = "next-scsi", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_UINT8(scsi_csr_1, NeXTSCSI), + VMSTATE_UINT8(scsi_csr_2, NeXTSCSI), + VMSTATE_END_OF_LIST() + }, +}; + +static void next_scsi_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "NeXT SCSI Controller"; + dc->realize = next_scsi_realize; + dc->vmsd = &next_scsi_vmstate; +} + +static const TypeInfo next_scsi_info = { + .name = TYPE_NEXT_SCSI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = next_scsi_init, + .instance_size = sizeof(NeXTSCSI), + .class_init = next_scsi_class_init, +}; + +static void next_floppy_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + switch (addr) { + case 0: + DPRINTF("FDCSR Write: %"PRIx64 "\n", val); + if (val == 0x0) { + /* qemu_irq_raise(s->fd_irq[0]); */ + } + break; + + default: + g_assert_not_reached(); + } +} + +static uint64_t next_floppy_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t val; + + switch (addr) { + case 0: + DPRINTF("FD read @ %x\n", (unsigned int)addr); + val = 0x40 | 0x04 | 0x2 | 0x1; + break; + + default: + g_assert_not_reached(); + } + + return val; +} + +static const MemoryRegionOps next_floppy_ops = { + .read = next_floppy_read, + .write = next_floppy_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void next_timer_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + switch (addr) { + case 0 ... 3: + /* Hardware timer latch - not implemented yet */ + break; + + default: + g_assert_not_reached(); + } +} + +static uint64_t next_timer_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t val; + + switch (addr) { + case 0 ... 3: + /* + * 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 + */ + val = extract32(clock(), (4 - addr - size) << 3, + size << 3); + break; + + default: + g_assert_not_reached(); + } + + return val; +} + +static const MemoryRegionOps next_timer_ops = { + .read = next_timer_read, + .write = next_timer_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void next_dummy_en_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + /* Do nothing */ +} + +static uint64_t next_dummy_en_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t val; + + switch (addr) { + case 0: + /* For now return dummy byte to allow the Ethernet test to timeout */ + val = 0xff; + break; + + default: + val = 0; + } + + return val; +} + +static const MemoryRegionOps next_dummy_en_ops = { + .read = next_dummy_en_read, + .write = next_dummy_en_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static bool next_rtc_cmd_is_write(uint8_t cmd) +{ + return (cmd >= 0x80 && cmd <= 0x9f) || + (cmd == 0xb1); +} + +static void next_rtc_data_in_irq(void *opaque, int n, int level) +{ + NeXTRTC *rtc = NEXT_RTC(opaque); + + if (rtc->phase < 8) { + rtc->command = (rtc->command << 1) | level; + + if (rtc->phase == 7 && !next_rtc_cmd_is_write(rtc->command)) { + if (rtc->command <= 0x1f) { + /* RAM registers */ + rtc->retval = rtc->ram[rtc->command]; + } + if ((rtc->command >= 0x20) && (rtc->command <= 0x2f)) { + /* RTC */ + time_t time_h = time(NULL); + struct tm *info = localtime(&time_h); + rtc->retval = 0; + + switch (rtc->command) { + case 0x20: + rtc->retval = SCR2_TOBCD(info->tm_sec); + break; + case 0x21: + rtc->retval = SCR2_TOBCD(info->tm_min); + break; + case 0x22: + rtc->retval = SCR2_TOBCD(info->tm_hour); + break; + case 0x24: + rtc->retval = SCR2_TOBCD(info->tm_mday); + break; + case 0x25: + rtc->retval = SCR2_TOBCD((info->tm_mon + 1)); + break; + case 0x26: + rtc->retval = SCR2_TOBCD((info->tm_year - 100)); + break; + } + } + if (rtc->command == 0x30) { + /* read the status 0x30 */ + rtc->retval = rtc->status; + } + if (rtc->command == 0x31) { + /* read the control 0x31 */ + rtc->retval = rtc->control; + } + } + } + if (rtc->phase >= 8 && rtc->phase < 16) { + if (next_rtc_cmd_is_write(rtc->command)) { + /* Shift in value to write */ + rtc->value = (rtc->value << 1) | level; + } else { + /* Shift out value to read */ + if (rtc->retval & (0x80 >> (rtc->phase - 8))) { + qemu_irq_raise(rtc->data_out_irq); + } else { + qemu_irq_lower(rtc->data_out_irq); + } + } + } + + rtc->phase++; + if (rtc->phase == 16 && next_rtc_cmd_is_write(rtc->command)) { + if (rtc->command >= 0x80 && rtc->command <= 0x9f) { + /* RAM registers */ + rtc->ram[rtc->command - 0x80] = rtc->value; + } + if (rtc->command == 0xb1) { + /* write to 0x30 register */ + if (rtc->value & 0x04) { + /* clear FTU */ + rtc->status = rtc->status & (~0x18); + qemu_irq_lower(rtc->power_irq); + } + } + } +} + +static void next_rtc_cmd_reset_irq(void *opaque, int n, int level) +{ + NeXTRTC *rtc = NEXT_RTC(opaque); + + if (level) { + rtc->phase = 0; + rtc->command = 0; + rtc->value = 0; + } +} + +static void next_rtc_reset_hold(Object *obj, ResetType type) +{ + NeXTRTC *rtc = NEXT_RTC(obj); + + rtc->status = 0x90; + + /* Load RTC RAM - TODO: provide possibility to load contents from file */ + memcpy(rtc->ram, rtc_ram2, 32); +} + +static void next_rtc_init(Object *obj) +{ + NeXTRTC *rtc = NEXT_RTC(obj); + + qdev_init_gpio_in_named(DEVICE(obj), next_rtc_data_in_irq, + "rtc-data-in", 1); + qdev_init_gpio_out_named(DEVICE(obj), &rtc->data_out_irq, + "rtc-data-out", 1); + qdev_init_gpio_in_named(DEVICE(obj), next_rtc_cmd_reset_irq, + "rtc-cmd-reset", 1); + qdev_init_gpio_out_named(DEVICE(obj), &rtc->power_irq, + "rtc-power-out", 1); +} + +static const VMStateDescription next_rtc_vmstate = { + .name = "next-rtc", + .version_id = 3, + .minimum_version_id = 3, + .fields = (const VMStateField[]) { + VMSTATE_INT8(phase, NeXTRTC), + VMSTATE_UINT8_ARRAY(ram, NeXTRTC, 32), + VMSTATE_UINT8(command, NeXTRTC), + VMSTATE_UINT8(value, NeXTRTC), + VMSTATE_UINT8(status, NeXTRTC), + VMSTATE_UINT8(control, NeXTRTC), + VMSTATE_UINT8(retval, NeXTRTC), + VMSTATE_END_OF_LIST() + }, +}; + +static void next_rtc_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->desc = "NeXT RTC"; + dc->vmsd = &next_rtc_vmstate; + rc->phases.hold = next_rtc_reset_hold; +} + +static const TypeInfo next_rtc_info = { + .name = TYPE_NEXT_RTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = next_rtc_init, + .instance_size = sizeof(NeXTRTC), + .class_init = next_rtc_class_init, +}; + +static void next_pc_rtc_data_in_irq(void *opaque, int n, int level) +{ + NeXTPC *s = NEXT_PC(opaque); + uint8_t scr2_2 = extract32(s->scr2, 8, 8); + + if (level) { + scr2_2 |= SCR2_RTDATA; + } else { + scr2_2 &= ~SCR2_RTDATA; + } + + s->scr2 = deposit32(s->scr2, 8, 8, scr2_2); +} + +static void next_pc_reset_hold(Object *obj, ResetType type) +{ + NeXTPC *s = NEXT_PC(obj); /* Set internal registers to initial values */ /* 0x0000XX00 << vital bits */ s->scr1 = 0x00011102; s->scr2 = 0x00ff0c80; - - s->rtc.status = 0x90; - - /* Load RTC RAM - TODO: provide possibility to load contents from file */ - memcpy(s->rtc.ram, rtc_ram2, 32); + s->old_scr2 = s->scr2; } static void next_pc_realize(DeviceState *dev, Error **errp) { NeXTPC *s = NEXT_PC(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + SysBusDevice *sbd; + DeviceState *d; + + /* SCSI */ + sbd = SYS_BUS_DEVICE(&s->next_scsi); + if (!sysbus_realize(sbd, errp)) { + return; + } - qdev_init_gpio_in(dev, next_irq, NEXT_NUM_IRQS); + d = DEVICE(object_resolve_path_component(OBJECT(&s->next_scsi), "esp")); + sysbus_connect_irq(SYS_BUS_DEVICE(d), 0, + qdev_get_gpio_in(DEVICE(s), NEXT_SCSI_I)); + + s->scsi_reset = qdev_get_gpio_in(d, 0); + s->scsi_dma = qdev_get_gpio_in(d, 1); + + /* ESCC */ + d = DEVICE(&s->escc); + qdev_prop_set_uint32(d, "disabled", 0); + qdev_prop_set_uint32(d, "frequency", 9600 * 384); + qdev_prop_set_uint32(d, "it_shift", 0); + qdev_prop_set_bit(d, "bit_swap", true); + qdev_prop_set_chr(d, "chrB", serial_hd(1)); + qdev_prop_set_chr(d, "chrA", serial_hd(0)); + qdev_prop_set_uint32(d, "chnBtype", escc_serial); + qdev_prop_set_uint32(d, "chnAtype", escc_serial); + + sbd = SYS_BUS_DEVICE(d); + if (!sysbus_realize(sbd, errp)) { + return; + } + sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(dev, NEXT_SCC_I)); + sysbus_connect_irq(sbd, 1, qdev_get_gpio_in(dev, NEXT_SCC_DMA_I)); - 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, - "next.scr", 0x20000); + /* RTC */ + d = DEVICE(&s->rtc); + if (!sysbus_realize(SYS_BUS_DEVICE(d), errp)) { + return; + } + /* Data from NeXTPC to RTC */ + qdev_connect_gpio_out_named(dev, "rtc-data-out", 0, + qdev_get_gpio_in_named(d, "rtc-data-in", 0)); + /* Data from RTC to NeXTPC */ + qdev_connect_gpio_out_named(d, "rtc-data-out", 0, + qdev_get_gpio_in_named(dev, + "rtc-data-in", 0)); + qdev_connect_gpio_out_named(dev, "rtc-cmd-reset", 0, + qdev_get_gpio_in_named(d, "rtc-cmd-reset", 0)); + qdev_connect_gpio_out_named(d, "rtc-power-out", 0, + qdev_get_gpio_in(dev, NEXT_PWR_I)); +} + +static void next_pc_init(Object *obj) +{ + NeXTPC *s = NEXT_PC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + qdev_init_gpio_in(DEVICE(obj), next_irq, NEXT_NUM_IRQS); + + memory_region_init_io(&s->mmiomem, OBJECT(s), &next_mmio_ops, s, + "next.mmio", 0x9000); sysbus_init_mmio(sbd, &s->mmiomem); - sysbus_init_mmio(sbd, &s->scrmem); + + memory_region_init_io(&s->dummyen_mem, OBJECT(s), &next_dummy_en_ops, s, + "next.en", 0x20); + sysbus_init_mmio(sbd, &s->dummyen_mem); + + object_initialize_child(obj, "next-scsi", &s->next_scsi, TYPE_NEXT_SCSI); + sysbus_init_mmio(sbd, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->next_scsi), 0)); + + memory_region_init_io(&s->floppy_mem, OBJECT(s), &next_floppy_ops, s, + "next.floppy", 4); + sysbus_init_mmio(sbd, &s->floppy_mem); + + object_initialize_child(obj, "escc", &s->escc, TYPE_ESCC); + sysbus_init_mmio(sbd, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->escc), 0)); + + memory_region_init_io(&s->timer_mem, OBJECT(s), &next_timer_ops, s, + "next.timer", 4); + sysbus_init_mmio(sbd, &s->timer_mem); + + object_initialize_child(obj, "rtc", &s->rtc, TYPE_NEXT_RTC); + + qdev_init_gpio_in_named(DEVICE(obj), next_pc_rtc_data_in_irq, + "rtc-data-in", 1); + qdev_init_gpio_out_named(DEVICE(obj), &s->rtc_data_irq, + "rtc-data-out", 1); + qdev_init_gpio_out_named(DEVICE(obj), &s->rtc_cmd_reset_irq, + "rtc-cmd-reset", 1); } /* @@ -886,71 +1209,52 @@ static void next_pc_realize(DeviceState *dev, Error **errp) * this cpu link property and could instead provide outbound IRQ lines * that the board could wire up to the CPU. */ -static Property next_pc_properties[] = { +static const Property next_pc_properties[] = { DEFINE_PROP_LINK("cpu", NeXTPC, cpu, TYPE_M68K_CPU, M68kCPU *), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription next_rtc_vmstate = { - .name = "next-rtc", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8_ARRAY(ram, NextRtc, 32), - VMSTATE_UINT8(command, NextRtc), - VMSTATE_UINT8(value, NextRtc), - VMSTATE_UINT8(status, NextRtc), - VMSTATE_UINT8(control, NextRtc), - VMSTATE_UINT8(retval, NextRtc), - VMSTATE_END_OF_LIST() - }, }; static const VMStateDescription next_pc_vmstate = { .name = "next-pc", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { + .version_id = 4, + .minimum_version_id = 4, + .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_UINT8(scsi_csr_1, NeXTPC), - VMSTATE_UINT8(scsi_csr_2, NeXTPC), - VMSTATE_STRUCT(rtc, NeXTPC, 0, next_rtc_vmstate, NextRtc), + VMSTATE_UINT32(led, NeXTPC), VMSTATE_END_OF_LIST() }, }; -static void next_pc_class_init(ObjectClass *klass, void *data) +static void next_pc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); dc->desc = "NeXT Peripheral Controller"; dc->realize = next_pc_realize; - dc->reset = next_pc_reset; device_class_set_props(dc, next_pc_properties); dc->vmsd = &next_pc_vmstate; + rc->phases.hold = next_pc_reset_hold; } static const TypeInfo next_pc_info = { .name = TYPE_NEXT_PC, .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = next_pc_init, .instance_size = sizeof(NeXTPC), .class_init = next_pc_class_init, }; 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 */ @@ -974,33 +1278,53 @@ 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); + sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 0, 0x02005000); /* BMAP IO - acts as a catch-all for now */ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 1, 0x02100000); + /* en network (dummy) */ + sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 1, 0x02106000); + + /* unknown: Brightness control register? */ + empty_slot_init("next.unknown.0", 0x02110000, 0x10); + /* unknown: Magneto-Optical drive controller? */ + empty_slot_init("next.unknown.1", 0x02112000, 0x10); + + /* SCSI */ + sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 2, 0x02114000); + /* Floppy */ + sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 3, 0x02114108); + /* ESCC */ + sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 4, 0x02118000); + + /* unknown: Serial clock configuration register? */ + empty_slot_init("next.unknown.2", 0x02118004, 0x10); + + /* Timer */ + sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 5, 0x0211a000); + /* 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); @@ -1010,7 +1334,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); @@ -1018,27 +1342,23 @@ static void next_cube_init(MachineState *machine) } } - /* Serial */ - next_escc_init(pcdev); - - /* TODO: */ - /* Network */ - /* SCSI */ - /* 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) +static void next_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); 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"); + mc->no_cdrom = true; } static const TypeInfo next_typeinfo = { @@ -1052,6 +1372,8 @@ static void next_register_type(void) { type_register_static(&next_typeinfo); type_register_static(&next_pc_info); + type_register_static(&next_scsi_info); + type_register_static(&next_rtc_info); } type_init(next_register_type) diff --git a/hw/m68k/next-kbd.c b/hw/m68k/next-kbd.c index 0c348c18cf2..2bec945acfb 100644 --- a/hw/m68k/next-kbd.c +++ b/hw/m68k/next-kbd.c @@ -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) @@ -163,71 +162,73 @@ static const MemoryRegionOps kbd_ops = { .write = kbd_writefn, .valid.min_access_size = 1, .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_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 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 +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, + + [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 = { @@ -264,14 +312,14 @@ static const VMStateDescription nextkbd_vmstate = { .unmigratable = 1, /* TODO: Implement this when m68k CPU is migratable */ }; -static void nextkbd_class_init(ObjectClass *oc, void *data) +static void nextkbd_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); 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 index 34c4f0e9876..36de67c328e 100644 --- a/hw/m68k/q800-glue.c +++ b/hw/m68k/q800-glue.c @@ -97,6 +97,11 @@ static void GLUE_set_irq(void *opaque, int irq, int level) 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(); } @@ -123,6 +128,10 @@ static void GLUE_set_irq(void *opaque, int irq, int level) irq = 6; break; + case GLUE_IRQ_IN_ASC: + irq = 4; + break; + default: g_assert_not_reached(); } @@ -166,9 +175,9 @@ static void glue_nmi_release(void *opaque) GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 0); } -static void glue_reset(DeviceState *dev) +static void glue_reset_hold(Object *obj, ResetType type) { - GLUEState *s = GLUE(dev); + GLUEState *s = GLUE(obj); s->ipr = 0; s->auxmode = 0; @@ -180,7 +189,7 @@ static const VMStateDescription vmstate_glue = { .name = "q800-glue", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(ipr, GLUEState), VMSTATE_UINT8(auxmode, GLUEState), VMSTATE_TIMER_PTR(nmi_release, GLUEState), @@ -194,9 +203,8 @@ static const VMStateDescription vmstate_glue = { * this cpu link property and could instead provide outbound IRQ lines * that the board could wire up to the CPU. */ -static Property glue_properties[] = { +static const Property glue_properties[] = { DEFINE_PROP_LINK("cpu", GLUEState, cpu, TYPE_M68K_CPU, M68kCPU *), - DEFINE_PROP_END_OF_LIST(), }; static void glue_finalize(Object *obj) @@ -214,20 +222,21 @@ static void glue_init(Object *obj) 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); + 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) +static void glue_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); NMIClass *nc = NMI_CLASS(klass); dc->vmsd = &vmstate_glue; - dc->reset = glue_reset; device_class_set_props(dc, glue_properties); + rc->phases.hold = glue_reset_hold; nc->nmi_monitor_handler = glue_nmi; } @@ -239,7 +248,7 @@ static const TypeInfo glue_info_types[] = { .instance_init = glue_init, .instance_finalize = glue_finalize, .class_init = glue_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NMI }, { } }, diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index b770b71d547..793b23f8155 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -24,7 +24,8 @@ #include "qemu/units.h" #include "qemu/datadir.h" #include "qemu/guest-random.h" -#include "sysemu/sysemu.h" +#include "exec/target_page.h" +#include "system/system.h" #include "cpu.h" #include "hw/boards.h" #include "hw/or-irq.h" @@ -40,16 +41,20 @@ #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" +#include "system/qtest.h" +#include "system/runstate.h" +#include "system/reset.h" #include "migration/vmstate.h" #define MACROM_ADDR 0x40800000 @@ -66,9 +71,11 @@ #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 @@ -82,6 +89,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 @@ -89,6 +99,9 @@ #define Q800_NUBUS_SLOTS_AVAILABLE (BIT(0x9) | BIT(0xc) | BIT(0xd) | \ BIT(0xe)) +/* Quadra 800 machine ID */ +#define Q800_MACHINE_ID 0xa55a2bad + static void main_cpu_reset(void *opaque) { @@ -190,6 +203,46 @@ static const MemoryRegionOps macio_alias_ops = { }, }; +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) +{ +} + +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) +{ +} + +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); @@ -200,7 +253,6 @@ static void q800_machine_init(MachineState *machine) int bios_size; ram_addr_t initrd_base; int32_t initrd_size; - MemoryRegion *dp8393x_prom = g_new(MemoryRegion, 1); uint8_t *prom; int i, checksum; MacFbMode *macfb_mode; @@ -218,6 +270,8 @@ static void q800_machine_init(MachineState *machine) BusState *adb_bus; NubusBus *nubus; DriveInfo *dinfo; + NICInfo *nd; + MACAddr mac; uint8_t rng_seed[32]; linux_boot = (kernel_filename != NULL); @@ -234,7 +288,11 @@ static void q800_machine_init(MachineState *machine) 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 @@ -251,12 +309,32 @@ static void q800_machine_init(MachineState *machine) memory_region_add_subregion(get_system_memory(), IO_BASE + IO_SLICE, &m->macio_alias); + 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 */ 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 */ object_initialize_child(OBJECT(machine), "via1", &m->via1, TYPE_MOS6522_Q800_VIA1); @@ -294,13 +372,6 @@ static void q800_machine_init(MachineState *machine) /* 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: @@ -310,14 +381,21 @@ static void q800_machine_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); - qdev_set_nic_properties(dev, &nd_table[0]); + 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); + qdev_prop_set_uint8(dev, "it_shift", 2); qdev_prop_set_bit(dev, "big_endian", true); object_property_set_link(OBJECT(dev), "dma_mr", @@ -329,16 +407,16 @@ static void q800_machine_init(MachineState *machine) 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; @@ -374,6 +452,12 @@ static void q800_machine_init(MachineState *machine) 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 */ object_initialize_child(OBJECT(machine), "esp", &m->esp, @@ -404,6 +488,26 @@ static void q800_machine_init(MachineState *machine) 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 */ object_initialize_child(OBJECT(machine), "swim", &m->swim, @@ -480,7 +584,7 @@ static void q800_machine_init(MachineState *machine) } kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &elf_entry, NULL, &high, NULL, 1, + &elf_entry, NULL, &high, NULL, ELFDATA2MSB, EM_68K, 0, 0); if (kernel_size < 0) { error_report("could not load kernel '%s'", kernel_filename); @@ -557,6 +661,11 @@ static void q800_machine_init(MachineState *machine) filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); 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) { bios_size = load_image_targphys(filename, MACROM_ADDR, MACROM_SIZE); @@ -574,13 +683,35 @@ static void q800_machine_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", "vendor", " SEAGATE" }, @@ -596,28 +727,33 @@ static GlobalProperty hw_compat_q800[] = { }; static const size_t hw_compat_q800_len = G_N_ELEMENTS(hw_compat_q800); -static const char *q800_machine_valid_cpu_types[] = { - M68K_CPU_TYPE_NAME("m68040"), - NULL -}; - -static void q800_machine_class_init(ObjectClass *oc, void *data) +static void q800_machine_class_init(ObjectClass *oc, const 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_machine_init; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040"); - mc->valid_cpu_types = q800_machine_valid_cpu_types; + 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, }; diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 2dd3c998948..875fd00ef8d 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -10,7 +10,8 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/guest-random.h" -#include "sysemu/sysemu.h" +#include "exec/target_page.h" +#include "system/system.h" #include "cpu.h" #include "hw/boards.h" #include "hw/qdev-properties.h" @@ -24,9 +25,9 @@ #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" +#include "system/qtest.h" +#include "system/runstate.h" +#include "system/reset.h" #include "hw/intc/m68k_irqc.h" #include "hw/misc/virt_ctrl.h" @@ -155,6 +156,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); /* @@ -199,11 +202,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; @@ -229,7 +229,7 @@ static void virt_init(MachineState *machine) } kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &elf_entry, NULL, &high, NULL, 1, + &elf_entry, NULL, &high, NULL, ELFDATA2MSB, EM_68K, 0, 0); if (kernel_size < 0) { error_report("could not load kernel '%s'", kernel_filename); @@ -240,9 +240,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, @@ -299,7 +310,7 @@ static void virt_init(MachineState *machine) } } -static void virt_machine_class_init(ObjectClass *oc, void *data) +static void virt_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->desc = "QEMU M68K Virtual Machine"; @@ -325,85 +336,129 @@ 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, \ + const 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), \ + static const TypeInfo MACHINE_VER_SYM(info, virt, __VA_ARGS__) = \ + { \ + .name = MACHINE_VER_TYPE_NAME("virt", __VA_ARGS__), \ .parent = MACHINE_TYPE_NAME("virt"), \ - .class_init = virt_##major##_##minor##_class_init, \ + .class_init = MACHINE_VER_SYM(class_init, virt, __VA_ARGS__), \ }; \ - static void machvirt_machine_##major##_##minor##_init(void) \ + static void MACHINE_VER_SYM(register, virt, __VA_ARGS__)(void) \ { \ - type_register_static(&machvirt_##major##_##minor##_info); \ + 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_10_1_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(10, 1) + +static void virt_machine_10_0_options(MachineClass *mc) +{ + virt_machine_10_1_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_0, hw_compat_10_0_len); +} +DEFINE_VIRT_MACHINE(10, 0) + +static void virt_machine_9_2_options(MachineClass *mc) +{ + virt_machine_10_0_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_2, hw_compat_9_2_len); +} +DEFINE_VIRT_MACHINE(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, 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, false) +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, false) +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, false) +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 4e314748d35..94e72749127 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -1,3 +1,14 @@ +/* + * 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" @@ -6,18 +17,29 @@ #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 "sysemu/hostmem.h" -#include "sysemu/numa.h" +#include "qemu/guest-random.h" +#include "system/hostmem.h" +#include "system/numa.h" #include "hw/cxl/cxl.h" #include "hw/pci/msix.h" +/* type3 device private */ +enum CXL_T3_MSIX_VECTOR { + CXL_T3_MSIX_PCIE_DOE_TABLE_ACCESS = 0, + CXL_T3_MSIX_EVENT_START = 2, + CXL_T3_MSIX_MBOX = CXL_T3_MSIX_EVENT_START + CXL_EVENT_TYPE_MAX, + CXL_T3_MSIX_VECTOR_NR +}; + #define DWORD_BYTE 4 +#define CXL_CAPACITY_MULTIPLIER (256 * MiB) /* Default CDAT entries for a memory region */ enum { @@ -30,37 +52,33 @@ enum { CT3_CDAT_NUM_ENTRIES }; -static int ct3_build_cdat_entries_for_mr(CDATSubHeader **cdat_table, - int dsmad_handle, MemoryRegion *mr, - bool is_pmem, uint64_t dpa_base) +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 = is_pmem ? CDAT_DSMAS_FLAG_NV : 0, + .flags = (is_pmem ? CDAT_DSMAS_FLAG_NV : 0) | + (is_dynamic ? CDAT_DSMAS_FLAG_DYNAMIC_CAP : 0), .DPA_base = dpa_base, - .DPA_length = memory_region_size(mr), + .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, @@ -74,9 +92,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, @@ -90,9 +105,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, @@ -106,9 +118,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, @@ -122,9 +131,6 @@ 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, @@ -137,18 +143,16 @@ static int ct3_build_cdat_entries_for_mr(CDATSubHeader **cdat_table, */ .EFI_memory_type_attr = is_pmem ? 2 : 1, .DPA_offset = 0, - .DPA_length = memory_region_size(mr), + .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) @@ -156,12 +160,13 @@ static int ct3_build_cdat_table(CDATSubHeader ***cdat_table, void *priv) g_autofree CDATSubHeader **table = NULL; CXLType3Dev *ct3d = priv; MemoryRegion *volatile_mr = NULL, *nonvolatile_mr = NULL; + MemoryRegion *dc_mr = NULL; + uint64_t vmr_size = 0, pmr_size = 0; int dsmad_handle = 0; int cur_ent = 0; int len = 0; - int rc, i; - if (!ct3d->hostpmem && !ct3d->hostvmem) { + if (!ct3d->hostpmem && !ct3d->hostvmem && !ct3d->dc.num_regions) { return 0; } @@ -171,6 +176,7 @@ static int ct3_build_cdat_table(CDATSubHeader ***cdat_table, void *priv) return -EINVAL; } len += CT3_CDAT_NUM_ENTRIES; + vmr_size = memory_region_size(volatile_mr); } if (ct3d->hostpmem) { @@ -179,43 +185,62 @@ static int ct3_build_cdat_table(CDATSubHeader ***cdat_table, void *priv) return -EINVAL; } len += CT3_CDAT_NUM_ENTRIES; + pmr_size = memory_region_size(nonvolatile_mr); } - table = g_malloc0(len * sizeof(*table)); - if (!table) { - return -ENOMEM; + 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) { - rc = ct3_build_cdat_entries_for_mr(table, dsmad_handle++, volatile_mr, - false, 0); - if (rc < 0) { - return rc; - } + ct3_build_cdat_entries_for_mr(table, dsmad_handle++, vmr_size, + false, false, 0); cur_ent = CT3_CDAT_NUM_ENTRIES; } if (nonvolatile_mr) { - rc = ct3_build_cdat_entries_for_mr(&(table[cur_ent]), dsmad_handle++, - nonvolatile_mr, true, - (volatile_mr ? - memory_region_size(volatile_mr) : 0)); - if (rc < 0) { - goto error_cleanup; - } + 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 len; -error_cleanup: - for (i = 0; i < cur_ent; i++) { - g_free(table[i]); - } - return rc; } static void ct3_free_cdat_table(CDATSubHeader **cdat_table, int num, void *priv) @@ -321,10 +346,17 @@ static void build_dvsecs(CXLType3Dev *ct3d) range2_size_lo = (2 << 5) | (2 << 2) | 0x3 | (ct3d->hostpmem->size & 0xF0000000); } - } else { + } 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){ @@ -343,7 +375,7 @@ static void build_dvsecs(CXLType3Dev *ct3d) 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, @@ -370,41 +402,39 @@ static void build_dvsecs(CXLType3Dev *ct3d) .rcvd_mod_ts_data_phase1 = 0xef, /* WTF? */ }; cxl_component_create_dvsec(cxl_cstate, CXL2_TYPE3_DEVICE, - 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); } 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); + ctrl = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL + which * hdm_inc); /* TODO: Sanity checks that the decoder is possible */ ctrl = FIELD_DP32(ctrl, CXL_HDM_DECODER0_CTRL, ERR, 0); ctrl = FIELD_DP32(ctrl, CXL_HDM_DECODER0_CTRL, COMMITTED, 1); - stl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL, ctrl); + 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; - assert(which == 0); - - ctrl = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_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, ctrl); + 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) @@ -487,10 +517,26 @@ static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value, 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); + uint32_t fe = FIELD_EX32(capctrl, CXL_RAS_ERR_CAP_CTRL, + FIRST_ERROR_POINTER); CXLError *cxl_err; uint32_t unc_err; @@ -509,7 +555,8 @@ static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value, * closest to behavior of hardware not capable of multiple * header recording. */ - QTAILQ_FOREACH_SAFE(cxl_err, &ct3d->error_list, node, cxl_next) { + 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); @@ -538,7 +585,7 @@ static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value, FIRST_ERROR_POINTER, cxl_err->type); } else { /* - * If no more errors, then follow recomendation of PCI spec + * 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. */ @@ -576,11 +623,103 @@ static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value, } } +/* + * 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); - if (!ct3d->hostmem && !ct3d->hostvmem && !ct3d->hostpmem) { + 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) { @@ -607,6 +746,11 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) 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); @@ -617,7 +761,7 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) } address_space_init(&ct3d->hostvmem_as, vmr, v_name); ct3d->cxl_dstate.vmem_size = memory_region_size(vmr); - ct3d->cxl_dstate.mem_size += memory_region_size(vmr); + ct3d->cxl_dstate.static_mem_size += memory_region_size(vmr); g_free(v_name); } @@ -630,6 +774,11 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) 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); @@ -640,10 +789,52 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp) } address_space_init(&ct3d->hostpmem_as, pmr, p_name); ct3d->cxl_dstate.pmem_size = memory_region_size(pmr); - ct3d->cxl_dstate.mem_size += memory_region_size(pmr); + ct3d->cxl_dstate.static_mem_size += memory_region_size(pmr); g_free(p_name); } + ct3d->dc.total_capacity = 0; + if (ct3d->dc.num_regions > 0) { + MemoryRegion *dc_mr; + char *dc_name; + + 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; } @@ -652,15 +843,29 @@ static DOEProtocol doe_cdat_prot[] = { { } }; +/* Initialize CXL device alerts with default threshold values. */ +static void init_alert_config(CXLType3Dev *ct3d) +{ + ct3d->alert_config = (CXLAlertConfig) { + .life_used_crit_alert_thresh = 75, + .life_used_warn_thresh = 40, + .over_temp_crit_alert_thresh = 35, + .under_temp_crit_alert_thresh = 10, + .over_temp_warn_thresh = 25, + .under_temp_warn_thresh = 20 + }; +} + static void ct3_realize(PCIDevice *pci_dev, Error **errp) { + ERRP_GUARD(); CXLType3Dev *ct3d = CXL_TYPE3(pci_dev); CXLComponentState *cxl_cstate = &ct3d->cxl_cstate; ComponentRegisters *regs = &cxl_cstate->crb; MemoryRegion *mr = ®s->component_registers; uint8_t *pci_conf = pci_dev->config; - unsigned short msix_num = 6; int i, rc; + uint16_t count; QTAILQ_INIT(&ct3d->error_list); @@ -691,54 +896,81 @@ 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 */ - rc = msix_init_exclusive_bar(pci_dev, msix_num, 4, NULL); + /* MSI(-X) Initialization */ + rc = msix_init_exclusive_bar(pci_dev, CXL_T3_MSIX_VECTOR_NR, 4, errp); if (rc) { - goto err_address_space_free; + goto err_free_special_ops; } - for (i = 0; i < msix_num; i++) { + for (i = 0; i < CXL_T3_MSIX_VECTOR_NR; i++) { msix_vector_use(pci_dev, i); } - /* DOE Initailization */ - pcie_doe_init(pci_dev, &ct3d->doe_cdat, 0x190, doe_cdat_prot, true, 0); + /* DOE Initialization */ + pcie_doe_init(pci_dev, &ct3d->doe_cdat, 0x190, doe_cdat_prot, true, + CXL_T3_MSIX_PCIE_DOE_TABLE_ACCESS); cxl_cstate->cdat.build_cdat_table = ct3_build_cdat_table; cxl_cstate->cdat.free_cdat_table = ct3_free_cdat_table; cxl_cstate->cdat.private = ct3d; - cxl_doe_cdat_init(cxl_cstate, errp); - if (*errp) { - goto err_free_special_ops; + if (!cxl_doe_cdat_init(cxl_cstate, errp)) { + goto err_msix_uninit; } + init_alert_config(ct3d); 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); + rc = pcie_aer_init(pci_dev, PCI_ERR_VER, 0x200, PCI_ERR_SIZEOF, errp); if (rc) { goto err_release_cdat; } - cxl_event_init(&ct3d->cxl_dstate, 2); + cxl_event_init(&ct3d->cxl_dstate, CXL_T3_MSIX_EVENT_START); + + /* 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_msix_uninit: + msix_uninit_exclusive_bar(pci_dev); 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) @@ -749,7 +981,13 @@ static void ct3_exit(PCIDevice *pci_dev) pcie_aer_exit(pci_dev); cxl_doe_cdat_release(cxl_cstate); + msix_uninit_exclusive_bar(pci_dev); g_free(regs->special_ops); + cxl_destroy_cci(&ct3d->cci); + 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); } @@ -758,36 +996,139 @@ static void ct3_exit(PCIDevice *pci_dev) } } -/* TODO: Support multiple HDM decoders and DPA skip */ -static bool cxl_type3_dpa(CXLType3Dev *ct3d, hwaddr host_addr, uint64_t *dpa) +/* + * 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) { - 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; + CXLDCRegion *region; - 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; + region = cxl_find_dc_region(ct3d, dpa, len); + if (!region) { + return; } - hpa_offset = (uint64_t)host_addr - decoder_base; + bitmap_set(region->blk_bitmap, (dpa - region->base) / region->block_size, + len / region->block_size); +} - 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) { +/* + * 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; } - 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); + 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); +} - *dpa = (MAKE_64BIT_MASK(0, 8 + ig) & hpa_offset) | - ((MAKE_64BIT_MASK(8 + ig + iw, 64 - 8 - ig - iw) & hpa_offset) >> iw); +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; + unsigned int hdm_count; + uint32_t cap; + uint64_t dpa_base = 0; + int i; - return true; + 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; + } + + if (iw < 8) { + *dpa = dpa_base + + ((MAKE_64BIT_MASK(0, 8 + ig) & hpa_offset) | + ((MAKE_64BIT_MASK(8 + ig + iw, 64 - 8 - ig - iw) & hpa_offset) + >> iw)); + } else { + *dpa = dpa_base + + ((MAKE_64BIT_MASK(0, 8 + ig) & hpa_offset) | + ((((MAKE_64BIT_MASK(ig + iw, 64 - ig - iw) & hpa_offset) + >> (ig + iw)) / 3) << (ig + 8))); + } + + return true; + } + return false; } static int cxl_type3_hpa_to_as_and_dpa(CXLType3Dev *ct3d, @@ -796,16 +1137,23 @@ static int cxl_type3_hpa_to_as_and_dpa(CXLType3Dev *ct3d, AddressSpace **as, uint64_t *dpa_offset) { - MemoryRegion *vmr = NULL, *pmr = NULL; + 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); } - if (!vmr && !pmr) { + if (!vmr && !pmr && !dc_mr) { return -ENODEV; } @@ -813,19 +1161,22 @@ static int cxl_type3_hpa_to_as_and_dpa(CXLType3Dev *ct3d, return -EINVAL; } - if (*dpa_offset > ct3d->cxl_dstate.mem_size) { + if (*dpa_offset >= vmr_size + pmr_size + dc_size) { return -EINVAL; } - if (vmr) { - if (*dpa_offset < memory_region_size(vmr)) { - *as = &ct3d->hostvmem_as; - } else { - *as = &ct3d->hostpmem_as; - *dpa_offset -= memory_region_size(vmr); - } - } else { + 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; @@ -834,32 +1185,43 @@ static int cxl_type3_hpa_to_as_and_dpa(CXLType3Dev *ct3d, 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 = 0; AddressSpace *as = NULL; int res; - res = cxl_type3_hpa_to_as_and_dpa(CXL_TYPE3(d), host_addr, size, + 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)) { + qemu_guest_getrandom_nofail(data, size); + return MEMTX_OK; + } + 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 = 0; AddressSpace *as = NULL; int res; - res = cxl_type3_hpa_to_as_and_dpa(CXL_TYPE3(d), host_addr, size, + 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; + } + return address_space_write(as, dpa_offset, attrs, &data, size); } @@ -869,11 +1231,28 @@ 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, CXL_T3_MSIX_MBOX); + + /* + * Bring up an endpoint to target with MCTP over VDM. + * This device is emulating an MLD with single LD for now. + */ + if (ct3d->vdm_fm_owned_ld_mctp_cci.initialized) { + cxl_destroy_cci(&ct3d->vdm_fm_owned_ld_mctp_cci); + } + cxl_initialize_t3_fm_owned_ld_mctpcci(&ct3d->vdm_fm_owned_ld_mctp_cci, + DEVICE(ct3d), DEVICE(ct3d), + 512); /* Max payload made up */ + if (ct3d->ld0_cci.initialized) { + cxl_destroy_cci(&ct3d->ld0_cci); + } + cxl_initialize_t3_ld_cci(&ct3d->ld0_cci, DEVICE(ct3d), DEVICE(ct3d), + 512); /* Max payload made up */ } -static Property ct3_props[] = { +static const Property ct3_props[] = { DEFINE_PROP_LINK("memdev", CXLType3Dev, hostmem, TYPE_MEMORY_BACKEND, HostMemoryBackend *), /* for backward compatibility */ DEFINE_PROP_LINK("persistent-memdev", CXLType3Dev, hostpmem, @@ -884,7 +1263,13 @@ static Property ct3_props[] = { HostMemoryBackend *), DEFINE_PROP_UINT64("sn", CXLType3Dev, sn, UI64_NULL), DEFINE_PROP_STRING("cdat", CXLType3Dev, cxl_cstate.cdat.filename), - DEFINE_PROP_END_OF_LIST(), + 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), }; static uint64_t get_lsa_size(CXLType3Dev *ct3d) @@ -950,36 +1335,42 @@ 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; + 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) { + if (!vmr && !pmr && !dc_mr) { return false; } - if (dpa_offset + CXL_CACHE_LINE_SIZE > ct3d->cxl_dstate.mem_size) { + if (dpa_offset + CXL_CACHE_LINE_SIZE > vmr_size + pmr_size + dc_size) { return false; } - if (vmr) { - if (dpa_offset < memory_region_size(vmr)) { - as = &ct3d->hostvmem_as; - } else { - as = &ct3d->hostpmem_as; - dpa_offset -= memory_region_size(vmr); - } - } else { + 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, + address_space_write(as, dpa_offset, MEMTXATTRS_UNSPECIFIED, data, CXL_CACHE_LINE_SIZE); return true; } @@ -991,6 +1382,12 @@ void cxl_set_poison_list_overflowed(CXLType3Dev *ct3d) 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) { @@ -1018,26 +1415,28 @@ void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length, ct3d = CXL_TYPE3(obj); QLIST_FOREACH(p, &ct3d->poison_list, node) { - if (((start >= p->start) && (start < p->start + p->length)) || - ((start + length > p->start) && - (start + length <= p->start + p->length))) { - error_setg(errp, "Overlap with existing poisoned region not supported"); + if ((start < p->start + p->length) && (start + length > p->start)) { + error_setg(errp, + "Overlap with existing poisoned region not supported"); return; } } - if (ct3d->poison_list_cnt == CXL_POISON_LIST_LIMIT) { - cxl_set_poison_list_overflowed(ct3d); - return; - } - p = g_new0(CXLPoison, 1); p->length = length; p->start = start; - p->type = CXL_POISON_TYPE_INTERNAL; /* Different from injected via the mbox */ + /* Different from injected via the mbox */ + p->type = CXL_POISON_TYPE_INTERNAL; - QLIST_INSERT_HEAD(&ct3d->poison_list, p, node); - ct3d->poison_list_cnt++; + 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 */ @@ -1090,9 +1489,6 @@ void qmp_cxl_inject_uncorrectable_errors(const char *path, } cxl_err = g_malloc0(sizeof(*cxl_err)); - if (!cxl_err) { - return; - } cxl_err->type = cxl_err_code; while (header && header_count < 32) { @@ -1134,8 +1530,6 @@ void qmp_cxl_inject_uncorrectable_errors(const char *path, 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, @@ -1171,7 +1565,8 @@ void qmp_cxl_inject_correctable_error(const char *path, CxlCorErrorType type, 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))) { + if (!((1 << cxl_err_type) & + ~ldl_le_p(reg_state + R_CXL_RAS_COR_ERR_MASK))) { return; } @@ -1222,7 +1617,6 @@ static int ct3d_qmp_cxl_event_log_enc(CxlEventLog log) return CXL_EVENT_TYPE_FAIL; case CXL_EVENT_LOG_FATAL: return CXL_EVENT_TYPE_FATAL; -/* DCD not yet supported */ default: return -EINVAL; } @@ -1321,7 +1715,8 @@ void qmp_cxl_inject_dram_event(const char *path, CxlEventLog log, uint8_t flags, 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, + bool has_correction_mask, + uint64List *correction_mask, Error **errp) { Object *obj = object_resolve_path(path, NULL); @@ -1410,7 +1805,6 @@ void qmp_cxl_inject_dram_event(const char *path, CxlEventLog log, uint8_t 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, @@ -1422,7 +1816,7 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log, int16_t temperature, uint32_t dirty_shutdown_count, uint32_t corrected_volatile_error_count, - uint32_t corrected_persistent_error_count, + uint32_t corrected_persist_error_count, Error **errp) { Object *obj = object_resolve_path(path, NULL); @@ -1462,15 +1856,312 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log, 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_persistent_error_count); + stl_le_p(&module.corrected_volatile_error_count, + corrected_volatile_error_count); + stl_le_p(&module.corrected_persistent_error_count, + corrected_persist_error_count); if (cxl_event_insert(cxlds, enc_log, (CXLEventRecordRaw *)&module)) { cxl_event_irq_assert(ct3d); } } -static void ct3_class_init(ObjectClass *oc, void *data) +/* 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, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); @@ -1488,7 +2179,7 @@ static void ct3_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); dc->desc = "CXL Memory Device (Type 3)"; - dc->reset = ct3d_reset; + device_class_set_legacy_reset(dc, ct3d_reset); device_class_set_props(dc, ct3_props); cvc->get_lsa_size = get_lsa_size; @@ -1503,7 +2194,7 @@ static const TypeInfo ct3d_info = { .class_size = sizeof(struct CXLType3Class), .class_init = ct3_class_init, .instance_size = sizeof(CXLType3Dev), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CXL_DEVICE }, { INTERFACE_PCIE_DEVICE }, {} diff --git a/hw/mem/cxl_type3_stubs.c b/hw/mem/cxl_type3_stubs.c index f3e4a9fa728..c1a5e4a7c19 100644 --- a/hw/mem/cxl_type3_stubs.c +++ b/hw/mem/cxl_type3_stubs.c @@ -1,3 +1,13 @@ +/* + * 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" @@ -23,7 +33,8 @@ void qmp_cxl_inject_dram_event(const char *path, CxlEventLog log, uint8_t flags, 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, + bool has_correction_mask, + uint64List *correction_mask, Error **errp) {} void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log, @@ -35,7 +46,7 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log, int16_t temperature, uint32_t dirty_shutdown_count, uint32_t corrected_volatile_error_count, - uint32_t corrected_persistent_error_count, + uint32_t corrected_persist_error_count, Error **errp) {} void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length, @@ -56,3 +67,28 @@ void qmp_cxl_inject_correctable_error(const char *path, CxlCorErrorType type, { 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 00000000000..15fd93ff674 --- /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 667d56bd291..1a432e9bd22 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -16,10 +16,26 @@ #include "hw/boards.h" #include "qemu/range.h" #include "hw/virtio/vhost.h" -#include "sysemu/kvm.h" -#include "exec/address-spaces.h" +#include "system/kvm.h" +#include "system/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); @@ -52,19 +68,135 @@ static int memory_device_build_list(Object *obj, void *opaque) return 0; } -static void memory_device_check_addable(MachineState *ms, MemoryRegion *mr, - Error **errp) +static unsigned int memory_device_get_memslots(MemoryDeviceState *md) +{ + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md); + + if (mdc->get_memslots) { + return mdc->get_memslots(md); + } + return 1; +} + +/* + * Memslots that are reserved by memory devices (required but still reported + * as free from KVM / vhost). + */ +static unsigned int get_reserved_memslots(MachineState *ms) +{ + 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; +} + +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 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"); + /* 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; } @@ -104,12 +236,6 @@ static uint64_t memory_device_get_free_addr(MachineState *ms, 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 @@ -133,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)); @@ -176,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); @@ -195,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); } } @@ -214,13 +345,18 @@ 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" @@ -233,19 +369,29 @@ void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms, goto out; } - memory_device_check_addable(ms, mr, &local_err); + memory_device_check_addable(ms, md, mr, &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)); + /* + * 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); @@ -264,9 +410,17 @@ void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms, 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. @@ -275,6 +429,11 @@ void memory_device_plug(MemoryDeviceState *md, MachineState *ms) 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); @@ -283,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. @@ -293,7 +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)); } @@ -313,6 +482,50 @@ 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); @@ -322,8 +535,16 @@ void machine_memory_devices_init(MachineState *ms, hwaddr base, uint64_t size) 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 = { diff --git a/hw/mem/meson.build b/hw/mem/meson.build index ec26ef55443..1c1c6da24b5 100644 --- a/hw/mem/meson.build +++ b/hw/mem/meson.build @@ -5,8 +5,8 @@ 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')) -system_ss.add(when: 'CONFIG_ALL', if_true: files('cxl_type3_stubs.c')) +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) system_ss.add(when: 'CONFIG_SPARSE_MEM', if_true: files('sparse-mem.c')) diff --git a/hw/mem/npcm7xx_mc.c b/hw/mem/npcm7xx_mc.c index abc5af56208..07fc108e0a9 100644 --- a/hw/mem/npcm7xx_mc.c +++ b/hw/mem/npcm7xx_mc.c @@ -65,7 +65,7 @@ static void npcm7xx_mc_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio); } -static void npcm7xx_mc_class_init(ObjectClass *klass, void *data) +static void npcm7xx_mc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index 31080c22c91..23ab143ef8e 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -30,7 +30,7 @@ #include "hw/mem/nvdimm.h" #include "hw/qdev-properties.h" #include "hw/mem/memory-device.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" static void nvdimm_get_label_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -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); @@ -242,12 +246,11 @@ static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf, memory_region_set_dirty(mr, backend_offset, size); } -static Property nvdimm_properties[] = { +static const Property nvdimm_properties[] = { DEFINE_PROP_BOOL(NVDIMM_UNARMED_PROP, NVDIMMDevice, unarmed, false), - DEFINE_PROP_END_OF_LIST(), }; -static void nvdimm_class_init(ObjectClass *oc, void *data) +static void nvdimm_class_init(ObjectClass *oc, const void *data) { PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc); MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc); diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 37f1f4ccfd4..f701d5b5f9b 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -28,8 +28,8 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "qemu/module.h" -#include "sysemu/hostmem.h" -#include "sysemu/numa.h" +#include "system/hostmem.h" +#include "system/numa.h" #include "trace.h" static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); @@ -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) @@ -152,14 +150,13 @@ static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp) return slot; } -static Property pc_dimm_properties[] = { +static const Property pc_dimm_properties[] = { DEFINE_PROP_UINT64(PC_DIMM_ADDR_PROP, PCDIMMDevice, addr, 0), DEFINE_PROP_UINT32(PC_DIMM_NODE_PROP, PCDIMMDevice, node, 0), DEFINE_PROP_INT32(PC_DIMM_SLOT_PROP, PCDIMMDevice, slot, PC_DIMM_UNASSIGNED_SLOT), DEFINE_PROP_LINK(PC_DIMM_MEMDEV_PROP, PCDIMMDevice, hostmem, TYPE_MEMORY_BACKEND, HostMemoryBackend *), - DEFINE_PROP_END_OF_LIST(), }; static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name, @@ -279,7 +276,7 @@ static void pc_dimm_md_fill_device_info(const MemoryDeviceState *md, } } -static void pc_dimm_class_init(ObjectClass *oc, void *data) +static void pc_dimm_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc); @@ -304,7 +301,7 @@ static const TypeInfo pc_dimm_info = { .instance_init = pc_dimm_init, .class_init = pc_dimm_class_init, .class_size = sizeof(PCDIMMDeviceClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_MEMORY_DEVICE }, { } }, diff --git a/hw/mem/sparse-mem.c b/hw/mem/sparse-mem.c index 6e8f4f84fbd..d7b00e563ae 100644 --- a/hw/mem/sparse-mem.c +++ b/hw/mem/sparse-mem.c @@ -17,7 +17,7 @@ #include "hw/sysbus.h" #include "qapi/error.h" #include "qemu/units.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "hw/mem/sparse-mem.h" #define SPARSE_MEM(obj) OBJECT_CHECK(SparseMemState, (obj), TYPE_SPARSE_MEM) @@ -82,7 +82,6 @@ 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 = { @@ -96,14 +95,13 @@ static const MemoryRegionOps sparse_mem_ops = { }, }; -static Property sparse_mem_properties[] = { +static const Property sparse_mem_properties[] = { /* The base address of the memory */ DEFINE_PROP_UINT64("baseaddr", SparseMemState, baseaddr, 0x0), /* The length of the sparse memory region */ DEFINE_PROP_UINT64("length", SparseMemState, length, UINT64_MAX), /* Max amount of actual memory that can be used to back the sparse memory */ DEFINE_PROP_UINT64("maxsize", SparseMemState, maxsize, 10 * MiB), - DEFINE_PROP_END_OF_LIST(), }; MemoryRegion *sparse_mem_init(uint64_t addr, uint64_t length) @@ -138,7 +136,7 @@ static void sparse_mem_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->mmio); } -static void sparse_mem_class_init(ObjectClass *klass, void *data) +static void sparse_mem_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/meson.build b/hw/meson.build index f01fac4617c..791ce21ab42 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,25 +35,27 @@ subdir('smbios') subdir('ssi') subdir('timer') subdir('tpm') +subdir('uefi') subdir('ufs') subdir('usb') subdir('vfio') +subdir('vfio-user') subdir('virtio') +subdir('vmapple') subdir('watchdog') 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 e2697ced9cc..b0214b2c8b2 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 ed61e483ee8..4a9c9df3181 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -31,8 +31,8 @@ #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 "system/device_tree.h" +#include "system/reset.h" #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" @@ -114,8 +114,8 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr) return addr - 0x30000000LL; } -void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, - uint32_t ramsize, +void microblaze_load_kernel(MicroBlazeCPU *cpu, bool is_little_endian, + hwaddr ddr_base, uint32_t ramsize, const char *initrd_filename, const char *dtb_filename, void (*machine_cpu_reset)(MicroBlazeCPU *)) @@ -130,7 +130,7 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, dtb_arg = current_machine->dtb; /* default to pcbios dtb as passed by machine_init */ if (!dtb_arg && dtb_filename) { - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_filename); + filename = qemu_find_file(QEMU_FILE_TYPE_DTB, dtb_filename); } boot_info.machine_cpu_reset = machine_cpu_reset; @@ -144,13 +144,15 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &entry, NULL, &high, NULL, - TARGET_BIG_ENDIAN, EM_MICROBLAZE, 0, 0); + is_little_endian ? ELFDATA2LSB : ELFDATA2MSB, + EM_MICROBLAZE, 0, 0); base32 = entry; if (base32 == 0xc0000000) { kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, NULL, &entry, NULL, NULL, NULL, - TARGET_BIG_ENDIAN, EM_MICROBLAZE, 0, 0); + is_little_endian ? ELFDATA2LSB : ELFDATA2MSB, + EM_MICROBLAZE, 0, 0); } /* Always boot into physical ram. */ boot_info.bootstrap_pc = (uint32_t)entry; diff --git a/hw/microblaze/boot.h b/hw/microblaze/boot.h index 5a8c2f79750..d179a551a69 100644 --- a/hw/microblaze/boot.h +++ b/hw/microblaze/boot.h @@ -2,8 +2,8 @@ #define MICROBLAZE_BOOT_H -void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, - uint32_t ramsize, +void microblaze_load_kernel(MicroBlazeCPU *cpu, bool is_little_endian, + hwaddr ddr_base, uint32_t ramsize, const char *initrd_filename, const char *dtb_filename, void (*machine_cpu_reset)(MicroBlazeCPU *)); diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index ea0fb68cf02..6e923c49cfc 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -32,11 +32,11 @@ #include "hw/sysbus.h" #include "net/net.h" #include "hw/block/flash.h" -#include "sysemu/sysemu.h" +#include "system/system.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 "system/address-spaces.h" #include "hw/ssi/ssi.h" #include "boot.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. */ @@ -111,6 +111,7 @@ petalogix_ml605_init(MachineState *machine) dev = qdev_new("xlnx.xps-intc"); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); qdev_prop_set_uint32(dev, "kind-of-intr", 1 << TIMER_IRQ); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); @@ -126,6 +127,7 @@ petalogix_ml605_init(MachineState *machine) /* 2 timers at irq 2 @ 100 Mhz. */ dev = qdev_new("xlnx.xps-timer"); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); qdev_prop_set_uint32(dev, "one-timer-only", 0); qdev_prop_set_uint32(dev, "clock-frequency", 100 * 1000000); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -133,7 +135,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 +146,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, @@ -174,6 +175,7 @@ petalogix_ml605_init(MachineState *machine) SSIBus *spi; dev = qdev_new("xlnx.xps-spi"); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); qdev_prop_set_uint8(dev, "num-ss-bits", NUM_SPI_FLASHES); busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); @@ -183,7 +185,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"); @@ -205,7 +207,7 @@ petalogix_ml605_init(MachineState *machine) cpu->cfg.pvr_regs[5] = 0xc56be000; cpu->cfg.pvr_regs[10] = 0x0e000000; /* virtex 6 */ - microblaze_load_kernel(cpu, MEMORY_BASEADDR, ram_size, + microblaze_load_kernel(cpu, true, MEMORY_BASEADDR, ram_size, machine->initrd_filename, BINARY_DEVICE_TREE_FILE, NULL); @@ -214,7 +216,7 @@ petalogix_ml605_init(MachineState *machine) static void petalogix_ml605_machine_init(MachineClass *mc) { - mc->desc = "PetaLogix linux refdesign for xilinx ml605 little endian"; + mc->desc = "PetaLogix linux refdesign for xilinx ml605 (little endian)"; mc->init = petalogix_ml605_init; } diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 505639c2980..e8d0ddfdf86 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -30,10 +30,10 @@ #include "hw/sysbus.h" #include "net/net.h" #include "hw/block/flash.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/misc/unimp.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/char/xilinx_uartlite.h" #include "boot.h" @@ -55,9 +55,23 @@ #define ETHLITE_IRQ 1 #define UARTLITE_IRQ 3 +#define TYPE_PETALOGIX_S3ADSP1800_MACHINE \ + MACHINE_TYPE_NAME("petalogix-s3adsp1800") + +struct S3Adsp1800MachineState { + MachineState parent_class; + + EndianMode endianness; +}; + +OBJECT_DECLARE_TYPE(S3Adsp1800MachineState, MachineClass, + PETALOGIX_S3ADSP1800_MACHINE) + + static void petalogix_s3adsp1800_init(MachineState *machine) { + S3Adsp1800MachineState *psms = PETALOGIX_S3ADSP1800_MACHINE(machine); ram_addr_t ram_size = machine->ram_size; DeviceState *dev; MicroBlazeCPU *cpu; @@ -68,9 +82,12 @@ petalogix_s3adsp1800_init(MachineState *machine) MemoryRegion *phys_ram = g_new(MemoryRegion, 1); qemu_irq irq[32]; MemoryRegion *sysmem = get_system_memory(); + EndianMode endianness = psms->endianness; 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", + endianness == ENDIAN_MODE_LITTLE, &error_abort); qdev_realize(DEVICE(cpu), NULL, &error_abort); /* Attach emulated BRAM through the LMB. */ @@ -90,6 +107,7 @@ petalogix_s3adsp1800_init(MachineState *machine) 64 * KiB, 1, 0x89, 0x18, 0x0000, 0x0, 1); dev = qdev_new("xlnx.xps-intc"); + qdev_prop_set_enum(dev, "endianness", endianness); qdev_prop_set_uint32(dev, "kind-of-intr", 1 << ETHLITE_IRQ | 1 << UARTLITE_IRQ); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -101,6 +119,7 @@ petalogix_s3adsp1800_init(MachineState *machine) } dev = qdev_new(TYPE_XILINX_UARTLITE); + qdev_prop_set_enum(dev, "endianness", endianness); 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); @@ -108,34 +127,68 @@ petalogix_s3adsp1800_init(MachineState *machine) /* 2 timers at irq 2 @ 62 Mhz. */ dev = qdev_new("xlnx.xps-timer"); + qdev_prop_set_enum(dev, "endianness", endianness); qdev_prop_set_uint32(dev, "one-timer-only", 0); qdev_prop_set_uint32(dev, "clock-frequency", 62 * 1000000); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); 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]); + qdev_prop_set_enum(dev, "endianness", endianness); + 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, + microblaze_load_kernel(cpu, endianness == ENDIAN_MODE_LITTLE, ddr_base, + ram_size, machine->initrd_filename, BINARY_DEVICE_TREE_FILE, NULL); } -static void petalogix_s3adsp1800_machine_init(MachineClass *mc) +static int machine_get_endianness(Object *obj, Error **errp G_GNUC_UNUSED) +{ + S3Adsp1800MachineState *ms = PETALOGIX_S3ADSP1800_MACHINE(obj); + return ms->endianness; +} + +static void machine_set_endianness(Object *obj, int endianness, Error **errp) +{ + S3Adsp1800MachineState *ms = PETALOGIX_S3ADSP1800_MACHINE(obj); + ms->endianness = endianness; +} + +static void petalogix_s3adsp1800_machine_class_init(ObjectClass *oc, + const void *data) { + MachineClass *mc = MACHINE_CLASS(oc); + ObjectProperty *prop; + mc->desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800"; mc->init = petalogix_s3adsp1800_init; mc->is_default = true; + + prop = object_class_property_add_enum(oc, "endianness", "EndianMode", + &EndianMode_lookup, + machine_get_endianness, + machine_set_endianness); + object_property_set_default_str(prop, TARGET_BIG_ENDIAN ? "big" : "little"); + object_class_property_set_description(oc, "endianness", + "Defines whether the machine runs in big or little endian mode"); } -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, + .instance_size = sizeof(S3Adsp1800MachineState), + }, +}; + +DEFINE_TYPES(petalogix_s3adsp1800_machine_types) diff --git a/hw/microblaze/xlnx-zynqmp-pmu.c b/hw/microblaze/xlnx-zynqmp-pmu.c index 5a2016672a3..e909802bb74 100644 --- a/hw/microblaze/xlnx-zynqmp-pmu.c +++ b/hw/microblaze/xlnx-zynqmp-pmu.c @@ -17,7 +17,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/boards.h" #include "cpu.h" #include "boot.h" @@ -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); @@ -121,10 +121,12 @@ static void xlnx_zynqmp_pmu_soc_realize(DeviceState *dev, Error **errp) } } -static void xlnx_zynqmp_pmu_soc_class_init(ObjectClass *oc, void *data) +static void xlnx_zynqmp_pmu_soc_class_init(ObjectClass *oc, const 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; } @@ -170,7 +172,7 @@ static void xlnx_zynqmp_pmu_init(MachineState *machine) qdev_realize(DEVICE(pmu), NULL, &error_fatal); /* Load the kernel */ - microblaze_load_kernel(&pmu->cpu, XLNX_ZYNQMP_PMU_RAM_ADDR, + microblaze_load_kernel(&pmu->cpu, true, XLNX_ZYNQMP_PMU_RAM_ADDR, machine->ram_size, machine->initrd_filename, machine->dtb, @@ -179,9 +181,8 @@ static void xlnx_zynqmp_pmu_init(MachineState *machine) static void xlnx_zynqmp_pmu_machine_init(MachineClass *mc) { - mc->desc = "Xilinx ZynqMP PMU machine"; + mc->desc = "Xilinx ZynqMP PMU machine (little endian)"; 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 da3a37e215e..b09c89a0175 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -1,17 +1,29 @@ config MALTA bool + default y + depends on MIPS + imply PCNET_PCI + imply PCI_DEVICES + imply TEST_DEVICES + select FDC37M81X select GT64120 - select ISA_SUPERIO - select PIIX4 + 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 @@ -25,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/boston.c b/hw/mips/boston.c index 4e11ff6cd6d..149a263bd5a 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" @@ -37,11 +37,11 @@ #include "qemu/guest-random.h" #include "qemu/log.h" #include "chardev/char.h" -#include "sysemu/device_tree.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "sysemu/runstate.h" -#include "sysemu/reset.h" +#include "system/device_tree.h" +#include "system/system.h" +#include "system/qtest.h" +#include "system/runstate.h" +#include "system/reset.h" #include #include "qom/object.h" @@ -220,7 +220,7 @@ static void boston_lcd_write(void *opaque, hwaddr addr, static const MemoryRegionOps boston_lcd_ops = { .read = boston_lcd_read, .write = boston_lcd_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static uint64_t boston_platreg_read(void *opaque, hwaddr addr, @@ -299,7 +299,7 @@ static void boston_platreg_write(void *opaque, hwaddr addr, static const MemoryRegionOps boston_platreg_ops = { .read = boston_platreg_read, .write = boston_platreg_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void mips_boston_instance_init(Object *obj) @@ -358,8 +358,8 @@ static void gen_firmware(void *p, hwaddr kernel_entry, hwaddr fdt_addr) kernel_entry); } -static const void *boston_fdt_filter(void *opaque, const void *fdt_orig, - const void *match_data, hwaddr *load_addr) +static void *boston_fdt_filter(void *opaque, const void *fdt_orig, + const void *match_data, hwaddr *load_addr) { BostonState *s = BOSTON(opaque); MachineState *machine = s->mach; @@ -395,7 +395,6 @@ static const void *boston_fdt_filter(void *opaque, const void *fdt_orig, 1, ram_high_sz); fdt = g_realloc(fdt, fdt_totalsize(fdt)); - qemu_fdt_dumpdtb(fdt, fdt_sz); s->fdt_base = *load_addr; @@ -677,7 +676,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; @@ -757,7 +757,7 @@ static void boston_mach_init(MachineState *machine) s->uart = serial_mm_init(sys_mem, boston_memmap[BOSTON_UART].base, 2, get_cps_irq(&s->cps, 3), 10000000, - serial_hd(0), DEVICE_NATIVE_ENDIAN); + serial_hd(0), DEVICE_LITTLE_ENDIAN); lcd = g_new(MemoryRegion, 1); memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8); @@ -769,11 +769,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, + pdev = pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus, PCI_DEVFN(0, 0), 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); + 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, @@ -790,12 +791,12 @@ static void boston_mach_init(MachineState *machine) kernel_size = load_elf(machine->kernel_filename, NULL, cpu_mips_kseg0_to_phys, NULL, &kernel_entry, NULL, &kernel_high, - NULL, 0, EM_MIPS, 1, 0); + NULL, ELFDATA2LSB, EM_MIPS, 1, 0); if (kernel_size > 0) { int dt_size; g_autofree const void *dtb_file_data = NULL; - g_autofree const void *dtb_load_data = NULL; + void *dtb_load_data = NULL; hwaddr dtb_paddr = QEMU_ALIGN_UP(kernel_high, 64 * KiB); hwaddr dtb_vaddr = cpu_mips_phys_to_kseg0(NULL, dtb_paddr); @@ -808,6 +809,12 @@ static void boston_mach_init(MachineState *machine) dtb_load_data = boston_fdt_filter(s, dtb_file_data, NULL, &dtb_vaddr); + if (!dtb_load_data) { + /* boston_fdt_filter() already printed the error for us */ + exit(1); + } + + machine->fdt = dtb_load_data; /* Calculate real fdt size after filter */ dt_size = fdt_totalsize(dtb_load_data); @@ -816,7 +823,8 @@ static void boston_mach_init(MachineState *machine) rom_ptr(dtb_paddr, dt_size)); } else { /* Try to load file as FIT */ - fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s); + fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, + &machine->fdt, s); if (fit_err) { error_report("unable to load kernel image"); exit(1); diff --git a/hw/mips/cps.c b/hw/mips/cps.c index 2b5269ebf1f..2a3ba3f58d2 100644 --- a/hw/mips/cps.c +++ b/hw/mips/cps.c @@ -24,9 +24,8 @@ #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" +#include "system/kvm.h" +#include "system/reset.h" qemu_irq get_cps_irq(MIPSCPSState *s, int pin_number) { @@ -78,11 +77,13 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) 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); @@ -98,7 +99,6 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) 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); } @@ -106,8 +106,6 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) /* Inter-Thread Communication Unit */ if (itu_present) { object_initialize_child(OBJECT(dev), "itu", &s->itu, TYPE_MIPS_ITU); - object_property_set_link(OBJECT(&s->itu), "cpu[0]", - OBJECT(first_cpu), &error_abort); object_property_set_uint(OBJECT(&s->itu), "num-fifo", 16, &error_abort); object_property_set_uint(OBJECT(&s->itu), "num-semaphores", 16, @@ -168,14 +166,14 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gcr), 0)); } -static Property mips_cps_properties[] = { +static const 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_END_OF_LIST() + DEFINE_PROP_BOOL("cpu-big-endian", MIPSCPSState, cpu_is_bigendian, false), }; -static void mips_cps_class_init(ObjectClass *klass, void *data) +static void mips_cps_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/mips/fuloong2e.c b/hw/mips/fuloong2e.c index c827f615f3b..2a8507b8b0a 100644 --- a/hw/mips/fuloong2e.c +++ b/hw/mips/fuloong2e.c @@ -30,17 +30,17 @@ #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" #include "hw/qdev-properties.h" #include "elf.h" #include "hw/isa/vt82c686.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" -#include "sysemu/sysemu.h" +#include "system/qtest.h" +#include "system/reset.h" +#include "system/system.h" #include "qemu/error-report.h" +#include "exec/tswap.h" #define ENVP_PADDR 0x2000 #define ENVP_VADDR cpu_mips_phys_to_kseg0(NULL, ENVP_PADDR) @@ -106,7 +106,7 @@ static uint64_t load_kernel(MIPSCPU *cpu) cpu_mips_kseg0_to_phys, NULL, &kernel_entry, NULL, &kernel_high, NULL, - 0, EM_MIPS, 1, 0); + ELFDATA2LSB, EM_MIPS, 1, 0); if (kernel_size < 0) { error_report("could not load kernel '%s': %s", loaderparams.kernel_filename, @@ -202,19 +202,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 +230,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 +285,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), - 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)); @@ -336,7 +334,7 @@ static void mips_fuloong2e_machine_init(MachineClass *mc) mc->default_cpu_type = MIPS_CPU_TYPE_NAME("Loongson-2E"); mc->default_ram_size = 256 * MiB; mc->default_ram_id = "fuloong2e.ram"; - mc->minimum_page_bits = 14; + machine_add_audiodev_property(mc); } DEFINE_MACHINE("fuloong2e", mips_fuloong2e_machine_init) diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index 0081dcf9211..7fb0b97a388 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -26,18 +26,16 @@ #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" -#include "sysemu/sysemu.h" +#include "system/system.h" #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" @@ -46,14 +44,15 @@ #include "hw/audio/pcspk.h" #include "hw/input/i8042.h" #include "hw/sysbus.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" +#include "system/qtest.h" +#include "system/reset.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/help_option.h" #ifdef CONFIG_TCG -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" #endif /* CONFIG_TCG */ +#include "cpu.h" enum jazz_model_e { JAZZ_MAGNUM, @@ -114,6 +113,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) @@ -123,6 +162,8 @@ static const MemoryRegionOps dma_dummy_ops = { static void mips_jazz_init(MachineState *machine, enum jazz_model_e jazz_model) { + const char *bios_name = TARGET_BIG_ENDIAN ? "mips_bios.bin" + : "mipsel_bios.bin"; MemoryRegion *address_space = get_system_memory(); char *filename; int bios_size, n; @@ -138,12 +179,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); @@ -167,7 +208,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); @@ -199,7 +241,8 @@ static void mips_jazz_init(MachineState *machine, memory_region_add_subregion(address_space, 0xfff00000LL, bios2); /* load the BIOS image. */ - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware ?: BIOS_FILENAME); + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, + machine->firmware ?: bios_name); if (filename) { bios_size = load_image_targphys(filename, 0xfff00000LL, MAGNUM_BIOS_SIZE); @@ -244,9 +287,11 @@ static void mips_jazz_init(MachineState *machine, /* ISA devices */ i8259 = i8259_init(isa_bus, env->irq[4]); isa_bus_register_input_irqs(isa_bus, i8259); - i8257_dma_init(isa_bus, 0); + 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) { @@ -281,48 +326,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", 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; - 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); @@ -408,7 +412,7 @@ void mips_pica61_init(MachineState *machine) mips_jazz_init(machine, JAZZ_PICA61); } -static void mips_magnum_class_init(ObjectClass *oc, void *data) +static void mips_magnum_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -425,7 +429,7 @@ static const TypeInfo mips_magnum_type = { .class_init = mips_magnum_class_init, }; -static void mips_pica61_class_init(ObjectClass *oc, void *data) +static void mips_pica61_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/mips/loongson3_bootp.c b/hw/mips/loongson3_bootp.c index f99af229327..67812666c5b 100644 --- a/hw/mips/loongson3_bootp.c +++ b/hw/mips/loongson3_bootp.c @@ -21,18 +21,17 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/cutils.h" -#include "cpu.h" -#include "hw/boards.h" +#include "qemu/bswap.h" +#include "exec/hwaddr.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) +static void init_cpu_info(void *g_cpuinfo, uint32_t cpu_count, + uint32_t processor_id, uint64_t cpu_freq) { struct efi_cpuinfo_loongson *c = g_cpuinfo; c->cputype = cpu_to_le32(Loongson_3A); - c->processor_id = cpu_to_le32(MIPS_CPU(first_cpu)->env.CP0_PRid); + c->processor_id = cpu_to_le32(processor_id); if (cpu_freq > UINT_MAX) { c->cpu_clock_freq = cpu_to_le32(UINT_MAX); } else { @@ -40,8 +39,8 @@ static void init_cpu_info(void *g_cpuinfo, uint64_t cpu_freq) } c->cpu_startup_core_id = cpu_to_le16(0); - c->nr_cpus = cpu_to_le32(current_machine->smp.cpus); - c->total_node = cpu_to_le32(DIV_ROUND_UP(current_machine->smp.cpus, + c->nr_cpus = cpu_to_le32(cpu_count); + c->total_node = cpu_to_le32(DIV_ROUND_UP(cpu_count, LOONGSON3_CORE_PER_NODE)); } @@ -112,9 +111,10 @@ static void init_special_info(void *g_special) } void init_loongson_params(struct loongson_params *lp, void *p, + uint32_t cpu_count, uint32_t processor_id, uint64_t cpu_freq, uint64_t ram_size) { - init_cpu_info(p, cpu_freq); + init_cpu_info(p, cpu_count, processor_id, cpu_freq); lp->cpu_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp); p += ROUND_UP(sizeof(struct efi_cpuinfo_loongson), 64); @@ -148,4 +148,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 d525ab745a6..9dc325a8557 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 }; @@ -230,6 +233,7 @@ enum { extern const MemMapEntry virt_memmap[]; void init_loongson_params(struct loongson_params *lp, void *p, + uint32_t cpu_count, uint32_t processor_id, uint64_t cpu_freq, uint64_t ram_size); void init_reset_system(struct efi_reset_system_t *reset); diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index b74b358874f..de6fbcc0cb4 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -29,14 +29,14 @@ #include "qemu/datadir.h" #include "qapi/error.h" #include "elf.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" @@ -45,10 +45,10 @@ #include "hw/pci-host/gpex.h" #include "hw/usb.h" #include "net/net.h" -#include "sysemu/kvm.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/kvm.h" +#include "system/qtest.h" +#include "system/reset.h" +#include "system/runstate.h" #include "qemu/error-report.h" #define PM_CNTL_MODE 0x10 @@ -75,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 */ @@ -96,6 +97,7 @@ struct LoongsonMachineState { MemoryRegion *pio_alias; MemoryRegion *mmio_alias; MemoryRegion *ecam_alias; + MemoryRegion *core_iocsr[LOONGSON_MAX_VCPUS]; }; typedef struct LoongsonMachineState LoongsonMachineState; @@ -128,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; @@ -139,7 +144,7 @@ static void loongson3_pm_write(void *opaque, hwaddr addr, static const MemoryRegionOps loongson3_pm_ops = { .read = loongson3_pm_read, .write = loongson3_pm_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, .max_access_size = 1 @@ -148,7 +153,7 @@ static const MemoryRegionOps loongson3_pm_ops = { #define DEF_LOONGSON3_FREQ (800 * 1000 * 1000) -static uint64_t get_cpu_freq_hz(void) +static uint64_t get_cpu_freq_hz(const MIPSCPU *cpu) { #ifdef CONFIG_KVM int ret; @@ -159,7 +164,7 @@ static uint64_t get_cpu_freq_hz(void) }; if (kvm_enabled()) { - ret = kvm_vcpu_ioctl(first_cpu, KVM_GET_ONE_REG, &freq_reg); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_ONE_REG, &freq_reg); if (ret >= 0) { return freq * 2; } @@ -168,7 +173,7 @@ static uint64_t get_cpu_freq_hz(void) return DEF_LOONGSON3_FREQ; } -static void init_boot_param(void) +static void init_boot_param(unsigned cpu_count, uint32_t processor_id) { static void *p; struct boot_params *bp; @@ -179,7 +184,7 @@ static void init_boot_param(void) bp->efi.smbios.vers = cpu_to_le16(1); init_reset_system(&(bp->reset_system)); p += ROUND_UP(sizeof(struct boot_params), 64); - init_loongson_params(&(bp->efi.smbios.lp), p, + init_loongson_params(&(bp->efi.smbios.lp), p, cpu_count, processor_id, loaderparams.cpu_freq, loaderparams.ram_size); rom_add_blob_fixed("params_rom", bp, @@ -251,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 */ }; @@ -264,17 +280,22 @@ 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 void fw_conf_init(unsigned long ram_size) +static void fw_conf_init(void) { + static const uint8_t suspend[6] = {128, 0, 0, 129, 128, 128}; FWCfgState *fw_cfg; hwaddr cfg_addr = virt_memmap[VIRT_FW_CFG].base; fw_cfg = fw_cfg_init_mem_wide(cfg_addr, cfg_addr + 8, 8, 0, NULL); fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)current_machine->smp.cpus); fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)current_machine->smp.max_cpus); - fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); + fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, loaderparams.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_i64(fw_cfg, FW_CFG_CPU_FREQ, loaderparams.cpu_freq); + + 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); } @@ -335,9 +356,9 @@ 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, - NULL, 0, EM_MIPS, 1, 0); + &kernel_entry, + &kernel_low, &kernel_high, + NULL, ELFDATA2LSB, EM_MIPS, 1, 0); if (kernel_size < 0) { error_report("could not load kernel '%s': %s", loaderparams.kernel_filename, @@ -378,25 +399,33 @@ static uint64_t load_kernel(CPUMIPSState *env) return kernel_entry; } -static void main_cpu_reset(void *opaque) +static void generic_cpu_reset(void *opaque) { MIPSCPU *cpu = opaque; CPUMIPSState *env = &cpu->env; cpu_reset(CPU(cpu)); - /* Loongson-3 reset stuff */ if (loaderparams.kernel_filename) { - if (cpu == MIPS_CPU(first_cpu)) { - env->active_tc.gpr[4] = loaderparams.a0; - env->active_tc.gpr[5] = loaderparams.a1; - env->active_tc.gpr[6] = loaderparams.a2; - env->active_tc.PC = loaderparams.kernel_entry; - } env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL)); } } +static void main_cpu_reset(void *opaque) +{ + generic_cpu_reset(opaque); + + if (loaderparams.kernel_filename) { + MIPSCPU *cpu = opaque; + CPUMIPSState *env = &cpu->env; + + env->active_tc.gpr[4] = loaderparams.a0; + env->active_tc.gpr[5] = loaderparams.a1; + env->active_tc.gpr[6] = loaderparams.a2; + env->active_tc.PC = loaderparams.kernel_entry; + } +} + static inline void loongson3_virt_devices_init(MachineState *machine, DeviceState *pic) { @@ -437,7 +466,7 @@ static inline void loongson3_virt_devices_init(MachineState *machine, virt_memmap[VIRT_PCIE_PIO].base, s->pio_alias); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, virt_memmap[VIRT_PCIE_PIO].base); - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { irq = qdev_get_gpio_in(pic, PCIE_IRQ_BASE + i); sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); gpex_set_irq_num(GPEX_HOST(dev), i, PCIE_IRQ_BASE + i); @@ -447,33 +476,37 @@ static inline void loongson3_virt_devices_init(MachineState *machine, pci_vga_init(pci_bus); 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++) { - pci_nic_init_nofail(&nd_table[i], pci_bus, mc->default_nic, NULL); - } + pci_init_nic_devices(pci_bus, mc->default_nic); } static void mips_loongson3_virt_init(MachineState *machine) { int i; long bios_size; - MIPSCPU *cpu; + MIPSCPU *cpu = NULL; 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()) { @@ -507,6 +540,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); @@ -514,7 +560,7 @@ static void mips_loongson3_virt_init(MachineState *machine) serial_mm_init(address_space_mem, virt_memmap[VIRT_UART].base, 0, qdev_get_gpio_in(liointc, UART_IRQ), 115200, serial_hd(0), - DEVICE_NATIVE_ENDIAN); + DEVICE_LITTLE_ENDIAN); sysbus_create_simple("goldfish_rtc", virt_memmap[VIRT_RTC].base, qdev_get_gpio_in(liointc, RTC_IRQ)); @@ -522,28 +568,47 @@ static void mips_loongson3_virt_init(MachineState *machine) cpuclk = clock_new(OBJECT(machine), "cpu-refclk"); clock_set_hz(cpuclk, DEF_LOONGSON3_FREQ); - for (i = 0; i < machine->smp.cpus; i++) { + for (i = machine->smp.cpus - 1; i >= 0; --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); + qemu_register_reset(i ? generic_cpu_reset : main_cpu_reset, cpu); + + 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 (i >= 4) { + 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]); } } - env = &MIPS_CPU(first_cpu)->env; + assert(cpu); /* This variable points to the first created cpu. */ /* Allocate RAM/BIOS, 0x00000000~0x10000000 is alias of 0x80000000~0x90000000 */ memory_region_init_rom(bios, NULL, "loongson3.bios", @@ -552,6 +617,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); @@ -567,16 +633,16 @@ static void mips_loongson3_virt_init(MachineState *machine) * Please use -L to set the BIOS path and -bios to set bios name. */ + loaderparams.cpu_freq = get_cpu_freq_hz(cpu); + loaderparams.ram_size = ram_size; if (kernel_filename) { - loaderparams.cpu_freq = get_cpu_freq_hz(); - loaderparams.ram_size = ram_size; loaderparams.kernel_filename = kernel_filename; loaderparams.kernel_cmdline = kernel_cmdline; loaderparams.initrd_filename = initrd_filename; - loaderparams.kernel_entry = load_kernel(env); + loaderparams.kernel_entry = load_kernel(&cpu->env); init_boot_rom(); - init_boot_param(); + init_boot_param(machine->smp.cpus, cpu->env.CP0_PRid); } else { filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware ?: LOONGSON3_BIOSNAME); @@ -595,13 +661,13 @@ static void mips_loongson3_virt_init(MachineState *machine) exit(1); } - fw_conf_init(ram_size); + fw_conf_init(); } loongson3_virt_devices_init(machine, liointc); } -static void loongson3v_machine_class_init(ObjectClass *oc, void *data) +static void loongson3v_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -611,7 +677,6 @@ 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->minimum_page_bits = 14; mc->default_nic = "virtio-net-pci"; } diff --git a/hw/mips/malta.c b/hw/mips/malta.c index dac27fad9d9..cbdbb210568 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -26,22 +26,22 @@ #include "qemu/units.h" #include "qemu/bitops.h" #include "qemu/datadir.h" +#include "qemu/cutils.h" #include "qemu/guest-random.h" +#include "exec/tswap.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" @@ -49,17 +49,18 @@ #include "qom/object.h" #include "hw/sysbus.h" /* SysBusDevice */ #include "qemu/host-utils.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/qtest.h" +#include "system/reset.h" +#include "system/runstate.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" +#include "system/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) @@ -72,6 +73,7 @@ #define RESET_ADDRESS 0x1fc00000ULL #define FLASH_SIZE 0x400000 +#define BIOS_SIZE (4 * MiB) #define PIIX4_PCI_DEVFN PCI_DEVFN(10, 0) @@ -206,7 +208,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; @@ -375,11 +377,7 @@ static uint64_t malta_fpga_read(void *opaque, hwaddr addr, /* STATUS Register */ case 0x00208: -#if TARGET_BIG_ENDIAN - val = 0x00000012; -#else - val = 0x00000010; -#endif + val = TARGET_BIG_ENDIAN ? 0x00000012 : 0x00000010; break; /* JMPRS Register */ @@ -606,18 +604,9 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space, /* 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, "pcnet") == 0)) - /* The malta board has a PCNet card using PCI SLOT 11 */ - default_devaddr = "0b"; - - pci_nic_init_nofail(nd, pci_bus, "pcnet", default_devaddr); - } + /* 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"); } static void bl_setup_gt64120_jump_kernel(void **p, uint64_t run_addr, @@ -853,15 +842,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 */ @@ -873,15 +865,14 @@ static uint64_t load_kernel(void) uint32_t *prom_buf; long prom_size; int prom_index = 0; - uint8_t rng_seed[32]; - char rng_seed_hex[sizeof(rng_seed) * 2 + 1]; size_t rng_seed_prom_offset; kernel_size = load_elf(loaderparams.kernel_filename, NULL, cpu_mips_kseg0_to_phys, NULL, &kernel_entry, NULL, - &kernel_high, NULL, TARGET_BIG_ENDIAN, EM_MIPS, - 1, 0); + &kernel_high, NULL, + TARGET_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB, + EM_MIPS, 1, 0); if (kernel_size < 0) { error_report("could not load kernel '%s': %s", loaderparams.kernel_filename, @@ -949,14 +940,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); @@ -1036,7 +1026,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); @@ -1056,6 +1047,8 @@ 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_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); @@ -1174,9 +1167,12 @@ void mips_malta_init(MachineState *machine) target_long bios_size = FLASH_SIZE; /* Load firmware from flash. */ if (!dinfo) { + const char *bios_name = TARGET_BIG_ENDIAN ? "mips_bios.bin" + : "mipsel_bios.bin"; + /* Load a BIOS image. */ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, - machine->firmware ?: BIOS_FILENAME); + machine->firmware ?: bios_name); if (filename) { bios_size = load_image_targphys(filename, FLASH_ADDRESS, BIOS_SIZE); @@ -1194,8 +1190,7 @@ void mips_malta_init(MachineState *machine) * In little endian mode the 32bit words in the bios are swapped, * a neat trick which allows bi-endian firmware. */ -#if !TARGET_BIG_ENDIAN - { + if (!TARGET_BIG_ENDIAN) { uint32_t *end, *addr; const size_t swapsize = MIN(bios_size, 0x3e0000); addr = rom_ptr(FLASH_ADDRESS, swapsize); @@ -1208,7 +1203,6 @@ void mips_malta_init(MachineState *machine) addr++; } } -#endif } /* @@ -1238,8 +1232,9 @@ void mips_malta_init(MachineState *machine) pci_bus_map_irqs(pci_bus, malta_pci_slot_get_pirq); /* Southbridge */ - piix4 = pci_create_simple_multifunction(pci_bus, PIIX4_PCI_DEVFN, - 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 900613fc087..31dbd2bf4d9 100644 --- a/hw/mips/meson.build +++ b/hw/mips/meson.build @@ -1,15 +1,16 @@ mips_ss = ss.source_set() mips_ss.add(files('bootloader.c', 'mips_int.c')) -mips_ss.add(when: 'CONFIG_FW_CFG_MIPS', if_true: files('fw_cfg.c')) -mips_ss.add(when: 'CONFIG_LOONGSON3V', if_true: files('loongson3_bootp.c', 'loongson3_virt.c')) +common_ss.add(when: 'CONFIG_FW_CFG_MIPS', if_true: files('fw_cfg.c')) +common_ss.add(when: 'CONFIG_LOONGSON3V', if_true: files('loongson3_bootp.c')) +mips_ss.add(when: 'CONFIG_LOONGSON3V', if_true: files('loongson3_virt.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 73437cd90f4..26fdb934f50 100644 --- a/hw/mips/mips_int.c +++ b/hw/mips/mips_int.c @@ -23,8 +23,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "hw/irq.h" -#include "hw/mips/cpudevs.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_mips.h" static void cpu_mips_irq_request(void *opaque, int irq, int level) @@ -37,7 +36,7 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level) return; } - QEMU_IOTHREAD_LOCK_GUARD(); + BQL_LOCK_GUARD(); if (level) { env->CP0_Cause |= 1 << (irq + CP0Ca_IP); diff --git a/hw/mips/mipssim.c b/hw/mips/mipssim.c index 2f951f7fc6d..e843307b9b6 100644 --- a/hw/mips/mipssim.c +++ b/hw/mips/mipssim.c @@ -28,22 +28,23 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/datadir.h" +#include "system/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 "system/system.h" #include "hw/boards.h" -#include "hw/mips/bios.h" #include "hw/loader.h" #include "elf.h" #include "hw/sysbus.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" +#include "system/qtest.h" +#include "system/reset.h" +#include "cpu.h" + +#define BIOS_SIZE (4 * MiB) static struct _loaderparams { int ram_size; @@ -66,7 +67,8 @@ static uint64_t load_kernel(void) kernel_size = load_elf(loaderparams.kernel_filename, NULL, cpu_mips_kseg0_to_phys, NULL, &entry, NULL, - &kernel_high, NULL, TARGET_BIG_ENDIAN, + &kernel_high, NULL, + TARGET_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB, EM_MIPS, 1, 0); if (kernel_size < 0) { error_report("could not load kernel '%s': %s", @@ -111,13 +113,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); @@ -133,6 +137,8 @@ mips_mipssim_init(MachineState *machine) const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; + const char *bios_name = TARGET_BIG_ENDIAN ? "mips_bios.bin" + : "mipsel_bios.bin"; char *filename; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *isa = g_new(MemoryRegion, 1); @@ -151,7 +157,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); @@ -168,7 +175,8 @@ mips_mipssim_init(MachineState *machine) /* Map the BIOS / boot exception handler. */ memory_region_add_subregion(address_space_mem, 0x1fc00000LL, bios); /* Load a BIOS / boot exception handler image. */ - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware ?: BIOS_FILENAME); + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, + machine->firmware ?: bios_name); if (filename) { bios_size = load_image_targphys(filename, 0x1fc00000LL, BIOS_SIZE); g_free(filename); @@ -197,7 +205,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); @@ -214,13 +226,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/misc/Kconfig b/hw/misc/Kconfig index 6996d265e4c..ec0fa5aa9f8 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -30,8 +30,9 @@ config EDU default y if TEST_DEVICES depends on PCI && MSI_NONBROKEN -config PCA9552 +config I2C_ECHO bool + default y if TEST_DEVICES depends on I2C config PL310 @@ -63,9 +64,13 @@ config IVSHMEM_DEVICE default y if PCI_DEVICES depends on PCI && LINUX && IVSHMEM && MSI_NONBROKEN +config IVSHMEM_FLAT_DEVICE + bool + default y + depends on LINUX && IVSHMEM + config ECCMEMCTL bool - select ECC config IMX bool @@ -73,6 +78,15 @@ config IMX select SSI select USB_EHCI_SYSBUS +config FSL_IMX8MP_ANALOG + bool + +config FSL_IMX8MP_CCM + bool + +config STM32_RCC + bool + config STM32F2XX_SYSCFG bool @@ -82,6 +96,15 @@ config STM32F4XX_SYSCFG config STM32F4XX_EXTI bool +config STM32L4X5_EXTI + bool + +config STM32L4X5_SYSCFG + bool + +config STM32L4X5_RCC + bool + config MIPS_ITU bool @@ -125,6 +148,10 @@ config PVPANIC_ISA depends on ISA_BUS select PVPANIC_COMMON +config PVPANIC_MMIO + bool + select PVPANIC_COMMON + config AUX bool select I2C @@ -186,4 +213,13 @@ 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 a375ebc9878..bb00ae29692 100644 --- a/hw/misc/a9scu.c +++ b/hw/misc/a9scu.c @@ -116,25 +116,24 @@ 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() } }; -static Property a9_scu_properties[] = { +static const Property a9_scu_properties[] = { DEFINE_PROP_UINT32("num-cpu", A9SCUState, num_cpu, 1), - DEFINE_PROP_END_OF_LIST(), }; -static void a9_scu_class_init(ObjectClass *klass, void *data) +static void a9_scu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 index 68146ee3401..6b188c25a5d 100644 --- a/hw/misc/allwinner-a10-ccm.c +++ b/hw/misc/allwinner-a10-ccm.c @@ -147,7 +147,7 @@ static void allwinner_a10_ccm_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_a10_ccm_ops = { .read = allwinner_a10_ccm_read, .write = allwinner_a10_ccm_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -193,13 +193,13 @@ static const VMStateDescription allwinner_a10_ccm_vmstate = { .name = "allwinner-a10-ccm", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .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) +static void allwinner_a10_ccm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/misc/allwinner-a10-dramc.c b/hw/misc/allwinner-a10-dramc.c index e118b0c2fd4..c16814cc5b8 100644 --- a/hw/misc/allwinner-a10-dramc.c +++ b/hw/misc/allwinner-a10-dramc.c @@ -114,7 +114,7 @@ static void allwinner_a10_dramc_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_a10_dramc_ops = { .read = allwinner_a10_dramc_read, .write = allwinner_a10_dramc_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -147,14 +147,14 @@ static const VMStateDescription allwinner_a10_dramc_vmstate = { .name = "allwinner-a10-dramc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .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) +static void allwinner_a10_dramc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/misc/allwinner-cpucfg.c b/hw/misc/allwinner-cpucfg.c index bbd33a7dac8..90dd872abf0 100644 --- a/hw/misc/allwinner-cpucfg.c +++ b/hw/misc/allwinner-cpucfg.c @@ -217,7 +217,7 @@ static void allwinner_cpucfg_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_cpucfg_ops = { .read = allwinner_cpucfg_read, .write = allwinner_cpucfg_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -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), @@ -258,11 +258,11 @@ static const VMStateDescription allwinner_cpucfg_vmstate = { } }; -static void allwinner_cpucfg_class_init(ObjectClass *klass, void *data) +static void allwinner_cpucfg_class_init(ObjectClass *klass, const 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 18d10745458..be91c0c1cae 100644 --- a/hw/misc/allwinner-h3-ccu.c +++ b/hw/misc/allwinner-h3-ccu.c @@ -155,7 +155,7 @@ static void allwinner_h3_ccu_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_h3_ccu_ops = { .read = allwinner_h3_ccu_read, .write = allwinner_h3_ccu_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -212,17 +212,17 @@ 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() } }; -static void allwinner_h3_ccu_class_init(ObjectClass *klass, void *data) +static void allwinner_h3_ccu_class_init(ObjectClass *klass, const 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 1d37cf422cd..8834524c30c 100644 --- a/hw/misc/allwinner-h3-dramc.c +++ b/hw/misc/allwinner-h3-dramc.c @@ -24,7 +24,7 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "hw/misc/allwinner-h3-dramc.h" @@ -219,7 +219,7 @@ static void allwinner_h3_dramphy_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_h3_dramcom_ops = { .read = allwinner_h3_dramcom_read, .write = allwinner_h3_dramcom_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -230,7 +230,7 @@ static const MemoryRegionOps allwinner_h3_dramcom_ops = { static const MemoryRegionOps allwinner_h3_dramctl_ops = { .read = allwinner_h3_dramctl_read, .write = allwinner_h3_dramctl_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -241,7 +241,7 @@ static const MemoryRegionOps allwinner_h3_dramctl_ops = { static const MemoryRegionOps allwinner_h3_dramphy_ops = { .read = allwinner_h3_dramphy_read, .write = allwinner_h3_dramphy_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -314,17 +314,16 @@ static void allwinner_h3_dramc_init(Object *obj) sysbus_init_mmio(sbd, &s->dramphy_iomem); } -static Property allwinner_h3_dramc_properties[] = { +static const Property allwinner_h3_dramc_properties[] = { DEFINE_PROP_UINT64("ram-addr", AwH3DramCtlState, ram_addr, 0x0), DEFINE_PROP_UINT32("ram-size", AwH3DramCtlState, ram_size, 256 * MiB), - DEFINE_PROP_END_OF_LIST() }; static const VMStateDescription allwinner_h3_dramc_vmstate = { .name = "allwinner-h3-dramc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .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), @@ -332,11 +331,11 @@ static const VMStateDescription allwinner_h3_dramc_vmstate = { } }; -static void allwinner_h3_dramc_class_init(ObjectClass *klass, void *data) +static void allwinner_h3_dramc_class_init(ObjectClass *klass, const 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 1d07efa880d..6b86524606a 100644 --- a/hw/misc/allwinner-h3-sysctrl.c +++ b/hw/misc/allwinner-h3-sysctrl.c @@ -78,7 +78,7 @@ static void allwinner_h3_sysctrl_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_h3_sysctrl_ops = { .read = allwinner_h3_sysctrl_read, .write = allwinner_h3_sysctrl_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -110,17 +110,18 @@ 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() } }; -static void allwinner_h3_sysctrl_class_init(ObjectClass *klass, void *data) +static void allwinner_h3_sysctrl_class_init(ObjectClass *klass, + const 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 index d82fee12db6..4e21eeafdd0 100644 --- a/hw/misc/allwinner-r40-ccu.c +++ b/hw/misc/allwinner-r40-ccu.c @@ -129,7 +129,7 @@ static void allwinner_r40_ccu_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_r40_ccu_ops = { .read = allwinner_r40_ccu_read, .write = allwinner_r40_ccu_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -179,17 +179,17 @@ static const VMStateDescription allwinner_r40_ccu_vmstate = { .name = "allwinner-r40-ccu", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .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) +static void allwinner_r40_ccu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = allwinner_r40_ccu_reset; + device_class_set_legacy_reset(dc, allwinner_r40_ccu_reset); dc->vmsd = &allwinner_r40_ccu_vmstate; } diff --git a/hw/misc/allwinner-r40-dramc.c b/hw/misc/allwinner-r40-dramc.c index 6944f844559..1c8e17e3c0b 100644 --- a/hw/misc/allwinner-r40-dramc.c +++ b/hw/misc/allwinner-r40-dramc.c @@ -24,7 +24,7 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/bitops.h" @@ -297,7 +297,7 @@ static void allwinner_r40_dramphy_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_r40_dramcom_ops = { .read = allwinner_r40_dramcom_read, .write = allwinner_r40_dramcom_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -308,7 +308,7 @@ static const MemoryRegionOps allwinner_r40_dramcom_ops = { static const MemoryRegionOps allwinner_r40_dramctl_ops = { .read = allwinner_r40_dramctl_read, .write = allwinner_r40_dramctl_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -319,7 +319,7 @@ static const MemoryRegionOps allwinner_r40_dramctl_ops = { static const MemoryRegionOps allwinner_r40_dramphy_ops = { .read = allwinner_r40_dramphy_read, .write = allwinner_r40_dramphy_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -358,7 +358,7 @@ static void allwinner_r40_detect_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_r40_detect_ops = { .read = allwinner_r40_detect_read, .write = allwinner_r40_detect_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -393,7 +393,7 @@ static uint64_t allwinner_r40_dualrank_detect_read(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_r40_dualrank_detect_ops = { .read = allwinner_r40_dualrank_detect_read, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -421,19 +421,23 @@ static void allwinner_r40_dramc_realize(DeviceState *dev, Error **errp) exit(1); } - /* detect_cells */ - sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s), 3, s->ram_addr, 10); + /* 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. index = 4 + * after 1G for dualrank detect. */ memory_region_init_io(&s->dram_high, OBJECT(s), &allwinner_r40_dualrank_detect_ops, s, "DRAMHIGH", KiB); - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->dram_high); - sysbus_mmio_map(SYS_BUS_DEVICE(s), 4, s->ram_addr + GiB); + memory_region_add_subregion(get_system_memory(), s->ram_addr + GiB, + &s->dram_high); } static void allwinner_r40_dramc_init(Object *obj) @@ -458,25 +462,18 @@ static void allwinner_r40_dramc_init(Object *obj) &allwinner_r40_dramphy_ops, s, "DRAMPHY", 4 * KiB); sysbus_init_mmio(sbd, &s->dramphy_iomem); - - /* R40 support max 2G memory but we only support up to 1G now. index 3 */ - memory_region_init_io(&s->detect_cells, OBJECT(s), - &allwinner_r40_detect_ops, s, - "DRAMCELLS", 1 * GiB); - sysbus_init_mmio(sbd, &s->detect_cells); } -static Property allwinner_r40_dramc_properties[] = { +static const 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 = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(dramcom, AwR40DramCtlState, AW_R40_DRAMCOM_REGS_NUM), VMSTATE_UINT32_ARRAY(dramctl, AwR40DramCtlState, @@ -487,11 +484,11 @@ static const VMStateDescription allwinner_r40_dramc_vmstate = { } }; -static void allwinner_r40_dramc_class_init(ObjectClass *klass, void *data) +static void allwinner_r40_dramc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = allwinner_r40_dramc_reset; + 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); diff --git a/hw/misc/allwinner-sid.c b/hw/misc/allwinner-sid.c index 6d61f55b1d4..1e66c14567b 100644 --- a/hw/misc/allwinner-sid.c +++ b/hw/misc/allwinner-sid.c @@ -99,7 +99,7 @@ static void allwinner_sid_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_sid_ops = { .read = allwinner_sid_read, .write = allwinner_sid_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -127,16 +127,15 @@ static void allwinner_sid_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static Property allwinner_sid_properties[] = { +static const Property allwinner_sid_properties[] = { DEFINE_PROP_UUID_NODEFAULT("identifier", AwSidState, identifier), - DEFINE_PROP_END_OF_LIST() }; static const VMStateDescription allwinner_sid_vmstate = { .name = "allwinner-sid", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(control, AwSidState), VMSTATE_UINT32(rdkey, AwSidState), VMSTATE_UINT8_ARRAY_V(identifier.data, AwSidState, sizeof(QemuUUID), 1), @@ -144,11 +143,11 @@ static const VMStateDescription allwinner_sid_vmstate = { } }; -static void allwinner_sid_class_init(ObjectClass *klass, void *data) +static void allwinner_sid_class_init(ObjectClass *klass, const 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 index d76c24d081f..ed299ecaae7 100644 --- a/hw/misc/allwinner-sramc.c +++ b/hw/misc/allwinner-sramc.c @@ -104,7 +104,7 @@ static void allwinner_sramc_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_sramc_ops = { .read = allwinner_sramc_read, .write = allwinner_sramc_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -116,7 +116,7 @@ static const VMStateDescription allwinner_sramc_vmstate = { .name = "allwinner-sramc", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(sram_ver, AwSRAMCState), VMSTATE_UINT32(sram_soft_entry_reg0, AwSRAMCState), VMSTATE_END_OF_LIST() @@ -135,11 +135,11 @@ static void allwinner_sramc_reset(DeviceState *dev) } } -static void allwinner_sramc_class_init(ObjectClass *klass, void *data) +static void allwinner_sramc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = allwinner_sramc_reset; + device_class_set_legacy_reset(dc, allwinner_sramc_reset); dc->vmsd = &allwinner_sramc_vmstate; } @@ -163,7 +163,7 @@ static const TypeInfo allwinner_sramc_info = { .class_init = allwinner_sramc_class_init, }; -static void allwinner_r40_sramc_class_init(ObjectClass *klass, void *data) +static void allwinner_r40_sramc_class_init(ObjectClass *klass, const void *data) { AwSRAMCClass *sc = AW_SRAMC_CLASS(klass); diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index 72300d0cbc3..a015d4a9b8c 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -145,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; @@ -161,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) { @@ -269,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 = { @@ -342,14 +330,30 @@ 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[] = { +static const Property applesmc_isa_properties[] = { DEFINE_PROP_UINT32(APPLESMC_PROP_IO_BASE, AppleSMCState, iobase, APPLESMC_DEFAULT_IOBASE), DEFINE_PROP_STRING("osk", AppleSMCState, osk), - DEFINE_PROP_END_OF_LIST(), }; static void build_applesmc_aml(AcpiDevAmlIf *adev, Aml *scope) @@ -371,13 +375,14 @@ static void build_applesmc_aml(AcpiDevAmlIf *adev, Aml *scope) aml_append(scope, dev); } -static void qdev_applesmc_class_init(ObjectClass *klass, void *data) +static void qdev_applesmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); dc->realize = applesmc_isa_realize; - dc->reset = qdev_applesmc_isa_reset; + 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; @@ -388,7 +393,7 @@ static const TypeInfo applesmc_isa_info = { .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(AppleSMCState), .class_init = qdev_applesmc_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, diff --git a/hw/misc/arm11scu.c b/hw/misc/arm11scu.c index 17c36a05457..2ad4fd1d21d 100644 --- a/hw/misc/arm11scu.c +++ b/hw/misc/arm11scu.c @@ -75,12 +75,11 @@ static void arm11_scu_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static Property arm11_scu_properties[] = { +static const Property arm11_scu_properties[] = { DEFINE_PROP_UINT32("num-cpu", ARM11SCUState, num_cpu, 1), - DEFINE_PROP_END_OF_LIST() }; -static void arm11_scu_class_init(ObjectClass *oc, void *data) +static void arm11_scu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/misc/arm_l2x0.c b/hw/misc/arm_l2x0.c index 75c3eb8982f..8b4b61eed00 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), @@ -173,18 +173,17 @@ static void l2x0_priv_init(Object *obj) sysbus_init_mmio(dev, &s->iomem); } -static Property l2x0_properties[] = { +static const Property l2x0_properties[] = { DEFINE_PROP_UINT32("cache-type", L2x0State, cache_type, 0x1c100100), - DEFINE_PROP_END_OF_LIST(), }; -static void l2x0_class_init(ObjectClass *klass, void *data) +static void l2x0_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 42d46938543..0f4e37cd474 100644 --- a/hw/misc/arm_sysctl.c +++ b/hw/misc/arm_sysctl.c @@ -11,7 +11,7 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "qemu/bitops.h" #include "hw/sysbus.h" #include "migration/vmstate.h" @@ -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), @@ -520,7 +520,7 @@ static void arm_sysctl_write(void *opaque, hwaddr offset, * as zero. */ s->sys_cfgctrl = val & ~((3 << 18) | (1 << 31)); - if (val & (1 << 31)) { + if (extract64(val, 31, 1)) { /* Start bit set -- actually do something */ unsigned int dcc = extract32(s->sys_cfgctrl, 26, 4); unsigned int function = extract32(s->sys_cfgctrl, 20, 6); @@ -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; } } } @@ -623,7 +623,7 @@ static void arm_sysctl_finalize(Object *obj) g_free(s->db_clock_reset); } -static Property arm_sysctl_properties[] = { +static const Property arm_sysctl_properties[] = { DEFINE_PROP_UINT32("sys_id", arm_sysctl_state, sys_id, 0), DEFINE_PROP_UINT32("proc_id", arm_sysctl_state, proc_id, 0), /* Daughterboard power supply voltages (as reported via SYS_CFG) */ @@ -632,15 +632,14 @@ static Property arm_sysctl_properties[] = { /* Daughterboard clock reset values (as reported via SYS_CFG) */ DEFINE_PROP_ARRAY("db-clock", arm_sysctl_state, db_num_clocks, db_clock_reset, qdev_prop_uint32, uint32_t), - DEFINE_PROP_END_OF_LIST(), }; -static void arm_sysctl_class_init(ObjectClass *klass, void *data) +static void arm_sysctl_class_init(ObjectClass *klass, const 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 42fc38879f2..66e9218f277 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() }, @@ -125,11 +125,11 @@ static void pwrctrl_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static void pwrctrl_class_init(ObjectClass *klass, void *data) +static void pwrctrl_class_init(ObjectClass *klass, const 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-cpuid.c b/hw/misc/armsse-cpuid.c index e785a090519..a57764d731f 100644 --- a/hw/misc/armsse-cpuid.c +++ b/hw/misc/armsse-cpuid.c @@ -92,9 +92,8 @@ static const MemoryRegionOps armsse_cpuid_ops = { .valid.max_access_size = 4, }; -static Property armsse_cpuid_props[] = { +static const Property armsse_cpuid_props[] = { DEFINE_PROP_UINT32("CPUID", ARMSSECPUID, cpuid, 0), - DEFINE_PROP_END_OF_LIST() }; static void armsse_cpuid_init(Object *obj) @@ -107,7 +106,7 @@ static void armsse_cpuid_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static void armsse_cpuid_class_init(ObjectClass *klass, void *data) +static void armsse_cpuid_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/armsse-mhu.c b/hw/misc/armsse-mhu.c index 0be7f0fc874..d5d307a186c 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() @@ -176,11 +176,11 @@ static void armsse_mhu_init(Object *obj) sysbus_init_irq(sbd, &s->cpu1irq); } -static void armsse_mhu_class_init(ObjectClass *klass, void *data) +static void armsse_mhu_class_init(ObjectClass *klass, const 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/armv7m_ras.c b/hw/misc/armv7m_ras.c index de24922c944..7bf5acd0a54 100644 --- a/hw/misc/armv7m_ras.c +++ b/hw/misc/armv7m_ras.c @@ -72,7 +72,7 @@ static void armv7m_ras_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static void armv7m_ras_class_init(ObjectClass *klass, void *data) +static void armv7m_ras_class_init(ObjectClass *klass, const void *data) { /* This device has no state: no need for vmstate or reset */ } diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index b07506ec04e..726368fbbc2 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 @@ -9,14 +10,17 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qemu/log.h" #include "qemu/error-report.h" +#include "qemu/iov.h" #include "hw/misc/aspeed_hace.h" #include "qapi/error.h" #include "migration/vmstate.h" #include "crypto/hash.h" #include "hw/qdev-properties.h" #include "hw/irq.h" +#include "trace.h" #define R_CRYPT_CMD (0x10 / 4) @@ -26,9 +30,12 @@ #define TAG_IRQ BIT(15) #define R_HASH_SRC (0x20 / 4) -#define R_HASH_DEST (0x24 / 4) +#define R_HASH_DIGEST (0x24 / 4) #define R_HASH_KEY_BUFF (0x28 / 4) #define R_HASH_SRC_LEN (0x2c / 4) +#define R_HASH_SRC_HI (0x90 / 4) +#define R_HASH_DIGEST_HI (0x94 / 4) +#define R_HASH_KEY_BUFF_HI (0x98 / 4) #define R_HASH_CMD (0x30 / 4) /* Hash algorithm selection */ @@ -58,6 +65,7 @@ /* Other cmd bits */ #define HASH_IRQ_EN BIT(9) #define HASH_SG_EN BIT(18) +#define CRYPT_IRQ_EN BIT(12) /* Scatter-gather data list */ #define SG_LIST_LEN_SIZE 4 #define SG_LIST_LEN_MASK 0x0FFFFFFF @@ -68,17 +76,56 @@ 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 void hace_hexdump(const char *desc, const char *buf, size_t size) +{ + g_autoptr(GString) str = g_string_sized_new(64); + size_t len; + size_t i; + + for (i = 0; i < size; i += len) { + len = MIN(16, size - i); + g_string_truncate(str, 0); + qemu_hexdump_line(str, buf + i, len, 1, 4); + trace_aspeed_hace_hexdump(desc, i, str->str); + } +} + +static void hace_iov_hexdump(const char *desc, const struct iovec *iov, + const unsigned int iov_cnt) +{ + size_t size = 0; + char *buf; + int i; + + for (i = 0; i < iov_cnt; i++) { + size += iov[i].iov_len; + } + + buf = g_malloc(size); + + if (!buf) { + return; + } + + iov_to_buf(iov, iov_cnt, 0, buf, size); + hace_hexdump(desc, buf, size); + g_free(buf); +} + static int hash_algo_lookup(uint32_t reg) { int i; @@ -123,6 +170,11 @@ static bool has_padding(AspeedHACEState *s, struct iovec *iov, if (*total_msg_len <= s->total_req_len) { uint32_t padding_size = s->total_req_len - *total_msg_len; uint8_t *padding = iov->iov_base; + + if (padding_size > req_len) { + return false; + } + *pad_offset = req_len - padding_size; if (padding[*pad_offset] == 0x80) { return true; @@ -132,162 +184,269 @@ static bool has_padding(AspeedHACEState *s, struct iovec *iov, return false; } -static int reconstruct_iov(AspeedHACEState *s, struct iovec *iov, int id, - uint32_t *pad_offset) +static uint64_t hash_get_source_addr(AspeedHACEState *s) { - int i, iov_count; - if (*pad_offset != 0) { - s->iov_cache[s->iov_count].iov_base = iov[id].iov_base; - s->iov_cache[s->iov_count].iov_len = *pad_offset; - ++s->iov_count; - } - for (i = 0; i < s->iov_count; i++) { - iov[i].iov_base = s->iov_cache[i].iov_base; - iov[i].iov_len = s->iov_cache[i].iov_len; + AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s); + uint64_t src_addr = 0; + + src_addr = deposit64(src_addr, 0, 32, s->regs[R_HASH_SRC]); + if (ahc->has_dma64) { + src_addr = deposit64(src_addr, 32, 32, s->regs[R_HASH_SRC_HI]); } - iov_count = s->iov_count; - s->iov_count = 0; - s->total_req_len = 0; - return iov_count; + + return src_addr; } -/** - * 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) +static int hash_prepare_direct_iov(AspeedHACEState *s, struct iovec *iov, + bool acc_mode, bool *acc_final_request) { - uint32_t pad_offset; uint32_t total_msg_len; - s->total_req_len += *req_len; + uint32_t pad_offset; + uint64_t src; + void *haddr; + hwaddr plen; + int iov_idx; + + plen = s->regs[R_HASH_SRC_LEN]; + src = hash_get_source_addr(s); + trace_aspeed_hace_hash_addr("src", src); + haddr = address_space_map(&s->dram_as, src, &plen, false, + MEMTXATTRS_UNSPECIFIED); + if (haddr == NULL) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unable to map address, addr=0x%" HWADDR_PRIx + " ,plen=0x%" HWADDR_PRIx "\n", + __func__, src, plen); + return -1; + } - 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); - } + iov[0].iov_base = haddr; + iov_idx = 1; - *req_len -= s->total_req_len - total_msg_len; - s->total_req_len = 0; - iov[id].iov_len = *req_len; + if (acc_mode) { + s->total_req_len += plen; + + if (has_padding(s, &iov[0], plen, &total_msg_len, + &pad_offset)) { + /* Padding being present indicates the final request */ + *acc_final_request = true; + iov[0].iov_len = pad_offset; + } else { + iov[0].iov_len = plen; + } } 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; + iov[0].iov_len = plen; } - return id + 1; + return iov_idx; } -static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, - bool acc_mode) +static int hash_prepare_sg_iov(AspeedHACEState *s, struct iovec *iov, + bool acc_mode, bool *acc_final_request) { - struct iovec iov[ASPEED_HACE_MAX_SG]; - g_autofree uint8_t *digest_buf = NULL; - size_t digest_len = 0; - int niov = 0; - int i; + uint32_t total_msg_len; + uint32_t pad_offset; + uint32_t len = 0; + uint32_t sg_addr; + uint64_t src; + int iov_idx; + hwaddr plen; void *haddr; - if (sg_mode) { - uint32_t len = 0; - - for (i = 0; !(len & SG_LIST_LEN_LAST); i++) { - uint32_t addr, src; - hwaddr plen; - - if (i == ASPEED_HACE_MAX_SG) { - qemu_log_mask(LOG_GUEST_ERROR, - "aspeed_hace: guest failed to set end of sg list marker\n"); - break; - } - - src = s->regs[R_HASH_SRC] + (i * SG_LIST_ENTRY_SIZE); + src = hash_get_source_addr(s); + for (iov_idx = 0; !(len & SG_LIST_LEN_LAST); iov_idx++) { + if (iov_idx == ASPEED_HACE_MAX_SG) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to set end of sg list marker\n", + __func__); + return -1; + } - len = address_space_ldl_le(&s->dram_as, src, + len = address_space_ldl_le(&s->dram_as, src, + MEMTXATTRS_UNSPECIFIED, NULL); + sg_addr = address_space_ldl_le(&s->dram_as, src + SG_LIST_LEN_SIZE, MEMTXATTRS_UNSPECIFIED, NULL); + sg_addr &= SG_LIST_ADDR_MASK; + trace_aspeed_hace_hash_sg(iov_idx, src, sg_addr, len); + /* + * To maintain compatibility with older SoCs such as the AST2600, + * the AST2700 HW automatically set bit 34 of the 64-bit sg_addr. + * As a result, the firmware only needs to provide a 32-bit sg_addr + * containing bits [31:0]. This is sufficient for the AST2700, as + * it uses a DRAM offset rather than a DRAM address. + */ + plen = len & SG_LIST_LEN_MASK; + haddr = address_space_map(&s->dram_as, sg_addr, &plen, false, + MEMTXATTRS_UNSPECIFIED); - addr = address_space_ldl_le(&s->dram_as, src + SG_LIST_LEN_SIZE, - MEMTXATTRS_UNSPECIFIED, NULL); - addr &= SG_LIST_ADDR_MASK; + if (haddr == NULL) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unable to map address, sg_addr=0x%x, " + "plen=0x%" HWADDR_PRIx "\n", + __func__, sg_addr, plen); + return -1; + } - plen = len & SG_LIST_LEN_MASK; - 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); + src += SG_LIST_ENTRY_SIZE; + + iov[iov_idx].iov_base = haddr; + if (acc_mode) { + s->total_req_len += plen; + if (has_padding(s, &iov[iov_idx], plen, &total_msg_len, + &pad_offset)) { + /* Padding being present indicates the final request */ + *acc_final_request = true; + iov[iov_idx].iov_len = pad_offset; } else { - iov[i].iov_len = plen; + iov[iov_idx].iov_len = plen; } + } else { + iov[iov_idx].iov_len = plen; } - } 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_idx; +} + +static uint64_t hash_get_digest_addr(AspeedHACEState *s) +{ + AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s); + uint64_t digest_addr = 0; + + digest_addr = deposit64(digest_addr, 0, 32, s->regs[R_HASH_DIGEST]); + if (ahc->has_dma64) { + digest_addr = deposit64(digest_addr, 32, 32, s->regs[R_HASH_DIGEST_HI]); + } + + return digest_addr; +} + +static void hash_write_digest_and_unmap_iov(AspeedHACEState *s, + struct iovec *iov, + int iov_idx, + uint8_t *digest_buf, + size_t digest_len) +{ + uint64_t digest_addr = 0; + + digest_addr = hash_get_digest_addr(s); + trace_aspeed_hace_hash_addr("digest", digest_addr); + if (address_space_write(&s->dram_as, digest_addr, + MEMTXATTRS_UNSPECIFIED, + digest_buf, digest_len)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to write digest to 0x%" HWADDR_PRIx "\n", + __func__, digest_addr); + } + + if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) { + hace_hexdump("digest", (char *)digest_buf, digest_len); + } + + for (; iov_idx > 0; iov_idx--) { + address_space_unmap(&s->dram_as, iov[iov_idx - 1].iov_base, + iov[iov_idx - 1].iov_len, false, + iov[iov_idx - 1].iov_len); + } +} + +static void hash_execute_non_acc_mode(AspeedHACEState *s, int algo, + struct iovec *iov, int iov_idx) +{ + g_autofree uint8_t *digest_buf = NULL; + Error *local_err = NULL; + size_t digest_len = 0; + + if (qcrypto_hash_bytesv(algo, iov, iov_idx, &digest_buf, + &digest_len, &local_err) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: qcrypto hash bytesv failed : %s", + __func__, error_get_pretty(local_err)); + error_free(local_err); + return; + } + + hash_write_digest_and_unmap_iov(s, iov, iov_idx, digest_buf, digest_len); +} + +static void hash_execute_acc_mode(AspeedHACEState *s, int algo, + struct iovec *iov, int iov_idx, + bool final_request) +{ + g_autofree uint8_t *digest_buf = NULL; + Error *local_err = NULL; + size_t digest_len = 0; + + trace_aspeed_hace_hash_execute_acc_mode(final_request); + + if (s->hash_ctx == NULL) { + s->hash_ctx = qcrypto_hash_new(algo, &local_err); + if (s->hash_ctx == NULL) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto hash new failed : %s", + __func__, error_get_pretty(local_err)); + error_free(local_err); return; } - iov[0].iov_base = haddr; - iov[0].iov_len = len; - i = 1; - - if (s->iov_count) { - /* - * In aspeed sdk kernel driver, sg_mode is disabled in hash_final(). - * Thus if we received a request with sg_mode disabled, it is - * 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); - } - } } - if (niov) { - i = niov; + if (qcrypto_hash_updatev(s->hash_ctx, iov, iov_idx, &local_err) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto hash updatev failed : %s", + __func__, 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__); - return; + if (final_request) { + if (qcrypto_hash_finalize_bytes(s->hash_ctx, &digest_buf, + &digest_len, &local_err)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: qcrypto hash finalize bytes failed : %s", + __func__, error_get_pretty(local_err)); + error_free(local_err); + local_err = NULL; + } + + qcrypto_hash_free(s->hash_ctx); + + s->hash_ctx = NULL; + s->total_req_len = 0; } - if (address_space_write(&s->dram_as, s->regs[R_HASH_DEST], - MEMTXATTRS_UNSPECIFIED, - digest_buf, digest_len)) { + hash_write_digest_and_unmap_iov(s, iov, iov_idx, digest_buf, digest_len); +} + +static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, + bool acc_mode) +{ + QEMU_UNINITIALIZED struct iovec iov[ASPEED_HACE_MAX_SG]; + bool acc_final_request = false; + int iov_idx = -1; + + /* Prepares the iov for hashing operations based on the selected mode */ + if (sg_mode) { + iov_idx = hash_prepare_sg_iov(s, iov, acc_mode, &acc_final_request); + } else { + iov_idx = hash_prepare_direct_iov(s, iov, acc_mode, + &acc_final_request); + } + + if (iov_idx <= 0) { qemu_log_mask(LOG_GUEST_ERROR, - "aspeed_hace: address space write failed\n"); + "%s: Failed to prepare iov\n", __func__); + return; } - for (; i > 0; i--) { - address_space_unmap(&s->dram_as, iov[i - 1].iov_base, - iov[i - 1].iov_len, false, - iov[i - 1].iov_len); + if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) { + hace_iov_hexdump("plaintext", iov, iov_idx); } - /* - * Set status bits to indicate completion. Testing shows hardware sets - * these irrespective of HASH_IRQ_EN. - */ - s->regs[R_STATUS] |= HASH_IRQ; + /* Executes the hash operation */ + if (acc_mode) { + hash_execute_acc_mode(s, algo, iov, iov_idx, acc_final_request); + } else { + hash_execute_non_acc_mode(s, algo, iov, iov_idx); + } } static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int size) @@ -296,12 +455,7 @@ static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int size) addr >>= 2; - if (addr >= ASPEED_HACE_NR_REGS) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", - __func__, addr << 2); - return 0; - } + trace_aspeed_hace_read(addr << 2, s->regs[addr]); return s->regs[addr]; } @@ -314,12 +468,7 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, addr >>= 2; - if (addr >= ASPEED_HACE_NR_REGS) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", - __func__, addr << 2); - return; - } + trace_aspeed_hace_write(addr << 2, data); switch (addr) { case R_STATUS: @@ -330,11 +479,20 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, qemu_irq_lower(s->irq); } } + if (ahc->raise_crypt_interrupt_workaround) { + if (data & CRYPT_IRQ) { + data &= ~CRYPT_IRQ; + + if (s->regs[addr] & CRYPT_IRQ) { + qemu_irq_lower(s->irq); + } + } + } break; case R_HASH_SRC: data &= ahc->src_mask; break; - case R_HASH_DEST: + case R_HASH_DIGEST: data &= ahc->dest_mask; break; case R_HASH_KEY_BUFF: @@ -362,10 +520,16 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid hash algorithm selection 0x%"PRIx64"\n", __func__, data & ahc->hash_mask); - break; + } else { + do_hash_operation(s, algo, data & HASH_SG_EN, + ((data & HASH_HMAC_MASK) == HASH_DIGEST_ACCUM)); } - do_hash_operation(s, algo, data & HASH_SG_EN, - ((data & HASH_HMAC_MASK) == HASH_DIGEST_ACCUM)); + + /* + * Set status bits to indicate completion. Testing shows hardware sets + * these irrespective of HASH_IRQ_EN. + */ + s->regs[R_STATUS] |= HASH_IRQ; if (data & HASH_IRQ_EN) { qemu_irq_raise(s->irq); @@ -375,6 +539,21 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, case R_CRYPT_CMD: qemu_log_mask(LOG_UNIMP, "%s: Crypt commands not implemented\n", __func__); + if (ahc->raise_crypt_interrupt_workaround) { + s->regs[R_STATUS] |= CRYPT_IRQ; + if (data & CRYPT_IRQ_EN) { + qemu_irq_raise(s->irq); + } + } + break; + case R_HASH_SRC_HI: + data &= ahc->src_hi_mask; + break; + case R_HASH_DIGEST_HI: + data &= ahc->dest_hi_mask; + break; + case R_HASH_KEY_BUFF_HI: + data &= ahc->key_hi_mask; break; default: break; @@ -396,9 +575,14 @@ static const MemoryRegionOps aspeed_hace_ops = { static void aspeed_hace_reset(DeviceState *dev) { struct AspeedHACEState *s = ASPEED_HACE(dev); + AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s); - memset(s->regs, 0, sizeof(s->regs)); - s->iov_count = 0; + if (s->hash_ctx != NULL) { + qcrypto_hash_free(s->hash_ctx); + s->hash_ctx = NULL; + } + + memset(s->regs, 0, ahc->nr_regs << 2); s->total_req_len = 0; } @@ -406,11 +590,13 @@ static void aspeed_hace_realize(DeviceState *dev, Error **errp) { AspeedHACEState *s = ASPEED_HACE(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s); sysbus_init_irq(sbd, &s->irq); + s->regs = g_new(uint32_t, ahc->nr_regs); memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_hace_ops, s, - TYPE_ASPEED_HACE, 0x1000); + TYPE_ASPEED_HACE, ahc->nr_regs << 2); if (!s->dram_mr) { error_setg(errp, TYPE_ASPEED_HACE ": 'dram' link not set"); @@ -422,31 +608,37 @@ static void aspeed_hace_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); } -static Property aspeed_hace_properties[] = { +static const Property aspeed_hace_properties[] = { DEFINE_PROP_LINK("dram", AspeedHACEState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription vmstate_aspeed_hace = { .name = TYPE_ASPEED_HACE, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, AspeedHACEState, ASPEED_HACE_NR_REGS), + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { VMSTATE_UINT32(total_req_len, AspeedHACEState), - VMSTATE_UINT32(iov_count, AspeedHACEState), VMSTATE_END_OF_LIST(), } }; -static void aspeed_hace_class_init(ObjectClass *klass, void *data) +static void aspeed_hace_unrealize(DeviceState *dev) +{ + AspeedHACEState *s = ASPEED_HACE(dev); + + g_free(s->regs); + s->regs = NULL; +} + +static void aspeed_hace_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_hace_realize; - dc->reset = aspeed_hace_reset; + dc->unrealize = aspeed_hace_unrealize; + device_class_set_legacy_reset(dc, aspeed_hace_reset); device_class_set_props(dc, aspeed_hace_properties); dc->vmsd = &vmstate_aspeed_hace; } @@ -459,13 +651,14 @@ static const TypeInfo aspeed_hace_info = { .class_size = sizeof(AspeedHACEClass) }; -static void aspeed_ast2400_hace_class_init(ObjectClass *klass, void *data) +static void aspeed_ast2400_hace_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass); dc->desc = "AST2400 Hash and Crypto Engine"; + ahc->nr_regs = 0x64 >> 2; ahc->src_mask = 0x0FFFFFFF; ahc->dest_mask = 0x0FFFFFF8; ahc->key_mask = 0x0FFFFFC0; @@ -478,13 +671,14 @@ static const TypeInfo aspeed_ast2400_hace_info = { .class_init = aspeed_ast2400_hace_class_init, }; -static void aspeed_ast2500_hace_class_init(ObjectClass *klass, void *data) +static void aspeed_ast2500_hace_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass); dc->desc = "AST2500 Hash and Crypto Engine"; + ahc->nr_regs = 0x64 >> 2; ahc->src_mask = 0x3fffffff; ahc->dest_mask = 0x3ffffff8; ahc->key_mask = 0x3FFFFFC0; @@ -497,13 +691,14 @@ static const TypeInfo aspeed_ast2500_hace_info = { .class_init = aspeed_ast2500_hace_class_init, }; -static void aspeed_ast2600_hace_class_init(ObjectClass *klass, void *data) +static void aspeed_ast2600_hace_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass); dc->desc = "AST2600 Hash and Crypto Engine"; + ahc->nr_regs = 0x64 >> 2; ahc->src_mask = 0x7FFFFFFF; ahc->dest_mask = 0x7FFFFFF8; ahc->key_mask = 0x7FFFFFF8; @@ -516,13 +711,14 @@ static const TypeInfo aspeed_ast2600_hace_info = { .class_init = aspeed_ast2600_hace_class_init, }; -static void aspeed_ast1030_hace_class_init(ObjectClass *klass, void *data) +static void aspeed_ast1030_hace_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass); dc->desc = "AST1030 Hash and Crypto Engine"; + ahc->nr_regs = 0x64 >> 2; ahc->src_mask = 0x7FFFFFFF; ahc->dest_mask = 0x7FFFFFF8; ahc->key_mask = 0x7FFFFFF8; @@ -535,12 +731,58 @@ static const TypeInfo aspeed_ast1030_hace_info = { .class_init = aspeed_ast1030_hace_class_init, }; +static void aspeed_ast2700_hace_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass); + + dc->desc = "AST2700 Hash and Crypto Engine"; + + ahc->nr_regs = 0x9C >> 2; + ahc->src_mask = 0x7FFFFFFF; + ahc->dest_mask = 0x7FFFFFF8; + ahc->key_mask = 0x7FFFFFF8; + ahc->hash_mask = 0x00147FFF; + + /* + * The AST2700 supports a maximum DRAM size of 8 GB, with a DRAM + * addressable range from 0x0_0000_0000 to 0x1_FFFF_FFFF. Since this range + * fits within 34 bits, only bits [33:0] are needed to store the DRAM + * offset. To optimize address storage, the high physical address bits + * [1:0] of the source, digest and key buffer addresses are stored as + * dram_offset bits [33:32]. + * + * This approach eliminates the need to reduce the high part of the DRAM + * physical address for DMA operations. Previously, this was calculated as + * (high physical address bits [7:0] - 4), since the DRAM start address is + * 0x4_00000000, making the high part address [7:0] - 4. + */ + ahc->src_hi_mask = 0x00000003; + ahc->dest_hi_mask = 0x00000003; + ahc->key_hi_mask = 0x00000003; + + /* + * Currently, it does not support the CRYPT command. Instead, it only + * sends an interrupt to notify the firmware that the crypt command + * has completed. It is a temporary workaround. + */ + ahc->raise_crypt_interrupt_workaround = true; + ahc->has_dma64 = true; +} + +static const TypeInfo aspeed_ast2700_hace_info = { + .name = TYPE_ASPEED_AST2700_HACE, + .parent = TYPE_ASPEED_HACE, + .class_init = aspeed_ast2700_hace_class_init, +}; + static void aspeed_hace_register_types(void) { type_register_static(&aspeed_ast2400_hace_info); type_register_static(&aspeed_ast2500_hace_info); type_register_static(&aspeed_ast2600_hace_info); type_register_static(&aspeed_ast1030_hace_info); + type_register_static(&aspeed_ast2700_hace_info); type_register_static(&aspeed_hace_info); } diff --git a/hw/misc/aspeed_i3c.c b/hw/misc/aspeed_i3c.c index f54f5da522b..3bef1c84dd1 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; } @@ -323,18 +323,17 @@ static void aspeed_i3c_realize(DeviceState *dev, Error **errp) } -static Property aspeed_i3c_device_properties[] = { +static const Property aspeed_i3c_device_properties[] = { DEFINE_PROP_UINT8("device-id", AspeedI3CDevice, id, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void aspeed_i3c_device_class_init(ObjectClass *klass, void *data) +static void aspeed_i3c_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 +348,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), @@ -357,12 +356,12 @@ static const VMStateDescription vmstate_aspeed_i3c = { } }; -static void aspeed_i3c_class_init(ObjectClass *klass, void *data) +static void aspeed_i3c_class_init(ObjectClass *klass, const 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 2dddb27c35d..78406dae248 100644 --- a/hw/misc/aspeed_lpc.c +++ b/hw/misc/aspeed_lpc.c @@ -447,24 +447,23 @@ 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(), } }; -static Property aspeed_lpc_properties[] = { +static const Property aspeed_lpc_properties[] = { DEFINE_PROP_UINT32("hicr7", AspeedLPCState, hicr7, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void aspeed_lpc_class_init(ObjectClass *klass, void *data) +static void aspeed_lpc_class_init(ObjectClass *klass, const 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 93cc672e968..a7a449a9236 100644 --- a/hw/misc/aspeed_peci.c +++ b/hw/misc/aspeed_peci.c @@ -130,12 +130,12 @@ static void aspeed_peci_reset(DeviceState *dev) memset(s->regs, 0, sizeof(s->regs)); } -static void aspeed_peci_class_init(ObjectClass *klass, void *data) +static void aspeed_peci_class_init(ObjectClass *klass, const 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 c6f328e3be2..a7d101ba71f 100644 --- a/hw/misc/aspeed_sbc.c +++ b/hw/misc/aspeed_sbc.c @@ -130,24 +130,23 @@ static const VMStateDescription vmstate_aspeed_sbc = { .name = TYPE_ASPEED_SBC, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedSBCState, ASPEED_SBC_NR_REGS), VMSTATE_END_OF_LIST(), } }; -static Property aspeed_sbc_properties[] = { +static const Property aspeed_sbc_properties[] = { DEFINE_PROP_BOOL("emmc-abr", AspeedSBCState, emmc_abr, 0), DEFINE_PROP_UINT32("signing-settings", AspeedSBCState, signing_settings, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void aspeed_sbc_class_init(ObjectClass *klass, void *data) +static void aspeed_sbc_class_init(ObjectClass *klass, const 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); } @@ -160,7 +159,7 @@ static const TypeInfo aspeed_sbc_info = { .class_size = sizeof(AspeedSBCClass) }; -static void aspeed_ast2600_sbc_class_init(ObjectClass *klass, void *data) +static void aspeed_ast2600_sbc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 83353649064..4930e00fed1 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -134,6 +134,49 @@ #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_SCU_VGA_SCRATCH_0 TO_REG(0x900) + +#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 +287,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 +320,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(); @@ -364,6 +427,10 @@ static const MemoryRegionOps aspeed_ast2400_scu_ops = { .read = aspeed_scu_read, .write = aspeed_ast2400_scu_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, .valid = { .min_access_size = 1, .max_access_size = 4, @@ -374,7 +441,9 @@ static const MemoryRegionOps aspeed_ast2500_scu_ops = { .read = aspeed_scu_read, .write = aspeed_ast2500_scu_write, .endianness = DEVICE_LITTLE_ENDIAN, - .valid.min_access_size = 4, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, .valid.max_access_size = 4, .valid.unaligned = false, }; @@ -494,6 +563,11 @@ 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, + AST2700_A1_SILICON_REV, + AST2750_A1_SILICON_REV, }; bool is_supported_silicon_rev(uint32_t silicon_rev) @@ -531,25 +605,24 @@ 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() } }; -static Property aspeed_scu_properties[] = { +static const Property aspeed_scu_properties[] = { DEFINE_PROP_UINT32("silicon-rev", AspeedSCUState, silicon_rev, 0), DEFINE_PROP_UINT32("hw-strap1", AspeedSCUState, hw_strap1, 0), DEFINE_PROP_UINT32("hw-strap2", AspeedSCUState, hw_strap2, 0), DEFINE_PROP_UINT32("hw-prot-key", AspeedSCUState, hw_prot_key, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void aspeed_scu_class_init(ObjectClass *klass, void *data) +static void aspeed_scu_class_init(ObjectClass *klass, const 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); @@ -564,7 +637,7 @@ static const TypeInfo aspeed_scu_info = { .abstract = true, }; -static void aspeed_2400_scu_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_scu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); @@ -586,7 +659,7 @@ static const TypeInfo aspeed_2400_scu_info = { .class_init = aspeed_2400_scu_class_init, }; -static void aspeed_2500_scu_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_scu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); @@ -712,7 +785,9 @@ static const MemoryRegionOps aspeed_ast2600_scu_ops = { .read = aspeed_ast2600_scu_read, .write = aspeed_ast2600_scu_write, .endianness = DEVICE_LITTLE_ENDIAN, - .valid.min_access_size = 4, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, .valid.max_access_size = 4, .valid.unaligned = false, }; @@ -760,13 +835,13 @@ static void aspeed_ast2600_scu_reset(DeviceState *dev) s->regs[PROT_KEY] = s->hw_prot_key; } -static void aspeed_2600_scu_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_scu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 +858,246 @@ 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, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .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_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, + [AST2700_SCU_VGA_SCRATCH_0] = 0x00000040, +}; + +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); + s->regs[AST2700_SILICON_REV] = s->silicon_rev; + s->regs[AST2700_HW_STRAP1] = s->hw_strap1; +} + +static void aspeed_2700_scu_class_init(ObjectClass *klass, const 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, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .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_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, const 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, @@ -811,13 +1126,13 @@ static void aspeed_ast1030_scu_reset(DeviceState *dev) s->regs[PROT_KEY] = s->hw_prot_key; } -static void aspeed_1030_scu_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_scu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 +1156,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 abb27279339..f04d9930dd7 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -27,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 */ @@ -54,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) * @@ -76,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 | \ @@ -100,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); @@ -231,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, @@ -241,24 +286,24 @@ 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() } }; -static Property aspeed_sdmc_properties[] = { +static const Property aspeed_sdmc_properties[] = { DEFINE_PROP_UINT64("max-ram-size", AspeedSDMCState, max_ram_size, 0), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_BOOL("unlocked", AspeedSDMCState, unlocked, false), }; -static void aspeed_sdmc_class_init(ObjectClass *klass, void *data) +static void aspeed_sdmc_class_init(ObjectClass *klass, const 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); @@ -311,7 +356,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; } @@ -334,7 +380,7 @@ static void aspeed_2400_sdmc_write(AspeedSDMCState *s, uint32_t reg, static const uint64_t aspeed_2400_ram_sizes[] = { 64 * MiB, 128 * MiB, 256 * MiB, 512 * MiB, 0}; -static void aspeed_2400_sdmc_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_sdmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass); @@ -369,7 +415,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; } @@ -401,7 +448,7 @@ static void aspeed_2500_sdmc_write(AspeedSDMCState *s, uint32_t reg, static const uint64_t aspeed_2500_ram_sizes[] = { 128 * MiB, 256 * MiB, 512 * MiB, 1024 * MiB, 0}; -static void aspeed_2500_sdmc_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_sdmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass); @@ -449,8 +496,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; } @@ -494,7 +542,7 @@ static void aspeed_2600_sdmc_write(AspeedSDMCState *s, uint32_t reg, static const uint64_t aspeed_2600_ram_sizes[] = { 256 * MiB, 512 * MiB, 1024 * MiB, 2048 * MiB, 0}; -static void aspeed_2600_sdmc_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_sdmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass); @@ -512,12 +560,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, const 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 00000000000..c51484035e0 --- /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, const 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, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "AST2700 SLI Controller"; +} + +static void aspeed_2700_sliio_class_init(ObjectClass *klass, const 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 1c21577c98c..cc03c422b0e 100644 --- a/hw/misc/aspeed_xdma.c +++ b/hw/misc/aspeed_xdma.c @@ -144,13 +144,13 @@ 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(), }, }; -static void aspeed_2600_xdma_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_xdma_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedXDMAClass *axc = ASPEED_XDMA_CLASS(klass); @@ -173,7 +173,7 @@ static const TypeInfo aspeed_2600_xdma_info = { .class_init = aspeed_2600_xdma_class_init, }; -static void aspeed_2500_xdma_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_xdma_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedXDMAClass *axc = ASPEED_XDMA_CLASS(klass); @@ -195,7 +195,7 @@ static const TypeInfo aspeed_2500_xdma_info = { .class_init = aspeed_2500_xdma_class_init, }; -static void aspeed_2400_xdma_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_xdma_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedXDMAClass *axc = ASPEED_XDMA_CLASS(klass); @@ -217,12 +217,12 @@ static const TypeInfo aspeed_2400_xdma_info = { .class_init = aspeed_2400_xdma_class_init, }; -static void aspeed_xdma_class_init(ObjectClass *classp, void *data) +static void aspeed_xdma_class_init(ObjectClass *classp, const 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 28d50d9d097..877f3456062 100644 --- a/hw/misc/auxbus.c +++ b/hw/misc/auxbus.c @@ -50,7 +50,7 @@ static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent); static inline I2CBus *aux_bridge_get_i2c_bus(AUXTOI2CState *bridge); /* aux-bus implementation (internal not public) */ -static void aux_bus_class_init(ObjectClass *klass, void *data) +static void aux_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); @@ -256,7 +256,7 @@ struct AUXTOI2CState { I2CBus *i2c_bus; }; -static void aux_bridge_class_init(ObjectClass *oc, void *data) +static void aux_bridge_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -311,7 +311,7 @@ void aux_init_mmio(AUXSlave *aux_slave, MemoryRegion *mmio) aux_slave->mmio = mmio; } -static void aux_slave_class_init(ObjectClass *klass, void *data) +static void aux_slave_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); diff --git a/hw/misc/avr_power.c b/hw/misc/avr_power.c index a5412f2cfe6..411f016c997 100644 --- a/hw/misc/avr_power.c +++ b/hw/misc/avr_power.c @@ -90,11 +90,11 @@ static void avr_mask_init(Object *dev) s->val = 0x00; } -static void avr_mask_class_init(ObjectClass *klass, void *data) +static void avr_mask_class_init(ObjectClass *klass, const 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 index 41538c1cd78..46d17712dde 100644 --- a/hw/misc/axp2xx.c +++ b/hw/misc/axp2xx.c @@ -217,7 +217,7 @@ static int axp2xx_tx(I2CSlave *i2c, uint8_t data) static const VMStateDescription vmstate_axp2xx = { .name = TYPE_AXP2XX, .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(regs, AXP2xxI2CState, NR_REGS), VMSTATE_UINT8(ptr, AXP2xxI2CState), VMSTATE_UINT8(count, AXP2xxI2CState), @@ -225,7 +225,7 @@ static const VMStateDescription vmstate_axp2xx = { } }; -static void axp2xx_class_init(ObjectClass *oc, void *data) +static void axp2xx_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc); @@ -247,7 +247,7 @@ static const TypeInfo axp2xx_info = { .abstract = true, }; -static void axp209_class_init(ObjectClass *oc, void *data) +static void axp209_class_init(ObjectClass *oc, const void *data) { AXP2xxClass *sc = AXP2XX_CLASS(oc); @@ -260,7 +260,7 @@ static const TypeInfo axp209_info = { .class_init = axp209_class_init }; -static void axp221_class_init(ObjectClass *oc, void *data) +static void axp221_class_init(ObjectClass *oc, const void *data) { AXP2xxClass *sc = AXP2XX_CLASS(oc); diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c index 75e6c574d46..efe6f900dbf 100644 --- a/hw/misc/bcm2835_cprman.c +++ b/hw/misc/bcm2835_cprman.c @@ -125,18 +125,20 @@ 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() } }; -static void pll_class_init(ObjectClass *klass, void *data) +static void pll_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = pll_reset; + device_class_set_legacy_reset(dc, pll_reset); dc->vmsd = &pll_vmstate; + /* Reason: Part of BCM2835CprmanState component */ + dc->user_creatable = false; } static const TypeInfo cprman_pll_info = { @@ -229,18 +231,20 @@ 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() } }; -static void pll_channel_class_init(ObjectClass *klass, void *data) +static void pll_channel_class_init(ObjectClass *klass, const 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; + /* Reason: Part of BCM2835CprmanState component */ + dc->user_creatable = false; } static const TypeInfo cprman_pll_channel_info = { @@ -349,19 +353,21 @@ 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() } }; -static void clock_mux_class_init(ObjectClass *klass, void *data) +static void clock_mux_class_init(ObjectClass *klass, const 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; + /* Reason: Part of BCM2835CprmanState component */ + dc->user_creatable = false; } static const TypeInfo cprman_clock_mux_info = { @@ -404,18 +410,20 @@ 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() } }; -static void dsi0hsck_mux_class_init(ObjectClass *klass, void *data) +static void dsi0hsck_mux_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &dsi0hsck_mux_vmstate; + /* Reason: Part of BCM2835CprmanState component */ + dc->user_creatable = false; } static const TypeInfo cprman_dsi0hsck_mux_info = { @@ -772,23 +780,22 @@ 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() } }; -static Property cprman_properties[] = { +static const Property cprman_properties[] = { DEFINE_PROP_UINT32("xosc-freq-hz", BCM2835CprmanState, xosc_freq, 19200000), - DEFINE_PROP_END_OF_LIST() }; -static void cprman_class_init(ObjectClass *klass, void *data) +static void cprman_class_init(ObjectClass *klass, const 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 1e4e061bc1f..603eaaa710d 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), @@ -314,12 +314,12 @@ static void bcm2835_mbox_realize(DeviceState *dev, Error **errp) bcm2835_mbox_reset(dev); } -static void bcm2835_mbox_class_init(ObjectClass *klass, void *data) +static void bcm2835_mbox_class_init(ObjectClass *klass, const 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 0428e10ba5c..55d79e7e876 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), @@ -166,12 +166,12 @@ const VMStateDescription vmstate_mphi_state = { } }; -static void mphi_class_init(ObjectClass *klass, void *data) +static void mphi_class_init(ObjectClass *klass, const 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 976f3d34e5e..3ec7abad0e0 100644 --- a/hw/misc/bcm2835_powermgt.c +++ b/hw/misc/bcm2835_powermgt.c @@ -13,7 +13,7 @@ #include "qemu/module.h" #include "hw/misc/bcm2835_powermgt.h" #include "migration/vmstate.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #define PASSWORD 0x5a000000 #define PASSWORD_MASK 0xff000000 @@ -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), @@ -136,11 +136,11 @@ static void bcm2835_powermgt_reset(DeviceState *dev) s->wdog = 0x00000000; } -static void bcm2835_powermgt_class_init(ObjectClass *klass, void *data) +static void bcm2835_powermgt_class_init(ObjectClass *klass, const 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 4ed9faa54a1..a21c6a541c0 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -12,24 +12,20 @@ #include "migration/vmstate.h" #include "hw/irq.h" #include "hw/misc/bcm2835_mbox_defs.h" -#include "hw/misc/raspberrypi-fw-defs.h" -#include "sysemu/dma.h" +#include "hw/arm/raspberrypi-fw-defs.h" +#include "system/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 @@ -48,10 +44,10 @@ 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 RPI_FWREQ_PROPERTY_END: break; @@ -95,13 +91,16 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 8; break; 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 + { + /* + * 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 */ @@ -138,6 +137,13 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 8; break; + 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: @@ -264,18 +270,25 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 16; break; case RPI_FWREQ_FRAMEBUFFER_SET_PALETTE: - offset = ldl_le_phys(&s->dma_as, value + 12); - length = ldl_le_phys(&s->dma_as, value + 16); - n = 0; - 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++; + { + 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 RPI_FWREQ_FRAMEBUFFER_GET_NUM_DISPLAYS: stl_le_phys(&s->dma_as, value + 12, 1); resplen = 4; @@ -301,6 +314,107 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) 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); @@ -384,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), @@ -428,19 +542,21 @@ 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); bcm2835_property_reset(dev); } -static Property bcm2835_property_props[] = { +static const 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() }; -static void bcm2835_property_class_init(ObjectClass *klass, void *data) +static void bcm2835_property_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/bcm2835_rng.c b/hw/misc/bcm2835_rng.c index b3c80cf1867..e4d2c224c88 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() @@ -123,11 +123,11 @@ static void bcm2835_rng_reset(DeviceState *dev) s->rng_status = 0; } -static void bcm2835_rng_class_init(ObjectClass *klass, void *data) +static void bcm2835_rng_class_init(ObjectClass *klass, const 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 c6f3b1ad605..33bfc91c7aa 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,18 +107,18 @@ 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() } }; -static void bcm2835_thermal_class_init(ObjectClass *klass, void *data) +static void bcm2835_thermal_class_init(ObjectClass *klass, const 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 653e8ddcd5b..00000000000 --- 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 ab6de69ce72..04a9fc31223 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 "system/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 = { @@ -54,13 +56,12 @@ static void debug_exit_realizefn(DeviceState *d, Error **errp) isa->iobase, &isa->io); } -static Property debug_exit_properties[] = { +static const Property debug_exit_properties[] = { DEFINE_PROP_UINT32("iobase", ISADebugExitState, iobase, 0x501), DEFINE_PROP_UINT32("iosize", ISADebugExitState, iosize, 0x02), - DEFINE_PROP_END_OF_LIST(), }; -static void debug_exit_class_initfn(ObjectClass *klass, void *data) +static void debug_exit_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/djmemc.c b/hw/misc/djmemc.c new file mode 100644 index 00000000000..c5b09f551b0 --- /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, const 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 c65806e3d9a..81fc536131c 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), @@ -325,17 +325,16 @@ static void ecc_realize(DeviceState *dev, Error **errp) } } -static Property ecc_properties[] = { +static const Property ecc_properties[] = { DEFINE_PROP_UINT32("version", ECCState, version, -1), - DEFINE_PROP_END_OF_LIST(), }; -static void ecc_class_init(ObjectClass *klass, void *data) +static void ecc_class_init(ObjectClass *klass, const 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 a1f8bc77e77..cece633e113 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; -} - -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)) { + uint64_t xfer_end = xfer_start + xfer_size; + uint64_t dma_end = dma_start + dma_size; + + /* + * 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; @@ -355,9 +357,9 @@ static void *edu_fact_thread(void *opaque) 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(); } } @@ -413,7 +415,7 @@ static void edu_instance_init(Object *obj) &edu->dma_mask, OBJ_PROP_FLAG_READWRITE); } -static void edu_class_init(ObjectClass *class, void *data) +static void edu_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); PCIDeviceClass *k = PCI_DEVICE_CLASS(class); @@ -427,21 +429,18 @@ static void edu_class_init(ObjectClass *class, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); } -static void pci_edu_register_types(void) -{ - static InterfaceInfo interfaces[] = { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { }, - }; - static const TypeInfo edu_info = { +static const TypeInfo edu_types[] = { + { .name = TYPE_PCI_EDU_DEVICE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(EduState), .instance_init = edu_instance_init, .class_init = edu_class_init, - .interfaces = interfaces, - }; + .interfaces = (const InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, + } +}; - type_register_static(&edu_info); -} -type_init(pci_edu_register_types) +DEFINE_TYPES(edu_types) diff --git a/hw/misc/empty_slot.c b/hw/misc/empty_slot.c index 37b0ddfb02a..239d7603207 100644 --- a/hw/misc/empty_slot.c +++ b/hw/misc/empty_slot.c @@ -79,13 +79,12 @@ static void empty_slot_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); } -static Property empty_slot_properties[] = { +static const Property empty_slot_properties[] = { DEFINE_PROP_UINT64("size", EmptySlot, size, 0), DEFINE_PROP_STRING("name", EmptySlot, name), - DEFINE_PROP_END_OF_LIST(), }; -static void empty_slot_class_init(ObjectClass *klass, void *data) +static void empty_slot_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/exynos4210_clk.c b/hw/misc/exynos4210_clk.c index 58cec282f75..fdf5bdd6034 100644 --- a/hw/misc/exynos4210_clk.c +++ b/hw/misc/exynos4210_clk.c @@ -135,17 +135,17 @@ 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() } }; -static void exynos4210_clk_class_init(ObjectClass *klass, void *data) +static void exynos4210_clk_class_init(ObjectClass *klass, const 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 e24139c630b..a86ec9aba85 100644 --- a/hw/misc/exynos4210_pmu.c +++ b/hw/misc/exynos4210_pmu.c @@ -28,7 +28,7 @@ #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "qom/object.h" #ifndef DEBUG_PMU @@ -492,17 +492,17 @@ 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() } }; -static void exynos4210_pmu_class_init(ObjectClass *klass, void *data) +static void exynos4210_pmu_class_init(ObjectClass *klass, const 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 9214ec14cc0..2d0ebc457bc 100644 --- a/hw/misc/exynos4210_rng.c +++ b/hw/misc/exynos4210_rng.c @@ -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), @@ -253,11 +255,11 @@ static const VMStateDescription exynos4210_rng_vmstate = { } }; -static void exynos4210_rng_class_init(ObjectClass *klass, void *data) +static void exynos4210_rng_class_init(ObjectClass *klass, const 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/grlib_ahb_apb_pnp.c b/hw/misc/grlib_ahb_apb_pnp.c index 5b05f158592..cdca00ad542 100644 --- a/hw/misc/grlib_ahb_apb_pnp.c +++ b/hw/misc/grlib_ahb_apb_pnp.c @@ -168,7 +168,7 @@ static void grlib_ahb_pnp_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &ahb_pnp->iomem); } -static void grlib_ahb_pnp_class_init(ObjectClass *klass, void *data) +static void grlib_ahb_pnp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -280,7 +280,7 @@ static void grlib_apb_pnp_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &apb_pnp->iomem); } -static void grlib_apb_pnp_class_init(ObjectClass *klass, void *data) +static void grlib_apb_pnp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/i2c-echo.c b/hw/misc/i2c-echo.c index 5705ab5d734..2bb99ec0dbf 100644 --- a/hw/misc/i2c-echo.c +++ b/hw/misc/i2c-echo.c @@ -1,8 +1,19 @@ +/* + * 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" +#include "trace.h" #define TYPE_I2C_ECHO "i2c-echo" OBJECT_DECLARE_SIMPLE_TYPE(I2CEchoState, I2C_ECHO) @@ -70,11 +81,13 @@ static int i2c_echo_event(I2CSlave *s, enum i2c_event event) case I2C_START_RECV: state->pos = 0; + trace_i2c_echo_event(DEVICE(s)->canonical_path, "I2C_START_RECV"); break; case I2C_START_SEND: state->pos = 0; + trace_i2c_echo_event(DEVICE(s)->canonical_path, "I2C_START_SEND"); break; case I2C_FINISH: @@ -82,12 +95,15 @@ static int i2c_echo_event(I2CSlave *s, enum i2c_event event) state->state = I2C_ECHO_STATE_START_SEND; i2c_bus_master(state->bus, state->bh); + trace_i2c_echo_event(DEVICE(s)->canonical_path, "I2C_FINISH"); break; case I2C_NACK: + trace_i2c_echo_event(DEVICE(s)->canonical_path, "I2C_NACK"); break; default: + trace_i2c_echo_event(DEVICE(s)->canonical_path, "UNHANDLED"); return -1; } @@ -102,6 +118,7 @@ static uint8_t i2c_echo_recv(I2CSlave *s) return 0xff; } + trace_i2c_echo_recv(DEVICE(s)->canonical_path, state->data[state->pos]); return state->data[state->pos++]; } @@ -109,6 +126,7 @@ static int i2c_echo_send(I2CSlave *s, uint8_t data) { I2CEchoState *state = I2C_ECHO(s); + trace_i2c_echo_send(DEVICE(s)->canonical_path, data); if (state->pos > 2) { return -1; } @@ -125,11 +143,9 @@ static void i2c_echo_realize(DeviceState *dev, Error **errp) 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) +static void i2c_echo_class_init(ObjectClass *oc, const void *data) { I2CSlaveClass *sc = I2C_SLAVE_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/misc/imx25_ccm.c b/hw/misc/imx25_ccm.c index ff996e2f2ca..a6665d55353 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() }, @@ -292,12 +292,12 @@ static void imx25_ccm_init(Object *obj) sysbus_init_mmio(sd, &s->iomem); } -static void imx25_ccm_class_init(ObjectClass *klass, void *data) +static void imx25_ccm_class_init(ObjectClass *klass, const 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 ad30a4b2c0c..339458e8596 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() }, @@ -319,12 +319,12 @@ static void imx31_ccm_init(Object *obj) sysbus_init_mmio(sd, &s->iomem); } -static void imx31_ccm_class_init(ObjectClass *klass, void *data) +static void imx31_ccm_class_init(ObjectClass *klass, const 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 4c830fd89ae..a10b67d3961 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: @@ -755,12 +741,12 @@ static void imx6_ccm_init(Object *obj) sysbus_init_mmio(sd, &s->container); } -static void imx6_ccm_class_init(ObjectClass *klass, void *data) +static void imx6_ccm_class_init(ObjectClass *klass, const 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 a9c64d06ebc..8d2c4175e4e 100644 --- a/hw/misc/imx6_src.c +++ b/hw/misc/imx6_src.c @@ -17,18 +17,7 @@ #include "qemu/module.h" #include "target/arm/arm-powerctl.h" #include "hw/core/cpu.h" - -#ifndef DEBUG_IMX6_SRC -#define DEBUG_IMX6_SRC 0 -#endif - -#define DPRINTF(fmt, args...) \ - do { \ - if (DEBUG_IMX6_SRC) { \ - fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_SRC, \ - __func__, ##args); \ - } \ - } while (0) +#include "trace.h" static const char *imx6_src_reg_name(uint32_t reg) { @@ -68,7 +57,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 +66,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() }, @@ -87,7 +76,7 @@ static void imx6_src_reset(DeviceState *dev) { IMX6SRCState *s = IMX6_SRC(dev); - DPRINTF("\n"); + trace_imx6_src_reset(); memset(s->regs, 0, sizeof(s->regs)); @@ -111,7 +100,7 @@ static uint64_t imx6_src_read(void *opaque, hwaddr offset, unsigned size) } - DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_src_reg_name(index), value); + trace_imx6_src_read(imx6_src_reg_name(index), value); return value; } @@ -131,11 +120,10 @@ 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", - imx6_src_reg_name(SRC_SCR), s->regs[SRC_SCR]); + trace_imx6_clear_reset_bit(imx6_src_reg_name(SRC_SCR), s->regs[SRC_SCR]); g_free(ri); } @@ -173,8 +161,7 @@ static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value, return; } - DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_src_reg_name(index), - (uint32_t)current_value); + trace_imx6_src_write(imx6_src_reg_name(index), value); change_mask = s->regs[index] ^ (uint32_t)current_value; @@ -286,12 +273,12 @@ static void imx6_src_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); } -static void imx6_src_class_init(ObjectClass *klass, void *data) +static void imx6_src_class_init(ObjectClass *klass, const 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 e01bb68ac72..7f3ae61710c 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() @@ -904,12 +904,12 @@ static void imx6ul_ccm_init(Object *obj) sysbus_init_mmio(sd, &s->container); } -static void imx6ul_ccm_class_init(ObjectClass *klass, void *data) +static void imx6ul_ccm_class_init(ObjectClass *klass, const 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 f135ec7b7e4..c061a584e46 100644 --- a/hw/misc/imx7_ccm.c +++ b/hw/misc/imx7_ccm.c @@ -214,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() }, @@ -227,7 +227,7 @@ static uint32_t imx7_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) * 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 additionnal information when calling this + * This means we need additional information when calling this * function to know the requester's identity. */ uint32_t freq = 0; @@ -262,12 +262,12 @@ static uint32_t imx7_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) return freq; } -static void imx7_ccm_class_init(ObjectClass *klass, void *data) +static void imx7_ccm_class_init(ObjectClass *klass, const 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"; @@ -286,18 +286,18 @@ 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() }, }; -static void imx7_analog_class_init(ObjectClass *klass, void *data) +static void imx7_analog_class_init(ObjectClass *klass, const 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_gpr.c b/hw/misc/imx7_gpr.c index b03341a2eba..e12b4962730 100644 --- a/hw/misc/imx7_gpr.c +++ b/hw/misc/imx7_gpr.c @@ -102,7 +102,7 @@ static void imx7_gpr_init(Object *obj) sysbus_init_mmio(sd, &s->mmio); } -static void imx7_gpr_class_init(ObjectClass *klass, void *data) +static void imx7_gpr_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/imx7_snvs.c b/hw/misc/imx7_snvs.c index ee7698bd9cd..6a8733d23d7 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/runstate.h" +#include "system/system.h" +#include "system/rtc.h" +#include "system/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); + + 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); + } - if (offset == SNVS_LPCR && ((value & mask) == mask)) { - qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + 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) +static void imx7_snvs_class_init(ObjectClass *klass, const 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 index 983251e86f7..df0b0a69057 100644 --- a/hw/misc/imx7_src.c +++ b/hw/misc/imx7_src.c @@ -75,7 +75,7 @@ static const char *imx7_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; } } @@ -84,7 +84,7 @@ static const VMStateDescription vmstate_imx7_src = { .name = TYPE_IMX7_SRC, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, IMX7SRCState, SRC_MAX), VMSTATE_END_OF_LIST() }, @@ -136,7 +136,7 @@ static void imx7_clear_reset_bit(CPUState *cpu, run_on_cpu_data data) struct SRCSCRResetInfo *ri = data.host_ptr; IMX7SRCState *s = ri->s; - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); s->regs[SRC_A7RCR0] = deposit32(s->regs[SRC_A7RCR0], ri->reset_bit, 1, 0); @@ -251,12 +251,12 @@ static void imx7_src_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); } -static void imx7_src_class_init(ObjectClass *klass, void *data) +static void imx7_src_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = imx7_src_realize; - dc->reset = imx7_src_reset; + device_class_set_legacy_reset(dc, imx7_src_reset); dc->vmsd = &vmstate_imx7_src; dc->desc = "i.MX6 System Reset Controller"; } diff --git a/hw/misc/imx8mp_analog.c b/hw/misc/imx8mp_analog.c new file mode 100644 index 00000000000..23ffae84f89 --- /dev/null +++ b/hw/misc/imx8mp_analog.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2025 Bernhard Beschow + * + * i.MX 8M Plus ANALOG IP block emulation code + * + * Based on hw/misc/imx7_ccm.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" + +#include "hw/misc/imx8mp_analog.h" +#include "migration/vmstate.h" + +#define ANALOG_PLL_LOCK BIT(31) + +static void imx8mp_analog_reset(DeviceState *dev) +{ + IMX8MPAnalogState *s = IMX8MP_ANALOG(dev); + + memset(s->analog, 0, sizeof(s->analog)); + + s->analog[ANALOG_AUDIO_PLL1_GEN_CTRL] = 0x00002010; + s->analog[ANALOG_AUDIO_PLL1_FDIV_CTL0] = 0x00145032; + s->analog[ANALOG_AUDIO_PLL1_FDIV_CTL1] = 0x00000000; + s->analog[ANALOG_AUDIO_PLL1_SSCG_CTRL] = 0x00000000; + s->analog[ANALOG_AUDIO_PLL1_MNIT_CTRL] = 0x00100103; + s->analog[ANALOG_AUDIO_PLL2_GEN_CTRL] = 0x00002010; + s->analog[ANALOG_AUDIO_PLL2_FDIV_CTL0] = 0x00145032; + s->analog[ANALOG_AUDIO_PLL2_FDIV_CTL1] = 0x00000000; + s->analog[ANALOG_AUDIO_PLL2_SSCG_CTRL] = 0x00000000; + s->analog[ANALOG_AUDIO_PLL2_MNIT_CTRL] = 0x00100103; + s->analog[ANALOG_VIDEO_PLL1_GEN_CTRL] = 0x00002010; + s->analog[ANALOG_VIDEO_PLL1_FDIV_CTL0] = 0x00145032; + s->analog[ANALOG_VIDEO_PLL1_FDIV_CTL1] = 0x00000000; + s->analog[ANALOG_VIDEO_PLL1_SSCG_CTRL] = 0x00000000; + s->analog[ANALOG_VIDEO_PLL1_MNIT_CTRL] = 0x00100103; + s->analog[ANALOG_DRAM_PLL_GEN_CTRL] = 0x00002010; + s->analog[ANALOG_DRAM_PLL_FDIV_CTL0] = 0x0012c032; + s->analog[ANALOG_DRAM_PLL_FDIV_CTL1] = 0x00000000; + s->analog[ANALOG_DRAM_PLL_SSCG_CTRL] = 0x00000000; + s->analog[ANALOG_DRAM_PLL_MNIT_CTRL] = 0x00100103; + s->analog[ANALOG_GPU_PLL_GEN_CTRL] = 0x00000810; + s->analog[ANALOG_GPU_PLL_FDIV_CTL0] = 0x000c8031; + s->analog[ANALOG_GPU_PLL_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_GPU_PLL_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_VPU_PLL_GEN_CTRL] = 0x00000810; + s->analog[ANALOG_VPU_PLL_FDIV_CTL0] = 0x0012c032; + s->analog[ANALOG_VPU_PLL_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_VPU_PLL_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_ARM_PLL_GEN_CTRL] = 0x00000810; + s->analog[ANALOG_ARM_PLL_FDIV_CTL0] = 0x000fa031; + s->analog[ANALOG_ARM_PLL_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_ARM_PLL_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_SYS_PLL1_GEN_CTRL] = 0x0aaaa810; + s->analog[ANALOG_SYS_PLL1_FDIV_CTL0] = 0x00190032; + s->analog[ANALOG_SYS_PLL1_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_SYS_PLL1_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_SYS_PLL2_GEN_CTRL] = 0x0aaaa810; + s->analog[ANALOG_SYS_PLL2_FDIV_CTL0] = 0x000fa031; + s->analog[ANALOG_SYS_PLL2_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_SYS_PLL2_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_SYS_PLL3_GEN_CTRL] = 0x00000810; + s->analog[ANALOG_SYS_PLL3_FDIV_CTL0] = 0x000fa031; + s->analog[ANALOG_SYS_PLL3_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_SYS_PLL3_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_OSC_MISC_CFG] = 0x00000000; + s->analog[ANALOG_ANAMIX_PLL_MNIT_CTL] = 0x00000000; + s->analog[ANALOG_DIGPROG] = 0x00824010; + + /* all PLLs need to be locked */ + s->analog[ANALOG_AUDIO_PLL1_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_AUDIO_PLL2_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_VIDEO_PLL1_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_DRAM_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_GPU_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_VPU_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_ARM_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_SYS_PLL1_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_SYS_PLL2_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_SYS_PLL3_GEN_CTRL] |= ANALOG_PLL_LOCK; +} + +static uint64_t imx8mp_analog_read(void *opaque, hwaddr offset, unsigned size) +{ + IMX8MPAnalogState *s = opaque; + + return s->analog[offset >> 2]; +} + +static void imx8mp_analog_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IMX8MPAnalogState *s = opaque; + + if (offset >> 2 == ANALOG_DIGPROG) { + qemu_log_mask(LOG_GUEST_ERROR, + "Guest write to read-only ANALOG_DIGPROG register\n"); + } else { + s->analog[offset >> 2] = value; + } +} + +static const struct MemoryRegionOps imx8mp_analog_ops = { + .read = imx8mp_analog_read, + .write = imx8mp_analog_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx8mp_analog_init(Object *obj) +{ + IMX8MPAnalogState *s = IMX8MP_ANALOG(obj); + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + + memory_region_init(&s->mmio.container, obj, TYPE_IMX8MP_ANALOG, 0x10000); + + memory_region_init_io(&s->mmio.analog, obj, &imx8mp_analog_ops, s, + TYPE_IMX8MP_ANALOG, sizeof(s->analog)); + memory_region_add_subregion(&s->mmio.container, 0, &s->mmio.analog); + + sysbus_init_mmio(sd, &s->mmio.container); +} + +static const VMStateDescription imx8mp_analog_vmstate = { + .name = TYPE_IMX8MP_ANALOG, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(analog, IMX8MPAnalogState, ANALOG_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx8mp_analog_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_legacy_reset(dc, imx8mp_analog_reset); + dc->vmsd = &imx8mp_analog_vmstate; + dc->desc = "i.MX 8M Plus Analog Module"; +} + +static const TypeInfo imx8mp_analog_types[] = { + { + .name = TYPE_IMX8MP_ANALOG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMX8MPAnalogState), + .instance_init = imx8mp_analog_init, + .class_init = imx8mp_analog_class_init, + } +}; + +DEFINE_TYPES(imx8mp_analog_types); diff --git a/hw/misc/imx8mp_ccm.c b/hw/misc/imx8mp_ccm.c new file mode 100644 index 00000000000..911911ed865 --- /dev/null +++ b/hw/misc/imx8mp_ccm.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2025 Bernhard Beschow + * + * i.MX 8M Plus CCM IP block emulation code + * + * Based on hw/misc/imx7_ccm.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" + +#include "hw/misc/imx8mp_ccm.h" +#include "migration/vmstate.h" + +#include "trace.h" + +#define CKIH_FREQ 16000000 /* 16MHz crystal input */ + +static void imx8mp_ccm_reset(DeviceState *dev) +{ + IMX8MPCCMState *s = IMX8MP_CCM(dev); + + memset(s->ccm, 0, sizeof(s->ccm)); +} + +#define CCM_INDEX(offset) (((offset) & ~(hwaddr)0xF) / sizeof(uint32_t)) +#define CCM_BITOP(offset) ((offset) & (hwaddr)0xF) + +enum { + CCM_BITOP_NONE = 0x00, + CCM_BITOP_SET = 0x04, + CCM_BITOP_CLR = 0x08, + CCM_BITOP_TOG = 0x0C, +}; + +static uint64_t imx8mp_set_clr_tog_read(void *opaque, hwaddr offset, + unsigned size) +{ + const uint32_t *mmio = opaque; + + return mmio[CCM_INDEX(offset)]; +} + +static void imx8mp_set_clr_tog_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + const uint8_t bitop = CCM_BITOP(offset); + const uint32_t index = CCM_INDEX(offset); + uint32_t *mmio = opaque; + + switch (bitop) { + case CCM_BITOP_NONE: + mmio[index] = value; + break; + case CCM_BITOP_SET: + mmio[index] |= value; + break; + case CCM_BITOP_CLR: + mmio[index] &= ~value; + break; + case CCM_BITOP_TOG: + mmio[index] ^= value; + break; + }; +} + +static const struct MemoryRegionOps imx8mp_set_clr_tog_ops = { + .read = imx8mp_set_clr_tog_read, + .write = imx8mp_set_clr_tog_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + /* + * 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 imx8mp_ccm_init(Object *obj) +{ + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + IMX8MPCCMState *s = IMX8MP_CCM(obj); + + memory_region_init_io(&s->iomem, + obj, + &imx8mp_set_clr_tog_ops, + s->ccm, + TYPE_IMX8MP_CCM ".ccm", + sizeof(s->ccm)); + + sysbus_init_mmio(sd, &s->iomem); +} + +static const VMStateDescription imx8mp_ccm_vmstate = { + .name = TYPE_IMX8MP_CCM, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(ccm, IMX8MPCCMState, CCM_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static uint32_t imx8mp_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) +{ + /* + * 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. + */ + 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_IMX8MP_CCM, __func__, clock); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n", + TYPE_IMX8MP_CCM, __func__, clock); + break; + } + + trace_ccm_clock_freq(clock, freq); + + return freq; +} + +static void imx8mp_ccm_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IMXCCMClass *ccm = IMX_CCM_CLASS(klass); + + device_class_set_legacy_reset(dc, imx8mp_ccm_reset); + dc->vmsd = &imx8mp_ccm_vmstate; + dc->desc = "i.MX 8M Plus Clock Control Module"; + + ccm->get_clock_frequency = imx8mp_ccm_get_clock_frequency; +} + +static const TypeInfo imx8mp_ccm_types[] = { + { + .name = TYPE_IMX8MP_CCM, + .parent = TYPE_IMX_CCM, + .instance_size = sizeof(IMX8MPCCMState), + .instance_init = imx8mp_ccm_init, + .class_init = imx8mp_ccm_class_init, + }, +}; + +DEFINE_TYPES(imx8mp_ccm_types); diff --git a/hw/misc/imx_rngc.c b/hw/misc/imx_rngc.c index 082c6980ad5..630f6cb54b5 100644 --- a/hw/misc/imx_rngc.c +++ b/hw/misc/imx_rngc.c @@ -245,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), @@ -254,12 +254,12 @@ static const VMStateDescription vmstate_imx_rngc = { } }; -static void imx_rngc_class_init(ObjectClass *klass, void *data) +static void imx_rngc_class_init(ObjectClass *klass, const 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 00000000000..96221e1ee5a --- /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, const 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 b5a9e30a2c6..afd9ab48df7 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,24 +807,23 @@ 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 }, }; -static Property iotkit_secctl_props[] = { +static const Property iotkit_secctl_props[] = { DEFINE_PROP_UINT32("sse-version", IoTKitSecCtl, sse_version, 0), - DEFINE_PROP_END_OF_LIST() }; -static void iotkit_secctl_class_init(ObjectClass *klass, void *data) +static void iotkit_secctl_class_init(ObjectClass *klass, const 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 e664215ee67..d70e51ab2ec 100644 --- a/hw/misc/iotkit-sysctl.c +++ b/hw/misc/iotkit-sysctl.c @@ -20,7 +20,7 @@ #include "qemu/bitops.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "trace.h" #include "qapi/error.h" #include "hw/sysbus.h" @@ -777,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), @@ -798,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), @@ -818,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), @@ -828,29 +828,28 @@ 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 } }; -static Property iotkit_sysctl_props[] = { +static const Property iotkit_sysctl_props[] = { DEFINE_PROP_UINT32("sse-version", IoTKitSysCtl, sse_version, 0), DEFINE_PROP_UINT32("CPUWAIT_RST", IoTKitSysCtl, cpuwait_rst, 0), DEFINE_PROP_UINT32("INITSVTOR0_RST", IoTKitSysCtl, initsvtor0_rst, 0x10000000), DEFINE_PROP_UINT32("INITSVTOR1_RST", IoTKitSysCtl, initsvtor1_rst, 0x10000000), - DEFINE_PROP_END_OF_LIST() }; -static void iotkit_sysctl_class_init(ObjectClass *klass, void *data) +static void iotkit_sysctl_class_init(ObjectClass *klass, const 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/iotkit-sysinfo.c b/hw/misc/iotkit-sysinfo.c index aaa9305b2ea..57405cb7e17 100644 --- a/hw/misc/iotkit-sysinfo.c +++ b/hw/misc/iotkit-sysinfo.c @@ -131,12 +131,11 @@ static const MemoryRegionOps iotkit_sysinfo_ops = { .valid.max_access_size = 4, }; -static Property iotkit_sysinfo_props[] = { +static const Property iotkit_sysinfo_props[] = { DEFINE_PROP_UINT32("SYS_VERSION", IoTKitSysInfo, sys_version, 0), DEFINE_PROP_UINT32("SYS_CONFIG", IoTKitSysInfo, sys_config, 0), DEFINE_PROP_UINT32("sse-version", IoTKitSysInfo, sse_version, 0), DEFINE_PROP_UINT32("IIDR", IoTKitSysInfo, iidr, 0), - DEFINE_PROP_END_OF_LIST() }; static void iotkit_sysinfo_init(Object *obj) @@ -159,7 +158,7 @@ static void iotkit_sysinfo_realize(DeviceState *dev, Error **errp) } } -static void iotkit_sysinfo_class_init(ObjectClass *klass, void *data) +static void iotkit_sysinfo_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/ivshmem-flat.c b/hw/misc/ivshmem-flat.c new file mode 100644 index 00000000000..be28c24d73e --- /dev/null +++ b/hw/misc/ivshmem-flat.c @@ -0,0 +1,458 @@ +/* + * Inter-VM Shared Memory Flat Device + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) 2023 Linaro Ltd. + * Authors: + * Gustavo Romero + * + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "hw/irq.h" +#include "hw/qdev-properties-system.h" +#include "hw/sysbus.h" +#include "chardev/char-fe.h" +#include "system/address-spaces.h" +#include "trace.h" + +#include "hw/misc/ivshmem-flat.h" + +static int64_t ivshmem_flat_recv_msg(IvshmemFTState *s, int *pfd) +{ + int64_t msg; + int n, ret; + + n = 0; + do { + ret = qemu_chr_fe_read_all(&s->server_chr, (uint8_t *)&msg + n, + sizeof(msg) - n); + if (ret < 0) { + if (ret == -EINTR) { + continue; + } + exit(1); + } + n += ret; + } while (n < sizeof(msg)); + + if (pfd) { + *pfd = qemu_chr_fe_get_msgfd(&s->server_chr); + } + return le64_to_cpu(msg); +} + +static void ivshmem_flat_irq_handler(void *opaque) +{ + VectorInfo *vi = opaque; + EventNotifier *e = &vi->event_notifier; + uint16_t vector_id; + const VectorInfo (*v)[64]; + + assert(e->initialized); + + vector_id = vi->id; + + /* + * The vector info struct is passed to the handler via the 'opaque' pointer. + * This struct pointer allows the retrieval of the vector ID and its + * associated event notifier. However, for triggering an interrupt using + * qemu_set_irq, it's necessary to also have a pointer to the device state, + * i.e., a pointer to the IvshmemFTState struct. Since the vector info + * struct is contained within the IvshmemFTState struct, its pointer can be + * used to obtain the pointer to IvshmemFTState through simple pointer math. + */ + v = (void *)(vi - vector_id); /* v = &IvshmemPeer->vector[0] */ + IvshmemPeer *own_peer = container_of(v, IvshmemPeer, vector); + IvshmemFTState *s = container_of(own_peer, IvshmemFTState, own); + + /* Clear event */ + if (!event_notifier_test_and_clear(e)) { + return; + } + + trace_ivshmem_flat_irq_handler(vector_id); + + /* + * Toggle device's output line, which is connected to interrupt controller, + * generating an interrupt request to the CPU. + */ + qemu_irq_pulse(s->irq); +} + +static IvshmemPeer *ivshmem_flat_find_peer(IvshmemFTState *s, uint16_t peer_id) +{ + IvshmemPeer *peer; + + /* Own ID */ + if (s->own.id == peer_id) { + return &s->own; + } + + /* Peer ID */ + QTAILQ_FOREACH(peer, &s->peer, next) { + if (peer->id == peer_id) { + return peer; + } + } + + return NULL; +} + +static IvshmemPeer *ivshmem_flat_add_peer(IvshmemFTState *s, uint16_t peer_id) +{ + IvshmemPeer *new_peer; + + new_peer = g_malloc0(sizeof(*new_peer)); + new_peer->id = peer_id; + new_peer->vector_counter = 0; + + QTAILQ_INSERT_TAIL(&s->peer, new_peer, next); + + trace_ivshmem_flat_new_peer(peer_id); + + return new_peer; +} + +static void ivshmem_flat_remove_peer(IvshmemFTState *s, uint16_t peer_id) +{ + IvshmemPeer *peer; + + peer = ivshmem_flat_find_peer(s, peer_id); + assert(peer); + + QTAILQ_REMOVE(&s->peer, peer, next); + for (int n = 0; n < peer->vector_counter; n++) { + int efd; + efd = event_notifier_get_fd(&(peer->vector[n].event_notifier)); + close(efd); + } + + g_free(peer); +} + +static void ivshmem_flat_add_vector(IvshmemFTState *s, IvshmemPeer *peer, + int vector_fd) +{ + if (peer->vector_counter >= IVSHMEM_MAX_VECTOR_NUM) { + trace_ivshmem_flat_add_vector_failure(peer->vector_counter, + vector_fd, peer->id); + close(vector_fd); + + return; + } + + trace_ivshmem_flat_add_vector_success(peer->vector_counter, + vector_fd, peer->id); + + /* + * Set vector ID and its associated eventfd notifier and add them to the + * peer. + */ + peer->vector[peer->vector_counter].id = peer->vector_counter; + g_unix_set_fd_nonblocking(vector_fd, true, NULL); + event_notifier_init_fd(&peer->vector[peer->vector_counter].event_notifier, + vector_fd); + + /* + * If it's the device's own ID, register also the handler for the eventfd + * so the device can be notified by the other peers. + */ + if (peer == &s->own) { + qemu_set_fd_handler(vector_fd, ivshmem_flat_irq_handler, NULL, + &peer->vector); + } + + peer->vector_counter++; +} + +static void ivshmem_flat_process_msg(IvshmemFTState *s, uint64_t msg, int fd) +{ + uint16_t peer_id; + IvshmemPeer *peer; + + peer_id = msg & 0xFFFF; + peer = ivshmem_flat_find_peer(s, peer_id); + + if (!peer) { + peer = ivshmem_flat_add_peer(s, peer_id); + } + + if (fd >= 0) { + ivshmem_flat_add_vector(s, peer, fd); + } else { /* fd == -1, which is received when peers disconnect. */ + ivshmem_flat_remove_peer(s, peer_id); + } +} + +static int ivshmem_flat_can_receive_data(void *opaque) +{ + IvshmemFTState *s = opaque; + + assert(s->msg_buffered_bytes < sizeof(s->msg_buf)); + return sizeof(s->msg_buf) - s->msg_buffered_bytes; +} + +static void ivshmem_flat_read_msg(void *opaque, const uint8_t *buf, int size) +{ + IvshmemFTState *s = opaque; + int fd; + int64_t msg; + + assert(size >= 0 && s->msg_buffered_bytes + size <= sizeof(s->msg_buf)); + memcpy((unsigned char *)&s->msg_buf + s->msg_buffered_bytes, buf, size); + s->msg_buffered_bytes += size; + if (s->msg_buffered_bytes < sizeof(s->msg_buf)) { + return; + } + msg = le64_to_cpu(s->msg_buf); + s->msg_buffered_bytes = 0; + + fd = qemu_chr_fe_get_msgfd(&s->server_chr); + + ivshmem_flat_process_msg(s, msg, fd); +} + +static uint64_t ivshmem_flat_iomem_read(void *opaque, + hwaddr offset, unsigned size) +{ + IvshmemFTState *s = opaque; + uint32_t ret; + + trace_ivshmem_flat_read_mmr(offset); + + switch (offset) { + case INTMASK: + ret = 0; /* Ignore read since all bits are reserved in rev 1. */ + break; + case INTSTATUS: + ret = 0; /* Ignore read since all bits are reserved in rev 1. */ + break; + case IVPOSITION: + ret = s->own.id; + break; + case DOORBELL: + trace_ivshmem_flat_read_mmr_doorbell(); /* DOORBELL is write-only */ + ret = 0; + break; + default: + /* Should never reach out here due to iomem map range being exact */ + trace_ivshmem_flat_read_write_mmr_invalid(offset); + ret = 0; + } + + return ret; +} + +static int ivshmem_flat_interrupt_peer(IvshmemFTState *s, + uint16_t peer_id, uint16_t vector_id) +{ + IvshmemPeer *peer; + + peer = ivshmem_flat_find_peer(s, peer_id); + if (!peer) { + trace_ivshmem_flat_interrupt_invalid_peer(peer_id); + return 1; + } + + event_notifier_set(&(peer->vector[vector_id].event_notifier)); + + return 0; +} + +static void ivshmem_flat_iomem_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IvshmemFTState *s = opaque; + uint16_t peer_id = (value >> 16) & 0xFFFF; + uint16_t vector_id = value & 0xFFFF; + + trace_ivshmem_flat_write_mmr(offset); + + switch (offset) { + case INTMASK: + break; + case INTSTATUS: + break; + case IVPOSITION: + break; + case DOORBELL: + trace_ivshmem_flat_interrupt_peer(peer_id, vector_id); + ivshmem_flat_interrupt_peer(s, peer_id, vector_id); + break; + default: + /* Should never reach out here due to iomem map range being exact. */ + trace_ivshmem_flat_read_write_mmr_invalid(offset); + break; + } +} + +static const MemoryRegionOps ivshmem_flat_ops = { + .read = ivshmem_flat_iomem_read, + .write = ivshmem_flat_iomem_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { /* Read/write aligned at 32 bits. */ + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void ivshmem_flat_instance_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + IvshmemFTState *s = IVSHMEM_FLAT(obj); + + /* + * Init mem region for 4 MMRs (ivshmem_registers), + * 32 bits each => 16 bytes (0x10). + */ + memory_region_init_io(&s->iomem, obj, &ivshmem_flat_ops, s, + "ivshmem-mmio", 0x10); + sysbus_init_mmio(sbd, &s->iomem); + + /* + * Create one output IRQ that will be connect to the + * machine's interrupt controller. + */ + sysbus_init_irq(sbd, &s->irq); + + QTAILQ_INIT(&s->peer); +} + +static bool ivshmem_flat_connect_server(DeviceState *dev, Error **errp) +{ + IvshmemFTState *s = IVSHMEM_FLAT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + int64_t protocol_version, msg; + int shmem_fd; + uint16_t peer_id; + struct stat fdstat; + + /* Check ivshmem server connection. */ + if (!qemu_chr_fe_backend_connected(&s->server_chr)) { + error_setg(errp, "ivshmem server socket not specified or incorret." + " Can't create device."); + return false; + } + + /* + * Message sequence from server on new connection: + * _____________________________________ + * |STEP| uint64_t msg | int fd | + * ------------------------------------- + * + * 0 PROTOCOL -1 \ + * 1 OWN PEER ID -1 |-- Header/Greeting + * 2 -1 shmem fd / + * + * 3 PEER IDx Other peer's Vector 0 eventfd + * 4 PEER IDx Other peer's Vector 1 eventfd + * . . + * . . + * . . + * N PEER IDy Other peer's Vector 0 eventfd + * N+1 PEER IDy Other peer's Vector 1 eventfd + * . . + * . . + * . . + * + * ivshmem_flat_recv_msg() calls return 'msg' and 'fd'. + * + * See ./docs/specs/ivshmem-spec.txt for details on the protocol. + */ + + /* Step 0 */ + protocol_version = ivshmem_flat_recv_msg(s, NULL); + + /* Step 1 */ + msg = ivshmem_flat_recv_msg(s, NULL); + peer_id = 0xFFFF & msg; + s->own.id = peer_id; + s->own.vector_counter = 0; + + trace_ivshmem_flat_proto_ver_own_id(protocol_version, s->own.id); + + /* Step 2 */ + msg = ivshmem_flat_recv_msg(s, &shmem_fd); + /* Map shmem fd and MMRs into memory regions. */ + if (msg != -1 || shmem_fd < 0) { + error_setg(errp, "Could not receive valid shmem fd." + " Can't create device!"); + return false; + } + + if (fstat(shmem_fd, &fdstat) != 0) { + error_setg(errp, "Could not determine shmem fd size." + " Can't create device!"); + return false; + } + trace_ivshmem_flat_shmem_size(shmem_fd, fdstat.st_size); + + /* + * Shmem size provided by the ivshmem server must be equal to + * device's shmem size. + */ + if (fdstat.st_size != s->shmem_size) { + error_setg(errp, "Can't map shmem fd: shmem size different" + " from device size!"); + return false; + } + + /* + * Beyond step 2 ivshmem_process_msg, called by ivshmem_flat_read_msg + * handler -- when data is available on the server socket -- will handle + * the additional messages that will be generated by the server as peers + * connect or disconnect. + */ + qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_flat_can_receive_data, + ivshmem_flat_read_msg, NULL, NULL, s, NULL, true); + + memory_region_init_ram_from_fd(&s->shmem, OBJECT(s), + "ivshmem-shmem", s->shmem_size, + RAM_SHARED, shmem_fd, 0, NULL); + sysbus_init_mmio(sbd, &s->shmem); + + return true; +} + +static void ivshmem_flat_realize(DeviceState *dev, Error **errp) +{ + if (!ivshmem_flat_connect_server(dev, errp)) { + return; + } +} + +static const Property ivshmem_flat_props[] = { + DEFINE_PROP_CHR("chardev", IvshmemFTState, server_chr), + DEFINE_PROP_UINT32("shmem-size", IvshmemFTState, shmem_size, 4 * MiB), +}; + +static void ivshmem_flat_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->hotpluggable = true; + dc->realize = ivshmem_flat_realize; + + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, ivshmem_flat_props); + + /* Reason: Must be wired up in code (sysbus MRs and IRQ) */ + dc->user_creatable = false; +} + +static const TypeInfo ivshmem_flat_types[] = { + { + .name = TYPE_IVSHMEM_FLAT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IvshmemFTState), + .instance_init = ivshmem_flat_instance_init, + .class_init = ivshmem_flat_class_init, + }, +}; + +DEFINE_TYPES(ivshmem_flat_types) diff --git a/hw/misc/ivshmem-pci.c b/hw/misc/ivshmem-pci.c new file mode 100644 index 00000000000..5a10bca633d --- /dev/null +++ b/hw/misc/ivshmem-pci.c @@ -0,0 +1,1131 @@ +/* + * Inter-VM Shared Memory PCI device. + * + * Author: + * Cam Macdonell + * + * Based On: cirrus_vga.c + * Copyright (c) 2004 Fabrice Bellard + * Copyright (c) 2004 Makoto Suzuki (suzu) + * + * and rtl8139.c + * Copyright (c) 2006 Igor Kovalenko + * + * 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/units.h" +#include "qapi/error.h" +#include "qemu/cutils.h" +#include "hw/pci/pci.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" +#include "system/kvm.h" +#include "migration/blocker.h" +#include "migration/vmstate.h" +#include "qemu/error-report.h" +#include "qemu/event_notifier.h" +#include "qemu/module.h" +#include "qom/object_interfaces.h" +#include "chardev/char-fe.h" +#include "system/hostmem.h" +#include "qapi/visitor.h" + +#include "hw/misc/ivshmem.h" +#include "qom/object.h" + +#define PCI_VENDOR_ID_IVSHMEM PCI_VENDOR_ID_REDHAT_QUMRANET +#define PCI_DEVICE_ID_IVSHMEM 0x1110 + +#define IVSHMEM_MAX_PEERS UINT16_MAX +#define IVSHMEM_IOEVENTFD 0 +#define IVSHMEM_MSI 1 + +#define IVSHMEM_REG_BAR_SIZE 0x100 + +#define IVSHMEM_DEBUG 0 +#define IVSHMEM_DPRINTF(fmt, ...) \ + do { \ + if (IVSHMEM_DEBUG) { \ + printf("IVSHMEM: " fmt, ## __VA_ARGS__); \ + } \ + } while (0) + +#define TYPE_IVSHMEM_COMMON "ivshmem-common" +typedef struct IVShmemState IVShmemState; +DECLARE_INSTANCE_CHECKER(IVShmemState, IVSHMEM_COMMON, + TYPE_IVSHMEM_COMMON) + +#define TYPE_IVSHMEM_PLAIN "ivshmem-plain" +DECLARE_INSTANCE_CHECKER(IVShmemState, IVSHMEM_PLAIN, + TYPE_IVSHMEM_PLAIN) + +#define TYPE_IVSHMEM_DOORBELL "ivshmem-doorbell" +DECLARE_INSTANCE_CHECKER(IVShmemState, IVSHMEM_DOORBELL, + TYPE_IVSHMEM_DOORBELL) + +#define TYPE_IVSHMEM "ivshmem" +DECLARE_INSTANCE_CHECKER(IVShmemState, IVSHMEM, + TYPE_IVSHMEM) + +typedef struct Peer { + int nb_eventfds; + EventNotifier *eventfds; +} Peer; + +typedef struct MSIVector { + PCIDevice *pdev; + int virq; + bool unmasked; +} MSIVector; + +struct IVShmemState { + /*< private >*/ + PCIDevice parent_obj; + /*< public >*/ + + uint32_t features; + + /* exactly one of these two may be set */ + HostMemoryBackend *hostmem; /* with interrupts */ + CharBackend server_chr; /* without interrupts */ + + /* registers */ + uint32_t intrmask; + uint32_t intrstatus; + int vm_id; + + /* BARs */ + MemoryRegion ivshmem_mmio; /* BAR 0 (registers) */ + MemoryRegion *ivshmem_bar2; /* BAR 2 (shared memory) */ + MemoryRegion server_bar2; /* used with server_chr */ + + /* interrupt support */ + Peer *peers; + int nb_peers; /* space in @peers[] */ + uint32_t vectors; + MSIVector *msi_vectors; + uint64_t msg_buf; /* buffer for receiving server messages */ + int msg_buffered_bytes; /* #bytes in @msg_buf */ + + /* migration stuff */ + OnOffAuto master; + Error *migration_blocker; +}; + +/* registers for the Inter-VM shared memory device */ +enum ivshmem_registers { + INTRMASK = 0, + INTRSTATUS = 4, + IVPOSITION = 8, + DOORBELL = 12, +}; + +static inline uint32_t ivshmem_has_feature(IVShmemState *ivs, + unsigned int feature) { + return (ivs->features & (1 << feature)); +} + +static inline bool ivshmem_is_master(IVShmemState *s) +{ + assert(s->master != ON_OFF_AUTO_AUTO); + return s->master == ON_OFF_AUTO_ON; +} + +static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val) +{ + IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val); + + s->intrmask = val; +} + +static uint32_t ivshmem_IntrMask_read(IVShmemState *s) +{ + uint32_t ret = s->intrmask; + + IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret); + return ret; +} + +static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val) +{ + IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val); + + s->intrstatus = val; +} + +static uint32_t ivshmem_IntrStatus_read(IVShmemState *s) +{ + uint32_t ret = s->intrstatus; + + /* reading ISR clears all interrupts */ + s->intrstatus = 0; + return ret; +} + +static void ivshmem_io_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + IVShmemState *s = opaque; + + uint16_t dest = val >> 16; + uint16_t vector = val & 0xff; + + addr &= 0xfc; + + IVSHMEM_DPRINTF("writing to addr " HWADDR_FMT_plx "\n", addr); + switch (addr) + { + case INTRMASK: + ivshmem_IntrMask_write(s, val); + break; + + case INTRSTATUS: + ivshmem_IntrStatus_write(s, val); + break; + + case DOORBELL: + /* check that dest VM ID is reasonable */ + if (dest >= s->nb_peers) { + IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest); + break; + } + + /* check doorbell range */ + if (vector < s->peers[dest].nb_eventfds) { + IVSHMEM_DPRINTF("Notifying VM %d on vector %d\n", dest, vector); + event_notifier_set(&s->peers[dest].eventfds[vector]); + } else { + IVSHMEM_DPRINTF("Invalid destination vector %d on VM %d\n", + vector, dest); + } + break; + default: + IVSHMEM_DPRINTF("Unhandled write " HWADDR_FMT_plx "\n", addr); + } +} + +static uint64_t ivshmem_io_read(void *opaque, hwaddr addr, + unsigned size) +{ + + IVShmemState *s = opaque; + uint32_t ret; + + switch (addr) + { + case INTRMASK: + ret = ivshmem_IntrMask_read(s); + break; + + case INTRSTATUS: + ret = ivshmem_IntrStatus_read(s); + break; + + case IVPOSITION: + ret = s->vm_id; + break; + + default: + IVSHMEM_DPRINTF("why are we reading " HWADDR_FMT_plx "\n", addr); + ret = 0; + } + + return ret; +} + +static const MemoryRegionOps ivshmem_mmio_ops = { + .read = ivshmem_io_read, + .write = ivshmem_io_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void ivshmem_vector_notify(void *opaque) +{ + MSIVector *entry = opaque; + PCIDevice *pdev = entry->pdev; + IVShmemState *s = IVSHMEM_COMMON(pdev); + int vector = entry - s->msi_vectors; + EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; + + if (!event_notifier_test_and_clear(n)) { + return; + } + + IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, vector); + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + if (msix_enabled(pdev)) { + msix_notify(pdev, vector); + } + } else { + ivshmem_IntrStatus_write(s, 1); + } +} + +static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector, + MSIMessage msg) +{ + IVShmemState *s = IVSHMEM_COMMON(dev); + EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; + MSIVector *v = &s->msi_vectors[vector]; + int ret; + + IVSHMEM_DPRINTF("vector unmask %p %d\n", dev, vector); + if (!v->pdev) { + error_report("ivshmem: vector %d route does not exist", vector); + return -EINVAL; + } + assert(!v->unmasked); + + ret = kvm_irqchip_update_msi_route(kvm_state, v->virq, msg, dev); + if (ret < 0) { + return ret; + } + kvm_irqchip_commit_routes(kvm_state); + + ret = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq); + if (ret < 0) { + return ret; + } + v->unmasked = true; + + return 0; +} + +static void ivshmem_vector_mask(PCIDevice *dev, unsigned vector) +{ + IVShmemState *s = IVSHMEM_COMMON(dev); + EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; + MSIVector *v = &s->msi_vectors[vector]; + int ret; + + IVSHMEM_DPRINTF("vector mask %p %d\n", dev, vector); + if (!v->pdev) { + error_report("ivshmem: vector %d route does not exist", vector); + return; + } + assert(v->unmasked); + + ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, v->virq); + if (ret < 0) { + error_report("remove_irqfd_notifier_gsi failed"); + return; + } + v->unmasked = false; +} + +static void ivshmem_vector_poll(PCIDevice *dev, + unsigned int vector_start, + unsigned int vector_end) +{ + IVShmemState *s = IVSHMEM_COMMON(dev); + unsigned int vector; + + IVSHMEM_DPRINTF("vector poll %p %d-%d\n", dev, vector_start, vector_end); + + vector_end = MIN(vector_end, s->vectors); + + for (vector = vector_start; vector < vector_end; vector++) { + EventNotifier *notifier = &s->peers[s->vm_id].eventfds[vector]; + + if (!msix_is_masked(dev, vector)) { + continue; + } + + if (event_notifier_test_and_clear(notifier)) { + msix_set_pending(dev, vector); + } + } +} + +static void watch_vector_notifier(IVShmemState *s, EventNotifier *n, + int vector) +{ + int eventfd = event_notifier_get_fd(n); + + assert(!s->msi_vectors[vector].pdev); + s->msi_vectors[vector].pdev = PCI_DEVICE(s); + + qemu_set_fd_handler(eventfd, ivshmem_vector_notify, + NULL, &s->msi_vectors[vector]); +} + +static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i) +{ + memory_region_add_eventfd(&s->ivshmem_mmio, + DOORBELL, + 4, + true, + (posn << 16) | i, + &s->peers[posn].eventfds[i]); +} + +static void ivshmem_del_eventfd(IVShmemState *s, int posn, int i) +{ + memory_region_del_eventfd(&s->ivshmem_mmio, + DOORBELL, + 4, + true, + (posn << 16) | i, + &s->peers[posn].eventfds[i]); +} + +static void close_peer_eventfds(IVShmemState *s, int posn) +{ + int i, n; + + assert(posn >= 0 && posn < s->nb_peers); + n = s->peers[posn].nb_eventfds; + + if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { + memory_region_transaction_begin(); + for (i = 0; i < n; i++) { + ivshmem_del_eventfd(s, posn, i); + } + memory_region_transaction_commit(); + } + + for (i = 0; i < n; i++) { + event_notifier_cleanup(&s->peers[posn].eventfds[i]); + } + + g_free(s->peers[posn].eventfds); + s->peers[posn].nb_eventfds = 0; +} + +static void resize_peers(IVShmemState *s, int nb_peers) +{ + int old_nb_peers = s->nb_peers; + int i; + + assert(nb_peers > old_nb_peers); + IVSHMEM_DPRINTF("bumping storage to %d peers\n", nb_peers); + + s->peers = g_renew(Peer, s->peers, nb_peers); + s->nb_peers = nb_peers; + + for (i = old_nb_peers; i < nb_peers; i++) { + s->peers[i].eventfds = g_new0(EventNotifier, s->vectors); + s->peers[i].nb_eventfds = 0; + } +} + +static void ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector, + Error **errp) +{ + PCIDevice *pdev = PCI_DEVICE(s); + KVMRouteChange c; + int ret; + + IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector); + assert(!s->msi_vectors[vector].pdev); + + c = kvm_irqchip_begin_route_changes(kvm_state); + ret = kvm_irqchip_add_msi_route(&c, vector, pdev); + if (ret < 0) { + error_setg(errp, "kvm_irqchip_add_msi_route failed"); + return; + } + kvm_irqchip_commit_route_changes(&c); + + s->msi_vectors[vector].virq = ret; + s->msi_vectors[vector].pdev = pdev; +} + +static void setup_interrupt(IVShmemState *s, int vector, Error **errp) +{ + EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; + bool with_irqfd = kvm_msi_via_irqfd_enabled() && + ivshmem_has_feature(s, IVSHMEM_MSI); + PCIDevice *pdev = PCI_DEVICE(s); + Error *err = NULL; + + IVSHMEM_DPRINTF("setting up interrupt for vector: %d\n", vector); + + if (!with_irqfd) { + IVSHMEM_DPRINTF("with eventfd\n"); + watch_vector_notifier(s, n, vector); + } else if (msix_enabled(pdev)) { + IVSHMEM_DPRINTF("with irqfd\n"); + ivshmem_add_kvm_msi_virq(s, vector, &err); + if (err) { + error_propagate(errp, err); + return; + } + + if (!msix_is_masked(pdev, vector)) { + kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, + s->msi_vectors[vector].virq); + /* TODO handle error */ + } + } else { + /* it will be delayed until msix is enabled, in write_config */ + IVSHMEM_DPRINTF("with irqfd, delayed until msix enabled\n"); + } +} + +static void process_msg_shmem(IVShmemState *s, int fd, Error **errp) +{ + struct stat buf; + size_t size; + + if (s->ivshmem_bar2) { + error_setg(errp, "server sent unexpected shared memory message"); + close(fd); + return; + } + + if (fstat(fd, &buf) < 0) { + error_setg_errno(errp, errno, + "can't determine size of shared memory sent by server"); + close(fd); + return; + } + + size = buf.st_size; + + /* mmap the region and map into the BAR2 */ + if (!memory_region_init_ram_from_fd(&s->server_bar2, OBJECT(s), + "ivshmem.bar2", size, RAM_SHARED, + fd, 0, errp)) { + return; + } + + s->ivshmem_bar2 = &s->server_bar2; +} + +static void process_msg_disconnect(IVShmemState *s, uint16_t posn, + Error **errp) +{ + IVSHMEM_DPRINTF("posn %d has gone away\n", posn); + if (posn >= s->nb_peers || posn == s->vm_id) { + error_setg(errp, "invalid peer %d", posn); + return; + } + close_peer_eventfds(s, posn); +} + +static void process_msg_connect(IVShmemState *s, uint16_t posn, int fd, + Error **errp) +{ + Peer *peer = &s->peers[posn]; + int vector; + + /* + * The N-th connect message for this peer comes with the file + * descriptor for vector N-1. Count messages to find the vector. + */ + if (peer->nb_eventfds >= s->vectors) { + error_setg(errp, "Too many eventfd received, device has %d vectors", + s->vectors); + close(fd); + return; + } + vector = peer->nb_eventfds++; + + IVSHMEM_DPRINTF("eventfds[%d][%d] = %d\n", posn, vector, fd); + event_notifier_init_fd(&peer->eventfds[vector], fd); + g_unix_set_fd_nonblocking(fd, true, NULL); /* msix/irqfd poll non block */ + + if (posn == s->vm_id) { + setup_interrupt(s, vector, errp); + /* TODO do we need to handle the error? */ + } + + if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { + ivshmem_add_eventfd(s, posn, vector); + } +} + +static void process_msg(IVShmemState *s, int64_t msg, int fd, Error **errp) +{ + IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", msg, fd); + + if (msg < -1 || msg > IVSHMEM_MAX_PEERS) { + error_setg(errp, "server sent invalid message %" PRId64, msg); + close(fd); + return; + } + + if (msg == -1) { + process_msg_shmem(s, fd, errp); + return; + } + + if (msg >= s->nb_peers) { + resize_peers(s, msg + 1); + } + + if (fd >= 0) { + process_msg_connect(s, msg, fd, errp); + } else { + process_msg_disconnect(s, msg, errp); + } +} + +static int ivshmem_can_receive(void *opaque) +{ + IVShmemState *s = opaque; + + assert(s->msg_buffered_bytes < sizeof(s->msg_buf)); + return sizeof(s->msg_buf) - s->msg_buffered_bytes; +} + +static void ivshmem_read(void *opaque, const uint8_t *buf, int size) +{ + IVShmemState *s = opaque; + Error *err = NULL; + int fd; + int64_t msg; + + assert(size >= 0 && s->msg_buffered_bytes + size <= sizeof(s->msg_buf)); + memcpy((unsigned char *)&s->msg_buf + s->msg_buffered_bytes, buf, size); + s->msg_buffered_bytes += size; + if (s->msg_buffered_bytes < sizeof(s->msg_buf)) { + return; + } + msg = le64_to_cpu(s->msg_buf); + s->msg_buffered_bytes = 0; + + fd = qemu_chr_fe_get_msgfd(&s->server_chr); + + process_msg(s, msg, fd, &err); + if (err) { + error_report_err(err); + } +} + +static int64_t ivshmem_recv_msg(IVShmemState *s, int *pfd, Error **errp) +{ + int64_t msg; + int n, ret; + + n = 0; + do { + ret = qemu_chr_fe_read_all(&s->server_chr, (uint8_t *)&msg + n, + sizeof(msg) - n); + if (ret < 0) { + if (ret == -EINTR) { + continue; + } + error_setg_errno(errp, -ret, "read from server failed"); + return INT64_MIN; + } + n += ret; + } while (n < sizeof(msg)); + + *pfd = qemu_chr_fe_get_msgfd(&s->server_chr); + return le64_to_cpu(msg); +} + +static void ivshmem_recv_setup(IVShmemState *s, Error **errp) +{ + Error *err = NULL; + int64_t msg; + int fd; + + msg = ivshmem_recv_msg(s, &fd, &err); + if (err) { + error_propagate(errp, err); + return; + } + if (msg != IVSHMEM_PROTOCOL_VERSION) { + error_setg(errp, "server sent version %" PRId64 ", expecting %d", + msg, IVSHMEM_PROTOCOL_VERSION); + return; + } + if (fd != -1) { + error_setg(errp, "server sent invalid version message"); + return; + } + + /* + * ivshmem-server sends the remaining initial messages in a fixed + * order, but the device has always accepted them in any order. + * Stay as compatible as practical, just in case people use + * servers that behave differently. + */ + + /* + * ivshmem_device_spec.txt has always required the ID message + * right here, and ivshmem-server has always complied. However, + * older versions of the device accepted it out of order, but + * broke when an interrupt setup message arrived before it. + */ + msg = ivshmem_recv_msg(s, &fd, &err); + if (err) { + error_propagate(errp, err); + return; + } + if (fd != -1 || msg < 0 || msg > IVSHMEM_MAX_PEERS) { + error_setg(errp, "server sent invalid ID message"); + return; + } + s->vm_id = msg; + + /* + * Receive more messages until we got shared memory. + */ + do { + msg = ivshmem_recv_msg(s, &fd, &err); + if (err) { + error_propagate(errp, err); + return; + } + process_msg(s, msg, fd, &err); + if (err) { + error_propagate(errp, err); + return; + } + } while (msg != -1); + + /* + * This function must either map the shared memory or fail. The + * loop above ensures that: it terminates normally only after it + * successfully processed the server's shared memory message. + * Assert that actually mapped the shared memory: + */ + assert(s->ivshmem_bar2); +} + +/* Select the MSI-X vectors used by device. + * ivshmem maps events to vectors statically, so + * we just enable all vectors on init and after reset. */ +static void ivshmem_msix_vector_use(IVShmemState *s) +{ + PCIDevice *d = PCI_DEVICE(s); + int i; + + for (i = 0; i < s->vectors; i++) { + msix_vector_use(d, i); + } +} + +static void ivshmem_disable_irqfd(IVShmemState *s); + +static void ivshmem_reset(DeviceState *d) +{ + IVShmemState *s = IVSHMEM_COMMON(d); + + ivshmem_disable_irqfd(s); + + s->intrstatus = 0; + s->intrmask = 0; + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + ivshmem_msix_vector_use(s); + } +} + +static int ivshmem_setup_interrupts(IVShmemState *s, Error **errp) +{ + /* allocate QEMU callback data for receiving interrupts */ + s->msi_vectors = g_new0(MSIVector, s->vectors); + + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1, errp)) { + return -1; + } + + IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors); + ivshmem_msix_vector_use(s); + } + + return 0; +} + +static void ivshmem_remove_kvm_msi_virq(IVShmemState *s, int vector) +{ + IVSHMEM_DPRINTF("ivshmem_remove_kvm_msi_virq vector:%d\n", vector); + + if (s->msi_vectors[vector].pdev == NULL) { + return; + } + + /* it was cleaned when masked in the frontend. */ + kvm_irqchip_release_virq(kvm_state, s->msi_vectors[vector].virq); + + s->msi_vectors[vector].pdev = NULL; +} + +static void ivshmem_enable_irqfd(IVShmemState *s) +{ + PCIDevice *pdev = PCI_DEVICE(s); + int i; + + for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) { + Error *err = NULL; + + ivshmem_add_kvm_msi_virq(s, i, &err); + if (err) { + error_report_err(err); + goto undo; + } + } + + if (msix_set_vector_notifiers(pdev, + ivshmem_vector_unmask, + ivshmem_vector_mask, + ivshmem_vector_poll)) { + error_report("ivshmem: msix_set_vector_notifiers failed"); + goto undo; + } + return; + +undo: + while (--i >= 0) { + ivshmem_remove_kvm_msi_virq(s, i); + } +} + +static void ivshmem_disable_irqfd(IVShmemState *s) +{ + PCIDevice *pdev = PCI_DEVICE(s); + int i; + + if (!pdev->msix_vector_use_notifier) { + return; + } + + msix_unset_vector_notifiers(pdev); + + for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) { + /* + * MSI-X is already disabled here so msix_unset_vector_notifiers() + * didn't call our release notifier. Do it now to keep our masks and + * unmasks balanced. + */ + if (s->msi_vectors[i].unmasked) { + ivshmem_vector_mask(pdev, i); + } + ivshmem_remove_kvm_msi_virq(s, i); + } + +} + +static void ivshmem_write_config(PCIDevice *pdev, uint32_t address, + uint32_t val, int len) +{ + IVShmemState *s = IVSHMEM_COMMON(pdev); + int is_enabled, was_enabled = msix_enabled(pdev); + + pci_default_write_config(pdev, address, val, len); + is_enabled = msix_enabled(pdev); + + if (kvm_msi_via_irqfd_enabled()) { + if (!was_enabled && is_enabled) { + ivshmem_enable_irqfd(s); + } else if (was_enabled && !is_enabled) { + ivshmem_disable_irqfd(s); + } + } +} + +static void ivshmem_common_realize(PCIDevice *dev, Error **errp) +{ + ERRP_GUARD(); + IVShmemState *s = IVSHMEM_COMMON(dev); + Error *err = NULL; + uint8_t *pci_conf; + + /* IRQFD requires MSI */ + if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && + !ivshmem_has_feature(s, IVSHMEM_MSI)) { + error_setg(errp, "ioeventfd/irqfd requires MSI"); + return; + } + + pci_conf = dev->config; + pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + + memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s, + "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE); + + /* region for registers*/ + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, + &s->ivshmem_mmio); + + if (s->hostmem != NULL) { + IVSHMEM_DPRINTF("using hostmem\n"); + + s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem); + host_memory_backend_set_mapped(s->hostmem, true); + } else { + Chardev *chr = qemu_chr_fe_get_driver(&s->server_chr); + assert(chr); + + IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", + chr->filename); + + /* we allocate enough space for 16 peers and grow as needed */ + resize_peers(s, 16); + + /* + * Receive setup messages from server synchronously. + * Older versions did it asynchronously, but that creates a + * number of entertaining race conditions. + */ + ivshmem_recv_setup(s, &err); + if (err) { + error_propagate(errp, err); + return; + } + + if (s->master == ON_OFF_AUTO_ON && s->vm_id != 0) { + error_setg(errp, + "master must connect to the server before any peers"); + return; + } + + qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_can_receive, + ivshmem_read, NULL, NULL, s, NULL, true); + + if (ivshmem_setup_interrupts(s, errp) < 0) { + error_prepend(errp, "Failed to initialize interrupts: "); + return; + } + } + + if (s->master == ON_OFF_AUTO_AUTO) { + s->master = s->vm_id == 0 ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + } + + 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) { + return; + } + } + + vmstate_register_ram(s->ivshmem_bar2, DEVICE(s)); + pci_register_bar(PCI_DEVICE(s), 2, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_PREFETCH | + PCI_BASE_ADDRESS_MEM_TYPE_64, + s->ivshmem_bar2); +} + +static void ivshmem_exit(PCIDevice *dev) +{ + IVShmemState *s = IVSHMEM_COMMON(dev); + int i; + + migrate_del_blocker(&s->migration_blocker); + + if (memory_region_is_mapped(s->ivshmem_bar2)) { + if (!s->hostmem) { + void *addr = memory_region_get_ram_ptr(s->ivshmem_bar2); + int fd; + + if (munmap(addr, memory_region_size(s->ivshmem_bar2) == -1)) { + error_report("Failed to munmap shared memory %s", + strerror(errno)); + } + + fd = memory_region_get_fd(s->ivshmem_bar2); + close(fd); + } + + vmstate_unregister_ram(s->ivshmem_bar2, DEVICE(dev)); + } + + if (s->hostmem) { + host_memory_backend_set_mapped(s->hostmem, false); + } + + if (s->peers) { + for (i = 0; i < s->nb_peers; i++) { + close_peer_eventfds(s, i); + } + g_free(s->peers); + } + + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + msix_uninit_exclusive_bar(dev); + } + + g_free(s->msi_vectors); +} + +static int ivshmem_pre_load(void *opaque) +{ + IVShmemState *s = opaque; + + if (!ivshmem_is_master(s)) { + error_report("'peer' devices are not migratable"); + return -EINVAL; + } + + return 0; +} + +static int ivshmem_post_load(void *opaque, int version_id) +{ + IVShmemState *s = opaque; + + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + ivshmem_msix_vector_use(s); + } + return 0; +} + +static void ivshmem_common_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->realize = ivshmem_common_realize; + k->exit = ivshmem_exit; + k->config_write = ivshmem_write_config; + k->vendor_id = PCI_VENDOR_ID_IVSHMEM; + k->device_id = PCI_DEVICE_ID_IVSHMEM; + k->class_id = PCI_CLASS_MEMORY_RAM; + k->revision = 1; + device_class_set_legacy_reset(dc, ivshmem_reset); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->desc = "Inter-VM shared memory"; +} + +static const TypeInfo ivshmem_common_info = { + .name = TYPE_IVSHMEM_COMMON, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(IVShmemState), + .abstract = true, + .class_init = ivshmem_common_class_init, + .interfaces = (const InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + +static const VMStateDescription ivshmem_plain_vmsd = { + .name = TYPE_IVSHMEM_PLAIN, + .version_id = 0, + .minimum_version_id = 0, + .pre_load = ivshmem_pre_load, + .post_load = ivshmem_post_load, + .fields = (const VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, IVShmemState), + VMSTATE_UINT32(intrstatus, IVShmemState), + VMSTATE_UINT32(intrmask, IVShmemState), + VMSTATE_END_OF_LIST() + }, +}; + +static const Property ivshmem_plain_properties[] = { + DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), + DEFINE_PROP_LINK("memdev", IVShmemState, hostmem, TYPE_MEMORY_BACKEND, + HostMemoryBackend *), +}; + +static void ivshmem_plain_realize(PCIDevice *dev, Error **errp) +{ + IVShmemState *s = IVSHMEM_COMMON(dev); + + if (!s->hostmem) { + error_setg(errp, "You must specify a 'memdev'"); + return; + } else if (host_memory_backend_is_mapped(s->hostmem)) { + error_setg(errp, "can't use already busy memdev: %s", + object_get_canonical_path_component(OBJECT(s->hostmem))); + return; + } + + ivshmem_common_realize(dev, errp); +} + +static void ivshmem_plain_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->realize = ivshmem_plain_realize; + device_class_set_props(dc, ivshmem_plain_properties); + dc->vmsd = &ivshmem_plain_vmsd; +} + +static const TypeInfo ivshmem_plain_info = { + .name = TYPE_IVSHMEM_PLAIN, + .parent = TYPE_IVSHMEM_COMMON, + .instance_size = sizeof(IVShmemState), + .class_init = ivshmem_plain_class_init, +}; + +static const VMStateDescription ivshmem_doorbell_vmsd = { + .name = TYPE_IVSHMEM_DOORBELL, + .version_id = 0, + .minimum_version_id = 0, + .pre_load = ivshmem_pre_load, + .post_load = ivshmem_post_load, + .fields = (const VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, IVShmemState), + VMSTATE_MSIX(parent_obj, IVShmemState), + VMSTATE_UINT32(intrstatus, IVShmemState), + VMSTATE_UINT32(intrmask, IVShmemState), + VMSTATE_END_OF_LIST() + }, +}; + +static const Property ivshmem_doorbell_properties[] = { + DEFINE_PROP_CHR("chardev", IVShmemState, server_chr), + DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1), + DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, + true), + DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), +}; + +static void ivshmem_doorbell_init(Object *obj) +{ + IVShmemState *s = IVSHMEM_DOORBELL(obj); + + s->features |= (1 << IVSHMEM_MSI); +} + +static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp) +{ + IVShmemState *s = IVSHMEM_COMMON(dev); + + if (!qemu_chr_fe_backend_connected(&s->server_chr)) { + error_setg(errp, "You must specify a 'chardev'"); + return; + } + + ivshmem_common_realize(dev, errp); +} + +static void ivshmem_doorbell_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->realize = ivshmem_doorbell_realize; + device_class_set_props(dc, ivshmem_doorbell_properties); + dc->vmsd = &ivshmem_doorbell_vmsd; +} + +static const TypeInfo ivshmem_doorbell_info = { + .name = TYPE_IVSHMEM_DOORBELL, + .parent = TYPE_IVSHMEM_COMMON, + .instance_size = sizeof(IVShmemState), + .instance_init = ivshmem_doorbell_init, + .class_init = ivshmem_doorbell_class_init, +}; + +static void ivshmem_register_types(void) +{ + type_register_static(&ivshmem_common_info); + type_register_static(&ivshmem_plain_info); + type_register_static(&ivshmem_doorbell_info); +} + +type_init(ivshmem_register_types) diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c deleted file mode 100644 index d66d9121722..00000000000 --- a/hw/misc/ivshmem.c +++ /dev/null @@ -1,1138 +0,0 @@ -/* - * Inter-VM Shared Memory PCI device. - * - * Author: - * Cam Macdonell - * - * Based On: cirrus_vga.c - * Copyright (c) 2004 Fabrice Bellard - * Copyright (c) 2004 Makoto Suzuki (suzu) - * - * and rtl8139.c - * Copyright (c) 2006 Igor Kovalenko - * - * 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/units.h" -#include "qapi/error.h" -#include "qemu/cutils.h" -#include "hw/pci/pci.h" -#include "hw/qdev-properties.h" -#include "hw/qdev-properties-system.h" -#include "hw/pci/msi.h" -#include "hw/pci/msix.h" -#include "sysemu/kvm.h" -#include "migration/blocker.h" -#include "migration/vmstate.h" -#include "qemu/error-report.h" -#include "qemu/event_notifier.h" -#include "qemu/module.h" -#include "qom/object_interfaces.h" -#include "chardev/char-fe.h" -#include "sysemu/hostmem.h" -#include "qapi/visitor.h" - -#include "hw/misc/ivshmem.h" -#include "qom/object.h" - -#define PCI_VENDOR_ID_IVSHMEM PCI_VENDOR_ID_REDHAT_QUMRANET -#define PCI_DEVICE_ID_IVSHMEM 0x1110 - -#define IVSHMEM_MAX_PEERS UINT16_MAX -#define IVSHMEM_IOEVENTFD 0 -#define IVSHMEM_MSI 1 - -#define IVSHMEM_REG_BAR_SIZE 0x100 - -#define IVSHMEM_DEBUG 0 -#define IVSHMEM_DPRINTF(fmt, ...) \ - do { \ - if (IVSHMEM_DEBUG) { \ - printf("IVSHMEM: " fmt, ## __VA_ARGS__); \ - } \ - } while (0) - -#define TYPE_IVSHMEM_COMMON "ivshmem-common" -typedef struct IVShmemState IVShmemState; -DECLARE_INSTANCE_CHECKER(IVShmemState, IVSHMEM_COMMON, - TYPE_IVSHMEM_COMMON) - -#define TYPE_IVSHMEM_PLAIN "ivshmem-plain" -DECLARE_INSTANCE_CHECKER(IVShmemState, IVSHMEM_PLAIN, - TYPE_IVSHMEM_PLAIN) - -#define TYPE_IVSHMEM_DOORBELL "ivshmem-doorbell" -DECLARE_INSTANCE_CHECKER(IVShmemState, IVSHMEM_DOORBELL, - TYPE_IVSHMEM_DOORBELL) - -#define TYPE_IVSHMEM "ivshmem" -DECLARE_INSTANCE_CHECKER(IVShmemState, IVSHMEM, - TYPE_IVSHMEM) - -typedef struct Peer { - int nb_eventfds; - EventNotifier *eventfds; -} Peer; - -typedef struct MSIVector { - PCIDevice *pdev; - int virq; - bool unmasked; -} MSIVector; - -struct IVShmemState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - uint32_t features; - - /* exactly one of these two may be set */ - HostMemoryBackend *hostmem; /* with interrupts */ - CharBackend server_chr; /* without interrupts */ - - /* registers */ - uint32_t intrmask; - uint32_t intrstatus; - int vm_id; - - /* BARs */ - MemoryRegion ivshmem_mmio; /* BAR 0 (registers) */ - MemoryRegion *ivshmem_bar2; /* BAR 2 (shared memory) */ - MemoryRegion server_bar2; /* used with server_chr */ - - /* interrupt support */ - Peer *peers; - int nb_peers; /* space in @peers[] */ - uint32_t vectors; - MSIVector *msi_vectors; - uint64_t msg_buf; /* buffer for receiving server messages */ - int msg_buffered_bytes; /* #bytes in @msg_buf */ - - /* migration stuff */ - OnOffAuto master; - Error *migration_blocker; -}; - -/* registers for the Inter-VM shared memory device */ -enum ivshmem_registers { - INTRMASK = 0, - INTRSTATUS = 4, - IVPOSITION = 8, - DOORBELL = 12, -}; - -static inline uint32_t ivshmem_has_feature(IVShmemState *ivs, - unsigned int feature) { - return (ivs->features & (1 << feature)); -} - -static inline bool ivshmem_is_master(IVShmemState *s) -{ - assert(s->master != ON_OFF_AUTO_AUTO); - return s->master == ON_OFF_AUTO_ON; -} - -static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val) -{ - IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val); - - s->intrmask = val; -} - -static uint32_t ivshmem_IntrMask_read(IVShmemState *s) -{ - uint32_t ret = s->intrmask; - - IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret); - return ret; -} - -static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val) -{ - IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val); - - s->intrstatus = val; -} - -static uint32_t ivshmem_IntrStatus_read(IVShmemState *s) -{ - uint32_t ret = s->intrstatus; - - /* reading ISR clears all interrupts */ - s->intrstatus = 0; - return ret; -} - -static void ivshmem_io_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - IVShmemState *s = opaque; - - uint16_t dest = val >> 16; - uint16_t vector = val & 0xff; - - addr &= 0xfc; - - IVSHMEM_DPRINTF("writing to addr " HWADDR_FMT_plx "\n", addr); - switch (addr) - { - case INTRMASK: - ivshmem_IntrMask_write(s, val); - break; - - case INTRSTATUS: - ivshmem_IntrStatus_write(s, val); - break; - - case DOORBELL: - /* check that dest VM ID is reasonable */ - if (dest >= s->nb_peers) { - IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest); - break; - } - - /* check doorbell range */ - if (vector < s->peers[dest].nb_eventfds) { - IVSHMEM_DPRINTF("Notifying VM %d on vector %d\n", dest, vector); - event_notifier_set(&s->peers[dest].eventfds[vector]); - } else { - IVSHMEM_DPRINTF("Invalid destination vector %d on VM %d\n", - vector, dest); - } - break; - default: - IVSHMEM_DPRINTF("Unhandled write " HWADDR_FMT_plx "\n", addr); - } -} - -static uint64_t ivshmem_io_read(void *opaque, hwaddr addr, - unsigned size) -{ - - IVShmemState *s = opaque; - uint32_t ret; - - switch (addr) - { - case INTRMASK: - ret = ivshmem_IntrMask_read(s); - break; - - case INTRSTATUS: - ret = ivshmem_IntrStatus_read(s); - break; - - case IVPOSITION: - ret = s->vm_id; - break; - - default: - IVSHMEM_DPRINTF("why are we reading " HWADDR_FMT_plx "\n", addr); - ret = 0; - } - - return ret; -} - -static const MemoryRegionOps ivshmem_mmio_ops = { - .read = ivshmem_io_read, - .write = ivshmem_io_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void ivshmem_vector_notify(void *opaque) -{ - MSIVector *entry = opaque; - PCIDevice *pdev = entry->pdev; - IVShmemState *s = IVSHMEM_COMMON(pdev); - int vector = entry - s->msi_vectors; - EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; - - if (!event_notifier_test_and_clear(n)) { - return; - } - - IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, vector); - if (ivshmem_has_feature(s, IVSHMEM_MSI)) { - if (msix_enabled(pdev)) { - msix_notify(pdev, vector); - } - } else { - ivshmem_IntrStatus_write(s, 1); - } -} - -static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector, - MSIMessage msg) -{ - IVShmemState *s = IVSHMEM_COMMON(dev); - EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; - MSIVector *v = &s->msi_vectors[vector]; - int ret; - - IVSHMEM_DPRINTF("vector unmask %p %d\n", dev, vector); - if (!v->pdev) { - error_report("ivshmem: vector %d route does not exist", vector); - return -EINVAL; - } - assert(!v->unmasked); - - ret = kvm_irqchip_update_msi_route(kvm_state, v->virq, msg, dev); - if (ret < 0) { - return ret; - } - kvm_irqchip_commit_routes(kvm_state); - - ret = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq); - if (ret < 0) { - return ret; - } - v->unmasked = true; - - return 0; -} - -static void ivshmem_vector_mask(PCIDevice *dev, unsigned vector) -{ - IVShmemState *s = IVSHMEM_COMMON(dev); - EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; - MSIVector *v = &s->msi_vectors[vector]; - int ret; - - IVSHMEM_DPRINTF("vector mask %p %d\n", dev, vector); - if (!v->pdev) { - error_report("ivshmem: vector %d route does not exist", vector); - return; - } - assert(v->unmasked); - - ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, v->virq); - if (ret < 0) { - error_report("remove_irqfd_notifier_gsi failed"); - return; - } - v->unmasked = false; -} - -static void ivshmem_vector_poll(PCIDevice *dev, - unsigned int vector_start, - unsigned int vector_end) -{ - IVShmemState *s = IVSHMEM_COMMON(dev); - unsigned int vector; - - IVSHMEM_DPRINTF("vector poll %p %d-%d\n", dev, vector_start, vector_end); - - vector_end = MIN(vector_end, s->vectors); - - for (vector = vector_start; vector < vector_end; vector++) { - EventNotifier *notifier = &s->peers[s->vm_id].eventfds[vector]; - - if (!msix_is_masked(dev, vector)) { - continue; - } - - if (event_notifier_test_and_clear(notifier)) { - msix_set_pending(dev, vector); - } - } -} - -static void watch_vector_notifier(IVShmemState *s, EventNotifier *n, - int vector) -{ - int eventfd = event_notifier_get_fd(n); - - assert(!s->msi_vectors[vector].pdev); - s->msi_vectors[vector].pdev = PCI_DEVICE(s); - - qemu_set_fd_handler(eventfd, ivshmem_vector_notify, - NULL, &s->msi_vectors[vector]); -} - -static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i) -{ - memory_region_add_eventfd(&s->ivshmem_mmio, - DOORBELL, - 4, - true, - (posn << 16) | i, - &s->peers[posn].eventfds[i]); -} - -static void ivshmem_del_eventfd(IVShmemState *s, int posn, int i) -{ - memory_region_del_eventfd(&s->ivshmem_mmio, - DOORBELL, - 4, - true, - (posn << 16) | i, - &s->peers[posn].eventfds[i]); -} - -static void close_peer_eventfds(IVShmemState *s, int posn) -{ - int i, n; - - assert(posn >= 0 && posn < s->nb_peers); - n = s->peers[posn].nb_eventfds; - - if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { - memory_region_transaction_begin(); - for (i = 0; i < n; i++) { - ivshmem_del_eventfd(s, posn, i); - } - memory_region_transaction_commit(); - } - - for (i = 0; i < n; i++) { - event_notifier_cleanup(&s->peers[posn].eventfds[i]); - } - - g_free(s->peers[posn].eventfds); - s->peers[posn].nb_eventfds = 0; -} - -static void resize_peers(IVShmemState *s, int nb_peers) -{ - int old_nb_peers = s->nb_peers; - int i; - - assert(nb_peers > old_nb_peers); - IVSHMEM_DPRINTF("bumping storage to %d peers\n", nb_peers); - - s->peers = g_renew(Peer, s->peers, nb_peers); - s->nb_peers = nb_peers; - - for (i = old_nb_peers; i < nb_peers; i++) { - s->peers[i].eventfds = g_new0(EventNotifier, s->vectors); - s->peers[i].nb_eventfds = 0; - } -} - -static void ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector, - Error **errp) -{ - PCIDevice *pdev = PCI_DEVICE(s); - KVMRouteChange c; - int ret; - - IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector); - assert(!s->msi_vectors[vector].pdev); - - c = kvm_irqchip_begin_route_changes(kvm_state); - ret = kvm_irqchip_add_msi_route(&c, vector, pdev); - if (ret < 0) { - error_setg(errp, "kvm_irqchip_add_msi_route failed"); - return; - } - kvm_irqchip_commit_route_changes(&c); - - s->msi_vectors[vector].virq = ret; - s->msi_vectors[vector].pdev = pdev; -} - -static void setup_interrupt(IVShmemState *s, int vector, Error **errp) -{ - EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; - bool with_irqfd = kvm_msi_via_irqfd_enabled() && - ivshmem_has_feature(s, IVSHMEM_MSI); - PCIDevice *pdev = PCI_DEVICE(s); - Error *err = NULL; - - IVSHMEM_DPRINTF("setting up interrupt for vector: %d\n", vector); - - if (!with_irqfd) { - IVSHMEM_DPRINTF("with eventfd\n"); - watch_vector_notifier(s, n, vector); - } else if (msix_enabled(pdev)) { - IVSHMEM_DPRINTF("with irqfd\n"); - ivshmem_add_kvm_msi_virq(s, vector, &err); - if (err) { - error_propagate(errp, err); - return; - } - - if (!msix_is_masked(pdev, vector)) { - kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, - s->msi_vectors[vector].virq); - /* TODO handle error */ - } - } else { - /* it will be delayed until msix is enabled, in write_config */ - IVSHMEM_DPRINTF("with irqfd, delayed until msix enabled\n"); - } -} - -static void process_msg_shmem(IVShmemState *s, int fd, Error **errp) -{ - Error *local_err = NULL; - struct stat buf; - size_t size; - - if (s->ivshmem_bar2) { - error_setg(errp, "server sent unexpected shared memory message"); - close(fd); - return; - } - - if (fstat(fd, &buf) < 0) { - error_setg_errno(errp, errno, - "can't determine size of shared memory sent by server"); - close(fd); - return; - } - - 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); - return; - } - - s->ivshmem_bar2 = &s->server_bar2; -} - -static void process_msg_disconnect(IVShmemState *s, uint16_t posn, - Error **errp) -{ - IVSHMEM_DPRINTF("posn %d has gone away\n", posn); - if (posn >= s->nb_peers || posn == s->vm_id) { - error_setg(errp, "invalid peer %d", posn); - return; - } - close_peer_eventfds(s, posn); -} - -static void process_msg_connect(IVShmemState *s, uint16_t posn, int fd, - Error **errp) -{ - Peer *peer = &s->peers[posn]; - int vector; - - /* - * The N-th connect message for this peer comes with the file - * descriptor for vector N-1. Count messages to find the vector. - */ - if (peer->nb_eventfds >= s->vectors) { - error_setg(errp, "Too many eventfd received, device has %d vectors", - s->vectors); - close(fd); - return; - } - vector = peer->nb_eventfds++; - - IVSHMEM_DPRINTF("eventfds[%d][%d] = %d\n", posn, vector, fd); - event_notifier_init_fd(&peer->eventfds[vector], fd); - g_unix_set_fd_nonblocking(fd, true, NULL); /* msix/irqfd poll non block */ - - if (posn == s->vm_id) { - setup_interrupt(s, vector, errp); - /* TODO do we need to handle the error? */ - } - - if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { - ivshmem_add_eventfd(s, posn, vector); - } -} - -static void process_msg(IVShmemState *s, int64_t msg, int fd, Error **errp) -{ - IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", msg, fd); - - if (msg < -1 || msg > IVSHMEM_MAX_PEERS) { - error_setg(errp, "server sent invalid message %" PRId64, msg); - close(fd); - return; - } - - if (msg == -1) { - process_msg_shmem(s, fd, errp); - return; - } - - if (msg >= s->nb_peers) { - resize_peers(s, msg + 1); - } - - if (fd >= 0) { - process_msg_connect(s, msg, fd, errp); - } else { - process_msg_disconnect(s, msg, errp); - } -} - -static int ivshmem_can_receive(void *opaque) -{ - IVShmemState *s = opaque; - - assert(s->msg_buffered_bytes < sizeof(s->msg_buf)); - return sizeof(s->msg_buf) - s->msg_buffered_bytes; -} - -static void ivshmem_read(void *opaque, const uint8_t *buf, int size) -{ - IVShmemState *s = opaque; - Error *err = NULL; - int fd; - int64_t msg; - - assert(size >= 0 && s->msg_buffered_bytes + size <= sizeof(s->msg_buf)); - memcpy((unsigned char *)&s->msg_buf + s->msg_buffered_bytes, buf, size); - s->msg_buffered_bytes += size; - if (s->msg_buffered_bytes < sizeof(s->msg_buf)) { - return; - } - msg = le64_to_cpu(s->msg_buf); - s->msg_buffered_bytes = 0; - - fd = qemu_chr_fe_get_msgfd(&s->server_chr); - - process_msg(s, msg, fd, &err); - if (err) { - error_report_err(err); - } -} - -static int64_t ivshmem_recv_msg(IVShmemState *s, int *pfd, Error **errp) -{ - int64_t msg; - int n, ret; - - n = 0; - do { - ret = qemu_chr_fe_read_all(&s->server_chr, (uint8_t *)&msg + n, - sizeof(msg) - n); - if (ret < 0) { - if (ret == -EINTR) { - continue; - } - error_setg_errno(errp, -ret, "read from server failed"); - return INT64_MIN; - } - n += ret; - } while (n < sizeof(msg)); - - *pfd = qemu_chr_fe_get_msgfd(&s->server_chr); - return le64_to_cpu(msg); -} - -static void ivshmem_recv_setup(IVShmemState *s, Error **errp) -{ - Error *err = NULL; - int64_t msg; - int fd; - - msg = ivshmem_recv_msg(s, &fd, &err); - if (err) { - error_propagate(errp, err); - return; - } - if (msg != IVSHMEM_PROTOCOL_VERSION) { - error_setg(errp, "server sent version %" PRId64 ", expecting %d", - msg, IVSHMEM_PROTOCOL_VERSION); - return; - } - if (fd != -1) { - error_setg(errp, "server sent invalid version message"); - return; - } - - /* - * ivshmem-server sends the remaining initial messages in a fixed - * order, but the device has always accepted them in any order. - * Stay as compatible as practical, just in case people use - * servers that behave differently. - */ - - /* - * ivshmem_device_spec.txt has always required the ID message - * right here, and ivshmem-server has always complied. However, - * older versions of the device accepted it out of order, but - * broke when an interrupt setup message arrived before it. - */ - msg = ivshmem_recv_msg(s, &fd, &err); - if (err) { - error_propagate(errp, err); - return; - } - if (fd != -1 || msg < 0 || msg > IVSHMEM_MAX_PEERS) { - error_setg(errp, "server sent invalid ID message"); - return; - } - s->vm_id = msg; - - /* - * Receive more messages until we got shared memory. - */ - do { - msg = ivshmem_recv_msg(s, &fd, &err); - if (err) { - error_propagate(errp, err); - return; - } - process_msg(s, msg, fd, &err); - if (err) { - error_propagate(errp, err); - return; - } - } while (msg != -1); - - /* - * This function must either map the shared memory or fail. The - * loop above ensures that: it terminates normally only after it - * successfully processed the server's shared memory message. - * Assert that actually mapped the shared memory: - */ - assert(s->ivshmem_bar2); -} - -/* Select the MSI-X vectors used by device. - * ivshmem maps events to vectors statically, so - * we just enable all vectors on init and after reset. */ -static void ivshmem_msix_vector_use(IVShmemState *s) -{ - PCIDevice *d = PCI_DEVICE(s); - int i; - - for (i = 0; i < s->vectors; i++) { - msix_vector_use(d, i); - } -} - -static void ivshmem_disable_irqfd(IVShmemState *s); - -static void ivshmem_reset(DeviceState *d) -{ - IVShmemState *s = IVSHMEM_COMMON(d); - - ivshmem_disable_irqfd(s); - - s->intrstatus = 0; - s->intrmask = 0; - if (ivshmem_has_feature(s, IVSHMEM_MSI)) { - ivshmem_msix_vector_use(s); - } -} - -static int ivshmem_setup_interrupts(IVShmemState *s, Error **errp) -{ - /* allocate QEMU callback data for receiving interrupts */ - s->msi_vectors = g_new0(MSIVector, s->vectors); - - if (ivshmem_has_feature(s, IVSHMEM_MSI)) { - if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1, errp)) { - return -1; - } - - IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors); - ivshmem_msix_vector_use(s); - } - - return 0; -} - -static void ivshmem_remove_kvm_msi_virq(IVShmemState *s, int vector) -{ - IVSHMEM_DPRINTF("ivshmem_remove_kvm_msi_virq vector:%d\n", vector); - - if (s->msi_vectors[vector].pdev == NULL) { - return; - } - - /* it was cleaned when masked in the frontend. */ - kvm_irqchip_release_virq(kvm_state, s->msi_vectors[vector].virq); - - s->msi_vectors[vector].pdev = NULL; -} - -static void ivshmem_enable_irqfd(IVShmemState *s) -{ - PCIDevice *pdev = PCI_DEVICE(s); - int i; - - for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) { - Error *err = NULL; - - ivshmem_add_kvm_msi_virq(s, i, &err); - if (err) { - error_report_err(err); - goto undo; - } - } - - if (msix_set_vector_notifiers(pdev, - ivshmem_vector_unmask, - ivshmem_vector_mask, - ivshmem_vector_poll)) { - error_report("ivshmem: msix_set_vector_notifiers failed"); - goto undo; - } - return; - -undo: - while (--i >= 0) { - ivshmem_remove_kvm_msi_virq(s, i); - } -} - -static void ivshmem_disable_irqfd(IVShmemState *s) -{ - PCIDevice *pdev = PCI_DEVICE(s); - int i; - - if (!pdev->msix_vector_use_notifier) { - return; - } - - msix_unset_vector_notifiers(pdev); - - for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) { - /* - * MSI-X is already disabled here so msix_unset_vector_notifiers() - * didn't call our release notifier. Do it now to keep our masks and - * unmasks balanced. - */ - if (s->msi_vectors[i].unmasked) { - ivshmem_vector_mask(pdev, i); - } - ivshmem_remove_kvm_msi_virq(s, i); - } - -} - -static void ivshmem_write_config(PCIDevice *pdev, uint32_t address, - uint32_t val, int len) -{ - IVShmemState *s = IVSHMEM_COMMON(pdev); - int is_enabled, was_enabled = msix_enabled(pdev); - - pci_default_write_config(pdev, address, val, len); - is_enabled = msix_enabled(pdev); - - if (kvm_msi_via_irqfd_enabled()) { - if (!was_enabled && is_enabled) { - ivshmem_enable_irqfd(s); - } else if (was_enabled && !is_enabled) { - ivshmem_disable_irqfd(s); - } - } -} - -static void ivshmem_common_realize(PCIDevice *dev, Error **errp) -{ - IVShmemState *s = IVSHMEM_COMMON(dev); - Error *err = NULL; - uint8_t *pci_conf; - - /* IRQFD requires MSI */ - if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && - !ivshmem_has_feature(s, IVSHMEM_MSI)) { - error_setg(errp, "ioeventfd/irqfd requires MSI"); - return; - } - - pci_conf = dev->config; - pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY; - - memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s, - "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE); - - /* region for registers*/ - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, - &s->ivshmem_mmio); - - if (s->hostmem != NULL) { - IVSHMEM_DPRINTF("using hostmem\n"); - - s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem); - host_memory_backend_set_mapped(s->hostmem, true); - } else { - Chardev *chr = qemu_chr_fe_get_driver(&s->server_chr); - assert(chr); - - IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", - chr->filename); - - /* we allocate enough space for 16 peers and grow as needed */ - resize_peers(s, 16); - - /* - * Receive setup messages from server synchronously. - * Older versions did it asynchronously, but that creates a - * number of entertaining race conditions. - */ - ivshmem_recv_setup(s, &err); - if (err) { - error_propagate(errp, err); - return; - } - - if (s->master == ON_OFF_AUTO_ON && s->vm_id != 0) { - error_setg(errp, - "master must connect to the server before any peers"); - return; - } - - qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_can_receive, - ivshmem_read, NULL, NULL, s, NULL, true); - - if (ivshmem_setup_interrupts(s, errp) < 0) { - error_prepend(errp, "Failed to initialize interrupts: "); - return; - } - } - - if (s->master == ON_OFF_AUTO_AUTO) { - s->master = s->vm_id == 0 ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; - } - - 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); - return; - } - } - - vmstate_register_ram(s->ivshmem_bar2, DEVICE(s)); - pci_register_bar(PCI_DEVICE(s), 2, - PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_PREFETCH | - PCI_BASE_ADDRESS_MEM_TYPE_64, - s->ivshmem_bar2); -} - -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); - } - - if (memory_region_is_mapped(s->ivshmem_bar2)) { - if (!s->hostmem) { - void *addr = memory_region_get_ram_ptr(s->ivshmem_bar2); - int fd; - - if (munmap(addr, memory_region_size(s->ivshmem_bar2) == -1)) { - error_report("Failed to munmap shared memory %s", - strerror(errno)); - } - - fd = memory_region_get_fd(s->ivshmem_bar2); - close(fd); - } - - vmstate_unregister_ram(s->ivshmem_bar2, DEVICE(dev)); - } - - if (s->hostmem) { - host_memory_backend_set_mapped(s->hostmem, false); - } - - if (s->peers) { - for (i = 0; i < s->nb_peers; i++) { - close_peer_eventfds(s, i); - } - g_free(s->peers); - } - - if (ivshmem_has_feature(s, IVSHMEM_MSI)) { - msix_uninit_exclusive_bar(dev); - } - - g_free(s->msi_vectors); -} - -static int ivshmem_pre_load(void *opaque) -{ - IVShmemState *s = opaque; - - if (!ivshmem_is_master(s)) { - error_report("'peer' devices are not migratable"); - return -EINVAL; - } - - return 0; -} - -static int ivshmem_post_load(void *opaque, int version_id) -{ - IVShmemState *s = opaque; - - if (ivshmem_has_feature(s, IVSHMEM_MSI)) { - ivshmem_msix_vector_use(s); - } - return 0; -} - -static void ivshmem_common_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = ivshmem_common_realize; - k->exit = ivshmem_exit; - k->config_write = ivshmem_write_config; - k->vendor_id = PCI_VENDOR_ID_IVSHMEM; - k->device_id = PCI_DEVICE_ID_IVSHMEM; - k->class_id = PCI_CLASS_MEMORY_RAM; - k->revision = 1; - dc->reset = ivshmem_reset; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->desc = "Inter-VM shared memory"; -} - -static const TypeInfo ivshmem_common_info = { - .name = TYPE_IVSHMEM_COMMON, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(IVShmemState), - .abstract = true, - .class_init = ivshmem_common_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { }, - }, -}; - -static const VMStateDescription ivshmem_plain_vmsd = { - .name = TYPE_IVSHMEM_PLAIN, - .version_id = 0, - .minimum_version_id = 0, - .pre_load = ivshmem_pre_load, - .post_load = ivshmem_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, IVShmemState), - VMSTATE_UINT32(intrstatus, IVShmemState), - VMSTATE_UINT32(intrmask, IVShmemState), - VMSTATE_END_OF_LIST() - }, -}; - -static Property ivshmem_plain_properties[] = { - DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), - DEFINE_PROP_LINK("memdev", IVShmemState, hostmem, TYPE_MEMORY_BACKEND, - HostMemoryBackend *), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ivshmem_plain_realize(PCIDevice *dev, Error **errp) -{ - IVShmemState *s = IVSHMEM_COMMON(dev); - - if (!s->hostmem) { - error_setg(errp, "You must specify a 'memdev'"); - return; - } else if (host_memory_backend_is_mapped(s->hostmem)) { - error_setg(errp, "can't use already busy memdev: %s", - object_get_canonical_path_component(OBJECT(s->hostmem))); - return; - } - - ivshmem_common_realize(dev, errp); -} - -static void ivshmem_plain_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = ivshmem_plain_realize; - device_class_set_props(dc, ivshmem_plain_properties); - dc->vmsd = &ivshmem_plain_vmsd; -} - -static const TypeInfo ivshmem_plain_info = { - .name = TYPE_IVSHMEM_PLAIN, - .parent = TYPE_IVSHMEM_COMMON, - .instance_size = sizeof(IVShmemState), - .class_init = ivshmem_plain_class_init, -}; - -static const VMStateDescription ivshmem_doorbell_vmsd = { - .name = TYPE_IVSHMEM_DOORBELL, - .version_id = 0, - .minimum_version_id = 0, - .pre_load = ivshmem_pre_load, - .post_load = ivshmem_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, IVShmemState), - VMSTATE_MSIX(parent_obj, IVShmemState), - VMSTATE_UINT32(intrstatus, IVShmemState), - VMSTATE_UINT32(intrmask, IVShmemState), - VMSTATE_END_OF_LIST() - }, -}; - -static Property ivshmem_doorbell_properties[] = { - DEFINE_PROP_CHR("chardev", IVShmemState, server_chr), - DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1), - DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, - true), - DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ivshmem_doorbell_init(Object *obj) -{ - IVShmemState *s = IVSHMEM_DOORBELL(obj); - - s->features |= (1 << IVSHMEM_MSI); -} - -static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp) -{ - IVShmemState *s = IVSHMEM_COMMON(dev); - - if (!qemu_chr_fe_backend_connected(&s->server_chr)) { - error_setg(errp, "You must specify a 'chardev'"); - return; - } - - ivshmem_common_realize(dev, errp); -} - -static void ivshmem_doorbell_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = ivshmem_doorbell_realize; - device_class_set_props(dc, ivshmem_doorbell_properties); - dc->vmsd = &ivshmem_doorbell_vmsd; -} - -static const TypeInfo ivshmem_doorbell_info = { - .name = TYPE_IVSHMEM_DOORBELL, - .parent = TYPE_IVSHMEM_COMMON, - .instance_size = sizeof(IVShmemState), - .instance_init = ivshmem_doorbell_init, - .class_init = ivshmem_doorbell_class_init, -}; - -static void ivshmem_register_types(void) -{ - type_register_static(&ivshmem_common_info); - type_register_static(&ivshmem_plain_info); - type_register_static(&ivshmem_doorbell_info); -} - -type_init(ivshmem_register_types) diff --git a/hw/misc/lasi.c b/hw/misc/lasi.c index ff9dc893ae6..9f758c6a863 100644 --- a/hw/misc/lasi.c +++ b/hw/misc/lasi.c @@ -15,8 +15,8 @@ #include "qapi/error.h" #include "trace.h" #include "hw/irq.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/runstate.h" #include "migration/vmstate.h" #include "qom/object.h" #include "hw/misc/lasi.h" @@ -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; @@ -196,7 +209,7 @@ static const VMStateDescription vmstate_lasi = { .name = "Lasi", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(irr, LasiState), VMSTATE_UINT32(imr, LasiState), VMSTATE_UINT32(ipr, LasiState), @@ -250,11 +263,11 @@ static void lasi_init(Object *obj) qdev_init_gpio_in(DEVICE(obj), lasi_set_irq, LASI_IRQS); } -static void lasi_class_init(ObjectClass *klass, void *data) +static void lasi_class_init(ObjectClass *klass, const 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 f6d6d68bce5..f7f709072af 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() } @@ -101,20 +101,19 @@ static void led_realize(DeviceState *dev, Error **errp) qdev_init_gpio_in(DEVICE(s), led_set_state_gpio_handler, 1); } -static Property led_properties[] = { +static const Property led_properties[] = { DEFINE_PROP_STRING("color", LEDState, color), DEFINE_PROP_STRING("description", LEDState, description), DEFINE_PROP_BOOL("gpio-active-high", LEDState, gpio_active_high, true), - DEFINE_PROP_END_OF_LIST(), }; -static void led_class_init(ObjectClass *klass, void *data) +static void led_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 0787a0268d1..bc37e2a2cb7 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -16,6 +16,7 @@ */ #include "qemu/osdep.h" +#include "system/address-spaces.h" #include "migration/vmstate.h" #include "hw/sysbus.h" #include "hw/irq.h" @@ -23,13 +24,13 @@ #include "hw/misc/mac_via.h" #include "hw/misc/mos6522.h" #include "hw/input/adb.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" -#include "sysemu/block-backend.h" -#include "sysemu/rtc.h" +#include "system/block-backend.h" +#include "system/rtc.h" #include "trace.h" #include "qemu/log.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. @@ -491,7 +495,6 @@ static void via1_rtc_update(MOS6522Q800VIA1State *v1s) break; default: g_assert_not_reached(); - break; } return; } @@ -552,7 +555,6 @@ static void via1_rtc_update(MOS6522Q800VIA1State *v1s) break; default: g_assert_not_reached(); - break; } return; } @@ -698,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; } @@ -865,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); } } @@ -872,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, @@ -882,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) { @@ -894,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; } } @@ -976,7 +1201,7 @@ static int via1_post_load(void *opaque, int version_id) } /* VIA 1 */ -static void mos6522_q800_via1_reset_hold(Object *obj) +static void mos6522_q800_via1_reset_hold(Object *obj, ResetType type) { MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(obj); MOS6522State *ms = MOS6522(v1s); @@ -984,7 +1209,7 @@ static void mos6522_q800_via1_reset_hold(Object *obj) ADBBusState *adb_bus = &v1s->adb_bus; if (mdc->parent_phases.hold) { - mdc->parent_phases.hold(obj); + mdc->parent_phases.hold(obj, type); } ms->timers[0].frequency = VIA_TIMER_FREQ; @@ -996,6 +1221,9 @@ static void mos6522_q800_via1_reset_hold(Object *obj) 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) @@ -1062,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), @@ -1088,16 +1316,17 @@ 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() } }; -static Property mos6522_q800_via1_properties[] = { +static const Property mos6522_q800_via1_properties[] = { DEFINE_PROP_DRIVE("drive", MOS6522Q800VIA1State, blk), - DEFINE_PROP_END_OF_LIST(), }; -static void mos6522_q800_via1_class_init(ObjectClass *oc, void *data) +static void mos6522_q800_via1_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); ResettableClass *rc = RESETTABLE_CLASS(oc); @@ -1127,13 +1356,13 @@ static void mos6522_q800_via2_portB_write(MOS6522State *s) } } -static void mos6522_q800_via2_reset_hold(Object *obj) +static void mos6522_q800_via2_reset_hold(Object *obj, ResetType type) { MOS6522State *ms = MOS6522(obj); MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); if (mdc->parent_phases.hold) { - mdc->parent_phases.hold(obj); + mdc->parent_phases.hold(obj, type); } ms->timers[0].frequency = VIA_TIMER_FREQ; @@ -1179,14 +1408,14 @@ 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() } }; -static void mos6522_q800_via2_class_init(ObjectClass *oc, void *data) +static void mos6522_q800_via2_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); ResettableClass *rc = RESETTABLE_CLASS(oc); diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 6336dcb1948..bcd00c9bb1b 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -29,8 +29,8 @@ #include "migration/vmstate.h" #include "hw/misc/macio/cuda.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" -#include "sysemu/rtc.h" +#include "system/runstate.h" +#include "system/rtc.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/log.h" @@ -487,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), @@ -554,17 +554,16 @@ static void cuda_init(Object *obj) DEVICE(obj), "adb.0"); } -static Property cuda_properties[] = { +static const Property cuda_properties[] = { DEFINE_PROP_UINT64("timebase-frequency", CUDAState, tb_frequency, 0), - DEFINE_PROP_END_OF_LIST() }; -static void cuda_class_init(ObjectClass *oc, void *data) +static void cuda_class_init(ObjectClass *oc, const 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); @@ -586,20 +585,20 @@ static void mos6522_cuda_portB_write(MOS6522State *s) cuda_update(cs); } -static void mos6522_cuda_reset_hold(Object *obj) +static void mos6522_cuda_reset_hold(Object *obj, ResetType type) { MOS6522State *ms = MOS6522(obj); MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); if (mdc->parent_phases.hold) { - mdc->parent_phases.hold(obj); + mdc->parent_phases.hold(obj, type); } ms->timers[0].frequency = CUDA_TIMER_FREQ; ms->timers[1].frequency = (SCALE_US * 6000) / 4700; } -static void mos6522_cuda_class_init(ObjectClass *oc, void *data) +static void mos6522_cuda_class_init(ObjectClass *oc, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_CLASS(oc); diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c index 4deb3304719..990551f91f4 100644 --- a/hw/misc/macio/gpio.c +++ b/hw/misc/macio/gpio.c @@ -34,6 +34,11 @@ #include "qemu/module.h" #include "trace.h" +enum MacioGPIORegisterBits { + OUT_DATA = 1, + IN_DATA = 2, + OUT_ENABLE = 4, +}; void macio_set_gpio(MacIOGPIOState *s, uint32_t gpio, bool state) { @@ -41,14 +46,14 @@ void macio_set_gpio(MacIOGPIOState *s, uint32_t gpio, bool state) trace_macio_set_gpio(gpio, state); - if (s->gpio_regs[gpio] & 4) { + if (s->gpio_regs[gpio] & OUT_ENABLE) { qemu_log_mask(LOG_GUEST_ERROR, "GPIO: Setting GPIO %d while it's an output\n", gpio); } - new_reg = s->gpio_regs[gpio] & ~2; + new_reg = s->gpio_regs[gpio] & ~IN_DATA; if (state) { - new_reg |= 2; + new_reg |= IN_DATA; } if (new_reg == s->gpio_regs[gpio]) { @@ -107,12 +112,12 @@ static void macio_gpio_write(void *opaque, hwaddr addr, uint64_t value, addr -= 8; if (addr < 36) { - value &= ~2; + value &= ~IN_DATA; - if (value & 4) { - ibit = (value & 1) << 1; + if (value & OUT_ENABLE) { + ibit = (value & OUT_DATA) << 1; } else { - ibit = s->gpio_regs[addr] & 2; + ibit = s->gpio_regs[addr] & IN_DATA; } s->gpio_regs[addr] = value | ibit; @@ -135,7 +140,7 @@ static uint64_t macio_gpio_read(void *opaque, hwaddr addr, unsigned size) } } - trace_macio_gpio_write(addr, val); + trace_macio_gpio_read(addr, val); return val; } @@ -168,7 +173,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() @@ -189,12 +194,12 @@ static void macio_gpio_nmi(NMIState *n, int cpu_index, Error **errp) macio_set_gpio(MACIO_GPIO(n), 9, false); } -static void macio_gpio_class_init(ObjectClass *oc, void *data) +static void macio_gpio_class_init(ObjectClass *oc, const 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; } @@ -205,7 +210,7 @@ static const TypeInfo macio_gpio_init_info = { .instance_size = sizeof(MacIOGPIOState), .instance_init = macio_gpio_init, .class_init = macio_gpio_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NMI }, { } }, diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index 80a789f32b8..b2b42dd5622 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -44,7 +44,7 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/log.h" -#include "sysemu/dma.h" +#include "system/dma.h" /* debug DBDMA */ #define DEBUG_DBDMA 0 @@ -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() @@ -917,12 +917,12 @@ static void mac_dbdma_realize(DeviceState *dev, Error **errp) s->bh = qemu_bh_new_guarded(DBDMA_run_bh, s, &dev->mem_reentrancy_guard); } -static void mac_dbdma_class_init(ObjectClass *oc, void *data) +static void mac_dbdma_class_init(ObjectClass *oc, const 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 265c0bbd8db..6710485d728 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -123,14 +123,17 @@ static bool macio_realize_ide(MacIOState *s, MACIOIDEState *ide, { SysBusDevice *sbd = SYS_BUS_DEVICE(ide); - sysbus_connect_irq(sbd, 0, irq0); - sysbus_connect_irq(sbd, 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); - return qdev_realize(DEVICE(ide), BUS(&s->macio_bus), errp); + return true; } static void macio_oldworld_realize(PCIDevice *d, Error **errp) @@ -376,13 +379,13 @@ 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() } }; -static void macio_oldworld_class_init(ObjectClass *oc, void *data) +static void macio_oldworld_class_init(ObjectClass *oc, const void *data) { PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -396,19 +399,18 @@ 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() } }; -static Property macio_newworld_properties[] = { +static const Property macio_newworld_properties[] = { DEFINE_PROP_BOOL("has-pmu", NewWorldMacIOState, has_pmu, false), DEFINE_PROP_BOOL("has-adb", NewWorldMacIOState, has_adb, false), - DEFINE_PROP_END_OF_LIST() }; -static void macio_newworld_class_init(ObjectClass *oc, void *data) +static void macio_newworld_class_init(ObjectClass *oc, const void *data) { PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -419,12 +421,11 @@ static void macio_newworld_class_init(ObjectClass *oc, void *data) device_class_set_props(dc, macio_newworld_properties); } -static Property macio_properties[] = { +static const Property macio_properties[] = { DEFINE_PROP_UINT64("frequency", MacIOState, frequency, 0), - DEFINE_PROP_END_OF_LIST() }; -static void macio_class_init(ObjectClass *klass, void *data) +static void macio_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -464,7 +465,7 @@ static const TypeInfo macio_type_info = { .instance_init = macio_instance_init, .abstract = true, .class_init = macio_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index 58316d18713..37349139948 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -34,8 +34,8 @@ #include "hw/irq.h" #include "hw/misc/macio/pmu.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" -#include "sysemu/rtc.h" +#include "system/runstate.h" +#include "system/rtc.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/log.h" @@ -668,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() @@ -679,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), @@ -698,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 } @@ -737,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); } } @@ -761,17 +760,16 @@ static void pmu_init(Object *obj) sysbus_init_mmio(d, &s->mem); } -static Property pmu_properties[] = { +static const Property pmu_properties[] = { DEFINE_PROP_BOOL("has-adb", PMUState, has_adb, true), - DEFINE_PROP_END_OF_LIST() }; -static void pmu_class_init(ObjectClass *oc, void *data) +static void pmu_class_init(ObjectClass *oc, const 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); @@ -793,7 +791,7 @@ static void mos6522_pmu_portB_write(MOS6522State *s) pmu_update(ps); } -static void mos6522_pmu_reset_hold(Object *obj) +static void mos6522_pmu_reset_hold(Object *obj, ResetType type) { MOS6522State *ms = MOS6522(obj); MOS6522PMUState *mps = container_of(ms, MOS6522PMUState, parent_obj); @@ -801,7 +799,7 @@ static void mos6522_pmu_reset_hold(Object *obj) MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms); if (mdc->parent_phases.hold) { - mdc->parent_phases.hold(obj); + mdc->parent_phases.hold(obj, type); } ms->timers[0].frequency = VIA_TIMER_FREQ; @@ -810,7 +808,7 @@ static void mos6522_pmu_reset_hold(Object *obj) s->last_b = ms->b = TACK | TREQ; } -static void mos6522_pmu_class_init(ObjectClass *oc, void *data) +static void mos6522_pmu_class_init(ObjectClass *oc, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_CLASS(oc); diff --git a/hw/misc/macio/trace-events b/hw/misc/macio/trace-events index ad4b9d1c08e..055a407aebb 100644 --- a/hw/misc/macio/trace-events +++ b/hw/misc/macio/trace-events @@ -18,7 +18,8 @@ macio_timer_read(uint64_t addr, unsigned len, uint32_t val) "read addr 0x%"PRIx6 macio_set_gpio(int gpio, bool state) "setting GPIO %d to %d" macio_gpio_irq_assert(int gpio) "asserting GPIO %d" macio_gpio_irq_deassert(int gpio) "deasserting GPIO %d" -macio_gpio_write(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 +macio_gpio_write(uint64_t addr, uint64_t val) "addr 0x%"PRIx64" val 0x%"PRIx64 +macio_gpio_read(uint64_t addr, uint64_t val) "addr 0x%"PRIx64" val 0x%"PRIx64 # pmu.c pmu_adb_poll(int olen) "ADB autopoll, olen=%d" diff --git a/hw/misc/mchp_pfsoc_dmc.c b/hw/misc/mchp_pfsoc_dmc.c index 43d8e970abc..599f845f459 100644 --- a/hw/misc/mchp_pfsoc_dmc.c +++ b/hw/misc/mchp_pfsoc_dmc.c @@ -110,7 +110,8 @@ static void mchp_pfsoc_ddr_sgmii_phy_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->sgmii_phy); } -static void mchp_pfsoc_ddr_sgmii_phy_class_init(ObjectClass *klass, void *data) +static void mchp_pfsoc_ddr_sgmii_phy_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -192,7 +193,7 @@ static void mchp_pfsoc_ddr_cfg_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->cfg); } -static void mchp_pfsoc_ddr_cfg_class_init(ObjectClass *klass, void *data) +static void mchp_pfsoc_ddr_cfg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/mchp_pfsoc_ioscb.c b/hw/misc/mchp_pfsoc_ioscb.c index a71d134295a..10fc7ea2a95 100644 --- a/hw/misc/mchp_pfsoc_ioscb.c +++ b/hw/misc/mchp_pfsoc_ioscb.c @@ -292,7 +292,7 @@ static void mchp_pfsoc_ioscb_realize(DeviceState *dev, Error **errp) sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); } -static void mchp_pfsoc_ioscb_class_init(ObjectClass *klass, void *data) +static void mchp_pfsoc_ioscb_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/mchp_pfsoc_sysreg.c b/hw/misc/mchp_pfsoc_sysreg.c index 7876fe0c5ba..f47c835f80d 100644 --- a/hw/misc/mchp_pfsoc_sysreg.c +++ b/hw/misc/mchp_pfsoc_sysreg.c @@ -27,7 +27,9 @@ #include "hw/irq.h" #include "hw/sysbus.h" #include "hw/misc/mchp_pfsoc_sysreg.h" +#include "system/runstate.h" +#define MSS_RESET_CR 0x18 #define ENVM_CR 0xb8 #define MESSAGE_INT 0x118c @@ -56,6 +58,11 @@ static void mchp_pfsoc_sysreg_write(void *opaque, hwaddr offset, { MchpPfSoCSysregState *s = opaque; switch (offset) { + case MSS_RESET_CR: + if (value == 0xdead) { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } + break; case MESSAGE_INT: qemu_irq_lower(s->irq); break; @@ -85,7 +92,7 @@ static void mchp_pfsoc_sysreg_realize(DeviceState *dev, Error **errp) sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); } -static void mchp_pfsoc_sysreg_class_init(ObjectClass *klass, void *data) +static void mchp_pfsoc_sysreg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 88ecab83923..6d47de482c5 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -3,7 +3,6 @@ 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_PCA9552', if_true: files('pca9552.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')) @@ -20,6 +19,8 @@ system_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_ras.c')) # Mac devices 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 system_ss.add(when: 'CONFIG_VIRT_CTRL', if_true: files('virt_ctrl.c')) @@ -36,7 +37,9 @@ system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c')) subdir('macio') -system_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem.c')) +# ivshmem devices +system_ss.add(when: 'CONFIG_IVSHMEM_DEVICE', if_true: files('ivshmem-pci.c')) +system_ss.add(when: 'CONFIG_IVSHMEM_FLAT_DEVICE', if_true: files('ivshmem-flat.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')) @@ -50,9 +53,10 @@ system_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-ccu.c' system_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40-dramc.c')) system_ss.add(when: 'CONFIG_AXP2XX_PMU', if_true: files('axp2xx.c')) system_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c')) -system_ss.add(when: 'CONFIG_NSERIES', if_true: files('cbus.c')) system_ss.add(when: 'CONFIG_ECCMEMCTL', if_true: files('eccmemctl.c')) system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pmu.c', 'exynos4210_clk.c', 'exynos4210_rng.c')) +system_ss.add(when: 'CONFIG_FSL_IMX8MP_ANALOG', if_true: files('imx8mp_analog.c')) +system_ss.add(when: 'CONFIG_FSL_IMX8MP_CCM', if_true: files('imx8mp_ccm.c')) system_ss.add(when: 'CONFIG_IMX', if_true: files( 'imx25_ccm.c', 'imx31_ccm.c', @@ -66,20 +70,15 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files( 'imx_ccm.c', 'imx_rngc.c', )) -system_ss.add(when: 'CONFIG_MAINSTONE', if_true: files('mst_fpga.c')) system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files( - 'npcm7xx_clk.c', - 'npcm7xx_gcr.c', + 'npcm_clk.c', + 'npcm_gcr.c', 'npcm7xx_mft.c', 'npcm7xx_pwm.c', 'npcm7xx_rng.c', )) system_ss.add(when: 'CONFIG_OMAP', if_true: files( 'omap_clk.c', - 'omap_gpmc.c', - 'omap_l4.c', - 'omap_sdrc.c', - 'omap_tap.c', )) system_ss.add(when: 'CONFIG_RASPI', if_true: files( 'bcm2835_mbox.c', @@ -94,17 +93,24 @@ 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')) -specific_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-crl.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', )) +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')) @@ -120,6 +126,7 @@ system_ss.add(when: 'CONFIG_ARMSSE_MHU', if_true: files('armsse-mhu.c')) 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_PVPANIC_MMIO', if_true: files('pvpanic-mmio.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', @@ -129,14 +136,15 @@ system_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')) 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')) system_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_ahb_apb_pnp.c')) -system_ss.add(when: 'CONFIG_I2C', if_true: files('i2c-echo.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')) diff --git a/hw/misc/mips_cmgcr.c b/hw/misc/mips_cmgcr.c index 66eb11662c7..5484b739674 100644 --- a/hw/misc/mips_cmgcr.c +++ b/hw/misc/mips_cmgcr.c @@ -205,13 +205,13 @@ 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[] = { +static const Property mips_gcr_properties[] = { 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), @@ -219,7 +219,6 @@ static Property mips_gcr_properties[] = { MemoryRegion *), DEFINE_PROP_LINK("cpc", MIPSGCRState, cpc_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void mips_gcr_realize(DeviceState *dev, Error **errp) @@ -230,12 +229,12 @@ static void mips_gcr_realize(DeviceState *dev, Error **errp) s->vps = g_new(MIPSGCRVPState, s->num_vps); } -static void mips_gcr_class_init(ObjectClass *klass, void *data) +static void mips_gcr_class_init(ObjectClass *klass, const 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 4a94c870546..9bfb7c97219 100644 --- a/hw/misc/mips_cpc.c +++ b/hw/misc/mips_cpc.c @@ -92,8 +92,6 @@ static void cpc_write(void *opaque, hwaddr offset, uint64_t data, "%s: Bad offset 0x%x\n", __func__, (int)offset); break; } - - return; } static uint64_t cpc_read(void *opaque, hwaddr offset, unsigned size) @@ -157,24 +155,23 @@ 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() }, }; -static Property mips_cpc_properties[] = { +static const Property mips_cpc_properties[] = { DEFINE_PROP_UINT32("num-vp", MIPSCPCState, num_vp, 0x1), DEFINE_PROP_UINT64("vp-start-running", MIPSCPCState, vp_start_running, 0x1), - DEFINE_PROP_END_OF_LIST(), }; -static void mips_cpc_class_init(ObjectClass *klass, void *data) +static void mips_cpc_class_init(ObjectClass *klass, const 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 0eda302db45..fc17385cde7 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) { - address = (tag->saar[0] & 0xFFFFFFFFE000ULL) << 4; - size = 1ULL << ((tag->saar[0] >> 1) & 0x1f); - is_enabled = tag->saar[0] & 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) { - 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, @@ -515,7 +505,6 @@ static void mips_itu_init(Object *obj) static void mips_itu_realize(DeviceState *dev, Error **errp) { MIPSITUState *s = MIPS_ITU(dev); - CPUMIPSState *env; if (s->num_fifo > ITC_FIFO_NUM_MAX) { error_setg(errp, "Exceed maximum number of FIFO cells: %d", @@ -527,15 +516,6 @@ static void mips_itu_realize(DeviceState *dev, Error **errp) s->num_semaphores); return; } - if (!s->cpu0) { - error_setg(errp, "Missing 'cpu[0]' property"); - return; - } - - env = &s->cpu0->env; - if (env->saarp) { - s->saar = env->CP0_SAAR; - } s->cell = g_new(ITCStorageCell, get_num_cells(s)); } @@ -544,36 +524,29 @@ static void mips_itu_reset(DeviceState *dev) { MIPSITUState *s = MIPS_ITU(dev); - if (s->saar) { - s->saar[0] = 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[] = { +static const Property mips_itu_properties[] = { DEFINE_PROP_UINT32("num-fifo", MIPSITUState, num_fifo, ITC_FIFO_NUM_MAX), DEFINE_PROP_UINT32("num-semaphores", MIPSITUState, num_semaphores, ITC_SEMAPH_NUM_MAX), - DEFINE_PROP_LINK("cpu[0]", MIPSITUState, cpu0, TYPE_MIPS_CPU, MIPSCPU *), - DEFINE_PROP_END_OF_LIST(), }; -static void mips_itu_class_init(ObjectClass *klass, void *data) +static void mips_itu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 d6ba47bde97..8dd6b82ac5f 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -611,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), @@ -625,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), @@ -642,7 +642,7 @@ const VMStateDescription vmstate_mos6522 = { } }; -static void mos6522_reset_hold(Object *obj) +static void mos6522_reset_hold(Object *obj, ResetType type) { MOS6522State *s = MOS6522(obj); @@ -696,12 +696,11 @@ static void mos6522_finalize(Object *obj) timer_free(s->timers[1].timer); } -static Property mos6522_properties[] = { +static const Property mos6522_properties[] = { DEFINE_PROP_UINT64("frequency", MOS6522State, frequency, 0), - DEFINE_PROP_END_OF_LIST() }; -static void mos6522_class_init(ObjectClass *oc, void *data) +static void mos6522_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); ResettableClass *rc = RESETTABLE_CLASS(oc); diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c index 07b8cbdad28..bee1309f5a6 100644 --- a/hw/misc/mps2-fpgaio.c +++ b/hw/misc/mps2-fpgaio.c @@ -198,7 +198,7 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value, s->led0 = value & MAKE_64BIT_MASK(0, s->num_leds); for (i = 0; i < s->num_leds; i++) { - led_set_state(s->led[i], value & (1 << i)); + led_set_state(s->led[i], extract64(value, i, 1)); } } break; @@ -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), @@ -319,23 +319,22 @@ static const VMStateDescription mps2_fpgaio_vmstate = { }, }; -static Property mps2_fpgaio_properties[] = { +static const Property mps2_fpgaio_properties[] = { /* Frequency of the prescale counter */ DEFINE_PROP_UINT32("prescale-clk", MPS2FPGAIO, prescale_clk, 20000000), /* Number of LEDs controlled by LED0 register */ DEFINE_PROP_UINT32("num-leds", MPS2FPGAIO, num_leds, 2), DEFINE_PROP_BOOL("has-switches", MPS2FPGAIO, has_switches, false), DEFINE_PROP_BOOL("has-dbgctrl", MPS2FPGAIO, has_dbgctrl, false), - DEFINE_PROP_END_OF_LIST(), }; -static void mps2_fpgaio_class_init(ObjectClass *klass, void *data) +static void mps2_fpgaio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 b3b42a792cd..a9a5d4a535b 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,10 +449,14 @@ 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 } }; -static Property mps2_scc_properties[] = { +static const Property mps2_scc_properties[] = { /* Values for various read-only ID registers (which are specific * to the board model or FPGA image) */ @@ -367,16 +472,15 @@ static Property mps2_scc_properties[] = { */ DEFINE_PROP_ARRAY("oscclk", MPS2SCC, num_oscclk, oscclk_reset, qdev_prop_uint32, uint32_t), - DEFINE_PROP_END_OF_LIST(), }; -static void mps2_scc_class_init(ObjectClass *klass, void *data) +static void mps2_scc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 +489,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 2dce55c3649..ce0ad50c1be 100644 --- a/hw/misc/msf2-sysreg.c +++ b/hw/misc/msf2-sysreg.c @@ -112,17 +112,16 @@ 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() } }; -static Property msf2_sysreg_properties[] = { +static const Property msf2_sysreg_properties[] = { /* default divisors in Libero GUI */ DEFINE_PROP_UINT8("apb0divisor", MSF2SysregState, apb0div, 2), DEFINE_PROP_UINT8("apb1divisor", MSF2SysregState, apb1div, 2), - DEFINE_PROP_END_OF_LIST(), }; static void msf2_sysreg_realize(DeviceState *dev, Error **errp) @@ -137,12 +136,12 @@ static void msf2_sysreg_realize(DeviceState *dev, Error **errp) } } -static void msf2_sysreg_class_init(ObjectClass *klass, void *data) +static void msf2_sysreg_class_init(ObjectClass *klass, const 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 7692825867d..00000000000 --- 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" HWADDR_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" HWADDR_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 deleted file mode 100644 index bc2b879feb5..00000000000 --- a/hw/misc/npcm7xx_clk.c +++ /dev/null @@ -1,1095 +0,0 @@ -/* - * Nuvoton NPCM7xx Clock Control Registers. - * - * Copyright 2020 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/misc/npcm7xx_clk.h" -#include "hw/timer/npcm7xx_timer.h" -#include "hw/qdev-clock.h" -#include "migration/vmstate.h" -#include "qemu/error-report.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "qemu/timer.h" -#include "qemu/units.h" -#include "trace.h" -#include "sysemu/watchdog.h" - -/* - * The reference clock hz, and the SECCNT and CNTR25M registers in this module, - * is always 25 MHz. - */ -#define NPCM7XX_CLOCK_REF_HZ (25000000) - -/* Register Field Definitions */ -#define NPCM7XX_CLK_WDRCR_CA9C BIT(0) /* Cortex-A9 Cores */ - -#define PLLCON_LOKI BIT(31) -#define PLLCON_LOKS BIT(30) -#define PLLCON_PWDEN BIT(12) -#define PLLCON_FBDV(con) extract32((con), 16, 12) -#define PLLCON_OTDV2(con) extract32((con), 13, 3) -#define PLLCON_OTDV1(con) extract32((con), 8, 3) -#define PLLCON_INDV(con) extract32((con), 0, 6) - -enum NPCM7xxCLKRegisters { - NPCM7XX_CLK_CLKEN1, - NPCM7XX_CLK_CLKSEL, - NPCM7XX_CLK_CLKDIV1, - NPCM7XX_CLK_PLLCON0, - NPCM7XX_CLK_PLLCON1, - NPCM7XX_CLK_SWRSTR, - NPCM7XX_CLK_IPSRST1 = 0x20 / sizeof(uint32_t), - NPCM7XX_CLK_IPSRST2, - NPCM7XX_CLK_CLKEN2, - NPCM7XX_CLK_CLKDIV2, - NPCM7XX_CLK_CLKEN3, - NPCM7XX_CLK_IPSRST3, - NPCM7XX_CLK_WD0RCR, - NPCM7XX_CLK_WD1RCR, - NPCM7XX_CLK_WD2RCR, - NPCM7XX_CLK_SWRSTC1, - NPCM7XX_CLK_SWRSTC2, - NPCM7XX_CLK_SWRSTC3, - NPCM7XX_CLK_SWRSTC4, - NPCM7XX_CLK_PLLCON2, - NPCM7XX_CLK_CLKDIV3, - NPCM7XX_CLK_CORSTC, - NPCM7XX_CLK_PLLCONG, - NPCM7XX_CLK_AHBCKFI, - NPCM7XX_CLK_SECCNT, - NPCM7XX_CLK_CNTR25M, - NPCM7XX_CLK_REGS_END, -}; - -/* - * These reset values were taken from version 0.91 of the NPCM750R data sheet. - * - * All are loaded on power-up reset. CLKENx and SWRSTR should also be loaded on - * core domain reset, but this reset type is not yet supported by QEMU. - */ -static const uint32_t cold_reset_values[NPCM7XX_CLK_NR_REGS] = { - [NPCM7XX_CLK_CLKEN1] = 0xffffffff, - [NPCM7XX_CLK_CLKSEL] = 0x004aaaaa, - [NPCM7XX_CLK_CLKDIV1] = 0x5413f855, - [NPCM7XX_CLK_PLLCON0] = 0x00222101 | PLLCON_LOKI, - [NPCM7XX_CLK_PLLCON1] = 0x00202101 | PLLCON_LOKI, - [NPCM7XX_CLK_IPSRST1] = 0x00001000, - [NPCM7XX_CLK_IPSRST2] = 0x80000000, - [NPCM7XX_CLK_CLKEN2] = 0xffffffff, - [NPCM7XX_CLK_CLKDIV2] = 0xaa4f8f9f, - [NPCM7XX_CLK_CLKEN3] = 0xffffffff, - [NPCM7XX_CLK_IPSRST3] = 0x03000000, - [NPCM7XX_CLK_WD0RCR] = 0xffffffff, - [NPCM7XX_CLK_WD1RCR] = 0xffffffff, - [NPCM7XX_CLK_WD2RCR] = 0xffffffff, - [NPCM7XX_CLK_SWRSTC1] = 0x00000003, - [NPCM7XX_CLK_PLLCON2] = 0x00c02105 | PLLCON_LOKI, - [NPCM7XX_CLK_CORSTC] = 0x04000003, - [NPCM7XX_CLK_PLLCONG] = 0x01228606 | PLLCON_LOKI, - [NPCM7XX_CLK_AHBCKFI] = 0x000000c8, -}; - -/* The number of watchdogs that can trigger a reset. */ -#define NPCM7XX_NR_WATCHDOGS (3) - -/* Clock converter functions */ - -#define TYPE_NPCM7XX_CLOCK_PLL "npcm7xx-clock-pll" -#define NPCM7XX_CLOCK_PLL(obj) OBJECT_CHECK(NPCM7xxClockPLLState, \ - (obj), TYPE_NPCM7XX_CLOCK_PLL) -#define TYPE_NPCM7XX_CLOCK_SEL "npcm7xx-clock-sel" -#define NPCM7XX_CLOCK_SEL(obj) OBJECT_CHECK(NPCM7xxClockSELState, \ - (obj), TYPE_NPCM7XX_CLOCK_SEL) -#define TYPE_NPCM7XX_CLOCK_DIVIDER "npcm7xx-clock-divider" -#define NPCM7XX_CLOCK_DIVIDER(obj) OBJECT_CHECK(NPCM7xxClockDividerState, \ - (obj), TYPE_NPCM7XX_CLOCK_DIVIDER) - -static void npcm7xx_clk_update_pll(void *opaque) -{ - NPCM7xxClockPLLState *s = opaque; - uint32_t con = s->clk->regs[s->reg]; - uint64_t freq; - - /* The PLL is grounded if it is not locked yet. */ - if (con & PLLCON_LOKI) { - freq = clock_get_hz(s->clock_in); - freq *= PLLCON_FBDV(con); - freq /= PLLCON_INDV(con) * PLLCON_OTDV1(con) * PLLCON_OTDV2(con); - } else { - freq = 0; - } - - clock_update_hz(s->clock_out, freq); -} - -static void npcm7xx_clk_update_sel(void *opaque) -{ - NPCM7xxClockSELState *s = opaque; - uint32_t index = extract32(s->clk->regs[NPCM7XX_CLK_CLKSEL], s->offset, - s->len); - - if (index >= s->input_size) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: SEL index: %u out of range\n", - __func__, index); - index = 0; - } - clock_update_hz(s->clock_out, clock_get_hz(s->clock_in[index])); -} - -static void npcm7xx_clk_update_divider(void *opaque) -{ - NPCM7xxClockDividerState *s = opaque; - uint32_t freq; - - freq = s->divide(s); - clock_update_hz(s->clock_out, freq); -} - -static uint32_t divide_by_constant(NPCM7xxClockDividerState *s) -{ - return clock_get_hz(s->clock_in) / s->divisor; -} - -static uint32_t divide_by_reg_divisor(NPCM7xxClockDividerState *s) -{ - return clock_get_hz(s->clock_in) / - (extract32(s->clk->regs[s->reg], s->offset, s->len) + 1); -} - -static uint32_t divide_by_reg_divisor_times_2(NPCM7xxClockDividerState *s) -{ - return divide_by_reg_divisor(s) / 2; -} - -static uint32_t shift_by_reg_divisor(NPCM7xxClockDividerState *s) -{ - return clock_get_hz(s->clock_in) >> - extract32(s->clk->regs[s->reg], s->offset, s->len); -} - -static NPCM7xxClockPLL find_pll_by_reg(enum NPCM7xxCLKRegisters reg) -{ - switch (reg) { - case NPCM7XX_CLK_PLLCON0: - return NPCM7XX_CLOCK_PLL0; - case NPCM7XX_CLK_PLLCON1: - return NPCM7XX_CLOCK_PLL1; - case NPCM7XX_CLK_PLLCON2: - return NPCM7XX_CLOCK_PLL2; - case NPCM7XX_CLK_PLLCONG: - return NPCM7XX_CLOCK_PLLG; - default: - g_assert_not_reached(); - } -} - -static void npcm7xx_clk_update_all_plls(NPCM7xxCLKState *clk) -{ - int i; - - for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) { - npcm7xx_clk_update_pll(&clk->plls[i]); - } -} - -static void npcm7xx_clk_update_all_sels(NPCM7xxCLKState *clk) -{ - int i; - - for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) { - npcm7xx_clk_update_sel(&clk->sels[i]); - } -} - -static void npcm7xx_clk_update_all_dividers(NPCM7xxCLKState *clk) -{ - int i; - - for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) { - npcm7xx_clk_update_divider(&clk->dividers[i]); - } -} - -static void npcm7xx_clk_update_all_clocks(NPCM7xxCLKState *clk) -{ - clock_update_hz(clk->clkref, NPCM7XX_CLOCK_REF_HZ); - npcm7xx_clk_update_all_plls(clk); - npcm7xx_clk_update_all_sels(clk); - npcm7xx_clk_update_all_dividers(clk); -} - -/* Types of clock sources. */ -typedef enum ClockSrcType { - CLKSRC_REF, - CLKSRC_PLL, - CLKSRC_SEL, - CLKSRC_DIV, -} ClockSrcType; - -typedef struct PLLInitInfo { - const char *name; - ClockSrcType src_type; - int src_index; - int reg; - const char *public_name; -} PLLInitInfo; - -typedef struct SELInitInfo { - const char *name; - uint8_t input_size; - ClockSrcType src_type[NPCM7XX_CLK_SEL_MAX_INPUT]; - int src_index[NPCM7XX_CLK_SEL_MAX_INPUT]; - int offset; - int len; - const char *public_name; -} SELInitInfo; - -typedef struct DividerInitInfo { - const char *name; - ClockSrcType src_type; - int src_index; - uint32_t (*divide)(NPCM7xxClockDividerState *s); - int reg; /* not used when type == CONSTANT */ - int offset; /* not used when type == CONSTANT */ - int len; /* not used when type == CONSTANT */ - int divisor; /* used only when type == CONSTANT */ - const char *public_name; -} DividerInitInfo; - -static const PLLInitInfo pll_init_info_list[] = { - [NPCM7XX_CLOCK_PLL0] = { - .name = "pll0", - .src_type = CLKSRC_REF, - .reg = NPCM7XX_CLK_PLLCON0, - }, - [NPCM7XX_CLOCK_PLL1] = { - .name = "pll1", - .src_type = CLKSRC_REF, - .reg = NPCM7XX_CLK_PLLCON1, - }, - [NPCM7XX_CLOCK_PLL2] = { - .name = "pll2", - .src_type = CLKSRC_REF, - .reg = NPCM7XX_CLK_PLLCON2, - }, - [NPCM7XX_CLOCK_PLLG] = { - .name = "pllg", - .src_type = CLKSRC_REF, - .reg = NPCM7XX_CLK_PLLCONG, - }, -}; - -static const SELInitInfo sel_init_info_list[] = { - [NPCM7XX_CLOCK_PIXCKSEL] = { - .name = "pixcksel", - .input_size = 2, - .src_type = {CLKSRC_PLL, CLKSRC_REF}, - .src_index = {NPCM7XX_CLOCK_PLLG, 0}, - .offset = 5, - .len = 1, - .public_name = "pixel-clock", - }, - [NPCM7XX_CLOCK_MCCKSEL] = { - .name = "mccksel", - .input_size = 4, - .src_type = {CLKSRC_DIV, CLKSRC_REF, CLKSRC_REF, - /*MCBPCK, shouldn't be used in normal operation*/ - CLKSRC_REF}, - .src_index = {NPCM7XX_CLOCK_PLL1D2, 0, 0, 0}, - .offset = 12, - .len = 2, - .public_name = "mc-phy-clock", - }, - [NPCM7XX_CLOCK_CPUCKSEL] = { - .name = "cpucksel", - .input_size = 4, - .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, - /*SYSBPCK, shouldn't be used in normal operation*/ - CLKSRC_REF}, - .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, 0}, - .offset = 0, - .len = 2, - .public_name = "system-clock", - }, - [NPCM7XX_CLOCK_CLKOUTSEL] = { - .name = "clkoutsel", - .input_size = 5, - .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, - CLKSRC_PLL, CLKSRC_DIV}, - .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, - NPCM7XX_CLOCK_PLLG, NPCM7XX_CLOCK_PLL2D2}, - .offset = 18, - .len = 3, - .public_name = "tock", - }, - [NPCM7XX_CLOCK_UARTCKSEL] = { - .name = "uartcksel", - .input_size = 4, - .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV}, - .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, - NPCM7XX_CLOCK_PLL2D2}, - .offset = 8, - .len = 2, - }, - [NPCM7XX_CLOCK_TIMCKSEL] = { - .name = "timcksel", - .input_size = 4, - .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV}, - .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, - NPCM7XX_CLOCK_PLL2D2}, - .offset = 14, - .len = 2, - }, - [NPCM7XX_CLOCK_SDCKSEL] = { - .name = "sdcksel", - .input_size = 4, - .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV}, - .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, - NPCM7XX_CLOCK_PLL2D2}, - .offset = 6, - .len = 2, - }, - [NPCM7XX_CLOCK_GFXMSEL] = { - .name = "gfxmksel", - .input_size = 2, - .src_type = {CLKSRC_REF, CLKSRC_PLL}, - .src_index = {0, NPCM7XX_CLOCK_PLL2}, - .offset = 21, - .len = 1, - }, - [NPCM7XX_CLOCK_SUCKSEL] = { - .name = "sucksel", - .input_size = 4, - .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV}, - .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, - NPCM7XX_CLOCK_PLL2D2}, - .offset = 10, - .len = 2, - }, -}; - -static const DividerInitInfo divider_init_info_list[] = { - [NPCM7XX_CLOCK_PLL1D2] = { - .name = "pll1d2", - .src_type = CLKSRC_PLL, - .src_index = NPCM7XX_CLOCK_PLL1, - .divide = divide_by_constant, - .divisor = 2, - }, - [NPCM7XX_CLOCK_PLL2D2] = { - .name = "pll2d2", - .src_type = CLKSRC_PLL, - .src_index = NPCM7XX_CLOCK_PLL2, - .divide = divide_by_constant, - .divisor = 2, - }, - [NPCM7XX_CLOCK_MC_DIVIDER] = { - .name = "mc-divider", - .src_type = CLKSRC_SEL, - .src_index = NPCM7XX_CLOCK_MCCKSEL, - .divide = divide_by_constant, - .divisor = 2, - .public_name = "mc-clock" - }, - [NPCM7XX_CLOCK_AXI_DIVIDER] = { - .name = "axi-divider", - .src_type = CLKSRC_SEL, - .src_index = NPCM7XX_CLOCK_CPUCKSEL, - .divide = shift_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV1, - .offset = 0, - .len = 1, - .public_name = "clk2" - }, - [NPCM7XX_CLOCK_AHB_DIVIDER] = { - .name = "ahb-divider", - .src_type = CLKSRC_DIV, - .src_index = NPCM7XX_CLOCK_AXI_DIVIDER, - .divide = divide_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV1, - .offset = 26, - .len = 2, - .public_name = "clk4" - }, - [NPCM7XX_CLOCK_AHB3_DIVIDER] = { - .name = "ahb3-divider", - .src_type = CLKSRC_DIV, - .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, - .divide = divide_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV1, - .offset = 6, - .len = 5, - .public_name = "ahb3-spi3-clock" - }, - [NPCM7XX_CLOCK_SPI0_DIVIDER] = { - .name = "spi0-divider", - .src_type = CLKSRC_DIV, - .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, - .divide = divide_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV3, - .offset = 6, - .len = 5, - .public_name = "spi0-clock", - }, - [NPCM7XX_CLOCK_SPIX_DIVIDER] = { - .name = "spix-divider", - .src_type = CLKSRC_DIV, - .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, - .divide = divide_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV3, - .offset = 1, - .len = 5, - .public_name = "spix-clock", - }, - [NPCM7XX_CLOCK_APB1_DIVIDER] = { - .name = "apb1-divider", - .src_type = CLKSRC_DIV, - .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, - .divide = shift_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV2, - .offset = 24, - .len = 2, - .public_name = "apb1-clock", - }, - [NPCM7XX_CLOCK_APB2_DIVIDER] = { - .name = "apb2-divider", - .src_type = CLKSRC_DIV, - .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, - .divide = shift_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV2, - .offset = 26, - .len = 2, - .public_name = "apb2-clock", - }, - [NPCM7XX_CLOCK_APB3_DIVIDER] = { - .name = "apb3-divider", - .src_type = CLKSRC_DIV, - .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, - .divide = shift_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV2, - .offset = 28, - .len = 2, - .public_name = "apb3-clock", - }, - [NPCM7XX_CLOCK_APB4_DIVIDER] = { - .name = "apb4-divider", - .src_type = CLKSRC_DIV, - .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, - .divide = shift_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV2, - .offset = 30, - .len = 2, - .public_name = "apb4-clock", - }, - [NPCM7XX_CLOCK_APB5_DIVIDER] = { - .name = "apb5-divider", - .src_type = CLKSRC_DIV, - .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, - .divide = shift_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV2, - .offset = 22, - .len = 2, - .public_name = "apb5-clock", - }, - [NPCM7XX_CLOCK_CLKOUT_DIVIDER] = { - .name = "clkout-divider", - .src_type = CLKSRC_SEL, - .src_index = NPCM7XX_CLOCK_CLKOUTSEL, - .divide = divide_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV2, - .offset = 16, - .len = 5, - .public_name = "clkout", - }, - [NPCM7XX_CLOCK_UART_DIVIDER] = { - .name = "uart-divider", - .src_type = CLKSRC_SEL, - .src_index = NPCM7XX_CLOCK_UARTCKSEL, - .divide = divide_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV1, - .offset = 16, - .len = 5, - .public_name = "uart-clock", - }, - [NPCM7XX_CLOCK_TIMER_DIVIDER] = { - .name = "timer-divider", - .src_type = CLKSRC_SEL, - .src_index = NPCM7XX_CLOCK_TIMCKSEL, - .divide = divide_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV1, - .offset = 21, - .len = 5, - .public_name = "timer-clock", - }, - [NPCM7XX_CLOCK_ADC_DIVIDER] = { - .name = "adc-divider", - .src_type = CLKSRC_DIV, - .src_index = NPCM7XX_CLOCK_TIMER_DIVIDER, - .divide = shift_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV1, - .offset = 28, - .len = 3, - .public_name = "adc-clock", - }, - [NPCM7XX_CLOCK_MMC_DIVIDER] = { - .name = "mmc-divider", - .src_type = CLKSRC_SEL, - .src_index = NPCM7XX_CLOCK_SDCKSEL, - .divide = divide_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV1, - .offset = 11, - .len = 5, - .public_name = "mmc-clock", - }, - [NPCM7XX_CLOCK_SDHC_DIVIDER] = { - .name = "sdhc-divider", - .src_type = CLKSRC_SEL, - .src_index = NPCM7XX_CLOCK_SDCKSEL, - .divide = divide_by_reg_divisor_times_2, - .reg = NPCM7XX_CLK_CLKDIV2, - .offset = 0, - .len = 4, - .public_name = "sdhc-clock", - }, - [NPCM7XX_CLOCK_GFXM_DIVIDER] = { - .name = "gfxm-divider", - .src_type = CLKSRC_SEL, - .src_index = NPCM7XX_CLOCK_GFXMSEL, - .divide = divide_by_constant, - .divisor = 3, - .public_name = "gfxm-clock", - }, - [NPCM7XX_CLOCK_UTMI_DIVIDER] = { - .name = "utmi-divider", - .src_type = CLKSRC_SEL, - .src_index = NPCM7XX_CLOCK_SUCKSEL, - .divide = divide_by_reg_divisor, - .reg = NPCM7XX_CLK_CLKDIV2, - .offset = 8, - .len = 5, - .public_name = "utmi-clock", - }, -}; - -static void npcm7xx_clk_update_pll_cb(void *opaque, ClockEvent event) -{ - npcm7xx_clk_update_pll(opaque); -} - -static void npcm7xx_clk_pll_init(Object *obj) -{ - NPCM7xxClockPLLState *pll = NPCM7XX_CLOCK_PLL(obj); - - pll->clock_in = qdev_init_clock_in(DEVICE(pll), "clock-in", - npcm7xx_clk_update_pll_cb, pll, - ClockUpdate); - pll->clock_out = qdev_init_clock_out(DEVICE(pll), "clock-out"); -} - -static void npcm7xx_clk_update_sel_cb(void *opaque, ClockEvent event) -{ - npcm7xx_clk_update_sel(opaque); -} - -static void npcm7xx_clk_sel_init(Object *obj) -{ - int i; - NPCM7xxClockSELState *sel = NPCM7XX_CLOCK_SEL(obj); - - for (i = 0; i < NPCM7XX_CLK_SEL_MAX_INPUT; ++i) { - g_autofree char *s = g_strdup_printf("clock-in[%d]", i); - sel->clock_in[i] = qdev_init_clock_in(DEVICE(sel), s, - npcm7xx_clk_update_sel_cb, sel, ClockUpdate); - } - sel->clock_out = qdev_init_clock_out(DEVICE(sel), "clock-out"); -} - -static void npcm7xx_clk_update_divider_cb(void *opaque, ClockEvent event) -{ - npcm7xx_clk_update_divider(opaque); -} - -static void npcm7xx_clk_divider_init(Object *obj) -{ - NPCM7xxClockDividerState *div = NPCM7XX_CLOCK_DIVIDER(obj); - - div->clock_in = qdev_init_clock_in(DEVICE(div), "clock-in", - npcm7xx_clk_update_divider_cb, - div, ClockUpdate); - div->clock_out = qdev_init_clock_out(DEVICE(div), "clock-out"); -} - -static void npcm7xx_init_clock_pll(NPCM7xxClockPLLState *pll, - NPCM7xxCLKState *clk, const PLLInitInfo *init_info) -{ - pll->name = init_info->name; - pll->clk = clk; - pll->reg = init_info->reg; - if (init_info->public_name != NULL) { - qdev_alias_clock(DEVICE(pll), "clock-out", DEVICE(clk), - init_info->public_name); - } -} - -static void npcm7xx_init_clock_sel(NPCM7xxClockSELState *sel, - NPCM7xxCLKState *clk, const SELInitInfo *init_info) -{ - int input_size = init_info->input_size; - - sel->name = init_info->name; - sel->clk = clk; - sel->input_size = init_info->input_size; - g_assert(input_size <= NPCM7XX_CLK_SEL_MAX_INPUT); - sel->offset = init_info->offset; - sel->len = init_info->len; - if (init_info->public_name != NULL) { - qdev_alias_clock(DEVICE(sel), "clock-out", DEVICE(clk), - init_info->public_name); - } -} - -static void npcm7xx_init_clock_divider(NPCM7xxClockDividerState *div, - NPCM7xxCLKState *clk, const DividerInitInfo *init_info) -{ - div->name = init_info->name; - div->clk = clk; - - div->divide = init_info->divide; - if (div->divide == divide_by_constant) { - div->divisor = init_info->divisor; - } else { - div->reg = init_info->reg; - div->offset = init_info->offset; - div->len = init_info->len; - } - if (init_info->public_name != NULL) { - qdev_alias_clock(DEVICE(div), "clock-out", DEVICE(clk), - init_info->public_name); - } -} - -static Clock *npcm7xx_get_clock(NPCM7xxCLKState *clk, ClockSrcType type, - int index) -{ - switch (type) { - case CLKSRC_REF: - return clk->clkref; - case CLKSRC_PLL: - return clk->plls[index].clock_out; - case CLKSRC_SEL: - return clk->sels[index].clock_out; - case CLKSRC_DIV: - return clk->dividers[index].clock_out; - default: - g_assert_not_reached(); - } -} - -static void npcm7xx_connect_clocks(NPCM7xxCLKState *clk) -{ - int i, j; - Clock *src; - - for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) { - src = npcm7xx_get_clock(clk, pll_init_info_list[i].src_type, - pll_init_info_list[i].src_index); - clock_set_source(clk->plls[i].clock_in, src); - } - for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) { - for (j = 0; j < sel_init_info_list[i].input_size; ++j) { - src = npcm7xx_get_clock(clk, sel_init_info_list[i].src_type[j], - sel_init_info_list[i].src_index[j]); - clock_set_source(clk->sels[i].clock_in[j], src); - } - } - for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) { - src = npcm7xx_get_clock(clk, divider_init_info_list[i].src_type, - divider_init_info_list[i].src_index); - clock_set_source(clk->dividers[i].clock_in, src); - } -} - -static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size) -{ - uint32_t reg = offset / sizeof(uint32_t); - NPCM7xxCLKState *s = opaque; - int64_t now_ns; - uint32_t value = 0; - - if (reg >= NPCM7XX_CLK_NR_REGS) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: offset 0x%04" HWADDR_PRIx " out of range\n", - __func__, offset); - return 0; - } - - switch (reg) { - case NPCM7XX_CLK_SWRSTR: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n", - __func__, offset); - break; - - case NPCM7XX_CLK_SECCNT: - now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - value = (now_ns - s->ref_ns) / NANOSECONDS_PER_SECOND; - break; - - case NPCM7XX_CLK_CNTR25M: - now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - /* - * This register counts 25 MHz cycles, updating every 640 ns. It rolls - * over to zero every second. - * - * The 4 LSBs are always zero: (1e9 / 640) << 4 = 25000000. - */ - value = (((now_ns - s->ref_ns) / 640) << 4) % NPCM7XX_CLOCK_REF_HZ; - break; - - default: - value = s->regs[reg]; - break; - }; - - trace_npcm7xx_clk_read(offset, value); - - return value; -} - -static void npcm7xx_clk_write(void *opaque, hwaddr offset, - uint64_t v, unsigned size) -{ - uint32_t reg = offset / sizeof(uint32_t); - NPCM7xxCLKState *s = opaque; - uint32_t value = v; - - trace_npcm7xx_clk_write(offset, value); - - if (reg >= NPCM7XX_CLK_NR_REGS) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: offset 0x%04" HWADDR_PRIx " out of range\n", - __func__, offset); - return; - } - - switch (reg) { - case NPCM7XX_CLK_SWRSTR: - qemu_log_mask(LOG_UNIMP, "%s: SW reset not implemented: 0x%02x\n", - __func__, value); - value = 0; - break; - - case NPCM7XX_CLK_PLLCON0: - case NPCM7XX_CLK_PLLCON1: - case NPCM7XX_CLK_PLLCON2: - case NPCM7XX_CLK_PLLCONG: - if (value & PLLCON_PWDEN) { - /* Power down -- clear lock and indicate loss of lock */ - value &= ~PLLCON_LOKI; - value |= PLLCON_LOKS; - } else { - /* Normal mode -- assume always locked */ - value |= PLLCON_LOKI; - /* Keep LOKS unchanged unless cleared by writing 1 */ - if (value & PLLCON_LOKS) { - value &= ~PLLCON_LOKS; - } else { - value |= (value & PLLCON_LOKS); - } - } - /* Only update PLL when it is locked. */ - if (value & PLLCON_LOKI) { - npcm7xx_clk_update_pll(&s->plls[find_pll_by_reg(reg)]); - } - break; - - case NPCM7XX_CLK_CLKSEL: - npcm7xx_clk_update_all_sels(s); - break; - - case NPCM7XX_CLK_CLKDIV1: - case NPCM7XX_CLK_CLKDIV2: - case NPCM7XX_CLK_CLKDIV3: - npcm7xx_clk_update_all_dividers(s); - break; - - case NPCM7XX_CLK_CNTR25M: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n", - __func__, offset); - return; - } - - s->regs[reg] = value; -} - -/* Perform reset action triggered by a watchdog */ -static void npcm7xx_clk_perform_watchdog_reset(void *opaque, int n, - int level) -{ - NPCM7xxCLKState *clk = NPCM7XX_CLK(opaque); - uint32_t rcr; - - g_assert(n >= 0 && n <= NPCM7XX_NR_WATCHDOGS); - rcr = clk->regs[NPCM7XX_CLK_WD0RCR + n]; - if (rcr & NPCM7XX_CLK_WDRCR_CA9C) { - watchdog_perform_action(); - } else { - qemu_log_mask(LOG_UNIMP, - "%s: only CPU reset is implemented. (requested 0x%" PRIx32")\n", - __func__, rcr); - } -} - -static const struct MemoryRegionOps npcm7xx_clk_ops = { - .read = npcm7xx_clk_read, - .write = npcm7xx_clk_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - -static void npcm7xx_clk_enter_reset(Object *obj, ResetType type) -{ - NPCM7xxCLKState *s = NPCM7XX_CLK(obj); - - 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; - } - - /* - * 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) -{ - int i; - - s->clkref = qdev_init_clock_in(DEVICE(s), "clkref", NULL, NULL, 0); - - /* First pass: init all converter modules */ - QEMU_BUILD_BUG_ON(ARRAY_SIZE(pll_init_info_list) != NPCM7XX_CLOCK_NR_PLLS); - QEMU_BUILD_BUG_ON(ARRAY_SIZE(sel_init_info_list) != NPCM7XX_CLOCK_NR_SELS); - QEMU_BUILD_BUG_ON(ARRAY_SIZE(divider_init_info_list) - != NPCM7XX_CLOCK_NR_DIVIDERS); - for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) { - object_initialize_child(OBJECT(s), pll_init_info_list[i].name, - &s->plls[i], TYPE_NPCM7XX_CLOCK_PLL); - npcm7xx_init_clock_pll(&s->plls[i], s, - &pll_init_info_list[i]); - } - for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) { - object_initialize_child(OBJECT(s), sel_init_info_list[i].name, - &s->sels[i], TYPE_NPCM7XX_CLOCK_SEL); - npcm7xx_init_clock_sel(&s->sels[i], s, - &sel_init_info_list[i]); - } - for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) { - object_initialize_child(OBJECT(s), divider_init_info_list[i].name, - &s->dividers[i], TYPE_NPCM7XX_CLOCK_DIVIDER); - npcm7xx_init_clock_divider(&s->dividers[i], s, - ÷r_init_info_list[i]); - } - - /* Second pass: connect converter modules */ - npcm7xx_connect_clocks(s); - - clock_update_hz(s->clkref, NPCM7XX_CLOCK_REF_HZ); -} - -static void npcm7xx_clk_init(Object *obj) -{ - NPCM7xxCLKState *s = NPCM7XX_CLK(obj); - - memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s, - TYPE_NPCM7XX_CLK, 4 * KiB); - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); -} - -static int npcm7xx_clk_post_load(void *opaque, int version_id) -{ - if (version_id >= 1) { - NPCM7xxCLKState *clk = opaque; - - npcm7xx_clk_update_all_clocks(clk); - } - - return 0; -} - -static void npcm7xx_clk_realize(DeviceState *dev, Error **errp) -{ - int i; - NPCM7xxCLKState *s = NPCM7XX_CLK(dev); - - qdev_init_gpio_in_named(DEVICE(s), npcm7xx_clk_perform_watchdog_reset, - NPCM7XX_WATCHDOG_RESET_GPIO_IN, NPCM7XX_NR_WATCHDOGS); - npcm7xx_clk_init_clock_hierarchy(s); - - /* Realize child devices */ - for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) { - if (!qdev_realize(DEVICE(&s->plls[i]), NULL, errp)) { - return; - } - } - for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) { - if (!qdev_realize(DEVICE(&s->sels[i]), NULL, errp)) { - return; - } - } - for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) { - if (!qdev_realize(DEVICE(&s->dividers[i]), NULL, errp)) { - return; - } - } -} - -static const VMStateDescription vmstate_npcm7xx_clk_pll = { - .name = "npcm7xx-clock-pll", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_CLOCK(clock_in, NPCM7xxClockPLLState), - VMSTATE_END_OF_LIST(), - }, -}; - -static const VMStateDescription vmstate_npcm7xx_clk_sel = { - .name = "npcm7xx-clock-sel", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(clock_in, NPCM7xxClockSELState, - NPCM7XX_CLK_SEL_MAX_INPUT, 0, vmstate_clock, Clock), - VMSTATE_END_OF_LIST(), - }, -}; - -static const VMStateDescription vmstate_npcm7xx_clk_divider = { - .name = "npcm7xx-clock-divider", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_CLOCK(clock_in, NPCM7xxClockDividerState), - VMSTATE_END_OF_LIST(), - }, -}; - -static const VMStateDescription vmstate_npcm7xx_clk = { - .name = "npcm7xx-clk", - .version_id = 1, - .minimum_version_id = 1, - .post_load = npcm7xx_clk_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, NPCM7xxCLKState, NPCM7XX_CLK_NR_REGS), - VMSTATE_INT64(ref_ns, NPCM7xxCLKState), - VMSTATE_CLOCK(clkref, NPCM7xxCLKState), - VMSTATE_END_OF_LIST(), - }, -}; - -static void npcm7xx_clk_pll_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->desc = "NPCM7xx Clock PLL Module"; - dc->vmsd = &vmstate_npcm7xx_clk_pll; -} - -static void npcm7xx_clk_sel_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->desc = "NPCM7xx Clock SEL Module"; - dc->vmsd = &vmstate_npcm7xx_clk_sel; -} - -static void npcm7xx_clk_divider_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->desc = "NPCM7xx Clock Divider Module"; - dc->vmsd = &vmstate_npcm7xx_clk_divider; -} - -static void npcm7xx_clk_class_init(ObjectClass *klass, void *data) -{ - ResettableClass *rc = RESETTABLE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - QEMU_BUILD_BUG_ON(NPCM7XX_CLK_REGS_END > NPCM7XX_CLK_NR_REGS); - - dc->desc = "NPCM7xx Clock Control Registers"; - dc->vmsd = &vmstate_npcm7xx_clk; - dc->realize = npcm7xx_clk_realize; - rc->phases.enter = npcm7xx_clk_enter_reset; -} - -static const TypeInfo npcm7xx_clk_pll_info = { - .name = TYPE_NPCM7XX_CLOCK_PLL, - .parent = TYPE_DEVICE, - .instance_size = sizeof(NPCM7xxClockPLLState), - .instance_init = npcm7xx_clk_pll_init, - .class_init = npcm7xx_clk_pll_class_init, -}; - -static const TypeInfo npcm7xx_clk_sel_info = { - .name = TYPE_NPCM7XX_CLOCK_SEL, - .parent = TYPE_DEVICE, - .instance_size = sizeof(NPCM7xxClockSELState), - .instance_init = npcm7xx_clk_sel_init, - .class_init = npcm7xx_clk_sel_class_init, -}; - -static const TypeInfo npcm7xx_clk_divider_info = { - .name = TYPE_NPCM7XX_CLOCK_DIVIDER, - .parent = TYPE_DEVICE, - .instance_size = sizeof(NPCM7xxClockDividerState), - .instance_init = npcm7xx_clk_divider_init, - .class_init = npcm7xx_clk_divider_class_init, -}; - -static const TypeInfo npcm7xx_clk_info = { - .name = TYPE_NPCM7XX_CLK, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(NPCM7xxCLKState), - .instance_init = npcm7xx_clk_init, - .class_init = npcm7xx_clk_class_init, -}; - -static void npcm7xx_clk_register_type(void) -{ - type_register_static(&npcm7xx_clk_pll_info); - type_register_static(&npcm7xx_clk_sel_info); - type_register_static(&npcm7xx_clk_divider_info); - type_register_static(&npcm7xx_clk_info); -} -type_init(npcm7xx_clk_register_type); diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c deleted file mode 100644 index eace9e1967a..00000000000 --- a/hw/misc/npcm7xx_gcr.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Nuvoton NPCM7xx System Global Control Registers. - * - * Copyright 2020 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/misc/npcm7xx_gcr.h" -#include "hw/qdev-properties.h" -#include "migration/vmstate.h" -#include "qapi/error.h" -#include "qemu/cutils.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "qemu/units.h" - -#include "trace.h" - -#define NPCM7XX_GCR_MIN_DRAM_SIZE (128 * MiB) -#define NPCM7XX_GCR_MAX_DRAM_SIZE (2 * GiB) - -enum NPCM7xxGCRRegisters { - NPCM7XX_GCR_PDID, - NPCM7XX_GCR_PWRON, - NPCM7XX_GCR_MFSEL1 = 0x0c / sizeof(uint32_t), - NPCM7XX_GCR_MFSEL2, - NPCM7XX_GCR_MISCPE, - NPCM7XX_GCR_SPSWC = 0x038 / sizeof(uint32_t), - NPCM7XX_GCR_INTCR, - NPCM7XX_GCR_INTSR, - NPCM7XX_GCR_HIFCR = 0x050 / sizeof(uint32_t), - NPCM7XX_GCR_INTCR2 = 0x060 / sizeof(uint32_t), - NPCM7XX_GCR_MFSEL3, - NPCM7XX_GCR_SRCNT, - NPCM7XX_GCR_RESSR, - NPCM7XX_GCR_RLOCKR1, - NPCM7XX_GCR_FLOCKR1, - NPCM7XX_GCR_DSCNT, - NPCM7XX_GCR_MDLR, - NPCM7XX_GCR_SCRPAD3, - NPCM7XX_GCR_SCRPAD2, - NPCM7XX_GCR_DAVCLVLR = 0x098 / sizeof(uint32_t), - NPCM7XX_GCR_INTCR3, - NPCM7XX_GCR_VSINTR = 0x0ac / sizeof(uint32_t), - NPCM7XX_GCR_MFSEL4, - NPCM7XX_GCR_CPBPNTR = 0x0c4 / sizeof(uint32_t), - NPCM7XX_GCR_CPCTL = 0x0d0 / sizeof(uint32_t), - NPCM7XX_GCR_CP2BST, - NPCM7XX_GCR_B2CPNT, - NPCM7XX_GCR_CPPCTL, - NPCM7XX_GCR_I2CSEGSEL, - NPCM7XX_GCR_I2CSEGCTL, - NPCM7XX_GCR_VSRCR, - NPCM7XX_GCR_MLOCKR, - NPCM7XX_GCR_SCRPAD = 0x013c / sizeof(uint32_t), - NPCM7XX_GCR_USB1PHYCTL, - NPCM7XX_GCR_USB2PHYCTL, - NPCM7XX_GCR_REGS_END, -}; - -static const uint32_t cold_reset_values[NPCM7XX_GCR_NR_REGS] = { - [NPCM7XX_GCR_PDID] = 0x04a92750, /* Poleg A1 */ - [NPCM7XX_GCR_MISCPE] = 0x0000ffff, - [NPCM7XX_GCR_SPSWC] = 0x00000003, - [NPCM7XX_GCR_INTCR] = 0x0000035e, - [NPCM7XX_GCR_HIFCR] = 0x0000004e, - [NPCM7XX_GCR_INTCR2] = (1U << 19), /* DDR initialized */ - [NPCM7XX_GCR_RESSR] = 0x80000000, - [NPCM7XX_GCR_DSCNT] = 0x000000c0, - [NPCM7XX_GCR_DAVCLVLR] = 0x5a00f3cf, - [NPCM7XX_GCR_SCRPAD] = 0x00000008, - [NPCM7XX_GCR_USB1PHYCTL] = 0x034730e4, - [NPCM7XX_GCR_USB2PHYCTL] = 0x034730e4, -}; - -static uint64_t npcm7xx_gcr_read(void *opaque, hwaddr offset, unsigned size) -{ - uint32_t reg = offset / sizeof(uint32_t); - NPCM7xxGCRState *s = opaque; - - if (reg >= NPCM7XX_GCR_NR_REGS) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: offset 0x%04" HWADDR_PRIx " out of range\n", - __func__, offset); - return 0; - } - - trace_npcm7xx_gcr_read(offset, s->regs[reg]); - - return s->regs[reg]; -} - -static void npcm7xx_gcr_write(void *opaque, hwaddr offset, - uint64_t v, unsigned size) -{ - uint32_t reg = offset / sizeof(uint32_t); - NPCM7xxGCRState *s = opaque; - uint32_t value = v; - - trace_npcm7xx_gcr_write(offset, value); - - if (reg >= NPCM7XX_GCR_NR_REGS) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: offset 0x%04" HWADDR_PRIx " out of range\n", - __func__, offset); - return; - } - - switch (reg) { - case NPCM7XX_GCR_PDID: - case NPCM7XX_GCR_PWRON: - case NPCM7XX_GCR_INTSR: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n", - __func__, offset); - return; - - case NPCM7XX_GCR_RESSR: - case NPCM7XX_GCR_CP2BST: - /* Write 1 to clear */ - value = s->regs[reg] & ~value; - break; - - case NPCM7XX_GCR_RLOCKR1: - case NPCM7XX_GCR_MDLR: - /* Write 1 to set */ - value |= s->regs[reg]; - break; - }; - - s->regs[reg] = value; -} - -static const struct MemoryRegionOps npcm7xx_gcr_ops = { - .read = npcm7xx_gcr_read, - .write = npcm7xx_gcr_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - -static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type) -{ - NPCM7xxGCRState *s = NPCM7XX_GCR(obj); - - 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; - } -} - -static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp) -{ - ERRP_GUARD(); - NPCM7xxGCRState *s = NPCM7XX_GCR(dev); - uint64_t dram_size; - Object *obj; - - obj = object_property_get_link(OBJECT(dev), "dram-mr", errp); - if (!obj) { - error_prepend(errp, "%s: required dram-mr link not found: ", __func__); - return; - } - dram_size = memory_region_size(MEMORY_REGION(obj)); - if (!is_power_of_2(dram_size) || - dram_size < NPCM7XX_GCR_MIN_DRAM_SIZE || - dram_size > NPCM7XX_GCR_MAX_DRAM_SIZE) { - g_autofree char *sz = size_to_str(dram_size); - g_autofree char *min_sz = size_to_str(NPCM7XX_GCR_MIN_DRAM_SIZE); - g_autofree char *max_sz = size_to_str(NPCM7XX_GCR_MAX_DRAM_SIZE); - error_setg(errp, "%s: unsupported DRAM size %s", __func__, sz); - error_append_hint(errp, - "DRAM size must be a power of two between %s and %s," - " inclusive.\n", min_sz, max_sz); - return; - } - - /* Power-on reset value */ - s->reset_intcr3 = 0x00001002; - - /* - * The GMMAP (Graphics Memory Map) field is used by u-boot to detect the - * DRAM size, and is normally initialized by the boot block as part of DRAM - * training. However, since we don't have a complete emulation of the - * memory controller and try to make it look like it has already been - * initialized, the boot block will skip this initialization, and we need - * to make sure this field is set correctly up front. - * - * WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 GiB of - * DRAM will be interpreted as 128 MiB. - * - * https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244 - */ - s->reset_intcr3 |= ctz64(dram_size / NPCM7XX_GCR_MIN_DRAM_SIZE) << 8; -} - -static void npcm7xx_gcr_init(Object *obj) -{ - NPCM7xxGCRState *s = NPCM7XX_GCR(obj); - - memory_region_init_io(&s->iomem, obj, &npcm7xx_gcr_ops, s, - TYPE_NPCM7XX_GCR, 4 * KiB); - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); -} - -static const VMStateDescription vmstate_npcm7xx_gcr = { - .name = "npcm7xx-gcr", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, NPCM7xxGCRState, NPCM7XX_GCR_NR_REGS), - VMSTATE_END_OF_LIST(), - }, -}; - -static Property npcm7xx_gcr_properties[] = { - DEFINE_PROP_UINT32("disabled-modules", NPCM7xxGCRState, reset_mdlr, 0), - DEFINE_PROP_UINT32("power-on-straps", NPCM7xxGCRState, reset_pwron, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data) -{ - ResettableClass *rc = RESETTABLE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - QEMU_BUILD_BUG_ON(NPCM7XX_GCR_REGS_END > NPCM7XX_GCR_NR_REGS); - - dc->desc = "NPCM7xx System Global Control Registers"; - dc->realize = npcm7xx_gcr_realize; - dc->vmsd = &vmstate_npcm7xx_gcr; - rc->phases.enter = npcm7xx_gcr_enter_reset; - - device_class_set_props(dc, npcm7xx_gcr_properties); -} - -static const TypeInfo npcm7xx_gcr_info = { - .name = TYPE_NPCM7XX_GCR, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(NPCM7xxGCRState), - .instance_init = npcm7xx_gcr_init, - .class_init = npcm7xx_gcr_class_init, -}; - -static void npcm7xx_gcr_register_type(void) -{ - type_register_static(&npcm7xx_gcr_info); -} -type_init(npcm7xx_gcr_register_type); diff --git a/hw/misc/npcm7xx_mft.c b/hw/misc/npcm7xx_mft.c index a30583a1b05..b35e971fe53 100644 --- a/hw/misc/npcm7xx_mft.c +++ b/hw/misc/npcm7xx_mft.c @@ -172,8 +172,9 @@ static NPCM7xxMFTCaptureState npcm7xx_mft_compute_cnt( * RPM = revolution/min. The time for one revlution (in ns) is * MINUTE_TO_NANOSECOND / RPM. */ - count = clock_ns_to_ticks(clock, (60 * NANOSECONDS_PER_SECOND) / - (rpm * NPCM7XX_MFT_PULSE_PER_REVOLUTION)); + count = clock_ns_to_ticks(clock, + (uint64_t)(60 * NANOSECONDS_PER_SECOND) / + ((uint64_t)rpm * NPCM7XX_MFT_PULSE_PER_REVOLUTION)); } if (count > NPCM7XX_MFT_MAX_CNT) { @@ -467,7 +468,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 +504,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), @@ -514,7 +515,7 @@ static const VMStateDescription vmstate_npcm7xx_mft = { }, }; -static void npcm7xx_mft_class_init(ObjectClass *klass, void *data) +static void npcm7xx_mft_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/npcm7xx_pwm.c b/hw/misc/npcm7xx_pwm.c index 2be5bd25c6c..2de18d09b82 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, @@ -543,7 +543,7 @@ static const VMStateDescription vmstate_npcm7xx_pwm_module = { }, }; -static void npcm7xx_pwm_class_init(ObjectClass *klass, void *data) +static void npcm7xx_pwm_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/npcm7xx_rng.c b/hw/misc/npcm7xx_rng.c index b01df7cdb25..7d47a1caa07 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), @@ -158,7 +158,7 @@ static const VMStateDescription vmstate_npcm7xx_rng = { }, }; -static void npcm7xx_rng_class_init(ObjectClass *klass, void *data) +static void npcm7xx_rng_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/npcm_clk.c b/hw/misc/npcm_clk.c new file mode 100644 index 00000000000..c48d40b4468 --- /dev/null +++ b/hw/misc/npcm_clk.c @@ -0,0 +1,1220 @@ +/* + * Nuvoton NPCM7xx/8xx Clock Control Registers. + * + * Copyright 2020 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/misc/npcm_clk.h" +#include "hw/timer/npcm7xx_timer.h" +#include "hw/qdev-clock.h" +#include "migration/vmstate.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/timer.h" +#include "qemu/units.h" +#include "trace.h" +#include "system/watchdog.h" + +/* + * The reference clock hz, and the SECCNT and CNTR25M registers in this module, + * is always 25 MHz. + */ +#define NPCM7XX_CLOCK_REF_HZ (25000000) + +/* Register Field Definitions */ +#define NPCM7XX_CLK_WDRCR_CA9C BIT(0) /* Cortex-A9 Cores */ + +#define PLLCON_LOKI BIT(31) +#define PLLCON_LOKS BIT(30) +#define PLLCON_PWDEN BIT(12) +#define PLLCON_FBDV(con) extract32((con), 16, 12) +#define PLLCON_OTDV2(con) extract32((con), 13, 3) +#define PLLCON_OTDV1(con) extract32((con), 8, 3) +#define PLLCON_INDV(con) extract32((con), 0, 6) + +enum NPCM7xxCLKRegisters { + NPCM7XX_CLK_CLKEN1, + NPCM7XX_CLK_CLKSEL, + NPCM7XX_CLK_CLKDIV1, + NPCM7XX_CLK_PLLCON0, + NPCM7XX_CLK_PLLCON1, + NPCM7XX_CLK_SWRSTR, + NPCM7XX_CLK_IPSRST1 = 0x20 / sizeof(uint32_t), + NPCM7XX_CLK_IPSRST2, + NPCM7XX_CLK_CLKEN2, + NPCM7XX_CLK_CLKDIV2, + NPCM7XX_CLK_CLKEN3, + NPCM7XX_CLK_IPSRST3, + NPCM7XX_CLK_WD0RCR, + NPCM7XX_CLK_WD1RCR, + NPCM7XX_CLK_WD2RCR, + NPCM7XX_CLK_SWRSTC1, + NPCM7XX_CLK_SWRSTC2, + NPCM7XX_CLK_SWRSTC3, + NPCM7XX_CLK_SWRSTC4, + NPCM7XX_CLK_PLLCON2, + NPCM7XX_CLK_CLKDIV3, + NPCM7XX_CLK_CORSTC, + NPCM7XX_CLK_PLLCONG, + NPCM7XX_CLK_AHBCKFI, + NPCM7XX_CLK_SECCNT, + NPCM7XX_CLK_CNTR25M, +}; + +enum NPCM8xxCLKRegisters { + NPCM8XX_CLK_CLKEN1, + NPCM8XX_CLK_CLKSEL, + NPCM8XX_CLK_CLKDIV1, + NPCM8XX_CLK_PLLCON0, + NPCM8XX_CLK_PLLCON1, + NPCM8XX_CLK_SWRSTR, + NPCM8XX_CLK_IPSRST1 = 0x20 / sizeof(uint32_t), + NPCM8XX_CLK_IPSRST2, + NPCM8XX_CLK_CLKEN2, + NPCM8XX_CLK_CLKDIV2, + NPCM8XX_CLK_CLKEN3, + NPCM8XX_CLK_IPSRST3, + NPCM8XX_CLK_WD0RCR, + NPCM8XX_CLK_WD1RCR, + NPCM8XX_CLK_WD2RCR, + NPCM8XX_CLK_SWRSTC1, + NPCM8XX_CLK_SWRSTC2, + NPCM8XX_CLK_SWRSTC3, + NPCM8XX_CLK_TIPRSTC, + NPCM8XX_CLK_PLLCON2, + NPCM8XX_CLK_CLKDIV3, + NPCM8XX_CLK_CORSTC, + NPCM8XX_CLK_PLLCONG, + NPCM8XX_CLK_AHBCKFI, + NPCM8XX_CLK_SECCNT, + NPCM8XX_CLK_CNTR25M, + /* Registers unique to NPCM8XX SoC */ + NPCM8XX_CLK_CLKEN4, + NPCM8XX_CLK_IPSRST4, + NPCM8XX_CLK_BUSTO, + NPCM8XX_CLK_CLKDIV4, + NPCM8XX_CLK_WD0RCRB, + NPCM8XX_CLK_WD1RCRB, + NPCM8XX_CLK_WD2RCRB, + NPCM8XX_CLK_SWRSTC1B, + NPCM8XX_CLK_SWRSTC2B, + NPCM8XX_CLK_SWRSTC3B, + NPCM8XX_CLK_TIPRSTCB, + NPCM8XX_CLK_CORSTCB, + NPCM8XX_CLK_IPSRSTDIS1, + NPCM8XX_CLK_IPSRSTDIS2, + NPCM8XX_CLK_IPSRSTDIS3, + NPCM8XX_CLK_IPSRSTDIS4, + NPCM8XX_CLK_CLKENDIS1, + NPCM8XX_CLK_CLKENDIS2, + NPCM8XX_CLK_CLKENDIS3, + NPCM8XX_CLK_CLKENDIS4, + NPCM8XX_CLK_THRTL_CNT, +}; + +/* + * These reset values were taken from version 0.91 of the NPCM750R data sheet. + * + * All are loaded on power-up reset. CLKENx and SWRSTR should also be loaded on + * core domain reset, but this reset type is not yet supported by QEMU. + */ +static const uint32_t npcm7xx_cold_reset_values[NPCM7XX_CLK_NR_REGS] = { + [NPCM7XX_CLK_CLKEN1] = 0xffffffff, + [NPCM7XX_CLK_CLKSEL] = 0x004aaaaa, + [NPCM7XX_CLK_CLKDIV1] = 0x5413f855, + [NPCM7XX_CLK_PLLCON0] = 0x00222101 | PLLCON_LOKI, + [NPCM7XX_CLK_PLLCON1] = 0x00202101 | PLLCON_LOKI, + [NPCM7XX_CLK_IPSRST1] = 0x00001000, + [NPCM7XX_CLK_IPSRST2] = 0x80000000, + [NPCM7XX_CLK_CLKEN2] = 0xffffffff, + [NPCM7XX_CLK_CLKDIV2] = 0xaa4f8f9f, + [NPCM7XX_CLK_CLKEN3] = 0xffffffff, + [NPCM7XX_CLK_IPSRST3] = 0x03000000, + [NPCM7XX_CLK_WD0RCR] = 0xffffffff, + [NPCM7XX_CLK_WD1RCR] = 0xffffffff, + [NPCM7XX_CLK_WD2RCR] = 0xffffffff, + [NPCM7XX_CLK_SWRSTC1] = 0x00000003, + [NPCM7XX_CLK_PLLCON2] = 0x00c02105 | PLLCON_LOKI, + [NPCM7XX_CLK_CORSTC] = 0x04000003, + [NPCM7XX_CLK_PLLCONG] = 0x01228606 | PLLCON_LOKI, + [NPCM7XX_CLK_AHBCKFI] = 0x000000c8, +}; + +/* + * These reset values were taken from version 0.92 of the NPCM8xx data sheet. + */ +static const uint32_t npcm8xx_cold_reset_values[NPCM8XX_CLK_NR_REGS] = { + [NPCM8XX_CLK_CLKEN1] = 0xffffffff, + [NPCM8XX_CLK_CLKSEL] = 0x154aaaaa, + [NPCM8XX_CLK_CLKDIV1] = 0x5413f855, + [NPCM8XX_CLK_PLLCON0] = 0x00222101 | PLLCON_LOKI, + [NPCM8XX_CLK_PLLCON1] = 0x00202101 | PLLCON_LOKI, + [NPCM8XX_CLK_IPSRST1] = 0x00001000, + [NPCM8XX_CLK_IPSRST2] = 0x80000000, + [NPCM8XX_CLK_CLKEN2] = 0xffffffff, + [NPCM8XX_CLK_CLKDIV2] = 0xaa4f8f9f, + [NPCM8XX_CLK_CLKEN3] = 0xffffffff, + [NPCM8XX_CLK_IPSRST3] = 0x03000000, + [NPCM8XX_CLK_WD0RCR] = 0xffffffff, + [NPCM8XX_CLK_WD1RCR] = 0xffffffff, + [NPCM8XX_CLK_WD2RCR] = 0xffffffff, + [NPCM8XX_CLK_SWRSTC1] = 0x00000003, + [NPCM8XX_CLK_SWRSTC2] = 0x00000001, + [NPCM8XX_CLK_SWRSTC3] = 0x00000001, + [NPCM8XX_CLK_TIPRSTC] = 0x00000001, + [NPCM8XX_CLK_PLLCON2] = 0x00c02105 | PLLCON_LOKI, + [NPCM8XX_CLK_CLKDIV3] = 0x00009100, + [NPCM8XX_CLK_CORSTC] = 0x04000003, + [NPCM8XX_CLK_PLLCONG] = 0x01228606 | PLLCON_LOKI, + [NPCM8XX_CLK_AHBCKFI] = 0x000000c8, + [NPCM8XX_CLK_CLKEN4] = 0xffffffff, + [NPCM8XX_CLK_CLKDIV4] = 0x70009000, + [NPCM8XX_CLK_IPSRST4] = 0x02000000, + [NPCM8XX_CLK_WD0RCRB] = 0xfffffe71, + [NPCM8XX_CLK_WD1RCRB] = 0xfffffe71, + [NPCM8XX_CLK_WD2RCRB] = 0xfffffe71, + [NPCM8XX_CLK_SWRSTC1B] = 0xfffffe71, + [NPCM8XX_CLK_SWRSTC2B] = 0xfffffe71, + [NPCM8XX_CLK_SWRSTC3B] = 0xfffffe71, + [NPCM8XX_CLK_TIPRSTCB] = 0xfffffe71, + [NPCM8XX_CLK_CORSTCB] = 0xfffffe71, +}; + +/* The number of watchdogs that can trigger a reset. */ +#define NPCM7XX_NR_WATCHDOGS (3) + +/* Clock converter functions */ + +#define TYPE_NPCM7XX_CLOCK_PLL "npcm7xx-clock-pll" +#define NPCM7XX_CLOCK_PLL(obj) OBJECT_CHECK(NPCM7xxClockPLLState, \ + (obj), TYPE_NPCM7XX_CLOCK_PLL) +#define TYPE_NPCM7XX_CLOCK_SEL "npcm7xx-clock-sel" +#define NPCM7XX_CLOCK_SEL(obj) OBJECT_CHECK(NPCM7xxClockSELState, \ + (obj), TYPE_NPCM7XX_CLOCK_SEL) +#define TYPE_NPCM7XX_CLOCK_DIVIDER "npcm7xx-clock-divider" +#define NPCM7XX_CLOCK_DIVIDER(obj) OBJECT_CHECK(NPCM7xxClockDividerState, \ + (obj), TYPE_NPCM7XX_CLOCK_DIVIDER) + +static void npcm7xx_clk_update_pll(void *opaque) +{ + NPCM7xxClockPLLState *s = opaque; + uint32_t con = s->clk->regs[s->reg]; + uint64_t freq; + + /* The PLL is grounded if it is not locked yet. */ + if (con & PLLCON_LOKI) { + freq = clock_get_hz(s->clock_in); + freq *= PLLCON_FBDV(con); + freq /= PLLCON_INDV(con) * PLLCON_OTDV1(con) * PLLCON_OTDV2(con); + } else { + freq = 0; + } + + clock_update_hz(s->clock_out, freq); +} + +static void npcm7xx_clk_update_sel(void *opaque) +{ + NPCM7xxClockSELState *s = opaque; + uint32_t index = extract32(s->clk->regs[NPCM7XX_CLK_CLKSEL], s->offset, + s->len); + + if (index >= s->input_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SEL index: %u out of range\n", + __func__, index); + index = 0; + } + clock_update_hz(s->clock_out, clock_get_hz(s->clock_in[index])); +} + +static void npcm7xx_clk_update_divider(void *opaque) +{ + NPCM7xxClockDividerState *s = opaque; + uint32_t freq; + + freq = s->divide(s); + clock_update_hz(s->clock_out, freq); +} + +static uint32_t divide_by_constant(NPCM7xxClockDividerState *s) +{ + return clock_get_hz(s->clock_in) / s->divisor; +} + +static uint32_t divide_by_reg_divisor(NPCM7xxClockDividerState *s) +{ + return clock_get_hz(s->clock_in) / + (extract32(s->clk->regs[s->reg], s->offset, s->len) + 1); +} + +static uint32_t divide_by_reg_divisor_times_2(NPCM7xxClockDividerState *s) +{ + return divide_by_reg_divisor(s) / 2; +} + +static uint32_t shift_by_reg_divisor(NPCM7xxClockDividerState *s) +{ + return clock_get_hz(s->clock_in) >> + extract32(s->clk->regs[s->reg], s->offset, s->len); +} + +static NPCM7xxClockPLL find_pll_by_reg(enum NPCM7xxCLKRegisters reg) +{ + switch (reg) { + case NPCM7XX_CLK_PLLCON0: + return NPCM7XX_CLOCK_PLL0; + case NPCM7XX_CLK_PLLCON1: + return NPCM7XX_CLOCK_PLL1; + case NPCM7XX_CLK_PLLCON2: + return NPCM7XX_CLOCK_PLL2; + case NPCM7XX_CLK_PLLCONG: + return NPCM7XX_CLOCK_PLLG; + default: + g_assert_not_reached(); + } +} + +static void npcm7xx_clk_update_all_plls(NPCMCLKState *clk) +{ + int i; + + for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) { + npcm7xx_clk_update_pll(&clk->plls[i]); + } +} + +static void npcm7xx_clk_update_all_sels(NPCMCLKState *clk) +{ + int i; + + for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) { + npcm7xx_clk_update_sel(&clk->sels[i]); + } +} + +static void npcm7xx_clk_update_all_dividers(NPCMCLKState *clk) +{ + int i; + + for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) { + npcm7xx_clk_update_divider(&clk->dividers[i]); + } +} + +static void npcm7xx_clk_update_all_clocks(NPCMCLKState *clk) +{ + clock_update_hz(clk->clkref, NPCM7XX_CLOCK_REF_HZ); + npcm7xx_clk_update_all_plls(clk); + npcm7xx_clk_update_all_sels(clk); + npcm7xx_clk_update_all_dividers(clk); +} + +/* Types of clock sources. */ +typedef enum ClockSrcType { + CLKSRC_REF, + CLKSRC_PLL, + CLKSRC_SEL, + CLKSRC_DIV, +} ClockSrcType; + +typedef struct PLLInitInfo { + const char *name; + ClockSrcType src_type; + int src_index; + int reg; + const char *public_name; +} PLLInitInfo; + +typedef struct SELInitInfo { + const char *name; + uint8_t input_size; + ClockSrcType src_type[NPCM7XX_CLK_SEL_MAX_INPUT]; + int src_index[NPCM7XX_CLK_SEL_MAX_INPUT]; + int offset; + int len; + const char *public_name; +} SELInitInfo; + +typedef struct DividerInitInfo { + const char *name; + ClockSrcType src_type; + int src_index; + uint32_t (*divide)(NPCM7xxClockDividerState *s); + int reg; /* not used when type == CONSTANT */ + int offset; /* not used when type == CONSTANT */ + int len; /* not used when type == CONSTANT */ + int divisor; /* used only when type == CONSTANT */ + const char *public_name; +} DividerInitInfo; + +static const PLLInitInfo pll_init_info_list[] = { + [NPCM7XX_CLOCK_PLL0] = { + .name = "pll0", + .src_type = CLKSRC_REF, + .reg = NPCM7XX_CLK_PLLCON0, + }, + [NPCM7XX_CLOCK_PLL1] = { + .name = "pll1", + .src_type = CLKSRC_REF, + .reg = NPCM7XX_CLK_PLLCON1, + }, + [NPCM7XX_CLOCK_PLL2] = { + .name = "pll2", + .src_type = CLKSRC_REF, + .reg = NPCM7XX_CLK_PLLCON2, + }, + [NPCM7XX_CLOCK_PLLG] = { + .name = "pllg", + .src_type = CLKSRC_REF, + .reg = NPCM7XX_CLK_PLLCONG, + }, +}; + +static const SELInitInfo sel_init_info_list[] = { + [NPCM7XX_CLOCK_PIXCKSEL] = { + .name = "pixcksel", + .input_size = 2, + .src_type = {CLKSRC_PLL, CLKSRC_REF}, + .src_index = {NPCM7XX_CLOCK_PLLG, 0}, + .offset = 5, + .len = 1, + .public_name = "pixel-clock", + }, + [NPCM7XX_CLOCK_MCCKSEL] = { + .name = "mccksel", + .input_size = 4, + .src_type = {CLKSRC_DIV, CLKSRC_REF, CLKSRC_REF, + /*MCBPCK, shouldn't be used in normal operation*/ + CLKSRC_REF}, + .src_index = {NPCM7XX_CLOCK_PLL1D2, 0, 0, 0}, + .offset = 12, + .len = 2, + .public_name = "mc-phy-clock", + }, + [NPCM7XX_CLOCK_CPUCKSEL] = { + .name = "cpucksel", + .input_size = 4, + .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, + /*SYSBPCK, shouldn't be used in normal operation*/ + CLKSRC_REF}, + .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, 0}, + .offset = 0, + .len = 2, + .public_name = "system-clock", + }, + [NPCM7XX_CLOCK_CLKOUTSEL] = { + .name = "clkoutsel", + .input_size = 5, + .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, + CLKSRC_PLL, CLKSRC_DIV}, + .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, + NPCM7XX_CLOCK_PLLG, NPCM7XX_CLOCK_PLL2D2}, + .offset = 18, + .len = 3, + .public_name = "tock", + }, + [NPCM7XX_CLOCK_UARTCKSEL] = { + .name = "uartcksel", + .input_size = 4, + .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV}, + .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, + NPCM7XX_CLOCK_PLL2D2}, + .offset = 8, + .len = 2, + }, + [NPCM7XX_CLOCK_TIMCKSEL] = { + .name = "timcksel", + .input_size = 4, + .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV}, + .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, + NPCM7XX_CLOCK_PLL2D2}, + .offset = 14, + .len = 2, + }, + [NPCM7XX_CLOCK_SDCKSEL] = { + .name = "sdcksel", + .input_size = 4, + .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV}, + .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, + NPCM7XX_CLOCK_PLL2D2}, + .offset = 6, + .len = 2, + }, + [NPCM7XX_CLOCK_GFXMSEL] = { + .name = "gfxmksel", + .input_size = 2, + .src_type = {CLKSRC_REF, CLKSRC_PLL}, + .src_index = {0, NPCM7XX_CLOCK_PLL2}, + .offset = 21, + .len = 1, + }, + [NPCM7XX_CLOCK_SUCKSEL] = { + .name = "sucksel", + .input_size = 4, + .src_type = {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV}, + .src_index = {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, + NPCM7XX_CLOCK_PLL2D2}, + .offset = 10, + .len = 2, + }, +}; + +static const DividerInitInfo divider_init_info_list[] = { + [NPCM7XX_CLOCK_PLL1D2] = { + .name = "pll1d2", + .src_type = CLKSRC_PLL, + .src_index = NPCM7XX_CLOCK_PLL1, + .divide = divide_by_constant, + .divisor = 2, + }, + [NPCM7XX_CLOCK_PLL2D2] = { + .name = "pll2d2", + .src_type = CLKSRC_PLL, + .src_index = NPCM7XX_CLOCK_PLL2, + .divide = divide_by_constant, + .divisor = 2, + }, + [NPCM7XX_CLOCK_MC_DIVIDER] = { + .name = "mc-divider", + .src_type = CLKSRC_SEL, + .src_index = NPCM7XX_CLOCK_MCCKSEL, + .divide = divide_by_constant, + .divisor = 2, + .public_name = "mc-clock" + }, + [NPCM7XX_CLOCK_AXI_DIVIDER] = { + .name = "axi-divider", + .src_type = CLKSRC_SEL, + .src_index = NPCM7XX_CLOCK_CPUCKSEL, + .divide = shift_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV1, + .offset = 0, + .len = 1, + .public_name = "clk2" + }, + [NPCM7XX_CLOCK_AHB_DIVIDER] = { + .name = "ahb-divider", + .src_type = CLKSRC_DIV, + .src_index = NPCM7XX_CLOCK_AXI_DIVIDER, + .divide = divide_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV1, + .offset = 26, + .len = 2, + .public_name = "clk4" + }, + [NPCM7XX_CLOCK_AHB3_DIVIDER] = { + .name = "ahb3-divider", + .src_type = CLKSRC_DIV, + .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, + .divide = divide_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV1, + .offset = 6, + .len = 5, + .public_name = "ahb3-spi3-clock" + }, + [NPCM7XX_CLOCK_SPI0_DIVIDER] = { + .name = "spi0-divider", + .src_type = CLKSRC_DIV, + .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, + .divide = divide_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV3, + .offset = 6, + .len = 5, + .public_name = "spi0-clock", + }, + [NPCM7XX_CLOCK_SPIX_DIVIDER] = { + .name = "spix-divider", + .src_type = CLKSRC_DIV, + .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, + .divide = divide_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV3, + .offset = 1, + .len = 5, + .public_name = "spix-clock", + }, + [NPCM7XX_CLOCK_APB1_DIVIDER] = { + .name = "apb1-divider", + .src_type = CLKSRC_DIV, + .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, + .divide = shift_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV2, + .offset = 24, + .len = 2, + .public_name = "apb1-clock", + }, + [NPCM7XX_CLOCK_APB2_DIVIDER] = { + .name = "apb2-divider", + .src_type = CLKSRC_DIV, + .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, + .divide = shift_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV2, + .offset = 26, + .len = 2, + .public_name = "apb2-clock", + }, + [NPCM7XX_CLOCK_APB3_DIVIDER] = { + .name = "apb3-divider", + .src_type = CLKSRC_DIV, + .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, + .divide = shift_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV2, + .offset = 28, + .len = 2, + .public_name = "apb3-clock", + }, + [NPCM7XX_CLOCK_APB4_DIVIDER] = { + .name = "apb4-divider", + .src_type = CLKSRC_DIV, + .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, + .divide = shift_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV2, + .offset = 30, + .len = 2, + .public_name = "apb4-clock", + }, + [NPCM7XX_CLOCK_APB5_DIVIDER] = { + .name = "apb5-divider", + .src_type = CLKSRC_DIV, + .src_index = NPCM7XX_CLOCK_AHB_DIVIDER, + .divide = shift_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV2, + .offset = 22, + .len = 2, + .public_name = "apb5-clock", + }, + [NPCM7XX_CLOCK_CLKOUT_DIVIDER] = { + .name = "clkout-divider", + .src_type = CLKSRC_SEL, + .src_index = NPCM7XX_CLOCK_CLKOUTSEL, + .divide = divide_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV2, + .offset = 16, + .len = 5, + .public_name = "clkout", + }, + [NPCM7XX_CLOCK_UART_DIVIDER] = { + .name = "uart-divider", + .src_type = CLKSRC_SEL, + .src_index = NPCM7XX_CLOCK_UARTCKSEL, + .divide = divide_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV1, + .offset = 16, + .len = 5, + .public_name = "uart-clock", + }, + [NPCM7XX_CLOCK_TIMER_DIVIDER] = { + .name = "timer-divider", + .src_type = CLKSRC_SEL, + .src_index = NPCM7XX_CLOCK_TIMCKSEL, + .divide = divide_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV1, + .offset = 21, + .len = 5, + .public_name = "timer-clock", + }, + [NPCM7XX_CLOCK_ADC_DIVIDER] = { + .name = "adc-divider", + .src_type = CLKSRC_DIV, + .src_index = NPCM7XX_CLOCK_TIMER_DIVIDER, + .divide = shift_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV1, + .offset = 28, + .len = 3, + .public_name = "adc-clock", + }, + [NPCM7XX_CLOCK_MMC_DIVIDER] = { + .name = "mmc-divider", + .src_type = CLKSRC_SEL, + .src_index = NPCM7XX_CLOCK_SDCKSEL, + .divide = divide_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV1, + .offset = 11, + .len = 5, + .public_name = "mmc-clock", + }, + [NPCM7XX_CLOCK_SDHC_DIVIDER] = { + .name = "sdhc-divider", + .src_type = CLKSRC_SEL, + .src_index = NPCM7XX_CLOCK_SDCKSEL, + .divide = divide_by_reg_divisor_times_2, + .reg = NPCM7XX_CLK_CLKDIV2, + .offset = 0, + .len = 4, + .public_name = "sdhc-clock", + }, + [NPCM7XX_CLOCK_GFXM_DIVIDER] = { + .name = "gfxm-divider", + .src_type = CLKSRC_SEL, + .src_index = NPCM7XX_CLOCK_GFXMSEL, + .divide = divide_by_constant, + .divisor = 3, + .public_name = "gfxm-clock", + }, + [NPCM7XX_CLOCK_UTMI_DIVIDER] = { + .name = "utmi-divider", + .src_type = CLKSRC_SEL, + .src_index = NPCM7XX_CLOCK_SUCKSEL, + .divide = divide_by_reg_divisor, + .reg = NPCM7XX_CLK_CLKDIV2, + .offset = 8, + .len = 5, + .public_name = "utmi-clock", + }, +}; + +static void npcm7xx_clk_update_pll_cb(void *opaque, ClockEvent event) +{ + npcm7xx_clk_update_pll(opaque); +} + +static void npcm7xx_clk_pll_init(Object *obj) +{ + NPCM7xxClockPLLState *pll = NPCM7XX_CLOCK_PLL(obj); + + pll->clock_in = qdev_init_clock_in(DEVICE(pll), "clock-in", + npcm7xx_clk_update_pll_cb, pll, + ClockUpdate); + pll->clock_out = qdev_init_clock_out(DEVICE(pll), "clock-out"); +} + +static void npcm7xx_clk_update_sel_cb(void *opaque, ClockEvent event) +{ + npcm7xx_clk_update_sel(opaque); +} + +static void npcm7xx_clk_sel_init(Object *obj) +{ + int i; + NPCM7xxClockSELState *sel = NPCM7XX_CLOCK_SEL(obj); + + for (i = 0; i < NPCM7XX_CLK_SEL_MAX_INPUT; ++i) { + g_autofree char *s = g_strdup_printf("clock-in[%d]", i); + sel->clock_in[i] = qdev_init_clock_in(DEVICE(sel), s, + npcm7xx_clk_update_sel_cb, sel, ClockUpdate); + } + sel->clock_out = qdev_init_clock_out(DEVICE(sel), "clock-out"); +} + +static void npcm7xx_clk_update_divider_cb(void *opaque, ClockEvent event) +{ + npcm7xx_clk_update_divider(opaque); +} + +static void npcm7xx_clk_divider_init(Object *obj) +{ + NPCM7xxClockDividerState *div = NPCM7XX_CLOCK_DIVIDER(obj); + + div->clock_in = qdev_init_clock_in(DEVICE(div), "clock-in", + npcm7xx_clk_update_divider_cb, + div, ClockUpdate); + div->clock_out = qdev_init_clock_out(DEVICE(div), "clock-out"); +} + +static void npcm7xx_init_clock_pll(NPCM7xxClockPLLState *pll, + NPCMCLKState *clk, const PLLInitInfo *init_info) +{ + pll->name = init_info->name; + pll->clk = clk; + pll->reg = init_info->reg; + if (init_info->public_name != NULL) { + qdev_alias_clock(DEVICE(pll), "clock-out", DEVICE(clk), + init_info->public_name); + } +} + +static void npcm7xx_init_clock_sel(NPCM7xxClockSELState *sel, + NPCMCLKState *clk, const SELInitInfo *init_info) +{ + int input_size = init_info->input_size; + + sel->name = init_info->name; + sel->clk = clk; + sel->input_size = init_info->input_size; + g_assert(input_size <= NPCM7XX_CLK_SEL_MAX_INPUT); + sel->offset = init_info->offset; + sel->len = init_info->len; + if (init_info->public_name != NULL) { + qdev_alias_clock(DEVICE(sel), "clock-out", DEVICE(clk), + init_info->public_name); + } +} + +static void npcm7xx_init_clock_divider(NPCM7xxClockDividerState *div, + NPCMCLKState *clk, const DividerInitInfo *init_info) +{ + div->name = init_info->name; + div->clk = clk; + + div->divide = init_info->divide; + if (div->divide == divide_by_constant) { + div->divisor = init_info->divisor; + } else { + div->reg = init_info->reg; + div->offset = init_info->offset; + div->len = init_info->len; + } + if (init_info->public_name != NULL) { + qdev_alias_clock(DEVICE(div), "clock-out", DEVICE(clk), + init_info->public_name); + } +} + +static Clock *npcm7xx_get_clock(NPCMCLKState *clk, ClockSrcType type, + int index) +{ + switch (type) { + case CLKSRC_REF: + return clk->clkref; + case CLKSRC_PLL: + return clk->plls[index].clock_out; + case CLKSRC_SEL: + return clk->sels[index].clock_out; + case CLKSRC_DIV: + return clk->dividers[index].clock_out; + default: + g_assert_not_reached(); + } +} + +static void npcm7xx_connect_clocks(NPCMCLKState *clk) +{ + int i, j; + Clock *src; + + for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) { + src = npcm7xx_get_clock(clk, pll_init_info_list[i].src_type, + pll_init_info_list[i].src_index); + clock_set_source(clk->plls[i].clock_in, src); + } + for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) { + for (j = 0; j < sel_init_info_list[i].input_size; ++j) { + src = npcm7xx_get_clock(clk, sel_init_info_list[i].src_type[j], + sel_init_info_list[i].src_index[j]); + clock_set_source(clk->sels[i].clock_in[j], src); + } + } + for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) { + src = npcm7xx_get_clock(clk, divider_init_info_list[i].src_type, + divider_init_info_list[i].src_index); + clock_set_source(clk->dividers[i].clock_in, src); + } +} + +static uint64_t npcm_clk_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t reg = offset / sizeof(uint32_t); + NPCMCLKState *s = opaque; + NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s); + int64_t now_ns; + uint32_t value = 0; + + if (reg >= c->nr_regs) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offset 0x%04" HWADDR_PRIx " out of range\n", + __func__, offset); + return 0; + } + + switch (reg) { + case NPCM7XX_CLK_SWRSTR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n", + __func__, offset); + break; + + case NPCM7XX_CLK_SECCNT: + now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + value = (now_ns - s->ref_ns) / NANOSECONDS_PER_SECOND; + break; + + case NPCM7XX_CLK_CNTR25M: + now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + /* + * This register counts 25 MHz cycles, updating every 640 ns. It rolls + * over to zero every second. + * + * The 4 LSBs are always zero: (1e9 / 640) << 4 = 25000000. + */ + value = (((now_ns - s->ref_ns) / 640) << 4) % NPCM7XX_CLOCK_REF_HZ; + break; + + default: + value = s->regs[reg]; + break; + }; + + trace_npcm_clk_read(offset, value); + + return value; +} + +static void npcm_clk_write(void *opaque, hwaddr offset, + uint64_t v, unsigned size) +{ + uint32_t reg = offset / sizeof(uint32_t); + NPCMCLKState *s = opaque; + NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s); + uint32_t value = v; + + trace_npcm_clk_write(offset, value); + + if (reg >= c->nr_regs) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offset 0x%04" HWADDR_PRIx " out of range\n", + __func__, offset); + return; + } + + switch (reg) { + case NPCM7XX_CLK_SWRSTR: + qemu_log_mask(LOG_UNIMP, "%s: SW reset not implemented: 0x%02x\n", + __func__, value); + value = 0; + break; + + case NPCM7XX_CLK_PLLCON0: + case NPCM7XX_CLK_PLLCON1: + case NPCM7XX_CLK_PLLCON2: + case NPCM7XX_CLK_PLLCONG: + if (value & PLLCON_PWDEN) { + /* Power down -- clear lock and indicate loss of lock */ + value &= ~PLLCON_LOKI; + value |= PLLCON_LOKS; + } else { + /* Normal mode -- assume always locked */ + value |= PLLCON_LOKI; + /* Keep LOKS unchanged unless cleared by writing 1 */ + if (value & PLLCON_LOKS) { + value &= ~PLLCON_LOKS; + } else { + value |= (value & PLLCON_LOKS); + } + } + /* Only update PLL when it is locked. */ + if (value & PLLCON_LOKI) { + npcm7xx_clk_update_pll(&s->plls[find_pll_by_reg(reg)]); + } + break; + + case NPCM7XX_CLK_CLKSEL: + npcm7xx_clk_update_all_sels(s); + break; + + case NPCM7XX_CLK_CLKDIV1: + case NPCM7XX_CLK_CLKDIV2: + case NPCM7XX_CLK_CLKDIV3: + npcm7xx_clk_update_all_dividers(s); + break; + + case NPCM7XX_CLK_CNTR25M: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n", + __func__, offset); + return; + } + + s->regs[reg] = value; +} + +/* Perform reset action triggered by a watchdog */ +static void npcm7xx_clk_perform_watchdog_reset(void *opaque, int n, + int level) +{ + NPCMCLKState *clk = NPCM_CLK(opaque); + uint32_t rcr; + + g_assert(n >= 0 && n <= NPCM7XX_NR_WATCHDOGS); + rcr = clk->regs[NPCM7XX_CLK_WD0RCR + n]; + if (rcr & NPCM7XX_CLK_WDRCR_CA9C) { + watchdog_perform_action(); + } else { + qemu_log_mask(LOG_UNIMP, + "%s: only CPU reset is implemented. (requested 0x%" PRIx32")\n", + __func__, rcr); + } +} + +static const struct MemoryRegionOps npcm_clk_ops = { + .read = npcm_clk_read, + .write = npcm_clk_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void npcm_clk_enter_reset(Object *obj, ResetType type) +{ + NPCMCLKState *s = NPCM_CLK(obj); + NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s); + + size_t sizeof_regs = c->nr_regs * sizeof(uint32_t); + g_assert(sizeof(s->regs) >= sizeof_regs); + memcpy(s->regs, c->cold_reset_values, sizeof_regs); + 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. + */ +} + +static void npcm7xx_clk_init_clock_hierarchy(NPCMCLKState *s) +{ + int i; + + s->clkref = qdev_init_clock_in(DEVICE(s), "clkref", NULL, NULL, 0); + + /* First pass: init all converter modules */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(pll_init_info_list) != NPCM7XX_CLOCK_NR_PLLS); + QEMU_BUILD_BUG_ON(ARRAY_SIZE(sel_init_info_list) != NPCM7XX_CLOCK_NR_SELS); + QEMU_BUILD_BUG_ON(ARRAY_SIZE(divider_init_info_list) + != NPCM7XX_CLOCK_NR_DIVIDERS); + for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) { + object_initialize_child(OBJECT(s), pll_init_info_list[i].name, + &s->plls[i], TYPE_NPCM7XX_CLOCK_PLL); + npcm7xx_init_clock_pll(&s->plls[i], s, + &pll_init_info_list[i]); + } + for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) { + object_initialize_child(OBJECT(s), sel_init_info_list[i].name, + &s->sels[i], TYPE_NPCM7XX_CLOCK_SEL); + npcm7xx_init_clock_sel(&s->sels[i], s, + &sel_init_info_list[i]); + } + for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) { + object_initialize_child(OBJECT(s), divider_init_info_list[i].name, + &s->dividers[i], TYPE_NPCM7XX_CLOCK_DIVIDER); + npcm7xx_init_clock_divider(&s->dividers[i], s, + ÷r_init_info_list[i]); + } + + /* Second pass: connect converter modules */ + npcm7xx_connect_clocks(s); + + clock_update_hz(s->clkref, NPCM7XX_CLOCK_REF_HZ); +} + +static void npcm_clk_init(Object *obj) +{ + NPCMCLKState *s = NPCM_CLK(obj); + + memory_region_init_io(&s->iomem, obj, &npcm_clk_ops, s, + TYPE_NPCM_CLK, 4 * KiB); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); +} + +static int npcm_clk_post_load(void *opaque, int version_id) +{ + if (version_id >= 1) { + NPCMCLKState *clk = opaque; + + npcm7xx_clk_update_all_clocks(clk); + } + + return 0; +} + +static void npcm_clk_realize(DeviceState *dev, Error **errp) +{ + int i; + NPCMCLKState *s = NPCM_CLK(dev); + + qdev_init_gpio_in_named(DEVICE(s), npcm7xx_clk_perform_watchdog_reset, + NPCM7XX_WATCHDOG_RESET_GPIO_IN, NPCM7XX_NR_WATCHDOGS); + npcm7xx_clk_init_clock_hierarchy(s); + + /* Realize child devices */ + for (i = 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) { + if (!qdev_realize(DEVICE(&s->plls[i]), NULL, errp)) { + return; + } + } + for (i = 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) { + if (!qdev_realize(DEVICE(&s->sels[i]), NULL, errp)) { + return; + } + } + for (i = 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) { + if (!qdev_realize(DEVICE(&s->dividers[i]), NULL, errp)) { + return; + } + } +} + +static const VMStateDescription vmstate_npcm7xx_clk_pll = { + .name = "npcm7xx-clock-pll", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_CLOCK(clock_in, NPCM7xxClockPLLState), + VMSTATE_END_OF_LIST(), + }, +}; + +static const VMStateDescription vmstate_npcm7xx_clk_sel = { + .name = "npcm7xx-clock-sel", + .version_id = 0, + .minimum_version_id = 0, + .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(), + }, +}; + +static const VMStateDescription vmstate_npcm7xx_clk_divider = { + .name = "npcm7xx-clock-divider", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_CLOCK(clock_in, NPCM7xxClockDividerState), + VMSTATE_END_OF_LIST(), + }, +}; + +static const VMStateDescription vmstate_npcm_clk = { + .name = "npcm-clk", + .version_id = 3, + .minimum_version_id = 3, + .post_load = npcm_clk_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, NPCMCLKState, NPCM_CLK_MAX_NR_REGS), + VMSTATE_INT64(ref_ns, NPCMCLKState), + VMSTATE_CLOCK(clkref, NPCMCLKState), + VMSTATE_END_OF_LIST(), + }, +}; + +static void npcm7xx_clk_pll_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "NPCM7xx Clock PLL Module"; + dc->vmsd = &vmstate_npcm7xx_clk_pll; + /* Reason: Part of NPCMCLKState component */ + dc->user_creatable = false; +} + +static void npcm7xx_clk_sel_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "NPCM7xx Clock SEL Module"; + dc->vmsd = &vmstate_npcm7xx_clk_sel; + /* Reason: Part of NPCMCLKState component */ + dc->user_creatable = false; +} + +static void npcm7xx_clk_divider_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "NPCM7xx Clock Divider Module"; + dc->vmsd = &vmstate_npcm7xx_clk_divider; + /* Reason: Part of NPCMCLKState component */ + dc->user_creatable = false; +} + +static void npcm_clk_class_init(ObjectClass *klass, const void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_npcm_clk; + dc->realize = npcm_clk_realize; + rc->phases.enter = npcm_clk_enter_reset; +} + +static void npcm7xx_clk_class_init(ObjectClass *klass, const void *data) +{ + NPCMCLKClass *c = NPCM_CLK_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "NPCM7xx Clock Control Registers"; + c->nr_regs = NPCM7XX_CLK_NR_REGS; + c->cold_reset_values = npcm7xx_cold_reset_values; +} + +static void npcm8xx_clk_class_init(ObjectClass *klass, const void *data) +{ + NPCMCLKClass *c = NPCM_CLK_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "NPCM8xx Clock Control Registers"; + c->nr_regs = NPCM8XX_CLK_NR_REGS; + c->cold_reset_values = npcm8xx_cold_reset_values; +} + +static const TypeInfo npcm7xx_clk_pll_info = { + .name = TYPE_NPCM7XX_CLOCK_PLL, + .parent = TYPE_DEVICE, + .instance_size = sizeof(NPCM7xxClockPLLState), + .instance_init = npcm7xx_clk_pll_init, + .class_init = npcm7xx_clk_pll_class_init, +}; + +static const TypeInfo npcm7xx_clk_sel_info = { + .name = TYPE_NPCM7XX_CLOCK_SEL, + .parent = TYPE_DEVICE, + .instance_size = sizeof(NPCM7xxClockSELState), + .instance_init = npcm7xx_clk_sel_init, + .class_init = npcm7xx_clk_sel_class_init, +}; + +static const TypeInfo npcm7xx_clk_divider_info = { + .name = TYPE_NPCM7XX_CLOCK_DIVIDER, + .parent = TYPE_DEVICE, + .instance_size = sizeof(NPCM7xxClockDividerState), + .instance_init = npcm7xx_clk_divider_init, + .class_init = npcm7xx_clk_divider_class_init, +}; + +static const TypeInfo npcm_clk_info = { + .name = TYPE_NPCM_CLK, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NPCMCLKState), + .instance_init = npcm_clk_init, + .class_size = sizeof(NPCMCLKClass), + .class_init = npcm_clk_class_init, + .abstract = true, +}; + +static const TypeInfo npcm7xx_clk_info = { + .name = TYPE_NPCM7XX_CLK, + .parent = TYPE_NPCM_CLK, + .class_init = npcm7xx_clk_class_init, +}; + +static const TypeInfo npcm8xx_clk_info = { + .name = TYPE_NPCM8XX_CLK, + .parent = TYPE_NPCM_CLK, + .class_init = npcm8xx_clk_class_init, +}; + +static void npcm7xx_clk_register_type(void) +{ + type_register_static(&npcm7xx_clk_pll_info); + type_register_static(&npcm7xx_clk_sel_info); + type_register_static(&npcm7xx_clk_divider_info); + type_register_static(&npcm_clk_info); + type_register_static(&npcm7xx_clk_info); + type_register_static(&npcm8xx_clk_info); +} +type_init(npcm7xx_clk_register_type); diff --git a/hw/misc/npcm_gcr.c b/hw/misc/npcm_gcr.c new file mode 100644 index 00000000000..2acaa167713 --- /dev/null +++ b/hw/misc/npcm_gcr.c @@ -0,0 +1,482 @@ +/* + * Nuvoton NPCM7xx/8xx System Global Control Registers. + * + * Copyright 2020 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/misc/npcm_gcr.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qemu/cutils.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/units.h" + +#include "trace.h" + +#define NPCM7XX_GCR_MIN_DRAM_SIZE (128 * MiB) +#define NPCM7XX_GCR_MAX_DRAM_SIZE (2 * GiB) + +enum NPCM7xxGCRRegisters { + NPCM7XX_GCR_PDID, + NPCM7XX_GCR_PWRON, + NPCM7XX_GCR_MFSEL1 = 0x0c / sizeof(uint32_t), + NPCM7XX_GCR_MFSEL2, + NPCM7XX_GCR_MISCPE, + NPCM7XX_GCR_SPSWC = 0x038 / sizeof(uint32_t), + NPCM7XX_GCR_INTCR, + NPCM7XX_GCR_INTSR, + NPCM7XX_GCR_HIFCR = 0x050 / sizeof(uint32_t), + NPCM7XX_GCR_INTCR2 = 0x060 / sizeof(uint32_t), + NPCM7XX_GCR_MFSEL3, + NPCM7XX_GCR_SRCNT, + NPCM7XX_GCR_RESSR, + NPCM7XX_GCR_RLOCKR1, + NPCM7XX_GCR_FLOCKR1, + NPCM7XX_GCR_DSCNT, + NPCM7XX_GCR_MDLR, + NPCM7XX_GCR_SCRPAD3, + NPCM7XX_GCR_SCRPAD2, + NPCM7XX_GCR_DAVCLVLR = 0x098 / sizeof(uint32_t), + NPCM7XX_GCR_INTCR3, + NPCM7XX_GCR_VSINTR = 0x0ac / sizeof(uint32_t), + NPCM7XX_GCR_MFSEL4, + NPCM7XX_GCR_CPBPNTR = 0x0c4 / sizeof(uint32_t), + NPCM7XX_GCR_CPCTL = 0x0d0 / sizeof(uint32_t), + NPCM7XX_GCR_CP2BST, + NPCM7XX_GCR_B2CPNT, + NPCM7XX_GCR_CPPCTL, + NPCM7XX_GCR_I2CSEGSEL, + NPCM7XX_GCR_I2CSEGCTL, + NPCM7XX_GCR_VSRCR, + NPCM7XX_GCR_MLOCKR, + NPCM7XX_GCR_SCRPAD = 0x013c / sizeof(uint32_t), + NPCM7XX_GCR_USB1PHYCTL, + NPCM7XX_GCR_USB2PHYCTL, +}; + +static const uint32_t npcm7xx_cold_reset_values[NPCM7XX_GCR_NR_REGS] = { + [NPCM7XX_GCR_PDID] = 0x04a92750, /* Poleg A1 */ + [NPCM7XX_GCR_MISCPE] = 0x0000ffff, + [NPCM7XX_GCR_SPSWC] = 0x00000003, + [NPCM7XX_GCR_INTCR] = 0x0000035e, + [NPCM7XX_GCR_HIFCR] = 0x0000004e, + [NPCM7XX_GCR_INTCR2] = (1U << 19), /* DDR initialized */ + [NPCM7XX_GCR_RESSR] = 0x80000000, + [NPCM7XX_GCR_DSCNT] = 0x000000c0, + [NPCM7XX_GCR_DAVCLVLR] = 0x5a00f3cf, + [NPCM7XX_GCR_SCRPAD] = 0x00000008, + [NPCM7XX_GCR_USB1PHYCTL] = 0x034730e4, + [NPCM7XX_GCR_USB2PHYCTL] = 0x034730e4, +}; + +enum NPCM8xxGCRRegisters { + NPCM8XX_GCR_PDID, + NPCM8XX_GCR_PWRON, + NPCM8XX_GCR_MISCPE = 0x014 / sizeof(uint32_t), + NPCM8XX_GCR_FLOCKR2 = 0x020 / sizeof(uint32_t), + NPCM8XX_GCR_FLOCKR3, + NPCM8XX_GCR_A35_MODE = 0x034 / sizeof(uint32_t), + NPCM8XX_GCR_SPSWC, + NPCM8XX_GCR_INTCR, + NPCM8XX_GCR_INTSR, + NPCM8XX_GCR_HIFCR = 0x050 / sizeof(uint32_t), + NPCM8XX_GCR_INTCR2 = 0x060 / sizeof(uint32_t), + NPCM8XX_GCR_SRCNT = 0x068 / sizeof(uint32_t), + NPCM8XX_GCR_RESSR, + NPCM8XX_GCR_RLOCKR1, + NPCM8XX_GCR_FLOCKR1, + NPCM8XX_GCR_DSCNT, + NPCM8XX_GCR_MDLR, + NPCM8XX_GCR_SCRPAD_C = 0x080 / sizeof(uint32_t), + NPCM8XX_GCR_SCRPAD_B, + NPCM8XX_GCR_DAVCLVLR = 0x098 / sizeof(uint32_t), + NPCM8XX_GCR_INTCR3, + NPCM8XX_GCR_PCIRCTL = 0x0a0 / sizeof(uint32_t), + NPCM8XX_GCR_VSINTR, + NPCM8XX_GCR_SD2SUR1 = 0x0b4 / sizeof(uint32_t), + NPCM8XX_GCR_SD2SUR2, + NPCM8XX_GCR_INTCR4 = 0x0c0 / sizeof(uint32_t), + NPCM8XX_GCR_CPCTL = 0x0d0 / sizeof(uint32_t), + NPCM8XX_GCR_CP2BST, + NPCM8XX_GCR_B2CPNT, + NPCM8XX_GCR_CPPCTL, + NPCM8XX_GCR_I2CSEGSEL = 0x0e0 / sizeof(uint32_t), + NPCM8XX_GCR_I2CSEGCTL, + NPCM8XX_GCR_VSRCR, + NPCM8XX_GCR_MLOCKR, + NPCM8XX_GCR_SCRPAD = 0x13c / sizeof(uint32_t), + NPCM8XX_GCR_USB1PHYCTL, + NPCM8XX_GCR_USB2PHYCTL, + NPCM8XX_GCR_USB3PHYCTL, + NPCM8XX_GCR_MFSEL1 = 0x260 / sizeof(uint32_t), + NPCM8XX_GCR_MFSEL2, + NPCM8XX_GCR_MFSEL3, + NPCM8XX_GCR_MFSEL4, + NPCM8XX_GCR_MFSEL5, + NPCM8XX_GCR_MFSEL6, + NPCM8XX_GCR_MFSEL7, + NPCM8XX_GCR_MFSEL_LK1 = 0x280 / sizeof(uint32_t), + NPCM8XX_GCR_MFSEL_LK2, + NPCM8XX_GCR_MFSEL_LK3, + NPCM8XX_GCR_MFSEL_LK4, + NPCM8XX_GCR_MFSEL_LK5, + NPCM8XX_GCR_MFSEL_LK6, + NPCM8XX_GCR_MFSEL_LK7, + NPCM8XX_GCR_MFSEL_SET1 = 0x2a0 / sizeof(uint32_t), + NPCM8XX_GCR_MFSEL_SET2, + NPCM8XX_GCR_MFSEL_SET3, + NPCM8XX_GCR_MFSEL_SET4, + NPCM8XX_GCR_MFSEL_SET5, + NPCM8XX_GCR_MFSEL_SET6, + NPCM8XX_GCR_MFSEL_SET7, + NPCM8XX_GCR_MFSEL_CLR1 = 0x2c0 / sizeof(uint32_t), + NPCM8XX_GCR_MFSEL_CLR2, + NPCM8XX_GCR_MFSEL_CLR3, + NPCM8XX_GCR_MFSEL_CLR4, + NPCM8XX_GCR_MFSEL_CLR5, + NPCM8XX_GCR_MFSEL_CLR6, + NPCM8XX_GCR_MFSEL_CLR7, + NPCM8XX_GCR_WD0RCRLK = 0x400 / sizeof(uint32_t), + NPCM8XX_GCR_WD1RCRLK, + NPCM8XX_GCR_WD2RCRLK, + NPCM8XX_GCR_SWRSTC1LK, + NPCM8XX_GCR_SWRSTC2LK, + NPCM8XX_GCR_SWRSTC3LK, + NPCM8XX_GCR_TIPRSTCLK, + NPCM8XX_GCR_CORSTCLK, + NPCM8XX_GCR_WD0RCRBLK, + NPCM8XX_GCR_WD1RCRBLK, + NPCM8XX_GCR_WD2RCRBLK, + NPCM8XX_GCR_SWRSTC1BLK, + NPCM8XX_GCR_SWRSTC2BLK, + NPCM8XX_GCR_SWRSTC3BLK, + NPCM8XX_GCR_TIPRSTCBLK, + NPCM8XX_GCR_CORSTCBLK, + /* 64 scratch pad registers start here. 0xe00 ~ 0xefc */ + NPCM8XX_GCR_SCRPAD_00 = 0xe00 / sizeof(uint32_t), + /* 32 semaphore registers start here. 0xf00 ~ 0xf7c */ + NPCM8XX_GCR_GP_SEMFR_00 = 0xf00 / sizeof(uint32_t), + NPCM8XX_GCR_GP_SEMFR_31 = 0xf7c / sizeof(uint32_t), +}; + +static const uint32_t npcm8xx_cold_reset_values[NPCM8XX_GCR_NR_REGS] = { + [NPCM8XX_GCR_PDID] = 0x04a35850, /* Arbel A1 */ + [NPCM8XX_GCR_MISCPE] = 0x0000ffff, + [NPCM8XX_GCR_A35_MODE] = 0xfff4ff30, + [NPCM8XX_GCR_SPSWC] = 0x00000003, + [NPCM8XX_GCR_INTCR] = 0x0010035e, + [NPCM8XX_GCR_HIFCR] = 0x0000004e, + [NPCM8XX_GCR_SD2SUR1] = 0xfdc80000, + [NPCM8XX_GCR_SD2SUR2] = 0x5200b130, + [NPCM8XX_GCR_INTCR2] = (1U << 19), /* DDR initialized */ + [NPCM8XX_GCR_RESSR] = 0x80000000, + [NPCM8XX_GCR_DAVCLVLR] = 0x5a00f3cf, + [NPCM8XX_GCR_INTCR3] = 0x5e001002, + [NPCM8XX_GCR_VSRCR] = 0x00004800, + [NPCM8XX_GCR_SCRPAD] = 0x00000008, + [NPCM8XX_GCR_USB1PHYCTL] = 0x034730e4, + [NPCM8XX_GCR_USB2PHYCTL] = 0x034730e4, + [NPCM8XX_GCR_USB3PHYCTL] = 0x034730e4, + /* All 32 semaphores should be initialized to 1. */ + [NPCM8XX_GCR_GP_SEMFR_00...NPCM8XX_GCR_GP_SEMFR_31] = 0x00000001, +}; + +static uint64_t npcm_gcr_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t reg = offset / sizeof(uint32_t); + NPCMGCRState *s = opaque; + NPCMGCRClass *c = NPCM_GCR_GET_CLASS(s); + uint64_t value; + + if (reg >= c->nr_regs) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offset 0x%04" HWADDR_PRIx " out of range\n", + __func__, offset); + return 0; + } + + switch (size) { + case 4: + value = s->regs[reg]; + break; + + case 8: + g_assert(!(reg & 1)); + value = deposit64(s->regs[reg], 32, 32, s->regs[reg + 1]); + break; + + default: + g_assert_not_reached(); + } + + trace_npcm_gcr_read(offset, value); + return value; +} + +static void npcm_gcr_write(void *opaque, hwaddr offset, + uint64_t v, unsigned size) +{ + uint32_t reg = offset / sizeof(uint32_t); + NPCMGCRState *s = opaque; + NPCMGCRClass *c = NPCM_GCR_GET_CLASS(s); + uint32_t value = v; + + trace_npcm_gcr_write(offset, v); + + if (reg >= c->nr_regs) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offset 0x%04" HWADDR_PRIx " out of range\n", + __func__, offset); + return; + } + + switch (size) { + case 4: + switch (reg) { + case NPCM7XX_GCR_PDID: + case NPCM7XX_GCR_PWRON: + case NPCM7XX_GCR_INTSR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n", + __func__, offset); + return; + + case NPCM7XX_GCR_RESSR: + case NPCM7XX_GCR_CP2BST: + /* Write 1 to clear */ + value = s->regs[reg] & ~value; + break; + + case NPCM7XX_GCR_RLOCKR1: + case NPCM7XX_GCR_MDLR: + /* Write 1 to set */ + value |= s->regs[reg]; + break; + }; + s->regs[reg] = value; + break; + + case 8: + g_assert(!(reg & 1)); + s->regs[reg] = value; + s->regs[reg + 1] = extract64(v, 32, 32); + break; + + default: + g_assert_not_reached(); + } +} + +static bool npcm_gcr_check_mem_op(void *opaque, hwaddr offset, + unsigned size, bool is_write, + MemTxAttrs attrs) +{ + NPCMGCRClass *c = NPCM_GCR_GET_CLASS(opaque); + + if (offset >= c->nr_regs * sizeof(uint32_t)) { + return false; + } + + switch (size) { + case 4: + return true; + case 8: + if (offset >= NPCM8XX_GCR_SCRPAD_00 * sizeof(uint32_t) && + offset < (NPCM8XX_GCR_NR_REGS - 1) * sizeof(uint32_t)) { + return true; + } else { + return false; + } + default: + return false; + } +} + +static const struct MemoryRegionOps npcm_gcr_ops = { + .read = npcm_gcr_read, + .write = npcm_gcr_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + .accepts = npcm_gcr_check_mem_op, + .unaligned = false, + }, +}; + +static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type) +{ + NPCMGCRState *s = NPCM_GCR(obj); + NPCMGCRClass *c = NPCM_GCR_GET_CLASS(obj); + + g_assert(sizeof(s->regs) >= sizeof(c->cold_reset_values)); + g_assert(sizeof(s->regs) >= c->nr_regs * sizeof(uint32_t)); + memcpy(s->regs, c->cold_reset_values, c->nr_regs * sizeof(uint32_t)); + /* These 3 registers are at the same location in both 7xx and 8xx. */ + 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 npcm8xx_gcr_enter_reset(Object *obj, ResetType type) +{ + NPCMGCRState *s = NPCM_GCR(obj); + NPCMGCRClass *c = NPCM_GCR_GET_CLASS(obj); + + memcpy(s->regs, c->cold_reset_values, c->nr_regs * sizeof(uint32_t)); + /* These 3 registers are at the same location in both 7xx and 8xx. */ + s->regs[NPCM8XX_GCR_PWRON] = s->reset_pwron; + s->regs[NPCM8XX_GCR_MDLR] = s->reset_mdlr; + s->regs[NPCM8XX_GCR_INTCR3] = s->reset_intcr3; + s->regs[NPCM8XX_GCR_SCRPAD_B] = s->reset_scrpad_b; +} + +static void npcm_gcr_realize(DeviceState *dev, Error **errp) +{ + ERRP_GUARD(); + NPCMGCRState *s = NPCM_GCR(dev); + uint64_t dram_size; + Object *obj; + + obj = object_property_get_link(OBJECT(dev), "dram-mr", errp); + if (!obj) { + error_prepend(errp, "%s: required dram-mr link not found: ", __func__); + return; + } + dram_size = memory_region_size(MEMORY_REGION(obj)); + if (!is_power_of_2(dram_size) || + dram_size < NPCM7XX_GCR_MIN_DRAM_SIZE || + dram_size > NPCM7XX_GCR_MAX_DRAM_SIZE) { + g_autofree char *sz = size_to_str(dram_size); + g_autofree char *min_sz = size_to_str(NPCM7XX_GCR_MIN_DRAM_SIZE); + g_autofree char *max_sz = size_to_str(NPCM7XX_GCR_MAX_DRAM_SIZE); + error_setg(errp, "%s: unsupported DRAM size %s", __func__, sz); + error_append_hint(errp, + "DRAM size must be a power of two between %s and %s," + " inclusive.\n", min_sz, max_sz); + return; + } + + /* Power-on reset value */ + s->reset_intcr3 = 0x00001002; + + /* + * The GMMAP (Graphics Memory Map) field is used by u-boot to detect the + * DRAM size, and is normally initialized by the boot block as part of DRAM + * training. However, since we don't have a complete emulation of the + * memory controller and try to make it look like it has already been + * initialized, the boot block will skip this initialization, and we need + * to make sure this field is set correctly up front. + * + * WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 GiB of + * DRAM will be interpreted as 128 MiB. + * + * https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244 + */ + s->reset_intcr3 |= ctz64(dram_size / NPCM7XX_GCR_MIN_DRAM_SIZE) << 8; + + /* + * The boot block starting from 0.0.6 for NPCM8xx SoCs stores the DRAM size + * in the SCRPAD2 registers. We need to set this field correctly since + * the initialization is skipped as we mentioned above. + * https://github.com/Nuvoton-Israel/u-boot/blob/npcm8mnx-v2019.01_tmp/board/nuvoton/arbel/arbel.c#L737 + */ + s->reset_scrpad_b = dram_size; +} + +static void npcm_gcr_init(Object *obj) +{ + NPCMGCRState *s = NPCM_GCR(obj); + + memory_region_init_io(&s->iomem, obj, &npcm_gcr_ops, s, + TYPE_NPCM_GCR, 4 * KiB); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); +} + +static const VMStateDescription vmstate_npcm_gcr = { + .name = "npcm-gcr", + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, NPCMGCRState, NPCM_GCR_MAX_NR_REGS), + VMSTATE_END_OF_LIST(), + }, +}; + +static const Property npcm_gcr_properties[] = { + DEFINE_PROP_UINT32("disabled-modules", NPCMGCRState, reset_mdlr, 0), + DEFINE_PROP_UINT32("power-on-straps", NPCMGCRState, reset_pwron, 0), +}; + +static void npcm_gcr_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = npcm_gcr_realize; + dc->vmsd = &vmstate_npcm_gcr; + + device_class_set_props(dc, npcm_gcr_properties); +} + +static void npcm7xx_gcr_class_init(ObjectClass *klass, const void *data) +{ + NPCMGCRClass *c = NPCM_GCR_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->desc = "NPCM7xx System Global Control Registers"; + rc->phases.enter = npcm7xx_gcr_enter_reset; + + c->nr_regs = NPCM7XX_GCR_NR_REGS; + c->cold_reset_values = npcm7xx_cold_reset_values; + rc->phases.enter = npcm7xx_gcr_enter_reset; +} + +static void npcm8xx_gcr_class_init(ObjectClass *klass, const void *data) +{ + NPCMGCRClass *c = NPCM_GCR_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->desc = "NPCM8xx System Global Control Registers"; + c->nr_regs = NPCM8XX_GCR_NR_REGS; + c->cold_reset_values = npcm8xx_cold_reset_values; + rc->phases.enter = npcm8xx_gcr_enter_reset; +} + +static const TypeInfo npcm_gcr_info[] = { + { + .name = TYPE_NPCM_GCR, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NPCMGCRState), + .instance_init = npcm_gcr_init, + .class_size = sizeof(NPCMGCRClass), + .class_init = npcm_gcr_class_init, + .abstract = true, + }, + { + .name = TYPE_NPCM7XX_GCR, + .parent = TYPE_NPCM_GCR, + .class_init = npcm7xx_gcr_class_init, + }, + { + .name = TYPE_NPCM8XX_GCR, + .parent = TYPE_NPCM_GCR, + .class_init = npcm8xx_gcr_class_init, + }, +}; +DEFINE_TYPES(npcm_gcr_info) diff --git a/hw/misc/nrf51_rng.c b/hw/misc/nrf51_rng.c index fc86e1b6979..8cd7ffe3f55 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: @@ -219,19 +219,18 @@ static void nrf51_rng_reset(DeviceState *dev) } -static Property nrf51_rng_properties[] = { +static const Property nrf51_rng_properties[] = { DEFINE_PROP_UINT16("period_unfiltered_us", NRF51RNGState, period_unfiltered_us, 167), DEFINE_PROP_UINT16("period_filtered_us", NRF51RNGState, period_filtered_us, 660), - DEFINE_PROP_END_OF_LIST(), }; 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), @@ -241,13 +240,13 @@ static const VMStateDescription vmstate_rng = { } }; -static void nrf51_rng_class_init(ObjectClass *klass, void *data) +static void nrf51_rng_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 c77ca2fc74e..da95c4ace58 100644 --- a/hw/misc/omap_clk.c +++ b/hw/misc/omap_clk.c @@ -30,174 +30,170 @@ struct clk { struct clk *parent; struct clk *child1; struct clk *sibling; -#define ALWAYS_ENABLED (1 << 0) -#define CLOCK_IN_OMAP310 (1 << 10) -#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) +#define ALWAYS_ENABLED (1 << 0) +#define CLOCK_IN_OMAP310 (1 << 10) +#define CLOCK_IN_OMAP730 (1 << 11) +#define CLOCK_IN_OMAP1510 (1 << 12) +#define CLOCK_IN_OMAP16XX (1 << 13) uint32_t flags; int id; - int running; /* Is currently ticking */ - int enabled; /* Is enabled, regardless of its input clk */ - unsigned long rate; /* Current rate (if .running) */ - unsigned int divisor; /* Rate relative to input (if .enabled) */ - unsigned int multiplier; /* Rate relative to input (if .enabled) */ - qemu_irq users[16]; /* Who to notify on change */ - int usecount; /* Automatically idle when unused */ + int running; /* Is currently ticking */ + int enabled; /* Is enabled, regardless of its input clk */ + unsigned long rate; /* Current rate (if .running) */ + unsigned int divisor; /* Rate relative to input (if .enabled) */ + unsigned int multiplier; /* Rate relative to input (if .enabled) */ + qemu_irq users[16]; /* Who to notify on change */ + int usecount; /* Automatically idle when unused */ }; static struct clk xtal_osc12m = { - .name = "xtal_osc_12m", - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "xtal_osc_12m", + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; 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, + .name = "xtal_osc_32k", + .rate = 32768, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk ck_ref = { - .name = "ck_ref", - .alias = "clkin", - .parent = &xtal_osc12m, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "ck_ref", + .alias = "clkin", + .parent = &xtal_osc12m, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; /* If a dpll is disabled it becomes a bypass, child clocks don't stop */ static struct clk dpll1 = { - .name = "dpll1", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "dpll1", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk dpll2 = { - .name = "dpll2", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP310 | ALWAYS_ENABLED, + .name = "dpll2", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk dpll3 = { - .name = "dpll3", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP310 | ALWAYS_ENABLED, + .name = "dpll3", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk dpll4 = { - .name = "dpll4", - .parent = &ck_ref, - .multiplier = 4, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "dpll4", + .parent = &ck_ref, + .multiplier = 4, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk apll = { - .name = "apll", - .parent = &ck_ref, - .multiplier = 48, - .divisor = 12, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "apll", + .parent = &ck_ref, + .multiplier = 48, + .divisor = 12, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk ck_48m = { - .name = "ck_48m", - .parent = &dpll4, /* either dpll4 or apll */ - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "ck_48m", + .parent = &dpll4, /* either dpll4 or apll */ + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk ck_dpll1out = { - .name = "ck_dpll1out", - .parent = &dpll1, - .flags = CLOCK_IN_OMAP16XX, + .name = "ck_dpll1out", + .parent = &dpll1, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk sossi_ck = { - .name = "ck_sossi", - .parent = &ck_dpll1out, - .flags = CLOCK_IN_OMAP16XX, + .name = "ck_sossi", + .parent = &ck_dpll1out, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk clkm1 = { - .name = "clkm1", - .alias = "ck_gen1", - .parent = &dpll1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "clkm1", + .alias = "ck_gen1", + .parent = &dpll1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk clkm2 = { - .name = "clkm2", - .alias = "ck_gen2", - .parent = &dpll1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "clkm2", + .alias = "ck_gen2", + .parent = &dpll1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk clkm3 = { - .name = "clkm3", - .alias = "ck_gen3", - .parent = &dpll1, /* either dpll1 or ck_ref */ - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "clkm3", + .alias = "ck_gen3", + .parent = &dpll1, /* either dpll1 or ck_ref */ + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk arm_ck = { - .name = "arm_ck", - .alias = "mpu_ck", - .parent = &clkm1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "arm_ck", + .alias = "mpu_ck", + .parent = &clkm1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk armper_ck = { - .name = "armper_ck", - .alias = "mpuper_ck", - .parent = &clkm1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "armper_ck", + .alias = "mpuper_ck", + .parent = &clkm1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk arm_gpio_ck = { - .name = "arm_gpio_ck", - .alias = "mpu_gpio_ck", - .parent = &clkm1, - .divisor = 1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, + .name = "arm_gpio_ck", + .alias = "mpu_gpio_ck", + .parent = &clkm1, + .divisor = 1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, }; static struct clk armxor_ck = { - .name = "armxor_ck", - .alias = "mpuxor_ck", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "armxor_ck", + .alias = "mpuxor_ck", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk armtim_ck = { - .name = "armtim_ck", - .alias = "mputim_ck", - .parent = &ck_ref, /* either CLKIN or DPLL1 */ - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "armtim_ck", + .alias = "mputim_ck", + .parent = &ck_ref, /* either CLKIN or DPLL1 */ + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk armwdt_ck = { - .name = "armwdt_ck", - .alias = "mpuwd_ck", - .parent = &clkm1, - .divisor = 14, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "armwdt_ck", + .alias = "mpuwd_ck", + .parent = &clkm1, + .divisor = 14, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk arminth_ck16xx = { - .name = "arminth_ck", - .parent = &arm_ck, - .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, + .name = "arminth_ck", + .parent = &arm_ck, + .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, /* Note: On 16xx the frequency can be divided by 2 by programming * ARM_CKCTL:ARM_INTHCK_SEL(14) to 1 * @@ -206,48 +202,48 @@ static struct clk arminth_ck16xx = { }; static struct clk dsp_ck = { - .name = "dsp_ck", - .parent = &clkm2, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .name = "dsp_ck", + .parent = &clkm2, + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, }; static struct clk dspmmu_ck = { - .name = "dspmmu_ck", - .parent = &clkm2, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + .name = "dspmmu_ck", + .parent = &clkm2, + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, }; static struct clk dspper_ck = { - .name = "dspper_ck", - .parent = &clkm2, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .name = "dspper_ck", + .parent = &clkm2, + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, }; static struct clk dspxor_ck = { - .name = "dspxor_ck", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .name = "dspxor_ck", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, }; static struct clk dsptim_ck = { - .name = "dsptim_ck", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .name = "dsptim_ck", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, }; static struct clk tc_ck = { - .name = "tc_ck", - .parent = &clkm3, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + .name = "tc_ck", + .parent = &clkm3, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk arminth_ck15xx = { - .name = "arminth_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, + .name = "arminth_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, /* Note: On 1510 the frequency follows TC_CK * * 16xx version is in MPU clocks. @@ -256,698 +252,259 @@ static struct clk arminth_ck15xx = { static struct clk tipb_ck = { /* No-idle controlled by "tc_ck" */ - .name = "tipb_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, + .name = "tipb_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk l3_ocpi_ck = { /* No-idle controlled by "tc_ck" */ - .name = "l3_ocpi_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX, + .name = "l3_ocpi_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk tc1_ck = { - .name = "tc1_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX, + .name = "tc1_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk tc2_ck = { - .name = "tc2_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX, + .name = "tc2_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk dma_ck = { /* No-idle controlled by "tc_ck" */ - .name = "dma_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "dma_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk dma_lcdfree_ck = { - .name = "dma_lcdfree_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, + .name = "dma_lcdfree_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, }; static struct clk api_ck = { - .name = "api_ck", - .alias = "mpui_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "api_ck", + .alias = "mpui_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk lb_ck = { - .name = "lb_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, + .name = "lb_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, }; static struct clk lbfree_ck = { - .name = "lbfree_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, + .name = "lbfree_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, }; static struct clk hsab_ck = { - .name = "hsab_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, + .name = "hsab_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, }; static struct clk rhea1_ck = { - .name = "rhea1_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, + .name = "rhea1_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, }; static struct clk rhea2_ck = { - .name = "rhea2_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, + .name = "rhea2_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, }; static struct clk lcd_ck_16xx = { - .name = "lcd_ck", - .parent = &clkm3, - .flags = CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730, + .name = "lcd_ck", + .parent = &clkm3, + .flags = CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730, }; static struct clk lcd_ck_1510 = { - .name = "lcd_ck", - .parent = &clkm3, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, + .name = "lcd_ck", + .parent = &clkm3, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, }; static struct clk uart1_1510 = { - .name = "uart1_ck", + .name = "uart1_ck", /* Direct from ULPD, no real parent */ - .parent = &armper_ck, /* either armper_ck or dpll4 */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, + .parent = &armper_ck, /* either armper_ck or dpll4 */ + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk uart1_16xx = { - .name = "uart1_ck", + .name = "uart1_ck", /* Direct from ULPD, no real parent */ - .parent = &armper_ck, - .rate = 48000000, - .flags = CLOCK_IN_OMAP16XX, + .parent = &armper_ck, + .rate = 48000000, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk uart2_ck = { - .name = "uart2_ck", + .name = "uart2_ck", /* Direct from ULPD, no real parent */ - .parent = &armper_ck, /* either armper_ck or dpll4 */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .parent = &armper_ck, /* either armper_ck or dpll4 */ + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk uart3_1510 = { - .name = "uart3_ck", + .name = "uart3_ck", /* Direct from ULPD, no real parent */ - .parent = &armper_ck, /* either armper_ck or dpll4 */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, + .parent = &armper_ck, /* either armper_ck or dpll4 */ + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk uart3_16xx = { - .name = "uart3_ck", + .name = "uart3_ck", /* Direct from ULPD, no real parent */ - .parent = &armper_ck, - .rate = 48000000, - .flags = CLOCK_IN_OMAP16XX, + .parent = &armper_ck, + .rate = 48000000, + .flags = CLOCK_IN_OMAP16XX, }; -static struct clk usb_clk0 = { /* 6 MHz output on W4_USB_CLK0 */ - .name = "usb_clk0", - .alias = "usb.clko", +static struct clk usb_clk0 = { /* 6 MHz output on W4_USB_CLK0 */ + .name = "usb_clk0", + .alias = "usb.clko", /* Direct from ULPD, no parent */ - .rate = 6000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .rate = 6000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk usb_hhc_ck1510 = { - .name = "usb_hhc_ck", + .name = "usb_hhc_ck", /* Direct from ULPD, no parent */ - .rate = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */ - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, + .rate = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */ + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, }; static struct clk usb_hhc_ck16xx = { - .name = "usb_hhc_ck", + .name = "usb_hhc_ck", /* Direct from ULPD, no parent */ - .rate = 48000000, + .rate = 48000000, /* OTG_SYSCON_2.OTG_PADEN == 0 (not 1510-compatible) */ - .flags = CLOCK_IN_OMAP16XX, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk usb_w2fc_mclk = { - .name = "usb_w2fc_mclk", - .alias = "usb_w2fc_ck", - .parent = &ck_48m, - .rate = 48000000, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .name = "usb_w2fc_mclk", + .alias = "usb_w2fc_ck", + .parent = &ck_48m, + .rate = 48000000, + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, }; static struct clk mclk_1510 = { - .name = "mclk", + .name = "mclk", /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510, + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510, }; static struct clk bclk_310 = { - .name = "bt_mclk_out", /* Alias midi_mclk_out? */ - .parent = &armper_ck, - .flags = CLOCK_IN_OMAP310, + .name = "bt_mclk_out", /* Alias midi_mclk_out? */ + .parent = &armper_ck, + .flags = CLOCK_IN_OMAP310, }; static struct clk mclk_310 = { - .name = "com_mclk_out", - .parent = &armper_ck, - .flags = CLOCK_IN_OMAP310, + .name = "com_mclk_out", + .parent = &armper_ck, + .flags = CLOCK_IN_OMAP310, }; static struct clk mclk_16xx = { - .name = "mclk", + .name = "mclk", /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .flags = CLOCK_IN_OMAP16XX, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk bclk_1510 = { - .name = "bclk", + .name = "bclk", /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510, + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510, }; static struct clk bclk_16xx = { - .name = "bclk", + .name = "bclk", /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .flags = CLOCK_IN_OMAP16XX, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk mmc1_ck = { - .name = "mmc_ck", - .id = 1, + .name = "mmc_ck", + .id = 1, /* Functional clock is direct from ULPD, interface clock is ARMPER */ - .parent = &armper_ck, /* either armper_ck or dpll4 */ - .rate = 48000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .parent = &armper_ck, /* either armper_ck or dpll4 */ + .rate = 48000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk mmc2_ck = { - .name = "mmc_ck", - .id = 2, + .name = "mmc_ck", + .id = 2, /* Functional clock is direct from ULPD, interface clock is ARMPER */ - .parent = &armper_ck, - .rate = 48000000, - .flags = CLOCK_IN_OMAP16XX, + .parent = &armper_ck, + .rate = 48000000, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk cam_mclk = { - .name = "cam.mclk", - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, - .rate = 12000000, + .name = "cam.mclk", + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .rate = 12000000, }; static struct clk cam_exclk = { - .name = "cam.exclk", - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .name = "cam.exclk", + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, /* Either 12M from cam.mclk or 48M from dpll4 */ - .parent = &cam_mclk, + .parent = &cam_mclk, }; static struct clk cam_lclk = { - .name = "cam.lclk", - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .name = "cam.lclk", + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, }; static struct clk i2c_fck = { - .name = "i2c_fck", - .id = 1, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + .name = "i2c_fck", + .id = 1, + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, - .parent = &armxor_ck, + .parent = &armxor_ck, }; static struct clk i2c_ick = { - .name = "i2c_ick", - .id = 1, - .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, - .parent = &armper_ck, + .name = "i2c_ick", + .id = 1, + .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, + .parent = &armper_ck, }; 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, - .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, + .name = "clk32-kHz", + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + ALWAYS_ENABLED, + .parent = &xtal_osc32k, }; static struct clk *onchip_clks[] = { @@ -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 67158eb1649..00000000000 --- 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 = 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 = 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 = 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 = 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 = 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 = 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 b7875489da3..00000000000 --- a/hw/misc/omap_l4.c +++ /dev/null @@ -1,162 +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 = 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 = 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 6aa1b3ef7fb..00000000000 --- a/hw/misc/omap_sdrc.c +++ /dev/null @@ -1,167 +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 = 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 = 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 4d7fb7d85f1..00000000000 --- a/hw/misc/omap_tap.c +++ /dev/null @@ -1,117 +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 = 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/pc-testdev.c b/hw/misc/pc-testdev.c index e3896518694..67c486f347c 100644 --- a/hw/misc/pc-testdev.c +++ b/hw/misc/pc-testdev.c @@ -193,7 +193,7 @@ static void testdev_realizefn(DeviceState *d, Error **errp) memory_region_add_subregion(mem, 0xff000000, &dev->iomem); } -static void testdev_class_init(ObjectClass *klass, void *data) +static void testdev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index 49303134e4c..ba71c5069ff 100644 --- a/hw/misc/pci-testdev.c +++ b/hw/misc/pci-testdev.c @@ -23,7 +23,7 @@ #include "hw/qdev-properties.h" #include "qemu/event_notifier.h" #include "qemu/module.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qom/object.h" typedef struct PCITestDevHdr { @@ -90,6 +90,7 @@ struct PCITestDevState { int current; uint64_t membar_size; + bool membar_backed; MemoryRegion membar; }; @@ -245,7 +246,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; @@ -259,8 +259,14 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp) pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->portio); if (d->membar_size) { - memory_region_init(&d->membar, OBJECT(d), "pci-testdev-membar", - d->membar_size); + if (d->membar_backed) + memory_region_init_ram(&d->membar, OBJECT(d), + "pci-testdev-membar-backed", + d->membar_size, NULL); + else + memory_region_init(&d->membar, OBJECT(d), + "pci-testdev-membar", + d->membar_size); pci_register_bar(pci_dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_PREFETCH | @@ -279,7 +285,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; @@ -320,12 +326,12 @@ static void qdev_pci_testdev_reset(DeviceState *dev) pci_testdev_reset(d); } -static Property pci_testdev_properties[] = { +static const Property pci_testdev_properties[] = { DEFINE_PROP_SIZE("membar", PCITestDevState, membar_size, 0), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_BOOL("membar-backed", PCITestDevState, membar_backed, false), }; -static void pci_testdev_class_init(ObjectClass *klass, void *data) +static void pci_testdev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -338,7 +344,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); } @@ -347,7 +353,7 @@ static const TypeInfo pci_testdev_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCITestDevState), .class_init = pci_testdev_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/misc/pvpanic-isa.c b/hw/misc/pvpanic-isa.c index ccec50f61bb..f7b421c713b 100644 --- a/hw/misc/pvpanic-isa.c +++ b/hw/misc/pvpanic-isa.c @@ -14,14 +14,13 @@ #include "qemu/osdep.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/nvram/fw_cfg.h" #include "hw/qdev-properties.h" #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) @@ -99,14 +98,13 @@ static void build_pvpanic_isa_aml(AcpiDevAmlIf *adev, Aml *scope) aml_append(scope, dev); } -static Property pvpanic_isa_properties[] = { +static const 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), - DEFINE_PROP_END_OF_LIST(), + PVPANIC_EVENTS), }; -static void pvpanic_isa_class_init(ObjectClass *klass, void *data) +static void pvpanic_isa_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); @@ -123,7 +121,7 @@ static const TypeInfo pvpanic_isa_info = { .instance_size = sizeof(PVPanicISAState), .instance_init = pvpanic_isa_initfn, .class_init = pvpanic_isa_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, diff --git a/hw/misc/pvpanic-mmio.c b/hw/misc/pvpanic-mmio.c new file mode 100644 index 00000000000..2a363106b2d --- /dev/null +++ b/hw/misc/pvpanic-mmio.c @@ -0,0 +1,60 @@ +/* + * QEMU simulated pvpanic device (MMIO frontend) + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "hw/qdev-properties.h" +#include "hw/misc/pvpanic.h" +#include "hw/sysbus.h" +#include "standard-headers/misc/pvpanic.h" + +OBJECT_DECLARE_SIMPLE_TYPE(PVPanicMMIOState, PVPANIC_MMIO_DEVICE) + +#define PVPANIC_MMIO_SIZE 0x2 + +struct PVPanicMMIOState { + SysBusDevice parent_obj; + + PVPanicState pvpanic; +}; + +static void pvpanic_mmio_initfn(Object *obj) +{ + PVPanicMMIOState *s = PVPANIC_MMIO_DEVICE(obj); + + pvpanic_setup_io(&s->pvpanic, DEVICE(s), PVPANIC_MMIO_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->pvpanic.mr); +} + +static const Property pvpanic_mmio_properties[] = { + DEFINE_PROP_UINT8("events", PVPanicMMIOState, pvpanic.events, + PVPANIC_PANICKED | PVPANIC_CRASH_LOADED), +}; + +static void pvpanic_mmio_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, pvpanic_mmio_properties); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static const TypeInfo pvpanic_mmio_info = { + .name = TYPE_PVPANIC_MMIO_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PVPanicMMIOState), + .instance_init = pvpanic_mmio_initfn, + .class_init = pvpanic_mmio_class_init, +}; + +static void pvpanic_register_types(void) +{ + type_register_static(&pvpanic_mmio_info); +} + +type_init(pvpanic_register_types) diff --git a/hw/misc/pvpanic-pci.c b/hw/misc/pvpanic-pci.c index fbcaa50731b..2869b6a7ff8 100644 --- a/hw/misc/pvpanic-pci.c +++ b/hw/misc/pvpanic-pci.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/nvram/fw_cfg.h" #include "hw/qdev-properties.h" @@ -21,7 +21,7 @@ #include "hw/misc/pvpanic.h" #include "qom/object.h" #include "hw/pci/pci_device.h" -#include "standard-headers/linux/pvpanic.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,18 +48,17 @@ 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[] = { +static const Property pvpanic_pci_properties[] = { DEFINE_PROP_UINT8("events", PVPanicPCIState, pvpanic.events, - PVPANIC_PANICKED | PVPANIC_CRASH_LOADED), - DEFINE_PROP_END_OF_LIST(), + PVPANIC_EVENTS), }; -static void pvpanic_pci_class_init(ObjectClass *klass, void *data) +static void pvpanic_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); @@ -81,7 +80,7 @@ static const TypeInfo pvpanic_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PVPanicPCIState), .class_init = pvpanic_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } } diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index 1540e9091a4..c83247c4087 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -15,19 +15,19 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/nvram/fw_cfg.h" #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 86b23a5372f..dfee1af5ad2 100644 --- a/hw/misc/sbsa_ec.c +++ b/hw/misc/sbsa_ec.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "hw/sysbus.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" typedef struct SECUREECState { SysBusDevice parent_obj; @@ -73,7 +73,7 @@ static void sbsa_ec_init(Object *obj) sysbus_init_mmio(dev, &s->iomem); } -static void sbsa_ec_class_init(ObjectClass *klass, void *data) +static void sbsa_ec_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/sifive_e_aon.c b/hw/misc/sifive_e_aon.c index 4656457d0bb..6eef38d622a 100644 --- a/hw/misc/sifive_e_aon.c +++ b/hw/misc/sifive_e_aon.c @@ -24,7 +24,7 @@ #include "hw/misc/sifive_e_aon.h" #include "qapi/visitor.h" #include "qapi/error.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "hw/qdev-properties.h" REG32(AON_WDT_WDOGCFG, 0x0) @@ -289,17 +289,16 @@ static void sifive_e_aon_init(Object *obj) sysbus_init_irq(sbd, &r->wdog_irq); } -static Property sifive_e_aon_properties[] = { +static const 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) +static void sifive_e_aon_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - dc->reset = sifive_e_aon_reset; + device_class_set_legacy_reset(dc, sifive_e_aon_reset); device_class_set_props(dc, sifive_e_aon_properties); } diff --git a/hw/misc/sifive_test.c b/hw/misc/sifive_test.c index 56df45bfe59..b94bb2d29db 100644 --- a/hw/misc/sifive_test.c +++ b/hw/misc/sifive_test.c @@ -23,8 +23,9 @@ #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/misc/sifive_test.h" +#include "system/system.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 8965f5c22aa..1ebed2fd8b4 100644 --- a/hw/misc/sifive_u_otp.c +++ b/hw/misc/sifive_u_otp.c @@ -27,8 +27,8 @@ #include "qemu/log.h" #include "qemu/module.h" #include "hw/misc/sifive_u_otp.h" -#include "sysemu/blockdev.h" -#include "sysemu/block-backend.h" +#include "system/blockdev.h" +#include "system/block-backend.h" #define WRITTEN_BIT_ON 0x1 @@ -194,10 +194,9 @@ static const MemoryRegionOps sifive_u_otp_ops = { } }; -static Property sifive_u_otp_properties[] = { +static const Property sifive_u_otp_properties[] = { DEFINE_PROP_UINT32("serial", SiFiveUOTPState, serial, 0), DEFINE_PROP_DRIVE("drive", SiFiveUOTPState, blk), - DEFINE_PROP_END_OF_LIST(), }; static void sifive_u_otp_realize(DeviceState *dev, Error **errp) @@ -271,7 +270,7 @@ static void sifive_u_otp_realize(DeviceState *dev, Error **errp) memset(s->fuse_wo, 0x00, sizeof(s->fuse_wo)); } -static void sifive_u_otp_class_init(ObjectClass *klass, void *data) +static void sifive_u_otp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/sifive_u_prci.c b/hw/misc/sifive_u_prci.c index 5d9d446ee86..6e75cb6d0d5 100644 --- a/hw/misc/sifive_u_prci.c +++ b/hw/misc/sifive_u_prci.c @@ -146,12 +146,12 @@ static void sifive_u_prci_reset(DeviceState *dev) s->coreclksel = SIFIVE_U_PRCI_CORECLKSEL_HFCLK; } -static void sifive_u_prci_class_init(ObjectClass *klass, void *data) +static void sifive_u_prci_class_init(ObjectClass *klass, const 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 e8eb71570a8..a034df3592f 100644 --- a/hw/misc/slavio_misc.c +++ b/hw/misc/slavio_misc.c @@ -27,7 +27,7 @@ #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "trace.h" #include "qom/object.h" @@ -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), @@ -483,11 +483,11 @@ static void slavio_misc_init(Object *obj) qdev_init_gpio_in(dev, slavio_set_power_fail, 1); } -static void slavio_misc_class_init(ObjectClass *klass, void *data) +static void slavio_misc_class_init(ObjectClass *klass, const 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 00000000000..5815b3efa54 --- /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(addr, value); + + 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, const 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 04c22c28502..d285896ea76 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: @@ -138,11 +138,11 @@ static void stm32f2xx_syscfg_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } -static void stm32f2xx_syscfg_class_init(ObjectClass *klass, void *data) +static void stm32f2xx_syscfg_class_init(ObjectClass *klass, const 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 02e78100461..0688e6e73ec 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), @@ -164,11 +164,11 @@ static const VMStateDescription vmstate_stm32f4xx_exti = { } }; -static void stm32f4xx_exti_class_init(ObjectClass *klass, void *data) +static void stm32f4xx_exti_class_init(ObjectClass *klass, const 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 f960e4ea1ee..addfb031e88 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, @@ -147,11 +147,11 @@ static const VMStateDescription vmstate_stm32f4xx_syscfg = { } }; -static void stm32f4xx_syscfg_class_init(ObjectClass *klass, void *data) +static void stm32f4xx_syscfg_class_init(ObjectClass *klass, const 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 00000000000..9c002164c82 --- /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, const 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 00000000000..0e1f27fbdda --- /dev/null +++ b/hw/misc/stm32l4x5_rcc.c @@ -0,0 +1,1477 @@ +/* + * 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, const 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; + /* Reason: Part of Stm32l4x5RccState component */ + dc->user_creatable = false; +} + +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, const 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; + /* Reason: Part of Stm32l4x5RccState component */ + dc->user_creatable = false; +} + +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 const 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), +}; + +static void stm32l4x5_rcc_class_init(ObjectClass *klass, const 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 00000000000..4e21756e0b4 --- /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, const 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 e8b2be14c05..e3f64c0ff6b 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -93,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" @@ -115,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 @@ -122,13 +130,13 @@ mos6522_set_sr_int(void) "set sr_int" mos6522_write(uint64_t addr, const char *name, uint64_t val) "reg=0x%"PRIx64 " [%s] val=0x%"PRIx64 mos6522_read(uint64_t addr, const char *name, unsigned val) "reg=0x%"PRIx64 " [%s] val=0x%x" -# npcm7xx_clk.c -npcm7xx_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 -npcm7xx_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 +# npcm_clk.c +npcm_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 +npcm_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 -# npcm7xx_gcr.c -npcm7xx_gcr_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 -npcm7xx_gcr_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 +# npcm_gcr.c +npcm_gcr_read(uint64_t offset, uint64_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx64 +npcm_gcr_write(uint64_t offset, uint64_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx64 # npcm7xx_mft.c npcm7xx_mft_read(const char *name, uint64_t offset, uint16_t value) "%s: offset: 0x%04" PRIx64 " value: 0x%04" PRIx16 @@ -148,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" @@ -155,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" @@ -192,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" @@ -199,6 +253,12 @@ 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 +# imx6_src.c +imx6_src_read(const char *reg_name, uint32_t value) "reg[%s] => 0x%" PRIx32 +imx6_src_write(const char *reg_name, uint64_t value) "reg[%s] <= 0x%" PRIx64 +imx6_clear_reset_bit(const char *reg_name, uint32_t value) "reg[%s] <= 0x%" PRIx32 +imx6_src_reset(void) "" + # 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 @@ -242,6 +302,14 @@ aspeed_peci_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" aspeed_peci_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 aspeed_peci_raise_interrupt(uint32_t ctrl, uint32_t status) "ctrl 0x%" PRIx32 " status 0x%" PRIx32 +# aspeed_hace.c +aspeed_hace_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 +aspeed_hace_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 +aspeed_hace_hash_sg(int index, uint64_t list_addr, uint64_t buf_addr, uint32_t len) "%d: list_addr 0x%" PRIx64 " buf_addr 0x%" PRIx64 " len 0x%" PRIx32 +aspeed_hace_hash_addr(const char *s, uint64_t addr) "%s: 0x%" PRIx64 +aspeed_hace_hash_execute_acc_mode(bool final_request) "final request: %d" +aspeed_hace_hexdump(const char *desc, uint32_t offset, char *s) "%s: 0x%08x: %s" + # bcm2835_property.c bcm2835_mbox_property(uint32_t tag, uint32_t bufsize, size_t resplen) "mbox property tag:0x%08x in_sz:%u out_sz:%zu" @@ -271,7 +339,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" @@ -281,10 +351,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 @@ -301,3 +367,39 @@ 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 + +# ivshmem-flat.c +ivshmem_flat_irq_handler(uint16_t vector_id) "Caught interrupt request: vector %d" +ivshmem_flat_new_peer(uint16_t peer_id) "New peer ID: %d" +ivshmem_flat_add_vector_failure(uint16_t vector_id, uint32_t vector_fd, uint16_t peer_id) "Failed to add vector %u (fd = %u) to peer ID %u, maximum number of vectors reached" +ivshmem_flat_add_vector_success(uint16_t vector_id, uint32_t vector_fd, uint16_t peer_id) "Successful addition of vector %u (fd = %u) to peer ID %u" +ivshmem_flat_irq_resolved(const char *irq_qompath) "IRQ QOM path '%s' correctly resolved" +ivshmem_flat_proto_ver_own_id(uint64_t proto_ver, uint16_t peer_id) "Protocol Version = 0x%"PRIx64", Own Peer ID = %u" +ivshmem_flat_shmem_size(int fd, uint64_t size) "Shmem fd (%d) total size is %"PRIu64" byte(s)" +ivshmem_flat_shmem_map(uint64_t addr) "Mapping shmem @ 0x%"PRIx64 +ivshmem_flat_mmio_map(uint64_t addr) "Mapping MMRs @ 0x%"PRIx64 +ivshmem_flat_read_mmr(uint64_t addr_offset) "Read access at offset %"PRIu64 +ivshmem_flat_read_mmr_doorbell(void) "DOORBELL register is write-only!" +ivshmem_flat_read_write_mmr_invalid(uint64_t addr_offset) "No ivshmem register mapped at offset %"PRIu64 +ivshmem_flat_interrupt_invalid_peer(uint16_t peer_id) "Can't interrupt non-existing peer %u" +ivshmem_flat_write_mmr(uint64_t addr_offset) "Write access at offset %"PRIu64 +ivshmem_flat_interrupt_peer(uint16_t peer_id, uint16_t vector_id) "Interrupting peer ID %u, vector %u..." + +# i2c-echo.c +i2c_echo_event(const char *id, const char *event) "%s: %s" +i2c_echo_recv(const char *id, uint8_t data) "%s: recv 0x%02" PRIx8 +i2c_echo_send(const char *id, uint8_t data) "%s: send 0x%02" PRIx8 diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index 30481e1c909..a158d4a2944 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), @@ -587,19 +587,18 @@ static const VMStateDescription tz_mpc_vmstate = { } }; -static Property tz_mpc_properties[] = { +static const Property tz_mpc_properties[] = { DEFINE_PROP_LINK("downstream", TZMPC, downstream, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; -static void tz_mpc_class_init(ObjectClass *klass, void *data) +static void tz_mpc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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); } @@ -612,7 +611,7 @@ static const TypeInfo tz_mpc_info = { }; static void tz_mpc_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/misc/tz-msc.c b/hw/misc/tz-msc.c index acbe94400ba..af0cc5d4718 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), @@ -278,21 +278,20 @@ static const VMStateDescription tz_msc_vmstate = { } }; -static Property tz_msc_properties[] = { +static const Property tz_msc_properties[] = { DEFINE_PROP_LINK("downstream", TZMSC, downstream, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_LINK("idau", TZMSC, idau, TYPE_IDAU_INTERFACE, IDAUInterface *), - DEFINE_PROP_END_OF_LIST(), }; -static void tz_msc_class_init(ObjectClass *klass, void *data) +static void tz_msc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 36495c68e76..e4235a846d4 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), @@ -305,7 +305,7 @@ static const VMStateDescription tz_ppc_vmstate = { DEFINE_PROP_LINK("port[" #N "]", TZPPC, port[N].downstream, \ TYPE_MEMORY_REGION, MemoryRegion *) -static Property tz_ppc_properties[] = { +static const Property tz_ppc_properties[] = { DEFINE_PROP_UINT32("NONSEC_MASK", TZPPC, nonsec_mask, 0), DEFINE_PORT(0), DEFINE_PORT(1), @@ -323,16 +323,15 @@ static Property tz_ppc_properties[] = { DEFINE_PORT(13), DEFINE_PORT(14), DEFINE_PORT(15), - DEFINE_PROP_END_OF_LIST(), }; -static void tz_ppc_class_init(ObjectClass *klass, void *data) +static void tz_ppc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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/unimp.c b/hw/misc/unimp.c index 6cfc5727f0b..4370c14ef16 100644 --- a/hw/misc/unimp.c +++ b/hw/misc/unimp.c @@ -70,13 +70,12 @@ static void unimp_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); } -static Property unimp_properties[] = { +static const Property unimp_properties[] = { DEFINE_PROP_UINT64("size", UnimplementedDeviceState, size, 0), DEFINE_PROP_STRING("name", UnimplementedDeviceState, name), - DEFINE_PROP_END_OF_LIST(), }; -static void unimp_class_init(ObjectClass *klass, void *data) +static void unimp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/virt_ctrl.c b/hw/misc/virt_ctrl.c index e75d1e7e17b..9f16093ca2c 100644 --- a/hw/misc/virt_ctrl.c +++ b/hw/misc/virt_ctrl.c @@ -10,7 +10,7 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "trace.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/misc/virt_ctrl.h" enum { @@ -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() } @@ -125,11 +125,11 @@ static void virt_ctrl_instance_init(Object *obj) sysbus_init_irq(dev, &s->irq); } -static void virt_ctrl_class_init(ObjectClass *oc, void *data) +static void virt_ctrl_class_init(ObjectClass *oc, const 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 a9d718fc236..9c2e9005ad3 100644 --- a/hw/misc/vmcoreinfo.c +++ b/hw/misc/vmcoreinfo.c @@ -13,22 +13,22 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/nvram/fw_cfg.h" #include "migration/vmstate.h" #include "hw/misc/vmcoreinfo.h" -static void fw_cfg_vmci_write(void *dev, off_t offset, size_t len) +static void fw_cfg_vmci_write(void *opaque, off_t offset, size_t len) { - VMCoreInfoState *s = VMCOREINFO(dev); + VMCoreInfoState *s = opaque; s->has_vmcoreinfo = offset == 0 && len == sizeof(s->vmcoreinfo) && s->vmcoreinfo.guest_format != FW_CFG_VMCOREINFO_FORMAT_NONE; } -static void vmcoreinfo_reset(void *dev) +static void vmcoreinfo_reset_hold(Object *obj, ResetType type) { - VMCoreInfoState *s = VMCOREINFO(dev); + VMCoreInfoState *s = VMCOREINFO(obj); s->has_vmcoreinfo = false; memset(&s->vmcoreinfo, 0, sizeof(s->vmcoreinfo)); @@ -47,13 +47,13 @@ static void vmcoreinfo_realize(DeviceState *dev, Error **errp) */ if (!vmcoreinfo_find()) { error_setg(errp, "at most one %s device is permitted", - VMCOREINFO_DEVICE); + TYPE_VMCOREINFO); return; } if (!fw_cfg || !fw_cfg->dma_enabled) { error_setg(errp, "%s device requires fw_cfg with DMA", - VMCOREINFO_DEVICE); + TYPE_VMCOREINFO); return; } @@ -65,7 +65,7 @@ static void vmcoreinfo_realize(DeviceState *dev, Error **errp) * This device requires to register a global reset because it is * not plugged to a bus (which, as its QOM parent, would reset it). */ - qemu_register_reset(vmcoreinfo_reset, dev); + qemu_register_resettable(OBJECT(s)); vmcoreinfo_state = s; } @@ -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), @@ -83,26 +83,25 @@ static const VMStateDescription vmstate_vmcoreinfo = { }, }; -static void vmcoreinfo_device_class_init(ObjectClass *klass, void *data) +static void vmcoreinfo_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); dc->vmsd = &vmstate_vmcoreinfo; dc->realize = vmcoreinfo_realize; dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_MISC, dc->categories); + rc->phases.hold = vmcoreinfo_reset_hold; } -static const TypeInfo vmcoreinfo_device_info = { - .name = VMCOREINFO_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(VMCoreInfoState), - .class_init = vmcoreinfo_device_class_init, +static const TypeInfo vmcoreinfo_types[] = { + { + .name = TYPE_VMCOREINFO, + .parent = TYPE_DEVICE, + .instance_size = sizeof(VMCoreInfoState), + .class_init = vmcoreinfo_device_class_init, + } }; -static void vmcoreinfo_register_types(void) -{ - type_register_static(&vmcoreinfo_device_info); -} - -type_init(vmcoreinfo_register_types) +DEFINE_TYPES(vmcoreinfo_types) diff --git a/hw/misc/xlnx-versal-cframe-reg.c b/hw/misc/xlnx-versal-cframe-reg.c index 8e8ec0715ab..1ce083e2409 100644 --- a/hw/misc/xlnx-versal-cframe-reg.c +++ b/hw/misc/xlnx-versal-cframe-reg.c @@ -542,7 +542,7 @@ static void cframe_reg_reset_enter(Object *obj, ResetType type) } } -static void cframe_reg_reset_hold(Object *obj) +static void cframe_reg_reset_hold(Object *obj, ResetType type) { XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(obj); @@ -697,7 +697,7 @@ static const VMStateDescription vmstate_cframe = { .name = "cframe", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(data, XlnxCFrame, FRAME_NUM_WORDS), VMSTATE_END_OF_LIST() } @@ -707,7 +707,7 @@ static const VMStateDescription vmstate_cframe_reg = { .name = TYPE_XLNX_VERSAL_CFRAME_REG, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(wfifo, XlnxVersalCFrameReg, 4), VMSTATE_UINT32_ARRAY(regs, XlnxVersalCFrameReg, CFRAME_REG_R_MAX), VMSTATE_BOOL(rowon, XlnxVersalCFrameReg), @@ -720,7 +720,7 @@ static const VMStateDescription vmstate_cframe_reg = { } }; -static Property cframe_regs_props[] = { +static const Property cframe_regs_props[] = { DEFINE_PROP_LINK("cfu-fdro", XlnxVersalCFrameReg, cfg.cfu_fdro, TYPE_XLNX_CFI_IF, XlnxCfiIf *), DEFINE_PROP_UINT32("blktype0-frames", XlnxVersalCFrameReg, @@ -737,7 +737,6 @@ static Property cframe_regs_props[] = { 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) @@ -765,13 +764,13 @@ static const VMStateDescription vmstate_cframe_bcast_reg = { .name = TYPE_XLNX_VERSAL_CFRAME_BCAST_REG, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(wfifo, XlnxVersalCFrameBcastReg, 4), VMSTATE_END_OF_LIST(), } }; -static Property cframe_bcast_regs_props[] = { +static const 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], @@ -802,10 +801,9 @@ static Property cframe_bcast_regs_props[] = { 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) +static void cframe_reg_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -819,7 +817,7 @@ static void cframe_reg_class_init(ObjectClass *klass, void *data) xcic->cfi_transfer_packet = cframe_reg_cfi_transfer_packet; } -static void cframe_bcast_reg_class_init(ObjectClass *klass, void *data) +static void cframe_bcast_reg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -835,7 +833,7 @@ static const TypeInfo cframe_reg_info = { .instance_size = sizeof(XlnxVersalCFrameReg), .class_init = cframe_reg_class_init, .instance_init = cframe_reg_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XLNX_CFI_IF }, { } } diff --git a/hw/misc/xlnx-versal-cfu.c b/hw/misc/xlnx-versal-cfu.c index 8e588ac1d83..b920fc77c3f 100644 --- a/hw/misc/xlnx-versal-cfu.c +++ b/hw/misc/xlnx-versal-cfu.c @@ -397,6 +397,13 @@ static void cfu_fdro_init(Object *obj) 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); @@ -419,7 +426,7 @@ static void cfu_fdro_cfi_transfer_packet(XlnxCfiIf *cfi_if, XlnxCfiPacket *pkt) } } -static Property cfu_props[] = { +static const Property cfu_props[] = { DEFINE_PROP_LINK("cframe0", XlnxVersalCFUAPB, cfg.cframe[0], TYPE_XLNX_CFI_IF, XlnxCfiIf *), DEFINE_PROP_LINK("cframe1", XlnxVersalCFUAPB, cfg.cframe[1], @@ -450,20 +457,18 @@ static Property cfu_props[] = { 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[] = { +static const 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 = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(wfifo, XlnxVersalCFUAPB, 4), VMSTATE_UINT32_ARRAY(regs, XlnxVersalCFUAPB, R_MAX), VMSTATE_UINT8(fdri_row_addr, XlnxVersalCFUAPB), @@ -475,7 +480,7 @@ static const VMStateDescription vmstate_cfu_fdro = { .name = TYPE_XLNX_VERSAL_CFU_FDRO, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_FIFO32(fdro_data, XlnxVersalCFUFDRO), VMSTATE_END_OF_LIST(), } @@ -485,22 +490,22 @@ static const VMStateDescription vmstate_cfu_sfr = { .name = TYPE_XLNX_VERSAL_CFU_SFR, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(wfifo, XlnxVersalCFUSFR, 4), VMSTATE_END_OF_LIST(), } }; -static void cfu_apb_class_init(ObjectClass *klass, void *data) +static void cfu_apb_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = cfu_apb_reset; + 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) +static void cfu_fdro_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -511,7 +516,7 @@ static void cfu_fdro_class_init(ObjectClass *klass, void *data) rc->phases.enter = cfu_fdro_reset_enter; } -static void cfu_sfr_class_init(ObjectClass *klass, void *data) +static void cfu_sfr_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -527,7 +532,7 @@ static const TypeInfo cfu_apb_info = { .instance_size = sizeof(XlnxVersalCFUAPB), .class_init = cfu_apb_class_init, .instance_init = cfu_apb_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XLNX_CFI_IF }, { } } @@ -539,7 +544,8 @@ static const TypeInfo cfu_fdro_info = { .instance_size = sizeof(XlnxVersalCFUFDRO), .class_init = cfu_fdro_class_init, .instance_init = cfu_fdro_init, - .interfaces = (InterfaceInfo[]) { + .instance_finalize = cfu_fdro_finalize, + .interfaces = (const InterfaceInfo[]) { { TYPE_XLNX_CFI_IF }, { } } diff --git a/hw/misc/xlnx-versal-crl.c b/hw/misc/xlnx-versal-crl.c index 767106b7a30..08ff2fcc24f 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,13 +388,13 @@ 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(), } }; -static void crl_class_init(ObjectClass *klass, void *data) +static void crl_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/xlnx-versal-pmc-iou-slcr.c b/hw/misc/xlnx-versal-pmc-iou-slcr.c index 07b7ebc2173..d76df468d49 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,14 +1412,15 @@ 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(), } }; -static void xlnx_versal_pmc_iou_slcr_class_init(ObjectClass *klass, void *data) +static void xlnx_versal_pmc_iou_slcr_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/misc/xlnx-versal-trng.c b/hw/misc/xlnx-versal-trng.c new file mode 100644 index 00000000000..f34dd3ef352 --- /dev/null +++ b/hw/misc/xlnx-versal-trng.c @@ -0,0 +1,714 @@ +/* + * 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) +{ + const 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 = { + .type = "uint32", + .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 const Property trng_props[] = { + DEFINE_PROP_UNSIGNED("forced-prng", XlnxVersalTRng, forced_prng_seed, + 0, trng_prop_uint64, uint64_t), + DEFINE_PROP_UINT32("hw-version", XlnxVersalTRng, hw_version, 0x0200), + DEFINE_PROP("fips-fault-events", XlnxVersalTRng, forced_faults, + trng_prop_fault_events, uint32_t), +}; + +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, const 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; + + 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 e5b719a0ed4..07370b80c0d 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,18 +212,17 @@ 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(), } }; -static Property xram_ctrl_properties[] = { +static const Property xram_ctrl_properties[] = { DEFINE_PROP_UINT64("size", XlnxXramCtrl, cfg.size, 1 * MiB), - DEFINE_PROP_END_OF_LIST(), }; -static void xram_ctrl_class_init(ObjectClass *klass, void *data) +static void xram_ctrl_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/xlnx-zynqmp-apu-ctrl.c b/hw/misc/xlnx-zynqmp-apu-ctrl.c index 3d2be95e6db..e85da32d99c 100644 --- a/hw/misc/xlnx-zynqmp-apu-ctrl.c +++ b/hw/misc/xlnx-zynqmp-apu-ctrl.c @@ -150,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); @@ -218,13 +218,13 @@ 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(), } }; -static void zynqmp_apu_class_init(ObjectClass *klass, void *data) +static void zynqmp_apu_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/xlnx-zynqmp-crf.c b/hw/misc/xlnx-zynqmp-crf.c index 57bc8cf49ae..cccca0e814e 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,13 +233,13 @@ 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(), } }; -static void crf_class_init(ObjectClass *klass, void *data) +static void crf_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c index 8b702859618..010387beec4 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/log.h" @@ -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,28 @@ 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 void zynq_slcr_class_init(ObjectClass *klass, void *data) +static const Property zynq_slcr_props[] = { + DEFINE_PROP_UINT8("boot-mode", ZynqSLCRState, boot_mode, 1), +}; + +static void zynq_slcr_class_init(ObjectClass *klass, const 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 7fcc0d7faa2..7f80218d10f 100644 --- a/hw/net/Kconfig +++ b/hw/net/Kconfig @@ -62,8 +62,12 @@ config VMXNET3_PCI config SMC91C111 bool +config LAN9118_PHY + bool + config LAN9118 bool + select LAN9118_PHY select PTIMER config NE2000_ISA @@ -89,6 +93,7 @@ config ALLWINNER_SUN8I_EMAC config IMX_FEC bool + select LAN9118_PHY config CADENCE bool diff --git a/hw/net/allwinner-sun8i-emac.c b/hw/net/allwinner-sun8i-emac.c index fac4405f452..30a81576b4c 100644 --- a/hw/net/allwinner-sun8i-emac.c +++ b/hw/net/allwinner-sun8i-emac.c @@ -30,7 +30,7 @@ #include "net/checksum.h" #include "qemu/module.h" #include "exec/cpu-common.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/net/allwinner-sun8i-emac.h" /* EMAC register offsets */ @@ -784,7 +784,7 @@ static void allwinner_sun8i_emac_set_link(NetClientState *nc) static const MemoryRegionOps allwinner_sun8i_emac_mem_ops = { .read = allwinner_sun8i_emac_read, .write = allwinner_sun8i_emac_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -824,16 +824,16 @@ 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); } -static Property allwinner_sun8i_emac_properties[] = { +static const Property allwinner_sun8i_emac_properties[] = { DEFINE_NIC_PROPERTIES(AwSun8iEmacState, conf), DEFINE_PROP_UINT8("phy-addr", AwSun8iEmacState, mii_phy_addr, 0), DEFINE_PROP_LINK("dma-memory", AwSun8iEmacState, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static int allwinner_sun8i_emac_post_load(void *opaque, int version_id) @@ -850,7 +850,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), @@ -875,12 +875,13 @@ static const VMStateDescription vmstate_aw_emac = { } }; -static void allwinner_sun8i_emac_class_init(ObjectClass *klass, void *data) +static void allwinner_sun8i_emac_class_init(ObjectClass *klass, + const 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 372e5b66dab..77d089d9887 100644 --- a/hw/net/allwinner_emac.c +++ b/hw/net/allwinner_emac.c @@ -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 */ @@ -421,7 +421,7 @@ static void aw_emac_set_link(NetClientState *nc) static const MemoryRegionOps aw_emac_mem_ops = { .read = aw_emac_read, .write = aw_emac_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -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); @@ -461,17 +462,16 @@ static void aw_emac_realize(DeviceState *dev, Error **errp) fifo8_create(&s->tx_fifo[1], TX_FIFO_SIZE); } -static Property aw_emac_properties[] = { +static const Property aw_emac_properties[] = { DEFINE_NIC_PROPERTIES(AwEmacState, conf), DEFINE_PROP_UINT8("phy-addr", AwEmacState, phy_addr, 0), - DEFINE_PROP_END_OF_LIST(), }; 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 +494,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), @@ -514,13 +514,13 @@ static const VMStateDescription vmstate_aw_emac = { } }; -static void aw_emac_class_init(ObjectClass *klass, void *data) +static void aw_emac_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 42ea2411a21..50025d5a6f2 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -23,16 +23,17 @@ */ #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" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "net/checksum.h" #include "net/eth.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 */ - -#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(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) + +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) - -#define GEM_INT_Q1_STATUS (0x00000400 / 4) -#define GEM_INT_Q1_MASK (0x00000640 / 4) - -#define GEM_TRANSMIT_Q1_PTR (0x00000440 / 4) -#define GEM_TRANSMIT_Q7_PTR (GEM_TRANSMIT_Q1_PTR + 6) - -#define GEM_RECEIVE_Q1_PTR (0x00000480 / 4) -#define GEM_RECEIVE_Q7_PTR (GEM_RECEIVE_Q1_PTR + 6) - -#define GEM_TBQPH (0x000004C8 / 4) -#define GEM_RBQPH (0x000004D4 / 4) - -#define GEM_INT_Q1_ENABLE (0x00000600 / 4) -#define GEM_INT_Q7_ENABLE (GEM_INT_Q1_ENABLE + 6) - -#define GEM_INT_Q1_DISABLE (0x00000620 / 4) -#define GEM_INT_Q7_DISABLE (GEM_INT_Q1_DISABLE + 6) - -#define GEM_INT_Q1_MASK (0x00000640 / 4) -#define GEM_INT_Q7_MASK (GEM_INT_Q1_MASK + 6) - -#define GEM_SCREENING_TYPE1_REGISTER_0 (0x00000500 / 4) - -#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) - -#define GEM_SCREENING_TYPE2_REGISTER_0 (0x00000540 / 4) - -#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(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) + +REG32(INT_Q1_STATUS, 0x400) +REG32(INT_Q1_MASK, 0x640) + +REG32(TRANSMIT_Q1_PTR, 0x440) +REG32(TRANSMIT_Q7_PTR, 0x458) + +REG32(RECEIVE_Q1_PTR, 0x480) +REG32(RECEIVE_Q7_PTR, 0x498) + +REG32(TBQPH, 0x4c8) +REG32(RBQPH, 0x4d4) + +REG32(INT_Q1_ENABLE, 0x600) +REG32(INT_Q7_ENABLE, 0x618) + +REG32(INT_Q1_DISABLE, 0x620) +REG32(INT_Q7_DISABLE, 0x638) + +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) + +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) + +REG32(SCREENING_TYPE2_ETHERTYPE_REG0, 0x6e0) + +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) + +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; - uint16_t rx_cmp; + uint32_t cr0, cr1, mask, compare, disable_mask; + uint32_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"); @@ -846,10 +946,27 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, break; } - rx_cmp = rxbuf_ptr[offset] << 8 | rxbuf_ptr[offset]; - mask = extract32(cr0, 0, 16); + disable_mask = + FIELD_EX32(cr1, TYPE2_COMPARE_0_WORD_1, DISABLE_MASK); + if (disable_mask) { + /* + * If disable_mask is set, mask_value is used as an + * additional 2 byte Compare Value; that is equivalent + * to using the whole cr0 register as the comparison value. + * Load 32 bits of data from rx_buf, and set mask to + * all-ones so we compare all 32 bits. + */ + rx_cmp = ldl_le_p(rxbuf_ptr + offset); + mask = 0xFFFFFFFF; + compare = cr0; + } else { + rx_cmp = lduw_le_p(rxbuf_ptr + offset); + 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 +974,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 +988,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 +1015,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 +1047,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 +1071,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 +1092,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 +1118,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 +1148,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 +1224,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 +1243,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 +1292,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 +1317,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 +1338,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 +1375,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 +1390,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 +1406,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 +1423,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 +1469,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 +1537,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. @@ -1437,24 +1585,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 +1629,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 +1649,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 +1759,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 +1781,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), @@ -1678,7 +1800,7 @@ static const VMStateDescription vmstate_cadence_gem = { } }; -static Property gem_properties[] = { +static const Property gem_properties[] = { DEFINE_NIC_PROPERTIES(CadenceGEMState, conf), DEFINE_PROP_UINT32("revision", CadenceGEMState, revision, GEM_MODID_VALUE), @@ -1691,17 +1813,18 @@ static Property gem_properties[] = { num_type2_screeners, 4), DEFINE_PROP_UINT16("jumbo-max-len", CadenceGEMState, jumbo_max_len, 10240), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_LINK("dma", CadenceGEMState, dma_mr, + TYPE_MEMORY_REGION, MemoryRegion *), }; -static void gem_class_init(ObjectClass *klass, void *data) +static void gem_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 2cd90cef1e1..be16769de29 100644 --- a/hw/net/can/can_kvaser_pci.c +++ b/hw/net/can/can_kvaser_pci.c @@ -30,12 +30,8 @@ */ #include "qemu/osdep.h" -#include "qemu/event_notifier.h" #include "qemu/module.h" -#include "qemu/thread.h" -#include "qemu/sockets.h" #include "qapi/error.h" -#include "chardev/char.h" #include "hw/irq.h" #include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" @@ -266,7 +262,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), @@ -286,7 +282,7 @@ static void kvaser_pci_instance_init(Object *obj) 0); } -static void kvaser_pci_class_init(ObjectClass *klass, void *data) +static void kvaser_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -299,7 +295,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); } @@ -309,7 +305,7 @@ static const TypeInfo kvaser_pci_info = { .instance_size = sizeof(KvaserPCIState), .class_init = kvaser_pci_class_init, .instance_init = kvaser_pci_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/can/can_mioe3680_pci.c b/hw/net/can/can_mioe3680_pci.c index b9918773b3f..44f3ba370dc 100644 --- a/hw/net/can/can_mioe3680_pci.c +++ b/hw/net/can/can_mioe3680_pci.c @@ -26,12 +26,8 @@ */ #include "qemu/osdep.h" -#include "qemu/event_notifier.h" #include "qemu/module.h" -#include "qemu/thread.h" -#include "qemu/sockets.h" #include "qapi/error.h" -#include "chardev/char.h" #include "hw/irq.h" #include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" @@ -203,7 +199,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), @@ -227,7 +223,7 @@ static void mioe3680_pci_instance_init(Object *obj) 0); } -static void mioe3680_pci_class_init(ObjectClass *klass, void *data) +static void mioe3680_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -243,7 +239,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 = { @@ -252,7 +248,7 @@ static const TypeInfo mioe3680_pci_info = { .instance_size = sizeof(Mioe3680PCIState), .class_init = mioe3680_pci_class_init, .instance_init = mioe3680_pci_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/can/can_pcm3680_pci.c b/hw/net/can/can_pcm3680_pci.c index 8ef3e4659cc..7296d63be79 100644 --- a/hw/net/can/can_pcm3680_pci.c +++ b/hw/net/can/can_pcm3680_pci.c @@ -26,12 +26,8 @@ */ #include "qemu/osdep.h" -#include "qemu/event_notifier.h" #include "qemu/module.h" -#include "qemu/thread.h" -#include "qemu/sockets.h" #include "qapi/error.h" -#include "chardev/char.h" #include "hw/irq.h" #include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" @@ -204,7 +200,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), @@ -228,7 +224,7 @@ static void pcm3680i_pci_instance_init(Object *obj) 0); } -static void pcm3680i_pci_class_init(ObjectClass *klass, void *data) +static void pcm3680i_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -244,7 +240,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 = { @@ -253,7 +249,7 @@ static const TypeInfo pcm3680i_pci_info = { .instance_size = sizeof(Pcm3680iPCIState), .class_init = pcm3680i_pci_class_init, .instance_init = pcm3680i_pci_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/can/can_sja1000.c b/hw/net/can/can_sja1000.c index 73201f91395..5b6ba9df6c4 100644 --- a/hw/net/can/can_sja1000.c +++ b/hw/net/can/can_sja1000.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "chardev/char.h" +#include "qemu/bitops.h" #include "hw/irq.h" #include "migration/vmstate.h" #include "net/can_emu.h" @@ -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 f2c3b6a7061..17131a4e18f 100644 --- a/hw/net/can/ctucan_core.c +++ b/hw/net/can/ctucan_core.c @@ -28,7 +28,8 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "chardev/char.h" +#include "qemu/bswap.h" +#include "qemu/bitops.h" #include "hw/irq.h" #include "migration/vmstate.h" #include "net/can_emu.h" @@ -399,8 +400,6 @@ void ctucan_mem_write(CtuCanCoreState *s, hwaddr addr, uint64_t val, ctucan_update_irq(s); } - - return; } uint64_t ctucan_mem_read(CtuCanCoreState *s, hwaddr addr, unsigned size) @@ -617,7 +616,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 +635,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 ea079e2af56..bed6785433e 100644 --- a/hw/net/can/ctucan_pci.c +++ b/hw/net/can/ctucan_pci.c @@ -27,12 +27,8 @@ */ #include "qemu/osdep.h" -#include "qemu/event_notifier.h" #include "qemu/module.h" -#include "qemu/thread.h" -#include "qemu/sockets.h" #include "qapi/error.h" -#include "chardev/char.h" #include "hw/irq.h" #include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" @@ -215,7 +211,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), @@ -241,7 +237,7 @@ static void ctucan_pci_instance_init(Object *obj) #endif } -static void ctucan_pci_class_init(ObjectClass *klass, void *data) +static void ctucan_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -257,7 +253,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 = { @@ -266,7 +262,7 @@ static const TypeInfo ctucan_pci_info = { .instance_size = sizeof(CtuCanPCIState), .class_init = ctucan_pci_class_init, .instance_init = ctucan_pci_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/can/xlnx-versal-canfd.c b/hw/net/can/xlnx-versal-canfd.c index 5b8ce0a285e..3eb111949f8 100644 --- a/hw/net/can/xlnx-versal-canfd.c +++ b/hw/net/can/xlnx-versal-canfd.c @@ -678,12 +678,10 @@ REG32(RB_DW15_REGISTER_1, 0x4144) FIELD(RB_DW15_REGISTER_1, DATA_BYTES62, 8, 8) FIELD(RB_DW15_REGISTER_1, DATA_BYTES63, 0, 8) -static uint8_t canfd_dlc_array[8] = {8, 12, 16, 20, 24, 32, 48, 64}; - static void canfd_update_irq(XlnxVersalCANFDState *s) { - unsigned int irq = s->regs[R_INTERRUPT_STATUS_REGISTER] & - s->regs[R_INTERRUPT_ENABLE_REGISTER]; + 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. */ @@ -869,6 +867,10 @@ static void regs2frame(XlnxVersalCANFDState *s, qemu_can_frame *frame, 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 * @@ -877,56 +879,37 @@ static void regs2frame(XlnxVersalCANFDState *s, qemu_can_frame *frame, dlc_reg_val = s->regs[reg_num + 1]; dlc_value = FIELD_EX32(dlc_reg_val, TB0_DLC_REGISTER, DLC); - frame->can_id = s->regs[reg_num]; + 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)) { - /* - * CANFD frame. - * Converting dlc(0 to 15) 4 Byte data to plain length(i.e. 0 to 64) - * 1 Byte data. This is done to make it work with SocketCAN. - * On actual CANFD frame, this value can't be more than 0xF. - * Conversion table for DLC to plain length: - * - * DLC Plain Length - * 0 - 8 0 - 8 - * 9 9 - 12 - * 10 13 - 16 - * 11 17 - 20 - * 12 21 - 24 - * 13 25 - 32 - * 14 33 - 48 - * 15 49 - 64 - */ - - frame->flags = QEMU_CAN_FRMF_TYPE_FD; + frame->flags |= QEMU_CAN_FRMF_TYPE_FD; - if (dlc_value < 8) { - frame->can_dlc = dlc_value; - } else { - assert((dlc_value - 8) < ARRAY_SIZE(canfd_dlc_array)); - frame->can_dlc = canfd_dlc_array[dlc_value - 8]; + if (FIELD_EX32(dlc_reg_val, TB0_DLC_REGISTER, BRS)) { + frame->flags |= QEMU_CAN_FRMF_BRS; } } else { - /* - * FD Format bit not set that means it is a CAN Frame. - * Conversion table for classic CAN: - * - * DLC Plain Length - * 0 - 7 0 - 7 - * 8 - 15 8 - */ - - if (dlc_value > 8) { - frame->can_dlc = 8; - qemu_log_mask(LOG_GUEST_ERROR, "Maximum DLC value for Classic CAN" - " frame is 8. Only 8 byte data will be sent.\n"); - } else { - frame->can_dlc = dlc_value; + 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 * i; + val = 8 * (3 - i); frame->data[j] = extract32(s->regs[reg_num + 2 + (j / 4)], val, 8); i++; @@ -948,6 +931,33 @@ static void process_cancellation_requests(XlnxVersalCANFDState *s) 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, @@ -955,7 +965,6 @@ static void store_rx_sequential(XlnxVersalCANFDState *s, bool rx_fifo_id, uint8_t filter_index) { int i; - bool is_canfd_frame; uint8_t dlc = frame->can_dlc; uint8_t rx_reg_num = 0; uint32_t dlc_reg_val = 0; @@ -999,30 +1008,21 @@ static void store_rx_sequential(XlnxVersalCANFDState *s, NUM_REGS_PER_MSG_SPACE)); } - s->regs[store_location] = frame->can_id; + s->regs[store_location] = frame_to_reg_id(frame); - dlc = frame->can_dlc; + dlc_reg_val = FIELD_DP32(0, RB_DLC_REGISTER, DLC, can_len2dlc(dlc)); - if (frame->flags == QEMU_CAN_FRMF_TYPE_FD) { - is_canfd_frame = true; + if (frame->flags & QEMU_CAN_FRMF_TYPE_FD) { + dlc_reg_val |= FIELD_DP32(0, RB_DLC_REGISTER, FDF, 1); - /* Store dlc value in Xilinx specific format. */ - for (i = 0; i < ARRAY_SIZE(canfd_dlc_array); i++) { - if (canfd_dlc_array[i] == frame->can_dlc) { - dlc_reg_val = FIELD_DP32(0, RB_DLC_REGISTER, DLC, 8 + i); - } + if (frame->flags & QEMU_CAN_FRMF_BRS) { + dlc_reg_val |= FIELD_DP32(0, RB_DLC_REGISTER, BRS, 1); } - } else { - is_canfd_frame = false; - - if (frame->can_dlc > 8) { - dlc = 8; + 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, DLC, dlc); } - dlc_reg_val |= FIELD_DP32(0, RB_DLC_REGISTER, FDF, is_canfd_frame); dlc_reg_val |= FIELD_DP32(0, RB_DLC_REGISTER, TIMESTAMP, rx_timestamp); dlc_reg_val |= FIELD_DP32(0, RB_DLC_REGISTER, MATCHED_FILTER_INDEX, filter_index); @@ -1034,19 +1034,19 @@ static void store_rx_sequential(XlnxVersalCANFDState *s, case 0: rx_reg_num = i / 4; - data_reg_val = FIELD_DP32(0, RB_DW0_REGISTER, DATA_BYTES3, + 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_BYTES2, + 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_BYTES1, + 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_BYTES0, + 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 @@ -1090,11 +1090,12 @@ static void update_rx_sequential(XlnxVersalCANFDState *s, 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] & - frame->can_id; + 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, @@ -1143,18 +1144,8 @@ static void update_rx_sequential(XlnxVersalCANFDState *s, read_index = ARRAY_FIELD_EX32(s->regs, RX_FIFO_STATUS_REGISTER, RI); store_index = read_index + fill_level; - if (read_index == s->cfg.rx0_fifo - 1) { - /* - * When ri is s->cfg.rx0_fifo - 1 i.e. max, it goes cyclic that - * means we reset the ri to 0x0. - */ - read_index = 0; - ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, RI, - read_index); - } - if (store_index > s->cfg.rx0_fifo - 1) { - store_index -= s->cfg.rx0_fifo - 1; + store_index -= s->cfg.rx0_fifo; } store_location = R_RB_ID_REGISTER + @@ -1171,18 +1162,8 @@ static void update_rx_sequential(XlnxVersalCANFDState *s, RI_1); store_index = read_index + fill_level; - if (read_index == s->cfg.rx1_fifo - 1) { - /* - * When ri is s->cfg.rx1_fifo - 1 i.e. max, it goes cyclic that - * means we reset the ri to 0x0. - */ - read_index = 0; - ARRAY_FIELD_DP32(s->regs, RX_FIFO_STATUS_REGISTER, RI_1, - read_index); - } - if (store_index > s->cfg.rx1_fifo - 1) { - store_index -= s->cfg.rx1_fifo - 1; + store_index -= s->cfg.rx1_fifo; } store_location = R_RB_ID_REGISTER_1 + @@ -1264,18 +1245,8 @@ static void tx_fifo_stamp(XlnxVersalCANFDState *s, uint32_t tb0_regid) " Discarding the message\n"); ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXEOFLW, 1); } else { - if (read_index == s->cfg.tx_fifo - 1) { - /* - * When ri is s->cfg.tx_fifo - 1 i.e. max, it goes cyclic that - * means we reset the ri to 0x0. - */ - read_index = 0; - ARRAY_FIELD_DP32(s->regs, TX_EVENT_FIFO_STATUS_REGISTER, TXE_RI, - read_index); - } - if (store_index > s->cfg.tx_fifo - 1) { - store_index -= s->cfg.tx_fifo - 1; + store_index -= s->cfg.tx_fifo; } assert(store_index < s->cfg.tx_fifo); @@ -1307,12 +1278,15 @@ static void tx_fifo_stamp(XlnxVersalCANFDState *s, uint32_t tb0_regid) } } -static gint g_cmp_ids(gconstpointer data1, gconstpointer data2) +static gint g_cmp_ids(gconstpointer data1, gconstpointer data2, gpointer d) { tx_ready_reg_info *tx_reg_1 = (tx_ready_reg_info *) data1; tx_ready_reg_info *tx_reg_2 = (tx_ready_reg_info *) data2; - return tx_reg_1->can_id - tx_reg_2->can_id; + 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) @@ -1324,8 +1298,6 @@ static void free_list(GSList *list) } g_slist_free(list); - - return; } static GSList *prepare_tx_data(XlnxVersalCANFDState *s) @@ -1344,7 +1316,7 @@ static GSList *prepare_tx_data(XlnxVersalCANFDState *s) 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); + list = g_slist_sort_with_data(list, g_cmp_ids, NULL); } reg_ready >>= 1; @@ -2060,7 +2032,7 @@ static const VMStateDescription vmstate_canfd = { .name = TYPE_XILINX_CANFD, .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, XlnxVersalCANFDState, XLNX_VERSAL_CANFD_R_MAX), VMSTATE_PTIMER(canfd_timer, XlnxVersalCANFDState), @@ -2068,7 +2040,7 @@ static const VMStateDescription vmstate_canfd = { } }; -static Property canfd_core_properties[] = { +static const 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), @@ -2078,14 +2050,13 @@ static Property canfd_core_properties[] = { 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) +static void canfd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = canfd_reset; + device_class_set_legacy_reset(dc, canfd_reset); dc->realize = canfd_realize; device_class_set_props(dc, canfd_core_properties); dc->vmsd = &vmstate_canfd; diff --git a/hw/net/can/xlnx-zynqmp-can.c b/hw/net/can/xlnx-zynqmp-can.c index e93e6c5e194..ca9edd4a5b8 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), @@ -1124,15 +1169,14 @@ static const VMStateDescription vmstate_can = { } }; -static Property xlnx_zynqmp_can_properties[] = { +static const Property xlnx_zynqmp_can_properties[] = { DEFINE_PROP_UINT32("ext_clk_freq", XlnxZynqMPCANState, cfg.ext_clk_freq, CAN_DEFAULT_CLOCK), DEFINE_PROP_LINK("canbus", XlnxZynqMPCANState, canbus, TYPE_CAN_BUS, CanBusState *), - DEFINE_PROP_END_OF_LIST(), }; -static void xlnx_zynqmp_can_class_init(ObjectClass *klass, void *data) +static void xlnx_zynqmp_can_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index a596f7fbc6b..d49032059bb 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -27,7 +27,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "qemu/timer.h" -#include +#include /* for crc32 */ #include "qom/object.h" #include "trace.h" @@ -551,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]; } @@ -913,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); @@ -923,29 +924,28 @@ 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() } }; -static Property dp8393x_properties[] = { +static const Property dp8393x_properties[] = { DEFINE_NIC_PROPERTIES(dp8393xState, conf), DEFINE_PROP_LINK("dma_mr", dp8393xState, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0), DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false), - DEFINE_PROP_END_OF_LIST(), }; -static void dp8393x_class_init(ObjectClass *klass, void *data) +static void dp8393x_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 093c2d45315..a80a7b0cdb4 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -33,8 +33,8 @@ #include "net/eth.h" #include "net/net.h" #include "net/checksum.h" -#include "sysemu/sysemu.h" -#include "sysemu/dma.h" +#include "system/system.h" +#include "system/dma.h" #include "qemu/iov.h" #include "qemu/module.h" #include "qemu/range.h" @@ -127,14 +127,8 @@ 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) @@ -180,7 +174,7 @@ e1000_autoneg_done(E1000State *s) static bool have_autoneg(E1000State *s) { - return chkflag(AUTONEG) && (s->phy_reg[MII_BMCR] & MII_BMCR_AUTOEN); + return (s->phy_reg[MII_BMCR] & MII_BMCR_AUTOEN); } static void @@ -308,35 +302,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); @@ -378,7 +371,7 @@ static bool e1000_vet_init_need(void *opaque) return chkflag(VET); } -static void e1000_reset_hold(Object *obj) +static void e1000_reset_hold(Object *obj, ResetType type) { E1000State *d = E1000(obj); E1000BaseClass *edc = E1000_GET_CLASS(d); @@ -1217,55 +1210,51 @@ enum { NWRITEOPS = ARRAY_SIZE(macreg_writeops) }; enum { MAC_ACCESS_PARTIAL = 1, MAC_ACCESS_FLAG_NEEDED = 2 }; -#define markflag(x) ((E1000_FLAG_##x << 2) | MAC_ACCESS_FLAG_NEEDED) /* In the array below the meaning of the bits is: [f|f|f|f|f|f|n|p] * f - flag bits (up to 6 possible flags) * n - flag needed - * p - partially implenented */ + * p - partially implemented */ 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), - [ECOL] = markflag(MAC), [MCC] = markflag(MAC), - [DC] = markflag(MAC), [TNCRS] = markflag(MAC), - [RLEC] = markflag(MAC), [XONRXC] = markflag(MAC), - [XOFFTXC] = markflag(MAC), [RFC] = markflag(MAC), - [TSCTFC] = markflag(MAC), [MGTPRC] = markflag(MAC), - [WUS] = markflag(MAC), [AIT] = markflag(MAC), - [FFLT] = markflag(MAC), [FFMT] = markflag(MAC), - [SCC] = markflag(MAC), [FCRUC] = markflag(MAC), - [LATECOL] = markflag(MAC), [COLC] = markflag(MAC), - [SEQEC] = markflag(MAC), [CEXTERR] = markflag(MAC), - [XONTXC] = markflag(MAC), [XOFFRXC] = markflag(MAC), - [RJC] = markflag(MAC), [RNBC] = markflag(MAC), - [MGTPDC] = markflag(MAC), [MGTPTC] = markflag(MAC), - [RUC] = markflag(MAC), [ROC] = markflag(MAC), - [GORCL] = markflag(MAC), [GORCH] = markflag(MAC), - [GOTCL] = markflag(MAC), [GOTCH] = markflag(MAC), - [BPRC] = markflag(MAC), [MPRC] = markflag(MAC), - [TSCTC] = markflag(MAC), [PRC64] = markflag(MAC), - [PRC127] = markflag(MAC), [PRC255] = markflag(MAC), - [PRC511] = markflag(MAC), [PRC1023] = markflag(MAC), - [PRC1522] = markflag(MAC), [PTC64] = markflag(MAC), - [PTC127] = markflag(MAC), [PTC255] = markflag(MAC), - [PTC511] = markflag(MAC), [PTC1023] = markflag(MAC), - [PTC1522] = markflag(MAC), [MPTC] = markflag(MAC), - [BPTC] = markflag(MAC), - - [TDFH] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [TDFT] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [TDFHS] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [TDFTS] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [TDFPC] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [RDFH] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [RDFT] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [RDFHS] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [RDFTS] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [RDFPC] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [PBM] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [IPAV] = MAC_ACCESS_FLAG_NEEDED, [WUC] = MAC_ACCESS_FLAG_NEEDED, + [IP6AT] = MAC_ACCESS_FLAG_NEEDED, [IP4AT] = MAC_ACCESS_FLAG_NEEDED, + [FFVT] = MAC_ACCESS_FLAG_NEEDED, [WUPM] = MAC_ACCESS_FLAG_NEEDED, + [ECOL] = MAC_ACCESS_FLAG_NEEDED, [MCC] = MAC_ACCESS_FLAG_NEEDED, + [DC] = MAC_ACCESS_FLAG_NEEDED, [TNCRS] = MAC_ACCESS_FLAG_NEEDED, + [RLEC] = MAC_ACCESS_FLAG_NEEDED, [XONRXC] = MAC_ACCESS_FLAG_NEEDED, + [XOFFTXC] = MAC_ACCESS_FLAG_NEEDED, [RFC] = MAC_ACCESS_FLAG_NEEDED, + [TSCTFC] = MAC_ACCESS_FLAG_NEEDED, [MGTPRC] = MAC_ACCESS_FLAG_NEEDED, + [WUS] = MAC_ACCESS_FLAG_NEEDED, [AIT] = MAC_ACCESS_FLAG_NEEDED, + [FFLT] = MAC_ACCESS_FLAG_NEEDED, [FFMT] = MAC_ACCESS_FLAG_NEEDED, + [SCC] = MAC_ACCESS_FLAG_NEEDED, [FCRUC] = MAC_ACCESS_FLAG_NEEDED, + [LATECOL] = MAC_ACCESS_FLAG_NEEDED, [COLC] = MAC_ACCESS_FLAG_NEEDED, + [SEQEC] = MAC_ACCESS_FLAG_NEEDED, [CEXTERR] = MAC_ACCESS_FLAG_NEEDED, + [XONTXC] = MAC_ACCESS_FLAG_NEEDED, [XOFFRXC] = MAC_ACCESS_FLAG_NEEDED, + [RJC] = MAC_ACCESS_FLAG_NEEDED, [RNBC] = MAC_ACCESS_FLAG_NEEDED, + [MGTPDC] = MAC_ACCESS_FLAG_NEEDED, [MGTPTC] = MAC_ACCESS_FLAG_NEEDED, + [RUC] = MAC_ACCESS_FLAG_NEEDED, [ROC] = MAC_ACCESS_FLAG_NEEDED, + [GORCL] = MAC_ACCESS_FLAG_NEEDED, [GORCH] = MAC_ACCESS_FLAG_NEEDED, + [GOTCL] = MAC_ACCESS_FLAG_NEEDED, [GOTCH] = MAC_ACCESS_FLAG_NEEDED, + [BPRC] = MAC_ACCESS_FLAG_NEEDED, [MPRC] = MAC_ACCESS_FLAG_NEEDED, + [TSCTC] = MAC_ACCESS_FLAG_NEEDED, [PRC64] = MAC_ACCESS_FLAG_NEEDED, + [PRC127] = MAC_ACCESS_FLAG_NEEDED, [PRC255] = MAC_ACCESS_FLAG_NEEDED, + [PRC511] = MAC_ACCESS_FLAG_NEEDED, [PRC1023] = MAC_ACCESS_FLAG_NEEDED, + [PRC1522] = MAC_ACCESS_FLAG_NEEDED, [PTC64] = MAC_ACCESS_FLAG_NEEDED, + [PTC127] = MAC_ACCESS_FLAG_NEEDED, [PTC255] = MAC_ACCESS_FLAG_NEEDED, + [PTC511] = MAC_ACCESS_FLAG_NEEDED, [PTC1023] = MAC_ACCESS_FLAG_NEEDED, + [PTC1522] = MAC_ACCESS_FLAG_NEEDED, [MPTC] = MAC_ACCESS_FLAG_NEEDED, + [BPTC] = MAC_ACCESS_FLAG_NEEDED, + + [TDFH] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [TDFT] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [TDFHS] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [TDFTS] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [TDFPC] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [RDFH] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [RDFT] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [RDFHS] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [RDFTS] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [RDFPC] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [PBM] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, }; static void @@ -1394,11 +1383,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); @@ -1432,20 +1416,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; - - return chkflag(MAC); -} - static bool e1000_tso_state_needed(void *opaque) { E1000State *s = opaque; @@ -1457,8 +1427,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), @@ -1472,8 +1441,7 @@ static const VMStateDescription vmstate_e1000_full_mac_state = { .name = "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() } @@ -1485,7 +1453,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), @@ -1507,7 +1475,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. */ @@ -1579,7 +1547,7 @@ static const VMStateDescription vmstate_e1000 = { 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, @@ -1687,7 +1655,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); @@ -1697,19 +1666,12 @@ static void pci_e1000_realize(PCIDevice *pci_dev, Error **errp) e1000_flush_queue_timer, d); } -static Property e1000_properties[] = { +static const 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, compat_flags, E1000_FLAG_TSO_BIT, true), DEFINE_PROP_BIT("init-vet", E1000State, compat_flags, E1000_FLAG_VET_BIT, true), - DEFINE_PROP_END_OF_LIST(), }; typedef struct E1000Info { @@ -1719,7 +1681,7 @@ typedef struct E1000Info { uint16_t phy_id2; } E1000Info; -static void e1000_class_init(ObjectClass *klass, void *data) +static void e1000_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -1757,7 +1719,7 @@ static const TypeInfo e1000_base_info = { .instance_init = e1000_instance_init, .class_size = sizeof(E1000BaseClass), .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -1795,10 +1757,10 @@ static void e1000_register_types(void) type_info.name = info->name; type_info.parent = TYPE_E1000_BASE; - type_info.class_data = (void *)info; + type_info.class_data = info; type_info.class_init = e1000_class_init; - type_register(&type_info); + type_register_static(&type_info); } } diff --git a/hw/net/e1000_regs.h b/hw/net/e1000_regs.h index 8a4ce82034c..39f4882510a 100644 --- a/hw/net/e1000_regs.h +++ b/hw/net/e1000_regs.h @@ -130,7 +130,7 @@ #define E1000_GCR2 0x05B64 /* 3GIO Control Register 2 */ #define E1000_FFLT_DBG 0x05F04 /* Debug Register */ -#define E1000_HICR 0x08F00 /* Host Inteface Control */ +#define E1000_HICR 0x08F00 /* Host Interface Control */ #define E1000_RXMTRL 0x0B634 /* Time sync Rx EtherType and Msg Type - RW */ #define E1000_RXUDP 0x0B638 /* Time Sync Rx UDP Port - RW */ diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index c3848797b8f..89e6d52ba0f 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -40,7 +40,7 @@ #include "net/tap.h" #include "qemu/module.h" #include "qemu/range.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/hw.h" #include "hw/net/mii.h" #include "hw/pci/msi.h" @@ -320,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; @@ -352,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); } } @@ -373,8 +372,7 @@ static int e1000e_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); + int ret = pci_pm_init(pdev, offset, &local_err); if (local_err) { error_report_err(local_err); @@ -513,7 +511,7 @@ static void e1000e_pci_uninit(PCIDevice *pci_dev) msi_uninit(pci_dev); } -static void e1000e_qdev_reset_hold(Object *obj) +static void e1000e_qdev_reset_hold(Object *obj, ResetType type) { E1000EState *s = E1000E(obj); @@ -564,7 +562,7 @@ 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), @@ -588,7 +586,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() @@ -609,7 +607,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), @@ -662,7 +660,7 @@ static PropertyInfo e1000e_prop_disable_vnet, e1000e_prop_subsys_ven, e1000e_prop_subsys; -static Property e1000e_properties[] = { +static const Property e1000e_properties[] = { DEFINE_NIC_PROPERTIES(E1000EState, conf), DEFINE_PROP_SIGNED("disable_vnet_hdr", E1000EState, disable_vnet, false, e1000e_prop_disable_vnet, bool), @@ -673,10 +671,9 @@ static Property e1000e_properties[] = { 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) +static void e1000e_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); ResettableClass *rc = RESETTABLE_CLASS(class); @@ -724,7 +721,7 @@ static const TypeInfo e1000e_info = { .instance_size = sizeof(E1000EState), .class_init = e1000e_class_init, .instance_init = e1000e_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index f8aeafa16be..24138587905 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -40,7 +40,7 @@ #include "hw/net/mii.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "net_tx_pkt.h" #include "net_rx_pkt.h" @@ -123,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) { @@ -398,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) { @@ -587,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]); @@ -810,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]; @@ -836,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; @@ -852,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]); @@ -867,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; @@ -896,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 } }; @@ -908,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 } }; @@ -930,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)) { @@ -960,7 +932,7 @@ e1000e_start_xmit(E1000ECore *core, const E1000E_TxRing *txr) } 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); @@ -1397,17 +1369,17 @@ 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]); @@ -1418,11 +1390,11 @@ e1000e_write_hdr_to_rx_buffers(E1000ECore *core, } 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]; @@ -1460,7 +1432,7 @@ e1000e_update_rx_stats(E1000ECore *core, size_t pkt_size, size_t 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; @@ -1521,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; @@ -1530,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; @@ -1568,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; @@ -1585,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); } } @@ -1594,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; @@ -1607,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)); } } @@ -2852,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); } } @@ -3330,12 +3306,6 @@ 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) { @@ -3347,22 +3317,6 @@ e1000e_autoneg_resume(E1000ECore *core) } } -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, @@ -3375,9 +3329,6 @@ 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, E1000E_MAX_TX_FRAGS); } @@ -3401,8 +3352,6 @@ 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_uninit(core->tx[i].tx_pkt); } @@ -3572,5 +3521,12 @@ e1000e_core_post_load(E1000ECore *core) */ 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 66b025cc43f..01510ca78b4 100644 --- a/hw/net/e1000e_core.h +++ b/hw/net/e1000e_core.h @@ -98,8 +98,6 @@ struct E1000Core { E1000IntrDelayTimer eitr[E1000E_MSIX_VEC_NUM]; - VMChangeStateEntry *vmstate; - uint32_t itr_guest_value; uint32_t eitr_guest_value[E1000E_MSIX_VEC_NUM]; diff --git a/hw/net/e1000x_regs.h b/hw/net/e1000x_regs.h index 13760c66d31..e9a74de6f42 100644 --- a/hw/net/e1000x_regs.h +++ b/hw/net/e1000x_regs.h @@ -839,7 +839,7 @@ union e1000_rx_desc_packet_split { #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_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 */ @@ -900,7 +900,7 @@ struct e1000_context_desc { uint16_t tucse; /* TCP checksum end */ } tcp_fields; } upper_setup; - uint32_t cmd_and_length; /* */ + uint32_t cmd_and_length; union { uint32_t data; struct { diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index dc07984ae9a..d47df5a97fd 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 @@ -48,9 +50,9 @@ #include "net/net.h" #include "net/eth.h" #include "hw/nvram/eeprom93xx.h" -#include "sysemu/sysemu.h" -#include "sysemu/dma.h" -#include "sysemu/reset.h" +#include "system/system.h" +#include "system/dma.h" +#include "system/reset.h" #include "qemu/bitops.h" #include "qemu/module.h" #include "qapi/error.h" @@ -549,9 +551,7 @@ static void e100_pci_reset(EEPRO100State *s, Error **errp) if (info->power_management) { /* Power Management Capabilities */ int cfg_offset = 0xdc; - int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM, - cfg_offset, PCI_PM_SIZEOF, - errp); + int r = pci_pm_init(&s->dev, cfg_offset, errp); if (r < 0) { return; } @@ -1772,7 +1772,7 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) static const VMStateDescription vmstate_eepro100 = { .version_id = 3, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, EEPRO100State), VMSTATE_UNUSED(32), VMSTATE_BUFFER(mult, EEPRO100State), @@ -1874,7 +1874,9 @@ static void e100_nic_realize(PCIDevice *pci_dev, Error **errp) nic_reset(s); s->nic = qemu_new_nic(&net_eepro100_info, &s->conf, - object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s); + object_get_typename(OBJECT(pci_dev)), + pci_dev->qdev.id, + &pci_dev->qdev.mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str)); @@ -1883,8 +1885,7 @@ static void e100_nic_realize(PCIDevice *pci_dev, Error **errp) s->vmstate = g_memdup(&vmstate_eepro100, sizeof(vmstate_eepro100)); s->vmstate->name = qemu_get_queue(s->nic)->model; - vmstate_register(VMSTATE_IF(&pci_dev->qdev), VMSTATE_INSTANCE_ID_ANY, - s->vmstate, s); + vmstate_register_any(VMSTATE_IF(&pci_dev->qdev), s->vmstate, s); } static void eepro100_instance_init(Object *obj) @@ -2055,12 +2056,11 @@ static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s) return eepro100_get_class_by_name(object_get_typename(OBJECT(s))); } -static Property e100_properties[] = { +static const Property e100_properties[] = { DEFINE_NIC_PROPERTIES(EEPRO100State, conf), - DEFINE_PROP_END_OF_LIST(), }; -static void eepro100_class_init(ObjectClass *klass, void *data) +static void eepro100_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -2094,12 +2094,12 @@ static void eepro100_register_types(void) type_info.class_init = eepro100_class_init; type_info.instance_size = sizeof(EEPRO100State); type_info.instance_init = eepro100_instance_init; - type_info.interfaces = (InterfaceInfo[]) { + type_info.interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }; - type_register(&type_info); + type_register_static(&type_info); } } diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c deleted file mode 100644 index 1b82aec7943..00000000000 --- 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 798ea33d080..846f6cbc5d9 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -36,7 +36,6 @@ #include "registers.h" #include "qapi/error.h" #include "qemu/log.h" -#include "qemu/module.h" /* #define HEX_DUMP */ /* #define DEBUG_REGISTER */ @@ -390,8 +389,10 @@ static void etsec_realize(DeviceState *dev, Error **errp) { eTSEC *etsec = ETSEC_COMMON(dev); + qemu_macaddr_default_if_unset(&etsec->conf.macaddr); 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); @@ -414,33 +415,29 @@ static void etsec_instance_init(Object *obj) sysbus_init_irq(sbd, &etsec->err_irq); } -static Property etsec_properties[] = { +static const Property etsec_properties[] = { DEFINE_NIC_PROPERTIES(eTSEC, conf), - DEFINE_PROP_END_OF_LIST(), }; -static void etsec_class_init(ObjectClass *klass, void *data) +static void etsec_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = etsec_realize; - dc->reset = etsec_reset; + dc->desc = "Freescale Enhanced Three-Speed Ethernet Controller"; + device_class_set_legacy_reset(dc, etsec_reset); device_class_set_props(dc, etsec_properties); - /* Supported by ppce500 machine */ - dc->user_creatable = true; + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } -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_DYNAMIC_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/miim.c b/hw/net/fsl_etsec/miim.c index b48d2cb57bd..4e9169907a9 100644 --- a/hw/net/fsl_etsec/miim.c +++ b/hw/net/fsl_etsec/miim.c @@ -29,13 +29,6 @@ /* #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; @@ -47,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; @@ -84,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; diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c index 788463f1b62..42216de6c9a 100644 --- a/hw/net/fsl_etsec/rings.c +++ b/hw/net/fsl_etsec/rings.c @@ -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 702b001be2f..c41ce889cf1 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "hw/irq.h" #include "hw/net/ftgmac100.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" @@ -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 @@ static int ftgmac100_insert_vlan(FTGMAC100State *s, int frame_size, 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; @@ -1020,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; @@ -1074,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)); @@ -1103,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), @@ -1146,22 +1246,26 @@ 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() } }; -static Property ftgmac100_properties[] = { +static const Property ftgmac100_properties[] = { DEFINE_PROP_BOOL("aspeed", FTGMAC100State, aspeed, false), DEFINE_NIC_PROPERTIES(FTGMAC100State, conf), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_BOOL("dma64", FTGMAC100State, dma64, false), }; -static void ftgmac100_class_init(ObjectClass *klass, void *data) +static void ftgmac100_class_init(ObjectClass *klass, const 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; @@ -1303,25 +1407,24 @@ 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() } }; -static Property aspeed_mii_properties[] = { +static const Property aspeed_mii_properties[] = { DEFINE_PROP_LINK("nic", AspeedMiiState, nic, TYPE_FTGMAC100, FTGMAC100State *), - DEFINE_PROP_END_OF_LIST(), }; -static void aspeed_mii_class_init(ObjectClass *klass, void *data) +static void aspeed_mii_class_init(ObjectClass *klass, const 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 6defa9d3a12..c1ff3e6c564 100644 --- a/hw/net/i82596.c +++ b/hw/net/i82596.c @@ -5,7 +5,7 @@ * This work is licensed under the GNU GPL license version 2 or later. * * This software was written to be compatible with the specification: - * https://www.intel.com/assets/pdf/general/82596ca.pdf + * https://parisc.docs.kernel.org/en/latest/_downloads/96672be0650d9fc046bbcea40b92482f/82596CA.pdf */ #include "qemu/osdep.h" @@ -15,11 +15,11 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "exec/address-spaces.h" +#include "system/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 @@ -177,6 +177,26 @@ static void set_individual_address(I82596State *s, uint32_t addr) trace_i82596_new_mac(nc->info_str); } +static void i82596_configure(I82596State *s, uint32_t addr) +{ + uint8_t byte_cnt; + byte_cnt = get_byte(addr + 8) & 0x0f; + + byte_cnt = MAX(byte_cnt, 4); + byte_cnt = MIN(byte_cnt, sizeof(s->config)); + /* copy byte_cnt max. */ + address_space_read(&address_space_memory, addr + 8, + MEMTXATTRS_UNSPECIFIED, s->config, byte_cnt); + /* config byte according to page 35ff */ + s->config[2] &= 0x82; /* mask valid bits */ + s->config[2] |= 0x40; + s->config[7] &= 0xf7; /* clear zero bit */ + assert(I596_NOCRC_INS == 0); /* do CRC insertion */ + s->config[10] = MAX(s->config[10], 5); /* min frame length */ + s->config[12] &= 0x40; /* only full duplex field valid */ + s->config[13] |= 0x3f; /* set ones in byte 13 */ +} + static void set_multicast_list(I82596State *s, uint32_t addr) { uint16_t mc_count, i; @@ -234,7 +254,6 @@ static void command_loop(I82596State *s) { uint16_t cmd; uint16_t status; - uint8_t byte_cnt; DBG(printf("STARTING COMMAND LOOP cmd_p=%08x\n", s->cmd_p)); @@ -254,20 +273,7 @@ static void command_loop(I82596State *s) set_individual_address(s, s->cmd_p); break; case CmdConfigure: - byte_cnt = get_byte(s->cmd_p + 8) & 0x0f; - byte_cnt = MAX(byte_cnt, 4); - byte_cnt = MIN(byte_cnt, sizeof(s->config)); - /* copy byte_cnt max. */ - address_space_read(&address_space_memory, s->cmd_p + 8, - MEMTXATTRS_UNSPECIFIED, s->config, byte_cnt); - /* config byte according to page 35ff */ - s->config[2] &= 0x82; /* mask valid bits */ - s->config[2] |= 0x40; - s->config[7] &= 0xf7; /* clear zero bit */ - assert(I596_NOCRC_INS == 0); /* do CRC insertion */ - s->config[10] = MAX(s->config[10], 5); /* min frame length */ - s->config[12] &= 0x40; /* only full duplex field valid */ - s->config[13] |= 0x3f; /* set ones in byte 13 */ + i82596_configure(s, s->cmd_p); break; case CmdTDR: /* get signal LINK */ @@ -282,7 +288,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 */ @@ -713,7 +719,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() @@ -726,7 +732,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/i82596.h b/hw/net/i82596.h index f0bbe810eb2..dc1fa1a1dc0 100644 --- a/hw/net/i82596.h +++ b/hw/net/i82596.h @@ -3,8 +3,8 @@ #define I82596_IOPORT_SIZE 0x20 -#include "exec/memory.h" -#include "exec/address-spaces.h" +#include "system/memory.h" +#include "system/address-spaces.h" #define PORT_RESET 0x00 /* reset 82596 */ #define PORT_SELFTEST 0x01 /* selftest */ diff --git a/hw/net/igb.c b/hw/net/igb.c index 8ff832acfc3..e4c02365d67 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -44,7 +44,7 @@ #include "net/tap.h" #include "qemu/module.h" #include "qemu/range.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/hw.h" #include "hw/net/mii.h" #include "hw/pci/pci.h" @@ -78,6 +78,7 @@ struct IGBState { uint32_t ioaddr; IGBCore core; + bool has_flr; }; #define IGB_CAP_SRIOV_OFFSET (0x160) @@ -101,6 +102,9 @@ static void igb_write_config(PCIDevice *dev, uint32_t addr, 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)) { @@ -122,6 +126,12 @@ igb_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) 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) { @@ -315,7 +325,7 @@ igb_init_net_peer(IGBState *s, PCIDevice *pci_dev, uint8_t *macaddr) int i; s->nic = qemu_new_nic(&net_igb_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; @@ -339,7 +349,6 @@ igb_init_net_peer(IGBState *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); } } @@ -347,8 +356,7 @@ 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); + int ret = pci_pm_init(pdev, offset, &local_err); if (local_err) { error_report_err(local_err); @@ -427,15 +435,23 @@ static void igb_pci_realize(PCIDevice *pci_dev, Error **errp) } /* 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); + if (!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, + errp)) { + igb_cleanup_msix(s); + return; + } pcie_sriov_pf_init_vf_bar(pci_dev, IGBVF_MMIO_BAR_IDX, PCI_BASE_ADDRESS_MEM_TYPE_64 | PCI_BASE_ADDRESS_MEM_PREFETCH, @@ -472,14 +488,12 @@ static void igb_pci_uninit(PCIDevice *pci_dev) msi_uninit(pci_dev); } -static void igb_qdev_reset_hold(Object *obj) +static void igb_qdev_reset_hold(Object *obj, ResetType type) { - PCIDevice *d = PCI_DEVICE(obj); IGBState *s = IGB(obj); trace_e1000e_cb_qdev_reset_hold(); - pcie_sriov_pf_disable_vfs(d); igb_core_reset(&s->core); } @@ -506,7 +520,7 @@ static const VMStateDescription igb_vmstate_tx_ctx = { .name = "igb-tx-ctx", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .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), @@ -519,7 +533,7 @@ static const VMStateDescription igb_vmstate_tx = { .name = "igb-tx", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { + .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), @@ -534,7 +548,7 @@ static const VMStateDescription igb_vmstate_intr_timer = { .name = "igb-intr-timer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(timer, IGBIntrDelayTimer), VMSTATE_BOOL(running, IGBIntrDelayTimer), VMSTATE_END_OF_LIST() @@ -555,7 +569,7 @@ static const VMStateDescription igb_vmstate = { .minimum_version_id = 1, .pre_save = igb_pre_save, .post_load = igb_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, IGBState), VMSTATE_MSIX(parent_obj, IGBState), @@ -580,12 +594,12 @@ static const VMStateDescription igb_vmstate = { } }; -static Property igb_properties[] = { +static const Property igb_properties[] = { DEFINE_NIC_PROPERTIES(IGBState, conf), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_BOOL("x-pcie-flr-init", IGBState, has_flr, true), }; -static void igb_class_init(ObjectClass *class, void *data) +static void igb_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); ResettableClass *rc = RESETTABLE_CLASS(class); @@ -621,7 +635,7 @@ static const TypeInfo igb_info = { .instance_size = sizeof(IGBState), .class_init = igb_class_init, .instance_init = igb_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/net/igb_common.h b/hw/net/igb_common.h index 5c261ba9d39..b316a5bcfa5 100644 --- a/hw/net/igb_common.h +++ b/hw/net/igb_common.h @@ -152,5 +152,6 @@ enum { 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 index 8b6b75c522f..39e3ce1c8fe 100644 --- a/hw/net/igb_core.c +++ b/hw/net/igb_core.c @@ -44,7 +44,7 @@ #include "hw/net/mii.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "net_tx_pkt.h" #include "net_rx_pkt.h" @@ -160,14 +160,6 @@ igb_intmgr_timer_resume(IGBIntrDelayTimer *timer) } } -static void -igb_intmgr_timer_pause(IGBIntrDelayTimer *timer) -{ - if (timer->running) { - timer_del(timer->timer); - } -} - static void igb_intrmgr_on_msix_throttling_timer(void *opaque) { @@ -212,16 +204,6 @@ igb_intrmgr_resume(IGBCore *core) } } -static void -igb_intrmgr_pause(IGBCore *core) -{ - int i; - - for (i = 0; i < IGB_INTR_NUM; i++) { - igb_intmgr_timer_pause(&core->eitr[i]); - } -} - static void igb_intrmgr_reset(IGBCore *core) { @@ -267,6 +249,29 @@ igb_rx_use_legacy_descriptor(IGBCore *core) 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) { @@ -392,8 +397,7 @@ igb_rss_calc_hash(IGBCore *core, struct NetRxPkt *pkt, E1000E_RSSInfo *info) type = NetPktRssIpV6Udp; break; default: - assert(false); - return 0; + g_assert_not_reached(); } return net_rx_pkt_calc_rss_hash(pkt, type, (uint8_t *) &core->mac[RSSRK]); @@ -694,24 +698,15 @@ static uint32_t igb_rx_wb_eic(IGBCore *core, int queue_idx) return (ent & E1000_IVAR_VALID) ? BIT(ent & 0x1f) : 0; } -typedef struct E1000E_RingInfo_st { - int dbah; - int dbal; - int dlen; - int dh; - int dt; - int idx; -} E1000E_RingInfo; - static inline bool -igb_ring_empty(IGBCore *core, const E1000E_RingInfo *r) +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 E1000E_RingInfo *r) +igb_ring_base(IGBCore *core, const E1000ERingInfo *r) { uint64_t bah = core->mac[r->dbah]; uint64_t bal = core->mac[r->dbal]; @@ -720,13 +715,13 @@ igb_ring_base(IGBCore *core, const E1000E_RingInfo *r) } static inline uint64_t -igb_ring_head_descr(IGBCore *core, const E1000E_RingInfo *r) +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 E1000E_RingInfo *r, uint32_t count) +igb_ring_advance(IGBCore *core, const E1000ERingInfo *r, uint32_t count) { core->mac[r->dh] += count; @@ -736,7 +731,7 @@ igb_ring_advance(IGBCore *core, const E1000E_RingInfo *r, uint32_t count) } static inline uint32_t -igb_ring_free_descr_num(IGBCore *core, const E1000E_RingInfo *r) +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]); @@ -751,17 +746,16 @@ igb_ring_free_descr_num(IGBCore *core, const E1000E_RingInfo *r) } g_assert_not_reached(); - return 0; } static inline bool -igb_ring_enabled(IGBCore *core, const E1000E_RingInfo *r) +igb_ring_enabled(IGBCore *core, const E1000ERingInfo *r) { return core->mac[r->dlen] > 0; } typedef struct IGB_TxRing_st { - const E1000E_RingInfo *i; + const E1000ERingInfo *i; struct igb_tx *tx; } IGB_TxRing; @@ -774,7 +768,7 @@ igb_mq_queue_idx(int base_reg_idx, int reg_idx) static inline void igb_tx_ring_init(IGBCore *core, IGB_TxRing *txr, int idx) { - static const E1000E_RingInfo i[IGB_NUM_QUEUES] = { + 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 }, @@ -800,13 +794,13 @@ igb_tx_ring_init(IGBCore *core, IGB_TxRing *txr, int idx) } typedef struct E1000E_RxRing_st { - const E1000E_RingInfo *i; + const E1000ERingInfo *i; } E1000E_RxRing; static inline void igb_rx_ring_init(IGBCore *core, E1000E_RxRing *rxr, int idx) { - static const E1000E_RingInfo i[IGB_NUM_QUEUES] = { + 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 }, @@ -833,7 +827,7 @@ igb_rx_ring_init(IGBCore *core, E1000E_RxRing *rxr, int idx) static uint32_t igb_txdesc_writeback(IGBCore *core, dma_addr_t base, union e1000_adv_tx_desc *tx_desc, - const E1000E_RingInfo *txi) + const E1000ERingInfo *txi) { PCIDevice *d; uint32_t cmd_type_len = le32_to_cpu(tx_desc->read.cmd_type_len); @@ -866,7 +860,7 @@ igb_txdesc_writeback(IGBCore *core, dma_addr_t base, } static inline bool -igb_tx_enabled(IGBCore *core, const E1000E_RingInfo *txi) +igb_tx_enabled(IGBCore *core, const E1000ERingInfo *txi) { bool vmdq = core->mac[MRQC] & 1; uint16_t qn = txi->idx; @@ -883,7 +877,7 @@ igb_start_xmit(IGBCore *core, const IGB_TxRing *txr) PCIDevice *d; dma_addr_t base; union e1000_adv_tx_desc desc; - const E1000E_RingInfo *txi = txr->i; + const E1000ERingInfo *txi = txr->i; uint32_t eic = 0; if (!igb_tx_enabled(core, txi)) { @@ -918,7 +912,7 @@ igb_start_xmit(IGBCore *core, const IGB_TxRing *txr) } static uint32_t -igb_rxbufsize(IGBCore *core, const E1000E_RingInfo *r) +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; @@ -930,7 +924,7 @@ igb_rxbufsize(IGBCore *core, const E1000E_RingInfo *r) } static bool -igb_has_rxbufs(IGBCore *core, const E1000E_RingInfo *r, size_t total_size) +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); @@ -941,6 +935,14 @@ igb_has_rxbufs(IGBCore *core, const E1000E_RingInfo *r, size_t total_size) 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) { @@ -1225,21 +1227,77 @@ igb_read_lgcy_rx_descr(IGBCore *core, struct e1000_rx_desc *desc, } static inline void -igb_read_adv_rx_descr(IGBCore *core, union e1000_adv_rx_desc *desc, - hwaddr *buff_addr) +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_rx_descr(IGBCore *core, union e1000_rx_desc_union *desc, - hwaddr *buff_addr) +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, buff_addr); - } else { - igb_read_adv_rx_descr(core, &desc->adv, buff_addr); + 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 @@ -1281,15 +1339,11 @@ igb_verify_csum_in_sw(IGBCore *core, } static void -igb_build_rx_metadata(IGBCore *core, - struct NetRxPkt *pkt, - bool is_eop, - const E1000E_RSSInfo *rss_info, uint16_t etqf, bool ts, - uint16_t *pkt_info, uint16_t *hdr_info, - uint32_t *rss, - uint32_t *status_flags, - uint16_t *ip_id, - uint16_t *vlan_tag) +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; @@ -1298,7 +1352,6 @@ igb_build_rx_metadata(IGBCore *core, *status_flags = E1000_RXD_STAT_DD; /* No additional metadata needed for non-EOP descriptors */ - /* TODO: EOP apply only to status so don't skip whole function. */ if (!is_eop) { goto func_exit; } @@ -1315,64 +1368,6 @@ igb_build_rx_metadata(IGBCore *core, trace_e1000e_rx_metadata_vlan(*vlan_tag); } - /* Packet parsing results */ - if ((core->mac[RXCSUM] & E1000_RXCSUM_PCSD) != 0) { - if (rss_info->enabled) { - *rss = cpu_to_le32(rss_info->hash); - trace_igb_rx_metadata_rss(*rss); - } - } 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 (l4hdr_proto == ETH_L4_HDR_PROTO_TCP && net_rx_pkt_is_tcp_ack(pkt)) { - *status_flags |= E1000_RXD_STAT_ACK; - trace_e1000e_rx_metadata_ack(); - } - - if (pkt_info) { - *pkt_info = rss_info->enabled ? rss_info->type : 0; - - if (etqf < 8) { - *pkt_info |= (BIT(11) | etqf) << 4; - } else { - if (hasip4) { - *pkt_info |= E1000_ADVRXD_PKT_IP4; - } - - if (hasip6) { - *pkt_info |= E1000_ADVRXD_PKT_IP6; - } - - switch (l4hdr_proto) { - case ETH_L4_HDR_PROTO_TCP: - *pkt_info |= E1000_ADVRXD_PKT_TCP; - break; - - case ETH_L4_HDR_PROTO_UDP: - *pkt_info |= E1000_ADVRXD_PKT_UDP; - break; - - case ETH_L4_HDR_PROTO_SCTP: - *pkt_info |= E1000_ADVRXD_PKT_SCTP; - break; - - default: - break; - } - } - } - - if (hdr_info) { - *hdr_info = 0; - } - - if (ts) { - *status_flags |= BIT(16); - } - /* RX CSO information */ if (hasip6 && (core->mac[RFCTL] & E1000_RFCTL_IPV6_XSUM_DIS)) { trace_e1000e_rx_metadata_ipv6_sum_disabled(); @@ -1428,56 +1423,168 @@ igb_build_rx_metadata(IGBCore *core, 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 etqf, bool ts, + const E1000E_RSSInfo *rss_info, uint16_t length) { - uint32_t status_flags, rss; - uint16_t ip_id; + uint32_t status_flags; assert(!rss_info->enabled); + + memset(desc, 0, sizeof(*desc)); desc->length = cpu_to_le16(length); - desc->csum = 0; + igb_build_rx_metadata_common(core, pkt, pkt != NULL, + &status_flags, + &desc->special); - igb_build_rx_metadata(core, pkt, pkt != NULL, - rss_info, etqf, ts, - NULL, NULL, &rss, - &status_flags, &ip_id, - &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); - igb_build_rx_metadata(core, pkt, pkt != NULL, - rss_info, etqf, ts, - &desc->wb.lower.lo_dword.pkt_info, - &desc->wb.lower.lo_dword.hdr_info, - &desc->wb.lower.hi_dword.rss, - &desc->wb.upper.status_error, - &desc->wb.lower.hi_dword.csum_ip.ip_id, - &desc->wb.upper.vlan); + 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, uint16_t length) +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, - etqf, ts, length); + 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, length); + etqf, ts, pdma_st->bastate.written[1]); } } @@ -1514,20 +1621,7 @@ igb_pci_dma_write_rx_desc(IGBCore *core, PCIDevice *dev, dma_addr_t addr, } static void -igb_write_to_rx_buffers(IGBCore *core, - PCIDevice *d, - hwaddr ba, - uint16_t *written, - const char *data, - dma_addr_t data_len) -{ - trace_igb_rx_desc_buff_write(ba, *written, data, data_len); - pci_dma_write(d, ba + *written, data, data_len); - *written += data_len; -} - -static void -igb_update_rx_stats(IGBCore *core, const E1000E_RingInfo *rxi, +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); @@ -1545,12 +1639,256 @@ igb_update_rx_stats(IGBCore *core, const E1000E_RingInfo *rxi, } static inline bool -igb_rx_descr_threshold_hit(IGBCore *core, const E1000E_RingInfo *rxi) +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, @@ -1560,95 +1898,61 @@ igb_write_packet_to_guest(IGBCore *core, struct NetRxPkt *pkt, PCIDevice *d; dma_addr_t base; union e1000_rx_desc_union desc; - size_t desc_size; - size_t desc_offset = 0; - size_t iov_ofs = 0; - - 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 = rxr->i; - size_t bufsize = igb_rxbufsize(core, rxi); - + 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 { - hwaddr ba; - uint16_t written = 0; + memset(&pdma_st.bastate, 0, sizeof(IGBBAState)); bool is_last = false; - desc_size = total_size - desc_offset; - - if (desc_size > bufsize) { - desc_size = bufsize; - } - 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); - pci_dma_read(d, base, &desc, core->rx_desc_len); - - trace_e1000e_rx_descr(rxi->idx, base, core->rx_desc_len); - - igb_read_rx_descr(core, &desc, &ba); - - if (ba) { - if (desc_offset < size) { - static const uint32_t fcs_pad; - size_t iov_copy; - size_t copy_size = size - desc_offset; - if (copy_size > bufsize) { - copy_size = bufsize; - } + igb_read_rx_descr(core, &desc, &pdma_st, rxi); - /* Copy packet payload */ - while (copy_size) { - iov_copy = MIN(copy_size, iov->iov_len - iov_ofs); - - igb_write_to_rx_buffers(core, d, ba, &written, - iov->iov_base + iov_ofs, iov_copy); - - copy_size -= iov_copy; - iov_ofs += iov_copy; - if (iov_ofs == iov->iov_len) { - iov++; - iov_ofs = 0; - } - } - - if (desc_offset + desc_size >= total_size) { - /* Simulate FCS checksum presence in the last descriptor */ - igb_write_to_rx_buffers(core, d, ba, &written, - (const char *) &fcs_pad, e1000x_fcs_len(core->mac)); - } - } - } else { /* as per intel docs; skip descriptors with null buf addr */ - trace_e1000e_rx_null_descriptor(); - } - desc_offset += desc_size; - if (desc_offset >= total_size) { + 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 ? core->rx_pkt : NULL, - rss_info, etqf, ts, written); - igb_pci_dma_write_rx_desc(core, d, base, &desc, core->rx_desc_len); + 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_ring_advance(core, rxi, core->rx_desc_len / E1000_MIN_RX_DESC_LEN); - - } while (desc_offset < total_size); - - igb_update_rx_stats(core, rxi, size, total_size); + igb_update_rx_stats(core, rxi, pdma_st.size, pdma_st.total_size); } static bool -igb_rx_strip_vlan(IGBCore *core, const E1000E_RingInfo *rxi) +igb_rx_strip_vlan(IGBCore *core, const E1000ERingInfo *rxi) { if (core->mac[MRQC] & 1) { uint16_t pool = rxi->idx % IGB_NUM_VM_POOLS; @@ -2153,11 +2457,13 @@ static void igb_set_vfmailbox(IGBCore *core, int index, uint32_t val) } } -static void igb_vf_reset(IGBCore *core, uint16_t vfn) +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; @@ -2236,7 +2542,7 @@ static void igb_set_vtctrl(IGBCore *core, int index, uint32_t val) if (val & E1000_CTRL_RST) { vfn = (index - PVTCTRL0) / 0x40; - igb_vf_reset(core, vfn); + igb_core_vf_reset(core, vfn); } } @@ -2753,7 +3059,7 @@ igb_update_rx_offloads(IGBCore *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); } } @@ -3964,12 +4270,6 @@ igb_core_read(IGBCore *core, hwaddr addr, unsigned size) return 0; } -static inline void -igb_autoneg_pause(IGBCore *core) -{ - timer_del(core->autoneg_timer); -} - static void igb_autoneg_resume(IGBCore *core) { @@ -3981,22 +4281,6 @@ igb_autoneg_resume(IGBCore *core) } } -static void -igb_vm_state_change(void *opaque, bool running, RunState state) -{ - IGBCore *core = opaque; - - if (running) { - trace_e1000e_vm_state_running(); - igb_intrmgr_resume(core); - igb_autoneg_resume(core); - } else { - trace_e1000e_vm_state_stopped(); - igb_autoneg_pause(core); - igb_intrmgr_pause(core); - } -} - void igb_core_pci_realize(IGBCore *core, const uint16_t *eeprom_templ, @@ -4009,8 +4293,6 @@ igb_core_pci_realize(IGBCore *core, igb_autoneg_timer, core); igb_intrmgr_pci_realize(core); - core->vmstate = qemu_add_vm_change_state_handler(igb_vm_state_change, core); - for (i = 0; i < IGB_NUM_QUEUES; i++) { net_tx_pkt_init(&core->tx[i].tx_pkt, E1000E_MAX_TX_FRAGS); } @@ -4034,8 +4316,6 @@ igb_core_pci_uninit(IGBCore *core) igb_intrmgr_pci_unint(core); - qemu_del_vm_change_state_handler(core->vmstate); - for (i = 0; i < IGB_NUM_QUEUES; i++) { net_tx_pkt_uninit(core->tx[i].tx_pkt); } @@ -4260,5 +4540,12 @@ igb_core_post_load(IGBCore *core) */ 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 index 9cbbfd516bd..d70b54e318f 100644 --- a/hw/net/igb_core.h +++ b/hw/net/igb_core.h @@ -90,8 +90,6 @@ struct IGBCore { IGBIntrDelayTimer eitr[IGB_INTR_NUM]; - VMChangeStateEntry *vmstate; - uint32_t eitr_guest_value[IGB_INTR_NUM]; uint8_t permanent_mac[ETH_ALEN]; @@ -130,6 +128,9 @@ 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); diff --git a/hw/net/igb_regs.h b/hw/net/igb_regs.h index 82ff195dfc3..4dc4c31da27 100644 --- a/hw/net/igb_regs.h +++ b/hw/net/igb_regs.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * This is copied + edited from kernel header files in * drivers/net/ethernet/intel/igb @@ -364,7 +364,7 @@ union e1000_adv_rx_desc { /* 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 exra info for certain messages */ +/* 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 */ @@ -452,6 +452,7 @@ union e1000_adv_rx_desc { #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 @@ -490,7 +491,7 @@ union e1000_adv_rx_desc { #define E1000_VF_MBX_INIT_DELAY 500 /* usec delay between retries */ #define E1000_VT_MSGINFO_SHIFT 16 -/* bits 23:16 are used for exra info for certain messages */ +/* 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 */ @@ -692,11 +693,20 @@ union e1000_adv_rx_desc { #define E1000_STATUS_NUM_VFS_SHIFT 14 -#define E1000_ADVRXD_PKT_IP4 BIT(4) -#define E1000_ADVRXD_PKT_IP6 BIT(6) -#define E1000_ADVRXD_PKT_TCP BIT(8) -#define E1000_ADVRXD_PKT_UDP BIT(9) -#define E1000_ADVRXD_PKT_SCTP BIT(10) +#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) { diff --git a/hw/net/igbvf.c b/hw/net/igbvf.c index d55e1e8a6ab..31d72c4977d 100644 --- a/hw/net/igbvf.c +++ b/hw/net/igbvf.c @@ -204,6 +204,10 @@ static void igbvf_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, { 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) @@ -266,6 +270,11 @@ static void igbvf_pci_realize(PCIDevice *dev, Error **errp) 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"); } @@ -273,6 +282,13 @@ static void igbvf_pci_realize(PCIDevice *dev, Error **errp) 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); @@ -283,10 +299,11 @@ static void igbvf_pci_uninit(PCIDevice *dev) msix_uninit(dev, &s->msix, &s->msix); } -static void igbvf_class_init(ObjectClass *class, void *data) +static void igbvf_class_init(ObjectClass *class, const 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; @@ -295,6 +312,8 @@ static void igbvf_class_init(ObjectClass *class, void *data) 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; @@ -306,7 +325,7 @@ static const TypeInfo igbvf_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(IgbVfState), .class_init = igbvf_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index 5d1f1f104cc..e5e34dd1a47 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -26,22 +26,21 @@ #include "hw/net/imx_fec.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/log.h" #include "qemu/module.h" #include "net/checksum.h" #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() @@ -204,33 +203,20 @@ static const VMStateDescription vmstate_imx_eth_txdescs = { static const VMStateDescription vmstate_imx_eth = { .name = TYPE_IMX_FEC, - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { + .version_id = 3, + .minimum_version_id = 3, + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, IMXFECState, ENET_MAX), VMSTATE_UINT32(rx_descriptor, IMXFECState), VMSTATE_UINT32(tx_descriptor[0], IMXFECState), - VMSTATE_UINT32(phy_status, IMXFECState), - VMSTATE_UINT32(phy_control, IMXFECState), - VMSTATE_UINT32(phy_advertise, IMXFECState), - VMSTATE_UINT32(phy_int, IMXFECState), - VMSTATE_UINT32(phy_int_mask, IMXFECState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_imx_eth_txdescs, NULL }, }; -#define PHY_INT_ENERGYON (1 << 7) -#define PHY_INT_AUTONEG_COMPLETE (1 << 6) -#define PHY_INT_FAULT (1 << 5) -#define PHY_INT_DOWN (1 << 4) -#define PHY_INT_AUTONEG_LP (1 << 3) -#define PHY_INT_PARFAULT (1 << 2) -#define PHY_INT_AUTONEG_PAGE (1 << 1) - static void imx_eth_update(IMXFECState *s); /* @@ -239,47 +225,19 @@ static void imx_eth_update(IMXFECState *s); * For now we don't handle any GPIO/interrupt line, so the OS will * have to poll for the PHY status. */ -static void imx_phy_update_irq(IMXFECState *s) -{ - imx_eth_update(s); -} - -static void imx_phy_update_link(IMXFECState *s) +static void imx_phy_update_irq(void *opaque, int n, int level) { - /* Autonegotiation status mirrors link status. */ - if (qemu_get_queue(s->nic)->link_down) { - trace_imx_phy_update_link("down"); - s->phy_status &= ~0x0024; - s->phy_int |= PHY_INT_DOWN; - } else { - trace_imx_phy_update_link("up"); - s->phy_status |= 0x0024; - s->phy_int |= PHY_INT_ENERGYON; - s->phy_int |= PHY_INT_AUTONEG_COMPLETE; - } - imx_phy_update_irq(s); + imx_eth_update(opaque); } static void imx_eth_set_link(NetClientState *nc) { - imx_phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc))); -} - -static void imx_phy_reset(IMXFECState *s) -{ - trace_imx_phy_reset(); - - s->phy_status = 0x7809; - s->phy_control = 0x3000; - s->phy_advertise = 0x01e1; - s->phy_int_mask = 0; - s->phy_int = 0; - imx_phy_update_link(s); + lan9118_phy_update_link(&IMX_FEC(qemu_get_nic_opaque(nc))->mii, + nc->link_down); } static uint32_t imx_phy_read(IMXFECState *s, int reg) { - uint32_t val; uint32_t phy = reg / 32; if (!s->phy_connected) { @@ -297,54 +255,7 @@ static uint32_t imx_phy_read(IMXFECState *s, int reg) reg %= 32; - switch (reg) { - case 0: /* Basic Control */ - val = s->phy_control; - break; - case 1: /* Basic Status */ - val = s->phy_status; - break; - case 2: /* ID1 */ - val = 0x0007; - break; - case 3: /* ID2 */ - val = 0xc0d1; - break; - case 4: /* Auto-neg advertisement */ - val = s->phy_advertise; - break; - case 5: /* Auto-neg Link Partner Ability */ - val = 0x0f71; - break; - case 6: /* Auto-neg Expansion */ - val = 1; - break; - case 29: /* Interrupt source. */ - val = s->phy_int; - s->phy_int = 0; - imx_phy_update_irq(s); - break; - case 30: /* Interrupt mask */ - val = s->phy_int_mask; - break; - case 17: - case 18: - case 27: - case 31: - qemu_log_mask(LOG_UNIMP, "[%s.phy]%s: reg %d not implemented\n", - TYPE_IMX_FEC, __func__, reg); - val = 0; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", - TYPE_IMX_FEC, __func__, reg); - val = 0; - break; - } - - trace_imx_phy_read(val, phy, reg); - - return val; + return lan9118_phy_read(&s->mii, reg); } static void imx_phy_write(IMXFECState *s, int reg, uint32_t val) @@ -366,39 +277,7 @@ static void imx_phy_write(IMXFECState *s, int reg, uint32_t val) reg %= 32; - trace_imx_phy_write(val, phy, reg); - - switch (reg) { - case 0: /* Basic Control */ - if (val & 0x8000) { - imx_phy_reset(s); - } else { - s->phy_control = val & 0x7980; - /* Complete autonegotiation immediately. */ - if (val & 0x1000) { - s->phy_status |= 0x0020; - } - } - break; - case 4: /* Auto-neg advertisement */ - s->phy_advertise = (val & 0x2d7f) | 0x80; - break; - case 30: /* Interrupt mask */ - s->phy_int_mask = val & 0xff; - imx_phy_update_irq(s); - break; - case 17: - case 18: - case 27: - case 31: - qemu_log_mask(LOG_UNIMP, "[%s.phy)%s: reg %d not implemented\n", - TYPE_IMX_FEC, __func__, reg); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", - TYPE_IMX_FEC, __func__, reg); - break; - } + lan9118_phy_write(&s->mii, reg, val); } static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr) @@ -683,9 +562,6 @@ static void imx_eth_reset(DeviceState *d) s->rx_descriptor = 0; memset(s->tx_descriptor, 0, sizeof(s->tx_descriptor)); - - /* We also reset the PHY */ - imx_phy_reset(s); } static uint32_t imx_default_read(IMXFECState *s, uint32_t index) @@ -792,7 +668,6 @@ static void imx_default_write(IMXFECState *s, uint32_t index, uint32_t value) { qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4); - return; } static void imx_fec_write(IMXFECState *s, uint32_t index, uint32_t value) @@ -1330,31 +1205,37 @@ static void imx_eth_realize(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->irq[0]); sysbus_init_irq(sbd, &s->irq[1]); + qemu_init_irq(&s->mii_irq, imx_phy_update_irq, s, 0); + object_initialize_child(OBJECT(s), "mii", &s->mii, TYPE_LAN9118_PHY); + if (!sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->mii), errp)) { + return; + } + qdev_connect_gpio_out(DEVICE(&s->mii), 0, &s->mii_irq); + qemu_macaddr_default_if_unset(&s->conf.macaddr); 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); } -static Property imx_eth_properties[] = { +static const 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(), }; -static void imx_eth_class_init(ObjectClass *klass, void *data) +static void imx_eth_class_init(ObjectClass *klass, const 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 e5c4af182df..6dda1e5c940 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -16,14 +16,14 @@ #include "net/net.h" #include "net/eth.h" #include "hw/irq.h" +#include "hw/net/lan9118_phy.h" #include "hw/net/lan9118.h" #include "hw/ptimer.h" #include "hw/qdev-properties.h" #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 @@ -140,16 +140,14 @@ do { printf("lan9118: " fmt , ## __VA_ARGS__); } while (0) #define MAC_CR_RXEN 0x00000004 #define MAC_CR_RESERVED 0x7f404213 -#define PHY_INT_ENERGYON 0x80 -#define PHY_INT_AUTONEG_COMPLETE 0x40 -#define PHY_INT_FAULT 0x20 -#define PHY_INT_DOWN 0x10 -#define PHY_INT_AUTONEG_LP 0x08 -#define PHY_INT_PARFAULT 0x04 -#define PHY_INT_AUTONEG_PAGE 0x02 - #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, @@ -166,14 +164,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), @@ -182,7 +180,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() } }; @@ -223,11 +221,8 @@ struct lan9118_state { uint32_t mac_mii_data; uint32_t mac_flow; - uint32_t phy_status; - uint32_t phy_control; - uint32_t phy_advertise; - uint32_t phy_int; - uint32_t phy_int_mask; + Lan9118PhyState mii; + IRQState mii_irq; int32_t eeprom_writable; uint8_t eeprom[128]; @@ -269,9 +264,9 @@ struct lan9118_state { static const VMStateDescription vmstate_lan9118 = { .name = "lan9118", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { + .version_id = 3, + .minimum_version_id = 3, + .fields = (const VMStateField[]) { VMSTATE_PTIMER(timer, lan9118_state), VMSTATE_UINT32(irq_cfg, lan9118_state), VMSTATE_UINT32(int_sts, lan9118_state), @@ -296,11 +291,6 @@ static const VMStateDescription vmstate_lan9118 = { VMSTATE_UINT32(mac_mii_acc, lan9118_state), VMSTATE_UINT32(mac_mii_data, lan9118_state), VMSTATE_UINT32(mac_flow, lan9118_state), - VMSTATE_UINT32(phy_status, lan9118_state), - VMSTATE_UINT32(phy_control, lan9118_state), - VMSTATE_UINT32(phy_advertise, lan9118_state), - VMSTATE_UINT32(phy_int, lan9118_state), - VMSTATE_UINT32(phy_int_mask, lan9118_state), VMSTATE_INT32(eeprom_writable, lan9118_state), VMSTATE_UINT8_ARRAY(eeprom, lan9118_state, 128), VMSTATE_INT32(tx_fifo_size, lan9118_state), @@ -380,9 +370,11 @@ static void lan9118_reload_eeprom(lan9118_state *s) lan9118_mac_changed(s); } -static void phy_update_irq(lan9118_state *s) +static void lan9118_update_irq(void *opaque, int n, int level) { - if (s->phy_int & s->phy_int_mask) { + lan9118_state *s = opaque; + + if (level) { s->int_sts |= PHY_INT; } else { s->int_sts &= ~PHY_INT; @@ -390,33 +382,10 @@ static void phy_update_irq(lan9118_state *s) lan9118_update(s); } -static void phy_update_link(lan9118_state *s) -{ - /* Autonegotiation status mirrors link status. */ - if (qemu_get_queue(s->nic)->link_down) { - s->phy_status &= ~0x0024; - s->phy_int |= PHY_INT_DOWN; - } else { - s->phy_status |= 0x0024; - s->phy_int |= PHY_INT_ENERGYON; - s->phy_int |= PHY_INT_AUTONEG_COMPLETE; - } - phy_update_irq(s); -} - static void lan9118_set_link(NetClientState *nc) { - phy_update_link(qemu_get_nic_opaque(nc)); -} - -static void phy_reset(lan9118_state *s) -{ - s->phy_status = 0x7809; - s->phy_control = 0x3000; - s->phy_advertise = 0x01e1; - s->phy_int_mask = 0; - s->phy_int = 0; - phy_update_link(s); + lan9118_phy_update_link(&LAN9118(qemu_get_nic_opaque(nc))->mii, + nc->link_down); } static void lan9118_reset(DeviceState *d) @@ -473,8 +442,6 @@ static void lan9118_reset(DeviceState *d) s->read_word_n = 0; s->write_word_n = 0; - phy_reset(s); - s->eeprom_writable = 0; lan9118_reload_eeprom(s); } @@ -544,7 +511,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; } @@ -673,7 +640,7 @@ static void do_tx_packet(lan9118_state *s) uint32_t status; /* FIXME: Honor TX disable, and allow queueing of packets. */ - if (s->phy_control & 0x4000) { + if (s->mii.control & 0x4000) { /* This assumes the receive routine doesn't touch the VLANClient. */ qemu_receive_packet(qemu_get_queue(s->nic), s->txp->data, s->txp->len); } else { @@ -793,8 +760,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; @@ -815,68 +796,6 @@ static void tx_fifo_push(lan9118_state *s, uint32_t val) } } -static uint32_t do_phy_read(lan9118_state *s, int reg) -{ - uint32_t val; - - switch (reg) { - case 0: /* Basic Control */ - return s->phy_control; - case 1: /* Basic Status */ - return s->phy_status; - case 2: /* ID1 */ - return 0x0007; - case 3: /* ID2 */ - return 0xc0d1; - case 4: /* Auto-neg advertisement */ - return s->phy_advertise; - case 5: /* Auto-neg Link Partner Ability */ - return 0x0f71; - case 6: /* Auto-neg Expansion */ - return 1; - /* TODO 17, 18, 27, 29, 30, 31 */ - case 29: /* Interrupt source. */ - val = s->phy_int; - s->phy_int = 0; - phy_update_irq(s); - return val; - case 30: /* Interrupt mask */ - return s->phy_int_mask; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "do_phy_read: PHY read reg %d\n", reg); - return 0; - } -} - -static void do_phy_write(lan9118_state *s, int reg, uint32_t val) -{ - switch (reg) { - case 0: /* Basic Control */ - if (val & 0x8000) { - phy_reset(s); - break; - } - s->phy_control = val & 0x7980; - /* Complete autonegotiation immediately. */ - if (val & 0x1000) { - s->phy_status |= 0x0020; - } - break; - case 4: /* Auto-neg advertisement */ - s->phy_advertise = (val & 0x2d7f) | 0x80; - break; - /* TODO 17, 18, 27, 31 */ - case 30: /* Interrupt mask */ - s->phy_int_mask = val & 0xff; - phy_update_irq(s); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "do_phy_write: PHY write reg %d = 0x%04x\n", reg, val); - } -} - static void do_mac_write(lan9118_state *s, int reg, uint32_t val) { switch (reg) { @@ -910,9 +829,9 @@ static void do_mac_write(lan9118_state *s, int reg, uint32_t val) if (val & 2) { DPRINTF("PHY write %d = 0x%04x\n", (val >> 6) & 0x1f, s->mac_mii_data); - do_phy_write(s, (val >> 6) & 0x1f, s->mac_mii_data); + lan9118_phy_write(&s->mii, (val >> 6) & 0x1f, s->mac_mii_data); } else { - s->mac_mii_data = do_phy_read(s, (val >> 6) & 0x1f); + s->mac_mii_data = lan9118_phy_read(&s->mii, (val >> 6) & 0x1f); DPRINTF("PHY read %d = 0x%04x\n", (val >> 6) & 0x1f, s->mac_mii_data); } @@ -1107,7 +1026,7 @@ static void lan9118_writel(void *opaque, hwaddr offset, break; case CSR_PMT_CTRL: if (val & 0x400) { - phy_reset(s); + lan9118_phy_reset(&s->mii); } s->pmt_ctrl &= ~0x34e; s->pmt_ctrl |= (val & 0x34e); @@ -1354,6 +1273,13 @@ static void lan9118_realize(DeviceState *dev, Error **errp) const MemoryRegionOps *mem_ops = s->mode_16bit ? &lan9118_16bit_mem_ops : &lan9118_mem_ops; + qemu_init_irq(&s->mii_irq, lan9118_update_irq, s, 0); + object_initialize_child(OBJECT(s), "mii", &s->mii, TYPE_LAN9118_PHY); + if (!sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->mii), errp)) { + return; + } + qdev_connect_gpio_out(DEVICE(&s->mii), 0, &s->mii_irq); + memory_region_init_io(&s->mmio, OBJECT(dev), mem_ops, s, "lan9118-mmio", 0x100); sysbus_init_mmio(sbd, &s->mmio); @@ -1361,7 +1287,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++) { @@ -1377,17 +1304,16 @@ static void lan9118_realize(DeviceState *dev, Error **errp) ptimer_transaction_commit(s->timer); } -static Property lan9118_properties[] = { +static const Property lan9118_properties[] = { DEFINE_NIC_PROPERTIES(lan9118_state, conf), DEFINE_PROP_UINT32("mode_16bit", lan9118_state, mode_16bit, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void lan9118_class_init(ObjectClass *klass, void *data) +static void lan9118_class_init(ObjectClass *klass, const 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; @@ -1407,14 +1333,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/lan9118_phy.c b/hw/net/lan9118_phy.c new file mode 100644 index 00000000000..4c4e03df115 --- /dev/null +++ b/hw/net/lan9118_phy.c @@ -0,0 +1,222 @@ +/* + * SMSC LAN9118 PHY emulation + * + * Copyright (c) 2009 CodeSourcery, LLC. + * Written by Paul Brook + * + * Copyright (c) 2013 Jean-Christophe Dubois. + * + * 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/net/lan9118_phy.h" +#include "hw/net/mii.h" +#include "hw/irq.h" +#include "hw/resettable.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "trace.h" + +#define PHY_INT_ENERGYON (1 << 7) +#define PHY_INT_AUTONEG_COMPLETE (1 << 6) +#define PHY_INT_FAULT (1 << 5) +#define PHY_INT_DOWN (1 << 4) +#define PHY_INT_AUTONEG_LP (1 << 3) +#define PHY_INT_PARFAULT (1 << 2) +#define PHY_INT_AUTONEG_PAGE (1 << 1) + +static void lan9118_phy_update_irq(Lan9118PhyState *s) +{ + qemu_set_irq(s->irq, !!(s->ints & s->int_mask)); +} + +uint16_t lan9118_phy_read(Lan9118PhyState *s, int reg) +{ + uint16_t val; + + switch (reg) { + case MII_BMCR: + val = s->control; + break; + case MII_BMSR: + val = s->status; + break; + case MII_PHYID1: + val = SMSCLAN9118_PHYID1; + break; + case MII_PHYID2: + val = SMSCLAN9118_PHYID2; + break; + case MII_ANAR: + val = s->advertise; + break; + case MII_ANLPAR: + val = MII_ANLPAR_PAUSEASY | MII_ANLPAR_PAUSE | MII_ANLPAR_T4 | + MII_ANLPAR_TXFD | MII_ANLPAR_TX | MII_ANLPAR_10FD | + MII_ANLPAR_10 | MII_ANLPAR_CSMACD; + break; + case MII_ANER: + val = MII_ANER_NWAY; + break; + case 29: /* Interrupt source. */ + val = s->ints; + s->ints = 0; + lan9118_phy_update_irq(s); + break; + case 30: /* Interrupt mask */ + val = s->int_mask; + break; + case 17: + case 18: + case 27: + case 31: + qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n", + __func__, reg); + val = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n", + __func__, reg); + val = 0; + break; + } + + trace_lan9118_phy_read(val, reg); + + return val; +} + +void lan9118_phy_write(Lan9118PhyState *s, int reg, uint16_t val) +{ + trace_lan9118_phy_write(val, reg); + + switch (reg) { + case MII_BMCR: + if (val & MII_BMCR_RESET) { + lan9118_phy_reset(s); + } else { + s->control = val & (MII_BMCR_LOOPBACK | MII_BMCR_SPEED100 | + MII_BMCR_AUTOEN | MII_BMCR_PDOWN | MII_BMCR_FD | + MII_BMCR_CTST); + /* Complete autonegotiation immediately. */ + if (val & MII_BMCR_AUTOEN) { + s->status |= MII_BMSR_AN_COMP; + } + } + break; + case MII_ANAR: + s->advertise = (val & (MII_ANAR_RFAULT | MII_ANAR_PAUSE_ASYM | + MII_ANAR_PAUSE | MII_ANAR_TXFD | MII_ANAR_10FD | + MII_ANAR_10 | MII_ANAR_SELECT)) + | MII_ANAR_TX; + break; + case 30: /* Interrupt mask */ + s->int_mask = val & 0xff; + lan9118_phy_update_irq(s); + break; + case 17: + case 18: + case 27: + case 31: + qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n", + __func__, reg); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n", + __func__, reg); + break; + } +} + +void lan9118_phy_update_link(Lan9118PhyState *s, bool link_down) +{ + s->link_down = link_down; + + /* Autonegotiation status mirrors link status. */ + if (link_down) { + trace_lan9118_phy_update_link("down"); + s->status &= ~(MII_BMSR_AN_COMP | MII_BMSR_LINK_ST); + s->ints |= PHY_INT_DOWN; + } else { + trace_lan9118_phy_update_link("up"); + s->status |= MII_BMSR_AN_COMP | MII_BMSR_LINK_ST; + s->ints |= PHY_INT_ENERGYON; + s->ints |= PHY_INT_AUTONEG_COMPLETE; + } + lan9118_phy_update_irq(s); +} + +void lan9118_phy_reset(Lan9118PhyState *s) +{ + trace_lan9118_phy_reset(); + + s->control = MII_BMCR_AUTOEN | MII_BMCR_SPEED100; + s->status = MII_BMSR_100TX_FD + | MII_BMSR_100TX_HD + | MII_BMSR_10T_FD + | MII_BMSR_10T_HD + | MII_BMSR_AUTONEG + | MII_BMSR_EXTCAP; + s->advertise = MII_ANAR_TXFD + | MII_ANAR_TX + | MII_ANAR_10FD + | MII_ANAR_10 + | MII_ANAR_CSMACD; + s->int_mask = 0; + s->ints = 0; + lan9118_phy_update_link(s, s->link_down); +} + +static void lan9118_phy_reset_hold(Object *obj, ResetType type) +{ + Lan9118PhyState *s = LAN9118_PHY(obj); + + lan9118_phy_reset(s); +} + +static void lan9118_phy_init(Object *obj) +{ + Lan9118PhyState *s = LAN9118_PHY(obj); + + qdev_init_gpio_out(DEVICE(s), &s->irq, 1); +} + +static const VMStateDescription vmstate_lan9118_phy = { + .name = "lan9118-phy", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT16(status, Lan9118PhyState), + VMSTATE_UINT16(control, Lan9118PhyState), + VMSTATE_UINT16(advertise, Lan9118PhyState), + VMSTATE_UINT16(ints, Lan9118PhyState), + VMSTATE_UINT16(int_mask, Lan9118PhyState), + VMSTATE_BOOL(link_down, Lan9118PhyState), + VMSTATE_END_OF_LIST() + } +}; + +static void lan9118_phy_class_init(ObjectClass *klass, const void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + rc->phases.hold = lan9118_phy_reset_hold; + dc->vmsd = &vmstate_lan9118_phy; +} + +static const TypeInfo types[] = { + { + .name = TYPE_LAN9118_PHY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Lan9118PhyState), + .instance_init = lan9118_phy_init, + .class_init = lan9118_phy_class_init, + } +}; + +DEFINE_TYPES(types) diff --git a/hw/net/lance.c b/hw/net/lance.c index 4c5f01baad9..dfb855c23a4 100644 --- a/hw/net/lance.c +++ b/hw/net/lance.c @@ -43,7 +43,7 @@ #include "hw/net/lance.h" #include "hw/qdev-properties.h" #include "trace.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static void parent_lance_reset(void *opaque, int irq, int level) @@ -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() } @@ -137,21 +137,20 @@ static void lance_instance_init(Object *obj) DEVICE(obj)); } -static Property lance_properties[] = { +static const Property lance_properties[] = { DEFINE_PROP_LINK("dma", SysBusPCNetState, state.dma_opaque, TYPE_DEVICE, DeviceState *), DEFINE_NIC_PROPERTIES(SysBusPCNetState, state.conf), - DEFINE_PROP_END_OF_LIST(), }; -static void lance_class_init(ObjectClass *klass, void *data) +static void lance_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 e37f7fabe95..9e1dd215467 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 "system/system.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 */ @@ -155,19 +158,18 @@ static void lasi_82596_instance_init(Object *obj) DEVICE(obj)); } -static Property lasi_82596_properties[] = { +static const Property lasi_82596_properties[] = { DEFINE_NIC_PROPERTIES(SysBusI82596State, state.conf), - DEFINE_PROP_END_OF_LIST(), }; -static void lasi_82596_class_init(ObjectClass *klass, void *data) +static void lasi_82596_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = lasi_82596_realize; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->fw_name = "ethernet"; - dc->reset = lasi_82596_reset; + 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 8aa27bd322d..ae128fa3114 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); } @@ -660,19 +660,18 @@ static void mcf_fec_instance_init(Object *obj) } } -static Property mcf_fec_properties[] = { +static const Property mcf_fec_properties[] = { DEFINE_NIC_PROPERTIES(mcf_fec_state, conf), - DEFINE_PROP_END_OF_LIST(), }; -static void mcf_fec_class_init(ObjectClass *oc, void *data) +static void mcf_fec_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); 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 2632634df3e..e6759e26ca6 100644 --- a/hw/net/meson.build +++ b/hw/net/meson.build @@ -1,5 +1,5 @@ system_ss.add(when: 'CONFIG_DP8393X', if_true: files('dp8393x.c')) -system_ss.add(when: 'CONFIG_XEN', if_true: files('xen_nic.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 @@ -19,6 +19,7 @@ system_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('vmxnet3.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_LAN9118_PHY', if_true: files('lan9118_phy.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')) @@ -33,14 +34,14 @@ system_ss.add(when: 'CONFIG_MARVELL_88W8618', if_true: files('mv88w8618_eth.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_I82596', if_true: files('lasi_i82596.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')) +system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_emc.c', 'npcm_gmac.c')) +system_ss.add(when: 'CONFIG_NPCM8XX', if_true: files('npcm_pcs.c')) -system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_eth.c')) system_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_fec.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_llan.c')) system_ss.add(when: 'CONFIG_XILINX_ETHLITE', if_true: files('xilinx_ethlite.c')) @@ -50,7 +51,6 @@ specific_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('virtio-net.c')) if have_vhost_net system_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost_net.c'), if_false: files('vhost_net-stub.c')) - system_ss.add(when: 'CONFIG_ALL', if_true: files('vhost_net-stub.c')) else system_ss.add(files('vhost_net-stub.c')) endif @@ -69,7 +69,6 @@ system_ss.add(when: 'CONFIG_ROCKER', if_true: files( 'rocker/rocker_of_dpa.c', 'rocker/rocker_world.c', ), if_false: files('rocker/qmp-norocker.c')) -system_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 2ade72dea08..583aa1c7de6 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); } @@ -265,19 +266,18 @@ static void mipsnet_sysbus_reset(DeviceState *dev) mipsnet_reset(s); } -static Property mipsnet_properties[] = { +static const Property mipsnet_properties[] = { DEFINE_NIC_PROPERTIES(MIPSnetState, conf), - DEFINE_PROP_END_OF_LIST(), }; -static void mipsnet_class_init(ObjectClass *klass, void *data) +static void mipsnet_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 db3a04deb19..59045973ab9 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); } @@ -545,18 +546,17 @@ static void msf2_emac_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } -static Property msf2_emac_properties[] = { +static const Property msf2_emac_properties[] = { DEFINE_PROP_LINK("ahb-bus", MSF2EmacState, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_NIC_PROPERTIES(MSF2EmacState, conf), - DEFINE_PROP_END_OF_LIST(), }; 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), @@ -565,12 +565,12 @@ static const VMStateDescription vmstate_msf2_emac = { } }; -static void msf2_emac_class_init(ObjectClass *klass, void *data) +static void msf2_emac_class_init(ObjectClass *klass, const 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 ef30b0d4a6a..6f08846c81c 100644 --- a/hw/net/mv88w8618_eth.c +++ b/hw/net/mv88w8618_eth.c @@ -12,7 +12,7 @@ #include "hw/irq.h" #include "hw/net/mv88w8618_eth.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "net/net.h" #define MP_ETH_SIZE 0x00001000 @@ -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), @@ -370,14 +371,13 @@ static const VMStateDescription mv88w8618_eth_vmsd = { } }; -static Property mv88w8618_eth_properties[] = { +static const Property mv88w8618_eth_properties[] = { DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf), DEFINE_PROP_LINK("dma-memory", mv88w8618_eth_state, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; -static void mv88w8618_eth_class_init(ObjectClass *klass, void *data) +static void mv88w8618_eth_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index 6ced6775ffd..673c785abc9 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -27,7 +27,7 @@ #include "hw/net/ne2000-isa.h" #include "migration/vmstate.h" #include "ne2000.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "qapi/visitor.h" #include "qemu/module.h" @@ -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,18 +74,18 @@ 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); } -static Property ne2000_isa_properties[] = { +static const Property ne2000_isa_properties[] = { DEFINE_PROP_UINT32("iobase", ISANE2000State, iobase, 0x300), DEFINE_PROP_UINT32("irq", ISANE2000State, isairq, 9), DEFINE_NIC_PROPERTIES(ISANE2000State, ne2000.c), - DEFINE_PROP_END_OF_LIST(), }; -static void isa_ne2000_class_initfn(ObjectClass *klass, void *data) +static void isa_ne2000_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/ne2000-pci.c b/hw/net/ne2000-pci.c index edc6689d33e..ce937e1b618 100644 --- a/hw/net/ne2000-pci.c +++ b/hw/net/ne2000-pci.c @@ -28,7 +28,7 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "ne2000.h" -#include "sysemu/sysemu.h" +#include "system/system.h" typedef struct PCINE2000State { PCIDevice dev; @@ -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); } @@ -95,12 +96,11 @@ static void ne2000_instance_init(Object *obj) &pci_dev->qdev); } -static Property ne2000_properties[] = { +static const Property ne2000_properties[] = { DEFINE_NIC_PROPERTIES(PCINE2000State, ne2000.c), - DEFINE_PROP_END_OF_LIST(), }; -static void ne2000_class_init(ObjectClass *klass, void *data) +static void ne2000_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -122,7 +122,7 @@ static const TypeInfo ne2000_info = { .instance_size = sizeof(PCINE2000State), .class_init = ne2000_class_init, .instance_init = ne2000_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index d79c884d50a..b1923c8c3e1 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "net/eth.h" #include "qemu/module.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/irq.h" #include "migration/vmstate.h" #include "ne2000.h" @@ -606,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 32e5f3f9cf7..f87b6f046cc 100644 --- a/hw/net/net_rx_pkt.c +++ b/hw/net/net_rx_pkt.c @@ -209,12 +209,6 @@ void net_rx_pkt_get_protocols(struct NetRxPkt *pkt, *l4hdr_proto = pkt->l4hdr_info.proto; } -size_t net_rx_pkt_get_l3_hdr_offset(struct NetRxPkt *pkt) -{ - assert(pkt); - return pkt->l3hdr_off; -} - size_t net_rx_pkt_get_l4_hdr_offset(struct NetRxPkt *pkt) { assert(pkt); @@ -375,8 +369,7 @@ net_rx_pkt_calc_rss_hash(struct NetRxPkt *pkt, _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); @@ -427,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) { diff --git a/hw/net/net_rx_pkt.h b/hw/net/net_rx_pkt.h index 55ec67a1a7b..ea077f5fdb1 100644 --- a/hw/net/net_rx_pkt.h +++ b/hw/net/net_rx_pkt.h @@ -77,14 +77,6 @@ void net_rx_pkt_get_protocols(struct NetRxPkt *pkt, bool *hasip4, bool *hasip6, EthL4HdrProto *l4hdr_proto); -/** -* fetches L3 header offset -* -* @pkt: packet -* -*/ -size_t net_rx_pkt_get_l3_hdr_offset(struct NetRxPkt *pkt); - /** * fetches L4 header offset * @@ -267,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 * diff --git a/hw/net/net_tx_pkt.c b/hw/net/net_tx_pkt.c index 2e5f58b3c9c..903238dca24 100644 --- a/hw/net/net_tx_pkt.c +++ b/hw/net/net_tx_pkt.c @@ -578,7 +578,7 @@ static void net_tx_pkt_sendv( { NetClientState *nc = opaque; - if (qemu_get_using_vnet_hdr(nc->peer)) { + 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); @@ -808,7 +808,7 @@ static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt *pkt, bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc) { - bool offload = qemu_get_using_vnet_hdr(nc->peer); + bool offload = qemu_get_vnet_hdr_len(nc->peer); return net_tx_pkt_send_custom(pkt, offload, net_tx_pkt_sendv, nc); } @@ -833,6 +833,7 @@ bool net_tx_pkt_send_custom(struct NetTxPkt *pkt, bool offload, 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); diff --git a/hw/net/npcm7xx_emc.c b/hw/net/npcm7xx_emc.c index 8156f701b07..9ba35e2c819 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" @@ -43,7 +42,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qemu/units.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "trace.h" #define CRC_LENGTH 4 @@ -821,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); } @@ -836,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), @@ -845,12 +845,11 @@ static const VMStateDescription vmstate_npcm7xx_emc = { }, }; -static Property npcm7xx_emc_properties[] = { +static const Property npcm7xx_emc_properties[] = { DEFINE_NIC_PROPERTIES(NPCM7xxEMCState, conf), - DEFINE_PROP_END_OF_LIST(), }; -static void npcm7xx_emc_class_init(ObjectClass *klass, void *data) +static void npcm7xx_emc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -858,7 +857,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 00000000000..a4341125809 --- /dev/null +++ b/hw/net/npcm_gmac.c @@ -0,0 +1,940 @@ +/* + * 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 "system/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 const Property npcm_gmac_properties[] = { + DEFINE_NIC_PROPERTIES(NPCMGMACState, conf), +}; + +static void npcm_gmac_class_init(ObjectClass *klass, const 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/npcm_pcs.c b/hw/net/npcm_pcs.c new file mode 100644 index 00000000000..6aec105271a --- /dev/null +++ b/hw/net/npcm_pcs.c @@ -0,0 +1,410 @@ +/* + * Nuvoton NPCM8xx PCS Module + * + * Copyright 2022 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. + */ + +/* + * Disclaimer: + * Currently we only implemented the default values of the registers and + * the soft reset feature. These are required to boot up the GMAC module + * in Linux kernel for NPCM845 boards. Other functionalities are not modeled. + */ + +#include "qemu/osdep.h" + +#include "exec/hwaddr.h" +#include "hw/registerfields.h" +#include "hw/net/npcm_pcs.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/units.h" +#include "trace.h" + +#define NPCM_PCS_IND_AC_BA 0x1fe +#define NPCM_PCS_IND_SR_CTL 0x1e00 +#define NPCM_PCS_IND_SR_MII 0x1f00 +#define NPCM_PCS_IND_SR_TIM 0x1f07 +#define NPCM_PCS_IND_VR_MII 0x1f80 + +REG16(NPCM_PCS_SR_CTL_ID1, 0x08) +REG16(NPCM_PCS_SR_CTL_ID2, 0x0a) +REG16(NPCM_PCS_SR_CTL_STS, 0x10) + +REG16(NPCM_PCS_SR_MII_CTRL, 0x00) +REG16(NPCM_PCS_SR_MII_STS, 0x02) +REG16(NPCM_PCS_SR_MII_DEV_ID1, 0x04) +REG16(NPCM_PCS_SR_MII_DEV_ID2, 0x06) +REG16(NPCM_PCS_SR_MII_AN_ADV, 0x08) +REG16(NPCM_PCS_SR_MII_LP_BABL, 0x0a) +REG16(NPCM_PCS_SR_MII_AN_EXPN, 0x0c) +REG16(NPCM_PCS_SR_MII_EXT_STS, 0x1e) + +REG16(NPCM_PCS_SR_TIM_SYNC_ABL, 0x10) +REG16(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR, 0x12) +REG16(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_UPR, 0x14) +REG16(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR, 0x16) +REG16(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_UPR, 0x18) +REG16(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR, 0x1a) +REG16(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_UPR, 0x1c) +REG16(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR, 0x1e) +REG16(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_UPR, 0x20) + +REG16(NPCM_PCS_VR_MII_MMD_DIG_CTRL1, 0x000) +REG16(NPCM_PCS_VR_MII_AN_CTRL, 0x002) +REG16(NPCM_PCS_VR_MII_AN_INTR_STS, 0x004) +REG16(NPCM_PCS_VR_MII_TC, 0x006) +REG16(NPCM_PCS_VR_MII_DBG_CTRL, 0x00a) +REG16(NPCM_PCS_VR_MII_EEE_MCTRL0, 0x00c) +REG16(NPCM_PCS_VR_MII_EEE_TXTIMER, 0x010) +REG16(NPCM_PCS_VR_MII_EEE_RXTIMER, 0x012) +REG16(NPCM_PCS_VR_MII_LINK_TIMER_CTRL, 0x014) +REG16(NPCM_PCS_VR_MII_EEE_MCTRL1, 0x016) +REG16(NPCM_PCS_VR_MII_DIG_STS, 0x020) +REG16(NPCM_PCS_VR_MII_ICG_ERRCNT1, 0x022) +REG16(NPCM_PCS_VR_MII_MISC_STS, 0x030) +REG16(NPCM_PCS_VR_MII_RX_LSTS, 0x040) +REG16(NPCM_PCS_VR_MII_MP_TX_BSTCTRL0, 0x070) +REG16(NPCM_PCS_VR_MII_MP_TX_LVLCTRL0, 0x074) +REG16(NPCM_PCS_VR_MII_MP_TX_GENCTRL0, 0x07a) +REG16(NPCM_PCS_VR_MII_MP_TX_GENCTRL1, 0x07c) +REG16(NPCM_PCS_VR_MII_MP_TX_STS, 0x090) +REG16(NPCM_PCS_VR_MII_MP_RX_GENCTRL0, 0x0b0) +REG16(NPCM_PCS_VR_MII_MP_RX_GENCTRL1, 0x0b2) +REG16(NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0, 0x0ba) +REG16(NPCM_PCS_VR_MII_MP_MPLL_CTRL0, 0x0f0) +REG16(NPCM_PCS_VR_MII_MP_MPLL_CTRL1, 0x0f2) +REG16(NPCM_PCS_VR_MII_MP_MPLL_STS, 0x110) +REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL2, 0x126) +REG16(NPCM_PCS_VR_MII_MP_LVL_CTRL, 0x130) +REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL0, 0x132) +REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL1, 0x134) +REG16(NPCM_PCS_VR_MII_DIG_CTRL2, 0x1c2) +REG16(NPCM_PCS_VR_MII_DIG_ERRCNT_SEL, 0x1c4) + +/* Register Fields */ +#define NPCM_PCS_SR_MII_CTRL_RST BIT(15) + +static const uint16_t npcm_pcs_sr_ctl_cold_reset_values[NPCM_PCS_NR_SR_CTLS] = { + [R_NPCM_PCS_SR_CTL_ID1] = 0x699e, + [R_NPCM_PCS_SR_CTL_STS] = 0x8000, +}; + +static const uint16_t npcm_pcs_sr_mii_cold_reset_values[NPCM_PCS_NR_SR_MIIS] = { + [R_NPCM_PCS_SR_MII_CTRL] = 0x1140, + [R_NPCM_PCS_SR_MII_STS] = 0x0109, + [R_NPCM_PCS_SR_MII_DEV_ID1] = 0x699e, + [R_NPCM_PCS_SR_MII_DEV_ID2] = 0xced0, + [R_NPCM_PCS_SR_MII_AN_ADV] = 0x0020, + [R_NPCM_PCS_SR_MII_EXT_STS] = 0xc000, +}; + +static const uint16_t npcm_pcs_sr_tim_cold_reset_values[NPCM_PCS_NR_SR_TIMS] = { + [R_NPCM_PCS_SR_TIM_SYNC_ABL] = 0x0003, + [R_NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR] = 0x0038, + [R_NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR] = 0x0038, + [R_NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR] = 0x0058, + [R_NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR] = 0x0048, +}; + +static const uint16_t npcm_pcs_vr_mii_cold_reset_values[NPCM_PCS_NR_VR_MIIS] = { + [R_NPCM_PCS_VR_MII_MMD_DIG_CTRL1] = 0x2400, + [R_NPCM_PCS_VR_MII_AN_INTR_STS] = 0x000a, + [R_NPCM_PCS_VR_MII_EEE_MCTRL0] = 0x899c, + [R_NPCM_PCS_VR_MII_DIG_STS] = 0x0010, + [R_NPCM_PCS_VR_MII_MP_TX_BSTCTRL0] = 0x000a, + [R_NPCM_PCS_VR_MII_MP_TX_LVLCTRL0] = 0x007f, + [R_NPCM_PCS_VR_MII_MP_TX_GENCTRL0] = 0x0001, + [R_NPCM_PCS_VR_MII_MP_RX_GENCTRL0] = 0x0100, + [R_NPCM_PCS_VR_MII_MP_RX_GENCTRL1] = 0x1100, + [R_NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0] = 0x000e, + [R_NPCM_PCS_VR_MII_MP_MPLL_CTRL0] = 0x0100, + [R_NPCM_PCS_VR_MII_MP_MPLL_CTRL1] = 0x0032, + [R_NPCM_PCS_VR_MII_MP_MPLL_STS] = 0x0001, + [R_NPCM_PCS_VR_MII_MP_LVL_CTRL] = 0x0019, +}; + +static void npcm_pcs_soft_reset(NPCMPCSState *s) +{ + memcpy(s->sr_ctl, npcm_pcs_sr_ctl_cold_reset_values, + NPCM_PCS_NR_SR_CTLS * sizeof(uint16_t)); + memcpy(s->sr_mii, npcm_pcs_sr_mii_cold_reset_values, + NPCM_PCS_NR_SR_MIIS * sizeof(uint16_t)); + memcpy(s->sr_tim, npcm_pcs_sr_tim_cold_reset_values, + NPCM_PCS_NR_SR_TIMS * sizeof(uint16_t)); + memcpy(s->vr_mii, npcm_pcs_vr_mii_cold_reset_values, + NPCM_PCS_NR_VR_MIIS * sizeof(uint16_t)); +} + +static uint16_t npcm_pcs_read_sr_ctl(NPCMPCSState *s, hwaddr offset) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_SR_CTLS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SR_CTL read offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return 0; + } + + return s->sr_ctl[regno]; +} + +static uint16_t npcm_pcs_read_sr_mii(NPCMPCSState *s, hwaddr offset) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_SR_MIIS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SR_MII read offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return 0; + } + + return s->sr_mii[regno]; +} + +static uint16_t npcm_pcs_read_sr_tim(NPCMPCSState *s, hwaddr offset) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_SR_TIMS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SR_TIM read offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return 0; + } + + return s->sr_tim[regno]; +} + +static uint16_t npcm_pcs_read_vr_mii(NPCMPCSState *s, hwaddr offset) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_VR_MIIS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: VR_MII read offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return 0; + } + + return s->vr_mii[regno]; +} + +static void npcm_pcs_write_sr_ctl(NPCMPCSState *s, hwaddr offset, uint16_t v) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_SR_CTLS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SR_CTL write offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return; + } + + s->sr_ctl[regno] = v; +} + +static void npcm_pcs_write_sr_mii(NPCMPCSState *s, hwaddr offset, uint16_t v) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_SR_MIIS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SR_MII write offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return; + } + + s->sr_mii[regno] = v; + + if ((offset == A_NPCM_PCS_SR_MII_CTRL) && (v & NPCM_PCS_SR_MII_CTRL_RST)) { + /* Trigger a soft reset */ + npcm_pcs_soft_reset(s); + } +} + +static void npcm_pcs_write_sr_tim(NPCMPCSState *s, hwaddr offset, uint16_t v) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_SR_TIMS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SR_TIM write offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return; + } + + s->sr_tim[regno] = v; +} + +static void npcm_pcs_write_vr_mii(NPCMPCSState *s, hwaddr offset, uint16_t v) +{ + hwaddr regno = offset / sizeof(uint16_t); + + if (regno >= NPCM_PCS_NR_VR_MIIS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: VR_MII write offset 0x%04" HWADDR_PRIx + " is out of range.\n", + DEVICE(s)->canonical_path, offset); + return; + } + + s->vr_mii[regno] = v; +} + +static uint64_t npcm_pcs_read(void *opaque, hwaddr offset, unsigned size) +{ + NPCMPCSState *s = opaque; + uint16_t v = 0; + + if (offset == NPCM_PCS_IND_AC_BA) { + v = s->indirect_access_base; + } else { + switch (s->indirect_access_base) { + case NPCM_PCS_IND_SR_CTL: + v = npcm_pcs_read_sr_ctl(s, offset); + break; + + case NPCM_PCS_IND_SR_MII: + v = npcm_pcs_read_sr_mii(s, offset); + break; + + case NPCM_PCS_IND_SR_TIM: + v = npcm_pcs_read_sr_tim(s, offset); + break; + + case NPCM_PCS_IND_VR_MII: + v = npcm_pcs_read_vr_mii(s, offset); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Read with invalid indirect address base: 0x%" + PRIx16 "\n", DEVICE(s)->canonical_path, + s->indirect_access_base); + } + } + + trace_npcm_pcs_reg_read(DEVICE(s)->canonical_path, s->indirect_access_base, + offset, v); + return v; +} + +static void npcm_pcs_write(void *opaque, hwaddr offset, + uint64_t v, unsigned size) +{ + NPCMPCSState *s = opaque; + + trace_npcm_pcs_reg_write(DEVICE(s)->canonical_path, s->indirect_access_base, + offset, v); + if (offset == NPCM_PCS_IND_AC_BA) { + s->indirect_access_base = v; + } else { + switch (s->indirect_access_base) { + case NPCM_PCS_IND_SR_CTL: + npcm_pcs_write_sr_ctl(s, offset, v); + break; + + case NPCM_PCS_IND_SR_MII: + npcm_pcs_write_sr_mii(s, offset, v); + break; + + case NPCM_PCS_IND_SR_TIM: + npcm_pcs_write_sr_tim(s, offset, v); + break; + + case NPCM_PCS_IND_VR_MII: + npcm_pcs_write_vr_mii(s, offset, v); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Write with invalid indirect address base: 0x%02" + PRIx16 "\n", DEVICE(s)->canonical_path, + s->indirect_access_base); + } + } +} + +static void npcm_pcs_enter_reset(Object *obj, ResetType type) +{ + NPCMPCSState *s = NPCM_PCS(obj); + + npcm_pcs_soft_reset(s); +} + +static const struct MemoryRegionOps npcm_pcs_ops = { + .read = npcm_pcs_read, + .write = npcm_pcs_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 2, + .max_access_size = 2, + .unaligned = false, + }, +}; + +static void npcm_pcs_realize(DeviceState *dev, Error **errp) +{ + NPCMPCSState *pcs = NPCM_PCS(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&pcs->iomem, OBJECT(pcs), &npcm_pcs_ops, pcs, + TYPE_NPCM_PCS, 8 * KiB); + sysbus_init_mmio(sbd, &pcs->iomem); +} + +static const VMStateDescription vmstate_npcm_pcs = { + .name = TYPE_NPCM_PCS, + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT16(indirect_access_base, NPCMPCSState), + VMSTATE_UINT16_ARRAY(sr_ctl, NPCMPCSState, NPCM_PCS_NR_SR_CTLS), + VMSTATE_UINT16_ARRAY(sr_mii, NPCMPCSState, NPCM_PCS_NR_SR_MIIS), + VMSTATE_UINT16_ARRAY(sr_tim, NPCMPCSState, NPCM_PCS_NR_SR_TIMS), + VMSTATE_UINT16_ARRAY(vr_mii, NPCMPCSState, NPCM_PCS_NR_VR_MIIS), + VMSTATE_END_OF_LIST(), + }, +}; + +static void npcm_pcs_class_init(ObjectClass *klass, const void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->desc = "NPCM PCS Controller"; + dc->realize = npcm_pcs_realize; + dc->vmsd = &vmstate_npcm_pcs; + rc->phases.enter = npcm_pcs_enter_reset; +} + +static const TypeInfo npcm_pcs_types[] = { + { + .name = TYPE_NPCM_PCS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NPCMPCSState), + .class_init = npcm_pcs_class_init, + }, +}; +DEFINE_TYPES(npcm_pcs_types) diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c index 0b3dc3146e6..7e955c01322 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) @@ -742,19 +743,18 @@ static void qdev_open_eth_reset(DeviceState *dev) open_eth_reset(d); } -static Property open_eth_properties[] = { +static const Property open_eth_properties[] = { DEFINE_NIC_PROPERTIES(OpenEthState, conf), - DEFINE_PROP_END_OF_LIST(), }; -static void open_eth_class_init(ObjectClass *klass, void *data) +static void open_eth_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 96a302c141a..0ca5bc21938 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -35,8 +35,8 @@ #include "net/net.h" #include "qemu/module.h" #include "qemu/timer.h" -#include "sysemu/dma.h" -#include "sysemu/sysemu.h" +#include "system/dma.h" +#include "system/system.h" #include "trace.h" #include "pcnet.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() @@ -252,12 +252,11 @@ static void pcnet_instance_init(Object *obj) DEVICE(obj)); } -static Property pcnet_properties[] = { +static const Property pcnet_properties[] = { DEFINE_NIC_PROPERTIES(PCIPCNetState, state.conf), - DEFINE_PROP_END_OF_LIST(), }; -static void pcnet_class_init(ObjectClass *klass, void *data) +static void pcnet_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -269,7 +268,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); @@ -281,7 +280,7 @@ static const TypeInfo pcnet_info = { .instance_size = sizeof(PCIPCNetState), .class_init = pcnet_class_init, .instance_init = pcnet_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c index 02828ae7165..ad675ab29d4 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, @@ -1682,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), @@ -1709,7 +1709,8 @@ void pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info) s->poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pcnet_poll_timer, s); qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s); + s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), + dev->id, &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); /* Initialize the PROM */ diff --git a/hw/net/pcnet.h b/hw/net/pcnet.h index eb7f46aab35..a94356ec30b 100644 --- a/hw/net/pcnet.h +++ b/hw/net/pcnet.h @@ -7,7 +7,7 @@ #define PCNET_LOOPTEST_CRC 1 #define PCNET_LOOPTEST_NOCRC 2 -#include "exec/memory.h" +#include "system/memory.h" #include "hw/irq.h" /* BUS CONFIGURATION REGISTERS */ diff --git a/hw/net/rocker/rocker-hmp-cmds.c b/hw/net/rocker/rocker-hmp-cmds.c index 197c6e28dc2..df40991f6d2 100644 --- a/hw/net/rocker/rocker-hmp-cmds.c +++ b/hw/net/rocker/rocker-hmp-cmds.c @@ -18,7 +18,7 @@ #include "monitor/monitor.h" #include "net/eth.h" #include "qapi/qapi-commands-rocker.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" void hmp_rocker(Monitor *mon, const QDict *qdict) { diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index 7ea8eb6ba55..cc49701dd3c 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -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) { @@ -1464,7 +1459,7 @@ static void rocker_reset(DeviceState *dev) DPRINTF("Reset done\n"); } -static Property rocker_properties[] = { +static const Property rocker_properties[] = { DEFINE_PROP_STRING("name", Rocker, name), DEFINE_PROP_STRING("world", Rocker, world_name), DEFINE_PROP_MACADDR("fp_start_macaddr", Rocker, @@ -1473,7 +1468,6 @@ static Property rocker_properties[] = { switch_id, 0), DEFINE_PROP_ARRAY("ports", Rocker, fp_ports, fp_ports_peers, qdev_prop_netdev, NICPeers), - DEFINE_PROP_END_OF_LIST(), }; static const VMStateDescription rocker_vmsd = { @@ -1481,7 +1475,7 @@ static const VMStateDescription rocker_vmsd = { .unmigratable = 1, }; -static void rocker_class_init(ObjectClass *klass, void *data) +static void rocker_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1494,7 +1488,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; } @@ -1504,7 +1498,7 @@ static const TypeInfo rocker_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(Rocker), .class_init = rocker_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/rocker/rocker.h b/hw/net/rocker/rocker.h index f85354d9d13..ae06c1c72af 100644 --- a/hw/net/rocker/rocker.h +++ b/hw/net/rocker/rocker.h @@ -36,15 +36,7 @@ static inline G_GNUC_PRINTF(1, 2) int DPRINTF(const char *fmt, ...) } #endif -#define __le16 uint16_t -#define __le32 uint32_t -#define __le64 uint64_t - -#define __be16 uint16_t -#define __be32 uint32_t -#define __be64 uint64_t - -static inline bool ipv4_addr_is_multicast(__be32 addr) +static inline bool ipv4_addr_is_multicast(uint32_t addr) { return (addr & htonl(0xf0000000)) == htonl(0xe0000000); } @@ -52,8 +44,8 @@ static inline bool ipv4_addr_is_multicast(__be32 addr) typedef struct ipv6_addr { union { uint8_t addr8[16]; - __be16 addr16[8]; - __be32 addr32[4]; + uint16_t addr16[8]; + uint32_t addr32[4]; }; } Ipv6Addr; @@ -72,7 +64,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_fp.c b/hw/net/rocker/rocker_fp.c index cbeed65bd5e..cb87ff752ad 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_hw.h b/hw/net/rocker/rocker_hw.h index 1786323fa4a..7ec6bfbcb92 100644 --- a/hw/net/rocker/rocker_hw.h +++ b/hw/net/rocker/rocker_hw.h @@ -9,10 +9,6 @@ #ifndef ROCKER_HW_H #define ROCKER_HW_H -#define __le16 uint16_t -#define __le32 uint32_t -#define __le64 uint64_t - /* * Return codes */ @@ -124,12 +120,12 @@ enum { */ typedef struct rocker_desc { - __le64 buf_addr; + uint64_t buf_addr; uint64_t cookie; - __le16 buf_size; - __le16 tlv_size; - __le16 rsvd[5]; /* pad to 32 bytes */ - __le16 comp_err; + uint16_t buf_size; + uint16_t tlv_size; + uint16_t rsvd[5]; /* pad to 32 bytes */ + uint16_t comp_err; } __attribute__((packed, aligned(8))) RockerDesc; /* @@ -137,9 +133,9 @@ typedef struct rocker_desc { */ typedef struct rocker_tlv { - __le32 type; - __le16 len; - __le16 rsvd; + uint32_t type; + uint16_t len; + uint16_t rsvd; } __attribute__((packed, aligned(8))) RockerTlv; /* cmd msg */ diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c index dfe47544694..4aed1787566 100644 --- a/hw/net/rocker/rocker_of_dpa.c +++ b/hw/net/rocker/rocker_of_dpa.c @@ -52,10 +52,10 @@ typedef struct of_dpa_flow_key { uint32_t tunnel_id; /* overlay tunnel id */ uint32_t tbl_id; /* table id */ struct { - __be16 vlan_id; /* 0 if no VLAN */ + uint16_t vlan_id; /* 0 if no VLAN */ MACAddr src; /* ethernet source address */ MACAddr dst; /* ethernet destination address */ - __be16 type; /* ethernet frame type */ + uint16_t type; /* ethernet frame type */ } eth; struct { uint8_t proto; /* IP protocol or ARP opcode */ @@ -66,14 +66,14 @@ typedef struct of_dpa_flow_key { union { struct { struct { - __be32 src; /* IP source address */ - __be32 dst; /* IP destination address */ + uint32_t src; /* IP source address */ + uint32_t dst; /* IP destination address */ } addr; union { struct { - __be16 src; /* TCP/UDP/SCTP source port */ - __be16 dst; /* TCP/UDP/SCTP destination port */ - __be16 flags; /* TCP flags */ + uint16_t src; /* TCP/UDP/SCTP source port */ + uint16_t dst; /* TCP/UDP/SCTP destination port */ + uint16_t flags; /* TCP flags */ } tp; struct { MACAddr sha; /* ARP source hardware address */ @@ -86,11 +86,11 @@ typedef struct of_dpa_flow_key { Ipv6Addr src; /* IPv6 source address */ Ipv6Addr dst; /* IPv6 destination address */ } addr; - __be32 label; /* IPv6 flow label */ + uint32_t label; /* IPv6 flow label */ struct { - __be16 src; /* TCP/UDP/SCTP source port */ - __be16 dst; /* TCP/UDP/SCTP destination port */ - __be16 flags; /* TCP flags */ + uint16_t src; /* TCP/UDP/SCTP source port */ + uint16_t dst; /* TCP/UDP/SCTP destination port */ + uint16_t flags; /* TCP flags */ } tp; struct { Ipv6Addr target; /* ND target address */ @@ -112,13 +112,13 @@ typedef struct of_dpa_flow_action { struct { uint32_t group_id; uint32_t tun_log_lport; - __be16 vlan_id; + uint16_t vlan_id; } write; struct { - __be16 new_vlan_id; + uint16_t new_vlan_id; uint32_t out_pport; uint8_t copy_to_cpu; - __be16 vlan_id; + uint16_t vlan_id; } apply; } OfDpaFlowAction; @@ -143,7 +143,7 @@ typedef struct of_dpa_flow { typedef struct of_dpa_flow_pkt_fields { uint32_t tunnel_id; struct eth_header *ethhdr; - __be16 *h_proto; + uint16_t *h_proto; struct vlan_header *vlanhdr; struct ip_header *ipv4hdr; struct ip6_header *ipv6hdr; @@ -180,7 +180,7 @@ typedef struct of_dpa_group { uint32_t group_id; MACAddr src_mac; MACAddr dst_mac; - __be16 vlan_id; + uint16_t vlan_id; } l2_rewrite; struct { uint16_t group_count; @@ -190,13 +190,13 @@ typedef struct of_dpa_group { uint32_t group_id; MACAddr src_mac; MACAddr dst_mac; - __be16 vlan_id; + uint16_t vlan_id; uint8_t ttl_check; } l3_unicast; }; } OfDpaGroup; -static int of_dpa_mask2prefix(__be32 mask) +static int of_dpa_mask2prefix(uint32_t mask) { int i; int count = 32; @@ -451,7 +451,7 @@ static void of_dpa_flow_pkt_parse(OfDpaFlowContext *fc, fc->iovcnt = iovcnt + 2; } -static void of_dpa_flow_pkt_insert_vlan(OfDpaFlowContext *fc, __be16 vlan_id) +static void of_dpa_flow_pkt_insert_vlan(OfDpaFlowContext *fc, uint16_t vlan_id) { OfDpaFlowPktFields *fields = &fc->fields; uint16_t h_proto = fields->ethhdr->h_proto; @@ -486,7 +486,7 @@ static void of_dpa_flow_pkt_strip_vlan(OfDpaFlowContext *fc) static void of_dpa_flow_pkt_hdr_rewrite(OfDpaFlowContext *fc, uint8_t *src_mac, uint8_t *dst_mac, - __be16 vlan_id) + uint16_t vlan_id) { OfDpaFlowPktFields *fields = &fc->fields; @@ -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]); diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index b4df75b2c92..654a087d80a 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -48,20 +48,18 @@ * 2011-Mar-22 Benjamin Poirier: Implemented VLAN offloading */ -/* For crc32 */ - #include "qemu/osdep.h" -#include +#include /* for crc32 */ #include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/module.h" #include "qemu/timer.h" #include "net/net.h" #include "net/eth.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qom/object.h" /* debug RTL8139 card */ @@ -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, @@ -1818,7 +1816,7 @@ static int rtl8139_transmit_one(RTL8139State *s, int descriptor) PCIDevice *d = PCI_DEVICE(s); int txsize = s->TxStatus[descriptor] & 0x1fff; - uint8_t txbuffer[0x2000]; + QEMU_UNINITIALIZED uint8_t txbuffer[0x2000]; DPRINTF("+++ transmit reading %d bytes from host memory at 0x%08x\n", txsize, s->TxAddr[descriptor]); @@ -2738,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); @@ -3150,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() } }; @@ -3173,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), @@ -3257,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 } @@ -3388,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; @@ -3407,12 +3410,11 @@ static void rtl8139_instance_init(Object *obj) DEVICE(obj)); } -static Property rtl8139_properties[] = { +static const Property rtl8139_properties[] = { DEFINE_NIC_PROPERTIES(RTL8139State, conf), - DEFINE_PROP_END_OF_LIST(), }; -static void rtl8139_class_init(ObjectClass *klass, void *data) +static void rtl8139_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -3424,7 +3426,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); @@ -3436,7 +3438,7 @@ static const TypeInfo rtl8139_info = { .instance_size = sizeof(RTL8139State), .class_init = rtl8139_class_init, .instance_init = rtl8139_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index ad778cd8fc7..5cd78e334b6 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -13,16 +13,23 @@ #include "net/net.h" #include "hw/irq.h" #include "hw/net/smc91c111.h" +#include "hw/registerfields.h" #include "hw/qdev-properties.h" #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. */ #define NUM_PACKETS 4 +/* + * Maximum size of a data frame, including the leading status word + * and byte count fields and the trailing CRC, last data byte + * and control byte (per figure 8-1 in the Microchip Technology + * LAN91C111 datasheet). + */ +#define MAX_PACKET_SIZE 2048 #define TYPE_SMC91C111 "smc91c111" OBJECT_DECLARE_SIMPLE_TYPE(smc91c111_state, SMC91C111) @@ -52,7 +59,7 @@ struct smc91c111_state { int tx_fifo_done_len; int tx_fifo_done[NUM_PACKETS]; /* Packet buffer memory. */ - uint8_t data[NUM_PACKETS][2048]; + uint8_t data[NUM_PACKETS][MAX_PACKET_SIZE]; uint8_t int_level; uint8_t int_mask; MemoryRegion mmio; @@ -62,7 +69,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), @@ -80,7 +87,8 @@ static const VMStateDescription vmstate_smc91c111 = { VMSTATE_INT32_ARRAY(rx_fifo, smc91c111_state, NUM_PACKETS), VMSTATE_INT32(tx_fifo_done_len, smc91c111_state), VMSTATE_INT32_ARRAY(tx_fifo_done, smc91c111_state, NUM_PACKETS), - VMSTATE_BUFFER_UNSAFE(data, smc91c111_state, 0, NUM_PACKETS * 2048), + VMSTATE_BUFFER_UNSAFE(data, smc91c111_state, 0, + NUM_PACKETS * MAX_PACKET_SIZE), VMSTATE_UINT8(int_level, smc91c111_state), VMSTATE_UINT8(int_mask, smc91c111_state), VMSTATE_END_OF_LIST() @@ -119,6 +127,18 @@ static const VMStateDescription vmstate_smc91c111 = { #define RS_TOOSHORT 0x0400 #define RS_MULTICAST 0x0001 +FIELD(PTR, PTR, 0, 11) +FIELD(PTR, NOT_EMPTY, 11, 1) +FIELD(PTR, RESERVED, 12, 1) +FIELD(PTR, READ, 13, 1) +FIELD(PTR, AUTOINCR, 14, 1) +FIELD(PTR, RCV, 15, 1) + +static inline bool packetnum_valid(int packet_num) +{ + return packet_num >= 0 && packet_num < NUM_PACKETS; +} + /* Update interrupt status. */ static void smc91c111_update(smc91c111_state *s) { @@ -183,6 +203,15 @@ static void smc91c111_pop_rx_fifo(smc91c111_state *s) { int i; + if (s->rx_fifo_len == 0) { + /* + * The datasheet doesn't document what the behaviour is if the + * guest tries to pop an empty RX FIFO, and there's no obvious + * error status register to report it. Just ignore the attempt. + */ + return; + } + s->rx_fifo_len--; if (s->rx_fifo_len) { for (i = 0; i < s->rx_fifo_len; i++) @@ -210,12 +239,33 @@ static void smc91c111_pop_tx_fifo_done(smc91c111_state *s) /* Release the memory allocated to a packet. */ static void smc91c111_release_packet(smc91c111_state *s, int packet) { + if (!packetnum_valid(packet)) { + /* + * Data sheet doesn't document behaviour in this guest error + * case, and there is no error status register to report it. + * Log and ignore the attempt. + */ + qemu_log_mask(LOG_GUEST_ERROR, + "smc91c111: attempt to release invalid packet %d\n", + packet); + return; + } s->allocated &= ~(1 << packet); if (s->tx_alloc == 0x80) smc91c111_tx_alloc(s); smc91c111_flush_queued_packets(s); } +static void smc91c111_complete_tx_packet(smc91c111_state *s, int packetnum) +{ + if (s->ctr & CTR_AUTO_RELEASE) { + /* Race? */ + smc91c111_release_packet(s, packetnum); + } else if (s->tx_fifo_done_len < NUM_PACKETS) { + s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum; + } +} + /* Flush the TX FIFO. */ static void smc91c111_do_tx(smc91c111_state *s) { @@ -231,12 +281,25 @@ static void smc91c111_do_tx(smc91c111_state *s) return; for (i = 0; i < s->tx_fifo_len; i++) { packetnum = s->tx_fifo[i]; + /* queue_tx checked the packet number was valid */ + assert(packetnum_valid(packetnum)); p = &s->data[packetnum][0]; /* Set status word. */ *(p++) = 0x01; *(p++) = 0x40; len = *(p++); len |= ((int)*(p++)) << 8; + if (len > MAX_PACKET_SIZE) { + /* + * Datasheet doesn't say what to do here, and there is no + * relevant tx error condition listed. Log, and drop the packet. + */ + qemu_log_mask(LOG_GUEST_ERROR, + "smc91c111: tx packet with bad length %d, dropping\n", + len); + smc91c111_complete_tx_packet(s, packetnum); + continue; + } len -= 6; control = p[len + 1]; if (control & 0x20) @@ -265,11 +328,7 @@ static void smc91c111_do_tx(smc91c111_state *s) } } #endif - if (s->ctr & CTR_AUTO_RELEASE) - /* Race? */ - smc91c111_release_packet(s, packetnum); - else if (s->tx_fifo_done_len < NUM_PACKETS) - s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum; + smc91c111_complete_tx_packet(s, packetnum); qemu_send_packet(qemu_get_queue(s->nic), p, len); } s->tx_fifo_len = 0; @@ -279,6 +338,17 @@ static void smc91c111_do_tx(smc91c111_state *s) /* Add a packet to the TX FIFO. */ static void smc91c111_queue_tx(smc91c111_state *s, int packet) { + if (!packetnum_valid(packet)) { + /* + * Datasheet doesn't document behaviour in this error case, and + * there's no error status register we could report it in. + * Log and ignore. + */ + qemu_log_mask(LOG_GUEST_ERROR, + "smc91c111: attempt to queue invalid packet %d\n", + packet); + return; + } if (s->tx_fifo_len == NUM_PACKETS) return; s->tx_fifo[s->tx_fifo_len++] = packet; @@ -310,6 +380,49 @@ static void smc91c111_reset(DeviceState *dev) #define SET_LOW(name, val) s->name = (s->name & 0xff00) | val #define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8) +/* + * The pointer register's pointer is an 11 bit value (so it exactly + * indexes a 2048-byte data frame). Add the specified offset to it, + * wrapping around at the 2048 byte mark, and return the resulting + * wrapped value. There are flag bits in the top part of the register, + * but we can ignore them here as the mask will mask them out. + */ +static int ptr_reg_add(smc91c111_state *s, int offset) +{ + return (s->ptr + offset) & R_PTR_PTR_MASK; +} + +/* + * For an access to the Data Register at @offset, return the + * required offset into the packet's data frame. This will + * perform the pointer register autoincrement if required, and + * guarantees to return an in-bounds offset. + */ +static int data_reg_ptr(smc91c111_state *s, int offset) +{ + int p; + + if (s->ptr & R_PTR_AUTOINCR_MASK) { + /* + * Autoincrement: use the current pointer value, and + * increment the pointer register's pointer field. + */ + p = FIELD_EX32(s->ptr, PTR, PTR); + s->ptr = FIELD_DP32(s->ptr, PTR, PTR, ptr_reg_add(s, 1)); + } else { + /* + * No autoincrement: register offset determines which + * byte we're addressing. Setting the pointer to the top + * of the data buffer and then using the pointer wrapping + * to read the bottom byte of the buffer is not something + * sensible guest software will do, but the datasheet + * doesn't say what the behaviour is, so we don't forbid it. + */ + p = ptr_reg_add(s, offset & 3); + } + return p; +} + static void smc91c111_writeb(void *opaque, hwaddr offset, uint32_t value) { @@ -361,7 +474,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: @@ -449,12 +562,14 @@ static void smc91c111_writeb(void *opaque, hwaddr offset, n = s->rx_fifo[0]; else n = s->packet_num; - p = s->ptr & 0x07ff; - if (s->ptr & 0x4000) { - s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff); - } else { - p += (offset & 3); + if (!packetnum_valid(n)) { + /* Datasheet doesn't document what to do here */ + qemu_log_mask(LOG_GUEST_ERROR, + "smc91c111: attempt to write data to invalid packet %d\n", + n); + return; } + p = data_reg_ptr(s, offset); s->data[n][p] = value; } return; @@ -597,12 +712,14 @@ static uint32_t smc91c111_readb(void *opaque, hwaddr offset) n = s->rx_fifo[0]; else n = s->packet_num; - p = s->ptr & 0x07ff; - if (s->ptr & 0x4000) { - s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff); - } else { - p += (offset & 3); + if (!packetnum_valid(n)) { + /* Datasheet doesn't document what to do here */ + qemu_log_mask(LOG_GUEST_ERROR, + "smc91c111: attempt to read data from invalid packet %d\n", + n); + return 0; } + p = data_reg_ptr(s, offset); return s->data[n][p]; } case 12: /* Interrupt status. */ @@ -698,13 +815,16 @@ static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t if (crc) packetsize += 4; /* TODO: Flag overrun and receive errors. */ - if (packetsize > 2048) + if (packetsize > MAX_PACKET_SIZE) { return -1; + } packetnum = smc91c111_allocate_packet(s); if (packetnum == 0x80) return -1; s->rx_fifo[s->rx_fifo_len++] = packetnum; + /* allocate_packet() will not hand us back an invalid packet number */ + assert(packetnum_valid(packetnum)); p = &s->data[packetnum][0]; /* ??? Multicast packets? */ status = 0; @@ -783,22 +903,22 @@ 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. */ } -static Property smc91c111_properties[] = { +static const Property smc91c111_properties[] = { DEFINE_NIC_PROPERTIES(smc91c111_state, conf), - DEFINE_PROP_END_OF_LIST(), }; -static void smc91c111_class_init(ObjectClass *klass, void *data) +static void smc91c111_class_init(ObjectClass *klass, const 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 +937,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 a6876a936db..f6f217d6320 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -33,7 +33,7 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" #include "hw/qdev-properties.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "trace.h" #include @@ -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; @@ -779,12 +786,11 @@ static target_ulong h_change_logical_lan_mac(PowerPCCPU *cpu, return H_SUCCESS; } -static Property spapr_vlan_properties[] = { +static const Property spapr_vlan_properties[] = { DEFINE_SPAPR_PROPERTIES(SpaprVioVlan, sdev), DEFINE_NIC_PROPERTIES(SpaprVioVlan, nicconf), DEFINE_PROP_BIT("use-rx-buffer-pools", SpaprVioVlan, compat_flags, SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT, true), - DEFINE_PROP_END_OF_LIST(), }; static bool spapr_vlan_rx_buffer_pools_needed(void *opaque) @@ -799,7 +805,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 +818,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 +830,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,13 +842,13 @@ static const VMStateDescription vmstate_spapr_llan = { VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_rx_pools, NULL } }; -static void spapr_vlan_class_init(ObjectClass *klass, void *data) +static void spapr_vlan_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index 8dd60783d81..2fc51e1e16d 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,21 +492,21 @@ 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); } -static Property stellaris_enet_properties[] = { +static const Property stellaris_enet_properties[] = { DEFINE_NIC_PROPERTIES(stellaris_enet_state, conf), - DEFINE_PROP_END_OF_LIST(), }; -static void stellaris_enet_class_init(ObjectClass *klass, void *data) +static void stellaris_enet_class_init(ObjectClass *klass, const 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 510b370e5ff..b405eb89fa5 100644 --- a/hw/net/sungem.c +++ b/hw/net/sungem.c @@ -17,7 +17,7 @@ #include "net/eth.h" #include "net/checksum.h" #include "hw/net/mii.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "trace.h" #include "qom/object.h" @@ -1228,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; @@ -1399,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); } @@ -1420,21 +1420,20 @@ static void sungem_instance_init(Object *obj) DEVICE(obj)); } -static Property sungem_properties[] = { +static const Property sungem_properties[] = { DEFINE_NIC_PROPERTIES(SunGEMState, conf), /* Phy address should be 0 for most Apple machines except * for K2 in which case it's 1. Will be set by a machine * override. */ DEFINE_PROP_UINT32("phy_addr", SunGEMState, phy_addr, 0), - DEFINE_PROP_END_OF_LIST(), }; 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), @@ -1455,7 +1454,7 @@ static const VMStateDescription vmstate_sungem = { } }; -static void sungem_class_init(ObjectClass *klass, void *data) +static void sungem_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1467,7 +1466,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); } @@ -1478,7 +1477,7 @@ static const TypeInfo sungem_info = { .instance_size = sizeof(SunGEMState), .class_init = sungem_class_init, .instance_init = sungem_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } } diff --git a/hw/net/sunhme.c b/hw/net/sunhme.c index 391d26fb82c..c2f7a8483da 100644 --- a/hw/net/sunhme.c +++ b/hw/net/sunhme.c @@ -31,7 +31,7 @@ #include "qemu/module.h" #include "net/checksum.h" #include "net/eth.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "trace.h" #include "qom/object.h" @@ -177,9 +177,8 @@ struct SunHMEState { uint16_t miiregs[HME_MII_REGS_SIZE]; }; -static Property sunhme_properties[] = { +static const Property sunhme_properties[] = { DEFINE_NIC_PROPERTIES(SunHMEState, conf), - DEFINE_PROP_END_OF_LIST(), }; static void sunhme_reset_tx(SunHMEState *s) @@ -881,7 +880,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); } @@ -901,7 +901,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; @@ -924,7 +924,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)), @@ -937,7 +937,7 @@ static const VMStateDescription vmstate_hme = { } }; -static void sunhme_class_init(ObjectClass *klass, void *data) +static void sunhme_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -947,7 +947,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); } @@ -958,7 +958,7 @@ static const TypeInfo sunhme_info = { .class_init = sunhme_class_init, .instance_size = sizeof(SunHMEState), .instance_init = sunhme_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } } diff --git a/hw/net/trace-events b/hw/net/trace-events index 6b5ba669a25..72b69c4a8bb 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -10,10 +10,11 @@ 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" +# lan9118_phy.c +lan9118_phy_read(uint16_t val, int reg) "[0x%02x] -> 0x%04" PRIx16 +lan9118_phy_write(uint16_t val, int reg) "[0x%02x] <- 0x%04" PRIx16 +lan9118_phy_update_link(const char *s) "%s" +lan9118_phy_reset(void) "" # lance.c lance_mem_readw(uint64_t addr, uint32_t ret) "addr=0x%"PRIx64"val=0x%04x" @@ -274,13 +275,14 @@ 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(uint64_t addr, uint16_t offset, const void* source, uint32_t len) "addr: 0x%"PRIx64", offset: %u, from: %p, length: %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) "RSS data: 0x%X" +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" @@ -295,6 +297,8 @@ 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 @@ -396,9 +400,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 @@ -428,12 +434,8 @@ i82596_set_multicast(uint16_t count) "Added %d multicast entries" i82596_channel_attention(void *s) "%p: Received CHANNEL ATTENTION" # imx_fec.c -imx_phy_read(uint32_t val, int phy, int reg) "0x%04"PRIx32" <= phy[%d].reg[%d]" imx_phy_read_num(int phy, int configured) "read request from unconfigured phy %d (configured %d)" -imx_phy_write(uint32_t val, int phy, int reg) "0x%04"PRIx32" => phy[%d].reg[%d]" imx_phy_write_num(int phy, int configured) "write request to unconfigured phy %d (configured %d)" -imx_phy_update_link(const char *s) "%s" -imx_phy_reset(void) "" imx_fec_read_bd(uint64_t addr, int flags, int len, int data) "tx_bd 0x%"PRIx64" flags 0x%04x len %d data 0x%08x" imx_enet_read_bd(uint64_t addr, int flags, int len, int data, int options, int status) "tx_bd 0x%"PRIx64" flags 0x%04x len %d data 0x%08x option 0x%04x status 0x%04x" imx_eth_tx_bd_busy(void) "tx_bd ran out of descriptors to transmit" @@ -464,6 +466,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_base, 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_base, 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" @@ -480,3 +502,18 @@ 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" + +# xilinx_ethlite.c +ethlite_pkt_lost(uint32_t rx_ctrl) "rx_ctrl:0x%" PRIx32 +ethlite_pkt_size_too_big(uint64_t size) "size:0x%" PRIx64 diff --git a/hw/net/tulip.c b/hw/net/tulip.c index 915e5fb5954..319af906c8d 100644 --- a/hw/net/tulip.c +++ b/hw/net/tulip.c @@ -13,7 +13,7 @@ #include "hw/qdev-properties.h" #include "hw/nvram/eeprom93xx.h" #include "migration/vmstate.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "tulip.h" #include "trace.h" #include "net/eth.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, }; @@ -629,7 +629,7 @@ static void tulip_setup_filter_addr(TULIPState *s, uint8_t *buf, int n) static void tulip_setup_frame(TULIPState *s, struct tulip_descriptor *desc) { - uint8_t buf[4096]; + QEMU_UNINITIALIZED uint8_t buf[4096]; int len = (desc->control >> TDES1_BUF1_SIZE_SHIFT) & TDES1_BUF1_SIZE_MASK; int i; @@ -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); } @@ -1006,12 +1007,11 @@ static void tulip_instance_init(Object *obj) &pci_dev->qdev); } -static Property tulip_properties[] = { +static const Property tulip_properties[] = { DEFINE_NIC_PROPERTIES(TULIPState, c), - DEFINE_PROP_END_OF_LIST(), }; -static void tulip_class_init(ObjectClass *klass, void *data) +static void tulip_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1020,12 +1020,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); } @@ -1035,7 +1035,7 @@ static const TypeInfo tulip_info = { .instance_size = sizeof(TULIPState), .class_init = tulip_class_init, .instance_init = tulip_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 6b958d6363e..891f235a0a6 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, @@ -153,6 +162,148 @@ void vhost_net_save_acked_features(NetClientState *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) { @@ -263,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; @@ -310,8 +456,8 @@ static int vhost_net_start_one(struct vhost_net *net, /* 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) { @@ -319,8 +465,6 @@ static int vhost_net_start_one(struct vhost_net *net, } vhost_dev_stop(&net->dev, dev, false); fail_start: - vhost_dev_disable_notifiers(&net->dev, dev); -fail_notifiers: return r; } @@ -342,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, @@ -387,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++) { @@ -405,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); @@ -428,6 +577,8 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, 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; } @@ -459,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) @@ -538,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) { @@ -626,8 +789,8 @@ int vhost_net_virtqueue_restart(VirtIODevice *vdev, NetClientState *nc, 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 7102ec4817f..eb93607b8c7 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -26,7 +26,7 @@ #include "qemu/option.h" #include "qemu/option_int.h" #include "qemu/config-file.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/virtio/virtio-net.h" #include "net/vhost_net.h" #include "net/announce.h" @@ -39,18 +39,18 @@ #include "hw/virtio/virtio-access.h" #include "migration/misc.h" #include "standard-headers/linux/ethtool.h" -#include "sysemu/sysemu.h" +#include "system/system.h" +#include "system/replay.h" #include "trace.h" #include "monitor/qdev.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" +#include "system/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 @@ -361,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 @@ -381,7 +382,7 @@ static void virtio_net_drop_tx_queue_data(VirtIODevice *vdev, VirtQueue *vq) } } -static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) +static int virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) { VirtIONet *n = VIRTIO_NET(vdev); VirtIONetQueue *q; @@ -417,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) { @@ -436,6 +437,7 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) } } } + return 0; } static void virtio_net_set_link_status(NetClientState *nc) @@ -600,40 +602,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); @@ -659,6 +627,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) { @@ -676,6 +653,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++) { @@ -796,6 +774,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); } @@ -804,6 +786,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; } @@ -859,17 +847,21 @@ 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; } @@ -1006,9 +998,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) 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); } @@ -1245,18 +1235,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); @@ -1264,6 +1242,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); } @@ -1277,7 +1256,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 = {}; @@ -1288,7 +1267,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; } @@ -1299,19 +1279,99 @@ 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) +{ + if (!virtio_net_attach_ebpf_to_backend(n->nic, -1)) { + return true; + } + + trace_virtio_net_rss_load(n, n->nr_ebpf_rss_fds, n->ebpf_rss_fds); + + /* + * 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) { + return virtio_net_load_ebpf_fds(n, errp); + } + + ebpf_rss_load(&n->ebpf_rss, &error_warn); + return true; } static void virtio_net_unload_ebpf(VirtIONet *n) @@ -1354,17 +1414,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; } @@ -1432,31 +1492,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; } @@ -1616,24 +1655,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; } @@ -1662,8 +1705,11 @@ static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr) static void work_around_broken_dhclient(struct virtio_net_hdr *hdr, uint8_t *buf, size_t size) { + size_t csum_size = ETH_HLEN + sizeof(struct ip_header) + + sizeof(struct udp_header); + if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */ - (size > 27 && size < 1500) && /* normal sized MTU */ + (size >= csum_size && 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 */ @@ -1805,16 +1851,9 @@ static uint8_t virtio_net_get_hash_type(bool hasip4, 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; @@ -1845,7 +1884,8 @@ static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf, 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; } @@ -1853,7 +1893,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) { @@ -1865,31 +1906,34 @@ static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf, } 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; + QEMU_UNINITIALIZED VirtQueueElement *elems[VIRTQUEUE_MAX_SIZE]; + QEMU_UNINITIALIZED size_t lens[VIRTQUEUE_MAX_SIZE]; + QEMU_UNINITIALIZED struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE]; + struct virtio_net_hdr_v1_hash extra_hdr; unsigned mhdr_cnt = 0; size_t offset, i, guest_offset, j; ssize_t err; - if (!virtio_net_can_receive(nc)) { - return -1; - } + memset(&extra_hdr, 0, sizeof(extra_hdr)); - if (!no_rss && n->rss_data.enabled && n->rss_data.enabled_software_rss) { - int index = virtio_net_process_rss(nc, buf, size); + if (n->rss_data.enabled && n->rss_data.enabled_software_rss) { + int index = virtio_net_process_rss(nc, buf, size, &extra_hdr); if (index >= 0) { - NetClientState *nc2 = qemu_get_subqueue(n->nic, index); - return virtio_net_receive_rcu(nc2, buf, size, true); + nc = qemu_get_subqueue(n->nic, index % n->curr_queue_pairs); } } + if (!virtio_net_can_receive(nc)) { + return -1; + } + + 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)) { return 0; @@ -1943,15 +1987,19 @@ 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(extra_hdr), hdr.num_buffers), + sizeof(extra_hdr.hdr.num_buffers)); + } else { + extra_hdr.hdr.num_buffers = cpu_to_le16(1); } receive_header(n, sg, elem->in_num, buf, size); if (n->rss_data.populate_hash) { - offset = sizeof(mhdr); + offset = offsetof(typeof(extra_hdr), hash_value); iov_from_buf(sg, elem->in_num, offset, - buf + offset, n->host_hdr_len - sizeof(mhdr)); + (char *)&extra_hdr + offset, + sizeof(extra_hdr.hash_value) + + sizeof(extra_hdr.hash_report)); } offset = n->host_hdr_len; total += n->guest_hdr_len; @@ -1981,10 +2029,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, &extra_hdr.hdr.num_buffers, i); iov_from_buf(mhdr_sg, mhdr_cnt, 0, - &mhdr.num_buffers, sizeof mhdr.num_buffers); + &extra_hdr.hdr.num_buffers, + sizeof extra_hdr.hdr.num_buffers); } for (j = 0; j < i; j++) { @@ -2012,7 +2061,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, @@ -2029,7 +2093,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, @@ -2046,9 +2110,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, @@ -2095,7 +2159,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); } } @@ -2197,7 +2261,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); @@ -2233,7 +2297,7 @@ static int32_t virtio_net_rsc_coalesce_data(VirtioNetRscChain *chain, 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 @@ -2331,7 +2395,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; } @@ -2569,7 +2633,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)); @@ -2644,7 +2708,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); @@ -2674,7 +2738,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) ssize_t ret; unsigned int out_num; struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg; - struct virtio_net_hdr_mrg_rxbuf mhdr; + struct virtio_net_hdr vhdr; elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement)); if (!elem) { @@ -2685,32 +2749,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 @@ -2719,6 +2776,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); @@ -2727,6 +2788,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), @@ -2747,6 +2813,11 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) } } 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); @@ -2785,6 +2856,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; @@ -2799,7 +2874,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) @@ -2882,7 +2957,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; } @@ -2896,7 +2971,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; } } @@ -2992,6 +3067,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; @@ -3052,26 +3136,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; } @@ -3094,7 +3159,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() }, @@ -3172,7 +3237,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, @@ -3209,7 +3274,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() }, @@ -3243,7 +3308,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() }, @@ -3259,7 +3324,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), @@ -3275,12 +3340,123 @@ static const VMStateDescription vmstate_virtio_net_rss = { }, }; +static struct vhost_dev *virtio_net_get_vhost(VirtIODevice *vdev) +{ + VirtIONet *n = VIRTIO_NET(vdev); + 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; +} + +static int vhost_user_net_save_state(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, + JSONWriter *vmdesc) +{ + VirtIONet *n = pv; + VirtIODevice *vdev = VIRTIO_DEVICE(n); + struct vhost_dev *vhdev; + Error *local_error = NULL; + int ret; + + vhdev = virtio_net_get_vhost(vdev); + if (vhdev == NULL) { + error_reportf_err(local_error, + "Error getting vhost back-end of %s device %s: ", + vdev->name, vdev->parent_obj.canonical_path); + return -1; + } + + ret = vhost_save_backend_state(vhdev, f, &local_error); + if (ret < 0) { + error_reportf_err(local_error, + "Error saving back-end state of %s device %s: ", + vdev->name, vdev->parent_obj.canonical_path); + return ret; + } + + return 0; +} + +static int vhost_user_net_load_state(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + VirtIONet *n = pv; + VirtIODevice *vdev = VIRTIO_DEVICE(n); + struct vhost_dev *vhdev; + Error *local_error = NULL; + int ret; + + vhdev = virtio_net_get_vhost(vdev); + if (vhdev == NULL) { + error_reportf_err(local_error, + "Error getting vhost back-end of %s device %s: ", + vdev->name, vdev->parent_obj.canonical_path); + return -1; + } + + ret = vhost_load_backend_state(vhdev, f, &local_error); + if (ret < 0) { + error_reportf_err(local_error, + "Error loading back-end state of %s device %s: ", + vdev->name, vdev->parent_obj.canonical_path); + return ret; + } + + return 0; +} + +static bool vhost_user_net_is_internal_migration(void *opaque) +{ + VirtIONet *n = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(n); + struct vhost_dev *vhdev; + + vhdev = virtio_net_get_vhost(vdev); + if (vhdev == NULL) { + return false; + } + + return vhost_supports_device_state(vhdev); +} + +static const VMStateDescription vhost_user_net_backend_state = { + .name = "virtio-net-device/backend", + .version_id = 0, + .needed = vhost_user_net_is_internal_migration, + .fields = (const VMStateField[]) { + { + .name = "backend", + .info = &(const VMStateInfo) { + .name = "virtio-net vhost-user backend state", + .get = vhost_user_net_load_state, + .put = vhost_user_net_save_state, + }, + }, + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_virtio_net_device = { .name = "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, @@ -3324,9 +3500,10 @@ 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, + &vhost_user_net_backend_state, NULL } }; @@ -3346,7 +3523,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. @@ -3378,7 +3555,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. @@ -3480,7 +3657,7 @@ static bool failover_replug_primary(VirtIONet *n, DeviceState *dev, 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; @@ -3492,7 +3669,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); @@ -3500,7 +3677,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) { @@ -3510,11 +3687,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, @@ -3605,8 +3783,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); } @@ -3677,9 +3855,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); @@ -3695,10 +3871,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++) { @@ -3707,9 +3885,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; @@ -3741,7 +3916,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) net_rx_pkt_init(&n->rx_pkt); if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) { - virtio_net_load_ebpf(n); + virtio_net_load_ebpf(n, errp); } } @@ -3769,7 +3944,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); } @@ -3789,13 +3964,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, @@ -3838,19 +4049,11 @@ static bool dev_unplug_pending(void *opaque) return vdc->primary_unplug_pending(dev); } -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); - return &net->dev; -} - 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() }, @@ -3858,7 +4061,7 @@ static const VMStateDescription vmstate_virtio_net = { .dev_unplug_pending = dev_unplug_pending, }; -static Property virtio_net_properties[] = { +static const Property virtio_net_properties[] = { DEFINE_PROP_BIT64("csum", VirtIONet, host_features, VIRTIO_NET_F_CSUM, true), DEFINE_PROP_BIT64("guest_csum", VirtIONet, host_features, @@ -3903,6 +4106,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, @@ -3922,10 +4127,15 @@ 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_END_OF_LIST(), + 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), }; -static void virtio_net_class_init(ObjectClass *klass, void *data) +static void virtio_net_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); @@ -3947,6 +4157,7 @@ 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; diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 3fb108751a2..7c0ca56b7c0 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -21,7 +21,7 @@ #include "hw/qdev-properties.h" #include "net/tap.h" #include "net/checksum.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/bswap.h" #include "qemu/log.h" #include "qemu/module.h" @@ -41,19 +41,9 @@ #define PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION 0x1 #define VMXNET3_MSIX_BAR_SIZE 0x2000 -/* Compatibility flags for migration */ -#define VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT 0 -#define VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS \ - (1 << VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT) -#define VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT 1 -#define VMXNET3_COMPAT_FLAG_DISABLE_PCIE \ - (1 << VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT) - #define VMXNET3_EXP_EP_OFFSET (0x48) -#define VMXNET3_MSI_OFFSET(s) \ - ((s)->compat_flags & VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS ? 0x50 : 0x84) -#define VMXNET3_MSIX_OFFSET(s) \ - ((s)->compat_flags & VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS ? 0 : 0x9c) +#define VMXNET3_MSI_OFFSET (0x84) +#define VMXNET3_MSIX_OFFSET (0x9c) #define VMXNET3_DSN_OFFSET (0x100) #define VMXNET3_BAR0_IDX (0) @@ -61,8 +51,7 @@ #define VMXNET3_MSIX_BAR_IDX (2) #define VMXNET3_OFF_MSIX_TABLE (0x000) -#define VMXNET3_OFF_MSIX_PBA(s) \ - ((s)->compat_flags & VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS ? 0x800 : 0x1000) +#define VMXNET3_OFF_MSIX_PBA (0x1000) /* Link speed in Mbps should be shifted by 16 */ #define VMXNET3_LINK_SPEED (1000 << 16) @@ -456,7 +445,6 @@ vmxnet3_setup_tx_offloads(VMXNET3State *s) default: g_assert_not_reached(); - return false; } return true; @@ -933,7 +921,6 @@ static void vmxnet3_rx_update_descr(struct NetRxPkt *pkt, nocsum: rxcd->cnc = 1; - return; } static void @@ -1341,6 +1328,8 @@ static void vmxnet3_update_features(VMXNET3State *s) s->lro_supported, s->lro_supported, 0, + 0, + 0, 0); } } @@ -1887,7 +1876,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; } @@ -2076,7 +2065,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; @@ -2089,8 +2078,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); @@ -2124,8 +2111,8 @@ vmxnet3_init_msix(VMXNET3State *s) &s->msix_bar, VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_TABLE, &s->msix_bar, - VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_PBA(s), - VMXNET3_MSIX_OFFSET(s), NULL); + VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_PBA, + VMXNET3_MSIX_OFFSET, NULL); if (0 > res) { VMW_WRPRN("Failed to initialize MSI-X, error %d", res); @@ -2223,7 +2210,7 @@ static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp) /* Interrupt pin A */ pci_dev->config[PCI_INTERRUPT_PIN] = 0x01; - ret = msi_init(pci_dev, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS, + ret = msi_init(pci_dev, VMXNET3_MSI_OFFSET, VMXNET3_MAX_NMSIX_INTRS, VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK, NULL); /* Any error other than -ENOTSUP(board's MSI support is broken) * is a programming error. Fall back to INTx silently on -ENOTSUP */ @@ -2251,6 +2238,7 @@ static void vmxnet3_instance_init(Object *obj) device_add_bootindex_property(obj, &s->conf.bootindex, "bootindex", "/ethernet-phy@0", DEVICE(obj)); + PCI_DEVICE(obj)->cap_present |= QEMU_PCI_CAP_EXPRESS; } static void vmxnet3_pci_uninit(PCIDevice *pci_dev) @@ -2305,7 +2293,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() @@ -2315,7 +2303,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), @@ -2328,7 +2316,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), @@ -2346,7 +2334,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, @@ -2362,7 +2350,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), @@ -2380,7 +2368,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), @@ -2416,7 +2404,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), @@ -2430,7 +2418,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), @@ -2466,39 +2454,20 @@ static const VMStateDescription vmstate_vmxnet3 = { VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmxstate_vmxnet3_mcast_list, NULL } }; -static Property vmxnet3_properties[] = { +static const Property vmxnet3_properties[] = { DEFINE_NIC_PROPERTIES(VMXNET3State, conf), - DEFINE_PROP_BIT("x-old-msi-offsets", VMXNET3State, compat_flags, - VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT, false), - DEFINE_PROP_BIT("x-disable-pcie", VMXNET3State, compat_flags, - VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT, false), - DEFINE_PROP_END_OF_LIST(), }; -static void vmxnet3_realize(DeviceState *qdev, Error **errp) -{ - VMXNET3Class *vc = VMXNET3_DEVICE_GET_CLASS(qdev); - PCIDevice *pci_dev = PCI_DEVICE(qdev); - VMXNET3State *s = VMXNET3(qdev); - - if (!(s->compat_flags & VMXNET3_COMPAT_FLAG_DISABLE_PCIE)) { - pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; - } - - vc->parent_dc_realize(qdev, errp); -} - -static void vmxnet3_class_init(ObjectClass *class, void *data) +static void vmxnet3_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); PCIDeviceClass *c = PCI_DEVICE_CLASS(class); - VMXNET3Class *vc = VMXNET3_DEVICE_CLASS(class); c->realize = vmxnet3_pci_realize; c->exit = vmxnet3_pci_uninit; @@ -2509,10 +2478,8 @@ static void vmxnet3_class_init(ObjectClass *class, void *data) c->class_id = PCI_CLASS_NETWORK_ETHERNET; c->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE; c->subsystem_id = PCI_DEVICE_ID_VMWARE_VMXNET3; - 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); @@ -2525,7 +2492,7 @@ static const TypeInfo vmxnet3_info = { .instance_size = sizeof(VMXNET3State), .class_init = vmxnet3_class_init, .instance_init = vmxnet3_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } diff --git a/hw/net/vmxnet3.h b/hw/net/vmxnet3.h index bf4f6de74a0..f9283f9e7b6 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/xen_nic.c b/hw/net/xen_nic.c index 9bbf6599fc2..34c6a1d0b08 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 "qobject/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,49 +146,52 @@ 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) > XEN_PAGE_SIZE) { - xen_pv_printf(&netdev->xendev, 0, "error: page crossing\n"); + 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; } @@ -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, txreq.gref); + 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; } @@ -244,24 +274,26 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size return 0; } if (size > XEN_PAGE_SIZE - NET_IP_ALIGN) { - xen_pv_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)", - (unsigned long)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, rxreq.gref); + 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,139 +307,361 @@ 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"); - } - - /* do we have all we need? */ - if (netdev->mac == NULL) { - return -1; - } + qemu_macaddr_default_if_unset(&netdev->conf.macaddr); - 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; + 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, "rx-ring-ref", - &netdev->rx_ring_ref) == -1) { - return 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 (xenstore_read_fe_int(&netdev->xendev, "event-channel", - &netdev->xendev.remote_port) == -1) { - return -1; + + if (xen_device_frontend_scanf(xendev, "event-channel", "%u", + &port) != 1) { + error_setg(errp, "failed to read event-channel"); + return false; } - if (xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy) == -1) { + 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->tx_ring_ref); - netdev->txs = NULL; - return -1; + error_prepend(errp, "failed to map rx grant ref: "); + return false; } + BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XEN_PAGE_SIZE); BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XEN_PAGE_SIZE); - xen_be_bind_evtchn(&netdev->xendev); + netdev->event_channel = xen_device_bind_event_channel(xendev, port, + net_event, + netdev, + errp); + if (!netdev->event_channel) { + return false; + } - 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); + 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); + + trace_xen_netdev_disconnect(netdev->dev); + + assert(bql_locked()); - xen_pv_unbind_evtchn(&netdev->xendev); + 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, - netdev->tx_ring_ref); + 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, - netdev->rx_ring_ref); + 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))); + int idx = (xen_mode == XEN_EMULATE) ? 0 : 1; + Error *local_err = NULL; + char *value; + + /* Theoretically we could go up to INT_MAX here but that's overkill */ + while (idx < 100) { + value = xs_node_read(xenbus->xsh, XBT_NULL, NULL, &local_err, + "/local/domain/%u/device/vif/%u", + xendev->frontend_id, idx); + if (!value) { + if (errno == ENOENT) { + netdev->dev = idx; + error_free(local_err); + goto found; + } + error_propagate(errp, local_err); + 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 const Property xen_netdev_properties[] = { + DEFINE_NIC_PROPERTIES(XenNetDev, conf), + DEFINE_PROP_INT32("idx", XenNetDev, dev, -1), +}; + +static void xen_netdev_class_init(ObjectClass *class, const 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 0ab6ae91aa1..d45f8724673 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() @@ -207,7 +207,7 @@ static void xgmac_enet_send(XgmacState *s) struct desc bd; int frame_size; int len; - uint8_t frame[8192]; + QEMU_UNINITIALIZED uint8_t frame[8192]; uint8_t *ptr; ptr = frame; @@ -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) | @@ -413,12 +414,11 @@ static void xgmac_enet_realize(DeviceState *dev, Error **errp) s->conf.macaddr.a[0]; } -static Property xgmac_properties[] = { +static const Property xgmac_properties[] = { DEFINE_NIC_PROPERTIES(XgmacState, conf), - DEFINE_PROP_END_OF_LIST(), }; -static void xgmac_enet_class_init(ObjectClass *klass, void *data) +static void xgmac_enet_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index 5b19a01eaa2..1f5c7480476 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -847,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 @@ -967,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); @@ -995,7 +996,7 @@ static void xilinx_enet_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static Property xilinx_enet_properties[] = { +static const Property xilinx_enet_properties[] = { DEFINE_PROP_UINT32("phyaddr", XilinxAXIEnet, c_phyaddr, 7), DEFINE_PROP_UINT32("rxmem", XilinxAXIEnet, c_rxmem, 0x1000), DEFINE_PROP_UINT32("txmem", XilinxAXIEnet, c_txmem, 0x1000), @@ -1004,27 +1005,27 @@ static Property xilinx_enet_properties[] = { tx_data_dev, TYPE_STREAM_SINK, StreamSink *), DEFINE_PROP_LINK("axistream-control-connected", XilinxAXIEnet, tx_control_dev, TYPE_STREAM_SINK, StreamSink *), - DEFINE_PROP_END_OF_LIST(), }; -static void xilinx_enet_class_init(ObjectClass *klass, void *data) +static void xilinx_enet_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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, - void *data) + const void *data) { StreamSinkClass *ssc = STREAM_SINK_CLASS(klass); ssc->push = xilinx_axienet_control_stream_push; } -static void xilinx_enet_data_stream_class_init(ObjectClass *klass, void *data) +static void xilinx_enet_data_stream_class_init(ObjectClass *klass, + const void *data) { StreamSinkClass *ssc = STREAM_SINK_CLASS(klass); @@ -1044,7 +1045,7 @@ static const TypeInfo xilinx_enet_data_stream_info = { .parent = TYPE_OBJECT, .instance_size = sizeof(XilinxAXIEnetStreamSink), .class_init = xilinx_enet_data_stream_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_STREAM_SINK }, { } } @@ -1055,7 +1056,7 @@ static const TypeInfo xilinx_enet_control_stream_info = { .parent = TYPE_OBJECT, .instance_size = sizeof(XilinxAXIEnetStreamSink), .class_init = xilinx_enet_control_stream_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_STREAM_SINK }, { } } diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 89f4f3b2540..42b19d07c76 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -2,6 +2,10 @@ * QEMU model of the Xilinx Ethernet Lite MAC. * * Copyright (c) 2009 Edgar E. Iglesias. + * Copyright (c) 2024 Linaro, Ltd + * + * DS580: https://docs.amd.com/v/u/en-US/xps_ethernetlite + * LogiCORE IP XPS Ethernet Lite Media Access Controller * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,27 +28,35 @@ #include "qemu/osdep.h" #include "qemu/module.h" +#include "qemu/bitops.h" #include "qom/object.h" -#include "exec/tswap.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "hw/irq.h" #include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "hw/misc/unimp.h" #include "net/net.h" +#include "trace.h" + +#define BUFSZ_MAX 0x07e4 +#define A_MDIO_BASE 0x07e4 +#define A_TX_BASE0 0x07f4 +#define A_TX_BASE1 0x0ff4 +#define A_RX_BASE0 0x17fc +#define A_RX_BASE1 0x1ffc + +enum { + TX_LEN = 0, + TX_GIE = 1, + TX_CTRL = 2, + TX_MAX +}; -#define D(x) -#define R_TX_BUF0 0 -#define R_TX_LEN0 (0x07f4 / 4) -#define R_TX_GIE0 (0x07f8 / 4) -#define R_TX_CTRL0 (0x07fc / 4) -#define R_TX_BUF1 (0x0800 / 4) -#define R_TX_LEN1 (0x0ff4 / 4) -#define R_TX_CTRL1 (0x0ffc / 4) - -#define R_RX_BUF0 (0x1000 / 4) -#define R_RX_CTRL0 (0x17fc / 4) -#define R_RX_BUF1 (0x1800 / 4) -#define R_RX_CTRL1 (0x1ffc / 4) -#define R_MAX (0x2000 / 4) +enum { + RX_CTRL = 0, + RX_MAX +}; #define GIE_GIE 0x80000000 @@ -52,174 +64,238 @@ #define CTRL_P 0x2 #define CTRL_S 0x1 +typedef struct XlnxXpsEthLitePort { + MemoryRegion txio; + MemoryRegion rxio; + MemoryRegion txbuf; + MemoryRegion rxbuf; + + struct { + uint32_t tx_len; + uint32_t tx_gie; + uint32_t tx_ctrl; + + uint32_t rx_ctrl; + } reg; +} XlnxXpsEthLitePort; + #define TYPE_XILINX_ETHLITE "xlnx.xps-ethernetlite" -DECLARE_INSTANCE_CHECKER(struct xlx_ethlite, XILINX_ETHLITE, - TYPE_XILINX_ETHLITE) +OBJECT_DECLARE_SIMPLE_TYPE(XlnxXpsEthLite, XILINX_ETHLITE) -struct xlx_ethlite +struct XlnxXpsEthLite { SysBusDevice parent_obj; - MemoryRegion mmio; + EndianMode model_endianness; + MemoryRegion container; qemu_irq irq; NICState *nic; NICConf conf; uint32_t c_tx_pingpong; uint32_t c_rx_pingpong; - unsigned int txbuf; - unsigned int rxbuf; + unsigned int port_index; /* dual port RAM index */ - uint32_t regs[R_MAX]; + UnimplementedDeviceState rsvd; + UnimplementedDeviceState mdio; + XlnxXpsEthLitePort port[2]; }; -static inline void eth_pulse_irq(struct xlx_ethlite *s) +static inline void eth_pulse_irq(XlnxXpsEthLite *s) { /* Only the first gie reg is active. */ - if (s->regs[R_TX_GIE0] & GIE_GIE) { + if (s->port[0].reg.tx_gie & GIE_GIE) { qemu_irq_pulse(s->irq); } } -static uint64_t -eth_read(void *opaque, hwaddr addr, unsigned int size) +static unsigned addr_to_port_index(hwaddr addr) { - struct xlx_ethlite *s = opaque; - uint32_t r = 0; + return extract64(addr, 11, 1); +} - addr >>= 2; +static void *txbuf_ptr(XlnxXpsEthLite *s, unsigned port_index) +{ + return memory_region_get_ram_ptr(&s->port[port_index].txbuf); +} - switch (addr) - { - case R_TX_GIE0: - case R_TX_LEN0: - case R_TX_LEN1: - case R_TX_CTRL1: - case R_TX_CTRL0: - case R_RX_CTRL1: - case R_RX_CTRL0: - r = s->regs[addr]; - D(qemu_log("%s " HWADDR_FMT_plx "=%x\n", __func__, addr * 4, r)); - break; - - default: - r = tswap32(s->regs[addr]); - break; +static void *rxbuf_ptr(XlnxXpsEthLite *s, unsigned port_index) +{ + return memory_region_get_ram_ptr(&s->port[port_index].rxbuf); +} + +static uint64_t port_tx_read(void *opaque, hwaddr addr, unsigned int size) +{ + XlnxXpsEthLite *s = opaque; + unsigned port_index = addr_to_port_index(addr); + uint32_t r = 0; + + switch (addr >> 2) { + case TX_LEN: + r = s->port[port_index].reg.tx_len; + break; + case TX_GIE: + r = s->port[port_index].reg.tx_gie; + break; + case TX_CTRL: + r = s->port[port_index].reg.tx_ctrl; + break; + default: + g_assert_not_reached(); } + return r; } -static void -eth_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) +static void port_tx_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) { - struct xlx_ethlite *s = opaque; - unsigned int base = 0; - uint32_t value = val64; - - addr >>= 2; - switch (addr) - { - case R_TX_CTRL0: - case R_TX_CTRL1: - if (addr == R_TX_CTRL1) - base = 0x800 / 4; - - 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), - (void *) &s->regs[base], - s->regs[base + R_TX_LEN0]); - D(qemu_log("eth_tx %d\n", s->regs[base + R_TX_LEN0])); - if (s->regs[base + R_TX_CTRL0] & CTRL_I) - eth_pulse_irq(s); - } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) { - memcpy(&s->conf.macaddr.a[0], &s->regs[base], 6); - if (s->regs[base + R_TX_CTRL0] & CTRL_I) - eth_pulse_irq(s); + XlnxXpsEthLite *s = opaque; + unsigned port_index = addr_to_port_index(addr); + + switch (addr >> 2) { + case TX_LEN: + s->port[port_index].reg.tx_len = value; + break; + case TX_GIE: + s->port[port_index].reg.tx_gie = value; + break; + case TX_CTRL: + if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { + qemu_send_packet(qemu_get_queue(s->nic), + txbuf_ptr(s, port_index), + s->port[port_index].reg.tx_len); + if (s->port[port_index].reg.tx_ctrl & CTRL_I) { + eth_pulse_irq(s); + } + } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) { + memcpy(&s->conf.macaddr.a[0], txbuf_ptr(s, port_index), 6); + if (s->port[port_index].reg.tx_ctrl & CTRL_I) { + eth_pulse_irq(s); } + } + /* + * We are fast and get ready pretty much immediately + * so we actually never flip the S nor P bits to one. + */ + s->port[port_index].reg.tx_ctrl = value & ~(CTRL_P | CTRL_S); + break; + default: + g_assert_not_reached(); + } +} - /* We are fast and get ready pretty much immediately so - we actually never flip the S nor P bits to one. */ - s->regs[addr] = value & ~(CTRL_P | CTRL_S); - break; +static const MemoryRegionOps eth_porttx_ops[2] = { + [0 ... 1] = { + .read = port_tx_read, + .write = port_tx_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + }, + [0].endianness = DEVICE_LITTLE_ENDIAN, + [1].endianness = DEVICE_BIG_ENDIAN, +}; - /* Keep these native. */ - case R_RX_CTRL0: - case R_RX_CTRL1: - if (!(value & CTRL_S)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - /* fall through */ - case R_TX_LEN0: - case R_TX_LEN1: - case R_TX_GIE0: - D(qemu_log("%s addr=" HWADDR_FMT_plx " val=%x\n", - __func__, addr * 4, value)); - s->regs[addr] = value; - break; - - default: - s->regs[addr] = tswap32(value); - break; +static uint64_t port_rx_read(void *opaque, hwaddr addr, unsigned int size) +{ + XlnxXpsEthLite *s = opaque; + unsigned port_index = addr_to_port_index(addr); + uint32_t r = 0; + + switch (addr >> 2) { + case RX_CTRL: + r = s->port[port_index].reg.rx_ctrl; + break; + default: + g_assert_not_reached(); } + + return r; } -static const MemoryRegionOps eth_ops = { - .read = eth_read, - .write = eth_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 +static void port_rx_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) +{ + XlnxXpsEthLite *s = opaque; + unsigned port_index = addr_to_port_index(addr); + + switch (addr >> 2) { + case RX_CTRL: + if (!(value & CTRL_S)) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } + s->port[port_index].reg.rx_ctrl = value; + break; + default: + g_assert_not_reached(); } +} + +static const MemoryRegionOps eth_portrx_ops[2] = { + [0 ... 1] = { + .read = port_rx_read, + .write = port_rx_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + }, + [0].endianness = DEVICE_LITTLE_ENDIAN, + [1].endianness = DEVICE_BIG_ENDIAN, }; static bool eth_can_rx(NetClientState *nc) { - struct xlx_ethlite *s = qemu_get_nic_opaque(nc); - unsigned int rxbase = s->rxbuf * (0x800 / 4); + XlnxXpsEthLite *s = qemu_get_nic_opaque(nc); - return !(s->regs[rxbase + R_RX_CTRL0] & CTRL_S); + return !(s->port[s->port_index].reg.rx_ctrl & CTRL_S); } static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) { - struct xlx_ethlite *s = qemu_get_nic_opaque(nc); - unsigned int rxbase = s->rxbuf * (0x800 / 4); + XlnxXpsEthLite *s = qemu_get_nic_opaque(nc); + unsigned int port_index = s->port_index; /* DA filter. */ if (!(buf[0] & 0x80) && memcmp(&s->conf.macaddr.a[0], buf, 6)) return size; - if (s->regs[rxbase + R_RX_CTRL0] & CTRL_S) { - D(qemu_log("ethlite lost packet %x\n", s->regs[R_RX_CTRL0])); + if (s->port[port_index].reg.rx_ctrl & CTRL_S) { + trace_ethlite_pkt_lost(s->port[port_index].reg.rx_ctrl); return -1; } - D(qemu_log("%s %zd rxbase=%x\n", __func__, size, rxbase)); - if (size > (R_MAX - R_RX_BUF0 - rxbase) * 4) { - D(qemu_log("ethlite packet is too big, size=%x\n", size)); + if (size >= BUFSZ_MAX) { + trace_ethlite_pkt_size_too_big(size); return -1; } - memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size); + memcpy(rxbuf_ptr(s, port_index), buf, size); - s->regs[rxbase + R_RX_CTRL0] |= CTRL_S; - if (s->regs[R_RX_CTRL0] & CTRL_I) { + s->port[port_index].reg.rx_ctrl |= CTRL_S; + if (s->port[port_index].reg.rx_ctrl & CTRL_I) { eth_pulse_irq(s); } /* If c_rx_pingpong was set flip buffers. */ - s->rxbuf ^= s->c_rx_pingpong; + s->port_index ^= s->c_rx_pingpong; return size; } static void xilinx_ethlite_reset(DeviceState *dev) { - struct xlx_ethlite *s = XILINX_ETHLITE(dev); + XlnxXpsEthLite *s = XILINX_ETHLITE(dev); - s->rxbuf = 0; + s->port_index = 0; } static NetClientInfo net_xilinx_ethlite_info = { @@ -231,52 +307,101 @@ static NetClientInfo net_xilinx_ethlite_info = { static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) { - struct xlx_ethlite *s = XILINX_ETHLITE(dev); + XlnxXpsEthLite *s = XILINX_ETHLITE(dev); + unsigned ops_index; + + if (s->model_endianness == ENDIAN_MODE_UNSPECIFIED) { + error_setg(errp, TYPE_XILINX_ETHLITE " property 'endianness'" + " must be set to 'big' or 'little'"); + return; + } + ops_index = s->model_endianness == ENDIAN_MODE_BIG ? 1 : 0; + + memory_region_init(&s->container, OBJECT(dev), + "xlnx.xps-ethernetlite", 0x2000); + + object_initialize_child(OBJECT(dev), "ethlite.reserved", &s->rsvd, + TYPE_UNIMPLEMENTED_DEVICE); + qdev_prop_set_string(DEVICE(&s->rsvd), "name", "ethlite.reserved"); + qdev_prop_set_uint64(DEVICE(&s->rsvd), "size", + memory_region_size(&s->container)); + sysbus_realize(SYS_BUS_DEVICE(&s->rsvd), &error_fatal); + memory_region_add_subregion_overlap(&s->container, 0, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rsvd), 0), + -1); + + object_initialize_child(OBJECT(dev), "ethlite.mdio", &s->mdio, + TYPE_UNIMPLEMENTED_DEVICE); + qdev_prop_set_string(DEVICE(&s->mdio), "name", "ethlite.mdio"); + qdev_prop_set_uint64(DEVICE(&s->mdio), "size", 4 * 4); + sysbus_realize(SYS_BUS_DEVICE(&s->mdio), &error_fatal); + memory_region_add_subregion(&s->container, A_MDIO_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mdio), 0)); + + for (unsigned i = 0; i < 2; i++) { + memory_region_init_ram(&s->port[i].txbuf, OBJECT(dev), + i ? "ethlite.tx[1]buf" : "ethlite.tx[0]buf", + BUFSZ_MAX, &error_abort); + memory_region_add_subregion(&s->container, 0x0800 * i, &s->port[i].txbuf); + memory_region_init_io(&s->port[i].txio, OBJECT(dev), + ð_porttx_ops[ops_index], s, + i ? "ethlite.tx[1]io" : "ethlite.tx[0]io", + 4 * TX_MAX); + memory_region_add_subregion(&s->container, i ? A_TX_BASE1 : A_TX_BASE0, + &s->port[i].txio); + + memory_region_init_ram(&s->port[i].rxbuf, OBJECT(dev), + i ? "ethlite.rx[1]buf" : "ethlite.rx[0]buf", + BUFSZ_MAX, &error_abort); + memory_region_add_subregion(&s->container, 0x1000 + 0x0800 * i, + &s->port[i].rxbuf); + memory_region_init_io(&s->port[i].rxio, OBJECT(dev), + ð_portrx_ops[ops_index], s, + i ? "ethlite.rx[1]io" : "ethlite.rx[0]io", + 4 * RX_MAX); + memory_region_add_subregion(&s->container, i ? A_RX_BASE1 : A_RX_BASE0, + &s->port[i].rxio); + } 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); } static void xilinx_ethlite_init(Object *obj) { - struct xlx_ethlite *s = XILINX_ETHLITE(obj); + XlnxXpsEthLite *s = XILINX_ETHLITE(obj); sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); - - memory_region_init_io(&s->mmio, obj, ð_ops, s, - "xlnx.xps-ethernetlite", R_MAX * 4); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->container); } -static Property xilinx_ethlite_properties[] = { - DEFINE_PROP_UINT32("tx-ping-pong", struct xlx_ethlite, c_tx_pingpong, 1), - DEFINE_PROP_UINT32("rx-ping-pong", struct xlx_ethlite, c_rx_pingpong, 1), - DEFINE_NIC_PROPERTIES(struct xlx_ethlite, conf), - DEFINE_PROP_END_OF_LIST(), +static const Property xilinx_ethlite_properties[] = { + DEFINE_PROP_ENDIAN_NODEFAULT("endianness", XlnxXpsEthLite, model_endianness), + DEFINE_PROP_UINT32("tx-ping-pong", XlnxXpsEthLite, c_tx_pingpong, 1), + DEFINE_PROP_UINT32("rx-ping-pong", XlnxXpsEthLite, c_rx_pingpong, 1), + DEFINE_NIC_PROPERTIES(XlnxXpsEthLite, conf), }; -static void xilinx_ethlite_class_init(ObjectClass *klass, void *data) +static void xilinx_ethlite_class_init(ObjectClass *klass, const 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); } -static const TypeInfo xilinx_ethlite_info = { - .name = TYPE_XILINX_ETHLITE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct xlx_ethlite), - .instance_init = xilinx_ethlite_init, - .class_init = xilinx_ethlite_class_init, +static const TypeInfo xilinx_ethlite_types[] = { + { + .name = TYPE_XILINX_ETHLITE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxXpsEthLite), + .instance_init = xilinx_ethlite_init, + .class_init = xilinx_ethlite_class_init, + }, }; -static void xilinx_ethlite_register_types(void) -{ - type_register_static(&xilinx_ethlite_info); -} - -type_init(xilinx_ethlite_register_types) +DEFINE_TYPES(xilinx_ethlite_types) diff --git a/hw/nios2/10m50_devboard.c b/hw/nios2/10m50_devboard.c deleted file mode 100644 index 91383fb0979..00000000000 --- 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 4748ae27b67..00000000000 --- 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 cd75803fc2f..00000000000 --- a/hw/nios2/boot.c +++ /dev/null @@ -1,234 +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; - - /* Boots a kernel elf binary. */ - kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &entry, NULL, &high, NULL, - TARGET_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, - TARGET_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 59b9fbfc622..00000000000 --- 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 48edb3ae373..00000000000 --- 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 22277bd6c56..00000000000 --- 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/mac-nubus-bridge.c b/hw/nubus/mac-nubus-bridge.c index a0da5a8b2fa..0dac8d19b37 100644 --- a/hw/nubus/mac-nubus-bridge.c +++ b/hw/nubus/mac-nubus-bridge.c @@ -40,7 +40,7 @@ static void mac_nubus_bridge_init(Object *obj) sysbus_init_mmio(sbd, &s->slot_alias); } -static void mac_nubus_bridge_class_init(ObjectClass *klass, void *data) +static void mac_nubus_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/nubus/meson.build b/hw/nubus/meson.build index e7ebda89935..9a7a12ea683 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')) system_ss.add_all(when: 'CONFIG_NUBUS', if_true: nubus_ss) diff --git a/hw/nubus/nubus-bridge.c b/hw/nubus/nubus-bridge.c index a42c86080f2..fb14402c4fc 100644 --- a/hw/nubus/nubus-bridge.c +++ b/hw/nubus/nubus-bridge.c @@ -23,13 +23,12 @@ static void nubus_bridge_init(Object *obj) qdev_init_gpio_out(DEVICE(s), bus->irqs, NUBUS_IRQS); } -static Property nubus_bridge_properties[] = { +static const Property nubus_bridge_properties[] = { DEFINE_PROP_UINT16("slot-available-mask", NubusBridge, bus.slot_available_mask, 0xffff), - DEFINE_PROP_END_OF_LIST() }; -static void nubus_bridge_class_init(ObjectClass *klass, void *data) +static void nubus_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/nubus/nubus-bus.c b/hw/nubus/nubus-bus.c index 07c279bde5c..44820f13a85 100644 --- a/hw/nubus/nubus-bus.c +++ b/hw/nubus/nubus-bus.c @@ -162,7 +162,7 @@ static bool nubus_check_address(BusState *bus, DeviceState *dev, Error **errp) return true; } -static void nubus_class_init(ObjectClass *oc, void *data) +static void nubus_class_init(ObjectClass *oc, const void *data) { BusClass *bc = BUS_CLASS(oc); diff --git a/hw/nubus/nubus-device.c b/hw/nubus/nubus-device.c index 49008e49385..7797e61c7fc 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,27 +85,34 @@ 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); } } -static Property nubus_device_properties[] = { +static const Property nubus_device_properties[] = { DEFINE_PROP_INT32("slot", NubusDevice, slot, -1), DEFINE_PROP_STRING("romfile", NubusDevice, romfile), - DEFINE_PROP_END_OF_LIST() }; -static void nubus_device_class_init(ObjectClass *oc, void *data) +static void nubus_device_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/nubus/nubus-virtio-mmio.c b/hw/nubus/nubus-virtio-mmio.c new file mode 100644 index 00000000000..63aeca5b122 --- /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, const 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/nvme/ctrl.c b/hw/nvme/ctrl.c index 90687b168ae..220002830de 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,6 +40,9 @@ * 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=, \ @@ -198,11 +201,12 @@ #include "qemu/range.h" #include "qapi/error.h" #include "qapi/visitor.h" -#include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" -#include "sysemu/hostmem.h" +#include "system/system.h" +#include "system/block-backend.h" +#include "system/hostmem.h" #include "hw/pci/msix.h" #include "hw/pci/pcie_sriov.h" +#include "system/spdm-socket.h" #include "migration/vmstate.h" #include "nvme.h" @@ -219,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 @@ -254,6 +257,7 @@ 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, @@ -262,7 +266,7 @@ static const uint32_t nvme_feature_cap[NVME_FID_MAX] = { [NVME_FDP_EVENTS] = NVME_FEAT_CAP_CHANGE | NVME_FEAT_CAP_NS, }; -static const uint32_t nvme_cse_acs[256] = { +static const uint32_t nvme_cse_acs_default[256] = { [NVME_ADM_CMD_DELETE_SQ] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_CREATE_SQ] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_GET_LOG_PAGE] = NVME_CMD_EFF_CSUPP, @@ -273,17 +277,14 @@ static const uint32_t nvme_cse_acs[256] = { [NVME_ADM_CMD_SET_FEATURES] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_GET_FEATURES] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_ASYNC_EV_REQ] = NVME_CMD_EFF_CSUPP, - [NVME_ADM_CMD_NS_ATTACHMENT] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_NIC, - [NVME_ADM_CMD_VIRT_MNGMT] = NVME_CMD_EFF_CSUPP, - [NVME_ADM_CMD_DBBUF_CONFIG] = NVME_CMD_EFF_CSUPP, + [NVME_ADM_CMD_NS_ATTACHMENT] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_NIC | + NVME_CMD_EFF_CCC, [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]; - -static const uint32_t nvme_cse_iocs_nvm[256] = { +static const uint32_t nvme_cse_iocs_nvm_default[256] = { [NVME_CMD_FLUSH] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_WRITE_ZEROES] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_WRITE] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, @@ -296,7 +297,7 @@ static const uint32_t nvme_cse_iocs_nvm[256] = { [NVME_CMD_IO_MGMT_SEND] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, }; -static const uint32_t nvme_cse_iocs_zoned[256] = { +static const uint32_t nvme_cse_iocs_zoned_default[256] = { [NVME_CMD_FLUSH] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_WRITE_ZEROES] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_WRITE] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, @@ -305,6 +306,9 @@ static const uint32_t nvme_cse_iocs_zoned[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, + [NVME_CMD_ZONE_APPEND] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_ZONE_MGMT_SEND] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_ZONE_MGMT_RECV] = NVME_CMD_EFF_CSUPP, @@ -652,6 +656,12 @@ static void nvme_irq_check(NvmeCtrl *n) if (msix_enabled(pci)) { return; } + + /* vfs does not implement intx */ + if (pci_is_vf(pci)) { + return; + } + if (~intms & n->irq_status) { pci_irq_assert(pci); } else { @@ -730,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. */ @@ -1047,7 +1057,8 @@ static uint16_t nvme_map_sgl(NvmeCtrl *n, NvmeSg *sg, NvmeSglDescriptor sgl, */ #define SEG_CHUNK_SIZE 256 - NvmeSglDescriptor segment[SEG_CHUNK_SIZE], *sgld, *last_sgld; + QEMU_UNINITIALIZED NvmeSglDescriptor segment[SEG_CHUNK_SIZE]; + NvmeSglDescriptor *sgld, *last_sgld; uint64_t nsgld; uint32_t seg_len; uint16_t status; @@ -1516,9 +1527,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) { @@ -1649,9 +1667,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); + } } } @@ -1738,43 +1763,6 @@ static uint16_t nvme_check_dulbe(NvmeNamespace *ns, uint64_t slba, return NVME_SUCCESS; } -static void nvme_aio_err(NvmeRequest *req, int ret) -{ - uint16_t status = NVME_SUCCESS; - Error *local_err = NULL; - - switch (req->cmd.opcode) { - case NVME_CMD_READ: - status = NVME_UNRECOVERED_READ; - break; - case NVME_CMD_FLUSH: - case NVME_CMD_WRITE: - case NVME_CMD_WRITE_ZEROES: - case NVME_CMD_ZONE_APPEND: - case NVME_CMD_COPY: - status = NVME_WRITE_FAULT; - break; - default: - status = NVME_INTERNAL_DEV_ERROR; - break; - } - - trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), status); - - error_setg_errno(&local_err, -ret, "aio failed"); - error_report_err(local_err); - - /* - * Set the command status code to the first encountered error but allow a - * subsequent Internal Device Error to trump it. - */ - if (req->status && status != NVME_INTERNAL_DEV_ERROR) { - return; - } - - req->status = status; -} - static inline uint32_t nvme_zone_idx(NvmeNamespace *ns, uint64_t slba) { return ns->zone_size_log2 > 0 ? slba >> ns->zone_size_log2 : @@ -1812,7 +1800,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; @@ -1866,7 +1854,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; @@ -2130,19 +2118,19 @@ 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; + uint16_t cid = nvme_cid(req); - trace_pci_nvme_misc_cb(nvme_cid(req)); + trace_pci_nvme_misc_cb(cid); if (ret) { - nvme_aio_err(req, ret); + if (!req->status) { + req->status = NVME_INTERNAL_DEV_ERROR; + } + + trace_pci_nvme_err_aio(cid, strerror(-ret), req->status); } nvme_enqueue_req_completion(nvme_cq(req), req); @@ -2159,8 +2147,30 @@ void nvme_rw_complete_cb(void *opaque, int ret) trace_pci_nvme_rw_complete_cb(nvme_cid(req), blk_name(blk)); if (ret) { + Error *err = NULL; + block_acct_failed(stats, acct); - nvme_aio_err(req, ret); + + switch (req->cmd.opcode) { + case NVME_CMD_READ: + req->status = NVME_UNRECOVERED_READ; + break; + + case NVME_CMD_WRITE: + case NVME_CMD_WRITE_ZEROES: + case NVME_CMD_ZONE_APPEND: + req->status = NVME_WRITE_FAULT; + break; + + default: + req->status = NVME_INTERNAL_DEV_ERROR; + break; + } + + trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), req->status); + + error_setg_errno(&err, -ret, "aio failed"); + error_report_err(err); } else { block_acct_done(stats, acct); } @@ -2245,7 +2255,10 @@ static void nvme_verify_cb(void *opaque, int ret) if (ret) { block_acct_failed(stats, acct); - nvme_aio_err(req, ret); + req->status = NVME_UNRECOVERED_READ; + + trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), req->status); + goto out; } @@ -2344,7 +2357,10 @@ static void nvme_compare_mdata_cb(void *opaque, int ret) if (ret) { block_acct_failed(stats, acct); - nvme_aio_err(req, ret); + req->status = NVME_UNRECOVERED_READ; + + trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), req->status); + goto out; } @@ -2426,7 +2442,10 @@ static void nvme_compare_data_cb(void *opaque, int ret) if (ret) { block_acct_failed(stats, acct); - nvme_aio_err(req, ret); + req->status = NVME_UNRECOVERED_READ; + + trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), req->status); + goto out; } @@ -2597,6 +2616,7 @@ static void nvme_dsm_cb(void *opaque, int ret) done: iocb->aiocb = NULL; iocb->common.cb(iocb->common.opaque, iocb->ret); + g_free(iocb->range); qemu_aio_unref(iocb); } @@ -2646,6 +2666,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); @@ -2665,7 +2686,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; } @@ -2701,6 +2726,7 @@ typedef struct NvmeCopyAIOCB { BlockAIOCB common; BlockAIOCB *aiocb; NvmeRequest *req; + NvmeCtrl *n; int ret; void *ranges; @@ -2719,6 +2745,8 @@ typedef struct NvmeCopyAIOCB { uint64_t slba; NvmeZone *zone; + NvmeNamespace *sns; + uint32_t tcl; } NvmeCopyAIOCB; static void nvme_copy_cancel(BlockAIOCB *aiocb) @@ -2765,13 +2793,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); @@ -2794,13 +2828,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); @@ -2832,18 +2872,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: @@ -2859,10 +2901,10 @@ static inline uint16_t nvme_check_copy_mcl(NvmeNamespace *ns, for (int idx = 0; idx < nr; idx++) { uint32_t nlb; nvme_copy_source_range_parse(iocb->ranges, idx, iocb->format, NULL, - &nlb, NULL, NULL, NULL); - copy_len += nlb + 1; + &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; } @@ -2874,21 +2916,22 @@ 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; + req->status = NVME_WRITE_FAULT; goto out; } else if (iocb->ret < 0) { 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++; @@ -2901,25 +2944,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); @@ -2933,59 +2976,71 @@ 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) { iocb->ret = ret; + req->status = NVME_UNRECOVERED_READ; goto out; } else if (iocb->ret < 0) { goto out; } 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) { @@ -2994,13 +3049,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; } @@ -3013,7 +3068,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; @@ -3028,23 +3086,22 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) 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; @@ -3053,14 +3110,78 @@ static void nvme_copy_in_cb(void *opaque, int ret) 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; @@ -3070,40 +3191,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; @@ -3122,9 +3327,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; @@ -3133,13 +3336,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; @@ -3150,14 +3349,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; @@ -3193,17 +3392,13 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) 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; @@ -3238,7 +3433,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; } @@ -3302,7 +3501,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); @@ -3314,6 +3512,7 @@ static void nvme_flush_ns_cb(void *opaque, int ret) if (ret < 0) { iocb->ret = ret; + iocb->req->status = NVME_WRITE_FAULT; goto out; } else if (iocb->ret < 0) { goto out; @@ -3416,7 +3615,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)) { @@ -3528,7 +3727,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)) { @@ -4174,7 +4373,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; @@ -4307,7 +4506,7 @@ static uint16_t nvme_io_mgmt_recv_ruhs(NvmeCtrl *n, NvmeRequest *req, nruhsd = ns->fdp.nphs * endgrp->fdp.nrg; trans_len = sizeof(NvmeRuhStatus) + nruhsd * sizeof(NvmeRuhStatusDescr); - buf = g_malloc(trans_len); + buf = g_malloc0(trans_len); trans_len = MIN(trans_len, len); @@ -4358,7 +4557,7 @@ static uint16_t nvme_io_mgmt_send_ruh_update(NvmeCtrl *n, NvmeRequest *req) NvmeNamespace *ns = req->ns; uint32_t cdw10 = le32_to_cpu(cmd->cdw10); uint16_t ret = NVME_SUCCESS; - uint32_t npid = (cdw10 >> 1) + 1; + uint32_t npid = (cdw10 >> 16) + 1; unsigned int i = 0; g_autofree uint16_t *pids = NULL; uint32_t maxnpid; @@ -4405,6 +4604,61 @@ static uint16_t nvme_io_mgmt_send(NvmeCtrl *n, NvmeRequest *req) }; } +static uint16_t __nvme_io_cmd_nvm(NvmeCtrl *n, NvmeRequest *req) +{ + switch (req->cmd.opcode) { + case NVME_CMD_WRITE: + return nvme_write(n, req); + case NVME_CMD_READ: + return nvme_read(n, req); + case NVME_CMD_COMPARE: + return nvme_compare(n, req); + case NVME_CMD_WRITE_ZEROES: + return nvme_write_zeroes(n, req); + case NVME_CMD_DSM: + return nvme_dsm(n, req); + case NVME_CMD_VERIFY: + return nvme_verify(n, req); + case NVME_CMD_COPY: + return nvme_copy(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); + } + + g_assert_not_reached(); +} + +static uint16_t nvme_io_cmd_nvm(NvmeCtrl *n, NvmeRequest *req) +{ + if (!(n->cse.iocs.nvm[req->cmd.opcode] & NVME_CMD_EFF_CSUPP)) { + trace_pci_nvme_err_invalid_opc(req->cmd.opcode); + return NVME_INVALID_OPCODE | NVME_DNR; + } + + return __nvme_io_cmd_nvm(n, req); +} + +static uint16_t nvme_io_cmd_zoned(NvmeCtrl *n, NvmeRequest *req) +{ + if (!(n->cse.iocs.zoned[req->cmd.opcode] & NVME_CMD_EFF_CSUPP)) { + trace_pci_nvme_err_invalid_opc(req->cmd.opcode); + return NVME_INVALID_OPCODE | NVME_DNR; + } + + switch (req->cmd.opcode) { + case NVME_CMD_ZONE_APPEND: + return nvme_zone_append(n, req); + case NVME_CMD_ZONE_MGMT_SEND: + return nvme_zone_mgmt_send(n, req); + case NVME_CMD_ZONE_MGMT_RECV: + return nvme_zone_mgmt_recv(n, req); + } + + return __nvme_io_cmd_nvm(n, req); +} + static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) { NvmeNamespace *ns; @@ -4413,10 +4667,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 @@ -4436,20 +4686,20 @@ 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; } - if (!(ns->iocs[req->cmd.opcode] & NVME_CMD_EFF_CSUPP)) { - trace_pci_nvme_err_invalid_opc(req->cmd.opcode); - return NVME_INVALID_OPCODE | NVME_DNR; - } - if (ns->status) { return ns->status; } @@ -4460,36 +4710,14 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) req->ns = ns; - switch (req->cmd.opcode) { - case NVME_CMD_WRITE_ZEROES: - return nvme_write_zeroes(n, req); - case NVME_CMD_ZONE_APPEND: - return nvme_zone_append(n, req); - case NVME_CMD_WRITE: - return nvme_write(n, req); - case NVME_CMD_READ: - return nvme_read(n, req); - case NVME_CMD_COMPARE: - return nvme_compare(n, req); - case NVME_CMD_DSM: - return nvme_dsm(n, req); - case NVME_CMD_VERIFY: - return nvme_verify(n, req); - case NVME_CMD_COPY: - return nvme_copy(n, req); - case NVME_CMD_ZONE_MGMT_SEND: - 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); + switch (ns->csi) { + case NVME_CSI_NVM: + return nvme_io_cmd_nvm(n, req); + case NVME_CSI_ZONED: + return nvme_io_cmd_zoned(n, req); } - return NVME_INVALID_OPCODE | NVME_DNR; + g_assert_not_reached(); } static void nvme_cq_notifier(EventNotifier *e) @@ -4598,6 +4826,7 @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeRequest *req) while (!QTAILQ_EMPTY(&sq->out_req_list)) { r = QTAILQ_FIRST(&sq->out_req_list); assert(r->aiocb); + r->status = NVME_CMD_ABORT_SQ_DEL; blk_aio_cancel(r->aiocb); } @@ -4716,6 +4945,45 @@ static void nvme_set_blk_stats(NvmeNamespace *ns, struct nvme_stats *stats) stats->write_commands += s->nr_ops[BLOCK_ACCT_WRITE]; } +static uint16_t nvme_ocp_extended_smart_info(NvmeCtrl *n, uint8_t rae, + uint32_t buf_len, uint64_t off, + NvmeRequest *req) +{ + NvmeNamespace *ns = NULL; + NvmeSmartLogExtended smart_l = { 0 }; + struct nvme_stats stats = { 0 }; + uint32_t trans_len; + + if (off >= sizeof(smart_l)) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + /* accumulate all stats from all namespaces */ + for (int i = 1; i <= NVME_MAX_NAMESPACES; i++) { + ns = nvme_ns(n, i); + if (ns) { + nvme_set_blk_stats(ns, &stats); + } + } + + smart_l.physical_media_units_written[0] = cpu_to_le64(stats.units_written); + smart_l.physical_media_units_read[0] = cpu_to_le64(stats.units_read); + smart_l.log_page_version = 0x0005; + + static const uint8_t guid[16] = { + 0xC5, 0xAF, 0x10, 0x28, 0xEA, 0xBF, 0xF2, 0xA4, + 0x9C, 0x4F, 0x6F, 0x7C, 0xC9, 0x14, 0xD5, 0xAF + }; + memcpy(smart_l.log_page_guid, guid, sizeof(smart_l.log_page_guid)); + + if (!rae) { + nvme_clear_events(n, NVME_AER_TYPE_SMART); + } + + trans_len = MIN(sizeof(smart_l) - off, buf_len); + return nvme_c2h(n, (uint8_t *) &smart_l + off, trans_len, req); +} + static uint16_t nvme_smart_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, uint64_t off, NvmeRequest *req) { @@ -4861,7 +5129,7 @@ static uint16_t nvme_error_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, static uint16_t nvme_changed_nslist(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, uint64_t off, NvmeRequest *req) { - uint32_t nslist[1024]; + uint32_t nslist[1024] = {}; uint32_t trans_len; int i = 0; uint32_t nsid; @@ -4871,7 +5139,6 @@ static uint16_t nvme_changed_nslist(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, return NVME_INVALID_FIELD | NVME_DNR; } - memset(nslist, 0x0, sizeof(nslist)); trans_len = MIN(sizeof(nslist) - off, buf_len); while ((nsid = find_first_bit(n->changed_nsids, NVME_CHANGED_NSID_SIZE)) != @@ -4909,7 +5176,7 @@ static uint16_t nvme_cmd_effects(NvmeCtrl *n, uint8_t csi, uint32_t buf_len, uint64_t off, NvmeRequest *req) { NvmeEffectsLog log = {}; - const uint32_t *src_iocs = NULL; + const uint32_t *iocs = NULL; uint32_t trans_len; if (off >= sizeof(log)) { @@ -4919,25 +5186,26 @@ static uint16_t nvme_cmd_effects(NvmeCtrl *n, uint8_t csi, uint32_t buf_len, switch (NVME_CC_CSS(ldl_le_p(&n->bar.cc))) { case NVME_CC_CSS_NVM: - src_iocs = nvme_cse_iocs_nvm; - /* fall through */ - case NVME_CC_CSS_ADMIN_ONLY: + iocs = n->cse.iocs.nvm; break; - case NVME_CC_CSS_CSI: + + case NVME_CC_CSS_ALL: switch (csi) { case NVME_CSI_NVM: - src_iocs = nvme_cse_iocs_nvm; + iocs = n->cse.iocs.nvm; break; case NVME_CSI_ZONED: - src_iocs = nvme_cse_iocs_zoned; + iocs = n->cse.iocs.zoned; break; } + + break; } - memcpy(log.acs, nvme_cse_acs, sizeof(nvme_cse_acs)); + memcpy(log.acs, n->cse.acs, sizeof(log.acs)); - if (src_iocs) { - memcpy(log.iocs, src_iocs, sizeof(log.iocs)); + if (iocs) { + memcpy(log.iocs, iocs, sizeof(log.iocs)); } trans_len = MIN(sizeof(log) - off, buf_len); @@ -4945,6 +5213,23 @@ 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 uint16_t nvme_vendor_specific_log(NvmeCtrl *n, uint8_t rae, + uint32_t buf_len, uint64_t off, + NvmeRequest *req, uint8_t lid) +{ + switch (lid) { + case NVME_OCP_EXTENDED_SMART_INFO: + if (n->params.ocp) { + return nvme_ocp_extended_smart_info(n, rae, buf_len, off, req); + } + break; + /* add a case for each additional vendor specific log id */ + } + + trace_pci_nvme_err_invalid_log_page(nvme_cid(req), lid); + return NVME_INVALID_FIELD | NVME_DNR; +} + static size_t sizeof_fdp_conf_descr(size_t nruh, size_t vss) { size_t entry_siz = sizeof(NvmeFdpDescrHdr) + nruh * sizeof(NvmeRuhDescr) @@ -5195,6 +5480,8 @@ static uint16_t nvme_get_log(NvmeCtrl *n, NvmeRequest *req) return nvme_smart_info(n, rae, len, off, req); case NVME_LOG_FW_SLOT_INFO: return nvme_fw_log_info(n, len, off, req); + case NVME_LOG_VENDOR_START...NVME_LOG_VENDOR_END: + return nvme_vendor_specific_log(n, rae, len, off, req, lid); case NVME_LOG_CHANGED_NSLIST: return nvme_changed_nslist(n, rae, len, off, req); case NVME_LOG_CMD_EFFECTS: @@ -5228,7 +5515,7 @@ static void nvme_free_cq(NvmeCQueue *cq, NvmeCtrl *n) event_notifier_set_handler(&cq->notifier, NULL); event_notifier_cleanup(&cq->notifier); } - if (msix_enabled(pci)) { + if (msix_enabled(pci) && cq->irq_enabled) { msix_vector_unuse(pci, cq->vector); } if (cq->cqid) { @@ -5269,9 +5556,10 @@ static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n, uint64_t dma_addr, { PCIDevice *pci = PCI_DEVICE(n); - if (msix_enabled(pci)) { + if (msix_enabled(pci) && irq_enabled) { msix_vector_use(pci, vector); } + cq->ctrl = n; cq->cqid = cqid; cq->size = size; @@ -5381,7 +5669,9 @@ static uint16_t nvme_identify_ctrl_csi(NvmeCtrl *n, NvmeRequest *req) switch (c->csi) { case NVME_CSI_NVM: id_nvm->vsl = n->params.vsl; + id_nvm->dmrl = NVME_ID_CTRL_NVM_DMRL_MAX; id_nvm->dmrsl = cpu_to_le32(n->dmrsl); + id_nvm->dmsl = NVME_ID_CTRL_NVM_DMRL_MAX * n->dmrsl; break; case NVME_CSI_ZONED: @@ -5423,7 +5713,7 @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeRequest *req, bool active) return nvme_c2h(n, (uint8_t *)&ns->id_ns, sizeof(NvmeIdNs), req); } - return NVME_INVALID_CMD_SET | NVME_DNR; + return NVME_INVALID_IOCS | NVME_DNR; } static uint16_t nvme_identify_ctrl_list(NvmeCtrl *n, NvmeRequest *req, @@ -5486,14 +5776,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; } @@ -5504,6 +5794,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) { @@ -5635,6 +5952,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; @@ -5646,6 +5983,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; @@ -5674,6 +6015,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; @@ -5726,6 +6075,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: @@ -5738,6 +6091,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: @@ -5753,12 +6108,41 @@ 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) { + r->status = NVME_CMD_ABORT_REQ; + blk_aio_cancel_async(r->aiocb); + } + break; + } + } + return NVME_SUCCESS; } @@ -5888,7 +6272,7 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeRequest *req) 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; @@ -6063,8 +6447,10 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeRequest *req) if (ret) { return ret; } - goto out; + break; + case NVME_WRITE_ATOMICITY: + result = n->dn; break; default: result = nvme_feature_default[fid]; @@ -6148,6 +6534,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); @@ -6292,7 +6680,7 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req) case NVME_COMMAND_SET_PROFILE: if (dw11 & 0x1ff) { trace_pci_nvme_err_invalid_iocsci(dw11 & 0x1ff); - return NVME_CMD_SET_CMB_REJECTED | NVME_DNR; + return NVME_IOCS_COMBINATION_REJECTED | NVME_DNR; } break; case NVME_FDP_MODE: @@ -6300,6 +6688,22 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req) 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; } @@ -6325,40 +6729,49 @@ static uint16_t nvme_aer(NvmeCtrl *n, NvmeRequest *req) return NVME_NO_COMPLETE; } -static void nvme_update_dmrsl(NvmeCtrl *n) +static void nvme_update_dsm_limits(NvmeCtrl *n, NvmeNamespace *ns) { - int nsid; + if (ns) { + n->dmrsl = + MIN_NON_ZERO(n->dmrsl, BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); - for (nsid = 1; nsid <= NVME_MAX_NAMESPACES; nsid++) { - NvmeNamespace *ns = nvme_ns(n, nsid); + return; + } + + for (uint32_t nsid = 1; nsid <= NVME_MAX_NAMESPACES; nsid++) { + ns = nvme_ns(n, nsid); if (!ns) { continue; } - n->dmrsl = MIN_NON_ZERO(n->dmrsl, - BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); + n->dmrsl = + MIN_NON_ZERO(n->dmrsl, BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); } } -static void nvme_select_iocs_ns(NvmeCtrl *n, NvmeNamespace *ns) +static bool nvme_csi_supported(NvmeCtrl *n, uint8_t csi) { - uint32_t cc = ldl_le_p(&n->bar.cc); + uint32_t cc; - ns->iocs = nvme_cse_iocs_none; - switch (ns->csi) { + switch (csi) { case NVME_CSI_NVM: - if (NVME_CC_CSS(cc) != NVME_CC_CSS_ADMIN_ONLY) { - ns->iocs = nvme_cse_iocs_nvm; - } - break; + return true; + case NVME_CSI_ZONED: - if (NVME_CC_CSS(cc) == NVME_CC_CSS_CSI) { - ns->iocs = nvme_cse_iocs_zoned; - } else if (NVME_CC_CSS(cc) == NVME_CC_CSS_NVM) { - ns->iocs = nvme_cse_iocs_nvm; - } - break; + cc = ldl_le_p(&n->bar.cc); + + return NVME_CC_CSS(cc) == NVME_CC_CSS_ALL; } + + g_assert_not_reached(); +} + +static void nvme_detach_ns(NvmeCtrl *n, NvmeNamespace *ns) +{ + assert(ns->attached > 0); + + n->namespaces[ns->params.nsid] = NULL; + ns->attached--; } static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) @@ -6403,7 +6816,7 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) switch (sel) { case NVME_NS_ATTACHMENT_ATTACH: - if (nvme_ns(ctrl, nsid)) { + if (nvme_ns(n, nsid)) { return NVME_NS_ALREADY_ATTACHED | NVME_DNR; } @@ -6411,20 +6824,18 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) return NVME_NS_PRIVATE | NVME_DNR; } + if (!nvme_csi_supported(n, ns->csi)) { + return NVME_IOCS_NOT_SUPPORTED | NVME_DNR; + } + nvme_attach_ns(ctrl, ns); - nvme_select_iocs_ns(ctrl, ns); + nvme_update_dsm_limits(ctrl, ns); break; case NVME_NS_ATTACHMENT_DETACH: - if (!nvme_ns(ctrl, nsid)) { - return NVME_NS_NOT_ATTACHED | NVME_DNR; - } - - ctrl->namespaces[nsid] = NULL; - ns->attached--; - - nvme_update_dmrsl(ctrl); + nvme_detach_ns(ctrl, ns); + nvme_update_dsm_limits(ctrl, NULL); break; @@ -6478,7 +6889,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, @@ -6928,7 +7338,7 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_admin_cmd(nvme_cid(req), nvme_sqid(req), req->cmd.opcode, nvme_adm_opc_str(req->cmd.opcode)); - if (!(nvme_cse_acs[req->cmd.opcode] & NVME_CMD_EFF_CSUPP)) { + if (!(n->cse.acs[req->cmd.opcode] & NVME_CMD_EFF_CSUPP)) { trace_pci_nvme_err_invalid_admin_opc(req->cmd.opcode); return NVME_INVALID_OPCODE | NVME_DNR; } @@ -6976,7 +7386,7 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *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; @@ -6998,6 +7408,81 @@ static void nvme_update_sq_tail(NvmeSQueue *sq) trace_pci_nvme_update_sq_tail(sq->sqid, sq->tail); } +#define NVME_ATOMIC_NO_START 0 +#define NVME_ATOMIC_START_ATOMIC 1 +#define NVME_ATOMIC_START_NONATOMIC 2 + +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) { NvmeSQueue *sq = opaque; @@ -7014,6 +7499,9 @@ static void nvme_process_sq(void *opaque) } while (!(nvme_sq_empty(sq) || QTAILQ_EMPTY(&sq->req_list))) { + 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); @@ -7021,6 +7509,26 @@ static void nvme_process_sq(void *opaque) 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); @@ -7030,6 +7538,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) { @@ -7117,14 +7629,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) { @@ -7137,6 +7645,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)) { @@ -7175,21 +7685,6 @@ static void nvme_ctrl_shutdown(NvmeCtrl *n) } } -static void nvme_select_iocs(NvmeCtrl *n) -{ - NvmeNamespace *ns; - int i; - - for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { - ns = nvme_ns(n, i); - if (!ns) { - continue; - } - - nvme_select_iocs_ns(n, ns); - } -} - static int nvme_start_ctrl(NvmeCtrl *n) { uint64_t cap = ldq_le_p(&n->bar.cap); @@ -7256,7 +7751,22 @@ static int nvme_start_ctrl(NvmeCtrl *n) nvme_set_timestamp(n, 0ULL); - nvme_select_iocs(n); + /* verify that the command sets of attached namespaces are supported */ + for (int i = 1; i <= NVME_MAX_NAMESPACES; i++) { + NvmeNamespace *ns = nvme_subsys_ns(n->subsys, i); + + if (!ns || (!ns->params.shared && ns->ctrl != n)) { + continue; + } + + if (nvme_csi_supported(n, ns->csi) && !ns->params.detached) { + if (!ns->attached || ns->params.shared) { + nvme_attach_ns(n, ns); + } + } + } + + nvme_update_dsm_limits(n, NULL); return 0; } @@ -7581,7 +8091,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; @@ -7594,7 +8103,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." @@ -7632,18 +8141,15 @@ 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) { stl_le_pci_dma(pci, cq->db_addr, cq->head, MEMTXATTRS_UNSPECIFIED); } - if (start_sqs) { - NvmeSQueue *sq; - QTAILQ_FOREACH(sq, &cq->sq_list, entry) { - qemu_bh_schedule(sq->bh); - } - qemu_bh_schedule(cq->bh); - } if (cq->tail == cq->head) { if (cq->irq_enabled) { @@ -7804,7 +8310,17 @@ static bool nvme_check_params(NvmeCtrl *n, Error **errp) 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))); @@ -7836,12 +8352,6 @@ static bool nvme_check_params(NvmeCtrl *n, Error **errp) return false; } - if (params->sriov_max_vfs > NVME_MAX_VFS) { - error_setg(errp, "sriov_max_vfs must be between 0 and %d", - NVME_MAX_VFS); - return false; - } - if (params->cmb_size_mb) { error_setg(errp, "CMB is not supported with SR-IOV"); return false; @@ -7906,9 +8416,11 @@ static bool nvme_check_params(NvmeCtrl *n, Error **errp) 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; @@ -7931,9 +8443,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); } @@ -7966,6 +8478,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) @@ -8010,13 +8545,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) { @@ -8031,28 +8571,32 @@ 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) +static bool nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset, + Error **errp) { 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); - pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id, - n->params.sriov_max_vfs, n->params.sriov_max_vfs, - NVME_VF_OFFSET, NVME_VF_STRIDE); + if (!pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id, + n->params.sriov_max_vfs, n->params.sriov_max_vfs, + NVME_VF_OFFSET, NVME_VF_STRIDE, errp)) { + return false; + } pcie_sriov_pf_init_vf_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, bar_size); + + return true; } static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset) @@ -8060,8 +8604,7 @@ static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset) Error *err = NULL; int ret; - ret = pci_add_capability(pci_dev, PCI_CAP_ID_PM, offset, - PCI_PM_SIZEOF, &err); + ret = pci_pm_init(pci_dev, offset, &err); if (err) { error_report_err(err); return ret; @@ -8077,15 +8620,37 @@ static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset) return 0; } +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; - 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) { @@ -8104,24 +8669,48 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) 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); + if (n->params.msix_exclusive_bar && !pci_is_vf(pci_dev)) { + bar_size = nvme_mbar_size(n->params.max_ioqpairs + 1, 0, NULL, NULL); + memory_region_init_io(&n->iomem, OBJECT(n), &nvme_mmio_ops, n, "nvme", + bar_size); + pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, &n->iomem); + ret = msix_init_exclusive_bar(pci_dev, n->params.msix_qsize, 4, errp); + } else { + assert(n->params.msix_qsize >= 1); - 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); + /* 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 { + NvmeCtrl *pn = NVME(pcie_sriov_get_pf(pci_dev)); + NvmePriCtrlCap *cap = &pn->pri_ctrl_cap; - 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); + 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); } - ret = msix_init(pci_dev, n->params.msix_qsize, - &n->bar0, 0, msix_table_offset, - &n->bar0, 0, msix_pba_offset, 0, errp); + if (ret == -ENOTSUP) { /* report that msix is not supported, but do not error out */ warn_report_err(*errp); @@ -8131,8 +8720,32 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) return false; } + if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs && + !nvme_init_sriov(n, pci_dev, 0x120, errp)) { + 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); } @@ -8141,10 +8754,6 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) nvme_init_pmr(n, pci_dev); } - if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs) { - nvme_init_sriov(n, pci_dev, 0x120); - } - return true; } @@ -8168,6 +8777,12 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) uint64_t cap = ldq_le_p(&n->bar.cap); NvmeSecCtrlEntry *sctrl = nvme_sctrl(n); uint32_t ctratt; + uint16_t oacs; + + memcpy(n->cse.acs, nvme_cse_acs_default, sizeof(n->cse.acs)); + memcpy(n->cse.iocs.nvm, nvme_cse_iocs_nvm_default, sizeof(n->cse.iocs.nvm)); + memcpy(n->cse.iocs.zoned, nvme_cse_iocs_zoned_default, + sizeof(n->cse.iocs.zoned)); 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)); @@ -8178,7 +8793,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); + ctratt = NVME_CTRATT_ELBAS; + if (n->params.ctratt.mem) { + ctratt |= NVME_CTRATT_MEM; + } id->rab = 6; @@ -8194,9 +8813,23 @@ 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 | - NVME_OACS_DIRECTIVES); + + oacs = NVME_OACS_NMS | NVME_OACS_FORMAT | NVME_OACS_DIRECTIVES; + + if (n->params.dbcs) { + oacs |= NVME_OACS_DBCS; + + n->cse.acs[NVME_ADM_CMD_DBBUF_CONFIG] = NVME_CMD_EFF_CSUPP; + } + + if (n->params.sriov_max_vfs) { + oacs |= NVME_OACS_VMS; + + n->cse.acs[NVME_ADM_CMD_VIRT_MNGMT] = NVME_CMD_EFF_CSUPP; + } + + id->oacs = cpu_to_le16(oacs); + id->cntrltype = 0x1; /* @@ -8224,7 +8857,8 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) 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 @@ -8235,8 +8869,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); @@ -8244,25 +8880,22 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->psd[0].enlat = cpu_to_le32(0x10); id->psd[0].exlat = cpu_to_le32(0x4); - if (n->subsys) { - id->cmic |= NVME_CMIC_MULTI_CTRL; - ctratt |= NVME_CTRATT_ENDGRPS; + id->cmic |= NVME_CMIC_MULTI_CTRL; + ctratt |= NVME_CTRATT_ENDGRPS; - id->endgidmax = cpu_to_le16(0x1); + id->endgidmax = cpu_to_le16(0x1); - if (n->subsys->endgrp.fdp.enabled) { - ctratt |= NVME_CTRATT_FDPS; - } + if (n->subsys->endgrp.fdp.enabled) { + ctratt |= NVME_CTRATT_FDPS; } id->ctratt = cpu_to_le32(ctratt); - NVME_CAP_SET_MQES(cap, 0x7ff); + NVME_CAP_SET_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); - NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_CSI_SUPP); - NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_ADMIN_ONLY); + NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_NCSS); + NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_IOCSS); NVME_CAP_SET_MPSMAX(cap, 4); NVME_CAP_SET_CMBS(cap, n->params.cmb_size_mb ? 1 : 0); NVME_CAP_SET_PMRS(cap, n->pmr.dev ? 1 : 0); @@ -8281,7 +8914,15 @@ static int nvme_init_subsys(NvmeCtrl *n, Error **errp) int cntlid; if (!n->subsys) { - return 0; + DeviceState *dev = qdev_new(TYPE_NVME_SUBSYS); + + qdev_prop_set_string(dev, "nqn", n->params.serial); + + if (!qdev_realize(dev, NULL, errp)) { + return -1; + } + + n->subsys = NVME_SUBSYS(dev); } cntlid = nvme_subsys_register_ctrl(n, errp); @@ -8301,9 +8942,6 @@ void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns) n->namespaces[nsid] = ns; ns->attached++; - - n->dmrsl = MIN_NON_ZERO(n->dmrsl, - BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); } static void nvme_realize(PCIDevice *pci_dev, Error **errp) @@ -8316,10 +8954,23 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) 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)); } if (!nvme_check_params(n, errp)) { @@ -8341,12 +8992,13 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) if (n->namespace.blkconf.blk) { ns = &n->namespace; ns->params.nsid = 1; + ns->ctrl = n; if (nvme_ns_setup(ns, errp)) { return; } - nvme_attach_ns(n, ns); + n->subsys->namespaces[ns->params.nsid] = ns; } } @@ -8358,17 +9010,15 @@ static void nvme_exit(PCIDevice *pci_dev) nvme_ctrl_reset(n, NVME_RESET_FUNCTION); - if (n->subsys) { - for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { - ns = nvme_ns(n, i); - if (ns) { - ns->attached--; - } + for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { + ns = nvme_ns(n, i); + if (ns) { + ns->attached--; } - - nvme_subsys_unregister_ctrl(n->subsys, n); } + nvme_subsys_unregister_ctrl(n->subsys, n); + g_free(n->cq); g_free(n->sq); g_free(n->aer_reqs); @@ -8377,6 +9027,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); } @@ -8385,11 +9040,16 @@ 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); } -static Property nvme_props[] = { +static const Property nvme_props[] = { DEFINE_BLOCK_PROPERTIES(NvmeCtrl, namespace.blkconf), DEFINE_PROP_LINK("pmrdev", NvmeCtrl, pmr.dev, TYPE_MEMORY_BACKEND, HostMemoryBackend *), @@ -8407,19 +9067,28 @@ static Property nvme_props[] = { DEFINE_PROP_BOOL("use-intel-id", NvmeCtrl, params.use_intel_id, false), DEFINE_PROP_BOOL("legacy-cmb", NvmeCtrl, params.legacy_cmb, false), DEFINE_PROP_BOOL("ioeventfd", NvmeCtrl, params.ioeventfd, false), + DEFINE_PROP_BOOL("dbcs", NvmeCtrl, params.dbcs, true), 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_END_OF_LIST(), + 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_BOOL("ocp", NvmeCtrl, params.ocp, false), }; static void nvme_get_smart_warning(Object *obj, Visitor *v, const char *name, @@ -8473,36 +9142,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 = { @@ -8510,13 +9183,14 @@ static const VMStateDescription nvme_vmstate = { .unmigratable = 1, }; -static void nvme_class_init(ObjectClass *oc, void *data) +static void nvme_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); 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; @@ -8525,7 +9199,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) @@ -8547,7 +9221,7 @@ static const TypeInfo nvme_info = { .instance_size = sizeof(NvmeCtrl), .instance_init = nvme_instance_init, .class_init = nvme_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/nvme/dif.c b/hw/nvme/dif.c index 01b19c33734..4e7874f3223 100644 --- a/hw/nvme/dif.c +++ b/hw/nvme/dif.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "nvme.h" #include "dif.h" @@ -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 1a6a2ca2f30..7d5caa53c28 100644 --- a/hw/nvme/meson.build +++ b/hw/nvme/meson.build @@ -1 +1 @@ -system_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 00000000000..4cd6fad6ac9 --- /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) +{ + const 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) +{ + const 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 = { + .type = "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 44aba8f4d9c..6df2e8e7c5a 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -18,8 +18,8 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "qemu/bitops.h" -#include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" +#include "system/system.h" +#include "system/block-backend.h" #include "nvme.h" #include "trace.h" @@ -30,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; @@ -55,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) @@ -62,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; @@ -72,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 */ @@ -89,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; @@ -107,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 }, @@ -120,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]; @@ -720,25 +727,14 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) uint32_t nsid = ns->params.nsid; int i; - if (!n->subsys) { - /* If no subsys, the ns cannot be attached to more than one ctrl. */ - ns->params.shared = false; - if (ns->params.detached) { - error_setg(errp, "detached requires that the nvme device is " - "linked to an nvme-subsys device"); - return; - } - } else { - /* - * If this namespace belongs to a subsystem (through a link on the - * controller device), reparent the device. - */ - if (!qdev_set_parent_bus(dev, &subsys->bus.parent_bus, errp)) { - return; - } - ns->subsys = subsys; - ns->endgrp = &subsys->endgrp; + assert(subsys); + + /* reparent to subsystem bus */ + 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)) { return; @@ -746,7 +742,7 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) if (!nsid) { for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { - if (nvme_ns(n, i) || nvme_subsys_ns(subsys, i)) { + if (nvme_subsys_ns(subsys, i)) { continue; } @@ -758,45 +754,28 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) error_setg(errp, "no free namespace id"); return; } - } else { - if (nvme_ns(n, nsid) || nvme_subsys_ns(subsys, nsid)) { - error_setg(errp, "namespace id '%d' already allocated", nsid); - return; - } + } else if (nvme_subsys_ns(subsys, nsid)) { + error_setg(errp, "namespace id '%d' already allocated", nsid); + return; } - if (subsys) { - subsys->namespaces[nsid] = ns; + subsys->namespaces[nsid] = ns; - ns->id_ns.endgid = cpu_to_le16(0x1); - - if (ns->params.detached) { - return; - } - - if (ns->params.shared) { - for (i = 0; i < ARRAY_SIZE(subsys->ctrls); i++) { - NvmeCtrl *ctrl = subsys->ctrls[i]; - - if (ctrl && ctrl != SUBSYS_SLOT_RSVD) { - nvme_attach_ns(ctrl, ns); - } - } - - return; - } + ns->id_ns.endgid = cpu_to_le16(0x1); + ns->id_ns_ind.endgrpid = cpu_to_le16(0x1); + if (!ns->params.shared) { + ns->ctrl = n; } - - nvme_attach_ns(n, ns); } -static Property nvme_ns_props[] = { +static const Property nvme_ns_props[] = { DEFINE_BLOCK_PROPERTIES(NvmeNamespace, blkconf), DEFINE_PROP_BOOL("detached", NvmeNamespace, params.detached, false), 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), @@ -825,10 +804,9 @@ static Property nvme_ns_props[] = { DEFINE_PROP_BOOL("eui64-default", NvmeNamespace, params.eui64_default, false), DEFINE_PROP_STRING("fdp.ruhs", NvmeNamespace, params.fdp.ruhs), - DEFINE_PROP_END_OF_LIST(), }; -static void nvme_ns_class_init(ObjectClass *oc, void *data) +static void nvme_ns_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index 5f2ae7b28b9..b5c9378ea4e 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -171,13 +171,27 @@ static const uint8_t nvme_fdp_evf_shifts[FDP_EVT_MAX] = { [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; @@ -206,6 +220,11 @@ typedef struct NvmeNamespaceParams { } fdp; } NvmeNamespaceParams; +typedef struct NvmeAtomic { + uint32_t atomic_max_write_size; + bool atomic_writes; +} NvmeAtomic; + typedef struct NvmeNamespace { DeviceState parent_obj; BlockConf blkconf; @@ -214,10 +233,10 @@ 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; - const uint32_t *iocs; uint8_t csi; uint16_t status; int attached; @@ -249,6 +268,9 @@ typedef struct NvmeNamespace { NvmeSubsystem *subsys; NvmeEnduranceGroup *endgrp; + /* NULL for shared namespaces; set to specific controller if private */ + NvmeCtrl *ctrl; + struct { uint32_t err_rec; } features; @@ -407,6 +429,7 @@ typedef struct NvmeRequest { NvmeCmd cmd; BlockAcctCookie acct; NvmeSg sg; + bool atomic_write; QTAILQ_ENTRY(NvmeRequest)entry; } NvmeRequest; @@ -507,6 +530,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; @@ -517,11 +541,22 @@ typedef struct NvmeParams { bool auto_transition_zones; bool legacy_cmb; bool ioeventfd; - uint8_t sriov_max_vfs; + bool dbcs; + 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; + bool ocp; + + struct { + bool mem; + } ctratt; + + uint16_t atomic_awun; + uint16_t atomic_awupf; + bool atomic_dn; } NvmeParams; typedef struct NvmeCtrl { @@ -552,6 +587,14 @@ typedef struct NvmeCtrl { uint64_t dbbuf_eis; bool dbbuf_enabled; + struct { + uint32_t acs[256]; + struct { + uint32_t nvm[256]; + uint32_t zoned[256]; + } iocs; + } cse; + struct { MemoryRegion mem; uint8_t *buf; @@ -597,11 +640,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 { @@ -647,7 +693,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; @@ -656,12 +702,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 d30bb8bfd5b..777e1c620fd 100644 --- a/hw/nvme/subsys.c +++ b/hw/nvme/subsys.c @@ -17,13 +17,13 @@ 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++; @@ -36,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) { @@ -56,11 +56,13 @@ int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp) { NvmeSubsystem *subsys = n->subsys; NvmeSecCtrlEntry *sctrl = nvme_sctrl(n); - int cntlid, nsid, num_rsvd, num_vfs = n->params.sriov_max_vfs; + int cntlid, num_rsvd, num_vfs = n->params.sriov_max_vfs; 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; @@ -90,13 +92,6 @@ int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp) subsys->ctrls[cntlid] = n; - for (nsid = 1; nsid < ARRAY_SIZE(subsys->namespaces); nsid++) { - NvmeNamespace *ns = subsys->namespaces[nsid]; - if (ns && ns->params.shared && !ns->params.detached) { - nvme_attach_ns(n, ns); - } - } - return cntlid; } @@ -214,17 +209,16 @@ static void nvme_subsys_realize(DeviceState *dev, Error **errp) nvme_subsys_setup(subsys, errp); } -static Property nvme_subsystem_props[] = { +static const 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(), }; -static void nvme_subsys_class_init(ObjectClass *oc, void *data) +static void nvme_subsys_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -232,7 +226,6 @@ static void nvme_subsys_class_init(ObjectClass *oc, void *data) dc->realize = nvme_subsys_realize; dc->desc = "Virtual NVMe subsystem"; - dc->hotpluggable = false; device_class_set_props(dc, nvme_subsystem_props); } diff --git a/hw/nvme/trace-events b/hw/nvme/trace-events index 3a67680c6ad..6be0bfa1c1f 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"" diff --git a/hw/nvram/bcm2835_otp.c b/hw/nvram/bcm2835_otp.c new file mode 100644 index 00000000000..6816b534178 --- /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, const 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/chrp_nvram.c b/hw/nvram/chrp_nvram.c index d4d10a7c03c..0b204e36c62 100644 --- a/hw/nvram/chrp_nvram.c +++ b/hw/nvram/chrp_nvram.c @@ -23,7 +23,7 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "hw/nvram/chrp_nvram.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static int chrp_nvram_set_var(uint8_t *nvram, int addr, const char *str, int max_len) diff --git a/hw/nvram/ds1225y.c b/hw/nvram/ds1225y.c index 3660a47c51c..dbfd0d2e536 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() @@ -142,13 +142,12 @@ static void nvram_sysbus_realize(DeviceState *dev, Error **errp) nvram_post_load(s, 0); } -static Property nvram_sysbus_properties[] = { +static const Property nvram_sysbus_properties[] = { DEFINE_PROP_UINT32("size", SysBusNvRamState, nvram.chip_size, 0x2000), DEFINE_PROP_STRING("filename", SysBusNvRamState, nvram.filename), - DEFINE_PROP_END_OF_LIST(), }; -static void nvram_sysbus_class_init(ObjectClass *klass, void *data) +static void nvram_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/nvram/eeprom93xx.c b/hw/nvram/eeprom93xx.c index 1081e2cc0d2..a8fd60a8fb5 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 613c4929e32..82ea97e552a 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -10,12 +10,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.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" +#include "system/block-backend.h" #include "qom/object.h" /* #define DEBUG_AT24C */ @@ -26,13 +27,8 @@ #define DPRINTK(FMT, ...) do {} while (0) #endif -#define ERR(FMT, ...) fprintf(stderr, TYPE_AT24C_EE " : " FMT, \ - ## __VA_ARGS__) - #define TYPE_AT24C_EE "at24c-eeprom" -typedef struct EEPROMState EEPROMState; -DECLARE_INSTANCE_CHECKER(EEPROMState, AT24C_EE, - TYPE_AT24C_EE) +OBJECT_DECLARE_SIMPLE_TYPE(EEPROMState, AT24C_EE) struct EEPROMState { I2CSlave parent_obj; @@ -51,7 +47,7 @@ struct EEPROMState { 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; @@ -77,8 +73,7 @@ int at24c_eeprom_event(I2CSlave *s, enum i2c_event event) if (ee->blk && ee->changed) { int ret = blk_pwrite(ee->blk, 0, ee->rsize, ee->mem, 0); if (ret < 0) { - ERR(TYPE_AT24C_EE - " : failed to write backing file\n"); + error_report("%s: failed to write backing file", __func__); } DPRINTK("Wrote to backing file\n"); } @@ -195,20 +190,18 @@ static void at24c_eeprom_realize(DeviceState *dev, Error **errp) } ee->mem = g_malloc0(ee->rsize); - memset(ee->mem, 0, ee->rsize); - - if (ee->init_rom) { - memcpy(ee->mem, ee->init_rom, MIN(ee->init_rom_size, ee->rsize)); - } if (ee->blk) { int ret = blk_pread(ee->blk, 0, ee->rsize, ee->mem, 0); if (ret < 0) { - ERR(TYPE_AT24C_EE - " : Failed initial sync with backing file\n"); + error_setg(errp, "%s: Failed initial sync with backing file", + TYPE_AT24C_EE); + return; } DPRINTK("Reset read backing file\n"); + } else if (ee->init_rom) { + memcpy(ee->mem, ee->init_rom, MIN(ee->init_rom_size, ee->rsize)); } /* @@ -234,16 +227,15 @@ void at24c_eeprom_reset(DeviceState *state) ee->haveaddr = 0; } -static Property at24c_eeprom_props[] = { +static const 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() }; static -void at24c_eeprom_class_init(ObjectClass *klass, void *data) +void at24c_eeprom_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); @@ -254,7 +246,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 00000000000..2e6ef89b989 --- /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 29a5bef1d56..aa240504935 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -24,9 +24,10 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" -#include "sysemu/sysemu.h" -#include "sysemu/dma.h" -#include "sysemu/reset.h" +#include "system/system.h" +#include "system/dma.h" +#include "system/reset.h" +#include "system/address-spaces.h" #include "hw/boards.h" #include "hw/nvram/fw_cfg.h" #include "hw/qdev-properties.h" @@ -40,7 +41,6 @@ #include "qemu/cutils.h" #include "qapi/error.h" #include "hw/acpi/aml-build.h" -#include "hw/pci/pci_bus.h" #include "hw/loader.h" #define FW_CFG_FILE_SLOTS_DFLT 0x20 @@ -656,7 +656,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 +668,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 +680,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, @@ -729,7 +729,6 @@ static void *fw_cfg_modify_bytes_read(FWCfgState *s, uint16_t key, ptr = s->entries[arch][key].data; s->entries[arch][key].data = data; s->entries[arch][key].len = len; - s->entries[arch][key].callback_opaque = NULL; s->entries[arch][key].allow_write = false; return ptr; @@ -818,66 +817,10 @@ void fw_cfg_modify_i64(FWCfgState *s, uint16_t key, uint64_t value) g_free(old); } -void fw_cfg_set_order_override(FWCfgState *s, int order) -{ - assert(s->fw_cfg_order_override == 0); - s->fw_cfg_order_override = order; -} - -void fw_cfg_reset_order_override(FWCfgState *s) -{ - assert(s->fw_cfg_order_override != 0); - s->fw_cfg_order_override = 0; -} - -/* - * This is the legacy order list. For legacy systems, files are in - * the fw_cfg in the order defined below, by the "order" value. Note - * that some entries (VGA ROMs, NIC option ROMS, etc.) go into a - * specific area, but there may be more than one and they occur in the - * order that the user specifies them on the command line. Those are - * handled in a special manner, using the order override above. - * - * For non-legacy, the files are sorted by filename to avoid this kind - * of complexity in the future. - * - * This is only for x86, other arches don't implement versioning so - * they won't set legacy mode. - */ -static struct { - const char *name; - int order; -} fw_cfg_order[] = { - { "etc/boot-menu-wait", 10 }, - { "bootsplash.jpg", 11 }, - { "bootsplash.bmp", 12 }, - { "etc/boot-fail-wait", 15 }, - { "etc/smbios/smbios-tables", 20 }, - { "etc/smbios/smbios-anchor", 30 }, - { "etc/e820", 40 }, - { "etc/reserved-memory-end", 50 }, - { "genroms/kvmvapic.bin", 55 }, - { "genroms/linuxboot.bin", 60 }, - { }, /* VGA ROMs from pc_vga_init come here, 70. */ - { }, /* NIC option ROMs from pc_nic_init come here, 80. */ - { "etc/system-states", 90 }, - { }, /* User ROMs come here, 100. */ - { }, /* Device FW comes here, 110. */ - { "etc/extra-pci-roots", 120 }, - { "etc/acpi/tables", 130 }, - { "etc/table-loader", 140 }, - { "etc/tpm/log", 150 }, - { "etc/acpi/rsdp", 160 }, - { "bootorder", 170 }, - { "etc/msr_feature_control", 180 }, - -#define FW_CFG_ORDER_OVERRIDE_LAST 200 -}; - /* * Any sub-page size update to these table MRs will be lost during migration, * as we use aligned size in ram_load_precopy() -> qemu_ram_resize() path. - * In order to avoid the inconsistency in sizes save them seperately and + * 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) @@ -891,29 +834,6 @@ static void fw_cfg_acpi_mr_save(FWCfgState *s, const char *filename, size_t len) } } -static int get_fw_cfg_order(FWCfgState *s, const char *name) -{ - int i; - - if (s->fw_cfg_order_override > 0) { - return s->fw_cfg_order_override; - } - - for (i = 0; i < ARRAY_SIZE(fw_cfg_order); i++) { - if (fw_cfg_order[i].name == NULL) { - continue; - } - - if (strcmp(name, fw_cfg_order[i].name) == 0) { - return fw_cfg_order[i].order; - } - } - - /* Stick unknown stuff at the end. */ - warn_report("Unknown firmware file in legacy mode: %s", name); - return FW_CFG_ORDER_OVERRIDE_LAST; -} - void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, FWCfgCallback select_cb, FWCfgWriteCallback write_cb, @@ -922,7 +842,6 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, { int i, index, count; size_t dsize; - MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); int order = 0; if (!s->files) { @@ -934,22 +853,11 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, count = be32_to_cpu(s->files->count); assert(count < fw_cfg_file_slots(s)); - /* Find the insertion point. */ - if (mc->legacy_fw_cfg_order) { - /* - * Sort by order. For files with the same order, we keep them - * in the sequence in which they were added. - */ - order = get_fw_cfg_order(s, filename); - for (index = count; - index > 0 && order < s->entry_order[index - 1]; - index--); - } else { - /* Sort by file name. */ - for (index = count; - index > 0 && strcmp(filename, s->files->f[index - 1].name) < 0; - index--); - } + /* Find the insertion point, sorting by file name. */ + for (index = count; + index > 0 && strcmp(filename, s->files->f[index - 1].name) < 0; + index--) + ; /* * Move all the entries from the index point and after down one @@ -1026,27 +934,29 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, return NULL; } -bool fw_cfg_add_from_generator(FWCfgState *s, const char *filename, - const char *gen_id, Error **errp) +bool fw_cfg_add_file_from_generator(FWCfgState *s, + Object *parent, const char *part, + const char *filename, Error **errp) { + ERRP_GUARD(); FWCfgDataGeneratorClass *klass; GByteArray *array; Object *obj; gsize size; - obj = object_resolve_path_component(object_get_objects_root(), gen_id); + obj = object_resolve_path_component(parent, part); if (!obj) { - error_setg(errp, "Cannot find object ID '%s'", gen_id); + error_setg(errp, "Cannot find object ID '%s'", part); return false; } if (!object_dynamic_cast(obj, TYPE_FW_CFG_DATA_GENERATOR_INTERFACE)) { error_setg(errp, "Object ID '%s' is not a '%s' subclass", - gen_id, TYPE_FW_CFG_DATA_GENERATOR_INTERFACE); + part, TYPE_FW_CFG_DATA_GENERATOR_INTERFACE); return false; } klass = FW_CFG_DATA_GENERATOR_GET_CLASS(obj); array = klass->get_data(obj, errp); - if (!array) { + if (*errp || !array) { return false; } size = array->len; @@ -1055,31 +965,8 @@ bool fw_cfg_add_from_generator(FWCfgState *s, const char *filename, return true; } -void fw_cfg_add_extra_pci_roots(PCIBus *bus, FWCfgState *s) -{ - int extra_hosts = 0; - - if (!bus) { - return; - } - - QLIST_FOREACH(bus, &bus->child, sibling) { - /* look for expander root buses */ - if (pci_bus_is_root(bus)) { - extra_hosts++; - } - } - - if (extra_hosts && s) { - uint64_t *val = g_malloc(sizeof(*val)); - *val = cpu_to_le64(extra_hosts); - fw_cfg_add_file(s, "etc/extra-pci-roots", val, sizeof(*val)); - } -} - static void fw_cfg_machine_reset(void *opaque) { - MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); FWCfgState *s = opaque; void *ptr; size_t len; @@ -1089,11 +976,9 @@ static void fw_cfg_machine_reset(void *opaque) ptr = fw_cfg_modify_file(s, "bootorder", (uint8_t *)buf, len); g_free(ptr); - if (!mc->legacy_fw_cfg_order) { - buf = get_boot_devices_lchs_list(&len); - ptr = fw_cfg_modify_file(s, "bios-geometry", (uint8_t *)buf, len); - g_free(ptr); - } + buf = get_boot_devices_lchs_list(&len); + ptr = fw_cfg_modify_file(s, "bios-geometry", (uint8_t *)buf, len); + g_free(ptr); } static void fw_cfg_machine_ready(struct Notifier *n, void *data) @@ -1102,9 +987,8 @@ static void fw_cfg_machine_ready(struct Notifier *n, void *data) qemu_register_reset(fw_cfg_machine_reset, s); } -static Property fw_cfg_properties[] = { +static const Property fw_cfg_properties[] = { DEFINE_PROP_BOOL("acpi-mr-restore", FWCfgState, acpi_mr_restore, true), - DEFINE_PROP_END_OF_LIST(), }; static void fw_cfg_common_realize(DeviceState *dev, Error **errp) @@ -1142,6 +1026,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 +1040,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 +1048,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) @@ -1254,11 +1134,11 @@ void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key, fw_cfg_add_bytes(fw_cfg, data_key, data, size); } -static void fw_cfg_class_init(ObjectClass *klass, void *data) +static void fw_cfg_class_init(ObjectClass *klass, const 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); @@ -1297,12 +1177,11 @@ static void fw_cfg_file_slots_allocate(FWCfgState *s, Error **errp) s->entry_order = g_new0(int, fw_cfg_max_entry(s)); } -static Property fw_cfg_io_properties[] = { +static const Property fw_cfg_io_properties[] = { DEFINE_PROP_BOOL("dma_enabled", FWCfgIoState, parent_obj.dma_enabled, true), DEFINE_PROP_UINT16("x-file-slots", FWCfgIoState, parent_obj.file_slots, FW_CFG_FILE_SLOTS_DFLT), - DEFINE_PROP_END_OF_LIST(), }; static void fw_cfg_io_realize(DeviceState *dev, Error **errp) @@ -1330,7 +1209,7 @@ static void fw_cfg_io_realize(DeviceState *dev, Error **errp) fw_cfg_common_realize(dev, errp); } -static void fw_cfg_io_class_init(ObjectClass *klass, void *data) +static void fw_cfg_io_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1346,13 +1225,12 @@ static const TypeInfo fw_cfg_io_info = { }; -static Property fw_cfg_mem_properties[] = { +static const Property fw_cfg_mem_properties[] = { DEFINE_PROP_UINT32("data_width", FWCfgMemState, data_width, -1), DEFINE_PROP_BOOL("dma_enabled", FWCfgMemState, parent_obj.dma_enabled, true), DEFINE_PROP_UINT16("x-file-slots", FWCfgMemState, parent_obj.file_slots, FW_CFG_FILE_SLOTS_DFLT), - DEFINE_PROP_END_OF_LIST(), }; static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) @@ -1392,7 +1270,7 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) fw_cfg_common_realize(dev, errp); } -static void fw_cfg_mem_class_init(ObjectClass *klass, void *data) +static void fw_cfg_mem_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c index 810e84f07e4..66526a22914 100644 --- a/hw/nvram/mac_nvram.c +++ b/hw/nvram/mac_nvram.c @@ -29,12 +29,13 @@ #include "hw/nvram/mac_nvram.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" -#include "sysemu/block-backend.h" +#include "system/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 @@ -48,7 +49,10 @@ static void macio_nvram_writeb(void *opaque, hwaddr addr, trace_macio_nvram_write(addr, value); s->data[addr] = value; if (s->blk) { - blk_pwrite(s->blk, addr, 1, &s->data[addr], 0); + 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)); + } } } @@ -79,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() } @@ -130,20 +134,19 @@ static void macio_nvram_unrealizefn(DeviceState *dev) g_free(s->data); } -static Property macio_nvram_properties[] = { +static const 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() }; -static void macio_nvram_class_init(ObjectClass *oc, void *data) +static void macio_nvram_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); 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 988dff6f8e5..10f3639db6a 100644 --- a/hw/nvram/meson.build +++ b/hw/nvram/meson.build @@ -1,9 +1,6 @@ -if have_system or have_tools - # QOM interfaces must be available anytime QOM is used. - qom_ss.add(files('fw_cfg-interface.c')) -endif - +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')) @@ -21,3 +18,4 @@ system_ss.add(when: 'CONFIG_XLNX_EFUSE_ZYNQMP', if_true: files( 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 c61f2fc1aa2..1fb752b20f4 100644 --- a/hw/nvram/npcm7xx_otp.c +++ b/hw/nvram/npcm7xx_otp.c @@ -384,14 +384,14 @@ 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(), }, }; -static void npcm7xx_otp_class_init(ObjectClass *klass, void *data) +static void npcm7xx_otp_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -403,14 +403,14 @@ static void npcm7xx_otp_class_init(ObjectClass *klass, void *data) rc->phases.enter = npcm7xx_otp_enter_reset; } -static void npcm7xx_key_storage_class_init(ObjectClass *klass, void *data) +static void npcm7xx_key_storage_class_init(ObjectClass *klass, const void *data) { NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass); oc->mmio_ops = &npcm7xx_key_storage_ops; } -static void npcm7xx_fuse_array_class_init(ObjectClass *klass, void *data) +static void npcm7xx_fuse_array_class_init(ObjectClass *klass, const void *data) { NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass); diff --git a/hw/nvram/nrf51_nvm.c b/hw/nvram/nrf51_nvm.c index 7f1db8c4239..23cc9fe9b3d 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; } @@ -357,16 +354,15 @@ static void nrf51_nvm_reset(DeviceState *dev) memset(s->uicr_content, 0xFF, sizeof(s->uicr_content)); } -static Property nrf51_nvm_properties[] = { +static const Property nrf51_nvm_properties[] = { DEFINE_PROP_UINT32("flash-size", NRF51NVMState, flash_size, 0x40000), - DEFINE_PROP_END_OF_LIST(), }; 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), @@ -374,14 +370,14 @@ static const VMStateDescription vmstate_nvm = { } }; -static void nrf51_nvm_class_init(ObjectClass *klass, void *data) +static void nrf51_nvm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 2d72f304422..d0ac4e5735f 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -28,10 +28,10 @@ #include "qapi/error.h" #include -#include "sysemu/block-backend.h" -#include "sysemu/device_tree.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/block-backend.h" +#include "system/device_tree.h" +#include "system/system.h" +#include "system/runstate.h" #include "migration/vmstate.h" #include "hw/nvram/chrp_nvram.h" #include "hw/ppc/spapr.h" @@ -245,20 +245,19 @@ 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() }, }; -static Property spapr_nvram_properties[] = { +static const Property spapr_nvram_properties[] = { DEFINE_SPAPR_PROPERTIES(SpaprNvram, sdev), DEFINE_PROP_DRIVE("drive", SpaprNvram, blk), - DEFINE_PROP_END_OF_LIST(), }; -static void spapr_nvram_class_init(ObjectClass *klass, void *data) +static void spapr_nvram_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c index c6b484cc85b..5702bb3f310 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 @@ -28,7 +29,7 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "qapi/error.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "migration/vmstate.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" @@ -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) { @@ -495,7 +502,7 @@ static void bbram_prop_release_drive(Object *obj, const char *name, } static const PropertyInfo bbram_prop_drive = { - .name = "str", + .type = "str", .description = "Node name or ID of a block device to use as BBRAM backend", .realized_set_allowed = true, .get = bbram_prop_get_drive, @@ -507,23 +514,23 @@ 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(), } }; -static Property bbram_ctrl_props[] = { +static const Property bbram_ctrl_props[] = { DEFINE_PROP("drive", XlnxBBRam, blk, bbram_prop_drive, BlockBackend *), DEFINE_PROP_UINT32("crc-zpads", XlnxBBRam, crc_zpads, 1), - DEFINE_PROP_END_OF_LIST(), }; -static void bbram_ctrl_class_init(ObjectClass *klass, void *data) +static void bbram_ctrl_class_init(ObjectClass *klass, const 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 +542,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 655c40b8d1e..4c23f8b9312 100644 --- a/hw/nvram/xlnx-efuse.c +++ b/hw/nvram/xlnx-efuse.c @@ -30,7 +30,7 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "qapi/error.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" @@ -224,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) { @@ -250,7 +257,7 @@ static void efuse_prop_release_drive(Object *obj, const char *name, } static const PropertyInfo efuse_prop_drive = { - .name = "str", + .type = "str", .description = "Node name or ID of a block device to use as eFUSE backend", .realized_set_allowed = true, .get = efuse_prop_get_drive, @@ -258,28 +265,30 @@ static const PropertyInfo efuse_prop_drive = { .release = efuse_prop_release_drive, }; -static Property efuse_properties[] = { +static const Property efuse_properties[] = { DEFINE_PROP("drive", XlnxEFuse, blk, efuse_prop_drive, BlockBackend *), DEFINE_PROP_UINT8("efuse-nr", XlnxEFuse, efuse_nr, 3), DEFINE_PROP_UINT32("efuse-size", XlnxEFuse, efuse_size, 64 * 32), DEFINE_PROP_BOOL("init-factory-tbits", XlnxEFuse, init_tbits, true), DEFINE_PROP_ARRAY("read-only", XlnxEFuse, ro_bits_cnt, ro_bits, qdev_prop_uint32, uint32_t), - DEFINE_PROP_END_OF_LIST(), }; -static void efuse_class_init(ObjectClass *klass, void *data) +static void efuse_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = efuse_realize; device_class_set_props(dc, efuse_properties); + /* Reason: Part of Xilinx SoC */ + dc->user_creatable = false; } 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-cache.c b/hw/nvram/xlnx-versal-efuse-cache.c index eaec64d785e..d4ec96a6264 100644 --- a/hw/nvram/xlnx-versal-efuse-cache.c +++ b/hw/nvram/xlnx-versal-efuse-cache.c @@ -83,15 +83,13 @@ static void efuse_cache_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static Property efuse_cache_props[] = { +static const Property efuse_cache_props[] = { DEFINE_PROP_LINK("efuse", XlnxVersalEFuseCache, efuse, TYPE_XLNX_EFUSE, XlnxEFuse *), - - DEFINE_PROP_END_OF_LIST(), }; -static void efuse_cache_class_init(ObjectClass *klass, void *data) +static void efuse_cache_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/nvram/xlnx-versal-efuse-ctrl.c b/hw/nvram/xlnx-versal-efuse-ctrl.c index b35ba65ab57..90962198008 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 @@ -493,7 +494,6 @@ static void efuse_rd_addr_postw(RegisterInfo *reg, uint64_t val64) ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, RD_DONE, 1); efuse_imr_update_irq(s); - return; } static uint64_t efuse_cache_load_prew(RegisterInfo *reg, uint64_t val64) @@ -657,9 +657,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 +711,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,36 +720,43 @@ 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(), } }; -static Property efuse_ctrl_props[] = { +static const Property efuse_ctrl_props[] = { DEFINE_PROP_LINK("efuse", XlnxVersalEFuseCtrl, efuse, TYPE_XLNX_EFUSE, XlnxEFuse *), DEFINE_PROP_ARRAY("pg0-lock", XlnxVersalEFuseCtrl, extra_pg0_lock_n16, extra_pg0_lock_spec, qdev_prop_uint16, uint16_t), - - DEFINE_PROP_END_OF_LIST(), }; -static void efuse_ctrl_class_init(ObjectClass *klass, void *data) +static void efuse_ctrl_class_init(ObjectClass *klass, const 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 +768,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 228ba0bbfaf..5a218c32e84 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,33 +812,39 @@ 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(), } }; -static Property zynqmp_efuse_props[] = { +static const Property zynqmp_efuse_props[] = { DEFINE_PROP_LINK("efuse", XlnxZynqMPEFuse, efuse, TYPE_XLNX_EFUSE, XlnxEFuse *), - - DEFINE_PROP_END_OF_LIST(), }; -static void zynqmp_efuse_class_init(ObjectClass *klass, void *data) +static void zynqmp_efuse_class_init(ObjectClass *klass, const 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 +857,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 97af258b556..0702f622a55 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 55475aa6d60..c81efe8138a 100644 --- a/hw/openrisc/boot.c +++ b/hw/openrisc/boot.c @@ -9,12 +9,13 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/cpu-defs.h" +#include "exec/target_page.h" #include "elf.h" #include "hw/loader.h" #include "hw/openrisc/boot.h" -#include "sysemu/device_tree.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" +#include "system/device_tree.h" +#include "system/qtest.h" +#include "system/reset.h" #include "qemu/error-report.h" #include @@ -32,7 +33,7 @@ hwaddr openrisc_load_kernel(ram_addr_t ram_size, if (kernel_filename && !qtest_enabled()) { kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &elf_entry, NULL, &high_addr, NULL, 1, + &elf_entry, NULL, &high_addr, NULL, ELFDATA2MSB, EM_OPENRISC, 1, 0); entry = elf_entry; if (kernel_size < 0) { @@ -90,8 +91,8 @@ hwaddr openrisc_load_initrd(void *fdt, const char *filename, return start + size; } -uint32_t openrisc_load_fdt(void *fdt, hwaddr load_start, - uint64_t mem_size) +uint32_t openrisc_load_fdt(MachineState *ms, void *fdt, + hwaddr load_start, uint64_t mem_size) { uint32_t fdt_addr; int ret; @@ -109,7 +110,9 @@ uint32_t openrisc_load_fdt(void *fdt, hwaddr load_start, /* Should only fail if we've built a corrupted tree */ g_assert(ret == 0); /* copy in the device tree */ - qemu_fdt_dumpdtb(fdt, fdtsize); + + /* Save FDT for dumpdtb monitor command */ + ms->fdt = fdt; rom_add_blob_fixed_as("fdt", fdt, fdtsize, fdt_addr, &address_space_memory); diff --git a/hw/openrisc/cputimer.c b/hw/openrisc/cputimer.c index 10163b391b2..6331997d56b 100644 --- a/hw/openrisc/cputimer.c +++ b/hw/openrisc/cputimer.c @@ -22,14 +22,15 @@ #include "cpu.h" #include "migration/vmstate.h" #include "qemu/timer.h" -#include "sysemu/reset.h" +#include "system/reset.h" #define TIMER_PERIOD 50 /* 50 ns period for 20 MHz timer */ /* 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 2dbc6365bb7..82f1f0ef1cc 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 35da123aef4..880c8ebbb8b 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -24,16 +24,16 @@ #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" -#include "exec/address-spaces.h" -#include "sysemu/device_tree.h" -#include "sysemu/sysemu.h" +#include "system/address-spaces.h" +#include "system/device_tree.h" +#include "system/system.h" #include "hw/sysbus.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" +#include "system/qtest.h" +#include "system/reset.h" #include "hw/core/split-irq.h" #include @@ -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,8 +265,8 @@ 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), - DEVICE_NATIVE_ENDIAN); + serial_hd(uart_idx), + DEVICE_BIG_ENDIAN); /* Add device tree node for serial. */ nodename = g_strdup_printf("/serial@%" HWADDR_PRIx, base); @@ -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); } @@ -301,8 +306,6 @@ static void openrisc_sim_init(MachineState *machine) exit(1); } - cpu_openrisc_clock_init(cpus[n]); - qemu_register_reset(main_cpu_reset, cpus[n]); } @@ -313,12 +316,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 +327,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); @@ -340,12 +352,12 @@ static void openrisc_sim_init(MachineState *machine) machine->initrd_filename, load_addr, machine->ram_size); } - boot_info.fdt_addr = openrisc_load_fdt(state->fdt, load_addr, + boot_info.fdt_addr = openrisc_load_fdt(machine, state->fdt, load_addr, machine->ram_size); } } -static void openrisc_sim_machine_init(ObjectClass *oc, void *data) +static void openrisc_sim_machine_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c index f8a68a6a6b1..a98071c9365 100644 --- a/hw/openrisc/virt.c +++ b/hw/openrisc/virt.c @@ -11,10 +11,10 @@ #include "qemu/guest-random.h" #include "qapi/error.h" #include "cpu.h" -#include "exec/address-spaces.h" +#include "system/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" @@ -24,10 +24,10 @@ #include "hw/rtc/goldfish_rtc.h" #include "hw/sysbus.h" #include "hw/virtio/virtio-mmio.h" -#include "sysemu/device_tree.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" +#include "system/device_tree.h" +#include "system/system.h" +#include "system/qtest.h" +#include "system/reset.h" #include @@ -236,7 +236,7 @@ static void openrisc_virt_serial_init(OR1KVirtState *state, hwaddr base, qemu_irq serial_irq = get_per_cpu_irq(cpus, num_cpus, irq_pin); serial_mm_init(get_system_memory(), base, 0, serial_irq, 115200, - serial_hd(0), DEVICE_NATIVE_ENDIAN); + serial_hd(0), DEVICE_BIG_ENDIAN); /* Add device tree node for serial. */ nodename = g_strdup_printf("/serial@%" HWADDR_PRIx, base); @@ -318,7 +318,7 @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base, { int pin, dev; uint32_t irq_map_stride = 0; - uint32_t full_irq_map[GPEX_NUM_IRQS * GPEX_NUM_IRQS * 6] = {}; + uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * 6] = {}; uint32_t *irq_map = full_irq_map; /* @@ -330,11 +330,11 @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base, * 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++) { + for (dev = 0; dev < PCI_NUM_PINS; dev++) { int devfn = dev << 3; - for (pin = 0; pin < GPEX_NUM_IRQS; pin++) { - int irq_nr = irq_base + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS); + for (pin = 0; pin < PCI_NUM_PINS; pin++) { + int irq_nr = irq_base + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS); int i = 0; /* Fill PCI address cells */ @@ -357,7 +357,7 @@ static void create_pcie_irq_map(void *fdt, char *nodename, int irq_base, } qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map, - GPEX_NUM_IRQS * GPEX_NUM_IRQS * + PCI_NUM_PINS * PCI_NUM_PINS * irq_map_stride * sizeof(uint32_t)); qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask", @@ -409,7 +409,7 @@ static void openrisc_virt_pcie_init(OR1KVirtState *state, memory_region_add_subregion(get_system_memory(), pio_base, alias); /* Connect IRQ lines. */ - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { pcie_irq = get_per_cpu_irq(cpus, num_cpus, irq_base + i); sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pcie_irq); @@ -487,8 +487,6 @@ static void openrisc_virt_init(MachineState *machine) exit(1); } - cpu_openrisc_clock_init(cpus[n]); - qemu_register_reset(main_cpu_reset, cpus[n]); } @@ -540,12 +538,12 @@ static void openrisc_virt_init(MachineState *machine) machine->initrd_filename, load_addr, machine->ram_size); } - boot_info.fdt_addr = openrisc_load_fdt(state->fdt, load_addr, + boot_info.fdt_addr = openrisc_load_fdt(machine, state->fdt, load_addr, machine->ram_size); } } -static void openrisc_virt_machine_init(ObjectClass *oc, void *data) +static void openrisc_virt_machine_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/pci-bridge/Kconfig b/hw/pci-bridge/Kconfig index 67077366cc7..449ec986435 100644 --- a/hw/pci-bridge/Kconfig +++ b/hw/pci-bridge/Kconfig @@ -1,3 +1,8 @@ +config PCI_BRIDGE + bool + default y if PCI_DEVICES + depends on PCI + config PCIE_PORT bool default y if PCI_DEVICES diff --git a/hw/pci-bridge/cxl_downstream.c b/hw/pci-bridge/cxl_downstream.c index 54f507318f3..1065245a8b8 100644 --- a/hw/pci-bridge/cxl_downstream.c +++ b/hw/pci-bridge/cxl_downstream.c @@ -13,6 +13,9 @@ #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 { @@ -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,11 +212,19 @@ static void cxl_dsp_exitfn(PCIDevice *d) pci_bridge_exitfn(d); } -static void cxl_dsp_class_init(ObjectClass *oc, void *data) +static const 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), +}; + +static void cxl_dsp_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *k = PCI_DEVICE_CLASS(oc); + 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; @@ -225,7 +233,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 = { @@ -233,7 +241,7 @@ static const TypeInfo cxl_dsp_info = { .instance_size = sizeof(CXLDownstreamPort), .parent = TYPE_PCIE_SLOT, .class_init = cxl_dsp_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CXL_DEVICE }, { } diff --git a/hw/pci-bridge/cxl_root_port.c b/hw/pci-bridge/cxl_root_port.c index 7dfd20aa678..e6a4035d26c 100644 --- a/hw/pci-bridge/cxl_root_port.c +++ b/hw/pci-bridge/cxl_root_port.c @@ -24,6 +24,7 @@ #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" @@ -107,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, @@ -129,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, @@ -175,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); @@ -186,19 +187,19 @@ static void cxl_rp_realize(DeviceState *dev, Error **errp) component_bar); } -static void cxl_rp_reset_hold(Object *obj) +static void cxl_rp_reset_hold(Object *obj, ResetType type) { PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(obj); CXLRootPort *crp = CXL_ROOT_PORT(obj); if (rpc->parent_phases.hold) { - rpc->parent_phases.hold(obj); + rpc->parent_phases.hold(obj, type); } latch_registers(crp); } -static Property gen_rp_props[] = { +static const Property gen_rp_props[] = { DEFINE_PROP_UINT32("bus-reserve", CXLRootPort, res_reserve.bus, -1), DEFINE_PROP_SIZE("io-reserve", CXLRootPort, res_reserve.io, -1), DEFINE_PROP_SIZE("mem-reserve", CXLRootPort, res_reserve.mem_non_pref, -1), @@ -206,7 +207,10 @@ static Property gen_rp_props[] = { -1), DEFINE_PROP_SIZE("pref64-reserve", CXLRootPort, res_reserve.mem_pref_64, -1), - DEFINE_PROP_END_OF_LIST() + 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), }; static void cxl_rp_dvsec_write_config(PCIDevice *dev, uint32_t addr, @@ -258,7 +262,7 @@ static void cxl_rp_write_config(PCIDevice *d, uint32_t address, uint32_t val, cxl_rp_dvsec_write_config(d, address, val, len); } -static void cxl_root_port_class_init(ObjectClass *oc, void *data) +static void cxl_root_port_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *k = PCI_DEVICE_CLASS(oc); @@ -290,7 +294,7 @@ static const TypeInfo cxl_root_port_info = { .parent = TYPE_PCIE_ROOT_PORT, .instance_size = sizeof(CXLRootPort), .class_init = cxl_root_port_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CXL_DEVICE }, { } }, diff --git a/hw/pci-bridge/cxl_upstream.c b/hw/pci-bridge/cxl_upstream.c index 9159f48a8cb..208e0c6172e 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, }, }; @@ -277,8 +272,8 @@ static int build_cdat_table(CDATSubHeader ***cdat_table, void *priv) *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; } @@ -296,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; @@ -326,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); @@ -342,8 +340,7 @@ 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 (*errp) { + if (!cxl_doe_cdat_init(cxl_cstate, errp)) { goto err_cap; } @@ -365,12 +362,16 @@ static void cxl_usp_exitfn(PCIDevice *d) pci_bridge_exitfn(d); } -static Property cxl_upstream_props[] = { +static const Property cxl_upstream_props[] = { + DEFINE_PROP_UINT64("sn", CXLUpstreamPort, sn, UI64_NULL), DEFINE_PROP_STRING("cdat", CXLUpstreamPort, cxl_cstate.cdat.filename), - DEFINE_PROP_END_OF_LIST() + 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), }; -static void cxl_upstream_class_init(ObjectClass *oc, void *data) +static void cxl_upstream_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *k = PCI_DEVICE_CLASS(oc); @@ -384,7 +385,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); } @@ -393,7 +394,7 @@ static const TypeInfo cxl_usp_info = { .parent = TYPE_PCIE_PORT, .instance_size = sizeof(CXLUpstreamPort), .class_init = cxl_upstream_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CXL_DEVICE }, { } diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c index 1ce4e7bebae..d9078e783bf 100644 --- a/hw/pci-bridge/gen_pcie_root_port.c +++ b/hw/pci-bridge/gen_pcie_root_port.c @@ -117,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), @@ -128,7 +128,7 @@ static const VMStateDescription vmstate_rp_dev = { } }; -static Property gen_rp_props[] = { +static const Property gen_rp_props[] = { DEFINE_PROP_BOOL("x-migrate-msix", GenPCIERootPort, migrate_msix, true), DEFINE_PROP_UINT32("bus-reserve", GenPCIERootPort, @@ -145,10 +145,9 @@ static Property gen_rp_props[] = { speed, PCIE_LINK_SPEED_16), DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot, width, PCIE_LINK_WIDTH_32), - DEFINE_PROP_END_OF_LIST() }; -static void gen_rp_dev_class_init(ObjectClass *klass, void *data) +static void gen_rp_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/pci-bridge/i82801b11.c b/hw/pci-bridge/i82801b11.c index 0e83cd11b2c..1d73c14c1f8 100644 --- a/hw/pci-bridge/i82801b11.c +++ b/hw/pci-bridge/i82801b11.c @@ -81,13 +81,13 @@ static void i82801b11_bridge_realize(PCIDevice *d, Error **errp) 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() } }; -static void i82801b11_bridge_class_init(ObjectClass *klass, void *data) +static void i82801b11_bridge_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -98,7 +98,7 @@ static void i82801b11_bridge_class_init(ObjectClass *klass, void *data) 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); } @@ -107,7 +107,7 @@ static const TypeInfo i82801b11_bridge_info = { .parent = TYPE_PCI_BRIDGE, .instance_size = sizeof(I82801b11Bridge), .class_init = i82801b11_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c index f1e16135a32..bba640f495b 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), @@ -96,7 +96,7 @@ static const VMStateDescription vmstate_ioh3420 = { } }; -static void ioh3420_class_init(ObjectClass *klass, void *data) +static void ioh3420_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/pci-bridge/meson.build b/hw/pci-bridge/meson.build index 6d5ad9f37b2..2e0eb0d2332 100644 --- a/hw/pci-bridge/meson.build +++ b/hw/pci-bridge/meson.build @@ -1,5 +1,5 @@ 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')) @@ -13,5 +13,3 @@ pci_ss.add(when: 'CONFIG_CXL', if_true: files('cxl_root_port.c', 'cxl_upstream.c pci_ss.add(when: 'CONFIG_SIMBA', if_true: files('simba.c')) system_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) - -system_ss.add(when: 'CONFIG_ALL', if_true: files('pci_expander_bridge_stubs.c')) diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 4b2696ea7ff..b328e50ab31 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -28,7 +28,7 @@ #include "hw/pci/shpc.h" #include "hw/pci/slotid_cap.h" #include "hw/qdev-properties.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/pci/pci_bus.h" #include "hw/hotplug.h" #include "qom/object.h" @@ -168,7 +168,7 @@ static void qdev_pci_bridge_dev_reset(DeviceState *qdev) } } -static Property pci_bridge_dev_properties[] = { +static const Property pci_bridge_dev_properties[] = { /* Note: 0 is not a legal chassis number. */ DEFINE_PROP_UINT8(PCI_BRIDGE_DEV_PROP_CHASSIS_NR, PCIBridgeDev, chassis_nr, 0), @@ -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(), }; static bool pci_device_shpc_present(void *opaque, int version_id) @@ -199,7 +198,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() @@ -241,7 +240,7 @@ void pci_bridge_dev_unplug_request_cb(HotplugHandler *hotplug_dev, shpc_device_unplug_request_cb(hotplug_dev, dev, errp); } -static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) +static void pci_bridge_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -254,7 +253,7 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE; k->class_id = PCI_CLASS_BRIDGE_PCI; 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); @@ -269,7 +268,7 @@ static const TypeInfo pci_bridge_dev_info = { .instance_size = sizeof(PCIBridgeDev), .class_init = pci_bridge_dev_class_init, .instance_finalize = pci_bridge_dev_instance_finalize, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } @@ -281,7 +280,7 @@ static const TypeInfo pci_bridge_dev_info = { * different pci id, so we can match it easily in the guest for * automagic multiseat configuration. See docs/multiseat.txt for more. */ -static void pci_bridge_dev_seat_class_init(ObjectClass *klass, void *data) +static void pci_bridge_dev_seat_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 613857b6016..3a29dfefc2c 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -23,7 +23,7 @@ #include "qemu/range.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "sysemu/numa.h" +#include "system/numa.h" #include "hw/boards.h" #include "qom/object.h" @@ -38,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) @@ -85,12 +84,25 @@ static uint16_t pxb_bus_numa_node(PCIBus *bus) return pxb->numa_node; } -static void pxb_bus_class_init(ObjectClass *class, void *data) +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, const 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 = { @@ -157,7 +169,7 @@ static char *pxb_host_ofw_unit_address(const SysBusDevice *dev) return NULL; } -static void pxb_host_class_init(ObjectClass *class, void *data) +static void pxb_host_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(class); @@ -212,7 +224,7 @@ void pxb_cxl_hook_up_registers(CXLState *cxl_state, PCIBus *bus, Error **errp) cxl_state->next_mr_idx++; } -static void pxb_cxl_host_class_init(ObjectClass *class, void *data) +static void pxb_cxl_host_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class); @@ -263,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,7 +302,7 @@ static void pxb_cxl_dev_reset(DeviceState *dev) 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); + 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. @@ -318,7 +330,7 @@ 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 = PXB_DEV(dev); @@ -330,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) { @@ -382,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) @@ -407,15 +420,14 @@ static void pxb_dev_exitfn(PCIDevice *pci_dev) pxb_dev_list = g_list_remove(pxb_dev_list, pxb); } -static Property pxb_dev_properties[] = { +static const Property pxb_dev_properties[] = { /* Note: 0 is not a legal PXB bus number. */ DEFINE_PROP_UINT8("bus_nr", PXBDev, bus_nr, 0), DEFINE_PROP_UINT16("numa_node", PXBDev, numa_node, NUMA_NODE_UNASSIGNED), DEFINE_PROP_BOOL("bypass_iommu", PXBDev, bypass_iommu, false), - DEFINE_PROP_END_OF_LIST(), }; -static void pxb_dev_class_init(ObjectClass *klass, void *data) +static void pxb_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -437,7 +449,7 @@ static const TypeInfo pxb_dev_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PXBDev), .class_init = pxb_dev_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -453,7 +465,7 @@ static void pxb_pcie_dev_realize(PCIDevice *dev, Error **errp) pxb_dev_realize_common(dev, PCIE, errp); } -static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data) +static void pxb_pcie_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -474,7 +486,7 @@ static const TypeInfo pxb_pcie_dev_info = { .parent = TYPE_PXB_DEV, .instance_size = sizeof(PXBPCIEDev), .class_init = pxb_pcie_dev_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -488,16 +500,17 @@ static void pxb_cxl_dev_realize(PCIDevice *dev, Error **errp) return; } - pxb_dev_realize_common(dev, CXL, errp); + if (!pxb_dev_realize_common(dev, CXL, errp)) { + return; + } pxb_cxl_dev_reset(DEVICE(dev)); } -static Property pxb_cxl_dev_properties[] = { +static const 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) +static void pxb_cxl_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -515,7 +528,7 @@ static void pxb_cxl_dev_class_init(ObjectClass *klass, void *data) /* Host bridges aren't hotpluggable. FIXME: spec reference */ dc->hotpluggable = false; - dc->reset = pxb_cxl_dev_reset; + device_class_set_legacy_reset(dc, pxb_cxl_dev_reset); } static const TypeInfo pxb_cxl_dev_info = { @@ -524,7 +537,7 @@ static const TypeInfo pxb_cxl_dev_info = { .instance_size = sizeof(PXBCXLDev), .class_init = pxb_cxl_dev_class_init, .interfaces = - (InterfaceInfo[]){ + (const InterfaceInfo[]){ { INTERFACE_CONVENTIONAL_PCI_DEVICE }, {}, }, diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c index 2301b2ca0b0..fce292a519b 100644 --- a/hw/pci-bridge/pcie_pci_bridge.c +++ b/hw/pci-bridge/pcie_pci_bridge.c @@ -52,11 +52,10 @@ static void pcie_pci_bridge_realize(PCIDevice *d, Error **errp) goto cap_error; } - pos = pci_add_capability(d, PCI_CAP_ID_PM, 0, PCI_PM_SIZEOF, errp); + pos = pci_pm_init(d, 0, errp); if (pos < 0) { goto pm_error; } - d->exp.pm_cap = pos; pci_set_word(d->config + pos + PCI_PM_PMC, 0x3); pcie_cap_arifwd_init(d); @@ -124,22 +123,21 @@ static void pcie_pci_bridge_write_config(PCIDevice *d, shpc_cap_write_config(d, address, val, len); } -static Property pcie_pci_bridge_dev_properties[] = { +static const Property pcie_pci_bridge_dev_properties[] = { DEFINE_PROP_ON_OFF_AUTO("msi", PCIEPCIBridge, msi, ON_OFF_AUTO_AUTO), - DEFINE_PROP_END_OF_LIST(), }; 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() } }; -static void pcie_pci_bridge_class_init(ObjectClass *klass, void *data) +static void pcie_pci_bridge_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -152,7 +150,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; @@ -164,7 +162,7 @@ static const TypeInfo pcie_pci_bridge_info = { .parent = TYPE_PCI_BRIDGE, .instance_size = sizeof(PCIEPCIBridge), .class_init = pcie_pci_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { INTERFACE_PCIE_DEVICE }, { }, diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c index efd96bf1741..22c2fdb71e7 100644 --- a/hw/pci-bridge/pcie_root_port.c +++ b/hw/pci-bridge/pcie_root_port.c @@ -43,7 +43,7 @@ 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_hold(Object *obj) +static void rp_reset_hold(Object *obj, ResetType type) { PCIDevice *d = PCI_DEVICE(obj); DeviceState *qdev = DEVICE(obj); @@ -148,11 +148,10 @@ static void rp_exit(PCIDevice *d) pci_bridge_exitfn(d); } -static Property rp_props[] = { +static const Property rp_props[] = { DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present, QEMU_PCIE_SLTCAP_PCP_BITNR, true), DEFINE_PROP_BOOL("disable-acs", PCIESlot, disable_acs, false), - DEFINE_PROP_END_OF_LIST() }; static void rp_instance_post_init(Object *obj) @@ -168,7 +167,7 @@ static void rp_instance_post_init(Object *obj) } } -static void rp_class_init(ObjectClass *klass, void *data) +static void rp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -189,7 +188,7 @@ static const TypeInfo rp_info = { .class_init = rp_class_init, .abstract = true, .class_size = sizeof(PCIERootPortClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/pci-bridge/simba.c b/hw/pci-bridge/simba.c index 17aa0d7b216..bbae594e119 100644 --- a/hw/pci-bridge/simba.c +++ b/hw/pci-bridge/simba.c @@ -66,7 +66,7 @@ static void simba_pci_bridge_realize(PCIDevice *dev, Error **errp) pci_bridge_update_mappings(PCI_BRIDGE(br)); } -static void simba_pci_bridge_class_init(ObjectClass *klass, void *data) +static void simba_pci_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -78,7 +78,7 @@ static void simba_pci_bridge_class_init(ObjectClass *klass, void *data) k->revision = 0x11; k->config_write = pci_bridge_write_config; 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; } @@ -87,7 +87,7 @@ static const TypeInfo simba_pci_bridge_info = { .parent = TYPE_PCI_BRIDGE, .class_init = simba_pci_bridge_class_init, .instance_size = sizeof(SimbaPCIBridge), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c index 38a2361fa2a..dc7d1aa7d77 100644 --- a/hw/pci-bridge/xio3130_downstream.c +++ b/hw/pci-bridge/xio3130_downstream.c @@ -134,10 +134,9 @@ static void xio3130_downstream_exitfn(PCIDevice *d) pci_bridge_exitfn(d); } -static Property xio3130_downstream_props[] = { +static const Property xio3130_downstream_props[] = { DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present, QEMU_PCIE_SLTCAP_PCP_BITNR, true), - DEFINE_PROP_END_OF_LIST() }; static const VMStateDescription vmstate_xio3130_downstream = { @@ -146,7 +145,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), @@ -154,7 +153,7 @@ static const VMStateDescription vmstate_xio3130_downstream = { } }; -static void xio3130_downstream_class_init(ObjectClass *klass, void *data) +static void xio3130_downstream_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -167,7 +166,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); } @@ -176,7 +175,7 @@ static const TypeInfo xio3130_downstream_info = { .name = TYPE_XIO3130_DOWNSTREAM, .parent = TYPE_PCIE_SLOT, .class_init = xio3130_downstream_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c index a48bfe3bc54..40057b749bf 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), @@ -123,7 +123,7 @@ static const VMStateDescription vmstate_xio3130_upstream = { } }; -static void xio3130_upstream_class_init(ObjectClass *klass, void *data) +static void xio3130_upstream_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -136,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; } @@ -144,7 +144,7 @@ static const TypeInfo xio3130_upstream_info = { .name = "x3130-upstream", .parent = TYPE_PCIE_PORT, .class_init = xio3130_upstream_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig index a07070eddf2..35c04152428 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 @@ -82,6 +95,13 @@ config DINO bool select PCI +config ASTRO + bool + select PCI + +config PCI_EXPRESS_FSL_IMX8M_PHY + bool + config GT64120 bool select PCI diff --git a/hw/pci-host/articia.c b/hw/pci-host/articia.c new file mode 100644 index 00000000000..cc65aac2a87 --- /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, const 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, const 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, const 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 = (const 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 = (const 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 00000000000..859e308c577 --- /dev/null +++ b/hw/pci-host/astro.c @@ -0,0 +1,955 @@ +/* + * 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" +#include "exec/target_page.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 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, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_legacy_reset(dc, elroy_reset); + 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 void adjust_LMMIO_DIRECT_mapping(AstroState *s, unsigned int reg_index) +{ + MemoryRegion *lmmio_alias; + unsigned int lmmio_index, map_route; + hwaddr map_addr; + uint32_t map_size; + struct ElroyState *elroy; + + /* pointer to LMMIO_DIRECT entry */ + lmmio_index = reg_index / 3; + lmmio_alias = &s->lmmio_direct[lmmio_index]; + + map_addr = s->ioc_ranges[3 * lmmio_index + 0]; + map_size = s->ioc_ranges[3 * lmmio_index + 1]; + map_route = s->ioc_ranges[3 * lmmio_index + 2]; + + /* find elroy to which this address is routed */ + map_route &= (ELROY_NUM - 1); + elroy = s->elroy[map_route]; + + if (lmmio_alias->enabled) { + memory_region_set_enabled(lmmio_alias, false); + } + + map_addr = F_EXTEND(map_addr); + map_addr &= TARGET_PAGE_MASK; + map_size = (~map_size) + 1; + map_size &= TARGET_PAGE_MASK; + + /* exit if disabled or zero map size */ + if (!(map_addr & 1) || !map_size) { + return; + } + + if (!memory_region_size(lmmio_alias)) { + memory_region_init_alias(lmmio_alias, OBJECT(elroy), + "pci-lmmmio-alias", &elroy->pci_mmio, + (uint32_t) map_addr, map_size); + memory_region_add_subregion(get_system_memory(), map_addr, + lmmio_alias); + } else { + memory_region_set_alias_offset(lmmio_alias, map_addr); + memory_region_set_size(lmmio_alias, map_size); + memory_region_set_enabled(lmmio_alias, true); + } +} + +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); + unsigned int index = (addr - 0x300) / 8; + /* check if one of the 4 LMMIO_DIRECT regs, each using 3 entries. */ + if (index < LMMIO_DIRECT_RANGES * 3) { + adjust_LMMIO_DIRECT_mapping(s, index); + } + 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, const 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, + const 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 4701481b9b4..7d6251a78d7 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -48,7 +48,7 @@ #include "hw/pci-host/bonito.h" #include "hw/pci/pci_host.h" #include "migration/vmstate.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/misc/unimp.h" #include "hw/registerfields.h" #include "qom/object.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) @@ -590,7 +590,7 @@ static int pci_bonito_map_irq(PCIDevice *pci_dev, int irq_num) } } -static void bonito_reset_hold(Object *obj) +static void bonito_reset_hold(Object *obj, ResetType type) { PCIBonitoState *s = PCI_BONITO(obj); uint32_t val = 0; @@ -619,7 +619,7 @@ 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() } @@ -654,7 +654,7 @@ static void bonito_host_realize(DeviceState *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 = s->pcihost; MemoryRegion *pcimem_alias = g_new(MemoryRegion, 1); @@ -668,48 +668,45 @@ static void bonito_pci_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, @@ -719,8 +716,7 @@ static void bonito_pci_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); @@ -761,7 +757,7 @@ PCIBus *bonito_init(qemu_irq *pic) return phb->bus; } -static void bonito_pci_class_init(ObjectClass *klass, void *data) +static void bonito_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -787,13 +783,13 @@ static const TypeInfo bonito_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIBonitoState), .class_init = bonito_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -static void bonito_host_class_init(ObjectClass *klass, void *data) +static void bonito_host_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c index 388d252ee23..f6e49ce9b8d 100644 --- a/hw/pci-host/designware.c +++ b/hw/pci-host/designware.c @@ -20,8 +20,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qemu/module.h" #include "qemu/log.h" +#include "qemu/bitops.h" #include "hw/pci/msi.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/pci_host.h" @@ -55,7 +55,17 @@ #define DESIGNWARE_PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff) #define DESIGNWARE_PCIE_ATU_UPPER_TARGET 0x91C -#define DESIGNWARE_PCIE_IRQ_MSI 3 +static void designware_pcie_root_bus_class_init(ObjectClass *klass, + const void *data) +{ + BusClass *k = BUS_CLASS(klass); + + /* + * Designware has only a single root complex. Enforce the limit on the + * parent bus + */ + k->max_dev = 1; +} static DesignwarePCIEHost * designware_pcie_root_to_host(DesignwarePCIERoot *root) @@ -90,7 +100,7 @@ static void designware_pcie_root_msi_write(void *opaque, hwaddr addr, root->msi.intr[0].status |= BIT(val) & root->msi.intr[0].enable; if (root->msi.intr[0].status & ~root->msi.intr[0].mask) { - qemu_set_irq(host->pci.irqs[DESIGNWARE_PCIE_IRQ_MSI], 1); + qemu_set_irq(host->pci.msi, 1); } } @@ -153,11 +163,9 @@ designware_pcie_root_config_read(PCIDevice *d, uint32_t address, int len) break; case DESIGNWARE_PCIE_MSI_ADDR_LO: - val = root->msi.base; - break; - case DESIGNWARE_PCIE_MSI_ADDR_HI: - val = root->msi.base >> 32; + val = extract64(root->msi.base, + address == DESIGNWARE_PCIE_MSI_ADDR_LO ? 0 : 32, 32); break; case DESIGNWARE_PCIE_MSI_INTR0_ENABLE: @@ -181,19 +189,16 @@ designware_pcie_root_config_read(PCIDevice *d, uint32_t address, int len) break; case DESIGNWARE_PCIE_ATU_LOWER_BASE: - val = viewport->base; - break; - case DESIGNWARE_PCIE_ATU_UPPER_BASE: - val = viewport->base >> 32; + val = extract64(viewport->base, + address == DESIGNWARE_PCIE_ATU_LOWER_BASE ? 0 : 32, 32); break; case DESIGNWARE_PCIE_ATU_LOWER_TARGET: - val = viewport->target; - break; - case DESIGNWARE_PCIE_ATU_UPPER_TARGET: - val = viewport->target >> 32; + val = extract64(viewport->target, + address == DESIGNWARE_PCIE_ATU_LOWER_TARGET ? 0 : 32, + 32); break; case DESIGNWARE_PCIE_ATU_LIMIT: @@ -312,14 +317,10 @@ static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address, break; case DESIGNWARE_PCIE_MSI_ADDR_LO: - root->msi.base &= 0xFFFFFFFF00000000ULL; - root->msi.base |= val; - designware_pcie_root_update_msi_mapping(root); - break; - case DESIGNWARE_PCIE_MSI_ADDR_HI: - root->msi.base &= 0x00000000FFFFFFFFULL; - root->msi.base |= (uint64_t)val << 32; + root->msi.base = deposit64(root->msi.base, + address == DESIGNWARE_PCIE_MSI_ADDR_LO + ? 0 : 32, 32, val); designware_pcie_root_update_msi_mapping(root); break; @@ -335,32 +336,28 @@ static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address, case DESIGNWARE_PCIE_MSI_INTR0_STATUS: root->msi.intr[0].status ^= val; if (!root->msi.intr[0].status) { - qemu_set_irq(host->pci.irqs[DESIGNWARE_PCIE_IRQ_MSI], 0); + qemu_set_irq(host->pci.msi, 0); } break; case DESIGNWARE_PCIE_ATU_VIEWPORT: + val &= DESIGNWARE_PCIE_ATU_REGION_INBOUND | + (DESIGNWARE_PCIE_NUM_VIEWPORTS - 1); root->atu_viewport = val; break; case DESIGNWARE_PCIE_ATU_LOWER_BASE: - viewport->base &= 0xFFFFFFFF00000000ULL; - viewport->base |= val; - break; - case DESIGNWARE_PCIE_ATU_UPPER_BASE: - viewport->base &= 0x00000000FFFFFFFFULL; - viewport->base |= (uint64_t)val << 32; + viewport->base = deposit64(viewport->base, + address == DESIGNWARE_PCIE_ATU_LOWER_BASE + ? 0 : 32, 32, val); break; case DESIGNWARE_PCIE_ATU_LOWER_TARGET: - viewport->target &= 0xFFFFFFFF00000000ULL; - viewport->target |= val; - break; - case DESIGNWARE_PCIE_ATU_UPPER_TARGET: - viewport->target &= 0x00000000FFFFFFFFULL; - viewport->target |= val; + viewport->target = deposit64(viewport->target, + address == DESIGNWARE_PCIE_ATU_LOWER_TARGET + ? 0 : 32, 32, val); break; case DESIGNWARE_PCIE_ATU_LIMIT: @@ -393,6 +390,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 +431,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 +456,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 +486,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 +501,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 +527,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 +539,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 +555,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 +568,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, @@ -589,7 +587,8 @@ static const VMStateDescription vmstate_designware_pcie_root = { } }; -static void designware_pcie_root_class_init(ObjectClass *klass, void *data) +static void designware_pcie_root_class_init(ObjectClass *klass, + const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -605,7 +604,7 @@ static void designware_pcie_root_class_init(ObjectClass *klass, void *data) 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. @@ -663,6 +662,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); @@ -673,6 +676,7 @@ static void designware_pcie_host_realize(DeviceState *dev, Error **errp) for (i = 0; i < ARRAY_SIZE(s->pci.irqs); i++) { sysbus_init_irq(sbd, &s->pci.irqs[i]); } + sysbus_init_irq(sbd, &s->pci.msi); memory_region_init_io(&s->mmio, OBJECT(s), @@ -693,7 +697,7 @@ static void designware_pcie_host_realize(DeviceState *dev, Error **errp) &s->pci.memory, &s->pci.io, 0, 4, - TYPE_PCIE_BUS); + TYPE_DESIGNWARE_PCIE_ROOT_BUS); pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; memory_region_init(&s->pci.address_space_root, @@ -705,7 +709,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 +718,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, @@ -724,7 +728,8 @@ static const VMStateDescription vmstate_designware_pcie_host = { } }; -static void designware_pcie_host_class_init(ObjectClass *klass, void *data) +static void designware_pcie_host_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); @@ -746,28 +751,28 @@ 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_ROOT_BUS, + .parent = TYPE_PCIE_BUS, + .instance_size = sizeof(DesignwarePCIERootBus), + .class_init = designware_pcie_root_bus_class_init, + }, { + .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 = (const 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 e8eaebca545..11b353be2ea 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 * @@ -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,24 +485,23 @@ 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); qdev_init_gpio_in(DEVICE(obj), dino_set_irq, DINO_IRQS); } -static Property dino_pcihost_properties[] = { +static const Property dino_pcihost_properties[] = { DEFINE_PROP_LINK("memory-as", DinoState, memory_as, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; -static void dino_pcihost_class_init(ObjectClass *klass, void *data) +static void dino_pcihost_class_init(ObjectClass *klass, const 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/fsl_imx8m_phy.c b/hw/pci-host/fsl_imx8m_phy.c new file mode 100644 index 00000000000..04da3f99a07 --- /dev/null +++ b/hw/pci-host/fsl_imx8m_phy.c @@ -0,0 +1,98 @@ +/* + * i.MX8 PCIe PHY emulation + * + * Copyright (c) 2025 Bernhard Beschow + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/pci-host/fsl_imx8m_phy.h" +#include "hw/resettable.h" +#include "migration/vmstate.h" + +#define CMN_REG075 0x1d4 +#define ANA_PLL_LOCK_DONE BIT(1) +#define ANA_PLL_AFC_DONE BIT(0) + +static uint64_t fsl_imx8m_pcie_phy_read(void *opaque, hwaddr offset, + unsigned size) +{ + FslImx8mPciePhyState *s = opaque; + + if (offset == CMN_REG075) { + return s->data[offset] | ANA_PLL_LOCK_DONE | ANA_PLL_AFC_DONE; + } + + return s->data[offset]; +} + +static void fsl_imx8m_pcie_phy_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + FslImx8mPciePhyState *s = opaque; + + s->data[offset] = value; +} + +static const MemoryRegionOps fsl_imx8m_pcie_phy_ops = { + .read = fsl_imx8m_pcie_phy_read, + .write = fsl_imx8m_pcie_phy_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void fsl_imx8m_pcie_phy_realize(DeviceState *dev, Error **errp) +{ + FslImx8mPciePhyState *s = FSL_IMX8M_PCIE_PHY(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &fsl_imx8m_pcie_phy_ops, s, + TYPE_FSL_IMX8M_PCIE_PHY, ARRAY_SIZE(s->data)); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); +} + +static void fsl_imx8m_pcie_phy_reset_hold(Object *obj, ResetType type) +{ + FslImx8mPciePhyState *s = FSL_IMX8M_PCIE_PHY(obj); + + memset(s->data, 0, sizeof(s->data)); +} + +static const VMStateDescription fsl_imx8m_pcie_phy_vmstate = { + .name = "fsl-imx8m-pcie-phy", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT8_ARRAY(data, FslImx8mPciePhyState, + FSL_IMX8M_PCIE_PHY_DATA_SIZE), + VMSTATE_END_OF_LIST() + } +}; + +static void fsl_imx8m_pcie_phy_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->realize = fsl_imx8m_pcie_phy_realize; + dc->vmsd = &fsl_imx8m_pcie_phy_vmstate; + rc->phases.hold = fsl_imx8m_pcie_phy_reset_hold; +} + +static const TypeInfo fsl_imx8m_pcie_phy_types[] = { + { + .name = TYPE_FSL_IMX8M_PCIE_PHY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(FslImx8mPciePhyState), + .class_init = fsl_imx8m_pcie_phy_class_init, + } +}; + +DEFINE_TYPES(fsl_imx8m_pcie_phy_types) diff --git a/hw/pci-host/gpex-acpi.c b/hw/pci-host/gpex-acpi.c index 7c7316bc964..0aba47c71c0 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,18 +171,18 @@ 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: - * 1. The resources the pci-brige/pcie-root-port need. + * Resources defined for PXBs are composed of the following parts: + * 1. The resources the pci-bridge/pcie-root-port need. * 2. The resources the devices behind pxb need. */ crs = build_crs(PCI_HOST_BRIDGE(BUS(bus)->parent), &crs_range_set, @@ -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 a6752fac5e8..b806a2286f7 100644 --- a/hw/pci-host/gpex.c +++ b/hw/pci-host/gpex.c @@ -32,6 +32,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/irq.h" +#include "hw/pci/pci_bus.h" #include "hw/pci-host/gpex.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -41,20 +42,25 @@ * GPEX host */ +struct GPEXIrq { + qemu_irq irq; + int irq_num; +}; + static void gpex_set_irq(void *opaque, int irq_num, int level) { GPEXHost *s = opaque; - qemu_set_irq(s->irq[irq_num], level); + qemu_set_irq(s->irq[irq_num].irq, level); } int gpex_set_irq_num(GPEXHost *s, int index, int gsi) { - if (index >= GPEX_NUM_IRQS) { + if (index >= s->num_irqs) { return -EINVAL; } - s->irq_num[index] = gsi; + s->irq[index].irq_num = gsi; return 0; } @@ -62,7 +68,7 @@ static PCIINTxRoute gpex_route_intx_pin_to_irq(void *opaque, int pin) { PCIINTxRoute route; GPEXHost *s = opaque; - int gsi = s->irq_num[pin]; + int gsi = s->irq[pin].irq_num; route.irq = gsi; if (gsi < 0) { @@ -74,6 +80,13 @@ static PCIINTxRoute gpex_route_intx_pin_to_irq(void *opaque, int pin) return route; } +static int gpex_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin) +{ + PCIBus *bus = pci_device_root_bus(pci_dev); + + return (PCI_SLOT(pci_dev->devfn) + pin) % bus->nirq; +} + static void gpex_host_realize(DeviceState *dev, Error **errp) { PCIHostState *pci = PCI_HOST_BRIDGE(dev); @@ -82,6 +95,8 @@ static void gpex_host_realize(DeviceState *dev, Error **errp) PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev); int i; + s->irq = g_malloc0_n(s->num_irqs, sizeof(*s->irq)); + pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MAX); sysbus_init_mmio(sbd, &pex->mmio); @@ -128,42 +143,63 @@ static void gpex_host_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->io_ioport); } - for (i = 0; i < GPEX_NUM_IRQS; i++) { - sysbus_init_irq(sbd, &s->irq[i]); - s->irq_num[i] = -1; + for (i = 0; i < s->num_irqs; i++) { + sysbus_init_irq(sbd, &s->irq[i].irq); + s->irq[i].irq_num = -1; } pci->bus = pci_register_root_bus(dev, "pcie.0", gpex_set_irq, - pci_swizzle_map_irq_fn, s, &s->io_mmio, - &s->io_ioport, 0, 4, TYPE_PCIE_BUS); + gpex_swizzle_map_irq_fn, + s, &s->io_mmio, &s->io_ioport, 0, + s->num_irqs, TYPE_PCIE_BUS); pci_bus_set_route_irq_fn(pci->bus, gpex_route_intx_pin_to_irq); qdev_realize(DEVICE(&s->gpex_root), BUS(pci->bus), &error_fatal); } +static void gpex_host_unrealize(DeviceState *dev) +{ + GPEXHost *s = GPEX_HOST(dev); + + g_free(s->irq); +} + static const char *gpex_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) { return "0000:00"; } -static Property gpex_host_properties[] = { +static const Property gpex_host_properties[] = { /* * Permit CPU accesses to unmapped areas of the PIO and MMIO windows * (discarding writes and returning -1 for reads) rather than aborting. */ DEFINE_PROP_BOOL("allow-unmapped-accesses", GPEXHost, allow_unmapped_accesses, true), - DEFINE_PROP_END_OF_LIST(), + 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_UINT8("num-irqs", GPEXHost, num_irqs, PCI_NUM_PINS), }; -static void gpex_host_class_init(ObjectClass *klass, void *data) +static void gpex_host_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); hc->root_bus_path = gpex_host_root_bus_path; dc->realize = gpex_host_realize; + dc->unrealize = gpex_host_unrealize; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->fw_name = "pci"; device_class_set_props(dc, gpex_host_properties); @@ -195,13 +231,13 @@ 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() } }; -static void gpex_root_class_init(ObjectClass *klass, void *data) +static void gpex_root_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -225,7 +261,7 @@ static const TypeInfo gpex_root_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(GPEXRootState), .class_init = gpex_root_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index 8e589ff2c9e..f9da5a908c1 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -94,7 +94,7 @@ static void grackle_pci_realize(PCIDevice *d, Error **errp) d->config[PCI_CLASS_PROG] = 0x01; } -static void grackle_pci_class_init(ObjectClass *klass, void *data) +static void grackle_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -116,7 +116,7 @@ static const TypeInfo grackle_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = grackle_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -129,12 +129,11 @@ static char *grackle_ofw_unit_address(const SysBusDevice *dev) return g_strdup_printf("%x", s->ofw_addr); } -static Property grackle_properties[] = { +static const Property grackle_properties[] = { DEFINE_PROP_UINT32("ofw-addr", GrackleState, ofw_addr, -1), - DEFINE_PROP_END_OF_LIST() }; -static void grackle_class_init(ObjectClass *klass, void *data) +static void grackle_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); diff --git a/hw/pci-host/gt64120.c b/hw/pci-host/gt64120.c index 82c15edb469..b12a25696c4 100644 --- a/hw/pci-host/gt64120.c +++ b/hw/pci-host/gt64120.c @@ -1,6 +1,8 @@ /* * 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 @@ -318,38 +320,6 @@ static void gt64120_isd_mapping(GT64120State *s) 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 Endianess bit in the CPU - * Configuration Register. See: - * - Table 16: 32-bit PCI Transaction Endianess - * - 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(); @@ -431,7 +401,7 @@ static const VMStateDescription vmstate_gt64120 = { .version_id = 1, .minimum_version_id = 1, .post_load = gt64120_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, GT64120State, GT_REGS), VMSTATE_END_OF_LIST() } @@ -643,7 +613,6 @@ static void gt64120_writel(void *opaque, hwaddr addr, case GT_PCI0_CMD: case GT_PCI1_CMD: s->regs[saddr] = val & 0x0401fc0f; - gt64120_update_pci_cfgdata_mapping(s); break; case GT_PCI0_TOR: case GT_PCI0_BS_SCS10: @@ -687,7 +656,6 @@ static void gt64120_writel(void *opaque, hwaddr addr, case GT_PCI0_CFGDATA: /* Mapped via in gt64120_pci_mapping() */ g_assert_not_reached(); - break; /* Interrupts */ case GT_INTRCAUSE: @@ -931,7 +899,6 @@ static uint64_t gt64120_readl(void *opaque, case GT_PCI0_CFGDATA: /* Mapped via in gt64120_pci_mapping() */ g_assert_not_reached(); - break; case GT_PCI0_CMD: case GT_PCI0_TOR: @@ -1024,6 +991,48 @@ static const MemoryRegionOps isd_mem_ops = { }, }; +static bool bswap(const GT64120State *s) +{ + PCIHostState *phb = PCI_HOST_BRIDGE(s); + /*check for bus == 0 && device == 0, Bits 11:15 = Device , Bits 16:23 = Bus*/ + bool is_phb_dev0 = extract32(phb->config_reg, 11, 13) == 0; + bool le_mode = FIELD_EX32(s->regs[GT_PCI0_CMD], GT_PCI0_CMD, MByteSwap); + /* Only swap for non-bridge devices in big-endian mode */ + return !le_mode && !is_phb_dev0; +} + +static uint64_t gt64120_pci_data_read(void *opaque, hwaddr addr, unsigned size) +{ + GT64120State *s = opaque; + uint32_t val = pci_host_data_le_ops.read(opaque, addr, size); + + if (bswap(s)) { + val = bswap32(val); + } + return val; +} + +static void gt64120_pci_data_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + GT64120State *s = opaque; + + if (bswap(s)) { + val = bswap32(val); + } + pci_host_data_le_ops.write(opaque, addr, val, size); +} + +static const MemoryRegionOps gt64120_pci_data_ops = { + .read = gt64120_pci_data_read, + .write = gt64120_pci_data_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + static void gt64120_reset(DeviceState *dev) { GT64120State *s = GT64120_PCI_HOST_BRIDGE(dev); @@ -1178,7 +1187,6 @@ static void gt64120_reset(DeviceState *dev) gt64120_isd_mapping(s); gt64120_pci_mapping(s); - gt64120_update_pci_cfgdata_mapping(s); } static void gt64120_realize(DeviceState *dev, Error **errp) @@ -1202,6 +1210,12 @@ static void gt64120_realize(DeviceState *dev, Error **errp) memory_region_add_subregion_overlap(&s->ISD_mem, GT_PCI0_CFGADDR << 2, &phb->conf_mem, 1); + memory_region_init_io(&phb->data_mem, OBJECT(phb), + >64120_pci_data_ops, + s, "pci-conf-data", 4); + memory_region_add_subregion_overlap(&s->ISD_mem, GT_PCI0_CFGDATA << 2, + &phb->data_mem, 1); + /* * The whole address space decoded by the GT-64120A doesn't generate @@ -1213,25 +1227,44 @@ static void gt64120_realize(DeviceState *dev, Error **errp) static void gt64120_pci_realize(PCIDevice *d, Error **errp) { - /* FIXME: Malta specific hw assumptions ahead */ + /* 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) +static void gt64120_pci_class_init(ObjectClass *klass, const 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; @@ -1249,26 +1282,25 @@ static const TypeInfo gt64120_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = gt64120_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -static Property gt64120_properties[] = { +static const 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) +static void gt64120_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); device_class_set_props(dc, gt64120_properties); dc->realize = gt64120_realize; - dc->reset = gt64120_reset; + device_class_set_legacy_reset(dc, gt64120_reset); dc->vmsd = &vmstate_gt64120; } diff --git a/hw/pci-host/i440fx.c b/hw/pci-host/i440fx.c index 62d62876818..e13bb1b53e4 100644 --- a/hw/pci-host/i440fx.c +++ b/hw/pci-host/i440fx.c @@ -56,7 +56,6 @@ struct I440FXState { uint64_t above_4g_mem_size; uint64_t pci_hole64_size; bool pci_hole64_fix; - uint32_t short_root_bus; char *pci_type; }; @@ -126,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. @@ -316,7 +315,7 @@ static void i440fx_pcihost_realize(DeviceState *dev, Error **errp) i440fx_update_memory_mappings(f); } -static void i440fx_class_init(ObjectClass *klass, void *data) +static void i440fx_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -342,7 +341,7 @@ static const TypeInfo i440fx_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCII440FXState), .class_init = i440fx_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -351,29 +350,21 @@ 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[] = { +static const 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(), }; -static void i440fx_pcihost_class_init(ObjectClass *klass, void *data) +static void i440fx_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build index 64eada76fe6..937a0f72acf 100644 --- a/hw/pci-host/meson.build +++ b/hw/pci-host/meson.build @@ -14,19 +14,25 @@ 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')) # ARM devices +pci_ss.add(when: 'CONFIG_PCI_EXPRESS_FSL_IMX8M_PHY', if_true: files('fsl_imx8m_phy.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')) system_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) diff --git a/hw/pci-host/mv64361.c b/hw/pci-host/mv64361.c index 01bd8c887fa..e05b6770102 100644 --- a/hw/pci-host/mv64361.c +++ b/hw/pci-host/mv64361.c @@ -17,7 +17,7 @@ #include "hw/irq.h" #include "hw/intc/i8259.h" #include "hw/qdev-properties.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qemu/log.h" #include "qemu/error-report.h" #include "trace.h" @@ -26,7 +26,7 @@ #define TYPE_MV64361_PCI_BRIDGE "mv64361-pcibridge" -static void mv64361_pcibridge_class_init(ObjectClass *klass, void *data) +static void mv64361_pcibridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -46,7 +46,7 @@ static const TypeInfo mv64361_pcibridge_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = mv64361_pcibridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -95,14 +95,14 @@ static void mv64361_pcihost_realize(DeviceState *dev, Error **errp) &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[] = { +static const Property mv64361_pcihost_props[] = { DEFINE_PROP_UINT8("index", MV64361PCIState, index, 0), - DEFINE_PROP_END_OF_LIST() }; -static void mv64361_pcihost_class_init(ObjectClass *klass, void *data) +static void mv64361_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -923,12 +923,12 @@ static void mv64361_reset(DeviceState *dev) set_mem_windows(s, 0xfbfff); } -static void mv64361_class_init(ObjectClass *klass, void *data) +static void mv64361_class_init(ObjectClass *klass, const 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/pnv_phb.c b/hw/pci-host/pnv_phb.c index 82332d7a054..4b0ced79b08 100644 --- a/hw/pci-host/pnv_phb.c +++ b/hw/pci-host/pnv_phb.c @@ -17,7 +17,7 @@ #include "hw/ppc/pnv.h" #include "hw/qdev-properties.h" #include "qom/object.h" -#include "sysemu/sysemu.h" +#include "system/system.h" /* @@ -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 @@ -183,7 +183,7 @@ static const char *pnv_phb_root_bus_path(PCIHostState *host_bridge, return phb->bus_path; } -static Property pnv_phb_properties[] = { +static const Property pnv_phb_properties[] = { DEFINE_PROP_UINT32("index", PnvPHB, phb_id, 0), DEFINE_PROP_UINT32("chip-id", PnvPHB, chip_id, 0), DEFINE_PROP_UINT32("version", PnvPHB, version, 0), @@ -192,11 +192,9 @@ static Property pnv_phb_properties[] = { DEFINE_PROP_LINK("pec", PnvPHB, pec, TYPE_PNV_PHB4_PEC, PnvPhb4PecState *), - - DEFINE_PROP_END_OF_LIST(), }; -static void pnv_phb_class_init(ObjectClass *klass, void *data) +static void pnv_phb_class_init(ObjectClass *klass, const void *data) { PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -208,7 +206,7 @@ static void pnv_phb_class_init(ObjectClass *klass, void *data) dc->user_creatable = true; } -static void pnv_phb_root_port_reset_hold(Object *obj) +static void pnv_phb_root_port_reset_hold(Object *obj, ResetType type) { PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(obj); PnvPHBRootPort *phb_rp = PNV_PHB_ROOT_PORT(obj); @@ -216,7 +214,7 @@ static void pnv_phb_root_port_reset_hold(Object *obj) uint8_t *conf = d->config; if (rpc->parent_phases.hold) { - rpc->parent_phases.hold(obj); + rpc->parent_phases.hold(obj, type); } if (phb_rp->version == 3) { @@ -302,13 +300,11 @@ static void pnv_phb_root_port_realize(DeviceState *dev, Error **errp) pci_config_set_interrupt_pin(pci->config, 0); } -static Property pnv_phb_root_port_properties[] = { +static const Property pnv_phb_root_port_properties[] = { DEFINE_PROP_UINT32("version", PnvPHBRootPort, version, 0), - - DEFINE_PROP_END_OF_LIST(), }; -static void pnv_phb_root_port_class_init(ObjectClass *klass, void *data) +static void pnv_phb_root_port_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c index 7a21497cf8f..a4335f44f22 100644 --- a/hw/pci-host/pnv_phb3.c +++ b/hw/pci-host/pnv_phb3.c @@ -20,7 +20,7 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "qom/object.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #define phb3_error(phb, fmt, ...) \ qemu_log_mask(LOG_GUEST_ERROR, "phb3[%d:%d]: " fmt "\n", \ @@ -757,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"); @@ -888,7 +888,7 @@ DECLARE_INSTANCE_CHECKER(IOMMUMemoryRegion, PNV_PHB3_IOMMU_MEMORY_REGION, TYPE_PNV_PHB3_IOMMU_MEMORY_REGION) static void pnv_phb3_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); @@ -968,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); @@ -1012,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) @@ -1086,15 +1090,14 @@ void pnv_phb3_update_regions(PnvPHB3 *phb) pnv_phb3_check_all_m64s(phb); } -static Property pnv_phb3_properties[] = { +static const Property pnv_phb3_properties[] = { DEFINE_PROP_UINT32("index", PnvPHB3, phb_id, 0), DEFINE_PROP_UINT32("chip-id", PnvPHB3, chip_id, 0), DEFINE_PROP_LINK("chip", PnvPHB3, chip, TYPE_PNV_CHIP, PnvChip *), DEFINE_PROP_LINK("phb-base", PnvPHB3, phb_base, TYPE_PNV_PHB, PnvPHB *), - DEFINE_PROP_END_OF_LIST(), }; -static void pnv_phb3_class_init(ObjectClass *klass, void *data) +static void pnv_phb3_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1146,7 +1149,7 @@ static void pnv_phb3_root_bus_set_prop(Object *obj, Visitor *v, } } -static void pnv_phb3_root_bus_class_init(ObjectClass *klass, void *data) +static void pnv_phb3_root_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); diff --git a/hw/pci-host/pnv_phb3_msi.c b/hw/pci-host/pnv_phb3_msi.c index 41e63b066f9..3a83311faff 100644 --- a/hw/pci-host/pnv_phb3_msi.c +++ b/hw/pci-host/pnv_phb3_msi.c @@ -13,10 +13,9 @@ #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" +#include "system/reset.h" static uint64_t phb3_msi_ive_addr(PnvPHB3 *phb, int srcno) { @@ -228,13 +227,13 @@ static void phb3_msi_resend(ICSState *ics) } } -static void phb3_msi_reset_hold(Object *obj) +static void phb3_msi_reset_hold(Object *obj, ResetType type) { Phb3MsiState *msi = PHB3_MSI(obj); ICSStateClass *icsc = ICS_GET_CLASS(obj); if (icsc->parent_phases.hold) { - icsc->parent_phases.hold(obj); + icsc->parent_phases.hold(obj, type); } memset(msi->rba, 0, sizeof(msi->rba)); @@ -281,11 +280,11 @@ 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; } -static void phb3_msi_class_init(ObjectClass *klass, void *data) +static void phb3_msi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ICSStateClass *isc = ICS_CLASS(klass); @@ -316,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; @@ -335,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_phb3_pbcq.c b/hw/pci-host/pnv_phb3_pbcq.c index 82f70efa431..1f7a149580d 100644 --- a/hw/pci-host/pnv_phb3_pbcq.c +++ b/hw/pci-host/pnv_phb3_pbcq.c @@ -337,7 +337,7 @@ static void phb3_pbcq_instance_init(Object *obj) OBJ_PROP_LINK_STRONG); } -static void pnv_pbcq_class_init(ObjectClass *klass, void *data) +static void pnv_pbcq_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); @@ -354,7 +354,7 @@ static const TypeInfo pnv_pbcq_type_info = { .instance_size = sizeof(PnvPBCQState), .instance_init = phb3_pbcq_instance_init, .class_init = pnv_pbcq_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index 6232cbeee16..77ea35299d3 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" @@ -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: @@ -1041,28 +1041,33 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, PEC_NEST_STK_BAR_EN_MMIO1)) { 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"); } - 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"); } - 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 @@ -1353,7 +1362,7 @@ DECLARE_INSTANCE_CHECKER(IOMMUMemoryRegion, PNV_PHB4_IOMMU_MEMORY_REGION, TYPE_PNV_PHB4_IOMMU_MEMORY_REGION) static void pnv_phb4_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); @@ -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]); } @@ -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; } @@ -1675,16 +1688,15 @@ static void pnv_phb4_xive_notify(XiveNotifier *xf, uint32_t srcno, } } -static Property pnv_phb4_properties[] = { +static const Property pnv_phb4_properties[] = { DEFINE_PROP_UINT32("index", PnvPHB4, phb_id, 0), DEFINE_PROP_UINT32("chip-id", PnvPHB4, chip_id, 0), DEFINE_PROP_LINK("pec", PnvPHB4, pec, TYPE_PNV_PHB4_PEC, PnvPhb4PecState *), DEFINE_PROP_LINK("phb-base", PnvPHB4, phb_base, TYPE_PNV_PHB, PnvPHB *), - DEFINE_PROP_END_OF_LIST(), }; -static void pnv_phb4_class_init(ObjectClass *klass, void *data) +static void pnv_phb4_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); XiveNotifierClass *xfc = XIVE_NOTIFIER_CLASS(klass); @@ -1702,7 +1714,7 @@ static const TypeInfo pnv_phb4_type_info = { .instance_init = pnv_phb4_instance_init, .instance_size = sizeof(PnvPHB4), .class_init = pnv_phb4_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XIVE_NOTIFIER }, { }, } @@ -1749,7 +1761,7 @@ static void pnv_phb4_root_bus_set_prop(Object *obj, Visitor *v, } } -static void pnv_phb4_root_bus_class_init(ObjectClass *klass, void *data) +static void pnv_phb4_root_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); @@ -1787,17 +1799,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 3b2850f7a3e..5bac1c42ed0 100644 --- a/hw/pci-host/pnv_phb4_pec.c +++ b/hw/pci-host/pnv_phb4_pec.c @@ -19,7 +19,7 @@ #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_chip.h" #include "hw/qdev-properties.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include @@ -34,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]; } @@ -45,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: @@ -81,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]; } @@ -93,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__, @@ -174,6 +197,9 @@ static PnvPHB *pnv_pec_default_phb_realize(PnvPhb4PecState *pec, return phb; } +#define XPEC_P9_PCI_LANE_CFG PPC_BITMASK(10, 11) +#define XPEC_P10_PCI_LANE_CFG PPC_BITMASK(0, 1) + static void pnv_pec_realize(DeviceState *dev, Error **errp) { PnvPhb4PecState *pec = PNV_PHB4_PEC(dev); @@ -188,6 +214,43 @@ static void pnv_pec_realize(DeviceState *dev, Error **errp) pec->num_phbs = pecc->num_phbs[pec->index]; + /* Pervasive chiplet */ + object_initialize_child(OBJECT(pec), "nest-pervasive-common", + &pec->nest_pervasive, + TYPE_PNV_NEST_CHIPLET_PERVASIVE); + if (!qdev_realize(DEVICE(&pec->nest_pervasive), NULL, errp)) { + return; + } + + /* Set up pervasive chiplet registers */ + /* + * Most registers are not set up, this just sets the PCI CONF1 link-width + * field because skiboot probes it. + */ + if (pecc->version == PNV_PHB4_VERSION) { + /* + * On P9, PEC2 has configurable 1/2/3-furcation). + * Make it trifurcated (x8, x4, x4) to match pnv_pec_num_phbs. + */ + if (pec->index == 2) { + pec->nest_pervasive.control_regs.cplt_cfg1 = + SETFIELD(XPEC_P9_PCI_LANE_CFG, + pec->nest_pervasive.control_regs.cplt_cfg1, + 0b10); + } + } else if (pecc->version == PNV_PHB5_VERSION) { + /* + * On P10, both PECs are configurable 1/2/3-furcation). + * Both are trifurcated to match pnv_phb5_pec_num_stacks. + */ + pec->nest_pervasive.control_regs.cplt_cfg1 = + SETFIELD(XPEC_P10_PCI_LANE_CFG, + pec->nest_pervasive.control_regs.cplt_cfg1, + 0b10); + } else { + g_assert_not_reached(); + } + /* Create PHBs if running with defaults */ if (defaults_enabled()) { g_assert(pec->num_phbs <= MAX_PHBS_PER_PEC); @@ -260,17 +323,23 @@ static int pnv_pec_dt_xscom(PnvXScomInterface *dev, void *fdt, return 0; } -static Property pnv_pec_properties[] = { +static const Property pnv_pec_properties[] = { DEFINE_PROP_UINT32("index", PnvPhb4PecState, index, 0), DEFINE_PROP_UINT32("chip-id", PnvPhb4PecState, chip_id, 0), DEFINE_PROP_LINK("chip", PnvPhb4PecState, chip, TYPE_PNV_CHIP, PnvChip *), - DEFINE_PROP_END_OF_LIST(), }; +#define XPEC_PCI_CPLT_OFFSET 0x1000000ULL + +static uint32_t pnv_pec_xscom_cplt_base(PnvPhb4PecState *pec) +{ + return PNV9_XSCOM_PEC_NEST_CPLT_BASE + XPEC_PCI_CPLT_OFFSET * pec->index; +} + static uint32_t pnv_pec_xscom_pci_base(PnvPhb4PecState *pec) { - return PNV9_XSCOM_PEC_PCI_BASE + 0x1000000 * pec->index; + return PNV9_XSCOM_PEC_PCI_BASE + XPEC_PCI_CPLT_OFFSET * pec->index; } static uint32_t pnv_pec_xscom_nest_base(PnvPhb4PecState *pec) @@ -285,7 +354,7 @@ static uint32_t pnv_pec_xscom_nest_base(PnvPhb4PecState *pec) */ static const uint32_t pnv_pec_num_phbs[] = { 1, 2, 3 }; -static void pnv_pec_class_init(ObjectClass *klass, void *data) +static void pnv_pec_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); @@ -299,6 +368,7 @@ static void pnv_pec_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, pnv_pec_properties); dc->user_creatable = false; + pecc->xscom_cplt_base = pnv_pec_xscom_cplt_base; pecc->xscom_nest_base = pnv_pec_xscom_nest_base; pecc->xscom_pci_base = pnv_pec_xscom_pci_base; pecc->xscom_nest_size = PNV9_XSCOM_PEC_NEST_SIZE; @@ -318,7 +388,7 @@ static const TypeInfo pnv_pec_type_info = { .instance_size = sizeof(PnvPhb4PecState), .class_init = pnv_pec_class_init, .class_size = sizeof(PnvPhb4PecClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } @@ -327,6 +397,10 @@ static const TypeInfo pnv_pec_type_info = { /* * POWER10 definitions */ +static uint32_t pnv_phb5_pec_xscom_cplt_base(PnvPhb4PecState *pec) +{ + return PNV10_XSCOM_PEC_NEST_CPLT_BASE + XPEC_PCI_CPLT_OFFSET * pec->index; +} static uint32_t pnv_phb5_pec_xscom_pci_base(PnvPhb4PecState *pec) { @@ -345,12 +419,13 @@ static uint32_t pnv_phb5_pec_xscom_nest_base(PnvPhb4PecState *pec) */ static const uint32_t pnv_phb5_pec_num_stacks[] = { 3, 3 }; -static void pnv_phb5_pec_class_init(ObjectClass *klass, void *data) +static void pnv_phb5_pec_class_init(ObjectClass *klass, const void *data) { PnvPhb4PecClass *pecc = PNV_PHB4_PEC_CLASS(klass); static const char compat[] = "ibm,power10-pbcq"; static const char stk_compat[] = "ibm,power10-phb-stack"; + pecc->xscom_cplt_base = pnv_phb5_pec_xscom_cplt_base; pecc->xscom_nest_base = pnv_phb5_pec_xscom_nest_base; pecc->xscom_pci_base = pnv_phb5_pec_xscom_pci_base; pecc->xscom_nest_size = PNV10_XSCOM_PEC_NEST_SIZE; @@ -370,7 +445,7 @@ static const TypeInfo pnv_phb5_pec_type_info = { .instance_size = sizeof(PnvPhb4PecState), .class_init = pnv_phb5_pec_class_init, .class_size = sizeof(PnvPhb4PecClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/ppc/ppc440_pcix.c b/hw/pci-host/ppc440_pcix.c similarity index 96% rename from hw/ppc/ppc440_pcix.c rename to hw/pci-host/ppc440_pcix.c index 672090de947..744b85e49cc 100644 --- a/hw/ppc/ppc440_pcix.c +++ b/hw/pci-host/ppc440_pcix.c @@ -25,8 +25,7 @@ #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-host/ppc4xx.h" #include "hw/pci/pci_device.h" #include "hw/pci/pci_host.h" #include "trace.h" @@ -53,7 +52,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(PPC440PCIXState, PPC440_PCIX_HOST) 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; @@ -172,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: @@ -303,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: @@ -449,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 @@ -495,15 +498,12 @@ static void ppc440_pcix_realize(DeviceState *dev, Error **errp) 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, &s->iomem, - PCI_DEVFN(0, 0), 1, TYPE_PCI_BUS); - - s->dev = pci_create_simple(h->bus, PCI_DEVFN(0, 0), - TYPE_PPC4xx_HOST_BRIDGE); + 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, @@ -519,12 +519,12 @@ static void ppc440_pcix_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); } -static void ppc440_pcix_class_init(ObjectClass *klass, void *data) +static void ppc440_pcix_class_init(ObjectClass *klass, const 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 = { 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 66521190086..25478176881 100644 --- a/hw/ppc/ppc4xx_pci.c +++ b/hw/pci-host/ppc4xx_pci.c @@ -24,11 +24,10 @@ #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 "system/reset.h" #include "hw/pci/pci_device.h" #include "hw/pci/pci_host.h" #include "trace.h" @@ -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), @@ -350,7 +349,7 @@ static void ppc4xx_pcihost_realize(DeviceState *dev, Error **errp) qemu_register_reset(ppc4xx_pci_reset, s); } -static void ppc4xx_host_bridge_class_init(ObjectClass *klass, void *data) +static void ppc4xx_host_bridge_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -371,13 +370,13 @@ static const TypeInfo ppc4xx_host_bridge_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = ppc4xx_host_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -static void ppc4xx_pcihost_class_init(ObjectClass *klass, void *data) +static void ppc4xx_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index 38814247f2a..52269b05bb1 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 @@ -16,13 +16,11 @@ #include "qemu/osdep.h" #include "hw/irq.h" -#include "hw/ppc/e500-ccsr.h" #include "hw/qdev-properties.h" #include "migration/vmstate.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" @@ -379,7 +377,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 +390,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 +403,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, @@ -419,11 +417,12 @@ static const VMStateDescription vmstate_ppce500_pci = { static void e500_pcihost_bridge_realize(PCIDevice *d, Error **errp) { PPCE500PCIBridgeState *b = PPC_E500_PCI_BRIDGE(d); - PPCE500CCSRState *ccsr = CCSR(container_get(qdev_get_machine(), - "/e500-ccsr")); + SysBusDevice *ccsr = SYS_BUS_DEVICE( + object_resolve_path_component(qdev_get_machine(), "e500-ccsr")); + MemoryRegion *ccsr_space = sysbus_mmio_get_region(ccsr, 0); - memory_region_init_alias(&b->bar0, OBJECT(ccsr), "e500-pci-bar0", &ccsr->ccsr_space, - 0, int128_get64(ccsr->ccsr_space.size)); + memory_region_init_alias(&b->bar0, OBJECT(ccsr), "e500-pci-bar0", + ccsr_space, 0, int128_get64(ccsr_space->size)); pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &b->bar0); } @@ -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, @@ -487,7 +490,7 @@ static void e500_pcihost_realize(DeviceState *dev, Error **errp) pci_bus_set_route_irq_fn(b, e500_route_intx_pin_to_irq); } -static void e500_host_bridge_class_init(ObjectClass *klass, void *data) +static void e500_host_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -504,24 +507,12 @@ 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[] = { +static const Property pcihost_properties[] = { DEFINE_PROP_UINT32("first_slot", PPCE500PCIState, first_slot, 0x11), DEFINE_PROP_UINT32("first_pin_irq", PPCE500PCIState, first_pin_irq, 0x1), - DEFINE_PROP_END_OF_LIST(), }; -static void e500_pcihost_class_init(ObjectClass *klass, void *data) +static void e500_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -531,17 +522,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 = (const 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 91c46df9ae3..1951ae440cc 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -73,12 +73,6 @@ static void q35_host_realize(DeviceState *dev, Error **errp) 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"; } @@ -176,21 +170,21 @@ static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v, * properties need to be initialized manually by * q35_host_initfn() after the object_initialize() call. */ -static Property q35_host_props[] = { +static const Property q35_host_props[] = { DEFINE_PROP_UINT64(PCIE_HOST_MCFG_BASE, Q35PCIHost, parent_obj.base_addr, 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(), }; -static void q35_host_class_init(ObjectClass *klass, void *data) +static void q35_host_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); @@ -221,6 +215,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); @@ -483,6 +478,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); @@ -501,10 +500,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. @@ -527,7 +529,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. @@ -545,18 +547,20 @@ 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); } @@ -575,6 +579,20 @@ static void mch_realize(PCIDevice *d, Error **errp) /* setup pci memory mapping */ 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", mch->pci_address_space, MCH_HOST_BRIDGE_SMRAM_C_BASE, @@ -641,42 +659,22 @@ static void mch_realize(PCIDevice *d, Error **errp) object_property_add_const_link(qdev_get_machine(), "smram", OBJECT(&mch->smram)); - - 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); - } -} - -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[] = { +static const Property mch_props[] = { DEFINE_PROP_UINT16("extended-tseg-mbytes", MCHPCIState, ext_tseg_mbytes, 16), DEFINE_PROP_BOOL("smbase-smram", MCHPCIState, has_smram_at_smbase, true), - DEFINE_PROP_END_OF_LIST(), }; -static void mch_class_init(ObjectClass *klass, void *data) +static void mch_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); 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"; @@ -705,7 +703,7 @@ static const TypeInfo mch_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(MCHPCIState), .class_init = mch_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index 9a11ac4b2b6..f8c0be5d21c 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -24,7 +24,6 @@ */ #include "qemu/osdep.h" -#include "qemu/datadir.h" #include "qemu/units.h" #include "qemu/log.h" #include "qapi/error.h" @@ -35,9 +34,7 @@ #include "migration/vmstate.h" #include "hw/intc/i8259.h" #include "hw/irq.h" -#include "hw/loader.h" #include "hw/or-irq.h" -#include "elf.h" #include "qom/object.h" #define TYPE_RAVEN_PCI_DEVICE "raven" @@ -47,10 +44,6 @@ OBJECT_DECLARE_SIMPLE_TYPE(RavenPCIState, RAVEN_PCI_DEVICE) struct RavenPCIState { PCIDevice dev; - - uint32_t elf_machine; - char *bios_name; - MemoryRegion bios; }; typedef struct PRePPCIState PREPPCIState; @@ -75,11 +68,8 @@ struct PRePPCIState { RavenPCIState pci_dev; int contiguous_map; - bool is_legacy_prep; }; -#define BIOS_SIZE (1 * MiB) - #define PCI_IO_BASE_ADDR 0x80000000 /* Physical address on main bus */ static inline uint32_t raven_pci_io_config(hwaddr addr) @@ -200,6 +190,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 +214,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; @@ -238,22 +233,18 @@ static void raven_pcihost_realizefn(DeviceState *d, Error **errp) MemoryRegion *address_space_mem = get_system_memory(); int i; - if (s->is_legacy_prep) { - for (i = 0; i < PCI_NUM_PINS; i++) { - sysbus_init_irq(dev, &s->pci_irqs[i]); - } - } else { - /* According to PReP specification section 6.1.6 "System Interrupt - * Assignments", all PCI interrupts are routed via IRQ 15 */ - s->or_irq = OR_IRQ(object_new(TYPE_OR_IRQ)); - object_property_set_int(OBJECT(s->or_irq), "num-lines", PCI_NUM_PINS, - &error_fatal); - qdev_realize(DEVICE(s->or_irq), NULL, &error_fatal); - sysbus_init_irq(dev, &s->or_irq->out_irq); - - for (i = 0; i < PCI_NUM_PINS; i++) { - s->pci_irqs[i] = qdev_get_gpio_in(DEVICE(s->or_irq), i); - } + /* + * According to PReP specification section 6.1.6 "System Interrupt + * Assignments", all PCI interrupts are routed via IRQ 15 + */ + s->or_irq = OR_IRQ(object_new(TYPE_OR_IRQ)); + object_property_set_int(OBJECT(s->or_irq), "num-lines", PCI_NUM_PINS, + &error_fatal); + qdev_realize(DEVICE(s->or_irq), NULL, &error_fatal); + sysbus_init_irq(dev, &s->or_irq->out_irq); + + for (i = 0; i < PCI_NUM_PINS; i++) { + s->pci_irqs[i] = qdev_get_gpio_in(DEVICE(s->or_irq), i); } qdev_init_gpio_in(d, raven_change_gpio, 1); @@ -320,7 +311,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; @@ -333,59 +324,22 @@ static void raven_pcihost_initfn(Object *obj) static void raven_realize(PCIDevice *d, Error **errp) { - RavenPCIState *s = RAVEN_PCI_DEVICE(d); - char *filename; - int bios_size = -1; - 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); - memory_region_add_subregion(get_system_memory(), (uint32_t)(-BIOS_SIZE), - &s->bios); - if (s->bios_name) { - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, s->bios_name); - if (filename) { - if (s->elf_machine != EM_NONE) { - bios_size = load_elf(filename, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, 1, s->elf_machine, - 0, 0); - } - if (bios_size < 0) { - bios_size = get_image_size(filename); - if (bios_size > 0 && bios_size <= BIOS_SIZE) { - hwaddr bios_addr; - bios_size = (bios_size + 0xfff) & ~0xfff; - bios_addr = (uint32_t)(-BIOS_SIZE); - bios_size = load_image_targphys(filename, bios_addr, - bios_size); - } - } - } - g_free(filename); - if (bios_size < 0 || bios_size > BIOS_SIZE) { - memory_region_del_subregion(get_system_memory(), &s->bios); - error_setg(errp, "Could not load bios image '%s'", s->bios_name); - return; - } - } - - vmstate_register_ram_global(&s->bios); } 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() }, }; -static void raven_class_init(ObjectClass *klass, void *data) +static void raven_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -409,29 +363,18 @@ static const TypeInfo raven_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(RavenPCIState), .class_init = raven_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -static Property raven_pcihost_properties[] = { - DEFINE_PROP_UINT32("elf-machine", PREPPCIState, pci_dev.elf_machine, - EM_NONE), - DEFINE_PROP_STRING("bios-name", PREPPCIState, pci_dev.bios_name), - /* Temporary workaround until legacy prep machine is removed */ - DEFINE_PROP_BOOL("is-legacy-prep", PREPPCIState, is_legacy_prep, - false), - DEFINE_PROP_END_OF_LIST() -}; - -static void raven_pcihost_class_init(ObjectClass *klass, void *data) +static void raven_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->realize = raven_pcihost_realizefn; - device_class_set_props(dc, raven_pcihost_properties); dc->fw_name = "pci"; } diff --git a/hw/pci-host/remote.c b/hw/pci-host/remote.c index bfb25ef6af8..e6d2af4502a 100644 --- a/hw/pci-host/remote.c +++ b/hw/pci-host/remote.c @@ -28,7 +28,7 @@ #include "hw/pci/pcie_host.h" #include "hw/qdev-properties.h" #include "hw/pci-host/remote.h" -#include "exec/memory.h" +#include "system/memory.h" static const char *remote_pcihost_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) @@ -46,7 +46,7 @@ static void remote_pcihost_realize(DeviceState *dev, Error **errp) 0, TYPE_PCIE_BUS); } -static void remote_pcihost_class_init(ObjectClass *klass, void *data) +static void remote_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); diff --git a/hw/pci-host/sabre.c b/hw/pci-host/sabre.c index dcb2e230b67..538624c5079 100644 --- a/hw/pci-host/sabre.c +++ b/hw/pci-host/sabre.c @@ -37,7 +37,7 @@ #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "trace.h" /* @@ -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,7 +388,7 @@ 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), TYPE_SIMBA_PCI_BRIDGE); @@ -452,7 +456,7 @@ static void sabre_pci_realize(PCIDevice *d, Error **errp) PCI_STATUS_DEVSEL_MEDIUM); } -static void sabre_pci_class_init(ObjectClass *klass, void *data) +static void sabre_pci_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -473,7 +477,7 @@ static const TypeInfo sabre_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(SabrePCIState), .class_init = sabre_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -488,19 +492,18 @@ static char *sabre_ofw_unit_address(const SysBusDevice *dev) (uint32_t)(s->special_base & 0xffffffff)); } -static Property sabre_properties[] = { +static const Property sabre_properties[] = { DEFINE_PROP_UINT64("special-base", SabreState, special_base, 0), DEFINE_PROP_UINT64("mem-base", SabreState, mem_base, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void sabre_class_init(ObjectClass *klass, void *data) +static void sabre_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 77e7bbc65fc..de8f6a84aab 100644 --- a/hw/pci-host/sh_pci.c +++ b/hw/pci-host/sh_pci.c @@ -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, const 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, const 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 = (const 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 9d216bb89f6..0a816b9aa12 100644 --- a/hw/pci-host/trace-events +++ b/hw/pci-host/trace-events @@ -37,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 @@ -46,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 e4c1abd8715..194037d6e75 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -311,7 +311,7 @@ static void unin_internal_pci_host_realize(PCIDevice *d, Error **errp) d->config[PCI_CAPABILITY_LIST] = 0x00; } -static void unin_main_pci_host_class_init(ObjectClass *klass, void *data) +static void unin_main_pci_host_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -333,13 +333,13 @@ static const TypeInfo unin_main_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = unin_main_pci_host_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -static void u3_agp_pci_host_class_init(ObjectClass *klass, void *data) +static void u3_agp_pci_host_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -361,13 +361,13 @@ static const TypeInfo u3_agp_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = u3_agp_pci_host_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -static void unin_agp_pci_host_class_init(ObjectClass *klass, void *data) +static void unin_agp_pci_host_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -389,13 +389,14 @@ static const TypeInfo unin_agp_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = unin_agp_pci_host_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -static void unin_internal_pci_host_class_init(ObjectClass *klass, void *data) +static void unin_internal_pci_host_class_init(ObjectClass *klass, + const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -417,18 +418,17 @@ static const TypeInfo unin_internal_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = unin_internal_pci_host_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -static Property pci_unin_main_pci_host_props[] = { +static const Property pci_unin_main_pci_host_props[] = { DEFINE_PROP_UINT32("ofw-addr", UNINHostState, ofw_addr, -1), - DEFINE_PROP_END_OF_LIST() }; -static void pci_unin_main_class_init(ObjectClass *klass, void *data) +static void pci_unin_main_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); @@ -448,7 +448,7 @@ static const TypeInfo pci_unin_main_info = { .class_init = pci_unin_main_class_init, }; -static void pci_u3_agp_class_init(ObjectClass *klass, void *data) +static void pci_u3_agp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -464,7 +464,7 @@ static const TypeInfo pci_u3_agp_info = { .class_init = pci_u3_agp_class_init, }; -static void pci_unin_agp_class_init(ObjectClass *klass, void *data) +static void pci_unin_agp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -480,7 +480,7 @@ static const TypeInfo pci_unin_agp_info = { .class_init = pci_unin_agp_class_init, }; -static void pci_unin_internal_class_init(ObjectClass *klass, void *data) +static void pci_unin_internal_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -536,7 +536,7 @@ static void unin_init(Object *obj) sysbus_init_mmio(sbd, &s->mem); } -static void unin_class_init(ObjectClass *klass, void *data) +static void unin_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index 60d4e7cd923..8ea26e3ff00 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -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), @@ -246,7 +246,7 @@ static uint64_t pci_vpb_reg_read(void *opaque, hwaddr addr, static const MemoryRegionOps pci_vpb_reg_ops = { .read = pci_vpb_reg_read, .write = pci_vpb_reg_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -312,7 +312,7 @@ static uint64_t pci_vpb_config_read(void *opaque, hwaddr addr, static const MemoryRegionOps pci_vpb_config_ops = { .read = pci_vpb_config_read, .write = pci_vpb_config_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static int pci_vpb_map_irq(PCIDevice *d, int irq_num) @@ -471,7 +471,7 @@ static void versatile_pci_host_realize(PCIDevice *d, Error **errp) pci_set_byte(d->config + PCI_LATENCY_TIMER, 0x10); } -static void versatile_pci_host_class_init(ObjectClass *klass, void *data) +static void versatile_pci_host_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -492,24 +492,23 @@ static const TypeInfo versatile_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = versatile_pci_host_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -static Property pci_vpb_properties[] = { +static const Property pci_vpb_properties[] = { DEFINE_PROP_UINT8("broken-irq-mapping", PCIVPBState, irq_mapping_prop, PCI_VPB_IRQMAP_ASSUME_OK), - DEFINE_PROP_END_OF_LIST() }; -static void pci_vpb_class_init(ObjectClass *klass, void *data) +static void pci_vpb_class_init(ObjectClass *klass, const 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/xen_igd_pt.c b/hw/pci-host/xen_igd_pt.c index d094b675d6d..5dd17ef236f 100644 --- a/hw/pci-host/xen_igd_pt.c +++ b/hw/pci-host/xen_igd_pt.c @@ -95,7 +95,8 @@ static void igd_pt_i440fx_realize(PCIDevice *pci_dev, Error **errp) } } -static void igd_passthrough_i440fx_class_init(ObjectClass *klass, void *data) +static void igd_passthrough_i440fx_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c index c9ab7052f43..c71492de9e7 100644 --- a/hw/pci-host/xilinx-pcie.c +++ b/hw/pci-host/xilinx-pcie.c @@ -156,17 +156,16 @@ static void xilinx_pcie_host_init(Object *obj) qdev_prop_set_bit(DEVICE(root), "multifunction", false); } -static Property xilinx_pcie_host_props[] = { +static const Property xilinx_pcie_host_props[] = { DEFINE_PROP_UINT32("bus_nr", XilinxPCIEHost, bus_nr, 0), DEFINE_PROP_SIZE("cfg_base", XilinxPCIEHost, cfg_base, 0), DEFINE_PROP_SIZE("cfg_size", XilinxPCIEHost, cfg_size, 32 * MiB), DEFINE_PROP_SIZE("mmio_base", XilinxPCIEHost, mmio_base, 0), DEFINE_PROP_SIZE("mmio_size", XilinxPCIEHost, mmio_size, 1 * MiB), DEFINE_PROP_BOOL("link_up", XilinxPCIEHost, link_up, true), - DEFINE_PROP_END_OF_LIST(), }; -static void xilinx_pcie_host_class_init(ObjectClass *klass, void *data) +static void xilinx_pcie_host_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); @@ -287,7 +286,7 @@ static void xilinx_pcie_root_realize(PCIDevice *pci_dev, Error **errp) } } -static void xilinx_pcie_root_class_init(ObjectClass *klass, void *data) +static void xilinx_pcie_root_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -300,7 +299,7 @@ static void xilinx_pcie_root_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_BRIDGE_HOST; 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; /* @@ -315,7 +314,7 @@ static const TypeInfo xilinx_pcie_root_info = { .parent = TYPE_PCI_BRIDGE, .instance_size = sizeof(XilinxPCIERoot), .class_init = xilinx_pcie_root_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/pci/meson.build b/hw/pci/meson.build index b1855452f5b..b9c34b2acfe 100644 --- a/hw/pci/meson.build +++ b/hw/pci/meson.build @@ -20,4 +20,3 @@ system_ss.add(when: 'CONFIG_PCI_EXPRESS', if_true: files('pcie_port.c', 'pcie_ho system_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) system_ss.add(when: 'CONFIG_PCI', if_false: files('pci-stub.c')) -system_ss.add(when: 'CONFIG_ALL', if_true: files('pci-stub.c')) diff --git a/hw/pci/msi.c b/hw/pci/msi.c index 041b0bdbec4..b9f5b45920b 100644 --- a/hw/pci/msi.c +++ b/hw/pci/msi.c @@ -23,6 +23,7 @@ #include "hw/xen/xen.h" #include "qemu/range.h" #include "qapi/error.h" +#include "system/xen.h" #include "hw/i386/kvm/xen_evtchn.h" @@ -308,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; } diff --git a/hw/pci/msix.c b/hw/pci/msix.c index ab8869d9d07..8c7f6709e2a 100644 --- a/hw/pci/msix.c +++ b/hw/pci/msix.c @@ -15,11 +15,12 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/pci/pci.h" #include "hw/xen/xen.h" -#include "sysemu/xen.h" +#include "system/xen.h" #include "migration/qemu-file-types.h" #include "migration/vmstate.h" #include "qemu/range.h" @@ -71,7 +72,7 @@ static uint8_t *msix_pending_byte(PCIDevice *dev, int vector) return dev->msix_pba + vector / 8; } -static int msix_is_pending(PCIDevice *dev, int vector) +int msix_is_pending(PCIDevice *dev, unsigned int vector) { return *msix_pending_byte(dev, vector) & msix_pending_mask(vector); } @@ -250,7 +251,7 @@ static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr, PCIDevice *dev = opaque; if (dev->msix_vector_poll_notifier) { unsigned vector_start = addr * 8; - unsigned vector_end = MIN(addr + size * 8, dev->msix_entries_nr); + unsigned vector_end = MIN((addr + size) * 8, dev->msix_entries_nr); dev->msix_vector_poll_notifier(dev, vector_start, vector_end); } @@ -260,6 +261,14 @@ static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr, static void msix_pba_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { + PCIDevice *dev = opaque; + + qemu_log_mask(LOG_GUEST_ERROR, + "PCI [%s:%02x:%02x.%x] attempt to write to MSI-X " + "PBA at 0x%" FMT_PCIBUS ", ignoring.\n", + pci_root_bus_path(dev), pci_dev_bus_num(dev), + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + addr); } static const MemoryRegionOps msix_pba_mmio_ops = { @@ -648,6 +657,7 @@ int msix_set_vector_notifiers(PCIDevice *dev, } dev->msix_vector_use_notifier = NULL; dev->msix_vector_release_notifier = NULL; + dev->msix_vector_poll_notifier = NULL; return ret; } @@ -684,7 +694,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, @@ -692,7 +702,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 index b09fce93770..a5f6483cc3d 100644 --- a/hw/pci/pci-hmp-cmds.c +++ b/hw/pci/pci-hmp-cmds.c @@ -20,7 +20,7 @@ #include "monitor/monitor.h" #include "pci-internal.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qapi-commands-pci.h" #include "qemu/cutils.h" @@ -83,15 +83,25 @@ static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev) 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); + 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 { - 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); + 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" : ""); + } } } diff --git a/hw/pci/pci-stub.c b/hw/pci/pci-stub.c index f0508682d2b..3397d0c82ea 100644 --- a/hw/pci/pci-stub.c +++ b/hw/pci/pci-stub.c @@ -46,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 881d774fb64..c70b5ceebaf 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -32,12 +32,13 @@ #include "hw/pci/pci_host.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" +#include "migration/cpr.h" #include "migration/qemu-file-types.h" #include "migration/vmstate.h" #include "net/net.h" -#include "sysemu/numa.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/numa.h" +#include "system/runstate.h" +#include "system/system.h" #include "hw/loader.h" #include "qemu/error-report.h" #include "qemu/range.h" @@ -46,6 +47,7 @@ #include "hw/pci/msix.h" #include "hw/hotplug.h" #include "hw/boards.h" +#include "hw/nvram/fw_cfg.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "pci-internal.h" @@ -53,25 +55,31 @@ #include "hw/xen/xen.h" #include "hw/i386/kvm/xen_evtchn.h" -//#define DEBUG_PCI -#ifdef DEBUG_PCI -# define PCI_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) -#else -# define PCI_DPRINTF(format, ...) do { } while (0) -#endif - bool pci_available = true; 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 Property pci_props[] = { +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 = { + .type = "busnr", + .get = prop_pci_busnr_get, +}; + +static const 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("rombar", PCIDevice, rom_bar, 1), + DEFINE_PROP_UINT32("romsize", PCIDevice, romsize, UINT32_MAX), + DEFINE_PROP_INT32("rombar", PCIDevice, rom_bar, -1), DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present, QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false), DEFINE_PROP_BIT("x-pcie-lnksta-dllla", PCIDevice, cap_present, @@ -85,14 +93,19 @@ static Property pci_props[] = { QEMU_PCIE_ERR_UNC_MASK_BITNR, true), DEFINE_PROP_BIT("x-pcie-ari-nextfn-1", PCIDevice, cap_present, QEMU_PCIE_ARI_NEXTFN_1_BITNR, false), - DEFINE_PROP_END_OF_LIST() + DEFINE_PROP_SIZE32("x-max-bounce-buffer-size", PCIDevice, + max_bounce_buffer_size, DEFAULT_MAX_BOUNCE_BUFFER_SIZE), + DEFINE_PROP_STRING("sriov-pf", PCIDevice, sriov_pf), + DEFINE_PROP_BIT("x-pcie-ext-tag", PCIDevice, cap_present, + QEMU_PCIE_EXT_TAG_BITNR, true), + { .name = "busnr", .info = &prop_pci_busnr }, }; 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, @@ -116,6 +129,12 @@ static GSequence *pci_acpi_index_list(void) return used_acpi_index_list; } +static void pci_set_master(PCIDevice *d, bool enable) +{ + memory_region_set_enabled(&d->bus_master_enable_region, enable); + d->is_master = enable; /* cache the status */ +} + static void pci_init_bus_master(PCIDevice *pci_dev) { AddressSpace *dma_as = pci_device_iommu_address_space(pci_dev); @@ -123,7 +142,7 @@ static void pci_init_bus_master(PCIDevice *pci_dev) memory_region_init_alias(&pci_dev->bus_master_enable_region, OBJECT(pci_dev), "bus master", dma_as->root, 0, memory_region_size(dma_as->root)); - memory_region_set_enabled(&pci_dev->bus_master_enable_region, false); + pci_set_master(pci_dev, false); memory_region_add_subregion(&pci_dev->bus_master_container_region, 0, &pci_dev->bus_master_enable_region); } @@ -147,7 +166,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) @@ -198,20 +217,70 @@ static uint16_t pcibus_numa_node(PCIBus *bus) return NUMA_NODE_UNASSIGNED; } -static void pci_bus_class_init(ObjectClass *klass, void *data) +bool pci_bus_add_fw_cfg_extra_pci_roots(FWCfgState *fw_cfg, + PCIBus *bus, + Error **errp) +{ + Object *obj; + + if (!bus) { + return true; + } + obj = OBJECT(bus); + + return fw_cfg_add_file_from_generator(fw_cfg, obj->parent, + object_get_canonical_path_component(obj), + "etc/extra-pci-roots", errp); +} + +static GByteArray *pci_bus_fw_cfg_gen_data(Object *obj, Error **errp) +{ + PCIBus *bus = PCI_BUS(obj); + GByteArray *byte_array; + uint64_t extra_hosts = 0; + + if (!bus) { + return NULL; + } + + QLIST_FOREACH(bus, &bus->child, sibling) { + /* look for expander root buses */ + if (pci_bus_is_root(bus)) { + extra_hosts++; + } + } + + if (!extra_hosts) { + return NULL; + } + extra_hosts = cpu_to_le64(extra_hosts); + + byte_array = g_byte_array_new(); + g_byte_array_append(byte_array, + (const void *)&extra_hosts, sizeof(extra_hosts)); + + return byte_array; +} + +static void pci_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); PCIBusClass *pbc = PCI_BUS_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + FWCfgDataGeneratorClass *fwgc = FW_CFG_DATA_GENERATOR_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; + + fwgc->get_data = pci_bus_fw_cfg_gen_data; } static const TypeInfo pci_bus_info = { @@ -220,6 +289,10 @@ static const TypeInfo pci_bus_info = { .instance_size = sizeof(PCIBus), .class_size = sizeof(PCIBusClass), .class_init = pci_bus_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_FW_CFG_DATA_GENERATOR_INTERFACE }, + { } + } }; static const TypeInfo cxl_interface_info = { @@ -237,7 +310,7 @@ static const TypeInfo conventional_pci_interface_info = { .parent = TYPE_INTERFACE, }; -static void pcie_bus_class_init(ObjectClass *klass, void *data) +static void pcie_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); @@ -363,6 +436,84 @@ static void pci_msi_trigger(PCIDevice *dev, MSIMessage msg) attrs, NULL); } +/* + * Register and track a PM capability. If wmask is also enabled for the power + * state field of the pmcsr register, guest writes may change the device PM + * state. BAR access is only enabled while the device is in the D0 state. + * Return the capability offset or negative error code. + */ +int pci_pm_init(PCIDevice *d, uint8_t offset, Error **errp) +{ + int cap = pci_add_capability(d, PCI_CAP_ID_PM, offset, PCI_PM_SIZEOF, errp); + + if (cap < 0) { + return cap; + } + + d->pm_cap = cap; + d->cap_present |= QEMU_PCI_CAP_PM; + + return cap; +} + +static uint8_t pci_pm_state(PCIDevice *d) +{ + uint16_t pmcsr; + + if (!(d->cap_present & QEMU_PCI_CAP_PM)) { + return 0; + } + + pmcsr = pci_get_word(d->config + d->pm_cap + PCI_PM_CTRL); + + return pmcsr & PCI_PM_CTRL_STATE_MASK; +} + +/* + * Update the PM capability state based on the new value stored in config + * space respective to the old, pre-write state provided. If the new value + * is rejected (unsupported or invalid transition) restore the old value. + * Return the resulting PM state. + */ +static uint8_t pci_pm_update(PCIDevice *d, uint32_t addr, int l, uint8_t old) +{ + uint16_t pmc; + uint8_t new; + + if (!(d->cap_present & QEMU_PCI_CAP_PM) || + !range_covers_byte(addr, l, d->pm_cap + PCI_PM_CTRL)) { + return old; + } + + new = pci_pm_state(d); + if (new == old) { + return old; + } + + pmc = pci_get_word(d->config + d->pm_cap + PCI_PM_PMC); + + /* + * Transitions to D1 & D2 are only allowed if supported. Devices may + * only transition to higher D-states or to D0. + */ + if ((!(pmc & PCI_PM_CAP_D1) && new == 1) || + (!(pmc & PCI_PM_CAP_D2) && new == 2) || + (old && new && new < old)) { + pci_word_test_and_clear_mask(d->config + d->pm_cap + PCI_PM_CTRL, + PCI_PM_CTRL_STATE_MASK); + pci_word_test_and_set_mask(d->config + d->pm_cap + PCI_PM_CTRL, + old); + trace_pci_pm_bad_transition(d->name, pci_dev_bus_num(d), + PCI_SLOT(d->devfn), PCI_FUNC(d->devfn), + old, new); + return old; + } + + trace_pci_pm_transition(d->name, pci_dev_bus_num(d), PCI_SLOT(d->devfn), + PCI_FUNC(d->devfn), old, new); + return new; +} + static void pci_reset_regions(PCIDevice *dev) { int r; @@ -387,6 +538,10 @@ static void pci_reset_regions(PCIDevice *dev) static void pci_do_device_reset(PCIDevice *dev) { + if ((dev->cap_present & QEMU_PCI_SKIP_RESET_ON_CPR) && cpr_is_incoming()) { + return; + } + pci_device_deassert_intx(dev); assert(dev->irq_state == 0); @@ -402,11 +557,17 @@ static void pci_do_device_reset(PCIDevice *dev) pci_get_word(dev->wmask + PCI_INTERRUPT_LINE) | pci_get_word(dev->w1cmask + PCI_INTERRUPT_LINE)); dev->config[PCI_CACHE_LINE_SIZE] = 0x0; + /* Default PM state is D0 */ + if (dev->cap_present & QEMU_PCI_CAP_PM) { + pci_word_test_and_clear_mask(dev->config + dev->pm_cap + PCI_PM_CTRL, + PCI_PM_CTRL_STATE_MASK); + } pci_reset_regions(dev); pci_update_mappings(dev); msi_reset(dev); msix_reset(dev); + pcie_sriov_pf_reset(dev); } /* @@ -424,9 +585,9 @@ void pci_device_reset(PCIDevice *dev) * 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) { @@ -500,15 +661,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 */ @@ -529,25 +689,21 @@ 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) { 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; } @@ -586,15 +742,13 @@ void pci_bus_irqs_cleanup(PCIBus *bus) 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); + 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; @@ -661,9 +815,8 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size, pci_bridge_update_mappings(PCI_BRIDGE(s)); } - memory_region_set_enabled(&s->bus_master_enable_region, - pci_get_word(s->config + PCI_COMMAND) - & PCI_COMMAND_MASTER); + pci_set_master(s, pci_get_word(s->config + PCI_COMMAND) + & PCI_COMMAND_MASTER); g_free(config); return 0; @@ -680,7 +833,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, @@ -721,7 +874,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, @@ -737,11 +890,18 @@ static bool migrate_is_not_pcie(void *opaque, int version_id) return !pci_is_express((PCIDevice *)opaque); } +static int pci_post_load(void *opaque, int version_id) +{ + pcie_sriov_pf_post_load(opaque); + return 0; +} + const VMStateDescription vmstate_pci_device = { .name = "PCIDevice", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .post_load = pci_post_load, + .fields = (const VMStateField[]) { VMSTATE_INT32_POSITIVE_LE(version_id, PCIDevice), VMSTATE_BUFFER_UNSAFE_INFO_TEST(config, PCIDevice, migrate_is_not_pcie, @@ -893,7 +1053,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 */ @@ -956,13 +1116,8 @@ static void pci_init_multifunction(PCIBus *bus, PCIDevice *dev, Error **errp) dev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; } - /* - * With SR/IOV and ARI, a device at function 0 need not be a multifunction - * device, as it may just be a VF that ended up with function 0 in - * the legacy PCI interpretation. Avoid failing in such cases: - */ - if (pci_is_vf(dev) && - dev->exp.sriov_vf.pf->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + /* SR/IOV is not handled here. */ + if (pci_is_vf(dev)) { return; } @@ -995,7 +1150,8 @@ static void pci_init_multifunction(PCIBus *bus, PCIDevice *dev, Error **errp) } /* function 0 indicates single function, so function > 0 must be NULL */ for (func = 1; func < PCI_FUNC_MAX; ++func) { - if (bus->devices[PCI_DEVFN(slot, func)]) { + PCIDevice *device = bus->devices[PCI_DEVFN(slot, func)]; + if (device && !pci_is_vf(device)) { error_setg(errp, "PCI: %x.0 indicates single function, " "but %x.%x is already populated.", slot, slot, func); @@ -1183,14 +1339,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; - } /* - * Populating function 0 triggers a scan from the guest that - * exposes other non-zero functions. Hence we need to ensure that - * function 0 wasn't added yet. - */ - else if (dev->hotplugged && - !pci_is_vf(pci_dev) && - pci_get_function_0(pci_dev)) { + } + + /* + * 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), @@ -1208,6 +1365,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); @@ -1280,6 +1439,7 @@ static void pci_qdev_unrealize(DeviceState *dev) pci_unregister_io_regions(pci_dev); pci_del_option_rom(pci_dev); + pcie_sriov_unregister_device(pci_dev); if (pc->exit) { pc->exit(pci_dev); @@ -1311,7 +1471,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, pcibus_t size = memory_region_size(memory); uint8_t hdr_type; - assert(!pci_is_vf(pci_dev)); /* VFs must use pcie_sriov_vf_register_bar */ assert(region_num >= 0); assert(region_num < PCI_NUM_REGIONS); assert(is_power_of_2(size)); @@ -1322,7 +1481,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, assert(hdr_type != PCI_HEADER_TYPE_BRIDGE || region_num < 2); r = &pci_dev->io_regions[region_num]; - r->addr = PCI_BAR_UNMAPPED; + assert(!r->size); r->size = size; r->type = type; r->memory = memory; @@ -1330,22 +1489,35 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, ? pci_get_bus(pci_dev)->address_space_io : pci_get_bus(pci_dev)->address_space_mem; - wmask = ~(size - 1); - if (region_num == PCI_ROM_SLOT) { - /* ROM enable bit is writable */ - wmask |= PCI_ROM_ADDRESS_ENABLE; - } - - addr = pci_bar(pci_dev, region_num); - pci_set_long(pci_dev->config + addr, type); + if (pci_is_vf(pci_dev)) { + PCIDevice *pf = pci_dev->exp.sriov_vf.pf; + assert(!pf || type == pf->exp.sriov_pf.vf_bar_type[region_num]); - if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) && - r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { - pci_set_quad(pci_dev->wmask + addr, wmask); - pci_set_quad(pci_dev->cmask + addr, ~0ULL); + r->addr = pci_bar_address(pci_dev, region_num, r->type, r->size); + if (r->addr != PCI_BAR_UNMAPPED) { + memory_region_add_subregion_overlap(r->address_space, + r->addr, r->memory, 1); + } } else { - pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff); - pci_set_long(pci_dev->cmask + addr, 0xffffffff); + r->addr = PCI_BAR_UNMAPPED; + + wmask = ~(size - 1); + if (region_num == PCI_ROM_SLOT) { + /* ROM enable bit is writable */ + wmask |= PCI_ROM_ADDRESS_ENABLE; + } + + addr = pci_bar(pci_dev, region_num); + pci_set_long(pci_dev->config + addr, type); + + if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) && + r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { + pci_set_quad(pci_dev->wmask + addr, wmask); + pci_set_quad(pci_dev->cmask + addr, ~0ULL); + } else { + pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff); + pci_set_long(pci_dev->cmask + addr, 0xffffffff); + } } } @@ -1434,7 +1606,11 @@ static pcibus_t pci_config_get_bar_addr(PCIDevice *d, int reg, pci_get_word(pf->config + sriov_cap + PCI_SRIOV_VF_OFFSET); uint16_t vf_stride = pci_get_word(pf->config + sriov_cap + PCI_SRIOV_VF_STRIDE); - uint32_t vf_num = (d->devfn - (pf->devfn + vf_offset)) / vf_stride; + uint32_t vf_num = d->devfn - (pf->devfn + vf_offset); + + if (vf_num) { + vf_num /= vf_stride; + } if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) { new_addr = pci_get_quad(pf->config + bar); @@ -1529,7 +1705,7 @@ static void pci_update_mappings(PCIDevice *d) continue; new_addr = pci_bar_address(d, i, r->type, r->size); - if (!d->has_power) { + if (!d->enabled || pci_pm_state(d)) { new_addr = PCI_BAR_UNMAPPED; } @@ -1559,7 +1735,7 @@ static void pci_update_mappings(PCIDevice *d) pci_update_vga(d); } -static inline int pci_irq_disabled(PCIDevice *d) +int pci_irq_disabled(PCIDevice *d) { return pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_INTX_DISABLE; } @@ -1595,6 +1771,7 @@ uint32_t pci_default_read_config(PCIDevice *d, void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int l) { + uint8_t new_pm_state, old_pm_state = pci_pm_state(d); int i, was_irq_disabled = pci_irq_disabled(d); uint32_t val = val_in; @@ -1607,17 +1784,21 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask); d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */ } + + new_pm_state = pci_pm_update(d, addr, l, old_pm_state); + if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) || ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) || ranges_overlap(addr, l, PCI_ROM_ADDRESS1, 4) || - range_covers_byte(addr, l, PCI_COMMAND)) + range_covers_byte(addr, l, PCI_COMMAND) || + !!new_pm_state != !!old_pm_state) { pci_update_mappings(d); + } 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) - & PCI_COMMAND_MASTER) && d->has_power); + pci_set_master(d, (pci_get_word(d->config + PCI_COMMAND) & + PCI_COMMAND_MASTER) && d->enabled); } msi_write_config(d, addr, val_in, l); @@ -1860,76 +2041,49 @@ const pci_class_desc *get_class_desc(int class) return desc; } -/* Initialize a PCI NIC. */ -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) { - const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr; - GPtrArray *pci_nic_models; - PCIBus *bus; + qemu_create_nic_bus_devices(&bus->qbus, TYPE_PCI_DEVICE, default_model, + "virtio", "virtio-net-pci"); +} + +bool pci_init_nic_in_slot(PCIBus *rootbus, const char *model, + const char *alias, const char *devaddr) +{ + 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"); - } - - pci_nic_models = qemu_get_nic_models(TYPE_PCI_DEVICE); - - if (qemu_show_nic_models(nd->model, (const char **)pci_nic_models->pdata)) { - exit(0); + if (!nd) { + return false; } - 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) @@ -2097,7 +2251,7 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp) g_cmp_uint32, NULL); } - if (pci_dev->romsize != -1 && !is_power_of_2(pci_dev->romsize)) { + 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; } @@ -2129,6 +2283,11 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp) } } + if (!pcie_sriov_register_device(pci_dev, errp)) { + pci_qdev_unrealize(DEVICE(pci_dev)); + return; + } + /* * A PCIe Downstream Port that do not have ARI Forwarding enabled must * associate only Device 0 with the device attached to the bus @@ -2300,12 +2459,12 @@ static void pci_patch_ids(PCIDevice *pdev, uint8_t *ptr, uint32_t size) /* Only a valid rom will be patched. */ rom_magic = pci_get_word(ptr); if (rom_magic != 0xaa55) { - PCI_DPRINTF("Bad ROM magic %04x\n", rom_magic); + trace_pci_bad_rom_magic(rom_magic, 0xaa55); return; } pcir_offset = pci_get_word(ptr + 0x18); if (pcir_offset + 8 >= size || memcmp(ptr + pcir_offset, "PCIR", 4)) { - PCI_DPRINTF("Bad PCIR offset 0x%x or signature\n", pcir_offset); + trace_pci_bad_pcir_offset(pcir_offset); return; } @@ -2314,8 +2473,8 @@ static void pci_patch_ids(PCIDevice *pdev, uint8_t *ptr, uint32_t size) rom_vendor_id = pci_get_word(ptr + pcir_offset + 4); rom_device_id = pci_get_word(ptr + pcir_offset + 6); - PCI_DPRINTF("%s: ROM id %04x%04x / PCI id %04x%04x\n", pdev->romfile, - vendor_id, device_id, rom_vendor_id, rom_device_id); + trace_pci_rom_and_pci_ids(pdev->romfile, vendor_id, device_id, + rom_vendor_id, rom_device_id); checksum = ptr[6]; @@ -2323,7 +2482,7 @@ static void pci_patch_ids(PCIDevice *pdev, uint8_t *ptr, uint32_t size) /* Patch vendor id and checksum (at offset 6 for etherboot roms). */ checksum += (uint8_t)rom_vendor_id + (uint8_t)(rom_vendor_id >> 8); checksum -= (uint8_t)vendor_id + (uint8_t)(vendor_id >> 8); - PCI_DPRINTF("ROM checksum %02x / %02x\n", ptr[6], checksum); + trace_pci_rom_checksum_change(ptr[6], checksum); ptr[6] = checksum; pci_set_word(ptr + pcir_offset + 4, vendor_id); } @@ -2332,7 +2491,7 @@ static void pci_patch_ids(PCIDevice *pdev, uint8_t *ptr, uint32_t size) /* Patch device id and checksum (at offset 6 for etherboot roms). */ checksum += (uint8_t)rom_device_id + (uint8_t)(rom_device_id >> 8); checksum -= (uint8_t)device_id + (uint8_t)(device_id >> 8); - PCI_DPRINTF("ROM checksum %02x / %02x\n", ptr[6], checksum); + trace_pci_rom_checksum_change(ptr[6], checksum); ptr[6] = checksum; pci_set_word(ptr + pcir_offset + 6, device_id); } @@ -2383,7 +2542,15 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, return; } - if (load_file || pdev->romsize == -1) { + if (pci_is_vf(pdev)) { + if (pdev->rom_bar > 0) { + error_setg(errp, "ROM BAR cannot be enabled for SR-IOV VF"); + } + + return; + } + + 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); @@ -2402,7 +2569,7 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, pdev->romfile); return; } - if (pdev->romsize != -1) { + if (pdev->romsize != UINT_MAX) { if (size > pdev->romsize) { error_setg(errp, "romfile \"%s\" (%u bytes) " "is too large for ROM size %u", @@ -2656,7 +2823,7 @@ MemoryRegion *pci_address_space_io(PCIDevice *dev) return pci_get_bus(dev)->address_space_io; } -static void pci_device_class_init(ObjectClass *klass, void *data) +static void pci_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); @@ -2664,9 +2831,13 @@ 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) +static void pci_device_class_base_init(ObjectClass *klass, const void *data) { if (!object_class_is_abstract(klass)) { ObjectClass *conventional = @@ -2679,13 +2850,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); /* @@ -2724,15 +2911,261 @@ 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) +int pci_iommu_init_iotlb_notifier(PCIDevice *dev, IOMMUNotifier *n, + IOMMUNotify fn, void *opaque) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->init_iotlb_notifier) { + iommu_bus->iommu_ops->init_iotlb_notifier(bus, iommu_bus->iommu_opaque, + devfn, n, fn, opaque); + return 0; + } + + return -ENODEV; +} + +bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod, + Error **errp) +{ + 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); + } +} + +int pci_pri_request_page(PCIDevice *dev, uint32_t pasid, bool priv_req, + bool exec_req, hwaddr addr, bool lpig, + uint16_t prgi, bool is_read, bool is_write) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + if (!dev->is_master || + ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev))) { + return -EPERM; + } + + if (!pcie_pri_enabled(dev)) { + return -EPERM; + } + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->pri_request_page) { + return iommu_bus->iommu_ops->pri_request_page(bus, + iommu_bus->iommu_opaque, + devfn, pasid, priv_req, + exec_req, addr, lpig, prgi, + is_read, is_write); + } + + return -ENODEV; +} + +int pci_pri_register_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUPRINotifier *notifier) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + if (!dev->is_master || + ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev))) { + return -EPERM; + } + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->pri_register_notifier) { + iommu_bus->iommu_ops->pri_register_notifier(bus, + iommu_bus->iommu_opaque, + devfn, pasid, notifier); + return 0; + } + + return -ENODEV; +} + +void pci_pri_unregister_notifier(PCIDevice *dev, uint32_t pasid) { - bus->iommu_fn = fn; + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->pri_unregister_notifier) { + iommu_bus->iommu_ops->pri_unregister_notifier(bus, + iommu_bus->iommu_opaque, + devfn, pasid); + } +} + +ssize_t pci_ats_request_translation(PCIDevice *dev, uint32_t pasid, + bool priv_req, bool exec_req, + hwaddr addr, size_t length, + bool no_write, IOMMUTLBEntry *result, + size_t result_length, + uint32_t *err_count) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + if (!dev->is_master || + ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev))) { + return -EPERM; + } + + if (result_length == 0) { + return -ENOSPC; + } + + if (!pcie_ats_enabled(dev)) { + return -EPERM; + } + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->ats_request_translation) { + return iommu_bus->iommu_ops->ats_request_translation(bus, + iommu_bus->iommu_opaque, + devfn, pasid, priv_req, + exec_req, addr, length, + no_write, result, + result_length, err_count); + } + + return -ENODEV; +} + +int pci_iommu_register_iotlb_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUNotifier *n) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + if ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev)) { + return -EPERM; + } + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->register_iotlb_notifier) { + iommu_bus->iommu_ops->register_iotlb_notifier(bus, + iommu_bus->iommu_opaque, devfn, + pasid, n); + return 0; + } + + return -ENODEV; +} + +int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUNotifier *n) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + if ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev)) { + return -EPERM; + } + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->unregister_iotlb_notifier) { + iommu_bus->iommu_ops->unregister_iotlb_notifier(bus, + iommu_bus->iommu_opaque, + devfn, pasid, n); + return 0; + } + + return -ENODEV; +} + +int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width, + uint32_t *min_page_size) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->get_iotlb_info) { + iommu_bus->iommu_ops->get_iotlb_info(iommu_bus->iommu_opaque, + addr_width, min_page_size); + return 0; + } + + return -ENODEV; +} + +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; } @@ -2836,16 +3269,30 @@ MSIMessage pci_get_msi_message(PCIDevice *dev, int vector) void pci_set_power(PCIDevice *d, bool state) { - if (d->has_power == state) { + /* + * Don't change the enabled state of VFs when powering on/off the device. + * + * When powering on, VFs must not be enabled immediately but they must + * wait until the guest configures SR-IOV. + * When powering off, their corresponding PFs will be reset and disable + * VFs. + */ + if (!pci_is_vf(d)) { + pci_set_enabled(d, state); + } +} + +void pci_set_enabled(PCIDevice *d, bool state) +{ + if (d->enabled == state) { return; } - d->has_power = state; + d->enabled = state; pci_update_mappings(d); - memory_region_set_enabled(&d->bus_master_enable_region, - (pci_get_word(d->config + PCI_COMMAND) - & PCI_COMMAND_MASTER) && d->has_power); - if (!d->has_power) { + pci_set_master(d, (pci_get_word(d->config + PCI_COMMAND) + & PCI_COMMAND_MASTER) && d->enabled); + if (qdev_is_realized(&d->qdev)) { pci_device_reset(d); } } diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index e7b9345615e..76255c4cd89 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -38,6 +38,7 @@ #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 @@ -379,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); + 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 */ @@ -393,6 +402,8 @@ void pci_bridge_exitfn(PCIDevice *pci_dev) PCIBridge *s = PCI_BRIDGE(pci_dev); assert(QLIST_EMPTY(&s->sec_bus.child)); QLIST_REMOVE(&s->sec_bus, sibling); + 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 */ @@ -466,10 +477,17 @@ int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset, return 0; } -static void pci_bridge_class_init(ObjectClass *klass, void *data) +static const Property pci_bridge_properties[] = { + DEFINE_PROP_BOOL("x-pci-express-writeable-slt-bug", PCIBridge, + pcie_writeable_slt_bug, false), +}; + +static void pci_bridge_class_init(ObjectClass *klass, const 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; } @@ -479,7 +497,7 @@ static const TypeInfo pci_bridge_type_info = { .instance_size = sizeof(PCIBridge), .class_init = pci_bridge_class_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c index a18aa0a8d4c..7179d99178b 100644 --- a/hw/pci/pci_host.c +++ b/hw/pci/pci_host.c @@ -86,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 || is_pci_dev_ejected(pci_dev)) { + !pci_dev->enabled || is_pci_dev_ejected(pci_dev)) { return; } @@ -111,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 || is_pci_dev_ejected(pci_dev)) { + !pci_dev->enabled || is_pci_dev_ejected(pci_dev)) { return ~0x0; } @@ -217,12 +217,6 @@ const MemoryRegionOps pci_host_data_le_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -const MemoryRegionOps pci_host_data_be_ops = { - .read = pci_host_data_read, - .write = pci_host_data_write, - .endianness = DEVICE_BIG_ENDIAN, -}; - static bool pci_host_needed(void *opaque) { PCIHostState *s = opaque; @@ -234,20 +228,19 @@ 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() } }; -static Property pci_host_properties_common[] = { +static const Property pci_host_properties_common[] = { DEFINE_PROP_BOOL("x-config-reg-migration-enabled", PCIHostState, mig_enabled, true), DEFINE_PROP_BOOL(PCI_HOST_BYPASS_IOMMU, PCIHostState, bypass_iommu, false), - DEFINE_PROP_END_OF_LIST(), }; -static void pci_host_class_init(ObjectClass *klass, void *data) +static void pci_host_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_props(dc, pci_host_properties_common); diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 6db0cf69cd8..eaeb68894e6 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 @@ -45,6 +46,23 @@ static bool pcie_sltctl_powered_off(uint16_t sltctl) && (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 */ @@ -68,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) | @@ -87,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); @@ -97,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 @@ -126,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, @@ -735,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) @@ -779,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. @@ -1016,18 +1113,22 @@ void pcie_sync_bridge_lnk(PCIDevice *bridge_dev) if ((lnksta & PCI_EXP_LNKSTA_NLW) > (lnkcap & PCI_EXP_LNKCAP_MLW)) { lnksta &= ~PCI_EXP_LNKSTA_NLW; lnksta |= lnkcap & PCI_EXP_LNKCAP_MLW; - } else if (!(lnksta & PCI_EXP_LNKSTA_NLW)) { - lnksta |= QEMU_PCI_EXP_LNKSTA_NLW(QEMU_PCI_EXP_LNK_X1); } if ((lnksta & PCI_EXP_LNKSTA_CLS) > (lnkcap & PCI_EXP_LNKCAP_SLS)) { lnksta &= ~PCI_EXP_LNKSTA_CLS; lnksta |= lnkcap & PCI_EXP_LNKCAP_SLS; - } else if (!(lnksta & PCI_EXP_LNKSTA_CLS)) { - lnksta |= QEMU_PCI_EXP_LNKSTA_CLS(QEMU_PCI_EXP_LNK_2_5GT); } } + if (!(lnksta & PCI_EXP_LNKSTA_NLW)) { + lnksta |= QEMU_PCI_EXP_LNKSTA_NLW(QEMU_PCI_EXP_LNK_X1); + } + + if (!(lnksta & PCI_EXP_LNKSTA_CLS)) { + lnksta |= QEMU_PCI_EXP_LNKSTA_CLS(QEMU_PCI_EXP_LNK_2_5GT); + } + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW); pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA, lnksta & @@ -1113,3 +1214,81 @@ void pcie_acs_reset(PCIDevice *dev) pci_set_word(dev->config + dev->exp.acs_cap + PCI_ACS_CTRL, 0); } } + +/* PASID */ +void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, + bool exec_perm, bool priv_mod) +{ + static const uint16_t control_reg_rw_mask = 0x07; + uint16_t capability_reg; + + assert(pasid_width <= PCI_EXT_CAP_PASID_MAX_WIDTH); + + pcie_add_capability(dev, PCI_EXT_CAP_ID_PASID, PCI_PASID_VER, offset, + PCI_EXT_CAP_PASID_SIZEOF); + + capability_reg = ((uint16_t)pasid_width) << PCI_PASID_CAP_WIDTH_SHIFT; + capability_reg |= exec_perm ? PCI_PASID_CAP_EXEC : 0; + capability_reg |= priv_mod ? PCI_PASID_CAP_PRIV : 0; + pci_set_word(dev->config + offset + PCI_PASID_CAP, capability_reg); + + /* Everything is disabled by default */ + pci_set_word(dev->config + offset + PCI_PASID_CTRL, 0); + + pci_set_word(dev->wmask + offset + PCI_PASID_CTRL, control_reg_rw_mask); + + dev->exp.pasid_cap = offset; +} + +/* PRI */ +void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap, + bool prg_response_pasid_req) +{ + static const uint16_t control_reg_rw_mask = 0x3; + static const uint16_t status_reg_rw1_mask = 0x3; + static const uint32_t pr_alloc_reg_rw_mask = 0xffffffff; + uint16_t status_reg; + + status_reg = prg_response_pasid_req ? PCI_PRI_STATUS_PASID : 0; + status_reg |= PCI_PRI_STATUS_STOPPED; /* Stopped by default */ + + pcie_add_capability(dev, PCI_EXT_CAP_ID_PRI, PCI_PRI_VER, offset, + PCI_EXT_CAP_PRI_SIZEOF); + /* Disabled by default */ + + pci_set_word(dev->config + offset + PCI_PRI_STATUS, status_reg); + pci_set_long(dev->config + offset + PCI_PRI_MAX_REQ, outstanding_pr_cap); + + pci_set_word(dev->wmask + offset + PCI_PRI_CTRL, control_reg_rw_mask); + pci_set_word(dev->w1cmask + offset + PCI_PRI_STATUS, status_reg_rw1_mask); + pci_set_long(dev->wmask + offset + PCI_PRI_ALLOC_REQ, pr_alloc_reg_rw_mask); + + dev->exp.pri_cap = offset; +} + +bool pcie_pri_enabled(const PCIDevice *dev) +{ + if (!pci_is_express(dev) || !dev->exp.pri_cap) { + return false; + } + return (pci_get_word(dev->config + dev->exp.pri_cap + PCI_PRI_CTRL) & + PCI_PRI_CTRL_ENABLE) != 0; +} + +bool pcie_pasid_enabled(const PCIDevice *dev) +{ + if (!pci_is_express(dev) || !dev->exp.pasid_cap) { + return false; + } + return (pci_get_word(dev->config + dev->exp.pasid_cap + PCI_PASID_CTRL) & + PCI_PASID_CTRL_ENABLE) != 0; +} + +bool pcie_ats_enabled(const PCIDevice *dev) +{ + if (!pci_is_express(dev) || !dev->exp.ats_cap) { + return false; + } + return (pci_get_word(dev->config + dev->exp.ats_cap + PCI_ATS_CTRL) & + PCI_ATS_CTRL_ENABLE) != 0; +} diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index 374d593ead2..2c85a78fcde 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -324,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. */ } @@ -797,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), @@ -818,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), diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c index 20ff2b39e88..f3841a26568 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; @@ -121,15 +111,14 @@ void pcie_chassis_del_slot(PCIESlot *s) QLIST_REMOVE(s, next); } -static Property pcie_port_props[] = { +static const Property pcie_port_props[] = { DEFINE_PROP_UINT8("port", PCIEPort, port, 0), DEFINE_PROP_UINT16("aer_log_max", PCIEPort, parent_obj.parent_obj.exp.aer_log.log_max, PCIE_AER_LOG_MAX_DEFAULT), - DEFINE_PROP_END_OF_LIST() }; -static void pcie_port_class_init(ObjectClass *oc, void *data) +static void pcie_port_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -199,7 +188,7 @@ int pcie_count_ds_ports(PCIBus *bus) return dsp_count; } -static bool pcie_slot_is_hotpluggbale_bus(HotplugHandler *plug_handler, +static bool pcie_slot_is_hotpluggable_bus(HotplugHandler *plug_handler, BusState *bus) { PCIESlot *s = PCIE_SLOT(bus->parent); @@ -214,16 +203,15 @@ static const TypeInfo pcie_port_type_info = { .class_init = pcie_port_class_init, }; -static Property pcie_slot_props[] = { +static const 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-do-not-expose-native-hotplug-cap", PCIESlot, hide_native_hotplug_cap, false), - DEFINE_PROP_END_OF_LIST() }; -static void pcie_slot_class_init(ObjectClass *oc, void *data) +static void pcie_slot_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); @@ -233,7 +221,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; + hc->is_hotpluggable_bus = pcie_slot_is_hotpluggable_bus; } static const TypeInfo pcie_slot_type_info = { @@ -242,7 +230,7 @@ static const TypeInfo pcie_slot_type_info = { .instance_size = sizeof(PCIESlot), .abstract = true, .class_init = pcie_slot_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 76a3b6917e6..3ad18744f4a 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -15,28 +15,83 @@ #include "hw/pci/pcie.h" #include "hw/pci/pci_bus.h" #include "hw/qdev-properties.h" -#include "qemu/error-report.h" #include "qemu/range.h" #include "qapi/error.h" #include "trace.h" -static PCIDevice *register_vf(PCIDevice *pf, int devfn, - const char *name, uint16_t vf_num); -static void unregister_vfs(PCIDevice *dev); +static GHashTable *pfs; -void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, - const char *vfname, uint16_t vf_dev_id, - uint16_t init_vfs, uint16_t total_vfs, - uint16_t vf_offset, uint16_t vf_stride) +static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs) +{ + for (uint16_t i = 0; i < total_vfs; i++) { + PCIDevice *vf = dev->exp.sriov_pf.vf[i]; + object_unparent(OBJECT(vf)); + object_unref(OBJECT(vf)); + } + g_free(dev->exp.sriov_pf.vf); + dev->exp.sriov_pf.vf = NULL; +} + +static void register_vfs(PCIDevice *dev) { + uint16_t num_vfs; + uint16_t i; + uint16_t sriov_cap = dev->exp.sriov_cap; + + assert(sriov_cap > 0); + num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); + + trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), num_vfs); + for (i = 0; i < num_vfs; i++) { + pci_set_enabled(dev->exp.sriov_pf.vf[i], true); + } + + pci_set_word(dev->wmask + sriov_cap + PCI_SRIOV_NUM_VF, 0); +} + +static void unregister_vfs(PCIDevice *dev) +{ + uint8_t *cfg = dev->config + dev->exp.sriov_cap; + uint16_t i; + + trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) { + pci_set_enabled(dev->exp.sriov_pf.vf[i], false); + } + + pci_set_word(dev->wmask + dev->exp.sriov_cap + PCI_SRIOV_NUM_VF, 0xffff); +} + +static bool pcie_sriov_pf_init_common(PCIDevice *dev, uint16_t offset, + uint16_t vf_dev_id, uint16_t init_vfs, + uint16_t total_vfs, uint16_t vf_offset, + uint16_t vf_stride, Error **errp) +{ + int32_t devfn = dev->devfn + vf_offset; uint8_t *cfg = dev->config + offset; uint8_t *wmask; + if (!pci_is_express(dev)) { + error_setg(errp, "PCI Express is required for SR-IOV PF"); + return false; + } + + if (pci_is_vf(dev)) { + error_setg(errp, "a device cannot be a SR-IOV PF and a VF at the same time"); + return false; + } + + if (total_vfs && + (uint32_t)devfn + (uint32_t)(total_vfs - 1) * vf_stride >= PCI_DEVFN_MAX) { + error_setg(errp, "VF addr overflows"); + return false; + } + pcie_add_capability(dev, PCI_EXT_CAP_ID_SRIOV, 1, offset, PCI_EXT_CAP_SRIOV_SIZEOF); dev->exp.sriov_cap = offset; - dev->exp.sriov_pf.num_vfs = 0; - dev->exp.sriov_pf.vfname = g_strdup(vfname); dev->exp.sriov_pf.vf = NULL; pci_set_word(cfg + PCI_SRIOV_VF_OFFSET, vf_offset); @@ -69,13 +124,74 @@ void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, pci_set_word(wmask + PCI_SRIOV_SYS_PGSIZE, 0x553); qdev_prop_set_bit(&dev->qdev, "multifunction", true); + + return true; +} + +bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, + const char *vfname, uint16_t vf_dev_id, + uint16_t init_vfs, uint16_t total_vfs, + uint16_t vf_offset, uint16_t vf_stride, + Error **errp) +{ + BusState *bus = qdev_get_parent_bus(&dev->qdev); + int32_t devfn = dev->devfn + vf_offset; + + if (pfs && g_hash_table_contains(pfs, dev->qdev.id)) { + error_setg(errp, "attaching user-created SR-IOV VF unsupported"); + return false; + } + + if (!pcie_sriov_pf_init_common(dev, offset, vf_dev_id, init_vfs, + total_vfs, vf_offset, vf_stride, errp)) { + return false; + } + + dev->exp.sriov_pf.vf = g_new(PCIDevice *, total_vfs); + + for (uint16_t i = 0; i < total_vfs; i++) { + PCIDevice *vf = pci_new(devfn, vfname); + vf->exp.sriov_vf.pf = dev; + vf->exp.sriov_vf.vf_number = i; + + if (!qdev_realize(&vf->qdev, bus, errp)) { + object_unparent(OBJECT(vf)); + object_unref(vf); + unparent_vfs(dev, i); + return false; + } + + /* set vid/did according to sr/iov spec - they are not used */ + pci_config_set_vendor_id(vf->config, 0xffff); + pci_config_set_device_id(vf->config, 0xffff); + + dev->exp.sriov_pf.vf[i] = vf; + devfn += vf_stride; + } + + return true; } void pcie_sriov_pf_exit(PCIDevice *dev) { - unregister_vfs(dev); - g_free((char *)dev->exp.sriov_pf.vfname); - dev->exp.sriov_pf.vfname = NULL; + uint8_t *cfg = dev->config + dev->exp.sriov_cap; + + if (dev->exp.sriov_pf.vf_user_created) { + uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID); + uint16_t total_vfs = pci_get_word(dev->config + PCI_SRIOV_TOTAL_VF); + uint16_t vf_dev_id = pci_get_word(dev->config + PCI_SRIOV_VF_DID); + + unregister_vfs(dev); + + for (uint16_t i = 0; i < total_vfs; i++) { + dev->exp.sriov_pf.vf[i]->exp.sriov_vf.pf = NULL; + + pci_config_set_vendor_id(dev->exp.sriov_pf.vf[i]->config, ven_id); + pci_config_set_device_id(dev->exp.sriov_pf.vf[i]->config, vf_dev_id); + } + } else { + unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)); + } } void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, @@ -108,115 +224,179 @@ void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, MemoryRegion *memory) { - PCIIORegion *r; - PCIBus *bus = pci_get_bus(dev); uint8_t type; - pcibus_t size = memory_region_size(memory); - assert(pci_is_vf(dev)); /* PFs must use pci_register_bar */ - assert(region_num >= 0); - assert(region_num < PCI_NUM_REGIONS); + assert(dev->exp.sriov_vf.pf); type = dev->exp.sriov_vf.pf->exp.sriov_pf.vf_bar_type[region_num]; - if (!is_power_of_2(size)) { - error_report("%s: PCI region size must be a power" - " of two - type=0x%x, size=0x%"FMT_PCIBUS, - __func__, type, size); - exit(1); - } + return pci_register_bar(dev, region_num, type, memory); +} - r = &dev->io_regions[region_num]; - r->memory = memory; - r->address_space = - type & PCI_BASE_ADDRESS_SPACE_IO - ? bus->address_space_io - : bus->address_space_mem; - r->size = size; - r->type = type; - - r->addr = pci_bar_address(dev, region_num, r->type, r->size); - if (r->addr != PCI_BAR_UNMAPPED) { - memory_region_add_subregion_overlap(r->address_space, - r->addr, r->memory, 1); - } +static gint compare_vf_devfns(gconstpointer a, gconstpointer b) +{ + return (*(PCIDevice **)a)->devfn - (*(PCIDevice **)b)->devfn; } -static PCIDevice *register_vf(PCIDevice *pf, int devfn, const char *name, - uint16_t vf_num) +int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev, + uint16_t offset, + Error **errp) { - PCIDevice *dev = pci_new(devfn, name); - dev->exp.sriov_vf.pf = pf; - dev->exp.sriov_vf.vf_number = vf_num; - PCIBus *bus = pci_get_bus(pf); - Error *local_err = NULL; - - qdev_realize(&dev->qdev, &bus->qbus, &local_err); - if (local_err) { - error_report_err(local_err); - return NULL; + GPtrArray *pf; + PCIDevice **vfs; + BusState *bus = qdev_get_parent_bus(DEVICE(dev)); + uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID); + uint16_t size = PCI_EXT_CAP_SRIOV_SIZEOF; + uint16_t vf_dev_id; + uint16_t vf_offset; + uint16_t vf_stride; + uint16_t i; + + if (!pfs || !dev->qdev.id) { + return 0; + } + + pf = g_hash_table_lookup(pfs, dev->qdev.id); + if (!pf) { + return 0; + } + + if (pf->len > UINT16_MAX) { + error_setg(errp, "too many VFs"); + return -1; } - /* set vid/did according to sr/iov spec - they are not used */ - pci_config_set_vendor_id(dev->config, 0xffff); - pci_config_set_device_id(dev->config, 0xffff); + g_ptr_array_sort(pf, compare_vf_devfns); + vfs = (void *)pf->pdata; - return dev; + if (vfs[0]->devfn <= dev->devfn) { + error_setg(errp, "a VF function number is less than the PF function number"); + return -1; + } + + vf_dev_id = pci_get_word(vfs[0]->config + PCI_DEVICE_ID); + vf_offset = vfs[0]->devfn - dev->devfn; + vf_stride = pf->len < 2 ? 0 : vfs[1]->devfn - vfs[0]->devfn; + + for (i = 0; i < pf->len; i++) { + if (bus != qdev_get_parent_bus(&vfs[i]->qdev)) { + error_setg(errp, "SR-IOV VF parent bus mismatches with PF"); + return -1; + } + + if (ven_id != pci_get_word(vfs[i]->config + PCI_VENDOR_ID)) { + error_setg(errp, "SR-IOV VF vendor ID mismatches with PF"); + return -1; + } + + if (vf_dev_id != pci_get_word(vfs[i]->config + PCI_DEVICE_ID)) { + error_setg(errp, "inconsistent SR-IOV VF device IDs"); + return -1; + } + + for (size_t j = 0; j < PCI_NUM_REGIONS; j++) { + if (vfs[i]->io_regions[j].size != vfs[0]->io_regions[j].size || + vfs[i]->io_regions[j].type != vfs[0]->io_regions[j].type) { + error_setg(errp, "inconsistent SR-IOV BARs"); + return -1; + } + } + + if (vfs[i]->devfn - vfs[0]->devfn != vf_stride * i) { + error_setg(errp, "inconsistent SR-IOV stride"); + return -1; + } + } + + if (!pcie_sriov_pf_init_common(dev, offset, vf_dev_id, pf->len, + pf->len, vf_offset, vf_stride, errp)) { + return -1; + } + + if (!pcie_find_capability(dev, PCI_EXT_CAP_ID_ARI)) { + pcie_ari_init(dev, offset + size); + size += PCI_ARI_SIZEOF; + } + + for (i = 0; i < pf->len; i++) { + vfs[i]->exp.sriov_vf.pf = dev; + vfs[i]->exp.sriov_vf.vf_number = i; + + /* set vid/did according to sr/iov spec - they are not used */ + pci_config_set_vendor_id(vfs[i]->config, 0xffff); + pci_config_set_device_id(vfs[i]->config, 0xffff); + } + + dev->exp.sriov_pf.vf = vfs; + dev->exp.sriov_pf.vf_user_created = true; + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + PCIIORegion *region = &vfs[0]->io_regions[i]; + + if (region->size) { + pcie_sriov_pf_init_vf_bar(dev, i, region->type, region->size); + } + } + + return size; } -static void register_vfs(PCIDevice *dev) +bool pcie_sriov_register_device(PCIDevice *dev, Error **errp) { - uint16_t num_vfs; - uint16_t i; - uint16_t sriov_cap = dev->exp.sriov_cap; - uint16_t vf_offset = - pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_OFFSET); - uint16_t vf_stride = - pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_STRIDE); - int32_t devfn = dev->devfn + vf_offset; + if (!dev->exp.sriov_pf.vf && dev->qdev.id && + pfs && g_hash_table_contains(pfs, dev->qdev.id)) { + error_setg(errp, "attaching user-created SR-IOV VF unsupported"); + return false; + } - assert(sriov_cap > 0); - num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); + if (dev->sriov_pf) { + PCIDevice *pci_pf; + GPtrArray *pf; - dev->exp.sriov_pf.vf = g_new(PCIDevice *, num_vfs); - assert(dev->exp.sriov_pf.vf); + if (!PCI_DEVICE_GET_CLASS(dev)->sriov_vf_user_creatable) { + error_setg(errp, "user cannot create SR-IOV VF with this device type"); + return false; + } - trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), num_vfs); - for (i = 0; i < num_vfs; i++) { - dev->exp.sriov_pf.vf[i] = register_vf(dev, devfn, - dev->exp.sriov_pf.vfname, i); - if (!dev->exp.sriov_pf.vf[i]) { - num_vfs = i; - break; + if (!pci_is_express(dev)) { + error_setg(errp, "PCI Express is required for SR-IOV VF"); + return false; } - devfn += vf_stride; + + if (!pci_qdev_find_device(dev->sriov_pf, &pci_pf)) { + error_setg(errp, "PCI device specified as SR-IOV PF already exists"); + return false; + } + + if (!pfs) { + pfs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + } + + pf = g_hash_table_lookup(pfs, dev->sriov_pf); + if (!pf) { + pf = g_ptr_array_new(); + g_hash_table_insert(pfs, g_strdup(dev->sriov_pf), pf); + } + + g_ptr_array_add(pf, dev); } - dev->exp.sriov_pf.num_vfs = num_vfs; + + return true; } -static void unregister_vfs(PCIDevice *dev) +void pcie_sriov_unregister_device(PCIDevice *dev) { - Error *local_err = NULL; - uint16_t num_vfs = dev->exp.sriov_pf.num_vfs; - uint16_t i; + if (dev->sriov_pf && pfs) { + GPtrArray *pf = g_hash_table_lookup(pfs, dev->sriov_pf); - trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), num_vfs); - for (i = 0; i < num_vfs; i++) { - 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 (pf) { + g_ptr_array_remove_fast(pf, dev); + + if (!pf->len) { + g_hash_table_remove(pfs, dev->sriov_pf); + g_ptr_array_free(pf, FALSE); + } } - 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, @@ -237,29 +417,57 @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, PCI_FUNC(dev->devfn), off, val, len); if (range_covers_byte(off, len, PCI_SRIOV_CTRL)) { - if (dev->exp.sriov_pf.num_vfs) { - if (!(val & PCI_SRIOV_CTRL_VFE)) { - unregister_vfs(dev); - } + if (val & PCI_SRIOV_CTRL_VFE) { + register_vfs(dev); } else { - if (val & PCI_SRIOV_CTRL_VFE) { - register_vfs(dev); - } + unregister_vfs(dev); + } + } else if (range_covers_byte(off, len, PCI_SRIOV_NUM_VF)) { + uint8_t *cfg = dev->config + sriov_cap; + uint8_t *wmask = dev->wmask + sriov_cap; + uint16_t num_vfs = pci_get_word(cfg + PCI_SRIOV_NUM_VF); + uint16_t wmask_val = PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI; + + if (num_vfs <= pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)) { + wmask_val |= PCI_SRIOV_CTRL_VFE; } + + pci_set_word(wmask + PCI_SRIOV_CTRL, wmask_val); + } +} + +void pcie_sriov_pf_post_load(PCIDevice *dev) +{ + if (dev->exp.sriov_cap) { + register_vfs(dev); } } -/* 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); + pci_set_word(dev->wmask + sriov_cap + PCI_SRIOV_CTRL, + PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI); + + /* + * 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]); } } @@ -284,7 +492,7 @@ void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize) uint16_t pcie_sriov_vf_number(PCIDevice *dev) { - assert(pci_is_vf(dev)); + assert(dev->exp.sriov_vf.pf); return dev->exp.sriov_vf.vf_number; } @@ -296,7 +504,7 @@ PCIDevice *pcie_sriov_get_pf(PCIDevice *dev) PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n) { assert(!pci_is_vf(dev)); - if (n < dev->exp.sriov_pf.num_vfs) { + if (n < pcie_sriov_num_vfs(dev)) { return dev->exp.sriov_pf.vf[n]; } return NULL; @@ -304,5 +512,10 @@ PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n) uint16_t pcie_sriov_num_vfs(PCIDevice *dev) { - return dev->exp.sriov_pf.num_vfs; + uint16_t sriov_cap = dev->exp.sriov_cap; + uint8_t *cfg = dev->config + sriov_cap; + + return sriov_cap && + (pci_get_word(cfg + PCI_SRIOV_CTRL) & PCI_SRIOV_CTRL_VFE) ? + pci_get_word(cfg + PCI_SRIOV_NUM_VF) : 0; } diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index e7bc7192f1f..aac6f2d0345 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,6 +124,34 @@ #define SHPC_PCI_TO_IDX(pci_slot) ((pci_slot) - 1) #define SHPC_IDX_TO_PHYSICAL(slot) ((slot) + 1) +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); @@ -302,6 +331,23 @@ static void shpc_slot_command(PCIDevice *d, uint8_t target, shpc_set_status(shpc, slot, state, SHPC_SLOT_STATE_MASK); } + 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)) { @@ -615,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; @@ -736,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/trace-events b/hw/pci/trace-events index 42430869ce0..02c80d3ec86 100644 --- a/hw/pci/trace-events +++ b/hw/pci/trace-events @@ -1,9 +1,15 @@ # See docs/devel/tracing.rst for syntax documentation. # pci.c +pci_pm_bad_transition(const char *dev, uint32_t bus, uint32_t slot, uint32_t func, uint8_t old, uint8_t new) "%s %02x:%02x.%x REJECTED PM transition D%d->D%d" +pci_pm_transition(const char *dev, uint32_t bus, uint32_t slot, uint32_t func, uint8_t old, uint8_t new) "%s %02x:%02x.%x PM transition D%d->D%d" 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_bad_rom_magic(uint16_t bad_rom_magic, uint16_t good_rom_magic) "Bad ROM magic number: %04"PRIX16". Should be: %04"PRIX16 +pci_bad_pcir_offset(uint16_t pcir_offset) "Bad PCIR offset 0x%"PRIx16" or signature" +pci_rom_and_pci_ids(char *romfile, uint16_t vendor_id, uint16_t device_id, uint16_t rom_vendor_id, uint16_t rom_device_id) "%s: ROM ID %04"PRIx16":%04"PRIx16" | PCI ID %04"PRIx16":%04"PRIx16 +pci_rom_checksum_change(uint8_t old_checksum, uint8_t new_checksum) "ROM checksum changed from %02"PRIx8" to %02"PRIx8 # 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" @@ -14,5 +20,11 @@ msix_write_config(char *name, bool enabled, bool masked) "dev %s enabled %d mask # hw/pci/pcie_sriov.c 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_unregister_vfs(const char *name, int slot, int function) "%s %02x:%x: Unregistering 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 41f2df91366..00000000000 --- 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 04e29c109c0..00000000000 --- a/hw/pcmcia/meson.build +++ /dev/null @@ -1,2 +0,0 @@ -system_ss.add(when: 'CONFIG_PCMCIA', if_true: files('pcmcia.c')) -system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx.c')) diff --git a/hw/pcmcia/pcmcia.c b/hw/pcmcia/pcmcia.c deleted file mode 100644 index 03d13e7d670..00000000000 --- 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 fcca7e571b2..00000000000 --- 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 5dfbf47ef55..ced6bbc7404 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 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,45 +37,60 @@ config POWERNV select XIVE select FDT_PPC select PCI_POWERNV - -config PPC405 - bool - select M48T59 - select PFLASH_CFI02 - select PPC4XX - select SERIAL + select PCA9552 + select PCA9554 + select SERIAL_ISA + select SSI + select SSI_M25P80 + select PNV_SPI 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 @@ -79,6 +99,8 @@ config PEGASOS2 config PREP bool + default y + depends on PPC imply PCI_DEVICES imply TEST_DEVICES select CS4231A @@ -95,6 +117,8 @@ config RS6000_MC config MAC_OLDWORLD bool + default y + depends on PPC imply PCI_DEVICES imply SUNGEM imply TEST_DEVICES @@ -106,16 +130,18 @@ 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 select MAC_PMU select UNIN_PCI select FW_CFG_PPC - select USB_OHCI_PCI config E500 bool @@ -128,7 +154,7 @@ config E500 select PLATFORM_BUS select PPCE500_PCI select SDHCI - select SERIAL + select SERIAL_MM select MPC_I2C select FDT_PPC select DS1338 @@ -136,17 +162,23 @@ config E500 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 @@ -156,6 +188,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 00000000000..12279f42bc9 --- /dev/null +++ b/hw/ppc/amigaone.c @@ -0,0 +1,438 @@ +/* + * 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 "system/block-backend.h" +#include "system/qtest.h" +#include "system/reset.h" +#include "kvm_ppc.h" +#include "elf.h" + +#include /* for crc32 */ + +#define BUS_FREQ_HZ 100000000 + +#define INITRD_MIN_ADDR 0x600000 +#define INIT_RAM_ADDR 0x40000000 + +#define PCI_HIGH_ADDR 0x80000000 +#define PCI_HIGH_SIZE 0x7d000000 +#define PCI_LOW_ADDR 0xfd000000 +#define PCI_LOW_SIZE 0xe0000 + +#define ARTICIA_ADDR 0xfe000000 + +/* + * 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[] = { + 0x54, 0x63, 0xc2, 0x3e, /* srwi r3,r3,8 */ + 0x7c, 0x63, 0x18, 0xf8, /* not r3,r3 */ + 0x4e, 0x80, 0x00, 0x20, /* blr */ +}; + +#define NVRAM_ADDR 0xfd0e0000 +#define NVRAM_SIZE (4 * KiB) + +static const char default_env[] = + "baudrate=115200\0" + "stdout=vga\0" + "stdin=ps2kbd\0" + "bootcmd=boota; menu; run menuboot_cmd\0" + "boot1=ide\0" + "boot2=cdrom\0" + "boota_timeout=3\0" + "ide_doreset=on\0" + "pci_irqa=9\0" + "pci_irqa_select=level\0" + "pci_irqb=10\0" + "pci_irqb_select=level\0" + "pci_irqc=11\0" + "pci_irqc_select=level\0" + "pci_irqd=7\0" + "pci_irqd_select=level\0" + "a1ide_irq=1111\0" + "a1ide_xfer=FFFF\0"; +#define CRC32_DEFAULT_ENV 0xb5548481 +#define CRC32_ALL_ZEROS 0x603b0489 + +#define TYPE_A1_NVRAM "a1-nvram" +OBJECT_DECLARE_SIMPLE_TYPE(A1NVRAMState, A1_NVRAM) + +struct A1NVRAMState { + SysBusDevice parent_obj; + + MemoryRegion mr; + BlockBackend *blk; +}; + +static uint64_t nvram_read(void *opaque, hwaddr addr, unsigned int size) +{ + /* read callback not used because of romd mode */ + g_assert_not_reached(); +} + +static void nvram_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) +{ + A1NVRAMState *s = opaque; + uint8_t *p = memory_region_get_ram_ptr(&s->mr); + + p[addr] = val; + if (s->blk && blk_pwrite(s->blk, addr, 1, &val, 0) < 0) { + error_report("%s: could not write %s", __func__, blk_name(s->blk)); + } +} + +static const MemoryRegionOps nvram_ops = { + .read = nvram_read, + .write = nvram_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static void nvram_realize(DeviceState *dev, Error **errp) +{ + A1NVRAMState *s = A1_NVRAM(dev); + void *p; + uint32_t crc, *c; + + memory_region_init_rom_device(&s->mr, NULL, &nvram_ops, s, "nvram", + NVRAM_SIZE, &error_fatal); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr); + c = p = memory_region_get_ram_ptr(&s->mr); + if (s->blk) { + if (blk_getlength(s->blk) != NVRAM_SIZE) { + error_setg(errp, "NVRAM backing file size must be %" PRId64 "bytes", + NVRAM_SIZE); + return; + } + blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, + BLK_PERM_ALL, &error_fatal); + if (blk_pread(s->blk, 0, NVRAM_SIZE, p, 0) < 0) { + error_setg(errp, "Cannot read NVRAM contents from backing file"); + return; + } + } + crc = crc32(0, p + 4, NVRAM_SIZE - 4); + if (crc == CRC32_ALL_ZEROS) { /* If env is uninitialized set default */ + *c = cpu_to_be32(CRC32_DEFAULT_ENV); + /* Also copies terminating \0 as env is terminated by \0\0 */ + memcpy(p + 4, default_env, sizeof(default_env)); + if (s->blk && + blk_pwrite(s->blk, 0, sizeof(crc) + sizeof(default_env), p, 0) < 0 + ) { + error_report("%s: could not write %s", __func__, blk_name(s->blk)); + } + return; + } + if (*c == 0) { + *c = cpu_to_be32(crc32(0, p + 4, NVRAM_SIZE - 4)); + if (s->blk && blk_pwrite(s->blk, 0, 4, p, 0) < 0) { + error_report("%s: could not write %s", __func__, blk_name(s->blk)); + } + } + if (be32_to_cpu(*c) != crc) { + warn_report("NVRAM checksum mismatch"); + } +} + +static const Property nvram_properties[] = { + DEFINE_PROP_DRIVE("drive", A1NVRAMState, blk), +}; + +static void nvram_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = nvram_realize; + device_class_set_props(dc, nvram_properties); +} + +static const TypeInfo nvram_types[] = { + { + .name = TYPE_A1_NVRAM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(A1NVRAMState), + .class_init = nvram_class_init, + }, +}; +DEFINE_TYPES(nvram_types) + +struct boot_info { + hwaddr entry; + hwaddr stack; + hwaddr bd_info; + hwaddr initrd_start; + hwaddr initrd_end; + hwaddr cmdline_start; + hwaddr cmdline_end; +}; + +/* Board info struct from U-Boot */ +struct bd_info { + uint32_t bi_memstart; + uint32_t bi_memsize; + uint32_t bi_flashstart; + uint32_t bi_flashsize; + uint32_t bi_flashoffset; + uint32_t bi_sramstart; + uint32_t bi_sramsize; + uint32_t bi_bootflags; + uint32_t bi_ip_addr; + uint8_t bi_enetaddr[6]; + uint16_t bi_ethspeed; + uint32_t bi_intfreq; + uint32_t bi_busfreq; + uint32_t bi_baudrate; +} QEMU_PACKED; + +static void create_bd_info(hwaddr addr, ram_addr_t ram_size) +{ + struct bd_info *bd = g_new0(struct bd_info, 1); + + bd->bi_memsize = cpu_to_be32(ram_size); + bd->bi_flashstart = cpu_to_be32(PROM_ADDR); + bd->bi_flashsize = cpu_to_be32(1); /* match what U-Boot detects */ + bd->bi_bootflags = cpu_to_be32(1); + bd->bi_intfreq = cpu_to_be32(11.5 * BUS_FREQ_HZ); + bd->bi_busfreq = cpu_to_be32(BUS_FREQ_HZ); + bd->bi_baudrate = cpu_to_be32(115200); + + cpu_physical_memory_write(addr, bd, sizeof(*bd)); +} + +static void amigaone_cpu_reset(void *opaque) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + + cpu_reset(CPU(cpu)); + if (env->load_info) { + struct boot_info *bi = env->load_info; + + env->gpr[1] = bi->stack; + env->gpr[2] = 1024; + env->gpr[3] = bi->bd_info; + env->gpr[4] = bi->initrd_start; + env->gpr[5] = bi->initrd_end; + env->gpr[6] = bi->cmdline_start; + env->gpr[7] = bi->cmdline_end; + env->nip = bi->entry; + } + cpu_ppc_tb_reset(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; + DriveInfo *di; + hwaddr loadaddr; + struct boot_info *bi = NULL; + + /* 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(), INIT_RAM_ADDR, mr); + } + + /* nvram */ + dev = qdev_new(TYPE_A1_NVRAM); + di = drive_get(IF_MTD, 0, 0); + if (di) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(di)); + } + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + memory_region_add_subregion(get_system_memory(), NVRAM_ADDR, + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); + + /* 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, ARTICIA_ADDR, 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, PCI_LOW_SIZE); + memory_region_add_subregion(get_system_memory(), PCI_LOW_ADDR, mr); + mr = g_new(MemoryRegion, 1); + memory_region_init_alias(mr, OBJECT(dev), "pci-mem-high", pci_mem, + PCI_HIGH_ADDR, PCI_HIGH_SIZE); + memory_region_add_subregion(get_system_memory(), PCI_HIGH_ADDR, 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 (int 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); + + if (!machine->kernel_filename) { + return; + } + + /* handle -kernel, -initrd, -append options and emulate U-Boot */ + bi = g_new0(struct boot_info, 1); + cpu->env.load_info = bi; + + loadaddr = MIN(machine->ram_size, 256 * MiB); + bi->bd_info = loadaddr - 8 * MiB; + create_bd_info(bi->bd_info, machine->ram_size); + bi->stack = bi->bd_info - 64 * KiB - 8; + + if (machine->kernel_cmdline && machine->kernel_cmdline[0]) { + size_t len = strlen(machine->kernel_cmdline); + + loadaddr = bi->bd_info + 1 * MiB; + cpu_physical_memory_write(loadaddr, machine->kernel_cmdline, len + 1); + bi->cmdline_start = loadaddr; + bi->cmdline_end = loadaddr + len + 1; /* including terminating '\0' */ + } + + sz = load_elf(machine->kernel_filename, NULL, NULL, NULL, + &bi->entry, &loadaddr, NULL, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); + if (sz <= 0) { + sz = load_uimage(machine->kernel_filename, &bi->entry, &loadaddr, + NULL, NULL, NULL); + } + if (sz <= 0) { + error_report("Could not load kernel '%s'", + machine->kernel_filename); + exit(1); + } + loadaddr += sz; + + if (machine->initrd_filename) { + loadaddr = ROUND_UP(loadaddr + 4 * MiB, 4 * KiB); + loadaddr = MAX(loadaddr, INITRD_MIN_ADDR); + sz = load_image_targphys(machine->initrd_filename, loadaddr, + bi->bd_info - loadaddr); + if (sz <= 0) { + error_report("Could not load initrd '%s'", + machine->initrd_filename); + exit(1); + } + bi->initrd_start = loadaddr; + bi->initrd_end = loadaddr + sz; + } +} + +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 d5b6820d1dc..723c97fad2e 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -18,21 +18,22 @@ #include "qemu/datadir.h" #include "qemu/units.h" #include "qemu/guest-random.h" +#include "exec/target_page.h" #include "qapi/error.h" #include "e500.h" #include "e500-ccsr.h" #include "net/net.h" #include "qemu/config-file.h" #include "hw/block/flash.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "hw/pci/pci.h" -#include "sysemu/block-backend-io.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/block-backend-io.h" +#include "system/system.h" +#include "system/kvm.h" +#include "system/reset.h" +#include "system/runstate.h" #include "kvm_ppc.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/ppc/openpic.h" #include "hw/ppc/openpic_kvm.h" #include "hw/ppc/ppc.h" @@ -78,8 +79,6 @@ #define MPC85XX_ESDHC_IRQ 72 #define RTC_REGS_OFFSET 0x68 -#define PLATFORM_CLK_FREQ_HZ (400 * 1000 * 1000) - struct boot_info { uint32_t dt_base; @@ -119,7 +118,7 @@ static uint32_t *pci_map_create(void *fdt, uint32_t mpic, int first_slot, } static void dt_serial_create(void *fdt, unsigned long long offset, - const char *soc, const char *mpic, + const char *soc, uint32_t freq, const char *mpic, const char *alias, int idx, bool defcon) { char *ser; @@ -130,7 +129,7 @@ static void dt_serial_create(void *fdt, unsigned long long offset, qemu_fdt_setprop_string(fdt, ser, "compatible", "ns16550"); qemu_fdt_setprop_cells(fdt, ser, "reg", offset, 0x100); qemu_fdt_setprop_cell(fdt, ser, "cell-index", idx); - qemu_fdt_setprop_cell(fdt, ser, "clock-frequency", PLATFORM_CLK_FREQ_HZ); + qemu_fdt_setprop_cell(fdt, ser, "clock-frequency", freq); qemu_fdt_setprop_cells(fdt, ser, "interrupts", 42, 2); qemu_fdt_setprop_phandle(fdt, ser, "interrupt-parent", mpic); qemu_fdt_setprop_string(fdt, "/aliases", alias, ser); @@ -203,6 +202,8 @@ 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); @@ -373,14 +374,13 @@ 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; void *fdt; uint8_t hypercall[16]; - uint32_t clock_freq = PLATFORM_CLK_FREQ_HZ; - uint32_t tb_freq = PLATFORM_CLK_FREQ_HZ; + uint32_t clock_freq, tb_freq; int i; char compatible_sb[] = "fsl,mpc8544-immr\0simple-bus"; char *soc; @@ -408,7 +408,7 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, if (dtb_file) { char *filename; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_file); + filename = qemu_find_file(QEMU_FILE_TYPE_DTB, dtb_file); if (!filename) { goto out; } @@ -481,6 +481,9 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, if (kvmppc_get_hasidle(env)) { qemu_fdt_setprop(fdt, "/hypervisor", "has-idle", NULL, 0); } + } else { + clock_freq = pmc->clock_freq; + tb_freq = pmc->tb_freq; } /* Create CPU nodes */ @@ -499,7 +502,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); @@ -561,12 +564,12 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, */ if (serial_hd(1)) { dt_serial_create(fdt, MPC8544_SERIAL1_REGS_OFFSET, - soc, mpic, "serial1", 1, false); + soc, pmc->clock_freq, mpic, "serial1", 1, false); } if (serial_hd(0)) { dt_serial_create(fdt, MPC8544_SERIAL0_REGS_OFFSET, - soc, mpic, "serial0", 0, true); + soc, pmc->clock_freq, mpic, "serial0", 0, true); } /* i2c */ @@ -656,7 +659,6 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, 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 */ @@ -721,11 +723,21 @@ static int ppce500_prep_device_tree(PPCE500MachineState *machine, kernel_base, kernel_size, true); } -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; @@ -751,25 +763,6 @@ static uint64_t mmubooke_initial_mapsize(CPUPPCState *env) return (1ULL << 10 << tsize); } -/* Create -kernel TLB entries for BookE. */ -static void mmubooke_create_initial_mapping(CPUPPCState *env) -{ - ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 0); - 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; - -#ifdef CONFIG_KVM - env->tlb_dirty = true; -#endif -} - static void ppce500_cpu_reset_sec(void *opaque) { PowerPCCPU *cpu = opaque; @@ -786,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); @@ -796,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, @@ -832,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; @@ -854,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, @@ -868,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, @@ -928,7 +931,6 @@ void ppce500_init(MachineState *machine) CPUPPCState *firstenv = NULL; MemoryRegion *ccsr_addr_space; SysBusDevice *s; - PPCE500CCSRState *ccsr; I2CBus *i2c; irqs = g_new0(IrqLines, smp_cpus); @@ -951,7 +953,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) { @@ -965,7 +967,7 @@ void ppce500_init(MachineState *machine) env->spr_cb[SPR_BOOKE_PIR].default_value = cs->cpu_index = i; env->mpic_iack = pmc->ccsrbar_base + MPC8544_MPIC_REGS_OFFSET + 0xa0; - ppc_booke_timers_init(cpu, PLATFORM_CLK_FREQ_HZ, PPC_TIMER_E500); + ppc_booke_timers_init(cpu, pmc->tb_freq, PPC_TIMER_E500); /* Register reset handler */ if (!i) { @@ -990,10 +992,10 @@ void ppce500_init(MachineState *machine) memory_region_add_subregion(address_space_mem, 0, machine->ram); dev = qdev_new("e500-ccsr"); + s = SYS_BUS_DEVICE(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; + sysbus_realize_and_unref(s, &error_fatal); + ccsr_addr_space = sysbus_mmio_get_region(s, 0); memory_region_add_subregion(address_space_mem, pmc->ccsrbar_base, ccsr_addr_space); @@ -1020,7 +1022,7 @@ void ppce500_init(MachineState *machine) 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 */ @@ -1041,6 +1043,7 @@ void ppce500_init(MachineState *machine) dev = qdev_new(TYPE_SYSBUS_SDHCI); qdev_prop_set_uint8(dev, "sd-spec-version", 2); qdev_prop_set_uint8(dev, "endianness", DEVICE_BIG_ENDIAN); + qdev_prop_set_uint8(dev, "vendor", SDHCI_VENDOR_FSL); 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)); @@ -1069,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, mc->default_nic, NULL); - } + pci_init_nic_devices(pci_bus, mc->default_nic); } /* Register spinning region */ @@ -1193,7 +1194,7 @@ void ppce500_init(MachineState *machine) payload_size = load_elf(filename, NULL, NULL, NULL, &bios_entry, &loadaddr, NULL, NULL, - 1, PPC_ELF_MACHINE, 0, 0); + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (payload_size < 0) { /* * Hrm. No ELF image? Try a uImage, maybe someone is giving us an @@ -1282,6 +1283,7 @@ static void e500_ccsr_initfn(Object *obj) PPCE500CCSRState *ccsr = CCSR(obj); memory_region_init(&ccsr->ccsr_space, obj, "e500-ccsr", MPC8544_CCSRBAR_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(ccsr), &ccsr->ccsr_space); } static const TypeInfo e500_ccsr_info = { diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h index 8c09ef92e44..00f490519c2 100644 --- a/hw/ppc/e500.h +++ b/hw/ppc/e500.h @@ -5,6 +5,8 @@ #include "hw/platform-bus.h" #include "qom/object.h" +#define PLATFORM_CLK_FREQ_HZ (400 * 1000 * 1000) + struct PPCE500MachineState { /*< private >*/ MachineState parent_obj; @@ -37,12 +39,12 @@ struct PPCE500MachineClass { hwaddr pci_mmio_base; hwaddr pci_mmio_bus_base; hwaddr spin_base; + uint32_t clock_freq; + uint32_t tb_freq; }; 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 7aa2f2107a7..4f1d659e723 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -13,8 +13,8 @@ #include "qemu/units.h" #include "e500.h" #include "hw/net/fsl_etsec/etsec.h" -#include "sysemu/device_tree.h" -#include "sysemu/kvm.h" +#include "system/device_tree.h" +#include "system/kvm.h" #include "hw/sysbus.h" #include "hw/pci/pci.h" #include "hw/ppc/openpic.h" @@ -68,7 +68,7 @@ HotplugHandler *e500plat_machine_get_hotpug_handler(MachineState *machine, #define TYPE_E500PLAT_MACHINE MACHINE_TYPE_NAME("ppce500") -static void e500plat_machine_class_init(ObjectClass *oc, void *data) +static void e500plat_machine_class_init(ObjectClass *oc, const void *data) { PPCE500MachineClass *pmc = PPCE500_MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); @@ -93,6 +93,8 @@ static void e500plat_machine_class_init(ObjectClass *oc, void *data) pmc->pci_mmio_base = 0xC00000000ULL; pmc->pci_mmio_bus_base = 0xE0000000ULL; pmc->spin_base = 0xFEF000000ULL; + pmc->clock_freq = PLATFORM_CLK_FREQ_HZ; + pmc->tb_freq = PLATFORM_CLK_FREQ_HZ; mc->desc = "generic paravirt e500 platform"; mc->init = e500plat_init; @@ -107,7 +109,7 @@ static const TypeInfo e500plat_info = { .name = TYPE_E500PLAT_MACHINE, .parent = TYPE_PPCE500_MACHINE, .class_init = e500plat_machine_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 535710314a5..0b6e096116a 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -50,6 +50,7 @@ #include "qemu/datadir.h" #include "qemu/units.h" #include "qapi/error.h" +#include "exec/target_page.h" #include "hw/ppc/ppc.h" #include "hw/qdev-properties.h" #include "hw/nvram/mac_nvram.h" @@ -59,7 +60,7 @@ #include "hw/ppc/mac_dbdma.h" #include "hw/pci/pci.h" #include "net/net.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/nvram/fw_cfg.h" #include "hw/char/escc.h" #include "hw/misc/macio/macio.h" @@ -68,8 +69,8 @@ #include "hw/fw-path-provider.h" #include "elf.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" +#include "system/kvm.h" +#include "system/reset.h" #include "kvm_ppc.h" #include "hw/usb.h" #include "hw/sysbus.h" @@ -77,7 +78,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) @@ -182,7 +183,8 @@ static void ppc_core99_init(MachineState *machine) if (filename) { /* Load OpenBIOS (ELF) */ bios_size = load_elf(filename, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); + NULL, NULL, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (bios_size <= 0) { /* or load binary ROM image */ @@ -196,19 +198,14 @@ static void ppc_core99_init(MachineState *machine) } if (machine->kernel_filename) { - int bswap_needed = 0; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#endif kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(machine->kernel_filename, NULL, translate_kernel_address, NULL, NULL, NULL, - NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); + NULL, NULL, ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (kernel_size < 0) { kernel_size = load_aout(machine->kernel_filename, kernel_base, machine->ram_size - kernel_base, - bswap_needed, TARGET_PAGE_SIZE); + true, TARGET_PAGE_SIZE); } if (kernel_size < 0) { kernel_size = load_image_targphys(machine->kernel_filename, @@ -431,8 +428,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"); } @@ -444,9 +443,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, mc->default_nic, 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) { @@ -566,12 +563,12 @@ static int core99_kvm_type(MachineState *machine, const char *arg) return 2; } -static void core99_machine_class_init(ObjectClass *oc, void *data) +static void core99_machine_class_init(ObjectClass *oc, const 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 */ @@ -634,8 +631,6 @@ static void core99_instance_init(Object *obj) object_property_set_description(obj, "via", "Set VIA configuration. " "Valid values are cuda, pmu and pmu-adb"); - - return; } static const TypeInfo core99_machine_info = { @@ -644,7 +639,7 @@ static const TypeInfo core99_machine_info = { .class_init = core99_machine_class_init, .instance_init = core99_instance_init, .instance_size = sizeof(Core99MachineState), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { } }, diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 9acc7adfc92..40ae936ad8a 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -28,11 +28,12 @@ #include "qemu/datadir.h" #include "qemu/units.h" #include "qapi/error.h" +#include "exec/target_page.h" #include "hw/ppc/ppc.h" #include "hw/qdev-properties.h" #include "hw/boards.h" #include "hw/input/adb.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "net/net.h" #include "hw/isa/isa.h" #include "hw/pci/pci.h" @@ -45,8 +46,8 @@ #include "hw/fw-path-provider.h" #include "elf.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" +#include "system/kvm.h" +#include "system/reset.h" #include "kvm_ppc.h" #define MAX_IDE_BUS 2 @@ -136,7 +137,7 @@ static void ppc_heathrow_init(MachineState *machine) if (filename) { /* Load OpenBIOS (ELF) */ bios_size = load_elf(filename, NULL, NULL, NULL, NULL, &bios_addr, - NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); + NULL, NULL, ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); /* Unfortunately, load_elf sign-extends reading elf32 */ bios_addr = (uint32_t)bios_addr; @@ -153,19 +154,14 @@ static void ppc_heathrow_init(MachineState *machine) } if (machine->kernel_filename) { - int bswap_needed = 0; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#endif kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(machine->kernel_filename, NULL, translate_kernel_address, NULL, NULL, NULL, - NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); + NULL, NULL, ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (kernel_size < 0) { kernel_size = load_aout(machine->kernel_filename, kernel_base, machine->ram_size - kernel_base, - bswap_needed, TARGET_PAGE_SIZE); + true, TARGET_PAGE_SIZE); } if (kernel_size < 0) { kernel_size = load_image_targphys(machine->kernel_filename, @@ -277,9 +273,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, mc->default_nic, NULL); - } + pci_init_nic_devices(pci_bus, mc->default_nic); /* MacIO IDE */ ide_drive_get(hd, ARRAY_SIZE(hd)); @@ -408,12 +402,12 @@ static int heathrow_kvm_type(MachineState *machine, const char *arg) return 2; } -static void heathrow_class_init(ObjectClass *oc, void *data) +static void heathrow_class_init(ObjectClass *oc, const 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 */ @@ -436,7 +430,7 @@ static const TypeInfo ppc_heathrow_machine_info = { .name = MACHINE_TYPE_NAME("g3beige"), .parent = TYPE_MACHINE, .class_init = heathrow_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { } }, diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index a313d4b964a..9893f8adebb 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) @@ -31,37 +29,39 @@ 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( - 'ppc405_boards.c', - '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 @@ -82,6 +82,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 a26e83d0484..a25041e8367 100644 --- a/hw/ppc/mpc8544_guts.c +++ b/hw/ppc/mpc8544_guts.c @@ -18,9 +18,8 @@ */ #include "qemu/osdep.h" -#include "qemu/module.h" #include "qemu/log.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "cpu.h" #include "hw/sysbus.h" #include "qom/object.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 b7130903d66..582698559d2 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "e500.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/ppc/openpic.h" #include "qemu/error-report.h" #include "qemu/units.h" @@ -37,7 +37,7 @@ static void mpc8544ds_init(MachineState *machine) ppce500_init(machine); } -static void mpc8544ds_machine_class_init(ObjectClass *oc, void *data) +static void mpc8544ds_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); PPCE500MachineClass *pmc = PPCE500_MACHINE_CLASS(oc); @@ -55,6 +55,8 @@ static void mpc8544ds_machine_class_init(ObjectClass *oc, void *data) pmc->pci_mmio_bus_base = 0xC0000000ULL; pmc->pci_pio_base = 0xE1000000ULL; pmc->spin_base = 0xEF000000ULL; + pmc->clock_freq = PLATFORM_CLK_FREQ_HZ; + pmc->tb_freq = PLATFORM_CLK_FREQ_HZ; mc->desc = "mpc8544ds"; mc->init = mpc8544ds_init; diff --git a/hw/ppc/pef.c b/hw/ppc/pef.c index cc44d5e3396..254f5707876 100644 --- a/hw/ppc/pef.c +++ b/hw/ppc/pef.c @@ -12,10 +12,9 @@ #include "qapi/error.h" #include "qom/object_interfaces.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "migration/blocker.h" -#include "exec/confidential-guest-support.h" -#include "hw/ppc/pef.h" +#include "system/confidential-guest-support.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; @@ -129,8 +128,12 @@ OBJECT_DEFINE_TYPE_WITH_INTERFACES(PefGuest, { TYPE_USER_CREATABLE }, { NULL }) -static void pef_guest_class_init(ObjectClass *oc, void *data) +static void pef_guest_class_init(ObjectClass *oc, const 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 bd397cf2b5c..e15cf964278 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -14,28 +14,29 @@ #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" #include "hw/i2c/smbus_eeprom.h" #include "hw/qdev-properties.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/qtest.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/qtest.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" #include "elf.h" #include "qemu/log.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_ppc.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qom/qom-qobject.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "trace.h" #include "qemu/datadir.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/ppc/vof.h" #include @@ -73,8 +74,11 @@ 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; @@ -156,8 +160,8 @@ static void pegasos2_init(MachineState *machine) } memory_region_init_rom(rom, NULL, "pegasos2.rom", PROM_SIZE, &error_fatal); memory_region_add_subregion(get_system_memory(), PROM_ADDR, rom); - sz = load_elf(filename, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, - PPC_ELF_MACHINE, 0, 0); + sz = load_elf(filename, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (sz <= 0) { sz = load_image_targphys(filename, pm->vof ? 0 : PROM_ADDR, PROM_SIZE); } @@ -177,19 +181,25 @@ static void pegasos2_init(MachineState *machine) pm->mv_pirq[i] = qdev_get_gpio_in_named(pm->mv, "gpp", 12 + i); } pci_bus = mv64361_get_pci_bus(pm->mv, 1); - pci_bus_irqs(pci_bus, pegasos2_pci_irq, pm, PCI_NUM_PINS); /* VIA VT8231 South Bridge (multifunction PCI device) */ - via = OBJECT(pci_create_simple_multifunction(pci_bus, PCI_DEVFN(12, 0), - TYPE_VT8231_ISA)); + via = OBJECT(pci_new_multifunction(PCI_DEVFN(12, 0), TYPE_VT8231_ISA)); + + /* Set properties on individual devices before realizing the south bridge */ + if (machine->audiodev) { + dev = PCI_DEVICE(object_resolve_path_component(via, "ac97")); + qdev_prop_set_string(DEVICE(dev), "audiodev", machine->audiodev); + } + + pci_realize_and_unref(PCI_DEVICE(via), pci_bus, &error_abort); for (i = 0; i < PCI_NUM_PINS; i++) { pm->via_pirq[i] = qdev_get_gpio_in_named(DEVICE(via), "pirq", i); } object_property_add_alias(OBJECT(machine), "rtc-time", object_resolve_path_component(via, "rtc"), "date"); - qdev_connect_gpio_out(DEVICE(via), 0, - qdev_get_gpio_in_named(pm->mv, "gpp", 31)); + 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); @@ -202,10 +212,35 @@ 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, - PPC_ELF_MACHINE, 0, 0); + &pm->kernel_entry, &pm->kernel_addr, NULL, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (sz <= 0) { error_report("Could not load kernel '%s'", machine->kernel_filename); @@ -278,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 */ } @@ -303,6 +344,12 @@ 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) | @@ -370,7 +417,6 @@ static void pegasos2_machine_reset(MachineState *machine, ShutdownCause reason) d[1] = cpu_to_be64(pm->kernel_size - (pm->kernel_entry - pm->kernel_addr)); qemu_fdt_setprop(fdt, "/chosen", "qemu,boot-kernel", d, sizeof(d)); - qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); g_free(pm->fdt_blob); pm->fdt_blob = fdt; @@ -381,6 +427,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 { @@ -508,7 +555,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"); @@ -541,7 +588,7 @@ static bool pegasos2_setprop(MachineState *ms, const char *path, return true; } -static void pegasos2_machine_class_init(ObjectClass *oc, void *data) +static void pegasos2_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(oc); @@ -556,6 +603,7 @@ static void pegasos2_machine_class_init(ObjectClass *oc, void *data) 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; @@ -571,7 +619,7 @@ static const TypeInfo pegasos2_machine_info = { .parent = TYPE_MACHINE, .class_init = pegasos2_machine_class_init, .instance_size = sizeof(Pegasos2MachineState), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PPC_VIRTUAL_HYPERVISOR }, { TYPE_VOF_MACHINE_IF }, { } @@ -964,7 +1012,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); diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index eb54f93986d..4a49e9d1a86 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1,7 +1,9 @@ /* * QEMU PowerPC PowerNV machine model * - * Copyright (c) 2016, IBM Corporation. + * Copyright (c) 2016-2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,14 +24,14 @@ #include "qemu/units.h" #include "qemu/cutils.h" #include "qapi/error.h" -#include "sysemu/qtest.h" -#include "sysemu/sysemu.h" -#include "sysemu/numa.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/cpus.h" -#include "sysemu/device_tree.h" -#include "sysemu/hw_accel.h" +#include "system/qtest.h" +#include "system/system.h" +#include "system/numa.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/cpus.h" +#include "system/device_tree.h" +#include "system/hw_accel.h" #include "target/ppc/cpu.h" #include "hw/ppc/fdt.h" #include "hw/ppc/ppc.h" @@ -38,7 +40,6 @@ #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" @@ -54,7 +55,7 @@ #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 @@ -65,6 +66,8 @@ #define FW_LOAD_ADDR 0x0 #define FW_MAX_SIZE (16 * MiB) +#define PNOR_FILE_NAME "pnv-pnor.bin" + #define KERNEL_LOAD_ADDR 0x20000000 #define KERNEL_MAX_SIZE (128 * MiB) #define INITRD_LOAD_ADDR 0x28000000 @@ -133,7 +136,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); @@ -141,32 +144,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]))); @@ -236,33 +241,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); } @@ -299,6 +326,17 @@ PnvChip *pnv_chip_add_phb(PnvChip *chip, PnvPHB *phb) return chip; } +/* + * Same as spapr pa_features_207 except pnv always enables CI largepages bit. + * HTM is always enabled because TCG does implement HTM, it's just a + * degenerate implementation. + */ +static const uint8_t pa_features_207[] = { 24, 0, + 0xf6, 0x3f, 0xc7, 0xc0, 0x00, 0xf0, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 }; + static void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt) { static const char compat[] = "ibm,power8-xscom\0ibm,xscom"; @@ -311,11 +349,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) { @@ -323,6 +365,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"; @@ -335,8 +406,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; + + offset = pnv_dt_core(chip, pnv_core, fdt); - 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) { @@ -346,6 +425,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"; @@ -358,8 +471,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; + + offset = pnv_dt_core(chip, pnv_core, fdt); + + _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", + pa_features_31, sizeof(pa_features_31)))); - pnv_dt_core(chip, pnv_core, fdt); + if (pnv_core->big_core) { + i++; /* Big-core groups two QEMU cores */ + } } if (chip->ram_size) { @@ -592,13 +713,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. @@ -619,21 +740,26 @@ 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) @@ -641,7 +767,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); } @@ -650,25 +777,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); } @@ -677,45 +827,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, @@ -755,15 +904,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 */ @@ -790,12 +938,14 @@ 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; int i; char *chip_typename; - DriveInfo *pnor = drive_get(IF_MTD, 0, 0); + DriveInfo *pnor; DeviceState *dev; if (kvm_enabled()) { @@ -811,12 +961,32 @@ 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); /* * Create our simple PNOR device */ dev = qdev_new(TYPE_PNV_PNOR); + pnor = drive_get(IF_MTD, 0, 0); + if (!pnor && defaults_enabled()) { + fw_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, PNOR_FILE_NAME); + if (!fw_filename) { + warn_report("Could not find PNOR '%s'", PNOR_FILE_NAME); + } else { + QemuOpts *opts; + opts = drive_add(IF_MTD, -1, fw_filename, "format=raw,readonly=on"); + pnor = drive_new(opts, IF_MTD, &error_fatal); + g_free(fw_filename); + } + } if (pnor) { qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(pnor)); } @@ -862,6 +1032,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; @@ -885,20 +1070,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 (machine->smp.threads > 8) { - error_report("Cannot support more than 8 threads/core " - "on a powernv machine"); + 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" + 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..., @@ -932,6 +1149,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. @@ -970,7 +1191,7 @@ static void pnv_init(MachineState *machine) * Since we can not reach the remote BMC machine with LPC memops, * map it always for now. */ - memory_region_add_subregion(pnv->chips[0]->fw_mr, PNOR_SPI_OFFSET, + memory_region_add_subregion(pnv->chips[0]->fw_mr, pnv->pnor->lpc_address, &pnv->pnor->mmio); /* @@ -979,6 +1200,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); + } } /* @@ -987,9 +1215,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, @@ -1026,9 +1261,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); } /* @@ -1041,14 +1276,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) -{ - return (chip->chip_id << 8) | (core_id << 2); +static void pnv_get_pir_tir_p9(PnvChip *chip, + uint32_t core_id, uint32_t thread_id, + uint32_t *pir, uint32_t *tir) +{ + 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) -{ - return (chip->chip_id << 8) | (core_id << 2); +/* + * 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) +{ + 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, @@ -1090,9 +1372,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, @@ -1134,9 +1416,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); } /* @@ -1217,10 +1499,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++) { @@ -1228,8 +1509,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); @@ -1249,12 +1533,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) { @@ -1263,11 +1542,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, @@ -1292,31 +1571,32 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) return; } + /* HOMER (must be created before OCC) */ + object_property_set_link(OBJECT(&chip8->homer), "chip", OBJECT(chip), + &error_abort); + if (!qdev_realize(DEVICE(&chip8->homer), NULL, errp)) { + return; + } + /* Homer Xscom region */ + pnv_xscom_add_subregion(chip, PNV_XSCOM_PBA_BASE, &chip8->homer.pba_regs); + /* Homer RAM region */ + memory_region_add_subregion(get_system_memory(), chip8->homer.base, + &chip8->homer.mem); + /* Create the simplified OCC model */ + object_property_set_link(OBJECT(&chip8->occ), "homer", + OBJECT(&chip8->homer), &error_abort); if (!qdev_realize(DEVICE(&chip8->occ), NULL, errp)) { return; } 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), &chip8->occ.sram_regs); - /* HOMER */ - object_property_set_link(OBJECT(&chip8->homer), "chip", OBJECT(chip), - &error_abort); - if (!qdev_realize(DEVICE(&chip8->homer), NULL, errp)) { - return; - } - /* Homer Xscom region */ - pnv_xscom_add_subregion(chip, PNV_XSCOM_PBA_BASE, &chip8->homer.pba_regs); - - /* Homer mmio region */ - memory_region_add_subregion(get_system_memory(), PNV_HOMER_BASE(chip), - &chip8->homer.regs); - /* PHB controllers */ for (i = 0; i < chip8->num_phbs; i++) { PnvPHB *phb = chip8->phbs[i]; @@ -1338,7 +1618,7 @@ static uint32_t pnv_chip_power8_xscom_pcba(PnvChip *chip, uint64_t addr) return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf); } -static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) +static void pnv_chip_power8e_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); @@ -1346,7 +1626,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; @@ -1362,7 +1642,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) &k->parent_realize); } -static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) +static void pnv_chip_power8_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); @@ -1370,7 +1650,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; @@ -1386,7 +1666,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) &k->parent_realize); } -static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) +static void pnv_chip_power8nvl_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); @@ -1394,7 +1674,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; @@ -1417,6 +1697,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"); @@ -1425,6 +1706,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); @@ -1438,6 +1721,10 @@ 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, @@ -1483,6 +1770,7 @@ static void pnv_chip_power9_pec_realize(PnvChip *chip, Error **errp) for (i = 0; i < chip->num_pecs; i++) { PnvPhb4PecState *pec = &chip9->pecs[i]; PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec); + uint32_t pec_cplt_base; uint32_t pec_nest_base; uint32_t pec_pci_base; @@ -1495,9 +1783,12 @@ static void pnv_chip_power9_pec_realize(PnvChip *chip, Error **errp) return; } + pec_cplt_base = pecc->xscom_cplt_base(pec); pec_nest_base = pecc->xscom_nest_base(pec); pec_pci_base = pecc->xscom_pci_base(pec); + pnv_xscom_add_subregion(chip, pec_cplt_base, + &pec->nest_pervasive.xscom_ctrl_regs_mr); pnv_xscom_add_subregion(chip, pec_nest_base, &pec->nest_regs_mr); pnv_xscom_add_subregion(chip, pec_pci_base, &pec->pci_regs_mr); } @@ -1510,20 +1801,25 @@ 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); + pnv_xscom_init(chip, PNV9_XSCOM_SIZE, PNV9_XSCOM_BASE(chip)); + + pcc->parent_realize(dev, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV9_XSCOM_BASE(chip)); - pcc->parent_realize(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); + /* 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) { @@ -1549,12 +1845,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, @@ -1571,17 +1867,18 @@ 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)); - /* Create the simplified OCC model */ - if (!qdev_realize(DEVICE(&chip9->occ), NULL, errp)) { + /* 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_OCC_BASE, &chip9->occ.xscom_regs); - qdev_connect_gpio_out(DEVICE(&chip9->occ), 0, qdev_get_gpio_in( - DEVICE(&chip9->psi), PSIHB9_IRQ_OCC)); - - /* OCC SRAM model */ - memory_region_add_subregion(get_system_memory(), PNV9_OCC_SENSOR_BASE(chip), - &chip9->occ.sram_regs); + pnv_xscom_add_subregion(chip, PNV9_XSCOM_CHIPTOD_BASE, + &chip9->chiptod.xscom_regs); /* SBE */ if (!qdev_realize(DEVICE(&chip9->sbe), NULL, errp)) { @@ -1592,9 +1889,9 @@ 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 */ + /* HOMER (must be created before OCC) */ object_property_set_link(OBJECT(&chip9->homer), "chip", OBJECT(chip), &error_abort); if (!qdev_realize(DEVICE(&chip9->homer), NULL, errp)) { @@ -1602,10 +1899,23 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) } /* Homer Xscom region */ pnv_xscom_add_subregion(chip, PNV9_XSCOM_PBA_BASE, &chip9->homer.pba_regs); + /* Homer RAM region */ + memory_region_add_subregion(get_system_memory(), chip9->homer.base, + &chip9->homer.mem); + + /* Create the simplified OCC model */ + object_property_set_link(OBJECT(&chip9->occ), "homer", + OBJECT(&chip9->homer), &error_abort); + 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(psi9), PSIHB9_IRQ_OCC)); - /* Homer mmio region */ - memory_region_add_subregion(get_system_memory(), PNV9_HOMER_BASE(chip), - &chip9->homer.regs); + /* OCC SRAM model */ + memory_region_add_subregion(get_system_memory(), PNV9_OCC_SENSOR_BASE(chip), + &chip9->occ.sram_regs); /* PEC PHBs */ pnv_chip_power9_pec_realize(chip, &local_err); @@ -1613,6 +1923,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) @@ -1621,14 +1954,15 @@ static uint32_t pnv_chip_power9_xscom_pcba(PnvChip *chip, uint64_t addr) return addr >> 3; } -static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) +static void pnv_chip_power9_class_init(ObjectClass *klass, const 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; @@ -1640,6 +1974,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); @@ -1652,14 +1988,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; @@ -1667,6 +2008,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) @@ -1699,6 +2049,7 @@ static void pnv_chip_power10_phb_realize(PnvChip *chip, Error **errp) for (i = 0; i < chip->num_pecs; i++) { PnvPhb4PecState *pec = &chip10->pecs[i]; PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec); + uint32_t pec_cplt_base; uint32_t pec_nest_base; uint32_t pec_pci_base; @@ -1711,9 +2062,12 @@ static void pnv_chip_power10_phb_realize(PnvChip *chip, Error **errp) return; } + pec_cplt_base = pecc->xscom_cplt_base(pec); pec_nest_base = pecc->xscom_nest_base(pec); pec_pci_base = pecc->xscom_pci_base(pec); + pnv_xscom_add_subregion(chip, pec_cplt_base, + &pec->nest_pervasive.xscom_ctrl_regs_mr); pnv_xscom_add_subregion(chip, pec_nest_base, &pec->nest_regs_mr); pnv_xscom_add_subregion(chip, pec_pci_base, &pec->pci_regs_mr); } @@ -1725,20 +2079,25 @@ 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); + pnv_xscom_init(chip, PNV10_XSCOM_SIZE, PNV10_XSCOM_BASE(chip)); + + pcc->parent_realize(dev, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV10_XSCOM_BASE(chip)); - pcc->parent_realize(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); + /* 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) { @@ -1790,7 +2149,35 @@ 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); + + /* HOMER (must be created before OCC) */ + object_property_set_link(OBJECT(&chip10->homer), "chip", OBJECT(chip), + &error_abort); + if (!qdev_realize(DEVICE(&chip10->homer), NULL, errp)) { + return; + } + /* Homer Xscom region */ + pnv_xscom_add_subregion(chip, PNV10_XSCOM_PBA_BASE, + &chip10->homer.pba_regs); + /* Homer RAM region */ + memory_region_add_subregion(get_system_memory(), chip10->homer.base, + &chip10->homer.mem); + /* Create the simplified OCC model */ + object_property_set_link(OBJECT(&chip10->occ), "homer", + OBJECT(&chip10->homer), &error_abort); if (!qdev_realize(DEVICE(&chip10->occ), NULL, errp)) { return; } @@ -1815,19 +2202,18 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) qdev_connect_gpio_out(DEVICE(&chip10->sbe), 0, qdev_get_gpio_in( DEVICE(&chip10->psi), PSIHB9_IRQ_PSU)); - /* HOMER */ - object_property_set_link(OBJECT(&chip10->homer), "chip", OBJECT(chip), - &error_abort); - if (!qdev_realize(DEVICE(&chip10->homer), NULL, errp)) { + /* N1 chiplet */ + if (!qdev_realize(DEVICE(&chip10->n1_chiplet), NULL, errp)) { return; } - /* Homer Xscom region */ - pnv_xscom_add_subregion(chip, PNV10_XSCOM_PBA_BASE, - &chip10->homer.pba_regs); + 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); - /* Homer mmio region */ - memory_region_add_subregion(get_system_memory(), PNV10_HOMER_BASE(chip), - &chip10->homer.regs); + 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); @@ -1835,6 +2221,79 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) 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); + object_property_set_int(OBJECT(&chip10->pib_spic[i]), "chip-id", + chip->chip_id, &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) @@ -1843,14 +2302,15 @@ static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr) return addr >> 3; } -static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) +static void pnv_chip_power10_class_init(ObjectClass *klass, const 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; @@ -1862,12 +2322,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; @@ -1888,6 +2351,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) { @@ -1899,11 +2373,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); @@ -1911,7 +2386,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; @@ -1938,12 +2413,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 */ @@ -1968,17 +2450,18 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) } } -static Property pnv_chip_properties[] = { +static const Property pnv_chip_properties[] = { DEFINE_PROP_UINT32("chip-id", PnvChip, chip_id, 0), DEFINE_PROP_UINT64("ram-start", PnvChip, ram_start, 0), DEFINE_PROP_UINT64("ram-size", PnvChip, ram_size, 0), DEFINE_PROP_UINT32("nr-cores", PnvChip, nr_cores, 1), DEFINE_PROP_UINT64("cores-mask", PnvChip, cores_mask, 0x0), DEFINE_PROP_UINT32("nr-threads", PnvChip, nr_threads, 1), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_BOOL("big-core", PnvChip, big_core, false), + DEFINE_PROP_BOOL("lpar-per-core", PnvChip, lpar_per_core, false), }; -static void pnv_chip_class_init(ObjectClass *klass, void *data) +static void pnv_chip_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1988,6 +2471,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; @@ -2005,6 +2503,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); @@ -2073,29 +2586,31 @@ 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); + for (i = 0; i < pnv->num_chips; i++) { + PnvChip *chip = pnv->chips[i]; - /* 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); - } + /* First CPU presenters */ + pnv_chip_foreach_cpu(chip, pnv_pic_intc_print_info, buf); - for (i = 0; i < pnv->num_chips; i++) { - PNV_CHIP_GET_CLASS(pnv->chips[i])->pic_print_info(pnv->chips[i], mon); + /* Then other devices, PHB, PSI, XIVE */ + PNV_CHIP_GET_CLASS(chip)->pic_print_info(chip, buf); } } static int pnv_match_nvt(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match) { @@ -2109,8 +2624,8 @@ static int pnv_match_nvt(XiveFabric *xfb, uint8_t format, XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); int count; - count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore, - priority, logic_serv, match); + count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, + cam_ignore, priority, logic_serv, match); if (count < 0) { return count; @@ -2124,7 +2639,7 @@ static int pnv_match_nvt(XiveFabric *xfb, uint8_t format, static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match) { @@ -2138,8 +2653,8 @@ static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); int count; - count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore, - priority, logic_serv, match); + count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, + cam_ignore, priority, logic_serv, match); if (count < 0) { return count; @@ -2151,7 +2666,65 @@ static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, return total_count; } -static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) +static int pnv10_xive_broadcast(XiveFabric *xfb, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, + uint8_t priority) +{ + PnvMachineState *pnv = PNV_MACHINE(xfb); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]); + XivePresenter *xptr = XIVE_PRESENTER(&chip10->xive); + XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); + + xpc->broadcast(xptr, nvt_blk, nvt_idx, crowd, cam_ignore, priority); + } + return 0; +} + +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, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); XICSFabricClass *xic = XICS_FABRIC_CLASS(oc); @@ -2173,11 +2746,14 @@ 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); } -static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) +static void pnv_machine_power9_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc); @@ -2195,16 +2771,28 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) 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, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); @@ -2216,39 +2804,63 @@ 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; + xfc->broadcast = pnv10_xive_broadcast; 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, const 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, + const 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); @@ -2272,18 +2884,36 @@ 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); } } -static void pnv_machine_class_init(ObjectClass *oc, void *data) +static void pnv_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc); @@ -2334,11 +2964,16 @@ 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, .class_init = pnv_machine_power10_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XIVE_FABRIC }, { }, }, @@ -2347,7 +2982,7 @@ static const TypeInfo types[] = { .name = MACHINE_TYPE_NAME("powernv9"), .parent = TYPE_PNV_MACHINE, .class_init = pnv_machine_power9_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XIVE_FABRIC }, { }, }, @@ -2356,7 +2991,7 @@ static const TypeInfo types[] = { .name = MACHINE_TYPE_NAME("powernv8"), .parent = TYPE_PNV_MACHINE, .class_init = pnv_machine_power8_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XICS_FABRIC }, { }, }, @@ -2368,7 +3003,7 @@ static const TypeInfo types[] = { .instance_size = sizeof(PnvMachineState), .class_init = pnv_machine_class_init, .class_size = sizeof(PnvMachineClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_INTERRUPT_STATS_PROVIDER }, { TYPE_NMI }, { }, diff --git a/hw/ppc/pnv_adu.c b/hw/ppc/pnv_adu.c new file mode 100644 index 00000000000..005fbda4750 --- /dev/null +++ b/hw/ppc/pnv_adu.c @@ -0,0 +1,217 @@ +/* + * 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 const Property pnv_adu_properties[] = { + DEFINE_PROP_LINK("lpc", PnvADU, lpc, TYPE_PNV_LPC, PnvLpcController *), +}; + +static void pnv_adu_class_init(ObjectClass *klass, const 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 = (const 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 99f1e8d7f9f..fb70a8c1f22 100644 --- a/hw/ppc/pnv_bmc.c +++ b/hw/ppc/pnv_bmc.c @@ -174,8 +174,8 @@ static void hiomap_cmd(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, { PnvPnor *pnor = PNV_PNOR(object_property_get_link(OBJECT(ibs), "pnor", &error_abort)); + uint32_t pnor_addr = pnor->lpc_address; uint32_t pnor_size = pnor->size; - uint32_t pnor_addr = PNOR_SPI_OFFSET; bool readonly = false; rsp_buffer_push(rsp, cmd[2]); @@ -251,10 +251,38 @@ static const IPMINetfn hiomap_netfn = { void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor) { + uint32_t pnor_addr = pnor->lpc_address; + uint32_t pnor_size = pnor->size; + if (!pnv_bmc_is_simulator(bmc)) { return; } + /* + * The HIOMAP protocol uses block units and 16-bit addressing. + * Prevent overflow or misalign. + */ + if (pnor_addr >= 1U << (BLOCK_SHIFT + 16)) { + warn_report("PNOR address is larger than 2^%d, disabling PNOR", + BLOCK_SHIFT + 16); + return; + } + if (pnor_addr & ((1U << BLOCK_SHIFT) - 1)) { + warn_report("PNOR address is not aligned to 2^%d, disabling PNOR", + BLOCK_SHIFT); + return; + } + if (pnor_size > 1U << (BLOCK_SHIFT + 16)) { + warn_report("PNOR size is larger than 2^%d, disabling PNOR", + BLOCK_SHIFT + 16); + return; + } + if (pnor_size & ((1U << BLOCK_SHIFT) - 1)) { + warn_report("PNOR size is not aligned to 2^%d, disabling PNOR", + BLOCK_SHIFT); + return; + } + object_ref(OBJECT(pnor)); object_property_add_const_link(OBJECT(bmc), "pnor", OBJECT(pnor)); @@ -269,13 +297,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 00000000000..b9e9c7ba3db --- /dev/null +++ b/hw/ppc/pnv_chiptod.c @@ -0,0 +1,584 @@ +/* + * 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 "system/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 const 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 *), +}; + +static void pnv_chiptod_power9_class_init(ObjectClass *klass, const 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 = (const 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, const 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 = (const 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, const 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 8c7afe037f0..08c20224b97 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -18,7 +18,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" @@ -58,6 +58,10 @@ 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); @@ -181,16 +185,43 @@ static const MemoryRegionOps pnv_core_power9_xscom_ops = { */ #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: - val = 0; + 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__, @@ -203,9 +234,55 @@ static uint64_t pnv_core_power10_xscom_read(void *opaque, hwaddr addr, 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); + env->quiesced = true; + ppc_maybe_interrupt(env); + cpu_pause(cs); + } + if (val & PPC_BIT(6 + 8 * i)) { /* start */ + val &= ~PPC_BIT(6 + 8 * i); + env->quiesced = false; + ppc_maybe_interrupt(env); + cpu_resume(cs); + } + if (val & PPC_BIT(4 + 8 * i)) { /* sreset */ + val &= ~PPC_BIT(4 + 8 * i); + env->quiesced = false; + ppc_maybe_interrupt(env); + pnv_cpu_do_nmi_resume(cs); + } + if (val & PPC_BIT(3 + 8 * i)) { /* clear maint */ + env->quiesced = false; + ppc_maybe_interrupt(env); + /* + * 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); @@ -226,9 +303,10 @@ static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp, int thread_index) { CPUPPCState *env = &cpu->env; - int core_pir; - ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; - ppc_spr_t *tir = &env->spr_cb[SPR_TIR]; + 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); @@ -242,10 +320,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); + + pcc->get_pir_tir(pc->chip, core_hwid, thread_index, &pir, &tir); + pir_spr->default_value = pir; + tir_spr->default_value = tir; - tir->default_value = thread_index; - pir->default_value = core_pir + thread_index; + 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); @@ -278,16 +370,22 @@ 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); } @@ -341,14 +439,17 @@ static void pnv_core_unrealize(DeviceState *dev) g_free(pc->threads); } -static Property pnv_core_properties[] = { - DEFINE_PROP_UINT32("pir", PnvCore, pir, 0), +static const Property pnv_core_properties[] = { + 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(), }; -static void pnv_core_power8_class_init(ObjectClass *oc, void *data) +static void pnv_core_power8_class_init(ObjectClass *oc, const void *data) { PnvCoreClass *pcc = PNV_CORE_CLASS(oc); @@ -356,7 +457,7 @@ static void pnv_core_power8_class_init(ObjectClass *oc, void *data) pcc->xscom_size = PNV_XSCOM_EX_SIZE; } -static void pnv_core_power9_class_init(ObjectClass *oc, void *data) +static void pnv_core_power9_class_init(ObjectClass *oc, const void *data) { PnvCoreClass *pcc = PNV_CORE_CLASS(oc); @@ -364,7 +465,7 @@ static void pnv_core_power9_class_init(ObjectClass *oc, void *data) pcc->xscom_size = PNV_XSCOM_EX_SIZE; } -static void pnv_core_power10_class_init(ObjectClass *oc, void *data) +static void pnv_core_power10_class_init(ObjectClass *oc, const void *data) { PnvCoreClass *pcc = PNV_CORE_CLASS(oc); @@ -372,7 +473,7 @@ static void pnv_core_power10_class_init(ObjectClass *oc, void *data) pcc->xscom_size = PNV10_XSCOM_EC_SIZE; } -static void pnv_core_class_init(ObjectClass *oc, void *data) +static void pnv_core_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -504,6 +605,7 @@ static const MemoryRegionOps pnv_quad_power10_xscom_ops = { 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; @@ -511,10 +613,14 @@ static uint64_t pnv_qme_power10_xscom_read(void *opaque, hwaddr addr, * Forth nibble selects the core within a quad, mask it to process read * for any core. */ - switch (offset & ~0xf000) { - case P10_QME_SPWU_HYP: + switch (offset & ~PPC_BITMASK32(16, 19)) { case P10_QME_SSH_HYP: - return 0; + 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); @@ -526,9 +632,22 @@ static uint64_t pnv_qme_power10_xscom_read(void *opaque, hwaddr addr, 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) { + 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); @@ -577,12 +696,11 @@ static void pnv_quad_power10_realize(DeviceState *dev, Error **errp) pqc->xscom_qme_size); } -static Property pnv_quad_properties[] = { +static const Property pnv_quad_properties[] = { DEFINE_PROP_UINT32("quad-id", PnvQuad, quad_id, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void pnv_quad_power9_class_init(ObjectClass *oc, void *data) +static void pnv_quad_power9_class_init(ObjectClass *oc, const void *data) { PnvQuadClass *pqc = PNV_QUAD_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -593,7 +711,7 @@ static void pnv_quad_power9_class_init(ObjectClass *oc, void *data) pqc->xscom_size = PNV9_XSCOM_EQ_SIZE; } -static void pnv_quad_power10_class_init(ObjectClass *oc, void *data) +static void pnv_quad_power10_class_init(ObjectClass *oc, const void *data) { PnvQuadClass *pqc = PNV_QUAD_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -607,7 +725,7 @@ static void pnv_quad_power10_class_init(ObjectClass *oc, void *data) pqc->xscom_qme_size = PNV10_XSCOM_QME_SIZE; } -static void pnv_quad_class_init(ObjectClass *oc, void *data) +static void pnv_quad_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ppc/pnv_homer.c b/hw/ppc/pnv_homer.c index f9a203d11d0..2208ffe632c 100644 --- a/hw/ppc/pnv_homer.c +++ b/hw/ppc/pnv_homer.c @@ -20,8 +20,8 @@ #include "qemu/log.h" #include "qapi/error.h" #include "exec/hwaddr.h" -#include "exec/memory.h" -#include "sysemu/cpus.h" +#include "system/memory.h" +#include "system/cpus.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "hw/ppc/pnv.h" @@ -29,94 +29,6 @@ #include "hw/ppc/pnv_homer.h" #include "hw/ppc/pnv_xscom.h" - -static bool core_max_array(PnvHomer *homer, hwaddr addr) -{ - int i; - PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); - - for (i = 0; i <= homer->chip->nr_cores; i++) { - if (addr == (hmrc->core_max_base + i)) { - return true; - } - } - return false; -} - -/* P8 Pstate table */ - -#define PNV8_OCC_PSTATE_VERSION 0x1f8001 -#define PNV8_OCC_PSTATE_MIN 0x1f8003 -#define PNV8_OCC_PSTATE_VALID 0x1f8000 -#define PNV8_OCC_PSTATE_THROTTLE 0x1f8002 -#define PNV8_OCC_PSTATE_NOM 0x1f8004 -#define PNV8_OCC_PSTATE_TURBO 0x1f8005 -#define PNV8_OCC_PSTATE_ULTRA_TURBO 0x1f8006 -#define PNV8_OCC_PSTATE_DATA 0x1f8008 -#define PNV8_OCC_PSTATE_ID_ZERO 0x1f8010 -#define PNV8_OCC_PSTATE_ID_ONE 0x1f8018 -#define PNV8_OCC_PSTATE_ID_TWO 0x1f8020 -#define PNV8_OCC_VDD_VOLTAGE_IDENTIFIER 0x1f8012 -#define PNV8_OCC_VCS_VOLTAGE_IDENTIFIER 0x1f8013 -#define PNV8_OCC_PSTATE_ZERO_FREQUENCY 0x1f8014 -#define PNV8_OCC_PSTATE_ONE_FREQUENCY 0x1f801c -#define PNV8_OCC_PSTATE_TWO_FREQUENCY 0x1f8024 -#define PNV8_CORE_MAX_BASE 0x1f8810 - - -static uint64_t pnv_power8_homer_read(void *opaque, hwaddr addr, - unsigned size) -{ - PnvHomer *homer = PNV_HOMER(opaque); - - switch (addr) { - case PNV8_OCC_PSTATE_VERSION: - case PNV8_OCC_PSTATE_MIN: - case PNV8_OCC_PSTATE_ID_ZERO: - return 0; - case PNV8_OCC_PSTATE_VALID: - case PNV8_OCC_PSTATE_THROTTLE: - case PNV8_OCC_PSTATE_NOM: - case PNV8_OCC_PSTATE_TURBO: - case PNV8_OCC_PSTATE_ID_ONE: - case PNV8_OCC_VDD_VOLTAGE_IDENTIFIER: - case PNV8_OCC_VCS_VOLTAGE_IDENTIFIER: - return 1; - case PNV8_OCC_PSTATE_ULTRA_TURBO: - case PNV8_OCC_PSTATE_ID_TWO: - return 2; - case PNV8_OCC_PSTATE_DATA: - return 0x1000000000000000; - /* P8 frequency for 0, 1, and 2 pstates */ - case PNV8_OCC_PSTATE_ZERO_FREQUENCY: - case PNV8_OCC_PSTATE_ONE_FREQUENCY: - case PNV8_OCC_PSTATE_TWO_FREQUENCY: - return 3000; - } - /* pstate table core max array */ - if (core_max_array(homer, addr)) { - return 1; - } - return 0; -} - -static void pnv_power8_homer_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - /* callback function defined to homer write */ - return; -} - -static const MemoryRegionOps pnv_power8_homer_ops = { - .read = pnv_power8_homer_read, - .write = pnv_power8_homer_write, - .valid.min_access_size = 1, - .valid.max_access_size = 8, - .impl.min_access_size = 1, - .impl.max_access_size = 8, - .endianness = DEVICE_BIG_ENDIAN, -}; - /* P8 PBA BARs */ #define PBA_BAR0 0x00 #define PBA_BAR1 0x01 @@ -131,16 +43,16 @@ static uint64_t pnv_homer_power8_pba_read(void *opaque, hwaddr addr, unsigned size) { PnvHomer *homer = PNV_HOMER(opaque); - PnvChip *chip = homer->chip; + PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); uint32_t reg = addr >> 3; uint64_t val = 0; switch (reg) { case PBA_BAR0: - val = PNV_HOMER_BASE(chip); + val = homer->base; break; case PBA_BARMASK0: /* P8 homer region mask */ - val = (PNV_HOMER_SIZE - 1) & 0x300000; + val = (hmrc->size - 1) & 0x300000; break; case PBA_BAR3: /* P8 occ common area */ val = PNV_OCC_COMMON_AREA_BASE; @@ -172,15 +84,19 @@ static const MemoryRegionOps pnv_homer_power8_pba_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_homer_power8_class_init(ObjectClass *klass, void *data) +static hwaddr pnv_homer_power8_get_base(PnvChip *chip) +{ + return PNV_HOMER_BASE(chip); +} + +static void pnv_homer_power8_class_init(ObjectClass *klass, const void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); + homer->get_base = pnv_homer_power8_get_base; + homer->size = PNV_HOMER_SIZE; homer->pba_size = PNV_XSCOM_PBA_SIZE; homer->pba_ops = &pnv_homer_power8_pba_ops; - homer->homer_size = PNV_HOMER_SIZE; - homer->homer_ops = &pnv_power8_homer_ops; - homer->core_max_base = PNV8_CORE_MAX_BASE; } static const TypeInfo pnv_homer_power8_type_info = { @@ -190,100 +106,20 @@ static const TypeInfo pnv_homer_power8_type_info = { .class_init = pnv_homer_power8_class_init, }; -/* P9 Pstate table */ - -#define PNV9_OCC_PSTATE_ID_ZERO 0xe2018 -#define PNV9_OCC_PSTATE_ID_ONE 0xe2020 -#define PNV9_OCC_PSTATE_ID_TWO 0xe2028 -#define PNV9_OCC_PSTATE_DATA 0xe2000 -#define PNV9_OCC_PSTATE_DATA_AREA 0xe2008 -#define PNV9_OCC_PSTATE_MIN 0xe2003 -#define PNV9_OCC_PSTATE_NOM 0xe2004 -#define PNV9_OCC_PSTATE_TURBO 0xe2005 -#define PNV9_OCC_PSTATE_ULTRA_TURBO 0xe2818 -#define PNV9_OCC_MAX_PSTATE_ULTRA_TURBO 0xe2006 -#define PNV9_OCC_PSTATE_MAJOR_VERSION 0xe2001 -#define PNV9_OCC_OPAL_RUNTIME_DATA 0xe2b85 -#define PNV9_CHIP_HOMER_IMAGE_POINTER 0x200008 -#define PNV9_CHIP_HOMER_BASE 0x0 -#define PNV9_OCC_PSTATE_ZERO_FREQUENCY 0xe201c -#define PNV9_OCC_PSTATE_ONE_FREQUENCY 0xe2024 -#define PNV9_OCC_PSTATE_TWO_FREQUENCY 0xe202c -#define PNV9_OCC_ROLE_MASTER_OR_SLAVE 0xe2002 -#define PNV9_CORE_MAX_BASE 0xe2819 - - -static uint64_t pnv_power9_homer_read(void *opaque, hwaddr addr, - unsigned size) -{ - PnvHomer *homer = PNV_HOMER(opaque); - - switch (addr) { - case PNV9_OCC_MAX_PSTATE_ULTRA_TURBO: - case PNV9_OCC_PSTATE_ID_ZERO: - return 0; - case PNV9_OCC_PSTATE_DATA: - case PNV9_OCC_ROLE_MASTER_OR_SLAVE: - case PNV9_OCC_PSTATE_NOM: - case PNV9_OCC_PSTATE_TURBO: - case PNV9_OCC_PSTATE_ID_ONE: - case PNV9_OCC_PSTATE_ULTRA_TURBO: - case PNV9_OCC_OPAL_RUNTIME_DATA: - return 1; - case PNV9_OCC_PSTATE_MIN: - case PNV9_OCC_PSTATE_ID_TWO: - return 2; - - /* 3000 khz frequency for 0, 1, and 2 pstates */ - case PNV9_OCC_PSTATE_ZERO_FREQUENCY: - case PNV9_OCC_PSTATE_ONE_FREQUENCY: - case PNV9_OCC_PSTATE_TWO_FREQUENCY: - return 3000; - case PNV9_OCC_PSTATE_MAJOR_VERSION: - return 0x90; - case PNV9_CHIP_HOMER_BASE: - case PNV9_OCC_PSTATE_DATA_AREA: - case PNV9_CHIP_HOMER_IMAGE_POINTER: - return 0x1000000000000000; - } - /* pstate table core max array */ - if (core_max_array(homer, addr)) { - return 1; - } - return 0; -} - -static void pnv_power9_homer_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - /* callback function defined to homer write */ - return; -} - -static const MemoryRegionOps pnv_power9_homer_ops = { - .read = pnv_power9_homer_read, - .write = pnv_power9_homer_write, - .valid.min_access_size = 1, - .valid.max_access_size = 8, - .impl.min_access_size = 1, - .impl.max_access_size = 8, - .endianness = DEVICE_BIG_ENDIAN, -}; - static uint64_t pnv_homer_power9_pba_read(void *opaque, hwaddr addr, unsigned size) { PnvHomer *homer = PNV_HOMER(opaque); - PnvChip *chip = homer->chip; + PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); uint32_t reg = addr >> 3; uint64_t val = 0; switch (reg) { case PBA_BAR0: - val = PNV9_HOMER_BASE(chip); + val = homer->base; break; case PBA_BARMASK0: /* P9 homer region mask */ - val = (PNV9_HOMER_SIZE - 1) & 0x300000; + val = (hmrc->size - 1) & 0x300000; break; case PBA_BAR2: /* P9 occ common area */ val = PNV9_OCC_COMMON_AREA_BASE; @@ -315,15 +151,19 @@ static const MemoryRegionOps pnv_homer_power9_pba_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_homer_power9_class_init(ObjectClass *klass, void *data) +static hwaddr pnv_homer_power9_get_base(PnvChip *chip) +{ + return PNV9_HOMER_BASE(chip); +} + +static void pnv_homer_power9_class_init(ObjectClass *klass, const void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); + homer->get_base = pnv_homer_power9_get_base; + homer->size = PNV_HOMER_SIZE; homer->pba_size = PNV9_XSCOM_PBA_SIZE; homer->pba_ops = &pnv_homer_power9_pba_ops; - homer->homer_size = PNV9_HOMER_SIZE; - homer->homer_ops = &pnv_power9_homer_ops; - homer->core_max_base = PNV9_CORE_MAX_BASE; } static const TypeInfo pnv_homer_power9_type_info = { @@ -337,16 +177,16 @@ static uint64_t pnv_homer_power10_pba_read(void *opaque, hwaddr addr, unsigned size) { PnvHomer *homer = PNV_HOMER(opaque); - PnvChip *chip = homer->chip; + PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); uint32_t reg = addr >> 3; uint64_t val = 0; switch (reg) { case PBA_BAR0: - val = PNV10_HOMER_BASE(chip); + val = homer->base; break; case PBA_BARMASK0: /* P10 homer region mask */ - val = (PNV10_HOMER_SIZE - 1) & 0x300000; + val = (hmrc->size - 1) & 0x300000; break; case PBA_BAR2: /* P10 occ common area */ val = PNV10_OCC_COMMON_AREA_BASE; @@ -378,15 +218,19 @@ static const MemoryRegionOps pnv_homer_power10_pba_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_homer_power10_class_init(ObjectClass *klass, void *data) +static hwaddr pnv_homer_power10_get_base(PnvChip *chip) +{ + return PNV10_HOMER_BASE(chip); +} + +static void pnv_homer_power10_class_init(ObjectClass *klass, const void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); + homer->get_base = pnv_homer_power10_get_base; + homer->size = PNV_HOMER_SIZE; homer->pba_size = PNV10_XSCOM_PBA_SIZE; homer->pba_ops = &pnv_homer_power10_pba_ops; - homer->homer_size = PNV10_HOMER_SIZE; - homer->homer_ops = &pnv_power9_homer_ops; /* TODO */ - homer->core_max_base = PNV9_CORE_MAX_BASE; } static const TypeInfo pnv_homer_power10_type_info = { @@ -400,24 +244,29 @@ static void pnv_homer_realize(DeviceState *dev, Error **errp) { PnvHomer *homer = PNV_HOMER(dev); PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); + char homer_str[32]; assert(homer->chip); pnv_xscom_region_init(&homer->pba_regs, OBJECT(dev), hmrc->pba_ops, homer, "xscom-pba", hmrc->pba_size); - /* homer region */ - memory_region_init_io(&homer->regs, OBJECT(dev), - hmrc->homer_ops, homer, "homer-main-memory", - hmrc->homer_size); + /* Homer RAM region */ + homer->base = hmrc->get_base(homer->chip); + + snprintf(homer_str, sizeof(homer_str), "homer-chip%d-memory", + homer->chip->chip_id); + if (!memory_region_init_ram(&homer->mem, OBJECT(homer), + homer_str, hmrc->size, errp)) { + return; + } } -static Property pnv_homer_properties[] = { +static const Property pnv_homer_properties[] = { DEFINE_PROP_LINK("chip", PnvHomer, chip, TYPE_PNV_CHIP, PnvChip *), - DEFINE_PROP_END_OF_LIST(), }; -static void pnv_homer_class_init(ObjectClass *klass, void *data) +static void pnv_homer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c new file mode 100644 index 00000000000..60de4794917 --- /dev/null +++ b/hw/ppc/pnv_i2c.c @@ -0,0 +1,583 @@ +/* + * 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 "system/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 const 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), +}; + +static void pnv_i2c_class_init(ObjectClass *klass, const 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 = (const 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 d692858bee7..f6beba0917d 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -64,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 ... */ @@ -84,7 +85,7 @@ enum { #define ISA_IO_SIZE 0x00010000 #define ISA_MEM_SIZE 0x10000000 -#define ISA_FW_SIZE 0x10000000 +#define ISA_FW_SIZE 0x100000000 #define LPC_IO_OPB_ADDR 0xd0010000 #define LPC_IO_OPB_SIZE 0x00010000 #define LPC_MEM_OPB_ADDR 0xe0000000 @@ -235,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, @@ -276,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 | @@ -293,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 ? */ @@ -352,6 +353,8 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static void pnv_lpc_opb_noresponse(PnvLpcController *lpc); + static uint64_t pnv_lpc_mmio_read(void *opaque, hwaddr addr, unsigned size) { PnvLpcController *lpc = PNV_LPC(opaque); @@ -375,6 +378,7 @@ static uint64_t pnv_lpc_mmio_read(void *opaque, hwaddr addr, unsigned size) } if (result != MEMTX_OK) { + pnv_lpc_opb_noresponse(lpc); qemu_log_mask(LOG_GUEST_ERROR, "OPB read failed at @0x%" HWADDR_PRIx "\n", addr); } @@ -405,6 +409,7 @@ static void pnv_lpc_mmio_write(void *opaque, hwaddr addr, } if (result != MEMTX_OK) { + pnv_lpc_opb_noresponse(lpc); qemu_log_mask(LOG_GUEST_ERROR, "OPB write failed at @0x%" HWADDR_PRIx "\n", addr); } @@ -420,22 +425,85 @@ 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; + + active_irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask; + if (!(lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN)) { + active_irqs &= ~LPC_HC_IRQ_SERIRQ_ALL; + } + + /* Reflect the interrupt */ + if (lpc->psi_has_serirq) { + /* + * POWER9 and later 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. + */ + 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; + } + } - /* Update LPC controller to OPB line */ - if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) { - uint32_t irqs; + 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]); - irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask; - lpc_to_opb_irq = (irqs != 0); + /* + * POWER9 and later LPC controller internal irqs still go via the OPB + * and LPCHC PSI irqs like P8, so take the SERIRQs out and continue. + */ + active_irqs &= ~LPC_HC_IRQ_SERIRQ_ALL; } - /* We don't honor the polarity register, it's pointless and unused + /* + * 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 (lpc_to_opb_irq) { + if (active_irqs) { lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC; } else { lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC; @@ -444,8 +512,13 @@ static void pnv_lpc_eval_irqs(PnvLpcController *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); + qemu_set_irq(lpc->psi_irq_lpchc, lpc->opb_irq_stat != 0); +} + +static void pnv_lpc_opb_noresponse(PnvLpcController *lpc) +{ + lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SYNC_NORESP_ERR; + pnv_lpc_eval_irqs(lpc); } static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) @@ -488,10 +561,13 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, switch (addr) { case LPC_HC_FW_SEG_IDSEL: - /* XXX Actually figure out how that works as this impact - * memory regions/aliases + /* + * ISA FW "devices" are modeled as 16x256MB windows into a + * 4GB LPC FW address space. */ + val &= 0xf; /* Selects device 0-15 */ lpc->lpc_hc_fw_seg_idsel = val; + memory_region_set_alias_offset(&lpc->opb_isa_fw, val * LPC_FW_OPB_SIZE); break; case LPC_HC_FW_RD_ACC_SIZE: lpc->lpc_hc_fw_rd_acc_size = val; @@ -505,7 +581,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: @@ -536,10 +619,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: @@ -568,11 +651,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; @@ -627,7 +714,7 @@ static void pnv_lpc_power8_realize(DeviceState *dev, Error **errp) PNV_XSCOM_LPC_SIZE); } -static void pnv_lpc_power8_class_init(ObjectClass *klass, void *data) +static void pnv_lpc_power8_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); @@ -645,7 +732,7 @@ static const TypeInfo pnv_lpc_power8_info = { .name = TYPE_PNV8_LPC, .parent = TYPE_PNV_LPC, .class_init = pnv_lpc_power8_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } @@ -657,6 +744,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); @@ -666,9 +755,12 @@ 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) +static void pnv_lpc_power9_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvLpcClass *plc = PNV_LPC_CLASS(klass); @@ -685,7 +777,7 @@ static const TypeInfo pnv_lpc_power9_info = { .class_init = pnv_lpc_power9_class_init, }; -static void pnv_lpc_power10_class_init(ObjectClass *klass, void *data) +static void pnv_lpc_power10_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -709,9 +801,9 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) memory_region_init(&lpc->opb_mr, OBJECT(dev), "lpc-opb", 0x100000000ull); address_space_init(&lpc->opb_as, &lpc->opb_mr, "lpc-opb"); - /* Create ISA IO and Mem space regions which are the root of - * the ISA bus (ie, ISA address spaces). We don't create a - * separate one for FW which we alias to memory. + /* + * Create ISA IO, Mem, and FW space regions which are the root of + * the ISA bus (ie, ISA address spaces). */ memory_region_init(&lpc->isa_io, OBJECT(dev), "isa-io", ISA_IO_SIZE); memory_region_init(&lpc->isa_mem, OBJECT(dev), "isa-mem", ISA_MEM_SIZE); @@ -744,13 +836,18 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR, &lpc->lpc_hc_regs); - qdev_init_gpio_out(dev, &lpc->psi_irq, 1); + qdev_init_gpio_out_named(dev, &lpc->psi_irq_lpchc, "LPCHC", 1); } -static void pnv_lpc_class_init(ObjectClass *klass, void *data) +static const Property pnv_lpc_properties[] = { + DEFINE_PROP_BOOL("psi-serirq", PnvLpcController, psi_has_serirq, false), +}; + +static void pnv_lpc_class_init(ObjectClass *klass, const 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; @@ -796,18 +893,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); + } } } @@ -838,6 +951,7 @@ 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_register_input_irqs(isa_bus, irqs); diff --git a/hw/ppc/pnv_n1_chiplet.c b/hw/ppc/pnv_n1_chiplet.c new file mode 100644 index 00000000000..053f6473f22 --- /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, const 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 = (const 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 00000000000..1b1b14fed95 --- /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, const 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 = (const 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 48123ceae17..24b789c191c 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -24,40 +24,53 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_chip.h" #include "hw/ppc/pnv_xscom.h" #include "hw/ppc/pnv_occ.h" +#define P8_HOMER_OPAL_DATA_OFFSET 0x1F8000 +#define P9_HOMER_OPAL_DATA_OFFSET 0x0E2000 + #define OCB_OCI_OCCMISC 0x4020 #define OCB_OCI_OCCMISC_AND 0x4021 #define OCB_OCI_OCCMISC_OR 0x4022 +#define OCCMISC_PSI_IRQ PPC_BIT(0) +#define OCCMISC_IRQ_SHMEM PPC_BIT(3) /* OCC sensors */ -#define OCC_SENSOR_DATA_BLOCK_OFFSET 0x580000 -#define OCC_SENSOR_DATA_VALID 0x580001 -#define OCC_SENSOR_DATA_VERSION 0x580002 -#define OCC_SENSOR_DATA_READING_VERSION 0x580004 -#define OCC_SENSOR_DATA_NR_SENSORS 0x580008 -#define OCC_SENSOR_DATA_NAMES_OFFSET 0x580010 -#define OCC_SENSOR_DATA_READING_PING_OFFSET 0x580014 -#define OCC_SENSOR_DATA_READING_PONG_OFFSET 0x58000c -#define OCC_SENSOR_DATA_NAME_LENGTH 0x58000d -#define OCC_SENSOR_NAME_STRUCTURE_TYPE 0x580023 -#define OCC_SENSOR_LOC_CORE 0x580022 -#define OCC_SENSOR_LOC_GPU 0x580020 -#define OCC_SENSOR_TYPE_POWER 0x580003 -#define OCC_SENSOR_NAME 0x580005 -#define HWMON_SENSORS_MASK 0x58001e -#define SLW_IMAGE_BASE 0x0 +#define OCC_SENSOR_DATA_BLOCK_OFFSET 0x0000 +#define OCC_SENSOR_DATA_VALID 0x0001 +#define OCC_SENSOR_DATA_VERSION 0x0002 +#define OCC_SENSOR_DATA_READING_VERSION 0x0004 +#define OCC_SENSOR_DATA_NR_SENSORS 0x0008 +#define OCC_SENSOR_DATA_NAMES_OFFSET 0x0010 +#define OCC_SENSOR_DATA_READING_PING_OFFSET 0x0014 +#define OCC_SENSOR_DATA_READING_PONG_OFFSET 0x000c +#define OCC_SENSOR_DATA_NAME_LENGTH 0x000d +#define OCC_SENSOR_NAME_STRUCTURE_TYPE 0x0023 +#define OCC_SENSOR_LOC_CORE 0x0022 +#define OCC_SENSOR_LOC_GPU 0x0020 +#define OCC_SENSOR_TYPE_POWER 0x0003 +#define OCC_SENSOR_NAME 0x0005 +#define HWMON_SENSORS_MASK 0x001e static void pnv_occ_set_misc(PnvOCC *occ, uint64_t val) { - bool irq_state; - - val &= 0xffff000000000000ull; + val &= PPC_BITMASK(0, 18); /* Mask out unimplemented bits */ occ->occmisc = val; - irq_state = !!(val >> 63); - qemu_set_irq(occ->psi_irq, irq_state); + + /* + * OCCMISC IRQ bit triggers the interrupt on a 0->1 edge, but not clear + * how that is handled in PSI so it is level-triggered here, which is not + * really correct (but skiboot is okay with it). + */ + qemu_set_irq(occ->psi_irq, !!(val & OCCMISC_PSI_IRQ)); +} + +static void pnv_occ_raise_msg_irq(PnvOCC *occ) +{ + pnv_occ_set_misc(occ, occ->occmisc | OCCMISC_PSI_IRQ | OCCMISC_IRQ_SHMEM); } static uint64_t pnv_occ_power8_xscom_read(void *opaque, hwaddr addr, @@ -129,8 +142,6 @@ static uint64_t pnv_occ_common_area_read(void *opaque, hwaddr addr, case HWMON_SENSORS_MASK: case OCC_SENSOR_LOC_GPU: return 0x8e00; - case SLW_IMAGE_BASE: - return 0x1000000000000000; } return 0; } @@ -139,7 +150,6 @@ static void pnv_occ_common_area_write(void *opaque, hwaddr addr, uint64_t val, unsigned width) { /* callback function defined to occ common area write */ - return; } static const MemoryRegionOps pnv_occ_power8_xscom_ops = { @@ -162,10 +172,14 @@ const MemoryRegionOps pnv_occ_sram_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_occ_power8_class_init(ObjectClass *klass, void *data) +static void pnv_occ_power8_class_init(ObjectClass *klass, const void *data) { PnvOCCClass *poc = PNV_OCC_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + dc->desc = "PowerNV OCC Controller (POWER8)"; + poc->opal_shared_memory_offset = P8_HOMER_OPAL_DATA_OFFSET; + poc->opal_shared_memory_version = 0x02; poc->xscom_size = PNV_XSCOM_OCC_SIZE; poc->xscom_ops = &pnv_occ_power8_xscom_ops; } @@ -232,14 +246,17 @@ static const MemoryRegionOps pnv_occ_power9_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_occ_power9_class_init(ObjectClass *klass, void *data) +static void pnv_occ_power9_class_init(ObjectClass *klass, const void *data) { PnvOCCClass *poc = PNV_OCC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "PowerNV OCC Controller (POWER9)"; + poc->opal_shared_memory_offset = P9_HOMER_OPAL_DATA_OFFSET; + poc->opal_shared_memory_version = 0x90; poc->xscom_size = PNV9_XSCOM_OCC_SIZE; poc->xscom_ops = &pnv_occ_power9_xscom_ops; + assert(!dc->user_creatable); } static const TypeInfo pnv_occ_power9_type_info = { @@ -249,23 +266,52 @@ static const TypeInfo pnv_occ_power9_type_info = { .class_init = pnv_occ_power9_class_init, }; -static void pnv_occ_power10_class_init(ObjectClass *klass, void *data) +static void pnv_occ_power10_class_init(ObjectClass *klass, const void *data) { + PnvOCCClass *poc = PNV_OCC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "PowerNV OCC Controller (POWER10)"; + poc->opal_shared_memory_offset = P9_HOMER_OPAL_DATA_OFFSET; + poc->opal_shared_memory_version = 0xA0; + poc->xscom_size = PNV9_XSCOM_OCC_SIZE; + poc->xscom_ops = &pnv_occ_power9_xscom_ops; + assert(!dc->user_creatable); } static const TypeInfo pnv_occ_power10_type_info = { .name = TYPE_PNV10_OCC, - .parent = TYPE_PNV9_OCC, + .parent = TYPE_PNV_OCC, .class_init = pnv_occ_power10_class_init, }; +static bool occ_init_homer_memory(PnvOCC *occ, Error **errp); +static bool occ_model_tick(PnvOCC *occ); + +/* Relatively arbitrary */ +#define OCC_POLL_MS 100 + +static void occ_state_machine_timer(void *opaque) +{ + PnvOCC *occ = opaque; + uint64_t next = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + OCC_POLL_MS; + + if (occ_model_tick(occ)) { + timer_mod(&occ->state_machine_timer, next); + } +} + static void pnv_occ_realize(DeviceState *dev, Error **errp) { PnvOCC *occ = PNV_OCC(dev); PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); + PnvHomer *homer = occ->homer; + + assert(homer); + + if (!occ_init_homer_memory(occ, errp)) { + return; + } occ->occmisc = 0; @@ -279,14 +325,22 @@ static void pnv_occ_realize(DeviceState *dev, Error **errp) PNV_OCC_SENSOR_DATA_BLOCK_SIZE); qdev_init_gpio_out(dev, &occ->psi_irq, 1); + + timer_init_ms(&occ->state_machine_timer, QEMU_CLOCK_VIRTUAL, + occ_state_machine_timer, occ); + timer_mod(&occ->state_machine_timer, OCC_POLL_MS); } -static void pnv_occ_class_init(ObjectClass *klass, void *data) +static const Property pnv_occ_properties[] = { + DEFINE_PROP_LINK("homer", PnvOCC, homer, TYPE_PNV_HOMER, PnvHomer *), +}; + +static void pnv_occ_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pnv_occ_realize; - dc->desc = "PowerNV OCC Controller"; + device_class_set_props(dc, pnv_occ_properties); dc->user_creatable = false; } @@ -308,3 +362,561 @@ static void pnv_occ_register_types(void) } type_init(pnv_occ_register_types); + +/* + * From skiboot/hw/occ.c with following changes: + * - tab to space conversion + * - Type conversions u8->uint8_t s8->int8_t __be16->uint16_t etc + * - __packed -> QEMU_PACKED + */ +/* OCC Communication Area for PStates */ + +#define OPAL_DYNAMIC_DATA_OFFSET 0x0B80 +/* relative to HOMER_OPAL_DATA_OFFSET */ + +#define MAX_PSTATES 256 +#define MAX_P8_CORES 12 +#define MAX_P9_CORES 24 +#define MAX_P10_CORES 32 + +#define MAX_OPAL_CMD_DATA_LENGTH 4090 +#define MAX_OCC_RSP_DATA_LENGTH 8698 + +#define P8_PIR_CORE_MASK 0xFFF8 +#define P9_PIR_QUAD_MASK 0xFFF0 +#define P10_PIR_CHIP_MASK 0x0000 +#define FREQ_MAX_IN_DOMAIN 0 +#define FREQ_MOST_RECENTLY_SET 1 + +/** + * OCC-OPAL Shared Memory Region + * + * Reference document : + * https://github.com/open-power/docs/blob/master/occ/OCC_OpenPwr_FW_Interfaces.pdf + * + * Supported layout versions: + * - 0x01, 0x02 : P8 + * https://github.com/open-power/occ/blob/master_p8/src/occ/proc/proc_pstate.h + * + * - 0x90 : P9 + * https://github.com/open-power/occ/blob/master/src/occ_405/proc/proc_pstate.h + * In 0x90 the data is separated into :- + * -- Static Data (struct occ_pstate_table): Data is written once by OCC + * -- Dynamic Data (struct occ_dynamic_data): Data is updated at runtime + * + * struct occ_pstate_table - Pstate table layout + * @valid: Indicates if data is valid + * @version: Layout version [Major/Minor] + * @v2.throttle: Reason for limiting the max pstate + * @v9.occ_role: OCC role (Master/Slave) + * @v#.pstate_min: Minimum pstate ever allowed + * @v#.pstate_nom: Nominal pstate + * @v#.pstate_turbo: Maximum turbo pstate + * @v#.pstate_ultra_turbo: Maximum ultra turbo pstate and the maximum + * pstate ever allowed + * @v#.pstates: Pstate-id and frequency list from Pmax to Pmin + * @v#.pstates.id: Pstate-id + * @v#.pstates.flags: Pstate-flag(reserved) + * @v2.pstates.vdd: Voltage Identifier + * @v2.pstates.vcs: Voltage Identifier + * @v#.pstates.freq_khz: Frequency in KHz + * @v#.core_max[1..N]: Max pstate with N active cores + * @spare/reserved/pad: Unused data + */ +struct occ_pstate_table { + uint8_t valid; + uint8_t version; + union QEMU_PACKED { + struct QEMU_PACKED { /* Version 0x01 and 0x02 */ + uint8_t throttle; + int8_t pstate_min; + int8_t pstate_nom; + int8_t pstate_turbo; + int8_t pstate_ultra_turbo; + uint8_t spare; + uint64_t reserved; + struct QEMU_PACKED { + int8_t id; + uint8_t flags; + uint8_t vdd; + uint8_t vcs; + uint32_t freq_khz; + } pstates[MAX_PSTATES]; + int8_t core_max[MAX_P8_CORES]; + uint8_t pad[100]; + } v2; + struct QEMU_PACKED { /* Version 0x90 */ + uint8_t occ_role; + uint8_t pstate_min; + uint8_t pstate_nom; + uint8_t pstate_turbo; + uint8_t pstate_ultra_turbo; + uint8_t spare; + uint64_t reserved1; + uint64_t reserved2; + struct QEMU_PACKED { + uint8_t id; + uint8_t flags; + uint16_t reserved; + uint32_t freq_khz; + } pstates[MAX_PSTATES]; + uint8_t core_max[MAX_P9_CORES]; + uint8_t pad[56]; + } v9; + struct QEMU_PACKED { /* Version 0xA0 */ + uint8_t occ_role; + uint8_t pstate_min; + uint8_t pstate_fixed_freq; + uint8_t pstate_base; + uint8_t pstate_ultra_turbo; + uint8_t pstate_fmax; + uint8_t minor; + uint8_t pstate_bottom_throttle; + uint8_t spare; + uint8_t spare1; + uint32_t reserved_32; + uint64_t reserved_64; + struct QEMU_PACKED { + uint8_t id; + uint8_t valid; + uint16_t reserved; + uint32_t freq_khz; + } pstates[MAX_PSTATES]; + uint8_t core_max[MAX_P10_CORES]; + uint8_t pad[48]; + } v10; + }; +} QEMU_PACKED; + +/** + * OPAL-OCC Command Response Interface + * + * OPAL-OCC Command Buffer + * + * --------------------------------------------------------------------- + * | OPAL | Cmd | OPAL | | Cmd Data | Cmd Data | OPAL | + * | Cmd | Request | OCC | Reserved | Length | Length | Cmd | + * | Flags | ID | Cmd | | (MSB) | (LSB) | Data... | + * --------------------------------------------------------------------- + * | ….OPAL Command Data up to max of Cmd Data Length 4090 bytes | + * | | + * --------------------------------------------------------------------- + * + * OPAL Command Flag + * + * ----------------------------------------------------------------- + * | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | + * | (msb) | | | | | | | (lsb) | + * ----------------------------------------------------------------- + * |Cmd | | | | | | | | + * |Ready | | | | | | | | + * ----------------------------------------------------------------- + * + * struct opal_command_buffer - Defines the layout of OPAL command buffer + * @flag: Provides general status of the command + * @request_id: Token to identify request + * @cmd: Command sent + * @data_size: Command data length + * @data: Command specific data + * @spare: Unused byte + */ +struct opal_command_buffer { + uint8_t flag; + uint8_t request_id; + uint8_t cmd; + uint8_t spare; + uint16_t data_size; + uint8_t data[MAX_OPAL_CMD_DATA_LENGTH]; +} QEMU_PACKED; + +/** + * OPAL-OCC Response Buffer + * + * --------------------------------------------------------------------- + * | OCC | Cmd | OPAL | Response | Rsp Data | Rsp Data | OPAL | + * | Rsp | Request | OCC | Status | Length | Length | Rsp | + * | Flags | ID | Cmd | | (MSB) | (LSB) | Data... | + * --------------------------------------------------------------------- + * | ….OPAL Response Data up to max of Rsp Data Length 8698 bytes | + * | | + * --------------------------------------------------------------------- + * + * OCC Response Flag + * + * ----------------------------------------------------------------- + * | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | + * | (msb) | | | | | | | (lsb) | + * ----------------------------------------------------------------- + * | | | | | | |OCC in | Rsp | + * | | | | | | |progress|Ready | + * ----------------------------------------------------------------- + * + * struct occ_response_buffer - Defines the layout of OCC response buffer + * @flag: Provides general status of the response + * @request_id: Token to identify request + * @cmd: Command requested + * @status: Indicates success/failure status of + * the command + * @data_size: Response data length + * @data: Response specific data + */ +struct occ_response_buffer { + uint8_t flag; + uint8_t request_id; + uint8_t cmd; + uint8_t status; + uint16_t data_size; + uint8_t data[MAX_OCC_RSP_DATA_LENGTH]; +} QEMU_PACKED; + +/** + * OCC-OPAL Shared Memory Interface Dynamic Data Vx90 + * + * struct occ_dynamic_data - Contains runtime attributes + * @occ_state: Current state of OCC + * @major_version: Major version number + * @minor_version: Minor version number (backwards compatible) + * Version 1 indicates GPU presence populated + * @gpus_present: Bitmask of GPUs present (on systems where GPU + * presence is detected through APSS) + * @cpu_throttle: Reason for limiting the max pstate + * @mem_throttle: Reason for throttling memory + * @quick_pwr_drop: Indicates if QPD is asserted + * @pwr_shifting_ratio: Indicates the current percentage of power to + * take away from the CPU vs GPU when shifting + * power to maintain a power cap. Value of 100 + * means take all power from CPU. + * @pwr_cap_type: Indicates type of power cap in effect + * @hard_min_pwr_cap: Hard minimum system power cap in Watts. + * Guaranteed unless hardware failure + * @max_pwr_cap: Maximum allowed system power cap in Watts + * @cur_pwr_cap: Current system power cap + * @soft_min_pwr_cap: Soft powercap minimum. OCC may or may not be + * able to maintain this + * @spare/reserved: Unused data + * @cmd: Opal Command Buffer + * @rsp: OCC Response Buffer + */ +struct occ_dynamic_data { + uint8_t occ_state; + uint8_t major_version; + uint8_t minor_version; + uint8_t gpus_present; + union QEMU_PACKED { + struct QEMU_PACKED { /* Version 0x90 */ + uint8_t spare1; + } v9; + struct QEMU_PACKED { /* Version 0xA0 */ + uint8_t wof_enabled; + } v10; + }; + uint8_t cpu_throttle; + uint8_t mem_throttle; + uint8_t quick_pwr_drop; + uint8_t pwr_shifting_ratio; + uint8_t pwr_cap_type; + uint16_t hard_min_pwr_cap; + uint16_t max_pwr_cap; + uint16_t cur_pwr_cap; + uint16_t soft_min_pwr_cap; + uint8_t pad[110]; + struct opal_command_buffer cmd; + struct occ_response_buffer rsp; +} QEMU_PACKED; + +enum occ_response_status { + OCC_RSP_SUCCESS = 0x00, + OCC_RSP_INVALID_COMMAND = 0x11, + OCC_RSP_INVALID_CMD_DATA_LENGTH = 0x12, + OCC_RSP_INVALID_DATA = 0x13, + OCC_RSP_INTERNAL_ERROR = 0x15, +}; + +#define OCC_ROLE_SLAVE 0x00 +#define OCC_ROLE_MASTER 0x01 + +#define OCC_FLAG_RSP_READY 0x01 +#define OCC_FLAG_CMD_IN_PROGRESS 0x02 +#define OPAL_FLAG_CMD_READY 0x80 + +#define PCAP_MAX_POWER_W 100 +#define PCAP_SOFT_MIN_POWER_W 20 +#define PCAP_HARD_MIN_POWER_W 10 + +static bool occ_write_static_data(PnvOCC *occ, + struct occ_pstate_table *static_data, + Error **errp) +{ + PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); + PnvHomer *homer = occ->homer; + hwaddr static_addr = homer->base + poc->opal_shared_memory_offset; + MemTxResult ret; + + ret = address_space_write(&address_space_memory, static_addr, + MEMTXATTRS_UNSPECIFIED, static_data, + sizeof(*static_data)); + if (ret != MEMTX_OK) { + error_setg(errp, "OCC: cannot write OCC-OPAL static data"); + return false; + } + + return true; +} + +static bool occ_read_dynamic_data(PnvOCC *occ, + struct occ_dynamic_data *dynamic_data, + Error **errp) +{ + PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); + PnvHomer *homer = occ->homer; + hwaddr static_addr = homer->base + poc->opal_shared_memory_offset; + hwaddr dynamic_addr = static_addr + OPAL_DYNAMIC_DATA_OFFSET; + MemTxResult ret; + + ret = address_space_read(&address_space_memory, dynamic_addr, + MEMTXATTRS_UNSPECIFIED, dynamic_data, + sizeof(*dynamic_data)); + if (ret != MEMTX_OK) { + error_setg(errp, "OCC: cannot read OCC-OPAL dynamic data"); + return false; + } + + return true; +} + +static bool occ_write_dynamic_data(PnvOCC *occ, + struct occ_dynamic_data *dynamic_data, + Error **errp) +{ + PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); + PnvHomer *homer = occ->homer; + hwaddr static_addr = homer->base + poc->opal_shared_memory_offset; + hwaddr dynamic_addr = static_addr + OPAL_DYNAMIC_DATA_OFFSET; + MemTxResult ret; + + ret = address_space_write(&address_space_memory, dynamic_addr, + MEMTXATTRS_UNSPECIFIED, dynamic_data, + sizeof(*dynamic_data)); + if (ret != MEMTX_OK) { + error_setg(errp, "OCC: cannot write OCC-OPAL dynamic data"); + return false; + } + + return true; +} + +static bool occ_opal_send_response(PnvOCC *occ, + struct occ_dynamic_data *dynamic_data, + enum occ_response_status status, + uint8_t *data, uint16_t datalen) +{ + struct opal_command_buffer *cmd = &dynamic_data->cmd; + struct occ_response_buffer *rsp = &dynamic_data->rsp; + + rsp->request_id = cmd->request_id; + rsp->cmd = cmd->cmd; + rsp->status = status; + rsp->data_size = cpu_to_be16(datalen); + if (datalen) { + memcpy(rsp->data, data, datalen); + } + if (!occ_write_dynamic_data(occ, dynamic_data, NULL)) { + return false; + } + /* Would be a memory barrier here */ + rsp->flag = OCC_FLAG_RSP_READY; + cmd->flag = 0; + if (!occ_write_dynamic_data(occ, dynamic_data, NULL)) { + return false; + } + + pnv_occ_raise_msg_irq(occ); + + return true; +} + +/* Returns error status */ +static bool occ_opal_process_command(PnvOCC *occ, + struct occ_dynamic_data *dynamic_data) +{ + struct opal_command_buffer *cmd = &dynamic_data->cmd; + struct occ_response_buffer *rsp = &dynamic_data->rsp; + + if (rsp->flag == 0) { + /* Spend one "tick" in the in-progress state */ + rsp->flag = OCC_FLAG_CMD_IN_PROGRESS; + return occ_write_dynamic_data(occ, dynamic_data, NULL); + } else if (rsp->flag != OCC_FLAG_CMD_IN_PROGRESS) { + return occ_opal_send_response(occ, dynamic_data, + OCC_RSP_INTERNAL_ERROR, + NULL, 0); + } + + switch (cmd->cmd) { + case 0xD1: { /* SET_POWER_CAP */ + uint16_t data; + if (be16_to_cpu(cmd->data_size) != 2) { + return occ_opal_send_response(occ, dynamic_data, + OCC_RSP_INVALID_CMD_DATA_LENGTH, + (uint8_t *)&dynamic_data->cur_pwr_cap, + 2); + } + data = be16_to_cpu(*(uint16_t *)cmd->data); + if (data == 0) { /* clear power cap */ + dynamic_data->pwr_cap_type = 0x00; /* none */ + data = PCAP_MAX_POWER_W; + } else { + dynamic_data->pwr_cap_type = 0x02; /* user set in-band */ + if (data < PCAP_HARD_MIN_POWER_W) { + data = PCAP_HARD_MIN_POWER_W; + } else if (data > PCAP_MAX_POWER_W) { + data = PCAP_MAX_POWER_W; + } + } + dynamic_data->cur_pwr_cap = cpu_to_be16(data); + return occ_opal_send_response(occ, dynamic_data, + OCC_RSP_SUCCESS, + (uint8_t *)&dynamic_data->cur_pwr_cap, 2); + } + + default: + return occ_opal_send_response(occ, dynamic_data, + OCC_RSP_INVALID_COMMAND, + NULL, 0); + } + g_assert_not_reached(); +} + +static bool occ_model_tick(PnvOCC *occ) +{ + QEMU_UNINITIALIZED struct occ_dynamic_data dynamic_data; + + if (!occ_read_dynamic_data(occ, &dynamic_data, NULL)) { + /* Can't move OCC state field to safe because we can't map it! */ + qemu_log("OCC: failed to read HOMER data, shutting down OCC\n"); + return false; + } + if (dynamic_data.cmd.flag == OPAL_FLAG_CMD_READY) { + if (!occ_opal_process_command(occ, &dynamic_data)) { + qemu_log("OCC: failed to write HOMER data, shutting down OCC\n"); + return false; + } + } + + return true; +} + +static bool occ_init_homer_memory(PnvOCC *occ, Error **errp) +{ + PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); + PnvHomer *homer = occ->homer; + PnvChip *chip = homer->chip; + struct occ_pstate_table static_data; + struct occ_dynamic_data dynamic_data; + int i; + + memset(&static_data, 0, sizeof(static_data)); + static_data.valid = 1; + static_data.version = poc->opal_shared_memory_version; + switch (poc->opal_shared_memory_version) { + case 0x02: + static_data.v2.throttle = 0; + static_data.v2.pstate_min = -2; + static_data.v2.pstate_nom = -1; + static_data.v2.pstate_turbo = -1; + static_data.v2.pstate_ultra_turbo = 0; + static_data.v2.pstates[0].id = 0; + static_data.v2.pstates[1].freq_khz = cpu_to_be32(4000000); + static_data.v2.pstates[1].id = -1; + static_data.v2.pstates[1].freq_khz = cpu_to_be32(3000000); + static_data.v2.pstates[2].id = -2; + static_data.v2.pstates[2].freq_khz = cpu_to_be32(2000000); + for (i = 0; i < chip->nr_cores; i++) { + static_data.v2.core_max[i] = 1; + } + break; + case 0x90: + if (chip->chip_id == 0) { + static_data.v9.occ_role = OCC_ROLE_MASTER; + } else { + static_data.v9.occ_role = OCC_ROLE_SLAVE; + } + static_data.v9.pstate_min = 2; + static_data.v9.pstate_nom = 1; + static_data.v9.pstate_turbo = 1; + static_data.v9.pstate_ultra_turbo = 0; + static_data.v9.pstates[0].id = 0; + static_data.v9.pstates[0].freq_khz = cpu_to_be32(4000000); + static_data.v9.pstates[1].id = 1; + static_data.v9.pstates[1].freq_khz = cpu_to_be32(3000000); + static_data.v9.pstates[2].id = 2; + static_data.v9.pstates[2].freq_khz = cpu_to_be32(2000000); + for (i = 0; i < chip->nr_cores; i++) { + static_data.v9.core_max[i] = 1; + } + break; + case 0xA0: + if (chip->chip_id == 0) { + static_data.v10.occ_role = OCC_ROLE_MASTER; + } else { + static_data.v10.occ_role = OCC_ROLE_SLAVE; + } + static_data.v10.pstate_min = 4; + static_data.v10.pstate_fixed_freq = 3; + static_data.v10.pstate_base = 2; + static_data.v10.pstate_ultra_turbo = 0; + static_data.v10.pstate_fmax = 1; + static_data.v10.minor = 0x01; + static_data.v10.pstates[0].valid = 1; + static_data.v10.pstates[0].id = 0; + static_data.v10.pstates[0].freq_khz = cpu_to_be32(4200000); + static_data.v10.pstates[1].valid = 1; + static_data.v10.pstates[1].id = 1; + static_data.v10.pstates[1].freq_khz = cpu_to_be32(4000000); + static_data.v10.pstates[2].valid = 1; + static_data.v10.pstates[2].id = 2; + static_data.v10.pstates[2].freq_khz = cpu_to_be32(3800000); + static_data.v10.pstates[3].valid = 1; + static_data.v10.pstates[3].id = 3; + static_data.v10.pstates[3].freq_khz = cpu_to_be32(3000000); + static_data.v10.pstates[4].valid = 1; + static_data.v10.pstates[4].id = 4; + static_data.v10.pstates[4].freq_khz = cpu_to_be32(2000000); + for (i = 0; i < chip->nr_cores; i++) { + static_data.v10.core_max[i] = 1; + } + break; + default: + g_assert_not_reached(); + } + if (!occ_write_static_data(occ, &static_data, errp)) { + return false; + } + + memset(&dynamic_data, 0, sizeof(dynamic_data)); + dynamic_data.occ_state = 0x3; /* active */ + dynamic_data.major_version = 0x0; + dynamic_data.hard_min_pwr_cap = cpu_to_be16(PCAP_HARD_MIN_POWER_W); + dynamic_data.max_pwr_cap = cpu_to_be16(PCAP_MAX_POWER_W); + dynamic_data.cur_pwr_cap = cpu_to_be16(PCAP_MAX_POWER_W); + dynamic_data.soft_min_pwr_cap = cpu_to_be16(PCAP_SOFT_MIN_POWER_W); + switch (poc->opal_shared_memory_version) { + case 0xA0: + dynamic_data.minor_version = 0x1; + dynamic_data.v10.wof_enabled = 0x1; + break; + case 0x90: + dynamic_data.minor_version = 0x1; + break; + case 0x02: + dynamic_data.minor_version = 0x0; + break; + default: + g_assert_not_reached(); + } + if (!occ_write_dynamic_data(occ, &dynamic_data, errp)) { + return false; + } + + return true; +} diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c index 62804082992..af7cfd028ba 100644 --- a/hw/ppc/pnv_pnor.c +++ b/hw/ppc/pnv_pnor.c @@ -11,8 +11,8 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/units.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" +#include "system/block-backend.h" +#include "system/blockdev.h" #include "hw/loader.h" #include "hw/ppc/pnv_pnor.h" #include "hw/qdev-properties.h" @@ -108,17 +108,18 @@ static void pnv_pnor_realize(DeviceState *dev, Error **errp) memset(s->storage, 0xFF, s->size); } + s->lpc_address = PNOR_SPI_OFFSET; + memory_region_init_io(&s->mmio, OBJECT(s), &pnv_pnor_ops, s, TYPE_PNV_PNOR, s->size); } -static Property pnv_pnor_properties[] = { +static const Property pnv_pnor_properties[] = { DEFINE_PROP_INT64("size", PnvPnor, size, 128 * MiB), DEFINE_PROP_DRIVE("drive", PnvPnor, blk), - DEFINE_PROP_END_OF_LIST(), }; -static void pnv_pnor_class_init(ObjectClass *klass, void *data) +static void pnv_pnor_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index daaa2f0575f..5d947d8b520 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -18,14 +18,13 @@ */ #include "qemu/osdep.h" -#include "exec/address-spaces.h" +#include "system/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 "system/reset.h" #include "qapi/error.h" -#include "monitor/monitor.h" #include "hw/ppc/fdt.h" @@ -553,13 +552,12 @@ static int pnv_psi_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) return 0; } -static Property pnv_psi_properties[] = { +static const Property pnv_psi_properties[] = { DEFINE_PROP_UINT64("bar", PnvPsi, bar, 0), DEFINE_PROP_UINT64("fsp-bar", PnvPsi, fsp_bar, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void pnv_psi_power8_class_init(ObjectClass *klass, void *data) +static void pnv_psi_power8_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvPsiClass *ppc = PNV_PSI_CLASS(klass); @@ -738,8 +736,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); } } @@ -888,7 +887,7 @@ static void pnv_psi_power9_realize(DeviceState *dev, Error **errp) pnv_psi_realize(dev, errp); } -static void pnv_psi_power9_class_init(ObjectClass *klass, void *data) +static void pnv_psi_power9_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvPsiClass *ppc = PNV_PSI_CLASS(klass); @@ -897,7 +896,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; @@ -914,13 +913,13 @@ static const TypeInfo pnv_psi_power9_info = { .instance_size = sizeof(Pnv9Psi), .instance_init = pnv_psi_power9_instance_init, .class_init = pnv_psi_power9_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XIVE_NOTIFIER }, { }, }, }; -static void pnv_psi_power10_class_init(ObjectClass *klass, void *data) +static void pnv_psi_power10_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvPsiClass *ppc = PNV_PSI_CLASS(klass); @@ -940,7 +939,7 @@ static const TypeInfo pnv_psi_power10_info = { .class_init = pnv_psi_power10_class_init, }; -static void pnv_psi_class_init(ObjectClass *klass, void *data) +static void pnv_psi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); @@ -949,7 +948,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; } @@ -960,7 +959,7 @@ static const TypeInfo pnv_psi_info = { .class_init = pnv_psi_class_init, .class_size = sizeof(PnvPsiClass), .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } @@ -976,14 +975,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 74cee4eea7a..34dc013d47b 100644 --- a/hw/ppc/pnv_sbe.c +++ b/hw/ppc/pnv_sbe.c @@ -331,7 +331,7 @@ static const MemoryRegionOps pnv_sbe_power9_xscom_mbox_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_sbe_power9_class_init(ObjectClass *klass, void *data) +static void pnv_sbe_power9_class_init(ObjectClass *klass, const void *data) { PnvSBEClass *psc = PNV_SBE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -350,7 +350,7 @@ static const TypeInfo pnv_sbe_power9_type_info = { .class_init = pnv_sbe_power9_class_init, }; -static void pnv_sbe_power10_class_init(ObjectClass *klass, void *data) +static void pnv_sbe_power10_class_init(ObjectClass *klass, const void *data) { PnvSBEClass *psc = PNV_SBE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -386,7 +386,7 @@ static void pnv_sbe_realize(DeviceState *dev, Error **errp) sbe->timer = timer_new_us(QEMU_CLOCK_VIRTUAL, sbe_timer, sbe); } -static void pnv_sbe_class_init(ObjectClass *klass, void *data) +static void pnv_sbe_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c index d820e05e405..fbfec829d5b 100644 --- a/hw/ppc/pnv_xscom.c +++ b/hw/ppc/pnv_xscom.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "target/ppc/cpu.h" #include "hw/sysbus.h" @@ -44,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; } } @@ -78,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 */ @@ -122,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 */ @@ -221,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 aeb116d9199..43d0d0e7553 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -27,13 +27,14 @@ #include "hw/ppc/ppc.h" #include "hw/ppc/ppc_e500.h" #include "qemu/timer.h" -#include "sysemu/cpus.h" +#include "exec/cpu-interrupt.h" +#include "system/cpus.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "system/kvm.h" +#include "system/replay.h" +#include "system/runstate.h" #include "kvm_ppc.h" #include "migration/vmstate.h" #include "trace.h" @@ -47,7 +48,7 @@ void ppc_set_irq(PowerPCCPU *cpu, int irq, int level) unsigned int old_pending; /* We may already have the BQL if coming from the reset path */ - QEMU_IOTHREAD_LOCK_GUARD(); + BQL_LOCK_GUARD(); old_pending = env->pending_interrupts; @@ -267,7 +268,6 @@ static void power9_set_irq(void *opaque, int pin, int level) break; default: g_assert_not_reached(); - return; } } @@ -314,7 +314,7 @@ void store_40x_dbcr0(CPUPPCState *env, uint32_t val) { PowerPCCPU *cpu = env_archcpu(env); - qemu_mutex_lock_iothread(); + bql_lock(); switch ((val >> 28) & 0x3) { case 0x0: @@ -334,7 +334,7 @@ void store_40x_dbcr0(CPUPPCState *env, uint32_t val) break; } - qemu_mutex_unlock_iothread(); + bql_unlock(); } /* PowerPC 40x internal IRQ controller */ @@ -633,6 +633,16 @@ void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value) ((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) { ppc_tb_t *tb_env = env->tb_env; @@ -719,7 +729,9 @@ static inline int64_t __cpu_ppc_load_decr(CPUPPCState *env, int64_t now, int64_t decr; n = ns_to_tb(tb_env->decr_freq, now); - if (next > n && tb_env->flags & PPC_TIMER_BOOKE) { + + /* BookE timers stop when reaching 0. */ + if (next < n && tb_env->flags & PPC_TIMER_BOOKE) { decr = 0; } else { decr = next - n; @@ -738,7 +750,7 @@ static target_ulong _cpu_ppc_load_decr(CPUPPCState *env, int64_t now) 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) { @@ -1066,7 +1078,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() @@ -1112,16 +1124,21 @@ void cpu_ppc_tb_reset(CPUPPCState *env) timer_del(tb_env->hdecr_timer); ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 0); tb_env->hdecr_next = 0; + _cpu_ppc_store_hdecr(cpu, 0, 0, 0, 64); } /* * 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... + * + * On machine reset, this is called before icount is reset, so for + * icount-mode, setting TB registers using now == qemu_clock_get_ns() + * results in them being garbage after icount is reset. Use an + * explicit now == 0 to get a consistent reset state. */ - cpu_ppc_store_decr(env, -1); - cpu_ppc_store_hdecr(env, -1); - cpu_ppc_store_purr(env, 0x0000000000000000ULL); + _cpu_ppc_store_decr(cpu, 0, 0, -1, 64); + _cpu_ppc_store_purr(env, 0, 0); } void cpu_ppc_tb_free(CPUPPCState *env) diff --git a/hw/ppc/ppc405.h b/hw/ppc/ppc405.h deleted file mode 100644 index 9a4312691e1..00000000000 --- a/hw/ppc/ppc405.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * QEMU PowerPC 405 shared definitions - * - * Copyright (c) 2007 Jocelyn Mayer - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef PPC405_H -#define PPC405_H - -#include "qom/object.h" -#include "hw/ppc/ppc4xx.h" -#include "hw/intc/ppc-uic.h" -#include "hw/i2c/ppc4xx_i2c.h" - -/* PLB to OPB bridge */ -#define TYPE_PPC405_POB "ppc405-pob" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405PobState, PPC405_POB); -struct Ppc405PobState { - Ppc4xxDcrDeviceState parent_obj; - - uint32_t bear; - uint32_t besr0; - uint32_t besr1; -}; - -/* OPB arbitrer */ -#define TYPE_PPC405_OPBA "ppc405-opba" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405OpbaState, PPC405_OPBA); -struct Ppc405OpbaState { - SysBusDevice parent_obj; - - MemoryRegion io; - uint8_t cr; - uint8_t pr; -}; - -/* DMA controller */ -#define TYPE_PPC405_DMA "ppc405-dma" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405DmaState, PPC405_DMA); -struct Ppc405DmaState { - Ppc4xxDcrDeviceState parent_obj; - - qemu_irq irqs[4]; - uint32_t cr[4]; - uint32_t ct[4]; - uint32_t da[4]; - uint32_t sa[4]; - uint32_t sg[4]; - uint32_t sr; - uint32_t sgc; - uint32_t slp; - uint32_t pol; -}; - -/* GPIO */ -#define TYPE_PPC405_GPIO "ppc405-gpio" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405GpioState, PPC405_GPIO); -struct Ppc405GpioState { - SysBusDevice parent_obj; - - MemoryRegion io; - uint32_t or; - uint32_t tcr; - uint32_t osrh; - uint32_t osrl; - uint32_t tsrh; - uint32_t tsrl; - uint32_t odr; - uint32_t ir; - uint32_t rr1; - uint32_t isr1h; - uint32_t isr1l; -}; - -/* On Chip Memory */ -#define TYPE_PPC405_OCM "ppc405-ocm" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405OcmState, PPC405_OCM); -struct Ppc405OcmState { - Ppc4xxDcrDeviceState parent_obj; - - MemoryRegion ram; - MemoryRegion isarc_ram; - MemoryRegion dsarc_ram; - uint32_t isarc; - uint32_t isacntl; - uint32_t dsarc; - uint32_t dsacntl; -}; - -/* General purpose timers */ -#define TYPE_PPC405_GPT "ppc405-gpt" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405GptState, PPC405_GPT); -struct Ppc405GptState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - int64_t tb_offset; - uint32_t tb_freq; - QEMUTimer *timer; - qemu_irq irqs[5]; - uint32_t oe; - uint32_t ol; - uint32_t im; - uint32_t is; - uint32_t ie; - uint32_t comp[5]; - uint32_t mask[5]; -}; - -#define TYPE_PPC405_CPC "ppc405-cpc" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405CpcState, PPC405_CPC); - -enum { - PPC405EP_CPU_CLK = 0, - PPC405EP_PLB_CLK = 1, - PPC405EP_OPB_CLK = 2, - PPC405EP_EBC_CLK = 3, - PPC405EP_MAL_CLK = 4, - PPC405EP_PCI_CLK = 5, - PPC405EP_UART0_CLK = 6, - PPC405EP_UART1_CLK = 7, - PPC405EP_CLK_NB = 8, -}; - -struct Ppc405CpcState { - Ppc4xxDcrDeviceState parent_obj; - - uint32_t sysclk; - clk_setup_t clk_setup[PPC405EP_CLK_NB]; - uint32_t boot; - uint32_t epctl; - uint32_t pllmr[2]; - uint32_t ucr; - uint32_t srr; - uint32_t jtagid; - uint32_t pci; - /* Clock and power management */ - uint32_t er; - uint32_t fr; - uint32_t sr; -}; - -#define TYPE_PPC405_SOC "ppc405-soc" -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405SoCState, PPC405_SOC); - -struct Ppc405SoCState { - /* Private */ - DeviceState parent_obj; - - /* Public */ - PowerPCCPU cpu; - PPCUIC uic; - Ppc405CpcState cpc; - Ppc405GptState gpt; - Ppc405OcmState ocm; - Ppc405GpioState gpio; - Ppc405DmaState dma; - PPC4xxI2CState i2c; - Ppc4xxEbcState ebc; - Ppc405OpbaState opba; - Ppc405PobState pob; - Ppc4xxPlbState plb; - Ppc4xxMalState mal; - Ppc4xxSdramDdrState sdram; -}; - -#endif /* PPC405_H */ diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c deleted file mode 100644 index 4092ebc1ab5..00000000000 --- a/hw/ppc/ppc405_boards.c +++ /dev/null @@ -1,519 +0,0 @@ -/* - * QEMU PowerPC 405 evaluation boards emulation - * - * 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 "qemu/units.h" -#include "qapi/error.h" -#include "qemu/datadir.h" -#include "cpu.h" -#include "hw/ppc/ppc.h" -#include "hw/qdev-properties.h" -#include "hw/sysbus.h" -#include "ppc405.h" -#include "hw/rtc/m48t59.h" -#include "hw/block/flash.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" -#include "sysemu/block-backend.h" -#include "hw/boards.h" -#include "qemu/error-report.h" -#include "hw/loader.h" -#include "qemu/cutils.h" -#include "elf.h" - -#define BIOS_FILENAME "ppc405_rom.bin" -#define BIOS_SIZE (2 * MiB) - -#define KERNEL_LOAD_ADDR 0x01000000 -#define INITRD_LOAD_ADDR 0x01800000 - -#define PPC405EP_SDRAM_BASE 0x00000000 -#define PPC405EP_SRAM_BASE 0xFFF00000 -#define PPC405EP_SRAM_SIZE (512 * KiB) - -#define USE_FLASH_BIOS - -#define TYPE_PPC405_MACHINE MACHINE_TYPE_NAME("ppc405") -OBJECT_DECLARE_SIMPLE_TYPE(Ppc405MachineState, PPC405_MACHINE); - -struct Ppc405MachineState { - /* Private */ - MachineState parent_obj; - /* Public */ - - Ppc405SoCState soc; -}; - -/* CPU reset handler when booting directly from a loaded kernel */ -static struct boot_info { - uint32_t entry; - uint32_t bdloc; - uint32_t initrd_base; - uint32_t initrd_size; - uint32_t cmdline_base; - uint32_t cmdline_size; -} boot_info; - -static void main_cpu_reset(void *opaque) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - struct boot_info *bi = env->load_info; - - cpu_reset(CPU(cpu)); - - /* stack: top of sram */ - env->gpr[1] = PPC405EP_SRAM_BASE + PPC405EP_SRAM_SIZE - 8; - - /* Tune our boot state */ - env->gpr[3] = bi->bdloc; - env->gpr[4] = bi->initrd_base; - env->gpr[5] = bi->initrd_base + bi->initrd_size; - env->gpr[6] = bi->cmdline_base; - env->gpr[7] = bi->cmdline_size; - - env->nip = bi->entry; -} - -/* Bootinfo as set-up by u-boot */ -typedef struct { - uint32_t bi_memstart; - uint32_t bi_memsize; - uint32_t bi_flashstart; - uint32_t bi_flashsize; - uint32_t bi_flashoffset; /* 0x10 */ - uint32_t bi_sramstart; - uint32_t bi_sramsize; - uint32_t bi_bootflags; - uint32_t bi_ipaddr; /* 0x20 */ - uint8_t bi_enetaddr[6]; - uint16_t bi_ethspeed; - uint32_t bi_intfreq; - uint32_t bi_busfreq; /* 0x30 */ - uint32_t bi_baudrate; - uint8_t bi_s_version[4]; - uint8_t bi_r_version[32]; - uint32_t bi_procfreq; - uint32_t bi_plb_busfreq; - uint32_t bi_pci_busfreq; - uint8_t bi_pci_enetaddr[6]; - uint8_t bi_pci_enetaddr2[6]; /* PPC405EP specific */ - uint32_t bi_opbfreq; - uint32_t bi_iic_fast[2]; -} ppc4xx_bd_info_t; - -static void ppc405_set_default_bootinfo(ppc4xx_bd_info_t *bd, - ram_addr_t ram_size) -{ - memset(bd, 0, sizeof(*bd)); - - bd->bi_memstart = PPC405EP_SDRAM_BASE; - bd->bi_memsize = ram_size; - bd->bi_sramstart = PPC405EP_SRAM_BASE; - bd->bi_sramsize = PPC405EP_SRAM_SIZE; - bd->bi_bootflags = 0; - bd->bi_intfreq = 133333333; - bd->bi_busfreq = 33333333; - bd->bi_baudrate = 115200; - bd->bi_s_version[0] = 'Q'; - bd->bi_s_version[1] = 'M'; - bd->bi_s_version[2] = 'U'; - bd->bi_s_version[3] = '\0'; - bd->bi_r_version[0] = 'Q'; - bd->bi_r_version[1] = 'E'; - bd->bi_r_version[2] = 'M'; - bd->bi_r_version[3] = 'U'; - bd->bi_r_version[4] = '\0'; - bd->bi_procfreq = 133333333; - bd->bi_plb_busfreq = 33333333; - bd->bi_pci_busfreq = 33333333; - bd->bi_opbfreq = 33333333; -} - -static ram_addr_t __ppc405_set_bootinfo(CPUPPCState *env, ppc4xx_bd_info_t *bd) -{ - CPUState *cs = env_cpu(env); - ram_addr_t bdloc; - int i, n; - - /* We put the bd structure at the top of memory */ - if (bd->bi_memsize >= 0x01000000UL) { - bdloc = 0x01000000UL - sizeof(ppc4xx_bd_info_t); - } else { - bdloc = bd->bi_memsize - sizeof(ppc4xx_bd_info_t); - } - stl_be_phys(cs->as, bdloc + 0x00, bd->bi_memstart); - stl_be_phys(cs->as, bdloc + 0x04, bd->bi_memsize); - stl_be_phys(cs->as, bdloc + 0x08, bd->bi_flashstart); - stl_be_phys(cs->as, bdloc + 0x0C, bd->bi_flashsize); - stl_be_phys(cs->as, bdloc + 0x10, bd->bi_flashoffset); - stl_be_phys(cs->as, bdloc + 0x14, bd->bi_sramstart); - stl_be_phys(cs->as, bdloc + 0x18, bd->bi_sramsize); - stl_be_phys(cs->as, bdloc + 0x1C, bd->bi_bootflags); - stl_be_phys(cs->as, bdloc + 0x20, bd->bi_ipaddr); - for (i = 0; i < 6; i++) { - stb_phys(cs->as, bdloc + 0x24 + i, bd->bi_enetaddr[i]); - } - stw_be_phys(cs->as, bdloc + 0x2A, bd->bi_ethspeed); - stl_be_phys(cs->as, bdloc + 0x2C, bd->bi_intfreq); - stl_be_phys(cs->as, bdloc + 0x30, bd->bi_busfreq); - stl_be_phys(cs->as, bdloc + 0x34, bd->bi_baudrate); - for (i = 0; i < 4; i++) { - stb_phys(cs->as, bdloc + 0x38 + i, bd->bi_s_version[i]); - } - for (i = 0; i < 32; i++) { - stb_phys(cs->as, bdloc + 0x3C + i, bd->bi_r_version[i]); - } - stl_be_phys(cs->as, bdloc + 0x5C, bd->bi_procfreq); - stl_be_phys(cs->as, bdloc + 0x60, bd->bi_plb_busfreq); - stl_be_phys(cs->as, bdloc + 0x64, bd->bi_pci_busfreq); - for (i = 0; i < 6; i++) { - stb_phys(cs->as, bdloc + 0x68 + i, bd->bi_pci_enetaddr[i]); - } - n = 0x70; /* includes 2 bytes hole */ - for (i = 0; i < 6; i++) { - stb_phys(cs->as, bdloc + n++, bd->bi_pci_enetaddr2[i]); - } - stl_be_phys(cs->as, bdloc + n, bd->bi_opbfreq); - n += 4; - for (i = 0; i < 2; i++) { - stl_be_phys(cs->as, bdloc + n, bd->bi_iic_fast[i]); - n += 4; - } - - return bdloc; -} - -static ram_addr_t ppc405_set_bootinfo(CPUPPCState *env, ram_addr_t ram_size) -{ - ppc4xx_bd_info_t bd; - - memset(&bd, 0, sizeof(bd)); - - ppc405_set_default_bootinfo(&bd, ram_size); - - return __ppc405_set_bootinfo(env, &bd); -} - -static void boot_from_kernel(MachineState *machine, PowerPCCPU *cpu) -{ - CPUPPCState *env = &cpu->env; - hwaddr boot_entry; - hwaddr kernel_base; - int kernel_size; - hwaddr initrd_base; - int initrd_size; - ram_addr_t bdloc; - int len; - - bdloc = ppc405_set_bootinfo(env, machine->ram_size); - boot_info.bdloc = bdloc; - - kernel_size = load_elf(machine->kernel_filename, NULL, NULL, NULL, - &boot_entry, &kernel_base, NULL, NULL, - 1, PPC_ELF_MACHINE, 0, 0); - if (kernel_size < 0) { - error_report("Could not load kernel '%s' : %s", - machine->kernel_filename, load_elf_strerror(kernel_size)); - exit(1); - } - boot_info.entry = boot_entry; - - /* load initrd */ - if (machine->initrd_filename) { - initrd_base = INITRD_LOAD_ADDR; - initrd_size = load_image_targphys(machine->initrd_filename, initrd_base, - machine->ram_size - initrd_base); - if (initrd_size < 0) { - error_report("could not load initial ram disk '%s'", - machine->initrd_filename); - exit(1); - } - - boot_info.initrd_base = initrd_base; - boot_info.initrd_size = initrd_size; - } - - if (machine->kernel_cmdline) { - len = strlen(machine->kernel_cmdline); - bdloc -= ((len + 255) & ~255); - cpu_physical_memory_write(bdloc, machine->kernel_cmdline, len + 1); - boot_info.cmdline_base = bdloc; - boot_info.cmdline_size = bdloc + len; - } - - /* Install our custom reset handler to start from Linux */ - qemu_register_reset(main_cpu_reset, cpu); - env->load_info = &boot_info; -} - -static void ppc405_init(MachineState *machine) -{ - Ppc405MachineState *ppc405 = PPC405_MACHINE(machine); - const char *kernel_filename = machine->kernel_filename; - MemoryRegion *sysmem = get_system_memory(); - - object_initialize_child(OBJECT(machine), "soc", &ppc405->soc, - TYPE_PPC405_SOC); - object_property_set_link(OBJECT(&ppc405->soc), "dram", - OBJECT(machine->ram), &error_abort); - object_property_set_uint(OBJECT(&ppc405->soc), "sys-clk", 33333333, - &error_abort); - qdev_realize(DEVICE(&ppc405->soc), NULL, &error_fatal); - - /* allocate and load BIOS */ - if (machine->firmware) { - MemoryRegion *bios = g_new(MemoryRegion, 1); - g_autofree char *filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, - machine->firmware); - long bios_size; - - memory_region_init_rom(bios, NULL, "ef405ep.bios", BIOS_SIZE, - &error_fatal); - - if (!filename) { - error_report("Could not find firmware '%s'", machine->firmware); - exit(1); - } - - bios_size = load_image_size(filename, - memory_region_get_ram_ptr(bios), - BIOS_SIZE); - if (bios_size < 0) { - error_report("Could not load PowerPC BIOS '%s'", machine->firmware); - exit(1); - } - - bios_size = (bios_size + 0xfff) & ~0xfff; - memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios); - } - - /* Load kernel and initrd using U-Boot images */ - if (kernel_filename && machine->firmware) { - target_ulong kernel_base, initrd_base; - long kernel_size, initrd_size; - - kernel_base = KERNEL_LOAD_ADDR; - kernel_size = load_image_targphys(kernel_filename, kernel_base, - machine->ram_size - kernel_base); - if (kernel_size < 0) { - error_report("could not load kernel '%s'", kernel_filename); - exit(1); - } - - /* load initrd */ - if (machine->initrd_filename) { - initrd_base = INITRD_LOAD_ADDR; - initrd_size = load_image_targphys(machine->initrd_filename, - initrd_base, - machine->ram_size - initrd_base); - if (initrd_size < 0) { - error_report("could not load initial ram disk '%s'", - machine->initrd_filename); - exit(1); - } - } - - /* Load ELF kernel and rootfs.cpio */ - } else if (kernel_filename && !machine->firmware) { - ppc4xx_sdram_ddr_enable(&ppc405->soc.sdram); - boot_from_kernel(machine, &ppc405->soc.cpu); - } -} - -static void ppc405_machine_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "PPC405 generic machine"; - mc->init = ppc405_init; - mc->default_ram_size = 128 * MiB; - mc->default_ram_id = "ppc405.ram"; -} - -static const TypeInfo ppc405_machine_type = { - .name = TYPE_PPC405_MACHINE, - .parent = TYPE_MACHINE, - .instance_size = sizeof(Ppc405MachineState), - .class_init = ppc405_machine_class_init, - .abstract = true, -}; - -/*****************************************************************************/ -/* PPC405EP reference board (IBM) */ -/* - * Standalone board with: - * - PowerPC 405EP CPU - * - SDRAM (0x00000000) - * - Flash (0xFFF80000) - * - SRAM (0xFFF00000) - * - NVRAM (0xF0000000) - * - FPGA (0xF0300000) - */ - -#define PPC405EP_NVRAM_BASE 0xF0000000 -#define PPC405EP_FPGA_BASE 0xF0300000 -#define PPC405EP_FLASH_BASE 0xFFF80000 - -#define TYPE_REF405EP_FPGA "ref405ep-fpga" -OBJECT_DECLARE_SIMPLE_TYPE(Ref405epFpgaState, REF405EP_FPGA); -struct Ref405epFpgaState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - uint8_t reg0; - uint8_t reg1; -}; - -static uint64_t ref405ep_fpga_readb(void *opaque, hwaddr addr, unsigned size) -{ - Ref405epFpgaState *fpga = opaque; - uint32_t ret; - - switch (addr) { - case 0x0: - ret = fpga->reg0; - break; - case 0x1: - ret = fpga->reg1; - break; - default: - ret = 0; - break; - } - - return ret; -} - -static void ref405ep_fpga_writeb(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - Ref405epFpgaState *fpga = opaque; - - switch (addr) { - case 0x0: - /* Read only */ - break; - case 0x1: - fpga->reg1 = value; - break; - default: - break; - } -} - -static const MemoryRegionOps ref405ep_fpga_ops = { - .read = ref405ep_fpga_readb, - .write = ref405ep_fpga_writeb, - .impl.min_access_size = 1, - .impl.max_access_size = 1, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .endianness = DEVICE_BIG_ENDIAN, -}; - -static void ref405ep_fpga_reset(DeviceState *dev) -{ - Ref405epFpgaState *fpga = REF405EP_FPGA(dev); - - fpga->reg0 = 0x00; - fpga->reg1 = 0x0F; -} - -static void ref405ep_fpga_realize(DeviceState *dev, Error **errp) -{ - Ref405epFpgaState *s = REF405EP_FPGA(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &ref405ep_fpga_ops, s, - "fpga", 0x00000100); - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); -} - -static void ref405ep_fpga_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ref405ep_fpga_realize; - dc->reset = ref405ep_fpga_reset; - /* Reason: only works as part of a ppc405 board */ - dc->user_creatable = false; -} - -static const TypeInfo ref405ep_fpga_type = { - .name = TYPE_REF405EP_FPGA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Ref405epFpgaState), - .class_init = ref405ep_fpga_class_init, -}; - -static void ref405ep_init(MachineState *machine) -{ - DeviceState *dev; - SysBusDevice *s; - MemoryRegion *sram = g_new(MemoryRegion, 1); - - ppc405_init(machine); - - /* allocate SRAM */ - memory_region_init_ram(sram, NULL, "ref405ep.sram", PPC405EP_SRAM_SIZE, - &error_fatal); - memory_region_add_subregion(get_system_memory(), PPC405EP_SRAM_BASE, sram); - - /* Register FPGA */ - dev = qdev_new(TYPE_REF405EP_FPGA); - object_property_add_child(OBJECT(machine), "fpga", OBJECT(dev)); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, PPC405EP_FPGA_BASE); - - /* Register NVRAM */ - dev = qdev_new("sysbus-m48t08"); - qdev_prop_set_int32(dev, "base-year", 1968); - s = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(s, &error_fatal); - sysbus_mmio_map(s, 0, PPC405EP_NVRAM_BASE); -} - -static void ref405ep_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "ref405ep"; - mc->init = ref405ep_init; -} - -static const TypeInfo ref405ep_type = { - .name = MACHINE_TYPE_NAME("ref405ep"), - .parent = TYPE_PPC405_MACHINE, - .class_init = ref405ep_class_init, -}; - -static void ppc405_machine_init(void) -{ - type_register_static(&ppc405_machine_type); - type_register_static(&ref405ep_type); - type_register_static(&ref405ep_fpga_type); -} - -type_init(ppc405_machine_init) diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c deleted file mode 100644 index 0cc68178adf..00000000000 --- a/hw/ppc/ppc405_uc.c +++ /dev/null @@ -1,1217 +0,0 @@ -/* - * QEMU PowerPC 405 embedded processors emulation - * - * 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 "qemu/units.h" -#include "qapi/error.h" -#include "qemu/log.h" -#include "cpu.h" -#include "hw/ppc/ppc.h" -#include "hw/i2c/ppc4xx_i2c.h" -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "ppc405.h" -#include "hw/char/serial.h" -#include "qemu/timer.h" -#include "sysemu/reset.h" -#include "sysemu/sysemu.h" -#include "exec/address-spaces.h" -#include "hw/intc/ppc-uic.h" -#include "trace.h" - -/*****************************************************************************/ -/* Shared peripherals */ - -/*****************************************************************************/ -/* PLB to OPB bridge */ -enum { - POB0_BESR0 = 0x0A0, - POB0_BESR1 = 0x0A2, - POB0_BEAR = 0x0A4, -}; - -static uint32_t dcr_read_pob(void *opaque, int dcrn) -{ - Ppc405PobState *pob = opaque; - uint32_t ret; - - switch (dcrn) { - case POB0_BEAR: - ret = pob->bear; - break; - case POB0_BESR0: - ret = pob->besr0; - break; - case POB0_BESR1: - ret = pob->besr1; - break; - default: - /* Avoid gcc warning */ - ret = 0; - break; - } - - return ret; -} - -static void dcr_write_pob(void *opaque, int dcrn, uint32_t val) -{ - Ppc405PobState *pob = opaque; - - switch (dcrn) { - case POB0_BEAR: - /* Read only */ - break; - case POB0_BESR0: - /* Write-clear */ - pob->besr0 &= ~val; - break; - case POB0_BESR1: - /* Write-clear */ - pob->besr1 &= ~val; - break; - } -} - -static void ppc405_pob_reset(DeviceState *dev) -{ - Ppc405PobState *pob = PPC405_POB(dev); - - /* No error */ - pob->bear = 0x00000000; - pob->besr0 = 0x0000000; - pob->besr1 = 0x0000000; -} - -static void ppc405_pob_realize(DeviceState *dev, Error **errp) -{ - Ppc405PobState *pob = PPC405_POB(dev); - Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); - - ppc4xx_dcr_register(dcr, POB0_BEAR, pob, &dcr_read_pob, &dcr_write_pob); - ppc4xx_dcr_register(dcr, POB0_BESR0, pob, &dcr_read_pob, &dcr_write_pob); - ppc4xx_dcr_register(dcr, POB0_BESR1, pob, &dcr_read_pob, &dcr_write_pob); -} - -static void ppc405_pob_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_pob_realize; - dc->reset = ppc405_pob_reset; - /* Reason: only works as function of a ppc4xx SoC */ - dc->user_creatable = false; -} - -/*****************************************************************************/ -/* OPB arbitrer */ -static uint64_t opba_readb(void *opaque, hwaddr addr, unsigned size) -{ - Ppc405OpbaState *opba = opaque; - uint32_t ret; - - switch (addr) { - case 0x00: - ret = opba->cr; - break; - case 0x01: - ret = opba->pr; - break; - default: - ret = 0x00; - break; - } - - trace_opba_readb(addr, ret); - return ret; -} - -static void opba_writeb(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - Ppc405OpbaState *opba = opaque; - - trace_opba_writeb(addr, value); - - switch (addr) { - case 0x00: - opba->cr = value & 0xF8; - break; - case 0x01: - opba->pr = value & 0xFF; - break; - default: - break; - } -} -static const MemoryRegionOps opba_ops = { - .read = opba_readb, - .write = opba_writeb, - .impl.min_access_size = 1, - .impl.max_access_size = 1, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .endianness = DEVICE_BIG_ENDIAN, -}; - -static void ppc405_opba_reset(DeviceState *dev) -{ - Ppc405OpbaState *opba = PPC405_OPBA(dev); - - opba->cr = 0x00; /* No dynamic priorities - park disabled */ - opba->pr = 0x11; -} - -static void ppc405_opba_realize(DeviceState *dev, Error **errp) -{ - Ppc405OpbaState *s = PPC405_OPBA(dev); - - memory_region_init_io(&s->io, OBJECT(s), &opba_ops, s, "opba", 2); - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->io); -} - -static void ppc405_opba_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_opba_realize; - dc->reset = ppc405_opba_reset; - /* Reason: only works as function of a ppc4xx SoC */ - dc->user_creatable = false; -} - -/*****************************************************************************/ -/* Code decompression controller */ -/* XXX: TODO */ - -/*****************************************************************************/ -/* DMA controller */ -enum { - DMA0_CR0 = 0x100, - DMA0_CT0 = 0x101, - DMA0_DA0 = 0x102, - DMA0_SA0 = 0x103, - DMA0_SG0 = 0x104, - DMA0_CR1 = 0x108, - DMA0_CT1 = 0x109, - DMA0_DA1 = 0x10A, - DMA0_SA1 = 0x10B, - DMA0_SG1 = 0x10C, - DMA0_CR2 = 0x110, - DMA0_CT2 = 0x111, - DMA0_DA2 = 0x112, - DMA0_SA2 = 0x113, - DMA0_SG2 = 0x114, - DMA0_CR3 = 0x118, - DMA0_CT3 = 0x119, - DMA0_DA3 = 0x11A, - DMA0_SA3 = 0x11B, - DMA0_SG3 = 0x11C, - DMA0_SR = 0x120, - DMA0_SGC = 0x123, - DMA0_SLP = 0x125, - DMA0_POL = 0x126, -}; - -static uint32_t dcr_read_dma(void *opaque, int dcrn) -{ - return 0; -} - -static void dcr_write_dma(void *opaque, int dcrn, uint32_t val) -{ -} - -static void ppc405_dma_reset(DeviceState *dev) -{ - Ppc405DmaState *dma = PPC405_DMA(dev); - int i; - - for (i = 0; i < 4; i++) { - dma->cr[i] = 0x00000000; - dma->ct[i] = 0x00000000; - dma->da[i] = 0x00000000; - dma->sa[i] = 0x00000000; - dma->sg[i] = 0x00000000; - } - dma->sr = 0x00000000; - dma->sgc = 0x00000000; - dma->slp = 0x7C000000; - dma->pol = 0x00000000; -} - -static void ppc405_dma_realize(DeviceState *dev, Error **errp) -{ - Ppc405DmaState *dma = PPC405_DMA(dev); - Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); - int i; - - for (i = 0; i < ARRAY_SIZE(dma->irqs); i++) { - sysbus_init_irq(SYS_BUS_DEVICE(dma), &dma->irqs[i]); - } - - ppc4xx_dcr_register(dcr, DMA0_CR0, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_CT0, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_DA0, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SA0, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SG0, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_CR1, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_CT1, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_DA1, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SA1, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SG1, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_CR2, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_CT2, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_DA2, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SA2, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SG2, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_CR3, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_CT3, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_DA3, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SA3, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SG3, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SR, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SGC, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_SLP, dma, &dcr_read_dma, &dcr_write_dma); - ppc4xx_dcr_register(dcr, DMA0_POL, dma, &dcr_read_dma, &dcr_write_dma); -} - -static void ppc405_dma_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_dma_realize; - dc->reset = ppc405_dma_reset; - /* Reason: only works as function of a ppc4xx SoC */ - dc->user_creatable = false; -} - -/*****************************************************************************/ -/* GPIO */ -static uint64_t ppc405_gpio_read(void *opaque, hwaddr addr, unsigned size) -{ - trace_ppc405_gpio_read(addr, size); - return 0; -} - -static void ppc405_gpio_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - trace_ppc405_gpio_write(addr, size, value); -} - -static const MemoryRegionOps ppc405_gpio_ops = { - .read = ppc405_gpio_read, - .write = ppc405_gpio_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void ppc405_gpio_realize(DeviceState *dev, Error **errp) -{ - Ppc405GpioState *s = PPC405_GPIO(dev); - - memory_region_init_io(&s->io, OBJECT(s), &ppc405_gpio_ops, s, "gpio", - 0x38); - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->io); -} - -static void ppc405_gpio_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_gpio_realize; - /* Reason: only works as function of a ppc4xx SoC */ - dc->user_creatable = false; -} - -/*****************************************************************************/ -/* On Chip Memory */ -enum { - OCM0_ISARC = 0x018, - OCM0_ISACNTL = 0x019, - OCM0_DSARC = 0x01A, - OCM0_DSACNTL = 0x01B, -}; - -static void ocm_update_mappings(Ppc405OcmState *ocm, - uint32_t isarc, uint32_t isacntl, - uint32_t dsarc, uint32_t dsacntl) -{ - trace_ocm_update_mappings(isarc, isacntl, dsarc, dsacntl, ocm->isarc, - ocm->isacntl, ocm->dsarc, ocm->dsacntl); - - if (ocm->isarc != isarc || - (ocm->isacntl & 0x80000000) != (isacntl & 0x80000000)) { - if (ocm->isacntl & 0x80000000) { - /* Unmap previously assigned memory region */ - trace_ocm_unmap("ISA", ocm->isarc); - memory_region_del_subregion(get_system_memory(), &ocm->isarc_ram); - } - if (isacntl & 0x80000000) { - /* Map new instruction memory region */ - trace_ocm_map("ISA", isarc); - memory_region_add_subregion(get_system_memory(), isarc, - &ocm->isarc_ram); - } - } - if (ocm->dsarc != dsarc || - (ocm->dsacntl & 0x80000000) != (dsacntl & 0x80000000)) { - if (ocm->dsacntl & 0x80000000) { - /* Beware not to unmap the region we just mapped */ - if (!(isacntl & 0x80000000) || ocm->dsarc != isarc) { - /* Unmap previously assigned memory region */ - trace_ocm_unmap("DSA", ocm->dsarc); - memory_region_del_subregion(get_system_memory(), - &ocm->dsarc_ram); - } - } - if (dsacntl & 0x80000000) { - /* Beware not to remap the region we just mapped */ - if (!(isacntl & 0x80000000) || dsarc != isarc) { - /* Map new data memory region */ - trace_ocm_map("DSA", dsarc); - memory_region_add_subregion(get_system_memory(), dsarc, - &ocm->dsarc_ram); - } - } - } -} - -static uint32_t dcr_read_ocm(void *opaque, int dcrn) -{ - Ppc405OcmState *ocm = opaque; - uint32_t ret; - - switch (dcrn) { - case OCM0_ISARC: - ret = ocm->isarc; - break; - case OCM0_ISACNTL: - ret = ocm->isacntl; - break; - case OCM0_DSARC: - ret = ocm->dsarc; - break; - case OCM0_DSACNTL: - ret = ocm->dsacntl; - break; - default: - ret = 0; - break; - } - - return ret; -} - -static void dcr_write_ocm(void *opaque, int dcrn, uint32_t val) -{ - Ppc405OcmState *ocm = opaque; - uint32_t isarc, dsarc, isacntl, dsacntl; - - isarc = ocm->isarc; - dsarc = ocm->dsarc; - isacntl = ocm->isacntl; - dsacntl = ocm->dsacntl; - switch (dcrn) { - case OCM0_ISARC: - isarc = val & 0xFC000000; - break; - case OCM0_ISACNTL: - isacntl = val & 0xC0000000; - break; - case OCM0_DSARC: - isarc = val & 0xFC000000; - break; - case OCM0_DSACNTL: - isacntl = val & 0xC0000000; - break; - } - ocm_update_mappings(ocm, isarc, isacntl, dsarc, dsacntl); - ocm->isarc = isarc; - ocm->dsarc = dsarc; - ocm->isacntl = isacntl; - ocm->dsacntl = dsacntl; -} - -static void ppc405_ocm_reset(DeviceState *dev) -{ - Ppc405OcmState *ocm = PPC405_OCM(dev); - uint32_t isarc, dsarc, isacntl, dsacntl; - - isarc = 0x00000000; - isacntl = 0x00000000; - dsarc = 0x00000000; - dsacntl = 0x00000000; - ocm_update_mappings(ocm, isarc, isacntl, dsarc, dsacntl); - ocm->isarc = isarc; - ocm->dsarc = dsarc; - ocm->isacntl = isacntl; - ocm->dsacntl = dsacntl; -} - -static void ppc405_ocm_realize(DeviceState *dev, Error **errp) -{ - Ppc405OcmState *ocm = PPC405_OCM(dev); - Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); - - /* XXX: Size is 4096 or 0x04000000 */ - memory_region_init_ram(&ocm->isarc_ram, OBJECT(ocm), "ppc405.ocm", 4 * KiB, - &error_fatal); - memory_region_init_alias(&ocm->dsarc_ram, OBJECT(ocm), "ppc405.dsarc", - &ocm->isarc_ram, 0, 4 * KiB); - - ppc4xx_dcr_register(dcr, OCM0_ISARC, ocm, &dcr_read_ocm, &dcr_write_ocm); - ppc4xx_dcr_register(dcr, OCM0_ISACNTL, ocm, &dcr_read_ocm, &dcr_write_ocm); - ppc4xx_dcr_register(dcr, OCM0_DSARC, ocm, &dcr_read_ocm, &dcr_write_ocm); - ppc4xx_dcr_register(dcr, OCM0_DSACNTL, ocm, &dcr_read_ocm, &dcr_write_ocm); -} - -static void ppc405_ocm_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_ocm_realize; - dc->reset = ppc405_ocm_reset; - /* Reason: only works as function of a ppc4xx SoC */ - dc->user_creatable = false; -} - -/*****************************************************************************/ -/* General purpose timers */ -static int ppc4xx_gpt_compare(Ppc405GptState *gpt, int n) -{ - /* XXX: TODO */ - return 0; -} - -static void ppc4xx_gpt_set_output(Ppc405GptState *gpt, int n, int level) -{ - /* XXX: TODO */ -} - -static void ppc4xx_gpt_set_outputs(Ppc405GptState *gpt) -{ - uint32_t mask; - int i; - - mask = 0x80000000; - for (i = 0; i < 5; i++) { - if (gpt->oe & mask) { - /* Output is enabled */ - if (ppc4xx_gpt_compare(gpt, i)) { - /* Comparison is OK */ - ppc4xx_gpt_set_output(gpt, i, gpt->ol & mask); - } else { - /* Comparison is KO */ - ppc4xx_gpt_set_output(gpt, i, gpt->ol & mask ? 0 : 1); - } - } - mask = mask >> 1; - } -} - -static void ppc4xx_gpt_set_irqs(Ppc405GptState *gpt) -{ - uint32_t mask; - int i; - - mask = 0x00008000; - for (i = 0; i < 5; i++) { - if (gpt->is & gpt->im & mask) { - qemu_irq_raise(gpt->irqs[i]); - } else { - qemu_irq_lower(gpt->irqs[i]); - } - mask = mask >> 1; - } -} - -static void ppc4xx_gpt_compute_timer(Ppc405GptState *gpt) -{ - /* XXX: TODO */ -} - -static uint64_t ppc4xx_gpt_read(void *opaque, hwaddr addr, unsigned size) -{ - Ppc405GptState *gpt = opaque; - uint32_t ret; - int idx; - - trace_ppc4xx_gpt_read(addr, size); - - switch (addr) { - case 0x00: - /* Time base counter */ - ret = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + gpt->tb_offset, - gpt->tb_freq, NANOSECONDS_PER_SECOND); - break; - case 0x10: - /* Output enable */ - ret = gpt->oe; - break; - case 0x14: - /* Output level */ - ret = gpt->ol; - break; - case 0x18: - /* Interrupt mask */ - ret = gpt->im; - break; - case 0x1C: - case 0x20: - /* Interrupt status */ - ret = gpt->is; - break; - case 0x24: - /* Interrupt enable */ - ret = gpt->ie; - break; - case 0x80 ... 0x90: - /* Compare timer */ - idx = (addr - 0x80) >> 2; - ret = gpt->comp[idx]; - break; - case 0xC0 ... 0xD0: - /* Compare mask */ - idx = (addr - 0xC0) >> 2; - ret = gpt->mask[idx]; - break; - default: - ret = -1; - break; - } - - return ret; -} - -static void ppc4xx_gpt_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - Ppc405GptState *gpt = opaque; - int idx; - - trace_ppc4xx_gpt_write(addr, size, value); - - switch (addr) { - case 0x00: - /* Time base counter */ - gpt->tb_offset = muldiv64(value, NANOSECONDS_PER_SECOND, gpt->tb_freq) - - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ppc4xx_gpt_compute_timer(gpt); - break; - case 0x10: - /* Output enable */ - gpt->oe = value & 0xF8000000; - ppc4xx_gpt_set_outputs(gpt); - break; - case 0x14: - /* Output level */ - gpt->ol = value & 0xF8000000; - ppc4xx_gpt_set_outputs(gpt); - break; - case 0x18: - /* Interrupt mask */ - gpt->im = value & 0x0000F800; - break; - case 0x1C: - /* Interrupt status set */ - gpt->is |= value & 0x0000F800; - ppc4xx_gpt_set_irqs(gpt); - break; - case 0x20: - /* Interrupt status clear */ - gpt->is &= ~(value & 0x0000F800); - ppc4xx_gpt_set_irqs(gpt); - break; - case 0x24: - /* Interrupt enable */ - gpt->ie = value & 0x0000F800; - ppc4xx_gpt_set_irqs(gpt); - break; - case 0x80 ... 0x90: - /* Compare timer */ - idx = (addr - 0x80) >> 2; - gpt->comp[idx] = value & 0xF8000000; - ppc4xx_gpt_compute_timer(gpt); - break; - case 0xC0 ... 0xD0: - /* Compare mask */ - idx = (addr - 0xC0) >> 2; - gpt->mask[idx] = value & 0xF8000000; - ppc4xx_gpt_compute_timer(gpt); - break; - } -} - -static const MemoryRegionOps gpt_ops = { - .read = ppc4xx_gpt_read, - .write = ppc4xx_gpt_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void ppc4xx_gpt_cb(void *opaque) -{ - Ppc405GptState *gpt = opaque; - - ppc4xx_gpt_set_irqs(gpt); - ppc4xx_gpt_set_outputs(gpt); - ppc4xx_gpt_compute_timer(gpt); -} - -static void ppc405_gpt_reset(DeviceState *dev) -{ - Ppc405GptState *gpt = PPC405_GPT(dev); - int i; - - timer_del(gpt->timer); - gpt->oe = 0x00000000; - gpt->ol = 0x00000000; - gpt->im = 0x00000000; - gpt->is = 0x00000000; - gpt->ie = 0x00000000; - for (i = 0; i < 5; i++) { - gpt->comp[i] = 0x00000000; - gpt->mask[i] = 0x00000000; - } -} - -static void ppc405_gpt_realize(DeviceState *dev, Error **errp) -{ - Ppc405GptState *s = PPC405_GPT(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - int i; - - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &ppc4xx_gpt_cb, s); - memory_region_init_io(&s->iomem, OBJECT(s), &gpt_ops, s, "gpt", 0xd4); - sysbus_init_mmio(sbd, &s->iomem); - - for (i = 0; i < ARRAY_SIZE(s->irqs); i++) { - sysbus_init_irq(sbd, &s->irqs[i]); - } -} - -static void ppc405_gpt_finalize(Object *obj) -{ - /* timer will be NULL if the GPT wasn't realized */ - if (PPC405_GPT(obj)->timer) { - timer_del(PPC405_GPT(obj)->timer); - } -} - -static void ppc405_gpt_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_gpt_realize; - dc->reset = ppc405_gpt_reset; - /* Reason: only works as function of a ppc4xx SoC */ - dc->user_creatable = false; -} - -/*****************************************************************************/ -/* PowerPC 405EP */ -/* CPU control */ -enum { - PPC405EP_CPC0_PLLMR0 = 0x0F0, - PPC405EP_CPC0_BOOT = 0x0F1, - PPC405EP_CPC0_EPCTL = 0x0F3, - PPC405EP_CPC0_PLLMR1 = 0x0F4, - PPC405EP_CPC0_UCR = 0x0F5, - PPC405EP_CPC0_SRR = 0x0F6, - PPC405EP_CPC0_JTAGID = 0x0F7, - PPC405EP_CPC0_PCI = 0x0F9, -#if 0 - PPC405EP_CPC0_ER = xxx, - PPC405EP_CPC0_FR = xxx, - PPC405EP_CPC0_SR = xxx, -#endif -}; - -static void ppc405ep_compute_clocks(Ppc405CpcState *cpc) -{ - uint32_t CPU_clk, PLB_clk, OPB_clk, EBC_clk, MAL_clk, PCI_clk; - uint32_t UART0_clk, UART1_clk; - uint64_t VCO_out, PLL_out; - int M, D; - - VCO_out = 0; - if ((cpc->pllmr[1] & 0x80000000) && !(cpc->pllmr[1] & 0x40000000)) { - M = (((cpc->pllmr[1] >> 20) - 1) & 0xF) + 1; /* FBMUL */ - trace_ppc405ep_clocks_compute("FBMUL", (cpc->pllmr[1] >> 20) & 0xF, M); - D = 8 - ((cpc->pllmr[1] >> 16) & 0x7); /* FWDA */ - trace_ppc405ep_clocks_compute("FWDA", (cpc->pllmr[1] >> 16) & 0x7, D); - VCO_out = (uint64_t)cpc->sysclk * M * D; - if (VCO_out < 500000000UL || VCO_out > 1000000000UL) { - /* Error - unlock the PLL */ - qemu_log_mask(LOG_GUEST_ERROR, "VCO out of range %" PRIu64 "\n", - VCO_out); -#if 0 - cpc->pllmr[1] &= ~0x80000000; - goto pll_bypass; -#endif - } - PLL_out = VCO_out / D; - /* Pretend the PLL is locked */ - cpc->boot |= 0x00000001; - } else { -#if 0 - pll_bypass: -#endif - PLL_out = cpc->sysclk; - if (cpc->pllmr[1] & 0x40000000) { - /* Pretend the PLL is not locked */ - cpc->boot &= ~0x00000001; - } - } - /* Now, compute all other clocks */ - D = ((cpc->pllmr[0] >> 20) & 0x3) + 1; /* CCDV */ - trace_ppc405ep_clocks_compute("CCDV", (cpc->pllmr[0] >> 20) & 0x3, D); - CPU_clk = PLL_out / D; - D = ((cpc->pllmr[0] >> 16) & 0x3) + 1; /* CBDV */ - trace_ppc405ep_clocks_compute("CBDV", (cpc->pllmr[0] >> 16) & 0x3, D); - PLB_clk = CPU_clk / D; - D = ((cpc->pllmr[0] >> 12) & 0x3) + 1; /* OPDV */ - trace_ppc405ep_clocks_compute("OPDV", (cpc->pllmr[0] >> 12) & 0x3, D); - OPB_clk = PLB_clk / D; - D = ((cpc->pllmr[0] >> 8) & 0x3) + 2; /* EPDV */ - trace_ppc405ep_clocks_compute("EPDV", (cpc->pllmr[0] >> 8) & 0x3, D); - EBC_clk = PLB_clk / D; - D = ((cpc->pllmr[0] >> 4) & 0x3) + 1; /* MPDV */ - trace_ppc405ep_clocks_compute("MPDV", (cpc->pllmr[0] >> 4) & 0x3, D); - MAL_clk = PLB_clk / D; - D = (cpc->pllmr[0] & 0x3) + 1; /* PPDV */ - trace_ppc405ep_clocks_compute("PPDV", cpc->pllmr[0] & 0x3, D); - PCI_clk = PLB_clk / D; - D = ((cpc->ucr - 1) & 0x7F) + 1; /* U0DIV */ - trace_ppc405ep_clocks_compute("U0DIV", cpc->ucr & 0x7F, D); - UART0_clk = PLL_out / D; - D = (((cpc->ucr >> 8) - 1) & 0x7F) + 1; /* U1DIV */ - trace_ppc405ep_clocks_compute("U1DIV", (cpc->ucr >> 8) & 0x7F, D); - UART1_clk = PLL_out / D; - - if (trace_event_get_state_backends(TRACE_PPC405EP_CLOCKS_SETUP)) { - g_autofree char *trace = g_strdup_printf( - "Setup PPC405EP clocks - sysclk %" PRIu32 " VCO %" PRIu64 - " PLL out %" PRIu64 " Hz\n" - "CPU %" PRIu32 " PLB %" PRIu32 " OPB %" PRIu32 " EBC %" PRIu32 - " MAL %" PRIu32 " PCI %" PRIu32 " UART0 %" PRIu32 - " UART1 %" PRIu32 "\n", - cpc->sysclk, VCO_out, PLL_out, - CPU_clk, PLB_clk, OPB_clk, EBC_clk, MAL_clk, PCI_clk, - UART0_clk, UART1_clk); - trace_ppc405ep_clocks_setup(trace); - } - - /* Setup CPU clocks */ - clk_setup(&cpc->clk_setup[PPC405EP_CPU_CLK], CPU_clk); - /* Setup PLB clock */ - clk_setup(&cpc->clk_setup[PPC405EP_PLB_CLK], PLB_clk); - /* Setup OPB clock */ - clk_setup(&cpc->clk_setup[PPC405EP_OPB_CLK], OPB_clk); - /* Setup external clock */ - clk_setup(&cpc->clk_setup[PPC405EP_EBC_CLK], EBC_clk); - /* Setup MAL clock */ - clk_setup(&cpc->clk_setup[PPC405EP_MAL_CLK], MAL_clk); - /* Setup PCI clock */ - clk_setup(&cpc->clk_setup[PPC405EP_PCI_CLK], PCI_clk); - /* Setup UART0 clock */ - clk_setup(&cpc->clk_setup[PPC405EP_UART0_CLK], UART0_clk); - /* Setup UART1 clock */ - clk_setup(&cpc->clk_setup[PPC405EP_UART1_CLK], UART1_clk); -} - -static uint32_t dcr_read_epcpc(void *opaque, int dcrn) -{ - Ppc405CpcState *cpc = opaque; - uint32_t ret; - - switch (dcrn) { - case PPC405EP_CPC0_BOOT: - ret = cpc->boot; - break; - case PPC405EP_CPC0_EPCTL: - ret = cpc->epctl; - break; - case PPC405EP_CPC0_PLLMR0: - ret = cpc->pllmr[0]; - break; - case PPC405EP_CPC0_PLLMR1: - ret = cpc->pllmr[1]; - break; - case PPC405EP_CPC0_UCR: - ret = cpc->ucr; - break; - case PPC405EP_CPC0_SRR: - ret = cpc->srr; - break; - case PPC405EP_CPC0_JTAGID: - ret = cpc->jtagid; - break; - case PPC405EP_CPC0_PCI: - ret = cpc->pci; - break; - default: - /* Avoid gcc warning */ - ret = 0; - break; - } - - return ret; -} - -static void dcr_write_epcpc(void *opaque, int dcrn, uint32_t val) -{ - Ppc405CpcState *cpc = opaque; - - switch (dcrn) { - case PPC405EP_CPC0_BOOT: - /* Read-only register */ - break; - case PPC405EP_CPC0_EPCTL: - /* Don't care for now */ - cpc->epctl = val & 0xC00000F3; - break; - case PPC405EP_CPC0_PLLMR0: - cpc->pllmr[0] = val & 0x00633333; - ppc405ep_compute_clocks(cpc); - break; - case PPC405EP_CPC0_PLLMR1: - cpc->pllmr[1] = val & 0xC0F73FFF; - ppc405ep_compute_clocks(cpc); - break; - case PPC405EP_CPC0_UCR: - /* UART control - don't care for now */ - cpc->ucr = val & 0x003F7F7F; - break; - case PPC405EP_CPC0_SRR: - cpc->srr = val; - break; - case PPC405EP_CPC0_JTAGID: - /* Read-only */ - break; - case PPC405EP_CPC0_PCI: - cpc->pci = val; - break; - } -} - -static void ppc405_cpc_reset(DeviceState *dev) -{ - Ppc405CpcState *cpc = PPC405_CPC(dev); - - cpc->boot = 0x00000010; /* Boot from PCI - IIC EEPROM disabled */ - cpc->epctl = 0x00000000; - cpc->pllmr[0] = 0x00021002; - cpc->pllmr[1] = 0x80a552be; - cpc->ucr = 0x00004646; - cpc->srr = 0x00040000; - cpc->pci = 0x00000000; - cpc->er = 0x00000000; - cpc->fr = 0x00000000; - cpc->sr = 0x00000000; - cpc->jtagid = 0x20267049; - ppc405ep_compute_clocks(cpc); -} - -/* XXX: sysclk should be between 25 and 100 MHz */ -static void ppc405_cpc_realize(DeviceState *dev, Error **errp) -{ - Ppc405CpcState *cpc = PPC405_CPC(dev); - Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); - - assert(dcr->cpu); - cpc->clk_setup[PPC405EP_CPU_CLK].cb = - ppc_40x_timers_init(&dcr->cpu->env, cpc->sysclk, PPC_INTERRUPT_PIT); - cpc->clk_setup[PPC405EP_CPU_CLK].opaque = &dcr->cpu->env; - - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_BOOT, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_EPCTL, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_PLLMR0, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_PLLMR1, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_UCR, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_SRR, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_JTAGID, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc4xx_dcr_register(dcr, PPC405EP_CPC0_PCI, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); -} - -static Property ppc405_cpc_properties[] = { - DEFINE_PROP_UINT32("sys-clk", Ppc405CpcState, sysclk, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ppc405_cpc_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_cpc_realize; - dc->reset = ppc405_cpc_reset; - /* Reason: only works as function of a ppc4xx SoC */ - dc->user_creatable = false; - device_class_set_props(dc, ppc405_cpc_properties); -} - -/* PPC405_SOC */ - -static void ppc405_soc_instance_init(Object *obj) -{ - Ppc405SoCState *s = PPC405_SOC(obj); - - object_initialize_child(obj, "cpu", &s->cpu, - POWERPC_CPU_TYPE_NAME("405ep")); - - object_initialize_child(obj, "uic", &s->uic, TYPE_PPC_UIC); - - object_initialize_child(obj, "cpc", &s->cpc, TYPE_PPC405_CPC); - object_property_add_alias(obj, "sys-clk", OBJECT(&s->cpc), "sys-clk"); - - object_initialize_child(obj, "gpt", &s->gpt, TYPE_PPC405_GPT); - - object_initialize_child(obj, "ocm", &s->ocm, TYPE_PPC405_OCM); - - object_initialize_child(obj, "gpio", &s->gpio, TYPE_PPC405_GPIO); - - object_initialize_child(obj, "dma", &s->dma, TYPE_PPC405_DMA); - - object_initialize_child(obj, "i2c", &s->i2c, TYPE_PPC4xx_I2C); - - object_initialize_child(obj, "ebc", &s->ebc, TYPE_PPC4xx_EBC); - - object_initialize_child(obj, "opba", &s->opba, TYPE_PPC405_OPBA); - - object_initialize_child(obj, "pob", &s->pob, TYPE_PPC405_POB); - - object_initialize_child(obj, "plb", &s->plb, TYPE_PPC4xx_PLB); - - object_initialize_child(obj, "mal", &s->mal, TYPE_PPC4xx_MAL); - - object_initialize_child(obj, "sdram", &s->sdram, TYPE_PPC4xx_SDRAM_DDR); - object_property_add_alias(obj, "dram", OBJECT(&s->sdram), "dram"); -} - -static void ppc405_reset(void *opaque) -{ - cpu_reset(CPU(opaque)); -} - -static void ppc405_soc_realize(DeviceState *dev, Error **errp) -{ - Ppc405SoCState *s = PPC405_SOC(dev); - CPUPPCState *env; - SysBusDevice *sbd; - int i; - - /* init CPUs */ - if (!qdev_realize(DEVICE(&s->cpu), NULL, errp)) { - return; - } - qemu_register_reset(ppc405_reset, &s->cpu); - - env = &s->cpu.env; - - ppc_dcr_init(env, NULL, NULL); - - /* CPU control */ - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->cpc), &s->cpu, errp)) { - return; - } - - /* PLB arbitrer */ - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->plb), &s->cpu, errp)) { - return; - } - - /* PLB to OPB bridge */ - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->pob), &s->cpu, errp)) { - return; - } - - /* OBP arbitrer */ - sbd = SYS_BUS_DEVICE(&s->opba); - if (!sysbus_realize(sbd, errp)) { - return; - } - sysbus_mmio_map(sbd, 0, 0xef600600); - - /* Universal interrupt controller */ - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->uic), &s->cpu, errp)) { - return; - } - sbd = SYS_BUS_DEVICE(&s->uic); - sysbus_connect_irq(sbd, PPCUIC_OUTPUT_INT, - qdev_get_gpio_in(DEVICE(&s->cpu), PPC40x_INPUT_INT)); - sysbus_connect_irq(sbd, PPCUIC_OUTPUT_CINT, - qdev_get_gpio_in(DEVICE(&s->cpu), PPC40x_INPUT_CINT)); - - /* SDRAM controller */ - /* - * We use the 440 DDR SDRAM controller which has more regs and features - * but it's compatible enough for now - */ - object_property_set_int(OBJECT(&s->sdram), "nbanks", 2, &error_abort); - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->sdram), &s->cpu, errp)) { - return; - } - /* XXX 405EP has no ECC interrupt */ - sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdram), 0, - qdev_get_gpio_in(DEVICE(&s->uic), 17)); - - /* External bus controller */ - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->ebc), &s->cpu, errp)) { - return; - } - - /* DMA controller */ - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->dma), &s->cpu, errp)) { - return; - } - sbd = SYS_BUS_DEVICE(&s->dma); - for (i = 0; i < ARRAY_SIZE(s->dma.irqs); i++) { - sysbus_connect_irq(sbd, i, qdev_get_gpio_in(DEVICE(&s->uic), 5 + i)); - } - - /* I2C controller */ - sbd = SYS_BUS_DEVICE(&s->i2c); - if (!sysbus_realize(sbd, errp)) { - return; - } - sysbus_mmio_map(sbd, 0, 0xef600500); - sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(DEVICE(&s->uic), 2)); - - /* GPIO */ - sbd = SYS_BUS_DEVICE(&s->gpio); - if (!sysbus_realize(sbd, errp)) { - return; - } - sysbus_mmio_map(sbd, 0, 0xef600700); - - /* Serial ports */ - if (serial_hd(0) != NULL) { - serial_mm_init(get_system_memory(), 0xef600300, 0, - qdev_get_gpio_in(DEVICE(&s->uic), 0), - PPC_SERIAL_MM_BAUDBASE, serial_hd(0), - DEVICE_BIG_ENDIAN); - } - if (serial_hd(1) != NULL) { - serial_mm_init(get_system_memory(), 0xef600400, 0, - qdev_get_gpio_in(DEVICE(&s->uic), 1), - PPC_SERIAL_MM_BAUDBASE, serial_hd(1), - DEVICE_BIG_ENDIAN); - } - - /* OCM */ - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->ocm), &s->cpu, errp)) { - return; - } - - /* GPT */ - sbd = SYS_BUS_DEVICE(&s->gpt); - if (!sysbus_realize(sbd, errp)) { - return; - } - sysbus_mmio_map(sbd, 0, 0xef600000); - for (i = 0; i < ARRAY_SIZE(s->gpt.irqs); i++) { - sysbus_connect_irq(sbd, i, qdev_get_gpio_in(DEVICE(&s->uic), 19 + i)); - } - - /* MAL */ - object_property_set_int(OBJECT(&s->mal), "txc-num", 4, &error_abort); - object_property_set_int(OBJECT(&s->mal), "rxc-num", 2, &error_abort); - if (!ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(&s->mal), &s->cpu, errp)) { - return; - } - sbd = SYS_BUS_DEVICE(&s->mal); - for (i = 0; i < ARRAY_SIZE(s->mal.irqs); i++) { - sysbus_connect_irq(sbd, i, qdev_get_gpio_in(DEVICE(&s->uic), 11 + i)); - } - - /* Ethernet */ - /* Uses UIC IRQs 9, 15, 17 */ -} - -static void ppc405_soc_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = ppc405_soc_realize; - /* Reason: only works as part of a ppc405 board/machine */ - dc->user_creatable = false; -} - -static const TypeInfo ppc405_types[] = { - { - .name = TYPE_PPC405_POB, - .parent = TYPE_PPC4xx_DCR_DEVICE, - .instance_size = sizeof(Ppc405PobState), - .class_init = ppc405_pob_class_init, - }, { - .name = TYPE_PPC405_OPBA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Ppc405OpbaState), - .class_init = ppc405_opba_class_init, - }, { - .name = TYPE_PPC405_DMA, - .parent = TYPE_PPC4xx_DCR_DEVICE, - .instance_size = sizeof(Ppc405DmaState), - .class_init = ppc405_dma_class_init, - }, { - .name = TYPE_PPC405_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Ppc405GpioState), - .class_init = ppc405_gpio_class_init, - }, { - .name = TYPE_PPC405_OCM, - .parent = TYPE_PPC4xx_DCR_DEVICE, - .instance_size = sizeof(Ppc405OcmState), - .class_init = ppc405_ocm_class_init, - }, { - .name = TYPE_PPC405_GPT, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Ppc405GptState), - .instance_finalize = ppc405_gpt_finalize, - .class_init = ppc405_gpt_class_init, - }, { - .name = TYPE_PPC405_CPC, - .parent = TYPE_PPC4xx_DCR_DEVICE, - .instance_size = sizeof(Ppc405CpcState), - .class_init = ppc405_cpc_class_init, - }, { - .name = TYPE_PPC405_SOC, - .parent = TYPE_DEVICE, - .instance_size = sizeof(Ppc405SoCState), - .instance_init = ppc405_soc_instance_init, - .class_init = ppc405_soc_class_init, - } -}; - -DEFINE_TYPES(ppc405_types) diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 45f409c8386..6fff0d8afbc 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -15,18 +15,19 @@ #include "qemu/units.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 "sysemu/device_tree.h" +#include "system/kvm.h" +#include "system/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 "sysemu/sysemu.h" -#include "sysemu/reset.h" +#include "hw/pci-host/ppc4xx.h" +#include "system/system.h" +#include "system/reset.h" #include "hw/sysbus.h" #include "hw/intc/ppc-uic.h" #include "hw/qdev-properties.h" @@ -63,7 +64,7 @@ static int bamboo_load_device_tree(MachineState *machine, uint32_t tb_freq = 400000000; uint32_t clock_freq = 400000000; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); + filename = qemu_find_file(QEMU_FILE_TYPE_DTB, BINARY_DEVICE_TREE_FILE); if (!filename) { return -1; } @@ -109,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; @@ -142,8 +120,9 @@ 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) @@ -162,7 +141,6 @@ 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", @@ -235,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, mc->default_nic, 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. */ @@ -253,7 +228,8 @@ static void bamboo_init(MachineState *machine) if (success < 0) { uint64_t elf_entry; success = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, - NULL, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); + NULL, NULL, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); entry = elf_entry; } /* XXX try again as binary */ diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index 4181c843a82..89e3fae08d7 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -14,9 +14,10 @@ #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 "system/reset.h" #include "cpu.h" #include "ppc440.h" @@ -73,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; @@ -193,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) @@ -203,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) @@ -1061,15 +1020,14 @@ static void ppc460ex_pcie_realize(DeviceState *dev, Error **errp) ppc460ex_pcie_register_dcrs(s); } -static Property ppc460ex_pcie_props[] = { +static const 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(), }; -static void ppc460ex_pcie_class_init(ObjectClass *klass, void *data) +static void ppc460ex_pcie_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c index c1d111465db..f36c519c8bf 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -231,18 +231,17 @@ static void ppc4xx_mal_finalize(Object *obj) g_free(mal->txctpr); } -static Property ppc4xx_mal_properties[] = { +static const Property ppc4xx_mal_properties[] = { DEFINE_PROP_UINT8("txc-num", Ppc4xxMalState, txcnum, 0), DEFINE_PROP_UINT8("rxc-num", Ppc4xxMalState, rxcnum, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void ppc4xx_mal_class_init(ObjectClass *oc, void *data) +static void ppc4xx_mal_class_init(ObjectClass *oc, const 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); @@ -327,12 +326,12 @@ static void ppc405_plb_realize(DeviceState *dev, Error **errp) ppc4xx_dcr_register(dcr, PLB4A1_ACR, plb, &dcr_read_plb, &dcr_write_plb); } -static void ppc405_plb_class_init(ObjectClass *oc, void *data) +static void ppc405_plb_class_init(ObjectClass *oc, const 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; } @@ -513,12 +512,12 @@ static void ppc405_ebc_realize(DeviceState *dev, Error **errp) ppc4xx_dcr_register(dcr, EBC0_CFGDATA, ebc, &dcr_read_ebc, &dcr_write_ebc); } -static void ppc405_ebc_class_init(ObjectClass *oc, void *data) +static void ppc405_ebc_class_init(ObjectClass *oc, const 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; } @@ -539,13 +538,12 @@ bool ppc4xx_dcr_realize(Ppc4xxDcrDeviceState *dev, PowerPCCPU *cpu, return sysbus_realize(SYS_BUS_DEVICE(dev), errp); } -static Property ppc4xx_dcr_properties[] = { +static const Property ppc4xx_dcr_properties[] = { DEFINE_PROP_LINK("cpu", Ppc4xxDcrDeviceState, cpu, TYPE_POWERPC_CPU, PowerPCCPU *), - DEFINE_PROP_END_OF_LIST(), }; -static void ppc4xx_dcr_class_init(ObjectClass *oc, void *data) +static void ppc4xx_dcr_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ppc/ppc4xx_sdram.c b/hw/ppc/ppc4xx_sdram.c index c0c87ff636a..592769826bd 100644 --- a/hw/ppc/ppc4xx_sdram.c +++ b/hw/ppc/ppc4xx_sdram.c @@ -34,7 +34,7 @@ #include "qapi/error.h" #include "qemu/log.h" #include "qemu/error-report.h" -#include "exec/address-spaces.h" /* get_system_memory() */ +#include "system/address-spaces.h" /* get_system_memory() */ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/ppc/ppc4xx.h" @@ -425,19 +425,18 @@ static void ppc4xx_sdram_ddr_realize(DeviceState *dev, Error **errp) s, &sdram_ddr_dcr_read, &sdram_ddr_dcr_write); } -static Property ppc4xx_sdram_ddr_props[] = { +static const Property ppc4xx_sdram_ddr_props[] = { DEFINE_PROP_LINK("dram", Ppc4xxSdramDdrState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdrState, nbanks, 4), - DEFINE_PROP_END_OF_LIST(), }; -static void ppc4xx_sdram_ddr_class_init(ObjectClass *oc, void *data) +static void ppc4xx_sdram_ddr_class_init(ObjectClass *oc, const 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); @@ -710,19 +709,18 @@ static void ppc4xx_sdram_ddr2_realize(DeviceState *dev, Error **errp) s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); } -static Property ppc4xx_sdram_ddr2_props[] = { +static const Property ppc4xx_sdram_ddr2_props[] = { DEFINE_PROP_LINK("dram", Ppc4xxSdramDdr2State, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdr2State, nbanks, 4), - DEFINE_PROP_END_OF_LIST(), }; -static void ppc4xx_sdram_ddr2_class_init(ObjectClass *oc, void *data) +static void ppc4xx_sdram_ddr2_class_init(ObjectClass *oc, const 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 ca22da196ad..3872ae28222 100644 --- a/hw/ppc/ppc_booke.c +++ b/hw/ppc/ppc_booke.c @@ -24,13 +24,24 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/target_page.h" #include "hw/ppc/ppc.h" #include "qemu/timer.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/reset.h" +#include "system/runstate.h" #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 bbce63e8a42..2310f62a91e 100644 --- a/hw/ppc/ppce500_spin.c +++ b/hw/ppc/ppce500_spin.c @@ -32,7 +32,8 @@ #include "qemu/units.h" #include "hw/hw.h" #include "hw/sysbus.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" +#include "hw/ppc/ppc.h" #include "e500.h" #include "qom/object.h" @@ -70,31 +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; -#ifdef CONFIG_KVM - env->tlb_dirty = true; -#endif -} - static void spin_kick(CPUState *cs, run_on_cpu_data data) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); SpinInfo *curspin = data.host_ptr; - hwaddr map_size = 64 * MiB; - hwaddr map_start; + 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]); @@ -108,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; @@ -188,11 +175,11 @@ static void ppce500_spin_initfn(Object *obj) sysbus_init_mmio(dev, &s->iomem); } -static void ppce500_spin_class_init(ObjectClass *klass, void *data) +static void ppce500_spin_class_init(ObjectClass *klass, const 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 f6fd35fcb9e..982e40e53e1 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" @@ -36,15 +35,18 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/log.h" +#include "qemu/datadir.h" #include "hw/loader.h" #include "hw/rtc/mc146818rtc.h" #include "hw/isa/pc87312.h" #include "hw/qdev-properties.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" +#include "exec/target_page.h" +#include "system/kvm.h" +#include "system/reset.h" #include "trace.h" #include "elf.h" #include "qemu/units.h" +#include "audio/audio.h" /* SMP is not enabled, for now */ #define MAX_CPUS 1 @@ -54,6 +56,8 @@ #define KERNEL_LOAD_ADDR 0x01000000 #define INITRD_LOAD_ADDR 0x01800000 +#define BIOS_ADDR 0xfff00000 +#define BIOS_SIZE (1 * MiB) #define NVRAM_SIZE 0x2000 static void fw_cfg_boot_set(void *opaque, const char *boot_device, @@ -240,7 +244,9 @@ static void ibm_40p_init(MachineState *machine) ISADevice *isa_dev; ISABus *isa_bus; void *fw_cfg; - int i; + MemoryRegion *bios = g_new(MemoryRegion, 1); + char *filename; + ssize_t bios_size = -1; uint32_t kernel_base = 0, initrd_base = 0; long kernel_size = 0, initrd_size = 0; char boot_device; @@ -263,10 +269,27 @@ static void ibm_40p_init(MachineState *machine) cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); qemu_register_reset(ppc_prep_reset, cpu); + /* allocate and load firmware */ + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!filename) { + error_report("Could not find bios image '%s'", bios_name); + exit(1); + } + memory_region_init_rom(bios, NULL, "bios", BIOS_SIZE, &error_fatal); + memory_region_add_subregion(get_system_memory(), BIOS_ADDR, bios); + bios_size = load_elf(filename, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); + if (bios_size < 0) { + bios_size = load_image_targphys(filename, BIOS_ADDR, BIOS_SIZE); + } + if (bios_size < 0 || bios_size > BIOS_SIZE) { + error_report("Could not load bios image '%s'", filename); + return; + } + g_free(filename); + /* PCI host */ dev = qdev_new("raven-pcihost"); - qdev_prop_set_string(dev, "bios-name", bios_name); - qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE); pcihost = SYS_BUS_DEVICE(dev); object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev)); sysbus_realize_and_unref(pcihost, &error_fatal); @@ -278,9 +301,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")); @@ -310,6 +333,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"); @@ -331,10 +358,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, mc->default_nic, - 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 */ @@ -426,6 +452,8 @@ static void ibm_40p_machine_init(MachineClass *mc) 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 5a56f155f50..41cd923b94a 100644 --- a/hw/ppc/prep_systemio.c +++ b/hw/ppc/prep_systemio.c @@ -28,18 +28,18 @@ #include "hw/isa/isa.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qom/object.h" #include "qemu/error-report.h" /* for error_report() */ #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "cpu.h" #include "trace.h" #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), @@ -285,13 +285,12 @@ static const VMStateDescription vmstate_prep_systemio = { }, }; -static Property prep_systemio_properties[] = { +static const Property prep_systemio_properties[] = { DEFINE_PROP_UINT8("ibm-planar-id", PrepSystemIoState, ibm_planar_id, 0), DEFINE_PROP_UINT8("equipment", PrepSystemIoState, equipment, 0), - DEFINE_PROP_END_OF_LIST() }; -static void prep_systemio_class_initfn(ObjectClass *klass, void *data) +static void prep_systemio_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/rs6000_mc.c b/hw/ppc/rs6000_mc.c index c0bc212e924..a0964051d1e 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 @@ -22,7 +24,7 @@ #include "hw/isa/isa.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qapi/error.h" #include "trace.h" #include "qom/object.h" @@ -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,19 +201,18 @@ 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() }, }; -static Property rs6000mc_properties[] = { +static const Property rs6000mc_properties[] = { DEFINE_PROP_UINT32("ram-size", RS6000MCState, ram_size, 0), DEFINE_PROP_BOOL("auto-configure", RS6000MCState, autoconfigure, true), - DEFINE_PROP_END_OF_LIST() }; -static void rs6000mc_class_initfn(ObjectClass *klass, void *data) +static void rs6000mc_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 1e615b8d355..ee31bd8f349 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -17,21 +17,24 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "hw/boards.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_ppc.h" -#include "sysemu/device_tree.h" -#include "sysemu/block-backend.h" +#include "system/device_tree.h" +#include "system/block-backend.h" +#include "exec/page-protection.h" #include "hw/loader.h" #include "elf.h" -#include "exec/memory.h" +#include "system/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 "system/system.h" +#include "system/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" @@ -139,7 +142,7 @@ static int sam460ex_load_device_tree(MachineState *machine, uint32_t clock_freq = CPU_FREQ; int offset; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); + filename = qemu_find_file(QEMU_FILE_TYPE_DTB, BINARY_DEVICE_TREE_FILE); if (!filename) { error_report("Couldn't find dtb file `%s'", BINARY_DEVICE_TREE_FILE); exit(1); @@ -210,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; @@ -250,20 +221,27 @@ 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); - env->gpr[6] = tswap32(EPAPR_MAGIC); + booke_set_tlb(&env->tlb.tlbe[0], 0, 0, 1 << 31); + env->gpr[6] = 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; } } @@ -273,6 +251,7 @@ static void sam460ex_init(MachineState *machine) DeviceState *uic[4]; int i; PCIBus *pci_bus; + USBBus *usb_bus; PowerPCCPU *cpu; CPUPPCState *env; I2CBus *i2c; @@ -420,8 +399,10 @@ 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); @@ -445,15 +426,27 @@ static void sam460ex_init(MachineState *machine) /* 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(get_system_memory(), 0x4ef600300, 0, qdev_get_gpio_in(uic[1], 1), @@ -486,7 +479,7 @@ static void sam460ex_init(MachineState *machine) success = load_elf(machine->kernel_filename, NULL, NULL, NULL, &elf_entry, NULL, NULL, NULL, - 1, PPC_ELF_MACHINE, 0, 0); + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); entry = elf_entry; } /* XXX try again as binary */ @@ -527,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 f7cc6a890f4..702f774cda9 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4,6 +4,9 @@ * Copyright (c) 2004-2007 Fabrice Bellard * Copyright (c) 2007 Jocelyn Mayer * Copyright (c) 2010 David Gibson, IBM Corporation. + * Copyright (c) 2010-2024, IBM Corporation.. + * + * SPDX-License-Identifier: GPL-2.0-or-later * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,19 +35,20 @@ #include "qapi/qapi-events-machine.h" #include "qapi/qapi-events-qdev.h" #include "qapi/visitor.h" -#include "sysemu/sysemu.h" -#include "sysemu/hostmem.h" -#include "sysemu/numa.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/hostmem.h" +#include "system/numa.h" +#include "system/tcg.h" +#include "system/qtest.h" +#include "system/reset.h" +#include "system/runstate.h" #include "qemu/log.h" #include "hw/fw-path-provider.h" #include "elf.h" #include "net/net.h" -#include "sysemu/device_tree.h" -#include "sysemu/cpus.h" -#include "sysemu/hw_accel.h" +#include "system/device_tree.h" +#include "system/cpus.h" +#include "system/hw_accel.h" #include "kvm_ppc.h" #include "migration/misc.h" #include "migration/qemu-file-types.h" @@ -73,7 +77,8 @@ #include "hw/virtio/virtio-scsi.h" #include "hw/virtio/vhost-scsi-common.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" +#include "system/confidential-guest-support.h" #include "hw/usb.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -86,9 +91,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 @@ -133,40 +135,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); @@ -212,29 +180,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 */ @@ -244,6 +223,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, 64: DAWR1 (ISA 3.1) */ + 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; @@ -259,6 +268,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; } @@ -282,10 +295,40 @@ static void spapr_dt_pa_features(SpaprMachineState *spapr, * in pa-features. So hide it from them. */ pa_features[40 + 2] &= ~0x80; /* Radix MMU */ } + if (spapr_get_cap(spapr, SPAPR_CAP_DAWR1)) { + g_assert(pa_size > 66); + pa_features[66] |= 0x80; + } _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) { @@ -591,7 +634,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 +675,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 +708,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 +789,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 +823,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 +872,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); @@ -1119,7 +1168,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))); @@ -1208,9 +1257,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; } @@ -1277,7 +1324,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)); @@ -1335,7 +1382,6 @@ void spapr_init_all_lpcrs(target_ulong value, target_ulong mask) } } - static bool spapr_get_pate(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu, target_ulong lpid, ppc_v3_pate_t *entry) { @@ -1348,40 +1394,46 @@ 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); + 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(); + } + } +} - patb = spapr->nested_ptcr & PTCR_PATB; - pats = spapr->nested_ptcr & PTCR_PATS; +static uint64_t *hpte_get_ptr(SpaprMachineState *s, unsigned index) +{ + uint64_t *table = s->htab; - /* Check if partition table is properly aligned */ - if (patb & MAKE_64BIT_MASK(0, pats + 12)) { - return false; - } + return &table[2 * index]; +} - /* Calculate number of entries */ - pats = 1ull << (pats + 12 - 4); - if (pats <= lpid) { - return false; - } +static bool hpte_is_valid(SpaprMachineState *s, unsigned index) +{ + return ldq_be_p(hpte_get_ptr(s, index)) & HPTE64_V_VALID; +} - /* Grab entry */ - patb += 16 * lpid; - entry->dw0 = ldq_phys(CPU(cpu)->as, patb); - entry->dw1 = ldq_phys(CPU(cpu)->as, patb + 8); - } +static bool hpte_is_dirty(SpaprMachineState *s, unsigned index) +{ + return ldq_be_p(hpte_get_ptr(s, index)) & HPTE64_V_HPTE_DIRTY; +} - return true; +static void hpte_set_clean(SpaprMachineState *s, unsigned index) +{ + stq_be_p(hpte_get_ptr(s, index), + ldq_be_p(hpte_get_ptr(s, index)) & ~HPTE64_V_HPTE_DIRTY); } -#define HPTE(_table, _i) (void *)(((uint64_t *)(_table)) + ((_i) * 2)) -#define HPTE_VALID(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_VALID) -#define HPTE_DIRTY(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_HPTE_DIRTY) -#define CLEAN_HPTE(_hpte) ((*(uint64_t *)(_hpte)) &= tswap64(~HPTE64_V_HPTE_DIRTY)) -#define DIRTY_HPTE(_hpte) ((*(uint64_t *)(_hpte)) |= tswap64(HPTE64_V_HPTE_DIRTY)) +static void hpte_set_dirty(SpaprMachineState *s, unsigned index) +{ + stq_be_p(hpte_get_ptr(s, index), + ldq_be_p(hpte_get_ptr(s, index)) | HPTE64_V_HPTE_DIRTY); +} /* * Get the fd to access the kernel htab, re-opening it if necessary @@ -1592,7 +1644,7 @@ int spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, Error **errp) spapr->htab_shift = shift; for (i = 0; i < size / HASH_PTE_SIZE_64; i++) { - DIRTY_HPTE(HPTE(spapr->htab, i)); + hpte_set_dirty(spapr, i); } } /* We're setting up a hash table, so that means we're not radix */ @@ -1644,7 +1696,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; @@ -1652,7 +1704,7 @@ static void spapr_machine_reset(MachineState *machine, ShutdownCause reason) void *fdt; int rc; - if (reason != SHUTDOWN_CAUSE_SNAPSHOT_LOAD) { + if (type != RESET_TYPE_SNAPSHOT_LOAD) { /* * Record-replay snapshot load must not consume random, this was * already replayed from initial machine reset. @@ -1660,8 +1712,11 @@ static void spapr_machine_reset(MachineState *machine, ShutdownCause reason) qemu_guest_getrandom_nofail(spapr->fdt_rng_seed, 32); } - pef_kvm_reset(machine->cgs, &error_fatal); + 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() && @@ -1678,7 +1733,7 @@ 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(); @@ -1735,7 +1790,6 @@ static void spapr_machine_reset(MachineState *machine, ShutdownCause reason) 0, fdt_addr, 0); cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); } - qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); g_free(spapr->fdt_blob); spapr->fdt_size = fdt_totalsize(fdt); @@ -1755,7 +1809,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) @@ -1892,7 +1946,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, @@ -1906,7 +1960,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() @@ -1962,7 +2016,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() @@ -1981,7 +2035,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() }, @@ -1999,7 +2053,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() }, @@ -2029,7 +2083,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, @@ -2067,7 +2121,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), @@ -2082,7 +2136,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), @@ -2092,7 +2146,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, @@ -2111,11 +2165,14 @@ 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, + &vmstate_spapr_cap_dawr1, NULL } }; -static int htab_save_setup(QEMUFile *f, void *opaque) +static int htab_save_setup(QEMUFile *f, void *opaque, Error **errp) { SpaprMachineState *spapr = opaque; @@ -2145,7 +2202,7 @@ static void htab_save_chunk(QEMUFile *f, SpaprMachineState *spapr, qemu_put_be32(f, chunkstart); qemu_put_be16(f, n_valid); qemu_put_be16(f, n_invalid); - qemu_put_buffer(f, HPTE(spapr->htab, chunkstart), + qemu_put_buffer(f, (void *)hpte_get_ptr(spapr, chunkstart), HASH_PTE_SIZE_64 * n_valid); } @@ -2171,16 +2228,16 @@ static void htab_save_first_pass(QEMUFile *f, SpaprMachineState *spapr, /* Consume invalid HPTEs */ while ((index < htabslots) - && !HPTE_VALID(HPTE(spapr->htab, index))) { - CLEAN_HPTE(HPTE(spapr->htab, index)); + && !hpte_is_valid(spapr, index)) { + hpte_set_clean(spapr, index); index++; } /* Consume valid HPTEs */ chunkstart = index; while ((index < htabslots) && (index - chunkstart < USHRT_MAX) - && HPTE_VALID(HPTE(spapr->htab, index))) { - CLEAN_HPTE(HPTE(spapr->htab, index)); + && hpte_is_valid(spapr, index)) { + hpte_set_clean(spapr, index); index++; } @@ -2220,7 +2277,7 @@ static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, /* Consume non-dirty HPTEs */ while ((index < htabslots) - && !HPTE_DIRTY(HPTE(spapr->htab, index))) { + && !hpte_is_dirty(spapr, index)) { index++; examined++; } @@ -2228,9 +2285,9 @@ static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, chunkstart = index; /* Consume valid dirty HPTEs */ while ((index < htabslots) && (index - chunkstart < USHRT_MAX) - && HPTE_DIRTY(HPTE(spapr->htab, index)) - && HPTE_VALID(HPTE(spapr->htab, index))) { - CLEAN_HPTE(HPTE(spapr->htab, index)); + && hpte_is_dirty(spapr, index) + && hpte_is_valid(spapr, index)) { + hpte_set_clean(spapr, index); index++; examined++; } @@ -2238,9 +2295,9 @@ static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, invalidstart = index; /* Consume invalid dirty HPTEs */ while ((index < htabslots) && (index - invalidstart < USHRT_MAX) - && HPTE_DIRTY(HPTE(spapr->htab, index)) - && !HPTE_VALID(HPTE(spapr->htab, index))) { - CLEAN_HPTE(HPTE(spapr->htab, index)); + && hpte_is_dirty(spapr, index) + && !hpte_is_valid(spapr, index)) { + hpte_set_clean(spapr, index); index++; examined++; } @@ -2422,11 +2479,11 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) if (spapr->htab) { if (n_valid) { - qemu_get_buffer(f, HPTE(spapr->htab, index), + qemu_get_buffer(f, (void *)hpte_get_ptr(spapr, index), HASH_PTE_SIZE_64 * n_valid); } if (n_invalid) { - memset(HPTE(spapr->htab, index + n_valid), 0, + memset(hpte_get_ptr(spapr, index + n_valid), 0, HASH_PTE_SIZE_64 * n_invalid); } } else { @@ -2573,7 +2630,7 @@ static void spapr_set_vsmt_mode(SpaprMachineState *spapr, Error **errp) return; } - /* Detemine the VSMT mode to use: */ + /* Determine the VSMT mode to use: */ if (vsmt_user) { if (spapr->vsmt < smp_threads) { error_setg(errp, "Cannot support VSMT mode %d" @@ -2629,7 +2686,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; @@ -2658,17 +2714,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; @@ -2771,6 +2816,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); @@ -2785,7 +2831,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; @@ -2842,10 +2890,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); @@ -2872,11 +2918,12 @@ static void spapr_machine_init(MachineState *machine) spapr_ovec_set(spapr->ov5, OV5_XIVE_EXPLOIT); } + qemu_guest_getrandom_nofail(&spapr->hashpkey_val, + sizeof(spapr->hashpkey_val)); + /* 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); @@ -2931,16 +2978,7 @@ static void spapr_machine_init(MachineState *machine) 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); @@ -2980,21 +3018,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); } @@ -3009,15 +3038,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"); } @@ -3028,13 +3055,13 @@ static void spapr_machine_init(MachineState *machine) spapr->kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, spapr, - NULL, &loaded_addr, NULL, NULL, 1, - PPC_ELF_MACHINE, 0, 0); + NULL, &loaded_addr, NULL, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (spapr->kernel_size == ELF_LOAD_WRONG_ENDIAN) { spapr->kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, spapr, - NULL, &loaded_addr, NULL, NULL, 0, - PPC_ELF_MACHINE, 0, 0); + NULL, &loaded_addr, NULL, NULL, + ELFDATA2LSB, PPC_ELF_MACHINE, 0, 0); spapr->kernel_le = spapr->kernel_size > 0; } if (spapr->kernel_size < 0) { @@ -3109,7 +3136,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". @@ -3212,8 +3239,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) { @@ -3471,8 +3498,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 */ @@ -3592,7 +3618,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); @@ -3601,11 +3626,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); @@ -3629,7 +3649,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 { @@ -3715,7 +3735,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) { @@ -3752,16 +3771,8 @@ 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->canonical_path); } @@ -3871,21 +3882,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); @@ -3966,10 +3965,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; @@ -4003,7 +4000,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 @@ -4017,12 +4014,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, @@ -4134,7 +4125,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); } @@ -4343,7 +4333,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); @@ -4397,8 +4387,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. @@ -4443,9 +4432,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; } @@ -4470,14 +4456,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"); } /* @@ -4485,7 +4470,7 @@ static void spapr_pic_print_info(InterruptStatsProvider *obj, */ static int spapr_match_nvt(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match) { SpaprMachineState *spapr = SPAPR_MACHINE(xfb); @@ -4493,7 +4478,7 @@ static int spapr_match_nvt(XiveFabric *xfb, uint8_t format, XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); int count; - count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore, + count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, cam_ignore, priority, logic_serv, match); if (count < 0) { return count; @@ -4610,7 +4595,7 @@ static void spapr_cpu_exec_exit(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu) } } -static void spapr_machine_class_init(ObjectClass *oc, void *data) +static void spapr_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(oc); @@ -4636,13 +4621,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 = ""; @@ -4662,9 +4644,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.2"); + 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; @@ -4702,10 +4683,12 @@ 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; + smc->default_caps.caps[SPAPR_CAP_DAWR1] = SPAPR_CAP_ON; /* * This cap specifies whether the AIL 3 mode for @@ -4734,7 +4717,7 @@ static const TypeInfo spapr_machine_info = { .instance_finalize = spapr_machine_finalizefn, .class_size = sizeof(SpaprMachineClass), .class_init = spapr_machine_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { TYPE_NMI }, { TYPE_HOTPLUG_HANDLER }, @@ -4753,36 +4736,101 @@ 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, \ + const 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), \ + static const TypeInfo MACHINE_VER_SYM(info, spapr, __VA_ARGS__) = \ + { \ + .name = MACHINE_VER_TYPE_NAME("pseries", __VA_ARGS__), \ .parent = TYPE_SPAPR_MACHINE, \ - .class_init = spapr_machine_##suffix##_class_init, \ + .class_init = MACHINE_VER_SYM(class_init, spapr, __VA_ARGS__), \ }; \ - static void spapr_machine_register_##suffix(void) \ + static void MACHINE_VER_SYM(register, spapr, __VA_ARGS__)(void) \ { \ - type_register(&spapr_machine_##suffix##_info); \ + MACHINE_VER_DELETION(__VA_ARGS__); \ + type_register_static(&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-10.1 + */ +static void spapr_machine_10_1_class_options(MachineClass *mc) +{ + /* Defaults for the latest behaviour inherited from the base class */ +} + +DEFINE_SPAPR_MACHINE_AS_LATEST(10, 1); + +/* + * pseries-10.0 + */ +static void spapr_machine_10_0_class_options(MachineClass *mc) +{ + spapr_machine_10_1_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_0, hw_compat_10_0_len); +} + +DEFINE_SPAPR_MACHINE(10, 0); + +/* + * pseries-9.2 + */ +static void spapr_machine_9_2_class_options(MachineClass *mc) +{ + spapr_machine_10_0_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_2, hw_compat_9_2_len); +} + +DEFINE_SPAPR_MACHINE(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) { - /* Defaults for the latest behaviour inherited from the base class */ + spapr_machine_9_0_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_8_2, hw_compat_8_2_len); + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power9_v2.2"); } -DEFINE_SPAPR_MACHINE(8_2, "8.2", true); +DEFINE_SPAPR_MACHINE(8, 2); /* * pseries-8.1 @@ -4793,7 +4841,7 @@ static void spapr_machine_8_1_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_8_1, hw_compat_8_1_len); } -DEFINE_SPAPR_MACHINE(8_1, "8.1", false); +DEFINE_SPAPR_MACHINE(8, 1); /* * pseries-8.0 @@ -4804,7 +4852,7 @@ static void spapr_machine_8_0_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_8_0, hw_compat_8_0_len); } -DEFINE_SPAPR_MACHINE(8_0, "8.0", false); +DEFINE_SPAPR_MACHINE(8, 0); /* * pseries-7.2 @@ -4815,7 +4863,7 @@ static void spapr_machine_7_2_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_7_2, hw_compat_7_2_len); } -DEFINE_SPAPR_MACHINE(7_2, "7.2", false); +DEFINE_SPAPR_MACHINE(7, 2); /* * pseries-7.1 @@ -4826,7 +4874,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 @@ -4837,7 +4885,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 @@ -4848,7 +4896,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 @@ -4863,7 +4911,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 @@ -4874,7 +4922,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 @@ -4885,7 +4933,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 @@ -4899,7 +4947,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 @@ -4918,7 +4966,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 @@ -4935,7 +4983,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 @@ -4955,7 +5003,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 @@ -4963,16 +5011,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) @@ -4986,7 +5030,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 @@ -5008,7 +5052,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 @@ -5026,282 +5070,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)->device_memory) { - /* 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 5a0755d34fb..f2f5722d8ad 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -26,14 +26,15 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/visitor.h" -#include "sysemu/hw_accel.h" -#include "exec/ram_addr.h" +#include "system/hw_accel.h" +#include "system/ram_addr.h" #include "target/ppc/cpu.h" #include "target/ppc/mmu-hash64.h" #include "cpu-models.h" #include "kvm_ppc.h" #include "migration/vmstate.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" +#include "system/hostmem.h" #include "hw/ppc/spapr.h" @@ -194,8 +195,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 +213,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"); } @@ -487,6 +485,50 @@ static void cap_nested_kvm_hv_apply(SpaprMachineState *spapr, 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"); } } @@ -655,6 +697,34 @@ static void cap_ail_mode_3_apply(SpaprMachineState *spapr, } } +static void cap_dawr1_apply(SpaprMachineState *spapr, uint8_t val, + Error **errp) +{ + ERRP_GUARD(); + + if (!val) { + return; /* Disable by default */ + } + + if (!ppc_type_check_compat(MACHINE(spapr)->cpu_type, + CPU_POWERPC_LOGICAL_3_10, 0, + spapr->max_compat_pvr)) { + error_setg(errp, "DAWR1 supported only on POWER10 and later CPUs"); + error_append_hint(errp, "Try appending -machine cap-dawr1=off\n"); + return; + } + + if (kvm_enabled()) { + if (!kvmppc_has_cap_dawr1()) { + error_setg(errp, "DAWR1 not supported by KVM."); + error_append_hint(errp, "Try appending -machine cap-dawr1=off"); + } else if (kvmppc_set_cap_dawr1(val) < 0) { + error_setg(errp, "Error enabling cap-dawr1 with KVM."); + error_append_hint(errp, "Try appending -machine cap-dawr1=off"); + } + } +} + SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = { [SPAPR_CAP_HTM] = { .name = "htm", @@ -735,6 +805,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", @@ -781,6 +860,15 @@ SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .type = "bool", .apply = cap_ail_mode_3_apply, }, + [SPAPR_CAP_DAWR1] = { + .name = "dawr1", + .description = "Allow 2nd Data Address Watchpoint Register (DAWR1)", + .index = SPAPR_CAP_DAWR1, + .get = spapr_cap_get_bool, + .set = spapr_cap_set_bool, + .type = "bool", + .apply = cap_dawr1_apply, + }, }; static SpaprCapabilities default_caps_with_cpu(SpaprMachineState *spapr, @@ -791,6 +879,11 @@ static SpaprCapabilities default_caps_with_cpu(SpaprMachineState *spapr, caps = smc->default_caps; + if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_3_10, + 0, spapr->max_compat_pvr)) { + caps.caps[SPAPR_CAP_DAWR1] = SPAPR_CAP_OFF; + } + if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_3_00, 0, spapr->max_compat_pvr)) { caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_OFF; @@ -904,7 +997,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() \ @@ -919,10 +1012,13 @@ 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); +SPAPR_CAP_MIG_STATE(dawr1, SPAPR_CAP_DAWR1); void spapr_caps_init(SpaprMachineState *spapr) { @@ -982,7 +1078,7 @@ void spapr_caps_add_properties(SpaprMachineClass *smc) for (i = 0; i < ARRAY_SIZE(capability_table); i++) { SpaprCapabilityInfo *cap = &capability_table[i]; g_autofree char *name = g_strdup_printf("cap-%s", cap->name); - g_autofree char *desc = g_strdup_printf("%s", cap->description); + g_autofree char *desc = g_strdup(cap->description); object_class_property_add(klass, name, cap->type, cap->get, cap->set, diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 91fae56573e..4952f9bd2cf 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -15,15 +15,15 @@ #include "target/ppc/cpu.h" #include "hw/ppc/spapr.h" #include "qapi/error.h" -#include "sysemu/cpus.h" -#include "sysemu/kvm.h" +#include "system/cpus.h" +#include "system/kvm.h" #include "target/ppc/kvm_ppc.h" #include "hw/ppc/ppc.h" #include "target/ppc/mmu-hash64.h" #include "target/ppc/power8-pmu.h" -#include "sysemu/numa.h" -#include "sysemu/reset.h" -#include "sysemu/hw_accel.h" +#include "system/numa.h" +#include "system/reset.h" +#include "system/hw_accel.h" #include "qemu/error-report.h" static void spapr_reset_vcpu(PowerPCCPU *cpu) @@ -37,11 +37,18 @@ static void spapr_reset_vcpu(PowerPCCPU *cpu) cpu_reset(cs); + env->quiesced = true; /* set "RTAS stopped" state. */ + ppc_maybe_interrupt(env); + /* * "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]; @@ -94,6 +101,9 @@ void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, CPU(cpu)->halted = 0; /* Enable Power-saving mode Exit Cause exceptions */ ppc_store_lpcr(cpu, env->spr[SPR_LPCR] | pcc->lpcr_pm); + + env->quiesced = false; /* clear "RTAS stopped" state. */ + ppc_maybe_interrupt(env); } /* @@ -127,7 +137,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() @@ -146,7 +156,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() @@ -165,11 +175,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 @@ -180,10 +190,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 } @@ -193,9 +203,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)); @@ -245,8 +253,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]); @@ -272,6 +279,8 @@ static bool spapr_realize_vcpu(PowerPCCPU *cpu, SpaprMachineState *spapr, env->spr_cb[SPR_PIR].default_value = cs->cpu_index; env->spr_cb[SPR_TIR].default_value = thread_index; + env->spr_cb[SPR_HASHPKEYR].default_value = spapr->hashpkey_val; + cpu_ppc_set_1lpar(cpu); /* Set time-base frequency to 512 MHz. vhyp must be set first. */ @@ -282,10 +291,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; } @@ -297,21 +304,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); @@ -342,30 +354,33 @@ 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, i, 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; } } } -static Property spapr_cpu_core_properties[] = { +static const 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() }; -static void spapr_cpu_core_class_init(ObjectClass *oc, void *data) +static void spapr_cpu_core_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); SpaprCpuCoreClass *scc = SPAPR_CPU_CORE_CLASS(oc); 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; } @@ -373,7 +388,7 @@ static void spapr_cpu_core_class_init(ObjectClass *oc, void *data) #define DEFINE_SPAPR_CPU_CORE_TYPE(cpu_model) \ { \ .parent = TYPE_SPAPR_CPU_CORE, \ - .class_data = (void *) POWERPC_CPU_TYPE_NAME(cpu_model), \ + .class_data = POWERPC_CPU_TYPE_NAME(cpu_model), \ .class_init = spapr_cpu_core_class_init, \ .name = SPAPR_CPU_CORE_TYPE_NAME(cpu_model), \ } @@ -389,17 +404,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("power9_v2.2"), - DEFINE_SPAPR_CPU_CORE_TYPE("power10_v1.0"), 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 b5c400a94d1..d2044b4fb52 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "qemu/cutils.h" #include "hw/ppc/spapr_drc.h" #include "qom/object.h" @@ -23,11 +23,11 @@ #include "hw/ppc/spapr.h" /* for RTAS return codes */ #include "hw/pci-host/spapr.h" /* spapr_phb_remove_pci_device_cb callback */ #include "hw/ppc/spapr_nvdimm.h" -#include "sysemu/device_tree.h" -#include "sysemu/reset.h" +#include "system/device_tree.h" +#include "system/reset.h" #include "trace.h" -#define DRC_CONTAINER_PATH "/dr-connector" +#define DRC_CONTAINER_PATH "dr-connector" #define DRC_INDEX_TYPE_SHIFT 28 #define DRC_INDEX_ID_MASK ((1ULL << DRC_INDEX_TYPE_SHIFT) - 1) @@ -341,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; @@ -351,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; @@ -369,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++) { @@ -471,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() } @@ -504,16 +504,26 @@ 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 } }; +static void drc_container_create(void) +{ + object_property_add_new_container(object_get_root(), DRC_CONTAINER_PATH); +} + +static Object *drc_container_get(void) +{ + return object_resolve_path_component(object_get_root(), DRC_CONTAINER_PATH); +} + static void drc_realize(DeviceState *d, Error **errp) { SpaprDrc *drc = SPAPR_DR_CONNECTOR(d); @@ -529,7 +539,7 @@ static void drc_realize(DeviceState *d, Error **errp) * inaccessible by the guest, since lookups rely on this path * existing in the composition tree */ - root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); + root_container = drc_container_get(); child_name = object_get_canonical_path_component(OBJECT(drc)); trace_spapr_drc_realize_child(spapr_drc_index(drc), child_name); object_property_add_alias(root_container, link_name, @@ -543,12 +553,10 @@ static void drc_unrealize(DeviceState *d) { SpaprDrc *drc = SPAPR_DR_CONNECTOR(d); g_autofree gchar *name = g_strdup_printf("%x", spapr_drc_index(drc)); - Object *root_container; trace_spapr_drc_unrealize(spapr_drc_index(drc)); vmstate_unregister(VMSTATE_IF(drc), &vmstate_spapr_drc, drc); - root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); - object_property_del(root_container, name); + object_property_del(drc_container_get(), name); } SpaprDrc *spapr_dr_connector_new(Object *owner, const char *type, @@ -581,10 +589,12 @@ static void spapr_dr_connector_instance_init(Object *obj) drc->state = drck->empty_state; } -static void spapr_dr_connector_class_init(ObjectClass *k, void *data) +static void spapr_dr_connector_class_init(ObjectClass *k, const void *data) { DeviceClass *dk = DEVICE_CLASS(k); + drc_container_create(); + dk->realize = drc_realize; dk->unrealize = drc_unrealize; /* @@ -611,7 +621,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() } @@ -655,7 +665,7 @@ static void unrealize_physical(DeviceState *d) qemu_unregister_reset(drc_physical_reset, drcp); } -static void spapr_drc_physical_class_init(ObjectClass *k, void *data) +static void spapr_drc_physical_class_init(ObjectClass *k, const void *data) { DeviceClass *dk = DEVICE_CLASS(k); SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -669,7 +679,7 @@ static void spapr_drc_physical_class_init(ObjectClass *k, void *data) drck->empty_state = SPAPR_DRC_STATE_PHYSICAL_POWERON; } -static void spapr_drc_logical_class_init(ObjectClass *k, void *data) +static void spapr_drc_logical_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -680,7 +690,7 @@ static void spapr_drc_logical_class_init(ObjectClass *k, void *data) drck->empty_state = SPAPR_DRC_STATE_LOGICAL_UNUSABLE; } -static void spapr_drc_cpu_class_init(ObjectClass *k, void *data) +static void spapr_drc_cpu_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -691,7 +701,7 @@ static void spapr_drc_cpu_class_init(ObjectClass *k, void *data) drck->dt_populate = spapr_core_dt_populate; } -static void spapr_drc_pci_class_init(ObjectClass *k, void *data) +static void spapr_drc_pci_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -702,7 +712,7 @@ static void spapr_drc_pci_class_init(ObjectClass *k, void *data) drck->dt_populate = spapr_pci_dt_populate; } -static void spapr_drc_lmb_class_init(ObjectClass *k, void *data) +static void spapr_drc_lmb_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -713,7 +723,7 @@ static void spapr_drc_lmb_class_init(ObjectClass *k, void *data) drck->dt_populate = spapr_lmb_dt_populate; } -static void spapr_drc_phb_class_init(ObjectClass *k, void *data) +static void spapr_drc_phb_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -724,7 +734,7 @@ static void spapr_drc_phb_class_init(ObjectClass *k, void *data) drck->dt_populate = spapr_phb_dt_populate; } -static void spapr_drc_pmem_class_init(ObjectClass *k, void *data) +static void spapr_drc_pmem_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -796,9 +806,8 @@ static const TypeInfo spapr_drc_pmem_info = { SpaprDrc *spapr_drc_by_index(uint32_t index) { Object *obj; - g_autofree gchar *name = g_strdup_printf("%s/%x", DRC_CONTAINER_PATH, - index); - obj = object_resolve_path(name, NULL); + g_autofree gchar *name = g_strdup_printf("%x", index); + obj = object_resolve_path_component(drc_container_get(), name); return !obj ? NULL : SPAPR_DR_CONNECTOR(obj); } @@ -860,7 +869,7 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask) /* aliases for all DRConnector objects will be rooted in QOM * composition tree at DRC_CONTAINER_PATH */ - root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); + root_container = drc_container_get(); object_property_iter_init(&iter, root_container); while ((prop = object_property_iter_next(&iter))) { @@ -953,7 +962,7 @@ void spapr_drc_reset_all(SpaprMachineState *spapr) ObjectProperty *prop; ObjectPropertyIterator iter; - drc_container = container_get(object_get_root(), DRC_CONTAINER_PATH); + drc_container = drc_container_get(); restart: object_property_iter_init(&iter, drc_container); while ((prop = object_property_iter_next(&iter))) { @@ -1237,8 +1246,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 4508e408147..832b0212f31 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -27,8 +27,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/device_tree.h" -#include "sysemu/runstate.h" +#include "system/device_tree.h" +#include "system/runstate.h" #include "hw/ppc/fdt.h" #include "hw/ppc/spapr.h" @@ -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 b7dc388f2fc..1e936f35e44 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1,15 +1,15 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qapi/error.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" -#include "sysemu/tcg.h" +#include "system/hw_accel.h" +#include "system/runstate.h" +#include "system/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 "exec/target_page.h" #include "helper_regs.h" #include "hw/ppc/ppc.h" #include "hw/ppc/spapr.h" @@ -124,9 +124,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) @@ -192,9 +194,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); } @@ -296,8 +300,10 @@ static target_ulong h_page_init(PowerPCCPU *cpu, SpaprMachineState *spapr, if (flags & (H_ICACHE_SYNCHRONIZE | H_ICACHE_INVALIDATE)) { if (kvm_enabled()) { kvmppc_icbi_range(cpu, pdst, len); - } else { + } else if (tcg_enabled()) { tb_flush(CPU(cpu)); + } else { + g_assert_not_reached(); } } @@ -575,6 +581,8 @@ static target_ulong h_confer(PowerPCCPU *cpu, SpaprMachineState *spapr, CPUState *cs = CPU(cpu); SpaprCpuState *spapr_cpu; + assert(tcg_enabled()); /* KVM will have handled this */ + /* * -1 means confer to all other CPUs without dispatch counter check, * otherwise it's a targeted confer. @@ -815,11 +823,12 @@ static target_ulong h_set_mode_resource_set_ciabr(PowerPCCPU *cpu, 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) +static target_ulong h_set_mode_resource_set_dawr(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong mflags, + target_ulong resource, + target_ulong value1, + target_ulong value2) { CPUPPCState *env = &cpu->env; @@ -832,8 +841,15 @@ static target_ulong h_set_mode_resource_set_dawr0(PowerPCCPU *cpu, return H_P4; } - ppc_store_dawr0(env, value1); - ppc_store_dawrx0(env, value2); + if (resource == H_SET_MODE_RESOURCE_SET_DAWR0) { + ppc_store_dawr0(env, value1); + ppc_store_dawrx0(env, value2); + } else if (resource == H_SET_MODE_RESOURCE_SET_DAWR1) { + ppc_store_dawr1(env, value1); + ppc_store_dawrx1(env, value2); + } else { + g_assert_not_reached(); + } return H_SUCCESS; } @@ -912,8 +928,9 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, SpaprMachineState *spapr, 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]); + case H_SET_MODE_RESOURCE_SET_DAWR1: + ret = h_set_mode_resource_set_dawr(cpu, spapr, args[0], args[1], + 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]); @@ -965,7 +982,6 @@ static void spapr_check_setup_free_hpt(SpaprMachineState *spapr, /* RADIX->HASH || NOTHING->HASH : Allocate HPT */ spapr_setup_hpt(spapr); } - return; } #define FLAGS_MASK 0x01FULL @@ -1522,6 +1538,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) { @@ -1615,7 +1653,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 @@ -1635,8 +1673,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); - - spapr_register_nested(); } type_init(hypercall_register_types) diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 5e3973fc5fb..c2432a0c00c 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -21,10 +21,10 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_ppc.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "trace.h" #include "hw/ppc/spapr.h" @@ -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 } @@ -668,11 +668,11 @@ int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, tcet->liobn, 0, tcet->nb_table << tcet->page_shift); } -static void spapr_tce_table_class_init(ObjectClass *klass, void *data) +static void spapr_tce_table_class_init(ObjectClass *klass, const 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; @@ -693,7 +693,8 @@ static const TypeInfo spapr_tce_table_info = { .class_init = spapr_tce_table_class_init, }; -static void spapr_iommu_memory_region_class_init(ObjectClass *klass, void *data) +static void spapr_iommu_memory_region_class_init(ObjectClass *klass, + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/ppc/spapr_irq.c b/hw/ppc/spapr_irq.c index a0d1e1298e1..d6d368dd08c 100644 --- a/hw/ppc/spapr_irq.c +++ b/hw/ppc/spapr_irq.c @@ -19,10 +19,12 @@ #include "hw/ppc/xics_spapr.h" #include "hw/qdev-properties.h" #include "cpu-models.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #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 index 121aa96ddcd..10cf634da19 100644 --- a/hw/ppc/spapr_nested.c +++ b/hw/ppc/spapr_nested.c @@ -1,13 +1,90 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "exec/target_long.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) +{ + return spapr->nested.guests ? + g_hash_table_lookup(spapr->nested.guests, + GINT_TO_POINTER(guestid)) : NULL; +} + +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, @@ -25,7 +102,7 @@ static target_ulong h_set_ptbl(PowerPCCPU *cpu, return H_PARAMETER; } - spapr->nested_ptcr = ptcr; /* Save new partition table */ + spapr->nested.ptcr = ptcr; /* Save new partition table */ return H_SUCCESS; } @@ -59,6 +136,7 @@ static target_ulong h_copy_tofrom_guest(PowerPCCPU *cpu, 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)); @@ -85,13 +163,79 @@ static void nested_save_state(struct nested_ppc_state *save, PowerPCCPU *cpu) save->pidr = env->spr[SPR_BOOKS_PID]; save->ppr = env->spr[SPR_PPR]; - save->tb_offset = env->tb_env->tb_offset; + 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) { - CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); memcpy(env->gpr, load->gpr, sizeof(env->gpr)); @@ -118,20 +262,58 @@ static void nested_load_state(PowerPCCPU *cpu, struct nested_ppc_state *load) env->spr[SPR_BOOKS_PID] = load->pidr; env->spr[SPR_PPR] = load->ppr; - env->tb_env->tb_offset = load->tb_offset; - - /* - * MSR updated, 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 */ + 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; + } } /* @@ -146,6 +328,7 @@ static target_ulong h_enter_nested(PowerPCCPU *cpu, { 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]; @@ -157,7 +340,7 @@ static target_ulong h_enter_nested(PowerPCCPU *cpu, struct kvmppc_pt_regs *regs; hwaddr len; - if (spapr->nested_ptcr == 0) { + if (spapr->nested.ptcr == 0) { return H_NOT_AVAILABLE; } @@ -244,6 +427,7 @@ static target_ulong h_enter_nested(PowerPCCPU *cpu, * 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); @@ -272,9 +456,10 @@ static target_ulong h_enter_nested(PowerPCCPU *cpu, return env->gpr[3]; } -void spapr_exit_nested(PowerPCCPU *cpu, int excp) +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]; @@ -284,8 +469,6 @@ void spapr_exit_nested(PowerPCCPU *cpu, int excp) struct kvmppc_pt_regs *regs; hwaddr len; - assert(spapr_cpu->in_nested); - nested_save_state(&l2_state, cpu); hsrr0 = env->spr[SPR_HSRR0]; hsrr1 = env->spr[SPR_HSRR1]; @@ -298,6 +481,7 @@ void spapr_exit_nested(PowerPCCPU *cpu, int excp) */ 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); @@ -375,21 +559,1411 @@ void spapr_exit_nested(PowerPCCPU *cpu, int excp) address_space_unmap(CPU(cpu)->as, regs, len, len, true); } -void spapr_register_nested(void) +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(SpaprMachineState *spapr, + SpaprMachineStateNestedGuest *guest, + target_ulong vcpuid) +{ + assert(spapr_nested_vcpu_check(guest, vcpuid, false)); + return &guest->vcpus[vcpuid].state; +} + +static void *get_vcpu_ptr(SpaprMachineState *spapr, + SpaprMachineStateNestedGuest *guest, + target_ulong vcpuid) +{ + assert(spapr_nested_vcpu_check(guest, vcpuid, false)); + return &guest->vcpus[vcpuid]; +} + +static void *get_guest_ptr(SpaprMachineState *spapr, + SpaprMachineStateNestedGuest *guest, + target_ulong vcpuid) +{ + return guest; /* for GSBE_NESTED */ +} + +static void *get_machine_ptr(SpaprMachineState *spapr, + SpaprMachineStateNestedGuest *guest, + target_ulong vcpuid) +{ + /* ignore guest and vcpuid for this */ + return &spapr->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), + GSBE_NESTED_MACHINE_DW(GSB_L0_GUEST_HEAP_INUSE, l0_guest_heap_inuse), + GSBE_NESTED_MACHINE_DW(GSB_L0_GUEST_HEAP_MAX, l0_guest_heap_max), + GSBE_NESTED_MACHINE_DW(GSB_L0_GUEST_PGTABLE_SIZE_INUSE, + l0_guest_pgtable_size_inuse), + GSBE_NESTED_MACHINE_DW(GSB_L0_GUEST_PGTABLE_SIZE_MAX, + l0_guest_pgtable_size_max), + GSBE_NESTED_MACHINE_DW(GSB_L0_GUEST_PGTABLE_RECLAIMED, + l0_guest_pgtable_reclaimed), +}; + +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_L0_GUEST_HEAP_INUSE) + + /*0x0800 - 0x0804 Hostwide Counters + RO */ + type->flags = GUEST_STATE_ELEMENT_TYPE_FLAG_HOST_WIDE | + GUEST_STATE_ELEMENT_TYPE_FLAG_READ_ONLY; + else if (type->id >= GSB_VCPU_LPVR) + /* 0x0003 - 0x07ff 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_HOST_WIDE) { + /* Hostwide elements cant be clubbed with other types */ + if (!(gsr->flags & GUEST_STATE_REQUEST_HOST_WIDE)) { + qemu_log_mask(LOG_GUEST_ERROR, "trying to get/set a host wide " + "Element ID:%04x.\n", id); + return false; + } + } else 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 get/set a guest wide " + "Element ID:%04x.\n", id); + return false; + } + } else { + /* thread wide element type */ + if (gsr->flags & (GUEST_STATE_REQUEST_GUEST_WIDE | + GUEST_STATE_REQUEST_HOST_WIDE)) { + qemu_log_mask(LOG_GUEST_ERROR, "trying to get/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(SpaprMachineState *spapr, + 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(spapr, 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, + SpaprMachineState *spapr, + 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(spapr, 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 = NULL; + + gsr.buf = buf; + assert(buflen <= GSB_MAX_BUF_SIZE); + gsr.len = buflen; + gsr.flags = 0; + + /* Works for both get/set state */ + if ((flags & H_GUEST_GET_STATE_FLAGS_GUEST_WIDE) || + (flags & H_GUEST_SET_STATE_FLAGS_GUEST_WIDE)) { + gsr.flags |= GUEST_STATE_REQUEST_GUEST_WIDE; + } + + if (set) { + if (flags & ~H_GUEST_SET_STATE_FLAGS_MASK) { + return H_PARAMETER; + } + gsr.flags |= GUEST_STATE_REQUEST_SET; + } else { + /* + * No reserved fields to be set in flags nor both + * GUEST/HOST wide bits + */ + if ((flags & ~H_GUEST_GET_STATE_FLAGS_MASK) || + (flags == H_GUEST_GET_STATE_FLAGS_MASK)) { + return H_PARAMETER; + } + + if (flags & H_GUEST_GET_STATE_FLAGS_HOST_WIDE) { + gsr.flags |= GUEST_STATE_REQUEST_HOST_WIDE; + } + } + + if (!(gsr.flags & GUEST_STATE_REQUEST_HOST_WIDE)) { + guest = spapr_get_nested_guest(spapr, lpid); + if (!guest) { + return H_P2; + } + } + return map_and_getset_state(cpu, spapr, 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(SpaprMachineState *spapr, + 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(spapr, guest, vcpuid, &gsr); + + address_space_unmap(CPU(cpu)->as, gsb, len, true, len); +} + +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(spapr, 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, spapr, 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(void) +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 a64098c375e..ea6762d3d2b 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 a8688243a6f..72b4a6329fb 100644 --- a/hw/ppc/spapr_nvdimm.c +++ b/hw/ppc/spapr_nvdimm.c @@ -235,8 +235,6 @@ void spapr_dt_persistent_memory(SpaprMachineState *spapr, void *fdt) spapr_dt_nvdimm(spapr, fdt, offset, nvdimm); } g_slist_free(nvdimms); - - return; } static target_ulong h_scm_read_metadata(PowerPCCPU *cpu, @@ -320,7 +318,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 +376,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; @@ -527,7 +526,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), @@ -540,7 +539,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, @@ -588,7 +587,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) { @@ -875,8 +874,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) @@ -884,22 +882,22 @@ static void spapr_nvdimm_unrealize(NVDIMMDevice *dimm) vmstate_unregister(NULL, &vmstate_spapr_nvdimm_states, dimm); } -static Property spapr_nvdimm_properties[] = { #ifdef CONFIG_LIBPMEM +static const Property spapr_nvdimm_properties[] = { DEFINE_PROP_BOOL("pmem-override", SpaprNVDIMMDevice, pmem_override, false), -#endif - DEFINE_PROP_END_OF_LIST(), }; +#endif -static void spapr_nvdimm_class_init(ObjectClass *oc, void *data) +static void spapr_nvdimm_class_init(ObjectClass *oc, const void *data) { - DeviceClass *dc = DEVICE_CLASS(oc); NVDIMMClass *nvc = NVDIMM_CLASS(oc); nvc->realize = spapr_nvdimm_realize; nvc->unrealize = spapr_nvdimm_unrealize; - device_class_set_props(dc, spapr_nvdimm_properties); +#ifdef CONFIG_LIBPMEM + device_class_set_props(DEVICE_CLASS(oc), spapr_nvdimm_properties); +#endif } static void spapr_nvdimm_init(Object *obj) diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c index b2567caa5cf..75ab4fe2623 100644 --- a/hw/ppc/spapr_ovec.c +++ b/hw/ppc/spapr_ovec.c @@ -15,7 +15,8 @@ #include "hw/ppc/spapr_ovec.h" #include "migration/vmstate.h" #include "qemu/bitmap.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" +#include "system/memory.h" #include "qemu/error-report.h" #include "trace.h" #include @@ -36,7 +37,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 75aacda65a1..1ac1185825e 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -34,22 +34,21 @@ #include "hw/pci/pci_host.h" #include "hw/ppc/spapr.h" #include "hw/pci-host/spapr.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include #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" #include "hw/pci/pci_ids.h" #include "hw/ppc/spapr_drc.h" #include "hw/qdev-properties.h" -#include "sysemu/device_tree.h" -#include "sysemu/kvm.h" -#include "sysemu/hostmem.h" -#include "sysemu/numa.h" +#include "system/device_tree.h" +#include "system/kvm.h" +#include "system/hostmem.h" +#include "system/numa.h" #include "hw/ppc/spapr_numa.h" #include "qemu/log.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--) { @@ -1288,8 +1283,7 @@ static void spapr_dt_pci_device_cb(PCIBus *bus, PCIDevice *pdev, PciWalkFdt *p = opaque; int err; - if (p->err) { - /* Something's already broken, don't keep going */ + if (p->err || !pdev->enabled) { return; } @@ -1443,8 +1437,6 @@ 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 (!IS_PCI_BRIDGE(dev)) { /* Properties only for non-bridges */ uint32_t min_grant = pci_default_read_config(dev, PCI_MIN_GNT, 1); @@ -1547,17 +1539,6 @@ static void spapr_pci_pre_plug(HotplugHandler *plug_handler, 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 (IS_PCI_BRIDGE(plugged_dev)) { if (!bridge_has_valid_chassis_nr(OBJECT(plugged_dev), errp)) { return; @@ -1568,7 +1549,9 @@ static void spapr_pci_pre_plug(HotplugHandler *plug_handler, * hotplug, we do not allow functions to be hotplugged to a * slot that already has function 0 present */ - if (plugged_dev->hotplugged && bus->devices[PCI_DEVFN(slotnr, 0)] && + if (plugged_dev->hotplugged && + !pci_is_vf(pdev) && + bus->devices[PCI_DEVFN(slotnr, 0)] && PCI_FUNC(pdev->devfn) != 0) { error_setg(errp, "PCI: slot %d function 0 already occupied by %s," " additional functions can no longer be exposed to guest.", @@ -1591,10 +1574,10 @@ static void spapr_pci_plug(HotplugHandler *plug_handler, 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 DR or the PCI device is disabled we don't need to do anything + * in the case of hotplug or coldplug callbacks. */ - if (!phb->dr_enabled) { + if (!pdev->enabled) { return; } @@ -1672,13 +1655,12 @@ 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))); + g_assert(drc); + + if (!drc->dev) { return; } - g_assert(drc); g_assert(drc->dev == plugged_dev); if (!spapr_drc_unplug_requested(drc)) { @@ -1757,8 +1739,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; @@ -1830,9 +1810,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; @@ -1848,30 +1828,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; @@ -1982,7 +1947,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); @@ -2069,14 +2034,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); @@ -2088,7 +2047,7 @@ static void spapr_phb_reset(DeviceState *qdev) g_hash_table_remove_all(sphb->msi); } -static Property spapr_phb_properties[] = { +static const Property spapr_phb_properties[] = { DEFINE_PROP_UINT32("index", SpaprPhbState, index, -1), DEFINE_PROP_UINT64("mem_win_size", SpaprPhbState, mem_win_size, SPAPR_PCI_MEM32_WIN_SIZE), @@ -2096,8 +2055,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), @@ -2108,22 +2065,17 @@ 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(), }; 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() @@ -2134,7 +2086,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), @@ -2149,20 +2101,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); @@ -2197,10 +2135,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); @@ -2210,13 +2147,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, @@ -2224,13 +2154,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), @@ -2248,7 +2173,7 @@ static const char *spapr_phb_root_bus_path(PCIHostState *host_bridge, return sphb->dtbusname; } -static void spapr_phb_class_init(ObjectClass *klass, void *data) +static void spapr_phb_class_init(ObjectClass *klass, const void *data) { PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -2258,7 +2183,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; @@ -2275,7 +2200,7 @@ static const TypeInfo spapr_phb_info = { .instance_size = sizeof(SpaprPhbState), .instance_finalize = spapr_phb_finalizefn, .class_init = spapr_phb_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } @@ -2362,7 +2287,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)); @@ -2443,12 +2367,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 2a8a11be1d6..00000000000 --- 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 d8aeee0b7e5..e318d0d912f 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -18,13 +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/pci/pci_device.h" -#include "hw/vfio/vfio.h" +#include "hw/vfio/vfio-container.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_address_space_get(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_address_space_put(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) { @@ -78,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 @@ -216,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 df5c4b96873..6fec6070370 100644 --- a/hw/ppc/spapr_rng.c +++ b/hw/ppc/spapr_rng.c @@ -22,8 +22,8 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" -#include "sysemu/device_tree.h" -#include "sysemu/rng.h" +#include "system/device_tree.h" +#include "system/rng.h" #include "hw/ppc/spapr.h" #include "hw/qdev-properties.h" #include "kvm_ppc.h" @@ -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); @@ -130,14 +130,13 @@ static void spapr_rng_realize(DeviceState *dev, Error **errp) } } -static Property spapr_rng_properties[] = { +static const Property spapr_rng_properties[] = { DEFINE_PROP_BOOL("use-kvm", SpaprRngState, use_kvm, false), DEFINE_PROP_LINK("rng", SpaprRngState, backend, TYPE_RNG_BACKEND, RngBackend *), - DEFINE_PROP_END_OF_LIST(), }; -static void spapr_rng_class_init(ObjectClass *oc, void *data) +static void spapr_rng_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 7df21581c21..78309dbb09d 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -28,17 +28,16 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/error-report.h" -#include "sysemu/sysemu.h" -#include "sysemu/device_tree.h" -#include "sysemu/cpus.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" -#include "sysemu/qtest.h" +#include "system/system.h" +#include "system/device_tree.h" +#include "system/cpus.h" +#include "system/hw_accel.h" +#include "system/runstate.h" +#include "system/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" @@ -111,7 +110,8 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, id = rtas_ld(args, 0); cpu = spapr_find_cpu(id); if (cpu != NULL) { - if (CPU(cpu)->halted) { + CPUPPCState *env = &cpu->env; + if (env->quiesced) { rtas_st(rets, 1, 0); } else { rtas_st(rets, 1, 2); @@ -216,6 +216,8 @@ static void rtas_stop_self(PowerPCCPU *cpu, SpaprMachineState *spapr, * For the same reason, set PSSCR_EC. */ env->spr[SPR_PSSCR] |= PSSCR_EC; + env->quiesced = true; /* set "RTAS stopped" state. */ + ppc_maybe_interrupt(env); cs->halted = 1; ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm); kvmppc_set_reg_ppc_online(cpu, 0); @@ -496,7 +498,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 { @@ -531,8 +533,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; @@ -566,7 +568,6 @@ static bool spapr_qtest_callback(CharBackend *chr, gchar **words) 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; diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c index d55b4b0c50d..1f7d2d8f898 100644 --- a/hw/ppc/spapr_rtc.c +++ b/hw/ppc/spapr_rtc.c @@ -27,8 +27,8 @@ #include "qemu/osdep.h" #include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "sysemu/rtc.h" +#include "system/system.h" +#include "system/rtc.h" #include "hw/ppc/spapr.h" #include "migration/vmstate.h" #include "qapi/error.h" @@ -157,13 +157,13 @@ 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() }, }; -static void spapr_rtc_class_init(ObjectClass *oc, void *data) +static void spapr_rtc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ppc/spapr_softmmu.c b/hw/ppc/spapr_softmmu.c deleted file mode 100644 index 278666317ef..00000000000 --- a/hw/ppc/spapr_softmmu.c +++ /dev/null @@ -1,615 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu/cutils.h" -#include "qemu/memalign.h" -#include "qemu/error-report.h" -#include "cpu.h" -#include "helper_regs.h" -#include "hw/ppc/spapr.h" -#include "mmu-hash64.h" -#include "mmu-book3s-v3.h" - - -static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex) -{ - /* - * 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_tpm_proxy.c b/hw/ppc/spapr_tpm_proxy.c index e10af35a185..1297b3ad569 100644 --- a/hw/ppc/spapr_tpm_proxy.c +++ b/hw/ppc/spapr_tpm_proxy.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/ppc/spapr.h" #include "hw/qdev-properties.h" #include "trace.h" @@ -41,8 +41,8 @@ static ssize_t tpm_execute(SpaprTpmProxy *tpm_proxy, target_ulong *args) target_ulong data_in_size = args[2]; uint64_t data_out = ppc64_phys_to_real(args[3]); target_ulong data_out_size = args[4]; - uint8_t buf_in[TPM_SPAPR_BUFSIZE]; - uint8_t buf_out[TPM_SPAPR_BUFSIZE]; + QEMU_UNINITIALIZED uint8_t buf_in[TPM_SPAPR_BUFSIZE]; + QEMU_UNINITIALIZED uint8_t buf_out[TPM_SPAPR_BUFSIZE]; ssize_t ret; trace_spapr_tpm_execute(data_in, data_in_size, data_out, data_out_size); @@ -145,12 +145,11 @@ static void spapr_tpm_proxy_unrealize(DeviceState *d) qemu_unregister_reset(spapr_tpm_proxy_reset, tpm_proxy); } -static Property spapr_tpm_proxy_properties[] = { +static const Property spapr_tpm_proxy_properties[] = { DEFINE_PROP_STRING("host-path", SpaprTpmProxy, host_path), - DEFINE_PROP_END_OF_LIST(), }; -static void spapr_tpm_proxy_class_init(ObjectClass *k, void *data) +static void spapr_tpm_proxy_class_init(ObjectClass *k, const void *data) { DeviceClass *dk = DEVICE_CLASS(k); diff --git a/hw/ppc/spapr_vhyp_mmu.c b/hw/ppc/spapr_vhyp_mmu.c new file mode 100644 index 00000000000..2d41d7f77b7 --- /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 9d4fec2c04d..7759436a4f5 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -27,8 +27,8 @@ #include "hw/loader.h" #include "elf.h" #include "hw/sysbus.h" -#include "sysemu/kvm.h" -#include "sysemu/device_tree.h" +#include "system/kvm.h" +#include "system/device_tree.h" #include "kvm_ppc.h" #include "migration/vmstate.h" @@ -50,7 +50,7 @@ static char *spapr_vio_get_dev_name(DeviceState *qdev) return g_strdup_printf("%s@%x", pc->dt_name, dev->reg); } -static void spapr_vio_bus_class_init(ObjectClass *klass, void *data) +static void spapr_vio_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); @@ -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); @@ -598,7 +599,7 @@ SpaprVioBus *spapr_vio_bus_init(void) return bus; } -static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data) +static void spapr_vio_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -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), @@ -630,11 +631,11 @@ const VMStateDescription vmstate_spapr_vio = { }, }; -static void vio_spapr_device_class_init(ObjectClass *klass, void *data) +static void vio_spapr_device_class_init(ObjectClass *klass, const 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 09f29be0b9d..46d78756e63 100644 --- a/hw/ppc/spapr_vof.c +++ b/hw/ppc/spapr_vof.c @@ -10,7 +10,7 @@ #include "hw/ppc/spapr_cpu_core.h" #include "hw/ppc/fdt.h" #include "hw/ppc/vof.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qom/qom-qobject.h" #include "trace.h" @@ -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 f670e8906cc..1f125ce8419 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 f2f81bd4259..c9969ae48a8 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -25,14 +25,15 @@ #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" +#include "system/system.h" +#include "system/reset.h" #include "hw/boards.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/loader.h" #include "elf.h" #include "qapi/error.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,9 +116,10 @@ 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); - env->gpr[6] = tswap32(EPAPR_MAGIC); + /* 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] = EPAPR_MAGIC; env->gpr[7] = bi->ima_size; } @@ -168,7 +146,7 @@ static int xilinx_load_device_tree(MachineState *machine, /* Try the local "ppc.dtb" override. */ fdt = load_device_tree("ppc.dtb", &fdt_size); if (!fdt) { - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); + path = qemu_find_file(QEMU_FILE_TYPE_DTB, BINARY_DEVICE_TREE_FILE); if (path) { fdt = load_device_tree(path, &fdt_size); g_free(path); @@ -239,6 +217,7 @@ static void virtex_init(MachineState *machine) cpu_irq = qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT); dev = qdev_new("xlnx.xps-intc"); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_BIG); qdev_prop_set_uint32(dev, "kind-of-intr", 0); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); @@ -252,6 +231,7 @@ static void virtex_init(MachineState *machine) /* 2 timers at irq 2 @ 62 Mhz. */ dev = qdev_new("xlnx.xps-timer"); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_BIG); qdev_prop_set_uint32(dev, "one-timer-only", 0); qdev_prop_set_uint32(dev, "clock-frequency", 62 * 1000000); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -264,8 +244,8 @@ static void virtex_init(MachineState *machine) /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &entry, NULL, &high, NULL, 1, PPC_ELF_MACHINE, - 0, 0); + &entry, NULL, &high, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); boot_info.bootstrap_pc = entry & 0x00ffffff; if (kernel_size < 0) { diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c index e3b430a81f4..f14efa3a7c8 100644 --- a/hw/ppc/vof.c +++ b/hw/ppc/vof.c @@ -15,10 +15,10 @@ #include "qemu/units.h" #include "qemu/log.h" #include "qapi/error.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/ppc/vof.h" #include "hw/ppc/fdt.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "qom/qom-qobject.h" #include "trace.h" @@ -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)); } diff --git a/hw/rdma/Kconfig b/hw/rdma/Kconfig deleted file mode 100644 index 840320bdc01..00000000000 --- a/hw/rdma/Kconfig +++ /dev/null @@ -1,3 +0,0 @@ -config VMW_PVRDMA - default y if PCI_DEVICES - depends on PVRDMA && MSI_NONBROKEN && VMXNET3_PCI diff --git a/hw/rdma/meson.build b/hw/rdma/meson.build deleted file mode 100644 index 363c9b8c83a..00000000000 --- a/hw/rdma/meson.build +++ /dev/null @@ -1,12 +0,0 @@ -system_ss.add(when: 'CONFIG_VMW_PVRDMA', if_true: files( - 'rdma.c', - 'rdma_backend.c', - 'rdma_utils.c', - 'vmw/pvrdma_qp_ops.c', -)) -specific_ss.add(when: 'CONFIG_VMW_PVRDMA', if_true: files( - 'rdma_rm.c', - 'vmw/pvrdma_cmd.c', - 'vmw/pvrdma_dev_ring.c', - 'vmw/pvrdma_main.c', -)) diff --git a/hw/rdma/rdma.c b/hw/rdma/rdma.c deleted file mode 100644 index 7bec0d0d2c7..00000000000 --- 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 6dcdfbbbe23..00000000000 --- 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 225af481e07..00000000000 --- 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 4e6c0ad695c..00000000000 --- 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 038d564433f..00000000000 --- a/hw/rdma/rdma_rm.c +++ /dev/null @@ -1,812 +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" - -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 d69a917795d..00000000000 --- 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 534f2f74d3d..00000000000 --- 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 c948baf052f..00000000000 --- a/hw/rdma/rdma_utils.c +++ /dev/null @@ -1,126 +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 "hw/pci/pci_device.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 54e4f56eddd..00000000000 --- a/hw/rdma/rdma_utils.h +++ /dev/null @@ -1,63 +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 "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 c23175120e1..00000000000 --- 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 b3fa8ebc51d..00000000000 --- 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 4cbc10c980b..00000000000 --- 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/msix.h" -#include "hw/pci/pci_device.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 c6ed0259821..00000000000 --- a/hw/rdma/vmw/pvrdma_cmd.c +++ /dev/null @@ -1,811 +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; - - memset(resp, 0, sizeof(*resp)); - return rdma_rm_alloc_pd(&dev->rdma_dev_res, &dev->backend_dev, - &resp->pd_handle, cmd->ctx_handle); -} - -static int destroy_pd(PVRDMADev *dev, union pvrdma_cmd_req *req, - 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 = 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 = 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; - - /* No need to verify sgid_index since it is u8 */ - - return rdma_rm_modify_qp(&dev->rdma_dev_res, &dev->backend_dev, - cmd->qp_handle, cmd->attr_mask, - cmd->attrs.ah_attr.grh.sgid_index, - (union ibv_gid *)&cmd->attrs.ah_attr.grh.dgid, - cmd->attrs.dest_qp_num, - (enum ibv_qp_state)cmd->attrs.qp_state, - cmd->attrs.qkey, cmd->attrs.rq_psn, - cmd->attrs.sq_psn); -} - -static int query_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, - 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; - - memset(resp, 0, sizeof(*resp)); - - return rdma_rm_query_qp(&dev->rdma_dev_res, &dev->backend_dev, - cmd->qp_handle, - (struct ibv_qp_attr *)&resp->attrs, - cmd->attr_mask, - &init_attr); -} - -static int destroy_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, - 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; - union ibv_gid *gid = (union ibv_gid *)&cmd->new_gid; - - if (cmd->index >= MAX_PORT_GIDS) { - return -EINVAL; - } - - return rdma_rm_add_gid(&dev->rdma_dev_res, &dev->backend_dev, - dev->backend_eth_device_name, gid, cmd->index); -} - -static int destroy_bind(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_destroy_bind *cmd = &req->destroy_bind; - - if (cmd->index >= MAX_PORT_GIDS) { - return -EINVAL; - } - - return rdma_rm_del_gid(&dev->rdma_dev_res, &dev->backend_dev, - dev->backend_eth_device_name, cmd->index); -} - -static int create_uc(PVRDMADev *dev, union pvrdma_cmd_req *req, - 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; - - memset(resp, 0, sizeof(*resp)); - return rdma_rm_alloc_uc(&dev->rdma_dev_res, cmd->pfn, &resp->ctx_handle); -} - -static int destroy_uc(PVRDMADev *dev, union pvrdma_cmd_req *req, - 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 = 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 30ce22a5be3..00000000000 --- a/hw/rdma/vmw/pvrdma_dev_ring.c +++ /dev/null @@ -1,141 +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 "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 d231588ce00..00000000000 --- 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 4fc67120256..00000000000 --- 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 c30c8344f67..00000000000 --- 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 = 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 = 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 = 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 bf2b15c5ce9..00000000000 --- 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 a6c77e1e10a..00000000000 --- 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 3ebc9fb7ad6..00000000000 --- 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 40dfee4bad4..988d3285ccc 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 1391dd712ce..3e0758a21ed 100644 --- a/hw/remote/iommu.c +++ b/hw/remote/iommu.c @@ -13,8 +13,8 @@ #include "hw/remote/iommu.h" #include "hw/pci/pci_bus.h" #include "hw/pci/pci.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" +#include "system/memory.h" +#include "system/address-spaces.h" #include "trace.h" /** @@ -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 fdc6c441bbd..e4b47838bad 100644 --- a/hw/remote/machine.c +++ b/hw/remote/machine.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "hw/remote/machine.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qapi/error.h" #include "hw/pci/pci_host.h" #include "hw/remote/iohub.h" @@ -121,7 +121,7 @@ static void remote_machine_dev_unplug_cb(HotplugHandler *hotplug_dev, } } -static void remote_machine_class_init(ObjectClass *oc, void *data) +static void remote_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); @@ -146,7 +146,7 @@ static const TypeInfo remote_machine = { .instance_size = sizeof(RemoteMachineState), .instance_init = remote_machine_instance_init, .class_init = remote_machine_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/remote/memory.c b/hw/remote/memory.c index 6d60da91e01..00193a552fa 100644 --- a/hw/remote/memory.c +++ b/hw/remote/memory.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "hw/remote/memory.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "qapi/error.h" static void remote_sysmem_reset(void) diff --git a/hw/remote/meson.build b/hw/remote/meson.build index a1e8708c732..41eb4971d98 100644 --- a/hw/remote/meson.build +++ b/hw/remote/meson.build @@ -7,9 +7,10 @@ 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')) diff --git a/hw/remote/message.c b/hw/remote/message.c index 50f6bf2d495..273f1e0323c 100644 --- a/hw/remote/message.c +++ b/hw/remote/message.c @@ -13,12 +13,12 @@ #include "io/channel.h" #include "hw/remote/mpqemu-link.h" #include "qapi/error.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/pci/pci.h" #include "exec/memattrs.h" #include "hw/remote/memory.h" #include "hw/remote/iohub.h" -#include "sysemu/reset.h" +#include "system/reset.h" static void process_config_write(QIOChannel *ioc, PCIDevice *dev, MPQemuMsg *msg, Error **errp); @@ -215,13 +215,10 @@ static void process_bar_read(QIOChannel *ioc, MPQemuMsg *msg, Error **errp) 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 9bd98e82197..49885a1db6e 100644 --- a/hw/remote/mpqemu-link.c +++ b/hw/remote/mpqemu-link.c @@ -17,7 +17,7 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "io/channel.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" #include "trace.h" /* @@ -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); + ret = qio_channel_readv_full_all_eof(ioc, &iov, 1, fds, nfds, 0, 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 a926f61ebe9..30ac74961dd 100644 --- a/hw/remote/proxy-memory-listener.c +++ b/hw/remote/proxy-memory-listener.c @@ -10,9 +10,9 @@ #include "qemu/int128.h" #include "qemu/range.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/cpu-common.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/remote/mpqemu-link.h" diff --git a/hw/remote/proxy.c b/hw/remote/proxy.c index 2052d721e5c..b0165aa2a1d 100644 --- a/hw/remote/proxy.c +++ b/hw/remote/proxy.c @@ -21,7 +21,7 @@ #include "hw/remote/proxy-memory-listener.h" #include "qom/object.h" #include "qemu/event_notifier.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" static void probe_pci_info(PCIDevice *dev, Error **errp); static void proxy_device_reset(DeviceState *dev); @@ -107,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; } @@ -134,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); @@ -194,12 +191,11 @@ static void pci_proxy_write_config(PCIDevice *d, uint32_t addr, uint32_t val, config_op_send(PCI_PROXY_DEV(d), addr, &val, len, MPQEMU_CMD_PCI_CFGWRITE); } -static Property proxy_properties[] = { +static const Property proxy_properties[] = { DEFINE_PROP_STRING("fd", PCIProxyDev, fd), - DEFINE_PROP_END_OF_LIST(), }; -static void pci_proxy_dev_class_init(ObjectClass *klass, void *data) +static void pci_proxy_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -209,7 +205,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); } @@ -219,7 +215,7 @@ static const TypeInfo pci_proxy_dev_type_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIProxyDev), .class_init = pci_proxy_dev_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/remote/remote-obj.c b/hw/remote/remote-obj.c index 65b6f7cc863..85882902d7f 100644 --- a/hw/remote/remote-obj.c +++ b/hw/remote/remote-obj.c @@ -17,7 +17,7 @@ #include "hw/remote/machine.h" #include "io/channel-util.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/pci/pci.h" #include "qemu/sockets.h" #include "monitor/monitor.h" @@ -49,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; @@ -162,7 +163,7 @@ static void remote_object_finalize(Object *obj) g_free(o->devid); } -static void remote_object_class_init(ObjectClass *klass, void *data) +static void remote_object_class_init(ObjectClass *klass, const void *data) { RemoteObjectClass *k = REMOTE_OBJECT_CLASS(klass); @@ -187,7 +188,7 @@ static const TypeInfo remote_object_info = { .instance_finalize = remote_object_finalize, .class_size = sizeof(RemoteObjectClass), .class_init = remote_object_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } 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 8b10c32a3c6..ea6165ebdce 100644 --- a/hw/remote/vfio-user-obj.c +++ b/hw/remote/vfio-user-obj.c @@ -43,7 +43,7 @@ #include "qom/object_interfaces.h" #include "qemu/error-report.h" #include "trace.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/boards.h" #include "hw/remote/machine.h" #include "qapi/error.h" @@ -52,12 +52,12 @@ #include "qemu/notify.h" #include "qemu/thread.h" #include "qemu/main-loop.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "libvfio-user.h" #include "hw/qdev-core.h" #include "hw/pci/pci.h" #include "qemu/timer.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/remote/vfio-user-obj.h" @@ -281,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); @@ -289,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; @@ -358,7 +358,7 @@ static int vfu_object_mr_rw(MemoryRegion *mr, uint8_t *buf, hwaddr offset, int access_size; uint64_t val; - if (memory_access_is_direct(mr, is_write)) { + if (memory_access_is_direct(mr, is_write, MEMTXATTRS_UNSPECIFIED)) { /** * Some devices expose a PCI expansion ROM, which could be buffer * based as compared to other regions which are primarily based on @@ -400,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; } @@ -917,7 +917,7 @@ static void vfu_object_finalize(Object *obj) } } -static void vfu_object_class_init(ObjectClass *klass, void *data) +static void vfu_object_class_init(ObjectClass *klass, const void *data) { VfuObjectClass *k = VFU_OBJECT_CLASS(klass); @@ -944,7 +944,7 @@ static const TypeInfo vfu_object_info = { .instance_finalize = vfu_object_finalize, .class_size = sizeof(VfuObjectClass), .class_init = vfu_object_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index b6a5eb4452e..e6a0ac1fa1d 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -1,3 +1,6 @@ +config RISCV_IOMMU + bool + config RISCV_NUMA bool @@ -8,7 +11,11 @@ config IBEX 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 @@ -18,36 +25,54 @@ config MICROCHIP_PFSOC select SIFIVE_PLIC select UNIMP +config MICROBLAZE_V + bool + default y + depends on RISCV32 || RISCV64 + select XILINX + select XILINX_AXI + select XILINX_ETHLITE + config OPENTITAN bool + default y + depends on RISCV32 select IBEX 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 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 @@ -55,6 +80,8 @@ config SHAKTI_C config SIFIVE_E bool + default y + depends on RISCV32 || RISCV64 select RISCV_ACLINT select SIFIVE_GPIO select SIFIVE_PLIC @@ -65,7 +92,11 @@ config SIFIVE_E config SIFIVE_U bool + default y + depends on RISCV32 || RISCV64 select CADENCE + select CPU_CLUSTER + select DEVICE_TREE select RISCV_ACLINT select SIFIVE_GPIO select SIFIVE_PDMA @@ -81,6 +112,9 @@ config SIFIVE_U config SPIKE bool + default y + depends on RISCV32 || RISCV64 + select DEVICE_TREE select RISCV_NUMA select HTIF select RISCV_ACLINT diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 52bf8e67de2..828a867be39 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -27,16 +27,17 @@ #include "hw/riscv/boot.h" #include "hw/riscv/boot_opensbi.h" #include "elf.h" -#include "sysemu/device_tree.h" -#include "sysemu/qtest.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" +#include "system/device_tree.h" +#include "system/qtest.h" +#include "system/kvm.h" +#include "system/reset.h" #include bool riscv_is_32bit(RISCVHartArrayState *harts) { - return harts->harts[0].env.misa_mxl_max == MXL_RV32; + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(&harts->harts[0]); + return mcc->def->misa_mxl_max == MXL_RV32; } /* @@ -66,9 +67,16 @@ char *riscv_plic_hart_config_string(int hart_count) return g_strjoinv(",", (char **)vals); } -target_ulong riscv_calc_kernel_start_addr(RISCVHartArrayState *harts, +void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts) +{ + info->kernel_size = 0; + info->initrd_size = 0; + info->is_32bit = riscv_is_32bit(harts); +} + +target_ulong riscv_calc_kernel_start_addr(RISCVBootInfo *info, target_ulong firmware_end_addr) { - if (riscv_is_32bit(harts)) { + if (info->is_32bit) { return QEMU_ALIGN_UP(firmware_end_addr, 4 * MiB); } else { return QEMU_ALIGN_UP(firmware_end_addr, 2 * MiB); @@ -127,11 +135,11 @@ char *riscv_find_firmware(const char *firmware_filename, 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 *firmware_filename; - target_ulong firmware_end_addr = firmware_load_addr; + target_ulong firmware_end_addr = *firmware_load_addr; firmware_filename = riscv_find_firmware(machine->firmware, default_machine_firmware); @@ -147,7 +155,7 @@ target_ulong riscv_find_and_load_firmware(MachineState *machine, } 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; @@ -158,22 +166,23 @@ target_ulong riscv_load_firmware(const char *firmware_filename, 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); } -static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry) +static void riscv_load_initrd(MachineState *machine, RISCVBootInfo *info) { const char *filename = machine->initrd_filename; uint64_t mem_size = machine->ram_size; @@ -188,13 +197,13 @@ static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry) * 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. + * 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, 128 * MiB); + start = info->image_low_addr + MIN(mem_size / 2, 512 * MiB); size = load_ramdisk(filename, start, mem_size - start); if (size == -1) { @@ -205,22 +214,25 @@ static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry) } } + info->initrd_start = start; + info->initrd_size = size; + /* Some RISC-V machines (e.g. opentitan) don't have a fdt. */ if (fdt) { end = start + size; - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", start); - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", end); + 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) +void riscv_load_kernel(MachineState *machine, + RISCVBootInfo *info, + 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; + ssize_t kernel_size; void *fdt = machine->fdt; g_assert(kernel_filename != NULL); @@ -232,21 +244,29 @@ target_ulong riscv_load_kernel(MachineState *machine, * the (expected) load address load address. This allows kernels to have * separate SBI and ELF entry points (used by FreeBSD, for example). */ - 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) { - kernel_entry = kernel_load_base; + kernel_size = load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, NULL, + &info->image_low_addr, &info->image_high_addr, + NULL, ELFDATA2LSB, EM_RISCV, + 1, 0, NULL, true, sym_cb); + if (kernel_size > 0) { + info->kernel_size = kernel_size; goto out; } - if (load_uimage_as(kernel_filename, &kernel_entry, NULL, NULL, - NULL, NULL, NULL) > 0) { + kernel_size = load_uimage_as(kernel_filename, &info->image_low_addr, + NULL, NULL, NULL, NULL, NULL); + if (kernel_size > 0) { + info->kernel_size = kernel_size; + info->image_high_addr = info->image_low_addr + kernel_size; goto out; } - if (load_image_targphys_as(kernel_filename, kernel_start_addr, - current_machine->ram_size, NULL) > 0) { - kernel_entry = kernel_start_addr; + kernel_size = load_image_targphys_as(kernel_filename, kernel_start_addr, + current_machine->ram_size, NULL); + if (kernel_size > 0) { + info->kernel_size = kernel_size; + info->image_low_addr = kernel_start_addr; + info->image_high_addr = info->image_low_addr + kernel_size; goto out; } @@ -255,23 +275,21 @@ target_ulong riscv_load_kernel(MachineState *machine, out: /* - * For 32 bit CPUs 'kernel_entry' can be sign-extended by + * For 32 bit CPUs 'image_low_addr' can be sign-extended by * load_elf_ram_sym(). */ - if (riscv_is_32bit(harts)) { - kernel_entry = extract64(kernel_entry, 0, 32); + if (info->is_32bit) { + info->image_low_addr = extract64(info->image_low_addr, 0, 32); } if (load_initrd && machine->initrd_filename) { - riscv_load_initrd(machine, kernel_entry); + riscv_load_initrd(machine, info); } if (fdt && machine->kernel_cmdline && *machine->kernel_cmdline) { qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", machine->kernel_cmdline); } - - return kernel_entry; } /* @@ -291,11 +309,12 @@ target_ulong riscv_load_kernel(MachineState *machine, * The FDT is fdt_packed() during the calculation. */ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, - MachineState *ms) + MachineState *ms, RISCVBootInfo *info) { int ret = fdt_pack(ms->fdt); hwaddr dram_end, temp; int fdtsize; + uint64_t dtb_start, dtb_start_limit; /* Should only fail if we've built a corrupted tree */ g_assert(ret == 0); @@ -306,6 +325,17 @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, exit(1); } + if (info->initrd_size) { + /* If initrd is successfully loaded, place DTB after it. */ + dtb_start_limit = info->initrd_start + info->initrd_size; + } else if (info->kernel_size) { + /* If only kernel is successfully loaded, place DTB after it. */ + dtb_start_limit = info->image_high_addr; + } else { + /* Otherwise, do not check DTB overlapping */ + dtb_start_limit = 0; + } + /* * A dram_size == 0, usually from a MemMapEntry[].size element, * means that the DRAM block goes all the way to ms->ram_size. @@ -315,13 +345,24 @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, /* * We should put fdt as far as possible to avoid kernel/initrd overwriting - * its content. But it should be addressable by 32 bit system as well. - * Thus, put it at an 2MB aligned address that less than fdt size from the - * end of dram or 3GB whichever is lesser. + * its content. But it should be addressable by 32 bit system as well in RV32. + * Thus, put it near to the end of dram in RV64, and put it near to the end + * of dram or 3GB whichever is lesser in RV32. */ - temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end; + if (!info->is_32bit) { + temp = dram_end; + } else { + temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end; + } - return QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB); + dtb_start = QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB); + + if (dtb_start_limit && (dtb_start < dtb_start_limit)) { + error_report("No enough memory to place DTB after kernel/initrd"); + exit(1); + } + + return dtb_start; } /* @@ -333,35 +374,39 @@ void riscv_load_fdt(hwaddr fdt_addr, void *fdt) uint32_t fdtsize = fdt_totalsize(fdt); /* copy in the device tree */ - qemu_fdt_dumpdtb(fdt, fdtsize); - rom_add_blob_fixed_as("fdt", fdt, fdtsize, fdt_addr, &address_space_memory); qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, rom_ptr_for_as(&address_space_memory, fdt_addr, fdtsize)); } -void riscv_rom_copy_firmware_info(MachineState *machine, hwaddr rom_base, - 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 @@ -373,7 +418,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); } @@ -414,7 +462,7 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts reset_vec[4] = 0x0182b283; /* ld t0, 24(t0) */ } - if (!harts->harts[0].cfg.ext_icsr) { + 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 @@ -429,7 +477,9 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts } 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 2f7ee81be3c..c22f3a72162 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')) @@ -10,5 +10,8 @@ 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', 'riscv-iommu-sys.c', 'riscv-iommu-hpm.c')) +riscv_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: files('microblaze-v-generic.c')) hw_arch += {'riscv': riscv_ss} diff --git a/hw/riscv/microblaze-v-generic.c b/hw/riscv/microblaze-v-generic.c new file mode 100644 index 00000000000..e863c50cbc9 --- /dev/null +++ b/hw/riscv/microblaze-v-generic.c @@ -0,0 +1,189 @@ +/* + * QEMU model of Microblaze V generic board. + * + * based on hw/microblaze/petalogix_ml605_mmu.c + * + * Copyright (c) 2011 Michal Simek + * Copyright (c) 2011 PetaLogix + * Copyright (c) 2009 Edgar E. Iglesias. + * Copyright (C) 2024, Advanced Micro Devices, Inc. + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Written by Sai Pavan Boddu . + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "cpu.h" +#include "hw/sysbus.h" +#include "system/system.h" +#include "net/net.h" +#include "hw/boards.h" +#include "hw/char/serial-mm.h" +#include "system/address-spaces.h" +#include "hw/char/xilinx_uartlite.h" +#include "hw/misc/unimp.h" + +#define LMB_BRAM_SIZE (128 * KiB) +#define MEMORY_BASEADDR 0x80000000 +#define INTC_BASEADDR 0x41200000 +#define TIMER_BASEADDR 0x41c00000 +#define TIMER_BASEADDR2 0x41c10000 +#define UARTLITE_BASEADDR 0x40600000 +#define ETHLITE_BASEADDR 0x40e00000 +#define UART16550_BASEADDR 0x44a10000 +#define AXIENET_BASEADDR 0x40c00000 +#define AXIDMA_BASEADDR 0x41e00000 +#define GPIO_BASEADDR 0x40000000 +#define GPIO_BASEADDR2 0x40010000 +#define GPIO_BASEADDR3 0x40020000 +#define I2C_BASEADDR 0x40800000 +#define QSPI_BASEADDR 0x44a00000 + +#define TIMER_IRQ 0 +#define UARTLITE_IRQ 1 +#define UART16550_IRQ 4 +#define ETHLITE_IRQ 5 +#define TIMER_IRQ2 6 +#define AXIENET_IRQ 7 +#define AXIDMA_IRQ1 8 +#define AXIDMA_IRQ0 9 + +static void mb_v_generic_init(MachineState *machine) +{ + ram_addr_t ram_size = machine->ram_size; + DeviceState *dev, *dma, *eth0; + Object *ds, *cs; + int i; + RISCVCPU *cpu; + hwaddr ddr_base = MEMORY_BASEADDR; + MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1); + MemoryRegion *phys_ram = g_new(MemoryRegion, 1); + qemu_irq irq[32]; + MemoryRegion *sysmem = get_system_memory(); + + cpu = RISCV_CPU(object_new(machine->cpu_type)); + object_property_set_bool(OBJECT(cpu), "h", false, NULL); + object_property_set_bool(OBJECT(cpu), "d", false, NULL); + qdev_realize(DEVICE(cpu), NULL, &error_abort); + /* Attach emulated BRAM through the LMB. */ + memory_region_init_ram(phys_lmb_bram, NULL, + "mb_v.lmb_bram", LMB_BRAM_SIZE, + &error_fatal); + memory_region_add_subregion(sysmem, 0x00000000, phys_lmb_bram); + + memory_region_init_ram(phys_ram, NULL, "mb_v.ram", + ram_size, &error_fatal); + memory_region_add_subregion(sysmem, ddr_base, phys_ram); + + dev = qdev_new("xlnx.xps-intc"); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); + qdev_prop_set_uint32(dev, "kind-of-intr", + 1 << UARTLITE_IRQ); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, + qdev_get_gpio_in(DEVICE(cpu), 11)); + for (i = 0; i < 32; i++) { + irq[i] = qdev_get_gpio_in(dev, i); + } + + /* Uartlite */ + dev = qdev_new(TYPE_XILINX_UARTLITE); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); + 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]); + + /* Full uart */ + serial_mm_init(sysmem, UART16550_BASEADDR + 0x1000, 2, + irq[UART16550_IRQ], 115200, serial_hd(1), + DEVICE_LITTLE_ENDIAN); + + /* 2 timers at irq 0 @ 100 Mhz. */ + dev = qdev_new("xlnx.xps-timer"); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); + qdev_prop_set_uint32(dev, "one-timer-only", 0); + qdev_prop_set_uint32(dev, "clock-frequency", 100000000); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]); + + /* 2 timers at irq 3 @ 100 Mhz. */ + dev = qdev_new("xlnx.xps-timer"); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); + qdev_prop_set_uint32(dev, "one-timer-only", 0); + qdev_prop_set_uint32(dev, "clock-frequency", 100000000); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR2); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ2]); + + /* Emaclite */ + dev = qdev_new("xlnx.xps-ethernetlite"); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); + 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]); + + /* axi ethernet and dma initialization. */ + eth0 = qdev_new("xlnx.axi-ethernet"); + dma = qdev_new("xlnx.axi-dma"); + + /* FIXME: attach to the sysbus instead */ + object_property_add_child(qdev_get_machine(), "xilinx-eth", OBJECT(eth0)); + object_property_add_child(qdev_get_machine(), "xilinx-dma", OBJECT(dma)); + + ds = object_property_get_link(OBJECT(dma), + "axistream-connected-target", NULL); + cs = object_property_get_link(OBJECT(dma), + "axistream-control-connected-target", NULL); + 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, + &error_abort); + object_property_set_link(OBJECT(eth0), "axistream-control-connected", cs, + &error_abort); + sysbus_realize_and_unref(SYS_BUS_DEVICE(eth0), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(eth0), 0, AXIENET_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(eth0), 0, irq[AXIENET_IRQ]); + + ds = object_property_get_link(OBJECT(eth0), + "axistream-connected-target", NULL); + cs = object_property_get_link(OBJECT(eth0), + "axistream-control-connected-target", NULL); + qdev_prop_set_uint32(dma, "freqhz", 100000000); + object_property_set_link(OBJECT(dma), "axistream-connected", ds, + &error_abort); + object_property_set_link(OBJECT(dma), "axistream-control-connected", cs, + &error_abort); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dma), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dma), 0, AXIDMA_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dma), 0, irq[AXIDMA_IRQ0]); + sysbus_connect_irq(SYS_BUS_DEVICE(dma), 1, irq[AXIDMA_IRQ1]); + + /* unimplemented devices */ + create_unimplemented_device("gpio", GPIO_BASEADDR, 0x10000); + create_unimplemented_device("gpio2", GPIO_BASEADDR2, 0x10000); + create_unimplemented_device("gpio3", GPIO_BASEADDR3, 0x10000); + create_unimplemented_device("i2c", I2C_BASEADDR, 0x10000); + create_unimplemented_device("qspi", QSPI_BASEADDR, 0x10000); +} + +static void mb_v_generic_machine_init(MachineClass *mc) +{ + mc->desc = "AMD Microblaze-V generic platform"; + mc->init = mb_v_generic_init; + mc->min_cpus = 1; + mc->max_cpus = 1; + mc->default_cpu_type = TYPE_RISCV_CPU_BASE; + mc->default_cpus = 1; +} + +DEFINE_MACHINE("amd-microblaze-v-generic", mb_v_generic_machine_init) diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index b775aa89464..2e74783fce2 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -39,6 +39,7 @@ #include "qemu/units.h" #include "qemu/cutils.h" #include "qapi/error.h" +#include "qapi/visitor.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/sysbus.h" @@ -51,8 +52,8 @@ #include "hw/riscv/microchip_pfsoc.h" #include "hw/intc/riscv_aclint.h" #include "hw/intc/sifive_plic.h" -#include "sysemu/device_tree.h" -#include "sysemu/sysemu.h" +#include "system/device_tree.h" +#include "system/system.h" /* * The BIOS image used by this machine is called Hart Software Services (HSS). @@ -61,9 +62,6 @@ #define BIOS_FILENAME "hss.bin" #define RESET_VECTOR 0x20220000 -/* CLINT timebase frequency */ -#define CLINT_TIMEBASE_FREQ 1000000 - /* GEM version */ #define GEM_REVISION 0x0107010c @@ -193,6 +191,7 @@ static void microchip_pfsoc_soc_instance_init(Object *obj) static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) { MachineState *ms = MACHINE(qdev_get_machine()); + MicrochipIcicleKitState *iks = MICROCHIP_ICICLE_KIT_MACHINE(ms); MicrochipPFSoCState *s = MICROCHIP_PFSOC(dev); const MemMapEntry *memmap = microchip_pfsoc_memmap; MemoryRegion *system_memory = get_system_memory(); @@ -202,7 +201,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); @@ -254,7 +252,7 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) memmap[MICROCHIP_PFSOC_CLINT].base + RISCV_ACLINT_SWI_SIZE, RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus, RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME, - CLINT_TIMEBASE_FREQ, false); + iks->clint_timebase_freq, false); /* L2 cache controller */ create_unimplemented_device("microchip.pfsoc.l2cc", @@ -411,17 +409,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); @@ -489,7 +478,7 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) qspi_xip_mem); } -static void microchip_pfsoc_soc_class_init(ObjectClass *oc, void *data) +static void microchip_pfsoc_soc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -526,11 +515,11 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) uint64_t mem_low_size, mem_high_size; hwaddr firmware_load_addr; const char *firmware_name; - bool kernel_as_payload = false; target_ulong firmware_end_addr, kernel_start_addr; uint64_t kernel_entry; - uint32_t fdt_load_addr; + uint64_t fdt_load_addr; DriveInfo *dinfo = drive_get(IF_SD, 0, 0); + RISCVBootInfo boot_info; /* Sanity check on RAM size */ if (machine->ram_size < mc->default_ram_size) { @@ -588,65 +577,135 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) } /* - * We follow the following table to select which payload we execute. + * We follow the following table to select which firmware we use. * - * -bios | -kernel | payload - * -------+------------+-------- - * N | N | HSS - * Y | don't care | HSS - * N | Y | kernel - * - * This ensures backwards compatibility with how we used to expose -bios - * to users but allows them to run through direct kernel booting as well. - * - * When -kernel is used for direct boot, -dtb must be present to provide - * a valid device tree for the board, as we don't generate device tree. + * -bios | -kernel | firmware + * --------------+------------+-------- + * none | N | error + * none | Y | kernel + * NULL, default | N | BIOS_FILENAME + * NULL, default | Y | RISCV64_BIOS_BIN + * other | don't care | other */ - - if (machine->kernel_filename && machine->dtb) { - int fdt_size; - machine->fdt = load_device_tree(machine->dtb, &fdt_size); - if (!machine->fdt) { - error_report("load_device_tree() failed"); + if (machine->firmware && !strcmp(machine->firmware, "none")) { + if (!machine->kernel_filename) { + error_report("for -bios none, a kernel is required"); exit(1); } - firmware_name = RISCV64_BIOS_BIN; - firmware_load_addr = memmap[MICROCHIP_PFSOC_DRAM_LO].base; - kernel_as_payload = true; - } - - if (!kernel_as_payload) { - firmware_name = BIOS_FILENAME; + firmware_name = NULL; + firmware_load_addr = RESET_VECTOR; + } else if (!machine->firmware || !strcmp(machine->firmware, "default")) { + if (machine->kernel_filename) { + firmware_name = RISCV64_BIOS_BIN; + firmware_load_addr = memmap[MICROCHIP_PFSOC_DRAM_LO].base; + } else { + firmware_name = BIOS_FILENAME; + firmware_load_addr = RESET_VECTOR; + } + } else { + firmware_name = machine->firmware; firmware_load_addr = RESET_VECTOR; } - /* Load the firmware */ - firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, - firmware_load_addr, NULL); + /* Load the firmware if necessary */ + firmware_end_addr = firmware_load_addr; + if (firmware_name) { + char *filename = riscv_find_firmware(firmware_name, NULL); + if (filename) { + firmware_end_addr = riscv_load_firmware(filename, + &firmware_load_addr, NULL); + g_free(filename); + } + } - if (kernel_as_payload) { - kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc.u_cpus, + riscv_boot_info_init(&boot_info, &s->soc.u_cpus); + if (machine->kernel_filename) { + kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); - kernel_entry = riscv_load_kernel(machine, &s->soc.u_cpus, - kernel_start_addr, true, NULL); + riscv_load_kernel(machine, &boot_info, kernel_start_addr, + true, NULL); + kernel_entry = boot_info.image_low_addr; + + if (machine->dtb) { + int fdt_size; + machine->fdt = load_device_tree(machine->dtb, &fdt_size); + if (!machine->fdt) { + error_report("load_device_tree() failed"); + exit(1); + } + + /* Compute the FDT load address in DRAM */ + hwaddr kernel_ram_base = memmap[MICROCHIP_PFSOC_DRAM_LO].base; + hwaddr kernel_ram_size = memmap[MICROCHIP_PFSOC_DRAM_LO].size; + + if (kernel_entry - kernel_ram_base >= kernel_ram_size) { + kernel_ram_base = memmap[MICROCHIP_PFSOC_DRAM_HI].base; + kernel_ram_size = mem_high_size; + } + + fdt_load_addr = riscv_compute_fdt_addr(kernel_ram_base, kernel_ram_size, + machine, &boot_info); + riscv_load_fdt(fdt_load_addr, machine->fdt); + } else { + warn_report_once("The QEMU microchip-icicle-kit machine does not " + "generate a device tree, so no device tree is " + "being provided to the guest."); + fdt_load_addr = 0; + } - /* Compute the fdt load address in dram */ - 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); + hwaddr start_addr; + if (firmware_name) { + start_addr = firmware_load_addr; + } else { + start_addr = kernel_entry; + } /* Load the reset vector */ - riscv_setup_rom_reset_vec(machine, &s->soc.u_cpus, firmware_load_addr, + riscv_setup_rom_reset_vec(machine, &s->soc.u_cpus, start_addr, memmap[MICROCHIP_PFSOC_ENVM_DATA].base, memmap[MICROCHIP_PFSOC_ENVM_DATA].size, kernel_entry, fdt_load_addr); } } -static void microchip_icicle_kit_machine_class_init(ObjectClass *oc, void *data) +static void microchip_icicle_kit_set_clint_timebase_freq(Object *obj, + Visitor *v, + const char *name, + void *opaque, + Error **errp) +{ + MicrochipIcicleKitState *s = MICROCHIP_ICICLE_KIT_MACHINE(obj); + uint32_t value; + + if (!visit_type_uint32(v, name, &value, errp)) { + return; + } + + s->clint_timebase_freq = value; +} + +static void microchip_icicle_kit_get_clint_timebase_freq(Object *obj, + Visitor *v, + const char *name, + void *opaque, + Error **errp) +{ + MicrochipIcicleKitState *s = MICROCHIP_ICICLE_KIT_MACHINE(obj); + uint32_t value = s->clint_timebase_freq; + + visit_type_uint32(v, name, &value, errp); +} + +static void microchip_icicle_kit_machine_instance_init(Object *obj) +{ + MicrochipIcicleKitState *m = MICROCHIP_ICICLE_KIT_MACHINE(obj); + m->clint_timebase_freq = 1000000; +} + +static void microchip_icicle_kit_machine_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -657,6 +716,7 @@ static void microchip_icicle_kit_machine_class_init(ObjectClass *oc, void *data) mc->min_cpus = MICROCHIP_PFSOC_MANAGEMENT_CPU_COUNT + 1; mc->default_cpus = mc->min_cpus; mc->default_ram_id = "microchip.icicle.kit.ram"; + mc->auto_create_sdcard = true; /* * Map 513 MiB high memory, the minimum required high memory size, because @@ -666,12 +726,20 @@ static void microchip_icicle_kit_machine_class_init(ObjectClass *oc, void *data) * See memory_tests() in mss_ddr.c in the HSS source code. */ mc->default_ram_size = 1537 * MiB; + + object_class_property_add(oc, "clint-timebase-frequency", "uint32_t", + microchip_icicle_kit_get_clint_timebase_freq, + microchip_icicle_kit_set_clint_timebase_freq, + NULL, NULL); + object_class_property_set_description(oc, "clint-timebase-frequency", + "Set CLINT timebase frequency in Hz."); } static const TypeInfo microchip_icicle_kit_machine_typeinfo = { .name = MACHINE_TYPE_NAME("microchip-icicle-kit"), .parent = TYPE_MACHINE, .class_init = microchip_icicle_kit_machine_class_init, + .instance_init = microchip_icicle_kit_machine_instance_init, .instance_size = sizeof(MicrochipIcicleKitState), }; diff --git a/hw/riscv/numa.c b/hw/riscv/numa.c index d319aefb451..7a7b0120077 100644 --- a/hw/riscv/numa.c +++ b/hw/riscv/numa.c @@ -23,7 +23,7 @@ #include "hw/boards.h" #include "hw/qdev-properties.h" #include "hw/riscv/numa.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" static bool numa_enabled(const MachineState *ms) { @@ -167,7 +167,8 @@ void riscv_socket_fdt_write_id(const MachineState *ms, const char *node_name, 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); @@ -189,7 +190,6 @@ void riscv_socket_fdt_write_distance_matrix(const MachineState *ms) "numa-distance-map-v1"); qemu_fdt_setprop(ms->fdt, "/distance-map", "distance-matrix", dist_matrix, dist_matrix_size); - g_free(dist_matrix); } } diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 6a2fcc4ade3..d369a8a7dcd 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -27,7 +27,8 @@ #include "hw/misc/unimp.h" #include "hw/riscv/boot.h" #include "qemu/units.h" -#include "sysemu/sysemu.h" +#include "system/system.h" +#include "system/address-spaces.h" /* * This version of the OpenTitan machine currently supports @@ -81,6 +82,7 @@ static void opentitan_machine_init(MachineState *machine) OpenTitanState *s = OPENTITAN_MACHINE(machine); const MemMapEntry *memmap = ibex_memmap; MemoryRegion *sys_mem = get_system_memory(); + RISCVBootInfo boot_info; if (machine->ram_size != mc->default_ram_size) { char *sz = size_to_str(mc->default_ram_size); @@ -98,17 +100,19 @@ static void opentitan_machine_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); } + riscv_boot_info_init(&boot_info, &s->soc.cpus); if (machine->kernel_filename) { - riscv_load_kernel(machine, &s->soc.cpus, + riscv_load_kernel(machine, &boot_info, memmap[IBEX_DEV_RAM].base, false, NULL); } } -static void opentitan_machine_class_init(ObjectClass *oc, void *data) +static void opentitan_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -227,7 +231,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; @@ -305,12 +309,11 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) memmap[IBEX_DEV_IBEX_CFG].base, memmap[IBEX_DEV_IBEX_CFG].size); } -static Property lowrisc_ibex_soc_props[] = { +static const Property lowrisc_ibex_soc_props[] = { DEFINE_PROP_UINT32("resetvec", LowRISCIbexSoCState, resetvec, 0x20000400), - DEFINE_PROP_END_OF_LIST() }; -static void lowrisc_ibex_soc_class_init(ObjectClass *oc, void *data) +static void lowrisc_ibex_soc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/riscv/riscv-iommu-bits.h b/hw/riscv/riscv-iommu-bits.h new file mode 100644 index 00000000000..1017d73fc6e --- /dev/null +++ b/hw/riscv/riscv-iommu-bits.h @@ -0,0 +1,468 @@ +/* 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_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) + +/* 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_HPM BIT_ULL(30) +#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) + +enum riscv_iommu_igs_modes { + RISCV_IOMMU_CAP_IGS_MSI = 0, + RISCV_IOMMU_CAP_IGS_WSI, + RISCV_IOMMU_CAP_IGS_BOTH +}; + +/* 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 +}; + +#define RISCV_IOMMU_IOCOUNT_NUM 31 + +/* 5.19 Performance monitoring counter overflow status (32bits) */ +#define RISCV_IOMMU_REG_IOCOUNTOVF 0x0058 +#define RISCV_IOMMU_IOCOUNTOVF_CY BIT(0) + +/* 5.20 Performance monitoring counter inhibits (32bits) */ +#define RISCV_IOMMU_REG_IOCOUNTINH 0x005C +#define RISCV_IOMMU_IOCOUNTINH_CY BIT(0) + +/* 5.21 Performance monitoring cycles counter (64bits) */ +#define RISCV_IOMMU_REG_IOHPMCYCLES 0x0060 +#define RISCV_IOMMU_IOHPMCYCLES_COUNTER GENMASK_ULL(62, 0) +#define RISCV_IOMMU_IOHPMCYCLES_OVF BIT_ULL(63) + +/* 5.22 Performance monitoring event counters (31 * 64bits) */ +#define RISCV_IOMMU_REG_IOHPMCTR_BASE 0x0068 +#define RISCV_IOMMU_REG_IOHPMCTR(_n) \ + (RISCV_IOMMU_REG_IOHPMCTR_BASE + (_n * 0x8)) + +/* 5.23 Performance monitoring event selectors (31 * 64bits) */ +#define RISCV_IOMMU_REG_IOHPMEVT_BASE 0x0160 +#define RISCV_IOMMU_REG_IOHPMEVT(_n) \ + (RISCV_IOMMU_REG_IOHPMEVT_BASE + (_n * 0x8)) +#define RISCV_IOMMU_IOHPMEVT_EVENT_ID GENMASK_ULL(14, 0) +#define RISCV_IOMMU_IOHPMEVT_DMASK BIT_ULL(15) +#define RISCV_IOMMU_IOHPMEVT_PID_PSCID GENMASK_ULL(35, 16) +#define RISCV_IOMMU_IOHPMEVT_DID_GSCID GENMASK_ULL(59, 36) +#define RISCV_IOMMU_IOHPMEVT_PV_PSCV BIT_ULL(60) +#define RISCV_IOMMU_IOHPMEVT_DV_GSCV BIT_ULL(61) +#define RISCV_IOMMU_IOHPMEVT_IDT BIT_ULL(62) +#define RISCV_IOMMU_IOHPMEVT_OF BIT_ULL(63) + +enum RISCV_IOMMU_HPMEVENT_id { + RISCV_IOMMU_HPMEVENT_INVALID = 0, + RISCV_IOMMU_HPMEVENT_URQ = 1, + RISCV_IOMMU_HPMEVENT_TRQ = 2, + RISCV_IOMMU_HPMEVENT_ATS_RQ = 3, + RISCV_IOMMU_HPMEVENT_TLB_MISS = 4, + RISCV_IOMMU_HPMEVENT_DD_WALK = 5, + RISCV_IOMMU_HPMEVENT_PD_WALK = 6, + RISCV_IOMMU_HPMEVENT_S_VS_WALKS = 7, + RISCV_IOMMU_HPMEVENT_G_WALKS = 8, + RISCV_IOMMU_HPMEVENT_MAX = 9 +}; + +/* 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 + +/* 2.2 Process Directory Table */ +#define RISCV_IOMMU_PDTE_VALID BIT_ULL(0) +#define RISCV_IOMMU_PDTE_PPN RISCV_IOMMU_PPN_FIELD + +/* 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 RISCV_IOMMU_ATP_PPN_FIELD +#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, +}; + +/* + * 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-hpm.c b/hw/riscv/riscv-iommu-hpm.c new file mode 100644 index 00000000000..c5034bff795 --- /dev/null +++ b/hw/riscv/riscv-iommu-hpm.c @@ -0,0 +1,381 @@ +/* + * RISC-V IOMMU - Hardware Performance Monitor (HPM) helpers + * + * 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 "qemu/timer.h" +#include "cpu_bits.h" +#include "riscv-iommu-hpm.h" +#include "riscv-iommu.h" +#include "riscv-iommu-bits.h" +#include "trace.h" + +/* For now we assume IOMMU HPM frequency to be 1GHz so 1-cycle is of 1-ns. */ +static inline uint64_t get_cycles(void) +{ + return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +} + +uint64_t riscv_iommu_hpmcycle_read(RISCVIOMMUState *s) +{ + const uint64_t cycle = riscv_iommu_reg_get64( + s, RISCV_IOMMU_REG_IOHPMCYCLES); + const uint32_t inhibit = riscv_iommu_reg_get32( + s, RISCV_IOMMU_REG_IOCOUNTINH); + const uint64_t ctr_prev = s->hpmcycle_prev; + const uint64_t ctr_val = s->hpmcycle_val; + + trace_riscv_iommu_hpm_read(cycle, inhibit, ctr_prev, ctr_val); + + if (get_field(inhibit, RISCV_IOMMU_IOCOUNTINH_CY)) { + /* + * Counter should not increment if inhibit bit is set. We can't really + * stop the QEMU_CLOCK_VIRTUAL, so we just return the last updated + * counter value to indicate that counter was not incremented. + */ + return (ctr_val & RISCV_IOMMU_IOHPMCYCLES_COUNTER) | + (cycle & RISCV_IOMMU_IOHPMCYCLES_OVF); + } + + return (ctr_val + get_cycles() - ctr_prev) | + (cycle & RISCV_IOMMU_IOHPMCYCLES_OVF); +} + +static void hpm_incr_ctr(RISCVIOMMUState *s, uint32_t ctr_idx) +{ + const uint32_t off = ctr_idx << 3; + uint64_t cntr_val; + + cntr_val = ldq_le_p(&s->regs_rw[RISCV_IOMMU_REG_IOHPMCTR_BASE + off]); + stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_IOHPMCTR_BASE + off], cntr_val + 1); + + trace_riscv_iommu_hpm_incr_ctr(cntr_val); + + /* Handle the overflow scenario. */ + if (cntr_val == UINT64_MAX) { + /* + * Generate interrupt only if OF bit is clear. +1 to offset the cycle + * register OF bit. + */ + const uint32_t ovf = + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IOCOUNTOVF, + BIT(ctr_idx + 1), 0); + if (!get_field(ovf, BIT(ctr_idx + 1))) { + riscv_iommu_reg_mod64(s, + RISCV_IOMMU_REG_IOHPMEVT_BASE + off, + RISCV_IOMMU_IOHPMEVT_OF, + 0); + riscv_iommu_notify(s, RISCV_IOMMU_INTR_PM); + } + } +} + +void riscv_iommu_hpm_incr_ctr(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, + unsigned event_id) +{ + const uint32_t inhibit = riscv_iommu_reg_get32( + s, RISCV_IOMMU_REG_IOCOUNTINH); + uint32_t did_gscid; + uint32_t pid_pscid; + uint32_t ctr_idx; + gpointer value; + uint32_t ctrs; + uint64_t evt; + + if (!(s->cap & RISCV_IOMMU_CAP_HPM)) { + return; + } + + value = g_hash_table_lookup(s->hpm_event_ctr_map, + GUINT_TO_POINTER(event_id)); + if (value == NULL) { + return; + } + + for (ctrs = GPOINTER_TO_UINT(value); ctrs != 0; ctrs &= ctrs - 1) { + ctr_idx = ctz32(ctrs); + if (get_field(inhibit, BIT(ctr_idx + 1))) { + continue; + } + + evt = riscv_iommu_reg_get64(s, + RISCV_IOMMU_REG_IOHPMEVT_BASE + (ctr_idx << 3)); + + /* + * It's quite possible that event ID has been changed in counter + * but hashtable hasn't been updated yet. We don't want to increment + * counter for the old event ID. + */ + if (event_id != get_field(evt, RISCV_IOMMU_IOHPMEVT_EVENT_ID)) { + continue; + } + + if (get_field(evt, RISCV_IOMMU_IOHPMEVT_IDT)) { + did_gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID); + pid_pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID); + } else { + did_gscid = ctx->devid; + pid_pscid = ctx->process_id; + } + + if (get_field(evt, RISCV_IOMMU_IOHPMEVT_PV_PSCV)) { + /* + * If the transaction does not have a valid process_id, counter + * increments if device_id matches DID_GSCID. If the transaction + * has a valid process_id, counter increments if device_id + * matches DID_GSCID and process_id matches PID_PSCID. See + * IOMMU Specification, Chapter 5.23. Performance-monitoring + * event selector. + */ + if (ctx->process_id && + get_field(evt, RISCV_IOMMU_IOHPMEVT_PID_PSCID) != pid_pscid) { + continue; + } + } + + if (get_field(evt, RISCV_IOMMU_IOHPMEVT_DV_GSCV)) { + uint32_t mask = ~0; + + if (get_field(evt, RISCV_IOMMU_IOHPMEVT_DMASK)) { + /* + * 1001 1011 mask = GSCID + * 0000 0111 mask = mask ^ (mask + 1) + * 1111 1000 mask = ~mask; + */ + mask = get_field(evt, RISCV_IOMMU_IOHPMEVT_DID_GSCID); + mask = mask ^ (mask + 1); + mask = ~mask; + } + + if ((get_field(evt, RISCV_IOMMU_IOHPMEVT_DID_GSCID) & mask) != + (did_gscid & mask)) { + continue; + } + } + + hpm_incr_ctr(s, ctr_idx); + } +} + +/* Timer callback for cycle counter overflow. */ +void riscv_iommu_hpm_timer_cb(void *priv) +{ + RISCVIOMMUState *s = priv; + const uint32_t inhibit = riscv_iommu_reg_get32( + s, RISCV_IOMMU_REG_IOCOUNTINH); + uint32_t ovf; + + if (get_field(inhibit, RISCV_IOMMU_IOCOUNTINH_CY)) { + return; + } + + if (s->irq_overflow_left > 0) { + uint64_t irq_trigger_at = + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->irq_overflow_left; + timer_mod_anticipate_ns(s->hpm_timer, irq_trigger_at); + s->irq_overflow_left = 0; + return; + } + + ovf = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_IOCOUNTOVF); + if (!get_field(ovf, RISCV_IOMMU_IOCOUNTOVF_CY)) { + /* + * We don't need to set hpmcycle_val to zero and update hpmcycle_prev to + * current clock value. The way we calculate iohpmcycs will overflow + * and return the correct value. This avoids the need to synchronize + * timer callback and write callback. + */ + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IOCOUNTOVF, + RISCV_IOMMU_IOCOUNTOVF_CY, 0); + riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_IOHPMCYCLES, + RISCV_IOMMU_IOHPMCYCLES_OVF, 0); + riscv_iommu_notify(s, RISCV_IOMMU_INTR_PM); + } +} + +static void hpm_setup_timer(RISCVIOMMUState *s, uint64_t value) +{ + const uint32_t inhibit = riscv_iommu_reg_get32( + s, RISCV_IOMMU_REG_IOCOUNTINH); + uint64_t overflow_at, overflow_ns; + + if (get_field(inhibit, RISCV_IOMMU_IOCOUNTINH_CY)) { + return; + } + + /* + * We are using INT64_MAX here instead to UINT64_MAX because cycle counter + * has 63-bit precision and INT64_MAX is the maximum it can store. + */ + if (value) { + overflow_ns = INT64_MAX - value + 1; + } else { + overflow_ns = INT64_MAX; + } + + overflow_at = (uint64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + overflow_ns; + + if (overflow_at > INT64_MAX) { + s->irq_overflow_left = overflow_at - INT64_MAX; + overflow_at = INT64_MAX; + } + + timer_mod_anticipate_ns(s->hpm_timer, overflow_at); +} + +/* Updates the internal cycle counter state when iocntinh:CY is changed. */ +void riscv_iommu_process_iocntinh_cy(RISCVIOMMUState *s, bool prev_cy_inh) +{ + const uint32_t inhibit = riscv_iommu_reg_get32( + s, RISCV_IOMMU_REG_IOCOUNTINH); + + /* We only need to process CY bit toggle. */ + if (!(inhibit ^ prev_cy_inh)) { + return; + } + + trace_riscv_iommu_hpm_iocntinh_cy(prev_cy_inh); + + if (!(inhibit & RISCV_IOMMU_IOCOUNTINH_CY)) { + /* + * Cycle counter is enabled. Just start the timer again and update + * the clock snapshot value to point to the current time to make + * sure iohpmcycles read is correct. + */ + s->hpmcycle_prev = get_cycles(); + hpm_setup_timer(s, s->hpmcycle_val); + } else { + /* + * Cycle counter is disabled. Stop the timer and update the cycle + * counter to record the current value which is last programmed + * value + the cycles passed so far. + */ + s->hpmcycle_val = s->hpmcycle_val + (get_cycles() - s->hpmcycle_prev); + timer_del(s->hpm_timer); + } +} + +void riscv_iommu_process_hpmcycle_write(RISCVIOMMUState *s) +{ + const uint64_t val = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_IOHPMCYCLES); + const uint32_t ovf = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_IOCOUNTOVF); + + trace_riscv_iommu_hpm_cycle_write(ovf, val); + + /* + * Clear OF bit in IOCNTOVF if it's being cleared in IOHPMCYCLES register. + */ + if (get_field(ovf, RISCV_IOMMU_IOCOUNTOVF_CY) && + !get_field(val, RISCV_IOMMU_IOHPMCYCLES_OVF)) { + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IOCOUNTOVF, 0, + RISCV_IOMMU_IOCOUNTOVF_CY); + } + + s->hpmcycle_val = val & ~RISCV_IOMMU_IOHPMCYCLES_OVF; + s->hpmcycle_prev = get_cycles(); + hpm_setup_timer(s, s->hpmcycle_val); +} + +static inline bool check_valid_event_id(unsigned event_id) +{ + return event_id > RISCV_IOMMU_HPMEVENT_INVALID && + event_id < RISCV_IOMMU_HPMEVENT_MAX; +} + +static gboolean hpm_event_equal(gpointer key, gpointer value, gpointer udata) +{ + uint32_t *pair = udata; + + if (GPOINTER_TO_UINT(value) & (1 << pair[0])) { + pair[1] = GPOINTER_TO_UINT(key); + return true; + } + + return false; +} + +/* Caller must check ctr_idx against hpm_ctrs to see if its supported or not. */ +static void update_event_map(RISCVIOMMUState *s, uint64_t value, + uint32_t ctr_idx) +{ + unsigned event_id = get_field(value, RISCV_IOMMU_IOHPMEVT_EVENT_ID); + uint32_t pair[2] = { ctr_idx, RISCV_IOMMU_HPMEVENT_INVALID }; + uint32_t new_value = 1 << ctr_idx; + gpointer data; + + /* + * If EventID field is RISCV_IOMMU_HPMEVENT_INVALID + * remove the current mapping. + */ + if (event_id == RISCV_IOMMU_HPMEVENT_INVALID) { + data = g_hash_table_find(s->hpm_event_ctr_map, hpm_event_equal, pair); + + new_value = GPOINTER_TO_UINT(data) & ~(new_value); + if (new_value != 0) { + g_hash_table_replace(s->hpm_event_ctr_map, + GUINT_TO_POINTER(pair[1]), + GUINT_TO_POINTER(new_value)); + } else { + g_hash_table_remove(s->hpm_event_ctr_map, + GUINT_TO_POINTER(pair[1])); + } + + return; + } + + /* Update the counter mask if the event is already enabled. */ + if (g_hash_table_lookup_extended(s->hpm_event_ctr_map, + GUINT_TO_POINTER(event_id), + NULL, + &data)) { + new_value |= GPOINTER_TO_UINT(data); + } + + g_hash_table_insert(s->hpm_event_ctr_map, + GUINT_TO_POINTER(event_id), + GUINT_TO_POINTER(new_value)); +} + +void riscv_iommu_process_hpmevt_write(RISCVIOMMUState *s, uint32_t evt_reg) +{ + const uint32_t ctr_idx = (evt_reg - RISCV_IOMMU_REG_IOHPMEVT_BASE) >> 3; + const uint32_t ovf = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_IOCOUNTOVF); + uint64_t val = riscv_iommu_reg_get64(s, evt_reg); + + if (ctr_idx >= s->hpm_cntrs) { + return; + } + + trace_riscv_iommu_hpm_evt_write(ctr_idx, ovf, val); + + /* Clear OF bit in IOCNTOVF if it's being cleared in IOHPMEVT register. */ + if (get_field(ovf, BIT(ctr_idx + 1)) && + !get_field(val, RISCV_IOMMU_IOHPMEVT_OF)) { + /* +1 to offset CYCLE register OF bit. */ + riscv_iommu_reg_mod32( + s, RISCV_IOMMU_REG_IOCOUNTOVF, 0, BIT(ctr_idx + 1)); + } + + if (!check_valid_event_id(get_field(val, RISCV_IOMMU_IOHPMEVT_EVENT_ID))) { + /* Reset EventID (WARL) field to invalid. */ + val = set_field(val, RISCV_IOMMU_IOHPMEVT_EVENT_ID, + RISCV_IOMMU_HPMEVENT_INVALID); + riscv_iommu_reg_set64(s, evt_reg, val); + } + + update_event_map(s, val, ctr_idx); +} diff --git a/hw/riscv/riscv-iommu-hpm.h b/hw/riscv/riscv-iommu-hpm.h new file mode 100644 index 00000000000..5fc4ef2e8bd --- /dev/null +++ b/hw/riscv/riscv-iommu-hpm.h @@ -0,0 +1,33 @@ +/* + * RISC-V IOMMU - Hardware Performance Monitor (HPM) helpers + * + * 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_HPM_H +#define HW_RISCV_IOMMU_HPM_H + +#include "qom/object.h" +#include "hw/riscv/riscv-iommu.h" + +uint64_t riscv_iommu_hpmcycle_read(RISCVIOMMUState *s); +void riscv_iommu_hpm_incr_ctr(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, + unsigned event_id); +void riscv_iommu_hpm_timer_cb(void *priv); +void riscv_iommu_process_iocntinh_cy(RISCVIOMMUState *s, bool prev_cy_inh); +void riscv_iommu_process_hpmcycle_write(RISCVIOMMUState *s); +void riscv_iommu_process_hpmevt_write(RISCVIOMMUState *s, uint32_t evt_reg); + +#endif diff --git a/hw/riscv/riscv-iommu-pci.c b/hw/riscv/riscv-iommu-pci.c new file mode 100644 index 00000000000..cdb4a7a8f03 --- /dev/null +++ b/hw/riscv/riscv-iommu-pci.c @@ -0,0 +1,217 @@ +/* + * 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 "exec/target_page.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" +#include "trace.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; + riscv_iommu_set_cap_igs(iommu, RISCV_IOMMU_CAP_IGS_MSI); +} + +static const 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), +}; + +static void riscv_iommu_pci_reset_hold(Object *obj, ResetType type) +{ + RISCVIOMMUStatePci *pci = RISCV_IOMMU_PCI(obj); + RISCVIOMMUState *iommu = &pci->iommu; + + riscv_iommu_reset(iommu); + + trace_riscv_iommu_pci_reset_hold(type); +} + +static void riscv_iommu_pci_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = riscv_iommu_pci_reset_hold; + + 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 = (const 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-sys.c b/hw/riscv/riscv-iommu-sys.c new file mode 100644 index 00000000000..e34d00aef64 --- /dev/null +++ b/hw/riscv/riscv-iommu-sys.c @@ -0,0 +1,248 @@ +/* + * QEMU emulation of an RISC-V IOMMU Platform Device + * + * 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/irq.h" +#include "hw/pci/pci_bus.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/host-utils.h" +#include "qemu/module.h" +#include "qom/object.h" +#include "trace.h" + +#include "riscv-iommu.h" + +#define RISCV_IOMMU_SYSDEV_ICVEC_VECTORS 0x3333 + +#define RISCV_IOMMU_PCI_MSIX_VECTORS 5 + +/* RISC-V IOMMU System Platform Device Emulation */ + +struct RISCVIOMMUStateSys { + SysBusDevice parent; + uint64_t addr; + uint32_t base_irq; + DeviceState *irqchip; + RISCVIOMMUState iommu; + + /* Wired int support */ + qemu_irq irqs[RISCV_IOMMU_INTR_COUNT]; + + /* Memory Regions for MSIX table and pending bit entries. */ + MemoryRegion msix_table_mmio; + MemoryRegion msix_pba_mmio; + uint8_t *msix_table; + uint8_t *msix_pba; +}; + +static uint64_t msix_table_mmio_read(void *opaque, hwaddr addr, + unsigned size) +{ + RISCVIOMMUStateSys *s = opaque; + + g_assert(addr + size <= RISCV_IOMMU_PCI_MSIX_VECTORS * PCI_MSIX_ENTRY_SIZE); + return pci_get_long(s->msix_table + addr); +} + +static void msix_table_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + RISCVIOMMUStateSys *s = opaque; + + g_assert(addr + size <= RISCV_IOMMU_PCI_MSIX_VECTORS * PCI_MSIX_ENTRY_SIZE); + pci_set_long(s->msix_table + addr, val); +} + +static const MemoryRegionOps msix_table_mmio_ops = { + .read = msix_table_mmio_read, + .write = msix_table_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .max_access_size = 4, + }, +}; + +static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr, + unsigned size) +{ + RISCVIOMMUStateSys *s = opaque; + + return pci_get_long(s->msix_pba + addr); +} + +static void msix_pba_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ +} + +static const MemoryRegionOps msix_pba_mmio_ops = { + .read = msix_pba_mmio_read, + .write = msix_pba_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .max_access_size = 4, + }, +}; + +static void riscv_iommu_sysdev_init_msi(RISCVIOMMUStateSys *s, + uint32_t n_vectors) +{ + RISCVIOMMUState *iommu = &s->iommu; + uint32_t table_size = n_vectors * PCI_MSIX_ENTRY_SIZE; + uint32_t table_offset = RISCV_IOMMU_REG_MSI_CONFIG; + uint32_t pba_size = QEMU_ALIGN_UP(n_vectors, 64) / 8; + uint32_t pba_offset = RISCV_IOMMU_REG_MSI_CONFIG + 256; + + s->msix_table = g_malloc0(table_size); + s->msix_pba = g_malloc0(pba_size); + + memory_region_init_io(&s->msix_table_mmio, OBJECT(s), &msix_table_mmio_ops, + s, "msix-table", table_size); + memory_region_add_subregion(&iommu->regs_mr, table_offset, + &s->msix_table_mmio); + + memory_region_init_io(&s->msix_pba_mmio, OBJECT(s), &msix_pba_mmio_ops, s, + "msix-pba", pba_size); + memory_region_add_subregion(&iommu->regs_mr, pba_offset, + &s->msix_pba_mmio); +} + +static void riscv_iommu_sysdev_send_MSI(RISCVIOMMUStateSys *s, + uint32_t vector) +{ + uint8_t *table_entry = s->msix_table + vector * PCI_MSIX_ENTRY_SIZE; + uint64_t msi_addr = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR); + uint32_t msi_data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA); + MemTxResult result; + + address_space_stl_le(&address_space_memory, msi_addr, + msi_data, MEMTXATTRS_UNSPECIFIED, &result); + trace_riscv_iommu_sys_msi_sent(vector, msi_addr, msi_data, result); +} + +static void riscv_iommu_sysdev_notify(RISCVIOMMUState *iommu, + unsigned vector) +{ + RISCVIOMMUStateSys *s = container_of(iommu, RISCVIOMMUStateSys, iommu); + uint32_t fctl = riscv_iommu_reg_get32(iommu, RISCV_IOMMU_REG_FCTL); + + if (fctl & RISCV_IOMMU_FCTL_WSI) { + qemu_irq_pulse(s->irqs[vector]); + trace_riscv_iommu_sys_irq_sent(vector); + return; + } + + riscv_iommu_sysdev_send_MSI(s, vector); +} + +static void riscv_iommu_sys_realize(DeviceState *dev, Error **errp) +{ + RISCVIOMMUStateSys *s = RISCV_IOMMU_SYS(dev); + SysBusDevice *sysdev = SYS_BUS_DEVICE(s); + PCIBus *pci_bus; + qemu_irq irq; + + qdev_realize(DEVICE(&s->iommu), NULL, errp); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iommu.regs_mr); + if (s->addr) { + sysbus_mmio_map(SYS_BUS_DEVICE(s), 0, s->addr); + } + + pci_bus = (PCIBus *) object_resolve_path_type("", TYPE_PCI_BUS, NULL); + if (pci_bus) { + riscv_iommu_pci_setup_iommu(&s->iommu, pci_bus, errp); + } + + s->iommu.notify = riscv_iommu_sysdev_notify; + + /* 4 IRQs are defined starting from s->base_irq */ + for (int i = 0; i < RISCV_IOMMU_INTR_COUNT; i++) { + sysbus_init_irq(sysdev, &s->irqs[i]); + irq = qdev_get_gpio_in(s->irqchip, s->base_irq + i); + sysbus_connect_irq(sysdev, i, irq); + } + + riscv_iommu_sysdev_init_msi(s, RISCV_IOMMU_PCI_MSIX_VECTORS); +} + +static void riscv_iommu_sys_init(Object *obj) +{ + RISCVIOMMUStateSys *s = RISCV_IOMMU_SYS(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_SYSDEV_ICVEC_VECTORS; + riscv_iommu_set_cap_igs(iommu, RISCV_IOMMU_CAP_IGS_BOTH); +} + +static const Property riscv_iommu_sys_properties[] = { + DEFINE_PROP_UINT64("addr", RISCVIOMMUStateSys, addr, 0), + DEFINE_PROP_UINT32("base-irq", RISCVIOMMUStateSys, base_irq, 0), + DEFINE_PROP_LINK("irqchip", RISCVIOMMUStateSys, irqchip, + TYPE_DEVICE, DeviceState *), +}; + +static void riscv_iommu_sys_reset_hold(Object *obj, ResetType type) +{ + RISCVIOMMUStateSys *sys = RISCV_IOMMU_SYS(obj); + RISCVIOMMUState *iommu = &sys->iommu; + + riscv_iommu_reset(iommu); + + trace_riscv_iommu_sys_reset_hold(type); +} + +static void riscv_iommu_sys_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = riscv_iommu_sys_reset_hold; + + dc->realize = riscv_iommu_sys_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, riscv_iommu_sys_properties); +} + +static const TypeInfo riscv_iommu_sys = { + .name = TYPE_RISCV_IOMMU_SYS, + .parent = TYPE_SYS_BUS_DEVICE, + .class_init = riscv_iommu_sys_class_init, + .instance_init = riscv_iommu_sys_init, + .instance_size = sizeof(RISCVIOMMUStateSys), +}; + +static void riscv_iommu_register_sys(void) +{ + type_register_static(&riscv_iommu_sys); +} + +type_init(riscv_iommu_register_sys) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c new file mode 100644 index 00000000000..a877e5da84b --- /dev/null +++ b/hw/riscv/riscv-iommu.c @@ -0,0 +1,2679 @@ +/* + * 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 "exec/target_page.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 "riscv-iommu-hpm.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 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; +}; + +typedef enum RISCVIOMMUTransTag { + RISCV_IOMMU_TRANS_TAG_BY, /* Bypass */ + RISCV_IOMMU_TRANS_TAG_SS, /* Single Stage */ + RISCV_IOMMU_TRANS_TAG_VG, /* G-stage only */ + RISCV_IOMMU_TRANS_TAG_VN, /* Nested translation */ +} RISCVIOMMUTransTag; + +/* Address translation cache entry */ +struct RISCVIOMMUEntry { + RISCVIOMMUTransTag tag; /* Translation Tag */ + 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(); + } +} + +void riscv_iommu_notify(RISCVIOMMUState *s, int vec_type) +{ + uint32_t ipsr, icvec, vector; + + if (!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_len = va_skip + va_bits; + const uint64_t va_mask = (1ULL << va_len) - 1; + + if (pass == S_STAGE && va_len > 32) { + target_ulong mask, masked_msbs; + + mask = (1L << (TARGET_LONG_BITS - (va_len - 1))) - 1; + masked_msbs = (addr >> (va_len - 1)) & mask; + + if (masked_msbs != 0 && masked_msbs != mask) { + return (iotlb->perm & IOMMU_WO) ? + RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S : + RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S; + } + } else { + if ((addr & va_mask) != addr) { + return (iotlb->perm & IOMMU_WO) ? + RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS : + RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS; + } + } + } + + + if (pass == S_STAGE) { + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_S_VS_WALKS); + } else { + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_G_WALKS); + } + + /* 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; ) { + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_DD_WALK); + /* + * 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)); + } + + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_DD_WALK); + + /* 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; ) { + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_PD_WALK); + + /* + * 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_PDTE_VALID)) { + return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID; + } + addr = PPN_PHYS(get_field(de, RISCV_IOMMU_PDTE_PPN)); + } + + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_PD_WALK); + + /* 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 && t1->tag == t2->tag; +} + +static guint riscv_iommu_iot_hash(gconstpointer v) +{ + RISCVIOMMUEntry *t = (RISCVIOMMUEntry *) v; + return (guint)t->iova; +} + +/* GV: 0 AV: 0 PSCV: 0 GVMA: 0 */ +/* GV: 0 AV: 0 GVMA: 1 */ +static +void riscv_iommu_iot_inval_all(gpointer key, gpointer value, gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->tag == arg->tag) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 0 AV: 0 PSCV: 1 GVMA: 0 */ +static +void riscv_iommu_iot_inval_pscid(gpointer key, gpointer value, gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->tag == arg->tag && + iot->pscid == arg->pscid) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 0 AV: 1 PSCV: 0 GVMA: 0 */ +static +void riscv_iommu_iot_inval_iova(gpointer key, gpointer value, gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->tag == arg->tag && + iot->iova == arg->iova) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 0 AV: 1 PSCV: 1 GVMA: 0 */ +static void riscv_iommu_iot_inval_pscid_iova(gpointer key, gpointer value, + gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->tag == arg->tag && + iot->pscid == arg->pscid && + iot->iova == arg->iova) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 1 AV: 0 PSCV: 0 GVMA: 0 */ +/* GV: 1 AV: 0 GVMA: 1 */ +static +void riscv_iommu_iot_inval_gscid(gpointer key, gpointer value, gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->tag == arg->tag && + iot->gscid == arg->gscid) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 1 AV: 0 PSCV: 1 GVMA: 0 */ +static void riscv_iommu_iot_inval_gscid_pscid(gpointer key, gpointer value, + gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->tag == arg->tag && + iot->gscid == arg->gscid && + iot->pscid == arg->pscid) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 1 AV: 1 PSCV: 0 GVMA: 0 */ +/* GV: 1 AV: 1 GVMA: 1 */ +static void riscv_iommu_iot_inval_gscid_iova(gpointer key, gpointer value, + gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->tag == arg->tag && + iot->gscid == arg->gscid && + iot->iova == arg->iova) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 1 AV: 1 PSCV: 1 GVMA: 0 */ +static void riscv_iommu_iot_inval_gscid_pscid_iova(gpointer key, gpointer value, + gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->tag == arg->tag && + iot->gscid == arg->gscid && + iot->pscid == arg->pscid && + iot->iova == arg->iova) { + 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, RISCVIOMMUTransTag transtag) +{ + RISCVIOMMUEntry key = { + .tag = transtag, + .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, RISCVIOMMUTransTag transtag) +{ + GHashTable *iot_cache; + RISCVIOMMUEntry key = { + .tag = transtag, + .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 RISCVIOMMUTransTag riscv_iommu_get_transtag(RISCVIOMMUContext *ctx) +{ + uint64_t satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD); + uint64_t gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD); + + if (satp == RISCV_IOMMU_DC_FSC_MODE_BARE) { + return (gatp == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) ? + RISCV_IOMMU_TRANS_TAG_BY : RISCV_IOMMU_TRANS_TAG_VG; + } else { + return (gatp == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) ? + RISCV_IOMMU_TRANS_TAG_SS : RISCV_IOMMU_TRANS_TAG_VN; + } +} + +static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, + IOMMUTLBEntry *iotlb, bool enable_cache) +{ + RISCVIOMMUTransTag transtag = riscv_iommu_get_transtag(ctx); + RISCVIOMMUEntry *iot; + IOMMUAccessFlags perm; + bool enable_pid; + bool enable_pri; + GHashTable *iot_cache; + int fault; + + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_URQ); + + 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) { + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_ATS_RQ); + /* 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, transtag); + 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; + } + + riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_TLB_MISS); + + /* 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; + iot->tag = transtag; + 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): + { + bool gv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV); + bool av = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV); + bool pscv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV); + uint32_t gscid = get_field(cmd.dword0, + RISCV_IOMMU_CMD_IOTINVAL_GSCID); + uint32_t pscid = get_field(cmd.dword0, + RISCV_IOMMU_CMD_IOTINVAL_PSCID); + hwaddr iova = (cmd.dword1 << 2) & TARGET_PAGE_MASK; + + if (pscv) { + /* illegal command arguments IOTINVAL.GVMA & PSCV == 1 */ + goto cmd_ill; + } + + func = riscv_iommu_iot_inval_all; + + if (gv) { + func = (av) ? riscv_iommu_iot_inval_gscid_iova : + riscv_iommu_iot_inval_gscid; + } + + riscv_iommu_iot_inval( + s, func, gscid, pscid, iova, RISCV_IOMMU_TRANS_TAG_VG); + + riscv_iommu_iot_inval( + s, func, gscid, pscid, iova, RISCV_IOMMU_TRANS_TAG_VN); + break; + } + + case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_VMA, + RISCV_IOMMU_CMD_IOTINVAL_OPCODE): + { + bool gv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV); + bool av = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV); + bool pscv = !!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV); + uint32_t gscid = get_field(cmd.dword0, + RISCV_IOMMU_CMD_IOTINVAL_GSCID); + uint32_t pscid = get_field(cmd.dword0, + RISCV_IOMMU_CMD_IOTINVAL_PSCID); + hwaddr iova = (cmd.dword1 << 2) & TARGET_PAGE_MASK; + RISCVIOMMUTransTag transtag; + + if (gv) { + transtag = RISCV_IOMMU_TRANS_TAG_VN; + if (pscv) { + func = (av) ? riscv_iommu_iot_inval_gscid_pscid_iova : + riscv_iommu_iot_inval_gscid_pscid; + } else { + func = (av) ? riscv_iommu_iot_inval_gscid_iova : + riscv_iommu_iot_inval_gscid; + } + } else { + transtag = RISCV_IOMMU_TRANS_TAG_SS; + if (pscv) { + func = (av) ? riscv_iommu_iot_inval_pscid_iova : + riscv_iommu_iot_inval_pscid; + } else { + func = (av) ? riscv_iommu_iot_inval_iova : + riscv_iommu_iot_inval_all; + } + } + + riscv_iommu_iot_inval(s, func, gscid, pscid, iova, transtag); + 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); +} + +static void riscv_iommu_process_hpm_writes(RISCVIOMMUState *s, + uint32_t regb, + bool prev_cy_inh) +{ + switch (regb) { + case RISCV_IOMMU_REG_IOCOUNTINH: + riscv_iommu_process_iocntinh_cy(s, prev_cy_inh); + break; + + case RISCV_IOMMU_REG_IOHPMCYCLES: + case RISCV_IOMMU_REG_IOHPMCYCLES + 4: + riscv_iommu_process_hpmcycle_write(s); + break; + + case RISCV_IOMMU_REG_IOHPMEVT_BASE ... + RISCV_IOMMU_REG_IOHPMEVT(RISCV_IOMMU_IOCOUNT_NUM) + 4: + riscv_iommu_process_hpmevt_write(s, regb & ~7); + break; + } +} + +/* + * 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; + bool cy_inh = false; + + 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; + + case RISCV_IOMMU_REG_IOCOUNTINH: + if (addr != RISCV_IOMMU_REG_IOCOUNTINH) { + break; + } + /* Store previous value of CY bit. */ + cy_inh = !!(riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_IOCOUNTINH) & + RISCV_IOMMU_IOCOUNTINH_CY); + 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); + } + + /* Process HPM writes and update any internal state if needed. */ + if (regb >= RISCV_IOMMU_REG_IOCOUNTOVF && + regb <= (RISCV_IOMMU_REG_IOHPMEVT(RISCV_IOMMU_IOCOUNT_NUM) + 4)) { + riscv_iommu_process_hpm_writes(s, regb, cy_inh); + } + + 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; + } + + /* Compute cycle register value. */ + if ((addr & ~7) == RISCV_IOMMU_REG_IOHPMCYCLES) { + val = riscv_iommu_hpmcycle_read(s); + ptr = (uint8_t *)&val + (addr & 7); + } else if ((addr & ~3) == RISCV_IOMMU_REG_IOCOUNTOVF) { + /* + * Software can read RISCV_IOMMU_REG_IOCOUNTOVF before timer + * callback completes. In which case CY_OF bit in + * RISCV_IOMMU_IOHPMCYCLES_OVF would be 0. Here we take the + * CY_OF bit state from RISCV_IOMMU_REG_IOHPMCYCLES register as + * it's not dependent over the timer callback and is computed + * from cycle overflow. + */ + val = ldq_le_p(&s->regs_rw[addr]); + val |= (riscv_iommu_hpmcycle_read(s) & RISCV_IOMMU_IOHPMCYCLES_OVF) + ? RISCV_IOMMU_IOCOUNTOVF_CY + : 0; + ptr = (uint8_t *)&val + (addr & 3); + } else { + 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, + } +}; + +void riscv_iommu_set_cap_igs(RISCVIOMMUState *s, riscv_iommu_igs_mode mode) +{ + s->cap = set_field(s->cap, RISCV_IOMMU_CAP_IGS, mode); +} + +static void riscv_iommu_instance_init(Object *obj) +{ + RISCVIOMMUState *s = RISCV_IOMMU(obj); + + /* 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; + + /* 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); + + /* 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_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; + } + + if (s->hpm_cntrs > 0) { + /* Clip number of HPM counters to maximum supported (31). */ + if (s->hpm_cntrs > RISCV_IOMMU_IOCOUNT_NUM) { + s->hpm_cntrs = RISCV_IOMMU_IOCOUNT_NUM; + } + /* Enable hardware performance monitor interface */ + s->cap |= RISCV_IOMMU_CAP_HPM; + } + + /* 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 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); + } + + /* If HPM registers are enabled. */ + if (s->cap & RISCV_IOMMU_CAP_HPM) { + /* +1 for cycle counter bit. */ + stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_IOCOUNTINH], + ~((2 << s->hpm_cntrs) - 1)); + stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_IOHPMCYCLES], 0); + memset(&s->regs_ro[RISCV_IOMMU_REG_IOHPMCTR_BASE], + 0x00, s->hpm_cntrs * 8); + memset(&s->regs_ro[RISCV_IOMMU_REG_IOHPMEVT_BASE], + 0x00, s->hpm_cntrs * 8); + } + + /* 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"); + + if (s->cap & RISCV_IOMMU_CAP_HPM) { + s->hpm_timer = + timer_new_ns(QEMU_CLOCK_VIRTUAL, riscv_iommu_hpm_timer_cb, s); + s->hpm_event_ctr_map = g_hash_table_new(g_direct_hash, g_direct_equal); + } +} + +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); + + if (s->cap & RISCV_IOMMU_CAP_HPM) { + g_hash_table_unref(s->hpm_event_ctr_map); + timer_free(s->hpm_timer); + } +} + +void riscv_iommu_reset(RISCVIOMMUState *s) +{ + uint32_t reg_clr; + int ddtp_mode; + + /* + * Clear DDTP while setting DDTP_mode back to user + * initial setting. + */ + ddtp_mode = s->enable_off ? + RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE; + s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, ddtp_mode); + riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_DDTP, s->ddtp); + + reg_clr = RISCV_IOMMU_CQCSR_CQEN | RISCV_IOMMU_CQCSR_CIE | + RISCV_IOMMU_CQCSR_CQON | RISCV_IOMMU_CQCSR_BUSY; + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, 0, reg_clr); + + reg_clr = RISCV_IOMMU_FQCSR_FQEN | RISCV_IOMMU_FQCSR_FIE | + RISCV_IOMMU_FQCSR_FQON | RISCV_IOMMU_FQCSR_BUSY; + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, 0, reg_clr); + + reg_clr = RISCV_IOMMU_PQCSR_PQEN | RISCV_IOMMU_PQCSR_PIE | + RISCV_IOMMU_PQCSR_PQON | RISCV_IOMMU_PQCSR_BUSY; + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, 0, reg_clr); + + riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_TR_REQ_CTL, 0, + RISCV_IOMMU_TR_REQ_CTL_GO_BUSY); + + riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_IPSR, 0); + + g_hash_table_remove_all(s->ctx_cache); + g_hash_table_remove_all(s->iot_cache); +} + +static const 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_UINT8("hpm-counters", RISCVIOMMUState, hpm_cntrs, + RISCV_IOMMU_IOCOUNT_NUM), +}; + +static void riscv_iommu_class_init(ObjectClass *klass, const 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), + .instance_init = riscv_iommu_instance_init, + .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, const 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 00000000000..a31aa62144f --- /dev/null +++ b/hw/riscv/riscv-iommu.h @@ -0,0 +1,157 @@ +/* + * 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/qdev-properties.h" +#include "system/dma.h" +#include "hw/riscv/iommu.h" +#include "hw/riscv/riscv-iommu-bits.h" + +typedef enum riscv_iommu_igs_modes riscv_iommu_igs_mode; + +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 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; + + /* HPM cycle counter */ + QEMUTimer *hpm_timer; + uint64_t hpmcycle_val; /* Current value of cycle register */ + uint64_t hpmcycle_prev; /* Saved value of QEMU_CLOCK_VIRTUAL clock */ + uint64_t irq_overflow_left; /* Value beyond INT64_MAX after overflow */ + + /* HPM event counters */ + GHashTable *hpm_event_ctr_map; /* Mapping of events to counters */ + uint8_t hpm_cntrs; +}; + +void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus, + Error **errp); +void riscv_iommu_set_cap_igs(RISCVIOMMUState *s, riscv_iommu_igs_mode mode); +void riscv_iommu_reset(RISCVIOMMUState *s); +void riscv_iommu_notify(RISCVIOMMUState *s, int vec_type); + +typedef struct RISCVIOMMUContext RISCVIOMMUContext; +/* 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 */ +}; + +/* 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/riscv_hart.c b/hw/riscv/riscv_hart.c index 613ea2aaa0b..7f2676008cf 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -21,19 +21,38 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" -#include "sysemu/reset.h" +#include "system/reset.h" +#include "system/qtest.h" +#include "qemu/cutils.h" #include "hw/sysbus.h" #include "target/riscv/cpu.h" #include "hw/qdev-properties.h" #include "hw/riscv/riscv_hart.h" +#include "qemu/error-report.h" -static Property riscv_harts_props[] = { +static const Property riscv_harts_props[] = { DEFINE_PROP_UINT32("num-harts", RISCVHartArrayState, num_harts, 1), DEFINE_PROP_UINT32("hartid-base", RISCVHartArrayState, hartid_base, 0), DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type), DEFINE_PROP_UINT64("resetvec", RISCVHartArrayState, resetvec, DEFAULT_RSTVEC), - DEFINE_PROP_END_OF_LIST(), + + /* + * Smrnmi implementation-defined interrupt and exception trap handlers. + * + * When an RNMI interrupt is detected, the hart then enters M-mode and + * jumps to the address defined by "rnmi-interrupt-vector". + * + * When the hart encounters an exception while executing in M-mode with + * the mnstatus.NMIE bit clear, the hart then jumps to the address + * defined by "rnmi-exception-vector". + */ + DEFINE_PROP_ARRAY("rnmi-interrupt-vector", RISCVHartArrayState, + num_rnmi_irqvec, rnmi_irqvec, qdev_prop_uint64, + uint64_t), + DEFINE_PROP_ARRAY("rnmi-exception-vector", RISCVHartArrayState, + num_rnmi_excpvec, rnmi_excpvec, qdev_prop_uint64, + uint64_t), }; static void riscv_harts_cpu_reset(void *opaque) @@ -42,11 +61,85 @@ static void riscv_harts_cpu_reset(void *opaque) cpu_reset(CPU(cpu)); } +#ifndef CONFIG_USER_ONLY +static void csr_call(char *cmd, uint64_t cpu_num, int csrno, uint64_t *val) +{ + RISCVCPU *cpu = RISCV_CPU(cpu_by_arch_id(cpu_num)); + CPURISCVState *env = &cpu->env; + + int ret = RISCV_EXCP_NONE; + if (strcmp(cmd, "get_csr") == 0) { + ret = riscv_csrr(env, csrno, (target_ulong *)val); + } else if (strcmp(cmd, "set_csr") == 0) { + ret = riscv_csrrw(env, csrno, NULL, *(target_ulong *)val, + MAKE_64BIT_MASK(0, TARGET_LONG_BITS), 0); + } + + g_assert(ret == RISCV_EXCP_NONE); +} + +static bool csr_qtest_callback(CharBackend *chr, gchar **words) +{ + if (strcmp(words[0], "csr") == 0) { + + uint64_t cpu; + uint64_t val; + int rc, csr; + + rc = qemu_strtou64(words[2], NULL, 0, &cpu); + g_assert(rc == 0); + rc = qemu_strtoi(words[3], NULL, 0, &csr); + g_assert(rc == 0); + rc = qemu_strtou64(words[4], NULL, 0, &val); + g_assert(rc == 0); + csr_call(words[1], cpu, csr, &val); + + qtest_sendf(chr, "OK 0 "TARGET_FMT_lx"\n", (target_ulong)val); + + return true; + } + + return false; +} + +static void riscv_cpu_register_csr_qtest_callback(void) +{ + static bool first = true; + if (first) { + first = false; + qtest_set_command_cb(csr_qtest_callback); + } +} +#endif + static bool riscv_hart_realize(RISCVHartArrayState *s, int idx, char *cpu_type, Error **errp) { object_initialize_child(OBJECT(s), "harts[*]", &s->harts[idx], cpu_type); qdev_prop_set_uint64(DEVICE(&s->harts[idx]), "resetvec", s->resetvec); + + if (s->harts[idx].cfg.ext_smrnmi) { + if (idx < s->num_rnmi_irqvec) { + qdev_prop_set_uint64(DEVICE(&s->harts[idx]), + "rnmi-interrupt-vector", s->rnmi_irqvec[idx]); + } + + if (idx < s->num_rnmi_excpvec) { + qdev_prop_set_uint64(DEVICE(&s->harts[idx]), + "rnmi-exception-vector", s->rnmi_excpvec[idx]); + } + } else { + if (s->num_rnmi_irqvec > 0) { + warn_report_once("rnmi-interrupt-vector property is ignored " + "because Smrnmi extension is not enabled."); + } + + if (s->num_rnmi_excpvec > 0) { + warn_report_once("rnmi-exception-vector property is ignored " + "because Smrnmi extension is not enabled."); + } + } + s->harts[idx].env.mhartid = s->hartid_base + idx; qemu_register_reset(riscv_harts_cpu_reset, &s->harts[idx]); return qdev_realize(DEVICE(&s->harts[idx]), NULL, errp); @@ -59,6 +152,10 @@ static void riscv_harts_realize(DeviceState *dev, Error **errp) s->harts = g_new0(RISCVCPU, s->num_harts); +#ifndef CONFIG_USER_ONLY + riscv_cpu_register_csr_qtest_callback(); +#endif + for (n = 0; n < s->num_harts; n++) { if (!riscv_hart_realize(s, n, s->cpu_type, errp)) { return; @@ -66,7 +163,7 @@ static void riscv_harts_realize(DeviceState *dev, Error **errp) } } -static void riscv_harts_class_init(ObjectClass *klass, void *data) +static void riscv_harts_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c index 12ea74b0324..3e7f4411727 100644 --- a/hw/riscv/shakti_c.c +++ b/hw/riscv/shakti_c.c @@ -23,12 +23,11 @@ #include "qemu/error-report.h" #include "hw/intc/sifive_plic.h" #include "hw/intc/riscv_aclint.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/qdev-properties.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/riscv/boot.h" - static const struct MemmapEntry { hwaddr base; hwaddr size; @@ -46,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, @@ -63,28 +57,32 @@ 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) { } -static void shakti_c_machine_class_init(ObjectClass *klass, void *data) +static void shakti_c_machine_class_init(ObjectClass *klass, const 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"; } @@ -144,7 +142,7 @@ static void shakti_c_soc_state_realize(DeviceState *dev, Error **errp) shakti_c_memmap[SHAKTI_C_ROM].base, &sss->rom); } -static void shakti_c_soc_class_init(ObjectClass *klass, void *data) +static void shakti_c_soc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = shakti_c_soc_state_realize; diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 0d37adc542b..7baed1958e0 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" @@ -47,7 +46,7 @@ #include "hw/misc/sifive_e_prci.h" #include "hw/misc/sifive_e_aon.h" #include "chardev/char.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static const MemMapEntry sifive_e_memmap[] = { [SIFIVE_E_DEV_DEBUG] = { 0x0, 0x1000 }, @@ -79,6 +78,7 @@ static void sifive_e_machine_init(MachineState *machine) SiFiveEState *s = RISCV_E_MACHINE(machine); MemoryRegion *sys_mem = get_system_memory(); int i; + RISCVBootInfo boot_info; if (machine->ram_size != mc->default_ram_size) { char *sz = size_to_str(mc->default_ram_size); @@ -114,8 +114,9 @@ static void sifive_e_machine_init(MachineState *machine) rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), memmap[SIFIVE_E_DEV_MROM].base, &address_space_memory); + riscv_boot_info_init(&boot_info, &s->soc.cpus); if (machine->kernel_filename) { - riscv_load_kernel(machine, &s->soc.cpus, + riscv_load_kernel(machine, &boot_info, memmap[SIFIVE_E_DEV_DTIM].base, false, NULL); } @@ -142,7 +143,7 @@ static void sifive_e_machine_instance_init(Object *obj) s->revb = false; } -static void sifive_e_machine_class_init(ObjectClass *oc, void *data) +static void sifive_e_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -225,7 +226,7 @@ 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); + SIFIVE_E_LFCLK_DEFAULT_FREQ, false); sifive_e_prci_create(memmap[SIFIVE_E_DEV_PRCI].base); /* AON */ @@ -283,7 +284,7 @@ static void sifive_e_soc_realize(DeviceState *dev, Error **errp) &s->xip_mem); } -static void sifive_e_soc_class_init(ObjectClass *oc, void *data) +static void sifive_e_soc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index ec76dce6c95..d69f942cfbe 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" @@ -57,9 +56,9 @@ #include "hw/intc/sifive_plic.h" #include "chardev/char.h" #include "net/eth.h" -#include "sysemu/device_tree.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/device_tree.h" +#include "system/runstate.h" +#include "system/system.h" #include @@ -171,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) { @@ -180,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); @@ -194,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); } @@ -518,17 +514,19 @@ 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; + uint32_t fdt_load_addr_hi32 = 0x00000000; int i; - uint32_t fdt_load_addr; + uint64_t fdt_load_addr; uint64_t kernel_entry; DriveInfo *dinfo; BlockBackend *blk; DeviceState *flash_dev, *sd_dev, *card_dev; qemu_irq flash_cs, sd_cs; + RISCVBootInfo boot_info; /* Initialize SoC */ object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RISCV_U_SOC); @@ -592,14 +590,15 @@ static void sifive_u_machine_init(MachineState *machine) firmware_name = riscv_default_firmware_name(&s->soc.u_cpus); firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, - start_addr, NULL); + &start_addr, NULL); + riscv_boot_info_init(&boot_info, &s->soc.u_cpus); if (machine->kernel_filename) { - kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc.u_cpus, + kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); - - kernel_entry = riscv_load_kernel(machine, &s->soc.u_cpus, - kernel_start_addr, true, NULL); + riscv_load_kernel(machine, &boot_info, kernel_start_addr, + true, NULL); + kernel_entry = boot_info.image_low_addr; } else { /* * If dynamic firmware is used, it doesn't know where is the next mode @@ -610,11 +609,12 @@ static void sifive_u_machine_init(MachineState *machine) fdt_load_addr = riscv_compute_fdt_addr(memmap[SIFIVE_U_DEV_DRAM].base, memmap[SIFIVE_U_DEV_DRAM].size, - machine); + machine, &boot_info); riscv_load_fdt(fdt_load_addr, machine->fdt); if (!riscv_is_32bit(&s->soc.u_cpus)) { start_addr_hi32 = (uint64_t)start_addr >> 32; + fdt_load_addr_hi32 = fdt_load_addr >> 32; } /* reset vector */ @@ -629,7 +629,7 @@ static void sifive_u_machine_init(MachineState *machine) start_addr, /* start: .dword */ start_addr_hi32, fdt_load_addr, /* fdt_laddr: .dword */ - 0x00000000, + fdt_load_addr_hi32, 0x00000000, /* fw_dyn: */ }; @@ -649,7 +649,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); @@ -712,7 +713,7 @@ static void sifive_u_machine_instance_init(Object *obj) object_property_set_description(obj, "serial", "Board serial number"); } -static void sifive_u_machine_class_init(ObjectClass *oc, void *data) +static void sifive_u_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -723,6 +724,7 @@ static void sifive_u_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = SIFIVE_U_CPU; mc->default_cpus = mc->min_cpus; mc->default_ram_id = "riscv.sifive.u.ram"; + mc->auto_create_sdcard = true; object_class_property_add_bool(oc, "start-in-flash", sifive_u_machine_get_start_in_flash, @@ -789,7 +791,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); @@ -893,11 +894,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)) { @@ -944,13 +941,12 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_QSPI2_IRQ)); } -static Property sifive_u_soc_props[] = { +static const Property sifive_u_soc_props[] = { DEFINE_PROP_UINT32("serial", SiFiveUSoCState, serial, OTP_SERIAL), DEFINE_PROP_STRING("cpu-type", SiFiveUSoCState, cpu_type), - DEFINE_PROP_END_OF_LIST() }; -static void sifive_u_soc_class_init(ObjectClass *oc, void *data) +static void sifive_u_soc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 81f7e53aedd..641aae8c019 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -36,8 +36,8 @@ #include "hw/char/riscv_htif.h" #include "hw/intc/riscv_aclint.h" #include "chardev/char.h" -#include "sysemu/device_tree.h" -#include "sysemu/sysemu.h" +#include "system/device_tree.h" +#include "system/system.h" #include @@ -59,7 +59,7 @@ static void create_fdt(SpikeState *s, const MemMapEntry *memmap, 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" @@ -113,9 +113,7 @@ 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", @@ -200,13 +198,15 @@ static void spike_board_init(MachineState *machine) MemoryRegion *system_memory = get_system_memory(); MemoryRegion *mask_rom = g_new(MemoryRegion, 1); 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 fdt_load_addr; uint64_t kernel_entry; char *soc_name; int i, base_hartid, hart_count; bool htif_custom_base = false; + RISCVBootInfo boot_info; /* Check socket count limit */ if (SPIKE_SOCKETS_MAX < riscv_socket_count(machine)) { @@ -292,7 +292,7 @@ static void spike_board_init(MachineState *machine) /* Load firmware */ if (firmware_name) { firmware_end_addr = riscv_load_firmware(firmware_name, - memmap[SPIKE_DRAM].base, + &firmware_load_addr, htif_symbol_callback); g_free(firmware_name); } @@ -301,13 +301,14 @@ static void spike_board_init(MachineState *machine) create_fdt(s, memmap, riscv_is_32bit(&s->soc[0]), htif_custom_base); /* Load kernel */ + riscv_boot_info_init(&boot_info, &s->soc[0]); if (machine->kernel_filename) { - kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0], + kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); - kernel_entry = riscv_load_kernel(machine, &s->soc[0], - kernel_start_addr, - true, htif_symbol_callback); + riscv_load_kernel(machine, &boot_info, kernel_start_addr, + true, htif_symbol_callback); + kernel_entry = boot_info.image_low_addr; } else { /* * If dynamic firmware is used, it doesn't know where is the next mode @@ -318,11 +319,11 @@ static void spike_board_init(MachineState *machine) fdt_load_addr = riscv_compute_fdt_addr(memmap[SPIKE_DRAM].base, memmap[SPIKE_DRAM].size, - machine); + machine, &boot_info); 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); @@ -341,7 +342,7 @@ static void spike_machine_instance_init(Object *obj) { } -static void spike_machine_class_init(ObjectClass *oc, void *data) +static void spike_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/riscv/trace-events b/hw/riscv/trace-events new file mode 100644 index 00000000000..b50b14a6542 --- /dev/null +++ b/hw/riscv/trace-events @@ -0,0 +1,26 @@ +# 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" +riscv_iommu_sys_irq_sent(uint32_t vector) "IRQ sent to vector %u" +riscv_iommu_sys_msi_sent(uint32_t vector, uint64_t msi_addr, uint32_t msi_data, uint32_t result) "MSI sent to vector %u msi_addr 0x%"PRIx64" msi_data 0x%x result %u" +riscv_iommu_sys_reset_hold(int reset_type) "reset type %d" +riscv_iommu_pci_reset_hold(int reset_type) "reset type %d" +riscv_iommu_hpm_read(uint64_t cycle, uint32_t inhibit, uint64_t ctr_prev, uint64_t ctr_val) "cycle 0x%"PRIx64" inhibit 0x%x ctr_prev 0x%"PRIx64" ctr_val 0x%"PRIx64 +riscv_iommu_hpm_incr_ctr(uint64_t cntr_val) "cntr_val 0x%"PRIx64 +riscv_iommu_hpm_iocntinh_cy(bool prev_cy_inh) "prev_cy_inh %d" +riscv_iommu_hpm_cycle_write(uint32_t ovf, uint64_t val) "ovf 0x%x val 0x%"PRIx64 +riscv_iommu_hpm_evt_write(uint32_t ctr_idx, uint32_t ovf, uint64_t val) "ctr_idx 0x%x ovf 0x%x val 0x%"PRIx64 diff --git a/hw/riscv/trace.h b/hw/riscv/trace.h new file mode 100644 index 00000000000..8c0e3ca1f3d --- /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 index 7331248f592..8b5683dbdeb 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -27,16 +27,21 @@ #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 "qapi/error.h" -#include "qemu/error-report.h" -#include "sysemu/reset.h" -#include "migration/vmstate.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/intc/riscv_aclint.h" +#include "hw/virtio/virtio-acpi.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "system/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) */ @@ -58,17 +63,56 @@ static void acpi_align_size(GArray *blob, unsigned align) static void riscv_acpi_madt_add_rintc(uint32_t uid, const CPUArchIdList *arch_ids, - GArray *entry) + 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, 20, 1); /* Length */ + 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) @@ -87,7 +131,7 @@ static void acpi_dsdt_add_cpus(Aml *scope, RISCVVirtState *s) aml_int(arch_ids->cpus[i].arch_id))); /* build _MAT object */ - riscv_acpi_madt_add_rintc(i, arch_ids, madt_buf); + 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))); @@ -97,22 +141,131 @@ static void acpi_dsdt_add_cpus(Aml *scope, RISCVVirtState *s) } } -static void acpi_dsdt_add_fw_cfg(Aml *scope, const MemMapEntry *fw_cfg_memmap) +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("FWCF"); - aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002"))); + 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))); - /* 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(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); +} + +/* + * Add DSDT entry for the IOMMU platform device. + * ACPI ID for IOMMU is defined in the section 6.2 of RISC-V BRS spec. + * https://github.com/riscv-non-isa/riscv-brs/releases/download/v0.8/riscv-brs-spec.pdf + */ +static void acpi_dsdt_add_iommu_sys(Aml *scope, const MemMapEntry *iommu_memmap, + uint32_t iommu_irq) +{ + uint32_t i; + + Aml *dev = aml_device("IMU0"); + aml_append(dev, aml_name_decl("_HID", aml_string("RSCV0004"))); + aml_append(dev, aml_name_decl("_UID", aml_int(0))); Aml *crs = aml_resource_template(); - aml_append(crs, aml_memory32_fixed(fw_cfg_memmap->base, - fw_cfg_memmap->size, AML_READ_WRITE)); + aml_append(crs, aml_memory32_fixed(iommu_memmap->base, + iommu_memmap->size, AML_READ_WRITE)); + for (i = iommu_irq; i < iommu_irq + 4; i++) { + aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, AML_ACTIVE_LOW, + AML_EXCLUSIVE, &i, 1)); + } + aml_append(dev, aml_name_decl("_CRS", crs)); aml_append(scope, dev); } +/* + * Serial Port Console Redirection Table (SPCR) + * Rev: 1.10 + */ + +static void +spcr_setup(GArray *table_data, BIOSLinker *linker, RISCVVirtState *s) +{ + const char name[] = "."; + AcpiSpcrData serial = { + .interface_type = 0x12, /* 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, + .uart_clk_freq = 0, + .precise_baudrate = 0, + .namespace_string_length = sizeof(name), + .namespace_string_offset = 88, + }; + + build_spcr(table_data, linker, &serial, 4, s->oem_id, s->oem_table_id, + name); +} + /* RHCT Node[N] starts at offset 56 */ #define RHCT_NODE_ARRAY_OFFSET 56 @@ -121,6 +274,7 @@ static void acpi_dsdt_add_fw_cfg(Aml *scope, const MemMapEntry *fw_cfg_memmap) * 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, @@ -130,9 +284,11 @@ static void build_rhct(GArray *table_data, 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; - RISCVCPU *cpu; - char *isa; + uint32_t isa_offset, num_rhct_nodes, cmo_offset = 0; + RISCVCPU *cpu = &s->soc[0].harts[0]; + uint32_t mmu_offset = 0; + bool rv32 = riscv_cpu_is_32bit(cpu); + g_autofree char *isa = NULL; AcpiTable table = { .sig = "RHCT", .rev = 1, .oem_id = s->oem_id, .oem_table_id = s->oem_table_id }; @@ -147,6 +303,13 @@ static void build_rhct(GArray *table_data, /* ISA + N hart info */ num_rhct_nodes = 1 + ms->smp.cpus; + if (cpu->cfg.ext_zicbom || cpu->cfg.ext_zicboz) { + num_rhct_nodes++; + } + + if (!rv32 && cpu->cfg.max_satp_mode >= VM_1_10_SV39) { + num_rhct_nodes++; + } /* Number of RHCT nodes*/ build_append_int_noprefix(table_data, num_rhct_nodes, 4); @@ -158,7 +321,6 @@ static void build_rhct(GArray *table_data, isa_offset = table_data->len - table.table_offset; build_append_int_noprefix(table_data, 0, 2); /* Type 0 */ - cpu = &s->soc[0].harts[0]; isa = riscv_isa_string(cpu); len = 8 + strlen(isa) + 1; aligned_len = (len % 2) ? (len + 1) : len; @@ -174,14 +336,86 @@ static void build_rhct(GArray *table_data, 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 (!rv32 && cpu->cfg.max_satp_mode >= VM_1_10_SV39) { + 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 (cpu->cfg.max_satp_mode == VM_1_10_SV57) { + build_append_int_noprefix(table_data, 2, 1); /* Sv57 */ + } else if (cpu->cfg.max_satp_mode == VM_1_10_SV48) { + build_append_int_noprefix(table_data, 1, 1); /* Sv48 */ + } else if (cpu->cfg.max_satp_mode == VM_1_10_SV39) { + build_append_int_noprefix(table_data, 0, 1); /* Sv39 */ + } else { + g_assert_not_reached(); + } + } + /* 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 */ - build_append_int_noprefix(table_data, 16, 2); /* Length */ - build_append_int_noprefix(table_data, 0x1, 2); /* Revision */ - build_append_int_noprefix(table_data, 1, 2); /* Number of offsets */ - build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */ - build_append_int_noprefix(table_data, isa_offset, 4); /* Offsets[0] */ + + /* 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); @@ -209,6 +443,8 @@ static void build_dsdt(GArray *table_data, 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 }; @@ -226,7 +462,41 @@ static void build_dsdt(GArray *table_data, scope = aml_scope("\\_SB"); acpi_dsdt_add_cpus(scope, s); - acpi_dsdt_add_fw_cfg(scope, &memmap[VIRT_FW_CFG]); + 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 (virt_is_iommu_sys_enabled(s)) { + acpi_dsdt_add_iommu_sys(scope, &memmap[VIRT_IOMMU_SYS], IOMMU_SYS_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); @@ -242,6 +512,7 @@ static void build_dsdt(GArray *table_data, * 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, @@ -250,6 +521,21 @@ static void build_madt(GArray *table_data, 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 }; @@ -261,7 +547,314 @@ static void build_madt(GArray *table_data, /* 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); + 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); +} + +#define ID_MAPPING_ENTRY_SIZE 20 +#define IOMMU_ENTRY_SIZE 40 +#define RISCV_INTERRUPT_WIRE_OFFSSET 40 +#define ROOT_COMPLEX_ENTRY_SIZE 20 +#define RIMT_NODE_OFFSET 48 + +/* + * ID Mapping Structure + */ +static void build_rimt_id_mapping(GArray *table_data, uint32_t source_id_base, + uint32_t num_ids, uint32_t dest_id_base) +{ + /* Source ID Base */ + build_append_int_noprefix(table_data, source_id_base, 4); + /* Number of IDs */ + build_append_int_noprefix(table_data, num_ids, 4); + /* Destination Device ID Base */ + build_append_int_noprefix(table_data, source_id_base, 4); + /* Destination IOMMU Offset */ + build_append_int_noprefix(table_data, dest_id_base, 4); + /* Flags */ + build_append_int_noprefix(table_data, 0, 4); +} + +struct AcpiRimtIdMapping { + uint32_t source_id_base; + uint32_t num_ids; +}; +typedef struct AcpiRimtIdMapping AcpiRimtIdMapping; + +/* Build the rimt ID mapping to IOMMU for a given PCI host bridge */ +static int rimt_host_bridges(Object *obj, void *opaque) +{ + GArray *idmap_blob = opaque; + + if (object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) { + PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus; + + if (bus && !pci_bus_bypass_iommu(bus)) { + int min_bus, max_bus; + + pci_bus_range(bus, &min_bus, &max_bus); + + AcpiRimtIdMapping idmap = { + .source_id_base = min_bus << 8, + .num_ids = (max_bus - min_bus + 1) << 8, + }; + g_array_append_val(idmap_blob, idmap); + } + } + + return 0; +} + +static int rimt_idmap_compare(gconstpointer a, gconstpointer b) +{ + AcpiRimtIdMapping *idmap_a = (AcpiRimtIdMapping *)a; + AcpiRimtIdMapping *idmap_b = (AcpiRimtIdMapping *)b; + + return idmap_a->source_id_base - idmap_b->source_id_base; +} + +/* + * RISC-V IO Mapping Table (RIMT) + * https://github.com/riscv-non-isa/riscv-acpi-rimt/releases/download/v0.99/rimt-spec.pdf + */ +static void build_rimt(GArray *table_data, BIOSLinker *linker, + RISCVVirtState *s) +{ + int i, nb_nodes, rc_mapping_count; + size_t node_size, iommu_offset = 0; + uint32_t id = 0; + g_autoptr(GArray) iommu_idmaps = g_array_new(false, true, + sizeof(AcpiRimtIdMapping)); + + AcpiTable table = { .sig = "RIMT", .rev = 1, .oem_id = s->oem_id, + .oem_table_id = s->oem_table_id }; + + acpi_table_begin(&table, table_data); + + object_child_foreach_recursive(object_get_root(), + rimt_host_bridges, iommu_idmaps); + + /* Sort the ID mapping by Source ID Base*/ + g_array_sort(iommu_idmaps, rimt_idmap_compare); + + nb_nodes = 2; /* RC, IOMMU */ + rc_mapping_count = iommu_idmaps->len; + /* Number of RIMT Nodes */ + build_append_int_noprefix(table_data, nb_nodes, 4); + + /* Offset to Array of RIMT Nodes */ + build_append_int_noprefix(table_data, RIMT_NODE_OFFSET, 4); + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + + iommu_offset = table_data->len - table.table_offset; + /* IOMMU Device Structure */ + build_append_int_noprefix(table_data, 0, 1); /* Type - IOMMU*/ + build_append_int_noprefix(table_data, 1, 1); /* Revision */ + node_size = IOMMU_ENTRY_SIZE; + build_append_int_noprefix(table_data, node_size, 2); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + build_append_int_noprefix(table_data, id++, 2); /* ID */ + if (virt_is_iommu_sys_enabled(s)) { + /* Hardware ID */ + build_append_int_noprefix(table_data, 'R', 1); + build_append_int_noprefix(table_data, 'S', 1); + build_append_int_noprefix(table_data, 'C', 1); + build_append_int_noprefix(table_data, 'V', 1); + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '4', 1); + /* Base Address */ + build_append_int_noprefix(table_data, + s->memmap[VIRT_IOMMU_SYS].base, 8); + build_append_int_noprefix(table_data, 0, 4); /* Flags */ + } else { + /* Hardware ID */ + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '1', 1); + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '1', 1); + build_append_int_noprefix(table_data, '4', 1); + + build_append_int_noprefix(table_data, 0, 8); /* Base Address */ + build_append_int_noprefix(table_data, 1, 4); /* Flags */ + } + + build_append_int_noprefix(table_data, 0, 4); /* Proximity Domain */ + build_append_int_noprefix(table_data, 0, 2); /* PCI Segment number */ + /* PCIe B/D/F */ + if (virt_is_iommu_sys_enabled(s)) { + build_append_int_noprefix(table_data, 0, 2); + } else { + build_append_int_noprefix(table_data, s->pci_iommu_bdf, 2); + } + /* Number of interrupt wires */ + build_append_int_noprefix(table_data, 0, 2); + /* Interrupt wire array offset */ + build_append_int_noprefix(table_data, RISCV_INTERRUPT_WIRE_OFFSSET, 2); + + /* PCIe Root Complex Node */ + build_append_int_noprefix(table_data, 1, 1); /* Type */ + build_append_int_noprefix(table_data, 1, 1); /* Revision */ + node_size = ROOT_COMPLEX_ENTRY_SIZE + + ID_MAPPING_ENTRY_SIZE * rc_mapping_count; + build_append_int_noprefix(table_data, node_size, 2); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + build_append_int_noprefix(table_data, id++, 2); /* ID */ + build_append_int_noprefix(table_data, 0, 4); /* Flags */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + /* PCI Segment number */ + build_append_int_noprefix(table_data, 0, 2); + /* ID mapping array offset */ + build_append_int_noprefix(table_data, ROOT_COMPLEX_ENTRY_SIZE, 2); + /* Number of ID mappings */ + build_append_int_noprefix(table_data, rc_mapping_count, 2); + + /* Output Reference */ + AcpiRimtIdMapping *range; + + /* ID mapping array */ + for (i = 0; i < iommu_idmaps->len; i++) { + range = &g_array_index(iommu_idmaps, AcpiRimtIdMapping, i); + if (virt_is_iommu_sys_enabled(s)) { + range->source_id_base = 0; + } else { + range->source_id_base = s->pci_iommu_bdf + 1; + } + range->num_ids = 0xffff - s->pci_iommu_bdf; + build_rimt_id_mapping(table_data, range->source_id_base, + range->num_ids, iommu_offset); + } + + 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); @@ -272,6 +865,7 @@ 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)); @@ -294,6 +888,34 @@ static void virt_acpi_build(RISCVVirtState *s, AcpiBuildTables *tables) acpi_add_table(table_offsets, tables_blob); build_rhct(tables_blob, tables->linker, s); + if (virt_is_iommu_sys_enabled(s) || s->pci_iommu_bdf) { + acpi_add_table(table_offsets, tables_blob); + build_rimt(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, @@ -374,7 +996,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/riscv/virt.c b/hw/riscv/virt.c index 5edc1d98d23..cf280a92e5a 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -27,59 +27,56 @@ #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/riscv-iommu-bits.h" #include "hw/riscv/virt.h" #include "hw/riscv/boot.h" #include "hw/riscv/numa.h" -#include "kvm_riscv.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 "system/device_tree.h" +#include "system/system.h" +#include "system/tcg.h" +#include "system/kvm.h" +#include "system/tpm.h" +#include "system/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" +#include "hw/uefi/var-service-api.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_aplic_imsic(RISCVVirtAIAType aia_type) +{ + bool msimode = 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 accommodate single IMSIC group in address space" -#endif + return riscv_is_kvm_aia_aplic_imsic(msimode); +} -#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 +static bool virt_use_emulated_aplic(RISCVVirtAIAType aia_type) +{ + bool msimode = aia_type == VIRT_AIA_TYPE_APLIC_IMSIC; -/* KVM AIA only supports APLIC MSI. APLIC Wired is always emulated by QEMU. */ -static bool virt_use_kvm_aia(RISCVVirtState *s) + return riscv_use_emulated_aplic(msimode); +} + +static bool virt_aclint_allowed(void) { - return kvm_irqchip_in_kernel() && s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC; + return tcg_enabled() || qtest_enabled(); } static const MemMapEntry virt_memmap[] = { @@ -90,6 +87,7 @@ static const MemMapEntry virt_memmap[] = { [VIRT_CLINT] = { 0x2000000, 0x10000 }, [VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 }, [VIRT_PCIE_PIO] = { 0x3000000, 0x10000 }, + [VIRT_IOMMU_SYS] = { 0x3010000, 0x1000 }, [VIRT_PLATFORM_BUS] = { 0x4000000, 0x2000000 }, [VIRT_PLIC] = { 0xc000000, VIRT_PLIC_SIZE(VIRT_CPUS_MAX * 2) }, [VIRT_APLIC_M] = { 0xc000000, APLIC_SIZE(VIRT_CPUS_MAX) }, @@ -168,8 +166,8 @@ static void virt_flash_map1(PFlashCFI01 *flash, static void virt_flash_map(RISCVVirtState *s, MemoryRegion *sysmem) { - hwaddr flashsize = virt_memmap[VIRT_FLASH].size / 2; - hwaddr flashbase = virt_memmap[VIRT_FLASH].base; + hwaddr flashsize = s->memmap[VIRT_FLASH].size / 2; + hwaddr flashbase = s->memmap[VIRT_FLASH].base; virt_flash_map1(s->flash[0], flashbase, flashsize, sysmem); @@ -182,7 +180,7 @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename, { int pin, dev; uint32_t irq_map_stride = 0; - uint32_t full_irq_map[GPEX_NUM_IRQS * GPEX_NUM_IRQS * + uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * FDT_MAX_INT_MAP_WIDTH] = {}; uint32_t *irq_map = full_irq_map; @@ -194,11 +192,11 @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename, * 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++) { + for (dev = 0; dev < PCI_NUM_PINS; dev++) { int devfn = dev * 0x8; - for (pin = 0; pin < GPEX_NUM_IRQS; pin++) { - int irq_nr = PCIE_IRQ + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS); + for (pin = 0; pin < PCI_NUM_PINS; pin++) { + int irq_nr = PCIE_IRQ + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS); int i = 0; /* Fill PCI address cells */ @@ -224,7 +222,7 @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename, } qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map, - GPEX_NUM_IRQS * GPEX_NUM_IRQS * + PCI_NUM_PINS * PCI_NUM_PINS * irq_map_stride * sizeof(uint32_t)); qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask", @@ -238,12 +236,15 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, int cpu; uint32_t cpu_phandle; MachineState *ms = MACHINE(s); - char *name, *cpu_name, *core_name, *intc_name, *sv_name; 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]; + int8_t satp_mode_max = cpu_ptr->cfg.max_satp_mode; + 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)++; @@ -251,28 +252,29 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, s->soc[socket].hartid_base + cpu); 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); + if (satp_mode_max != -1) { 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); - g_free(sv_name); } - name = riscv_isa_string(cpu_ptr); - qemu_fdt_setprop_string(ms->fdt, cpu_name, "riscv,isa", name); - g_free(name); + riscv_isa_write_fdt(cpu_ptr, ms->fdt, cpu_name); - if (cpu_ptr->cfg.ext_icbom) { + 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_icboz) { + 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", @@ -295,38 +297,33 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, core_name = g_strdup_printf("%s/core%d", clust_name, cpu); qemu_fdt_add_subnode(ms->fdt, core_name); qemu_fdt_setprop_cell(ms->fdt, core_name, "cpu", cpu_phandle); - - g_free(core_name); - g_free(intc_name); - g_free(cpu_name); } } -static void create_fdt_socket_memory(RISCVVirtState *s, - const MemMapEntry *memmap, int socket) +static void create_fdt_socket_memory(RISCVVirtState *s, int socket) { - char *mem_name; - uint64_t addr, size; + g_autofree char *mem_name = NULL; + hwaddr addr; + uint64_t size; MachineState *ms = MACHINE(s); - addr = memmap[VIRT_DRAM].base + riscv_socket_mem_offset(ms, socket); + addr = s->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); + mem_name = g_strdup_printf("/memory@%"HWADDR_PRIx, addr); 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(ms->fdt, mem_name, "device_type", "memory"); riscv_socket_fdt_write_id(ms, mem_name, socket); - g_free(mem_name); } static void create_fdt_socket_clint(RISCVVirtState *s, - const MemMapEntry *memmap, int socket, + int socket, 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 *ms = MACHINE(s); static const char * const clint_compat[2] = { @@ -342,33 +339,31 @@ static void create_fdt_socket_clint(RISCVVirtState *s, clint_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER); } - clint_addr = memmap[VIRT_CLINT].base + (memmap[VIRT_CLINT].size * socket); + clint_addr = s->memmap[VIRT_CLINT].base + + (s->memmap[VIRT_CLINT].size * socket); clint_name = g_strdup_printf("/soc/clint@%lx", clint_addr); 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(ms->fdt, clint_name, "reg", - 0x0, clint_addr, 0x0, memmap[VIRT_CLINT].size); + 0x0, clint_addr, 0x0, s->memmap[VIRT_CLINT].size); 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(ms, clint_name, socket); - g_free(clint_name); - - g_free(clint_cells); } static void create_fdt_socket_aclint(RISCVVirtState *s, - const MemMapEntry *memmap, int socket, + int socket, uint32_t *intc_phandles) { int cpu; 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; + 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); @@ -386,8 +381,10 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, aclint_cells_size = s->soc[socket].num_harts * sizeof(uint32_t) * 2; if (s->aia_type != VIRT_AIA_TYPE_APLIC_IMSIC) { - addr = memmap[VIRT_CLINT].base + (memmap[VIRT_CLINT].size * socket); + addr = s->memmap[VIRT_CLINT].base + + (s->memmap[VIRT_CLINT].size * socket); name = g_strdup_printf("/soc/mswi@%lx", addr); + qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "riscv,aclint-mswi"); @@ -402,13 +399,13 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, } if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { - addr = memmap[VIRT_CLINT].base + + addr = s->memmap[VIRT_CLINT].base + (RISCV_ACLINT_DEFAULT_MTIMER_SIZE * socket); size = RISCV_ACLINT_DEFAULT_MTIMER_SIZE; } else { - addr = memmap[VIRT_CLINT].base + RISCV_ACLINT_SWI_SIZE + - (memmap[VIRT_CLINT].size * socket); - size = memmap[VIRT_CLINT].size - RISCV_ACLINT_SWI_SIZE; + addr = s->memmap[VIRT_CLINT].base + RISCV_ACLINT_SWI_SIZE + + (s->memmap[VIRT_CLINT].size * socket); + size = s->memmap[VIRT_CLINT].size - RISCV_ACLINT_SWI_SIZE; } name = g_strdup_printf("/soc/mtimer@%lx", addr); qemu_fdt_add_subnode(ms->fdt, name); @@ -425,14 +422,15 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, g_free(name); if (s->aia_type != VIRT_AIA_TYPE_APLIC_IMSIC) { - addr = memmap[VIRT_ACLINT_SSWI].base + - (memmap[VIRT_ACLINT_SSWI].size * socket); + addr = s->memmap[VIRT_ACLINT_SSWI].base + + (s->memmap[VIRT_ACLINT_SSWI].size * socket); + name = g_strdup_printf("/soc/sswi@%lx", addr); qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "riscv,aclint-sswi"); qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, addr, 0x0, memmap[VIRT_ACLINT_SSWI].size); + 0x0, addr, 0x0, s->memmap[VIRT_ACLINT_SSWI].size); qemu_fdt_setprop(ms->fdt, name, "interrupts-extended", aclint_sswi_cells, aclint_cells_size); qemu_fdt_setprop(ms->fdt, name, "interrupt-controller", NULL, 0); @@ -440,60 +438,64 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, 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, - const MemMapEntry *memmap, int socket, + int socket, uint32_t *phandle, uint32_t *intc_phandles, 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 *ms = MACHINE(s); static const char * const plic_compat[2] = { "sifive,plic-1.0.0", "riscv,plic0" }; + plic_phandles[socket] = (*phandle)++; + plic_addr = s->memmap[VIRT_PLIC].base + + (s->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(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); - qemu_fdt_setprop(ms->fdt, plic_name, "interrupts-extended", - plic_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4); qemu_fdt_setprop_cells(ms->fdt, plic_name, "reg", - 0x0, plic_addr, 0x0, memmap[VIRT_PLIC].size); + 0x0, plic_addr, 0x0, s->memmap[VIRT_PLIC].size); qemu_fdt_setprop_cell(ms->fdt, plic_name, "riscv,ndev", VIRT_IRQCHIP_NUM_SOURCES - 1); riscv_socket_fdt_write_id(ms, plic_name, socket); @@ -502,17 +504,13 @@ static void create_fdt_socket_plic(RISCVVirtState *s, if (!socket) { platform_bus_add_all_fdt_nodes(ms->fdt, plic_name, - memmap[VIRT_PLATFORM_BUS].base, - memmap[VIRT_PLATFORM_BUS].size, + s->memmap[VIRT_PLATFORM_BUS].base, + s->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; @@ -528,11 +526,15 @@ static void create_fdt_one_imsic(RISCVVirtState *s, hwaddr base_addr, bool m_mode, uint32_t imsic_guest_bits) { int cpu, socket; - char *imsic_name; + g_autofree char *imsic_name = NULL; MachineState *ms = MACHINE(s); int socket_count = riscv_socket_count(ms); - uint32_t imsic_max_hart_per_socket; - uint32_t *imsic_cells, *imsic_regs, imsic_addr, imsic_size; + 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" + }; imsic_cells = g_new0(uint32_t, ms->smp.cpus * 2); imsic_regs = g_new0(uint32_t, socket_count * 4); @@ -556,9 +558,13 @@ static void create_fdt_one_imsic(RISCVVirtState *s, hwaddr base_addr, } } - imsic_name = g_strdup_printf("/soc/imsics@%lx", (unsigned long)base_addr); + 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(ms->fdt, imsic_name, "compatible", "riscv,imsics"); + 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); @@ -584,13 +590,9 @@ static void create_fdt_one_imsic(RISCVVirtState *s, hwaddr base_addr, IMSIC_MMIO_GROUP_MIN_SHIFT); } qemu_fdt_setprop_cell(ms->fdt, imsic_name, "phandle", msi_phandle); - - g_free(imsic_name); - g_free(imsic_regs); - g_free(imsic_cells); } -static void create_fdt_imsic(RISCVVirtState *s, const MemMapEntry *memmap, +static void create_fdt_imsic(RISCVVirtState *s, uint32_t *phandle, uint32_t *intc_phandles, uint32_t *msi_m_phandle, uint32_t *msi_s_phandle) { @@ -599,17 +601,23 @@ static void create_fdt_imsic(RISCVVirtState *s, const MemMapEntry *memmap, if (!kvm_enabled()) { /* M-level IMSIC node */ - create_fdt_one_imsic(s, memmap[VIRT_IMSIC_M].base, intc_phandles, + create_fdt_one_imsic(s, 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, + create_fdt_one_imsic(s, 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, @@ -619,20 +627,24 @@ static void create_fdt_one_aplic(RISCVVirtState *s, int socket, bool m_mode, int num_harts) { int cpu; - char *aplic_name; - uint32_t *aplic_cells; + 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); - - aplic_cells = g_new0(uint32_t, num_harts * 2); + 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); } - aplic_name = g_strdup_printf("/soc/aplic@%lx", aplic_addr); qemu_fdt_add_subnode(ms->fdt, aplic_name); - qemu_fdt_setprop_string(ms->fdt, aplic_name, "compatible", "riscv,aplic"); + qemu_fdt_setprop_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); @@ -652,6 +664,15 @@ static void create_fdt_one_aplic(RISCVVirtState *s, int socket, 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); @@ -659,13 +680,10 @@ static void create_fdt_one_aplic(RISCVVirtState *s, int socket, riscv_socket_fdt_write_id(ms, aplic_name, socket); qemu_fdt_setprop_cell(ms->fdt, aplic_name, "phandle", aplic_phandle); - - g_free(aplic_name); - g_free(aplic_cells); } static void create_fdt_socket_aplic(RISCVVirtState *s, - const MemMapEntry *memmap, int socket, + int socket, uint32_t msi_m_phandle, uint32_t msi_s_phandle, uint32_t *phandle, @@ -673,7 +691,6 @@ static void create_fdt_socket_aplic(RISCVVirtState *s, uint32_t *aplic_phandles, int num_harts) { - char *aplic_name; unsigned long aplic_addr; MachineState *ms = MACHINE(s); uint32_t aplic_m_phandle, aplic_s_phandle; @@ -683,66 +700,63 @@ static void create_fdt_socket_aplic(RISCVVirtState *s, 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, + aplic_addr = s->memmap[VIRT_APLIC_M].base + + (s->memmap[VIRT_APLIC_M].size * socket); + create_fdt_one_aplic(s, socket, aplic_addr, + s->memmap[VIRT_APLIC_M].size, msi_m_phandle, intc_phandles, aplic_m_phandle, aplic_s_phandle, true, num_harts); } /* S-level APLIC node */ - aplic_addr = memmap[VIRT_APLIC_S].base + - (memmap[VIRT_APLIC_S].size * socket); - create_fdt_one_aplic(s, socket, aplic_addr, memmap[VIRT_APLIC_S].size, + aplic_addr = s->memmap[VIRT_APLIC_S].base + + (s->memmap[VIRT_APLIC_S].size * socket); + create_fdt_one_aplic(s, socket, aplic_addr, s->memmap[VIRT_APLIC_S].size, msi_s_phandle, intc_phandles, aplic_s_phandle, 0, false, num_harts); - aplic_name = g_strdup_printf("/soc/aplic@%lx", aplic_addr); - if (!socket) { + 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, + s->memmap[VIRT_PLATFORM_BUS].base, + s->memmap[VIRT_PLATFORM_BUS].size, VIRT_PLATFORM_BUS_IRQ); } - g_free(aplic_name); - aplic_phandles[socket] = aplic_s_phandle; } static void create_fdt_pmu(RISCVVirtState *s) { - char *pmu_name; + 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("/pmu"); 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.cfg.pmu_num, pmu_name); - - g_free(pmu_name); + riscv_pmu_generate_fdt_node(ms->fdt, hart.pmu_avail_ctrs, pmu_name); } -static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, +static void create_fdt_sockets(RISCVVirtState *s, 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 *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(ms->fdt, "/cpus"); qemu_fdt_setprop_cell(ms->fdt, "/cpus", "timebase-frequency", + kvm_enabled() ? + kvm_riscv_get_timebase_frequency(&s->soc->harts[0]) : RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ); qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0); qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1); @@ -752,6 +766,7 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, 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); @@ -760,59 +775,54 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, create_fdt_socket_cpus(s, socket, clust_name, phandle, &intc_phandles[phandle_pos]); - create_fdt_socket_memory(s, memmap, socket); + create_fdt_socket_memory(s, socket); - g_free(clust_name); - - if (tcg_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, socket, + &intc_phandles[phandle_pos]); + } else if (tcg_enabled()) { + create_fdt_socket_clint(s, socket, + &intc_phandles[phandle_pos]); } } if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { - create_fdt_imsic(s, memmap, phandle, intc_phandles, - &msi_m_phandle, &msi_s_phandle); + create_fdt_imsic(s, phandle, intc_phandles, + &msi_m_phandle, &msi_s_phandle); *msi_pcie_phandle = msi_s_phandle; } - /* KVM AIA only has one APLIC instance */ - if (kvm_enabled() && virt_use_kvm_aia(s)) { - create_fdt_socket_aplic(s, memmap, 0, + /* + * With KVM AIA aplic-imsic, using an irqchip without split + * mode, we'll use only one APLIC instance. + */ + if (!virt_use_emulated_aplic(s->aia_type)) { + create_fdt_socket_aplic(s, 0, msi_m_phandle, msi_s_phandle, phandle, &intc_phandles[0], xplic_phandles, ms->smp.cpus); + + *irq_mmio_phandle = xplic_phandles[0]; + *irq_virtio_phandle = xplic_phandles[0]; + *irq_pcie_phandle = xplic_phandles[0]; } 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, + create_fdt_socket_plic(s, socket, phandle, &intc_phandles[phandle_pos], xplic_phandles); } else { - create_fdt_socket_aplic(s, memmap, socket, + create_fdt_socket_aplic(s, socket, msi_m_phandle, msi_s_phandle, phandle, &intc_phandles[phandle_pos], xplic_phandles, s->soc[socket].num_harts); } } - } - - g_free(intc_phandles); - 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]; @@ -832,21 +842,24 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, riscv_socket_fdt_write_distance_matrix(ms); } -static void create_fdt_virtio(RISCVVirtState *s, const MemMapEntry *memmap, - uint32_t irq_virtio_phandle) +static void create_fdt_virtio(RISCVVirtState *s, uint32_t irq_virtio_phandle) { int i; - char *name; MachineState *ms = MACHINE(s); + hwaddr virtio_base = s->memmap[VIRT_VIRTIO].base; for (i = 0; i < VIRTIO_COUNT; i++) { - name = g_strdup_printf("/soc/virtio_mmio@%lx", - (long)(memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size)); + g_autofree char *name = NULL; + uint64_t size = s->memmap[VIRT_VIRTIO].size; + hwaddr addr = virtio_base + i * size; + + name = g_strdup_printf("/soc/virtio_mmio@%"HWADDR_PRIx, addr); + 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); + 0x0, addr, + 0x0, size); qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_virtio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { @@ -856,20 +869,19 @@ static void create_fdt_virtio(RISCVVirtState *s, const MemMapEntry *memmap, qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", VIRTIO_IRQ + i, 0x4); } - g_free(name); } } -static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap, +static void create_fdt_pcie(RISCVVirtState *s, uint32_t irq_pcie_phandle, - uint32_t msi_pcie_phandle) + uint32_t msi_pcie_phandle, + uint32_t iommu_sys_phandle) { - char *name; + 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(ms->fdt, name); + name = g_strdup_printf("/soc/pci@%"HWADDR_PRIx, + s->memmap[VIRT_PCIE_ECAM].base); qemu_fdt_setprop_cell(ms->fdt, name, "#address-cells", FDT_PCI_ADDR_CELLS); qemu_fdt_setprop_cell(ms->fdt, name, "#interrupt-cells", @@ -880,37 +892,41 @@ static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap, 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); + s->memmap[VIRT_PCIE_ECAM].size / PCIE_MMCFG_SIZE_MIN - 1); qemu_fdt_setprop(ms->fdt, name, "dma-coherent", NULL, 0); if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { qemu_fdt_setprop_cell(ms->fdt, name, "msi-parent", msi_pcie_phandle); } qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0, - memmap[VIRT_PCIE_ECAM].base, 0, memmap[VIRT_PCIE_ECAM].size); + s->memmap[VIRT_PCIE_ECAM].base, 0, s->memmap[VIRT_PCIE_ECAM].size); 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, + 2, s->memmap[VIRT_PCIE_PIO].base, 2, s->memmap[VIRT_PCIE_PIO].size, 1, FDT_PCI_RANGE_MMIO, - 2, memmap[VIRT_PCIE_MMIO].base, - 2, memmap[VIRT_PCIE_MMIO].base, 2, memmap[VIRT_PCIE_MMIO].size, + 2, s->memmap[VIRT_PCIE_MMIO].base, + 2, s->memmap[VIRT_PCIE_MMIO].base, 2, s->memmap[VIRT_PCIE_MMIO].size, 1, FDT_PCI_RANGE_MMIO_64BIT, 2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.size); + if (virt_is_iommu_sys_enabled(s)) { + qemu_fdt_setprop_cells(ms->fdt, name, "iommu-map", + 0, iommu_sys_phandle, 0, 0, 0, + iommu_sys_phandle, 0, 0xffff); + } + create_pcie_irq_map(s, ms->fdt, name, irq_pcie_phandle); - g_free(name); } -static void create_fdt_reset(RISCVVirtState *s, const MemMapEntry *memmap, - uint32_t *phandle) +static void create_fdt_reset(RISCVVirtState *s, uint32_t *phandle) { char *name; uint32_t test_phandle; MachineState *ms = MACHINE(s); test_phandle = (*phandle)++; - name = g_strdup_printf("/soc/test@%lx", - (long)memmap[VIRT_TEST].base); + name = g_strdup_printf("/soc/test@%"HWADDR_PRIx, + s->memmap[VIRT_TEST].base); qemu_fdt_add_subnode(ms->fdt, name); { static const char * const compat[3] = { @@ -920,7 +936,7 @@ static void create_fdt_reset(RISCVVirtState *s, const MemMapEntry *memmap, (char **)&compat, ARRAY_SIZE(compat)); } qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, memmap[VIRT_TEST].base, 0x0, memmap[VIRT_TEST].size); + 0x0, s->memmap[VIRT_TEST].base, 0x0, s->memmap[VIRT_TEST].size); qemu_fdt_setprop_cell(ms->fdt, name, "phandle", test_phandle); test_phandle = qemu_fdt_get_phandle(ms->fdt, name); g_free(name); @@ -942,18 +958,19 @@ static void create_fdt_reset(RISCVVirtState *s, const MemMapEntry *memmap, g_free(name); } -static void create_fdt_uart(RISCVVirtState *s, const MemMapEntry *memmap, +static void create_fdt_uart(RISCVVirtState *s, uint32_t irq_mmio_phandle) { - char *name; + g_autofree char *name = NULL; MachineState *ms = MACHINE(s); - name = g_strdup_printf("/soc/serial@%lx", (long)memmap[VIRT_UART0].base); + name = g_strdup_printf("/soc/serial@%"HWADDR_PRIx, + s->memmap[VIRT_UART0].base); qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "ns16550a"); qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, memmap[VIRT_UART0].base, - 0x0, memmap[VIRT_UART0].size); + 0x0, s->memmap[VIRT_UART0].base, + 0x0, s->memmap[VIRT_UART0].size); 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) { @@ -962,23 +979,23 @@ static void create_fdt_uart(RISCVVirtState *s, const MemMapEntry *memmap, qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", UART0_IRQ, 0x4); } - qemu_fdt_add_subnode(ms->fdt, "/chosen"); qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", name); - g_free(name); + qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", name); } -static void create_fdt_rtc(RISCVVirtState *s, const MemMapEntry *memmap, +static void create_fdt_rtc(RISCVVirtState *s, uint32_t irq_mmio_phandle) { - char *name; + g_autofree char *name = NULL; MachineState *ms = MACHINE(s); - name = g_strdup_printf("/soc/rtc@%lx", (long)memmap[VIRT_RTC].base); + name = g_strdup_printf("/soc/rtc@%"HWADDR_PRIx, + s->memmap[VIRT_RTC].base); qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "google,goldfish-rtc"); qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, memmap[VIRT_RTC].base, 0x0, memmap[VIRT_RTC].size); + 0x0, s->memmap[VIRT_RTC].base, 0x0, s->memmap[VIRT_RTC].size); qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_mmio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { @@ -986,49 +1003,163 @@ static void create_fdt_rtc(RISCVVirtState *s, const MemMapEntry *memmap, } else { qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", RTC_IRQ, 0x4); } - g_free(name); } -static void create_fdt_flash(RISCVVirtState *s, const MemMapEntry *memmap) +static void create_fdt_flash(RISCVVirtState *s) { - char *name; MachineState *ms = MACHINE(s); - hwaddr flashsize = virt_memmap[VIRT_FLASH].size / 2; - hwaddr flashbase = virt_memmap[VIRT_FLASH].base; + hwaddr flashsize = s->memmap[VIRT_FLASH].size / 2; + hwaddr flashbase = s->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(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(ms->fdt, name, "bank-width", 4); - g_free(name); } -static void create_fdt_fw_cfg(RISCVVirtState *s, const MemMapEntry *memmap) +static void create_fdt_fw_cfg(RISCVVirtState *s) { - char *nodename; MachineState *ms = MACHINE(s); - hwaddr base = memmap[VIRT_FW_CFG].base; - hwaddr size = memmap[VIRT_FW_CFG].size; + hwaddr base = s->memmap[VIRT_FW_CFG].base; + hwaddr size = s->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(ms->fdt, nodename); qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "qemu,fw-cfg-mmio"); qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); - g_free(nodename); } -static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap) +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@%"HWADDR_PRIx, + s->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_sys(RISCVVirtState *s, uint32_t irq_chip, + uint32_t msi_phandle, + uint32_t *iommu_sys_phandle) +{ + const char comp[] = "riscv,iommu"; + void *fdt = MACHINE(s)->fdt; + uint32_t iommu_phandle; + g_autofree char *iommu_node = NULL; + hwaddr addr = s->memmap[VIRT_IOMMU_SYS].base; + hwaddr size = s->memmap[VIRT_IOMMU_SYS].size; + uint32_t iommu_irq_map[RISCV_IOMMU_INTR_COUNT] = { + IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_CQ, + IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_FQ, + IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_PM, + IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_PQ, + }; + + iommu_node = g_strdup_printf("/soc/iommu@%x", + (unsigned int) s->memmap[VIRT_IOMMU_SYS].base); + 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", + addr >> 32, addr, size >> 32, size); + qemu_fdt_setprop_cell(fdt, iommu_node, "interrupt-parent", irq_chip); + + qemu_fdt_setprop_cells(fdt, iommu_node, "interrupts", + iommu_irq_map[0], FDT_IRQ_TYPE_EDGE_LOW, + iommu_irq_map[1], FDT_IRQ_TYPE_EDGE_LOW, + iommu_irq_map[2], FDT_IRQ_TYPE_EDGE_LOW, + iommu_irq_map[3], FDT_IRQ_TYPE_EDGE_LOW); + + qemu_fdt_setprop_cell(fdt, iommu_node, "msi-parent", msi_phandle); + + *iommu_sys_phandle = iommu_phandle; +} + +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@%"HWADDR_PRIx, + s->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); + s->pci_iommu_bdf = bdf; +} + +static void finalize_fdt(RISCVVirtState *s) { - MachineState *ms = MACHINE(s); uint32_t phandle = 1, irq_mmio_phandle = 1, msi_pcie_phandle = 1; uint32_t irq_pcie_phandle = 1, irq_virtio_phandle = 1; + uint32_t iommu_sys_phandle = 1; + + create_fdt_sockets(s, &phandle, &irq_mmio_phandle, + &irq_pcie_phandle, &irq_virtio_phandle, + &msi_pcie_phandle); + + create_fdt_virtio(s, irq_virtio_phandle); + + if (virt_is_iommu_sys_enabled(s)) { + create_fdt_iommu_sys(s, irq_mmio_phandle, msi_pcie_phandle, + &iommu_sys_phandle); + } + create_fdt_pcie(s, irq_pcie_phandle, msi_pcie_phandle, + iommu_sys_phandle); + + create_fdt_reset(s, &phandle); + + create_fdt_uart(s, irq_mmio_phandle); + + create_fdt_rtc(s, irq_mmio_phandle); +} + +static void create_fdt(RISCVVirtState *s) +{ + 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) { @@ -1047,46 +1178,66 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap) 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, &phandle, &irq_mmio_phandle, - &irq_pcie_phandle, &irq_virtio_phandle, - &msi_pcie_phandle); - - create_fdt_virtio(s, memmap, irq_virtio_phandle); - - 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); + /* + * The "/soc/pci@..." node is needed for PCIE hotplugs + * that might happen before finalize_fdt(). + */ + name = g_strdup_printf("/soc/pci@%"HWADDR_PRIx, + s->memmap[VIRT_PCIE_ECAM].base); + qemu_fdt_add_subnode(ms->fdt, name); - create_fdt_flash(s, memmap); - create_fdt_fw_cfg(s, memmap); - create_fdt_pmu(s); + 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)); + + qemu_fdt_add_subnode(ms->fdt, "/aliases"); + + create_fdt_flash(s); + create_fdt_fw_cfg(s); + create_fdt_pmu(s); } static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem, - hwaddr ecam_base, hwaddr ecam_size, - hwaddr mmio_base, hwaddr mmio_size, - hwaddr high_mmio_base, - hwaddr high_mmio_size, - hwaddr pio_base, - DeviceState *irqchip) + DeviceState *irqchip, + RISCVVirtState *s) { DeviceState *dev; MemoryRegion *ecam_alias, *ecam_reg; MemoryRegion *mmio_alias, *high_mmio_alias, *mmio_reg; + hwaddr ecam_base = s->memmap[VIRT_PCIE_ECAM].base; + hwaddr ecam_size = s->memmap[VIRT_PCIE_ECAM].size; + hwaddr mmio_base = s->memmap[VIRT_PCIE_MMIO].base; + hwaddr mmio_size = s->memmap[VIRT_PCIE_MMIO].size; + hwaddr high_mmio_base = virt_high_pcie_memmap.base; + hwaddr high_mmio_size = virt_high_pcie_memmap.size; + hwaddr pio_base = s->memmap[VIRT_PCIE_PIO].base; + hwaddr pio_size = s->memmap[VIRT_PCIE_PIO].size; qemu_irq irq; int i; dev = qdev_new(TYPE_GPEX_HOST); + /* Set GPEX object properties for the virt machine */ + object_property_set_uint(OBJECT(dev), PCI_HOST_ECAM_BASE, + ecam_base, NULL); + object_property_set_int(OBJECT(dev), PCI_HOST_ECAM_SIZE, + ecam_size, NULL); + object_property_set_uint(OBJECT(dev), PCI_HOST_BELOW_4G_MMIO_BASE, + mmio_base, NULL); + object_property_set_int(OBJECT(dev), PCI_HOST_BELOW_4G_MMIO_SIZE, + mmio_size, NULL); + object_property_set_uint(OBJECT(dev), PCI_HOST_ABOVE_4G_MMIO_BASE, + high_mmio_base, NULL); + object_property_set_int(OBJECT(dev), PCI_HOST_ABOVE_4G_MMIO_SIZE, + high_mmio_size, NULL); + object_property_set_uint(OBJECT(dev), PCI_HOST_PIO_BASE, + pio_base, NULL); + object_property_set_int(OBJECT(dev), PCI_HOST_PIO_SIZE, + pio_size, NULL); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); ecam_alias = g_new0(MemoryRegion, 1); @@ -1110,19 +1261,19 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem, sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, pio_base); - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { irq = qdev_get_gpio_in(irqchip, PCIE_IRQ + i); sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); gpex_set_irq_num(GPEX_HOST(dev), i, PCIE_IRQ + i); } + GPEX_HOST(dev)->gpex_cfg.bus = PCI_HOST_BRIDGE(dev)->bus; return dev; } -static FWCfgState *create_fw_cfg(const MachineState *ms) +static FWCfgState *create_fw_cfg(const MachineState *ms, hwaddr base) { - hwaddr base = virt_memmap[VIRT_FW_CFG].base; FWCfgState *fw_cfg; fw_cfg = fw_cfg_init_mem_wide(base + 8, base, 8, base + 16, @@ -1135,29 +1286,22 @@ static FWCfgState *create_fw_cfg(const MachineState *ms) 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); /* Per-socket PLIC */ - ret = sifive_plic_create( - memmap[VIRT_PLIC].base + socket * memmap[VIRT_PLIC].size, - plic_hart_config, hart_count, base_hartid, - VIRT_IRQCHIP_NUM_SOURCES, - ((1U << VIRT_IRQCHIP_NUM_PRIO_BITS) - 1), - VIRT_PLIC_PRIORITY_BASE, - VIRT_PLIC_PENDING_BASE, - VIRT_PLIC_ENABLE_BASE, - VIRT_PLIC_ENABLE_STRIDE, - VIRT_PLIC_CONTEXT_BASE, - VIRT_PLIC_CONTEXT_STRIDE, - memmap[VIRT_PLIC].size); - - g_free(plic_hart_config); - - return ret; + return sifive_plic_create( + memmap[VIRT_PLIC].base + socket * memmap[VIRT_PLIC].size, + plic_hart_config, hart_count, base_hartid, + VIRT_IRQCHIP_NUM_SOURCES, + ((1U << VIRT_IRQCHIP_NUM_PRIO_BITS) - 1), + VIRT_PLIC_PRIORITY_BASE, VIRT_PLIC_PENDING_BASE, + VIRT_PLIC_ENABLE_BASE, VIRT_PLIC_ENABLE_STRIDE, + VIRT_PLIC_CONTEXT_BASE, + VIRT_PLIC_CONTEXT_STRIDE, + memmap[VIRT_PLIC].size); } static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests, @@ -1165,7 +1309,7 @@ static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests, int base_hartid, int hart_count) { int i; - hwaddr addr; + hwaddr addr = 0; uint32_t guest_bits; DeviceState *aplic_s = NULL; DeviceState *aplic_m = NULL; @@ -1215,6 +1359,10 @@ static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests, VIRT_IRQCHIP_NUM_PRIO_BITS, msimode, false, aplic_m); + if (kvm_enabled() && msimode) { + riscv_aplic_set_kvm_msicfgaddr(RISCV_APLIC(aplic_s), addr); + } + return kvm_enabled() ? aplic_s : aplic_m; } @@ -1222,14 +1370,13 @@ static void create_platform_bus(RISCVVirtState *s, DeviceState *irqchip) { DeviceState *dev; SysBusDevice *sysbus; - const MemMapEntry *memmap = virt_memmap; int i; MemoryRegion *sysmem = get_system_memory(); dev = qdev_new(TYPE_PLATFORM_BUS_DEVICE); dev->id = g_strdup(TYPE_PLATFORM_BUS_DEVICE); qdev_prop_set_uint32(dev, "num_irqs", VIRT_PLATFORM_BUS_NUM_IRQS); - qdev_prop_set_uint32(dev, "mmio_size", memmap[VIRT_PLATFORM_BUS].size); + qdev_prop_set_uint32(dev, "mmio_size", s->memmap[VIRT_PLATFORM_BUS].size); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); s->platform_bus_dev = dev; @@ -1240,32 +1387,68 @@ static void create_platform_bus(RISCVVirtState *s, DeviceState *irqchip) } memory_region_add_subregion(sysmem, - memmap[VIRT_PLATFORM_BUS].base, + s->memmap[VIRT_PLATFORM_BUS].base, 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 = s->memmap[VIRT_DRAM].base; target_ulong firmware_end_addr, kernel_start_addr; const char *firmware_name = riscv_default_firmware_name(&s->soc[0]); uint64_t fdt_load_addr; uint64_t kernel_entry = 0; BlockBackend *pflash_blk0; + RISCVBootInfo boot_info; - /* 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); + /* + * An user provided dtb must include everything, including + * dynamic sysbus devices. Our FDT needs to be finalized. + */ + if (machine->dtb == NULL) { + finalize_fdt(s); } /* @@ -1285,7 +1468,7 @@ static void virt_machine_done(Notifier *notifier, void *data) } firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, - start_addr, NULL); + &start_addr, NULL); pflash_blk0 = pflash_cfi01_get_blk(s->flash[0]); if (pflash_blk0) { @@ -1296,34 +1479,36 @@ static void virt_machine_done(Notifier *notifier, void *data) * let's overwrite the address we jump to after reset to * the base of the flash. */ - start_addr = virt_memmap[VIRT_FLASH].base; + start_addr = s->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; + kernel_entry = s->memmap[VIRT_FLASH].base; } } + riscv_boot_info_init(&boot_info, &s->soc[0]); + if (machine->kernel_filename && !kernel_entry) { - kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0], + kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); - - kernel_entry = riscv_load_kernel(machine, &s->soc[0], - kernel_start_addr, true, NULL); + riscv_load_kernel(machine, &boot_info, kernel_start_addr, + true, NULL); + kernel_entry = boot_info.image_low_addr; } - fdt_load_addr = riscv_compute_fdt_addr(memmap[VIRT_DRAM].base, - memmap[VIRT_DRAM].size, - machine); + fdt_load_addr = riscv_compute_fdt_addr(s->memmap[VIRT_DRAM].base, + s->memmap[VIRT_DRAM].size, + machine, &boot_info); riscv_load_fdt(fdt_load_addr, machine->fdt); /* load the reset vector */ riscv_setup_rom_reset_vec(machine, &s->soc[0], start_addr, - virt_memmap[VIRT_MROM].base, - virt_memmap[VIRT_MROM].size, kernel_entry, + s->memmap[VIRT_MROM].base, + s->memmap[VIRT_MROM].size, kernel_entry, fdt_load_addr); /* @@ -1335,6 +1520,8 @@ static void virt_machine_done(Notifier *notifier, void *data) riscv_setup_direct_kernel(kernel_entry, fdt_load_addr); } + virt_build_smbios(s); + if (virt_is_acpi_enabled(s)) { virt_acpi_setup(s); } @@ -1342,15 +1529,15 @@ static void virt_machine_done(Notifier *notifier, void *data) static void virt_machine_init(MachineState *machine) { - const MemMapEntry *memmap = virt_memmap; 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); + s->memmap = virt_memmap; + /* Check socket count limit */ if (VIRT_SOCKETS_MAX < socket_count) { error_report("number of sockets/nodes should be less than %d", @@ -1358,7 +1545,7 @@ static void virt_machine_init(MachineState *machine) exit(1); } - if (!tcg_enabled() && s->have_aclint) { + if (!virt_aclint_allowed() && s->have_aclint) { error_report("'aclint' is only available with TCG acceleration"); exit(1); } @@ -1366,6 +1553,8 @@ static void virt_machine_init(MachineState *machine) /* Initialize sockets */ mmio_irqchip = virtio_irqchip = pcie_irqchip = NULL; 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); @@ -1383,10 +1572,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", @@ -1395,54 +1582,52 @@ static void virt_machine_init(MachineState *machine) hart_count, &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_fatal); - if (tcg_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(s->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 + - i * memmap[VIRT_CLINT].size, + } else { + /* Per-socket ACLINT MSWI, MTIMER, and SSWI */ + riscv_aclint_swi_create(s->memmap[VIRT_CLINT].base + + i * s->memmap[VIRT_CLINT].size, base_hartid, hart_count, false); - riscv_aclint_mtimer_create(memmap[VIRT_CLINT].base + - i * memmap[VIRT_CLINT].size + + riscv_aclint_mtimer_create(s->memmap[VIRT_CLINT].base + + i * s->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); - riscv_aclint_swi_create(memmap[VIRT_ACLINT_SSWI].base + - i * memmap[VIRT_ACLINT_SSWI].size, + riscv_aclint_swi_create(s->memmap[VIRT_ACLINT_SSWI].base + + i * s->memmap[VIRT_ACLINT_SSWI].size, base_hartid, hart_count, true); - } - } else { - /* Per-socket SiFive CLINT */ - riscv_aclint_swi_create( - memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size, + } + } else if (tcg_enabled()) { + /* Per-socket SiFive CLINT */ + riscv_aclint_swi_create( + s->memmap[VIRT_CLINT].base + i * s->memmap[VIRT_CLINT].size, base_hartid, hart_count, false); - riscv_aclint_mtimer_create(memmap[VIRT_CLINT].base + - i * memmap[VIRT_CLINT].size + RISCV_ACLINT_SWI_SIZE, + riscv_aclint_mtimer_create(s->memmap[VIRT_CLINT].base + + i * s->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 */ if (s->aia_type == VIRT_AIA_TYPE_NONE) { - s->irqchip[i] = virt_create_plic(memmap, i, + s->irqchip[i] = virt_create_plic(s->memmap, i, base_hartid, hart_count); } else { s->irqchip[i] = virt_create_aia(s->aia_type, s->aia_guests, - memmap, i, base_hartid, + s->memmap, i, base_hartid, hart_count); } @@ -1461,11 +1646,11 @@ static void virt_machine_init(MachineState *machine) } } - if (kvm_enabled() && virt_use_kvm_aia(s)) { + if (kvm_enabled() && virt_use_kvm_aia_aplic_imsic(s->aia_type)) { 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->memmap[VIRT_APLIC_S].base, + s->memmap[VIRT_IMSIC_S].base, s->aia_guests); } @@ -1481,57 +1666,48 @@ static void virt_machine_init(MachineState *machine) virt_high_pcie_memmap.size = VIRT32_HIGH_PCIE_MMIO_SIZE; } else { virt_high_pcie_memmap.size = VIRT64_HIGH_PCIE_MMIO_SIZE; - virt_high_pcie_memmap.base = memmap[VIRT_DRAM].base + machine->ram_size; + virt_high_pcie_memmap.base = s->memmap[VIRT_DRAM].base + + machine->ram_size; virt_high_pcie_memmap.base = 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); + memory_region_add_subregion(system_memory, s->memmap[VIRT_DRAM].base, + machine->ram); /* boot rom */ memory_region_init_rom(mask_rom, NULL, "riscv_virt_board.mrom", - memmap[VIRT_MROM].size, &error_fatal); - memory_region_add_subregion(system_memory, memmap[VIRT_MROM].base, + s->memmap[VIRT_MROM].size, &error_fatal); + memory_region_add_subregion(system_memory, s->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); + s->fw_cfg = create_fw_cfg(machine, s->memmap[VIRT_FW_CFG].base); rom_set_fw(s->fw_cfg); /* SiFive Test MMIO device */ - sifive_test_create(memmap[VIRT_TEST].base); + sifive_test_create(s->memmap[VIRT_TEST].base); /* VirtIO MMIO devices */ for (i = 0; i < VIRTIO_COUNT; i++) { sysbus_create_simple("virtio-mmio", - memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size, + s->memmap[VIRT_VIRTIO].base + i * s->memmap[VIRT_VIRTIO].size, 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, - pcie_irqchip); + gpex_pcie_init(system_memory, pcie_irqchip, s); create_platform_bus(s, mmio_irqchip); - serial_mm_init(system_memory, memmap[VIRT_UART0].base, + serial_mm_init(system_memory, s->memmap[VIRT_UART0].base, 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, + sysbus_create_simple("goldfish_rtc", s->memmap[VIRT_RTC].base, qdev_get_gpio_in(mmio_irqchip, RTC_IRQ)); for (i = 0; i < ARRAY_SIZE(s->flash); i++) { @@ -1541,6 +1717,33 @@ static void virt_machine_init(MachineState *machine) } virt_flash_map(s, system_memory); + /* 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); + } + + if (virt_is_iommu_sys_enabled(s)) { + DeviceState *iommu_sys = qdev_new(TYPE_RISCV_IOMMU_SYS); + + object_property_set_uint(OBJECT(iommu_sys), "addr", + s->memmap[VIRT_IOMMU_SYS].base, + &error_fatal); + object_property_set_uint(OBJECT(iommu_sys), "base-irq", + IOMMU_SYS_IRQ, + &error_fatal); + object_property_set_link(OBJECT(iommu_sys), "irqchip", + OBJECT(mmio_irqchip), + &error_fatal); + + sysbus_realize_and_unref(SYS_BUS_DEVICE(iommu_sys), &error_fatal); + } + s->machine_done.notify = virt_machine_done; qemu_add_machine_init_done_notifier(&s->machine_done); } @@ -1554,15 +1757,14 @@ static void virt_machine_instance_init(Object *obj) 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; + s->iommu_sys = 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) @@ -1628,6 +1830,28 @@ static void virt_set_aclint(Object *obj, bool value, Error **errp) s->have_aclint = value; } +bool virt_is_iommu_sys_enabled(RISCVVirtState *s) +{ + return s->iommu_sys == ON_OFF_AUTO_ON; +} + +static void virt_get_iommu_sys(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + OnOffAuto iommu_sys = s->iommu_sys; + + visit_type_OnOffAuto(v, name, &iommu_sys, errp); +} + +static void virt_set_iommu_sys(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &s->iommu_sys, errp); +} + bool virt_is_acpi_enabled(RISCVVirtState *s) { return s->acpi != ON_OFF_AUTO_OFF; @@ -1654,10 +1878,15 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, DeviceState *dev) { MachineClass *mc = MACHINE_GET_CLASS(machine); + RISCVVirtState *s = RISCV_VIRT_MACHINE(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)) { + s->iommu_sys = ON_OFF_AUTO_OFF; return HOTPLUG_HANDLER(machine); } + return NULL; } @@ -1674,11 +1903,19 @@ 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))); + s->iommu_sys = ON_OFF_AUTO_OFF; + } } -static void virt_machine_class_init(ObjectClass *oc, void *data) +static void virt_machine_class_init(ObjectClass *oc, const void *data) { - char str[128]; MachineClass *mc = MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); @@ -1686,6 +1923,8 @@ 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; @@ -1700,11 +1939,11 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) hc->plug = virt_machine_device_plug_cb; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif - object_class_property_add_bool(oc, "aclint", virt_get_aclint, virt_set_aclint); object_class_property_set_description(oc, "aclint", @@ -1722,14 +1961,25 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) 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"); + + object_class_property_add(oc, "iommu-sys", "OnOffAuto", + virt_get_iommu_sys, virt_set_iommu_sys, + NULL, NULL); + object_class_property_set_description(oc, "iommu-sys", + "Enable IOMMU platform device"); } static const TypeInfo virt_machine_typeinfo = { @@ -1738,7 +1988,7 @@ static const TypeInfo virt_machine_typeinfo = { .class_init = virt_machine_class_init, .instance_init = virt_machine_instance_init, .instance_size = sizeof(RISCVVirtState), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } }, diff --git a/hw/rtc/Kconfig b/hw/rtc/Kconfig index d0d8dda0840..315b0e4eccd 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 @@ -30,3 +26,8 @@ config GOLDFISH_RTC config LS7A_RTC bool + +config RS5C372_RTC + bool + depends on I2C + default y if I2C_DEVICES diff --git a/hw/rtc/allwinner-rtc.c b/hw/rtc/allwinner-rtc.c index 7e493f0e79d..a747bff534c 100644 --- a/hw/rtc/allwinner-rtc.c +++ b/hw/rtc/allwinner-rtc.c @@ -25,7 +25,7 @@ #include "qemu/module.h" #include "hw/qdev-properties.h" #include "hw/rtc/allwinner-rtc.h" -#include "sysemu/rtc.h" +#include "system/rtc.h" #include "trace.h" /* RTC registers */ @@ -259,7 +259,7 @@ static void allwinner_rtc_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_rtc_ops = { .read = allwinner_rtc_read, .write = allwinner_rtc_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -305,22 +305,21 @@ 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() } }; -static Property allwinner_rtc_properties[] = { +static const Property allwinner_rtc_properties[] = { DEFINE_PROP_INT32("base-year", AwRtcState, base_year, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void allwinner_rtc_class_init(ObjectClass *klass, void *data) +static void allwinner_rtc_class_init(ObjectClass *klass, const 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); } @@ -331,7 +330,7 @@ static void allwinner_rtc_sun4i_init(Object *obj) s->base_year = 2010; } -static void allwinner_rtc_sun4i_class_init(ObjectClass *klass, void *data) +static void allwinner_rtc_sun4i_class_init(ObjectClass *klass, const void *data) { AwRtcClass *arc = AW_RTC_CLASS(klass); @@ -347,7 +346,7 @@ static void allwinner_rtc_sun6i_init(Object *obj) s->base_year = 1970; } -static void allwinner_rtc_sun6i_class_init(ObjectClass *klass, void *data) +static void allwinner_rtc_sun6i_class_init(ObjectClass *klass, const void *data) { AwRtcClass *arc = AW_RTC_CLASS(klass); @@ -363,7 +362,7 @@ static void allwinner_rtc_sun7i_init(Object *obj) s->base_year = 1970; } -static void allwinner_rtc_sun7i_class_init(ObjectClass *klass, void *data) +static void allwinner_rtc_sun7i_class_init(ObjectClass *klass, const void *data) { AwRtcClass *arc = AW_RTC_CLASS(klass); allwinner_rtc_sun4i_class_init(klass, arc); diff --git a/hw/rtc/aspeed_rtc.c b/hw/rtc/aspeed_rtc.c index fa861e2d494..c4feea23a0b 100644 --- a/hw/rtc/aspeed_rtc.c +++ b/hw/rtc/aspeed_rtc.c @@ -11,7 +11,7 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/timer.h" -#include "sysemu/rtc.h" +#include "system/rtc.h" #include "trace.h" @@ -137,7 +137,7 @@ static const MemoryRegionOps aspeed_rtc_ops = { static const VMStateDescription vmstate_aspeed_rtc = { .name = TYPE_ASPEED_RTC, .version_id = 2, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(reg, AspeedRtcState, 0x18), VMSTATE_INT64(offset, AspeedRtcState), VMSTATE_END_OF_LIST() @@ -156,13 +156,13 @@ static void aspeed_rtc_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); } -static void aspeed_rtc_class_init(ObjectClass *klass, void *data) +static void aspeed_rtc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 36d8121ddda..5f1ee2e410a 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 "system/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; @@ -215,7 +220,7 @@ static void ds1338_reset(DeviceState *dev) s->addr_byte = false; } -static void ds1338_class_init(ObjectClass *klass, void *data) +static void ds1338_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); @@ -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 2b8a38a2969..624b4f61a85 100644 --- a/hw/rtc/exynos4210_rtc.c +++ b/hw/rtc/exynos4210_rtc.c @@ -38,7 +38,7 @@ #include "hw/arm/exynos4210.h" #include "qom/object.h" -#include "sysemu/rtc.h" +#include "system/rtc.h" #define DEBUG_RTC 0 @@ -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) { @@ -592,11 +592,11 @@ static void exynos4210_rtc_finalize(Object *obj) ptimer_free(s->ptimer_1Hz); } -static void exynos4210_rtc_class_init(ObjectClass *klass, void *data) +static void exynos4210_rtc_class_init(ObjectClass *klass, const 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 19a56402a0c..78df031cf29 100644 --- a/hw/rtc/goldfish_rtc.c +++ b/hw/rtc/goldfish_rtc.c @@ -27,8 +27,8 @@ #include "hw/sysbus.h" #include "qemu/bitops.h" #include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "sysemu/rtc.h" +#include "system/system.h" +#include "system/rtc.h" #include "qemu/cutils.h" #include "qemu/log.h" @@ -178,38 +178,21 @@ static void goldfish_rtc_write(void *opaque, hwaddr offset, trace_goldfish_rtc_write(offset, value); } -static int goldfish_rtc_pre_save(void *opaque) -{ - uint64_t delta; - GoldfishRTCState *s = opaque; - - /* - * We want to migrate this offset, which sounds straightforward. - * Unfortunately, we cannot directly pass tick_offset because - * rtc_clock on destination Host might not be same source Host. - * - * To tackle, this we pass tick_offset relative to vm_clock from - * source Host and make it relative to rtc_clock at destination Host. - */ - delta = qemu_clock_get_ns(rtc_clock) - - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->tick_offset_vmstate = s->tick_offset + delta; - - return 0; -} - static int goldfish_rtc_post_load(void *opaque, int version_id) { - uint64_t delta; GoldfishRTCState *s = opaque; - /* - * We extract tick_offset from tick_offset_vmstate by doing - * reverse math compared to pre_save() function. - */ - delta = qemu_clock_get_ns(rtc_clock) - - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->tick_offset = s->tick_offset_vmstate - delta; + if (version_id < 3) { + /* + * Previous versions didn't migrate tick_offset directly. Instead, they + * migrated tick_offset_vmstate, which is a recalculation based on + * QEMU_CLOCK_VIRTUAL. We use tick_offset_vmstate when migrating from + * older versions. + */ + uint64_t delta = qemu_clock_get_ns(rtc_clock) - + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + s->tick_offset = s->tick_offset_vmstate - delta; + } goldfish_rtc_set_alarm(s); @@ -239,16 +222,16 @@ static const MemoryRegionOps goldfish_rtc_ops[2] = { static const VMStateDescription goldfish_rtc_vmstate = { .name = TYPE_GOLDFISH_RTC, - .version_id = 2, - .pre_save = goldfish_rtc_pre_save, + .version_id = 3, .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), VMSTATE_UINT32(irq_pending, GoldfishRTCState), VMSTATE_UINT32(irq_enabled, GoldfishRTCState), VMSTATE_UINT32(time_high, GoldfishRTCState), + VMSTATE_UINT64_V(tick_offset, GoldfishRTCState, 3), VMSTATE_END_OF_LIST() } }; @@ -256,15 +239,8 @@ static const VMStateDescription goldfish_rtc_vmstate = { static void goldfish_rtc_reset(DeviceState *dev) { GoldfishRTCState *s = GOLDFISH_RTC(dev); - struct tm tm; timer_del(s->timer); - - qemu_get_timedate(&tm, 0); - s->tick_offset = mktimegm(&tm); - s->tick_offset *= NANOSECONDS_PER_SECOND; - s->tick_offset -= qemu_clock_get_ns(rtc_clock); - s->tick_offset_vmstate = 0; s->alarm_next = 0; s->alarm_running = 0; s->irq_pending = 0; @@ -275,6 +251,7 @@ static void goldfish_rtc_realize(DeviceState *d, Error **errp) { SysBusDevice *dev = SYS_BUS_DEVICE(d); GoldfishRTCState *s = GOLDFISH_RTC(d); + struct tm tm; memory_region_init_io(&s->iomem, OBJECT(s), &goldfish_rtc_ops[s->big_endian], s, @@ -284,21 +261,25 @@ static void goldfish_rtc_realize(DeviceState *d, Error **errp) sysbus_init_irq(dev, &s->irq); s->timer = timer_new_ns(rtc_clock, goldfish_rtc_interrupt, s); + + qemu_get_timedate(&tm, 0); + s->tick_offset = mktimegm(&tm); + s->tick_offset *= NANOSECONDS_PER_SECOND; + s->tick_offset -= qemu_clock_get_ns(rtc_clock); } -static Property goldfish_rtc_properties[] = { +static const Property goldfish_rtc_properties[] = { DEFINE_PROP_BOOL("big-endian", GoldfishRTCState, big_endian, false), - DEFINE_PROP_END_OF_LIST(), }; -static void goldfish_rtc_class_init(ObjectClass *klass, void *data) +static void goldfish_rtc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_props(dc, goldfish_rtc_properties); dc->realize = goldfish_rtc_realize; - dc->reset = goldfish_rtc_reset; + 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 1f9e38a735b..10097b2db75 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,14 +8,14 @@ #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 "system/system.h" #include "qemu/cutils.h" #include "qemu/log.h" #include "migration/vmstate.h" #include "hw/misc/unimp.h" -#include "sysemu/rtc.h" +#include "system/rtc.h" #include "hw/registerfields.h" #define SYS_TOYTRIM 0x20 @@ -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), @@ -464,12 +464,12 @@ static const VMStateDescription vmstate_ls7a_rtc = { } }; -static void ls7a_rtc_class_init(ObjectClass *klass, void *data) +static void ls7a_rtc_class_init(ObjectClass *klass, const 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/m41t80.c b/hw/rtc/m41t80.c index e045c864bb4..c631ec3ea1d 100644 --- a/hw/rtc/m41t80.c +++ b/hw/rtc/m41t80.c @@ -14,7 +14,7 @@ #include "qemu/bcd.h" #include "hw/i2c/i2c.h" #include "qom/object.h" -#include "sysemu/rtc.h" +#include "system/rtc.h" #define TYPE_M41T80 "m41t80" OBJECT_DECLARE_SIMPLE_TYPE(M41t80State, M41T80) @@ -94,7 +94,7 @@ static int m41t80_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static void m41t80_class_init(ObjectClass *klass, void *data) +static void m41t80_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); diff --git a/hw/rtc/m48t59-isa.c b/hw/rtc/m48t59-isa.c index 5bb46f23838..9e2f6563a0a 100644 --- a/hw/rtc/m48t59-isa.c +++ b/hw/rtc/m48t59-isa.c @@ -77,11 +77,10 @@ static void m48txx_isa_toggle_lock(Nvram *obj, int lock) m48t59_toggle_lock(&d->state, lock); } -static Property m48t59_isa_properties[] = { +static const Property m48t59_isa_properties[] = { DEFINE_PROP_INT32("base-year", M48txxISAState, state.base_year, 0), DEFINE_PROP_UINT32("iobase", M48txxISAState, io_base, 0x74), DEFINE_PROP_UINT8("irq", M48txxISAState, isairq, 8), - DEFINE_PROP_END_OF_LIST(), }; static void m48t59_reset_isa(DeviceState *d) @@ -114,23 +113,23 @@ static void m48t59_isa_realize(DeviceState *dev, Error **errp) } } -static void m48txx_isa_class_init(ObjectClass *klass, void *data) +static void m48txx_isa_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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; nc->toggle_lock = m48txx_isa_toggle_lock; } -static void m48txx_isa_concrete_class_init(ObjectClass *klass, void *data) +static void m48txx_isa_concrete_class_init(ObjectClass *klass, const void *data) { M48txxISADeviceClass *u = M48TXX_ISA_CLASS(klass); - M48txxInfo *info = data; + const M48txxInfo *info = data; u->info = *info; } @@ -141,7 +140,7 @@ static const TypeInfo m48txx_isa_type_info = { .instance_size = sizeof(M48txxISAState), .abstract = true, .class_init = m48txx_isa_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NVRAM }, { } } @@ -161,7 +160,7 @@ static void m48t59_isa_register_types(void) for (i = 0; i < ARRAY_SIZE(m48txx_isa_info); i++) { isa_type_info.name = m48txx_isa_info[i].bus_name; isa_type_info.class_data = &m48txx_isa_info[i]; - type_register(&isa_type_info); + type_register_static(&isa_type_info); } } diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c index 2e2c849985c..68be2dad6f3 100644 --- a/hw/rtc/m48t59.c +++ b/hw/rtc/m48t59.c @@ -28,14 +28,15 @@ #include "hw/qdev-properties.h" #include "hw/rtc/m48t59.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" -#include "sysemu/rtc.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/rtc.h" +#include "system/system.h" #include "hw/sysbus.h" #include "qapi/error.h" #include "qemu/bcd.h" #include "qemu/module.h" #include "trace.h" +#include "system/watchdog.h" #include "m48t59-internal.h" #include "migration/vmstate.h" @@ -163,8 +164,7 @@ static void watchdog_cb (void *opaque) 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); + watchdog_perform_action(); } else { qemu_set_irq(NVRAM->IRQ, 1); qemu_set_irq(NVRAM->IRQ, 0); @@ -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), @@ -618,18 +618,17 @@ static void m48txx_sysbus_toggle_lock(Nvram *obj, int lock) m48t59_toggle_lock(&d->state, lock); } -static Property m48t59_sysbus_properties[] = { +static const Property m48t59_sysbus_properties[] = { DEFINE_PROP_INT32("base-year", M48txxSysBusState, state.base_year, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void m48txx_sysbus_class_init(ObjectClass *klass, void *data) +static void m48txx_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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; @@ -637,10 +636,11 @@ static void m48txx_sysbus_class_init(ObjectClass *klass, void *data) nc->toggle_lock = m48txx_sysbus_toggle_lock; } -static void m48txx_sysbus_concrete_class_init(ObjectClass *klass, void *data) +static void m48txx_sysbus_concrete_class_init(ObjectClass *klass, + const void *data) { M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_CLASS(klass); - M48txxInfo *info = data; + const M48txxInfo *info = data; u->info = *info; } @@ -658,7 +658,7 @@ static const TypeInfo m48txx_sysbus_type_info = { .instance_init = m48t59_init1, .abstract = true, .class_init = m48txx_sysbus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NVRAM }, { } } @@ -679,7 +679,7 @@ static void m48t59_register_types(void) for (i = 0; i < ARRAY_SIZE(m48txx_sysbus_info); i++) { sysbus_type_info.name = m48txx_sysbus_info[i].bus_name; sysbus_type_info.class_data = &m48txx_sysbus_info[i]; - type_register(&sysbus_type_info); + type_register_static(&sysbus_type_info); } } diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index c27c362db9e..f9f5cf396f0 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -32,11 +32,11 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "sysemu/replay.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/rtc.h" +#include "system/system.h" +#include "system/replay.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/rtc.h" #include "hw/rtc/mc146818rtc.h" #include "hw/rtc/mc146818rtc_regs.h" #include "migration/vmstate.h" @@ -104,16 +104,9 @@ static void rtc_coalesced_timer_update(MC146818RtcState *s) } } -static QLIST_HEAD(, MC146818RtcState) rtc_devices = - QLIST_HEAD_INITIALIZER(rtc_devices); - -void qmp_rtc_reset_reinjection(Error **errp) +void rtc_reset_reinjection(MC146818RtcState *rtc) { - MC146818RtcState *s; - - QLIST_FOREACH(s, &rtc_devices, link) { - s->irq_coalesced = 0; - } + rtc->irq_coalesced = 0; } static bool rtc_policy_slew_deliver_irq(MC146818RtcState *s) @@ -599,7 +592,7 @@ static void rtc_get_time(MC146818RtcState *s, struct tm *tm) 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); @@ -817,7 +810,7 @@ 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[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(irq_reinject_on_ack_count, MC146818RtcState), VMSTATE_END_OF_LIST() } @@ -826,26 +819,26 @@ static const VMStateDescription vmstate_rtc_irq_reinject_on_ack_count = { static const VMStateDescription vmstate_rtc = { .name = "mc146818rtc", .version_id = 3, - .minimum_version_id = 1, + .minimum_version_id = 3, .pre_save = rtc_pre_save, .post_load = rtc_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BUFFER(cmos_data, MC146818RtcState), VMSTATE_UINT8(cmos_index, MC146818RtcState), VMSTATE_UNUSED(7*4), VMSTATE_TIMER_PTR(periodic_timer, MC146818RtcState), VMSTATE_INT64(next_periodic_time, MC146818RtcState), VMSTATE_UNUSED(3*8), - 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_UINT32(irq_coalesced, MC146818RtcState), + VMSTATE_UINT32(period, MC146818RtcState), + VMSTATE_UINT64(base_rtc, MC146818RtcState), + VMSTATE_UINT64(last_update, MC146818RtcState), + VMSTATE_INT64(offset, MC146818RtcState), + VMSTATE_TIMER_PTR(update_timer, MC146818RtcState), + VMSTATE_UINT64(next_alarm_time, MC146818RtcState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_rtc_irq_reinject_on_ack_count, NULL } @@ -936,12 +929,9 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->io, 0, &s->coalesced_io); memory_region_add_coalescing(&s->coalesced_io, 0, 1); - qdev_set_legacy_instance_id(dev, s->io_base, 3); - 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); } MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year, @@ -968,13 +958,12 @@ MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year, return s; } -static Property mc146818rtc_properties[] = { +static const Property mc146818rtc_properties[] = { 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) @@ -998,7 +987,7 @@ static void rtc_reset_enter(Object *obj, ResetType type) } } -static void rtc_reset_hold(Object *obj) +static void rtc_reset_hold(Object *obj, ResetType type) { MC146818RtcState *s = MC146818_RTC(obj); @@ -1027,7 +1016,7 @@ static void rtc_build_aml(AcpiDevAmlIf *adev, Aml *scope) aml_append(scope, dev); } -static void rtc_class_initfn(ObjectClass *klass, void *data) +static void rtc_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -1047,7 +1036,7 @@ static const TypeInfo mc146818rtc_info = { .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(MC146818RtcState), .class_init = rtc_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, diff --git a/hw/rtc/meson.build b/hw/rtc/meson.build index 3ea2affe0b9..6c87864dc07 100644 --- a/hw/rtc/meson.build +++ b/hw/rtc/meson.build @@ -3,7 +3,6 @@ system_ss.add(when: 'CONFIG_DS1338', if_true: files('ds1338.c')) system_ss.add(when: 'CONFIG_M41T80', if_true: files('m41t80.c')) system_ss.add(when: 'CONFIG_M48T59', if_true: files('m48t59.c')) system_ss.add(when: 'CONFIG_PL031', if_true: files('pl031.c')) -system_ss.add(when: 'CONFIG_TWL92230', if_true: files('twl92230.c')) system_ss.add(when: ['CONFIG_ISA_BUS', 'CONFIG_M48T59'], if_true: files('m48t59-isa.c')) system_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-rtc.c')) @@ -14,3 +13,4 @@ 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')) +system_ss.add(when: 'CONFIG_RS5C372_RTC', if_true: files('rs5c372.c')) diff --git a/hw/rtc/pl031.c b/hw/rtc/pl031.c index b01d0e75d1a..e545b9dfd64 100644 --- a/hw/rtc/pl031.c +++ b/hw/rtc/pl031.c @@ -18,8 +18,8 @@ #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "sysemu/rtc.h" +#include "system/system.h" +#include "system/rtc.h" #include "qemu/cutils.h" #include "qemu/log.h" #include "qemu/module.h" @@ -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,13 +313,13 @@ 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 } }; -static Property pl031_properties[] = { +static const Property pl031_properties[] = { /* * True to correctly migrate the tick offset of the RTC. False to * obtain backward migration compatibility with older QEMU versions, @@ -329,10 +330,9 @@ static Property pl031_properties[] = { */ DEFINE_PROP_BOOL("migrate-tick-offset", PL031State, migrate_tick_offset, true), - DEFINE_PROP_END_OF_LIST() }; -static void pl031_class_init(ObjectClass *klass, void *data) +static void pl031_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/rtc/rs5c372.c b/hw/rtc/rs5c372.c new file mode 100644 index 00000000000..bb924534a73 --- /dev/null +++ b/hw/rtc/rs5c372.c @@ -0,0 +1,236 @@ +/* + * Ricoh RS5C372, R222x I2C RTC + * + * Copyright (c) 2025 Bernhard Beschow + * + * Based on hw/rtc/ds1338.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/i2c/i2c.h" +#include "hw/qdev-properties.h" +#include "hw/resettable.h" +#include "migration/vmstate.h" +#include "qemu/bcd.h" +#include "qom/object.h" +#include "system/rtc.h" +#include "trace.h" + +#define NVRAM_SIZE 0x10 + +/* Flags definitions */ +#define SECONDS_CH 0x80 +#define HOURS_PM 0x20 +#define CTRL2_24 0x20 + +#define TYPE_RS5C372 "rs5c372" +OBJECT_DECLARE_SIMPLE_TYPE(RS5C372State, RS5C372) + +struct RS5C372State { + I2CSlave parent_obj; + + int64_t offset; + uint8_t wday_offset; + uint8_t nvram[NVRAM_SIZE]; + uint8_t ptr; + uint8_t tx_format; + bool addr_byte; +}; + +static void capture_current_time(RS5C372State *s) +{ + /* + * Capture the current time into the secondary registers which will be + * actually read by the data transfer operation. + */ + struct tm now; + qemu_get_timedate(&now, s->offset); + s->nvram[0] = to_bcd(now.tm_sec); + s->nvram[1] = to_bcd(now.tm_min); + if (s->nvram[0xf] & CTRL2_24) { + s->nvram[2] = to_bcd(now.tm_hour); + } else { + int tmp = now.tm_hour; + if (tmp % 12 == 0) { + tmp += 12; + } + if (tmp <= 12) { + s->nvram[2] = to_bcd(tmp); + } else { + s->nvram[2] = HOURS_PM | to_bcd(tmp - 12); + } + } + s->nvram[3] = (now.tm_wday + s->wday_offset) % 7 + 1; + s->nvram[4] = to_bcd(now.tm_mday); + s->nvram[5] = to_bcd(now.tm_mon + 1); + s->nvram[6] = to_bcd(now.tm_year - 100); +} + +static void inc_regptr(RS5C372State *s) +{ + s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1); +} + +static int rs5c372_event(I2CSlave *i2c, enum i2c_event event) +{ + RS5C372State *s = RS5C372(i2c); + + switch (event) { + case I2C_START_RECV: + /* + * In h/w, capture happens on any START condition, not just a + * START_RECV, but there is no need to actually capture on + * START_SEND, because the guest can't get at that data + * without going through a START_RECV which would overwrite it. + */ + capture_current_time(s); + s->ptr = 0xf; + break; + case I2C_START_SEND: + s->addr_byte = true; + break; + default: + break; + } + + return 0; +} + +static uint8_t rs5c372_recv(I2CSlave *i2c) +{ + RS5C372State *s = RS5C372(i2c); + uint8_t res; + + res = s->nvram[s->ptr]; + + trace_rs5c372_recv(s->ptr, res); + + inc_regptr(s); + return res; +} + +static int rs5c372_send(I2CSlave *i2c, uint8_t data) +{ + RS5C372State *s = RS5C372(i2c); + + if (s->addr_byte) { + s->ptr = data >> 4; + s->tx_format = data & 0xf; + s->addr_byte = false; + return 0; + } + + trace_rs5c372_send(s->ptr, data); + + if (s->ptr < 7) { + /* Time register. */ + struct tm now; + qemu_get_timedate(&now, s->offset); + switch (s->ptr) { + case 0: + now.tm_sec = from_bcd(data & 0x7f); + break; + case 1: + now.tm_min = from_bcd(data & 0x7f); + break; + case 2: + if (s->nvram[0xf] & CTRL2_24) { + now.tm_hour = from_bcd(data & 0x3f); + } else { + int tmp = from_bcd(data & (HOURS_PM - 1)); + if (data & HOURS_PM) { + tmp += 12; + } + if (tmp % 12 == 0) { + tmp -= 12; + } + now.tm_hour = tmp; + } + break; + case 3: + { + /* + * The day field is supposed to contain a value in the range + * 1-7. Otherwise behavior is undefined. + */ + int user_wday = (data & 7) - 1; + s->wday_offset = (user_wday - now.tm_wday + 7) % 7; + } + break; + case 4: + now.tm_mday = from_bcd(data & 0x3f); + break; + case 5: + now.tm_mon = from_bcd(data & 0x1f) - 1; + break; + case 6: + now.tm_year = from_bcd(data) + 100; + break; + } + s->offset = qemu_timedate_diff(&now); + } else { + s->nvram[s->ptr] = data; + } + inc_regptr(s); + return 0; +} + +static void rs5c372_reset_hold(Object *obj, ResetType type) +{ + RS5C372State *s = RS5C372(obj); + + /* The clock is running and synchronized with the host */ + s->offset = 0; + s->wday_offset = 0; + memset(s->nvram, 0, NVRAM_SIZE); + s->ptr = 0; + s->addr_byte = false; +} + +static const VMStateDescription rs5c372_vmstate = { + .name = "rs5c372", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_I2C_SLAVE(parent_obj, RS5C372State), + VMSTATE_INT64(offset, RS5C372State), + VMSTATE_UINT8_V(wday_offset, RS5C372State, 2), + VMSTATE_UINT8_ARRAY(nvram, RS5C372State, NVRAM_SIZE), + VMSTATE_UINT8(ptr, RS5C372State), + VMSTATE_UINT8(tx_format, RS5C372State), + VMSTATE_BOOL(addr_byte, RS5C372State), + VMSTATE_END_OF_LIST() + } +}; + +static void rs5c372_init(Object *obj) +{ + qdev_prop_set_uint8(DEVICE(obj), "address", 0x32); +} + +static void rs5c372_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + k->event = rs5c372_event; + k->recv = rs5c372_recv; + k->send = rs5c372_send; + dc->vmsd = &rs5c372_vmstate; + rc->phases.hold = rs5c372_reset_hold; +} + +static const TypeInfo rs5c372_types[] = { + { + .name = TYPE_RS5C372, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(RS5C372State), + .instance_init = rs5c372_init, + .class_init = rs5c372_class_init, + }, +}; + +DEFINE_TYPES(rs5c372_types) diff --git a/hw/rtc/sun4v-rtc.c b/hw/rtc/sun4v-rtc.c index e037acd1b56..29e24ef6bed 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. */ @@ -75,7 +75,7 @@ static void sun4v_rtc_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); } -static void sun4v_rtc_class_init(ObjectClass *klass, void *data) +static void sun4v_rtc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/rtc/trace-events b/hw/rtc/trace-events index ebb311a5b0e..b9f2852d35f 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 @@ -31,3 +35,7 @@ m48txx_nvram_mem_write(uint32_t addr, uint32_t value) "mem write addr:0x%04x val # goldfish_rtc.c goldfish_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 goldfish_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 + +# rs5c372.c +rs5c372_recv(uint32_t addr, uint8_t value) "[0x%" PRIx32 "] -> 0x%02" PRIx8 +rs5c372_send(uint32_t addr, uint8_t value) "[0x%" PRIx32 "] <- 0x%02" PRIx8 diff --git a/hw/rtc/twl92230.c b/hw/rtc/twl92230.c deleted file mode 100644 index 64c61c3daeb..00000000000 --- 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; - int64_t sec_offset; - int64_t 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 3e7d61a41c1..500982a8011 100644 --- a/hw/rtc/xlnx-zynqmp-rtc.c +++ b/hw/rtc/xlnx-zynqmp-rtc.c @@ -32,8 +32,8 @@ #include "qemu/module.h" #include "hw/irq.h" #include "qemu/cutils.h" -#include "sysemu/sysemu.h" -#include "sysemu/rtc.h" +#include "system/system.h" +#include "system/rtc.h" #include "trace.h" #include "hw/rtc/xlnx-zynqmp-rtc.h" #include "migration/vmstate.h" @@ -244,18 +244,18 @@ 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(), } }; -static void rtc_class_init(ObjectClass *klass, void *data) +static void rtc_class_init(ObjectClass *klass, const 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 2b297c5a6a6..aa9242d1ef6 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 47c17026c73..5b9004e9e15 100644 --- a/hw/rx/rx-gdbsim.c +++ b/hw/rx/rx-gdbsim.c @@ -20,12 +20,13 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/guest-random.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/loader.h" #include "hw/rx/rx62n.h" -#include "sysemu/qtest.h" -#include "sysemu/device_tree.h" -#include "sysemu/reset.h" +#include "system/qtest.h" +#include "system/device_tree.h" +#include "system/reset.h" #include "hw/boards.h" #include "qom/object.h" @@ -109,9 +110,6 @@ static void rx_gdbsim_init(MachineState *machine) if (!kernel_filename) { if (machine->firmware) { rom_add_file_fixed(machine->firmware, RX62N_CFLASH_BASE, 0); - } else if (!qtest_enabled()) { - error_report("No bios or kernel specified"); - exit(1); } } @@ -126,7 +124,7 @@ static void rx_gdbsim_init(MachineState *machine) * the latter half of the SDRAM space. */ kernel_offset = machine->ram_size / 2; - rx_load_image(RX_CPU(first_cpu), kernel_filename, + rx_load_image(&s->mcu.cpu, kernel_filename, SDRAM_BASE + kernel_offset, kernel_offset); if (dtb_filename) { ram_addr_t dtb_offset; @@ -152,12 +150,12 @@ static void rx_gdbsim_init(MachineState *machine) qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, rom_ptr(SDRAM_BASE + dtb_offset, dtb_size)); /* Set dtb address to R1 */ - RX_CPU(first_cpu)->env.regs[1] = SDRAM_BASE + dtb_offset; + s->mcu.cpu.env.regs[1] = SDRAM_BASE + dtb_offset; } } } -static void rx_gdbsim_class_init(ObjectClass *oc, void *data) +static void rx_gdbsim_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -167,7 +165,7 @@ static void rx_gdbsim_class_init(ObjectClass *oc, void *data) mc->default_ram_id = "ext-sdram"; } -static void rx62n7_class_init(ObjectClass *oc, void *data) +static void rx62n7_class_init(ObjectClass *oc, const void *data) { RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); @@ -177,7 +175,7 @@ static void rx62n7_class_init(ObjectClass *oc, void *data) mc->desc = "gdb simulator (R5F562N7 MCU and external RAM)"; }; -static void rx62n8_class_init(ObjectClass *oc, void *data) +static void rx62n8_class_init(ObjectClass *oc, const void *data) { RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c index 3e887a0fc7b..a2a243afa41 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 "system/system.h" +#include "qobject/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,30 +132,27 @@ 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); + + ipr_map = qlist_new(); 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]); + qlist_append_int(ipr_map, ipr_table[i]); } + qdev_prop_set_array(DEVICE(icu), "ipr-map", ipr_map); - for (i = 0; i < NR_IRQS; i++) { - s->irq[i] = qdev_get_gpio_in(DEVICE(icu), i); + 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_connect_irq(icu, 2, qdev_get_gpio_in(DEVICE(&s->icu), SWI)); sysbus_mmio_map(icu, 0, RX62N_ICU_BASE); } @@ -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); } @@ -255,15 +257,14 @@ static void rx62n_realize(DeviceState *dev, Error **errp) register_sci(s, 0); } -static Property rx62n_properties[] = { +static const Property rx62n_properties[] = { DEFINE_PROP_LINK("main-bus", RX62NState, sysmem, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_BOOL("load-kernel", RX62NState, kernel, false), DEFINE_PROP_UINT32("xtal-frequency-hz", RX62NState, xtal_freq_hz, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void rx62n_class_init(ObjectClass *klass, void *data) +static void rx62n_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -271,7 +272,7 @@ static void rx62n_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, rx62n_properties); } -static void r5f562n7_class_init(ObjectClass *oc, void *data) +static void r5f562n7_class_init(ObjectClass *oc, const void *data) { RX62NClass *rxc = RX62N_MCU_CLASS(oc); @@ -280,7 +281,7 @@ static void r5f562n7_class_init(ObjectClass *oc, void *data) rxc->data_flash_size = 32 * KiB; }; -static void r5f562n8_class_init(ObjectClass *oc, void *data) +static void r5f562n8_class_init(ObjectClass *oc, const void *data) { RX62NClass *rxc = RX62N_MCU_CLASS(oc); diff --git a/hw/s390x/3270-ccw.c b/hw/s390x/3270-ccw.c index 69e6783ade5..3f0d384fd80 100644 --- a/hw/s390x/3270-ccw.c +++ b/hw/s390x/3270-ccw.c @@ -150,15 +150,10 @@ static void emulated_ccw_3270_realize(DeviceState *ds, Error **errp) g_free(sch); } -static Property emulated_ccw_3270_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - -static void emulated_ccw_3270_class_init(ObjectClass *klass, void *data) +static void emulated_ccw_3270_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - device_class_set_props(dc, emulated_ccw_3270_properties); dc->realize = emulated_ccw_3270_realize; dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); diff --git a/hw/s390x/Kconfig b/hw/s390x/Kconfig index 4c068d7960b..02ea1997013 100644 --- a/hw/s390x/Kconfig +++ b/hw/s390x/Kconfig @@ -1,14 +1,19 @@ config S390_CCW_VIRTIO bool + default y + depends on S390X imply VIRTIO_PCI imply TERMINAL3270 imply VFIO_AP imply VFIO_CCW imply WDT_DIAG288 + 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 + select VIRTIO_MEM_SUPPORTED diff --git a/hw/s390x/ap-bridge.c b/hw/s390x/ap-bridge.c index ef8fa2b15be..edeb3dbef34 100644 --- a/hw/s390x/ap-bridge.c +++ b/hw/s390x/ap-bridge.c @@ -22,7 +22,7 @@ static char *ap_bus_get_dev_path(DeviceState *dev) return g_strdup_printf("/1"); } -static void ap_bus_class_init(ObjectClass *oc, void *data) +static void ap_bus_class_init(ObjectClass *oc, const void *data) { BusClass *k = BUS_CLASS(oc); @@ -61,7 +61,7 @@ void s390_init_ap(void) qbus_set_hotplug_handler(bus, OBJECT(dev)); } -static void ap_bridge_class_init(ObjectClass *oc, void *data) +static void ap_bridge_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); @@ -75,7 +75,7 @@ static const TypeInfo ap_bridge_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = 0, .class_init = ap_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/s390x/ap-device.c b/hw/s390x/ap-device.c index 237d1f19c50..733104403e5 100644 --- a/hw/s390x/ap-device.c +++ b/hw/s390x/ap-device.c @@ -12,7 +12,7 @@ #include "qapi/error.h" #include "hw/s390x/ap-device.h" -static void ap_class_init(ObjectClass *klass, void *data) +static void ap_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/s390x/ap-stub.c b/hw/s390x/ap-stub.c new file mode 100644 index 00000000000..001fe5f8b01 --- /dev/null +++ b/hw/s390x/ap-stub.c @@ -0,0 +1,21 @@ +/* + * VFIO based AP matrix device assignment + * + * Copyright 2025 IBM Corp. + * Author(s): Rorie Reyes + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/s390x/ap-bridge.h" + +int ap_chsc_sei_nt0_get_event(void *res) +{ + return EVENT_INFORMATION_NOT_STORED; +} + +bool ap_chsc_sei_nt0_have_event(void) +{ + return false; +} diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index 95f269ab441..19c2238f760 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,34 +35,75 @@ 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 Property ccw_device_properties[] = { +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 = { + .type = "str", + .description = "Up to 8 chars in set of [A-Za-z0-9. ] to select" + " a guest kernel", + .get = ccw_device_get_loadparm, + .set = ccw_device_set_loadparm, +}; + +static const Property ccw_device_properties[] = { DEFINE_PROP_CSS_DEV_ID("devno", CcwDevice, devno), DEFINE_PROP_CSS_DEV_ID_RO("dev_id", CcwDevice, dev_id), DEFINE_PROP_CSS_DEV_ID_RO("subch_id", CcwDevice, subch_id), - 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); } -static void ccw_device_class_init(ObjectClass *klass, void *data) +static void ccw_device_class_init(ObjectClass *klass, const 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 +111,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 6dff95225df..4439feb1403 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 00000000000..b513f8936e4 --- /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-s390x.h" +#include "qapi/qapi-events-machine-s390x.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 4017081d49c..0f87b8c5c44 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(); @@ -66,23 +66,16 @@ static char *virtual_css_bus_get_dev_path(DeviceState *dev) { CcwDevice *ccw_dev = CCW_DEVICE(dev); SubchDev *sch = ccw_dev->sch; - VirtualCssBridge *bridge = - VIRTUAL_CSS_BRIDGE(qdev_get_parent_bus(dev)->parent); - /* - * We can't provide a dev path for backward compatibility on - * older machines, as it is visible in the migration stream. - */ - return bridge->css_dev_path ? - g_strdup_printf("/%02x.%1x.%04x", sch->cssid, sch->ssid, sch->devno) : - NULL; + return g_strdup_printf("/%02x.%1x.%04x", sch->cssid, sch->ssid, sch->devno); } -static void virtual_css_bus_class_init(ObjectClass *klass, void *data) +static void virtual_css_bus_class_init(ObjectClass *klass, const 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 +88,6 @@ static const TypeInfo virtual_css_bus_info = { VirtualCssBus *virtual_css_bus_init(void) { - VirtualCssBus *cbus; BusState *bus; DeviceState *dev; @@ -103,42 +95,35 @@ 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 ********************/ -static Property virtual_css_bridge_properties[] = { - DEFINE_PROP_BOOL("css_dev_path", VirtualCssBridge, css_dev_path, - true), - DEFINE_PROP_END_OF_LIST(), -}; - static bool prop_get_true(Object *obj, Error **errp) { return true; } -static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) +static void virtual_css_bridge_class_init(ObjectClass *klass, const void *data) { HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); hc->unplug = ccw_device_unplug; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - device_class_set_props(dc, virtual_css_bridge_properties); object_class_property_add_bool(klass, "cssid-unrestricted", prop_get_true, NULL); object_class_property_set_description(klass, "cssid-unrestricted", @@ -151,7 +136,7 @@ static const TypeInfo virtual_css_bridge_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(VirtualCssBridge), .class_init = virtual_css_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 95d1b3a3cee..53444f68288 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -14,7 +14,7 @@ #include "qapi/visitor.h" #include "qemu/bitops.h" #include "qemu/error-report.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/s390x/ioinst.h" #include "hw/qdev-properties.h" #include "hw/s390x/css.h" @@ -32,7 +32,7 @@ static const VMStateDescription vmstate_crw = { .name = "s390_crw", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(flags, CRW), VMSTATE_UINT16(rsid, CRW), VMSTATE_END_OF_LIST() @@ -43,7 +43,7 @@ static const VMStateDescription vmstate_crw_container = { .name = "s390_crw_container", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(crw, CrwContainer, 0, vmstate_crw, CRW), VMSTATE_END_OF_LIST() }, @@ -59,7 +59,7 @@ static const VMStateDescription vmstate_chp_info = { .name = "s390_chp_info", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(in_use, ChpInfo), VMSTATE_UINT8(type, ChpInfo), VMSTATE_UINT8(is_virtual, ChpInfo), @@ -77,7 +77,7 @@ static const VMStateDescription vmstate_scsw = { .name = "s390_scsw", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(flags, SCSW), VMSTATE_UINT16(ctrl, SCSW), VMSTATE_UINT32(cpa, SCSW), @@ -92,7 +92,7 @@ static const VMStateDescription vmstate_pmcw = { .name = "s390_pmcw", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(intparm, PMCW), VMSTATE_UINT16(flags, PMCW), VMSTATE_UINT16(devno, PMCW), @@ -113,7 +113,7 @@ static const VMStateDescription vmstate_schib = { .name = "s390_schib", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(pmcw, SCHIB, 0, vmstate_pmcw, PMCW), VMSTATE_STRUCT(scsw, SCHIB, 0, vmstate_scsw, SCSW), VMSTATE_UINT64(mba, SCHIB), @@ -127,7 +127,7 @@ static const VMStateDescription vmstate_ccw1 = { .name = "s390_ccw1", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(cmd_code, CCW1), VMSTATE_UINT8(flags, CCW1), VMSTATE_UINT16(count, CCW1), @@ -140,7 +140,7 @@ static const VMStateDescription vmstate_ciw = { .name = "s390_ciw", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(type, CIW), VMSTATE_UINT8(command, CIW), VMSTATE_UINT16(count, CIW), @@ -152,7 +152,7 @@ static const VMStateDescription vmstate_sense_id = { .name = "s390_sense_id", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(reserved, SenseId), VMSTATE_UINT16(cu_type, SenseId), VMSTATE_UINT8(cu_model, SenseId), @@ -168,7 +168,7 @@ static const VMStateDescription vmstate_orb = { .name = "s390_orb", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(intparm, ORB), VMSTATE_UINT16(ctrl0, ORB), VMSTATE_UINT8(lpm, ORB), @@ -178,17 +178,11 @@ static const VMStateDescription vmstate_orb = { } }; -static bool vmstate_schdev_orb_needed(void *opaque) -{ - return css_migration_enabled(); -} - static const VMStateDescription vmstate_schdev_orb = { .name = "s390_subch_dev/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 +201,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 +217,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 +258,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 +272,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 +287,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 +324,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,25 +382,6 @@ 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()) { - /* No compat voodoo to do ;) */ - return 0; - } - /* - * Hack alert. If we don't migrate the channel subsystem status - * we still need to find out if the guest enabled mss/mcss-e. - * If the subchannel is enabled, it certainly was able to access it, - * so adjust the max_ssid/max_cssid values for relevant ssid/cssid - * values. This is not watertight, but better than nothing. - */ - if (s->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA) { - if (s->ssid) { - channel_subsys.max_ssid = MAX_SSID; - } - if (s->cssid != channel_subsys.default_cssid) { - channel_subsys.max_cssid = MAX_CSSID; - } - } return 0; } @@ -644,8 +619,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; @@ -2458,7 +2434,7 @@ void css_reset(void) static void get_css_devid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; CssDevId *dev_id = object_field_prop_ptr(obj, prop); char buffer[] = "xx.x.xxxx"; char *p = buffer; @@ -2487,7 +2463,7 @@ static void get_css_devid(Object *obj, Visitor *v, const char *name, static void set_css_devid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; CssDevId *dev_id = object_field_prop_ptr(obj, prop); char *str; int num, n1, n2; @@ -2518,7 +2494,7 @@ static void set_css_devid(Object *obj, Visitor *v, const char *name, } const PropertyInfo css_devid_propinfo = { - .name = "str", + .type = "str", .description = "Identifier of an I/O device in the channel " "subsystem, example: fe.1.23ab", .get = get_css_devid, @@ -2526,7 +2502,7 @@ const PropertyInfo css_devid_propinfo = { }; const PropertyInfo css_devid_ro_propinfo = { - .name = "str", + .type = "str", .description = "Read-only identifier of an I/O device in the channel " "subsystem, example: fe.1.23ab", .get = get_css_devid, diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index 6891e3cd73b..7b7bf237fed 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -45,13 +45,6 @@ struct SCLPEventFacility { uint32_t receive_mask_pieces[2]; sccb_mask_t receive_mask; }; - /* - * when false, we keep the same broken, backwards compatible behaviour as - * before, allowing only masks of size exactly 4; when true, we implement - * the architecture correctly, allowing all valid mask sizes. Needed for - * migration toward older versions. - */ - bool allow_all_mask_sizes; /* length of the receive mask */ uint16_t mask_length; }; @@ -294,8 +287,7 @@ static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb) uint16_t mask_length = be16_to_cpu(we_mask->mask_length); sccb_mask_t tmp_mask; - if (!mask_length || (mask_length > SCLP_EVENT_MASK_LEN_MAX) || - ((mask_length != 4) && !ef->allow_all_mask_sizes)) { + if (!mask_length || mask_length > SCLP_EVENT_MASK_LEN_MAX) { sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_MASK_LENGTH); return; } @@ -355,19 +347,12 @@ static bool vmstate_event_facility_mask64_needed(void *opaque) return (ef->receive_mask & 0xFFFFFFFF) != 0; } -static bool vmstate_event_facility_mask_length_needed(void *opaque) -{ - SCLPEventFacility *ef = opaque; - - return ef->allow_all_mask_sizes; -} - static const VMStateDescription vmstate_event_facility_mask64 = { .name = "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() } @@ -377,8 +362,7 @@ static const VMStateDescription vmstate_event_facility_mask_length = { .name = "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() } @@ -388,42 +372,23 @@ 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 } }; -static void sclp_event_set_allow_all_mask_sizes(Object *obj, bool value, - Error **errp) -{ - SCLPEventFacility *ef = (SCLPEventFacility *)obj; - - ef->allow_all_mask_sizes = value; -} - -static bool sclp_event_get_allow_all_mask_sizes(Object *obj, Error **errp) -{ - SCLPEventFacility *ef = (SCLPEventFacility *)obj; - - return ef->allow_all_mask_sizes; -} - static void init_event_facility(Object *obj) { SCLPEventFacility *event_facility = EVENT_FACILITY(obj); DeviceState *sdev = DEVICE(obj); event_facility->mask_length = 4; - event_facility->allow_all_mask_sizes = true; - object_property_add_bool(obj, "allow_all_mask_sizes", - sclp_event_get_allow_all_mask_sizes, - sclp_event_set_allow_all_mask_sizes); /* Spawn a new bus for SCLP events */ qbus_init(&event_facility->sbus, sizeof(event_facility->sbus), @@ -460,14 +425,14 @@ static void reset_event_facility(DeviceState *dev) sdev->receive_mask = 0; } -static void init_event_facility_class(ObjectClass *klass, void *data) +static void init_event_facility_class(ObjectClass *klass, const void *data) { SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(sbdc); 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; @@ -497,7 +462,7 @@ static void event_realize(DeviceState *qdev, Error **errp) } } -static void event_class_init(ObjectClass *klass, void *data) +static void event_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -523,16 +488,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 515dcf51b5f..2f082396c73 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -15,9 +15,9 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" #include "qapi/error.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/tcg.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/tcg.h" #include "elf.h" #include "hw/loader.h" #include "hw/qdev-properties.h" @@ -26,7 +26,6 @@ #include "hw/s390x/vfio-ccw.h" #include "hw/s390x/css.h" #include "hw/s390x/ebcdic.h" -#include "target/s390x/kvm/pv.h" #include "hw/scsi/scsi.h" #include "hw/virtio/virtio-net.h" #include "ipl.h" @@ -34,8 +33,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,21 +45,21 @@ #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) +/* Place the IPLB chain immediately before the BIOS in memory */ +static uint64_t find_iplb_chain_addr(uint64_t bios_addr, uint16_t count) { - S390IPLState *ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL)); - - return ipl->iplbext_migration; + 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 +69,13 @@ static const VMStateDescription vmstate_iplb = { .name = "ipl/iplb", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(reserved1, IplParameterBlock, 110), VMSTATE_UINT16(devno, IplParameterBlock), VMSTATE_UINT8_ARRAY(reserved2, IplParameterBlock, 88), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_iplb_extended, NULL } @@ -86,7 +85,7 @@ static const VMStateDescription vmstate_ipl = { .name = "ipl", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(compat_start_addr, S390IPLState), VMSTATE_UINT64(compat_bios_start_addr, S390IPLState), VMSTATE_STRUCT(iplb, S390IPLState, 0, vmstate_iplb, IplParameterBlock), @@ -145,7 +144,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) { @@ -155,8 +161,8 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) bios_size = load_elf(bios_filename, NULL, bios_translate_addr, &fwbase, - &ipl->bios_start_addr, NULL, NULL, NULL, 1, - EM_S390, 0, 0); + &ipl->bios_start_addr, NULL, NULL, NULL, + ELFDATA2MSB, EM_S390, 0, 0); if (bios_size > 0) { /* Adjust ELF start address to final location */ ipl->bios_start_addr += fwbase; @@ -180,7 +186,7 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) if (ipl->kernel) { kernel_size = load_elf(ipl->kernel, NULL, NULL, NULL, &pentry, NULL, - NULL, NULL, 1, EM_S390, 0, 0); + NULL, NULL, ELFDATA2MSB, EM_S390, 0, 0); if (kernel_size < 0) { kernel_size = load_image_targphys(ipl->kernel, 0, ms->ram_size); if (kernel_size < 0) { @@ -253,8 +259,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); } } } @@ -276,16 +282,12 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) qemu_register_reset(resettable_cold_reset_fn, dev); } -static Property s390_ipl_properties[] = { +static const Property s390_ipl_properties[] = { DEFINE_PROP_STRING("kernel", S390IPLState, kernel), 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), - DEFINE_PROP_END_OF_LIST(), }; static void s390_ipl_set_boot_menu(S390IPLState *ipl) @@ -391,174 +393,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; - } + S390IPLState *ipl = get_ipl_device(); + uint16_t index; + index = ipl->rebuilt_iplb ? ipl->iplb_index : dev_index; - return -1; + ipl->rebuilt_iplb = s390_build_iplb(get_boot_device(index), iplb); + ipl->iplb_index = index; } -static int load_netboot_image(Error **errp) +static bool s390_init_all_iplbs(S390IPLState *ipl) { - 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; - - mr = memory_region_find(sysmem, 0, 1).mr; - if (!mr) { - error_setg(errp, "Failed to find memory region at address 0"); - return -1; - } + int iplb_num = 0; + IplParameterBlock iplb_chain[7]; + DeviceState *dev_st = get_boot_device(0); + Object *machine = qdev_get_machine(); - ram_ptr = memory_region_get_ram_ptr(mr); - if (!ram_ptr) { - error_setg(errp, "No RAM found"); - goto unref_mr; + /* + * 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; } - 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; + /* 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); } - 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; - } + iplb_num = 1; + s390_build_iplb(dev_st, &ipl->iplb); - if (img_size < 0) { - error_setg(errp, "Failed to load network bootloader"); + /* Index any fallback boot devices */ + while (get_boot_device(iplb_num)) { + iplb_num++; } - g_free(netboot_filename); - -unref_mr: - memory_region_unref(mr); - return img_size; -} - -static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb, - int virtio_id) -{ - uint8_t cssid; - uint8_t ssid; - uint16_t devno; - uint16_t schid; - SubchDev *sch = NULL; + 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); - if (iplb->pbt != S390_IPL_TYPE_CCW) { - return false; + iplb_num = MAX_BOOT_DEVS; } - 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); + ipl->qipl.chain_len = cpu_to_be16(iplb_num - 1); - if (sch && sch->devno == devno) { - return sch->id.cu_model == virtio_id; - } + /* + * 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]); } - } - return false; -} -static bool is_virtio_net_device(IplParameterBlock *iplb) -{ - return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_NET); -} + ipl->qipl.next_iplb = cpu_to_be64(s390_ipl_map_iplb_chain(iplb_chain)); + } -static bool is_virtio_scsi_device(IplParameterBlock *iplb) -{ - return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_SCSI); + return iplb_num; } static void update_machine_ipl_properties(IplParameterBlock *iplb) @@ -578,7 +568,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 +590,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 +617,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); - } - } + ipl->reset_type = reset_type; if (reset_type == S390_RESET_MODIFIED_CLEAR || reset_type == S390_RESET_LOAD_NORMAL || reset_type == S390_RESET_PV) { @@ -703,7 +675,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(struct S390PVResponse *pv_resp, Error **errp) { IplParameterBlock *ipib = s390_ipl_get_iplb_pv(); IPLBlockPV *ipib_pv = &ipib->pv; @@ -712,13 +684,13 @@ 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, + pv_resp, errp); g_free(hdr); return rc; } -int s390_ipl_pv_unpack(void) +int s390_ipl_pv_unpack(struct S390PVResponse *pv_resp) { IplParameterBlock *ipib = s390_ipl_get_iplb_pv(); IPLBlockPV *ipib_pv = &ipib->pv; @@ -727,7 +699,8 @@ int s390_ipl_pv_unpack(void) for (i = 0; i < ipib_pv->num_comp; i++) { rc = s390_pv_unpack(ipib_pv->components[i].addr, TARGET_PAGE_ALIGN(ipib_pv->components[i].size), - ipib_pv->components[i].tweak_pref); + ipib_pv->components[i].tweak_pref, + pv_resp); if (rc) { break; } @@ -745,13 +718,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); } @@ -766,13 +737,13 @@ static void s390_ipl_reset(DeviceState *dev) } } -static void s390_ipl_class_init(ObjectClass *klass, void *data) +static void s390_ipl_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 7fc86e79054..505cded490c 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -14,101 +14,24 @@ #define HW_S390_IPL_H #include "cpu.h" -#include "exec/address-spaces.h" +#include "exec/target_page.h" +#include "system/address-spaces.h" +#include "system/memory.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; +#include "target/s390x/kvm/pv.h" #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_pv_unpack(void); +int s390_ipl_prepare_pv_header(struct S390PVResponse *pv_resp, + Error **errp); +int s390_ipl_pv_unpack(struct S390PVResponse *pv_resp); void s390_ipl_prepare_cpu(S390CPU *cpu); IplParameterBlock *s390_ipl_get_iplb(void); IplParameterBlock *s390_ipl_get_iplb_pv(void); @@ -131,27 +54,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 +70,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,11 +81,9 @@ struct S390IPLState { char *initrd; char *cmdline; char *firmware; - char *netboot_fw; uint8_t cssid; uint8_t ssid; uint16_t devno; - bool iplbext_migration; }; QEMU_BUILD_BUG_MSG(offsetof(S390IPLState, iplb) & 3, "alignment of iplb wrong"); @@ -276,11 +177,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 6fd096813a1..99cbcbd7d63 100644 --- a/hw/s390x/meson.build +++ b/hw/s390x/meson.build @@ -12,7 +12,6 @@ s390x_ss.add(files( 's390-pci-inst.c', 's390-skeys.c', 's390-stattrib.c', - 's390-virtio-hcall.c', 'sclp.c', 'sclpcpu.c', 'sclpquiesce.c', @@ -23,13 +22,18 @@ s390x_ss.add(when: 'CONFIG_KVM', if_true: files( 's390-skeys-kvm.c', 's390-stattrib-kvm.c', 's390-pci-kvm.c', + 'cpu-topology.c', )) s390x_ss.add(when: 'CONFIG_TCG', if_true: files( 'tod-tcg.c', )) -s390x_ss.add(when: 'CONFIG_S390_CCW_VIRTIO', if_true: files('s390-virtio-ccw.c')) +s390x_ss.add(when: 'CONFIG_S390_CCW_VIRTIO', if_true: files( + 's390-virtio-ccw.c', + 's390-hypercall.c', +)) s390x_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('3270-ccw.c')) s390x_ss.add(when: 'CONFIG_VFIO', if_true: files('s390-pci-vfio.c')) +s390x_ss.add(when: 'CONFIG_VFIO_AP', if_false: files('ap-stub.c')) virtio_ss = ss.source_set() virtio_ss.add(files('virtio-ccw.c')) @@ -47,8 +51,12 @@ endif virtio_ss.add(when: 'CONFIG_VHOST_SCSI', if_true: files('vhost-scsi-ccw.c')) virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-ccw.c')) virtio_ss.add(when: 'CONFIG_VHOST_USER_FS', if_true: files('vhost-user-fs-ccw.c')) +virtio_ss.add(when: 'CONFIG_VIRTIO_MD', if_true: files('virtio-ccw-md.c')) +virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-ccw-mem.c')) s390x_ss.add_all(when: 'CONFIG_VIRTIO_CCW', if_true: virtio_ss) +s390x_ss.add(when: 'CONFIG_VIRTIO_MD', if_false: files('virtio-ccw-md-stubs.c')) + hw_arch += {'s390x': s390x_ss} hw_s390x_modules = {} diff --git a/hw/s390x/s390-ccw.c b/hw/s390x/s390-ccw.c index e2d86d96e72..10c81a4c893 100644 --- a/hw/s390x/s390-ccw.c +++ b/hw/s390x/s390-ccw.c @@ -18,7 +18,7 @@ #include "hw/s390x/css.h" #include "hw/s390x/css-bridge.h" #include "hw/s390x/s390-ccw.h" -#include "sysemu/sysemu.h" +#include "system/system.h" IOInstEnding s390_ccw_cmd_request(SubchDev *sch) { @@ -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 @@ static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) 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) @@ -175,7 +175,7 @@ static void s390_ccw_instance_init(Object *obj) "/disk@0,0", DEVICE(obj)); } -static void s390_ccw_class_init(ObjectClass *klass, void *data) +static void s390_ccw_class_init(ObjectClass *klass, const void *data) { S390CCWDeviceClass *cdc = S390_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/s390-hypercall.c b/hw/s390x/s390-hypercall.c new file mode 100644 index 00000000000..ac1b08b2cd5 --- /dev/null +++ b/hw/s390x/s390-hypercall.c @@ -0,0 +1,85 @@ +/* + * Support for QEMU/KVM hypercalls on s390 + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck + * + * 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 "cpu.h" +#include "hw/s390x/s390-virtio-ccw.h" +#include "hw/s390x/s390-hypercall.h" +#include "hw/s390x/ioinst.h" +#include "hw/s390x/css.h" +#include "virtio-ccw.h" + +static int handle_virtio_notify(uint64_t mem) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + + if (mem < ms->ram_size) { + /* Early printk */ + return 0; + } + return -EINVAL; +} + +static int handle_virtio_ccw_notify(uint64_t subch_id, uint64_t data) +{ + 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; + } + sch = css_find_subch(m, cssid, ssid, schid); + if (!sch || !css_subch_visible(sch)) { + return -EINVAL; + } + + vdev = virtio_ccw_get_vdev(sch); + if (vq_idx >= VIRTIO_QUEUE_MAX || !virtio_queue_get_num(vdev, vq_idx)) { + return -EINVAL; + } + + 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 uint64_t handle_storage_limit(void) +{ + S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine()); + + return s390_get_memory_limit(s390ms) - 1; +} + +void handle_diag_500(S390CPU *cpu, uintptr_t ra) +{ + CPUS390XState *env = &cpu->env; + const uint64_t subcode = env->regs[1]; + + switch (subcode) { + case DIAG500_VIRTIO_NOTIFY: + env->regs[2] = handle_virtio_notify(env->regs[2]); + break; + case DIAG500_VIRTIO_CCW_NOTIFY: + env->regs[2] = handle_virtio_ccw_notify(env->regs[2], env->regs[3]); + break; + case DIAG500_STORAGE_LIMIT: + env->regs[2] = handle_storage_limit(); + break; + default: + s390_program_interrupt(env, PGM_SPECIFICATION, ra); + } +} diff --git a/hw/s390x/s390-hypercall.h b/hw/s390x/s390-hypercall.h new file mode 100644 index 00000000000..4f07209128c --- /dev/null +++ b/hw/s390x/s390-hypercall.h @@ -0,0 +1,25 @@ +/* + * Support for QEMU/KVM hypercalls on s390x + * + * Copyright IBM Corp. 2012, 2017 + * Author(s): Cornelia Huck + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef HW_S390_HYPERCALL_H +#define HW_S390_HYPERCALL_H + +#include "cpu.h" + +#define DIAG500_VIRTIO_NOTIFY 0 /* legacy, implemented as a NOP */ +#define DIAG500_VIRTIO_RESET 1 /* legacy */ +#define DIAG500_VIRTIO_SET_STATUS 2 /* legacy */ +#define DIAG500_VIRTIO_CCW_NOTIFY 3 /* KVM_S390_VIRTIO_CCW_NOTIFY */ +#define DIAG500_STORAGE_LIMIT 4 + +void handle_diag_500(S390CPU *cpu, uintptr_t ra); + +#endif /* HW_S390_HYPERCALL_H */ diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 2ca36f9f3be..e6aa44531f6 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -14,18 +14,21 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/visitor.h" +#include "exec/target_page.h" #include "hw/s390x/s390-pci-bus.h" #include "hw/s390x/s390-pci-inst.h" #include "hw/s390x/s390-pci-kvm.h" #include "hw/s390x/s390-pci-vfio.h" +#include "hw/s390x/s390-virtio-ccw.h" +#include "hw/boards.h" #include "hw/pci/pci_bus.h" #include "hw/qdev-properties.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/msi.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/reset.h" +#include "system/runstate.h" #include "trace.h" @@ -151,20 +154,12 @@ static void s390_pci_shutdown_notifier(Notifier *n, void *opaque) pci_device_reset(pbdev->pdev); } -static void s390_pci_reset_cb(void *opaque) -{ - S390PCIBusDevice *pbdev = opaque; - - 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); - qemu_unregister_reset(s390_pci_reset_cb, pbdev); } /* Unplug the PCI device */ @@ -603,7 +598,6 @@ static void s390_pci_iommu_replay(IOMMUMemoryRegion *iommu, * zpci device" construct. But when we support migration of vfio-pci * devices in future, we need to revisit this. */ - return; } static S390PCIIOMMU *s390_pci_get_iommu(S390pciState *s, PCIBus *bus, @@ -652,6 +646,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; @@ -728,12 +726,42 @@ void s390_pci_iommu_enable(S390PCIIOMMU *iommu) g_free(name); } +void s390_pci_iommu_direct_map_enable(S390PCIIOMMU *iommu) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + S390CcwMachineState *s390ms = S390_CCW_MACHINE(ms); + + /* + * For direct-mapping we must map the entire guest address space. Rather + * than using an iommu, create a memory region alias that maps GPA X to + * IOVA X + SDMA. VFIO will handle pinning via its memory listener. + */ + g_autofree char *name = g_strdup_printf("iommu-dm-s390-%04x", + iommu->pbdev->uid); + + iommu->dm_mr = g_malloc0(sizeof(*iommu->dm_mr)); + memory_region_init_alias(iommu->dm_mr, OBJECT(&iommu->mr), name, + get_system_memory(), 0, + s390_get_memory_limit(s390ms)); + iommu->enabled = true; + memory_region_add_subregion(&iommu->mr, iommu->pbdev->zpci_fn.sdma, + iommu->dm_mr); +} + void s390_pci_iommu_disable(S390PCIIOMMU *iommu) { iommu->enabled = false; g_hash_table_remove_all(iommu->iotlb); - memory_region_del_subregion(&iommu->mr, MEMORY_REGION(&iommu->iommu_mr)); - object_unparent(OBJECT(&iommu->iommu_mr)); + if (iommu->dm_mr) { + memory_region_del_subregion(&iommu->mr, iommu->dm_mr); + object_unparent(OBJECT(iommu->dm_mr)); + g_free(iommu->dm_mr); + iommu->dm_mr = NULL; + } else { + memory_region_del_subregion(&iommu->mr, + MEMORY_REGION(&iommu->iommu_mr)); + object_unparent(OBJECT(&iommu->iommu_mr)); + } } static void s390_pci_iommu_free(S390pciState *s, PCIBus *bus, int32_t devfn) @@ -839,7 +867,7 @@ static void s390_pcihost_realize(DeviceState *dev, Error **errp) 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)); @@ -975,14 +1003,7 @@ static void s390_pcihost_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, "this device"); } - if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { - PCIDevice *pdev = PCI_DEVICE(dev); - - if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { - error_setg(errp, "multifunction not supported in s390"); - return; - } - } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { S390PCIBusDevice *pbdev = S390_PCI_DEVICE(dev); if (!s390_pci_alloc_idx(s, pbdev)) { @@ -1058,7 +1079,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)); @@ -1073,6 +1094,18 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev, } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { pdev = PCI_DEVICE(dev); + /* + * Multifunction is not supported due to the lack of CLP. However, + * do not check for multifunction capability for SR-IOV devices because + * SR-IOV devices automatically add the multifunction capability whether + * the user intends to use the functions other than the PF. + */ + if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION && + !pdev->exp.sriov_cap) { + error_setg(errp, "multifunction not supported in s390"); + return; + } + if (!dev->id) { /* In the case the PCI device does not define an id */ /* we generate one based on the PCI address */ @@ -1084,6 +1117,16 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev, pbdev = s390_pci_find_dev_by_target(s, dev->id); if (!pbdev) { + /* + * VFs are automatically created by PF, and creating zpci for them + * will result in unexpected usage of fids. Currently QEMU does not + * support multifunction for s390x so we don't need zpci for VFs + * anyway. + */ + if (pci_is_vf(pdev)) { + return; + } + pbdev = s390_pci_device_new(s, dev->id, errp); if (!pbdev) { return; @@ -1128,13 +1171,13 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev, if (pbdev->pft == ZPCI_PFT_ISM) { pbdev->shutdown_notifier.notify = s390_pci_shutdown_notifier; qemu_register_shutdown_notifier(&pbdev->shutdown_notifier); - qemu_register_reset(s390_pci_reset_cb, pbdev); } } else { pbdev->fh |= FH_SHM_EMUL; /* Always intercept emulated devices */ pbdev->interp = false; pbdev->forwarding_assist = false; + pbdev->rtr_avail = false; } if (s390_pci_msix_init(pbdev) && !pbdev->interp) { @@ -1172,7 +1215,10 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, int32_t devfn; pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev)); - g_assert(pbdev); + if (!pbdev) { + g_assert(pci_is_vf(pci_dev)); + return; + } s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED, pbdev->fh, pbdev->fid); @@ -1211,7 +1257,11 @@ static void s390_pcihost_unplug_request(HotplugHandler *hotplug_dev, * we've checked the PCI device already (to prevent endless recursion). */ pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev)); - g_assert(pbdev); + if (!pbdev) { + g_assert(pci_is_vf(PCI_DEVICE(dev))); + return; + } + pbdev->pci_unplug_request_processed = true; qdev_unplug(DEVICE(pbdev), errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { @@ -1275,6 +1325,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); @@ -1306,12 +1373,12 @@ static void s390_pcihost_reset(DeviceState *dev) pci_for_each_device_under_bus(bus, s390_pci_enumerate_bridge, s); } -static void s390_pcihost_class_init(ObjectClass *klass, void *data) +static void s390_pcihost_class_init(ObjectClass *klass, const 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; @@ -1326,7 +1393,7 @@ static const TypeInfo s390_pcihost_info = { .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(S390pciState), .class_init = s390_pcihost_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } @@ -1441,7 +1508,7 @@ static void s390_pci_device_reset(DeviceState *dev) static void s390_pci_get_fid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint32(v, name, ptr, errp); @@ -1451,7 +1518,7 @@ static void s390_pci_set_fid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { S390PCIBusDevice *zpci = S390_PCI_DEVICE(obj); - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); if (!visit_type_uint32(v, name, ptr, errp)) { @@ -1461,7 +1528,8 @@ static void s390_pci_set_fid(Object *obj, Visitor *v, const char *name, } static const PropertyInfo s390_pci_fid_propinfo = { - .name = "zpci_fid", + .type = "uint32", + .description = "zpci_fid", .get = s390_pci_get_fid, .set = s390_pci_set_fid, }; @@ -1469,14 +1537,15 @@ static const PropertyInfo s390_pci_fid_propinfo = { #define DEFINE_PROP_S390_PCI_FID(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, s390_pci_fid_propinfo, uint32_t) -static Property s390_pci_device_properties[] = { +static const Property s390_pci_device_properties[] = { DEFINE_PROP_UINT16("uid", S390PCIBusDevice, uid, UID_UNDEFINED), DEFINE_PROP_S390_PCI_FID("fid", S390PCIBusDevice, fid), DEFINE_PROP_STRING("target", S390PCIBusDevice, target), DEFINE_PROP_BOOL("interpret", S390PCIBusDevice, interp, true), DEFINE_PROP_BOOL("forwarding-assist", S390PCIBusDevice, forwarding_assist, true), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_BOOL("relaxed-translation", S390PCIBusDevice, rtr_avail, + true), }; static const VMStateDescription s390_pci_device_vmstate = { @@ -1488,13 +1557,13 @@ static const VMStateDescription s390_pci_device_vmstate = { .unmigratable = 1, }; -static void s390_pci_device_class_init(ObjectClass *klass, void *data) +static void s390_pci_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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); @@ -1514,7 +1583,8 @@ static const TypeInfo s390_pci_iommu_info = { .instance_size = sizeof(S390PCIIOMMU), }; -static void s390_iommu_memory_region_class_init(ObjectClass *klass, void *data) +static void s390_iommu_memory_region_class_init(ObjectClass *klass, + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 30149546c08..b5dddb22b84 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -13,9 +13,11 @@ #include "qemu/osdep.h" #include "exec/memop.h" -#include "exec/memory.h" +#include "exec/target_page.h" +#include "system/memory.h" #include "qemu/error-report.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" +#include "hw/boards.h" #include "hw/pci/pci_device.h" #include "hw/s390x/s390-pci-inst.h" #include "hw/s390x/s390-pci-bus.h" @@ -55,26 +57,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); @@ -87,13 +89,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; @@ -102,33 +104,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? */ trace_s390_pci_list_entry(g_l2, - lduw_p(&rrb->response.fh_list[i].vendor_id), - lduw_p(&rrb->response.fh_list[i].device_id), - ldl_p(&rrb->response.fh_list[i].fid), - ldl_p(&rrb->response.fh_list[i].fh)); + 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++; } @@ -138,13 +140,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) { trace_s390_pci_list(rc); - stw_p(&rrb->response.hdr.rsp, res_code); + stw_be_p(&rrb->response.hdr.rsp, res_code); } return rc; } @@ -172,7 +174,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; @@ -184,7 +186,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; @@ -201,11 +203,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); @@ -215,9 +217,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; } @@ -225,17 +227,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; } @@ -249,29 +251,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_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: trace_s390_pci_unknown("set-pci", reqsetpci->oc); - stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); + stw_be_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); break; } break; @@ -280,23 +282,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) { - trace_s390_pci_nodev("query", ldl_p(&reqquery->fh)); - 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); @@ -304,16 +306,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; trace_s390_pci_bar(i, - ldl_p(&resquery->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: { @@ -326,23 +328,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: - trace_s390_pci_unknown("clp", lduw_p(&reqh->cmd)); - 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; } @@ -914,7 +916,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); @@ -935,13 +937,13 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, 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) { @@ -953,13 +955,13 @@ 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)); trace_s390_pci_irqs("register", pbdev->routes.adapter.adapter_id); return 0; @@ -994,9 +996,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; @@ -1008,17 +1010,25 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib, } /* currently we only support designation type 1 with translation */ - if (!(dt == ZPCI_IOTA_RTTO && t)) { + if (t && dt != ZPCI_IOTA_RTTO) { error_report("unsupported ioat dt %d t %d", dt, t); s390_program_interrupt(env, PGM_OPERAND, ra); return -EINVAL; + } else if (!t && !pbdev->rtr_avail) { + error_report("relaxed translation not allowed"); + s390_program_interrupt(env, PGM_OPERAND, ra); + return -EINVAL; } iommu->pba = pba; iommu->pal = pal; iommu->g_iota = g_iota; - s390_pci_iommu_enable(iommu); + if (t) { + s390_pci_iommu_enable(iommu); + } else { + s390_pci_iommu_direct_map_enable(iommu); + } return 0; } @@ -1289,7 +1299,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; @@ -1399,17 +1409,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 ff41e4106d1..9eef4fc3ec7 100644 --- a/hw/s390x/s390-pci-kvm.c +++ b/hw/s390x/s390-pci-kvm.c @@ -18,6 +18,7 @@ #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 59a2e03873b..aaf91319b4e 100644 --- a/hw/s390x/s390-pci-vfio.c +++ b/hw/s390x/s390-pci-vfio.c @@ -20,7 +20,8 @@ #include "hw/s390x/s390-pci-clp.h" #include "hw/s390x/s390-pci-vfio.h" #include "hw/vfio/pci.h" -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-container.h" +#include "hw/vfio/vfio-helpers.h" /* * Get the current DMA available count from vfio. Returns true if vfio is @@ -66,6 +67,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)) { @@ -127,13 +132,28 @@ static void s390_pci_read_base(S390PCIBusDevice *pbdev, /* Store function type separately for type-specific behavior */ pbdev->pft = cap->pft; + /* + * If the device is a passthrough ISM device, disallow relaxed + * translation. + */ + if (pbdev->pft == ZPCI_PFT_ISM) { + pbdev->rtr_avail = false; + } + /* * If appropriate, reduce the size of the supported DMA aperture reported - * to the guest based upon the vfio DMA limit. + * to the guest based upon the vfio DMA limit. This is applicable for + * devices that are guaranteed to not use relaxed translation. If the + * device is capable of relaxed translation then we must advertise the + * full aperture. In this case, if translation is used then we will + * rely on the vfio DMA limit counting and use RPCIT CC1 / status 16 + * to request that the guest free DMA mappings as necessary. */ - vfio_size = pbdev->iommu->max_dma_limit << TARGET_PAGE_BITS; - if (vfio_size < (cap->end_dma - cap->start_dma + 1)) { - pbdev->zpci_fn.edma = cap->start_dma + vfio_size - 1; + if (!pbdev->rtr_avail) { + 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; + } } } @@ -219,8 +239,11 @@ static void s390_pci_read_group(S390PCIBusDevice *pbdev, pbdev->pci_group = s390_group_create(pbdev->zpci_fn.pfgid, start_gid); resgrp = &pbdev->pci_group->zpci_group; + if (pbdev->rtr_avail) { + resgrp->fr |= CLP_RSP_QPCIG_MASK_RTR; + } if (cap->flags & VFIO_DEVICE_INFO_ZPCI_FLAG_REFRESH) { - resgrp->fr = 1; + resgrp->fr |= CLP_RSP_QPCIG_MASK_REFRESH; } resgrp->dasm = cap->dasm; resgrp->msia = cap->msi_addr; @@ -345,6 +368,4 @@ void s390_pci_get_clp_info(S390PCIBusDevice *pbdev) s390_pci_read_group(pbdev, info); s390_pci_read_util(pbdev, info); s390_pci_read_pfip(pbdev, info); - - return; } diff --git a/hw/s390x/s390-skeys-kvm.c b/hw/s390x/s390-skeys-kvm.c index 3ff9d94b802..f3056d6d25c 100644 --- a/hw/s390x/s390-skeys-kvm.c +++ b/hw/s390x/s390-skeys-kvm.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "hw/s390x/storage-keys.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qemu/error-report.h" #include "qemu/module.h" @@ -52,7 +52,7 @@ static int kvm_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn, return kvm_vm_ioctl(kvm_state, KVM_S390_SET_SKEYS, &args); } -static void kvm_s390_skeys_class_init(ObjectClass *oc, void *data) +static void kvm_s390_skeys_class_init(ObjectClass *oc, const void *data) { S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 5024faf4113..8eeecfd58fc 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -11,17 +11,20 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "hw/boards.h" +#include "exec/target_page.h" +#include "hw/s390x/s390-virtio-ccw.h" +#include "hw/qdev-properties.h" #include "hw/s390x/storage-keys.h" #include "qapi/error.h" -#include "qapi/qapi-commands-misc-target.h" -#include "qapi/qmp/qdict.h" +#include "qapi/qapi-commands-machine.h" +#include "qobject/qdict.h" #include "qemu/error-report.h" -#include "sysemu/memory_mapping.h" -#include "exec/address-spaces.h" -#include "sysemu/kvm.h" +#include "system/memory_mapping.h" +#include "system/address-spaces.h" +#include "system/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 +56,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) { @@ -114,7 +143,7 @@ void hmp_dump_skeys(Monitor *mon, const QDict *qdict) } } -void qmp_dump_skeys(const char *filename, Error **errp) +void s390_qmp_dump_skeys(const char *filename, Error **errp) { S390SKeysState *ss = s390_get_skeys_device(); S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); @@ -152,7 +181,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); @@ -223,9 +252,9 @@ static bool qemu_s390_enable_skeys(S390SKeysState *ss) * g_once_init_enter() is good enough. */ if (g_once_init_enter(&initialized)) { - MachineState *machine = MACHINE(qdev_get_machine()); + S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine()); - skeys->key_count = machine->ram_size / TARGET_PAGE_SIZE; + skeys->key_count = s390_get_memory_limit(s390ms) / TARGET_PAGE_SIZE; skeys->keydata = g_malloc0(skeys->key_count); g_once_init_leave(&initialized, 1); } @@ -274,7 +303,7 @@ static int qemu_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn, return 0; } -static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data) +static void qemu_s390_skeys_class_init(ObjectClass *oc, const void *data) { S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -288,14 +317,6 @@ static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data) dc->user_creatable = false; } -static const TypeInfo qemu_s390_skeys_info = { - .name = TYPE_QEMU_S390_SKEYS, - .parent = TYPE_S390_SKEYS, - .instance_size = sizeof(QEMUS390SKeysState), - .class_init = qemu_s390_skeys_class_init, - .class_size = sizeof(S390SKeysClass), -}; - static void s390_storage_keys_save(QEMUFile *f, void *opaque) { S390SKeysState *ss = S390_SKEYS(opaque); @@ -432,68 +453,48 @@ 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; - - 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); - } -} + S390SKeysState *ss = S390_SKEYS(dev); -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); + register_savevm_live(TYPE_S390_SKEYS, 0, 1, &savevm_s390_storage_keys, ss); } -static void s390_skeys_class_init(ObjectClass *oc, void *data) +static void s390_skeys_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->hotpluggable = false; + dc->realize = s390_skeys_realize; 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), - .abstract = true, +static const TypeInfo s390_skeys_types[] = { + { + .name = TYPE_DUMP_SKEYS_INTERFACE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(DumpSKeysInterface), + }, + { + .name = TYPE_S390_SKEYS, + .parent = TYPE_DEVICE, + .instance_size = sizeof(S390SKeysState), + .class_init = s390_skeys_class_init, + .class_size = sizeof(S390SKeysClass), + .abstract = true, + }, + { + .name = TYPE_QEMU_S390_SKEYS, + .parent = TYPE_S390_SKEYS, + .instance_size = sizeof(QEMUS390SKeysState), + .class_init = qemu_s390_skeys_class_init, + .class_size = sizeof(S390SKeysClass), + }, }; -static void qemu_s390_skeys_register_types(void) -{ - type_register_static(&s390_skeys_info); - type_register_static(&qemu_s390_skeys_info); -} - -type_init(qemu_s390_skeys_register_types) +DEFINE_TYPES(s390_skeys_types) diff --git a/hw/s390x/s390-stattrib-kvm.c b/hw/s390x/s390-stattrib-kvm.c index 24cd01382e2..e1fee361dc3 100644 --- a/hw/s390x/s390-stattrib-kvm.c +++ b/hw/s390x/s390-stattrib-kvm.c @@ -10,13 +10,15 @@ */ #include "qemu/osdep.h" -#include "hw/boards.h" +#include "hw/s390x/s390-virtio-ccw.h" #include "migration/qemu-file.h" #include "hw/s390x/storage-attributes.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" -#include "exec/ram_addr.h" +#include "system/kvm.h" +#include "system/memory_mapping.h" +#include "system/ram_addr.h" #include "kvm/kvm_s390x.h" +#include "qapi/error.h" Object *kvm_s390_stattrib_create(void) { @@ -83,8 +85,8 @@ static int kvm_s390_stattrib_set_stattr(S390StAttribState *sa, uint8_t *values) { KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa); - MachineState *machine = MACHINE(qdev_get_machine()); - unsigned long max = machine->ram_size / TARGET_PAGE_SIZE; + S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine()); + unsigned long max = s390_get_memory_limit(s390ms) / TARGET_PAGE_SIZE; if (start_gfn + count > max) { error_report("Out of memory bounds when setting storage attributes"); @@ -102,49 +104,74 @@ static int kvm_s390_stattrib_set_stattr(S390StAttribState *sa, static void kvm_s390_stattrib_synchronize(S390StAttribState *sa) { KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa); - MachineState *machine = MACHINE(qdev_get_machine()); - unsigned long max = machine->ram_size / TARGET_PAGE_SIZE; - /* We do not need to reach the maximum buffer size allowed */ - unsigned long cx, len = KVM_S390_SKEYS_MAX / 2; + S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine()); + unsigned long max = s390_get_memory_limit(s390ms) / TARGET_PAGE_SIZE; + unsigned long start_gfn, end_gfn, pages; + GuestPhysBlockList guest_phys_blocks; + GuestPhysBlock *block; int r; struct kvm_s390_cmma_log clog = { .flags = 0, .mask = ~0ULL, }; - if (sas->incoming_buffer) { - for (cx = 0; cx + len <= max; cx += len) { - clog.start_gfn = cx; - clog.count = len; - clog.values = (uint64_t)(sas->incoming_buffer + cx); - r = kvm_vm_ioctl(kvm_state, KVM_S390_SET_CMMA_BITS, &clog); - if (r) { - error_report("KVM_S390_SET_CMMA_BITS failed: %s", strerror(-r)); - return; - } - } - if (cx < max) { - clog.start_gfn = cx; - clog.count = max - cx; - clog.values = (uint64_t)(sas->incoming_buffer + cx); + if (!sas->incoming_buffer) { + return; + } + guest_phys_blocks_init(&guest_phys_blocks); + guest_phys_blocks_append(&guest_phys_blocks); + + QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) { + assert(QEMU_IS_ALIGNED(block->target_start, TARGET_PAGE_SIZE)); + assert(QEMU_IS_ALIGNED(block->target_end, TARGET_PAGE_SIZE)); + + start_gfn = block->target_start / TARGET_PAGE_SIZE; + end_gfn = block->target_end / TARGET_PAGE_SIZE; + + while (start_gfn < end_gfn) { + /* Don't exceed the maximum buffer size. */ + pages = MIN(end_gfn - start_gfn, KVM_S390_SKEYS_MAX / 2); + + /* + * If we ever get guest physical memory beyond the configured + * memory limit, something went very wrong. + */ + assert(start_gfn + pages <= max); + + clog.start_gfn = start_gfn; + clog.count = pages; + clog.values = (uint64_t)(sas->incoming_buffer + start_gfn); r = kvm_vm_ioctl(kvm_state, KVM_S390_SET_CMMA_BITS, &clog); if (r) { error_report("KVM_S390_SET_CMMA_BITS failed: %s", strerror(-r)); + goto out; } + + start_gfn += pages; } - g_free(sas->incoming_buffer); - sas->incoming_buffer = NULL; } + +out: + guest_phys_blocks_free(&guest_phys_blocks); + g_free(sas->incoming_buffer); + sas->incoming_buffer = NULL; } -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) @@ -158,10 +185,10 @@ static long long kvm_s390_stattrib_get_dirtycount(S390StAttribState *sa) static int kvm_s390_stattrib_get_active(S390StAttribState *sa) { - return kvm_s390_cmma_active() && sa->migration_enabled; + return kvm_s390_cmma_active(); } -static void kvm_s390_stattrib_class_init(ObjectClass *oc, void *data) +static void kvm_s390_stattrib_class_init(ObjectClass *oc, const void *data) { S390StAttribClass *sac = S390_STATTRIB_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index 220e845d127..f74cf326361 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 "system/ram_addr.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/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; } @@ -259,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) @@ -292,17 +296,18 @@ 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; } static int qemu_s390_get_active(S390StAttribState *sa) { - return sa->migration_enabled; + return true; } -static void qemu_s390_stattrib_class_init(ObjectClass *oc, void *data) +static void qemu_s390_stattrib_class_init(ObjectClass *oc, const void *data) { S390StAttribClass *sa_cl = S390_STATTRIB_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -330,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; @@ -337,10 +353,14 @@ 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 void s390_stattrib_class_init(ObjectClass *oc, void *data) +static void s390_stattrib_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -349,44 +369,10 @@ static void s390_stattrib_class_init(ObjectClass *oc, void *data) dc->realize = s390_stattrib_realize; } -static inline bool s390_stattrib_get_migration_enabled(Object *obj, - Error **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, - .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_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 2d75f2131fc..ce3c13defb5 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -13,12 +13,11 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "exec/ram_addr.h" -#include "hw/s390x/s390-virtio-hcall.h" +#include "system/ram_addr.h" +#include "system/confidential-guest-support.h" +#include "hw/boards.h" #include "hw/s390x/sclp.h" #include "hw/s390x/s390_flic.h" -#include "hw/s390x/ioinst.h" -#include "hw/s390x/css.h" #include "virtio-ccw.h" #include "qemu/config-file.h" #include "qemu/ctype.h" @@ -27,7 +26,7 @@ #include "qemu/qemu-print.h" #include "qemu/units.h" #include "hw/s390x/s390-pci-bus.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/s390x/storage-keys.h" #include "hw/s390x/storage-attributes.h" #include "hw/s390x/event-facility.h" @@ -36,34 +35,25 @@ #include "hw/s390x/css-bridge.h" #include "hw/s390x/ap-bridge.h" #include "migration/register.h" -#include "cpu_models.h" +#include "target/s390x/cpu_models.h" #include "hw/nmi.h" #include "hw/qdev-properties.h" #include "hw/s390x/tod.h" -#include "sysemu/sysemu.h" -#include "sysemu/cpus.h" +#include "system/system.h" +#include "system/cpus.h" +#include "system/hostmem.h" #include "target/s390x/kvm/pv.h" #include "migration/blocker.h" #include "qapi/visitor.h" +#include "hw/s390x/cpu-topology.h" +#include "kvm/kvm_s390x.h" +#include "hw/virtio/virtio-md-pci.h" +#include "hw/s390x/virtio-ccw-md.h" +#include "system/replay.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) { @@ -117,69 +107,105 @@ 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) { device_cold_reset(dev); } } + if (s390_has_topology()) { + s390_topology_reset(); + } } -static int virtio_ccw_hcall_notify(const uint64_t *args) +static void s390_set_memory_limit(S390CcwMachineState *s390ms, + uint64_t new_limit) { - uint64_t subch_id = args[0]; - uint64_t queue = args[1]; - SubchDev *sch; - int cssid, ssid, schid, m; + uint64_t hw_limit = 0; + int ret = 0; - if (ioinst_disassemble_sch_ident(subch_id, &m, &cssid, &ssid, &schid)) { - return -EINVAL; - } - sch = css_find_subch(m, cssid, ssid, schid); - if (!sch || !css_subch_visible(sch)) { - return -EINVAL; + assert(!s390ms->memory_limit && new_limit); + if (kvm_enabled()) { + ret = kvm_s390_set_mem_limit(new_limit, &hw_limit); } - if (queue >= VIRTIO_QUEUE_MAX) { - return -EINVAL; + if (ret == -E2BIG) { + error_report("host supports a maximum of %" PRIu64 " GB", + hw_limit / GiB); + exit(EXIT_FAILURE); + } else if (ret) { + error_report("setting the guest size failed"); + exit(EXIT_FAILURE); } - virtio_queue_notify(virtio_ccw_get_vdev(sch), queue); - return 0; - + s390ms->memory_limit = new_limit; } -static int virtio_ccw_hcall_early_printk(const uint64_t *args) +static void s390_set_max_pagesize(S390CcwMachineState *s390ms, + uint64_t pagesize) { - uint64_t mem = args[0]; - MachineState *ms = MACHINE(qdev_get_machine()); - - if (mem < ms->ram_size) { - /* Early printk */ - return 0; + assert(!s390ms->max_pagesize && pagesize); + if (kvm_enabled()) { + kvm_s390_set_max_pagesize(pagesize, &error_fatal); } - return -EINVAL; -} - -static void virtio_ccw_register_hcalls(void) -{ - s390_register_virtio_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, - virtio_ccw_hcall_notify); - /* Tolerate early printk. */ - s390_register_virtio_hypercall(KVM_S390_VIRTIO_NOTIFY, - virtio_ccw_hcall_early_printk); + s390ms->max_pagesize = pagesize; } -static void s390_memory_init(MemoryRegion *ram) +static void s390_memory_init(MachineState *machine) { + S390CcwMachineState *s390ms = S390_CCW_MACHINE(machine); MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *ram = machine->ram; + uint64_t ram_size = memory_region_size(ram); + uint64_t devmem_base, devmem_size; + + if (!QEMU_IS_ALIGNED(ram_size, 1 * MiB)) { + /* + * SCLP cannot possibly expose smaller granularity right now and KVM + * cannot handle smaller granularity. As we don't support NUMA, the + * region size directly corresponds to machine->ram_size, and the region + * is a single RAM memory region. + */ + error_report("ram size must be multiples of 1 MiB"); + exit(EXIT_FAILURE); + } + + devmem_size = 0; + devmem_base = ram_size; +#ifdef CONFIG_MEM_DEVICE + if (machine->ram_size < machine->maxram_size) { + + /* + * Make sure memory devices have a sane default alignment, even + * when weird initial memory sizes are specified. + */ + devmem_base = QEMU_ALIGN_UP(devmem_base, 1 * GiB); + devmem_size = machine->maxram_size - machine->ram_size; + } +#endif + s390_set_memory_limit(s390ms, devmem_base + devmem_size); - /* allocate RAM for core */ + /* Map the initial memory. Must happen after setting the memory limit. */ memory_region_add_subregion(sysmem, 0, ram); + /* Initialize address space for memory devices. */ +#ifdef CONFIG_MEM_DEVICE + if (devmem_size) { + machine_memory_devices_init(machine, devmem_base, devmem_size); + } +#endif /* CONFIG_MEM_DEVICE */ + /* * Configure the maximum page size. As no memory devices were created * yet, this is the page size of initial memory only. */ - s390_set_max_pagesize(qemu_maxrampagesize(), &error_fatal); + s390_set_max_pagesize(s390ms, qemu_maxrampagesize()); /* Initialize storage key device */ s390_skeys_init(); /* Initialize storage attributes device */ @@ -189,11 +215,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); @@ -204,11 +229,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); @@ -217,45 +237,51 @@ static void s390_init_ipl_dev(const char *kernel_filename, static void s390_create_virtio_net(BusState *bus, const char *name) { - int i; - - for (i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - DeviceState *dev; - - qemu_check_nic_model(nd, "virtio"); + DeviceState *dev; + int cnt = 0; - 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); + s390_memory_init(machine); /* init CPUs (incl. CPU model) early so s390_has_feature() works */ 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(); @@ -270,34 +296,29 @@ 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, OBJECT(dev)); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - /* register hypercalls */ - virtio_ccw_register_hcalls(); - 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), 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 */ @@ -307,11 +328,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(); @@ -332,12 +361,12 @@ static void s390_machine_unprotect(S390CcwMachineState *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); } -static int s390_machine_protect(S390CcwMachineState *ms) +static int s390_machine_protect(S390CcwMachineState *ms, + struct S390PVResponse *pv_resp) { Error *local_err = NULL; int rc; @@ -356,11 +385,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; } @@ -368,8 +396,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; } @@ -382,25 +409,28 @@ static int s390_machine_protect(S390CcwMachineState *ms) } /* Set SE header and unpack */ - rc = s390_ipl_prepare_pv_header(); + rc = s390_ipl_prepare_pv_header(pv_resp, &local_err); if (rc) { goto out_err; } /* Decrypt image */ - rc = s390_ipl_pv_unpack(); + rc = s390_ipl_pv_unpack(pv_resp); if (rc) { goto out_err; } /* Verify integrity */ - rc = s390_pv_verify(); + rc = s390_pv_verify(pv_resp); if (rc) { goto out_err; } return rc; out_err: + if (local_err) { + error_report_err(local_err); + } s390_machine_unprotect(ms); return rc; } @@ -420,13 +450,26 @@ 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); + struct S390PVResponse pv_resp; enum s390_reset reset_type; CPUState *cs, *t; S390CPU *cpu; + /* + * Temporarily drop the record/replay mutex to let rr_cpu_thread_fn() + * process the run_on_cpu() requests below. This is safe, because at this + * point one of the following is true: + * - All CPU threads are not running, either because the machine is being + * initialized, or because the guest requested a reset using diag 308. + * There is no risk to desync the record/replay state. + * - A snapshot is about to be loaded. The record/replay state consistency + * is not important. + */ + replay_mutex_unlock(); + /* get the reset parameters, reset them once done */ s390_ipl_get_reset_request(&cs, &reset_type); @@ -452,7 +495,7 @@ static void s390_machine_reset(MachineState *machine, ShutdownCause reason) * Device reset includes CPU clear resets so this has to be * done AFTER the unprotect call above. */ - qemu_devices_reset(reason); + qemu_devices_reset(type); s390_crypto_reset(); /* configure and start the ipl CPU only */ @@ -499,14 +542,14 @@ static void s390_machine_reset(MachineState *machine, ShutdownCause reason) } run_on_cpu(cs, s390_do_cpu_reset, RUN_ON_CPU_NULL); - if (s390_machine_protect(ms)) { - s390_pv_inject_reset_error(cs); + if (s390_machine_protect(ms, &pv_resp)) { + s390_pv_inject_reset_error(cs, pv_resp); /* * Continue after the diag308 so the guest knows something * went wrong. */ s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu); - return; + goto out_lock; } run_on_cpu(cs, s390_do_cpu_load_normal, RUN_ON_CPU_NULL); @@ -519,13 +562,54 @@ static void s390_machine_reset(MachineState *machine, ShutdownCause reason) run_on_cpu(t, s390_do_cpu_set_diag318, RUN_ON_CPU_HOST_ULONG(0)); } s390_ipl_clear_reset_request(); + +out_lock: + /* + * Re-take the record/replay mutex, temporarily dropping the BQL in order + * to satisfy the ordering requirements. + */ + bql_unlock(); + replay_mutex_lock(); + bql_lock(); +} + +static void s390_machine_device_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) { + virtio_ccw_md_pre_plug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_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); + } } static void s390_machine_device_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + S390CcwMachineState *s390ms = S390_CCW_MACHINE(hotplug_dev); + if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { s390_cpu_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { + /* + * At this point, the device is realized and set all memdevs mapped, so + * qemu_maxrampagesize() will pick up the page sizes of these memdevs + * as well. Before we plug the device and expose any RAM memory regions + * to the system, make sure we don't exceed the previously set max page + * size. While only relevant for KVM, there is not really any use case + * for this with TCG, so we'll unconditionally reject it. + */ + if (qemu_maxrampagesize() != s390ms->max_pagesize) { + error_setg(errp, "Memory device uses a bigger page size than" + " initial memory"); + return; + } + if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) { + virtio_ccw_md_plug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), errp); + } else { + virtio_md_pci_plug(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), errp); + } } } @@ -534,10 +618,25 @@ static void s390_machine_device_unplug_request(HotplugHandler *hotplug_dev, { if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { error_setg(errp, "CPU hot unplug not supported on this machine"); - return; + } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) { + virtio_ccw_md_unplug_request(VIRTIO_MD_CCW(dev), MACHINE(hotplug_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); } } +static void s390_machine_device_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) { + virtio_ccw_md_unplug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_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); + } + } + static CpuInstanceProperties s390_cpu_index_to_props(MachineState *ms, unsigned cpu_index) { @@ -562,11 +661,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; @@ -575,7 +683,9 @@ static const CPUArchIdList *s390_possible_cpu_arch_ids(MachineState *ms) static HotplugHandler *s390_get_hotplug_handler(MachineState *machine, DeviceState *dev) { - if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) { return HOTPLUG_HANDLER(machine); } return NULL; @@ -638,50 +748,6 @@ static inline void machine_set_dea_key_wrap(Object *obj, bool value, ms->dea_key_wrap = value; } -static S390CcwMachineClass *current_mc; - -/* - * Get the class of the s390-ccw-virtio machine that is currently in use. - * Note: libvirt is using the "none" machine to probe for the features of the - * host CPU, so in case this is called with the "none" machine, the function - * returns the TYPE_S390_CCW_MACHINE base class. In this base class, all the - * various "*_allowed" variables are enabled, so that the *_allowed() wrappers - * below return the correct default value for the "none" machine. - * - * Attention! Do *not* add additional new wrappers for CPU features (e.g. like - * the ri_allowed() wrapper) via this mechanism anymore. CPU features should - * be handled via the CPU models, i.e. checking with cpu_model_allowed() during - * CPU initialization and s390_has_feat() later should be sufficient. - */ -static S390CcwMachineClass *get_machine_class(void) -{ - if (unlikely(!current_mc)) { - /* - * No s390 ccw machine was instantiated, we are likely to - * be called for the 'none' machine. The properties will - * have their after-initialization values. - */ - current_mc = S390_CCW_MACHINE_CLASS( - object_class_by_name(TYPE_S390_CCW_MACHINE)); - } - return current_mc; -} - -bool ri_allowed(void) -{ - return get_machine_class()->ri_allowed; -} - -bool cpu_model_allowed(void) -{ - return get_machine_class()->cpu_model_allowed; -} - -bool hpage_1m_allowed(void) -{ - return get_machine_class()->hpage_1m_allowed; -} - static void machine_get_loadparm(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -699,62 +765,47 @@ 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); + g_free(val); } -static void ccw_machine_class_init(ObjectClass *oc, void *data) +static void ccw_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); + DumpSKeysInterface *dsi = DUMP_SKEYS_INTERFACE_CLASS(oc); - s390mc->ri_allowed = true; - s390mc->cpu_model_allowed = true; - s390mc->css_migration_enabled = true; - s390mc->hpage_1m_allowed = true; s390mc->max_threads = 1; - mc->init = ccw_init; mc->reset = s390_machine_reset; mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; mc->no_floppy = 1; mc->no_parallel = 1; - mc->no_sdcard = 1; mc->max_cpus = S390_MAX_CPUS; mc->has_hotpluggable_cpus = true; + mc->smp_props.books_supported = true; + mc->smp_props.drawers_supported = true; assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = s390_get_hotplug_handler; mc->cpu_index_to_instance_props = s390_cpu_index_to_props; mc->possible_cpu_arch_ids = s390_possible_cpu_arch_ids; /* it is overridden with 'host' cpu *in kvm_arch_init* */ mc->default_cpu_type = S390_CPU_TYPE_NAME("qemu"); + hc->pre_plug = s390_machine_device_pre_plug; hc->plug = s390_machine_device_plug; hc->unplug_request = s390_machine_device_unplug_request; + hc->unplug = s390_machine_device_unplug; nc->nmi_monitor_handler = s390_nmi; mc->default_ram_id = "s390.ram"; mc->default_nic = "virtio-net-ccw"; + dsi->qmp_dump_skeys = s390_qmp_dump_skeys; object_class_property_add_bool(oc, "aes-key-wrap", machine_get_aes_key_wrap, @@ -793,56 +844,132 @@ static const TypeInfo ccw_machine_info = { .instance_init = s390_machine_initfn, .class_size = sizeof(S390CcwMachineClass), .class_init = ccw_machine_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NMI }, { TYPE_HOTPLUG_HANDLER}, + { TYPE_DUMP_SKEYS_INTERFACE}, { } }, }; -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(mach_init, ccw, __VA_ARGS__)(MachineState *mach) \ + { \ + MACHINE_VER_SYM(instance_options, ccw, __VA_ARGS__)(mach); \ + ccw_init(mach); \ + } \ + static void MACHINE_VER_SYM(class_init, ccw, __VA_ARGS__)( \ + ObjectClass *oc, \ + const 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__) ")"; \ + mc->init = MACHINE_VER_SYM(mach_init, ccw, __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 const TypeInfo MACHINE_VER_SYM(info, ccw, __VA_ARGS__) = \ { \ - MachineState *machine = MACHINE(obj); \ - current_mc = S390_CCW_MACHINE_CLASS(MACHINE_GET_CLASS(machine)); \ - ccw_machine_##suffix##_instance_options(machine); \ - } \ - static const TypeInfo ccw_machine_##suffix##_info = { \ - .name = MACHINE_TYPE_NAME("s390-ccw-virtio-" verstr), \ + .name = MACHINE_VER_TYPE_NAME("s390-ccw-virtio", __VA_ARGS__), \ .parent = TYPE_S390_CCW_MACHINE, \ - .class_init = ccw_machine_##suffix##_class_init, \ - .instance_init = ccw_machine_##suffix##_instance_init, \ + .class_init = MACHINE_VER_SYM(class_init, ccw, __VA_ARGS__), \ }; \ - static void ccw_machine_register_##suffix(void) \ + static void MACHINE_VER_SYM(register, ccw, __VA_ARGS__)(void) \ { \ - type_register_static(&ccw_machine_##suffix##_info); \ + 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_10_1_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_10_1_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE_AS_LATEST(10, 1); + +static void ccw_machine_10_0_instance_options(MachineState *machine) +{ + ccw_machine_10_1_instance_options(machine); +} + +static void ccw_machine_10_0_class_options(MachineClass *mc) +{ + ccw_machine_10_1_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_0, hw_compat_10_0_len); +} +DEFINE_CCW_MACHINE(10, 0); + +static void ccw_machine_9_2_instance_options(MachineState *machine) +{ + ccw_machine_10_0_instance_options(machine); +} + +static void ccw_machine_9_2_class_options(MachineClass *mc) +{ + static GlobalProperty compat[] = { + { TYPE_S390_PCI_DEVICE, "relaxed-translation", "off", }, + }; + + ccw_machine_10_0_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_2, hw_compat_9_2_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); +} +DEFINE_CCW_MACHINE(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, "8.2", true); +DEFINE_CCW_MACHINE(8, 2); static void ccw_machine_8_1_instance_options(MachineState *machine) { @@ -853,8 +980,10 @@ static void ccw_machine_8_1_class_options(MachineClass *mc) { ccw_machine_8_2_class_options(mc); compat_props_add(mc->compat_props, hw_compat_8_1, hw_compat_8_1_len); + mc->smp_props.drawers_supported = false; + mc->smp_props.books_supported = false; } -DEFINE_CCW_MACHINE(8_1, "8.1", false); +DEFINE_CCW_MACHINE(8, 1); static void ccw_machine_8_0_instance_options(MachineState *machine) { @@ -866,7 +995,7 @@ static void ccw_machine_8_0_class_options(MachineClass *mc) ccw_machine_8_1_class_options(mc); compat_props_add(mc->compat_props, hw_compat_8_0, hw_compat_8_0_len); } -DEFINE_CCW_MACHINE(8_0, "8.0", false); +DEFINE_CCW_MACHINE(8, 0); static void ccw_machine_7_2_instance_options(MachineState *machine) { @@ -878,7 +1007,7 @@ static void ccw_machine_7_2_class_options(MachineClass *mc) ccw_machine_8_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_7_2, hw_compat_7_2_len); } -DEFINE_CCW_MACHINE(7_2, "7.2", false); +DEFINE_CCW_MACHINE(7, 2); static void ccw_machine_7_1_instance_options(MachineState *machine) { @@ -902,7 +1031,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) { @@ -917,7 +1046,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) { @@ -932,7 +1061,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) { @@ -950,7 +1079,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) { @@ -965,7 +1094,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) { @@ -977,7 +1106,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) { @@ -989,7 +1118,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) { @@ -1001,7 +1130,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) { @@ -1014,226 +1143,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); - -static void ccw_machine_4_1_instance_options(MachineState *machine) -{ - static const S390FeatInit qemu_cpu_feat = { S390_FEAT_LIST_QEMU_V4_1 }; - ccw_machine_4_2_instance_options(machine); - s390_set_qemu_cpu_model(0x2964, 13, 2, qemu_cpu_feat); -} - -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); - -static void ccw_machine_4_0_instance_options(MachineState *machine) -{ - static const S390FeatInit qemu_cpu_feat = { S390_FEAT_LIST_QEMU_V4_0 }; - ccw_machine_4_1_instance_options(machine); - s390_set_qemu_cpu_model(0x2827, 12, 2, qemu_cpu_feat); -} - -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); - -static void ccw_machine_3_1_instance_options(MachineState *machine) -{ - static const S390FeatInit qemu_cpu_feat = { S390_FEAT_LIST_QEMU_V3_1 }; - ccw_machine_4_0_instance_options(machine); - s390_cpudef_featoff_greater(14, 1, S390_FEAT_MULTIPLE_EPOCH); - s390_cpudef_group_featoff_greater(14, 1, S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF); - s390_set_qemu_cpu_model(0x2827, 12, 2, qemu_cpu_feat); -} - -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); - -static void ccw_machine_3_0_instance_options(MachineState *machine) -{ - ccw_machine_3_1_instance_options(machine); -} - -static void ccw_machine_3_0_class_options(MachineClass *mc) -{ - S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); - - s390mc->hpage_1m_allowed = false; - 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); - -static void ccw_machine_2_12_instance_options(MachineState *machine) -{ - ccw_machine_3_0_instance_options(machine); - s390_cpudef_featoff_greater(11, 1, S390_FEAT_PPA15); - s390_cpudef_featoff_greater(11, 1, S390_FEAT_BPB); -} - -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); - -static void ccw_machine_2_11_instance_options(MachineState *machine) -{ - static const S390FeatInit qemu_cpu_feat = { S390_FEAT_LIST_QEMU_V2_11 }; - ccw_machine_2_12_instance_options(machine); - - /* before 2.12 we emulated the very first z900 */ - s390_set_qemu_cpu_model(0x2064, 7, 1, qemu_cpu_feat); -} - -static void ccw_machine_2_11_class_options(MachineClass *mc) -{ - static GlobalProperty compat[] = { - { TYPE_SCLP_EVENT_FACILITY, "allow_all_mask_sizes", "off", }, - }; - - ccw_machine_2_12_class_options(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); - -static void ccw_machine_2_10_instance_options(MachineState *machine) -{ - ccw_machine_2_11_instance_options(machine); -} - -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); - -static void ccw_machine_2_9_instance_options(MachineState *machine) -{ - ccw_machine_2_10_instance_options(machine); - s390_cpudef_featoff_greater(12, 1, S390_FEAT_ESOP); - s390_cpudef_featoff_greater(12, 1, S390_FEAT_SIDE_EFFECT_ACCESS_ESOP2); - s390_cpudef_featoff_greater(12, 1, S390_FEAT_ZPCI); - s390_cpudef_featoff_greater(12, 1, S390_FEAT_ADAPTER_INT_SUPPRESSION); - s390_cpudef_featoff_greater(12, 1, S390_FEAT_ADAPTER_EVENT_NOTIFICATION); -} - -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", }, - }; - - 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; -} -DEFINE_CCW_MACHINE(2_9, "2.9", false); - -static void ccw_machine_2_8_instance_options(MachineState *machine) -{ - ccw_machine_2_9_instance_options(machine); -} - -static void ccw_machine_2_8_class_options(MachineClass *mc) -{ - static GlobalProperty compat[] = { - { TYPE_S390_FLIC_COMMON, "adapter_routes_max_batch", "64", }, - }; - - ccw_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)); -} -DEFINE_CCW_MACHINE(2_8, "2.8", false); - -static void ccw_machine_2_7_instance_options(MachineState *machine) -{ - ccw_machine_2_8_instance_options(machine); -} - -static void ccw_machine_2_7_class_options(MachineClass *mc) -{ - S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); - - s390mc->cpu_model_allowed = false; - 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); - -static void ccw_machine_2_6_instance_options(MachineState *machine) -{ - ccw_machine_2_7_instance_options(machine); -} - -static void ccw_machine_2_6_class_options(MachineClass *mc) -{ - S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); - static GlobalProperty compat[] = { - { TYPE_S390_IPL, "iplbext_migration", "off", }, - { TYPE_VIRTUAL_CSS_BRIDGE, "css_dev_path", "off", }, - }; - - s390mc->ri_allowed = false; - ccw_machine_2_7_class_options(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); - -static void ccw_machine_2_5_instance_options(MachineState *machine) -{ - ccw_machine_2_6_instance_options(machine); -} - -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); - -static void ccw_machine_2_4_instance_options(MachineState *machine) -{ - ccw_machine_2_5_instance_options(machine); -} - -static void ccw_machine_2_4_class_options(MachineClass *mc) -{ - static GlobalProperty compat[] = { - { TYPE_S390_SKEYS, "migration-enabled", "off", }, - { "virtio-blk-ccw", "max_revision", "0", }, - { "virtio-balloon-ccw", "max_revision", "0", }, - { "virtio-serial-ccw", "max_revision", "0", }, - { "virtio-9p-ccw", "max_revision", "0", }, - { "virtio-rng-ccw", "max_revision", "0", }, - { "virtio-net-ccw", "max_revision", "0", }, - { "virtio-scsi-ccw", "max_revision", "0", }, - { "vhost-scsi-ccw", "max_revision", "0", }, - }; - - ccw_machine_2_5_class_options(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(4, 2); static void ccw_machine_register_types(void) { diff --git a/hw/s390x/s390-virtio-hcall.c b/hw/s390x/s390-virtio-hcall.c deleted file mode 100644 index ec7cf8beb3f..00000000000 --- a/hw/s390x/s390-virtio-hcall.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Support for virtio hypercalls on s390 - * - * Copyright 2012 IBM Corp. - * Author(s): Cornelia Huck - * - * 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 "cpu.h" -#include "hw/s390x/s390-virtio-hcall.h" - -#define MAX_DIAG_SUBCODES 255 - -static s390_virtio_fn s390_diag500_table[MAX_DIAG_SUBCODES]; - -void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn) -{ - assert(code < MAX_DIAG_SUBCODES); - assert(!s390_diag500_table[code]); - - s390_diag500_table[code] = fn; -} - -int s390_virtio_hypercall(CPUS390XState *env) -{ - s390_virtio_fn fn; - - if (env->regs[1] < MAX_DIAG_SUBCODES) { - fn = s390_diag500_table[env->regs[1]]; - if (fn) { - env->regs[2] = fn(&env->regs[2]); - return 0; - } - } - - return -EINVAL; -} diff --git a/hw/s390x/s390-virtio-hcall.h b/hw/s390x/s390-virtio-hcall.h deleted file mode 100644 index 9800c4b351e..00000000000 --- a/hw/s390x/s390-virtio-hcall.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Support for virtio hypercalls on s390x - * - * Copyright IBM Corp. 2012, 2017 - * Author(s): Cornelia Huck - * - * This work is licensed under the terms of the GNU GPL, version 2 or (at - * your option) any later version. See the COPYING file in the top-level - * directory. - */ - -#ifndef HW_S390_VIRTIO_HCALL_H -#define HW_S390_VIRTIO_HCALL_H - -#include "standard-headers/asm-s390/virtio-ccw.h" - -/* The only thing that we need from the old kvm_virtio.h file */ -#define KVM_S390_VIRTIO_NOTIFY 0 - -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 eff74479f45..9718564fa42 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); @@ -156,7 +161,11 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) read_info->rnsize2 = cpu_to_be32(rnsize); } - /* we don't support standby memory, maxram_size is never exposed */ + /* + * We don't support standby memory. maxram_size is used for sizing the + * memory device region, which is not exposed through SCLP but through + * diag500. + */ rnmax = machine->ram_size >> sclp->increment_size; if (rnmax < 0x10000) { read_info->rnmax = cpu_to_be16(rnmax); @@ -165,12 +174,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 +269,9 @@ static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code) * service_interrupt call. */ #define SCLP_PV_DUMMY_ADDR 0x4000 -int sclp_service_call_protected(CPUS390XState *env, uint64_t sccb, - uint32_t code) +int sclp_service_call_protected(S390CPU *cpu, uint64_t sccb, uint32_t code) { + CPUS390XState *env = &cpu->env; SCLPDevice *sclp = get_sclp_device(); SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); SCCBHeader header; @@ -291,8 +296,9 @@ int sclp_service_call_protected(CPUS390XState *env, uint64_t sccb, 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,22 +378,9 @@ 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()); SCLPDevice *sclp = SCLP(dev); - uint64_t hw_limit; - int ret; /* * qdev_device_add searches the sysbus for TYPE_SCLP_EVENTS_BUS. As long @@ -397,14 +390,6 @@ static void sclp_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(SYS_BUS_DEVICE(sclp->event_facility), errp)) { return; } - - ret = s390_set_memory_limit(machine->maxram_size, &hw_limit); - if (ret == -E2BIG) { - error_setg(errp, "host supports a maximum of %" PRIu64 " GB", - hw_limit / GiB); - } else if (ret) { - error_setg(errp, "setting the guest size failed"); - } } static void sclp_memory_init(SCLPDevice *sclp) @@ -439,7 +424,7 @@ static void sclp_init(Object *obj) sclp_memory_init(sclp); } -static void sclp_class_init(ObjectClass *oc, void *data) +static void sclp_class_init(ObjectClass *oc, const void *data) { SCLPDeviceClass *sc = SCLP_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/s390x/sclpcpu.c b/hw/s390x/sclpcpu.c index f2b1a4b0371..4b6ebfed469 100644 --- a/hw/s390x/sclpcpu.c +++ b/hw/s390x/sclpcpu.c @@ -17,7 +17,7 @@ #include "hw/s390x/sclp.h" #include "qemu/module.h" #include "hw/s390x/event-facility.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" typedef struct ConfigMgtData { EventBufferHeader ebh; @@ -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, const 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 ce07b168840..da4c8f3e369 100644 --- a/hw/s390x/sclpquiesce.c +++ b/hw/s390x/sclpquiesce.c @@ -16,7 +16,7 @@ #include "hw/s390x/sclp.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/s390x/event-facility.h" typedef struct SignalQuiesce { @@ -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; @@ -112,12 +112,12 @@ static void quiesce_reset(DeviceState *dev) event->event_pending = false; } -static void quiesce_class_init(ObjectClass *klass, void *data) +static void quiesce_class_init(ObjectClass *klass, const 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 9588b90f2b9..c9b8896b638 100644 --- a/hw/s390x/tod-kvm.c +++ b/hw/s390x/tod-kvm.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/s390x/tod.h" #include "target/s390x/kvm/pv.h" #include "kvm/kvm_s390x.h" @@ -133,7 +133,7 @@ static void kvm_s390_tod_realize(DeviceState *dev, Error **errp) qemu_add_vm_change_state_handler(kvm_s390_tod_vm_state_change, td); } -static void kvm_s390_tod_class_init(ObjectClass *oc, void *data) +static void kvm_s390_tod_class_init(ObjectClass *oc, const void *data) { S390TODClass *tdc = S390_TOD_CLASS(oc); diff --git a/hw/s390x/tod-tcg.c b/hw/s390x/tod-tcg.c index 2d540dba65a..0cc96624e1b 100644 --- a/hw/s390x/tod-tcg.c +++ b/hw/s390x/tod-tcg.c @@ -16,7 +16,7 @@ #include "qemu/module.h" #include "cpu.h" #include "tcg/tcg_s390x.h" -#include "sysemu/rtc.h" +#include "system/rtc.h" static void qemu_s390_tod_get(const S390TODState *td, S390TOD *tod, Error **errp) @@ -52,7 +52,7 @@ static void qemu_s390_tod_set(S390TODState *td, const S390TOD *tod, } } -static void qemu_s390_tod_class_init(ObjectClass *oc, void *data) +static void qemu_s390_tod_class_init(ObjectClass *oc, const void *data) { S390TODClass *tdc = S390_TOD_CLASS(oc); diff --git a/hw/s390x/tod.c b/hw/s390x/tod.c index c81b1c03383..3f913cc88ab 100644 --- a/hw/s390x/tod.c +++ b/hw/s390x/tod.c @@ -13,9 +13,9 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" -#include "sysemu/qtest.h" +#include "system/kvm.h" +#include "system/tcg.h" +#include "system/qtest.h" #include "migration/qemu-file-types.h" #include "migration/register.h" @@ -111,7 +111,7 @@ static void s390_tod_realize(DeviceState *dev, Error **errp) register_savevm_live("todclock", 0, 1, &savevm_tod, td); } -static void s390_tod_class_init(ObjectClass *oc, void *data) +static void s390_tod_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/s390x/trace-events b/hw/s390x/trace-events index 34da5ea3230..4e74bf4484f 100644 --- a/hw/s390x/trace-events +++ b/hw/s390x/trace-events @@ -36,3 +36,7 @@ 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/vhost-scsi-ccw.c b/hw/s390x/vhost-scsi-ccw.c index 40dc14bbc71..8341b23a95c 100644 --- a/hw/s390x/vhost-scsi-ccw.c +++ b/hw/s390x/vhost-scsi-ccw.c @@ -41,13 +41,12 @@ static void vhost_ccw_scsi_instance_init(Object *obj) TYPE_VHOST_SCSI); } -static Property vhost_ccw_scsi_properties[] = { +static const Property vhost_ccw_scsi_properties[] = { DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; -static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data) +static void vhost_ccw_scsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/vhost-user-fs-ccw.c b/hw/s390x/vhost-user-fs-ccw.c index 6c6f2692930..cc1b8227fc0 100644 --- a/hw/s390x/vhost-user-fs-ccw.c +++ b/hw/s390x/vhost-user-fs-ccw.c @@ -23,12 +23,11 @@ typedef struct VHostUserFSCcw { OBJECT_CHECK(VHostUserFSCcw, (obj), TYPE_VHOST_USER_FS_CCW) -static Property vhost_user_fs_ccw_properties[] = { +static const Property vhost_user_fs_ccw_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_user_fs_ccw_realize(VirtioCcwDevice *ccw_dev, Error **errp) @@ -49,7 +48,7 @@ static void vhost_user_fs_ccw_instance_init(Object *obj) TYPE_VHOST_USER_FS); } -static void vhost_user_fs_ccw_class_init(ObjectClass *klass, void *data) +static void vhost_user_fs_ccw_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/vhost-vsock-ccw.c b/hw/s390x/vhost-vsock-ccw.c index 07845a9a006..552e9e86a4b 100644 --- a/hw/s390x/vhost-vsock-ccw.c +++ b/hw/s390x/vhost-vsock-ccw.c @@ -22,10 +22,9 @@ struct VHostVSockCCWState { VHostVSock vdev; }; -static Property vhost_vsock_ccw_properties[] = { +static const Property vhost_vsock_ccw_properties[] = { DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_vsock_ccw_realize(VirtioCcwDevice *ccw_dev, Error **errp) @@ -36,7 +35,7 @@ static void vhost_vsock_ccw_realize(VirtioCcwDevice *ccw_dev, Error **errp) qdev_realize(vdev, BUS(&ccw_dev->bus), errp); } -static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data) +static void vhost_vsock_ccw_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-9p.c b/hw/s390x/virtio-ccw-9p.c index 6f931f5994c..72bf6ec80c8 100644 --- a/hw/s390x/virtio-ccw-9p.c +++ b/hw/s390x/virtio-ccw-9p.c @@ -41,15 +41,14 @@ static void virtio_ccw_9p_instance_init(Object *obj) TYPE_VIRTIO_9P); } -static Property virtio_ccw_9p_properties[] = { +static const Property virtio_ccw_9p_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_9p_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-balloon.c b/hw/s390x/virtio-ccw-balloon.c index 44287b9bbec..399b40f366e 100644 --- a/hw/s390x/virtio-ccw-balloon.c +++ b/hw/s390x/virtio-ccw-balloon.c @@ -46,15 +46,14 @@ static void virtio_ccw_balloon_instance_init(Object *obj) "guest-stats-polling-interval"); } -static Property virtio_ccw_balloon_properties[] = { +static const Property virtio_ccw_balloon_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_balloon_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-blk.c b/hw/s390x/virtio-ccw-blk.c index 8e0e58b77d8..7d8c4a75ced 100644 --- a/hw/s390x/virtio-ccw-blk.c +++ b/hw/s390x/virtio-ccw-blk.c @@ -43,15 +43,15 @@ static void virtio_ccw_blk_instance_init(Object *obj) "bootindex"); } -static Property virtio_ccw_blk_properties[] = { +static const Property virtio_ccw_blk_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_CCW_LOADPARM("loadparm", CcwDevice, loadparm), }; -static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_blk_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-crypto.c b/hw/s390x/virtio-ccw-crypto.c index 0fa2f894439..75e714603b1 100644 --- a/hw/s390x/virtio-ccw-crypto.c +++ b/hw/s390x/virtio-ccw-crypto.c @@ -44,15 +44,14 @@ static void virtio_ccw_crypto_instance_init(Object *obj) TYPE_VIRTIO_CRYPTO); } -static Property virtio_ccw_crypto_properties[] = { +static const Property virtio_ccw_crypto_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_crypto_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-gpu.c b/hw/s390x/virtio-ccw-gpu.c index 0642c5281d9..edb6a47d376 100644 --- a/hw/s390x/virtio-ccw-gpu.c +++ b/hw/s390x/virtio-ccw-gpu.c @@ -42,15 +42,14 @@ static void virtio_ccw_gpu_instance_init(Object *obj) TYPE_VIRTIO_GPU); } -static Property virtio_ccw_gpu_properties[] = { +static const Property virtio_ccw_gpu_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_ccw_gpu_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_gpu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-input.c b/hw/s390x/virtio-ccw-input.c index 61a07ba38d0..2250d8cf981 100644 --- a/hw/s390x/virtio-ccw-input.c +++ b/hw/s390x/virtio-ccw-input.c @@ -43,15 +43,14 @@ static void virtio_ccw_input_realize(VirtioCcwDevice *ccw_dev, Error **errp) qdev_realize(vdev, BUS(&ccw_dev->bus), errp); } -static Property virtio_ccw_input_properties[] = { +static const Property virtio_ccw_input_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_ccw_input_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_input_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-md-stubs.c b/hw/s390x/virtio-ccw-md-stubs.c new file mode 100644 index 00000000000..e9378655503 --- /dev/null +++ b/hw/s390x/virtio-ccw-md-stubs.c @@ -0,0 +1,24 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/s390x/virtio-ccw-md.h" + +void virtio_ccw_md_pre_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp) +{ + error_setg(errp, "virtio based memory devices not supported"); +} + +void virtio_ccw_md_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp) +{ + error_setg(errp, "virtio based memory devices not supported"); +} + +void virtio_ccw_md_unplug_request(VirtIOMDCcw *vmd, MachineState *ms, + Error **errp) +{ + error_setg(errp, "virtio based memory devices not supported"); +} + +void virtio_ccw_md_unplug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp) +{ + error_setg(errp, "virtio based memory devices not supported"); +} diff --git a/hw/s390x/virtio-ccw-md.c b/hw/s390x/virtio-ccw-md.c new file mode 100644 index 00000000000..0370f584505 --- /dev/null +++ b/hw/s390x/virtio-ccw-md.c @@ -0,0 +1,153 @@ +/* + * Virtio CCW support for abstract virtio based memory device + * + * Copyright (C) 2024 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/s390x/virtio-ccw-md.h" +#include "hw/mem/memory-device.h" +#include "qapi/error.h" +#include "qemu/error-report.h" + +void virtio_ccw_md_pre_plug(VirtIOMDCcw *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, but + * 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_ccw_md_plug(VirtIOMDCcw *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_ccw_md_unplug_request(VirtIOMDCcw *vmd, MachineState *ms, + Error **errp) +{ + VirtIOMDCcwClass *vmdc = VIRTIO_MD_CCW_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_ccw_md_unplug(vmd, ms, &local_err); + if (!local_err) { + object_unparent(OBJECT(dev)); + } + } +} + +void virtio_ccw_md_unplug(VirtIOMDCcw *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_ccw_md_info = { + .name = TYPE_VIRTIO_MD_CCW, + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtIOMDCcw), + .class_size = sizeof(VirtIOMDCcwClass), + .abstract = true, + .interfaces = (const InterfaceInfo[]) { + { TYPE_MEMORY_DEVICE }, + { } + }, +}; + +static void virtio_ccw_md_register(void) +{ + type_register_static(&virtio_ccw_md_info); +} +type_init(virtio_ccw_md_register) diff --git a/hw/s390x/virtio-ccw-md.h b/hw/s390x/virtio-ccw-md.h new file mode 100644 index 00000000000..39ba864c927 --- /dev/null +++ b/hw/s390x/virtio-ccw-md.h @@ -0,0 +1,44 @@ +/* + * Virtio CCW support for abstract virtio based memory device + * + * Copyright (C) 2024 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_S390X_VIRTIO_CCW_MD_H +#define HW_S390X_VIRTIO_CCW_MD_H + +#include "virtio-ccw.h" +#include "qom/object.h" + +/* + * virtio-md-ccw: This extends VirtioCcwDevice. + */ +#define TYPE_VIRTIO_MD_CCW "virtio-md-ccw" + +OBJECT_DECLARE_TYPE(VirtIOMDCcw, VirtIOMDCcwClass, VIRTIO_MD_CCW) + +struct VirtIOMDCcwClass { + /* private */ + VirtIOCCWDeviceClass parent; + + /* public */ + void (*unplug_request_check)(VirtIOMDCcw *vmd, Error **errp); +}; + +struct VirtIOMDCcw { + VirtioCcwDevice parent_obj; +}; + +void virtio_ccw_md_pre_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp); +void virtio_ccw_md_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp); +void virtio_ccw_md_unplug_request(VirtIOMDCcw *vmd, MachineState *ms, + Error **errp); +void virtio_ccw_md_unplug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp); + +#endif /* HW_S390X_VIRTIO_CCW_MD_H */ diff --git a/hw/s390x/virtio-ccw-mem.c b/hw/s390x/virtio-ccw-mem.c new file mode 100644 index 00000000000..daa485d189f --- /dev/null +++ b/hw/s390x/virtio-ccw-mem.c @@ -0,0 +1,225 @@ +/* + * virtio-mem CCW implementation + * + * Copyright (C) 2024 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/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "virtio-ccw-mem.h" +#include "hw/mem/memory-device.h" +#include "qapi/qapi-events-machine.h" +#include "qapi/qapi-events-misc.h" + +static void virtio_ccw_mem_realize(VirtioCcwDevice *ccw_dev, Error **errp) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + qdev_realize(vdev, BUS(&ccw_dev->bus), errp); +} + +static void virtio_ccw_mem_set_addr(MemoryDeviceState *md, uint64_t addr, + Error **errp) +{ + object_property_set_uint(OBJECT(md), VIRTIO_MEM_ADDR_PROP, addr, errp); +} + +static uint64_t virtio_ccw_mem_get_addr(const MemoryDeviceState *md) +{ + return object_property_get_uint(OBJECT(md), VIRTIO_MEM_ADDR_PROP, + &error_abort); +} + +static MemoryRegion *virtio_ccw_mem_get_memory_region(MemoryDeviceState *md, + Error **errp) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(md); + VirtIOMEM *vmem = &dev->vdev; + VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem); + + return vmc->get_memory_region(vmem, errp); +} + +static void virtio_ccw_mem_decide_memslots(MemoryDeviceState *md, + unsigned int limit) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(md); + VirtIOMEM *vmem = VIRTIO_MEM(&dev->vdev); + VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem); + + vmc->decide_memslots(vmem, limit); +} + +static unsigned int virtio_ccw_mem_get_memslots(MemoryDeviceState *md) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(md); + VirtIOMEM *vmem = VIRTIO_MEM(&dev->vdev); + VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem); + + return vmc->get_memslots(vmem); +} + +static uint64_t virtio_ccw_mem_get_plugged_size(const MemoryDeviceState *md, + Error **errp) +{ + return object_property_get_uint(OBJECT(md), VIRTIO_MEM_SIZE_PROP, + errp); +} + +static void virtio_ccw_mem_fill_device_info(const MemoryDeviceState *md, + MemoryDeviceInfo *info) +{ + VirtioMEMDeviceInfo *vi = g_new0(VirtioMEMDeviceInfo, 1); + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(md); + VirtIOMEM *vmem = &dev->vdev; + VirtIOMEMClass *vpc = VIRTIO_MEM_GET_CLASS(vmem); + DeviceState *vdev = DEVICE(md); + + if (vdev->id) { + vi->id = g_strdup(vdev->id); + } + + /* let the real device handle everything else */ + vpc->fill_device_info(vmem, vi); + + info->u.virtio_mem.data = vi; + info->type = MEMORY_DEVICE_INFO_KIND_VIRTIO_MEM; +} + +static uint64_t virtio_ccw_mem_get_min_alignment(const MemoryDeviceState *md) +{ + return object_property_get_uint(OBJECT(md), VIRTIO_MEM_BLOCK_SIZE_PROP, + &error_abort); +} + +static void virtio_ccw_mem_size_change_notify(Notifier *notifier, void *data) +{ + VirtIOMEMCcw *dev = container_of(notifier, VirtIOMEMCcw, + size_change_notifier); + DeviceState *vdev = DEVICE(dev); + char *qom_path = object_get_canonical_path(OBJECT(dev)); + const uint64_t * const size_p = data; + + qapi_event_send_memory_device_size_change(vdev->id, *size_p, qom_path); + g_free(qom_path); +} + +static void virtio_ccw_mem_unplug_request_check(VirtIOMDCcw *vmd, Error **errp) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(vmd); + VirtIOMEM *vmem = &dev->vdev; + VirtIOMEMClass *vpc = VIRTIO_MEM_GET_CLASS(vmem); + + vpc->unplug_request_check(vmem, errp); +} + +static void virtio_ccw_mem_get_requested_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(obj); + + object_property_get(OBJECT(&dev->vdev), name, v, errp); +} + +static void virtio_ccw_mem_set_requested_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(obj); + DeviceState *vdev = DEVICE(obj); + + /* + * If we passed virtio_ccw_mem_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 (vdev->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(&dev->vdev), name, v, errp); +} + +static const Property virtio_ccw_mem_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, + VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, + VIRTIO_CCW_MAX_REV), +}; + +static void virtio_ccw_mem_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(klass); + VirtIOMDCcwClass *vmdc = VIRTIO_MD_CCW_CLASS(klass); + + k->realize = virtio_ccw_mem_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, virtio_ccw_mem_properties); + + mdc->get_addr = virtio_ccw_mem_get_addr; + mdc->set_addr = virtio_ccw_mem_set_addr; + mdc->get_plugged_size = virtio_ccw_mem_get_plugged_size; + mdc->get_memory_region = virtio_ccw_mem_get_memory_region; + mdc->decide_memslots = virtio_ccw_mem_decide_memslots; + mdc->get_memslots = virtio_ccw_mem_get_memslots; + mdc->fill_device_info = virtio_ccw_mem_fill_device_info; + mdc->get_min_alignment = virtio_ccw_mem_get_min_alignment; + + vmdc->unplug_request_check = virtio_ccw_mem_unplug_request_check; +} + +static void virtio_ccw_mem_instance_init(Object *obj) +{ + VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(obj); + VirtIOMEMClass *vmc; + VirtIOMEM *vmem; + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_MEM); + + dev->size_change_notifier.notify = virtio_ccw_mem_size_change_notify; + vmem = &dev->vdev; + vmc = VIRTIO_MEM_GET_CLASS(vmem); + /* + * We never remove the notifier again, as we expect both devices to + * disappear at the same time. + */ + vmc->add_size_change_notifier(vmem, &dev->size_change_notifier); + + object_property_add_alias(obj, VIRTIO_MEM_BLOCK_SIZE_PROP, + 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(obj, VIRTIO_MEM_REQUESTED_SIZE_PROP, "size", + virtio_ccw_mem_get_requested_size, + virtio_ccw_mem_set_requested_size, NULL, NULL); +} + +static const TypeInfo virtio_ccw_mem = { + .name = TYPE_VIRTIO_MEM_CCW, + .parent = TYPE_VIRTIO_MD_CCW, + .instance_size = sizeof(VirtIOMEMCcw), + .instance_init = virtio_ccw_mem_instance_init, + .class_init = virtio_ccw_mem_class_init, +}; + +static void virtio_ccw_mem_register_types(void) +{ + type_register_static(&virtio_ccw_mem); +} +type_init(virtio_ccw_mem_register_types) diff --git a/hw/s390x/virtio-ccw-mem.h b/hw/s390x/virtio-ccw-mem.h new file mode 100644 index 00000000000..738ab2c744b --- /dev/null +++ b/hw/s390x/virtio-ccw-mem.h @@ -0,0 +1,34 @@ +/* + * Virtio MEM CCW device + * + * Copyright (C) 2024 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_S390X_VIRTIO_CCW_MEM_H +#define HW_S390X_VIRTIO_CCW_MEM_H + +#include "virtio-ccw-md.h" +#include "hw/virtio/virtio-mem.h" +#include "qom/object.h" + +typedef struct VirtIOMEMCcw VirtIOMEMCcw; + +/* + * virtio-mem-ccw: This extends VirtIOMDCcw + */ +#define TYPE_VIRTIO_MEM_CCW "virtio-mem-ccw" +DECLARE_INSTANCE_CHECKER(VirtIOMEMCcw, VIRTIO_MEM_CCW, TYPE_VIRTIO_MEM_CCW) + +struct VirtIOMEMCcw { + VirtIOMDCcw parent_obj; + VirtIOMEM vdev; + Notifier size_change_notifier; +}; + +#endif /* HW_S390X_VIRTIO_CCW_MEM_H */ diff --git a/hw/s390x/virtio-ccw-net.c b/hw/s390x/virtio-ccw-net.c index 484e6176599..a7d4afbeb9e 100644 --- a/hw/s390x/virtio-ccw-net.c +++ b/hw/s390x/virtio-ccw-net.c @@ -46,15 +46,15 @@ static void virtio_ccw_net_instance_init(Object *obj) "bootindex"); } -static Property virtio_ccw_net_properties[] = { +static const Property virtio_ccw_net_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_CCW_LOADPARM("loadparm", CcwDevice, loadparm), }; -static void virtio_ccw_net_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_net_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-rng.c b/hw/s390x/virtio-ccw-rng.c index a3fffb51384..3263287d45a 100644 --- a/hw/s390x/virtio-ccw-rng.c +++ b/hw/s390x/virtio-ccw-rng.c @@ -43,15 +43,14 @@ static void virtio_ccw_rng_instance_init(Object *obj) TYPE_VIRTIO_RNG); } -static Property virtio_ccw_rng_properties[] = { +static const Property virtio_ccw_rng_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_rng_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-scsi.c b/hw/s390x/virtio-ccw-scsi.c index d003f89f437..06b4c6c4a5a 100644 --- a/hw/s390x/virtio-ccw-scsi.c +++ b/hw/s390x/virtio-ccw-scsi.c @@ -53,15 +53,14 @@ static void virtio_ccw_scsi_instance_init(Object *obj) TYPE_VIRTIO_SCSI); } -static Property virtio_ccw_scsi_properties[] = { +static const Property virtio_ccw_scsi_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_scsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-serial.c b/hw/s390x/virtio-ccw-serial.c index 8f8d2302f81..0dac590c080 100644 --- a/hw/s390x/virtio-ccw-serial.c +++ b/hw/s390x/virtio-ccw-serial.c @@ -53,15 +53,14 @@ static void virtio_ccw_serial_instance_init(Object *obj) TYPE_VIRTIO_SERIAL); } -static Property virtio_ccw_serial_properties[] = { +static const Property virtio_ccw_serial_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_serial_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 17c548b84ff..d2f85b39f30 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -12,8 +12,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "exec/address-spaces.h" -#include "sysemu/kvm.h" +#include "system/address-spaces.h" +#include "system/kvm.h" #include "net/net.h" #include "hw/virtio/virtio.h" #include "migration/qemu-file-types.h" @@ -32,7 +32,7 @@ #include "trace.h" #include "hw/s390x/css-bridge.h" #include "hw/s390x/s390-virtio-ccw.h" -#include "sysemu/replay.h" +#include "system/replay.h" #define NR_CLASSIC_INDICATOR_BITS 64 @@ -87,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() } @@ -98,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), @@ -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); } } @@ -1160,7 +1157,6 @@ static void virtio_ccw_device_plugged(DeviceState *d, Error **errp) CcwDevice *ccw_dev = CCW_DEVICE(d); SubchDev *sch = ccw_dev->sch; int n = virtio_get_num_queues(vdev); - S390FLICState *flic = s390_get_flic(); if (!virtio_has_feature(vdev->host_features, VIRTIO_F_VERSION_1)) { dev->max_rev = 0; @@ -1187,10 +1183,10 @@ static void virtio_ccw_device_plugged(DeviceState *d, Error **errp) VIRTIO_QUEUE_MAX); return; } - if (virtio_get_num_queues(vdev) > flic->adapter_routes_max_batch) { + if (virtio_get_num_queues(vdev) > ADAPTER_ROUTES_MAX_GSI) { error_setg(errp, "The number of virtqueues %d " "exceeds flic adapter route limit %d", n, - flic->adapter_routes_max_batch); + ADAPTER_ROUTES_MAX_GSI); return; } @@ -1232,16 +1228,18 @@ static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, virtio_ccw_stop_ioeventfd(_dev); } -static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_device_class_init(ObjectClass *klass, const 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 = { @@ -1264,7 +1262,7 @@ static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, qbus_init(bus, bus_size, TYPE_VIRTIO_CCW_BUS, qdev, virtio_bus_name); } -static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_bus_class_init(ObjectClass *klass, const void *data) { VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); BusClass *bus_class = BUS_CLASS(klass); diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index fac186c8f64..c7a830a1944 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/esp-pci.c b/hw/scsi/esp-pci.c index 4e890db0e21..12c86eb7aaa 100644 --- a/hw/scsi/esp-pci.c +++ b/hw/scsi/esp-pci.c @@ -77,6 +77,41 @@ 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 = &pci->esp; @@ -89,6 +124,7 @@ 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) @@ -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 = &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); } } @@ -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) @@ -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, }; @@ -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); } @@ -406,7 +427,7 @@ static void esp_pci_init(Object *obj) object_initialize_child(obj, "esp", &pci->esp, TYPE_ESP); } -static void esp_pci_class_init(ObjectClass *klass, void *data) +static void esp_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -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; } @@ -429,7 +450,7 @@ static const TypeInfo esp_pci_info = { .instance_init = esp_pci_init, .instance_size = sizeof(PCIESPState), .class_init = esp_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -536,7 +557,7 @@ static void dc390_scsi_realize(PCIDevice *dev, Error **errp) contents[EE_CHKSUM2] = chksum >> 8; } -static void dc390_class_init(ObjectClass *klass, void *data) +static void dc390_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index e52188d0228..f24991fd16d 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; - buf = fifo8_pop_buf(fifo, maxlen, &n); - if (dest) { - memcpy(dest, buf, n); + if (fifo8_is_empty(&s->fifo)) { + val = 0; + } else { + val = fifo8_pop(&s->fifo); } - return n; + esp_update_drq(s); + return val; +} + +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) @@ -166,38 +242,19 @@ static uint32_t esp_get_stc(ESPState *s) 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); - } - - return val; + return esp_fifo_pop(s); } 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 (s->do_cmd) { - esp_fifo_push(&s->cmdfifo, val); - } else { - esp_fifo_push(&s->fifo, val); + if (dmalen && s->drq_state) { + dmalen--; + esp_set_tc(s, dmalen); } - - 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 +264,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 +302,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 +337,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 +348,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,471 +360,579 @@ 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; + + if (esp_select(s) < 0) { + return; } -} -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); + 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; + + if (esp_select(s) < 0) { + return; } -} -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); + 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); + int len = fifo8_num_used(&s->cmdfifo) - s->cmdfifo_cdb_offset; + const uint8_t *pbuf; + uint32_t n; + int cdblen; + + if (len <= 0) { + return false; + } + + 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; + } + + cdblen = scsi_cdb_length((uint8_t *)&pbuf[s->cmdfifo_cdb_offset]); + + return cdblen < 0 ? false : (len >= cdblen); } -static void do_dma_pdma_cb(ESPState *s) +static void esp_dma_ti_check(ESPState *s) { - int to_device = ((s->rregs[ESP_RSTAT] & 7) == STAT_DO); - int len; - uint32_t n; + if (esp_get_tc(s) == 0 && fifo8_num_used(&s->fifo) < 2) { + s->rregs[ESP_RINTR] |= INTR_BS; + esp_raise_irq(s); + } +} - 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; - } +static void esp_do_dma(ESPState *s) +{ + uint32_t len, cmdlen; + uint8_t buf[ESP_CMDFIFO_SZ]; - 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; - } + len = esp_get_tc(s); - /* Command has been received */ - s->do_cmd = 0; - do_cmd(s); + 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 { - /* - * 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); + len = esp_fifo_pop_buf(s, buf, fifo8_num_used(&s->fifo)); + len = MIN(fifo8_num_free(&s->cmdfifo), len); } - return; - } - - if (!s->current_req) { - return; - } - 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; - - 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; - } + fifo8_push_all(&s->cmdfifo, buf, len); + s->cmdfifo_cdb_offset += len; - if (s->async_len == 0) { - scsi_req_continue(s->current_req); - return; - } + 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 (esp_get_tc(s) == 0) { - esp_lower_drq(s); - esp_dma_done(s); - } + if (fifo8_num_used(&s->cmdfifo) > 1) { + /* Process any additional command phase data */ + esp_do_dma(s); + } + } + break; - 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; - } + 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; - 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); + /* 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) { - /* Indicate transfer to FIFO is complete */ - s->rregs[ESP_RSTAT] |= STAT_TC; + esp_set_phase(s, STAT_CD); + s->rregs[ESP_CMD] = 0; + s->rregs[ESP_RINTR] |= INTR_BS; + esp_raise_irq(s); } - return; + break; } + break; - /* Partially filled a scsi buffer. Complete immediately. */ - esp_lower_drq(s); - esp_dma_done(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()) - */ + 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); - esp_set_pdma_cb(s, DO_DMA_PDMA_CB); - esp_raise_drq(s); + break; + } - /* Indicate transfer to FIFO is complete */ - s->rregs[ESP_RSTAT] |= STAT_TC; + 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; + + 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; + } + break; + + 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_nodma_ti_dataout(ESPState *s) +{ + int len; + + if (!s->current_req) { + return; } - esp_set_tc(s, esp_get_tc(s) - len); + if (s->async_len == 0) { + /* Defer until data is available. */ + return; + } + 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; - if (to_device) { - s->ti_size += len; - } else { - s->ti_size -= len; - } + 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; - } + return; } - /* Partially filled a scsi buffer. Complete immediately. */ - esp_dma_done(s); - esp_lower_drq(s); + s->rregs[ESP_RINTR] |= INTR_BS; + esp_raise_irq(s); } static void esp_do_nodma(ESPState *s) { - int to_device = ((s->rregs[ESP_RSTAT] & 7) == STAT_DO); + uint8_t buf[ESP_FIFO_SZ]; 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; + 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; - /* 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 - */ + 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); - s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD; - s->rregs[ESP_RSEQ] = SEQ_CD; + esp_set_phase(s, STAT_CD); + s->rregs[ESP_CMD] = 0; s->rregs[ESP_RINTR] |= INTR_BS; esp_raise_irq(s); + break; } - return; - } + break; - if (!s->current_req) { - return; - } + 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); - if (s->async_len == 0) { - /* Defer until data is available. */ - return; - } + cmdlen = fifo8_num_used(&s->cmdfifo); + trace_esp_handle_ti_cmd(cmdlen); - 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 { + /* 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 STAT_DO: + /* Accumulate data in FIFO until non-DMA TI is executed */ + break; + + 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)) { - fifo8_push(&s->fifo, s->async_buf[0]); + 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 (s->async_len == 0) { + scsi_req_continue(s->current_req); + return; + } - s->rregs[ESP_RINTR] |= INTR_BS; - esp_raise_irq(s); -} + /* If preloading the FIFO, defer until TI command issued */ + if (s->rregs[ESP_CMD] != CMD_TI) { + return; + } -static void esp_pdma_cb(ESPState *s) -{ - switch (s->pdma_cb) { - case SATN_PDMA_CB: - satn_pdma_cb(s); - break; - case S_WITHOUT_SATN_PDMA_CB: - s_without_satn_pdma_cb(s); - break; - case SATN_STOP_PDMA_CB: - satn_stop_pdma_cb(s); + s->rregs[ESP_RINTR] |= INTR_BS; + esp_raise_irq(s); break; - case WRITE_RESPONSE_PDMA_CB: - write_response_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 DO_DMA_PDMA_CB: - do_dma_pdma_cb(s); + + 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 +944,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 +953,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 +992,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; - esp_raise_irq(s); - } + if (!s->data_ready) { + s->data_ready = true; - 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; - } + 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; - if (s->ti_cmd == (CMD_TI | CMD_DMA)) { - if (dmalen) { - esp_do_dma(s); - } else if (s->ti_size <= 0) { + case CMD_TI | CMD_DMA: + case CMD_TI: /* - * If this was the last part of a DMA transfer then the - * completion interrupt is deferred to here. + * Bus service interrupt raised because of initial change to + * DATA phase */ - esp_dma_done(s); - esp_lower_drq(s); + s->rregs[ESP_CMD] = 0; + s->rregs[ESP_RINTR] |= INTR_BS; + break; } - } else if (s->ti_cmd == CMD_TI) { + + esp_raise_irq(s); + } + + /* + * 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->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 +1065,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 +1089,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 +1097,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 +1113,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 +1216,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 +1227,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 +1261,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 +1317,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 +1360,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 +1386,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 +1444,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 +1465,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 +1509,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 +1525,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,31 +1561,23 @@ 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() } }; -static void sysbus_esp_class_init(ObjectClass *klass, void *data) +static void sysbus_esp_class_init(ObjectClass *klass, const 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); @@ -1488,7 +1594,7 @@ static void esp_init(Object *obj) fifo8_create(&s->cmdfifo, ESP_CMDFIFO_SZ); } -static void esp_class_init(ObjectClass *klass, void *data) +static void esp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1497,19 +1603,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 f7d45b0b20f..9ea4aa0a853 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -19,7 +19,7 @@ #include "hw/pci/pci_device.h" #include "hw/scsi/scsi.h" #include "migration/vmstate.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/log.h" #include "qemu/module.h" #include "trace.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) { @@ -1096,7 +1112,7 @@ static void lsi_do_msgout(LSIState *s) static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count) { int n; - uint8_t buf[LSI_BUF_SIZE]; + QEMU_UNINITIALIZED uint8_t buf[LSI_BUF_SIZE]; trace_lsi_memcpy(dest, src, count); while (count) { @@ -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 @@ static void lsi_execute_script(LSIState *s) /* * 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 @@ static void lsi_execute_script(LSIState *s) * 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 @@ static void lsi_execute_script(LSIState *s) } 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,7 @@ 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 @@ -2336,9 +2372,10 @@ static void lsi_scsi_exit(PCIDevice *dev) LSIState *s = LSI53C895A(dev); address_space_destroy(&s->pci_io_as); + timer_free(s->scripts_timer); } -static void lsi_class_init(ObjectClass *klass, void *data) +static void lsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -2349,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); } @@ -2359,13 +2396,13 @@ static const TypeInfo lsi_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(LSIState), .class_init = lsi_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -static void lsi53c810_class_init(ObjectClass *klass, void *data) +static void lsi53c810_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 32c70c9e997..844643d916f 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -21,9 +21,9 @@ #include "qemu/osdep.h" #include "hw/pci/pci.h" #include "hw/qdev-properties.h" -#include "sysemu/dma.h" -#include "sysemu/block-backend.h" -#include "sysemu/rtc.h" +#include "system/dma.h" +#include "system/block-backend.h" +#include "system/rtc.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "qemu/iov.h" @@ -981,13 +981,11 @@ static int megasas_event_wait(MegasasState *s, MegasasCmd *cmd) static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd) { - struct mfi_pd_list info; - size_t dcmd_size = sizeof(info); + struct mfi_pd_list info = {}; BusChild *kid; uint32_t offset, dcmd_limit, num_pd_disks = 0, max_pd_disks; dma_addr_t residual; - memset(&info, 0, dcmd_size); offset = 8; dcmd_limit = offset + sizeof(struct mfi_pd_address); if (cmd->iov_size < dcmd_limit) { @@ -1429,11 +1427,10 @@ static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd) static int megasas_dcmd_get_properties(MegasasState *s, MegasasCmd *cmd) { - struct mfi_ctrl_props info; + struct mfi_ctrl_props info = {}; size_t dcmd_size = sizeof(info); dma_addr_t residual; - memset(&info, 0x0, dcmd_size); if (cmd->iov_size < dcmd_size) { trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, dcmd_size); @@ -1781,7 +1778,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); @@ -1790,7 +1787,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); @@ -1805,15 +1801,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)); @@ -1824,7 +1811,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); @@ -2236,7 +2223,6 @@ static uint64_t megasas_queue_read(void *opaque, hwaddr addr, static void megasas_queue_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - return; } static const MemoryRegionOps megasas_queue_ops = { @@ -2299,7 +2285,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), @@ -2317,7 +2303,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), @@ -2458,7 +2444,7 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp) scsi_bus_init(&s->bus, sizeof(s->bus), DEVICE(dev), &megasas_scsi_info); } -static Property megasas_properties_gen1[] = { +static const Property megasas_properties_gen1[] = { DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge, MEGASAS_DEFAULT_SGE), DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds, @@ -2469,10 +2455,9 @@ static Property megasas_properties_gen1[] = { DEFINE_PROP_ON_OFF_AUTO("msix", MegasasState, msix, ON_OFF_AUTO_AUTO), DEFINE_PROP_BIT("use_jbod", MegasasState, flags, MEGASAS_FLAG_USE_JBOD, false), - DEFINE_PROP_END_OF_LIST(), }; -static Property megasas_properties_gen2[] = { +static const Property megasas_properties_gen2[] = { DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge, MEGASAS_DEFAULT_SGE), DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds, @@ -2483,7 +2468,6 @@ static Property megasas_properties_gen2[] = { DEFINE_PROP_ON_OFF_AUTO("msix", MegasasState, msix, ON_OFF_AUTO_AUTO), DEFINE_PROP_BIT("use_jbod", MegasasState, flags, MEGASAS_FLAG_USE_JBOD, false), - DEFINE_PROP_END_OF_LIST(), }; typedef struct MegasasInfo { @@ -2497,8 +2481,9 @@ typedef struct MegasasInfo { int mmio_bar; int osts; const VMStateDescription *vmsd; - Property *props; - InterfaceInfo *interfaces; + const Property *props; + size_t props_count; + const InterfaceInfo *interfaces; } MegasasInfo; static struct MegasasInfo megasas_devices[] = { @@ -2514,7 +2499,8 @@ static struct MegasasInfo megasas_devices[] = { .osts = MFI_1078_RM | 1, .vmsd = &vmstate_megasas_gen1, .props = megasas_properties_gen1, - .interfaces = (InterfaceInfo[]) { + .props_count = ARRAY_SIZE(megasas_properties_gen1), + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -2530,14 +2516,15 @@ static struct MegasasInfo megasas_devices[] = { .osts = MFI_GEN2_RM, .vmsd = &vmstate_megasas_gen2, .props = megasas_properties_gen2, - .interfaces = (InterfaceInfo[]) { + .props_count = ARRAY_SIZE(megasas_properties_gen2), + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, } }; -static void megasas_class_init(ObjectClass *oc, void *data) +static void megasas_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); @@ -2556,8 +2543,8 @@ static void megasas_class_init(ObjectClass *oc, void *data) e->osts = info->osts; e->product_name = info->product_name; e->product_version = info->product_version; - device_class_set_props(dc, info->props); - dc->reset = megasas_scsi_reset; + device_class_set_props_n(dc, info->props, info->props_count); + device_class_set_legacy_reset(dc, megasas_scsi_reset); dc->vmsd = info->vmsd; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); dc->desc = info->desc; @@ -2582,11 +2569,11 @@ static void megasas_register_types(void) type_info.name = info->name; type_info.parent = TYPE_MEGASAS_BASE; - type_info.class_data = (void *)info; + type_info.class_data = info; type_info.class_init = megasas_class_init; type_info.interfaces = info->interfaces; - type_register(&type_info); + type_register_static(&type_info); } } diff --git a/hw/scsi/mfi.h b/hw/scsi/mfi.h index 0b4ee53dfc0..cf7a2d775b0 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/mptendian.c b/hw/scsi/mptendian.c index 0d5abb4b6cc..6cba92f3761 100644 --- a/hw/scsi/mptendian.c +++ b/hw/scsi/mptendian.c @@ -22,7 +22,7 @@ #include "qemu/osdep.h" #include "hw/pci/pci.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/pci/msi.h" #include "qemu/iov.h" #include "hw/scsi/scsi.h" diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index 3de288b454c..1ebe0b82a79 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "hw/pci/pci.h" #include "hw/qdev-properties.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/pci/msi.h" #include "qemu/iov.h" #include "qemu/main-loop.h" @@ -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; @@ -1366,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), @@ -1410,14 +1410,13 @@ static const VMStateDescription vmstate_mptsas = { } }; -static Property mptsas_properties[] = { +static const Property mptsas_properties[] = { DEFINE_PROP_UINT64("sas_address", MPTSASState, sas_addr, 0), /* TODO: test MSI support under Windows */ DEFINE_PROP_ON_OFF_AUTO("msi", MPTSASState, msi, ON_OFF_AUTO_AUTO), - DEFINE_PROP_END_OF_LIST(), }; -static void mptsas1068_class_init(ObjectClass *oc, void *data) +static void mptsas1068_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); @@ -1431,7 +1430,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); @@ -1442,7 +1441,7 @@ static const TypeInfo mptsas_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(MPTSASState), .class_init = mptsas1068_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -1450,7 +1449,7 @@ static const TypeInfo mptsas_info = { static void mptsas_register_types(void) { - type_register(&mptsas_info); + type_register_static(&mptsas_info); } type_init(mptsas_register_types) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index fc4b77fdb02..9b12ee7f1c6 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -9,12 +9,12 @@ #include "migration/qemu-file-types.h" #include "migration/vmstate.h" #include "scsi/constants.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/block-backend.h" +#include "system/blockdev.h" +#include "system/system.h" +#include "system/runstate.h" #include "trace.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/cutils.h" static char *scsibus_get_dev_path(DeviceState *dev); @@ -85,6 +85,135 @@ 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()); + + /* + * Locking is not necessary because the guest is stopped and no other + * threads can be accessing the requests list, but take the lock for + * consistency. + */ + WITH_QEMU_LOCK_GUARD(&s->requests_lock) { + 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; + g_autoptr(GList) reqs = NULL; + + /* + * Build a list of requests in this AioContext so fn() can be invoked later + * outside requests_lock. + */ + WITH_QEMU_LOCK_GUARD(&s->requests_lock) { + AioContext *ctx = qemu_get_current_aio_context(); + SCSIRequest *req; + SCSIRequest *next; + + QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) { + if (req->ctx == ctx) { + scsi_req_ref(req); /* dropped after calling fn() */ + reqs = g_list_prepend(reqs, req); + } + } + } + + /* Call fn() on each request */ + for (GList *elem = g_list_first(reqs); elem; elem = g_list_next(elem)) { + data->fn(elem->data, data->fn_opaque); + scsi_req_unref(elem->data); + } + + /* 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); +} + +static void scsi_device_for_each_req_async_do_ctx(gpointer key, gpointer value, + gpointer user_data) +{ + AioContext *ctx = key; + SCSIDeviceForEachReqAsyncData *params = user_data; + SCSIDeviceForEachReqAsyncData *data; + + data = g_new(SCSIDeviceForEachReqAsyncData, 1); + data->s = params->s; + data->fn = params->fn; + data->fn_opaque = params->fn_opaque; + + /* + * Hold a reference to the SCSIDevice until + * scsi_device_for_each_req_async_bh() finishes. + */ + object_ref(OBJECT(data->s)); + + /* Paired with scsi_device_for_each_req_async_bh() */ + blk_inc_in_flight(data->s->conf.blk); + + aio_bh_schedule_oneshot(ctx, scsi_device_for_each_req_async_bh, data); +} + +/* + * Schedule @fn() to be invoked for each enqueued request in device @s. @fn() + * must be thread-safe because it runs concurrently in each AioContext that is + * executing a 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()); + + /* The set of AioContexts where the requests are being processed */ + g_autoptr(GHashTable) aio_contexts = g_hash_table_new(NULL, NULL); + WITH_QEMU_LOCK_GUARD(&s->requests_lock) { + SCSIRequest *req; + QTAILQ_FOREACH(req, &s->requests, next) { + g_hash_table_add(aio_contexts, req->ctx); + } + } + + /* Schedule a BH for each AioContext */ + SCSIDeviceForEachReqAsyncData params = { + .s = s, + .fn = fn, + .fn_opaque = opaque, + }; + g_hash_table_foreach( + aio_contexts, + scsi_device_for_each_req_async_do_ctx, + ¶ms + ); +} + static void scsi_device_realize(SCSIDevice *s, Error **errp) { SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s); @@ -144,20 +273,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; - - qemu_bh_delete(s->bh); - s->bh = NULL; + req->retry = true; +} - 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,37 +293,22 @@ 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_guarded(ctx, scsi_dma_restart_bh, s, - &DEVICE(s)->mem_reentrancy_guard); - 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, @@ -280,6 +392,7 @@ static void scsi_qdev_realize(DeviceState *qdev, Error **errp) dev->lun = lun; } + qemu_mutex_init(&dev->requests_lock); QTAILQ_INIT(&dev->requests); scsi_device_realize(dev, &local_err); if (local_err) { @@ -287,7 +400,7 @@ static void scsi_qdev_realize(DeviceState *qdev, Error **errp) return; } dev->vmsentry = qdev_add_vm_change_state_handler(DEVICE(dev), - scsi_dma_restart_cb, dev); + scsi_dma_restart_cb, NULL, dev); } static void scsi_qdev_unrealize(DeviceState *qdev) @@ -300,6 +413,8 @@ static void scsi_qdev_unrealize(DeviceState *qdev) scsi_device_purge_requests(dev, SENSE_CODE(NO_SENSE)); + qemu_mutex_destroy(&dev->requests_lock); + scsi_device_unrealize(dev); blockdev_mark_auto_del(dev->conf.blk); @@ -307,16 +422,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"; @@ -333,11 +447,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); } @@ -348,19 +469,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) @@ -368,6 +482,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++) { @@ -377,10 +497,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); } @@ -797,6 +914,7 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, } } + req->ctx = qemu_get_current_aio_context(); req->cmd = cmd; req->residual = req->cmd.xfer; @@ -893,7 +1011,10 @@ static void scsi_req_enqueue_internal(SCSIRequest *req) req->sg = NULL; } req->enqueued = true; - QTAILQ_INSERT_TAIL(&req->dev->requests, req, next); + + WITH_QEMU_LOCK_GUARD(&req->dev->requests_lock) { + QTAILQ_INSERT_TAIL(&req->dev->requests, req, next); + } } int32_t scsi_req_enqueue(SCSIRequest *req) @@ -913,7 +1034,9 @@ static void scsi_req_dequeue(SCSIRequest *req) trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag); req->retry = false; if (req->enqueued) { - QTAILQ_REMOVE(&req->dev->requests, req, next); + WITH_QEMU_LOCK_GUARD(&req->dev->requests_lock) { + QTAILQ_REMOVE(&req->dev->requests, req, next); + } req->enqueued = false; scsi_req_unref(req); } @@ -1657,17 +1780,25 @@ 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); } @@ -1737,31 +1868,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; } @@ -1826,7 +1959,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), @@ -1838,7 +1971,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), @@ -1856,20 +1989,19 @@ const VMStateDescription vmstate_scsi_device = { }, VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_scsi_sense_state, NULL } }; -static Property scsi_props[] = { +static const Property scsi_props[] = { DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0), DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1), DEFINE_PROP_UINT32("lun", SCSIDevice, lun, -1), - DEFINE_PROP_END_OF_LIST(), }; -static void scsi_device_class_init(ObjectClass *klass, void *data) +static void scsi_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_STORAGE, k->categories); @@ -1881,8 +2013,7 @@ static void scsi_device_class_init(ObjectClass *klass, void *data) static void scsi_dev_instance_init(Object *obj) { - DeviceState *dev = DEVICE(obj); - SCSIDevice *s = SCSI_DEVICE(dev); + SCSIDevice *s = SCSI_DEVICE(obj); device_add_bootindex_property(obj, &s->conf.bootindex, "bootindex", NULL, @@ -1899,7 +2030,7 @@ static const TypeInfo scsi_device_type_info = { .instance_init = scsi_dev_instance_init, }; -static void scsi_bus_class_init(ObjectClass *klass, void *data) +static void scsi_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); @@ -1915,7 +2046,7 @@ static const TypeInfo scsi_bus_info = { .parent = TYPE_BUS, .instance_size = sizeof(SCSIBus), .class_init = scsi_bus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index e0d79c7966c..b4782c6248d 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -32,13 +32,14 @@ #include "migration/vmstate.h" #include "hw/scsi/emulation.h" #include "scsi/constants.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" +#include "system/arch_init.h" +#include "system/block-backend.h" +#include "system/blockdev.h" #include "hw/block/block.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" -#include "sysemu/dma.h" -#include "sysemu/sysemu.h" +#include "system/dma.h" +#include "system/system.h" #include "qemu/cutils.h" #include "trace.h" #include "qom/object.h" @@ -58,13 +59,22 @@ #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. For host_status + * errors, the function passes ret == -ENODEV and sets the host_status field + * of the SCSIRequest. + */ DMAIOFunc *dma_readv; DMAIOFunc *dma_writev; - bool (*need_fua_emulation)(SCSICommand *cmd); + bool (*need_fua)(SCSICommand *cmd); void (*update_sense)(SCSIRequest *r); }; @@ -75,7 +85,7 @@ typedef struct SCSIDiskReq { uint32_t sector_count; uint32_t buflen; bool started; - bool need_fua_emulation; + bool need_fua; struct iovec iov; QEMUIOVector qiov; BlockAcctCookie acct; @@ -95,12 +105,12 @@ struct SCSIDiskState { uint64_t max_unmap_size; uint64_t max_io_size; uint32_t quirks; - QEMUBH *bh; char *version; char *serial; char *vendor; char *product; char *device_id; + char *loadparm; /* only for s390x */ bool tray_open; bool tray_locked; /* @@ -111,6 +121,7 @@ struct SCSIDiskState { * 0xffff - reserved */ uint16_t rotation_rate; + bool migrate_emulated_scsi_request; }; static void scsi_free_request(SCSIRequest *req) @@ -159,6 +170,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 +202,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,22 +224,61 @@ 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; + int16_t host_status; + int error; bool req_has_sense = false; BlockErrorAction action; int status; + /* + * host_status should only be set for SG_IO requests that came back with a + * host_status error in scsi_block_sgio_complete(). This error path passes + * -ENODEV as the return value. + * + * Reset host_status in the request because we may still want to complete + * the request successfully with the 'stop' or 'ignore' error policy. + */ + host_status = r->req.host_status; + if (host_status != -1) { + assert(ret == -ENODEV); + r->req.host_status = -1; + } + if (ret < 0) { status = scsi_sense_from_errno(-ret, &sense); error = -ret; } 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 +288,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 { @@ -234,6 +303,10 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) if (acct_failed) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); } + if (host_status != -1) { + scsi_req_complete_failed(&r->req, host_status); + return true; + } if (req_has_sense) { sdc->update_sense(&r->req); } else if (status == CHECK_CONDITION) { @@ -261,7 +334,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,7 +346,8 @@ static void scsi_aio_complete(void *opaque, int ret) SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); + /* The request must run in its AioContext */ + assert(r->req.ctx == qemu_get_current_aio_context()); assert(r->req.aiocb != NULL); r->req.aiocb = NULL; @@ -286,7 +360,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); } @@ -316,45 +389,21 @@ static bool scsi_is_cmd_fua(SCSICommand *cmd) } } -static void scsi_write_do_fua(SCSIDiskReq *r) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - - assert(r->req.aiocb == NULL); - assert(!r->req.io_canceled); - - if (r->need_fua_emulation) { - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_aio_complete, r); - return; - } - - scsi_req_complete(&r->req, GOOD); - scsi_req_unref(&r->req); -} - 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; } r->sector += r->sector_count; r->sector_count = 0; - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - scsi_write_do_fua(r); - return; - } else { - scsi_req_complete(&r->req, GOOD); - } + scsi_req_complete(&r->req, GOOD); done: scsi_req_unref(&r->req); } -/* Called with AioContext lock held */ static void scsi_dma_complete(void *opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; @@ -363,9 +412,10 @@ static void scsi_dma_complete(void *opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; + /* 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); @@ -375,8 +425,11 @@ static void scsi_read_complete_noio(SCSIDiskReq *r, int ret) { uint32_t n; + /* The request must run in its AioContext */ + assert(r->req.ctx == 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; } @@ -394,19 +447,17 @@ static void scsi_read_complete(void *opaque, int ret) SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); - assert(r->req.aiocb != NULL); r->req.aiocb = NULL; + /* 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. */ @@ -426,8 +477,7 @@ static void scsi_do_read(SCSIDiskReq *r, int ret) if (r->req.sg) { dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_READ); r->req.residual -= r->req.sg->size; - r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk), - r->req.sg, r->sector << BDRV_SECTOR_BITS, + r->req.aiocb = dma_blk_io(r->req.sg, r->sector << BDRV_SECTOR_BITS, BDRV_SECTOR_SIZE, sdc->dma_readv, r, scsi_dma_complete, r, DMA_DIRECTION_FROM_DEVICE); @@ -448,8 +498,6 @@ static void scsi_do_read_cb(void *opaque, int ret) SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); - assert (r->req.aiocb != NULL); r->req.aiocb = NULL; @@ -459,7 +507,6 @@ static void scsi_do_read_cb(void *opaque, int ret) 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. */ @@ -494,7 +541,7 @@ static void scsi_read_data(SCSIRequest *req) first = !r->started; r->started = true; - if (first && r->need_fua_emulation) { + if (first && r->need_fua) { block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0, BLOCK_ACCT_FLUSH); r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read_cb, r); @@ -507,8 +554,11 @@ static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) { uint32_t n; + /* The request must run in its AioContext */ + assert(r->req.ctx == 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; } @@ -516,8 +566,7 @@ static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) r->sector += n; r->sector_count -= n; if (r->sector_count == 0) { - scsi_write_do_fua(r); - return; + scsi_req_complete(&r->req, GOOD); } else { scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); trace_scsi_disk_write_complete_noio(r->req.tag, r->qiov.size); @@ -533,18 +582,16 @@ static void scsi_write_complete(void * opaque, int ret) SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); - assert (r->req.aiocb != NULL); r->req.aiocb = NULL; + /* 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) @@ -552,6 +599,7 @@ static void scsi_write_data(SCSIRequest *req) SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s)); + BlockCompletionFunc *cb; /* No data transfer may already be in progress */ assert(r->req.aiocb == NULL); @@ -577,19 +625,17 @@ static void scsi_write_data(SCSIRequest *req) if (r->req.cmd.buf[0] == VERIFY_10 || r->req.cmd.buf[0] == VERIFY_12 || r->req.cmd.buf[0] == VERIFY_16) { - if (r->req.sg) { - scsi_dma_complete_noio(r, 0); - } else { - scsi_write_complete_noio(r, 0); - } + block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0, + BLOCK_ACCT_FLUSH); + cb = r->req.sg ? scsi_dma_complete : scsi_write_complete; + r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, cb, r); return; } if (r->req.sg) { dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_WRITE); r->req.residual -= r->req.sg->size; - r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk), - r->req.sg, r->sector << BDRV_SECTOR_BITS, + r->req.aiocb = dma_blk_io(r->req.sg, r->sector << BDRV_SECTOR_BITS, BDRV_SECTOR_SIZE, sdc->dma_writev, r, scsi_dma_complete, r, DMA_DIRECTION_TO_DEVICE); @@ -647,8 +693,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); @@ -1628,9 +1674,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); } @@ -1741,8 +1788,6 @@ static void scsi_unmap_complete(void *opaque, int ret) SCSIDiskReq *r = data->r; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); - assert(r->req.aiocb != NULL); r->req.aiocb = NULL; @@ -1753,7 +1798,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) @@ -1821,8 +1865,6 @@ static void scsi_write_same_complete(void *opaque, int ret) SCSIDiskReq *r = data->r; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); - assert(r->req.aiocb != NULL); r->req.aiocb = NULL; @@ -1846,7 +1888,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; } @@ -1856,7 +1897,6 @@ static void scsi_write_same_complete(void *opaque, int ret) 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) @@ -1958,6 +1998,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(); } @@ -2323,7 +2367,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf) scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); return 0; } - r->need_fua_emulation = sdc->need_fua_emulation(&r->req.cmd); + r->need_fua = sdc->need_fua(&r->req.cmd); if (r->sector_count == 0) { scsi_req_complete(&r->req, GOOD); } @@ -2339,14 +2383,10 @@ static void scsi_disk_reset(DeviceState *dev) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev); uint64_t nb_sectors; - AioContext *ctx; scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET)); - ctx = blk_get_aio_context(s->qdev.conf.blk); - aio_context_acquire(ctx); blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - aio_context_release(ctx); nb_sectors /= s->qdev.blocksize / BDRV_SECTOR_SIZE; if (nb_sectors) { @@ -2506,9 +2546,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) { @@ -2545,15 +2596,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; @@ -2562,16 +2611,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; @@ -2587,8 +2631,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; @@ -2596,7 +2638,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); } @@ -2607,6 +2648,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 = { @@ -2663,19 +2706,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, @@ -2727,7 +2763,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; @@ -2742,9 +2777,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) { @@ -2752,18 +2784,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. @@ -2783,9 +2815,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 { @@ -2805,34 +2834,17 @@ 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) { if (io_hdr->host_status != SCSI_HOST_OK) { - scsi_req_complete_failed(&r->req, io_hdr->host_status); - scsi_req_unref(&r->req); - return; - } - - if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) { + r->req.host_status = io_hdr->host_status; + ret = -ENODEV; + } else if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) { ret = BUSY; } 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); @@ -3101,19 +3113,57 @@ BlockAIOCB *scsi_dma_writev(int64_t offset, QEMUIOVector *iov, { SCSIDiskReq *r = opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - return blk_aio_pwritev(s->qdev.conf.blk, offset, iov, 0, cb, cb_opaque); + int flags = r->need_fua ? BDRV_REQ_FUA : 0; + return blk_aio_pwritev(s->qdev.conf.blk, offset, iov, flags, 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_disk_base_class_initfn(ObjectClass *klass, void *data) +static void scsi_property_add_specifics(DeviceClass *dc) +{ + ObjectClass *oc = OBJECT_CLASS(dc); + + /* The loadparm property is only supported on s390x */ + if (qemu_arch_available(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, const 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; + sdc->need_fua = scsi_is_cmd_fua; } static const TypeInfo scsi_disk_base_info = { @@ -3133,15 +3183,16 @@ 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[] = { +static const Property scsi_hd_properties[] = { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_BIT("removable", SCSIDiskState, features, SCSI_DISK_F_REMOVABLE, false), DEFINE_PROP_BIT("dpofua", SCSIDiskState, features, - SCSI_DISK_F_DPOFUA, false), + SCSI_DISK_F_DPOFUA, true), DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0), DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0), DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0), @@ -3156,14 +3207,13 @@ static Property scsi_hd_properties[] = { quirks, SCSI_DISK_QUIRK_MODE_PAGE_VENDOR_SPECIFIC_APPLE, 0), DEFINE_BLOCK_CHS_PROPERTIES(SCSIDiskState, qdev.conf), - DEFINE_PROP_END_OF_LIST(), }; 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), @@ -3174,7 +3224,7 @@ static const VMStateDescription vmstate_scsi_disk_state = { } }; -static void scsi_hd_class_initfn(ObjectClass *klass, void *data) +static void scsi_hd_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); @@ -3186,6 +3236,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 = { @@ -3194,7 +3246,7 @@ static const TypeInfo scsi_hd_info = { .class_init = scsi_hd_class_initfn, }; -static Property scsi_cd_properties[] = { +static const Property scsi_cd_properties[] = { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0), DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0), @@ -3212,10 +3264,9 @@ static Property scsi_cd_properties[] = { 0), DEFINE_PROP_BIT("quirk_mode_page_truncated", SCSIDiskState, quirks, SCSI_DISK_QUIRK_MODE_PAGE_TRUNCATED, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void scsi_cd_class_initfn(ObjectClass *klass, void *data) +static void scsi_cd_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); @@ -3226,6 +3277,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 = { @@ -3235,7 +3288,7 @@ static const TypeInfo scsi_cd_info = { }; #ifdef __linux__ -static Property scsi_block_properties[] = { +static const Property scsi_block_properties[] = { DEFINE_BLOCK_ERROR_PROPERTIES(SCSIDiskState, qdev.conf), DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.blk), DEFINE_PROP_BOOL("share-rw", SCSIDiskState, qdev.conf.share_rw, false), @@ -3248,10 +3301,9 @@ static Property scsi_block_properties[] = { -1), DEFINE_PROP_UINT32("io_timeout", SCSIDiskState, qdev.io_timeout, DEFAULT_IO_TIMEOUT), - DEFINE_PROP_END_OF_LIST(), }; -static void scsi_block_class_initfn(ObjectClass *klass, void *data) +static void scsi_block_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); @@ -3263,7 +3315,7 @@ static void scsi_block_class_initfn(ObjectClass *klass, void *data) sdc->dma_readv = scsi_block_dma_readv; sdc->dma_writev = scsi_block_dma_writev; sdc->update_sense = scsi_block_update_sense; - sdc->need_fua_emulation = scsi_block_no_fua; + sdc->need_fua = scsi_block_no_fua; dc->desc = "SCSI block device passthrough"; device_class_set_props(dc, scsi_block_properties); dc->vmsd = &vmstate_scsi_disk_state; diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 2417f0ad847..9e380a2109e 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -21,7 +21,7 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "hw/scsi/emulation.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "trace.h" #ifdef __linux__ @@ -109,15 +109,11 @@ static void scsi_command_complete_noio(SCSIGenericReq *r, int ret) static void scsi_command_complete(void *opaque, int ret) { SCSIGenericReq *r = (SCSIGenericReq *)opaque; - SCSIDevice *s = r->req.dev; - - aio_context_acquire(blk_get_aio_context(s->conf.blk)); assert(r->req.aiocb != NULL); r->req.aiocb = NULL; scsi_command_complete_noio(r, ret); - aio_context_release(blk_get_aio_context(s->conf.blk)); } static int execute_command(BlockBackend *blk, @@ -274,14 +270,12 @@ static void scsi_read_complete(void * opaque, int ret) SCSIDevice *s = r->req.dev; int len; - aio_context_acquire(blk_get_aio_context(s->conf.blk)); - assert(r->req.aiocb != NULL); r->req.aiocb = NULL; 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; @@ -320,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. */ @@ -356,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. */ @@ -391,14 +382,12 @@ static void scsi_write_complete(void * opaque, int ret) trace_scsi_generic_write_complete(ret); - aio_context_acquire(blk_get_aio_context(s->conf.blk)); - assert(r->req.aiocb != NULL); r->req.aiocb = NULL; 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 && @@ -408,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. @@ -766,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); } @@ -787,12 +772,11 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, return scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private); } -static Property scsi_generic_properties[] = { +static const Property scsi_generic_properties[] = { DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.blk), DEFINE_PROP_BOOL("share-rw", SCSIDevice, conf.share_rw, false), DEFINE_PROP_UINT32("io_timeout", SCSIDevice, io_timeout, DEFAULT_IO_TIMEOUT), - DEFINE_PROP_END_OF_LIST(), }; static int scsi_generic_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, @@ -802,7 +786,7 @@ static int scsi_generic_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, return scsi_bus_parse_cdb(dev, cmd, buf, buf_len, hba_private); } -static void scsi_generic_class_initfn(ObjectClass *klass, void *data) +static void scsi_generic_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); @@ -812,7 +796,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 5bbbef64ef3..20f70fb2729 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), @@ -1250,16 +1250,15 @@ static int spapr_vscsi_devnode(SpaprVioDevice *dev, void *fdt, int node_off) return 0; } -static Property spapr_vscsi_properties[] = { +static const Property spapr_vscsi_properties[] = { DEFINE_SPAPR_PROPERTIES(VSCSIState, vdev), - DEFINE_PROP_END_OF_LIST(), }; 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 */ /* ???? */ @@ -1268,7 +1267,7 @@ static const VMStateDescription vmstate_spapr_vscsi = { }, }; -static void spapr_vscsi_class_init(ObjectClass *klass, void *data) +static void spapr_vscsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events index bdd4e2c7c78..f0f2a98c2ee 100644 --- a/hw/scsi/trace-events +++ b/hw/scsi/trace-events @@ -197,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" @@ -301,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 a06f01af262..43525ba46d3 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,39 +95,31 @@ 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); return ret; } -void vhost_scsi_common_stop(VHostSCSICommon *vsc) +int vhost_scsi_common_stop(VHostSCSICommon *vsc) { VirtIODevice *vdev = VIRTIO_DEVICE(vsc); BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); int ret = 0; - vhost_dev_stop(&vsc->dev, vdev, true); + ret = vhost_dev_stop(&vsc->dev, vdev, true); if (k->set_guest_notifiers) { - ret = k->set_guest_notifiers(qbus->parent, vsc->dev.nvqs, false); - if (ret < 0) { - error_report("vhost guest notifier cleanup failed: %d", ret); + int r = k->set_guest_notifiers(qbus->parent, vsc->dev.nvqs, false); + if (r < 0) { + error_report("vhost guest notifier cleanup failed: %d", ret); + return r; } } - assert(ret >= 0); - - if (vsc->inflight) { - vhost_dev_free_inflight(vsc->inflight); - g_free(vsc->inflight); - vsc->inflight = NULL; - } vhost_dev_disable_notifiers(&vsc->dev, vdev); + return ret; } uint64_t vhost_scsi_common_get_features(VirtIODevice *vdev, uint64_t features, diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 443f67daa4c..cdf405b0f86 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -29,7 +29,7 @@ #include "hw/fw-path-provider.h" #include "hw/qdev-properties.h" #include "qemu/cutils.h" -#include "sysemu/sysemu.h" +#include "system/system.h" /* Features supported by host kernel. */ static const int kernel_feature_bits[] = { @@ -38,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 }; @@ -75,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) { @@ -88,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; } @@ -110,7 +114,7 @@ static void vhost_scsi_stop(VHostSCSI *s) vhost_scsi_common_stop(vsc); } -static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) +static int vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) { VHostSCSI *s = VHOST_SCSI(vdev); VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); @@ -121,7 +125,7 @@ static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) } if (vhost_dev_is_started(&vsc->dev) == start) { - return; + return 0; } if (start) { @@ -135,6 +139,7 @@ static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) } else { vhost_scsi_stop(s); } + return 0; } static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) @@ -156,15 +161,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; @@ -208,7 +267,7 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) "When external environment supports it (Orchestrator migrates " "target SCSI device state or use shared storage over network), " "set 'migratable' property to true to enable migration."); - if (migrate_add_blocker(vsc->migration_blocker, errp) < 0) { + if (migrate_add_blocker_normal(&vsc->migration_blocker, errp) < 0) { goto free_virtio; } } @@ -230,6 +289,13 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) goto free_vqs; } + ret = vhost_scsi_set_workers(vsc, vs->conf.worker_per_virtqueue); + if (ret < 0) { + error_setg(errp, "vhost-scsi: vhost worker setup failed: %s", + strerror(-ret)); + goto free_vqs; + } + /* At present, channel and lun both are 0 for bootable vhost-scsi disk */ vsc->channel = 0; vsc->lun = 0; @@ -241,16 +307,14 @@ 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) { close(vhostfd); } - return; } static void vhost_scsi_unrealize(DeviceState *dev) @@ -260,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. */ @@ -280,7 +343,7 @@ static struct vhost_dev *vhost_scsi_get_vhost(VirtIODevice *vdev) return &vsc->dev; } -static Property vhost_scsi_properties[] = { +static const Property vhost_scsi_properties[] = { DEFINE_PROP_STRING("vhostfd", VirtIOSCSICommon, conf.vhostfd), DEFINE_PROP_STRING("wwpn", VirtIOSCSICommon, conf.wwpn), DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0), @@ -296,11 +359,15 @@ static Property vhost_scsi_properties[] = { DEFINE_PROP_BIT64("t10_pi", VHostSCSICommon, host_features, VIRTIO_SCSI_F_T10_PI, false), + DEFINE_PROP_BIT64("hotplug", VHostSCSICommon, host_features, + VIRTIO_SCSI_F_HOTPLUG, + false), DEFINE_PROP_BOOL("migratable", VHostSCSICommon, migratable, false), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_BOOL("worker_per_virtqueue", VirtIOSCSICommon, + conf.worker_per_virtqueue, false), }; -static void vhost_scsi_class_init(ObjectClass *klass, void *data) +static void vhost_scsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); @@ -334,7 +401,7 @@ static const TypeInfo vhost_scsi_info = { .instance_size = sizeof(VHostSCSI), .class_init = vhost_scsi_class_init, .instance_init = vhost_scsi_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { } }, diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index ee99b19e7ac..25f2d894e7c 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -27,7 +27,7 @@ #include "hw/virtio/vhost-user-scsi.h" #include "hw/virtio/virtio.h" #include "chardev/char-fe.h" -#include "sysemu/sysemu.h" +#include "system/system.h" /* Features supported by the host application */ static const int user_feature_bits[] = { @@ -36,76 +36,242 @@ 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 int vhost_user_scsi_stop(VHostUserSCSI *s) +{ + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + + if (!s->started_vu) { + return 0; + } + s->started_vu = false; + + return vhost_scsi_common_stop(vsc); +} -static void vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status) +static int 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) { - return; + if (!s->connected) { + return -1; } - if (start) { - int ret; + if (vhost_dev_is_started(&vsc->dev) == should_start) { + return 0; + } - 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); + ret = vhost_user_scsi_stop(s); + if (ret) { + return ret; + } } + return 0; } -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); + + Error *local_err = NULL; + int i, ret; + + if (!vdev->start_on_kick) { + return; + } + + if (!s->connected) { + return; + } + + if (vhost_dev_is_started(&vsc->dev)) { + return; + } /* - * Historically, reset was not implemented so only reset devices - * that are expecting it. + * Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start + * vhost here instead of waiting for .set_status(). */ - if (!virtio_has_feature(dev->protocol_features, - VHOST_USER_PROTOCOL_F_RESET_DEVICE)) { + 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; } - if (dev->vhost_ops->vhost_reset_device) { - dev->vhost_ops->vhost_reset_device(dev); + /* 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; @@ -115,18 +281,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; @@ -135,8 +311,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); } @@ -146,19 +326,26 @@ 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[] = { +static const Property vhost_user_scsi_properties[] = { DEFINE_PROP_CHR("chardev", VirtIOSCSICommon, conf.chardev), DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0), DEFINE_PROP_UINT32("num_queues", VirtIOSCSICommon, conf.num_queues, @@ -177,20 +364,33 @@ static Property vhost_user_scsi_properties[] = { DEFINE_PROP_BIT64("t10_pi", VHostSCSICommon, host_features, VIRTIO_SCSI_F_T10_PI, false), - 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() }, }; -static void vhost_user_scsi_class_init(ObjectClass *klass, void *data) +static void vhost_user_scsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); @@ -204,8 +404,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) @@ -225,7 +426,7 @@ static const TypeInfo vhost_user_scsi_info = { .instance_size = sizeof(VHostUserSCSI), .class_init = vhost_user_scsi_class_init, .instance_init = vhost_user_scsi_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { } }, diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 1e684beebe2..95f13fb7c28 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -15,12 +15,13 @@ #include "qapi/error.h" #include "hw/virtio/virtio-scsi.h" #include "qemu/error-report.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "hw/scsi/scsi.h" #include "scsi/constants.h" +#include "hw/virtio/iothread-vq-mapping.h" #include "hw/virtio/virtio-bus.h" -/* Context: QEMU global mutex held */ +/* Context: BQL held */ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp) { VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); @@ -28,7 +29,14 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp) BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - if (vs->conf.iothread) { + if (vs->conf.iothread && vs->conf.iothread_vq_mapping_list) { + error_setg(errp, + "iothread and iothread-vq-mapping properties cannot be set " + "at the same time"); + return; + } + + if (vs->conf.iothread || vs->conf.iothread_vq_mapping_list) { if (!k->set_guest_notifiers || !k->ioeventfd_assign) { error_setg(errp, "device is incompatible with iothread " @@ -39,13 +47,62 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp) error_setg(errp, "ioeventfd is required for iothread"); return; } - s->ctx = iothread_get_aio_context(vs->conf.iothread); - } else { - if (!virtio_device_ioeventfd_enabled(vdev)) { + } + + s->vq_aio_context = g_new(AioContext *, vs->conf.num_queues + + VIRTIO_SCSI_VQ_NUM_FIXED); + + /* + * Handle the ctrl virtqueue in the main loop thread where device resets + * can be performed. + */ + s->vq_aio_context[0] = qemu_get_aio_context(); + + /* + * Handle the event virtqueue in the main loop thread where its no_poll + * behavior won't stop IOThread polling. + */ + s->vq_aio_context[1] = qemu_get_aio_context(); + + if (vs->conf.iothread_vq_mapping_list) { + if (!iothread_vq_mapping_apply(vs->conf.iothread_vq_mapping_list, + &s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED], + vs->conf.num_queues, errp)) { + g_free(s->vq_aio_context); + s->vq_aio_context = NULL; return; } - s->ctx = qemu_get_aio_context(); + } else if (vs->conf.iothread) { + AioContext *ctx = iothread_get_aio_context(vs->conf.iothread); + for (uint16_t i = 0; i < vs->conf.num_queues; i++) { + s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i] = ctx; + } + + /* Released in virtio_scsi_dataplane_cleanup() */ + object_ref(OBJECT(vs->conf.iothread)); + } else { + AioContext *ctx = qemu_get_aio_context(); + for (unsigned i = 0; i < vs->conf.num_queues; i++) { + s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i] = ctx; + } + } +} + +/* Context: BQL held */ +void virtio_scsi_dataplane_cleanup(VirtIOSCSI *s) +{ + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + + if (vs->conf.iothread_vq_mapping_list) { + iothread_vq_mapping_cleanup(vs->conf.iothread_vq_mapping_list); } + + if (vs->conf.iothread) { + object_unref(OBJECT(vs->conf.iothread)); + } + + g_free(s->vq_aio_context); + s->vq_aio_context = NULL; } static int virtio_scsi_set_host_notifier(VirtIOSCSI *s, VirtQueue *vq, int n) @@ -66,34 +123,23 @@ static int virtio_scsi_set_host_notifier(VirtIOSCSI *s, VirtQueue *vq, int n) } /* Context: BH in IOThread */ -static void virtio_scsi_dataplane_stop_bh(void *opaque) +static void virtio_scsi_dataplane_stop_vq_bh(void *opaque) { - VirtIOSCSI *s = opaque; - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + AioContext *ctx = qemu_get_current_aio_context(); + VirtQueue *vq = opaque; 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); + virtio_queue_aio_detach_host_notifier(vq, ctx); + host_notifier = virtio_queue_get_host_notifier(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; @@ -149,23 +195,20 @@ 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() */ if (s->bus.drain_count == 0) { - 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); + virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, + s->vq_aio_context[0]); + virtio_queue_aio_attach_host_notifier_no_poll(vs->event_vq, + s->vq_aio_context[1]); for (i = 0; i < vs->conf.num_queues; i++) { - virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], s->ctx); + AioContext *ctx = s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i]; + virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], ctx); } - aio_context_release(s->ctx); } return 0; @@ -191,7 +234,7 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev) 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)); @@ -213,7 +256,11 @@ void virtio_scsi_dataplane_stop(VirtIODevice *vdev) s->dataplane_stopping = true; if (s->bus.drain_count == 0) { - aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s); + for (i = 0; i < vs->conf.num_queues + VIRTIO_SCSI_VQ_NUM_FIXED; i++) { + VirtQueue *vq = virtio_get_queue(&vs->parent_obj, i); + AioContext *ctx = s->vq_aio_context[i]; + aio_wait_bh_oneshot(ctx, virtio_scsi_dataplane_stop_vq_bh, vq); + } } 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 45b95ea0709..34ae14f7bf9 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -18,14 +18,16 @@ #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 "system/block-backend.h" +#include "system/dma.h" #include "hw/qdev-properties.h" #include "hw/scsi/scsi.h" #include "scsi/constants.h" +#include "hw/virtio/iothread-vq-mapping.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" #include "trace.h" @@ -46,7 +48,7 @@ typedef struct VirtIOSCSIReq { /* Used for two-stage request submission and TMFs deferred to BH */ QTAILQ_ENTRY(VirtIOSCSIReq) next; - /* Used for cancellation of request during TMFs */ + /* Used for cancellation of request during TMFs. Atomic. */ int remaining; SCSIRequest *sreq; @@ -101,13 +103,18 @@ static void virtio_scsi_free_req(VirtIOSCSIReq *req) g_free(req); } -static void virtio_scsi_complete_req(VirtIOSCSIReq *req) +static void virtio_scsi_complete_req(VirtIOSCSIReq *req, QemuMutex *vq_lock) { VirtIOSCSI *s = req->dev; VirtQueue *vq = req->vq; VirtIODevice *vdev = VIRTIO_DEVICE(s); qemu_iovec_from_buf(&req->resp_iov, 0, &req->resp, req->resp_size); + + if (vq_lock) { + qemu_mutex_lock(vq_lock); + } + virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size); if (s->dataplane_started && !s->dataplane_fenced) { virtio_notify_irqfd(vdev, vq); @@ -115,6 +122,10 @@ static void virtio_scsi_complete_req(VirtIOSCSIReq *req) virtio_notify(vdev, vq); } + if (vq_lock) { + qemu_mutex_unlock(vq_lock); + } + if (req->sreq) { req->sreq->hba_private = NULL; scsi_req_unref(req->sreq); @@ -122,10 +133,20 @@ static void virtio_scsi_complete_req(VirtIOSCSIReq *req) virtio_scsi_free_req(req); } -static void virtio_scsi_bad_req(VirtIOSCSIReq *req) +static void virtio_scsi_bad_req(VirtIOSCSIReq *req, QemuMutex *vq_lock) { virtio_error(VIRTIO_DEVICE(req->dev), "wrong size for virtio-scsi headers"); + + if (vq_lock) { + qemu_mutex_lock(vq_lock); + } + virtqueue_detach_element(req->vq, &req->elem, 0); + + if (vq_lock) { + qemu_mutex_unlock(vq_lock); + } + virtio_scsi_free_req(req); } @@ -210,12 +231,21 @@ static int virtio_scsi_parse_req(VirtIOSCSIReq *req, return 0; } -static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq) +static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq, QemuMutex *vq_lock) { VirtIOSCSICommon *vs = (VirtIOSCSICommon *)s; VirtIOSCSIReq *req; + if (vq_lock) { + qemu_mutex_lock(vq_lock); + } + req = virtqueue_pop(vq, sizeof(VirtIOSCSIReq) + vs->cdb_size); + + if (vq_lock) { + qemu_mutex_unlock(vq_lock); + } + if (!req) { return NULL; } @@ -269,143 +299,157 @@ typedef struct { VirtIOSCSIReq *tmf_req; } VirtIOSCSICancelNotifier; +static void virtio_scsi_tmf_dec_remaining(VirtIOSCSIReq *tmf) +{ + if (qatomic_fetch_dec(&tmf->remaining) == 1) { + trace_virtio_scsi_tmf_resp(virtio_scsi_get_lun(tmf->req.tmf.lun), + tmf->req.tmf.tag, tmf->resp.tmf.response); + + virtio_scsi_complete_req(tmf, &tmf->dev->ctrl_lock); + } +} + static void virtio_scsi_cancel_notify(Notifier *notifier, void *data) { VirtIOSCSICancelNotifier *n = container_of(notifier, VirtIOSCSICancelNotifier, notifier); - if (--n->tmf_req->remaining == 0) { - VirtIOSCSIReq *req = n->tmf_req; - - trace_virtio_scsi_tmf_resp(virtio_scsi_get_lun(req->req.tmf.lun), - req->req.tmf.tag, req->resp.tmf.response); - virtio_scsi_complete_req(req); - } + virtio_scsi_tmf_dec_remaining(n->tmf_req); g_free(n); } -static inline void virtio_scsi_ctx_check(VirtIOSCSI *s, SCSIDevice *d) +static void virtio_scsi_tmf_cancel_req(VirtIOSCSIReq *tmf, SCSIRequest *r) { - if (s->dataplane_started && d && blk_is_available(d->conf.blk)) { - assert(blk_get_aio_context(d->conf.blk) == s->ctx); - } + VirtIOSCSICancelNotifier *notifier; + + assert(r->ctx == qemu_get_current_aio_context()); + + /* Decremented in virtio_scsi_cancel_notify() */ + qatomic_inc(&tmf->remaining); + + notifier = g_new(VirtIOSCSICancelNotifier, 1); + notifier->notifier.notify = virtio_scsi_cancel_notify; + notifier->tmf_req = tmf; + scsi_req_cancel_async(r, ¬ifier->notifier); } -static void virtio_scsi_do_one_tmf_bh(VirtIOSCSIReq *req) +/* Execute a TMF on the requests in the current AioContext */ +static void virtio_scsi_do_tmf_aio_context(void *opaque) { - VirtIOSCSI *s = req->dev; - SCSIDevice *d = virtio_scsi_device_get(s, req->req.tmf.lun); - BusChild *kid; - int target; + AioContext *ctx = qemu_get_current_aio_context(); + VirtIOSCSIReq *tmf = opaque; + VirtIOSCSI *s = tmf->dev; + SCSIDevice *d = virtio_scsi_device_get(s, tmf->req.tmf.lun); + SCSIRequest *r; + bool match_tag; - 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); + if (!d) { + tmf->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET; + virtio_scsi_tmf_dec_remaining(tmf); + return; + } + + /* + * This function could handle other subtypes that need to be processed in + * the request's AioContext in the future, but for now only request + * cancelation subtypes are performed here. + */ + switch (tmf->req.tmf.subtype) { + case VIRTIO_SCSI_T_TMF_ABORT_TASK: + match_tag = true; break; + case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET: + case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET: + match_tag = false; + break; + default: + g_assert_not_reached(); + } - case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: - target = req->req.tmf.lun[1]; - qatomic_inc(&s->resetting); + WITH_QEMU_LOCK_GUARD(&d->requests_lock) { + QTAILQ_FOREACH(r, &d->requests, next) { + VirtIOSCSIReq *cmd_req = r->hba_private; + assert(cmd_req); /* request has hba_private while enqueued */ - 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); + if (r->ctx != ctx) { + continue; } + if (match_tag && cmd_req->req.cmd.tag != tmf->req.tmf.tag) { + continue; + } + virtio_scsi_tmf_cancel_req(tmf, r); } - rcu_read_unlock(); - - qatomic_dec(&s->resetting); - break; - - default: - g_assert_not_reached(); - break; } -out: - object_unref(OBJECT(d)); + /* Incremented by virtio_scsi_do_tmf() */ + virtio_scsi_tmf_dec_remaining(tmf); - virtio_scsi_acquire(s); - virtio_scsi_complete_req(req); - virtio_scsi_release(s); + object_unref(d); } -/* Some TMFs must be processed from the main loop thread */ -static void virtio_scsi_do_tmf_bh(void *opaque) +static void dummy_bh(void *opaque) { - VirtIOSCSI *s = opaque; - QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); - VirtIOSCSIReq *req; - VirtIOSCSIReq *tmp; + /* Do nothing */ +} +/* + * Wait for pending virtio_scsi_defer_tmf_to_aio_context() BHs. + */ +static void virtio_scsi_flush_defer_tmf_to_aio_context(VirtIOSCSI *s) +{ GLOBAL_STATE_CODE(); - virtio_scsi_acquire(s); - - 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; + assert(!s->dataplane_started); - virtio_scsi_release(s); + for (uint32_t i = 0; i < s->parent_obj.conf.num_queues; i++) { + AioContext *ctx = s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i]; - QTAILQ_FOREACH_SAFE(req, &reqs, next, tmp) { - QTAILQ_REMOVE(&reqs, req, next); - virtio_scsi_do_one_tmf_bh(req); + /* Our BH only runs after previously scheduled BHs */ + aio_wait_bh_oneshot(ctx, dummy_bh, NULL); } } -static void virtio_scsi_reset_tmf_bh(VirtIOSCSI *s) +/* + * Run the TMF in a specific AioContext, handling only requests in that + * AioContext. This is necessary because requests can run in different + * AioContext and it is only possible to cancel them from the AioContext where + * they are running. + */ +static void virtio_scsi_defer_tmf_to_aio_context(VirtIOSCSIReq *tmf, + AioContext *ctx) { - VirtIOSCSIReq *req; - VirtIOSCSIReq *tmp; - - GLOBAL_STATE_CODE(); - - virtio_scsi_acquire(s); - - 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); - } + /* Decremented in virtio_scsi_do_tmf_aio_context() */ + qatomic_inc(&tmf->remaining); - virtio_scsi_release(s); + /* See virtio_scsi_flush_defer_tmf_to_aio_context() cleanup during reset */ + aio_bh_schedule_oneshot(ctx, virtio_scsi_do_tmf_aio_context, tmf); } -static void virtio_scsi_defer_tmf_to_bh(VirtIOSCSIReq *req) +/* + * Returns the AioContext for a given TMF's tag field or NULL. Note that the + * request identified by the tag may have completed by the time you can execute + * a BH in the AioContext, so don't assume the request still exists in your BH. + */ +static AioContext *find_aio_context_for_tmf_tag(SCSIDevice *d, + VirtIOSCSIReq *tmf) { - VirtIOSCSI *s = req->dev; + WITH_QEMU_LOCK_GUARD(&d->requests_lock) { + SCSIRequest *r; + SCSIRequest *next; + + QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { + VirtIOSCSIReq *cmd_req = r->hba_private; - QTAILQ_INSERT_TAIL(&s->tmf_bh_list, req, next); + /* hba_private is non-NULL while the request is enqueued */ + assert(cmd_req); - if (!s->tmf_bh) { - s->tmf_bh = qemu_bh_new(virtio_scsi_do_tmf_bh, s); - qemu_bh_schedule(s->tmf_bh); + if (cmd_req->req.cmd.tag == tmf->req.tmf.tag) { + return r->ctx; + } + } } + return NULL; } /* Return 0 if the request is ready to be completed and return to guest; @@ -415,9 +459,9 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) { SCSIDevice *d = virtio_scsi_device_get(s, req->req.tmf.lun); SCSIRequest *r, *next; + AioContext *ctx; int ret = 0; - virtio_scsi_ctx_check(s, d); /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */ req->resp.tmf.response = VIRTIO_SCSI_S_OK; @@ -432,7 +476,22 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) req->req.tmf.tag, req->req.tmf.subtype); switch (req->req.tmf.subtype) { - case VIRTIO_SCSI_T_TMF_ABORT_TASK: + case VIRTIO_SCSI_T_TMF_ABORT_TASK: { + if (!d) { + goto fail; + } + if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { + goto incorrect_lun; + } + + ctx = find_aio_context_for_tmf_tag(d, req); + if (ctx) { + virtio_scsi_defer_tmf_to_aio_context(req, ctx); + ret = -EINPROGRESS; + } + break; + } + case VIRTIO_SCSI_T_TMF_QUERY_TASK: if (!d) { goto fail; @@ -440,44 +499,82 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { goto incorrect_lun; } - QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { - VirtIOSCSIReq *cmd_req = r->hba_private; - if (cmd_req && cmd_req->req.cmd.tag == req->req.tmf.tag) { - break; + + WITH_QEMU_LOCK_GUARD(&d->requests_lock) { + QTAILQ_FOREACH(r, &d->requests, next) { + VirtIOSCSIReq *cmd_req = r->hba_private; + assert(cmd_req); /* request has hba_private while enqueued */ + + if (cmd_req->req.cmd.tag == req->req.tmf.tag) { + /* + * "If the specified command is present in the task set, + * then return a service response set to FUNCTION + * SUCCEEDED". + */ + req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; + } } } - if (r) { - /* - * Assert that the request has not been completed yet, we - * check for it in the loop above. - */ - assert(r->hba_private); - if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) { - /* "If the specified command is present in the task set, then - * return a service response set to FUNCTION SUCCEEDED". - */ - req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; - } else { - VirtIOSCSICancelNotifier *notifier; - - req->remaining = 1; - notifier = g_new(VirtIOSCSICancelNotifier, 1); - notifier->tmf_req = req; - notifier->notifier.notify = virtio_scsi_cancel_notify; - scsi_req_cancel_async(r, ¬ifier->notifier); - ret = -EINPROGRESS; + 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; + } + qatomic_inc(&s->resetting); + device_cold_reset(&d->qdev); + qatomic_dec(&s->resetting); + break; + + case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: { + BusChild *kid; + int 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; + } - case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET: - case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: - virtio_scsi_defer_tmf_to_bh(req); + case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET: + case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET: { + g_autoptr(GHashTable) aio_contexts = g_hash_table_new(NULL, NULL); + + if (!d) { + goto fail; + } + if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { + goto incorrect_lun; + } + + qatomic_inc(&req->remaining); + + for (uint32_t i = 0; i < s->parent_obj.conf.num_queues; i++) { + ctx = s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i]; + + if (!g_hash_table_add(aio_contexts, ctx)) { + continue; /* skip previously added AioContext */ + } + + virtio_scsi_defer_tmf_to_aio_context(req, ctx); + } + + virtio_scsi_tmf_dec_remaining(req); ret = -EINPROGRESS; break; + } - case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET: - case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET: case VIRTIO_SCSI_T_TMF_QUERY_TASK_SET: if (!d) { goto fail; @@ -486,34 +583,19 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) goto incorrect_lun; } - /* Add 1 to "remaining" until virtio_scsi_do_tmf returns. - * This way, if the bus starts calling back to the notifiers - * even before we finish the loop, virtio_scsi_cancel_notify - * will not complete the TMF too early. - */ - req->remaining = 1; - QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { - if (r->hba_private) { - if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) { - /* "If there is any command present in the task set, then - * return a service response set to FUNCTION SUCCEEDED". - */ - req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; - break; - } else { - VirtIOSCSICancelNotifier *notifier; - - req->remaining++; - notifier = g_new(VirtIOSCSICancelNotifier, 1); - notifier->notifier.notify = virtio_scsi_cancel_notify; - notifier->tmf_req = req; - scsi_req_cancel_async(r, ¬ifier->notifier); - } + WITH_QEMU_LOCK_GUARD(&d->requests_lock) { + QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { + /* Request has hba_private while enqueued */ + assert(r->hba_private); + + /* + * "If there is any command present in the task set, then + * return a service response set to FUNCTION SUCCEEDED". + */ + req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; + break; } } - if (--req->remaining > 0) { - ret = -EINPROGRESS; - } break; case VIRTIO_SCSI_T_TMF_CLEAR_ACA: @@ -544,7 +626,7 @@ static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0, &type, sizeof(type)) < sizeof(type)) { - virtio_scsi_bad_req(req); + virtio_scsi_bad_req(req, &s->ctrl_lock); return; } @@ -552,7 +634,7 @@ static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) if (type == VIRTIO_SCSI_T_TMF) { if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlTMFReq), sizeof(VirtIOSCSICtrlTMFResp)) < 0) { - virtio_scsi_bad_req(req); + virtio_scsi_bad_req(req, &s->ctrl_lock); return; } else { r = virtio_scsi_do_tmf(s, req); @@ -562,7 +644,7 @@ static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) type == VIRTIO_SCSI_T_AN_SUBSCRIBE) { if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlANReq), sizeof(VirtIOSCSICtrlANResp)) < 0) { - virtio_scsi_bad_req(req); + virtio_scsi_bad_req(req, &s->ctrl_lock); return; } else { req->req.an.event_requested = @@ -582,7 +664,7 @@ static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) type == VIRTIO_SCSI_T_AN_SUBSCRIBE) trace_virtio_scsi_an_resp(virtio_scsi_get_lun(req->req.an.lun), req->resp.an.response); - virtio_scsi_complete_req(req); + virtio_scsi_complete_req(req, &s->ctrl_lock); } else { assert(r == -EINPROGRESS); } @@ -592,7 +674,7 @@ static void virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq) { VirtIOSCSIReq *req; - while ((req = virtio_scsi_pop_req(s, vq))) { + while ((req = virtio_scsi_pop_req(s, vq, &s->ctrl_lock))) { virtio_scsi_handle_ctrl_req(s, req); } } @@ -607,9 +689,12 @@ static void virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq) */ static bool virtio_scsi_defer_to_dataplane(VirtIOSCSI *s) { - if (!s->ctx || s->dataplane_started) { + if (s->dataplane_started) { return false; } + if (s->vq_aio_context[0] == qemu_get_aio_context()) { + return false; /* not using IOThreads */ + } virtio_device_start_ioeventfd(&s->parent_obj.parent_obj); return !s->dataplane_fenced; @@ -623,9 +708,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) @@ -638,7 +721,7 @@ static void virtio_scsi_complete_cmd_req(VirtIOSCSIReq *req) * in virtio_scsi_command_complete. */ req->resp_size = sizeof(VirtIOSCSICmdResp); - virtio_scsi_complete_req(req); + virtio_scsi_complete_req(req, NULL); } static void virtio_scsi_command_failed(SCSIRequest *r) @@ -761,7 +844,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; @@ -772,7 +855,7 @@ static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) virtio_scsi_fail_cmd_req(req); return -ENOTSUP; } else { - virtio_scsi_bad_req(req); + virtio_scsi_bad_req(req, NULL); return -EINVAL; } } @@ -785,7 +868,6 @@ static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) virtio_scsi_complete_cmd_req(req); return -ENOENT; } - virtio_scsi_ctx_check(s, d); req->sreq = scsi_req_new(d, req->req.cmd.tag, virtio_scsi_get_lun(req->req.cmd.lun), req->req.cmd.cdb, vs->cdb_size, req); @@ -799,7 +881,7 @@ static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) return -ENOBUFS; } scsi_req_ref(req->sreq); - blk_io_plug(); + defer_call_begin(); object_unref(OBJECT(d)); return 0; } @@ -810,7 +892,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(); + defer_call_end(); scsi_req_unref(sreq); } @@ -827,7 +909,7 @@ static void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq) virtio_queue_set_notification(vq, 0); } - while ((req = virtio_scsi_pop_req(s, vq))) { + while ((req = virtio_scsi_pop_req(s, vq, NULL))) { ret = virtio_scsi_handle_cmd_req_prepare(s, req); if (!ret) { QTAILQ_INSERT_TAIL(&reqs, req, next); @@ -836,7 +918,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(); + defer_call_end(); scsi_req_unref(req->sreq); virtqueue_detach_element(req->vq, &req->elem, 0); virtio_scsi_free_req(req); @@ -863,9 +945,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, @@ -922,7 +1002,7 @@ static void virtio_scsi_reset(VirtIODevice *vdev) assert(!s->dataplane_started); - virtio_scsi_reset_tmf_bh(s); + virtio_scsi_flush_defer_tmf_to_aio_context(s); qatomic_inc(&s->resetting); bus_cold_reset(BUS(&s->bus)); @@ -930,7 +1010,10 @@ static void virtio_scsi_reset(VirtIODevice *vdev) vs->sense_size = VIRTIO_SCSI_SENSE_DEFAULT_SIZE; vs->cdb_size = VIRTIO_SCSI_CDB_DEFAULT_SIZE; - s->events_dropped = false; + + WITH_QEMU_LOCK_GUARD(&s->event_lock) { + s->events_dropped = false; + } } typedef struct { @@ -959,19 +1042,21 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, return; } - req = virtio_scsi_pop_req(s, vs->event_vq); - if (!req) { - s->events_dropped = true; - return; - } + req = virtio_scsi_pop_req(s, vs->event_vq, &s->event_lock); + WITH_QEMU_LOCK_GUARD(&s->event_lock) { + if (!req) { + s->events_dropped = true; + return; + } - if (s->events_dropped) { - event |= VIRTIO_SCSI_T_EVENTS_MISSED; - s->events_dropped = false; + if (s->events_dropped) { + event |= VIRTIO_SCSI_T_EVENTS_MISSED; + s->events_dropped = false; + } } if (virtio_scsi_parse_req(req, 0, sizeof(VirtIOSCSIEvent))) { - virtio_scsi_bad_req(req); + virtio_scsi_bad_req(req, &s->event_lock); return; } @@ -991,12 +1076,18 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, } trace_virtio_scsi_event(virtio_scsi_get_lun(evt->lun), event, reason); - virtio_scsi_complete_req(req); + virtio_scsi_complete_req(req, &s->event_lock); } static void virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq) { - if (s->events_dropped) { + bool events_dropped; + + WITH_QEMU_LOCK_GUARD(&s->event_lock) { + events_dropped = s->events_dropped; + } + + if (events_dropped) { VirtIOSCSIEventInfo info = { .event = VIRTIO_SCSI_T_NO_EVENT, }; @@ -1012,9 +1103,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) @@ -1033,9 +1122,7 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) }, }; - virtio_scsi_acquire(s); virtio_scsi_push_event(s, &info); - virtio_scsi_release(s); } } @@ -1051,21 +1138,16 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, { VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); VirtIOSCSI *s = VIRTIO_SCSI(vdev); + AioContext *ctx = s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED]; 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 (ctx != qemu_get_aio_context() && !s->dataplane_fenced) { + /* + * Try to make the BlockBackend's AioContext match ours. Ignore failure + * because I/O will still work although block jobs and other users + * might be slower when multiple AioContexts use a BlockBackend. + */ + blk_set_aio_context(sd->conf.blk, ctx, NULL); } if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { @@ -1078,10 +1160,8 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, }, }; - virtio_scsi_acquire(s); virtio_scsi_push_event(s, &info); scsi_bus_set_ua(&s->bus, SENSE_CODE(REPORTED_LUNS_CHANGED)); - virtio_scsi_release(s); } } @@ -1102,18 +1182,14 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, qdev_simple_device_unplug_cb(hotplug_dev, dev, errp); - if (s->ctx) { - virtio_scsi_acquire(s); + if (s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED] != qemu_get_aio_context()) { /* 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_acquire(s); virtio_scsi_push_event(s, &info); scsi_bus_set_ua(&s->bus, SENSE_CODE(REPORTED_LUNS_CHANGED)); - virtio_scsi_release(s); } } @@ -1140,7 +1216,7 @@ static void virtio_scsi_drained_begin(SCSIBus *bus) 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); + virtio_queue_aio_detach_host_notifier(vq, s->vq_aio_context[i]); } } @@ -1148,6 +1224,7 @@ static void virtio_scsi_drained_begin(SCSIBus *bus) 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; @@ -1165,7 +1242,13 @@ static void virtio_scsi_drained_end(SCSIBus *bus) for (uint32_t i = 0; i < total_queues; i++) { VirtQueue *vq = virtio_get_queue(vdev, i); - virtio_queue_aio_attach_host_notifier(vq, s->ctx); + AioContext *ctx = s->vq_aio_context[i]; + + if (vq == vs->event_vq) { + virtio_queue_aio_attach_host_notifier_no_poll(vq, ctx); + } else { + virtio_queue_aio_attach_host_notifier(vq, ctx); + } } } @@ -1233,7 +1316,8 @@ 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->ctrl_lock); + qemu_mutex_init(&s->event_lock); virtio_scsi_common_realize(dev, virtio_scsi_handle_ctrl, @@ -1268,17 +1352,19 @@ void virtio_scsi_common_unrealize(DeviceState *dev) virtio_cleanup(vdev); } +/* main loop */ static void virtio_scsi_device_unrealize(DeviceState *dev) { VirtIOSCSI *s = VIRTIO_SCSI(dev); - virtio_scsi_reset_tmf_bh(s); - + virtio_scsi_dataplane_cleanup(s); qbus_set_hotplug_handler(BUS(&s->bus), NULL); virtio_scsi_common_unrealize(dev); + qemu_mutex_destroy(&s->event_lock); + qemu_mutex_destroy(&s->ctrl_lock); } -static Property virtio_scsi_properties[] = { +static const Property virtio_scsi_properties[] = { DEFINE_PROP_UINT32("num_queues", VirtIOSCSI, parent_obj.conf.num_queues, VIRTIO_SCSI_AUTO_NUM_QUEUES), DEFINE_PROP_UINT32("virtqueue_size", VirtIOSCSI, @@ -1295,20 +1381,21 @@ static Property virtio_scsi_properties[] = { VIRTIO_SCSI_F_CHANGE, true), DEFINE_PROP_LINK("iothread", VirtIOSCSI, parent_obj.conf.iothread, TYPE_IOTHREAD, IOThread *), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_IOTHREAD_VQ_MAPPING_LIST("iothread-vq-mapping", VirtIOSCSI, + parent_obj.conf.iothread_vq_mapping_list), }; 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() }, }; -static void virtio_scsi_common_class_init(ObjectClass *klass, void *data) +static void virtio_scsi_common_class_init(ObjectClass *klass, const void *data) { VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -1317,7 +1404,7 @@ static void virtio_scsi_common_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } -static void virtio_scsi_class_init(ObjectClass *klass, void *data) +static void virtio_scsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); @@ -1351,7 +1438,7 @@ static const TypeInfo virtio_scsi_info = { .parent = TYPE_VIRTIO_SCSI_COMMON, .instance_size = sizeof(VirtIOSCSI), .class_init = virtio_scsi_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 4de34536e98..7c98b1b8ea6 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -68,18 +68,7 @@ struct PVSCSIClass { OBJECT_DECLARE_TYPE(PVSCSIState, PVSCSIClass, PVSCSI) -/* Compatibility flags for migration */ -#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT 0 -#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION \ - (1 << PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT) -#define PVSCSI_COMPAT_DISABLE_PCIE_BIT 1 -#define PVSCSI_COMPAT_DISABLE_PCIE \ - (1 << PVSCSI_COMPAT_DISABLE_PCIE_BIT) - -#define PVSCSI_USE_OLD_PCI_CONFIGURATION(s) \ - ((s)->compat_flags & PVSCSI_COMPAT_OLD_PCI_CONFIGURATION) -#define PVSCSI_MSI_OFFSET(s) \ - (PVSCSI_USE_OLD_PCI_CONFIGURATION(s) ? 0x50 : 0x7c) +#define PVSCSI_MSI_OFFSET (0x7c) #define PVSCSI_EXP_EP_OFFSET (0x40) typedef struct PVSCSIRingInfo { @@ -129,8 +118,6 @@ struct PVSCSIState { uint8_t msi_used; /* For migration compatibility */ PVSCSIRingInfo rings; /* Data transfer rings manager */ uint32_t resetting; /* Reset in progress */ - - uint32_t compat_flags; }; typedef struct PVSCSIRequest { @@ -1110,7 +1097,7 @@ pvscsi_init_msi(PVSCSIState *s) int res; PCIDevice *d = PCI_DEVICE(s); - res = msi_init(d, PVSCSI_MSI_OFFSET(s), PVSCSI_MSIX_NUM_VECTORS, + res = msi_init(d, PVSCSI_MSI_OFFSET, PVSCSI_MSIX_NUM_VECTORS, PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK, NULL); if (res < 0) { trace_pvscsi_init_msi_fail(res); @@ -1158,15 +1145,11 @@ pvscsi_realizefn(PCIDevice *pci_dev, Error **errp) trace_pvscsi_state("init"); /* PCI subsystem ID, subsystem vendor ID, revision */ - if (PVSCSI_USE_OLD_PCI_CONFIGURATION(s)) { - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, 0x1000); - } else { - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, - PCI_VENDOR_ID_VMWARE); - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, - PCI_DEVICE_ID_VMWARE_PVSCSI); - pci_config_set_revision(pci_dev->config, 0x2); - } + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, + PCI_VENDOR_ID_VMWARE); + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, + PCI_DEVICE_ID_VMWARE_PVSCSI); + pci_config_set_revision(pci_dev->config, 0x2); /* PCI latency timer = 255 */ pci_dev->config[PCI_LATENCY_TIMER] = 0xff; @@ -1234,22 +1217,9 @@ pvscsi_post_load(void *opaque, int version_id) return 0; } -static bool pvscsi_vmstate_need_pcie_device(void *opaque) -{ - PVSCSIState *s = PVSCSI(opaque); - - return !(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE); -} - -static bool pvscsi_vmstate_test_pci_device(void *opaque, int version_id) -{ - return !pvscsi_vmstate_need_pcie_device(opaque); -} - 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() } @@ -1261,10 +1231,7 @@ static const VMStateDescription vmstate_pvscsi = { .minimum_version_id = 0, .pre_save = pvscsi_pre_save, .post_load = pvscsi_post_load, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_TEST(parent_obj, PVSCSIState, - pvscsi_vmstate_test_pci_device, 0, - vmstate_pci_device, PCIDevice), + .fields = (const VMStateField[]) { VMSTATE_UINT8(msi_used, PVSCSIState), VMSTATE_UINT32(resetting, PVSCSIState), VMSTATE_UINT64(reg_interrupt_status, PVSCSIState), @@ -1290,39 +1257,25 @@ static const VMStateDescription vmstate_pvscsi = { VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_pvscsi_pcie_device, NULL } }; -static Property pvscsi_properties[] = { +static const Property pvscsi_properties[] = { DEFINE_PROP_UINT8("use_msg", PVSCSIState, use_msg, 1), - DEFINE_PROP_BIT("x-old-pci-configuration", PVSCSIState, compat_flags, - PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT, false), - DEFINE_PROP_BIT("x-disable-pcie", PVSCSIState, compat_flags, - PVSCSI_COMPAT_DISABLE_PCIE_BIT, false), - DEFINE_PROP_END_OF_LIST(), }; -static void pvscsi_realize(DeviceState *qdev, Error **errp) +static void pvscsi_instance_init(Object *obj) { - PVSCSIClass *pvs_c = PVSCSI_GET_CLASS(qdev); - PCIDevice *pci_dev = PCI_DEVICE(qdev); - PVSCSIState *s = PVSCSI(qdev); - - if (!(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE)) { - pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; - } - - pvs_c->parent_dc_realize(qdev, errp); + PCI_DEVICE(obj)->cap_present |= QEMU_PCI_CAP_EXPRESS; } -static void pvscsi_class_init(ObjectClass *klass, void *data) +static void pvscsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - PVSCSIClass *pvs_k = PVSCSI_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); k->realize = pvscsi_realizefn; @@ -1331,9 +1284,7 @@ static void pvscsi_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_VMWARE_PVSCSI; k->class_id = PCI_CLASS_STORAGE_SCSI; 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); @@ -1347,7 +1298,8 @@ static const TypeInfo pvscsi_info = { .class_size = sizeof(PVSCSIClass), .instance_size = sizeof(PVSCSIState), .class_init = pvscsi_class_init, - .interfaces = (InterfaceInfo[]) { + .instance_init = pvscsi_instance_init, + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c index 1a576d62ae2..b31da5c399c 100644 --- a/hw/sd/allwinner-sdhost.c +++ b/hw/sd/allwinner-sdhost.c @@ -22,8 +22,8 @@ #include "qemu/module.h" #include "qemu/units.h" #include "qapi/error.h" -#include "sysemu/blockdev.h" -#include "sysemu/dma.h" +#include "system/blockdev.h" +#include "system/dma.h" #include "hw/qdev-properties.h" #include "hw/irq.h" #include "hw/sd/allwinner-sdhost.h" @@ -761,7 +761,7 @@ static void allwinner_sdhost_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_sdhost_ops = { .read = allwinner_sdhost_read, .write = allwinner_sdhost_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -773,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), @@ -808,10 +808,9 @@ static const VMStateDescription vmstate_allwinner_sdhost = { } }; -static Property allwinner_sdhost_properties[] = { +static const Property allwinner_sdhost_properties[] = { DEFINE_PROP_LINK("dma-memory", AwSdHostState, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), }; static void allwinner_sdhost_init(Object *obj) @@ -889,24 +888,26 @@ static void allwinner_sdhost_reset(DeviceState *dev) } } -static void allwinner_sdhost_bus_class_init(ObjectClass *klass, void *data) +static void allwinner_sdhost_bus_class_init(ObjectClass *klass, + const void *data) { SDBusClass *sbc = SD_BUS_CLASS(klass); sbc->set_inserted = allwinner_sdhost_set_inserted; } -static void allwinner_sdhost_class_init(ObjectClass *klass, void *data) +static void allwinner_sdhost_class_init(ObjectClass *klass, const 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); } -static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass, void *data) +static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass, + const void *data) { AwSdHostClass *sc = AW_SDHOST_CLASS(klass); sc->max_desc_size = 8 * KiB; @@ -914,7 +915,8 @@ static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass, void *data) sc->can_calibrate = false; } -static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, void *data) +static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, + const void *data) { AwSdHostClass *sc = AW_SDHOST_CLASS(klass); sc->max_desc_size = 64 * KiB; @@ -923,7 +925,7 @@ static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, void *data) } static void allwinner_sdhost_sun50i_a64_class_init(ObjectClass *klass, - void *data) + const void *data) { AwSdHostClass *sc = AW_SDHOST_CLASS(klass); sc->max_desc_size = 64 * KiB; @@ -932,7 +934,7 @@ static void allwinner_sdhost_sun50i_a64_class_init(ObjectClass *klass, } static void allwinner_sdhost_sun50i_a64_emmc_class_init(ObjectClass *klass, - void *data) + const void *data) { AwSdHostClass *sc = AW_SDHOST_CLASS(klass); sc->max_desc_size = 8 * KiB; diff --git a/hw/sd/aspeed_sdhci.c b/hw/sd/aspeed_sdhci.c index be8cafd65f8..fc38ad39ce1 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,28 @@ 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) { @@ -128,6 +148,7 @@ static void aspeed_sdhci_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); AspeedSDHCIState *sdhci = ASPEED_SDHCI(dev); + AspeedSDHCIClass *asc = ASPEED_SDHCI_GET_CLASS(sdhci); /* Create input irqs for the slots */ qdev_init_gpio_in_named_with_opaque(DEVICE(sbd), aspeed_sdhci_set_irq, @@ -147,7 +168,7 @@ static void aspeed_sdhci_realize(DeviceState *dev, Error **errp) } if (!object_property_set_uint(sdhci_slot, "capareg", - ASPEED_SDHCI_CAPABILITIES, errp)) { + asc->capareg, errp)) { return; } @@ -177,37 +198,91 @@ 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(), }, }; -static Property aspeed_sdhci_properties[] = { +static const Property aspeed_sdhci_properties[] = { DEFINE_PROP_UINT8("num-slots", AspeedSDHCIState, num_slots, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void aspeed_sdhci_class_init(ObjectClass *classp, void *data) +static void aspeed_sdhci_class_init(ObjectClass *classp, const 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 void aspeed_2400_sdhci_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass); + + dc->desc = "ASPEED 2400 SDHCI Controller"; + asc->capareg = 0x0000000001e80080; +} + +static void aspeed_2500_sdhci_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass); + + dc->desc = "ASPEED 2500 SDHCI Controller"; + asc->capareg = 0x0000000001e80080; +} -static void aspeed_sdhci_register_types(void) +static void aspeed_2600_sdhci_class_init(ObjectClass *klass, const void *data) { - type_register_static(&aspeed_sdhci_info); + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass); + + dc->desc = "ASPEED 2600 SDHCI Controller"; + asc->capareg = 0x0000000701f80080; +} + +static void aspeed_2700_sdhci_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass); + + dc->desc = "ASPEED 2700 SDHCI Controller"; + asc->capareg = 0x0000000719f80080; } -type_init(aspeed_sdhci_register_types) +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, + .class_size = sizeof(AspeedSDHCIClass), + .abstract = true, + }, + { + .name = TYPE_ASPEED_2400_SDHCI, + .parent = TYPE_ASPEED_SDHCI, + .class_init = aspeed_2400_sdhci_class_init, + }, + { + .name = TYPE_ASPEED_2500_SDHCI, + .parent = TYPE_ASPEED_SDHCI, + .class_init = aspeed_2500_sdhci_class_init, + }, + { + .name = TYPE_ASPEED_2600_SDHCI, + .parent = TYPE_ASPEED_SDHCI, + .class_init = aspeed_2600_sdhci_class_init, + }, + { + .name = TYPE_ASPEED_2700_SDHCI, + .parent = TYPE_ASPEED_SDHCI, + .class_init = aspeed_2700_sdhci_class_init, + }, +}; + +DEFINE_TYPES(aspeed_sdhci_types) diff --git a/hw/sd/bcm2835_sdhost.c b/hw/sd/bcm2835_sdhost.c index 9431c359145..29debdf59e4 100644 --- a/hw/sd/bcm2835_sdhost.c +++ b/hw/sd/bcm2835_sdhost.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/module.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "hw/irq.h" #include "hw/sd/bcm2835_sdhost.h" #include "migration/vmstate.h" @@ -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), @@ -428,32 +428,27 @@ static void bcm2835_sdhost_reset(DeviceState *dev) s->fifo_len = 0; } -static void bcm2835_sdhost_class_init(ObjectClass *klass, void *data) +static void bcm2835_sdhost_class_init(ObjectClass *klass, const 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 75db34befe5..d576855a1a8 100644 --- a/hw/sd/cadence_sdhci.c +++ b/hw/sd/cadence_sdhci.c @@ -159,33 +159,30 @@ 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(), }, }; -static void cadence_sdhci_class_init(ObjectClass *classp, void *data) +static void cadence_sdhci_class_init(ObjectClass *classp, const void *data) { DeviceClass *dc = DEVICE_CLASS(classp); 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 30ee62c5106..4b30218b520 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 abfac9e4618..b43d45bc564 100644 --- a/hw/sd/meson.build +++ b/hw/sd/meson.build @@ -1,11 +1,10 @@ system_ss.add(when: 'CONFIG_PL181', if_true: files('pl181.c')) -system_ss.add(when: 'CONFIG_SD', if_true: files('sd.c', 'core.c', 'sdmmc-internal.c')) +system_ss.add(when: 'CONFIG_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')) system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_mmc.c')) -system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_mmci.c')) system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_sdhost.c')) system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_sdhci.c')) system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sdhost.c')) diff --git a/hw/sd/npcm7xx_sdhci.c b/hw/sd/npcm7xx_sdhci.c index b2f5b4a5425..0233d7bd4d3 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,19 +143,19 @@ 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(), }, }; -static void npcm7xx_sdhci_class_init(ObjectClass *classp, void *data) +static void npcm7xx_sdhci_class_init(ObjectClass *classp, const void *data) { DeviceClass *dc = DEVICE_CLASS(classp); 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 edd3cf2a1eb..b7648d41cc5 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -21,17 +21,22 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qapi/error.h" #include "hw/irq.h" +#include "hw/sysbus.h" #include "hw/arm/omap.h" -#include "hw/sd/sdcard_legacy.h" +#include "hw/sd/sd.h" + +typedef struct OMAPMMCState { + SysBusDevice parent_obj; + + SDBus sdbus; -struct omap_mmc_s { qemu_irq irq; - qemu_irq *dma; - qemu_irq coverswitch; + qemu_irq dma_tx_gpio; + qemu_irq dma_rx_gpio; MemoryRegion iomem; omap_clk clk; - SDState *card; uint16_t last_cmd; uint16_t sdio; uint16_t rsp[8]; @@ -64,16 +69,15 @@ struct omap_mmc_s { int cdet_wakeup; int cdet_enable; - int cdet_state; qemu_irq cdet; -}; +} OMAPMMCState; -static void omap_mmc_interrupts_update(struct omap_mmc_s *s) +static void omap_mmc_interrupts_update(OMAPMMCState *s) { qemu_set_irq(s->irq, !!(s->status & s->mask)); } -static void omap_mmc_fifolevel_update(struct omap_mmc_s *host) +static void omap_mmc_fifolevel_update(OMAPMMCState *host) { if (!host->transfer && !host->fifo_len) { host->status &= 0xf3ff; @@ -83,37 +87,47 @@ static void omap_mmc_fifolevel_update(struct omap_mmc_s *host) if (host->fifo_len > host->af_level && host->ddir) { if (host->rx_dma) { host->status &= 0xfbff; - qemu_irq_raise(host->dma[1]); + qemu_irq_raise(host->dma_rx_gpio); } else host->status |= 0x0400; } else { host->status &= 0xfbff; - qemu_irq_lower(host->dma[1]); + qemu_irq_lower(host->dma_rx_gpio); } if (host->fifo_len < host->ae_level && !host->ddir) { if (host->tx_dma) { host->status &= 0xf7ff; - qemu_irq_raise(host->dma[0]); + qemu_irq_raise(host->dma_tx_gpio); } else host->status |= 0x0800; } else { - qemu_irq_lower(host->dma[0]); + qemu_irq_lower(host->dma_tx_gpio); host->status &= 0xf7ff; } } +/* These must match the encoding of the MMC_CMD Response field */ typedef enum { - sd_nore = 0, /* no response */ - sd_r1, /* normal response command */ - sd_r2, /* CID, CSD registers */ - sd_r3, /* OCR register */ - sd_r6 = 6, /* Published RCA response */ + sd_nore = 0, /* no response */ + sd_r1, /* normal response command */ + sd_r2, /* CID, CSD registers */ + sd_r3, /* OCR register */ + sd_r6 = 6, /* Published RCA response */ sd_r1b = -1, } sd_rsp_type_t; -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) +/* 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(OMAPMMCState *host, int cmd, int dir, + MMCCmdType type, int busy, + sd_rsp_type_t resptype, int init) { uint32_t rspstatus, mask; int rsplen, timeout; @@ -128,7 +142,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; @@ -143,7 +157,7 @@ static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir, request.arg = host->arg; request.crc = 0; /* FIXME */ - rsplen = sd_do_command(host->card, &request, response); + rsplen = sdbus_do_command(&host->sdbus, &request, response); /* TODO: validate CRCs */ switch (resptype) { @@ -215,12 +229,12 @@ static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir, if (timeout) host->status |= 0x0080; else if (cmd == 12) - host->status |= 0x0005; /* Makes it more real */ + host->status |= 0x0005; /* Makes it more real */ else host->status |= 0x0001; } -static void omap_mmc_transfer(struct omap_mmc_s *host) +static void omap_mmc_transfer(OMAPMMCState *host) { uint8_t value; @@ -232,10 +246,10 @@ static void omap_mmc_transfer(struct omap_mmc_s *host) if (host->fifo_len > host->af_level) break; - value = sd_read_byte(host->card); + value = sdbus_read_byte(&host->sdbus); host->fifo[(host->fifo_start + host->fifo_len) & 31] = value; if (-- host->blen_counter) { - value = sd_read_byte(host->card); + value = sdbus_read_byte(&host->sdbus); host->fifo[(host->fifo_start + host->fifo_len) & 31] |= value << 8; host->blen_counter --; @@ -247,10 +261,10 @@ static void omap_mmc_transfer(struct omap_mmc_s *host) break; value = host->fifo[host->fifo_start] & 0xff; - sd_write_byte(host->card, value); + sdbus_write_byte(&host->sdbus, value); if (-- host->blen_counter) { value = host->fifo[host->fifo_start] >> 8; - sd_write_byte(host->card, value); + sdbus_write_byte(&host->sdbus, value); host->blen_counter --; } @@ -275,19 +289,19 @@ static void omap_mmc_transfer(struct omap_mmc_s *host) static void omap_mmc_update(void *opaque) { - struct omap_mmc_s *s = opaque; + OMAPMMCState *s = opaque; omap_mmc_transfer(s); omap_mmc_fifolevel_update(s); omap_mmc_interrupts_update(s); } -static void omap_mmc_pseudo_reset(struct omap_mmc_s *host) +static void omap_mmc_pseudo_reset(OMAPMMCState *host) { host->status = 0; host->fifo_len = 0; } -void omap_mmc_reset(struct omap_mmc_s *host) +static void omap_mmc_reset(OMAPMMCState *host) { host->last_cmd = 0; memset(host->rsp, 0, sizeof(host->rsp)); @@ -309,54 +323,47 @@ void omap_mmc_reset(struct omap_mmc_s *host) host->transfer = 0; host->cdet_wakeup = 0; host->cdet_enable = 0; - qemu_set_irq(host->coverswitch, host->cdet_state); host->clkdiv = 0; omap_mmc_pseudo_reset(host); - - /* Since we're still using the legacy SD API the card is not plugged - * into any bus, and we must reset it manually. When omap_mmc is - * QOMified this must move into the QOM reset function. - */ - device_cold_reset(DEVICE(host->card)); } static uint64_t omap_mmc_read(void *opaque, hwaddr offset, unsigned size) { uint16_t i; - struct omap_mmc_s *s = opaque; + OMAPMMCState *s = opaque; if (size != 2) { return omap_badwidth_read16(opaque, offset); } switch (offset) { - case 0x00: /* MMC_CMD */ + case 0x00: /* MMC_CMD */ return s->last_cmd; - case 0x04: /* MMC_ARGL */ + case 0x04: /* MMC_ARGL */ return s->arg & 0x0000ffff; - case 0x08: /* MMC_ARGH */ + case 0x08: /* MMC_ARGH */ return s->arg >> 16; - case 0x0c: /* MMC_CON */ + case 0x0c: /* MMC_CON */ return (s->dw << 15) | (s->mode << 12) | (s->enable << 11) | (s->be << 10) | s->clkdiv; - case 0x10: /* MMC_STAT */ + case 0x10: /* MMC_STAT */ return s->status; - case 0x14: /* MMC_IE */ + case 0x14: /* MMC_IE */ return s->mask; - case 0x18: /* MMC_CTO */ + case 0x18: /* MMC_CTO */ return s->cto; - case 0x1c: /* MMC_DTO */ + case 0x1c: /* MMC_DTO */ return s->dto; - case 0x20: /* MMC_DATA */ + case 0x20: /* MMC_DATA */ /* TODO: support 8-bit access */ i = s->fifo[s->fifo_start]; if (s->fifo_len == 0) { @@ -371,42 +378,42 @@ static uint64_t omap_mmc_read(void *opaque, hwaddr offset, unsigned size) omap_mmc_interrupts_update(s); return i; - case 0x24: /* MMC_BLEN */ + case 0x24: /* MMC_BLEN */ return s->blen_counter; - case 0x28: /* MMC_NBLK */ + case 0x28: /* MMC_NBLK */ return s->nblk_counter; - case 0x2c: /* MMC_BUF */ + case 0x2c: /* MMC_BUF */ return (s->rx_dma << 15) | (s->af_level << 8) | (s->tx_dma << 7) | s->ae_level; - case 0x30: /* MMC_SPI */ + case 0x30: /* MMC_SPI */ return 0x0000; - case 0x34: /* MMC_SDIO */ + case 0x34: /* MMC_SDIO */ return (s->cdet_wakeup << 2) | (s->cdet_enable) | s->sdio; - case 0x38: /* MMC_SYST */ + case 0x38: /* MMC_SYST */ return 0x0000; - case 0x3c: /* MMC_REV */ + case 0x3c: /* MMC_REV */ return s->rev; - case 0x40: /* MMC_RSP0 */ - case 0x44: /* MMC_RSP1 */ - case 0x48: /* MMC_RSP2 */ - case 0x4c: /* MMC_RSP3 */ - case 0x50: /* MMC_RSP4 */ - case 0x54: /* MMC_RSP5 */ - case 0x58: /* MMC_RSP6 */ - case 0x5c: /* MMC_RSP7 */ + case 0x40: /* MMC_RSP0 */ + case 0x44: /* MMC_RSP1 */ + case 0x48: /* MMC_RSP2 */ + case 0x4c: /* MMC_RSP3 */ + case 0x50: /* MMC_RSP4 */ + case 0x54: /* MMC_RSP5 */ + case 0x58: /* MMC_RSP6 */ + case 0x5c: /* MMC_RSP7 */ return s->rsp[(offset - 0x40) >> 2]; /* OMAP2-specific */ - case 0x60: /* MMC_IOSR */ - case 0x64: /* MMC_SYSC */ + case 0x60: /* MMC_IOSR */ + case 0x64: /* MMC_SYSC */ return 0; - case 0x68: /* MMC_SYSS */ - return 1; /* RSTD */ + case 0x68: /* MMC_SYSS */ + return 1; /* RSTD */ } OMAP_BAD_REG(offset); @@ -417,7 +424,7 @@ static void omap_mmc_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { int i; - struct omap_mmc_s *s = opaque; + OMAPMMCState *s = opaque; if (size != 2) { omap_badwidth_write16(opaque, offset, value); @@ -425,7 +432,7 @@ static void omap_mmc_write(void *opaque, hwaddr offset, } switch (offset) { - case 0x00: /* MMC_CMD */ + case 0x00: /* MMC_CMD */ if (!s->enable) break; @@ -433,24 +440,24 @@ 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; - case 0x04: /* MMC_ARGL */ + case 0x04: /* MMC_ARGL */ s->arg &= 0xffff0000; s->arg |= 0x0000ffff & value; break; - case 0x08: /* MMC_ARGH */ + case 0x08: /* MMC_ARGH */ s->arg &= 0x0000ffff; s->arg |= value << 16; break; - case 0x0c: /* MMC_CON */ + case 0x0c: /* MMC_CON */ s->dw = (value >> 15) & 1; s->mode = (value >> 12) & 3; s->enable = (value >> 11) & 1; @@ -470,27 +477,27 @@ static void omap_mmc_write(void *opaque, hwaddr offset, omap_mmc_pseudo_reset(s); break; - case 0x10: /* MMC_STAT */ + case 0x10: /* MMC_STAT */ s->status &= ~value; omap_mmc_interrupts_update(s); break; - case 0x14: /* MMC_IE */ + case 0x14: /* MMC_IE */ s->mask = value & 0x7fff; omap_mmc_interrupts_update(s); break; - case 0x18: /* MMC_CTO */ + case 0x18: /* MMC_CTO */ s->cto = value & 0xff; if (s->cto > 0xfd && s->rev <= 1) printf("MMC: CTO of 0xff and 0xfe cannot be used!\n"); break; - case 0x1c: /* MMC_DTO */ + case 0x1c: /* MMC_DTO */ s->dto = value & 0xffff; break; - case 0x20: /* MMC_DATA */ + case 0x20: /* MMC_DATA */ /* TODO: support 8-bit access */ if (s->fifo_len == 32) break; @@ -501,18 +508,18 @@ static void omap_mmc_write(void *opaque, hwaddr offset, omap_mmc_interrupts_update(s); break; - case 0x24: /* MMC_BLEN */ + case 0x24: /* MMC_BLEN */ s->blen = (value & 0x07ff) + 1; s->blen_counter = s->blen; break; - case 0x28: /* MMC_NBLK */ + case 0x28: /* MMC_NBLK */ s->nblk = (value & 0x07ff) + 1; s->nblk_counter = s->nblk; s->blen_counter = s->blen; break; - case 0x2c: /* MMC_BUF */ + case 0x2c: /* MMC_BUF */ s->rx_dma = (value >> 15) & 1; s->af_level = (value >> 8) & 0x1f; s->tx_dma = (value >> 7) & 1; @@ -527,38 +534,38 @@ static void omap_mmc_write(void *opaque, hwaddr offset, break; /* SPI, SDIO and TEST modes unimplemented */ - case 0x30: /* MMC_SPI (OMAP1 only) */ + case 0x30: /* MMC_SPI (OMAP1 only) */ break; - case 0x34: /* MMC_SDIO */ + case 0x34: /* MMC_SDIO */ s->sdio = value & (s->rev >= 2 ? 0xfbf3 : 0x2020); s->cdet_wakeup = (value >> 9) & 1; s->cdet_enable = (value >> 2) & 1; break; - case 0x38: /* MMC_SYST */ + case 0x38: /* MMC_SYST */ break; - case 0x3c: /* MMC_REV */ - case 0x40: /* MMC_RSP0 */ - case 0x44: /* MMC_RSP1 */ - case 0x48: /* MMC_RSP2 */ - case 0x4c: /* MMC_RSP3 */ - case 0x50: /* MMC_RSP4 */ - case 0x54: /* MMC_RSP5 */ - case 0x58: /* MMC_RSP6 */ - case 0x5c: /* MMC_RSP7 */ + case 0x3c: /* MMC_REV */ + case 0x40: /* MMC_RSP0 */ + case 0x44: /* MMC_RSP1 */ + case 0x48: /* MMC_RSP2 */ + case 0x4c: /* MMC_RSP3 */ + case 0x50: /* MMC_RSP4 */ + case 0x54: /* MMC_RSP5 */ + case 0x58: /* MMC_RSP6 */ + case 0x5c: /* MMC_RSP7 */ OMAP_RO_REG(offset); break; /* OMAP2-specific */ - case 0x60: /* MMC_IOSR */ + case 0x60: /* MMC_IOSR */ if (value & 0xf) printf("MMC: SDIO bits used!\n"); break; - case 0x64: /* MMC_SYSC */ - if (value & (1 << 2)) /* SRTS */ + case 0x64: /* MMC_SYSC */ + if (value & (1 << 2)) /* SRTS */ omap_mmc_reset(s); break; - case 0x68: /* MMC_SYSS */ + case 0x68: /* MMC_SYSS */ OMAP_RO_REG(offset); break; @@ -573,92 +580,56 @@ static const MemoryRegionOps omap_mmc_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void omap_mmc_cover_cb(void *opaque, int line, int level) +void omap_mmc_set_clk(DeviceState *dev, omap_clk clk) { - struct omap_mmc_s *host = opaque; + OMAPMMCState *s = OMAP_MMC(dev); - 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; - } + s->clk = clk; } -struct omap_mmc_s *omap_mmc_init(hwaddr base, - MemoryRegion *sysmem, - BlockBackend *blk, - qemu_irq irq, qemu_irq dma[], omap_clk clk) +static void omap_mmc_reset_hold(Object *obj, ResetType type) { - struct omap_mmc_s *s = g_new0(struct omap_mmc_s, 1); - - s->irq = irq; - s->dma = dma; - s->clk = clk; - s->lines = 1; /* TODO: needs to be settable per-board */ - s->rev = 1; - - memory_region_init_io(&s->iomem, NULL, &omap_mmc_ops, s, "omap.mmc", 0x800); - memory_region_add_subregion(sysmem, base, &s->iomem); - - /* Instantiate the storage */ - s->card = sd_init(blk, false); - if (s->card == NULL) { - exit(1); - } + OMAPMMCState *s = OMAP_MMC(obj); omap_mmc_reset(s); - - 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) +static void omap_mmc_initfn(Object *obj) { - 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); - } + OMAPMMCState *s = OMAP_MMC(obj); - s->cdet = qemu_allocate_irq(omap_mmc_cover_cb, s, 0); - sd_set_cb(s->card, NULL, s->cdet); + /* In theory these could be settable per-board */ + s->lines = 1; + s->rev = 1; - omap_mmc_reset(s); + memory_region_init_io(&s->iomem, obj, &omap_mmc_ops, s, "omap.mmc", 0x800); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); - return s; + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + qdev_init_gpio_out_named(DEVICE(obj), &s->dma_tx_gpio, "dma-tx", 1); + qdev_init_gpio_out_named(DEVICE(obj), &s->dma_rx_gpio, "dma-rx", 1); + + qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, DEVICE(obj), "sd-bus"); } -void omap_mmc_handlers(struct omap_mmc_s *s, qemu_irq ro, qemu_irq cover) +static void omap_mmc_class_init(ObjectClass *oc, const void *data) { - 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); + ResettableClass *rc = RESETTABLE_CLASS(oc); + + rc->phases.hold = omap_mmc_reset_hold; } -void omap_mmc_enable(struct omap_mmc_s *s, int enable) +static const TypeInfo omap_mmc_info = { + .name = TYPE_OMAP_MMC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(OMAPMMCState), + .instance_init = omap_mmc_initfn, + .class_init = omap_mmc_class_init, +}; + +static void omap_mmc_register_types(void) { - sd_enable(s->card, enable); + type_register_static(&omap_mmc_info); } + +type_init(omap_mmc_register_types) diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index 5e554bd4676..b8fc9f86f13 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "hw/irq.h" @@ -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), @@ -509,25 +509,17 @@ static void pl181_init(Object *obj) qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_PL181_BUS, dev, "sd-bus"); } -static void pl181_class_init(ObjectClass *klass, void *data) +static void pl181_class_init(ObjectClass *klass, const 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) +static void pl181_bus_class_init(ObjectClass *klass, const 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 124fbf8bbd4..00000000000 --- 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 4823befdef8..c275fdda2d0 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é @@ -35,9 +37,8 @@ #include "qemu/cutils.h" #include "hw/irq.h" #include "hw/registerfields.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "hw/sd/sd.h" -#include "hw/sd/sdcard_legacy.h" #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/bitmap.h" @@ -46,6 +47,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 +70,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,33 +85,40 @@ 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; - sd_cmd_handler cmd[SDMMC_CMD_MAX]; - sd_cmd_handler acmd[SDMMC_CMD_MAX]; + 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; - /* If true, created by sd_init() for a non-qdevified caller */ - /* TODO purge them with fire */ - bool me_no_qdev_me_kill_mammoth_with_rocks; - /* SD Memory Card Registers */ uint32_t ocr; uint8_t scr[8]; @@ -110,11 +127,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; + uint8_t boot_config; + + const SDProto *proto; /* Runtime changeables */ @@ -133,36 +161,35 @@ 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; - bool enable; uint8_t dat_lines; bool cmd_line; }; static void sd_realize(DeviceState *dev, Error **errp); -static const struct SDProto *sd_proto(SDState *sd) -{ - SDCardClass *sc = SD_CARD_GET_CLASS(sd); - - return sc->proto; -} - static const SDProto sd_proto_spi; +static const SDProto sd_proto_emmc; static bool sd_is_spi(SDState *sd) { - return sd_proto(sd) == &sd_proto_spi; + 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) @@ -178,6 +205,17 @@ static const char *sd_version_str(enum SDPhySpecificationVersion 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[] = { @@ -187,13 +225,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]; } @@ -219,14 +263,40 @@ 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; + return sd->dat_lines; } static bool sd_get_cmd_line(SDState *sd) { - return sd->enable ? sd->cmd_line : false; + return sd->cmd_line; } static void sd_set_voltage(SDState *sd, uint16_t millivolts) @@ -267,27 +337,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; @@ -304,6 +353,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) @@ -353,6 +404,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 */ @@ -375,6 +428,8 @@ static void sd_set_scr(SDState *sd) sd->scr[7] = 0x00; } +/* Card IDentification register */ + #define MID 0xaa #define OID "XY" #define PNM "QEMU!" @@ -393,16 +448,32 @@ static void sd_set_cid(SDState *sd) 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; + 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; } +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 */ @@ -414,6 +485,79 @@ static const uint8_t sd_csd_rw_mask[16] = { 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; @@ -462,9 +606,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; @@ -474,14 +616,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) +{ + 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) { - sd->rca += 0x4567; + 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) @@ -532,7 +697,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) @@ -540,6 +705,21 @@ 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]; @@ -579,6 +759,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); @@ -586,7 +814,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; @@ -596,17 +825,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); @@ -650,17 +884,10 @@ static void sd_cardchange(void *opaque, bool load, Error **errp) trace_sdcard_ejected(); } - if (sd->me_no_qdev_me_kill_mammoth_with_rocks) { - qemu_set_irq(sd->inserted_cb, inserted); - if (inserted) { - qemu_set_irq(sd->readonly_cb, readonly); - } - } else { - sdbus = SD_BUS(qdev_get_parent_bus(dev)); - sdbus_set_inserted(sdbus, inserted); - if (inserted) { - sdbus_set_readonly(sdbus, readonly); - } + sdbus = SD_BUS(qdev_get_parent_bus(dev)); + sdbus_set_inserted(sdbus, inserted); + if (inserted) { + sdbus_set_readonly(sdbus, readonly); } } @@ -681,13 +908,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; @@ -706,7 +951,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), @@ -730,60 +975,20 @@ static const VMStateDescription sd_vmstate = { VMSTATE_UINT32(data_offset, SDState), VMSTATE_UINT8_ARRAY(data, SDState, 512), VMSTATE_UNUSED_V(1, 512), - VMSTATE_BOOL(enable, SDState), + VMSTATE_UNUSED(1), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &sd_ocr_vmstate, + &emmc_extcsd_vmstate, NULL }, }; -/* Legacy initialization function for use by non-qdevified callers */ -SDState *sd_init(BlockBackend *blk, bool is_spi) -{ - Object *obj; - DeviceState *dev; - SDState *sd; - Error *err = NULL; - - 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; - } - - /* - * Realizing the device properly would put it into the QOM - * composition tree even though it is not plugged into an - * appropriate bus. That's a no-no. Hide the device from - * QOM/qdev, and call its qdev realize callback directly. - */ - object_ref(obj); - object_unparent(obj); - sd_realize(dev, &err); - if (err) { - error_reportf_err(err, "sd_init failed: "); - return NULL; - } - - sd = SD_CARD(dev); - sd->me_no_qdev_me_kill_mammoth_with_rocks = true; - return sd; -} - -void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert) -{ - sd->readonly_cb = readonly; - sd->inserted_cb = insert; - qemu_set_irq(readonly, sd->blk ? !blk_is_writable(sd->blk) : 0); - qemu_set_irq(insert, sd->blk ? blk_is_inserted(sd->blk) : 0); -} - 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"); } @@ -792,16 +997,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; @@ -822,8 +1023,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; } @@ -850,7 +1051,7 @@ static void sd_erase(SDState *sd) continue; } } - BLK_WRITE_BLOCK(erase_addr, erase_len); + sd_blk_write(sd, erase_addr, erase_len); } } @@ -878,6 +1079,47 @@ 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; @@ -1001,7 +1243,16 @@ static bool address_in_range(SDState *sd, const char *desc, static sd_rsp_type_t sd_invalid_state_for_cmd(SDState *sd, SDRequest req) { qemu_log_mask(LOG_GUEST_ERROR, "%s: CMD%i in a wrong state: %s (spec %s)\n", - sd_proto(sd)->name, req.cmd, sd_state_name(sd->state), + sd->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; @@ -1010,7 +1261,7 @@ static sd_rsp_type_t sd_invalid_state_for_cmd(SDState *sd, SDRequest req) static sd_rsp_type_t sd_cmd_illegal(SDState *sd, SDRequest req) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown CMD%i for spec %s\n", - sd_proto(sd)->name, req.cmd, + sd->proto->name, req.cmd, sd_version_str(sd->spec_version)); return sd_illegal; @@ -1020,13 +1271,68 @@ static sd_rsp_type_t sd_cmd_illegal(SDState *sd, SDRequest req) static sd_rsp_type_t sd_cmd_unimplemented(SDState *sd, SDRequest req) { qemu_log_mask(LOG_UNIMP, "%s: CMD%i not implemented\n", - sd_proto(sd)->name, req.cmd); + 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)); @@ -1035,31 +1341,37 @@ static sd_rsp_type_t sd_cmd_GO_IDLE_STATE(SDState *sd, SDRequest req) return sd_is_spi(sd) ? sd_r1 : sd_r0; } -static sd_rsp_type_t sd_cmd_SEND_OP_CMD(SDState *sd, SDRequest req) +/* 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) { - if (sd->state != sd_ready_state) { + 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); } - - sd->state = sd_identification_state; - - return sd_r2_i; } +/* 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; - sd_set_rca(sd); + qemu_guest_getrandom_nofail(&random_rca, sizeof(random_rca)); + sd_set_rca(sd, random_rca); return sd_r6; default: @@ -1067,397 +1379,659 @@ static sd_rsp_type_t sd_cmd_SEND_RELATIVE_ADDR(SDState *sd, SDRequest req) } } -static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req) +static sd_rsp_type_t emmc_cmd_SET_RELATIVE_ADDR(SDState *sd, SDRequest req) { - if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { - return sd_cmd_illegal(sd, req); - } - - if (sd->state != sd_transfer_state) { - return sd_invalid_state_for_cmd(sd, req); - } - - sd->state = sd_sendingdata_state; - sd->data_offset = 0; - + 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); + } } -static sd_rsp_type_t sd_cmd_SET_BLOCK_COUNT(SDState *sd, SDRequest req) +/* CMD5 */ +static sd_rsp_type_t emmc_cmd_sleep_awake(SDState *sd, SDRequest req) { - if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { - return sd_cmd_illegal(sd, req); - } + bool do_sleep = extract32(req.arg, 15, 1); - if (sd->state != sd_transfer_state) { - return sd_invalid_state_for_cmd(sd, req); + switch (sd->state) { + case sd_sleep_state: + if (!do_sleep) { + /* Awake */ + sd->state = sd_standby_state; } + return sd_r1b; - sd->multi_blk_cnt = req.arg; + case sd_standby_state: + if (do_sleep) { + sd->state = sd_sleep_state; + } + return sd_r1b; - return sd_r1; + default: + return sd_invalid_state_for_cmd(sd, req); + } } -static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) +/* CMD6 */ +static sd_rsp_type_t sd_cmd_SWITCH_FUNCTION(SDState *sd, SDRequest req) { - uint32_t rca = 0x0000; - uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg; - - /* 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(sd)->name, - sd_cmd_name(req.cmd), req.cmd, - req.arg, sd_state_name(sd->state)); + if (sd->mode != sd_data_transfer_mode) { + return sd_invalid_mode_for_cmd(sd, req); } - - /* 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; + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); } - /* 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; - } + sd_function_switch(sd, req.arg); + return sd_cmd_to_sendingdata(sd, req, 0, NULL, 64); +} - if (sd_cmd_class[req.cmd] == 6 && FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) { - /* Only Standard Capacity cards support class 6 commands */ - return sd_illegal; +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); } +} - if (sd_proto(sd)->cmd[req.cmd]) { - return sd_proto(sd)->cmd[req.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 (req.cmd) { - /* Basic commands (Class 0 and Class 1) */ - case 4: /* CMD4: SEND_DSR */ - switch (sd->state) { - case sd_standby_state: - break; + switch (sd->state) { + case sd_standby_state: + if (!same_rca) { + return sd_r0; + } + sd->state = sd_transfer_state; + return sd_r1b; - default: + case sd_transfer_state: + case sd_sendingdata_state: + if (same_rca) { break; } - break; + sd->state = sd_standby_state; + return sd_r1b; - 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; + case sd_disconnect_state: + if (!same_rca) { + return sd_r0; + } + sd->state = sd_programming_state; + return sd_r1b; - default: + 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); +} - case 7: /* CMD7: SELECT/DESELECT_CARD */ - switch (sd->state) { - case sd_standby_state: - if (sd->rca != rca) - return sd_r0; +/* 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; - sd->state = sd_transfer_state; - return sd_r1b; + /* 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; + } - case sd_transfer_state: - case sd_sendingdata_state: - if (sd->rca == rca) - break; + /* Accept. */ + sd->vhs = req.arg; + return sd_r7; +} - sd->state = sd_standby_state; - return sd_r1b; +/* 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); + } - case sd_disconnect_state: - if (sd->rca != rca) - return sd_r0; + return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req), + sd->ext_csd, sizeof(sd->ext_csd)); +} - sd->state = sd_programming_state; - return sd_r1b; +/* 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); +} - case sd_programming_state: - if (sd->rca == rca) - break; +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); + } - sd->state = sd_disconnect_state; - return sd_r1b; + return sd_req_rca_same(sd, req) ? sd_r2_s : sd_r0; +} - default: - break; - } - break; +/* 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); +} - 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; +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); + } - /* 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; - } + return sd_req_rca_same(sd, req) ? sd_r2_i : sd_r0; +} - /* Accept. */ - sd->vhs = req.arg; - return sd_r7; +/* 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); + } +} - case 9: /* CMD9: SEND_CSD */ - switch (sd->state) { - case sd_standby_state: - if (sd->rca != rca) - return sd_r0; +/* 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); + } - return sd_r2_s; + 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); + } - case sd_transfer_state: - if (!sd_is_spi(sd)) { - break; - } - sd->state = sd_sendingdata_state; - memcpy(sd->data, sd->csd, 16); - sd->data_start = addr; - sd->data_offset = 0; - return sd_r1; + if (sd_is_spi(sd)) { + return sd_r2_s; + } - default: - break; - } + 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; + } - case 10: /* CMD10: SEND_CID */ - switch (sd->state) { - case sd_standby_state: - if (sd->rca != rca) - return sd_r0; + return sd_r0; +} - return sd_r2_i; +/* 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; + } - case sd_transfer_state: - if (!sd_is_spi(sd)) { - break; - } - sd->state = sd_sendingdata_state; - memcpy(sd->data, sd->cid, 16); - sd->data_start = addr; - sd->data_offset = 0; - return sd_r1; + return sd_r1; +} - default: - break; - } - break; +/* CMD17 */ +static sd_rsp_type_t sd_cmd_READ_SINGLE_BLOCK(SDState *sd, SDRequest req) +{ + uint64_t addr; - case 12: /* CMD12: STOP_TRANSMISSION */ - switch (sd->state) { - case sd_sendingdata_state: - sd->state = sd_transfer_state; - return sd_r1b; + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } - case sd_receivingdata_state: - sd->state = sd_programming_state; - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - return sd_r1b; + addr = sd_req_get_address(sd, req); + if (!address_in_range(sd, "READ_SINGLE_BLOCK", addr, sd->blk_len)) { + return sd_r1; + } - default: - break; - } - break; + sd_blk_read(sd, addr, sd->blk_len); + return sd_cmd_to_sendingdata(sd, req, addr, NULL, sd->blk_len); +} - case 13: /* CMD13: SEND_STATUS */ - switch (sd->mode) { - case sd_data_transfer_mode: - if (!sd_is_spi(sd) && sd->rca != rca) { - return sd_r0; - } +/* 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_r1; + return sd_cmd_to_sendingdata(sd, req, 0, + sd_tuning_block_pattern4, + sizeof(sd_tuning_block_pattern4)); +} - default: - break; - } - break; +/* 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); + } - case 15: /* CMD15: GO_INACTIVE_STATE */ - switch (sd->mode) { - case sd_data_transfer_mode: - if (sd->rca != rca) - return sd_r0; + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } - sd->state = sd_inactive_state; - return sd_r0; + 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); - default: - break; - } - break; + return sd_r1; +} - /* Block read commands (Class 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; - } +/* CMD24 */ +static sd_rsp_type_t sd_cmd_WRITE_SINGLE_BLOCK(SDState *sd, SDRequest req) +{ + uint64_t addr; - return sd_r1; + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } - default: - break; + 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; } - break; + } + if (sd->csd[14] & 0x30) { + sd->card_status |= WP_VIOLATION; + } - case 17: /* CMD17: READ_SINGLE_BLOCK */ - case 18: /* CMD18: READ_MULTIPLE_BLOCK */ - switch (sd->state) { - case sd_transfer_state: + sd->blk_written = 0; + return sd_cmd_to_receivingdata(sd, req, addr, sd->blk_len); +} - if (!address_in_range(sd, "READ_BLOCK", addr, sd->blk_len)) { - return sd_r1; - } +/* 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)); +} - sd->state = sd_sendingdata_state; - sd->data_start = addr; - sd->data_offset = 0; - return sd_r1; +/* 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)); +} - default: - break; - } - break; +static sd_rsp_type_t sd_cmd_SET_CLR_WRITE_PROT(SDState *sd, SDRequest req, + bool is_write) +{ + uint64_t addr; - /* Block write commands (Class 4) */ - case 24: /* CMD24: WRITE_SINGLE_BLOCK */ - case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ - switch (sd->state) { - case sd_transfer_state: + if (sd->size > SDSC_MAX_CAPACITY) { + return sd_illegal; + } - if (!address_in_range(sd, "WRITE_BLOCK", addr, sd->blk_len)) { - return sd_r1; - } + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } - sd->state = sd_receivingdata_state; - sd->data_start = addr; - sd->data_offset = 0; - sd->blk_written = 0; + 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; + } - if (sd->size <= SDSC_MAX_CAPACITY) { - if (sd_wp_addr(sd, sd->data_start)) { - sd->card_status |= WP_VIOLATION; - } - } - if (sd->csd[14] & 0x30) { - sd->card_status |= WP_VIOLATION; - } - return sd_r1; + 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; +} - default: - break; - } - break; +/* 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); +} - case 26: /* CMD26: PROGRAM_CID */ - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_receivingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; +/* 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); +} - default: - break; - } - break; +/* CMD30 */ +static sd_rsp_type_t sd_cmd_SEND_WRITE_PROT(SDState *sd, SDRequest req) +{ + uint64_t addr; + uint32_t data; - 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; + if (sd->size > SDSC_MAX_CAPACITY) { + return sd_illegal; + } - default: - break; - } - break; + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } - /* Write protection (Class 6) */ - case 28: /* CMD28: SET_WRITE_PROT */ - if (sd->size > SDSC_MAX_CAPACITY) { - return sd_illegal; - } + addr = sd_req_get_address(sd, req); + if (!address_in_range(sd, "SEND_WRITE_PROT", addr, sd->blk_len)) { + return sd_r1; + } - switch (sd->state) { - case sd_transfer_state: - if (!address_in_range(sd, "SET_WRITE_PROT", addr, 1)) { - return sd_r1b; - } + data = sd_wpbits(sd, req.arg); + return sd_cmd_to_sendingdata(sd, req, addr, &data, sizeof(data)); +} - 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; +/* 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; +} - default: - break; - } - break; +/* 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; +} - case 29: /* CMD29: CLR_WRITE_PROT */ - if (sd->size > SDSC_MAX_CAPACITY) { - return sd_illegal; - } +/* 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; + } - 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; + sd_erase(sd); + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_transfer_state; + 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; +/* CMD42 */ +static sd_rsp_type_t sd_cmd_LOCK_UNLOCK(SDState *sd, SDRequest req) +{ + return sd_cmd_to_receivingdata(sd, req, 0, 0); +} - default: - break; +/* 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); + } - case 30: /* CMD30: SEND_WRITE_PROT */ - if (sd->size > SDSC_MAX_CAPACITY) { - return sd_illegal; + /* + * 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) +{ + 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->last_cmd_name, req.cmd, + req.arg, sd_state_name(sd->state)); + } + /* Not interpreting this as an app command */ + sd->card_status &= ~APP_CMD; + + /* 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->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) { + /* 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: - if (!address_in_range(sd, "SEND_WRITE_PROT", - req.arg, sd->blk_len)) { + + if (!address_in_range(sd, "READ_BLOCK", addr, 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; @@ -1467,94 +2041,29 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } 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 */ + /* Block write commands (Class 4) */ + case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ + addr = sd_req_get_address(sd, req); 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; + if (!address_in_range(sd, "WRITE_BLOCK", addr, sd->blk_len)) { + return sd_r1; } - 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_start = addr; sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; + sd->blk_written = 0; - /* 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); + if (sd->size <= SDSC_MAX_CAPACITY) { + if (sd_wp_addr(sd, sd->data_start)) { + sd->card_status |= WP_VIOLATION; + } } - default: - break; - } - if (!sd_is_spi(sd)) { - if (sd->rca != rca) { - return sd_r0; + if (sd->csd[14] & 0x30) { + sd->card_status |= WP_VIOLATION; } - } - 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: @@ -1562,12 +2071,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 58: /* CMD58: READ_OCR (SPI) */ - return sd_r3; - - case 59: /* CMD59: CRC_ON_OFF (SPI) */ - return sd_r1; - default: qemu_log_mask(LOG_GUEST_ERROR, "SD: Unknown CMD%i\n", req.cmd); return sd_illegal; @@ -1579,126 +2082,16 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) static sd_rsp_type_t sd_app_command(SDState *sd, SDRequest req) { - trace_sdcard_app_command(sd_proto(sd)->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(sd)->acmd[req.cmd]) { - return sd_proto(sd)->acmd[req.cmd](sd, req); + 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 */ - 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->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: @@ -1720,8 +2113,10 @@ static sd_rsp_type_t sd_app_command(SDState *sd, 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) @@ -1734,21 +2129,31 @@ 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; + } + if (!sd->proto->cmd[cmd].handler) { + return false; } - return sd_cmd_class[cmd] == 0 || sd_cmd_class[cmd] == 7; + cmd_class = sd->proto->cmd[cmd].class; + + return cmd_class == 0 || cmd_class == 7; } -int sd_do_command(SDState *sd, SDRequest *req, - uint8_t *response) { +static int sd_do_command(SDState *sd, SDRequest *req, + uint8_t *response) { int last_state; sd_rsp_type_t rtype; int rsplen; - if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable) { + if (!sd->blk || !blk_is_inserted(sd->blk)) { 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; @@ -1761,6 +2166,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; @@ -1787,9 +2198,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: @@ -1826,6 +2236,13 @@ int sd_do_command(SDState *sd, SDRequest *req, 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; @@ -1845,15 +2262,43 @@ int sd_do_command(SDState *sd, SDRequest *req, qemu_hexdump(stderr, "Response", response, rsplen); #endif + sd->current_cmd = rtype == sd_illegal ? 0 : req->cmd; + return rsplen; } -void sd_write_byte(SDState *sd, uint8_t value) +/* 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; +} + +static void sd_write_byte(SDState *sd, uint8_t value) { int i; - if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable) + if (!sd->blk || !blk_is_inserted(sd->blk)) { return; + } if (sd->state != sd_receivingdata_state) { qemu_log_mask(LOG_GUEST_ERROR, @@ -1864,16 +2309,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(sd)->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) { + 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. */ @@ -1899,7 +2343,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; @@ -1919,8 +2363,7 @@ 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)) { + 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 ++) @@ -1938,8 +2381,7 @@ 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)) { + 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 ++) @@ -1962,8 +2404,7 @@ 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) { + if (sd_generic_write_byte(sd, value)) { /* TODO: Check CRC before committing */ sd->state = sd_programming_state; sd_lock_command(sd); @@ -1973,95 +2414,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; - } + 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) +static 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; + if (!sd->blk || !blk_is_inserted(sd->blk)) { + 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(sd)->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; - break; - + case 8: /* CMD8: SEND_EXT_CSD */ 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; + 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 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 ++]; @@ -2079,46 +2487,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; @@ -2134,63 +2506,178 @@ static bool sd_data_ready(SDState *sd) return sd->state == sd_sendingdata_state; } -void sd_enable(SDState *sd, bool enable) -{ - sd->enable = enable; -} - static const SDProto sd_proto_spi = { .name = "SPI", .cmd = { - [0] = sd_cmd_GO_IDLE_STATE, - [1] = sd_cmd_SEND_OP_CMD, - [2 ... 4] = sd_cmd_illegal, - [5] = sd_cmd_illegal, - [7] = sd_cmd_illegal, - [15] = sd_cmd_illegal, - [26] = sd_cmd_illegal, - [52 ... 54] = sd_cmd_illegal, + [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 = { - [6] = sd_cmd_unimplemented, - [41] = sd_cmd_SEND_OP_CMD, + [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] = sd_cmd_GO_IDLE_STATE, - [1] = sd_cmd_illegal, - [2] = sd_cmd_ALL_SEND_CID, - [3] = sd_cmd_SEND_RELATIVE_ADDR, - [5] = sd_cmd_illegal, - [19] = sd_cmd_SEND_TUNING_BLOCK, - [23] = sd_cmd_SET_BLOCK_COUNT, - [52 ... 54] = sd_cmd_illegal, - [58] = sd_cmd_illegal, - [59] = sd_cmd_illegal, + [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->enable = true; + sd->proto = sc->proto; + sd->last_cmd_name = "UNSET"; 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; switch (sd->spec_version) { @@ -2241,26 +2728,37 @@ 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 const 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_END_OF_LIST() }; -static void sd_class_init(ObjectClass *klass, void *data) +static const Property sd_properties[] = { + DEFINE_PROP_UINT8("spec_version", SDState, + spec_version, SD_PHY_SPECv3_01_VERS), +}; + +static const Property emmc_properties[] = { + DEFINE_PROP_UINT64("boot-partition-size", SDState, boot_part_size, 0), + DEFINE_PROP_UINT8("boot-config", SDState, boot_config, 0x0), +}; + +static void sdmmc_common_class_init(ObjectClass *klass, const 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); @@ -2272,21 +2770,22 @@ static void sd_class_init(ObjectClass *klass, void *data) sc->read_byte = sd_read_byte; sc->receive_ready = sd_receive_ready; sc->data_ready = sd_data_ready; - sc->enable = sd_enable; sc->get_inserted = sd_get_inserted; sc->get_readonly = sd_get_readonly; - sc->proto = &sd_proto_sd; } -static const TypeInfo sd_info = { - .name = TYPE_SD_CARD, - .parent = TYPE_DEVICE, - .instance_size = sizeof(SDState), - .class_size = sizeof(SDCardClass), - .class_init = sd_class_init, - .instance_init = sd_instance_init, - .instance_finalize = sd_instance_finalize, -}; +static void sd_class_init(ObjectClass *klass, const void *data) +{ + 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; +} /* * We do not model the chip select pin, so allow the board to select @@ -2294,25 +2793,58 @@ static const TypeInfo sd_info = { * board to ensure that ssi transfers only occur when the chip select * is asserted. */ -static void sd_spi_class_init(ObjectClass *klass, void *data) +static void sd_spi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SDCardClass *sc = SD_CARD_CLASS(klass); + SDCardClass *sc = SDMMC_COMMON_CLASS(klass); dc->desc = "SD SPI"; sc->proto = &sd_proto_spi; } -static const TypeInfo sd_spi_info = { - .name = TYPE_SD_CARD_SPI, - .parent = TYPE_SD_CARD, - .class_init = sd_spi_class_init, -}; - -static void sd_register_types(void) +static void emmc_class_init(ObjectClass *klass, const void *data) { - type_register_static(&sd_info); - type_register_static(&sd_spi_info); -} + 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, + }, +}; -type_init(sd_register_types) +DEFINE_TYPES(sd_types) diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h index 5f3765f12d2..9f768c418e0 100644 --- a/hw/sd/sdhci-internal.h +++ b/hw/sd/sdhci-internal.h @@ -322,6 +322,6 @@ void sdhci_initfn(SDHCIState *s); void sdhci_uninitfn(SDHCIState *s); void sdhci_common_realize(SDHCIState *s, Error **errp); void sdhci_common_unrealize(SDHCIState *s); -void sdhci_common_class_init(ObjectClass *klass, void *data); +void sdhci_common_class_init(ObjectClass *klass, const void *data); #endif diff --git a/hw/sd/sdhci-pci.c b/hw/sd/sdhci-pci.c index c737c8b930e..c18b91fe63c 100644 --- a/hw/sd/sdhci-pci.c +++ b/hw/sd/sdhci-pci.c @@ -18,13 +18,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/module.h" +#include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/sd/sdhci.h" #include "sdhci-internal.h" -static Property sdhci_pci_properties[] = { +static const Property sdhci_pci_properties[] = { DEFINE_SDHCI_COMMON_PROPERTIES(SDHCIState), - DEFINE_PROP_END_OF_LIST(), }; static void sdhci_pci_realize(PCIDevice *dev, Error **errp) @@ -49,11 +49,12 @@ static void sdhci_pci_exit(PCIDevice *dev) { SDHCIState *s = PCI_SDHCI(dev); + qemu_free_irq(s->irq); sdhci_common_unrealize(s); sdhci_uninitfn(s); } -static void sdhci_pci_class_init(ObjectClass *klass, void *data) +static void sdhci_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -68,20 +69,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 = (const 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 5564765a9bf..226ff133ff9 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -30,14 +30,13 @@ #include "qapi/error.h" #include "hw/irq.h" #include "hw/qdev-properties.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qemu/timer.h" #include "qemu/bitops.h" #include "hw/sd/sdhci.h" #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; @@ -275,6 +274,10 @@ static void sdhci_set_readonly(DeviceState *dev, bool level) { SDHCIState *s = (SDHCIState *)dev; + if (s->wp_inverted) { + level = !level; + } + if (level) { s->prnsts &= ~SDHC_WRITE_PROTECT; } else { @@ -290,9 +293,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 */ @@ -302,11 +307,16 @@ static void sdhci_reset(SDHCIState *s) s->data_count = 0; s->stopped_state = sdhc_not_stopped; s->pending_insert_state = false; + if (s->vendor == SDHCI_VENDOR_FSL) { + s->norintstsen = 0x013f; + s->errintstsen = 0x117f; + } } 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 +331,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 +383,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 +419,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 +456,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 +485,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 +561,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 +576,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 +609,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; } @@ -653,12 +673,13 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) } } + if (s->norintstsen & SDHC_NISEN_DMA) { + s->norintsts |= SDHC_NIS_DMA; + } + if (s->blkcnt == 0) { sdhci_end_transfer(s); } else { - if (s->norintstsen & SDHC_NISEN_DMA) { - s->norintsts |= SDHC_NIS_DMA; - } sdhci_update_irq(s); } } @@ -679,9 +700,22 @@ static void sdhci_sdma_transfer_single_block(SDHCIState *s) } s->blkcnt--; + if (s->norintstsen & SDHC_NISEN_DMA) { + s->norintsts |= SDHC_NIS_DMA; + } + sdhci_end_transfer(s); } +static void sdhci_sdma_transfer(SDHCIState *s) +{ + if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) { + sdhci_sdma_transfer_single_block(s); + } else { + sdhci_sdma_transfer_multi_blocks(s); + } +} + typedef struct ADMADescr { hwaddr addr; uint16_t length; @@ -699,7 +733,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 +778,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 +877,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; @@ -911,12 +947,7 @@ static void sdhci_data_transfer(void *opaque) if (s->trnmod & SDHC_TRNS_DMA) { switch (SDHC_DMA_TYPE(s->hostctl1)) { case SDHC_CTRL_SDMA: - if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) { - sdhci_sdma_transfer_single_block(s); - } else { - sdhci_sdma_transfer_multi_blocks(s); - } - + sdhci_sdma_transfer(s); break; case SDHC_CTRL_ADMA1_32: if (!(s->capareg & R_SDHC_CAPAB_ADMA1_MASK)) { @@ -973,14 +1004,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,13 +1188,10 @@ 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); - } else { - sdhci_sdma_transfer_single_block(s); - } + sdhci_sdma_transfer(s); } } break; @@ -1168,7 +1199,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 +1231,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 +1306,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; @@ -1380,8 +1425,10 @@ 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_le_ops; } @@ -1429,11 +1476,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; } @@ -1450,7 +1499,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() }, @@ -1460,7 +1509,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), @@ -1491,30 +1540,31 @@ 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 }, }; -void sdhci_common_class_init(ObjectClass *klass, void *data) +void sdhci_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 --- */ -static Property sdhci_sysbus_properties[] = { +static const Property sdhci_sysbus_properties[] = { DEFINE_SDHCI_COMMON_PROPERTIES(SDHCIState), DEFINE_PROP_BOOL("pending-insert-quirk", SDHCIState, pending_insert_quirk, false), DEFINE_PROP_LINK("dma", SDHCIState, dma_mr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_BOOL("wp-inverted", SDHCIState, + wp_inverted, false), }; static void sdhci_sysbus_init(Object *obj) @@ -1570,7 +1620,7 @@ static void sdhci_sysbus_unrealize(DeviceState *dev) } } -static void sdhci_sysbus_class_init(ObjectClass *klass, void *data) +static void sdhci_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1581,18 +1631,9 @@ 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) +static void sdhci_bus_class_init(ObjectClass *klass, const void *data) { SDBusClass *sbc = SD_BUS_CLASS(klass); @@ -1600,13 +1641,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 @@ -1701,16 +1735,10 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) case USDHC_VENDOR_SPEC: s->vendor_spec = value; - switch (s->vendor) { - case SDHCI_VENDOR_IMX: - if (value & USDHC_IMX_FRC_SDCLK_ON) { - s->prnsts &= ~SDHC_IMX_CLOCK_GATE_OFF; - } else { - s->prnsts |= SDHC_IMX_CLOCK_GATE_OFF; - } - break; - default: - break; + if (value & USDHC_IMX_FRC_SDCLK_ON) { + s->prnsts &= ~SDHC_IMX_CLOCK_GATE_OFF; + } else { + s->prnsts |= SDHC_IMX_CLOCK_GATE_OFF; } break; @@ -1865,12 +1893,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 @@ -1929,18 +1951,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 8648a7808dc..00000000000 --- 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", [1] = "SEND_OP_CMD", - [2] = "ALL_SEND_CID", [3] = "SEND_RELATIVE_ADDR", - [4] = "SET_DSR", [5] = "IO_SEND_OP_COND", - [6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD", - [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 d8bf17d204f..91eb5b6b2fc 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. - * - * 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. +/* + * EXT_CSD Modes segment * - * 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); - -/** - * 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. +#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 * - * Returns: The application command name of @cmd or "UNKNOWN_ACMD". + * Define the Device capabilities, cannot be modified by the host. */ -const char *sd_acmd_name(uint8_t cmd); +#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 + +#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 167c03b780f..6c90a86ab41 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -16,7 +16,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "hw/ssi/ssi.h" #include "migration/vmstate.h" #include "hw/qdev-properties.h" @@ -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), @@ -389,7 +389,7 @@ static void ssi_sd_reset(DeviceState *dev) s->stopping = 0; } -static void ssi_sd_class_init(ObjectClass *klass, void *data) +static void ssi_sd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); @@ -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 94a00557b26..db0644256d9 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 e03bd09b50e..bc6331b4abb 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 00000000000..9017ce6116c --- /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, const 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 8f4a1c2cd4b..0c739aa0d85 100644 --- a/hw/sensor/adm1272.c +++ b/hw/sensor/adm1272.c @@ -185,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); @@ -386,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: @@ -457,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), @@ -511,7 +511,7 @@ static void adm1272_init(Object *obj) } -static void adm1272_class_init(ObjectClass *klass, void *data) +static void adm1272_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/sensor/dps310.c b/hw/sensor/dps310.c index addee99b196..bcf615423ae 100644 --- a/hw/sensor/dps310.c +++ b/hw/sensor/dps310.c @@ -188,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), @@ -197,7 +197,7 @@ static const VMStateDescription vmstate_dps310 = { } }; -static void dps310_class_init(ObjectClass *klass, void *data) +static void dps310_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); @@ -205,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 7ce8f4e9794..7b2ce383a12 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), @@ -265,19 +265,19 @@ static void emc141x_initfn(Object *obj) emc141x_set_temperature, NULL, NULL); } -static void emc141x_class_init(ObjectClass *klass, void *data) +static void emc141x_class_init(ObjectClass *klass, const 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; dc->vmsd = &vmstate_emc141x; } -static void emc1413_class_init(ObjectClass *klass, void *data) +static void emc1413_class_init(ObjectClass *klass, const void *data) { EMC141XClass *ec = EMC141X_CLASS(klass); @@ -286,7 +286,7 @@ static void emc1413_class_init(ObjectClass *klass, void *data) ec->sensors_count = 3; } -static void emc1414_class_init(ObjectClass *klass, void *data) +static void emc1414_class_init(ObjectClass *klass, const void *data) { EMC141XClass *ec = EMC141X_CLASS(klass); diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c index e51269f6b83..e8d29b08ff3 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); @@ -102,11 +102,11 @@ static void isl_pmbus_vr_exit_reset(Object *obj) } /* The raa228000 uses different direct mode coefficients from most isl devices */ -static void raa228000_exit_reset(Object *obj) +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)); @@ -233,7 +233,7 @@ static void raa228000_init(Object *obj) isl_pmbus_vr_add_props(obj, flags, 1); } -static void isl_pmbus_vr_class_init(ObjectClass *klass, void *data, +static void isl_pmbus_vr_class_init(ObjectClass *klass, const void *data, uint8_t pages) { PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass); @@ -242,7 +242,7 @@ static void isl_pmbus_vr_class_init(ObjectClass *klass, void *data, k->device_num_pages = pages; } -static void isl69260_class_init(ObjectClass *klass, void *data) +static void isl69260_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -251,7 +251,7 @@ static void isl69260_class_init(ObjectClass *klass, void *data) isl_pmbus_vr_class_init(klass, data, 2); } -static void raa228000_class_init(ObjectClass *klass, void *data) +static void raa228000_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -260,7 +260,7 @@ static void raa228000_class_init(ObjectClass *klass, void *data) isl_pmbus_vr_class_init(klass, data, 1); } -static void raa229004_class_init(ObjectClass *klass, void *data) +static void raa229004_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -269,7 +269,7 @@ static void raa229004_class_init(ObjectClass *klass, void *data) isl_pmbus_vr_class_init(klass, data, 2); } -static void isl69259_class_init(ObjectClass *klass, void *data) +static void isl69259_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/sensor/lsm303dlhc_mag.c b/hw/sensor/lsm303dlhc_mag.c index bb8d48b2fdb..f9e501da840 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), @@ -530,12 +530,12 @@ static void lsm303dlhc_mag_initfn(Object *obj) /* * Set the virtual method pointers (bus state change, tx/rx, etc.). */ -static void lsm303dlhc_mag_class_init(ObjectClass *klass, void *data) +static void lsm303dlhc_mag_class_init(ObjectClass *klass, const 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 8b95e324814..c75581455a2 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), @@ -544,7 +544,7 @@ static void max31785_init(Object *obj) } } -static void max31785_class_init(ObjectClass *klass, void *data) +static void max31785_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/sensor/max34451.c b/hw/sensor/max34451.c index 9db52ef6778..a369d2b5319 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), @@ -746,7 +746,7 @@ static void max34451_init(Object *obj) } -static void max34451_class_init(ObjectClass *klass, void *data) +static void max34451_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build index 30e20e27b87..420fdc33592 100644 --- a/hw/sensor/meson.build +++ b/hw/sensor/meson.build @@ -2,6 +2,7 @@ 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')) diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c index 20564494899..f5b61109e36 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; @@ -300,7 +313,7 @@ static void tmp105_initfn(Object *obj) tmp105_set_temperature, NULL, NULL); } -static void tmp105_class_init(ObjectClass *klass, void *data) +static void tmp105_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); diff --git a/hw/sensor/tmp421.c b/hw/sensor/tmp421.c index a3db57dcb5a..3421c440869 100644 --- a/hw/sensor/tmp421.c +++ b/hw/sensor/tmp421.c @@ -68,7 +68,7 @@ struct TMP421State { struct TMP421Class { I2CSlaveClass parent_class; - DeviceInfo *dev; + const DeviceInfo *dev; }; #define TYPE_TMP421 "tmp421-generic" @@ -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), @@ -337,7 +337,7 @@ static void tmp421_realize(DeviceState *dev, Error **errp) tmp421_reset(&s->i2c); } -static void tmp421_class_init(ObjectClass *klass, void *data) +static void tmp421_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); @@ -382,9 +382,9 @@ static void tmp421_register_types(void) .name = devices[i].name, .parent = TYPE_TMP421, .class_init = tmp421_class_init, - .class_data = (void *) &devices[i], + .class_data = &devices[i], }; - type_register(&ti); + type_register_static(&ti); } } diff --git a/hw/sensor/trace-events b/hw/sensor/trace-events new file mode 100644 index 00000000000..a3fe54fa6dc --- /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 00000000000..e4721560b03 --- /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 ab733a3f760..1660d292d53 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 424d5674dea..7d27839fee6 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 4944994e9c8..d68c94e82ef 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -30,9 +30,9 @@ #include "cpu.h" #include "hw/sysbus.h" #include "hw/sh4/sh.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/pci/pci.h" #include "hw/qdev-properties.h" @@ -43,6 +43,7 @@ #include "hw/loader.h" #include "hw/usb.h" #include "hw/block/flash.h" +#include "exec/tswap.h" #define FLASH_BASE 0x00000000 #define FLASH_SIZE (16 * MiB) @@ -62,6 +63,12 @@ #define PA_VERREG 0x32 #define PA_OUTPORT 0x36 +enum r2d_fpga_irq { + PCI_INTD, CF_IDE, CF_CD, PCI_INTC, SM501, KEY, RTC_A, RTC_T, + SDCARD, PCI_INTA, PCI_INTB, EXT, TP, + NR_IRQS +}; + typedef struct { uint16_t bcr; uint16_t irlmsk; @@ -87,15 +94,10 @@ typedef struct { /* output pin */ qemu_irq irl; + IRQState irq[NR_IRQS]; MemoryRegion iomem; } r2d_fpga_t; -enum r2d_fpga_irq { - PCI_INTD, CF_IDE, CF_CD, PCI_INTC, SM501, KEY, RTC_A, RTC_T, - SDCARD, PCI_INTA, PCI_INTB, EXT, TP, - NR_IRQS -}; - static const struct { short irl; uint16_t msk; } irqtab[NR_IRQS] = { [CF_IDE] = { 1, 1 << 9 }, [CF_CD] = { 2, 1 << 8 }, @@ -185,8 +187,8 @@ static const MemoryRegionOps r2d_fpga_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static qemu_irq *r2d_fpga_init(MemoryRegion *sysmem, - hwaddr base, qemu_irq irl) +static r2d_fpga_t *r2d_fpga_init(MemoryRegion *sysmem, + hwaddr base, qemu_irq irl) { r2d_fpga_t *s; @@ -196,7 +198,10 @@ static qemu_irq *r2d_fpga_init(MemoryRegion *sysmem, memory_region_init_io(&s->iomem, NULL, &r2d_fpga_ops, s, "r2d-fpga", 0x40); memory_region_add_subregion(sysmem, base, &s->iomem); - return qemu_allocate_irqs(r2d_fpga_irq_set, s, NR_IRQS); + + qemu_init_irqs(s->irq, NR_IRQS, r2d_fpga_irq_set, s); + + return s; } typedef struct ResetData { @@ -238,13 +243,13 @@ static void r2d_init(MachineState *machine) ResetData *reset_info; struct SH7750State *s; 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; + r2d_fpga_t *fpga; cpu = SUPERH_CPU(cpu_create(machine->cpu_type)); env = &cpu->env; @@ -259,7 +264,7 @@ static void r2d_init(MachineState *machine) memory_region_add_subregion(address_space_mem, SDRAM_BASE, sdram); /* Register peripherals */ s = sh7750_init(cpu, address_space_mem); - irq = r2d_fpga_init(address_space_mem, 0x04000000, sh7750_irl(s)); + fpga = r2d_fpga_init(address_space_mem, 0x04000000, sh7750_irl(s)); dev = qdev_new("sh_pci"); busdev = SYS_BUS_DEVICE(dev); @@ -267,10 +272,10 @@ static void r2d_init(MachineState *machine) pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci")); sysbus_mmio_map(busdev, 0, P4ADDR(0x1e200000)); sysbus_mmio_map(busdev, 1, A7ADDR(0x1e200000)); - sysbus_connect_irq(busdev, 0, irq[PCI_INTA]); - sysbus_connect_irq(busdev, 1, irq[PCI_INTB]); - sysbus_connect_irq(busdev, 2, irq[PCI_INTC]); - sysbus_connect_irq(busdev, 3, irq[PCI_INTD]); + sysbus_connect_irq(busdev, 0, &fpga->irq[PCI_INTA]); + sysbus_connect_irq(busdev, 1, &fpga->irq[PCI_INTB]); + sysbus_connect_irq(busdev, 2, &fpga->irq[PCI_INTC]); + sysbus_connect_irq(busdev, 3, &fpga->irq[PCI_INTD]); dev = qdev_new("sysbus-sm501"); busdev = SYS_BUS_DEVICE(dev); @@ -280,13 +285,13 @@ static void r2d_init(MachineState *machine) sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, 0x10000000); sysbus_mmio_map(busdev, 1, 0x13e00000); - sysbus_connect_irq(busdev, 0, irq[SM501]); + sysbus_connect_irq(busdev, 0, &fpga->irq[SM501]); /* onboard CF (True IDE mode, Master only). */ dinfo = drive_get(IF_IDE, 0, 0); dev = qdev_new("mmio-ide"); busdev = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(busdev, 0, irq[CF_IDE]); + sysbus_connect_irq(busdev, 0, &fpga->irq[CF_IDE]); qdev_prop_set_uint32(dev, "shift", 1); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, 0x14001000); @@ -309,12 +314,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, - mc->default_nic, 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)); diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index ebe0fd96d94..300eabc595d 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -28,18 +28,17 @@ #include "hw/sysbus.h" #include "hw/irq.h" #include "hw/sh4/sh.h" -#include "sysemu/sysemu.h" +#include "system/system.h" +#include "target/sh4/cpu.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "sh7750_regs.h" #include "sh7750_regnames.h" #include "hw/sh4/sh_intc.h" #include "hw/timer/tmu012.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "trace.h" -#define NB_DEVICES 4 - typedef struct SH7750State { MemoryRegion iomem; MemoryRegion iomem_1f0; @@ -75,7 +74,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 +90,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 +127,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); - } } /* diff --git a/hw/sh4/sh7750_regs.h b/hw/sh4/sh7750_regs.h index 94043431e61..946ad7b3aaf 100644 --- a/hw/sh4/sh7750_regs.h +++ b/hw/sh4/sh7750_regs.h @@ -113,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) @@ -172,7 +172,7 @@ /* - * Exeption-related registers + * Exception-related registers */ /* Immediate data for TRAPA instruction - TRA */ @@ -183,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 /* @@ -1274,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 aa812512f0c..00000000000 --- 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 4b61cd56c89..6bfd7eebc41 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 553adf4bfcf..8d989a2f1bc 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 6eeae4b35c2..a59039f6692 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')) +smbios_ss.add(when: 'CONFIG_SMBIOS_LEGACY', + if_true: files('smbios_legacy.c'), + if_false: files('smbios_legacy_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')) - -system_ss.add(when: 'CONFIG_ALL', if_true: files( - 'smbios-stub.c', - 'smbios_type_38-stub.c', -)) diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 10cd22f610e..ad4cd6721e6 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -19,10 +19,9 @@ #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" +#include "system/system.h" #include "qemu/uuid.h" #include "hw/firmware/smbios.h" #include "hw/loader.h" @@ -31,60 +30,30 @@ #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 DECLARE_BITMAP(have_binfile_bitmap, SMBIOS_MAX_TYPE+1); -static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1); +static uint32_t smbios_cpuid_version, smbios_cpuid_features; -static struct { - const char *vendor, *version, *date; - bool have_major_minor, uefi; - uint8_t major, minor; -} type0; +DECLARE_BITMAP(smbios_have_binfile_bitmap, SMBIOS_MAX_TYPE + 1); +DECLARE_BITMAP(smbios_have_fields_bitmap, SMBIOS_MAX_TYPE + 1); -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; @@ -102,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; @@ -110,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 { @@ -119,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; @@ -337,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, @@ -346,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, @@ -366,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, @@ -379,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[] = { @@ -458,126 +508,33 @@ opts_init(smbios_register_config); */ #define SMBIOS_21_MAX_TABLES_LEN 0xffff -static void smbios_validate_table(MachineState *ms) -{ - 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_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); - } -} - - -/* legacy setup functions for <= 2.0 machines */ -static void smbios_add_field(int type, int offset, const void *data, size_t len) +static bool smbios_check_type4_count(uint32_t expected_t4_count, Error **errp) { - 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); + 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; } -uint8_t *smbios_get_table_legacy(MachineState *ms, size_t *length) +bool smbios_validate_table(SmbiosEntryPointType ep_type, Error **errp) { - 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; + if (ep_type == SMBIOS_ENTRY_POINT_TYPE_32 && + smbios_tables_len > SMBIOS_21_MAX_TABLES_LEN) { + error_setg(errp, "SMBIOS 2.1 table length %zu exceeds %d", + smbios_tables_len, SMBIOS_21_MAX_TABLES_LEN); + return false; } - *length = smbios_entries_len; - return smbios_entries; + return true; } -/* 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) { @@ -591,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 @@ -604,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; @@ -641,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; } @@ -709,14 +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; } @@ -726,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); @@ -758,11 +716,17 @@ static void smbios_build_type_4_table(MachineState *ms, unsigned instance) t->thread_count = (threads_per_socket > 255) ? 0xFF : threads_per_socket; t->processor_characteristics = cpu_to_le16(0x02); /* Unknown */ - t->processor_family2 = cpu_to_le16(0x01); /* Other */ + t->processor_family2 = cpu_to_le16(type4.processor_family); if (tbl_len == SMBIOS_TYPE_4_LEN_V30) { t->core_count2 = t->core_enabled2 = cpu_to_le16(cores_per_socket); t->thread_count2 = cpu_to_le16(threads_per_socket); + } else if (t->core_count == 0xFF || t->thread_count == 0xFF) { + error_setg(errp, "SMBIOS 2.0 doesn't support number of processor " + "cores/threads more than 255, use " + "-machine smbios-entry-point-type=64 option to enable " + "SMBIOS 3.0 support"); + return; } SMBIOS_BUILD_TABLE_POST; @@ -789,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]; @@ -983,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); @@ -1021,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); @@ -1072,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, @@ -1080,77 +1093,88 @@ void smbios_get_tables(MachineState *ms, Error **errp) { unsigned i, dimm_cnt, offset; - - if (smbios_legacy) { - *tables = *anchor = NULL; - *tables_len = *anchor_len = 0; - return; - } - - if (!smbios_immutable) { - smbios_build_type_0_table(); - smbios_build_type_1_table(); - smbios_build_type_2_table(); - smbios_build_type_3_table(); - - smbios_smp_sockets = ms->smp.sockets; - assert(smbios_smp_sockets >= 1); - - for (i = 0; i < smbios_smp_sockets; i++) { - smbios_build_type_4_table(ms, i); + MachineClass *mc = MACHINE_GET_CLASS(ms); + ERRP_GUARD(); + + 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; } + } - smbios_build_type_8_table(); - smbios_build_type_11_table(); + smbios_build_type_8_table(); + smbios_build_type_9_table(errp); + smbios_build_type_11_table(); -#define MAX_DIMM_SZ (16 * GiB) -#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ \ - : ((current_machine->ram_size - 1) % MAX_DIMM_SZ) + 1) +#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? mc->smbios_memory_device_size \ + : ((current_machine->ram_size - 1) % mc->smbios_memory_device_size) + 1) - dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, MAX_DIMM_SZ) / MAX_DIMM_SZ; + dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, + mc->smbios_memory_device_size) / + mc->smbios_memory_device_size; - /* - * The offset determines if we need to keep additional space betweeen - * table 17 and table 19 header handle numbers so that they do - * not overlap. For example, for a VM with larger than 8 TB guest - * memory and DIMM like chunks of 16 GiB, the default space between - * the two tables (T19_BASE - T17_BASE = 512) is not enough. - */ - offset = (dimm_cnt > (T19_BASE - T17_BASE)) ? \ - dimm_cnt - (T19_BASE - T17_BASE) : 0; + /* + * The offset determines if we need to keep additional space between + * table 17 and table 19 header handle numbers so that they do + * not overlap. For example, for a VM with larger than 8 TB guest + * memory and DIMM like chunks of 16 GiB, the default space between + * the two tables (T19_BASE - T17_BASE = 512) is not enough. + */ + offset = (dimm_cnt > (T19_BASE - T17_BASE)) ? \ + dimm_cnt - (T19_BASE - T17_BASE) : 0; - smbios_build_type_16_table(dimm_cnt); + smbios_build_type_16_table(dimm_cnt); - for (i = 0; i < dimm_cnt; i++) { - smbios_build_type_17_table(i, GET_DIMM_SZ); - } + for (i = 0; i < 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); - } + 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)); + /* + * 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_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; + 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); @@ -1159,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) @@ -1210,6 +1285,9 @@ static int save_opt_one(void *opaque, g_byte_array_append(data, (guint8 *)buf, ret); } + buf[0] = '\0'; + g_byte_array_append(data, (guint8 *)buf, 1); + qemu_close(fd); *opt->dest = g_renew(char *, *opt->dest, (*opt->ndest) + 1); @@ -1244,13 +1322,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; @@ -1267,9 +1342,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); @@ -1277,47 +1352,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; } @@ -1331,41 +1389,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) { @@ -1402,6 +1461,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"); @@ -1423,14 +1485,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; @@ -1452,27 +1536,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 00000000000..14319d4897f --- /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 "system/system.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 00000000000..7d593dca986 --- /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/smbios/smbios_type_38.c b/hw/smbios/smbios_type_38.c index 168b886647d..e9b856fcd96 100644 --- a/hw/smbios/smbios_type_38.c +++ b/hw/smbios/smbios_type_38.c @@ -72,7 +72,12 @@ static void smbios_build_one_type_38(IPMIFwInfo *info) " SMBIOS, ignoring this entry.", info->register_spacing); return; } - t->interrupt_number = info->interrupt_number; + if (info->irq_source == IPMI_ISA_IRQ) { + t->interrupt_number = info->interrupt_number; + } else { + /* TODO: How to handle PCI? */ + t->interrupt_number = 0; + } SMBIOS_BUILD_TABLE_POST; } diff --git a/hw/sparc/Kconfig b/hw/sparc/Kconfig index 79d58beb7a6..3cc165dbfb7 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 1e39d2e2d0a..0aeaad3becc 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 @@ -32,15 +34,17 @@ #include "qemu/timer.h" #include "hw/ptimer.h" #include "hw/qdev-properties.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" +#include "system/system.h" +#include "system/qtest.h" +#include "system/reset.h" #include "hw/boards.h" #include "hw/loader.h" #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) +{ + cpu->halted = 0; +} + +static void leon3_start_cpu(void *opaque, int n, int level) { - leon3_irq_ack(irq_manager, intno); + 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)); + + 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; - cpu_sparc_set_id(env, 0); + cpu_sparc_set_id(env, i); - /* 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); + /* 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); @@ -324,7 +380,7 @@ static void leon3_generic_hw_init(MachineState *machine) kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &entry, NULL, NULL, NULL, - 1 /* big endian */, EM_SPARC, 0, 0); + ELFDATA2MSB, EM_SPARC, 0, 0); if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, NULL, &entry, NULL, NULL, NULL); @@ -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 17bf5f28791..8ac7e625ef1 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -27,6 +27,7 @@ #include "qapi/error.h" #include "qemu/datadir.h" #include "cpu.h" +#include "exec/target_page.h" #include "hw/sysbus.h" #include "qemu/error-report.h" #include "qemu/timer.h" @@ -35,9 +36,9 @@ #include "migration/vmstate.h" #include "hw/sparc/sparc32_dma.h" #include "hw/block/fdc.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/system.h" #include "net/net.h" #include "hw/boards.h" #include "hw/scsi/esp.h" @@ -233,19 +234,13 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename, kernel_size = 0; if (linux_boot) { - int bswap_needed; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#else - bswap_needed = 0; -#endif kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, NULL, - NULL, NULL, NULL, NULL, 1, EM_SPARC, 0, 0); + NULL, NULL, NULL, NULL, + ELFDATA2MSB, EM_SPARC, 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, - RAM_size - KERNEL_LOAD_ADDR, bswap_needed, + RAM_size - KERNEL_LOAD_ADDR, true, TARGET_PAGE_SIZE); if (kernel_size < 0) kernel_size = load_image_targphys(kernel_filename, @@ -299,30 +294,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 +584,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; } @@ -591,7 +595,7 @@ static void idreg_realize(DeviceState *ds, Error **errp) sysbus_init_mmio(dev, &s->mem); } -static void idreg_class_init(ObjectClass *oc, void *data) +static void idreg_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -631,12 +635,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; } @@ -644,7 +645,7 @@ static void afx_realize(DeviceState *ds, Error **errp) sysbus_init_mmio(dev, &s->mem); } -static void afx_class_init(ObjectClass *oc, void *data) +static void afx_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -697,7 +698,7 @@ static void prom_init(hwaddr addr, const char *bios_name) if (filename) { ret = load_elf(filename, NULL, translate_prom_address, &addr, NULL, - NULL, NULL, NULL, 1, EM_SPARC, 0, 0); + NULL, NULL, NULL, ELFDATA2MSB, EM_SPARC, 0, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); } @@ -715,12 +716,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; } @@ -729,15 +727,10 @@ static void prom_realize(DeviceState *ds, Error **errp) sysbus_init_mmio(dev, &s->prom); } -static Property prom_properties[] = { - {/* end of property list */}, -}; - -static void prom_class_init(ObjectClass *klass, void *data) +static void prom_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - device_class_set_props(dc, prom_properties); dc->realize = prom_realize; } @@ -778,7 +771,7 @@ static void ram_initfn(Object *obj) "Valid value is ID of a hostmem backend"); } -static void ram_class_init(ObjectClass *klass, void *data) +static void ram_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -804,7 +797,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 +825,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 +886,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); @@ -977,7 +969,7 @@ static void sun4m_hw_init(MachineState *machine) sysbus_mmio_map(s, 0, hwdef->ms_kb_base); /* Logically OR both its IRQs together */ - ms_kb_orgate = DEVICE(object_new(TYPE_OR_IRQ)); + ms_kb_orgate = qdev_new(TYPE_OR_IRQ); object_property_set_int(OBJECT(ms_kb_orgate), "num-lines", 2, &error_fatal); qdev_realize_and_unref(ms_kb_orgate, NULL, &error_fatal); sysbus_connect_irq(s, 0, qdev_get_gpio_in(ms_kb_orgate, 0)); @@ -998,7 +990,7 @@ static void sun4m_hw_init(MachineState *machine) sysbus_mmio_map(s, 0, hwdef->serial_base); /* Logically OR both its IRQs together */ - serial_orgate = DEVICE(object_new(TYPE_OR_IRQ)); + serial_orgate = qdev_new(TYPE_OR_IRQ); object_property_set_int(OBJECT(serial_orgate), "num-lines", 2, &error_fatal); qdev_realize_and_unref(serial_orgate, NULL, &error_fatal); @@ -1048,7 +1040,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"); @@ -1106,7 +1098,7 @@ enum { ss600mp_id, }; -static void sun4m_machine_class_init(ObjectClass *oc, void *data) +static void sun4m_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1117,7 +1109,7 @@ static void sun4m_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_id = "sun4m.ram"; } -static void ss5_class_init(ObjectClass *oc, void *data) +static void ss5_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1154,7 +1146,7 @@ static void ss5_class_init(ObjectClass *oc, void *data) smc->hwdef = &ss5_hwdef; } -static void ss10_class_init(ObjectClass *oc, void *data) +static void ss10_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1189,7 +1181,7 @@ static void ss10_class_init(ObjectClass *oc, void *data) smc->hwdef = &ss10_hwdef; } -static void ss600mp_class_init(ObjectClass *oc, void *data) +static void ss600mp_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1222,7 +1214,7 @@ static void ss600mp_class_init(ObjectClass *oc, void *data) smc->hwdef = &ss600mp_hwdef; } -static void ss20_class_init(ObjectClass *oc, void *data) +static void ss20_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1273,7 +1265,7 @@ static void ss20_class_init(ObjectClass *oc, void *data) smc->hwdef = &ss20_hwdef; } -static void voyager_class_init(ObjectClass *oc, void *data) +static void voyager_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1305,7 +1297,7 @@ static void voyager_class_init(ObjectClass *oc, void *data) smc->hwdef = &voyager_hwdef; } -static void ss_lx_class_init(ObjectClass *oc, void *data) +static void ss_lx_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1338,7 +1330,7 @@ static void ss_lx_class_init(ObjectClass *oc, void *data) smc->hwdef = &ss_lx_hwdef; } -static void ss4_class_init(ObjectClass *oc, void *data) +static void ss4_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1371,7 +1363,7 @@ static void ss4_class_init(ObjectClass *oc, void *data) smc->hwdef = &ss4_hwdef; } -static void scls_class_init(ObjectClass *oc, void *data) +static void scls_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1403,7 +1395,7 @@ static void scls_class_init(ObjectClass *oc, void *data) smc->hwdef = &scls_hwdef; } -static void sbook_class_init(ObjectClass *oc, void *data) +static void sbook_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); diff --git a/hw/sparc/sun4m_iommu.c b/hw/sparc/sun4m_iommu.c index eb40f9377c1..a7ff36ee78c 100644 --- a/hw/sparc/sun4m_iommu.c +++ b/hw/sparc/sun4m_iommu.c @@ -29,7 +29,7 @@ #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "trace.h" /* @@ -238,7 +238,7 @@ static void iommu_mem_write(void *opaque, hwaddr addr, static const MemoryRegionOps iommu_mem_ops = { .read = iommu_mem_read, .write = iommu_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -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() @@ -368,16 +368,15 @@ static void iommu_init(Object *obj) sysbus_init_mmio(dev, &s->iomem); } -static Property iommu_properties[] = { +static const Property iommu_properties[] = { DEFINE_PROP_UINT32("version", IOMMUState, version, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void iommu_class_init(ObjectClass *klass, void *data) +static void iommu_class_init(ObjectClass *klass, const 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); } @@ -390,7 +389,8 @@ static const TypeInfo iommu_info = { .class_init = iommu_class_init, }; -static void sun4m_iommu_memory_region_class_init(ObjectClass *klass, void *data) +static void sun4m_iommu_memory_region_class_init(ObjectClass *klass, + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/sparc64/Kconfig b/hw/sparc64/Kconfig index 7e557ad17b0..f764c8a219d 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 ab3c4ec3463..1ffe92060ad 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -27,15 +27,15 @@ #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" #include "hw/rtc/sun4v-rtc.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/error-report.h" -#include "sysemu/qtest.h" -#include "sysemu/sysemu.h" +#include "system/qtest.h" +#include "system/system.h" #include "qapi/error.h" typedef struct NiagaraBoardState { @@ -157,7 +157,7 @@ static void niagara_init(MachineState *machine) sun4v_rtc_init(NIAGARA_RTC_BASE); } -static void niagara_class_init(ObjectClass *oc, void *data) +static void niagara_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/sparc64/sparc64.c b/hw/sparc64/sparc64.c index 72f0849f50c..9cffc92aa3b 100644 --- a/hw/sparc64/sparc64.c +++ b/hw/sparc64/sparc64.c @@ -24,11 +24,12 @@ #include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "hw/boards.h" #include "hw/sparc/sparc64.h" #include "qemu/timer.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "trace.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 d908a38f730..e9f9b0a4cb9 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -28,13 +28,15 @@ #include "qapi/error.h" #include "qemu/datadir.h" #include "cpu.h" +#include "exec/target_page.h" #include "hw/irq.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bridge.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/serial-isa.h" +#include "hw/char/serial-mm.h" #include "hw/char/parallel-isa.h" #include "hw/rtc/m48t59.h" #include "migration/vmstate.h" @@ -42,8 +44,8 @@ #include "hw/block/fdc.h" #include "net/net.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/nvram/sun_nvram.h" #include "hw/nvram/chrp_nvram.h" @@ -167,21 +169,14 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, kernel_size = 0; if (linux_boot) { - int bswap_needed; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#else - bswap_needed = 0; -#endif kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, kernel_entry, - kernel_addr, &kernel_top, NULL, 1, EM_SPARCV9, 0, - 0); + kernel_addr, &kernel_top, NULL, + ELFDATA2MSB, EM_SPARCV9, 0, 0); if (kernel_size < 0) { *kernel_addr = KERNEL_LOAD_ADDR; *kernel_entry = KERNEL_LOAD_ADDR; kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, - RAM_size - KERNEL_LOAD_ADDR, bswap_needed, + RAM_size - KERNEL_LOAD_ADDR, true, TARGET_PAGE_SIZE); } if (kernel_size < 0) { @@ -253,7 +248,7 @@ static void power_mem_write(void *opaque, hwaddr addr, static const MemoryRegionOps power_mem_ops = { .read = power_mem_read, .write = power_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -271,7 +266,7 @@ static void power_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &d->power_mmio); } -static void power_class_init(ObjectClass *klass, void *data) +static void power_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -360,21 +355,25 @@ 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); } -static Property ebus_properties[] = { +static const Property ebus_properties[] = { DEFINE_PROP_UINT64("console-serial-base", EbusState, console_serial_base, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void ebus_class_init(ObjectClass *klass, void *data) +static void ebus_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -392,7 +391,7 @@ static const TypeInfo ebus_info = { .parent = TYPE_PCI_DEVICE, .class_init = ebus_class_init, .instance_size = sizeof(EbusState), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -436,7 +435,7 @@ static void prom_init(hwaddr addr, const char *bios_name) filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { ret = load_elf(filename, NULL, translate_prom_address, &addr, - NULL, NULL, NULL, NULL, 1, EM_SPARCV9, 0, 0); + NULL, NULL, NULL, NULL, ELFDATA2MSB, EM_SPARCV9, 0, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); } @@ -454,12 +453,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; } @@ -468,15 +464,10 @@ static void prom_realize(DeviceState *ds, Error **errp) sysbus_init_mmio(dev, &s->prom); } -static Property prom_properties[] = { - {/* end of property list */}, -}; - -static void prom_class_init(ObjectClass *klass, void *data) +static void prom_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - device_class_set_props(dc, prom_properties); dc->realize = prom_realize; } @@ -529,12 +520,11 @@ static void ram_init(hwaddr addr, ram_addr_t RAM_size) sysbus_mmio_map(s, 0, addr); } -static Property ram_properties[] = { +static const Property ram_properties[] = { DEFINE_PROP_UINT64("size", RamDevice, size, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void ram_class_init(ObjectClass *klass, void *data) +static void ram_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -642,29 +632,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, mc->default_nic) == 0) { - if (!onboard_nic) { - pci_dev = pci_new_multifunction(PCI_DEVFN(1, 1), mc->default_nic); - bus = pci_busA; - memcpy(&macaddr, &nd->macaddr.a, sizeof(MACAddr)); - onboard_nic = true; - } else { - pci_dev = pci_new(-1, mc->default_nic); - 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,7 +781,13 @@ static void sun4v_init(MachineState *machine) sun4uv_init(get_system_memory(), machine, &hwdefs[1]); } -static void sun4u_class_init(ObjectClass *oc, void *data) +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, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); @@ -819,19 +804,20 @@ static void sun4u_class_init(ObjectClass *oc, void *data) 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 = { .name = MACHINE_TYPE_NAME("sun4u"), .parent = TYPE_MACHINE, .class_init = sun4u_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { } }, }; -static void sun4v_class_init(ObjectClass *oc, void *data) +static void sun4v_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/sparc64/sun4u_iommu.c b/hw/sparc64/sun4u_iommu.c index 1c1dca712e3..14645f475a0 100644 --- a/hw/sparc64/sun4u_iommu.c +++ b/hw/sparc64/sun4u_iommu.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/sparc/sun4u_iommu.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qemu/log.h" #include "qemu/module.h" #include "trace.h" @@ -305,11 +305,11 @@ static void iommu_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static void iommu_class_init(ObjectClass *klass, void *data) +static void iommu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = iommu_reset; + device_class_set_legacy_reset(dc, iommu_reset); } static const TypeInfo iommu_info = { @@ -320,7 +320,8 @@ static const TypeInfo iommu_info = { .class_init = iommu_class_init, }; -static void sun4u_iommu_memory_region_class_init(ObjectClass *klass, void *data) +static void sun4u_iommu_memory_region_class_init(ObjectClass *klass, + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig index 7d90a02181e..1bd56463c1e 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 00000000000..6b7cca8d32a --- /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_LITTLE_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, const 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 2a4001b774a..614528b8ef6 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__) @@ -344,7 +359,7 @@ static const MemoryRegionOps aspeed_smc_flash_default_ops = { .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, - .max_access_size = 4, + .max_access_size = 8, }, }; @@ -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); } @@ -655,29 +670,42 @@ static const MemoryRegionOps aspeed_smc_flash_ops = { .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, - .max_access_size = 4, + .max_access_size = 8, }, }; 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) @@ -695,6 +723,14 @@ static void aspeed_smc_reset(DeviceState *d) 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); } @@ -706,6 +742,8 @@ static void aspeed_smc_reset(DeviceState *d) 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, @@ -734,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 && @@ -764,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(); } /* @@ -834,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. @@ -841,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) { @@ -852,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) { @@ -868,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)) { @@ -879,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; } @@ -913,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; } } @@ -926,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; } } @@ -1079,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); @@ -1199,29 +1276,30 @@ 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[] = { +static const 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(), }; -static void aspeed_smc_class_init(ObjectClass *klass, void *data) +static void aspeed_smc_class_init(ObjectClass *klass, const 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; } @@ -1252,19 +1330,18 @@ 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); } -static Property aspeed_smc_flash_properties[] = { +static const Property aspeed_smc_flash_properties[] = { DEFINE_PROP_UINT8("cs", AspeedSMCFlash, cs, 0), DEFINE_PROP_LINK("controller", AspeedSMCFlash, controller, TYPE_ASPEED_SMC, AspeedSMCState *), - DEFINE_PROP_END_OF_LIST(), }; -static void aspeed_smc_flash_class_init(ObjectClass *klass, void *data) +static void aspeed_smc_flash_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1306,7 +1383,7 @@ static const AspeedSegments aspeed_2400_smc_segments[] = { { 0x10000000, 32 * MiB }, }; -static void aspeed_2400_smc_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_smc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1327,6 +1404,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 = { @@ -1351,7 +1429,7 @@ static const AspeedSegments aspeed_2400_fmc_segments[] = { { 0x2A000000, 32 * MiB } }; -static void aspeed_2400_fmc_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_fmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1372,10 +1450,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 = { @@ -1393,7 +1473,7 @@ static int aspeed_2400_spi1_addr_width(const AspeedSMCState *s) return s->regs[R_SPI_CTRL0] & CTRL_AST2400_SPI_4BYTE ? 4 : 3; } -static void aspeed_2400_spi1_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_spi1_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1415,6 +1495,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,12 +1515,12 @@ static const AspeedSegments aspeed_2500_fmc_segments[] = { { 0x2A000000, 32 * MiB }, }; -static void aspeed_2500_fmc_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_fmc_class_init(ObjectClass *klass, const 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; @@ -1455,10 +1536,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,12 +1555,12 @@ static const AspeedSegments aspeed_2500_spi1_segments[] = { { 0x32000000, 96 * MiB }, /* end address is readonly */ }; -static void aspeed_2500_spi1_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_spi1_class_init(ObjectClass *klass, const 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; @@ -1494,6 +1577,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,12 +1591,12 @@ static const AspeedSegments aspeed_2500_spi2_segments[] = { { 0x3A000000, 96 * MiB }, /* end address is readonly */ }; -static void aspeed_2500_spi2_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_spi2_class_init(ObjectClass *klass, const 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; @@ -1529,6 +1613,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 = { @@ -1589,7 +1674,7 @@ static const AspeedSegments aspeed_2600_fmc_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_2600_fmc_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_fmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1611,10 +1696,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 = { @@ -1628,7 +1715,7 @@ static const AspeedSegments aspeed_2600_spi1_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_2600_spi1_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_spi1_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1649,10 +1736,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 = { @@ -1667,7 +1756,7 @@ static const AspeedSegments aspeed_2600_spi2_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_2600_spi2_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_spi2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1688,10 +1777,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 = { @@ -1748,7 +1839,7 @@ static const AspeedSegments aspeed_1030_fmc_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_1030_fmc_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_fmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1769,10 +1860,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 = { @@ -1786,7 +1879,7 @@ static const AspeedSegments aspeed_1030_spi1_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_1030_spi1_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_spi1_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1806,10 +1899,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 = { @@ -1822,7 +1917,7 @@ static const AspeedSegments aspeed_1030_spi2_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_1030_spi2_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_spi2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1842,10 +1937,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 = { @@ -1854,6 +1951,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, const 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, const 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, const 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, const 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); @@ -1870,6 +2195,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 00000000000..bf8ba35e04c --- /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, const 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 1ee7d88c226..f05be687483 100644 --- a/hw/ssi/ibex_spi_host.c +++ b/hw/ssi/ibex_spi_host.c @@ -154,7 +154,6 @@ static void ibex_spi_host_reset(DeviceState *dev) ibex_spi_txfifo_reset(s); s->init_status = true; - return; } /* @@ -205,9 +204,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)) { @@ -229,8 +229,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) @@ -559,16 +560,15 @@ static const MemoryRegionOps ibex_spi_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static Property ibex_spi_properties[] = { +static const Property ibex_spi_properties[] = { DEFINE_PROP_UINT32("num_cs", IbexSPIHostState, num_cs, 1), - DEFINE_PROP_END_OF_LIST(), }; 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), @@ -622,11 +622,11 @@ static void ibex_spi_host_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } -static void ibex_spi_host_class_init(ObjectClass *klass, void *data) +static void ibex_spi_host_class_init(ObjectClass *klass, const 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 189423bb3a5..1312f588d2e 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), @@ -475,13 +475,13 @@ static void imx_spi_realize(DeviceState *dev, Error **errp) fifo32_create(&s->rx_fifo, ECSPI_FIFO_SIZE); } -static void imx_spi_class_init(ObjectClass *klass, void *data) +static void imx_spi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 0aebcdd6142..6afb1ea2001 100644 --- a/hw/ssi/meson.build +++ b/hw/ssi/meson.build @@ -1,3 +1,4 @@ +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')) @@ -9,5 +10,6 @@ system_ss.add(when: 'CONFIG_XILINX_SPI', if_true: files('xilinx_spi.c')) system_ss.add(when: 'CONFIG_XILINX_SPIPS', if_true: files('xilinx_spips.c')) system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-ospi.c')) system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c')) -system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_spi.c')) system_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_spi_host.c')) +system_ss.add(when: 'CONFIG_BCM2835_SPI', if_true: files('bcm2835_spi.c')) +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 b2432c5a132..fd7ba7eeb04 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), @@ -398,12 +398,12 @@ static const VMStateDescription vmstate_mss_spi = { } }; -static void mss_spi_class_init(ObjectClass *klass, void *data) +static void mss_spi_class_init(ObjectClass *klass, const 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 4eedb2927e7..056ce13394f 100644 --- a/hw/ssi/npcm7xx_fiu.c +++ b/hw/ssi/npcm7xx_fiu.c @@ -29,7 +29,7 @@ #include "trace.h" /* Up to 128 MiB of flash may be accessed directly as memory. */ -#define NPCM7XX_FIU_FLASH_WINDOW_SIZE (128 * MiB) +#define NPCM7XX_FIU_MAX_FLASH_WINDOW_SIZE (128 * MiB) /* Each module has 4 KiB of register space. Only a fraction of it is used. */ #define NPCM7XX_FIU_CTRL_REGS_SIZE (4 * KiB) @@ -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; @@ -507,6 +507,17 @@ static void npcm7xx_fiu_realize(DeviceState *dev, Error **errp) return; } + if (s->flash_size == 0) { + error_setg(errp, "%s: flash size must be set", dev->canonical_path); + return; + } + + if (s->flash_size > NPCM7XX_FIU_MAX_FLASH_WINDOW_SIZE) { + error_setg(errp, "%s: flash size should not exceed 128 MiB", + dev->canonical_path); + return; + } + s->spi = ssi_create_bus(dev, "spi"); s->cs_lines = g_new0(qemu_irq, s->cs_count); qdev_init_gpio_out_named(DEVICE(s), s->cs_lines, "cs", s->cs_count); @@ -525,7 +536,7 @@ static void npcm7xx_fiu_realize(DeviceState *dev, Error **errp) flash->fiu = s; memory_region_init_io(&flash->direct_access, OBJECT(s), &npcm7xx_fiu_flash_ops, &s->flash[i], "flash", - NPCM7XX_FIU_FLASH_WINDOW_SIZE); + s->flash_size); sysbus_init_mmio(sbd, &flash->direct_access); } } @@ -534,19 +545,19 @@ 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(), }, }; -static Property npcm7xx_fiu_properties[] = { +static const Property npcm7xx_fiu_properties[] = { DEFINE_PROP_INT32("cs-count", NPCM7xxFIUState, cs_count, 0), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_SIZE("flash-size", NPCM7xxFIUState, flash_size, 0), }; -static void npcm7xx_fiu_class_init(ObjectClass *klass, void *data) +static void npcm7xx_fiu_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ssi/npcm_pspi.c b/hw/ssi/npcm_pspi.c index 3fb935043ab..a31dcc050e8 100644 --- a/hw/ssi/npcm_pspi.c +++ b/hw/ssi/npcm_pspi.c @@ -192,14 +192,14 @@ static const VMStateDescription vmstate_npcm_pspi = { .name = "npcm-pspi", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .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) +static void npcm_pspi_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ssi/omap_spi.c b/hw/ssi/omap_spi.c deleted file mode 100644 index 8f85c3e3918..00000000000 --- a/hw/ssi/omap_spi.c +++ /dev/null @@ -1,380 +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 = 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 = 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 8954ffebb1f..1dc0bcbcc04 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), @@ -292,11 +292,11 @@ static void pl022_realize(DeviceState *dev, Error **errp) s->ssi = ssi_create_bus(dev, "ssi"); } -static void pl022_class_init(ObjectClass *klass, void *data) +static void pl022_class_init(ObjectClass *klass, const 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 00000000000..f40e8836b9c --- /dev/null +++ b/hw/ssi/pnv_spi.c @@ -0,0 +1,1231 @@ +/* + * 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) +#define PNV_SPI_FIFO_SIZE 16 +#define RDR_MATCH_FAILURE_LIMIT 16 + +/* + * 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) + +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 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 if (!fifo8_is_empty(&s->rx_fifo)) { + byte = fifo8_pop(&s->rx_fifo); + trace_pnv_spi_shift_rx(byte, count); + s->regs[SPI_RCV_DATA_REG] = (s->regs[SPI_RCV_DATA_REG] << 8) | byte; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: Reading empty RX_FIFO\n"); + } + count++; + } /* end of while */ + return shift_in_count; +} + +static void spi_response(PnvSpi *s) +{ + uint8_t ecc_count; + uint8_t shift_in_count; + uint32_t rx_len; + int i; + + /* + * 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 + */ + rx_len = fifo8_num_used(&s->rx_fifo); + if (rx_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), rx_len); + } else { + uint8_t ecc_control; + trace_pnv_spi_rx_received(rx_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, s->N1_bytes, ecc_count, shift_in_count); + } + /* Handle the N2 portion of the frame */ + if (s->N2_rx != 0) { + /* pop out N1_bytes from rx_fifo if not already */ + if (s->N1_rx == 0) { + for (i = 0; i < s->N1_bytes; i++) { + if (!fifo8_is_empty(&s->rx_fifo)) { + fifo8_pop(&s->rx_fifo); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: Reading empty" + " RX_FIFO\n"); + } + } + } + trace_pnv_spi_rx_read_N2frame(); + shift_in_count = read_from_frame(s, 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) +{ + uint32_t tx, rx, payload_len; + uint8_t rx_byte; + + payload_len = fifo8_num_used(&s->tx_fifo); + 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 if (!fifo8_is_empty(&s->tx_fifo)) { + tx = (tx << 8) | fifo8_pop(&s->tx_fifo); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: TX_FIFO underflow\n"); + } + } + rx = ssi_transfer(s->ssi_bus, tx); + for (int i = 0; i < s->transfer_len; i++) { + if ((offset + i) >= payload_len) { + break; + } + rx_byte = (rx >> (8 * (s->transfer_len - 1) - i * 8)) & 0xFF; + if (!fifo8_is_full(&s->rx_fifo)) { + fifo8_push(&s->rx_fifo, rx_byte); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: RX_FIFO is full\n"); + break; + } + } + } + spi_response(s); + /* Reset fifo for next frame */ + fifo8_reset(&s->tx_fifo); + fifo8_reset(&s->rx_fifo); +} + +/* + * 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, bool send_n1_alone) +{ + uint8_t n1_count; + bool stop = false; + /* + * 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); + if (!fifo8_is_full(&s->tx_fifo)) { + trace_pnv_spi_tx_append("n1_byte", n1_byte, n1_count); + fifo8_push(&s->tx_fifo, n1_byte); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: TX_FIFO is full\n"); + break; + } + } 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 if (!fifo8_is_full(&s->tx_fifo)) { + trace_pnv_spi_tx_append_FF("n1_byte"); + fifo8_push(&s->tx_fifo, 0xff); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: TX_FIFO is full\n"); + break; + } + } + 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", fifo8_num_used(&s->tx_fifo)); + transfer(s); + /* 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; + } + 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) +{ + uint8_t n2_count; + bool stop = false; + /* + * 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)); + if (!fifo8_is_full(&s->tx_fifo)) { + trace_pnv_spi_tx_append("n2_byte", n2_byte, (s->N1_tx + n2_count)); + fifo8_push(&s->tx_fifo, n2_byte); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: TX_FIFO is full\n"); + break; + } + } else if (!fifo8_is_full(&s->tx_fifo)) { + /* + * 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"); + fifo8_push(&s->tx_fifo, 0xff); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: TX_FIFO is full\n"); + break; + } + 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", fifo8_num_used(&s->tx_fifo)); + transfer(s); + /* + * 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; + } + 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; + uint8_t seq_index; + + /* + * 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); + } + /* + * SPI_STS_SEQ_INDEX of status register is kept in seq_index variable and + * updated back to status register at the end of operation_sequencer(). + */ + seq_index = GETFIELD(SPI_STS_SEQ_INDEX, s->status); + /* + * There are only 8 possible operation IDs to iterate through though + * some operations may cause more than one frame to be sequenced. + */ + while (seq_index < NUM_SEQ_OPS) { + opcode = s->seq_op[seq_index]; + /* 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", seq_index); + + 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", seq_index); + /* + * 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); + seq_index++; + 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; + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, + SEQ_STATE_INDEX_INCREMENT); + } + 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", seq_index); + /* + * 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 ((seq_index != 7) && + PNV_SPI_MASKED_OPCODE(s->seq_op[(seq_index + 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, 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); + seq_index++; + } 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; + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, + SEQ_STATE_INDEX_INCREMENT); + } + } + 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", seq_index); + 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); + /* + * 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 */ + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, + SEQ_STATE_INDEX_INCREMENT); + } + } + 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", seq_index); + /* + * 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"); + s->fail_count = 0; + /* A match occurred, increment the sequencer index. */ + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, + SEQ_STATE_INDEX_INCREMENT); + } else { + trace_pnv_spi_RDR_match("failed"); + s->fail_count++; + /* + * Branch the sequencer to the index coded into the op + * code. + */ + seq_index = PNV_SPI_OPCODE_LO_NIBBLE(opcode); + } + if (s->fail_count >= RDR_MATCH_FAILURE_LIMIT) { + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi: RDR match failure" + " limit crossed %d times hence requesting " + "sequencer to stop.\n", + RDR_MATCH_FAILURE_LIMIT); + stop = true; + } + /* + * 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"); + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_INDEX_INCREMENT); + 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", seq_index); + /* + * 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. + */ + seq_index = PNV_SPI_OPCODE_LO_NIBBLE(opcode); + s->loop_counter_1++; + } else { + /* Continue to next index if loop counter is reached */ + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, + SEQ_STATE_INDEX_INCREMENT); + } + 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", seq_index); + 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. + */ + seq_index = PNV_SPI_OPCODE_LO_NIBBLE(opcode); + s->loop_counter_2++; + } else { + /* Continue to next index if loop counter is reached */ + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, + SEQ_STATE_INDEX_INCREMENT); + } + break; + + default: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + /* Ignore unsupported operations. */ + seq_index++; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_INDEX_INCREMENT); + 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 (seq_index == NUM_SEQ_OPS) { + /* All 8 opcodes completed, sequencer idling */ + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE); + seq_index = 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 */ + /* Update sequencer index field in status.*/ + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, seq_index); +} /* 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); + } +} + +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 const Property pnv_spi_properties[] = { + DEFINE_PROP_UINT32("spic_num", PnvSpi, spic_num, 0), + DEFINE_PROP_UINT32("chip-id", PnvSpi, chip_id, 0), + DEFINE_PROP_UINT8("transfer_len", PnvSpi, transfer_len, 4), +}; + +static void pnv_spi_realize(DeviceState *dev, Error **errp) +{ + PnvSpi *s = PNV_SPI(dev); + g_autofree char *name = g_strdup_printf("chip%d." TYPE_PNV_SPI_BUS ".%d", + s->chip_id, 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); + + fifo8_create(&s->tx_fifo, PNV_SPI_FIFO_SIZE); + fifo8_create(&s->rx_fifo, PNV_SPI_FIFO_SIZE); + + /* 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, const 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 = (const 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 1b4a401ca18..3e01fef61cd 100644 --- a/hw/ssi/sifive_spi.c +++ b/hw/ssi/sifive_spi.c @@ -328,17 +328,16 @@ static void sifive_spi_realize(DeviceState *dev, Error **errp) fifo8_create(&s->rx_fifo, FIFO_CAPACITY); } -static Property sifive_spi_properties[] = { +static const Property sifive_spi_properties[] = { DEFINE_PROP_UINT32("num-cs", SiFiveSPIState, num_cs, 1), - DEFINE_PROP_END_OF_LIST(), }; -static void sifive_spi_class_init(ObjectClass *klass, void *data) +static void sifive_spi_class_init(ObjectClass *klass, const 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 1f3e540ab8a..d0de640fe64 100644 --- a/hw/ssi/ssi.c +++ b/hw/ssi/ssi.c @@ -55,7 +55,7 @@ static bool ssi_bus_check_address(BusState *b, DeviceState *dev, Error **errp) return true; } -static void ssi_bus_class_init(ObjectClass *klass, void *data) +static void ssi_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); @@ -108,12 +108,11 @@ static void ssi_peripheral_realize(DeviceState *dev, Error **errp) ssc->realize(s, errp); } -static Property ssi_peripheral_properties[] = { +static const 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) +static void ssi_peripheral_class_init(ObjectClass *klass, const void *data) { SSIPeripheralClass *ssc = SSI_PERIPHERAL_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -172,7 +171,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 cd6e8443db3..871d57324d1 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), @@ -202,11 +202,11 @@ static void stm32f2xx_spi_init(Object *obj) s->ssi = ssi_create_bus(dev, "ssi"); } -static void stm32f2xx_spi_class_init(ObjectClass *klass, void *data) +static void stm32f2xx_spi_class_init(ObjectClass *klass, const 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 2d5bd2b83d0..2f36cf96b8b 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" @@ -32,3 +32,34 @@ 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 d4de2e7aabc..4144c8a6270 100644 --- a/hw/ssi/xilinx_spi.c +++ b/hw/ssi/xilinx_spi.c @@ -25,6 +25,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/module.h" @@ -32,6 +33,7 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" #include "hw/ssi/ssi.h" #include "qom/object.h" @@ -83,6 +85,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(XilinxSPI, XILINX_SPI) struct XilinxSPI { SysBusDevice parent_obj; + EndianMode model_endianness; MemoryRegion mmio; qemu_irq irq; @@ -313,14 +316,17 @@ spi_write(void *opaque, hwaddr addr, xlx_spi_update_irq(s); } -static const MemoryRegionOps spi_ops = { - .read = spi_read, - .write = spi_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } +static const MemoryRegionOps spi_ops[2] = { + [0 ... 1] = { + .read = spi_read, + .write = spi_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + }, + [0].endianness = DEVICE_LITTLE_ENDIAN, + [1].endianness = DEVICE_BIG_ENDIAN, }; static void xilinx_spi_realize(DeviceState *dev, Error **errp) @@ -329,6 +335,12 @@ static void xilinx_spi_realize(DeviceState *dev, Error **errp) XilinxSPI *s = XILINX_SPI(dev); int i; + if (s->model_endianness == ENDIAN_MODE_UNSPECIFIED) { + error_setg(errp, TYPE_XILINX_SPI " property 'endianness'" + " must be set to 'big' or 'little'"); + return; + } + DB_PRINT("\n"); s->spi = ssi_create_bus(dev, "spi"); @@ -339,7 +351,8 @@ static void xilinx_spi_realize(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->cs_lines[i]); } - memory_region_init_io(&s->mmio, OBJECT(s), &spi_ops, s, + memory_region_init_io(&s->mmio, OBJECT(s), + &spi_ops[s->model_endianness == ENDIAN_MODE_BIG], s, "xilinx-spi", R_MAX * 4); sysbus_init_mmio(sbd, &s->mmio); @@ -353,7 +366,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), @@ -361,17 +374,17 @@ static const VMStateDescription vmstate_xilinx_spi = { } }; -static Property xilinx_spi_properties[] = { +static const Property xilinx_spi_properties[] = { + DEFINE_PROP_ENDIAN_NODEFAULT("endianness", XilinxSPI, model_endianness), DEFINE_PROP_UINT8("num-ss-bits", XilinxSPI, num_cs, 1), - DEFINE_PROP_END_OF_LIST(), }; -static void xilinx_spi_class_init(ObjectClass *klass, void *data) +static void xilinx_spi_class_init(ObjectClass *klass, const 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 97009d3a5d0..a79f3b8e494 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -33,7 +33,7 @@ #include "hw/ssi/xilinx_spips.h" #include "qapi/error.h" #include "hw/register.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "migration/blocker.h" #include "migration/vmstate.h" @@ -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; @@ -973,6 +975,8 @@ static void xilinx_spips_write(void *opaque, hwaddr addr, 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), @@ -1416,54 +1420,55 @@ static const VMStateDescription vmstate_xlnx_zynqmp_qspips = { } }; -static Property xilinx_zynqmp_qspips_properties[] = { +static const Property xilinx_zynqmp_qspips_properties[] = { DEFINE_PROP_UINT32("dma-burst-size", XlnxZynqMPQSPIPS, dma_burst_size, 64), - DEFINE_PROP_END_OF_LIST(), }; -static Property xilinx_spips_properties[] = { +static const Property xilinx_spips_properties[] = { DEFINE_PROP_UINT8("num-busses", XilinxSPIPS, num_busses, 1), DEFINE_PROP_UINT8("num-ss-bits", XilinxSPIPS, num_cs, 4), DEFINE_PROP_UINT8("num-txrx-bytes", XilinxSPIPS, num_txrx_bytes, 1), - DEFINE_PROP_END_OF_LIST(), }; -static void xilinx_qspips_class_init(ObjectClass *klass, void * data) +static void xilinx_qspips_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); XilinxSPIPSClass *xsc = XILINX_SPIPS_CLASS(klass); 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; } -static void xilinx_spips_class_init(ObjectClass *klass, void *data) +static void xilinx_spips_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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; } -static void xlnx_zynqmp_qspips_class_init(ObjectClass *klass, void * data) +static void xlnx_zynqmp_qspips_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 c762e0b3679..56d51ce0e30 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), @@ -1819,18 +1825,17 @@ static const VMStateDescription vmstate_xlnx_versal_ospi = { } }; -static Property xlnx_versal_ospi_properties[] = { +static const Property xlnx_versal_ospi_properties[] = { DEFINE_PROP_BOOL("dac-with-indac", XlnxVersalOspi, dac_with_indac, false), DEFINE_PROP_BOOL("indac-write-disabled", XlnxVersalOspi, ind_write_disabled, false), - DEFINE_PROP_END_OF_LIST(), }; -static void xlnx_versal_ospi_class_init(ObjectClass *klass, void *data) +static void xlnx_versal_ospi_class_init(ObjectClass *klass, const 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 010be7ed1f5..b3d823ce2c3 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -12,18 +12,23 @@ config A9_GTIMER config HPET bool default y if PC + # The HPET has both a Rust and a C implementation + select HPET_C if !HAVE_RUST + select X_HPET_RUST if HAVE_RUST + +config HPET_C + bool config I8254 bool depends on ISA_BUS -config ALTERA_TIMER +config ALLWINNER_A10_PIT bool select PTIMER -config ALLWINNER_A10_PIT +config PXA2XX_TIMER bool - select PTIMER config SIFIVE_PWM bool diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index 5e959b6d09f..690140f5a6c 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 "system/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,24 +367,23 @@ static const VMStateDescription vmstate_a9_gtimer = { A9GTimerPerCPU), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_a9_gtimer_control, NULL } }; -static Property a9_gtimer_properties[] = { +static const Property a9_gtimer_properties[] = { DEFINE_PROP_UINT32("num-cpu", A9GTimerState, num_cpu, 0), - DEFINE_PROP_END_OF_LIST() }; -static void a9_gtimer_class_init(ObjectClass *klass, void *data) +static void a9_gtimer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 971f78462ab..e4c353273a1 100644 --- a/hw/timer/allwinner-a10-pit.c +++ b/hw/timer/allwinner-a10-pit.c @@ -185,22 +185,21 @@ static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value, static const MemoryRegionOps a10_pit_ops = { .read = a10_pit_read, .write = a10_pit_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; -static Property a10_pit_properties[] = { +static const Property a10_pit_properties[] = { DEFINE_PROP_UINT32("clk0-freq", AwA10PITState, clk_freq[0], 0), DEFINE_PROP_UINT32("clk1-freq", AwA10PITState, clk_freq[1], 0), DEFINE_PROP_UINT32("clk2-freq", AwA10PITState, clk_freq[2], 0), DEFINE_PROP_UINT32("clk3-freq", AwA10PITState, clk_freq[3], 0), - DEFINE_PROP_END_OF_LIST(), }; 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), @@ -289,11 +288,11 @@ static void a10_pit_finalize(Object *obj) } } -static void a10_pit_class_init(ObjectClass *klass, void *data) +static void a10_pit_class_init(ObjectClass *klass, const 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 0f1f54206a3..00000000000 --- 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 cdfca3000be..7cc5915e9ec 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,25 +293,24 @@ 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() } }; -static Property arm_mptimer_properties[] = { +static const Property arm_mptimer_properties[] = { DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0), - DEFINE_PROP_END_OF_LIST() }; -static void arm_mptimer_class_init(ObjectClass *klass, void *data) +static void arm_mptimer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 69c88634722..56638ff5cdc 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() } @@ -387,13 +387,12 @@ static const TypeInfo icp_pit_info = { .instance_init = icp_pit_init, }; -static Property sp804_properties[] = { +static const Property sp804_properties[] = { DEFINE_PROP_UINT32("freq0", SP804State, freq0, 1000000), DEFINE_PROP_UINT32("freq1", SP804State, freq1, 1000000), - DEFINE_PROP_END_OF_LIST(), }; -static void sp804_class_init(ObjectClass *klass, void *data) +static void sp804_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); diff --git a/hw/timer/armv7m_systick.c b/hw/timer/armv7m_systick.c index 5dfe39afe36..7e4ddcd4058 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), @@ -285,12 +285,12 @@ static const VMStateDescription vmstate_systick = { } }; -static void systick_class_init(ObjectClass *klass, void *data) +static void systick_class_init(ObjectClass *klass, const 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 9c20b3d6ad8..57db03512f4 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); @@ -239,9 +239,8 @@ static uint64_t aspeed_timer_get_value(AspeedTimer *t, int reg) return value; } -static uint64_t aspeed_timer_read(void *opaque, hwaddr offset, unsigned size) +static uint64_t aspeed_timer_read_common(AspeedTimerCtrlState *s, hwaddr offset) { - AspeedTimerCtrlState *s = opaque; const int reg = (offset & 0xf) / 4; uint64_t value; @@ -256,10 +255,11 @@ static uint64_t aspeed_timer_read(void *opaque, hwaddr offset, unsigned size) value = aspeed_timer_get_value(&s->timers[(offset >> 4) - 1], reg); break; default: - value = ASPEED_TIMER_GET_CLASS(s)->read(s, offset); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + value = 0; break; } - trace_aspeed_timer_read(offset, size, value); return value; } @@ -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 @@ -428,12 +431,11 @@ static void aspeed_timer_set_ctrl2(AspeedTimerCtrlState *s, uint32_t value) trace_aspeed_timer_set_ctrl2(value); } -static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value, - unsigned size) +static void aspeed_timer_write_common(AspeedTimerCtrlState *s, hwaddr offset, + uint64_t value) { const uint32_t tv = (uint32_t)(value & 0xFFFFFFFF); const int reg = (offset & 0xf) / 4; - AspeedTimerCtrlState *s = opaque; switch (offset) { /* Control Registers */ @@ -448,11 +450,25 @@ static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value, aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS) - 1, reg, tv); break; default: - ASPEED_TIMER_GET_CLASS(s)->write(s, offset, value); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); break; } } +static uint64_t aspeed_timer_read(void *opaque, hwaddr offset, unsigned size) +{ + AspeedTimerCtrlState *s = ASPEED_TIMER(opaque); + return ASPEED_TIMER_GET_CLASS(s)->read(s, offset); +} + +static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + AspeedTimerCtrlState *s = ASPEED_TIMER(opaque); + ASPEED_TIMER_GET_CLASS(s)->write(s, offset, value); +} + static const MemoryRegionOps aspeed_timer_ops = { .read = aspeed_timer_read, .write = aspeed_timer_write, @@ -472,12 +488,15 @@ static uint64_t aspeed_2400_timer_read(AspeedTimerCtrlState *s, hwaddr offset) break; case 0x38: case 0x3C: - default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); value = 0; break; + default: + value = aspeed_timer_read_common(s, offset); + break; } + trace_aspeed_timer_read(offset, value); return value; } @@ -492,10 +511,12 @@ static void aspeed_2400_timer_write(AspeedTimerCtrlState *s, hwaddr offset, break; case 0x38: case 0x3C: - default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); break; + default: + aspeed_timer_write_common(s, offset, value); + break; } } @@ -511,12 +532,15 @@ static uint64_t aspeed_2500_timer_read(AspeedTimerCtrlState *s, hwaddr offset) value = s->ctrl3 & BIT(0); break; case 0x3C: - default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); value = 0; break; + default: + value = aspeed_timer_read_common(s, offset); + break; } + trace_aspeed_timer_read(offset, value); return value; } @@ -545,8 +569,7 @@ static void aspeed_2500_timer_write(AspeedTimerCtrlState *s, hwaddr offset, break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", - __func__, offset); + aspeed_timer_write_common(s, offset, value); break; } } @@ -561,12 +584,15 @@ static uint64_t aspeed_2600_timer_read(AspeedTimerCtrlState *s, hwaddr offset) break; case 0x38: case 0x3C: - default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); value = 0; break; + default: + value = aspeed_timer_read_common(s, offset); + break; } + trace_aspeed_timer_read(offset, value); return value; } @@ -577,17 +603,209 @@ 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", __func__, offset); break; + default: + aspeed_timer_write_common(s, offset, value); + break; + } +} + +static void aspeed_2700_timer_set_ctrl(AspeedTimerCtrlState *s, int index, + uint32_t reg) +{ + const uint8_t overflow_interrupt_mask = BIT(op_overflow_interrupt); + const uint8_t external_clock_mask = BIT(op_external_clock); + const uint8_t pulse_enable_mask = BIT(op_pulse_enable); + const uint8_t enable_mask = BIT(op_enable); + AspeedTimer *t; + uint8_t t_old; + uint8_t t_new; + int shift; + + /* + * Only 1 will set the specific bits to 1 + * Handle a dependency between the 'enable' and remaining three + * configuration bits - i.e. if more than one bit in the control set has + * set, including the 'enable' bit, perform configuration and then + * enable the timer. + * Interrupt Status bit should not be set. + */ + + t = &s->timers[index]; + shift = index * TIMER_CTRL_BITS; + + t_old = (s->ctrl >> shift) & TIMER_CTRL_MASK; + t_new = reg & TIMER_CTRL_MASK; + + if (!(t_old & external_clock_mask) && + (t_new & external_clock_mask)) { + aspeed_timer_ctrl_external_clock(t, true); + s->ctrl = deposit32(s->ctrl, shift + op_external_clock, 1, 1); + } + + if (!(t_old & overflow_interrupt_mask) && + (t_new & overflow_interrupt_mask)) { + aspeed_timer_ctrl_overflow_interrupt(t, true); + s->ctrl = deposit32(s->ctrl, shift + op_overflow_interrupt, 1, 1); + } + + + if (!(t_old & pulse_enable_mask) && + (t_new & pulse_enable_mask)) { + aspeed_timer_ctrl_pulse_enable(t, true); + s->ctrl = deposit32(s->ctrl, shift + op_pulse_enable, 1, 1); + } + + /* If we are enabling, do so last */ + if (!(t_old & enable_mask) && + (t_new & enable_mask)) { + aspeed_timer_ctrl_enable(t, true); + s->ctrl = deposit32(s->ctrl, shift + op_enable, 1, 1); + } +} + +static void aspeed_2700_timer_clear_ctrl(AspeedTimerCtrlState *s, int index, + uint32_t reg) +{ + const uint8_t overflow_interrupt_mask = BIT(op_overflow_interrupt); + const uint8_t external_clock_mask = BIT(op_external_clock); + const uint8_t pulse_enable_mask = BIT(op_pulse_enable); + const uint8_t enable_mask = BIT(op_enable); + AspeedTimer *t; + uint8_t t_old; + uint8_t t_new; + int shift; + + /* + * Only 1 will clear the specific bits to 0 + * Handle a dependency between the 'enable' and remaining three + * configuration bits - i.e. if more than one bit in the control set has + * clear, including the 'enable' bit, then disable the timer and perform + * configuration + */ + + t = &s->timers[index]; + shift = index * TIMER_CTRL_BITS; + + t_old = (s->ctrl >> shift) & TIMER_CTRL_MASK; + t_new = reg & TIMER_CTRL_MASK; + + /* If we are disabling, do so first */ + if ((t_old & enable_mask) && + (t_new & enable_mask)) { + aspeed_timer_ctrl_enable(t, false); + s->ctrl = deposit32(s->ctrl, shift + op_enable, 1, 0); + } + + if ((t_old & external_clock_mask) && + (t_new & external_clock_mask)) { + aspeed_timer_ctrl_external_clock(t, false); + s->ctrl = deposit32(s->ctrl, shift + op_external_clock, 1, 0); + } + + if ((t_old & overflow_interrupt_mask) && + (t_new & overflow_interrupt_mask)) { + aspeed_timer_ctrl_overflow_interrupt(t, false); + s->ctrl = deposit32(s->ctrl, shift + op_overflow_interrupt, 1, 0); + } + + if ((t_old & pulse_enable_mask) && + (t_new & pulse_enable_mask)) { + aspeed_timer_ctrl_pulse_enable(t, false); + s->ctrl = deposit32(s->ctrl, shift + op_pulse_enable, 1, 0); + } + + /* Clear interrupt status */ + if (reg & 0x10000) { + s->irq_sts = deposit32(s->irq_sts, index, 1, 0); + } +} + +static uint64_t aspeed_2700_timer_read(AspeedTimerCtrlState *s, hwaddr offset) +{ + uint32_t timer_offset = offset & 0x3f; + int timer_index = offset >> 6; + uint64_t value = 0; + + if (timer_index >= ASPEED_TIMER_NR_TIMERS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offset 0x%" PRIx64 " out of bounds\n", + __func__, offset); + return 0; + } + + switch (timer_offset) { + /* + * Counter Status + * Counter Reload + * Counter First Matching + * Counter Second Matching + */ + case 0x00 ... 0x0C: + value = aspeed_timer_get_value(&s->timers[timer_index], + timer_offset >> 2); + break; + /* Counter Control and Interrupt Status */ + case 0x10: + value = deposit64(value, 0, 4, + extract32(s->ctrl, timer_index * 4, 4)); + value = deposit64(value, 16, 1, + extract32(s->irq_sts, timer_index, 1)); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%" + PRIx64"\n", __func__, offset); + value = 0; + break; + } + trace_aspeed_timer_read(offset, value); + return value; +} + +static void aspeed_2700_timer_write(AspeedTimerCtrlState *s, hwaddr offset, + uint64_t value) +{ + const uint32_t timer_value = (uint32_t)(value & 0xFFFFFFFF); + uint32_t timer_offset = offset & 0x3f; + int timer_index = offset >> 6; + + if (timer_index >= ASPEED_TIMER_NR_TIMERS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offset 0x%" PRIx64 " out of bounds\n", + __func__, offset); + } + + switch (timer_offset) { + /* + * Counter Status + * Counter Reload + * Counter First Matching + * Counter Second Matching + */ + case 0x00 ... 0x0C: + aspeed_timer_set_value(s, timer_index, timer_offset >> 2, + timer_value); + break; + /* Counter Control Set and Interrupt Status */ + case 0x10: + aspeed_2700_timer_set_ctrl(s, timer_index, timer_value); + break; + /* Counter Control Clear and Interrupr Status */ + case 0x14: + aspeed_2700_timer_clear_ctrl(s, timer_index, timer_value); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%" + PRIx64"\n", __func__, offset); + break; } } @@ -623,7 +841,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 +864,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 +878,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), @@ -671,18 +890,17 @@ static const VMStateDescription vmstate_aspeed_timer_state = { } }; -static Property aspeed_timer_properties[] = { +static const Property aspeed_timer_properties[] = { DEFINE_PROP_LINK("scu", AspeedTimerCtrlState, scu, TYPE_ASPEED_SCU, AspeedSCUState *), - DEFINE_PROP_END_OF_LIST(), }; -static void timer_class_init(ObjectClass *klass, void *data) +static void timer_class_init(ObjectClass *klass, const 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); @@ -697,7 +915,7 @@ static const TypeInfo aspeed_timer_info = { .abstract = true, }; -static void aspeed_2400_timer_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass); @@ -713,7 +931,7 @@ static const TypeInfo aspeed_2400_timer_info = { .class_init = aspeed_2400_timer_class_init, }; -static void aspeed_2500_timer_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass); @@ -729,7 +947,7 @@ static const TypeInfo aspeed_2500_timer_info = { .class_init = aspeed_2500_timer_class_init, }; -static void aspeed_2600_timer_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass); @@ -745,7 +963,7 @@ static const TypeInfo aspeed_2600_timer_info = { .class_init = aspeed_2600_timer_class_init, }; -static void aspeed_1030_timer_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass); @@ -761,6 +979,22 @@ static const TypeInfo aspeed_1030_timer_info = { .class_init = aspeed_1030_timer_class_init, }; +static void aspeed_2700_timer_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass); + + dc->desc = "ASPEED 2700 Timer"; + awc->read = aspeed_2700_timer_read; + awc->write = aspeed_2700_timer_write; +} + +static const TypeInfo aspeed_2700_timer_info = { + .name = TYPE_ASPEED_2700_TIMER, + .parent = TYPE_ASPEED_TIMER, + .class_init = aspeed_2700_timer_class_init, +}; + static void aspeed_timer_register_types(void) { type_register_static(&aspeed_timer_info); @@ -768,6 +1002,7 @@ static void aspeed_timer_register_types(void) type_register_static(&aspeed_2500_timer_info); type_register_static(&aspeed_2600_timer_info); type_register_static(&aspeed_1030_timer_info); + type_register_static(&aspeed_2700_timer_info); } type_init(aspeed_timer_register_types) diff --git a/hw/timer/avr_timer16.c b/hw/timer/avr_timer16.c index c48555da525..012d8290018 100644 --- a/hw/timer/avr_timer16.c +++ b/hw/timer/avr_timer16.c @@ -542,11 +542,10 @@ static const MemoryRegionOps avr_timer16_ifr_ops = { .impl = {.max_access_size = 1} }; -static Property avr_timer16_properties[] = { +static const Property avr_timer16_properties[] = { DEFINE_PROP_UINT8("id", struct AVRTimer16State, id, 0), DEFINE_PROP_UINT64("cpu-frequency-hz", struct AVRTimer16State, cpu_freq_hz, 0), - DEFINE_PROP_END_OF_LIST(), }; static void avr_timer16_pr(void *opaque, int irq, int level) @@ -596,11 +595,11 @@ static void avr_timer16_realize(DeviceState *dev, Error **errp) s->enabled = true; } -static void avr_timer16_class_init(ObjectClass *klass, void *data) +static void avr_timer16_class_init(ObjectClass *klass, const 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 67669a57ff3..7929aaa882a 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), @@ -154,12 +154,12 @@ static const VMStateDescription bcm2835_systmr_vmstate = { } }; -static void bcm2835_systmr_class_init(ObjectClass *klass, void *data) +static void bcm2835_systmr_class_init(ObjectClass *klass, const 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 e57a0f5f09f..9c7ba168768 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), @@ -451,7 +451,7 @@ static const VMStateDescription vmstate_cadence_ttc = { } }; -static void cadence_ttc_class_init(ObjectClass *klass, void *data) +static void cadence_ttc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/cmsdk-apb-dualtimer.c b/hw/timer/cmsdk-apb-dualtimer.c index d4a509c798e..34c550a3f98 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, @@ -534,13 +534,13 @@ static const VMStateDescription cmsdk_apb_dualtimer_vmstate = { } }; -static void cmsdk_apb_dualtimer_class_init(ObjectClass *klass, void *data) +static void cmsdk_apb_dualtimer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 68aa1a76360..4095267b4aa 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), @@ -261,13 +261,13 @@ static const VMStateDescription cmsdk_apb_timer_vmstate = { } }; -static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data) +static void cmsdk_apb_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 973eab4386e..355138d3547 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), @@ -161,11 +161,11 @@ static void digic_timer_finalize(Object *obj) ptimer_free(s->ptimer); } -static void digic_timer_class_init(ObjectClass *klass, void *class_data) +static void digic_timer_class_init(ObjectClass *klass, const 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 2d6d92ef936..00000000000 --- a/hw/timer/etraxfs_timer.c +++ /dev/null @@ -1,407 +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 " HWADDR_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 446bbd2b96c..bb0f9c8b9a6 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), @@ -815,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; } @@ -1546,11 +1546,11 @@ static void exynos4210_mct_finalize(Object *obj) } } -static void exynos4210_mct_class_init(ObjectClass *klass, void *data) +static void exynos4210_mct_class_init(ObjectClass *klass, const 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 3528d0f33ab..69f737a8e6f 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), @@ -420,11 +420,11 @@ static void exynos4210_pwm_finalize(Object *obj) } } -static void exynos4210_pwm_class_init(ObjectClass *klass, void *data) +static void exynos4210_pwm_class_init(ObjectClass *klass, const 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 5c4923c1e09..0e06fa09e99 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" @@ -401,19 +403,18 @@ static void grlib_gptimer_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &unit->iomem); } -static Property grlib_gptimer_properties[] = { +static const Property grlib_gptimer_properties[] = { DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000), DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8), DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2), - DEFINE_PROP_END_OF_LIST(), }; -static void grlib_gptimer_class_init(ObjectClass *klass, void *data) +static void grlib_gptimer_class_init(ObjectClass *klass, const 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 6998094233a..cb48cc151f1 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -25,7 +25,6 @@ */ #include "qemu/osdep.h" -#include "hw/i386/pc.h" #include "hw/irq.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -37,15 +36,11 @@ #include "hw/rtc/mc146818rtc_regs.h" #include "migration/vmstate.h" #include "hw/timer/i8254.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qom/object.h" +#include "trace.h" -//#define HPET_DEBUG -#ifdef HPET_DEBUG -#define DPRINTF printf -#else -#define DPRINTF(...) -#endif +struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX}; #define HPET_MSI_SUPPORT 0 @@ -61,10 +56,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 { @@ -80,6 +77,7 @@ struct HPETState { uint8_t rtc_irq_level; qemu_irq pit_enabled; uint8_t num_timers; + uint8_t num_timers_save; uint32_t intcap; HPETTimer timer[HPET_MAX_TIMERS]; @@ -122,11 +120,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); } @@ -163,29 +156,34 @@ static uint64_t hpet_get_ticks(HPETState *s) return ns_to_ticks(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->hpet_offset); } +static uint64_t hpet_get_ns(HPETState *s, uint64_t tick) +{ + return ticks_to_ns(tick) - s->hpet_offset; +} + /* - * calculate diff between comparator value and current ticks + * 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 inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current) +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; @@ -203,21 +201,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]); } } @@ -230,15 +238,12 @@ static int hpet_pre_save(void *opaque) s->hpet_counter = hpet_get_ticks(s); } - return 0; -} - -static int hpet_pre_load(void *opaque) -{ - HPETState *s = opaque; - - /* version 1 only supports 3, later versions will load the actual value */ - s->num_timers = HPET_MIN_TIMERS; + /* + * The number of timers must match on source and destination, but it was + * also added to the migration stream. Check that it matches the value + * that was configured. + */ + s->num_timers_save = s->num_timers; return 0; } @@ -246,34 +251,25 @@ static bool hpet_validate_num_timers(void *opaque, int version_id) { HPETState *s = opaque; - if (s->num_timers < HPET_MIN_TIMERS) { - return false; - } else if (s->num_timers > HPET_MAX_TIMERS) { - return false; - } - return true; + return s->num_timers == s->num_timers_save; } 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) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); } - /* Push number of timers into capability returned via HPET_ID */ - s->capability &= ~HPET_ID_NUM_TIM_MASK; - s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT; - hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; - - /* Derive HPET_MSI_SUPPORT from the capability of the first timer. */ - s->flags &= ~(1 << HPET_MSI_SUPPORT); - if (s->timer[0].config & HPET_TN_FSB_CAP) { - s->flags |= 1 << HPET_MSI_SUPPORT; - } return 0; } @@ -296,7 +292,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() } @@ -307,7 +303,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() } @@ -317,7 +313,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), @@ -332,35 +328,37 @@ static const VMStateDescription vmstate_hpet_timer = { static const VMStateDescription vmstate_hpet = { .name = "hpet", .version_id = 2, - .minimum_version_id = 1, + .minimum_version_id = 2, .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), - VMSTATE_UINT8_V(num_timers, HPETState, 2), - VMSTATE_VALIDATE("num_timers in range", hpet_validate_num_timers), - VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0, + VMSTATE_UINT8(num_timers_save, HPETState), + VMSTATE_VALIDATE("num_timers must match", hpet_validate_num_timers), + VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers_save, 0, 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); } /* @@ -369,128 +367,106 @@ 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; - DPRINTF("qemu: Enter hpet_ram_readl at %" PRIx64 "\n", addr); - index = addr; - /*address range of all TN regs*/ - if (index >= 0x100 && index <= 0x3ff) { - uint8_t timer_id = (addr - 0x100) / 0x20; - HPETTimer *timer = &s->timer[timer_id]; + trace_hpet_ram_read(addr); + addr &= ~4; - if (timer_id > s->num_timers) { - DPRINTF("qemu: timer id out of range\n"); - return 0; - } - - switch ((addr - 0x100) % 0x20) { - case HPET_TN_CFG: - return timer->config; - case HPET_TN_CFG + 4: // Interrupt capabilities - return timer->config >> 32; - case HPET_TN_CMP: // comparator register - return timer->cmp; - case HPET_TN_CMP + 4: - return timer->cmp >> 32; - case HPET_TN_ROUTE: - return timer->fsb; - case HPET_TN_ROUTE + 4: - return timer->fsb >> 32; - default: - DPRINTF("qemu: invalid hpet_ram_readl\n"); - break; - } - } else { - switch (index) { - case HPET_ID: - return s->capability; - case HPET_PERIOD: - return s->capability >> 32; + /*address range of all global regs*/ + if (addr <= 0xff) { + switch (addr) { + 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: + trace_hpet_ram_read_invalid(); + break; + } + } else { + uint8_t timer_id = (addr - 0x100) / 0x20; + HPETTimer *timer = &s->timer[timer_id]; + + if (timer_id > s->num_timers) { + trace_hpet_timer_id_out_of_range(timer_id); + return 0; + } + + switch (addr & 0x1f) { + case HPET_TN_CFG: // including interrupt capabilities + return timer->config >> shift; + case HPET_TN_CMP: // comparator register + return timer->cmp >> shift; + case HPET_TN_ROUTE: + return timer->fsb >> shift; default: - DPRINTF("qemu: invalid hpet_ram_readl\n"); + trace_hpet_ram_read_invalid(); break; } } @@ -502,114 +478,32 @@ static void hpet_ram_write(void *opaque, hwaddr addr, { int i; HPETState *s = opaque; - uint64_t old_val, new_val, val, index; - - 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; + int shift = (addr & 4) * 8; + int len = MIN(size * 8, 64 - shift); + uint64_t old_val, new_val, cleared; - /*address range of all TN regs*/ - if (index >= 0x100 && index <= 0x3ff) { - uint8_t timer_id = (addr - 0x100) / 0x20; - HPETTimer *timer = &s->timer[timer_id]; + trace_hpet_ram_write(addr, value); + addr &= ~4; - DPRINTF("qemu: hpet_ram_writel timer_id = 0x%x\n", timer_id); - if (timer_id > s->num_timers) { - DPRINTF("qemu: timer id out of range\n"); - return; - } - switch ((addr - 0x100) % 0x20) { - case HPET_TN_CFG: - DPRINTF("qemu: hpet_ram_writel HPET_TN_CFG\n"); - if (activating_bit(old_val, new_val, HPET_TN_FSB_ENABLE)) { - update_irq(timer, 0); - } - val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); - timer->config = (timer->config & 0xffffffff00000000ULL) | val; - 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)) { - 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; - } - if (!timer_is_periodic(timer) - || (timer->config & HPET_TN_SETVAL)) { - timer->cmp = (timer->cmp & 0xffffffff00000000ULL) | new_val; - } - 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->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); - break; - default: - DPRINTF("qemu: invalid hpet_ram_writel\n"); - break; - } - return; - } else { - switch (index) { + /*address range of all global regs*/ + if (addr <= 0xff) { + switch (addr) { 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. */ @@ -630,39 +524,91 @@ 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); + s->hpet_counter = deposit64(s->hpet_counter, shift, len, value); break; - case HPET_COUNTER + 4: + default: + trace_hpet_ram_write_invalid(); + break; + } + } else { + uint8_t timer_id = (addr - 0x100) / 0x20; + HPETTimer *timer = &s->timer[timer_id]; + + trace_hpet_ram_write_timer_id(timer_id); + if (timer_id > s->num_timers) { + trace_hpet_timer_id_out_of_range(timer_id); + return; + } + switch (addr & 0x18) { + case HPET_TN_CFG: + 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); + } + 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 (hpet_enabled(s)) { + hpet_set_timer(timer); + } + break; + case HPET_TN_CMP: // comparator register + if (timer->config & HPET_TN_32BIT) { + /* 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 = deposit64(timer->cmp, shift, len, value); + } + if (timer_is_periodic(timer)) { + timer->period = deposit64(timer->period, shift, len, value); + } + timer->config &= ~HPET_TN_SETVAL; if (hpet_enabled(s)) { - DPRINTF("qemu: Writing counter while HPET enabled!\n"); + hpet_set_timer(timer); } - 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); + break; + case HPET_TN_ROUTE: + timer->fsb = deposit64(timer->fsb, shift, len, value); break; default: - DPRINTF("qemu: invalid hpet_ram_writel\n"); + trace_hpet_ram_write_invalid(); break; } + return; } } @@ -671,7 +617,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, }; @@ -701,8 +651,8 @@ static void hpet_reset(DeviceState *d) s->hpet_counter = 0ULL; s->hpet_offset = 0ULL; s->config = 0ULL; - hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; - hpet_cfg.hpet[s->hpet_id].address = sbd->mmio[0].addr; + hpet_fw_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; + hpet_fw_cfg.hpet[s->hpet_id].address = sbd->mmio[0].addr; /* to document that the RTC lowers its output on reset as well */ s->rtc_irq_level = 0; @@ -741,30 +691,31 @@ static void hpet_realize(DeviceState *dev, Error **errp) int i; HPETTimer *timer; + if (s->num_timers < HPET_MIN_TIMERS || s->num_timers > HPET_MAX_TIMERS) { + error_setg(errp, "hpet.num_timers must be between %d and %d", + HPET_MIN_TIMERS, HPET_MAX_TIMERS); + return; + } if (!s->intcap) { - warn_report("Hpet's intcap not initialized"); + error_setg(errp, "hpet.hpet-intcap not initialized"); + return; } - if (hpet_cfg.count == UINT8_MAX) { + if (hpet_fw_cfg.count == UINT8_MAX) { /* first instance */ - hpet_cfg.count = 0; + hpet_fw_cfg.count = 0; } - if (hpet_cfg.count == 8) { - error_setg(errp, "Only 8 instances of HPET is allowed"); + if (hpet_fw_cfg.count == 8) { + error_setg(errp, "Only 8 instances of HPET are allowed"); return; } - s->hpet_id = hpet_cfg.count++; + s->hpet_id = hpet_fw_cfg.count++; for (i = 0; i < HPET_NUM_IRQ_ROUTES; i++) { sysbus_init_irq(sbd, &s->irqs[i]); } - if (s->num_timers < HPET_MIN_TIMERS) { - s->num_timers = HPET_MIN_TIMERS; - } else if (s->num_timers > HPET_MAX_TIMERS) { - s->num_timers = HPET_MAX_TIMERS; - } for (i = 0; i < HPET_MAX_TIMERS; i++) { timer = &s->timer[i]; timer->qemu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hpet_timer, timer); @@ -772,7 +723,7 @@ static void hpet_realize(DeviceState *dev, Error **errp) timer->state = s; } - /* 64-bit main counter; LegacyReplacementRoute. */ + /* 64-bit General Capabilities and ID Register; LegacyReplacementRoute. */ s->capability = 0x8086a001ULL; s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT; s->capability |= ((uint64_t)(HPET_CLK_PERIOD * FS_PER_NS) << 32); @@ -781,20 +732,19 @@ static void hpet_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(dev, &s->pit_enabled, 1); } -static Property hpet_device_properties[] = { +static const Property hpet_device_properties[] = { DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS), DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false), DEFINE_PROP_UINT32(HPET_INTCAP, HPETState, intcap, 0), DEFINE_PROP_BOOL("hpet-offset-saved", HPETState, hpet_offset_saved, true), - DEFINE_PROP_END_OF_LIST(), }; -static void hpet_device_class_init(ObjectClass *klass, void *data) +static void hpet_device_class_init(ObjectClass *klass, const 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 c235496fc9c..4b25c487f79 100644 --- a/hw/timer/i8254.c +++ b/hw/timer/i8254.c @@ -350,7 +350,7 @@ static void pit_realizefn(DeviceState *dev, Error **errp) pc->parent_realize(dev, errp); } -static void pit_class_initfn(ObjectClass *klass, void *data) +static void pit_class_initfn(ObjectClass *klass, const void *data) { PITClass *pc = PIT_CLASS(klass); PITCommonClass *k = PIT_COMMON_CLASS(klass); @@ -360,7 +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_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 e4093e29040..ad091594cde 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,12 +238,11 @@ static const VMStateDescription vmstate_pit_common = { } }; -static Property pit_common_properties[] = { +static const 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) +static void pit_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/ibex_timer.c b/hw/timer/ibex_timer.c index d8b8e4e1f60..c7320ef30fa 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), @@ -263,9 +263,8 @@ static const VMStateDescription vmstate_ibex_timer = { } }; -static Property ibex_timer_properties[] = { +static const Property ibex_timer_properties[] = { DEFINE_PROP_UINT32("timebase-freq", IbexTimerState, timebase_freq, 10000), - DEFINE_PROP_END_OF_LIST(), }; static void ibex_timer_init(Object *obj) @@ -287,11 +286,11 @@ static void ibex_timer_realize(DeviceState *dev, Error **errp) } -static void ibex_timer_class_init(ObjectClass *klass, void *data) +static void ibex_timer_class_init(ObjectClass *klass, const 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 640e4399c24..6123321c352 100644 --- a/hw/timer/imx_epit.c +++ b/hw/timer/imx_epit.c @@ -383,7 +383,7 @@ static const VMStateDescription vmstate_imx_timer_epit = { .name = TYPE_IMX_EPIT, .version_id = 3, .minimum_version_id = 3, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT32(cr, IMXEPITState), VMSTATE_UINT32(sr, IMXEPITState), VMSTATE_UINT32(lr, IMXEPITState), @@ -427,12 +427,12 @@ static void imx_epit_dev_reset(DeviceState *dev) imx_epit_reset(s, true); } -static void imx_epit_class_init(ObjectClass *klass, void *data) +static void imx_epit_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = imx_epit_realize; - dc->reset = imx_epit_dev_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 7222b1b3874..8c7cbfdeac3 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -18,18 +18,7 @@ #include "migration/vmstate.h" #include "qemu/module.h" #include "qemu/log.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) +#include "trace.h" static const char *imx_gpt_reg_name(uint32_t reg) { @@ -63,7 +52,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), @@ -137,6 +126,17 @@ static const IMXClk imx7_gpt_clocks[] = { CLK_NONE, /* 111 not defined */ }; +static const IMXClk imx8mp_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_HIGH, /* 101 ipg_clk_16M */ + CLK_NONE, /* 110 not defined */ + CLK_NONE, /* 111 not defined */ +}; + /* Must be called from within ptimer_transaction_begin/commit block */ static void imx_gpt_set_freq(IMXGPTState *s) { @@ -145,7 +145,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); @@ -317,7 +317,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; } @@ -384,8 +384,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: @@ -485,7 +484,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; @@ -519,12 +518,12 @@ static void imx_gpt_realize(DeviceState *dev, Error **errp) s->timer = ptimer_init(imx_gpt_timeout, s, PTIMER_POLICY_LEGACY); } -static void imx_gpt_class_init(ObjectClass *klass, void *data) +static void imx_gpt_class_init(ObjectClass *klass, const 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"; } @@ -564,6 +563,13 @@ static void imx7_gpt_init(Object *obj) s->clocks = imx7_gpt_clocks; } +static void imx8mp_gpt_init(Object *obj) +{ + IMXGPTState *s = IMX_GPT(obj); + + s->clocks = imx8mp_gpt_clocks; +} + static const TypeInfo imx25_gpt_info = { .name = TYPE_IMX25_GPT, .parent = TYPE_SYS_BUS_DEVICE, @@ -596,6 +602,12 @@ static const TypeInfo imx7_gpt_info = { .instance_init = imx7_gpt_init, }; +static const TypeInfo imx8mp_gpt_info = { + .name = TYPE_IMX8MP_GPT, + .parent = TYPE_IMX25_GPT, + .instance_init = imx8mp_gpt_init, +}; + static void imx_gpt_register_types(void) { type_register_static(&imx25_gpt_info); @@ -603,6 +615,7 @@ static void imx_gpt_register_types(void) type_register_static(&imx6_gpt_info); type_register_static(&imx6ul_gpt_info); type_register_static(&imx7_gpt_info); + type_register_static(&imx8mp_gpt_info); } type_init(imx_gpt_register_types) diff --git a/hw/timer/meson.build b/hw/timer/meson.build index fc632ad4451..178321c029c 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -1,6 +1,5 @@ system_ss.add(when: 'CONFIG_A9_GTIMER', if_true: files('a9gtimer.c')) system_ss.add(when: 'CONFIG_ALLWINNER_A10_PIT', if_true: files('allwinner-a10-pit.c')) -system_ss.add(when: 'CONFIG_ALTERA_TIMER', if_true: files('altera_timer.c')) system_ss.add(when: 'CONFIG_ARM_MPTIMER', if_true: files('arm_mptimer.c')) system_ss.add(when: 'CONFIG_ARM_TIMER', if_true: files('arm_timer.c')) system_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_systick.c')) @@ -11,11 +10,10 @@ system_ss.add(when: 'CONFIG_CMSDK_APB_TIMER', if_true: files('cmsdk-apb-timer.c' system_ss.add(when: 'CONFIG_RENESAS_TMR', if_true: files('renesas_tmr.c')) system_ss.add(when: 'CONFIG_RENESAS_CMT', if_true: files('renesas_cmt.c')) system_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-timer.c')) -system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_timer.c')) system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_mct.c')) system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pwm.c')) system_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_gptimer.c')) -system_ss.add(when: 'CONFIG_HPET', if_true: files('hpet.c')) +system_ss.add(when: 'CONFIG_HPET_C', 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')) @@ -23,9 +21,7 @@ system_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_gictimer.c')) system_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-timer.c')) system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_timer.c')) system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_timer.c')) -system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gptimer.c')) -system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_synctimer.c')) -system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_timer.c')) +system_ss.add(when: 'CONFIG_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')) diff --git a/hw/timer/mss-timer.c b/hw/timer/mss-timer.c index ee7438f1684..2ce821178b6 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), @@ -279,14 +279,13 @@ static const VMStateDescription vmstate_mss_timer = { } }; -static Property mss_timer_properties[] = { +static const Property mss_timer_properties[] = { /* Libero GUI shows 100Mhz as default for clocks */ DEFINE_PROP_UINT32("clock-frequency", MSSTimerState, freq_hz, 100 * 1000000), - DEFINE_PROP_END_OF_LIST(), }; -static void mss_timer_class_init(ObjectClass *klass, void *data) +static void mss_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c index 32f5e021f85..6a116ad54b7 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, @@ -686,7 +689,7 @@ static const VMStateDescription vmstate_npcm7xx_timer_ctrl = { }, }; -static void npcm7xx_timer_class_init(ObjectClass *klass, void *data) +static void npcm7xx_timer_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/nrf51_timer.c b/hw/timer/nrf51_timer.c index 50c6772383e..e228fdebc34 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), @@ -379,16 +379,15 @@ static const VMStateDescription vmstate_nrf51_timer = { } }; -static Property nrf51_timer_properties[] = { +static const Property nrf51_timer_properties[] = { DEFINE_PROP_UINT8("id", NRF51TimerState, id, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void nrf51_timer_class_init(ObjectClass *klass, void *data) +static void nrf51_timer_class_init(ObjectClass *klass, const 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 34e6af7aff5..00000000000 --- a/hw/timer/omap_gptimer.c +++ /dev/null @@ -1,512 +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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 d93a9344ede..00000000000 --- 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 = 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 = 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 2ae5ae32123..6d4ac31574e 100644 --- a/hw/timer/pxa2xx_timer.c +++ b/hw/timer/pxa2xx_timer.c @@ -11,50 +11,49 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" -#include "hw/arm/pxa.h" +#include "system/runstate.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" - -#define OSMR0 0x00 -#define OSMR1 0x04 -#define OSMR2 0x08 -#define OSMR3 0x0c -#define OSMR4 0x80 -#define OSMR5 0x84 -#define OSMR6 0x88 -#define OSMR7 0x8c -#define OSMR8 0x90 -#define OSMR9 0x94 -#define OSMR10 0x98 -#define OSMR11 0x9c -#define OSCR 0x10 /* OS Timer Count */ -#define OSCR4 0x40 -#define OSCR5 0x44 -#define OSCR6 0x48 -#define OSCR7 0x4c -#define OSCR8 0x50 -#define OSCR9 0x54 -#define OSCR10 0x58 -#define OSCR11 0x5c -#define OSSR 0x14 /* Timer status register */ -#define OWER 0x18 -#define OIER 0x1c /* Interrupt enable register 3-0 to E3-E0 */ -#define OMCR4 0xc0 /* OS Match Control registers */ -#define OMCR5 0xc4 -#define OMCR6 0xc8 -#define OMCR7 0xcc -#define OMCR8 0xd0 -#define OMCR9 0xd4 -#define OMCR10 0xd8 -#define OMCR11 0xdc -#define OSNR 0x20 - -#define PXA25X_FREQ 3686400 /* 3.6864 MHz */ -#define PXA27X_FREQ 3250000 /* 3.25 MHz */ +#include "system/watchdog.h" + +#define OSMR0 0x00 +#define OSMR1 0x04 +#define OSMR2 0x08 +#define OSMR3 0x0c +#define OSMR4 0x80 +#define OSMR5 0x84 +#define OSMR6 0x88 +#define OSMR7 0x8c +#define OSMR8 0x90 +#define OSMR9 0x94 +#define OSMR10 0x98 +#define OSMR11 0x9c +#define OSCR 0x10 /* OS Timer Count */ +#define OSCR4 0x40 +#define OSCR5 0x44 +#define OSCR6 0x48 +#define OSCR7 0x4c +#define OSCR8 0x50 +#define OSCR9 0x54 +#define OSCR10 0x58 +#define OSCR11 0x5c +#define OSSR 0x14 /* Timer status register */ +#define OWER 0x18 +#define OIER 0x1c /* Interrupt enable register 3-0 to E3-E0 */ +#define OMCR4 0xc0 /* OS Match Control registers */ +#define OMCR5 0xc4 +#define OMCR6 0xc8 +#define OMCR7 0xcc +#define OMCR8 0xd0 +#define OMCR9 0xd4 +#define OMCR10 0xd8 +#define OMCR11 0xdc +#define OSNR 0x20 + +#define PXA25X_FREQ 3686400 /* 3.6864 MHz */ static int pxa2xx_timer4_freq[8] = { [0] = 0, @@ -107,7 +106,7 @@ struct PXA2xxTimerInfo { PXA2xxTimer4 tm4[8]; }; -#define PXA2XX_TIMER_HAVE_TM4 0 +#define PXA2XX_TIMER_HAVE_TM4 0 static inline int pxa2xx_timer_has_tm4(PXA2xxTimerInfo *s) { @@ -231,7 +230,7 @@ static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset, NANOSECONDS_PER_SECOND); case OIER: return s->irq_enabled; - case OSSR: /* Status register */ + case OSSR: /* Status register */ return s->events; case OWER: return s->reset3; @@ -337,7 +336,7 @@ static void pxa2xx_timer_write(void *opaque, hwaddr offset, case OIER: s->irq_enabled = value & 0xfff; break; - case OSSR: /* Status register */ + case OSSR: /* Status register */ value &= s->events; s->events &= ~value; for (i = 0; i < 4; i ++, value >>= 1) @@ -346,7 +345,7 @@ static void pxa2xx_timer_write(void *opaque, hwaddr offset, if (pxa2xx_timer_has_tm4(s) && !(s->events & 0xff0) && value) qemu_irq_lower(s->irq4); break; - case OWER: /* XXX: Reset on OSMR3 match? */ + case OWER: /* XXX: Reset on OSMR3 match? */ s->reset3 = value; break; case OMCR7: tm ++; @@ -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), @@ -550,14 +549,13 @@ static const VMStateDescription vmstate_pxa2xx_timer_regs = { } }; -static Property pxa25x_timer_dev_properties[] = { +static const Property pxa25x_timer_dev_properties[] = { DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA25X_FREQ), DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags, PXA2XX_TIMER_HAVE_TM4, false), - DEFINE_PROP_END_OF_LIST(), }; -static void pxa25x_timer_dev_class_init(ObjectClass *klass, void *data) +static void pxa25x_timer_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -572,29 +570,7 @@ 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) +static void pxa2xx_timer_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -615,7 +591,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 69eabc678a6..cdff7f47f16 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), @@ -253,17 +253,16 @@ static const VMStateDescription vmstate_rcmt = { } }; -static Property rcmt_properties[] = { +static const Property rcmt_properties[] = { DEFINE_PROP_UINT64("input-freq", RCMTState, input_freq, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void rcmt_class_init(ObjectClass *klass, void *data) +static void rcmt_class_init(ObjectClass *klass, const 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 c15f6547381..95707f2b8c5 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), @@ -463,17 +463,16 @@ static const VMStateDescription vmstate_rtmr = { } }; -static Property rtmr_properties[] = { +static const Property rtmr_properties[] = { DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void rtmr_class_init(ObjectClass *klass, void *data) +static void rtmr_class_init(ObjectClass *klass, const 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/sh_timer.c b/hw/timer/sh_timer.c index 77889397669..d4fa32c9d65 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -9,7 +9,7 @@ */ #include "qemu/osdep.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/log.h" #include "hw/irq.h" #include "hw/sh4/sh.h" diff --git a/hw/timer/sifive_pwm.c b/hw/timer/sifive_pwm.c index c664480ccf5..e85e389f7a5 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), @@ -404,11 +404,10 @@ static const VMStateDescription vmstate_sifive_pwm = { } }; -static Property sifive_pwm_properties[] = { +static const Property sifive_pwm_properties[] = { /* 0.5Ghz per spec after FSBL */ DEFINE_PROP_UINT64("clock-frequency", struct SiFivePwmState, freq_hz, 500000000ULL), - DEFINE_PROP_END_OF_LIST(), }; static void sifive_pwm_init(Object *obj) @@ -442,11 +441,11 @@ static void sifive_pwm_realize(DeviceState *dev, Error **errp) sifive_pwm_interrupt_3, s); } -static void sifive_pwm_class_init(ObjectClass *klass, void *data) +static void sifive_pwm_class_init(ObjectClass *klass, const 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 8c4f6eb06b6..3e071fbdb4c 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() @@ -420,16 +420,15 @@ static void slavio_timer_init(Object *obj) } } -static Property slavio_timer_properties[] = { +static const Property slavio_timer_properties[] = { DEFINE_PROP_UINT32("num_cpus", SLAVIO_TIMERState, num_cpus, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void slavio_timer_class_init(ObjectClass *klass, void *data) +static void slavio_timer_class_init(ObjectClass *klass, const 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 16c0e8ad15d..31f77acf61e 100644 --- a/hw/timer/sse-counter.c +++ b/hw/timer/sse-counter.c @@ -442,19 +442,19 @@ 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() } }; -static void sse_counter_class_init(ObjectClass *klass, void *data) +static void sse_counter_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 e92e83747d2..866d5eef8af 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), @@ -440,18 +440,17 @@ static const VMStateDescription sse_timer_vmstate = { } }; -static Property sse_timer_properties[] = { +static const Property sse_timer_properties[] = { DEFINE_PROP_LINK("counter", SSETimer, counter, TYPE_SSE_COUNTER, SSECounter *), - DEFINE_PROP_END_OF_LIST(), }; -static void sse_timer_class_init(ObjectClass *klass, void *data) +static void sse_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 fd71c79be48..d97b2f8309c 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), @@ -308,7 +308,7 @@ static void stellaris_gptm_realize(DeviceState *dev, Error **errp) s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]); } -static void stellaris_gptm_class_init(ObjectClass *klass, void *data) +static void stellaris_gptm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c index ba8694dcd34..be844e7f5a3 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), @@ -298,10 +298,9 @@ static const VMStateDescription vmstate_stm32f2xx_timer = { } }; -static Property stm32f2xx_timer_properties[] = { +static const Property stm32f2xx_timer_properties[] = { DEFINE_PROP_UINT64("clock-frequency", struct STM32F2XXTimerState, freq_hz, 1000000000), - DEFINE_PROP_END_OF_LIST(), }; static void stm32f2xx_timer_init(Object *obj) @@ -321,11 +320,11 @@ static void stm32f2xx_timer_realize(DeviceState *dev, Error **errp) s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, stm32f2xx_timer_interrupt, s); } -static void stm32f2xx_timer_class_init(ObjectClass *klass, void *data) +static void stm32f2xx_timer_class_init(ObjectClass *klass, const 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 3eccef83858..c5b6db49f58 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -31,11 +31,11 @@ aspeed_timer_ctrl_overflow_interrupt(uint8_t i, bool enable) "Timer %" PRIu8 ": aspeed_timer_ctrl_pulse_enable(uint8_t i, bool enable) "Timer %" PRIu8 ": %d" aspeed_timer_set_ctrl2(uint32_t value) "Value: 0x%" PRIx32 aspeed_timer_set_value(int timer, int reg, uint32_t value) "Timer %d register %d: 0x%" PRIx32 -aspeed_timer_read(uint64_t offset, unsigned size, uint64_t value) "From 0x%" PRIx64 ": of size %u: 0x%" PRIx64 +aspeed_timer_read(uint64_t offset, uint64_t value) "From 0x%" PRIx64 ": 0x%" PRIx64 # 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 32a9df69e0b..ff4a224d08f 100644 --- a/hw/timer/xilinx_timer.c +++ b/hw/timer/xilinx_timer.c @@ -3,6 +3,9 @@ * * Copyright (c) 2009 Edgar E. Iglesias. * + * DS573: https://docs.amd.com/v/u/en-US/xps_timer + * LogiCORE IP XPS Timer/Counter (v1.02a) + * * 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 @@ -23,10 +26,12 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "hw/irq.h" #include "hw/ptimer.h" #include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" @@ -69,6 +74,7 @@ struct XpsTimerState { SysBusDevice parent_obj; + EndianMode model_endianness; MemoryRegion mmio; qemu_irq irq; uint8_t one_timer_only; @@ -189,14 +195,21 @@ timer_write(void *opaque, hwaddr addr, timer_update_irq(t); } -static const MemoryRegionOps timer_ops = { - .read = timer_read, - .write = timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } +static const MemoryRegionOps timer_ops[2] = { + [0 ... 1] = { + .read = timer_read, + .write = timer_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + }, + [0].endianness = DEVICE_LITTLE_ENDIAN, + [1].endianness = DEVICE_BIG_ENDIAN, }; static void timer_hit(void *opaque) @@ -216,6 +229,12 @@ static void xilinx_timer_realize(DeviceState *dev, Error **errp) XpsTimerState *t = XILINX_TIMER(dev); unsigned int i; + if (t->model_endianness == ENDIAN_MODE_UNSPECIFIED) { + error_setg(errp, TYPE_XILINX_TIMER " property 'endianness'" + " must be set to 'big' or 'little'"); + return; + } + /* Init all the ptimers. */ t->timers = g_malloc0(sizeof t->timers[0] * num_timers(t)); for (i = 0; i < num_timers(t); i++) { @@ -229,8 +248,9 @@ static void xilinx_timer_realize(DeviceState *dev, Error **errp) ptimer_transaction_commit(xt->ptimer); } - memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, "xlnx.xps-timer", - R_MAX * 4 * num_timers(t)); + memory_region_init_io(&t->mmio, OBJECT(t), + &timer_ops[t->model_endianness == ENDIAN_MODE_BIG], + t, "xlnx.xps-timer", R_MAX * 4 * num_timers(t)); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &t->mmio); } @@ -242,13 +262,13 @@ static void xilinx_timer_init(Object *obj) sysbus_init_irq(SYS_BUS_DEVICE(obj), &t->irq); } -static Property xilinx_timer_properties[] = { +static const Property xilinx_timer_properties[] = { + DEFINE_PROP_ENDIAN_NODEFAULT("endianness", XpsTimerState, model_endianness), 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(), }; -static void xilinx_timer_class_init(ObjectClass *klass, void *data) +static void xilinx_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c index ea930da545a..bc7a78f898c 100644 --- a/hw/tpm/tpm_crb.c +++ b/hw/tpm/tpm_crb.c @@ -18,15 +18,15 @@ #include "qemu/module.h" #include "qapi/error.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/qdev-properties.h" #include "hw/pci/pci_ids.h" #include "hw/acpi/tpm.h" #include "migration/vmstate.h" -#include "sysemu/tpm_backend.h" -#include "sysemu/tpm_util.h" -#include "sysemu/reset.h" -#include "sysemu/xen.h" +#include "system/tpm_backend.h" +#include "system/tpm_util.h" +#include "system/reset.h" +#include "system/xen.h" #include "tpm_prop.h" #include "tpm_ppi.h" #include "trace.h" @@ -220,16 +220,15 @@ 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(), } }; -static Property tpm_crb_properties[] = { +static const Property tpm_crb_properties[] = { DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe), DEFINE_PROP_BOOL("ppi", CRBState, ppi_enabled, true), - DEFINE_PROP_END_OF_LIST(), }; static void tpm_crb_reset(void *dev) @@ -316,7 +315,7 @@ static void tpm_crb_realize(DeviceState *dev, Error **errp) } } -static void tpm_crb_class_init(ObjectClass *klass, void *data) +static void tpm_crb_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); TPMIfClass *tc = TPM_IF_CLASS(klass); @@ -338,7 +337,7 @@ static const TypeInfo tpm_crb_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(CRBState), .class_init = tpm_crb_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TPM_IF }, { } } diff --git a/hw/tpm/tpm_ppi.c b/hw/tpm/tpm_ppi.c index 7f74e26ec6c..984d3d10808 100644 --- a/hw/tpm/tpm_ppi.c +++ b/hw/tpm/tpm_ppi.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "qemu/memalign.h" #include "qapi/error.h" -#include "sysemu/memory_mapping.h" +#include "system/memory_mapping.h" #include "migration/vmstate.h" #include "hw/qdev-core.h" #include "hw/acpi/tpm.h" @@ -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_ppi.h b/hw/tpm/tpm_ppi.h index bf5d4a300f0..88f316ee957 100644 --- a/hw/tpm/tpm_ppi.h +++ b/hw/tpm/tpm_ppi.h @@ -12,7 +12,7 @@ #ifndef TPM_TPM_PPI_H #define TPM_TPM_PPI_H -#include "exec/memory.h" +#include "system/memory.h" typedef struct TPMPPI { MemoryRegion ram; diff --git a/hw/tpm/tpm_prop.h b/hw/tpm/tpm_prop.h index bbd4225d667..c4df74805a7 100644 --- a/hw/tpm/tpm_prop.h +++ b/hw/tpm/tpm_prop.h @@ -22,7 +22,7 @@ #ifndef HW_TPM_PROP_H #define HW_TPM_PROP_H -#include "sysemu/tpm_backend.h" +#include "system/tpm_backend.h" #include "hw/qdev-properties.h" extern const PropertyInfo qdev_prop_tpm; diff --git a/hw/tpm/tpm_spapr.c b/hw/tpm/tpm_spapr.c index dea7b1333b8..ea608ba4c80 100644 --- a/hw/tpm/tpm_spapr.c +++ b/hw/tpm/tpm_spapr.c @@ -19,8 +19,8 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "sysemu/tpm_backend.h" -#include "sysemu/tpm_util.h" +#include "system/tpm_backend.h" +#include "system/tpm_util.h" #include "tpm_prop.h" #include "hw/ppc/spapr.h" @@ -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), @@ -365,10 +364,9 @@ static const VMStateDescription vmstate_spapr_vtpm = { } }; -static Property tpm_spapr_properties[] = { +static const Property tpm_spapr_properties[] = { DEFINE_SPAPR_PROPERTIES(SpaprTpmState, vdev), DEFINE_PROP_TPMBE("tpmdev", SpaprTpmState, be_driver), - DEFINE_PROP_END_OF_LIST(), }; static void tpm_spapr_realizefn(SpaprVioDevice *dev, Error **errp) @@ -389,7 +387,7 @@ static void tpm_spapr_realizefn(SpaprVioDevice *dev, Error **errp) s->buffer = g_malloc(TPM_SPAPR_BUFFER_MAX); } -static void tpm_spapr_class_init(ObjectClass *klass, void *data) +static void tpm_spapr_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); @@ -416,7 +414,7 @@ static const TypeInfo tpm_spapr_info = { .parent = TYPE_VIO_SPAPR_DEVICE, .instance_size = sizeof(SpaprTpmState), .class_init = tpm_spapr_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TPM_IF }, { } } diff --git a/hw/tpm/tpm_tis.h b/hw/tpm/tpm_tis.h index 6f29a508dd4..184632ff66b 100644 --- a/hw/tpm/tpm_tis.h +++ b/hw/tpm/tpm_tis.h @@ -19,12 +19,12 @@ * 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 -#include "sysemu/tpm_backend.h" +#include "system/tpm_backend.h" #include "tpm_ppi.h" #define TPM_TIS_NUM_LOCALITIES 5 /* per spec */ diff --git a/hw/tpm/tpm_tis_common.c b/hw/tpm/tpm_tis_common.c index c07c179dbca..cdd0df11374 100644 --- a/hw/tpm/tpm_tis_common.c +++ b/hw/tpm/tpm_tis_common.c @@ -20,7 +20,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" #include "hw/irq.h" @@ -34,8 +34,8 @@ #include "hw/pci/pci_ids.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "sysemu/tpm_backend.h" -#include "sysemu/tpm_util.h" +#include "system/tpm_backend.h" +#include "system/tpm_util.h" #include "tpm_ppi.h" #include "trace.h" @@ -879,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 index b695fd3a46d..5ce84dc7a45 100644 --- a/hw/tpm/tpm_tis_i2c.c +++ b/hw/tpm/tpm_tis_i2c.c @@ -13,7 +13,7 @@ * Family 2.0, Level 00, Revision 1.00 * * 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 * */ @@ -115,7 +115,7 @@ static const VMStateDescription vmstate_tpm_tis_i2c = { .version_id = 0, .pre_save = tpm_tis_i2c_pre_save, .post_load = tpm_tis_i2c_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BUFFER(state.buffer, TPMStateI2C), VMSTATE_UINT16(state.rw_offset, TPMStateI2C), VMSTATE_UINT8(state.active_locty, TPMStateI2C), @@ -211,8 +211,6 @@ static inline void tpm_tis_i2c_clear_data(TPMStateI2C *i2cst) i2cst->tis_addr = 0xffffffff; i2cst->reg_name = NULL; memset(i2cst->data, 0, sizeof(i2cst->data)); - - return; } /* Send data to TPM */ @@ -281,8 +279,6 @@ static inline void tpm_tis_i2c_tpm_send(TPMStateI2C *i2cst) tpm_tis_i2c_clear_data(i2cst); } - - return; } /* Callback from TPM to indicate that response is copied */ @@ -491,9 +487,8 @@ static int tpm_tis_i2c_send(I2CSlave *i2c, uint8_t data) return 1; } -static Property tpm_tis_i2c_properties[] = { +static const 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) @@ -507,7 +502,7 @@ static void tpm_tis_i2c_realizefn(DeviceState *dev, Error **errp) } /* - * Get the backend pointer. It is not initialized propery during + * Get the backend pointer. It is not initialized properly during * device_class_set_props */ s->be_driver = qemu_find_tpm_be("tpm0"); @@ -531,14 +526,14 @@ static void tpm_tis_i2c_reset(DeviceState *dev) return tpm_tis_reset(s); } -static void tpm_tis_i2c_class_init(ObjectClass *klass, void *data) +static void tpm_tis_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); TPMIfClass *tc = TPM_IF_CLASS(klass); dc->realize = tpm_tis_i2c_realizefn; - dc->reset = tpm_tis_i2c_reset; + 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); @@ -557,7 +552,7 @@ static const TypeInfo tpm_tis_i2c_info = { .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(TPMStateI2C), .class_init = tpm_tis_i2c_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TPM_IF }, { } } diff --git a/hw/tpm/tpm_tis_isa.c b/hw/tpm/tpm_tis_isa.c index 91e37922484..dce83057a94 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), @@ -91,11 +91,10 @@ static void tpm_tis_isa_reset(DeviceState *dev) return tpm_tis_reset(s); } -static Property tpm_tis_isa_properties[] = { +static const Property tpm_tis_isa_properties[] = { DEFINE_PROP_UINT32("irq", TPMStateISA, state.irq_num, TPM_TIS_IRQ), DEFINE_PROP_TPMBE("tpmdev", TPMStateISA, state.be_driver), DEFINE_PROP_BOOL("ppi", TPMStateISA, state.ppi_enabled, true), - DEFINE_PROP_END_OF_LIST(), }; static void tpm_tis_isa_initfn(Object *obj) @@ -167,7 +166,7 @@ static void build_tpm_tis_isa_aml(AcpiDevAmlIf *adev, Aml *scope) aml_append(scope, dev); } -static void tpm_tis_isa_class_init(ObjectClass *klass, void *data) +static void tpm_tis_isa_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); TPMIfClass *tc = TPM_IF_CLASS(klass); @@ -177,7 +176,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); @@ -190,7 +189,7 @@ static const TypeInfo tpm_tis_isa_info = { .instance_size = sizeof(TPMStateISA), .instance_init = tpm_tis_isa_initfn, .class_init = tpm_tis_isa_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TPM_IF }, { TYPE_ACPI_DEV_AML_IF }, { } diff --git a/hw/tpm/tpm_tis_sysbus.c b/hw/tpm/tpm_tis_sysbus.c index 6724b3d4f66..2ffa85852ac 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), @@ -90,10 +90,9 @@ static void tpm_tis_sysbus_reset(DeviceState *dev) return tpm_tis_reset(s); } -static Property tpm_tis_sysbus_properties[] = { +static const 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_END_OF_LIST(), }; static void tpm_tis_sysbus_initfn(Object *obj) @@ -125,7 +124,7 @@ static void tpm_tis_sysbus_realizefn(DeviceState *dev, Error **errp) } } -static void tpm_tis_sysbus_class_init(ObjectClass *klass, void *data) +static void tpm_tis_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); TPMIfClass *tc = TPM_IF_CLASS(klass); @@ -134,8 +133,7 @@ static void tpm_tis_sysbus_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_tpm_tis_sysbus; tc->model = TPM_MODEL_TPM_TIS; dc->realize = tpm_tis_sysbus_realizefn; - dc->user_creatable = true; - dc->reset = tpm_tis_sysbus_reset; + 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); @@ -143,11 +141,11 @@ static void tpm_tis_sysbus_class_init(ObjectClass *klass, void *data) static const TypeInfo tpm_tis_sysbus_info = { .name = TYPE_TPM_TIS_SYSBUS, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_DYNAMIC_SYS_BUS_DEVICE, .instance_size = sizeof(TPMStateSysBus), .instance_init = tpm_tis_sysbus_initfn, .class_init = tpm_tis_sysbus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TPM_IF }, { } } diff --git a/hw/tricore/Kconfig b/hw/tricore/Kconfig index 33c1e852c33..6c04f64949d 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/tc27x_soc.c b/hw/tricore/tc27x_soc.c index ecd92717b50..f3b84980e52 100644 --- a/hw/tricore/tc27x_soc.c +++ b/hw/tricore/tc27x_soc.c @@ -201,19 +201,14 @@ static void tc27x_soc_init(Object *obj) object_initialize_child(obj, "tc27x", &s->cpu, sc->cpu_type); } -static Property tc27x_soc_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - -static void tc27x_soc_class_init(ObjectClass *klass, void *data) +static void tc27x_soc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = tc27x_soc_realize; - device_class_set_props(dc, tc27x_soc_properties); } -static void tc277d_soc_class_init(ObjectClass *oc, void *data) +static void tc277d_soc_class_init(ObjectClass *oc, const void *data) { TC27XSoCClass *sc = TC27X_SOC_CLASS(oc); diff --git a/hw/tricore/triboard.c b/hw/tricore/triboard.c index 4dba0259cd3..cb45b01d2d2 100644 --- a/hw/tricore/triboard.c +++ b/hw/tricore/triboard.c @@ -31,22 +31,20 @@ #include "hw/tricore/triboard.h" #include "hw/tricore/tc27x_soc.h" -static void tricore_load_kernel(const char *kernel_filename) +static void tricore_load_kernel(TriCoreCPU *cpu, const char *kernel_filename) { uint64_t entry; long kernel_size; - TriCoreCPU *cpu; CPUTriCoreState *env; kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &entry, NULL, - NULL, NULL, 0, + NULL, NULL, ELFDATA2LSB, EM_TRICORE, 1, 0); if (kernel_size <= 0) { error_report("no kernel file '%s'", kernel_filename); exit(1); } - cpu = TRICORE_CPU(first_cpu); env = &cpu->env; env->PC = entry; } @@ -62,12 +60,12 @@ static void triboard_machine_init(MachineState *machine) sysbus_realize(SYS_BUS_DEVICE(&ms->tc27x_soc), &error_fatal); if (machine->kernel_filename) { - tricore_load_kernel(machine->kernel_filename); + tricore_load_kernel(&ms->tc27x_soc.cpu, machine->kernel_filename); } } static void triboard_machine_tc277d_class_init(ObjectClass *oc, - void *data) + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); TriBoardMachineClass *amc = TRIBOARD_MACHINE_CLASS(oc); diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c index b6810e3be05..3facfdfd611 100644 --- a/hw/tricore/tricore_testboard.c +++ b/hw/tricore/tricore_testboard.c @@ -42,7 +42,7 @@ static void tricore_load_kernel(CPUTriCoreState *env) kernel_size = load_elf(tricoretb_binfo.kernel_filename, NULL, NULL, NULL, &entry, NULL, - NULL, NULL, 0, + NULL, NULL, ELFDATA2LSB, EM_TRICORE, 1, 0); if (kernel_size <= 0) { error_report("no kernel file '%s'", @@ -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 a1563aa5689..e8daf952982 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); } @@ -43,7 +47,7 @@ static const MemoryRegionOps tricore_testdevice_ops = { .min_access_size = 4, .max_access_size = 4, }, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void tricore_testdevice_init(Object *obj) @@ -54,16 +58,11 @@ static void tricore_testdevice_init(Object *obj) "tricore_testdevice", 0x4); } -static Property tricore_testdevice_properties[] = { - DEFINE_PROP_END_OF_LIST() -}; - -static void tricore_testdevice_class_init(ObjectClass *klass, void *data) +static void tricore_testdevice_class_init(ObjectClass *klass, const 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/uefi/Kconfig b/hw/uefi/Kconfig new file mode 100644 index 00000000000..046d55320e1 --- /dev/null +++ b/hw/uefi/Kconfig @@ -0,0 +1,3 @@ +config UEFI_VARS + bool + default y if X86_64 || AARCH64 || RISCV64 || LOONGARCH64 diff --git a/hw/uefi/LIMITATIONS.md b/hw/uefi/LIMITATIONS.md new file mode 100644 index 00000000000..29308bd587a --- /dev/null +++ b/hw/uefi/LIMITATIONS.md @@ -0,0 +1,7 @@ +known issues and limitations +---------------------------- + +* works only on little endian hosts + - accessing structs in guest ram is done without endian conversion. +* works only for 64-bit guests + - UINTN is mapped to uint64_t, for 32-bit guests that would be uint32_t diff --git a/hw/uefi/hardware-info.c b/hw/uefi/hardware-info.c new file mode 100644 index 00000000000..930502a4df3 --- /dev/null +++ b/hw/uefi/hardware-info.c @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * pass hardware information to uefi + * + * see OvmfPkg/Library/HardwareInfoLib/ in edk2 + */ + +#include "qemu/osdep.h" + +#include "hw/nvram/fw_cfg.h" +#include "hw/uefi/hardware-info.h" + +static void *blob; +static uint64_t blobsize; + +void hardware_info_register(HARDWARE_INFO_TYPE type, void *info, uint64_t infosize) +{ + HARDWARE_INFO_HEADER hdr = { + .type.value = cpu_to_le64(type), + .size = cpu_to_le64(infosize), + }; + + blob = g_realloc(blob, blobsize + sizeof(hdr) + infosize); + memcpy(blob + blobsize, &hdr, sizeof(hdr)); + blobsize += sizeof(hdr); + memcpy(blob + blobsize, info, infosize); + blobsize += infosize; + + fw_cfg_modify_file(fw_cfg_find(), "etc/hardware-info", blob, blobsize); +} diff --git a/hw/uefi/meson.build b/hw/uefi/meson.build new file mode 100644 index 00000000000..91eb95f89e6 --- /dev/null +++ b/hw/uefi/meson.build @@ -0,0 +1,21 @@ +system_ss.add(files('hardware-info.c')) + +uefi_vars_ss = ss.source_set() +if (config_all_devices.has_key('CONFIG_UEFI_VARS')) + uefi_vars_ss.add(files('var-service-core.c', + 'var-service-json.c', + 'var-service-vars.c', + 'var-service-auth.c', + 'var-service-guid.c', + 'var-service-utils.c', + 'var-service-policy.c', + 'var-service-sysbus.c')) + uefi_vars_ss.add(when: gnutls, + if_true: files('var-service-pkcs7.c'), + if_false: files('var-service-pkcs7-stub.c')) + uefi_vars_ss.add(files('var-service-siglist.c')) +endif + +modules += { 'hw-uefi' : { + 'vars' : uefi_vars_ss, +}} diff --git a/hw/uefi/trace-events b/hw/uefi/trace-events new file mode 100644 index 00000000000..3694712a946 --- /dev/null +++ b/hw/uefi/trace-events @@ -0,0 +1,17 @@ +# device +uefi_reg_read(uint64_t addr, unsigned size) "addr 0x%" PRIx64 ", size %u" +uefi_reg_write(uint64_t addr, uint64_t val, unsigned size) "addr 0x%" PRIx64 ", val 0x%" PRIx64 ", size %d" +uefi_hard_reset(void) "" + +# generic uefi +uefi_variable(const char *context, const char *name, uint64_t size, const char *uuid) "context %s, name %s, size %" PRIu64 ", uuid %s" +uefi_status(const char *context, const char *name) "context %s, status %s" +uefi_event(const char *name) "event %s" + +# variable protocol +uefi_vars_proto_cmd(const char *cmd) "cmd %s" +uefi_vars_security_violation(const char *reason) "reason %s" + +# variable policy protocol +uefi_vars_policy_cmd(const char *cmd) "cmd %s" +uefi_vars_policy_deny(const char *reason) "reason %s" diff --git a/hw/uefi/var-service-auth.c b/hw/uefi/var-service-auth.c new file mode 100644 index 00000000000..fba5a0956a5 --- /dev/null +++ b/hw/uefi/var-service-auth.c @@ -0,0 +1,361 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - AuthVariableLib + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "system/dma.h" + +#include "hw/uefi/var-service.h" + +static const uint16_t name_pk[] = u"PK"; +static const uint16_t name_kek[] = u"KEK"; +static const uint16_t name_db[] = u"db"; +static const uint16_t name_dbx[] = u"dbx"; +static const uint16_t name_setup_mode[] = u"SetupMode"; +static const uint16_t name_sigs_support[] = u"SignatureSupport"; +static const uint16_t name_sb[] = u"SecureBoot"; +static const uint16_t name_sb_enable[] = u"SecureBootEnable"; +static const uint16_t name_custom_mode[] = u"CustomMode"; +static const uint16_t name_vk[] = u"VendorKeys"; +static const uint16_t name_vk_nv[] = u"VendorKeysNv"; + +static const uint32_t sigdb_attrs = + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + +static void set_secure_boot(uefi_vars_state *uv, uint8_t sb) +{ + uefi_vars_set_variable(uv, EfiGlobalVariable, + name_sb, sizeof(name_sb), + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + &sb, sizeof(sb)); +} + +static void set_secure_boot_enable(uefi_vars_state *uv, uint8_t sbe) +{ + uefi_vars_set_variable(uv, EfiSecureBootEnableDisable, + name_sb_enable, sizeof(name_sb_enable), + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS, + &sbe, sizeof(sbe)); +} + +static void set_setup_mode(uefi_vars_state *uv, uint8_t sm) +{ + uefi_vars_set_variable(uv, EfiGlobalVariable, + name_setup_mode, sizeof(name_setup_mode), + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + &sm, sizeof(sm)); +} + +static void set_custom_mode(uefi_vars_state *uv, uint8_t cm) +{ + uefi_vars_set_variable(uv, EfiCustomModeEnable, + name_custom_mode, sizeof(name_custom_mode), + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS, + &cm, sizeof(cm)); +} + +static void set_signature_support(uefi_vars_state *uv) +{ + QemuUUID sigs_support[5]; + + sigs_support[0] = EfiCertSha256Guid; + sigs_support[1] = EfiCertSha384Guid; + sigs_support[2] = EfiCertSha512Guid; + sigs_support[3] = EfiCertRsa2048Guid; + sigs_support[4] = EfiCertX509Guid; + + uefi_vars_set_variable(uv, EfiGlobalVariable, + name_sigs_support, sizeof(name_sigs_support), + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + sigs_support, sizeof(sigs_support)); +} + +static bool setup_mode_is_active(uefi_vars_state *uv) +{ + uefi_variable *var; + uint8_t *value; + + var = uefi_vars_find_variable(uv, EfiGlobalVariable, + name_setup_mode, sizeof(name_setup_mode)); + if (var) { + value = var->data; + if (value[0] == SETUP_MODE) { + return true; + } + } + return false; +} + +static bool custom_mode_is_active(uefi_vars_state *uv) +{ + uefi_variable *var; + uint8_t *value; + + var = uefi_vars_find_variable(uv, EfiCustomModeEnable, + name_custom_mode, sizeof(name_custom_mode)); + if (var) { + value = var->data; + if (value[0] == CUSTOM_SECURE_BOOT_MODE) { + return true; + } + } + return false; +} + +bool uefi_vars_is_sb_pk(uefi_variable *var) +{ + if (qemu_uuid_is_equal(&var->guid, &EfiGlobalVariable) && + uefi_str_equal(var->name, var->name_size, name_pk, sizeof(name_pk))) { + return true; + } + return false; +} + +static bool uefi_vars_is_sb_kek(uefi_variable *var) +{ + if (qemu_uuid_is_equal(&var->guid, &EfiGlobalVariable) && + uefi_str_equal(var->name, var->name_size, name_kek, sizeof(name_kek))) { + return true; + } + return false; +} + +static bool uefi_vars_is_sb_db(uefi_variable *var) +{ + if (!qemu_uuid_is_equal(&var->guid, &EfiImageSecurityDatabase)) { + return false; + } + if (uefi_str_equal(var->name, var->name_size, name_db, sizeof(name_db))) { + return true; + } + if (uefi_str_equal(var->name, var->name_size, name_dbx, sizeof(name_dbx))) { + return true; + } + return false; +} + +bool uefi_vars_is_sb_any(uefi_variable *var) +{ + if (uefi_vars_is_sb_pk(var) || + uefi_vars_is_sb_kek(var) || + uefi_vars_is_sb_db(var)) { + return true; + } + return false; +} + +static uefi_variable *uefi_vars_find_siglist(uefi_vars_state *uv, + uefi_variable *var) +{ + if (uefi_vars_is_sb_pk(var)) { + return uefi_vars_find_variable(uv, EfiGlobalVariable, + name_pk, sizeof(name_pk)); + } + if (uefi_vars_is_sb_kek(var)) { + return uefi_vars_find_variable(uv, EfiGlobalVariable, + name_pk, sizeof(name_pk)); + } + if (uefi_vars_is_sb_db(var)) { + return uefi_vars_find_variable(uv, EfiGlobalVariable, + name_kek, sizeof(name_kek)); + } + + return NULL; +} + +static efi_status uefi_vars_check_auth_2_sb(uefi_vars_state *uv, + uefi_variable *var, + mm_variable_access *va, + void *data, + uint64_t data_offset) +{ + variable_auth_2 *auth = data; + uefi_variable *siglist; + + if (custom_mode_is_active(uv)) { + /* no authentication in custom mode */ + return EFI_SUCCESS; + } + + if (setup_mode_is_active(uv) && !uefi_vars_is_sb_pk(var)) { + /* no authentication in setup mode (except PK) */ + return EFI_SUCCESS; + } + + if (auth->hdr_length == 24) { + /* no signature (auth->cert_data is empty) */ + return EFI_SECURITY_VIOLATION; + } + + siglist = uefi_vars_find_siglist(uv, var); + if (!siglist && setup_mode_is_active(uv) && uefi_vars_is_sb_pk(var)) { + /* check PK is self-signed */ + uefi_variable tmp = { + .guid = EfiGlobalVariable, + .name = (uint16_t *)name_pk, + .name_size = sizeof(name_pk), + .attributes = sigdb_attrs, + .data = data + data_offset, + .data_size = va->data_size - data_offset, + }; + return uefi_vars_check_pkcs7_2(&tmp, NULL, NULL, va, data); + } + + return uefi_vars_check_pkcs7_2(siglist, NULL, NULL, va, data); +} + +efi_status uefi_vars_check_auth_2(uefi_vars_state *uv, uefi_variable *var, + mm_variable_access *va, void *data) +{ + variable_auth_2 *auth = data; + uint64_t data_offset; + efi_status status; + + if (va->data_size < sizeof(*auth)) { + return EFI_SECURITY_VIOLATION; + } + if (uadd64_overflow(sizeof(efi_time), auth->hdr_length, &data_offset)) { + return EFI_SECURITY_VIOLATION; + } + if (va->data_size < data_offset) { + return EFI_SECURITY_VIOLATION; + } + + if (auth->hdr_revision != 0x0200 || + auth->hdr_cert_type != WIN_CERT_TYPE_EFI_GUID || + !qemu_uuid_is_equal(&auth->guid_cert_type, &EfiCertTypePkcs7Guid)) { + return EFI_UNSUPPORTED; + } + + if (uefi_vars_is_sb_any(var)) { + /* secure boot variables */ + status = uefi_vars_check_auth_2_sb(uv, var, va, data, data_offset); + if (status != EFI_SUCCESS) { + return status; + } + } else { + /* other authenticated variables */ + status = uefi_vars_check_pkcs7_2(NULL, + &var->digest, &var->digest_size, + va, data); + if (status != EFI_SUCCESS) { + return status; + } + } + + /* checks passed, set variable data */ + var->time = auth->timestamp; + if (va->data_size - data_offset > 0) { + var->data = g_malloc(va->data_size - data_offset); + memcpy(var->data, data + data_offset, va->data_size - data_offset); + var->data_size = va->data_size - data_offset; + } + + return EFI_SUCCESS; +} + +efi_status uefi_vars_check_secure_boot(uefi_vars_state *uv, uefi_variable *var) +{ + uint8_t *value = var->data; + + if (uefi_vars_is_sb_any(var)) { + if (var->attributes != sigdb_attrs) { + return EFI_INVALID_PARAMETER; + } + } + + /* reject SecureBootEnable updates if force_secure_boot is set */ + if (qemu_uuid_is_equal(&var->guid, &EfiSecureBootEnableDisable) && + uefi_str_equal(var->name, var->name_size, + name_sb_enable, sizeof(name_sb_enable)) && + uv->force_secure_boot && + value[0] != SECURE_BOOT_ENABLE) { + return EFI_WRITE_PROTECTED; + } + + /* reject CustomMode updates if disable_custom_mode is set */ + if (qemu_uuid_is_equal(&var->guid, &EfiCustomModeEnable) && + uefi_str_equal(var->name, var->name_size, + name_custom_mode, sizeof(name_custom_mode)) && + uv->disable_custom_mode) { + return EFI_WRITE_PROTECTED; + } + + return EFI_SUCCESS; +} + +/* AuthVariableLibInitialize */ +void uefi_vars_auth_init(uefi_vars_state *uv) +{ + uefi_variable *pk_var, *sbe_var; + uint8_t platform_mode, sb, sbe, vk; + + /* SetupMode */ + pk_var = uefi_vars_find_variable(uv, EfiGlobalVariable, + name_pk, sizeof(name_pk)); + if (!pk_var) { + platform_mode = SETUP_MODE; + } else { + platform_mode = USER_MODE; + } + set_setup_mode(uv, platform_mode); + + /* SignatureSupport */ + set_signature_support(uv); + + /* SecureBootEnable */ + sbe = SECURE_BOOT_DISABLE; + sbe_var = uefi_vars_find_variable(uv, EfiSecureBootEnableDisable, + name_sb_enable, sizeof(name_sb_enable)); + if (sbe_var) { + if (platform_mode == USER_MODE) { + sbe = ((uint8_t *)sbe_var->data)[0]; + } + } else if (platform_mode == USER_MODE) { + sbe = SECURE_BOOT_ENABLE; + set_secure_boot_enable(uv, sbe); + } + + if (uv->force_secure_boot && sbe != SECURE_BOOT_ENABLE) { + sbe = SECURE_BOOT_ENABLE; + set_secure_boot_enable(uv, sbe); + } + + /* SecureBoot */ + if ((sbe == SECURE_BOOT_ENABLE) && (platform_mode == USER_MODE)) { + sb = SECURE_BOOT_MODE_ENABLE; + } else { + sb = SECURE_BOOT_MODE_DISABLE; + } + set_secure_boot(uv, sb); + + /* CustomMode */ + set_custom_mode(uv, STANDARD_SECURE_BOOT_MODE); + + vk = 0; + uefi_vars_set_variable(uv, EfiGlobalVariable, + name_vk_nv, sizeof(name_vk_nv), + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, + &vk, sizeof(vk)); + uefi_vars_set_variable(uv, EfiGlobalVariable, + name_vk, sizeof(name_vk), + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + &vk, sizeof(vk)); + + /* flush to disk */ + uefi_vars_json_save(uv); +} diff --git a/hw/uefi/var-service-core.c b/hw/uefi/var-service-core.c new file mode 100644 index 00000000000..4836a0cb811 --- /dev/null +++ b/hw/uefi/var-service-core.c @@ -0,0 +1,322 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device + */ +#include "qemu/osdep.h" +#include "qemu/crc32c.h" +#include "system/dma.h" +#include "migration/vmstate.h" + +#include "hw/uefi/var-service.h" +#include "hw/uefi/var-service-api.h" +#include "hw/uefi/var-service-edk2.h" + +#include "trace/trace-hw_uefi.h" + +static int uefi_vars_pre_load(void *opaque) +{ + uefi_vars_state *uv = opaque; + + uefi_vars_clear_all(uv); + uefi_vars_policies_clear(uv); + g_free(uv->buffer); + return 0; +} + +static int uefi_vars_post_load(void *opaque, int version_id) +{ + uefi_vars_state *uv = opaque; + + uefi_vars_update_storage(uv); + uefi_vars_json_save(uv); + uv->buffer = g_malloc(uv->buf_size); + return 0; +} + +const VMStateDescription vmstate_uefi_vars = { + .name = "uefi-vars", + .pre_load = uefi_vars_pre_load, + .post_load = uefi_vars_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT16(sts, uefi_vars_state), + VMSTATE_UINT32(buf_size, uefi_vars_state), + VMSTATE_UINT32(buf_addr_lo, uefi_vars_state), + VMSTATE_UINT32(buf_addr_hi, uefi_vars_state), + VMSTATE_UINT32(pio_xfer_offset, uefi_vars_state), + VMSTATE_VBUFFER_ALLOC_UINT32(pio_xfer_buffer, uefi_vars_state, + 0, NULL, buf_size), + VMSTATE_BOOL(end_of_dxe, uefi_vars_state), + VMSTATE_BOOL(ready_to_boot, uefi_vars_state), + VMSTATE_BOOL(exit_boot_service, uefi_vars_state), + VMSTATE_BOOL(policy_locked, uefi_vars_state), + VMSTATE_UINT64(used_storage, uefi_vars_state), + VMSTATE_QTAILQ_V(variables, uefi_vars_state, 0, + vmstate_uefi_variable, uefi_variable, next), + VMSTATE_QTAILQ_V(var_policies, uefi_vars_state, 0, + vmstate_uefi_var_policy, uefi_var_policy, next), + VMSTATE_END_OF_LIST() + }, +}; + +static uint32_t uefi_vars_cmd_mm(uefi_vars_state *uv, bool dma_mode) +{ + hwaddr dma; + mm_header *mhdr; + uint64_t size; + uint32_t retval; + + dma = uv->buf_addr_lo | ((hwaddr)uv->buf_addr_hi << 32); + mhdr = (mm_header *) uv->buffer; + + if (!uv->buffer || uv->buf_size < sizeof(*mhdr)) { + return UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE; + } + + /* read header */ + if (dma_mode) { + dma_memory_read(&address_space_memory, dma, + uv->buffer, sizeof(*mhdr), + MEMTXATTRS_UNSPECIFIED); + } else { + memcpy(uv->buffer, uv->pio_xfer_buffer, sizeof(*mhdr)); + } + + if (uadd64_overflow(sizeof(*mhdr), mhdr->length, &size)) { + return UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE; + } + if (uv->buf_size < size) { + return UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE; + } + + /* read buffer (excl header) */ + if (dma_mode) { + dma_memory_read(&address_space_memory, dma + sizeof(*mhdr), + uv->buffer + sizeof(*mhdr), mhdr->length, + MEMTXATTRS_UNSPECIFIED); + } else { + memcpy(uv->buffer + sizeof(*mhdr), + uv->pio_xfer_buffer + sizeof(*mhdr), + mhdr->length); + } + memset(uv->buffer + size, 0, uv->buf_size - size); + + /* dispatch */ + if (qemu_uuid_is_equal(&mhdr->guid, &EfiSmmVariableProtocolGuid)) { + retval = uefi_vars_mm_vars_proto(uv); + + } else if (qemu_uuid_is_equal(&mhdr->guid, &VarCheckPolicyLibMmiHandlerGuid)) { + retval = uefi_vars_mm_check_policy_proto(uv); + + } else if (qemu_uuid_is_equal(&mhdr->guid, &EfiEndOfDxeEventGroupGuid)) { + trace_uefi_event("end-of-dxe"); + uv->end_of_dxe = true; + retval = UEFI_VARS_STS_SUCCESS; + + } else if (qemu_uuid_is_equal(&mhdr->guid, &EfiEventReadyToBootGuid)) { + trace_uefi_event("ready-to-boot"); + uv->ready_to_boot = true; + retval = UEFI_VARS_STS_SUCCESS; + + } else if (qemu_uuid_is_equal(&mhdr->guid, &EfiEventExitBootServicesGuid)) { + trace_uefi_event("exit-boot-service"); + uv->exit_boot_service = true; + retval = UEFI_VARS_STS_SUCCESS; + + } else { + retval = UEFI_VARS_STS_ERR_NOT_SUPPORTED; + } + + /* write buffer */ + if (dma_mode) { + dma_memory_write(&address_space_memory, dma, + uv->buffer, sizeof(*mhdr) + mhdr->length, + MEMTXATTRS_UNSPECIFIED); + } else { + memcpy(uv->pio_xfer_buffer + sizeof(*mhdr), + uv->buffer + sizeof(*mhdr), + sizeof(*mhdr) + mhdr->length); + } + + return retval; +} + +static void uefi_vars_soft_reset(uefi_vars_state *uv) +{ + g_free(uv->buffer); + uv->buffer = NULL; + uv->buf_size = 0; + uv->buf_addr_lo = 0; + uv->buf_addr_hi = 0; +} + +void uefi_vars_hard_reset(uefi_vars_state *uv) +{ + trace_uefi_hard_reset(); + uefi_vars_soft_reset(uv); + + uv->end_of_dxe = false; + uv->ready_to_boot = false; + uv->exit_boot_service = false; + uv->policy_locked = false; + + uefi_vars_clear_volatile(uv); + uefi_vars_policies_clear(uv); + uefi_vars_auth_init(uv); +} + +static uint32_t uefi_vars_cmd(uefi_vars_state *uv, uint32_t cmd) +{ + switch (cmd) { + case UEFI_VARS_CMD_RESET: + uefi_vars_soft_reset(uv); + return UEFI_VARS_STS_SUCCESS; + case UEFI_VARS_CMD_DMA_MM: + return uefi_vars_cmd_mm(uv, true); + case UEFI_VARS_CMD_PIO_MM: + return uefi_vars_cmd_mm(uv, false); + case UEFI_VARS_CMD_PIO_ZERO_OFFSET: + uv->pio_xfer_offset = 0; + return UEFI_VARS_STS_SUCCESS; + default: + return UEFI_VARS_STS_ERR_NOT_SUPPORTED; + } +} + +static uint64_t uefi_vars_read(void *opaque, hwaddr addr, unsigned size) +{ + uefi_vars_state *uv = opaque; + uint64_t retval = -1; + void *xfer_ptr; + + trace_uefi_reg_read(addr, size); + + switch (addr) { + case UEFI_VARS_REG_MAGIC: + retval = UEFI_VARS_MAGIC_VALUE; + break; + case UEFI_VARS_REG_CMD_STS: + retval = uv->sts; + break; + case UEFI_VARS_REG_BUFFER_SIZE: + retval = uv->buf_size; + break; + case UEFI_VARS_REG_DMA_BUFFER_ADDR_LO: + retval = uv->buf_addr_lo; + break; + case UEFI_VARS_REG_DMA_BUFFER_ADDR_HI: + retval = uv->buf_addr_hi; + break; + case UEFI_VARS_REG_PIO_BUFFER_TRANSFER: + if (uv->pio_xfer_offset + size > uv->buf_size) { + retval = 0; + break; + } + xfer_ptr = uv->pio_xfer_buffer + uv->pio_xfer_offset; + switch (size) { + case 1: + retval = *(uint8_t *)xfer_ptr; + break; + case 2: + retval = *(uint16_t *)xfer_ptr; + break; + case 4: + retval = *(uint32_t *)xfer_ptr; + break; + case 8: + retval = *(uint64_t *)xfer_ptr; + break; + } + uv->pio_xfer_offset += size; + break; + case UEFI_VARS_REG_PIO_BUFFER_CRC32C: + retval = crc32c(0xffffffff, uv->pio_xfer_buffer, uv->pio_xfer_offset); + break; + case UEFI_VARS_REG_FLAGS: + retval = 0; + if (uv->use_pio) { + retval |= UEFI_VARS_FLAG_USE_PIO; + } + } + return retval; +} + +static void uefi_vars_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + uefi_vars_state *uv = opaque; + void *xfer_ptr; + + trace_uefi_reg_write(addr, val, size); + + switch (addr) { + case UEFI_VARS_REG_CMD_STS: + uv->sts = uefi_vars_cmd(uv, val); + break; + case UEFI_VARS_REG_BUFFER_SIZE: + if (val > MAX_BUFFER_SIZE) { + val = MAX_BUFFER_SIZE; + } + uv->buf_size = val; + g_free(uv->buffer); + g_free(uv->pio_xfer_buffer); + uv->buffer = g_malloc(uv->buf_size); + uv->pio_xfer_buffer = g_malloc(uv->buf_size); + break; + case UEFI_VARS_REG_DMA_BUFFER_ADDR_LO: + uv->buf_addr_lo = val; + break; + case UEFI_VARS_REG_DMA_BUFFER_ADDR_HI: + uv->buf_addr_hi = val; + break; + case UEFI_VARS_REG_PIO_BUFFER_TRANSFER: + if (uv->pio_xfer_offset + size > uv->buf_size) { + break; + } + xfer_ptr = uv->pio_xfer_buffer + uv->pio_xfer_offset; + switch (size) { + case 1: + *(uint8_t *)xfer_ptr = val; + break; + case 2: + *(uint16_t *)xfer_ptr = val; + break; + case 4: + *(uint32_t *)xfer_ptr = val; + break; + case 8: + *(uint64_t *)xfer_ptr = val; + break; + } + uv->pio_xfer_offset += size; + break; + case UEFI_VARS_REG_PIO_BUFFER_CRC32C: + case UEFI_VARS_REG_FLAGS: + default: + break; + } +} + +static const MemoryRegionOps uefi_vars_ops = { + .read = uefi_vars_read, + .write = uefi_vars_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 2, + .max_access_size = 4, + }, +}; + +void uefi_vars_init(Object *obj, uefi_vars_state *uv) +{ + QTAILQ_INIT(&uv->variables); + QTAILQ_INIT(&uv->var_policies); + uv->jsonfd = -1; + memory_region_init_io(&uv->mr, obj, &uefi_vars_ops, uv, + "uefi-vars", UEFI_VARS_REGS_SIZE); +} + +void uefi_vars_realize(uefi_vars_state *uv, Error **errp) +{ + uefi_vars_json_init(uv, errp); + uefi_vars_json_load(uv, errp); +} diff --git a/hw/uefi/var-service-guid.c b/hw/uefi/var-service-guid.c new file mode 100644 index 00000000000..eba3655c8d3 --- /dev/null +++ b/hw/uefi/var-service-guid.c @@ -0,0 +1,99 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - GUIDs + */ + +#include "qemu/osdep.h" +#include "system/dma.h" + +#include "hw/uefi/var-service.h" + +/* variable namespaces */ + +const QemuUUID EfiGlobalVariable = { + .data = UUID_LE(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, + 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c) +}; + +const QemuUUID EfiImageSecurityDatabase = { + .data = UUID_LE(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, + 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) +}; + +const QemuUUID EfiCustomModeEnable = { + .data = UUID_LE(0xc076ec0c, 0x7028, 0x4399, 0xa0, 0x72, + 0x71, 0xee, 0x5c, 0x44, 0x8b, 0x9f) +}; + +const QemuUUID EfiSecureBootEnableDisable = { + .data = UUID_LE(0xf0a30bc7, 0xaf08, 0x4556, 0x99, 0xc4, + 0x0, 0x10, 0x9, 0xc9, 0x3a, 0x44) +}; + +/* signatures */ + +const QemuUUID EfiCertSha256Guid = { + .data = UUID_LE(0xc1c41626, 0x504c, 0x4092, 0xac, 0xa9, + 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28) +}; + +const QemuUUID EfiCertSha384Guid = { + .data = UUID_LE(0xff3e5307, 0x9fd0, 0x48c9, 0x85, 0xf1, + 0x8a, 0xd5, 0x6c, 0x70, 0x1e, 0x1) +}; + +const QemuUUID EfiCertSha512Guid = { + .data = UUID_LE(0x93e0fae, 0xa6c4, 0x4f50, 0x9f, 0x1b, + 0xd4, 0x1e, 0x2b, 0x89, 0xc1, 0x9a) +}; + +const QemuUUID EfiCertRsa2048Guid = { + .data = UUID_LE(0x3c5766e8, 0x269c, 0x4e34, 0xaa, 0x14, + 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6) +}; + +const QemuUUID EfiCertX509Guid = { + .data = UUID_LE(0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, + 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72) +}; + +const QemuUUID EfiCertTypePkcs7Guid = { + .data = UUID_LE(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, + 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7) +}; + +/* + * mm_header.guid values that the guest DXE/BDS phases use for + * sending requests to management mode + */ + +const QemuUUID EfiSmmVariableProtocolGuid = { + .data = UUID_LE(0xed32d533, 0x99e6, 0x4209, 0x9c, 0xc0, + 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7) +}; + +const QemuUUID VarCheckPolicyLibMmiHandlerGuid = { + .data = UUID_LE(0xda1b0d11, 0xd1a7, 0x46c4, 0x9d, 0xc9, + 0xf3, 0x71, 0x48, 0x75, 0xc6, 0xeb) +}; + +/* + * mm_header.guid values that the guest DXE/BDS phases use for + * reporting event groups being signaled to management mode + */ + +const QemuUUID EfiEndOfDxeEventGroupGuid = { + .data = UUID_LE(0x02ce967a, 0xdd7e, 0x4FFc, 0x9e, 0xe7, + 0x81, 0x0c, 0xF0, 0x47, 0x08, 0x80) +}; + +const QemuUUID EfiEventReadyToBootGuid = { + .data = UUID_LE(0x7ce88Fb3, 0x4bd7, 0x4679, 0x87, 0xa8, + 0xa8, 0xd8, 0xde, 0xe5, 0x0d, 0x2b) +}; + +const QemuUUID EfiEventExitBootServicesGuid = { + .data = UUID_LE(0x27abF055, 0xb1b8, 0x4c26, 0x80, 0x48, + 0x74, 0x8F, 0x37, 0xba, 0xa2, 0xdF) +}; diff --git a/hw/uefi/var-service-json.c b/hw/uefi/var-service-json.c new file mode 100644 index 00000000000..ad3462cd155 --- /dev/null +++ b/hw/uefi/var-service-json.c @@ -0,0 +1,257 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - serialize non-volatile varstore from/to json, + * using qapi + * + * tools which can read/write these json files: + * - https://gitlab.com/kraxel/virt-firmware + * - https://github.com/awslabs/python-uefivars + */ +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "system/dma.h" + +#include "hw/uefi/var-service.h" + +#include "qobject/qobject.h" +#include "qobject/qjson.h" + +#include "qapi/dealloc-visitor.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" +#include "qapi/qapi-types-uefi.h" +#include "qapi/qapi-visit-uefi.h" + +static char *generate_hexstr(void *data, size_t len) +{ + static const char hex[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', + }; + uint8_t *src = data; + char *dest; + size_t i; + + dest = g_malloc(len * 2 + 1); + for (i = 0; i < len * 2;) { + dest[i++] = hex[*src >> 4]; + dest[i++] = hex[*src & 15]; + src++; + } + dest[i++] = 0; + + return dest; +} + +static UefiVarStore *uefi_vars_to_qapi(uefi_vars_state *uv) +{ + UefiVarStore *vs; + UefiVariableList **tail; + UefiVariable *v; + QemuUUID be; + uefi_variable *var; + + vs = g_new0(UefiVarStore, 1); + vs->version = 2; + tail = &vs->variables; + + QTAILQ_FOREACH(var, &uv->variables, next) { + if (!(var->attributes & EFI_VARIABLE_NON_VOLATILE)) { + continue; + } + + v = g_new0(UefiVariable, 1); + be = qemu_uuid_bswap(var->guid); + v->guid = qemu_uuid_unparse_strdup(&be); + v->name = uefi_ucs2_to_ascii(var->name, var->name_size); + v->attr = var->attributes; + + v->data = generate_hexstr(var->data, var->data_size); + + if (var->attributes & + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { + v->time = generate_hexstr(&var->time, sizeof(var->time)); + if (var->digest && var->digest_size) { + v->digest = generate_hexstr(var->digest, var->digest_size); + } + } + + QAPI_LIST_APPEND(tail, v); + } + return vs; +} + +static unsigned parse_hexchar(char c) +{ + switch (c) { + case '0' ... '9': return c - '0'; + case 'a' ... 'f': return c - 'a' + 0xa; + case 'A' ... 'F': return c - 'A' + 0xA; + default: return 0; + } +} + +static void parse_hexstr(void *dest, char *src, int len) +{ + uint8_t *data = dest; + size_t i; + + for (i = 0; i < len; i += 2) { + *(data++) = + parse_hexchar(src[i]) << 4 | + parse_hexchar(src[i + 1]); + } +} + +static void uefi_vars_from_qapi(uefi_vars_state *uv, UefiVarStore *vs) +{ + UefiVariableList *item; + UefiVariable *v; + QemuUUID be; + uefi_variable *var; + uint8_t *data; + size_t i, len; + + for (item = vs->variables; item != NULL; item = item->next) { + v = item->value; + + var = g_new0(uefi_variable, 1); + var->attributes = v->attr; + qemu_uuid_parse(v->guid, &be); + var->guid = qemu_uuid_bswap(be); + + len = strlen(v->name); + var->name_size = len * 2 + 2; + var->name = g_malloc(var->name_size); + for (i = 0; i <= len; i++) { + var->name[i] = v->name[i]; + } + + len = strlen(v->data); + var->data_size = len / 2; + var->data = data = g_malloc(var->data_size); + parse_hexstr(var->data, v->data, len); + + if (v->time && strlen(v->time) == 32) { + parse_hexstr(&var->time, v->time, 32); + } + + if (v->digest) { + len = strlen(v->digest); + var->digest_size = len / 2; + var->digest = g_malloc(var->digest_size); + parse_hexstr(var->digest, v->digest, len); + } + + QTAILQ_INSERT_TAIL(&uv->variables, var, next); + } +} + +static GString *uefi_vars_to_json(uefi_vars_state *uv) +{ + UefiVarStore *vs = uefi_vars_to_qapi(uv); + QObject *qobj = NULL; + Visitor *v; + GString *gstr; + + v = qobject_output_visitor_new(&qobj); + if (visit_type_UefiVarStore(v, NULL, &vs, NULL)) { + visit_complete(v, &qobj); + } + visit_free(v); + qapi_free_UefiVarStore(vs); + + gstr = qobject_to_json_pretty(qobj, true); + qobject_unref(qobj); + + return gstr; +} + +void uefi_vars_json_init(uefi_vars_state *uv, Error **errp) +{ + if (uv->jsonfile) { + uv->jsonfd = qemu_create(uv->jsonfile, O_RDWR, 0666, errp); + } +} + +void uefi_vars_json_save(uefi_vars_state *uv) +{ + g_autoptr(GString) gstr = NULL; + int rc; + + if (uv->jsonfd == -1) { + return; + } + + gstr = uefi_vars_to_json(uv); + + rc = lseek(uv->jsonfd, 0, SEEK_SET); + if (rc < 0) { + warn_report("%s: lseek error", __func__); + return; + } + + rc = ftruncate(uv->jsonfd, 0); + if (rc != 0) { + warn_report("%s: ftruncate error", __func__); + return; + } + + rc = write(uv->jsonfd, gstr->str, gstr->len); + if (rc != gstr->len) { + warn_report("%s: write error", __func__); + return; + } + + fsync(uv->jsonfd); +} + +void uefi_vars_json_load(uefi_vars_state *uv, Error **errp) +{ + UefiVarStore *vs; + QObject *qobj; + Visitor *v; + char *str; + ssize_t len; + int rc; + + if (uv->jsonfd == -1) { + return; + } + + len = lseek(uv->jsonfd, 0, SEEK_END); + if (len < 0) { + warn_report("%s: lseek error", __func__); + return; + } + if (len == 0) { + /* empty file */ + return; + } + + str = g_malloc(len + 1); + lseek(uv->jsonfd, 0, SEEK_SET); + rc = read(uv->jsonfd, str, len); + if (rc != len) { + warn_report("%s: read error", __func__); + g_free(str); + return; + } + str[len] = 0; + + qobj = qobject_from_json(str, errp); + v = qobject_input_visitor_new(qobj); + visit_type_UefiVarStore(v, NULL, &vs, errp); + visit_free(v); + + if (!(*errp)) { + uefi_vars_from_qapi(uv, vs); + uefi_vars_update_storage(uv); + } + + qapi_free_UefiVarStore(vs); + qobject_unref(qobj); + g_free(str); +} diff --git a/hw/uefi/var-service-pkcs7-stub.c b/hw/uefi/var-service-pkcs7-stub.c new file mode 100644 index 00000000000..118cba446d4 --- /dev/null +++ b/hw/uefi/var-service-pkcs7-stub.c @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - pkcs7 stubs + */ +#include "qemu/osdep.h" +#include "system/dma.h" + +#include "hw/uefi/var-service.h" + +efi_status uefi_vars_check_pkcs7_2(uefi_variable *siglist, + void **digest, uint32_t *digest_size, + mm_variable_access *va, void *data) +{ + return EFI_WRITE_PROTECTED; +} diff --git a/hw/uefi/var-service-pkcs7.c b/hw/uefi/var-service-pkcs7.c new file mode 100644 index 00000000000..32accf4e44e --- /dev/null +++ b/hw/uefi/var-service-pkcs7.c @@ -0,0 +1,436 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - pkcs7 verification + */ +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "system/dma.h" + +#include +#include +#include + +#include "hw/uefi/var-service.h" + +#define AUTHVAR_DIGEST_ALGO GNUTLS_DIG_SHA256 +#define AUTHVAR_DIGEST_SIZE 32 + +/* + * Replicate the signed data for signature verification. + */ +static gnutls_datum_t *build_signed_data(mm_variable_access *va, void *data) +{ + variable_auth_2 *auth = data; + uint64_t data_offset = sizeof(efi_time) + auth->hdr_length; + uint16_t *name = (void *)va + sizeof(mm_variable_access); + gnutls_datum_t *sdata; + uint64_t pos = 0; + + sdata = g_new(gnutls_datum_t, 1); + sdata->size = (va->name_size - 2 + + sizeof(QemuUUID) + + sizeof(va->attributes) + + sizeof(auth->timestamp) + + va->data_size - data_offset); + sdata->data = g_malloc(sdata->size); + + /* Variable Name (without terminating \0) */ + memcpy(sdata->data + pos, name, va->name_size - 2); + pos += va->name_size - 2; + + /* Variable Namespace Guid */ + memcpy(sdata->data + pos, &va->guid, sizeof(va->guid)); + pos += sizeof(va->guid); + + /* Attributes */ + memcpy(sdata->data + pos, &va->attributes, sizeof(va->attributes)); + pos += sizeof(va->attributes); + + /* TimeStamp */ + memcpy(sdata->data + pos, &auth->timestamp, sizeof(auth->timestamp)); + pos += sizeof(auth->timestamp); + + /* Variable Content */ + memcpy(sdata->data + pos, data + data_offset, va->data_size - data_offset); + pos += va->data_size - data_offset; + + assert(pos == sdata->size); + return sdata; +} + +/* + * See WrapPkcs7Data() in edk2. + * + * UEFI spec allows pkcs7 signatures being used without the envelope which + * identifies them as pkcs7 signatures. openssl and gnutls will not parse them + * without the envelope though. So add it if needed. + */ +static void wrap_pkcs7(gnutls_datum_t *pkcs7) +{ + static uint8_t signed_data_oid[9] = { + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02 + }; + gnutls_datum_t wrap; + + if (pkcs7->data[4] == 0x06 && + pkcs7->data[5] == 0x09 && + memcmp(pkcs7->data + 6, signed_data_oid, sizeof(signed_data_oid)) == 0 && + pkcs7->data[15] == 0x0a && + pkcs7->data[16] == 0x82) { + return; + } + + wrap.size = pkcs7->size + 19; + wrap.data = g_malloc(wrap.size); + + wrap.data[0] = 0x30; + wrap.data[1] = 0x82; + wrap.data[2] = (wrap.size - 4) >> 8; + wrap.data[3] = (wrap.size - 4) & 0xff; + wrap.data[4] = 0x06; + wrap.data[5] = 0x09; + memcpy(wrap.data + 6, signed_data_oid, sizeof(signed_data_oid)); + + wrap.data[15] = 0xa0; + wrap.data[16] = 0x82; + wrap.data[17] = pkcs7->size >> 8; + wrap.data[18] = pkcs7->size & 0xff; + memcpy(wrap.data + 19, pkcs7->data, pkcs7->size); + + g_free(pkcs7->data); + *pkcs7 = wrap; +} + +static gnutls_datum_t *build_pkcs7(void *data) +{ + variable_auth_2 *auth = data; + gnutls_datum_t *pkcs7; + + pkcs7 = g_new(gnutls_datum_t, 1); + pkcs7->size = auth->hdr_length - 24; + pkcs7->data = g_malloc(pkcs7->size); + memcpy(pkcs7->data, data + 16 + 24, pkcs7->size); + + wrap_pkcs7(pkcs7); + + return pkcs7; +} + +/* + * Read UEFI signature database, store x509 all certificates found in + * gnutls_x509_trust_list_t. + */ +static gnutls_x509_trust_list_t build_trust_list_sb(uefi_variable *var) +{ + gnutls_x509_trust_list_t tlist; + gnutls_datum_t cert_data; + gnutls_x509_crt_t cert; + uefi_vars_siglist siglist; + uefi_vars_cert *c; + int rc; + + rc = gnutls_x509_trust_list_init(&tlist, 0); + if (rc < 0) { + warn_report("gnutls_x509_trust_list_init error: %s", + gnutls_strerror(rc)); + return NULL; + } + + uefi_vars_siglist_init(&siglist); + uefi_vars_siglist_parse(&siglist, var->data, var->data_size); + + QTAILQ_FOREACH(c, &siglist.x509, next) { + cert_data.size = c->size; + cert_data.data = c->data; + + rc = gnutls_x509_crt_init(&cert); + if (rc < 0) { + warn_report("gnutls_x509_crt_init error: %s", gnutls_strerror(rc)); + break; + } + rc = gnutls_x509_crt_import(cert, &cert_data, GNUTLS_X509_FMT_DER); + if (rc < 0) { + warn_report("gnutls_x509_crt_import error: %s", + gnutls_strerror(rc)); + gnutls_x509_crt_deinit(cert); + break; + } + rc = gnutls_x509_trust_list_add_cas(tlist, &cert, 1, 0); + if (rc < 0) { + warn_report("gnutls_x509_crt_import error: %s", + gnutls_strerror(rc)); + gnutls_x509_crt_deinit(cert); + break; + } + } + + uefi_vars_siglist_free(&siglist); + + return tlist; +} + +static int build_digest_authvar(gnutls_x509_crt_t signer, + gnutls_x509_crt_t root, + uint8_t *hash_digest) +{ + char *cn; + size_t cn_size = 0; + uint8_t fp[AUTHVAR_DIGEST_SIZE]; + size_t fp_size = sizeof(fp); + gnutls_hash_hd_t hash; + int rc; + + /* get signer CN */ + rc = gnutls_x509_crt_get_dn_by_oid(signer, GNUTLS_OID_X520_COMMON_NAME, + 0, 0, NULL, &cn_size); + if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) { + warn_report("gnutls_x509_crt_get_dn_by_oid error #1: %s", + gnutls_strerror(rc)); + return rc; + } + + cn = g_malloc(cn_size); + rc = gnutls_x509_crt_get_dn_by_oid(signer, GNUTLS_OID_X520_COMMON_NAME, + 0, 0, cn, &cn_size); + if (rc < 0) { + warn_report("gnutls_x509_crt_get_dn_by_oid error #2: %s", + gnutls_strerror(rc)); + goto err; + } + + /* get root certificate fingerprint */ + rc = gnutls_x509_crt_get_fingerprint(root, AUTHVAR_DIGEST_ALGO, + fp, &fp_size); + if (rc < 0) { + warn_report("gnutls_x509_crt_get_fingerprint error: %s", + gnutls_strerror(rc)); + goto err; + } + + /* digest both items */ + rc = gnutls_hash_init(&hash, AUTHVAR_DIGEST_ALGO); + if (rc < 0) { + warn_report("gnutls_hash_init error: %s", + gnutls_strerror(rc)); + goto err; + } + rc = gnutls_hash(hash, cn, cn_size); + if (rc < 0) { + warn_report("gnutls_hash error: %s", + gnutls_strerror(rc)); + goto err; + } + rc = gnutls_hash(hash, fp, fp_size); + if (rc < 0) { + warn_report("gnutls_hash error: %s", + gnutls_strerror(rc)); + goto err; + } + gnutls_hash_deinit(hash, hash_digest); + + return 0; + +err: + g_free(cn); + return rc; +} + +/* + * uefi spec 2.9, section 8.2.2 + * + * For EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS variables which are + * NOT secure boot variables we should track the root certificate of the trust + * chain, and the subject CN of the signer certificate. + * + * So we'll go store a digest of these two items so we can verify this. Also + * create a gnutls_x509_trust_list_t with the root certificate, so + * gnutls_pkcs7_verify() will pass (assuming the signature is otherwise + * correct). + */ +static gnutls_x509_trust_list_t build_trust_list_authvar(gnutls_pkcs7_t pkcs7, + uint8_t *hash_digest) +{ + gnutls_datum_t signer_data = { 0 }; + gnutls_datum_t root_data = { 0 }; + gnutls_x509_crt_t signer = NULL; + gnutls_x509_crt_t root = NULL; + gnutls_x509_trust_list_t tlist = NULL; + int n, rc; + + n = gnutls_pkcs7_get_crt_count(pkcs7); + + /* first is signer certificate */ + rc = gnutls_pkcs7_get_crt_raw2(pkcs7, 0, &signer_data); + if (rc < 0) { + warn_report("gnutls_pkcs7_get_crt_raw2(0) error: %s", + gnutls_strerror(rc)); + goto done; + } + rc = gnutls_x509_crt_init(&signer); + if (rc < 0) { + warn_report("gnutls_x509_crt_init error: %s", gnutls_strerror(rc)); + goto done; + } + rc = gnutls_x509_crt_import(signer, &signer_data, GNUTLS_X509_FMT_DER); + if (rc < 0) { + warn_report("gnutls_x509_crt_import error: %s", + gnutls_strerror(rc)); + gnutls_x509_crt_deinit(signer); + goto done; + } + + /* last is root-of-trust certificate (can be identical to signer) */ + rc = gnutls_pkcs7_get_crt_raw2(pkcs7, n - 1, &root_data); + if (rc < 0) { + warn_report("gnutls_pkcs7_get_crt_raw2(%d) error: %s", + n - 1, gnutls_strerror(rc)); + goto done; + } + rc = gnutls_x509_crt_init(&root); + if (rc < 0) { + warn_report("gnutls_x509_crt_init error: %s", gnutls_strerror(rc)); + goto done; + } + rc = gnutls_x509_crt_import(root, &root_data, GNUTLS_X509_FMT_DER); + if (rc < 0) { + warn_report("gnutls_x509_crt_import error: %s", + gnutls_strerror(rc)); + goto done; + } + + /* calc digest for signer CN + root cert */ + rc = build_digest_authvar(signer, root, hash_digest); + if (rc < 0) { + goto done; + } + + /* add root to trust list */ + rc = gnutls_x509_trust_list_init(&tlist, 0); + if (rc < 0) { + warn_report("gnutls_x509_trust_list_init error: %s", + gnutls_strerror(rc)); + goto done; + } + rc = gnutls_x509_trust_list_add_cas(tlist, &root, 1, 0); + if (rc < 0) { + warn_report("gnutls_x509_crt_import error: %s", + gnutls_strerror(rc)); + gnutls_x509_trust_list_deinit(tlist, 1); + tlist = NULL; + goto done; + } else { + /* ownership passed to tlist */ + root = NULL; + } + +done: + if (signer_data.data) { + gnutls_free(signer_data.data); + } + if (root_data.data) { + gnutls_free(root_data.data); + } + if (signer) { + gnutls_x509_crt_deinit(signer); + } + if (root) { + gnutls_x509_crt_deinit(root); + } + return tlist; +} + +static void free_datum(gnutls_datum_t *ptr) +{ + if (!ptr) { + return; + } + g_free(ptr->data); + g_free(ptr); +} + +static void gnutls_log_stderr(int level, const char *msg) +{ + if (strncmp(msg, "ASSERT:", 7) == 0) { + return; + } + fprintf(stderr, " %d: %s", level, msg); +} + +/* + * pkcs7 signature verification (EFI_VARIABLE_AUTHENTICATION_2). + */ +efi_status uefi_vars_check_pkcs7_2(uefi_variable *siglist, + void **digest, uint32_t *digest_size, + mm_variable_access *va, void *data) +{ + gnutls_x509_trust_list_t tlist = NULL; + gnutls_datum_t *signed_data = NULL; + gnutls_datum_t *pkcs7_data = NULL; + gnutls_pkcs7_t pkcs7 = NULL; + efi_status status = EFI_SECURITY_VIOLATION; + int rc; + + if (0) { + /* gnutls debug logging */ + static bool first = true; + + if (first) { + first = false; + gnutls_global_set_log_function(gnutls_log_stderr); + gnutls_global_set_log_level(99); + } + } + + signed_data = build_signed_data(va, data); + pkcs7_data = build_pkcs7(data); + + rc = gnutls_pkcs7_init(&pkcs7); + if (rc < 0) { + warn_report("gnutls_pkcs7_init error: %s", gnutls_strerror(rc)); + goto out; + } + + rc = gnutls_pkcs7_import(pkcs7, pkcs7_data, GNUTLS_X509_FMT_DER); + if (rc < 0) { + warn_report("gnutls_pkcs7_import error: %s", gnutls_strerror(rc)); + goto out; + } + + if (siglist) { + /* secure boot variables */ + tlist = build_trust_list_sb(siglist); + } else if (digest && digest_size) { + /* other authenticated variables */ + *digest_size = AUTHVAR_DIGEST_SIZE; + *digest = g_malloc(*digest_size); + tlist = build_trust_list_authvar(pkcs7, *digest); + } else { + /* should not happen */ + goto out; + } + + rc = gnutls_pkcs7_verify(pkcs7, tlist, + NULL, 0, + 0, signed_data, + GNUTLS_VERIFY_DISABLE_TIME_CHECKS | + GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS); + if (rc < 0) { + warn_report("gnutls_pkcs7_verify error: %s", gnutls_strerror(rc)); + goto out; + } + + /* check passed */ + status = EFI_SUCCESS; + +out: + free_datum(signed_data); + free_datum(pkcs7_data); + if (tlist) { + gnutls_x509_trust_list_deinit(tlist, 1); + } + if (pkcs7) { + gnutls_pkcs7_deinit(pkcs7); + } + return status; +} diff --git a/hw/uefi/var-service-policy.c b/hw/uefi/var-service-policy.c new file mode 100644 index 00000000000..3b1155fe4ea --- /dev/null +++ b/hw/uefi/var-service-policy.c @@ -0,0 +1,370 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - VarCheckPolicyLibMmiHandler implementation + * + * variable policy specs: + * https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md + */ +#include "qemu/osdep.h" +#include "system/dma.h" +#include "migration/vmstate.h" + +#include "hw/uefi/var-service.h" +#include "hw/uefi/var-service-api.h" +#include "hw/uefi/var-service-edk2.h" + +#include "trace/trace-hw_uefi.h" + +static void calc_policy(uefi_var_policy *pol); + +static int uefi_var_policy_post_load(void *opaque, int version_id) +{ + uefi_var_policy *pol = opaque; + + calc_policy(pol); + return 0; +} + +const VMStateDescription vmstate_uefi_var_policy = { + .name = "uefi-var-policy", + .post_load = uefi_var_policy_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(entry_size, uefi_var_policy), + VMSTATE_VBUFFER_ALLOC_UINT32(entry, uefi_var_policy, + 0, NULL, entry_size), + VMSTATE_END_OF_LIST() + }, +}; + +static void print_policy_entry(variable_policy_entry *pe) +{ + uint16_t *name = (void *)pe + pe->offset_to_name; + + fprintf(stderr, "%s:\n", __func__); + + fprintf(stderr, " name ´"); + while (*name) { + fprintf(stderr, "%c", *name); + name++; + } + fprintf(stderr, "', version=%d.%d, size=%d\n", + pe->version >> 16, pe->version & 0xffff, pe->size); + + if (pe->min_size) { + fprintf(stderr, " size min=%d\n", pe->min_size); + } + if (pe->max_size != UINT32_MAX) { + fprintf(stderr, " size max=%u\n", pe->max_size); + } + if (pe->attributes_must_have) { + fprintf(stderr, " attr must=0x%x\n", pe->attributes_must_have); + } + if (pe->attributes_cant_have) { + fprintf(stderr, " attr cant=0x%x\n", pe->attributes_cant_have); + } + if (pe->lock_policy_type) { + fprintf(stderr, " lock policy type %d\n", pe->lock_policy_type); + } +} + +static gboolean wildcard_str_equal(uefi_var_policy *pol, + uefi_variable *var) +{ + return uefi_str_equal_ex(pol->name, pol->name_size, + var->name, var->name_size, + true); +} + +static uefi_var_policy *find_policy(uefi_vars_state *uv, QemuUUID guid, + uint16_t *name, uint64_t name_size) +{ + uefi_var_policy *pol; + + QTAILQ_FOREACH(pol, &uv->var_policies, next) { + if (!qemu_uuid_is_equal(&pol->entry->namespace, &guid)) { + continue; + } + if (!uefi_str_equal(pol->name, pol->name_size, + name, name_size)) { + continue; + } + return pol; + } + return NULL; +} + +static uefi_var_policy *wildcard_find_policy(uefi_vars_state *uv, + uefi_variable *var) +{ + uefi_var_policy *pol; + + QTAILQ_FOREACH(pol, &uv->var_policies, next) { + if (!qemu_uuid_is_equal(&pol->entry->namespace, &var->guid)) { + continue; + } + if (!wildcard_str_equal(pol, var)) { + continue; + } + return pol; + } + return NULL; +} + +static void calc_policy(uefi_var_policy *pol) +{ + variable_policy_entry *pe = pol->entry; + unsigned int i; + + pol->name = (void *)pol->entry + pe->offset_to_name; + pol->name_size = pe->size - pe->offset_to_name; + + for (i = 0; i < pol->name_size / 2; i++) { + if (pol->name[i] == '#') { + pol->hashmarks++; + } + } +} + +uefi_var_policy *uefi_vars_add_policy(uefi_vars_state *uv, + variable_policy_entry *pe) +{ + uefi_var_policy *pol, *p; + + pol = g_new0(uefi_var_policy, 1); + pol->entry = g_malloc(pe->size); + memcpy(pol->entry, pe, pe->size); + pol->entry_size = pe->size; + + calc_policy(pol); + + /* keep list sorted by priority, add to tail of priority group */ + QTAILQ_FOREACH(p, &uv->var_policies, next) { + if ((p->hashmarks > pol->hashmarks) || + (!p->name_size && pol->name_size)) { + QTAILQ_INSERT_BEFORE(p, pol, next); + return pol; + } + } + + QTAILQ_INSERT_TAIL(&uv->var_policies, pol, next); + return pol; +} + +efi_status uefi_vars_policy_check(uefi_vars_state *uv, + uefi_variable *var, + gboolean is_newvar) +{ + uefi_var_policy *pol; + variable_policy_entry *pe; + variable_lock_on_var_state *lvarstate; + uint16_t *lvarname; + size_t lvarnamesize; + uefi_variable *lvar; + + if (!uv->end_of_dxe) { + return EFI_SUCCESS; + } + + pol = wildcard_find_policy(uv, var); + if (!pol) { + return EFI_SUCCESS; + } + pe = pol->entry; + + uefi_trace_variable(__func__, var->guid, var->name, var->name_size); + print_policy_entry(pe); + + if ((var->attributes & pe->attributes_must_have) != pe->attributes_must_have) { + trace_uefi_vars_policy_deny("must-have-attr"); + return EFI_INVALID_PARAMETER; + } + if ((var->attributes & pe->attributes_cant_have) != 0) { + trace_uefi_vars_policy_deny("cant-have-attr"); + return EFI_INVALID_PARAMETER; + } + + if (var->data_size < pe->min_size) { + trace_uefi_vars_policy_deny("min-size"); + return EFI_INVALID_PARAMETER; + } + if (var->data_size > pe->max_size) { + trace_uefi_vars_policy_deny("max-size"); + return EFI_INVALID_PARAMETER; + } + + switch (pe->lock_policy_type) { + case VARIABLE_POLICY_TYPE_NO_LOCK: + break; + + case VARIABLE_POLICY_TYPE_LOCK_NOW: + trace_uefi_vars_policy_deny("lock-now"); + return EFI_WRITE_PROTECTED; + + case VARIABLE_POLICY_TYPE_LOCK_ON_CREATE: + if (!is_newvar) { + trace_uefi_vars_policy_deny("lock-on-create"); + return EFI_WRITE_PROTECTED; + } + break; + + case VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE: + lvarstate = (void *)pol->entry + sizeof(*pe); + lvarname = (void *)pol->entry + sizeof(*pe) + sizeof(*lvarstate); + lvarnamesize = pe->offset_to_name - sizeof(*pe) - sizeof(*lvarstate); + + uefi_trace_variable(__func__, lvarstate->namespace, + lvarname, lvarnamesize); + lvar = uefi_vars_find_variable(uv, lvarstate->namespace, + lvarname, lvarnamesize); + if (lvar && lvar->data_size == 1) { + uint8_t *value = lvar->data; + if (lvarstate->value == *value) { + return EFI_WRITE_PROTECTED; + } + } + break; + } + + return EFI_SUCCESS; +} + +void uefi_vars_policies_clear(uefi_vars_state *uv) +{ + uefi_var_policy *pol; + + while (!QTAILQ_EMPTY(&uv->var_policies)) { + pol = QTAILQ_FIRST(&uv->var_policies); + QTAILQ_REMOVE(&uv->var_policies, pol, next); + g_free(pol->entry); + g_free(pol); + } +} + +static size_t uefi_vars_mm_policy_error(mm_header *mhdr, + mm_check_policy *mchk, + uint64_t status) +{ + mchk->result = status; + return sizeof(*mchk); +} + +static uint32_t uefi_vars_mm_check_policy_is_enabled(uefi_vars_state *uv, + mm_header *mhdr, + mm_check_policy *mchk, + void *func) +{ + mm_check_policy_is_enabled *mpar = func; + size_t length; + + length = sizeof(*mchk) + sizeof(*mpar); + if (mhdr->length < length) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } + + mpar->state = TRUE; + mchk->result = EFI_SUCCESS; + return sizeof(*mchk); +} + +static uint32_t uefi_vars_mm_check_policy_register(uefi_vars_state *uv, + mm_header *mhdr, + mm_check_policy *mchk, + void *func) +{ + variable_policy_entry *pe = func; + uefi_var_policy *pol; + uint64_t length; + + if (uadd64_overflow(sizeof(*mchk), pe->size, &length)) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } + if (mhdr->length < length) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } + if (pe->size < sizeof(*pe)) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } + if (pe->offset_to_name < sizeof(*pe)) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } + + if (pe->lock_policy_type == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE && + pe->offset_to_name < sizeof(*pe) + sizeof(variable_lock_on_var_state)) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } + + /* check space for minimum string length */ + if (pe->size < (size_t)pe->offset_to_name) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } + + if (!uefi_str_is_valid((void *)pe + pe->offset_to_name, + pe->size - pe->offset_to_name, + false)) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_INVALID_PARAMETER); + } + + pol = find_policy(uv, pe->namespace, + (void *)pe + pe->offset_to_name, + pe->size - pe->offset_to_name); + if (pol) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_ALREADY_STARTED); + } + + uefi_vars_add_policy(uv, pe); + + mchk->result = EFI_SUCCESS; + return sizeof(*mchk); +} + +uint32_t uefi_vars_mm_check_policy_proto(uefi_vars_state *uv) +{ + static const char *fnames[] = { + "zero", + "disable", + "is-enabled", + "register", + "dump", + "lock", + }; + const char *fname; + mm_header *mhdr = (mm_header *) uv->buffer; + mm_check_policy *mchk = (mm_check_policy *) (uv->buffer + sizeof(*mhdr)); + void *func = (uv->buffer + sizeof(*mhdr) + sizeof(*mchk)); + + if (mhdr->length < sizeof(*mchk)) { + return UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE; + } + + fname = mchk->command < ARRAY_SIZE(fnames) + ? fnames[mchk->command] + : "unknown"; + trace_uefi_vars_policy_cmd(fname); + + switch (mchk->command) { + case VAR_CHECK_POLICY_COMMAND_DISABLE: + mchk->result = EFI_UNSUPPORTED; + break; + case VAR_CHECK_POLICY_COMMAND_IS_ENABLED: + uefi_vars_mm_check_policy_is_enabled(uv, mhdr, mchk, func); + break; + case VAR_CHECK_POLICY_COMMAND_REGISTER: + if (uv->policy_locked) { + mchk->result = EFI_WRITE_PROTECTED; + } else { + uefi_vars_mm_check_policy_register(uv, mhdr, mchk, func); + } + break; + case VAR_CHECK_POLICY_COMMAND_LOCK: + uv->policy_locked = true; + mchk->result = EFI_SUCCESS; + break; + default: + mchk->result = EFI_UNSUPPORTED; + break; + } + + uefi_trace_status(__func__, mchk->result); + return UEFI_VARS_STS_SUCCESS; +} diff --git a/hw/uefi/var-service-siglist.c b/hw/uefi/var-service-siglist.c new file mode 100644 index 00000000000..8948f1b7847 --- /dev/null +++ b/hw/uefi/var-service-siglist.c @@ -0,0 +1,212 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - parse and generate efi signature databases + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "system/dma.h" + +#include "hw/uefi/var-service.h" + +/* + * Add x509 certificate to list (with duplicate check). + */ +static void uefi_vars_siglist_add_x509(uefi_vars_siglist *siglist, + QemuUUID *owner, + void *data, uint64_t size) +{ + uefi_vars_cert *c; + + QTAILQ_FOREACH(c, &siglist->x509, next) { + if (c->size != size) { + continue; + } + if (memcmp(c->data, data, size) != 0) { + continue; + } + return; + } + + c = g_malloc(sizeof(*c) + size); + c->owner = *owner; + c->size = size; + memcpy(c->data, data, size); + QTAILQ_INSERT_TAIL(&siglist->x509, c, next); +} + +/* + * Add sha256 hash to list (with duplicate check). + */ +static void uefi_vars_siglist_add_sha256(uefi_vars_siglist *siglist, + QemuUUID *owner, + void *data) +{ + uefi_vars_hash *h; + + QTAILQ_FOREACH(h, &siglist->sha256, next) { + if (memcmp(h->data, data, 32) != 0) { + continue; + } + return; + } + + h = g_malloc(sizeof(*h) + 32); + h->owner = *owner; + memcpy(h->data, data, 32); + QTAILQ_INSERT_TAIL(&siglist->sha256, h, next); +} + +void uefi_vars_siglist_init(uefi_vars_siglist *siglist) +{ + memset(siglist, 0, sizeof(*siglist)); + QTAILQ_INIT(&siglist->x509); + QTAILQ_INIT(&siglist->sha256); +} + +void uefi_vars_siglist_free(uefi_vars_siglist *siglist) +{ + uefi_vars_cert *c, *cs; + uefi_vars_hash *h, *hs; + + QTAILQ_FOREACH_SAFE(c, &siglist->x509, next, cs) { + QTAILQ_REMOVE(&siglist->x509, c, next); + g_free(c); + } + QTAILQ_FOREACH_SAFE(h, &siglist->sha256, next, hs) { + QTAILQ_REMOVE(&siglist->sha256, h, next); + g_free(h); + } +} + +/* + * Parse UEFI signature list. + */ +void uefi_vars_siglist_parse(uefi_vars_siglist *siglist, + void *data, uint64_t size) +{ + efi_siglist *efilist; + uint64_t start; + + while (size) { + if (size < sizeof(*efilist)) { + break; + } + efilist = data; + if (size < efilist->siglist_size) { + break; + } + + if (uadd64_overflow(sizeof(*efilist), efilist->header_size, &start)) { + break; + } + if (efilist->sig_size <= sizeof(QemuUUID)) { + break; + } + + if (qemu_uuid_is_equal(&efilist->guid_type, &EfiCertX509Guid)) { + if (start + efilist->sig_size != efilist->siglist_size) { + break; + } + uefi_vars_siglist_add_x509(siglist, + (QemuUUID *)(data + start), + data + start + sizeof(QemuUUID), + efilist->sig_size - sizeof(QemuUUID)); + + } else if (qemu_uuid_is_equal(&efilist->guid_type, &EfiCertSha256Guid)) { + if (efilist->sig_size != sizeof(QemuUUID) + 32) { + break; + } + if (start + efilist->sig_size > efilist->siglist_size) { + break; + } + while (start <= efilist->siglist_size - efilist->sig_size) { + uefi_vars_siglist_add_sha256(siglist, + (QemuUUID *)(data + start), + data + start + sizeof(QemuUUID)); + start += efilist->sig_size; + } + + } else { + QemuUUID be = qemu_uuid_bswap(efilist->guid_type); + char *str_uuid = qemu_uuid_unparse_strdup(&be); + warn_report("%s: unknown type (%s)", __func__, str_uuid); + g_free(str_uuid); + } + + data += efilist->siglist_size; + size -= efilist->siglist_size; + } +} + +uint64_t uefi_vars_siglist_blob_size(uefi_vars_siglist *siglist) +{ + uefi_vars_cert *c; + uefi_vars_hash *h; + uint64_t size = 0; + + QTAILQ_FOREACH(c, &siglist->x509, next) { + size += sizeof(efi_siglist) + sizeof(QemuUUID) + c->size; + } + + if (!QTAILQ_EMPTY(&siglist->sha256)) { + size += sizeof(efi_siglist); + QTAILQ_FOREACH(h, &siglist->sha256, next) { + size += sizeof(QemuUUID) + 32; + } + } + + return size; +} + +/* + * Generate UEFI signature list. + */ +void uefi_vars_siglist_blob_generate(uefi_vars_siglist *siglist, + void *data, uint64_t size) +{ + uefi_vars_cert *c; + uefi_vars_hash *h; + efi_siglist *efilist; + uint64_t pos = 0, start; + uint32_t i; + + QTAILQ_FOREACH(c, &siglist->x509, next) { + efilist = data + pos; + efilist->guid_type = EfiCertX509Guid; + efilist->sig_size = sizeof(QemuUUID) + c->size; + efilist->header_size = 0; + + start = pos + sizeof(efi_siglist); + memcpy(data + start, + &c->owner, sizeof(QemuUUID)); + memcpy(data + start + sizeof(QemuUUID), + c->data, c->size); + + efilist->siglist_size = sizeof(efi_siglist) + efilist->sig_size; + pos += efilist->siglist_size; + } + + if (!QTAILQ_EMPTY(&siglist->sha256)) { + efilist = data + pos; + efilist->guid_type = EfiCertSha256Guid; + efilist->sig_size = sizeof(QemuUUID) + 32; + efilist->header_size = 0; + + i = 0; + start = pos + sizeof(efi_siglist); + QTAILQ_FOREACH(h, &siglist->sha256, next) { + memcpy(data + start + efilist->sig_size * i, + &h->owner, sizeof(QemuUUID)); + memcpy(data + start + efilist->sig_size * i + sizeof(QemuUUID), + h->data, 32); + i++; + } + + efilist->siglist_size = sizeof(efi_siglist) + efilist->sig_size * i; + pos += efilist->siglist_size; + } + + assert(pos == size); +} diff --git a/hw/uefi/var-service-sysbus.c b/hw/uefi/var-service-sysbus.c new file mode 100644 index 00000000000..a5aa218e260 --- /dev/null +++ b/hw/uefi/var-service-sysbus.c @@ -0,0 +1,124 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - sysbus variant. + */ +#include "qemu/osdep.h" +#include "migration/vmstate.h" + +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" + +#include "hw/uefi/hardware-info.h" +#include "hw/uefi/var-service.h" +#include "hw/uefi/var-service-api.h" + +OBJECT_DECLARE_SIMPLE_TYPE(uefi_vars_sysbus_state, UEFI_VARS_SYSBUS) + +struct uefi_vars_sysbus_state { + SysBusDevice parent_obj; + struct uefi_vars_state state; +}; + +static const VMStateDescription vmstate_uefi_vars_sysbus = { + .name = TYPE_UEFI_VARS_SYSBUS, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(state, uefi_vars_sysbus_state, 0, + vmstate_uefi_vars, uefi_vars_state), + VMSTATE_END_OF_LIST() + } +}; + +static const Property uefi_vars_sysbus_properties[] = { + DEFINE_PROP_SIZE("size", uefi_vars_sysbus_state, state.max_storage, + 256 * 1024), + DEFINE_PROP_STRING("jsonfile", uefi_vars_sysbus_state, state.jsonfile), + DEFINE_PROP_BOOL("force-secure-boot", uefi_vars_sysbus_state, + state.force_secure_boot, false), + DEFINE_PROP_BOOL("disable-custom-mode", uefi_vars_sysbus_state, + state.disable_custom_mode, false), + DEFINE_PROP_BOOL("use-pio", uefi_vars_sysbus_state, + state.use_pio, false), +}; + +static void uefi_vars_sysbus_init(Object *obj) +{ + uefi_vars_sysbus_state *uv = UEFI_VARS_SYSBUS(obj); + + uefi_vars_init(obj, &uv->state); +} + +static void uefi_vars_sysbus_reset(DeviceState *dev) +{ + uefi_vars_sysbus_state *uv = UEFI_VARS_SYSBUS(dev); + + uefi_vars_hard_reset(&uv->state); +} + +static void uefi_vars_sysbus_realize(DeviceState *dev, Error **errp) +{ + uefi_vars_sysbus_state *uv = UEFI_VARS_SYSBUS(dev); + SysBusDevice *sysbus = SYS_BUS_DEVICE(dev); + + sysbus_init_mmio(sysbus, &uv->state.mr); + uefi_vars_realize(&uv->state, errp); +} + +static void uefi_vars_sysbus_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = uefi_vars_sysbus_realize; + dc->vmsd = &vmstate_uefi_vars_sysbus; + dc->user_creatable = true; + device_class_set_legacy_reset(dc, uefi_vars_sysbus_reset); + device_class_set_props(dc, uefi_vars_sysbus_properties); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +/* generic: hardware discovery via FDT */ +static const TypeInfo uefi_vars_sysbus_info = { + .name = TYPE_UEFI_VARS_SYSBUS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(uefi_vars_sysbus_state), + .instance_init = uefi_vars_sysbus_init, + .class_init = uefi_vars_sysbus_class_init, +}; +module_obj(TYPE_UEFI_VARS_SYSBUS); + +static void uefi_vars_x64_realize(DeviceState *dev, Error **errp) +{ + HARDWARE_INFO_SIMPLE_DEVICE hwinfo = { + .mmio_address = cpu_to_le64(0xfef10000), + }; + SysBusDevice *sysbus = SYS_BUS_DEVICE(dev); + + uefi_vars_sysbus_realize(dev, errp); + + hardware_info_register(HardwareInfoQemuUefiVars, + &hwinfo, sizeof(hwinfo)); + sysbus_mmio_map(sysbus, 0, hwinfo.mmio_address); +} + +static void uefi_vars_x64_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = uefi_vars_x64_realize; +} + +/* x64: hardware discovery via etc/hardware-info fw_cfg */ +static const TypeInfo uefi_vars_x64_info = { + .name = TYPE_UEFI_VARS_X64, + .parent = TYPE_UEFI_VARS_SYSBUS, + .class_init = uefi_vars_x64_class_init, +}; +module_obj(TYPE_UEFI_VARS_X64); + +static void uefi_vars_sysbus_register_types(void) +{ + type_register_static(&uefi_vars_sysbus_info); + type_register_static(&uefi_vars_x64_info); +} + +type_init(uefi_vars_sysbus_register_types) diff --git a/hw/uefi/var-service-utils.c b/hw/uefi/var-service-utils.c new file mode 100644 index 00000000000..c9ef46570f4 --- /dev/null +++ b/hw/uefi/var-service-utils.c @@ -0,0 +1,241 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - helper functions for ucs2 strings and tracing + */ +#include "qemu/osdep.h" +#include "system/dma.h" + +#include "hw/uefi/var-service.h" + +#include "trace/trace-hw_uefi.h" + +/* ------------------------------------------------------------------ */ + +/* + * string helper functions. + * + * Most of the time uefi ucs2 strings are NULL-terminated, except + * sometimes when they are not (for example in variable policies). + */ + +gboolean uefi_str_is_valid(const uint16_t *str, size_t len, + gboolean must_be_null_terminated) +{ + size_t pos = 0; + + for (;;) { + if (pos == len) { + if (must_be_null_terminated) { + return false; + } else { + return true; + } + } + switch (str[pos]) { + case 0: + /* end of string */ + return true; + case 0xd800 ... 0xdfff: + /* reject surrogates */ + return false; + default: + /* char is good, check next */ + break; + } + pos++; + } +} + +size_t uefi_strlen(const uint16_t *str, size_t len) +{ + size_t pos = 0; + + for (;;) { + if (pos == len) { + return pos; + } + if (str[pos] == 0) { + return pos; + } + pos++; + } +} + +gboolean uefi_str_equal_ex(const uint16_t *a, size_t alen, + const uint16_t *b, size_t blen, + gboolean wildcards_in_a) +{ + size_t pos = 0; + + alen = alen / 2; + blen = blen / 2; + for (;;) { + if (pos == alen && pos == blen) { + return true; + } + if (pos == alen && b[pos] == 0) { + return true; + } + if (pos == blen && a[pos] == 0) { + return true; + } + if (pos == alen || pos == blen) { + return false; + } + if (a[pos] == 0 && b[pos] == 0) { + return true; + } + + if (wildcards_in_a && a[pos] == '#') { + if (!isxdigit(b[pos])) { + return false; + } + } else { + if (a[pos] != b[pos]) { + return false; + } + } + pos++; + } +} + +gboolean uefi_str_equal(const uint16_t *a, size_t alen, + const uint16_t *b, size_t blen) +{ + return uefi_str_equal_ex(a, alen, b, blen, false); +} + +char *uefi_ucs2_to_ascii(const uint16_t *ucs2, uint64_t ucs2_size) +{ + char *str = g_malloc0(ucs2_size / 2 + 1); + int i; + + for (i = 0; i * 2 < ucs2_size; i++) { + if (ucs2[i] == 0) { + break; + } + if (ucs2[i] < 128) { + str[i] = ucs2[i]; + } else { + str[i] = '?'; + } + } + str[i] = 0; + return str; +} + +/* ------------------------------------------------------------------ */ +/* time helper functions */ + +int uefi_time_compare(efi_time *a, efi_time *b) +{ + if (a->year < b->year) { + return -1; + } + if (a->year > b->year) { + return 1; + } + + if (a->month < b->month) { + return -1; + } + if (a->month > b->month) { + return 1; + } + + if (a->day < b->day) { + return -1; + } + if (a->day > b->day) { + return 1; + } + + if (a->hour < b->hour) { + return -1; + } + if (a->hour > b->hour) { + return 1; + } + + if (a->minute < b->minute) { + return -1; + } + if (a->minute > b->minute) { + return 1; + } + + if (a->second < b->second) { + return -1; + } + if (a->second > b->second) { + return 1; + } + + if (a->nanosecond < b->nanosecond) { + return -1; + } + if (a->nanosecond > b->nanosecond) { + return 1; + } + + return 0; +} + +/* ------------------------------------------------------------------ */ +/* tracing helper functions */ + +void uefi_trace_variable(const char *action, QemuUUID guid, + const uint16_t *name, uint64_t name_size) +{ + QemuUUID be = qemu_uuid_bswap(guid); + char *str_uuid = qemu_uuid_unparse_strdup(&be); + char *str_name = uefi_ucs2_to_ascii(name, name_size); + + trace_uefi_variable(action, str_name, name_size, str_uuid); + + g_free(str_name); + g_free(str_uuid); +} + +void uefi_trace_status(const char *action, efi_status status) +{ + switch (status) { + case EFI_SUCCESS: + trace_uefi_status(action, "success"); + break; + case EFI_INVALID_PARAMETER: + trace_uefi_status(action, "invalid parameter"); + break; + case EFI_UNSUPPORTED: + trace_uefi_status(action, "unsupported"); + break; + case EFI_BAD_BUFFER_SIZE: + trace_uefi_status(action, "bad buffer size"); + break; + case EFI_BUFFER_TOO_SMALL: + trace_uefi_status(action, "buffer too small"); + break; + case EFI_WRITE_PROTECTED: + trace_uefi_status(action, "write protected"); + break; + case EFI_OUT_OF_RESOURCES: + trace_uefi_status(action, "out of resources"); + break; + case EFI_NOT_FOUND: + trace_uefi_status(action, "not found"); + break; + case EFI_ACCESS_DENIED: + trace_uefi_status(action, "access denied"); + break; + case EFI_ALREADY_STARTED: + trace_uefi_status(action, "already started"); + break; + case EFI_SECURITY_VIOLATION: + trace_uefi_status(action, "security violation"); + break; + default: + trace_uefi_status(action, "unknown error"); + break; + } +} diff --git a/hw/uefi/var-service-vars.c b/hw/uefi/var-service-vars.c new file mode 100644 index 00000000000..7f98d77a38d --- /dev/null +++ b/hw/uefi/var-service-vars.c @@ -0,0 +1,725 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - EfiSmmVariableProtocol implementation + */ +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "system/dma.h" +#include "migration/vmstate.h" + +#include "hw/uefi/var-service.h" +#include "hw/uefi/var-service-api.h" +#include "hw/uefi/var-service-edk2.h" + +#include "trace/trace-hw_uefi.h" + +#define EFI_VARIABLE_ATTRIBUTE_SUPPORTED \ + (EFI_VARIABLE_NON_VOLATILE | \ + EFI_VARIABLE_BOOTSERVICE_ACCESS | \ + EFI_VARIABLE_RUNTIME_ACCESS | \ + EFI_VARIABLE_HARDWARE_ERROR_RECORD | \ + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | \ + EFI_VARIABLE_APPEND_WRITE) + + +const VMStateDescription vmstate_uefi_time = { + .name = "uefi-time", + .fields = (VMStateField[]) { + VMSTATE_UINT16(year, efi_time), + VMSTATE_UINT8(month, efi_time), + VMSTATE_UINT8(day, efi_time), + VMSTATE_UINT8(hour, efi_time), + VMSTATE_UINT8(minute, efi_time), + VMSTATE_UINT8(second, efi_time), + VMSTATE_UINT32(nanosecond, efi_time), + VMSTATE_END_OF_LIST() + }, +}; + +const VMStateDescription vmstate_uefi_variable = { + .name = "uefi-variable", + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY_V(guid.data, uefi_variable, sizeof(QemuUUID), 0), + VMSTATE_UINT32(name_size, uefi_variable), + VMSTATE_UINT32(data_size, uefi_variable), + VMSTATE_UINT32(attributes, uefi_variable), + VMSTATE_VBUFFER_ALLOC_UINT32(name, uefi_variable, 0, NULL, name_size), + VMSTATE_VBUFFER_ALLOC_UINT32(data, uefi_variable, 0, NULL, data_size), + VMSTATE_STRUCT(time, uefi_variable, 0, vmstate_uefi_time, efi_time), + VMSTATE_END_OF_LIST() + }, +}; + +uefi_variable *uefi_vars_find_variable(uefi_vars_state *uv, QemuUUID guid, + const uint16_t *name, uint64_t name_size) +{ + uefi_variable *var; + + QTAILQ_FOREACH(var, &uv->variables, next) { + if (!uefi_str_equal(var->name, var->name_size, + name, name_size)) { + continue; + } + if (!qemu_uuid_is_equal(&var->guid, &guid)) { + continue; + } + if (!var->data_size) { + /* in process of being created/updated */ + continue; + } + return var; + } + return NULL; +} + +static uefi_variable *add_variable(uefi_vars_state *uv, QemuUUID guid, + const uint16_t *name, uint64_t name_size, + uint32_t attributes) +{ + uefi_variable *var; + + var = g_new0(uefi_variable, 1); + var->guid = guid; + var->name = g_malloc(name_size); + memcpy(var->name, name, name_size); + var->name_size = name_size; + var->attributes = attributes; + + var->attributes &= ~EFI_VARIABLE_APPEND_WRITE; + + QTAILQ_INSERT_TAIL(&uv->variables, var, next); + return var; +} + +static void del_variable(uefi_vars_state *uv, uefi_variable *var) +{ + if (!var) { + return; + } + + QTAILQ_REMOVE(&uv->variables, var, next); + g_free(var->data); + g_free(var->name); + g_free(var->digest); + g_free(var); +} + +static size_t variable_size(uefi_variable *var) +{ + size_t size; + + size = sizeof(*var); + size += var->name_size; + size += var->data_size; + size += var->digest_size; + return size; +} + +void uefi_vars_set_variable(uefi_vars_state *uv, QemuUUID guid, + const uint16_t *name, uint64_t name_size, + uint32_t attributes, + void *data, uint64_t data_size) +{ + uefi_variable *old_var, *new_var; + + uefi_trace_variable(__func__, guid, name, name_size); + + old_var = uefi_vars_find_variable(uv, guid, name, name_size); + if (old_var) { + uv->used_storage -= variable_size(old_var); + del_variable(uv, old_var); + } + + new_var = add_variable(uv, guid, name, name_size, attributes); + new_var->data = g_malloc(data_size); + new_var->data_size = data_size; + memcpy(new_var->data, data, data_size); + uv->used_storage += variable_size(new_var); +} + +void uefi_vars_clear_volatile(uefi_vars_state *uv) +{ + uefi_variable *var, *n; + + QTAILQ_FOREACH_SAFE(var, &uv->variables, next, n) { + if (var->attributes & EFI_VARIABLE_NON_VOLATILE) { + continue; + } + uv->used_storage -= variable_size(var); + del_variable(uv, var); + } +} + +void uefi_vars_clear_all(uefi_vars_state *uv) +{ + uefi_variable *var, *n; + + QTAILQ_FOREACH_SAFE(var, &uv->variables, next, n) { + del_variable(uv, var); + } + uv->used_storage = 0; +} + +void uefi_vars_update_storage(uefi_vars_state *uv) +{ + uefi_variable *var; + + uv->used_storage = 0; + QTAILQ_FOREACH(var, &uv->variables, next) { + uv->used_storage += variable_size(var); + } +} + +static gboolean check_access(uefi_vars_state *uv, uefi_variable *var) +{ + if (!uv->exit_boot_service) { + if (!(var->attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS)) { + return false; + } + } else { + if (!(var->attributes & EFI_VARIABLE_RUNTIME_ACCESS)) { + return false; + } + } + return true; +} + +static efi_status check_update(uefi_vars_state *uv, uefi_variable *old_var, + uefi_variable *new_var) +{ + efi_status status; + + if (old_var) { + if (!check_access(uv, old_var)) { + return EFI_ACCESS_DENIED; + } + } + + if (new_var) { + if (new_var->attributes & ~EFI_VARIABLE_ATTRIBUTE_SUPPORTED) { + return EFI_UNSUPPORTED; + } + if (!check_access(uv, new_var)) { + return EFI_ACCESS_DENIED; + } + } + + if (old_var && new_var) { + if (old_var->attributes != new_var->attributes) { + return EFI_INVALID_PARAMETER; + } + } + + if (new_var) { + /* create + update */ + status = uefi_vars_policy_check(uv, new_var, old_var == NULL); + } else { + /* delete */ + g_assert(old_var); + status = uefi_vars_policy_check(uv, old_var, false); + } + if (status != EFI_SUCCESS) { + return status; + } + + status = uefi_vars_check_secure_boot(uv, new_var ?: old_var); + if (status != EFI_SUCCESS) { + return status; + } + + return EFI_SUCCESS; +} + +static void append_write(uefi_variable *old_var, + uefi_variable *new_var) +{ + uefi_vars_siglist siglist; + uint64_t size; + void *data; + + uefi_vars_siglist_init(&siglist); + uefi_vars_siglist_parse(&siglist, old_var->data, old_var->data_size); + uefi_vars_siglist_parse(&siglist, new_var->data, new_var->data_size); + + size = uefi_vars_siglist_blob_size(&siglist); + data = g_malloc(size); + uefi_vars_siglist_blob_generate(&siglist, data, size); + + g_free(new_var->data); + new_var->data = data; + new_var->data_size = size; + + uefi_vars_siglist_free(&siglist); +} + +static size_t uefi_vars_mm_error(mm_header *mhdr, mm_variable *mvar, + uint64_t status) +{ + mvar->status = status; + return sizeof(*mvar); +} + +static size_t uefi_vars_mm_get_variable(uefi_vars_state *uv, mm_header *mhdr, + mm_variable *mvar, void *func) +{ + mm_variable_access *va = func; + uint16_t *name; + void *data; + uefi_variable *var; + uint64_t length; + + length = sizeof(*mvar) + sizeof(*va); + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + if (va->name_size > uv->max_storage || + va->data_size > uv->max_storage) { + return uefi_vars_mm_error(mhdr, mvar, EFI_OUT_OF_RESOURCES); + } + + name = func + sizeof(*va); + if (uadd64_overflow(length, va->name_size, &length)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + if (!uefi_str_is_valid(name, va->name_size, true)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_INVALID_PARAMETER); + } + + uefi_trace_variable(__func__, va->guid, name, va->name_size); + + var = uefi_vars_find_variable(uv, va->guid, name, va->name_size); + if (!var) { + return uefi_vars_mm_error(mhdr, mvar, EFI_NOT_FOUND); + } + + /* check permissions etc. */ + if (!check_access(uv, var)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_ACCESS_DENIED); + } + + data = func + sizeof(*va) + va->name_size; + if (uadd64_overflow(length, va->data_size, &length)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + if (uv->buf_size < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + va->attributes = var->attributes; + if (va->data_size < var->data_size) { + va->data_size = var->data_size; + length -= va->data_size; + mvar->status = EFI_BUFFER_TOO_SMALL; + } else { + va->data_size = var->data_size; + memcpy(data, var->data, var->data_size); + mvar->status = EFI_SUCCESS; + } + return length; +} + +static size_t +uefi_vars_mm_get_next_variable(uefi_vars_state *uv, mm_header *mhdr, + mm_variable *mvar, void *func) +{ + mm_next_variable *nv = func; + uefi_variable *var; + uint16_t *name; + uint64_t length; + + length = sizeof(*mvar) + sizeof(*nv); + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + if (nv->name_size > uv->max_storage) { + return uefi_vars_mm_error(mhdr, mvar, EFI_OUT_OF_RESOURCES); + } + + name = func + sizeof(*nv); + if (uadd64_overflow(length, nv->name_size, &length)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + if (!uefi_str_is_valid(name, nv->name_size, true)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_INVALID_PARAMETER); + } + + if (uefi_strlen(name, nv->name_size) == 0) { + /* empty string -> first */ + var = QTAILQ_FIRST(&uv->variables); + if (!var) { + return uefi_vars_mm_error(mhdr, mvar, EFI_NOT_FOUND); + } + } else { + var = uefi_vars_find_variable(uv, nv->guid, name, nv->name_size); + if (!var) { + return uefi_vars_mm_error(mhdr, mvar, EFI_INVALID_PARAMETER); + } + do { + var = QTAILQ_NEXT(var, next); + } while (var && !check_access(uv, var)); + if (!var) { + return uefi_vars_mm_error(mhdr, mvar, EFI_NOT_FOUND); + } + } + + length = sizeof(*mvar) + sizeof(*nv) + var->name_size; + if (uv->buf_size < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + nv->guid = var->guid; + nv->name_size = var->name_size; + memcpy(name, var->name, var->name_size); + mvar->status = EFI_SUCCESS; + return length; +} + +static bool uefi_vars_mm_digest_compare(uefi_variable *old_var, + uefi_variable *new_var) +{ + if (!old_var->digest || + !new_var->digest || + !old_var->digest_size || + !new_var->digest_size) { + /* should not happen */ + trace_uefi_vars_security_violation("inconsistent authvar digest state"); + return false; + } + if (old_var->digest_size != new_var->digest_size) { + trace_uefi_vars_security_violation("authvar digest size mismatch"); + return false; + } + if (memcmp(old_var->digest, new_var->digest, + old_var->digest_size) != 0) { + trace_uefi_vars_security_violation("authvar digest data mismatch"); + return false; + } + return true; +} + +static size_t uefi_vars_mm_set_variable(uefi_vars_state *uv, mm_header *mhdr, + mm_variable *mvar, void *func) +{ + mm_variable_access *va = func; + uint32_t attributes = 0; + uint16_t *name; + void *data; + uefi_variable *old_var, *new_var; + uint64_t length; + size_t new_storage; + efi_status status; + + length = sizeof(*mvar) + sizeof(*va); + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + if (va->name_size > uv->max_storage || + va->data_size > uv->max_storage) { + return uefi_vars_mm_error(mhdr, mvar, EFI_OUT_OF_RESOURCES); + } + + name = func + sizeof(*va); + if (uadd64_overflow(length, va->name_size, &length)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + data = func + sizeof(*va) + va->name_size; + if (uadd64_overflow(length, va->data_size, &length)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + g_assert(va->name_size < G_MAXUINT32); + g_assert(va->data_size < G_MAXUINT32); + + if (!uefi_str_is_valid(name, va->name_size, true)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_INVALID_PARAMETER); + } + + uefi_trace_variable(__func__, va->guid, name, va->name_size); + + old_var = uefi_vars_find_variable(uv, va->guid, name, va->name_size); + if (va->data_size) { + new_var = add_variable(uv, va->guid, name, va->name_size, + va->attributes); + if (va->attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) { + /* not implemented (deprecated in uefi spec) */ + warn_report("%s: AUTHENTICATED_WRITE_ACCESS", __func__); + mvar->status = EFI_UNSUPPORTED; + goto rollback; + } else if (va->attributes & + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { + status = uefi_vars_check_auth_2(uv, new_var, va, data); + if (status != EFI_SUCCESS) { + mvar->status = status; + goto rollback; + } + if (old_var && new_var) { + if (uefi_time_compare(&old_var->time, &new_var->time) > 0) { + trace_uefi_vars_security_violation("time check failed"); + mvar->status = EFI_SECURITY_VIOLATION; + goto rollback; + } + if (old_var->digest_size || new_var->digest_size) { + if (!uefi_vars_mm_digest_compare(old_var, new_var)) { + mvar->status = EFI_SECURITY_VIOLATION; + goto rollback; + } + } + } + } else { + new_var->data = g_malloc(va->data_size); + memcpy(new_var->data, data, va->data_size); + new_var->data_size = va->data_size; + } + if (!new_var->data) { + /* we land here when deleting authenticated variables */ + del_variable(uv, new_var); + new_var = NULL; + } + } else { + new_var = NULL; + } + + if (!old_var && !new_var) { + /* delete non-existing variable -> nothing to do */ + mvar->status = EFI_SUCCESS; + return sizeof(*mvar); + } + + /* check permissions etc. */ + status = check_update(uv, old_var, new_var); + if (status != EFI_SUCCESS) { + mvar->status = status; + goto rollback; + } + + if (va->attributes & EFI_VARIABLE_APPEND_WRITE && old_var && new_var) { + /* merge signature databases */ + if (!uefi_vars_is_sb_any(new_var)) { + mvar->status = EFI_UNSUPPORTED; + goto rollback; + } + append_write(old_var, new_var); + } + + /* check storage space */ + new_storage = uv->used_storage; + if (old_var) { + new_storage -= variable_size(old_var); + } + if (new_var) { + new_storage += variable_size(new_var); + } + if (new_storage > uv->max_storage) { + mvar->status = EFI_OUT_OF_RESOURCES; + goto rollback; + } + + attributes = new_var + ? new_var->attributes + : old_var->attributes; + + /* all good, commit */ + del_variable(uv, old_var); + uv->used_storage = new_storage; + + if (attributes & EFI_VARIABLE_NON_VOLATILE) { + uefi_vars_json_save(uv); + } + + if (new_var && uefi_vars_is_sb_pk(new_var)) { + uefi_vars_auth_init(uv); + } + + mvar->status = EFI_SUCCESS; + return sizeof(*mvar); + +rollback: + del_variable(uv, new_var); + return sizeof(*mvar); +} + +static size_t uefi_vars_mm_variable_info(uefi_vars_state *uv, mm_header *mhdr, + mm_variable *mvar, void *func) +{ + mm_variable_info *vi = func; + uint64_t length; + + length = sizeof(*mvar) + sizeof(*vi); + if (uv->buf_size < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + vi->max_storage_size = uv->max_storage; + vi->free_storage_size = uv->max_storage - uv->used_storage; + vi->max_variable_size = uv->max_storage >> 2; + vi->attributes = 0; + + mvar->status = EFI_SUCCESS; + return length; +} + +static size_t +uefi_vars_mm_get_payload_size(uefi_vars_state *uv, mm_header *mhdr, + mm_variable *mvar, void *func) +{ + mm_get_payload_size *ps = func; + uint64_t length; + + length = sizeof(*mvar) + sizeof(*ps); + if (uv->buf_size < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + ps->payload_size = uv->buf_size; + mvar->status = EFI_SUCCESS; + return length; +} + +static size_t +uefi_vars_mm_lock_variable(uefi_vars_state *uv, mm_header *mhdr, + mm_variable *mvar, void *func) +{ + mm_lock_variable *lv = func; + variable_policy_entry *pe; + uint16_t *name, *dest; + uint64_t length; + + length = sizeof(*mvar) + sizeof(*lv); + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + name = func + sizeof(*lv); + if (uadd64_overflow(length, lv->name_size, &length)) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + if (mhdr->length < length) { + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); + } + + uefi_trace_variable(__func__, lv->guid, name, lv->name_size); + + pe = g_malloc0(sizeof(*pe) + lv->name_size); + pe->version = VARIABLE_POLICY_ENTRY_REVISION; + pe->size = sizeof(*pe) + lv->name_size; + pe->offset_to_name = sizeof(*pe); + pe->namespace = lv->guid; + pe->min_size = 0; + pe->max_size = UINT32_MAX; + pe->attributes_must_have = 0; + pe->attributes_cant_have = 0; + pe->lock_policy_type = VARIABLE_POLICY_TYPE_LOCK_NOW; + + dest = (void *)pe + pe->offset_to_name; + memcpy(dest, name, lv->name_size); + + uefi_vars_add_policy(uv, pe); + g_free(pe); + + mvar->status = EFI_SUCCESS; + return length; +} + +uint32_t uefi_vars_mm_vars_proto(uefi_vars_state *uv) +{ + static const char *fnames[] = { + "zero", + "get-variable", + "get-next-variable-name", + "set-variable", + "query-variable-info", + "ready-to-boot", + "exit-boot-service", + "get-statistics", + "lock-variable", + "var-check-prop-set", + "var-check-prop-get", + "get-payload-size", + "init-runtime-cache-contect", + "sync-runtime-cache", + "get-runtime-cache-info", + }; + const char *fname; + uint64_t length; + + mm_header *mhdr = (mm_header *) uv->buffer; + mm_variable *mvar = (mm_variable *) (uv->buffer + sizeof(*mhdr)); + void *func = (uv->buffer + sizeof(*mhdr) + sizeof(*mvar)); + + if (mhdr->length < sizeof(*mvar)) { + return UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE; + } + + fname = mvar->function < ARRAY_SIZE(fnames) + ? fnames[mvar->function] + : "unknown"; + trace_uefi_vars_proto_cmd(fname); + + switch (mvar->function) { + case SMM_VARIABLE_FUNCTION_GET_VARIABLE: + length = uefi_vars_mm_get_variable(uv, mhdr, mvar, func); + break; + + case SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME: + length = uefi_vars_mm_get_next_variable(uv, mhdr, mvar, func); + break; + + case SMM_VARIABLE_FUNCTION_SET_VARIABLE: + length = uefi_vars_mm_set_variable(uv, mhdr, mvar, func); + break; + + case SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO: + length = uefi_vars_mm_variable_info(uv, mhdr, mvar, func); + break; + + case SMM_VARIABLE_FUNCTION_LOCK_VARIABLE: + length = uefi_vars_mm_lock_variable(uv, mhdr, mvar, func); + break; + + case SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE: + length = uefi_vars_mm_get_payload_size(uv, mhdr, mvar, func); + break; + + case SMM_VARIABLE_FUNCTION_READY_TO_BOOT: + trace_uefi_event("ready-to-boot"); + uv->ready_to_boot = true; + length = 0; + break; + + case SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE: + trace_uefi_event("exit-boot-service"); + uv->exit_boot_service = true; + length = 0; + break; + + default: + length = uefi_vars_mm_error(mhdr, mvar, EFI_UNSUPPORTED); + break; + } + + if (mhdr->length < length) { + mvar->status = EFI_BUFFER_TOO_SMALL; + } + + uefi_trace_status(__func__, mvar->status); + return UEFI_VARS_STS_SUCCESS; +} diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index e1c46bddb11..2d8ffd72c5d 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -14,62 +14,122 @@ #include "qemu/memalign.h" #include "hw/scsi/scsi.h" #include "scsi/constants.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qemu/cutils.h" #include "trace.h" #include "ufs.h" -/* - * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c, - * with minor adjustments to make it work for UFS. - */ +#define SCSI_COMMAND_FAIL (-1) -#define SCSI_DMA_BUF_SIZE (128 * KiB) -#define SCSI_MAX_INQUIRY_LEN 256 -#define SCSI_INQUIRY_DATA_SIZE 36 -#define SCSI_MAX_MODE_LEN 256 - -typedef struct UfsSCSIReq { - SCSIRequest req; - /* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. */ - uint64_t sector; - uint32_t sector_count; - uint32_t buflen; - bool started; - bool need_fua_emulation; - struct iovec iov; - QEMUIOVector qiov; - BlockAcctCookie acct; -} UfsSCSIReq; - -static void ufs_scsi_free_request(SCSIRequest *req) +static void ufs_build_upiu_sense_data(UfsRequest *req, uint8_t *sense, + uint32_t sense_len) { - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); + 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; + } - qemu_vfree(r->iov.iov_base); + 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 scsi_check_condition(UfsSCSIReq *r, SCSISense sense) +static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid) { - trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, - sense.ascq); - scsi_req_build_sense(&r->req, sense); - scsi_req_complete(&r->req, CHECK_CONDITION); + 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 int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, - uint32_t outbuf_len) +static QEMUSGList *ufs_get_sg_list(SCSIRequest *scsi_req) { - UfsHc *u = UFS(req->bus->qbus.parent); - UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); - uint8_t page_code = req->cmd.buf[2]; - int start, buflen = 0; + 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; - if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { - return -1; + /* TODO: Support for cases where SELECT REPORT is 1 and 2 */ + if (req->req_upiu.sc.cdb[2] != 0) { + return SCSI_COMMAND_FAIL; } - outbuf[buflen++] = lu->qdev.type & 0x1f; + 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; @@ -78,36 +138,12 @@ static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, switch (page_code) { case 0x00: /* Supported page codes, mandatory */ { - trace_ufs_scsi_emulate_vpd_page_00(req->cmd.xfer); outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ - if (u->params.serial) { - outbuf[buflen++] = 0x80; /* unit serial number */ - } outbuf[buflen++] = 0x87; /* mode page policy */ break; } - case 0x80: /* Device serial number, optional */ - { - int l; - - if (!u->params.serial) { - trace_ufs_scsi_emulate_vpd_page_80_not_supported(); - return -1; - } - - l = strlen(u->params.serial); - if (l > SCSI_INQUIRY_DATA_SIZE) { - l = SCSI_INQUIRY_DATA_SIZE; - } - - trace_ufs_scsi_emulate_vpd_page_80(req->cmd.xfer); - memcpy(outbuf + buflen, u->params.serial, l); - buflen += l; - break; - } case 0x87: /* Mode Page Policy, mandatory */ { - trace_ufs_scsi_emulate_vpd_page_87(req->cmd.xfer); outbuf[buflen++] = 0x3f; /* apply to all mode pages and subpages */ outbuf[buflen++] = 0xff; outbuf[buflen++] = 0; /* shared */ @@ -115,7 +151,7 @@ static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, break; } default: - return -1; + return SCSI_COMMAND_FAIL; } /* done with EVPD */ assert(buflen - start <= 255); @@ -123,1150 +159,129 @@ static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, return buflen; } -static int ufs_scsi_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf, +static int ufs_emulate_wlun_inquiry(UfsRequest *req, uint8_t *outbuf, uint32_t outbuf_len) { - int buflen = 0; - - if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { - return -1; + if (outbuf_len < SCSI_INQUIRY_LEN) { + return 0; } - if (req->cmd.buf[1] & 0x1) { + 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->cmd.buf[2] != 0) { - return -1; + if (req->req_upiu.sc.cdb[2] != 0) { + return SCSI_COMMAND_FAIL; } - /* PAGE CODE == 0 */ - buflen = req->cmd.xfer; - if (buflen > SCSI_MAX_INQUIRY_LEN) { - buflen = SCSI_MAX_INQUIRY_LEN; - } - - if (is_wlun(req->lun)) { - outbuf[0] = TYPE_WLUN; - } else { - outbuf[0] = 0; - } + outbuf[0] = TYPE_WLUN; outbuf[1] = 0; - - strpadcpy((char *)&outbuf[16], 16, "QEMU UFS", ' '); + 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); - outbuf[2] = 0x06; /* SPC-4 */ - outbuf[3] = 0x2; - - if (buflen > SCSI_INQUIRY_DATA_SIZE) { - outbuf[4] = buflen - 5; /* Additional Length = (Len - 1) - 4 */ - } else { - /* - * If the allocation length of CDB is too small, the additional - * length is not adjusted - */ - outbuf[4] = SCSI_INQUIRY_DATA_SIZE - 5; - } - - /* Support TCQ. */ - outbuf[7] = req->bus->info->tcq ? 0x02 : 0; - return buflen; -} - -static int mode_sense_page(UfsLu *lu, int page, uint8_t **p_outbuf, - int page_control) -{ - static const int mode_sense_valid[0x3f] = { - [MODE_PAGE_CACHING] = 1, - [MODE_PAGE_R_W_ERROR] = 1, - [MODE_PAGE_CONTROL] = 1, - }; - - uint8_t *p = *p_outbuf + 2; - int length; - - assert(page < ARRAY_SIZE(mode_sense_valid)); - if ((mode_sense_valid[page]) == 0) { - return -1; - } - - /* - * If Changeable Values are requested, a mask denoting those mode parameters - * that are changeable shall be returned. As we currently don't support - * parameter changes via MODE_SELECT all bits are returned set to zero. - * The buffer was already memset to zero by the caller of this function. - */ - switch (page) { - case MODE_PAGE_CACHING: - length = 0x12; - if (page_control == 1 || /* Changeable Values */ - blk_enable_write_cache(lu->qdev.conf.blk)) { - p[0] = 4; /* WCE */ - } - break; - - case MODE_PAGE_R_W_ERROR: - length = 10; - if (page_control == 1) { /* Changeable Values */ - break; - } - p[0] = 0x80; /* Automatic Write Reallocation Enabled */ - break; - - case MODE_PAGE_CONTROL: - length = 10; - if (page_control == 1) { /* Changeable Values */ - break; - } - p[1] = 0x10; /* Queue Algorithm modifier */ - p[8] = 0xff; /* Busy Timeout Period */ - p[9] = 0xff; - break; - - default: - return -1; - } - - assert(length < 256); - (*p_outbuf)[0] = page; - (*p_outbuf)[1] = length; - *p_outbuf += length + 2; - return length + 2; + return SCSI_INQUIRY_LEN; } -static int ufs_scsi_emulate_mode_sense(UfsSCSIReq *r, uint8_t *outbuf) +static UfsReqResult ufs_emulate_scsi_cmd(UfsLu *lu, UfsRequest *req) { - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - bool dbd; - int page, buflen, ret, page_control; - uint8_t *p; - uint8_t dev_specific_param = 0; - - dbd = (r->req.cmd.buf[1] & 0x8) != 0; - if (!dbd) { - return -1; - } + uint8_t lun = lu->lun; + QEMU_UNINITIALIZED uint8_t outbuf[4096]; + uint8_t sense_buf[UFS_SENSE_SIZE]; + uint8_t scsi_status; + int len = 0; - page = r->req.cmd.buf[2] & 0x3f; - page_control = (r->req.cmd.buf[2] & 0xc0) >> 6; - - trace_ufs_scsi_emulate_mode_sense((r->req.cmd.buf[0] == MODE_SENSE) ? 6 : - 10, - page, r->req.cmd.xfer, page_control); - memset(outbuf, 0, r->req.cmd.xfer); - p = outbuf; - - if (!blk_is_writable(lu->qdev.conf.blk)) { - dev_specific_param |= 0x80; /* Readonly. */ - } - - p[2] = 0; /* Medium type. */ - p[3] = dev_specific_param; - p[6] = p[7] = 0; /* Block descriptor length. */ - p += 8; - - if (page_control == 3) { - /* Saved Values */ - scsi_check_condition(r, SENSE_CODE(SAVING_PARAMS_NOT_SUPPORTED)); - return -1; - } - - if (page == 0x3f) { - for (page = 0; page <= 0x3e; page++) { - mode_sense_page(lu, page, &p, page_control); - } - } else { - ret = mode_sense_page(lu, page, &p, page_control); - if (ret == -1) { - return -1; - } - } - - buflen = p - outbuf; - /* - * The mode data length field specifies the length in bytes of the - * following data that is available to be transferred. The mode data - * length does not include itself. - */ - outbuf[0] = ((buflen - 2) >> 8) & 0xff; - outbuf[1] = (buflen - 2) & 0xff; - return buflen; -} - -/* - * 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 - * caller should not do anything else for this request. Note that - * scsi_handle_rw_error always manages its reference counts, independent - * of the return value. - */ -static bool scsi_handle_rw_error(UfsSCSIReq *r, int ret, bool acct_failed) -{ - bool is_read = (r->req.cmd.mode == SCSI_XFER_FROM_DEV); - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - SCSISense sense = SENSE_CODE(NO_SENSE); - int error = 0; - bool req_has_sense = false; - BlockErrorAction action; - int status; - - if (ret < 0) { - status = scsi_sense_from_errno(-ret, &sense); - error = -ret; - } else { - /* A passthrough command has completed with nonzero status. */ - status = ret; - if (status == CHECK_CONDITION) { - req_has_sense = true; - error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense)); + 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 { - error = EINVAL; - } - } - - /* - * Check whether the error has to be handled by the guest or should - * rather follow the rerror=/werror= settings. Guest-handled errors - * 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))) { - action = BLOCK_ERROR_ACTION_REPORT; - acct_failed = false; - } else { - action = blk_get_error_action(lu->qdev.conf.blk, is_read, error); - blk_error_action(lu->qdev.conf.blk, action, is_read, error); - } - - switch (action) { - case BLOCK_ERROR_ACTION_REPORT: - if (acct_failed) { - block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } - if (!req_has_sense && status == CHECK_CONDITION) { - scsi_req_build_sense(&r->req, sense); - } - scsi_req_complete(&r->req, status); - return true; - - case BLOCK_ERROR_ACTION_IGNORE: - return false; - - case BLOCK_ERROR_ACTION_STOP: - scsi_req_retry(&r->req); - return true; - - default: - g_assert_not_reached(); - } -} - -static bool ufs_scsi_req_check_error(UfsSCSIReq *r, int ret, bool acct_failed) -{ - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); - return true; - } - - if (ret < 0) { - return scsi_handle_rw_error(r, ret, acct_failed); - } - - return false; -} - -static void scsi_aio_complete(void *opaque, int ret) -{ - UfsSCSIReq *r = (UfsSCSIReq *)opaque; - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk)); - if (ufs_scsi_req_check_error(r, ret, true)) { - goto done; - } - - block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct); - scsi_req_complete(&r->req, GOOD); - -done: - aio_context_release(blk_get_aio_context(lu->qdev.conf.blk)); - scsi_req_unref(&r->req); -} - -static int32_t ufs_scsi_emulate_command(SCSIRequest *req, uint8_t *buf) -{ - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); - UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); - uint32_t last_block = 0; - uint8_t *outbuf; - int buflen; - - switch (req->cmd.buf[0]) { - case INQUIRY: - case MODE_SENSE_10: - case START_STOP: - case REQUEST_SENSE: - break; - - default: - if (!blk_is_available(lu->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - return 0; + scsi_status = GOOD; } break; - } - - /* - * FIXME: we shouldn't return anything bigger than 4k, but the code - * requires the buffer to be as big as req->cmd.xfer in several - * places. So, do not allow CDBs with a very large ALLOCATION - * LENGTH. The real fix would be to modify scsi_read_data and - * dma_buf_read, so that they return data beyond the buflen - * as all zeros. - */ - if (req->cmd.xfer > 65536) { - goto illegal_request; - } - r->buflen = MAX(4096, req->cmd.xfer); - - if (!r->iov.iov_base) { - r->iov.iov_base = blk_blockalign(lu->qdev.conf.blk, r->buflen); - } - - outbuf = r->iov.iov_base; - memset(outbuf, 0, r->buflen); - switch (req->cmd.buf[0]) { - case TEST_UNIT_READY: - assert(blk_is_available(lu->qdev.conf.blk)); - break; case INQUIRY: - buflen = ufs_scsi_emulate_inquiry(req, outbuf, r->buflen); - if (buflen < 0) { - goto illegal_request; - } - break; - case MODE_SENSE_10: - buflen = ufs_scsi_emulate_mode_sense(r, outbuf); - if (buflen < 0) { - goto illegal_request; + 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 READ_CAPACITY_10: - /* The normal LEN field for this command is zero. */ - memset(outbuf, 0, 8); - if (lu->qdev.max_lba > 0) { - last_block = lu->qdev.max_lba - 1; - }; - outbuf[0] = (last_block >> 24) & 0xff; - outbuf[1] = (last_block >> 16) & 0xff; - outbuf[2] = (last_block >> 8) & 0xff; - outbuf[3] = last_block & 0xff; - outbuf[4] = (lu->qdev.blocksize >> 24) & 0xff; - outbuf[5] = (lu->qdev.blocksize >> 16) & 0xff; - outbuf[6] = (lu->qdev.blocksize >> 8) & 0xff; - outbuf[7] = lu->qdev.blocksize & 0xff; - break; case REQUEST_SENSE: - /* Just return "NO SENSE". */ - buflen = scsi_convert_sense(NULL, 0, outbuf, r->buflen, - (req->cmd.buf[1] & 1) == 0); - if (buflen < 0) { - goto illegal_request; - } - break; - case SYNCHRONIZE_CACHE: - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(lu->qdev.conf.blk, scsi_aio_complete, r); - return 0; - case VERIFY_10: - trace_ufs_scsi_emulate_command_VERIFY((req->cmd.buf[1] >> 1) & 3); - if (req->cmd.buf[1] & 6) { - goto illegal_request; - } - break; - case SERVICE_ACTION_IN_16: - /* Service Action In subcommands. */ - if ((req->cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) { - trace_ufs_scsi_emulate_command_SAI_16(); - memset(outbuf, 0, req->cmd.xfer); - - if (lu->qdev.max_lba > 0) { - last_block = lu->qdev.max_lba - 1; - }; - outbuf[0] = 0; - outbuf[1] = 0; - outbuf[2] = 0; - outbuf[3] = 0; - outbuf[4] = (last_block >> 24) & 0xff; - outbuf[5] = (last_block >> 16) & 0xff; - outbuf[6] = (last_block >> 8) & 0xff; - outbuf[7] = last_block & 0xff; - outbuf[8] = (lu->qdev.blocksize >> 24) & 0xff; - outbuf[9] = (lu->qdev.blocksize >> 16) & 0xff; - outbuf[10] = (lu->qdev.blocksize >> 8) & 0xff; - outbuf[11] = lu->qdev.blocksize & 0xff; - outbuf[12] = 0; - outbuf[13] = get_physical_block_exp(&lu->qdev.conf); - - if (lu->unit_desc.provisioning_type == 2 || - lu->unit_desc.provisioning_type == 3) { - outbuf[14] = 0x80; - } - /* Protection, exponent and lowest lba field left blank. */ - break; - } - trace_ufs_scsi_emulate_command_SAI_unsupported(); - goto illegal_request; - case MODE_SELECT_10: - trace_ufs_scsi_emulate_command_MODE_SELECT_10(r->req.cmd.xfer); + /* 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: START_STOP is not yet implemented. It always returns success. - * Revisit it when ufs power management is implemented. - */ - trace_ufs_scsi_emulate_command_START_STOP(); - break; - case FORMAT_UNIT: - trace_ufs_scsi_emulate_command_FORMAT_UNIT(); - break; - case SEND_DIAGNOSTIC: - trace_ufs_scsi_emulate_command_SEND_DIAGNOSTIC(); - break; - default: - trace_ufs_scsi_emulate_command_UNKNOWN(buf[0], - scsi_command_name(buf[0])); - scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); - return 0; - } - assert(!r->req.aiocb); - r->iov.iov_len = MIN(r->buflen, req->cmd.xfer); - if (r->iov.iov_len == 0) { - scsi_req_complete(&r->req, GOOD); - } - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - assert(r->iov.iov_len == req->cmd.xfer); - return -r->iov.iov_len; - } else { - return r->iov.iov_len; - } - -illegal_request: - if (r->req.status == -1) { - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - } - return 0; -} - -static void ufs_scsi_emulate_read_data(SCSIRequest *req) -{ - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); - int buflen = r->iov.iov_len; - - if (buflen) { - trace_ufs_scsi_emulate_read_data(buflen); - r->iov.iov_len = 0; - r->started = true; - scsi_req_data(&r->req, buflen); - return; - } - - /* This also clears the sense buffer for REQUEST SENSE. */ - scsi_req_complete(&r->req, GOOD); -} - -static int ufs_scsi_check_mode_select(UfsLu *lu, int page, uint8_t *inbuf, - int inlen) -{ - uint8_t mode_current[SCSI_MAX_MODE_LEN]; - uint8_t mode_changeable[SCSI_MAX_MODE_LEN]; - uint8_t *p; - int len, expected_len, changeable_len, i; - - /* - * The input buffer does not include the page header, so it is - * off by 2 bytes. - */ - expected_len = inlen + 2; - if (expected_len > SCSI_MAX_MODE_LEN) { - return -1; - } - - /* MODE_PAGE_ALLS is only valid for MODE SENSE commands */ - if (page == MODE_PAGE_ALLS) { - return -1; - } - - p = mode_current; - memset(mode_current, 0, inlen + 2); - len = mode_sense_page(lu, page, &p, 0); - if (len < 0 || len != expected_len) { - return -1; - } - - p = mode_changeable; - memset(mode_changeable, 0, inlen + 2); - changeable_len = mode_sense_page(lu, page, &p, 1); - assert(changeable_len == len); - - /* - * Check that unchangeable bits are the same as what MODE SENSE - * would return. - */ - for (i = 2; i < len; i++) { - if (((mode_current[i] ^ inbuf[i - 2]) & ~mode_changeable[i]) != 0) { - return -1; + /* TODO: Revisit it when Power Management is implemented */ + if (lun == UFS_UPIU_UFS_DEVICE_WLUN) { + scsi_status = GOOD; + break; } - } - return 0; -} - -static void ufs_scsi_apply_mode_select(UfsLu *lu, int page, uint8_t *p) -{ - switch (page) { - case MODE_PAGE_CACHING: - blk_set_enable_write_cache(lu->qdev.conf.blk, (p[0] & 4) != 0); - break; - + /* fallthrough */ default: - break; + scsi_build_sense(sense_buf, SENSE_CODE(INVALID_OPCODE)); + scsi_status = CHECK_CONDITION; } -} - -static int mode_select_pages(UfsSCSIReq *r, uint8_t *p, int len, bool change) -{ - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - while (len > 0) { - int page, page_len; - - page = p[0] & 0x3f; - if (p[0] & 0x40) { - goto invalid_param; - } else { - if (len < 2) { - goto invalid_param_len; - } - page_len = p[1]; - p += 2; - len -= 2; - } - - if (page_len > len) { - goto invalid_param_len; - } - if (!change) { - if (ufs_scsi_check_mode_select(lu, page, p, page_len) < 0) { - goto invalid_param; - } - } else { - ufs_scsi_apply_mode_select(lu, page, p); - } - - p += page_len; - len -= page_len; + 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; } - return 0; -invalid_param: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM)); - return -1; - -invalid_param_len: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); - return -1; + ufs_build_scsi_response_upiu(req, sense_buf, sizeof(sense_buf), len, + scsi_status); + return UFS_REQUEST_SUCCESS; } -static void ufs_scsi_emulate_mode_select(UfsSCSIReq *r, uint8_t *inbuf) +static UfsReqResult ufs_process_scsi_cmd(UfsLu *lu, UfsRequest *req) { - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - uint8_t *p = inbuf; - int len = r->req.cmd.xfer; - int hdr_len = 8; - int bd_len; - int pass; - - /* We only support PF=1, SP=0. */ - if ((r->req.cmd.buf[1] & 0x11) != 0x10) { - goto invalid_field; - } - - if (len < hdr_len) { - goto invalid_param_len; - } - - bd_len = lduw_be_p(&p[6]); - if (bd_len != 0) { - goto invalid_param; - } - - len -= hdr_len; - p += hdr_len; - - /* Ensure no change is made if there is an error! */ - for (pass = 0; pass < 2; pass++) { - if (mode_select_pages(r, p, len, pass == 1) < 0) { - assert(pass == 0); - return; - } - } - - if (!blk_enable_write_cache(lu->qdev.conf.blk)) { - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(lu->qdev.conf.blk, scsi_aio_complete, r); - return; - } - - scsi_req_complete(&r->req, GOOD); - return; - -invalid_param: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM)); - return; - -invalid_param_len: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); - return; + uint8_t task_tag = req->req_upiu.header.task_tag; -invalid_field: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); -} - -/* block_num and nb_blocks expected to be in qdev blocksize */ -static inline bool check_lba_range(UfsLu *lu, uint64_t block_num, - uint32_t nb_blocks) -{ /* - * The first line tests that no overflow happens when computing the last - * block. The second line tests that the last accessed block is in - * range. - * - * Careful, the computations should not underflow for nb_blocks == 0, - * and a 0-block read to the first LBA beyond the end of device is - * valid. + * 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. */ - return (block_num <= block_num + nb_blocks && - block_num + nb_blocks <= lu->qdev.max_lba + 1); -} - -static void ufs_scsi_emulate_write_data(SCSIRequest *req) -{ - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); - - if (r->iov.iov_len) { - int buflen = r->iov.iov_len; - trace_ufs_scsi_emulate_write_data(buflen); - r->iov.iov_len = 0; - scsi_req_data(&r->req, buflen); - return; - } - - switch (req->cmd.buf[0]) { - case MODE_SELECT_10: - /* This also clears the sense buffer for REQUEST SENSE. */ - ufs_scsi_emulate_mode_select(r, r->iov.iov_base); - break; - default: - abort(); - } -} - -/* Return a pointer to the data buffer. */ -static uint8_t *ufs_scsi_get_buf(SCSIRequest *req) -{ - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); - - return (uint8_t *)r->iov.iov_base; -} - -static int32_t ufs_scsi_dma_command(SCSIRequest *req, uint8_t *buf) -{ - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); - UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev); - uint32_t len; - uint8_t command; - - command = buf[0]; - - if (!blk_is_available(lu->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - return 0; - } - - len = scsi_data_cdb_xfer(r->req.cmd.buf); - switch (command) { - case READ_6: - case READ_10: - trace_ufs_scsi_dma_command_READ(r->req.cmd.lba, len); - if (r->req.cmd.buf[1] & 0xe0) { - goto illegal_request; - } - if (!check_lba_range(lu, r->req.cmd.lba, len)) { - goto illegal_lba; - } - r->sector = r->req.cmd.lba * (lu->qdev.blocksize / BDRV_SECTOR_SIZE); - r->sector_count = len * (lu->qdev.blocksize / BDRV_SECTOR_SIZE); - break; - case WRITE_6: - case WRITE_10: - trace_ufs_scsi_dma_command_WRITE(r->req.cmd.lba, len); - if (!blk_is_writable(lu->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); - return 0; - } - if (r->req.cmd.buf[1] & 0xe0) { - goto illegal_request; - } - if (!check_lba_range(lu, r->req.cmd.lba, len)) { - goto illegal_lba; - } - r->sector = r->req.cmd.lba * (lu->qdev.blocksize / BDRV_SECTOR_SIZE); - r->sector_count = len * (lu->qdev.blocksize / BDRV_SECTOR_SIZE); - break; - default: - abort(); - illegal_request: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - return 0; - illegal_lba: - scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); - return 0; - } - r->need_fua_emulation = ((r->req.cmd.buf[1] & 8) != 0); - if (r->sector_count == 0) { - scsi_req_complete(&r->req, GOOD); - } - assert(r->iov.iov_len == 0); - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - return -r->sector_count * BDRV_SECTOR_SIZE; - } else { - return r->sector_count * BDRV_SECTOR_SIZE; - } -} - -static void scsi_write_do_fua(UfsSCSIReq *r) -{ - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb == NULL); - assert(!r->req.io_canceled); - - if (r->need_fua_emulation) { - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(lu->qdev.conf.blk, scsi_aio_complete, r); - return; - } - - scsi_req_complete(&r->req, GOOD); - scsi_req_unref(&r->req); -} - -static void scsi_dma_complete_noio(UfsSCSIReq *r, int ret) -{ - assert(r->req.aiocb == NULL); - if (ufs_scsi_req_check_error(r, ret, false)) { - goto done; - } - - r->sector += r->sector_count; - r->sector_count = 0; - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - scsi_write_do_fua(r); - return; - } else { - scsi_req_complete(&r->req, GOOD); - } - -done: - scsi_req_unref(&r->req); -} - -static void scsi_dma_complete(void *opaque, int ret) -{ - UfsSCSIReq *r = (UfsSCSIReq *)opaque; - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - - aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk)); - if (ret < 0) { - block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } else { - block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } - scsi_dma_complete_noio(r, ret); - aio_context_release(blk_get_aio_context(lu->qdev.conf.blk)); -} - -static BlockAIOCB *scsi_dma_readv(int64_t offset, QEMUIOVector *iov, - BlockCompletionFunc *cb, void *cb_opaque, - void *opaque) -{ - UfsSCSIReq *r = opaque; - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - return blk_aio_preadv(lu->qdev.conf.blk, offset, iov, 0, cb, cb_opaque); -} - -static void scsi_init_iovec(UfsSCSIReq *r, size_t size) -{ - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - if (!r->iov.iov_base) { - r->buflen = size; - r->iov.iov_base = blk_blockalign(lu->qdev.conf.blk, r->buflen); - } - r->iov.iov_len = MIN(r->sector_count * BDRV_SECTOR_SIZE, r->buflen); - qemu_iovec_init_external(&r->qiov, &r->iov, 1); -} - -static void scsi_read_complete_noio(UfsSCSIReq *r, int ret) -{ - uint32_t n; - - assert(r->req.aiocb == NULL); - if (ufs_scsi_req_check_error(r, ret, false)) { - goto done; - } - - n = r->qiov.size / BDRV_SECTOR_SIZE; - r->sector += n; - r->sector_count -= n; - scsi_req_data(&r->req, r->qiov.size); - -done: - scsi_req_unref(&r->req); -} - -static void scsi_read_complete(void *opaque, int ret) -{ - UfsSCSIReq *r = (UfsSCSIReq *)opaque; - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - trace_ufs_scsi_read_data_count(r->sector_count); - aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk)); - if (ret < 0) { - block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } else { - block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct); - trace_ufs_scsi_read_complete(r->req.tag, r->qiov.size); - } - scsi_read_complete_noio(r, ret); - aio_context_release(blk_get_aio_context(lu->qdev.conf.blk)); -} - -/* Actually issue a read to the block device. */ -static void scsi_do_read(UfsSCSIReq *r, int ret) -{ - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb == NULL); - if (ufs_scsi_req_check_error(r, ret, false)) { - goto done; - } - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - - if (r->req.sg) { - dma_acct_start(lu->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_READ); - r->req.residual -= r->req.sg->size; - r->req.aiocb = dma_blk_io( - blk_get_aio_context(lu->qdev.conf.blk), r->req.sg, - r->sector << BDRV_SECTOR_BITS, BDRV_SECTOR_SIZE, scsi_dma_readv, r, - scsi_dma_complete, r, DMA_DIRECTION_FROM_DEVICE); - } else { - scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, - r->qiov.size, BLOCK_ACCT_READ); - r->req.aiocb = scsi_dma_readv(r->sector << BDRV_SECTOR_BITS, &r->qiov, - scsi_read_complete, r, r); - } - -done: - scsi_req_unref(&r->req); -} - -static void scsi_do_read_cb(void *opaque, int ret) -{ - UfsSCSIReq *r = (UfsSCSIReq *)opaque; - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - - aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk)); - if (ret < 0) { - block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } else { - block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } - scsi_do_read(opaque, ret); - aio_context_release(blk_get_aio_context(lu->qdev.conf.blk)); -} - -/* Read more data from scsi device into buffer. */ -static void scsi_read_data(SCSIRequest *req) -{ - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - bool first; - - trace_ufs_scsi_read_data_count(r->sector_count); - if (r->sector_count == 0) { - /* This also clears the sense buffer for REQUEST SENSE. */ - scsi_req_complete(&r->req, GOOD); - return; - } - - /* No data transfer may already be in progress */ - assert(r->req.aiocb == NULL); - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - trace_ufs_scsi_read_data_invalid(); - scsi_read_complete_noio(r, -EINVAL); - return; - } - - if (!blk_is_available(req->dev->conf.blk)) { - scsi_read_complete_noio(r, -ENOMEDIUM); - return; - } - - first = !r->started; - r->started = true; - if (first && r->need_fua_emulation) { - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(lu->qdev.conf.blk, scsi_do_read_cb, r); - } else { - scsi_do_read(r, 0); - } -} - -static void scsi_write_complete_noio(UfsSCSIReq *r, int ret) -{ - uint32_t n; - - assert(r->req.aiocb == NULL); - if (ufs_scsi_req_check_error(r, ret, false)) { - goto done; - } - - n = r->qiov.size / BDRV_SECTOR_SIZE; - r->sector += n; - r->sector_count -= n; - if (r->sector_count == 0) { - scsi_write_do_fua(r); - return; - } else { - scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); - trace_ufs_scsi_write_complete_noio(r->req.tag, r->qiov.size); - scsi_req_data(&r->req, r->qiov.size); + if (req->req_upiu.sc.cdb[0] == REPORT_LUNS) { + return ufs_emulate_scsi_cmd(lu, req); } -done: - scsi_req_unref(&r->req); -} - -static void scsi_write_complete(void *opaque, int ret) -{ - UfsSCSIReq *r = (UfsSCSIReq *)opaque; - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; + SCSIRequest *scsi_req = + scsi_req_new(lu->scsi_dev, task_tag, lu->lun, req->req_upiu.sc.cdb, + UFS_CDB_SIZE, req); - aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk)); - if (ret < 0) { - block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } else { - block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct); + uint32_t len = scsi_req_enqueue(scsi_req); + if (len) { + scsi_req_continue(scsi_req); } - scsi_write_complete_noio(r, ret); - aio_context_release(blk_get_aio_context(lu->qdev.conf.blk)); -} -static BlockAIOCB *scsi_dma_writev(int64_t offset, QEMUIOVector *iov, - BlockCompletionFunc *cb, void *cb_opaque, - void *opaque) -{ - UfsSCSIReq *r = opaque; - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - return blk_aio_pwritev(lu->qdev.conf.blk, offset, iov, 0, cb, cb_opaque); + return UFS_REQUEST_NO_COMPLETE; } -static void scsi_write_data(SCSIRequest *req) -{ - UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req); - UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev); - - /* No data transfer may already be in progress */ - assert(r->req.aiocb == NULL); - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - if (r->req.cmd.mode != SCSI_XFER_TO_DEV) { - trace_ufs_scsi_write_data_invalid(); - scsi_write_complete_noio(r, -EINVAL); - return; - } - - if (!r->req.sg && !r->qiov.size) { - /* Called for the first time. Ask the driver to send us more data. */ - r->started = true; - scsi_write_complete_noio(r, 0); - return; - } - if (!blk_is_available(req->dev->conf.blk)) { - scsi_write_complete_noio(r, -ENOMEDIUM); - return; - } - - if (r->req.sg) { - dma_acct_start(lu->qdev.conf.blk, &r->acct, r->req.sg, - BLOCK_ACCT_WRITE); - r->req.residual -= r->req.sg->size; - r->req.aiocb = dma_blk_io( - blk_get_aio_context(lu->qdev.conf.blk), r->req.sg, - r->sector << BDRV_SECTOR_BITS, BDRV_SECTOR_SIZE, scsi_dma_writev, r, - scsi_dma_complete, r, DMA_DIRECTION_TO_DEVICE); - } else { - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, - r->qiov.size, BLOCK_ACCT_WRITE); - r->req.aiocb = scsi_dma_writev(r->sector << BDRV_SECTOR_BITS, &r->qiov, - scsi_write_complete, r, r); - } -} - -static const SCSIReqOps ufs_scsi_emulate_reqops = { - .size = sizeof(UfsSCSIReq), - .free_req = ufs_scsi_free_request, - .send_command = ufs_scsi_emulate_command, - .read_data = ufs_scsi_emulate_read_data, - .write_data = ufs_scsi_emulate_write_data, - .get_buf = ufs_scsi_get_buf, -}; - -static const SCSIReqOps ufs_scsi_dma_reqops = { - .size = sizeof(UfsSCSIReq), - .free_req = ufs_scsi_free_request, - .send_command = ufs_scsi_dma_command, - .read_data = scsi_read_data, - .write_data = scsi_write_data, - .get_buf = ufs_scsi_get_buf, -}; - -/* - * Following commands are not yet supported - * PRE_FETCH(10), - * UNMAP, - * WRITE_BUFFER, READ_BUFFER, - * SECURITY_PROTOCOL_IN, SECURITY_PROTOCOL_OUT - */ -static const SCSIReqOps *const ufs_scsi_reqops_dispatch[256] = { - [TEST_UNIT_READY] = &ufs_scsi_emulate_reqops, - [INQUIRY] = &ufs_scsi_emulate_reqops, - [MODE_SENSE_10] = &ufs_scsi_emulate_reqops, - [START_STOP] = &ufs_scsi_emulate_reqops, - [READ_CAPACITY_10] = &ufs_scsi_emulate_reqops, - [REQUEST_SENSE] = &ufs_scsi_emulate_reqops, - [SYNCHRONIZE_CACHE] = &ufs_scsi_emulate_reqops, - [MODE_SELECT_10] = &ufs_scsi_emulate_reqops, - [VERIFY_10] = &ufs_scsi_emulate_reqops, - [FORMAT_UNIT] = &ufs_scsi_emulate_reqops, - [SERVICE_ACTION_IN_16] = &ufs_scsi_emulate_reqops, - [SEND_DIAGNOSTIC] = &ufs_scsi_emulate_reqops, - - [READ_6] = &ufs_scsi_dma_reqops, - [READ_10] = &ufs_scsi_dma_reqops, - [WRITE_6] = &ufs_scsi_dma_reqops, - [WRITE_10] = &ufs_scsi_dma_reqops, +static const Property ufs_lu_props[] = { + DEFINE_PROP_DRIVE("drive", UfsLu, conf.blk), + DEFINE_PROP_UINT8("lun", UfsLu, lun, 0), }; -static SCSIRequest *scsi_new_request(SCSIDevice *dev, uint32_t tag, - uint32_t lun, uint8_t *buf, - void *hba_private) -{ - UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev); - SCSIRequest *req; - const SCSIReqOps *ops; - uint8_t command; - - command = buf[0]; - ops = ufs_scsi_reqops_dispatch[command]; - if (!ops) { - ops = &ufs_scsi_emulate_reqops; - } - req = scsi_req_alloc(ops, &lu->qdev, tag, lun, hba_private); - - return req; -} - -static Property ufs_lu_props[] = { - DEFINE_PROP_DRIVE("drive", UfsLu, qdev.conf.blk), - DEFINE_PROP_END_OF_LIST(), -}; - -static bool ufs_lu_brdv_init(UfsLu *lu, Error **errp) -{ - SCSIDevice *dev = &lu->qdev; - bool read_only; - - if (!lu->qdev.conf.blk) { - error_setg(errp, "drive property not set"); - return false; - } - - if (!blkconf_blocksizes(&lu->qdev.conf, errp)) { - return false; - } - - if (blk_get_aio_context(lu->qdev.conf.blk) != qemu_get_aio_context() && - !lu->qdev.hba_supports_iothread) { - error_setg(errp, "HBA does not support iothreads"); - return false; - } - - read_only = !blk_supports_write_perm(lu->qdev.conf.blk); - - if (!blkconf_apply_backend_options(&dev->conf, read_only, - dev->type == TYPE_DISK, errp)) { - return false; - } - - if (blk_is_sg(lu->qdev.conf.blk)) { - error_setg(errp, "unwanted /dev/sg*"); - return false; - } - - blk_iostatus_enable(lu->qdev.conf.blk); - return true; -} - static bool ufs_add_lu(UfsHc *u, UfsLu *lu, Error **errp) { - BlockBackend *blk = lu->qdev.conf.blk; + 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); @@ -1288,158 +303,143 @@ static bool ufs_add_lu(UfsHc *u, UfsLu *lu, Error **errp) return true; } -static inline uint8_t ufs_log2(uint64_t input) +void ufs_init_wlu(UfsLu *wlu, uint8_t wlun) { - int log = 0; - while (input >>= 1) { - log++; - } - return log; + wlu->lun = wlun; + wlu->scsi_op = &ufs_emulate_scsi_cmd; } static void ufs_init_lu(UfsLu *lu) { - BlockBackend *blk = lu->qdev.conf.blk; + BlockBackend *blk = lu->conf.blk; int64_t brdv_len = blk_getlength(blk); - lu->lun = lu->qdev.lun; 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_log2(lu->qdev.blocksize); - lu->unit_desc.unit_index = lu->qdev.lun; + 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->qdev.conf.blk) { + if (!lu->conf.blk) { error_setg(errp, "drive property not set"); return false; } - if (lu->qdev.channel != 0) { - error_setg(errp, "ufs logical unit does not support channel"); + if (lu->lun >= UFS_MAX_LUS) { + error_setg(errp, "lun must be between 0 and %d", UFS_MAX_LUS - 1); return false; } - if (lu->qdev.lun >= UFS_MAX_LUS) { - error_setg(errp, "lun must be between 1 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; } - return true; + 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(SCSIDevice *dev, Error **errp) +static void ufs_lu_realize(DeviceState *dev, Error **errp) { UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev); - BusState *s = qdev_get_parent_bus(&dev->qdev); + BusState *s = qdev_get_parent_bus(dev); UfsHc *u = UFS(s->parent); - AioContext *ctx = NULL; - uint64_t nb_sectors, nb_blocks; + BlockBackend *blk = lu->conf.blk; if (!ufs_lu_check_constraints(lu, errp)) { return; } - if (lu->qdev.conf.blk) { - ctx = blk_get_aio_context(lu->qdev.conf.blk); - aio_context_acquire(ctx); - if (!blkconf_blocksizes(&lu->qdev.conf, errp)) { - goto out; - } + if (!blk) { + error_setg(errp, "drive property not set"); + return; + } + + if (!blkconf_blocksizes(&lu->conf, errp)) { + return; } - lu->qdev.blocksize = UFS_BLOCK_SIZE; - blk_get_geometry(lu->qdev.conf.blk, &nb_sectors); - nb_blocks = nb_sectors / (lu->qdev.blocksize / BDRV_SECTOR_SIZE); - if (nb_blocks > UINT32_MAX) { - nb_blocks = UINT32_MAX; + + if (!blkconf_apply_backend_options(&lu->conf, !blk_supports_write_perm(blk), + true, errp)) { + return; } - lu->qdev.max_lba = nb_blocks; - lu->qdev.type = TYPE_DISK; ufs_init_lu(lu); if (!ufs_add_lu(u, lu, errp)) { - goto out; + return; } - ufs_lu_brdv_init(lu, errp); -out: - if (ctx) { - aio_context_release(ctx); - } + ufs_init_scsi_device(lu, blk, errp); } -static void ufs_lu_unrealize(SCSIDevice *dev) +static void ufs_lu_unrealize(DeviceState *dev) { UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev); - blk_drain(lu->qdev.conf.blk); -} - -static void ufs_wlu_realize(DeviceState *qdev, Error **errp) -{ - UfsWLu *wlu = UFSWLU(qdev); - SCSIDevice *dev = &wlu->qdev; - - if (!is_wlun(dev->lun)) { - error_setg(errp, "not well-known logical unit number"); - return; + if (lu->scsi_dev) { + object_unref(OBJECT(lu->scsi_dev)); + lu->scsi_dev = NULL; } - - QTAILQ_INIT(&dev->requests); } -static void ufs_lu_class_init(ObjectClass *oc, void *data) +static void ufs_lu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(oc); - sc->realize = ufs_lu_realize; - sc->unrealize = ufs_lu_unrealize; - sc->alloc_req = scsi_new_request; + 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 void ufs_wlu_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(oc); - - /* - * The realize() function of TYPE_SCSI_DEVICE causes a segmentation fault - * if a block drive does not exist. Define a new realize function for - * well-known LUs that do not have a block drive. - */ - dc->realize = ufs_wlu_realize; - sc->alloc_req = scsi_new_request; - dc->bus_type = TYPE_UFS_BUS; - dc->desc = "Virtual UFS well-known logical unit"; -} - static const TypeInfo ufs_lu_info = { .name = TYPE_UFS_LU, - .parent = TYPE_SCSI_DEVICE, + .parent = TYPE_DEVICE, .class_init = ufs_lu_class_init, .instance_size = sizeof(UfsLu), }; -static const TypeInfo ufs_wlu_info = { - .name = TYPE_UFS_WLU, - .parent = TYPE_SCSI_DEVICE, - .class_init = ufs_wlu_class_init, - .instance_size = sizeof(UfsWLu), -}; - static void ufs_lu_register_types(void) { type_register_static(&ufs_lu_info); - type_register_static(&ufs_wlu_info); } type_init(ufs_lu_register_types) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index 1e55fb0d08e..531dcfc6865 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -11,38 +11,18 @@ 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"" - -# lu.c -ufs_scsi_check_condition(uint32_t tag, uint8_t key, uint8_t asc, uint8_t ascq) "Command complete tag=0x%x sense=%d/%d/%d" -ufs_scsi_read_complete(uint32_t tag, size_t size) "Data ready tag=0x%x len=%zd" -ufs_scsi_read_data_count(uint32_t sector_count) "Read sector_count=%d" -ufs_scsi_read_data_invalid(void) "Data transfer direction invalid" -ufs_scsi_write_complete_noio(uint32_t tag, size_t size) "Write complete tag=0x%x more=%zd" -ufs_scsi_write_data_invalid(void) "Data transfer direction invalid" -ufs_scsi_emulate_vpd_page_00(size_t xfer) "Inquiry EVPD[Supported pages] buffer size %zd" -ufs_scsi_emulate_vpd_page_80_not_supported(void) "Inquiry EVPD[Serial number] not supported" -ufs_scsi_emulate_vpd_page_80(size_t xfer) "Inquiry EVPD[Serial number] buffer size %zd" -ufs_scsi_emulate_vpd_page_87(size_t xfer) "Inquiry EVPD[Mode Page Policy] buffer size %zd" -ufs_scsi_emulate_mode_sense(int cmd, int page, size_t xfer, int control) "Mode Sense(%d) (page %d, xfer %zd, page_control %d)" -ufs_scsi_emulate_read_data(int buflen) "Read buf_len=%d" -ufs_scsi_emulate_write_data(int buflen) "Write buf_len=%d" -ufs_scsi_emulate_command_START_STOP(void) "START STOP UNIT" -ufs_scsi_emulate_command_FORMAT_UNIT(void) "FORMAT UNIT" -ufs_scsi_emulate_command_SEND_DIAGNOSTIC(void) "SEND DIAGNOSTIC" -ufs_scsi_emulate_command_SAI_16(void) "SAI READ CAPACITY(16)" -ufs_scsi_emulate_command_SAI_unsupported(void) "Unsupported Service Action In" -ufs_scsi_emulate_command_MODE_SELECT_10(size_t xfer) "Mode Select(10) (len %zd)" -ufs_scsi_emulate_command_VERIFY(int bytchk) "Verify (bytchk %d)" -ufs_scsi_emulate_command_UNKNOWN(int cmd, const char *name) "Unknown SCSI command (0x%2.2x=%s)" -ufs_scsi_dma_command_READ(uint64_t lba, uint32_t len) "Read (block %" PRIu64 ", count %u)" -ufs_scsi_dma_command_WRITE(uint64_t lba, int len) "Write (block %" PRIu64 ", count %u)" +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" @@ -56,3 +36,15 @@ ufs_err_query_invalid_opcode(uint8_t opcode) "query request has invalid opcode. 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/ufs.c b/hw/ufs/ufs.c index 0ecedb9aed2..0577747f469 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -9,7 +9,7 @@ */ /** - * Reference Specs: https://www.jedec.org/, 3.1 + * Reference Specs: https://www.jedec.org/, 4.0 * * Usage * ----- @@ -24,13 +24,68 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "migration/vmstate.h" +#include "scsi/constants.h" +#include "hw/irq.h" #include "trace.h" #include "ufs.h" -/* The QEMU-UFS device follows spec version 3.1 */ -#define UFS_SPEC_VER 0x0310 +/* 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 + +/* Each value represents the temperature in celsius as (value - 80) */ +#define UFS_TEMPERATURE 120 +#define UFS_TOO_HIGH_TEMP_BOUNDARY 160 +#define UFS_TOO_LOW_TEMP_BOUNDARY 60 + +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) { @@ -62,8 +117,6 @@ static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, return pci_dma_write(PCI_DEVICE(u), addr, buf, size); } -static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); - static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) { hwaddr utrl_base_addr = (((hwaddr)u->reg.utrlbau) << 32) + u->reg.utrlba; @@ -127,6 +180,10 @@ static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req) 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); @@ -163,11 +220,13 @@ static MemTxResult ufs_dma_read_prdt(UfsRequest *req) 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; } @@ -176,9 +235,14 @@ static MemTxResult ufs_dma_read_upiu(UfsRequest *req) { MemTxResult ret; - ret = ufs_dma_read_utrd(req); - if (ret) { - return 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); @@ -224,6 +288,10 @@ static MemTxResult ufs_dma_write_rsp_upiu(UfsRequest *req) 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); @@ -258,7 +326,7 @@ static void ufs_irq_check(UfsHc *u) static void ufs_process_db(UfsHc *u, uint32_t val) { - unsigned long doorbell; + DECLARE_BITMAP(doorbell, UFS_MAX_NUTRS); uint32_t slot; uint32_t nutrs = u->params.nutrs; UfsRequest *req; @@ -268,8 +336,8 @@ static void ufs_process_db(UfsHc *u, uint32_t val) return; } - doorbell = val; - slot = find_first_bit(&doorbell, nutrs); + doorbell[0] = val; + slot = find_first_bit(doorbell, nutrs); while (slot < nutrs) { req = &u->req_list[slot]; @@ -285,7 +353,7 @@ static void ufs_process_db(UfsHc *u, uint32_t val) trace_ufs_process_db(slot); req->state = UFS_REQUEST_READY; - slot = find_next_bit(&doorbell, nutrs, slot + 1); + slot = find_next_bit(doorbell, nutrs, slot + 1); } qemu_bh_schedule(u->doorbell_bh); @@ -326,6 +394,219 @@ static void ufs_process_uiccmd(UfsHc *u, uint32_t val) 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) { @@ -381,6 +662,12 @@ static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size) 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: @@ -393,18 +680,138 @@ static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size) } } +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; - uint8_t *ptr = (uint8_t *)&u->reg; + uint32_t *ptr; uint64_t value; - - if (addr > sizeof(u->reg) - size) { + 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 = *(uint32_t *)(ptr + addr); + value = ptr[offset >> 2]; trace_ufs_mmio_read(addr, value, size); return value; } @@ -414,13 +821,17 @@ static void ufs_mmio_write(void *opaque, hwaddr addr, uint64_t data, { UfsHc *u = (UfsHc *)opaque; - if (addr > sizeof(u->reg) - size) { + 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); - return; } - - trace_ufs_mmio_write(addr, data, size); - ufs_write_reg(u, addr, data, size); } static const MemoryRegionOps ufs_mmio_ops = { @@ -433,122 +844,97 @@ static const MemoryRegionOps ufs_mmio_ops = { }, }; -static QEMUSGList *ufs_get_sg_list(SCSIRequest *scsi_req) +static void ufs_update_ee_status(UfsHc *u) { - UfsRequest *req = scsi_req->hba_private; - return req->sg; + uint16_t ee_status = be16_to_cpu(u->attributes.exception_event_status); + uint8_t high_temp_thresh = u->attributes.device_too_high_temp_boundary; + uint8_t low_temp_thresh = u->attributes.device_too_low_temp_boundary; + + if (u->temperature >= high_temp_thresh) { + ee_status |= MASK_EE_TOO_HIGH_TEMP; + } else { + ee_status &= ~MASK_EE_TOO_HIGH_TEMP; + } + + if (u->temperature <= low_temp_thresh) { + ee_status |= MASK_EE_TOO_LOW_TEMP; + } else { + ee_status &= ~MASK_EE_TOO_LOW_TEMP; + } + + u->attributes.exception_event_status = cpu_to_be16(ee_status); } -static void ufs_build_upiu_sense_data(UfsRequest *req, SCSIRequest *scsi_req) +static bool ufs_check_exception_event_alert(UfsHc *u, uint8_t trans_type) { - req->rsp_upiu.sr.sense_data_len = cpu_to_be16(scsi_req->sense_len); - assert(scsi_req->sense_len <= SCSI_SENSE_LEN); - memcpy(req->rsp_upiu.sr.sense_data, scsi_req->sense, scsi_req->sense_len); + uint16_t ee_control = be16_to_cpu(u->attributes.exception_event_control); + uint16_t ee_status; + + if (trans_type != UFS_UPIU_TRANSACTION_RESPONSE) { + return false; + } + + ufs_update_ee_status(u); + + ee_status = be16_to_cpu(u->attributes.exception_event_status); + + return ee_control & ee_status; } -static 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_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.device_inf = + ufs_check_exception_event_alert(req->hc, trans_type); req->rsp_upiu.header.data_segment_length = cpu_to_be16(data_segment_length); } -static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid) +void ufs_build_query_response(UfsRequest *req) { - UfsRequest *req = scsi_req->hba_private; - int16_t status = scsi_req->status; - uint32_t expected_len = be32_to_cpu(req->req_upiu.sc.exp_data_transfer_len); - uint32_t transfered_len = scsi_req->cmd.xfer - resid; - uint8_t flags = 0, response = UFS_COMMAND_RESULT_SUCESS; - 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, scsi_req); - response = UFS_COMMAND_RESULT_FAIL; - } - - data_segment_length = cpu_to_be16(scsi_req->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); - - ufs_complete_req(req, UFS_REQUEST_SUCCESS); - - scsi_req->hba_private = NULL; - scsi_req_unref(scsi_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 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 UfsReqResult ufs_exec_scsi_cmd(UfsRequest *req) { UfsHc *u = req->hc; uint8_t lun = req->req_upiu.header.lun; - uint8_t task_tag = req->req_upiu.header.task_tag; - SCSIDevice *dev = NULL; + + UfsLu *lu = NULL; trace_ufs_exec_scsi_cmd(req->slot, lun, req->req_upiu.sc.cdb[0]); - if (!is_wlun(lun)) { - if (lun >= u->device_desc.number_lu) { - trace_ufs_err_scsi_cmd_invalid_lun(lun); - return UFS_REQUEST_FAIL; - } else if (u->lus[lun] == NULL) { - trace_ufs_err_scsi_cmd_invalid_lun(lun); - return UFS_REQUEST_FAIL; - } + 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: - dev = &u->report_wlu->qdev; + lu = &u->report_wlu; break; case UFS_UPIU_UFS_DEVICE_WLUN: - dev = &u->dev_wlu->qdev; + lu = &u->dev_wlu; break; case UFS_UPIU_BOOT_WLUN: - dev = &u->boot_wlu->qdev; + lu = &u->boot_wlu; break; case UFS_UPIU_RPMB_WLUN: - dev = &u->rpmb_wlu->qdev; + lu = &u->rpmb_wlu; break; default: - dev = &u->lus[lun]->qdev; - } - - SCSIRequest *scsi_req = scsi_req_new( - dev, task_tag, 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); + lu = u->lus[lun]; } - return UFS_REQUEST_NO_COMPLETE; + return lu->scsi_op(lu, req); } static UfsReqResult ufs_exec_nop_cmd(UfsRequest *req) @@ -700,6 +1086,25 @@ static QueryRespCode ufs_exec_query_flag(UfsRequest *req, int op) return UFS_QUERY_RESULT_SUCCESS; } +static inline uint8_t ufs_read_device_temp(UfsHc *u) +{ + uint8_t feat_sup = u->device_desc.ufs_features_support; + bool high_temp_sup, low_temp_sup, high_temp_en, low_temp_en; + uint16_t ee_control = be16_to_cpu(u->attributes.exception_event_control); + + high_temp_sup = feat_sup & UFS_DEV_HIGH_TEMP_NOTIF; + low_temp_sup = feat_sup & UFS_DEV_LOW_TEMP_NOTIF; + high_temp_en = ee_control & MASK_EE_TOO_HIGH_TEMP; + low_temp_en = ee_control & MASK_EE_TOO_LOW_TEMP; + + if ((high_temp_sup && high_temp_en) || + (low_temp_sup && low_temp_en)) { + return u->temperature; + } + + return 0; +} + static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn) { switch (idn) { @@ -730,6 +1135,7 @@ static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn) case UFS_QUERY_ATTR_IDN_EE_CONTROL: return be16_to_cpu(u->attributes.exception_event_control); case UFS_QUERY_ATTR_IDN_EE_STATUS: + ufs_update_ee_status(u); return be16_to_cpu(u->attributes.exception_event_status); case UFS_QUERY_ATTR_IDN_SECONDS_PASSED: return be32_to_cpu(u->attributes.seconds_passed); @@ -744,7 +1150,8 @@ static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn) 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; + u->attributes.device_case_rough_temperature = ufs_read_device_temp(u); + return u->attributes.device_case_rough_temperature; case UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND: return u->attributes.device_too_high_temp_boundary; case UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND: @@ -769,10 +1176,13 @@ static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn) return 0; } -static void ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value) +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: @@ -800,6 +1210,7 @@ static void ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value) 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) @@ -816,13 +1227,13 @@ static QueryRespCode ufs_exec_query_attr(UfsRequest *req, int op) if (op == UFS_QUERY_ATTR_READ) { value = ufs_read_attr_value(u, idn); + ret = UFS_QUERY_RESULT_SUCCESS; } else { value = be32_to_cpu(req->req_upiu.qr.value); - ufs_write_attr_value(u, idn, value); + ret = ufs_write_attr_value(u, idn, value); } - req->rsp_upiu.qr.value = cpu_to_be32(value); - return UFS_QUERY_RESULT_SUCCESS; + return ret; } static const RpmbUnitDescriptor rpmb_unit_desc = { @@ -838,7 +1249,7 @@ static QueryRespCode ufs_read_unit_desc(UfsRequest *req) uint8_t lun = req->req_upiu.qr.index; if (lun != UFS_UPIU_RPMB_WLUN && - (lun > UFS_MAX_LUS || u->lus[lun] == NULL)) { + (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; } @@ -945,9 +1356,12 @@ 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)); @@ -993,10 +1407,6 @@ static QueryRespCode ufs_read_desc(UfsRequest *req) if (length > req->rsp_upiu.qr.data[0]) { length = req->rsp_upiu.qr.data[0]; } - req->rsp_upiu.qr.opcode = req->req_upiu.qr.opcode; - req->rsp_upiu.qr.idn = req->req_upiu.qr.idn; - req->rsp_upiu.qr.index = req->req_upiu.qr.index; - req->rsp_upiu.qr.selector = req->req_upiu.qr.selector; req->rsp_upiu.qr.length = cpu_to_be16(length); return status; @@ -1077,6 +1487,7 @@ static UfsReqResult ufs_exec_query_cmd(UfsRequest *req) 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; @@ -1137,7 +1548,7 @@ static void ufs_process_req(void *opaque) } } -static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result) +void ufs_complete_req(UfsRequest *req, UfsReqResult req_result) { UfsHc *u = req->hc; assert(req->state == UFS_REQUEST_RUNNING); @@ -1148,9 +1559,16 @@ static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result) req->utrd.header.dword_2 = cpu_to_le32(UFS_OCS_INVALID_CMD_TABLE_ATTR); } - trace_ufs_complete_req(req->slot); req->state = UFS_REQUEST_COMPLETE; - qemu_bh_schedule(u->complete_bh); + + 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) @@ -1159,6 +1577,7 @@ static void ufs_clear_req(UfsRequest *req) qemu_sglist_destroy(req->sg); g_free(req->sg); req->sg = NULL; + req->data_len = 0; } memset(&req->utrd, 0, sizeof(req->utrd)); @@ -1219,6 +1638,11 @@ static bool ufs_check_constraints(UfsHc *u, Error **errp) 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; } @@ -1250,15 +1674,24 @@ static void ufs_init_state(UfsHc *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(sizeof(UfsReg)); + 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)); @@ -1267,7 +1700,29 @@ static void ufs_init_hc(UfsHc *u) 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, 0); + 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)); @@ -1287,15 +1742,19 @@ static void ufs_init_hc(UfsHc *u) 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.ufs_features_support = UFS_DEV_HIGH_TEMP_NOTIF | + UFS_DEV_LOW_TEMP_NOTIF; u->device_desc.queue_depth = u->params.nutrs; u->device_desc.product_revision_level = 0x04; + u->device_desc.extended_ufs_features_support = + cpu_to_be32(UFS_DEV_HIGH_TEMP_NOTIF | UFS_DEV_LOW_TEMP_NOTIF); 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.segment_size = cpu_to_be32(0x2000); /* 4MB: 8192 * 512B */ + u->geometry_desc.allocation_unit_size = 0x1; /* 4MB: 1 segment */ 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; @@ -1312,31 +1771,17 @@ static void ufs_init_hc(UfsHc *u) /* configure descriptor is not supported */ u->attributes.config_descr_lock = 0x01; u->attributes.max_num_of_rtt = 0x02; + u->attributes.device_too_high_temp_boundary = UFS_TOO_HIGH_TEMP_BOUNDARY; + u->attributes.device_too_low_temp_boundary = UFS_TOO_LOW_TEMP_BOUNDARY; memset(&u->flags, 0, sizeof(u->flags)); u->flags.permanently_disable_fw_update = 1; -} - -static bool ufs_init_wlu(UfsHc *u, UfsWLu **wlu, uint8_t wlun, Error **errp) -{ - UfsWLu *new_wlu = UFSWLU(qdev_new(TYPE_UFS_WLU)); - - qdev_prop_set_uint32(DEVICE(new_wlu), "lun", wlun); /* - * The well-known lu shares the same bus as the normal lu. If the well-known - * lu writes the same channel value as the normal lu, the report will be - * made not only for the normal lu but also for the well-known lu at - * REPORT_LUN time. To prevent this, the channel value of normal lu is fixed - * to 0 and the channel value of well-known lu is fixed to 1. + * The temperature value is fixed to UFS_TEMPERATURE and does not change + * dynamically */ - qdev_prop_set_uint32(DEVICE(new_wlu), "channel", 1); - if (!qdev_realize_and_unref(DEVICE(new_wlu), BUS(&u->bus), errp)) { - return false; - } - - *wlu = new_wlu; - return true; + u->temperature = UFS_TEMPERATURE; } static void ufs_realize(PCIDevice *pci_dev, Error **errp) @@ -1349,52 +1794,22 @@ static void ufs_realize(PCIDevice *pci_dev, Error **errp) qbus_init(&u->bus, sizeof(UfsBus), TYPE_UFS_BUS, &pci_dev->qdev, u->parent_obj.qdev.id); - u->bus.parent_bus.info = &ufs_scsi_info; ufs_init_state(u); ufs_init_hc(u); ufs_init_pci(u, pci_dev); - if (!ufs_init_wlu(u, &u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN, errp)) { - return; - } - - if (!ufs_init_wlu(u, &u->dev_wlu, UFS_UPIU_UFS_DEVICE_WLUN, errp)) { - return; - } - - if (!ufs_init_wlu(u, &u->boot_wlu, UFS_UPIU_BOOT_WLUN, errp)) { - return; - } - - if (!ufs_init_wlu(u, &u->rpmb_wlu, UFS_UPIU_RPMB_WLUN, errp)) { - return; - } + 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); - if (u->dev_wlu) { - object_unref(OBJECT(u->dev_wlu)); - u->dev_wlu = NULL; - } - - if (u->report_wlu) { - object_unref(OBJECT(u->report_wlu)); - u->report_wlu = NULL; - } - - if (u->rpmb_wlu) { - object_unref(OBJECT(u->rpmb_wlu)); - u->rpmb_wlu = NULL; - } - - if (u->boot_wlu) { - object_unref(OBJECT(u->boot_wlu)); - u->boot_wlu = NULL; - } + qemu_free_irq(u->irq); qemu_bh_delete(u->doorbell_bh); qemu_bh_delete(u->complete_bh); @@ -1403,13 +1818,25 @@ static void ufs_exit(PCIDevice *pci_dev) 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[] = { +static const Property ufs_props[] = { DEFINE_PROP_STRING("serial", UfsHc, params.serial), DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32), DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_BOOL("mcq", UfsHc, params.mcq, false), + DEFINE_PROP_UINT8("mcq-maxq", UfsHc, params.mcq_maxq, 2), }; static const VMStateDescription ufs_vmstate = { @@ -1417,7 +1844,7 @@ static const VMStateDescription ufs_vmstate = { .unmigratable = 1, }; -static void ufs_class_init(ObjectClass *oc, void *data) +static void ufs_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); @@ -1437,43 +1864,26 @@ static void ufs_class_init(ObjectClass *oc, void *data) static bool ufs_bus_check_address(BusState *qbus, DeviceState *qdev, Error **errp) { - SCSIDevice *dev = SCSI_DEVICE(qdev); - UfsBusClass *ubc = UFS_BUS_GET_CLASS(qbus); - UfsHc *u = UFS(qbus->parent); - - if (strcmp(object_get_typename(OBJECT(dev)), TYPE_UFS_WLU) == 0) { - if (dev->lun != UFS_UPIU_REPORT_LUNS_WLUN && - dev->lun != UFS_UPIU_UFS_DEVICE_WLUN && - dev->lun != UFS_UPIU_BOOT_WLUN && dev->lun != UFS_UPIU_RPMB_WLUN) { - error_setg(errp, "bad well-known lun: %d", dev->lun); - return false; - } - - if ((dev->lun == UFS_UPIU_REPORT_LUNS_WLUN && u->report_wlu != NULL) || - (dev->lun == UFS_UPIU_UFS_DEVICE_WLUN && u->dev_wlu != NULL) || - (dev->lun == UFS_UPIU_BOOT_WLUN && u->boot_wlu != NULL) || - (dev->lun == UFS_UPIU_RPMB_WLUN && u->rpmb_wlu != NULL)) { - error_setg(errp, "well-known lun %d already exists", dev->lun); - return false; - } - - return true; - } - - if (strcmp(object_get_typename(OBJECT(dev)), TYPE_UFS_LU) != 0) { + 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(dev))); + object_get_typename(OBJECT(qdev))); return false; } - return ubc->parent_check_address(qbus, qdev, errp); + 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) +static void ufs_bus_class_init(ObjectClass *class, const void *data) { BusClass *bc = BUS_CLASS(class); - UfsBusClass *ubc = UFS_BUS_CLASS(class); - ubc->parent_check_address = bc->check_address; + bc->get_dev_path = ufs_bus_get_dev_path; bc->check_address = ufs_bus_check_address; } @@ -1482,12 +1892,12 @@ static const TypeInfo ufs_info = { .parent = TYPE_PCI_DEVICE, .class_init = ufs_class_init, .instance_size = sizeof(UfsHc), - .interfaces = (InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} }, + .interfaces = (const InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} }, }; static const TypeInfo ufs_bus_info = { .name = TYPE_UFS_BUS, - .parent = TYPE_SCSI_BUS, + .parent = TYPE_BUS, .class_init = ufs_bus_class_init, .class_size = sizeof(UfsBusClass), .instance_size = sizeof(UfsBus), diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h index f2442286175..3799d97f30d 100644 --- a/hw/ufs/ufs.h +++ b/hw/ufs/ufs.h @@ -16,7 +16,9 @@ #include "block/ufs.h" #define UFS_MAX_LUS 32 -#define UFS_BLOCK_SIZE 4096 +#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; @@ -24,7 +26,7 @@ typedef struct UfsBusClass { } UfsBusClass; typedef struct UfsBus { - SCSIBus parent_bus; + BusState parent_bus; } UfsBus; #define TYPE_UFS_BUS "ufs-bus" @@ -44,10 +46,11 @@ typedef enum UfsReqResult { UFS_REQUEST_NO_COMPLETE = 2, } UfsReqResult; +#define UFS_INVALID_SLOT (-1) typedef struct UfsRequest { struct UfsHc *hc; UfsRequestState state; - int slot; + int slot; /* -1 when it's a MCQ request */ UtpTransferReqDesc utrd; UtpUpiuReq req_upiu; @@ -55,39 +58,82 @@ typedef struct UfsRequest { /* 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 { - SCSIDevice qdev; + DeviceState qdev; uint8_t lun; UnitDescriptor unit_desc; + SCSIBus bus; + SCSIDevice *scsi_dev; + BlockConf conf; + UfsScsiOp scsi_op; } UfsLu; -typedef struct UfsWLu { - SCSIDevice qdev; - uint8_t lun; -} UfsWLu; - 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]; - UfsWLu *report_wlu; - UfsWLu *dev_wlu; - UfsWLu *boot_wlu; - UfsWLu *rpmb_wlu; + UfsLu report_wlu; + UfsLu dev_wlu; + UfsLu boot_wlu; + UfsLu rpmb_wlu; DeviceDescriptor device_desc; GeometryDescriptor geometry_desc; Attributes attributes; @@ -96,17 +142,70 @@ typedef struct UfsHc { qemu_irq irq; QEMUBH *doorbell_bh; QEMUBH *complete_bh; + + /* MCQ properties */ + UfsSq *sq[UFS_MAX_MCQ_QNUM]; + UfsCq *cq[UFS_MAX_MCQ_QNUM]; + + uint8_t temperature; } 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) -#define TYPE_UFS_WLU "ufs-wlu" -#define UFSWLU(obj) OBJECT_CHECK(UfsWLu, (obj), TYPE_UFS_WLU) - typedef enum UfsQueryFlagPerm { UFS_QUERY_FLAG_NONE = 0x0, UFS_QUERY_FLAG_READ = 0x1, @@ -128,4 +227,10 @@ static inline bool is_wlun(uint8_t lun) 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 0f486764ed6..69c663be52f 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 @@ -49,17 +53,19 @@ config USB_XHCI_SYSBUS bool select USB_XHCI -config USB_MUSB +config USB_DWC2 bool select USB -config USB_DWC2 +config USB_HUB bool - select USB + default y + depends on USB -config TUSB6010 +config USB_HID bool - select USB_MUSB + default y + depends on USB config USB_TABLET_WACOM bool @@ -137,3 +143,7 @@ config USB_DWC3 config XLNX_USB_SUBSYS bool select USB_DWC3 + +config USB_CHIPIDEA + bool + select USB_EHCI_SYSBUS diff --git a/hw/usb/bus-stub.c b/hw/usb/bus-stub.c new file mode 100644 index 00000000000..cd0c317b718 --- /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 "system/system.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 92d6ed56261..8dd2ce415eb 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -6,7 +6,7 @@ #include "qapi/type-helpers.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "migration/vmstate.h" #include "monitor/monitor.h" #include "trace.h" @@ -18,16 +18,15 @@ static char *usb_get_dev_path(DeviceState *dev); static char *usb_get_fw_dev_path(DeviceState *qdev); static void usb_qdev_unrealize(DeviceState *qdev); -static Property usb_props[] = { +static const Property usb_props[] = { DEFINE_PROP_STRING("port", USBDevice, port_path), DEFINE_PROP_STRING("serial", USBDevice, serial), DEFINE_PROP_BIT("msos-desc", USBDevice, flags, USB_DEV_FLAG_MSOS_DESC_ENABLE, true), DEFINE_PROP_STRING("pcap", USBDevice, pcap_filename), - DEFINE_PROP_END_OF_LIST() }; -static void usb_bus_class_init(ObjectClass *klass, void *data) +static void usb_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); @@ -43,7 +42,7 @@ static const TypeInfo usb_bus_info = { .parent = TYPE_BUS, .instance_size = sizeof(USBBus), .class_init = usb_bus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } @@ -69,7 +68,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 +99,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 +259,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 +316,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) { @@ -447,7 +411,7 @@ void usb_claim_port(USBDevice *dev, Error **errp) } else { if (bus->nfree == 1 && strcmp(object_get_typename(OBJECT(dev)), "usb-hub") != 0) { /* Create a new hub and chain it on */ - hub = usb_try_new("usb-hub"); + hub = USB_DEVICE(qdev_try_new("usb-hub")); if (hub) { usb_realize_and_unref(hub, bus, NULL); } @@ -666,7 +630,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; @@ -698,7 +662,8 @@ USBDevice *usbdevice_create(const char *driver) return NULL; } - dev = f->usbdevice_init ? f->usbdevice_init() : usb_new(f->name); + dev = f->usbdevice_init ? f->usbdevice_init() + : USB_DEVICE(qdev_new(f->name)); if (!dev) { error_report("Failed to create USB device '%s'", f->name); return NULL; @@ -748,7 +713,7 @@ static void usb_device_instance_init(Object *obj) } } -static void usb_device_class_init(ObjectClass *klass, void *data) +static void usb_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->bus_type = TYPE_USB_BUS; diff --git a/hw/usb/canokey.c b/hw/usb/canokey.c index b306eeb20e9..cbefbb5daf8 100644 --- a/hw/usb/canokey.c +++ b/hw/usb/canokey.c @@ -197,8 +197,8 @@ static void canokey_handle_data(USBDevice *dev, USBPacket *p) switch (p->pid) { case USB_TOKEN_OUT: trace_canokey_handle_data_out(ep_out, p->iov.size); - usb_packet_copy(p, key->ep_out_buffer[ep_out], p->iov.size); out_pos = 0; + /* segment packet into (possibly multiple) ep_out */ while (out_pos != p->iov.size) { /* * key->ep_out[ep_out] set by prepare_receive @@ -207,8 +207,8 @@ static void canokey_handle_data(USBDevice *dev, USBPacket *p) * to be the buffer length */ out_len = MIN(p->iov.size - out_pos, key->ep_out_size[ep_out]); - memcpy(key->ep_out[ep_out], - key->ep_out_buffer[ep_out] + out_pos, out_len); + /* usb_packet_copy would update the pos offset internally */ + usb_packet_copy(p, key->ep_out[ep_out], out_len); out_pos += out_len; /* update ep_out_size to actual len */ key->ep_out_size[ep_out] = out_len; @@ -296,12 +296,11 @@ static void canokey_unrealize(USBDevice *base) trace_canokey_unrealize(); } -static Property canokey_properties[] = { +static const Property canokey_properties[] = { DEFINE_PROP_STRING("file", CanoKeyState, file), - DEFINE_PROP_END_OF_LIST(), }; -static void canokey_class_init(ObjectClass *klass, void *data) +static void canokey_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/canokey.h b/hw/usb/canokey.h index e528889d332..1b60d734850 100644 --- a/hw/usb/canokey.h +++ b/hw/usb/canokey.h @@ -24,8 +24,6 @@ #define CANOKEY_EP_NUM 3 /* BULK/INTR IN can be up to 1352 bytes, e.g. get key info */ #define CANOKEY_EP_IN_BUFFER_SIZE 2048 -/* BULK OUT can be up to 270 bytes, e.g. PIV import cert */ -#define CANOKEY_EP_OUT_BUFFER_SIZE 512 typedef enum { CANOKEY_EP_IN_WAIT, @@ -59,8 +57,6 @@ typedef struct CanoKeyState { /* OUT pointer to canokey recv buffer */ uint8_t *ep_out[CANOKEY_EP_NUM]; uint32_t ep_out_size[CANOKEY_EP_NUM]; - /* For large BULK OUT, multiple write to ep_out is needed */ - uint8_t ep_out_buffer[CANOKEY_EP_NUM][CANOKEY_EP_OUT_BUFFER_SIZE]; /* Properties */ char *file; /* canokey-file */ diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c index 3ee9c73b879..c21cefd82d2 100644 --- a/hw/usb/ccid-card-emulated.c +++ b/hw/usb/ccid-card-emulated.c @@ -582,17 +582,16 @@ static void emulated_unrealize(CCIDCardState *base) qemu_mutex_destroy(&card->event_list_mutex); } -static Property emulated_card_properties[] = { +static const Property emulated_card_properties[] = { DEFINE_PROP_STRING("backend", EmulatedState, backend_str), DEFINE_PROP_STRING("cert1", EmulatedState, cert1), DEFINE_PROP_STRING("cert2", EmulatedState, cert2), DEFINE_PROP_STRING("cert3", EmulatedState, cert3), DEFINE_PROP_STRING("db", EmulatedState, db), DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void emulated_class_initfn(ObjectClass *klass, void *data) +static void emulated_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); CCIDCardClass *cc = CCID_CARD_CLASS(klass); diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index 07ee42f304f..1eea21a7337 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), @@ -388,13 +388,12 @@ static const VMStateDescription passthru_vmstate = { } }; -static Property passthru_card_properties[] = { +static const Property passthru_card_properties[] = { DEFINE_PROP_CHR("chardev", PassthruState, cs), DEFINE_PROP_UINT8("debug", PassthruState, debug, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void passthru_class_initfn(ObjectClass *klass, void *data) +static void passthru_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); CCIDCardClass *cc = CCID_CARD_CLASS(klass); diff --git a/hw/usb/chipidea.c b/hw/usb/chipidea.c index b1c85404d6f..250c2b3bca9 100644 --- a/hw/usb/chipidea.c +++ b/hw/usb/chipidea.c @@ -144,7 +144,7 @@ static void chipidea_init(Object *obj) } } -static void chipidea_class_init(ObjectClass *klass, void *data) +static void chipidea_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(klass); diff --git a/hw/usb/desc.c b/hw/usb/desc.c index 7f6cc2f99bd..f2bdc05a953 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 8748c1ba040..26af709f314 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; @@ -989,15 +990,14 @@ static const VMStateDescription vmstate_usb_audio = { .unmigratable = 1, }; -static Property usb_audio_properties[] = { +static const Property usb_audio_properties[] = { DEFINE_AUDIO_PROPERTIES(USBAudioState, card), DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0), DEFINE_PROP_UINT32("buffer", USBAudioState, buffer_user, 0), DEFINE_PROP_BOOL("multi", USBAudioState, multi, false), - DEFINE_PROP_END_OF_LIST(), }; -static void usb_audio_class_init(ObjectClass *klass, void *data) +static void usb_audio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *k = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index bdd6d1ffafe..54d064e54e1 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,14 +767,14 @@ 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() } }; -static void usb_hid_class_initfn(ObjectClass *klass, void *data) +static void usb_hid_class_initfn(ObjectClass *klass, const void *data) { USBDeviceClass *uc = USB_DEVICE_CLASS(klass); @@ -793,14 +793,13 @@ static const TypeInfo usb_hid_type_info = { .class_init = usb_hid_class_initfn, }; -static Property usb_tablet_properties[] = { +static const Property usb_tablet_properties[] = { DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), DEFINE_PROP_STRING("display", USBHIDState, display), DEFINE_PROP_UINT32("head", USBHIDState, head, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void usb_tablet_class_initfn(ObjectClass *klass, void *data) +static void usb_tablet_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); @@ -818,12 +817,11 @@ static const TypeInfo usb_tablet_info = { .class_init = usb_tablet_class_initfn, }; -static Property usb_mouse_properties[] = { +static const Property usb_mouse_properties[] = { DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), - DEFINE_PROP_END_OF_LIST(), }; -static void usb_mouse_class_initfn(ObjectClass *klass, void *data) +static void usb_mouse_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); @@ -841,13 +839,12 @@ static const TypeInfo usb_mouse_info = { .class_init = usb_mouse_class_initfn, }; -static Property usb_keyboard_properties[] = { +static const Property usb_keyboard_properties[] = { DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), DEFINE_PROP_STRING("display", USBHIDState, display), - DEFINE_PROP_END_OF_LIST(), }; -static void usb_keyboard_class_initfn(ObjectClass *klass, void *data) +static void usb_keyboard_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index a6b50dbc8df..a19350d9c42 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; } @@ -623,7 +624,7 @@ static const VMStateDescription vmstate_usb_hub_port = { .name = "usb-hub-port", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT16(wPortStatus, USBHubPort), VMSTATE_UINT16(wPortChange, USBHubPort), VMSTATE_END_OF_LIST() @@ -642,7 +643,7 @@ static const VMStateDescription vmstate_usb_hub_port_timer = { .version_id = 1, .minimum_version_id = 1, .needed = usb_hub_port_timer_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(port_timer, USBHubState), VMSTATE_END_OF_LIST() }, @@ -652,25 +653,24 @@ 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 } }; -static Property usb_hub_properties[] = { +static const Property usb_hub_properties[] = { DEFINE_PROP_UINT32("ports", USBHubState, num_ports, 8), DEFINE_PROP_BOOL("port-power", USBHubState, port_power, false), - DEFINE_PROP_END_OF_LIST(), }; -static void usb_hub_class_initfn(ObjectClass *klass, void *data) +static void usb_hub_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 1cac1cd4350..ce45c9cd061 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); @@ -1234,8 +1234,6 @@ static void usb_mtp_object_delete(MTPState *s, uint32_t handle, default: g_assert_not_reached(); } - - return; } static void usb_mtp_command(MTPState *s, MTPControl *c) @@ -2072,20 +2070,19 @@ 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() } }; -static Property mtp_properties[] = { +static const Property mtp_properties[] = { DEFINE_PROP_STRING("rootdir", MTPState, root), DEFINE_PROP_STRING("desc", MTPState, desc), DEFINE_PROP_BOOL("readonly", MTPState, readonly, true), - DEFINE_PROP_END_OF_LIST(), }; -static void usb_mtp_class_initfn(ObjectClass *klass, void *data) +static void usb_mtp_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 5fff487ee5d..81cc09dcac9 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -33,7 +33,7 @@ #include "qemu/error-report.h" #include "qemu/queue.h" #include "qemu/config-file.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/iov.h" #include "qemu/module.h" #include "qemu/cutils.h" @@ -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", @@ -1414,12 +1407,11 @@ static const VMStateDescription vmstate_usb_net = { .unmigratable = 1, }; -static Property net_properties[] = { +static const Property net_properties[] = { DEFINE_NIC_PROPERTIES(USBNetState, conf), - DEFINE_PROP_END_OF_LIST(), }; -static void usb_net_class_initfn(ObjectClass *klass, void *data) +static void usb_net_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 63047d79cfd..1c116d8b0fe 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -472,8 +472,6 @@ static void usb_serial_token_in(USBSerialState *s, USBPacket *p) s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; packet_len -= len + 2; } - - return; } static void usb_serial_handle_data(USBDevice *dev, USBPacket *p) @@ -624,7 +622,7 @@ static USBDevice *usb_braille_init(void) return NULL; } - dev = usb_new("usb-braille"); + dev = USB_DEVICE(qdev_new("usb-braille")); qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); return dev; } @@ -634,13 +632,12 @@ static const VMStateDescription vmstate_usb_serial = { .unmigratable = 1, }; -static Property serial_properties[] = { +static const Property serial_properties[] = { DEFINE_PROP_CHR("chardev", USBSerialState, cs), DEFINE_PROP_BOOL("always-plugged", USBSerialState, always_plugged, false), - DEFINE_PROP_END_OF_LIST(), }; -static void usb_serial_dev_class_init(ObjectClass *klass, void *data) +static void usb_serial_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); @@ -661,7 +658,7 @@ static const TypeInfo usb_serial_dev_type_info = { .class_init = usb_serial_dev_class_init, }; -static void usb_serial_class_initfn(ObjectClass *klass, void *data) +static void usb_serial_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); @@ -677,12 +674,11 @@ static const TypeInfo serial_info = { .class_init = usb_serial_class_initfn, }; -static Property braille_properties[] = { +static const Property braille_properties[] = { DEFINE_PROP_CHR("chardev", USBSerialState, cs), - DEFINE_PROP_END_OF_LIST(), }; -static void usb_braille_class_initfn(ObjectClass *klass, void *data) +static void usb_braille_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index be0a4fc3bc4..6ce7154feed 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -1069,7 +1069,6 @@ static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p) err: p->status = USB_RET_STALL; s->bulk_out_pos = 0; - return; } static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p, @@ -1171,9 +1170,8 @@ static Answer *ccid_peek_next_answer(USBCCIDState *s) : &s->pending_answers[s->pending_answers_start % PENDING_ANSWERS_NUM]; } -static Property ccid_props[] = { +static const Property ccid_props[] = { DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0), - DEFINE_PROP_END_OF_LIST(), }; static const TypeInfo ccid_bus_info = { @@ -1367,7 +1365,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), @@ -1379,7 +1377,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() @@ -1390,7 +1388,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), @@ -1404,7 +1402,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), @@ -1431,12 +1429,11 @@ static const VMStateDescription ccid_vmstate = { } }; -static Property ccid_properties[] = { +static const Property ccid_properties[] = { DEFINE_PROP_UINT8("debug", USBCCIDState, debug, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void ccid_class_initfn(ObjectClass *klass, void *data) +static void ccid_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); @@ -1461,13 +1458,13 @@ static const TypeInfo ccid_info = { .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBCCIDState), .class_init = ccid_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } }; -static void ccid_card_class_init(ObjectClass *klass, void *data) +static void ccid_card_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->bus_type = TYPE_CCID_BUS; diff --git a/hw/usb/dev-storage-bot.c b/hw/usb/dev-storage-bot.c index 1e5c5c711f5..df6ab7f656b 100644 --- a/hw/usb/dev-storage-bot.c +++ b/hw/usb/dev-storage-bot.c @@ -40,7 +40,7 @@ static void usb_msd_bot_realize(USBDevice *dev, Error **errp) usb_msd_handle_reset(dev); } -static void usb_msd_class_bot_initfn(ObjectClass *klass, void *data) +static void usb_msd_class_bot_initfn(ObjectClass *klass, const void *data) { USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-storage-classic.c b/hw/usb/dev-storage-classic.c index 84d19752b55..dabe1563593 100644 --- a/hw/usb/dev-storage-classic.c +++ b/hw/usb/dev-storage-classic.c @@ -13,8 +13,8 @@ #include "hw/usb.h" #include "hw/usb/desc.h" #include "hw/usb/msd.h" -#include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" +#include "system/system.h" +#include "system/block-backend.h" static const struct SCSIBusInfo usb_msd_scsi_info_storage = { .tcq = false, @@ -38,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 @@ -67,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; @@ -79,15 +67,14 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) s->scsi_dev = scsi_dev; } -static Property msd_properties[] = { +static const Property msd_properties[] = { DEFINE_BLOCK_PROPERTIES(MSDState, conf), DEFINE_BLOCK_ERROR_PROPERTIES(MSDState, conf), DEFINE_PROP_BOOL("removable", MSDState, removable, false), DEFINE_PROP_BOOL("commandlog", MSDState, commandlog, false), - DEFINE_PROP_END_OF_LIST(), }; -static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data) +static void usb_msd_class_storage_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index e3bcffb3e0d..b13fe345c45 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -177,7 +177,7 @@ static const USBDesc desc = { .str = desc_strings, }; -static void usb_msd_packet_complete(MSDState *s) +static void usb_msd_packet_complete(MSDState *s, int status) { USBPacket *p = s->packet; @@ -187,6 +187,7 @@ static void usb_msd_packet_complete(MSDState *s) * usb_packet_complete returns. */ trace_usb_msd_packet_complete(); + p->status = status; s->packet = NULL; usb_packet_complete(&s->dev, p); } @@ -196,8 +197,7 @@ static void usb_msd_fatal_error(MSDState *s) trace_usb_msd_fatal_error(); if (s->packet) { - s->packet->status = USB_RET_STALL; - usb_msd_packet_complete(s); + usb_msd_packet_complete(s, USB_RET_STALL); } /* @@ -255,8 +255,8 @@ void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) usb_msd_copy_data(s, p); p = s->packet; if (p && p->actual_length == p->iov.size) { - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - usb_msd_packet_complete(s); + /* USB_RET_SUCCESS status clears previous ASYNC status */ + usb_msd_packet_complete(s, USB_RET_SUCCESS); } } } @@ -295,8 +295,8 @@ void usb_msd_command_complete(SCSIRequest *req, size_t resid) s->mode = USB_MSDM_CSW; } } - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - usb_msd_packet_complete(s); + /* USB_RET_SUCCESS status clears previous ASYNC status */ + usb_msd_packet_complete(s, USB_RET_SUCCESS); } else if (s->data_len == 0) { s->mode = USB_MSDM_CSW; } @@ -332,8 +332,7 @@ void usb_msd_handle_reset(USBDevice *dev) assert(s->req == NULL); if (s->packet) { - s->packet->status = USB_RET_STALL; - usb_msd_packet_complete(s); + usb_msd_packet_complete(s, USB_RET_STALL); } memset(&s->csw, 0, sizeof(s->csw)); @@ -403,7 +402,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 +464,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 +525,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 +571,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), @@ -586,7 +585,7 @@ static const VMStateDescription vmstate_usb_msd = { } }; -static void usb_msd_class_initfn_common(ObjectClass *klass, void *data) +static void usb_msd_class_initfn_common(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index f013ded91eb..21cc2835c62 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -914,7 +914,6 @@ static void usb_uas_handle_data(USBDevice *dev, USBPacket *p) err_stream: error_report("%s: invalid stream %d", __func__, p->stream); p->status = USB_RET_STALL; - return; } static void usb_uas_unrealize(USBDevice *dev) @@ -947,18 +946,17 @@ 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() } }; -static Property uas_properties[] = { +static const Property uas_properties[] = { DEFINE_PROP_UINT32("log-scsi-req", UASDevice, requestlog, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void usb_uas_class_initfn(ObjectClass *klass, void *data) +static void usb_uas_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-wacom.c b/hw/usb/dev-wacom.c index 7177c17f031..f4b71a21472 100644 --- a/hw/usb/dev-wacom.c +++ b/hw/usb/dev-wacom.c @@ -420,7 +420,7 @@ static const VMStateDescription vmstate_usb_wacom = { .unmigratable = 1, }; -static void usb_wacom_class_init(ObjectClass *klass, void *data) +static void usb_wacom_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/hcd-dwc2.c b/hw/usb/hcd-dwc2.c index a0c4e782b2a..83864505bb8 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; @@ -1391,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), @@ -1411,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, @@ -1443,12 +1448,11 @@ const VMStateDescription vmstate_dwc2_state = { } }; -static Property dwc2_usb_properties[] = { +static const Property dwc2_usb_properties[] = { DEFINE_PROP_UINT32("usb_version", DWC2State, usb_version, 2), - DEFINE_PROP_END_OF_LIST(), }; -static void dwc2_class_init(ObjectClass *klass, void *data) +static void dwc2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); DWC2Class *c = DWC2_USB_CLASS(klass); diff --git a/hw/usb/hcd-dwc2.h b/hw/usb/hcd-dwc2.h index 9c3d88ea14b..2d5a5690fc2 100644 --- a/hw/usb/hcd-dwc2.h +++ b/hw/usb/hcd-dwc2.h @@ -23,7 +23,7 @@ #include "hw/irq.h" #include "hw/sysbus.h" #include "hw/usb.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qom/object.h" #define DWC2_MMIO_SIZE 0x11000 diff --git a/hw/usb/hcd-dwc3.c b/hw/usb/hcd-dwc3.c index 279263489e4..98a342b8b82 100644 --- a/hw/usb/hcd-dwc3.c +++ b/hw/usb/hcd-dwc3.c @@ -343,6 +343,8 @@ REG32(GFLADJ, 0x530) FIELD(GFLADJ, GFLADJ_REFCLK_FLADJ, 8, 14) FIELD(GFLADJ, GFLADJ_30MHZ_SDBND_SEL, 7, 1) FIELD(GFLADJ, GFLADJ_30MHZ, 0, 6) +REG32(GUSB2RHBCTL, 0x540) + FIELD(GUSB2RHBCTL, OVRD_L1TIMEOUT, 0, 4) #define DWC3_GLOBAL_OFFSET 0xC100 static void reset_csr(USBDWC3 * s) @@ -560,6 +562,9 @@ static const RegisterAccessInfo usb_dwc3_regs_info[] = { .rsvd = 0x40, .ro = 0x400040, .unimp = 0xffffffff, + },{ .name = "GUSB2RHBCTL", .addr = A_GUSB2RHBCTL, + .rsvd = 0xfffffff0, + .unimp = 0xffffffff, } }; @@ -648,7 +653,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), @@ -656,17 +661,16 @@ static const VMStateDescription vmstate_usb_dwc3 = { } }; -static Property usb_dwc3_properties[] = { +static const Property usb_dwc3_properties[] = { DEFINE_PROP_UINT32("DWC_USB3_USERID", USBDWC3, cfg.dwc_usb3_user, 0x12345678), - DEFINE_PROP_END_OF_LIST(), }; -static void usb_dwc3_class_init(ObjectClass *klass, void *data) +static void usb_dwc3_class_init(ObjectClass *klass, const 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 345444a5739..38ad3406b32 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -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; @@ -135,23 +135,22 @@ static void usb_ehci_pci_write_config(PCIDevice *dev, uint32_t addr, i->ehci.as = busmaster ? pci_get_address_space(dev) : &address_space_memory; } -static Property ehci_pci_properties[] = { +static const Property ehci_pci_properties[] = { DEFINE_PROP_UINT32("maxframes", EHCIPCIState, ehci.maxframes, 128), - DEFINE_PROP_END_OF_LIST(), }; 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() } }; -static void ehci_class_init(ObjectClass *klass, void *data) +static void ehci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -162,7 +161,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 = { @@ -173,17 +172,17 @@ static const TypeInfo ehci_pci_type_info = { .instance_finalize = usb_ehci_pci_finalize, .abstract = true, .class_init = ehci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -static void ehci_data_class_init(ObjectClass *klass, void *data) +static void ehci_data_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - EHCIPCIInfo *i = data; + const EHCIPCIInfo *i = data; k->vendor_id = i->vendor_id; k->device_id = i->device_id; @@ -228,7 +227,7 @@ static void ehci_pci_register_types(void) for (i = 0; i < ARRAY_SIZE(ehci_pci_info); i++) { ehci_type_info.name = ehci_pci_info[i].name; ehci_type_info.class_data = ehci_pci_info + i; - type_register(&ehci_type_info); + type_register_static(&ehci_type_info); } } diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index a12e2188489..0449f5fa6d6 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -19,23 +19,21 @@ #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() } }; -static Property ehci_sysbus_properties[] = { +static const Property ehci_sysbus_properties[] = { DEFINE_PROP_UINT32("maxframes", EHCISysBusState, ehci.maxframes, 128), DEFINE_PROP_BOOL("companion-enable", EHCISysBusState, ehci.companion_enable, false), - DEFINE_PROP_END_OF_LIST(), }; static void usb_ehci_sysbus_realize(DeviceState *dev, Error **errp) @@ -82,33 +80,22 @@ static void ehci_sysbus_finalize(Object *obj) usb_ehci_finalize(s); } -static void ehci_sysbus_class_init(ObjectClass *klass, void *data) +static void ehci_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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) +static void ehci_platform_class_init(ObjectClass *oc, const void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -118,13 +105,7 @@ 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) +static void ehci_exynos4210_class_init(ObjectClass *oc, const void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -134,13 +115,7 @@ 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) +static void ehci_aw_h3_class_init(ObjectClass *oc, const void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -150,13 +125,7 @@ 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) +static void ehci_npcm7xx_class_init(ObjectClass *oc, const void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -168,13 +137,7 @@ 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) +static void ehci_tegra2_class_init(ObjectClass *oc, const void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -184,12 +147,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); @@ -197,7 +154,7 @@ static void ehci_ppc4xx_init(Object *o) s->ehci.companion_enable = true; } -static void ehci_ppc4xx_class_init(ObjectClass *oc, void *data) +static void ehci_ppc4xx_class_init(ObjectClass *oc, const void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -207,13 +164,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 */ @@ -270,7 +220,7 @@ static void fusbh200_ehci_init(Object *obj) &f->mem_vendor); } -static void fusbh200_ehci_class_init(ObjectClass *oc, void *data) +static void fusbh200_ehci_class_init(ObjectClass *oc, const void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -282,24 +232,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 19b4534c20c..b090f253656 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -35,7 +35,7 @@ #include "trace.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #define FRAME_TIMER_FREQ 1000 #define FRAME_TIMER_NS (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ) @@ -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; @@ -2286,7 +2287,8 @@ static void ehci_work_bh(void *opaque) ehci_update_frindex(ehci, skipped_uframes); ehci->last_run_ns += UFRAME_TIMER_NS * skipped_uframes; uframes -= skipped_uframes; - DPRINTF("WARNING - EHCI skipped %d uframes\n", skipped_uframes); + DPRINTF("WARNING - EHCI skipped %"PRIu64" uframes\n", + skipped_uframes); } for (i = 0; i < uframes; i++) { @@ -2426,7 +2428,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,7 +2453,7 @@ 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); } @@ -2473,7 +2475,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 +2510,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) { diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 2cd821f49e4..ffd6c5108eb 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -20,7 +20,7 @@ #include "qemu/timer.h" #include "hw/usb.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/pci/pci_device.h" #include "hw/sysbus.h" @@ -37,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; @@ -288,7 +288,7 @@ struct EHCIState { uint32_t configflag; }; }; - uint32_t portsc[NB_PORTS]; + uint32_t portsc[EHCI_PORTS]; /* * Internal states, shadow registers, etc @@ -298,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 6dca373cb1f..00000000000 --- 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 6b630d35a7f..94d1077eb9e 100644 --- a/hw/usb/hcd-ohci-pci.c +++ b/hw/usb/hcd-ohci-pci.c @@ -109,25 +109,24 @@ static void usb_ohci_reset_pci(DeviceState *d) ohci_hard_reset(s); } -static Property ohci_pci_properties[] = { +static const Property ohci_pci_properties[] = { DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus), DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3), DEFINE_PROP_UINT32("firstport", OHCIPCIState, firstport, 0), - DEFINE_PROP_END_OF_LIST(), }; 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() } }; -static void ohci_pci_class_init(ObjectClass *klass, void *data) +static void ohci_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -142,7 +141,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 = { @@ -150,7 +149,7 @@ static const TypeInfo ohci_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(OHCIPCIState), .class_init = ohci_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/usb/hcd-ohci-sysbus.c b/hw/usb/hcd-ohci-sysbus.c new file mode 100644 index 00000000000..3fc6cce44b0 --- /dev/null +++ b/hw/usb/hcd-ohci-sysbus.c @@ -0,0 +1,87 @@ +/* + * 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 const 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), +}; + +static void ohci_sysbus_class_init(ObjectClass *klass, const 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 7ff1b65ced1..72a9f9f4749 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -577,7 +577,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) USBDevice *dev; USBEndpoint *ep; USBPacket *pkt; - uint8_t buf[8192]; + QEMU_UNINITIALIZED uint8_t buf[8192]; bool int_req; struct ohci_iso_td iso_td; uint32_t addr; @@ -927,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); @@ -936,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; } @@ -1955,36 +1960,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() }, @@ -2002,7 +1982,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() }, @@ -2012,7 +1992,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), @@ -2049,41 +2029,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 e1827227ac3..3cc35a5cdc5 100644 --- a/hw/usb/hcd-ohci.h +++ b/hw/usb/hcd-ohci.h @@ -22,7 +22,7 @@ #define HCD_OHCI_H #include "hw/sysbus.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/usb.h" #include "qom/object.h" diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 77baaa7a6b1..4822c704f69 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -36,7 +36,7 @@ #include "qapi/error.h" #include "qemu/timer.h" #include "qemu/iov.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "trace.h" #include "qemu/main-loop.h" #include "qemu/module.h" @@ -67,7 +67,7 @@ struct UHCIPCIDeviceClass { UHCIInfo info; }; -/* +/* * Pending async transaction. * 'packet' must be the first field because completion * handler does "(UHCIAsync *) pkt" cast. @@ -220,8 +220,9 @@ static void uhci_async_cancel(UHCIAsync *async) uhci_async_unlink(async); trace_usb_uhci_packet_cancel(async->queue->token, async->td_addr, async->done); - if (!async->done) + if (!async->done) { usb_cancel_packet(&async->packet); + } uhci_async_free(async); } @@ -322,7 +323,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) { @@ -339,7 +340,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() } @@ -361,10 +362,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), @@ -387,8 +388,8 @@ static void uhci_port_write(void *opaque, hwaddr addr, trace_usb_uhci_mmio_writew(addr, val); - switch(addr) { - case 0x00: + switch (addr) { + case UHCI_USBCMD: if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) { /* start frame processing */ trace_usb_uhci_schedule_start(); @@ -404,7 +405,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); } @@ -423,48 +424,53 @@ static void uhci_port_write(void *opaque, hwaddr addr, } } break; - case 0x02: + case UHCI_USBSTS: s->status &= ~val; - /* XXX: the chip spec is not coherent, so we add a hidden - register to distinguish between IOC and SPD */ - if (val & UHCI_STS_USBINT) + /* + * XXX: the chip spec is not coherent, so we add a hidden + * register to distinguish between IOC and SPD + */ + if (val & UHCI_STS_USBINT) { s->status2 = 0; + } uhci_update_irq(s); break; - case 0x04: + case UHCI_USBINTR: s->intr = val; uhci_update_irq(s); break; - case 0x06: - if (s->status & UHCI_STS_HCHALTED) + case UHCI_USBFRNUM: + if (s->status & UHCI_STS_HCHALTED) { s->frnum = val & 0x7ff; + } break; - case 0x08: + case UHCI_USBFLBASEADD: s->fl_base_addr &= 0xffff0000; s->fl_base_addr |= val & ~0xfff; break; - case 0x0a: + case UHCI_USBFLBASEADD + 2: s->fl_base_addr &= 0x0000ffff; s->fl_base_addr |= (val << 16); break; - case 0x0c: + case UHCI_USBSOF: s->sof_timing = val & 0xff; break; - case 0x10 ... 0x1f: + case UHCI_USBPORTSC1 ... UHCI_USBPORTSC4: { UHCIPort *port; USBDevice *dev; 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) { /* port reset */ - if ( (val & UHCI_PORT_RESET) && - !(port->ctrl & UHCI_PORT_RESET) ) { + if ((val & UHCI_PORT_RESET) && + !(port->ctrl & UHCI_PORT_RESET)) { usb_device_reset(dev); } } @@ -486,35 +492,36 @@ static uint64_t uhci_port_read(void *opaque, hwaddr addr, unsigned size) UHCIState *s = opaque; uint32_t val; - switch(addr) { - case 0x00: + switch (addr) { + case UHCI_USBCMD: val = s->cmd; break; - case 0x02: + case UHCI_USBSTS: val = s->status; break; - case 0x04: + case UHCI_USBINTR: val = s->intr; break; - case 0x06: + case UHCI_USBFRNUM: val = s->frnum; break; - case 0x08: + case UHCI_USBFLBASEADD: val = s->fl_base_addr & 0xffff; break; - case 0x0a: + case UHCI_USBFLBASEADD + 2: val = (s->fl_base_addr >> 16) & 0xffff; break; - case 0x0c: + case UHCI_USBSOF: val = s->sof_timing; break; - case 0x10 ... 0x1f: + case UHCI_USBPORTSC1 ... UHCI_USBPORTSC4: { 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; } @@ -531,12 +538,13 @@ static uint64_t uhci_port_read(void *opaque, hwaddr addr, unsigned size) } /* signal resume if controller suspended */ -static void uhci_resume (void *opaque) +static void uhci_resume(void *opaque) { UHCIState *s = (UHCIState *)opaque; - if (!s) + if (!s) { return; + } if (s->cmd & UHCI_CMD_EGSM) { s->cmd |= UHCI_CMD_FGR; @@ -607,7 +615,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; @@ -672,7 +680,8 @@ static int uhci_handle_td_error(UHCIState *s, UHCI_TD *td, uint32_t td_addr, return ret; } -static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask) +static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, + uint32_t *int_mask) { int len = 0, max_len; uint8_t pid; @@ -680,8 +689,9 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; - if (td->ctrl & TD_CTRL_IOS) + if (td->ctrl & TD_CTRL_IOS) { td->ctrl &= ~TD_CTRL_ACTIVE; + } if (async->packet.status != USB_RET_SUCCESS) { return uhci_handle_td_error(s, td, async->td_addr, @@ -691,12 +701,15 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ len = async->packet.actual_length; td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); - /* The NAK bit may have been set by a previous frame, so clear it - here. The docs are somewhat unclear, but win2k relies on this - behavior. */ + /* + * The NAK bit may have been set by a previous frame, so clear it + * here. The docs are somewhat unclear, but win2k relies on this + * behavior. + */ td->ctrl &= ~(TD_CTRL_ACTIVE | TD_CTRL_NAK); - if (td->ctrl & TD_CTRL_IOC) + if (td->ctrl & TD_CTRL_IOC) { *int_mask |= 0x01; + } if (pid == USB_TOKEN_IN) { pci_dma_write(&s->dev, td->buffer, async->buf, len); @@ -778,9 +791,11 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, if (async) { if (queuing) { - /* we are busy filling the queue, we are not prepared - to consume completed packages then, just leave them - in async state */ + /* + * we are busy filling the queue, we are not prepared + * to consume completed packages then, just leave them + * in async state + */ return TD_RESULT_ASYNC_CONT; } if (!async->done) { @@ -830,7 +845,7 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, } usb_packet_addbuf(&async->packet, async->buf, max_len); - switch(pid) { + switch (pid) { case USB_TOKEN_OUT: case USB_TOKEN_SETUP: pci_dma_read(&s->dev, td->buffer, async->buf, max_len); @@ -909,12 +924,15 @@ static void qhdb_reset(QhDb *db) static int qhdb_insert(QhDb *db, uint32_t addr) { int i; - for (i = 0; i < db->count; i++) - if (db->addr[i] == addr) + for (i = 0; i < db->count; i++) { + if (db->addr[i] == addr) { return 1; + } + } - if (db->count >= UHCI_MAX_QUEUES) + if (db->count >= UHCI_MAX_QUEUES) { return 1; + } db->addr[db->count++] = addr; return 0; @@ -968,8 +986,10 @@ static void uhci_process_frame(UHCIState *s) for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) { if (!s->completions_only && s->frame_bytes >= s->frame_bandwidth) { - /* We've reached the usb 1.1 bandwidth, which is - 1280 bytes/frame, stop processing */ + /* + * We've reached the usb 1.1 bandwidth, which is + * 1280 bytes/frame, stop processing + */ trace_usb_uhci_frame_stop_bandwidth(); break; } @@ -1118,8 +1138,10 @@ static void uhci_frame_timer(void *opaque) uhci_async_validate_begin(s); uhci_process_frame(s); uhci_async_validate_end(s); - /* The spec says frnum is the frame currently being processed, and - * the guest must look at frnum - 1 on interrupt, so inc frnum now */ + /* + * The spec says frnum is the frame currently being processed, and + * the guest must look at frnum - 1 on interrupt, so inc frnum now + */ s->frnum = (s->frnum + 1) & 0x7ff; s->expire_time += frame_t; } @@ -1171,11 +1193,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); @@ -1185,21 +1207,23 @@ 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_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, "uhci", 0x20); - /* Use region 4 for consistency with real hardware. BSD guests seem - to rely on this. */ + /* + * Use region 4 for consistency with real hardware. BSD guests seem + * to rely on this. + */ pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar); } @@ -1225,27 +1249,25 @@ static void usb_uhci_exit(PCIDevice *dev) } } -static Property uhci_properties_companion[] = { +static const Property uhci_properties_companion[] = { DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), DEFINE_PROP_UINT32("bandwidth", UHCIState, frame_bandwidth, 1280), DEFINE_PROP_UINT32("maxframes", UHCIState, maxframes, 128), - DEFINE_PROP_END_OF_LIST(), }; -static Property uhci_properties_standalone[] = { +static const Property uhci_properties_standalone[] = { DEFINE_PROP_UINT32("bandwidth", UHCIState, frame_bandwidth, 1280), DEFINE_PROP_UINT32("maxframes", UHCIState, maxframes, 128), - DEFINE_PROP_END_OF_LIST(), }; -static void uhci_class_init(ObjectClass *klass, void *data) +static void uhci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 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); } @@ -1256,18 +1278,18 @@ static const TypeInfo uhci_pci_type_info = { .class_size = sizeof(UHCIPCIDeviceClass), .abstract = true, .class_init = uhci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }; -void uhci_data_class_init(ObjectClass *klass, void *data) +void uhci_data_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); UHCIPCIDeviceClass *u = UHCI_CLASS(klass); - UHCIInfo *info = data; + const UHCIInfo *info = data; k->realize = info->realize ? info->realize : usb_uhci_common_realize; k->exit = info->unplug ? usb_uhci_exit : NULL; @@ -1360,7 +1382,7 @@ static void uhci_register_types(void) for (i = 0; i < ARRAY_SIZE(uhci_info); i++) { uhci_type_info.name = uhci_info[i].name; uhci_type_info.class_data = uhci_info + i; - type_register(&uhci_type_info); + type_register_static(&uhci_type_info); } } diff --git a/hw/usb/hcd-uhci.h b/hw/usb/hcd-uhci.h index 69f8b40c49c..e0a6525505c 100644 --- a/hw/usb/hcd-uhci.h +++ b/hw/usb/hcd-uhci.h @@ -28,14 +28,14 @@ #ifndef HW_USB_HCD_UHCI_H #define HW_USB_HCD_UHCI_H -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/timer.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; @@ -88,7 +88,7 @@ typedef struct UHCIInfo { bool notuser; /* disallow user_creatable */ } UHCIInfo; -void uhci_data_class_init(ObjectClass *klass, void *data); +void uhci_data_class_init(ObjectClass *klass, const void *data); void usb_uhci_common_realize(PCIDevice *dev, Error **errp); #define TYPE_PIIX3_USB_UHCI "piix3-usb-uhci" diff --git a/hw/usb/hcd-xhci-nec.c b/hw/usb/hcd-xhci-nec.c index 328e5bfe7c8..9e0fea26f40 100644 --- a/hw/usb/hcd-xhci-nec.c +++ b/hw/usb/hcd-xhci-nec.c @@ -30,24 +30,15 @@ OBJECT_DECLARE_SIMPLE_TYPE(XHCINecState, NEC_XHCI) struct XHCINecState { - /*< private >*/ XHCIPciState parent_obj; - /*< public >*/ - uint32_t flags; + uint32_t intrs; uint32_t slots; }; -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), +static const Property nec_xhci_properties[] = { DEFINE_PROP_UINT32("intrs", XHCINecState, intrs, XHCI_MAXINTRS), DEFINE_PROP_UINT32("slots", XHCINecState, slots, XHCI_MAXSLOTS), - DEFINE_PROP_END_OF_LIST(), }; static void nec_xhci_instance_init(Object *obj) @@ -55,12 +46,11 @@ static void nec_xhci_instance_init(Object *obj) XHCIPciState *pci = XHCI_PCI(obj); XHCINecState *nec = NEC_XHCI(obj); - pci->xhci.flags = nec->flags; pci->xhci.numintrs = nec->intrs; pci->xhci.numslots = nec->slots; } -static void nec_xhci_class_init(ObjectClass *klass, void *data) +static void nec_xhci_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c index 643d4643e4d..b93c80b09d8 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -74,6 +74,7 @@ static bool xhci_pci_intr_raise(XHCIState *xhci, int n, bool level) } if (msi_enabled(pci_dev) && level) { + n %= msi_nr_vectors_allocated(pci_dev); msi_notify(pci_dev, n); return true; } @@ -81,6 +82,21 @@ static bool xhci_pci_intr_raise(XHCIState *xhci, int n, bool level) return false; } +static bool xhci_pci_intr_mapping_conditional(XHCIState *xhci) +{ + XHCIPciState *s = container_of(xhci, XHCIPciState, xhci); + PCIDevice *pci_dev = PCI_DEVICE(s); + + /* + * Implementation of the "conditional-intr-mapping" property, which only + * enables interrupter mapping if MSI or MSI-X is available and active. + * Forces all events onto interrupter/event ring 0 in pin-based IRQ mode. + * Provides compatibility with macOS guests on machine types where MSI(-X) + * is not available. + */ + return msix_enabled(pci_dev) || msi_enabled(pci_dev); +} + static void xhci_pci_reset(DeviceState *dev) { XHCIPciState *s = XHCI_PCI(dev); @@ -94,7 +110,7 @@ static int xhci_pci_vmstate_post_load(void *opaque, int version_id) PCIDevice *pci_dev = PCI_DEVICE(s); int intr; - for (intr = 0; intr < s->xhci.numintrs; intr++) { + for (intr = 0; intr < s->xhci.numintrs; intr++) { if (s->xhci.intr[intr].msix_used) { msix_vector_use(pci_dev, intr); } else { @@ -118,6 +134,9 @@ static void usb_xhci_pci_realize(struct PCIDevice *dev, Error **errp) object_property_set_link(OBJECT(&s->xhci), "host", OBJECT(s), NULL); s->xhci.intr_update = xhci_pci_intr_update; s->xhci.intr_raise = xhci_pci_intr_raise; + if (s->conditional_intr_mapping) { + s->xhci.intr_mapping_supported = xhci_pci_intr_mapping_conditional; + } if (!qdev_realize(DEVICE(&s->xhci), NULL, errp)) { return; } @@ -148,8 +167,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 +196,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), @@ -198,17 +216,29 @@ static void xhci_instance_init(Object *obj) qdev_alias_all_properties(DEVICE(&s->xhci), obj); } -static void xhci_class_init(ObjectClass *klass, void *data) +static const Property xhci_pci_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_BOOL("conditional-intr-mapping", XHCIPciState, + conditional_intr_mapping, false), +}; + +static void xhci_class_init(ObjectClass *klass, const 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; k->exit = usb_xhci_pci_exit; k->class_id = PCI_CLASS_SERIAL_USB; + device_class_set_props(dc, xhci_pci_properties); + object_class_property_set_description(klass, "conditional-intr-mapping", + "When true, disables interrupter mapping for pin-based IRQ mode. " + "Intended to be used with guest drivers with questionable behaviour, " + "such as macOS's."); } static const TypeInfo xhci_pci_info = { @@ -218,14 +248,14 @@ static const TypeInfo xhci_pci_info = { .class_init = xhci_class_init, .instance_init = xhci_instance_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } }, }; -static void qemu_xhci_class_init(ObjectClass *klass, void *data) +static void qemu_xhci_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -243,7 +273,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 08f70ce97cc..5b61ae84555 100644 --- a/hw/usb/hcd-xhci-pci.h +++ b/hw/usb/hcd-xhci-pci.h @@ -40,6 +40,7 @@ typedef struct XHCIPciState { XHCIState xhci; OnOffAuto msi; OnOffAuto msix; + bool conditional_intr_mapping; } XHCIPciState; #endif diff --git a/hw/usb/hcd-xhci-sysbus.c b/hw/usb/hcd-xhci-sysbus.c index faf57b47975..244698e5f2b 100644 --- a/hw/usb/hcd-xhci-sysbus.c +++ b/hw/usb/hcd-xhci-sysbus.c @@ -82,26 +82,25 @@ void xhci_sysbus_build_aml(Aml *scope, uint32_t mmio, unsigned int irq) aml_append(scope, dev); } -static Property xhci_sysbus_props[] = { +static const Property xhci_sysbus_props[] = { DEFINE_PROP_UINT32("intrs", XHCISysbusState, xhci.numintrs, XHCI_MAXINTRS), DEFINE_PROP_UINT32("slots", XHCISysbusState, xhci.numslots, XHCI_MAXSLOTS), - DEFINE_PROP_END_OF_LIST(), }; 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() } }; -static void xhci_sysbus_class_init(ObjectClass *klass, void *data) +static void xhci_sysbus_class_init(ObjectClass *klass, const 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 324177ad5df..292c378bfc9 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; @@ -652,6 +644,11 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v) dma_addr_t erdp; unsigned int dp_idx; + if (xhci->numintrs == 1 || + (xhci->intr_mapping_supported && !xhci->intr_mapping_supported(xhci))) { + v = 0; + } + if (v >= xhci->numintrs) { DPRINTF("intr nr out of range (%d >= %d)\n", v, xhci->numintrs); return; @@ -1190,6 +1187,12 @@ static void xhci_ep_free_xfer(XHCITransfer *xfer) g_free(xfer); } +static void xhci_xfer_unmap(XHCITransfer *xfer) +{ + usb_packet_unmap(&xfer->packet, &xfer->sgl); + qemu_sglist_destroy(&xfer->sgl); +} + static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report) { int killed = 0; @@ -1201,6 +1204,7 @@ static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report) if (t->running_async) { usb_cancel_packet(&t->packet); + xhci_xfer_unmap(t); t->running_async = 0; killed = 1; } @@ -1483,12 +1487,6 @@ static int xhci_xfer_create_sgl(XHCITransfer *xfer, int in_xfer) return -1; } -static void xhci_xfer_unmap(XHCITransfer *xfer) -{ - usb_packet_unmap(&xfer->packet, &xfer->sgl); - qemu_sglist_destroy(&xfer->sgl); -} - static void xhci_xfer_report(XHCITransfer *xfer) { uint32_t edtla = 0; @@ -1894,7 +1892,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); @@ -2779,11 +2777,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 */ @@ -2795,11 +2789,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 */ @@ -2826,9 +2816,15 @@ static uint64_t xhci_port_read(void *ptr, hwaddr reg, unsigned size) case 0x08: /* PORTLI */ ret = 0; break; - case 0x0c: /* reserved */ + case 0x0c: /* PORTHLPMC */ + ret = 0; + qemu_log_mask(LOG_UNIMP, "%s: read from port register PORTHLPMC", + __func__); + break; default: - trace_usb_xhci_unimplemented("port read", reg); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: read from port offset 0x%" HWADDR_PRIx, + __func__, reg); ret = 0; } @@ -2897,9 +2893,22 @@ static void xhci_port_write(void *ptr, hwaddr reg, } break; case 0x04: /* PORTPMSC */ + case 0x0c: /* PORTHLPMC */ + qemu_log_mask(LOG_UNIMP, + "%s: write 0x%" PRIx64 + " (%u bytes) to port register at offset 0x%" HWADDR_PRIx, + __func__, val, size, reg); + break; case 0x08: /* PORTLI */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Write to read-only PORTLI register", + __func__); + break; default: - trace_usb_xhci_unimplemented("port write", reg); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write 0x%" PRIx64 " (%u bytes) to unknown port " + "register at offset 0x%" HWADDR_PRIx, + __func__, val, size, reg); + break; } } @@ -3349,13 +3358,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 | @@ -3366,13 +3370,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); @@ -3522,7 +3521,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() @@ -3532,7 +3531,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() } @@ -3541,7 +3540,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() @@ -3551,7 +3550,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), @@ -3571,7 +3570,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), @@ -3604,7 +3603,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, @@ -3631,23 +3630,22 @@ const VMStateDescription vmstate_xhci = { } }; -static Property xhci_properties[] = { +static const Property xhci_properties[] = { DEFINE_PROP_BIT("streams", XHCIState, flags, XHCI_FLAG_ENABLE_STREAMS, true), DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4), DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4), DEFINE_PROP_LINK("host", XHCIState, hostOpaque, TYPE_DEVICE, DeviceState *), - DEFINE_PROP_END_OF_LIST(), }; -static void xhci_class_init(ObjectClass *klass, void *data) +static void xhci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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 98f598382ad..9c3974f1489 100644 --- a/hw/usb/hcd-xhci.h +++ b/hw/usb/hcd-xhci.h @@ -25,7 +25,7 @@ #include "hw/usb.h" #include "hw/usb/xhci.h" -#include "sysemu/dma.h" +#include "system/dma.h" OBJECT_DECLARE_SIMPLE_TYPE(XHCIState, XHCI) @@ -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 { @@ -195,6 +193,11 @@ typedef struct XHCIState { uint32_t max_pstreams_mask; void (*intr_update)(XHCIState *s, int n, bool enable); bool (*intr_raise)(XHCIState *s, int n, bool level); + /* + * Callback for special-casing interrupter mapping support. NULL for most + * implementations, for defaulting to enabled mapping unless numintrs == 1. + */ + bool (*intr_mapping_supported)(XHCIState *s); DeviceState *hostOpaque; /* Operational Registers */ diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index f500db85ab4..b74670ae256 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -51,8 +51,8 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.h" #include "trace.h" #include "hw/qdev-properties.h" @@ -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; @@ -1212,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); @@ -1753,13 +1752,13 @@ 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() } }; -static Property usb_host_dev_properties[] = { +static const Property usb_host_dev_properties[] = { DEFINE_PROP_UINT32("hostbus", USBHostDevice, match.bus_num, 0), DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr, 0), DEFINE_PROP_STRING("hostport", USBHostDevice, match.port), @@ -1780,10 +1779,9 @@ static Property usb_host_dev_properties[] = { USB_HOST_OPT_PIPELINE, true), DEFINE_PROP_BOOL("suppress-remote-wake", USBHostDevice, suppress_remote_wake, true), - DEFINE_PROP_END_OF_LIST(), }; -static void usb_host_class_initfn(ObjectClass *klass, void *data) +static void usb_host_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/host.h b/hw/usb/host.h deleted file mode 100644 index 048ff3b482a..00000000000 --- 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 1a97b36a119..c25566d0fae 100644 --- a/hw/usb/imx-usb-phy.c +++ b/hw/usb/imx-usb-phy.c @@ -20,7 +20,7 @@ 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() }, @@ -214,11 +214,11 @@ static void imx_usbphy_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); } -static void imx_usbphy_class_init(ObjectClass *klass, void *data) +static void imx_usbphy_class_init(ObjectClass *klass, const 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/libhw.c b/hw/usb/libhw.c index f350eae443d..4f03ef4ba9a 100644 --- a/hw/usb/libhw.c +++ b/hw/usb/libhw.c @@ -21,7 +21,7 @@ */ #include "qemu/osdep.h" #include "hw/usb.h" -#include "sysemu/dma.h" +#include "system/dma.h" int usb_packet_map(USBPacket *p, QEMUSGList *sgl) { diff --git a/hw/usb/meson.build b/hw/usb/meson.build index e94149ebdeb..17360a5b5a4 100644 --- a/hw/usb/meson.build +++ b/hw/usb/meson.build @@ -9,33 +9,32 @@ system_ss.add(when: 'CONFIG_USB', if_true: files( 'desc-msos.c', 'libhw.c', 'pcap.c', -)) +), if_false: files('bus-stub.c')) # usb host adapters 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.c', 'hcd-ehci-sysbus.c')) +system_ss.add(when: 'CONFIG_USB_EHCI_SYSBUS', if_true: files('hcd-ehci-sysbus.c')) system_ss.add(when: 'CONFIG_USB_XHCI', if_true: files('hcd-xhci.c')) system_ss.add(when: 'CONFIG_USB_XHCI_PCI', if_true: files('hcd-xhci-pci.c')) system_ss.add(when: 'CONFIG_USB_XHCI_SYSBUS', if_true: files('hcd-xhci-sysbus.c')) system_ss.add(when: 'CONFIG_USB_XHCI_NEC', if_true: files('hcd-xhci-nec.c')) -system_ss.add(when: 'CONFIG_USB_MUSB', if_true: files('hcd-musb.c')) system_ss.add(when: 'CONFIG_USB_DWC2', if_true: files('hcd-dwc2.c')) system_ss.add(when: 'CONFIG_USB_DWC3', if_true: files('hcd-dwc3.c')) +system_ss.add(when: 'CONFIG_USB_CHIPIDEA', if_true: files('chipidea.c')) -system_ss.add(when: 'CONFIG_TUSB6010', if_true: files('tusb6010.c')) -system_ss.add(when: 'CONFIG_IMX', if_true: files('chipidea.c')) system_ss.add(when: 'CONFIG_IMX_USBPHY', if_true: files('imx-usb-phy.c')) system_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686-uhci-pci.c')) system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-usb2-ctrl-regs.c')) system_ss.add(when: 'CONFIG_XLNX_USB_SUBSYS', if_true: files('xlnx-usb-subsystem.c')) # emulated usb devices -system_ss.add(when: 'CONFIG_USB', if_true: files('dev-hub.c')) -system_ss.add(when: 'CONFIG_USB', if_true: files('dev-hid.c')) +system_ss.add(when: 'CONFIG_USB_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')) @@ -44,7 +43,9 @@ 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')) -system_ss.add(when: ['CONFIG_POSIX', 'CONFIG_USB_STORAGE_MTP'], if_true: files('dev-mtp.c')) +if host_os != 'windows' + system_ss.add(when: 'CONFIG_USB_STORAGE_MTP', if_true: files('dev-mtp.c')) +endif # smartcard system_ss.add(when: 'CONFIG_USB_SMARTCARD', if_true: files('dev-smartcard-reader.c')) @@ -58,7 +59,9 @@ endif # U2F system_ss.add(when: 'CONFIG_USB_U2F', if_true: files('u2f.c')) -system_ss.add(when: ['CONFIG_LINUX', 'CONFIG_USB_U2F'], if_true: [libudev, files('u2f-passthru.c')]) +if host_os == 'linux' + system_ss.add(when: 'CONFIG_USB_U2F', if_true: [libudev, files('u2f-passthru.c')]) +endif if u2f.found() system_ss.add(when: 'CONFIG_USB_U2F', if_true: [u2f, files('u2f-emulated.c')]) endif diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index c9893df8677..f516ff42a11 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -30,8 +30,8 @@ #include "qemu/units.h" #include "qapi/error.h" #include "qemu/timer.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qemu/iov.h" @@ -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 */ } } @@ -2373,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() } @@ -2391,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() } @@ -2401,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), @@ -2424,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 @@ -2481,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, @@ -2501,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), @@ -2520,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, @@ -2543,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), { @@ -2573,17 +2573,16 @@ static const VMStateDescription usbredir_vmstate = { } }; -static Property usbredir_properties[] = { +static const Property usbredir_properties[] = { DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, usbredirparser_warning), DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str), DEFINE_PROP_BOOL("streams", USBRedirDevice, enable_streams, true), DEFINE_PROP_BOOL("suppress-remote-wake", USBRedirDevice, suppress_remote_wake, true), - DEFINE_PROP_END_OF_LIST(), }; -static void usbredir_class_initfn(ObjectClass *klass, void *data) +static void usbredir_class_initfn(ObjectClass *klass, const void *data) { USBDeviceClass *uc = USB_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/usb/trace-events b/hw/usb/trace-events index ed7dc210d3f..dd04f14add1 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,7 +56,7 @@ 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" diff --git a/hw/usb/tusb6010.c b/hw/usb/tusb6010.c deleted file mode 100644 index 1dd4071e683..00000000000 --- 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-emulated.c b/hw/usb/u2f-emulated.c index 63cceaa5fc8..ace5eceadd9 100644 --- a/hw/usb/u2f-emulated.c +++ b/hw/usb/u2f-emulated.c @@ -369,16 +369,15 @@ static void u2f_emulated_unrealize(U2FKeyState *base) } } -static Property u2f_emulated_properties[] = { +static const Property u2f_emulated_properties[] = { DEFINE_PROP_STRING("dir", U2FEmulatedState, dir), DEFINE_PROP_STRING("cert", U2FEmulatedState, cert), DEFINE_PROP_STRING("privkey", U2FEmulatedState, privkey), DEFINE_PROP_STRING("entropy", U2FEmulatedState, entropy), DEFINE_PROP_STRING("counter", U2FEmulatedState, counter), - DEFINE_PROP_END_OF_LIST(), }; -static void u2f_emulated_class_init(ObjectClass *klass, void *data) +static void u2f_emulated_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); U2FKeyClass *kc = U2F_KEY_CLASS(klass); diff --git a/hw/usb/u2f-passthru.c b/hw/usb/u2f-passthru.c index fc93429c9c0..fa8d9cdda89 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,18 +510,17 @@ 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() } }; -static Property u2f_passthru_properties[] = { +static const Property u2f_passthru_properties[] = { DEFINE_PROP_STRING("hidraw", U2FPassthruState, hidraw), - DEFINE_PROP_END_OF_LIST(), }; -static void u2f_passthru_class_init(ObjectClass *klass, void *data) +static void u2f_passthru_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); U2FKeyClass *kc = U2F_KEY_CLASS(klass); diff --git a/hw/usb/u2f.c b/hw/usb/u2f.c index 56001249a44..b051a999d3a 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, @@ -317,7 +317,7 @@ const VMStateDescription vmstate_u2f_key = { } }; -static void u2f_key_class_init(ObjectClass *klass, void *data) +static void u2f_key_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/vt82c686-uhci-pci.c b/hw/usb/vt82c686-uhci-pci.c index b4884c9011c..61628061722 100644 --- a/hw/usb/vt82c686-uhci-pci.c +++ b/hw/usb/vt82c686-uhci-pci.c @@ -1,7 +1,14 @@ #include "qemu/osdep.h" +#include "hw/irq.h" #include "hw/isa/vt82c686.h" #include "hcd-uhci.h" +static void uhci_isa_set_irq(void *opaque, int irq_num, int level) +{ + UHCIState *s = opaque; + via_isa_set_irq(&s->dev, 0, level); +} + static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp) { UHCIState *s = UHCI(dev); @@ -15,6 +22,8 @@ static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp) pci_set_long(pci_conf + 0xc0, 0x00002000); usb_uhci_common_realize(dev, errp); + object_unref(s->irq); + s->irq = qemu_allocate_irq(uhci_isa_set_irq, s, 0); } static UHCIInfo uhci_info[] = { diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index 09ec326aeae..fa46a7da01c 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -30,8 +30,8 @@ #include "hw/xen/xen-legacy-backend.h" #include "monitor/qdev.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "hw/xen/interface/io/usbif.h" @@ -755,10 +755,10 @@ static void usbback_portid_add(struct usbback_info *usbif, unsigned port, qdict = qdict_new(); qdict_put_str(qdict, "driver", "usb-host"); - tmp = g_strdup_printf("%s.0", usbif->xendev.qdev.id); + tmp = g_strdup_printf("%s.0", DEVICE(&usbif->xendev)->id); qdict_put_str(qdict, "bus", tmp); g_free(tmp); - tmp = g_strdup_printf("%s-%u", usbif->xendev.qdev.id, port); + tmp = g_strdup_printf("%s-%u", DEVICE(&usbif->xendev)->id, port); qdict_put_str(qdict, "id", tmp); g_free(tmp); qdict_put_int(qdict, "port", port); @@ -1022,7 +1022,7 @@ static void usbback_alloc(struct XenLegacyDevice *xendev) usbif = container_of(xendev, struct usbback_info, xendev); usb_bus_new(&usbif->bus, sizeof(usbif->bus), &xen_usb_bus_ops, - DEVICE(&xendev->qdev)); + DEVICE(xendev)); for (i = 0; i < USBBACK_MAXPORTS; i++) { p = &(usbif->ports[i].port); usb_register_port(&usbif->bus, p, usbif, i, &xen_usb_port_ops, @@ -1083,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, @@ -1095,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-usb-subsystem.c b/hw/usb/xlnx-usb-subsystem.c index d8deeb6ced5..98967ef49f1 100644 --- a/hw/usb/xlnx-usb-subsystem.c +++ b/hw/usb/xlnx-usb-subsystem.c @@ -69,7 +69,7 @@ static void versal_usb2_init(Object *obj) object_property_add_alias(obj, "dma", OBJECT(&s->dwc3.sysbus_xhci), "dma"); } -static void versal_usb2_class_init(ObjectClass *klass, void *data) +static void versal_usb2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/usb/xlnx-versal-usb2-ctrl-regs.c b/hw/usb/xlnx-versal-usb2-ctrl-regs.c index 1c094aa1a63..4114672d4f4 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,13 +196,13 @@ 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(), } }; -static void usb2_ctrl_regs_class_init(ObjectClass *klass, void *data) +static void usb2_ctrl_regs_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/vfio-user/Kconfig b/hw/vfio-user/Kconfig new file mode 100644 index 00000000000..24bdf7af903 --- /dev/null +++ b/hw/vfio-user/Kconfig @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config VFIO_USER + bool + default y + depends on VFIO_PCI + diff --git a/hw/vfio-user/container.c b/hw/vfio-user/container.c new file mode 100644 index 00000000000..3133fef1773 --- /dev/null +++ b/hw/vfio-user/container.c @@ -0,0 +1,370 @@ +/* + * Container for vfio-user IOMMU type: rather than communicating with the kernel + * vfio driver, we communicate over a socket to a server using the vfio-user + * protocol. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include "qemu/osdep.h" + +#include "hw/vfio-user/container.h" +#include "hw/vfio-user/device.h" +#include "hw/vfio-user/trace.h" +#include "hw/vfio/vfio-cpr.h" +#include "hw/vfio/vfio-device.h" +#include "hw/vfio/vfio-listener.h" +#include "qapi/error.h" + +/* + * When DMA space is the physical address space, the region add/del listeners + * will fire during memory update transactions. These depend on BQL being held, + * so do any resulting map/demap ops async while keeping BQL. + */ +static void vfio_user_listener_begin(VFIOContainerBase *bcontainer) +{ + VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, + bcontainer); + + container->proxy->async_ops = true; +} + +static void vfio_user_listener_commit(VFIOContainerBase *bcontainer) +{ + VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, + bcontainer); + + /* wait here for any async requests sent during the transaction */ + container->proxy->async_ops = false; + vfio_user_wait_reqs(container->proxy); +} + +static int vfio_user_dma_unmap(const VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + IOMMUTLBEntry *iotlb, bool unmap_all) +{ + VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, + bcontainer); + Error *local_err = NULL; + int ret = 0; + + VFIOUserDMAUnmap *msgp = g_malloc(sizeof(*msgp)); + + vfio_user_request_msg(&msgp->hdr, VFIO_USER_DMA_UNMAP, sizeof(*msgp), 0); + msgp->argsz = sizeof(struct vfio_iommu_type1_dma_unmap); + msgp->flags = unmap_all ? VFIO_DMA_UNMAP_FLAG_ALL : 0; + msgp->iova = iova; + msgp->size = size; + trace_vfio_user_dma_unmap(msgp->iova, msgp->size, msgp->flags, + container->proxy->async_ops); + + if (container->proxy->async_ops) { + if (!vfio_user_send_nowait(container->proxy, &msgp->hdr, NULL, + 0, &local_err)) { + error_report_err(local_err); + ret = -EFAULT; + } else { + ret = 0; + } + } else { + if (!vfio_user_send_wait(container->proxy, &msgp->hdr, NULL, + 0, &local_err)) { + error_report_err(local_err); + ret = -EFAULT; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + ret = -msgp->hdr.error_reply; + } + + g_free(msgp); + } + + return ret; +} + +static int vfio_user_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, + ram_addr_t size, void *vaddr, bool readonly, + MemoryRegion *mrp) +{ + VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, + bcontainer); + int fd = memory_region_get_fd(mrp); + Error *local_err = NULL; + int ret; + + VFIOUserFDs *fds = NULL; + VFIOUserDMAMap *msgp = g_malloc0(sizeof(*msgp)); + + vfio_user_request_msg(&msgp->hdr, VFIO_USER_DMA_MAP, sizeof(*msgp), 0); + msgp->argsz = sizeof(struct vfio_iommu_type1_dma_map); + msgp->flags = VFIO_DMA_MAP_FLAG_READ; + msgp->offset = 0; + msgp->iova = iova; + msgp->size = size; + + /* + * vaddr enters as a QEMU process address; make it either a file offset + * for mapped areas or leave as 0. + */ + if (fd != -1) { + msgp->offset = qemu_ram_block_host_offset(mrp->ram_block, vaddr); + } + + if (!readonly) { + msgp->flags |= VFIO_DMA_MAP_FLAG_WRITE; + } + + trace_vfio_user_dma_map(msgp->iova, msgp->size, msgp->offset, msgp->flags, + container->proxy->async_ops); + + /* + * The async_ops case sends without blocking. They're later waited for in + * vfio_send_wait_reqs. + */ + if (container->proxy->async_ops) { + /* can't use auto variable since we don't block */ + if (fd != -1) { + fds = vfio_user_getfds(1); + fds->send_fds = 1; + fds->fds[0] = fd; + } + + if (!vfio_user_send_nowait(container->proxy, &msgp->hdr, fds, + 0, &local_err)) { + error_report_err(local_err); + ret = -EFAULT; + } else { + ret = 0; + } + } else { + VFIOUserFDs local_fds = { 1, 0, &fd }; + + fds = fd != -1 ? &local_fds : NULL; + + if (!vfio_user_send_wait(container->proxy, &msgp->hdr, fds, + 0, &local_err)) { + error_report_err(local_err); + ret = -EFAULT; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + ret = -msgp->hdr.error_reply; + } + + g_free(msgp); + } + + return ret; +} + +static int +vfio_user_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, + bool start, Error **errp) +{ + error_setg_errno(errp, ENOTSUP, "Not supported"); + return -ENOTSUP; +} + +static int vfio_user_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, hwaddr iova, + hwaddr size, Error **errp) +{ + error_setg_errno(errp, ENOTSUP, "Not supported"); + return -ENOTSUP; +} + +static bool vfio_user_setup(VFIOContainerBase *bcontainer, Error **errp) +{ + VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, + bcontainer); + + assert(container->proxy->dma_pgsizes != 0); + bcontainer->pgsizes = container->proxy->dma_pgsizes; + bcontainer->dma_max_mappings = container->proxy->max_dma; + + /* No live migration support yet. */ + bcontainer->dirty_pages_supported = false; + bcontainer->max_dirty_bitmap_size = container->proxy->max_bitmap; + bcontainer->dirty_pgsizes = container->proxy->migr_pgsize; + + return true; +} + +static VFIOUserContainer *vfio_user_create_container(VFIODevice *vbasedev, + Error **errp) +{ + VFIOUserContainer *container; + + container = VFIO_IOMMU_USER(object_new(TYPE_VFIO_IOMMU_USER)); + container->proxy = vbasedev->proxy; + return container; +} + +/* + * Try to mirror vfio_container_connect() as much as possible. + */ +static VFIOUserContainer * +vfio_user_container_connect(AddressSpace *as, VFIODevice *vbasedev, + Error **errp) +{ + VFIOContainerBase *bcontainer; + VFIOUserContainer *container; + VFIOAddressSpace *space; + VFIOIOMMUClass *vioc; + int ret; + + space = vfio_address_space_get(as); + + container = vfio_user_create_container(vbasedev, errp); + if (!container) { + goto put_space_exit; + } + + bcontainer = &container->bcontainer; + + if (!vfio_cpr_register_container(bcontainer, errp)) { + goto free_container_exit; + } + + ret = ram_block_uncoordinated_discard_disable(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_address_space_insert(space, bcontainer); + + if (!vfio_listener_register(bcontainer, errp)) { + goto listener_release_exit; + } + + bcontainer->initialized = true; + + return container; + +listener_release_exit: + vfio_listener_unregister(bcontainer); + if (vioc->release) { + vioc->release(bcontainer); + } + +enable_discards_exit: + ram_block_uncoordinated_discard_disable(false); + +unregister_container_exit: + vfio_cpr_unregister_container(bcontainer); + +free_container_exit: + object_unref(container); + +put_space_exit: + vfio_address_space_put(space); + + return NULL; +} + +static void vfio_user_container_disconnect(VFIOUserContainer *container) +{ + VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + VFIOAddressSpace *space = bcontainer->space; + + ram_block_uncoordinated_discard_disable(false); + + vfio_listener_unregister(bcontainer); + if (vioc->release) { + vioc->release(bcontainer); + } + + vfio_cpr_unregister_container(bcontainer); + object_unref(container); + + vfio_address_space_put(space); +} + +static bool vfio_user_device_get(VFIOUserContainer *container, + VFIODevice *vbasedev, Error **errp) +{ + struct vfio_device_info info = { .argsz = sizeof(info) }; + + + if (!vfio_user_get_device_info(vbasedev->proxy, &info, errp)) { + return false; + } + + vbasedev->fd = -1; + + vfio_device_prepare(vbasedev, &container->bcontainer, &info); + + return true; +} + +/* + * vfio_user_device_attach: attach a device to a new container. + */ +static bool vfio_user_device_attach(const char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp) +{ + VFIOUserContainer *container; + + container = vfio_user_container_connect(as, vbasedev, errp); + if (container == NULL) { + error_prepend(errp, "failed to connect proxy"); + return false; + } + + return vfio_user_device_get(container, vbasedev, errp); +} + +static void vfio_user_device_detach(VFIODevice *vbasedev) +{ + VFIOUserContainer *container = container_of(vbasedev->bcontainer, + VFIOUserContainer, bcontainer); + + vfio_device_unprepare(vbasedev); + + vfio_user_container_disconnect(container); +} + +static int vfio_user_pci_hot_reset(VFIODevice *vbasedev, bool single) +{ + /* ->needs_reset is always false for vfio-user. */ + return 0; +} + +static void vfio_iommu_user_class_init(ObjectClass *klass, const void *data) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); + + vioc->setup = vfio_user_setup; + vioc->listener_begin = vfio_user_listener_begin, + vioc->listener_commit = vfio_user_listener_commit, + vioc->dma_map = vfio_user_dma_map; + vioc->dma_unmap = vfio_user_dma_unmap; + vioc->attach_device = vfio_user_device_attach; + vioc->detach_device = vfio_user_device_detach; + vioc->set_dirty_page_tracking = vfio_user_set_dirty_page_tracking; + vioc->query_dirty_bitmap = vfio_user_query_dirty_bitmap; + vioc->pci_hot_reset = vfio_user_pci_hot_reset; +}; + +static const TypeInfo types[] = { + { + .name = TYPE_VFIO_IOMMU_USER, + .parent = TYPE_VFIO_IOMMU, + .instance_size = sizeof(VFIOUserContainer), + .class_init = vfio_iommu_user_class_init, + }, +}; + +DEFINE_TYPES(types) diff --git a/hw/vfio-user/container.h b/hw/vfio-user/container.h new file mode 100644 index 00000000000..2bb1fa13431 --- /dev/null +++ b/hw/vfio-user/container.h @@ -0,0 +1,23 @@ +/* + * vfio-user specific definitions. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_USER_CONTAINER_H +#define HW_VFIO_USER_CONTAINER_H + +#include "qemu/osdep.h" + +#include "hw/vfio/vfio-container-base.h" +#include "hw/vfio-user/proxy.h" + +/* MMU container sub-class for vfio-user. */ +typedef struct VFIOUserContainer { + VFIOContainerBase bcontainer; + VFIOUserProxy *proxy; +} VFIOUserContainer; + +OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserContainer, VFIO_IOMMU_USER); + +#endif /* HW_VFIO_USER_CONTAINER_H */ diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c new file mode 100644 index 00000000000..32ac3fbf757 --- /dev/null +++ b/hw/vfio-user/device.c @@ -0,0 +1,441 @@ +/* + * vfio protocol over a UNIX socket device handling. + * + * Copyright © 2018, 2021 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/lockable.h" +#include "qemu/thread.h" + +#include "hw/vfio-user/device.h" +#include "hw/vfio-user/trace.h" + +/* + * These are to defend against a malign server trying + * to force us to run out of memory. + */ +#define VFIO_USER_MAX_REGIONS 100 +#define VFIO_USER_MAX_IRQS 50 + +bool vfio_user_get_device_info(VFIOUserProxy *proxy, + struct vfio_device_info *info, Error **errp) +{ + VFIOUserDeviceInfo msg; + uint32_t argsz = sizeof(msg) - sizeof(msg.hdr); + + memset(&msg, 0, sizeof(msg)); + vfio_user_request_msg(&msg.hdr, VFIO_USER_DEVICE_GET_INFO, sizeof(msg), 0); + msg.argsz = argsz; + + if (!vfio_user_send_wait(proxy, &msg.hdr, NULL, 0, errp)) { + return false; + } + + if (msg.hdr.flags & VFIO_USER_ERROR) { + error_setg_errno(errp, -msg.hdr.error_reply, + "VFIO_USER_DEVICE_GET_INFO failed"); + return false; + } + + trace_vfio_user_get_info(msg.num_regions, msg.num_irqs); + + memcpy(info, &msg.argsz, argsz); + + /* defend against a malicious server */ + if (info->num_regions > VFIO_USER_MAX_REGIONS || + info->num_irqs > VFIO_USER_MAX_IRQS) { + error_setg_errno(errp, EINVAL, "invalid reply"); + return false; + } + + return true; +} + +void vfio_user_device_reset(VFIOUserProxy *proxy) +{ + Error *local_err = NULL; + VFIOUserHdr hdr; + + vfio_user_request_msg(&hdr, VFIO_USER_DEVICE_RESET, sizeof(hdr), 0); + + if (!vfio_user_send_wait(proxy, &hdr, NULL, 0, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return; + } + + if (hdr.flags & VFIO_USER_ERROR) { + error_printf("reset reply error %d\n", hdr.error_reply); + } +} + +static int vfio_user_get_region_info(VFIOUserProxy *proxy, + struct vfio_region_info *info, + VFIOUserFDs *fds) +{ + g_autofree VFIOUserRegionInfo *msgp = NULL; + Error *local_err = NULL; + uint32_t size; + + /* data returned can be larger than vfio_region_info */ + if (info->argsz < sizeof(*info)) { + error_printf("vfio_user_get_region_info argsz too small\n"); + return -E2BIG; + } + if (fds != NULL && fds->send_fds != 0) { + error_printf("vfio_user_get_region_info can't send FDs\n"); + return -EINVAL; + } + + size = info->argsz + sizeof(VFIOUserHdr); + msgp = g_malloc0(size); + + vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_GET_REGION_INFO, + sizeof(*msgp), 0); + msgp->argsz = info->argsz; + msgp->index = info->index; + + if (!vfio_user_send_wait(proxy, &msgp->hdr, fds, size, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return -EFAULT; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + return -msgp->hdr.error_reply; + } + trace_vfio_user_get_region_info(msgp->index, msgp->flags, msgp->size); + + memcpy(info, &msgp->argsz, info->argsz); + + /* + * If at least one region is directly mapped into the VM, then we can no + * longer rely on the sequential nature of vfio-user request handling to + * ensure that posted writes are completed before a subsequent read. In this + * case, disable posted write support. This is a per-device property, not + * per-region. + */ + if (info->flags & VFIO_REGION_INFO_FLAG_MMAP) { + vfio_user_disable_posted_writes(proxy); + } + + return 0; +} + +static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev, + struct vfio_region_info *info, + int *fd) +{ + VFIOUserFDs fds = { 0, 1, fd}; + int ret; + + if (info->index > vbasedev->num_regions) { + return -EINVAL; + } + + ret = vfio_user_get_region_info(vbasedev->proxy, info, &fds); + if (ret) { + return ret; + } + + /* cap_offset in valid area */ + if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) && + (info->cap_offset < sizeof(*info) || info->cap_offset > info->argsz)) { + return -EINVAL; + } + + return 0; +} + +static int vfio_user_device_io_get_irq_info(VFIODevice *vbasedev, + struct vfio_irq_info *info) +{ + VFIOUserProxy *proxy = vbasedev->proxy; + Error *local_err = NULL; + VFIOUserIRQInfo msg; + + memset(&msg, 0, sizeof(msg)); + vfio_user_request_msg(&msg.hdr, VFIO_USER_DEVICE_GET_IRQ_INFO, + sizeof(msg), 0); + msg.argsz = info->argsz; + msg.index = info->index; + + if (!vfio_user_send_wait(proxy, &msg.hdr, NULL, 0, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return -EFAULT; + } + + if (msg.hdr.flags & VFIO_USER_ERROR) { + return -msg.hdr.error_reply; + } + trace_vfio_user_get_irq_info(msg.index, msg.flags, msg.count); + + memcpy(info, &msg.argsz, sizeof(*info)); + return 0; +} + +static int irq_howmany(int *fdp, uint32_t cur, uint32_t max) +{ + int n = 0; + + if (fdp[cur] != -1) { + do { + n++; + } while (n < max && fdp[cur + n] != -1); + } else { + do { + n++; + } while (n < max && fdp[cur + n] == -1); + } + + return n; +} + +static int vfio_user_device_io_set_irqs(VFIODevice *vbasedev, + struct vfio_irq_set *irq) +{ + VFIOUserProxy *proxy = vbasedev->proxy; + g_autofree VFIOUserIRQSet *msgp = NULL; + uint32_t size, nfds, send_fds, sent_fds, max; + Error *local_err = NULL; + + if (irq->argsz < sizeof(*irq)) { + error_printf("vfio_user_set_irqs argsz too small\n"); + return -EINVAL; + } + + /* + * Handle simple case + */ + if ((irq->flags & VFIO_IRQ_SET_DATA_EVENTFD) == 0) { + size = sizeof(VFIOUserHdr) + irq->argsz; + msgp = g_malloc0(size); + + vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS, size, 0); + msgp->argsz = irq->argsz; + msgp->flags = irq->flags; + msgp->index = irq->index; + msgp->start = irq->start; + msgp->count = irq->count; + trace_vfio_user_set_irqs(msgp->index, msgp->start, msgp->count, + msgp->flags); + + if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return -EFAULT; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + return -msgp->hdr.error_reply; + } + + return 0; + } + + /* + * Calculate the number of FDs to send + * and adjust argsz + */ + nfds = (irq->argsz - sizeof(*irq)) / sizeof(int); + irq->argsz = sizeof(*irq); + msgp = g_malloc0(sizeof(*msgp)); + /* + * Send in chunks if over max_send_fds + */ + for (sent_fds = 0; nfds > sent_fds; sent_fds += send_fds) { + VFIOUserFDs *arg_fds, loop_fds; + + /* must send all valid FDs or all invalid FDs in single msg */ + max = nfds - sent_fds; + if (max > proxy->max_send_fds) { + max = proxy->max_send_fds; + } + send_fds = irq_howmany((int *)irq->data, sent_fds, max); + + vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS, + sizeof(*msgp), 0); + msgp->argsz = irq->argsz; + msgp->flags = irq->flags; + msgp->index = irq->index; + msgp->start = irq->start + sent_fds; + msgp->count = send_fds; + trace_vfio_user_set_irqs(msgp->index, msgp->start, msgp->count, + msgp->flags); + + loop_fds.send_fds = send_fds; + loop_fds.recv_fds = 0; + loop_fds.fds = (int *)irq->data + sent_fds; + arg_fds = loop_fds.fds[0] != -1 ? &loop_fds : NULL; + + if (!vfio_user_send_wait(proxy, &msgp->hdr, arg_fds, 0, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return -EFAULT; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + return -msgp->hdr.error_reply; + } + } + + return 0; +} + +static int vfio_user_device_io_region_read(VFIODevice *vbasedev, uint8_t index, + off_t off, uint32_t count, + void *data) +{ + g_autofree VFIOUserRegionRW *msgp = NULL; + VFIOUserProxy *proxy = vbasedev->proxy; + int size = sizeof(*msgp) + count; + Error *local_err = NULL; + + if (count > proxy->max_xfer_size) { + return -EINVAL; + } + + msgp = g_malloc0(size); + vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_READ, sizeof(*msgp), 0); + msgp->offset = off; + msgp->region = index; + msgp->count = count; + trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count); + + if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, size, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return -EFAULT; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + return -msgp->hdr.error_reply; + } else if (msgp->count > count) { + return -E2BIG; + } else { + memcpy(data, &msgp->data, msgp->count); + } + + return msgp->count; +} + +/* + * If this is a posted write, and VFIO_PROXY_NO_POST is not set, then we are OK + * to send the write to the socket without waiting for the server's reply: + * a subsequent read (of any region) will not pass the posted write, as all + * messages are handled sequentially. + */ +static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index, + off_t off, unsigned count, + void *data, bool post) +{ + VFIOUserRegionRW *msgp = NULL; + VFIOUserProxy *proxy = vbasedev->proxy; + int size = sizeof(*msgp) + count; + Error *local_err = NULL; + bool can_multi; + int flags = 0; + int ret; + + if (count > proxy->max_xfer_size) { + return -EINVAL; + } + + if (proxy->flags & VFIO_PROXY_NO_POST) { + post = false; + } + + if (post) { + flags |= VFIO_USER_NO_REPLY; + } + + /* write eligible to be in a WRITE_MULTI msg ? */ + can_multi = (proxy->flags & VFIO_PROXY_USE_MULTI) && post && + count <= VFIO_USER_MULTI_DATA; + + /* + * This should be a rare case, so first check without the lock, + * if we're wrong, vfio_send_queued() will flush any posted writes + * we missed here + */ + if (proxy->wr_multi != NULL || + (proxy->num_outgoing > VFIO_USER_OUT_HIGH && can_multi)) { + + /* + * re-check with lock + * + * if already building a WRITE_MULTI msg, + * add this one if possible else flush pending before + * sending the current one + * + * else if outgoing queue is over the highwater, + * start a new WRITE_MULTI message + */ + WITH_QEMU_LOCK_GUARD(&proxy->lock) { + if (proxy->wr_multi != NULL) { + if (can_multi) { + vfio_user_add_multi(proxy, index, off, count, data); + return count; + } + vfio_user_flush_multi(proxy); + } else if (proxy->num_outgoing > VFIO_USER_OUT_HIGH && can_multi) { + vfio_user_create_multi(proxy); + vfio_user_add_multi(proxy, index, off, count, data); + return count; + } + } + } + + msgp = g_malloc0(size); + vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_WRITE, size, flags); + msgp->offset = off; + msgp->region = index; + msgp->count = count; + memcpy(&msgp->data, data, count); + trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count); + + /* async send will free msg after it's sent */ + if (post) { + if (!vfio_user_send_async(proxy, &msgp->hdr, NULL, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return -EFAULT; + } + + return count; + } + + if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + g_free(msgp); + return -EFAULT; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + ret = -msgp->hdr.error_reply; + } else { + ret = count; + } + + g_free(msgp); + return ret; +} + +/* + * Socket-based io_ops + */ +VFIODeviceIOOps vfio_user_device_io_ops_sock = { + .get_region_info = vfio_user_device_io_get_region_info, + .get_irq_info = vfio_user_device_io_get_irq_info, + .set_irqs = vfio_user_device_io_set_irqs, + .region_read = vfio_user_device_io_region_read, + .region_write = vfio_user_device_io_region_write, + +}; diff --git a/hw/vfio-user/device.h b/hw/vfio-user/device.h new file mode 100644 index 00000000000..d183a3950e2 --- /dev/null +++ b/hw/vfio-user/device.h @@ -0,0 +1,24 @@ +#ifndef VFIO_USER_DEVICE_H +#define VFIO_USER_DEVICE_H + +/* + * vfio protocol over a UNIX socket device handling. + * + * Copyright © 2018, 2021 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "linux/vfio.h" + +#include "hw/vfio-user/proxy.h" + +bool vfio_user_get_device_info(VFIOUserProxy *proxy, + struct vfio_device_info *info, Error **errp); + +void vfio_user_device_reset(VFIOUserProxy *proxy); + +extern VFIODeviceIOOps vfio_user_device_io_ops_sock; + +#endif /* VFIO_USER_DEVICE_H */ diff --git a/hw/vfio-user/meson.build b/hw/vfio-user/meson.build new file mode 100644 index 00000000000..2ed0ae5b1dd --- /dev/null +++ b/hw/vfio-user/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +vfio_user_ss = ss.source_set() +vfio_user_ss.add(files( + 'container.c', + 'device.c', + 'pci.c', + 'proxy.c', +)) + +system_ss.add_all(when: 'CONFIG_VFIO_USER', if_true: vfio_user_ss) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c new file mode 100644 index 00000000000..be71c777291 --- /dev/null +++ b/hw/vfio-user/pci.c @@ -0,0 +1,475 @@ +/* + * vfio PCI device over a UNIX socket. + * + * Copyright © 2018, 2021 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include "qemu/osdep.h" +#include "qapi-visit-sockets.h" +#include "qemu/error-report.h" + +#include "hw/qdev-properties.h" +#include "hw/vfio/pci.h" +#include "hw/vfio-user/device.h" +#include "hw/vfio-user/proxy.h" + +#define TYPE_VFIO_USER_PCI "vfio-user-pci" +OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserPCIDevice, VFIO_USER_PCI) + +struct VFIOUserPCIDevice { + VFIOPCIDevice device; + SocketAddress *socket; + bool send_queued; /* all sends are queued */ + uint32_t wait_time; /* timeout for message replies */ + bool no_post; /* all region writes are sync */ +}; + +/* + * The server maintains the device's pending interrupts, + * via its MSIX table and PBA, so we treat these accesses + * like PCI config space and forward them. + */ +static uint64_t vfio_user_pba_read(void *opaque, hwaddr addr, + unsigned size) +{ + VFIOPCIDevice *vdev = opaque; + VFIORegion *region = &vdev->bars[vdev->msix->pba_bar].region; + uint64_t data; + + /* server copy is what matters */ + data = vfio_region_read(region, addr + vdev->msix->pba_offset, size); + return data; +} + +static void vfio_user_pba_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + /* dropped */ +} + +static const MemoryRegionOps vfio_user_pba_ops = { + .read = vfio_user_pba_read, + .write = vfio_user_pba_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vfio_user_msix_setup(VFIOPCIDevice *vdev) +{ + MemoryRegion *vfio_reg, *msix_reg, *pba_reg; + + pba_reg = g_new0(MemoryRegion, 1); + vdev->msix->pba_region = pba_reg; + + vfio_reg = vdev->bars[vdev->msix->pba_bar].mr; + msix_reg = &vdev->pdev.msix_pba_mmio; + memory_region_init_io(pba_reg, OBJECT(vdev), &vfio_user_pba_ops, vdev, + "VFIO MSIX PBA", int128_get64(msix_reg->size)); + memory_region_add_subregion_overlap(vfio_reg, vdev->msix->pba_offset, + pba_reg, 1); +} + +static void vfio_user_msix_teardown(VFIOPCIDevice *vdev) +{ + MemoryRegion *mr, *sub; + + mr = vdev->bars[vdev->msix->pba_bar].mr; + sub = vdev->msix->pba_region; + memory_region_del_subregion(mr, sub); + + g_free(vdev->msix->pba_region); + vdev->msix->pba_region = NULL; +} + +static void vfio_user_dma_read(VFIOPCIDevice *vdev, VFIOUserDMARW *msg) +{ + PCIDevice *pdev = &vdev->pdev; + VFIOUserProxy *proxy = vdev->vbasedev.proxy; + VFIOUserDMARW *res; + MemTxResult r; + size_t size; + + if (msg->hdr.size < sizeof(*msg)) { + vfio_user_send_error(proxy, &msg->hdr, EINVAL); + return; + } + if (msg->count > proxy->max_xfer_size) { + vfio_user_send_error(proxy, &msg->hdr, E2BIG); + return; + } + + /* switch to our own message buffer */ + size = msg->count + sizeof(VFIOUserDMARW); + res = g_malloc0(size); + memcpy(res, msg, sizeof(*res)); + g_free(msg); + + r = pci_dma_read(pdev, res->offset, &res->data, res->count); + + switch (r) { + case MEMTX_OK: + if (res->hdr.flags & VFIO_USER_NO_REPLY) { + g_free(res); + return; + } + vfio_user_send_reply(proxy, &res->hdr, size); + break; + case MEMTX_ERROR: + vfio_user_send_error(proxy, &res->hdr, EFAULT); + break; + case MEMTX_DECODE_ERROR: + vfio_user_send_error(proxy, &res->hdr, ENODEV); + break; + case MEMTX_ACCESS_ERROR: + vfio_user_send_error(proxy, &res->hdr, EPERM); + break; + default: + error_printf("vfio_user_dma_read unknown error %d\n", r); + vfio_user_send_error(vdev->vbasedev.proxy, &res->hdr, EINVAL); + } +} + +static void vfio_user_dma_write(VFIOPCIDevice *vdev, VFIOUserDMARW *msg) +{ + PCIDevice *pdev = &vdev->pdev; + VFIOUserProxy *proxy = vdev->vbasedev.proxy; + MemTxResult r; + + if (msg->hdr.size < sizeof(*msg)) { + vfio_user_send_error(proxy, &msg->hdr, EINVAL); + return; + } + /* make sure transfer count isn't larger than the message data */ + if (msg->count > msg->hdr.size - sizeof(*msg)) { + vfio_user_send_error(proxy, &msg->hdr, E2BIG); + return; + } + + r = pci_dma_write(pdev, msg->offset, &msg->data, msg->count); + + switch (r) { + case MEMTX_OK: + if ((msg->hdr.flags & VFIO_USER_NO_REPLY) == 0) { + vfio_user_send_reply(proxy, &msg->hdr, sizeof(msg->hdr)); + } else { + g_free(msg); + } + break; + case MEMTX_ERROR: + vfio_user_send_error(proxy, &msg->hdr, EFAULT); + break; + case MEMTX_DECODE_ERROR: + vfio_user_send_error(proxy, &msg->hdr, ENODEV); + break; + case MEMTX_ACCESS_ERROR: + vfio_user_send_error(proxy, &msg->hdr, EPERM); + break; + default: + error_printf("vfio_user_dma_write unknown error %d\n", r); + vfio_user_send_error(vdev->vbasedev.proxy, &msg->hdr, EINVAL); + } +} + +/* + * Incoming request message callback. + * + * Runs off main loop, so BQL held. + */ +static void vfio_user_pci_process_req(void *opaque, VFIOUserMsg *msg) +{ + VFIOPCIDevice *vdev = opaque; + VFIOUserHdr *hdr = msg->hdr; + + /* no incoming PCI requests pass FDs */ + if (msg->fds != NULL) { + vfio_user_send_error(vdev->vbasedev.proxy, hdr, EINVAL); + vfio_user_putfds(msg); + return; + } + + switch (hdr->command) { + case VFIO_USER_DMA_READ: + vfio_user_dma_read(vdev, (VFIOUserDMARW *)hdr); + break; + case VFIO_USER_DMA_WRITE: + vfio_user_dma_write(vdev, (VFIOUserDMARW *)hdr); + break; + default: + error_printf("vfio_user_pci_process_req unknown cmd %d\n", + hdr->command); + vfio_user_send_error(vdev->vbasedev.proxy, hdr, ENOSYS); + } +} + +/* + * Emulated devices don't use host hot reset + */ +static void vfio_user_compute_needs_reset(VFIODevice *vbasedev) +{ + vbasedev->needs_reset = false; +} + +static Object *vfio_user_pci_get_object(VFIODevice *vbasedev) +{ + VFIOUserPCIDevice *vdev = container_of(vbasedev, VFIOUserPCIDevice, + device.vbasedev); + + return OBJECT(vdev); +} + +static VFIODeviceOps vfio_user_pci_ops = { + .vfio_compute_needs_reset = vfio_user_compute_needs_reset, + .vfio_eoi = vfio_pci_intx_eoi, + .vfio_get_object = vfio_user_pci_get_object, + /* No live migration support yet. */ + .vfio_save_config = NULL, + .vfio_load_config = NULL, +}; + +static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp) +{ + ERRP_GUARD(); + VFIOUserPCIDevice *udev = VFIO_USER_PCI(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); + VFIODevice *vbasedev = &vdev->vbasedev; + const char *sock_name; + AddressSpace *as; + SocketAddress addr; + VFIOUserProxy *proxy; + + if (!udev->socket) { + error_setg(errp, "No socket specified"); + error_append_hint(errp, "e.g. -device '{" + "\"driver\":\"vfio-user-pci\", " + "\"socket\": {\"path\": \"/tmp/vfio-user.sock\", " + "\"type\": \"unix\"}'" + "}'\n"); + return; + } + + sock_name = udev->socket->u.q_unix.path; + + vbasedev->name = g_strdup_printf("vfio-user:%s", sock_name); + + memset(&addr, 0, sizeof(addr)); + addr.type = SOCKET_ADDRESS_TYPE_UNIX; + addr.u.q_unix.path = (char *)sock_name; + proxy = vfio_user_connect_dev(&addr, errp); + if (!proxy) { + return; + } + vbasedev->proxy = proxy; + vfio_user_set_handler(vbasedev, vfio_user_pci_process_req, vdev); + + vbasedev->name = g_strdup_printf("vfio-user:%s", sock_name); + + if (udev->send_queued) { + proxy->flags |= VFIO_PROXY_FORCE_QUEUED; + } + + if (udev->no_post) { + proxy->flags |= VFIO_PROXY_NO_POST; + } + + /* user specified or 5 sec default */ + proxy->wait_time = udev->wait_time; + + if (!vfio_user_validate_version(proxy, errp)) { + goto error; + } + + /* + * Use socket-based device I/O instead of vfio kernel driver. + */ + vbasedev->io_ops = &vfio_user_device_io_ops_sock; + + /* + * vfio-user devices are effectively mdevs (don't use a host iommu). + */ + vbasedev->mdev = true; + + /* + * Enable per-region fds. + */ + vbasedev->use_region_fds = true; + + as = pci_device_iommu_address_space(pdev); + if (!vfio_device_attach_by_iommu_type(TYPE_VFIO_IOMMU_USER, + vbasedev->name, vbasedev, + as, errp)) { + goto error; + } + + if (!vfio_pci_populate_device(vdev, errp)) { + goto error; + } + + if (!vfio_pci_config_setup(vdev, errp)) { + goto error; + } + + /* + * vfio_pci_config_setup will have registered the device's BARs + * and setup any MSIX BARs, so errors after it succeeds must + * use out_teardown + */ + + if (!vfio_pci_add_capabilities(vdev, errp)) { + goto out_teardown; + } + + if (vdev->msix != NULL) { + vfio_user_msix_setup(vdev); + } + + if (!vfio_pci_interrupt_setup(vdev, errp)) { + goto out_teardown; + } + + vfio_pci_register_err_notifier(vdev); + vfio_pci_register_req_notifier(vdev); + + return; + +out_teardown: + vfio_pci_teardown_msi(vdev); + vfio_pci_bars_exit(vdev); +error: + error_prepend(errp, VFIO_MSG_PREFIX, vdev->vbasedev.name); + vfio_pci_put_device(vdev); +} + +static void vfio_user_instance_init(Object *obj) +{ + PCIDevice *pci_dev = PCI_DEVICE(obj); + VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); + VFIODevice *vbasedev = &vdev->vbasedev; + + device_add_bootindex_property(obj, &vdev->bootindex, + "bootindex", NULL, + &pci_dev->qdev); + vdev->host.domain = ~0U; + vdev->host.bus = ~0U; + vdev->host.slot = ~0U; + vdev->host.function = ~0U; + + vfio_device_init(vbasedev, VFIO_DEVICE_TYPE_PCI, &vfio_user_pci_ops, + DEVICE(vdev), false); + + vdev->nv_gpudirect_clique = 0xFF; + + /* + * QEMU_PCI_CAP_EXPRESS initialization does not depend on QEMU command + * line, therefore, no need to wait to realize like other devices. + */ + pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; +} + +static void vfio_user_instance_finalize(Object *obj) +{ + VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); + VFIODevice *vbasedev = &vdev->vbasedev; + + if (vdev->msix != NULL) { + vfio_user_msix_teardown(vdev); + } + + vfio_pci_put_device(vdev); + + if (vbasedev->proxy != NULL) { + vfio_user_disconnect(vbasedev->proxy); + } +} + +static void vfio_user_pci_reset(DeviceState *dev) +{ + VFIOPCIDevice *vdev = VFIO_PCI_BASE(dev); + VFIODevice *vbasedev = &vdev->vbasedev; + + vfio_pci_pre_reset(vdev); + + if (vbasedev->reset_works) { + vfio_user_device_reset(vbasedev->proxy); + } + + vfio_pci_post_reset(vdev); +} + +static const Property vfio_user_pci_dev_properties[] = { + DEFINE_PROP_UINT32("x-pci-vendor-id", VFIOPCIDevice, + vendor_id, PCI_ANY_ID), + DEFINE_PROP_UINT32("x-pci-device-id", VFIOPCIDevice, + device_id, PCI_ANY_ID), + DEFINE_PROP_UINT32("x-pci-sub-vendor-id", VFIOPCIDevice, + sub_vendor_id, PCI_ANY_ID), + DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice, + sub_device_id, PCI_ANY_ID), + DEFINE_PROP_BOOL("x-send-queued", VFIOUserPCIDevice, send_queued, false), + DEFINE_PROP_UINT32("x-msg-timeout", VFIOUserPCIDevice, wait_time, 5000), + DEFINE_PROP_BOOL("x-no-posted-writes", VFIOUserPCIDevice, no_post, false), +}; + +static void vfio_user_pci_set_socket(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + VFIOUserPCIDevice *udev = VFIO_USER_PCI(obj); + bool success; + + if (udev->device.vbasedev.proxy) { + error_setg(errp, "Proxy is connected"); + return; + } + + qapi_free_SocketAddress(udev->socket); + + udev->socket = NULL; + + success = visit_type_SocketAddress(v, name, &udev->socket, errp); + + if (!success) { + return; + } + + if (udev->socket->type != SOCKET_ADDRESS_TYPE_UNIX) { + error_setg(errp, "Unsupported socket type %s", + SocketAddressType_str(udev->socket->type)); + qapi_free_SocketAddress(udev->socket); + udev->socket = NULL; + return; + } +} + +static void vfio_user_pci_dev_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); + + device_class_set_legacy_reset(dc, vfio_user_pci_reset); + device_class_set_props(dc, vfio_user_pci_dev_properties); + + object_class_property_add(klass, "socket", "SocketAddress", NULL, + vfio_user_pci_set_socket, NULL, NULL); + object_class_property_set_description(klass, "socket", + "SocketAddress (UNIX sockets only)"); + + dc->desc = "VFIO over socket PCI device assignment"; + pdc->realize = vfio_user_pci_realize; +} + +static const TypeInfo vfio_user_pci_dev_info = { + .name = TYPE_VFIO_USER_PCI, + .parent = TYPE_VFIO_PCI_BASE, + .instance_size = sizeof(VFIOUserPCIDevice), + .class_init = vfio_user_pci_dev_class_init, + .instance_init = vfio_user_instance_init, + .instance_finalize = vfio_user_instance_finalize, +}; + +static void register_vfio_user_dev_type(void) +{ + type_register_static(&vfio_user_pci_dev_info); +} + + type_init(register_vfio_user_dev_type) diff --git a/hw/vfio-user/protocol.h b/hw/vfio-user/protocol.h new file mode 100644 index 00000000000..3249a4a6b62 --- /dev/null +++ b/hw/vfio-user/protocol.h @@ -0,0 +1,242 @@ +#ifndef VFIO_USER_PROTOCOL_H +#define VFIO_USER_PROTOCOL_H + +/* + * vfio protocol over a UNIX socket. + * + * Copyright © 2018, 2021 Oracle and/or its affiliates. + * + * Each message has a standard header that describes the command + * being sent, which is almost always a VFIO ioctl(). + * + * The header may be followed by command-specific data, such as the + * region and offset info for read and write commands. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +typedef struct { + uint16_t id; + uint16_t command; + uint32_t size; + uint32_t flags; + uint32_t error_reply; +} VFIOUserHdr; + +/* VFIOUserHdr commands */ +enum vfio_user_command { + VFIO_USER_VERSION = 1, + VFIO_USER_DMA_MAP = 2, + VFIO_USER_DMA_UNMAP = 3, + VFIO_USER_DEVICE_GET_INFO = 4, + VFIO_USER_DEVICE_GET_REGION_INFO = 5, + VFIO_USER_DEVICE_GET_REGION_IO_FDS = 6, + VFIO_USER_DEVICE_GET_IRQ_INFO = 7, + VFIO_USER_DEVICE_SET_IRQS = 8, + VFIO_USER_REGION_READ = 9, + VFIO_USER_REGION_WRITE = 10, + VFIO_USER_DMA_READ = 11, + VFIO_USER_DMA_WRITE = 12, + VFIO_USER_DEVICE_RESET = 13, + VFIO_USER_DIRTY_PAGES = 14, + VFIO_USER_REGION_WRITE_MULTI = 15, + VFIO_USER_MAX, +}; + +/* VFIOUserHdr flags */ +#define VFIO_USER_REQUEST 0x0 +#define VFIO_USER_REPLY 0x1 +#define VFIO_USER_TYPE 0xF + +#define VFIO_USER_NO_REPLY 0x10 +#define VFIO_USER_ERROR 0x20 + + +/* + * VFIO_USER_VERSION + */ +typedef struct { + VFIOUserHdr hdr; + uint16_t major; + uint16_t minor; + char capabilities[]; +} VFIOUserVersion; + +#define VFIO_USER_MAJOR_VER 0 +#define VFIO_USER_MINOR_VER 0 + +#define VFIO_USER_CAP "capabilities" + +/* "capabilities" members */ +#define VFIO_USER_CAP_MAX_FDS "max_msg_fds" +#define VFIO_USER_CAP_MAX_XFER "max_data_xfer_size" +#define VFIO_USER_CAP_PGSIZES "pgsizes" +#define VFIO_USER_CAP_MAP_MAX "max_dma_maps" +#define VFIO_USER_CAP_MIGR "migration" +#define VFIO_USER_CAP_MULTI "write_multiple" + +/* "migration" members */ +#define VFIO_USER_CAP_PGSIZE "pgsize" +#define VFIO_USER_CAP_MAX_BITMAP "max_bitmap_size" + +/* + * Max FDs mainly comes into play when a device supports multiple interrupts + * where each ones uses an eventfd to inject it into the guest. + * It is clamped by the the number of FDs the qio channel supports in a + * single message. + */ +#define VFIO_USER_DEF_MAX_FDS 8 +#define VFIO_USER_MAX_MAX_FDS 16 + +/* + * Max transfer limits the amount of data in region and DMA messages. + * Region R/W will be very small (limited by how much a single instruction + * can process) so just use a reasonable limit here. + */ +#define VFIO_USER_DEF_MAX_XFER (1024 * 1024) +#define VFIO_USER_MAX_MAX_XFER (64 * 1024 * 1024) + +/* + * Default pagesizes supported is 4k. + */ +#define VFIO_USER_DEF_PGSIZE 4096 + +/* + * Default max number of DMA mappings is stolen from the + * linux kernel "dma_entry_limit" + */ +#define VFIO_USER_DEF_MAP_MAX 65535 + +/* + * Default max bitmap size is also take from the linux kernel, + * where usage of signed ints limits the VA range to 2^31 bytes. + * Dividing that by the number of bits per byte yields 256MB + */ +#define VFIO_USER_DEF_MAX_BITMAP (256 * 1024 * 1024) + +/* + * VFIO_USER_DMA_MAP + * imported from struct vfio_iommu_type1_dma_map + */ +typedef struct { + VFIOUserHdr hdr; + uint32_t argsz; + uint32_t flags; + uint64_t offset; /* FD offset */ + uint64_t iova; + uint64_t size; +} VFIOUserDMAMap; + +/* + * VFIO_USER_DMA_UNMAP + * imported from struct vfio_iommu_type1_dma_unmap + */ +typedef struct { + VFIOUserHdr hdr; + uint32_t argsz; + uint32_t flags; + uint64_t iova; + uint64_t size; +} VFIOUserDMAUnmap; + +/* + * VFIO_USER_DEVICE_GET_INFO + * imported from struct vfio_device_info + */ +typedef struct { + VFIOUserHdr hdr; + uint32_t argsz; + uint32_t flags; + uint32_t num_regions; + uint32_t num_irqs; +} VFIOUserDeviceInfo; + +/* + * VFIO_USER_DEVICE_GET_REGION_INFO + * imported from struct vfio_region_info + */ +typedef struct { + VFIOUserHdr hdr; + uint32_t argsz; + uint32_t flags; + uint32_t index; + uint32_t cap_offset; + uint64_t size; + uint64_t offset; +} VFIOUserRegionInfo; + +/* + * VFIO_USER_DEVICE_GET_IRQ_INFO + * imported from struct vfio_irq_info + */ +typedef struct { + VFIOUserHdr hdr; + uint32_t argsz; + uint32_t flags; + uint32_t index; + uint32_t count; +} VFIOUserIRQInfo; + +/* + * VFIO_USER_DEVICE_SET_IRQS + * imported from struct vfio_irq_set + */ +typedef struct { + VFIOUserHdr hdr; + uint32_t argsz; + uint32_t flags; + uint32_t index; + uint32_t start; + uint32_t count; +} VFIOUserIRQSet; + +/* + * VFIO_USER_REGION_READ + * VFIO_USER_REGION_WRITE + */ +typedef struct { + VFIOUserHdr hdr; + uint64_t offset; + uint32_t region; + uint32_t count; + char data[]; +} VFIOUserRegionRW; + +/* + * VFIO_USER_DMA_READ + * VFIO_USER_DMA_WRITE + */ +typedef struct { + VFIOUserHdr hdr; + uint64_t offset; + uint32_t count; + char data[]; +} VFIOUserDMARW; + +/* imported from struct vfio_bitmap */ +typedef struct { + uint64_t pgsize; + uint64_t size; + char data[]; +} VFIOUserBitmap; + +/* + * VFIO_USER_REGION_WRITE_MULTI + */ +#define VFIO_USER_MULTI_DATA 8 +#define VFIO_USER_MULTI_MAX 200 + +typedef struct { + uint64_t offset; + uint32_t region; + uint32_t count; + char data[VFIO_USER_MULTI_DATA]; +} VFIOUserWROne; + +typedef struct { + VFIOUserHdr hdr; + uint64_t wr_cnt; + VFIOUserWROne wrs[VFIO_USER_MULTI_MAX]; +} VFIOUserWRMulti; + +#endif /* VFIO_USER_PROTOCOL_H */ diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c new file mode 100644 index 00000000000..c4189544408 --- /dev/null +++ b/hw/vfio-user/proxy.c @@ -0,0 +1,1356 @@ +/* + * vfio protocol over a UNIX socket. + * + * Copyright © 2018, 2021 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include + +#include "hw/vfio/vfio-device.h" +#include "hw/vfio-user/proxy.h" +#include "hw/vfio-user/trace.h" +#include "qapi/error.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qnum.h" +#include "qemu/error-report.h" +#include "qemu/lockable.h" +#include "qemu/main-loop.h" +#include "qemu/thread.h" +#include "system/iothread.h" + +static IOThread *vfio_user_iothread; + +static void vfio_user_shutdown(VFIOUserProxy *proxy); +static VFIOUserMsg *vfio_user_getmsg(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds); +static void vfio_user_recycle(VFIOUserProxy *proxy, VFIOUserMsg *msg); + +static void vfio_user_recv(void *opaque); +static void vfio_user_send(void *opaque); +static void vfio_user_cb(void *opaque); + +static void vfio_user_request(void *opaque); + +static inline void vfio_user_set_error(VFIOUserHdr *hdr, uint32_t err) +{ + hdr->flags |= VFIO_USER_ERROR; + hdr->error_reply = err; +} + +/* + * Functions called by main, CPU, or iothread threads + */ + +static void vfio_user_shutdown(VFIOUserProxy *proxy) +{ + qio_channel_shutdown(proxy->ioc, QIO_CHANNEL_SHUTDOWN_READ, NULL); + qio_channel_set_aio_fd_handler(proxy->ioc, proxy->ctx, NULL, + proxy->ctx, NULL, NULL); +} + +/* + * Same return values as qio_channel_writev_full(): + * + * QIO_CHANNEL_ERR_BLOCK: *errp not set + * -1: *errp will be populated + * otherwise: bytes written + */ +static ssize_t vfio_user_send_qio(VFIOUserProxy *proxy, VFIOUserMsg *msg, + Error **errp) +{ + VFIOUserFDs *fds = msg->fds; + struct iovec iov = { + .iov_base = msg->hdr, + .iov_len = msg->hdr->size, + }; + size_t numfds = 0; + int *fdp = NULL; + ssize_t ret; + + if (fds != NULL && fds->send_fds != 0) { + numfds = fds->send_fds; + fdp = fds->fds; + } + + ret = qio_channel_writev_full(proxy->ioc, &iov, 1, fdp, numfds, 0, errp); + + if (ret == -1) { + vfio_user_set_error(msg->hdr, EIO); + vfio_user_shutdown(proxy); + } + trace_vfio_user_send_write(msg->hdr->id, ret); + + return ret; +} + +static VFIOUserMsg *vfio_user_getmsg(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds) +{ + VFIOUserMsg *msg; + + msg = QTAILQ_FIRST(&proxy->free); + if (msg != NULL) { + QTAILQ_REMOVE(&proxy->free, msg, next); + } else { + msg = g_malloc0(sizeof(*msg)); + qemu_cond_init(&msg->cv); + } + + msg->hdr = hdr; + msg->fds = fds; + return msg; +} + +/* + * Recycle a message list entry to the free list. + */ +static void vfio_user_recycle(VFIOUserProxy *proxy, VFIOUserMsg *msg) +{ + if (msg->type == VFIO_MSG_NONE) { + error_printf("vfio_user_recycle - freeing free msg\n"); + return; + } + + /* free msg buffer if no one is waiting to consume the reply */ + if (msg->type == VFIO_MSG_NOWAIT || msg->type == VFIO_MSG_ASYNC) { + g_free(msg->hdr); + if (msg->fds != NULL) { + g_free(msg->fds); + } + } + + msg->type = VFIO_MSG_NONE; + msg->hdr = NULL; + msg->fds = NULL; + msg->complete = false; + msg->pending = false; + QTAILQ_INSERT_HEAD(&proxy->free, msg, next); +} + +VFIOUserFDs *vfio_user_getfds(int numfds) +{ + VFIOUserFDs *fds = g_malloc0(sizeof(*fds) + (numfds * sizeof(int))); + + fds->fds = (int *)((char *)fds + sizeof(*fds)); + + return fds; +} + +/* + * Functions only called by iothread + */ + +/* + * Process a received message. + */ +static void vfio_user_process(VFIOUserProxy *proxy, VFIOUserMsg *msg, + bool isreply) +{ + + /* + * Replies signal a waiter, if none just check for errors + * and free the message buffer. + * + * Requests get queued for the BH. + */ + if (isreply) { + msg->complete = true; + if (msg->type == VFIO_MSG_WAIT) { + qemu_cond_signal(&msg->cv); + } else { + if (msg->hdr->flags & VFIO_USER_ERROR) { + error_printf("vfio_user_process: error reply on async "); + error_printf("request command %x error %s\n", + msg->hdr->command, + strerror(msg->hdr->error_reply)); + } + /* youngest nowait msg has been ack'd */ + if (proxy->last_nowait == msg) { + proxy->last_nowait = NULL; + } + vfio_user_recycle(proxy, msg); + } + } else { + QTAILQ_INSERT_TAIL(&proxy->incoming, msg, next); + qemu_bh_schedule(proxy->req_bh); + } +} + +/* + * Complete a partial message read + */ +static int vfio_user_complete(VFIOUserProxy *proxy, Error **errp) +{ + VFIOUserMsg *msg = proxy->part_recv; + size_t msgleft = proxy->recv_left; + bool isreply; + char *data; + int ret; + + data = (char *)msg->hdr + (msg->hdr->size - msgleft); + while (msgleft > 0) { + ret = qio_channel_read(proxy->ioc, data, msgleft, errp); + + /* error or would block */ + if (ret <= 0) { + /* try for rest on next iternation */ + if (ret == QIO_CHANNEL_ERR_BLOCK) { + proxy->recv_left = msgleft; + } + return ret; + } + trace_vfio_user_recv_read(msg->hdr->id, ret); + + msgleft -= ret; + data += ret; + } + + /* + * Read complete message, process it. + */ + proxy->part_recv = NULL; + proxy->recv_left = 0; + isreply = (msg->hdr->flags & VFIO_USER_TYPE) == VFIO_USER_REPLY; + vfio_user_process(proxy, msg, isreply); + + /* return positive value */ + return 1; +} + +/* + * Receive and process one incoming message. + * + * For replies, find matching outgoing request and wake any waiters. + * For requests, queue in incoming list and run request BH. + */ +static int vfio_user_recv_one(VFIOUserProxy *proxy, Error **errp) +{ + VFIOUserMsg *msg = NULL; + g_autofree int *fdp = NULL; + VFIOUserFDs *reqfds; + VFIOUserHdr hdr; + struct iovec iov = { + .iov_base = &hdr, + .iov_len = sizeof(hdr), + }; + bool isreply = false; + int i, ret; + size_t msgleft, numfds = 0; + char *data = NULL; + char *buf = NULL; + + /* + * Complete any partial reads + */ + if (proxy->part_recv != NULL) { + ret = vfio_user_complete(proxy, errp); + + /* still not complete, try later */ + if (ret == QIO_CHANNEL_ERR_BLOCK) { + return ret; + } + + if (ret <= 0) { + goto fatal; + } + /* else fall into reading another msg */ + } + + /* + * Read header + */ + ret = qio_channel_readv_full(proxy->ioc, &iov, 1, &fdp, &numfds, 0, + errp); + if (ret == QIO_CHANNEL_ERR_BLOCK) { + return ret; + } + + /* read error or other side closed connection */ + if (ret <= 0) { + goto fatal; + } + + if (ret < sizeof(hdr)) { + error_setg(errp, "short read of header"); + goto fatal; + } + + /* + * Validate header + */ + if (hdr.size < sizeof(VFIOUserHdr)) { + error_setg(errp, "bad header size"); + goto fatal; + } + switch (hdr.flags & VFIO_USER_TYPE) { + case VFIO_USER_REQUEST: + isreply = false; + break; + case VFIO_USER_REPLY: + isreply = true; + break; + default: + error_setg(errp, "unknown message type"); + goto fatal; + } + trace_vfio_user_recv_hdr(proxy->sockname, hdr.id, hdr.command, hdr.size, + hdr.flags); + + /* + * For replies, find the matching pending request. + * For requests, reap incoming FDs. + */ + if (isreply) { + QTAILQ_FOREACH(msg, &proxy->pending, next) { + if (hdr.id == msg->id) { + break; + } + } + if (msg == NULL) { + error_setg(errp, "unexpected reply"); + goto err; + } + QTAILQ_REMOVE(&proxy->pending, msg, next); + + /* + * Process any received FDs + */ + if (numfds != 0) { + if (msg->fds == NULL || msg->fds->recv_fds < numfds) { + error_setg(errp, "unexpected FDs"); + goto err; + } + msg->fds->recv_fds = numfds; + memcpy(msg->fds->fds, fdp, numfds * sizeof(int)); + } + } else { + if (numfds != 0) { + reqfds = vfio_user_getfds(numfds); + memcpy(reqfds->fds, fdp, numfds * sizeof(int)); + } else { + reqfds = NULL; + } + } + + /* + * Put the whole message into a single buffer. + */ + if (isreply) { + if (hdr.size > msg->rsize) { + error_setg(errp, "reply larger than recv buffer"); + goto err; + } + *msg->hdr = hdr; + data = (char *)msg->hdr + sizeof(hdr); + } else { + if (hdr.size > proxy->max_xfer_size + sizeof(VFIOUserDMARW)) { + error_setg(errp, "vfio_user_recv request larger than max"); + goto err; + } + buf = g_malloc0(hdr.size); + memcpy(buf, &hdr, sizeof(hdr)); + data = buf + sizeof(hdr); + msg = vfio_user_getmsg(proxy, (VFIOUserHdr *)buf, reqfds); + msg->type = VFIO_MSG_REQ; + } + + /* + * Read rest of message. + */ + msgleft = hdr.size - sizeof(hdr); + while (msgleft > 0) { + ret = qio_channel_read(proxy->ioc, data, msgleft, errp); + + /* prepare to complete read on next iternation */ + if (ret == QIO_CHANNEL_ERR_BLOCK) { + proxy->part_recv = msg; + proxy->recv_left = msgleft; + return ret; + } + + if (ret <= 0) { + goto fatal; + } + trace_vfio_user_recv_read(hdr.id, ret); + + msgleft -= ret; + data += ret; + } + + vfio_user_process(proxy, msg, isreply); + return 0; + + /* + * fatal means the other side closed or we don't trust the stream + * err means this message is corrupt + */ +fatal: + vfio_user_shutdown(proxy); + proxy->state = VFIO_PROXY_ERROR; + + /* set error if server side closed */ + if (ret == 0) { + error_setg(errp, "server closed socket"); + } + +err: + for (i = 0; i < numfds; i++) { + close(fdp[i]); + } + if (isreply && msg != NULL) { + /* force an error to keep sending thread from hanging */ + vfio_user_set_error(msg->hdr, EINVAL); + msg->complete = true; + qemu_cond_signal(&msg->cv); + } + return -1; +} + +static void vfio_user_recv(void *opaque) +{ + VFIOUserProxy *proxy = opaque; + + QEMU_LOCK_GUARD(&proxy->lock); + + if (proxy->state == VFIO_PROXY_CONNECTED) { + Error *local_err = NULL; + + while (vfio_user_recv_one(proxy, &local_err) == 0) { + ; + } + + if (local_err != NULL) { + error_report_err(local_err); + } + } +} + +/* + * Send a single message, same return semantics as vfio_user_send_qio(). + * + * Sent async messages are freed, others are moved to pending queue. + */ +static ssize_t vfio_user_send_one(VFIOUserProxy *proxy, Error **errp) +{ + VFIOUserMsg *msg; + ssize_t ret; + + msg = QTAILQ_FIRST(&proxy->outgoing); + ret = vfio_user_send_qio(proxy, msg, errp); + if (ret < 0) { + return ret; + } + + QTAILQ_REMOVE(&proxy->outgoing, msg, next); + proxy->num_outgoing--; + if (msg->type == VFIO_MSG_ASYNC) { + vfio_user_recycle(proxy, msg); + } else { + QTAILQ_INSERT_TAIL(&proxy->pending, msg, next); + msg->pending = true; + } + + return ret; +} + +/* + * Send messages from outgoing queue when the socket buffer has space. + * If we deplete 'outgoing', remove ourselves from the poll list. + */ +static void vfio_user_send(void *opaque) +{ + VFIOUserProxy *proxy = opaque; + + QEMU_LOCK_GUARD(&proxy->lock); + + if (proxy->state == VFIO_PROXY_CONNECTED) { + while (!QTAILQ_EMPTY(&proxy->outgoing)) { + Error *local_err = NULL; + int ret; + + ret = vfio_user_send_one(proxy, &local_err); + + if (ret == QIO_CHANNEL_ERR_BLOCK) { + return; + } else if (ret == -1) { + error_report_err(local_err); + return; + } + } + qio_channel_set_aio_fd_handler(proxy->ioc, proxy->ctx, + vfio_user_recv, NULL, NULL, proxy); + + /* queue empty - send any pending multi write msgs */ + if (proxy->wr_multi != NULL) { + vfio_user_flush_multi(proxy); + } + } +} + +static void vfio_user_cb(void *opaque) +{ + VFIOUserProxy *proxy = opaque; + + QEMU_LOCK_GUARD(&proxy->lock); + + proxy->state = VFIO_PROXY_CLOSED; + qemu_cond_signal(&proxy->close_cv); +} + + +/* + * Functions called by main or CPU threads + */ + +/* + * Process incoming requests. + * + * The bus-specific callback has the form: + * request(opaque, msg) + * where 'opaque' was specified in vfio_user_set_handler + * and 'msg' is the inbound message. + * + * The callback is responsible for disposing of the message buffer, + * usually by re-using it when calling vfio_send_reply or vfio_send_error, + * both of which free their message buffer when the reply is sent. + * + * If the callback uses a new buffer, it needs to free the old one. + */ +static void vfio_user_request(void *opaque) +{ + VFIOUserProxy *proxy = opaque; + VFIOUserMsgQ new, free; + VFIOUserMsg *msg, *m1; + + /* reap all incoming */ + QTAILQ_INIT(&new); + WITH_QEMU_LOCK_GUARD(&proxy->lock) { + QTAILQ_FOREACH_SAFE(msg, &proxy->incoming, next, m1) { + QTAILQ_REMOVE(&proxy->incoming, msg, next); + QTAILQ_INSERT_TAIL(&new, msg, next); + } + } + + /* process list */ + QTAILQ_INIT(&free); + QTAILQ_FOREACH_SAFE(msg, &new, next, m1) { + QTAILQ_REMOVE(&new, msg, next); + trace_vfio_user_recv_request(msg->hdr->command); + proxy->request(proxy->req_arg, msg); + QTAILQ_INSERT_HEAD(&free, msg, next); + } + + /* free list */ + WITH_QEMU_LOCK_GUARD(&proxy->lock) { + QTAILQ_FOREACH_SAFE(msg, &free, next, m1) { + vfio_user_recycle(proxy, msg); + } + } +} + +/* + * Messages are queued onto the proxy's outgoing list. + * + * It handles 3 types of messages: + * + * async messages - replies and posted writes + * + * There will be no reply from the server, so message + * buffers are freed after they're sent. + * + * nowait messages - map/unmap during address space transactions + * + * These are also sent async, but a reply is expected so that + * vfio_wait_reqs() can wait for the youngest nowait request. + * They transition from the outgoing list to the pending list + * when sent, and are freed when the reply is received. + * + * wait messages - all other requests + * + * The reply to these messages is waited for by their caller. + * They also transition from outgoing to pending when sent, but + * the message buffer is returned to the caller with the reply + * contents. The caller is responsible for freeing these messages. + * + * As an optimization, if the outgoing list and the socket send + * buffer are empty, the message is sent inline instead of being + * added to the outgoing list. The rest of the transitions are + * unchanged. + */ +static bool vfio_user_send_queued(VFIOUserProxy *proxy, VFIOUserMsg *msg, + Error **errp) +{ + int ret; + + /* older coalesced writes go first */ + if (proxy->wr_multi != NULL && + ((msg->hdr->flags & VFIO_USER_TYPE) == VFIO_USER_REQUEST)) { + vfio_user_flush_multi(proxy); + } + + /* + * Unsent outgoing msgs - add to tail + */ + if (!QTAILQ_EMPTY(&proxy->outgoing)) { + QTAILQ_INSERT_TAIL(&proxy->outgoing, msg, next); + proxy->num_outgoing++; + return true; + } + + /* + * Try inline - if blocked, queue it and kick send poller + */ + if (proxy->flags & VFIO_PROXY_FORCE_QUEUED) { + ret = QIO_CHANNEL_ERR_BLOCK; + } else { + ret = vfio_user_send_qio(proxy, msg, errp); + } + + if (ret == QIO_CHANNEL_ERR_BLOCK) { + QTAILQ_INSERT_HEAD(&proxy->outgoing, msg, next); + proxy->num_outgoing = 1; + qio_channel_set_aio_fd_handler(proxy->ioc, proxy->ctx, + vfio_user_recv, proxy->ctx, + vfio_user_send, proxy); + return true; + } + if (ret == -1) { + return false; + } + + /* + * Sent - free async, add others to pending + */ + if (msg->type == VFIO_MSG_ASYNC) { + vfio_user_recycle(proxy, msg); + } else { + QTAILQ_INSERT_TAIL(&proxy->pending, msg, next); + msg->pending = true; + } + + return true; +} + +/* + * nowait send - vfio_wait_reqs() can wait for it later + * + * Returns false if we did not successfully receive a reply message, in which + * case @errp will be populated. + * + * In either case, ownership of @hdr and @fds is taken, and the caller must + * *not* free them itself. + */ +bool vfio_user_send_nowait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds, int rsize, Error **errp) +{ + VFIOUserMsg *msg; + + QEMU_LOCK_GUARD(&proxy->lock); + + msg = vfio_user_getmsg(proxy, hdr, fds); + msg->id = hdr->id; + msg->rsize = rsize ? rsize : hdr->size; + msg->type = VFIO_MSG_NOWAIT; + + if (hdr->flags & VFIO_USER_NO_REPLY) { + error_setg_errno(errp, EINVAL, "%s on NO_REPLY message", __func__); + vfio_user_recycle(proxy, msg); + return false; + } + + if (!vfio_user_send_queued(proxy, msg, errp)) { + vfio_user_recycle(proxy, msg); + return false; + } + + proxy->last_nowait = msg; + + return true; +} + +/* + * Returns false if we did not successfully receive a reply message, in which + * case @errp will be populated. + * + * In either case, the caller must free @hdr and @fds if needed. + */ +bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds, int rsize, Error **errp) +{ + VFIOUserMsg *msg; + bool ok = false; + + if (hdr->flags & VFIO_USER_NO_REPLY) { + error_setg_errno(errp, EINVAL, "%s on NO_REPLY message", __func__); + return false; + } + + qemu_mutex_lock(&proxy->lock); + + msg = vfio_user_getmsg(proxy, hdr, fds); + msg->id = hdr->id; + msg->rsize = rsize ? rsize : hdr->size; + msg->type = VFIO_MSG_WAIT; + + ok = vfio_user_send_queued(proxy, msg, errp); + + if (ok) { + while (!msg->complete) { + if (!qemu_cond_timedwait(&msg->cv, &proxy->lock, + proxy->wait_time)) { + VFIOUserMsgQ *list; + + list = msg->pending ? &proxy->pending : &proxy->outgoing; + QTAILQ_REMOVE(list, msg, next); + error_setg_errno(errp, ETIMEDOUT, + "timed out waiting for reply"); + ok = false; + break; + } + } + } + + vfio_user_recycle(proxy, msg); + + qemu_mutex_unlock(&proxy->lock); + + return ok; +} + +/* + * async send - msg can be queued, but will be freed when sent + * + * Returns false on failure, in which case @errp will be populated. + * + * In either case, ownership of @hdr and @fds is taken, and the caller must + * *not* free them itself. + */ +bool vfio_user_send_async(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds, Error **errp) +{ + VFIOUserMsg *msg; + + QEMU_LOCK_GUARD(&proxy->lock); + + msg = vfio_user_getmsg(proxy, hdr, fds); + msg->id = hdr->id; + msg->rsize = 0; + msg->type = VFIO_MSG_ASYNC; + + if (!(hdr->flags & (VFIO_USER_NO_REPLY | VFIO_USER_REPLY))) { + error_setg_errno(errp, EINVAL, "%s on sync message", __func__); + vfio_user_recycle(proxy, msg); + return false; + } + + if (!vfio_user_send_queued(proxy, msg, errp)) { + vfio_user_recycle(proxy, msg); + return false; + } + + return true; +} + +void vfio_user_wait_reqs(VFIOUserProxy *proxy) +{ + VFIOUserMsg *msg; + + /* + * Any DMA map/unmap requests sent in the middle + * of a memory region transaction were sent nowait. + * Wait for them here. + */ + qemu_mutex_lock(&proxy->lock); + if (proxy->last_nowait != NULL) { + /* + * Change type to WAIT to wait for reply + */ + msg = proxy->last_nowait; + msg->type = VFIO_MSG_WAIT; + proxy->last_nowait = NULL; + while (!msg->complete) { + if (!qemu_cond_timedwait(&msg->cv, &proxy->lock, + proxy->wait_time)) { + VFIOUserMsgQ *list; + + list = msg->pending ? &proxy->pending : &proxy->outgoing; + QTAILQ_REMOVE(list, msg, next); + error_printf("vfio_wait_reqs - timed out\n"); + break; + } + } + + if (msg->hdr->flags & VFIO_USER_ERROR) { + error_printf("vfio_user_wait_reqs - error reply on async "); + error_printf("request: command %x error %s\n", msg->hdr->command, + strerror(msg->hdr->error_reply)); + } + + /* + * Change type back to NOWAIT to free + */ + msg->type = VFIO_MSG_NOWAIT; + vfio_user_recycle(proxy, msg); + } + + qemu_mutex_unlock(&proxy->lock); +} + +/* + * Reply to an incoming request. + */ +void vfio_user_send_reply(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int size) +{ + Error *local_err = NULL; + + if (size < sizeof(VFIOUserHdr)) { + error_printf("%s: size too small", __func__); + g_free(hdr); + return; + } + + /* + * convert header to associated reply + */ + hdr->flags = VFIO_USER_REPLY; + hdr->size = size; + + if (!vfio_user_send_async(proxy, hdr, NULL, &local_err)) { + error_report_err(local_err); + } +} + +/* + * Send an error reply to an incoming request. + */ +void vfio_user_send_error(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int error) +{ + Error *local_err = NULL; + + /* + * convert header to associated reply + */ + hdr->flags = VFIO_USER_REPLY; + hdr->flags |= VFIO_USER_ERROR; + hdr->error_reply = error; + hdr->size = sizeof(*hdr); + + if (!vfio_user_send_async(proxy, hdr, NULL, &local_err)) { + error_report_err(local_err); + } +} + +/* + * Close FDs erroneously received in an incoming request. + */ +void vfio_user_putfds(VFIOUserMsg *msg) +{ + VFIOUserFDs *fds = msg->fds; + int i; + + for (i = 0; i < fds->recv_fds; i++) { + close(fds->fds[i]); + } + g_free(fds); + msg->fds = NULL; +} + +void +vfio_user_disable_posted_writes(VFIOUserProxy *proxy) +{ + WITH_QEMU_LOCK_GUARD(&proxy->lock) { + proxy->flags |= VFIO_PROXY_NO_POST; + } +} + +static QLIST_HEAD(, VFIOUserProxy) vfio_user_sockets = + QLIST_HEAD_INITIALIZER(vfio_user_sockets); + +VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp) +{ + VFIOUserProxy *proxy; + QIOChannelSocket *sioc; + QIOChannel *ioc; + char *sockname; + + if (addr->type != SOCKET_ADDRESS_TYPE_UNIX) { + error_setg(errp, "vfio_user_connect - bad address family"); + return NULL; + } + sockname = addr->u.q_unix.path; + + sioc = qio_channel_socket_new(); + ioc = QIO_CHANNEL(sioc); + if (qio_channel_socket_connect_sync(sioc, addr, errp)) { + object_unref(OBJECT(ioc)); + return NULL; + } + qio_channel_set_blocking(ioc, false, NULL); + + proxy = g_malloc0(sizeof(VFIOUserProxy)); + proxy->sockname = g_strdup_printf("unix:%s", sockname); + proxy->ioc = ioc; + + /* init defaults */ + proxy->max_xfer_size = VFIO_USER_DEF_MAX_XFER; + proxy->max_send_fds = VFIO_USER_DEF_MAX_FDS; + proxy->max_dma = VFIO_USER_DEF_MAP_MAX; + proxy->dma_pgsizes = VFIO_USER_DEF_PGSIZE; + proxy->max_bitmap = VFIO_USER_DEF_MAX_BITMAP; + proxy->migr_pgsize = VFIO_USER_DEF_PGSIZE; + + proxy->flags = VFIO_PROXY_CLIENT; + proxy->state = VFIO_PROXY_CONNECTED; + + qemu_mutex_init(&proxy->lock); + qemu_cond_init(&proxy->close_cv); + + if (vfio_user_iothread == NULL) { + vfio_user_iothread = iothread_create("VFIO user", errp); + } + + proxy->ctx = iothread_get_aio_context(vfio_user_iothread); + proxy->req_bh = qemu_bh_new(vfio_user_request, proxy); + + QTAILQ_INIT(&proxy->outgoing); + QTAILQ_INIT(&proxy->incoming); + QTAILQ_INIT(&proxy->free); + QTAILQ_INIT(&proxy->pending); + QLIST_INSERT_HEAD(&vfio_user_sockets, proxy, next); + + return proxy; +} + +void vfio_user_set_handler(VFIODevice *vbasedev, + void (*handler)(void *opaque, VFIOUserMsg *msg), + void *req_arg) +{ + VFIOUserProxy *proxy = vbasedev->proxy; + + proxy->request = handler; + proxy->req_arg = req_arg; + qio_channel_set_aio_fd_handler(proxy->ioc, proxy->ctx, + vfio_user_recv, NULL, NULL, proxy); +} + +void vfio_user_disconnect(VFIOUserProxy *proxy) +{ + VFIOUserMsg *r1, *r2; + + qemu_mutex_lock(&proxy->lock); + + /* our side is quitting */ + if (proxy->state == VFIO_PROXY_CONNECTED) { + vfio_user_shutdown(proxy); + if (!QTAILQ_EMPTY(&proxy->pending)) { + error_printf("vfio_user_disconnect: outstanding requests\n"); + } + } + object_unref(OBJECT(proxy->ioc)); + proxy->ioc = NULL; + qemu_bh_delete(proxy->req_bh); + proxy->req_bh = NULL; + + proxy->state = VFIO_PROXY_CLOSING; + QTAILQ_FOREACH_SAFE(r1, &proxy->outgoing, next, r2) { + qemu_cond_destroy(&r1->cv); + QTAILQ_REMOVE(&proxy->outgoing, r1, next); + g_free(r1); + } + QTAILQ_FOREACH_SAFE(r1, &proxy->incoming, next, r2) { + qemu_cond_destroy(&r1->cv); + QTAILQ_REMOVE(&proxy->incoming, r1, next); + g_free(r1); + } + QTAILQ_FOREACH_SAFE(r1, &proxy->pending, next, r2) { + qemu_cond_destroy(&r1->cv); + QTAILQ_REMOVE(&proxy->pending, r1, next); + g_free(r1); + } + QTAILQ_FOREACH_SAFE(r1, &proxy->free, next, r2) { + qemu_cond_destroy(&r1->cv); + QTAILQ_REMOVE(&proxy->free, r1, next); + g_free(r1); + } + + /* + * Make sure the iothread isn't blocking anywhere + * with a ref to this proxy by waiting for a BH + * handler to run after the proxy fd handlers were + * deleted above. + */ + aio_bh_schedule_oneshot(proxy->ctx, vfio_user_cb, proxy); + qemu_cond_wait(&proxy->close_cv, &proxy->lock); + + /* we now hold the only ref to proxy */ + qemu_mutex_unlock(&proxy->lock); + qemu_cond_destroy(&proxy->close_cv); + qemu_mutex_destroy(&proxy->lock); + + QLIST_REMOVE(proxy, next); + if (QLIST_EMPTY(&vfio_user_sockets)) { + iothread_destroy(vfio_user_iothread); + vfio_user_iothread = NULL; + } + + g_free(proxy->sockname); + g_free(proxy); +} + +void vfio_user_request_msg(VFIOUserHdr *hdr, uint16_t cmd, + uint32_t size, uint32_t flags) +{ + static uint16_t next_id; + + hdr->id = qatomic_fetch_inc(&next_id); + hdr->command = cmd; + hdr->size = size; + hdr->flags = (flags & ~VFIO_USER_TYPE) | VFIO_USER_REQUEST; + hdr->error_reply = 0; +} + +struct cap_entry { + const char *name; + bool (*check)(VFIOUserProxy *proxy, QObject *qobj, Error **errp); +}; + +static bool caps_parse(VFIOUserProxy *proxy, QDict *qdict, + struct cap_entry caps[], Error **errp) +{ + QObject *qobj; + struct cap_entry *p; + + for (p = caps; p->name != NULL; p++) { + qobj = qdict_get(qdict, p->name); + if (qobj != NULL) { + if (!p->check(proxy, qobj, errp)) { + return false; + } + qdict_del(qdict, p->name); + } + } + + /* warning, for now */ + if (qdict_size(qdict) != 0) { + warn_report("spurious capabilities"); + } + return true; +} + +static bool check_migr_pgsize(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QNum *qn = qobject_to(QNum, qobj); + uint64_t pgsize; + + if (qn == NULL || !qnum_get_try_uint(qn, &pgsize)) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_PGSIZE); + return false; + } + + /* must be larger than default */ + if (pgsize & (VFIO_USER_DEF_PGSIZE - 1)) { + error_setg(errp, "pgsize 0x%"PRIx64" too small", pgsize); + return false; + } + + proxy->migr_pgsize = pgsize; + return true; +} + +static bool check_bitmap(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QNum *qn = qobject_to(QNum, qobj); + uint64_t bitmap_size; + + if (qn == NULL || !qnum_get_try_uint(qn, &bitmap_size)) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_MAX_BITMAP); + return false; + } + + /* can only lower it */ + if (bitmap_size > VFIO_USER_DEF_MAX_BITMAP) { + error_setg(errp, "%s too large", VFIO_USER_CAP_MAX_BITMAP); + return false; + } + + proxy->max_bitmap = bitmap_size; + return true; +} + +static struct cap_entry caps_migr[] = { + { VFIO_USER_CAP_PGSIZE, check_migr_pgsize }, + { VFIO_USER_CAP_MAX_BITMAP, check_bitmap }, + { NULL } +}; + +static bool check_max_fds(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QNum *qn = qobject_to(QNum, qobj); + uint64_t max_send_fds; + + if (qn == NULL || !qnum_get_try_uint(qn, &max_send_fds) || + max_send_fds > VFIO_USER_MAX_MAX_FDS) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_MAX_FDS); + return false; + } + proxy->max_send_fds = max_send_fds; + return true; +} + +static bool check_max_xfer(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QNum *qn = qobject_to(QNum, qobj); + uint64_t max_xfer_size; + + if (qn == NULL || !qnum_get_try_uint(qn, &max_xfer_size) || + max_xfer_size > VFIO_USER_MAX_MAX_XFER) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_MAX_XFER); + return false; + } + proxy->max_xfer_size = max_xfer_size; + return true; +} + +static bool check_pgsizes(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QNum *qn = qobject_to(QNum, qobj); + uint64_t pgsizes; + + if (qn == NULL || !qnum_get_try_uint(qn, &pgsizes)) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_PGSIZES); + return false; + } + + /* must be larger than default */ + if (pgsizes & (VFIO_USER_DEF_PGSIZE - 1)) { + error_setg(errp, "pgsize 0x%"PRIx64" too small", pgsizes); + return false; + } + + proxy->dma_pgsizes = pgsizes; + return true; +} + +static bool check_max_dma(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QNum *qn = qobject_to(QNum, qobj); + uint64_t max_dma; + + if (qn == NULL || !qnum_get_try_uint(qn, &max_dma)) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_MAP_MAX); + return false; + } + + /* can only lower it */ + if (max_dma > VFIO_USER_DEF_MAP_MAX) { + error_setg(errp, "%s too large", VFIO_USER_CAP_MAP_MAX); + return false; + } + + proxy->max_dma = max_dma; + return true; +} + +static bool check_migr(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QDict *qdict = qobject_to(QDict, qobj); + + if (qdict == NULL) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_MAX_FDS); + return true; + } + return caps_parse(proxy, qdict, caps_migr, errp); +} + +static bool check_multi(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QBool *qb = qobject_to(QBool, qobj); + + if (qb == NULL) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_MULTI); + return false; + } + if (qbool_get_bool(qb)) { + proxy->flags |= VFIO_PROXY_USE_MULTI; + } + return true; +} + +static struct cap_entry caps_cap[] = { + { VFIO_USER_CAP_MAX_FDS, check_max_fds }, + { VFIO_USER_CAP_MAX_XFER, check_max_xfer }, + { VFIO_USER_CAP_PGSIZES, check_pgsizes }, + { VFIO_USER_CAP_MAP_MAX, check_max_dma }, + { VFIO_USER_CAP_MIGR, check_migr }, + { VFIO_USER_CAP_MULTI, check_multi }, + { NULL } +}; + +static bool check_cap(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QDict *qdict = qobject_to(QDict, qobj); + + if (qdict == NULL) { + error_setg(errp, "malformed %s", VFIO_USER_CAP); + return false; + } + return caps_parse(proxy, qdict, caps_cap, errp); +} + +static struct cap_entry ver_0_0[] = { + { VFIO_USER_CAP, check_cap }, + { NULL } +}; + +static bool caps_check(VFIOUserProxy *proxy, int minor, const char *caps, + Error **errp) +{ + QObject *qobj; + QDict *qdict; + bool ret; + + qobj = qobject_from_json(caps, NULL); + if (qobj == NULL) { + error_setg(errp, "malformed capabilities %s", caps); + return false; + } + qdict = qobject_to(QDict, qobj); + if (qdict == NULL) { + error_setg(errp, "capabilities %s not an object", caps); + qobject_unref(qobj); + return false; + } + ret = caps_parse(proxy, qdict, ver_0_0, errp); + + qobject_unref(qobj); + return ret; +} + +static GString *caps_json(void) +{ + QDict *dict = qdict_new(); + QDict *capdict = qdict_new(); + QDict *migdict = qdict_new(); + GString *str; + + qdict_put_int(migdict, VFIO_USER_CAP_PGSIZE, VFIO_USER_DEF_PGSIZE); + qdict_put_int(migdict, VFIO_USER_CAP_MAX_BITMAP, VFIO_USER_DEF_MAX_BITMAP); + qdict_put_obj(capdict, VFIO_USER_CAP_MIGR, QOBJECT(migdict)); + + qdict_put_int(capdict, VFIO_USER_CAP_MAX_FDS, VFIO_USER_MAX_MAX_FDS); + qdict_put_int(capdict, VFIO_USER_CAP_MAX_XFER, VFIO_USER_DEF_MAX_XFER); + qdict_put_int(capdict, VFIO_USER_CAP_PGSIZES, VFIO_USER_DEF_PGSIZE); + qdict_put_int(capdict, VFIO_USER_CAP_MAP_MAX, VFIO_USER_DEF_MAP_MAX); + qdict_put_bool(capdict, VFIO_USER_CAP_MULTI, true); + + qdict_put_obj(dict, VFIO_USER_CAP, QOBJECT(capdict)); + + str = qobject_to_json(QOBJECT(dict)); + qobject_unref(dict); + return str; +} + +bool vfio_user_validate_version(VFIOUserProxy *proxy, Error **errp) +{ + g_autofree VFIOUserVersion *msgp = NULL; + GString *caps; + char *reply; + int size, caplen; + + caps = caps_json(); + caplen = caps->len + 1; + size = sizeof(*msgp) + caplen; + msgp = g_malloc0(size); + + vfio_user_request_msg(&msgp->hdr, VFIO_USER_VERSION, size, 0); + msgp->major = VFIO_USER_MAJOR_VER; + msgp->minor = VFIO_USER_MINOR_VER; + memcpy(&msgp->capabilities, caps->str, caplen); + g_string_free(caps, true); + trace_vfio_user_version(msgp->major, msgp->minor, msgp->capabilities); + + if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, errp)) { + return false; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + error_setg_errno(errp, msgp->hdr.error_reply, "version reply"); + return false; + } + + if (msgp->major != VFIO_USER_MAJOR_VER || + msgp->minor > VFIO_USER_MINOR_VER) { + error_setg(errp, "incompatible server version"); + return false; + } + + reply = msgp->capabilities; + if (reply[msgp->hdr.size - sizeof(*msgp) - 1] != '\0') { + error_setg(errp, "corrupt version reply"); + return false; + } + + if (!caps_check(proxy, msgp->minor, reply, errp)) { + return false; + } + + trace_vfio_user_version(msgp->major, msgp->minor, msgp->capabilities); + return true; +} + +void vfio_user_flush_multi(VFIOUserProxy *proxy) +{ + VFIOUserMsg *msg; + VFIOUserWRMulti *wm = proxy->wr_multi; + Error *local_err = NULL; + + proxy->wr_multi = NULL; + + /* adjust size for actual # of writes */ + wm->hdr.size -= (VFIO_USER_MULTI_MAX - wm->wr_cnt) * sizeof(VFIOUserWROne); + + msg = vfio_user_getmsg(proxy, &wm->hdr, NULL); + msg->id = wm->hdr.id; + msg->rsize = 0; + msg->type = VFIO_MSG_ASYNC; + trace_vfio_user_wrmulti("flush", wm->wr_cnt); + + if (!vfio_user_send_queued(proxy, msg, &local_err)) { + error_report_err(local_err); + vfio_user_recycle(proxy, msg); + } +} + +void vfio_user_create_multi(VFIOUserProxy *proxy) +{ + VFIOUserWRMulti *wm; + + wm = g_malloc0(sizeof(*wm)); + vfio_user_request_msg(&wm->hdr, VFIO_USER_REGION_WRITE_MULTI, + sizeof(*wm), VFIO_USER_NO_REPLY); + proxy->wr_multi = wm; +} + +void vfio_user_add_multi(VFIOUserProxy *proxy, uint8_t index, + off_t offset, uint32_t count, void *data) +{ + VFIOUserWRMulti *wm = proxy->wr_multi; + VFIOUserWROne *w1 = &wm->wrs[wm->wr_cnt]; + + w1->offset = offset; + w1->region = index; + w1->count = count; + memcpy(&w1->data, data, count); + + wm->wr_cnt++; + trace_vfio_user_wrmulti("add", wm->wr_cnt); + if (wm->wr_cnt == VFIO_USER_MULTI_MAX || + proxy->num_outgoing < VFIO_USER_OUT_LOW) { + vfio_user_flush_multi(proxy); + } +} diff --git a/hw/vfio-user/proxy.h b/hw/vfio-user/proxy.h new file mode 100644 index 00000000000..61e64a0020f --- /dev/null +++ b/hw/vfio-user/proxy.h @@ -0,0 +1,135 @@ +#ifndef VFIO_USER_PROXY_H +#define VFIO_USER_PROXY_H + +/* + * vfio protocol over a UNIX socket. + * + * Copyright © 2018, 2021 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "io/channel.h" +#include "io/channel-socket.h" + +#include "qemu/queue.h" +#include "qemu/sockets.h" +#include "qemu/thread.h" +#include "hw/vfio/vfio-device.h" +#include "hw/vfio-user/protocol.h" + +typedef struct { + int send_fds; + int recv_fds; + int *fds; +} VFIOUserFDs; + +enum msg_type { + VFIO_MSG_NONE, + VFIO_MSG_ASYNC, + VFIO_MSG_WAIT, + VFIO_MSG_NOWAIT, + VFIO_MSG_REQ, +}; + +typedef struct VFIOUserMsg { + QTAILQ_ENTRY(VFIOUserMsg) next; + VFIOUserHdr *hdr; + VFIOUserFDs *fds; + uint32_t rsize; + uint32_t id; + QemuCond cv; + bool complete; + bool pending; + enum msg_type type; +} VFIOUserMsg; + + +enum proxy_state { + VFIO_PROXY_CONNECTED = 1, + VFIO_PROXY_ERROR = 2, + VFIO_PROXY_CLOSING = 3, + VFIO_PROXY_CLOSED = 4, +}; + +typedef QTAILQ_HEAD(VFIOUserMsgQ, VFIOUserMsg) VFIOUserMsgQ; + +typedef struct VFIOUserProxy { + QLIST_ENTRY(VFIOUserProxy) next; + char *sockname; + struct QIOChannel *ioc; + void (*request)(void *opaque, VFIOUserMsg *msg); + void *req_arg; + uint64_t max_xfer_size; + uint64_t max_send_fds; + uint64_t max_dma; + uint64_t dma_pgsizes; + uint64_t max_bitmap; + uint64_t migr_pgsize; + int flags; + uint32_t wait_time; + QemuCond close_cv; + AioContext *ctx; + QEMUBH *req_bh; + bool async_ops; + + /* + * above only changed when BQL is held + * below are protected by per-proxy lock + */ + QemuMutex lock; + VFIOUserMsgQ free; + VFIOUserMsgQ pending; + VFIOUserMsgQ incoming; + VFIOUserMsgQ outgoing; + VFIOUserMsg *last_nowait; + VFIOUserMsg *part_recv; + size_t recv_left; + VFIOUserWRMulti *wr_multi; + int num_outgoing; + enum proxy_state state; +} VFIOUserProxy; + +/* VFIOProxy flags */ +#define VFIO_PROXY_CLIENT 0x1 +#define VFIO_PROXY_FORCE_QUEUED 0x4 +#define VFIO_PROXY_NO_POST 0x8 +#define VFIO_PROXY_USE_MULTI 0x16 + +/* coalescing high and low water marks for VFIOProxy num_outgoing */ +#define VFIO_USER_OUT_HIGH 1024 +#define VFIO_USER_OUT_LOW 128 + +typedef struct VFIODevice VFIODevice; + +VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp); +void vfio_user_disconnect(VFIOUserProxy *proxy); +void vfio_user_set_handler(VFIODevice *vbasedev, + void (*handler)(void *opaque, VFIOUserMsg *msg), + void *reqarg); +bool vfio_user_validate_version(VFIOUserProxy *proxy, Error **errp); + +VFIOUserFDs *vfio_user_getfds(int numfds); +void vfio_user_putfds(VFIOUserMsg *msg); + +void vfio_user_disable_posted_writes(VFIOUserProxy *proxy); + +void vfio_user_request_msg(VFIOUserHdr *hdr, uint16_t cmd, + uint32_t size, uint32_t flags); +void vfio_user_wait_reqs(VFIOUserProxy *proxy); +bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds, int rsize, Error **errp); +bool vfio_user_send_nowait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds, int rsize, Error **errp); +bool vfio_user_send_async(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds, Error **errp); + +void vfio_user_send_reply(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int size); +void vfio_user_send_error(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int error); + +void vfio_user_flush_multi(VFIOUserProxy *proxy); +void vfio_user_create_multi(VFIOUserProxy *proxy); +void vfio_user_add_multi(VFIOUserProxy *proxy, uint8_t index, + off_t offset, uint32_t count, void *data); + +#endif /* VFIO_USER_PROXY_H */ diff --git a/hw/vfio-user/trace-events b/hw/vfio-user/trace-events new file mode 100644 index 00000000000..abb67f4c119 --- /dev/null +++ b/hw/vfio-user/trace-events @@ -0,0 +1,20 @@ +# See docs/devel/tracing.rst for syntax documentation. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +# common.c +vfio_user_recv_hdr(const char *name, uint16_t id, uint16_t cmd, uint32_t size, uint32_t flags) " (%s) id 0x%x cmd 0x%x size 0x%x flags 0x%x" +vfio_user_recv_read(uint16_t id, int read) " id 0x%x read 0x%x" +vfio_user_recv_request(uint16_t cmd) " command 0x%x" +vfio_user_send_write(uint16_t id, int wrote) " id 0x%x wrote 0x%x" +vfio_user_version(uint16_t major, uint16_t minor, const char *caps) " major %d minor %d caps: %s" +vfio_user_get_info(uint32_t nregions, uint32_t nirqs) " #regions %d #irqs %d" +vfio_user_get_region_info(uint32_t index, uint32_t flags, uint64_t size) " index %d flags 0x%x size 0x%"PRIx64 +vfio_user_region_rw(uint32_t region, uint64_t off, uint32_t count) " region %d offset 0x%"PRIx64" count %d" +vfio_user_get_irq_info(uint32_t index, uint32_t flags, uint32_t count) " index %d flags 0x%x count %d" +vfio_user_set_irqs(uint32_t index, uint32_t start, uint32_t count, uint32_t flags) " index %d start %d count %d flags 0x%x" +vfio_user_wrmulti(const char *s, uint64_t wr_cnt) " %s count 0x%"PRIx64 + +# container.c +vfio_user_dma_map(uint64_t iova, uint64_t size, uint64_t off, uint32_t flags, bool async_ops) " iova 0x%"PRIx64" size 0x%"PRIx64" off 0x%"PRIx64" flags 0x%x async_ops %d" +vfio_user_dma_unmap(uint64_t iova, uint64_t size, uint32_t flags, bool async_ops) " iova 0x%"PRIx64" size 0x%"PRIx64" flags 0x%x async_ops %d" diff --git a/hw/vfio-user/trace.h b/hw/vfio-user/trace.h new file mode 100644 index 00000000000..9cf02d9506d --- /dev/null +++ b/hw/vfio-user/trace.h @@ -0,0 +1,4 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "trace/trace-hw_vfio_user.h" diff --git a/hw/vfio/amd-xgbe.c b/hw/vfio/amd-xgbe.c index 96bd608b8dd..58f590e385b 100644 --- a/hw/vfio/amd-xgbe.c +++ b/hw/vfio/amd-xgbe.c @@ -15,12 +15,14 @@ #include "hw/vfio/vfio-amd-xgbe.h" #include "migration/vmstate.h" #include "qemu/module.h" +#include "qemu/error-report.h" static void amd_xgbe_realize(DeviceState *dev, Error **errp) { VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev); VFIOAmdXgbeDeviceClass *k = VFIO_AMD_XGBE_DEVICE_GET_CLASS(dev); + warn_report("-device vfio-amd-xgbe is deprecated"); vdev->compat = g_strdup("amd,xgbe-seattle-v1a"); vdev->num_compat = 1; @@ -32,7 +34,7 @@ static const VMStateDescription vfio_platform_amd_xgbe_vmstate = { .unmigratable = 1, }; -static void vfio_amd_xgbe_class_init(ObjectClass *klass, void *data) +static void vfio_amd_xgbe_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VFIOAmdXgbeDeviceClass *vcxc = @@ -41,8 +43,6 @@ static void vfio_amd_xgbe_class_init(ObjectClass *klass, void *data) &vcxc->parent_realize); dc->desc = "VFIO AMD XGBE"; dc->vmsd = &vfio_platform_amd_xgbe_vmstate; - /* Supported by TYPE_VIRT_MACHINE */ - dc->user_creatable = true; } static const TypeInfo vfio_amd_xgbe_dev_info = { diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 6e21d1da5a7..874e0d1eaf1 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -10,15 +10,19 @@ * directory. */ +#include #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 "hw/vfio/vfio-device.h" +#include "system/iommufd.h" #include "hw/s390x/ap-device.h" +#include "hw/s390x/css.h" #include "qemu/error-report.h" #include "qemu/event_notifier.h" +#include "qemu/lockable.h" #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/option.h" @@ -27,7 +31,7 @@ #include "migration/vmstate.h" #include "hw/qdev-properties.h" #include "hw/s390x/ap-bridge.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qom/object.h" #define TYPE_VFIO_AP_DEVICE "vfio-ap" @@ -36,8 +40,18 @@ struct VFIOAPDevice { APDevice apdev; VFIODevice vdev; EventNotifier req_notifier; + EventNotifier cfg_notifier; }; +typedef struct APConfigChgEvent { + QTAILQ_ENTRY(APConfigChgEvent) next; +} APConfigChgEvent; + +static QTAILQ_HEAD(, APConfigChgEvent) cfg_chg_events = + QTAILQ_HEAD_INITIALIZER(cfg_chg_events); + +static QemuMutex cfg_chg_events_lock; + OBJECT_DECLARE_SIMPLE_TYPE(VFIOAPDevice, VFIO_AP_DEVICE) static void vfio_ap_compute_needs_reset(VFIODevice *vdev) @@ -53,64 +67,81 @@ struct VFIODeviceOps vfio_ap_ops = { .vfio_compute_needs_reset = vfio_ap_compute_needs_reset, }; -static void vfio_ap_put_device(VFIOAPDevice *vapdev) +static void vfio_ap_req_notifier_handler(void *opaque) { - g_free(vapdev->vdev.name); - vfio_put_base_device(&vapdev->vdev); + VFIOAPDevice *vapdev = opaque; + Error *err = NULL; + + if (!event_notifier_test_and_clear(&vapdev->req_notifier)) { + return; + } + + qdev_unplug(DEVICE(vapdev), &err); + + if (err) { + warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name); + } } -static VFIOGroup *vfio_ap_get_group(VFIOAPDevice *vapdev, Error **errp) +static void vfio_ap_cfg_chg_notifier_handler(void *opaque) { - GError *gerror = NULL; - char *symlink, *group_path; - int groupid; - - 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; + APConfigChgEvent *cfg_chg_event; + VFIOAPDevice *vapdev = opaque; + + if (!event_notifier_test_and_clear(&vapdev->cfg_notifier)) { + return; } - if (sscanf(basename(group_path), "%d", &groupid) != 1) { - error_setg(errp, "vfio: failed to read %s", group_path); - g_free(group_path); - return NULL; + cfg_chg_event = g_new0(APConfigChgEvent, 1); + + WITH_QEMU_LOCK_GUARD(&cfg_chg_events_lock) { + QTAILQ_INSERT_TAIL(&cfg_chg_events, cfg_chg_event, next); } - g_free(group_path); + css_generate_css_crws(0); - return vfio_get_group(groupid, &address_space_memory, errp); } -static void vfio_ap_req_notifier_handler(void *opaque) +int ap_chsc_sei_nt0_get_event(void *res) { - VFIOAPDevice *vapdev = opaque; - Error *err = NULL; + ChscSeiNt0Res *nt0_res = (ChscSeiNt0Res *)res; + APConfigChgEvent *cfg_chg_event; - if (!event_notifier_test_and_clear(&vapdev->req_notifier)) { - return; + WITH_QEMU_LOCK_GUARD(&cfg_chg_events_lock) { + if (QTAILQ_EMPTY(&cfg_chg_events)) { + return EVENT_INFORMATION_NOT_STORED; + } + + cfg_chg_event = QTAILQ_FIRST(&cfg_chg_events); + QTAILQ_REMOVE(&cfg_chg_events, cfg_chg_event, next); } - qdev_unplug(DEVICE(vapdev), &err); + memset(nt0_res, 0, sizeof(*nt0_res)); + g_free(cfg_chg_event); + nt0_res->flags |= PENDING_EVENT_INFO_BITMASK; + nt0_res->length = sizeof(ChscSeiNt0Res); + nt0_res->code = NT0_RES_RESPONSE_CODE; + nt0_res->nt = NT0_RES_NT_DEFAULT; + nt0_res->rs = NT0_RES_RS_AP_CHANGE; + nt0_res->cc = NT0_RES_CC_AP_CHANGE; - if (err) { - warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name); - } + return EVENT_INFORMATION_STORED; +} + +bool ap_chsc_sei_nt0_have_event(void) +{ + QEMU_LOCK_GUARD(&cfg_chg_events_lock); + return !QTAILQ_EMPTY(&cfg_chg_events); } -static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, +static bool vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, unsigned int irq, Error **errp) { int fd; - size_t argsz; + int ret; IOHandler *fd_read; EventNotifier *notifier; - struct vfio_irq_info *irq_info; + struct vfio_irq_info irq_info; VFIODevice *vdev = &vapdev->vdev; switch (irq) { @@ -118,47 +149,50 @@ static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, notifier = &vapdev->req_notifier; fd_read = vfio_ap_req_notifier_handler; break; + case VFIO_AP_CFG_CHG_IRQ_INDEX: + notifier = &vapdev->cfg_notifier; + fd_read = vfio_ap_cfg_chg_notifier_handler; + 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); - irq_info = g_malloc0(argsz); - irq_info->index = irq; - irq_info->argsz = argsz; + ret = vfio_device_get_irq_info(vdev, irq, &irq_info); - if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, - irq_info) < 0 || irq_info->count < 1) { - error_setg_errno(errp, errno, "vfio: Error getting irq info"); - goto out_free_info; + if (ret < 0) { + error_setg_errno(errp, -ret, "vfio: Error getting irq info"); + return false; + } + + if (irq_info.count < 1) { + error_setg(errp, "vfio: Error getting irq info, count=0"); + 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, vapdev); - if (vfio_set_irq_signaling(vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd, - errp)) { + if (!vfio_device_irq_set_signaling(vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd, + errp)) { qemu_set_fd_handler(fd, NULL, NULL, vapdev); event_notifier_cleanup(notifier); } -out_free_info: - g_free(irq_info); - + return true; } static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev, @@ -171,13 +205,16 @@ static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev, case VFIO_AP_REQ_IRQ_INDEX: notifier = &vapdev->req_notifier; break; + case VFIO_AP_CFG_CHG_IRQ_INDEX: + notifier = &vapdev->cfg_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)) { + if (!vfio_device_irq_set_signaling(&vapdev->vdev, irq, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name); } @@ -188,74 +225,73 @@ static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev, static void vfio_ap_realize(DeviceState *dev, Error **errp) { - int ret; - char *mdevid; + ERRP_GUARD(); Error *err = NULL; - VFIOGroup *vfio_group; - APDevice *apdev = AP_DEVICE(dev); - VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); + VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev); + VFIODevice *vbasedev = &vapdev->vdev; - vfio_group = vfio_ap_get_group(vapdev, errp); - if (!vfio_group) { - return; + static bool lock_initialized; + + if (!lock_initialized) { + qemu_mutex_init(&cfg_chg_events_lock); + lock_initialized = true; } - 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_device_get_name(vbasedev, errp)) { + return; + } - /* - * 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; + if (!vfio_device_attach(vbasedev->name, vbasedev, + &address_space_memory, errp)) { + goto error; + } - 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); } - vfio_ap_register_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX, &err); - if (err) { + if (!vfio_ap_register_irq_notifier(vapdev, VFIO_AP_CFG_CHG_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; -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_unregister_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX); - vfio_ap_put_device(vapdev); - vfio_put_group(group); + vfio_ap_unregister_irq_notifier(vapdev, VFIO_AP_CFG_CHG_IRQ_INDEX); + vfio_device_detach(&vapdev->vdev); + g_free(vapdev->vdev.name); } -static Property vfio_ap_properties[] = { +static const Property vfio_ap_properties[] = { DEFINE_PROP_STRING("sysfsdev", VFIOAPDevice, vdev.sysfsdev), - DEFINE_PROP_END_OF_LIST(), +#ifdef CONFIG_IOMMUFD + DEFINE_PROP_LINK("iommufd", VFIOAPDevice, vdev.iommufd, + TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), +#endif }; 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) { @@ -269,25 +305,63 @@ static const VMStateDescription vfio_ap_vmstate = { .unmigratable = 1, }; -static void vfio_ap_class_init(ObjectClass *klass, void *data) +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, const 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; + + object_class_property_set_description(klass, /* 3.1 */ + "sysfsdev", + "Host sysfs path of assigned device"); +#ifdef CONFIG_IOMMUFD + object_class_property_set_description(klass, /* 9.0 */ + "iommufd", + "Set host IOMMUFD backend device"); +#endif } 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/calxeda-xgmac.c b/hw/vfio/calxeda-xgmac.c index 87c382e7361..03f2ff57630 100644 --- a/hw/vfio/calxeda-xgmac.c +++ b/hw/vfio/calxeda-xgmac.c @@ -15,12 +15,14 @@ #include "hw/vfio/vfio-calxeda-xgmac.h" #include "migration/vmstate.h" #include "qemu/module.h" +#include "qemu/error-report.h" static void calxeda_xgmac_realize(DeviceState *dev, Error **errp) { VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev); VFIOCalxedaXgmacDeviceClass *k = VFIO_CALXEDA_XGMAC_DEVICE_GET_CLASS(dev); + warn_report("-device vfio-calxeda-xgmac is deprecated"); vdev->compat = g_strdup("calxeda,hb-xgmac"); vdev->num_compat = 1; @@ -32,7 +34,7 @@ static const VMStateDescription vfio_platform_calxeda_xgmac_vmstate = { .unmigratable = 1, }; -static void vfio_calxeda_xgmac_class_init(ObjectClass *klass, void *data) +static void vfio_calxeda_xgmac_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VFIOCalxedaXgmacDeviceClass *vcxc = @@ -41,8 +43,6 @@ static void vfio_calxeda_xgmac_class_init(ObjectClass *klass, void *data) &vcxc->parent_realize); dc->desc = "VFIO Calxeda XGMAC"; dc->vmsd = &vfio_platform_calxeda_xgmac_vmstate; - /* Supported by TYPE_VIRT_MACHINE */ - dc->user_creatable = true; } static const TypeInfo vfio_calxeda_xgmac_dev_info = { diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 1e2fce83b08..cea9d6e0056 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -15,18 +15,19 @@ */ #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 "hw/vfio/vfio-device.h" +#include "system/iommufd.h" #include "hw/s390x/s390-ccw.h" #include "hw/s390x/vfio-ccw.h" #include "hw/qdev-properties.h" #include "hw/s390x/ccw-device.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" @@ -50,17 +51,8 @@ struct VFIOCCWDevice { EventNotifier crw_notifier; EventNotifier req_notifier; bool force_orb_pfch; - bool warned_orb_pfch; }; -static inline void warn_once_pfch(VFIOCCWDevice *vcdev, SubchDev *sch, - const char *msg) -{ - warn_report_once_cond(&vcdev->warned_orb_pfch, - "vfio-ccw (devno %x.%x.%04x): %s", - sch->cssid, sch->ssid, sch->devno, msg); -} - static void vfio_ccw_compute_needs_reset(VFIODevice *vdev) { vdev->needs_reset = false; @@ -82,7 +74,8 @@ static IOInstEnding vfio_ccw_handle_request(SubchDev *sch) if (!(sch->orb.ctrl0 & ORB_CTRL0_MASK_PFCH) && vcdev->force_orb_pfch) { sch->orb.ctrl0 |= ORB_CTRL0_MASK_PFCH; - warn_once_pfch(vcdev, sch, "PFCH flag forced"); + warn_report_once("vfio-ccw (devno %x.%x.%04x): PFCH flag forced", + sch->cssid, sch->ssid, sch->devno); } QEMU_BUILD_BUG_ON(sizeof(region->orb_area) != sizeof(ORB)); @@ -378,13 +371,13 @@ static void vfio_ccw_io_notifier_handler(void *opaque) 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; - size_t argsz; + struct vfio_irq_info irq_info; + int ret; int fd; EventNotifier *notifier; IOHandler *fd_read; @@ -404,43 +397,44 @@ 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); - irq_info = g_malloc0(argsz); - irq_info->index = irq; - irq_info->argsz = argsz; - 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; + ret = vfio_device_get_irq_info(vdev, irq, &irq_info); + + if (ret < 0) { + error_setg_errno(errp, -ret, "vfio: Error getting irq info"); + return false; + } + + if (irq_info.count < 1) { + error_setg(errp, "vfio: Error getting irq info, count=0"); + 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_device_irq_set_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, @@ -464,8 +458,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_device_irq_set_signaling(&vcdev->vdev, irq, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { warn_reportf_err(err, VFIO_MSG_PREFIX, vcdev->vdev.name); } @@ -474,7 +468,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; @@ -483,7 +477,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; } /* @@ -493,13 +487,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); + ret = vfio_device_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; @@ -510,11 +504,10 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) vcdev->io_region_offset = info->offset; vcdev->io_region = g_malloc0(info->size); - g_free(info); /* check for the optional async command region */ - ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW, - VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD, &info); + ret = vfio_device_get_region_info_type(vdev, VFIO_REGION_TYPE_CCW, + VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD, &info); if (!ret) { vcdev->async_cmd_region_size = info->size; if (sizeof(*vcdev->async_cmd_region) != vcdev->async_cmd_region_size) { @@ -523,11 +516,10 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) } vcdev->async_cmd_region_offset = info->offset; vcdev->async_cmd_region = g_malloc0(info->size); - g_free(info); } - ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW, - VFIO_REGION_SUBTYPE_CCW_SCHIB, &info); + ret = vfio_device_get_region_info_type(vdev, VFIO_REGION_TYPE_CCW, + VFIO_REGION_SUBTYPE_CCW_SCHIB, &info); if (!ret) { vcdev->schib_region_size = info->size; if (sizeof(*vcdev->schib_region) != vcdev->schib_region_size) { @@ -536,11 +528,10 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) } vcdev->schib_region_offset = info->offset; vcdev->schib_region = g_malloc(info->size); - g_free(info); } - ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW, - VFIO_REGION_SUBTYPE_CCW_CRW, &info); + ret = vfio_device_get_region_info_type(vdev, VFIO_REGION_TYPE_CCW, + VFIO_REGION_SUBTYPE_CCW_CRW, &info); if (!ret) { vcdev->crw_region_size = info->size; @@ -550,18 +541,16 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) } vcdev->crw_region_offset = info->offset; vcdev->crw_region = g_malloc(info->size); - g_free(info); } - return; + return true; out_err: g_free(vcdev->crw_region); g_free(vcdev->schib_region); 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) @@ -572,131 +561,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) -{ - S390CCWDevice *cdev = S390_CCW_DEVICE(vcdev); - char *name = g_strdup_printf("%x.%x.%04x", cdev->hostid.cssid, - cdev->hostid.ssid, - 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, 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 = DEVICE(vcdev); - - 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; 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_device_attach(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; @@ -708,15 +617,13 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) 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_device_detach(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) @@ -724,24 +631,27 @@ static void vfio_ccw_unrealize(DeviceState *dev) 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_device_detach(&vcdev->vdev); + g_free(vcdev->vdev.name); if (cdc->unrealize) { cdc->unrealize(cdev); } } -static Property vfio_ccw_properties[] = { +static const Property vfio_ccw_properties[] = { DEFINE_PROP_STRING("sysfsdev", VFIOCCWDevice, vdev.sysfsdev), DEFINE_PROP_BOOL("force-orb-pfch", VFIOCCWDevice, force_orb_pfch, false), - DEFINE_PROP_END_OF_LIST(), +#ifdef CONFIG_IOMMUFD + DEFINE_PROP_LINK("iommufd", VFIOCCWDevice, vdev.iommufd, + TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), +#endif + DEFINE_PROP_CCW_LOADPARM("loadparm", CcwDevice, loadparm), }; static const VMStateDescription vfio_ccw_vmstate = { @@ -749,29 +659,75 @@ static const VMStateDescription vfio_ccw_vmstate = { .unmigratable = 1, }; -static void vfio_ccw_class_init(ObjectClass *klass, void *data) +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, const 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; cdc->handle_clear = vfio_ccw_handle_clear; cdc->handle_store = vfio_ccw_handle_store; + + object_class_property_set_description(klass, /* 2.10 */ + "sysfsdev", + "Host sysfs path of assigned device"); + object_class_property_set_description(klass, /* 3.0 */ + "force-orb-pfch", + "Force unlimited prefetch"); +#ifdef CONFIG_IOMMUFD + object_class_property_set_description(klass, /* 9.0 */ + "iommufd", + "Set host IOMMUFD backend device"); +#endif + object_class_property_set_description(klass, /* 9.2 */ + "loadparm", + "Define which devices that can be used for booting"); } 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 deleted file mode 100644 index 134649226d4..00000000000 --- a/hw/vfio/common.c +++ /dev/null @@ -1,3156 +0,0 @@ -/* - * 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 -#ifdef CONFIG_KVM -#include -#endif -#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" -#include "hw/hw.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" -#include "qemu/range.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" -#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); -static QLIST_HEAD(, VFIOAddressSpace) vfio_address_spaces = - QLIST_HEAD_INITIALIZER(vfio_address_spaces); - -#ifdef CONFIG_KVM -/* - * We have a single VFIO pseudo device per KVM VM. Once created it lives - * for the life of the VM. Closing the file descriptor only drops our - * reference to it and the device's reference to kvm. Therefore once - * 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; -#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 - */ - -typedef struct { - unsigned long *bitmap; - hwaddr size; - hwaddr pages; -} VFIOBitmap; - -static 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; -} - -static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova, - uint64_t size, ram_addr_t ram_addr); - -bool vfio_mig_active(void) -{ - VFIOGroup *group; - VFIODevice *vbasedev; - - if (QLIST_EMPTY(&vfio_group_list)) { - return false; - } - - QLIST_FOREACH(group, &vfio_group_list, next) { - QLIST_FOREACH(vbasedev, &group->device_list, next) { - if (vbasedev->migration_blocker) { - return false; - } - } - } - return true; -} - -static Error *multiple_devices_migration_blocker; - -/* - * Multiple devices migration is allowed only if all devices support P2P - * migration. Single device migration is allowed regardless of P2P migration - * support. - */ -static bool vfio_multiple_devices_migration_is_supported(void) -{ - VFIOGroup *group; - VFIODevice *vbasedev; - unsigned int device_num = 0; - bool all_support_p2p = true; - - QLIST_FOREACH(group, &vfio_group_list, next) { - QLIST_FOREACH(vbasedev, &group->device_list, next) { - if (vbasedev->migration) { - device_num++; - - if (!(vbasedev->migration->mig_flags & VFIO_MIGRATION_P2P)) { - all_support_p2p = false; - } - } - } - } - - return all_support_p2p || device_num <= 1; -} - -int vfio_block_multiple_devices_migration(VFIODevice *vbasedev, Error **errp) -{ - int ret; - - if (vfio_multiple_devices_migration_is_supported()) { - return 0; - } - - 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(multiple_devices_migration_blocker, errp); - if (ret < 0) { - error_free(multiple_devices_migration_blocker); - multiple_devices_migration_blocker = NULL; - } - - return ret; -} - -void vfio_unblock_multiple_devices_migration(void) -{ - if (!multiple_devices_migration_blocker || - !vfio_multiple_devices_migration_is_supported()) { - return; - } - - migrate_del_blocker(multiple_devices_migration_blocker); - error_free(multiple_devices_migration_blocker); - multiple_devices_migration_blocker = NULL; -} - -bool vfio_viommu_preset(VFIODevice *vbasedev) -{ - return vbasedev->group->container->space->as != &address_space_memory; -} - -static void vfio_set_migration_error(int err) -{ - MigrationState *ms = migrate_get_current(); - - if (migration_is_setup_or_active(ms->state)) { - WITH_QEMU_LOCK_GUARD(&ms->qemu_file_lock) { - if (ms->to_dst_file) { - qemu_file_set_error(ms->to_dst_file, err); - } - } - } -} - -bool vfio_device_state_is_running(VFIODevice *vbasedev) -{ - VFIOMigration *migration = vbasedev->migration; - - return migration->device_state == VFIO_DEVICE_STATE_RUNNING || - migration->device_state == VFIO_DEVICE_STATE_RUNNING_P2P; -} - -bool vfio_device_state_is_precopy(VFIODevice *vbasedev) -{ - VFIOMigration *migration = vbasedev->migration; - - return migration->device_state == VFIO_DEVICE_STATE_PRE_COPY || - migration->device_state == VFIO_DEVICE_STATE_PRE_COPY_P2P; -} - -static bool vfio_devices_all_dirty_tracking(VFIOContainer *container) -{ - VFIOGroup *group; - VFIODevice *vbasedev; - MigrationState *ms = migrate_get_current(); - - if (ms->state != MIGRATION_STATUS_ACTIVE && - ms->state != MIGRATION_STATUS_DEVICE) { - 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 && - (vfio_device_state_is_running(vbasedev) || - vfio_device_state_is_precopy(vbasedev))) { - return false; - } - } - } - return true; -} - -static bool vfio_devices_all_device_dirty_tracking(VFIOContainer *container) -{ - VFIOGroup *group; - VFIODevice *vbasedev; - - QLIST_FOREACH(group, &container->group_list, container_next) { - QLIST_FOREACH(vbasedev, &group->device_list, next) { - if (!vbasedev->dirty_pages_supported) { - return false; - } - } - } - - return true; -} - -/* - * Check if all VFIO devices are running and migration is active, which is - * essentially equivalent to the migration being in pre-copy phase. - */ -static bool vfio_devices_all_running_and_mig_active(VFIOContainer *container) -{ - VFIOGroup *group; - VFIODevice *vbasedev; - - if (!migration_is_active(migrate_get_current())) { - 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 (vfio_device_state_is_running(vbasedev) || - vfio_device_state_is_precopy(vbasedev)) { - 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; - 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 > container->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_dma_unmap(VFIOContainer *container, - hwaddr iova, ram_addr_t size, - IOMMUTLBEntry *iotlb) -{ - struct vfio_iommu_type1_dma_unmap unmap = { - .argsz = sizeof(unmap), - .flags = 0, - .iova = iova, - .size = size, - }; - bool need_dirty_sync = false; - int ret; - - if (iotlb && vfio_devices_all_running_and_mig_active(container)) { - if (!vfio_devices_all_device_dirty_tracking(container) && - container->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_dma_unmap_overflow_workaround(); - unmap.size -= 1ULL << ctz64(container->pgsizes); - continue; - } - error_report("VFIO_UNMAP_DMA failed: %s", strerror(errno)); - return -errno; - } - - if (need_dirty_sync) { - ret = vfio_get_dirty_bitmap(container, iova, size, - iotlb->translated_addr); - if (ret) { - return ret; - } - } - - return 0; -} - -static int vfio_dma_map(VFIOContainer *container, hwaddr iova, - ram_addr_t size, void *vaddr, bool readonly) -{ - 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_dma_unmap(container, 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 void vfio_host_win_add(VFIOContainer *container, - hwaddr min_iova, hwaddr max_iova, - uint64_t iova_pgsizes) -{ - VFIOHostDMAWindow *hostwin; - - 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__); - } - } - - 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); -} - -static int vfio_host_win_del(VFIOContainer *container, hwaddr min_iova, - hwaddr max_iova) -{ - VFIOHostDMAWindow *hostwin; - - 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; - } - } - - return -1; -} - -static bool vfio_listener_skipped_section(MemoryRegionSection *section) -{ - return (!memory_region_is_ram(section->mr) && - !memory_region_is_iommu(section->mr)) || - memory_region_is_protected(section->mr) || - /* - * Sizing an enabled 64-bit BAR can cause spurious mappings to - * addresses in the upper part of the 64-bit address space. These - * are never accessed by the CPU and beyond the address width of - * some IOMMU hardware. TODO: VFIO should tell us the IOMMU width. - */ - section->offset_within_address_space & (1ULL << 63); -} - -/* Called with rcu_read_lock held. */ -static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, - ram_addr_t *ram_addr, bool *read_only) -{ - bool ret, mr_has_discard_manager; - - ret = memory_get_xlat_addr(iotlb, vaddr, ram_addr, read_only, - &mr_has_discard_manager); - if (ret && mr_has_discard_manager) { - /* - * Malicious VMs might trigger discarding of IOMMU-mapped memory. The - * pages will remain pinned inside vfio until unmapped, resulting in a - * higher memory consumption than expected. If memory would get - * populated again later, there would be an inconsistency between pages - * pinned by vfio and pages seen by QEMU. This is the case until - * unmapped from the IOMMU (e.g., during device reset). - * - * With malicious guests, we really only care about pinning more memory - * than expected. RLIMIT_MEMLOCK set for the user/process can never be - * exceeded and can be used to mitigate this problem. - */ - warn_report_once("Using vfio with vIOMMUs and coordinated discarding of" - " RAM (e.g., virtio-mem) works, however, malicious" - " guests can trigger pinning of more memory than" - " intended via an IOMMU. It's possible to mitigate " - " by setting/adjusting RLIMIT_MEMLOCK."); - } - return ret; -} - -static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) -{ - VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n); - VFIOContainer *container = giommu->container; - hwaddr iova = iotlb->iova + giommu->iommu_offset; - void *vaddr; - int ret; - - trace_vfio_iommu_map_notify(iotlb->perm == IOMMU_NONE ? "UNMAP" : "MAP", - 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"); - vfio_set_migration_error(-EINVAL); - return; - } - - rcu_read_lock(); - - if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) { - bool read_only; - - if (!vfio_get_xlat_addr(iotlb, &vaddr, NULL, &read_only)) { - goto out; - } - /* - * vaddr is only valid until rcu_read_unlock(). But after - * vfio_dma_map has set up the mapping the pages will be - * pinned by the kernel. This makes sure that the RAM backend - * 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); - if (ret) { - error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx", %p) = %d (%s)", - container, iova, - iotlb->addr_mask + 1, vaddr, ret, strerror(-ret)); - } - } else { - ret = vfio_dma_unmap(container, iova, iotlb->addr_mask + 1, iotlb); - if (ret) { - error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%s)", - container, iova, - iotlb->addr_mask + 1, ret, strerror(-ret)); - vfio_set_migration_error(ret); - } - } -out: - rcu_read_unlock(); -} - -static void vfio_ram_discard_notify_discard(RamDiscardListener *rdl, - MemoryRegionSection *section) -{ - VFIORamDiscardListener *vrdl = container_of(rdl, VFIORamDiscardListener, - listener); - 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); - if (ret) { - error_report("%s: vfio_dma_unmap() failed: %s", __func__, - strerror(-ret)); - } -} - -static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl, - MemoryRegionSection *section) -{ - VFIORamDiscardListener *vrdl = container_of(rdl, VFIORamDiscardListener, - listener); - const hwaddr end = section->offset_within_region + - int128_get64(section->size); - hwaddr start, next, iova; - void *vaddr; - int ret; - - /* - * Map in (aligned within memory region) minimum granularity, so we can - * unmap in minimum granularity later. - */ - for (start = section->offset_within_region; start < end; start = next) { - next = ROUND_UP(start + 1, vrdl->granularity); - next = MIN(next, end); - - iova = start - section->offset_within_region + - 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); - if (ret) { - /* Rollback */ - vfio_ram_discard_notify_discard(rdl, section); - return ret; - } - } - return 0; -} - -static void vfio_register_ram_discard_listener(VFIOContainer *container, - MemoryRegionSection *section) -{ - RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); - VFIORamDiscardListener *vrdl; - - /* Ignore some corner cases not relevant in practice. */ - g_assert(QEMU_IS_ALIGNED(section->offset_within_region, TARGET_PAGE_SIZE)); - g_assert(QEMU_IS_ALIGNED(section->offset_within_address_space, - TARGET_PAGE_SIZE)); - g_assert(QEMU_IS_ALIGNED(int128_get64(section->size), TARGET_PAGE_SIZE)); - - vrdl = g_new0(VFIORamDiscardListener, 1); - vrdl->container = container; - vrdl->mr = section->mr; - vrdl->offset_within_address_space = section->offset_within_address_space; - vrdl->size = int128_get64(section->size); - vrdl->granularity = ram_discard_manager_get_min_granularity(rdm, - section->mr); - - g_assert(vrdl->granularity && is_power_of_2(vrdl->granularity)); - g_assert(container->pgsizes && - vrdl->granularity >= 1ULL << ctz64(container->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); - - /* - * Sanity-check if we have a theoretically problematic setup where we could - * exceed the maximum number of possible DMA mappings over time. We assume - * that each mapped section in the same address space as a RamDiscardManager - * section consumes exactly one DMA mapping, with the exception of - * RamDiscardManager sections; i.e., we don't expect to have gIOMMU sections - * in the same address space as RamDiscardManager sections. - * - * We assume that each section in the address space consumes one memslot. - * We take the number of KVM memory slots as a best guess for the maximum - * number of sections in the address space we could have over time, - * also consuming DMA mappings. - */ - if (container->dma_max_mappings) { - unsigned int vrdl_count = 0, vrdl_mappings = 0, max_memslots = 512; - -#ifdef CONFIG_KVM - if (kvm_enabled()) { - max_memslots = kvm_get_max_memslots(); - } -#endif - - QLIST_FOREACH(vrdl, &container->vrdl_list, next) { - hwaddr start, end; - - start = QEMU_ALIGN_DOWN(vrdl->offset_within_address_space, - vrdl->granularity); - end = ROUND_UP(vrdl->offset_within_address_space + vrdl->size, - vrdl->granularity); - vrdl_mappings += (end - start) / vrdl->granularity; - vrdl_count++; - } - - if (vrdl_mappings + max_memslots - vrdl_count > - container->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, - max_memslots); - } - } -} - -static void vfio_unregister_ram_discard_listener(VFIOContainer *container, - MemoryRegionSection *section) -{ - RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); - VFIORamDiscardListener *vrdl = NULL; - - QLIST_FOREACH(vrdl, &container->vrdl_list, next) { - if (vrdl->mr == section->mr && - vrdl->offset_within_address_space == - section->offset_within_address_space) { - break; - } - } - - if (!vrdl) { - hw_error("vfio: Trying to unregister missing RAM discard listener"); - } - - ram_discard_manager_unregister_listener(rdm, &vrdl->listener); - QLIST_REMOVE(vrdl, next); - g_free(vrdl); -} - -static VFIOHostDMAWindow *vfio_find_hostwin(VFIOContainer *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 bool vfio_known_safe_misalignment(MemoryRegionSection *section) -{ - MemoryRegion *mr = section->mr; - - if (!TPM_IS_CRB(mr->owner)) { - return false; - } - - /* this is a known safe misaligned region, just trace for debug purpose */ - trace_vfio_known_safe_misalignment(memory_region_name(mr), - section->offset_within_address_space, - section->offset_within_region, - qemu_real_host_page_size()); - return true; -} - -static bool vfio_listener_valid_section(MemoryRegionSection *section, - const char *name) -{ - if (vfio_listener_skipped_section(section)) { - 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 false; - } - - 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()); - } - return false; - } - - return true; -} - -static bool vfio_get_section_iova_range(VFIOContainer *container, - 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) -{ - VFIOContainer *container = container_of(listener, VFIOContainer, listener); - hwaddr iova, end; - Int128 llend, llsize; - void *vaddr; - int ret; - VFIOHostDMAWindow *hostwin; - Error *err = NULL; - - if (!vfio_listener_valid_section(section, "region_add")) { - return; - } - - if (!vfio_get_section_iova_range(container, 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), - section->offset_within_address_space, - int128_getlo(section->size), - qemu_real_host_page_size()); - } - return; - } - - 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 = vfio_find_hostwin(container, iova, end); - if (!hostwin) { - error_setg(&err, "Container %p can't map guest IOVA region" - " 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx, container, iova, end); - goto fail; - } - - memory_region_ref(section->mr); - - if (memory_region_is_iommu(section->mr)) { - VFIOGuestIOMMU *giommu; - IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); - int iommu_idx; - - trace_vfio_listener_region_add_iommu(iova, end); - /* - * FIXME: For VFIO iommu types which have KVM acceleration to - * avoid bouncing all map/unmaps through qemu this way, this - * would be the right place to wire that up (tell the KVM - * device emulation the VFIO iommu handles to use). - */ - giommu = g_malloc0(sizeof(*giommu)); - giommu->iommu_mr = iommu_mr; - giommu->iommu_offset = section->offset_within_address_space - - section->offset_within_region; - giommu->container = container; - llend = int128_add(int128_make64(section->offset_within_region), - section->size); - llend = int128_sub(llend, int128_one()); - iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr, - MEMTXATTRS_UNSPECIFIED); - iommu_notifier_init(&giommu->n, vfio_iommu_map_notify, - IOMMU_NOTIFIER_IOTLB_EVENTS, - section->offset_within_region, - 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); - memory_region_iommu_replay(giommu->iommu_mr, &giommu->n); - - return; - } - - /* Here we assume that memory_region_is_ram(section->mr)==true */ - - /* - * For RAM memory regions with a RamDiscardManager, we only want to map the - * actually populated parts - and update the mapping whenever we're notified - * about changes. - */ - if (memory_region_has_ram_discard_manager(section->mr)) { - vfio_register_ram_discard_listener(container, section); - return; - } - - vaddr = memory_region_get_ram_ptr(section->mr) + - section->offset_within_region + - (iova - section->offset_within_address_space); - - trace_vfio_listener_region_add_ram(iova, end, vaddr); - - llsize = int128_sub(llend, int128_make64(iova)); - - if (memory_region_is_ram_device(section->mr)) { - hwaddr pgmask = (1ULL << ctz64(hostwin->iova_pgsizes)) - 1; - - if ((iova & pgmask) || (int128_get64(llsize) & pgmask)) { - trace_vfio_listener_region_add_no_dma_map( - memory_region_name(section->mr), - section->offset_within_address_space, - int128_getlo(section->size), - pgmask + 1); - return; - } - } - - ret = vfio_dma_map(container, iova, int128_get64(llsize), - vaddr, section->readonly); - if (ret) { - error_setg(&err, "vfio_dma_map(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx", %p) = %d (%s)", - container, 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); - return; - } - goto fail; - } - - return; - -fail: - if (memory_region_is_ram_device(section->mr)) { - error_report("failed to vfio_dma_map. pci p2p may not work"); - return; - } - /* - * On the initfn path, store the first error in the container so we - * 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, - "Region %s: ", - memory_region_name(section->mr)); - } else { - error_free(err); - } - } else { - error_report_err(err); - hw_error("vfio: DMA mapping failed, unable to continue"); - } -} - -static void vfio_listener_region_del(MemoryListener *listener, - MemoryRegionSection *section) -{ - VFIOContainer *container = container_of(listener, VFIOContainer, listener); - hwaddr iova, end; - Int128 llend, llsize; - int ret; - bool try_unmap = true; - - 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) { - if (MEMORY_REGION(giommu->iommu_mr) == section->mr && - giommu->n.start == section->offset_within_region) { - memory_region_unregister_iommu_notifier(section->mr, - &giommu->n); - QLIST_REMOVE(giommu, giommu_next); - g_free(giommu); - break; - } - } - - /* - * FIXME: We assume the one big unmap below is adequate to - * remove any individual page mappings in the IOMMU which - * might have been copied into VFIO. This works for a page table - * based IOMMU where a big unmap flattens a large range of IO-PTEs. - * That may not be true for all IOMMU types. - */ - } - - if (!vfio_get_section_iova_range(container, section, &iova, &end, &llend)) { - return; - } - - llsize = int128_sub(llend, int128_make64(iova)); - - trace_vfio_listener_region_del(iova, end); - - if (memory_region_is_ram_device(section->mr)) { - hwaddr pgmask; - VFIOHostDMAWindow *hostwin; - - hostwin = vfio_find_hostwin(container, iova, end); - assert(hostwin); /* or region_add() would have failed */ - - pgmask = (1ULL << ctz64(hostwin->iova_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); - /* Unregistering will trigger an unmap. */ - try_unmap = false; - } - - if (try_unmap) { - 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); - if (ret) { - error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%s)", - container, iova, int128_get64(llsize), ret, - strerror(-ret)); - } - iova += int128_get64(llsize); - } - ret = vfio_dma_unmap(container, iova, int128_get64(llsize), NULL); - if (ret) { - error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%s)", - container, 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); - } - } -} - -static int vfio_set_dirty_page_tracking(VFIOContainer *container, bool start) -{ - int ret; - struct vfio_iommu_type1_dirty_bitmap dirty = { - .argsz = sizeof(dirty), - }; - - if (!container->dirty_pages_supported) { - return 0; - } - - if (start) { - dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_START; - } else { - dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP; - } - - ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, &dirty); - if (ret) { - ret = -errno; - error_report("Failed to set dirty tracking flag 0x%x errno: %d", - dirty.flags, errno); - } - - return ret; -} - -typedef struct VFIODirtyRanges { - hwaddr min32; - hwaddr max32; - hwaddr min64; - hwaddr max64; - hwaddr minpci64; - hwaddr maxpci64; -} VFIODirtyRanges; - -typedef struct VFIODirtyRangesListener { - VFIOContainer *container; - VFIODirtyRanges ranges; - MemoryListener listener; -} VFIODirtyRangesListener; - -static bool vfio_section_is_vfio_pci(MemoryRegionSection *section, - VFIOContainer *container) -{ - VFIOPCIDevice *pcidev; - VFIODevice *vbasedev; - VFIOGroup *group; - Object *owner; - - owner = memory_region_owner(section->mr); - - QLIST_FOREACH(group, &container->group_list, container_next) { - QLIST_FOREACH(vbasedev, &group->device_list, next) { - if (vbasedev->type != VFIO_DEVICE_TYPE_PCI) { - continue; - } - pcidev = container_of(vbasedev, VFIOPCIDevice, vbasedev); - if (OBJECT(pcidev) == owner) { - return true; - } - } - } - - return false; -} - -static void vfio_dirty_tracking_update(MemoryListener *listener, - MemoryRegionSection *section) -{ - VFIODirtyRangesListener *dirty = container_of(listener, - VFIODirtyRangesListener, - listener); - VFIODirtyRanges *range = &dirty->ranges; - hwaddr iova, end, *min, *max; - - if (!vfio_listener_valid_section(section, "tracking_update") || - !vfio_get_section_iova_range(dirty->container, section, - &iova, &end, NULL)) { - return; - } - - /* - * The address space passed to the dirty tracker is reduced to three ranges: - * one for 32-bit DMA ranges, one for 64-bit DMA ranges and one for the - * PCI 64-bit hole. - * - * The underlying reports of dirty will query a sub-interval of each of - * these ranges. - * - * The purpose of the three range handling is to handle known cases of big - * holes in the address space, like the x86 AMD 1T hole, and firmware (like - * OVMF) which may relocate the pci-hole64 to the end of the address space. - * The latter would otherwise generate large ranges for tracking, stressing - * the limits of supported hardware. The pci-hole32 will always be below 4G - * (overlapping or not) so it doesn't need special handling and is part of - * the 32-bit range. - * - * The alternative would be an IOVATree but that has a much bigger runtime - * overhead and unnecessary complexity. - */ - if (vfio_section_is_vfio_pci(section, dirty->container) && - iova >= UINT32_MAX) { - min = &range->minpci64; - max = &range->maxpci64; - } else { - min = (end <= UINT32_MAX) ? &range->min32 : &range->min64; - max = (end <= UINT32_MAX) ? &range->max32 : &range->max64; - } - if (*min > iova) { - *min = iova; - } - if (*max < end) { - *max = end; - } - - trace_vfio_device_dirty_tracking_update(iova, end, *min, *max); - return; -} - -static const MemoryListener vfio_dirty_tracking_listener = { - .name = "vfio-tracking", - .region_add = vfio_dirty_tracking_update, -}; - -static void vfio_dirty_tracking_init(VFIOContainer *container, - 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.container = container; - - memory_listener_register(&dirty.listener, - container->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(VFIOContainer *container) -{ - 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; - VFIOGroup *group; - - feature->argsz = sizeof(buf); - feature->flags = VFIO_DEVICE_FEATURE_SET | - VFIO_DEVICE_FEATURE_DMA_LOGGING_STOP; - - QLIST_FOREACH(group, &container->group_list, container_next) { - QLIST_FOREACH(vbasedev, &group->device_list, 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 struct vfio_device_feature * -vfio_device_feature_dma_logging_start_create(VFIOContainer *container, - VFIODirtyRanges *tracking) -{ - 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; - - 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 = (__u64)(uintptr_t)ranges; - if (tracking->max32) { - ranges->iova = tracking->min32; - ranges->length = (tracking->max32 - tracking->min32) + 1; - ranges++; - } - if (tracking->max64) { - ranges->iova = tracking->min64; - ranges->length = (tracking->max64 - tracking->min64) + 1; - ranges++; - } - if (tracking->maxpci64) { - ranges->iova = tracking->minpci64; - ranges->length = (tracking->maxpci64 - tracking->minpci64) + 1; - } - - trace_vfio_device_dirty_tracking_start(control->num_ranges, - tracking->min32, tracking->max32, - tracking->min64, tracking->max64, - tracking->minpci64, tracking->maxpci64); - - return feature; -} - -static void vfio_device_feature_dma_logging_start_destroy( - struct vfio_device_feature *feature) -{ - struct vfio_device_feature_dma_logging_control *control = - (struct vfio_device_feature_dma_logging_control *)feature->data; - struct vfio_device_feature_dma_logging_range *ranges = - (struct vfio_device_feature_dma_logging_range *)(uintptr_t)control->ranges; - - g_free(ranges); - g_free(feature); -} - -static int vfio_devices_dma_logging_start(VFIOContainer *container) -{ - struct vfio_device_feature *feature; - VFIODirtyRanges ranges; - VFIODevice *vbasedev; - VFIOGroup *group; - int ret = 0; - - vfio_dirty_tracking_init(container, &ranges); - feature = vfio_device_feature_dma_logging_start_create(container, - &ranges); - if (!feature) { - return -errno; - } - - QLIST_FOREACH(group, &container->group_list, container_next) { - QLIST_FOREACH(vbasedev, &group->device_list, next) { - if (vbasedev->dirty_tracking) { - continue; - } - - ret = ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature); - if (ret) { - ret = -errno; - error_report("%s: Failed to start DMA logging, err %d (%s)", - vbasedev->name, ret, strerror(errno)); - goto out; - } - vbasedev->dirty_tracking = true; - } - } - -out: - if (ret) { - vfio_devices_dma_logging_stop(container); - } - - vfio_device_feature_dma_logging_start_destroy(feature); - - return ret; -} - -static void vfio_listener_log_global_start(MemoryListener *listener) -{ - VFIOContainer *container = container_of(listener, VFIOContainer, listener); - int ret; - - if (vfio_devices_all_device_dirty_tracking(container)) { - ret = vfio_devices_dma_logging_start(container); - } else { - ret = vfio_set_dirty_page_tracking(container, true); - } - - if (ret) { - error_report("vfio: Could not start dirty page tracking, err: %d (%s)", - ret, strerror(-ret)); - vfio_set_migration_error(ret); - } -} - -static void vfio_listener_log_global_stop(MemoryListener *listener) -{ - VFIOContainer *container = container_of(listener, VFIOContainer, listener); - int ret = 0; - - if (vfio_devices_all_device_dirty_tracking(container)) { - vfio_devices_dma_logging_stop(container); - } else { - ret = vfio_set_dirty_page_tracking(container, false); - } - - if (ret) { - error_report("vfio: Could not stop dirty page tracking, err: %d (%s)", - ret, strerror(-ret)); - vfio_set_migration_error(ret); - } -} - -static int vfio_device_dma_logging_report(VFIODevice *vbasedev, hwaddr iova, - hwaddr size, void *bitmap) -{ - uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature) + - sizeof(struct vfio_device_feature_dma_logging_report), - sizeof(__u64))] = {}; - 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 = (__u64)(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; -} - -static int vfio_devices_query_dirty_bitmap(VFIOContainer *container, - VFIOBitmap *vbmap, hwaddr iova, - hwaddr size) -{ - VFIODevice *vbasedev; - VFIOGroup *group; - int ret; - - QLIST_FOREACH(group, &container->group_list, container_next) { - QLIST_FOREACH(vbasedev, &group->device_list, next) { - ret = vfio_device_dma_logging_report(vbasedev, iova, size, - vbmap->bitmap); - if (ret) { - error_report("%s: Failed to get DMA logging report, iova: " - "0x%" HWADDR_PRIx ", size: 0x%" HWADDR_PRIx - ", err: %d (%s)", - vbasedev->name, iova, size, ret, strerror(-ret)); - - return ret; - } - } - } - - return 0; -} - -static int vfio_query_dirty_bitmap(VFIOContainer *container, VFIOBitmap *vbmap, - hwaddr iova, hwaddr size) -{ - struct vfio_iommu_type1_dirty_bitmap *dbitmap; - struct vfio_iommu_type1_dirty_bitmap_get *range; - int ret; - - dbitmap = g_malloc0(sizeof(*dbitmap) + sizeof(*range)); - - dbitmap->argsz = sizeof(*dbitmap) + sizeof(*range); - dbitmap->flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP; - range = (struct vfio_iommu_type1_dirty_bitmap_get *)&dbitmap->data; - range->iova = iova; - range->size = size; - - /* - * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of - * qemu_real_host_page_size to mark those dirty. Hence set bitmap's pgsize - * to qemu_real_host_page_size. - */ - range->bitmap.pgsize = qemu_real_host_page_size(); - range->bitmap.size = vbmap->size; - range->bitmap.data = (__u64 *)vbmap->bitmap; - - ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, dbitmap); - if (ret) { - ret = -errno; - error_report("Failed to get dirty bitmap for iova: 0x%"PRIx64 - " size: 0x%"PRIx64" err: %d", (uint64_t)range->iova, - (uint64_t)range->size, errno); - } - - g_free(dbitmap); - - return ret; -} - -static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova, - uint64_t size, ram_addr_t ram_addr) -{ - bool all_device_dirty_tracking = - vfio_devices_all_device_dirty_tracking(container); - uint64_t dirty_pages; - VFIOBitmap vbmap; - int ret; - - if (!container->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) { - return ret; - } - - if (all_device_dirty_tracking) { - ret = vfio_devices_query_dirty_bitmap(container, &vbmap, iova, size); - } else { - ret = vfio_query_dirty_bitmap(container, &vbmap, iova, size); - } - - if (ret) { - goto out; - } - - dirty_pages = cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, ram_addr, - vbmap.pages); - - trace_vfio_get_dirty_bitmap(container->fd, iova, size, vbmap.size, - ram_addr, dirty_pages); -out: - g_free(vbmap.bitmap); - - return ret; -} - -typedef struct { - IOMMUNotifier n; - VFIOGuestIOMMU *giommu; -} vfio_giommu_dirty_notifier; - -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; - hwaddr iova = iotlb->iova + giommu->iommu_offset; - ram_addr_t translated_addr; - int ret = -EINVAL; - - trace_vfio_iommu_map_dirty_notify(iova, iova + iotlb->addr_mask); - - if (iotlb->target_as != &address_space_memory) { - error_report("Wrong target AS \"%s\", only system memory is allowed", - iotlb->target_as->name ? iotlb->target_as->name : "none"); - goto out; - } - - rcu_read_lock(); - if (vfio_get_xlat_addr(iotlb, NULL, &translated_addr, NULL)) { - 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 (%s)", - container, iova, iotlb->addr_mask + 1, ret, - strerror(-ret)); - } - } - rcu_read_unlock(); - -out: - if (ret) { - vfio_set_migration_error(ret); - } -} - -static int vfio_ram_discard_get_dirty_bitmap(MemoryRegionSection *section, - void *opaque) -{ - const hwaddr size = int128_get64(section->size); - const hwaddr iova = section->offset_within_address_space; - const ram_addr_t ram_addr = memory_region_get_ram_addr(section->mr) + - section->offset_within_region; - VFIORamDiscardListener *vrdl = opaque; - - /* - * Sync the whole mapped region (spanning multiple individual mappings) - * in one go. - */ - return vfio_get_dirty_bitmap(vrdl->container, iova, size, ram_addr); -} - -static int vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainer *container, - MemoryRegionSection *section) -{ - RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); - VFIORamDiscardListener *vrdl = NULL; - - QLIST_FOREACH(vrdl, &container->vrdl_list, next) { - if (vrdl->mr == section->mr && - vrdl->offset_within_address_space == - section->offset_within_address_space) { - break; - } - } - - if (!vrdl) { - hw_error("vfio: Trying to sync missing RAM discard listener"); - } - - /* - * We only want/can synchronize the bitmap for actually mapped parts - - * which correspond to populated parts. Replay all populated parts. - */ - return ram_discard_manager_replay_populated(rdm, section, - vfio_ram_discard_get_dirty_bitmap, - &vrdl); -} - -static int vfio_sync_dirty_bitmap(VFIOContainer *container, - MemoryRegionSection *section) -{ - 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; - } else if (memory_region_has_ram_discard_manager(section->mr)) { - return vfio_sync_ram_discard_listener_dirty_bitmap(container, section); - } - - ram_addr = memory_region_get_ram_addr(section->mr) + - section->offset_within_region; - - return vfio_get_dirty_bitmap(container, - REAL_HOST_PAGE_ALIGN(section->offset_within_address_space), - int128_get64(section->size), ram_addr); -} - -static void vfio_listener_log_sync(MemoryListener *listener, - MemoryRegionSection *section) -{ - VFIOContainer *container = container_of(listener, VFIOContainer, listener); - int ret; - - if (vfio_listener_skipped_section(section)) { - return; - } - - if (vfio_devices_all_dirty_tracking(container)) { - ret = vfio_sync_dirty_bitmap(container, section); - if (ret) { - error_report("vfio: Failed to sync dirty bitmap, err: %d (%s)", ret, - strerror(-ret)); - vfio_set_migration_error(ret); - } - } -} - -static const MemoryListener vfio_memory_listener = { - .name = "vfio", - .region_add = vfio_listener_region_add, - .region_del = vfio_listener_region_del, - .log_global_start = vfio_listener_log_global_start, - .log_global_stop = vfio_listener_log_global_stop, - .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(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); - } - } - } -} - -static void vfio_kvm_device_add_group(VFIOGroup *group) -{ -#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, - }; - - if (!kvm_enabled()) { - return; - } - - if (vfio_kvm_device_fd < 0) { - struct kvm_create_device cd = { - .type = KVM_DEV_TYPE_VFIO, - }; - - if (kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd)) { - error_report("Failed to create KVM VFIO device: %m"); - return; - } - - 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); - } -#endif -} - -static void vfio_kvm_device_del_group(VFIOGroup *group) -{ -#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, - }; - - if (vfio_kvm_device_fd < 0) { - return; - } - - 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); - } -#endif -} - -static VFIOAddressSpace *vfio_get_address_space(AddressSpace *as) -{ - VFIOAddressSpace *space; - - QLIST_FOREACH(space, &vfio_address_spaces, list) { - if (space->as == as) { - return space; - } - } - - /* No suitable VFIOAddressSpace, create a new one */ - space = g_malloc0(sizeof(*space)); - space->as = as; - QLIST_INIT(&space->containers); - - QLIST_INSERT_HEAD(&vfio_address_spaces, space, list); - - return space; -} - -static 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) { - 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()) { - 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)) { - qemu_unregister_reset(vfio_reset_handler, NULL); - } -} - -struct vfio_device_info *vfio_get_device_info(int fd) -{ - struct vfio_device_info *info; - uint32_t argsz = sizeof(*info); - - info = g_malloc0(argsz); - -retry: - info->argsz = argsz; - - 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); - goto retry; - } - - return info; -} - -int vfio_get_device(VFIOGroup *group, const char *name, - VFIODevice *vbasedev, Error **errp) -{ - g_autofree struct vfio_device_info *info = NULL; - int fd; - - fd = ioctl(group->fd, VFIO_GROUP_GET_DEVICE_FD, name); - if (fd < 0) { - error_setg_errno(errp, errno, "error getting device from group %d", - group->groupid); - error_append_hint(errp, - "Verify all devices in group %d are bound to vfio- " - "or pci-stub and not already in use\n", group->groupid); - return fd; - } - - info = vfio_get_device_info(fd); - if (!info) { - error_setg_errno(errp, errno, "error getting device info"); - close(fd); - return -1; - } - - /* - * Set discarding of RAM as not broken for this group if the driver knows - * the device operates compatibly with discarding. Setting must be - * consistent per group, but since compatibility is really only possible - * with mdev currently, we expect singleton groups. - */ - if (vbasedev->ram_block_discard_allowed != - group->ram_block_discard_allowed) { - if (!QLIST_EMPTY(&group->device_list)) { - error_setg(errp, "Inconsistent setting of support for discarding " - "RAM (e.g., balloon) within group"); - close(fd); - return -1; - } - - if (!group->ram_block_discard_allowed) { - group->ram_block_discard_allowed = true; - vfio_ram_block_discard_disable(group->container, false); - } - } - - vbasedev->fd = fd; - vbasedev->group = group; - QLIST_INSERT_HEAD(&group->device_list, vbasedev, next); - - vbasedev->num_irqs = info->num_irqs; - vbasedev->num_regions = info->num_regions; - vbasedev->flags = info->flags; - - trace_vfio_get_device(name, info->flags, info->num_regions, info->num_irqs); - - vbasedev->reset_works = !!(info->flags & VFIO_DEVICE_FLAGS_RESET); - - return 0; -} - -void vfio_put_base_device(VFIODevice *vbasedev) -{ - if (!vbasedev->group) { - return; - } - QLIST_REMOVE(vbasedev, next); - vbasedev->group = NULL; - trace_vfio_put_base_device(vbasedev->fd); - close(vbasedev->fd); -} - -int vfio_get_region_info(VFIODevice *vbasedev, int index, - struct vfio_region_info **info) -{ - size_t argsz = sizeof(struct vfio_region_info); - - *info = g_malloc0(argsz); - - (*info)->index = index; -retry: - (*info)->argsz = argsz; - - if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info)) { - g_free(*info); - *info = NULL; - return -errno; - } - - if ((*info)->argsz > argsz) { - argsz = (*info)->argsz; - *info = g_realloc(*info, argsz); - - goto retry; - } - - return 0; -} - -int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, - uint32_t subtype, struct vfio_region_info **info) -{ - int i; - - for (i = 0; i < vbasedev->num_regions; i++) { - struct vfio_info_cap_header *hdr; - struct vfio_region_info_cap_type *cap_type; - - if (vfio_get_region_info(vbasedev, i, info)) { - continue; - } - - hdr = vfio_get_region_info_cap(*info, VFIO_REGION_INFO_CAP_TYPE); - if (!hdr) { - g_free(*info); - continue; - } - - cap_type = container_of(hdr, struct vfio_region_info_cap_type, header); - - trace_vfio_get_dev_region(vbasedev->name, i, - cap_type->type, cap_type->subtype); - - if (cap_type->type == type && cap_type->subtype == subtype) { - return 0; - } - - g_free(*info); - } - - *info = NULL; - return -ENODEV; -} - -bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type) -{ - struct vfio_region_info *info = NULL; - bool ret = false; - - if (!vfio_get_region_info(vbasedev, region, &info)) { - if (vfio_get_region_info_cap(info, cap_type)) { - ret = true; - } - g_free(info); - } - - return ret; -} - -/* - * Interfaces for IBM EEH (Enhanced Error Handling) - */ -static bool vfio_eeh_container_ok(VFIOContainer *container) -{ - /* - * As of 2016-03-04 (linux-4.5) the host kernel EEH/VFIO - * implementation is broken if there are multiple groups in a - * container. The hardware works in units of Partitionable - * Endpoints (== IOMMU groups) and the EEH operations naively - * iterate across all groups in the container, without any logic - * to make sure the groups have their state synchronized. For - * certain operations (ENABLE) that might be ok, until an error - * occurs, but for others (GET_STATE) it's clearly broken. - */ - - /* - * XXX Once fixed kernels exist, test for them here - */ - - if (QLIST_EMPTY(&container->group_list)) { - return false; - } - - if (QLIST_NEXT(QLIST_FIRST(&container->group_list), container_next)) { - return false; - } - - return true; -} - -static int vfio_eeh_container_op(VFIOContainer *container, uint32_t op) -{ - struct vfio_eeh_pe_op pe_op = { - .argsz = sizeof(pe_op), - .op = op, - }; - int ret; - - if (!vfio_eeh_container_ok(container)) { - error_report("vfio/eeh: EEH_PE_OP 0x%x: " - "kernel requires a container with exactly one group", op); - return -EPERM; - } - - ret = ioctl(container->fd, VFIO_EEH_PE_OP, &pe_op); - if (ret < 0) { - error_report("vfio/eeh: EEH_PE_OP 0x%x failed: %m", op); - return -errno; - } - - return ret; -} - -static VFIOContainer *vfio_eeh_as_container(AddressSpace *as) -{ - VFIOAddressSpace *space = vfio_get_address_space(as); - VFIOContainer *container = NULL; - - if (QLIST_EMPTY(&space->containers)) { - /* No containers to act on */ - goto out; - } - - container = QLIST_FIRST(&space->containers); - - if (QLIST_NEXT(container, next)) { - /* We don't yet have logic to synchronize EEH state across - * multiple containers */ - container = NULL; - goto out; - } - -out: - vfio_put_address_space(space); - return container; -} - -bool vfio_eeh_as_ok(AddressSpace *as) -{ - VFIOContainer *container = vfio_eeh_as_container(as); - - return (container != NULL) && vfio_eeh_container_ok(container); -} - -int vfio_eeh_as_op(AddressSpace *as, uint32_t op) -{ - VFIOContainer *container = vfio_eeh_as_container(as); - - if (!container) { - return -ENODEV; - } - return vfio_eeh_container_op(container, op); -} diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c new file mode 100644 index 00000000000..d834bd48229 --- /dev/null +++ b/hw/vfio/container-base.c @@ -0,0 +1,338 @@ +/* + * 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 +#include + +#include "qemu/osdep.h" +#include "system/tcg.h" +#include "system/ram_addr.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "hw/vfio/vfio-container-base.h" +#include "hw/vfio/vfio-device.h" /* vfio_device_reset_handler */ +#include "system/reset.h" +#include "vfio-helpers.h" + +#include "trace.h" + +static QLIST_HEAD(, VFIOAddressSpace) vfio_address_spaces = + QLIST_HEAD_INITIALIZER(vfio_address_spaces); + +VFIOAddressSpace *vfio_address_space_get(AddressSpace *as) +{ + VFIOAddressSpace *space; + + QLIST_FOREACH(space, &vfio_address_spaces, list) { + if (space->as == as) { + return space; + } + } + + /* No suitable VFIOAddressSpace, create a new one */ + space = g_malloc0(sizeof(*space)); + space->as = as; + QLIST_INIT(&space->containers); + + if (QLIST_EMPTY(&vfio_address_spaces)) { + qemu_register_reset(vfio_device_reset_handler, NULL); + } + + QLIST_INSERT_HEAD(&vfio_address_spaces, space, list); + + return space; +} + +void vfio_address_space_put(VFIOAddressSpace *space) +{ + if (!QLIST_EMPTY(&space->containers)) { + return; + } + + QLIST_REMOVE(space, list); + g_free(space); + + if (QLIST_EMPTY(&vfio_address_spaces)) { + qemu_unregister_reset(vfio_device_reset_handler, NULL); + } +} + +void vfio_address_space_insert(VFIOAddressSpace *space, + VFIOContainerBase *bcontainer) +{ + QLIST_INSERT_HEAD(&space->containers, bcontainer, next); + bcontainer->space = space; +} + +int vfio_container_dma_map(VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + void *vaddr, bool readonly, MemoryRegion *mr) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + g_assert(vioc->dma_map); + return vioc->dma_map(bcontainer, iova, size, vaddr, readonly, mr); +} + +int vfio_container_dma_unmap(VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + IOMMUTLBEntry *iotlb, bool unmap_all) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + g_assert(vioc->dma_unmap); + return vioc->dma_unmap(bcontainer, iova, size, iotlb, unmap_all); +} + +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); + int ret; + + if (!bcontainer->dirty_pages_supported) { + return 0; + } + + g_assert(vioc->set_dirty_page_tracking); + if (bcontainer->dirty_pages_started == start) { + return 0; + } + + ret = vioc->set_dirty_page_tracking(bcontainer, start, errp); + if (!ret) { + bcontainer->dirty_pages_started = start; + } + + return ret; +} + +static bool vfio_container_devices_dirty_tracking_is_started( + const VFIOContainerBase *bcontainer) +{ + VFIODevice *vbasedev; + + QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { + if (!vbasedev->dirty_tracking) { + return false; + } + } + + return true; +} + +bool vfio_container_dirty_tracking_is_started( + const VFIOContainerBase *bcontainer) +{ + return vfio_container_devices_dirty_tracking_is_started(bcontainer) || + bcontainer->dirty_pages_started; +} + +bool vfio_container_devices_dirty_tracking_is_supported( + 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; + } + } + + return true; +} + +static int vfio_device_dma_logging_report(VFIODevice *vbasedev, hwaddr iova, + hwaddr size, void *bitmap) +{ + 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; + + return vbasedev->io_ops->device_feature(vbasedev, feature); +} + +static int vfio_container_iommu_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 int vfio_container_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) +{ + VFIODevice *vbasedev; + int ret; + + 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); + + return ret; + } + } + + return 0; +} + +int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, + uint64_t size, ram_addr_t ram_addr, Error **errp) +{ + bool all_device_dirty_tracking = + vfio_container_devices_dirty_tracking_is_supported(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_setg_errno(errp, -ret, + "Failed to allocate dirty tracking bitmap"); + return ret; + } + + if (all_device_dirty_tracking) { + ret = vfio_container_devices_query_dirty_bitmap(bcontainer, &vbmap, iova, size, + errp); + } else { + ret = vfio_container_iommu_query_dirty_bitmap(bcontainer, &vbmap, iova, size, + errp); + } + + if (ret) { + goto out; + } + + dirty_pages = cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, ram_addr, + vbmap.pages); + + trace_vfio_container_query_dirty_bitmap(iova, size, vbmap.size, ram_addr, + dirty_pages); +out: + g_free(vbmap.bitmap); + + return ret; +} + +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 00000000000..3e8d645ebbd --- /dev/null +++ b/hw/vfio/container.c @@ -0,0 +1,1274 @@ +/* + * 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-device.h" +#include "system/address-spaces.h" +#include "system/memory.h" +#include "system/ram_addr.h" +#include "qemu/error-report.h" +#include "qemu/range.h" +#include "system/reset.h" +#include "trace.h" +#include "qapi/error.h" +#include "migration/cpr.h" +#include "migration/blocker.h" +#include "pci.h" +#include "hw/vfio/vfio-container.h" +#include "vfio-helpers.h" +#include "vfio-listener.h" + +#define TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO TYPE_HOST_IOMMU_DEVICE "-legacy-vfio" + +typedef QLIST_HEAD(VFIOGroupList, VFIOGroup) VFIOGroupList; +static 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; +} + +static int vfio_legacy_dma_unmap_one(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; + + g_assert(!cpr_is_incoming()); + + if (iotlb && vfio_container_dirty_tracking_is_started(bcontainer)) { + if (!vfio_container_devices_dirty_tracking_is_supported(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; + } + return -errno; + } + + if (need_dirty_sync) { + ret = vfio_container_query_dirty_bitmap(bcontainer, iova, size, + iotlb->translated_addr, &local_err); + if (ret) { + error_report_err(local_err); + return ret; + } + } + + return 0; +} + +/* + * 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, bool unmap_all) +{ + int ret; + + if (unmap_all) { + /* The unmap ioctl doesn't accept a full 64-bit span. */ + Int128 llsize = int128_rshift(int128_2_64(), 1); + + ret = vfio_legacy_dma_unmap_one(bcontainer, 0, int128_get64(llsize), + iotlb); + + if (ret == 0) { + ret = vfio_legacy_dma_unmap_one(bcontainer, int128_get64(llsize), + int128_get64(llsize), iotlb); + } + + } else { + ret = vfio_legacy_dma_unmap_one(bcontainer, iova, size, iotlb); + } + + return ret; +} + +static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, + ram_addr_t size, void *vaddr, bool readonly, + MemoryRegion *mr) +{ + 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, false) == 0 && + ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map) == 0)) { + return 0; + } + + 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 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_group_add_kvm_device(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_group_del_kvm_device(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; + } + + /* + * During CPR, just set the container type and skip the ioctls, as the + * container and group are already configured in the kernel. + */ + if (!cpr_is_incoming() && + !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_container_attach_discard_disable(VFIOContainer *container, + VFIOGroup *group, Error **errp) +{ + int ret; + + /* + * 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. + */ + + 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; +} + +static bool vfio_container_group_add(VFIOContainer *container, VFIOGroup *group, + Error **errp) +{ + if (!vfio_container_attach_discard_disable(container, group, errp)) { + return false; + } + group->container = container; + QLIST_INSERT_HEAD(&container->group_list, group, container_next); + vfio_group_add_kvm_device(group); + /* + * Remember the container fd for each group, so we can attach to the same + * container after CPR. + */ + cpr_resave_fd("vfio_container_for_group", group->groupid, container->fd); + return true; +} + +static void vfio_container_group_del(VFIOContainer *container, VFIOGroup *group) +{ + QLIST_REMOVE(group, container_next); + group->container = NULL; + vfio_group_del_kvm_device(group); + vfio_ram_block_discard_disable(container, false); + cpr_delete_fd("vfio_container_for_group", group->groupid); +} + +static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, + Error **errp) +{ + VFIOContainer *container; + VFIOContainerBase *bcontainer; + int ret, fd = -1; + VFIOAddressSpace *space; + VFIOIOMMUClass *vioc = NULL; + bool new_container = false; + bool group_was_added = false; + + space = vfio_address_space_get(as); + fd = cpr_find_fd("vfio_container_for_group", group->groupid); + + if (!cpr_is_incoming()) { + QLIST_FOREACH(bcontainer, &space->containers, next) { + container = container_of(bcontainer, VFIOContainer, bcontainer); + if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) { + return vfio_container_group_add(container, group, errp); + } + } + + fd = qemu_open("/dev/vfio/vfio", O_RDWR, errp); + if (fd < 0) { + goto fail; + } + } else { + /* + * For incoming CPR, the group is already attached in the kernel. + * If a container with matching fd is found, then update the + * userland group list and return. If not, then after the loop, + * create the container struct and group list. + */ + QLIST_FOREACH(bcontainer, &space->containers, next) { + container = container_of(bcontainer, VFIOContainer, bcontainer); + + if (vfio_cpr_container_match(container, group, fd)) { + return vfio_container_group_add(container, group, errp); + } + } + } + + 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 fail; + } + + container = vfio_create_container(fd, group, errp); + if (!container) { + goto fail; + } + new_container = true; + bcontainer = &container->bcontainer; + + if (!vfio_legacy_cpr_register_container(container, errp)) { + goto fail; + } + + vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + assert(vioc->setup); + + if (!vioc->setup(bcontainer, errp)) { + goto fail; + } + + vfio_address_space_insert(space, bcontainer); + + if (!vfio_container_group_add(container, group, errp)) { + goto fail; + } + group_was_added = true; + + /* + * If CPR, register the listener later, after all state that may + * affect regions and mapping boundaries has been cpr load'ed. Later, + * the listener will invoke its callback on each flat section and call + * dma_map to supply the new vaddr, and the calls will match the mappings + * remembered by the kernel. + */ + if (!cpr_is_incoming()) { + if (!vfio_listener_register(bcontainer, errp)) { + goto fail; + } + } + + bcontainer->initialized = true; + + return true; + +fail: + vfio_listener_unregister(bcontainer); + + if (group_was_added) { + vfio_container_group_del(container, group); + } + if (vioc && vioc->release) { + vioc->release(bcontainer); + } + if (new_container) { + vfio_legacy_cpr_unregister_container(container); + object_unref(container); + } + if (fd >= 0) { + close(fd); + } + vfio_address_space_put(space); + + return false; +} + +static void vfio_container_disconnect(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; + cpr_delete_fd("vfio_container_for_group", group->groupid); + + /* + * 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_unregister(bcontainer); + 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_container_disconnect(container->fd); + vfio_legacy_cpr_unregister_container(container); + close(container->fd); + object_unref(container); + + vfio_address_space_put(space); + } +} + +static VFIOGroup *vfio_group_get(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 = cpr_open_fd(path, O_RDWR, "vfio_group", groupid, 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_container_connect(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: + cpr_delete_fd("vfio_group", groupid); + close(group->fd); + +free_group_exit: + g_free(group); + + return NULL; +} + +static void vfio_group_put(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_group_del_kvm_device(group); + vfio_container_disconnect(group); + QLIST_REMOVE(group, next); + trace_vfio_group_put(group->fd); + cpr_delete_fd("vfio_group", group->groupid); + close(group->fd); + g_free(group); +} + +static bool vfio_device_get(VFIOGroup *group, const char *name, + VFIODevice *vbasedev, Error **errp) +{ + g_autofree struct vfio_device_info *info = NULL; + int fd; + + fd = vfio_cpr_group_get_device_fd(group->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"); + goto fail; + } + + /* + * 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"); + goto fail; + } + + if (!group->ram_block_discard_allowed) { + group->ram_block_discard_allowed = true; + vfio_ram_block_discard_disable(group->container, false); + } + } + + vfio_device_prepare(vbasedev, &group->container->bcontainer, info); + + vbasedev->fd = fd; + vbasedev->group = group; + QLIST_INSERT_HEAD(&group->device_list, vbasedev, next); + + trace_vfio_device_get(name, info->flags, info->num_regions, info->num_irqs); + + return true; + +fail: + close(fd); + cpr_delete_fd(name, 0); + return false; +} + +static void vfio_device_put(VFIODevice *vbasedev) +{ + if (!vbasedev->group) { + return; + } + QLIST_REMOVE(vbasedev, next); + vbasedev->group = NULL; + trace_vfio_device_put(vbasedev->fd); + cpr_delete_fd(vbasedev->name, 0); + close(vbasedev->fd); +} + +static int vfio_device_get_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_device_attach: 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_get_groupid(vbasedev, errp); + VFIODevice *vbasedev_iter; + VFIOGroup *group; + + if (groupid < 0) { + return false; + } + + trace_vfio_device_attach(vbasedev->name, groupid); + + group = vfio_group_get(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"); + goto group_put_exit; + } + } + if (!vfio_device_get(group, name, vbasedev, errp)) { + goto group_put_exit; + } + + if (!vfio_device_hiod_create_and_realize(vbasedev, + TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO, + errp)) { + goto device_put_exit; + } + + if (vbasedev->mdev) { + error_setg(&vbasedev->cpr.mdev_blocker, + "CPR does not support vfio mdev %s", vbasedev->name); + migrate_add_blocker_modes(&vbasedev->cpr.mdev_blocker, &error_fatal, + MIG_MODE_CPR_TRANSFER, -1); + } + + return true; + +device_put_exit: + vfio_device_put(vbasedev); +group_put_exit: + vfio_group_put(group); + return false; +} + +static void vfio_legacy_detach_device(VFIODevice *vbasedev) +{ + VFIOGroup *group = vbasedev->group; + + trace_vfio_device_detach(vbasedev->name, group->groupid); + + vfio_device_unprepare(vbasedev); + + migrate_del_blocker(&vbasedev->cpr.mdev_blocker); + object_unref(vbasedev->hiod); + vfio_device_put(vbasedev); + vfio_group_put(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, const void *data) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); + + vioc->setup = vfio_legacy_setup; + vioc->dma_map = vfio_legacy_dma_map; + vioc->dma_unmap = vfio_legacy_dma_unmap; + vioc->attach_device = vfio_legacy_attach_device; + vioc->detach_device = vfio_legacy_detach_device; + vioc->set_dirty_page_tracking = vfio_legacy_set_dirty_page_tracking; + vioc->query_dirty_bitmap = vfio_legacy_query_dirty_bitmap; + vioc->pci_hot_reset = vfio_legacy_pci_hot_reset; +}; + +static 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, const 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-legacy.c b/hw/vfio/cpr-legacy.c new file mode 100644 index 00000000000..a84c3247b71 --- /dev/null +++ b/hw/vfio/cpr-legacy.c @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2021-2025 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include "qemu/osdep.h" +#include "hw/vfio/vfio-container.h" +#include "hw/vfio/vfio-device.h" +#include "hw/vfio/vfio-listener.h" +#include "migration/blocker.h" +#include "migration/cpr.h" +#include "migration/migration.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qemu/error-report.h" + +static bool vfio_dma_unmap_vaddr_all(VFIOContainer *container, Error **errp) +{ + struct vfio_iommu_type1_dma_unmap unmap = { + .argsz = sizeof(unmap), + .flags = VFIO_DMA_UNMAP_FLAG_VADDR | VFIO_DMA_UNMAP_FLAG_ALL, + .iova = 0, + .size = 0, + }; + if (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) { + error_setg_errno(errp, errno, "vfio_dma_unmap_vaddr_all"); + return false; + } + container->cpr.vaddr_unmapped = true; + return true; +} + +/* + * Set the new @vaddr for any mappings registered during cpr load. + * The incoming state is cleared thereafter. + */ +static int vfio_legacy_cpr_dma_map(const VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, void *vaddr, + bool readonly, MemoryRegion *mr) +{ + const VFIOContainer *container = container_of(bcontainer, VFIOContainer, + bcontainer); + struct vfio_iommu_type1_dma_map map = { + .argsz = sizeof(map), + .flags = VFIO_DMA_MAP_FLAG_VADDR, + .vaddr = (__u64)(uintptr_t)vaddr, + .iova = iova, + .size = size, + }; + + g_assert(cpr_is_incoming()); + + if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map)) { + return -errno; + } + + return 0; +} + +static void vfio_region_remap(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainer *container = container_of(listener, VFIOContainer, + cpr.remap_listener); + vfio_container_region_add(&container->bcontainer, section, true); +} + +static bool vfio_cpr_supported(VFIOContainer *container, Error **errp) +{ + if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UPDATE_VADDR)) { + error_setg(errp, "VFIO container does not support VFIO_UPDATE_VADDR"); + return false; + + } else if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UNMAP_ALL)) { + error_setg(errp, "VFIO container does not support VFIO_UNMAP_ALL"); + return false; + + } else { + return true; + } +} + +static int vfio_container_pre_save(void *opaque) +{ + VFIOContainer *container = opaque; + Error *local_err = NULL; + + if (!vfio_dma_unmap_vaddr_all(container, &local_err)) { + error_report_err(local_err); + return -1; + } + return 0; +} + +static int vfio_container_post_load(void *opaque, int version_id) +{ + VFIOContainer *container = opaque; + VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOGroup *group; + Error *local_err = NULL; + + if (!vfio_listener_register(bcontainer, &local_err)) { + error_report_err(local_err); + return -1; + } + + QLIST_FOREACH(group, &container->group_list, container_next) { + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + /* Restore original dma_map function */ + vioc->dma_map = container->cpr.saved_dma_map; + } + return 0; +} + +static const VMStateDescription vfio_container_vmstate = { + .name = "vfio-container", + .version_id = 0, + .minimum_version_id = 0, + .priority = MIG_PRI_LOW, /* Must happen after devices and groups */ + .pre_save = vfio_container_pre_save, + .post_load = vfio_container_post_load, + .needed = cpr_incoming_needed, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + } +}; + +static int vfio_cpr_fail_notifier(NotifierWithReturn *notifier, + MigrationEvent *e, Error **errp) +{ + VFIOContainer *container = + container_of(notifier, VFIOContainer, cpr.transfer_notifier); + VFIOContainerBase *bcontainer = &container->bcontainer; + + if (e->type != MIG_EVENT_PRECOPY_FAILED) { + return 0; + } + + if (container->cpr.vaddr_unmapped) { + /* + * Force a call to vfio_region_remap for each mapped section by + * temporarily registering a listener, and temporarily diverting + * dma_map to vfio_legacy_cpr_dma_map. The latter restores vaddr. + */ + + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + vioc->dma_map = vfio_legacy_cpr_dma_map; + + container->cpr.remap_listener = (MemoryListener) { + .name = "vfio cpr recover", + .region_add = vfio_region_remap + }; + memory_listener_register(&container->cpr.remap_listener, + bcontainer->space->as); + memory_listener_unregister(&container->cpr.remap_listener); + container->cpr.vaddr_unmapped = false; + vioc->dma_map = container->cpr.saved_dma_map; + } + return 0; +} + +bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp) +{ + VFIOContainerBase *bcontainer = &container->bcontainer; + Error **cpr_blocker = &container->cpr.blocker; + + migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier, + vfio_cpr_reboot_notifier, + MIG_MODE_CPR_REBOOT); + + if (!vfio_cpr_supported(container, cpr_blocker)) { + return migrate_add_blocker_modes(cpr_blocker, errp, + MIG_MODE_CPR_TRANSFER, -1) == 0; + } + + vmstate_register(NULL, -1, &vfio_container_vmstate, container); + + /* During incoming CPR, divert calls to dma_map. */ + if (cpr_is_incoming()) { + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + container->cpr.saved_dma_map = vioc->dma_map; + vioc->dma_map = vfio_legacy_cpr_dma_map; + } + + migration_add_notifier_mode(&container->cpr.transfer_notifier, + vfio_cpr_fail_notifier, + MIG_MODE_CPR_TRANSFER); + return true; +} + +void vfio_legacy_cpr_unregister_container(VFIOContainer *container) +{ + VFIOContainerBase *bcontainer = &container->bcontainer; + + migration_remove_notifier(&bcontainer->cpr_reboot_notifier); + migrate_del_blocker(&container->cpr.blocker); + vmstate_unregister(NULL, &vfio_container_vmstate, container); + migration_remove_notifier(&container->cpr.transfer_notifier); +} + +/* + * In old QEMU, VFIO_DMA_UNMAP_FLAG_VADDR may fail on some mapping after + * succeeding for others, so the latter have lost their vaddr. Call this + * to restore vaddr for a section with a giommu. + * + * The giommu already exists. Find it and replay it, which calls + * vfio_legacy_cpr_dma_map further down the stack. + */ +void vfio_cpr_giommu_remap(VFIOContainerBase *bcontainer, + MemoryRegionSection *section) +{ + VFIOGuestIOMMU *giommu = NULL; + hwaddr as_offset = section->offset_within_address_space; + hwaddr iommu_offset = as_offset - section->offset_within_region; + + QLIST_FOREACH(giommu, &bcontainer->giommu_list, giommu_next) { + if (giommu->iommu_mr == IOMMU_MEMORY_REGION(section->mr) && + giommu->iommu_offset == iommu_offset) { + break; + } + } + g_assert(giommu); + memory_region_iommu_replay(giommu->iommu_mr, &giommu->n); +} + +/* + * In old QEMU, VFIO_DMA_UNMAP_FLAG_VADDR may fail on some mapping after + * succeeding for others, so the latter have lost their vaddr. Call this + * to restore vaddr for a section with a RamDiscardManager. + * + * The ram discard listener already exists. Call its populate function + * directly, which calls vfio_legacy_cpr_dma_map. + */ +bool vfio_cpr_ram_discard_register_listener(VFIOContainerBase *bcontainer, + MemoryRegionSection *section) +{ + VFIORamDiscardListener *vrdl = + vfio_find_ram_discard_listener(bcontainer, section); + + g_assert(vrdl); + return vrdl->listener.notify_populate(&vrdl->listener, section) == 0; +} + +int vfio_cpr_group_get_device_fd(int d, const char *name) +{ + const int id = 0; + int fd = cpr_find_fd(name, id); + + if (fd < 0) { + fd = ioctl(d, VFIO_GROUP_GET_DEVICE_FD, name); + if (fd >= 0) { + cpr_save_fd(name, id, fd); + } + } + return fd; +} + +static bool same_device(int fd1, int fd2) +{ + struct stat st1, st2; + + return !fstat(fd1, &st1) && !fstat(fd2, &st2) && st1.st_dev == st2.st_dev; +} + +bool vfio_cpr_container_match(VFIOContainer *container, VFIOGroup *group, + int fd) +{ + if (container->fd == fd) { + return true; + } + if (!same_device(container->fd, fd)) { + return false; + } + /* + * Same device, different fd. This occurs when the container fd is + * cpr_save'd multiple times, once for each groupid, so SCM_RIGHTS + * produces duplicates. De-dup it. + */ + cpr_delete_fd("vfio_container_for_group", group->groupid); + close(fd); + cpr_save_fd("vfio_container_for_group", group->groupid, container->fd); + return true; +} diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c new file mode 100644 index 00000000000..fdbb58e2039 --- /dev/null +++ b/hw/vfio/cpr.c @@ -0,0 +1,70 @@ +/* + * 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-device.h" +#include "hw/vfio/vfio-cpr.h" +#include "hw/vfio/pci.h" +#include "migration/cpr.h" +#include "qapi/error.h" +#include "system/runstate.h" + +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); +} + +/* + * The kernel may change non-emulated config bits. Exclude them from the + * changed-bits check in get_pci_config_device. + */ +static int vfio_cpr_pci_pre_load(void *opaque) +{ + VFIOPCIDevice *vdev = opaque; + PCIDevice *pdev = &vdev->pdev; + int size = MIN(pci_config_size(pdev), vdev->config_size); + int i; + + for (i = 0; i < size; i++) { + pdev->cmask[i] &= vdev->emulated_config_bits[i]; + } + + return 0; +} + +const VMStateDescription vfio_cpr_pci_vmstate = { + .name = "vfio-cpr-pci", + .version_id = 0, + .minimum_version_id = 0, + .pre_load = vfio_cpr_pci_pre_load, + .needed = cpr_incoming_needed, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + } +}; diff --git a/hw/vfio/device.c b/hw/vfio/device.c new file mode 100644 index 00000000000..d32600eda1a --- /dev/null +++ b/hw/vfio/device.c @@ -0,0 +1,569 @@ +/* + * VFIO device + * + * 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-device.h" +#include "hw/vfio/pci.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" +#include "vfio-helpers.h" + +VFIODeviceList vfio_device_list = + QLIST_HEAD_INITIALIZER(vfio_device_list); + +/* + * We want to differentiate hot reset of multiple in-use devices vs + * hot reset of a single in-use device. VFIO_DEVICE_RESET will already + * handle the case of doing hot resets when there is only a single + * device per bus. The in-use here refers to how many VFIODevices are + * affected. A hot reset that affects multiple devices, but only a + * single in-use device, means that we can call it from our bus + * ->reset() callback since the extent is effectively a single + * device. This allows us to make use of it in the hotplug path. When + * there are multiple in-use devices, we can only trigger the hot + * reset during a system reset and thus from our reset handler. We + * separate _one vs _multi here so that we don't overlap and do a + * double reset on the system reset path where both our reset handler + * and ->reset() callback are used. Calling _one() will only do a hot + * reset for the one in-use devices case, calling _multi() will do + * nothing if a _one() would have been sufficient. + */ +void vfio_device_reset_handler(void *opaque) +{ + VFIODevice *vbasedev; + + trace_vfio_device_reset_handler(); + QLIST_FOREACH(vbasedev, &vfio_device_list, global_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->needs_reset) { + vbasedev->ops->vfio_hot_reset_multi(vbasedev); + } + } +} + +/* + * Common VFIO interrupt disable + */ +void vfio_device_irq_disable(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, + }; + + vbasedev->io_ops->set_irqs(vbasedev, &irq_set); +} + +void vfio_device_irq_unmask(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, + }; + + vbasedev->io_ops->set_irqs(vbasedev, &irq_set); +} + +void vfio_device_irq_mask(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, + }; + + vbasedev->io_ops->set_irqs(vbasedev, &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_device_irq_set_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 (!vbasedev->io_ops->set_irqs(vbasedev, 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; +} + +int vfio_device_get_irq_info(VFIODevice *vbasedev, int index, + struct vfio_irq_info *info) +{ + memset(info, 0, sizeof(*info)); + + info->argsz = sizeof(*info); + info->index = index; + + return vbasedev->io_ops->get_irq_info(vbasedev, info); +} + +int vfio_device_get_region_info(VFIODevice *vbasedev, int index, + struct vfio_region_info **info) +{ + size_t argsz = sizeof(struct vfio_region_info); + int fd = -1; + int ret; + + /* check cache */ + if (vbasedev->reginfo[index] != NULL) { + *info = vbasedev->reginfo[index]; + return 0; + } + + *info = g_malloc0(argsz); + + (*info)->index = index; +retry: + (*info)->argsz = argsz; + + ret = vbasedev->io_ops->get_region_info(vbasedev, *info, &fd); + if (ret != 0) { + g_free(*info); + *info = NULL; + return ret; + } + + if ((*info)->argsz > argsz) { + argsz = (*info)->argsz; + *info = g_realloc(*info, argsz); + + if (fd != -1) { + close(fd); + fd = -1; + } + + goto retry; + } + + /* fill cache */ + vbasedev->reginfo[index] = *info; + if (vbasedev->region_fds != NULL) { + vbasedev->region_fds[index] = fd; + } + + return 0; +} + +int vfio_device_get_region_info_type(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_device_get_region_info(vbasedev, i, info)) { + continue; + } + + hdr = vfio_get_region_info_cap(*info, VFIO_REGION_INFO_CAP_TYPE); + if (!hdr) { + continue; + } + + cap_type = container_of(hdr, struct vfio_region_info_cap_type, header); + + trace_vfio_device_get_region_info_type(vbasedev->name, i, + cap_type->type, cap_type->subtype); + + if (cap_type->type == type && cap_type->subtype == subtype) { + return 0; + } + } + + *info = NULL; + return -ENODEV; +} + +bool vfio_device_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type) +{ + struct vfio_region_info *info = NULL; + bool ret = false; + + if (!vfio_device_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; +} + +static VFIODeviceIOOps vfio_device_io_ops_ioctl; + +void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops, + DeviceState *dev, bool ram_discard) +{ + vbasedev->type = type; + vbasedev->ops = ops; + vbasedev->io_ops = &vfio_device_io_ops_ioctl; + vbasedev->dev = dev; + vbasedev->fd = -1; + vbasedev->use_region_fds = false; + + 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_create_and_realize(VFIODevice *vbasedev, + const char *typename, Error **errp) +{ + HostIOMMUDevice *hiod; + + if (vbasedev->mdev) { + return true; + } + + hiod = HOST_IOMMU_DEVICE(object_new(typename)); + + if (!HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp)) { + object_unref(hiod); + return false; + } + + vbasedev->hiod = hiod; + return true; +} + +VFIODevice *vfio_get_vfio_device(Object *obj) +{ + if (object_dynamic_cast(obj, TYPE_VFIO_PCI)) { + return &VFIO_PCI_BASE(obj)->vbasedev; + } else { + return NULL; + } +} + +bool vfio_device_attach_by_iommu_type(const char *iommu_type, char *name, + VFIODevice *vbasedev, AddressSpace *as, + Error **errp) +{ + const VFIOIOMMUClass *ops = + VFIO_IOMMU_CLASS(object_class_by_name(iommu_type)); + + assert(ops); + + return ops->attach_device(name, vbasedev, as, errp); +} + +bool vfio_device_attach(char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp) +{ + const char *iommu_type = vbasedev->iommufd ? + TYPE_VFIO_IOMMU_IOMMUFD : + TYPE_VFIO_IOMMU_LEGACY; + + return vfio_device_attach_by_iommu_type(iommu_type, name, vbasedev, + as, errp); +} + +void vfio_device_detach(VFIODevice *vbasedev) +{ + if (!vbasedev->bcontainer) { + return; + } + VFIO_IOMMU_GET_CLASS(vbasedev->bcontainer)->detach_device(vbasedev); +} + +void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainerBase *bcontainer, + struct vfio_device_info *info) +{ + vbasedev->num_irqs = info->num_irqs; + vbasedev->num_regions = info->num_regions; + vbasedev->flags = info->flags; + vbasedev->reset_works = !!(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); + + vbasedev->reginfo = g_new0(struct vfio_region_info *, + vbasedev->num_regions); + if (vbasedev->use_region_fds) { + vbasedev->region_fds = g_new0(int, vbasedev->num_regions); + } +} + +void vfio_device_unprepare(VFIODevice *vbasedev) +{ + int i; + + for (i = 0; i < vbasedev->num_regions; i++) { + g_free(vbasedev->reginfo[i]); + if (vbasedev->region_fds != NULL && vbasedev->region_fds[i] != -1) { + close(vbasedev->region_fds[i]); + } + + } + + g_clear_pointer(&vbasedev->reginfo, g_free); + g_clear_pointer(&vbasedev->region_fds, g_free); + + QLIST_REMOVE(vbasedev, container_next); + QLIST_REMOVE(vbasedev, global_next); + vbasedev->bcontainer = NULL; +} + +/* + * Traditional ioctl() based io + */ + +static int vfio_device_io_device_feature(VFIODevice *vbasedev, + struct vfio_device_feature *feature) +{ + int ret; + + ret = ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature); + + return ret < 0 ? -errno : ret; +} + +static int vfio_device_io_get_region_info(VFIODevice *vbasedev, + struct vfio_region_info *info, + int *fd) +{ + int ret; + + *fd = -1; + + ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, info); + + return ret < 0 ? -errno : ret; +} + +static int vfio_device_io_get_irq_info(VFIODevice *vbasedev, + struct vfio_irq_info *info) +{ + int ret; + + ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_IRQ_INFO, info); + + return ret < 0 ? -errno : ret; +} + +static int vfio_device_io_set_irqs(VFIODevice *vbasedev, + struct vfio_irq_set *irqs) +{ + int ret; + + ret = ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irqs); + + return ret < 0 ? -errno : ret; +} + +static int vfio_device_io_region_read(VFIODevice *vbasedev, uint8_t index, + off_t off, uint32_t size, void *data) +{ + struct vfio_region_info *info; + int ret; + + ret = vfio_device_get_region_info(vbasedev, index, &info); + if (ret != 0) { + return ret; + } + + ret = pread(vbasedev->fd, data, size, info->offset + off); + + return ret < 0 ? -errno : ret; +} + +static int vfio_device_io_region_write(VFIODevice *vbasedev, uint8_t index, + off_t off, uint32_t size, void *data, + bool post) +{ + struct vfio_region_info *info; + int ret; + + ret = vfio_device_get_region_info(vbasedev, index, &info); + if (ret != 0) { + return ret; + } + + ret = pwrite(vbasedev->fd, data, size, info->offset + off); + + return ret < 0 ? -errno : ret; +} + +static VFIODeviceIOOps vfio_device_io_ops_ioctl = { + .device_feature = vfio_device_io_device_feature, + .get_region_info = vfio_device_io_get_region_info, + .get_irq_info = vfio_device_io_get_irq_info, + .set_irqs = vfio_device_io_set_irqs, + .region_read = vfio_device_io_region_read, + .region_write = vfio_device_io_region_write, +}; diff --git a/hw/vfio/display.c b/hw/vfio/display.c index bec864f482f..9c6f5aa265d 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -16,9 +16,9 @@ #include "qemu/error-report.h" #include "hw/display/edid.h" -#include "ui/console.h" #include "qapi/error.h" #include "pci.h" +#include "vfio-display.h" #include "trace.h" #ifndef DRM_PLANE_TYPE_PRIMARY @@ -104,7 +104,6 @@ static void vfio_display_edid_update(VFIOPCIDevice *vdev, bool enabled, err: trace_vfio_display_edid_write_error(); - return; } static void vfio_display_edid_ui_info(void *opaque, uint32_t idx, @@ -124,18 +123,19 @@ 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; int ret; - ret = vfio_get_dev_region_info(&vdev->vbasedev, - VFIO_REGION_TYPE_GFX, - VFIO_REGION_SUBTYPE_GFX_EDID, - &dpy->edid_info); + ret = vfio_device_get_region_info_type(&vdev->vbasedev, + VFIO_REGION_TYPE_GFX, + 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(); @@ -167,13 +167,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) @@ -182,6 +185,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); @@ -209,6 +213,7 @@ static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, struct vfio_device_gfx_plane_info plane; VFIODMABuf *dmabuf; int fd, ret; + uint32_t offset = 0; memset(&plane, 0, sizeof(plane)); plane.argsz = sizeof(plane); @@ -241,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, &offset, + &plane.stride, 0, 0, plane.width, + plane.height, plane.drm_format, + plane.drm_format_mod, &fd, 1, false, false); + if (plane_type == DRM_PLANE_TYPE_CURSOR) { vfio_display_update_cursor(dmabuf, &plane); } @@ -258,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); } @@ -284,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); @@ -294,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; } @@ -312,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) { @@ -326,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); @@ -344,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); @@ -357,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) @@ -476,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, @@ -484,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) @@ -500,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; @@ -523,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) @@ -542,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 00000000000..d0dbab1d171 --- /dev/null +++ b/hw/vfio/helpers.c @@ -0,0 +1,200 @@ +/* + * 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 "system/kvm.h" +#include "hw/vfio/vfio-device.h" +#include "hw/hw.h" +#include "qapi/error.h" +#include "vfio-helpers.h" + +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); +} + +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; +} + +#ifdef CONFIG_KVM +/* + * We have a single VFIO pseudo device per KVM VM. Once created it lives + * for the life of the VM. Closing the file descriptor only drops our + * reference to it and the device's reference to kvm. Therefore once + * initialized, this file descriptor is only released on QEMU exit and + * we'll re-use it should another vfio device be attached before then. + */ +int vfio_kvm_device_fd = -1; +#endif + +int vfio_kvm_device_add_fd(int fd, Error **errp) +{ +#ifdef CONFIG_KVM + struct kvm_device_attr attr = { + .group = KVM_DEV_VFIO_FILE, + .attr = KVM_DEV_VFIO_FILE_ADD, + .addr = (uint64_t)(unsigned long)&fd, + }; + + if (!kvm_enabled()) { + return 0; + } + + if (vfio_kvm_device_fd < 0) { + struct kvm_create_device cd = { + .type = KVM_DEV_TYPE_VFIO, + }; + + if (kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd)) { + 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_setg_errno(errp, errno, "Failed to add fd %d to KVM VFIO device", + fd); + return -errno; + } +#endif + return 0; +} + +int vfio_kvm_device_del_fd(int fd, Error **errp) +{ +#ifdef CONFIG_KVM + struct kvm_device_attr attr = { + .group = KVM_DEV_VFIO_FILE, + .attr = KVM_DEV_VFIO_FILE_DEL, + .addr = (uint64_t)(unsigned long)&fd, + }; + + if (vfio_kvm_device_fd < 0) { + error_setg(errp, "KVM VFIO device isn't created yet"); + return -EINVAL; + } + + if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { + error_setg_errno(errp, errno, + "Failed to remove fd %d from KVM VFIO device", fd); + return -errno; + } +#endif + return 0; +} + +struct vfio_device_info *vfio_get_device_info(int fd) +{ + struct vfio_device_info *info; + uint32_t argsz = sizeof(*info); + + info = g_malloc0(argsz); + +retry: + info->argsz = argsz; + + 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); + goto retry; + } + + return info; +} diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index b31ee79c60b..e7a9d1ffc13 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -14,9 +14,12 @@ #include "qemu/units.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "qapi/qmp/qerror.h" +#include "hw/boards.h" #include "hw/hw.h" #include "hw/nvram/fw_cfg.h" #include "pci.h" +#include "pci-quirks.h" #include "trace.h" /* @@ -59,49 +62,151 @@ */ static int igd_gen(VFIOPCIDevice *vdev) { - if ((vdev->device_id & 0xfff) == 0xa84) { - return 8; /* Broxton */ + /* + * Device IDs for Broxton/Apollo Lake are 0x0a84, 0x1a84, 0x1a85, 0x5a84 + * and 0x5a85, match bit 11:1 here + * Prefix 0x0a is taken by Haswell, this rule should be matched first. + */ + if ((vdev->device_id & 0xffe) == 0xa84) { + return 9; } switch (vdev->device_id & 0xff00) { - /* Old, untested, unavailable, unknown */ - case 0x0000: - case 0x2500: - case 0x2700: - case 0x2900: - case 0x2a00: - case 0x2e00: - case 0x3500: - case 0xa000: - return -1; - /* SandyBridge, IvyBridge, ValleyView, Haswell */ - case 0x0100: - case 0x0400: - case 0x0a00: - case 0x0c00: - case 0x0d00: - case 0x0f00: + case 0x0100: /* SandyBridge, IvyBridge */ return 6; - /* BroadWell, CherryView, SkyLake, KabyLake */ - case 0x1600: - case 0x1900: - case 0x2200: - case 0x5900: + case 0x0400: /* Haswell */ + case 0x0a00: /* Haswell */ + case 0x0c00: /* Haswell */ + case 0x0d00: /* Haswell */ + case 0x0f00: /* Valleyview/Bay Trail */ + return 7; + case 0x1600: /* Broadwell */ + case 0x2200: /* Cherryview */ return 8; + case 0x1900: /* Skylake */ + case 0x3100: /* Gemini Lake */ + case 0x5900: /* Kaby Lake */ + case 0x3e00: /* Coffee Lake */ + case 0x9B00: /* Comet Lake */ + return 9; + case 0x8A00: /* Ice Lake */ + case 0x4500: /* Elkhart Lake */ + case 0x4E00: /* Jasper Lake */ + return 11; + case 0x9A00: /* Tiger Lake */ + case 0x4C00: /* Rocket Lake */ + case 0x4600: /* Alder Lake */ + case 0xA700: /* Raptor Lake */ + 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 for not applying any generation-specific quirks. + */ + return -1; } -typedef struct VFIOIGDQuirk { - struct VFIOPCIDevice *vdev; - uint32_t index; - uint32_t bdsm; -} VFIOIGDQuirk; - +#define IGD_ASLS 0xfc /* ASL Storage Register */ #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 */ + +#define IGD_GMCH_GEN6_GMS_SHIFT 3 /* SNB_GMCH in i915 */ +#define IGD_GMCH_GEN6_GMS_MASK 0x1f +#define IGD_GMCH_GEN8_GMS_SHIFT 8 /* BDW_GMCH in i915 */ +#define IGD_GMCH_GEN8_GMS_MASK 0xff + +static uint64_t igd_stolen_memory_size(int gen, uint32_t gmch) +{ + uint64_t gms; + + if (gen < 8) { + gms = (gmch >> IGD_GMCH_GEN6_GMS_SHIFT) & IGD_GMCH_GEN6_GMS_MASK; + } else { + gms = (gmch >> IGD_GMCH_GEN8_GMS_SHIFT) & IGD_GMCH_GEN8_GMS_MASK; + } + + if (gen < 9) { + return gms * 32 * MiB; + } else { + if (gms < 0xf0) { + return gms * 32 * MiB; + } else { + return (gms - 0xf0 + 1) * 4 * MiB; + } + } + return 0; +} + +/* + * The OpRegion includes the Video BIOS Table, which seems important for + * telling the driver what sort of outputs it has. Without this, the device + * may work in the guest, but we may not get output. This also requires BIOS + * support to reserve and populate a section of guest memory sufficient for + * the table and to write the base address of that memory to the ASLS register + * of the IGD device. + */ +static bool vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, + struct vfio_region_info *info, + Error **errp) +{ + int ret; + + vdev->igd_opregion = g_malloc0(info->size); + ret = pread(vdev->vbasedev.fd, vdev->igd_opregion, + info->size, info->offset); + if (ret != info->size) { + error_setg(errp, "failed to read IGD OpRegion"); + g_free(vdev->igd_opregion); + vdev->igd_opregion = NULL; + return false; + } + + /* + * Provide fw_cfg with a copy of the OpRegion which the VM firmware is to + * allocate 32bit reserved memory for, copy these contents into, and write + * the reserved memory base address to the device ASLS register at 0xFC. + * Alignment of this reserved region seems flexible, but using a 4k page + * alignment seems to work well. This interface assumes a single IGD + * device, which may be at VM address 00:02.0 in legacy mode or another + * address in UPT mode. + * + * NB, there may be future use cases discovered where the VM should have + * direct interaction with the host OpRegion, in which case the write to + * the ASLS register would trigger MemoryRegion setup to enable that. + */ + fw_cfg_add_file(fw_cfg_find(), "etc/igd-opregion", + vdev->igd_opregion, info->size); + + trace_vfio_pci_igd_opregion_enabled(vdev->vbasedev.name); + + return true; +} + +static bool vfio_pci_igd_opregion_detect(VFIOPCIDevice *vdev, + struct vfio_region_info **opregion) +{ + int ret; + + ret = vfio_device_get_region_info_type(&vdev->vbasedev, + VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, + VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, opregion); + if (ret) { + return false; + } + + /* Hotplugging is not supported for opregion access */ + if (vdev->pdev.qdev.hotplugged) { + warn_report("IGD device detected, but OpRegion is not supported " + "on hotplugged device."); + return false; + } + + return true; +} /* * The rather short list of registers that we copy from the host devices. @@ -188,7 +293,8 @@ static void vfio_pci_igd_lpc_bridge_realize(PCIDevice *pdev, Error **errp) } } -static void vfio_pci_igd_lpc_bridge_class_init(ObjectClass *klass, void *data) +static void vfio_pci_igd_lpc_bridge_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -204,7 +310,7 @@ static const TypeInfo vfio_pci_igd_lpc_bridge_info = { .name = "vfio-pci-igd-lpc-bridge", .parent = TYPE_PCI_DEVICE, .class_init = vfio_pci_igd_lpc_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -239,379 +345,373 @@ static int vfio_pci_igd_lpc_init(VFIOPCIDevice *vdev, return ret; } -/* - * IGD Gen8 and newer support up to 8MB for the GTT and use a 64bit PTE - * entry, older IGDs use 2MB and 32bit. Each PTE maps a 4k page. Therefore - * we either have 2M/4k * 4 = 2k or 8M/4k * 8 = 16k as the maximum iobar index - * for programming the GTT. - * - * See linux:include/drm/i915_drm.h for shift and mask values. - */ -static int vfio_igd_gtt_max(VFIOPCIDevice *vdev) +static bool vfio_pci_igd_setup_lpc_bridge(VFIOPCIDevice *vdev, Error **errp) { - uint32_t gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch)); - int ggms, gen = igd_gen(vdev); + struct vfio_region_info *host = NULL; + struct vfio_region_info *lpc = NULL; + PCIDevice *lpc_bridge; + int ret; - gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch)); - ggms = (gmch >> (gen < 8 ? 8 : 6)) & 0x3; - if (gen > 6) { - ggms = 1 << ggms; + /* + * Copying IDs or creating new devices are not supported on hotplug + */ + if (vdev->pdev.qdev.hotplugged) { + error_setg(errp, "IGD LPC is not supported on hotplugged device"); + return false; } - ggms *= MiB; + /* + * We need to create an LPC/ISA bridge at PCI bus address 00:1f.0 that we + * can stuff host values into, so if there's already one there and it's not + * one we can hack on, this quirk is no-go. Sorry Q35. + */ + lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev), + 0, PCI_DEVFN(0x1f, 0)); + if (lpc_bridge && !object_dynamic_cast(OBJECT(lpc_bridge), + "vfio-pci-igd-lpc-bridge")) { + error_setg(errp, + "Cannot create LPC bridge due to existing device at 1f.0"); + return false; + } - return (ggms / (4 * KiB)) * (gen < 8 ? 4 : 8); -} + /* + * Check whether we have all the vfio device specific regions to + * support LPC quirk (added in Linux v4.6). + */ + ret = vfio_device_get_region_info_type(&vdev->vbasedev, + VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, + VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG, &lpc); + if (ret) { + error_setg(errp, "IGD LPC bridge access is not supported by kernel"); + return false; + } -/* - * The IGD ROM will make use of stolen memory (GGMS) for support of VESA modes. - * Somehow the host stolen memory range is used for this, but how the ROM gets - * it is a mystery, perhaps it's hardcoded into the ROM. Thankfully though, it - * reprograms the GTT through the IOBAR where we can trap it and transpose the - * programming to the VM allocated buffer. That buffer gets reserved by the VM - * firmware via the fw_cfg entry added below. Here we're just monitoring the - * IOBAR address and data registers to detect a write sequence targeting the - * GTTADR. This code is developed by observed behavior and doesn't have a - * direct spec reference, unfortunately. - */ -static uint64_t vfio_igd_quirk_data_read(void *opaque, - hwaddr addr, unsigned size) -{ - VFIOIGDQuirk *igd = opaque; - VFIOPCIDevice *vdev = igd->vdev; + ret = vfio_device_get_region_info_type(&vdev->vbasedev, + VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, + VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG, &host); + if (ret) { + error_setg(errp, "IGD host bridge access is not supported by kernel"); + return false; + } + + /* Create/modify LPC bridge */ + ret = vfio_pci_igd_lpc_init(vdev, lpc); + if (ret) { + error_setg(errp, "Failed to create/modify LPC bridge for IGD"); + return false; + } - igd->index = ~0; + /* Stuff some host values into the VM PCI host bridge */ + ret = vfio_pci_igd_host_init(vdev, host); + if (ret) { + error_setg(errp, "Failed to modify host bridge for IGD"); + return false; + } - return vfio_region_read(&vdev->bars[4].region, addr + 4, size); + return true; } -static void vfio_igd_quirk_data_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) +static bool vfio_pci_igd_override_gms(int gen, uint32_t gms, uint32_t *gmch) { - VFIOIGDQuirk *igd = opaque; - VFIOPCIDevice *vdev = igd->vdev; - uint64_t val = data; - int gen = igd_gen(vdev); - - /* - * Programming the GGMS starts at index 0x1 and uses every 4th index (ie. - * 0x1, 0x5, 0x9, 0xd,...). For pre-Gen8 each 4-byte write is a whole PTE - * entry, with 0th bit enable set. For Gen8 and up, PTEs are 64bit, so - * entries 0x5 & 0xd are the high dword, in our case zero. Each PTE points - * to a 4k page, which we translate to a page from the VM allocated region, - * pointed to by the BDSM register. If this is not set, we fail. - * - * We trap writes to the full configured GTT size, but we typically only - * see the vBIOS writing up to (nearly) the 1MB barrier. In fact it often - * seems to miss the last entry for an even 1MB GTT. Doing a gratuitous - * write of that last entry does work, but is hopefully unnecessary since - * we clear the previous GTT on initialization. - */ - if ((igd->index % 4 == 1) && igd->index < vfio_igd_gtt_max(vdev)) { - if (gen < 8 || (igd->index % 8 == 1)) { - uint32_t base; - - base = pci_get_long(vdev->pdev.config + IGD_BDSM); - if (!base) { - hw_error("vfio-igd: Guest attempted to program IGD GTT before " - "BIOS reserved stolen memory. Unsupported BIOS?"); - } - - val = data - igd->bdsm + base; + bool ret = false; + + if (gen == -1) { + error_report("x-igd-gms is not supported on this device"); + } else if (gen < 8) { + if (gms <= 0x10) { + *gmch &= ~(IGD_GMCH_GEN6_GMS_MASK << IGD_GMCH_GEN6_GMS_SHIFT); + *gmch |= gms << IGD_GMCH_GEN6_GMS_SHIFT; + ret = true; } else { - val = 0; /* upper 32bits of pte, we only enable below 4G PTEs */ + error_report(QERR_INVALID_PARAMETER_VALUE, "x-igd-gms", "0~0x10"); + } + } else if (gen == 8) { + if (gms <= 0x40) { + *gmch &= ~(IGD_GMCH_GEN8_GMS_MASK << IGD_GMCH_GEN8_GMS_SHIFT); + *gmch |= gms << IGD_GMCH_GEN8_GMS_SHIFT; + ret = true; + } else { + error_report(QERR_INVALID_PARAMETER_VALUE, "x-igd-gms", "0~0x40"); + } + } else { + /* 0x0 to 0x40: 32MB increments starting at 0MB */ + /* 0xf0 to 0xfe: 4MB increments starting at 4MB */ + if ((gms <= 0x40) || (gms >= 0xf0 && gms <= 0xfe)) { + *gmch &= ~(IGD_GMCH_GEN8_GMS_MASK << IGD_GMCH_GEN8_GMS_SHIFT); + *gmch |= gms << IGD_GMCH_GEN8_GMS_SHIFT; + ret = true; + } else { + error_report(QERR_INVALID_PARAMETER_VALUE, + "x-igd-gms", "0~0x40 or 0xf0~0xfe"); } - - trace_vfio_pci_igd_bar4_write(vdev->vbasedev.name, - igd->index, data, val); } - vfio_region_write(&vdev->bars[4].region, addr + 4, val, size); - - igd->index = ~0; + return ret; } -static const MemoryRegionOps vfio_igd_data_quirk = { - .read = vfio_igd_quirk_data_read, - .write = vfio_igd_quirk_data_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; +#define IGD_GGC_MMIO_OFFSET 0x108040 +#define IGD_BDSM_MMIO_OFFSET 0x1080C0 -static uint64_t vfio_igd_quirk_index_read(void *opaque, - hwaddr addr, unsigned size) +void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) { - VFIOIGDQuirk *igd = opaque; - VFIOPCIDevice *vdev = igd->vdev; + VFIOQuirk *ggc_quirk, *bdsm_quirk; + VFIOConfigMirrorQuirk *ggc_mirror, *bdsm_mirror; + int gen; - igd->index = ~0; + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || + !vfio_is_vga(vdev) || nr != 0) { + return; + } - return vfio_region_read(&vdev->bars[4].region, addr, size); -} + /* Only on IGD Gen6-12 device needs quirks in BAR 0 */ + gen = igd_gen(vdev); + if (gen < 6) { + return; + } -static void vfio_igd_quirk_index_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIOIGDQuirk *igd = opaque; - VFIOPCIDevice *vdev = igd->vdev; + if (vdev->igd_gms) { + ggc_quirk = vfio_quirk_alloc(1); + ggc_mirror = ggc_quirk->data = g_malloc0(sizeof(*ggc_mirror)); + ggc_mirror->mem = ggc_quirk->mem; + ggc_mirror->vdev = vdev; + ggc_mirror->bar = nr; + ggc_mirror->offset = IGD_GGC_MMIO_OFFSET; + ggc_mirror->config_offset = IGD_GMCH; + + memory_region_init_io(ggc_mirror->mem, OBJECT(vdev), + &vfio_generic_mirror_quirk, ggc_mirror, + "vfio-igd-ggc-quirk", 2); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + ggc_mirror->offset, ggc_mirror->mem, + 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, ggc_quirk, next); + } - igd->index = data; + bdsm_quirk = vfio_quirk_alloc(1); + bdsm_mirror = bdsm_quirk->data = g_malloc0(sizeof(*bdsm_mirror)); + bdsm_mirror->mem = bdsm_quirk->mem; + bdsm_mirror->vdev = vdev; + bdsm_mirror->bar = nr; + bdsm_mirror->offset = IGD_BDSM_MMIO_OFFSET; + bdsm_mirror->config_offset = (gen < 11) ? IGD_BDSM : IGD_BDSM_GEN11; + + memory_region_init_io(bdsm_mirror->mem, OBJECT(vdev), + &vfio_generic_mirror_quirk, bdsm_mirror, + "vfio-igd-bdsm-quirk", (gen < 11) ? 4 : 8); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + bdsm_mirror->offset, bdsm_mirror->mem, + 1); - vfio_region_write(&vdev->bars[4].region, addr, data, size); + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, bdsm_quirk, next); } -static const MemoryRegionOps vfio_igd_index_quirk = { - .read = vfio_igd_quirk_index_read, - .write = vfio_igd_quirk_index_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) +static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) { - struct vfio_region_info *rom = NULL, *opregion = NULL, - *host = NULL, *lpc = NULL; - VFIOQuirk *quirk; - VFIOIGDQuirk *igd; - PCIDevice *lpc_bridge; - int i, ret, ggms_mb, gms_mb = 0, gen; + struct vfio_region_info *opregion = NULL; + int ret, gen; + uint64_t gms_size = 0; uint64_t *bdsm_size; uint32_t gmch; - uint16_t cmd_orig, cmd; + bool legacy_mode_enabled = false; Error *err = NULL; - /* - * This must be an Intel VGA device at address 00:02.0 for us to even - * consider enabling legacy mode. The vBIOS has dependencies on the - * PCI bus address. - */ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || - !vfio_is_vga(vdev) || nr != 4 || - &vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev), - 0, PCI_DEVFN(0x2, 0))) { - return; + !vfio_is_vga(vdev)) { + return true; } - /* - * We need to create an LPC/ISA bridge at PCI bus address 00:1f.0 that we - * can stuff host values into, so if there's already one there and it's not - * one we can hack on, legacy mode is no-go. Sorry Q35. - */ - lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev), - 0, PCI_DEVFN(0x1f, 0)); - if (lpc_bridge && !object_dynamic_cast(OBJECT(lpc_bridge), - "vfio-pci-igd-lpc-bridge")) { - error_report("IGD device %s cannot support legacy mode due to existing " - "devices at address 1f.0", vdev->vbasedev.name); - return; + /* IGD device always comes with OpRegion */ + if (!vfio_pci_igd_opregion_detect(vdev, &opregion)) { + return true; } + info_report("OpRegion detected on Intel display %x.", vdev->device_id); - /* - * IGD is not a standard, they like to change their specs often. We - * only attempt to support back to SandBridge and we hope that newer - * devices maintain compatibility with generation 8. - */ gen = igd_gen(vdev); - if (gen != 6 && gen != 8) { - error_report("IGD device %s is unsupported in legacy mode, " - "try SandyBridge or newer", vdev->vbasedev.name); - return; - } + gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4); /* - * Most of what we're doing here is to enable the ROM to run, so if - * there's no ROM, there's no point in setting up this quirk. - * NB. We only seem to get BIOS ROMs, so a UEFI VM would need CSM support. + * For backward compatibility, enable legacy mode when + * - Device geneation is 6 to 9 (including both) + * - Machine type is i440fx (pc_piix) + * - IGD device is at guest BDF 00:02.0 + * - Not manually disabled by x-igd-legacy-mode=off */ - ret = vfio_get_region_info(&vdev->vbasedev, - VFIO_PCI_ROM_REGION_INDEX, &rom); - if ((ret || !rom->size) && !vdev->pdev.romfile) { - error_report("IGD device %s has no ROM, legacy mode disabled", - vdev->vbasedev.name); - goto out; - } + if ((vdev->igd_legacy_mode != ON_OFF_AUTO_OFF) && + (gen >= 6 && gen <= 9) && + !strcmp(MACHINE_GET_CLASS(qdev_get_machine())->family, "pc_piix") && + (&vdev->pdev == pci_find_device(pci_device_root_bus(&vdev->pdev), + 0, PCI_DEVFN(0x2, 0)))) { + /* + * IGD legacy mode requires: + * - VBIOS in ROM BAR or file + * - VGA IO/MMIO ranges are claimed by IGD + * - OpRegion + * - Same LPC bridge and Host bridge VID/DID/SVID/SSID as host + */ + struct vfio_region_info *rom = NULL; + + legacy_mode_enabled = true; + info_report("IGD legacy mode enabled, " + "use x-igd-legacy-mode=off to disable it if unwanted."); + + /* + * Most of what we're doing here is to enable the ROM to run, so if + * there's no ROM, there's no point in setting up this quirk. + * NB. We only seem to get BIOS ROMs, so UEFI VM would need CSM support. + */ + ret = vfio_device_get_region_info(&vdev->vbasedev, + VFIO_PCI_ROM_REGION_INDEX, &rom); + if ((ret || !rom->size) && !vdev->pdev.romfile) { + error_setg(&err, "Device has no ROM"); + goto error; + } - /* - * Ignore the hotplug corner case, mark the ROM failed, we can't - * create the devices we need for legacy mode in the hotplug scenario. - */ - if (vdev->pdev.qdev.hotplugged) { - error_report("IGD device %s hotplugged, ROM disabled, " - "legacy mode disabled", vdev->vbasedev.name); - vdev->rom_read_failed = true; - goto out; - } + /* + * If IGD VGA Disable is clear (expected) and VGA is not already + * enabled, try to enable it. Probably shouldn't be using legacy mode + * without VGA, but also no point in us enabling VGA if disabled in + * hardware. + */ + if (!(gmch & 0x2) && !vdev->vga && !vfio_populate_vga(vdev, &err)) { + error_setg(&err, "Unable to enable VGA access"); + goto error; + } - /* - * Check whether we have all the vfio device specific regions to - * support legacy mode (added in Linux v4.6). If not, bail. - */ - ret = vfio_get_dev_region_info(&vdev->vbasedev, - VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, - VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion); - if (ret) { - error_report("IGD device %s does not support OpRegion access," - "legacy mode disabled", vdev->vbasedev.name); - goto out; + /* Enable OpRegion and LPC bridge quirk */ + vdev->features |= VFIO_FEATURE_ENABLE_IGD_OPREGION; + vdev->features |= VFIO_FEATURE_ENABLE_IGD_LPC; + } else if (vdev->igd_legacy_mode == ON_OFF_AUTO_ON) { + error_setg(&err, + "Machine is not i440fx, assigned BDF is not 00:02.0, " + "or device %04x (gen %d) doesn't support legacy mode", + vdev->device_id, gen); + goto error; } - ret = vfio_get_dev_region_info(&vdev->vbasedev, - VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, - VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG, &host); - if (ret) { - error_report("IGD device %s does not support host bridge access," - "legacy mode disabled", vdev->vbasedev.name); - goto out; + /* Setup OpRegion access */ + if ((vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) && + !vfio_pci_igd_opregion_init(vdev, opregion, errp)) { + goto error; } - ret = vfio_get_dev_region_info(&vdev->vbasedev, - VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, - VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG, &lpc); - if (ret) { - error_report("IGD device %s does not support LPC bridge access," - "legacy mode disabled", vdev->vbasedev.name); - goto out; + /* Setup LPC bridge / Host bridge PCI IDs */ + if ((vdev->features & VFIO_FEATURE_ENABLE_IGD_LPC) && + !vfio_pci_igd_setup_lpc_bridge(vdev, errp)) { + goto error; } - gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4); - /* - * If IGD VGA Disable is clear (expected) and VGA is not already enabled, - * try to enable it. Probably shouldn't be using legacy mode without VGA, - * but also no point in us enabling VGA if disabled in hardware. + * ASLS (OpRegion address) is read-only, emulated + * It contains HPA, guest firmware need to reprogram it with GPA. */ - if (!(gmch & 0x2) && !vdev->vga && vfio_populate_vga(vdev, &err)) { - error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); - error_report("IGD device %s failed to enable VGA access, " - "legacy mode disabled", vdev->vbasedev.name); - goto out; - } - - /* Create our LPC/ISA bridge */ - ret = vfio_pci_igd_lpc_init(vdev, lpc); - if (ret) { - error_report("IGD device %s failed to create LPC bridge, " - "legacy mode disabled", vdev->vbasedev.name); - goto out; - } - - /* Stuff some host values into the VM PCI host bridge */ - ret = vfio_pci_igd_host_init(vdev, host); - if (ret) { - error_report("IGD device %s failed to modify host bridge, " - "legacy mode disabled", vdev->vbasedev.name); - goto out; - } - - /* Setup OpRegion access */ - ret = vfio_pci_igd_opregion_init(vdev, opregion, &err); - if (ret) { - error_append_hint(&err, "IGD legacy mode disabled\n"); - error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); - goto out; - } - - /* Setup our quirk to munge GTT addresses to the VM allocated buffer */ - quirk = vfio_quirk_alloc(2); - igd = quirk->data = g_malloc0(sizeof(*igd)); - igd->vdev = vdev; - igd->index = ~0; - igd->bdsm = vfio_pci_read_config(&vdev->pdev, IGD_BDSM, 4); - igd->bdsm &= ~((1 * MiB) - 1); /* 1MB aligned */ - - memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_igd_index_quirk, - igd, "vfio-igd-index-quirk", 4); - memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, - 0, &quirk->mem[0], 1); - - memory_region_init_io(&quirk->mem[1], OBJECT(vdev), &vfio_igd_data_quirk, - igd, "vfio-igd-data-quirk", 4); - memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, - 4, &quirk->mem[1], 1); + pci_set_long(vdev->pdev.config + IGD_ASLS, 0); + pci_set_long(vdev->pdev.wmask + IGD_ASLS, ~0); + pci_set_long(vdev->emulated_config_bits + IGD_ASLS, ~0); - QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + /* + * Allow user to override dsm size using x-igd-gms option, in multiples of + * 32MiB. This option should only be used when the desired size cannot be + * set from DVMT Pre-Allocated option in host BIOS. + */ + if (vdev->igd_gms) { + if (!vfio_pci_igd_override_gms(gen, vdev->igd_gms, &gmch)) { + return false; + } - /* Determine the size of stolen memory needed for GTT */ - ggms_mb = (gmch >> (gen < 8 ? 8 : 6)) & 0x3; - if (gen > 6) { - ggms_mb = 1 << ggms_mb; + /* GMCH is read-only, emulated */ + pci_set_long(vdev->pdev.config + IGD_GMCH, gmch); + pci_set_long(vdev->pdev.wmask + IGD_GMCH, 0); + pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0); } - /* - * 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 (gen > 0) { + gms_size = igd_stolen_memory_size(gen, gmch); - if (vdev->igd_gms) { - if (vdev->igd_gms <= 0x10) { - gms_mb = vdev->igd_gms * 32; - gmch |= vdev->igd_gms << (gen < 8 ? 3 : 8); + /* BDSM is read-write, emulated. BIOS needs to be able to write it */ + 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 { - error_report("Unsupported IGD GMS value 0x%x", vdev->igd_gms); - vdev->igd_gms = 0; + 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); } } /* * Request reserved memory for stolen memory via fw_cfg. VM firmware * must allocate a 1MB aligned reserved memory region below 4GB with - * the requested size (in bytes) for use by the Intel PCI class VGA - * device at VM address 00:02.0. The base address of this reserved - * memory region must be written to the device BDSM register at PCI - * config offset 0x5C. + * the requested size (in bytes) for use by the IGD device. The base + * address of this reserved memory region must be written to the + * device BDSM register. + * For newer device without BDSM register, this fw_cfg item is 0. */ bdsm_size = g_malloc(sizeof(*bdsm_size)); - *bdsm_size = cpu_to_le64((ggms_mb + gms_mb) * MiB); + *bdsm_size = cpu_to_le64(gms_size); fw_cfg_add_file(fw_cfg_find(), "etc/igd-bdsm-size", bdsm_size, sizeof(*bdsm_size)); - /* GMCH is read-only, emulated */ - pci_set_long(vdev->pdev.config + IGD_GMCH, gmch); - pci_set_long(vdev->pdev.wmask + IGD_GMCH, 0); - pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0); + trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, (gms_size / MiB)); - /* 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); + return true; +error: /* - * This IOBAR gives us access to GTTADR, which allows us to write to - * the GTT itself. So let's go ahead and write zero to all the GTT - * entries to avoid spurious DMA faults. Be sure I/O access is enabled - * before talking to the device. + * When legacy mode is implicity enabled, continue on error, + * to keep compatibility */ - if (pread(vdev->vbasedev.fd, &cmd_orig, sizeof(cmd_orig), - vdev->config_offset + PCI_COMMAND) != sizeof(cmd_orig)) { - error_report("IGD device %s - failed to read PCI command register", - vdev->vbasedev.name); + if (legacy_mode_enabled && (vdev->igd_legacy_mode == ON_OFF_AUTO_AUTO)) { + error_report_err(err); + error_report("IGD legacy mode disabled"); + return true; } - cmd = cmd_orig | PCI_COMMAND_IO; + error_propagate(errp, err); + return false; +} - if (pwrite(vdev->vbasedev.fd, &cmd, sizeof(cmd), - vdev->config_offset + PCI_COMMAND) != sizeof(cmd)) { - error_report("IGD device %s - failed to write PCI command register", - vdev->vbasedev.name); +/* + * KVMGT/GVT-g vGPU exposes an emulated OpRegion. So far, users have to specify + * x-igd-opregion=on to enable the access. + * TODO: Check VID/DID and enable opregion access automatically + */ +static bool vfio_pci_kvmgt_config_quirk(VFIOPCIDevice *vdev, Error **errp) +{ + struct vfio_region_info *opregion = NULL; + int gen; + + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || + !vfio_is_vga(vdev)) { + return true; } - for (i = 1; i < vfio_igd_gtt_max(vdev); i += 4) { - vfio_region_write(&vdev->bars[4].region, 0, i, 4); - vfio_region_write(&vdev->bars[4].region, 4, 0, 4); + /* FIXME: Cherryview is Gen8, but don't support GVT-g */ + gen = igd_gen(vdev); + if (gen != 8 && gen != 9) { + return true; } - if (pwrite(vdev->vbasedev.fd, &cmd_orig, sizeof(cmd_orig), - vdev->config_offset + PCI_COMMAND) != sizeof(cmd_orig)) { - error_report("IGD device %s - failed to restore PCI command register", - vdev->vbasedev.name); + if (!vfio_pci_igd_opregion_detect(vdev, &opregion)) { + /* Should never reach here, KVMGT always emulates OpRegion */ + return false; } - trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, ggms_mb + gms_mb); + if ((vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) && + !vfio_pci_igd_opregion_init(vdev, opregion, errp)) { + return false; + } + + return true; +} + +bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) +{ + /* KVMGT/GVT-g vGPU is exposed as mdev */ + if (vdev->vbasedev.mdev) { + return vfio_pci_kvmgt_config_quirk(vdev, errp); + } -out: - g_free(rom); - g_free(opregion); - g_free(host); - g_free(lpc); + return vfio_pci_igd_config_quirk(vdev, errp); } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c new file mode 100644 index 00000000000..d3efef71afd --- /dev/null +++ b/hw/vfio/iommufd.c @@ -0,0 +1,911 @@ +/* + * 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-device.h" +#include "qemu/error-report.h" +#include "trace.h" +#include "qapi/error.h" +#include "system/iommufd.h" +#include "hw/qdev-core.h" +#include "hw/vfio/vfio-cpr.h" +#include "system/reset.h" +#include "qemu/cutils.h" +#include "qemu/chardev_open.h" +#include "pci.h" +#include "vfio-iommufd.h" +#include "vfio-helpers.h" +#include "vfio-listener.h" + +#define TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO \ + TYPE_HOST_IOMMU_DEVICE_IOMMUFD "-vfio" + +static int iommufd_cdev_map(const VFIOContainerBase *bcontainer, hwaddr iova, + ram_addr_t size, void *vaddr, bool readonly, + MemoryRegion *mr) +{ + 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, bool unmap_all) +{ + const VFIOIOMMUFDContainer *container = + container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); + + /* unmap in halves */ + if (unmap_all) { + Int128 llsize = int128_rshift(int128_2_64(), 1); + int ret; + + ret = iommufd_backend_unmap_dma(container->be, container->ioas_id, + 0, int128_get64(llsize)); + + if (ret == 0) { + ret = iommufd_backend_unmap_dma(container->be, container->ioas_id, + int128_get64(llsize), + int128_get64(llsize)); + } + + return ret; + } + + /* 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 type, flags = 0; + uint64_t hw_caps; + 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 (!iommufd_backend_get_device_info(vbasedev->iommufd, vbasedev->devid, + &type, NULL, 0, &hw_caps, errp)) { + return false; + } + + if (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; + } + vfio_cpr_unregister_container(bcontainer); + vfio_listener_unregister(bcontainer); + 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_address_space_get(as); + + /* 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_errno(errp, -ret, + "Cannot set discarding of RAM broken"); + 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) { + error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken"); + 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(); + } + + if (!vfio_listener_register(bcontainer, errp)) { + goto err_listener_register; + } + + if (!vfio_cpr_register_container(bcontainer, errp)) { + 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; + } + + /* + * Do not move this code before attachment! The nested IOMMU support + * needs device and hwpt id which are generated only after attachment. + */ + if (!vfio_device_hiod_create_and_realize(vbasedev, + TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO, 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); + } + + vfio_device_prepare(vbasedev, bcontainer, &dev_info); + + 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_address_space_put(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); + vfio_device_unprepare(vbasedev); + + if (!vbasedev->ram_block_discard_allowed) { + iommufd_cdev_ram_block_discard_disable(false); + } + + object_unref(vbasedev->hiod); + iommufd_cdev_detach_container(vbasedev, container); + iommufd_cdev_container_destroy(container); + vfio_address_space_put(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, const void *data) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); + + vioc->dma_map = iommufd_cdev_map; + vioc->dma_unmap = iommufd_cdev_unmap; + vioc->attach_device = iommufd_cdev_attach; + vioc->detach_device = iommufd_cdev_detach; + vioc->pci_hot_reset = iommufd_cdev_pci_hot_reset; + vioc->set_dirty_page_tracking = iommufd_set_dirty_page_tracking; + vioc->query_dirty_bitmap = iommufd_query_dirty_bitmap; +}; + +static bool +host_iommu_device_iommufd_vfio_attach_hwpt(HostIOMMUDeviceIOMMUFD *idev, + uint32_t hwpt_id, Error **errp) +{ + VFIODevice *vbasedev = HOST_IOMMU_DEVICE(idev)->agent; + + return !iommufd_cdev_attach_ioas_hwpt(vbasedev, hwpt_id, errp); +} + +static bool +host_iommu_device_iommufd_vfio_detach_hwpt(HostIOMMUDeviceIOMMUFD *idev, + Error **errp) +{ + VFIODevice *vbasedev = HOST_IOMMU_DEVICE(idev)->agent; + + return iommufd_cdev_detach_ioas_hwpt(vbasedev, errp); +} + +static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, + Error **errp) +{ + VFIODevice *vdev = opaque; + HostIOMMUDeviceIOMMUFD *idev; + HostIOMMUDeviceCaps *caps = &hiod->caps; + VendorCaps *vendor_caps = &caps->vendor_caps; + enum iommu_hw_info_type type; + uint64_t hw_caps; + + hiod->agent = opaque; + + if (!iommufd_backend_get_device_info(vdev->iommufd, vdev->devid, &type, + vendor_caps, sizeof(*vendor_caps), + &hw_caps, errp)) { + return false; + } + + hiod->name = g_strdup(vdev->name); + caps->type = type; + caps->hw_caps = hw_caps; + + idev = HOST_IOMMU_DEVICE_IOMMUFD(hiod); + idev->iommufd = vdev->iommufd; + idev->devid = vdev->devid; + idev->hwpt_id = vdev->hwpt->hwpt_id; + + 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, const void *data) +{ + HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_CLASS(oc); + HostIOMMUDeviceIOMMUFDClass *idevc = HOST_IOMMU_DEVICE_IOMMUFD_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; + + idevc->attach_hwpt = host_iommu_device_iommufd_vfio_attach_hwpt; + idevc->detach_hwpt = host_iommu_device_iommufd_vfio_detach_hwpt; +}; + +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/listener.c b/hw/vfio/listener.c new file mode 100644 index 00000000000..f498e23a937 --- /dev/null +++ b/hw/vfio/listener.c @@ -0,0 +1,1253 @@ +/* + * 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 +#ifdef CONFIG_KVM +#include +#endif +#include + +#include "hw/vfio/vfio-device.h" +#include "hw/vfio/pci.h" +#include "system/address-spaces.h" +#include "system/memory.h" +#include "system/ram_addr.h" +#include "hw/hw.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "qemu/range.h" +#include "system/kvm.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "trace.h" +#include "qapi/error.h" +#include "migration/misc.h" +#include "migration/qemu-file.h" +#include "system/tcg.h" +#include "system/tpm.h" +#include "vfio-migration-internal.h" +#include "vfio-helpers.h" +#include "vfio-listener.h" + +/* + * Device state interfaces + */ + + +static bool vfio_log_sync_needed(const VFIOContainerBase *bcontainer) +{ + VFIODevice *vbasedev; + + if (!vfio_container_dirty_tracking_is_started(bcontainer)) { + 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; +} + +static bool vfio_listener_skipped_section(MemoryRegionSection *section) +{ + return (!memory_region_is_ram(section->mr) && + !memory_region_is_iommu(section->mr)) || + memory_region_is_protected(section->mr) || + /* + * Sizing an enabled 64-bit BAR can cause spurious mappings to + * addresses in the upper part of the 64-bit address space. These + * are never accessed by the CPU and beyond the address width of + * some IOMMU hardware. TODO: VFIO should tell us the IOMMU width. + */ + section->offset_within_address_space & (1ULL << 63); +} + +/* + * Called with rcu_read_lock held. + * The returned MemoryRegion must not be accessed after calling rcu_read_unlock. + */ +static MemoryRegion *vfio_translate_iotlb(IOMMUTLBEntry *iotlb, hwaddr *xlat_p, + Error **errp) +{ + MemoryRegion *mr; + + mr = memory_translate_iotlb(iotlb, xlat_p, errp); + if (mr && memory_region_has_ram_discard_manager(mr)) { + /* + * Malicious VMs might trigger discarding of IOMMU-mapped memory. The + * pages will remain pinned inside vfio until unmapped, resulting in a + * higher memory consumption than expected. If memory would get + * populated again later, there would be an inconsistency between pages + * pinned by vfio and pages seen by QEMU. This is the case until + * unmapped from the IOMMU (e.g., during device reset). + * + * With malicious guests, we really only care about pinning more memory + * than expected. RLIMIT_MEMLOCK set for the user/process can never be + * exceeded and can be used to mitigate this problem. + */ + warn_report_once("Using vfio with vIOMMUs and coordinated discarding of" + " RAM (e.g., virtio-mem) works, however, malicious" + " guests can trigger pinning of more memory than" + " intended via an IOMMU. It's possible to mitigate " + " by setting/adjusting RLIMIT_MEMLOCK."); + } + return mr; +} + +static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) +{ + VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n); + VFIOContainerBase *bcontainer = giommu->bcontainer; + hwaddr iova = iotlb->iova + giommu->iommu_offset; + MemoryRegion *mr; + hwaddr xlat; + void *vaddr; + int ret; + Error *local_err = NULL; + + trace_vfio_iommu_map_notify(iotlb->perm == IOMMU_NONE ? "UNMAP" : "MAP", + iova, iova + iotlb->addr_mask); + + if (iotlb->target_as != &address_space_memory) { + error_setg(&local_err, + "Wrong target AS \"%s\", only system memory is allowed", + iotlb->target_as->name ? iotlb->target_as->name : "none"); + if (migration_is_running()) { + migration_file_set_error(-EINVAL, local_err); + } else { + error_report_err(local_err); + } + return; + } + + rcu_read_lock(); + + if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) { + bool read_only; + + mr = vfio_translate_iotlb(iotlb, &xlat, &local_err); + if (!mr) { + error_report_err(local_err); + goto out; + } + vaddr = memory_region_get_ram_ptr(mr) + xlat; + read_only = !(iotlb->perm & IOMMU_WO) || mr->readonly; + + /* + * vaddr is only valid until rcu_read_unlock(). But after + * vfio_dma_map has set up the mapping the pages will be + * pinned by the kernel. This makes sure that the RAM backend + * of vaddr will always be there, even if the memory object is + * destroyed and its backing memory munmap-ed. + */ + ret = vfio_container_dma_map(bcontainer, iova, + iotlb->addr_mask + 1, vaddr, + read_only, mr); + if (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_container_dma_unmap(bcontainer, iova, + iotlb->addr_mask + 1, iotlb, false); + if (ret) { + error_setg(&local_err, + "vfio_container_dma_unmap(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx") = %d (%s)", + bcontainer, iova, + iotlb->addr_mask + 1, ret, strerror(-ret)); + if (migration_is_running()) { + migration_file_set_error(ret, local_err); + } else { + error_report_err(local_err); + } + } + } +out: + rcu_read_unlock(); +} + +static void vfio_ram_discard_notify_discard(RamDiscardListener *rdl, + MemoryRegionSection *section) +{ + 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_container_dma_unmap(bcontainer, iova, size , NULL, false); + if (ret) { + error_report("%s: vfio_container_dma_unmap() failed: %s", __func__, + strerror(-ret)); + } +} + +static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl, + MemoryRegionSection *section) +{ + 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; + void *vaddr; + int ret; + + /* + * Map in (aligned within memory region) minimum granularity, so we can + * unmap in minimum granularity later. + */ + for (start = section->offset_within_region; start < end; start = next) { + next = ROUND_UP(start + 1, vrdl->granularity); + next = MIN(next, end); + + iova = start - section->offset_within_region + + section->offset_within_address_space; + vaddr = memory_region_get_ram_ptr(section->mr) + start; + + ret = vfio_container_dma_map(bcontainer, iova, next - start, + vaddr, section->readonly, section->mr); + if (ret) { + /* Rollback */ + vfio_ram_discard_notify_discard(rdl, section); + return ret; + } + } + return 0; +} + +static void vfio_ram_discard_register_listener(VFIOContainerBase *bcontainer, + MemoryRegionSection *section) +{ + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); + int target_page_size = qemu_target_page_size(); + VFIORamDiscardListener *vrdl; + + /* Ignore some corner cases not relevant in practice. */ + g_assert(QEMU_IS_ALIGNED(section->offset_within_region, target_page_size)); + g_assert(QEMU_IS_ALIGNED(section->offset_within_address_space, + target_page_size)); + g_assert(QEMU_IS_ALIGNED(int128_get64(section->size), target_page_size)); + + vrdl = g_new0(VFIORamDiscardListener, 1); + vrdl->bcontainer = bcontainer; + vrdl->mr = section->mr; + vrdl->offset_within_address_space = section->offset_within_address_space; + vrdl->size = int128_get64(section->size); + vrdl->granularity = ram_discard_manager_get_min_granularity(rdm, + section->mr); + + g_assert(vrdl->granularity && is_power_of_2(vrdl->granularity)); + 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(&bcontainer->vrdl_list, vrdl, next); + + /* + * Sanity-check if we have a theoretically problematic setup where we could + * exceed the maximum number of possible DMA mappings over time. We assume + * that each mapped section in the same address space as a RamDiscardManager + * section consumes exactly one DMA mapping, with the exception of + * RamDiscardManager sections; i.e., we don't expect to have gIOMMU sections + * in the same address space as RamDiscardManager sections. + * + * We assume that each section in the address space consumes one memslot. + * We take the number of KVM memory slots as a best guess for the maximum + * number of sections in the address space we could have over time, + * also consuming DMA mappings. + */ + if (bcontainer->dma_max_mappings) { + unsigned int vrdl_count = 0, vrdl_mappings = 0, max_memslots = 512; + +#ifdef CONFIG_KVM + if (kvm_enabled()) { + max_memslots = kvm_get_max_memslots(); + } +#endif + + QLIST_FOREACH(vrdl, &bcontainer->vrdl_list, next) { + hwaddr start, end; + + start = QEMU_ALIGN_DOWN(vrdl->offset_within_address_space, + vrdl->granularity); + end = ROUND_UP(vrdl->offset_within_address_space + vrdl->size, + vrdl->granularity); + vrdl_mappings += (end - start) / vrdl->granularity; + vrdl_count++; + } + + if (vrdl_mappings + max_memslots - vrdl_count > + 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__, bcontainer->dma_max_mappings, + max_memslots); + } + } +} + +static void vfio_ram_discard_unregister_listener(VFIOContainerBase *bcontainer, + MemoryRegionSection *section) +{ + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); + VFIORamDiscardListener *vrdl = NULL; + + QLIST_FOREACH(vrdl, &bcontainer->vrdl_list, next) { + if (vrdl->mr == section->mr && + vrdl->offset_within_address_space == + section->offset_within_address_space) { + break; + } + } + + if (!vrdl) { + hw_error("vfio: Trying to unregister missing RAM discard listener"); + } + + ram_discard_manager_unregister_listener(rdm, &vrdl->listener); + QLIST_REMOVE(vrdl, next); + g_free(vrdl); +} + +static bool vfio_known_safe_misalignment(MemoryRegionSection *section) +{ + MemoryRegion *mr = section->mr; + + if (!TPM_IS_CRB(mr->owner)) { + return false; + } + + /* this is a known safe misaligned region, just trace for debug purpose */ + trace_vfio_known_safe_misalignment(memory_region_name(mr), + section->offset_within_address_space, + section->offset_within_region, + qemu_real_host_page_size()); + return true; +} + +static bool vfio_listener_valid_section(MemoryRegionSection *section, + const char *name) +{ + if (vfio_listener_skipped_section(section)) { + 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 false; + } + + 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()); + } + 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_begin(MemoryListener *listener) +{ + VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, + listener); + void (*listener_begin)(VFIOContainerBase *bcontainer); + + listener_begin = VFIO_IOMMU_GET_CLASS(bcontainer)->listener_begin; + + if (listener_begin) { + listener_begin(bcontainer); + } +} + +static void vfio_listener_commit(MemoryListener *listener) +{ + VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, + listener); + void (*listener_commit)(VFIOContainerBase *bcontainer); + + listener_commit = VFIO_IOMMU_GET_CLASS(bcontainer)->listener_commit; + + if (listener_commit) { + listener_commit(bcontainer); + } +} + +static void vfio_device_error_append(VFIODevice *vbasedev, Error **errp) +{ + /* + * MMIO region mapping failures are not fatal but in this case PCI + * peer-to-peer transactions are broken. + */ + if (vbasedev && vbasedev->type == VFIO_DEVICE_TYPE_PCI) { + error_append_hint(errp, "%s: PCI peer-to-peer transactions " + "on BARs are not supported.\n", vbasedev->name); + } +} + +VFIORamDiscardListener *vfio_find_ram_discard_listener( + VFIOContainerBase *bcontainer, MemoryRegionSection *section) +{ + VFIORamDiscardListener *vrdl = NULL; + + QLIST_FOREACH(vrdl, &bcontainer->vrdl_list, next) { + if (vrdl->mr == section->mr && + vrdl->offset_within_address_space == + section->offset_within_address_space) { + break; + } + } + + if (!vrdl) { + hw_error("vfio: Trying to sync missing RAM discard listener"); + /* does not return */ + } + return vrdl; +} + +static void vfio_listener_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, + listener); + vfio_container_region_add(bcontainer, section, false); +} + +void vfio_container_region_add(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, + bool cpr_remap) +{ + 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), + section->offset_within_address_space, + int128_getlo(section->size), + qemu_real_host_page_size()); + } + return; + } + + /* PPC64/pseries machine only */ + if (!vfio_container_add_section_window(bcontainer, section, &err)) { + goto mmio_dma_error; + } + + memory_region_ref(section->mr); + + if (memory_region_is_iommu(section->mr)) { + VFIOGuestIOMMU *giommu; + IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); + int iommu_idx; + + trace_vfio_listener_region_add_iommu(section->mr->name, iova, end); + + if (cpr_remap) { + vfio_cpr_giommu_remap(bcontainer, section); + } + + /* + * FIXME: For VFIO iommu types which have KVM acceleration to + * avoid bouncing all map/unmaps through qemu this way, this + * would be the right place to wire that up (tell the KVM + * device emulation the VFIO iommu handles to use). + */ + giommu = g_malloc0(sizeof(*giommu)); + giommu->iommu_mr = iommu_mr; + giommu->iommu_offset = section->offset_within_address_space - + section->offset_within_region; + giommu->bcontainer = bcontainer; + llend = int128_add(int128_make64(section->offset_within_region), + section->size); + llend = int128_sub(llend, int128_one()); + iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr, + MEMTXATTRS_UNSPECIFIED); + iommu_notifier_init(&giommu->n, vfio_iommu_map_notify, + IOMMU_NOTIFIER_IOTLB_EVENTS, + section->offset_within_region, + int128_get64(llend), + iommu_idx); + + ret = memory_region_register_iommu_notifier(section->mr, &giommu->n, + &err); + if (ret) { + g_free(giommu); + goto fail; + } + QLIST_INSERT_HEAD(&bcontainer->giommu_list, giommu, giommu_next); + memory_region_iommu_replay(giommu->iommu_mr, &giommu->n); + + return; + } + + /* Here we assume that memory_region_is_ram(section->mr)==true */ + + /* + * For RAM memory regions with a RamDiscardManager, we only want to map the + * actually populated parts - and update the mapping whenever we're notified + * about changes. + */ + if (memory_region_has_ram_discard_manager(section->mr)) { + if (!cpr_remap) { + vfio_ram_discard_register_listener(bcontainer, section); + } else if (!vfio_cpr_ram_discard_register_listener(bcontainer, + section)) { + goto fail; + } + return; + } + + vaddr = memory_region_get_ram_ptr(section->mr) + + section->offset_within_region + + (iova - section->offset_within_address_space); + + trace_vfio_listener_region_add_ram(iova, end, vaddr); + + llsize = int128_sub(llend, int128_make64(iova)); + + if (memory_region_is_ram_device(section->mr)) { + hwaddr pgmask = (1ULL << ctz64(bcontainer->pgsizes)) - 1; + + if ((iova & pgmask) || (int128_get64(llsize) & pgmask)) { + trace_vfio_listener_region_add_no_dma_map( + memory_region_name(section->mr), + section->offset_within_address_space, + int128_getlo(section->size), + pgmask + 1); + return; + } + } + + ret = vfio_container_dma_map(bcontainer, iova, int128_get64(llsize), + vaddr, section->readonly, section->mr); + if (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)); + mmio_dma_error: + if (memory_region_is_ram_device(section->mr)) { + /* Allow unexpected mappings not to be fatal for RAM devices */ + VFIODevice *vbasedev = + vfio_get_vfio_device(memory_region_owner(section->mr)); + vfio_device_error_append(vbasedev, &err); + warn_report_err_once(err); + return; + } + goto fail; + } + + return; + +fail: + if (!bcontainer->initialized) { + /* + * At machine init time or when the device is attached to the + * VM, store the first error in the container so we can + * gracefully fail the device realize routine. + */ + if (!bcontainer->error) { + error_propagate_prepend(&bcontainer->error, err, + "Region %s: ", + memory_region_name(section->mr)); + } else { + error_free(err); + } + } else { + /* + * At runtime, there's not much we can do other than throw a + * hardware error. + */ + error_report_err(err); + hw_error("vfio: DMA mapping failed, unable to continue"); + } +} + +static void vfio_listener_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, + listener); + hwaddr iova, end; + Int128 llend, llsize; + int ret; + bool try_unmap = true; + + if (!vfio_listener_valid_section(section, "region_del")) { + return; + } + + if (memory_region_is_iommu(section->mr)) { + VFIOGuestIOMMU *giommu; + + 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, + &giommu->n); + QLIST_REMOVE(giommu, giommu_next); + g_free(giommu); + break; + } + } + + /* + * FIXME: We assume the one big unmap below is adequate to + * remove any individual page mappings in the IOMMU which + * might have been copied into VFIO. This works for a page table + * based IOMMU where a big unmap flattens a large range of IO-PTEs. + * That may not be true for all IOMMU types. + */ + } + + if (!vfio_get_section_iova_range(bcontainer, section, &iova, &end, + &llend)) { + return; + } + + llsize = int128_sub(llend, int128_make64(iova)); + + trace_vfio_listener_region_del(iova, end); + + if (memory_region_is_ram_device(section->mr)) { + hwaddr pgmask; + + 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_ram_discard_unregister_listener(bcontainer, section); + /* Unregistering will trigger an unmap. */ + try_unmap = false; + } + + if (try_unmap) { + bool unmap_all = false; + + if (int128_eq(llsize, int128_2_64())) { + unmap_all = true; + llsize = int128_zero(); + } + ret = vfio_container_dma_unmap(bcontainer, iova, int128_get64(llsize), + NULL, unmap_all); + if (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); + + /* PPC64/pseries machine only */ + vfio_container_del_section_window(bcontainer, section); +} + +typedef struct VFIODirtyRanges { + hwaddr min32; + hwaddr max32; + hwaddr min64; + hwaddr max64; + hwaddr minpci64; + hwaddr maxpci64; +} VFIODirtyRanges; + +typedef struct VFIODirtyRangesListener { + VFIOContainerBase *bcontainer; + VFIODirtyRanges ranges; + MemoryListener listener; +} VFIODirtyRangesListener; + +static bool vfio_section_is_vfio_pci(MemoryRegionSection *section, + VFIOContainerBase *bcontainer) +{ + VFIOPCIDevice *pcidev; + VFIODevice *vbasedev; + Object *owner; + + owner = memory_region_owner(section->mr); + + QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { + if (vbasedev->type != VFIO_DEVICE_TYPE_PCI) { + continue; + } + pcidev = container_of(vbasedev, VFIOPCIDevice, vbasedev); + if (OBJECT(pcidev) == owner) { + return true; + } + } + + return false; +} + +static void vfio_dirty_tracking_update_range(VFIODirtyRanges *range, + hwaddr iova, hwaddr end, + bool update_pci) +{ + hwaddr *min, *max; + + /* + * 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 { + min = (end <= UINT32_MAX) ? &range->min32 : &range->min64; + max = (end <= UINT32_MAX) ? &range->max32 : &range->max64; + } + if (*min > iova) { + *min = iova; + } + if (*max < end) { + *max = end; + } + + trace_vfio_device_dirty_tracking_update(iova, end, *min, *max); +} + +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) { + int ret; + + if (!vbasedev->dirty_tracking) { + continue; + } + + ret = vbasedev->io_ops->device_feature(vbasedev, feature); + + if (ret != 0) { + warn_report("%s: Failed to stop DMA logging, err %d (%s)", + vbasedev->name, -ret, strerror(-ret)); + } + vbasedev->dirty_tracking = false; + } +} + +static struct vfio_device_feature * +vfio_device_feature_dma_logging_start_create(VFIOContainerBase *bcontainer, + VFIODirtyRanges *tracking) +{ + 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; + + 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 = vbasedev->io_ops->device_feature(vbasedev, feature); + if (ret) { + error_setg_errno(errp, -ret, "%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_container_devices_dirty_tracking_is_supported(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) +{ + VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, + listener); + Error *local_err = NULL; + int ret = 0; + + if (vfio_container_devices_dirty_tracking_is_supported(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 - "); + if (migration_is_running()) { + migration_file_set_error(ret, local_err); + } else { + error_report_err(local_err); + } + } +} + +typedef struct { + IOMMUNotifier n; + VFIOGuestIOMMU *giommu; +} vfio_giommu_dirty_notifier; + +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; + VFIOContainerBase *bcontainer = giommu->bcontainer; + hwaddr iova = iotlb->iova + giommu->iommu_offset; + ram_addr_t translated_addr; + Error *local_err = NULL; + int ret = -EINVAL; + MemoryRegion *mr; + hwaddr xlat; + + trace_vfio_iommu_map_dirty_notify(iova, iova + iotlb->addr_mask); + + if (iotlb->target_as != &address_space_memory) { + error_setg(&local_err, + "Wrong target AS \"%s\", only system memory is allowed", + iotlb->target_as->name ? iotlb->target_as->name : "none"); + goto out; + } + + rcu_read_lock(); + mr = vfio_translate_iotlb(iotlb, &xlat, &local_err); + if (!mr) { + goto out_unlock; + } + translated_addr = memory_region_get_ram_addr(mr) + xlat; + + ret = vfio_container_query_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); + } + +out_unlock: + rcu_read_unlock(); + +out: + if (ret) { + if (migration_is_running()) { + migration_file_set_error(ret, local_err); + } else { + error_report_err(local_err); + } + } +} + +static int vfio_ram_discard_query_dirty_bitmap(MemoryRegionSection *section, + void *opaque) +{ + const hwaddr size = int128_get64(section->size); + const hwaddr iova = section->offset_within_address_space; + 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. + */ + ret = vfio_container_query_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(VFIOContainerBase *bcontainer, + MemoryRegionSection *section) +{ + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); + VFIORamDiscardListener *vrdl = + vfio_find_ram_discard_listener(bcontainer, section); + + /* + * We only want/can synchronize the bitmap for actually mapped parts - + * which correspond to populated parts. Replay all populated parts. + */ + return ram_discard_manager_replay_populated(rdm, section, + vfio_ram_discard_query_dirty_bitmap, + &vrdl); +} + +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)) { + return vfio_sync_iommu_dirty_bitmap(bcontainer, section); + } else if (memory_region_has_ram_discard_manager(section->mr)) { + 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_container_query_dirty_bitmap(bcontainer, + REAL_HOST_PAGE_ALIGN(section->offset_within_address_space), + int128_get64(section->size), ram_addr, errp); +} + +static void vfio_listener_log_sync(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, + listener); + int ret; + Error *local_err = NULL; + + if (vfio_listener_skipped_section(section)) { + return; + } + + if (vfio_log_sync_needed(bcontainer)) { + ret = vfio_sync_dirty_bitmap(bcontainer, section, &local_err); + if (ret) { + if (migration_is_running()) { + migration_file_set_error(ret, local_err); + } else { + error_report_err(local_err); + } + } + } +} + +static const MemoryListener vfio_memory_listener = { + .name = "vfio", + .begin = vfio_listener_begin, + .commit = vfio_listener_commit, + .region_add = vfio_listener_region_add, + .region_del = vfio_listener_region_del, + .log_global_start = vfio_listener_log_global_start, + .log_global_stop = vfio_listener_log_global_stop, + .log_sync = vfio_listener_log_sync, +}; + +bool vfio_listener_register(VFIOContainerBase *bcontainer, Error **errp) +{ + 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: "); + return false; + } + + return true; +} + +void vfio_listener_unregister(VFIOContainerBase *bcontainer) +{ + memory_listener_unregister(&bcontainer->listener); +} diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index da9af297a0c..73d29f925ff 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -1,19 +1,35 @@ vfio_ss = ss.source_set() vfio_ss.add(files( - 'common.c', - 'spapr.c', - 'migration.c', + 'listener.c', + 'container-base.c', + 'container.c', + 'helpers.c', )) +vfio_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr.c')) vfio_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( - 'display.c', 'pci-quirks.c', 'pci.c', )) vfio_ss.add(when: 'CONFIG_VFIO_CCW', if_true: files('ccw.c')) vfio_ss.add(when: 'CONFIG_VFIO_PLATFORM', if_true: files('platform.c')) -vfio_ss.add(when: 'CONFIG_VFIO_XGMAC', if_true: files('calxeda-xgmac.c')) -vfio_ss.add(when: 'CONFIG_VFIO_AMD_XGBE', if_true: files('amd-xgbe.c')) vfio_ss.add(when: 'CONFIG_VFIO_AP', if_true: files('ap.c')) vfio_ss.add(when: 'CONFIG_VFIO_IGD', if_true: files('igd.c')) specific_ss.add_all(when: 'CONFIG_VFIO', if_true: vfio_ss) + +system_ss.add(when: 'CONFIG_VFIO_XGMAC', if_true: files('calxeda-xgmac.c')) +system_ss.add(when: 'CONFIG_VFIO_AMD_XGBE', if_true: files('amd-xgbe.c')) +system_ss.add(when: 'CONFIG_VFIO', if_true: files( + 'cpr.c', + 'cpr-legacy.c', + 'device.c', + 'migration.c', + 'migration-multifd.c', + 'region.c', +)) +system_ss.add(when: ['CONFIG_VFIO', 'CONFIG_IOMMUFD'], if_true: files( + 'iommufd.c', +)) +system_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( + 'display.c', +)) diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c new file mode 100644 index 00000000000..850a3194887 --- /dev/null +++ b/hw/vfio/migration-multifd.c @@ -0,0 +1,685 @@ +/* + * Multifd VFIO migration + * + * Copyright (C) 2024,2025 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/vfio/vfio-device.h" +#include "migration/misc.h" +#include "qapi/error.h" +#include "qemu/bswap.h" +#include "qemu/error-report.h" +#include "qemu/lockable.h" +#include "qemu/main-loop.h" +#include "qemu/thread.h" +#include "io/channel-buffer.h" +#include "migration/qemu-file.h" +#include "migration-multifd.h" +#include "vfio-migration-internal.h" +#include "trace.h" + +#define VFIO_DEVICE_STATE_CONFIG_STATE (1) + +#define VFIO_DEVICE_STATE_PACKET_VER_CURRENT (0) + +typedef struct VFIODeviceStatePacket { + uint32_t version; + uint32_t idx; + uint32_t flags; + uint8_t data[0]; +} QEMU_PACKED VFIODeviceStatePacket; + +/* type safety */ +typedef struct VFIOStateBuffers { + GArray *array; +} VFIOStateBuffers; + +typedef struct VFIOStateBuffer { + bool is_present; + char *data; + size_t len; +} VFIOStateBuffer; + +typedef struct VFIOMultifd { + bool load_bufs_thread_running; + bool load_bufs_thread_want_exit; + + VFIOStateBuffers load_bufs; + QemuCond load_bufs_buffer_ready_cond; + QemuCond load_bufs_thread_finished_cond; + QemuMutex load_bufs_mutex; /* Lock order: this lock -> BQL */ + uint32_t load_buf_idx; + uint32_t load_buf_idx_last; +} VFIOMultifd; + +static void vfio_state_buffer_clear(gpointer data) +{ + VFIOStateBuffer *lb = data; + + if (!lb->is_present) { + return; + } + + g_clear_pointer(&lb->data, g_free); + lb->is_present = false; +} + +static void vfio_state_buffers_init(VFIOStateBuffers *bufs) +{ + bufs->array = g_array_new(FALSE, TRUE, sizeof(VFIOStateBuffer)); + g_array_set_clear_func(bufs->array, vfio_state_buffer_clear); +} + +static void vfio_state_buffers_destroy(VFIOStateBuffers *bufs) +{ + g_clear_pointer(&bufs->array, g_array_unref); +} + +static void vfio_state_buffers_assert_init(VFIOStateBuffers *bufs) +{ + assert(bufs->array); +} + +static unsigned int vfio_state_buffers_size_get(VFIOStateBuffers *bufs) +{ + return bufs->array->len; +} + +static void vfio_state_buffers_size_set(VFIOStateBuffers *bufs, + unsigned int size) +{ + g_array_set_size(bufs->array, size); +} + +static VFIOStateBuffer *vfio_state_buffers_at(VFIOStateBuffers *bufs, + unsigned int idx) +{ + return &g_array_index(bufs->array, VFIOStateBuffer, idx); +} + +/* called with load_bufs_mutex locked */ +static bool vfio_load_state_buffer_insert(VFIODevice *vbasedev, + VFIODeviceStatePacket *packet, + size_t packet_total_size, + Error **errp) +{ + VFIOMigration *migration = vbasedev->migration; + VFIOMultifd *multifd = migration->multifd; + VFIOStateBuffer *lb; + + vfio_state_buffers_assert_init(&multifd->load_bufs); + if (packet->idx >= vfio_state_buffers_size_get(&multifd->load_bufs)) { + vfio_state_buffers_size_set(&multifd->load_bufs, packet->idx + 1); + } + + lb = vfio_state_buffers_at(&multifd->load_bufs, packet->idx); + if (lb->is_present) { + error_setg(errp, "%s: state buffer %" PRIu32 " already filled", + vbasedev->name, packet->idx); + return false; + } + + assert(packet->idx >= multifd->load_buf_idx); + + lb->data = g_memdup2(&packet->data, packet_total_size - sizeof(*packet)); + lb->len = packet_total_size - sizeof(*packet); + lb->is_present = true; + + return true; +} + +bool vfio_multifd_load_state_buffer(void *opaque, char *data, size_t data_size, + Error **errp) +{ + VFIODevice *vbasedev = opaque; + VFIOMigration *migration = vbasedev->migration; + VFIOMultifd *multifd = migration->multifd; + VFIODeviceStatePacket *packet = (VFIODeviceStatePacket *)data; + + if (!vfio_multifd_transfer_enabled(vbasedev)) { + error_setg(errp, + "%s: got device state packet but not doing multifd transfer", + vbasedev->name); + return false; + } + + assert(multifd); + + if (data_size < sizeof(*packet)) { + error_setg(errp, "%s: packet too short at %zu (min is %zu)", + vbasedev->name, data_size, sizeof(*packet)); + return false; + } + + packet->version = be32_to_cpu(packet->version); + if (packet->version != VFIO_DEVICE_STATE_PACKET_VER_CURRENT) { + error_setg(errp, "%s: packet has unknown version %" PRIu32, + vbasedev->name, packet->version); + return false; + } + + packet->idx = be32_to_cpu(packet->idx); + packet->flags = be32_to_cpu(packet->flags); + + if (packet->idx == UINT32_MAX) { + error_setg(errp, "%s: packet index is invalid", vbasedev->name); + return false; + } + + trace_vfio_load_state_device_buffer_incoming(vbasedev->name, packet->idx); + + /* + * Holding BQL here would violate the lock order and can cause + * a deadlock once we attempt to lock load_bufs_mutex below. + */ + assert(!bql_locked()); + + WITH_QEMU_LOCK_GUARD(&multifd->load_bufs_mutex) { + /* config state packet should be the last one in the stream */ + if (packet->flags & VFIO_DEVICE_STATE_CONFIG_STATE) { + multifd->load_buf_idx_last = packet->idx; + } + + if (!vfio_load_state_buffer_insert(vbasedev, packet, data_size, + errp)) { + return false; + } + + qemu_cond_signal(&multifd->load_bufs_buffer_ready_cond); + } + + return true; +} + +static bool vfio_load_bufs_thread_load_config(VFIODevice *vbasedev, + Error **errp) +{ + VFIOMigration *migration = vbasedev->migration; + VFIOMultifd *multifd = migration->multifd; + VFIOStateBuffer *lb; + g_autoptr(QIOChannelBuffer) bioc = NULL; + g_autoptr(QEMUFile) f_out = NULL, f_in = NULL; + uint64_t mig_header; + int ret; + + assert(multifd->load_buf_idx == multifd->load_buf_idx_last); + lb = vfio_state_buffers_at(&multifd->load_bufs, multifd->load_buf_idx); + assert(lb->is_present); + + bioc = qio_channel_buffer_new(lb->len); + qio_channel_set_name(QIO_CHANNEL(bioc), "vfio-device-config-load"); + + f_out = qemu_file_new_output(QIO_CHANNEL(bioc)); + qemu_put_buffer(f_out, (uint8_t *)lb->data, lb->len); + + ret = qemu_fflush(f_out); + if (ret) { + error_setg(errp, "%s: load config state flush failed: %d", + vbasedev->name, ret); + return false; + } + + qio_channel_io_seek(QIO_CHANNEL(bioc), 0, 0, NULL); + f_in = qemu_file_new_input(QIO_CHANNEL(bioc)); + + mig_header = qemu_get_be64(f_in); + if (mig_header != VFIO_MIG_FLAG_DEV_CONFIG_STATE) { + error_setg(errp, "%s: expected FLAG_DEV_CONFIG_STATE but got %" PRIx64, + vbasedev->name, mig_header); + return false; + } + + bql_lock(); + ret = vfio_load_device_config_state(f_in, vbasedev); + bql_unlock(); + + if (ret < 0) { + error_setg(errp, "%s: vfio_load_device_config_state() failed: %d", + vbasedev->name, ret); + return false; + } + + return true; +} + +static VFIOStateBuffer *vfio_load_state_buffer_get(VFIOMultifd *multifd) +{ + VFIOStateBuffer *lb; + unsigned int bufs_len; + + bufs_len = vfio_state_buffers_size_get(&multifd->load_bufs); + if (multifd->load_buf_idx >= bufs_len) { + assert(multifd->load_buf_idx == bufs_len); + return NULL; + } + + lb = vfio_state_buffers_at(&multifd->load_bufs, + multifd->load_buf_idx); + if (!lb->is_present) { + return NULL; + } + + return lb; +} + +static bool vfio_load_state_buffer_write(VFIODevice *vbasedev, + VFIOStateBuffer *lb, + Error **errp) +{ + VFIOMigration *migration = vbasedev->migration; + VFIOMultifd *multifd = migration->multifd; + g_autofree char *buf = NULL; + char *buf_cur; + size_t buf_len; + + if (!lb->len) { + return true; + } + + trace_vfio_load_state_device_buffer_load_start(vbasedev->name, + multifd->load_buf_idx); + + /* lb might become re-allocated when we drop the lock */ + buf = g_steal_pointer(&lb->data); + buf_cur = buf; + buf_len = lb->len; + while (buf_len > 0) { + ssize_t wr_ret; + int errno_save; + + /* + * Loading data to the device takes a while, + * drop the lock during this process. + */ + qemu_mutex_unlock(&multifd->load_bufs_mutex); + wr_ret = write(migration->data_fd, buf_cur, buf_len); + errno_save = errno; + qemu_mutex_lock(&multifd->load_bufs_mutex); + + if (wr_ret < 0) { + error_setg(errp, + "%s: writing state buffer %" PRIu32 " failed: %d", + vbasedev->name, multifd->load_buf_idx, errno_save); + return false; + } + + assert(wr_ret <= buf_len); + buf_len -= wr_ret; + buf_cur += wr_ret; + } + + trace_vfio_load_state_device_buffer_load_end(vbasedev->name, + multifd->load_buf_idx); + + return true; +} + +static bool vfio_load_bufs_thread_want_exit(VFIOMultifd *multifd, + bool *should_quit) +{ + return multifd->load_bufs_thread_want_exit || qatomic_read(should_quit); +} + +/* + * This thread is spawned by vfio_multifd_switchover_start() which gets + * called upon encountering the switchover point marker in main migration + * stream. + * + * It exits after either: + * * completing loading the remaining device state and device config, OR: + * * encountering some error while doing the above, OR: + * * being forcefully aborted by the migration core by it setting should_quit + * or by vfio_load_cleanup_load_bufs_thread() setting + * multifd->load_bufs_thread_want_exit. + */ +static bool vfio_load_bufs_thread(void *opaque, bool *should_quit, Error **errp) +{ + VFIODevice *vbasedev = opaque; + VFIOMigration *migration = vbasedev->migration; + VFIOMultifd *multifd = migration->multifd; + bool ret = false; + + trace_vfio_load_bufs_thread_start(vbasedev->name); + + assert(multifd); + QEMU_LOCK_GUARD(&multifd->load_bufs_mutex); + + assert(multifd->load_bufs_thread_running); + + while (true) { + VFIOStateBuffer *lb; + + /* + * Always check cancellation first after the buffer_ready wait below in + * case that cond was signalled by vfio_load_cleanup_load_bufs_thread(). + */ + if (vfio_load_bufs_thread_want_exit(multifd, should_quit)) { + error_setg(errp, "operation cancelled"); + goto thread_exit; + } + + assert(multifd->load_buf_idx <= multifd->load_buf_idx_last); + + lb = vfio_load_state_buffer_get(multifd); + if (!lb) { + trace_vfio_load_state_device_buffer_starved(vbasedev->name, + multifd->load_buf_idx); + qemu_cond_wait(&multifd->load_bufs_buffer_ready_cond, + &multifd->load_bufs_mutex); + continue; + } + + if (multifd->load_buf_idx == multifd->load_buf_idx_last) { + break; + } + + if (multifd->load_buf_idx == 0) { + trace_vfio_load_state_device_buffer_start(vbasedev->name); + } + + if (!vfio_load_state_buffer_write(vbasedev, lb, errp)) { + goto thread_exit; + } + + if (multifd->load_buf_idx == multifd->load_buf_idx_last - 1) { + trace_vfio_load_state_device_buffer_end(vbasedev->name); + } + + multifd->load_buf_idx++; + } + + if (!vfio_load_bufs_thread_load_config(vbasedev, errp)) { + goto thread_exit; + } + + ret = true; + +thread_exit: + /* + * Notify possibly waiting vfio_load_cleanup_load_bufs_thread() that + * this thread is exiting. + */ + multifd->load_bufs_thread_running = false; + qemu_cond_signal(&multifd->load_bufs_thread_finished_cond); + + trace_vfio_load_bufs_thread_end(vbasedev->name); + + return ret; +} + +static VFIOMultifd *vfio_multifd_new(void) +{ + VFIOMultifd *multifd = g_new(VFIOMultifd, 1); + + vfio_state_buffers_init(&multifd->load_bufs); + + qemu_mutex_init(&multifd->load_bufs_mutex); + + multifd->load_buf_idx = 0; + multifd->load_buf_idx_last = UINT32_MAX; + qemu_cond_init(&multifd->load_bufs_buffer_ready_cond); + + multifd->load_bufs_thread_running = false; + multifd->load_bufs_thread_want_exit = false; + qemu_cond_init(&multifd->load_bufs_thread_finished_cond); + + return multifd; +} + +/* + * Terminates vfio_load_bufs_thread by setting + * multifd->load_bufs_thread_want_exit and signalling all the conditions + * the thread could be blocked on. + * + * Waits for the thread to signal that it had finished. + */ +static void vfio_load_cleanup_load_bufs_thread(VFIOMultifd *multifd) +{ + /* The lock order is load_bufs_mutex -> BQL so unlock BQL here first */ + bql_unlock(); + WITH_QEMU_LOCK_GUARD(&multifd->load_bufs_mutex) { + while (multifd->load_bufs_thread_running) { + multifd->load_bufs_thread_want_exit = true; + + qemu_cond_signal(&multifd->load_bufs_buffer_ready_cond); + qemu_cond_wait(&multifd->load_bufs_thread_finished_cond, + &multifd->load_bufs_mutex); + } + } + bql_lock(); +} + +static void vfio_multifd_free(VFIOMultifd *multifd) +{ + vfio_load_cleanup_load_bufs_thread(multifd); + + qemu_cond_destroy(&multifd->load_bufs_thread_finished_cond); + vfio_state_buffers_destroy(&multifd->load_bufs); + qemu_cond_destroy(&multifd->load_bufs_buffer_ready_cond); + qemu_mutex_destroy(&multifd->load_bufs_mutex); + + g_free(multifd); +} + +void vfio_multifd_cleanup(VFIODevice *vbasedev) +{ + VFIOMigration *migration = vbasedev->migration; + + g_clear_pointer(&migration->multifd, vfio_multifd_free); +} + +bool vfio_multifd_transfer_supported(void) +{ + return multifd_device_state_supported() && + migrate_send_switchover_start(); +} + +bool vfio_multifd_transfer_enabled(VFIODevice *vbasedev) +{ + VFIOMigration *migration = vbasedev->migration; + + return migration->multifd_transfer; +} + +bool vfio_multifd_setup(VFIODevice *vbasedev, bool alloc_multifd, Error **errp) +{ + VFIOMigration *migration = vbasedev->migration; + + /* + * Make a copy of this setting at the start in case it is changed + * mid-migration. + */ + if (vbasedev->migration_multifd_transfer == ON_OFF_AUTO_AUTO) { + migration->multifd_transfer = vfio_multifd_transfer_supported(); + } else { + migration->multifd_transfer = + vbasedev->migration_multifd_transfer == ON_OFF_AUTO_ON; + } + + if (!vfio_multifd_transfer_enabled(vbasedev)) { + /* Nothing further to check or do */ + return true; + } + + if (!vfio_multifd_transfer_supported()) { + error_setg(errp, + "%s: Multifd device transfer requested but unsupported in the current config", + vbasedev->name); + return false; + } + + if (alloc_multifd) { + assert(!migration->multifd); + migration->multifd = vfio_multifd_new(); + } + + return true; +} + +void vfio_multifd_emit_dummy_eos(VFIODevice *vbasedev, QEMUFile *f) +{ + assert(vfio_multifd_transfer_enabled(vbasedev)); + + /* + * Emit dummy NOP data on the main migration channel since the actual + * device state transfer is done via multifd channels. + */ + qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE); +} + +static bool +vfio_save_complete_precopy_thread_config_state(VFIODevice *vbasedev, + char *idstr, + uint32_t instance_id, + uint32_t idx, + Error **errp) +{ + g_autoptr(QIOChannelBuffer) bioc = NULL; + g_autoptr(QEMUFile) f = NULL; + int ret; + g_autofree VFIODeviceStatePacket *packet = NULL; + size_t packet_len; + + bioc = qio_channel_buffer_new(0); + qio_channel_set_name(QIO_CHANNEL(bioc), "vfio-device-config-save"); + + f = qemu_file_new_output(QIO_CHANNEL(bioc)); + + if (vfio_save_device_config_state(f, vbasedev, errp)) { + return false; + } + + ret = qemu_fflush(f); + if (ret) { + error_setg(errp, "%s: save config state flush failed: %d", + vbasedev->name, ret); + return false; + } + + packet_len = sizeof(*packet) + bioc->usage; + packet = g_malloc0(packet_len); + packet->version = cpu_to_be32(VFIO_DEVICE_STATE_PACKET_VER_CURRENT); + packet->idx = cpu_to_be32(idx); + packet->flags = cpu_to_be32(VFIO_DEVICE_STATE_CONFIG_STATE); + memcpy(&packet->data, bioc->data, bioc->usage); + + if (!multifd_queue_device_state(idstr, instance_id, + (char *)packet, packet_len)) { + error_setg(errp, "%s: multifd config data queuing failed", + vbasedev->name); + return false; + } + + vfio_migration_add_bytes_transferred(packet_len); + + return true; +} + +/* + * This thread is spawned by the migration core directly via + * .save_live_complete_precopy_thread SaveVMHandler. + * + * It exits after either: + * * completing saving the remaining device state and device config, OR: + * * encountering some error while doing the above, OR: + * * being forcefully aborted by the migration core by + * multifd_device_state_save_thread_should_exit() returning true. + */ +bool +vfio_multifd_save_complete_precopy_thread(SaveLiveCompletePrecopyThreadData *d, + Error **errp) +{ + VFIODevice *vbasedev = d->handler_opaque; + VFIOMigration *migration = vbasedev->migration; + bool ret = false; + g_autofree VFIODeviceStatePacket *packet = NULL; + uint32_t idx; + + if (!vfio_multifd_transfer_enabled(vbasedev)) { + /* Nothing to do, vfio_save_complete_precopy() does the transfer. */ + return true; + } + + trace_vfio_save_complete_precopy_thread_start(vbasedev->name, + d->idstr, d->instance_id); + + /* We reach here with device state STOP or STOP_COPY only */ + if (vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_STOP_COPY, + VFIO_DEVICE_STATE_STOP, errp)) { + goto thread_exit; + } + + packet = g_malloc0(sizeof(*packet) + migration->data_buffer_size); + packet->version = cpu_to_be32(VFIO_DEVICE_STATE_PACKET_VER_CURRENT); + + for (idx = 0; ; idx++) { + ssize_t data_size; + size_t packet_size; + + if (multifd_device_state_save_thread_should_exit()) { + error_setg(errp, "operation cancelled"); + goto thread_exit; + } + + data_size = read(migration->data_fd, &packet->data, + migration->data_buffer_size); + if (data_size < 0) { + error_setg(errp, "%s: reading state buffer %" PRIu32 " failed: %d", + vbasedev->name, idx, errno); + goto thread_exit; + } else if (data_size == 0) { + break; + } + + packet->idx = cpu_to_be32(idx); + packet_size = sizeof(*packet) + data_size; + + if (!multifd_queue_device_state(d->idstr, d->instance_id, + (char *)packet, packet_size)) { + error_setg(errp, "%s: multifd data queuing failed", vbasedev->name); + goto thread_exit; + } + + vfio_migration_add_bytes_transferred(packet_size); + } + + if (!vfio_save_complete_precopy_thread_config_state(vbasedev, + d->idstr, + d->instance_id, + idx, errp)) { + goto thread_exit; + } + + ret = true; + +thread_exit: + trace_vfio_save_complete_precopy_thread_end(vbasedev->name, ret); + + return ret; +} + +int vfio_multifd_switchover_start(VFIODevice *vbasedev) +{ + VFIOMigration *migration = vbasedev->migration; + VFIOMultifd *multifd = migration->multifd; + + assert(multifd); + + /* The lock order is load_bufs_mutex -> BQL so unlock BQL here first */ + bql_unlock(); + WITH_QEMU_LOCK_GUARD(&multifd->load_bufs_mutex) { + assert(!multifd->load_bufs_thread_running); + multifd->load_bufs_thread_running = true; + } + bql_lock(); + + qemu_loadvm_start_load_thread(vfio_load_bufs_thread, vbasedev); + + return 0; +} diff --git a/hw/vfio/migration-multifd.h b/hw/vfio/migration-multifd.h new file mode 100644 index 00000000000..0bab63211d3 --- /dev/null +++ b/hw/vfio/migration-multifd.h @@ -0,0 +1,34 @@ +/* + * Multifd VFIO migration + * + * Copyright (C) 2024,2025 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_MIGRATION_MULTIFD_H +#define HW_VFIO_MIGRATION_MULTIFD_H + +#include "hw/vfio/vfio-device.h" + +bool vfio_multifd_setup(VFIODevice *vbasedev, bool alloc_multifd, Error **errp); +void vfio_multifd_cleanup(VFIODevice *vbasedev); + +bool vfio_multifd_transfer_supported(void); +bool vfio_multifd_transfer_enabled(VFIODevice *vbasedev); + +bool vfio_multifd_load_state_buffer(void *opaque, char *data, size_t data_size, + Error **errp); + +void vfio_multifd_emit_dummy_eos(VFIODevice *vbasedev, QEMUFile *f); + +bool +vfio_multifd_save_complete_precopy_thread(SaveLiveCompletePrecopyThreadData *d, + Error **errp); + +int vfio_multifd_switchover_start(VFIODevice *vbasedev); + +#endif diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index da43dcd2fe0..b76697bd1a2 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -15,39 +15,23 @@ #include #include -#include "sysemu/runstate.h" -#include "hw/vfio/vfio-common.h" -#include "migration/migration.h" -#include "migration/options.h" +#include "system/runstate.h" +#include "hw/vfio/vfio-device.h" +#include "hw/vfio/vfio-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 "migration-multifd.h" #include "qapi/error.h" +#include "qapi/qapi-events-vfio.h" #include "exec/ramlist.h" -#include "exec/ram_addr.h" #include "pci.h" #include "trace.h" #include "hw/hw.h" - -/* - * Flags to be used as unique delimiters for VFIO devices in the migration - * stream. These flags are composed as: - * 0xffffffff => MSB 32-bit all 1s - * 0xef10 => Magic ID, represents emulated (virtual) function IO - * 0x0000 => 16-bits reserved for flags - * - * The beginning of state information is marked by _DEV_CONFIG_STATE, - * _DEV_SETUP_STATE, or _DEV_DATA_STATE, respectively. The end of a - * certain state information is marked by _END_OF_STATE. - */ -#define VFIO_MIG_FLAG_END_OF_STATE (0xffffffffef100001ULL) -#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) +#include "vfio-migration-internal.h" /* * This is an arbitrary size based on migration of mlx5 devices, where typically @@ -56,7 +40,7 @@ */ #define VFIO_MIG_DEFAULT_DATA_BUFFER_SIZE (1 * MiB) -static int64_t bytes_transferred; +static unsigned long bytes_transferred; static const char *mig_state_to_str(enum vfio_device_mig_state state) { @@ -82,9 +66,65 @@ static const char *mig_state_to_str(enum vfio_device_mig_state state) } } -static int vfio_migration_set_state(VFIODevice *vbasedev, - enum vfio_device_mig_state new_state, - enum vfio_device_mig_state recover_state) +static QapiVfioMigrationState +mig_state_to_qapi_state(enum vfio_device_mig_state state) +{ + 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(); + } +} + +static void vfio_migration_send_event(VFIODevice *vbasedev) +{ + VFIOMigration *migration = vbasedev->migration; + DeviceState *dev = vbasedev->dev; + g_autofree char *qom_path = NULL; + Object *obj; + + if (!vbasedev->migration_events) { + return; + } + + g_assert(vbasedev->ops->vfio_get_object); + obj = vbasedev->ops->vfio_get_object(vbasedev); + g_assert(obj); + qom_path = object_get_canonical_path(obj); + + qapi_event_send_vfio_migration( + dev->id, qom_path, mig_state_to_qapi_state(migration->device_state)); +} + +static void vfio_migration_set_device_state(VFIODevice *vbasedev, + enum vfio_device_mig_state state) +{ + VFIOMigration *migration = vbasedev->migration; + + trace_vfio_migration_set_device_state(vbasedev->name, + mig_state_to_str(state)); + + migration->device_state = state; + vfio_migration_send_event(vbasedev); +} + +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) + @@ -94,6 +134,16 @@ static int vfio_migration_set_state(VFIODevice *vbasedev, 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; + } feature->argsz = sizeof(buf); feature->flags = @@ -104,22 +154,24 @@ static int vfio_migration_set_state(VFIODevice *vbasedev, ret = -errno; if (recover_state == VFIO_DEVICE_STATE_ERROR) { - error_report("%s: Failed setting device state to %s, err: %s. " - "Recover state is ERROR. Resetting device", - vbasedev->name, mig_state_to_str(new_state), - strerror(errno)); + error_setg_errno(errp, errno, + "%s Recover state is ERROR. Resetting device", + error_prefix); goto reset_device; } - error_report( - "%s: Failed setting device state to %s, err: %s. Setting device in recover state %s", - vbasedev->name, mig_state_to_str(new_state), - strerror(errno), mig_state_to_str(recover_state)); + 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; + /* + * If setting the device in recover state fails, report + * the error here and propagate the first error. + */ error_report( "%s: Failed setting device in recover state, err: %s. Resetting device", vbasedev->name, strerror(errno)); @@ -127,19 +179,19 @@ static int vfio_migration_set_state(VFIODevice *vbasedev, goto reset_device; } - migration->device_state = recover_state; + vfio_migration_set_device_state(vbasedev, recover_state); return ret; } - migration->device_state = new_state; + 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_report("%s: data_fd out of sync", vbasedev->name); + error_setg(errp, "%s: data_fd out of sync", vbasedev->name); close(mig_state->data_fd); return -EBADF; @@ -148,8 +200,6 @@ static int vfio_migration_set_state(VFIODevice *vbasedev, migration->data_fd = mig_state->data_fd; } - trace_vfio_migration_set_state(vbasedev->name, mig_state_to_str(new_state)); - return 0; reset_device: @@ -158,11 +208,25 @@ static int vfio_migration_set_state(VFIODevice *vbasedev, strerror(errno)); } - migration->device_state = VFIO_DEVICE_STATE_RUNNING; + vfio_migration_set_device_state(vbasedev, VFIO_DEVICE_STATE_RUNNING); return ret; } +/* + * 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) +{ + 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) { @@ -175,28 +239,38 @@ static int vfio_load_buffer(QEMUFile *f, VFIODevice *vbasedev, return ret; } -static int vfio_save_device_config_state(QEMUFile *f, void *opaque) +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) +int vfio_load_device_config_state(QEMUFile *f, void *opaque) { VFIODevice *vbasedev = opaque; uint64_t data; + trace_vfio_load_device_config_state_start(vbasedev->name); + if (vbasedev->ops && vbasedev->ops->vfio_load_config) { int ret; @@ -215,7 +289,7 @@ static int vfio_load_device_config_state(QEMUFile *f, void *opaque) return -EINVAL; } - trace_vfio_load_device_config_state(vbasedev->name); + trace_vfio_load_device_config_state_end(vbasedev->name); return qemu_file_get_error(f); } @@ -282,6 +356,10 @@ static ssize_t vfio_save_block(QEMUFile *f, VFIOMigration *migration) * 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; } @@ -291,10 +369,13 @@ static ssize_t vfio_save_block(QEMUFile *f, VFIOMigration *migration) 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; + vfio_migration_add_bytes_transferred(data_size); trace_vfio_save_block(migration->vbasedev->name, data_size); @@ -365,11 +446,16 @@ static int vfio_save_prepare(void *opaque, Error **errp) return 0; } -static int vfio_save_setup(QEMUFile *f, void *opaque) +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; + + if (!vfio_multifd_setup(vbasedev, false, errp)) { + return -EINVAL; + } qemu_put_be64(f, VFIO_MIG_FLAG_DEV_SETUP_STATE); @@ -378,18 +464,19 @@ static int vfio_save_setup(QEMUFile *f, void *opaque) stop_copy_size); migration->data_buffer = g_try_malloc0(migration->data_buffer_size); if (!migration->data_buffer) { - error_report("%s: Failed to allocate migration data buffer", - vbasedev->name); + error_setg(errp, "%s: Failed to allocate migration data buffer", + vbasedev->name); return -ENOMEM; } - if (vfio_precopy_supported(vbasedev)) { - int ret; + 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); + VFIO_DEVICE_STATE_RUNNING, errp); if (ret) { return ret; } @@ -401,6 +488,8 @@ static int vfio_save_setup(QEMUFile *f, void *opaque) /* 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; } } @@ -409,25 +498,35 @@ static int vfio_save_setup(QEMUFile *f, void *opaque) qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE); - return qemu_file_get_error(f); + ret = qemu_file_get_error(f); + if (ret < 0) { + error_setg_errno(errp, -ret, "%s: save setup failed", vbasedev->name); + } + + return ret; } static void vfio_save_cleanup(void *opaque) { VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; + Error *local_err = NULL; + int ret; + + /* Currently a NOP, done for symmetry with load_cleanup() */ + vfio_multifd_cleanup(vbasedev); /* * 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) { - /* - * If setting the device in STOP state fails, the device should be - * reset. To do so, use ERROR state as a recover state. - */ - vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_STOP, - VFIO_DEVICE_STATE_ERROR); + 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); @@ -480,9 +579,6 @@ static void vfio_state_pending_exact(void *opaque, uint64_t *must_precopy, if (vfio_device_state_is_precopy(vbasedev)) { vfio_query_precopy_size(migration); - - *must_precopy += - migration->precopy_init_size + migration->precopy_dirty_size; } trace_vfio_state_pending_exact(vbasedev->name, *must_precopy, *can_postcopy, @@ -497,12 +593,23 @@ static bool vfio_is_active_iterate(void *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; ssize_t data_size; + if (!migration->event_save_iterate_started) { + trace_vfio_save_iterate_start(vbasedev->name); + migration->event_save_iterate_started = true; + } + data_size = vfio_save_block(f, migration); if (data_size < 0) { return data_size; @@ -521,11 +628,7 @@ static int vfio_save_iterate(QEMUFile *f, void *opaque) trace_vfio_save_iterate(vbasedev->name, migration->precopy_init_size, migration->precopy_dirty_size); - /* - * A VFIO device's pre-copy dirty_bytes is not guaranteed to reach zero. - * Return 1 so following handlers will not be potentially blocked. - */ - return 1; + return !migration->precopy_init_size && !migration->precopy_dirty_size; } static int vfio_save_complete_precopy(QEMUFile *f, void *opaque) @@ -533,11 +636,20 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque) VFIODevice *vbasedev = opaque; ssize_t data_size; int ret; + Error *local_err = NULL; + + if (vfio_multifd_transfer_enabled(vbasedev)) { + vfio_multifd_emit_dummy_eos(vbasedev, f); + return 0; + } + + 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); + VFIO_DEVICE_STATE_STOP, &local_err); if (ret) { + error_report_err(local_err); return ret; } @@ -550,9 +662,6 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque) qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE); ret = qemu_file_get_error(f); - if (ret) { - return ret; - } trace_vfio_save_complete_precopy(vbasedev->name, ret); @@ -562,28 +671,48 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque) 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); + if (vfio_multifd_transfer_enabled(vbasedev)) { + vfio_multifd_emit_dummy_eos(vbasedev, f); + return; + } + + 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; - return vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_RESUMING, - vbasedev->migration->device_state); + if (!vfio_multifd_setup(vbasedev, true, errp)) { + return -EINVAL; + } + + ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_RESUMING, + migration->device_state, errp); + if (ret) { + return ret; + } + + return 0; } static int vfio_load_cleanup(void *opaque) { VFIODevice *vbasedev = opaque; + vfio_multifd_cleanup(vbasedev); + vfio_migration_cleanup(vbasedev); trace_vfio_load_cleanup(vbasedev->name); @@ -604,6 +733,13 @@ static int vfio_load_state(QEMUFile *f, void *opaque, int version_id) switch (data) { case VFIO_MIG_FLAG_DEV_CONFIG_STATE: { + if (vfio_multifd_transfer_enabled(vbasedev)) { + error_report("%s: got DEV_CONFIG_STATE in main migration " + "channel but doing multifd transfer", + vbasedev->name); + return -EINVAL; + } + return vfio_load_device_config_state(f, opaque); } case VFIO_MIG_FLAG_DEV_SETUP_STATE: @@ -669,6 +805,17 @@ static bool vfio_switchover_ack_needed(void *opaque) return vfio_precopy_supported(vbasedev); } +static int vfio_switchover_start(void *opaque) +{ + VFIODevice *vbasedev = opaque; + + if (vfio_multifd_transfer_enabled(vbasedev)) { + return vfio_multifd_switchover_start(vbasedev); + } + + return 0; +} + static const SaveVMHandlers savevm_vfio_handlers = { .save_prepare = vfio_save_prepare, .save_setup = vfio_save_setup, @@ -683,6 +830,12 @@ static const SaveVMHandlers savevm_vfio_handlers = { .load_cleanup = vfio_load_cleanup, .load_state = vfio_load_state, .switchover_ack_needed = vfio_switchover_ack_needed, + /* + * Multifd support + */ + .load_state_buffer = vfio_multifd_load_state_buffer, + .switchover_start = vfio_switchover_start, + .save_live_complete_precopy_thread = vfio_multifd_save_complete_precopy_thread, }; /* ---------------------------------------------------------------------- */ @@ -693,26 +846,20 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running, VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; enum vfio_device_mig_state new_state; + Error *local_err = NULL; int ret; new_state = migration->device_state == VFIO_DEVICE_STATE_PRE_COPY ? VFIO_DEVICE_STATE_PRE_COPY_P2P : VFIO_DEVICE_STATE_RUNNING_P2P; - /* - * If setting the device in new_state fails, the device should be reset. - * To do so, use ERROR state as a recover state. - */ - ret = vfio_migration_set_state(vbasedev, new_state, - VFIO_DEVICE_STATE_ERROR); + 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. */ - if (migrate_get_current()->to_dst_file) { - qemu_file_set_error(migrate_get_current()->to_dst_file, ret); - } + migration_file_set_error(ret, local_err); } trace_vfio_vmstate_change_prepare(vbasedev->name, running, @@ -724,6 +871,7 @@ 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) { @@ -736,47 +884,44 @@ static void vfio_vmstate_change(void *opaque, bool running, RunState state) VFIO_DEVICE_STATE_STOP; } - /* - * If setting the device in new_state fails, the device should be reset. - * To do so, use ERROR state as a recover state. - */ - ret = vfio_migration_set_state(vbasedev, new_state, - VFIO_DEVICE_STATE_ERROR); + 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. */ - if (migrate_get_current()->to_dst_file) { - qemu_file_set_error(migrate_get_current()->to_dst_file, ret); - } + migration_file_set_error(ret, local_err); } trace_vfio_vmstate_change(vbasedev->name, running, RunState_str(state), mig_state_to_str(new_state)); } -static void vfio_migration_state_notifier(Notifier *notifier, void *data) +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: + if (e->type == MIG_EVENT_PRECOPY_FAILED) { /* - * If setting the device in RUNNING state fails, the device should - * be reset. To do so, use ERROR state as a recover state. + * 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. */ - vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_RUNNING, - VFIO_DEVICE_STATE_ERROR); + ret = vfio_migration_set_state_or_reset(vbasedev, + VFIO_DEVICE_STATE_RUNNING, + &local_err); + if (ret) { + error_report_err(local_err); + } } + return 0; } static void vfio_migration_free(VFIODevice *vbasedev) @@ -871,18 +1016,77 @@ static int vfio_migration_init(VFIODevice *vbasedev) vfio_vmstate_change_prepare : NULL; migration->vm_state = qdev_add_vm_change_state_handler_full( - vbasedev->dev, vfio_vmstate_change, prepare_cb, vbasedev); - migration->migration_state.notify = vfio_migration_state_notifier; - add_migration_state_change_notifier(&migration->migration_state); + vbasedev->dev, vfio_vmstate_change, prepare_cb, NULL, vbasedev); + migration_add_notifier(&migration->migration_state, + vfio_migration_state_notifier); return 0; } +static Error *multiple_devices_migration_blocker; + +/* + * Multiple devices migration is allowed only if all devices support P2P + * migration. Single device migration is allowed regardless of P2P migration + * support. + */ +static bool vfio_multiple_devices_migration_is_supported(void) +{ + VFIODevice *vbasedev; + unsigned int device_num = 0; + bool all_support_p2p = true; + + QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { + if (vbasedev->migration) { + device_num++; + + if (!(vbasedev->migration->mig_flags & VFIO_MIGRATION_P2P)) { + all_support_p2p = false; + } + } + } + + return all_support_p2p || device_num <= 1; +} + +static int vfio_block_multiple_devices_migration(VFIODevice *vbasedev, Error **errp) +{ + if (vfio_multiple_devices_migration_is_supported()) { + return 0; + } + + 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"); + return migrate_add_blocker_normal(&multiple_devices_migration_blocker, + errp); +} + +static void vfio_unblock_multiple_devices_migration(void) +{ + if (!multiple_devices_migration_blocker || + !vfio_multiple_devices_migration_is_supported()) { + return; + } + + migrate_del_blocker(&multiple_devices_migration_blocker); +} + static void vfio_migration_deinit(VFIODevice *vbasedev) { VFIOMigration *migration = vbasedev->migration; - remove_migration_state_change_notifier(&migration->migration_state); + 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); @@ -891,8 +1095,6 @@ static void vfio_migration_deinit(VFIODevice *vbasedev) static int vfio_block_migration(VFIODevice *vbasedev, Error *err, Error **errp) { - int ret; - if (vbasedev->enable_migration == ON_OFF_AUTO_ON) { error_propagate(errp, err); return -EINVAL; @@ -901,25 +1103,45 @@ static int vfio_block_migration(VFIODevice *vbasedev, Error *err, Error **errp) vbasedev->migration_blocker = error_copy(err); error_free(err); - ret = migrate_add_blocker(vbasedev->migration_blocker, errp); - if (ret < 0) { - error_free(vbasedev->migration_blocker); - vbasedev->migration_blocker = NULL; - } - - return ret; + return migrate_add_blocker_normal(&vbasedev->migration_blocker, errp); } /* ---------------------------------------------------------------------- */ -int64_t vfio_mig_bytes_transferred(void) +int64_t vfio_migration_bytes_transferred(void) +{ + return MIN(qatomic_read(&bytes_transferred), INT64_MAX); +} + +void vfio_migration_reset_bytes_transferred(void) { - return bytes_transferred; + qatomic_set(&bytes_transferred, 0); } -void vfio_reset_bytes_transferred(void) +void vfio_migration_add_bytes_transferred(unsigned long val) { - bytes_transferred = 0; + qatomic_add(&bytes_transferred, val); +} + +bool vfio_migration_active(void) +{ + VFIODevice *vbasedev; + + if (QLIST_EMPTY(&vfio_device_list)) { + return false; + } + + QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { + if (vbasedev->migration_blocker) { + return false; + } + } + return true; +} + +static bool vfio_viommu_preset(VFIODevice *vbasedev) +{ + return vbasedev->bcontainer->space->as != &address_space_memory; } /* @@ -953,16 +1175,18 @@ bool vfio_migration_realize(VFIODevice *vbasedev, Error **errp) return !vfio_block_migration(vbasedev, err, errp); } - if (!vbasedev->dirty_pages_supported) { + 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 dirty tracking", - vbasedev->name); + "%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 dirty tracking", - vbasedev->name); + warn_report("%s: VFIO device doesn't support device and " + "IOMMU dirty tracking", vbasedev->name); } ret = vfio_block_multiple_devices_migration(vbasedev, errp); @@ -994,9 +1218,21 @@ void vfio_migration_exit(VFIODevice *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); +} + +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; } diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index f4ff8368057..3f002252acf 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -25,6 +25,7 @@ #include "hw/nvram/fw_cfg.h" #include "hw/qdev-properties.h" #include "pci.h" +#include "pci-quirks.h" #include "trace.h" /* @@ -66,40 +67,6 @@ bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev) * Device specific region quirks (mostly backdoors to PCI config space) */ -/* - * The generic window quirks operate on an address and data register, - * vfio_generic_window_address_quirk handles the address register and - * vfio_generic_window_data_quirk handles the data register. These ops - * pass reads and writes through to hardware until a value matching the - * stored address match/mask is written. When this occurs, the data - * register access emulated PCI config space for the device rather than - * passing through accesses. This enables devices where PCI config space - * is accessible behind a window register to maintain the virtualization - * provided through vfio. - */ -typedef struct VFIOConfigWindowMatch { - uint32_t match; - uint32_t mask; -} VFIOConfigWindowMatch; - -typedef struct VFIOConfigWindowQuirk { - struct VFIOPCIDevice *vdev; - - uint32_t address_val; - - uint32_t address_offset; - uint32_t data_offset; - - bool window_enabled; - uint8_t bar; - - MemoryRegion *addr_mem; - MemoryRegion *data_mem; - - uint32_t nr_matches; - VFIOConfigWindowMatch matches[]; -} VFIOConfigWindowQuirk; - static uint64_t vfio_generic_window_quirk_address_read(void *opaque, hwaddr addr, unsigned size) @@ -135,7 +102,7 @@ static void vfio_generic_window_quirk_address_write(void *opaque, hwaddr addr, } } -static const MemoryRegionOps vfio_generic_window_address_quirk = { +const MemoryRegionOps vfio_generic_window_address_quirk = { .read = vfio_generic_window_quirk_address_read, .write = vfio_generic_window_quirk_address_write, .endianness = DEVICE_LITTLE_ENDIAN, @@ -178,26 +145,12 @@ static void vfio_generic_window_quirk_data_write(void *opaque, hwaddr addr, addr + window->data_offset, data, size); } -static const MemoryRegionOps vfio_generic_window_data_quirk = { +const MemoryRegionOps vfio_generic_window_data_quirk = { .read = vfio_generic_window_quirk_data_read, .write = vfio_generic_window_quirk_data_write, .endianness = DEVICE_LITTLE_ENDIAN, }; -/* - * The generic mirror quirk handles devices which expose PCI config space - * through a region within a BAR. When enabled, reads and writes are - * redirected through to emulated PCI config space. XXX if PCI config space - * used memory regions, this could just be an alias. - */ -typedef struct VFIOConfigMirrorQuirk { - struct VFIOPCIDevice *vdev; - uint32_t offset; - uint8_t bar; - MemoryRegion *mem; - uint8_t data[]; -} VFIOConfigMirrorQuirk; - static uint64_t vfio_generic_quirk_mirror_read(void *opaque, hwaddr addr, unsigned size) { @@ -209,6 +162,7 @@ static uint64_t vfio_generic_quirk_mirror_read(void *opaque, (void)vfio_region_read(&vdev->bars[mirror->bar].region, addr + mirror->offset, size); + addr += mirror->config_offset; data = vfio_pci_read_config(&vdev->pdev, addr, size); trace_vfio_quirk_generic_mirror_read(vdev->vbasedev.name, memory_region_name(mirror->mem), @@ -222,13 +176,14 @@ static void vfio_generic_quirk_mirror_write(void *opaque, hwaddr addr, VFIOConfigMirrorQuirk *mirror = opaque; VFIOPCIDevice *vdev = mirror->vdev; + addr += mirror->config_offset; vfio_pci_write_config(&vdev->pdev, addr, data, size); trace_vfio_quirk_generic_mirror_write(vdev->vbasedev.name, memory_region_name(mirror->mem), addr, data); } -static const MemoryRegionOps vfio_generic_mirror_quirk = { +const MemoryRegionOps vfio_generic_mirror_quirk = { .read = vfio_generic_quirk_mirror_read, .write = vfio_generic_quirk_mirror_write, .endianness = DEVICE_LITTLE_ENDIAN, @@ -448,7 +403,7 @@ static void vfio_probe_ati_bar4_quirk(VFIOPCIDevice *vdev, int nr) /* This windows doesn't seem to be used except by legacy VGA code */ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) || - !vdev->vga || nr != 4) { + !vdev->vga || nr != 4 || !vdev->bars[4].ioport) { return; } @@ -1159,59 +1114,19 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr) trace_vfio_quirk_rtl8168_probe(vdev->vbasedev.name); } -#define IGD_ASLS 0xfc /* ASL Storage Register */ - /* - * The OpRegion includes the Video BIOS Table, which seems important for - * telling the driver what sort of outputs it has. Without this, the device - * may work in the guest, but we may not get output. This also requires BIOS - * support to reserve and populate a section of guest memory sufficient for - * the table and to write the base address of that memory to the ASLS register - * of the IGD device. + * Common quirk probe entry points. */ -int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, - struct vfio_region_info *info, Error **errp) +bool vfio_config_quirk_setup(VFIOPCIDevice *vdev, Error **errp) { - int ret; - - vdev->igd_opregion = g_malloc0(info->size); - ret = pread(vdev->vbasedev.fd, vdev->igd_opregion, - info->size, info->offset); - if (ret != info->size) { - error_setg(errp, "failed to read IGD OpRegion"); - g_free(vdev->igd_opregion); - vdev->igd_opregion = NULL; - return -EINVAL; +#ifdef CONFIG_VFIO_IGD + if (!vfio_probe_igd_config_quirk(vdev, errp)) { + return false; } - - /* - * Provide fw_cfg with a copy of the OpRegion which the VM firmware is to - * allocate 32bit reserved memory for, copy these contents into, and write - * the reserved memory base address to the device ASLS register at 0xFC. - * Alignment of this reserved region seems flexible, but using a 4k page - * alignment seems to work well. This interface assumes a single IGD - * device, which may be at VM address 00:02.0 in legacy mode or another - * address in UPT mode. - * - * NB, there may be future use cases discovered where the VM should have - * direct interaction with the host OpRegion, in which case the write to - * the ASLS register would trigger MemoryRegion setup to enable that. - */ - fw_cfg_add_file(fw_cfg_find(), "etc/igd-opregion", - vdev->igd_opregion, info->size); - - trace_vfio_pci_igd_opregion_enabled(vdev->vbasedev.name); - - pci_set_long(vdev->pdev.config + IGD_ASLS, 0); - pci_set_long(vdev->pdev.wmask + IGD_ASLS, ~0); - pci_set_long(vdev->emulated_config_bits + IGD_ASLS, ~0); - - return 0; +#endif + return true; } -/* - * Common quirk probe entry points. - */ void vfio_vga_quirk_setup(VFIOPCIDevice *vdev) { vfio_vga_probe_ati_3c3_quirk(vdev); @@ -1259,7 +1174,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_bar4_quirk(vdev, nr); + vfio_probe_igd_bar0_quirk(vdev, nr); #endif } @@ -1498,7 +1413,7 @@ static void get_nv_gpudirect_clique_id(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint8_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint8(v, name, ptr, errp); @@ -1508,7 +1423,7 @@ static void set_nv_gpudirect_clique_id(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint8_t value, *ptr = object_field_prop_ptr(obj, prop); if (!visit_type_uint8(v, name, &value, errp)) { @@ -1524,7 +1439,7 @@ static void set_nv_gpudirect_clique_id(Object *obj, Visitor *v, } const PropertyInfo qdev_prop_nv_gpudirect_clique = { - .name = "uint4", + .type = "uint8", .description = "NVIDIA GPUDirect Clique ID (0 - 15)", .get = get_nv_gpudirect_clique_id, .set = set_nv_gpudirect_clique_id, @@ -1536,26 +1451,27 @@ static bool is_valid_std_cap_offset(uint8_t pos) pos <= (PCI_CFG_SPACE_SIZE - PCI_CAP_SIZEOF)); } -static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) { + ERRP_GUARD(); PCIDevice *pdev = &vdev->pdev; 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; } /* @@ -1571,7 +1487,7 @@ static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) vdev->config_offset + PCI_CAPABILITY_LIST); if (ret != 1 || !is_valid_std_cap_offset(tmp)) { error_setg(errp, "NVIDIA GPUDirect Clique ID: error getting cap list"); - return -EINVAL; + return false; } do { @@ -1589,13 +1505,13 @@ static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) pos = 0xD4; } else { error_setg(errp, "NVIDIA GPUDirect Clique ID: invalid config space"); - return -EINVAL; + 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); @@ -1607,122 +1523,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; } /* @@ -1743,8 +1544,9 @@ int vfio_pci_nvlink2_init(VFIOPCIDevice *vdev, Error **errp) */ #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; @@ -1752,7 +1554,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, @@ -1760,14 +1562,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); @@ -1777,22 +1579,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-quirks.h b/hw/vfio/pci-quirks.h new file mode 100644 index 00000000000..d1532e379b1 --- /dev/null +++ b/hw/vfio/pci-quirks.h @@ -0,0 +1,72 @@ +/* + * vfio generic region quirks (mostly backdoors to PCI config space) + * + * Copyright Red Hat, Inc. 2012-2015 + * + * 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. + */ +#ifndef HW_VFIO_VFIO_PCI_QUIRKS_H +#define HW_VFIO_VFIO_PCI_QUIRKS_H + +#include "qemu/osdep.h" +#include "exec/memop.h" + +/* + * The generic window quirks operate on an address and data register, + * vfio_generic_window_address_quirk handles the address register and + * vfio_generic_window_data_quirk handles the data register. These ops + * pass reads and writes through to hardware until a value matching the + * stored address match/mask is written. When this occurs, the data + * register access emulated PCI config space for the device rather than + * passing through accesses. This enables devices where PCI config space + * is accessible behind a window register to maintain the virtualization + * provided through vfio. + */ +typedef struct VFIOConfigWindowMatch { + uint32_t match; + uint32_t mask; +} VFIOConfigWindowMatch; + +typedef struct VFIOConfigWindowQuirk { + struct VFIOPCIDevice *vdev; + + uint32_t address_val; + + uint32_t address_offset; + uint32_t data_offset; + + bool window_enabled; + uint8_t bar; + + MemoryRegion *addr_mem; + MemoryRegion *data_mem; + + uint32_t nr_matches; + VFIOConfigWindowMatch matches[]; +} VFIOConfigWindowQuirk; + +extern const MemoryRegionOps vfio_generic_window_address_quirk; +extern const MemoryRegionOps vfio_generic_window_data_quirk; + +/* + * The generic mirror quirk handles devices which expose PCI config space + * through a region within a BAR. When enabled, reads and writes are + * redirected through to emulated PCI config space. XXX if PCI config space + * used memory regions, this could just be an alias. + */ +typedef struct VFIOConfigMirrorQuirk { + struct VFIOPCIDevice *vdev; + uint32_t offset; /* Offset in BAR */ + uint32_t config_offset; /* Offset in PCI config space */ + uint8_t bar; + MemoryRegion *mem; + uint8_t data[]; +} VFIOConfigMirrorQuirk; + +extern const MemoryRegionOps vfio_generic_mirror_quirk; + +#endif /* HW_VFIO_VFIO_PCI_QUIRKS_H */ diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index a205c6b1130..fa25bded25c 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 @@ -29,19 +30,23 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" -#include "qapi/qmp/qdict.h" +#include "migration/cpr.h" +#include "qobject/qdict.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/range.h" #include "qemu/units.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" +#include "system/kvm.h" +#include "system/runstate.h" #include "pci.h" #include "trace.h" #include "qapi/error.h" #include "migration/blocker.h" #include "migration/qemu-file.h" +#include "system/iommufd.h" +#include "vfio-migration-internal.h" +#include "vfio-helpers.h" #define TYPE_VFIO_PCI_NOHOTPLUG "vfio-pci-nohotplug" @@ -52,6 +57,23 @@ static void vfio_disable_interrupts(VFIOPCIDevice *vdev); static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled); static void vfio_msi_disable_common(VFIOPCIDevice *vdev); +static bool vfio_notifier_init(VFIOPCIDevice *vdev, EventNotifier *e, + const char *name, int nr, Error **errp) +{ + int ret = event_notifier_init(e, 0); + + if (ret) { + error_setg_errno(errp, -ret, "vfio_notifier_init %s failed", name); + } + return !ret; +} + +static void vfio_notifier_cleanup(VFIOPCIDevice *vdev, EventNotifier *e, + const char *name, int nr) +{ + event_notifier_cleanup(e); +} + /* * Disabling BAR mmaping can be slow, but toggling it around INTx can * also be a huge overhead. We try to get the best of both worlds by @@ -99,7 +121,7 @@ static void vfio_intx_interrupt(void *opaque) } } -static void vfio_intx_eoi(VFIODevice *vbasedev) +void vfio_pci_intx_eoi(VFIODevice *vbasedev) { VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); @@ -107,14 +129,14 @@ static void vfio_intx_eoi(VFIODevice *vbasedev) return; } - trace_vfio_intx_eoi(vbasedev->name); + trace_vfio_pci_intx_eoi(vbasedev->name); vdev->intx.pending = false; pci_irq_deassert(&vdev->pdev); - vfio_unmask_single_irqindex(vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + vfio_device_irq_unmask(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,18 +144,17 @@ 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 */ qemu_set_fd_handler(irq_fd, NULL, NULL, vdev); - vfio_mask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + vfio_device_irq_mask(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; pci_irq_deassert(&vdev->pdev); /* Get an eventfd for resample/unmask */ - if (event_notifier_init(&vdev->intx.unmask, 0)) { - error_setg(errp, "event_notifier_init failed eoi"); + if (!vfio_notifier_init(vdev, &vdev->intx.unmask, "intx-unmask", 0, errp)) { goto fail; } @@ -145,30 +166,33 @@ 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_device_irq_set_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; } /* Let'em rip */ - vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + vfio_device_irq_unmask(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.kvm_accel = true; trace_vfio_intx_enable_kvm(vdev->vbasedev.name); - return; + return true; fail_vfio: kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, &vdev->intx.interrupt, vdev->intx.route.irq); fail_irqfd: - event_notifier_cleanup(&vdev->intx.unmask); + vfio_notifier_cleanup(vdev, &vdev->intx.unmask, "intx-unmask", 0); fail: qemu_set_fd_handler(irq_fd, vfio_intx_interrupt, NULL, vdev); - vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + vfio_device_irq_unmask(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + return false; +#else + return true; #endif } @@ -183,7 +207,7 @@ static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev) * Get to a known state, hardware masked, QEMU ready to accept new * interrupts, QEMU IRQ de-asserted. */ - vfio_mask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + vfio_device_irq_mask(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; pci_irq_deassert(&vdev->pdev); @@ -194,7 +218,7 @@ static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev) } /* We only need to close the eventfd for VFIO to cleanup the kernel side */ - event_notifier_cleanup(&vdev->intx.unmask); + vfio_notifier_cleanup(vdev, &vdev->intx.unmask, "intx-unmask", 0); /* QEMU starts listening for interrupt events. */ qemu_set_fd_handler(event_notifier_get_fd(&vdev->intx.interrupt), @@ -203,7 +227,7 @@ static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev) vdev->intx.kvm_accel = false; /* If we've missed an event, let it re-fire through QEMU */ - vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + vfio_device_irq_unmask(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); trace_vfio_intx_disable_kvm(vdev->vbasedev.name); #endif @@ -224,18 +248,17 @@ 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); } /* Re-enable the interrupt in cased we missed an EOI */ - vfio_intx_eoi(&vdev->vbasedev); + vfio_pci_intx_eoi(&vdev->vbasedev); } static void vfio_intx_routing_notifier(PCIDevice *pdev) { - VFIOPCIDevice *vdev = VFIO_PCI(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); PCIINTxRoute route; if (vdev->interrupt != VFIO_INT_INTx) { @@ -257,16 +280,15 @@ 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; int32_t fd; - int ret; if (!pin) { - return 0; + return true; } vfio_disable_interrupts(vdev); @@ -285,30 +307,28 @@ static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) } #endif - ret = event_notifier_init(&vdev->intx.interrupt, 0); - if (ret) { - error_setg_errno(errp, -ret, "event_notifier_init failed"); - return ret; + if (!vfio_notifier_init(vdev, &vdev->intx.interrupt, "intx-interrupt", 0, + errp)) { + 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_device_irq_set_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; + vfio_notifier_cleanup(vdev, &vdev->intx.interrupt, "intx-interrupt", 0); + 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) @@ -317,20 +337,25 @@ static void vfio_intx_disable(VFIOPCIDevice *vdev) timer_del(vdev->intx.mmap_timer); vfio_intx_disable_kvm(vdev); - vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + vfio_device_irq_disable(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; pci_irq_deassert(&vdev->pdev); vfio_mmap_set_enabled(vdev, true); fd = event_notifier_get_fd(&vdev->intx.interrupt); qemu_set_fd_handler(fd, NULL, NULL, vdev); - event_notifier_cleanup(&vdev->intx.interrupt); + vfio_notifier_cleanup(vdev, &vdev->intx.interrupt, "intx-interrupt", 0); vdev->interrupt = VFIO_INT_NONE; trace_vfio_intx_disable(vdev->vbasedev.name); } +bool vfio_pci_intx_enable(VFIOPCIDevice *vdev, Error **errp) +{ + return vfio_intx_enable(vdev, errp); +} + /* * MSI/X */ @@ -369,12 +394,54 @@ 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 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; + + return vdev->vbasedev.io_ops->set_irqs(&vdev->vbasedev, irq_set); +} + 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); @@ -406,15 +473,15 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) fds[i] = fd; } - ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set); + ret = vdev->vbasedev.io_ops->set_irqs(&vdev->vbasedev, irq_set); g_free(irq_set); return ret; } -static void vfio_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, - int vector_n, bool msix) +void vfio_pci_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, + int vector_n, bool msix) { if ((msix && vdev->no_kvm_msix) || (!msix && vdev->no_kvm_msi)) { return; @@ -424,13 +491,16 @@ static void vfio_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, vector_n, &vdev->pdev); } -static void vfio_connect_kvm_msi_virq(VFIOMSIVector *vector) +static void vfio_connect_kvm_msi_virq(VFIOMSIVector *vector, int nr) { + const char *name = "kvm_interrupt"; + if (vector->virq < 0) { return; } - if (event_notifier_init(&vector->kvm_interrupt, 0)) { + if (!vfio_notifier_init(vector->vdev, &vector->kvm_interrupt, name, nr, + NULL)) { goto fail_notifier; } @@ -442,19 +512,20 @@ static void vfio_connect_kvm_msi_virq(VFIOMSIVector *vector) return; fail_kvm: - event_notifier_cleanup(&vector->kvm_interrupt); + vfio_notifier_cleanup(vector->vdev, &vector->kvm_interrupt, name, nr); fail_notifier: kvm_irqchip_release_virq(kvm_state, vector->virq); vector->virq = -1; } -static void vfio_remove_kvm_msi_virq(VFIOMSIVector *vector) +static void vfio_remove_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, + int nr) { kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, &vector->kvm_interrupt, vector->virq); kvm_irqchip_release_virq(kvm_state, vector->virq); vector->virq = -1; - event_notifier_cleanup(&vector->kvm_interrupt); + vfio_notifier_cleanup(vdev, &vector->kvm_interrupt, "kvm_interrupt", nr); } static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg, @@ -464,25 +535,57 @@ static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg, kvm_irqchip_commit_routes(kvm_state); } +static void set_irq_signalling(VFIODevice *vbasedev, VFIOMSIVector *vector, + unsigned int nr) +{ + 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); + } + + if (!vfio_device_irq_set_signaling(vbasedev, VFIO_PCI_MSIX_IRQ_INDEX, nr, + VFIO_IRQ_SET_ACTION_TRIGGER, + fd, &err)) { + error_reportf_err(err, VFIO_MSG_PREFIX, vbasedev->name); + } +} + +void vfio_pci_vector_init(VFIOPCIDevice *vdev, int nr) +{ + VFIOMSIVector *vector = &vdev->msi_vectors[nr]; + PCIDevice *pdev = &vdev->pdev; + Error *local_err = NULL; + + vector->vdev = vdev; + vector->virq = -1; + if (!vfio_notifier_init(vdev, &vector->interrupt, "interrupt", nr, + &local_err)) { + error_report_err(local_err); + } + vector->use = true; + if (vdev->interrupt == VFIO_INT_MSIX) { + msix_vector_use(pdev, nr); + } +} + static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, MSIMessage *msg, IOHandler *handler) { - VFIOPCIDevice *vdev = VFIO_PCI(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); VFIOMSIVector *vector; int ret; + bool resizing = !!(vdev->nr_vectors < nr + 1); trace_vfio_msix_vector_do_use(vdev->vbasedev.name, nr); vector = &vdev->msi_vectors[nr]; if (!vector->use) { - vector->vdev = vdev; - vector->virq = -1; - if (event_notifier_init(&vector->interrupt, 0)) { - error_report("vfio: Error: event_notifier_init failed"); - } - vector->use = true; - msix_vector_use(pdev, nr); + vfio_pci_vector_init(vdev, nr); } qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), @@ -494,51 +597,48 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, */ if (vector->virq >= 0) { if (!msg) { - vfio_remove_kvm_msi_virq(vector); + vfio_remove_kvm_msi_virq(vdev, vector, nr); } else { vfio_update_kvm_msi_virq(vector, *msg, pdev); } } else { if (msg) { if (vdev->defer_kvm_irq_routing) { - vfio_add_kvm_msi_virq(vdev, vector, nr, true); + vfio_pci_add_kvm_msi_virq(vdev, vector, nr, true); } else { vfio_route_change = kvm_irqchip_begin_route_changes(kvm_state); - vfio_add_kvm_msi_virq(vdev, vector, nr, true); + vfio_pci_add_kvm_msi_virq(vdev, vector, nr, true); kvm_irqchip_commit_route_changes(&vfio_route_change); - vfio_connect_kvm_msi_virq(vector); + vfio_connect_kvm_msi_virq(vector, 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) { - vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); + } + + if (!vdev->defer_kvm_irq_routing) { + if (vdev->msix->noresize && resizing) { + vfio_device_irq_disable(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); ret = vfio_enable_vectors(vdev, true); if (ret) { - error_report("vfio: failed to enable vectors, %d", ret); + error_report("vfio: failed to enable vectors, %s", + strerror(-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); - } - - 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); + set_irq_signalling(&vdev->vbasedev, vector, nr); } } @@ -561,7 +661,7 @@ static int vfio_msix_vector_use(PCIDevice *pdev, static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) { - VFIOPCIDevice *vdev = VFIO_PCI(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); VFIOMSIVector *vector = &vdev->msi_vectors[nr]; trace_vfio_msix_vector_release(vdev->vbasedev.name, nr); @@ -578,21 +678,22 @@ 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_device_irq_set_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); } } } -static void vfio_prepare_kvm_msi_virq_batch(VFIOPCIDevice *vdev) +void vfio_pci_prepare_kvm_msi_virq_batch(VFIOPCIDevice *vdev) { assert(!vdev->defer_kvm_irq_routing); vdev->defer_kvm_irq_routing = true; vfio_route_change = kvm_irqchip_begin_route_changes(kvm_state); } -static void vfio_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev) +void vfio_pci_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev) { int i; @@ -602,12 +703,14 @@ static void vfio_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev) kvm_irqchip_commit_route_changes(&vfio_route_change); for (i = 0; i < vdev->nr_vectors; i++) { - vfio_connect_kvm_msi_virq(&vdev->msi_vectors[i]); + vfio_connect_kvm_msi_virq(&vdev->msi_vectors[i], i); } } static void vfio_msix_enable(VFIOPCIDevice *vdev) { + int ret; + vfio_disable_interrupts(vdev); vdev->msi_vectors = g_new0(VFIOMSIVector, vdev->msix->entries); @@ -620,21 +723,20 @@ static void vfio_msix_enable(VFIOPCIDevice *vdev) * routes once rather than per vector provides a substantial * performance improvement. */ - vfio_prepare_kvm_msi_virq_batch(vdev); + vfio_pci_prepare_kvm_msi_virq_batch(vdev); if (msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use, vfio_msix_vector_release, NULL)) { error_report("vfio: msix_set_vector_notifiers failed"); } - vfio_commit_kvm_msi_virq_batch(vdev); + vfio_pci_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); + error_report("vfio: failed to enable vectors, %s", + strerror(-ret)); } } else { /* @@ -645,13 +747,15 @@ 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, %s", + strerror(-ret)); + } } trace_vfio_msix_enable(vdev->vbasedev.name); @@ -670,19 +774,21 @@ static void vfio_msi_enable(VFIOPCIDevice *vdev) * Deferring to commit the KVM routes once rather than per vector * provides a substantial performance improvement. */ - vfio_prepare_kvm_msi_virq_batch(vdev); + vfio_pci_prepare_kvm_msi_virq_batch(vdev); vdev->msi_vectors = g_new0(VFIOMSIVector, vdev->nr_vectors); for (i = 0; i < vdev->nr_vectors; i++) { VFIOMSIVector *vector = &vdev->msi_vectors[i]; + Error *local_err = NULL; vector->vdev = vdev; vector->virq = -1; vector->use = true; - if (event_notifier_init(&vector->interrupt, 0)) { - error_report("vfio: Error: event_notifier_init failed"); + if (!vfio_notifier_init(vdev, &vector->interrupt, "interrupt", i, + &local_err)) { + error_report_err(local_err); } qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), @@ -692,10 +798,10 @@ static void vfio_msi_enable(VFIOPCIDevice *vdev) * Attempt to enable route through KVM irqchip, * default to userspace handling if unavailable. */ - vfio_add_kvm_msi_virq(vdev, vector, i, false); + vfio_pci_add_kvm_msi_virq(vdev, vector, i, false); } - vfio_commit_kvm_msi_virq_batch(vdev); + vfio_pci_commit_kvm_msi_virq_batch(vdev); /* Set interrupt type prior to possible interrupts */ vdev->interrupt = VFIO_INT_MSI; @@ -703,7 +809,8 @@ static void vfio_msi_enable(VFIOPCIDevice *vdev) ret = vfio_enable_vectors(vdev, false); if (ret) { if (ret < 0) { - error_report("vfio: Error: Failed to setup MSI fds: %m"); + error_report("vfio: Error: Failed to setup MSI fds: %s", + strerror(-ret)); } else { error_report("vfio: Error: Failed to enable %d " "MSI vectors, retry with %d", vdev->nr_vectors, ret); @@ -737,11 +844,11 @@ static void vfio_msi_disable_common(VFIOPCIDevice *vdev) VFIOMSIVector *vector = &vdev->msi_vectors[i]; if (vdev->msi_vectors[i].use) { if (vector->virq >= 0) { - vfio_remove_kvm_msi_virq(vector); + vfio_remove_kvm_msi_virq(vdev, vector, i); } qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), NULL, NULL, NULL); - event_notifier_cleanup(&vector->interrupt); + vfio_notifier_cleanup(vdev, &vector->interrupt, "interrupt", i); } } @@ -769,13 +876,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_device_irq_disable(&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); } @@ -789,7 +897,7 @@ static void vfio_msi_disable(VFIOPCIDevice *vdev) { Error *err = NULL; - vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSI_IRQ_INDEX); + vfio_device_irq_disable(&vdev->vbasedev, VFIO_PCI_MSI_IRQ_INDEX); vfio_msi_disable_common(vdev); vfio_intx_enable(vdev, &err); if (err) { @@ -818,30 +926,31 @@ static void vfio_update_msi(VFIOPCIDevice *vdev) static void vfio_pci_load_rom(VFIOPCIDevice *vdev) { - struct vfio_region_info *reg_info; + VFIODevice *vbasedev = &vdev->vbasedev; + struct vfio_region_info *reg_info = NULL; uint64_t size; off_t off = 0; ssize_t bytes; + int ret; + + ret = vfio_device_get_region_info(vbasedev, VFIO_PCI_ROM_REGION_INDEX, + ®_info); - if (vfio_get_region_info(&vdev->vbasedev, - VFIO_PCI_ROM_REGION_INDEX, ®_info)) { - error_report("vfio: Error getting ROM info: %m"); + if (ret != 0) { + error_report("vfio: Error getting ROM info: %s", strerror(-ret)); return; } - trace_vfio_pci_load_rom(vdev->vbasedev.name, (unsigned long)reg_info->size, + trace_vfio_pci_load_rom(vbasedev->name, (unsigned long)reg_info->size, (unsigned long)reg_info->offset, (unsigned long)reg_info->flags); 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 " - "%s", vdev->vbasedev.name); + error_report("vfio-pci: Cannot read device rom at %s", vbasedev->name); error_printf("Device option ROM contents are probably invalid " "(check dmesg).\nSkip option ROM probe with rombar=0, " "or load from file with romfile=\n"); @@ -852,18 +961,22 @@ static void vfio_pci_load_rom(VFIOPCIDevice *vdev) memset(vdev->rom, 0xff, size); while (size) { - bytes = pread(vdev->vbasedev.fd, vdev->rom + off, - size, vdev->rom_offset + off); + bytes = vbasedev->io_ops->region_read(vbasedev, + VFIO_PCI_ROM_REGION_INDEX, + off, size, vdev->rom + off); + if (bytes == 0) { break; } else if (bytes > 0) { off += bytes; size -= bytes; } else { - if (errno == EINTR || errno == EAGAIN) { + if (bytes == -EINTR || bytes == -EAGAIN) { continue; } - error_report("vfio: Error reading device ROM: %m"); + error_report("vfio: Error reading device ROM: %s", + strreaderror(bytes)); + break; } } @@ -899,6 +1012,24 @@ static void vfio_pci_load_rom(VFIOPCIDevice *vdev) } } +/* "Raw" read of underlying config space. */ +static int vfio_pci_config_space_read(VFIOPCIDevice *vdev, off_t offset, + uint32_t size, void *data) +{ + return vdev->vbasedev.io_ops->region_read(&vdev->vbasedev, + VFIO_PCI_CONFIG_REGION_INDEX, + offset, size, data); +} + +/* "Raw" write of underlying config space. */ +static int vfio_pci_config_space_write(VFIOPCIDevice *vdev, off_t offset, + uint32_t size, void *data) +{ + return vdev->vbasedev.io_ops->region_write(&vdev->vbasedev, + VFIO_PCI_CONFIG_REGION_INDEX, + offset, size, data, false); +} + static uint64_t vfio_rom_read(void *opaque, hwaddr addr, unsigned size) { VFIOPCIDevice *vdev = opaque; @@ -951,11 +1082,9 @@ static const MemoryRegionOps vfio_rom_ops = { static void vfio_pci_size_rom(VFIOPCIDevice *vdev) { + VFIODevice *vbasedev = &vdev->vbasedev; uint32_t orig, size = cpu_to_le32((uint32_t)PCI_ROM_ADDRESS_MASK); - off_t offset = vdev->config_offset + PCI_ROM_ADDRESS; - DeviceState *dev = DEVICE(vdev); char *name; - int fd = vdev->vbasedev.fd; if (vdev->pdev.romfile || !vdev->pdev.rom_bar) { /* Since pci handles romfile, just print a message and return */ @@ -972,11 +1101,12 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) * Use the same size ROM BAR as the physical device. The contents * will get filled in later when the guest tries to read it. */ - if (pread(fd, &orig, 4, offset) != 4 || - pwrite(fd, &size, 4, offset) != 4 || - pread(fd, &size, 4, offset) != 4 || - pwrite(fd, &orig, 4, offset) != 4) { - error_report("%s(%s) failed: %m", __func__, vdev->vbasedev.name); + if (vfio_pci_config_space_read(vdev, PCI_ROM_ADDRESS, 4, &orig) != 4 || + vfio_pci_config_space_write(vdev, PCI_ROM_ADDRESS, 4, &size) != 4 || + vfio_pci_config_space_read(vdev, PCI_ROM_ADDRESS, 4, &size) != 4 || + vfio_pci_config_space_write(vdev, PCI_ROM_ADDRESS, 4, &orig) != 4) { + + error_report("%s(%s) ROM access failed", __func__, vbasedev->name); return; } @@ -987,12 +1117,12 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) } if (vfio_opt_rom_in_denylist(vdev)) { - if (dev->opts && qdict_haskey(dev->opts, "rombar")) { + if (vdev->pdev.rom_bar > 0) { warn_report("Device at %s is known to cause system instability" " issues during option rom execution", vdev->vbasedev.name); error_printf("Proceeding anyway since user specified" - " non zero value for rombar\n"); + " positive value for rombar\n"); } else { warn_report("Rom loading for device at %s has been disabled" " due to system instability issues", @@ -1109,7 +1239,7 @@ static const MemoryRegionOps vfio_vga_ops = { */ static void vfio_sub_page_bar_update_mapping(PCIDevice *pdev, int bar) { - VFIOPCIDevice *vdev = VFIO_PCI(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); VFIORegion *region = &vdev->bars[bar].region; MemoryRegion *mmap_mr, *region_mr, *base_mr; PCIIORegion *r; @@ -1155,7 +1285,8 @@ static void vfio_sub_page_bar_update_mapping(PCIDevice *pdev, int bar) */ uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) { - VFIOPCIDevice *vdev = VFIO_PCI(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); + VFIODevice *vbasedev = &vdev->vbasedev; uint32_t emu_bits = 0, emu_val = 0, phys_val = 0, val; memcpy(&emu_bits, vdev->emulated_config_bits + addr, len); @@ -1168,12 +1299,12 @@ uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) if (~emu_bits & (0xffffffffU >> (32 - len * 8))) { ssize_t ret; - ret = pread(vdev->vbasedev.fd, &phys_val, len, - vdev->config_offset + addr); + ret = vfio_pci_config_space_read(vdev, addr, len, &phys_val); if (ret != len) { - error_report("%s(%s, 0x%x, 0x%x) failed: %m", - __func__, vdev->vbasedev.name, addr, len); - return -errno; + error_report("%s(%s, 0x%x, 0x%x) failed: %s", + __func__, vbasedev->name, addr, len, + strreaderror(ret)); + return -1; } phys_val = le32_to_cpu(phys_val); } @@ -1188,16 +1319,19 @@ uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, uint32_t val, int len) { - VFIOPCIDevice *vdev = VFIO_PCI(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); + VFIODevice *vbasedev = &vdev->vbasedev; uint32_t val_le = cpu_to_le32(val); + int ret; trace_vfio_pci_write_config(vdev->vbasedev.name, addr, val, len); /* Write everything to VFIO, let it filter out what we can't write */ - if (pwrite(vdev->vbasedev.fd, &val_le, len, vdev->config_offset + addr) - != len) { - error_report("%s(%s, 0x%x, 0x%x, 0x%x) failed: %m", - __func__, vdev->vbasedev.name, addr, val, len); + ret = vfio_pci_config_space_write(vdev, addr, len, &val_le); + if (ret != len) { + error_report("%s(%s, 0x%x, 0x%x, 0x%x) failed: %s", + __func__, vbasedev->name, addr, val, len, + strwriteerror(ret)); } /* MSI/MSI-X Enabling/Disabling */ @@ -1278,17 +1412,19 @@ 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; int ret, entries; Error *err = NULL; - 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; + ret = vfio_pci_config_space_read(vdev, pos + PCI_CAP_FLAGS, + sizeof(ctrl), &ctrl); + if (ret != sizeof(ctrl)) { + error_setg(errp, "failed reading MSI PCI_CAP_FLAGS: %s", + strreaderror(ret)); + return false; } ctrl = le16_to_cpu(ctrl); @@ -1301,14 +1437,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) @@ -1320,8 +1456,8 @@ static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev) * If the host driver allows mapping of a MSIX data, we are going to * do map the entire BAR and emulate MSIX table on top of that. */ - if (vfio_has_region_cap(&vdev->vbasedev, region->nr, - VFIO_REGION_INFO_CAP_MSIX_MAPPABLE)) { + if (vfio_device_has_region_cap(&vdev->vbasedev, region->nr, + VFIO_REGION_INFO_CAP_MSIX_MAPPABLE)) { return; } @@ -1388,13 +1524,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 +1541,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 +1553,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 +1571,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 +1579,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 +1614,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 +1625,41 @@ 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; + struct vfio_irq_info irq_info; VFIOMSIXInfo *msix; + int ret; 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; + ret = vfio_pci_config_space_read(vdev, pos + PCI_MSIX_FLAGS, + sizeof(ctrl), &ctrl); + if (ret != sizeof(ctrl)) { + error_setg(errp, "failed to read PCI MSIX FLAGS: %s", + strreaderror(ret)); + 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; + ret = vfio_pci_config_space_read(vdev, pos + PCI_MSIX_TABLE, + sizeof(table), &table); + if (ret != sizeof(table)) { + error_setg(errp, "failed to read PCI MSIX TABLE: %s", + strreaderror(ret)); + 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; + ret = vfio_pci_config_space_read(vdev, pos + PCI_MSIX_PBA, + sizeof(pba), &pba); + if (ret != sizeof(pba)) { + error_setg(errp, "failed to read PCI MSIX PBA: %s", strreaderror(ret)); + return false; } ctrl = le16_to_cpu(ctrl); @@ -1530,6 +1673,16 @@ 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 = vfio_device_get_irq_info(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX, + &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 +1706,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 +1740,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,10 +1778,10 @@ 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) +void vfio_pci_teardown_msi(VFIOPCIDevice *vdev) { msi_uninit(&vdev->pdev); @@ -1664,10 +1818,10 @@ static void vfio_bar_prepare(VFIOPCIDevice *vdev, int nr) } /* Determine what type of BAR this is for registration */ - ret = pread(vdev->vbasedev.fd, &pci_bar, sizeof(pci_bar), - vdev->config_offset + PCI_BASE_ADDRESS_0 + (4 * nr)); + ret = vfio_pci_config_space_read(vdev, PCI_BASE_ADDRESS_0 + (4 * nr), + sizeof(pci_bar), &pci_bar); if (ret != sizeof(pci_bar)) { - error_report("vfio: Failed to read BAR %d (%m)", nr); + error_report("vfio: Failed to read BAR %d: %s", nr, strreaderror(ret)); return; } @@ -1677,6 +1831,9 @@ static void vfio_bar_prepare(VFIOPCIDevice *vdev, int nr) bar->type = pci_bar & (bar->ioport ? ~PCI_BASE_ADDRESS_IO_MASK : ~PCI_BASE_ADDRESS_MEM_MASK); bar->size = bar->region.size; + + /* IO regions are sync, memory can be async */ + bar->region.post_wr = (bar->ioport == 0); } static void vfio_bars_prepare(VFIOPCIDevice *vdev) @@ -1723,7 +1880,7 @@ static void vfio_bars_register(VFIOPCIDevice *vdev) } } -static void vfio_bars_exit(VFIOPCIDevice *vdev) +void vfio_pci_bars_exit(VFIOPCIDevice *vdev) { int i; @@ -1903,8 +2060,8 @@ static void vfio_pci_disable_rp_atomics(VFIOPCIDevice *vdev) } } -static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, - Error **errp) +static bool vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, + Error **errp) { uint16_t flags; uint8_t type; @@ -1918,7 +2075,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))) { @@ -1951,7 +2108,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))) { @@ -1989,7 +2146,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 { @@ -2025,12 +2182,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) @@ -2063,11 +2220,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]; @@ -2088,9 +2268,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 */ @@ -2098,9 +2277,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; } } @@ -2123,26 +2301,32 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) break; 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_pm_init(pdev, pos, errp) >= 0; + /* + * PCI-core config space emulation needs write access to the power + * state enabled for tracking BAR mapping relative to PM state. + */ + pci_set_word(pdev->wmask + pos + PCI_PM_CTRL, PCI_PM_CTRL_STATE_MASK); 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 0; + return ret; } static int vfio_setup_rebar_ecap(VFIOPCIDevice *vdev, uint16_t pos) @@ -2285,47 +2469,53 @@ static void vfio_add_ext_cap(VFIOPCIDevice *vdev) } g_free(config); - return; } -static int vfio_add_capabilities(VFIOPCIDevice *vdev, Error **errp) +bool vfio_pci_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; vfio_disable_interrupts(vdev); + /* + * Stop any ongoing DMA by disconnecting I/O, MMIO, and bus master. + * Also put INTx Disable in known state. + */ + cmd = vfio_pci_read_config(pdev, PCI_COMMAND, 2); + cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_INTX_DISABLE); + vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2); + /* Make sure the device is in D0 */ - if (vdev->pm_cap) { + if (pdev->pm_cap) { uint16_t pmcsr; uint8_t state; - pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2); + pmcsr = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); state = pmcsr & PCI_PM_CTRL_STATE_MASK; if (state) { pmcsr &= ~PCI_PM_CTRL_STATE_MASK; - vfio_pci_write_config(pdev, vdev->pm_cap + PCI_PM_CTRL, pmcsr, 2); + vfio_pci_write_config(pdev, pdev->pm_cap + PCI_PM_CTRL, pmcsr, 2); /* vfio handles the necessary delay here */ - pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2); + pmcsr = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); state = pmcsr & PCI_PM_CTRL_STATE_MASK; if (state) { error_report("vfio: Unable to power on device, stuck in D%d", @@ -2333,42 +2523,34 @@ static void vfio_pci_pre_reset(VFIOPCIDevice *vdev) } } } - - /* - * Stop any ongoing DMA by disconnecting I/O, MMIO, and bus master. - * Also put INTx Disable in known state. - */ - cmd = vfio_pci_read_config(pdev, PCI_COMMAND, 2); - cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | - PCI_COMMAND_INTX_DISABLE); - vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2); } -static void vfio_pci_post_reset(VFIOPCIDevice *vdev) +void vfio_pci_post_reset(VFIOPCIDevice *vdev) { + VFIODevice *vbasedev = &vdev->vbasedev; Error *err = NULL; - int nr; + int ret, nr; - vfio_intx_enable(vdev, &err); - if (err) { + if (!vfio_intx_enable(vdev, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } for (nr = 0; nr < PCI_NUM_REGIONS - 1; ++nr) { - off_t addr = vdev->config_offset + PCI_BASE_ADDRESS_0 + (4 * nr); + off_t addr = PCI_BASE_ADDRESS_0 + (4 * nr); uint32_t val = 0; uint32_t len = sizeof(val); - if (pwrite(vdev->vbasedev.fd, &val, len, addr) != len) { - error_report("%s(%s) reset bar %d failed: %m", __func__, - vdev->vbasedev.name, nr); + ret = vfio_pci_config_space_write(vdev, addr, len, &val); + if (ret != len) { + error_report("%s(%s) reset bar %d failed: %s", __func__, + vbasedev->name, nr, strwriteerror(ret)); } } 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]; @@ -2378,22 +2560,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); @@ -2401,163 +2574,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; - } - - 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; - } - } + return ret; } - /* 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; - } + *info_p = info; + return 0; +} - 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); +static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single) +{ + VFIODevice *vbasedev = &vdev->vbasedev; + const VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(vbasedev->bcontainer); - return ret; + return vioc->pci_hot_reset(vbasedev, single); } /* @@ -2608,22 +2654,54 @@ static bool vfio_msix_present(void *opaque, int version_id) return msix_present(pdev); } -const VMStateDescription vmstate_vfio_pci_config = { +static bool vfio_display_migration_needed(void *opaque) +{ + VFIOPCIDevice *vdev = opaque; + + /* + * We need to migrate the VFIODisplay object if ramfb *migration* was + * explicitly requested (in which case we enforced both ramfb=on and + * display=on), or ramfb migration was left at the default "auto" + * setting, and *ramfb* was explicitly requested (in which case we + * enforced display=on). + */ + return vdev->ramfb_migrate == ON_OFF_AUTO_ON || + (vdev->ramfb_migrate == ON_OFF_AUTO_AUTO && vdev->enable_ramfb); +} + +static const VMStateDescription vmstate_vfio_display = { + .name = "VFIOPCIDevice/VFIODisplay", + .version_id = 1, + .minimum_version_id = 1, + .needed = vfio_display_migration_needed, + .fields = (const VMStateField[]){ + VMSTATE_STRUCT_POINTER(dpy, VFIOPCIDevice, vfio_display_vmstate, + VFIODisplay), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_vfio_pci_config = { .name = "VFIOPCIDevice", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(pdev, VFIOPCIDevice), VMSTATE_MSIX_TEST(pdev, VFIOPCIDevice, vfio_msix_present), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_vfio_display, + NULL } }; -static void vfio_pci_save_config(VFIODevice *vbasedev, QEMUFile *f) +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) @@ -2669,24 +2747,24 @@ static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f) static VFIODeviceOps vfio_pci_ops = { .vfio_compute_needs_reset = vfio_pci_compute_needs_reset, .vfio_hot_reset_multi = vfio_pci_hot_reset_multi, - .vfio_eoi = vfio_intx_eoi, + .vfio_eoi = vfio_pci_intx_eoi, .vfio_get_object = vfio_pci_get_object, .vfio_save_config = vfio_pci_save_config, .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; + struct vfio_region_info *reg_info = NULL; int ret; - ret = vfio_get_region_info(vbasedev, VFIO_PCI_VGA_REGION_INDEX, ®_info); + ret = vfio_device_get_region_info(vbasedev, VFIO_PCI_VGA_REGION_INDEX, ®_info); if (ret) { 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) || @@ -2695,8 +2773,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); @@ -2704,8 +2781,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); @@ -2740,31 +2815,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) +bool vfio_pci_populate_device(VFIOPCIDevice *vdev, Error **errp) { VFIODevice *vbasedev = &vdev->vbasedev; - struct vfio_region_info *reg_info; - struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info) }; + struct vfio_region_info *reg_info = NULL; + struct vfio_irq_info 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++) { @@ -2776,20 +2851,20 @@ 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); } - ret = vfio_get_region_info(vbasedev, - VFIO_PCI_CONFIG_REGION_INDEX, ®_info); + ret = vfio_device_get_region_info(vbasedev, + 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, + trace_vfio_pci_populate_device_config(vdev->vbasedev.name, (unsigned long)reg_info->size, (unsigned long)reg_info->offset, (unsigned long)reg_info->flags); @@ -2800,23 +2875,18 @@ 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; } } - irq_info.index = VFIO_PCI_ERR_IRQ_INDEX; - - ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info); + ret = vfio_device_get_irq_info(vbasedev, VFIO_PCI_ERR_IRQ_INDEX, &irq_info); if (ret) { /* This can fail for an old kernel or legacy PCI dev */ - trace_vfio_populate_device_get_irq_info_failure(strerror(errno)); + trace_vfio_pci_populate_device_get_irq_info_failure(strerror(-ret)); } else if (irq_info.count == 1) { vdev->pci_aer = true; } else { @@ -2824,14 +2894,28 @@ 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) +void vfio_pci_put_device(VFIOPCIDevice *vdev) { + vfio_display_finalize(vdev); + vfio_bars_finalize(vdev); + g_free(vdev->emulated_config_bits); + g_free(vdev->rom); + /* + * XXX Leaking igd_opregion is not an oversight, we can't remove the + * fw_cfg entry therefore leaking this allocation seems like the safest + * option. + * + * g_free(vdev->igd_opregion); + */ + + vfio_device_detach(&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) @@ -2862,7 +2946,7 @@ static void vfio_err_notifier_handler(void *opaque) * and continue after disabling error recovery support for the * device. */ -static void vfio_register_err_notifier(VFIOPCIDevice *vdev) +void vfio_pci_register_err_notifier(VFIOPCIDevice *vdev) { Error *err = NULL; int32_t fd; @@ -2871,8 +2955,9 @@ static void vfio_register_err_notifier(VFIOPCIDevice *vdev) return; } - if (event_notifier_init(&vdev->err_notifier, 0)) { - error_report("vfio: Unable to init event notifier for error detection"); + if (!vfio_notifier_init(vdev, &vdev->err_notifier, "err_notifier", 0, + &err)) { + error_report_err(err); vdev->pci_aer = false; return; } @@ -2880,11 +2965,11 @@ 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_device_irq_set_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); + vfio_notifier_cleanup(vdev, &vdev->err_notifier, "err_notifier", 0); vdev->pci_aer = false; } } @@ -2897,13 +2982,13 @@ 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_device_irq_set_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), NULL, NULL, vdev); - event_notifier_cleanup(&vdev->err_notifier); + vfio_notifier_cleanup(vdev, &vdev->err_notifier, "err_notifier", 0); } static void vfio_req_notifier_handler(void *opaque) @@ -2921,35 +3006,37 @@ static void vfio_req_notifier_handler(void *opaque) } } -static void vfio_register_req_notifier(VFIOPCIDevice *vdev) +void vfio_pci_register_req_notifier(VFIOPCIDevice *vdev) { - struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info), - .index = VFIO_PCI_REQ_IRQ_INDEX }; + struct vfio_irq_info irq_info; Error *err = NULL; int32_t fd; + int ret; if (!(vdev->features & VFIO_FEATURE_ENABLE_REQ)) { return; } - if (ioctl(vdev->vbasedev.fd, - VFIO_DEVICE_GET_IRQ_INFO, &irq_info) < 0 || irq_info.count < 1) { + ret = vfio_device_get_irq_info(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, + &irq_info); + if (ret < 0 || irq_info.count < 1) { return; } - if (event_notifier_init(&vdev->req_notifier, 0)) { - error_report("vfio: Unable to init event notifier for device request"); + if (!vfio_notifier_init(vdev, &vdev->req_notifier, "req_notifier", 0, + &err)) { + error_report_err(err); return; } 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_device_irq_set_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); + vfio_notifier_cleanup(vdev, &vdev->req_notifier, "req_notifier", 0); } else { vdev->req_enabled = true; } @@ -2963,140 +3050,33 @@ 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_device_irq_set_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), NULL, NULL, vdev); - event_notifier_cleanup(&vdev->req_notifier); + vfio_notifier_cleanup(vdev, &vdev->req_notifier, "req_notifier", 0); vdev->req_enabled = false; } -static void vfio_realize(PCIDevice *pdev, Error **errp) +bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) { - VFIOPCIDevice *vdev = VFIO_PCI(pdev); + PCIDevice *pdev = &vdev->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_FMT_LEN]; - char *name; - - if (!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 " - "or -device vfio-pci,sysfsdev=PATH_TO_DEVICE\n"); - return; - } - vbasedev->sysfsdev = - g_strdup_printf("/sys/bus/pci/devices/%04x:%02x:%02x.%01x", - vdev->host.domain, vdev->host.bus, - 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); - 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); - - trace_vfio_mdev(vbasedev->name, is_mdev); - - if (vbasedev->ram_block_discard_allowed && !is_mdev) { - error_setg(errp, "x-balloon-allowed only potentially compatible " - "with mdev devices"); - vfio_put_group(group); - goto error; - } - - if (!qemu_uuid_is_null(&vdev->vf_token)) { - qemu_uuid_unparse(&vdev->vf_token, uuid); - name = g_strdup_printf("%s vf_token=%s", vbasedev->name, uuid); - } else { - name = g_strdup(vbasedev->name); - } - - ret = vfio_get_device(group, name, vbasedev, errp); - g_free(name); - if (ret) { - vfio_put_group(group); - goto error; - } + uint32_t config_space_size; + int ret; - vfio_populate_device(vdev, &err); - if (err) { - error_propagate(errp, err); - goto error; - } + config_space_size = MIN(pci_config_size(&vdev->pdev), vdev->config_size); /* Get a copy of config space */ - ret = pread(vbasedev->fd, vdev->pdev.config, - MIN(pci_config_size(&vdev->pdev), vdev->config_size), - vdev->config_offset); - if (ret < (int)MIN(pci_config_size(&vdev->pdev), vdev->config_size)) { - ret = ret < 0 ? -errno : -EFAULT; - error_setg_errno(errp, -ret, "failed to read device config space"); - goto error; + ret = vfio_pci_config_space_read(vdev, 0, config_space_size, + vdev->pdev.config); + if (ret < (int)config_space_size) { + ret = ret < 0 ? -ret : EFAULT; + error_setg_errno(errp, ret, "failed to read device config space"); + return false; } /* vfio emulates a lot for us, but some bits need extra love */ @@ -3115,7 +3095,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) if (vdev->vendor_id != PCI_ANY_ID) { if (vdev->vendor_id >= 0xffff) { error_setg(errp, "invalid PCI vendor ID provided"); - goto error; + return false; } vfio_add_emulated_word(vdev, PCI_VENDOR_ID, vdev->vendor_id, ~0); trace_vfio_pci_emulated_vendor_id(vbasedev->name, vdev->vendor_id); @@ -3126,7 +3106,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) if (vdev->device_id != PCI_ANY_ID) { if (vdev->device_id > 0xffff) { error_setg(errp, "invalid PCI device ID provided"); - goto error; + return false; } vfio_add_emulated_word(vdev, PCI_DEVICE_ID, vdev->device_id, ~0); trace_vfio_pci_emulated_device_id(vbasedev->name, vdev->device_id); @@ -3137,7 +3117,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) if (vdev->sub_vendor_id != PCI_ANY_ID) { if (vdev->sub_vendor_id > 0xffff) { error_setg(errp, "invalid PCI subsystem vendor ID provided"); - goto error; + return false; } vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_VENDOR_ID, vdev->sub_vendor_id, ~0); @@ -3148,7 +3128,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) if (vdev->sub_device_id != PCI_ANY_ID) { if (vdev->sub_device_id > 0xffff) { error_setg(errp, "invalid PCI subsystem device ID provided"); - goto error; + return false; } vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_ID, vdev->sub_device_id, ~0); trace_vfio_pci_emulated_sub_device_id(vbasedev->name, @@ -3178,53 +3158,18 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vfio_bars_prepare(vdev); - vfio_msix_early_setup(vdev, &err); - if (err) { - error_propagate(errp, err); - goto error; + if (!vfio_msix_early_setup(vdev, errp)) { + return false; } vfio_bars_register(vdev); - ret = vfio_add_capabilities(vdev, errp); - if (ret) { - goto out_teardown; - } - - if (vdev->vga) { - vfio_vga_quirk_setup(vdev); - } - - for (i = 0; i < PCI_ROM_SLOT; i++) { - vfio_bar_quirk_setup(vdev, i); - } - - if (!vdev->igd_opregion && - vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) { - struct vfio_region_info *opregion; - - if (vdev->pdev.qdev.hotplugged) { - error_setg(errp, - "cannot support IGD OpRegion feature on hotplugged " - "device"); - goto out_teardown; - } - - ret = vfio_get_dev_region_info(vbasedev, - VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, - VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion); - if (ret) { - error_setg_errno(errp, -ret, - "does not support requested IGD OpRegion feature"); - goto out_teardown; - } + return true; +} - ret = vfio_pci_igd_opregion_init(vdev, opregion, errp); - g_free(opregion); - if (ret) { - goto out_teardown; - } - } +bool vfio_pci_interrupt_setup(VFIOPCIDevice *vdev, Error **errp) +{ + PCIDevice *pdev = &vdev->pdev; /* QEMU emulates all of MSI & MSIX */ if (pdev->cap_present & QEMU_PCI_CAP_MSIX) { @@ -3239,20 +3184,115 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) { vdev->intx.mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, - vfio_intx_mmap_enable, vdev); + vfio_intx_mmap_enable, vdev); pci_device_set_intx_routing_notifier(&vdev->pdev, 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) { - goto out_deregister; + if (!vfio_intx_enable(vdev, errp)) { + timer_free(vdev->intx.mmap_timer); + pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); + kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); + return false; + } + } + return true; +} + +static void vfio_pci_realize(PCIDevice *pdev, Error **errp) +{ + ERRP_GUARD(); + VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); + VFIODevice *vbasedev = &vdev->vbasedev; + int i; + char uuid[UUID_STR_LEN]; + g_autofree char *name = NULL; + + 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; } + vbasedev->sysfsdev = + g_strdup_printf("/sys/bus/pci/devices/%04x:%02x:%02x.%01x", + vdev->host.domain, vdev->host.bus, + vdev->host.slot, vdev->host.function); + } + + if (!vfio_device_get_name(vbasedev, errp)) { + return; + } + + /* + * 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. + */ + vbasedev->mdev = vfio_device_is_mdev(vbasedev); + + trace_vfio_mdev(vbasedev->name, vbasedev->mdev); + + if (vbasedev->ram_block_discard_allowed && !vbasedev->mdev) { + error_setg(errp, "x-balloon-allowed only potentially compatible " + "with mdev devices"); + goto error; + } + + 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_device_attach(name, vbasedev, + pci_device_iommu_address_space(pdev), errp)) { + goto error; + } + + if (!vfio_pci_populate_device(vdev, errp)) { + goto error; + } + + if (!vfio_pci_config_setup(vdev, errp)) { + goto error; + } + + if (!vbasedev->mdev && + !pci_device_set_iommu_device(pdev, vbasedev->hiod, errp)) { + error_prepend(errp, "Failed to set vIOMMU: "); + goto out_teardown; + } + + if (!vfio_pci_add_capabilities(vdev, errp)) { + goto out_unset_idev; + } + + if (!vfio_config_quirk_setup(vdev, errp)) { + goto out_unset_idev; + } + + if (vdev->vga) { + vfio_vga_quirk_setup(vdev); + } + + for (i = 0; i < PCI_ROM_SLOT; i++) { + vfio_bar_quirk_setup(vdev, i); + } + + if (!vfio_pci_interrupt_setup(vdev, errp)) { + goto out_unset_idev; } if (vdev->display != ON_OFF_AUTO_OFF) { - ret = vfio_display_probe(vdev, errp); - if (ret) { + if (!vfio_display_probe(vdev, errp)) { goto out_deregister; } } @@ -3271,17 +3311,17 @@ 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; } } @@ -3291,8 +3331,8 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) } } - vfio_register_err_notifier(vdev); - vfio_register_req_notifier(vdev); + vfio_pci_register_err_notifier(vdev); + vfio_pci_register_req_notifier(vdev); vfio_setup_resetfn_quirk(vdev); return; @@ -3308,36 +3348,28 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) 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); + vfio_pci_teardown_msi(vdev); + vfio_pci_bars_exit(vdev); error: error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->name); } static void vfio_instance_finalize(Object *obj) { - VFIOPCIDevice *vdev = VFIO_PCI(obj); - VFIOGroup *group = vdev->vbasedev.group; + VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); - vfio_display_finalize(vdev); - vfio_bars_finalize(vdev); - g_free(vdev->emulated_config_bits); - g_free(vdev->rom); - /* - * XXX Leaking igd_opregion is not an oversight, we can't remove the - * fw_cfg entry therefore leaking this allocation seems like the safest - * option. - * - * 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); + VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); + VFIODevice *vbasedev = &vdev->vbasedev; vfio_unregister_req_notifier(vdev); vfio_unregister_err_notifier(vdev); @@ -3349,15 +3381,23 @@ static void vfio_exitfn(PCIDevice *pdev) if (vdev->intx.mmap_timer) { timer_free(vdev->intx.mmap_timer); } - vfio_teardown_msi(vdev); + vfio_pci_teardown_msi(vdev); vfio_pci_disable_rp_atomics(vdev); - vfio_bars_exit(vdev); - vfio_migration_exit(&vdev->vbasedev); + vfio_pci_bars_exit(vdev); + vfio_migration_exit(vbasedev); + if (!vbasedev->mdev) { + pci_device_unset_iommu_device(pdev); + } } static void vfio_pci_reset(DeviceState *dev) { - VFIOPCIDevice *vdev = VFIO_PCI(dev); + VFIOPCIDevice *vdev = VFIO_PCI_BASE(dev); + + /* Do not reset the device during qemu_system_reset prior to cpr load */ + if (cpr_is_incoming()) { + return; + } trace_vfio_pci_reset(vdev->vbasedev.name); @@ -3397,7 +3437,8 @@ static void vfio_pci_reset(DeviceState *dev) static void vfio_instance_init(Object *obj) { PCIDevice *pci_dev = PCI_DEVICE(obj); - VFIOPCIDevice *vdev = VFIO_PCI(obj); + VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); + VFIODevice *vbasedev = &vdev->vbasedev; device_add_bootindex_property(obj, &vdev->bootindex, "bootindex", NULL, @@ -3407,20 +3448,60 @@ 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 * line, therefore, no need to wait to realize like other devices */ pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; + + /* + * A device that is resuming for cpr is already configured, so do not + * reset it during qemu_system_reset prior to cpr load, else interrupts + * may be lost. + */ + pci_dev->cap_present |= QEMU_PCI_SKIP_RESET_ON_CPR; +} + +static void vfio_pci_base_dev_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); + + dc->desc = "VFIO PCI base device"; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + pdc->exit = vfio_exitfn; + pdc->config_read = vfio_pci_read_config; + pdc->config_write = vfio_pci_write_config; } -static Property vfio_pci_dev_properties[] = { +static const TypeInfo vfio_pci_base_dev_info = { + .name = TYPE_VFIO_PCI_BASE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(VFIOPCIDevice), + .abstract = true, + .class_init = vfio_pci_base_dev_class_init, + .interfaces = (const InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { } + }, +}; + +static PropertyInfo vfio_pci_migration_multifd_transfer_prop; + +static const 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), @@ -3432,9 +3513,19 @@ static Property vfio_pci_dev_properties[] = { DEFINE_PROP_BIT("x-req", VFIOPCIDevice, features, VFIO_FEATURE_ENABLE_REQ_BIT, true), DEFINE_PROP_BIT("x-igd-opregion", VFIOPCIDevice, features, - VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false), + VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, true), + DEFINE_PROP_BIT("x-igd-lpc", VFIOPCIDevice, features, + VFIO_FEATURE_ENABLE_IGD_LPC_BIT, false), + DEFINE_PROP_ON_OFF_AUTO("x-igd-legacy-mode", VFIOPCIDevice, + igd_legacy_mode, ON_OFF_AUTO_AUTO), DEFINE_PROP_ON_OFF_AUTO("enable-migration", VFIOPCIDevice, vbasedev.enable_migration, ON_OFF_AUTO_AUTO), + DEFINE_PROP("x-migration-multifd-transfer", VFIOPCIDevice, + vbasedev.migration_multifd_transfer, + vfio_pci_migration_multifd_transfer_prop, OnOffAuto, + .set_default = true, .defval.i = 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), @@ -3458,55 +3549,187 @@ 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), - */ - DEFINE_PROP_END_OF_LIST(), + 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), }; -static void vfio_pci_dev_class_init(ObjectClass *klass, void *data) +#ifdef CONFIG_IOMMUFD +static void vfio_pci_set_fd(Object *obj, const char *str, Error **errp) +{ + VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); + vfio_device_set_fd(&vdev->vbasedev, str, errp); +} +#endif + +static void vfio_pci_dev_class_init(ObjectClass *klass, const 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->vmsd = &vfio_cpr_pci_vmstate; dc->desc = "VFIO-based PCI device assignment"; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - pdc->realize = vfio_realize; - pdc->exit = vfio_exitfn; - pdc->config_read = vfio_pci_read_config; - pdc->config_write = vfio_pci_write_config; + pdc->realize = vfio_pci_realize; + + object_class_property_set_description(klass, /* 1.3 */ + "host", + "Host PCI address [domain:] of assigned device"); + object_class_property_set_description(klass, /* 1.3 */ + "x-intx-mmap-timeout-ms", + "When EOI is not provided by KVM/QEMU, wait time " + "(milliseconds) to re-enable device direct access " + "after INTx (DEBUG)"); + object_class_property_set_description(klass, /* 1.5 */ + "x-vga", + "Expose VGA address spaces for device"); + object_class_property_set_description(klass, /* 2.3 */ + "x-req", + "Disable device request notification support (DEBUG)"); + object_class_property_set_description(klass, /* 2.4 and 2.5 */ + "x-no-mmap", + "Disable MMAP for device. Allows to trace MMIO " + "accesses (DEBUG)"); + object_class_property_set_description(klass, /* 2.5 */ + "x-no-kvm-intx", + "Disable direct VFIO->KVM INTx injection. Allows to " + "trace INTx interrupts (DEBUG)"); + object_class_property_set_description(klass, /* 2.5 */ + "x-no-kvm-msi", + "Disable direct VFIO->KVM MSI injection. Allows to " + "trace MSI interrupts (DEBUG)"); + object_class_property_set_description(klass, /* 2.5 */ + "x-no-kvm-msix", + "Disable direct VFIO->KVM MSIx injection. Allows to " + "trace MSIx interrupts (DEBUG)"); + object_class_property_set_description(klass, /* 2.5 */ + "x-pci-vendor-id", + "Override PCI Vendor ID with provided value (DEBUG)"); + object_class_property_set_description(klass, /* 2.5 */ + "x-pci-device-id", + "Override PCI device ID with provided value (DEBUG)"); + object_class_property_set_description(klass, /* 2.5 */ + "x-pci-sub-vendor-id", + "Override PCI Subsystem Vendor ID with provided value " + "(DEBUG)"); + object_class_property_set_description(klass, /* 2.5 */ + "x-pci-sub-device-id", + "Override PCI Subsystem Device ID with provided value " + "(DEBUG)"); + object_class_property_set_description(klass, /* 2.6 */ + "sysfsdev", + "Host sysfs path of assigned device"); + object_class_property_set_description(klass, /* 2.7 */ + "x-igd-opregion", + "Expose host IGD OpRegion to guest"); + object_class_property_set_description(klass, /* 2.7 (See c4c45e943e51) */ + "x-igd-gms", + "Override IGD data stolen memory size (32MiB units)"); + object_class_property_set_description(klass, /* 2.11 */ + "x-nv-gpudirect-clique", + "Add NVIDIA GPUDirect capability indicating P2P DMA " + "clique for device [0-15]"); + object_class_property_set_description(klass, /* 2.12 */ + "x-no-geforce-quirks", + "Disable GeForce quirks (for NVIDIA Quadro/GRID/Tesla). " + "Improves performance"); + object_class_property_set_description(klass, /* 2.12 */ + "display", + "Enable display support for device, ex. vGPU"); + object_class_property_set_description(klass, /* 2.12 */ + "x-msix-relocation", + "Specify MSI-X MMIO relocation to the end of specified " + "existing BAR or new BAR to avoid virtualization overhead " + "due to adjacent device registers"); + object_class_property_set_description(klass, /* 3.0 */ + "x-no-kvm-ioeventfd", + "Disable registration of ioeventfds with KVM (DEBUG)"); + object_class_property_set_description(klass, /* 3.0 */ + "x-no-vfio-ioeventfd", + "Disable linking of KVM ioeventfds to VFIO ioeventfds " + "(DEBUG)"); + object_class_property_set_description(klass, /* 3.1 */ + "x-balloon-allowed", + "Override allowing ballooning with device (DEBUG, DANGER)"); + object_class_property_set_description(klass, /* 3.2 */ + "xres", + "Set X display resolution the vGPU should use"); + object_class_property_set_description(klass, /* 3.2 */ + "yres", + "Set Y display resolution the vGPU should use"); + object_class_property_set_description(klass, /* 5.2 */ + "x-pre-copy-dirty-page-tracking", + "Disable dirty pages tracking during iterative phase " + "(DEBUG)"); + object_class_property_set_description(klass, /* 5.2, 8.0 non-experimetal */ + "enable-migration", + "Enale device migration. Also requires a host VFIO PCI " + "variant or mdev driver with migration support enabled"); + object_class_property_set_description(klass, /* 8.1 */ + "vf-token", + "Specify UUID VF token. Required for VF when PF is owned " + "by another VFIO driver"); +#ifdef CONFIG_IOMMUFD + object_class_property_set_description(klass, /* 9.0 */ + "iommufd", + "Set host IOMMUFD backend device"); +#endif + object_class_property_set_description(klass, /* 9.1 */ + "x-device-dirty-page-tracking", + "Disable device dirty page tracking and use " + "container-based dirty page tracking"); + object_class_property_set_description(klass, /* 9.1 */ + "migration-events", + "Emit VFIO migration QAPI event when a VFIO device " + "changes its migration state. For management applications"); + object_class_property_set_description(klass, /* 9.1 */ + "skip-vsc-check", + "Skip config space check for Vendor Specific Capability. " + "Setting to false will enforce strict checking of VSC content " + "(DEBUG)"); + object_class_property_set_description(klass, /* 10.0 */ + "x-migration-multifd-transfer", + "Transfer this device state via " + "multifd channels when live migrating it"); } static const TypeInfo vfio_pci_dev_info = { .name = TYPE_VFIO_PCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VFIOPCIDevice), + .parent = TYPE_VFIO_PCI_BASE, .class_init = vfio_pci_dev_class_init, .instance_init = vfio_instance_init, .instance_finalize = vfio_instance_finalize, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_PCIE_DEVICE }, - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { } - }, }; -static Property vfio_pci_dev_nohotplug_properties[] = { +static const Property vfio_pci_dev_nohotplug_properties[] = { DEFINE_PROP_BOOL("ramfb", VFIOPCIDevice, enable_ramfb, false), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_ON_OFF_AUTO("x-ramfb-migrate", VFIOPCIDevice, ramfb_migrate, + ON_OFF_AUTO_AUTO), }; -static void vfio_pci_nohotplug_dev_class_init(ObjectClass *klass, void *data) +static void vfio_pci_nohotplug_dev_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_props(dc, vfio_pci_dev_nohotplug_properties); dc->hotpluggable = false; + + object_class_property_set_description(klass, /* 3.1 */ + "ramfb", + "Enable ramfb to provide pre-boot graphics for devices " + "enabling display option"); + object_class_property_set_description(klass, /* 8.2 */ + "x-ramfb-migrate", + "Override default migration support for ramfb support " + "(DEBUG)"); } static const TypeInfo vfio_pci_nohotplug_dev_info = { @@ -3518,6 +3741,18 @@ static const TypeInfo vfio_pci_nohotplug_dev_info = { static void register_vfio_pci_dev_type(void) { + /* + * Ordinary ON_OFF_AUTO property isn't runtime-mutable, but source VM can + * run for a long time before being migrated so it is desirable to have a + * fallback mechanism to the old way of transferring VFIO device state if + * it turns to be necessary. + * The following makes this type of property have the same mutability level + * as ordinary migration parameters. + */ + vfio_pci_migration_multifd_transfer_prop = qdev_prop_on_off_auto; + vfio_pci_migration_multifd_transfer_prop.realized_set_allowed = true; + + type_register_static(&vfio_pci_base_dev_info); type_register_static(&vfio_pci_dev_info); type_register_static(&vfio_pci_nohotplug_dev_info); } diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index a2771b9ff3c..5ba7330b27e 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -12,14 +12,16 @@ #ifndef HW_VFIO_VFIO_PCI_H #define HW_VFIO_VFIO_PCI_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/pci/pci_device.h" -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" +#include "hw/vfio/vfio-region.h" #include "qemu/event_notifier.h" #include "qemu/queue.h" #include "qemu/timer.h" #include "qom/object.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" +#include "vfio-display.h" #define PCI_ANY_ID (~0) @@ -113,10 +115,20 @@ typedef struct VFIOMSIXInfo { uint32_t table_offset; uint32_t pba_offset; unsigned long *pending; + bool noresize; + MemoryRegion *pba_region; } VFIOMSIXInfo; +/* + * TYPE_VFIO_PCI_BASE is an abstract type used to share code + * between VFIO implementations that use a kernel driver + * with those that use user sockets. + */ +#define TYPE_VFIO_PCI_BASE "vfio-pci-base" +OBJECT_DECLARE_SIMPLE_TYPE(VFIOPCIDevice, VFIO_PCI_BASE) + #define TYPE_VFIO_PCI "vfio-pci" -OBJECT_DECLARE_SIMPLE_TYPE(VFIOPCIDevice, VFIO_PCI) +/* TYPE_VFIO_PCI shares struct VFIOPCIDevice. */ struct VFIOPCIDevice { PCIDevice pdev; @@ -153,13 +165,16 @@ struct VFIOPCIDevice { #define VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT 2 #define VFIO_FEATURE_ENABLE_IGD_OPREGION \ (1 << VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT) +#define VFIO_FEATURE_ENABLE_IGD_LPC_BIT 3 +#define VFIO_FEATURE_ENABLE_IGD_LPC \ + (1 << VFIO_FEATURE_ENABLE_IGD_LPC_BIT) OnOffAuto display; uint32_t display_xres; uint32_t display_yres; int32_t bootindex; + OnOffAuto igd_legacy_mode; uint32_t igd_gms; OffAutoPCIBAR msix_relo; - uint8_t pm_cap; uint8_t nv_gpudirect_clique; bool pci_aer; bool req_enabled; @@ -173,8 +188,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; }; @@ -194,6 +211,14 @@ static inline bool vfio_is_vga(VFIOPCIDevice *vdev) return class == PCI_CLASS_DISPLAY_VGA; } +/* MSI/MSI-X/INTx */ +void vfio_pci_vector_init(VFIOPCIDevice *vdev, int nr); +void vfio_pci_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, + int vector_n, bool msix); +void vfio_pci_prepare_kvm_msi_virq_batch(VFIOPCIDevice *vdev); +void vfio_pci_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev); +bool vfio_pci_intx_enable(VFIOPCIDevice *vdev, Error **errp); + uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len); void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, uint32_t val, int len); @@ -202,6 +227,7 @@ uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size); void vfio_vga_write(void *opaque, hwaddr addr, uint64_t data, unsigned size); bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev); +bool vfio_config_quirk_setup(VFIOPCIDevice *vdev, Error **errp); void vfio_vga_quirk_setup(VFIOPCIDevice *vdev); void vfio_vga_quirk_exit(VFIOPCIDevice *vdev); void vfio_vga_quirk_finalize(VFIOPCIDevice *vdev); @@ -209,23 +235,37 @@ 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_bar4_quirk(VFIOPCIDevice *vdev, int nr); +void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr); +bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp); 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); 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; + +void vfio_pci_bars_exit(VFIOPCIDevice *vdev); +bool vfio_pci_add_capabilities(VFIOPCIDevice *vdev, Error **errp); +bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp); +bool vfio_pci_interrupt_setup(VFIOPCIDevice *vdev, Error **errp); +void vfio_pci_intx_eoi(VFIODevice *vbasedev); +void vfio_pci_put_device(VFIOPCIDevice *vdev); +bool vfio_pci_populate_device(VFIOPCIDevice *vdev, Error **errp); +void vfio_pci_register_err_notifier(VFIOPCIDevice *vdev); +void vfio_pci_register_req_notifier(VFIOPCIDevice *vdev); +void vfio_pci_teardown_msi(VFIOPCIDevice *vdev); + #endif /* HW_VFIO_VFIO_PCI_H */ diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 5af73f92876..9a21f2e50a2 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -15,26 +15,29 @@ */ #include "qemu/osdep.h" +#include CONFIG_DEVICES /* CONFIG_IOMMUFD */ #include "qapi/error.h" #include #include #include "hw/vfio/vfio-platform.h" +#include "system/iommufd.h" #include "migration/vmstate.h" #include "qemu/error-report.h" #include "qemu/lockable.h" #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/range.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" +#include "system/memory.h" +#include "system/address-spaces.h" #include "qemu/queue.h" #include "hw/sysbus.h" #include "trace.h" #include "hw/irq.h" #include "hw/platform-bus.h" #include "hw/qdev-properties.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" +#include "hw/vfio/vfio-region.h" /* * Functions used whatever the injection method @@ -113,18 +116,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_device_irq_set_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; } /* @@ -299,7 +301,7 @@ static void vfio_platform_eoi(VFIODevice *vbasedev) if (vfio_irq_is_automasked(intp)) { /* unmasks the physical level-sensitive IRQ */ - vfio_unmask_single_irqindex(vbasedev, intp->pin); + vfio_device_irq_unmask(vbasedev, intp->pin); } /* a single IRQ can be active at a time */ @@ -353,15 +355,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_device_irq_set_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; } /** @@ -418,7 +419,6 @@ static void vfio_start_irqfd_injection(SysBusDevice *sbdev, qemu_irq irq) abort(); fail_irqfd: vfio_start_eventfd_injection(sbdev, irq); - return; } /* VFIO skeleton */ @@ -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); @@ -474,10 +474,10 @@ static int vfio_populate_device(VFIODevice *vbasedev, Error **errp) QSIMPLEQ_INIT(&vdev->pending_intp_queue); for (i = 0; i < vbasedev->num_irqs; i++) { - struct vfio_irq_info irq = { .argsz = sizeof(irq) }; + struct vfio_irq_info irq; + + ret = vfio_device_get_irq_info(vbasedev, i, &irq); - irq.index = i; - ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq); if (ret) { error_setg_errno(errp, -ret, "failed to get device irq info"); goto irq_err; @@ -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 @@ static int vfio_populate_device(VFIODevice *vbasedev, Error **errp) 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; - } - - 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_device_attach(vbasedev->name, vbasedev, + &address_space_memory, errp)) { + return false; } - trace_vfio_platform_base_device_init(vbasedev->name, groupid); - - group = vfio_get_group(groupid, &address_space_memory, errp); - if (!group) { - return -ENOENT; + if (vfio_populate_device(vbasedev, errp)) { + return true; } - 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_device_detach(vbasedev); + return false; } /** @@ -613,24 +569,21 @@ 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; + warn_report("-device vfio-platform is deprecated"); qemu_mutex_init(&vdev->intp_mutex); trace_vfio_platform_realize(vbasedev->sysfsdev ? 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 +615,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 { @@ -679,35 +630,80 @@ static const VMStateDescription vfio_platform_vmstate = { .unmigratable = 1, }; -static Property vfio_platform_dev_properties[] = { +static const Property vfio_platform_dev_properties[] = { DEFINE_PROP_STRING("host", VFIOPlatformDevice, vbasedev.name), DEFINE_PROP_STRING("sysfsdev", VFIOPlatformDevice, vbasedev.sysfsdev), DEFINE_PROP_BOOL("x-no-mmap", VFIOPlatformDevice, vbasedev.no_mmap, false), DEFINE_PROP_UINT32("mmap-timeout-ms", VFIOPlatformDevice, mmap_timeout, 1100), DEFINE_PROP_BOOL("x-irqfd", VFIOPlatformDevice, irqfd_allowed, true), - DEFINE_PROP_END_OF_LIST(), +#ifdef CONFIG_IOMMUFD + DEFINE_PROP_LINK("iommufd", VFIOPlatformDevice, vbasedev.iommufd, + TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), +#endif }; -static void vfio_platform_class_init(ObjectClass *klass, void *data) +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, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); 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; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - /* Supported by TYPE_VIRT_MACHINE */ - dc->user_creatable = true; + + object_class_property_set_description(klass, /* 2.4 */ + "host", + "Host device name of assigned device"); + object_class_property_set_description(klass, /* 2.4 and 2.5 */ + "x-no-mmap", + "Disable MMAP for device. Allows to trace MMIO " + "accesses (DEBUG)"); + object_class_property_set_description(klass, /* 2.4 */ + "mmap-timeout-ms", + "When EOI is not provided by KVM/QEMU, wait time " + "(milliseconds) to re-enable device direct access " + "after level interrupt (DEBUG)"); + object_class_property_set_description(klass, /* 2.4 */ + "x-irqfd", + "Allow disabling irqfd support (DEBUG)"); + object_class_property_set_description(klass, /* 2.6 */ + "sysfsdev", + "Host sysfs path of assigned device"); +#ifdef CONFIG_IOMMUFD + object_class_property_set_description(klass, /* 9.0 */ + "iommufd", + "Set host IOMMUFD backend device"); +#endif } static const TypeInfo vfio_platform_dev_info = { .name = TYPE_VFIO_PLATFORM, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_DYNAMIC_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/region.c b/hw/vfio/region.c new file mode 100644 index 00000000000..f5b8e3cbf10 --- /dev/null +++ b/hw/vfio/region.c @@ -0,0 +1,406 @@ +/* + * VFIO regions + * + * 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-region.h" +#include "hw/vfio/vfio-device.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" +#include "vfio-helpers.h" + +/* + * 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; + int ret; + + 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; + } + + ret = vbasedev->io_ops->region_write(vbasedev, region->nr, + addr, size, &buf, region->post_wr); + if (ret != size) { + error_report("%s(%s:region%d+0x%"HWADDR_PRIx", 0x%"PRIx64 + ",%d) failed: %s", + __func__, vbasedev->name, region->nr, + addr, data, size, strwriteerror(ret)); + } + + 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; + int ret; + + ret = vbasedev->io_ops->region_read(vbasedev, region->nr, addr, size, &buf); + if (ret != size) { + error_report("%s(%s:region%d+0x%"HWADDR_PRIx", %d) failed: %s", + __func__, vbasedev->name, region->nr, + addr, size, strreaderror(ret)); + 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; +} + +static 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, + }, +}; + +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 = NULL; + int ret; + + ret = vfio_device_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; + region->post_wr = false; + + 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; + int fd; + + 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; + } + + /* Use the per-region fd if set, or the shared fd. */ + fd = region->vbasedev->region_fds ? + region->vbasedev->region_fds[region->nr] : + region->vbasedev->fd, + + 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, 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); +} diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 9ec1e95f6da..564b70ef97f 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -11,13 +11,33 @@ #include "qemu/osdep.h" #include #include +#include "system/kvm.h" +#include "system/hostmem.h" +#include "system/address-spaces.h" -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-container.h" #include "hw/hw.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "trace.h" +#include "vfio-helpers.h" + +typedef struct VFIOHostDMAWindow { + hwaddr min_iova; + hwaddr max_iova; + uint64_t iova_pgsizes; + QLIST_ENTRY(VFIOHostDMAWindow) hostwin_next; +} VFIOHostDMAWindow; + +typedef struct VFIOSpaprContainer { + VFIOContainer container; + MemoryListener prereg_listener; + QLIST_HEAD(, VFIOHostDMAWindow) hostwin_list; + unsigned int levels; +} VFIOSpaprContainer; + +OBJECT_DECLARE_SIMPLE_TYPE(VFIOSpaprContainer, VFIO_IOMMU_SPAPR); static bool vfio_prereg_listener_skipped_section(MemoryRegionSection *section) { @@ -39,8 +59,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 +105,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 +119,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,20 +158,98 @@ 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 bool vfio_spapr_create_window(VFIOContainer *container, + MemoryRegionSection *section, + hwaddr *pgsize, Error **errp) { int ret = 0; + VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, + container); 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; + unsigned entries, bits_total, bits_per_level, max_levels, ddw_levels; struct vfio_iommu_spapr_tce_create create = { .argsz = sizeof(create) }; long rampagesize = qemu_minrampagesize(); @@ -159,14 +260,14 @@ 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); - return -EINVAL; + error_setg_errno(errp, EINVAL, "Host doesn't support page size 0x%"PRIx64 + ", the supported mask is 0x%lx", + memory_region_iommu_get_min_page_size(iommu_mr), + bcontainer->pgsizes); + return false; } /* @@ -201,28 +302,41 @@ int vfio_spapr_create_window(VFIOContainer *container, */ bits_per_level = ctz64(qemu_real_host_page_size()) + 8; create.levels = bits_total / bits_per_level; - if (bits_total % bits_per_level) { - ++create.levels; - } - max_levels = (64 - create.page_shift) / ctz64(qemu_real_host_page_size()); - for ( ; create.levels <= max_levels; ++create.levels) { - ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_CREATE, &create); - if (!ret) { - break; + + ddw_levels = scontainer->levels; + if (ddw_levels > 1) { + if (bits_total % bits_per_level) { + ++create.levels; + } + max_levels = (64 - create.page_shift) / ctz64(qemu_real_host_page_size()); + for ( ; create.levels <= max_levels; ++create.levels) { + ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_CREATE, &create); + if (!ret) { + break; + } + } + } else { /* ddw_levels == 1 */ + if (create.levels > ddw_levels) { + error_setg_errno(errp, EINVAL, "Host doesn't support multi-level TCE tables" + ". Use larger IO page size. Supported mask is 0x%lx", + bcontainer->pgsizes); + return false; } + ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_CREATE, &create); } + if (ret) { - error_report("Failed to create a window, ret = %d (%m)", ret); - return -errno; + error_setg_errno(errp, errno, "Failed to create a window, ret = %d", ret); + return false; } if (create.start_addr != section->offset_within_address_space) { vfio_spapr_remove_window(container, create.start_addr); - error_report("Host doesn't support DMA window at %"HWADDR_PRIx", must be %"PRIx64, - section->offset_within_address_space, - (uint64_t)create.start_addr); - return -EINVAL; + error_setg_errno(errp, EINVAL, "Host doesn't support DMA window at %"HWADDR_PRIx + ", must be %"PRIx64, section->offset_within_address_space, + (uint64_t)create.start_addr); + return false; } trace_vfio_spapr_create_window(create.page_shift, create.levels, @@ -230,26 +344,238 @@ int vfio_spapr_create_window(VFIOContainer *container, create.start_addr); *pgsize = pagesize; - return 0; + return true; } -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); + /* + * 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; + } + + if (container->iommu_type != VFIO_SPAPR_TCE_v2_IOMMU) { + return true; + } + + /* 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, errp); + if (!ret) { + 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_report("Failed to remove window at %"PRIx64, - (uint64_t)remove.start_addr); - return -errno; + error_setg_errno(errp, errno, + "VFIO_IOMMU_SPAPR_TCE_GET_INFO failed"); + goto listener_unregister_exit; } - trace_vfio_spapr_remove_window(offset_within_address_space); + scontainer->levels = info.ddw.levels; - return 0; + 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, const 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 81ec7c7a958..f06236f37b7 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -2,7 +2,7 @@ # pci.c vfio_intx_interrupt(const char *name, char line) " (%s) Pin %c" -vfio_intx_eoi(const char *name) " (%s) EOI" +vfio_pci_intx_eoi(const char *name) " (%s) EOI" vfio_intx_enable_kvm(const char *name) " (%s) KVM INTx accel enabled" vfio_intx_disable_kvm(const char *name) " (%s) KVM INTx accel disabled" vfio_intx_update(const char *name, int new_irq, int target_irq) " (%s) IRQ moved %d -> %d" @@ -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,8 @@ 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_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_pci_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_pci_populate_device_get_irq_info_failure(const char *errstr) "VFIO_DEVICE_GET_IRQ_INFO failure: %s" 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,33 +81,39 @@ 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" vfio_pci_igd_host_bridge_enabled(const char *name) "%s" vfio_pci_igd_lpc_bridge_enabled(const char *name) "%s" -# common.c -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 +# listener.c vfio_iommu_map_notify(const char *op, uint64_t iova_start, uint64_t iova_end) "iommu %s @ 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(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" -vfio_put_base_device(int fd) "close vdev->fd=%d" +vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64 + +# container-base.c +vfio_container_query_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 + +# container.c +vfio_container_disconnect(int fd) "close container->fd=%d" +vfio_group_put(int fd) "close group->fd=%d" +vfio_device_get(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u" +vfio_device_put(int fd) "close vdev->fd=%d" +vfio_legacy_dma_unmap_overflow_workaround(void) "" + +# region.c +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_region_setup(const char *dev, int index, const char *name, unsigned long flags, unsigned long offset, unsigned long size) "Device %s, region %d \"%s\", flags: 0x%lx, offset: 0x%lx, size: 0x%lx" vfio_region_mmap_fault(const char *name, int index, unsigned long offset, unsigned long size, int fault) "Region %s mmaps[%d], [0x%lx - 0x%lx], fault: %d" vfio_region_mmap(const char *name, unsigned long offset, unsigned long end) "Region %s [0x%lx - 0x%lx]" @@ -118,13 +123,8 @@ 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/%08x" -vfio_dma_unmap_overflow_workaround(void) "" -vfio_get_dirty_bitmap(int fd, uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "container fd=%d, 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" @@ -151,20 +151,52 @@ vfio_display_edid_update(uint32_t prefx, uint32_t prefy) "%ux%u" vfio_display_edid_write_error(void) "" # migration.c +vfio_load_bufs_thread_start(const char *name) " (%s)" +vfio_load_bufs_thread_end(const char *name) " (%s)" vfio_load_cleanup(const char *name) " (%s)" -vfio_load_device_config_state(const char *name) " (%s)" +vfio_load_device_config_state_start(const char *name) " (%s)" +vfio_load_device_config_state_end(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_size, int ret) " (%s) size 0x%"PRIx64" ret %d" +vfio_load_state_device_data(const char *name, uint64_t data_size, int ret) " (%s) size %"PRIu64" ret %d" +vfio_load_state_device_buffer_incoming(const char *name, uint32_t idx) " (%s) idx %"PRIu32 +vfio_load_state_device_buffer_start(const char *name) " (%s)" +vfio_load_state_device_buffer_starved(const char *name, uint32_t idx) " (%s) idx %"PRIu32 +vfio_load_state_device_buffer_load_start(const char *name, uint32_t idx) " (%s) idx %"PRIu32 +vfio_load_state_device_buffer_load_end(const char *name, uint32_t idx) " (%s) idx %"PRIu32 +vfio_load_state_device_buffer_end(const char *name) " (%s)" vfio_migration_realize(const char *name) " (%s)" -vfio_migration_set_state(const char *name, const char *state) " (%s) state %s" -vfio_migration_state_notifier(const char *name, const char *state) " (%s) state %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_complete_precopy_thread_start(const char *name, const char *idstr, uint32_t instance_id) " (%s) idstr %s instance %"PRIu32 +vfio_save_complete_precopy_thread_end(const char *name, int ret) " (%s) ret %d" vfio_save_device_config_state(const char *name) " (%s)" -vfio_save_iterate(const char *name, uint64_t precopy_init_size, uint64_t precopy_dirty_size) " (%s) precopy initial size 0x%"PRIx64" precopy dirty size 0x%"PRIx64 -vfio_save_setup(const char *name, uint64_t data_buffer_size) " (%s) data buffer size 0x%"PRIx64 -vfio_state_pending_estimate(const char *name, uint64_t precopy, uint64_t postcopy, uint64_t precopy_init_size, uint64_t precopy_dirty_size) " (%s) precopy 0x%"PRIx64" postcopy 0x%"PRIx64" precopy initial size 0x%"PRIx64" precopy dirty size 0x%"PRIx64 -vfio_state_pending_exact(const char *name, uint64_t precopy, uint64_t postcopy, uint64_t stopcopy_size, uint64_t precopy_init_size, uint64_t precopy_dirty_size) " (%s) precopy 0x%"PRIx64" postcopy 0x%"PRIx64" stopcopy size 0x%"PRIx64" precopy initial size 0x%"PRIx64" precopy dirty size 0x%"PRIx64 +vfio_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" + +# device.c +vfio_device_get_region_info_type(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%08x" +vfio_device_reset_handler(void) "" +vfio_device_attach(const char *name, int group_id) " (%s) group %d" +vfio_device_detach(const char *name, int group_id) " (%s) group %d" diff --git a/hw/vfio/vfio-display.h b/hw/vfio/vfio-display.h new file mode 100644 index 00000000000..2606c34b396 --- /dev/null +++ b/hw/vfio/vfio-display.h @@ -0,0 +1,42 @@ +/* + * VFIO display + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_VFIO_DISPLAY_H +#define HW_VFIO_VFIO_DISPLAY_H + +#include "ui/console.h" +#include "hw/display/ramfb.h" +#include "hw/vfio/vfio-region.h" + +typedef struct VFIODMABuf { + QemuDmaBuf *buf; + uint32_t pos_x, pos_y, pos_updates; + uint32_t hot_x, hot_y, hot_updates; + int dmabuf_id; + QTAILQ_ENTRY(VFIODMABuf) next; +} VFIODMABuf; + +typedef struct VFIODisplay { + QemuConsole *con; + RAMFBState *ramfb; + struct vfio_region_info *edid_info; + struct vfio_region_gfx_edid *edid_regs; + uint8_t *edid_blob; + QEMUTimer *edid_link_timer; + struct { + VFIORegion buffer; + DisplaySurface *surface; + } region; + struct { + QTAILQ_HEAD(, VFIODMABuf) bufs; + VFIODMABuf *primary; + VFIODMABuf *cursor; + } dmabuf; +} VFIODisplay; + +#endif /* HW_VFIO_VFIO_DISPLAY_H */ diff --git a/hw/vfio/vfio-helpers.h b/hw/vfio/vfio-helpers.h new file mode 100644 index 00000000000..54a327ffbc0 --- /dev/null +++ b/hw/vfio/vfio-helpers.h @@ -0,0 +1,35 @@ +/* + * VFIO helpers + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_VFIO_HELPERS_H +#define HW_VFIO_VFIO_HELPERS_H + +#ifdef CONFIG_LINUX +#include + +extern int vfio_kvm_device_fd; + +struct vfio_info_cap_header * +vfio_get_cap(void *ptr, uint32_t cap_offset, uint16_t id); +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_region_info_cap(struct vfio_region_info *info, uint16_t id); +struct vfio_info_cap_header * +vfio_get_iommu_type1_info_cap(struct vfio_iommu_type1_info *info, uint16_t id); +bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, + unsigned int *avail); +#endif + +int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size); +struct vfio_device_info *vfio_get_device_info(int fd); + +int vfio_kvm_device_add_fd(int fd, Error **errp); +int vfio_kvm_device_del_fd(int fd, Error **errp); + +#endif /* HW_VFIO_VFIO_HELPERS_H */ diff --git a/hw/vfio/vfio-iommufd.h b/hw/vfio/vfio-iommufd.h new file mode 100644 index 00000000000..07ea0f43049 --- /dev/null +++ b/hw/vfio/vfio-iommufd.h @@ -0,0 +1,34 @@ +/* + * VFIO iommufd + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_VFIO_IOMMUFD_H +#define HW_VFIO_VFIO_IOMMUFD_H + +#include "hw/vfio/vfio-container-base.h" + +typedef struct VFIODevice VFIODevice; + +typedef struct VFIOIOASHwpt { + uint32_t hwpt_id; + uint32_t hwpt_flags; + QLIST_HEAD(, VFIODevice) device_list; + QLIST_ENTRY(VFIOIOASHwpt) next; +} VFIOIOASHwpt; + +typedef struct IOMMUFDBackend IOMMUFDBackend; + +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); + +#endif /* HW_VFIO_VFIO_IOMMUFD_H */ diff --git a/hw/vfio/vfio-listener.h b/hw/vfio/vfio-listener.h new file mode 100644 index 00000000000..eb69ddd374f --- /dev/null +++ b/hw/vfio/vfio-listener.h @@ -0,0 +1,15 @@ +/* + * VFIO MemoryListener services + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_VFIO_LISTENER_H +#define HW_VFIO_VFIO_LISTENER_H + +bool vfio_listener_register(VFIOContainerBase *bcontainer, Error **errp); +void vfio_listener_unregister(VFIOContainerBase *bcontainer); + +#endif /* HW_VFIO_VFIO_LISTENER_H */ diff --git a/hw/vfio/vfio-migration-internal.h b/hw/vfio/vfio-migration-internal.h new file mode 100644 index 00000000000..a8b456b239d --- /dev/null +++ b/hw/vfio/vfio-migration-internal.h @@ -0,0 +1,74 @@ +/* + * VFIO migration + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_VFIO_MIGRATION_INTERNAL_H +#define HW_VFIO_VFIO_MIGRATION_INTERNAL_H + +#ifdef CONFIG_LINUX +#include +#endif + +#include "qemu/typedefs.h" +#include "qemu/notify.h" + +/* + * Flags to be used as unique delimiters for VFIO devices in the migration + * stream. These flags are composed as: + * 0xffffffff => MSB 32-bit all 1s + * 0xef10 => Magic ID, represents emulated (virtual) function IO + * 0x0000 => 16-bits reserved for flags + * + * The beginning of state information is marked by _DEV_CONFIG_STATE, + * _DEV_SETUP_STATE, or _DEV_DATA_STATE, respectively. The end of a + * certain state information is marked by _END_OF_STATE. + */ +#define VFIO_MIG_FLAG_END_OF_STATE (0xffffffffef100001ULL) +#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) + +typedef struct VFIODevice VFIODevice; +typedef struct VFIOMultifd VFIOMultifd; + +typedef struct VFIOMigration { + struct VFIODevice *vbasedev; + VMChangeStateEntry *vm_state; + NotifierWithReturn migration_state; + uint32_t device_state; + 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 multifd_transfer; + VFIOMultifd *multifd; + bool initial_data_sent; + + bool event_save_iterate_started; + bool event_precopy_empty_hit; +} VFIOMigration; + +bool vfio_migration_realize(VFIODevice *vbasedev, Error **errp); +void vfio_migration_exit(VFIODevice *vbasedev); +bool vfio_device_state_is_running(VFIODevice *vbasedev); +bool vfio_device_state_is_precopy(VFIODevice *vbasedev); +int vfio_save_device_config_state(QEMUFile *f, void *opaque, Error **errp); +int vfio_load_device_config_state(QEMUFile *f, void *opaque); + +#ifdef CONFIG_LINUX +int vfio_migration_set_state(VFIODevice *vbasedev, + enum vfio_device_mig_state new_state, + enum vfio_device_mig_state recover_state, + Error **errp); +#endif + +void vfio_migration_add_bytes_transferred(unsigned long val); + +#endif /* HW_VFIO_VFIO_MIGRATION_INTERNAL_H */ diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig index 92c9cf6c96c..7648a2d68da 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 @@ -24,6 +29,7 @@ config VIRTIO_MMIO config VIRTIO_CCW bool select VIRTIO + select VIRTIO_MD_SUPPORTED config VIRTIO_BALLOON bool @@ -35,10 +41,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 @@ -46,9 +59,11 @@ config VIRTIO_PMEM bool default y depends on VIRTIO + depends on VIRTIO_MD_SUPPORTED depends on VIRTIO_PMEM_SUPPORTED select VIRTIO_MD +# selected by the board if it has the required support code config VIRTIO_MEM_SUPPORTED bool @@ -57,6 +72,7 @@ config VIRTIO_MEM default y depends on VIRTIO depends on LINUX + depends on VIRTIO_MD_SUPPORTED depends on VIRTIO_MEM_SUPPORTED select VIRTIO_MD @@ -101,7 +117,12 @@ config VHOST_VDPA_DEV default y depends on VIRTIO && VHOST_VDPA && LINUX -config VHOST_USER_SCMI +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 00000000000..49f55df3994 --- /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/iothread-vq-mapping.c b/hw/virtio/iothread-vq-mapping.c new file mode 100644 index 00000000000..15909eb9332 --- /dev/null +++ b/hw/virtio/iothread-vq-mapping.c @@ -0,0 +1,131 @@ +/* + * IOThread Virtqueue Mapping + * + * Copyright Red Hat, Inc + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include "qemu/osdep.h" +#include "system/iothread.h" +#include "hw/virtio/iothread-vq-mapping.h" + +static bool +iothread_vq_mapping_validate(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; +} + +bool iothread_vq_mapping_apply( + IOThreadVirtQueueMappingList *list, + AioContext **vq_aio_context, + uint16_t num_queues, + Error **errp) +{ + IOThreadVirtQueueMappingList *node; + size_t num_iothreads = 0; + size_t cur_iothread = 0; + + if (!iothread_vq_mapping_validate(list, num_queues, errp)) { + return false; + } + + for (node = list; node; node = node->next) { + num_iothreads++; + } + + for (node = 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; +} + +void iothread_vq_mapping_cleanup(IOThreadVirtQueueMappingList *list) +{ + IOThreadVirtQueueMappingList *node; + + for (node = list; node; node = node->next) { + IOThread *iothread = iothread_by_id(node->value->iothread); + object_unref(OBJECT(iothread)); + } +} + diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index 13e7c6c272b..164f6fd995a 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -1,27 +1,52 @@ -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')) -softmmu_virtio_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('virtio-crypto.c')) -softmmu_virtio_ss.add(when: 'CONFIG_VHOST_VSOCK_COMMON', if_true: files('vhost-vsock-common.c')) -softmmu_virtio_ss.add(when: 'CONFIG_VIRTIO_IOMMU', if_true: files('virtio-iommu.c')) -softmmu_virtio_ss.add(when: 'CONFIG_VHOST_VDPA_DEV', if_true: files('vdpa-dev.c')) +system_virtio_ss = ss.source_set() +system_virtio_ss.add(files('virtio-bus.c')) +system_virtio_ss.add(files('iothread-vq-mapping.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')) 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 - softmmu_virtio_ss.add(files('vhost.c')) + system_virtio_ss.add(files('vhost.c')) specific_virtio_ss.add(files('vhost-backend.c', 'vhost-iova-tree.c')) if have_vhost_user + # 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 - specific_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 specific_virtio_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: files('virtio-balloon.c')) @@ -30,11 +55,8 @@ 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_I2C', if_true: files('vhost-user-i2c.c')) -specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng.c')) -specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_GPIO', if_true: files('vhost-user-gpio.c')) -specific_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_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')) @@ -42,9 +64,6 @@ 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')) @@ -53,6 +72,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')) @@ -67,11 +87,13 @@ virtio_pci_ss.add(when: 'CONFIG_VIRTIO_MD', if_true: files('virtio-md-pci.c')) specific_virtio_ss.add_all(when: 'CONFIG_VIRTIO_PCI', if_true: virtio_pci_ss) -system_ss.add_all(when: 'CONFIG_VIRTIO', if_true: softmmu_virtio_ss) +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_ALL', if_true: files('vhost-stub.c')) -system_ss.add(when: 'CONFIG_ALL', if_true: files('virtio-stub.c')) +system_ss.add(when: ['CONFIG_VIRTIO_MD', 'CONFIG_VIRTIO_PCI'], + 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 7109cf1a3bc..76f0d458b29 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -30,16 +30,17 @@ 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, uint32_t asid, uint64_t iova, uint64_t size, uint64_t uaddr, uint8_t perm, uint8_t type) "vdpa:%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:%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:%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_listener_region_add_unaligned(void *v, const char *name, uint64_t offset_as, uint64_t offset_page) "vdpa: %p region %s offset_within_address_space %"PRIu64" offset_within_region %"PRIu64 +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: %p region %s offset_within_address_space %"PRIu64" offset_within_region %"PRIu64 +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 @@ -48,8 +49,8 @@ vhost_vdpa_set_features(void *dev, uint64_t features) "dev: %p features: 0x%"PRI vhost_vdpa_get_device_id(void *dev, uint32_t device_id) "dev: %p device_id %"PRIu32 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" @@ -57,8 +58,8 @@ 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 @@ -73,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,14 +108,15 @@ virtio_pci_notify_write(uint64_t addr, uint64_t val, unsigned int size) "0x%" PR virtio_pci_notify_write_pio(uint64_t addr, uint64_t val, unsigned int size) "0x%" PRIx64" = 0x%" PRIx64 " (%d)" # hw/virtio/virtio-iommu.c -virtio_iommu_device_reset(void) "reset!" +virtio_iommu_device_reset_exit(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 @@ -129,11 +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 index 5446e6b393e..30681121465 100644 --- a/hw/virtio/vdpa-dev-pci.c +++ b/hw/virtio/vdpa-dev-pci.c @@ -48,10 +48,6 @@ static void vhost_vdpa_device_pci_instance_init(Object *obj) "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); @@ -74,13 +70,13 @@ vhost_vdpa_device_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(DEVICE(&dev->vdev), BUS(&vpci_dev->bus), errp); } -static void vhost_vdpa_device_pci_class_init(ObjectClass *klass, void *data) +static void vhost_vdpa_device_pci_class_init(ObjectClass *klass, + const 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; } diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c index 363b6252438..d1da40afc80 100644 --- a/hw/virtio/vdpa-dev.c +++ b/hw/virtio/vdpa-dev.c @@ -26,8 +26,8 @@ #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" +#include "system/system.h" +#include "system/runstate.h" static void vhost_vdpa_device_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) @@ -66,7 +66,6 @@ static void vhost_vdpa_device_realize(DeviceState *dev, Error **errp) if (*errp) { return; } - v->vdpa.device_fd = v->vhostfd; v->vdev_id = vhost_vdpa_device_get_u32(v->vhostfd, VHOST_VDPA_GET_DEVICE_ID, errp); @@ -114,7 +113,9 @@ static void vhost_vdpa_device_realize(DeviceState *dev, Error **errp) strerror(-ret)); goto free_vqs; } - v->vdpa.iova_range = iova_range; + 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) { @@ -162,6 +163,7 @@ static void vhost_vdpa_device_realize(DeviceState *dev, Error **errp) vhost_dev_cleanup(&v->dev); free_vqs: g_free(vqs); + g_free(v->vdpa.shared); out: qemu_close(v->vhostfd); v->vhostfd = -1; @@ -184,6 +186,7 @@ static void vhost_vdpa_device_unrealize(DeviceState *dev) 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; } @@ -192,7 +195,14 @@ 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); } @@ -250,7 +260,7 @@ static int vhost_vdpa_device_start(VirtIODevice *vdev, Error **errp) s->dev.acked_features = vdev->guest_features; - ret = vhost_dev_start(&s->dev, vdev, false); + ret = vhost_dev_start(&s->dev, vdev, true); if (ret < 0) { error_setg_errno(errp, -ret, "Error starting vhost"); goto err_guest_notifiers; @@ -302,7 +312,7 @@ static void vhost_vdpa_device_stop(VirtIODevice *vdev) vhost_dev_disable_notifiers(&s->dev, vdev); } -static void vhost_vdpa_device_set_status(VirtIODevice *vdev, uint8_t status) +static int vhost_vdpa_device_set_status(VirtIODevice *vdev, uint8_t status) { VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); bool should_start = virtio_device_started(vdev, status); @@ -314,7 +324,7 @@ static void vhost_vdpa_device_set_status(VirtIODevice *vdev, uint8_t status) } if (s->started == should_start) { - return; + return 0; } if (should_start) { @@ -325,12 +335,12 @@ static void vhost_vdpa_device_set_status(VirtIODevice *vdev, uint8_t status) } else { vhost_vdpa_device_stop(vdev); } + return 0; } -static Property vhost_vdpa_device_properties[] = { +static const 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 = { @@ -338,13 +348,13 @@ static const VMStateDescription vmstate_vhost_vdpa_device = { .unmigratable = 1, .minimum_version_id = 1, .version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VIRTIO_DEVICE, VMSTATE_END_OF_LIST() }, }; -static void vhost_vdpa_device_class_init(ObjectClass *klass, void *data) +static void vhost_vdpa_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index 8e581575c9b..833804dd40f 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-iova-tree.c b/hw/virtio/vhost-iova-tree.c index 3d03395a77b..fa4147b7732 100644 --- a/hw/virtio/vhost-iova-tree.c +++ b/hw/virtio/vhost-iova-tree.c @@ -28,12 +28,18 @@ struct VhostIOVATree { /* IOVA address to qemu memory maps. */ IOVATree *iova_taddr_map; + + /* Allocated IOVA addresses */ + IOVATree *iova_map; + + /* GPA->IOVA address memory maps */ + IOVATree *gpa_iova_map; }; /** - * Create a new IOVA tree + * Create a new VhostIOVATree * - * Returns the new IOVA tree + * Returns the new VhostIOVATree. */ VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last) { @@ -44,25 +50,29 @@ VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last) tree->iova_last = iova_last; tree->iova_taddr_map = iova_tree_new(); + tree->iova_map = iova_tree_new(); + tree->gpa_iova_map = gpa_tree_new(); return tree; } /** - * Delete an iova tree + * Delete a VhostIOVATree */ void vhost_iova_tree_delete(VhostIOVATree *iova_tree) { iova_tree_destroy(iova_tree->iova_taddr_map); + iova_tree_destroy(iova_tree->iova_map); + iova_tree_destroy(iova_tree->gpa_iova_map); g_free(iova_tree); } /** * Find the IOVA address stored from a memory address * - * @tree: The iova tree + * @tree: The VhostIOVATree * @map: The map with the memory address * - * Return the stored mapping, or NULL if not found. + * Returns the stored IOVA->HVA mapping, or NULL if not found. */ const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree, const DMAMap *map) @@ -71,40 +81,111 @@ const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree, } /** - * Allocate a new mapping + * Allocate a new IOVA range and add the mapping to the IOVA->HVA tree * - * @tree: The iova tree - * @map: The iova map + * @tree: The VhostIOVATree + * @map: The IOVA mapping + * @taddr: The translated address (HVA) * * Returns: * - IOVA_OK if the map fits in the container * - IOVA_ERR_INVALID if the map does not make sense (like size overflow) * - IOVA_ERR_NOMEM if tree cannot allocate more space. * - * It returns assignated iova in map->iova if return value is VHOST_DMA_MAP_OK. + * It returns an assigned IOVA in map->iova if the return value is IOVA_OK. */ -int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map) +int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map, hwaddr taddr) { + int ret; + /* Some vhost devices do not like addr 0. Skip first page */ hwaddr iova_first = tree->iova_first ?: qemu_real_host_page_size(); - if (map->translated_addr + map->size < map->translated_addr || - map->perm == IOMMU_NONE) { + if (taddr + map->size < taddr || map->perm == IOMMU_NONE) { return IOVA_ERR_INVALID; } - /* Allocate a node in IOVA address */ - return iova_tree_alloc_map(tree->iova_taddr_map, map, iova_first, - tree->iova_last); + /* Allocate a node in the IOVA-only tree */ + ret = iova_tree_alloc_map(tree->iova_map, map, iova_first, tree->iova_last); + if (unlikely(ret != IOVA_OK)) { + return ret; + } + + /* Insert a node in the IOVA->HVA tree */ + map->translated_addr = taddr; + return iova_tree_insert(tree->iova_taddr_map, map); } /** - * Remove existing mappings from iova tree + * Remove existing mappings from the IOVA-only and IOVA->HVA trees * - * @iova_tree: The vhost iova tree + * @iova_tree: The VhostIOVATree * @map: The map to remove */ void vhost_iova_tree_remove(VhostIOVATree *iova_tree, DMAMap map) { iova_tree_remove(iova_tree->iova_taddr_map, map); + iova_tree_remove(iova_tree->iova_map, map); +} + +/** + * Find the IOVA address stored from a guest memory address (GPA) + * + * @tree: The VhostIOVATree + * @map: The map with the guest memory address + * + * Returns the stored GPA->IOVA mapping, or NULL if not found. + */ +const DMAMap *vhost_iova_tree_find_gpa(const VhostIOVATree *tree, + const DMAMap *map) +{ + return iova_tree_find_iova(tree->gpa_iova_map, map); +} + +/** + * Allocate a new IOVA range and add the mapping to the GPA->IOVA tree + * + * @tree: The VhostIOVATree + * @map: The IOVA mapping + * @taddr: The translated address (GPA) + * + * Returns: + * - IOVA_OK if the map fits both containers + * - IOVA_ERR_INVALID if the map does not make sense (like size overflow) + * - IOVA_ERR_NOMEM if the IOVA-only tree cannot allocate more space + * + * It returns an assigned IOVA in map->iova if the return value is IOVA_OK. + */ +int vhost_iova_tree_map_alloc_gpa(VhostIOVATree *tree, DMAMap *map, hwaddr taddr) +{ + int ret; + + /* Some vhost devices don't like addr 0. Skip first page */ + hwaddr iova_first = tree->iova_first ?: qemu_real_host_page_size(); + + if (taddr + map->size < taddr || map->perm == IOMMU_NONE) { + return IOVA_ERR_INVALID; + } + + /* Allocate a node in the IOVA-only tree */ + ret = iova_tree_alloc_map(tree->iova_map, map, iova_first, tree->iova_last); + if (unlikely(ret != IOVA_OK)) { + return ret; + } + + /* Insert a node in the GPA->IOVA tree */ + map->translated_addr = taddr; + return gpa_tree_insert(tree->gpa_iova_map, map); +} + +/** + * Remove existing mappings from the IOVA-only and GPA->IOVA trees + * + * @tree: The VhostIOVATree + * @map: The map to remove + */ +void vhost_iova_tree_remove_gpa(VhostIOVATree *iova_tree, DMAMap map) +{ + iova_tree_remove(iova_tree->gpa_iova_map, map); + iova_tree_remove(iova_tree->iova_map, map); } diff --git a/hw/virtio/vhost-iova-tree.h b/hw/virtio/vhost-iova-tree.h index 4adfd79ff03..08f63b61cd1 100644 --- a/hw/virtio/vhost-iova-tree.h +++ b/hw/virtio/vhost-iova-tree.h @@ -11,7 +11,7 @@ #define HW_VIRTIO_VHOST_IOVA_TREE_H #include "qemu/iova-tree.h" -#include "exec/memory.h" +#include "system/memory.h" typedef struct VhostIOVATree VhostIOVATree; @@ -21,7 +21,13 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostIOVATree, vhost_iova_tree_delete); const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *iova_tree, const DMAMap *map); -int vhost_iova_tree_map_alloc(VhostIOVATree *iova_tree, DMAMap *map); +int vhost_iova_tree_map_alloc(VhostIOVATree *iova_tree, DMAMap *map, + hwaddr taddr); void vhost_iova_tree_remove(VhostIOVATree *iova_tree, DMAMap map); +const DMAMap *vhost_iova_tree_find_gpa(const VhostIOVATree *iova_tree, + const DMAMap *map); +int vhost_iova_tree_map_alloc_gpa(VhostIOVATree *iova_tree, DMAMap *map, + hwaddr taddr); +void vhost_iova_tree_remove_gpa(VhostIOVATree *iova_tree, DMAMap map); #endif diff --git a/hw/virtio/vhost-scsi-pci.c b/hw/virtio/vhost-scsi-pci.c index 08980bc23bd..7399acef8e2 100644 --- a/hw/virtio/vhost-scsi-pci.c +++ b/hw/virtio/vhost-scsi-pci.c @@ -38,10 +38,9 @@ struct VHostSCSIPCI { VHostSCSI vdev; }; -static Property vhost_scsi_pci_properties[] = { +static const Property vhost_scsi_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -62,7 +61,7 @@ static void vhost_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_scsi_pci_class_init(ObjectClass *klass, void *data) +static void vhost_scsi_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c index 49e5aed931b..2481d49345d 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; } @@ -78,24 +78,39 @@ static uint16_t vhost_svq_available_slots(const VhostShadowVirtqueue *svq) * @vaddr: Translated IOVA addresses * @iovec: Source qemu's VA addresses * @num: Length of iovec and minimum length of vaddr + * @gpas: Descriptors' GPAs, if backed by guest memory */ static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq, hwaddr *addrs, const struct iovec *iovec, - size_t num) + size_t num, const hwaddr *gpas) { if (num == 0) { return true; } for (size_t i = 0; i < num; ++i) { - DMAMap needle = { - .translated_addr = (hwaddr)(uintptr_t)iovec[i].iov_base, - .size = iovec[i].iov_len, - }; Int128 needle_last, map_last; size_t off; + const DMAMap *map; + DMAMap needle; + + /* Check if the descriptor is backed by guest memory */ + if (gpas) { + /* Search the GPA->IOVA tree */ + needle = (DMAMap) { + .translated_addr = gpas[i], + .size = iovec[i].iov_len, + }; + map = vhost_iova_tree_find_gpa(svq->iova_tree, &needle); + } else { + /* Search the IOVA->HVA tree */ + needle = (DMAMap) { + .translated_addr = (hwaddr)(uintptr_t)iovec[i].iov_base, + .size = iovec[i].iov_len, + }; + map = vhost_iova_tree_find_iova(svq->iova_tree, &needle); + } - const DMAMap *map = vhost_iova_tree_find_iova(svq->iova_tree, &needle); /* * Map cannot be NULL since iova map contains all guest space and * qemu already has a physical address mapped @@ -130,6 +145,7 @@ static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq, * @sg: Cache for hwaddr * @iovec: The iovec from the guest * @num: iovec length + * @addr: Descriptors' GPAs, if backed by guest memory * @more_descs: True if more descriptors come in the chain * @write: True if they are writeable descriptors * @@ -137,7 +153,8 @@ static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq, */ static bool vhost_svq_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg, const struct iovec *iovec, size_t num, - bool more_descs, bool write) + const hwaddr *addr, bool more_descs, + bool write) { uint16_t i = svq->free_head, last = svq->free_head; unsigned n; @@ -149,7 +166,7 @@ static bool vhost_svq_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg, return true; } - ok = vhost_svq_translate_addr(svq, sg, iovec, num); + ok = vhost_svq_translate_addr(svq, sg, iovec, num, addr); if (unlikely(!ok)) { return false; } @@ -165,17 +182,18 @@ static bool vhost_svq_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg, descs[i].len = cpu_to_le32(iovec[n].iov_len); last = i; - i = cpu_to_le16(svq->desc_next[i]); + i = svq->desc_next[i]; } - svq->free_head = le16_to_cpu(svq->desc_next[last]); + svq->free_head = svq->desc_next[last]; return true; } static bool vhost_svq_add_split(VhostShadowVirtqueue *svq, const struct iovec *out_sg, size_t out_num, + const hwaddr *out_addr, const struct iovec *in_sg, size_t in_num, - unsigned *head) + const hwaddr *in_addr, unsigned *head) { unsigned avail_idx; vring_avail_t *avail = svq->vring.avail; @@ -191,13 +209,14 @@ static bool vhost_svq_add_split(VhostShadowVirtqueue *svq, return false; } - ok = vhost_svq_vring_write_descs(svq, sgs, out_sg, out_num, in_num > 0, - false); + ok = vhost_svq_vring_write_descs(svq, sgs, out_sg, out_num, out_addr, + in_num > 0, false); if (unlikely(!ok)) { return false; } - ok = vhost_svq_vring_write_descs(svq, sgs, in_sg, in_num, false, true); + ok = vhost_svq_vring_write_descs(svq, sgs, in_sg, in_num, in_addr, false, + true); if (unlikely(!ok)) { return false; } @@ -228,10 +247,12 @@ static void vhost_svq_kick(VhostShadowVirtqueue *svq) smp_mb(); if (virtio_vdev_has_feature(svq->vdev, VIRTIO_RING_F_EVENT_IDX)) { - uint16_t avail_event = *(uint16_t *)(&svq->vring.used->ring[svq->vring.num]); + uint16_t avail_event = le16_to_cpu( + *(uint16_t *)(&svq->vring.used->ring[svq->vring.num])); needs_kick = vring_need_event(avail_event, svq->shadow_avail_idx, svq->shadow_avail_idx - 1); } else { - needs_kick = !(svq->vring.used->flags & VRING_USED_F_NO_NOTIFY); + needs_kick = + !(svq->vring.used->flags & cpu_to_le16(VRING_USED_F_NO_NOTIFY)); } if (!needs_kick) { @@ -247,8 +268,9 @@ static void vhost_svq_kick(VhostShadowVirtqueue *svq) * Return -EINVAL if element is invalid, -ENOSPC if dev queue is full */ int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg, - size_t out_num, const struct iovec *in_sg, size_t in_num, - VirtQueueElement *elem) + size_t out_num, const hwaddr *out_addr, + const struct iovec *in_sg, size_t in_num, + const hwaddr *in_addr, VirtQueueElement *elem) { unsigned qemu_head; unsigned ndescs = in_num + out_num; @@ -258,7 +280,8 @@ int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg, return -ENOSPC; } - ok = vhost_svq_add_split(svq, out_sg, out_num, in_sg, in_num, &qemu_head); + ok = vhost_svq_add_split(svq, out_sg, out_num, out_addr, in_sg, in_num, + in_addr, &qemu_head); if (unlikely(!ok)) { return -EINVAL; } @@ -274,8 +297,8 @@ int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg, static int vhost_svq_add_element(VhostShadowVirtqueue *svq, VirtQueueElement *elem) { - return vhost_svq_add(svq, elem->out_sg, elem->out_num, elem->in_sg, - elem->in_num, elem); + return vhost_svq_add(svq, elem->out_sg, elem->out_num, elem->out_addr, + elem->in_sg, elem->in_num, elem->in_addr, elem); } /** @@ -365,7 +388,7 @@ static bool vhost_svq_more_used(VhostShadowVirtqueue *svq) return true; } - svq->shadow_used_idx = cpu_to_le16(*(volatile uint16_t *)used_idx); + svq->shadow_used_idx = le16_to_cpu(*(volatile uint16_t *)used_idx); return svq->last_used_idx != svq->shadow_used_idx; } @@ -383,7 +406,7 @@ static bool vhost_svq_enable_notification(VhostShadowVirtqueue *svq) { if (virtio_vdev_has_feature(svq->vdev, VIRTIO_RING_F_EVENT_IDX)) { uint16_t *used_event = (uint16_t *)&svq->vring.avail->ring[svq->vring.num]; - *used_event = svq->shadow_used_idx; + *used_event = cpu_to_le16(svq->shadow_used_idx); } else { svq->vring.avail->flags &= ~cpu_to_le16(VRING_AVAIL_F_NO_INTERRUPT); } @@ -408,12 +431,13 @@ static uint16_t vhost_svq_last_desc_of_chain(const VhostShadowVirtqueue *svq, uint16_t num, uint16_t i) { for (uint16_t j = 0; j < (num - 1); ++j) { - i = le16_to_cpu(svq->desc_next[i]); + i = svq->desc_next[i]; } return i; } +G_GNUC_WARN_UNUSED_RESULT static VirtQueueElement *vhost_svq_get_buf(VhostShadowVirtqueue *svq, uint32_t *len) { @@ -514,29 +538,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; } @@ -673,7 +706,7 @@ void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev, svq->desc_state = g_new0(SVQDescState, svq->vring.num); svq->desc_next = g_new0(uint16_t, svq->vring.num); for (unsigned i = 0; i < svq->vring.num - 1; i++) { - svq->desc_next[i] = cpu_to_le16(i + 1); + svq->desc_next[i] = i + 1; } } diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h index 6efe051a703..9c273739d6d 100644 --- a/hw/virtio/vhost-shadow-virtqueue.h +++ b/hw/virtio/vhost-shadow-virtqueue.h @@ -114,12 +114,14 @@ 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 out_num, const hwaddr *out_addr, + const struct iovec *in_sg, size_t in_num, + const hwaddr *in_addr, VirtQueueElement *elem); +size_t vhost_svq_poll(VhostShadowVirtqueue *svq, size_t num); void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd); void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd); diff --git a/hw/virtio/vhost-stub.c b/hw/virtio/vhost-stub.c index aa858ef3fbd..52d42adab26 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) diff --git a/hw/virtio/vhost-user-base.c b/hw/virtio/vhost-user-base.c new file mode 100644 index 00000000000..ff67a020b47 --- /dev/null +++ b/hw/virtio/vhost-user-base.c @@ -0,0 +1,379 @@ +/* + * 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 int 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 0; + } + + ret = vhost_dev_stop(&vub->vhost_dev, vdev, true); + + if (k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, false) < 0) { + error_report("vhost guest notifier cleanup failed: %d", ret); + return -1; + } + + vhost_dev_disable_notifiers(&vub->vhost_dev, vdev); + return ret; +} + +static int 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 0; + } + + if (should_start) { + vub_start(vdev); + } else { + int ret; + ret = vub_stop(vdev); + if (ret < 0) { + return ret; + } + } + return 0; +} + +/* + * 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, const 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-blk-pci.c b/hw/virtio/vhost-user-blk-pci.c index eef8641a989..904369f5a3d 100644 --- a/hw/virtio/vhost-user-blk-pci.c +++ b/hw/virtio/vhost-user-blk-pci.c @@ -43,11 +43,10 @@ struct VHostUserBlkPCI { VHostUserBlk vdev; }; -static Property vhost_user_blk_pci_properties[] = { +static const Property vhost_user_blk_pci_properties[] = { DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_user_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -66,7 +65,7 @@ static void vhost_user_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_blk_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_blk_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-device-pci.c b/hw/virtio/vhost-user-device-pci.c new file mode 100644 index 00000000000..f10bac874e7 --- /dev/null +++ b/hw/virtio/vhost-user-device-pci.c @@ -0,0 +1,73 @@ +/* + * 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, + const 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 00000000000..3939bdf7552 --- /dev/null +++ b/hw/virtio/vhost-user-device.c @@ -0,0 +1,63 @@ +/* + * 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 const 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), +}; + +static void vud_class_init(ObjectClass *klass, const 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-pci.c b/hw/virtio/vhost-user-fs-pci.c index 6829b8b7435..1490c118bca 100644 --- a/hw/virtio/vhost-user-fs-pci.c +++ b/hw/virtio/vhost-user-fs-pci.c @@ -29,10 +29,9 @@ typedef struct VHostUserFSPCI VHostUserFSPCI; DECLARE_INSTANCE_CHECKER(VHostUserFSPCI, VHOST_USER_FS_PCI, TYPE_VHOST_USER_FS_PCI) -static Property vhost_user_fs_pci_properties[] = { +static const Property vhost_user_fs_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_user_fs_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -48,7 +47,7 @@ static void vhost_user_fs_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_fs_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_fs_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c index 49d699ffc2b..e77c69eb12e 100644 --- a/hw/virtio/vhost-user-fs.c +++ b/hw/virtio/vhost-user-fs.c @@ -23,7 +23,7 @@ #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-user-fs.h" #include "monitor/monitor.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static const int user_feature_bits[] = { VIRTIO_F_VERSION_1, @@ -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 }; @@ -99,7 +100,7 @@ static void vuf_start(VirtIODevice *vdev) vhost_dev_disable_notifiers(&fs->vhost_dev, vdev); } -static void vuf_stop(VirtIODevice *vdev) +static int vuf_stop(VirtIODevice *vdev) { VHostUserFS *fs = VHOST_USER_FS(vdev); BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); @@ -107,34 +108,39 @@ static void vuf_stop(VirtIODevice *vdev) int ret; if (!k->set_guest_notifiers) { - return; + return 0; } - vhost_dev_stop(&fs->vhost_dev, vdev, true); + ret = vhost_dev_stop(&fs->vhost_dev, vdev, true); - ret = k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false); - if (ret < 0) { + if (k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false) < 0) { error_report("vhost guest notifier cleanup failed: %d", ret); - return; + return -1; } vhost_dev_disable_notifiers(&fs->vhost_dev, vdev); + return ret; } -static void vuf_set_status(VirtIODevice *vdev, uint8_t status) +static int vuf_set_status(VirtIODevice *vdev, uint8_t status) { VHostUserFS *fs = VHOST_USER_FS(vdev); bool should_start = virtio_device_should_start(vdev, status); if (vhost_dev_is_started(&fs->vhost_dev) == should_start) { - return; + return 0; } if (should_start) { vuf_start(vdev); } else { - vuf_stop(vdev); + int ret; + ret = vuf_stop(vdev); + if (ret < 0) { + return ret; + } } + return 0; } static uint64_t vuf_get_features(VirtIODevice *vdev, @@ -266,7 +272,6 @@ static void vuf_device_realize(DeviceState *dev, Error **errp) g_free(fs->req_vqs); virtio_cleanup(vdev); g_free(fs->vhost_dev.vqs); - return; } static void vuf_device_unrealize(DeviceState *dev) @@ -298,18 +303,116 @@ 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[] = { +static const Property vuf_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserFS, conf.chardev), DEFINE_PROP_STRING("tag", VHostUserFS, conf.tag), DEFINE_PROP_UINT16("num-request-queues", VHostUserFS, conf.num_request_queues, 1), DEFINE_PROP_UINT16("queue-size", VHostUserFS, conf.queue_size, 128), - DEFINE_PROP_END_OF_LIST(), }; static void vuf_instance_init(Object *obj) @@ -320,7 +423,7 @@ static void vuf_instance_init(Object *obj) "/filesystem@0", DEVICE(obj)); } -static void vuf_class_init(ObjectClass *klass, void *data) +static void vuf_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/vhost-user-gpio-pci.c b/hw/virtio/vhost-user-gpio-pci.c index b3028a24a19..9b165b54f8f 100644 --- a/hw/virtio/vhost-user-gpio-pci.c +++ b/hw/virtio/vhost-user-gpio-pci.c @@ -32,7 +32,7 @@ static void vhost_user_gpio_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_gpio_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_gpio_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-gpio.c b/hw/virtio/vhost-user-gpio.c index 3b013f2d0fb..a7fd49b10a3 100644 --- a/hw/virtio/vhost-user-gpio.c +++ b/hw/virtio/vhost-user-gpio.c @@ -11,382 +11,24 @@ #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 const Property vgpio_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), }; -static void vu_gpio_get_config(VirtIODevice *vdev, uint8_t *config) -{ - VHostUserGPIO *gpio = VHOST_USER_GPIO(vdev); - - memcpy(config, &gpio->config, sizeof(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) +static void vgpio_realize(DeviceState *dev, Error **errp) { - 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; + VHostUserBase *vub = VHOST_USER_BASE(dev); + VHostUserBaseClass *vubc = VHOST_USER_BASE_GET_CLASS(dev); - if (!gpio->started_vu) { - return; - } - gpio->started_vu = false; + /* Fixed for GPIO */ + vub->virtio_id = VIRTIO_ID_GPIO; + vub->num_vqs = 2; + vub->config_size = sizeof(struct virtio_gpio_config); - 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); - - /* - * 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(&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 = { @@ -394,30 +36,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) +static void vu_gpio_class_init(ObjectClass *klass, const 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-pci.c b/hw/virtio/vhost-user-i2c-pci.c index 00ac10941fa..692cd66fde6 100644 --- a/hw/virtio/vhost-user-i2c-pci.c +++ b/hw/virtio/vhost-user-i2c-pci.c @@ -32,7 +32,7 @@ static void vhost_user_i2c_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_i2c_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_i2c_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-i2c.c b/hw/virtio/vhost-user-i2c.c index 4eef3f06337..ae007fe97d9 100644 --- a/hw/virtio/vhost-user-i2c.c +++ b/hw/virtio/vhost-user-i2c.c @@ -14,253 +14,21 @@ #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 const Property vi2c_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), }; -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); - - /* - * We don't support interrupts, return early if index is set to - * VIRTIO_CONFIG_IRQ_IDX. - */ - if (idx == VIRTIO_CONFIG_IRQ_IDX) { - return; - } - - 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); - - /* - * We don't support interrupts, return early if index is set to - * VIRTIO_CONFIG_IRQ_IDX. - */ - if (idx == VIRTIO_CONFIG_IRQ_IDX) { - return false; - } - - 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 = { @@ -268,30 +36,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) +static void vu_i2c_class_init(ObjectClass *klass, const 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 b858898a363..3f4761ce88a 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 00000000000..5cfc5bbb564 --- /dev/null +++ b/hw/virtio/vhost-user-input.c @@ -0,0 +1,57 @@ +/* + * 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 const Property vinput_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), +}; + +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, const 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-pci.c b/hw/virtio/vhost-user-rng-pci.c index f64935453b6..9f45fc6f355 100644 --- a/hw/virtio/vhost-user-rng-pci.c +++ b/hw/virtio/vhost-user-rng-pci.c @@ -23,10 +23,9 @@ typedef struct VHostUserRNGPCI VHostUserRNGPCI; DECLARE_INSTANCE_CHECKER(VHostUserRNGPCI, VHOST_USER_RNG_PCI, TYPE_VHOST_USER_RNG_PCI) -static Property vhost_user_rng_pci_properties[] = { +static const Property vhost_user_rng_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_user_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -41,7 +40,7 @@ static void vhost_user_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_rng_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_rng_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-rng.c b/hw/virtio/vhost-user-rng.c index efc54cd3fb1..61dadcda054 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,46 @@ #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 const VMStateDescription vu_rng_vmstate = { + .name = "vhost-user-rng", + .unmigratable = 1, }; -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 const Property vrng_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), +}; -static void vu_rng_device_unrealize(DeviceState *dev) +static void vu_rng_base_realize(DeviceState *dev, Error **errp) { - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserRNG *rng = VHOST_USER_RNG(dev); - struct vhost_virtqueue *vhost_vqs = rng->vhost_dev.vqs; + VHostUserBase *vub = VHOST_USER_BASE(dev); + VHostUserBaseClass *vubs = VHOST_USER_BASE_GET_CLASS(dev); - vu_rng_set_status(vdev, 0); + /* Fixed for RNG */ + vub->virtio_id = VIRTIO_ID_RNG; + vub->num_vqs = 1; + vub->vq_size = 4; - 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); + vubs->parent_realize(dev, errp); } -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), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vu_rng_class_init(ObjectClass *klass, void *data) +static void vu_rng_class_init(ObjectClass *klass, const 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 index 7f53af7fced..0ab56a50bb3 100644 --- a/hw/virtio/vhost-user-scmi-pci.c +++ b/hw/virtio/vhost-user-scmi-pci.c @@ -31,7 +31,7 @@ static void vhost_user_scmi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_scmi_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_scmi_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-scmi.c b/hw/virtio/vhost-user-scmi.c index 918bb7dcf7d..f9264c4374e 100644 --- a/hw/virtio/vhost-user-scmi.c +++ b/hw/virtio/vhost-user-scmi.c @@ -56,9 +56,9 @@ static int vu_scmi_start(VirtIODevice *vdev) goto err_host_notifiers; } - vhost_ack_features(&scmi->vhost_dev, feature_bits, vdev->guest_features); + vhost_ack_features(vhost_dev, feature_bits, vdev->guest_features); - ret = vhost_dev_start(&scmi->vhost_dev, vdev, true); + ret = vhost_dev_start(vhost_dev, vdev, true); if (ret < 0) { error_report("Error starting vhost-user-scmi: %d", ret); goto err_guest_notifiers; @@ -71,7 +71,7 @@ static int vu_scmi_start(VirtIODevice *vdev) * enabling/disabling irqfd. */ for (i = 0; i < scmi->vhost_dev.nvqs; i++) { - vhost_virtqueue_mask(&scmi->vhost_dev, vdev, i, false); + vhost_virtqueue_mask(vhost_dev, vdev, i, false); } return 0; @@ -83,7 +83,7 @@ static int vu_scmi_start(VirtIODevice *vdev) return ret; } -static void vu_scmi_stop(VirtIODevice *vdev) +static int vu_scmi_stop(VirtIODevice *vdev) { VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); @@ -93,41 +93,46 @@ static void vu_scmi_stop(VirtIODevice *vdev) /* vhost_dev_is_started() check in the callers is not fully reliable. */ if (!scmi->started_vu) { - return; + return 0; } scmi->started_vu = false; if (!k->set_guest_notifiers) { - return; + return 0; } - vhost_dev_stop(vhost_dev, vdev, true); + ret = vhost_dev_stop(vhost_dev, vdev, true); - ret = k->set_guest_notifiers(qbus->parent, vhost_dev->nvqs, false); - if (ret < 0) { + if (k->set_guest_notifiers(qbus->parent, vhost_dev->nvqs, false) < 0) { error_report("vhost guest notifier cleanup failed: %d", ret); - return; + return -1; } vhost_dev_disable_notifiers(vhost_dev, vdev); + return ret; } -static void vu_scmi_set_status(VirtIODevice *vdev, uint8_t status) +static int 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; + return -1; } if (vhost_dev_is_started(&scmi->vhost_dev) == should_start) { - return; + return 0; } if (should_start) { vu_scmi_start(vdev); } else { - vu_scmi_stop(vdev); + int ret; + ret = vu_scmi_stop(vdev); + if (ret < 0) { + return ret; + } } + return 0; } static uint64_t vu_scmi_get_features(VirtIODevice *vdev, uint64_t features, @@ -258,8 +263,6 @@ static void vu_scmi_device_realize(DeviceState *dev, Error **errp) 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) @@ -277,12 +280,11 @@ static const VMStateDescription vu_scmi_vmstate = { .unmigratable = 1, }; -static Property vu_scmi_properties[] = { +static const 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) +static void vu_scmi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/vhost-user-scsi-pci.c b/hw/virtio/vhost-user-scsi-pci.c index 75882e3cf94..994e51a37bb 100644 --- a/hw/virtio/vhost-user-scsi-pci.c +++ b/hw/virtio/vhost-user-scsi-pci.c @@ -29,7 +29,7 @@ #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/loader.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/virtio/virtio-pci.h" #include "qom/object.h" @@ -44,10 +44,9 @@ struct VHostUserSCSIPCI { VHostUserSCSI vdev; }; -static Property vhost_user_scsi_pci_properties[] = { +static const Property vhost_user_scsi_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_user_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -68,7 +67,7 @@ static void vhost_user_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_scsi_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_scsi_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-snd-pci.c b/hw/virtio/vhost-user-snd-pci.c new file mode 100644 index 00000000000..f5015fb6c4e --- /dev/null +++ b/hw/virtio/vhost-user-snd-pci.c @@ -0,0 +1,70 @@ +/* + * 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 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, const 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); + 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 00000000000..732411c6551 --- /dev/null +++ b/hw/virtio/vhost-user-snd.c @@ -0,0 +1,82 @@ +/* + * 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 VirtIOFeature feature_sizes[] = { + {.flags = 1ULL << VIRTIO_SND_F_CTLS, + .end = endof(struct virtio_snd_config, controls)}, + {} +}; + +static const VirtIOConfigSizeParams cfg_size_params = { + .min_size = endof(struct virtio_snd_config, chmaps), + .max_size = sizeof(struct virtio_snd_config), + .feature_sizes = feature_sizes +}; + +static const VMStateDescription vu_snd_vmstate = { + .name = "vhost-user-snd", + .unmigratable = 1, +}; + +static const Property vsnd_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), + DEFINE_PROP_BIT64("controls", VHostUserBase, + parent_obj.host_features, VIRTIO_SND_F_CTLS, false), +}; + +static void vu_snd_base_realize(DeviceState *dev, Error **errp) +{ + VHostUserBase *vub = VHOST_USER_BASE(dev); + VHostUserBaseClass *vubs = VHOST_USER_BASE_GET_CLASS(dev); + VirtIODevice *vdev = &vub->parent_obj; + + vub->virtio_id = VIRTIO_ID_SOUND; + vub->num_vqs = 4; + vub->config_size = virtio_get_config_size(&cfg_size_params, + vdev->host_features); + vub->vq_size = 64; + + vubs->parent_realize(dev, errp); +} + +static void vu_snd_class_init(ObjectClass *klass, const 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-pci.c b/hw/virtio/vhost-user-vsock-pci.c index e5a86e80136..adb877b6e05 100644 --- a/hw/virtio/vhost-user-vsock-pci.c +++ b/hw/virtio/vhost-user-vsock-pci.c @@ -31,9 +31,8 @@ struct VHostUserVSockPCI { /* vhost-user-vsock-pci */ -static Property vhost_user_vsock_pci_properties[] = { +static const Property vhost_user_vsock_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_user_vsock_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -47,7 +46,8 @@ static void vhost_user_vsock_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_vsock_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_vsock_pci_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c index 9431b9792cd..993c2873482 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 }; @@ -52,23 +54,28 @@ const VhostDevConfigOps vsock_ops = { .vhost_dev_config_notifier = vuv_handle_config_change, }; -static void vuv_set_status(VirtIODevice *vdev, uint8_t status) +static int vuv_set_status(VirtIODevice *vdev, uint8_t status) { VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); bool should_start = virtio_device_should_start(vdev, status); + int ret; if (vhost_dev_is_started(&vvc->vhost_dev) == should_start) { - return; + return 0; } if (should_start) { - int ret = vhost_vsock_common_start(vdev); + ret = vhost_vsock_common_start(vdev); if (ret < 0) { - return; + return ret; } } else { - vhost_vsock_common_stop(vdev); + ret = vhost_vsock_common_stop(vdev); + if (ret < 0) { + return ret; + } } + return 0; } static uint64_t vuv_get_features(VirtIODevice *vdev, @@ -126,7 +133,6 @@ static void vuv_device_realize(DeviceState *dev, Error **errp) err_virtio: vhost_vsock_common_unrealize(vdev); vhost_user_cleanup(&vsock->vhost_user); - return; } static void vuv_device_unrealize(DeviceState *dev) @@ -146,12 +152,11 @@ static void vuv_device_unrealize(DeviceState *dev) } -static Property vuv_properties[] = { +static const Property vuv_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserVSock, conf.chardev), - DEFINE_PROP_END_OF_LIST(), }; -static void vuv_class_init(ObjectClass *klass, void *data) +static void vuv_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 8dcf049d422..1e1d6b0d6e0 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -10,6 +10,7 @@ #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" @@ -18,16 +19,16 @@ #include "hw/virtio/virtio-net.h" #include "chardev/char-fe.h" #include "io/channel-socket.h" -#include "sysemu/kvm.h" +#include "system/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 "system/runstate.h" +#include "system/cryptodev.h" #include "migration/postcopy-ram.h" #include "trace.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include #include @@ -56,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_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, - /* 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 { @@ -121,6 +101,9 @@ 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; @@ -129,6 +112,9 @@ typedef enum VhostUserBackendRequest { 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; @@ -202,6 +188,10 @@ typedef struct VhostUserInflight { uint16_t queue_size; } VhostUserInflight; +typedef struct VhostUserShared { + unsigned char uuid[16]; +} VhostUserShared; + typedef struct { VhostUserRequest request; @@ -212,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) @@ -226,6 +222,8 @@ typedef union { VhostUserCryptoSession session; VhostUserVringArea area; VhostUserInflight inflight; + VhostUserShared object; + VhostUserTransferDeviceState transfer_state; } VhostUserPayload; typedef struct VhostUserMsg { @@ -274,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; @@ -378,6 +371,7 @@ static bool vhost_user_per_device_request(VhostUserRequest request) 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; @@ -398,7 +392,7 @@ static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, * 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. */ @@ -660,8 +654,6 @@ static void scrub_shadow_regions(struct vhost_dev *dev, } *nr_rem_reg = rm_idx; *nr_add_reg = add_idx; - - return; } static int send_remove_regions(struct vhost_dev *dev, @@ -1083,9 +1075,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, @@ -1094,20 +1172,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); + } } /* @@ -1115,23 +1200,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) @@ -1149,7 +1245,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 @@ -1185,9 +1295,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) { @@ -1228,7 +1336,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; @@ -1255,75 +1363,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_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; -} - -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, @@ -1331,29 +1373,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, @@ -1365,26 +1391,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) @@ -1492,12 +1500,17 @@ 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); } @@ -1563,7 +1576,7 @@ static int vhost_user_backend_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; @@ -1601,6 +1614,160 @@ static int vhost_user_backend_handle_vring_host_notifier(struct vhost_dev *dev, return 0; } +static int +vhost_user_backend_handle_shared_object_add(struct vhost_dev *dev, + VhostUserShared *object) +{ + QemuUUID uuid; + + memcpy(uuid.data, object->uuid, sizeof(object->uuid)); + return !virtio_add_vhost_device(&uuid, dev); +} + +/* + * 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); @@ -1658,6 +1825,17 @@ static gboolean backend_read(QIOChannel *ioc, GIOCondition condition, ret = vhost_user_backend_handle_vring_host_notifier(dev, &payload.area, fd ? fd[0] : -1); break; + case VHOST_USER_BACKEND_SHARED_OBJECT_ADD: + ret = vhost_user_backend_handle_shared_object_add(dev, &payload.object); + break; + case VHOST_USER_BACKEND_SHARED_OBJECT_REMOVE: + ret = vhost_user_backend_handle_shared_object_remove(dev, + &payload.object); + break; + case VHOST_USER_BACKEND_SHARED_OBJECT_LOOKUP: + ret = vhost_user_backend_handle_shared_object_lookup(dev->opaque, ioc, + &hdr, &payload); + break; default: error_report("Received unexpected msg type: %d.", hdr.request); ret = -EINVAL; @@ -1668,21 +1846,10 @@ static gboolean backend_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; } @@ -1953,7 +2120,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, @@ -1965,20 +2132,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 */ @@ -2205,19 +2372,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; @@ -2500,10 +2654,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) { - return memory_region_get_fd(section->mr) >= 0; + return true; } static int vhost_user_get_inflight_fd(struct vhost_dev *dev, @@ -2602,15 +2755,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) @@ -2631,9 +2776,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; } @@ -2648,16 +2791,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); } @@ -2741,11 +2876,146 @@ static void vhost_user_reset_status(struct vhost_dev *dev) } } +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, @@ -2764,7 +3034,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, @@ -2772,9 +3041,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 42f2a4bae95..7061b6e1a38 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -14,16 +14,16 @@ #include #include #include +#include "exec/target_page.h" #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-backend.h" #include "hw/virtio/virtio-net.h" #include "hw/virtio/vhost-shadow-virtqueue.h" #include "hw/virtio/vhost-vdpa.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "migration/blocker.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" -#include "cpu.h" #include "trace.h" #include "qapi/error.h" @@ -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; } @@ -67,8 +74,8 @@ static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section, * size that maps to the kernel */ - if (!memory_region_is_iommu(section->mr)) { - llend = vhost_vdpa_section_end(section); + 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 ")", @@ -84,14 +91,14 @@ static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section, * 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(struct vhost_vdpa *v, uint32_t asid, hwaddr iova, +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; @@ -99,7 +106,7 @@ int vhost_vdpa_dma_map(struct vhost_vdpa *v, uint32_t asid, hwaddr iova, 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.asid, msg.iotlb.iova, + 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); @@ -116,20 +123,20 @@ int vhost_vdpa_dma_map(struct vhost_vdpa *v, uint32_t asid, hwaddr iova, * 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(struct vhost_vdpa *v, uint32_t asid, hwaddr iova, +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.asid, 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)) { @@ -141,56 +148,55 @@ int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, uint32_t asid, hwaddr iova, return ret; } -static void vhost_vdpa_listener_begin_batch(struct vhost_vdpa *v) +static void vhost_vdpa_listener_begin_batch(VhostVDPAShared *s) { - int fd = v->device_fd; + int fd = s->device_fd; struct vhost_msg_v2 msg = { - .type = v->msg_type, + .type = VHOST_IOTLB_MSG_V2, .iotlb.type = VHOST_IOTLB_BATCH_BEGIN, }; - trace_vhost_vdpa_listener_begin_batch(v, fd, msg.type, msg.iotlb.type); + trace_vhost_vdpa_listener_begin_batch(s, fd, msg.type, msg.iotlb.type); if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) { error_report("failed to write, fd=%d, errno=%d (%s)", fd, errno, strerror(errno)); } } -static void vhost_vdpa_iotlb_batch_begin_once(struct vhost_vdpa *v) +static void vhost_vdpa_iotlb_batch_begin_once(VhostVDPAShared *s) { - if (v->dev->backend_cap & (0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH) && - !v->iotlb_batch_begin_sent) { - vhost_vdpa_listener_begin_batch(v); + if (s->backend_cap & (0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH) && + !s->iotlb_batch_begin_sent) { + vhost_vdpa_listener_begin_batch(s); } - v->iotlb_batch_begin_sent = true; + s->iotlb_batch_begin_sent = true; } static void vhost_vdpa_listener_commit(MemoryListener *listener) { - struct vhost_vdpa *v = container_of(listener, struct vhost_vdpa, listener); - struct vhost_dev *dev = v->dev; + VhostVDPAShared *s = container_of(listener, VhostVDPAShared, listener); struct vhost_msg_v2 msg = {}; - int fd = v->device_fd; + int fd = s->device_fd; - if (!(dev->backend_cap & (0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH))) { + if (!(s->backend_cap & (0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH))) { return; } - if (!v->iotlb_batch_begin_sent) { + if (!s->iotlb_batch_begin_sent) { return; } - msg.type = v->msg_type; + msg.type = VHOST_IOTLB_MSG_V2; msg.iotlb.type = VHOST_IOTLB_BATCH_END; - trace_vhost_vdpa_listener_commit(v, fd, msg.type, msg.iotlb.type); + trace_vhost_vdpa_listener_commit(s, fd, msg.type, msg.iotlb.type); if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) { error_report("failed to write, fd=%d, errno=%d (%s)", fd, errno, strerror(errno)); } - v->iotlb_batch_begin_sent = false; + s->iotlb_batch_begin_sent = false; } static void vhost_vdpa_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) @@ -198,10 +204,13 @@ 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; - struct vhost_vdpa *v = iommu->dev; + VhostVDPAShared *s = iommu->dev_shared; void *vaddr; int ret; Int128 llend; + Error *local_err = NULL; + MemoryRegion *mr; + hwaddr xlat; if (iotlb->target_as != &address_space_memory) { error_report("Wrong target AS \"%s\", only system memory is allowed", @@ -211,33 +220,38 @@ static void vhost_vdpa_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) 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(v->iova_range.last))) { + 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 ")", - v->iova_range.last, int128_get64(llend)); + 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)) { + mr = memory_translate_iotlb(iotlb, &xlat, &local_err); + if (!mr) { + error_report_err(local_err); return; } - ret = vhost_vdpa_dma_map(v, VHOST_VDPA_GUEST_PA_ASID, iova, + vaddr = memory_region_get_ram_ptr(mr) + xlat; + read_only = !(iotlb->perm & IOMMU_WO) || mr->readonly; + + 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)", - v, iova, iotlb->addr_mask + 1, vaddr, ret); + s, iova, iotlb->addr_mask + 1, vaddr, ret); } } else { - ret = vhost_vdpa_dma_unmap(v, VHOST_VDPA_GUEST_PA_ASID, iova, + 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)", - v, iova, iotlb->addr_mask + 1, ret); + s, iova, iotlb->addr_mask + 1, ret); } } } @@ -245,7 +259,7 @@ static void vhost_vdpa_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) static void vhost_vdpa_iommu_region_add(MemoryListener *listener, MemoryRegionSection *section) { - struct vhost_vdpa *v = container_of(listener, struct vhost_vdpa, listener); + VhostVDPAShared *s = container_of(listener, VhostVDPAShared, listener); struct vdpa_iommu *iommu; Int128 end; @@ -269,7 +283,7 @@ static void vhost_vdpa_iommu_region_add(MemoryListener *listener, iommu_idx); iommu->iommu_offset = section->offset_within_address_space - section->offset_within_region; - iommu->dev = v; + iommu->dev_shared = s; ret = memory_region_register_iommu_notifier(section->mr, &iommu->n, NULL); if (ret) { @@ -277,20 +291,18 @@ static void vhost_vdpa_iommu_region_add(MemoryListener *listener, return; } - QLIST_INSERT_HEAD(&v->iommu_list, iommu, iommu_next); + 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) { - struct vhost_vdpa *v = container_of(listener, struct vhost_vdpa, listener); + VhostVDPAShared *s = container_of(listener, VhostVDPAShared, listener); struct vdpa_iommu *iommu; - QLIST_FOREACH(iommu, &v->iommu_list, iommu_next) + QLIST_FOREACH(iommu, &s->iommu_list, iommu_next) { if (MEMORY_REGION(iommu->iommu_mr) == section->mr && iommu->n.start == section->offset_within_region) { @@ -306,14 +318,16 @@ 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)) { @@ -321,16 +335,16 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, return; } - if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) != - (section->offset_within_region & ~TARGET_PAGE_MASK))) { - trace_vhost_vdpa_listener_region_add_unaligned(v, section->mr->name, - section->offset_within_address_space & ~TARGET_PAGE_MASK, - section->offset_within_region & ~TARGET_PAGE_MASK); + 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; } @@ -343,28 +357,34 @@ 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_data) { + if (s->shadow_data) { int r; + hwaddr gpa = section->offset_within_address_space; - 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_gpa(s->iova_tree, &mem_region, gpa); if (unlikely(r != IOVA_OK)) { error_report("Can't allocate a mapping (%d)", r); + + if (mem_region.translated_addr == gpa) { + error_report("Insertion to GPA->IOVA tree failed"); + /* Remove the mapping from the IOVA-only tree */ + goto fail_map; + } goto fail; } iova = mem_region.iova; } - vhost_vdpa_iotlb_batch_begin_once(v); - ret = vhost_vdpa_dma_map(v, VHOST_VDPA_GUEST_PA_ASID, iova, + 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!"); @@ -374,8 +394,8 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, return; fail_map: - if (v->shadow_data) { - vhost_iova_tree_remove(v->iova_tree, mem_region); + if (s->shadow_data) { + vhost_iova_tree_remove_gpa(s->iova_tree, mem_region); } fail: @@ -392,31 +412,33 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, 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 & ~TARGET_PAGE_MASK) != - (section->offset_within_region & ~TARGET_PAGE_MASK))) { - trace_vhost_vdpa_listener_region_del_unaligned(v, section->mr->name, - section->offset_within_address_space & ~TARGET_PAGE_MASK, - section->offset_within_region & ~TARGET_PAGE_MASK); + 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; } - 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); - trace_vhost_vdpa_listener_region_del(v, iova, + trace_vhost_vdpa_listener_region_del(s, iova, int128_get64(int128_sub(llend, int128_one()))); if (int128_ge(int128_make64(iova), llend)) { @@ -425,47 +447,44 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener, llsize = int128_sub(llend, int128_make64(iova)); - if (v->shadow_data) { + if (s->shadow_data) { const DMAMap *result; - const void *vaddr = memory_region_get_ram_ptr(section->mr) + - section->offset_within_region + - (iova - section->offset_within_address_space); DMAMap mem_region = { - .translated_addr = (hwaddr)(uintptr_t)vaddr, + .translated_addr = section->offset_within_address_space, .size = int128_get64(llsize) - 1, }; - result = vhost_iova_tree_find_iova(v->iova_tree, &mem_region); + result = vhost_iova_tree_find_gpa(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_gpa(s->iova_tree, *result); } - vhost_vdpa_iotlb_batch_begin_once(v); + 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(v, VHOST_VDPA_GUEST_PA_ASID, iova, + 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)", - v, iova, int128_get64(llsize), ret); + s, iova, int128_get64(llsize), ret); } iova += int128_get64(llsize); } - ret = vhost_vdpa_dma_unmap(v, VHOST_VDPA_GUEST_PA_ASID, iova, + 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)", - v, iova, int128_get64(llsize), ret); + s, iova, int128_get64(llsize), ret); } memory_region_unref(section->mr); @@ -486,7 +505,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); @@ -505,6 +524,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; @@ -546,6 +569,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) { @@ -571,18 +599,51 @@ static void vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v) v->shadow_vqs = g_steal_pointer(&shadow_vqs); } +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_ASID | + 0x1ULL << VHOST_BACKEND_F_SUSPEND; + int r; + + if (vhost_vdpa_call(dev, VHOST_GET_BACKEND_FEATURES, &features)) { + return -EFAULT; + } + + features &= f; + + if (vhost_vdpa_first_dev(dev)) { + r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features); + if (r) { + return -EFAULT; + } + } + + dev->backend_cap = features; + v->shared->backend_cap = features; + + 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 = opaque; v->dev = dev; dev->opaque = opaque ; - v->listener = vhost_vdpa_memory_listener; - v->msg_type = VHOST_IOTLB_MSG_V2; + + ret = vhost_vdpa_set_backend_cap(dev); + if (unlikely(ret != 0)) { + return ret; + } + vhost_vdpa_init_svq(dev, v); error_propagate(&dev->migration_blocker, v->migration_blocker); @@ -618,6 +679,7 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER); + v->shared->listener = vhost_vdpa_memory_listener; return 0; } @@ -645,7 +707,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; @@ -742,10 +804,10 @@ static int vhost_vdpa_cleanup(struct vhost_dev *dev) 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; @@ -820,33 +882,6 @@ static int vhost_vdpa_set_features(struct vhost_dev *dev, return vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK); } -static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev) -{ - uint64_t features; - uint64_t f = 0x1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2 | - 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)) { - return -EFAULT; - } - - features &= f; - - if (vhost_vdpa_first_dev(dev)) { - r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features); - if (r) { - return -EFAULT; - } - } - - dev->backend_cap = features; - - return 0; -} - static int vhost_vdpa_get_device_id(struct vhost_dev *dev, uint32_t *device_id) { @@ -864,8 +899,14 @@ static int vhost_vdpa_reset_device(struct vhost_dev *dev) ret = vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &status); trace_vhost_vdpa_reset_device(dev); + if (ret) { + return ret; + } + + memory_listener_unregister(&v->shared->listener); + v->shared->listener_registered = false; v->suspended = false; - return ret; + return 0; } static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, int idx) @@ -876,20 +917,41 @@ 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) { @@ -900,13 +962,15 @@ static int vhost_vdpa_set_config_call(struct vhost_dev *dev, 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); } } @@ -956,7 +1020,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); } @@ -1054,7 +1121,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; @@ -1064,13 +1132,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, v->address_space_id, 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, @@ -1091,26 +1160,33 @@ static void vhost_vdpa_svq_unmap_rings(struct vhost_dev *dev, * * @v: Vhost-vdpa device * @needle: The area to search iova + * @taddr: The translated address (HVA) * @errorp: Error pointer */ static bool vhost_vdpa_svq_map_ring(struct vhost_vdpa *v, DMAMap *needle, - Error **errp) + hwaddr taddr, Error **errp) { int r; - r = vhost_iova_tree_map_alloc(v->iova_tree, needle); + r = vhost_iova_tree_map_alloc(v->shared->iova_tree, needle, taddr); if (unlikely(r != IOVA_OK)) { error_setg(errp, "Cannot allocate iova (%d)", r); + + if (needle->translated_addr == taddr) { + error_append_hint(errp, "Insertion to IOVA->HVA tree failed"); + /* Remove the mapping from the IOVA-only tree */ + vhost_iova_tree_remove(v->shared->iova_tree, *needle); + } return false; } - r = vhost_vdpa_dma_map(v, v->address_space_id, needle->iova, + 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; @@ -1141,11 +1217,11 @@ static bool vhost_vdpa_svq_map_rings(struct vhost_dev *dev, vhost_svq_get_vring_addr(svq, &svq_addr); driver_region = (DMAMap) { - .translated_addr = svq_addr.desc_user_addr, .size = driver_size - 1, .perm = IOMMU_RO, }; - ok = vhost_vdpa_svq_map_ring(v, &driver_region, errp); + ok = vhost_vdpa_svq_map_ring(v, &driver_region, svq_addr.desc_user_addr, + errp); if (unlikely(!ok)) { error_prepend(errp, "Cannot create vq driver region: "); return false; @@ -1155,11 +1231,11 @@ static bool vhost_vdpa_svq_map_rings(struct vhost_dev *dev, addr->avail_user_addr = driver_region.iova + avail_offset; device_region = (DMAMap) { - .translated_addr = svq_addr.used_user_addr, .size = device_size - 1, .perm = IOMMU_RW, }; - ok = vhost_vdpa_svq_map_ring(v, &device_region, errp); + ok = vhost_vdpa_svq_map_ring(v, &device_region, svq_addr.used_user_addr, + errp); if (unlikely(!ok)) { error_prepend(errp, "Cannot create vq device region: "); vhost_vdpa_svq_unmap_ring(v, driver_region.translated_addr); @@ -1211,7 +1287,7 @@ static bool vhost_vdpa_svqs_start(struct vhost_dev *dev) goto err; } - vhost_svq_start(svq, dev->vdev, vq, v->iova_tree); + 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; @@ -1274,7 +1350,7 @@ static void vhost_vdpa_suspend(struct vhost_dev *dev) if (dev->backend_cap & BIT_ULL(VHOST_BACKEND_F_SUSPEND)) { trace_vhost_vdpa_suspend(dev); - r = ioctl(v->device_fd, VHOST_VDPA_SUSPEND); + r = ioctl(v->shared->device_fd, VHOST_VDPA_SUSPEND); if (unlikely(r)) { error_report("Cannot suspend: %s(%d)", g_strerror(errno), errno); } else { @@ -1298,14 +1374,13 @@ 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; } @@ -1315,7 +1390,15 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) "IOMMU and try again"); return -1; } - memory_listener_register(&v->listener, dev->vdev->dma_as); + if (v->shared->listener_registered && + dev->vdev->dma_as != v->shared->listener.address_space) { + memory_listener_unregister(&v->shared->listener); + v->shared->listener_registered = false; + } + if (!v->shared->listener_registered) { + memory_listener_register(&v->shared->listener, dev->vdev->dma_as); + v->shared->listener_registered = true; + } return vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); } @@ -1325,16 +1408,13 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) static void vhost_vdpa_reset_status(struct vhost_dev *dev) { - struct vhost_vdpa *v = dev->opaque; - - if (dev->vq_index + dev->nvqs != dev->vq_index_end) { + 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->listener); } static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base, @@ -1397,6 +1477,7 @@ 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; } @@ -1409,7 +1490,7 @@ static int vhost_vdpa_get_vring_base(struct vhost_dev *dev, } 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; } @@ -1437,7 +1518,15 @@ static int vhost_vdpa_set_vring_call(struct vhost_dev *dev, /* Remember last call fd because we can switch to SVQ anytime. */ vhost_svq_set_svq_call_fd(svq, file->fd); - if (v->shadow_vqs_enabled) { + /* + * 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; } @@ -1459,12 +1548,27 @@ static int vhost_vdpa_get_features(struct vhost_dev *dev, static int vhost_vdpa_set_owner(struct vhost_dev *dev) { + int r; + struct vhost_vdpa *v; + if (!vhost_vdpa_first_dev(dev)) { return 0; } trace_vhost_vdpa_set_owner(dev); - return vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL); + r = vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL); + if (unlikely(r < 0)) { + return r; + } + + /* + * Being optimistic and listening address space memory. If the device + * uses vIOMMU, it is changed at vhost_vdpa_dev_start. + */ + v = dev->opaque; + memory_listener_register(&v->shared->listener, &address_space_memory); + v->shared->listener_registered = true; + return 0; } static int vhost_vdpa_vq_get_addr(struct vhost_dev *dev, @@ -1496,7 +1600,6 @@ const VhostOps vdpa_ops = { .vhost_set_vring_kick = vhost_vdpa_set_vring_kick, .vhost_set_vring_call = vhost_vdpa_set_vring_call, .vhost_get_features = vhost_vdpa_get_features, - .vhost_set_backend_cap = vhost_vdpa_set_backend_cap, .vhost_set_owner = vhost_vdpa_set_owner, .vhost_set_vring_endian = NULL, .vhost_backend_memslots_limit = vhost_vdpa_memslots_limit, @@ -1504,11 +1607,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, diff --git a/hw/virtio/vhost-vsock-common.c b/hw/virtio/vhost-vsock-common.c index 12ea87d7a70..c6c44d8989f 100644 --- a/hw/virtio/vhost-vsock-common.c +++ b/hw/virtio/vhost-vsock-common.c @@ -22,6 +22,7 @@ const int feature_bits[] = { VIRTIO_VSOCK_F_SEQPACKET, VIRTIO_F_RING_RESET, + VIRTIO_F_RING_PACKED, VHOST_INVALID_FEATURE_BIT }; @@ -94,7 +95,7 @@ int vhost_vsock_common_start(VirtIODevice *vdev) return ret; } -void vhost_vsock_common_stop(VirtIODevice *vdev) +int vhost_vsock_common_stop(VirtIODevice *vdev) { VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); @@ -102,18 +103,18 @@ void vhost_vsock_common_stop(VirtIODevice *vdev) int ret; if (!k->set_guest_notifiers) { - return; + return 0; } - vhost_dev_stop(&vvc->vhost_dev, vdev, true); + ret = vhost_dev_stop(&vvc->vhost_dev, vdev, true); - ret = k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false); - if (ret < 0) { + if (k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false) < 0) { error_report("vhost guest notifier cleanup failed: %d", ret); - return; + return -1; } vhost_dev_disable_notifiers(&vvc->vhost_dev, vdev); + return ret; } @@ -284,13 +285,12 @@ static struct vhost_dev *vhost_vsock_common_get_vhost(VirtIODevice *vdev) return &vvc->vhost_dev; } -static Property vhost_vsock_common_properties[] = { +static const Property vhost_vsock_common_properties[] = { DEFINE_PROP_ON_OFF_AUTO("seqpacket", VHostVSockCommon, seqpacket, ON_OFF_AUTO_AUTO), - DEFINE_PROP_END_OF_LIST(), }; -static void vhost_vsock_common_class_init(ObjectClass *klass, void *data) +static void vhost_vsock_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/vhost-vsock-pci.c b/hw/virtio/vhost-vsock-pci.c index 9f34414d381..0022a713d43 100644 --- a/hw/virtio/vhost-vsock-pci.c +++ b/hw/virtio/vhost-vsock-pci.c @@ -35,9 +35,8 @@ struct VHostVSockPCI { /* vhost-vsock-pci */ -static Property vhost_vsock_pci_properties[] = { +static const Property vhost_vsock_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), - DEFINE_PROP_END_OF_LIST(), }; static void vhost_vsock_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -57,7 +56,7 @@ static void vhost_vsock_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_vsock_pci_class_init(ObjectClass *klass, void *data) +static void vhost_vsock_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c index aa16d584eed..6e4088831fc 100644 --- a/hw/virtio/vhost-vsock.c +++ b/hw/virtio/vhost-vsock.c @@ -67,37 +67,38 @@ static int vhost_vsock_set_running(VirtIODevice *vdev, int start) } -static void vhost_vsock_set_status(VirtIODevice *vdev, uint8_t status) +static int vhost_vsock_set_status(VirtIODevice *vdev, uint8_t status) { VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); bool should_start = virtio_device_should_start(vdev, status); int ret; if (vhost_dev_is_started(&vvc->vhost_dev) == should_start) { - return; + return 0; } if (should_start) { ret = vhost_vsock_common_start(vdev); if (ret < 0) { - return; + return 0; } ret = vhost_vsock_set_running(vdev, 1); if (ret < 0) { vhost_vsock_common_stop(vdev); error_report("Error starting vhost vsock: %d", -ret); - return; + return 0; } } else { ret = vhost_vsock_set_running(vdev, 0); if (ret < 0) { error_report("vhost vsock set running failed: %d", ret); - return; + return 0; } vhost_vsock_common_stop(vdev); } + return 0; } static uint64_t vhost_vsock_get_features(VirtIODevice *vdev, @@ -111,7 +112,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 +122,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); @@ -204,13 +206,12 @@ static void vhost_vsock_device_unrealize(DeviceState *dev) vhost_vsock_common_unrealize(vdev); } -static Property vhost_vsock_properties[] = { +static const Property vhost_vsock_properties[] = { DEFINE_PROP_UINT64("guest-cid", VHostVSock, conf.guest_cid, 0), DEFINE_PROP_STRING("vhostfd", VHostVSock, conf.vhostfd), - DEFINE_PROP_END_OF_LIST(), }; -static void vhost_vsock_class_init(ObjectClass *klass, void *data) +static void vhost_vsock_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index e2f6ffb446b..fc438537048 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -23,9 +23,10 @@ #include "qemu/log.h" #include "standard-headers/linux/vhost_types.h" #include "hw/virtio/virtio-bus.h" +#include "hw/mem/memory-device.h" #include "migration/blocker.h" #include "migration/qemu-file-types.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "trace.h" /* enabled until disconnected backend stabilizes */ @@ -42,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 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 slots_limit = ~0U; + unsigned int free = UINT_MAX; struct vhost_dev *hdev; QLIST_FOREACH(hdev, &vhost_devices, entry) { unsigned int r = hdev->vhost_ops->vhost_backend_memslots_limit(hdev); - slots_limit = MIN(slots_limit, r); + unsigned int cur_free; + + if (hdev->vhost_ops->vhost_backend_no_private_memslots && + hdev->vhost_ops->vhost_backend_no_private_memslots(hdev)) { + cur_free = r - used_shared_memslots; + } else { + cur_free = r - used_memslots; + } + free = MIN(free, cur_free); } - return slots_limit > used_memslots; + return free; } static void vhost_dev_sync_region(struct vhost_dev *dev, @@ -66,12 +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) { @@ -124,6 +150,47 @@ 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, @@ -141,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; @@ -262,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; } @@ -294,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; @@ -315,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 */ @@ -327,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; } @@ -351,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; @@ -474,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) { @@ -502,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; } @@ -549,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; @@ -568,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; @@ -628,7 +732,6 @@ static void vhost_commit(MemoryListener *listener) memory_region_unref(old_sections[n_old_sections].mr); } g_free(old_sections); - return; } /* Adds the section data to the tmp_section structure. @@ -668,7 +771,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 @@ -701,11 +804,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 = @@ -943,6 +1042,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) { @@ -1009,7 +1117,7 @@ static int vhost_migration_log(MemoryListener *listener, bool enable) return r; } -static void vhost_log_global_start(MemoryListener *listener) +static bool vhost_log_global_start(MemoryListener *listener, Error **errp) { int r; @@ -1017,6 +1125,7 @@ static void vhost_log_global_start(MemoryListener *listener) if (r < 0) { abort(); } + return true; } static void vhost_log_global_stop(MemoryListener *listener) @@ -1258,10 +1367,10 @@ int vhost_virtqueue_start(struct vhost_dev *dev, return r; } -void vhost_virtqueue_stop(struct vhost_dev *dev, - struct VirtIODevice *vdev, - struct vhost_virtqueue *vq, - unsigned idx) +int vhost_virtqueue_stop(struct vhost_dev *dev, + struct VirtIODevice *vdev, + struct vhost_virtqueue *vq, + unsigned idx) { int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx); struct vhost_vring_state state = { @@ -1271,7 +1380,7 @@ void vhost_virtqueue_stop(struct vhost_dev *dev, if (virtio_queue_get_desc_addr(vdev, idx) == 0) { /* Don't stop the virtqueue which might have not been started */ - return; + return 0; } r = dev->vhost_ops->vhost_get_vring_base(dev, &state); @@ -1302,6 +1411,7 @@ void vhost_virtqueue_stop(struct vhost_dev *dev, 0, virtio_queue_get_avail_size(vdev, idx)); vhost_memory_unmap(dev, vq->desc, virtio_queue_get_desc_size(vdev, idx), 0, virtio_queue_get_desc_size(vdev, idx)); + return r; } static int vhost_virtqueue_set_busyloop_timeout(struct vhost_dev *dev, @@ -1400,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; @@ -1426,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) { @@ -1478,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; } } @@ -1495,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; } @@ -1530,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) { @@ -1544,9 +1682,9 @@ void vhost_dev_cleanup(struct vhost_dev *hdev) memset(hdev, 0, sizeof(struct vhost_dev)); } -static void vhost_dev_disable_notifiers_nvqs(struct vhost_dev *hdev, - VirtIODevice *vdev, - unsigned int nvqs) +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; @@ -1792,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; @@ -1921,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; @@ -1974,7 +2062,8 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) 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, @@ -1984,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); @@ -2005,11 +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); @@ -2035,9 +2136,10 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) } /* Host notifiers must be enabled at this point. */ -void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) +int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) { int i; + int rc = 0; /* should only be called after backend is connected */ assert(hdev->vhost_ops); @@ -2056,10 +2158,10 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) vhost_dev_set_vring_enable(hdev, false); } for (i = 0; i < hdev->nvqs; ++i) { - vhost_virtqueue_stop(hdev, - vdev, - hdev->vqs + i, - hdev->vq_index + i); + rc |= vhost_virtqueue_stop(hdev, + vdev, + hdev->vqs + i, + hdev->vq_index + i); } if (hdev->vhost_ops->vhost_reset_status) { hdev->vhost_ops->vhost_reset_status(hdev); @@ -2076,6 +2178,7 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) hdev->started = false; vdev->vhost_started = false; hdev->vdev = NULL; + return rc; } int vhost_net_set_backend(struct vhost_dev *hdev, @@ -2087,3 +2190,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-9p-pci.c b/hw/virtio/virtio-9p-pci.c index 94c14f0b98c..594742ff656 100644 --- a/hw/virtio/virtio-9p-pci.c +++ b/hw/virtio/virtio-9p-pci.c @@ -43,14 +43,13 @@ static void virtio_9p_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static Property virtio_9p_pci_properties[] = { +static const Property virtio_9p_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_9p_pci_class_init(ObjectClass *klass, void *data) +static void virtio_9p_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); diff --git a/hw/virtio/virtio-acpi.c b/hw/virtio/virtio-acpi.c new file mode 100644 index 00000000000..85becef03c0 --- /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-pci.c b/hw/virtio/virtio-balloon-pci.c index ce2645ba718..96e88b6b86d 100644 --- a/hw/virtio/virtio-balloon-pci.c +++ b/hw/virtio/virtio-balloon-pci.c @@ -35,16 +35,27 @@ struct VirtIOBalloonPCI { VirtIOBalloon vdev; }; +static const Property virtio_balloon_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), +}; + static void virtio_balloon_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(vpci_dev); DeviceState *vdev = DEVICE(&dev->vdev); + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = 2; + } + vpci_dev->class_code = PCI_CLASS_OTHERS; qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data) +static void virtio_balloon_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); @@ -55,6 +66,7 @@ static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data) pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BALLOON; pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; pcidev_k->class_id = PCI_CLASS_OTHERS; + device_class_set_props(dc, virtio_balloon_properties); } static void virtio_balloon_pci_instance_init(Object *obj) diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index d004cf29d26..db787d00b31 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -22,18 +22,16 @@ #include "hw/mem/pc-dimm.h" #include "hw/qdev-properties.h" #include "hw/boards.h" -#include "sysemu/balloon.h" +#include "system/balloon.h" #include "hw/virtio/virtio-balloon.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qapi/error.h" #include "qapi/qapi-events-machine.h" #include "qapi/visitor.h" #include "trace.h" #include "qemu/error-report.h" #include "migration/misc.h" -#include "migration/migration.h" -#include "migration/options.h" - +#include "system/reset.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" @@ -169,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 @@ -633,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); @@ -817,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() @@ -829,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() } @@ -840,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 @@ -897,6 +910,8 @@ static void virtio_balloon_device_realize(DeviceState *dev, Error **errp) } reset_stats(s); + s->stats_last_update = 0; + qemu_register_resettable(OBJECT(dev)); } static void virtio_balloon_device_unrealize(DeviceState *dev) @@ -904,6 +919,7 @@ static void virtio_balloon_device_unrealize(DeviceState *dev) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOBalloon *s = VIRTIO_BALLOON(dev); + qemu_unregister_resettable(OBJECT(dev)); if (s->free_page_bh) { qemu_bh_delete(s->free_page_bh); object_unref(OBJECT(s->iothread)); @@ -942,7 +958,7 @@ static void virtio_balloon_device_reset(VirtIODevice *vdev) s->poison_val = 0; } -static void virtio_balloon_set_status(VirtIODevice *vdev, uint8_t status) +static int virtio_balloon_set_status(VirtIODevice *vdev, uint8_t status) { VirtIOBalloon *s = VIRTIO_BALLOON(vdev); @@ -972,6 +988,28 @@ static void virtio_balloon_set_status(VirtIODevice *vdev, uint8_t status) qemu_mutex_unlock(&s->free_page_lock); } } + return 0; +} + +static ResettableState *virtio_balloon_get_reset_state(Object *obj) +{ + VirtIOBalloon *s = VIRTIO_BALLOON(obj); + return &s->reset_state; +} + +static void virtio_balloon_reset_enter(Object *obj, ResetType type) +{ + VirtIOBalloon *s = VIRTIO_BALLOON(obj); + + /* + * When waking up from standby/suspend-to-ram, do not reset stats. + */ + if (type == RESET_TYPE_WAKEUP) { + return; + } + + reset_stats(s); + s->stats_last_update = 0; } static void virtio_balloon_instance_init(Object *obj) @@ -996,13 +1034,13 @@ 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() }, }; -static Property virtio_balloon_properties[] = { +static const Property virtio_balloon_properties[] = { DEFINE_PROP_BIT("deflate-on-oom", VirtIOBalloon, host_features, VIRTIO_BALLOON_F_DEFLATE_ON_OOM, false), DEFINE_PROP_BIT("free-page-hint", VirtIOBalloon, host_features, @@ -1019,13 +1057,13 @@ static Property virtio_balloon_properties[] = { qemu_4_0_config_size, false), DEFINE_PROP_LINK("iothread", VirtIOBalloon, iothread, TYPE_IOTHREAD, IOThread *), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_balloon_class_init(ObjectClass *klass, void *data) +static void virtio_balloon_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); device_class_set_props(dc, virtio_balloon_properties); dc->vmsd = &vmstate_virtio_balloon; @@ -1038,6 +1076,9 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data) vdc->get_features = virtio_balloon_get_features; vdc->set_status = virtio_balloon_set_status; vdc->vmsd = &vmstate_virtio_balloon_device; + + rc->get_state = virtio_balloon_get_reset_state; + rc->phases.enter = virtio_balloon_reset_enter; } static const TypeInfo virtio_balloon_info = { diff --git a/hw/virtio/virtio-blk-pci.c b/hw/virtio/virtio-blk-pci.c index 9743bee965a..fd33bbd7e83 100644 --- a/hw/virtio/virtio-blk-pci.c +++ b/hw/virtio/virtio-blk-pci.c @@ -38,13 +38,12 @@ struct VirtIOBlkPCI { VirtIOBlock vdev; }; -static Property virtio_blk_pci_properties[] = { +static const Property virtio_blk_pci_properties[] = { DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -64,7 +63,7 @@ static void virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void virtio_blk_pci_class_init(ObjectClass *klass, void *data) +static void virtio_blk_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index 896feb37a1c..11adfbf3aba 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -28,7 +28,7 @@ #include "qapi/error.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" /* #define DEBUG_VIRTIO_BUS */ @@ -348,7 +348,7 @@ bool virtio_bus_device_iommu_enabled(VirtIODevice *vdev) return klass->iommu_enabled(qbus->parent); } -static void virtio_bus_class_init(ObjectClass *klass, void *data) +static void virtio_bus_class_init(ObjectClass *klass, const void *data) { BusClass *bus_class = BUS_CLASS(klass); bus_class->get_dev_path = virtio_bus_get_dev_path; diff --git a/hw/virtio/virtio-crypto-pci.c b/hw/virtio/virtio-crypto-pci.c index 0783dc2f7ed..868abc03a9e 100644 --- a/hw/virtio/virtio-crypto-pci.c +++ b/hw/virtio/virtio-crypto-pci.c @@ -37,11 +37,10 @@ struct VirtIOCryptoPCI { VirtIOCrypto vdev; }; -static Property virtio_crypto_pci_properties[] = { +static const Property virtio_crypto_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_crypto_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -60,7 +59,7 @@ static void virtio_crypto_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) } } -static void virtio_crypto_pci_class_init(ObjectClass *klass, void *data) +static void virtio_crypto_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index 13aec771e11..517f2089c5a 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -22,7 +22,7 @@ #include "hw/virtio/virtio-crypto.h" #include "hw/qdev-properties.h" #include "standard-headers/linux/virtio_ids.h" -#include "sysemu/cryptodev-vhost.h" +#include "system/cryptodev-vhost.h" #define VIRTIO_CRYPTO_VM_VERSION 1 @@ -205,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); @@ -224,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; @@ -461,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 = QCRYPTODEV_BACKEND_ALG__MAX; + req->flags = QCRYPTODEV_BACKEND_ALGO_TYPE__MAX; memset(&req->op_info, 0x00, sizeof(req->op_info)); } @@ -471,7 +471,7 @@ static void virtio_crypto_free_request(VirtIOCryptoReq *req) return; } - if (req->flags == QCRYPTODEV_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; @@ -486,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 == QCRYPTODEV_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); @@ -571,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 == QCRYPTODEV_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 == QCRYPTODEV_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); } @@ -655,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; @@ -884,7 +884,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request) switch (opcode) { case VIRTIO_CRYPTO_CIPHER_ENCRYPT: case VIRTIO_CRYPTO_CIPHER_DECRYPT: - op_info->algtype = request->flags = QCRYPTODEV_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); @@ -894,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 = QCRYPTODEV_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); @@ -1008,19 +1008,19 @@ static uint32_t virtio_crypto_init_services(uint32_t qservices) { uint32_t vservices = 0; - if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_CIPHER)) { + if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_CIPHER)) { vservices |= (1 << VIRTIO_CRYPTO_SERVICE_CIPHER); } - if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_HASH)) { + if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_HASH)) { vservices |= (1 << VIRTIO_CRYPTO_SERVICE_HASH); } - if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_MAC)) { + if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_MAC)) { vservices |= (1 << VIRTIO_CRYPTO_SERVICE_MAC); } - if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_AEAD)) { + if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_AEAD)) { vservices |= (1 << VIRTIO_CRYPTO_SERVICE_AEAD); } - if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_AKCIPHER)) { + if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_AKCIPHER)) { vservices |= (1 << VIRTIO_CRYPTO_SERVICE_AKCIPHER); } @@ -1080,8 +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_guarded(virtio_crypto_dataq_bh, &vcrypto->vqs[i], - &dev->mem_reentrancy_guard); + virtio_bh_new_guarded(dev, virtio_crypto_dataq_bh, + &vcrypto->vqs[i]); vcrypto->vqs[i].vcrypto = vcrypto; } @@ -1122,16 +1122,15 @@ 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() }, }; -static Property virtio_crypto_properties[] = { +static const Property virtio_crypto_properties[] = { DEFINE_PROP_LINK("cryptodev", VirtIOCrypto, conf.cryptodev, TYPE_CRYPTODEV_BACKEND, CryptoDevBackend *), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_crypto_get_config(VirtIODevice *vdev, uint8_t *config) @@ -1198,11 +1197,12 @@ static void virtio_crypto_vhost_status(VirtIOCrypto *c, uint8_t status) } } -static void virtio_crypto_set_status(VirtIODevice *vdev, uint8_t status) +static int virtio_crypto_set_status(VirtIODevice *vdev, uint8_t status) { VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); virtio_crypto_vhost_status(vcrypto, status); + return 0; } static void virtio_crypto_guest_notifier_mask(VirtIODevice *vdev, int idx, @@ -1247,13 +1247,25 @@ static bool virtio_crypto_guest_notifier_pending(VirtIODevice *vdev, int 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; } -static void virtio_crypto_class_init(ObjectClass *klass, void *data) +static void virtio_crypto_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); @@ -1278,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 index 477c97dea21..7d8677bcf0d 100644 --- a/hw/virtio/virtio-hmp-cmds.c +++ b/hw/virtio/virtio-hmp-cmds.c @@ -9,7 +9,7 @@ #include "monitor/hmp.h" #include "monitor/monitor.h" #include "qapi/qapi-commands-virtio.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" static void hmp_virtio_dump_protocols(Monitor *mon, diff --git a/hw/virtio/virtio-input-pci.c b/hw/virtio/virtio-input-pci.c index a53edf46c43..3be5358b4cd 100644 --- a/hw/virtio/virtio-input-pci.c +++ b/hw/virtio/virtio-input-pci.c @@ -37,9 +37,8 @@ struct VirtIOInputHIDPCI { VirtIOInputHID vdev; }; -static Property virtio_input_pci_properties[] = { +static const Property virtio_input_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -51,7 +50,7 @@ static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void virtio_input_pci_class_init(ObjectClass *klass, void *data) +static void virtio_input_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); @@ -64,7 +63,8 @@ static void virtio_input_pci_class_init(ObjectClass *klass, void *data) pcidev_k->class_id = PCI_CLASS_INPUT_OTHER; } -static void virtio_input_hid_kbd_pci_class_init(ObjectClass *klass, void *data) +static void virtio_input_hid_kbd_pci_class_init(ObjectClass *klass, + const void *data) { PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); @@ -72,7 +72,7 @@ static void virtio_input_hid_kbd_pci_class_init(ObjectClass *klass, void *data) } static void virtio_input_hid_mouse_pci_class_init(ObjectClass *klass, - void *data) + const void *data) { PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c index 7ef2f9dcdba..8123c6f83a3 100644 --- a/hw/virtio/virtio-iommu-pci.c +++ b/hw/virtio/virtio-iommu-pci.c @@ -34,12 +34,11 @@ struct VirtIOIOMMUPCI { VirtIOIOMMU vdev; }; -static Property virtio_iommu_pci_properties[] = { +static const 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(), }; static void virtio_iommu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -54,9 +53,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; @@ -74,7 +73,7 @@ static void virtio_iommu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void virtio_iommu_pci_class_init(ObjectClass *klass, void *data) +static void virtio_iommu_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); @@ -95,10 +94,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 be51635895c..3500f1b0820 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -20,12 +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 "system/kvm.h" +#include "system/reset.h" +#include "system/system.h" +#include "qemu/reserved-region.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "trace.h" @@ -65,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); @@ -299,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); @@ -378,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) { @@ -408,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 @@ -444,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) { @@ -522,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; } @@ -624,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); @@ -666,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; } @@ -754,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; } @@ -790,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); @@ -856,7 +1149,7 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, bool bypass_allowed; int granule; bool found; - int i; + GList *l; interval.low = addr; interval.high = addr + 1; @@ -894,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; @@ -1087,56 +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 %s reports a page size mask 0x%"PRIx64 - " incompatible with currently supported mask 0x%"PRIx64, - mr->parent_obj.name, new_mask, cur_mask); - return -1; - } - - /* - * 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 (s->granule_frozen) { - int cur_granule = ctz64(cur_mask); - - if (!(BIT_ULL(cur_granule) & new_mask)) { - error_setg(errp, "virtio-iommu %s does not support frozen granule 0x%llx", - mr->parent_obj.name, BIT_ULL(cur_granule)); - return -1; - } - 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 @@ -1151,18 +1402,6 @@ static void virtio_iommu_freeze_granule(Notifier *notifier, void *data) VirtIOIOMMU *s = container_of(notifier, VirtIOIOMMU, machine_done); int granule; - if (likely(s->config.bypass)) { - /* - * Transient IOMMU MR enable to collect page_size_mask requirements - * through memory_region_iommu_set_page_size_mask() called by - * VFIO region_add() callback - */ - s->config.bypass = false; - virtio_iommu_switch_address_space_all(s); - /* restore default */ - s->config.bypass = true; - virtio_iommu_switch_address_space_all(s); - } s->granule_frozen = true; granule = ctz64(s->config.page_size_mask); trace_virtio_iommu_freeze_granule(BIT_ULL(granule)); @@ -1175,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); @@ -1186,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 = qemu_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; @@ -1205,8 +1466,11 @@ 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!"); } @@ -1240,11 +1504,11 @@ static void virtio_iommu_device_unrealize(DeviceState *dev) virtio_cleanup(vdev); } -static void virtio_iommu_device_reset(VirtIODevice *vdev) +static void virtio_iommu_device_reset_exit(Object *obj, ResetType type) { - VirtIOIOMMU *s = VIRTIO_IOMMU(vdev); + VirtIOIOMMU *s = VIRTIO_IOMMU(obj); - trace_virtio_iommu_device_reset(); + trace_virtio_iommu_device_reset_exit(); if (s->domains) { g_tree_destroy(s->domains); @@ -1258,9 +1522,10 @@ static void virtio_iommu_device_reset(VirtIODevice *vdev) NULL, NULL, virtio_iommu_put_endpoint); } -static void virtio_iommu_set_status(VirtIODevice *vdev, uint8_t status) +static int virtio_iommu_set_status(VirtIODevice *vdev, uint8_t status) { trace_virtio_iommu_device_status(status); + return 0; } static void virtio_iommu_instance_init(Object *obj) @@ -1272,7 +1537,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() \ @@ -1284,7 +1549,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() \ @@ -1309,7 +1574,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() } @@ -1320,7 +1585,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, @@ -1372,7 +1637,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), @@ -1385,23 +1650,26 @@ 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[] = { +static const Property virtio_iommu_properties[] = { DEFINE_PROP_LINK("primary-bus", VirtIOIOMMU, primary_bus, TYPE_PCI_BUS, PCIBus *), DEFINE_PROP_BOOL("boot-bypass", VirtIOIOMMU, boot_bypass, true), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_GRANULE_MODE("granule", VirtIOIOMMU, granule_mode, + GRANULE_MODE_HOST), + DEFINE_PROP_UINT8("aw-bits", VirtIOIOMMU, aw_bits, 64), }; -static void virtio_iommu_class_init(ObjectClass *klass, void *data) +static void virtio_iommu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); device_class_set_props(dc, virtio_iommu_properties); dc->vmsd = &vmstate_virtio_iommu; @@ -1409,7 +1677,12 @@ static void virtio_iommu_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); vdc->realize = virtio_iommu_device_realize; vdc->unrealize = virtio_iommu_device_unrealize; - vdc->reset = virtio_iommu_device_reset; + + /* + * Use 'exit' reset phase to make sure all DMA requests + * have been quiesced during 'enter' or 'hold' phase + */ + rc->phases.exit = virtio_iommu_device_reset_exit; vdc->get_config = virtio_iommu_get_config; vdc->set_config = virtio_iommu_set_config; vdc->get_features = virtio_iommu_get_features; @@ -1418,14 +1691,13 @@ static void virtio_iommu_class_init(ObjectClass *klass, void *data) } static void virtio_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(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 index 62bfb7920bf..9278b32cf84 100644 --- a/hw/virtio/virtio-md-pci.c +++ b/hw/virtio/virtio-md-pci.c @@ -37,7 +37,7 @@ void virtio_md_pci_pre_plug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp) * First, see if we can plug this memory device at all. If that * succeeds, branch of to the actual hotplug handler. */ - memory_device_pre_plug(md, ms, NULL, &local_err); + memory_device_pre_plug(md, ms, &local_err); if (!local_err && bus_handler) { hotplug_handler_pre_plug(bus_handler, dev, &local_err); } @@ -138,7 +138,7 @@ static const TypeInfo virtio_md_pci_info = { .instance_size = sizeof(VirtIOMDPCI), .class_size = sizeof(VirtIOMDPCIClass), .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_MEMORY_DEVICE }, { } }, diff --git a/stubs/virtio-md-pci.c b/hw/virtio/virtio-md-stubs.c similarity index 100% rename from stubs/virtio-md-pci.c rename to hw/virtio/virtio-md-stubs.c diff --git a/hw/virtio/virtio-mem-pci.c b/hw/virtio/virtio-mem-pci.c index c4597e029ea..f592eb1a784 100644 --- a/hw/virtio/virtio-mem-pci.c +++ b/hw/virtio/virtio-mem-pci.c @@ -22,6 +22,10 @@ static void virtio_mem_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) VirtIOMEMPCI *mem_pci = VIRTIO_MEM_PCI(vpci_dev); DeviceState *vdev = DEVICE(&mem_pci->vdev); + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = 2; + } + virtio_pci_force_virtio_1(vpci_dev); qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } @@ -48,6 +52,25 @@ static MemoryRegion *virtio_mem_pci_get_memory_region(MemoryDeviceState *md, 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) { @@ -133,7 +156,14 @@ static void virtio_mem_pci_set_requested_size(Object *obj, Visitor *v, object_property_set(OBJECT(&pci_mem->vdev), name, v, errp); } -static void virtio_mem_pci_class_init(ObjectClass *klass, void *data) +static const Property virtio_mem_pci_class_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), +}; + +static void virtio_mem_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); @@ -145,11 +175,14 @@ static void virtio_mem_pci_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; pcidev_k->class_id = PCI_CLASS_OTHERS; + device_class_set_props(dc, virtio_mem_pci_class_properties); mdc->get_addr = virtio_mem_pci_get_addr; 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; diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index b6e781741e6..c46f6f9c3e2 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -15,20 +15,20 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/units.h" -#include "sysemu/numa.h" -#include "sysemu/sysemu.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/numa.h" +#include "system/system.h" +#include "system/reset.h" +#include "system/runstate.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-mem.h" #include "qapi/error.h" #include "qapi/visitor.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "migration/misc.h" #include "hw/boards.h" #include "hw/qdev-properties.h" -#include CONFIG_DEVICES +#include "hw/acpi/acpi.h" #include "trace.h" static const VMStateDescription vmstate_virtio_mem_device_early; @@ -61,11 +61,20 @@ static uint32_t virtio_mem_default_thp_size(void) } else if (qemu_real_host_page_size() == 64 * KiB) { default_thp_size = 512 * MiB; } +#elif defined(__s390x__) + default_thp_size = 1 * MiB; #endif 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 @@ -81,6 +90,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; @@ -91,6 +101,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. @@ -154,7 +170,7 @@ static bool virtio_mem_has_shared_zeropage(RAMBlock *rb) * necessary (as the section size can change). But it's more likely that the * section size will rather get smaller and not bigger over time. */ -#if defined(TARGET_X86_64) || defined(TARGET_I386) +#if defined(TARGET_X86_64) || defined(TARGET_I386) || defined(TARGET_S390X) #define VIRTIO_MEM_USABLE_EXTENT (2 * (128 * MiB)) #elif defined(TARGET_ARM) #define VIRTIO_MEM_USABLE_EXTENT (2 * (512 * MiB)) @@ -174,13 +190,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; @@ -204,7 +220,7 @@ static int virtio_mem_for_each_unplugged_range(const VirtIOMEM *vmem, void *arg, return ret; } -static int virtio_mem_for_each_plugged_range(const VirtIOMEM *vmem, void *arg, +static int virtio_mem_for_each_plugged_range(VirtIOMEM *vmem, void *arg, virtio_mem_range_cb cb) { unsigned long first_bit, last_bit; @@ -228,28 +244,6 @@ static int virtio_mem_for_each_plugged_range(const VirtIOMEM *vmem, void *arg, 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 virtio_mem_intersect_memory_section(MemoryRegionSection *s, - uint64_t offset, uint64_t size) -{ - uint64_t start = MAX(s->offset_within_region, offset); - uint64_t end = MIN(s->offset_within_region + int128_get64(s->size), - offset + size); - - if (end <= start) { - return false; - } - - s->offset_within_address_space += start - s->offset_within_region; - s->offset_within_region = start; - s->size = int128_make64(end - start); - return true; -} - typedef int (*virtio_mem_section_cb)(MemoryRegionSection *s, void *arg); static int virtio_mem_for_each_plugged_section(const VirtIOMEM *vmem, @@ -271,7 +265,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 (!virtio_mem_intersect_memory_section(&tmp, offset, size)) { + if (!memory_region_section_intersect_range(&tmp, offset, size)) { break; } ret = cb(&tmp, arg); @@ -303,7 +297,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 (!virtio_mem_intersect_memory_section(&tmp, offset, size)) { + if (!memory_region_section_intersect_range(&tmp, offset, size)) { break; } ret = cb(&tmp, arg); @@ -339,7 +333,7 @@ static void virtio_mem_notify_unplug(VirtIOMEM *vmem, uint64_t offset, QLIST_FOREACH(rdl, &vmem->rdl_list, next) { MemoryRegionSection tmp = *rdl->section; - if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) { + if (!memory_region_section_intersect_range(&tmp, offset, size)) { continue; } rdl->notify_discard(rdl, &tmp); @@ -355,7 +349,7 @@ static int virtio_mem_notify_plug(VirtIOMEM *vmem, uint64_t offset, QLIST_FOREACH(rdl, &vmem->rdl_list, next) { MemoryRegionSection tmp = *rdl->section; - if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) { + if (!memory_region_section_intersect_range(&tmp, offset, size)) { continue; } ret = rdl->notify_populate(rdl, &tmp); @@ -372,7 +366,7 @@ static int virtio_mem_notify_plug(VirtIOMEM *vmem, uint64_t offset, if (rdl2 == rdl) { break; } - if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) { + if (!memory_region_section_intersect_range(&tmp, offset, size)) { continue; } rdl2->notify_discard(rdl2, &tmp); @@ -483,6 +477,92 @@ 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) { @@ -500,6 +580,10 @@ static int virtio_mem_set_block_state(VirtIOMEM *vmem, uint64_t start_gpa, } virtio_mem_notify_unplug(vmem, offset, size); 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; } @@ -508,8 +592,7 @@ static int virtio_mem_set_block_state(VirtIOMEM *vmem, uint64_t start_gpa, 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) { + if (!qemu_prealloc_mem(fd, area, size, 1, NULL, false, &local_err)) { static bool warned; /* @@ -527,7 +610,22 @@ static int virtio_mem_set_block_state(VirtIOMEM *vmem, uint64_t start_gpa, } 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. */ @@ -620,6 +718,7 @@ 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 (vmem->size) { @@ -634,6 +733,11 @@ static int virtio_mem_unplug_all(VirtIOMEM *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(); @@ -757,15 +861,16 @@ static uint64_t virtio_mem_get_features(VirtIODevice *vdev, uint64_t features, MachineState *ms = MACHINE(qdev_get_machine()); VirtIOMEM *vmem = VIRTIO_MEM(vdev); - if (ms->numa_state) { -#if defined(CONFIG_ACPI) + if (ms->numa_state && acpi_builtin()) { virtio_add_feature(&features, VIRTIO_MEM_F_ACPI_PXM); -#endif } assert(vmem->unplugged_inaccessible != ON_OFF_AUTO_AUTO); 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; } @@ -778,16 +883,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) @@ -798,6 +934,7 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) VirtIOMEM *vmem = VIRTIO_MEM(dev); uint64_t page_size; RAMBlock *rb; + Object *obj; int ret; if (!vmem->memdev) { @@ -830,7 +967,7 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) return; } - if (enable_mlock) { + if (should_mlock(mlock_state)) { error_setg(errp, "Incompatible with mlock"); return; } @@ -861,6 +998,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 @@ -902,6 +1047,17 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) return; } + /* + * Set ourselves as RamDiscardManager before the plug handler maps the + * memory region and exposes it via an address space. + */ + if (memory_region_set_ram_discard_manager(&vmem->memdev->mr, + RAM_DISCARD_MANAGER(vmem))) { + error_setg(errp, "Failed to set RamDiscardManager"); + 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 @@ -916,6 +1072,7 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) ret = ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb)); if (ret) { error_setg_errno(errp, -ret, "Unexpected error discarding RAM"); + memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL); ram_block_coordinated_discard_require(false); return; } @@ -930,20 +1087,53 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) virtio_init(vdev, VIRTIO_ID_MEM, sizeof(struct virtio_mem_config)); vmem->vq = virtio_add_queue(vdev, 128, virtio_mem_handle_request); + /* + * With "dynamic-memslots=off" (old behavior) we always map the whole + * RAM memory region directly. + */ + if (vmem->dynamic_memslots) { + if (!vmem->mr) { + virtio_mem_prepare_mr(vmem); + } + if (vmem->nb_memslots <= 1) { + vmem->nb_memslots = 1; + vmem->memslot_size = memory_region_size(&vmem->memdev->mr); + } + if (!vmem->memslots) { + virtio_mem_prepare_memslots(vmem); + } + } else { + assert(!vmem->mr && !vmem->nb_memslots && !vmem->memslots); + } + host_memory_backend_set_mapped(vmem->memdev, true); vmstate_register_ram(&vmem->memdev->mr, DEVICE(vmem)); if (vmem->early_migration) { - vmstate_register(VMSTATE_IF(vmem), VMSTATE_INSTANCE_ID_ANY, - &vmstate_virtio_mem_device_early, vmem); + vmstate_register_any(VMSTATE_IF(vmem), + &vmstate_virtio_mem_device_early, vmem); } - qemu_register_reset(virtio_mem_system_reset, vmem); /* - * Set ourselves as RamDiscardManager before the plug handler maps the - * memory region and exposes it via an address space. + * We only want to unplug all memory to start with a clean slate when + * it is safe for the guest -- during system resets that call + * qemu_devices_reset(). + * + * We'll filter out selected qemu_devices_reset() calls used for other + * purposes, like resetting all devices during wakeup from suspend on + * x86 based on the reset type passed to qemu_devices_reset(). + * + * Unplugging all memory during simple device resets can result in the VM + * unexpectedly losing RAM, corrupting VM state. + * + * Simple device resets (or resets triggered by getting a parent device + * reset) must not change the state of plugged memory blocks. Therefore, + * we need a dedicated reset object that only gets called during + * qemu_devices_reset(). */ - memory_region_set_ram_discard_manager(&vmem->memdev->mr, - RAM_DISCARD_MANAGER(vmem)); + obj = object_new(TYPE_VIRTIO_MEM_SYSTEM_RESET); + vmem->system_reset = VIRTIO_MEM_SYSTEM_RESET(obj); + vmem->system_reset->vmem = vmem; + qemu_register_resettable(obj); } static void virtio_mem_device_unrealize(DeviceState *dev) @@ -951,12 +1141,9 @@ static void virtio_mem_device_unrealize(DeviceState *dev) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOMEM *vmem = VIRTIO_MEM(dev); - /* - * The unplug handler unmapped the memory region, it cannot be - * 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->system_reset)); + object_unref(OBJECT(vmem->system_reset)); + if (vmem->early_migration) { vmstate_unregister(VMSTATE_IF(vmem), &vmstate_virtio_mem_device_early, vmem); @@ -966,10 +1153,15 @@ static void virtio_mem_device_unrealize(DeviceState *dev) virtio_del_queue(vdev, 0); virtio_cleanup(vdev); g_free(vmem->bitmap); + /* + * The unplug handler unmapped the memory region, it cannot be + * found via an address space anymore. Unset ourselves. + */ + memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL); 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; @@ -984,12 +1176,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. @@ -1001,6 +1212,20 @@ 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, @@ -1021,15 +1246,14 @@ static int virtio_mem_post_load(void *opaque, int version_id) return virtio_mem_restore_unplugged(vmem); } -static int virtio_mem_prealloc_range_cb(const VirtIOMEM *vmem, void *arg, +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; - qemu_prealloc_mem(fd, area, size, 1, NULL, &local_err); - if (local_err) { + if (!qemu_prealloc_mem(fd, area, size, 1, NULL, false, &local_err)) { error_report_err(local_err); return -ENOMEM; } @@ -1043,7 +1267,7 @@ static int virtio_mem_post_load_early(void *opaque, int version_id) int ret; if (!vmem->prealloc) { - return 0; + goto post_load_bitmap; } /* @@ -1051,7 +1275,7 @@ static int virtio_mem_post_load_early(void *opaque, int version_id) * don't mess with preallocation and postcopy. */ if (migrate_ram_is_ignored(rb)) { - return 0; + goto post_load_bitmap; } /* @@ -1084,7 +1308,10 @@ static int virtio_mem_post_load_early(void *opaque, int version_id) return -EBUSY; } } - return 0; + +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 { @@ -1119,7 +1346,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) { @@ -1146,7 +1373,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), @@ -1169,7 +1396,7 @@ static const VMStateDescription vmstate_virtio_mem_device = { .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), @@ -1199,7 +1426,7 @@ static const VMStateDescription vmstate_virtio_mem_device_early = { .version_id = 1, .early_setup = true, .post_load = virtio_mem_post_load_early, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_WITH_TMP(VirtIOMEM, VirtIOMEMMigSanityChecks, vmstate_virtio_mem_sanity_checks), VMSTATE_UINT64(size, VirtIOMEM), @@ -1212,7 +1439,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() }, @@ -1235,11 +1462,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) { @@ -1377,7 +1672,22 @@ static void virtio_mem_instance_init(Object *obj) NULL, NULL); } -static Property virtio_mem_properties[] = { +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 const Property virtio_mem_properties[] = { DEFINE_PROP_UINT64(VIRTIO_MEM_ADDR_PROP, VirtIOMEM, addr, 0), DEFINE_PROP_UINT32(VIRTIO_MEM_NODE_PROP, VirtIOMEM, node, 0), DEFINE_PROP_BOOL(VIRTIO_MEM_PREALLOC_PROP, VirtIOMEM, prealloc, false), @@ -1389,7 +1699,8 @@ static Property virtio_mem_properties[] = { #endif DEFINE_PROP_BOOL(VIRTIO_MEM_EARLY_MIGRATION_PROP, VirtIOMEM, early_migration, true), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_BOOL(VIRTIO_MEM_DYNAMIC_MEMSLOTS_PROP, VirtIOMEM, + dynamic_memslots, false), }; static uint64_t virtio_mem_rdm_get_min_granularity(const RamDiscardManager *rdm, @@ -1421,7 +1732,7 @@ static bool virtio_mem_rdm_is_populated(const RamDiscardManager *rdm, } struct VirtIOMEMReplayData { - void *fn; + ReplayRamDiscardState fn; void *opaque; }; @@ -1429,12 +1740,12 @@ static int virtio_mem_rdm_replay_populated_cb(MemoryRegionSection *s, void *arg) { struct VirtIOMEMReplayData *data = arg; - return ((ReplayRamPopulate)data->fn)(s, data->opaque); + return data->fn(s, data->opaque); } static int virtio_mem_rdm_replay_populated(const RamDiscardManager *rdm, MemoryRegionSection *s, - ReplayRamPopulate replay_fn, + ReplayRamDiscardState replay_fn, void *opaque) { const VirtIOMEM *vmem = VIRTIO_MEM(rdm); @@ -1453,14 +1764,13 @@ static int virtio_mem_rdm_replay_discarded_cb(MemoryRegionSection *s, { struct VirtIOMEMReplayData *data = arg; - ((ReplayRamDiscard)data->fn)(s, data->opaque); - return 0; + return data->fn(s, data->opaque); } -static void virtio_mem_rdm_replay_discarded(const RamDiscardManager *rdm, - MemoryRegionSection *s, - ReplayRamDiscard replay_fn, - void *opaque) +static int virtio_mem_rdm_replay_discarded(const RamDiscardManager *rdm, + MemoryRegionSection *s, + ReplayRamDiscardState replay_fn, + void *opaque) { const VirtIOMEM *vmem = VIRTIO_MEM(rdm); struct VirtIOMEMReplayData data = { @@ -1469,8 +1779,8 @@ static void virtio_mem_rdm_replay_discarded(const RamDiscardManager *rdm, }; g_assert(s->mr == &vmem->memdev->mr); - virtio_mem_for_each_unplugged_section(vmem, s, &data, - virtio_mem_rdm_replay_discarded_cb); + return virtio_mem_for_each_unplugged_section(vmem, s, &data, + virtio_mem_rdm_replay_discarded_cb); } static void virtio_mem_rdm_register_listener(RamDiscardManager *rdm, @@ -1525,8 +1835,8 @@ static void virtio_mem_unplug_request_check(VirtIOMEM *vmem, Error **errp) } if (vmem->size) { - error_setg(errp, "virtio-mem device cannot get unplugged while" - " '" VIRTIO_MEM_SIZE_PROP "' != '0'"); + error_setg(errp, "virtio-mem device cannot get unplugged while some" + " of its memory is still plugged"); return; } if (vmem->requested_size) { @@ -1536,7 +1846,7 @@ static void virtio_mem_unplug_request_check(VirtIOMEM *vmem, Error **errp) } } -static void virtio_mem_class_init(ObjectClass *klass, void *data) +static void virtio_mem_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); @@ -1556,6 +1866,8 @@ 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; @@ -1573,9 +1885,10 @@ 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[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_RAM_DISCARD_MANAGER }, { } }, @@ -1587,3 +1900,49 @@ static void virtio_register_types(void) } type_init(virtio_register_types) + +OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(VirtioMemSystemReset, virtio_mem_system_reset, VIRTIO_MEM_SYSTEM_RESET, OBJECT, { TYPE_RESETTABLE_INTERFACE }, { }) + +static void virtio_mem_system_reset_init(Object *obj) +{ +} + +static void virtio_mem_system_reset_finalize(Object *obj) +{ +} + +static ResettableState *virtio_mem_system_reset_get_state(Object *obj) +{ + VirtioMemSystemReset *vmem_reset = VIRTIO_MEM_SYSTEM_RESET(obj); + + return &vmem_reset->reset_state; +} + +static void virtio_mem_system_reset_hold(Object *obj, ResetType type) +{ + VirtioMemSystemReset *vmem_reset = VIRTIO_MEM_SYSTEM_RESET(obj); + VirtIOMEM *vmem = vmem_reset->vmem; + + /* + * 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_system_reset_class_init(ObjectClass *klass, + const void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->get_state = virtio_mem_system_reset_get_state; + rc->phases.hold = virtio_mem_system_reset_hold; +} diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index c2c6d854750..532c67107ba 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -28,8 +28,8 @@ #include "migration/qemu-file-types.h" #include "qemu/host-utils.h" #include "qemu/module.h" -#include "sysemu/kvm.h" -#include "sysemu/replay.h" +#include "system/kvm.h" +#include "system/replay.h" #include "hw/virtio/virtio-mmio.h" #include "qemu/error-report.h" #include "qemu/log.h" @@ -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); @@ -407,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: @@ -565,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), @@ -579,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, @@ -592,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 } @@ -744,13 +751,12 @@ static void virtio_mmio_pre_plugged(DeviceState *d, Error **errp) /* virtio-mmio device */ -static Property virtio_mmio_properties[] = { +static const Property virtio_mmio_properties[] = { DEFINE_PROP_BOOL("format_transport_address", VirtIOMMIOProxy, format_transport_address, true), DEFINE_PROP_BOOL("force-legacy", VirtIOMMIOProxy, legacy, true), DEFINE_PROP_BIT("ioeventfd", VirtIOMMIOProxy, flags, VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_mmio_realizefn(DeviceState *d, Error **errp) @@ -761,10 +767,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; @@ -782,12 +784,12 @@ static void virtio_mmio_realizefn(DeviceState *d, Error **errp) sysbus_init_mmio(sbd, &proxy->iomem); } -static void virtio_mmio_class_init(ObjectClass *klass, void *data) +static void virtio_mmio_class_init(ObjectClass *klass, const 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); } @@ -853,7 +855,7 @@ static void virtio_mmio_vmstate_change(DeviceState *d, bool running) } } -static void virtio_mmio_bus_class_init(ObjectClass *klass, void *data) +static void virtio_mmio_bus_class_init(ObjectClass *klass, const void *data) { BusClass *bus_class = BUS_CLASS(klass); VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); diff --git a/hw/virtio/virtio-net-pci.c b/hw/virtio/virtio-net-pci.c index e03543a70a7..f857a84f11c 100644 --- a/hw/virtio/virtio-net-pci.c +++ b/hw/virtio/virtio-net-pci.c @@ -38,12 +38,11 @@ struct VirtIONetPCI { VirtIONet vdev; }; -static Property virtio_net_properties[] = { +static const Property virtio_net_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_net_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -64,7 +63,7 @@ static void virtio_net_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void virtio_net_pci_class_init(ObjectClass *klass, void *data) +static void virtio_net_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -75,6 +74,7 @@ static void virtio_net_pci_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_VIRTIO_NET; k->revision = VIRTIO_PCI_ABI_VERSION; k->class_id = PCI_CLASS_NETWORK_ETHERNET; + k->sriov_vf_user_creatable = true; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); device_class_set_props(dc, virtio_net_properties); vpciklass->realize = virtio_net_pci_realize; diff --git a/hw/virtio/virtio-nsm-pci.c b/hw/virtio/virtio-nsm-pci.c new file mode 100644 index 00000000000..ec243963e1e --- /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, const 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 00000000000..3bf5e7009a4 --- /dev/null +++ b/hw/virtio/virtio-nsm.c @@ -0,0 +1,1737 @@ +/* + * 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) || 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)) { + str = cbor_bytestring_handle(pair[i].value); + if (!str) { + goto cleanup; + } + nsm_req->data_len = cbor_bytestring_length(pair[i].value); + } else if (cbor_isa_string(pair[i].value)) { + str = cbor_string_handle(pair[i].value); + if (!str) { + goto cleanup; + } + nsm_req->data_len = cbor_string_length(pair[i].value); + } else { + goto cleanup; + } + /* + * 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, sz); + 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); + } +} + +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 const Property virtio_nsm_properties[] = { + DEFINE_PROP_STRING("module-id", VirtIONSM, module_id), +}; + +static void virtio_nsm_class_init(ObjectClass *klass, const 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 edbc0daa186..fba2372c93b 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -33,12 +33,12 @@ #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/loader.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/virtio/virtio-pci.h" #include "qemu/range.h" #include "hw/virtio/virtio-bus.h" #include "qapi/visitor.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "trace.h" #define VIRTIO_PCI_REGION_SIZE(dev) VIRTIO_PCI_CONFIG_OFF(msix_present(dev)) @@ -97,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), @@ -120,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), @@ -135,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 } @@ -146,9 +146,7 @@ static const VMStateDescription vmstate_virtio_pci = { static bool virtio_pci_has_extra_state(DeviceState *d) { - VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - - return proxy->flags & VIRTIO_PCI_FLAG_MIGRATE_EXTRA; + return true; } static void virtio_pci_save_extra_state(DeviceState *d, QEMUFile *f) @@ -332,7 +330,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; @@ -343,13 +340,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); @@ -361,13 +353,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); @@ -395,7 +382,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) { @@ -419,8 +406,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: @@ -620,8 +613,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; } } @@ -780,15 +777,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); } } } @@ -804,15 +801,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); } } @@ -871,6 +868,9 @@ static int virtio_pci_get_notifier(VirtIOPCIProxy *proxy, int queue_no, 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; @@ -903,7 +903,7 @@ static int kvm_virtio_pci_vector_use_one(VirtIOPCIProxy *proxy, int queue_no) } ret = kvm_virtio_pci_vq_vector_use(proxy, vector); if (ret < 0) { - goto undo; + return ret; } /* * If guest supports masking, set up irqfd now. @@ -913,25 +913,11 @@ static int kvm_virtio_pci_vector_use_one(VirtIOPCIProxy *proxy, int queue_no) ret = kvm_virtio_pci_irqfd_use(proxy, n, vector); if (ret < 0) { kvm_virtio_pci_vq_vector_release(proxy, vector); - goto undo; + return ret; } } return 0; -undo: - - vector = virtio_queue_vector(vdev, queue_no); - if (vector >= msix_nr_vectors_allocated(dev)) { - return ret; - } - if (vdev->use_guest_notifier_mask && k->guest_notifier_mask) { - ret = virtio_pci_get_notifier(proxy, queue_no, &n, &vector); - if (ret < 0) { - return ret; - } - kvm_virtio_pci_irqfd_release(proxy, n, vector); - } - return ret; } static int kvm_virtio_pci_vector_vq_use(VirtIOPCIProxy *proxy, int nvqs) { @@ -1227,7 +1213,12 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign, static bool virtio_pci_query_guest_notifiers(DeviceState *d) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - return msix_enabled(&proxy->pci_dev); + + if (msix_enabled(&proxy->pci_dev)) { + return true; + } else { + return pci_irq_disabled(&proxy->pci_dev); + } } static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign) @@ -1435,6 +1426,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) { @@ -1563,7 +1604,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)) { @@ -1603,7 +1645,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) { @@ -1922,7 +1964,8 @@ 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); + int16_t res; /* * Virtio capabilities present without @@ -2018,6 +2061,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); @@ -2068,6 +2113,18 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) pci_register_bar(&proxy->pci_dev, proxy->legacy_io_bar_idx, PCI_BASE_ADDRESS_SPACE_IO, &proxy->bar); } + + if (pci_is_vf(&proxy->pci_dev)) { + pcie_ari_init(&proxy->pci_dev, proxy->last_pcie_cap_offset); + proxy->last_pcie_cap_offset += PCI_ARI_SIZEOF; + } else { + res = pcie_sriov_pf_init_from_user_created_vfs( + &proxy->pci_dev, proxy->last_pcie_cap_offset, errp); + if (res > 0) { + proxy->last_pcie_cap_offset += res; + virtio_add_feature(&vdev->host_features, VIRTIO_F_SR_IOV); + } + } } static void virtio_pci_device_unplugged(DeviceState *d) @@ -2096,10 +2153,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; @@ -2145,6 +2198,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; } @@ -2159,19 +2215,16 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) if (pcie_port && pci_is_express(pci_dev)) { int pos; - uint16_t last_pcie_cap_offset = PCI_CONFIG_SPACE_SIZE; + proxy->last_pcie_cap_offset = PCI_CONFIG_SPACE_SIZE; pos = pcie_endpoint_cap_init(pci_dev, 0); assert(pos > 0); - pos = pci_add_capability(pci_dev, PCI_CAP_ID_PM, 0, - PCI_PM_SIZEOF, errp); + pos = pci_pm_init(pci_dev, 0, errp); if (pos < 0) { return; } - pci_dev->exp.pm_cap = pos; - /* * Indicates that this function complies with revision 1.2 of the * PCI Power Management Interface Specification. @@ -2179,9 +2232,9 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3); if (proxy->flags & VIRTIO_PCI_FLAG_AER) { - pcie_aer_init(pci_dev, PCI_ERR_VER, last_pcie_cap_offset, + pcie_aer_init(pci_dev, PCI_ERR_VER, proxy->last_pcie_cap_offset, PCI_ERR_SIZEOF, NULL); - last_pcie_cap_offset += PCI_ERR_SIZEOF; + proxy->last_pcie_cap_offset += PCI_ERR_SIZEOF; } if (proxy->flags & VIRTIO_PCI_FLAG_INIT_DEVERR) { @@ -2194,6 +2247,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, @@ -2201,9 +2259,9 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) } if (proxy->flags & VIRTIO_PCI_FLAG_ATS) { - pcie_ats_init(pci_dev, last_pcie_cap_offset, + pcie_ats_init(pci_dev, proxy->last_pcie_cap_offset, proxy->flags & VIRTIO_PCI_FLAG_ATS_PAGE_ALIGNED); - last_pcie_cap_offset += PCI_EXT_CAP_ATS_SIZEOF; + proxy->last_pcie_cap_offset += PCI_EXT_CAP_ATS_SIZEOF; } if (proxy->flags & VIRTIO_PCI_FLAG_INIT_FLR) { @@ -2229,12 +2287,18 @@ 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; + pcie_sriov_pf_exit(&proxy->pci_dev); 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) @@ -2256,30 +2320,54 @@ static void virtio_pci_reset(DeviceState *qdev) } } -static void virtio_pci_bus_reset_hold(Object *obj) +static bool virtio_pci_no_soft_reset(PCIDevice *dev) +{ + uint16_t pmcsr; + + if (!pci_is_express(dev) || !(dev->cap_present & QEMU_PCI_CAP_PM)) { + return false; + } + + pmcsr = pci_get_word(dev->config + dev->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->pm_cap + PCI_PM_CTRL, + PCI_PM_CTRL_STATE_MASK); + } } } -static Property virtio_pci_properties[] = { +static const Property virtio_pci_properties[] = { DEFINE_PROP_BIT("virtio-pci-bus-master-bug-migration", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, false), - DEFINE_PROP_BIT("migrate-extra", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT, true), DEFINE_PROP_BIT("modern-pio-notify", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT, false), - DEFINE_PROP_BIT("x-disable-pcie", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT, false), DEFINE_PROP_BIT("page-per-vq", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_PAGE_PER_VQ_BIT, false), DEFINE_PROP_BOOL("x-ignore-backend-features", VirtIOPCIProxy, @@ -2294,11 +2382,12 @@ 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, VIRTIO_PCI_FLAG_AER_BIT, false), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_pci_dc_realize(DeviceState *qdev, Error **errp) @@ -2307,15 +2396,22 @@ static void virtio_pci_dc_realize(DeviceState *qdev, Error **errp) VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev); PCIDevice *pci_dev = &proxy->pci_dev; - if (!(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_PCIE) && - virtio_pci_modern(proxy)) { + if (virtio_pci_modern(proxy)) { pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; } vpciklass->parent_dc_realize(qdev, errp); } -static void virtio_pci_class_init(ObjectClass *klass, void *data) +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, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -2331,6 +2427,7 @@ static void virtio_pci_class_init(ObjectClass *klass, void *data) device_class_set_parent_realize(dc, virtio_pci_dc_realize, &vpciklass->parent_dc_realize); rc->phases.hold = virtio_pci_bus_reset_hold; + dc->sync_config = virtio_pci_sync_config; } static const TypeInfo virtio_pci_info = { @@ -2342,14 +2439,13 @@ static const TypeInfo virtio_pci_info = { .abstract = true, }; -static Property virtio_pci_generic_properties[] = { +static const Property virtio_pci_generic_properties[] = { DEFINE_PROP_ON_OFF_AUTO("disable-legacy", VirtIOPCIProxy, disable_legacy, ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("disable-modern", VirtIOPCIProxy, disable_modern, false), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_pci_base_class_init(ObjectClass *klass, void *data) +static void virtio_pci_base_class_init(ObjectClass *klass, const void *data) { const VirtioPCIDeviceTypeInfo *t = data; if (t->class_init) { @@ -2357,7 +2453,7 @@ static void virtio_pci_base_class_init(ObjectClass *klass, void *data) } } -static void virtio_pci_generic_class_init(ObjectClass *klass, void *data) +static void virtio_pci_generic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -2388,6 +2484,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, @@ -2396,7 +2493,7 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t) .name = t->generic_name, .parent = base_type_info.name, .class_init = virtio_pci_generic_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } @@ -2412,18 +2509,18 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t) generic_type_info.parent = base_name; generic_type_info.class_init = virtio_pci_base_class_init; - generic_type_info.class_data = (void *)t; + generic_type_info.class_data = t; assert(!t->non_transitional_name); assert(!t->transitional_name); } else { base_type_info.class_init = virtio_pci_base_class_init; - base_type_info.class_data = (void *)t; + base_type_info.class_data = t; } - type_register(&base_type_info); + type_register_static(&base_type_info); if (generic_type_info.name) { - type_register(&generic_type_info); + type_register_static(&generic_type_info); } if (t->non_transitional_name) { @@ -2431,13 +2528,13 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t) .name = t->non_transitional_name, .parent = base_type_info.name, .instance_init = virtio_pci_non_transitional_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } }, }; - type_register(&non_transitional_type_info); + type_register_static(&non_transitional_type_info); } if (t->transitional_name) { @@ -2445,7 +2542,7 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t) .name = t->transitional_name, .parent = base_type_info.name, .instance_init = virtio_pci_transitional_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { /* * Transitional virtio devices work only as Conventional PCI * devices because they require PIO ports. @@ -2454,7 +2551,7 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t) { } }, }; - type_register(&transitional_type_info); + type_register_static(&transitional_type_info); } g_free(base_name); } @@ -2501,7 +2598,7 @@ static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size, qbus_init(bus, bus_size, TYPE_VIRTIO_PCI_BUS, qdev, virtio_bus_name); } -static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) +static void virtio_pci_bus_class_init(ObjectClass *klass, const void *data) { BusClass *bus_class = BUS_CLASS(klass); VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); diff --git a/hw/virtio/virtio-pmem-pci.c b/hw/virtio/virtio-pmem-pci.c index cfe7f3b67ca..babd91c21f0 100644 --- a/hw/virtio/virtio-pmem-pci.c +++ b/hw/virtio/virtio-pmem-pci.c @@ -80,7 +80,7 @@ static void virtio_pmem_pci_fill_device_info(const MemoryDeviceState *md, info->type = MEMORY_DEVICE_INFO_KIND_VIRTIO_PMEM; } -static void virtio_pmem_pci_class_init(ObjectClass *klass, void *data) +static void virtio_pmem_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio-pmem.c b/hw/virtio/virtio-pmem.c index c3512c2dae3..3416ea1827c 100644 --- a/hw/virtio/virtio-pmem.c +++ b/hw/virtio/virtio-pmem.c @@ -21,7 +21,7 @@ #include "hw/virtio/virtio-access.h" #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/virtio_pmem.h" -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #include "block/aio.h" #include "block/thread-pool.h" #include "trace.h" @@ -155,14 +155,13 @@ static MemoryRegion *virtio_pmem_get_memory_region(VirtIOPMEM *pmem, return &pmem->memdev->mr; } -static Property virtio_pmem_properties[] = { +static const Property virtio_pmem_properties[] = { DEFINE_PROP_UINT64(VIRTIO_PMEM_ADDR_PROP, VirtIOPMEM, start, 0), DEFINE_PROP_LINK(VIRTIO_PMEM_MEMDEV_PROP, VirtIOPMEM, memdev, TYPE_MEMORY_BACKEND, HostMemoryBackend *), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_pmem_class_init(ObjectClass *klass, void *data) +static void virtio_pmem_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c index 7515b0947bb..3b6377cf0d2 100644 --- a/hw/virtio/virtio-qmp.c +++ b/hw/virtio/virtio-qmp.c @@ -15,8 +15,9 @@ #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 "qobject/qobject.h" +#include "qobject/qjson.h" +#include "hw/virtio/vhost-user.h" #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/vhost_types.h" @@ -30,32 +31,13 @@ #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 } -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_MAX -}; - /* Virtio transport features mapping */ static const qmp_virtio_feature_map_t virtio_transport_map[] = { /* Virtio device transport features */ @@ -136,6 +118,15 @@ static const qmp_virtio_feature_map_t vhost_user_protocol_map[] = { 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"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_SHARED_OBJECT, \ + "VHOST_USER_PROTOCOL_F_SHARED_OBJECT: Backend shared object " + "supported"), + FEATURE_ENTRY(VHOST_USER_PROTOCOL_F_DEVICE_STATE, \ + "VHOST_USER_PROTOCOL_F_DEVICE_STATE: Backend device state transfer " + "supported"), { -1, "" } }; @@ -178,6 +169,8 @@ static const qmp_virtio_feature_map_t virtio_blk_feature_map[] = { "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 @@ -301,6 +294,14 @@ static const qmp_virtio_feature_map_t virtio_net_feature_map[] = { 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, \ @@ -455,6 +456,9 @@ static const qmp_virtio_feature_map_t virtio_mem_feature_map[] = { 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 @@ -471,6 +475,18 @@ static const qmp_virtio_feature_map_t virtio_rng_feature_map[] = { }; #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; \ @@ -627,6 +643,12 @@ VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap) 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: @@ -653,7 +675,6 @@ VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t bitmap) case VIRTIO_ID_DMABUF: case VIRTIO_ID_PARAM_SERV: case VIRTIO_ID_AUDIO_POLICY: - case VIRTIO_ID_GPIO: break; default: g_assert_not_reached(); @@ -667,70 +688,43 @@ VirtioDeviceFeatures *qmp_decode_features(uint16_t device_id, uint64_t 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 *list = NULL; - VirtioInfo *node; - VirtIODevice *vdev; + VirtioInfoList *vdevs = NULL; - 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_new(VirtioInfo, 1); - node->path = g_strdup(dev->canonical_path); - node->name = g_strdup(vdev->name); - QAPI_LIST_PREPEND(list, node); - } - g_string_free(is_realized, true); - } - qobject_unref(obj); + /* 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 list; + return vdevs; } VirtIODevice *qmp_find_virtio_device(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; + /* 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 NULL; + return VIRTIO_DEVICE(dev); } VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp) @@ -740,7 +734,7 @@ VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp) vdev = qmp_find_virtio_device(path); if (vdev == NULL) { - error_setg(errp, "Path %s is not a VirtIODevice", path); + error_setg(errp, "Path %s is not a realized VirtIODevice", path); return NULL; } diff --git a/hw/virtio/virtio-qmp.h b/hw/virtio/virtio-qmp.h index 8af5f5e65a1..245a446a567 100644 --- a/hw/virtio/virtio-qmp.h +++ b/hw/virtio/virtio-qmp.h @@ -15,13 +15,6 @@ #include "hw/virtio/virtio.h" #include "hw/virtio/vhost.h" -#include "qemu/queue.h" - -typedef QTAILQ_HEAD(QmpVirtIODeviceList, VirtIODevice) QmpVirtIODeviceList; - -/* QAPI list of realized VirtIODevices */ -extern QmpVirtIODeviceList virtio_list; - VirtIODevice *qmp_find_virtio_device(const char *path); VirtioDeviceStatus *qmp_decode_status(uint8_t bitmap); VhostDeviceProtocols *qmp_decode_protocols(uint64_t bitmap); diff --git a/hw/virtio/virtio-rng-pci.c b/hw/virtio/virtio-rng-pci.c index 6e76f8b57be..39b600356ea 100644 --- a/hw/virtio/virtio-rng-pci.c +++ b/hw/virtio/virtio-rng-pci.c @@ -32,12 +32,11 @@ struct VirtIORngPCI { VirtIORNG vdev; }; -static Property virtio_rng_properties[] = { +static const Property virtio_rng_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -54,7 +53,7 @@ static void virtio_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) } } -static void virtio_rng_pci_class_init(ObjectClass *klass, void *data) +static void virtio_rng_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 7e12fc03bfc..3df5d2576ec 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -17,8 +17,8 @@ #include "hw/virtio/virtio.h" #include "hw/qdev-properties.h" #include "hw/virtio/virtio-rng.h" -#include "sysemu/rng.h" -#include "sysemu/runstate.h" +#include "system/rng.h" +#include "system/runstate.h" #include "qom/object_interfaces.h" #include "trace.h" @@ -159,17 +159,18 @@ static void check_rate_limit(void *opaque) vrng->activate_timer = true; } -static void virtio_rng_set_status(VirtIODevice *vdev, uint8_t status) +static int virtio_rng_set_status(VirtIODevice *vdev, uint8_t status) { VirtIORNG *vrng = VIRTIO_RNG(vdev); if (!vdev->vm_running) { - return; + return 0; } vdev->status = status; /* Something changed, try to process buffers */ virtio_rng_process(vrng); + return 0; } static void virtio_rng_device_realize(DeviceState *dev, Error **errp) @@ -184,8 +185,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,13 +244,13 @@ 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() }, }; -static Property virtio_rng_properties[] = { +static const Property virtio_rng_properties[] = { /* Set a default rate limit of 2^47 bytes per minute or roughly 2TB/s. If * you have an entropy source capable of generating more entropy than this * and you can pass it through via virtio-rng, then hats off to you. Until @@ -257,10 +259,9 @@ static Property virtio_rng_properties[] = { DEFINE_PROP_UINT64("max-bytes", VirtIORNG, conf.max_bytes, INT64_MAX), DEFINE_PROP_UINT32("period", VirtIORNG, conf.period_ms, 1 << 16), DEFINE_PROP_LINK("rng", VirtIORNG, conf.rng, TYPE_RNG_BACKEND, RngBackend *), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_rng_class_init(ObjectClass *klass, void *data) +static void virtio_rng_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/virtio-scsi-pci.c b/hw/virtio/virtio-scsi-pci.c index e8e3442f382..af877592070 100644 --- a/hw/virtio/virtio-scsi-pci.c +++ b/hw/virtio/virtio-scsi-pci.c @@ -35,12 +35,11 @@ struct VirtIOSCSIPCI { VirtIOSCSI vdev; }; -static Property virtio_scsi_pci_properties[] = { +static const Property virtio_scsi_pci_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), }; static void virtio_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) @@ -73,7 +72,7 @@ static void virtio_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void virtio_scsi_pci_class_init(ObjectClass *klass, void *data) +static void virtio_scsi_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio-serial-pci.c b/hw/virtio/virtio-serial-pci.c index cea31adcc4c..3f212ffe52f 100644 --- a/hw/virtio/virtio-serial-pci.c +++ b/hw/virtio/virtio-serial-pci.c @@ -69,15 +69,14 @@ static void virtio_serial_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static Property virtio_serial_pci_properties[] = { +static const Property virtio_serial_pci_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), - DEFINE_PROP_END_OF_LIST(), }; -static void virtio_serial_pci_class_init(ObjectClass *klass, void *data) +static void virtio_serial_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 969c25f4cfc..82a285a31d1 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -15,10 +15,12 @@ #include "qapi/error.h" #include "qapi/qapi-commands-virtio.h" #include "trace.h" +#include "qemu/defer-call.h" #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/module.h" +#include "exec/tswap.h" #include "qom/object_interfaces.h" #include "hw/core/cpu.h" #include "hw/virtio/virtio.h" @@ -28,8 +30,8 @@ #include "hw/virtio/virtio-bus.h" #include "hw/qdev-properties.h" #include "hw/virtio/virtio-access.h" -#include "sysemu/dma.h" -#include "sysemu/runstate.h" +#include "system/dma.h" +#include "system/runstate.h" #include "virtio-qmp.h" #include "standard-headers/linux/virtio_ids.h" @@ -45,8 +47,6 @@ #include "standard-headers/linux/virtio_mem.h" #include "standard-headers/linux/virtio_vsock.h" -QmpVirtIODeviceList virtio_list; - /* * Maximum size of virtio device config space */ @@ -205,6 +205,15 @@ static const char *virtio_id_to_name(uint16_t device_id) return name; } +static void virtio_check_indirect_feature(VirtIODevice *vdev) +{ + if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Device %s: indirect_desc was not negotiated!\n", + vdev->name); + } +} + /* Called within call_rcu(). */ static void virtio_free_region_cache(VRingMemoryRegionCaches *caches) { @@ -323,7 +332,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, @@ -745,6 +753,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) { @@ -873,6 +935,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, @@ -923,7 +1025,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); @@ -958,12 +1062,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; @@ -974,6 +1086,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)) { @@ -981,7 +1160,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); @@ -999,7 +1180,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) { @@ -1007,8 +1193,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(); } @@ -1039,9 +1232,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)) { @@ -1049,16 +1243,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; } @@ -1071,10 +1261,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; @@ -1136,7 +1328,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) { @@ -1207,12 +1399,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; @@ -1311,9 +1505,9 @@ static void virtqueue_packed_get_avail_bytes(VirtQueue *vq, 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; @@ -1346,7 +1540,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; @@ -1354,6 +1548,8 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, if (out_bytes) { *out_bytes = 0; } + + return -1; } int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, @@ -1485,19 +1681,21 @@ 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; VirtQueueElement *elem = NULL; unsigned out_num, in_num, elem_entries; - hwaddr addr[VIRTQUEUE_MAX_SIZE]; - struct iovec iov[VIRTQUEUE_MAX_SIZE]; + hwaddr QEMU_UNINITIALIZED addr[VIRTQUEUE_MAX_SIZE]; + struct iovec QEMU_UNINITIALIZED iov[VIRTQUEUE_MAX_SIZE]; VRingDesc desc; int rc; + address_space_cache_init_empty(&indirect_desc_cache); + RCU_READ_LOCK_GUARD(); if (virtio_queue_empty_rcu(vq)) { goto done; @@ -1544,6 +1742,7 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz) virtio_error(vdev, "Invalid size for indirect buffer table"); goto done; } + virtio_check_indirect_feature(vdev); /* loop over the indirect descriptor table */ len = address_space_cache_init(&indirect_desc_cache, vdev->dma_as, @@ -1587,7 +1786,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) { @@ -1607,6 +1806,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); @@ -1624,18 +1830,20 @@ 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; VirtQueueElement *elem = NULL; unsigned out_num, in_num, elem_entries; - hwaddr addr[VIRTQUEUE_MAX_SIZE]; - struct iovec iov[VIRTQUEUE_MAX_SIZE]; + hwaddr QEMU_UNINITIALIZED addr[VIRTQUEUE_MAX_SIZE]; + struct iovec QEMU_UNINITIALIZED iov[VIRTQUEUE_MAX_SIZE]; VRingPackedDesc desc; 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; @@ -1672,6 +1880,7 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz) virtio_error(vdev, "Invalid size for indirect buffer table"); goto done; } + virtio_check_indirect_feature(vdev); /* loop over the indirect descriptor table */ len = address_space_cache_init(&indirect_desc_cache, vdev->dma_as, @@ -1720,6 +1929,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++) { @@ -1733,6 +1947,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; @@ -2011,12 +2232,12 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val) { VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); trace_virtio_set_status(vdev, val); + int ret = 0; if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { if (!(vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) && val & VIRTIO_CONFIG_S_FEATURES_OK) { - int ret = virtio_validate_features(vdev); - + ret = virtio_validate_features(vdev); if (ret) { return ret; } @@ -2029,16 +2250,20 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val) } if (k->set_status) { - k->set_status(vdev, val); + ret = k->set_status(vdev, val); + if (ret) { + qemu_log("set %s status to %d failed, old status: %d\n", + vdev->name, val, vdev->status); + } } vdev->status = val; - return 0; + return ret; } static enum virtio_device_endian virtio_default_endian(void) { - if (target_words_bigendian()) { + if (target_big_endian()) { return VIRTIO_DEVICE_ENDIAN_BIG; } else { return VIRTIO_DEVICE_ENDIAN_LITTLE; @@ -2096,7 +2321,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."); } */ @@ -2106,41 +2331,6 @@ void virtio_queue_enable(VirtIODevice *vdev, uint32_t queue_index) } } -void virtio_reset(void *opaque) -{ - VirtIODevice *vdev = opaque; - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - int i; - - virtio_set_status(vdev, 0); - if (current_cpu) { - /* Guest initiated reset */ - vdev->device_endian = virtio_current_cpu_endian(); - } else { - /* System reset */ - vdev->device_endian = virtio_default_endian(); - } - - if (k->reset) { - k->reset(vdev); - } - - vdev->start_on_kick = false; - vdev->started = false; - vdev->broken = false; - vdev->guest_features = 0; - vdev->queue_sel = 0; - vdev->status = 0; - vdev->disabled = false; - qatomic_set(&vdev->isr, 0); - vdev->config_vector = VIRTIO_NO_VECTOR; - virtio_notify_vector(vdev, vdev->config_vector); - - for(i = 0; i < VIRTIO_QUEUE_MAX; i++) { - __virtio_queue_reset(vdev, i); - } -} - void virtio_queue_set_addr(VirtIODevice *vdev, int n, hwaddr addr) { if (!vdev->vq[n].vring.num) { @@ -2235,6 +2425,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) { @@ -2426,6 +2634,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() { @@ -2452,7 +2670,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) @@ -2564,7 +2782,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() @@ -2575,7 +2793,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), @@ -2590,7 +2808,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() @@ -2602,7 +2820,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() @@ -2613,7 +2831,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() } @@ -2624,7 +2842,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() @@ -2667,7 +2885,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, @@ -2686,7 +2904,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() } @@ -2697,7 +2915,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() } @@ -2708,7 +2926,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() } @@ -2719,7 +2937,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() } @@ -2730,7 +2948,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() } @@ -2740,10 +2958,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, @@ -2923,6 +3141,63 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val) return ret; } +void virtio_reset(void *opaque) +{ + VirtIODevice *vdev = opaque; + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + int i; + + virtio_set_status(vdev, 0); + if (current_cpu) { + /* Guest initiated reset */ + vdev->device_endian = virtio_current_cpu_endian(); + } else { + /* System reset */ + 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); + } + + vdev->start_on_kick = false; + vdev->started = false; + vdev->broken = false; + virtio_set_features_nocheck(vdev, 0); + vdev->queue_sel = 0; + vdev->status = 0; + vdev->disabled = false; + qatomic_set(&vdev->isr, 0); + vdev->config_vector = VIRTIO_NO_VECTOR; + virtio_notify_vector(vdev, vdev->config_vector); + + for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { + __virtio_queue_reset(vdev, i); + } +} + +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) { @@ -2995,6 +3270,13 @@ 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) { @@ -3152,7 +3434,7 @@ void virtio_cleanup(VirtIODevice *vdev) qemu_del_vm_change_state_handler(vdev->vmstate); } -static void virtio_vmstate_change(void *opaque, bool running, RunState state) +static int virtio_vmstate_change(void *opaque, bool running, RunState state) { VirtIODevice *vdev = opaque; BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); @@ -3169,8 +3451,12 @@ static void virtio_vmstate_change(void *opaque, bool running, RunState state) } if (!backend_run) { - virtio_set_status(vdev, vdev->status); + int ret = virtio_set_status(vdev, vdev->status); + if (ret) { + return ret; + } } + return 0; } void virtio_instance_init_common(Object *proxy_obj, void *data, @@ -3222,7 +3508,7 @@ void virtio_init(VirtIODevice *vdev, uint16_t device_id, size_t config_size) vdev->config = NULL; } vdev->vmstate = qdev_add_vm_change_state_handler(DEVICE(vdev), - virtio_vmstate_change, vdev); + NULL, virtio_vmstate_change, vdev); vdev->device_endian = virtio_default_endian(); vdev->use_guest_notifier_mask = true; } @@ -3381,7 +3667,6 @@ static void virtio_queue_packed_restore_last_avail_idx(VirtIODevice *vdev, int n) { /* We don't have a reference like avail idx in shared memory */ - return; } static void virtio_queue_split_restore_last_avail_idx(VirtIODevice *vdev, @@ -3406,10 +3691,9 @@ void virtio_queue_restore_last_avail_idx(VirtIODevice *vdev, int n) static void virtio_queue_packed_update_used_idx(VirtIODevice *vdev, int n) { /* used idx was updated through set_last_avail_idx() */ - 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) { @@ -3422,7 +3706,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); } } @@ -3526,6 +3810,17 @@ static void virtio_queue_host_notifier_aio_poll_end(EventNotifier *n) void virtio_queue_aio_attach_host_notifier(VirtQueue *vq, AioContext *ctx) { + /* + * 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, @@ -3533,6 +3828,13 @@ void virtio_queue_aio_attach_host_notifier(VirtQueue *vq, AioContext *ctx) 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); } /* @@ -3543,14 +3845,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) { + /* 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, 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) @@ -3641,6 +3967,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); @@ -3651,7 +3985,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) @@ -3666,7 +3999,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; } @@ -3697,13 +4029,12 @@ static void virtio_device_instance_finalize(Object *obj) g_free(vdev->vector_queues); } -static Property virtio_properties[] = { +static const Property virtio_properties[] = { DEFINE_VIRTIO_COMMON_FEATURES(VirtIODevice, host_features), DEFINE_PROP_BOOL("use-started", VirtIODevice, use_started, true), DEFINE_PROP_BOOL("use-disabled-flag", VirtIODevice, use_disabled_flag, true), DEFINE_PROP_BOOL("x-disable-legacy-check", VirtIODevice, disable_legacy_check, false), - DEFINE_PROP_END_OF_LIST(), }; static int virtio_device_start_ioeventfd_impl(VirtIODevice *vdev) @@ -3826,7 +4157,7 @@ void virtio_device_release_ioeventfd(VirtIODevice *vdev) virtio_bus_release_ioeventfd(vbus); } -static void virtio_device_class_init(ObjectClass *klass, void *data) +static void virtio_device_class_init(ObjectClass *klass, const void *data) { /* Set the default value here. */ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); @@ -3840,8 +4171,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) @@ -3970,13 +4299,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; @@ -4041,8 +4372,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: @@ -4068,3 +4398,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/vmapple/Kconfig b/hw/vmapple/Kconfig new file mode 100644 index 00000000000..2382b297672 --- /dev/null +++ b/hw/vmapple/Kconfig @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config VMAPPLE_AES + bool + +config VMAPPLE_BDIF + bool + +config VMAPPLE_CFG + bool + +config VMAPPLE_VIRTIO_BLK + bool + +config VMAPPLE + bool + depends on ARM + depends on HVF + default y if ARM + imply PCI_DEVICES + select ARM_GICV3 + select PLATFORM_BUS + select PCI_EXPRESS + select PCI_EXPRESS_GENERIC_BRIDGE + select PL011 # UART + select PL031 # RTC + select PL061 # GPIO + select GPIO_PWR + select PVPANIC_MMIO + select VMAPPLE_AES + select VMAPPLE_BDIF + select VMAPPLE_CFG + select MAC_PVG_MMIO + select VMAPPLE_VIRTIO_BLK diff --git a/hw/vmapple/aes.c b/hw/vmapple/aes.c new file mode 100644 index 00000000000..a4853a98f8c --- /dev/null +++ b/hw/vmapple/aes.c @@ -0,0 +1,581 @@ +/* + * QEMU Apple AES device emulation + * + * 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. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "trace.h" +#include "crypto/hash.h" +#include "crypto/aes.h" +#include "crypto/cipher.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "hw/vmapple/vmapple.h" +#include "migration/vmstate.h" +#include "qemu/cutils.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "system/dma.h" + +OBJECT_DECLARE_SIMPLE_TYPE(AESState, APPLE_AES) + +#define MAX_FIFO_SIZE 9 + +#define CMD_KEY 0x1 +#define CMD_KEY_CONTEXT_SHIFT 27 +#define CMD_KEY_CONTEXT_MASK (0x1 << CMD_KEY_CONTEXT_SHIFT) +#define CMD_KEY_SELECT_MAX_IDX 0x7 +#define CMD_KEY_SELECT_SHIFT 24 +#define CMD_KEY_SELECT_MASK (CMD_KEY_SELECT_MAX_IDX << CMD_KEY_SELECT_SHIFT) +#define CMD_KEY_KEY_LEN_NUM 4u +#define CMD_KEY_KEY_LEN_SHIFT 22 +#define CMD_KEY_KEY_LEN_MASK ((CMD_KEY_KEY_LEN_NUM - 1u) << CMD_KEY_KEY_LEN_SHIFT) +#define CMD_KEY_ENCRYPT_SHIFT 20 +#define CMD_KEY_ENCRYPT_MASK (0x1 << CMD_KEY_ENCRYPT_SHIFT) +#define CMD_KEY_BLOCK_MODE_SHIFT 16 +#define CMD_KEY_BLOCK_MODE_MASK (0x3 << CMD_KEY_BLOCK_MODE_SHIFT) +#define CMD_IV 0x2 +#define CMD_IV_CONTEXT_SHIFT 26 +#define CMD_IV_CONTEXT_MASK (0x3 << CMD_KEY_CONTEXT_SHIFT) +#define CMD_DSB 0x3 +#define CMD_SKG 0x4 +#define CMD_DATA 0x5 +#define CMD_DATA_KEY_CTX_SHIFT 27 +#define CMD_DATA_KEY_CTX_MASK (0x1 << CMD_DATA_KEY_CTX_SHIFT) +#define CMD_DATA_IV_CTX_SHIFT 25 +#define CMD_DATA_IV_CTX_MASK (0x3 << CMD_DATA_IV_CTX_SHIFT) +#define CMD_DATA_LEN_MASK 0xffffff +#define CMD_STORE_IV 0x6 +#define CMD_STORE_IV_ADDR_MASK 0xffffff +#define CMD_WRITE_REG 0x7 +#define CMD_FLAG 0x8 +#define CMD_FLAG_STOP_MASK BIT(26) +#define CMD_FLAG_RAISE_IRQ_MASK BIT(27) +#define CMD_FLAG_INFO_MASK 0xff +#define CMD_MAX 0x10 + +#define CMD_SHIFT 28 + +#define REG_STATUS 0xc +#define REG_STATUS_DMA_READ_RUNNING BIT(0) +#define REG_STATUS_DMA_READ_PENDING BIT(1) +#define REG_STATUS_DMA_WRITE_RUNNING BIT(2) +#define REG_STATUS_DMA_WRITE_PENDING BIT(3) +#define REG_STATUS_BUSY BIT(4) +#define REG_STATUS_EXECUTING BIT(5) +#define REG_STATUS_READY BIT(6) +#define REG_STATUS_TEXT_DPA_SEEDED BIT(7) +#define REG_STATUS_UNWRAP_DPA_SEEDED BIT(8) + +#define REG_IRQ_STATUS 0x18 +#define REG_IRQ_STATUS_INVALID_CMD BIT(2) +#define REG_IRQ_STATUS_FLAG BIT(5) +#define REG_IRQ_ENABLE 0x1c +#define REG_WATERMARK 0x20 +#define REG_Q_STATUS 0x24 +#define REG_FLAG_INFO 0x30 +#define REG_FIFO 0x200 + +static const uint32_t key_lens[CMD_KEY_KEY_LEN_NUM] = { + [0] = 16, + [1] = 24, + [2] = 32, + [3] = 64, +}; + +typedef struct Key { + uint32_t key_len; + uint8_t key[32]; +} Key; + +typedef struct IV { + uint32_t iv[4]; +} IV; + +static Key builtin_keys[CMD_KEY_SELECT_MAX_IDX + 1] = { + [1] = { + .key_len = 32, + .key = { 0x1 }, + }, + [2] = { + .key_len = 32, + .key = { 0x2 }, + }, + [3] = { + .key_len = 32, + .key = { 0x3 }, + } +}; + +struct AESState { + SysBusDevice parent_obj; + + qemu_irq irq; + MemoryRegion iomem1; + MemoryRegion iomem2; + AddressSpace *as; + + uint32_t status; + uint32_t q_status; + uint32_t irq_status; + uint32_t irq_enable; + uint32_t watermark; + uint32_t flag_info; + uint32_t fifo[MAX_FIFO_SIZE]; + uint32_t fifo_idx; + Key key[2]; + IV iv[4]; + bool is_encrypt; + QCryptoCipherMode block_mode; +}; + +static void aes_update_irq(AESState *s) +{ + qemu_set_irq(s->irq, !!(s->irq_status & s->irq_enable)); +} + +static uint64_t aes1_read(void *opaque, hwaddr offset, unsigned size) +{ + AESState *s = opaque; + uint64_t res = 0; + + switch (offset) { + case REG_STATUS: + res = s->status; + break; + case REG_IRQ_STATUS: + res = s->irq_status; + break; + case REG_IRQ_ENABLE: + res = s->irq_enable; + break; + case REG_WATERMARK: + res = s->watermark; + break; + case REG_Q_STATUS: + res = s->q_status; + break; + case REG_FLAG_INFO: + res = s->flag_info; + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: Unknown AES MMIO offset %" PRIx64 "\n", + __func__, offset); + break; + } + + trace_aes_read(offset, res); + + return res; +} + +static void fifo_append(AESState *s, uint64_t val) +{ + if (s->fifo_idx == MAX_FIFO_SIZE) { + /* Exceeded the FIFO. Bail out */ + return; + } + + s->fifo[s->fifo_idx++] = val; +} + +static bool has_payload(AESState *s, uint32_t elems) +{ + return s->fifo_idx >= elems + 1; +} + +static bool cmd_key(AESState *s) +{ + uint32_t cmd = s->fifo[0]; + uint32_t key_select = (cmd & CMD_KEY_SELECT_MASK) >> CMD_KEY_SELECT_SHIFT; + uint32_t ctxt = (cmd & CMD_KEY_CONTEXT_MASK) >> CMD_KEY_CONTEXT_SHIFT; + uint32_t key_len; + + switch ((cmd & CMD_KEY_BLOCK_MODE_MASK) >> CMD_KEY_BLOCK_MODE_SHIFT) { + case 0: + s->block_mode = QCRYPTO_CIPHER_MODE_ECB; + break; + case 1: + s->block_mode = QCRYPTO_CIPHER_MODE_CBC; + break; + default: + return false; + } + + s->is_encrypt = cmd & CMD_KEY_ENCRYPT_MASK; + key_len = key_lens[(cmd & CMD_KEY_KEY_LEN_MASK) >> CMD_KEY_KEY_LEN_SHIFT]; + + if (key_select) { + trace_aes_cmd_key_select_builtin(ctxt, key_select, + s->is_encrypt ? "en" : "de", + QCryptoCipherMode_str(s->block_mode)); + s->key[ctxt] = builtin_keys[key_select]; + } else { + trace_aes_cmd_key_select_new(ctxt, key_len, + s->is_encrypt ? "en" : "de", + QCryptoCipherMode_str(s->block_mode)); + if (key_len > sizeof(s->key[ctxt].key)) { + return false; + } + if (!has_payload(s, key_len / sizeof(uint32_t))) { + /* wait for payload */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: No payload\n", __func__); + return false; + } + memcpy(&s->key[ctxt].key, &s->fifo[1], key_len); + s->key[ctxt].key_len = key_len; + } + + return true; +} + +static bool cmd_iv(AESState *s) +{ + uint32_t cmd = s->fifo[0]; + uint32_t ctxt = (cmd & CMD_IV_CONTEXT_MASK) >> CMD_IV_CONTEXT_SHIFT; + + if (!has_payload(s, 4)) { + /* wait for payload */ + return false; + } + memcpy(&s->iv[ctxt].iv, &s->fifo[1], sizeof(s->iv[ctxt].iv)); + trace_aes_cmd_iv(ctxt, s->fifo[1], s->fifo[2], s->fifo[3], s->fifo[4]); + + return true; +} + +static void dump_data(const char *desc, const void *p, size_t len) +{ + static const size_t MAX_LEN = 0x1000; + char hex[MAX_LEN * 2 + 1] = ""; + + if (len > MAX_LEN) { + return; + } + + qemu_hexdump_to_buffer(hex, sizeof(hex), p, len); + trace_aes_dump_data(desc, hex); +} + +static bool cmd_data(AESState *s) +{ + uint32_t cmd = s->fifo[0]; + uint32_t ctxt_iv = 0; + uint32_t ctxt_key = (cmd & CMD_DATA_KEY_CTX_MASK) >> CMD_DATA_KEY_CTX_SHIFT; + uint32_t len = cmd & CMD_DATA_LEN_MASK; + uint64_t src_addr = s->fifo[2]; + uint64_t dst_addr = s->fifo[3]; + QCryptoCipherAlgo alg; + g_autoptr(QCryptoCipher) cipher = NULL; + g_autoptr(GByteArray) src = NULL; + g_autoptr(GByteArray) dst = NULL; + MemTxResult r; + + src_addr |= ((uint64_t)s->fifo[1] << 16) & 0xffff00000000ULL; + dst_addr |= ((uint64_t)s->fifo[1] << 32) & 0xffff00000000ULL; + + trace_aes_cmd_data(ctxt_key, ctxt_iv, src_addr, dst_addr, len); + + if (!has_payload(s, 3)) { + /* wait for payload */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: No payload\n", __func__); + return false; + } + + if (ctxt_key >= ARRAY_SIZE(s->key) || + ctxt_iv >= ARRAY_SIZE(s->iv)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid key or iv\n", __func__); + return false; + } + + src = g_byte_array_sized_new(len); + g_byte_array_set_size(src, len); + dst = g_byte_array_sized_new(len); + g_byte_array_set_size(dst, len); + + r = dma_memory_read(s->as, src_addr, src->data, len, MEMTXATTRS_UNSPECIFIED); + if (r != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA read of %"PRIu32" bytes " + "from 0x%"PRIx64" failed. (r=%d)\n", + __func__, len, src_addr, r); + return false; + } + + dump_data("cmd_data(): src_data=", src->data, len); + + switch (s->key[ctxt_key].key_len) { + case 128 / 8: + alg = QCRYPTO_CIPHER_ALGO_AES_128; + break; + case 192 / 8: + alg = QCRYPTO_CIPHER_ALGO_AES_192; + break; + case 256 / 8: + alg = QCRYPTO_CIPHER_ALGO_AES_256; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid key length\n", __func__); + return false; + } + cipher = qcrypto_cipher_new(alg, s->block_mode, + s->key[ctxt_key].key, + s->key[ctxt_key].key_len, NULL); + if (!cipher) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to create cipher object\n", + __func__); + return false; + } + if (s->block_mode != QCRYPTO_CIPHER_MODE_ECB) { + if (qcrypto_cipher_setiv(cipher, (void *)s->iv[ctxt_iv].iv, + sizeof(s->iv[ctxt_iv].iv), NULL) != 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to set IV\n", __func__); + return false; + } + } + if (s->is_encrypt) { + if (qcrypto_cipher_encrypt(cipher, src->data, dst->data, len, NULL) != 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Encryption failed\n", __func__); + return false; + } + } else { + if (qcrypto_cipher_decrypt(cipher, src->data, dst->data, len, NULL) != 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Decryption failed\n", __func__); + return false; + } + } + + dump_data("cmd_data(): dst_data=", dst->data, len); + r = dma_memory_write(s->as, dst_addr, dst->data, len, MEMTXATTRS_UNSPECIFIED); + if (r != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA write of %"PRIu32" bytes " + "to 0x%"PRIx64" failed. (r=%d)\n", + __func__, len, src_addr, r); + return false; + } + + return true; +} + +static bool cmd_store_iv(AESState *s) +{ + uint32_t cmd = s->fifo[0]; + uint32_t ctxt = (cmd & CMD_IV_CONTEXT_MASK) >> CMD_IV_CONTEXT_SHIFT; + uint64_t addr = s->fifo[1]; + MemTxResult dma_result; + + if (!has_payload(s, 1)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No payload\n", __func__); + return false; + } + + if (ctxt >= ARRAY_SIZE(s->iv)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid context. ctxt = %u, allowed: 0..%zu\n", + __func__, ctxt, ARRAY_SIZE(s->iv) - 1); + return false; + } + + addr |= ((uint64_t)cmd << 32) & 0xff00000000ULL; + dma_result = dma_memory_write(&address_space_memory, addr, + &s->iv[ctxt].iv, sizeof(s->iv[ctxt].iv), + MEMTXATTRS_UNSPECIFIED); + + trace_aes_cmd_store_iv(ctxt, addr, s->iv[ctxt].iv[0], s->iv[ctxt].iv[1], + s->iv[ctxt].iv[2], s->iv[ctxt].iv[3]); + + return dma_result == MEMTX_OK; +} + +static bool cmd_flag(AESState *s) +{ + uint32_t cmd = s->fifo[0]; + uint32_t raise_irq = cmd & CMD_FLAG_RAISE_IRQ_MASK; + + /* We always process data when it's coming in, so fire an IRQ immediately */ + if (raise_irq) { + s->irq_status |= REG_IRQ_STATUS_FLAG; + } + + s->flag_info = cmd & CMD_FLAG_INFO_MASK; + + trace_aes_cmd_flag(!!raise_irq, s->flag_info); + + return true; +} + +static void fifo_process(AESState *s) +{ + uint32_t cmd = s->fifo[0] >> CMD_SHIFT; + bool success = false; + + if (!s->fifo_idx) { + return; + } + + switch (cmd) { + case CMD_KEY: + success = cmd_key(s); + break; + case CMD_IV: + success = cmd_iv(s); + break; + case CMD_DATA: + success = cmd_data(s); + break; + case CMD_STORE_IV: + success = cmd_store_iv(s); + break; + case CMD_FLAG: + success = cmd_flag(s); + break; + default: + s->irq_status |= REG_IRQ_STATUS_INVALID_CMD; + break; + } + + if (success) { + s->fifo_idx = 0; + } + + trace_aes_fifo_process(cmd, success); +} + +static void aes1_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) +{ + AESState *s = opaque; + + trace_aes_write(offset, val); + + switch (offset) { + case REG_IRQ_STATUS: + s->irq_status &= ~val; + break; + case REG_IRQ_ENABLE: + s->irq_enable = val; + break; + case REG_FIFO: + fifo_append(s, val); + fifo_process(s); + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s: Unknown AES MMIO offset %"PRIx64", data %"PRIx64"\n", + __func__, offset, val); + return; + } + + aes_update_irq(s); +} + +static const MemoryRegionOps aes1_ops = { + .read = aes1_read, + .write = aes1_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static uint64_t aes2_read(void *opaque, hwaddr offset, unsigned size) +{ + uint64_t res = 0; + + switch (offset) { + case 0: + res = 0; + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s: Unknown AES MMIO 2 offset %"PRIx64"\n", + __func__, offset); + break; + } + + trace_aes_2_read(offset, res); + + return res; +} + +static void aes2_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) +{ + trace_aes_2_write(offset, val); + + switch (offset) { + default: + qemu_log_mask(LOG_UNIMP, + "%s: Unknown AES MMIO 2 offset %"PRIx64", data %"PRIx64"\n", + __func__, offset, val); + return; + } +} + +static const MemoryRegionOps aes2_ops = { + .read = aes2_read, + .write = aes2_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void aes_reset(Object *obj, ResetType type) +{ + AESState *s = APPLE_AES(obj); + + s->status = 0x3f80; + s->q_status = 2; + s->irq_status = 0; + s->irq_enable = 0; + s->watermark = 0; +} + +static void aes_init(Object *obj) +{ + AESState *s = APPLE_AES(obj); + + memory_region_init_io(&s->iomem1, obj, &aes1_ops, s, TYPE_APPLE_AES, 0x4000); + memory_region_init_io(&s->iomem2, obj, &aes2_ops, s, TYPE_APPLE_AES, 0x4000); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem1); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem2); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); + s->as = &address_space_memory; +} + +static void aes_class_init(ObjectClass *klass, const void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = aes_reset; +} + +static const TypeInfo aes_info = { + .name = TYPE_APPLE_AES, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AESState), + .class_init = aes_class_init, + .instance_init = aes_init, +}; + +static void aes_register_types(void) +{ + type_register_static(&aes_info); +} + +type_init(aes_register_types) diff --git a/hw/vmapple/bdif.c b/hw/vmapple/bdif.c new file mode 100644 index 00000000000..5ccd3745819 --- /dev/null +++ b/hw/vmapple/bdif.c @@ -0,0 +1,274 @@ +/* + * VMApple Backdoor Interface + * + * 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. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "trace.h" +#include "hw/vmapple/vmapple.h" +#include "hw/sysbus.h" +#include "hw/block/block.h" +#include "qapi/error.h" +#include "system/block-backend.h" +#include "system/dma.h" + +OBJECT_DECLARE_SIMPLE_TYPE(VMAppleBdifState, VMAPPLE_BDIF) + +struct VMAppleBdifState { + SysBusDevice parent_obj; + + BlockBackend *aux; + BlockBackend *root; + MemoryRegion mmio; +}; + +#define VMAPPLE_BDIF_SIZE 0x00200000 + +#define REG_DEVID_MASK 0xffff0000 +#define DEVID_ROOT 0x00000000 +#define DEVID_AUX 0x00010000 +#define DEVID_USB 0x00100000 + +#define REG_STATUS 0x0 +#define REG_STATUS_ACTIVE BIT(0) +#define REG_CFG 0x4 +#define REG_CFG_ACTIVE BIT(1) +#define REG_UNK1 0x8 +#define REG_BUSY 0x10 +#define REG_BUSY_READY BIT(0) +#define REG_UNK2 0x400 +#define REG_CMD 0x408 +#define REG_NEXT_DEVICE 0x420 +#define REG_UNK3 0x434 + +typedef struct VblkSector { + uint32_t pad; + uint32_t pad2; + uint32_t sector; + uint32_t pad3; +} VblkSector; + +typedef struct VblkReqCmd { + uint64_t addr; + uint32_t len; + uint32_t flags; +} VblkReqCmd; + +typedef struct VblkReq { + VblkReqCmd sector; + VblkReqCmd data; + VblkReqCmd retval; +} VblkReq; + +#define VBLK_DATA_FLAGS_READ 0x00030001 +#define VBLK_DATA_FLAGS_WRITE 0x00010001 + +#define VBLK_RET_SUCCESS 0 +#define VBLK_RET_FAILED 1 + +static uint64_t bdif_read(void *opaque, hwaddr offset, unsigned size) +{ + uint64_t ret = -1; + uint64_t devid = offset & REG_DEVID_MASK; + + switch (offset & ~REG_DEVID_MASK) { + case REG_STATUS: + ret = REG_STATUS_ACTIVE; + break; + case REG_CFG: + ret = REG_CFG_ACTIVE; + break; + case REG_UNK1: + ret = 0x420; + break; + case REG_BUSY: + ret = REG_BUSY_READY; + break; + case REG_UNK2: + ret = 0x1; + break; + case REG_UNK3: + ret = 0x0; + break; + case REG_NEXT_DEVICE: + switch (devid) { + case DEVID_ROOT: + ret = 0x8000000; + break; + case DEVID_AUX: + ret = 0x10000; + break; + } + break; + } + + trace_bdif_read(offset, size, ret); + return ret; +} + +static void le2cpu_sector(VblkSector *sector) +{ + sector->sector = le32_to_cpu(sector->sector); +} + +static void le2cpu_reqcmd(VblkReqCmd *cmd) +{ + cmd->addr = le64_to_cpu(cmd->addr); + cmd->len = le32_to_cpu(cmd->len); + cmd->flags = le32_to_cpu(cmd->flags); +} + +static void le2cpu_req(VblkReq *req) +{ + le2cpu_reqcmd(&req->sector); + le2cpu_reqcmd(&req->data); + le2cpu_reqcmd(&req->retval); +} + +static void vblk_cmd(uint64_t devid, BlockBackend *blk, uint64_t gp_addr, + uint64_t static_off) +{ + VblkReq req; + VblkSector sector; + uint64_t off = 0; + g_autofree char *buf = NULL; + uint8_t ret = VBLK_RET_FAILED; + int r; + MemTxResult dma_result; + + dma_result = dma_memory_read(&address_space_memory, gp_addr, + &req, sizeof(req), MEMTXATTRS_UNSPECIFIED); + if (dma_result != MEMTX_OK) { + goto out; + } + + le2cpu_req(&req); + + if (req.sector.len != sizeof(sector)) { + goto out; + } + + /* Read the vblk command */ + dma_result = dma_memory_read(&address_space_memory, req.sector.addr, + §or, sizeof(sector), + MEMTXATTRS_UNSPECIFIED); + if (dma_result != MEMTX_OK) { + goto out; + } + le2cpu_sector(§or); + + off = sector.sector * 512ULL + static_off; + + /* Sanity check that we're not allocating bogus sizes */ + if (req.data.len > 128 * MiB) { + goto out; + } + + buf = g_malloc0(req.data.len); + switch (req.data.flags) { + case VBLK_DATA_FLAGS_READ: + r = blk_pread(blk, off, req.data.len, buf, 0); + trace_bdif_vblk_read(devid == DEVID_AUX ? "aux" : "root", + req.data.addr, off, req.data.len, r); + if (r < 0) { + goto out; + } + dma_result = dma_memory_write(&address_space_memory, req.data.addr, buf, + req.data.len, MEMTXATTRS_UNSPECIFIED); + if (dma_result == MEMTX_OK) { + ret = VBLK_RET_SUCCESS; + } + break; + case VBLK_DATA_FLAGS_WRITE: + /* Not needed, iBoot only reads */ + break; + default: + break; + } + +out: + dma_memory_write(&address_space_memory, req.retval.addr, &ret, 1, + MEMTXATTRS_UNSPECIFIED); +} + +static void bdif_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + VMAppleBdifState *s = opaque; + uint64_t devid = (offset & REG_DEVID_MASK); + + trace_bdif_write(offset, size, value); + + switch (offset & ~REG_DEVID_MASK) { + case REG_CMD: + switch (devid) { + case DEVID_ROOT: + vblk_cmd(devid, s->root, value, 0x0); + break; + case DEVID_AUX: + vblk_cmd(devid, s->aux, value, 0x0); + break; + } + break; + } +} + +static const MemoryRegionOps bdif_ops = { + .read = bdif_read, + .write = bdif_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +static void bdif_init(Object *obj) +{ + VMAppleBdifState *s = VMAPPLE_BDIF(obj); + + memory_region_init_io(&s->mmio, obj, &bdif_ops, obj, + "VMApple Backdoor Interface", VMAPPLE_BDIF_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static const Property bdif_properties[] = { + DEFINE_PROP_DRIVE("aux", VMAppleBdifState, aux), + DEFINE_PROP_DRIVE("root", VMAppleBdifState, root), +}; + +static void bdif_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "VMApple Backdoor Interface"; + device_class_set_props(dc, bdif_properties); +} + +static const TypeInfo bdif_info = { + .name = TYPE_VMAPPLE_BDIF, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(VMAppleBdifState), + .instance_init = bdif_init, + .class_init = bdif_class_init, +}; + +static void bdif_register_types(void) +{ + type_register_static(&bdif_info); +} + +type_init(bdif_register_types) diff --git a/hw/vmapple/cfg.c b/hw/vmapple/cfg.c new file mode 100644 index 00000000000..3d58a29f69d --- /dev/null +++ b/hw/vmapple/cfg.c @@ -0,0 +1,195 @@ +/* + * VMApple Configuration Region + * + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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/vmapple/vmapple.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "net/net.h" + +OBJECT_DECLARE_SIMPLE_TYPE(VMAppleCfgState, VMAPPLE_CFG) + +#define VMAPPLE_CFG_SIZE 0x00010000 + +typedef struct VMAppleCfg { + uint32_t version; /* 0x000 */ + uint32_t nr_cpus; /* 0x004 */ + uint32_t unk1; /* 0x008 */ + uint32_t unk2; /* 0x00c */ + uint32_t unk3; /* 0x010 */ + uint32_t unk4; /* 0x014 */ + uint64_t ecid; /* 0x018 */ + uint64_t ram_size; /* 0x020 */ + uint32_t run_installer1; /* 0x028 */ + uint32_t unk5; /* 0x02c */ + uint32_t unk6; /* 0x030 */ + uint32_t run_installer2; /* 0x034 */ + uint32_t rnd; /* 0x038 */ + uint32_t unk7; /* 0x03c */ + MACAddr mac_en0; /* 0x040 */ + uint8_t pad1[2]; + MACAddr mac_en1; /* 0x048 */ + uint8_t pad2[2]; + MACAddr mac_wifi0; /* 0x050 */ + uint8_t pad3[2]; + MACAddr mac_bt0; /* 0x058 */ + uint8_t pad4[2]; + uint8_t reserved[0xa0]; /* 0x060 */ + uint32_t cpu_ids[0x80]; /* 0x100 */ + uint8_t scratch[0x200]; /* 0x180 */ + char serial[32]; /* 0x380 */ + char unk8[32]; /* 0x3a0 */ + char model[32]; /* 0x3c0 */ + uint8_t unk9[32]; /* 0x3e0 */ + uint32_t unk10; /* 0x400 */ + char soc_name[32]; /* 0x404 */ +} VMAppleCfg; + +struct VMAppleCfgState { + SysBusDevice parent_obj; + VMAppleCfg cfg; + + MemoryRegion mem; + char *serial; + char *model; + char *soc_name; +}; + +static void vmapple_cfg_reset(Object *obj, ResetType type) +{ + VMAppleCfgState *s = VMAPPLE_CFG(obj); + VMAppleCfg *cfg; + + cfg = memory_region_get_ram_ptr(&s->mem); + memset(cfg, 0, VMAPPLE_CFG_SIZE); + *cfg = s->cfg; +} + +static bool set_fixlen_property_or_error(char *restrict dst, + const char *restrict src, + size_t dst_size, Error **errp, + const char *property_name) +{ + ERRP_GUARD(); + size_t len; + + len = g_strlcpy(dst, src, dst_size); + if (len < dst_size) { /* len does not count nul terminator */ + return true; + } + + error_setg(errp, "Provided value too long for property '%s'", property_name); + error_append_hint(errp, "length (%zu) exceeds maximum of %zu\n", + len, dst_size - 1); + return false; +} + +#define set_fixlen_property_or_return(dst_array, src, errp, property_name) \ + do { \ + if (!set_fixlen_property_or_error((dst_array), (src), \ + ARRAY_SIZE(dst_array), \ + (errp), (property_name))) { \ + return; \ + } \ + } while (0) + +static void vmapple_cfg_realize(DeviceState *dev, Error **errp) +{ + VMAppleCfgState *s = VMAPPLE_CFG(dev); + uint32_t i; + + if (!s->serial) { + s->serial = g_strdup("1234"); + } + if (!s->model) { + s->model = g_strdup("VM0001"); + } + if (!s->soc_name) { + s->soc_name = g_strdup("Apple M1 (Virtual)"); + } + + set_fixlen_property_or_return(s->cfg.serial, s->serial, errp, "serial"); + set_fixlen_property_or_return(s->cfg.model, s->model, errp, "model"); + set_fixlen_property_or_return(s->cfg.soc_name, s->soc_name, errp, "soc_name"); + set_fixlen_property_or_return(s->cfg.unk8, "D/A", errp, "unk8"); + s->cfg.version = 2; + s->cfg.unk1 = 1; + s->cfg.unk2 = 1; + s->cfg.unk3 = 0x20; + s->cfg.unk4 = 0; + s->cfg.unk5 = 1; + s->cfg.unk6 = 1; + s->cfg.unk7 = 0; + s->cfg.unk10 = 1; + + if (s->cfg.nr_cpus > ARRAY_SIZE(s->cfg.cpu_ids)) { + error_setg(errp, + "Failed to create %u CPUs, vmapple machine supports %zu max", + s->cfg.nr_cpus, ARRAY_SIZE(s->cfg.cpu_ids)); + return; + } + for (i = 0; i < s->cfg.nr_cpus; i++) { + s->cfg.cpu_ids[i] = i; + } +} + +static void vmapple_cfg_init(Object *obj) +{ + VMAppleCfgState *s = VMAPPLE_CFG(obj); + + memory_region_init_ram(&s->mem, obj, "VMApple Config", VMAPPLE_CFG_SIZE, + &error_fatal); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mem); +} + +static const Property vmapple_cfg_properties[] = { + DEFINE_PROP_UINT32("nr-cpus", VMAppleCfgState, cfg.nr_cpus, 1), + DEFINE_PROP_UINT64("ecid", VMAppleCfgState, cfg.ecid, 0), + DEFINE_PROP_UINT64("ram-size", VMAppleCfgState, cfg.ram_size, 0), + DEFINE_PROP_UINT32("run_installer1", VMAppleCfgState, cfg.run_installer1, 0), + DEFINE_PROP_UINT32("run_installer2", VMAppleCfgState, cfg.run_installer2, 0), + DEFINE_PROP_UINT32("rnd", VMAppleCfgState, cfg.rnd, 0), + DEFINE_PROP_MACADDR("mac-en0", VMAppleCfgState, cfg.mac_en0), + DEFINE_PROP_MACADDR("mac-en1", VMAppleCfgState, cfg.mac_en1), + DEFINE_PROP_MACADDR("mac-wifi0", VMAppleCfgState, cfg.mac_wifi0), + DEFINE_PROP_MACADDR("mac-bt0", VMAppleCfgState, cfg.mac_bt0), + DEFINE_PROP_STRING("serial", VMAppleCfgState, serial), + DEFINE_PROP_STRING("model", VMAppleCfgState, model), + DEFINE_PROP_STRING("soc_name", VMAppleCfgState, soc_name), +}; + +static void vmapple_cfg_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->realize = vmapple_cfg_realize; + dc->desc = "VMApple Configuration Region"; + device_class_set_props(dc, vmapple_cfg_properties); + rc->phases.hold = vmapple_cfg_reset; +} + +static const TypeInfo vmapple_cfg_info = { + .name = TYPE_VMAPPLE_CFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(VMAppleCfgState), + .instance_init = vmapple_cfg_init, + .class_init = vmapple_cfg_class_init, +}; + +static void vmapple_cfg_register_types(void) +{ + type_register_static(&vmapple_cfg_info); +} + +type_init(vmapple_cfg_register_types) diff --git a/hw/vmapple/meson.build b/hw/vmapple/meson.build new file mode 100644 index 00000000000..23bc4c999e3 --- /dev/null +++ b/hw/vmapple/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c')) +system_ss.add(when: 'CONFIG_VMAPPLE_BDIF', if_true: files('bdif.c')) +system_ss.add(when: 'CONFIG_VMAPPLE_CFG', if_true: files('cfg.c')) +system_ss.add(when: 'CONFIG_VMAPPLE_VIRTIO_BLK', if_true: files('virtio-blk.c')) +specific_ss.add(when: 'CONFIG_VMAPPLE', if_true: files('vmapple.c')) diff --git a/hw/vmapple/trace-events b/hw/vmapple/trace-events new file mode 100644 index 00000000000..93380ede145 --- /dev/null +++ b/hw/vmapple/trace-events @@ -0,0 +1,21 @@ +# See docs/devel/tracing.rst for syntax documentation. +# SPDX-License-Identifier: GPL-2.0-or-later + +# aes.c +aes_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 +aes_cmd_key_select_builtin(uint32_t ctx, uint32_t key_id, const char *direction, const char *cipher) "[%d] Selecting builtin key %d to %scrypt with %s" +aes_cmd_key_select_new(uint32_t ctx, uint32_t key_len, const char *direction, const char *cipher) "[%d] Selecting new key size=%d to %scrypt with %s" +aes_cmd_iv(uint32_t ctx, uint32_t iv0, uint32_t iv1, uint32_t iv2, uint32_t iv3) "[%d] 0x%08x 0x%08x 0x%08x 0x%08x" +aes_cmd_data(uint32_t key, uint32_t iv, uint64_t src, uint64_t dst, uint32_t len) "[key=%d iv=%d] src=0x%"PRIx64" dst=0x%"PRIx64" len=0x%x" +aes_cmd_store_iv(uint32_t ctx, uint64_t addr, uint32_t iv0, uint32_t iv1, uint32_t iv2, uint32_t iv3) "[%d] addr=0x%"PRIx64"x -> 0x%08x 0x%08x 0x%08x 0x%08x" +aes_cmd_flag(uint32_t raise, uint32_t flag_info) "raise=%d flag_info=0x%x" +aes_fifo_process(uint32_t cmd, bool success) "cmd=%d success=%d" +aes_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 +aes_2_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64 +aes_2_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64 +aes_dump_data(const char *desc, const char *hex) "%s%s" + +# bdif.c +bdif_read(uint64_t offset, uint32_t size, uint64_t value) "offset=0x%"PRIx64" size=0x%x value=0x%"PRIx64 +bdif_write(uint64_t offset, uint32_t size, uint64_t value) "offset=0x%"PRIx64" size=0x%x value=0x%"PRIx64 +bdif_vblk_read(const char *dev, uint64_t addr, uint64_t offset, uint32_t len, int r) "dev=%s addr=0x%"PRIx64" off=0x%"PRIx64" size=0x%x r=%d" diff --git a/hw/vmapple/trace.h b/hw/vmapple/trace.h new file mode 100644 index 00000000000..d099d5ecd9e --- /dev/null +++ b/hw/vmapple/trace.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "trace/trace-hw_vmapple.h" diff --git a/hw/vmapple/virtio-blk.c b/hw/vmapple/virtio-blk.c new file mode 100644 index 00000000000..532b5649ab7 --- /dev/null +++ b/hw/vmapple/virtio-blk.c @@ -0,0 +1,205 @@ +/* + * VMApple specific VirtIO Block implementation + * + * 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. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * VMApple uses almost standard VirtIO Block, but with a few key differences: + * + * - Different PCI device/vendor ID + * - An additional "type" identifier to differentiate AUX and Root volumes + * - An additional BARRIER command + */ + +#include "qemu/osdep.h" +#include "hw/vmapple/vmapple.h" +#include "hw/virtio/virtio-blk.h" +#include "hw/virtio/virtio-pci.h" +#include "qemu/bswap.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qapi/error.h" + +#define TYPE_VMAPPLE_VIRTIO_BLK "vmapple-virtio-blk" +OBJECT_DECLARE_TYPE(VMAppleVirtIOBlk, VMAppleVirtIOBlkClass, VMAPPLE_VIRTIO_BLK) + +typedef struct VMAppleVirtIOBlkClass { + VirtIOBlkClass parent; + + void (*get_config)(VirtIODevice *vdev, uint8_t *config); +} VMAppleVirtIOBlkClass; + +typedef struct VMAppleVirtIOBlk { + VirtIOBlock parent_obj; + + uint32_t apple_type; +} VMAppleVirtIOBlk; + +/* + * vmapple-virtio-blk-pci: This extends VirtioPCIProxy. + */ +OBJECT_DECLARE_SIMPLE_TYPE(VMAppleVirtIOBlkPCI, VMAPPLE_VIRTIO_BLK_PCI) + +#define VIRTIO_BLK_T_APPLE_BARRIER 0x10000 + +static bool vmapple_virtio_blk_handle_unknown_request(VirtIOBlockReq *req, + MultiReqBuffer *mrb, + uint32_t type) +{ + switch (type) { + case VIRTIO_BLK_T_APPLE_BARRIER: + qemu_log_mask(LOG_UNIMP, "%s: Barrier requests are currently no-ops\n", + __func__); + virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); + g_free(req); + return true; + default: + return false; + } +} + +/* + * VMApple virtio-blk uses the same config format as normal virtio, with one + * exception: It adds an "apple type" specififer at the same location that + * the spec reserves for max_secure_erase_sectors. Let's hook into the + * get_config code path here, run it as usual and then patch in the apple type. + */ +static void vmapple_virtio_blk_get_config(VirtIODevice *vdev, uint8_t *config) +{ + VMAppleVirtIOBlk *dev = VMAPPLE_VIRTIO_BLK(vdev); + VMAppleVirtIOBlkClass *vvbk = VMAPPLE_VIRTIO_BLK_GET_CLASS(dev); + struct virtio_blk_config *blkcfg = (struct virtio_blk_config *)config; + + vvbk->get_config(vdev, config); + + g_assert(dev->parent_obj.config_size >= endof(struct virtio_blk_config, zoned)); + + /* Apple abuses the field for max_secure_erase_sectors as type id */ + stl_he_p(&blkcfg->max_secure_erase_sectors, dev->apple_type); +} + +static void vmapple_virtio_blk_class_init(ObjectClass *klass, const void *data) +{ + VirtIOBlkClass *vbk = VIRTIO_BLK_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + VMAppleVirtIOBlkClass *vvbk = VMAPPLE_VIRTIO_BLK_CLASS(klass); + + vbk->handle_unknown_request = vmapple_virtio_blk_handle_unknown_request; + vvbk->get_config = vdc->get_config; + vdc->get_config = vmapple_virtio_blk_get_config; +} + +static const TypeInfo vmapple_virtio_blk_info = { + .name = TYPE_VMAPPLE_VIRTIO_BLK, + .parent = TYPE_VIRTIO_BLK, + .instance_size = sizeof(VMAppleVirtIOBlk), + .class_size = sizeof(VMAppleVirtIOBlkClass), + .class_init = vmapple_virtio_blk_class_init, +}; + +/* PCI Devices */ + +struct VMAppleVirtIOBlkPCI { + VirtIOPCIProxy parent_obj; + + VMAppleVirtIOBlk vdev; + VMAppleVirtioBlkVariant variant; +}; + +static const Property vmapple_virtio_blk_pci_properties[] = { + DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), + DEFINE_PROP_VMAPPLE_VIRTIO_BLK_VARIANT("variant", VMAppleVirtIOBlkPCI, variant, + VM_APPLE_VIRTIO_BLK_VARIANT_UNSPECIFIED), +}; + +static void vmapple_virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + ERRP_GUARD(); + VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + VirtIOBlkConf *conf = &dev->vdev.parent_obj.conf; + + if (dev->variant == VM_APPLE_VIRTIO_BLK_VARIANT_UNSPECIFIED) { + error_setg(errp, "vmapple virtio block device variant unspecified"); + error_append_hint(errp, + "Variant property must be set to 'aux' or 'root'.\n" + "Use a regular virtio-blk-pci device instead when " + "neither is applicaple.\n"); + return; + } + + if (conf->num_queues == VIRTIO_BLK_AUTO_NUM_QUEUES) { + conf->num_queues = virtio_pci_optimal_num_queues(0); + } + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = conf->num_queues + 1; + } + + /* + * We don't support zones, but we need the additional config space size. + * Let's just expose the feature so the rest of the virtio-blk logic + * allocates enough space for us. The guest will ignore zones anyway. + */ + virtio_add_feature(&dev->vdev.parent_obj.host_features, VIRTIO_BLK_F_ZONED); + /* Propagate the apple type down to the virtio-blk device */ + dev->vdev.apple_type = dev->variant; + /* and spawn the virtio-blk device */ + qdev_realize(vdev, BUS(&vpci_dev->bus), errp); + + /* + * The virtio-pci machinery adjusts its vendor/device ID based on whether + * we support modern or legacy virtio. Let's patch it back to the Apple + * identifiers here. + */ + pci_config_set_vendor_id(vpci_dev->pci_dev.config, PCI_VENDOR_ID_APPLE); + pci_config_set_device_id(vpci_dev->pci_dev.config, + PCI_DEVICE_ID_APPLE_VIRTIO_BLK); +} + +static void vmapple_virtio_blk_pci_class_init(ObjectClass *klass, + const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + device_class_set_props(dc, vmapple_virtio_blk_pci_properties); + k->realize = vmapple_virtio_blk_pci_realize; + pcidev_k->vendor_id = PCI_VENDOR_ID_APPLE; + pcidev_k->device_id = PCI_DEVICE_ID_APPLE_VIRTIO_BLK; + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; + pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; +} + +static void vmapple_virtio_blk_pci_instance_init(Object *obj) +{ + VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VMAPPLE_VIRTIO_BLK); +} + +static const VirtioPCIDeviceTypeInfo vmapple_virtio_blk_pci_info = { + .generic_name = TYPE_VMAPPLE_VIRTIO_BLK_PCI, + .instance_size = sizeof(VMAppleVirtIOBlkPCI), + .instance_init = vmapple_virtio_blk_pci_instance_init, + .class_init = vmapple_virtio_blk_pci_class_init, +}; + +static void vmapple_virtio_blk_register_types(void) +{ + type_register_static(&vmapple_virtio_blk_info); + virtio_pci_types_register(&vmapple_virtio_blk_pci_info); +} + +type_init(vmapple_virtio_blk_register_types) diff --git a/hw/vmapple/vmapple.c b/hw/vmapple/vmapple.c new file mode 100644 index 00000000000..16e6110b68f --- /dev/null +++ b/hw/vmapple/vmapple.c @@ -0,0 +1,618 @@ +/* + * VMApple machine emulation + * + * 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. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * VMApple is the device model that the macOS built-in hypervisor called + * "Virtualization.framework" exposes to Apple Silicon macOS guests. The + * machine model in this file implements the same device model in QEMU, but + * does not use any code from Virtualization.Framework. + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/datadir.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qemu/help-texts.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/units.h" +#include "monitor/qdev.h" +#include "hw/boards.h" +#include "hw/irq.h" +#include "hw/loader.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "hw/usb.h" +#include "hw/arm/boot.h" +#include "hw/arm/primecell.h" +#include "hw/char/pl011.h" +#include "hw/intc/arm_gic.h" +#include "hw/intc/arm_gicv3_common.h" +#include "hw/misc/pvpanic.h" +#include "hw/pci-host/gpex.h" +#include "hw/usb/hcd-xhci-pci.h" +#include "hw/virtio/virtio-pci.h" +#include "hw/vmapple/vmapple.h" +#include "net/net.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qapi/qapi-visit-common.h" +#include "qobject/qlist.h" +#include "standard-headers/linux/input.h" +#include "system/hvf.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/system.h" + +struct VMAppleMachineState { + MachineState parent; + + Notifier machine_done; + struct arm_boot_info bootinfo; + const MemMapEntry *memmap; + const int *irqmap; + DeviceState *gic; + DeviceState *cfg; + DeviceState *pvpanic; + Notifier powerdown_notifier; + PCIBus *bus; + MemoryRegion fw_mr; + MemoryRegion ecam_alias; + uint64_t uuid; +}; + +#define TYPE_VMAPPLE_MACHINE MACHINE_TYPE_NAME("vmapple") +OBJECT_DECLARE_SIMPLE_TYPE(VMAppleMachineState, VMAPPLE_MACHINE) + +/* Number of external interrupt lines to configure the GIC with */ +#define NUM_IRQS 256 + +enum { + VMAPPLE_FIRMWARE, + VMAPPLE_CONFIG, + VMAPPLE_MEM, + VMAPPLE_GIC_DIST, + VMAPPLE_GIC_REDIST, + VMAPPLE_UART, + VMAPPLE_RTC, + VMAPPLE_PCIE, + VMAPPLE_PCIE_MMIO, + VMAPPLE_PCIE_ECAM, + VMAPPLE_GPIO, + VMAPPLE_PVPANIC, + VMAPPLE_APV_GFX, + VMAPPLE_APV_IOSFC, + VMAPPLE_AES_1, + VMAPPLE_AES_2, + VMAPPLE_BDOOR, + VMAPPLE_MEMMAP_LAST, +}; + +static const MemMapEntry memmap[] = { + [VMAPPLE_FIRMWARE] = { 0x00100000, 0x00100000 }, + [VMAPPLE_CONFIG] = { 0x00400000, 0x00010000 }, + + [VMAPPLE_GIC_DIST] = { 0x10000000, 0x00010000 }, + [VMAPPLE_GIC_REDIST] = { 0x10010000, 0x00400000 }, + + [VMAPPLE_UART] = { 0x20010000, 0x00010000 }, + [VMAPPLE_RTC] = { 0x20050000, 0x00001000 }, + [VMAPPLE_GPIO] = { 0x20060000, 0x00001000 }, + [VMAPPLE_PVPANIC] = { 0x20070000, 0x00000002 }, + [VMAPPLE_BDOOR] = { 0x30000000, 0x00200000 }, + [VMAPPLE_APV_GFX] = { 0x30200000, 0x00010000 }, + [VMAPPLE_APV_IOSFC] = { 0x30210000, 0x00010000 }, + [VMAPPLE_AES_1] = { 0x30220000, 0x00004000 }, + [VMAPPLE_AES_2] = { 0x30230000, 0x00004000 }, + [VMAPPLE_PCIE_ECAM] = { 0x40000000, 0x10000000 }, + [VMAPPLE_PCIE_MMIO] = { 0x50000000, 0x1fff0000 }, + + /* Actual RAM size depends on configuration */ + [VMAPPLE_MEM] = { 0x70000000ULL, GiB}, +}; + +static const int irqmap[] = { + [VMAPPLE_UART] = 1, + [VMAPPLE_RTC] = 2, + [VMAPPLE_GPIO] = 0x5, + [VMAPPLE_APV_IOSFC] = 0x10, + [VMAPPLE_APV_GFX] = 0x11, + [VMAPPLE_AES_1] = 0x12, + [VMAPPLE_PCIE] = 0x20, +}; + +#define GPEX_NUM_IRQS 16 + +static void create_bdif(VMAppleMachineState *vms, MemoryRegion *mem) +{ + DeviceState *bdif; + SysBusDevice *bdif_sb; + DriveInfo *di_aux = drive_get(IF_PFLASH, 0, 0); + DriveInfo *di_root = drive_get(IF_PFLASH, 0, 1); + + if (!di_aux) { + error_report("No AUX device. Please specify one as pflash drive."); + exit(1); + } + + if (!di_root) { + /* Fall back to the first IF_VIRTIO device as root device */ + di_root = drive_get(IF_VIRTIO, 0, 0); + } + + if (!di_root) { + error_report("No root device. Please specify one as virtio drive."); + exit(1); + } + + /* PV backdoor device */ + bdif = qdev_new(TYPE_VMAPPLE_BDIF); + bdif_sb = SYS_BUS_DEVICE(bdif); + sysbus_mmio_map(bdif_sb, 0, vms->memmap[VMAPPLE_BDOOR].base); + + qdev_prop_set_drive(DEVICE(bdif), "aux", blk_by_legacy_dinfo(di_aux)); + qdev_prop_set_drive(DEVICE(bdif), "root", blk_by_legacy_dinfo(di_root)); + + sysbus_realize_and_unref(bdif_sb, &error_fatal); +} + +static void create_pvpanic(VMAppleMachineState *vms, MemoryRegion *mem) +{ + SysBusDevice *pvpanic; + + vms->pvpanic = qdev_new(TYPE_PVPANIC_MMIO_DEVICE); + pvpanic = SYS_BUS_DEVICE(vms->pvpanic); + sysbus_mmio_map(pvpanic, 0, vms->memmap[VMAPPLE_PVPANIC].base); + + sysbus_realize_and_unref(pvpanic, &error_fatal); +} + +static bool create_cfg(VMAppleMachineState *vms, MemoryRegion *mem, + Error **errp) +{ + ERRP_GUARD(); + SysBusDevice *cfg; + MachineState *machine = MACHINE(vms); + uint32_t rnd = 1; + + vms->cfg = qdev_new(TYPE_VMAPPLE_CFG); + cfg = SYS_BUS_DEVICE(vms->cfg); + sysbus_mmio_map(cfg, 0, vms->memmap[VMAPPLE_CONFIG].base); + + qemu_guest_getrandom_nofail(&rnd, sizeof(rnd)); + + qdev_prop_set_uint32(vms->cfg, "nr-cpus", machine->smp.cpus); + qdev_prop_set_uint64(vms->cfg, "ecid", vms->uuid); + qdev_prop_set_uint64(vms->cfg, "ram-size", machine->ram_size); + qdev_prop_set_uint32(vms->cfg, "rnd", rnd); + + if (!sysbus_realize_and_unref(cfg, errp)) { + error_prepend(errp, "Error creating vmapple cfg device: "); + return false; + } + + return true; +} + +static void create_gfx(VMAppleMachineState *vms, MemoryRegion *mem) +{ + int irq_gfx = vms->irqmap[VMAPPLE_APV_GFX]; + int irq_iosfc = vms->irqmap[VMAPPLE_APV_IOSFC]; + SysBusDevice *gfx; + + gfx = SYS_BUS_DEVICE(qdev_new("apple-gfx-mmio")); + sysbus_mmio_map(gfx, 0, vms->memmap[VMAPPLE_APV_GFX].base); + sysbus_mmio_map(gfx, 1, vms->memmap[VMAPPLE_APV_IOSFC].base); + sysbus_connect_irq(gfx, 0, qdev_get_gpio_in(vms->gic, irq_gfx)); + sysbus_connect_irq(gfx, 1, qdev_get_gpio_in(vms->gic, irq_iosfc)); + sysbus_realize_and_unref(gfx, &error_fatal); +} + +static void create_aes(VMAppleMachineState *vms, MemoryRegion *mem) +{ + int irq = vms->irqmap[VMAPPLE_AES_1]; + SysBusDevice *aes; + + aes = SYS_BUS_DEVICE(qdev_new(TYPE_APPLE_AES)); + sysbus_mmio_map(aes, 0, vms->memmap[VMAPPLE_AES_1].base); + sysbus_mmio_map(aes, 1, vms->memmap[VMAPPLE_AES_2].base); + sysbus_connect_irq(aes, 0, qdev_get_gpio_in(vms->gic, irq)); + sysbus_realize_and_unref(aes, &error_fatal); +} + +static int arm_gic_ppi_index(int cpu_nr, int ppi_index) +{ + return NUM_IRQS + cpu_nr * GIC_INTERNAL + ppi_index; +} + +static void create_gic(VMAppleMachineState *vms, MemoryRegion *mem) +{ + MachineState *ms = MACHINE(vms); + /* We create a standalone GIC */ + SysBusDevice *gicbusdev; + QList *redist_region_count; + int i; + unsigned int smp_cpus = ms->smp.cpus; + + vms->gic = qdev_new(gicv3_class_name()); + qdev_prop_set_uint32(vms->gic, "revision", 3); + qdev_prop_set_uint32(vms->gic, "num-cpu", smp_cpus); + /* + * Note that the num-irq property counts both internal and external + * interrupts; there are always 32 of the former (mandated by GIC spec). + */ + qdev_prop_set_uint32(vms->gic, "num-irq", NUM_IRQS + 32); + + uint32_t redist0_capacity = + vms->memmap[VMAPPLE_GIC_REDIST].size / GICV3_REDIST_SIZE; + uint32_t redist0_count = MIN(smp_cpus, redist0_capacity); + + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, redist0_count); + qdev_prop_set_array(vms->gic, "redist-region-count", redist_region_count); + + gicbusdev = SYS_BUS_DEVICE(vms->gic); + sysbus_realize_and_unref(gicbusdev, &error_fatal); + sysbus_mmio_map(gicbusdev, 0, vms->memmap[VMAPPLE_GIC_DIST].base); + sysbus_mmio_map(gicbusdev, 1, vms->memmap[VMAPPLE_GIC_REDIST].base); + + /* + * Wire the outputs from each CPU's generic timer and the GICv3 + * maintenance interrupt signal to the appropriate GIC PPI inputs, + * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. + */ + for (i = 0; i < smp_cpus; i++) { + DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); + + /* Map the virt timer to PPI 27 */ + qdev_connect_gpio_out(cpudev, GTIMER_VIRT, + qdev_get_gpio_in(vms->gic, + arm_gic_ppi_index(i, 27))); + + /* Map the GIC IRQ and FIQ lines to CPU */ + sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(gicbusdev, i + smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + } +} + +static void create_uart(const VMAppleMachineState *vms, int uart, + MemoryRegion *mem, Chardev *chr) +{ + hwaddr base = vms->memmap[uart].base; + int irq = vms->irqmap[uart]; + DeviceState *dev = qdev_new(TYPE_PL011); + SysBusDevice *s = SYS_BUS_DEVICE(dev); + + qdev_prop_set_chr(dev, "chardev", chr); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + memory_region_add_subregion(mem, base, + sysbus_mmio_get_region(s, 0)); + sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq)); +} + +static void create_rtc(const VMAppleMachineState *vms) +{ + hwaddr base = vms->memmap[VMAPPLE_RTC].base; + int irq = vms->irqmap[VMAPPLE_RTC]; + + sysbus_create_simple("pl031", base, qdev_get_gpio_in(vms->gic, irq)); +} + +static DeviceState *gpio_key_dev; +static void vmapple_powerdown_req(Notifier *n, void *opaque) +{ + /* use gpio Pin 3 for power button event */ + qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1); +} + +static void create_gpio_devices(const VMAppleMachineState *vms, int gpio, + MemoryRegion *mem) +{ + DeviceState *pl061_dev; + hwaddr base = vms->memmap[gpio].base; + int irq = vms->irqmap[gpio]; + SysBusDevice *s; + + pl061_dev = qdev_new("pl061"); + /* Pull lines down to 0 if not driven by the PL061 */ + qdev_prop_set_uint32(pl061_dev, "pullups", 0); + qdev_prop_set_uint32(pl061_dev, "pulldowns", 0xff); + s = SYS_BUS_DEVICE(pl061_dev); + sysbus_realize_and_unref(s, &error_fatal); + memory_region_add_subregion(mem, base, sysbus_mmio_get_region(s, 0)); + sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq)); + gpio_key_dev = sysbus_create_simple("gpio-key", -1, + qdev_get_gpio_in(pl061_dev, 3)); +} + +static void vmapple_firmware_init(VMAppleMachineState *vms, + MemoryRegion *sysmem) +{ + hwaddr size = vms->memmap[VMAPPLE_FIRMWARE].size; + hwaddr base = vms->memmap[VMAPPLE_FIRMWARE].base; + const char *bios_name; + int image_size; + char *fname; + + bios_name = MACHINE(vms)->firmware; + if (!bios_name) { + error_report("No firmware specified"); + exit(1); + } + + fname = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!fname) { + error_report("Could not find ROM image '%s'", bios_name); + exit(1); + } + + memory_region_init_ram(&vms->fw_mr, NULL, "firmware", size, &error_fatal); + image_size = load_image_mr(fname, &vms->fw_mr); + + g_free(fname); + if (image_size < 0) { + error_report("Could not load ROM image '%s'", bios_name); + exit(1); + } + + memory_region_add_subregion(get_system_memory(), base, &vms->fw_mr); +} + +static void create_pcie(VMAppleMachineState *vms) +{ + hwaddr base_mmio = vms->memmap[VMAPPLE_PCIE_MMIO].base; + hwaddr size_mmio = vms->memmap[VMAPPLE_PCIE_MMIO].size; + hwaddr base_ecam = vms->memmap[VMAPPLE_PCIE_ECAM].base; + hwaddr size_ecam = vms->memmap[VMAPPLE_PCIE_ECAM].size; + int irq = vms->irqmap[VMAPPLE_PCIE]; + MemoryRegion *mmio_alias; + MemoryRegion *mmio_reg; + MemoryRegion *ecam_reg; + DeviceState *dev; + int i; + PCIHostState *pci; + DeviceState *usb_controller; + USBBus *usb_bus; + + dev = qdev_new(TYPE_GPEX_HOST); + qdev_prop_set_uint32(dev, "num-irqs", GPEX_NUM_IRQS); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + /* Map only the first size_ecam bytes of ECAM space */ + ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + memory_region_init_alias(&vms->ecam_alias, OBJECT(dev), "pcie-ecam", + ecam_reg, 0, size_ecam); + memory_region_add_subregion(get_system_memory(), base_ecam, + &vms->ecam_alias); + + /* + * Map the MMIO window from [0x50000000-0x7fff0000] in PCI space into + * system address space at [0x50000000-0x7fff0000]. + */ + mmio_alias = g_new0(MemoryRegion, 1); + mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); + memory_region_init_alias(mmio_alias, OBJECT(dev), "pcie-mmio", + mmio_reg, base_mmio, size_mmio); + memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias); + + for (i = 0; i < GPEX_NUM_IRQS; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, + qdev_get_gpio_in(vms->gic, irq + i)); + gpex_set_irq_num(GPEX_HOST(dev), i, irq + i); + } + + pci = PCI_HOST_BRIDGE(dev); + vms->bus = pci->bus; + g_assert(vms->bus); + + while ((dev = qemu_create_nic_device("virtio-net-pci", true, NULL))) { + qdev_realize_and_unref(dev, BUS(vms->bus), &error_fatal); + } + + if (defaults_enabled()) { + usb_controller = qdev_new(TYPE_QEMU_XHCI); + qdev_realize_and_unref(usb_controller, BUS(pci->bus), &error_fatal); + + usb_bus = USB_BUS(object_resolve_type_unambiguous(TYPE_USB_BUS, + &error_fatal)); + usb_create_simple(usb_bus, "usb-kbd"); + usb_create_simple(usb_bus, "usb-tablet"); + } +} + +static void vmapple_reset(void *opaque) +{ + VMAppleMachineState *vms = opaque; + hwaddr base = vms->memmap[VMAPPLE_FIRMWARE].base; + + cpu_set_pc(first_cpu, base); +} + +static void mach_vmapple_init(MachineState *machine) +{ + VMAppleMachineState *vms = VMAPPLE_MACHINE(machine); + MachineClass *mc = MACHINE_GET_CLASS(machine); + const CPUArchIdList *possible_cpus; + MemoryRegion *sysmem = get_system_memory(); + int n; + unsigned int smp_cpus = machine->smp.cpus; + unsigned int max_cpus = machine->smp.max_cpus; + + vms->memmap = memmap; + machine->usb = true; + + possible_cpus = mc->possible_cpu_arch_ids(machine); + assert(possible_cpus->len == max_cpus); + for (n = 0; n < possible_cpus->len; n++) { + Object *cpu; + CPUState *cs; + + if (n >= smp_cpus) { + break; + } + + cpu = object_new(possible_cpus->cpus[n].type); + object_property_set_int(cpu, "mp-affinity", + possible_cpus->cpus[n].arch_id, &error_fatal); + + cs = CPU(cpu); + cs->cpu_index = n; + + numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpu), + &error_fatal); + + if (object_property_find(cpu, "has_el3")) { + object_property_set_bool(cpu, "has_el3", false, &error_fatal); + } + if (object_property_find(cpu, "has_el2")) { + object_property_set_bool(cpu, "has_el2", false, &error_fatal); + } + object_property_set_int(cpu, "psci-conduit", QEMU_PSCI_CONDUIT_HVC, + &error_fatal); + + /* Secondary CPUs start in PSCI powered-down state */ + if (n > 0) { + object_property_set_bool(cpu, "start-powered-off", true, + &error_fatal); + } + + object_property_set_link(cpu, "memory", OBJECT(sysmem), &error_abort); + qdev_realize(DEVICE(cpu), NULL, &error_fatal); + object_unref(cpu); + } + + memory_region_add_subregion(sysmem, vms->memmap[VMAPPLE_MEM].base, + machine->ram); + + create_gic(vms, sysmem); + create_bdif(vms, sysmem); + create_pvpanic(vms, sysmem); + create_aes(vms, sysmem); + create_gfx(vms, sysmem); + create_uart(vms, VMAPPLE_UART, sysmem, serial_hd(0)); + create_rtc(vms); + create_pcie(vms); + + create_gpio_devices(vms, VMAPPLE_GPIO, sysmem); + + vmapple_firmware_init(vms, sysmem); + create_cfg(vms, sysmem, &error_fatal); + + /* connect powerdown request */ + vms->powerdown_notifier.notify = vmapple_powerdown_req; + qemu_register_powerdown_notifier(&vms->powerdown_notifier); + + vms->bootinfo.ram_size = machine->ram_size; + vms->bootinfo.board_id = -1; + vms->bootinfo.loader_start = vms->memmap[VMAPPLE_MEM].base; + vms->bootinfo.skip_dtb_autoload = true; + vms->bootinfo.firmware_loaded = true; + arm_load_kernel(ARM_CPU(first_cpu), machine, &vms->bootinfo); + + qemu_register_reset(vmapple_reset, vms); +} + +static CpuInstanceProperties +vmapple_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 vmapple_get_default_cpu_node_id(const MachineState *ms, int idx) +{ + return idx % ms->numa_state->num_nodes; +} + +static const CPUArchIdList *vmapple_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 = + arm_build_mp_affinity(n, GICV3_TARGETLIST_BITS); + ms->possible_cpus->cpus[n].props.has_thread_id = true; + ms->possible_cpus->cpus[n].props.thread_id = n; + } + return ms->possible_cpus; +} + +static GlobalProperty vmapple_compat_defaults[] = { + { TYPE_VIRTIO_PCI, "disable-legacy", "on" }, + /* + * macOS XHCI driver attempts to schedule events onto even rings 1 & 2 + * even when (as here) there is no MSI(-X) support. Disabling interrupter + * mapping in the XHCI controller works around the problem. + */ + { TYPE_XHCI_PCI, "conditional-intr-mapping", "on" }, +}; + +static void vmapple_machine_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = mach_vmapple_init; + mc->max_cpus = 32; + mc->block_default_type = IF_VIRTIO; + mc->no_cdrom = 1; + mc->pci_allow_0_address = true; + mc->minimum_page_bits = 12; + mc->possible_cpu_arch_ids = vmapple_possible_cpu_arch_ids; + mc->cpu_index_to_instance_props = vmapple_cpu_index_to_props; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("host"); + mc->get_default_cpu_node_id = vmapple_get_default_cpu_node_id; + mc->default_ram_id = "mach-vmapple.ram"; + mc->desc = "Apple aarch64 Virtual Machine"; + + compat_props_add(mc->compat_props, vmapple_compat_defaults, + G_N_ELEMENTS(vmapple_compat_defaults)); +} + +static void vmapple_instance_init(Object *obj) +{ + VMAppleMachineState *vms = VMAPPLE_MACHINE(obj); + + vms->irqmap = irqmap; + + object_property_add_uint64_ptr(obj, "uuid", &vms->uuid, + OBJ_PROP_FLAG_READWRITE); + object_property_set_description(obj, "uuid", "Machine UUID (SDOM)"); +} + +static const TypeInfo vmapple_machine_info = { + .name = TYPE_VMAPPLE_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(VMAppleMachineState), + .class_init = vmapple_machine_class_init, + .instance_init = vmapple_instance_init, +}; + +static void machvmapple_machine_init(void) +{ + type_register_static(&vmapple_machine_info); +} +type_init(machvmapple_machine_init); + diff --git a/hw/watchdog/allwinner-wdt.c b/hw/watchdog/allwinner-wdt.c index 6205765efec..8fcd7766752 100644 --- a/hw/watchdog/allwinner-wdt.c +++ b/hw/watchdog/allwinner-wdt.c @@ -28,7 +28,7 @@ #include "hw/sysbus.h" #include "hw/registerfields.h" #include "hw/watchdog/allwinner-wdt.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "migration/vmstate.h" /* WDT registers */ @@ -275,7 +275,7 @@ static void allwinner_wdt_write(void *opaque, hwaddr offset, static const MemoryRegionOps allwinner_wdt_ops = { .read = allwinner_wdt_read, .write = allwinner_wdt_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -313,7 +313,7 @@ static const VMStateDescription allwinner_wdt_vmstate = { .name = "allwinner-wdt", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PTIMER(timer, AwWdtState), VMSTATE_UINT32_ARRAY(regs, AwWdtState, AW_WDT_REGS_NUM), VMSTATE_END_OF_LIST() @@ -348,7 +348,7 @@ static void allwinner_wdt_realize(DeviceState *dev, Error **errp) ptimer_transaction_commit(s->timer); } -static void allwinner_wdt_class_init(ObjectClass *klass, void *data) +static void allwinner_wdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -358,7 +358,7 @@ static void allwinner_wdt_class_init(ObjectClass *klass, void *data) dc->vmsd = &allwinner_wdt_vmstate; } -static void allwinner_wdt_sun4i_class_init(ObjectClass *klass, void *data) +static void allwinner_wdt_sun4i_class_init(ObjectClass *klass, const void *data) { AwWdtClass *awc = AW_WDT_CLASS(klass); @@ -371,7 +371,7 @@ static void allwinner_wdt_sun4i_class_init(ObjectClass *klass, void *data) awc->get_intv_value = allwinner_wdt_sun4i_get_intv_value; } -static void allwinner_wdt_sun6i_class_init(ObjectClass *klass, void *data) +static void allwinner_wdt_sun6i_class_init(ObjectClass *klass, const void *data) { AwWdtClass *awc = AW_WDT_CLASS(klass); diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c index 5a2cd46eb76..6a8d07ca569 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: @@ -25,7 +25,7 @@ #include "trace.h" #include "qapi/error.h" #include "qemu/module.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "hw/sysbus.h" #include "hw/irq.h" #include "hw/qdev-properties.h" @@ -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), @@ -374,13 +394,13 @@ static const VMStateDescription cmsdk_apb_watchdog_vmstate = { } }; -static void cmsdk_apb_watchdog_class_init(ObjectClass *klass, void *data) +static void cmsdk_apb_watchdog_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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/sbsa_gwdt.c b/hw/watchdog/sbsa_gwdt.c index 7aa57a8c514..ce84849df0e 100644 --- a/hw/watchdog/sbsa_gwdt.c +++ b/hw/watchdog/sbsa_gwdt.c @@ -16,8 +16,9 @@ */ #include "qemu/osdep.h" -#include "sysemu/reset.h" -#include "sysemu/watchdog.h" +#include "system/reset.h" +#include "system/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) && @@ -173,7 +174,6 @@ static void sbsa_gwdt_write(void *opaque, hwaddr offset, uint64_t data, qemu_log_mask(LOG_GUEST_ERROR, "bad address in control frame write :" " 0x%x\n", (int)offset); } - return; } static void wdt_sbsa_gwdt_reset(DeviceState *dev) @@ -261,16 +261,27 @@ static void wdt_sbsa_gwdt_realize(DeviceState *dev, Error **errp) dev); } -static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, void *data) +static const 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), +}; + +static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, const 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 55ff1f03c1d..5b3f50de3a1 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), @@ -249,7 +249,7 @@ static void spapr_wdt_realize(DeviceState *dev, Error **errp) &w->leave_others, OBJ_PROP_FLAG_READ); } -static void spapr_wdt_class_init(ObjectClass *oc, void *data) +static void spapr_wdt_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events index 2739570652b..ad3be1e9bdd 100644 --- a/hw/watchdog/trace-events +++ b/hw/watchdog/trace-events @@ -17,6 +17,12 @@ 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 diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c index 955046161bf..07213739488 100644 --- a/hw/watchdog/watchdog.c +++ b/hw/watchdog/watchdog.c @@ -26,8 +26,8 @@ #include "qapi/error.h" #include "qapi/qapi-commands-run-state.h" #include "qapi/qapi-events-run-state.h" -#include "sysemu/runstate.h" -#include "sysemu/watchdog.h" +#include "system/runstate.h" +#include "system/watchdog.h" #include "hw/nmi.h" #include "qemu/help_option.h" #include "trace.h" @@ -85,7 +85,7 @@ void watchdog_perform_action(void) break; default: - assert(0); + g_assert_not_reached(); } } diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c index d267aa185c1..30226435efc 100644 --- a/hw/watchdog/wdt_aspeed.c +++ b/hw/watchdog/wdt_aspeed.c @@ -13,8 +13,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qemu/timer.h" -#include "sysemu/watchdog.h" -#include "hw/misc/aspeed_scu.h" +#include "system/watchdog.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "hw/watchdog/wdt_aspeed.h" @@ -52,11 +51,20 @@ #define WDT_TIMEOUT_CLEAR (0x14 / 4) #define WDT_RESTART_MAGIC 0x4755 +#define WDT_SW_RESET_ENABLE 0xAEEDF123 #define AST2600_SCU_RESET_CONTROL1 (0x40 / 4) #define SCU_RESET_CONTROL1 (0x04 / 4) #define SCU_RESET_SDRAM BIT(0) +static bool aspeed_wdt_is_soc_reset_mode(const AspeedWDTState *s) +{ + uint32_t mode; + + mode = extract32(s->regs[WDT_CTRL], 5, 2); + return (mode == WDT_CTRL_RESET_MODE_SOC); +} + static bool aspeed_wdt_is_enabled(const AspeedWDTState *s) { return s->regs[WDT_CTRL] & WDT_CTRL_ENABLE; @@ -200,26 +208,30 @@ 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); break; + case WDT_SW_RESET_CTRL: + if (aspeed_wdt_is_soc_reset_mode(s) && + (data == WDT_SW_RESET_ENABLE)) { + watchdog_perform_action(); + } + break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", __func__, offset); } - return; } 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() @@ -279,7 +291,8 @@ static void aspeed_wdt_realize(DeviceState *dev, Error **errp) s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, aspeed_wdt_timer_expired, dev); - /* FIXME: This setting should be derived from the SCU hw strapping + /* + * FIXME: This setting should be derived from the SCU hw strapping * register SCU70 */ s->pclk_freq = PCLK_HZ; @@ -289,19 +302,18 @@ static void aspeed_wdt_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); } -static Property aspeed_wdt_properties[] = { +static const Property aspeed_wdt_properties[] = { DEFINE_PROP_LINK("scu", AspeedWDTState, scu, TYPE_ASPEED_SCU, AspeedSCUState *), - DEFINE_PROP_END_OF_LIST(), }; -static void aspeed_wdt_class_init(ObjectClass *klass, void *data) +static void aspeed_wdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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); @@ -317,7 +329,7 @@ static const TypeInfo aspeed_wdt_info = { .abstract = true, }; -static void aspeed_2400_wdt_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_wdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); @@ -354,7 +366,7 @@ static void aspeed_2500_wdt_reset_pulse(AspeedWDTState *s, uint32_t property) } } -static void aspeed_2500_wdt_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_wdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); @@ -377,7 +389,7 @@ static const TypeInfo aspeed_2500_wdt_info = { .class_init = aspeed_2500_wdt_class_init, }; -static void aspeed_2600_wdt_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_wdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); @@ -400,7 +412,7 @@ static const TypeInfo aspeed_2600_wdt_info = { .class_init = aspeed_2600_wdt_class_init, }; -static void aspeed_1030_wdt_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_wdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); @@ -423,12 +435,36 @@ static const TypeInfo aspeed_1030_wdt_info = { .class_init = aspeed_1030_wdt_class_init, }; +static void aspeed_2700_wdt_class_init(ObjectClass *klass, const 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 76d89fbf785..1275353e8e5 100644 --- a/hw/watchdog/wdt_diag288.c +++ b/hw/watchdog/wdt_diag288.c @@ -12,8 +12,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/reset.h" -#include "sysemu/watchdog.h" +#include "system/reset.h" +#include "system/watchdog.h" #include "qemu/timer.h" #include "hw/watchdog/wdt_diag288.h" #include "migration/vmstate.h" @@ -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() @@ -108,14 +108,14 @@ static void wdt_diag288_unrealize(DeviceState *dev) timer_free(diag288->timer); } -static void wdt_diag288_class_init(ObjectClass *klass, void *data) +static void wdt_diag288_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); DIAG288Class *diag288 = DIAG288_CLASS(klass); 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 54c167cd358..bb8a2766b69 100644 --- a/hw/watchdog/wdt_i6300esb.c +++ b/hw/watchdog/wdt_i6300esb.c @@ -23,7 +23,7 @@ #include "qemu/module.h" #include "qemu/timer.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "hw/pci/pci_device.h" #include "migration/vmstate.h" #include "qom/object.h" @@ -418,7 +418,7 @@ static const VMStateDescription vmstate_i6300esb = { */ .version_id = 10000, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(dev, I6300State), VMSTATE_INT32(reboot_enabled, I6300State), VMSTATE_INT32(clock_scale, I6300State), @@ -457,7 +457,7 @@ static void i6300esb_exit(PCIDevice *dev) timer_free(d->timer); } -static void i6300esb_class_init(ObjectClass *klass, void *data) +static void i6300esb_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -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"; @@ -480,7 +480,7 @@ static const TypeInfo i6300esb_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(I6300State), .class_init = i6300esb_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/watchdog/wdt_ib700.c b/hw/watchdog/wdt_ib700.c index a1750a4957f..51a26a4cbbc 100644 --- a/hw/watchdog/wdt_ib700.c +++ b/hw/watchdog/wdt_ib700.c @@ -22,7 +22,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "qemu/timer.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "hw/isa/isa.h" #include "migration/vmstate.h" #include "qom/object.h" @@ -95,7 +95,7 @@ static const VMStateDescription vmstate_ib700 = { .name = "ib700_wdt", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(timer, IB700State), VMSTATE_END_OF_LIST() } @@ -128,12 +128,12 @@ static void wdt_ib700_reset(DeviceState *dev) timer_del(s->timer); } -static void wdt_ib700_class_init(ObjectClass *klass, void *data) +static void wdt_ib700_class_init(ObjectClass *klass, const 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 e776a2fbd40..10151a15d08 100644 --- a/hw/watchdog/wdt_imx2.c +++ b/hw/watchdog/wdt_imx2.c @@ -12,16 +12,19 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "qemu/module.h" -#include "sysemu/watchdog.h" +#include "system/watchdog.h" #include "migration/vmstate.h" #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), @@ -266,19 +281,18 @@ static void imx2_wdt_realize(DeviceState *dev, Error **errp) } } -static Property imx2_wdt_properties[] = { +static const Property imx2_wdt_properties[] = { DEFINE_PROP_BOOL("pretimeout-support", IMX2WdtState, pretimeout_support, false), - DEFINE_PROP_END_OF_LIST() }; -static void imx2_wdt_class_init(ObjectClass *klass, void *data) +static void imx2_wdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); 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/xen/meson.build b/hw/xen/meson.build index 277f9f292b4..a1850e76988 100644 --- a/hw/xen/meson.build +++ b/hw/xen/meson.build @@ -9,9 +9,17 @@ system_ss.add(when: ['CONFIG_XEN_BUS'], if_true: files( system_ss.add(when: ['CONFIG_XEN', xen], if_true: files( 'xen-operations.c', +), +if_false: files( + 'xen_stubs.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', @@ -26,10 +34,3 @@ else endif specific_ss.add_all(when: ['CONFIG_XEN', xen], if_true: xen_specific_ss) - -xen_ss = ss.source_set() - -xen_ss.add(when: 'CONFIG_XEN', if_true: files( - 'xen-mapcache.c', - 'xen-hvm-common.c', -)) diff --git a/hw/xen/trace-events b/hw/xen/trace-events index 67a6c419260..b67942d07b4 100644 --- a/hw/xen/trace-events +++ b/hw/xen/trace-events @@ -38,11 +38,11 @@ xen_device_remove_watch(const char *type, char *name, const char *node, const ch xs_node_create(const char *node) "%s" xs_node_destroy(const char *node) "%s" xs_node_vprintf(char *path, char *value) "%s %s" -xs_node_vscanf(char *path, char *value) "%s %s" +xs_node_read(const char *path, const char *value) "%s %s" xs_node_watch(char *path) "%s" xs_node_unwatch(char *path) "%s" -# xen-hvm.c +# 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" @@ -55,8 +55,31 @@ cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint6 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 5b0fb76eaeb..b9bf70a9f52 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 b2b2cc9c5d5..288fad422be 100644 --- a/hw/xen/xen-bus-helper.c +++ b/hw/xen/xen-bus-helper.c @@ -105,25 +105,22 @@ 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) { - char *path, *value; + char *value; int rc; - path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) : - g_strdup(key); - value = qemu_xen_xs_read(h, tid, path, NULL); - - trace_xs_node_vscanf(path, value); + if (node && strlen(node) != 0) { + value = xs_node_read(h, tid, NULL, errp, "%s/%s", node, key); + } else { + value = xs_node_read(h, tid, NULL, errp, "%s", key); + } if (value) { rc = vsscanf(value, fmt, ap); } else { - error_setg_errno(errp, errno, "failed to read from '%s'", - path); rc = EOF; } free(value); - g_free(path); return rc; } @@ -142,6 +139,28 @@ int xs_node_scanf(struct qemu_xs_handle *h, xs_transaction_t tid, return rc; } +char *xs_node_read(struct qemu_xs_handle *h, xs_transaction_t tid, + unsigned int *len, Error **errp, + const char *path_fmt, ...) +{ + char *path, *value; + va_list ap; + + va_start(ap, path_fmt); + path = g_strdup_vprintf(path_fmt, ap); + va_end(ap); + + value = qemu_xen_xs_read(h, tid, path, len); + trace_xs_node_read(path, value); + if (!value) { + error_setg_errno(errp, errno, "failed to read from '%s'", path); + } + + g_free(path); + + return value; +} + 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) diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index ece8ec40cd8..6bd2e546f6b 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 "qobject/qdict.h" +#include "system/system.h" +#include "net/net.h" #include "trace.h" static char *xen_device_get_backend_path(XenDevice *xendev) @@ -154,8 +156,8 @@ static void xen_bus_backend_create(XenBus *xenbus, const char *type, !strcmp(key[i], "hotplug-status")) continue; - if (xs_node_scanf(xenbus->xsh, tid, path, key[i], NULL, "%ms", - &val) == 1) { + val = xs_node_read(xenbus->xsh, tid, NULL, NULL, "%s/%s", path, key[i]); + if (val) { qdict_put_str(opts, key[i], val); free(val); } @@ -209,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, @@ -327,6 +330,9 @@ static void xen_bus_realize(BusState *bus, Error **errp) 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; @@ -347,10 +353,10 @@ static void xen_bus_realize(BusState *bus, Error **errp) 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, - "failed to set up '%s' enumeration watch: ", - type[i]); + warn_reportf_err(local_err, + "failed to set up '%s' enumeration watch: ", + type[i]); + local_err = NULL; } g_free(node); @@ -374,7 +380,7 @@ static void xen_bus_unplug_request(HotplugHandler *hotplug, xen_device_unplug(xendev, errp); } -static void xen_bus_class_init(ObjectClass *class, void *data) +static void xen_bus_class_init(ObjectClass *class, const void *data) { BusClass *bus_class = BUS_CLASS(class); HotplugHandlerClass *hotplug_class = HOTPLUG_HANDLER_CLASS(class); @@ -393,7 +399,7 @@ static const TypeInfo xen_bus_type_info = { .instance_size = sizeof(XenBus), .class_size = sizeof(XenBusClass), .class_init = xen_bus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } }, @@ -644,6 +650,16 @@ int xen_device_frontend_scanf(XenDevice *xendev, const char *key, return rc; } +char *xen_device_frontend_read(XenDevice *xendev, const char *key) +{ + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + + g_assert(xenbus->xsh); + + return xs_node_read(xenbus->xsh, XBT_NULL, NULL, NULL, "%s/%s", + xendev->frontend_path, key); +} + static void xen_device_frontend_set_state(XenDevice *xendev, enum xenbus_state state, bool publish) @@ -711,8 +727,17 @@ static void xen_device_frontend_create(XenDevice *xendev, Error **errp) { ERRP_GUARD(); XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + 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 @@ -912,6 +937,11 @@ void xen_device_notify_event_channel(XenDevice *xendev, } } +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) @@ -1072,13 +1102,12 @@ static void xen_device_realize(DeviceState *dev, Error **errp) xen_device_unrealize(dev); } -static Property xen_device_props[] = { +static const Property xen_device_props[] = { DEFINE_PROP_UINT16("frontend-id", XenDevice, frontend_id, DOMID_INVALID), - DEFINE_PROP_END_OF_LIST() }; -static void xen_device_class_init(ObjectClass *class, void *data) +static void xen_device_class_init(ObjectClass *class, const void *data) { DeviceClass *dev_class = DEVICE_CLASS(class); @@ -1125,4 +1154,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 8c6e9a1716a..eaf32f27107 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 4d8d34ecb02..270dcb27f7b 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 index 565dc39c8f6..78e0bc8f644 100644 --- a/hw/xen/xen-hvm-common.c +++ b/hw/xen/xen-hvm-common.c @@ -1,42 +1,63 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/error-report.h" #include "qapi/error.h" +#include "exec/target_long.h" +#include "exec/target_page.h" #include "trace.h" +#include "hw/hw.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" +#include "system/runstate.h" +#include "system/system.h" +#include "system/xen.h" +#include "system/xen-mapcache.h" -MemoryRegion ram_memory; +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 */ - fprintf(stderr, "%s: do not alloc "RAM_ADDR_FMT - " bytes of ram at "RAM_ADDR_FMT" when runstate is INMIGRATE\n", + warn_report("%s: do not alloc "RAM_ADDR_FMT + " bytes of ram at "RAM_ADDR_FMT" when runstate is INMIGRATE", __func__, size, ram_addr); return; } - if (mr == &ram_memory) { + if (xen_mr_is_memory(mr)) { return; } trace_xen_ram_alloc(ram_addr, size); - nr_pfn = size >> TARGET_PAGE_BITS; + 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; + 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)) { @@ -53,7 +74,7 @@ static void xen_set_memory(struct MemoryListener *listener, { XenIOState *state = container_of(listener, XenIOState, memory_listener); - if (section->mr == &ram_memory) { + if (xen_mr_is_memory(section->mr)) { return; } else { if (add) { @@ -169,11 +190,12 @@ 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); + 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; } @@ -460,11 +482,11 @@ static void handle_ioreq(XenIOState *state, ioreq_t *req) } } -static bool handle_buffered_iopage(XenIOState *state) +static unsigned int handle_buffered_iopage(XenIOState *state) { buffered_iopage_t *buf_page = state->buffered_io_page; buf_ioreq_t *buf_req = NULL; - bool handled_ioreq = false; + unsigned int handled = 0; ioreq_t req; int qw; @@ -477,7 +499,7 @@ static bool handle_buffered_iopage(XenIOState *state) req.count = 1; req.dir = IOREQ_WRITE; - for (;;) { + do { uint32_t rdptr = buf_page->read_pointer, wrptr; xen_rmb(); @@ -518,22 +540,30 @@ static bool handle_buffered_iopage(XenIOState *state) assert(!req.data_is_ptr); qatomic_add(&buf_page->read_pointer, qw + 1); - handled_ioreq = true; - } + handled += qw + 1; + } while (handled < IOREQ_BUFFER_SLOT_NUM); - return handled_ioreq; + return handled; } static void handle_buffered_io(void *opaque) { + unsigned int handled; XenIOState *state = opaque; - if (handle_buffered_iopage(state)) { + 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, - BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); - } else { + 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)); } } @@ -551,9 +581,9 @@ static void cpu_handle_ioreq(void *opaque) req->data = copy.data; if (req->state != STATE_IOREQ_INPROCESS) { - fprintf(stderr, "Badness in I/O request ... not in service?!: " + warn_report("Badness in I/O request ... not in service?!: " "%x, ptr: %x, port: %"PRIx64", " - "data: %"PRIx64", count: %u, size: %u, type: %u\n", + "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); @@ -601,10 +631,9 @@ static void xen_main_loop_prepare(XenIOState *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); + 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); @@ -623,7 +652,7 @@ void xen_hvm_change_state_handler(void *opaque, bool running, xen_set_ioreq_server_state(xen_domid, state->ioservid, - (rstate == RUN_STATE_RUNNING)); + running); } void xen_exit_notifier(Notifier *n, void *data) @@ -645,6 +674,8 @@ static int xen_map_ioreq_server(XenIOState *state) 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; /* @@ -653,65 +684,85 @@ static int xen_map_ioreq_server(XenIOState *state) */ 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, 0, 2, + state->ioservid, + frame, num_frames, &addr, PROT_READ | PROT_WRITE, 0); if (state->fres != NULL) { trace_xen_map_resource_ioreq(state->ioservid, addr); - state->buffered_io_page = addr; - state->shared_page = addr + XC_PAGE_SIZE; + 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; } - 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 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 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) { - DPRINTF("shared page at pfn %lx\n", ioreq_pfn); + 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); + state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &ioreq_pfn, NULL); + } if (state->shared_page == NULL) { error_report("map shared IO page returned error %d handle=%p", errno, xen_xc); } - } - if (state->buffered_io_page == NULL) { - DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); + 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; + 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) { + if (state->shared_page == NULL || + (state->has_bufioreq && state->buffered_io_page == NULL)) { return -1; } - DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); - - state->bufioreq_remote_port = bufioreq_evtchn; + if (state->has_bufioreq) { + trace_xen_map_ioreq_server_buffered_io_evtchn(bufioreq_evtchn); + state->bufioreq_remote_port = bufioreq_evtchn; + } return 0; } @@ -737,16 +788,17 @@ void destroy_hvm_domain(bool reboot) xc_handle = xc_interface_open(0, 0, 0); if (xc_handle == NULL) { - fprintf(stderr, "Cannot acquire xenctrl handle\n"); + trace_destroy_hvm_domain_cannot_acquire_handle(); } 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)); + trace_destroy_hvm_domain_failed_action( + reboot ? "reboot" : "poweroff", sts, strerror(errno) + ); } else { - fprintf(stderr, "Issued domain %d %s\n", xen_domid, - reboot ? "reboot" : "poweroff"); + trace_destroy_hvm_domain_action( + xen_domid, reboot ? "reboot" : "poweroff" + ); } xc_interface_close(xc_handle); } @@ -757,9 +809,9 @@ void xen_shutdown_fatal_error(const char *fmt, ...) va_list ap; va_start(ap, fmt); - vfprintf(stderr, fmt, ap); + error_vreport(fmt, ap); va_end(ap); - fprintf(stderr, "Will destroy the domain.\n"); + error_report("Will destroy the domain."); /* destroy the domain */ qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_ERROR); } @@ -807,14 +859,15 @@ static void xen_do_ioreq_register(XenIOState *state, state->ioreq_local_port[i] = rc; } - rc = qemu_xen_evtchn_bind_interdomain(state->xce_handle, xen_domid, - state->bufioreq_remote_port); - if (rc == -1) { - error_report("buffered evtchn bind error %d", errno); - goto err; + 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; } - state->bufioreq_local_port = rc; - /* Init RAM management */ #ifdef XEN_COMPAT_PHYSMAP xen_map_cache_init(xen_phys_offset_to_gaddr, state); @@ -842,6 +895,7 @@ static void xen_do_ioreq_register(XenIOState *state, } void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, + uint8_t handle_bufioreq, const MemoryListener *xen_memory_listener) { int rc; @@ -860,7 +914,8 @@ void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, goto err; } - rc = xen_create_ioreq_server(xen_domid, &state->ioservid); + 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 { @@ -869,8 +924,6 @@ void xen_register_ioreq(XenIOState *state, unsigned int max_cpus, xen_bus_init(); - xen_be_init(); - return; err: diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index 4ded3cec236..5ed53f8943e 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -147,30 +147,12 @@ void xen_be_unmap_grant_refs(struct XenLegacyDevice *xendev, void *ptr, } } -int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev, - bool to_domain, - XenGrantCopySegment segs[], - unsigned int nr_segs) -{ - int rc; - - assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); - - rc = qemu_xen_gnttab_grant_copy(xendev->gnttabdev, to_domain, xen_domid, - segs, nr_segs, NULL); - if (rc) { - xen_pv_printf(xendev, 0, "xengnttab_grant_copy failed: %s\n", - strerror(-rc)); - } - 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; @@ -181,7 +163,7 @@ static struct XenLegacyDevice *xen_be_get_xendev(const char *type, int dom, /* init new xendev */ xendev = g_malloc0(ops->size); - object_initialize(&xendev->qdev, ops->size, TYPE_XENBACKEND); + object_initialize(xendev, ops->size, TYPE_XENBACKEND); OBJECT(xendev)->free = g_free; qdev_set_id(DEVICE(xendev), g_strdup_printf("xen-%s-%d", type, dev), &error_fatal); @@ -520,7 +502,7 @@ void xen_be_check_state(struct XenLegacyDevice *xendev) struct xenstore_be { const char *type; int dom; - struct XenDevOps *ops; + const struct XenDevOps *ops; }; static void xenstore_update_be(void *opaque, const char *watch) @@ -557,7 +539,7 @@ static void xenstore_update_be(void *opaque, const char *watch) } } -static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) +static int xenstore_scan(const char *type, int dom, const struct XenDevOps *ops) { struct XenLegacyDevice *xendev; char path[XEN_BUFSIZE]; @@ -622,28 +604,11 @@ void xen_be_init(void) qbus_set_bus_hotplug_handler(xen_sysbus); 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_register(const char *type, struct XenDevOps *ops) +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); @@ -670,29 +635,22 @@ int xen_be_bind_evtchn(struct XenLegacyDevice *xendev) } -static Property xendev_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - -static void xendev_class_init(ObjectClass *klass, void *data) +static void xendev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - device_class_set_props(dc, xendev_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); - /* xen-backend devices can be plugged/unplugged dynamically */ - dc->user_creatable = true; dc->bus_type = TYPE_XENSYSBUS; } static const TypeInfo xendev_type_info = { .name = TYPE_XENBACKEND, - .parent = TYPE_DEVICE, + .parent = TYPE_DYNAMIC_SYS_BUS_DEVICE, .class_init = xendev_class_init, - .instance_size = sizeof(struct XenLegacyDevice), + .instance_size = sizeof(XenLegacyDevice), }; -static void xen_sysbus_class_init(ObjectClass *klass, void *data) +static void xen_sysbus_class_init(ObjectClass *klass, const void *data) { HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); @@ -703,28 +661,15 @@ static const TypeInfo xensysbus_info = { .name = TYPE_XENSYSBUS, .parent = TYPE_BUS, .class_init = xen_sysbus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } }; -static Property xen_sysdev_properties[] = { - {/* end of property list */}, -}; - -static void xen_sysdev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - device_class_set_props(dc, xen_sysdev_properties); -} - static const TypeInfo xensysdev_info = { .name = TYPE_XENSYSDEV, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusDevice), - .class_init = xen_sysdev_class_init, }; static void xenbe_register_types(void) diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index f7d974677d1..e31d3797025 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -14,32 +14,22 @@ #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 "system/runstate.h" +#include "system/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 +#include +#include #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. @@ -54,6 +44,7 @@ typedef struct MapCacheEntry { 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; @@ -75,7 +66,8 @@ typedef struct MapCache { /* For most cases (>99.9%), the page address is the same. */ MapCacheEntry *last_entry; unsigned long max_mcache_size; - unsigned int mcache_bucket_shift; + unsigned int bucket_shift; + unsigned long bucket_size; phys_offset_to_gaddr_t phys_offset_to_gaddr; QemuMutex lock; @@ -83,15 +75,18 @@ typedef struct MapCache { } MapCache; static MapCache *mapcache; +static MapCache *mapcache_grants_ro; +static MapCache *mapcache_grants_rw; +static xengnttab_handle *xen_region_gnttabdev; -static inline void mapcache_lock(void) +static inline void mapcache_lock(MapCache *mc) { - qemu_mutex_lock(&mapcache->lock); + qemu_mutex_lock(&mc->lock); } -static inline void mapcache_unlock(void) +static inline void mapcache_unlock(MapCache *mc) { - qemu_mutex_unlock(&mapcache->lock); + qemu_mutex_unlock(&mc->lock); } static inline int test_bits(int nr, int size, const unsigned long *addr) @@ -103,23 +98,62 @@ static inline int test_bits(int nr, int size, const unsigned long *addr) return 0; } -void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) +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; - struct rlimit rlimit_as; + MapCache *mc; + + assert(bucket_shift >= XC_PAGE_SHIFT); - mapcache = g_new0(MapCache, 1); + mc = g_new0(MapCache, 1); - mapcache->phys_offset_to_gaddr = f; - mapcache->opaque = opaque; - qemu_mutex_init(&mapcache->lock); + mc->phys_offset_to_gaddr = f; + mc->opaque = opaque; + qemu_mutex_init(&mc->lock); - QTAILQ_INIT(&mapcache->locked_entries); + 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; - mapcache->max_mcache_size = MCACHE_MAX_SIZE; + max_mcache_size = MCACHE_MAX_SIZE; } else { getrlimit(RLIMIT_AS, &rlimit_as); rlimit_as.rlim_cur = rlimit_as.rlim_max; @@ -129,42 +163,54 @@ void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) " 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; + max_mcache_size = rlimit_as.rlim_max - NON_MCACHE_MEMORY_SIZE; } else { - mapcache->max_mcache_size = MCACHE_MAX_SIZE; + max_mcache_size = MCACHE_MAX_SIZE; } } - setrlimit(RLIMIT_AS, &rlimit_as); + mapcache = xen_map_cache_init_single(f, opaque, + bucket_shift, + max_mcache_size); - mapcache->nr_buckets = - (((mapcache->max_mcache_size >> XC_PAGE_SHIFT) + - (1UL << (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)) - 1) >> - (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)); + /* + * Grant mappings must use XC_PAGE_SIZE granularity since we can't + * map anything beyond the number of pages granted to us. + */ + mapcache_grants_ro = xen_map_cache_init_single(f, opaque, + XC_PAGE_SHIFT, + max_mcache_size); + mapcache_grants_rw = xen_map_cache_init_single(f, opaque, + XC_PAGE_SHIFT, + max_mcache_size); - 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); + setrlimit(RLIMIT_AS, &rlimit_as); } -static void xen_remap_bucket(MapCacheEntry *entry, +static void xen_remap_bucket(MapCache *mc, + MapCacheEntry *entry, void *vaddr, hwaddr size, hwaddr address_index, - bool dummy) + bool dummy, + bool grant, + bool is_write, + ram_addr_t ram_offset) { uint8_t *vaddr_base; - xen_pfn_t *pfns; - int *err; + 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); - pfns = g_new0(xen_pfn_t, nb_pfn); + 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) { @@ -193,21 +239,51 @@ static void xen_remap_bucket(MapCacheEntry *entry, 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 (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; + } } - /* - * If the caller has requested the mapping at a specific address use - * MAP_FIXED to make sure it's honored. - */ + entry->flags &= ~XEN_MAPCACHE_ENTRY_GRANT; + if (!dummy) { - vaddr_base = xenforeignmemory_map2(xen_fmem, xen_domid, vaddr, - PROT_READ | PROT_WRITE, - vaddr ? MAP_FIXED : 0, - nb_pfn, pfns, err); + 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("xenforeignmemory_map2"); + perror(grant ? "xengnttab_map_domain_grant_refs" + : "xenforeignmemory_map2"); exit(-1); } } else { @@ -246,13 +322,13 @@ static void xen_remap_bucket(MapCacheEntry *entry, 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) +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; @@ -264,8 +340,8 @@ static uint8_t *xen_map_cache_unlocked(hwaddr phys_addr, hwaddr size, bool dummy = false; tryagain: - address_index = phys_addr >> MCACHE_BUCKET_SHIFT; - address_offset = phys_addr & (MCACHE_BUCKET_SIZE - 1); + address_index = phys_addr >> mc->bucket_shift; + address_offset = phys_addr & (mc->bucket_size - 1); trace_xen_map_cache(phys_addr); @@ -280,34 +356,36 @@ static uint8_t *xen_map_cache_unlocked(hwaddr phys_addr, hwaddr size, test_bit_size = XC_PAGE_SIZE; } - if (mapcache->last_entry != NULL && - mapcache->last_entry->paddr_index == address_index && + 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, - 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; + 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 MCACHE_BUCKET_SIZE */ + /* size is always a multiple of mc->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); + if (cache_size % mc->bucket_size) { + cache_size += mc->bucket_size - (cache_size % mc->bucket_size); } } else { - cache_size = MCACHE_BUCKET_SIZE; + cache_size = mc->bucket_size; } - entry = &mapcache->entry[address_index % mapcache->nr_buckets]; + 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 || + while (entry && (!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) { + if (!free_entry && (!entry->lock || !entry->vaddr_base)) { free_entry = entry; free_pentry = pentry; } @@ -321,24 +399,26 @@ static uint8_t *xen_map_cache_unlocked(hwaddr phys_addr, hwaddr size, if (!entry) { entry = g_new0(MapCacheEntry, 1); pentry->next = entry; - xen_remap_bucket(entry, NULL, cache_size, address_index, dummy); + 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(entry, NULL, cache_size, address_index, dummy); + 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)) { - mapcache->last_entry = NULL; + mc->last_entry = NULL; #ifdef XEN_COMPAT_PHYSMAP - if (!translated && mapcache->phys_offset_to_gaddr) { - phys_addr = mapcache->phys_offset_to_gaddr(phys_addr, size); + if (!translated && mc->phys_offset_to_gaddr) { + phys_addr = mc->phys_offset_to_gaddr(phys_addr, size); translated = true; goto tryagain; } @@ -351,39 +431,62 @@ static uint8_t *xen_map_cache_unlocked(hwaddr phys_addr, hwaddr size, return NULL; } - mapcache->last_entry = entry; + mc->last_entry = entry; if (lock) { MapCacheRev *reventry = g_new0(MapCacheRev, 1); entry->lock++; if (entry->lock == 0) { - fprintf(stderr, - "mapcache entry lock overflow: "HWADDR_FMT_plx" -> %p\n", - entry->paddr_index, entry->vaddr_base); + error_report("mapcache entry lock overflow: "HWADDR_FMT_plx" -> %p", + entry->paddr_index, entry->vaddr_base); abort(); } reventry->dma = dma; - reventry->vaddr_req = mapcache->last_entry->vaddr_base + address_offset; - reventry->paddr_index = mapcache->last_entry->paddr_index; + 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(&mapcache->locked_entries, reventry, next); + QTAILQ_INSERT_HEAD(&mc->locked_entries, reventry, next); } - trace_xen_map_cache_return(mapcache->last_entry->vaddr_base + address_offset); - return mapcache->last_entry->vaddr_base + address_offset; + 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(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) { + bool grant = xen_mr_is_grants(mr); + MapCache *mc = mapcache; uint8_t *p; - mapcache_lock(); - p = xen_map_cache_unlocked(phys_addr, size, lock, dma); - mapcache_unlock(); + if (grant) { + mc = is_write ? mapcache_grants_rw : mapcache_grants_ro; + } + + 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; } -ram_addr_t xen_ram_addr_from_mapcache(void *ptr) +static ram_addr_t xen_ram_addr_from_mapcache_single(MapCache *mc, void *ptr) { MapCacheEntry *entry = NULL; MapCacheRev *reventry; @@ -392,8 +495,8 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) ram_addr_t raddr; int found = 0; - mapcache_lock(); - QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { + mapcache_lock(mc); + QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { if (reventry->vaddr_req == ptr) { paddr_index = reventry->paddr_index; size = reventry->size; @@ -402,39 +505,52 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr) } } if (!found) { - fprintf(stderr, "%s, could not find %p\n", __func__, ptr); - QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { - DPRINTF(" "HWADDR_FMT_plx" -> %p is present\n", reventry->paddr_index, - reventry->vaddr_req); - } - abort(); - return 0; + trace_xen_ram_addr_from_mapcache_not_found(ptr); + mapcache_unlock(mc); + return RAM_ADDR_INVALID; } - entry = &mapcache->entry[paddr_index % mapcache->nr_buckets]; + entry = &mc->entry[paddr_index % mc->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; + trace_xen_ram_addr_from_mapcache_not_in_cache(ptr); + raddr = RAM_ADDR_INVALID; } else { - raddr = (reventry->paddr_index << MCACHE_BUCKET_SHIFT) + + raddr = (reventry->paddr_index << mc->bucket_shift) + ((unsigned long) ptr - (unsigned long) entry->vaddr_base); } - mapcache_unlock(); + mapcache_unlock(mc); return raddr; } -static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer) +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_ro, ptr); + } + if (addr == RAM_ADDR_INVALID) { + addr = xen_ram_addr_from_mapcache_single(mapcache_grants_rw, 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, &mapcache->locked_entries, next) { + QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { if (reventry->vaddr_req == buffer) { paddr_index = reventry->paddr_index; size = reventry->size; @@ -443,72 +559,128 @@ static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer) } } if (!found) { - DPRINTF("%s, could not find %p\n", __func__, buffer); - QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { - DPRINTF(" "HWADDR_FMT_plx" -> %p is present\n", reventry->paddr_index, reventry->vaddr_req); + 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(&mapcache->locked_entries, reventry, next); + QTAILQ_REMOVE(&mc->locked_entries, reventry, next); g_free(reventry); - if (mapcache->last_entry != NULL && - mapcache->last_entry->paddr_index == paddr_index) { - mapcache->last_entry = NULL; + if (mc->last_entry != NULL && + mc->last_entry->paddr_index == paddr_index) { + mc->last_entry = NULL; } - entry = &mapcache->entry[paddr_index % mapcache->nr_buckets]; + 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) { - DPRINTF("Trying to unmap address %p that is not in the mapcache!\n", buffer); + trace_xen_invalidate_map_cache_entry_unlocked_miss(buffer); return; } entry->lock--; - if (entry->lock > 0 || pentry == NULL) { + if (entry->lock > 0) { return; } - pentry->next = entry->next; ram_block_notify_remove(entry->vaddr_base, entry->size, entry->size); - if (munmap(entry->vaddr_base, entry->size) != 0) { + 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); - g_free(entry); + 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; + } } -void xen_invalidate_map_cache_entry(uint8_t *buffer) +typedef struct XenMapCacheData { + Coroutine *co; + uint8_t *buffer; +} XenMapCacheData; + +static void xen_invalidate_map_cache_entry_single(MapCache *mc, uint8_t *buffer) { - mapcache_lock(); - xen_invalidate_map_cache_entry_unlocked(buffer); - mapcache_unlock(); + mapcache_lock(mc); + xen_invalidate_map_cache_entry_unlocked(mc, buffer); + mapcache_unlock(mc); } -void xen_invalidate_map_cache(void) +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_ro, buffer); + xen_invalidate_map_cache_entry_single(mapcache_grants_rw, 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; - /* Flush pending AIO before destroying the mapcache */ - bdrv_drain_all(); - - mapcache_lock(); + mapcache_lock(mc); - QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { + QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { if (!reventry->dma) { continue; } - fprintf(stderr, "Locked DMA mapping while invalidating mapcache!" - " "HWADDR_FMT_plx" -> %p is present\n", - reventry->paddr_index, reventry->vaddr_req); + trace_xen_invalidate_map_cache(reventry->paddr_index, + reventry->vaddr_req); } - for (i = 0; i < mapcache->nr_buckets; i++) { - MapCacheEntry *entry = &mapcache->entry[i]; + for (i = 0; i < mc->nr_buckets; i++) { + MapCacheEntry *entry = &mc->entry[i]; if (entry->vaddr_base == NULL) { continue; @@ -529,12 +701,21 @@ void xen_invalidate_map_cache(void) entry->valid_mapping = NULL; } - mapcache->last_entry = NULL; + mc->last_entry = NULL; + + mapcache_unlock(mc); +} + +void xen_invalidate_map_cache(void) +{ + /* Flush pending AIO before destroying the mapcache */ + bdrv_drain_all(); - mapcache_unlock(); + xen_invalidate_map_cache_single(mapcache); } -static uint8_t *xen_replace_cache_entry_unlocked(hwaddr old_phys_addr, +static uint8_t *xen_replace_cache_entry_unlocked(MapCache *mc, + hwaddr old_phys_addr, hwaddr new_phys_addr, hwaddr size) { @@ -542,8 +723,8 @@ static uint8_t *xen_replace_cache_entry_unlocked(hwaddr old_phys_addr, 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); + 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 */ @@ -552,34 +733,36 @@ static uint8_t *xen_replace_cache_entry_unlocked(hwaddr old_phys_addr, 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); + if (cache_size % mc->bucket_size) { + cache_size += mc->bucket_size - (cache_size % mc->bucket_size); } - entry = &mapcache->entry[address_index % mapcache->nr_buckets]; + entry = &mc->entry[address_index % mc->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 "HWADDR_FMT_plx \ - "that is not in the mapcache!\n", old_phys_addr); + trace_xen_replace_cache_entry_unlocked(old_phys_addr); return NULL; } - address_index = new_phys_addr >> MCACHE_BUCKET_SHIFT; - address_offset = new_phys_addr & (MCACHE_BUCKET_SIZE - 1); + 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); - fprintf(stderr, "Replacing a dummy mapcache entry for "HWADDR_FMT_plx \ - " with "HWADDR_FMT_plx"\n", old_phys_addr, new_phys_addr); + trace_xen_replace_cache_entry_dummy(old_phys_addr, new_phys_addr); - xen_remap_bucket(entry, entry->vaddr_base, - cache_size, address_index, false); + 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)) { - DPRINTF("Unable to update a mapcache entry for "HWADDR_FMT_plx"!\n", - old_phys_addr); + trace_xen_replace_cache_entry_unlocked_could_not_update_entry( + old_phys_addr + ); return NULL; } @@ -592,8 +775,9 @@ uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr, { uint8_t *p; - mapcache_lock(); - p = xen_replace_cache_entry_unlocked(old_phys_addr, new_phys_addr, size); - mapcache_unlock(); + 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-pvh-common.c b/hw/xen/xen-pvh-common.c new file mode 100644 index 00000000000..b93ff80c859 --- /dev/null +++ b/hw/xen/xen-pvh-common.c @@ -0,0 +1,399 @@ +/* + * 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 "qemu/units.h" +#include "qapi/visitor.h" +#include "hw/boards.h" +#include "hw/irq.h" +#include "system/tpm.h" +#include "system/tpm_backend.h" +#include "system/runstate.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 < PCI_NUM_PINS; 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, const 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 9b7304e5442..5698cc7c0eb 100644 --- a/hw/xen/xen_devconfig.c +++ b/hw/xen/xen_devconfig.c @@ -1,8 +1,8 @@ #include "qemu/osdep.h" #include "hw/xen/xen-legacy-backend.h" #include "qemu/option.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" +#include "system/blockdev.h" +#include "system/system.h" /* ------------------------------------------------------------- */ @@ -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 36e6f93c372..9d16644d82e 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -59,7 +59,8 @@ #include "hw/pci/pci.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" -#include "xen_pt.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 "qemu/range.h" @@ -710,7 +711,7 @@ static void xen_pt_destroy(PCIDevice *d) { uint8_t intx; int rc; - if (machine_irq && !xen_host_pci_device_closed(&s->real_device)) { + if (machine_irq && !xen_host_pci_device_closed(host_dev)) { intx = xen_pt_pci_intx(s); rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq, PT_IRQ_TYPE_PCI, @@ -759,12 +760,63 @@ 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 */ +#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 42000 +static bool xen_pt_need_gsi(void) +{ + FILE *fp; + int len; + /* + * The max length of guest_type is "PVH"+'\n'+'\0', it is 5, + * so here set the length of type to be twice. + */ + char type[10]; + const char *guest_type = "/sys/hypervisor/guest_type"; + + fp = fopen(guest_type, "r"); + if (!fp) { + error_report("Cannot open %s: %s", guest_type, strerror(errno)); + return false; + } + + if (fgets(type, sizeof(type), fp)) { + len = strlen(type); + if (len) { + type[len - 1] = '\0'; + if (!strcmp(type, "PVH")) { + fclose(fp); + return true; + } + } + } + + fclose(fp); + return false; +} + +static int xen_pt_map_pirq_for_gsi(PCIDevice *d, int *pirq) +{ + int gsi; + XenPCIPassthroughState *s = XEN_PT_DEVICE(d); + + gsi = xc_pcidev_get_gsi(xen_xc, + PCI_SBDF(s->real_device.domain, + s->real_device.bus, + s->real_device.dev, + s->real_device.func)); + if (gsi >= 0) { + return xc_physdev_map_pirq_gsi(xen_xc, xen_domid, gsi, pirq); + } + + return gsi; +} +#endif + static void xen_pt_realize(PCIDevice *d, Error **errp) { ERRP_GUARD(); @@ -846,7 +898,16 @@ static void xen_pt_realize(PCIDevice *d, Error **errp) goto out; } +#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 42000 + if (xen_pt_need_gsi()) { + rc = xen_pt_map_pirq_for_gsi(d, &pirq); + } else { + rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq); + } +#else rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq); +#endif + if (rc < 0) { XEN_PT_ERR(d, "Mapping machine irq %u to pirq %i failed, (err: %d)\n", machine_irq, pirq, errno); @@ -930,10 +991,9 @@ static void xen_pt_unregister_device(PCIDevice *d) xen_pt_destroy(d); } -static Property xen_pci_passthrough_properties[] = { +static const Property xen_pci_passthrough_properties[] = { DEFINE_PROP_PCI_HOST_DEVADDR("hostaddr", XenPCIPassthroughState, hostaddr), DEFINE_PROP_BOOL("permissive", XenPCIPassthroughState, permissive, false), - DEFINE_PROP_END_OF_LIST(), }; static void xen_pci_passthrough_instance_init(Object *obj) @@ -987,7 +1047,7 @@ static void xen_igd_clear_slot(DeviceState *qdev, Error **errp) xpdc->pci_qdev_realize(qdev, errp); } -static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data) +static void xen_pci_passthrough_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1019,7 +1079,7 @@ static const TypeInfo xen_pci_passthrough_info = { .class_init = xen_pci_passthrough_class_init, .class_size = sizeof(XenPTDeviceClass), .instance_init = xen_pci_passthrough_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { INTERFACE_PCIE_DEVICE }, { }, diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index 31bcfdf7056..095a0f0365d 100644 --- a/hw/xen/xen_pt.h +++ b/hw/xen/xen_pt.h @@ -1,3 +1,13 @@ +/* + * 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 @@ -5,9 +15,6 @@ #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) @@ -52,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, @@ -343,11 +344,6 @@ static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar) 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)); -} 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 2b8680b112f..3edaeab1e34 100644 --- a/hw/xen/xen_pt_config_init.c +++ b/hw/xen/xen_pt_config_init.c @@ -15,7 +15,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/timer.h" -#include "xen_pt.h" +#include "hw/xen/xen_pt.h" +#include "hw/xen/xen_igd.h" #include "hw/xen/xen-legacy-backend.h" #define XEN_PT_MERGE_VALUE(value, 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, diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c index 0aed3bb6fd6..2c0cec97234 100644 --- a/hw/xen/xen_pt_graphics.c +++ b/hw/xen/xen_pt_graphics.c @@ -3,7 +3,8 @@ */ #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" static unsigned long igd_guest_opregion; @@ -346,7 +347,7 @@ static const IGDDeviceIDInfo igd_combo_id_infos[] = { {0x162D, 0x9cc3, 0x03}, /* BDWGT3SRVR, BDWM_w7 */ }; -static void isa_bridge_class_init(ObjectClass *klass, void *data) +static void isa_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -362,7 +363,7 @@ static const TypeInfo isa_bridge_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = isa_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/xen/xen_pt_load_rom.c b/hw/xen/xen_pt_load_rom.c index 03422a8a714..6bc64acd335 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_stub.c b/hw/xen/xen_pt_stub.c index 5c108446a86..72feebeb207 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 c5ad71e8dc4..fe95b62d133 100644 --- a/hw/xen/xen_pvdev.c +++ b/hw/xen/xen_pvdev.c @@ -22,6 +22,7 @@ #include "qemu/main-loop.h" #include "hw/qdev-core.h" #include "hw/xen/xen-legacy-backend.h" +#include "hw/xen/xen-bus-helper.h" #include "hw/xen/xen_pvdev.h" /* private */ @@ -81,12 +82,9 @@ int xenstore_write_str(const char *base, const char *node, const char *val) char *xenstore_read_str(const char *base, const char *node) { - char abspath[XEN_BUFSIZE]; - unsigned int len; char *str, *ret = NULL; - snprintf(abspath, sizeof(abspath), "%s/%s", base, node); - str = qemu_xen_xs_read(xenstore, 0, abspath, &len); + str = xs_node_read(xenstore, 0, NULL, NULL, "%s/%s", base, node); if (str != NULL) { /* move to qemu-allocated memory to make sure * callers can safely g_free() stuff. */ @@ -275,7 +273,7 @@ void xen_pv_del_xendev(struct XenLegacyDevice *xendev) QTAILQ_REMOVE(&xendevs, xendev, next); - qdev_unplug(&xendev->qdev, NULL); + qdev_unplug(DEVICE(xendev), NULL); } void xen_pv_insert_xendev(struct XenLegacyDevice *xendev) diff --git a/hw/xen/xen_stubs.c b/hw/xen/xen_stubs.c new file mode 100644 index 00000000000..5e565df3929 --- /dev/null +++ b/hw/xen/xen_stubs.c @@ -0,0 +1,51 @@ +/* + * Various stubs for xen functions + * + * Those functions are used only if xen_enabled(). This file is linked only if + * CONFIG_XEN is not set, so they should never be called. + * + * Copyright (c) 2025 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/xen.h" +#include "system/xen-mapcache.h" + +void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) +{ + g_assert_not_reached(); +} + +void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, + struct MemoryRegion *mr, Error **errp) +{ + g_assert_not_reached(); +} + +bool xen_mr_is_memory(MemoryRegion *mr) +{ + g_assert_not_reached(); +} + +void xen_invalidate_map_cache_entry(uint8_t *buffer) +{ + g_assert_not_reached(); +} + +ram_addr_t xen_ram_addr_from_mapcache(void *ptr) +{ + g_assert_not_reached(); +} + +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) +{ + g_assert_not_reached(); +} diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index 17cda5ec13a..99c02492ef9 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -27,18 +27,14 @@ #include "hw/boards.h" #include "hw/xen/xen-legacy-backend.h" #include "hw/xen/xen-bus.h" -#include "sysemu/block-backend.h" -#include "sysemu/sysemu.h" +#include "system/block-backend.h" +#include "system/system.h" static void xen_init_pv(MachineState *machine) { - DriveInfo *dinfo; - int i; - setup_xen_backend_ops(); - /* Initialize backend core & drivers */ - xen_be_init(); + xen_bus_init(); switch (xen_mode) { case XEN_ATTACH: @@ -54,9 +50,6 @@ static void xen_init_pv(MachineState *machine) break; } - 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"); @@ -64,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 0740657ea58..1f0492d89c0 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 ade7891ec50..4418c78d5bb 100644 --- a/hw/xtensa/bootparam.h +++ b/hw/xtensa/bootparam.h @@ -1,6 +1,9 @@ #ifndef HW_XTENSA_BOOTPARAM_H #define HW_XTENSA_BOOTPARAM_H +#include "exec/cpu-common.h" +#include "exec/tswap.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 8cef88c61bc..e3885316106 100644 --- a/hw/xtensa/pic_cpu.c +++ b/hw/xtensa/pic_cpu.c @@ -27,6 +27,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/cpu-interrupt.h" #include "hw/irq.h" #include "qemu/log.h" #include "qemu/timer.h" diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index 2160e619645..49d17e7bb29 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -27,12 +27,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/reset.h" -#include "sysemu/sysemu.h" +#include "system/reset.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/error-report.h" #include "xtensa_memory.h" #include "xtensa_sim.h" @@ -100,7 +100,8 @@ void xtensa_sim_load_kernel(XtensaCPU *cpu, MachineState *machine) if (kernel_filename) { uint64_t elf_entry; int success = load_elf(kernel_filename, NULL, translate_phys_addr, cpu, - &elf_entry, NULL, NULL, NULL, TARGET_BIG_ENDIAN, + &elf_entry, NULL, NULL, NULL, + TARGET_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB, EM_XTENSA, 0, 0); if (success > 0) { diff --git a/hw/xtensa/virt.c b/hw/xtensa/virt.c index a6cf646e997..b10866ccd85 100644 --- a/hw/xtensa/virt.c +++ b/hw/xtensa/virt.c @@ -27,13 +27,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/pci-host/gpex.h" #include "net/net.h" #include "elf.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/error-report.h" #include "xtensa_memory.h" #include "xtensa_sim.h" @@ -93,7 +93,7 @@ static void create_pcie(MachineState *ms, CPUXtensaState *env, int irq_base, /* Connect IRQ lines. */ extints = xtensa_get_extints(env); - for (i = 0; i < GPEX_NUM_IRQS; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { void *q = extints[irq_base + i]; sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, q); @@ -102,9 +102,7 @@ static void create_pcie(MachineState *ms, CPUXtensaState *env, int irq_base, pci = PCI_HOST_BRIDGE(dev); if (pci->bus) { - for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], pci->bus, mc->default_nic, NULL); - } + pci_init_nic_devices(pci->bus, mc->default_nic); } } diff --git a/hw/xtensa/xtensa_memory.c b/hw/xtensa/xtensa_memory.c index 2c1095f0170..13a6077d868 100644 --- a/hw/xtensa/xtensa_memory.c +++ b/hw/xtensa/xtensa_memory.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/error-report.h" #include "xtensa_memory.h" diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index fbad1c83a3f..6efffae466b 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -29,20 +29,21 @@ #include "qemu/units.h" #include "qapi/error.h" #include "cpu.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/qdev-properties.h" #include "elf.h" -#include "exec/memory.h" -#include "hw/char/serial.h" +#include "system/memory.h" +#include "exec/tswap.h" +#include "hw/char/serial-mm.h" #include "net/net.h" #include "hw/sysbus.h" #include "hw/block/flash.h" #include "chardev/char.h" -#include "sysemu/device_tree.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" +#include "system/device_tree.h" +#include "system/reset.h" +#include "system/runstate.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "bootparam.h" @@ -141,14 +142,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); @@ -301,10 +304,7 @@ 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); @@ -357,7 +357,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); @@ -374,14 +373,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, @@ -407,7 +398,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, TARGET_BIG_ENDIAN, + &elf_entry, NULL, NULL, NULL, + TARGET_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB, EM_XTENSA, 0, 0); if (success > 0) { entry_point = elf_entry; @@ -425,8 +417,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 */ @@ -435,7 +426,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 */ @@ -444,14 +436,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) { @@ -591,7 +585,7 @@ static void xtfpga_kc705_nommu_init(MachineState *machine) xtfpga_init(&kc705_board, machine); } -static void xtfpga_lx60_class_init(ObjectClass *oc, void *data) +static void xtfpga_lx60_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -608,7 +602,7 @@ static const TypeInfo xtfpga_lx60_type = { .class_init = xtfpga_lx60_class_init, }; -static void xtfpga_lx60_nommu_class_init(ObjectClass *oc, void *data) +static void xtfpga_lx60_nommu_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -625,7 +619,7 @@ static const TypeInfo xtfpga_lx60_nommu_type = { .class_init = xtfpga_lx60_nommu_class_init, }; -static void xtfpga_lx200_class_init(ObjectClass *oc, void *data) +static void xtfpga_lx200_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -642,7 +636,7 @@ static const TypeInfo xtfpga_lx200_type = { .class_init = xtfpga_lx200_class_init, }; -static void xtfpga_lx200_nommu_class_init(ObjectClass *oc, void *data) +static void xtfpga_lx200_nommu_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -659,7 +653,7 @@ static const TypeInfo xtfpga_lx200_nommu_type = { .class_init = xtfpga_lx200_nommu_class_init, }; -static void xtfpga_ml605_class_init(ObjectClass *oc, void *data) +static void xtfpga_ml605_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -676,7 +670,7 @@ static const TypeInfo xtfpga_ml605_type = { .class_init = xtfpga_ml605_class_init, }; -static void xtfpga_ml605_nommu_class_init(ObjectClass *oc, void *data) +static void xtfpga_ml605_nommu_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -693,7 +687,7 @@ static const TypeInfo xtfpga_ml605_nommu_type = { .class_init = xtfpga_ml605_nommu_class_init, }; -static void xtfpga_kc705_class_init(ObjectClass *oc, void *data) +static void xtfpga_kc705_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -710,7 +704,7 @@ static const TypeInfo xtfpga_kc705_type = { .class_init = xtfpga_kc705_class_init, }; -static void xtfpga_kc705_nommu_class_init(ObjectClass *oc, void *data) +static void xtfpga_kc705_nommu_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/include/accel/accel-cpu-target.h b/include/accel/accel-cpu-target.h new file mode 100644 index 00000000000..6feb344e29b --- /dev/null +++ b/include/accel/accel-cpu-target.h @@ -0,0 +1,31 @@ +/* + * Accelerator interface, specializes CPUClass + * This header is used only by target-specific code. + * + * 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_CPU_TARGET_H +#define ACCEL_CPU_TARGET_H + +/* + * This header is used to define new accelerator-specific target-specific + * accelerator cpu subclasses. + * It uses CPU_RESOLVING_TYPE, so this is clearly target-specific. + * + * Do not try to use for any other purpose than the implementation of new + * subclasses in target/, or the accel implementation itself in accel/ + */ + +#include "qom/object.h" +#include "accel/accel-cpu.h" +#include "cpu.h" + +#define TYPE_ACCEL_CPU "accel-" CPU_RESOLVING_TYPE +#define ACCEL_CPU_NAME(name) (name "-" TYPE_ACCEL_CPU) +DECLARE_CLASS_CHECKERS(AccelCPUClass, ACCEL_CPU, TYPE_ACCEL_CPU) + +#endif /* ACCEL_CPU_H */ diff --git a/include/accel/accel-cpu.h b/include/accel/accel-cpu.h new file mode 100644 index 00000000000..9e7eede7c3c --- /dev/null +++ b/include/accel/accel-cpu.h @@ -0,0 +1,23 @@ +/* + * Accelerator interface, specializes CPUClass + * + * Copyright 2021 SUSE LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ACCEL_CPU_H +#define ACCEL_CPU_H + +#include "qom/object.h" +#include "hw/core/cpu.h" + +typedef struct AccelCPUClass { + ObjectClass parent_class; + + void (*cpu_class_init)(CPUClass *cc); + void (*cpu_instance_init)(CPUState *cpu); + bool (*cpu_target_realize)(CPUState *cpu, Error **errp); +} AccelCPUClass; + +#endif /* ACCEL_CPU_H */ diff --git a/include/accel/tcg/cpu-ldst-common.h b/include/accel/tcg/cpu-ldst-common.h new file mode 100644 index 00000000000..8bf17c2fab0 --- /dev/null +++ b/include/accel/tcg/cpu-ldst-common.h @@ -0,0 +1,122 @@ +/* + * Software MMU support + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_CPU_LDST_COMMON_H +#define ACCEL_TCG_CPU_LDST_COMMON_H + +#ifndef CONFIG_TCG +#error Can only include this header with TCG +#endif + +#include "exec/memopidx.h" +#include "exec/vaddr.h" +#include "exec/mmu-access-type.h" +#include "qemu/int128.h" + +uint8_t cpu_ldb_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); +uint16_t cpu_ldw_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); +uint32_t cpu_ldl_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); +uint64_t cpu_ldq_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); +Int128 cpu_ld16_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra); + +void cpu_stb_mmu(CPUArchState *env, vaddr ptr, uint8_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_stw_mmu(CPUArchState *env, vaddr ptr, uint16_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_stl_mmu(CPUArchState *env, vaddr ptr, uint32_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_stq_mmu(CPUArchState *env, vaddr ptr, uint64_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_st16_mmu(CPUArchState *env, vaddr addr, Int128 val, + MemOpIdx oi, uintptr_t ra); + +uint32_t cpu_atomic_cmpxchgb_mmu(CPUArchState *env, vaddr addr, + uint32_t cmpv, uint32_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint32_t cpu_atomic_cmpxchgw_le_mmu(CPUArchState *env, vaddr addr, + uint32_t cmpv, uint32_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint32_t cpu_atomic_cmpxchgl_le_mmu(CPUArchState *env, vaddr addr, + uint32_t cmpv, uint32_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint64_t cpu_atomic_cmpxchgq_le_mmu(CPUArchState *env, vaddr addr, + uint64_t cmpv, uint64_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint32_t cpu_atomic_cmpxchgw_be_mmu(CPUArchState *env, vaddr addr, + uint32_t cmpv, uint32_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint32_t cpu_atomic_cmpxchgl_be_mmu(CPUArchState *env, vaddr addr, + uint32_t cmpv, uint32_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint64_t cpu_atomic_cmpxchgq_be_mmu(CPUArchState *env, vaddr 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, vaddr addr, TYPE val, \ + MemOpIdx oi, uintptr_t retaddr); + +#ifdef CONFIG_ATOMIC64 +#define GEN_ATOMIC_HELPER_ALL(NAME) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, b) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_be) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_be) \ + GEN_ATOMIC_HELPER(NAME, uint64_t, q_le) \ + GEN_ATOMIC_HELPER(NAME, uint64_t, q_be) +#else +#define GEN_ATOMIC_HELPER_ALL(NAME) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, b) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_be) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_be) +#endif + +GEN_ATOMIC_HELPER_ALL(fetch_add) +GEN_ATOMIC_HELPER_ALL(fetch_sub) +GEN_ATOMIC_HELPER_ALL(fetch_and) +GEN_ATOMIC_HELPER_ALL(fetch_or) +GEN_ATOMIC_HELPER_ALL(fetch_xor) +GEN_ATOMIC_HELPER_ALL(fetch_smin) +GEN_ATOMIC_HELPER_ALL(fetch_umin) +GEN_ATOMIC_HELPER_ALL(fetch_smax) +GEN_ATOMIC_HELPER_ALL(fetch_umax) + +GEN_ATOMIC_HELPER_ALL(add_fetch) +GEN_ATOMIC_HELPER_ALL(sub_fetch) +GEN_ATOMIC_HELPER_ALL(and_fetch) +GEN_ATOMIC_HELPER_ALL(or_fetch) +GEN_ATOMIC_HELPER_ALL(xor_fetch) +GEN_ATOMIC_HELPER_ALL(smin_fetch) +GEN_ATOMIC_HELPER_ALL(umin_fetch) +GEN_ATOMIC_HELPER_ALL(smax_fetch) +GEN_ATOMIC_HELPER_ALL(umax_fetch) + +GEN_ATOMIC_HELPER_ALL(xchg) + +#undef GEN_ATOMIC_HELPER_ALL +#undef GEN_ATOMIC_HELPER + +Int128 cpu_atomic_cmpxchgo_le_mmu(CPUArchState *env, vaddr addr, + Int128 cmpv, Int128 newv, + MemOpIdx oi, uintptr_t retaddr); +Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, vaddr addr, + Int128 cmpv, Int128 newv, + MemOpIdx oi, uintptr_t retaddr); + +uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr, + MemOpIdx oi, uintptr_t ra); +uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr, + MemOpIdx oi, uintptr_t ra); +uint32_t cpu_ldl_code_mmu(CPUArchState *env, vaddr addr, + MemOpIdx oi, uintptr_t ra); +uint64_t cpu_ldq_code_mmu(CPUArchState *env, vaddr addr, + MemOpIdx oi, uintptr_t ra); + +#endif /* ACCEL_TCG_CPU_LDST_COMMON_H */ diff --git a/include/accel/tcg/cpu-ldst.h b/include/accel/tcg/cpu-ldst.h new file mode 100644 index 00000000000..0de7f5eaa6b --- /dev/null +++ b/include/accel/tcg/cpu-ldst.h @@ -0,0 +1,505 @@ +/* + * 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 + * 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 . + * + */ + +/* + * Generate inline load/store functions for all MMU modes (typically + * at least _user and _kernel) as well as _data versions, for all data + * sizes. + * + * Used by target op helpers. + * + * The syntax for the accessors is: + * + * load: cpu_ld{sign}{size}{end}_{mmusuffix}(env, ptr) + * cpu_ld{sign}{size}{end}_{mmusuffix}_ra(env, ptr, retaddr) + * cpu_ld{sign}{size}{end}_mmuidx_ra(env, ptr, mmu_idx, retaddr) + * cpu_ld{sign}{size}{end}_mmu(env, ptr, oi, retaddr) + * + * store: cpu_st{size}{end}_{mmusuffix}(env, ptr, val) + * cpu_st{size}{end}_{mmusuffix}_ra(env, ptr, val, retaddr) + * cpu_st{size}{end}_mmuidx_ra(env, ptr, val, mmu_idx, retaddr) + * cpu_st{size}{end}_mmu(env, ptr, val, oi, retaddr) + * + * sign is: + * (empty): for 32 and 64 bit sizes + * u : unsigned + * s : signed + * + * size is: + * b: 8 bits + * w: 16 bits + * l: 32 bits + * q: 64 bits + * + * end is: + * (empty): for target native endian, or for 8 bit access + * _be: for forced big endian + * _le: for forced little endian + * + * mmusuffix is one of the generic suffixes "data" or "code", or "mmuidx". + * The "mmuidx" suffix carries an extra mmu_idx argument that specifies + * the index to use; the "data" and "code" suffixes take the index from + * cpu_mmu_index(). + * + * The "mmu" suffix carries the full MemOpIdx, with both mmu_idx and the + * MemOp including alignment requirements. The alignment will be enforced. + */ +#ifndef ACCEL_TCG_CPU_LDST_H +#define ACCEL_TCG_CPU_LDST_H + +#ifndef CONFIG_TCG +#error Can only include this header with TCG +#endif + +#include "exec/cpu-common.h" +#include "accel/tcg/cpu-ldst-common.h" +#include "accel/tcg/cpu-mmu-index.h" +#include "exec/abi_ptr.h" + +#if defined(CONFIG_USER_ONLY) +#include "user/guest-host.h" +#endif /* CONFIG_USER_ONLY */ + +static inline uint32_t +cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); + return cpu_ldb_mmu(env, addr, oi, ra); +} + +static inline int +cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) +{ + return (int8_t)cpu_ldub_mmuidx_ra(env, addr, mmu_idx, ra); +} + +static inline 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_mmu(env, addr, oi, ra); +} + +static inline int +cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return (int16_t)cpu_lduw_be_mmuidx_ra(env, addr, mmu_idx, ra); +} + +static inline 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_mmu(env, addr, oi, ra); +} + +static inline 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_mmu(env, addr, oi, ra); +} + +static inline 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_mmu(env, addr, oi, ra); +} + +static inline int +cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return (int16_t)cpu_lduw_le_mmuidx_ra(env, addr, mmu_idx, ra); +} + +static inline 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_mmu(env, addr, oi, ra); +} + +static inline 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_mmu(env, addr, oi, ra); +} + +static inline void +cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); + cpu_stb_mmu(env, addr, val, oi, ra); +} + +static inline 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_mmu(env, addr, val, oi, ra); +} + +static inline 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_mmu(env, addr, val, oi, ra); +} + +static inline 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_mmu(env, addr, val, oi, ra); +} + +static inline 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_mmu(env, addr, val, oi, ra); +} + +static inline 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_mmu(env, addr, val, oi, ra); +} + +static inline 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_mmu(env, addr, val, oi, ra); +} + +/*--------------------------*/ + +static inline uint32_t +cpu_ldub_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_ldub_mmuidx_ra(env, addr, mmu_index, ra); +} + +static inline int +cpu_ldsb_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return (int8_t)cpu_ldub_data_ra(env, addr, ra); +} + +static inline uint32_t +cpu_lduw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_lduw_be_mmuidx_ra(env, addr, mmu_index, ra); +} + +static inline int +cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return (int16_t)cpu_lduw_be_data_ra(env, addr, ra); +} + +static inline uint32_t +cpu_ldl_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_ldl_be_mmuidx_ra(env, addr, mmu_index, ra); +} + +static inline uint64_t +cpu_ldq_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_ldq_be_mmuidx_ra(env, addr, mmu_index, ra); +} + +static inline uint32_t +cpu_lduw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_lduw_le_mmuidx_ra(env, addr, mmu_index, ra); +} + +static inline int +cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return (int16_t)cpu_lduw_le_data_ra(env, addr, ra); +} + +static inline uint32_t +cpu_ldl_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_ldl_le_mmuidx_ra(env, addr, mmu_index, ra); +} + +static inline uint64_t +cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_ldq_le_mmuidx_ra(env, addr, mmu_index, ra); +} + +static inline void +cpu_stb_data_ra(CPUArchState *env, abi_ptr addr, uint32_t val, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stb_mmuidx_ra(env, addr, val, mmu_index, ra); +} + +static inline void +cpu_stw_be_data_ra(CPUArchState *env, abi_ptr addr, uint32_t val, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stw_be_mmuidx_ra(env, addr, val, mmu_index, ra); +} + +static inline void +cpu_stl_be_data_ra(CPUArchState *env, abi_ptr addr, uint32_t val, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stl_be_mmuidx_ra(env, addr, val, mmu_index, ra); +} + +static inline void +cpu_stq_be_data_ra(CPUArchState *env, abi_ptr addr, uint64_t val, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stq_be_mmuidx_ra(env, addr, val, mmu_index, ra); +} + +static inline void +cpu_stw_le_data_ra(CPUArchState *env, abi_ptr addr, uint32_t val, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stw_le_mmuidx_ra(env, addr, val, mmu_index, ra); +} + +static inline void +cpu_stl_le_data_ra(CPUArchState *env, abi_ptr addr, uint32_t val, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stl_le_mmuidx_ra(env, addr, val, mmu_index, ra); +} + +static inline void +cpu_stq_le_data_ra(CPUArchState *env, abi_ptr addr, uint64_t val, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stq_le_mmuidx_ra(env, addr, val, mmu_index, ra); +} + +/*--------------------------*/ + +static inline uint32_t +cpu_ldub_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_ldub_data_ra(env, addr, 0); +} + +static inline int +cpu_ldsb_data(CPUArchState *env, abi_ptr addr) +{ + return (int8_t)cpu_ldub_data(env, addr); +} + +static inline uint32_t +cpu_lduw_be_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_lduw_be_data_ra(env, addr, 0); +} + +static inline int +cpu_ldsw_be_data(CPUArchState *env, abi_ptr addr) +{ + return (int16_t)cpu_lduw_be_data(env, addr); +} + +static inline uint32_t +cpu_ldl_be_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_ldl_be_data_ra(env, addr, 0); +} + +static inline uint64_t +cpu_ldq_be_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_ldq_be_data_ra(env, addr, 0); +} + +static inline uint32_t +cpu_lduw_le_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_lduw_le_data_ra(env, addr, 0); +} + +static inline int +cpu_ldsw_le_data(CPUArchState *env, abi_ptr addr) +{ + return (int16_t)cpu_lduw_le_data(env, addr); +} + +static inline uint32_t +cpu_ldl_le_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_ldl_le_data_ra(env, addr, 0); +} + +static inline uint64_t +cpu_ldq_le_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_ldq_le_data_ra(env, addr, 0); +} + +static inline void +cpu_stb_data(CPUArchState *env, abi_ptr addr, uint32_t val) +{ + cpu_stb_data_ra(env, addr, val, 0); +} + +static inline void +cpu_stw_be_data(CPUArchState *env, abi_ptr addr, uint32_t val) +{ + cpu_stw_be_data_ra(env, addr, val, 0); +} + +static inline void +cpu_stl_be_data(CPUArchState *env, abi_ptr addr, uint32_t val) +{ + cpu_stl_be_data_ra(env, addr, val, 0); +} + +static inline void +cpu_stq_be_data(CPUArchState *env, abi_ptr addr, uint64_t val) +{ + cpu_stq_be_data_ra(env, addr, val, 0); +} + +static inline void +cpu_stw_le_data(CPUArchState *env, abi_ptr addr, uint32_t val) +{ + cpu_stw_le_data_ra(env, addr, val, 0); +} + +static inline void +cpu_stl_le_data(CPUArchState *env, abi_ptr addr, uint32_t val) +{ + cpu_stl_le_data_ra(env, addr, val, 0); +} + +static inline void +cpu_stq_le_data(CPUArchState *env, abi_ptr addr, uint64_t val) +{ + cpu_stq_le_data_ra(env, addr, val, 0); +} + +#if TARGET_BIG_ENDIAN +# define cpu_lduw_data cpu_lduw_be_data +# define cpu_ldsw_data cpu_ldsw_be_data +# define cpu_ldl_data cpu_ldl_be_data +# define cpu_ldq_data cpu_ldq_be_data +# define cpu_lduw_data_ra cpu_lduw_be_data_ra +# define cpu_ldsw_data_ra cpu_ldsw_be_data_ra +# define cpu_ldl_data_ra cpu_ldl_be_data_ra +# define cpu_ldq_data_ra cpu_ldq_be_data_ra +# define cpu_lduw_mmuidx_ra cpu_lduw_be_mmuidx_ra +# 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_stw_data cpu_stw_be_data +# define cpu_stl_data cpu_stl_be_data +# define cpu_stq_data cpu_stq_be_data +# define cpu_stw_data_ra cpu_stw_be_data_ra +# define cpu_stl_data_ra cpu_stl_be_data_ra +# define cpu_stq_data_ra cpu_stq_be_data_ra +# 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 +#else +# define cpu_lduw_data cpu_lduw_le_data +# define cpu_ldsw_data cpu_ldsw_le_data +# define cpu_ldl_data cpu_ldl_le_data +# define cpu_ldq_data cpu_ldq_le_data +# define cpu_lduw_data_ra cpu_lduw_le_data_ra +# define cpu_ldsw_data_ra cpu_ldsw_le_data_ra +# define cpu_ldl_data_ra cpu_ldl_le_data_ra +# define cpu_ldq_data_ra cpu_ldq_le_data_ra +# define cpu_lduw_mmuidx_ra cpu_lduw_le_mmuidx_ra +# 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_stw_data cpu_stw_le_data +# define cpu_stl_data cpu_stl_le_data +# define cpu_stq_data cpu_stq_le_data +# define cpu_stw_data_ra cpu_stw_le_data_ra +# define cpu_stl_data_ra cpu_stl_le_data_ra +# define cpu_stq_data_ra cpu_stq_le_data_ra +# 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 +#endif + +static inline uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr) +{ + CPUState *cs = env_cpu(env); + MemOpIdx oi = make_memop_idx(MO_UB, cpu_mmu_index(cs, true)); + return cpu_ldb_code_mmu(env, addr, oi, 0); +} + +static inline uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr addr) +{ + CPUState *cs = env_cpu(env); + MemOpIdx oi = make_memop_idx(MO_TEUW, cpu_mmu_index(cs, true)); + return cpu_ldw_code_mmu(env, addr, oi, 0); +} + +static inline uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr) +{ + CPUState *cs = env_cpu(env); + MemOpIdx oi = make_memop_idx(MO_TEUL, cpu_mmu_index(cs, true)); + return cpu_ldl_code_mmu(env, addr, oi, 0); +} + +static inline uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr) +{ + CPUState *cs = env_cpu(env); + MemOpIdx oi = make_memop_idx(MO_TEUQ, cpu_mmu_index(cs, true)); + return cpu_ldq_code_mmu(env, addr, oi, 0); +} + +#endif /* ACCEL_TCG_CPU_LDST_H */ diff --git a/include/accel/tcg/cpu-mmu-index.h b/include/accel/tcg/cpu-mmu-index.h new file mode 100644 index 00000000000..e681a90844c --- /dev/null +++ b/include/accel/tcg/cpu-mmu-index.h @@ -0,0 +1,42 @@ +/* + * cpu_mmu_index() + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_CPU_MMU_INDEX_H +#define ACCEL_TCG_CPU_MMU_INDEX_H + +#include "hw/core/cpu.h" +#include "accel/tcg/cpu-ops.h" +#include "tcg/debug-assert.h" +#ifdef COMPILING_PER_TARGET +# ifdef CONFIG_USER_ONLY +# include "cpu.h" +# endif +#endif + +/** + * 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(CPUState *cs, bool ifetch) +{ +#ifdef COMPILING_PER_TARGET +# ifdef CONFIG_USER_ONLY + return MMU_USER_IDX; +# endif +#endif + + int ret = cs->cc->tcg_ops->mmu_index(cs, ifetch); + tcg_debug_assert(ret >= 0 && ret < NB_MMU_MODES); + return ret; +} + +#endif /* ACCEL_TCG_CPU_MMU_INDEX_H */ diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h new file mode 100644 index 00000000000..dd8ea300168 --- /dev/null +++ b/include/accel/tcg/cpu-ops.h @@ -0,0 +1,333 @@ +/* + * TCG CPU-specific operations + * + * 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 TCG_CPU_OPS_H +#define TCG_CPU_OPS_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" +#include "accel/tcg/tb-cpu-state.h" +#include "tcg/tcg-mo.h" + +struct TCGCPUOps { + /** + * mttcg_supported: multi-threaded TCG is supported + * + * Target (TCG frontend) supports: + * - atomic instructions + * - memory ordering primitives (barriers) + */ + bool mttcg_supported; + + /** + * @precise_smc: Stores which modify code within the current TB force + * the TB to exit; the next executed instruction will see + * the result of the store. + */ + bool precise_smc; + + /** + * @guest_default_memory_order: default barrier that is required + * for the guest memory ordering. + */ + TCGBar guest_default_memory_order; + + /** + * @initialize: Initialize TCG state + * + * Called when the first CPU is realized. + */ + void (*initialize)(void); + /** + * @translate_code: Translate guest instructions to TCGOps + * @cpu: cpu context + * @tb: translation block + * @max_insns: max number of instructions to translate + * @pc: guest virtual program counter address + * @host_pc: host physical program counter address + * + * This function must be provided by the target, which should create + * the target-specific DisasContext, and then invoke translator_loop. + */ + void (*translate_code)(CPUState *cpu, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); + /** + * @get_tb_cpu_state: Extract CPU state for a TCG #TranslationBlock + * + * Fill in all data required to select or compile a TranslationBlock. + */ + TCGTBCPUState (*get_tb_cpu_state)(CPUState *cs); + /** + * @synchronize_from_tb: Synchronize state from a TCG #TranslationBlock + * + * This is called when we abandon execution of a TB before starting it, + * and must set all parts of the CPU state which the previous TB in the + * chain may not have updated. + * By default, when this is NULL, a call is made to @set_pc(tb->pc). + * + * If more state needs to be restored, the target must implement a + * function to restore all the state, and register it here. + */ + void (*synchronize_from_tb)(CPUState *cpu, const TranslationBlock *tb); + /** + * @restore_state_to_opc: Synchronize state from INDEX_op_start_insn + * + * This is called when we unwind state in the middle of a TB, + * usually before raising an exception. Set all part of the CPU + * state which are tracked insn-by-insn in the target-specific + * arguments to start_insn, passed as @data. + */ + void (*restore_state_to_opc)(CPUState *cpu, const TranslationBlock *tb, + const uint64_t *data); + + /** @cpu_exec_enter: Callback for cpu_exec preparation */ + void (*cpu_exec_enter)(CPUState *cpu); + /** @cpu_exec_exit: Callback for cpu_exec cleanup */ + void (*cpu_exec_exit)(CPUState *cpu); + /** @debug_excp_handler: Callback for handling debug exceptions */ + void (*debug_excp_handler)(CPUState *cpu); + + /** @mmu_index: Callback for choosing softmmu mmu index */ + int (*mmu_index)(CPUState *cpu, bool ifetch); + +#ifdef CONFIG_USER_ONLY + /** + * @fake_user_interrupt: Callback for 'fake exception' handling. + * + * Simulate 'fake exception' which will be handled outside the + * cpu execution loop (hack for x86 user mode). + */ + void (*fake_user_interrupt)(CPUState *cpu); + + /** + * 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); + + /** + * untagged_addr: Remove an ignored tag from an address + * @cpu: cpu context + * @addr: tagged guest address + */ + vaddr (*untagged_addr)(CPUState *cs, vaddr addr); +#else + /** @do_interrupt: Callback for interrupt handling. */ + void (*do_interrupt)(CPUState *cpu); + /** @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec */ + bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request); + /** @cpu_exec_reset: Callback for reset in cpu_exec. */ + void (*cpu_exec_reset)(CPUState *cpu); + /** + * @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 + * SysemuCPUOps::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 + * + * If the access is valid, call tlb_set_page and return true; + * if the access is invalid and probe is true, return false; + * otherwise raise an exception and do not return. + */ + bool (*tlb_fill)(CPUState *cpu, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr); + /** + * @pointer_wrap: + * + * We have incremented @base to @result, resulting in a page change. + * For the current cpu state, adjust @result for possible overflow. + */ + vaddr (*pointer_wrap)(CPUState *cpu, int mmu_idx, vaddr result, vaddr base); + /** + * @do_transaction_failed: Callback for handling failed memory transactions + * (ie bus faults or external aborts; not MMU faults) + */ + void (*do_transaction_failed)(CPUState *cpu, hwaddr physaddr, vaddr addr, + unsigned size, MMUAccessType access_type, + int mmu_idx, MemTxAttrs attrs, + MemTxResult response, uintptr_t retaddr); + /** + * @do_unaligned_access: Callback for unaligned access handling + * The callback must exit via raising an exception. + */ + G_NORETURN void (*do_unaligned_access)(CPUState *cpu, vaddr addr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr); + + /** + * @adjust_watchpoint_address: hack for cpu_check_watchpoint used by ARM + */ + vaddr (*adjust_watchpoint_address)(CPUState *cpu, vaddr addr, int len); + + /** + * @debug_check_watchpoint: return true if the architectural + * watchpoint whose address has matched should really fire, used by ARM + * and RISC-V + */ + bool (*debug_check_watchpoint)(CPUState *cpu, CPUWatchpoint *wp); + + /** + * @debug_check_breakpoint: return true if the architectural + * breakpoint whose PC has matched should really fire. + */ + bool (*debug_check_breakpoint)(CPUState *cpu); + + /** + * @io_recompile_replay_branch: Callback for cpu_io_recompile. + * + * The cpu has been stopped, and cpu_restore_state_from_tb has been + * called. If the faulting instruction is in a delay slot, and the + * target architecture requires re-execution of the branch, then + * adjust the cpu state as required and return true. + */ + bool (*io_recompile_replay_branch)(CPUState *cpu, + const TranslationBlock *tb); + /** + * @need_replay_interrupt: Return %true if @interrupt_request + * needs to be recorded for replay purposes. + */ + 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); + +/* + * Common pointer_wrap implementations. + */ +vaddr cpu_pointer_wrap_notreached(CPUState *, int, vaddr, vaddr); +vaddr cpu_pointer_wrap_uint32(CPUState *, int, vaddr, vaddr); + +#endif + +#endif /* TCG_CPU_OPS_H */ diff --git a/include/accel/tcg/getpc.h b/include/accel/tcg/getpc.h new file mode 100644 index 00000000000..0fc08addcf3 --- /dev/null +++ b/include/accel/tcg/getpc.h @@ -0,0 +1,20 @@ +/* + * Get host pc for helper unwinding. + * + * Copyright (c) 2003 Fabrice Bellard + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_GETPC_H +#define ACCEL_TCG_GETPC_H + +/* GETPC is the true target of the return instruction that we'll execute. */ +#ifdef CONFIG_TCG_INTERPRETER +extern __thread uintptr_t tci_tb_ptr; +# define GETPC() tci_tb_ptr +#else +# define GETPC() \ + ((uintptr_t)__builtin_extract_return_addr(__builtin_return_address(0))) +#endif + +#endif /* ACCEL_TCG_GETPC_H */ diff --git a/include/accel/tcg/helper-retaddr.h b/include/accel/tcg/helper-retaddr.h new file mode 100644 index 00000000000..037fda2b83d --- /dev/null +++ b/include/accel/tcg/helper-retaddr.h @@ -0,0 +1,43 @@ +/* + * Get user helper pc for memory unwinding. + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_HELPER_RETADDR_H +#define ACCEL_TCG_HELPER_RETADDR_H + +/* + * 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 /* ACCEL_TCG_HELPER_RETADDR_H */ diff --git a/include/accel/tcg/iommu.h b/include/accel/tcg/iommu.h new file mode 100644 index 00000000000..90cfd6c0ed1 --- /dev/null +++ b/include/accel/tcg/iommu.h @@ -0,0 +1,41 @@ +/* + * TCG IOMMU translations. + * + * Copyright (c) 2003 Fabrice Bellard + * SPDX-License-Identifier: LGPL-2.1-or-later + */ +#ifndef ACCEL_TCG_IOMMU_H +#define ACCEL_TCG_IOMMU_H + +#ifdef CONFIG_USER_ONLY +#error Cannot include accel/tcg/iommu.h from user emulation +#endif + +#include "exec/hwaddr.h" +#include "exec/memattrs.h" + +/** + * iotlb_to_section: + * @cpu: CPU performing the access + * @index: TCG CPU IOTLB entry + * + * Given a TCG CPU IOTLB entry, return the MemoryRegionSection that + * it refers to. @index will have been initially created and returned + * by memory_region_section_get_iotlb(). + */ +MemoryRegionSection *iotlb_to_section(CPUState *cpu, + hwaddr index, MemTxAttrs attrs); + +MemoryRegionSection *address_space_translate_for_iotlb(CPUState *cpu, + int asidx, + hwaddr addr, + hwaddr *xlat, + hwaddr *plen, + MemTxAttrs attrs, + int *prot); + +hwaddr memory_region_section_get_iotlb(CPUState *cpu, + MemoryRegionSection *section); + +#endif + diff --git a/include/accel/tcg/probe.h b/include/accel/tcg/probe.h new file mode 100644 index 00000000000..dd9ecbbdf11 --- /dev/null +++ b/include/accel/tcg/probe.h @@ -0,0 +1,122 @@ +/* + * Probe guest virtual addresses for access permissions. + * + * Copyright (c) 2003 Fabrice Bellard + * SPDX-License-Identifier: LGPL-2.1-or-later + */ +#ifndef ACCEL_TCG_PROBE_H +#define ACCEL_TCG_PROBE_H + +#include "exec/mmu-access-type.h" +#include "exec/vaddr.h" + +/** + * probe_access: + * @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 + * @retaddr: return address for unwinding + * + * Look up the guest virtual address @addr. Raise an exception if the + * page does not satisfy @access_type. Raise an exception if the + * access (@addr, @size) hits a watchpoint. For writes, mark a clean + * page as dirty. + * + * 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, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); + +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, vaddr addr, int size, + int mmu_idx, uintptr_t retaddr) +{ + return probe_access(env, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr); +} + +/** + * 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 + * @phost: return value for host address + * @retaddr: return address for unwinding + * + * Similar to probe_access, loosely returning the TLB_FLAGS_MASK for + * the page, and storing the host address for RAM in @phost. + * + * If @nonfault is set, do not raise an exception but return TLB_INVALID_MASK. + * Do not handle watchpoints, but include TLB_WATCHPOINT in the returned flags. + * 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, 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. + * + * 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, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + bool nonfault, void **phost, + CPUTLBEntryFull **pfull, uintptr_t retaddr); + +/** + * 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. + */ +int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + void **phost, CPUTLBEntryFull **pfull); + +#endif /* !CONFIG_USER_ONLY */ + +/** + * tlb_vaddr_to_host: + * @env: CPUArchState + * @addr: guest virtual address to look up + * @access_type: 0 for read, 1 for write, 2 for execute + * @mmu_idx: MMU index to use for lookup + * + * Look up the specified guest virtual index in the TCG softmmu TLB. + * If we can translate a host virtual address suitable for direct RAM + * access, without causing a guest exception, then return it. + * Otherwise (TLB entry is for an I/O access, guest software + * TLB fill required, etc) return NULL. + */ +void *tlb_vaddr_to_host(CPUArchState *env, vaddr addr, + MMUAccessType access_type, int mmu_idx); + +#endif diff --git a/include/accel/tcg/tb-cpu-state.h b/include/accel/tcg/tb-cpu-state.h new file mode 100644 index 00000000000..8f912900ca6 --- /dev/null +++ b/include/accel/tcg/tb-cpu-state.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Definition of TCGTBCPUState. + */ + +#ifndef EXEC_TB_CPU_STATE_H +#define EXEC_TB_CPU_STATE_H + +#include "exec/vaddr.h" + +typedef struct TCGTBCPUState { + vaddr pc; + uint32_t flags; + uint32_t cflags; + uint64_t cs_base; +} TCGTBCPUState; + +#endif diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h index 5449b6d7428..cf5e8bde1ca 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 applies - * 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,7 +75,7 @@ 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); \ @@ -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: @@ -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 32042e8905a..99ff48420b1 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -20,6 +20,7 @@ #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" @@ -31,7 +32,6 @@ 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; @@ -54,9 +54,9 @@ typedef void QEMUBHFunc(void *opaque); typedef bool AioPollFn(void *opaque); typedef void IOHandler(void *opaque); -struct ThreadPool; +struct ThreadPoolAio; struct LinuxAioState; -struct LuringState; +typedef struct LuringState LuringState; /* Is polling disabled? */ bool aio_poll_disabled(AioContext *ctx); @@ -123,6 +123,10 @@ struct BHListSlice { typedef QSLIST_HEAD(, AioHandler) AioHandlerSList; +typedef struct AioPolledEvent { + int64_t ns; /* current polling time in nanoseconds */ +} AioPolledEvent; + struct AioContext { GSource source; @@ -207,13 +211,13 @@ struct AioContext { /* Thread pool for performing work and receiving completion callbacks. * Has its own locking. */ - struct ThreadPool *thread_pool; + struct ThreadPoolAio *thread_pool; #ifdef CONFIG_LINUX_AIO struct LinuxAioState *linux_aio; #endif #ifdef CONFIG_LINUX_IO_URING - struct LuringState *linux_io_uring; + LuringState *linux_io_uring; /* State for file descriptor monitoring using Linux io_uring */ struct io_uring fdmon_io_uring; @@ -229,7 +233,6 @@ struct AioContext { int poll_disable_cnt; /* Polling mode parameters */ - int64_t poll_ns; /* current polling time in nanoseconds */ int64_t poll_max_ns; /* maximum polling time in nanoseconds */ int64_t poll_grow; /* polling time growth factor */ int64_t poll_shrink; /* polling time shrink factor */ @@ -279,23 +282,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. @@ -468,7 +454,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 @@ -498,9 +484,14 @@ void aio_set_event_notifier(AioContext *ctx, 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, @@ -512,8 +503,8 @@ void aio_set_event_notifier_poll(AioContext *ctx, */ GSource *aio_get_g_source(AioContext *ctx); -/* Return the ThreadPool bound to this AioContext */ -struct ThreadPool *aio_get_thread_pool(AioContext *ctx); +/* Return the ThreadPoolAio bound to this AioContext */ +struct ThreadPoolAio *aio_get_thread_pool(AioContext *ctx); /* Setup the LinuxAioState bound to this AioContext */ struct LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp); @@ -522,10 +513,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 @@ -642,6 +633,9 @@ void aio_co_schedule(AioContext *ctx, 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); @@ -674,6 +668,9 @@ void aio_co_enter(AioContext *ctx, 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); @@ -717,8 +714,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 18a9c41f4e6..c81d637617e 100644 --- a/include/block/aio_task.h +++ b/include/block/aio_task.h @@ -40,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 df5ffc8d09e..c8c626daeaa 100644 --- a/include/block/block-common.h +++ b/include/block/block-common.h @@ -66,10 +66,14 @@ * function. The coroutine yields after scheduling the BH and is reentered when * the wrapped function returns. * - * If the first parameter of the function is a BlockDriverState, BdrvChild or - * BlockBackend pointer, the AioContext lock for it is taken in the wrapper. + * 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" @@ -239,6 +243,8 @@ 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 */ + #define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH) @@ -251,6 +257,7 @@ typedef enum { #define BDRV_OPT_AUTO_READ_ONLY "auto-read-only" #define BDRV_OPT_DISCARD "discard" #define BDRV_OPT_FORCE_SHARE "force-share" +#define BDRV_OPT_ACTIVE "active" #define BDRV_SECTOR_BITS 9 @@ -287,6 +294,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 @@ -322,6 +331,18 @@ typedef enum { #define BDRV_BLOCK_ALLOCATED 0x10 #define BDRV_BLOCK_EOF 0x20 #define BDRV_BLOCK_RECURSE 0x40 +#define BDRV_BLOCK_COMPRESSED 0x80 + +/* + * Block status hints: the bitwise-or of these flags emphasize what + * the caller hopes to learn, and some drivers may be able to give + * faster answers by doing less work when the hint permits. + */ +#define BDRV_WANT_ZERO BDRV_BLOCK_ZERO +#define BDRV_WANT_OFFSET_VALID BDRV_BLOCK_OFFSET_VALID +#define BDRV_WANT_ALLOCATED BDRV_BLOCK_ALLOCATED +#define BDRV_WANT_PRECISE (BDRV_WANT_ZERO | BDRV_WANT_OFFSET_VALID | \ + BDRV_WANT_OFFSET_VALID) typedef QTAILQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue; @@ -346,7 +367,6 @@ typedef enum BlockOpType { BLOCK_OP_TYPE_CHANGE, BLOCK_OP_TYPE_COMMIT_SOURCE, BLOCK_OP_TYPE_COMMIT_TARGET, - BLOCK_OP_TYPE_DATAPLANE, BLOCK_OP_TYPE_DRIVE_DEL, BLOCK_OP_TYPE_EJECT, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, diff --git a/include/block/block-copy.h b/include/block/block-copy.h index 0700953ab8f..dd5cc82f3b2 100644 --- a/include/block/block-copy.h +++ b/include/block/block-copy.h @@ -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 */ diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index f347199bff1..84a2a4ecd5c 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -31,11 +31,10 @@ /* * 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 @@ -71,8 +70,10 @@ 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, @@ -101,9 +102,10 @@ bdrv_co_open_blockdev_ref(BlockdevRef *ref, Error **errp); int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, Error **errp); -int bdrv_set_backing_hd_drained(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); @@ -132,26 +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 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 @@ -160,40 +165,51 @@ 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 * GRAPH_RDLOCK check_to_replace_node(BlockDriverState *parent_bs, const char *node_name, Error **errp); -int no_coroutine_fn bdrv_activate(BlockDriverState *bs, Error **errp); -int coroutine_fn no_co_wrapper +bool GRAPH_RDLOCK bdrv_is_inactive(BlockDriverState *bs); + +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 no_coroutine_fn +bdrv_inactivate(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 GRAPH_UNLOCKED bdrv_drain_all_begin(void); void bdrv_drain_all_begin_nopoll(void); void bdrv_drain_all_end(void); -void bdrv_drain_all(void); +void GRAPH_UNLOCKED 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); @@ -206,15 +222,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, @@ -224,15 +243,25 @@ void bdrv_img_create(const char *filename, const char *fmt, void bdrv_ref(BlockDriverState *bs); void no_coroutine_fn bdrv_unref(BlockDriverState *bs); void coroutine_fn no_co_wrapper bdrv_co_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); - -bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp); +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); + 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); @@ -245,32 +274,25 @@ 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); +bool GRAPH_RDLOCK +bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp); +int GRAPH_UNLOCKED +bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, + BdrvChild *ignore_child, Error **errp); +int GRAPH_RDLOCK +bdrv_try_change_aio_context_locked(BlockDriverState *bs, AioContext *ctx, + BdrvChild *ignore_child, Error **errp); + +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-io.h b/include/block/block-io.h index 6db48f2d359..4cf83fb3674 100644 --- a/include/block/block-io.h +++ b/include/block/block-io.h @@ -31,8 +31,7 @@ /* * 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. @@ -101,7 +100,6 @@ 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 */ @@ -128,37 +126,48 @@ int coroutine_fn GRAPH_RDLOCK bdrv_co_zone_append(BlockDriverState *bs, BdrvRequestFlags flags); 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 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 bdrv_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 bdrv_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 bdrv_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 coroutine_fn GRAPH_RDLOCK +bdrv_co_is_all_zeroes(BlockDriverState *bs); + +int GRAPH_RDLOCK +bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg, + Error **errp); -int 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); @@ -175,10 +184,14 @@ 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); + +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); @@ -186,8 +199,9 @@ bdrv_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); int co_wrapper_mixed_bdrv_rdlock bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); -ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, - Error **errp); +ImageInfoSpecific * GRAPH_RDLOCK +bdrv_get_specific_info(BlockDriverState *bs, Error **errp); + BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs); void bdrv_round_to_subclusters(BlockDriverState *bs, int64_t offset, int64_t bytes, @@ -197,6 +211,14 @@ void bdrv_round_to_subclusters(BlockDriverState *bs, 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); @@ -312,11 +334,10 @@ bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, * "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. @@ -364,7 +385,7 @@ bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); * * Begin a quiesced section for the parent of @c. */ -void bdrv_parent_drained_begin_single(BdrvChild *c); +void GRAPH_RDLOCK bdrv_parent_drained_begin_single(BdrvChild *c); /** * bdrv_parent_drained_poll_single: @@ -372,14 +393,14 @@ void bdrv_parent_drained_begin_single(BdrvChild *c); * Returns true if there is any pending activity to cease before @c can be * called quiesced, false otherwise. */ -bool bdrv_parent_drained_poll_single(BdrvChild *c); +bool GRAPH_RDLOCK bdrv_parent_drained_poll_single(BdrvChild *c); /** * bdrv_parent_drained_end_single: * * End a quiesced section for the parent of @c. */ -void bdrv_parent_drained_end_single(BdrvChild *c); +void GRAPH_RDLOCK bdrv_parent_drained_end_single(BdrvChild *c); /** * bdrv_drain_poll: @@ -392,8 +413,9 @@ void bdrv_parent_drained_end_single(BdrvChild *c); * * This is part of bdrv_drained_begin. */ -bool bdrv_drain_poll(BlockDriverState *bs, 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: @@ -401,9 +423,15 @@ bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent, * 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); +void GRAPH_UNLOCKED bdrv_drained_begin(BlockDriverState *bs); /** * bdrv_do_drained_begin_quiesce: @@ -417,6 +445,12 @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent); * bdrv_drained_end: * * End a quiescent section started by bdrv_drained_begin(). + * + * 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); diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 85be256c09c..925a3e73530 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -235,19 +235,19 @@ 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 GRAPH_UNLOCKED_PTR (*bdrv_open)( BlockDriverState *bs, QDict *options, int flags, Error **errp); - /* Protocol drivers should implement this instead of bdrv_open */ - int GRAPH_UNLOCKED_PTR (*bdrv_file_open)( - BlockDriverState *bs, QDict *options, int flags, Error **errp); void (*bdrv_close)(BlockDriverState *bs); int coroutine_fn GRAPH_UNLOCKED_PTR (*bdrv_co_create)( @@ -256,20 +256,18 @@ struct BlockDriver { int coroutine_fn GRAPH_UNLOCKED_PTR (*bdrv_co_create_opts)( BlockDriver *drv, const char *filename, QemuOpts *opts, Error **errp); - int (*bdrv_amend_options)(BlockDriverState *bs, - QemuOpts *opts, - BlockDriverAmendStatusCB *status_cb, - void *cb_opaque, - bool force, - Error **errp); + int GRAPH_RDLOCK_PTR (*bdrv_amend_options)( + BlockDriverState *bs, QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + bool force, Error **errp); - int (*bdrv_make_empty)(BlockDriverState *bs); + 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. @@ -292,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 @@ -309,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 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 (*bdrv_change_backing_file)(BlockDriverState *bs, - const char *backing_file, const char *backing_fmt); + 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, @@ -346,7 +347,7 @@ struct BlockDriver { * 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 @@ -383,7 +384,8 @@ struct BlockDriver { * 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. @@ -391,12 +393,28 @@ 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); + /** + * Hot add a BDS's child. Used in combination with bdrv_del_child, so the + * user can take a child offline when it is broken and take a new child + * online. + * + * All block nodes must be drained. + */ + void GRAPH_WRLOCK_PTR (*bdrv_add_child)( + BlockDriverState *parent, BlockDriverState *child, Error **errp); + + /** + * Hot remove a BDS's child. Used in combination with bdrv_add_child, so the + * user can take a child offline when it is broken and take a new child + * online. + * + * All block nodes must be drained. + */ + void GRAPH_WRLOCK_PTR (*bdrv_del_child)( + BlockDriverState *parent, BdrvChild *child, Error **errp); /** * Informs the block driver that a permission change is intended. The @@ -413,8 +431,8 @@ 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 @@ -426,7 +444,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() @@ -436,7 +455,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 @@ -450,11 +469,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 @@ -501,10 +520,6 @@ struct BlockDriver { 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 GRAPH_RDLOCK_PTR (*bdrv_co_readv)(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); @@ -603,15 +618,16 @@ struct BlockDriver { * according to the current layer, and should only need to set * BDRV_BLOCK_DATA, BDRV_BLOCK_ZERO, BDRV_BLOCK_OFFSET_VALID, * and/or BDRV_BLOCK_RAW; if the current layer defers to a backing - * layer, the result should be 0 (and not BDRV_BLOCK_ZERO). See - * block.h for the overall meaning of the bits. As a hint, the - * flag want_zero is true if the caller cares more about precise - * mappings (favor accurate _OFFSET_VALID/_ZERO) or false for - * overall allocation (favor larger *pnum, perhaps by reporting - * _DATA instead of _ZERO). The block layer guarantees input - * clamped to bdrv_getlength() and aligned to request_alignment, - * as well as non-NULL pnum, map, and file; in turn, the driver - * must return an error or set pnum to an aligned non-zero value. + * layer, the result should be 0 (and not BDRV_BLOCK_ZERO). The + * caller will synthesize BDRV_BLOCK_ALLOCATED based on the + * non-zero results. See block.h for the overall meaning of the + * bits. As a hint, the flags in @mode may include a bitwise-or + * of BDRV_WANT_ALLOCATED, BDRV_WANT_OFFSET_VALID, or + * BDRV_WANT_ZERO based on what the caller is looking for in the + * results. The block layer guarantees input clamped to + * bdrv_getlength() and aligned to request_alignment, as well as + * non-NULL pnum, map, and file; in turn, the driver must return + * an error or set pnum to an aligned non-zero value. * * Note that @bytes is just a hint on how big of a region the * caller wants to inspect. It is not a limit on *pnum. @@ -623,8 +639,8 @@ struct BlockDriver { * to clamping *pnum for return to its caller. */ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_block_status)( - BlockDriverState *bs, - bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, + BlockDriverState *bs, unsigned int mode, + int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file); /* @@ -648,8 +664,8 @@ struct BlockDriver { 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); + BlockDriverState *bs, unsigned int mode, 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); @@ -723,8 +739,8 @@ struct BlockDriver { int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_get_info)( BlockDriverState *bs, BlockDriverInfo *bdi); - ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs, - Error **errp); + ImageInfoSpecific * GRAPH_RDLOCK_PTR (*bdrv_get_specific_info)( + BlockDriverState *bs, Error **errp); BlockStatsSpecific *(*bdrv_get_specific_stats)(BlockDriverState *bs); int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_save_vmstate)( @@ -944,8 +960,8 @@ 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 GRAPH_WRLOCK_PTR (*attach)(BdrvChild *child); void GRAPH_WRLOCK_PTR (*detach)(BdrvChild *child); @@ -961,15 +977,15 @@ struct BdrvChildClass { * 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); + 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 (*drained_poll)(BdrvChild *child); + bool GRAPH_RDLOCK_PTR (*drained_poll)(BdrvChild *child); /* * Notifies the parent that the filename of its child has changed (e.g. @@ -977,12 +993,26 @@ struct BdrvChildClass { * can update its reference. */ int (*update_filename)(BdrvChild *child, BlockDriverState *new_base, - const char *filename, Error **errp); - - bool (*change_aio_ctx)(BdrvChild *child, AioContext *ctx, - GHashTable *visited, Transaction *tran, + const char *filename, + bool backing_mask_protocol, Error **errp); + /* + * Notifies the parent that the child is trying to change its AioContext. + * The parent may in turn change the AioContext of other nodes in the same + * transaction. Returns true if the change is possible and the transaction + * can be continued. Returns false and sets @errp if not and the transaction + * must be aborted. + * + * @visited will accumulate all visited BdrvChild objects. The caller is + * responsible for freeing the list afterwards. + * + * Must be called with the affected block nodes drained. + */ + bool GRAPH_RDLOCK_PTR (*change_aio_ctx)(BdrvChild *child, AioContext *ctx, + GHashTable *visited, + Transaction *tran, Error **errp); + /* * I/O API functions. These functions are thread-safe. * @@ -1037,8 +1067,8 @@ struct BdrvChild { */ 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; }; /* @@ -1171,11 +1201,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; @@ -1184,8 +1214,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 diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h index da5fb310890..e7c8f1a8567 100644 --- a/include/block/block_int-global-state.h +++ b/include/block/block_int-global-state.h @@ -46,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. @@ -64,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, @@ -82,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. @@ -92,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: @@ -133,7 +139,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, * @buf_size: The amount of data that can be in flight at one time. * @mode: Whether to collapse all images in the chain to the target. * @backing_mode: How to establish the target's backing chain after completion. - * @zero_target: Whether the target should be explicitly zero-initialized + * @target_is_zero: Whether the target already is zero-initialized. * @on_source_error: The action to take upon error reading from the source. * @on_target_error: The action to take upon error writing to the target. * @unmap: Whether to unmap target where source sectors only contain zeroes. @@ -153,7 +159,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, int creation_flags, int64_t speed, uint32_t granularity, int64_t buf_size, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, - bool zero_target, + bool target_is_zero, BlockdevOnError on_source_error, BlockdevOnError on_target_error, bool unmap, const char *filter_node_name, @@ -173,6 +179,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, * all ".has_*" fields are ignored. * @on_source_error: The action to take upon error reading from the source. * @on_target_error: The action to take upon error writing to the target. + * @on_cbw_error: The action to take upon error in copy-before-write operations. * @creation_flags: Flags that control the behavior of the Job lifetime. * See @BlockJobCreateFlags * @cb: Completion function for the job. @@ -187,33 +194,36 @@ 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, BlockdevOnError on_target_error, + OnCbwError on_cbw_error, int creation_flags, 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 @@ -223,7 +233,8 @@ 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 GRAPH_RDLOCK bdrv_recurse_can_replace(BlockDriverState *bs, BlockDriverState *to_replace); @@ -274,7 +285,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: diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h index eb0da7232ef..4f94eb3c5a2 100644 --- a/include/block/block_int-io.h +++ b/include/block/block_int-io.h @@ -38,8 +38,8 @@ 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 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); + BlockDriverState *bs, unsigned int mode, 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); @@ -99,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); @@ -130,32 +130,36 @@ bdrv_co_refresh_total_sectors(BlockDriverState *bs, int64_t hint); int co_wrapper_mixed_bdrv_rdlock bdrv_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); +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 *bdrv_cow_bs(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)); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 058b0c824cd..990f3e179ac 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -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; @@ -137,9 +137,12 @@ BlockJob *block_job_get_locked(const char *id); * Add @bs to the list of BlockDriverState that are involved in * @job. This means that all operations will be blocked on @bs while * @job exists. + * + * All block nodes must be drained. */ -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 +175,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 104824040cc..4c3d2e25a20 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -67,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); }; /* @@ -99,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: diff --git a/include/block/export.h b/include/block/export.h index f2fe0f8078f..4bd9531d4d9 100644 --- a/include/block/export.h +++ b/include/block/export.h @@ -29,6 +29,9 @@ typedef struct BlockExportDriver { */ size_t instance_size; + /* True if the export type supports running on an inactive node */ + bool supports_inactive; + /* Creates and starts a new block export */ int (*create)(BlockExport *, BlockExportOptions *, Error **); diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h index 7e04f98ff02..2c26c721081 100644 --- a/include/block/graph-lock.h +++ b/include/block/graph-lock.h @@ -20,8 +20,6 @@ #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 @@ -110,20 +108,17 @@ void unregister_aiocontext(AioContext *ctx); * * The wrlock can only be taken from the main loop, with BQL held, as only the * main loop is allowed to modify the graph. - * - * If @bs is non-NULL, its AioContext is temporarily released. - * - * This function polls. Callers must not hold the lock of any AioContext other - * than the current one and the one of @bs. */ -void bdrv_graph_wrlock(BlockDriverState *bs) TSA_ACQUIRE(graph_lock) TSA_NO_TSA; +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 bdrv_graph_wrunlock(void) TSA_RELEASE(graph_lock) TSA_NO_TSA; +void no_coroutine_fn TSA_RELEASE(graph_lock) TSA_NO_TSA +bdrv_graph_wrunlock(void); /* * bdrv_graph_co_rdlock: @@ -212,31 +207,38 @@ typedef struct GraphLockable { } GraphLockable; * unlocked. TSA_ASSERT_SHARED() makes sure that the following calls know that * we hold the lock while unlocking is left unchecked. */ -static inline GraphLockable * TSA_ASSERT_SHARED(graph_lock) TSA_NO_TSA coroutine_fn +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_NO_TSA coroutine_fn -graph_lockable_auto_unlock(GraphLockable *x) +static inline void TSA_RELEASE_SHARED(graph_lock) coroutine_fn +graph_lockable_auto_unlock(GraphLockable **x) { bdrv_graph_co_rdunlock(); } -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockable, graph_lockable_auto_unlock) +#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 (g_autoptr(GraphLockable) var = graph_lockable_auto_lock(GML_OBJ_()); \ + for (GraphLockable *unlock_var GRAPH_AUTO_UNLOCK = \ + graph_lockable_auto_lock(GML_OBJ_()), \ + *var = unlock_var; \ var; \ - graph_lockable_auto_unlock(var), var = NULL) + var = NULL) #define WITH_GRAPH_RDLOCK_GUARD() \ WITH_GRAPH_RDLOCK_GUARD_(glue(graph_lockable_auto, __COUNTER__)) #define GRAPH_RDLOCK_GUARD(x) \ - g_autoptr(GraphLockable) \ + GraphLockable * GRAPH_AUTO_UNLOCK \ glue(graph_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \ graph_lockable_auto_lock(GML_OBJ_()) diff --git a/include/block/nbd.h b/include/block/nbd.h index f672b76173b..92987c76fd6 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -29,9 +29,23 @@ 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 */ typedef struct NBDOption { @@ -60,20 +74,23 @@ typedef enum NBDMode { 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 */ - /* TODO add NBD_MODE_EXTENDED */ + NBD_MODE_EXTENDED, /* newstyle, extended headers enabled */ } NBDMode; -/* Transmission phase structs - * - * Note: these are _NOT_ the same as the network representation of an NBD - * request and reply! +/* Transmission phase structs */ + +/* + * Note: NBDRequest is _NOT_ the same as the network representation of an NBD + * request! */ typedef struct NBDRequest { uint64_t cookie; - uint64_t from; - uint32_t len; + 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_* */ + 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 { @@ -91,20 +108,36 @@ typedef struct NBDStructuredReplyChunk { 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 @cookie 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 cookie; - } QEMU_PACKED; + }; } 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 { @@ -131,14 +164,34 @@ typedef struct NBDStructuredError { typedef struct NBDStructuredMeta { /* 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 */ @@ -156,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. */ @@ -192,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)) @@ -209,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 @@ -217,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 { @@ -248,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) @@ -305,7 +374,7 @@ typedef 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 @@ -335,7 +404,8 @@ 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); @@ -346,18 +416,21 @@ 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); void nbd_server_is_qemu_nbd(int max_connections); bool nbd_server_is_running(void); int nbd_server_max_connections(void); -void nbd_server_start(SocketAddress *addr, const char *tls_creds, - const char *tls_authz, uint32_t max_connections, - Error **errp); +void nbd_server_start(SocketAddress *addr, uint32_t handshake_max_secs, + const char *tls_creds, const char *tls_authz, + uint32_t max_connections, Error **errp); void nbd_server_start_options(NbdServerOptions *arg, Error **errp); /* nbd_read diff --git a/include/block/nvme.h b/include/block/nvme.h index bb231d0b9ad..358e516e38b 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -142,9 +142,9 @@ enum NvmeCapMask { ((cap) |= (uint64_t)((val) & CAP_CMBS_MASK) << CAP_CMBS_SHIFT) enum NvmeCapCss { - NVME_CAP_CSS_NVM = 1 << 0, - NVME_CAP_CSS_CSI_SUPP = 1 << 6, - NVME_CAP_CSS_ADMIN_ONLY = 1 << 7, + NVME_CAP_CSS_NCSS = 1 << 0, + NVME_CAP_CSS_IOCSS = 1 << 6, + NVME_CAP_CSS_NOIOCSS = 1 << 7, }; enum NvmeCcShift { @@ -177,7 +177,7 @@ enum NvmeCcMask { enum NvmeCcCss { NVME_CC_CSS_NVM = 0x0, - NVME_CC_CSS_CSI = 0x6, + NVME_CC_CSS_ALL = 0x6, NVME_CC_CSS_ADMIN_ONLY = 0x7, }; @@ -799,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 { @@ -820,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, @@ -899,8 +906,7 @@ enum NvmeStatusCodes { NVME_SGL_DESCR_TYPE_INVALID = 0x0011, NVME_INVALID_USE_OF_CMB = 0x0012, NVME_INVALID_PRP_OFFSET = 0x0013, - NVME_CMD_SET_CMB_REJECTED = 0x002b, - NVME_INVALID_CMD_SET = 0x002c, + NVME_COMMAND_INTERRUPTED = 0x0021, NVME_FDP_DISABLED = 0x0029, NVME_INVALID_PHID_LIST = 0x002a, NVME_LBA_RANGE = 0x0080, @@ -933,10 +939,16 @@ enum NvmeStatusCodes { NVME_INVALID_SEC_CTRL_STATE = 0x0120, NVME_INVALID_NUM_RESOURCES = 0x0121, NVME_INVALID_RESOURCE_ID = 0x0122, + NVME_IOCS_NOT_SUPPORTED = 0x0129, + NVME_IOCS_NOT_ENABLED = 0x012a, + NVME_IOCS_COMBINATION_REJECTED = 0x012b, + NVME_INVALID_IOCS = 0x012c, NVME_CONFLICTING_ATTRS = 0x0180, 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, @@ -1006,6 +1018,40 @@ typedef struct QEMU_PACKED NvmeSmartLog { uint8_t reserved2[320]; } NvmeSmartLog; +typedef struct QEMU_PACKED NvmeSmartLogExtended { + uint64_t physical_media_units_written[2]; + uint64_t physical_media_units_read[2]; + uint64_t bad_user_blocks; + uint64_t bad_system_nand_blocks; + uint64_t xor_recovery_count; + uint64_t uncorrectable_read_error_count; + uint64_t soft_ecc_error_count; + uint64_t end2end_correction_counts; + uint8_t system_data_percent_used; + uint8_t refresh_counts[7]; + uint64_t user_data_erase_counts; + uint16_t thermal_throttling_stat_and_count; + uint16_t dssd_spec_version[3]; + uint64_t pcie_correctable_error_count; + uint32_t incomplete_shutdowns; + uint32_t rsvd116; + uint8_t percent_free_blocks; + uint8_t rsvd121[7]; + uint16_t capacity_health; + uint8_t nvme_errata_ver; + uint8_t rsvd131[5]; + uint64_t unaligned_io; + uint64_t security_ver_num; + uint64_t total_nuse; + uint64_t plp_start_count[2]; + uint64_t endurance_estimate[2]; + uint64_t pcie_retraining_count; + uint64_t power_state_change_count; + uint8_t rsvd208[286]; + uint16_t log_page_version; + uint64_t log_page_guid[2]; +} NvmeSmartLogExtended; + #define NVME_SMART_WARN_MAX 6 enum NvmeSmartWarn { NVME_SMART_SPARE = 1 << 0, @@ -1043,6 +1089,12 @@ enum NvmeLogIdentifier { NVME_LOG_FDP_RUH_USAGE = 0x21, NVME_LOG_FDP_STATS = 0x22, NVME_LOG_FDP_EVENTS = 0x23, + NVME_LOG_VENDOR_START = 0xc0, + NVME_LOG_VENDOR_END = 0xff, +}; + +enum NvmeOcpLogIdentifier { + NVME_OCP_EXTENDED_SMART_INFO = 0xc0, }; typedef struct QEMU_PACKED NvmePSD { @@ -1068,15 +1120,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 { @@ -1155,6 +1210,8 @@ typedef struct NvmeIdCtrlZoned { uint8_t rsvd1[4095]; } NvmeIdCtrlZoned; +#define NVME_ID_CTRL_NVM_DMRL_MAX 255 + typedef struct NvmeIdCtrlNvm { uint8_t vsl; uint8_t wzsl; @@ -1172,6 +1229,7 @@ enum NvmeIdCtrlOaes { enum NvmeIdCtrlCtratt { NVME_CTRATT_ENDGRPS = 1 << 4, NVME_CTRATT_ELBAS = 1 << 15, + NVME_CTRATT_MEM = 1 << 16, NVME_CTRATT_FDPS = 1 << 19, }; @@ -1179,9 +1237,10 @@ 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_NMS = 1 << 3, NVME_OACS_DIRECTIVES = 1 << 5, - NVME_OACS_DBBUF = 1 << 8, + NVME_OACS_VMS = 1 << 7, + NVME_OACS_DBCS = 1 << 8, }; enum NvmeIdCtrlOncs { @@ -1194,11 +1253,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 { @@ -1271,6 +1334,8 @@ enum NvmeNsAttachmentOperation { #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, @@ -1332,7 +1397,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 { @@ -1397,9 +1464,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; @@ -1420,8 +1506,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 { @@ -1499,6 +1587,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 { @@ -1832,8 +1930,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); @@ -1847,6 +1945,7 @@ static inline void _nvme_check_size(void) QEMU_BUILD_BUG_ON(sizeof(NvmeErrorLog) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeFwSlotInfoLog) != 512); QEMU_BUILD_BUG_ON(sizeof(NvmeSmartLog) != 512); + QEMU_BUILD_BUG_ON(sizeof(NvmeSmartLogExtended) != 512); QEMU_BUILD_BUG_ON(sizeof(NvmeEffectsLog) != 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeIdCtrl) != 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeIdCtrlZoned) != 4096); @@ -1854,6 +1953,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); diff --git a/include/block/qapi.h b/include/block/qapi.h index 8663971c58b..54c48de26a3 100644 --- a/include/block/qapi.h +++ b/include/block/qapi.h @@ -29,18 +29,17 @@ #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, - bool flat, - bool skip_implicit_filters, - 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); diff --git a/include/block/qdict.h b/include/block/qdict.h index b4c28d96a9e..53c4df4cb26 100644 --- a/include/block/qdict.h +++ b/include/block/qdict.h @@ -10,7 +10,7 @@ #ifndef BLOCK_QDICT_H #define BLOCK_QDICT_H -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" QObject *qdict_crumple(const QDict *src, Error **errp); void qdict_flatten(QDict *qdict); diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h index 0f63c2800ce..6570244496a 100644 --- a/include/block/raw-aio.h +++ b/include/block/raw-aio.h @@ -17,6 +17,7 @@ #define QEMU_RAW_AIO_H #include "block/aio.h" +#include "block/block-common.h" #include "qemu/iov.h" /* AIO request types */ @@ -58,22 +59,36 @@ void laio_cleanup(LinuxAioState *s); /* 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); + int type, BdrvRequestFlags flags, + uint64_t dev_max_batch); +bool laio_has_fdsync(int); +bool laio_has_fua(void); void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context); void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context); +#else +static inline bool laio_has_fua(void) +{ + return false; +} #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); /* 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); + QEMUIOVector *qiov, int type, + BdrvRequestFlags flags); void luring_detach_aio_context(LuringState *s, AioContext *old_context); void luring_attach_aio_context(LuringState *s, AioContext *new_context); +bool luring_has_fua(void); +#else +static inline bool luring_has_fua(void) +{ + return false; +} #endif #ifdef _WIN32 diff --git a/include/block/snapshot.h b/include/block/snapshot.h index 50ff924710f..304cc6ea61c 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 948ff5f30c3..dd48cf07e85 100644 --- a/include/block/thread-pool.h +++ b/include/block/thread-pool.h @@ -24,20 +24,70 @@ typedef int ThreadPoolFunc(void *opaque); -typedef struct ThreadPool ThreadPool; +typedef struct ThreadPoolAio ThreadPoolAio; -ThreadPool *thread_pool_new(struct AioContext *ctx); -void thread_pool_free(ThreadPool *pool); +ThreadPoolAio *thread_pool_new_aio(struct AioContext *ctx); +void thread_pool_free_aio(ThreadPoolAio *pool); /* - * thread_pool_submit* API: submit I/O requests in the thread's + * thread_pool_submit_{aio,co} 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(ThreadPoolAio *pool, struct AioContext *ctx); + +/* ------------------------------------------- */ +/* Generic thread pool types and methods below */ +typedef struct ThreadPool ThreadPool; + +/* Create a new thread pool. Never returns NULL. */ +ThreadPool *thread_pool_new(void); + +/* + * Free the thread pool. + * Waits for all the previously submitted work to complete before performing + * the actual freeing operation. + */ +void thread_pool_free(ThreadPool *pool); + +/* + * Submit a new work (task) for the pool. + * + * @opaque_destroy is an optional GDestroyNotify for the @opaque argument + * to the work function at @func. + */ +void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, + void *opaque, GDestroyNotify opaque_destroy); + +/* + * Submit a new work (task) for the pool, making sure it starts getting + * processed immediately, launching a new thread for it if necessary. + * + * @opaque_destroy is an optional GDestroyNotify for the @opaque argument + * to the work function at @func. + */ +void thread_pool_submit_immediate(ThreadPool *pool, ThreadPoolFunc *func, + void *opaque, GDestroyNotify opaque_destroy); + +/* + * Wait for all previously submitted work to complete before returning. + * + * Can be used as a barrier between two sets of tasks executed on a thread + * pool without destroying it or in a performance sensitive path where the + * caller just wants to wait for all tasks to complete while deferring the + * pool free operation for later, less performance sensitive time. + */ +void thread_pool_wait(ThreadPool *pool); -void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx); +/* Set the maximum number of threads in the pool. */ +bool thread_pool_set_max_threads(ThreadPool *pool, int max_threads); + +/* + * Adjust the maximum number of threads in the pool to give each task its + * own thread (exactly one thread per task). + */ +bool thread_pool_adjust_max_threads_to_work(ThreadPool *pool); #endif diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h index ff282fc0f8a..2355e8d9de6 100644 --- a/include/block/throttle-groups.h +++ b/include/block/throttle-groups.h @@ -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 index fd884eb8ce3..a3ee62b027a 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -7,7 +7,7 @@ typedef struct QEMU_PACKED UfsReg { uint32_t cap; - uint32_t rsvd0; + uint32_t mcqcap; uint32_t ver; uint32_t rsvd1; uint32_t hcpid; @@ -46,6 +46,13 @@ typedef struct QEMU_PACKED UfsReg { 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)) @@ -57,6 +64,15 @@ REG32(CAP, offsetof(UfsReg, cap)) 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)) @@ -78,6 +94,7 @@ REG32(IS, offsetof(UfsReg, is)) 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) @@ -95,6 +112,7 @@ REG32(IE, offsetof(UfsReg, ie)) 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) @@ -111,14 +129,14 @@ REG32(UECT, offsetof(UfsReg, uect)) REG32(UECDME, offsetof(UfsReg, uecdme)) REG32(UTRIACR, offsetof(UfsReg, utriacr)) REG32(UTRLBA, offsetof(UfsReg, utrlba)) - FIELD(UTRLBA, UTRLBA, 9, 22) + 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, 9, 22) + FIELD(UTMRLBA, UTMRLBA, 10, 22) REG32(UTMRLBAU, offsetof(UfsReg, utmrlbau)) REG32(UTMRLDBR, offsetof(UfsReg, utmrldbr)) REG32(UTMRLCLR, offsetof(UfsReg, utmrlclr)) @@ -128,9 +146,14 @@ 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_CEFES_SHIFT) | (1 << R_IS_SBFES_SHIFT) | \ + ((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) | \ @@ -157,6 +180,84 @@ REG32(CCAP, offsetof(UfsReg, ccap)) ((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; @@ -360,7 +461,7 @@ typedef struct Attributes { 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_case_rough_temperature; uint8_t device_too_high_temp_boundary; uint8_t device_too_low_temp_boundary; uint8_t throttling_status; @@ -379,7 +480,7 @@ typedef struct Attributes { /* Command response result code */ typedef enum CommandRespCode { - UFS_COMMAND_RESULT_SUCESS = 0x00, + UFS_COMMAND_RESULT_SUCCESS = 0x00, UFS_COMMAND_RESULT_FAIL = 0x01, } CommandRespCode; @@ -657,11 +758,17 @@ typedef struct QEMU_PACKED UtpTaskReqDesc { #define UFS_UPIU_MAX_WB_LUN_ID 8 /* - * WriteBooster buffer lifetime has a limit setted by vendor. + * 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 @@ -966,6 +1073,11 @@ enum health_desc_param { UFS_HEALTH_DESC_PARAM_LIFE_TIME_EST_B = 0x4, }; +enum { + UFS_DEV_HIGH_TEMP_NOTIF = BIT(4), + UFS_DEV_LOW_TEMP_NOTIF = BIT(5), +}; + /* WriteBooster buffer mode */ enum { UFS_WB_BUF_MODE_LU_DEDICATED = 0x0, @@ -984,6 +1096,12 @@ enum ufs_lu_wp_type { UFS_LU_PERM_WP = 0x02, }; +/* Exception event mask values */ +enum { + MASK_EE_TOO_HIGH_TEMP = BIT(3), + MASK_EE_TOO_LOW_TEMP = BIT(4), +}; + /* UTP QUERY Transaction Specific Fields OpCode */ enum query_opcode { UFS_UPIU_QUERY_OPCODE_NOP = 0x0, @@ -1064,9 +1182,31 @@ typedef struct QEMU_PACKED UtpUpiuRsp { }; } 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) != 0x104); + 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); @@ -1086,5 +1226,7 @@ static inline void _ufs_check_size(void) 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/chardev/char-fe.h b/include/chardev/char-fe.h index 0ff6f875116..8ef05b3dd09 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; }; /** @@ -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: @@ -223,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); @@ -237,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); @@ -248,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 0708ca6fa97..d6d13ad37f9 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 01df55f9e8c..429852f8d9d 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -232,6 +232,7 @@ OBJECT_DECLARE_TYPE(Chardev, ChardevClass, CHARDEV) #define TYPE_CHARDEV_NULL "chardev-null" #define TYPE_CHARDEV_MUX "chardev-mux" +#define TYPE_CHARDEV_HUB "chardev-hub" #define TYPE_CHARDEV_RINGBUF "chardev-ringbuf" #define TYPE_CHARDEV_PTY "chardev-pty" #define TYPE_CHARDEV_CONSOLE "chardev-console" diff --git a/include/crypto/afsplit.h b/include/crypto/afsplit.h index 4894d643308..06f28fe67c8 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/block.h b/include/crypto/block.h index 4f63a378727..b013d27f008 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 083e12a7d9e..92939310ef0 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 00000000000..446931fe05b --- /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/hash.h b/include/crypto/hash.h index 54d87aa2a13..1868d4a0f78 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 ad4d7784161..da8a1e3ceb9 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 a09d5732daf..bfa5d28103f 100644 --- a/include/crypto/ivgen.h +++ b/include/crypto/ivgen.h @@ -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); @@ -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 2c31a44a276..cf59fce610e 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/tlssession.h b/include/crypto/tlssession.h index 571049bd0ec..d77ae0d4237 100644 --- a/include/crypto/tlssession.h +++ b/include/crypto/tlssession.h @@ -75,12 +75,14 @@ * GINT_TO_POINTER(fd)); * * while (1) { - * if (qcrypto_tls_session_handshake(sess, errp) < 0) { + * int ret = qcrypto_tls_session_handshake(sess, errp); + * + * if (ret < 0) { * qcrypto_tls_session_free(sess); * return -1; * } * - * switch(qcrypto_tls_session_get_handshake_status(sess)) { + * switch(ret) { * case QCRYPTO_TLS_HANDSHAKE_COMPLETE: * if (qcrypto_tls_session_check_credentials(sess, errp) < )) { * qcrypto_tls_session_free(sess); @@ -107,6 +109,7 @@ typedef struct QCryptoTLSSession QCryptoTLSSession; +#define QCRYPTO_TLS_SESSION_ERR_BLOCK -2 /** * qcrypto_tls_session_new: @@ -169,7 +172,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSSession, qcrypto_tls_session_free) * * Validate the peer's credentials after a successful * TLS handshake. It is an error to call this before - * qcrypto_tls_session_get_handshake_status() returns + * qcrypto_tls_session_handshake() returns * QCRYPTO_TLS_HANDSHAKE_COMPLETE * * Returns 0 if the credentials validated, -1 on error @@ -177,12 +180,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,41 +221,55 @@ 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 * registered with qcrypto_tls_session_set_callbacks() * * It is an error to call this before - * qcrypto_tls_session_get_handshake_status() returns + * qcrypto_tls_session_handshake() 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_session_handshake() 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: @@ -268,8 +291,7 @@ size_t qcrypto_tls_session_check_pending(QCryptoTLSSession *sess); * the underlying data channel is non-blocking, then * this method may return control before the handshake * is complete. On non-blocking channels the - * qcrypto_tls_session_get_handshake_status() method - * should be used to determine whether the handshake + * return value determines whether the handshake * has completed, or is waiting to send or receive * data. In the latter cases, the caller should setup * an event loop watch and call this method again @@ -285,22 +307,27 @@ typedef enum { QCRYPTO_TLS_HANDSHAKE_RECVING, } QCryptoTLSSessionHandshakeStatus; +typedef enum { + QCRYPTO_TLS_BYE_COMPLETE, + QCRYPTO_TLS_BYE_SENDING, + QCRYPTO_TLS_BYE_RECVING, +} QCryptoTLSSessionByeStatus; + /** - * qcrypto_tls_session_get_handshake_status: - * @sess: the TLS session object - * - * Check the status of the TLS handshake. This - * is used with non-blocking data channels to - * determine whether the handshake is waiting - * to send or receive further data to/from the - * remote peer. + * qcrypto_tls_session_bye: + * @session: the TLS session object + * @errp: pointer to a NULL-initialized error object * - * Once this returns QCRYPTO_TLS_HANDSHAKE_COMPLETE - * it is permitted to send/receive payload data on - * the channel + * Start, or continue, a TLS termination sequence. If the underlying + * data channel is non-blocking, then this method may return control + * before the termination is complete. The return value will indicate + * whether the termination has completed, or is waiting to send or + * receive data. In the latter cases, the caller should setup an event + * loop watch and call this method again once the underlying data + * channel is ready to read or write again. */ -QCryptoTLSSessionHandshakeStatus -qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess); +int +qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp); /** * qcrypto_tls_session_get_key_size: diff --git a/include/crypto/x509-utils.h b/include/crypto/x509-utils.h new file mode 100644 index 00000000000..1e99661a711 --- /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 e29068dd977..c43033f7f60 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 2324f6b1a46..3b50ecfb540 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -232,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 @@ -396,6 +388,14 @@ typedef struct disassemble_info { /* 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. */ void *target_info; @@ -444,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*); diff --git a/include/disas/disas.h b/include/disas/disas.h index 176775eff77..c702b1effc1 100644 --- a/include/disas/disas.h +++ b/include/disas/disas.h @@ -2,13 +2,18 @@ #define QEMU_DISAS_H /* Disassemble this for me please... (debugging). */ +#ifdef CONFIG_TCG void disas(FILE *out, const void *code, size_t size); -void target_disas(FILE *out, CPUState *cpu, uint64_t code, size_t size); +void target_disas(FILE *out, CPUState *cpu, const DisasContextBase *db); +#endif 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(uint64_t orig_addr); diff --git a/include/exec/abi_ptr.h b/include/exec/abi_ptr.h new file mode 100644 index 00000000000..2aedcceb0c2 --- /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/breakpoint.h b/include/exec/breakpoint.h new file mode 100644 index 00000000000..95f0482e6d0 --- /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 deleted file mode 100644 index ba2dd4b5dfc..00000000000 --- a/include/exec/confidential-guest-support.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * QEMU Confidential Guest support - * This interface describes the common pieces between various - * schemes for protecting guest memory or other state against a - * compromised hypervisor. This includes memory encryption (AMD's - * SEV and Intel's MKTME) or special protection modes (PEF on POWER, - * or PV on s390x). - * - * Copyright Red Hat. - * - * Authors: - * David Gibson - * - * 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_CONFIDENTIAL_GUEST_SUPPORT_H -#define QEMU_CONFIDENTIAL_GUEST_SUPPORT_H - -#ifndef CONFIG_USER_ONLY - -#include "qom/object.h" - -#define TYPE_CONFIDENTIAL_GUEST_SUPPORT "confidential-guest-support" -OBJECT_DECLARE_SIMPLE_TYPE(ConfidentialGuestSupport, CONFIDENTIAL_GUEST_SUPPORT) - -struct ConfidentialGuestSupport { - Object parent; - - /* - * ready: flag set by CGS initialization code once it's ready to - * start executing instructions in a potentially-secure - * guest - * - * The definition here is a bit fuzzy, because this is essentially - * part of a self-sanity-check, rather than a strict mechanism. - * - * It's not feasible to have a single point in the common machine - * init path to configure confidential guest support, because - * different mechanisms have different interdependencies requiring - * initialization in different places, often in arch or machine - * type specific code. It's also usually not possible to check - * for invalid configurations until that initialization code. - * That means it would be very easy to have a bug allowing CGS - * init to be bypassed entirely in certain configurations. - * - * Silently ignoring a requested security feature would be bad, so - * to avoid that we check late in init that this 'ready' flag is - * set if CGS was requested. If the CGS init hasn't happened, and - * so 'ready' is not set, we'll abort. - */ - bool ready; -}; - -typedef struct ConfidentialGuestSupportClass { - ObjectClass parent; -} ConfidentialGuestSupportClass; - -#endif /* !CONFIG_USER_ONLY */ - -#endif /* QEMU_CONFIDENTIAL_GUEST_SUPPORT_H */ diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h deleted file mode 100644 index c2c62160c6d..00000000000 --- a/include/exec/cpu-all.h +++ /dev/null @@ -1,497 +0,0 @@ -/* - * defines common to all virtual CPUs - * - * 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 CPU_ALL_H -#define CPU_ALL_H - -#include "exec/cpu-common.h" -#include "exec/memory.h" -#include "exec/tswap.h" -#include "qemu/thread.h" -#include "hw/core/cpu.h" -#include "qemu/rcu.h" - -#define EXCP_INTERRUPT 0x10000 /* async interruption */ -#define EXCP_HLT 0x10001 /* hlt instruction reached */ -#define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */ -#define EXCP_HALTED 0x10003 /* cpu is halted (waiting for external event) */ -#define EXCP_YIELD 0x10004 /* cpu wants to yield timeslice to another */ -#define EXCP_ATOMIC 0x10005 /* stop-the-world and emulate atomic */ - -/* some important defines: - * - * HOST_BIG_ENDIAN : whether the host cpu is big endian and - * otherwise little endian. - * - * TARGET_BIG_ENDIAN : same for the target cpu - */ - -#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN -#define BSWAP_NEEDED -#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. - */ -#if TARGET_BIG_ENDIAN -#define lduw_p(p) lduw_be_p(p) -#define ldsw_p(p) ldsw_be_p(p) -#define ldl_p(p) ldl_be_p(p) -#define ldq_p(p) ldq_be_p(p) -#define stw_p(p, v) stw_be_p(p, v) -#define stl_p(p, v) stl_be_p(p, v) -#define stq_p(p, v) stq_be_p(p, v) -#define ldn_p(p, sz) ldn_be_p(p, sz) -#define stn_p(p, sz, v) stn_be_p(p, sz, v) -#else -#define lduw_p(p) lduw_le_p(p) -#define ldsw_p(p) ldsw_le_p(p) -#define ldl_p(p) ldl_le_p(p) -#define ldq_p(p) ldq_le_p(p) -#define stw_p(p, v) stw_le_p(p, v) -#define stl_p(p, v) stl_le_p(p, v) -#define stq_p(p, v) stq_le_p(p, v) -#define ldn_p(p, sz) ldn_le_p(p, sz) -#define stn_p(p, sz, v) stn_le_p(p, sz, v) -#endif - -/* MMU memory access macros */ - -#if defined(CONFIG_USER_ONLY) -#include "exec/user/abitypes.h" -#include "exec/user/guest-base.h" - -extern bool have_guest_base; - -/* - * If non-zero, the guest virtual address space is a contiguous subset - * of the host virtual address space, i.e. '-R reserved_va' is in effect - * either from the command-line or by default. The value is the last - * byte of the guest address space e.g. UINT32_MAX. - * - * If zero, the host and guest virtual address spaces are intermingled. - */ -extern unsigned long reserved_va; - -/* - * Limit the guest addresses as best we can. - * - * When not using -R reserved_va, we cannot really limit the guest - * to less address space than the host. For 32-bit guests, this - * acts as a sanity check that we're not giving the guest an address - * that it cannot even represent. For 64-bit guests... the address - * might not be what the real kernel would give, but it is at least - * representable in the guest. - * - * TODO: Improve address allocation to avoid this problem, and to - * avoid setting bits at the top of guest addresses that might need - * to be used for tags. - */ -#define GUEST_ADDR_MAX_ \ - ((MIN_CONST(TARGET_VIRT_ADDR_SPACE_BITS, TARGET_ABI_BITS) <= 32) ? \ - UINT32_MAX : ~0ul) -#define GUEST_ADDR_MAX (reserved_va ? : GUEST_ADDR_MAX_) - -#else - -#include "exec/hwaddr.h" - -#define SUFFIX -#define ARG1 as -#define ARG1_DECL AddressSpace *as -#define TARGET_ENDIANNESS -#include "exec/memory_ldst.h.inc" - -#define SUFFIX _cached_slow -#define ARG1 cache -#define ARG1_DECL MemoryRegionCache *cache -#define TARGET_ENDIANNESS -#include "exec/memory_ldst.h.inc" - -static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val) -{ - address_space_stl_notdirty(as, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -#define SUFFIX -#define ARG1 as -#define ARG1_DECL AddressSpace *as -#define TARGET_ENDIANNESS -#include "exec/memory_ldst_phys.h.inc" - -/* Inline fast path for direct RAM access. */ -#define ENDIANNESS -#include "exec/memory_ldst_cached.h.inc" - -#define SUFFIX _cached -#define ARG1 cache -#define ARG1_DECL MemoryRegionCache *cache -#define TARGET_ENDIANNESS -#include "exec/memory_ldst_phys.h.inc" -#endif - -/* page related stuff */ - -#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; }) -#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) -#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); - -typedef int (*walk_memory_regions_fn)(void *, target_ulong, - target_ulong, unsigned long); -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 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) - * @address: guest virtual address - * - * Return TARGET_PAGE_DATA_SIZE bytes of out-of-band data to associate - * with the guest page at @address, allocating it if necessary. The - * caller should already have verified that the address is valid. - * - * The memory will be freed when the guest page is deallocated, - * e.g. with the munmap system call. - */ -void *page_get_target_data(target_ulong address) - __attribute__((returns_nonnull)); -#endif - -CPUArchState *cpu_copy(CPUArchState *env); - -/* Flags for use in ENV->INTERRUPT_PENDING. - - The numbers assigned here are non-sequential in order to preserve - binary compatibility with the vmstate dump. Bit 0 (0x0001) was - previously used for CPU_INTERRUPT_EXIT, and is cleared when loading - the vmstate dump. */ - -/* External hardware interrupt pending. This is typically used for - interrupts from devices. */ -#define CPU_INTERRUPT_HARD 0x0002 - -/* Exit the current TB. This is typically used when some system-level device - makes some change to the memory mapping. E.g. the a20 line change. */ -#define CPU_INTERRUPT_EXITTB 0x0004 - -/* Halt the CPU. */ -#define CPU_INTERRUPT_HALT 0x0020 - -/* Debug event pending. */ -#define CPU_INTERRUPT_DEBUG 0x0080 - -/* Reset signal. */ -#define CPU_INTERRUPT_RESET 0x0400 - -/* Several target-specific external hardware interrupts. Each target/cpu.h - should define proper names based on these defines. */ -#define CPU_INTERRUPT_TGT_EXT_0 0x0008 -#define CPU_INTERRUPT_TGT_EXT_1 0x0010 -#define CPU_INTERRUPT_TGT_EXT_2 0x0040 -#define CPU_INTERRUPT_TGT_EXT_3 0x0200 -#define CPU_INTERRUPT_TGT_EXT_4 0x1000 - -/* Several target-specific internal interrupts. These differ from the - preceding target-specific interrupts in that they are intended to - originate from within the cpu itself, typically in response to some - instruction being executed. These, therefore, are not masked while - single-stepping within the debugger. */ -#define CPU_INTERRUPT_TGT_INT_0 0x0100 -#define CPU_INTERRUPT_TGT_INT_1 0x0800 -#define CPU_INTERRUPT_TGT_INT_2 0x2000 - -/* First unused bit: 0x4000. */ - -/* The set of all bits that should be masked when single-stepping. */ -#define CPU_INTERRUPT_SSTEP_MASK \ - (CPU_INTERRUPT_HARD \ - | CPU_INTERRUPT_TGT_EXT_0 \ - | CPU_INTERRUPT_TGT_EXT_1 \ - | CPU_INTERRUPT_TGT_EXT_2 \ - | CPU_INTERRUPT_TGT_EXT_3 \ - | CPU_INTERRUPT_TGT_EXT_4) - -#ifdef CONFIG_USER_ONLY - -/* - * Allow some level of source compatibility with softmmu. We do not - * support any of the more exotic features, so only invalid pages may - * be signaled by probe_access_flags(). - */ -#define TLB_INVALID_MASK (1 << (TARGET_PAGE_BITS_MIN - 1)) -#define TLB_MMIO (1 << (TARGET_PAGE_BITS_MIN - 2)) -#define TLB_WATCHPOINT 0 - -#else - -/* - * Flags stored in the low bits of the TLB virtual address. - * These are defined so that fast path ram access is all zeros. - * The flags all must be between TARGET_PAGE_BITS and - * maximum address alignment bit. - * - * 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)) -/* Set if TLB entry references a clean RAM page. The iotlb entry will - contain the page physical address. */ -#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 writes ignored. */ -#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 - * in a TCG backend. - */ -#define TLB_FLAGS_MASK \ - (TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_MMIO \ - | 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) - -#define TLB_SLOW_FLAGS_MASK (TLB_BSWAP | TLB_WATCHPOINT) - -/* 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 - * TLB entry @tlb_addr - * - * @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(uint64_t tlb_addr, vaddr addr) -{ - return addr == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)); -} - -/** - * tlb_hit: return true if @addr is a hit against the TLB entry @tlb_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(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; -} - -#endif /* CPU_ALL_H */ diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 41788c0bdd9..a68485547d5 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -1,34 +1,29 @@ +/* + * 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. */ - -#ifndef CONFIG_USER_ONLY +#include "exec/vaddr.h" #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 */ @@ -38,26 +33,17 @@ 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); -#if !defined(CONFIG_USER_ONLY) - enum device_endian { DEVICE_NATIVE_ENDIAN, DEVICE_BIG_ENDIAN, DEVICE_LITTLE_ENDIAN, }; -#if HOST_BIG_ENDIAN -#define DEVICE_HOST_ENDIAN DEVICE_BIG_ENDIAN -#else -#define DEVICE_HOST_ENDIAN DEVICE_LITTLE_ENDIAN -#endif - /* address in the RAM (different from a physical address) */ #if defined(CONFIG_XEN_BACKEND) typedef uint64_t ram_addr_t; @@ -71,11 +57,26 @@ typedef uintptr_t ram_addr_t; /* memory API */ -void qemu_ram_remap(ram_addr_t addr, ram_addr_t length); +void qemu_ram_remap(ram_addr_t addr); /* This should not be used by devices. */ ram_addr_t qemu_ram_addr_from_host(void *ptr); ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr); RAMBlock *qemu_ram_block_by_name(const char *name); + +/* + * Translates a host ptr back to a RAMBlock and an offset in that RAMBlock. + * + * @ptr: The host pointer to translate. + * @round_offset: Whether to round the result offset down to a target page + * @offset: Will be set to the offset within the returned RAMBlock. + * + * Returns: RAMBlock (or NULL if not found) + * + * By the time this function returns, the returned pointer is not protected + * by RCU anymore. If the caller is not within an RCU critical section and + * does not hold the BQL, it must have other means of protecting the + * pointer, such as a reference to the memory region that owns the RAMBlock. + */ RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset, ram_addr_t *offset); ram_addr_t qemu_ram_block_host_offset(RAMBlock *rb, void *host); @@ -120,6 +121,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); @@ -138,8 +147,6 @@ void *cpu_physical_memory_map(hwaddr addr, 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); @@ -156,8 +163,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); - -#endif +int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t start, + size_t length); /* Returns: 0 on success, -1 on error */ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, @@ -166,4 +173,91 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, /* vl.c */ void list_cpus(void); +#ifdef CONFIG_TCG +#include "qemu/atomic.h" + +/** + * cpu_unwind_state_data: + * @cpu: the cpu context + * @host_pc: the host pc within the translation + * @data: output data + * + * Attempt to load 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); + +/** + * cpu_loop_exit_requested: + * @cpu: The CPU state to be tested + * + * Indicate if somebody asked for a return of the CPU to the main loop + * (e.g., via cpu_exit() or cpu_interrupt()). + * + * This is helpful for architectures that support interruptible + * instructions. After writing back all state to registers/memory, this + * call can be used to check if it makes sense to return to the main loop + * or to continue executing the interruptible instruction. + */ +static inline bool cpu_loop_exit_requested(CPUState *cpu) +{ + return (int32_t)qatomic_read(&cpu->neg.icount_decr.u32) < 0; +} + +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); +} + #endif /* CPU_COMMON_H */ diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index fb4c8d480ff..e01acb7c906 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -19,18 +19,10 @@ #ifndef CPU_DEFS_H #define CPU_DEFS_H -#ifndef NEED_CPU_H +#ifndef COMPILING_PER_TARGET #error cpu.h included from common code #endif -#include "qemu/host-utils.h" -#include "qemu/thread.h" -#ifndef CONFIG_USER_ONLY -#include "exec/hwaddr.h" -#endif -#include "exec/memattrs.h" -#include "hw/core/cpu.h" - #include "cpu-param.h" #ifndef TARGET_LONG_BITS @@ -42,180 +34,10 @@ #ifndef TARGET_VIRT_ADDR_SPACE_BITS # error TARGET_VIRT_ADDR_SPACE_BITS must be defined in cpu-param.h #endif -#ifndef TARGET_PAGE_BITS -# ifdef TARGET_PAGE_BITS_VARY -# ifndef TARGET_PAGE_BITS_MIN -# error TARGET_PAGE_BITS_MIN must be defined in cpu-param.h -# endif -# else -# error TARGET_PAGE_BITS must be defined in cpu-param.h -# endif +#if !defined(TARGET_PAGE_BITS) && !defined(TARGET_PAGE_BITS_VARY) +# error TARGET_PAGE_BITS must be defined in cpu-param.h #endif #include "exec/target_long.h" -/* - * Fix the number of mmu modes to 16, which is also the maximum - * supported by the softmmu tlb api. - */ -#define NB_MMU_MODES 16 - -#if defined(CONFIG_SOFTMMU) && defined(CONFIG_TCG) -#include "exec/tlb-common.h" - -/* use a fully associative victim tlb of 8 entries */ -#define CPU_VTLB_SIZE 8 - -#define CPU_TLB_DYN_MIN_BITS 6 -#define CPU_TLB_DYN_DEFAULT_BITS 8 - -# if HOST_LONG_BITS == 32 -/* Make sure we do not require a double-word shift for the TLB load */ -# define CPU_TLB_DYN_MAX_BITS (32 - TARGET_PAGE_BITS) -# else /* HOST_LONG_BITS == 64 */ -/* - * Assuming TARGET_PAGE_BITS==12, with 2**22 entries we can cover 2**(22+12) == - * 2**34 == 16G of address space. This is roughly what one would expect a - * TLB to cover in a modern (as of 2018) x86_64 CPU. For instance, Intel - * Skylake's Level-2 STLB has 16 1G entries. - * Also, make sure we do not size the TLB past the guest's address space. - */ -# ifdef TARGET_PAGE_BITS_VARY -# define CPU_TLB_DYN_MAX_BITS \ - MIN(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS) -# else -# define CPU_TLB_DYN_MAX_BITS \ - MIN_CONST(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS) -# endif -# endif - -#endif /* CONFIG_SOFTMMU && CONFIG_TCG */ - -#if defined(CONFIG_SOFTMMU) -/* - * The full TLB entry, which is not accessed by generated TCG code, - * so the layout is not as critical as that of CPUTLBEntry. This is - * also why we don't want to combine the two structs. - */ -typedef struct CPUTLBEntryFull { - /* - * @xlat_section contains: - * - in the lower TARGET_PAGE_BITS, a physical section number - * - with the lower TARGET_PAGE_BITS masked off, an offset which - * must be added to the virtual address to obtain: - * + the ram_addr_t of the target RAM (if the physical section - * number is PHYS_SECTION_NOTDIRTY or PHYS_SECTION_ROM) - * + the offset within the target MemoryRegion (otherwise) - */ - hwaddr xlat_section; - - /* - * @phys_addr contains the physical address in the address space - * given by cpu_asidx_from_attrs(cpu, @attrs). - */ - hwaddr phys_addr; - - /* @attrs contains the memory transaction attributes for the page. */ - MemTxAttrs attrs; - - /* @prot contains the complete protections for the page. */ - uint8_t prot; - - /* @lg_page_size contains the log2 of the page size. */ - uint8_t lg_page_size; - - /* - * Additional tlb flags 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. - */ -#ifdef TARGET_PAGE_ENTRY_EXTRA - TARGET_PAGE_ENTRY_EXTRA -#endif -} CPUTLBEntryFull; -#endif /* CONFIG_SOFTMMU */ - -#if defined(CONFIG_SOFTMMU) && defined(CONFIG_TCG) -/* - * 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 { - CPUTLBCommon c; - CPUTLBDesc d[NB_MMU_MODES]; - CPUTLBDescFast f[NB_MMU_MODES]; -} CPUTLB; - -#else - -typedef struct CPUTLB { } CPUTLB; - -#endif /* CONFIG_SOFTMMU && 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 diff --git a/include/exec/cpu-interrupt.h b/include/exec/cpu-interrupt.h new file mode 100644 index 00000000000..40715193ca3 --- /dev/null +++ b/include/exec/cpu-interrupt.h @@ -0,0 +1,70 @@ +/* + * Flags for use with cpu_interrupt() + * + * Copyright (c) 2003 Fabrice Bellard + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef CPU_INTERRUPT_H +#define CPU_INTERRUPT_H + +/* + * The numbers assigned here are non-sequential in order to preserve binary + * compatibility with the vmstate dump. Bit 0 (0x0001) was previously used + * for CPU_INTERRUPT_EXIT, and is cleared when loading the vmstate dump. + */ + +/* + * External hardware interrupt pending. + * This is typically used for interrupts from devices. + */ +#define CPU_INTERRUPT_HARD 0x0002 + +/* + * Exit the current TB. This is typically used when some system-level device + * makes some change to the memory mapping. E.g. the a20 line change. + */ +#define CPU_INTERRUPT_EXITTB 0x0004 + +/* Halt the CPU. */ +#define CPU_INTERRUPT_HALT 0x0020 + +/* Debug event pending. */ +#define CPU_INTERRUPT_DEBUG 0x0080 + +/* Reset signal. */ +#define CPU_INTERRUPT_RESET 0x0400 + +/* + * Several target-specific external hardware interrupts. Each target/cpu.h + * should define proper names based on these defines. + */ +#define CPU_INTERRUPT_TGT_EXT_0 0x0008 +#define CPU_INTERRUPT_TGT_EXT_1 0x0010 +#define CPU_INTERRUPT_TGT_EXT_2 0x0040 +#define CPU_INTERRUPT_TGT_EXT_3 0x0200 +#define CPU_INTERRUPT_TGT_EXT_4 0x1000 + +/* + * Several target-specific internal interrupts. These differ from the + * preceding target-specific interrupts in that they are intended to + * originate from within the cpu itself, typically in response to some + * instruction being executed. These, therefore, are not masked while + * single-stepping within the debugger. + */ +#define CPU_INTERRUPT_TGT_INT_0 0x0100 +#define CPU_INTERRUPT_TGT_INT_1 0x0800 +#define CPU_INTERRUPT_TGT_INT_2 0x2000 + +/* First unused bit: 0x4000. */ + +/* The set of all bits that should be masked when single-stepping. */ +#define CPU_INTERRUPT_SSTEP_MASK \ + (CPU_INTERRUPT_HARD \ + | CPU_INTERRUPT_TGT_EXT_0 \ + | CPU_INTERRUPT_TGT_EXT_1 \ + | CPU_INTERRUPT_TGT_EXT_2 \ + | CPU_INTERRUPT_TGT_EXT_3 \ + | CPU_INTERRUPT_TGT_EXT_4) + +#endif /* CPU_INTERRUPT_H */ diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h deleted file mode 100644 index da10ba14339..00000000000 --- a/include/exec/cpu_ldst.h +++ /dev/null @@ -1,475 +0,0 @@ -/* - * Software MMU support - * - * 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 . - * - */ - -/* - * Generate inline load/store functions for all MMU modes (typically - * at least _user and _kernel) as well as _data versions, for all data - * sizes. - * - * Used by target op helpers. - * - * The syntax for the accessors is: - * - * load: cpu_ld{sign}{size}{end}_{mmusuffix}(env, ptr) - * cpu_ld{sign}{size}{end}_{mmusuffix}_ra(env, ptr, retaddr) - * cpu_ld{sign}{size}{end}_mmuidx_ra(env, ptr, mmu_idx, retaddr) - * cpu_ld{sign}{size}{end}_mmu(env, ptr, oi, retaddr) - * - * store: cpu_st{size}{end}_{mmusuffix}(env, ptr, val) - * cpu_st{size}{end}_{mmusuffix}_ra(env, ptr, val, retaddr) - * cpu_st{size}{end}_mmuidx_ra(env, ptr, val, mmu_idx, retaddr) - * cpu_st{size}{end}_mmu(env, ptr, val, oi, retaddr) - * - * sign is: - * (empty): for 32 and 64 bit sizes - * u : unsigned - * s : signed - * - * size is: - * b: 8 bits - * w: 16 bits - * l: 32 bits - * q: 64 bits - * - * end is: - * (empty): for target native endian, or for 8 bit access - * _be: for forced big endian - * _le: for forced little endian - * - * mmusuffix is one of the generic suffixes "data" or "code", or "mmuidx". - * The "mmuidx" suffix carries an extra mmu_idx argument that specifies - * the index to use; the "data" and "code" suffixes take the index from - * cpu_mmu_index(). - * - * The "mmu" suffix carries the full MemOpIdx, with both mmu_idx and the - * MemOp including alignment requirements. The alignment will be enforced. - */ -#ifndef CPU_LDST_H -#define CPU_LDST_H - -#include "exec/memopidx.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 - -#ifndef TARGET_TAGGED_ADDRESSES -static inline abi_ptr cpu_untagged_addr(CPUState *cs, abi_ptr x) -{ - return x; -} -#endif - -/* All direct uses of g2h and h2g need to go away for usermode softmmu. */ -static inline void *g2h_untagged(abi_ptr x) -{ - return (void *)((uintptr_t)(x) + guest_base); -} - -static inline void *g2h(CPUState *cs, abi_ptr x) -{ - return g2h_untagged(cpu_untagged_addr(cs, x)); -} - -static inline bool guest_addr_valid_untagged(abi_ulong x) -{ - return x <= GUEST_ADDR_MAX; -} - -static inline bool guest_range_valid_untagged(abi_ulong start, abi_ulong len) -{ - return len - 1 <= GUEST_ADDR_MAX && start <= GUEST_ADDR_MAX - len + 1; -} - -#define h2g_valid(x) \ - (HOST_LONG_BITS <= TARGET_VIRT_ADDR_SPACE_BITS || \ - (uintptr_t)(x) - guest_base <= GUEST_ADDR_MAX) - -#define h2g_nocheck(x) ({ \ - uintptr_t __ret = (uintptr_t)(x) - guest_base; \ - (abi_ptr)__ret; \ -}) - -#define h2g(x) ({ \ - /* Check if given address fits target address space */ \ - assert(h2g_valid(x)); \ - h2g_nocheck(x); \ -}) -#else -typedef target_ulong abi_ptr; -#define TARGET_ABI_FMT_ptr TARGET_FMT_lx -#endif - -uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr); -int cpu_ldsb_data(CPUArchState *env, abi_ptr ptr); -uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr ptr); -int cpu_ldsw_be_data(CPUArchState *env, abi_ptr ptr); -uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr ptr); -uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr ptr); -uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr ptr); -int cpu_ldsw_le_data(CPUArchState *env, abi_ptr ptr); -uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr ptr); -uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr ptr); - -uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -uint32_t cpu_lduw_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -int cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -uint32_t cpu_ldl_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -uint64_t cpu_ldq_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -uint32_t cpu_lduw_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -int cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -uint32_t cpu_ldl_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -uint64_t cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); - -void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val); -void cpu_stw_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val); -void cpu_stl_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val); -void cpu_stq_be_data(CPUArchState *env, abi_ptr ptr, uint64_t val); -void cpu_stw_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val); -void cpu_stl_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val); -void cpu_stq_le_data(CPUArchState *env, abi_ptr ptr, uint64_t val); - -void cpu_stb_data_ra(CPUArchState *env, abi_ptr ptr, - uint32_t val, uintptr_t ra); -void cpu_stw_be_data_ra(CPUArchState *env, abi_ptr ptr, - uint32_t val, uintptr_t ra); -void cpu_stl_be_data_ra(CPUArchState *env, abi_ptr ptr, - uint32_t val, uintptr_t ra); -void cpu_stq_be_data_ra(CPUArchState *env, abi_ptr ptr, - uint64_t val, uintptr_t ra); -void cpu_stw_le_data_ra(CPUArchState *env, abi_ptr ptr, - uint32_t val, uintptr_t ra); -void cpu_stl_le_data_ra(CPUArchState *env, abi_ptr ptr, - uint32_t val, uintptr_t ra); -void cpu_stq_le_data_ra(CPUArchState *env, abi_ptr ptr, - uint64_t val, uintptr_t ra); - -uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -uint32_t cpu_lduw_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -int cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -uint32_t cpu_ldl_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -uint64_t cpu_ldq_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -uint32_t cpu_lduw_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -int cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -uint32_t cpu_ldl_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -uint64_t cpu_ldq_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); - -void cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, - int mmu_idx, uintptr_t ra); -void cpu_stw_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, - int mmu_idx, uintptr_t ra); -void cpu_stl_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, - int mmu_idx, uintptr_t ra); -void cpu_stq_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint64_t val, - int mmu_idx, uintptr_t ra); -void cpu_stw_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, - int mmu_idx, uintptr_t ra); -void cpu_stl_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, - int mmu_idx, uintptr_t ra); -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_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_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, abi_ptr addr, - uint32_t cmpv, uint32_t newv, - MemOpIdx oi, uintptr_t retaddr); -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, abi_ptr addr, - uint32_t cmpv, uint32_t newv, - MemOpIdx oi, uintptr_t retaddr); -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, abi_ptr addr, - uint32_t cmpv, uint32_t newv, - MemOpIdx oi, uintptr_t retaddr); -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, 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, abi_ptr addr, TYPE val, \ - MemOpIdx oi, uintptr_t retaddr); - -#ifdef CONFIG_ATOMIC64 -#define GEN_ATOMIC_HELPER_ALL(NAME) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, b) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, w_le) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, w_be) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, l_le) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, l_be) \ - GEN_ATOMIC_HELPER(NAME, uint64_t, q_le) \ - GEN_ATOMIC_HELPER(NAME, uint64_t, q_be) -#else -#define GEN_ATOMIC_HELPER_ALL(NAME) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, b) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, w_le) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, w_be) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, l_le) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, l_be) -#endif - -GEN_ATOMIC_HELPER_ALL(fetch_add) -GEN_ATOMIC_HELPER_ALL(fetch_sub) -GEN_ATOMIC_HELPER_ALL(fetch_and) -GEN_ATOMIC_HELPER_ALL(fetch_or) -GEN_ATOMIC_HELPER_ALL(fetch_xor) -GEN_ATOMIC_HELPER_ALL(fetch_smin) -GEN_ATOMIC_HELPER_ALL(fetch_umin) -GEN_ATOMIC_HELPER_ALL(fetch_smax) -GEN_ATOMIC_HELPER_ALL(fetch_umax) - -GEN_ATOMIC_HELPER_ALL(add_fetch) -GEN_ATOMIC_HELPER_ALL(sub_fetch) -GEN_ATOMIC_HELPER_ALL(and_fetch) -GEN_ATOMIC_HELPER_ALL(or_fetch) -GEN_ATOMIC_HELPER_ALL(xor_fetch) -GEN_ATOMIC_HELPER_ALL(smin_fetch) -GEN_ATOMIC_HELPER_ALL(umin_fetch) -GEN_ATOMIC_HELPER_ALL(smax_fetch) -GEN_ATOMIC_HELPER_ALL(umax_fetch) - -GEN_ATOMIC_HELPER_ALL(xchg) - -#undef GEN_ATOMIC_HELPER_ALL -#undef GEN_ATOMIC_HELPER - -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, abi_ptr addr, - Int128 cmpv, Int128 newv, - 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 - -#include "tcg/oversized-guest.h" - -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(CPUArchState *env, uintptr_t mmu_idx, - vaddr 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, - vaddr 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 -# define cpu_ldl_data cpu_ldl_be_data -# define cpu_ldq_data cpu_ldq_be_data -# define cpu_lduw_data_ra cpu_lduw_be_data_ra -# define cpu_ldsw_data_ra cpu_ldsw_be_data_ra -# define cpu_ldl_data_ra cpu_ldl_be_data_ra -# define cpu_ldq_data_ra cpu_ldq_be_data_ra -# define cpu_lduw_mmuidx_ra cpu_lduw_be_mmuidx_ra -# 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_stw_data cpu_stw_be_data -# define cpu_stl_data cpu_stl_be_data -# define cpu_stq_data cpu_stq_be_data -# define cpu_stw_data_ra cpu_stw_be_data_ra -# define cpu_stl_data_ra cpu_stl_be_data_ra -# define cpu_stq_data_ra cpu_stq_be_data_ra -# 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 -#else -# define cpu_lduw_data cpu_lduw_le_data -# define cpu_ldsw_data cpu_ldsw_le_data -# define cpu_ldl_data cpu_ldl_le_data -# define cpu_ldq_data cpu_ldq_le_data -# define cpu_lduw_data_ra cpu_lduw_le_data_ra -# define cpu_ldsw_data_ra cpu_ldsw_le_data_ra -# define cpu_ldl_data_ra cpu_ldl_le_data_ra -# define cpu_ldq_data_ra cpu_ldq_le_data_ra -# define cpu_lduw_mmuidx_ra cpu_lduw_le_mmuidx_ra -# 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_stw_data cpu_stw_le_data -# define cpu_stl_data cpu_stl_le_data -# define cpu_stq_data cpu_stq_le_data -# define cpu_stw_data_ra cpu_stw_le_data_ra -# define cpu_stl_data_ra cpu_stl_le_data_ra -# define cpu_stq_data_ra cpu_stq_le_data_ra -# 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 -#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); - -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 - * @addr: guest virtual address to look up - * @access_type: 0 for read, 1 for write, 2 for execute - * @mmu_idx: MMU index to use for lookup - * - * Look up the specified guest virtual index in the TCG softmmu TLB. - * If we can translate a host virtual address suitable for direct RAM - * access, without causing a guest exception, then return it. - * Otherwise (TLB entry is for an I/O access, guest software - * TLB fill required, etc) return NULL. - */ -#ifdef CONFIG_USER_ONLY -static inline void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr, - MMUAccessType access_type, int mmu_idx) -{ - return g2h(env_cpu(env), addr); -} -#else -void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr, - MMUAccessType access_type, int mmu_idx); -#endif - -#endif /* CPU_LDST_H */ diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index 19b16e58f84..03ed7e21659 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -21,11 +21,266 @@ #define CPUTLB_H #include "exec/cpu-common.h" +#include "exec/hwaddr.h" +#include "exec/memattrs.h" +#include "exec/vaddr.h" -#if !defined(CONFIG_USER_ONLY) -/* cputlb.c */ +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) 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 + +#ifndef CONFIG_USER_ONLY +void tlb_reset_dirty(CPUState *cpu, uintptr_t start, uintptr_t length); +void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length); #endif + +/** + * tlb_set_page_full: + * @cpu: CPU context + * @mmu_idx: mmu index of the tlb to modify + * @addr: virtual address of the entry to add + * @full: the details of the tlb entry + * + * Add an entry to @cpu tlb index @mmu_idx. All of the fields of + * @full must be filled, except for xlat_section, and constitute + * the complete description of the translated page. + * + * This is generally called by the target tlb_fill function after + * having performed a successful page table walk to find the physical + * address and attributes for the translation. + * + * At most one entry for a given virtual address is permitted. Only a + * single TARGET_PAGE_SIZE region is mapped; @full->lg_page_size is only + * used by tlb_flush_page. + */ +void tlb_set_page_full(CPUState *cpu, int mmu_idx, vaddr addr, + CPUTLBEntryFull *full); + +/** + * tlb_set_page_with_attrs: + * @cpu: CPU to add this TLB entry for + * @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) + * @mmu_idx: MMU index to insert TLB entry for + * @size: size of the page in bytes + * + * Add an entry to this CPU's TLB (a mapping from virtual address + * @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 + * the physical address and attributes for the virtual address + * which provoked the TLB miss. + * + * At most one entry for a given virtual address is permitted. Only a + * single TARGET_PAGE_SIZE region is mapped; the supplied @size is only + * used by tlb_flush_page. + */ +void tlb_set_page_with_attrs(CPUState *cpu, vaddr addr, + hwaddr paddr, MemTxAttrs attrs, + 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, vaddr addr, + hwaddr paddr, int prot, + int mmu_idx, vaddr size); + +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) +/** + * tlb_flush_page: + * @cpu: CPU whose TLB should be flushed + * @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(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 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, vaddr addr); + +/** + * tlb_flush: + * @cpu: CPU whose TLB should be flushed + * + * Flush the entire TLB for the specified CPU. Most CPU architectures + * allow the implementation to drop entries from the TLB at any time + * so this is generally safe. If more selective flushing is required + * use one of the other functions for efficiency. + */ +void tlb_flush(CPUState *cpu); + +/** + * tlb_flush_all_cpus_synced: + * @cpu: src CPU of the flush + * + * 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); + +/** + * tlb_flush_page_by_mmuidx: + * @cpu: CPU whose TLB should be flushed + * @addr: virtual address of page to be flushed + * @idxmap: bitmap of MMU indexes to flush + * + * Flush one page from the TLB of the specified CPU, for the specified + * MMU indexes. + */ +void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr 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. + * + * 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, vaddr addr, + uint16_t idxmap); + +/** + * tlb_flush_by_mmuidx: + * @cpu: CPU whose TLB should be flushed + * @wait: If true ensure synchronisation by exiting the cpu_loop + * @idxmap: bitmap of MMU indexes to flush + * + * Flush all entries from the TLB of the specified CPU, for the specified + * MMU indexes. + */ +void tlb_flush_by_mmuidx(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 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); + +/** + * tlb_flush_page_bits_by_mmuidx + * @cpu: CPU whose TLB should be flushed + * @addr: virtual address of page to be flushed + * @idxmap: bitmap of mmu indexes to flush + * @bits: number of significant bits in address + * + * Similar to tlb_flush_page_mask, but with a bitmap of indexes. + */ +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_synced(CPUState *cpu, vaddr addr, + uint16_t idxmap, + unsigned bits); + +/** + * tlb_flush_range_by_mmuidx + * @cpu: CPU whose TLB should be flushed + * @addr: virtual address of the start of the range to be flushed + * @len: length of range to be flushed + * @idxmap: bitmap of mmu indexes to flush + * @bits: number of significant bits in address + * + * 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, vaddr addr, + vaddr len, uint16_t idxmap, + unsigned bits); + +/* Similarly, with broadcast and syncing. */ +void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, + vaddr addr, + vaddr len, + uint16_t idxmap, + unsigned bits); +#else +static inline void tlb_flush_page(CPUState *cpu, vaddr 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_synced(CPUState *src_cpu) +{ +} +static inline void tlb_flush_page_by_mmuidx(CPUState *cpu, + 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_synced(CPUState *cpu, + vaddr addr, + 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, + vaddr addr, + uint16_t idxmap, + unsigned bits) +{ +} +static inline void +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, vaddr addr, + vaddr len, uint16_t idxmap, + unsigned bits) +{ +} +static inline void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, + vaddr addr, + vaddr len, + uint16_t idxmap, + unsigned bits) +{ +} +#endif /* CONFIG_TCG && !CONFIG_USER_ONLY */ +#endif /* CPUTLB_H */ diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h deleted file mode 100644 index b2f5cd4c2a3..00000000000 --- a/include/exec/exec-all.h +++ /dev/null @@ -1,708 +0,0 @@ -/* - * internal execution defines for qemu - * - * 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_ALL_H -#define EXEC_ALL_H - -#include "cpu.h" -#if defined(CONFIG_USER_ONLY) -#include "exec/cpu_ldst.h" -#endif -#include "exec/translation-block.h" -#include "qemu/clang-tsa.h" - -/** - * 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); - -/** - * cpu_loop_exit_requested: - * @cpu: The CPU state to be tested - * - * Indicate if somebody asked for a return of the CPU to the main loop - * (e.g., via cpu_exit() or cpu_interrupt()). - * - * This is helpful for architectures that support interruptible - * instructions. After writing back all state to registers/memory, this - * call can be used to check if it makes sense to return to the main loop - * or to continue executing the interruptible instruction. - */ -static inline bool cpu_loop_exit_requested(CPUState *cpu) -{ - return (int32_t)qatomic_read(&cpu_neg(cpu)->icount_decr.u32) < 0; -} - -#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG) -/* cputlb.c */ -/** - * tlb_init - initialize a CPU's TLB - * @cpu: CPU whose TLB should be initialized - */ -void tlb_init(CPUState *cpu); -/** - * tlb_destroy - destroy a CPU's TLB - * @cpu: CPU whose TLB should be destroyed - */ -void tlb_destroy(CPUState *cpu); -/** - * tlb_flush_page: - * @cpu: CPU whose TLB should be flushed - * @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(CPUState *cpu, vaddr 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, 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. - */ -void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr); -/** - * tlb_flush: - * @cpu: CPU whose TLB should be flushed - * - * Flush the entire TLB for the specified CPU. Most CPU architectures - * allow the implementation to drop entries from the TLB at any time - * so this is generally safe. If more selective flushing is required - * 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. - */ -void tlb_flush_all_cpus_synced(CPUState *src_cpu); -/** - * tlb_flush_page_by_mmuidx: - * @cpu: CPU whose TLB should be flushed - * @addr: virtual address of page to be flushed - * @idxmap: bitmap of MMU indexes to flush - * - * Flush one page from the TLB of the specified CPU, for the specified - * MMU indexes. - */ -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, vaddr 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. - */ -void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, - uint16_t idxmap); -/** - * tlb_flush_by_mmuidx: - * @cpu: CPU whose TLB should be flushed - * @wait: If true ensure synchronisation by exiting the cpu_loop - * @idxmap: bitmap of MMU indexes to flush - * - * Flush all entries from the TLB of the specified CPU, for the specified - * 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. - */ -void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap); - -/** - * tlb_flush_page_bits_by_mmuidx - * @cpu: CPU whose TLB should be flushed - * @addr: virtual address of page to be flushed - * @idxmap: bitmap of mmu indexes to flush - * @bits: number of significant bits in address - * - * Similar to tlb_flush_page_mask, but with a bitmap of indexes. - */ -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, vaddr addr, - uint16_t idxmap, unsigned bits); -void tlb_flush_page_bits_by_mmuidx_all_cpus_synced - (CPUState *cpu, vaddr addr, uint16_t idxmap, unsigned bits); - -/** - * tlb_flush_range_by_mmuidx - * @cpu: CPU whose TLB should be flushed - * @addr: virtual address of the start of the range to be flushed - * @len: length of range to be flushed - * @idxmap: bitmap of mmu indexes to flush - * @bits: number of significant bits in address - * - * 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, vaddr addr, - vaddr len, uint16_t idxmap, - unsigned bits); - -/* Similarly, with broadcast and syncing. */ -void tlb_flush_range_by_mmuidx_all_cpus(CPUState *cpu, vaddr addr, - vaddr len, uint16_t idxmap, - unsigned bits); -void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, - vaddr addr, - vaddr len, - uint16_t idxmap, - unsigned bits); - -/** - * tlb_set_page_full: - * @cpu: CPU context - * @mmu_idx: mmu index of the tlb to modify - * @addr: virtual address of the entry to add - * @full: the details of the tlb entry - * - * Add an entry to @cpu tlb index @mmu_idx. All of the fields of - * @full must be filled, except for xlat_section, and constitute - * the complete description of the translated page. - * - * This is generally called by the target tlb_fill function after - * having performed a successful page table walk to find the physical - * address and attributes for the translation. - * - * At most one entry for a given virtual address is permitted. Only a - * single TARGET_PAGE_SIZE region is mapped; @full->lg_page_size is only - * used by tlb_flush_page. - */ -void tlb_set_page_full(CPUState *cpu, int mmu_idx, vaddr addr, - CPUTLBEntryFull *full); - -/** - * tlb_set_page_with_attrs: - * @cpu: CPU to add this TLB entry for - * @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) - * @mmu_idx: MMU index to insert TLB entry for - * @size: size of the page in bytes - * - * Add an entry to this CPU's TLB (a mapping from virtual address - * @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 - * the physical address and attributes for the virtual address - * which provoked the TLB miss. - * - * At most one entry for a given virtual address is permitted. Only a - * single TARGET_PAGE_SIZE region is mapped; the supplied @size is only - * used by tlb_flush_page. - */ -void tlb_set_page_with_attrs(CPUState *cpu, vaddr addr, - hwaddr paddr, MemTxAttrs attrs, - 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, vaddr addr, - hwaddr paddr, int prot, - int mmu_idx, vaddr size); -#else -static inline void tlb_init(CPUState *cpu) -{ -} -static inline void tlb_destroy(CPUState *cpu) -{ -} -static inline void tlb_flush_page(CPUState *cpu, vaddr addr) -{ -} -static inline void tlb_flush_page_all_cpus(CPUState *src, vaddr 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, - 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, - vaddr addr, - uint16_t idxmap) -{ -} -static inline void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, - 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, - vaddr addr, - uint16_t idxmap, - unsigned bits) -{ -} -static inline void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *cpu, - vaddr addr, - uint16_t idxmap, - unsigned bits) -{ -} -static inline void -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, vaddr addr, - vaddr len, uint16_t idxmap, - unsigned bits) -{ -} -static inline void tlb_flush_range_by_mmuidx_all_cpus(CPUState *cpu, - vaddr addr, - vaddr len, - uint16_t idxmap, - unsigned bits) -{ -} -static inline void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, - vaddr addr, - vaddr len, - uint16_t idxmap, - unsigned bits) -{ -} -#endif -/** - * probe_access: - * @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 - * @retaddr: return address for unwinding - * - * Look up the guest virtual address @addr. Raise an exception if the - * page does not satisfy @access_type. Raise an exception if the - * access (@addr, @size) hits a watchpoint. For writes, mark a clean - * page as dirty. - * - * 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, vaddr addr, int size, - MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); - -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, vaddr addr, int size, - int mmu_idx, uintptr_t retaddr) -{ - return probe_access(env, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr); -} - -/** - * 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 - * @phost: return value for host address - * @retaddr: return address for unwinding - * - * Similar to probe_access, loosely returning the TLB_FLAGS_MASK for - * the page, and storing the host address for RAM in @phost. - * - * If @nonfault is set, do not raise an exception but return TLB_INVALID_MASK. - * Do not handle watchpoints, but include TLB_WATCHPOINT in the returned flags. - * 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, 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. - * - * The CPUTLBEntryFull structure returned via @pfull is transient - * and must be consumed or copied immediately, before any further - * access or changes to TLB @mmu_idx. - */ -int probe_access_full(CPUArchState *env, vaddr addr, int size, - MMUAccessType access_type, int mmu_idx, - bool nonfault, void **phost, - CPUTLBEntryFull **pfull, uintptr_t retaddr); - -/** - * probe_access_mmu() - Like probe_access_full except cannot fault and - * doesn't trigger instrumentation. - * - * @env: CPUArchState - * @vaddr: virtual address to probe - * @size: size of the probe - * @access_type: read, write or execute permission - * @mmu_idx: softmmu index - * @phost: ptr to return value host address or NULL - * @pfull: ptr to return value CPUTLBEntryFull structure or NULL - * - * The CPUTLBEntryFull structure returned via @pfull is transient - * and must be consumed or copied immediately, before any further - * access or changes to TLB @mmu_idx. - * - * Returns: TLB flags as per probe_access_flags() - */ -int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, - MMUAccessType access_type, int mmu_idx, - void **phost, CPUTLBEntryFull **pfull); - -#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); -} - -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(hwaddr addr); -#else -void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs); -#endif -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 last); -void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr); - -/* GETPC is the true target of the return instruction that we'll execute. */ -#if defined(CONFIG_TCG_INTERPRETER) -extern __thread uintptr_t tci_tb_ptr; -# define GETPC() tci_tb_ptr -#else -# define GETPC() \ - ((uintptr_t)__builtin_extract_return_addr(__builtin_return_address(0))) -#endif - -/* The true return address will often point to a host insn that is part of - the next translated guest insn. Adjust the address backward to point to - the middle of the call insn. Subtracting one would do the job except for - several compressed mode architectures (arm, mips) which set the low bit - to indicate the compressed mode; subtracting two works around that. It - is also the case that there are no host isas that contain a call insn - smaller than 4 bytes, so we don't worry about special-casing this. */ -#define GETPC_ADJ 2 - -#if !defined(CONFIG_USER_ONLY) - -/** - * iotlb_to_section: - * @cpu: CPU performing the access - * @index: TCG CPU IOTLB entry - * - * Given a TCG CPU IOTLB entry, return the MemoryRegionSection that - * it refers to. @index will have been initially created and returned - * by memory_region_section_get_iotlb(). - */ -struct MemoryRegionSection *iotlb_to_section(CPUState *cpu, - hwaddr index, MemTxAttrs attrs); -#endif - -/** - * get_page_addr_code_hostp() - * @env: CPUArchState - * @addr: guest virtual address of guest code - * - * See get_page_addr_code() (full-system version) for documentation on the - * return value. - * - * Sets *@hostp (when @hostp is non-NULL) as follows. - * If the return value is -1, sets *@hostp to NULL. Otherwise, sets *@hostp - * to the host address where @addr's content is kept. - * - * Note: this function can trigger an exception. - */ -tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, - void **hostp); - -/** - * get_page_addr_code() - * @env: CPUArchState - * @addr: guest virtual address of guest code - * - * If we cannot translate and execute from the entire RAM page, or if - * the region is not backed by RAM, returns -1. Otherwise, returns the - * ram_addr_t corresponding to the guest code at @addr. - * - * Note: this function can trigger an exception. - */ -static inline tb_page_addr_t get_page_addr_code(CPUArchState *env, - vaddr addr) -{ - return get_page_addr_code_hostp(env, addr, NULL); -} - -#if defined(CONFIG_USER_ONLY) -void TSA_NO_TSA mmap_lock(void); -void TSA_NO_TSA mmap_unlock(void); -bool have_mmap_lock(void); - -static inline void mmap_unlock_guard(void *unused) -{ - 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. - * @is_write: host memory operation was write, or read-modify-write. - * - * Alter @pc as required for unwinding. Return the type of the - * guest memory access -- host reads may be for guest execution. - */ -MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write); - -/** - * handle_sigsegv_accerr_write: - * @cpu: the cpu context - * @old_set: the sigset_t from the signal ucontext_t - * @host_pc: the host pc, adjusted for the signal - * @host_addr: the host address of the fault - * - * Return true if the write fault has been handled, and should be re-tried. - */ -bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, - uintptr_t host_pc, abi_ptr guest_addr); - -/** - * cpu_loop_exit_sigsegv: - * @cpu: the cpu context - * @addr: the guest address of the fault - * @access_type: access was read/write/execute - * @maperr: true for invalid page, false for permission fault - * @ra: host pc for unwinding - * - * Use the TCGCPUOps hook to record cpu state, do guest operating system - * specific things to raise SIGSEGV, and jump to the main cpu loop. - */ -G_NORETURN void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, - MMUAccessType access_type, - bool maperr, uintptr_t ra); - -/** - * cpu_loop_exit_sigbus: - * @cpu: the cpu context - * @addr: the guest address of the alignment fault - * @access_type: access was read/write/execute - * @ra: host pc for unwinding - * - * Use the TCGCPUOps hook to record cpu state, do guest operating system - * specific things to raise SIGBUS, and jump to the main cpu loop. - */ -G_NORETURN void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, - MMUAccessType access_type, - uintptr_t ra); - -#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, vaddr addr); - -MemoryRegionSection * -address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, - hwaddr *xlat, hwaddr *plen, - MemTxAttrs attrs, int *prot); -hwaddr memory_region_section_get_iotlb(CPUState *cpu, - MemoryRegionSection *section); -#endif - -#endif diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 16a139043f6..0675b0b646d 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -1,19 +1,31 @@ #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; /* 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 @@ -26,29 +38,113 @@ typedef int (*gdb_set_reg_cb)(CPUArchState *env, uint8_t *buf, int reg); */ 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); + +/** + * gdb_unregister_coprocessor_all() - unregisters supplemental set of registers + * @cpu - the CPU associated with registers + */ +void gdb_unregister_coprocessor_all(CPUState *cpu); /** * gdbserver_start: start the gdb server * @port_or_device: connection spec for gdb + * @errp: error handle * * For CONFIG_USER this is either a tcp port or a path to a fifo. For * system emulation you can use a full chardev spec for your gdbserver * port. + * + * The error handle should be either &error_fatal (for start-up) or + * &error_warn (for QMP/HMP initiated sessions). + * + * Returns true when server successfully started. */ -int gdbserver_start(const char *port_or_device); +bool gdbserver_start(const char *port_or_device, Error **errp); -void gdb_set_stop_cpu(CPUState *cpu); +/** + * 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_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_has_xml() - report of gdb supports modern target descriptions + * 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. * - * This will report true if the gdb negotiated qXfer:features:read - * target descriptions. + * Return: The static feature. */ -bool gdb_has_xml(void); +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); -/* in gdbstub-xml.c, generated by scripts/feature_to_c.sh */ -extern const char *const xml_builtin[][2]; +/* in gdbstub-xml.c, generated by scripts/feature_to_c.py */ +extern const GDBFeature gdb_static_features[]; -#endif +#endif /* GDBSTUB_H */ diff --git a/include/exec/helper-gen-common.h b/include/exec/helper-gen-common.h index 5d6d78a625f..834590dc4e5 100644 --- a/include/exec/helper-gen-common.h +++ b/include/exec/helper-gen-common.h @@ -11,8 +11,4 @@ #include "exec/helper-gen.h.inc" #undef HELPER_H -#define HELPER_H "accel/tcg/plugin-helpers.h" -#include "exec/helper-gen.h.inc" -#undef HELPER_H - #endif /* HELPER_GEN_COMMON_H */ diff --git a/include/exec/helper-gen.h.inc b/include/exec/helper-gen.h.inc index c0096415177..dabe138e205 100644 --- a/include/exec/helper-gen.h.inc +++ b/include/exec/helper-gen.h.inc @@ -8,13 +8,14 @@ #include "tcg/tcg.h" #include "tcg/helper-info.h" -#include "exec/helper-head.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), dh_retvar(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) \ @@ -22,7 +23,8 @@ extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1)) \ { \ - tcg_gen_call1(&glue(helper_info_, name), dh_retvar(ret), \ + tcg_gen_call1(glue(helper_info_,name).func, \ + &glue(helper_info_,name), dh_retvar(ret), \ dh_arg(t1, 1)); \ } @@ -31,7 +33,8 @@ extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2)) \ { \ - tcg_gen_call2(&glue(helper_info_, name), dh_retvar(ret), \ + tcg_gen_call2(glue(helper_info_,name).func, \ + &glue(helper_info_,name), dh_retvar(ret), \ dh_arg(t1, 1), dh_arg(t2, 2)); \ } @@ -40,7 +43,8 @@ extern TCGHelperInfo glue(helper_info_, name); \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3)) \ { \ - tcg_gen_call3(&glue(helper_info_, name), dh_retvar(ret), \ + 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)); \ } @@ -50,7 +54,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), \ dh_arg_decl(t3, 3), dh_arg_decl(t4, 4)) \ { \ - tcg_gen_call4(&glue(helper_info_, name), dh_retvar(ret), \ + 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)); \ } @@ -61,7 +66,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ dh_arg_decl(t4, 4), dh_arg_decl(t5, 5)) \ { \ - tcg_gen_call5(&glue(helper_info_, name), dh_retvar(ret), \ + 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)); \ } @@ -72,7 +78,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ dh_arg_decl(t4, 4), dh_arg_decl(t5, 5), dh_arg_decl(t6, 6)) \ { \ - tcg_gen_call6(&glue(helper_info_, name), dh_retvar(ret), \ + 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)); \ } @@ -84,7 +91,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t4, 4), dh_arg_decl(t5, 5), dh_arg_decl(t6, 6), \ dh_arg_decl(t7, 7)) \ { \ - tcg_gen_call7(&glue(helper_info_, name), dh_retvar(ret), \ + 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)); \ diff --git a/include/exec/helper-head.h b/include/exec/helper-head.h deleted file mode 100644 index 28ceab0a461..00000000000 --- a/include/exec/helper-head.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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 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 -#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_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-head.h.inc b/include/exec/helper-head.h.inc new file mode 100644 index 00000000000..5b248fd7138 --- /dev/null +++ b/include/exec/helper-head.h.inc @@ -0,0 +1,149 @@ +/* + * 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_fpst 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_fpst float_status * +#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 */ + +#if __SIZEOF_POINTER__ == 4 +# define dh_alias_vaddr i32 +# define dh_typecode_vaddr dh_typecode_i32 +#elif __SIZEOF_POINTER__ == 8 +# define dh_alias_vaddr i64 +# define dh_typecode_vaddr dh_typecode_i64 +#else +# error "sizeof pointer is different from {4,8}" +#endif /* __SIZEOF_POINTER__ */ +# define dh_ctype_vaddr uintptr_t + +/* 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_fpst 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 index 530d2e6d356..c551736d49f 100644 --- a/include/exec/helper-info.c.inc +++ b/include/exec/helper-info.c.inc @@ -7,7 +7,7 @@ #include "tcg/tcg.h" #include "tcg/helper-info.h" -#include "exec/helper-head.h" +#include "exec/helper-head.h.inc" /* * Need one more level of indirection before stringification diff --git a/include/exec/helper-proto-common.h b/include/exec/helper-proto-common.h index 8b67170a22c..76e6c25becb 100644 --- a/include/exec/helper-proto-common.h +++ b/include/exec/helper-proto-common.h @@ -13,8 +13,6 @@ #include "exec/helper-proto.h.inc" #undef HELPER_H -#define HELPER_H "accel/tcg/plugin-helpers.h" -#include "exec/helper-proto.h.inc" -#undef HELPER_H +#include "accel/tcg/getpc.h" #endif /* HELPER_PROTO_COMMON_H */ diff --git a/include/exec/helper-proto.h.inc b/include/exec/helper-proto.h.inc index c3aa666929d..f8e57e43ce7 100644 --- a/include/exec/helper-proto.h.inc +++ b/include/exec/helper-proto.h.inc @@ -5,7 +5,7 @@ * Define HELPER_H for the header file to be expanded. */ -#include "exec/helper-head.h" +#include "exec/helper-head.h.inc" /* * Work around an issue with --enable-lto, in which GCC's ipa-split pass diff --git a/include/exec/icount.h b/include/exec/icount.h new file mode 100644 index 00000000000..7a26b40084d --- /dev/null +++ b/include/exec/icount.h @@ -0,0 +1,76 @@ +/* + * icount - Instruction Counter API + * CPU timers state API + * + * Copyright 2020 SUSE LLC + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef EXEC_ICOUNT_H +#define EXEC_ICOUNT_H + +/** + * ICountMode: icount enablement state: + * + * @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 + */ +typedef enum { + ICOUNT_DISABLED = 0, + ICOUNT_PRECISE, + ICOUNT_ADAPTATIVE, +} ICountMode; + +#ifdef CONFIG_TCG +extern ICountMode use_icount; +#define icount_enabled() (use_icount) +#else +#define icount_enabled() ICOUNT_DISABLED +#endif + +/* Protect the CONFIG_USER_ONLY test vs poisoning. */ +#if defined(COMPILING_PER_TARGET) || defined(COMPILING_SYSTEM_VS_USER) +# ifdef CONFIG_USER_ONLY +# undef icount_enabled +# define icount_enabled() ICOUNT_DISABLED +# endif +#endif + +/* + * Update the icount with the executed instructions. Called by + * cpus-tcg vCPU thread so the main-loop can see time has moved forward. + */ +void icount_update(CPUState *cpu); + +/* get raw icount value */ +int64_t icount_get_raw(void); + +/* return the virtual CPU time in ns, based on the instruction counter. */ +int64_t icount_get(void); +/* + * convert an instruction counter value to ns, based on the icount shift. + * This shift is set as a fixed value with the icount "shift" option + * (precise mode), or it is constantly approximated and corrected at + * runtime in adaptive mode. + */ +int64_t icount_to_ns(int64_t icount); + +/** + * 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); + +/* if the CPUs are idle, start accounting real time to virtual clock. */ +void icount_start_warp_timer(void); +void icount_account_warp_timer(void); +void icount_notify_exit(void); + +#endif /* EXEC_ICOUNT_H */ diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h index d04170aa27a..8db1d304647 100644 --- a/include/exec/memattrs.h +++ b/include/exec/memattrs.h @@ -23,12 +23,6 @@ * different semantics. */ typedef struct MemTxAttrs { - /* Bus masters which don't specify any attributes will get this - * (via the MEMTXATTRS_UNSPECIFIED constant), so that we can - * distinguish "all attributes deliberately clear" from - * "didn't specify" if necessary. - */ - unsigned int unspecified:1; /* * ARM/AMBA: TrustZone Secure access * x86: System Management Mode access @@ -50,28 +44,37 @@ typedef struct MemTxAttrs { * (see MEMTX_ACCESS_ERROR). */ unsigned int memory:1; + /* Debug access that can even write to ROM. */ + unsigned int debug: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; + + /* + * Bus masters which don't specify any attributes will get this + * (via the MEMTXATTRS_UNSPECIFIED constant), so that we can + * distinguish "all attributes deliberately clear" from + * "didn't specify" if necessary. "debug" can be set alongside + * "unspecified". + */ + bool unspecified; + + uint8_t _reserved1; + uint16_t _reserved2; } MemTxAttrs; +QEMU_BUILD_BUG_ON(sizeof(MemTxAttrs) > 8); + /* Bus masters which don't specify any attributes will get this, * which has all attribute bits clear except the topmost one * (so that we can distinguish "all attributes deliberately clear" * from "didn't specify" if necessary). */ -#define MEMTXATTRS_UNSPECIFIED ((MemTxAttrs) { .unspecified = 1 }) +#define MEMTXATTRS_UNSPECIFIED ((MemTxAttrs) { .unspecified = true }) /* New-style MMIO accessors can indicate that the transaction failed. * A zero (MEMTX_OK) response means success; anything else is a failure diff --git a/include/exec/memop.h b/include/exec/memop.h index a86dc6743a6..cf7da3362e8 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 @@ -91,8 +91,12 @@ typedef enum MemOp { * 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. + * by the alignment. E.g. if an 8-byte value is accessed at an + * address which is 0 mod 8, then the whole 8-byte access is + * single-copy atomic; otherwise, if it is accessed at 0 mod 4 + * then each 4-byte subobject is single-copy atomic; otherwise + * if it is accessed at 0 mod 2 then the four 2-byte subobjects + * are single-copy atomic. * This is the atomicity e.g. of IBM Power. * MO_ATOM_NONE: the operation has no atomicity requirements. * @@ -135,7 +139,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, @@ -158,16 +162,57 @@ static inline unsigned memop_size(MemOp op) static inline MemOp size_memop(unsigned size) { #ifdef CONFIG_DEBUG_TCG - /* Power of 2 up to 8. */ - assert((size & (size - 1)) == 0 && size >= 1 && size <= 8); + /* Power of 2 up to 1024 */ + assert(is_power_of_2(size) && size >= 1 && size <= (1 << MO_SIZE)); #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) +{ + 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) { - return (op & MO_BSWAP) == MO_BE; + 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_ldst.h.inc b/include/exec/memory_ldst.h.inc index 92ad74e9560..7270235c600 100644 --- a/include/exec/memory_ldst.h.inc +++ b/include/exec/memory_ldst.h.inc @@ -19,7 +19,6 @@ * License along with this library; if not, see . */ -#ifdef TARGET_ENDIANNESS uint16_t glue(address_space_lduw, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result); uint32_t glue(address_space_ldl, SUFFIX)(ARG1_DECL, @@ -34,7 +33,6 @@ void glue(address_space_stl, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); void glue(address_space_stq, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); -#else uint8_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result); uint16_t glue(address_space_lduw_le, SUFFIX)(ARG1_DECL, @@ -63,9 +61,7 @@ void glue(address_space_stq_le, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); void glue(address_space_stq_be, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); -#endif #undef ARG1_DECL #undef ARG1 #undef SUFFIX -#undef TARGET_ENDIANNESS diff --git a/include/exec/memory_ldst_phys.h.inc b/include/exec/memory_ldst_phys.h.inc index ecd678610d1..db67de75251 100644 --- a/include/exec/memory_ldst_phys.h.inc +++ b/include/exec/memory_ldst_phys.h.inc @@ -19,7 +19,6 @@ * License along with this library; if not, see . */ -#ifdef TARGET_ENDIANNESS static inline uint16_t glue(lduw_phys, SUFFIX)(ARG1_DECL, hwaddr addr) { return glue(address_space_lduw, SUFFIX)(ARG1, addr, @@ -55,7 +54,7 @@ static inline void glue(stq_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) glue(address_space_stq, SUFFIX)(ARG1, addr, val, MEMTXATTRS_UNSPECIFIED, NULL); } -#else + static inline uint8_t glue(ldub_phys, SUFFIX)(ARG1_DECL, hwaddr addr) { return glue(address_space_ldub, SUFFIX)(ARG1, addr, @@ -139,9 +138,7 @@ static inline void glue(stq_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t va glue(address_space_stq_be, SUFFIX)(ARG1, addr, val, MEMTXATTRS_UNSPECIFIED, NULL); } -#endif #undef ARG1_DECL #undef ARG1 #undef SUFFIX -#undef TARGET_ENDIANNESS diff --git a/include/exec/mmap-lock.h b/include/exec/mmap-lock.h new file mode 100644 index 00000000000..50ffdab9c5d --- /dev/null +++ b/include/exec/mmap-lock.h @@ -0,0 +1,33 @@ +/* + * QEMU user-only mmap lock, with stubs for system mode + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ +#ifndef EXEC_MMAP_LOCK_H +#define EXEC_MMAP_LOCK_H + +#ifdef CONFIG_USER_ONLY + +void TSA_NO_TSA mmap_lock(void); +void TSA_NO_TSA mmap_unlock(void); +bool have_mmap_lock(void); + +static inline void mmap_unlock_guard(void *unused) +{ + 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) + +#else + +static inline void mmap_lock(void) {} +static inline void mmap_unlock(void) {} +#define WITH_MMAP_LOCK_GUARD() + +#endif /* CONFIG_USER_ONLY */ +#endif /* EXEC_MMAP_LOCK_H */ diff --git a/include/exec/mmu-access-type.h b/include/exec/mmu-access-type.h new file mode 100644 index 00000000000..28bbb05b94c --- /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 00000000000..c43231af8b5 --- /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 54ddde308a1..101c25911c1 100644 --- a/include/exec/page-vary.h +++ b/include/exec/page-vary.h @@ -49,4 +49,13 @@ bool set_preferred_target_page_bits(int bits); */ void finalize_target_page_bits(void); +/** + * migration_legacy_page_bits + * + * For migration compatibility with qemu v2.9, prior to the introduction + * of the configuration/target-page-bits section, return the value of + * TARGET_PAGE_BITS that the target had then. + */ +int migration_legacy_page_bits(void); + #endif /* EXEC_PAGE_VARY_H */ diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h index 52828781bc2..cbb2ca2131d 100644 --- a/include/exec/plugin-gen.h +++ b/include/exec/plugin-gen.h @@ -18,19 +18,17 @@ 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_i64 addr, uint32_t info); #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; } @@ -42,15 +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_i64 addr, uint32_t info) -{ } - #endif /* CONFIG_PLUGIN */ #endif /* QEMU_PLUGIN_GEN_H */ diff --git a/include/exec/poison.h b/include/exec/poison.h index 1ea5633eb33..a779adbb7a6 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 @@ -37,35 +35,17 @@ #pragma GCC poison TARGET_HAS_BFLT #pragma GCC poison TARGET_NAME -#pragma GCC poison TARGET_SUPPORTS_MTTCG #pragma GCC poison TARGET_BIG_ENDIAN -#pragma GCC poison BSWAP_NEEDED +#pragma GCC poison TCG_GUEST_DEFAULT_MO #pragma GCC poison TARGET_LONG_BITS #pragma GCC poison TARGET_FMT_lx #pragma GCC poison TARGET_FMT_ld #pragma GCC poison TARGET_FMT_lu -#pragma GCC poison TARGET_PAGE_SIZE -#pragma GCC poison TARGET_PAGE_MASK -#pragma GCC poison TARGET_PAGE_BITS -#pragma GCC poison TARGET_PAGE_ALIGN - -#pragma GCC poison CPU_INTERRUPT_HARD -#pragma GCC poison CPU_INTERRUPT_EXITTB -#pragma GCC poison CPU_INTERRUPT_HALT -#pragma GCC poison CPU_INTERRUPT_DEBUG -#pragma GCC poison CPU_INTERRUPT_TGT_EXT_0 -#pragma GCC poison CPU_INTERRUPT_TGT_EXT_1 -#pragma GCC poison CPU_INTERRUPT_TGT_EXT_2 -#pragma GCC poison CPU_INTERRUPT_TGT_EXT_3 -#pragma GCC poison CPU_INTERRUPT_TGT_EXT_4 -#pragma GCC poison CPU_INTERRUPT_TGT_INT_0 -#pragma GCC poison CPU_INTERRUPT_TGT_INT_1 -#pragma GCC poison CPU_INTERRUPT_TGT_INT_2 +#pragma GCC poison TARGET_PHYS_ADDR_SPACE_BITS #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 @@ -73,7 +53,6 @@ #pragma GCC poison CONFIG_M68K_DIS #pragma GCC poison CONFIG_MICROBLAZE_DIS #pragma GCC poison CONFIG_MIPS_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 @@ -87,4 +66,11 @@ #pragma GCC poison CONFIG_WHPX #pragma GCC poison CONFIG_XEN +#ifndef COMPILING_SYSTEM_VS_USER +#pragma GCC poison CONFIG_USER_ONLY +#pragma GCC poison CONFIG_SOFTMMU +#endif + +#pragma GCC poison KVM_HAVE_MCE_INJECTION + #endif diff --git a/include/exec/ramblock.h b/include/exec/ramblock.h deleted file mode 100644 index 69c6a539029..00000000000 --- a/include/exec/ramblock.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Declarations for cpu physical memory functions - * - * Copyright 2011 Red Hat, Inc. and/or its affiliates - * - * Authors: - * Avi Kivity - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * later. See the COPYING file in the top-level directory. - * - */ - -/* - * This header is for use by exec.c and memory.c ONLY. Do not include it. - * The functions declared here will be removed soon. - */ - -#ifndef QEMU_EXEC_RAMBLOCK_H -#define QEMU_EXEC_RAMBLOCK_H - -#ifndef CONFIG_USER_ONLY -#include "cpu-common.h" -#include "qemu/rcu.h" -#include "exec/ramlist.h" - -struct RAMBlock { - struct rcu_head rcu; - struct MemoryRegion *mr; - uint8_t *host; - uint8_t *colo_cache; /* For colo, VM's ram cache */ - ram_addr_t offset; - ram_addr_t used_length; - ram_addr_t max_length; - void (*resized)(const char*, uint64_t length, void *host); - uint32_t flags; - /* Protected by iothread lock. */ - char idstr[256]; - /* RCU-enabled, writes protected by the ramlist lock */ - QLIST_ENTRY(RAMBlock) next; - QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers; - int fd; - uint64_t fd_offset; - size_t page_size; - /* dirty bitmap used during migration */ - unsigned long *bmap; - /* bitmap of already received pages in postcopy */ - unsigned long *receivedmap; - - /* - * bitmap to track already cleared dirty bitmap. When the bit is - * set, it means the corresponding memory chunk needs a log-clear. - * Set this up to non-NULL to enable the capability to postpone - * and split clearing of dirty bitmap on the remote node (e.g., - * KVM). The bitmap will be set only when doing global sync. - * - * It is only used during src side of ram migration, and it is - * protected by the global ram_state.bitmap_mutex. - * - * NOTE: this bitmap is different comparing to the other bitmaps - * in that one bit can represent multiple guest pages (which is - * decided by the `clear_bmap_shift' variable below). On - * destination side, this should always be NULL, and the variable - * `clear_bmap_shift' is meaningless. - */ - unsigned long *clear_bmap; - uint8_t clear_bmap_shift; - - /* - * RAM block length that corresponds to the used_length on the migration - * source (after RAM block sizes were synchronized). Especially, after - * starting to run the guest, used_length and postcopy_length can differ. - * Used to register/unregister uffd handlers and as the size of the received - * bitmap. Receiving any page beyond this length will bail out, as it - * could not have been valid on the source. - */ - ram_addr_t postcopy_length; -}; -#endif -#endif diff --git a/include/exec/ramlist.h b/include/exec/ramlist.h index 2ad2a81accf..d9cfe530bea 100644 --- a/include/exec/ramlist.h +++ b/include/exec/ramlist.h @@ -50,6 +50,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/target_long.h b/include/exec/target_long.h index 93c9472971f..3cd8e26a23f 100644 --- a/include/exec/target_long.h +++ b/include/exec/target_long.h @@ -29,12 +29,14 @@ 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 diff --git a/include/exec/target_page.h b/include/exec/target_page.h index 98ffbb5c239..ca0ebbc8bbd 100644 --- a/include/exec/target_page.h +++ b/include/exec/target_page.h @@ -14,10 +14,54 @@ #ifndef EXEC_TARGET_PAGE_H #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); +/* + * If compiling per-target, get the real values. + * For generic code, reuse the mechanism for variable page size. + */ +#ifdef COMPILING_PER_TARGET +#include "cpu-param.h" +#include "exec/target_long.h" +#define TARGET_PAGE_TYPE target_long +#else +#define TARGET_PAGE_BITS_VARY +#define TARGET_PAGE_TYPE int +#endif + +#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_PAGE_TYPE)target_page.mask; }) +# else +# define TARGET_PAGE_BITS target_page.bits +# define TARGET_PAGE_MASK ((TARGET_PAGE_TYPE)target_page.mask) +# endif +# define TARGET_PAGE_SIZE (-(int)TARGET_PAGE_MASK) +#else +# define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS) +# define TARGET_PAGE_MASK ((TARGET_PAGE_TYPE)-1 << TARGET_PAGE_BITS) +#endif + +#define TARGET_PAGE_ALIGN(addr) ROUND_UP((addr), TARGET_PAGE_SIZE) + +static inline size_t qemu_target_page_size(void) +{ + return TARGET_PAGE_SIZE; +} + +static inline int qemu_target_page_mask(void) +{ + return TARGET_PAGE_MASK; +} + +static inline int qemu_target_page_bits(void) +{ + return TARGET_PAGE_BITS; +} 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 index d92d06565be..142c240d94c 100644 --- a/include/exec/tb-flush.h +++ b/include/exec/tb-flush.h @@ -23,4 +23,6 @@ */ 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 index dc5a5faa0b3..03b5a8ffc71 100644 --- a/include/exec/tlb-common.h +++ b/include/exec/tlb-common.h @@ -19,14 +19,14 @@ #ifndef EXEC_TLB_COMMON_H #define EXEC_TLB_COMMON_H 1 -#define CPU_TLB_ENTRY_BITS 5 +#define CPU_TLB_ENTRY_BITS (HOST_LONG_BITS == 32 ? 4 : 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; + uintptr_t addr_read; + uintptr_t addr_write; + uintptr_t addr_code; /* * Addend to virtual address to get host address. IO accesses * use the corresponding iotlb value. @@ -37,7 +37,7 @@ typedef union CPUTLBEntry { * 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)]; + uintptr_t addr_idx[(1 << CPU_TLB_ENTRY_BITS) / sizeof(uintptr_t)]; } CPUTLBEntry; QEMU_BUILD_BUG_ON(sizeof(CPUTLBEntry) != (1 << CPU_TLB_ENTRY_BITS)); diff --git a/include/exec/tlb-flags.h b/include/exec/tlb-flags.h new file mode 100644 index 00000000000..357e79095c9 --- /dev/null +++ b/include/exec/tlb-flags.h @@ -0,0 +1,86 @@ +/* + * TLB flags definition + * + * 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 TLB_FLAGS_H +#define TLB_FLAGS_H + +/* + * Flags returned for lookup of a TLB virtual address. + */ + +#ifdef CONFIG_USER_ONLY + +/* + * Allow some level of source compatibility with softmmu. + * Invalid is set when the page does not have requested permissions. + * MMIO is set when we want the target helper to use the functional + * interface for load/store so that plugins see the access. + */ +#define TLB_INVALID_MASK (1 << 0) +#define TLB_MMIO (1 << 1) +#define TLB_WATCHPOINT 0 + +#else + +/* + * 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) +/* Set if TLB entry writes ignored. */ +#define TLB_DISCARD_WRITE (1 << 3) +/* Set if TLB entry is an IO callback. */ +#define TLB_MMIO (1 << 4) + +#define TLB_SLOW_FLAGS_MASK \ + (TLB_BSWAP | TLB_WATCHPOINT | TLB_CHECK_ALIGNED | \ + TLB_DISCARD_WRITE | TLB_MMIO) + +/* + * Flags stored in CPUTLBEntry.addr_idx[x]. + * These must be above the largest alignment (64 bytes), + * and below the smallest page size (1024 bytes). + * This leaves bits [9:6] available for use. + */ + +/* Zero if TLB entry is valid. */ +#define TLB_INVALID_MASK (1 << 6) +/* Set if TLB entry references a clean RAM page. */ +#define TLB_NOTDIRTY (1 << 7) +/* Set if the slow path must be used; more flags in CPUTLBEntryFull. */ +#define TLB_FORCE_SLOW (1 << 8) + +/* + * Use this mask to check interception with an alignment mask + * in a TCG backend. + */ +#define TLB_FLAGS_MASK \ + (TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_FORCE_SLOW) + +/* The two sets of flags must not overlap. */ +QEMU_BUILD_BUG_ON(TLB_FLAGS_MASK & TLB_SLOW_FLAGS_MASK); + +#endif /* !CONFIG_USER_ONLY */ + +#endif /* TLB_FLAGS_H */ diff --git a/include/exec/translate-all.h b/include/exec/translate-all.h deleted file mode 100644 index 88602ae8d83..00000000000 --- a/include/exec/translate-all.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Translated block handling - * - * 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 TRANSLATE_ALL_H -#define TRANSLATE_ALL_H - -#include "exec/exec-all.h" - - -/* translate-all.c */ -void tb_invalidate_phys_page(tb_page_addr_t addr); -void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr); - -#ifdef CONFIG_USER_ONLY -void page_protect(tb_page_addr_t page_addr); -int page_unprotect(target_ulong address, uintptr_t pc); -#endif - -#endif /* TRANSLATE_ALL_H */ diff --git a/include/exec/translation-block.h b/include/exec/translation-block.h index b785751774e..cdce399ebab 100644 --- a/include/exec/translation-block.h +++ b/include/exec/translation-block.h @@ -7,10 +7,13 @@ #ifndef EXEC_TRANSLATION_BLOCK_H #define EXEC_TRANSLATION_BLOCK_H +#include "qemu/atomic.h" #include "qemu/thread.h" #include "exec/cpu-common.h" +#include "exec/vaddr.h" #ifdef CONFIG_USER_ONLY #include "qemu/interval-tree.h" +#include "exec/target_page.h" #endif /* @@ -71,13 +74,13 @@ struct TranslationBlock { #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_PCREL 0x00200000 /* Opcodes in TB are PC-relative */ +#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 @@ -146,4 +149,66 @@ struct TranslationBlock { /* 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); +} + +bool tcg_cflags_has(CPUState *cpu, uint32_t flags); +void tcg_cflags_set(CPUState *cpu, uint32_t flags); + +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 +} + +/* TranslationBlock invalidate API */ +void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start, + tb_page_addr_t last); + #endif /* EXEC_TRANSLATION_BLOCK_H */ diff --git a/include/exec/translator.h b/include/exec/translator.h index 4e17c4f401d..3c326555696 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -18,22 +18,8 @@ * member in your target-specific DisasContext. */ -#include "qemu/bswap.h" -#include "exec/cpu_ldst.h" /* for abi_ptr */ - -/** - * gen_intermediate_code - * @cpu: cpu context - * @tb: translation block - * @max_insns: max number of instructions to translate - * @pc: guest virtual program counter address - * @host_pc: host physical program counter address - * - * This function must be provided by the target, which should create - * the target-specific DisasContext, and then invoke translator_loop. - */ -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, - target_ulong pc, void *host_pc); +#include "exec/memop.h" +#include "exec/vaddr.h" /** * DisasJumpType: @@ -71,20 +57,38 @@ 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; + uint8_t code_mmuidx; + 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: @@ -116,7 +120,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; /** @@ -176,64 +180,100 @@ bool translator_io_start(DisasContextBase *db); * 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_end(CPUArchState *env, DisasContextBase *db, + vaddr pc, MemOp endian); +uint32_t translator_ldl_end(CPUArchState *env, DisasContextBase *db, + vaddr pc, MemOp endian); +uint64_t translator_ldq_end(CPUArchState *env, DisasContextBase *db, + vaddr pc, MemOp endian); + +#ifdef COMPILING_PER_TARGET +static inline uint16_t +translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc) +{ + return translator_lduw_end(env, db, pc, MO_TE); +} + +static inline uint32_t +translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc) +{ + return translator_ldl_end(env, db, pc, MO_TE); +} + +static inline uint64_t +translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc) +{ + return translator_ldq_end(env, db, pc, MO_TE); +} 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) { - ret = bswap16(ret); - } - return ret; + return translator_lduw_end(env, db, pc, MO_TE ^ (do_swap * MO_BSWAP)); } 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) { - ret = bswap32(ret); - } - return ret; + return translator_ldl_end(env, db, pc, MO_TE ^ (do_swap * MO_BSWAP)); } 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) { - ret = bswap64(ret); - } - return ret; + return translator_ldq_end(env, db, pc, MO_TE ^ (do_swap * MO_BSWAP)); } +#endif /* COMPILING_PER_TARGET */ /** - * 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_ldb(uint8_t insn8, abi_ptr pc); +void translator_fake_ld(DisasContextBase *db, const void *data, size_t len); -/* - * Return whether addr is on the same page as where disassembly started. +/** + * 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); + +/** + * translator_is_same_page + * @db: disassembly context + * @addr: virtual address within TB + * + * 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) -{ - return ((addr ^ db->pc_first) & TARGET_PAGE_MASK) == 0; -} +bool translator_is_same_page(const DisasContextBase *db, vaddr addr); #endif /* EXEC__TRANSLATOR_H */ diff --git a/include/exec/tswap.h b/include/exec/tswap.h index 68944a880ba..49511f26117 100644 --- a/include/exec/tswap.h +++ b/include/exec/tswap.h @@ -8,18 +8,29 @@ #ifndef TSWAP_H #define TSWAP_H -#include "hw/core/cpu.h" #include "qemu/bswap.h" +/** + * target_big_endian: + * Returns true if the (default) endianness of the target is big endian, + * false otherwise. 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_big_endian(void); +#ifdef COMPILING_PER_TARGET +#define target_big_endian() TARGET_BIG_ENDIAN +#endif + /* * If we're in target-specific code, we can hard-code the swapping * condition, otherwise we have to do (slower) run-time checks. */ -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #define target_needs_bswap() (HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN) #else -#define target_needs_bswap() (target_words_bigendian() != HOST_BIG_ENDIAN) -#endif +#define target_needs_bswap() (HOST_BIG_ENDIAN != target_big_endian()) +#endif /* COMPILING_PER_TARGET */ static inline uint16_t tswap16(uint16_t s) { @@ -69,4 +80,74 @@ static inline void tswap64s(uint64_t *s) } } +/* Return ld{word}_{le,be}_p following target endianness. */ +#define LOAD_IMPL(word, args...) \ +do { \ + if (target_big_endian()) { \ + return glue(glue(ld, word), _be_p)(args); \ + } else { \ + return glue(glue(ld, word), _le_p)(args); \ + } \ +} while (0) + +static inline int lduw_p(const void *ptr) +{ + LOAD_IMPL(uw, ptr); +} + +static inline int ldsw_p(const void *ptr) +{ + LOAD_IMPL(sw, ptr); +} + +static inline int ldl_p(const void *ptr) +{ + LOAD_IMPL(l, ptr); +} + +static inline uint64_t ldq_p(const void *ptr) +{ + LOAD_IMPL(q, ptr); +} + +static inline uint64_t ldn_p(const void *ptr, int sz) +{ + LOAD_IMPL(n, ptr, sz); +} + +#undef LOAD_IMPL + +/* Call st{word}_{le,be}_p following target endianness. */ +#define STORE_IMPL(word, args...) \ +do { \ + if (target_big_endian()) { \ + glue(glue(st, word), _be_p)(args); \ + } else { \ + glue(glue(st, word), _le_p)(args); \ + } \ +} while (0) + + +static inline void stw_p(void *ptr, uint16_t v) +{ + STORE_IMPL(w, ptr, v); +} + +static inline void stl_p(void *ptr, uint32_t v) +{ + STORE_IMPL(l, ptr, v); +} + +static inline void stq_p(void *ptr, uint64_t v) +{ + STORE_IMPL(q, ptr, v); +} + +static inline void stn_p(void *ptr, int sz, uint64_t v) +{ + STORE_IMPL(n, ptr, sz, v); +} + +#undef STORE_IMPL + #endif /* TSWAP_H */ diff --git a/include/exec/user/guest-base.h b/include/exec/user/guest-base.h deleted file mode 100644 index afe2ab7fbbe..00000000000 --- a/include/exec/user/guest-base.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Declaration of guest_base. - * Copyright (c) 2003 Fabrice Bellard - */ - -#ifndef EXEC_USER_GUEST_BASE_H -#define EXEC_USER_GUEST_BASE_H - -extern uintptr_t guest_base; - -#endif diff --git a/include/exec/vaddr.h b/include/exec/vaddr.h new file mode 100644 index 00000000000..28bec632fbb --- /dev/null +++ b/include/exec/vaddr.h @@ -0,0 +1,20 @@ +/* Define vaddr. */ + +#ifndef VADDR_H +#define VADDR_H + +/** + * vaddr: + * Type wide enough to contain any #target_ulong virtual address. + * We do not support 64-bit guest on 32-host and detect at configure time. + * Therefore, a host pointer width will always fit a guest pointer. + */ +typedef uintptr_t vaddr; +#define VADDR_PRId PRIdPTR +#define VADDR_PRIu PRIuPTR +#define VADDR_PRIo PRIoPTR +#define VADDR_PRIx PRIxPTR +#define VADDR_PRIX PRIXPTR +#define VADDR_MAX UINTPTR_MAX + +#endif diff --git a/include/exec/watchpoint.h b/include/exec/watchpoint.h new file mode 100644 index 00000000000..4b6668826c7 --- /dev/null +++ b/include/exec/watchpoint.h @@ -0,0 +1,41 @@ +/* + * CPU watchpoints + * + * Copyright (c) 2012 SUSE LINUX Products GmbH + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef EXEC_WATCHPOINT_H +#define EXEC_WATCHPOINT_H + +#if defined(CONFIG_USER_ONLY) +static inline int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, + int flags, CPUWatchpoint **watchpoint) +{ + return -ENOSYS; +} + +static inline int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, + vaddr len, int flags) +{ + return -ENOSYS; +} + +static inline void cpu_watchpoint_remove_by_ref(CPUState *cpu, + CPUWatchpoint *wp) +{ +} + +static inline void cpu_watchpoint_remove_all(CPUState *cpu, int mask) +{ +} +#else +int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, + int flags, CPUWatchpoint **watchpoint); +int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, + vaddr len, int flags); +void cpu_watchpoint_remove_by_ref(CPUState *cpu, CPUWatchpoint *watchpoint); +void cpu_watchpoint_remove_all(CPUState *cpu, int mask); +#endif + +#endif /* EXEC_WATCHPOINT_H */ diff --git a/include/fpu/softfloat-helpers.h b/include/fpu/softfloat-helpers.h index 94cbe073ec5..90862f5cd22 100644 --- a/include/fpu/softfloat-helpers.h +++ b/include/fpu/softfloat-helpers.h @@ -75,6 +75,36 @@ static inline void set_floatx80_rounding_precision(FloatX80RoundPrec val, status->floatx80_rounding_precision = val; } +static inline void set_floatx80_behaviour(FloatX80Behaviour b, + float_status *status) +{ + status->floatx80_behaviour = b; +} + +static inline void set_float_2nan_prop_rule(Float2NaNPropRule rule, + float_status *status) +{ + status->float_2nan_prop_rule = rule; +} + +static inline void set_float_3nan_prop_rule(Float3NaNPropRule rule, + float_status *status) +{ + status->float_3nan_prop_rule = rule; +} + +static inline void set_float_infzeronan_rule(FloatInfZeroNaNRule rule, + float_status *status) +{ + status->float_infzeronan_rule = rule; +} + +static inline void set_float_default_nan_pattern(uint8_t dnan_pattern, + float_status *status) +{ + status->default_nan_pattern = dnan_pattern; +} + static inline void set_flush_to_zero(bool val, float_status *status) { status->flush_to_zero = val; @@ -85,6 +115,12 @@ static inline void set_flush_inputs_to_zero(bool val, float_status *status) status->flush_inputs_to_zero = val; } +static inline void set_float_ftz_detection(FloatFTZDetection d, + float_status *status) +{ + status->ftz_detection = d; +} + static inline void set_default_nan_mode(bool val, float_status *status) { status->default_nan_mode = val; @@ -95,50 +131,79 @@ static inline void set_snan_bit_is_one(bool val, float_status *status) status->snan_bit_is_one = val; } -static inline void set_use_first_nan(bool val, float_status *status) -{ - status->use_first_nan = val; -} - static inline void set_no_signaling_nans(bool val, float_status *status) { status->no_signaling_nans = val; } -static inline bool get_float_detect_tininess(float_status *status) +static inline bool get_float_detect_tininess(const float_status *status) { return status->tininess_before_rounding; } -static inline FloatRoundMode get_float_rounding_mode(float_status *status) +static inline FloatRoundMode get_float_rounding_mode(const float_status *status) { return status->float_rounding_mode; } -static inline int get_float_exception_flags(float_status *status) +static inline int get_float_exception_flags(const float_status *status) { return status->float_exception_flags; } static inline FloatX80RoundPrec -get_floatx80_rounding_precision(float_status *status) +get_floatx80_rounding_precision(const float_status *status) { return status->floatx80_rounding_precision; } -static inline bool get_flush_to_zero(float_status *status) +static inline FloatX80Behaviour +get_floatx80_behaviour(const float_status *status) +{ + return status->floatx80_behaviour; +} + +static inline Float2NaNPropRule +get_float_2nan_prop_rule(const float_status *status) +{ + return status->float_2nan_prop_rule; +} + +static inline Float3NaNPropRule +get_float_3nan_prop_rule(const float_status *status) +{ + return status->float_3nan_prop_rule; +} + +static inline FloatInfZeroNaNRule +get_float_infzeronan_rule(const float_status *status) +{ + return status->float_infzeronan_rule; +} + +static inline uint8_t get_float_default_nan_pattern(const float_status *status) +{ + return status->default_nan_pattern; +} + +static inline bool get_flush_to_zero(const float_status *status) { return status->flush_to_zero; } -static inline bool get_flush_inputs_to_zero(float_status *status) +static inline bool get_flush_inputs_to_zero(const float_status *status) { return status->flush_inputs_to_zero; } -static inline bool get_default_nan_mode(float_status *status) +static inline bool get_default_nan_mode(const float_status *status) { return status->default_nan_mode; } +static inline FloatFTZDetection get_float_ftz_detection(const float_status *status) +{ + return status->ftz_detection; +} + #endif /* SOFTFLOAT_HELPERS_H */ diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 0884ec4ef7a..1af2a0cb14b 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -80,6 +80,8 @@ this code that are retained. #ifndef SOFTFLOAT_TYPES_H #define SOFTFLOAT_TYPES_H +#include "hw/registerfields.h" + /* * Software IEC/IEEE floating-point types. */ @@ -138,6 +140,8 @@ typedef enum __attribute__((__packed__)) { float_round_to_odd = 5, /* Not an IEEE rounding mode: round to closest odd, overflow to inf */ float_round_to_odd_inf = 6, + /* Not an IEEE rounding mode: round to nearest even, overflow to max */ + float_round_nearest_even_max = 7, } FloatRoundMode; /* @@ -150,8 +154,10 @@ enum { float_flag_overflow = 0x0004, float_flag_underflow = 0x0008, float_flag_inexact = 0x0010, - float_flag_input_denormal = 0x0020, - float_flag_output_denormal = 0x0040, + /* We flushed an input denormal to 0 (because of flush_inputs_to_zero) */ + float_flag_input_denormal_flushed = 0x0020, + /* We flushed an output denormal to 0 (because of flush_to_zero) */ + float_flag_output_denormal_flushed = 0x0040, float_flag_invalid_isi = 0x0080, /* inf - inf */ float_flag_invalid_imz = 0x0100, /* inf * 0 */ float_flag_invalid_idi = 0x0200, /* inf / inf */ @@ -159,6 +165,13 @@ enum { float_flag_invalid_sqrt = 0x0800, /* sqrt(-x) */ float_flag_invalid_cvti = 0x1000, /* non-nan to integer */ float_flag_invalid_snan = 0x2000, /* any operand was snan */ + /* + * An input was denormal and we used it (without flushing it to zero). + * Not set if we do not actually use the denormal input (e.g. + * because some other input was a NaN, or because the operation + * wasn't actually carried out (divide-by-zero; invalid)) + */ + float_flag_input_denormal_used = 0x4000, }; /* @@ -170,6 +183,193 @@ 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; + +/* + * 3-input NaN propagation rule, for fused multiply-add. 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 if it uses a muladd, and we will assert if we need + * to handle an input NaN and no rule was selected. + * + * The naming scheme for Float3NaNPropRule values is: + * float_3nan_prop_s_abc: + * = "Prefer SNaN over QNaN, then operand A over B over C" + * float_3nan_prop_abc: + * = "Prefer A over B over C regardless of SNaN vs QNAN" + * + * For QEMU, the multiply-add operation is A * B + C. + */ + +/* + * We set the Float3NaNPropRule enum values up so we can select the + * right value in pickNaNMulAdd in a data driven way. + */ +FIELD(3NAN, 1ST, 0, 2) /* which operand is most preferred ? */ +FIELD(3NAN, 2ND, 2, 2) /* which operand is next most preferred ? */ +FIELD(3NAN, 3RD, 4, 2) /* which operand is least preferred ? */ +FIELD(3NAN, SNAN, 6, 1) /* do we prefer SNaN over QNaN ? */ + +#define PROPRULE(X, Y, Z) \ + ((X << R_3NAN_1ST_SHIFT) | (Y << R_3NAN_2ND_SHIFT) | (Z << R_3NAN_3RD_SHIFT)) + +typedef enum __attribute__((__packed__)) { + float_3nan_prop_none = 0, /* No propagation rule specified */ + float_3nan_prop_abc = PROPRULE(0, 1, 2), + float_3nan_prop_acb = PROPRULE(0, 2, 1), + float_3nan_prop_bac = PROPRULE(1, 0, 2), + float_3nan_prop_bca = PROPRULE(1, 2, 0), + float_3nan_prop_cab = PROPRULE(2, 0, 1), + float_3nan_prop_cba = PROPRULE(2, 1, 0), + float_3nan_prop_s_abc = float_3nan_prop_abc | R_3NAN_SNAN_MASK, + float_3nan_prop_s_acb = float_3nan_prop_acb | R_3NAN_SNAN_MASK, + float_3nan_prop_s_bac = float_3nan_prop_bac | R_3NAN_SNAN_MASK, + float_3nan_prop_s_bca = float_3nan_prop_bca | R_3NAN_SNAN_MASK, + float_3nan_prop_s_cab = float_3nan_prop_cab | R_3NAN_SNAN_MASK, + float_3nan_prop_s_cba = float_3nan_prop_cba | R_3NAN_SNAN_MASK, +} Float3NaNPropRule; + +#undef PROPRULE + +/* + * Rule for result of fused multiply-add 0 * Inf + NaN. + * This must be a NaN, but implementations differ on whether this + * is the input NaN or the default NaN. + * + * You don't need to set this if default_nan_mode is enabled. + * When not in default-NaN mode, it is an error for the target + * not to set the rule in float_status if it uses muladd, 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_infzeronan_none = 0, + /* Result is never the default NaN (so always the input NaN) */ + float_infzeronan_dnan_never = 1, + /* Result is always the default NaN */ + float_infzeronan_dnan_always = 2, + /* Result is the default NaN if the input NaN is quiet */ + float_infzeronan_dnan_if_qnan = 3, + /* + * Don't raise Invalid for 0 * Inf + NaN. Default is to raise. + * IEEE 754-2008 section 7.2 makes it implementation defined whether + * 0 * Inf + QNaN raises Invalid or not. Note that 0 * Inf + SNaN will + * raise the Invalid flag for the SNaN anyway. + * + * This is a flag which can be ORed in with any of the above + * DNaN behaviour options. + */ + float_infzeronan_suppress_invalid = (1 << 7), +} FloatInfZeroNaNRule; + +/* + * When flush_to_zero is set, should we detect denormal results to + * be flushed before or after rounding? For most architectures this + * should be set to match the tininess_before_rounding setting, + * but a few architectures, e.g. MIPS MSA, detect FTZ before + * rounding but tininess after rounding. + * + * This enum is arranged so that the default if the target doesn't + * configure it matches the default for tininess_before_rounding + * (i.e. "after rounding"). + */ +typedef enum __attribute__((__packed__)) { + float_ftz_after_rounding = 0, + float_ftz_before_rounding = 1, +} FloatFTZDetection; + +/* + * floatx80 is primarily used by x86 and m68k, and there are + * differences in the handling, largely related to the explicit + * Integer bit which floatx80 has and the other float formats do not. + * These flag values allow specification of the target's requirements + * and can be ORed together to set floatx80_behaviour. + */ +typedef enum __attribute__((__packed__)) { + /* In the default Infinity value, is the Integer bit 0 ? */ + floatx80_default_inf_int_bit_is_zero = 1, + /* + * Are Pseudo-infinities (Inf with the Integer bit zero) valid? + * If so, floatx80_is_infinity() will return true for them. + * If not, floatx80_invalid_encoding will return false for them, + * and using them as inputs to a float op will raise Invalid. + */ + floatx80_pseudo_inf_valid = 2, + /* + * Are Pseudo-NaNs (NaNs where the Integer bit is zero) valid? + * If not, floatx80_invalid_encoding() will return false for them, + * and using them as inputs to a float op will raise Invalid. + */ + floatx80_pseudo_nan_valid = 4, + /* + * Are Unnormals (0 < exp < 0x7fff, Integer bit zero) valid? + * If not, floatx80_invalid_encoding() will return false for them, + * and using them as inputs to a float op will raise Invalid. + */ + floatx80_unnormal_valid = 8, + + /* + * If the exponent is 0 and the Integer bit is set, Intel call + * this a "pseudo-denormal"; x86 supports that only on input + * (treating them as denormals by ignoring the Integer bit). + * For m68k, the integer bit is considered validly part of the + * input value when the exponent is 0, and may be 0 or 1, + * giving extra range. They may also be generated as outputs. + * (The m68k manual actually calls these values part of the + * normalized number range, not the denormalized number range.) + * + * By default you get the Intel behaviour where the Integer + * bit is ignored; if this is set then the Integer bit value + * is honoured, m68k-style. + * + * Either way, floatx80_invalid_encoding() will always accept + * pseudo-denormals. + */ + floatx80_pseudo_denormal_valid = 16, +} FloatX80Behaviour; + /* * Floating Point Status. Individual architectures may maintain * several versions of float_status for different functions. The @@ -181,19 +381,34 @@ typedef struct float_status { uint16_t float_exception_flags; FloatRoundMode float_rounding_mode; FloatX80RoundPrec floatx80_rounding_precision; + FloatX80Behaviour floatx80_behaviour; + Float2NaNPropRule float_2nan_prop_rule; + Float3NaNPropRule float_3nan_prop_rule; + FloatInfZeroNaNRule float_infzeronan_rule; bool tininess_before_rounding; - /* should denormalised results go to zero and set the inexact flag? */ + /* should denormalised results go to zero and set output_denormal_flushed? */ bool flush_to_zero; - /* should denormalised inputs go to zero and set the input_denormal flag? */ + /* do we detect and flush denormal results before or after rounding? */ + FloatFTZDetection ftz_detection; + /* should denormalised inputs go to zero and set input_denormal_flushed? */ bool flush_inputs_to_zero; bool default_nan_mode; + /* + * The pattern to use for the default NaN. Here the high bit specifies + * the default NaN's sign bit, and bits 6..0 specify the high bits of the + * fractional part. The low bits of the fractional part are copies of bit 0. + * The exponent of the default NaN is (as for any NaN) always all 1s. + * Note that a value of 0 here is not a valid NaN. The target must set + * this to the correct non-zero value, or we will assert when trying to + * create a default NaN. + */ + uint8_t default_nan_pattern; /* * The flags below are not used on all specializations and may * constant fold away (see snan_bit_is_one()/no_signalling_nans() in * softfloat-specialize.inc.c) */ bool snan_bit_is_one; - bool use_first_nan; bool no_signaling_nans; /* should overflowed results subtract re_bias to its exponent? */ bool rebias_overflow; diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index cd130564d8b..c18ab2cb609 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -120,14 +120,16 @@ bfloat16 bfloat16_squash_input_denormal(bfloat16 a, float_status *status); | Using these differs from negating an input or output before calling | the muladd function in that this means that a NaN doesn't have its | sign bit inverted before it is propagated. -| We also support halving the result before rounding, as a special -| case to support the ARM fused-sqrt-step instruction FRSQRTS. +| +| With float_muladd_suppress_add_product_zero, if A or B is zero +| such that the product is a true zero, then return C without addition. +| This preserves the sign of C when C is +/- 0. Used for Hexagon. *----------------------------------------------------------------------------*/ enum { float_muladd_negate_c = 1, float_muladd_negate_product = 2, float_muladd_negate_result = 4, - float_muladd_halve_result = 8, + float_muladd_suppress_add_product_zero = 8, }; /*---------------------------------------------------------------------------- @@ -238,6 +240,8 @@ float16 float16_add(float16, float16, float_status *status); float16 float16_sub(float16, float16, float_status *status); float16 float16_mul(float16, float16, float_status *status); float16 float16_muladd(float16, float16, float16, int, float_status *status); +float16 float16_muladd_scalbn(float16, float16, float16, + int, int, float_status *status); float16 float16_div(float16, float16, float_status *status); float16 float16_scalbn(float16, int, float_status *status); float16 float16_min(float16, float16, float_status *status); @@ -366,6 +370,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 +379,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 +398,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); @@ -585,6 +601,8 @@ float32 float32_mul(float32, float32, float_status *status); float32 float32_div(float32, float32, float_status *status); float32 float32_rem(float32, float32, float_status *status); float32 float32_muladd(float32, float32, float32, int, float_status *status); +float32 float32_muladd_scalbn(float32, float32, float32, + int, int, float_status *status); float32 float32_sqrt(float32, float_status *status); float32 float32_exp2(float32, float_status *status); float32 float32_log2(float32, float_status *status); @@ -780,6 +798,8 @@ float64 float64_mul(float64, float64, float_status *status); float64 float64_div(float64, float64, float_status *status); float64 float64_rem(float64, float64, float_status *status); float64 float64_muladd(float64, float64, float64, int, float_status *status); +float64 float64_muladd_scalbn(float64, float64, float64, + int, int, float_status *status); float64 float64_sqrt(float64, float_status *status); float64 float64_log2(float64, float_status *status); FloatRelation float64_compare(float64, float64, float_status *status); @@ -940,7 +960,7 @@ float128 floatx80_to_float128(floatx80, float_status *status); /*---------------------------------------------------------------------------- | The pattern for an extended double-precision inf. *----------------------------------------------------------------------------*/ -extern const floatx80 floatx80_infinity; +floatx80 floatx80_default_inf(bool zSign, float_status *status); /*---------------------------------------------------------------------------- | Software IEC/IEEE extended double-precision operations. @@ -975,14 +995,19 @@ static inline floatx80 floatx80_chs(floatx80 a) return a; } -static inline bool floatx80_is_infinity(floatx80 a) +static inline bool floatx80_is_infinity(floatx80 a, float_status *status) { -#if defined(TARGET_M68K) - return (a.high & 0x7fff) == floatx80_infinity.high && !(a.low << 1); -#else - return (a.high & 0x7fff) == floatx80_infinity.high && - a.low == floatx80_infinity.low; -#endif + /* + * It's target-specific whether the Integer bit is permitted + * to be 0 in a valid Infinity value. (x86 says no, m68k says yes). + */ + bool intbit = a.low >> 63; + + if (!intbit && + !(status->floatx80_behaviour & floatx80_pseudo_inf_valid)) { + return false; + } + return (a.high & 0x7fff) == 0x7fff && !(a.low << 1); } static inline bool floatx80_is_neg(floatx80 a) @@ -1048,41 +1073,45 @@ static inline bool floatx80_unordered_quiet(floatx80 a, floatx80 b, /*---------------------------------------------------------------------------- | Return whether the given value is an invalid floatx80 encoding. -| Invalid floatx80 encodings arise when the integer bit is not set, but -| the exponent is not zero. The only times the integer bit is permitted to -| be zero is in subnormal numbers and the value zero. -| This includes what the Intel software developer's manual calls pseudo-NaNs, -| pseudo-infinities and un-normal numbers. It does not include -| pseudo-denormals, which must still be correctly handled as inputs even -| if they are never generated as outputs. +| Invalid floatx80 encodings may arise when the integer bit is not set +| correctly; this is target-specific. In Intel terminology the +| categories are: +| exp == 0, int = 0, mantissa == 0 : zeroes +| exp == 0, int = 0, mantissa != 0 : denormals +| exp == 0, int = 1 : pseudo-denormals +| 0 < exp < 0x7fff, int = 0 : unnormals +| 0 < exp < 0x7fff, int = 1 : normals +| exp == 0x7fff, int = 0, mantissa == 0 : pseudo-infinities +| exp == 0x7fff, int = 1, mantissa == 0 : infinities +| exp == 0x7fff, int = 0, mantissa != 0 : pseudo-NaNs +| exp == 0x7fff, int = 1, mantissa == 0 : NaNs +| +| The usual IEEE cases of zero, denormal, normal, inf and NaN are always valid. +| x87 permits as input also pseudo-denormals. +| m68k permits all those and also pseudo-infinities, pseudo-NaNs and unnormals. +| +| Since we don't have a target that handles floatx80 but prohibits +| pseudo-denormals in input, we don't currently have a floatx80_behaviour +| flag for that case, but instead always accept it. Conveniently this +| means that all cases with either exponent 0 or the integer bit set are +| valid for all targets. *----------------------------------------------------------------------------*/ -static inline bool floatx80_invalid_encoding(floatx80 a) -{ -#if defined(TARGET_M68K) - /*------------------------------------------------------------------------- - | With m68k, the explicit integer bit can be zero in the case of: - | - zeros (exp == 0, mantissa == 0) - | - denormalized numbers (exp == 0, mantissa != 0) - | - unnormalized numbers (exp != 0, exp < 0x7FFF) - | - infinities (exp == 0x7FFF, mantissa == 0) - | - not-a-numbers (exp == 0x7FFF, mantissa != 0) - | - | For infinities and NaNs, the explicit integer bit can be either one or - | zero. - | - | The IEEE 754 standard does not define a zero integer bit. Such a number - | is an unnormalized number. Hardware does not directly support - | denormalized and unnormalized numbers, but implicitly supports them by - | trapping them as unimplemented data types, allowing efficient conversion - | in software. - | - | See "M68000 FAMILY PROGRAMMER’S REFERENCE MANUAL", - | "1.6 FLOATING-POINT DATA TYPES" - *------------------------------------------------------------------------*/ - return false; -#else - return (a.low & (1ULL << 63)) == 0 && (a.high & 0x7FFF) != 0; -#endif +static inline bool floatx80_invalid_encoding(floatx80 a, float_status *s) +{ + if ((a.low >> 63) || (a.high & 0x7fff) == 0) { + /* Anything with the Integer bit set or the exponent 0 is valid */ + return false; + } + + if ((a.high & 0x7fff) == 0x7fff) { + if (a.low) { + return !(s->floatx80_behaviour & floatx80_pseudo_nan_valid); + } else { + return !(s->floatx80_behaviour & floatx80_pseudo_inf_valid); + } + } else { + return !(s->floatx80_behaviour & floatx80_unnormal_valid); + } } #define floatx80_zero make_floatx80(0x0000, 0x0000000000000000LL) diff --git a/include/gdbstub/commands.h b/include/gdbstub/commands.h new file mode 100644 index 00000000000..bff3674872e --- /dev/null +++ b/include/gdbstub/commands.h @@ -0,0 +1,108 @@ +#ifndef GDBSTUB_COMMANDS_H +#define GDBSTUB_COMMANDS_H + +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 00000000000..c4d54a1d083 --- /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 index c573aef2dcb..6f7cc48adcb 100644 --- a/include/gdbstub/helpers.h +++ b/include/gdbstub/helpers.h @@ -12,8 +12,12 @@ #ifndef _GDBSTUB_HELPERS_H_ #define _GDBSTUB_HELPERS_H_ -#ifdef NEED_CPU_H -#include "cpu.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 @@ -91,13 +95,13 @@ static inline uint8_t *gdb_get_reg_ptr(GByteArray *buf, int 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) -#endif - -#else -#error "gdbstub helpers should only be included by target specific code" +#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 index 243eaf8ce43..d63228e96b4 100644 --- a/include/gdbstub/syscalls.h +++ b/include/gdbstub/syscalls.h @@ -3,7 +3,7 @@ * * Copyright (c) 2023 Linaro Ltd * - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef _SYSCALLS_H_ @@ -110,4 +110,13 @@ int use_gdb_syscalls(void); */ 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 index d392e510c59..654986d483b 100644 --- a/include/gdbstub/user.h +++ b/include/gdbstub/user.h @@ -3,16 +3,21 @@ * * Copyright (c) 2022 Linaro Ltd * - * SPDX-License-Identifier: LGPL-2.0+ + * 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 @@ -24,7 +29,7 @@ * or 0 if no signal should be delivered, ie the signal that caused * us to stop should be ignored. */ -int gdb_handlesig(CPUState *, int); +int gdb_handlesig(CPUState *, int, const char *, void *, int); /** * gdb_signalled() - inform remote gdb of sig exit @@ -34,10 +39,29 @@ int gdb_handlesig(CPUState *, int); void gdb_signalled(CPUArchState *as, int sig); /** - * gdbserver_fork() - disable gdb stub for child processes. + * 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(CPUState *cs); +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 43a562974d8..2e32b90f051 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" @@ -36,6 +36,13 @@ #include #endif +/* + * These functions perform function pointer casts which can cause function call + * failure on Emscripten. Use g_slist_sort_with_data and g_list_sort_with_data + * instead of these functions. + */ +#pragma GCC poison g_slist_sort g_list_sort + /* * Note that because of the GLIB_VERSION_MAX_ALLOWED constant above, allowing * use of functions from newer GLib via this compat header needs a little @@ -105,29 +112,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 2b42e4192bf..2e6e3419987 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -90,6 +90,42 @@ 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; + 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 uart_clk_freq; + uint32_t precise_baudrate; + uint32_t namespace_string_length; + uint32_t namespace_string_offset; + char namespace_string[]; +} 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 e0e51e85b41..4b8ee094c41 100644 --- a/include/hw/acpi/acpi.h +++ b/include/hw/acpi/acpi.h @@ -21,7 +21,7 @@ */ #include "qemu/notify.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/acpi/acpi_dev_interface.h" /* @@ -150,6 +150,9 @@ struct ACPIREGS { Notifier wakeup; }; +/* Return whether ACPI subsystem is built in */ +bool acpi_builtin(void); + /* PM_TMR */ void acpi_pm_tmr_update(ACPIREGS *ar, bool enable); void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar); diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h index a1648220ffd..68d9d15f50a 100644 --- a/include/hw/acpi/acpi_dev_interface.h +++ b/include/hw/acpi/acpi_dev_interface.h @@ -3,7 +3,6 @@ #include "qapi/qapi-types-acpi.h" #include "qom/object.h" -#include "hw/boards.h" #include "hw/qdev-core.h" /* These values are part of guest ABI, and can not be changed */ @@ -52,7 +51,5 @@ struct AcpiDeviceIfClass { /* */ void (*ospm_status)(AcpiDeviceIf *adev, ACPIOSTInfoList ***list); void (*send_event)(AcpiDeviceIf *adev, AcpiEventStatusBits ev); - void (*madt_cpu)(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 ff2a310270a..c18f6813424 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -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, const char *name); #endif diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h index 999caaf5106..32654dc274f 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 acf44188868..8f22c71530d 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/generic_event_device.h b/include/hw/acpi/generic_event_device.h index d831bbd8896..d2dac87b4a9 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/ghes.h b/include/hw/acpi/ghes.h index 674f6958e90..578a582203c 100644 --- a/include/hw/acpi/ghes.h +++ b/include/hw/acpi/ghes.h @@ -23,6 +23,7 @@ #define ACPI_GHES_H #include "hw/acpi/bios-linker-loader.h" +#include "qapi/error.h" /* * Values for Hardware Error Notification Type field @@ -59,26 +60,27 @@ enum AcpiGhesNotifyType { enum { ACPI_HEST_SRC_ID_SEA = 0, /* future ids go here */ - ACPI_HEST_SRC_ID_RESERVED, + + ACPI_GHES_ERROR_SOURCE_COUNT }; typedef struct AcpiGhesState { - uint64_t ghes_addr_le; + uint64_t hw_error_le; bool present; /* True if GHES is present at all on this board */ } AcpiGhesState; -void build_ghes_error_table(GArray *hardware_errors, BIOSLinker *linker); -void acpi_build_hest(GArray *table_data, BIOSLinker *linker, +void acpi_build_hest(GArray *table_data, GArray *hardware_errors, + BIOSLinker *linker, const char *oem_id, const char *oem_table_id); void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s, GArray *hardware_errors); -int acpi_ghes_record_errors(uint8_t notify, uint64_t error_physical_addr); +int acpi_ghes_memory_errors(uint16_t source_id, uint64_t error_physical_addr); /** * acpi_ghes_present: Report whether ACPI GHES table is present * * Returns: true if the system has an ACPI GHES table and it is - * safe to call acpi_ghes_record_errors() to record a memory error. + * safe to call acpi_ghes_memory_errors() to record a memory error. */ bool acpi_ghes_present(void); #endif diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h index 2faf7f0caeb..245fe08dc24 100644 --- a/include/hw/acpi/ich9.h +++ b/include/hw/acpi/ich9.h @@ -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 */ @@ -68,6 +69,11 @@ typedef struct ICH9LPCPMRegs { 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" diff --git a/include/hw/acpi/ich9_tco.h b/include/hw/acpi/ich9_tco.h index c4393caee0f..b3c3f69451e 100644 --- a/include/hw/acpi/ich9_tco.h +++ b/include/hw/acpi/ich9_tco.h @@ -10,7 +10,8 @@ #ifndef HW_ACPI_TCO_H #define HW_ACPI_TCO_H -#include "exec/memory.h" +#include "system/memory.h" +#include "migration/vmstate.h" /* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */ #define TCO_TICK_NSEC 600000000LL diff --git a/include/hw/acpi/ich9_timer.h b/include/hw/acpi/ich9_timer.h new file mode 100644 index 00000000000..5112df43850 --- /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 dfe9cf3fde1..38841d7b065 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/pci.h b/include/hw/acpi/pci.h index 467a99461cd..6359d574fd9 100644 --- a/include/hw/acpi/pci.h +++ b/include/hw/acpi/pci.h @@ -40,4 +40,7 @@ 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 ef59810c17f..a97904badaa 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -56,9 +56,9 @@ typedef struct AcpiPciHpState { } AcpiPciHpState; void acpi_pcihp_init(Object *owner, AcpiPciHpState *, PCIBus *root, - MemoryRegion *address_space_io, uint16_t io_base); + MemoryRegion *io, uint16_t io_base); -bool acpi_pcihp_is_hotpluggbale_bus(AcpiPciHpState *s, BusState *bus); +bool acpi_pcihp_is_hotpluggable_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, diff --git a/include/hw/acpi/tpm.h b/include/hw/acpi/tpm.h index 579c45f5baf..9d0fe6f2f96 100644 --- a/include/hw/acpi/tpm.h +++ b/include/hw/acpi/tpm.h @@ -19,7 +19,7 @@ #include "qemu/units.h" #include "hw/registerfields.h" #include "hw/acpi/aml-build.h" -#include "sysemu/tpm.h" +#include "system/tpm.h" #ifdef CONFIG_TPM diff --git a/include/hw/acpi/vmclock.h b/include/hw/acpi/vmclock.h new file mode 100644 index 00000000000..5605605812c --- /dev/null +++ b/include/hw/acpi/vmclock.h @@ -0,0 +1,34 @@ +#ifndef ACPI_VMCLOCK_H +#define ACPI_VMCLOCK_H + +#include "hw/acpi/bios-linker-loader.h" +#include "hw/qdev-core.h" +#include "qemu/uuid.h" +#include "qom/object.h" + +#define TYPE_VMCLOCK "vmclock" + +#define VMCLOCK_ADDR 0xfeffb000 +#define VMCLOCK_SIZE 0x1000 + +OBJECT_DECLARE_SIMPLE_TYPE(VmclockState, VMCLOCK) + +struct vmclock_abi; + +struct VmclockState { + DeviceState parent_obj; + MemoryRegion clk_page; + uint64_t physaddr; + struct vmclock_abi *clk; +}; + +/* returns NULL unless there is exactly one device */ +static inline Object *find_vmclock_dev(void) +{ + return object_resolve_path_type("", TYPE_VMCLOCK, NULL); +} + +void vmclock_build_acpi(VmclockState *vms, GArray *table_data, + BIOSLinker *linker, const char *oem_id); + +#endif diff --git a/include/hw/adc/aspeed_adc.h b/include/hw/adc/aspeed_adc.h index ff1d06ea91d..f502f197ac0 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 beff59c815d..00000000000 --- 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/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h index cd1465c6138..445ba1be212 100644 --- a/include/hw/arm/allwinner-a10.h +++ b/include/hw/arm/allwinner-a10.h @@ -1,20 +1,20 @@ #ifndef HW_ARM_ALLWINNER_A10_H #define HW_ARM_ALLWINNER_A10_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 "system/block-backend.h" #include "target/arm/cpu.h" #include "qom/object.h" @@ -41,6 +41,7 @@ struct AwA10State { AllwinnerAHCIState sata; AwSdHostState mmc0; AWI2CState i2c0; + AWA10SPIState spi0; AwRtcState rtc; AwWdtState wdt; MemoryRegion sram_a; diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h index f15d6d7cc7d..db897c86f0b 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" @@ -50,7 +49,7 @@ #include "hw/i2c/allwinner-i2c.h" #include "hw/watchdog/allwinner-wdt.h" #include "target/arm/cpu.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" /** * Allwinner H3 device list diff --git a/include/hw/arm/allwinner-r40.h b/include/hw/arm/allwinner-r40.h index 72710d3edce..f8a0e94251e 100644 --- a/include/hw/arm/allwinner-r40.h +++ b/include/hw/arm/allwinner-r40.h @@ -21,8 +21,8 @@ #define HW_ARM_ALLWINNER_R40_H #include "qom/object.h" -#include "hw/arm/boot.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" @@ -31,8 +31,11 @@ #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" +#include "system/block-backend.h" enum { AW_R40_DEV_SRAM_A1, @@ -45,8 +48,14 @@ enum { 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, @@ -89,6 +98,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AwR40State, AW_R40) * 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 { @@ -106,7 +116,11 @@ struct AwR40State { 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; diff --git a/include/hw/arm/armv7m.h b/include/hw/arm/armv7m.h index e2cebbd15c0..5c057ab2ec9 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.) diff --git a/include/hw/arm/aspeed.h b/include/hw/arm/aspeed.h index cbeacb214ca..973277bea65 100644 --- a/include/hw/arm/aspeed.h +++ b/include/hw/arm/aspeed.h @@ -39,6 +39,8 @@ struct AspeedMachineClass { uint32_t macs_mask; void (*i2c_init)(AspeedMachineState *bmc); uint32_t uart_default; + bool sdhci_wp_inverted; + bool vbootrom; }; diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 8adff700728..217ef0eafd6 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,36 +38,35 @@ #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_SPIS_NUM 3 +#define ASPEED_EHCIS_NUM 4 +#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; MemoryRegion spi_boot_container; MemoryRegion spi_boot; - AspeedVICState vic; + MemoryRegion vbootrom; + AddressSpace dram_as; AspeedRtcState rtc; AspeedTimerCtrlState timerctrl; AspeedI2CState i2c; AspeedI3CState i3c; AspeedSCUState scu; + AspeedSCUState scuio; AspeedHACEState hace; AspeedXDMAState xdma; AspeedADCState adc; @@ -73,6 +74,8 @@ 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; @@ -88,6 +91,8 @@ struct AspeedSoCState { SerialMM uart[ASPEED_UARTS_NUM]; Clock *sysclk; UnimplementedDeviceState iomem; + UnimplementedDeviceState iomem0; + UnimplementedDeviceState iomem1; UnimplementedDeviceState video; UnimplementedDeviceState emmc_boot_controller; UnimplementedDeviceState dpmcu; @@ -95,17 +100,84 @@ struct AspeedSoCState { UnimplementedDeviceState espi; UnimplementedDeviceState udc; UnimplementedDeviceState sgpiom; + UnimplementedDeviceState ltpi; 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[2]; + GICv3State gic; + MemoryRegion dram_empty; +}; + +#define TYPE_ASPEED27X0_SOC "aspeed27x0-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0SoCState, ASPEED27X0_SOC) + +struct Aspeed10x0SoCState { + AspeedSoCState parent; + + ARMv7MState armv7m; +}; + +struct Aspeed27x0SSPSoCState { + AspeedSoCState parent; + AspeedINTCState intc[2]; + UnimplementedDeviceState ipc[2]; + UnimplementedDeviceState scuio; + + ARMv7MState armv7m; +}; + +#define TYPE_ASPEED27X0SSP_SOC "aspeed27x0ssp-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0SSPSoCState, ASPEED27X0SSP_SOC) + +struct Aspeed27x0TSPSoCState { + AspeedSoCState parent; + AspeedINTCState intc[2]; + UnimplementedDeviceState ipc[2]; + UnimplementedDeviceState scuio; + + ARMv7MState armv7m; +}; + +#define TYPE_ASPEED27X0TSP_SOC "aspeed27x0tsp-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0TSPSoCState, ASPEED27X0TSP_SOC) + +#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; @@ -114,16 +186,24 @@ struct AspeedSoCClass { 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_VBOOTROM, ASPEED_DEV_SPI_BOOT, ASPEED_DEV_IOMEM, + ASPEED_DEV_IOMEM0, + ASPEED_DEV_IOMEM1, + ASPEED_DEV_LTPI, + ASPEED_DEV_UART0, ASPEED_DEV_UART1, ASPEED_DEV_UART2, ASPEED_DEV_UART3, @@ -139,11 +219,16 @@ 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_EHCI3, + ASPEED_DEV_EHCI4, ASPEED_DEV_VIC, + ASPEED_DEV_INTC, + ASPEED_DEV_INTCIO, ASPEED_DEV_SDMC, ASPEED_DEV_SCU, ASPEED_DEV_ADC, @@ -191,10 +276,17 @@ enum { 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, + ASPEED_DEV_IPC0, + ASPEED_DEV_IPC1, }; -#define ASPEED_SOC_SPI_BOOT_ADDR 0x0 - qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev); bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp); void aspeed_soc_uart_set_chr(AspeedSoCState *s, int dev, Chardev *chr); @@ -206,4 +298,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 d724a2fc28a..1eeaeec9e0e 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 >*/ @@ -59,16 +65,14 @@ struct BCM2835PeripheralState { 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 6f90cabfa3a..918fb3bf142 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 00000000000..e53c7bedf92 --- /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 00000000000..7ee1bd066fa --- /dev/null +++ b/include/hw/arm/bcm2838_peripherals.h @@ -0,0 +1,84 @@ +/* + * BCM2838 peripherals emulation + * + * Copyright (C) 2022 Ovchinnikov Vitalii + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef BCM2838_PERIPHERALS_H +#define BCM2838_PERIPHERALS_H + +#include "hw/arm/bcm2835_peripherals.h" +#include "hw/sd/sdhci.h" +#include "hw/gpio/bcm2838_gpio.h" + +/* SPI */ +#define GIC_SPI_INTERRUPT_MBOX 33 +#define GIC_SPI_INTERRUPT_MPHI 40 +#define GIC_SPI_INTERRUPT_DWC2 73 +#define GIC_SPI_INTERRUPT_DMA_0 80 +#define GIC_SPI_INTERRUPT_DMA_6 86 +#define GIC_SPI_INTERRUPT_DMA_7_8 87 +#define GIC_SPI_INTERRUPT_DMA_9_10 88 +#define GIC_SPI_INTERRUPT_AUX_UART1 93 +#define GIC_SPI_INTERRUPT_SDHOST 120 +#define GIC_SPI_INTERRUPT_UART0 121 +#define GIC_SPI_INTERRUPT_RNG200 125 +#define GIC_SPI_INTERRUPT_EMMC_EMMC2 126 +#define GIC_SPI_INTERRUPT_PCI_INT_A 143 +#define GIC_SPI_INTERRUPT_GENET_A 157 +#define GIC_SPI_INTERRUPT_GENET_B 158 + + +/* GPU (legacy) DMA interrupts */ +#define GPU_INTERRUPT_DMA0 16 +#define GPU_INTERRUPT_DMA1 17 +#define GPU_INTERRUPT_DMA2 18 +#define GPU_INTERRUPT_DMA3 19 +#define GPU_INTERRUPT_DMA4 20 +#define GPU_INTERRUPT_DMA5 21 +#define GPU_INTERRUPT_DMA6 22 +#define GPU_INTERRUPT_DMA7_8 23 +#define GPU_INTERRUPT_DMA9_10 24 +#define GPU_INTERRUPT_DMA11 25 +#define GPU_INTERRUPT_DMA12 26 +#define GPU_INTERRUPT_DMA13 27 +#define GPU_INTERRUPT_DMA14 28 +#define GPU_INTERRUPT_DMA15 31 + +#define BCM2838_MPHI_OFFSET 0xb200 +#define BCM2838_MPHI_SIZE 0x200 + +#define TYPE_BCM2838_PERIPHERALS "bcm2838-peripherals" +OBJECT_DECLARE_TYPE(BCM2838PeripheralState, BCM2838PeripheralClass, + BCM2838_PERIPHERALS) + +struct BCM2838PeripheralState { + /*< private >*/ + BCMSocPeripheralBaseState parent_obj; + + /*< public >*/ + MemoryRegion peri_low_mr; + MemoryRegion peri_low_mr_alias; + MemoryRegion mphi_mr_alias; + + SDHCIState emmc2; + BCM2838GpioState gpio; + + OrIRQState mmc_irq_orgate; + OrIRQState dma_7_8_irq_orgate; + OrIRQState dma_9_10_irq_orgate; + + UnimplementedDeviceState asb; + UnimplementedDeviceState clkisp; +}; + +struct BCM2838PeripheralClass { + /*< private >*/ + BCMSocPeripheralBaseClass parent_class; + /*< public >*/ + uint64_t peri_low_size; /* Peripheral lower range size */ +}; + +#endif /* BCM2838_PERIPHERALS_H */ diff --git a/include/hw/arm/boot.h b/include/hw/arm/boot.h index 80c492d7421..a2e22bda8a5 100644 --- a/include/hw/arm/boot.h +++ b/include/hw/arm/boot.h @@ -132,6 +132,9 @@ struct arm_boot_info { bool secure_board_setup; arm_endianness endianness; + + /* CPU having load the kernel and that should be the first to boot. */ + ARMCPU *primary_cpu; }; /** @@ -160,6 +163,7 @@ AddressSpace *arm_boot_address_space(ARMCPU *cpu, * @binfo: struct describing the boot environment * @addr_limit: upper limit of the available memory area at @addr * @as: address space to load image to + * @cpu: ARM CPU object * * Load a device tree supplied by the machine or by the user with the * '-dtb' command line option, and put it at offset @addr in target @@ -176,7 +180,8 @@ AddressSpace *arm_boot_address_space(ARMCPU *cpu, * Note: Must not be called unless have_dtb(binfo) is true. */ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, - hwaddr addr_limit, AddressSpace *as, MachineState *ms); + hwaddr addr_limit, AddressSpace *as, MachineState *ms, + ARMCPU *cpu); /* Write a secure board setup routine with a dummy handler for SMCs */ void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu, diff --git a/include/hw/arm/bsa.h b/include/hw/arm/bsa.h new file mode 100644 index 00000000000..13ed2d2ac19 --- /dev/null +++ b/include/hw/arm/bsa.h @@ -0,0 +1,37 @@ +/* + * 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 ARCH_TIMER_S_EL2_VIRT_IRQ 19 +#define ARCH_TIMER_S_EL2_IRQ 20 +#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 68db19f0cb7..d33fe385865 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 diff --git a/include/hw/arm/fsl-imx25.h b/include/hw/arm/fsl-imx25.h index 1b1086e9458..b68d4334a0c 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" @@ -30,7 +29,7 @@ #include "hw/sd/sdhci.h" #include "hw/usb/chipidea.h" #include "hw/watchdog/wdt_imx2.h" -#include "exec/memory.h" +#include "system/memory.h" #include "target/arm/cpu.h" #include "qom/object.h" diff --git a/include/hw/arm/fsl-imx31.h b/include/hw/arm/fsl-imx31.h index c116a73e0b3..41232a22373 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" @@ -26,7 +25,7 @@ #include "hw/i2c/imx_i2c.h" #include "hw/gpio/imx_gpio.h" #include "hw/watchdog/wdt_imx2.h" -#include "exec/memory.h" +#include "system/memory.h" #include "target/arm/cpu.h" #include "qom/object.h" diff --git a/include/hw/arm/fsl-imx6.h b/include/hw/arm/fsl-imx6.h index 5b4d48da084..124bbd478fd 100644 --- a/include/hw/arm/fsl-imx6.h +++ b/include/hw/arm/fsl-imx6.h @@ -17,7 +17,6 @@ #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" @@ -33,7 +32,9 @@ #include "hw/net/imx_fec.h" #include "hw/usb/chipidea.h" #include "hw/usb/imx-usb-phy.h" -#include "exec/memory.h" +#include "hw/pci-host/designware.h" +#include "hw/or-irq.h" +#include "system/memory.h" #include "cpu.h" #include "qom/object.h" @@ -56,27 +57,29 @@ struct FslIMX6State { DeviceState parent_obj; /*< public >*/ - 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; - 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; + OrIRQState pcie4_msi_irq; + MemoryRegion rom; + MemoryRegion caam; + MemoryRegion ocram; + MemoryRegion ocram_alias; + uint32_t phy_num; }; @@ -456,7 +459,7 @@ struct FslIMX6State { #define FSL_IMX6_PCIE1_IRQ 120 #define FSL_IMX6_PCIE2_IRQ 121 #define FSL_IMX6_PCIE3_IRQ 122 -#define FSL_IMX6_PCIE4_IRQ 123 +#define FSL_IMX6_PCIE4_MSI_IRQ 123 #define FSL_IMX6_DCIC1_IRQ 124 #define FSL_IMX6_DCIC2_IRQ 125 #define FSL_IMX6_MLB150_HIGH_IRQ 126 diff --git a/include/hw/arm/fsl-imx6ul.h b/include/hw/arm/fsl-imx6ul.h index 63012628ff0..4e3209b25b2 100644 --- a/include/hw/arm/fsl-imx6ul.h +++ b/include/hw/arm/fsl-imx6ul.h @@ -17,7 +17,6 @@ #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" @@ -34,7 +33,7 @@ #include "hw/net/imx_fec.h" #include "hw/usb/chipidea.h" #include "hw/usb/imx-usb-phy.h" -#include "exec/memory.h" +#include "system/memory.h" #include "cpu.h" #include "qom/object.h" #include "qemu/units.h" @@ -183,6 +182,8 @@ enum FslIMX6ULMemoryMap { 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, diff --git a/include/hw/arm/fsl-imx7.h b/include/hw/arm/fsl-imx7.h index 2cbfc6b2b2c..aa7818c4999 100644 --- a/include/hw/arm/fsl-imx7.h +++ b/include/hw/arm/fsl-imx7.h @@ -19,7 +19,6 @@ #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" @@ -37,6 +36,7 @@ #include "hw/net/imx_fec.h" #include "hw/pci-host/designware.h" #include "hw/usb/chipidea.h" +#include "hw/or-irq.h" #include "cpu.h" #include "qom/object.h" #include "qemu/units.h" @@ -86,6 +86,7 @@ struct FslIMX7State { IMX7GPRState gpr; ChipideaState usb[FSL_IMX7_NUM_USBS]; DesignwarePCIEHost pcie; + OrIRQState pcie4_msi_irq; MemoryRegion rom; MemoryRegion caam; MemoryRegion ocram; @@ -429,7 +430,7 @@ enum FslIMX7IRQs { FSL_IMX7_PCI_INTA_IRQ = 125, FSL_IMX7_PCI_INTB_IRQ = 124, FSL_IMX7_PCI_INTC_IRQ = 123, - FSL_IMX7_PCI_INTD_IRQ = 122, + FSL_IMX7_PCI_INTD_MSI_IRQ = 122, FSL_IMX7_UART7_IRQ = 126, diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h new file mode 100644 index 00000000000..d016f7d3371 --- /dev/null +++ b/include/hw/arm/fsl-imx8mp.h @@ -0,0 +1,284 @@ +/* + * i.MX 8M Plus SoC Definitions + * + * Copyright (c) 2024, Bernhard Beschow + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FSL_IMX8MP_H +#define FSL_IMX8MP_H + +#include "cpu.h" +#include "hw/char/imx_serial.h" +#include "hw/gpio/imx_gpio.h" +#include "hw/i2c/imx_i2c.h" +#include "hw/intc/arm_gicv3_common.h" +#include "hw/misc/imx7_snvs.h" +#include "hw/misc/imx8mp_analog.h" +#include "hw/misc/imx8mp_ccm.h" +#include "hw/net/imx_fec.h" +#include "hw/or-irq.h" +#include "hw/pci-host/designware.h" +#include "hw/pci-host/fsl_imx8m_phy.h" +#include "hw/sd/sdhci.h" +#include "hw/ssi/imx_spi.h" +#include "hw/timer/imx_gpt.h" +#include "hw/usb/hcd-dwc3.h" +#include "hw/watchdog/wdt_imx2.h" +#include "hw/sysbus.h" +#include "qom/object.h" +#include "qemu/units.h" + +#define TYPE_FSL_IMX8MP "fsl-imx8mp" +OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mpState, FSL_IMX8MP) + +#define FSL_IMX8MP_RAM_START 0x40000000 +#define FSL_IMX8MP_RAM_SIZE_MAX (8 * GiB) + +enum FslImx8mpConfiguration { + FSL_IMX8MP_NUM_CPUS = 4, + FSL_IMX8MP_NUM_ECSPIS = 3, + FSL_IMX8MP_NUM_GPIOS = 5, + FSL_IMX8MP_NUM_GPTS = 6, + FSL_IMX8MP_NUM_I2CS = 6, + FSL_IMX8MP_NUM_IRQS = 160, + FSL_IMX8MP_NUM_UARTS = 4, + FSL_IMX8MP_NUM_USBS = 2, + FSL_IMX8MP_NUM_USDHCS = 3, + FSL_IMX8MP_NUM_WDTS = 3, +}; + +struct FslImx8mpState { + SysBusDevice parent_obj; + + ARMCPU cpu[FSL_IMX8MP_NUM_CPUS]; + GICv3State gic; + IMXGPTState gpt[FSL_IMX8MP_NUM_GPTS]; + IMXGPIOState gpio[FSL_IMX8MP_NUM_GPIOS]; + IMX8MPCCMState ccm; + IMX8MPAnalogState analog; + IMX7SNVSState snvs; + IMXSPIState spi[FSL_IMX8MP_NUM_ECSPIS]; + IMXI2CState i2c[FSL_IMX8MP_NUM_I2CS]; + IMXSerialState uart[FSL_IMX8MP_NUM_UARTS]; + IMXFECState enet; + SDHCIState usdhc[FSL_IMX8MP_NUM_USDHCS]; + IMX2WdtState wdt[FSL_IMX8MP_NUM_WDTS]; + USBDWC3 usb[FSL_IMX8MP_NUM_USBS]; + DesignwarePCIEHost pcie; + FslImx8mPciePhyState pcie_phy; + OrIRQState gpt5_gpt6_irq; + MemoryRegion ocram; + + uint32_t phy_num; + bool phy_connected; +}; + +enum FslImx8mpMemoryRegions { + FSL_IMX8MP_A53_DAP, + FSL_IMX8MP_AIPS1_CONFIGURATION, + FSL_IMX8MP_AIPS2_CONFIGURATION, + FSL_IMX8MP_AIPS3_CONFIGURATION, + FSL_IMX8MP_AIPS4_CONFIGURATION, + FSL_IMX8MP_AIPS5_CONFIGURATION, + FSL_IMX8MP_ANA_OSC, + FSL_IMX8MP_ANA_PLL, + FSL_IMX8MP_ANA_TSENSOR, + FSL_IMX8MP_APBH_DMA, + FSL_IMX8MP_ASRC, + FSL_IMX8MP_AUDIO_BLK_CTRL, + FSL_IMX8MP_AUDIO_DSP, + FSL_IMX8MP_AUDIO_XCVR_RX, + FSL_IMX8MP_AUD_IRQ_STEER, + FSL_IMX8MP_BOOT_ROM, + FSL_IMX8MP_BOOT_ROM_PROTECTED, + FSL_IMX8MP_CAAM, + FSL_IMX8MP_CAAM_MEM, + FSL_IMX8MP_CCM, + FSL_IMX8MP_CSU, + FSL_IMX8MP_DDR_BLK_CTRL, + FSL_IMX8MP_DDR_CTL, + FSL_IMX8MP_DDR_PERF_MON, + FSL_IMX8MP_DDR_PHY, + FSL_IMX8MP_DDR_PHY_BROADCAST, + FSL_IMX8MP_ECSPI1, + FSL_IMX8MP_ECSPI2, + FSL_IMX8MP_ECSPI3, + FSL_IMX8MP_EDMA_CHANNELS, + FSL_IMX8MP_EDMA_MANAGEMENT_PAGE, + FSL_IMX8MP_ENET1, + FSL_IMX8MP_ENET2_TSN, + FSL_IMX8MP_FLEXCAN1, + FSL_IMX8MP_FLEXCAN2, + FSL_IMX8MP_GIC_DIST, + FSL_IMX8MP_GIC_REDIST, + FSL_IMX8MP_GPC, + FSL_IMX8MP_GPIO1, + FSL_IMX8MP_GPIO2, + FSL_IMX8MP_GPIO3, + FSL_IMX8MP_GPIO4, + FSL_IMX8MP_GPIO5, + FSL_IMX8MP_GPT1, + FSL_IMX8MP_GPT2, + FSL_IMX8MP_GPT3, + FSL_IMX8MP_GPT4, + FSL_IMX8MP_GPT5, + FSL_IMX8MP_GPT6, + FSL_IMX8MP_GPU2D, + FSL_IMX8MP_GPU3D, + FSL_IMX8MP_HDMI_TX, + FSL_IMX8MP_HDMI_TX_AUDLNK_MSTR, + FSL_IMX8MP_HSIO_BLK_CTL, + FSL_IMX8MP_I2C1, + FSL_IMX8MP_I2C2, + FSL_IMX8MP_I2C3, + FSL_IMX8MP_I2C4, + FSL_IMX8MP_I2C5, + FSL_IMX8MP_I2C6, + FSL_IMX8MP_INTERCONNECT, + FSL_IMX8MP_IOMUXC, + FSL_IMX8MP_IOMUXC_GPR, + FSL_IMX8MP_IPS_DEWARP, + FSL_IMX8MP_ISI, + FSL_IMX8MP_ISP1, + FSL_IMX8MP_ISP2, + FSL_IMX8MP_LCDIF1, + FSL_IMX8MP_LCDIF2, + FSL_IMX8MP_MEDIA_BLK_CTL, + FSL_IMX8MP_MIPI_CSI1, + FSL_IMX8MP_MIPI_CSI2, + FSL_IMX8MP_MIPI_DSI1, + FSL_IMX8MP_MU_1_A, + FSL_IMX8MP_MU_1_B, + FSL_IMX8MP_MU_2_A, + FSL_IMX8MP_MU_2_B, + FSL_IMX8MP_MU_3_A, + FSL_IMX8MP_MU_3_B, + FSL_IMX8MP_NPU, + FSL_IMX8MP_OCOTP_CTRL, + FSL_IMX8MP_OCRAM, + FSL_IMX8MP_OCRAM_S, + FSL_IMX8MP_PCIE1, + FSL_IMX8MP_PCIE1_MEM, + FSL_IMX8MP_PCIE_PHY1, + FSL_IMX8MP_PDM, + FSL_IMX8MP_PERFMON1, + FSL_IMX8MP_PERFMON2, + FSL_IMX8MP_PWM1, + FSL_IMX8MP_PWM2, + FSL_IMX8MP_PWM3, + FSL_IMX8MP_PWM4, + FSL_IMX8MP_QOSC, + FSL_IMX8MP_QSPI, + FSL_IMX8MP_QSPI1_RX_BUFFER, + FSL_IMX8MP_QSPI1_TX_BUFFER, + FSL_IMX8MP_QSPI_MEM, + FSL_IMX8MP_RAM, + FSL_IMX8MP_RDC, + FSL_IMX8MP_SAI1, + FSL_IMX8MP_SAI2, + FSL_IMX8MP_SAI3, + FSL_IMX8MP_SAI5, + FSL_IMX8MP_SAI6, + FSL_IMX8MP_SAI7, + FSL_IMX8MP_SDMA1, + FSL_IMX8MP_SDMA2, + FSL_IMX8MP_SDMA3, + FSL_IMX8MP_SEMAPHORE1, + FSL_IMX8MP_SEMAPHORE2, + FSL_IMX8MP_SEMAPHORE_HS, + FSL_IMX8MP_SNVS_HP, + FSL_IMX8MP_SPBA1, + FSL_IMX8MP_SPBA2, + FSL_IMX8MP_SRC, + FSL_IMX8MP_SYSCNT_CMP, + FSL_IMX8MP_SYSCNT_CTRL, + FSL_IMX8MP_SYSCNT_RD, + FSL_IMX8MP_TCM_DTCM, + FSL_IMX8MP_TCM_ITCM, + FSL_IMX8MP_TZASC, + FSL_IMX8MP_UART1, + FSL_IMX8MP_UART2, + FSL_IMX8MP_UART3, + FSL_IMX8MP_UART4, + FSL_IMX8MP_USB1, + FSL_IMX8MP_USB2, + FSL_IMX8MP_USB1_DEV, + FSL_IMX8MP_USB2_DEV, + FSL_IMX8MP_USB1_OTG, + FSL_IMX8MP_USB2_OTG, + FSL_IMX8MP_USB1_GLUE, + FSL_IMX8MP_USB2_GLUE, + FSL_IMX8MP_USDHC1, + FSL_IMX8MP_USDHC2, + FSL_IMX8MP_USDHC3, + FSL_IMX8MP_VPU, + FSL_IMX8MP_VPU_BLK_CTRL, + FSL_IMX8MP_VPU_G1_DECODER, + FSL_IMX8MP_VPU_G2_DECODER, + FSL_IMX8MP_VPU_VC8000E_ENCODER, + FSL_IMX8MP_WDOG1, + FSL_IMX8MP_WDOG2, + FSL_IMX8MP_WDOG3, +}; + +enum FslImx8mpIrqs { + FSL_IMX8MP_USDHC1_IRQ = 22, + FSL_IMX8MP_USDHC2_IRQ = 23, + FSL_IMX8MP_USDHC3_IRQ = 24, + + FSL_IMX8MP_UART1_IRQ = 26, + FSL_IMX8MP_UART2_IRQ = 27, + FSL_IMX8MP_UART3_IRQ = 28, + FSL_IMX8MP_UART4_IRQ = 29, + FSL_IMX8MP_UART5_IRQ = 30, + FSL_IMX8MP_UART6_IRQ = 16, + + FSL_IMX8MP_ECSPI1_IRQ = 31, + FSL_IMX8MP_ECSPI2_IRQ = 32, + FSL_IMX8MP_ECSPI3_IRQ = 33, + + FSL_IMX8MP_I2C1_IRQ = 35, + FSL_IMX8MP_I2C2_IRQ = 36, + FSL_IMX8MP_I2C3_IRQ = 37, + FSL_IMX8MP_I2C4_IRQ = 38, + + FSL_IMX8MP_USB1_IRQ = 40, + FSL_IMX8MP_USB2_IRQ = 41, + + FSL_IMX8MP_GPT1_IRQ = 55, + FSL_IMX8MP_GPT2_IRQ = 54, + FSL_IMX8MP_GPT3_IRQ = 53, + FSL_IMX8MP_GPT4_IRQ = 52, + FSL_IMX8MP_GPT5_GPT6_IRQ = 51, + + FSL_IMX8MP_GPIO1_LOW_IRQ = 64, + FSL_IMX8MP_GPIO1_HIGH_IRQ = 65, + FSL_IMX8MP_GPIO2_LOW_IRQ = 66, + FSL_IMX8MP_GPIO2_HIGH_IRQ = 67, + FSL_IMX8MP_GPIO3_LOW_IRQ = 68, + FSL_IMX8MP_GPIO3_HIGH_IRQ = 69, + FSL_IMX8MP_GPIO4_LOW_IRQ = 70, + FSL_IMX8MP_GPIO4_HIGH_IRQ = 71, + FSL_IMX8MP_GPIO5_LOW_IRQ = 72, + FSL_IMX8MP_GPIO5_HIGH_IRQ = 73, + + FSL_IMX8MP_I2C5_IRQ = 76, + FSL_IMX8MP_I2C6_IRQ = 77, + + FSL_IMX8MP_WDOG1_IRQ = 78, + FSL_IMX8MP_WDOG2_IRQ = 79, + FSL_IMX8MP_WDOG3_IRQ = 10, + + FSL_IMX8MP_ENET1_MAC_IRQ = 118, + FSL_IMX6_ENET1_MAC_1588_IRQ = 121, + + FSL_IMX8MP_PCI_INTA_IRQ = 126, + FSL_IMX8MP_PCI_INTB_IRQ = 125, + FSL_IMX8MP_PCI_INTC_IRQ = 124, + FSL_IMX8MP_PCI_INTD_IRQ = 123, + FSL_IMX8MP_PCI_MSI_IRQ = 140, +}; + +#endif /* FSL_IMX8MP_H */ diff --git a/include/hw/arm/msf2-soc.h b/include/hw/arm/msf2-soc.h index ce417a6266a..9300664e8ea 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 72c77220964..56536565b74 100644 --- a/include/hw/arm/npcm7xx.h +++ b/include/hw/arm/npcm7xx.h @@ -23,12 +23,13 @@ #include "hw/gpio/npcm7xx_gpio.h" #include "hw/i2c/npcm7xx_smbus.h" #include "hw/mem/npcm7xx_mc.h" -#include "hw/misc/npcm7xx_clk.h" -#include "hw/misc/npcm7xx_gcr.h" +#include "hw/misc/npcm_clk.h" +#include "hw/misc/npcm_gcr.h" #include "hw/misc/npcm7xx_mft.h" #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" @@ -88,8 +89,8 @@ struct NPCM7xxState { MemoryRegion ram3; MemoryRegion *dram; - NPCM7xxGCRState gcr; - NPCM7xxCLKState clk; + NPCMGCRState gcr; + NPCMCLKState clk; NPCM7xxTimerCtrlState tim[3]; NPCM7xxADCState adc; NPCM7xxPWMState pwm[NPCM7XX_NR_PWM_MODULES]; @@ -104,6 +105,7 @@ struct NPCM7xxState { OHCISysBusState ohci; NPCM7xxFIUState fiu[2]; NPCM7xxEMCState emc[2]; + NPCMGMACState gmac[2]; NPCM7xxSDHCIState mmc; NPCMPSPIState pspi[2]; }; diff --git a/include/hw/arm/npcm8xx.h b/include/hw/arm/npcm8xx.h new file mode 100644 index 00000000000..a8377db4905 --- /dev/null +++ b/include/hw/arm/npcm8xx.h @@ -0,0 +1,132 @@ +/* + * Nuvoton NPCM8xx SoC family. + * + * Copyright 2022 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 NPCM8XX_H +#define NPCM8XX_H + +#include "hw/adc/npcm7xx_adc.h" +#include "hw/core/split-irq.h" +#include "hw/cpu/cluster.h" +#include "hw/gpio/npcm7xx_gpio.h" +#include "hw/i2c/npcm7xx_smbus.h" +#include "hw/intc/arm_gic_common.h" +#include "hw/mem/npcm7xx_mc.h" +#include "hw/misc/npcm_clk.h" +#include "hw/misc/npcm_gcr.h" +#include "hw/misc/npcm7xx_mft.h" +#include "hw/misc/npcm7xx_pwm.h" +#include "hw/misc/npcm7xx_rng.h" +#include "hw/net/npcm_gmac.h" +#include "hw/net/npcm_pcs.h" +#include "hw/nvram/npcm7xx_otp.h" +#include "hw/sd/npcm7xx_sdhci.h" +#include "hw/timer/npcm7xx_timer.h" +#include "hw/ssi/npcm7xx_fiu.h" +#include "hw/usb/hcd-ehci.h" +#include "hw/usb/hcd-ohci.h" +#include "target/arm/cpu.h" +#include "hw/ssi/npcm_pspi.h" + +#define NPCM8XX_MAX_NUM_CPUS (4) + +/* The first half of the address space is reserved for DDR4 DRAM. */ +#define NPCM8XX_DRAM_BA (0x00000000) +#define NPCM8XX_DRAM_SZ (2 * GiB) + +/* Magic addresses for setting up direct kernel booting and SMP boot stubs. */ +#define NPCM8XX_LOADER_START (0x00000000) /* Start of SDRAM */ +#define NPCM8XX_SMP_LOADER_START (0xffff0000) /* Boot ROM */ +#define NPCM8XX_SMP_BOOTREG_ADDR (0xf080013c) /* GCR.SCRPAD */ +#define NPCM8XX_BOARD_SETUP_ADDR (0xffff1000) /* Boot ROM */ + +#define NPCM8XX_NR_PWM_MODULES 3 + +struct NPCM8xxMachine { + MachineState parent_obj; + + /* + * PWM fan splitter. each splitter connects to one PWM output and + * multiple MFT inputs. + */ + SplitIRQ fan_splitter[NPCM8XX_NR_PWM_MODULES * + NPCM7XX_PWM_PER_MODULE]; +}; + + +struct NPCM8xxMachineClass { + MachineClass parent_class; + + const char *soc_type; +}; + +#define TYPE_NPCM8XX_MACHINE MACHINE_TYPE_NAME("npcm8xx") +OBJECT_DECLARE_TYPE(NPCM8xxMachine, NPCM8xxMachineClass, NPCM8XX_MACHINE) + +struct NPCM8xxState { + DeviceState parent_obj; + + ARMCPU cpu[NPCM8XX_MAX_NUM_CPUS]; + CPUClusterState cpu_cluster; + GICState gic; + + MemoryRegion sram; + MemoryRegion irom; + MemoryRegion ram3; + MemoryRegion *dram; + + NPCMGCRState gcr; + NPCMCLKState clk; + NPCM7xxTimerCtrlState tim[3]; + NPCM7xxADCState adc; + NPCM7xxPWMState pwm[NPCM8XX_NR_PWM_MODULES]; + NPCM7xxMFTState mft[8]; + NPCM7xxOTPState fuse_array; + NPCM7xxMCState mc; + NPCM7xxRNGState rng; + NPCM7xxGPIOState gpio[8]; + NPCM7xxSMBusState smbus[27]; + EHCISysBusState ehci[2]; + OHCISysBusState ohci[2]; + NPCM7xxFIUState fiu[3]; + NPCMGMACState gmac[4]; + NPCMPCSState pcs; + NPCM7xxSDHCIState mmc; + NPCMPSPIState pspi; +}; + +struct NPCM8xxClass { + DeviceClass parent_class; + + /* Bitmask of modules that are permanently disabled on this chip. */ + uint32_t disabled_modules; + /* Number of CPU cores enabled in this SoC class. */ + uint32_t num_cpus; +}; + +#define TYPE_NPCM8XX "npcm8xx" +OBJECT_DECLARE_TYPE(NPCM8xxState, NPCM8xxClass, NPCM8XX) + +/** + * npcm8xx_load_kernel - Loads memory with everything needed to boot + * @machine - The machine containing the SoC to be booted. + * @soc - The SoC containing the CPU to be booted. + * + * This will set up the ARM boot info structure for the specific NPCM8xx + * derivative and call arm_load_kernel() to set up loading of the kernel, etc. + * into memory, if requested by the user. + */ +void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc); + +#endif /* NPCM8XX_H */ diff --git a/include/hw/arm/nrf51_soc.h b/include/hw/arm/nrf51_soc.h index e52a56e75e0..f88ab1b7d3e 100644 --- a/include/hw/arm/nrf51_soc.h +++ b/include/hw/arm/nrf51_soc.h @@ -30,7 +30,7 @@ struct NRF51State { SysBusDevice parent_obj; /*< public >*/ - ARMv7MState cpu; + ARMv7MState armv7m; NRF51UARTState uart; NRF51RNGState rng; diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h index 067e9419f7e..bdb2e887e47 100644 --- a/include/hw/arm/omap.h +++ b/include/hw/arm/omap.h @@ -20,39 +20,29 @@ #ifndef HW_ARM_OMAP_H #define HW_ARM_OMAP_H -#include "exec/memory.h" -#include "hw/input/tsc2xxx.h" +#include "system/memory.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 -# define OMAP_CS3_BASE 0x0c000000 -# 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 -# define OMAP_CS3_SIZE 0x04000000 +#define OMAP_EMIFS_BASE 0x00000000 +#define OMAP_CS0_BASE 0x00000000 +#define OMAP_CS1_BASE 0x04000000 +#define OMAP_CS2_BASE 0x08000000 +#define OMAP_CS3_BASE 0x0c000000 +#define OMAP_EMIFF_BASE 0x10000000 +#define OMAP_IMIF_BASE 0x20000000 +#define OMAP_LOCALBUS_BASE 0x30000000 +#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 OMAP_CS0_SIZE 0x04000000 +#define OMAP_CS1_SIZE 0x04000000 +#define OMAP_CS2_SIZE 0x04000000 +#define OMAP_CS3_SIZE 0x04000000 /* omap_clk.c */ struct omap_mpu_state_s; @@ -69,7 +59,7 @@ int64_t omap_clk_getrate(omap_clk clk); void omap_clk_reparent(omap_clk clk, omap_clk parent); /* omap_intc.c */ -#define TYPE_OMAP_INTC "common-omap-intc" +#define TYPE_OMAP_INTC "omap-intc" typedef struct OMAPIntcState OMAPIntcState; DECLARE_INSTANCE_CHECKER(OMAPIntcState, OMAP_INTC, TYPE_OMAP_INTC) @@ -106,385 +96,241 @@ typedef struct Omap1GpioState Omap1GpioState; DECLARE_INSTANCE_CHECKER(Omap1GpioState, OMAP1_GPIO, TYPE_OMAP1_GPIO) -#define TYPE_OMAP2_GPIO "omap2-gpio" -typedef struct Omap2GpioState Omap2GpioState; -DECLARE_INSTANCE_CHECKER(Omap2GpioState, OMAP2_GPIO, - TYPE_OMAP2_GPIO) - /* TODO: clock framework (see above) */ void omap_gpio_set_clk(Omap1GpioState *gpio, omap_clk clk); -void omap2_gpio_set_iclk(Omap2GpioState *gpio, omap_clk clk); -void omap2_gpio_set_fclk(Omap2GpioState *gpio, uint8_t i, omap_clk clk); - -/* OMAP2 l4 Interconnect */ -struct omap_l4_s; -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); - /* * Common IRQ numbers for level 1 interrupt handler * See /usr/include/asm-arm/arch-omap/irqs.h in Linux. */ -# define OMAP_INT_CAMERA 1 -# define OMAP_INT_FIQ 3 -# define OMAP_INT_RTDX 6 -# define OMAP_INT_DSP_MMU_ABORT 7 -# define OMAP_INT_HOST 8 -# define OMAP_INT_ABORT 9 -# define OMAP_INT_BRIDGE_PRIV 13 -# define OMAP_INT_GPIO_BANK1 14 -# define OMAP_INT_UART3 15 -# define OMAP_INT_TIMER3 16 -# define OMAP_INT_DMA_CH0_6 19 -# define OMAP_INT_DMA_CH1_7 20 -# define OMAP_INT_DMA_CH2_8 21 -# define OMAP_INT_DMA_CH3 22 -# define OMAP_INT_DMA_CH4 23 -# define OMAP_INT_DMA_CH5 24 -# define OMAP_INT_DMA_LCD 25 -# define OMAP_INT_TIMER1 26 -# define OMAP_INT_WD_TIMER 27 -# define OMAP_INT_BRIDGE_PUB 28 -# define OMAP_INT_TIMER2 30 -# define OMAP_INT_LCD_CTRL 31 +#define OMAP_INT_CAMERA 1 +#define OMAP_INT_FIQ 3 +#define OMAP_INT_RTDX 6 +#define OMAP_INT_DSP_MMU_ABORT 7 +#define OMAP_INT_HOST 8 +#define OMAP_INT_ABORT 9 +#define OMAP_INT_BRIDGE_PRIV 13 +#define OMAP_INT_GPIO_BANK1 14 +#define OMAP_INT_UART3 15 +#define OMAP_INT_TIMER3 16 +#define OMAP_INT_DMA_CH0_6 19 +#define OMAP_INT_DMA_CH1_7 20 +#define OMAP_INT_DMA_CH2_8 21 +#define OMAP_INT_DMA_CH3 22 +#define OMAP_INT_DMA_CH4 23 +#define OMAP_INT_DMA_CH5 24 +#define OMAP_INT_DMA_LCD 25 +#define OMAP_INT_TIMER1 26 +#define OMAP_INT_WD_TIMER 27 +#define OMAP_INT_BRIDGE_PUB 28 +#define OMAP_INT_TIMER2 30 +#define OMAP_INT_LCD_CTRL 31 /* * Common OMAP-15xx IRQ numbers for level 1 interrupt handler */ -# define OMAP_INT_15XX_IH2_IRQ 0 -# define OMAP_INT_15XX_LB_MMU 17 -# define OMAP_INT_15XX_LOCAL_BUS 29 +#define OMAP_INT_15XX_IH2_IRQ 0 +#define OMAP_INT_15XX_LB_MMU 17 +#define OMAP_INT_15XX_LOCAL_BUS 29 /* * OMAP-1510 specific IRQ numbers for level 1 interrupt handler */ -# define OMAP_INT_1510_SPI_TX 4 -# define OMAP_INT_1510_SPI_RX 5 -# define OMAP_INT_1510_DSP_MAILBOX1 10 -# define OMAP_INT_1510_DSP_MAILBOX2 11 +#define OMAP_INT_1510_SPI_TX 4 +#define OMAP_INT_1510_SPI_RX 5 +#define OMAP_INT_1510_DSP_MAILBOX1 10 +#define OMAP_INT_1510_DSP_MAILBOX2 11 /* * OMAP-310 specific IRQ numbers for level 1 interrupt handler */ -# define OMAP_INT_310_McBSP2_TX 4 -# define OMAP_INT_310_McBSP2_RX 5 -# define OMAP_INT_310_HSB_MAILBOX1 12 -# define OMAP_INT_310_HSAB_MMU 18 +#define OMAP_INT_310_McBSP2_TX 4 +#define OMAP_INT_310_McBSP2_RX 5 +#define OMAP_INT_310_HSB_MAILBOX1 12 +#define OMAP_INT_310_HSAB_MMU 18 /* * OMAP-1610 specific IRQ numbers for level 1 interrupt handler */ -# define OMAP_INT_1610_IH2_IRQ 0 -# define OMAP_INT_1610_IH2_FIQ 2 -# define OMAP_INT_1610_McBSP2_TX 4 -# define OMAP_INT_1610_McBSP2_RX 5 -# define OMAP_INT_1610_DSP_MAILBOX1 10 -# define OMAP_INT_1610_DSP_MAILBOX2 11 -# define OMAP_INT_1610_LCD_LINE 12 -# define OMAP_INT_1610_GPTIMER1 17 -# define OMAP_INT_1610_GPTIMER2 18 -# define OMAP_INT_1610_SSR_FIFO_0 29 +#define OMAP_INT_1610_IH2_IRQ 0 +#define OMAP_INT_1610_IH2_FIQ 2 +#define OMAP_INT_1610_McBSP2_TX 4 +#define OMAP_INT_1610_McBSP2_RX 5 +#define OMAP_INT_1610_DSP_MAILBOX1 10 +#define OMAP_INT_1610_DSP_MAILBOX2 11 +#define OMAP_INT_1610_LCD_LINE 12 +#define OMAP_INT_1610_GPTIMER1 17 +#define OMAP_INT_1610_GPTIMER2 18 +#define OMAP_INT_1610_SSR_FIFO_0 29 /* * OMAP-730 specific IRQ numbers for level 1 interrupt handler */ -# define OMAP_INT_730_IH2_FIQ 0 -# define OMAP_INT_730_IH2_IRQ 1 -# define OMAP_INT_730_USB_NON_ISO 2 -# define OMAP_INT_730_USB_ISO 3 -# define OMAP_INT_730_ICR 4 -# define OMAP_INT_730_EAC 5 -# define OMAP_INT_730_GPIO_BANK1 6 -# define OMAP_INT_730_GPIO_BANK2 7 -# define OMAP_INT_730_GPIO_BANK3 8 -# define OMAP_INT_730_McBSP2TX 10 -# define OMAP_INT_730_McBSP2RX 11 -# define OMAP_INT_730_McBSP2RX_OVF 12 -# define OMAP_INT_730_LCD_LINE 14 -# define OMAP_INT_730_GSM_PROTECT 15 -# define OMAP_INT_730_TIMER3 16 -# define OMAP_INT_730_GPIO_BANK5 17 -# define OMAP_INT_730_GPIO_BANK6 18 -# define OMAP_INT_730_SPGIO_WR 29 +#define OMAP_INT_730_IH2_FIQ 0 +#define OMAP_INT_730_IH2_IRQ 1 +#define OMAP_INT_730_USB_NON_ISO 2 +#define OMAP_INT_730_USB_ISO 3 +#define OMAP_INT_730_ICR 4 +#define OMAP_INT_730_EAC 5 +#define OMAP_INT_730_GPIO_BANK1 6 +#define OMAP_INT_730_GPIO_BANK2 7 +#define OMAP_INT_730_GPIO_BANK3 8 +#define OMAP_INT_730_McBSP2TX 10 +#define OMAP_INT_730_McBSP2RX 11 +#define OMAP_INT_730_McBSP2RX_OVF 12 +#define OMAP_INT_730_LCD_LINE 14 +#define OMAP_INT_730_GSM_PROTECT 15 +#define OMAP_INT_730_TIMER3 16 +#define OMAP_INT_730_GPIO_BANK5 17 +#define OMAP_INT_730_GPIO_BANK6 18 +#define OMAP_INT_730_SPGIO_WR 29 /* * Common IRQ numbers for level 2 interrupt handler */ -# define OMAP_INT_KEYBOARD 1 -# define OMAP_INT_uWireTX 2 -# define OMAP_INT_uWireRX 3 -# define OMAP_INT_I2C 4 -# define OMAP_INT_MPUIO 5 -# define OMAP_INT_USB_HHC_1 6 -# define OMAP_INT_McBSP3TX 10 -# define OMAP_INT_McBSP3RX 11 -# define OMAP_INT_McBSP1TX 12 -# define OMAP_INT_McBSP1RX 13 -# define OMAP_INT_UART1 14 -# define OMAP_INT_UART2 15 -# define OMAP_INT_USB_W2FC 20 -# define OMAP_INT_1WIRE 21 -# define OMAP_INT_OS_TIMER 22 -# define OMAP_INT_OQN 23 -# define OMAP_INT_GAUGE_32K 24 -# define OMAP_INT_RTC_TIMER 25 -# define OMAP_INT_RTC_ALARM 26 -# define OMAP_INT_DSP_MMU 28 +#define OMAP_INT_KEYBOARD 1 +#define OMAP_INT_uWireTX 2 +#define OMAP_INT_uWireRX 3 +#define OMAP_INT_I2C 4 +#define OMAP_INT_MPUIO 5 +#define OMAP_INT_USB_HHC_1 6 +#define OMAP_INT_McBSP3TX 10 +#define OMAP_INT_McBSP3RX 11 +#define OMAP_INT_McBSP1TX 12 +#define OMAP_INT_McBSP1RX 13 +#define OMAP_INT_UART1 14 +#define OMAP_INT_UART2 15 +#define OMAP_INT_USB_W2FC 20 +#define OMAP_INT_1WIRE 21 +#define OMAP_INT_OS_TIMER 22 +#define OMAP_INT_OQN 23 +#define OMAP_INT_GAUGE_32K 24 +#define OMAP_INT_RTC_TIMER 25 +#define OMAP_INT_RTC_ALARM 26 +#define OMAP_INT_DSP_MMU 28 /* * OMAP-1510 specific IRQ numbers for level 2 interrupt handler */ -# define OMAP_INT_1510_BT_MCSI1TX 16 -# define OMAP_INT_1510_BT_MCSI1RX 17 -# define OMAP_INT_1510_SoSSI_MATCH 19 -# define OMAP_INT_1510_MEM_STICK 27 -# define OMAP_INT_1510_COM_SPI_RO 31 +#define OMAP_INT_1510_BT_MCSI1TX 16 +#define OMAP_INT_1510_BT_MCSI1RX 17 +#define OMAP_INT_1510_SoSSI_MATCH 19 +#define OMAP_INT_1510_MEM_STICK 27 +#define OMAP_INT_1510_COM_SPI_RO 31 /* * OMAP-310 specific IRQ numbers for level 2 interrupt handler */ -# define OMAP_INT_310_FAC 0 -# define OMAP_INT_310_USB_HHC_2 7 -# define OMAP_INT_310_MCSI1_FE 16 -# define OMAP_INT_310_MCSI2_FE 17 -# define OMAP_INT_310_USB_W2FC_ISO 29 -# define OMAP_INT_310_USB_W2FC_NON_ISO 30 -# define OMAP_INT_310_McBSP2RX_OF 31 +#define OMAP_INT_310_FAC 0 +#define OMAP_INT_310_USB_HHC_2 7 +#define OMAP_INT_310_MCSI1_FE 16 +#define OMAP_INT_310_MCSI2_FE 17 +#define OMAP_INT_310_USB_W2FC_ISO 29 +#define OMAP_INT_310_USB_W2FC_NON_ISO 30 +#define OMAP_INT_310_McBSP2RX_OF 31 /* * OMAP-1610 specific IRQ numbers for level 2 interrupt handler */ -# define OMAP_INT_1610_FAC 0 -# define OMAP_INT_1610_USB_HHC_2 7 -# define OMAP_INT_1610_USB_OTG 8 -# define OMAP_INT_1610_SoSSI 9 -# define OMAP_INT_1610_BT_MCSI1TX 16 -# define OMAP_INT_1610_BT_MCSI1RX 17 -# define OMAP_INT_1610_SoSSI_MATCH 19 -# define OMAP_INT_1610_MEM_STICK 27 -# define OMAP_INT_1610_McBSP2RX_OF 31 -# define OMAP_INT_1610_STI 32 -# define OMAP_INT_1610_STI_WAKEUP 33 -# define OMAP_INT_1610_GPTIMER3 34 -# define OMAP_INT_1610_GPTIMER4 35 -# define OMAP_INT_1610_GPTIMER5 36 -# define OMAP_INT_1610_GPTIMER6 37 -# define OMAP_INT_1610_GPTIMER7 38 -# define OMAP_INT_1610_GPTIMER8 39 -# define OMAP_INT_1610_GPIO_BANK2 40 -# define OMAP_INT_1610_GPIO_BANK3 41 -# define OMAP_INT_1610_MMC2 42 -# define OMAP_INT_1610_CF 43 -# define OMAP_INT_1610_WAKE_UP_REQ 46 -# define OMAP_INT_1610_GPIO_BANK4 48 -# define OMAP_INT_1610_SPI 49 -# define OMAP_INT_1610_DMA_CH6 53 -# define OMAP_INT_1610_DMA_CH7 54 -# define OMAP_INT_1610_DMA_CH8 55 -# define OMAP_INT_1610_DMA_CH9 56 -# define OMAP_INT_1610_DMA_CH10 57 -# define OMAP_INT_1610_DMA_CH11 58 -# define OMAP_INT_1610_DMA_CH12 59 -# define OMAP_INT_1610_DMA_CH13 60 -# define OMAP_INT_1610_DMA_CH14 61 -# define OMAP_INT_1610_DMA_CH15 62 -# define OMAP_INT_1610_NAND 63 +#define OMAP_INT_1610_FAC 0 +#define OMAP_INT_1610_USB_HHC_2 7 +#define OMAP_INT_1610_USB_OTG 8 +#define OMAP_INT_1610_SoSSI 9 +#define OMAP_INT_1610_BT_MCSI1TX 16 +#define OMAP_INT_1610_BT_MCSI1RX 17 +#define OMAP_INT_1610_SoSSI_MATCH 19 +#define OMAP_INT_1610_MEM_STICK 27 +#define OMAP_INT_1610_McBSP2RX_OF 31 +#define OMAP_INT_1610_STI 32 +#define OMAP_INT_1610_STI_WAKEUP 33 +#define OMAP_INT_1610_GPTIMER3 34 +#define OMAP_INT_1610_GPTIMER4 35 +#define OMAP_INT_1610_GPTIMER5 36 +#define OMAP_INT_1610_GPTIMER6 37 +#define OMAP_INT_1610_GPTIMER7 38 +#define OMAP_INT_1610_GPTIMER8 39 +#define OMAP_INT_1610_GPIO_BANK2 40 +#define OMAP_INT_1610_GPIO_BANK3 41 +#define OMAP_INT_1610_MMC2 42 +#define OMAP_INT_1610_CF 43 +#define OMAP_INT_1610_WAKE_UP_REQ 46 +#define OMAP_INT_1610_GPIO_BANK4 48 +#define OMAP_INT_1610_SPI 49 +#define OMAP_INT_1610_DMA_CH6 53 +#define OMAP_INT_1610_DMA_CH7 54 +#define OMAP_INT_1610_DMA_CH8 55 +#define OMAP_INT_1610_DMA_CH9 56 +#define OMAP_INT_1610_DMA_CH10 57 +#define OMAP_INT_1610_DMA_CH11 58 +#define OMAP_INT_1610_DMA_CH12 59 +#define OMAP_INT_1610_DMA_CH13 60 +#define OMAP_INT_1610_DMA_CH14 61 +#define OMAP_INT_1610_DMA_CH15 62 +#define OMAP_INT_1610_NAND 63 /* * OMAP-730 specific IRQ numbers for level 2 interrupt handler */ -# define OMAP_INT_730_HW_ERRORS 0 -# define OMAP_INT_730_NFIQ_PWR_FAIL 1 -# define OMAP_INT_730_CFCD 2 -# define OMAP_INT_730_CFIREQ 3 -# define OMAP_INT_730_I2C 4 -# define OMAP_INT_730_PCC 5 -# define OMAP_INT_730_MPU_EXT_NIRQ 6 -# define OMAP_INT_730_SPI_100K_1 7 -# define OMAP_INT_730_SYREN_SPI 8 -# define OMAP_INT_730_VLYNQ 9 -# define OMAP_INT_730_GPIO_BANK4 10 -# define OMAP_INT_730_McBSP1TX 11 -# define OMAP_INT_730_McBSP1RX 12 -# define OMAP_INT_730_McBSP1RX_OF 13 -# define OMAP_INT_730_UART_MODEM_IRDA_2 14 -# define OMAP_INT_730_UART_MODEM_1 15 -# define OMAP_INT_730_MCSI 16 -# define OMAP_INT_730_uWireTX 17 -# define OMAP_INT_730_uWireRX 18 -# define OMAP_INT_730_SMC_CD 19 -# define OMAP_INT_730_SMC_IREQ 20 -# define OMAP_INT_730_HDQ_1WIRE 21 -# define OMAP_INT_730_TIMER32K 22 -# define OMAP_INT_730_MMC_SDIO 23 -# define OMAP_INT_730_UPLD 24 -# define OMAP_INT_730_USB_HHC_1 27 -# define OMAP_INT_730_USB_HHC_2 28 -# define OMAP_INT_730_USB_GENI 29 -# define OMAP_INT_730_USB_OTG 30 -# define OMAP_INT_730_CAMERA_IF 31 -# define OMAP_INT_730_RNG 32 -# define OMAP_INT_730_DUAL_MODE_TIMER 33 -# define OMAP_INT_730_DBB_RF_EN 34 -# define OMAP_INT_730_MPUIO_KEYPAD 35 -# define OMAP_INT_730_SHA1_MD5 36 -# define OMAP_INT_730_SPI_100K_2 37 -# define OMAP_INT_730_RNG_IDLE 38 -# define OMAP_INT_730_MPUIO 39 -# define OMAP_INT_730_LLPC_LCD_CTRL_OFF 40 -# define OMAP_INT_730_LLPC_OE_FALLING 41 -# define OMAP_INT_730_LLPC_OE_RISING 42 -# define OMAP_INT_730_LLPC_VSYNC 43 -# define OMAP_INT_730_WAKE_UP_REQ 46 -# define OMAP_INT_730_DMA_CH6 53 -# define OMAP_INT_730_DMA_CH7 54 -# define OMAP_INT_730_DMA_CH8 55 -# define OMAP_INT_730_DMA_CH9 56 -# define OMAP_INT_730_DMA_CH10 57 -# define OMAP_INT_730_DMA_CH11 58 -# define OMAP_INT_730_DMA_CH12 59 -# define OMAP_INT_730_DMA_CH13 60 -# define OMAP_INT_730_DMA_CH14 61 -# 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 +#define OMAP_INT_730_HW_ERRORS 0 +#define OMAP_INT_730_NFIQ_PWR_FAIL 1 +#define OMAP_INT_730_CFCD 2 +#define OMAP_INT_730_CFIREQ 3 +#define OMAP_INT_730_I2C 4 +#define OMAP_INT_730_PCC 5 +#define OMAP_INT_730_MPU_EXT_NIRQ 6 +#define OMAP_INT_730_SPI_100K_1 7 +#define OMAP_INT_730_SYREN_SPI 8 +#define OMAP_INT_730_VLYNQ 9 +#define OMAP_INT_730_GPIO_BANK4 10 +#define OMAP_INT_730_McBSP1TX 11 +#define OMAP_INT_730_McBSP1RX 12 +#define OMAP_INT_730_McBSP1RX_OF 13 +#define OMAP_INT_730_UART_MODEM_IRDA_2 14 +#define OMAP_INT_730_UART_MODEM_1 15 +#define OMAP_INT_730_MCSI 16 +#define OMAP_INT_730_uWireTX 17 +#define OMAP_INT_730_uWireRX 18 +#define OMAP_INT_730_SMC_CD 19 +#define OMAP_INT_730_SMC_IREQ 20 +#define OMAP_INT_730_HDQ_1WIRE 21 +#define OMAP_INT_730_TIMER32K 22 +#define OMAP_INT_730_MMC_SDIO 23 +#define OMAP_INT_730_UPLD 24 +#define OMAP_INT_730_USB_HHC_1 27 +#define OMAP_INT_730_USB_HHC_2 28 +#define OMAP_INT_730_USB_GENI 29 +#define OMAP_INT_730_USB_OTG 30 +#define OMAP_INT_730_CAMERA_IF 31 +#define OMAP_INT_730_RNG 32 +#define OMAP_INT_730_DUAL_MODE_TIMER 33 +#define OMAP_INT_730_DBB_RF_EN 34 +#define OMAP_INT_730_MPUIO_KEYPAD 35 +#define OMAP_INT_730_SHA1_MD5 36 +#define OMAP_INT_730_SPI_100K_2 37 +#define OMAP_INT_730_RNG_IDLE 38 +#define OMAP_INT_730_MPUIO 39 +#define OMAP_INT_730_LLPC_LCD_CTRL_OFF 40 +#define OMAP_INT_730_LLPC_OE_FALLING 41 +#define OMAP_INT_730_LLPC_OE_RISING 42 +#define OMAP_INT_730_LLPC_VSYNC 43 +#define OMAP_INT_730_WAKE_UP_REQ 46 +#define OMAP_INT_730_DMA_CH6 53 +#define OMAP_INT_730_DMA_CH7 54 +#define OMAP_INT_730_DMA_CH8 55 +#define OMAP_INT_730_DMA_CH9 56 +#define OMAP_INT_730_DMA_CH10 57 +#define OMAP_INT_730_DMA_CH11 58 +#define OMAP_INT_730_DMA_CH12 59 +#define OMAP_INT_730_DMA_CH13 60 +#define OMAP_INT_730_DMA_CH14 61 +#define OMAP_INT_730_DMA_CH15 62 +#define OMAP_INT_730_NAND 63 /* 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; @@ -507,9 +353,9 @@ struct dma_irq_map { enum omap_dma_port { emiff = 0, emifs, - imif, /* omap16xx: ocp_t1 */ + imif, /* omap16xx: ocp_t1 */ tipb, - local, /* omap16xx: ocp_t2 */ + local, /* omap16xx: ocp_t2 */ tipb_mpui, __omap_dma_port_last, }; @@ -572,157 +418,71 @@ struct omap_dma_lcd_channel_s { * DMA request numbers for OMAP1 * See /usr/include/asm-arm/arch-omap/dma.h in Linux. */ -# define OMAP_DMA_NO_DEVICE 0 -# define OMAP_DMA_MCSI1_TX 1 -# define OMAP_DMA_MCSI1_RX 2 -# define OMAP_DMA_I2C_RX 3 -# define OMAP_DMA_I2C_TX 4 -# define OMAP_DMA_EXT_NDMA_REQ0 5 -# define OMAP_DMA_EXT_NDMA_REQ1 6 -# define OMAP_DMA_UWIRE_TX 7 -# define OMAP_DMA_MCBSP1_TX 8 -# define OMAP_DMA_MCBSP1_RX 9 -# define OMAP_DMA_MCBSP3_TX 10 -# define OMAP_DMA_MCBSP3_RX 11 -# define OMAP_DMA_UART1_TX 12 -# define OMAP_DMA_UART1_RX 13 -# define OMAP_DMA_UART2_TX 14 -# define OMAP_DMA_UART2_RX 15 -# define OMAP_DMA_MCBSP2_TX 16 -# define OMAP_DMA_MCBSP2_RX 17 -# define OMAP_DMA_UART3_TX 18 -# define OMAP_DMA_UART3_RX 19 -# define OMAP_DMA_CAMERA_IF_RX 20 -# define OMAP_DMA_MMC_TX 21 -# define OMAP_DMA_MMC_RX 22 -# define OMAP_DMA_NAND 23 /* Not in OMAP310 */ -# define OMAP_DMA_IRQ_LCD_LINE 24 /* Not in OMAP310 */ -# define OMAP_DMA_MEMORY_STICK 25 /* Not in OMAP310 */ -# define OMAP_DMA_USB_W2FC_RX0 26 -# define OMAP_DMA_USB_W2FC_RX1 27 -# define OMAP_DMA_USB_W2FC_RX2 28 -# define OMAP_DMA_USB_W2FC_TX0 29 -# define OMAP_DMA_USB_W2FC_TX1 30 -# define OMAP_DMA_USB_W2FC_TX2 31 +#define OMAP_DMA_NO_DEVICE 0 +#define OMAP_DMA_MCSI1_TX 1 +#define OMAP_DMA_MCSI1_RX 2 +#define OMAP_DMA_I2C_RX 3 +#define OMAP_DMA_I2C_TX 4 +#define OMAP_DMA_EXT_NDMA_REQ0 5 +#define OMAP_DMA_EXT_NDMA_REQ1 6 +#define OMAP_DMA_UWIRE_TX 7 +#define OMAP_DMA_MCBSP1_TX 8 +#define OMAP_DMA_MCBSP1_RX 9 +#define OMAP_DMA_MCBSP3_TX 10 +#define OMAP_DMA_MCBSP3_RX 11 +#define OMAP_DMA_UART1_TX 12 +#define OMAP_DMA_UART1_RX 13 +#define OMAP_DMA_UART2_TX 14 +#define OMAP_DMA_UART2_RX 15 +#define OMAP_DMA_MCBSP2_TX 16 +#define OMAP_DMA_MCBSP2_RX 17 +#define OMAP_DMA_UART3_TX 18 +#define OMAP_DMA_UART3_RX 19 +#define OMAP_DMA_CAMERA_IF_RX 20 +#define OMAP_DMA_MMC_TX 21 +#define OMAP_DMA_MMC_RX 22 +#define OMAP_DMA_NAND 23 /* Not in OMAP310 */ +#define OMAP_DMA_IRQ_LCD_LINE 24 /* Not in OMAP310 */ +#define OMAP_DMA_MEMORY_STICK 25 /* Not in OMAP310 */ +#define OMAP_DMA_USB_W2FC_RX0 26 +#define OMAP_DMA_USB_W2FC_RX1 27 +#define OMAP_DMA_USB_W2FC_RX2 28 +#define OMAP_DMA_USB_W2FC_TX0 29 +#define OMAP_DMA_USB_W2FC_TX1 30 +#define OMAP_DMA_USB_W2FC_TX2 31 /* These are only for 1610 */ -# define OMAP_DMA_CRYPTO_DES_IN 32 -# define OMAP_DMA_SPI_TX 33 -# define OMAP_DMA_SPI_RX 34 -# define OMAP_DMA_CRYPTO_HASH 35 -# define OMAP_DMA_CCP_ATTN 36 -# define OMAP_DMA_CCP_FIFO_NOT_EMPTY 37 -# define OMAP_DMA_CMT_APE_TX_CHAN_0 38 -# define OMAP_DMA_CMT_APE_RV_CHAN_0 39 -# define OMAP_DMA_CMT_APE_TX_CHAN_1 40 -# define OMAP_DMA_CMT_APE_RV_CHAN_1 41 -# define OMAP_DMA_CMT_APE_TX_CHAN_2 42 -# define OMAP_DMA_CMT_APE_RV_CHAN_2 43 -# define OMAP_DMA_CMT_APE_TX_CHAN_3 44 -# define OMAP_DMA_CMT_APE_RV_CHAN_3 45 -# define OMAP_DMA_CMT_APE_TX_CHAN_4 46 -# define OMAP_DMA_CMT_APE_RV_CHAN_4 47 -# define OMAP_DMA_CMT_APE_TX_CHAN_5 48 -# define OMAP_DMA_CMT_APE_RV_CHAN_5 49 -# define OMAP_DMA_CMT_APE_TX_CHAN_6 50 -# define OMAP_DMA_CMT_APE_RV_CHAN_6 51 -# define OMAP_DMA_CMT_APE_TX_CHAN_7 52 -# define OMAP_DMA_CMT_APE_RV_CHAN_7 53 -# define OMAP_DMA_MMC2_TX 54 -# 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); +#define OMAP_DMA_CRYPTO_DES_IN 32 +#define OMAP_DMA_SPI_TX 33 +#define OMAP_DMA_SPI_RX 34 +#define OMAP_DMA_CRYPTO_HASH 35 +#define OMAP_DMA_CCP_ATTN 36 +#define OMAP_DMA_CCP_FIFO_NOT_EMPTY 37 +#define OMAP_DMA_CMT_APE_TX_CHAN_0 38 +#define OMAP_DMA_CMT_APE_RV_CHAN_0 39 +#define OMAP_DMA_CMT_APE_TX_CHAN_1 40 +#define OMAP_DMA_CMT_APE_RV_CHAN_1 41 +#define OMAP_DMA_CMT_APE_TX_CHAN_2 42 +#define OMAP_DMA_CMT_APE_RV_CHAN_2 43 +#define OMAP_DMA_CMT_APE_TX_CHAN_3 44 +#define OMAP_DMA_CMT_APE_RV_CHAN_3 45 +#define OMAP_DMA_CMT_APE_TX_CHAN_4 46 +#define OMAP_DMA_CMT_APE_RV_CHAN_4 47 +#define OMAP_DMA_CMT_APE_TX_CHAN_5 48 +#define OMAP_DMA_CMT_APE_RV_CHAN_5 49 +#define OMAP_DMA_CMT_APE_TX_CHAN_6 50 +#define OMAP_DMA_CMT_APE_RV_CHAN_6 51 +#define OMAP_DMA_CMT_APE_TX_CHAN_7 52 +#define OMAP_DMA_CMT_APE_RV_CHAN_7 53 +#define OMAP_DMA_MMC2_TX 54 +#define OMAP_DMA_MMC2_RX 55 +#define OMAP_DMA_CRYPTO_DES_OUT 56 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); struct omap_mpuio_s; @@ -731,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; @@ -770,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); @@ -782,61 +528,29 @@ 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); +#define TYPE_OMAP_MMC "omap-mmc" +OBJECT_DECLARE_SIMPLE_TYPE(OMAPMMCState, OMAP_MMC) + +DeviceState *omap_mmc_init(hwaddr base, + MemoryRegion *sysmem, + qemu_irq irq, qemu_irq dma[], omap_clk clk); +/* TODO: clock framework (see above) */ +void omap_mmc_set_clk(DeviceState *dev, omap_clk clk); + /* omap_i2c.c */ I2CBus *omap_i2c_bus(DeviceState *omap_i2c); -# define cpu_is_omap310(cpu) (cpu->mpu_model == omap310) -# 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) \ +#define cpu_is_omap310(cpu) (cpu->mpu_model == omap310) +#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_omap15xx(cpu) \ (cpu_is_omap310(cpu) || cpu_is_omap1510(cpu)) -# define cpu_is_omap16xx(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 { @@ -844,13 +558,6 @@ struct omap_mpu_state_s { omap1510, omap1610, omap1710, - omap2410, - omap2420, - omap2422, - omap2423, - omap2430, - omap3430, - omap3630, } mpu_model; ARMCPU *cpu; @@ -897,7 +604,7 @@ struct omap_mpu_state_s { /* MPU public TIPB peripherals */ struct omap_32khz_timer_s *os_timer; - struct omap_mmc_s *mmc; + DeviceState *mmc; struct omap_mpuio_s *mpuio; @@ -960,33 +667,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); @@ -999,42 +685,14 @@ void omap_badwidth_write32(void *opaque, hwaddr addr, void omap_mpu_wakeup(void *opaque, int irq, int req); -# define OMAP_BAD_REG(paddr) \ +#define OMAP_BAD_REG(paddr) \ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad register %#08"HWADDR_PRIx"\n", \ __func__, paddr) -# define OMAP_RO_REG(paddr) \ +#define OMAP_RO_REG(paddr) \ qemu_log_mask(LOG_GUEST_ERROR, "%s: Read-only register %#08" \ 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 +#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 54eb895e42a..00000000000 --- a/include/hw/arm/pxa.h +++ /dev/null @@ -1,199 +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 */ -#define TYPE_PXA2XX_I2C "pxa2xx_i2c" -OBJECT_DECLARE_SIMPLE_TYPE(PXA2xxI2CState, PXA2XX_I2C) - -PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base, - qemu_irq irq, uint32_t page_size); -I2CBus *pxa2xx_i2c_bus(PXA2xxI2CState *s); - -typedef struct PXA2xxI2SState PXA2xxI2SState; - -#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(unsigned int sdram_size, const char *revision); -PXA2xxState *pxa255_init(unsigned int sdram_size); - -#endif /* PXA_H */ diff --git a/include/hw/misc/raspberrypi-fw-defs.h b/include/hw/arm/raspberrypi-fw-defs.h similarity index 95% rename from include/hw/misc/raspberrypi-fw-defs.h rename to include/hw/arm/raspberrypi-fw-defs.h index 4551fe7450d..60b8e5b4513 100644 --- a/include/hw/misc/raspberrypi-fw-defs.h +++ b/include/hw/arm/raspberrypi-fw-defs.h @@ -10,7 +10,6 @@ #ifndef INCLUDE_HW_MISC_RASPBERRYPI_FW_DEFS_H_ #define INCLUDE_HW_MISC_RASPBERRYPI_FW_DEFS_H_ -#include "qemu/osdep.h" enum rpi_firmware_property_tag { RPI_FWREQ_PROPERTY_END = 0, @@ -57,6 +56,7 @@ enum rpi_firmware_property_tag { 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, @@ -74,6 +74,7 @@ enum rpi_firmware_property_tag { 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, @@ -160,4 +161,15 @@ enum rpi_firmware_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 ede98e63c33..7bc4807fa51 100644 --- a/include/hw/arm/raspi_platform.h +++ b/include/hw/arm/raspi_platform.h @@ -28,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 */ @@ -37,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 */ diff --git a/include/hw/arm/sharpsl.h b/include/hw/arm/sharpsl.h index e986b28c527..1e3992fcd00 100644 --- a/include/hw/arm/sharpsl.h +++ b/include/hw/arm/sharpsl.h @@ -11,7 +11,7 @@ /* zaurus.c */ -#define SL_PXA_PARAM_BASE 0xa0000a00 +#define SL_PXA_PARAM_BASE 0xa0000a00 void sl_bootparam_write(hwaddr ptr); #endif diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index fd8d772da11..e5e2d09294d 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -37,6 +37,9 @@ #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 */ @@ -49,10 +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 { - int stage; + 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 { @@ -67,6 +78,7 @@ typedef struct SMMUTLBEntry { IOMMUTLBEntry entry; uint8_t level; uint8_t granule; + IOMMUAccessFlags parent_perm; } SMMUTLBEntry; /* Stage-2 configuration. */ @@ -77,7 +89,7 @@ typedef struct SMMUS2Cfg { bool record_faults; /* Record fault events (S2R) */ uint8_t granule_sz; /* Granule page shift (based on S2TG) */ uint8_t eff_ps; /* Effective PA output range (based on S2PS) */ - uint16_t vmid; /* Virtual Machine ID (S2VMID) */ + int vmid; /* Virtual Machine ID (S2VMID) */ uint64_t vttb; /* Address of translation table base (S2TTB) */ } SMMUS2Cfg; @@ -88,19 +100,19 @@ typedef struct SMMUS2Cfg { */ typedef struct SMMUTransCfg { /* Shared fields between stage-1 and stage-2. */ - int stage; /* translation stage */ + 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]; /* Used by stage-2 only. */ struct SMMUS2Cfg s2cfg; @@ -124,12 +136,17 @@ typedef struct SMMUPciBus { typedef struct SMMUIOTLBKey { uint64_t iova; - uint16_t asid; - uint16_t vmid; + int asid; + int vmid; uint8_t tg; uint8_t level; } SMMUIOTLBKey; +typedef struct SMMUSIDRange { + uint32_t start; + uint32_t end; +} SMMUSIDRange; + struct SMMUState { /* */ SysBusDevice dev; @@ -172,8 +189,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 @@ -181,22 +206,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, uint16_t vmid, 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_vmid(SMMUState *s, uint16_t vmid); +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); +void smmu_configs_inv_sid_range(SMMUState *s, SMMUSIDRange sid_range); /* Unmap the range of all the notifiers registered to any IOMMU mr */ void smmu_inv_notifiers_all(SMMUState *s); diff --git a/include/hw/arm/soc_dma.h b/include/hw/arm/soc_dma.h index e93a7499a80..bcdb91425a5 100644 --- a/include/hw/arm/soc_dma.h +++ b/include/hw/arm/soc_dma.h @@ -54,7 +54,7 @@ struct soc_dma_ch_s { int bytes; /* Initialised by the DMA module, call soc_dma_ch_update after writing. */ enum soc_dma_access_type type[2]; - hwaddr vaddr[2]; /* Updated by .transfer_fn(). */ + hwaddr vaddr[2]; /* Updated by .transfer_fn(). */ /* Private */ void *paddr[2]; soc_dma_io_t io_fn[2]; @@ -70,7 +70,7 @@ struct soc_dma_ch_s { struct soc_dma_s { /* Following fields are set by the SoC DMA module and can be used * by anybody. */ - uint64_t drqbmp; /* Is zeroed by soc_dma_reset() */ + uint64_t drqbmp; /* Is zeroed by soc_dma_reset() */ qemu_irq *drq; void *opaque; int64_t freq; diff --git a/include/hw/arm/stm32f100_soc.h b/include/hw/arm/stm32f100_soc.h index 40cd415b284..a74d7b369c1 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 5a4f7762642..4f4c8bbebc1 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; diff --git a/include/hw/arm/stm32f405_soc.h b/include/hw/arm/stm32f405_soc.h index c968ce3ab23..2eeada64ded 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" @@ -51,14 +52,11 @@ OBJECT_DECLARE_SIMPLE_TYPE(STM32F405State, STM32F405_SOC) #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]; diff --git a/include/hw/arm/stm32l4x5_soc.h b/include/hw/arm/stm32l4x5_soc.h new file mode 100644 index 00000000000..c2fae6e23fe --- /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 "system/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 e1ddbea96be..9a1b0f53d21 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -34,8 +34,9 @@ #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 "system/kvm.h" #include "hw/intc/arm_gicv3_common.h" #include "qom/object.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, @@ -124,14 +117,8 @@ typedef enum VirtGICType { struct VirtMachineClass { MachineClass parent; - bool disallow_affinity_adjustment; - bool no_its; bool no_tcg_its; - 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; bool no_kvm_steal_time; @@ -140,6 +127,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 { @@ -160,6 +149,7 @@ struct VirtMachineState { bool ras; bool mte; bool dtb_randomness; + bool second_ns_uart_present; OnOffAuto acpi; VirtGICType gic_version; VirtIOMMUType iommu; @@ -183,6 +173,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/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 7b419f88c2c..05ed641b6b6 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,9 +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) @@ -77,6 +78,7 @@ 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]; @@ -116,6 +118,7 @@ struct Versal { } iou; XlnxZynqMPRTC rtc; + XlnxVersalTRng trng; XlnxBBRam bbram; XlnxEFuse efuse; XlnxVersalEFuseCtrl efuse_ctrl; @@ -160,6 +163,7 @@ struct Versal { #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 @@ -329,4 +333,6 @@ struct Versal { #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 687c75e3b03..c137ac59e85 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; diff --git a/include/hw/audio/asc.h b/include/hw/audio/asc.h new file mode 100644 index 00000000000..04fac270b6a --- /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 95061795877..6be75a6b86e 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 270717a06a0..474c5ff94e5 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 00000000000..8dafedb276d --- /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 15fff664350..de3946a5f1e 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 35248c08379..c367c5efeaf 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 de93756cbe8..3671f0174d0 100644 --- a/include/hw/block/flash.h +++ b/include/hw/block/flash.h @@ -44,40 +44,10 @@ PFlashCFI02 *pflash_cfi02_register(hwaddr base, uint16_t unlock_addr1, int be); -/* nand.c */ -DeviceState *nand_init(BlockBackend *blk, int manf_id, int chip_id); -void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale, - uint8_t ce, uint8_t wp, uint8_t gnd); -void nand_getpins(DeviceState *dev, int *rb); -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 - -/* onenand.c */ -void *onenand_raw_otp(DeviceState *onenand_device); - -/* ecc.c */ -typedef struct { - uint8_t cp; /* Column parity */ - uint16_t lp[2]; /* Line parity */ - uint16_t count; -} ECCState; - -uint8_t ecc_digest(ECCState *s, uint8_t sample); -void ecc_reset(ECCState *s); -extern const VMStateDescription vmstate_ecc_state; - /* m25p80.c */ +#define TYPE_M25P80 "m25p80-generic" + BlockBackend *m25p80_get_blk(DeviceState *dev); #endif diff --git a/include/hw/block/swim.h b/include/hw/block/swim.h index 9b3dcb029d3..5f567e8d595 100644 --- a/include/hw/block/swim.h +++ b/include/hw/block/swim.h @@ -43,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 6c67af196a3..f424b2b5058 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -3,13 +3,14 @@ #ifndef HW_BOARDS_H #define HW_BOARDS_H -#include "exec/memory.h" -#include "sysemu/hostmem.h" -#include "sysemu/blockdev.h" +#include "system/memory.h" +#include "system/hostmem.h" +#include "system/blockdev.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" @@ -24,19 +25,35 @@ 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); /** @@ -113,7 +130,7 @@ typedef struct CPUArchId { uint64_t arch_id; int64_t vcpus_count; CpuInstanceProperties props; - Object *cpu; + CPUState *cpu; const char *type; } CPUArchId; @@ -134,12 +151,24 @@ typedef struct { * @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 + * @has_caches - whether cache properties are explicitly specified in the + * user provided smp-cache configuration */ 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]; + bool has_caches; } SMPCompatProps; /** @@ -201,6 +230,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: @@ -223,6 +256,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 >*/ @@ -236,9 +272,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; @@ -249,9 +286,8 @@ struct MachineClass { no_parallel:1, no_floppy:1, no_cdrom:1, - no_sdcard:1, - pci_allow_0_address:1, - legacy_fw_cfg_order:1; + pci_allow_0_address:1; + bool auto_create_sdcard; bool is_default; const char *default_machine_opts; const char *default_boot_order; @@ -268,7 +304,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; @@ -290,43 +326,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: */ @@ -351,6 +412,7 @@ struct MachineState { bool enable_graphics; ConfidentialGuestSupport *cgs; HostMemoryBackend *memdev; + bool aux_ram_share; /* * convenience alias to ram_memdev_id backend memory region * or to numa container memory region @@ -358,23 +420,349 @@ 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; BootConfiguration boot_config; char *kernel_filename; char *kernel_cmdline; + char *shim_filename; char *initrd_filename; const char *cpu_type; 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. + * + * These values must match the ver_machine_deprecation_version + * and ver_machine_deletion_version logic in docs/conf.py and + * the text in docs/about/deprecated.rst + */ +#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_CURRENT_EXPIRED(cutoff, major, minor) \ + (((QEMU_VERSION_MAJOR - major) > cutoff) || \ + (((QEMU_VERSION_MAJOR - major) == cutoff) && \ + (QEMU_VERSION_MINOR - minor) >= 0)) + +#define _MACHINE_VER_IS_NEXT_MINOR_EXPIRED(cutoff, major, minor) \ + (((QEMU_VERSION_MAJOR - major) > cutoff) || \ + (((QEMU_VERSION_MAJOR - major) == cutoff) && \ + ((QEMU_VERSION_MINOR + 1) - minor) >= 0)) + +#define _MACHINE_VER_IS_NEXT_MAJOR_EXPIRED(cutoff, major, minor) \ + ((((QEMU_VERSION_MAJOR + 1) - major) > cutoff) || \ + ((((QEMU_VERSION_MAJOR + 1) - major) == cutoff) && \ + (0 - minor) >= 0)) + +/* + * - The first check applies to formal releases + * - The second check applies to dev snapshots / release candidates + * where the next major version is the same. + * e.g. 9.0.50, 9.1.50, 9.0.90, 9.1.90 + * - The third check applies to dev snapshots / release candidates + * where the next major version will change. + * e.g. 9.2.50, 9.2.90 + * + * NB: this assumes we do 3 minor releases per year, before bumping major, + * and dev snapshots / release candidates are numbered with micro >= 50 + * If this ever changes the logic below will need modifying.... + */ +#define _MACHINE_VER_IS_EXPIRED_IMPL(cutoff, major, minor) \ + ((QEMU_VERSION_MICRO < 50 && \ + _MACHINE_VER_IS_CURRENT_EXPIRED(cutoff, major, minor)) || \ + (QEMU_VERSION_MICRO >= 50 && QEMU_VERSION_MINOR < 2 && \ + _MACHINE_VER_IS_NEXT_MINOR_EXPIRED(cutoff, major, minor)) || \ + (QEMU_VERSION_MICRO >= 50 && QEMU_VERSION_MINOR == 2 && \ + _MACHINE_VER_IS_NEXT_MAJOR_EXPIRED(cutoff, major, minor))) + +#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 + */ +#define MACHINE_VER_DELETION(...) \ + do { \ + if (MACHINE_VER_SHOULD_DELETE(__VA_ARGS__)) { \ + return; \ + } \ + } while (0) + #define DEFINE_MACHINE(namestr, machine_initfn) \ - static void machine_initfn##_class_init(ObjectClass *oc, void *data) \ + static void machine_initfn##_class_init(ObjectClass *oc, const void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ machine_initfn(mc); \ @@ -390,6 +778,21 @@ struct MachineState { } \ type_init(machine_initfn##_register_types) +extern GlobalProperty hw_compat_10_0[]; +extern const size_t hw_compat_10_0_len; + +extern GlobalProperty hw_compat_9_2[]; +extern const size_t hw_compat_9_2_len; + +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; @@ -459,19 +862,4 @@ extern const size_t hw_compat_2_7_len; extern GlobalProperty hw_compat_2_6[]; extern const size_t hw_compat_2_6_len; -extern GlobalProperty hw_compat_2_5[]; -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/escc.h b/include/hw/char/escc.h index 5669a5b811a..8c4c6a7730c 100644 --- a/include/hw/char/escc.h +++ b/include/hw/char/escc.h @@ -46,6 +46,9 @@ typedef struct ESCCChannelState { 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/grlib_uart.h b/include/hw/char/grlib_uart.h new file mode 100644 index 00000000000..7496f8fd5e9 --- /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/imx_serial.h b/include/hw/char/imx_serial.h index b823f945195..90ba3ff18c2 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,11 +69,13 @@ 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 */ @@ -78,26 +84,38 @@ OBJECT_DECLARE_SIMPLE_TYPE(IMXSerialState, IMX_SERIAL) #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; uint32_t ucr1; uint32_t ucr2; uint32_t uts1; + uint32_t ufcr; /* * The registers below are implemented just so that the * guest OS sees what it has written */ uint32_t onems; - uint32_t ufcr; uint32_t ubmr; uint32_t ubrc; uint32_t ucr3; diff --git a/include/hw/char/mchp_pfsoc_mmuart.h b/include/hw/char/mchp_pfsoc_mmuart.h index b0e14ca3554..a7b8b1b08b3 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 index d24ccecf05c..3edaf9dbe49 100644 --- a/include/hw/char/parallel-isa.h +++ b/include/hw/char/parallel-isa.h @@ -12,6 +12,7 @@ #include "parallel.h" +#include "system/ioport.h" #include "hw/isa/isa.h" #include "qom/object.h" @@ -25,6 +26,10 @@ struct ISAParallelState { 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 7b5a309a039..7b04478226b 100644 --- a/include/hw/char/parallel.h +++ b/include/hw/char/parallel.h @@ -1,8 +1,7 @@ #ifndef HW_PARALLEL_H #define HW_PARALLEL_H -#include "exec/ioport.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/isa/isa.h" #include "hw/irq.h" #include "chardev/char-fe.h" @@ -22,7 +21,6 @@ typedef struct ParallelState { uint32_t last_read_offset; /* For debugging */ /* Memory-mapped interface */ int it_shift; - PortioList portio_list; } ParallelState; void parallel_hds_isa_init(ISABus *bus, int n); diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h index d8538021323..299ca9b18bb 100644 --- a/include/hw/char/pl011.h +++ b/include/hw/char/pl011.h @@ -32,7 +32,6 @@ struct PL011State { SysBusDevice parent_obj; MemoryRegion iomem; - uint32_t readbuff; uint32_t flags; uint32_t lcr; uint32_t rsr; @@ -53,6 +52,11 @@ struct PL011State { Clock *clk; bool migrate_clk; const unsigned char *id; + /* + * Since some users embed this struct directly, we must + * ensure that the C struct is at least as big as the Rust one. + */ + uint8_t padding_for_rust[16]; }; DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr); diff --git a/include/hw/char/riscv_htif.h b/include/hw/char/riscv_htif.h index df493fdf6bc..ee0ca299024 100644 --- a/include/hw/char/riscv_htif.h +++ b/include/hw/char/riscv_htif.h @@ -22,7 +22,7 @@ #include "chardev/char.h" #include "chardev/char-fe.h" -#include "exec/memory.h" +#include "system/memory.h" #define TYPE_HTIF_UART "riscv.htif.uart" diff --git a/include/hw/char/serial-isa.h b/include/hw/char/serial-isa.h new file mode 100644 index 00000000000..8517afa128a --- /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 00000000000..77abd098e0d --- /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 "system/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 8ba7eca3d67..4bf90a46f3c 100644 --- a/include/hw/char/serial.h +++ b/include/hw/char/serial.h @@ -27,10 +27,8 @@ #define HW_SERIAL_H #include "chardev/char-fe.h" -#include "exec/memory.h" +#include "system/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 7f6c79f8bdb..0846cf62188 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 65bcc85470d..fdfa7424a70 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 00000000000..dd3866682a3 --- /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/clock.h b/include/hw/clock.h index bb12117f67b..a279bd4ba5e 100644 --- a/include/hw/clock.h +++ b/include/hw/clock.h @@ -141,14 +141,6 @@ Clock *clock_new(Object *parent, const char *name); void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque, unsigned int events); -/** - * clock_clear_callback: - * @clk: the clock to delete the callback from - * - * Unregister the callback registered with clock_set_callback. - */ -void clock_clear_callback(Clock *clk); - /** * clock_set_source: * @clk: the clock. @@ -357,6 +349,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 +368,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 deleted file mode 100644 index 5dbfd799553..00000000000 --- a/include/hw/core/accel-cpu.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Accelerator interface, specializes CPUClass - * This header is used only by target-specific code. - * - * 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_CPU_H -#define ACCEL_CPU_H - -/* - * This header is used to define new accelerator-specific target-specific - * accelerator cpu subclasses. - * It uses CPU_RESOLVING_TYPE, so this is clearly target-specific. - * - * Do not try to use for any other purpose than the implementation of new - * subclasses in target/, or the accel implementation itself in accel/ - */ - -#define TYPE_ACCEL_CPU "accel-" CPU_RESOLVING_TYPE -#define ACCEL_CPU_NAME(name) (name "-" TYPE_ACCEL_CPU) -typedef struct AccelCPUClass AccelCPUClass; -DECLARE_CLASS_CHECKERS(AccelCPUClass, ACCEL_CPU, TYPE_ACCEL_CPU) - -typedef struct AccelCPUClass { - /*< private >*/ - ObjectClass parent_class; - /*< public >*/ - - void (*cpu_class_init)(CPUClass *cc); - void (*cpu_instance_init)(CPUState *cpu); - bool (*cpu_realizefn)(CPUState *cpu, Error **errp); -} AccelCPUClass; - -#endif /* ACCEL_CPU_H */ diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 92a4234439a..33296a1c080 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-event.h" #include "qom/object.h" typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size, @@ -80,17 +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 -#define MMU_ACCESS_COUNT 3 -} 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; @@ -101,10 +101,10 @@ struct SysemuCPUOps; /** * CPUClass: * @class_by_name: Callback to map -cpu command line model name to an - * instantiatable CPU type. + * instantiatable CPU type. + * @list_cpus: list available CPU models and flags. * @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. * @memory_rw_debug: Callback for GDB memory access. * @dump_state: Callback for dumping state. * @query_cpu_fast: @@ -122,19 +122,24 @@ struct SysemuCPUOps; * @get_pc: Callback for getting the Program Counter register. * As above, with the semantics of the target architecture. * @gdb_read_register: Callback for letting GDB read a register. + * No more than @gdb_num_core_regs registers can be read. * @gdb_write_register: Callback for letting GDB write a register. + * No more than @gdb_num_core_regs registers can be written. * @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_get_core_xml_file: Optional callback that returns the file name for + * the core registers GDB XML description. The returned value is expected to + * be a simple constant string: the caller will not g_free() it. If this + * is NULL then @gdb_core_xml_file will be used instead. * @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. + * to GDB. The returned value is expected to be a simple constant string: + * the caller will not g_free() 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. @@ -149,11 +154,11 @@ struct CPUClass { /*< public >*/ ObjectClass *(*class_by_name)(const char *cpu_model); + void (*list_cpus)(void); void (*parse_features)(const char *typename, char *str, Error **errp); - bool (*has_work)(CPUState *cpu); int (*memory_rw_debug)(CPUState *cpu, vaddr addr, - uint8_t *buf, int len, bool is_write); + uint8_t *buf, size_t 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); @@ -164,8 +169,8 @@ 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); + const char * (*gdb_get_core_xml_file)(CPUState *cpu); void (*disas_set_info)(CPUState *cpu, disassemble_info *info); @@ -176,7 +181,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 @@ -192,6 +197,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 @@ -212,31 +351,27 @@ typedef union IcountDecr { } u16; } IcountDecr; -typedef struct CPUBreakpoint { - vaddr pc; - int flags; /* BP_* */ - QTAILQ_ENTRY(CPUBreakpoint) entry; -} CPUBreakpoint; - -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; @@ -266,7 +401,8 @@ struct qemu_work_item; #define CPU_UNSET_NUMA_NODE_ID -1 /** - * 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 @@ -276,12 +412,15 @@ struct qemu_work_item; * 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. @@ -290,30 +429,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. @@ -322,7 +453,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 >*/ @@ -331,7 +470,6 @@ struct CPUState { CPUClass *cc; /*< public >*/ - int nr_cores; int nr_threads; struct QemuThread *thread; @@ -365,17 +503,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; - - CPUJumpCache *tb_jmp_cache; + struct CPUJumpCache *tb_jmp_cache; - struct GDBRegisterState *gdb_regs; + GArray *gdb_regs; int gdb_num_regs; int gdb_num_g_regs; QTAILQ_ENTRY(CPUState) node; @@ -401,16 +537,13 @@ struct CPUState { uint32_t kvm_fetch_index; uint64_t dirty_pages; int kvm_vcpu_stats_fd; + bool vcpu_dirty; /* Use by accel-block: CPU is executing an ioctl() */ QemuLockCnt in_ioctl_lock; - DECLARE_BITMAP(plugin_mask, QEMU_PLUGIN_EV_MAX); - #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. */ @@ -418,12 +551,9 @@ struct CPUState { int cluster_index; uint32_t tcg_cflags; uint32_t halted; - uint32_t can_do_io; int32_t exception_index; AccelCPUState *accel; - /* shared by kvm and hvf */ - bool vcpu_dirty; /* Used to keep track of an outstanding cpu throttle thread for migration * autoconverge @@ -443,28 +573,35 @@ struct CPUState { /* 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; }; +/* Validate placement of CPUNegativeOffsetState. */ +QEMU_BUILD_BUG_ON(offsetof(CPUState, neg) != + sizeof(CPUState) - sizeof(CPUNegativeOffsetState)); + +static inline CPUArchState *cpu_env(CPUState *cpu) +{ + /* We validate that CPUArchState follows CPUState in cpu-target.c */ + return (CPUArchState *)(cpu + 1); +} + typedef QTAILQ_HEAD(CPUTailQ, CPUState) CPUTailQ; -extern CPUTailQ cpus; +extern CPUTailQ cpus_queue; -#define first_cpu QTAILQ_FIRST_RCU(&cpus) +#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; -/** - * qemu_tcg_mttcg_enabled: - * Check whether we are running MultiThread TCG or not. - * - * Returns: %true if we are in MTTCG mode %false otherwise. - */ -extern bool mttcg_enabled; -#define qemu_tcg_mttcg_enabled() (mttcg_enabled) - /** * cpu_paging_enabled: * @cpu: The CPU whose state is to be inspected. @@ -478,12 +615,12 @@ 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) - /** * cpu_write_elf64_note: * @f: pointer to a function that writes memory to a file @@ -533,8 +670,6 @@ int cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, */ GuestPanicInformation *cpu_get_crash_info(CPUState *cpu); -#endif /* !CONFIG_USER_ONLY */ - /** * CPUDumpFlags: * @CPU_DUMP_CODE: @@ -558,7 +693,6 @@ enum CPUDumpFlags { */ void cpu_dump_state(CPUState *cpu, FILE *f, int flags); -#ifndef CONFIG_USER_ONLY /** * cpu_get_phys_page_attrs_debug: * @cpu: The CPU to obtain the physical page address for. @@ -605,7 +739,15 @@ int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs); */ bool cpu_virtio_is_big_endian(CPUState *cpu); -#endif /* CONFIG_USER_ONLY */ +/** + * cpu_has_work: + * @cpu: The vCPU to check. + * + * Checks whether the CPU has work to do. + * + * Returns: %true if the CPU has work, %false otherwise. + */ +bool cpu_has_work(CPUState *cpu); /** * cpu_list_add: @@ -630,12 +772,26 @@ void cpu_reset(CPUState *cpu); * @typename: The CPU base type. * @cpu_model: The model string without any parameters. * - * Looks up a CPU #ObjectClass matching name @cpu_model. + * Looks up a concrete CPU #ObjectClass matching name @cpu_model. * - * Returns: A #CPUClass or %NULL if not matching class is found. + * Returns: A concrete #CPUClass or %NULL if no matching class is found + * or if the matching class is abstract. */ ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model); +/** + * cpu_model_from_type: + * @typename: The CPU type name + * + * Extract the CPU model name from the CPU type name. The + * CPU type name is either the combination of the CPU model + * name and suffix, or same to the CPU model name. + * + * Returns: CPU model name or NULL if the CPU class doesn't exist + * The user should g_free() the string once no longer needed. + */ +char *cpu_model_from_type(const char *typename); + /** * cpu_create: * @typename: The CPU type. @@ -657,22 +813,6 @@ CPUState *cpu_create(const char *typename); */ const char *parse_cpu_option(const char *cpu_option); -/** - * cpu_has_work: - * @cpu: The vCPU to check. - * - * Checks whether the CPU has work to do. - * - * Returns: %true if the CPU has work, %false otherwise. - */ -static inline bool cpu_has_work(CPUState *cpu) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - g_assert(cc->has_work); - return cc->has_work(cpu); -} - /** * qemu_cpu_is_self: * @cpu: The vCPU to check against. @@ -809,9 +949,7 @@ void cpu_interrupt(CPUState *cpu, int mask); */ static inline void cpu_set_pc(CPUState *cpu, vaddr addr) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - cc->set_pc(cpu, addr); + cpu->cc->set_pc(cpu, addr); } /** @@ -831,6 +969,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. @@ -847,6 +993,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. @@ -946,53 +1098,6 @@ static inline bool cpu_breakpoint_test(CPUState *cpu, vaddr pc, int mask) return false; } -#if defined(CONFIG_USER_ONLY) -static inline int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, - int flags, CPUWatchpoint **watchpoint) -{ - return -ENOSYS; -} - -static inline int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, - vaddr len, int flags) -{ - return -ENOSYS; -} - -static inline void cpu_watchpoint_remove_by_ref(CPUState *cpu, - CPUWatchpoint *wp) -{ -} - -static inline void cpu_watchpoint_remove_all(CPUState *cpu, int mask) -{ -} -#else -int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, - int flags, CPUWatchpoint **watchpoint); -int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, - vaddr len, int flags); -void cpu_watchpoint_remove_by_ref(CPUState *cpu, CPUWatchpoint *watchpoint); -void cpu_watchpoint_remove_all(CPUState *cpu, int mask); -#endif - -/** - * cpu_plugin_mem_cbs_enabled() - are plugin memory callbacks enabled? - * @cs: CPUState pointer - * - * The memory callbacks are installed if a plugin has instrumented an - * instruction for memory. This can be useful to know if you want to - * force a slow path for a series of memory accesses. - */ -static inline bool cpu_plugin_mem_cbs_enabled(const CPUState *cpu) -{ -#ifdef CONFIG_PLUGIN - return !!cpu->plugin_mem_cbs; -#else - return false; -#endif -} - /** * cpu_get_address_space: * @cpu: CPU to get address space from @@ -1008,43 +1113,23 @@ G_NORETURN void cpu_abort(CPUState *cpu, const char *fmt, ...) /* $(top_srcdir)/cpu.c */ void cpu_class_init_props(DeviceClass *dc); +void cpu_exec_class_post_init(CPUClass *cc); void cpu_exec_initfn(CPUState *cpu); -void cpu_exec_realizefn(CPUState *cpu, Error **errp); +void cpu_vmstate_register(CPUState *cpu); +void cpu_vmstate_unregister(CPUState *cpu); +bool cpu_exec_realizefn(CPUState *cpu, Error **errp); void cpu_exec_unrealizefn(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 NEED_CPU_H - -#ifndef CONFIG_USER_ONLY +void cpu_exec_reset_hold(CPUState *cpu); extern const VMStateDescription vmstate_cpu_common; -#define VMSTATE_CPU() { \ - .name = "parent_obj", \ - .size = sizeof(CPUState), \ - .vmsd = &vmstate_cpu_common, \ - .flags = VMS_STRUCT, \ - .offset = 0, \ -} -#endif /* !CONFIG_USER_ONLY */ - -#endif /* NEED_CPU_H */ - #define UNASSIGNED_CPU_INDEX -1 #define UNASSIGNED_CLUSTER_INDEX -1 +enum CacheType { + DATA_CACHE, + INSTRUCTION_CACHE, + UNIFIED_CACHE +}; + #endif diff --git a/include/hw/core/resetcontainer.h b/include/hw/core/resetcontainer.h new file mode 100644 index 00000000000..daeb18c1ea8 --- /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_SIMPLE_TYPE(ResettableContainer, 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 ee169b872ca..877892373f9 100644 --- a/include/hw/core/sysemu-cpu-ops.h +++ b/include/hw/core/sysemu-cpu-ops.h @@ -7,8 +7,8 @@ * See the COPYING file in the top-level directory. */ -#ifndef SYSEMU_CPU_OPS_H -#define SYSEMU_CPU_OPS_H +#ifndef SYSTEM_CPU_OPS_H +#define SYSTEM_CPU_OPS_H #include "hw/core/cpu.h" @@ -16,10 +16,14 @@ * struct SysemuCPUOps: System operations specific to a CPU class */ typedef struct SysemuCPUOps { + /** + * @has_work: Callback for checking if there is work to do. + */ + bool (*has_work)(CPUState *cpu); /* MANDATORY NON-NULL */ /** * @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. @@ -89,4 +93,4 @@ typedef struct SysemuCPUOps { } SysemuCPUOps; -#endif /* SYSEMU_CPU_OPS_H */ +#endif /* SYSTEM_CPU_OPS_H */ diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h deleted file mode 100644 index 479713a36e3..00000000000 --- a/include/hw/core/tcg-cpu-ops.h +++ /dev/null @@ -1,221 +0,0 @@ -/* - * TCG CPU-specific operations - * - * 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 TCG_CPU_OPS_H -#define TCG_CPU_OPS_H - -#include "hw/core/cpu.h" - -struct TCGCPUOps { - /** - * @initialize: Initialize TCG state - * - * Called when the first CPU is realized. - */ - void (*initialize)(void); - /** - * @synchronize_from_tb: Synchronize state from a TCG #TranslationBlock - * - * This is called when we abandon execution of a TB before starting it, - * and must set all parts of the CPU state which the previous TB in the - * chain may not have updated. - * By default, when this is NULL, a call is made to @set_pc(tb->pc). - * - * If more state needs to be restored, the target must implement a - * function to restore all the state, and register it here. - */ - void (*synchronize_from_tb)(CPUState *cpu, const TranslationBlock *tb); - /** - * @restore_state_to_opc: Synchronize state from INDEX_op_start_insn - * - * This is called when we unwind state in the middle of a TB, - * usually before raising an exception. Set all part of the CPU - * state which are tracked insn-by-insn in the target-specific - * arguments to start_insn, passed as @data. - */ - void (*restore_state_to_opc)(CPUState *cpu, const TranslationBlock *tb, - const uint64_t *data); - - /** @cpu_exec_enter: Callback for cpu_exec preparation */ - void (*cpu_exec_enter)(CPUState *cpu); - /** @cpu_exec_exit: Callback for cpu_exec cleanup */ - void (*cpu_exec_exit)(CPUState *cpu); - /** @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) - /** - * @fake_user_interrupt: Callback for 'fake exception' handling. - * - * Simulate 'fake exception' which will be handled outside the - * cpu execution loop (hack for x86 user mode). - */ - void (*fake_user_interrupt)(CPUState *cpu); -#else - /** - * @do_interrupt: Callback for interrupt handling. - */ - void (*do_interrupt)(CPUState *cpu); -#endif /* !CONFIG_USER_ONLY || !TARGET_I386 */ -#ifdef CONFIG_USER_ONLY - /** - * 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 - /** @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec */ - bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request); - /** - * @tlb_fill: Handle a softmmu tlb miss - * - * If the access is valid, call tlb_set_page and return true; - * if the access is invalid and probe is true, return false; - * otherwise raise an exception and do not return. - */ - bool (*tlb_fill)(CPUState *cpu, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); - /** - * @do_transaction_failed: Callback for handling failed memory transactions - * (ie bus faults or external aborts; not MMU faults) - */ - void (*do_transaction_failed)(CPUState *cpu, hwaddr physaddr, vaddr addr, - unsigned size, MMUAccessType access_type, - int mmu_idx, MemTxAttrs attrs, - MemTxResult response, uintptr_t retaddr); - /** - * @do_unaligned_access: Callback for unaligned access handling - * The callback must exit via raising an exception. - */ - G_NORETURN void (*do_unaligned_access)(CPUState *cpu, vaddr addr, - MMUAccessType access_type, - int mmu_idx, uintptr_t retaddr); - - /** - * @adjust_watchpoint_address: hack for cpu_check_watchpoint used by ARM - */ - vaddr (*adjust_watchpoint_address)(CPUState *cpu, vaddr addr, int len); - - /** - * @debug_check_watchpoint: return true if the architectural - * watchpoint whose address has matched should really fire, used by ARM - * and RISC-V - */ - bool (*debug_check_watchpoint)(CPUState *cpu, CPUWatchpoint *wp); - - /** - * @debug_check_breakpoint: return true if the architectural - * breakpoint whose PC has matched should really fire. - */ - bool (*debug_check_breakpoint)(CPUState *cpu); - - /** - * @io_recompile_replay_branch: Callback for cpu_io_recompile. - * - * The cpu has been stopped, and cpu_restore_state_from_tb has been - * called. If the faulting instruction is in a delay slot, and the - * target architecture requires re-execution of the branch, then - * adjust the cpu state as required and return true. - */ - bool (*io_recompile_replay_branch)(CPUState *cpu, - const TranslationBlock *tb); -#endif /* !CONFIG_USER_ONLY */ -#endif /* NEED_CPU_H */ - -}; - -#if defined(CONFIG_USER_ONLY) - -static inline void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, - MemTxAttrs atr, int fl, uintptr_t ra) -{ -} - -static inline int cpu_watchpoint_address_matches(CPUState *cpu, - vaddr addr, vaddr len) -{ - return 0; -} - -#else - -/** - * cpu_check_watchpoint: - * @cpu: cpu context - * @addr: guest virtual address - * @len: access length - * @attrs: memory access attributes - * @flags: watchpoint access type - * @ra: unwind return address - * - * Check for a watchpoint hit in [addr, addr+len) of the type - * specified by @flags. Exit via exception with a hit. - */ -void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, - MemTxAttrs attrs, int flags, uintptr_t ra); - -/** - * cpu_watchpoint_address_matches: - * @cpu: cpu context - * @addr: guest virtual address - * @len: access length - * - * Return the watchpoint flags that apply to [addr, addr+len). - * If no watchpoint is registered for the range, the result is 0. - */ -int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len); - -#endif - -#endif /* TCG_CPU_OPS_H */ diff --git a/include/hw/cris/etraxfs.h b/include/hw/cris/etraxfs.h deleted file mode 100644 index 467b529dc06..00000000000 --- a/include/hw/cris/etraxfs.h +++ /dev/null @@ -1,54 +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" -#include "qapi/error.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 095d76b9560..00000000000 --- 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 56c9e7676e2..75e47b68644 100644 --- a/include/hw/cxl/cxl.h +++ b/include/hw/cxl/cxl.h @@ -29,7 +29,7 @@ typedef struct PXBCXLDev PXBCXLDev; typedef struct CXLFixedWindow { uint64_t size; char **targets; - PXBCXLDev *target_hbs[8]; + PXBCXLDev *target_hbs[16]; uint8_t num_targets; uint8_t enc_int_ways; uint8_t enc_int_gran; @@ -61,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 7f676386859..17a09066dc3 100644 --- a/include/hw/cxl/cxl_cdat.h +++ b/include/hw/cxl/cxl_cdat.h @@ -16,17 +16,17 @@ /* * 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; @@ -35,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; @@ -82,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 { @@ -95,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 { @@ -122,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 { @@ -130,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 */ @@ -139,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 42c7e581a72..945ee6ffd04 100644 --- a/include/hw/cxl/cxl_component.h +++ b/include/hw/cxl/cxl_component.h @@ -10,7 +10,7 @@ #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 @@ -25,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) @@ -59,8 +62,9 @@ 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 @@ -94,22 +98,26 @@ REG32(CXL_RAS_COR_ERR_STATUS, CXL_RAS_REGISTERS_OFFSET + 0xc) 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) @@ -132,9 +140,18 @@ REG32(CXL_RAS_ERR_HEADER0, CXL_RAS_REGISTERS_OFFSET + 0x18) 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) @@ -143,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) +/* Support 4 decoders at all levels of topology */ +#define CXL_HDM_DECODER_COUNT 4 + HDM_DECODER_INIT(0); +HDM_DECODER_INIT(1); +HDM_DECODER_INIT(2); +HDM_DECODER_INIT(3); -/* 8.2.5.13 - CXL Extended Security Capability Structure (Root complex only) */ +/* + * 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 { @@ -175,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 @@ -225,31 +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 f717e3f384a..ed6cd50c672 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -58,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 \ @@ -83,7 +95,7 @@ (CXL_DEVICE_CAP_REG_SIZE + CXL_DEVICE_STATUS_REGISTERS_LENGTH + \ CXL_MAILBOX_REGISTERS_LENGTH + CXL_MEMORY_DEVICE_REGISTERS_LENGTH) -/* 8.2.8.4.5.1 Command Return Codes */ +/* CXL r3.1 Table 8-34: Command Return Codes */ typedef enum { CXL_MBOX_SUCCESS = 0x0, CXL_MBOX_BG_STARTED = 0x1, @@ -108,9 +120,33 @@ typedef enum { CXL_MBOX_INCORRECT_PASSPHRASE = 0x14, CXL_MBOX_UNSUPPORTED_MAILBOX = 0x15, CXL_MBOX_INVALID_PAYLOAD_LENGTH = 0x16, - CXL_MBOX_MAX = 0x17 + 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; @@ -127,10 +163,53 @@ typedef struct CXLEventLog { 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 */ + bool aborted; + uint64_t starttime; + /* set by each bg cmd, cleared by the bg_timer when complete */ + uint64_t runtime; + QEMUTimer *timer; + QemuMutex lock; /* serializes mbox abort vs timer cb */ + } 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; + bool initialized; +} CXLCCI; + typedef struct cxl_device_state { MemoryRegion device_registers; - /* mmio for device capabilities array - 8.2.8.2 */ + /* CXL r3.1 Section 8.2.8.3: Device Status Registers */ struct { MemoryRegion device; union { @@ -150,23 +229,22 @@ 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; @@ -174,25 +252,30 @@ typedef struct cxl_device_state { } timestamp; /* memory region size, HDM */ - uint64_t mem_size; + 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, int msi_n); +void cxl_device_register_init_swcci(CSWMBCCIDev *sw, int msi_n); /* - * 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) @@ -203,17 +286,18 @@ void cxl_event_set_status(CXLDeviceState *cxl_dstate, CXLEventLogType log_type, /* * 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 further 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) \ @@ -231,8 +315,23 @@ CXL_DEVICE_CAPABILITY_HEADER_REGISTER(MEMORY_DEVICE, CXL_DEVICE_CAP_HDR1_OFFSET + CXL_DEVICE_CAP_REG_SIZE * 2) -void 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_destroy_cci(CXLCCI *cci); +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); + +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 { \ @@ -251,45 +350,51 @@ void cxl_process_mailbox(CXLDeviceState *cxl_dstate); CAP_LENGTH, CXL_##reg##_REGISTERS_LENGTH); \ } while (0) -/* CXL 3.0 8.2.8.3.1 Event Status 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) -/* CXL 2.0 8.2.8.4.3 Mailbox Capabilities Register */ +#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) @@ -297,10 +402,34 @@ 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[32]; + uint32_t header[CXL_RAS_ERR_HEADER_NUM]; } CXLError; typedef QTAILQ_HEAD(, CXLError) CXLErrorList; @@ -317,6 +446,115 @@ typedef struct 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 CXLSanitizeInfo; + +typedef struct CXLAlertConfig { + uint8_t valid_alerts; + uint8_t enable_alerts; + uint8_t life_used_crit_alert_thresh; + uint8_t life_used_warn_thresh; + uint16_t over_temp_crit_alert_thresh; + uint16_t under_temp_crit_alert_thresh; + uint16_t over_temp_warn_thresh; + uint16_t under_temp_warn_thresh; + uint16_t cor_vmem_err_warn_thresh; + uint16_t cor_pmem_err_warn_thresh; +} QEMU_PACKED CXLAlertConfig; + struct CXLType3Dev { /* Private */ PCIDevice parent_obj; @@ -333,6 +571,16 @@ struct CXLType3Dev { 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; + + CXLAlertConfig alert_config; + + /* PCIe link characteristics */ + PCIExpLinkSpeed speed; + PCIExpLinkWidth width; /* DOE */ DOECap doe_cdat; @@ -345,6 +593,38 @@ struct CXLType3Dev { 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; + + struct CXLSanitizeInfo *media_op_sanitize; }; #define TYPE_CXL_TYPE3 "cxl-type3" @@ -361,9 +641,21 @@ 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); + 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, @@ -376,12 +668,39 @@ 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, - uint16_t *len); + 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 index 089ba2091ff..38cadaa0f34 100644 --- a/include/hw/cxl/cxl_events.h +++ b/include/hw/cxl/cxl_events.h @@ -13,7 +13,7 @@ #include "qemu/uuid.h" /* - * CXL rev 3.0 section 8.2.9.2.2; Table 8-49 + * 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. @@ -29,7 +29,7 @@ typedef enum CXLEventLogType { /* * Common Event Record Format - * CXL rev 3.0 section 8.2.9.2.1; Table 8-42 + * 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 { @@ -52,7 +52,7 @@ typedef struct CXLEventRecordRaw { /* * Get Event Records output payload - * CXL rev 3.0 section 8.2.9.2.2; Table 8-50 + * 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) @@ -70,7 +70,7 @@ typedef struct CXLGetEventPayload { /* * Clear Event Records input payload - * CXL rev 3.0 section 8.2.9.2.3; Table 8-51 + * CXL r3.1 section 8.2.9.2.3; Table 8-54 */ typedef struct CXLClearEventPayload { uint8_t event_log; /* CXLEventLogType */ @@ -80,10 +80,10 @@ typedef struct CXLClearEventPayload { uint16_t handle[]; } CXLClearEventPayload; -/** +/* * Event Interrupt Policy * - * CXL rev 3.0 section 8.2.9.2.4; Table 8-52 + * CXL r3.1 section 8.2.9.2.4; Table 8-55 */ typedef enum CXLEventIntMode { CXL_INT_NONE = 0x00, @@ -92,7 +92,8 @@ typedef enum CXLEventIntMode { 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) +#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; @@ -105,7 +106,7 @@ typedef struct CXLEventInterruptPolicy { /* * General Media Event Record - * CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43 + * 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 @@ -125,7 +126,7 @@ typedef struct CXLEventGenMedia { /* * DRAM Event Record - * CXL Rev 3.0 Section 8.2.9.2.1.2: Table 8-44 + * CXL r3.1 Section 8.2.9.2.1.2: Table 8-46 * All fields little endian. */ typedef struct CXLEventDram { @@ -148,7 +149,7 @@ typedef struct CXLEventDram { /* * Memory Module Event Record - * CXL Rev 3.0 Section 8.2.9.2.1.3: Table 8-45 + * CXL r3.1 Section 8.2.9.2.1.3: Table 8-47 * All fields little endian. */ typedef struct CXLEventMemoryModule { @@ -165,4 +166,22 @@ typedef struct CXLEventMemoryModule { 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_mailbox.h b/include/hw/cxl/cxl_mailbox.h new file mode 100644 index 00000000000..9008402d1c4 --- /dev/null +++ b/include/hw/cxl/cxl_mailbox.h @@ -0,0 +1,19 @@ +/* + * 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) +#define CXL_MBOX_BACKGROUND_OPERATION_ABORT (1 << 7) + +#endif diff --git a/include/hw/cxl/cxl_pci.h b/include/hw/cxl/cxl_pci.h index 407be95b9e5..d0855ed78b0 100644 --- a/include/hw/cxl/cxl_pci.h +++ b/include/hw/cxl/cxl_pci.h @@ -16,9 +16,8 @@ #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 @@ -29,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 @@ -55,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; @@ -82,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; @@ -100,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; @@ -116,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; @@ -124,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 38671afffd5..49541bf08f4 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 5b330188354..00000000000 --- 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 00000000000..4377f77bb75 --- /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 27cebefc9e7..0fae1f33a65 100644 --- a/include/hw/display/macfb.h +++ b/include/hw/display/macfb.h @@ -13,7 +13,7 @@ #ifndef MACFB_H #define MACFB_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/irq.h" #include "hw/nubus/nubus.h" #include "hw/sysbus.h" diff --git a/include/hw/display/ramfb.h b/include/hw/display/ramfb.h index b33a2c467b2..a7e00191445 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 f9263bf98a6..00000000000 --- 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 f652345d65a..33b6286d5a4 100644 --- a/include/hw/dma/i8257.h +++ b/include/hw/dma/i8257.h @@ -2,7 +2,7 @@ #define HW_I8257_H #include "hw/isa/isa.h" -#include "exec/ioport.h" +#include "system/ioport.h" #include "qom/object.h" #define TYPE_I8257 "i8257" @@ -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/xlnx-zdma.h b/include/hw/dma/xlnx-zdma.h index efc75217d59..9c57c499102 100644 --- a/include/hw/dma/xlnx-zdma.h +++ b/include/hw/dma/xlnx-zdma.h @@ -31,7 +31,7 @@ #include "hw/sysbus.h" #include "hw/register.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qom/object.h" #define ZDMA_R_MAX (0x204 / 4) diff --git a/include/hw/dma/xlnx_dpdma.h b/include/hw/dma/xlnx_dpdma.h index 40537a848b4..484b2e377f0 100644 --- a/include/hw/dma/xlnx_dpdma.h +++ b/include/hw/dma/xlnx_dpdma.h @@ -26,8 +26,7 @@ #define XLNX_DPDMA_H #include "hw/sysbus.h" -#include "ui/console.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qom/object.h" #define XLNX_DPDMA_REG_ARRAY_SIZE (0x1000 >> 2) diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h deleted file mode 100644 index dffb0e73d2a..00000000000 --- 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 00000000000..9c35d1b9da6 --- /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 7f3259a6300..f066ab72629 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 00000000000..878619eafa7 --- /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 "system/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 00000000000..cceb4bd6f18 --- /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 "system/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 00000000000..b634ecd3938 --- /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 "system/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 00000000000..f34765ed80b --- /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 "system/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 00000000000..12519073cd3 --- /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 "system/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 904eecf62c4..e6b2fe71b50 100644 --- a/include/hw/gpio/aspeed_gpio.h +++ b/include/hw/gpio/aspeed_gpio.h @@ -70,11 +70,14 @@ typedef struct AspeedGPIOReg { } AspeedGPIOReg; struct AspeedGPIOClass { - SysBusDevice parent_obj; + SysBusDeviceClass parent_class; const GPIOSetProperties *props; 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 00000000000..f2a57a697f2 --- /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/npcm7xx_gpio.h b/include/hw/gpio/npcm7xx_gpio.h index b1d771bd776..7c0bf61a960 100644 --- a/include/hw/gpio/npcm7xx_gpio.h +++ b/include/hw/gpio/npcm7xx_gpio.h @@ -15,7 +15,7 @@ #ifndef NPCM7XX_GPIO_H #define NPCM7XX_GPIO_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.h" /* Number of pins managed by each controller. */ 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 b6f4e264fed..c36525f0c3f 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 00000000000..54bfc4c4c7a --- /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 00000000000..602c4a90e0d --- /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 00000000000..3291d7dbbcf --- /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 00000000000..878bd19fc9b --- /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/hw.h b/include/hw/hw.h index 045c1c8b09b..1b33d12b7fc 100644 --- a/include/hw/hw.h +++ b/include/hw/hw.h @@ -1,10 +1,6 @@ #ifndef QEMU_HW_H #define QEMU_HW_H -#ifdef CONFIG_USER_ONLY -#error Cannot include hw/hw.h from user emulation -#endif - G_NORETURN void hw_error(const char *fmt, ...) G_GNUC_PRINTF(1, 2); #endif diff --git a/include/hw/hyperv/dynmem-proto.h b/include/hw/hyperv/dynmem-proto.h new file mode 100644 index 00000000000..68b8b606f26 --- /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 00000000000..c1efe70fc23 --- /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-proto.h b/include/hw/hyperv/hyperv-proto.h index 4a2297307b0..fffc5ce342f 100644 --- a/include/hw/hyperv/hyperv-proto.h +++ b/include/hw/hyperv/hyperv-proto.h @@ -61,6 +61,18 @@ #define HV_MESSAGE_X64_APIC_EOI 0x80010004 #define HV_MESSAGE_X64_LEGACY_FP_ERROR 0x80010005 +/* + * Hyper-V Synthetic debug options MSR + */ +#define HV_X64_MSR_SYNDBG_CONTROL 0x400000F1 +#define HV_X64_MSR_SYNDBG_STATUS 0x400000F2 +#define HV_X64_MSR_SYNDBG_SEND_BUFFER 0x400000F3 +#define HV_X64_MSR_SYNDBG_RECV_BUFFER 0x400000F4 +#define HV_X64_MSR_SYNDBG_PENDING_BUFFER 0x400000F5 +#define HV_X64_MSR_SYNDBG_OPTIONS 0x400000FF + +#define HV_X64_SYNDBG_OPTION_USE_HCALLS BIT(2) + /* * Message flags */ diff --git a/include/hw/hyperv/hyperv.h b/include/hw/hyperv/hyperv.h index 015c3524b1c..63a8b65278f 100644 --- a/include/hw/hyperv/hyperv.h +++ b/include/hw/hyperv/hyperv.h @@ -10,7 +10,8 @@ #ifndef HW_HYPERV_HYPERV_H #define HW_HYPERV_HYPERV_H -#include "cpu-qom.h" +#include "exec/hwaddr.h" +#include "hw/core/cpu.h" #include "hw/hyperv/hyperv-proto.h" typedef struct HvSintRoute HvSintRoute; @@ -139,4 +140,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 5c505852f22..06b948bbb08 100644 --- a/include/hw/hyperv/vmbus.h +++ b/include/hw/hyperv/vmbus.h @@ -10,8 +10,8 @@ #ifndef HW_HYPERV_VMBUS_H #define HW_HYPERV_VMBUS_H -#include "sysemu/sysemu.h" -#include "sysemu/dma.h" +#include "system/system.h" +#include "system/dma.h" #include "hw/qdev-core.h" #include "migration/vmstate.h" #include "hw/hyperv/vmbus-proto.h" diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h index a064479e599..2c4c81bd209 100644 --- a/include/hw/i2c/aspeed_i2c.h +++ b/include/hw/i2c/aspeed_i2c.h @@ -31,12 +31,14 @@ #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) @@ -225,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; @@ -239,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 { @@ -257,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; @@ -275,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) @@ -363,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 00000000000..0a56df4720b --- /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/npcm7xx_smbus.h b/include/hw/i2c/npcm7xx_smbus.h index dc45963c0e7..9c544c561b7 100644 --- a/include/hw/i2c/npcm7xx_smbus.h +++ b/include/hw/i2c/npcm7xx_smbus.h @@ -16,7 +16,7 @@ #ifndef NPCM7XX_SMBUS_H #define NPCM7XX_SMBUS_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/i2c/i2c.h" #include "hw/irq.h" #include "hw/sysbus.h" diff --git a/include/hw/i2c/pm_smbus.h b/include/hw/i2c/pm_smbus.h index 0d74207efb5..dafe0df4f69 100644 --- a/include/hw/i2c/pm_smbus.h +++ b/include/hw/i2c/pm_smbus.h @@ -1,7 +1,7 @@ #ifndef PM_SMBUS_H #define PM_SMBUS_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/i2c/smbus_master.h" #define PM_SMBUS_MAX_MSG_SIZE 32 diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h index 93f5d57c9dc..f195c113845 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 00000000000..85e96ff480d --- /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 bdc15a7a731..eb606d60760 100644 --- a/include/hw/i386/apic.h +++ b/include/hw/i386/apic.h @@ -3,14 +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 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); @@ -18,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 5f2ba24bfcd..429278da618 100644 --- a/include/hw/i386/apic_internal.h +++ b/include/hw/i386/apic_internal.h @@ -22,7 +22,7 @@ #define QEMU_APIC_INTERNAL_H #include "cpu.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/timer.h" #include "target/i386/cpu-qom.h" #include "qom/object.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 { diff --git a/include/hw/i386/hostmem-epc.h b/include/hw/i386/hostmem-epc.h index 846c7260854..3988deca854 100644 --- a/include/hw/i386/hostmem-epc.h +++ b/include/hw/i386/hostmem-epc.h @@ -12,7 +12,7 @@ #ifndef QEMU_HOSTMEM_EPC_H #define QEMU_HOSTMEM_EPC_H -#include "sysemu/hostmem.h" +#include "system/hostmem.h" #define TYPE_MEMORY_BACKEND_EPC "memory-backend-epc" diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 7fa0a695c87..e95477e8554 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -45,8 +45,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(IntelIOMMUState, INTEL_IOMMU_DEVICE) #define DMAR_REG_SIZE 0x230 #define VTD_HOST_AW_39BIT 39 #define VTD_HOST_AW_48BIT 48 -#define VTD_HOST_ADDRESS_WIDTH VTD_HOST_AW_39BIT +#define VTD_HOST_ADDRESS_WIDTH VTD_HOST_AW_48BIT #define VTD_HAW_MASK(aw) ((1ULL << (aw)) - 1) +#define VTD_MGAW_FROM_CAP(cap) ((cap >> 16) & 0x3fULL) #define DMAR_REPORT_F_INTR (1) @@ -152,9 +153,10 @@ struct VTDIOTLBEntry { uint64_t gfn; uint16_t domain_id; uint32_t pasid; - uint64_t slpte; + uint64_t pte; uint64_t mask; uint8_t access_flags; + uint8_t pgtt; }; /* VT-d Source-ID Qualifier types */ @@ -262,6 +264,7 @@ struct IntelIOMMUState { bool caching_mode; /* RO - is cap CM enabled? */ bool scalable_mode; /* RO - is Scalable Mode supported? */ + bool flts; /* RO - is stage-1 translation supported? */ bool snoop_control; /* RO - is SNP filed supported? */ dma_addr_t root; /* Current root table pointer */ @@ -292,6 +295,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 */ @@ -303,6 +308,10 @@ struct IntelIOMMUState { bool dma_drain; /* Whether DMA r/w draining enabled */ bool dma_translation; /* Whether DMA translation supported */ bool pasid; /* Whether to support PASID */ + bool fs1gp; /* First Stage 1-GByte Page Support */ + + /* Transient Mapping, Reserved(0) since VTD spec revision 3.2 */ + bool stale_tm; /* * Protects IOMMU states in general. Currently it protects the diff --git a/include/hw/i386/microvm.h b/include/hw/i386/microvm.h index fad97a891dc..b9ac34a3ef1 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 00000000000..885163ff640 --- /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_hash[QCRYPTO_HASH_DIGEST_LEN_SHA384]; + /* kernel + boot ramdisk + cmdline SHA384 hash */ + uint8_t bootstrap_hash[QCRYPTO_HASH_DIGEST_LEN_SHA384]; + /* application ramdisk(s) SHA384 hash */ + uint8_t app_hash[QCRYPTO_HASH_DIGEST_LEN_SHA384]; + /* certificate fingerprint SHA384 hash */ + uint8_t fingerprint_hash[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 0fabece236c..79b72c54dd3 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -12,10 +12,9 @@ #include "hw/hotplug.h" #include "qom/object.h" #include "hw/i386/sgx-epc.h" -#include "hw/firmware/smbios.h" #include "hw/cxl/cxl.h" -#define HPET_INTCAP "hpet-intcap" +#define MAX_IDE_BUS 2 /** * PCMachineState: @@ -32,16 +31,18 @@ typedef struct PCMachineState { Notifier machine_done; /* Pointers to devices and objects: */ - PCIBus *bus; + PCIBus *pcibus; I2CBus *smbus; PFlashCFI01 *flash[2]; ISADevice *pcspk; DeviceState *iommu; + BusState *idebus[MAX_IDE_BUS]; /* Configuration options: */ uint64_t max_ram_below_4g; OnOffAuto vmport; SmbiosEntryPointType smbios_entry_point_type; + const char *south_bridge; bool acpi_build_enabled; bool smbus_enabled; @@ -49,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 */ @@ -72,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 @@ -91,7 +88,7 @@ struct PCMachineClass { /* Device configuration: */ bool pci_enabled; - bool kvmclock_enabled; + const char *default_south_bridge; /* Compat options: */ @@ -100,23 +97,18 @@ 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; @@ -127,8 +119,11 @@ struct PCMachineClass { /* create kvmclock device even when KVM PV features are not exposed */ bool kvmclock_create_always; - /* resizable acpi blob compat */ - bool resizable_acpi_blob; + /* + * whether the machine type implements broken 32-bit address space bound + * check for memory. + */ + bool broken_32bit_mem_addr_check; }; #define TYPE_PC_MACHINE "generic-pc-machine" @@ -139,12 +134,9 @@ 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" @@ -156,7 +148,36 @@ 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, + +} 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); @@ -173,9 +194,6 @@ void pc_basic_device_init(struct PCMachineState *pcms, 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); @@ -193,13 +211,24 @@ bool pc_system_ovmf_table_find(const char *entry, uint8_t **data, int *data_len); void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size); -/* hw/i386/acpi-common.c */ -void pc_madt_cpu_entry(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_10_0[]; +extern const size_t pc_compat_10_0_len; + +extern GlobalProperty pc_compat_9_2[]; +extern const size_t pc_compat_9_2_len; + +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; @@ -269,45 +298,13 @@ extern const size_t pc_compat_2_7_len; extern GlobalProperty pc_compat_2_6[]; extern const size_t pc_compat_2_6_len; -extern GlobalProperty pc_compat_2_5[]; -extern const size_t pc_compat_2_5_len; - -extern GlobalProperty pc_compat_2_4[]; -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; - -int pc_machine_kvm_type(MachineState *machine, const char *vm_type); - #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ - static void pc_machine_##suffix##_class_init(ObjectClass *oc, void *data) \ + static void pc_machine_##suffix##_class_init(ObjectClass *oc, \ + const void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ optsfn(mc); \ mc->init = initfn; \ - mc->kvm_type = pc_machine_kvm_type; \ } \ static const TypeInfo pc_machine_type_##suffix = { \ .name = namestr TYPE_MACHINE_SUFFIX, \ @@ -316,8 +313,38 @@ int pc_machine_kvm_type(MachineState *machine, const char *vm_type); }; \ static void pc_machine_init_##suffix(void) \ { \ - type_register(&pc_machine_type_##suffix); \ + type_register_static(&pc_machine_type_##suffix); \ } \ type_init(pc_machine_init_##suffix) +#define DEFINE_PC_VER_MACHINE(namesym, namestr, initfn, isdefault, malias, ...) \ + 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, \ + const 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__); \ + mc->is_default = isdefault; \ + mc->alias = malias; \ + } \ + 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_static(&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 3e00efd870c..41d55da4799 100644 --- a/include/hw/i386/sgx-epc.h +++ b/include/hw/i386/sgx-epc.h @@ -58,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/tdvf.h b/include/hw/i386/tdvf.h new file mode 100644 index 00000000000..e75c8d1acc6 --- /dev/null +++ b/include/hw/i386/tdvf.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025 Intel Corporation + * Author: Isaku Yamahata + * + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_I386_TDVF_H +#define HW_I386_TDVF_H + +#include "qemu/osdep.h" + +#define TDVF_SECTION_TYPE_BFV 0 +#define TDVF_SECTION_TYPE_CFV 1 +#define TDVF_SECTION_TYPE_TD_HOB 2 +#define TDVF_SECTION_TYPE_TEMP_MEM 3 + +#define TDVF_SECTION_ATTRIBUTES_MR_EXTEND (1U << 0) +#define TDVF_SECTION_ATTRIBUTES_PAGE_AUG (1U << 1) + +typedef struct TdxFirmwareEntry { + uint32_t data_offset; + uint32_t data_len; + uint64_t address; + uint64_t size; + uint32_t type; + uint32_t attributes; + + void *mem_ptr; +} TdxFirmwareEntry; + +typedef struct TdxFirmware { + void *mem_ptr; + + uint32_t nr_entries; + TdxFirmwareEntry *entries; +} TdxFirmware; + +#define for_each_tdx_fw_entry(fw, e) \ + for (e = (fw)->entries; e != (fw)->entries + (fw)->nr_entries; e++) + +int tdvf_parse_metadata(TdxFirmware *fw, void *flash_ptr, int size); + +#endif /* HW_I386_TDVF_H */ diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h index 81573f6cfde..f6380f1ed75 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,40 +96,48 @@ 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 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 Die_ID field */ static inline unsigned apicid_die_offset(X86CPUTopoInfo *topo_info) { - return apicid_core_offset(topo_info) + apicid_core_width(topo_info); + return apicid_module_offset(topo_info) + apicid_module_width(topo_info); } -/* Bit offset of the Pkg_ID (socket ID) field - */ +/* Bit offset of the Pkg_ID (socket ID) field */ static inline unsigned apicid_pkg_offset(X86CPUTopoInfo *topo_info) { return apicid_die_offset(topo_info) + apicid_die_width(topo_info); } -/* Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID +/* + * Make APIC ID for the CPU based on topology and IDs of each topology level. * - * The caller must make sure core_id < nr_cores and smt_id < nr_threads. + * The caller must make sure the ID of each level doesn't exceed the width of + * the level. */ static inline apic_id_t x86_apicid_from_topo_ids(X86CPUTopoInfo *topo_info, const X86CPUTopoIDs *topo_ids) { 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 +145,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 +172,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 +194,38 @@ 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); +} + +static inline unsigned x86_module_per_pkg(X86CPUTopoInfo *topo_info) +{ + return topo_info->modules_per_die * topo_info->dies_per_pkg; +} + +static inline unsigned x86_cores_per_pkg(X86CPUTopoInfo *topo_info) +{ + return topo_info->cores_per_module * x86_module_per_pkg(topo_info); +} + +static inline unsigned x86_threads_per_pkg(X86CPUTopoInfo *topo_info) +{ + return topo_info->threads_per_core * x86_cores_per_pkg(topo_info); +} + +static inline unsigned x86_threads_per_module(X86CPUTopoInfo *topo_info) +{ + return topo_info->threads_per_core * topo_info->cores_per_module; +} + +static inline unsigned x86_threads_per_die(X86CPUTopoInfo *topo_info) +{ + return x86_threads_per_module(topo_info) * topo_info->modules_per_die; +} + #endif /* HW_I386_TOPOLOGY_H */ diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index da19ae15463..fc460b82f82 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -18,22 +18,21 @@ #define HW_I386_X86_H #include "exec/hwaddr.h" +#include "system/memory.h" #include "hw/boards.h" +#include "hw/i386/topology.h" #include "hw/intc/ioapic.h" #include "hw/isa/isa.h" #include "qom/object.h" struct X86MachineClass { - /*< private >*/ MachineClass parent; - /*< public >*/ - - /* TSC rate migration: */ - bool save_tsc_khz; /* use DMA capable linuxboot option rom */ bool fwcfg_dma_enabled; + /* CPU and apic information: */ + bool apic_xrupt_override; }; struct X86MachineState { @@ -50,6 +49,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; @@ -57,7 +68,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; @@ -95,16 +105,11 @@ struct X86MachineState { #define TYPE_X86_MACHINE MACHINE_TYPE_NAME("x86") OBJECT_DECLARE_TYPE(X86MachineState, X86MachineClass, X86_MACHINE) -uint32_t x86_cpu_apic_id_from_index(X86MachineState *pcms, +void init_topo_info(X86CPUTopoInfo *topo_info, const X86MachineState *x86ms); +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); @@ -115,7 +120,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, @@ -138,10 +145,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/ide.h b/include/hw/ide.h deleted file mode 100644 index db963bdb770..00000000000 --- a/include/hw/ide.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef HW_IDE_H -#define HW_IDE_H - -#include "exec/memory.h" - -/* 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 00000000000..face1a9a4a4 --- /dev/null +++ b/include/hw/ide/ahci-pci.h @@ -0,0 +1,24 @@ +/* + * 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" +#include "hw/irq.h" + +#define TYPE_ICH9_AHCI "ich9-ahci" +OBJECT_DECLARE_SIMPLE_TYPE(AHCIPCIState, ICH9_AHCI) + +struct AHCIPCIState { + PCIDevice parent_obj; + + AHCIState ahci; + IRQState irq; +}; + +#endif diff --git a/include/hw/ide/ahci-sysbus.h b/include/hw/ide/ahci-sysbus.h new file mode 100644 index 00000000000..06eaac8cb60 --- /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 210e5e734cf..cd07b87811b 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 "system/memory.h" typedef struct AHCIDevice AHCIDevice; @@ -38,51 +37,18 @@ typedef struct AHCIControlRegs { } AHCIControlRegs; typedef struct AHCIState { - DeviceState *container; - AHCIDevice *dev; AHCIControlRegs control_regs; MemoryRegion mem; 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 00000000000..121b455fcdb --- /dev/null +++ b/include/hw/ide/ide-bus.h @@ -0,0 +1,42 @@ +#ifndef HW_IDE_BUS_H +#define HW_IDE_BUS_H + +#include "system/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 00000000000..92e88687800 --- /dev/null +++ b/include/hw/ide/ide-dev.h @@ -0,0 +1,186 @@ +/* + * ide device definitions + * + * Copyright (c) 2009 Gerd Hoffmann + * + * This code is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef IDE_DEV_H +#define IDE_DEV_H + +#include "system/dma.h" +#include "hw/qdev-properties.h" +#include "hw/block/block.h" + +typedef struct IDEDevice IDEDevice; +typedef struct IDEState IDEState; +typedef struct IDEBus IDEBus; + +typedef void EndTransferFunc(IDEState *); + +#define MAX_IDE_DEVS 2 + +#define TYPE_IDE_DEVICE "ide-device" +OBJECT_DECLARE_TYPE(IDEDevice, IDEDeviceClass, IDE_DEVICE) + +typedef enum { IDE_HD, IDE_CD, IDE_CFATA } IDEDriveKind; + +struct unreported_events { + bool eject_request; + bool new_media; +}; + +enum ide_dma_cmd { + IDE_DMA_READ = 0, + IDE_DMA_WRITE, + IDE_DMA_TRIM, + IDE_DMA_ATAPI, + IDE_DMA__COUNT +}; + +/* NOTE: IDEState represents in fact one drive */ +struct IDEState { + IDEBus *bus; + uint8_t unit; + /* ide config */ + IDEDriveKind drive_kind; + int drive_heads, drive_sectors; + int cylinders, heads, sectors, chs_trans; + int64_t nb_sectors; + int mult_sectors; + int identify_set; + uint8_t identify_data[512]; + int drive_serial; + char drive_serial_str[21]; + char drive_model_str[41]; + bool win2k_install_hack; + uint64_t wwn; + /* ide regs */ + uint8_t feature; + uint8_t error; + uint32_t nsector; + uint8_t sector; + uint8_t lcyl; + uint8_t hcyl; + /* other part of tf for lba48 support */ + uint8_t hob_feature; + uint8_t hob_nsector; + uint8_t hob_sector; + uint8_t hob_lcyl; + uint8_t hob_hcyl; + + uint8_t select; + uint8_t status; + + bool io8; + bool reset_reverts; + + /* set for lba48 access */ + uint8_t lba48; + BlockBackend *blk; + char version[9]; + /* ATAPI specific */ + struct unreported_events events; + uint8_t sense_key; + uint8_t asc; + bool tray_open; + bool tray_locked; + uint8_t cdrom_changed; + int packet_transfer_size; + int elementary_transfer_size; + int32_t io_buffer_index; + int lba; + int cd_sector_size; + int atapi_dma; /* true if dma is requested for the packet cmd */ + BlockAcctCookie acct; + BlockAIOCB *pio_aiocb; + QEMUIOVector qiov; + QLIST_HEAD(, IDEBufferedRequest) buffered_requests; + /* ATA DMA state */ + uint64_t io_buffer_offset; + int32_t io_buffer_size; + QEMUSGList sg; + /* PIO transfer handling */ + int req_nb_sectors; /* number of sectors per interrupt */ + EndTransferFunc *end_transfer_func; + uint8_t *data_ptr; + uint8_t *data_end; + uint8_t *io_buffer; + /* PIO save/restore */ + int32_t io_buffer_total_len; + int32_t cur_io_buffer_offset; + int32_t cur_io_buffer_len; + uint8_t end_transfer_fn_idx; + QEMUTimer *sector_write_timer; /* only used for win2k install hack */ + uint32_t irq_count; /* counts IRQs when using win2k install hack */ + /* CF-ATA extended error */ + uint8_t ext_error; + /* CF-ATA metadata storage */ + uint32_t mdata_size; + uint8_t *mdata_storage; + int media_changed; + enum ide_dma_cmd dma_cmd; + /* SMART */ + uint8_t smart_enabled; + uint8_t smart_autosave; + int smart_errors; + uint8_t smart_selftest_count; + uint8_t *smart_selftest_data; + /* AHCI */ + int ncq_queues; +}; + +struct IDEDeviceClass { + DeviceClass parent_class; + void (*realize)(IDEDevice *dev, Error **errp); +}; + +struct IDEDevice { + DeviceState qdev; + uint32_t unit; + BlockConf conf; + int chs_trans; + char *version; + char *serial; + char *model; + uint64_t wwn; + /* + * 0x0000 - rotation rate not reported + * 0x0001 - non-rotating medium (SSD) + * 0x0002-0x0400 - reserved + * 0x0401-0xffe - rotations per minute + * 0xffff - reserved + */ + uint16_t rotation_rate; + bool win2k_install_hack; +}; + +typedef struct IDEDrive { + IDEDevice dev; +} IDEDrive; + +#define DEFINE_IDE_DEV_PROPERTIES() \ + DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf), \ + DEFINE_BLOCK_ERROR_PROPERTIES(IDEDrive, dev.conf), \ + DEFINE_PROP_STRING("ver", IDEDrive, dev.version), \ + DEFINE_PROP_UINT64("wwn", IDEDrive, dev.wwn, 0), \ + DEFINE_PROP_STRING("serial", IDEDrive, dev.serial),\ + DEFINE_PROP_STRING("model", IDEDrive, dev.model) + +void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp); + +void ide_drive_get(DriveInfo **hd, int max_bus); + +#endif diff --git a/include/hw/ide/ide-dma.h b/include/hw/ide/ide-dma.h new file mode 100644 index 00000000000..d0b19ac9c57 --- /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 2bfa7533d6a..00000000000 --- a/include/hw/ide/internal.h +++ /dev/null @@ -1,656 +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 "hw/ide.h" -#include "sysemu/dma.h" -#include "hw/block/block.h" -#include "exec/ioport.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 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 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; /* 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_DEVICE "ide-device" -OBJECT_DECLARE_TYPE(IDEDevice, IDEDeviceClass, IDE_DEVICE) - -struct IDEDeviceClass { - DeviceClass parent_class; - void (*realize)(IDEDevice *dev, Error **errp); -}; - -struct IDEDevice { - DeviceState qdev; - uint32_t unit; - BlockConf conf; - int chs_trans; - char *version; - char *serial; - char *model; - uint64_t wwn; - /* - * 0x0000 - rotation rate not reported - * 0x0001 - non-rotating medium (SSD) - * 0x0002-0x0400 - reserved - * 0x0401-0xffe - rotations per minute - * 0xffff - reserved - */ - uint16_t rotation_rate; -}; - -/* These are used for the error_status field of IDEBus */ -#define IDE_RETRY_MASK 0xf8 -#define IDE_RETRY_DMA 0x08 -#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, 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_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); - -/* hw/ide/qdev.c */ -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); - -int ide_handle_rw_error(IDEState *s, int error, int op); - -#endif /* HW_IDE_INTERNAL_H */ diff --git a/include/hw/ide/pci.h b/include/hw/ide/pci.h index 1ff469de876..ef03764caaf 100644 --- a/include/hw/ide/pci.h +++ b/include/hw/ide/pci.h @@ -1,7 +1,7 @@ #ifndef HW_IDE_PCI_H #define HW_IDE_PCI_H -#include "hw/ide/internal.h" +#include "hw/ide/ide-bus.h" #include "hw/pci/pci_device.h" #include "qom/object.h" @@ -61,6 +61,7 @@ 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 6f6aa2406aa..00000000000 --- 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 9fb3f8d7875..e90f008b667 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 e0e5d5ef20e..00000000000 --- 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/stellaris_gamepad.h b/include/hw/input/stellaris_gamepad.h new file mode 100644 index 00000000000..51085e166ca --- /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 00eca17674e..00000000000 --- 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, const MouseTransformInfo *info); -void tsc210x_key_event(uWireSlave *chip, int key, int down); - -/* tsc2005.c */ -void *tsc2005_init(qemu_irq pintdav); -uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len); -void tsc2005_set_transform(void *opaque, const MouseTransformInfo *info); - -#endif diff --git a/include/hw/intc/arm_gic.h b/include/hw/intc/arm_gic.h index 48f6a51a70a..be923f7ed88 100644 --- a/include/hw/intc/arm_gic.h +++ b/include/hw/intc/arm_gic.h @@ -27,6 +27,9 @@ * implement the security extensions * + QOM property "has-virtualization-extensions": set true if the GIC should * implement the virtualization extensions + * + QOM property "first-cpu-index": index of the first cpu attached to the + * GIC (default 0). The CPUs connected to the GIC are assumed to be + * first-cpu-index, first-cpu-index + 1, ... first-cpu-index + num-cpu - 1. * + unnamed GPIO inputs: (where P is number of SPIs, i.e. num-irq - 32) * [0..P-1] SPIs * [P..P+31] PPIs for CPU 0 diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h index 70803750081..93a3cc2bf81 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 @@ -127,6 +129,8 @@ struct GICState { uint32_t num_lrs; uint32_t num_cpu; + /* cpu_index of the first CPU, attached to this GIC. */ + uint32_t first_cpu_index; MemoryRegion iomem; /* Distributor */ /* This is just so we can have an opaque pointer which identifies diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index 4e2fb518e72..a3d6a0e5077 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; diff --git a/include/hw/intc/armv7m_nvic.h b/include/hw/intc/armv7m_nvic.h index 6b4ae566c9a..7b9964fe7e4 100644 --- a/include/hw/intc/armv7m_nvic.h +++ b/include/hw/intc/armv7m_nvic.h @@ -10,7 +10,7 @@ #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" @@ -189,21 +189,7 @@ int armv7m_nvic_raw_execution_priority(NVICState *s); * @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 00000000000..51288384a56 --- /dev/null +++ b/include/hw/intc/aspeed_intc.h @@ -0,0 +1,67 @@ +/* + * 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" +#define TYPE_ASPEED_2700_INTCIO TYPE_ASPEED_INTC "io-ast2700" +#define TYPE_ASPEED_2700SSP_INTC TYPE_ASPEED_INTC "-ast2700ssp" +#define TYPE_ASPEED_2700SSP_INTCIO TYPE_ASPEED_INTC "io-ast2700ssp" +#define TYPE_ASPEED_2700TSP_INTC TYPE_ASPEED_INTC "-ast2700tsp" +#define TYPE_ASPEED_2700TSP_INTCIO TYPE_ASPEED_INTC "io-ast2700tsp" + +OBJECT_DECLARE_TYPE(AspeedINTCState, AspeedINTCClass, ASPEED_INTC) + +#define ASPEED_INTC_MAX_INPINS 10 +#define ASPEED_INTC_MAX_OUTPINS 19 + +typedef struct AspeedINTCIRQ { + int inpin_idx; + int outpin_idx; + int num_outpins; + uint32_t enable_reg; + uint32_t status_reg; +} AspeedINTCIRQ; + +struct AspeedINTCState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + MemoryRegion iomem_container; + + uint32_t *regs; + OrIRQState orgates[ASPEED_INTC_MAX_INPINS]; + qemu_irq output_pins[ASPEED_INTC_MAX_OUTPINS]; + + uint32_t enable[ASPEED_INTC_MAX_INPINS]; + uint32_t mask[ASPEED_INTC_MAX_INPINS]; + uint32_t pending[ASPEED_INTC_MAX_INPINS]; +}; + +struct AspeedINTCClass { + SysBusDeviceClass parent_class; + + uint32_t num_lines; + uint32_t num_inpins; + uint32_t num_outpins; + uint64_t mem_size; + uint64_t nr_regs; + uint64_t reg_offset; + const MemoryRegionOps *reg_ops; + const AspeedINTCIRQ *irq_table; + int irq_table_count; +}; + +#endif /* ASPEED_INTC_H */ diff --git a/include/hw/intc/grlib_irqmp.h b/include/hw/intc/grlib_irqmp.h new file mode 100644 index 00000000000..a76acbf9403 --- /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 c4125757753..1f2420231f1 100644 --- a/include/hw/intc/i8259.h +++ b/include/hw/intc/i8259.h @@ -3,6 +3,8 @@ /* i8259.c */ +typedef struct PICCommonState PICCommonState; + extern PICCommonState *isa_pic; /* diff --git a/include/hw/intc/intc.h b/include/hw/intc/intc.h index 7018f608ca5..e40194b8e3b 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/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index fbdef9a7b3b..9be1d736ea6 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -5,60 +5,29 @@ * Copyright (C) 2021 Loongson Technology Corporation Limited */ -#include "hw/sysbus.h" -#include "hw/loongarch/virt.h" - #ifndef LOONGARCH_EXTIOI_H #define LOONGARCH_EXTIOI_H -#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 -#define EXTIOI_IRQS_NODETYPE_COUNT 16 -#define EXTIOI_IRQS_GROUP_COUNT 8 +#include "hw/intc/loongarch_extioi_common.h" -#define APIC_OFFSET 0x400 -#define APIC_BASE (0x1000ULL + APIC_OFFSET) +#define TYPE_LOONGARCH_EXTIOI "loongarch.extioi" +OBJECT_DECLARE_TYPE(LoongArchExtIOIState, LoongArchExtIOIClass, LOONGARCH_EXTIOI) -#define EXTIOI_NODETYPE_START (0x4a0 - APIC_OFFSET) -#define EXTIOI_NODETYPE_END (0x4c0 - APIC_OFFSET) -#define EXTIOI_IPMAP_START (0x4c0 - APIC_OFFSET) -#define EXTIOI_IPMAP_END (0x4c8 - APIC_OFFSET) -#define EXTIOI_ENABLE_START (0x600 - APIC_OFFSET) -#define EXTIOI_ENABLE_END (0x620 - APIC_OFFSET) -#define EXTIOI_BOUNCE_START (0x680 - APIC_OFFSET) -#define EXTIOI_BOUNCE_END (0x6a0 - APIC_OFFSET) -#define EXTIOI_ISR_START (0x700 - APIC_OFFSET) -#define EXTIOI_ISR_END (0x720 - APIC_OFFSET) -#define EXTIOI_COREISR_START (0x800 - APIC_OFFSET) -#define EXTIOI_COREISR_END (0xB20 - APIC_OFFSET) -#define EXTIOI_COREMAP_START (0xC00 - APIC_OFFSET) -#define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET) +struct LoongArchExtIOIState { + LoongArchExtIOICommonState parent_obj; + int dev_fd; +}; -#define TYPE_LOONGARCH_EXTIOI "loongarch.extioi" -OBJECT_DECLARE_SIMPLE_TYPE(LoongArchExtIOI, LOONGARCH_EXTIOI) -struct LoongArchExtIOI { - SysBusDevice parent_obj; - /* 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[EXTIOI_CPUS][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[EXTIOI_CPUS][LS3A_INTC_IP], EXTIOI_IRQS); - uint8_t sw_ipmap[EXTIOI_IRQS_IPMAP_SIZE]; - uint8_t sw_coremap[EXTIOI_IRQS]; - qemu_irq parent_irq[EXTIOI_CPUS][LS3A_INTC_IP]; - qemu_irq irq[EXTIOI_IRQS]; - MemoryRegion extioi_iocsr_mem[EXTIOI_CPUS]; - MemoryRegion extioi_system_mem; +struct LoongArchExtIOIClass { + LoongArchExtIOICommonClass parent_class; + + DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; + ResettablePhases parent_phases; }; + +void kvm_extioi_realize(DeviceState *dev, Error **errp); +int kvm_extioi_get(void *opaque); +int kvm_extioi_put(void *opaque, int version_id); + #endif /* LOONGARCH_EXTIOI_H */ diff --git a/include/hw/intc/loongarch_extioi_common.h b/include/hw/intc/loongarch_extioi_common.h new file mode 100644 index 00000000000..dca25ff893e --- /dev/null +++ b/include/hw/intc/loongarch_extioi_common.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch 3A5000 ext interrupt controller definitions + * Copyright (C) 2024 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_EXTIOI_COMMON_H +#define LOONGARCH_EXTIOI_COMMON_H + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "hw/loongarch/virt.h" + +#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 +#define EXTIOI_IRQS_NODETYPE_COUNT 16 +#define EXTIOI_IRQS_GROUP_COUNT 8 + +#define APIC_OFFSET 0x400 +#define APIC_BASE (0x1000ULL + APIC_OFFSET) +#define EXTIOI_NODETYPE_START (0x4a0 - APIC_OFFSET) +#define EXTIOI_NODETYPE_END (0x4c0 - APIC_OFFSET) +#define EXTIOI_IPMAP_START (0x4c0 - APIC_OFFSET) +#define EXTIOI_IPMAP_END (0x4c8 - APIC_OFFSET) +#define EXTIOI_ENABLE_START (0x600 - APIC_OFFSET) +#define EXTIOI_ENABLE_END (0x620 - APIC_OFFSET) +#define EXTIOI_BOUNCE_START (0x680 - APIC_OFFSET) +#define EXTIOI_BOUNCE_END (0x6a0 - APIC_OFFSET) +#define EXTIOI_ISR_START (0x700 - APIC_OFFSET) +#define EXTIOI_ISR_END (0x720 - APIC_OFFSET) +#define EXTIOI_COREISR_START (0x800 - APIC_OFFSET) +#define EXTIOI_COREISR_END (0x820 - 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) + +#define TYPE_LOONGARCH_EXTIOI_COMMON "loongarch_extioi_common" +OBJECT_DECLARE_TYPE(LoongArchExtIOICommonState, + LoongArchExtIOICommonClass, LOONGARCH_EXTIOI_COMMON) + +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]; + uint64_t arch_id; + CPUState *cpu; +} ExtIOICore; + +struct LoongArchExtIOICommonState { + 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 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]; + uint8_t sw_ipmap[EXTIOI_IRQS_IPMAP_SIZE]; + uint8_t sw_coremap[EXTIOI_IRQS]; + qemu_irq irq[EXTIOI_IRQS]; + ExtIOICore *cpu; + MemoryRegion extioi_system_mem; + MemoryRegion virt_extend; +}; + +struct LoongArchExtIOICommonClass { + SysBusDeviceClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; + int (*pre_save)(void *s); + int (*post_load)(void *s, int version_id); +}; +#endif /* LOONGARCH_EXTIOI_H */ diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongarch_ipi.h index 6c6194786e8..5175a6b0044 100644 --- a/include/hw/intc/loongarch_ipi.h +++ b/include/hw/intc/loongarch_ipi.h @@ -1,53 +1,32 @@ /* 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) - -#define 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[IPI_MBX_NUM * 2]; - qemu_irq irq; -} IPICore; +struct LoongarchIPIState { + LoongsonIPICommonState parent_obj; + int dev_fd; +}; -struct LoongArchIPI { - SysBusDevice parent_obj; - MemoryRegion ipi_iocsr_mem; - MemoryRegion ipi64_iocsr_mem; - IPICore ipi_core; +struct LoongarchIPIClass { + LoongsonIPICommonClass parent_class; + DeviceRealize parent_realize; + ResettablePhases parent_phases; }; +void kvm_ipi_realize(DeviceState *dev, Error **errp); +int kvm_ipi_get(void *opaque); +int kvm_ipi_put(void *opaque, int version_id); + #endif diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h index d5437e88f28..a46b6f8985d 100644 --- a/include/hw/intc/loongarch_pch_pic.h +++ b/include/hw/intc/loongarch_pch_pic.h @@ -5,65 +5,29 @@ * Copyright (c) 2021 Loongson Technology Corporation Limited */ -#include "hw/sysbus.h" +#ifndef HW_LOONGARCH_PCH_PIC_H +#define HW_LOONGARCH_PCH_PIC_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) +#include "hw/intc/loongarch_pic_common.h" -#define PCH_PIC_INT_ID_VAL 0x7000000UL -#define PCH_PIC_INT_ID_VER 0x1UL +#define TYPE_LOONGARCH_PIC "loongarch_pic" +#define PCH_PIC_NAME(name) TYPE_LOONGARCH_PIC#name +OBJECT_DECLARE_TYPE(LoongarchPICState, LoongarchPICClass, LOONGARCH_PIC) -#define PCH_PIC_INT_ID_LO 0x00 -#define PCH_PIC_INT_ID_HI 0x04 -#define PCH_PIC_INT_MASK_LO 0x20 -#define PCH_PIC_INT_MASK_HI 0x24 -#define PCH_PIC_HTMSI_EN_LO 0x40 -#define PCH_PIC_HTMSI_EN_HI 0x44 -#define PCH_PIC_INT_EDGE_LO 0x60 -#define PCH_PIC_INT_EDGE_HI 0x64 -#define PCH_PIC_INT_CLEAR_LO 0x80 -#define PCH_PIC_INT_CLEAR_HI 0x84 -#define PCH_PIC_AUTO_CTRL0_LO 0xc0 -#define PCH_PIC_AUTO_CTRL0_HI 0xc4 -#define PCH_PIC_AUTO_CTRL1_LO 0xe0 -#define PCH_PIC_AUTO_CTRL1_HI 0xe4 -#define PCH_PIC_ROUTE_ENTRY_OFFSET 0x100 -#define PCH_PIC_ROUTE_ENTRY_END 0x13f -#define PCH_PIC_HTMSI_VEC_OFFSET 0x200 -#define PCH_PIC_HTMSI_VEC_END 0x23f -#define PCH_PIC_INT_STATUS_LO 0x3a0 -#define PCH_PIC_INT_STATUS_HI 0x3a4 -#define PCH_PIC_INT_POL_LO 0x3e0 -#define PCH_PIC_INT_POL_HI 0x3e4 - -#define STATUS_LO_START 0 -#define STATUS_HI_START 0x4 -#define POL_LO_START 0x40 -#define POL_HI_START 0x44 -struct LoongArchPCHPIC { - SysBusDevice parent_obj; - qemu_irq parent_irq[64]; - uint64_t int_mask; /*0x020 interrupt mask register*/ - uint64_t htmsi_en; /*0x040 1=msi*/ - uint64_t intedge; /*0x060 edge=1 level =0*/ - uint64_t intclr; /*0x080 for clean edge int,set 1 clean,set 0 is noused*/ - uint64_t auto_crtl0; /*0x0c0*/ - uint64_t auto_crtl1; /*0x0e0*/ - uint64_t last_intirr; /* edge detection */ - uint64_t intirr; /* 0x380 interrupt request register */ - uint64_t intisr; /* 0x3a0 interrupt service register */ - /* - * 0x3e0 interrupt level polarity selection - * register 0 for high level trigger - */ - uint64_t int_polarity; +struct LoongarchPICState { + LoongArchPICCommonState parent_obj; + int dev_fd; +}; - uint8_t route_entry[64]; /*0x100 - 0x138*/ - uint8_t htmsi_vector[64]; /*0x200 - 0x238*/ +struct LoongarchPICClass { + LoongArchPICCommonClass parent_class; - MemoryRegion iomem32_low; - MemoryRegion iomem32_high; - MemoryRegion iomem8; - unsigned int irq_num; + DeviceRealize parent_realize; + ResettablePhases parent_phases; }; + +void kvm_pic_realize(DeviceState *dev, Error **errp); +int kvm_pic_get(void *opaque); +int kvm_pic_put(void *opaque, int version_id); + +#endif /* HW_LOONGARCH_PCH_PIC_H */ diff --git a/include/hw/intc/loongarch_pic_common.h b/include/hw/intc/loongarch_pic_common.h new file mode 100644 index 00000000000..f774c975d40 --- /dev/null +++ b/include/hw/intc/loongarch_pic_common.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch 7A1000 I/O interrupt controller definitions + * Copyright (c) 2024 Loongson Technology Corporation Limited + */ + +#ifndef HW_LOONGARCH_PIC_COMMON_H +#define HW_LOONGARCH_PIC_COMMON_H + +#include "hw/pci-host/ls7a.h" +#include "hw/sysbus.h" + +#define PCH_PIC_INT_ID 0x00 +#define PCH_PIC_INT_ID_VAL 0x7 +#define PCH_PIC_INT_ID_VER 0x1 +#define PCH_PIC_INT_MASK 0x20 +#define PCH_PIC_HTMSI_EN 0x40 +#define PCH_PIC_INT_EDGE 0x60 +#define PCH_PIC_INT_CLEAR 0x80 +#define PCH_PIC_AUTO_CTRL0 0xc0 +#define PCH_PIC_AUTO_CTRL1 0xe0 +#define PCH_PIC_ROUTE_ENTRY 0x100 +#define PCH_PIC_ROUTE_ENTRY_END 0x13f +#define PCH_PIC_HTMSI_VEC 0x200 +#define PCH_PIC_HTMSI_VEC_END 0x23f +#define PCH_PIC_INT_REQUEST 0x380 +#define PCH_PIC_INT_STATUS 0x3a0 +#define PCH_PIC_INT_POL 0x3e0 + +#define TYPE_LOONGARCH_PIC_COMMON "loongarch_pic_common" +OBJECT_DECLARE_TYPE(LoongArchPICCommonState, + LoongArchPICCommonClass, LOONGARCH_PIC_COMMON) + +union LoongArchPIC_ID { + struct { + uint8_t _reserved_0[3]; + uint8_t id; + uint8_t version; + uint8_t _reserved_1; + uint8_t irq_num; + uint8_t _reserved_2; + } QEMU_PACKED desc; + uint64_t data; +}; + +struct LoongArchPICCommonState { + SysBusDevice parent_obj; + + qemu_irq parent_irq[64]; + union LoongArchPIC_ID id; /* 0x00 interrupt ID register */ + uint64_t int_mask; /* 0x020 interrupt mask register */ + uint64_t htmsi_en; /* 0x040 1=msi */ + uint64_t intedge; /* 0x060 edge=1 level=0 */ + uint64_t intclr; /* 0x080 clean edge int, set 1 clean, 0 noused */ + uint64_t auto_crtl0; /* 0x0c0 */ + uint64_t auto_crtl1; /* 0x0e0 */ + uint64_t last_intirr; /* edge detection */ + uint64_t intirr; /* 0x380 interrupt request register */ + uint64_t intisr; /* 0x3a0 interrupt service register */ + /* + * 0x3e0 interrupt level polarity selection + * register 0 for high level trigger + */ + uint64_t int_polarity; + + uint8_t route_entry[64]; /* 0x100 - 0x138 */ + uint8_t htmsi_vector[64]; /* 0x200 - 0x238 */ + + MemoryRegion iomem; + unsigned int irq_num; +}; + +struct LoongArchPICCommonClass { + SysBusDeviceClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; + int (*pre_save)(LoongArchPICCommonState *s); + int (*post_load)(LoongArchPICCommonState *s, int version_id); +}; +#endif /* HW_LOONGARCH_PIC_COMMON_H */ diff --git a/include/hw/intc/loongson_ipi.h b/include/hw/intc/loongson_ipi.h new file mode 100644 index 00000000000..4e517cc8dc4 --- /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 00000000000..e58ce2aa1c4 --- /dev/null +++ b/include/hw/intc/loongson_ipi_common.h @@ -0,0 +1,79 @@ +/* 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; + uint64_t arch_id; + CPUState *cpu; +} 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); + int (*cpu_by_arch_id)(LoongsonIPICommonState *lics, int64_t id, + int *index, CPUState **pcs); + int (*pre_save)(void *opaque); + int (*post_load)(void *opaque, int version_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 ef91f218122..693e33b0aa5 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/nios2_vic.h b/include/hw/intc/nios2_vic.h deleted file mode 100644 index 5c975a2ac47..00000000000 --- a/include/hw/intc/nios2_vic.h +++ /dev/null @@ -1,66 +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 - -#include "hw/sysbus.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/riscv_aplic.h b/include/hw/intc/riscv_aplic.h index de8532fbc3a..489b9133c26 100644 --- a/include/hw/intc/riscv_aplic.h +++ b/include/hw/intc/riscv_aplic.h @@ -68,9 +68,17 @@ struct RISCVAPLICState { uint32_t num_irqs; bool msimode; bool mmode; + + /* To support KVM aia=aplic-imsic with irqchip split mode */ + bool kvm_splitmode; + uint32_t kvm_msicfgaddr; + uint32_t kvm_msicfgaddrH; }; void riscv_aplic_add_child(DeviceState *parent, DeviceState *child); +bool riscv_is_kvm_aia_aplic_imsic(bool msimode); +bool riscv_use_emulated_aplic(bool msimode); +void riscv_aplic_set_kvm_msicfgaddr(RISCVAPLICState *aplic, hwaddr addr); DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, uint32_t hartid_base, uint32_t num_harts, uint32_t num_sources, diff --git a/include/hw/ipack/ipack.h b/include/hw/ipack/ipack.h index cbcdda509d3..00f397fd020 100644 --- a/include/hw/ipack/ipack.h +++ b/include/hw/ipack/ipack.h @@ -12,6 +12,7 @@ #define QEMU_IPACK_H #include "hw/qdev-core.h" +#include "hw/irq.h" #include "qom/object.h" @@ -19,10 +20,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(IPackBus, IPACK_BUS) struct IPackBus { - /*< private >*/ BusState parent_obj; - /* All fields are private */ uint8_t n_slots; uint8_t free_slot; qemu_irq_handler set_irq; @@ -58,13 +57,11 @@ struct IPackDeviceClass { }; struct IPackDevice { - /*< private >*/ DeviceState parent_obj; - /*< public >*/ int32_t slot; /* IRQ objects for the IndustryPack INT0# and INT1# */ - qemu_irq *irq; + IRQState irq[2]; }; extern const VMStateDescription vmstate_ipack_device; diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h index 77a7213ed93..cd581aa1342 100644 --- a/include/hw/ipmi/ipmi.h +++ b/include/hw/ipmi/ipmi.h @@ -25,7 +25,7 @@ #ifndef HW_IPMI_H #define HW_IPMI_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #include "qom/object.h" @@ -41,6 +41,15 @@ enum ipmi_op { IPMI_SEND_NMI }; +/* Channel properties */ +#define IPMI_CHANNEL_IPMB 0x00 +#define IPMI_CHANNEL_SYSTEM 0x0f +#define IPMI_CHANNEL_MEDIUM_IPMB 0x01 +#define IPMI_CHANNEL_MEDIUM_SYSTEM 0x0c +#define IPMI_CHANNEL_PROTOCOL_IPMB 0x01 +#define IPMI_CHANNEL_PROTOCOL_KCS 0x05 +#define IPMI_CHANNEL_PROTOCOL_BT_15 0x08 + #define IPMI_CC_INVALID_CMD 0xc1 #define IPMI_CC_COMMAND_INVALID_FOR_LUN 0xc2 #define IPMI_CC_TIMEOUT 0xc3 @@ -76,6 +85,7 @@ typedef struct IPMIFwInfo { int interface_type; uint8_t ipmi_spec_major_revision; uint8_t ipmi_spec_minor_revision; + uint8_t ipmi_channel_protocol; uint8_t i2c_slave_address; uint32_t uuid; @@ -90,6 +100,11 @@ typedef struct IPMIFwInfo { } memspace; int interrupt_number; + enum { + IPMI_NO_IRQ = 0, + IPMI_ISA_IRQ, + IPMI_PCI_IRQ, + } irq_source; enum { IPMI_LEVEL_IRQ, IPMI_EDGE_IRQ diff --git a/include/hw/irq.h b/include/hw/irq.h index 645b73d2512..b3012237acd 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,24 @@ 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); + +/** + * qemu_init_irqs: Initialize an array of IRQs. + * + * @irq: Array of IRQs to initialize + * @count: number of IRQs to initialize + * @handler: handler to assign to each IRQ + * @opaque: opaque data to pass to @handler + */ +void qemu_init_irqs(IRQState irq[], size_t count, + qemu_irq_handler handler, void *opaque); + /* Returns an array of N IRQs. Each IRQ is assigned the argument handler and * opaque data. */ diff --git a/include/hw/isa/apm.h b/include/hw/isa/apm.h index b6e070c00e9..0834539045a 100644 --- a/include/hw/isa/apm.h +++ b/include/hw/isa/apm.h @@ -1,7 +1,7 @@ #ifndef APM_H #define APM_H -#include "exec/memory.h" +#include "system/memory.h" #define APM_CNT_IOPORT 0xb2 #define ACPI_PORT_SMI_CMD APM_CNT_IOPORT diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index 40d6224a4e6..a82c5f1004f 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -3,8 +3,8 @@ /* ISA bus */ -#include "exec/memory.h" -#include "exec/ioport.h" +#include "system/memory.h" +#include "system/ioport.h" #include "hw/qdev-core.h" #include "qom/object.h" diff --git a/include/hw/isa/superio.h b/include/hw/isa/superio.h index 0dc45104d47..14d051348b4 100644 --- a/include/hw/isa/superio.h +++ b/include/hw/isa/superio.h @@ -10,7 +10,7 @@ #ifndef HW_ISA_SUPERIO_H #define HW_ISA_SUPERIO_H -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/isa/isa.h" #include "qom/object.h" diff --git a/include/hw/isa/vt82c686.h b/include/hw/isa/vt82c686.h index b6e95b2851c..da1722daf2b 100644 --- a/include/hw/isa/vt82c686.h +++ b/include/hw/isa/vt82c686.h @@ -34,4 +34,6 @@ struct ViaAC97State { uint32_t ac97_cmd; }; +void via_isa_set_irq(PCIDevice *d, int n, int level); + #endif diff --git a/include/hw/loader-fit.h b/include/hw/loader-fit.h index 0832e379dc9..9a43490ed63 100644 --- a/include/hw/loader-fit.h +++ b/include/hw/loader-fit.h @@ -30,12 +30,27 @@ struct fit_loader_match { struct fit_loader { const struct fit_loader_match *matches; hwaddr (*addr_to_phys)(void *opaque, uint64_t addr); - const void *(*fdt_filter)(void *opaque, const void *fdt, - const void *match_data, hwaddr *load_addr); + void *(*fdt_filter)(void *opaque, const void *fdt, + const void *match_data, hwaddr *load_addr); const void *(*kernel_filter)(void *opaque, const void *kernel, hwaddr *load_addr, hwaddr *entry_addr); }; -int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque); +/** + * load_fit: load a FIT format image + * @ldr: structure defining board specific properties and hooks + * @filename: image to load + * @pfdt: pointer to update with address of FDT blob + * @opaque: opaque value passed back to the hook functions in @ldr + * Returns: 0 on success, or a negative errno on failure + * + * @pfdt is used to tell the caller about the FDT blob. On return, it + * has been set to point to the FDT blob, and it is now the caller's + * responsibility to free that memory with g_free(). Usually the caller + * will want to pass in &machine->fdt here, to record the FDT blob for + * the dumpdtb option and QMP/HMP commands. + */ +int load_fit(const struct fit_loader *ldr, const char *filename, void **pfdt, + void *opaque); #endif /* HW_LOADER_FIT_H */ diff --git a/include/hw/loader.h b/include/hw/loader.h index c4c14170ea3..c96b5e141c6 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -77,15 +77,13 @@ 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 @@ -103,7 +101,7 @@ ssize_t load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz); * Returns the size of the decompressed payload if decompression was performed * successfully. */ -ssize_t unpack_efi_zboot_image(uint8_t **buffer, int *size); +ssize_t unpack_efi_zboot_image(uint8_t **buffer, ssize_t *size); #define ELF_LOAD_FAILED -1 #define ELF_LOAD_NOT_ELF -2 @@ -122,7 +120,7 @@ const char *load_elf_strerror(ssize_t error); * @lowaddr: Populated with lowest loaded address. Ignored if NULL. * @highaddr: Populated with highest loaded address. Ignored if NULL. * @pflags: Populated with ELF processor-specific flags. Ignore if NULL. - * @bigendian: Expected ELF endianness. 0 for LE otherwise BE + * @elf_data_order: Expected ELF endianness (ELFDATA2LSB or ELFDATA2MSB). * @elf_machine: Expected ELF machine type * @clear_lsb: Set to mask off LSB of addresses (Some architectures use * this for non-address data) @@ -153,30 +151,18 @@ ssize_t load_elf_ram_sym(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, - uint32_t *pflags, int big_endian, int elf_machine, + uint32_t *pflags, int elf_data_order, int elf_machine, int clear_lsb, int data_swab, AddressSpace *as, bool load_rom, symbol_fn_t sym_cb); -/** load_elf_ram: - * Same as load_elf_ram_sym(), but doesn't allow the caller to specify a - * symbol callback function - */ -ssize_t load_elf_ram(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, - int big_endian, int elf_machine, int clear_lsb, - int data_swab, AddressSpace *as, bool load_rom); - /** load_elf_as: - * Same as load_elf_ram(), but always loads the elf as ROM + * Same as load_elf_ram_sym(), but always loads the elf as ROM */ ssize_t load_elf_as(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, + uint64_t *highaddr, uint32_t *pflags, int elf_data_order, int elf_machine, int clear_lsb, int data_swab, AddressSpace *as); @@ -188,7 +174,7 @@ ssize_t load_elf(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, + uint64_t *highaddr, uint32_t *pflags, int elf_data_order, int elf_machine, int clear_lsb, int data_swab); /** load_elf_hdr: @@ -204,7 +190,7 @@ ssize_t load_elf(const char *filename, void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp); ssize_t load_aout(const char *filename, hwaddr addr, int max_sz, - int bswap_needed, hwaddr target_page_size); + bool big_endian, hwaddr target_page_size); #define LOAD_UIMAGE_LOADADDR_INVALID (-1) @@ -272,7 +258,7 @@ void pstrcpy_targphys(const char *name, 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, @@ -284,8 +270,6 @@ int rom_add_elf_program(const char *name, GMappedFile *mapped_file, void *data, AddressSpace *as); int rom_check_and_register_reset(void); void rom_set_fw(FWCfgState *f); -void rom_set_order_override(int order); -void rom_reset_order_override(void); /** * rom_transaction_begin: @@ -338,7 +322,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 00000000000..9819f7fbe30 --- /dev/null +++ b/include/hw/loongarch/boot.h @@ -0,0 +1,118 @@ +/* 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; + uint64_t initrd_addr; + uint64_t initrd_size; +}; + +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 f1659655c64..602feab0f0d 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -8,37 +8,40 @@ #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_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_FLASH_SECTOR_SIZE (128 * KiB) -#define VIRT_FLASH_BASE 0x1d000000UL -#define VIRT_FLASH_SIZE (16 * 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) +#define VIRT_GED_CPUHP_ADDR (VIRT_GED_REG_ADDR + ACPI_GED_REG_COUNT) -struct LoongArchMachineState { +#define COMMAND_LINE_SIZE 512 + +#define FDT_BASE 0x100000 + +struct LoongArchVirtMachineState { /*< private >*/ MachineState parent_obj; MemoryRegion lowmem; MemoryRegion highmem; - MemoryRegion isa_io; MemoryRegion bios; bool bios_loaded; /* State for other subsystems/APIs: */ @@ -46,17 +49,35 @@ struct LoongArchMachineState { 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; + PFlashCFI01 *flash[2]; + MemoryRegion system_iocsr; + MemoryRegion iocsr_mem; + AddressSpace as_iocsr; + struct loongarch_boot_info bootinfo; + DeviceState *ipi; + DeviceState *extioi; + struct memmap_entry *memmap_table; + unsigned int memmap_entries; }; -#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 virt_acpi_setup(LoongArchVirtMachineState *lvms); +void virt_fdt_setup(LoongArchVirtMachineState *lvms); + +static inline bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms) +{ + if (lvms->veiointc == ON_OFF_AUTO_OFF) { + return false; + } + return true; +} + #endif diff --git a/include/hw/m68k/mcf.h b/include/hw/m68k/mcf.h index 8cbd587bbfb..5d9f876ffeb 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 index a35efc1c534..04fac25f6c2 100644 --- a/include/hw/m68k/q800-glue.h +++ b/include/hw/m68k/q800-glue.h @@ -23,7 +23,6 @@ #ifndef HW_Q800_GLUE_H #define HW_Q800_GLUE_H -#include "qemu/osdep.h" #include "hw/sysbus.h" #define TYPE_GLUE "q800-glue" @@ -35,7 +34,7 @@ struct GLUEState { M68kCPU *cpu; uint8_t ipr; uint8_t auxmode; - qemu_irq irqs[1]; + qemu_irq irqs[2]; QEMUTimer *nmi_release; }; @@ -44,7 +43,9 @@ struct GLUEState { #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 index b3d77f1cba6..9caaed9692b 100644 --- a/include/hw/m68k/q800.h +++ b/include/hw/m68k/q800.h @@ -26,7 +26,7 @@ #include "hw/boards.h" #include "qom/object.h" #include "target/m68k/cpu-qom.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/m68k/q800-glue.h" #include "hw/misc/mac_via.h" #include "hw/net/dp8393x.h" @@ -36,6 +36,9 @@ #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 @@ -44,20 +47,29 @@ 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") diff --git a/include/hw/mem/memory-device.h b/include/hw/mem/memory-device.h index 48d2611fc5e..c0a58087ccc 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/npcm7xx_mc.h b/include/hw/mem/npcm7xx_mc.h index 7ed38be2431..568cc35fdd7 100644 --- a/include/hw/mem/npcm7xx_mc.h +++ b/include/hw/mem/npcm7xx_mc.h @@ -16,7 +16,7 @@ #ifndef NPCM7XX_MC_H #define NPCM7XX_MC_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.h" /** diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h index acf887c83d9..d3b763453a5 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 322bebe555b..e0dbdd43dca 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -16,7 +16,7 @@ #ifndef QEMU_PC_DIMM_H #define QEMU_PC_DIMM_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #include "qom/object.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 44acb6815be..00000000000 --- 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/cps.h b/include/hw/mips/cps.h index 04d636246ab..05ef9f76b74 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 f7c9728fa9f..00000000000 --- 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/mips/mips.h b/include/hw/mips/mips.h index 101799f7d3e..1f3672ba5f5 100644 --- a/include/hw/mips/mips.h +++ b/include/hw/mips/mips.h @@ -7,7 +7,7 @@ /* Kernels can be configured with 64KB pages */ #define INITRD_PAGE_SIZE (64 * KiB) -#include "exec/memory.h" +#include "system/memory.h" /* bonito.c */ PCIBus *bonito_init(qemu_irq *pic); diff --git a/include/hw/misc/aspeed_hace.h b/include/hw/misc/aspeed_hace.h index ecb1b67de81..d5d07c6c021 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,16 +11,17 @@ #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" #define TYPE_ASPEED_AST2500_HACE TYPE_ASPEED_HACE "-ast2500" #define TYPE_ASPEED_AST2600_HACE TYPE_ASPEED_HACE "-ast2600" #define TYPE_ASPEED_AST1030_HACE TYPE_ASPEED_HACE "-ast1030" +#define TYPE_ASPEED_AST2700_HACE TYPE_ASPEED_HACE "-ast2700" OBJECT_DECLARE_TYPE(AspeedHACEState, AspeedHACEClass, ASPEED_HACE) -#define ASPEED_HACE_NR_REGS (0x64 >> 2) #define ASPEED_HACE_MAX_SG 256 /* max number of entries */ struct AspeedHACEState { @@ -28,23 +30,30 @@ struct AspeedHACEState { MemoryRegion iomem; qemu_irq irq; - struct iovec iov_cache[ASPEED_HACE_MAX_SG]; - uint32_t regs[ASPEED_HACE_NR_REGS]; + uint32_t *regs; uint32_t total_req_len; - uint32_t iov_count; MemoryRegion *dram_mr; AddressSpace dram_as; + + QCryptoHash *hash_ctx; }; struct AspeedHACEClass { SysBusDeviceClass parent_class; + const MemoryRegionOps *reg_ops; uint32_t src_mask; uint32_t dest_mask; uint32_t key_mask; uint32_t hash_mask; + uint64_t nr_regs; + bool raise_crypt_interrupt_workaround; + uint32_t src_hi_mask; + uint32_t dest_hi_mask; + uint32_t key_hi_mask; + bool has_dma64; }; #endif /* ASPEED_HACE_H */ diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h index 7cb6018dbc1..684b48b7222 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,6 +51,11 @@ 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 AST2700_A1_SILICON_REV 0x06010103U +#define AST2750_A1_SILICON_REV 0x06010003U #define ASPEED_IS_AST2500(si_rev) ((((si_rev) >> 24) & 0xff) == 0x04) @@ -87,7 +95,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 +118,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 +137,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 +155,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 +351,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 +377,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 ec2d59a14f5..61c979583ae 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 00000000000..23f346ab934 --- /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 03cacdee426..ccd18ce2095 100644 --- a/include/hw/misc/auxbus.h +++ b/include/hw/misc/auxbus.h @@ -25,7 +25,7 @@ #ifndef HW_MISC_AUXBUS_H #define HW_MISC_AUXBUS_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #include "qom/object.h" diff --git a/include/hw/misc/bcm2835_property.h b/include/hw/misc/bcm2835_property.h index ba8896610cc..2f93fd0c757 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,6 +27,7 @@ struct BCM2835PropertyState { MemoryRegion iomem; qemu_irq mbox_irq; BCM2835FBState *fbdev; + BCM2835OTPState *otp; MACAddr macaddr; uint32_t board_rev; diff --git a/include/hw/misc/cbus.h b/include/hw/misc/cbus.h deleted file mode 100644 index 5334984020d..00000000000 --- 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 00000000000..82d4e4a2fee --- /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 14a1d6fe6b0..1272076086a 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/imx8mp_analog.h b/include/hw/misc/imx8mp_analog.h new file mode 100644 index 00000000000..955f03215a0 --- /dev/null +++ b/include/hw/misc/imx8mp_analog.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2025 Bernhard Beschow + * + * i.MX8MP ANALOG IP block emulation code + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef IMX8MP_ANALOG_H +#define IMX8MP_ANALOG_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +enum IMX8MPAnalogRegisters { + ANALOG_AUDIO_PLL1_GEN_CTRL = 0x000 / 4, + ANALOG_AUDIO_PLL1_FDIV_CTL0 = 0x004 / 4, + ANALOG_AUDIO_PLL1_FDIV_CTL1 = 0x008 / 4, + ANALOG_AUDIO_PLL1_SSCG_CTRL = 0x00c / 4, + ANALOG_AUDIO_PLL1_MNIT_CTRL = 0x010 / 4, + ANALOG_AUDIO_PLL2_GEN_CTRL = 0x014 / 4, + ANALOG_AUDIO_PLL2_FDIV_CTL0 = 0x018 / 4, + ANALOG_AUDIO_PLL2_FDIV_CTL1 = 0x01c / 4, + ANALOG_AUDIO_PLL2_SSCG_CTRL = 0x020 / 4, + ANALOG_AUDIO_PLL2_MNIT_CTRL = 0x024 / 4, + ANALOG_VIDEO_PLL1_GEN_CTRL = 0x028 / 4, + ANALOG_VIDEO_PLL1_FDIV_CTL0 = 0x02c / 4, + ANALOG_VIDEO_PLL1_FDIV_CTL1 = 0x030 / 4, + ANALOG_VIDEO_PLL1_SSCG_CTRL = 0x034 / 4, + ANALOG_VIDEO_PLL1_MNIT_CTRL = 0x038 / 4, + ANALOG_DRAM_PLL_GEN_CTRL = 0x050 / 4, + ANALOG_DRAM_PLL_FDIV_CTL0 = 0x054 / 4, + ANALOG_DRAM_PLL_FDIV_CTL1 = 0x058 / 4, + ANALOG_DRAM_PLL_SSCG_CTRL = 0x05c / 4, + ANALOG_DRAM_PLL_MNIT_CTRL = 0x060 / 4, + ANALOG_GPU_PLL_GEN_CTRL = 0x064 / 4, + ANALOG_GPU_PLL_FDIV_CTL0 = 0x068 / 4, + ANALOG_GPU_PLL_LOCKD_CTRL = 0x06c / 4, + ANALOG_GPU_PLL_MNIT_CTRL = 0x070 / 4, + ANALOG_VPU_PLL_GEN_CTRL = 0x074 / 4, + ANALOG_VPU_PLL_FDIV_CTL0 = 0x078 / 4, + ANALOG_VPU_PLL_LOCKD_CTRL = 0x07c / 4, + ANALOG_VPU_PLL_MNIT_CTRL = 0x080 / 4, + ANALOG_ARM_PLL_GEN_CTRL = 0x084 / 4, + ANALOG_ARM_PLL_FDIV_CTL0 = 0x088 / 4, + ANALOG_ARM_PLL_LOCKD_CTRL = 0x08c / 4, + ANALOG_ARM_PLL_MNIT_CTRL = 0x090 / 4, + ANALOG_SYS_PLL1_GEN_CTRL = 0x094 / 4, + ANALOG_SYS_PLL1_FDIV_CTL0 = 0x098 / 4, + ANALOG_SYS_PLL1_LOCKD_CTRL = 0x09c / 4, + ANALOG_SYS_PLL1_MNIT_CTRL = 0x100 / 4, + ANALOG_SYS_PLL2_GEN_CTRL = 0x104 / 4, + ANALOG_SYS_PLL2_FDIV_CTL0 = 0x108 / 4, + ANALOG_SYS_PLL2_LOCKD_CTRL = 0x10c / 4, + ANALOG_SYS_PLL2_MNIT_CTRL = 0x110 / 4, + ANALOG_SYS_PLL3_GEN_CTRL = 0x114 / 4, + ANALOG_SYS_PLL3_FDIV_CTL0 = 0x118 / 4, + ANALOG_SYS_PLL3_LOCKD_CTRL = 0x11c / 4, + ANALOG_SYS_PLL3_MNIT_CTRL = 0x120 / 4, + ANALOG_OSC_MISC_CFG = 0x124 / 4, + ANALOG_ANAMIX_PLL_MNIT_CTL = 0x128 / 4, + + ANALOG_DIGPROG = 0x800 / 4, + ANALOG_MAX, +}; + +#define TYPE_IMX8MP_ANALOG "imx8mp.analog" +OBJECT_DECLARE_SIMPLE_TYPE(IMX8MPAnalogState, IMX8MP_ANALOG) + +struct IMX8MPAnalogState { + SysBusDevice parent_obj; + + struct { + MemoryRegion container; + MemoryRegion analog; + } mmio; + + uint32_t analog[ANALOG_MAX]; +}; + +#endif /* IMX8MP_ANALOG_H */ diff --git a/include/hw/misc/imx8mp_ccm.h b/include/hw/misc/imx8mp_ccm.h new file mode 100644 index 00000000000..685c8582ff8 --- /dev/null +++ b/include/hw/misc/imx8mp_ccm.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 Bernhard Beschow + * + * i.MX 8M Plus CCM IP block emulation code + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef IMX8MP_CCM_H +#define IMX8MP_CCM_H + +#include "hw/misc/imx_ccm.h" +#include "qom/object.h" + +enum IMX8MPCCMRegisters { + CCM_MAX = 0xc6fc / sizeof(uint32_t) + 1, +}; + +#define TYPE_IMX8MP_CCM "imx8mp.ccm" +OBJECT_DECLARE_SIMPLE_TYPE(IMX8MPCCMState, IMX8MP_CCM) + +struct IMX8MPCCMState { + IMXCCMState parent_obj; + + MemoryRegion iomem; + + uint32_t ccm[CCM_MAX]; +}; + +#endif /* IMX8MP_CCM_H */ diff --git a/include/hw/misc/iosb.h b/include/hw/misc/iosb.h new file mode 100644 index 00000000000..377f8ca7e2f --- /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/ivshmem-flat.h b/include/hw/misc/ivshmem-flat.h new file mode 100644 index 00000000000..09bc3abcad3 --- /dev/null +++ b/include/hw/misc/ivshmem-flat.h @@ -0,0 +1,86 @@ +/* + * Inter-VM Shared Memory Flat Device + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) 2023 Linaro Ltd. + * Authors: + * Gustavo Romero + * + */ + +#ifndef IVSHMEM_FLAT_H +#define IVSHMEM_FLAT_H + +#include "qemu/queue.h" +#include "qemu/event_notifier.h" +#include "chardev/char-fe.h" +#include "system/memory.h" +#include "qom/object.h" +#include "hw/sysbus.h" + +#define IVSHMEM_MAX_VECTOR_NUM 64 + +/* + * QEMU interface: + * + QOM property "chardev" is the character device id of the ivshmem server + * socket + * + QOM property "shmem-size" sets the size of the RAM region shared between + * the device and the ivshmem server + * + sysbus MMIO region 0: device I/O mapped registers + * + sysbus MMIO region 1: shared memory with ivshmem server + * + sysbus IRQ 0: single output interrupt + */ + +#define TYPE_IVSHMEM_FLAT "ivshmem-flat" +typedef struct IvshmemFTState IvshmemFTState; + +DECLARE_INSTANCE_CHECKER(IvshmemFTState, IVSHMEM_FLAT, TYPE_IVSHMEM_FLAT) + +/* Ivshmem registers. See ./docs/specs/ivshmem-spec.txt for details. */ +enum ivshmem_registers { + INTMASK = 0, + INTSTATUS = 4, + IVPOSITION = 8, + DOORBELL = 12, +}; + +typedef struct VectorInfo { + EventNotifier event_notifier; + uint16_t id; +} VectorInfo; + +typedef struct IvshmemPeer { + QTAILQ_ENTRY(IvshmemPeer) next; + VectorInfo vector[IVSHMEM_MAX_VECTOR_NUM]; + int vector_counter; + uint16_t id; +} IvshmemPeer; + +struct IvshmemFTState { + SysBusDevice parent_obj; + + uint64_t msg_buf; + int msg_buffered_bytes; + + QTAILQ_HEAD(, IvshmemPeer) peer; + IvshmemPeer own; + + CharBackend server_chr; + + /* IRQ */ + qemu_irq irq; + + /* I/O registers */ + MemoryRegion iomem; + uint32_t intmask; + uint32_t intstatus; + uint32_t ivposition; + uint32_t doorbell; + + /* Shared memory */ + MemoryRegion shmem; + int shmem_fd; + uint32_t shmem_size; +}; + +#endif /* IVSHMEM_FLAT_H */ diff --git a/include/hw/misc/lasi.h b/include/hw/misc/lasi.h index 0a8c7352be2..0bdfb11b501 100644 --- a/include/hw/misc/lasi.h +++ b/include/hw/misc/lasi.h @@ -12,7 +12,7 @@ #ifndef LASI_H #define LASI_H -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/pci/pci_host.h" #include "hw/boards.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 */ diff --git a/include/hw/misc/mac_via.h b/include/hw/misc/mac_via.h index 422da43bf90..6a152281505 100644 --- a/include/hw/misc/mac_via.h +++ b/include/hw/misc/mac_via.h @@ -9,7 +9,7 @@ #ifndef HW_MISC_MAC_VIA_H #define HW_MISC_MAC_VIA_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.h" #include "hw/misc/mos6522.h" #include "hw/input/adb.h" @@ -74,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/macio.h b/include/hw/misc/macio/macio.h index 86df2c2b60a..16aa95b876c 100644 --- a/include/hw/misc/macio/macio.h +++ b/include/hw/misc/macio/macio.h @@ -28,7 +28,7 @@ #include "hw/char/escc.h" #include "hw/pci/pci_device.h" -#include "hw/ide/internal.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/mips_itu.h b/include/hw/misc/mips_itu.h index 35218b2d146..27c9a1090d5 100644 --- a/include/hw/misc/mips_itu.h +++ b/include/hw/misc/mips_itu.h @@ -70,10 +70,6 @@ struct MIPSITUState { /* ITU Control Register */ uint64_t icr0; - - /* SAAR */ - uint64_t *saar; - MIPSCPU *cpu0; }; /* Get ITC Configuration Tag memory region. */ diff --git a/include/hw/misc/mos6522.h b/include/hw/misc/mos6522.h index fba45668aba..920871a598b 100644 --- a/include/hw/misc/mos6522.h +++ b/include/hw/misc/mos6522.h @@ -154,7 +154,7 @@ struct MOS6522State { OBJECT_DECLARE_TYPE(MOS6522State, MOS6522DeviceClass, MOS6522) struct MOS6522DeviceClass { - DeviceClass parent_class; + SysBusDeviceClass parent_class; ResettablePhases parent_phases; void (*portB_write)(MOS6522State *dev); diff --git a/include/hw/misc/mps2-scc.h b/include/hw/misc/mps2-scc.h index 3b2d13ac9c3..8ff188c06b1 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 deleted file mode 100644 index 5ed4a4672b3..00000000000 --- a/include/hw/misc/npcm7xx_clk.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Nuvoton NPCM7xx Clock Control Registers. - * - * Copyright 2020 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 NPCM7XX_CLK_H -#define NPCM7XX_CLK_H - -#include "exec/memory.h" -#include "hw/clock.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 NPCM7XX_CLK_NR_REGS (0x70 / sizeof(uint32_t)) - -#define NPCM7XX_WATCHDOG_RESET_GPIO_IN "npcm7xx-clk-watchdog-reset-gpio-in" - -/* Maximum amount of clock inputs in a SEL module. */ -#define NPCM7XX_CLK_SEL_MAX_INPUT 5 - -/* PLLs in CLK module. */ -typedef enum NPCM7xxClockPLL { - NPCM7XX_CLOCK_PLL0, - NPCM7XX_CLOCK_PLL1, - NPCM7XX_CLOCK_PLL2, - NPCM7XX_CLOCK_PLLG, - NPCM7XX_CLOCK_NR_PLLS, -} NPCM7xxClockPLL; - -/* SEL/MUX in CLK module. */ -typedef enum NPCM7xxClockSEL { - NPCM7XX_CLOCK_PIXCKSEL, - NPCM7XX_CLOCK_MCCKSEL, - NPCM7XX_CLOCK_CPUCKSEL, - NPCM7XX_CLOCK_CLKOUTSEL, - NPCM7XX_CLOCK_UARTCKSEL, - NPCM7XX_CLOCK_TIMCKSEL, - NPCM7XX_CLOCK_SDCKSEL, - NPCM7XX_CLOCK_GFXMSEL, - NPCM7XX_CLOCK_SUCKSEL, - NPCM7XX_CLOCK_NR_SELS, -} NPCM7xxClockSEL; - -/* Dividers in CLK module. */ -typedef enum NPCM7xxClockDivider { - NPCM7XX_CLOCK_PLL1D2, /* PLL1/2 */ - NPCM7XX_CLOCK_PLL2D2, /* PLL2/2 */ - NPCM7XX_CLOCK_MC_DIVIDER, - NPCM7XX_CLOCK_AXI_DIVIDER, - NPCM7XX_CLOCK_AHB_DIVIDER, - NPCM7XX_CLOCK_AHB3_DIVIDER, - NPCM7XX_CLOCK_SPI0_DIVIDER, - NPCM7XX_CLOCK_SPIX_DIVIDER, - NPCM7XX_CLOCK_APB1_DIVIDER, - NPCM7XX_CLOCK_APB2_DIVIDER, - NPCM7XX_CLOCK_APB3_DIVIDER, - NPCM7XX_CLOCK_APB4_DIVIDER, - NPCM7XX_CLOCK_APB5_DIVIDER, - NPCM7XX_CLOCK_CLKOUT_DIVIDER, - NPCM7XX_CLOCK_UART_DIVIDER, - NPCM7XX_CLOCK_TIMER_DIVIDER, - NPCM7XX_CLOCK_ADC_DIVIDER, - NPCM7XX_CLOCK_MMC_DIVIDER, - NPCM7XX_CLOCK_SDHC_DIVIDER, - NPCM7XX_CLOCK_GFXM_DIVIDER, /* divide by 3 */ - NPCM7XX_CLOCK_UTMI_DIVIDER, - NPCM7XX_CLOCK_NR_DIVIDERS, -} NPCM7xxClockConverter; - -typedef struct NPCM7xxCLKState NPCM7xxCLKState; - -/** - * struct NPCM7xxClockPLLState - A PLL module in CLK module. - * @name: The name of the module. - * @clk: The CLK module that owns this module. - * @clock_in: The input clock of this module. - * @clock_out: The output clock of this module. - * @reg: The control registers for this PLL module. - */ -typedef struct NPCM7xxClockPLLState { - DeviceState parent; - - const char *name; - NPCM7xxCLKState *clk; - Clock *clock_in; - Clock *clock_out; - - int reg; -} NPCM7xxClockPLLState; - -/** - * struct NPCM7xxClockSELState - A SEL module in CLK module. - * @name: The name of the module. - * @clk: The CLK module that owns this module. - * @input_size: The size of inputs of this module. - * @clock_in: The input clocks of this module. - * @clock_out: The output clocks of this module. - * @offset: The offset of this module in the control register. - * @len: The length of this module in the control register. - */ -typedef struct NPCM7xxClockSELState { - DeviceState parent; - - const char *name; - NPCM7xxCLKState *clk; - uint8_t input_size; - Clock *clock_in[NPCM7XX_CLK_SEL_MAX_INPUT]; - Clock *clock_out; - - int offset; - int len; -} NPCM7xxClockSELState; - -/** - * struct NPCM7xxClockDividerState - A Divider module in CLK module. - * @name: The name of the module. - * @clk: The CLK module that owns this module. - * @clock_in: The input clock of this module. - * @clock_out: The output clock of this module. - * @divide: The function the divider uses to divide the input. - * @reg: The index of the control register that contains the divisor. - * @offset: The offset of the divisor in the control register. - * @len: The length of the divisor in the control register. - * @divisor: The divisor for a constant divisor - */ -typedef struct NPCM7xxClockDividerState { - DeviceState parent; - - const char *name; - NPCM7xxCLKState *clk; - Clock *clock_in; - Clock *clock_out; - - uint32_t (*divide)(struct NPCM7xxClockDividerState *s); - union { - struct { - int reg; - int offset; - int len; - }; - int divisor; - }; -} NPCM7xxClockDividerState; - -struct NPCM7xxCLKState { - SysBusDevice parent; - - MemoryRegion iomem; - - /* Clock converters */ - NPCM7xxClockPLLState plls[NPCM7XX_CLOCK_NR_PLLS]; - NPCM7xxClockSELState sels[NPCM7XX_CLOCK_NR_SELS]; - NPCM7xxClockDividerState dividers[NPCM7XX_CLOCK_NR_DIVIDERS]; - - uint32_t regs[NPCM7XX_CLK_NR_REGS]; - - /* Time reference for SECCNT and CNTR25M, initialized by power on reset */ - int64_t ref_ns; - - /* The incoming reference clock. */ - Clock *clkref; -}; - -#define TYPE_NPCM7XX_CLK "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 deleted file mode 100644 index c0bbdda77e5..00000000000 --- a/include/hw/misc/npcm7xx_gcr.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Nuvoton NPCM7xx System Global Control Registers. - * - * Copyright 2020 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 NPCM7XX_GCR_H -#define NPCM7XX_GCR_H - -#include "exec/memory.h" -#include "hw/sysbus.h" - -/* - * NPCM7XX PWRON STRAP bit fields - * 12: SPI0 powered by VSBV3 at 1.8V - * 11: System flash attached to BMC - * 10: BSP alternative pins. - * 9:8: Flash UART command route enabled. - * 7: Security enabled. - * 6: HI-Z state control. - * 5: ECC disabled. - * 4: Reserved - * 3: JTAG2 enabled. - * 2:0: CPU and DRAM clock frequency. - */ -#define NPCM7XX_PWRON_STRAP_SPI0F18 BIT(12) -#define NPCM7XX_PWRON_STRAP_SFAB BIT(11) -#define NPCM7XX_PWRON_STRAP_BSPA BIT(10) -#define NPCM7XX_PWRON_STRAP_FUP(x) ((x) << 8) -#define FUP_NORM_UART2 3 -#define FUP_PROG_UART3 2 -#define FUP_PROG_UART2 1 -#define FUP_NORM_UART3 0 -#define NPCM7XX_PWRON_STRAP_SECEN BIT(7) -#define NPCM7XX_PWRON_STRAP_HIZ BIT(6) -#define NPCM7XX_PWRON_STRAP_ECC BIT(5) -#define NPCM7XX_PWRON_STRAP_RESERVE1 BIT(4) -#define NPCM7XX_PWRON_STRAP_J2EN BIT(3) -#define NPCM7XX_PWRON_STRAP_CKFRQ(x) (x) -#define CKFRQ_SKIPINIT 0x000 -#define CKFRQ_DEFAULT 0x111 - -/* - * Number of registers in our device state structure. Don't change this without - * incrementing the version_id in the vmstate. - */ -#define NPCM7XX_GCR_NR_REGS (0x148 / sizeof(uint32_t)) - -struct NPCM7xxGCRState { - SysBusDevice parent; - - MemoryRegion iomem; - - uint32_t regs[NPCM7XX_GCR_NR_REGS]; - - uint32_t reset_pwron; - uint32_t reset_mdlr; - uint32_t reset_intcr3; -}; - -#define TYPE_NPCM7XX_GCR "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 d6384382cea..e4b997a6ad3 100644 --- a/include/hw/misc/npcm7xx_mft.h +++ b/include/hw/misc/npcm7xx_mft.h @@ -16,7 +16,7 @@ #ifndef NPCM7XX_MFT_H #define NPCM7XX_MFT_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/clock.h" #include "hw/irq.h" #include "hw/sysbus.h" diff --git a/include/hw/misc/npcm_clk.h b/include/hw/misc/npcm_clk.h new file mode 100644 index 00000000000..52e972f4604 --- /dev/null +++ b/include/hw/misc/npcm_clk.h @@ -0,0 +1,195 @@ +/* + * Nuvoton NPCM7xx/8xx Clock Control Registers. + * + * Copyright 2020 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_CLK_H +#define NPCM_CLK_H + +#include "system/memory.h" +#include "hw/clock.h" +#include "hw/sysbus.h" + +#define NPCM7XX_CLK_NR_REGS (0x70 / sizeof(uint32_t)) +#define NPCM8XX_CLK_NR_REGS (0xc4 / sizeof(uint32_t)) +/* + * Number of maximum registers in NPCM device state structure. Don't change + * this without incrementing the version_id in the vmstate. + */ +#define NPCM_CLK_MAX_NR_REGS NPCM8XX_CLK_NR_REGS + +#define NPCM7XX_WATCHDOG_RESET_GPIO_IN "npcm7xx-clk-watchdog-reset-gpio-in" + +/* Maximum amount of clock inputs in a SEL module. */ +#define NPCM7XX_CLK_SEL_MAX_INPUT 5 + +/* PLLs in CLK module. */ +typedef enum NPCM7xxClockPLL { + NPCM7XX_CLOCK_PLL0, + NPCM7XX_CLOCK_PLL1, + NPCM7XX_CLOCK_PLL2, + NPCM7XX_CLOCK_PLLG, + NPCM7XX_CLOCK_NR_PLLS, +} NPCM7xxClockPLL; + +/* SEL/MUX in CLK module. */ +typedef enum NPCM7xxClockSEL { + NPCM7XX_CLOCK_PIXCKSEL, + NPCM7XX_CLOCK_MCCKSEL, + NPCM7XX_CLOCK_CPUCKSEL, + NPCM7XX_CLOCK_CLKOUTSEL, + NPCM7XX_CLOCK_UARTCKSEL, + NPCM7XX_CLOCK_TIMCKSEL, + NPCM7XX_CLOCK_SDCKSEL, + NPCM7XX_CLOCK_GFXMSEL, + NPCM7XX_CLOCK_SUCKSEL, + NPCM7XX_CLOCK_NR_SELS, +} NPCM7xxClockSEL; + +/* Dividers in CLK module. */ +typedef enum NPCM7xxClockDivider { + NPCM7XX_CLOCK_PLL1D2, /* PLL1/2 */ + NPCM7XX_CLOCK_PLL2D2, /* PLL2/2 */ + NPCM7XX_CLOCK_MC_DIVIDER, + NPCM7XX_CLOCK_AXI_DIVIDER, + NPCM7XX_CLOCK_AHB_DIVIDER, + NPCM7XX_CLOCK_AHB3_DIVIDER, + NPCM7XX_CLOCK_SPI0_DIVIDER, + NPCM7XX_CLOCK_SPIX_DIVIDER, + NPCM7XX_CLOCK_APB1_DIVIDER, + NPCM7XX_CLOCK_APB2_DIVIDER, + NPCM7XX_CLOCK_APB3_DIVIDER, + NPCM7XX_CLOCK_APB4_DIVIDER, + NPCM7XX_CLOCK_APB5_DIVIDER, + NPCM7XX_CLOCK_CLKOUT_DIVIDER, + NPCM7XX_CLOCK_UART_DIVIDER, + NPCM7XX_CLOCK_TIMER_DIVIDER, + NPCM7XX_CLOCK_ADC_DIVIDER, + NPCM7XX_CLOCK_MMC_DIVIDER, + NPCM7XX_CLOCK_SDHC_DIVIDER, + NPCM7XX_CLOCK_GFXM_DIVIDER, /* divide by 3 */ + NPCM7XX_CLOCK_UTMI_DIVIDER, + NPCM7XX_CLOCK_NR_DIVIDERS, +} NPCM7xxClockConverter; + +typedef struct NPCMCLKState NPCMCLKState; + +/** + * struct NPCM7xxClockPLLState - A PLL module in CLK module. + * @name: The name of the module. + * @clk: The CLK module that owns this module. + * @clock_in: The input clock of this module. + * @clock_out: The output clock of this module. + * @reg: The control registers for this PLL module. + */ +typedef struct NPCM7xxClockPLLState { + DeviceState parent; + + const char *name; + NPCMCLKState *clk; + Clock *clock_in; + Clock *clock_out; + + int reg; +} NPCM7xxClockPLLState; + +/** + * struct NPCM7xxClockSELState - A SEL module in CLK module. + * @name: The name of the module. + * @clk: The CLK module that owns this module. + * @input_size: The size of inputs of this module. + * @clock_in: The input clocks of this module. + * @clock_out: The output clocks of this module. + * @offset: The offset of this module in the control register. + * @len: The length of this module in the control register. + */ +typedef struct NPCM7xxClockSELState { + DeviceState parent; + + const char *name; + NPCMCLKState *clk; + uint8_t input_size; + Clock *clock_in[NPCM7XX_CLK_SEL_MAX_INPUT]; + Clock *clock_out; + + int offset; + int len; +} NPCM7xxClockSELState; + +/** + * struct NPCM7xxClockDividerState - A Divider module in CLK module. + * @name: The name of the module. + * @clk: The CLK module that owns this module. + * @clock_in: The input clock of this module. + * @clock_out: The output clock of this module. + * @divide: The function the divider uses to divide the input. + * @reg: The index of the control register that contains the divisor. + * @offset: The offset of the divisor in the control register. + * @len: The length of the divisor in the control register. + * @divisor: The divisor for a constant divisor + */ +typedef struct NPCM7xxClockDividerState { + DeviceState parent; + + const char *name; + NPCMCLKState *clk; + Clock *clock_in; + Clock *clock_out; + + uint32_t (*divide)(struct NPCM7xxClockDividerState *s); + union { + struct { + int reg; + int offset; + int len; + }; + int divisor; + }; +} NPCM7xxClockDividerState; + +struct NPCMCLKState { + SysBusDevice parent; + + MemoryRegion iomem; + + /* Clock converters */ + /* + * TODO: Implement unique clock converters for NPCM8xx. + * NPCM8xx adds a few more clock outputs. + */ + NPCM7xxClockPLLState plls[NPCM7XX_CLOCK_NR_PLLS]; + NPCM7xxClockSELState sels[NPCM7XX_CLOCK_NR_SELS]; + NPCM7xxClockDividerState dividers[NPCM7XX_CLOCK_NR_DIVIDERS]; + + uint32_t regs[NPCM_CLK_MAX_NR_REGS]; + + /* Time reference for SECCNT and CNTR25M, initialized by power on reset */ + int64_t ref_ns; + + /* The incoming reference clock. */ + Clock *clkref; +}; + +typedef struct NPCMCLKClass { + SysBusDeviceClass parent; + + size_t nr_regs; + const uint32_t *cold_reset_values; +} NPCMCLKClass; + +#define TYPE_NPCM_CLK "npcm-clk" +OBJECT_DECLARE_TYPE(NPCMCLKState, NPCMCLKClass, NPCM_CLK) +#define TYPE_NPCM7XX_CLK "npcm7xx-clk" +#define TYPE_NPCM8XX_CLK "npcm8xx-clk" + +#endif /* NPCM_CLK_H */ diff --git a/include/hw/misc/npcm_gcr.h b/include/hw/misc/npcm_gcr.h new file mode 100644 index 00000000000..702e7fddb1f --- /dev/null +++ b/include/hw/misc/npcm_gcr.h @@ -0,0 +1,86 @@ +/* + * Nuvoton NPCM7xx/8xx System Global Control Registers. + * + * Copyright 2020 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_GCR_H +#define NPCM_GCR_H + +#include "system/memory.h" +#include "hw/sysbus.h" +#include "qom/object.h" + +/* + * NPCM7XX PWRON STRAP bit fields + * 12: SPI0 powered by VSBV3 at 1.8V + * 11: System flash attached to BMC + * 10: BSP alternative pins. + * 9:8: Flash UART command route enabled. + * 7: Security enabled. + * 6: HI-Z state control. + * 5: ECC disabled. + * 4: Reserved + * 3: JTAG2 enabled. + * 2:0: CPU and DRAM clock frequency. + */ +#define NPCM7XX_PWRON_STRAP_SPI0F18 BIT(12) +#define NPCM7XX_PWRON_STRAP_SFAB BIT(11) +#define NPCM7XX_PWRON_STRAP_BSPA BIT(10) +#define NPCM7XX_PWRON_STRAP_FUP(x) ((x) << 8) +#define FUP_NORM_UART2 3 +#define FUP_PROG_UART3 2 +#define FUP_PROG_UART2 1 +#define FUP_NORM_UART3 0 +#define NPCM7XX_PWRON_STRAP_SECEN BIT(7) +#define NPCM7XX_PWRON_STRAP_HIZ BIT(6) +#define NPCM7XX_PWRON_STRAP_ECC BIT(5) +#define NPCM7XX_PWRON_STRAP_RESERVE1 BIT(4) +#define NPCM7XX_PWRON_STRAP_J2EN BIT(3) +#define NPCM7XX_PWRON_STRAP_CKFRQ(x) (x) +#define CKFRQ_SKIPINIT 0x000 +#define CKFRQ_DEFAULT 0x111 + +/* + * Number of registers in our device state structure. Don't change this without + * incrementing the version_id in the vmstate. + */ +#define NPCM_GCR_MAX_NR_REGS NPCM8XX_GCR_NR_REGS +#define NPCM7XX_GCR_NR_REGS (0x148 / sizeof(uint32_t)) +#define NPCM8XX_GCR_NR_REGS (0xf80 / sizeof(uint32_t)) + +typedef struct NPCMGCRState { + SysBusDevice parent; + + MemoryRegion iomem; + + uint32_t regs[NPCM_GCR_MAX_NR_REGS]; + + uint32_t reset_pwron; + uint32_t reset_mdlr; + uint32_t reset_intcr3; + uint32_t reset_scrpad_b; +} NPCMGCRState; + +typedef struct NPCMGCRClass { + SysBusDeviceClass parent; + + size_t nr_regs; + const uint32_t *cold_reset_values; +} NPCMGCRClass; + +#define TYPE_NPCM_GCR "npcm-gcr" +#define TYPE_NPCM7XX_GCR "npcm7xx-gcr" +#define TYPE_NPCM8XX_GCR "npcm8xx-gcr" +OBJECT_DECLARE_TYPE(NPCMGCRState, NPCMGCRClass, NPCM_GCR) + +#endif /* NPCM_GCR_H */ diff --git a/include/hw/misc/pvpanic.h b/include/hw/misc/pvpanic.h index fab94165d03..5098693437a 100644 --- a/include/hw/misc/pvpanic.h +++ b/include/hw/misc/pvpanic.h @@ -15,11 +15,18 @@ #ifndef HW_MISC_PVPANIC_H #define HW_MISC_PVPANIC_H -#include "exec/memory.h" +#include "system/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" +#define TYPE_PVPANIC_MMIO_DEVICE "pvpanic-mmio" #define PVPANIC_IOPORT_PROP "ioport" diff --git a/include/hw/misc/stm32_rcc.h b/include/hw/misc/stm32_rcc.h new file mode 100644 index 00000000000..ffbdf202ea5 --- /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 00000000000..62f79362f28 --- /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 00000000000..0fbfba5c40b --- /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 00000000000..ff1c834f694 --- /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 00000000000..c450df2b9ea --- /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/vmcoreinfo.h b/include/hw/misc/vmcoreinfo.h index 0b7b55d400a..1aa44771632 100644 --- a/include/hw/misc/vmcoreinfo.h +++ b/include/hw/misc/vmcoreinfo.h @@ -16,10 +16,9 @@ #include "standard-headers/linux/qemu_fw_cfg.h" #include "qom/object.h" -#define VMCOREINFO_DEVICE "vmcoreinfo" +#define TYPE_VMCOREINFO "vmcoreinfo" typedef struct VMCoreInfoState VMCoreInfoState; -DECLARE_INSTANCE_CHECKER(VMCoreInfoState, VMCOREINFO, - VMCOREINFO_DEVICE) +DECLARE_INSTANCE_CHECKER(VMCoreInfoState, VMCOREINFO, TYPE_VMCOREINFO) typedef struct fw_cfg_vmcoreinfo FWCfgVMCoreInfo; @@ -33,7 +32,7 @@ struct VMCoreInfoState { /* returns NULL unless there is exactly one device */ static inline VMCoreInfoState *vmcoreinfo_find(void) { - Object *o = object_resolve_path_type("", VMCOREINFO_DEVICE, NULL); + Object *o = object_resolve_path_type("", TYPE_VMCOREINFO, NULL); return o ? VMCOREINFO(o) : NULL; } diff --git a/include/hw/misc/xlnx-cfi-if.h b/include/hw/misc/xlnx-cfi-if.h index f9bd12292d4..50104015173 100644 --- a/include/hw/misc/xlnx-cfi-if.h +++ b/include/hw/misc/xlnx-cfi-if.h @@ -11,7 +11,6 @@ #define XLNX_CFI_IF_H 1 #include "qemu/help-texts.h" -#include "hw/hw.h" #include "qom/object.h" #define TYPE_XLNX_CFI_IF "xlnx-cfi-if" diff --git a/include/hw/misc/xlnx-versal-cframe-reg.h b/include/hw/misc/xlnx-versal-cframe-reg.h index a14fbd7fe45..83f6a077446 100644 --- a/include/hw/misc/xlnx-versal-cframe-reg.h +++ b/include/hw/misc/xlnx-versal-cframe-reg.h @@ -12,7 +12,7 @@ * https://www.xilinx.com/support/documentation/architecture-manuals/am011-versal-acap-trm.pdf * * [2] Versal ACAP Register Reference, - * https://www.xilinx.com/htmldocs/registers/am012/am012-versal-register-reference.html + * 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 @@ -23,7 +23,7 @@ #include "hw/misc/xlnx-versal-cfu.h" #include "qemu/fifo32.h" -#define TYPE_XLNX_VERSAL_CFRAME_REG "xlnx,cframe-reg" +#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" diff --git a/include/hw/misc/xlnx-versal-cfu.h b/include/hw/misc/xlnx-versal-cfu.h index 86fb8410538..3de3ee49231 100644 --- a/include/hw/misc/xlnx-versal-cfu.h +++ b/include/hw/misc/xlnx-versal-cfu.h @@ -12,7 +12,7 @@ * https://www.xilinx.com/support/documentation/architecture-manuals/am011-versal-acap-trm.pdf * * [2] Versal ACAP Register Reference, - * https://www.xilinx.com/htmldocs/registers/am012/am012-versal-register-reference.html + * 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 @@ -22,13 +22,13 @@ #include "hw/misc/xlnx-cfi-if.h" #include "qemu/fifo32.h" -#define TYPE_XLNX_VERSAL_CFU_APB "xlnx,versal-cfu-apb" +#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" +#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" +#define TYPE_XLNX_VERSAL_CFU_SFR "xlnx-versal-cfu-sfr" OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalCFUSFR, XLNX_VERSAL_CFU_SFR) REG32(CFU_ISR, 0x0) diff --git a/include/hw/misc/xlnx-versal-crl.h b/include/hw/misc/xlnx-versal-crl.h index 2857f4169a5..dba6d3585d1 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 f7d24c93c41..0c4a4fd66d9 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 diff --git a/include/hw/misc/xlnx-versal-trng.h b/include/hw/misc/xlnx-versal-trng.h new file mode 100644 index 00000000000..d96f8f9eff3 --- /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/net/dp8393x.h b/include/hw/net/dp8393x.h index 4a3f7478be4..24273dc1f43 100644 --- a/include/hw/net/dp8393x.h +++ b/include/hw/net/dp8393x.h @@ -22,7 +22,7 @@ #include "hw/sysbus.h" #include "net/net.h" -#include "exec/memory.h" +#include "system/memory.h" #define SONIC_REG_COUNT 0x40 diff --git a/include/hw/net/ftgmac100.h b/include/hw/net/ftgmac100.h index 765d1538a49..24ccdf0260a 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 2d13290c787..83b21637eeb 100644 --- a/include/hw/net/imx_fec.h +++ b/include/hw/net/imx_fec.h @@ -31,6 +31,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(IMXFECState, IMX_FEC) #define TYPE_IMX_ENET "imx.enet" #include "hw/sysbus.h" +#include "hw/net/lan9118_phy.h" +#include "hw/irq.h" #include "net/net.h" #define ENET_EIR 1 @@ -264,11 +266,8 @@ struct IMXFECState { uint32_t tx_descriptor[ENET_TX_RING_NUM]; uint32_t tx_ring_num; - uint32_t phy_status; - uint32_t phy_control; - uint32_t phy_advertise; - uint32_t phy_int; - uint32_t phy_int_mask; + Lan9118PhyState mii; + IRQState mii_irq; uint32_t phy_num; bool phy_connected; struct IMXFECState *phy_consumer; diff --git a/include/hw/net/lan9118.h b/include/hw/net/lan9118.h index 3d0c67f3393..4bf9da7a63e 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/lan9118_phy.h b/include/hw/net/lan9118_phy.h new file mode 100644 index 00000000000..af12fc33d5f --- /dev/null +++ b/include/hw/net/lan9118_phy.h @@ -0,0 +1,37 @@ +/* + * SMSC LAN9118 PHY emulation + * + * Copyright (c) 2009 CodeSourcery, LLC. + * 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_NET_LAN9118_PHY_H +#define HW_NET_LAN9118_PHY_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +#define TYPE_LAN9118_PHY "lan9118-phy" +OBJECT_DECLARE_SIMPLE_TYPE(Lan9118PhyState, LAN9118_PHY) + +typedef struct Lan9118PhyState { + SysBusDevice parent_obj; + + uint16_t status; + uint16_t control; + uint16_t advertise; + uint16_t ints; + uint16_t int_mask; + qemu_irq irq; + bool link_down; +} Lan9118PhyState; + +void lan9118_phy_update_link(Lan9118PhyState *s, bool link_down); +void lan9118_phy_reset(Lan9118PhyState *s); +uint16_t lan9118_phy_read(Lan9118PhyState *s, int reg); +void lan9118_phy_write(Lan9118PhyState *s, int reg, uint16_t val); + +#endif diff --git a/include/hw/net/lasi_82596.h b/include/hw/net/lasi_82596.h index 3ef2f47ba20..439356ec19b 100644 --- a/include/hw/net/lasi_82596.h +++ b/include/hw/net/lasi_82596.h @@ -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 f7feddac9bd..55bf7c92a1c 100644 --- a/include/hw/net/mii.h +++ b/include/hw/net/mii.h @@ -71,6 +71,7 @@ #define MII_BMSR_JABBER (1 << 1) /* Jabber detected */ #define MII_BMSR_EXTCAP (1 << 0) /* Ext-reg capability */ +#define MII_ANAR_RFAULT (1 << 13) /* Say we can detect faults */ #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) @@ -78,6 +79,7 @@ #define MII_ANAR_10FD (1 << 6) #define MII_ANAR_10 (1 << 5) #define MII_ANAR_CSMACD (1 << 0) +#define MII_ANAR_SELECT (0x001f) /* Selector bits */ #define MII_ANLPAR_ACK (1 << 14) #define MII_ANLPAR_PAUSEASY (1 << 11) /* can pause asymmetrically */ @@ -112,6 +114,10 @@ #define RTL8201CP_PHYID1 0x0000 #define RTL8201CP_PHYID2 0x8201 +/* SMSC LAN9118 */ +#define SMSCLAN9118_PHYID1 0x0007 +#define SMSCLAN9118_PHYID2 0xc0d1 + /* RealTek 8211E */ #define RTL8211E_PHYID1 0x001c #define RTL8211E_PHYID2 0xc915 diff --git a/include/hw/net/msf2-emac.h b/include/hw/net/msf2-emac.h index 846ba6e6dce..b5d9127e46c 100644 --- a/include/hw/net/msf2-emac.h +++ b/include/hw/net/msf2-emac.h @@ -23,7 +23,7 @@ */ #include "hw/sysbus.h" -#include "exec/memory.h" +#include "system/memory.h" #include "net/net.h" #include "net/eth.h" #include "qom/object.h" diff --git a/include/hw/net/ne2000-isa.h b/include/hw/net/ne2000-isa.h index af59ee0b028..73bae10ad1a 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/npcm_gmac.h b/include/hw/net/npcm_gmac.h new file mode 100644 index 00000000000..6340ffe92ca --- /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/npcm_pcs.h b/include/hw/net/npcm_pcs.h new file mode 100644 index 00000000000..d5c481ad70d --- /dev/null +++ b/include/hw/net/npcm_pcs.h @@ -0,0 +1,42 @@ +/* + * Nuvoton NPCM8xx PCS Module + * + * Copyright 2022 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_PCS_H +#define NPCM_PCS_H + +#include "hw/sysbus.h" + +#define NPCM_PCS_NR_SR_CTLS (0x12 / sizeof(uint16_t)) +#define NPCM_PCS_NR_SR_MIIS (0x20 / sizeof(uint16_t)) +#define NPCM_PCS_NR_SR_TIMS (0x22 / sizeof(uint16_t)) +#define NPCM_PCS_NR_VR_MIIS (0x1c6 / sizeof(uint16_t)) + +struct NPCMPCSState { + SysBusDevice parent; + + MemoryRegion iomem; + + uint16_t indirect_access_base; + uint16_t sr_ctl[NPCM_PCS_NR_SR_CTLS]; + uint16_t sr_mii[NPCM_PCS_NR_SR_MIIS]; + uint16_t sr_tim[NPCM_PCS_NR_SR_TIMS]; + uint16_t vr_mii[NPCM_PCS_NR_VR_MIIS]; +}; + +#define TYPE_NPCM_PCS "npcm-pcs" +OBJECT_DECLARE_SIMPLE_TYPE(NPCMPCSState, NPCM_PCS) + +#endif /* NPCM_PCS_H */ diff --git a/include/hw/net/smc91c111.h b/include/hw/net/smc91c111.h index df5b11dcef2..dba32a233fb 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/nubus/nubus-virtio-mmio.h b/include/hw/nubus/nubus-virtio-mmio.h new file mode 100644 index 00000000000..de497b7f763 --- /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 b3b4d2eadb4..7825840dcac 100644 --- a/include/hw/nubus/nubus.h +++ b/include/hw/nubus/nubus.h @@ -11,7 +11,7 @@ #include "hw/qdev-properties.h" #include "hw/sysbus.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qom/object.h" #include "qemu/units.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 00000000000..1df33700bde --- /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/fw_cfg.h b/include/hw/nvram/fw_cfg.h index c1f81a5f13a..d41b9328fd1 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -4,7 +4,7 @@ #include "exec/hwaddr.h" #include "standard-headers/linux/qemu_fw_cfg.h" #include "hw/sysbus.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "qom/object.h" #define TYPE_FW_CFG "fw_cfg" @@ -30,8 +30,9 @@ struct FWCfgDataGeneratorClass { * @obj: the object implementing this interface * @errp: pointer to a NULL-initialized error object * - * Returns: reference to a byte array containing the data on success, - * or NULL on error. + * Returns: A byte array containing data to add, or NULL without + * @errp set if no data is required, or NULL with @errp + * set on failure. * * The caller should release the reference when no longer * required. @@ -41,14 +42,6 @@ struct FWCfgDataGeneratorClass { typedef struct fw_cfg_file FWCfgFile; -#define FW_CFG_ORDER_OVERRIDE_VGA 70 -#define FW_CFG_ORDER_OVERRIDE_NIC 80 -#define FW_CFG_ORDER_OVERRIDE_USER 100 -#define FW_CFG_ORDER_OVERRIDE_DEVICE 110 - -void fw_cfg_set_order_override(FWCfgState *fw_cfg, int order); -void fw_cfg_reset_order_override(FWCfgState *fw_cfg); - typedef struct FWCfgFiles { uint32_t count; FWCfgFile f[]; @@ -59,6 +52,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; @@ -72,8 +67,6 @@ struct FWCfgState { uint32_t cur_offset; Notifier machine_ready; - int fw_cfg_order_override; - bool dma_enabled; dma_addr_t dma_addr; AddressSpace *dma_as; @@ -289,37 +282,31 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, void *data, size_t len); /** - * fw_cfg_add_from_generator: + * fw_cfg_add_file_from_generator: * @s: fw_cfg device being modified * @filename: name of new fw_cfg file item - * @gen_id: name of object implementing FW_CFG_DATA_GENERATOR interface + * @part: name of object implementing FW_CFG_DATA_GENERATOR interface + * @parent: the object in which to resolve the @part * @errp: pointer to a NULL initialized error object * - * Add a new NAMED fw_cfg item with the content generated from the - * @gen_id object. The data generated by the @gen_id object is copied - * into the data structure of the fw_cfg device. + * If the @part object generates content, add a new NAMED fw_cfg item with it. + * The data generated by the @part object is copied into the data structure of + * the fw_cfg device. * The next available (unused) selector key starting at FW_CFG_FILE_FIRST * will be used; also, a new entry will be added to the file directory * structure residing at key value FW_CFG_FILE_DIR, containing the item name, * data size, and assigned selector key value. * - * Returns: %true on success, %false on error. - */ -bool fw_cfg_add_from_generator(FWCfgState *s, const char *filename, - const char *gen_id, Error **errp); - -/** - * fw_cfg_add_extra_pci_roots: - * @bus: main pci root bus to be scanned from - * @s: fw_cfg device being modified + * If the @part object does not generate content, no fw_cfg item is added. * - * Add a new fw_cfg item... + * Returns: %true on success, %false on error. */ -void fw_cfg_add_extra_pci_roots(PCIBus *bus, FWCfgState *s); +bool fw_cfg_add_file_from_generator(FWCfgState *s, + Object *parent, const char *part, + const char *filename, Error **errp); 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 00000000000..dfd2a44ef0b --- /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 0c4dfaeff60..e9d8398f849 100644 --- a/include/hw/nvram/mac_nvram.h +++ b/include/hw/nvram/mac_nvram.h @@ -26,7 +26,7 @@ #ifndef MAC_NVRAM_H #define MAC_NVRAM_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.h" #define MACIO_NVRAM_SIZE 0x2000 diff --git a/include/hw/nvram/npcm7xx_otp.h b/include/hw/nvram/npcm7xx_otp.h index ea4b5d07310..77b05f8b82c 100644 --- a/include/hw/nvram/npcm7xx_otp.h +++ b/include/hw/nvram/npcm7xx_otp.h @@ -16,7 +16,7 @@ #ifndef NPCM7XX_OTP_H #define NPCM7XX_OTP_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.h" /* Each OTP module holds 8192 bits of one-time programmable storage */ diff --git a/include/hw/nvram/xlnx-bbram.h b/include/hw/nvram/xlnx-bbram.h index 87d59ef3c0c..58acbe9f51b 100644 --- a/include/hw/nvram/xlnx-bbram.h +++ b/include/hw/nvram/xlnx-bbram.h @@ -26,7 +26,7 @@ #ifndef XLNX_BBRAM_H #define XLNX_BBRAM_H -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "hw/qdev-core.h" #include "hw/irq.h" #include "hw/sysbus.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 58414e468b5..ef14fb0528a 100644 --- a/include/hw/nvram/xlnx-efuse.h +++ b/include/hw/nvram/xlnx-efuse.h @@ -27,10 +27,10 @@ #ifndef XLNX_EFUSE_H #define XLNX_EFUSE_H -#include "sysemu/block-backend.h" +#include "system/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 a873dc5cb01..afa4f4f9960 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 6b051ec4f15..7fb12df3fbb 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/openrisc/boot.h b/include/hw/openrisc/boot.h index 25a313d63a1..9b4d88072c4 100644 --- a/include/hw/openrisc/boot.h +++ b/include/hw/openrisc/boot.h @@ -20,6 +20,7 @@ #define OPENRISC_BOOT_H #include "exec/cpu-defs.h" +#include "hw/boards.h" hwaddr openrisc_load_kernel(ram_addr_t ram_size, const char *kernel_filename, @@ -28,7 +29,7 @@ hwaddr openrisc_load_kernel(ram_addr_t ram_size, hwaddr openrisc_load_initrd(void *fdt, const char *filename, hwaddr load_start, uint64_t mem_size); -uint32_t openrisc_load_fdt(void *fdt, hwaddr load_start, +uint32_t openrisc_load_fdt(MachineState *ms, void *fdt, hwaddr load_start, uint64_t mem_size); #endif /* OPENRISC_BOOT_H */ 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 00000000000..f208397ffe9 --- /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 00000000000..529c2402746 --- /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 00000000000..832125a05af --- /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 LMMIO_DIRECT_RANGES 4 + +#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 lmmio_direct[LMMIO_DIRECT_RANGES]; + + IOMMUMemoryRegion iommu; + AddressSpace iommu_as; +}; + +#endif diff --git a/include/hw/pci-host/designware.h b/include/hw/pci-host/designware.h index 908f3d946be..a35a3bd06c8 100644 --- a/include/hw/pci-host/designware.h +++ b/include/hw/pci-host/designware.h @@ -25,13 +25,18 @@ #include "hw/pci/pci_bridge.h" #include "qom/object.h" +#define TYPE_DESIGNWARE_PCIE_ROOT_BUS "designware-pcie-root-BUS" +OBJECT_DECLARE_SIMPLE_TYPE(DesignwarePCIERootBus, DESIGNWARE_PCIE_ROOT_BUS) + #define TYPE_DESIGNWARE_PCIE_HOST "designware-pcie-host" 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; +struct DesignwarePCIERootBus { + PCIBus parent; +}; typedef struct DesignwarePCIEViewport { DesignwarePCIERoot *root; @@ -88,6 +93,7 @@ struct DesignwarePCIEHost { MemoryRegion io; qemu_irq irqs[4]; + qemu_irq msi; } pci; MemoryRegion mmio; diff --git a/include/hw/pci-host/dino.h b/include/hw/pci-host/dino.h index fd7975c7984..5dc8cdf6108 100644 --- a/include/hw/pci-host/dino.h +++ b/include/hw/pci-host/dino.h @@ -109,10 +109,6 @@ static const uint32_t reg800_keep_bits[DINO800_REGS] = { struct DinoState { PCIHostState parent_obj; - /* - * PCI_CONFIG_ADDR is parent_obj.config_reg, via pci_host_conf_be_ops, - * so that we can map PCI_CONFIG_DATA to pci_host_data_be_ops. - */ uint32_t config_reg_dino; /* keep original copy, including 2 lowest bits */ uint32_t iar0; diff --git a/include/hw/pci-host/fsl_imx8m_phy.h b/include/hw/pci-host/fsl_imx8m_phy.h new file mode 100644 index 00000000000..5f1b212fd97 --- /dev/null +++ b/include/hw/pci-host/fsl_imx8m_phy.h @@ -0,0 +1,28 @@ +/* + * i.MX8 PCIe PHY emulation + * + * Copyright (c) 2025 Bernhard Beschow + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_PCIHOST_FSLIMX8MPCIEPHY_H +#define HW_PCIHOST_FSLIMX8MPCIEPHY_H + +#include "hw/sysbus.h" +#include "qom/object.h" +#include "system/memory.h" + +#define TYPE_FSL_IMX8M_PCIE_PHY "fsl-imx8m-pcie-phy" +OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mPciePhyState, FSL_IMX8M_PCIE_PHY) + +#define FSL_IMX8M_PCIE_PHY_DATA_SIZE 0x800 + +struct FslImx8mPciePhyState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + uint8_t data[FSL_IMX8M_PCIE_PHY_DATA_SIZE]; +}; + +#endif diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h index b0240bd7681..84471533af0 100644 --- a/include/hw/pci-host/gpex.h +++ b/include/hw/pci-host/gpex.h @@ -32,14 +32,22 @@ OBJECT_DECLARE_SIMPLE_TYPE(GPEXHost, GPEX_HOST) #define TYPE_GPEX_ROOT_DEVICE "gpex-root" OBJECT_DECLARE_SIMPLE_TYPE(GPEXRootState, GPEX_ROOT_DEVICE) -#define GPEX_NUM_IRQS 4 - struct GPEXRootState { /*< private >*/ PCIDevice parent_obj; /*< public >*/ }; +struct GPEXConfig { + MemMapEntry ecam; + MemMapEntry mmio32; + MemMapEntry mmio64; + MemMapEntry pio; + int irq; + PCIBus *bus; +}; + +typedef struct GPEXIrq GPEXIrq; struct GPEXHost { /*< private >*/ PCIExpressHost parent_obj; @@ -51,23 +59,26 @@ struct GPEXHost { MemoryRegion io_mmio; MemoryRegion io_ioport_window; MemoryRegion io_mmio_window; - qemu_irq irq[GPEX_NUM_IRQS]; - int irq_num[GPEX_NUM_IRQS]; + GPEXIrq *irq; + uint8_t 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/ls7a.h b/include/hw/pci-host/ls7a.h index e753449593b..79d4ea85012 100644 --- a/include/hw/pci-host/ls7a.h +++ b/include/hw/pci-host/ls7a.h @@ -24,6 +24,8 @@ #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 /* * GSI_BASE is hard-coded with 64 in linux kernel, else kernel fails to boot @@ -34,17 +36,18 @@ #define VIRT_PCH_PIC_IRQ_NUM 32 #define VIRT_GSI_BASE 64 #define VIRT_DEVICE_IRQS 16 +#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 (VIRT_GSI_BASE + 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 (VIRT_GSI_BASE + 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 (VIRT_GSI_BASE + 5) +#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 005916f826b..44f39081604 100644 --- a/include/hw/pci-host/pam.h +++ b/include/hw/pci-host/pam.h @@ -50,7 +50,7 @@ * 0xf0000 - 0xfffff System BIOS Area Memory Segments */ -#include "exec/memory.h" +#include "system/memory.h" #define SMRAM_C_BASE 0xa0000 #define SMRAM_C_END 0xc0000 diff --git a/include/hw/pci-host/pnv_phb3.h b/include/hw/pci-host/pnv_phb3.h index d62b3091ac8..24ca3dddaa9 100644 --- a/include/hw/pci-host/pnv_phb3.h +++ b/include/hw/pci-host/pnv_phb3.h @@ -40,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 2d026db9a3c..8a80c0c667a 100644 --- a/include/hw/pci-host/pnv_phb4.h +++ b/include/hw/pci-host/pnv_phb4.h @@ -13,6 +13,7 @@ #include "hw/pci-host/pnv_phb.h" #include "hw/pci/pci_bus.h" #include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_nest_pervasive.h" #include "hw/ppc/xive.h" #include "qom/object.h" @@ -117,7 +118,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; @@ -155,7 +156,7 @@ 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); @@ -174,6 +175,9 @@ struct PnvPhb4PecState { uint32_t index; uint32_t chip_id; + /* Pervasive chiplet control */ + PnvNestChipletPervasive nest_pervasive; + /* Nest registers, excuding per-stack */ #define PHB4_PEC_NEST_REGS_COUNT 0xf uint64_t nest_regs[PHB4_PEC_NEST_REGS_COUNT]; @@ -196,6 +200,7 @@ struct PnvPhb4PecState { struct PnvPhb4PecClass { DeviceClass parent_class; + uint32_t (*xscom_cplt_base)(PnvPhb4PecState *pec); uint32_t (*xscom_nest_base)(PnvPhb4PecState *pec); uint32_t xscom_nest_size; uint32_t (*xscom_pci_base)(PnvPhb4PecState *pec); diff --git a/include/hw/pci-host/pnv_phb4_regs.h b/include/hw/pci-host/pnv_phb4_regs.h index 4a0d3b28efb..bea96f4d91a 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 00000000000..32396417fc7 --- /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 1d98bbfe0dd..ddafc3f2e3d 100644 --- a/include/hw/pci-host/q35.h +++ b/include/hw/pci-host/q35.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; }; @@ -181,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/remote.h b/include/hw/pci-host/remote.h index 690a01f0fe9..5264c359369 100644 --- a/include/hw/pci-host/remote.h +++ b/include/hw/pci-host/remote.h @@ -11,7 +11,7 @@ #ifndef PCI_HOST_REMOTE_H #define PCI_HOST_REMOTE_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/pci/pcie_host.h" #define TYPE_REMOTE_PCIHOST "remote-pcihost" diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 5b03a7b0eb3..0db87f12815 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/msix.h b/include/hw/pci/msix.h index 0e6f257e451..11ef9454c13 100644 --- a/include/hw/pci/msix.h +++ b/include/hw/pci/msix.h @@ -32,6 +32,7 @@ int msix_present(PCIDevice *dev); bool msix_is_masked(PCIDevice *dev, unsigned vector); void msix_set_pending(PCIDevice *dev, unsigned vector); void msix_clr_pending(PCIDevice *dev, int vector); +int msix_is_pending(PCIDevice *dev, unsigned vector); void msix_vector_use(PCIDevice *dev, unsigned vector); void msix_vector_unuse(PCIDevice *dev, unsigned vector); diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index b70a0b95ff5..df3cc7b8750 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -1,8 +1,9 @@ #ifndef QEMU_PCI_H #define QEMU_PCI_H -#include "exec/memory.h" -#include "sysemu/dma.h" +#include "system/memory.h" +#include "system/dma.h" +#include "system/host_iommu_device.h" /* PCI includes legacy ISA access. */ #include "hw/isa/isa.h" @@ -15,13 +16,17 @@ 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 #define PCI_SLOT_MAX 32 #define PCI_FUNC_MAX 8 +#define PCI_SBDF(seg, bus, dev, func) \ + ((((uint32_t)(seg)) << 16) | \ + (PCI_BUILD_BDF(bus, PCI_DEVFN(dev, func)))) + /* Class, Vendor and Device IDs from Linux's pci_ids.h */ #include "hw/pci/pci_ids.h" @@ -115,6 +120,7 @@ extern bool pci_available; #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 @@ -212,6 +218,12 @@ enum { 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 QEMU_PCI_CAP_PM_BITNR 14 + QEMU_PCI_CAP_PM = (1 << QEMU_PCI_CAP_PM_BITNR), +#define QEMU_PCI_SKIP_RESET_ON_CPR_BITNR 15 + QEMU_PCI_SKIP_RESET_ON_CPR = (1 << QEMU_PCI_SKIP_RESET_ON_CPR_BITNR), }; typedef struct PCIINTxRoute { @@ -279,12 +291,10 @@ 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, @@ -295,6 +305,9 @@ 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); +bool pci_bus_add_fw_cfg_extra_pci_roots(FWCfgState *fw_cfg, + PCIBus *bus, + Error **errp); /* 0 <= pin <= 3 0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD */ static inline int pci_swizzle(int slot, int pin) { @@ -304,8 +317,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); @@ -317,10 +329,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) @@ -366,10 +377,392 @@ void pci_bus_get_w64_range(PCIBus *bus, Range *range); void pci_device_deassert_intx(PCIDevice *dev); -typedef AddressSpace *(*PCIIOMMUFunc)(PCIBus *, void *, int); +/* Page Request Interface */ +typedef enum { + IOMMU_PRI_RESP_SUCCESS, + IOMMU_PRI_RESP_INVALID_REQUEST, + IOMMU_PRI_RESP_FAILURE, +} IOMMUPRIResponseCode; + +typedef struct IOMMUPRIResponse { + IOMMUPRIResponseCode response_code; + uint16_t prgi; +} IOMMUPRIResponse; + +struct IOMMUPRINotifier; + +typedef void (*IOMMUPRINotify)(struct IOMMUPRINotifier *notifier, + IOMMUPRIResponse *response); + +typedef struct IOMMUPRINotifier { + IOMMUPRINotify notify; +} IOMMUPRINotifier; + +#define PCI_PRI_PRGI_MASK 0x1ffU + +/** + * 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); + /** + * @get_iotlb_info: get properties required to initialize a device IOTLB. + * + * Callback required if devices are allowed to cache translations. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @addr_width: the address width of the IOMMU (output parameter). + * + * @min_page_size: the page size of the IOMMU (output parameter). + */ + void (*get_iotlb_info)(void *opaque, uint8_t *addr_width, + uint32_t *min_page_size); + /** + * @init_iotlb_notifier: initialize an IOMMU notifier. + * + * 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. + * + * @n: the notifier to be initialized. + * + * @fn: the callback to be installed. + * + * @user_opaque: a user pointer that can be used to track a state. + */ + void (*init_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn, + IOMMUNotifier *n, IOMMUNotify fn, + void *user_opaque); + /** + * @register_iotlb_notifier: setup an IOTLB invalidation notifier. + * + * Callback required if devices are allowed to cache translations. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to watch. + * + * @n: the notifier to register. + */ + void (*register_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, IOMMUNotifier *n); + /** + * @unregister_iotlb_notifier: remove an IOTLB invalidation notifier. + * + * Callback required if devices are allowed to cache translations. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to stop watching. + * + * @n: the notifier to unregister. + */ + void (*unregister_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, IOMMUNotifier *n); + /** + * @ats_request_translation: issue an ATS request. + * + * Callback required if devices are allowed to use the address + * translation service. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to use for the request. + * + * @priv_req: privileged mode bit (PASID TLP). + * + * @exec_req: execute request bit (PASID TLP). + * + * @addr: start address of the memory range to be translated. + * + * @length: length of the memory range in bytes. + * + * @no_write: request a read-only translation (if supported). + * + * @result: buffer in which the TLB entries will be stored. + * + * @result_length: result buffer length. + * + * @err_count: number of untranslated subregions. + * + * Returns: the number of translations stored in the result buffer, or + * -ENOMEM if the buffer is not large enough. + */ + ssize_t (*ats_request_translation)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, bool priv_req, + bool exec_req, hwaddr addr, + size_t length, bool no_write, + IOMMUTLBEntry *result, + size_t result_length, + uint32_t *err_count); + /** + * @pri_register_notifier: setup the PRI completion callback. + * + * Callback required if devices are allowed to use the page request + * interface. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to track. + * + * @notifier: the notifier to register. + */ + void (*pri_register_notifier)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, IOMMUPRINotifier *notifier); + /** + * @pri_unregister_notifier: remove the PRI completion callback. + * + * Callback required if devices are allowed to use the page request + * interface. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to stop tracking. + */ + void (*pri_unregister_notifier)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid); + /** + * @pri_request_page: issue a PRI request. + * + * Callback required if devices are allowed to use the page request + * interface. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to use for the request. + * + * @priv_req: privileged mode bit (PASID TLP). + * + * @exec_req: execute request bit (PASID TLP). + * + * @addr: untranslated address of the requested page. + * + * @lpig: last page in group. + * + * @prgi: page request group index. + * + * @is_read: request read access. + * + * @is_write: request write access. + */ + int (*pri_request_page)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, bool priv_req, bool exec_req, + hwaddr addr, bool lpig, uint16_t prgi, bool is_read, + bool is_write); +} 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_iommu_get_iotlb_info: get properties required to initialize a + * device IOTLB. + * + * Returns 0 on success, or a negative errno otherwise. + * + * @dev: the device that wants to get the information. + * @addr_width: the address width of the IOMMU (output parameter). + * @min_page_size: the page size of the IOMMU (output parameter). + */ +int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width, + uint32_t *min_page_size); + +/** + * pci_iommu_init_iotlb_notifier: initialize an IOMMU notifier. + * + * This function is used by devices before registering an IOTLB notifier. + * + * @dev: the device. + * @n: the notifier to be initialized. + * @fn: the callback to be installed. + * @opaque: a user pointer that can be used to track a state. + */ +int pci_iommu_init_iotlb_notifier(PCIDevice *dev, IOMMUNotifier *n, + IOMMUNotify fn, void *opaque); + +/** + * pci_ats_request_translation: perform an ATS request. + * + * Returns the number of translations stored in @result in case of success, + * a negative error code otherwise. + * -ENOMEM is returned when the result buffer is not large enough to store + * all the translations. + * + * @dev: the ATS-capable PCI device. + * @pasid: the pasid of the address space in which the translation will be done. + * @priv_req: privileged mode bit (PASID TLP). + * @exec_req: execute request bit (PASID TLP). + * @addr: start address of the memory range to be translated. + * @length: length of the memory range in bytes. + * @no_write: request a read-only translation (if supported). + * @result: buffer in which the TLB entries will be stored. + * @result_length: result buffer length. + * @err_count: number of untranslated subregions. + */ +ssize_t pci_ats_request_translation(PCIDevice *dev, uint32_t pasid, + bool priv_req, bool exec_req, + hwaddr addr, size_t length, + bool no_write, IOMMUTLBEntry *result, + size_t result_length, + uint32_t *err_count); + +/** + * pci_pri_request_page: perform a PRI request. + * + * Returns 0 if the PRI request has been sent to the guest OS, + * an error code otherwise. + * + * @dev: the PRI-capable PCI device. + * @pasid: the pasid of the address space in which the translation will be done. + * @priv_req: privileged mode bit (PASID TLP). + * @exec_req: execute request bit (PASID TLP). + * @addr: untranslated address of the requested page. + * @lpig: last page in group. + * @prgi: page request group index. + * @is_read: request read access. + * @is_write: request write access. + */ +int pci_pri_request_page(PCIDevice *dev, uint32_t pasid, bool priv_req, + bool exec_req, hwaddr addr, bool lpig, + uint16_t prgi, bool is_read, bool is_write); + +/** + * pci_pri_register_notifier: register the PRI callback for a given address + * space. + * + * Returns 0 on success, an error code otherwise. + * + * @dev: the PRI-capable PCI device. + * @pasid: the pasid of the address space to track. + * @notifier: the notifier to register. + */ +int pci_pri_register_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUPRINotifier *notifier); + +/** + * pci_pri_unregister_notifier: remove the PRI callback from a given address + * space. + * + * @dev: the PRI-capable PCI device. + * @pasid: the pasid of the address space to stop tracking. + */ +void pci_pri_unregister_notifier(PCIDevice *dev, uint32_t pasid); + +/** + * pci_iommu_register_iotlb_notifier: register a notifier for changes to + * IOMMU translation entries in a specific address space. + * + * Returns 0 on success, or a negative errno otherwise. + * + * @dev: the device that wants to get notified. + * @pasid: the pasid of the address space to track. + * @n: the notifier to register. + */ +int pci_iommu_register_iotlb_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUNotifier *n); + +/** + * pci_iommu_unregister_iotlb_notifier: unregister a notifier that has been + * registerd with pci_iommu_register_iotlb_notifier. + * + * Returns 0 on success, or a negative errno otherwise. + * + * @dev: the device that wants to stop notifications. + * @pasid: the pasid of the address space to stop tracking. + * @n: the notifier to unregister. + */ +int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUNotifier *n); + +/** + * 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); @@ -592,6 +985,7 @@ 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); +int pci_irq_disabled(PCIDevice *d); static inline void pci_irq_assert(PCIDevice *pci_dev) { @@ -603,17 +997,9 @@ 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); -} - MSIMessage pci_get_msi_message(PCIDevice *dev, int vector); +void pci_set_enabled(PCIDevice *pci_dev, bool state); void pci_set_power(PCIDevice *pci_dev, bool state); +int pci_pm_init(PCIDevice *pci_dev, uint8_t offset, Error **errp); #endif diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h index ea54a81a15a..b0f5204d80f 100644 --- a/include/hw/pci/pci_bridge.h +++ b/include/hw/pci/pci_bridge.h @@ -72,11 +72,16 @@ struct PCIBridge { */ MemoryRegion address_space_mem; MemoryRegion address_space_io; + AddressSpace as_mem; + AddressSpace as_io; 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" @@ -99,6 +104,7 @@ typedef struct PXBPCIEDev { PXBDev parent_obj; } PXBPCIEDev; +#define TYPE_PXB_CXL_BUS "pxb-cxl-bus" #define TYPE_PXB_DEV "pxb" OBJECT_DECLARE_SIMPLE_TYPE(PXBDev, PXB_DEV) diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index 56531759578..22613125462 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 index d3dd0f64b27..eee03385686 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -3,6 +3,7 @@ #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; @@ -37,6 +38,8 @@ struct PCIDeviceClass { uint16_t subsystem_id; /* only for header type = 0 */ const char *romfile; /* rom bar */ + + bool sriov_vf_user_creatable; }; enum PCIReqIDType { @@ -56,7 +59,7 @@ typedef struct PCIReqIDCache PCIReqIDCache; struct PCIDevice { DeviceState qdev; bool partially_hotplugged; - bool has_power; + bool enabled; /* PCI config space */ uint8_t *config; @@ -87,6 +90,7 @@ struct PCIDevice { char name[64]; PCIIORegion io_regions[PCI_NUM_REGIONS]; AddressSpace bus_master_as; + bool is_master; MemoryRegion bus_master_container_region; MemoryRegion bus_master_enable_region; @@ -104,6 +108,9 @@ struct PCIDevice { /* Capability bits */ uint32_t cap_present; + /* Offset of PM capability in config space */ + uint8_t pm_cap; + /* Offset of MSI-X capability in config space */ uint8_t msix_cap; @@ -147,7 +154,7 @@ struct PCIDevice { uint32_t romsize; bool has_rom; MemoryRegion rom; - uint32_t rom_bar; + int32_t rom_bar; /* INTx routing notifier */ PCIINTxRoutingNotifier intx_routing_notifier; @@ -157,9 +164,24 @@ struct PCIDevice { 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; + + char *sriov_pf; }; static inline int pci_intx(PCIDevice *pci_dev) @@ -192,7 +214,7 @@ static inline int pci_is_express_downstream_port(const PCIDevice *d) static inline int pci_is_vf(const PCIDevice *d) { - return d->exp.sriov_vf.pf != NULL; + return d->sriov_pf || d->exp.sriov_vf.pf != NULL; } static inline uint32_t pci_config_size(const PCIDevice *d) diff --git a/include/hw/pci/pci_host.h b/include/hw/pci/pci_host.h index e52d8ec2cdc..954dd446fa4 100644 --- a/include/hw/pci/pci_host.h +++ b/include/hw/pci/pci_host.h @@ -68,6 +68,5 @@ uint32_t pci_data_read(PCIBus *s, uint32_t addr, unsigned len); extern const MemoryRegionOps pci_host_conf_le_ops; extern const MemoryRegionOps pci_host_conf_be_ops; extern const MemoryRegionOps pci_host_data_le_ops; -extern const MemoryRegionOps pci_host_data_be_ops; #endif /* PCI_HOST_H */ diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h index 85469b9b538..33e2898be95 100644 --- a/include/hw/pci/pci_ids.h +++ b/include/hw/pci/pci_ids.h @@ -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 @@ -189,6 +191,7 @@ #define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020 #define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b #define PCI_DEVICE_ID_APPLE_UNI_N_GMAC 0x0021 +#define PCI_DEVICE_ID_APPLE_VIRTIO_BLK 0x1a00 #define PCI_VENDOR_ID_SUN 0x108e #define PCI_DEVICE_ID_SUN_EBUS 0x1000 diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index 11f5a91bbb7..ff6ce08e135 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -27,6 +27,9 @@ #include "hw/pci/pcie_sriov.h" #include "hw/hotplug.h" +typedef struct PCIEPort PCIEPort; +typedef struct PCIESlot PCIESlot; + typedef enum { /* these bits must match the bits in Slot Control/Status registers. * PCI_EXP_HP_EV_xxx = PCI_EXP_SLTCTL_xxxE = PCI_EXP_SLTSTA_xxx @@ -55,8 +58,6 @@ typedef enum { struct PCIExpressDevice { /* Offset of express capability in config space */ uint8_t exp_cap; - /* Offset of Power Management capability in config space */ - uint8_t pm_cap; /* SLOT */ bool hpev_notified; /* Logical AND of conditions for hot plug event. @@ -69,8 +70,10 @@ struct PCIExpressDevice { uint16_t aer_cap; PCIEAERLog aer_log; - /* Offset of ATS capability in config space */ + /* Offset of ATS, PRI and PASID capabilities in config space */ uint16_t ats_cap; + uint16_t pasid_cap; + uint16_t pri_cap; /* ACS */ uint16_t acs_cap; @@ -138,6 +141,8 @@ void pcie_acs_reset(PCIDevice *dev); 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); @@ -147,4 +152,13 @@ void pcie_cap_slot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); + +void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, + bool exec_perm, bool priv_mod); +void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap, + bool prg_response_pasid_req); + +bool pcie_pri_enabled(const PCIDevice *dev); +bool pcie_pasid_enabled(const PCIDevice *dev); +bool pcie_ats_enabled(const PCIDevice *dev); #endif /* QEMU_PCIE_H */ diff --git a/include/hw/pci/pcie_aer.h b/include/hw/pci/pcie_aer.h index 4a9f0ea69dc..4d8c0e05077 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 */ @@ -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, diff --git a/include/hw/pci/pcie_doe.h b/include/hw/pci/pcie_doe.h index 87dc17dcef9..9e1275db8ae 100644 --- a/include/hw/pci/pcie_doe.h +++ b/include/hw/pci/pcie_doe.h @@ -46,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 @@ -106,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_host.h b/include/hw/pci/pcie_host.h index 82d92177da9..f09de76bfe6 100644 --- a/include/hw/pci/pcie_host.h +++ b/include/hw/pci/pcie_host.h @@ -22,7 +22,7 @@ #define PCIE_HOST_H #include "hw/pci/pci_host.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qom/object.h" #define TYPE_PCIE_HOST_BRIDGE "pcie-host-bridge" diff --git a/include/hw/pci/pcie_port.h b/include/hw/pci/pcie_port.h index 90e6cf45b87..7cd7af8cfa4 100644 --- a/include/hw/pci/pcie_port.h +++ b/include/hw/pci/pcie_port.h @@ -72,7 +72,6 @@ struct PCIESlot { }; 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); diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h index 4972106c429..33a22229fe2 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) @@ -84,6 +86,14 @@ typedef enum PCIExpLinkWidth { #define PCI_ARI_VER 1 #define PCI_ARI_SIZEOF 8 +/* PASID */ +#define PCI_PASID_VER 1 +#define PCI_EXT_CAP_PASID_MAX_WIDTH 20 +#define PCI_PASID_CAP_WIDTH_SHIFT 8 + +/* PRI */ +#define PCI_PRI_VER 1 + /* AER */ #define PCI_ERR_VER 2 #define PCI_ERR_SIZEOF 0x48 diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index 095fb0c9edf..aeaa38cf345 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -15,22 +15,22 @@ #include "hw/pci/pci.h" -struct PCIESriovPF { - uint16_t num_vfs; /* Number of virtual functions created */ +typedef struct PCIESriovPF { 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 */ -}; + bool vf_user_created; /* If VFs are created by user */ +} 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, +bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, const char *vfname, uint16_t vf_dev_id, uint16_t init_vfs, uint16_t total_vfs, - uint16_t vf_offset, uint16_t vf_stride); + uint16_t vf_offset, uint16_t vf_stride, + Error **errp); void pcie_sriov_pf_exit(PCIDevice *dev); /* Set up a VF bar in the SR/IOV bar area */ @@ -41,6 +41,26 @@ void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, MemoryRegion *memory); +/** + * pcie_sriov_pf_init_from_user_created_vfs() - Initialize PF with user-created + * VFs, adding ARI to PF + * @dev: A PCIe device being realized. + * @offset: The offset of the SR-IOV capability. + * @errp: pointer to Error*, to store an error if it happens. + * + * Initializes a PF with user-created VFs, adding the ARI extended capability to + * the PF. The VFs should call pcie_ari_init() to form an ARI device. + * + * Return: The size of added capabilities. 0 if the user did not create VFs. + * -1 if failed. + */ +int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev, + uint16_t offset, + Error **errp); + +bool pcie_sriov_register_device(PCIDevice *dev, Error **errp); +void pcie_sriov_unregister_device(PCIDevice *dev); + /* * Default (minimal) page size support values * as required by the SR/IOV standard: @@ -58,8 +78,10 @@ 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); +void pcie_sriov_pf_post_load(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); diff --git a/include/hw/pci/shpc.h b/include/hw/pci/shpc.h index 89c7a3b7fa9..ad1089567a0 100644 --- a/include/hw/pci/shpc.h +++ b/include/hw/pci/shpc.h @@ -1,7 +1,7 @@ #ifndef SHPC_H #define SHPC_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/hotplug.h" #include "hw/pci/pci_device.h" #include "migration/vmstate.h" @@ -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 ab268027511..00000000000 --- 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 a8cd85069fe..b56ac2a8cbb 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 4a3f644516b..896ee4a2b17 100644 --- a/include/hw/ppc/mac_dbdma.h +++ b/include/hw/ppc/mac_dbdma.h @@ -23,9 +23,9 @@ #ifndef HW_MAC_DBDMA_H #define HW_MAC_DBDMA_H -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/iov.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/sysbus.h" #include "qom/object.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 bae8dafe166..9c6af8e2076 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, }; diff --git a/include/hw/ppc/pef.h b/include/hw/ppc/pef.h deleted file mode 100644 index 707dbe524c4..00000000000 --- 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 7e5fef7c433..d8fca079f2f 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -28,6 +28,7 @@ #define TYPE_PNV_CHIP "pnv-chip" +typedef struct PnvCore PnvCore; typedef struct PnvChip PnvChip; typedef struct Pnv8Chip Pnv8Chip; typedef struct Pnv9Chip Pnv9Chip; @@ -56,6 +57,7 @@ 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; @@ -74,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 { @@ -97,6 +103,9 @@ 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); @@ -105,6 +114,8 @@ 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 */ @@ -194,9 +205,8 @@ void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor); #define PNV9_OCC_SENSOR_BASE(chip) (PNV9_OCC_COMMON_AREA_BASE + \ PNV_OCC_SENSOR_DATA_BLOCK_BASE((chip)->chip_id)) -#define PNV9_HOMER_SIZE 0x0000000000400000ull #define PNV9_HOMER_BASE(chip) \ - (0x203ffd800000ull + ((uint64_t)(chip)->chip_id) * PNV9_HOMER_SIZE) + (0x203ffd800000ull + ((uint64_t)(chip)->chip_id) * PNV_HOMER_SIZE) /* * POWER10 MMIO base addresses - 16TB stride per chip @@ -239,8 +249,7 @@ void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor); #define PNV10_OCC_SENSOR_BASE(chip) (PNV10_OCC_COMMON_AREA_BASE + \ PNV_OCC_SENSOR_DATA_BLOCK_BASE((chip)->chip_id)) -#define PNV10_HOMER_SIZE 0x0000000000400000ull #define PNV10_HOMER_BASE(chip) \ - (0x300ffd800000ll + ((uint64_t)(chip)->chip_id) * PNV10_HOMER_SIZE) + (0x300ffd800000ll + ((uint64_t)(chip)->chip_id) * PNV_HOMER_SIZE) #endif /* PPC_PNV_H */ diff --git a/include/hw/ppc/pnv_adu.h b/include/hw/ppc/pnv_adu.h new file mode 100644 index 00000000000..f9dbd8c8b37 --- /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 index 53e1d921d75..24ce37a9c8e 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -2,13 +2,18 @@ #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, @@ -23,6 +28,8 @@ struct PnvChip { 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; @@ -74,9 +81,11 @@ struct Pnv9Chip { PnvChip parent_obj; /*< public >*/ + PnvADU adu; PnvXive xive; Pnv9Psi psi; PnvLpcController lpc; + PnvChipTOD chiptod; PnvOCC occ; PnvSBE sbe; PnvHomer homer; @@ -86,6 +95,9 @@ struct Pnv9Chip { #define PNV9_CHIP_MAX_PEC 3 PnvPhb4PecState pecs[PNV9_CHIP_MAX_PEC]; + +#define PNV9_CHIP_MAX_I2C 4 + PnvI2C i2c[PNV9_CHIP_MAX_I2C]; }; /* @@ -103,22 +115,31 @@ struct Pnv10Chip { 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 >*/ @@ -130,16 +151,21 @@ struct PnvChipClass { uint32_t num_pecs; uint32_t num_phbs; + uint32_t i2c_num_engines; + const int *i2c_ports_per_engine; + DeviceRealize parent_realize; - uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id); + /* 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, Monitor *mon); + 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, Monitor *mon); + 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); }; diff --git a/include/hw/ppc/pnv_chiptod.h b/include/hw/ppc/pnv_chiptod.h new file mode 100644 index 00000000000..fde569bcbfa --- /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 4db21229a68..d8afb4f95f9 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -25,6 +25,27 @@ #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) @@ -35,8 +56,15 @@ struct PnvCore { /*< 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; @@ -53,6 +81,7 @@ struct PnvCoreClass { #define PNV_CORE_TYPE_NAME(cpu_model) cpu_model PNV_CORE_TYPE_SUFFIX typedef struct PnvCPUState { + PnvCore *pnv_core; Object *intc; } PnvCPUState; @@ -81,6 +110,9 @@ 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; diff --git a/include/hw/ppc/pnv_homer.h b/include/hw/ppc/pnv_homer.h index b1c5d498dc5..a6f2710fa16 100644 --- a/include/hw/ppc/pnv_homer.h +++ b/include/hw/ppc/pnv_homer.h @@ -41,19 +41,21 @@ struct PnvHomer { PnvChip *chip; MemoryRegion pba_regs; - MemoryRegion regs; + MemoryRegion mem; + hwaddr base; }; struct PnvHomerClass { DeviceClass parent_class; + /* Get base address of HOMER memory */ + hwaddr (*get_base)(PnvChip *chip); + /* Size of HOMER memory */ + int size; + int pba_size; const MemoryRegionOps *pba_ops; - int homer_size; - const MemoryRegionOps *homer_ops; - - hwaddr core_max_base; }; #endif /* PPC_PNV_HOMER_H */ diff --git a/include/hw/ppc/pnv_i2c.h b/include/hw/ppc/pnv_i2c.h new file mode 100644 index 00000000000..1a37730f1e5 --- /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 5d22c455704..266d56214f8 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -20,9 +20,10 @@ #ifndef PPC_PNV_LPC_H #define PPC_PNV_LPC_H -#include "exec/memory.h" +#include "system/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; @@ -73,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; @@ -84,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 { @@ -94,6 +109,11 @@ struct PnvLpcClass { DeviceRealize parent_realize; }; +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(PnvChip *chip, void *fdt, int root_offset, uint64_t lpcm_addr, uint64_t lpcm_size); diff --git a/include/hw/ppc/pnv_n1_chiplet.h b/include/hw/ppc/pnv_n1_chiplet.h new file mode 100644 index 00000000000..a7ad0396681 --- /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 00000000000..73cacf38238 --- /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 df321244e3b..013ea2e53e3 100644 --- a/include/hw/ppc/pnv_occ.h +++ b/include/hw/ppc/pnv_occ.h @@ -20,7 +20,7 @@ #ifndef PPC_PNV_OCC_H #define PPC_PNV_OCC_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #define TYPE_PNV_OCC "pnv-occ" @@ -41,11 +41,17 @@ DECLARE_INSTANCE_CHECKER(PnvOCC, PNV10_OCC, TYPE_PNV10_OCC) struct PnvOCC { DeviceState xd; + /* OCC dynamic model is driven by this timer. */ + QEMUTimer state_machine_timer; + /* OCC Misc interrupt */ uint64_t occmisc; qemu_irq psi_irq; + /* OCCs operate on regions of HOMER memory */ + PnvHomer *homer; + MemoryRegion xscom_regs; MemoryRegion sram_regs; }; @@ -53,6 +59,9 @@ struct PnvOCC { struct PnvOCCClass { DeviceClass parent_class; + hwaddr opal_shared_memory_offset; /* offset in HOMER */ + uint8_t opal_shared_memory_version; + int xscom_size; const MemoryRegionOps *xscom_ops; }; diff --git a/include/hw/ppc/pnv_pnor.h b/include/hw/ppc/pnv_pnor.h index 2e37ac88bf1..b44cafe918d 100644 --- a/include/hw/ppc/pnv_pnor.h +++ b/include/hw/ppc/pnv_pnor.h @@ -13,9 +13,11 @@ #include "hw/sysbus.h" /* - * PNOR offset on the LPC FW address space + * PNOR offset on the LPC FW address space. For now this should be 0 because + * skiboot 7.1 has a bug where IDSEL > 0 (LPC FW address > 256MB) access is + * not performed correctly. */ -#define PNOR_SPI_OFFSET 0x0c000000UL +#define PNOR_SPI_OFFSET 0x00000000UL #define TYPE_PNV_PNOR "pnv-pnor" OBJECT_DECLARE_SIMPLE_TYPE(PnvPnor, PNV_PNOR) @@ -26,6 +28,7 @@ struct PnvPnor { BlockBackend *blk; uint8_t *storage; + uint32_t lpc_address; /* Offset within LPC FW space */ int64_t size; MemoryRegion mmio; }; diff --git a/include/hw/ppc/pnv_psi.h b/include/hw/ppc/pnv_psi.h index 2a6f715350b..cf7f95a6b1f 100644 --- a/include/hw/ppc/pnv_psi.h +++ b/include/hw/ppc/pnv_psi.h @@ -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 b6b378ad14c..48a8b86a80b 100644 --- a/include/hw/ppc/pnv_sbe.h +++ b/include/hw/ppc/pnv_sbe.h @@ -20,7 +20,7 @@ #ifndef PPC_PNV_SBE_H #define PPC_PNV_SBE_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #define TYPE_PNV_SBE "pnv-sbe" diff --git a/include/hw/ppc/pnv_xive.h b/include/hw/ppc/pnv_xive.h index 9c48430ee41..5b4cb4167bb 100644 --- a/include/hw/ppc/pnv_xive.h +++ b/include/hw/ppc/pnv_xive.h @@ -93,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) @@ -163,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 9bc64635471..b14549db703 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -20,10 +20,10 @@ #ifndef PPC_PNV_XSCOM_H #define PPC_PNV_XSCOM_H -#include "exec/memory.h" -#include "hw/ppc/pnv.h" +#include "system/memory.h" typedef struct PnvXScomInterface PnvXScomInterface; +typedef struct PnvChip PnvChip; #define TYPE_PNV_XSCOM_INTERFACE "pnv-xscom-interface" #define PNV_XSCOM_INTERFACE(obj) \ @@ -64,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 @@ -79,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) */ @@ -90,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 @@ -114,11 +126,16 @@ struct PnvXScomInterfaceClass { #define PNV9_XSCOM_PEC_PCI_BASE 0xd010800 #define PNV9_XSCOM_PEC_PCI_SIZE 0x200 +#define PNV9_XSCOM_PEC_NEST_CPLT_BASE 0x0d000000 + /* XSCOM PCI "pass-through" window to PHB SCOM */ #define PNV9_XSCOM_PEC_PCI_STK0 0x100 #define PNV9_XSCOM_PEC_PCI_STK1 0x140 #define PNV9_XSCOM_PEC_PCI_STK2 0x180 +#define PNV10_XSCOM_ADU_BASE 0x0090000 +#define PNV10_XSCOM_ADU_SIZE 0x55 + /* * Layout of the XSCOM PCB addresses (POWER 10) */ @@ -149,6 +166,12 @@ struct PnvXScomInterfaceClass { #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 @@ -164,13 +187,27 @@ 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_NEST_CPLT_BASE 0x08000000 + #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 17a8dfc1071..8a14d623f8c 100644 --- a/include/hw/ppc/ppc.h +++ b/include/hw/ppc/ppc.h @@ -1,7 +1,7 @@ #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); @@ -116,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 ea7740239b3..2e94b006730 100644 --- a/include/hw/ppc/ppc4xx.h +++ b/include/hw/ppc/ppc4xx.h @@ -26,14 +26,9 @@ #define PPC4XX_H #include "hw/ppc/ppc.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.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" - /* * Generic DCR device */ diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index f4bd204d863..39bd5bd5ed3 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -2,7 +2,7 @@ #define HW_SPAPR_H #include "qemu/units.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/boards.h" #include "hw/ppc/spapr_drc.h" #include "hw/mem/pc-dimm.h" @@ -12,6 +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/spapr_nested.h" /* For SpaprMachineStateNested */ struct SpaprVioBus; struct SpaprPhbState; @@ -80,8 +81,12 @@ typedef enum { #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 +/* DAWR1 */ +#define SPAPR_CAP_DAWR1 0x0E /* Num Caps */ -#define SPAPR_CAP_NUM (SPAPR_CAP_AIL_MODE_3 + 1) +#define SPAPR_CAP_NUM (SPAPR_CAP_DAWR1 + 1) /* * Capability Values @@ -103,11 +108,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 @@ -141,11 +143,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 */ @@ -160,8 +159,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; @@ -197,7 +195,7 @@ 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; @@ -205,6 +203,7 @@ struct SpaprMachineState { uint32_t fdt_initial_size; void *fdt_blob; uint8_t fdt_rng_seed[32]; + uint64_t hashpkey_val; long kernel_size; bool kernel_le; uint64_t kernel_addr; @@ -217,7 +216,7 @@ struct SpaprMachineState { uint32_t vsmt; /* Virtual SMT mode (KVM's "core stride") */ /* Nested HV support (TCG only) */ - uint64_t nested_ptcr; + SpaprMachineStateNested nested; Notifier epow_notifier; QTAILQ_HEAD(, SpaprEventLogEntry) pending_events; @@ -276,7 +275,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]; @@ -368,6 +366,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 @@ -408,6 +409,7 @@ struct SpaprMachineState { #define H_SET_MODE_RESOURCE_SET_DAWR0 2 #define H_SET_MODE_RESOURCE_ADDR_TRANS_MODE 3 #define H_SET_MODE_RESOURCE_LE 4 +#define H_SET_MODE_RESOURCE_SET_DAWR1 5 /* Flags for H_SET_MODE_RESOURCE_LE */ #define H_SET_MODE_ENDIAN_BIG 0 @@ -587,8 +589,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. @@ -636,13 +646,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); -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); @@ -986,11 +1000,14 @@ 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; +extern const VMStateDescription vmstate_spapr_cap_dawr1; static inline uint8_t spapr_get_cap(SpaprMachineState *spapr, int cap) { @@ -1030,5 +1047,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 69a52e39b85..68f70834832 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 { diff --git a/include/hw/ppc/spapr_drc.h b/include/hw/ppc/spapr_drc.h index 02a63b36666..9ff42909c93 100644 --- a/include/hw/ppc/spapr_drc.h +++ b/include/hw/ppc/spapr_drc.h @@ -15,7 +15,7 @@ #include #include "qom/object.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/qdev-core.h" #include "qapi/error.h" diff --git a/include/hw/ppc/spapr_irq.h b/include/hw/ppc/spapr_irq.h index c22a72c9e27..cb9a85f6575 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 index d3834864764..f7be0d5a955 100644 --- a/include/hw/ppc/spapr_nested.h +++ b/include/hw/ppc/spapr_nested.h @@ -1,9 +1,407 @@ #ifndef HW_SPAPR_NESTED_H #define HW_SPAPR_NESTED_H -#include "qemu/osdep.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 - 0x07FF */ +#define GSB_L0_GUEST_HEAP_INUSE 0x0800 /* Guest Management Heap Size */ +#define GSB_L0_GUEST_HEAP_MAX 0x0801 /* Guest Management Heap Max Size */ +#define GSB_L0_GUEST_PGTABLE_SIZE_INUSE 0x0802 /* Guest Pagetable Size */ +#define GSB_L0_GUEST_PGTABLE_SIZE_MAX 0x0803 /* Guest Pagetable Max Size */ +#define GSB_L0_GUEST_PGTABLE_RECLAIMED 0x0804 /* Pagetable Reclaim in bytes */ + /* RESERVED 0x0805 - 0xBFF */ +#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; + + /** + * l0_guest_heap_inuse: The currently used bytes in the Hypervisor's Guest + * Management Space associated with the Host Partition. + **/ + uint64_t l0_guest_heap_inuse; + + /** + * host_heap_max: The maximum bytes available in the Hypervisor's Guest + * Management Space associated with the Host Partition. + **/ + uint64_t l0_guest_heap_max; + + /** + * host_pagetable: The currently used bytes in the Hypervisor's Guest + * Page Table Management Space associated with the Host Partition. + **/ + uint64_t l0_guest_pgtable_size_inuse; + + /** + * host_pagetable_max: The maximum bytes available in the Hypervisor's Guest + * Page Table Management Space associated with the Host Partition. + **/ + uint64_t l0_guest_pgtable_size_max; + + /** + * host_pagetable_reclaim: The amount of space in bytes that has been + * reclaimed due to overcommit in the Hypervisor's Guest Page Table + * Management Space associated with the Host Partition. + **/ + uint64_t l0_guest_pgtable_reclaimed; + + 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_GET_STATE_FLAGS_MASK 0xC000000000000000ULL +#define H_GUEST_SET_STATE_FLAGS_MASK 0x8000000000000000ULL +#define H_GUEST_SET_STATE_FLAGS_GUEST_WIDE 0x8000000000000000ULL +#define H_GUEST_GET_STATE_FLAGS_GUEST_WIDE 0x8000000000000000ULL +#define H_GUEST_GET_STATE_FLAGS_HOST_WIDE 0x4000000000000000ULL + +#define GUEST_STATE_REQUEST_GUEST_WIDE 0x1 +#define GUEST_STATE_REQUEST_HOST_WIDE 0x2 +#define GUEST_STATE_REQUEST_SET 0x4 + +/* + * 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_MACHINE_DW(i, f) { \ + .id = (i), \ + .size = 8, \ + .location = get_machine_ptr, \ + .offset = offsetof(struct SpaprMachineStateNested, f), \ + .copy = copy_state_8to8, \ + .mask = HVMASK_DEFAULT \ +} + +#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. @@ -94,9 +492,92 @@ struct nested_ppc_state { 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; }; -void spapr_register_nested(void); -void spapr_exit_nested(PowerPCCPU *cpu, int excp); +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_HOST_WIDE 0x2 +#define GUEST_STATE_ELEMENT_TYPE_FLAG_READ_ONLY 0x4 + uint16_t flags; + void *(*location)(struct SpaprMachineState *, 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 383611f10f2..00000000000 --- 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/spapr_vio.h b/include/hw/ppc/spapr_vio.h index 7eae1a48478..b8de4b06fb1 100644 --- a/include/hw/ppc/spapr_vio.h +++ b/include/hw/ppc/spapr_vio.h @@ -23,7 +23,7 @@ */ #include "hw/ppc/spapr.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/irq.h" #include "qom/object.h" diff --git a/include/hw/ppc/vof.h b/include/hw/ppc/vof.h index d3f293da8b7..3a0fbffe54c 100644 --- a/include/hw/ppc/vof.h +++ b/include/hw/ppc/vof.h @@ -7,8 +7,8 @@ #define HW_VOF_H #include "qom/object.h" -#include "exec/address-spaces.h" -#include "exec/memory.h" +#include "system/address-spaces.h" +#include "system/memory.h" #include "exec/cpu-defs.h" typedef struct Vof { diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 95ead0dd7c9..097fcdf00f9 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -28,7 +28,7 @@ #ifndef XICS_H #define XICS_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #include "qom/object.h" @@ -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 f120874e0ff..538f4386817 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -130,17 +130,15 @@ * TCTX Thread interrupt Context * * - * Copyright (c) 2017-2018, IBM Corporation. - * - * This code is licensed under the GPL version 2 or later. See the - * COPYING file in the top-level directory. + * Copyright (c) 2017-2024, IBM Corporation. * + * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef PPC_XIVE_H #define PPC_XIVE_H -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/sysbus.h" #include "hw/ppc/xive_regs.h" #include "qom/object.h" @@ -218,7 +216,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; } @@ -314,7 +312,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) { @@ -424,6 +422,7 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas); typedef struct XiveTCTXMatch { XiveTCTX *tctx; uint8_t ring; + bool precluded; } XiveTCTXMatch; #define TYPE_XIVE_PRESENTER "xive-presenter" @@ -439,10 +438,13 @@ struct XivePresenterClass { InterfaceClass parent; int (*match_nvt)(XivePresenter *xptr, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, 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 (*broadcast)(XivePresenter *xptr, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority); }; int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, @@ -451,8 +453,10 @@ int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, bool cam_ignore, uint32_t logic_serv); bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, - uint32_t logic_serv); + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, bool *precluded); + +uint32_t xive_get_vpgroup_size(uint32_t nvp_index); /* * XIVE Fabric (Interface between Interrupt Controller and Machine) @@ -469,8 +473,10 @@ struct XiveFabricClass { InterfaceClass parent; int (*match_nvt)(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, + bool crowd, bool cam_ignore, uint8_t priority, uint32_t logic_serv, XiveTCTXMatch *match); + int (*broadcast)(XiveFabric *xfb, uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority); }; /* @@ -510,6 +516,21 @@ static inline uint8_t xive_priority_to_ipb(uint8_t priority) 0 : 1 << (XIVE_PRIORITY_MAX - priority); } +static inline uint8_t xive_priority_to_pipr(uint8_t priority) +{ + return priority > XIVE_PRIORITY_MAX ? 0xFF : priority; +} + +/* + * Convert an Interrupt Pending Buffer (IPB) register to a Pending + * Interrupt Priority Register (PIPR), which contains the priority of + * the most favored pending notification. + */ +static inline uint8_t xive_ipb_to_pipr(uint8_t ibp) +{ + return ibp ? clz32((uint32_t)ibp << 24) : 0xff; +} + /* * XIVE Thread Interrupt Management Aera (TIMA) * @@ -528,12 +549,14 @@ 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_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, + uint8_t group_level); +void xive_tctx_reset_signal(XiveTCTX *tctx, uint8_t ring); +void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level); /* * KVM XIVE device helpers diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index ab68f8d157f..8cdf8191742 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -1,11 +1,9 @@ /* * QEMU PowerPC XIVE2 interrupt controller model (POWER10) * - * Copyright (c) 2019-2022, IBM Corporation. - * - * This code is licensed under the GPL version 2 or later. See the - * COPYING file in the top-level directory. + * Copyright (c) 2019-2024, IBM Corporation. * + * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef PPC_XIVE2_H @@ -53,6 +51,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; @@ -67,6 +71,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); @@ -78,7 +88,17 @@ void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked); int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint32_t logic_serv); + bool crowd, bool cam_ignore, + uint32_t logic_serv); + +uint64_t xive2_presenter_nvp_backlog_op(XivePresenter *xptr, + uint8_t blk, uint32_t idx, + uint16_t offset); + +uint64_t xive2_presenter_nvgc_backlog_op(XivePresenter *xptr, + bool crowd, + uint8_t blk, uint32_t idx, + uint16_t offset, uint16_t val); /* * XIVE2 END ESBs (POWER10) @@ -103,9 +123,21 @@ typedef struct Xive2EndSource { * XIVE2 Thread Interrupt Management Area (POWER10) */ +void xive2_tm_set_hv_cppr(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size); +void xive2_tm_set_os_cppr(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size); 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); +bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority); +void xive2_tm_set_lsmfb(XiveTCTX *tctx, int ring, uint8_t priority); +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 b7adbdb7b98..b11395c5635 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -1,16 +1,15 @@ /* * QEMU PowerPC XIVE2 internal structure definitions (POWER10) * - * Copyright (c) 2019-2022, IBM Corporation. + * Copyright (c) 2019-2024, IBM Corporation. * - * This code is licensed under the GPL version 2 or later. See the - * COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef PPC_XIVE2_REGS_H #define PPC_XIVE2_REGS_H -#include "cpu.h" +#include "qemu/bswap.h" /* * Thread Interrupt Management Area (TIMA) @@ -19,16 +18,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) @@ -48,7 +49,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) @@ -97,6 +98,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) @@ -111,6 +113,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) \ @@ -123,6 +127,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) { @@ -130,11 +138,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) @@ -143,7 +151,11 @@ typedef struct Xive2Nvp { uint32_t w0; #define NVP2_W0_VALID PPC_BIT32(0) #define NVP2_W0_HW PPC_BIT32(7) +#define NVP2_W0_L PPC_BIT32(8) +#define NVP2_W0_G PPC_BIT32(9) +#define NVP2_W0_T PPC_BIT32(10) #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) @@ -153,6 +165,8 @@ typedef struct Xive2Nvp { #define NVP2_W2_CPPR PPC_BITMASK32(0, 7) #define NVP2_W2_IPB PPC_BITMASK32(8, 15) #define NVP2_W2_LSMFB PPC_BITMASK32(16, 23) +#define NVP2_W2_T PPC_BIT32(27) +#define NVP2_W2_LGS PPC_BITMASK32(28, 31) uint32_t w3; uint32_t w4; #define NVP2_W4_ESC_ESB_BLOCK PPC_BITMASK32(0, 3) /* N:0 */ @@ -164,7 +178,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) @@ -194,12 +210,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; @@ -209,4 +228,16 @@ 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); + +#define NVx_BACKLOG_OP PPC_BITMASK(52, 53) +#define NVx_BACKLOG_PRIO PPC_BITMASK(57, 59) + +/* split the 6-bit crowd/group level */ +#define NVx_CROWD_LVL(level) ((level >> 4) & 0b11) +#define NVx_GROUP_LVL(level) (level & 0b1111) + #endif /* PPC_XIVE2_REGS_H */ diff --git a/include/hw/ppc/xive_regs.h b/include/hw/ppc/xive_regs.h index 4a3c9badd32..54bc6c53b46 100644 --- a/include/hw/ppc/xive_regs.h +++ b/include/hw/ppc/xive_regs.h @@ -7,10 +7,9 @@ * access to the different fields. * * - * Copyright (c) 2016-2018, IBM Corporation. + * Copyright (c) 2016-2024, IBM Corporation. * - * This code is licensed under the GPL version 2 or later. See the - * COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef PPC_XIVE_REGS_H @@ -77,8 +76,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 @@ -98,6 +100,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) @@ -114,26 +117,42 @@ * 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 */ +/* + * NSR fields for the various QW ack types + * + * P10 has an extra bit in QW3 for the group level instead of the + * reserved 'i' bit. Since it is not used and we don't support group + * interrupts on P9, we use the P10 definition for the group level so + * that we can have common macros for the NSR + */ #define TM_QW0_NSR_EB PPC_BIT8(0) #define TM_QW1_NSR_EO PPC_BIT8(0) #define TM_QW3_NSR_HE PPC_BITMASK8(0, 1) @@ -141,8 +160,15 @@ #define TM_QW3_NSR_HE_POOL 1 #define TM_QW3_NSR_HE_PHYS 2 #define TM_QW3_NSR_HE_LSI 3 -#define TM_QW3_NSR_I PPC_BIT8(2) -#define TM_QW3_NSR_GRP_LVL PPC_BIT8(3, 7) +#define TM_NSR_GRP_LVL PPC_BITMASK8(2, 7) +/* + * On P10, the format of the 6-bit group level is: 2 bits for the + * crowd size and 4 bits for the group size. Since group/crowd size is + * always a power of 2, we encode the log. For example, group_level=4 + * means crowd size = 0 and group size = 16 (2^4) + * Same encoding is used in the NVP and NVGC structures for + * PGoFirst and PGoNext fields + */ /* * EAS (Event Assignment Structure) @@ -167,7 +193,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) { @@ -261,9 +287,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 151d9682380..530f3da7021 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -95,6 +95,7 @@ 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); /** * struct DeviceClass - The base class for all devices. @@ -103,6 +104,9 @@ typedef void (*BusUnrealize)(BusState *bus); * 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 * @@ -132,7 +136,13 @@ struct DeviceClass { * ensures a compile-time error if someone attempts to assign * dc->props directly. */ - Property *props_; + const Property *props_; + + /** + * @props_count_: number of elements in @props_; should only be + * assigned by using device_class_set_props(). + */ + uint16_t props_count_; /** * @user_creatable: Can user instantiate with -device / device_add? @@ -152,16 +162,17 @@ struct DeviceClass { /* callbacks */ /** - * @reset: deprecated device reset method pointer + * @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; /** * @vmsd: device state serialisation description for @@ -237,10 +248,6 @@ struct DeviceState { * @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? */ @@ -294,6 +301,7 @@ struct DeviceState { MemReentrancyGuard mem_reentrancy_guard; }; +typedef struct DeviceListener DeviceListener; struct DeviceListener { void (*realize)(DeviceListener *listener, DeviceState *dev); void (*unrealize)(DeviceListener *listener, DeviceState *dev); @@ -329,8 +337,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) @@ -534,7 +540,8 @@ void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, int required_for_version); HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); -bool qdev_hotplug_allowed(DeviceState *dev, Error **errp); +bool qdev_hotplug_allowed(DeviceState *dev, BusState *bus, Error **errp); +bool qdev_hotunplug_allowed(DeviceState *dev, Error **errp); /** * qdev_get_hotplug_handler() - Get handler responsible for device wiring @@ -548,6 +555,7 @@ bool qdev_hotplug_allowed(DeviceState *dev, Error **errp); */ 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); @@ -625,8 +633,9 @@ 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 @@ -674,15 +683,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 @@ -797,7 +806,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 @@ -929,29 +938,38 @@ char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev); /** * 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() + * @props: an array of properties * * 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???? + * + * Validate that @props has at least one Property. + * Validate that @props is an array, not a pointer, via ARRAY_SIZE. + * Validate that the array does not have a legacy terminator at compile-time; + * requires -O2 and the array to be const. */ -void device_class_set_props(DeviceClass *dc, Property *props); +#define device_class_set_props(dc, props) \ + do { \ + QEMU_BUILD_BUG_ON(sizeof(props) == 0); \ + size_t props_count_ = ARRAY_SIZE(props); \ + if ((props)[props_count_ - 1].name == NULL) { \ + qemu_build_not_reached(); \ + } \ + device_class_set_props_n((dc), (props), props_count_); \ + } while (0) /** - * device_class_set_parent_reset() - legacy set device reset handlers - * @dc: device class - * @dev_reset: function pointer to reset handler - * @parent_reset: function pointer to parents reset handler - * - * Modern code should use the ResettableClass interface to - * implement a multi-phase reset instead. + * device_class_set_props_n(): add a set of properties to an device + * @dc: the parent DeviceClass all devices inherit + * @props: an array of properties + * @n: ARRAY_SIZE(@props) * - * TODO: remove the function when DeviceClass's reset method - * is not used anymore. + * 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_class_set_parent_reset(DeviceClass *dc, - DeviceReset dev_reset, - DeviceReset *parent_reset); +void device_class_set_props_n(DeviceClass *dc, const Property *props, size_t n); /** * device_class_set_parent_realize() - set up for chaining realize fns @@ -969,6 +987,19 @@ 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 @@ -993,6 +1024,40 @@ const char *qdev_fw_name(DeviceState *dev); void qdev_assert_realized_properly(void); Object *qdev_get_machine(void); +/** + * qdev_create_fake_machine(): Create a fake machine container. + * + * .. note:: + * This function is a kludge for user emulation (USER_ONLY) + * because when thread (TYPE_CPU) are realized, qdev_realize() + * access a machine container. + */ +void qdev_create_fake_machine(void); + +/** + * machine_get_container: + * @name: The name of container to lookup + * + * Get a container of the machine (QOM path "/machine/NAME"). + * + * Returns: the machine container object. + */ +Object *machine_get_container(const char *name); + +/** + * 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); @@ -1071,6 +1136,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 diff --git a/include/hw/qdev-properties-system.h b/include/hw/qdev-properties-system.h index 0ac327ae609..b921392c525 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,10 @@ 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; +extern const PropertyInfo qdev_prop_endian_mode; +extern const PropertyInfo qdev_prop_vmapple_virtio_blk_variant; #define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_pci_devfn, int32_t) @@ -41,6 +51,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 +91,23 @@ 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 *) + +#define DEFINE_PROP_ENDIAN(_name, _state, _field, _default) \ + DEFINE_PROP_UNSIGNED(_name, _state, _field, _default, \ + qdev_prop_endian_mode, EndianMode) +#define DEFINE_PROP_ENDIAN_NODEFAULT(_name, _state, _field) \ + DEFINE_PROP_ENDIAN(_name, _state, _field, ENDIAN_MODE_UNSPECIFIED) + +#define DEFINE_PROP_VMAPPLE_VIRTIO_BLK_VARIANT(_name, _state, _fld, _default) \ + DEFINE_PROP_UNSIGNED(_name, _state, _fld, _default, \ + qdev_prop_vmapple_virtio_blk_variant, \ + VMAppleVirtioBlkVariant) #endif diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index e1df08876c6..2c99856caac 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -16,28 +16,28 @@ struct Property { const char *name; const PropertyInfo *info; ptrdiff_t offset; - uint8_t bitnr; + const char *link_type; uint64_t bitmask; - bool set_default; union { int64_t i; uint64_t u; } defval; - int arrayoffset; const PropertyInfo *arrayinfo; + int arrayoffset; int arrayfieldsize; - const char *link_type; + uint8_t bitnr; + bool set_default; }; struct PropertyInfo { - const char *name; + const char *type; const char *description; const QEnumLookup *enum_table; bool realized_set_allowed; /* allow setting property on realized device */ - int (*print)(Object *obj, Property *prop, char *dest, size_t len); + int (*print)(Object *obj, const 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; @@ -49,10 +49,10 @@ struct PropertyInfo { extern const PropertyInfo qdev_prop_bit; extern const PropertyInfo qdev_prop_bit64; extern const PropertyInfo qdev_prop_bool; -extern const PropertyInfo qdev_prop_enum; extern const PropertyInfo qdev_prop_uint8; extern const PropertyInfo qdev_prop_uint16; extern const PropertyInfo qdev_prop_uint32; +extern const PropertyInfo qdev_prop_usize; extern const PropertyInfo qdev_prop_int32; extern const PropertyInfo qdev_prop_uint64; extern const PropertyInfo qdev_prop_uint64_checkmask; @@ -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. + * + * @_arraytype is required to be movable with memcpy(). * - * When the array length is set, the @_field member of the device + * 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) \ @@ -176,9 +171,6 @@ extern const PropertyInfo qdev_prop_link; #define DEFINE_PROP_SIZE32(_n, _s, _f, _d) \ DEFINE_PROP_UNSIGNED(_n, _s, _f, _d, qdev_prop_size32, uint32_t) -#define DEFINE_PROP_END_OF_LIST() \ - {} - /* * Set properties between creation and realization. * @@ -206,7 +198,10 @@ 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); -void *object_field_prop_ptr(Object *obj, Property *prop); +/* Takes ownership of @values */ +void qdev_prop_set_array(DeviceState *dev, const char *name, QList *values); + +void *object_field_prop_ptr(Object *obj, const Property *prop); void qdev_prop_register_global(GlobalProperty *prop); const GlobalProperty *qdev_find_global_prop(Object *obj, @@ -225,15 +220,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 80b2e531c47..00000000000 --- 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/register.h b/include/hw/register.h index 6a076cfcdf0..a913c52aee5 100644 --- a/include/hw/register.h +++ b/include/hw/register.h @@ -12,7 +12,7 @@ #define REGISTER_H #include "hw/qdev-core.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/registerfields.h" #include "qom/object.h" diff --git a/include/hw/remote/iohub.h b/include/hw/remote/iohub.h index 6a8444f9a97..09ee6c77b68 100644 --- a/include/hw/remote/iohub.h +++ b/include/hw/remote/iohub.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-memory-listener.h b/include/hw/remote/proxy-memory-listener.h index c4f3efb9285..ec516d82676 100644 --- a/include/hw/remote/proxy-memory-listener.h +++ b/include/hw/remote/proxy-memory-listener.h @@ -9,7 +9,7 @@ #ifndef PROXY_MEMORY_LISTENER_H #define PROXY_MEMORY_LISTENER_H -#include "exec/memory.h" +#include "system/memory.h" #include "io/channel.h" typedef struct ProxyMemoryListener { diff --git a/include/hw/resettable.h b/include/hw/resettable.h index bdcd1276b69..fd862f1e9f1 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 a2e4ae9cb02..7d59b2e6c63 100644 --- a/include/hw/riscv/boot.h +++ b/include/hw/riscv/boot.h @@ -27,36 +27,50 @@ #define RISCV32_BIOS_BIN "opensbi-riscv32-generic-fw_dynamic.bin" #define RISCV64_BIOS_BIN "opensbi-riscv64-generic-fw_dynamic.bin" +typedef struct RISCVBootInfo { + ssize_t kernel_size; + hwaddr image_low_addr; + hwaddr image_high_addr; + + hwaddr initrd_start; + ssize_t initrd_size; + + bool is_32bit; +} RISCVBootInfo; + bool riscv_is_32bit(RISCVHartArrayState *harts); char *riscv_plic_hart_config_string(int hart_count); -target_ulong riscv_calc_kernel_start_addr(RISCVHartArrayState *harts, +void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts); +target_ulong riscv_calc_kernel_start_addr(RISCVBootInfo *info, 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); 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(MachineState *machine, - RISCVHartArrayState *harts, - target_ulong firmware_end_addr, - bool load_initrd, - symbol_fn_t sym_cb); -uint64_t riscv_compute_fdt_addr(hwaddr dram_start, uint64_t dram_size, - MachineState *ms); +void riscv_load_kernel(MachineState *machine, + RISCVBootInfo *info, + target_ulong kernel_start_addr, + bool load_initrd, + symbol_fn_t sym_cb); +uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size, + MachineState *ms, RISCVBootInfo *info); 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 1b749663dca..18664a174b5 100644 --- a/include/hw/riscv/boot_opensbi.h +++ b/include/hw/riscv/boot_opensbi.h @@ -58,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 00000000000..8a8acfc3f07 --- /dev/null +++ b/include/hw/riscv/iommu.h @@ -0,0 +1,42 @@ +/* + * 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; + +#define TYPE_RISCV_IOMMU_SYS "riscv-iommu-device" +OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUStateSys, RISCV_IOMMU_SYS) +typedef struct RISCVIOMMUStateSys RISCVIOMMUStateSys; + +#define FDT_IRQ_TYPE_EDGE_LOW 1 + +#endif diff --git a/include/hw/riscv/microchip_pfsoc.h b/include/hw/riscv/microchip_pfsoc.h index daef086da60..7ca9b976c10 100644 --- a/include/hw/riscv/microchip_pfsoc.h +++ b/include/hw/riscv/microchip_pfsoc.h @@ -67,6 +67,7 @@ typedef struct MicrochipIcicleKitState { MachineState parent_obj; /*< public >*/ + uint32_t clint_timebase_freq; MicrochipPFSoCState soc; } MicrochipIcicleKitState; diff --git a/include/hw/riscv/numa.h b/include/hw/riscv/numa.h index 8f5280211d1..147f01619b9 100644 --- a/include/hw/riscv/numa.h +++ b/include/hw/riscv/numa.h @@ -21,7 +21,7 @@ #include "hw/boards.h" #include "hw/sysbus.h" -#include "sysemu/numa.h" +#include "system/numa.h" /** * riscv_socket_count: diff --git a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h index 912b4a2682d..a6ed73a1956 100644 --- a/include/hw/riscv/riscv_hart.h +++ b/include/hw/riscv/riscv_hart.h @@ -38,6 +38,10 @@ struct RISCVHartArrayState { uint32_t hartid_base; char *cpu_type; uint64_t resetvec; + uint32_t num_rnmi_irqvec; + uint64_t *rnmi_irqvec; + uint32_t num_rnmi_excpvec; + uint64_t *rnmi_excpvec; RISCVCPU *harts; }; diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index e5c474b26eb..7b4c2c8b7de 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -23,6 +23,7 @@ #include "hw/riscv/riscv_hart.h" #include "hw/sysbus.h" #include "hw/block/flash.h" +#include "hw/intc/riscv_imsic.h" #define VIRT_CPUS_MAX_BITS 9 #define VIRT_CPUS_MAX (1 << VIRT_CPUS_MAX_BITS) @@ -60,6 +61,9 @@ struct RISCVVirtState { char *oem_table_id; OnOffAuto acpi; const MemMapEntry *memmap; + struct GPEXHost *gpex_host; + OnOffAuto iommu_sys; + uint16_t pci_iommu_bdf; }; enum { @@ -82,7 +86,8 @@ enum { VIRT_PCIE_MMIO, VIRT_PCIE_PIO, VIRT_PLATFORM_BUS, - VIRT_PCIE_ECAM + VIRT_PCIE_ECAM, + VIRT_IOMMU_SYS, }; enum { @@ -91,6 +96,7 @@ enum { VIRTIO_IRQ = 1, /* 1 to 8 */ VIRTIO_COUNT = 8, PCIE_IRQ = 0x20, /* 32 to 35 */ + IOMMU_SYS_IRQ = 0x24, /* 36-39 */ VIRT_PLATFORM_BUS_IRQ = 64, /* 64 to 95 */ }; @@ -116,6 +122,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 + \ @@ -126,5 +133,30 @@ enum { 1 + FDT_APLIC_INT_CELLS) bool virt_is_acpi_enabled(RISCVVirtState *s); +bool virt_is_iommu_sys_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/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h index 97cec0b3e84..64893be1515 100644 --- a/include/hw/rtc/mc146818rtc.h +++ b/include/hw/rtc/mc146818rtc.h @@ -55,6 +55,6 @@ MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq); void mc146818rtc_set_cmos_data(MC146818RtcState *s, int addr, int val); int mc146818rtc_get_cmos_data(MC146818RtcState *s, int addr); -void qmp_rtc_reset_reinjection(Error **errp); +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 fc54dfcba47..26a9eb61967 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 73ceeb58e55..766fe0e4355 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 7f1703508c4..d4fadc4f7f8 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/ap-bridge.h b/include/hw/s390x/ap-bridge.h index 470e439a98e..7efc52928db 100644 --- a/include/hw/s390x/ap-bridge.h +++ b/include/hw/s390x/ap-bridge.h @@ -16,4 +16,43 @@ void s390_init_ap(void); +typedef struct ChscSeiNt0Res { + uint16_t length; + uint16_t code; + uint8_t reserved1; + uint16_t reserved2; + uint8_t nt; +#define PENDING_EVENT_INFO_BITMASK 0x80; + uint8_t flags; + uint8_t reserved3; + uint8_t rs; + uint8_t cc; +} QEMU_PACKED ChscSeiNt0Res; + +#define NT0_RES_RESPONSE_CODE 1 +#define NT0_RES_NT_DEFAULT 0 +#define NT0_RES_RS_AP_CHANGE 5 +#define NT0_RES_CC_AP_CHANGE 3 + +#define EVENT_INFORMATION_NOT_STORED 1 +#define EVENT_INFORMATION_STORED 0 + +/** + * ap_chsc_sei_nt0_get_event - Retrieve the next pending AP config + * change event + * @res: Pointer to a ChscSeiNt0Res struct to be filled with event + * data + * + * This function checks for any pending AP config change events and, + * if present, populates the provided response structure with the + * appropriate SEI NT0 fields. + * + * Return: + * EVENT_INFORMATION_STORED - An event was available and written to @res + * EVENT_INFORMATION_NOT_STORED - No event was available + */ +int ap_chsc_sei_nt0_get_event(void *res); + +bool ap_chsc_sei_nt0_have_event(void); + #endif diff --git a/include/hw/s390x/cpu-topology.h b/include/hw/s390x/cpu-topology.h new file mode 100644 index 00000000000..d5e9aa43f8f --- /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-s390x.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-bridge.h b/include/hw/s390x/css-bridge.h index deb606d71f1..4f874ed781b 100644 --- a/include/hw/s390x/css-bridge.h +++ b/include/hw/s390x/css-bridge.h @@ -19,7 +19,6 @@ /* virtual css bridge */ struct VirtualCssBridge { SysBusDevice sysbus_dev; - bool css_dev_path; }; #define TYPE_VIRTUAL_CSS_BRIDGE "virtual-css-bridge" diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index 75e53816135..0b0400a9d4f 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -15,7 +15,7 @@ #include "hw/s390x/adapter.h" #include "hw/s390x/s390_flic.h" #include "hw/s390x/ioinst.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "target/s390x/cpu-qom.h" /* Channel subsystem constants. */ @@ -233,12 +233,11 @@ 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); -#ifndef CONFIG_USER_ONLY SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid); bool css_subch_visible(SubchDev *sch); @@ -262,7 +261,6 @@ int css_enable_mss(void); IOInstEnding css_do_rsch(SubchDev *sch); int css_do_rchp(uint8_t cssid, uint8_t chpid); bool css_present(uint8_t cssid); -#endif extern const PropertyInfo css_devid_ro_propinfo; diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h index 3ffd575d8ff..ff874e792d3 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 00000000000..6824391111c --- /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 2c807ee3a1a..2e0a7099813 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 b1bdbeaeb5b..04944d4fed7 100644 --- a/include/hw/s390x/s390-pci-bus.h +++ b/include/hw/s390x/s390-pci-bus.h @@ -277,6 +277,7 @@ struct S390PCIIOMMU { AddressSpace as; MemoryRegion mr; IOMMUMemoryRegion iommu_mr; + MemoryRegion *dm_mr; bool enabled; uint64_t g_iota; uint64_t pba; @@ -361,6 +362,8 @@ struct S390PCIBusDevice { bool unplug_requested; bool interp; bool forwarding_assist; + bool aif; + bool rtr_avail; QTAILQ_ENTRY(S390PCIBusDevice) link; }; @@ -388,6 +391,7 @@ int pci_chsc_sei_nt2_have_event(void); void s390_pci_sclp_configure(SCCB *sccb); void s390_pci_sclp_deconfigure(SCCB *sccb); void s390_pci_iommu_enable(S390PCIIOMMU *iommu); +void s390_pci_iommu_direct_map_enable(S390PCIIOMMU *iommu); void s390_pci_iommu_disable(S390PCIIOMMU *iommu); void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid, uint64_t faddr, uint32_t e); @@ -400,5 +404,6 @@ S390PCIBusDevice *s390_pci_find_dev_by_target(S390pciState *s, const char *target); S390PCIBusDevice *s390_pci_find_next_avail_dev(S390pciState *s, S390PCIBusDevice *pbdev); +void s390_pci_ism_reset(void); #endif diff --git a/include/hw/s390x/s390-pci-clp.h b/include/hw/s390x/s390-pci-clp.h index 03b7f9ba5f7..6a635d693ba 100644 --- a/include/hw/s390x/s390-pci-clp.h +++ b/include/hw/s390x/s390-pci-clp.h @@ -158,6 +158,7 @@ typedef struct ClpRspQueryPciGrp { #define CLP_RSP_QPCIG_MASK_NOI 0xfff uint16_t i; uint8_t version; +#define CLP_RSP_QPCIG_MASK_RTR 0x20 #define CLP_RSP_QPCIG_MASK_FRAME 0x2 #define CLP_RSP_QPCIG_MASK_REFRESH 0x1 uint8_t fr; diff --git a/include/hw/s390x/s390-pci-inst.h b/include/hw/s390x/s390-pci-inst.h index a55c448aad2..5cb8da540be 100644 --- a/include/hw/s390x/s390-pci-inst.h +++ b/include/hw/s390x/s390-pci-inst.h @@ -15,7 +15,7 @@ #define HW_S390_PCI_INST_H #include "s390-pci-bus.h" -#include "sysemu/dma.h" +#include "system/dma.h" /* Load/Store status codes */ #define ZPCI_PCI_ST_FUNC_NOT_ENABLED 4 diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index 9bba21a9167..321b26df308 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,31 +29,31 @@ struct S390CcwMachineState { bool dea_key_wrap; bool pv; uint8_t loadparm[8]; + uint64_t memory_limit; + uint64_t max_pagesize; + + SCLPDevice *sclp; }; +static inline uint64_t s390_get_memory_limit(S390CcwMachineState *s390ms) +{ + /* We expect to be called only after the limit was set. */ + assert(s390ms->memory_limit); + return s390ms->memory_limit; +} + +#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; /*< public >*/ - bool ri_allowed; - bool cpu_model_allowed; - bool css_migration_enabled; - bool hpage_1m_allowed; int max_threads; }; -/* runtime-instrumentation allowed by the machine */ -bool ri_allowed(void); -/* cpu model allowed by the machine */ -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 3907a13d076..91edaaca409 100644 --- a/include/hw/s390x/s390_flic.h +++ b/include/hw/s390x/s390_flic.h @@ -35,17 +35,12 @@ 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) struct S390FLICState { SysBusDevice parent_obj; - /* to limit AdapterRoutes.num_routes for compat */ - uint32_t adapter_routes_max_batch; bool ais_supported; }; @@ -118,6 +113,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 cf1f2efae2e..d32f6180e0d 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 @@ -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 5239eb538c1..b5c6d8fa552 100644 --- a/include/hw/s390x/storage-attributes.h +++ b/include/hw/s390x/storage-attributes.h @@ -25,7 +25,6 @@ OBJECT_DECLARE_TYPE(S390StAttribState, S390StAttribClass, S390_STATTRIB) struct S390StAttribState { DeviceState parent_obj; uint64_t migration_cur_gfn; - bool migration_enabled; }; @@ -39,7 +38,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 aa2ec2aae50..ac303001f57 100644 --- a/include/hw/s390x/storage-keys.h +++ b/include/hw/s390x/storage-keys.h @@ -21,8 +21,6 @@ OBJECT_DECLARE_TYPE(S390SKeysState, S390SKeysClass, S390_SKEYS) struct S390SKeysState { DeviceState parent_obj; - bool migration_enabled; - }; @@ -111,10 +109,36 @@ 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); +void s390_qmp_dump_skeys(const char *filename, Error **errp); void hmp_dump_skeys(Monitor *mon, const QDict *qdict); void hmp_info_skeys(Monitor *mon, const QDict *qdict); +#define TYPE_DUMP_SKEYS_INTERFACE "dump-skeys-interface" + +typedef struct DumpSKeysInterface DumpSKeysInterface; +DECLARE_CLASS_CHECKERS(DumpSKeysInterface, DUMP_SKEYS_INTERFACE, + TYPE_DUMP_SKEYS_INTERFACE) + +struct DumpSKeysInterface { + InterfaceClass parent_class; + + /** + * @qmp_dump_skeys: Callback to dump guest's storage keys to @filename. + */ + void (*qmp_dump_skeys)(const char *filename, Error **errp); +}; + #endif /* S390_STORAGE_KEYS_H */ diff --git a/include/hw/s390x/vfio-ccw.h b/include/hw/s390x/vfio-ccw.h index 63a909eb7e9..1e0922dca11 100644 --- a/include/hw/s390x/vfio-ccw.h +++ b/include/hw/s390x/vfio-ccw.h @@ -14,7 +14,7 @@ #ifndef HW_VFIO_CCW_H #define HW_VFIO_CCW_H -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include "hw/s390x/s390-ccw.h" #include "hw/s390x/ccw-device.h" #include "qom/object.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 13b17496f8c..533d856aa34 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 3692ca82f31..90ee192b4d4 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -24,6 +24,7 @@ struct SCSIRequest { SCSIBus *bus; SCSIDevice *dev; const SCSIReqOps *ops; + AioContext *ctx; uint32_t refcount; uint32_t tag; uint32_t lun; @@ -48,6 +49,8 @@ struct SCSIRequest { bool dma_started; BlockAIOCB *aiocb; QEMUSGList *sg; + + /* Protected by SCSIDevice->requests_lock */ QTAILQ_ENTRY(SCSIRequest) next; }; @@ -69,14 +72,16 @@ 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; + + QemuMutex requests_lock; /* protects the requests list */ QTAILQ_HEAD(, SCSIRequest) requests; + uint32_t channel; uint32_t lun; int blocksize; @@ -194,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); diff --git a/include/hw/sd/aspeed_sdhci.h b/include/hw/sd/aspeed_sdhci.h index 057bc5f3d13..4ef17704711 100644 --- a/include/hw/sd/aspeed_sdhci.h +++ b/include/hw/sd/aspeed_sdhci.h @@ -13,9 +13,12 @@ #include "qom/object.h" #define TYPE_ASPEED_SDHCI "aspeed.sdhci" -OBJECT_DECLARE_SIMPLE_TYPE(AspeedSDHCIState, ASPEED_SDHCI) +#define TYPE_ASPEED_2400_SDHCI TYPE_ASPEED_SDHCI "-ast2400" +#define TYPE_ASPEED_2500_SDHCI TYPE_ASPEED_SDHCI "-ast2500" +#define TYPE_ASPEED_2600_SDHCI TYPE_ASPEED_SDHCI "-ast2600" +#define TYPE_ASPEED_2700_SDHCI TYPE_ASPEED_SDHCI "-ast2700" +OBJECT_DECLARE_TYPE(AspeedSDHCIState, AspeedSDHCIClass, ASPEED_SDHCI) -#define ASPEED_SDHCI_CAPABILITIES 0x01E80080 #define ASPEED_SDHCI_NUM_SLOTS 2 #define ASPEED_SDHCI_NUM_REGS (ASPEED_SDHCI_REG_SIZE / sizeof(uint32_t)) #define ASPEED_SDHCI_REG_SIZE 0x100 @@ -32,4 +35,10 @@ struct AspeedSDHCIState { uint32_t regs[ASPEED_SDHCI_NUM_REGS]; }; +struct AspeedSDHCIClass { + SysBusDeviceClass parent_class; + + uint64_t capareg; +}; + #endif /* ASPEED_SDHCI_H */ diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index 2c8748fb9b9..d6bad175131 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; @@ -96,6 +88,9 @@ 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,9 +119,10 @@ struct SDCardClass { void (*set_voltage)(SDState *sd, uint16_t millivolts); uint8_t (*get_dat_lines)(SDState *sd); bool (*get_cmd_line)(SDState *sd); - 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; }; diff --git a/include/hw/sd/sdcard_legacy.h b/include/hw/sd/sdcard_legacy.h deleted file mode 100644 index 0dc38895551..00000000000 --- a/include/hw/sd/sdcard_legacy.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * SD Memory Card emulation (deprecated legacy API) - * - * Copyright (c) 2006 Andrzej Zaborowski - * - * 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. - */ -#ifndef HW_SDCARD_LEGACY_H -#define HW_SDCARD_LEGACY_H - -#include "hw/sd/sd.h" - -/* Legacy functions to be used only by non-qdevified callers */ -SDState *sd_init(BlockBackend *blk, bool is_spi); -int sd_do_command(SDState *card, SDRequest *request, uint8_t *response); -void sd_write_byte(SDState *card, uint8_t value); -uint8_t sd_read_byte(SDState *card); -void sd_set_cb(SDState *card, qemu_irq readonly, qemu_irq insert); - -/* sd_enable should not be used -- it is only used on the nseries boards, - * where it is part of a broken implementation of the MMC card slot switch - * (there should be two card slots which are multiplexed to a single MMC - * controller, but instead we model it with one card and controller and - * disable the card when the second slot is selected, so it looks like the - * second slot is always empty). - */ -void sd_enable(SDState *card, bool enable); - -#endif /* HW_SDCARD_LEGACY_H */ diff --git a/include/hw/sd/sdhci.h b/include/hw/sd/sdhci.h index 6cd2822f1d1..51fb30ea528 100644 --- a/include/hw/sd/sdhci.h +++ b/include/hw/sd/sdhci.h @@ -100,11 +100,16 @@ struct SDHCIState { uint8_t sd_spec_version; uint8_t uhs_mode; uint8_t vendor; /* For vendor specific functionality */ + /* + * Write Protect pin default active low for detecting SD card + * to be protected. Set wp_inverted to invert the signal. + */ + bool wp_inverted; }; typedef struct SDHCIState SDHCIState; #define SDHCI_VENDOR_NONE 0 -#define SDHCI_VENDOR_IMX 1 +#define SDHCI_VENDOR_FSL 2 /* * Controller does not provide transfer-complete interrupt when not diff --git a/include/hw/sh4/sh.h b/include/hw/sh4/sh.h index ec716cdd458..c82feef8d0a 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/sh4/sh_intc.h b/include/hw/sh4/sh_intc.h index f62d5c5e136..94f183121eb 100644 --- a/include/hw/sh4/sh_intc.h +++ b/include/hw/sh4/sh_intc.h @@ -1,7 +1,7 @@ #ifndef SH_INTC_H #define SH_INTC_H -#include "exec/memory.h" +#include "system/memory.h" typedef unsigned char intc_enum; diff --git a/include/hw/southbridge/ich9.h b/include/hw/southbridge/ich9.h index fd01649d04b..1e231e89c92 100644 --- a/include/hw/southbridge/ich9.h +++ b/include/hw/southbridge/ich9.h @@ -7,7 +7,7 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_device.h" #include "hw/rtc/mc146818rtc.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/notify.h" #include "qom/object.h" @@ -196,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 diff --git a/include/hw/southbridge/piix.h b/include/hw/southbridge/piix.h index 278171752fa..86709ba2e48 100644 --- a/include/hw/southbridge/piix.h +++ b/include/hw/southbridge/piix.h @@ -13,7 +13,10 @@ #define HW_SOUTHBRIDGE_PIIX_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,32 +41,42 @@ struct PIIXState { * So one PIC level is tracked by PIIX_NUM_PIRQS bits. * * PIRQ is mapped to PIC pins, we track it by - * PIIX_NUM_PIRQS * PIIX_NUM_PIC_IRQS = 64 bits with + * PIIX_NUM_PIRQS * ISA_NUM_IRQS = 64 bits with * pic_irq * PIIX_NUM_PIRQS + pirq */ -#if PIIX_NUM_PIC_IRQS * PIIX_NUM_PIRQS > 64 +#if ISA_NUM_IRQS * PIIX_NUM_PIRQS > 64 #error "unable to encode pic state in 64bit in pic_levels." #endif uint64_t pic_levels; - qemu_irq *pic; + qemu_irq cpu_intr; + qemu_irq isa_irqs_in[ISA_NUM_IRQS]; /* This member isn't used. Just for save/load compatibility */ int32_t pci_irq_levels_vmstate[PIIX_NUM_PIRQS]; MC146818RtcState rtc; + PCIIDEState ide; + UHCIState uhci; + PIIX4PMState pm; + + uint32_t smb_io_base; /* Reset Control Register contents */ uint8_t rcr; /* IO memory region for Reset Control Register (PIIX_RCR_IOPORT) */ MemoryRegion rcr_mem; + + bool has_acpi; + bool has_pic; + bool has_pit; + bool has_usb; + bool smm_enabled; }; -typedef struct PIIXState PIIX3State; -#define TYPE_PIIX3_PCI_DEVICE "pci-piix3" -DECLARE_INSTANCE_CHECKER(PIIX3State, PIIX3_PCI_DEVICE, - TYPE_PIIX3_PCI_DEVICE) +#define TYPE_PIIX_PCI_DEVICE "pci-piix" +OBJECT_DECLARE_SIMPLE_TYPE(PIIXState, PIIX_PCI_DEVICE) #define TYPE_PIIX3_DEVICE "PIIX3" #define TYPE_PIIX4_PCI_DEVICE "piix4-isa" diff --git a/include/hw/sparc/grlib.h b/include/hw/sparc/grlib.h deleted file mode 100644 index ef1946c7f81..00000000000 --- 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 00000000000..da46e29a278 --- /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 8e1dda556b9..25b95e74060 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 00000000000..d3f8cec1119 --- /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/npcm7xx_fiu.h b/include/hw/ssi/npcm7xx_fiu.h index a3a17042896..7ebd422ca6c 100644 --- a/include/hw/ssi/npcm7xx_fiu.h +++ b/include/hw/ssi/npcm7xx_fiu.h @@ -60,6 +60,7 @@ struct NPCM7xxFIUState { int32_t cs_count; int32_t active_cs; qemu_irq *cs_lines; + uint64_t flash_size; NPCM7xxFIUFlash *flash; SSIBus *spi; diff --git a/include/hw/ssi/pnv_spi.h b/include/hw/ssi/pnv_spi.h new file mode 100644 index 00000000000..c591a0663da --- /dev/null +++ b/include/hw/ssi/pnv_spi.h @@ -0,0 +1,72 @@ +/* + * 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" +#include "qemu/fifo8.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 "spi" +typedef struct PnvSpi { + SysBusDevice parent_obj; + + SSIBus *ssi_bus; + qemu_irq *cs_line; + MemoryRegion xscom_spic_regs; + Fifo8 tx_fifo; + Fifo8 rx_fifo; + uint8_t fail_count; /* RDR Match failure counter */ + /* SPI object number */ + uint32_t spic_num; + uint32_t chip_id; + 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 00000000000..596e2c1911d --- /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/xilinx_spips.h b/include/hw/ssi/xilinx_spips.h index 1386d5ac8fe..7a754bf67a2 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 */ @@ -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 5d131d351d2..4ac975aa2fd 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 3564b7b6a22..7dc88aaa270 100644 --- a/include/hw/sysbus.h +++ b/include/hw/sysbus.h @@ -4,7 +4,7 @@ /* Devices attached directly to the main system bus. */ #include "hw/qdev-core.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qom/object.h" #define QDEV_MAX_MMIO 32 @@ -19,6 +19,8 @@ DECLARE_INSTANCE_CHECKER(BusState, SYSTEM_BUS, OBJECT_DECLARE_TYPE(SysBusDevice, SysBusDeviceClass, SYS_BUS_DEVICE) +#define TYPE_DYNAMIC_SYS_BUS_DEVICE "dynamic-sysbus-device" + /** * SysBusDeviceClass: * @@ -82,10 +84,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/aspeed_timer.h b/include/hw/timer/aspeed_timer.h index 07dc6b6f2cb..767cae4b05b 100644 --- a/include/hw/timer/aspeed_timer.h +++ b/include/hw/timer/aspeed_timer.h @@ -32,6 +32,7 @@ OBJECT_DECLARE_TYPE(AspeedTimerCtrlState, AspeedTimerClass, ASPEED_TIMER) #define TYPE_ASPEED_2500_TIMER TYPE_ASPEED_TIMER "-ast2500" #define TYPE_ASPEED_2600_TIMER TYPE_ASPEED_TIMER "-ast2600" #define TYPE_ASPEED_1030_TIMER TYPE_ASPEED_TIMER "-ast1030" +#define TYPE_ASPEED_2700_TIMER TYPE_ASPEED_TIMER "-ast2700" #define ASPEED_TIMER_NR_TIMERS 8 diff --git a/include/hw/timer/grlib_gptimer.h b/include/hw/timer/grlib_gptimer.h new file mode 100644 index 00000000000..e56f1b8bf34 --- /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 f04c4d32387..c2656f7f0be 100644 --- a/include/hw/timer/hpet.h +++ b/include/hw/timer/hpet.h @@ -58,7 +58,6 @@ #define HPET_TN_CFG_WRITE_MASK 0x7f4e #define HPET_TN_INT_ROUTE_SHIFT 9 #define HPET_TN_INT_ROUTE_CAP_SHIFT 32 -#define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U struct hpet_fw_entry { @@ -74,10 +73,12 @@ struct hpet_fw_config struct hpet_fw_entry hpet[8]; } QEMU_PACKED; -extern struct hpet_fw_config hpet_cfg; +extern struct hpet_fw_config hpet_fw_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/imx_gpt.h b/include/hw/timer/imx_gpt.h index 5a1230da35e..5488f7e4df5 100644 --- a/include/hw/timer/imx_gpt.h +++ b/include/hw/timer/imx_gpt.h @@ -80,6 +80,7 @@ #define TYPE_IMX6_GPT "imx6.gpt" #define TYPE_IMX6UL_GPT "imx6ul.gpt" #define TYPE_IMX7_GPT "imx7.gpt" +#define TYPE_IMX8MP_GPT "imx8mp.gpt" #define TYPE_IMX_GPT TYPE_IMX25_GPT diff --git a/include/hw/timer/npcm7xx_timer.h b/include/hw/timer/npcm7xx_timer.h index d45c051b56a..e287375dce6 100644 --- a/include/hw/timer/npcm7xx_timer.h +++ b/include/hw/timer/npcm7xx_timer.h @@ -16,7 +16,7 @@ #ifndef NPCM7XX_TIMER_H #define NPCM7XX_TIMER_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.h" #include "qemu/timer.h" diff --git a/include/hw/tricore/triboard.h b/include/hw/tricore/triboard.h index 4fdd2d7d97b..ca49a0c7520 100644 --- a/include/hw/tricore/triboard.h +++ b/include/hw/tricore/triboard.h @@ -20,8 +20,8 @@ #include "qapi/error.h" #include "hw/boards.h" -#include "sysemu/sysemu.h" -#include "exec/address-spaces.h" +#include "system/system.h" +#include "system/address-spaces.h" #include "qom/object.h" #include "hw/tricore/tc27x_soc.h" diff --git a/include/hw/tricore/tricore.h b/include/hw/tricore/tricore.h index c19ed3f013e..4ffc0fe1d65 100644 --- a/include/hw/tricore/tricore.h +++ b/include/hw/tricore/tricore.h @@ -1,7 +1,7 @@ #ifndef HW_TRICORE_H #define HW_TRICORE_H -#include "exec/memory.h" +#include "system/memory.h" struct tricore_boot_info { uint64_t ram_size; diff --git a/include/hw/tricore/tricore_testdevice.h b/include/hw/tricore/tricore_testdevice.h index 8b4fe15f24a..2c57b62f222 100644 --- a/include/hw/tricore/tricore_testdevice.h +++ b/include/hw/tricore/tricore_testdevice.h @@ -25,12 +25,9 @@ OBJECT_CHECK(TriCoreTestDeviceState, (obj), TYPE_TRICORE_TESTDEVICE) typedef struct { - /* */ SysBusDevice parent_obj; - /* */ MemoryRegion iomem; - } TriCoreTestDeviceState; #endif diff --git a/include/hw/uefi/hardware-info.h b/include/hw/uefi/hardware-info.h new file mode 100644 index 00000000000..94c38cff200 --- /dev/null +++ b/include/hw/uefi/hardware-info.h @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * pass hardware information to uefi + * + * see OvmfPkg/Library/HardwareInfoLib/ in edk2 + */ +#ifndef QEMU_UEFI_HARDWARE_INFO_H +#define QEMU_UEFI_HARDWARE_INFO_H + +/* data structures */ + +typedef enum { + HardwareInfoTypeUndefined = 0, + HardwareInfoTypeHostBridge = 1, + HardwareInfoQemuUefiVars = 2, +} HARDWARE_INFO_TYPE; + +typedef struct { + union { + uint64_t uint64; + HARDWARE_INFO_TYPE value; + } type; + uint64_t size; +} HARDWARE_INFO_HEADER; + +typedef struct { + uint64_t mmio_address; +} HARDWARE_INFO_SIMPLE_DEVICE; + +/* qemu functions */ + +void hardware_info_register(HARDWARE_INFO_TYPE type, void *info, uint64_t size); + +#endif /* QEMU_UEFI_HARDWARE_INFO_H */ diff --git a/include/hw/uefi/var-service-api.h b/include/hw/uefi/var-service-api.h new file mode 100644 index 00000000000..0d71638f3ef --- /dev/null +++ b/include/hw/uefi/var-service-api.h @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi-vars device - API of the virtual device for guest/host communication. + */ +#ifndef QEMU_UEFI_VAR_SERVICE_API_H +#define QEMU_UEFI_VAR_SERVICE_API_H + +/* qom: device names */ +#define TYPE_UEFI_VARS_X64 "uefi-vars-x64" +#define TYPE_UEFI_VARS_SYSBUS "uefi-vars-sysbus" + +/* sysbus: fdt node path */ +#define UEFI_VARS_FDT_NODE "qemu-uefi-vars" +#define UEFI_VARS_FDT_COMPAT "qemu,uefi-vars" + +/* registers */ +#define UEFI_VARS_REG_MAGIC 0x00 /* 16 bit */ +#define UEFI_VARS_REG_CMD_STS 0x02 /* 16 bit */ +#define UEFI_VARS_REG_BUFFER_SIZE 0x04 /* 32 bit */ +#define UEFI_VARS_REG_DMA_BUFFER_ADDR_LO 0x08 /* 32 bit */ +#define UEFI_VARS_REG_DMA_BUFFER_ADDR_HI 0x0c /* 32 bit */ +#define UEFI_VARS_REG_PIO_BUFFER_TRANSFER 0x10 /* 8-64 bit */ +#define UEFI_VARS_REG_PIO_BUFFER_CRC32C 0x18 /* 32 bit (read-only) */ +#define UEFI_VARS_REG_FLAGS 0x1c /* 32 bit */ +#define UEFI_VARS_REGS_SIZE 0x20 + +/* flags register */ +#define UEFI_VARS_FLAG_USE_PIO (1 << 0) + +/* magic value */ +#define UEFI_VARS_MAGIC_VALUE 0xef1 + +/* command values */ +#define UEFI_VARS_CMD_RESET 0x01 +#define UEFI_VARS_CMD_DMA_MM 0x02 +#define UEFI_VARS_CMD_PIO_MM 0x03 +#define UEFI_VARS_CMD_PIO_ZERO_OFFSET 0x04 + +/* status values */ +#define UEFI_VARS_STS_SUCCESS 0x00 +#define UEFI_VARS_STS_BUSY 0x01 +#define UEFI_VARS_STS_ERR_UNKNOWN 0x10 +#define UEFI_VARS_STS_ERR_NOT_SUPPORTED 0x11 +#define UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE 0x12 + + +#endif /* QEMU_UEFI_VAR_SERVICE_API_H */ diff --git a/include/hw/uefi/var-service-edk2.h b/include/hw/uefi/var-service-edk2.h new file mode 100644 index 00000000000..c743a8df948 --- /dev/null +++ b/include/hw/uefi/var-service-edk2.h @@ -0,0 +1,227 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi-vars device - structs and defines from edk2 + * + * Note: The edk2 UINTN type has been mapped to uint64_t, + * so the structs are compatible with 64bit edk2 builds. + */ +#ifndef QEMU_UEFI_VAR_SERVICE_EDK2_H +#define QEMU_UEFI_VAR_SERVICE_EDK2_H + +#include "qemu/uuid.h" + +#define MAX_BIT 0x8000000000000000ULL +#define ENCODE_ERROR(StatusCode) (MAX_BIT | (StatusCode)) +#define EFI_SUCCESS 0 +#define EFI_INVALID_PARAMETER ENCODE_ERROR(2) +#define EFI_UNSUPPORTED ENCODE_ERROR(3) +#define EFI_BAD_BUFFER_SIZE ENCODE_ERROR(4) +#define EFI_BUFFER_TOO_SMALL ENCODE_ERROR(5) +#define EFI_WRITE_PROTECTED ENCODE_ERROR(8) +#define EFI_OUT_OF_RESOURCES ENCODE_ERROR(9) +#define EFI_NOT_FOUND ENCODE_ERROR(14) +#define EFI_ACCESS_DENIED ENCODE_ERROR(15) +#define EFI_ALREADY_STARTED ENCODE_ERROR(20) +#define EFI_SECURITY_VIOLATION ENCODE_ERROR(26) + +#define EFI_VARIABLE_NON_VOLATILE 0x01 +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x02 +#define EFI_VARIABLE_RUNTIME_ACCESS 0x04 +#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x08 +#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x10 /* deprecated */ +#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x20 +#define EFI_VARIABLE_APPEND_WRITE 0x40 + +/* SecureBootEnable */ +#define SECURE_BOOT_ENABLE 1 +#define SECURE_BOOT_DISABLE 0 + +/* SecureBoot */ +#define SECURE_BOOT_MODE_ENABLE 1 +#define SECURE_BOOT_MODE_DISABLE 0 + +/* CustomMode */ +#define CUSTOM_SECURE_BOOT_MODE 1 +#define STANDARD_SECURE_BOOT_MODE 0 + +/* SetupMode */ +#define SETUP_MODE 1 +#define USER_MODE 0 + +typedef uint64_t efi_status; +typedef struct mm_header mm_header; + +/* EFI_MM_COMMUNICATE_HEADER */ +struct mm_header { + QemuUUID guid; + uint64_t length; +}; + +/* --- EfiSmmVariableProtocol ---------------------------------------- */ + +#define SMM_VARIABLE_FUNCTION_GET_VARIABLE 1 +#define SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME 2 +#define SMM_VARIABLE_FUNCTION_SET_VARIABLE 3 +#define SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO 4 +#define SMM_VARIABLE_FUNCTION_READY_TO_BOOT 5 +#define SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE 6 +#define SMM_VARIABLE_FUNCTION_LOCK_VARIABLE 8 +#define SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE 11 + +typedef struct mm_variable mm_variable; +typedef struct mm_variable_access mm_variable_access; +typedef struct mm_next_variable mm_next_variable; +typedef struct mm_next_variable mm_lock_variable; +typedef struct mm_variable_info mm_variable_info; +typedef struct mm_get_payload_size mm_get_payload_size; + +/* SMM_VARIABLE_COMMUNICATE_HEADER */ +struct mm_variable { + uint64_t function; + uint64_t status; +}; + +/* SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE */ +struct QEMU_PACKED mm_variable_access { + QemuUUID guid; + uint64_t data_size; + uint64_t name_size; + uint32_t attributes; + /* Name */ + /* Data */ +}; + +/* SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME */ +struct mm_next_variable { + QemuUUID guid; + uint64_t name_size; + /* Name */ +}; + +/* SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO */ +struct QEMU_PACKED mm_variable_info { + uint64_t max_storage_size; + uint64_t free_storage_size; + uint64_t max_variable_size; + uint32_t attributes; +}; + +/* SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE */ +struct mm_get_payload_size { + uint64_t payload_size; +}; + +/* --- VarCheckPolicyLibMmiHandler ----------------------------------- */ + +#define VAR_CHECK_POLICY_COMMAND_DISABLE 0x01 +#define VAR_CHECK_POLICY_COMMAND_IS_ENABLED 0x02 +#define VAR_CHECK_POLICY_COMMAND_REGISTER 0x03 +#define VAR_CHECK_POLICY_COMMAND_DUMP 0x04 +#define VAR_CHECK_POLICY_COMMAND_LOCK 0x05 + +typedef struct mm_check_policy mm_check_policy; +typedef struct mm_check_policy_is_enabled mm_check_policy_is_enabled; +typedef struct mm_check_policy_dump_params mm_check_policy_dump_params; + +/* VAR_CHECK_POLICY_COMM_HEADER */ +struct QEMU_PACKED mm_check_policy { + uint32_t signature; + uint32_t revision; + uint32_t command; + uint64_t result; +}; + +/* VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS */ +struct QEMU_PACKED mm_check_policy_is_enabled { + uint8_t state; +}; + +/* VAR_CHECK_POLICY_COMM_DUMP_PARAMS */ +struct QEMU_PACKED mm_check_policy_dump_params { + uint32_t page_requested; + uint32_t total_size; + uint32_t page_size; + uint8_t has_more; +}; + +/* --- Edk2VariablePolicyProtocol ------------------------------------ */ + +#define VARIABLE_POLICY_ENTRY_REVISION 0x00010000 + +#define VARIABLE_POLICY_TYPE_NO_LOCK 0 +#define VARIABLE_POLICY_TYPE_LOCK_NOW 1 +#define VARIABLE_POLICY_TYPE_LOCK_ON_CREATE 2 +#define VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE 3 + +typedef struct variable_policy_entry variable_policy_entry; +typedef struct variable_lock_on_var_state variable_lock_on_var_state; + +/* VARIABLE_POLICY_ENTRY */ +struct variable_policy_entry { + uint32_t version; + uint16_t size; + uint16_t offset_to_name; + QemuUUID namespace; + uint32_t min_size; + uint32_t max_size; + uint32_t attributes_must_have; + uint32_t attributes_cant_have; + uint8_t lock_policy_type; + uint8_t padding[3]; + /* LockPolicy */ + /* Name */ +}; + +/* VARIABLE_LOCK_ON_VAR_STATE_POLICY */ +struct variable_lock_on_var_state { + QemuUUID namespace; + uint8_t value; + uint8_t padding; + /* Name */ +}; + +/* --- variable authentication --------------------------------------- */ + +#define WIN_CERT_TYPE_EFI_GUID 0x0EF1 + +typedef struct efi_time efi_time; +typedef struct efi_siglist efi_siglist; +typedef struct variable_auth_2 variable_auth_2; + +/* EFI_TIME */ +struct efi_time { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t pad1; + uint32_t nanosecond; + int16_t timezone; + uint8_t daylight; + uint8_t pad2; +}; + +/* EFI_SIGNATURE_LIST */ +struct efi_siglist { + QemuUUID guid_type; + uint32_t siglist_size; + uint32_t header_size; + uint32_t sig_size; +}; + +/* EFI_VARIABLE_AUTHENTICATION_2 */ +struct variable_auth_2 { + struct efi_time timestamp; + + /* WIN_CERTIFICATE_UEFI_GUID */ + uint32_t hdr_length; + uint16_t hdr_revision; + uint16_t hdr_cert_type; + QemuUUID guid_cert_type; + uint8_t cert_data[]; +}; + +#endif /* QEMU_UEFI_VAR_SERVICE_EDK2_H */ diff --git a/include/hw/uefi/var-service.h b/include/hw/uefi/var-service.h new file mode 100644 index 00000000000..f7ceac4ce24 --- /dev/null +++ b/include/hw/uefi/var-service.h @@ -0,0 +1,191 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi-vars device - state struct and function prototypes + */ +#ifndef QEMU_UEFI_VAR_SERVICE_H +#define QEMU_UEFI_VAR_SERVICE_H + +#include "qemu/uuid.h" +#include "qemu/queue.h" + +#include "hw/uefi/var-service-edk2.h" + +#define MAX_BUFFER_SIZE (64 * 1024) + +typedef struct uefi_variable uefi_variable; +typedef struct uefi_var_policy uefi_var_policy; +typedef struct uefi_vars_state uefi_vars_state; + +typedef struct uefi_vars_cert uefi_vars_cert; +typedef struct uefi_vars_hash uefi_vars_hash; +typedef struct uefi_vars_siglist uefi_vars_siglist; + +struct uefi_variable { + QemuUUID guid; + uint16_t *name; + uint32_t name_size; + uint32_t attributes; + void *data; + uint32_t data_size; + efi_time time; + void *digest; + uint32_t digest_size; + QTAILQ_ENTRY(uefi_variable) next; +}; + +struct uefi_var_policy { + variable_policy_entry *entry; + uint32_t entry_size; + uint16_t *name; + uint32_t name_size; + + /* number of hashmarks (wildcard character) in name */ + uint32_t hashmarks; + + QTAILQ_ENTRY(uefi_var_policy) next; +}; + +struct uefi_vars_state { + MemoryRegion mr; + uint16_t sts; + uint32_t buf_size; + uint32_t buf_addr_lo; + uint32_t buf_addr_hi; + uint8_t *buffer; + QTAILQ_HEAD(, uefi_variable) variables; + QTAILQ_HEAD(, uefi_var_policy) var_policies; + + /* pio transfer buffer */ + uint32_t pio_xfer_offset; + uint8_t *pio_xfer_buffer; + + /* boot phases */ + bool end_of_dxe; + bool ready_to_boot; + bool exit_boot_service; + bool policy_locked; + + /* storage accounting */ + uint64_t max_storage; + uint64_t used_storage; + + /* config options */ + char *jsonfile; + int jsonfd; + bool force_secure_boot; + bool disable_custom_mode; + bool use_pio; +}; + +struct uefi_vars_cert { + QTAILQ_ENTRY(uefi_vars_cert) next; + QemuUUID owner; + uint64_t size; + uint8_t data[]; +}; + +struct uefi_vars_hash { + QTAILQ_ENTRY(uefi_vars_hash) next; + QemuUUID owner; + uint8_t data[]; +}; + +struct uefi_vars_siglist { + QTAILQ_HEAD(, uefi_vars_cert) x509; + QTAILQ_HEAD(, uefi_vars_hash) sha256; +}; + +/* vars-service-guid.c */ +extern const QemuUUID EfiGlobalVariable; +extern const QemuUUID EfiImageSecurityDatabase; +extern const QemuUUID EfiCustomModeEnable; +extern const QemuUUID EfiSecureBootEnableDisable; + +extern const QemuUUID EfiCertSha256Guid; +extern const QemuUUID EfiCertSha384Guid; +extern const QemuUUID EfiCertSha512Guid; +extern const QemuUUID EfiCertRsa2048Guid; +extern const QemuUUID EfiCertX509Guid; +extern const QemuUUID EfiCertTypePkcs7Guid; + +extern const QemuUUID EfiSmmVariableProtocolGuid; +extern const QemuUUID VarCheckPolicyLibMmiHandlerGuid; + +extern const QemuUUID EfiEndOfDxeEventGroupGuid; +extern const QemuUUID EfiEventReadyToBootGuid; +extern const QemuUUID EfiEventExitBootServicesGuid; + +/* vars-service-utils.c */ +gboolean uefi_str_is_valid(const uint16_t *str, size_t len, + gboolean must_be_null_terminated); +size_t uefi_strlen(const uint16_t *str, size_t len); +gboolean uefi_str_equal_ex(const uint16_t *a, size_t alen, + const uint16_t *b, size_t blen, + gboolean wildcards_in_a); +gboolean uefi_str_equal(const uint16_t *a, size_t alen, + const uint16_t *b, size_t blen); +char *uefi_ucs2_to_ascii(const uint16_t *ucs2, uint64_t ucs2_size); +int uefi_time_compare(efi_time *a, efi_time *b); +void uefi_trace_variable(const char *action, QemuUUID guid, + const uint16_t *name, uint64_t name_size); +void uefi_trace_status(const char *action, efi_status status); + +/* vars-service-core.c */ +extern const VMStateDescription vmstate_uefi_vars; +void uefi_vars_init(Object *obj, uefi_vars_state *uv); +void uefi_vars_realize(uefi_vars_state *uv, Error **errp); +void uefi_vars_hard_reset(uefi_vars_state *uv); + +/* vars-service-json.c */ +void uefi_vars_json_init(uefi_vars_state *uv, Error **errp); +void uefi_vars_json_save(uefi_vars_state *uv); +void uefi_vars_json_load(uefi_vars_state *uv, Error **errp); + +/* vars-service-vars.c */ +extern const VMStateDescription vmstate_uefi_variable; +uefi_variable *uefi_vars_find_variable(uefi_vars_state *uv, QemuUUID guid, + const uint16_t *name, + uint64_t name_size); +void uefi_vars_set_variable(uefi_vars_state *uv, QemuUUID guid, + const uint16_t *name, uint64_t name_size, + uint32_t attributes, + void *data, uint64_t data_size); +void uefi_vars_clear_volatile(uefi_vars_state *uv); +void uefi_vars_clear_all(uefi_vars_state *uv); +void uefi_vars_update_storage(uefi_vars_state *uv); +uint32_t uefi_vars_mm_vars_proto(uefi_vars_state *uv); + +/* vars-service-auth.c */ +bool uefi_vars_is_sb_pk(uefi_variable *var); +bool uefi_vars_is_sb_any(uefi_variable *var); +efi_status uefi_vars_check_auth_2(uefi_vars_state *uv, uefi_variable *var, + mm_variable_access *va, void *data); +efi_status uefi_vars_check_secure_boot(uefi_vars_state *uv, uefi_variable *var); +void uefi_vars_auth_init(uefi_vars_state *uv); + +/* vars-service-pkcs7.c */ +efi_status uefi_vars_check_pkcs7_2(uefi_variable *siglist, + void **digest, uint32_t *digest_size, + mm_variable_access *va, void *data); + +/* vars-service-siglist.c */ +void uefi_vars_siglist_init(uefi_vars_siglist *siglist); +void uefi_vars_siglist_free(uefi_vars_siglist *siglist); +void uefi_vars_siglist_parse(uefi_vars_siglist *siglist, + void *data, uint64_t size); +uint64_t uefi_vars_siglist_blob_size(uefi_vars_siglist *siglist); +void uefi_vars_siglist_blob_generate(uefi_vars_siglist *siglist, + void *data, uint64_t size); + +/* vars-service-policy.c */ +extern const VMStateDescription vmstate_uefi_var_policy; +efi_status uefi_vars_policy_check(uefi_vars_state *uv, + uefi_variable *var, + gboolean is_newvar); +void uefi_vars_policies_clear(uefi_vars_state *uv); +uefi_var_policy *uefi_vars_add_policy(uefi_vars_state *uv, + variable_policy_entry *pe); +uint32_t uefi_vars_mm_check_policy_proto(uefi_vars_state *uv); + +#endif /* QEMU_UEFI_VAR_SERVICE_H */ diff --git a/include/hw/usb.h b/include/hw/usb.h index 32c23a5ca2a..26a9f3ecdee 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -25,11 +25,12 @@ * THE SOFTWARE. */ -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #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,17 @@ void usb_pcap_init(FILE *fp); void usb_pcap_ctrl(USBPacket *p, bool setup); void usb_pcap_data(USBPacket *p, bool setup); +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_DEVICE(qdev_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 0bf3f2aa17f..b8b42665431 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 f752a27e940..dbdf12b21d7 100644 --- a/include/hw/usb/hcd-dwc3.h +++ b/include/hw/usb/hcd-dwc3.h @@ -35,7 +35,7 @@ #define USB_DWC3(obj) \ OBJECT_CHECK(USBDWC3, (obj), TYPE_USB_DWC3) -#define USB_DWC3_R_MAX ((0x530 / 4) + 1) +#define USB_DWC3_R_MAX (0x600 / 4) #define DWC3_SIZE 0x10000 typedef struct USBDWC3 { diff --git a/include/hw/usb/hcd-musb.h b/include/hw/usb/hcd-musb.h deleted file mode 100644 index 4d4b1ec0fc2..00000000000 --- a/include/hw/usb/hcd-musb.h +++ /dev/null @@ -1,49 +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 - -#include "exec/hwaddr.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/uhci-regs.h b/include/hw/usb/uhci-regs.h index fd45d29db0f..5b81714e5c2 100644 --- a/include/hw/usb/uhci-regs.h +++ b/include/hw/usb/uhci-regs.h @@ -1,6 +1,17 @@ #ifndef HW_USB_UHCI_REGS_H #define HW_USB_UHCI_REGS_H +#define UHCI_USBCMD 0 +#define UHCI_USBSTS 2 +#define UHCI_USBINTR 4 +#define UHCI_USBFRNUM 6 +#define UHCI_USBFLBASEADD 8 +#define UHCI_USBSOF 0x0c +#define UHCI_USBPORTSC1 0x10 +#define UHCI_USBPORTSC2 0x12 +#define UHCI_USBPORTSC3 0x14 +#define UHCI_USBPORTSC4 0x16 + #define UHCI_CMD_FGR (1 << 4) #define UHCI_CMD_EGSM (1 << 3) #define UHCI_CMD_GRESET (1 << 2) diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h deleted file mode 100644 index e9b89545953..00000000000 --- a/include/hw/vfio/vfio-common.h +++ /dev/null @@ -1,260 +0,0 @@ -/* - * common header for vfio based device assignment support - * - * 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) - */ - -#ifndef HW_VFIO_VFIO_COMMON_H -#define HW_VFIO_VFIO_COMMON_H - -#include "exec/memory.h" -#include "qemu/queue.h" -#include "qemu/notify.h" -#include "ui/console.h" -#include "hw/display/ramfb.h" -#ifdef CONFIG_LINUX -#include -#endif -#include "sysemu/sysemu.h" - -#define VFIO_MSG_PREFIX "vfio %s: " - -enum { - VFIO_DEVICE_TYPE_PCI = 0, - VFIO_DEVICE_TYPE_PLATFORM = 1, - VFIO_DEVICE_TYPE_CCW = 2, - VFIO_DEVICE_TYPE_AP = 3, -}; - -typedef struct VFIOMmap { - MemoryRegion mem; - void *mmap; - off_t offset; - size_t size; -} VFIOMmap; - -typedef struct VFIORegion { - struct VFIODevice *vbasedev; - off_t fd_offset; /* offset of region within device fd */ - MemoryRegion *mem; /* slow, read/write access */ - size_t size; - uint32_t flags; /* VFIO region flags (rd/wr/mmap) */ - uint32_t nr_mmaps; - VFIOMmap *mmaps; - uint8_t nr; /* cache the region number for debug */ -} VFIORegion; - -typedef struct VFIOMigration { - struct VFIODevice *vbasedev; - VMChangeStateEntry *vm_state; - Notifier migration_state; - uint32_t device_state; - int data_fd; - void *data_buffer; - size_t data_buffer_size; - uint64_t mig_flags; - uint64_t precopy_init_size; - uint64_t precopy_dirty_size; - bool initial_data_sent; -} VFIOMigration; - -typedef struct VFIOAddressSpace { - AddressSpace *as; - QLIST_HEAD(, VFIOContainer) containers; - QLIST_ENTRY(VFIOAddressSpace) list; -} VFIOAddressSpace; - -struct VFIOGroup; - -typedef struct VFIOContainer { - VFIOAddressSpace *space; - int fd; /* /dev/vfio/vfio, empowered by the attached groups */ - MemoryListener listener; - MemoryListener prereg_listener; - unsigned iommu_type; - Error *error; - bool initialized; - bool dirty_pages_supported; - uint64_t dirty_pgsizes; - uint64_t max_dirty_bitmap_size; - unsigned long pgsizes; - unsigned int dma_max_mappings; - QLIST_HEAD(, VFIOGuestIOMMU) giommu_list; - QLIST_HEAD(, VFIOHostDMAWindow) hostwin_list; - QLIST_HEAD(, VFIOGroup) group_list; - QLIST_HEAD(, VFIORamDiscardListener) vrdl_list; - QLIST_ENTRY(VFIOContainer) next; -} VFIOContainer; - -typedef struct VFIOGuestIOMMU { - VFIOContainer *container; - IOMMUMemoryRegion *iommu_mr; - hwaddr iommu_offset; - IOMMUNotifier n; - QLIST_ENTRY(VFIOGuestIOMMU) giommu_next; -} VFIOGuestIOMMU; - -typedef struct VFIORamDiscardListener { - VFIOContainer *container; - MemoryRegion *mr; - hwaddr offset_within_address_space; - hwaddr size; - uint64_t granularity; - RamDiscardListener listener; - QLIST_ENTRY(VFIORamDiscardListener) next; -} VFIORamDiscardListener; - -typedef struct VFIOHostDMAWindow { - hwaddr min_iova; - hwaddr max_iova; - uint64_t iova_pgsizes; - QLIST_ENTRY(VFIOHostDMAWindow) hostwin_next; -} VFIOHostDMAWindow; - -typedef struct VFIODeviceOps VFIODeviceOps; - -typedef struct VFIODevice { - QLIST_ENTRY(VFIODevice) next; - struct VFIOGroup *group; - char *sysfsdev; - char *name; - DeviceState *dev; - int fd; - int type; - bool reset_works; - bool needs_reset; - bool no_mmap; - bool ram_block_discard_allowed; - OnOffAuto enable_migration; - VFIODeviceOps *ops; - unsigned int num_irqs; - unsigned int num_regions; - unsigned int flags; - VFIOMigration *migration; - Error *migration_blocker; - OnOffAuto pre_copy_dirty_page_tracking; - bool dirty_pages_supported; - bool dirty_tracking; -} VFIODevice; - -struct VFIODeviceOps { - void (*vfio_compute_needs_reset)(VFIODevice *vdev); - 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); - int (*vfio_load_config)(VFIODevice *vdev, QEMUFile *f); -}; - -typedef struct VFIOGroup { - int fd; - int groupid; - VFIOContainer *container; - QLIST_HEAD(, VFIODevice) device_list; - QLIST_ENTRY(VFIOGroup) next; - QLIST_ENTRY(VFIOGroup) container_next; - bool ram_block_discard_allowed; -} VFIOGroup; - -typedef struct VFIODMABuf { - QemuDmaBuf buf; - uint32_t pos_x, pos_y, pos_updates; - uint32_t hot_x, hot_y, hot_updates; - int dmabuf_id; - QTAILQ_ENTRY(VFIODMABuf) next; -} VFIODMABuf; - -typedef struct VFIODisplay { - QemuConsole *con; - RAMFBState *ramfb; - struct vfio_region_info *edid_info; - struct vfio_region_gfx_edid *edid_regs; - uint8_t *edid_blob; - QEMUTimer *edid_link_timer; - struct { - VFIORegion buffer; - DisplaySurface *surface; - } region; - struct { - QTAILQ_HEAD(, VFIODMABuf) bufs; - VFIODMABuf *primary; - VFIODMABuf *cursor; - } dmabuf; -} VFIODisplay; - -void vfio_put_base_device(VFIODevice *vbasedev); -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); -void vfio_region_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size); -uint64_t vfio_region_read(void *opaque, - hwaddr addr, unsigned size); -int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, - int index, const char *name); -int vfio_region_mmap(VFIORegion *region); -void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled); -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); -struct vfio_device_info *vfio_get_device_info(int fd); -int vfio_get_device(VFIOGroup *group, const char *name, - VFIODevice *vbasedev, Error **errp); - -extern const MemoryRegionOps vfio_region_ops; -typedef QLIST_HEAD(VFIOGroupList, VFIOGroup) VFIOGroupList; -extern VFIOGroupList vfio_group_list; - -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, - struct vfio_region_info **info); -int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, - uint32_t subtype, struct vfio_region_info **info); -bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type); -struct vfio_info_cap_header * -vfio_get_region_info_cap(struct vfio_region_info *info, uint16_t id); -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); -#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); - -#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 00000000000..3cd86ec59e1 --- /dev/null +++ b/include/hw/vfio/vfio-container-base.h @@ -0,0 +1,263 @@ +/* + * 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 "system/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; + bool dirty_pages_started; /* Protected by BQL */ + 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; + +VFIOAddressSpace *vfio_address_space_get(AddressSpace *as); +void vfio_address_space_put(VFIOAddressSpace *space); +void vfio_address_space_insert(VFIOAddressSpace *space, + VFIOContainerBase *bcontainer); + +int vfio_container_dma_map(VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + void *vaddr, bool readonly, MemoryRegion *mr); +int vfio_container_dma_unmap(VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + IOMMUTLBEntry *iotlb, bool unmap_all); +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); +bool vfio_container_dirty_tracking_is_started( + const VFIOContainerBase *bcontainer); +bool vfio_container_devices_dirty_tracking_is_supported( + const VFIOContainerBase *bcontainer); +int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + uint64_t iova, uint64_t size, ram_addr_t ram_addr, 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" +#define TYPE_VFIO_IOMMU_USER TYPE_VFIO_IOMMU "-user" + +OBJECT_DECLARE_TYPE(VFIOContainerBase, VFIOIOMMUClass, VFIO_IOMMU) + +struct VFIOIOMMUClass { + ObjectClass parent_class; + + /** + * @setup + * + * Perform basic setup of the container, including configuring IOMMU + * capabilities, IOVA ranges, supported page sizes, etc. + * + * @bcontainer: #VFIOContainerBase + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns true to indicate success and false for error. + */ + bool (*setup)(VFIOContainerBase *bcontainer, Error **errp); + + /** + * @listener_begin + * + * Called at the beginning of an address space update transaction. + * See #MemoryListener. + * + * @bcontainer: #VFIOContainerBase + */ + void (*listener_begin)(VFIOContainerBase *bcontainer); + + /** + * @listener_commit + * + * Called at the end of an address space update transaction, + * See #MemoryListener. + * + * @bcontainer: #VFIOContainerBase + */ + void (*listener_commit)(VFIOContainerBase *bcontainer); + + /** + * @dma_map + * + * Map an address range into the container. Note that the memory region is + * referenced within an RCU read lock region across this call. + * + * @bcontainer: #VFIOContainerBase to use + * @iova: start address to map + * @size: size of the range to map + * @vaddr: process virtual address of mapping + * @readonly: true if mapping should be readonly + * @mr: the memory region for this mapping + * + * Returns 0 to indicate success and -errno otherwise. + */ + int (*dma_map)(const VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + void *vaddr, bool readonly, MemoryRegion *mr); + /** + * @dma_unmap + * + * Unmap an address range from the container. + * + * @bcontainer: #VFIOContainerBase to use for unmap + * @iova: start address to unmap + * @size: size of the range to unmap + * @iotlb: The IOMMU TLB mapping entry (or NULL) + * @unmap_all: if set, unmap the entire address space + * + * Returns 0 to indicate success and -errno otherwise. + */ + int (*dma_unmap)(const VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + IOMMUTLBEntry *iotlb, bool unmap_all); + + + /** + * @attach_device + * + * Associate the given device with a container and do some related + * initialization of the device context. + * + * @name: name of the device + * @vbasedev: the device + * @as: address space to use + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns true to indicate success and false for error. + */ + bool (*attach_device)(const char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp); + + /* + * @detach_device + * + * Detach the given device from its container and clean up any necessary + * state. + * + * @vbasedev: the device to disassociate + */ + 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); +}; + +VFIORamDiscardListener *vfio_find_ram_discard_listener( + VFIOContainerBase *bcontainer, MemoryRegionSection *section); + +void vfio_container_region_add(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, bool cpr_remap); + +#endif /* HW_VFIO_VFIO_CONTAINER_BASE_H */ diff --git a/include/hw/vfio/vfio-container.h b/include/hw/vfio/vfio-container.h new file mode 100644 index 00000000000..21e5807e48e --- /dev/null +++ b/include/hw/vfio/vfio-container.h @@ -0,0 +1,38 @@ +/* + * VFIO container + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_CONTAINER_H +#define HW_VFIO_CONTAINER_H + +#include "hw/vfio/vfio-container-base.h" +#include "hw/vfio/vfio-cpr.h" + +typedef struct VFIOContainer VFIOContainer; +typedef struct VFIODevice VFIODevice; + +typedef struct VFIOGroup { + int fd; + int groupid; + VFIOContainer *container; + QLIST_HEAD(, VFIODevice) device_list; + QLIST_ENTRY(VFIOGroup) next; + QLIST_ENTRY(VFIOGroup) container_next; + bool ram_block_discard_allowed; +} VFIOGroup; + +typedef struct VFIOContainer { + VFIOContainerBase bcontainer; + int fd; /* /dev/vfio/vfio, empowered by the attached groups */ + unsigned iommu_type; + QLIST_HEAD(, VFIOGroup) group_list; + VFIOContainerCPR cpr; +} VFIOContainer; + +OBJECT_DECLARE_SIMPLE_TYPE(VFIOContainer, VFIO_IOMMU_LEGACY); + +#endif /* HW_VFIO_CONTAINER_H */ diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h new file mode 100644 index 00000000000..8bf85b9f4ec --- /dev/null +++ b/include/hw/vfio/vfio-cpr.h @@ -0,0 +1,57 @@ +/* + * VFIO CPR + * + * Copyright (c) 2025 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_VFIO_CPR_H +#define HW_VFIO_VFIO_CPR_H + +#include "migration/misc.h" +#include "system/memory.h" + +struct VFIOContainer; +struct VFIOContainerBase; +struct VFIOGroup; + +typedef struct VFIOContainerCPR { + Error *blocker; + bool vaddr_unmapped; + NotifierWithReturn transfer_notifier; + MemoryListener remap_listener; + int (*saved_dma_map)(const struct VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + void *vaddr, bool readonly, MemoryRegion *mr); +} VFIOContainerCPR; + +typedef struct VFIODeviceCPR { + Error *mdev_blocker; +} VFIODeviceCPR; + +bool vfio_legacy_cpr_register_container(struct VFIOContainer *container, + Error **errp); +void vfio_legacy_cpr_unregister_container(struct VFIOContainer *container); + +int vfio_cpr_reboot_notifier(NotifierWithReturn *notifier, MigrationEvent *e, + Error **errp); + +bool vfio_cpr_register_container(struct VFIOContainerBase *bcontainer, + Error **errp); +void vfio_cpr_unregister_container(struct VFIOContainerBase *bcontainer); + +int vfio_cpr_group_get_device_fd(int d, const char *name); + +bool vfio_cpr_container_match(struct VFIOContainer *container, + struct VFIOGroup *group, int fd); + +void vfio_cpr_giommu_remap(struct VFIOContainerBase *bcontainer, + MemoryRegionSection *section); + +bool vfio_cpr_ram_discard_register_listener( + struct VFIOContainerBase *bcontainer, MemoryRegionSection *section); + +extern const VMStateDescription vfio_cpr_pci_vmstate; + +#endif /* HW_VFIO_VFIO_CPR_H */ diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h new file mode 100644 index 00000000000..7c0669de0d5 --- /dev/null +++ b/include/hw/vfio/vfio-device.h @@ -0,0 +1,273 @@ +/* + * VFIO Device interface + * + * 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) + */ + +#ifndef HW_VFIO_VFIO_COMMON_H +#define HW_VFIO_VFIO_COMMON_H + +#include "system/memory.h" +#include "qemu/queue.h" +#ifdef CONFIG_LINUX +#include +#endif +#include "system/system.h" +#include "hw/vfio/vfio-container-base.h" +#include "hw/vfio/vfio-cpr.h" +#include "system/host_iommu_device.h" +#include "system/iommufd.h" + +#define VFIO_MSG_PREFIX "vfio %s: " + +enum { + VFIO_DEVICE_TYPE_PCI = 0, + VFIO_DEVICE_TYPE_PLATFORM = 1, + VFIO_DEVICE_TYPE_CCW = 2, + VFIO_DEVICE_TYPE_AP = 3, +}; + +typedef struct VFIODeviceOps VFIODeviceOps; +typedef struct VFIODeviceIOOps VFIODeviceIOOps; +typedef struct VFIOMigration VFIOMigration; + +typedef struct IOMMUFDBackend IOMMUFDBackend; +typedef struct VFIOIOASHwpt VFIOIOASHwpt; +typedef struct VFIOUserProxy VFIOUserProxy; + +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; + OnOffAuto enable_migration; + OnOffAuto migration_multifd_transfer; + bool migration_events; + bool use_region_fds; + VFIODeviceOps *ops; + VFIODeviceIOOps *io_ops; + unsigned int num_irqs; + unsigned int num_regions; + unsigned int flags; + VFIOMigration *migration; + Error *migration_blocker; + OnOffAuto pre_copy_dirty_page_tracking; + OnOffAuto device_dirty_page_tracking; + bool dirty_pages_supported; + bool dirty_tracking; /* Protected by BQL */ + bool iommu_dirty_tracking; + HostIOMMUDevice *hiod; + int devid; + IOMMUFDBackend *iommufd; + VFIOIOASHwpt *hwpt; + QLIST_ENTRY(VFIODevice) hwpt_next; + struct vfio_region_info **reginfo; + int *region_fds; + VFIODeviceCPR cpr; + VFIOUserProxy *proxy; +} VFIODevice; + +struct VFIODeviceOps { + void (*vfio_compute_needs_reset)(VFIODevice *vdev); + int (*vfio_hot_reset_multi)(VFIODevice *vdev); + void (*vfio_eoi)(VFIODevice *vdev); + Object *(*vfio_get_object)(VFIODevice *vdev); + + /** + * @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); +}; + +/* + * Given a return value of either a short number of bytes read or -errno, + * construct a meaningful error message. + */ +#define strreaderror(ret) \ + (ret < 0 ? strerror(-ret) : "short read") + +/* + * Given a return value of either a short number of bytes written or -errno, + * construct a meaningful error message. + */ +#define strwriteerror(ret) \ + (ret < 0 ? strerror(-ret) : "short write") + +void vfio_device_irq_disable(VFIODevice *vbasedev, int index); +void vfio_device_irq_unmask(VFIODevice *vbasedev, int index); +void vfio_device_irq_mask(VFIODevice *vbasedev, int index); +bool vfio_device_irq_set_signaling(VFIODevice *vbasedev, int index, int subindex, + int action, int fd, Error **errp); + +void vfio_device_reset_handler(void *opaque); +bool vfio_device_is_mdev(VFIODevice *vbasedev); +bool vfio_device_hiod_create_and_realize(VFIODevice *vbasedev, + const char *typename, Error **errp); +bool vfio_device_attach(char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp); +bool vfio_device_attach_by_iommu_type(const char *iommu_type, char *name, + VFIODevice *vbasedev, AddressSpace *as, + Error **errp); +void vfio_device_detach(VFIODevice *vbasedev); +VFIODevice *vfio_get_vfio_device(Object *obj); + +typedef QLIST_HEAD(VFIODeviceList, VFIODevice) VFIODeviceList; +extern VFIODeviceList vfio_device_list; + +#ifdef CONFIG_LINUX +/* + * How devices communicate with the server. The default option is through + * ioctl() to the kernel VFIO driver, but vfio-user can use a socket to a remote + * process. + */ +struct VFIODeviceIOOps { + /** + * @device_feature + * + * Fill in feature info for the given device. + * + * @vdev: #VFIODevice to use + * @feat: feature information to fill in + * + * Returns 0 on success or -errno. + */ + int (*device_feature)(VFIODevice *vdev, struct vfio_device_feature *feat); + + /** + * @get_region_info + * + * Get the information for a given region on the device. + * + * @vdev: #VFIODevice to use + * @info: set @info->index to the region index to look up; the rest of the + * struct will be filled in on success + * @fd: pointer to the fd for the region; will be -1 if not found + * + * Returns 0 on success or -errno. + */ + int (*get_region_info)(VFIODevice *vdev, + struct vfio_region_info *info, int *fd); + + /** + * @get_irq_info + * + * @vdev: #VFIODevice to use + * @irq: set @irq->index to the IRQ index to look up; the rest of the struct + * will be filled in on success + * + * Returns 0 on success or -errno. + */ + int (*get_irq_info)(VFIODevice *vdev, struct vfio_irq_info *irq); + + /** + * @set_irqs + * + * Configure IRQs. + * + * @vdev: #VFIODevice to use + * @irqs: IRQ configuration as defined by VFIO docs. + * + * Returns 0 on success or -errno. + */ + int (*set_irqs)(VFIODevice *vdev, struct vfio_irq_set *irqs); + + /** + * @region_read + * + * Read part of a region. + * + * @vdev: #VFIODevice to use + * @nr: region index + * @off: offset within the region + * @size: size in bytes to read + * @data: buffer to read into + * + * Returns number of bytes read on success or -errno. + */ + int (*region_read)(VFIODevice *vdev, uint8_t nr, off_t off, uint32_t size, + void *data); + + /** + * @region_write + * + * Write part of a region. + * + * @vdev: #VFIODevice to use + * @nr: region index + * @off: offset within the region + * @size: size in bytes to write + * @data: buffer to write from + * + * Returns number of bytes write on success or -errno. + */ + int (*region_write)(VFIODevice *vdev, uint8_t nr, off_t off, uint32_t size, + void *data, bool post); +}; + +void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainerBase *bcontainer, + struct vfio_device_info *info); + +void vfio_device_unprepare(VFIODevice *vbasedev); + +int vfio_device_get_region_info(VFIODevice *vbasedev, int index, + struct vfio_region_info **info); +int vfio_device_get_region_info_type(VFIODevice *vbasedev, uint32_t type, + uint32_t subtype, struct vfio_region_info **info); +bool vfio_device_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type); + +int vfio_device_get_irq_info(VFIODevice *vbasedev, int index, + struct vfio_irq_info *info); +#endif + +/* 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-migration.h b/include/hw/vfio/vfio-migration.h new file mode 100644 index 00000000000..0d4ecd33d5d --- /dev/null +++ b/include/hw/vfio/vfio-migration.h @@ -0,0 +1,16 @@ +/* + * VFIO migration interface + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_VFIO_MIGRATION_H +#define HW_VFIO_VFIO_MIGRATION_H + +bool vfio_migration_active(void); +int64_t vfio_migration_bytes_transferred(void); +void vfio_migration_reset_bytes_transferred(void); + +#endif /* HW_VFIO_VFIO_MIGRATION_H */ diff --git a/include/hw/vfio/vfio-platform.h b/include/hw/vfio/vfio-platform.h index c414c3dffcc..256d8500b70 100644 --- a/include/hw/vfio/vfio-platform.h +++ b/include/hw/vfio/vfio-platform.h @@ -17,7 +17,7 @@ #define HW_VFIO_VFIO_PLATFORM_H #include "hw/sysbus.h" -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include "qemu/event_notifier.h" #include "qemu/queue.h" #include "qom/object.h" @@ -47,6 +47,8 @@ typedef struct VFIOINTp { /* function type for user side eventfd handler */ typedef void (*eventfd_user_side_handler_t)(VFIOINTp *intp); +typedef struct VFIORegion VFIORegion; + struct VFIOPlatformDevice { SysBusDevice sbdev; VFIODevice vbasedev; /* not a QOM object */ diff --git a/include/hw/vfio/vfio-region.h b/include/hw/vfio/vfio-region.h new file mode 100644 index 00000000000..ede6e0c8f99 --- /dev/null +++ b/include/hw/vfio/vfio-region.h @@ -0,0 +1,48 @@ +/* + * VFIO region + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_REGION_H +#define HW_VFIO_REGION_H + +#include "system/memory.h" + +typedef struct VFIOMmap { + MemoryRegion mem; + void *mmap; + off_t offset; + size_t size; +} VFIOMmap; + +typedef struct VFIODevice VFIODevice; + +typedef struct VFIORegion { + struct VFIODevice *vbasedev; + off_t fd_offset; /* offset of region within device fd */ + MemoryRegion *mem; /* slow, read/write access */ + size_t size; + uint32_t flags; /* VFIO region flags (rd/wr/mmap) */ + uint32_t nr_mmaps; + VFIOMmap *mmaps; + uint8_t nr; /* cache the region number for debug */ + bool post_wr; /* writes can be posted */ +} VFIORegion; + + +void vfio_region_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size); +uint64_t vfio_region_read(void *opaque, + hwaddr addr, unsigned size); +int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, + int index, const char *name); +int vfio_region_mmap(VFIORegion *region); +void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled); +void vfio_region_unmap(VFIORegion *region); +void vfio_region_exit(VFIORegion *region); +void vfio_region_finalize(VFIORegion *region); + +#endif /* HW_VFIO_REGION_H */ diff --git a/include/hw/vfio/vfio.h b/include/hw/vfio/vfio.h deleted file mode 100644 index 86248f54360..00000000000 --- 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 00000000000..f25fd481add --- /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/iothread-vq-mapping.h b/include/hw/virtio/iothread-vq-mapping.h new file mode 100644 index 00000000000..57335c3703c --- /dev/null +++ b/include/hw/virtio/iothread-vq-mapping.h @@ -0,0 +1,45 @@ +/* + * IOThread Virtqueue Mapping + * + * Copyright Red Hat, Inc + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifndef HW_VIRTIO_IOTHREAD_VQ_MAPPING_H +#define HW_VIRTIO_IOTHREAD_VQ_MAPPING_H + +#include "qapi/error.h" +#include "qapi/qapi-types-virtio.h" + +/** + * iothread_vq_mapping_apply: + * @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 @list. + * + * iothread_vq_mapping_cleanup() must be called to free IOThread object + * references after this function returns success. + * + * Returns: %true on success, %false on failure. + **/ +bool iothread_vq_mapping_apply( + IOThreadVirtQueueMappingList *list, + AioContext **vq_aio_context, + uint16_t num_queues, + Error **errp); + +/** + * iothread_vq_mapping_cleanup: + * @list: The mapping of virtqueues to IOThreads. + * + * Release IOThread object references that were acquired by + * iothread_vq_mapping_apply(). + */ +void iothread_vq_mapping_cleanup(IOThreadVirtQueueMappingList *list); + +#endif /* HW_VIRTIO_IOTHREAD_VQ_MAPPING_H */ diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index 31a251a9f50..d6df209a2f0 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -11,7 +11,7 @@ #ifndef VHOST_BACKEND_H #define VHOST_BACKEND_H -#include "exec/memory.h" +#include "system/memory.h" typedef enum VhostBackendType { VHOST_BACKEND_TYPE_NONE = 0, @@ -26,6 +26,18 @@ typedef enum VhostSetConfigType { 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, @@ -133,11 +151,21 @@ typedef int (*vhost_set_config_call_op)(struct vhost_dev *dev, 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; @@ -154,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; @@ -163,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; @@ -172,7 +203,6 @@ 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; @@ -181,6 +211,9 @@ typedef struct VhostOps { 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, @@ -196,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 18f115527ce..d54d9c916f2 100644 --- a/include/hw/virtio/vhost-scsi-common.h +++ b/include/hw/virtio/vhost-scsi-common.h @@ -39,8 +39,8 @@ struct VHostSCSICommon { struct vhost_inflight *inflight; }; -int vhost_scsi_common_start(VHostSCSICommon *vsc); -void vhost_scsi_common_stop(VHostSCSICommon *vsc); +int vhost_scsi_common_start(VHostSCSICommon *vsc, Error **errp); +int vhost_scsi_common_stop(VHostSCSICommon *vsc); char *vhost_scsi_common_get_fw_dev_path(FWPathProvider *p, BusState *bus, DeviceState *dev); void vhost_scsi_common_set_config(VirtIODevice *vdev, const uint8_t *config); diff --git a/include/hw/virtio/vhost-user-base.h b/include/hw/virtio/vhost-user-base.h new file mode 100644 index 00000000000..51d0968b893 --- /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 a9d3f9b049f..5814a8400a0 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 0f7acd40e3a..a9b5612ad01 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 ddd9f01eea6..10868c7de4d 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-scsi.h b/include/hw/virtio/vhost-user-scsi.h index 521b08e5599..78fe616ccbd 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 00000000000..f9260116a73 --- /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 191216a74f8..9a3f238b43c 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 e64bfc7f98d..449bf5c840c 100644 --- a/include/hw/virtio/vhost-vdpa.h +++ b/include/hw/virtio/vhost-vdpa.h @@ -30,41 +30,81 @@ typedef struct VhostVDPAHostNotifier { 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; - uint32_t address_space_id; 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 + * + * It is shared among all ASID for simplicity, whether CVQ shares ASID with + * guest or not: + * - 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. + */ + VhostIOVATree *iova_tree; + + /* Copy of backend features */ + uint64_t backend_cap; + + bool iotlb_batch_begin_sent; + + /* + * The memory listener has been registered, so DMA maps have been sent to + * the device. + */ + bool listener_registered; + /* 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; - /* IOVA mapping used by the Shadow Virtqueue */ - VhostIOVATree *iova_tree; + 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]; - QLIST_HEAD(, vdpa_iommu) iommu_list; IOMMUNotifier n; } VhostVDPA; int vhost_vdpa_get_iova_range(int fd, struct vhost_vdpa_iova_range *iova_range); +int vhost_vdpa_set_vring_ready(struct vhost_vdpa *v, unsigned idx); -int vhost_vdpa_dma_map(struct vhost_vdpa *v, uint32_t asid, hwaddr iova, +int vhost_vdpa_dma_map(VhostVDPAShared *s, uint32_t asid, hwaddr iova, hwaddr size, void *vaddr, bool readonly); -int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, uint32_t asid, hwaddr iova, +int vhost_vdpa_dma_unmap(VhostVDPAShared *s, uint32_t asid, hwaddr iova, hwaddr size); typedef struct vdpa_iommu { - struct vhost_vdpa *dev; + VhostVDPAShared *dev_shared; IOMMUMemoryRegion *iommu_mr; hwaddr iommu_offset; IOMMUNotifier n; diff --git a/include/hw/virtio/vhost-vsock-common.h b/include/hw/virtio/vhost-vsock-common.h index 93c782101dd..01bf6062af5 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" @@ -41,7 +42,7 @@ struct VHostVSockCommon { }; int vhost_vsock_common_start(VirtIODevice *vdev); -void vhost_vsock_common_stop(VirtIODevice *vdev); +int vhost_vsock_common_stop(VirtIODevice *vdev); int vhost_vsock_common_pre_save(void *opaque); int vhost_vsock_common_post_load(void *opaque, int version_id); void vhost_vsock_common_realize(VirtIODevice *vdev); diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 6a173cb9fa3..38800a7156b 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -3,11 +3,13 @@ #include "hw/virtio/vhost-backend.h" #include "hw/virtio/virtio.h" -#include "exec/memory.h" +#include "system/memory.h" #define VHOST_F_DEVICE_IOTLB 63 #define VHOST_USER_F_PROTOCOL_FEATURES 30 +#define VU_REALIZE_CONN_RETRIES 3 + /* Generic structures common for any vhost based device. */ struct vhost_inflight { @@ -127,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; @@ -168,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 @@ -225,8 +232,10 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings); * Stop the vhost device. After the device is stopped the notifiers * can be disabled (@vhost_dev_disable_notifiers) and the device can * be torn down (@vhost_dev_cleanup). + * + * Return: 0 on success, != 0 on error when stopping dev. */ -void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings); +int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings); /** * DOC: vhost device configuration handling @@ -315,7 +324,8 @@ 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); @@ -325,17 +335,161 @@ int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write); int vhost_virtqueue_start(struct vhost_dev *dev, struct VirtIODevice *vdev, struct vhost_virtqueue *vq, unsigned idx); -void vhost_virtqueue_stop(struct vhost_dev *dev, struct VirtIODevice *vdev, - struct vhost_virtqueue *vq, unsigned idx); +int vhost_virtqueue_stop(struct vhost_dev *dev, struct VirtIODevice *vdev, + struct vhost_virtqueue *vq, unsigned idx); void vhost_dev_reset_inflight(struct vhost_inflight *inflight); void vhost_dev_free_inflight(struct vhost_inflight *inflight); -void vhost_dev_save_inflight(struct vhost_inflight *inflight, QEMUFile *f); -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. + */ +#ifdef CONFIG_VHOST +bool vhost_supports_device_state(struct vhost_dev *dev); +#else +static inline bool vhost_supports_device_state(struct vhost_dev *dev) +{ + return false; +} +#endif + +/** + * 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. + */ +#ifdef CONFIG_VHOST +int vhost_save_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp); +#else +static inline int vhost_save_backend_state(struct vhost_dev *dev, QEMUFile *f, + Error **errp) +{ + return -ENOSYS; +} +#endif + +/** + * 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. + */ +#ifdef CONFIG_VHOST +int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp); +#else +static inline int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f, + Error **errp) +{ + return -ENOSYS; +} +#endif + #endif diff --git a/include/hw/virtio/virtio-acpi.h b/include/hw/virtio/virtio-acpi.h new file mode 100644 index 00000000000..cdfbd943ae8 --- /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-balloon.h b/include/hw/virtio/virtio-balloon.h index 5139cf8ab68..0456c211c6e 100644 --- a/include/hw/virtio/virtio-balloon.h +++ b/include/hw/virtio/virtio-balloon.h @@ -16,8 +16,9 @@ #define QEMU_VIRTIO_BALLOON_H #include "standard-headers/linux/virtio_balloon.h" +#include "hw/resettable.h" #include "hw/virtio/virtio.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" #include "qom/object.h" #define TYPE_VIRTIO_BALLOON "virtio-balloon-device" @@ -71,6 +72,9 @@ struct VirtIOBalloon { bool qemu_4_0_config_size; uint32_t poison_val; + + /* State of the resettable container */ + ResettableState reset_state; }; #endif diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h index dafec432ce0..3d8dee7ec15 100644 --- a/include/hw/virtio/virtio-blk.h +++ b/include/hw/virtio/virtio-blk.h @@ -17,13 +17,14 @@ #include "standard-headers/linux/virtio_blk.h" #include "hw/virtio/virtio.h" #include "hw/block/block.h" -#include "sysemu/iothread.h" -#include "sysemu/block-backend.h" -#include "sysemu/block-ram-registrar.h" +#include "system/iothread.h" +#include "system/block-backend.h" +#include "system/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) +OBJECT_DECLARE_TYPE(VirtIOBlock, VirtIOBlkClass, VIRTIO_BLK) /* This is the last element of the write scatter-gather list */ struct virtio_blk_inhdr @@ -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,20 +50,27 @@ struct VirtIOBlkConf bool x_enable_wce_if_config_wce; }; -struct VirtIOBlockDataPlane; - struct VirtIOBlockReq; struct VirtIOBlock { VirtIODevice parent_obj; BlockBackend *blk; - void *rq; + 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; @@ -91,6 +100,15 @@ typedef struct MultiReqBuffer { bool is_write; } MultiReqBuffer; +typedef struct VirtIOBlkClass { + /*< private >*/ + VirtioDeviceClass parent; + /*< public >*/ + bool (*handle_unknown_request)(VirtIOBlockReq *req, MultiReqBuffer *mrb, + uint32_t type); +} VirtIOBlkClass; + void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq); +void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status); #endif diff --git a/include/hw/virtio/virtio-crypto.h b/include/hw/virtio/virtio-crypto.h index 348749f5d5c..2d56513693e 100644 --- a/include/hw/virtio/virtio-crypto.h +++ b/include/hw/virtio/virtio-crypto.h @@ -16,8 +16,8 @@ #include "standard-headers/linux/virtio_crypto.h" #include "hw/virtio/virtio.h" -#include "sysemu/iothread.h" -#include "sysemu/cryptodev.h" +#include "system/iothread.h" +#include "system/cryptodev.h" #include "qom/object.h" diff --git a/include/hw/virtio/virtio-dmabuf.h b/include/hw/virtio/virtio-dmabuf.h new file mode 100644 index 00000000000..627c3b6db79 --- /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 637a0585d0d..dd1975e2d40 100644 --- a/include/hw/virtio/virtio-gpu-bswap.h +++ b/include/hw/virtio/virtio-gpu-bswap.h @@ -70,6 +70,21 @@ virtio_gpu_create_blob_bswap(struct virtio_gpu_resource_create_blob *cblob) 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 390c4642b81..a42957c4e2c 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -19,7 +19,7 @@ #include "ui/console.h" #include "hw/virtio/virtio.h" #include "qemu/log.h" -#include "sysemu/vhost-user-backend.h" +#include "system/vhost-user-backend.h" #include "standard-headers/linux/virtio_gpu.h" #include "standard-headers/linux/virtio_ids.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,9 +51,7 @@ struct virtio_gpu_simple_resource { unsigned int iov_cnt; uint32_t scanout_bitmask; pixman_image_t *image; -#ifdef WIN32 - HANDLE handle; -#endif + qemu_pixman_shareable share_handle; uint64_t hostmem; uint64_t blob_size; @@ -78,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 { @@ -93,6 +95,10 @@ 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, + VIRTIO_GPU_FLAG_RESOURCE_UUID_ENABLED, }; #define virtio_gpu_virgl_enabled(_cfg) \ @@ -105,12 +111,23 @@ 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_resource_uuid_enabled(_cfg) \ + (_cfg.flags & (1 << VIRTIO_GPU_FLAG_RESOURCE_UUID_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 { @@ -134,6 +151,8 @@ struct VirtIOGPUBase { int renderer_blocked; int enable; + MemoryRegion hostmem; + struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS]; int enabled_output_bitmask; @@ -154,7 +173,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; @@ -162,6 +181,7 @@ typedef struct VGPUDMABuf { struct VirtIOGPU { VirtIOGPUBase parent_obj; + uint8_t scanout_vmstate_version; uint64_t conf_max_hostmem; VirtQueue *ctrl_vq; @@ -180,8 +200,6 @@ struct VirtIOGPU { uint64_t hostmem; bool processing_cmdq; - QEMUTimer *fence_poll; - QEMUTimer *print_stats; uint32_t inflight; struct { @@ -195,6 +213,8 @@ struct VirtIOGPU { QTAILQ_HEAD(, VGPUDMABuf) bufs; VGPUDMABuf *primary[VIRTIO_GPU_MAX_SCANOUTS]; } dmabuf; + + GArray *capset_ids; }; struct VirtIOGPUClass { @@ -205,13 +225,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 { @@ -220,18 +255,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) @@ -249,6 +305,9 @@ void virtio_gpu_base_fill_display_info(VirtIOGPUBase *g, 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, @@ -267,6 +326,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); @@ -275,6 +336,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); @@ -285,6 +361,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); @@ -292,6 +375,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 08f15914246..e097b0b5217 100644 --- a/include/hw/virtio/virtio-input.h +++ b/include/hw/virtio/virtio-input.h @@ -1,8 +1,10 @@ #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" +#include "system/vhost-user-backend.h" /* ----------------------------------------------------------------- */ /* virtio input protocol */ @@ -84,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; @@ -97,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 a93fc5383e0..3b86050f2cf 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 "system/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,16 +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-mem.h b/include/hw/virtio/virtio-mem.h index ab0fe2b4f26..e0ab31b45a4 100644 --- a/include/hw/virtio/virtio-mem.h +++ b/include/hw/virtio/virtio-mem.h @@ -14,9 +14,10 @@ #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" +#include "system/hostmem.h" #include "qom/object.h" #define TYPE_VIRTIO_MEM "virtio-mem" @@ -24,6 +25,10 @@ OBJECT_DECLARE_TYPE(VirtIOMEM, VirtIOMEMClass, VIRTIO_MEM) +#define TYPE_VIRTIO_MEM_SYSTEM_RESET "virtio-mem-system-reset" + +OBJECT_DECLARE_SIMPLE_TYPE(VirtioMemSystemReset, VIRTIO_MEM_SYSTEM_RESET) + #define VIRTIO_MEM_MEMDEV_PROP "memdev" #define VIRTIO_MEM_NODE_PROP "node" #define VIRTIO_MEM_SIZE_PROP "size" @@ -33,6 +38,7 @@ OBJECT_DECLARE_TYPE(VirtIOMEM, VirtIOMEMClass, #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; @@ -44,7 +50,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 */ @@ -82,20 +109,38 @@ struct VirtIOMEM { */ 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; + + /* Catch system resets -> qemu_devices_reset() only. */ + VirtioMemSystemReset *system_reset; +}; + +struct VirtioMemSystemReset { + Object parent; + + ResettableState reset_state; + VirtIOMEM *vmem; }; struct VirtIOMEMClass { /* private */ - VirtIODevice parent; + VirtioDeviceClass parent_class; /* 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); diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index e07a7230275..b9ea9e824e3 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 */ @@ -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, diff --git a/include/hw/virtio/virtio-nsm.h b/include/hw/virtio/virtio-nsm.h new file mode 100644 index 00000000000..57ddbbbf3f5 --- /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 ab2051b64b3..eab5394898d 100644 --- a/include/hw/virtio/virtio-pci.h +++ b/include/hw/virtio/virtio-pci.h @@ -32,9 +32,7 @@ DECLARE_OBJ_CHECKERS(VirtioPCIBusState, VirtioPCIBusClass, enum { VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, - VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT, VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT, - VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT, VIRTIO_PCI_FLAG_PAGE_PER_VQ_BIT, VIRTIO_PCI_FLAG_ATS_BIT, VIRTIO_PCI_FLAG_INIT_DEVERR_BIT, @@ -43,6 +41,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. */ @@ -53,12 +52,6 @@ enum { * vcpu thread using ioeventfd for some devices. */ #define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT) -/* virtio version flags */ -#define VIRTIO_PCI_FLAG_DISABLE_PCIE (1 << VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT) - -/* migrate extra state */ -#define VIRTIO_PCI_FLAG_MIGRATE_EXTRA (1 << VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT) - /* have pio notification for modern device ? */ #define VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY \ (1 << VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT) @@ -79,6 +72,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,11 +139,15 @@ 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; uint32_t modern_mem_bar_idx; int config_cap; + uint16_t last_pcie_cap_offset; uint32_t flags; bool disable_modern; bool ignore_backend_features; @@ -246,8 +247,9 @@ typedef struct VirtioPCIDeviceTypeInfo { size_t instance_size; size_t class_size; void (*instance_init)(Object *obj); - void (*class_init)(ObjectClass *klass, void *data); - InterfaceInfo *interfaces; + void (*instance_finalize)(Object *obj); + void (*class_init)(ObjectClass *klass, const void *data); + const InterfaceInfo *interfaces; } VirtioPCIDeviceTypeInfo; /* Register virtio-pci type(s). @t must be static. */ @@ -264,4 +266,8 @@ 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-pmem.h b/include/hw/virtio/virtio-pmem.h index fc4fd1f7fe8..9cce600d0b2 100644 --- a/include/hw/virtio/virtio-pmem.h +++ b/include/hw/virtio/virtio-pmem.h @@ -36,7 +36,7 @@ struct VirtIOPMEM { struct VirtIOPMEMClass { /* private */ - VirtIODevice parent; + VirtioDeviceClass parent_class; /* public */ void (*fill_device_info)(const VirtIOPMEM *pmem, VirtioPMEMDeviceInfo *vi); diff --git a/include/hw/virtio/virtio-rng.h b/include/hw/virtio/virtio-rng.h index 82734255d99..7e6d27f9f08 100644 --- a/include/hw/virtio/virtio-rng.h +++ b/include/hw/virtio/virtio-rng.h @@ -13,7 +13,7 @@ #define QEMU_VIRTIO_RNG_H #include "hw/virtio/virtio.h" -#include "sysemu/rng.h" +#include "system/rng.h" #include "standard-headers/linux/virtio_rng.h" #include "qom/object.h" diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h index 779568ab5d2..31e852ed6c4 100644 --- a/include/hw/virtio/virtio-scsi.h +++ b/include/hw/virtio/virtio-scsi.h @@ -22,7 +22,8 @@ #include "hw/virtio/virtio.h" #include "hw/scsi/scsi.h" #include "chardev/char-fe.h" -#include "sysemu/iothread.h" +#include "qapi/qapi-types-virtio.h" +#include "system/iothread.h" #define TYPE_VIRTIO_SCSI_COMMON "virtio-scsi-common" OBJECT_DECLARE_SIMPLE_TYPE(VirtIOSCSICommon, VIRTIO_SCSI_COMMON) @@ -51,6 +52,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; @@ -59,6 +61,7 @@ struct VirtIOSCSIConf { CharBackend chardev; uint32_t boot_tpgt; IOThread *iothread; + IOThreadVirtQueueMappingList *iothread_vq_mapping_list; }; struct VirtIOSCSI; @@ -81,17 +84,14 @@ struct VirtIOSCSI { SCSIBus bus; int resetting; /* written from main loop thread, read from any thread */ + + QemuMutex event_lock; /* protects event_vq and events_dropped */ bool events_dropped; - /* - * TMFs deferred to main loop BH. These fields are protected by - * virtio_scsi_acquire(). - */ - QEMUBH *tmf_bh; - QTAILQ_HEAD(, VirtIOSCSIReq) tmf_bh_list; + QemuMutex ctrl_lock; /* protects ctrl_vq */ /* Fields for dataplane below */ - AioContext *ctx; /* one iothread per virtio-scsi-pci for now */ + AioContext **vq_aio_context; /* per-virtqueue AioContext pointer */ bool dataplane_started; bool dataplane_starting; @@ -100,20 +100,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, @@ -123,6 +109,7 @@ void virtio_scsi_common_realize(DeviceState *dev, void virtio_scsi_common_unrealize(DeviceState *dev); void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp); +void virtio_scsi_dataplane_cleanup(VirtIOSCSI *s); int virtio_scsi_dataplane_start(VirtIODevice *s); void virtio_scsi_dataplane_stop(VirtIODevice *s); diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index c8f72850bc0..214d4a77e93 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -14,7 +14,7 @@ #ifndef QEMU_VIRTIO_H #define QEMU_VIRTIO_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #include "net/net.h" #include "migration/vmstate.h" @@ -22,6 +22,7 @@ #include "standard-headers/linux/virtio_config.h" #include "standard-headers/linux/virtio_ring.h" #include "qom/object.h" +#include "block/aio.h" /* * A guest should never accept this. It implies negotiation is broken @@ -68,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; @@ -183,7 +186,7 @@ struct VirtioDeviceClass { void (*get_config)(VirtIODevice *vdev, uint8_t *config); void (*set_config)(VirtIODevice *vdev, const uint8_t *config); void (*reset)(VirtIODevice *vdev); - void (*set_status)(VirtIODevice *vdev, uint8_t val); + int (*set_status)(VirtIODevice *vdev, uint8_t val); /* Device must validate queue_index. */ void (*queue_reset)(VirtIODevice *vdev, uint32_t queue_index); /* Device must validate queue_index. */ @@ -207,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. */ @@ -220,6 +225,7 @@ 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); }; @@ -270,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); @@ -306,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); @@ -368,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); @@ -469,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) { @@ -479,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) @@ -508,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/vmapple/vmapple.h b/include/hw/vmapple/vmapple.h new file mode 100644 index 00000000000..9c1ad1bd8c3 --- /dev/null +++ b/include/hw/vmapple/vmapple.h @@ -0,0 +1,23 @@ +/* + * Devices specific to the VMApple machine type + * + * 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. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VMAPPLE_VMAPPLE_H +#define HW_VMAPPLE_VMAPPLE_H + +#define TYPE_APPLE_AES "apple-aes" + +#define TYPE_VMAPPLE_BDIF "vmapple-bdif" + +#define TYPE_VMAPPLE_CFG "vmapple-cfg" + +#define TYPE_VMAPPLE_VIRTIO_BLK_PCI "vmapple-virtio-blk-pci" + +#endif /* HW_VMAPPLE_VMAPPLE_H */ diff --git a/include/hw/watchdog/sbsa_gwdt.h b/include/hw/watchdog/sbsa_gwdt.h index 70b137de301..4bdc6c6fdb6 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 e90ef86651e..830b0a79368 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 (0x30 / 4) +#define ASPEED_WDT_REGS_MAX (0x80 / 4) struct AspeedWDTState { /*< private >*/ diff --git a/include/hw/xen/arch_hvm.h b/include/hw/xen/arch_hvm.h index c7c515220d2..df39c819c8f 100644 --- a/include/hw/xen/arch_hvm.h +++ b/include/hw/xen/arch_hvm.h @@ -1,5 +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) +#elif defined(TARGET_ARM) || defined(TARGET_AARCH64) #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 index 94b31511dde..1528ced5097 100644 --- a/include/hw/xen/interface/arch-arm.h +++ b/include/hw/xen/interface/arch-arm.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * arch-arm.h * * Guest OS interface to ARM Xen. * - * 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 2011 (C) Citrix Systems */ @@ -361,6 +344,7 @@ typedef uint64_t xen_callback_t; #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 @@ -383,7 +367,15 @@ typedef uint64_t xen_callback_t; #define PSR_MODE_EL1t 0x04 #define PSR_MODE_EL0t 0x00 -#define PSR_GUEST32_INIT (PSR_ABT_MASK|PSR_FIQ_MASK|PSR_IRQ_MASK|PSR_MODE_SVC) +/* + * 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) @@ -398,6 +390,10 @@ typedef uint64_t xen_callback_t; /* 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. @@ -484,6 +480,9 @@ typedef uint64_t xen_callback_t; #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 diff --git a/include/hw/xen/interface/arch-x86/cpuid.h b/include/hw/xen/interface/arch-x86/cpuid.h index ce46305bee9..7ecd16ae05f 100644 --- a/include/hw/xen/interface/arch-x86/cpuid.h +++ b/include/hw/xen/interface/arch-x86/cpuid.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * arch-x86/cpuid.h * * CPUID interface to Xen. * - * 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) 2007 Citrix Systems, Inc. * * Authors: @@ -102,6 +85,18 @@ #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) diff --git a/include/hw/xen/interface/arch-x86/xen-x86_32.h b/include/hw/xen/interface/arch-x86/xen-x86_32.h index 19d7388633e..139438e8353 100644 --- a/include/hw/xen/interface/arch-x86/xen-x86_32.h +++ b/include/hw/xen/interface/arch-x86/xen-x86_32.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * xen-x86_32.h * * Guest OS interface to x86 32-bit Xen. * - * 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-2007, K A Fraser */ diff --git a/include/hw/xen/interface/arch-x86/xen-x86_64.h b/include/hw/xen/interface/arch-x86/xen-x86_64.h index 40aed14366d..5d9035ed223 100644 --- a/include/hw/xen/interface/arch-x86/xen-x86_64.h +++ b/include/hw/xen/interface/arch-x86/xen-x86_64.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * xen-x86_64.h * * Guest OS interface to x86 64-bit Xen. * - * 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-2006, K A Fraser */ diff --git a/include/hw/xen/interface/arch-x86/xen.h b/include/hw/xen/interface/arch-x86/xen.h index 7acd94c8ebe..c0f4551247f 100644 --- a/include/hw/xen/interface/arch-x86/xen.h +++ b/include/hw/xen/interface/arch-x86/xen.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * arch-x86/xen.h * * Guest OS interface to x86 Xen. * - * 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-2006, K A Fraser */ @@ -320,12 +303,9 @@ struct xen_arch_domainconfig { uint32_t misc_flags; }; -/* Location of online VCPU bitmap. */ -#define XEN_ACPI_CPU_MAP 0xaf00 -#define XEN_ACPI_CPU_MAP_LEN ((HVM_MAX_VCPUS + 7) / 8) +/* Max XEN_X86_* constant. Used for ABI checking. */ +#define XEN_X86_MISC_FLAGS_MAX XEN_X86_MSR_RELAXED -/* GPE0 bit set during CPU hotplug */ -#define XEN_ACPI_GPE0_CPUHP_BIT 2 #endif /* diff --git a/include/hw/xen/interface/event_channel.h b/include/hw/xen/interface/event_channel.h index 73c9f38ce1a..0d91a1c4afa 100644 --- a/include/hw/xen/interface/event_channel.h +++ b/include/hw/xen/interface/event_channel.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * event_channel.h * * Event channels between domains. * - * 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, K A Fraser. */ diff --git a/include/hw/xen/interface/features.h b/include/hw/xen/interface/features.h index 9ee2f760ef9..d2a9175aae6 100644 --- a/include/hw/xen/interface/features.h +++ b/include/hw/xen/interface/features.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * features.h * * Feature flags, reported by XENVER_get_features. * - * 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) 2006, Keir Fraser */ diff --git a/include/hw/xen/interface/grant_table.h b/include/hw/xen/interface/grant_table.h index 7934d7b718a..1dfa17a6d02 100644 --- a/include/hw/xen/interface/grant_table.h +++ b/include/hw/xen/interface/grant_table.h @@ -1,27 +1,10 @@ +/* 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 */ diff --git a/include/hw/xen/interface/hvm/hvm_op.h b/include/hw/xen/interface/hvm/hvm_op.h index 870ec520607..e22adf03198 100644 --- a/include/hw/xen/interface/hvm/hvm_op.h +++ b/include/hw/xen/interface/hvm/hvm_op.h @@ -1,22 +1,5 @@ +/* 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. - * * Copyright (c) 2007, Keir Fraser */ diff --git a/include/hw/xen/interface/hvm/params.h b/include/hw/xen/interface/hvm/params.h index c9d6e70d7ba..a22b4ed45d2 100644 --- a/include/hw/xen/interface/hvm/params.h +++ b/include/hw/xen/interface/hvm/params.h @@ -1,22 +1,5 @@ +/* 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. - * * Copyright (c) 2007, Keir Fraser */ diff --git a/include/hw/xen/interface/io/blkif.h b/include/hw/xen/interface/io/blkif.h index 4cdba79abae..c5527999d1b 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 */ @@ -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 the backend driver will still try to map those grants + * since 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 diff --git a/include/hw/xen/interface/io/console.h b/include/hw/xen/interface/io/console.h index 4811f47220a..4509b4b689e 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 */ diff --git a/include/hw/xen/interface/io/fbif.h b/include/hw/xen/interface/io/fbif.h index cc25aab32ea..93c73195d89 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 */ diff --git a/include/hw/xen/interface/io/kbdif.h b/include/hw/xen/interface/io/kbdif.h index a6b01c52c79..4bde6b38215 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 */ diff --git a/include/hw/xen/interface/io/netif.h b/include/hw/xen/interface/io/netif.h index 00dd258712f..c13b85061df 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 * ============ diff --git a/include/hw/xen/interface/io/protocols.h b/include/hw/xen/interface/io/protocols.h index 52b4de0f818..7815e1ff0fb 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 c486c457e07..025939278b8 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. */ @@ -95,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): @@ -184,6 +166,11 @@ typedef struct __name##_back_ring __name##_back_ring_t #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); \ @@ -208,11 +195,11 @@ 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) #ifdef __GNUC__ -#define RING_HAS_UNCONSUMED_REQUESTS(_r) ({ \ +#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); \ @@ -220,13 +207,27 @@ typedef struct __name##_back_ring __name##_back_ring_t }) #else /* Same as above, but without the nice GCC ({ ... }) syntax. */ -#define RING_HAS_UNCONSUMED_REQUESTS(_r) \ +#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)) diff --git a/include/hw/xen/interface/io/usbif.h b/include/hw/xen/interface/io/usbif.h index c0a552e195a..875af0dc7cd 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__ diff --git a/include/hw/xen/interface/io/xenbus.h b/include/hw/xen/interface/io/xenbus.h index 927f9db5528..9cd0cd7c670 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. */ diff --git a/include/hw/xen/interface/io/xs_wire.h b/include/hw/xen/interface/io/xs_wire.h index 4dd66326696..04e6849feb1 100644 --- a/include/hw/xen/interface/io/xs_wire.h +++ b/include/hw/xen/interface/io/xs_wire.h @@ -1,25 +1,8 @@ +/* SPDX-License-Identifier: MIT */ /* * Details of the "wire" protocol between Xen Store Daemon and client * library or guest kernel. * - * 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 Rusty Russell IBM Corporation */ @@ -71,11 +54,12 @@ struct xsd_errors #ifdef EINVAL #define XSD_ERROR(x) { x, #x } /* LINTED: static unused */ -static struct xsd_errors xsd_errors[] +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), @@ -90,7 +74,8 @@ __attribute__((unused)) XSD_ERROR(EBUSY), XSD_ERROR(EAGAIN), XSD_ERROR(EISCONN), - XSD_ERROR(E2BIG) + XSD_ERROR(E2BIG), + XSD_ERROR(EPERM), }; #endif @@ -124,6 +109,7 @@ struct xenstore_domain_interface { 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. */ @@ -135,10 +121,18 @@ struct xenstore_domain_interface { /* 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 /* guest has initiated a reconnect */ +#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 */ diff --git a/include/hw/xen/interface/memory.h b/include/hw/xen/interface/memory.h index 383a9468c36..29cf5c82390 100644 --- a/include/hw/xen/interface/memory.h +++ b/include/hw/xen/interface/memory.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * memory.h * * Memory reservation and information. * - * 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 */ @@ -541,12 +524,14 @@ struct xen_mem_sharing_op { uint32_t gref; /* IN: gref to debug */ } u; } debug; - struct mem_sharing_op_fork { /* OP_FORK */ + 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; @@ -662,6 +647,13 @@ struct xen_mem_acquire_resource { * 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 diff --git a/include/hw/xen/interface/physdev.h b/include/hw/xen/interface/physdev.h index d271766ad0c..f0c0d4727c0 100644 --- a/include/hw/xen/interface/physdev.h +++ b/include/hw/xen/interface/physdev.h @@ -1,22 +1,5 @@ +/* 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. - * * Copyright (c) 2006, Keir Fraser */ @@ -211,8 +194,8 @@ struct physdev_manage_pci_ext { /* IN */ uint8_t bus; uint8_t devfn; - unsigned is_extfn; - unsigned is_virtfn; + uint32_t is_extfn; + uint32_t is_virtfn; struct { uint8_t bus; uint8_t devfn; diff --git a/include/hw/xen/interface/sched.h b/include/hw/xen/interface/sched.h index 811bd87c82e..b4362c6a1d6 100644 --- a/include/hw/xen/interface/sched.h +++ b/include/hw/xen/interface/sched.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * sched.h * * Scheduler state interactions * - * 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 */ diff --git a/include/hw/xen/interface/trace.h b/include/hw/xen/interface/trace.h index d5fa4aea8dd..62a179971d2 100644 --- a/include/hw/xen/interface/trace.h +++ b/include/hw/xen/interface/trace.h @@ -1,24 +1,7 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * include/public/trace.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. - * * Mark Williamson, (C) 2004 Intel Research Cambridge * Copyright (C) 2005 Bin Ren */ diff --git a/include/hw/xen/interface/vcpu.h b/include/hw/xen/interface/vcpu.h index 3623af932f7..81a3b3a7438 100644 --- a/include/hw/xen/interface/vcpu.h +++ b/include/hw/xen/interface/vcpu.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * vcpu.h * * VCPU initialisation, query, and hotplug. * - * 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 */ diff --git a/include/hw/xen/interface/version.h b/include/hw/xen/interface/version.h index 17a81e23cde..9c78b4f3b6a 100644 --- a/include/hw/xen/interface/version.h +++ b/include/hw/xen/interface/version.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * version.h * * Xen version, type, and compile information. * - * 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, Nguyen Anh Quynh * Copyright (c) 2005, Keir Fraser */ diff --git a/include/hw/xen/interface/xen-compat.h b/include/hw/xen/interface/xen-compat.h index e1c027a95c7..97fe6984989 100644 --- a/include/hw/xen/interface/xen-compat.h +++ b/include/hw/xen/interface/xen-compat.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * xen-compat.h * * Guest OS interface to Xen. Compatibility layer. * - * 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) 2006, Christian Limpach */ diff --git a/include/hw/xen/interface/xen.h b/include/hw/xen/interface/xen.h index e373592c33c..920567e0062 100644 --- a/include/hw/xen/interface/xen.h +++ b/include/hw/xen/interface/xen.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: MIT */ /****************************************************************************** * xen.h * * Guest OS interface to Xen. * - * 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 */ diff --git a/include/hw/xen/xen-backend.h b/include/hw/xen/xen-backend.h index aac2fd454d4..0f01631ae7a 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-block.h b/include/hw/xen/xen-block.h index d692ea75802..449a7f75fb5 100644 --- a/include/hw/xen/xen-block.h +++ b/include/hw/xen/xen-block.h @@ -11,7 +11,7 @@ #include "hw/xen/xen-bus.h" #include "hw/block/block.h" #include "hw/block/dataplane/xen-block.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" #include "qom/object.h" typedef enum XenBlockVdevType { diff --git a/include/hw/xen/xen-bus-helper.h b/include/hw/xen/xen-bus-helper.h index d8dcc2f0107..e9911115b32 100644 --- a/include/hw/xen/xen-bus-helper.h +++ b/include/hw/xen/xen-bus-helper.h @@ -38,6 +38,15 @@ int xs_node_scanf(struct qemu_xs_handle *h, xs_transaction_t tid, const char *fmt, ...) G_GNUC_SCANF(6, 7); +/* + * Unlike other functions here, the printf-formatted path_fmt is for + * the XenStore path, not the contents of the node. + */ +char *xs_node_read(struct qemu_xs_handle *h, xs_transaction_t tid, + unsigned int *len, Error **errp, + const char *path_fmt, ...) + G_GNUC_PRINTF(5, 6); + /* Watch node/key unless node is empty, in which case watch key */ struct qemu_xs_watch *xs_node_watch(struct qemu_xs_handle *h, const char *node, const char *key, xs_watch_fn fn, diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h index f4358981649..bdbf1ed6fd0 100644 --- a/include/hw/xen/xen-bus.h +++ b/include/hw/xen/xen-bus.h @@ -8,9 +8,10 @@ #ifndef HW_XEN_BUS_H #define HW_XEN_BUS_H +#include "hw/qdev-core.h" #include "hw/xen/xen_backend_ops.h" -#include "hw/sysbus.h" #include "qemu/notify.h" +#include "qemu/queue.h" #include "qom/object.h" typedef struct XenEventChannel XenEventChannel; @@ -33,6 +34,7 @@ struct XenDevice { }; 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, @@ -46,6 +48,7 @@ struct XenDeviceClass { /*< public >*/ const char *backend; const char *device; + XenDeviceGetFrontendPath get_frontend_path; XenDeviceGetName get_name; XenDeviceRealize realize; XenDeviceFrontendChanged frontend_changed; @@ -89,6 +92,7 @@ void xen_device_frontend_printf(XenDevice *xendev, const char *key, int xen_device_frontend_scanf(XenDevice *xendev, const char *key, const char *fmt, ...) G_GNUC_SCANF(3, 4); +char *xen_device_frontend_read(XenDevice *xendev, const char *key); void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs, Error **errp); @@ -129,5 +133,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 index 4e9904f1a65..19df5600a39 100644 --- a/include/hw/xen/xen-hvm-common.h +++ b/include/hw/xen/xen-hvm-common.h @@ -1,22 +1,14 @@ #ifndef HW_XEN_HVM_COMMON_H #define HW_XEN_HVM_COMMON_H -#include "qemu/osdep.h" -#include "qemu/units.h" - -#include "cpu.h" -#include "hw/pci/pci.h" -#include "hw/hw.h" +#include "qemu/queue.h" +#include "exec/hwaddr.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 "hw/xen/xen_backend_ops.h" #include -extern MemoryRegion ram_memory; +extern MemoryRegion xen_memory; +extern MemoryRegion xen_grants; extern MemoryListener xen_io_listener; extern DeviceListener xen_device_listener; @@ -30,6 +22,8 @@ extern DeviceListener xen_device_listener; 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; @@ -79,6 +73,8 @@ typedef struct XenIOState { QLIST_HEAD(, XenPciDevice) dev_list; DeviceListener device_listener; + bool has_bufioreq; + Notifier exit; } XenIOState; @@ -93,6 +89,7 @@ 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); diff --git a/include/hw/xen/xen-legacy-backend.h b/include/hw/xen/xen-legacy-backend.h index 6c307c5f2c3..2d0cbfecade 100644 --- a/include/hw/xen/xen-legacy-backend.h +++ b/include/hw/xen/xen-legacy-backend.h @@ -3,7 +3,6 @@ #include "hw/xen/xen_backend_ops.h" #include "hw/xen/xen_pvdev.h" -#include "net/net.h" #include "qom/object.h" #define TYPE_XENSYSDEV "xen-sysdev" @@ -40,7 +39,7 @@ void xen_be_check_state(struct XenLegacyDevice *xendev); /* xen backend driver bits */ void xen_be_init(void); -int xen_be_register(const char *type, struct XenDevOps *ops); +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, @@ -50,10 +49,6 @@ void *xen_be_map_grant_refs(struct XenLegacyDevice *xendev, uint32_t *refs, void xen_be_unmap_grant_refs(struct XenLegacyDevice *xendev, void *ptr, uint32_t *refs, unsigned int nr_refs); -int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev, - bool to_domain, XenGrantCopySegment segs[], - unsigned int nr_segs); - static inline void *xen_be_map_grant_ref(struct XenLegacyDevice *xendev, uint32_t ref, int prot) { @@ -66,25 +61,9 @@ static inline void xen_be_unmap_grant_ref(struct XenLegacyDevice *xendev, 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 00000000000..5db83d88ecc --- /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 "system/memory.h" +#include "qom/object.h" +#include "hw/boards.h" +#include "hw/pci-host/gpex.h" +#include "hw/xen/xen-hvm-common.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 37ecc91fc31..e94c6e5a318 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -24,8 +24,6 @@ #define __XEN_INTERFACE_VERSION__ 0x00040e00 #endif -#include "exec/cpu-common.h" - /* xen-machine.c */ enum xen_mode { XEN_DISABLED = 0, /* xen support disabled (default) */ @@ -36,6 +34,7 @@ enum xen_mode { 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); diff --git a/include/hw/xen/xen_igd.h b/include/hw/xen/xen_igd.h new file mode 100644 index 00000000000..7ffca06c109 --- /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 index 5d2718261f2..5caf91a6162 100644 --- a/include/hw/xen/xen_native.h +++ b/include/hw/xen/xen_native.h @@ -464,10 +464,11 @@ static inline void xen_unmap_pcidev(domid_t dom, } 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, - HVM_IOREQSRV_BUFIOREQ_ATOMIC, + handle_bufioreq, ioservid); if (rc == 0) { @@ -523,16 +524,16 @@ static inline int xen_set_ioreq_server_state(domid_t dom, enable); } -#if CONFIG_XEN_CTRL_INTERFACE_VERSION <= 41500 +#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 0; + return -1; } #endif -#if CONFIG_XEN_CTRL_INTERFACE_VERSION <= 41700 +#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 diff --git a/include/hw/xen/xen_pvdev.h b/include/hw/xen/xen_pvdev.h index ddad4b9f36a..629bec90d09 100644 --- a/include/hw/xen/xen_pvdev.h +++ b/include/hw/xen/xen_pvdev.h @@ -1,7 +1,7 @@ #ifndef QEMU_HW_XEN_PVDEV_H #define QEMU_HW_XEN_PVDEV_H -#include "hw/qdev-core.h" +#include "hw/sysbus.h" #include "hw/xen/xen_backend_ops.h" /* ------------------------------------------------------------- */ @@ -29,11 +29,11 @@ struct XenDevOps { const char *node); void (*frontend_changed)(struct XenLegacyDevice *xendev, const char *node); - int (*backend_register)(void); }; struct XenLegacyDevice { - DeviceState qdev; + SysBusDevice parent_obj; + const char *type; int dom; int dev; @@ -53,7 +53,7 @@ struct XenLegacyDevice { xenevtchn_handle *evtchndev; xengnttab_handle *gnttabdev; - struct XenDevOps *ops; + const struct XenDevOps *ops; QTAILQ_ENTRY(XenLegacyDevice) next; }; diff --git a/include/hw/xtensa/mx_pic.h b/include/hw/xtensa/mx_pic.h index 500424c8d35..cd316d86eb6 100644 --- a/include/hw/xtensa/mx_pic.h +++ b/include/hw/xtensa/mx_pic.h @@ -28,7 +28,7 @@ #ifndef XTENSA_MX_PIC_H #define XTENSA_MX_PIC_H -#include "exec/memory.h" +#include "system/memory.h" struct XtensaMxPic; typedef struct XtensaMxPic XtensaMxPic; diff --git a/include/io/channel-file.h b/include/io/channel-file.h index 50e8eb11386..d373a4e44d9 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 ab15577d384..a88cf8b3a9f 100644 --- a/include/io/channel-socket.h +++ b/include/io/channel-socket.h @@ -261,5 +261,18 @@ QIOChannelSocket * qio_channel_socket_accept(QIOChannelSocket *ioc, Error **errp); +/** + * qio_channel_socket_set_send_buffer: + * @ioc: the socket channel object + * @size: buffer size + * @errp: pointer to a NULL-initialized error object + * + * Set the underlying socket send buffer size. + * + * Retruns: 0 on success, or -1 on error. + */ +int qio_channel_socket_set_send_buffer(QIOChannelSocket *ioc, + size_t size, + Error **errp); #endif /* QIO_CHANNEL_SOCKET_H */ diff --git a/include/io/channel-tls.h b/include/io/channel-tls.h index 26c67f17e2d..7e9023570d8 100644 --- a/include/io/channel-tls.h +++ b/include/io/channel-tls.h @@ -49,8 +49,20 @@ struct QIOChannelTLS { QCryptoTLSSession *session; QIOChannelShutdown shutdown; guint hs_ioc_tag; + guint bye_ioc_tag; }; +/** + * qio_channel_tls_bye: + * @ioc: the TLS channel object + * @errp: pointer to a NULL-initialized error object + * + * Perform the TLS session termination. This method will return + * immediately and the termination will continue in the background, + * provided the main loop is running. + */ +void qio_channel_tls_bye(QIOChannelTLS *ioc, Error **errp); + /** * qio_channel_tls_new_server: * @master: the underlying channel object diff --git a/include/io/channel.h b/include/io/channel.h index 5f9dbaab65b..62b657109c7 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -35,6 +35,7 @@ OBJECT_DECLARE_TYPE(QIOChannel, QIOChannelClass, #define QIO_CHANNEL_WRITE_FLAG_ZERO_COPY 0x1 #define QIO_CHANNEL_READ_FLAG_MSG_PEEK 0x1 +#define QIO_CHANNEL_READ_FLAG_RELAXED_EOF 0x2 typedef enum QIOChannelFeature QIOChannelFeature; @@ -44,6 +45,7 @@ enum QIOChannelFeature { QIO_CHANNEL_FEATURE_LISTEN, QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY, QIO_CHANNEL_FEATURE_READ_MSG_PEEK, + QIO_CHANNEL_FEATURE_SEEKABLE, }; @@ -130,6 +132,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); @@ -149,6 +161,9 @@ struct QIOChannelClass { void *opaque); int (*io_flush)(QIOChannel *ioc, Error **errp); + int (*io_peerpid)(QIOChannel *ioc, + unsigned int *pid, + Error **errp); }; /* General I/O handling functions */ @@ -528,6 +543,78 @@ void qio_channel_set_follow_coroutine_ctx(QIOChannel *ioc, bool enabled); 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 @@ -799,6 +886,7 @@ void qio_channel_set_aio_fd_handler(QIOChannel *ioc, * @niov: the length of the @iov array * @fds: an array of file handles to read * @nfds: number of file handles in @fds + * @flags: read flags (QIO_CHANNEL_READ_FLAG_*) * @errp: pointer to a NULL-initialized error object * * @@ -817,6 +905,7 @@ int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc, const struct iovec *iov, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp); /** @@ -898,4 +987,22 @@ int coroutine_mixed_fn 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 dc7d32ebd07..0b5342ee843 100644 --- a/include/io/task.h +++ b/include/io/task.h @@ -149,7 +149,7 @@ typedef void (*QIOTaskWorker)(QIOTask *task, * 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 9cebe2ba06a..a687ac0efea 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 **reasonp, Error **errp); + +/** + * @migrate_del_blocker - remove a migration blocker from all modes and free it. + * + * @reasonp - address of the error blocking migration + * + * This function frees *@reasonp and sets it to NULL. + */ +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_internal(Error *reason, Error **errp); +int migrate_add_blocker_normal(Error **reasonp, Error **errp); /** - * @migrate_del_blocker - remove a blocking error from migration + * @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. * - * @reason - the error blocking migration + * *@reasonp is freed and set to NULL if failure is returned. + * On success, the caller must not free *@reasonp before the blocker is removed. */ -void migrate_del_blocker(Error *reason); +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 00000000000..289c9d77622 --- /dev/null +++ b/include/migration/client-options.h @@ -0,0 +1,29 @@ +/* + * 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 + + +/* properties */ +bool migrate_send_switchover_start(void); + +/* 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 eaac07f26d0..43222ef5ae6 100644 --- a/include/migration/colo.h +++ b/include/migration/colo.h @@ -49,7 +49,7 @@ void colo_checkpoint_delay_set(void); * * Called with BQL locked, may temporary release BQL. */ -int coroutine_fn colo_incoming_co(void); +void coroutine_fn colo_incoming_co(void); void colo_shutdown(void); #endif diff --git a/include/migration/cpr.h b/include/migration/cpr.h new file mode 100644 index 00000000000..07858e93fa2 --- /dev/null +++ b/include/migration/cpr.h @@ -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. + */ + +#ifndef MIGRATION_CPR_H +#define MIGRATION_CPR_H + +#include "qapi/qapi-types-migration.h" + +#define MIG_MODE_NONE -1 + +#define QEMU_CPR_FILE_MAGIC 0x51435052 +#define QEMU_CPR_FILE_VERSION 0x00000001 + +void cpr_save_fd(const char *name, int id, int fd); +void cpr_delete_fd(const char *name, int id); +int cpr_find_fd(const char *name, int id); +void cpr_resave_fd(const char *name, int id, int fd); +int cpr_open_fd(const char *path, int flags, const char *name, int id, + Error **errp); + +MigMode cpr_get_incoming_mode(void); +void cpr_set_incoming_mode(MigMode mode); +bool cpr_is_incoming(void); + +int cpr_state_save(MigrationChannel *channel, Error **errp); +int cpr_state_load(MigrationChannel *channel, Error **errp); +void cpr_state_close(void); +struct QIOChannel *cpr_state_ioc(void); + +bool cpr_incoming_needed(void *opaque); + +QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp); +QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp); + +#endif diff --git a/include/migration/misc.h b/include/migration/misc.h index 7dcc0b5c2cc..8fd36eba1da 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,43 +39,105 @@ 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 */ void dump_vmstate_json_to_file(FILE *out_fp); +void qemu_loadvm_start_load_thread(MigrationLoadThread function, + void *opaque); /* 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 *); + +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); +/* Wrapper for block active/inactive operations */ +bool migration_block_activate(Error **errp); +bool migration_block_inactivate(void); + +/* True if @uri starts with a syntactically valid URI prefix */ +bool migrate_is_uri(const char *uri); + +/* Parse @uri and return @channel, returning true on success */ +bool migrate_uri_parse(const char *uri, MigrationChannel **channel, + Error **errp); + +/* migration/multifd-device-state.c */ +typedef struct SaveLiveCompletePrecopyThreadData { + SaveLiveCompletePrecopyThreadHandler hdlr; + char *idstr; + uint32_t instance_id; + void *handler_opaque; +} SaveLiveCompletePrecopyThreadData; + +bool multifd_queue_device_state(char *idstr, uint32_t instance_id, + char *data, size_t len); +bool multifd_device_state_supported(void); + +void +multifd_spawn_device_state_save_thread(SaveLiveCompletePrecopyThreadHandler hdlr, + char *idstr, uint32_t instance_id, + void *opaque); + +bool multifd_device_state_save_thread_should_exit(void); + +void multifd_abort_device_state_save_threads(void); +bool multifd_join_device_state_save_threads(void); #endif diff --git a/include/migration/qemu-file-types.h b/include/migration/qemu-file-types.h index 9ba163f333e..adec5abc071 100644 --- a/include/migration/qemu-file-types.h +++ b/include/migration/qemu-file-types.h @@ -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); diff --git a/include/migration/register.h b/include/migration/register.h index 2b12c6adeca..b79dc81b8d2 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -16,76 +16,354 @@ #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; - /* - * save_prepare is called early, even before migration starts, and can be - * used to perform early checks. + /* 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. + * Note that this handler can be called even if save_setup + * wasn't called earlier. + * + * @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. */ + /** + * @save_live_complete_precopy_thread (invoked in a separate thread) + * + * Called at the end of a precopy phase from a separate worker thread + * in configurations where multifd device state transfer is supported + * in order to perform asynchronous transmission of the remaining data in + * parallel with @save_live_complete_precopy handlers. + * When postcopy is enabled, devices that support postcopy will skip this + * step. + * + * @d: a #SaveLiveCompletePrecopyThreadData containing parameters that the + * handler may need, including this device section idstr and instance_id, + * and opaque data pointer passed to register_savevm_live(). + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns true to indicate success and false for errors. + */ + SaveLiveCompletePrecopyThreadHandler save_live_complete_precopy_thread; + + /* 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); - /* Note for save_live_pending: - * must_precopy: - * - must be migrated in precopy or in stopped state - * - i.e. must be migrated before target start + /* This runs outside the BQL! */ + + /** + * @save_postcopy_prepare + * + * This hook will be invoked on the source side right before switching + * to postcopy (before VM stopped). * - * can_postcopy: - * - can migrate in postcopy or in stopped state - * - i.e. can migrate after target start - * - some can also be migrated during precopy (RAM) - * - some must be migrated after source stops (block-dirty-bitmap) + * @f: QEMUFile where to send the data + * @opaque: Data pointer passed to register_savevm_live() + * @errp: Error** used to report error message * - * Sum of can_postcopy and must_postcopy is the whole amount of + * Returns: true if succeeded, false if error occured. When false is + * returned, @errp must be set. + */ + bool (*save_postcopy_prepare)(QEMUFile *f, void *opaque, Error **errp); + + /** + * @state_pending_estimate + * + * 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) */ - /* This estimates the remaining data to transfer */ void (*state_pending_estimate)(void *opaque, uint64_t *must_precopy, uint64_t *can_postcopy); - /* This calculate the exact remaining data to transfer */ + + /** + * @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_state_buffer (invoked outside the BQL) + * + * Load device state buffer provided to qemu_loadvm_load_state_buffer(). + * + * @opaque: data pointer passed to register_savevm_live() + * @buf: the data buffer to load + * @len: the data length in buffer + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns true to indicate success and false for errors. + */ + bool (*load_state_buffer)(void *opaque, char *buf, size_t len, + Error **errp); + + /** + * @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. + * Note that this handler can be called even if load_setup + * wasn't called earlier. + * + * @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); - /* Checks if switchover ack should be used. Called only in dest */ + + /** + * @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); + + /** + * @switchover_start + * + * Notifies that the switchover has started. Called only on + * the destination. + * + * @opaque: data pointer passed to register_savevm_live() + * + * Returns zero to indicate success and negative for error + */ + int (*switchover_start)(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 e72083b117a..9e4dcaaa751 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 e4db9103396..1ff7bd9ac42 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -155,7 +155,11 @@ enum VMStateFlags { }; typedef enum { - MIG_PRI_DEFAULT = 0, + MIG_PRI_UNINITIALIZED = 0, /* An uninitialized priority field maps to */ + /* MIG_PRI_DEFAULT in save_state_priority */ + + MIG_PRI_LOW, /* Must happen after default */ + MIG_PRI_DEFAULT, MIG_PRI_IOMMU, /* Must happen before PCI devices */ MIG_PRI_PCI_BUS, /* Must happen before IOMMU */ MIG_PRI_VIRTIO_MEM, /* Must happen before IOMMU */ @@ -209,7 +213,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; @@ -230,6 +234,7 @@ extern const VMStateInfo vmstate_info_uint8; extern const VMStateInfo vmstate_info_uint16; extern const VMStateInfo vmstate_info_uint32; extern const VMStateInfo vmstate_info_uint64; +extern const VMStateInfo vmstate_info_fd; /** Put this in the stream when migrating a null pointer.*/ #define VMS_NULLPTR_MARKER (0x30U) /* '0' */ @@ -388,16 +393,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), \ @@ -912,6 +907,9 @@ extern const VMStateInfo vmstate_info_qlist; #define VMSTATE_UINT64_V(_f, _s, _v) \ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_uint64, uint64_t) +#define VMSTATE_FD_V(_f, _s, _v) \ + VMSTATE_SINGLE(_f, _s, _v, vmstate_info_fd, int32_t) + #ifdef CONFIG_LINUX #define VMSTATE_U8_V(_f, _s, _v) \ @@ -946,6 +944,9 @@ extern const VMStateInfo vmstate_info_qlist; #define VMSTATE_UINT64(_f, _s) \ VMSTATE_UINT64_V(_f, _s, 0) +#define VMSTATE_FD(_f, _s) \ + VMSTATE_FD_V(_f, _s, 0) + #ifdef CONFIG_LINUX #define VMSTATE_U8(_f, _s) \ @@ -1019,6 +1020,8 @@ extern const VMStateInfo vmstate_info_qlist; #define VMSTATE_UINT64_TEST(_f, _s, _t) \ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint64, uint64_t) +#define VMSTATE_FD_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_fd, int32_t) #define VMSTATE_TIMER_PTR_TEST(_f, _s, _test) \ VMSTATE_POINTER_TEST(_f, _s, _test, vmstate_info_timer, QEMUTimer *) @@ -1196,11 +1199,13 @@ 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 @@ -1228,6 +1233,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 d78e979f053..b679aaebbff 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 -#include "cpu.h" - -#define MD_TLONG 0 -#define MD_I32 1 +typedef struct MonitorDef MonitorDef; +#ifdef COMPILING_PER_TARGET +#include "cpu.h" 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); diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index 13f9a2dedb8..ae116d9804a 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -35,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); @@ -103,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); @@ -142,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); @@ -181,5 +177,6 @@ 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 965f5d54500..c3740ec616a 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -51,9 +51,8 @@ int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func, AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, const char *opaque, Error **errp); -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); 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/net/announce.h b/include/net/announce.h index 3d90c83c237..72e7e501f7e 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 7dec37e56c7..188e4cca0b7 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 3b80b6e07f3..14c34f530fe 100644 --- a/include/net/eth.h +++ b/include/net/eth.h @@ -56,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 */ diff --git a/include/net/filter.h b/include/net/filter.h index 27ffc630df4..f15f7932b29 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 1448d00afbc..cdd5b109b0d 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -54,11 +54,10 @@ typedef void (LinkStatusChanged)(NetClientState *); typedef void (NetClientDestructor)(NetClientState *); typedef RxFilterInfo *(QueryRxFilter)(NetClientState *); typedef bool (HasUfo)(NetClientState *); +typedef bool (HasUso)(NetClientState *); typedef bool (HasVnetHdr)(NetClientState *); typedef bool (HasVnetHdrLen)(NetClientState *, int); -typedef bool (GetUsingVnetHdr)(NetClientState *); -typedef void (UsingVnetHdr)(NetClientState *, bool); -typedef void (SetOffload)(NetClientState *, int, int, int, int, int); +typedef void (SetOffload)(NetClientState *, int, int, int, int, int, int, int); typedef int (GetVnetHdrLen)(NetClientState *); typedef void (SetVnetHdrLen)(NetClientState *, int); typedef int (SetVnetLE)(NetClientState *, bool); @@ -73,7 +72,6 @@ typedef struct NetClientInfo { NetClientDriver type; size_t size; NetReceive *receive; - NetReceive *receive_raw; NetReceiveIOV *receive_iov; NetCanReceive *can_receive; NetStart *start; @@ -84,12 +82,10 @@ typedef struct NetClientInfo { QueryRxFilter *query_rx_filter; NetPoll *poll; HasUfo *has_ufo; + HasUso *has_uso; HasVnetHdr *has_vnet_hdr; HasVnetHdrLen *has_vnet_hdr_len; - GetUsingVnetHdr *get_using_vnet_hdr; - UsingVnetHdr *using_vnet_hdr; SetOffload *set_offload; - GetVnetHdrLen *get_vnet_hdr_len; SetVnetHdrLen *set_vnet_hdr_len; SetVnetLE *set_vnet_le; SetVnetBE *set_vnet_be; @@ -124,6 +120,7 @@ typedef QTAILQ_HEAD(NetClientStateList, NetClientState) NetClientStateList; typedef struct NICState { NetClientState *ncs; NICConf *conf; + MemReentrancyGuard *reentrancy_guard; void *opaque; bool peer_deleted; } NICState; @@ -157,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); @@ -174,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); @@ -187,22 +182,79 @@ void qemu_set_info_str(NetClientState *nc, const char *fmt, ...) G_GNUC_PRINTF(2, 3); void qemu_format_nic_info_str(NetClientState *nc, uint8_t macaddr[6]); bool qemu_has_ufo(NetClientState *nc); +bool qemu_has_uso(NetClientState *nc); bool qemu_has_vnet_hdr(NetClientState *nc); bool qemu_has_vnet_hdr_len(NetClientState *nc, int len); -bool qemu_get_using_vnet_hdr(NetClientState *nc); -void qemu_using_vnet_hdr(NetClientState *nc, bool enable); void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6, - int ecn, int ufo); + int ecn, int ufo, int uso4, int uso6); int qemu_get_vnet_hdr_len(NetClientState *nc); void qemu_set_vnet_hdr_len(NetClientState *nc, int len); int qemu_set_vnet_le(NetClientState *nc, bool is_le); int qemu_set_vnet_be(NetClientState *nc, bool is_be); void qemu_macaddr_default_if_unset(MACAddr *macaddr); -int qemu_show_nic_models(const char *arg, const char *const *models); -void qemu_check_nic_model(NICInfo *nd, const char *model); -int qemu_find_nic_model(NICInfo *nd, const char * const *models, - const char *default_model); +/** + * qemu_find_nic_info: Obtain NIC configuration information + * @typename: Name of device object type + * @match_default: Match NIC configurations with no model specified + * @alias: Additional model string to match (for user convenience and + * backward compatibility). + * + * Search for a NIC configuration matching the NIC model constraints. + */ +NICInfo *qemu_find_nic_info(const char *typename, bool match_default, + const char *alias); +/** + * qemu_configure_nic_device: Apply NIC configuration to a given device + * @dev: Network device to be configured + * @match_default: Match NIC configurations with no model specified + * @alias: Additional model string to match + * + * Search for a NIC configuration for the provided device, using the + * additionally specified matching constraints. If found, apply the + * configuration using qdev_set_nic_properties() and return %true. + * + * This is used by platform code which creates the device anyway, + * regardless of whether there is a configuration for it. This tends + * to be platforms which ignore `--nodefaults` and create net devices + * anyway, for example because the Ethernet device on that board is + * always physically present. + */ +bool qemu_configure_nic_device(DeviceState *dev, bool match_default, + const char *alias); +/** + * qemu_create_nic_device: Create a NIC device if a configuration exists for it + * @typename: Object typename of network device + * @match_default: Match NIC configurations with no model specified + * @alias: Additional model string to match + * + * Search for a NIC configuration for the provided device type. If found, + * create an object of the corresponding type and return it. + */ +DeviceState *qemu_create_nic_device(const char *typename, bool match_default, + const char *alias); + +/* + * qemu_create_nic_bus_devices: Create configured NIC devices for a given bus + * @bus: Bus on which to create devices + * @parent_type: Object type for devices to be created (e.g. TYPE_PCI_DEVICE) + * @default_model: Object type name for default NIC model (or %NULL) + * @alias: Additional model string to replace, for user convenience + * @alias_target: Actual object type name to be used in place of @alias + * + * Instantiate dynamic NICs on a given bus, typically a PCI bus. This scans + * for available NIC configurations which either specify a model which is + * a child type of @parent_type, or which do not specify a model when + * @default_model is non-NULL. Each device is instantiated on the given @bus. + * + * A single substitution is supported, e.g. "xen" → "xen-net-device" for the + * Xen bus, or "virtio" → "virtio-net-pci" for PCI. This allows the user to + * specify a more understandable "model=" parameter on the command line, not + * only the real object typename. + */ +void qemu_create_nic_bus_devices(BusState *bus, const char *parent_type, + const char *default_model, + const char *alias, const char *alias_target); void print_net_client(Monitor *mon, NetClientState *nc); void net_socket_rs_init(SocketReadState *rs, SocketReadStateFinalize *finalize, @@ -238,15 +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 */ extern NetClientStateList net_clients; -bool netdev_is_modern(const char *optarg); -void netdev_parse_modern(const char *optarg); -void net_client_parse(QemuOptsList *opts_list, const char *str); +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); @@ -256,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 9f2f289d771..2e686b1b61d 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_net.h b/include/net/vhost_net.h index c37aba35e65..c6a5361a2ae 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; diff --git a/include/qapi/clone-visitor.h b/include/qapi/clone-visitor.h index adf9a788e23..ebc182b034d 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/compat-policy.h b/include/qapi/compat-policy.h index 8b7b25c0b5a..ea65e10744a 100644 --- a/include/qapi/compat-policy.h +++ b/include/qapi/compat-policy.h @@ -18,7 +18,7 @@ extern CompatPolicy compat_policy; -bool compat_policy_input_ok(unsigned special_features, +bool compat_policy_input_ok(uint64_t features, const CompatPolicy *policy, ErrorClass error_class, const char *kind, const char *name, diff --git a/include/qapi/error-internal.h b/include/qapi/error-internal.h new file mode 100644 index 00000000000..ff18a2086d2 --- /dev/null +++ b/include/qapi/error-internal.h @@ -0,0 +1,35 @@ +/* + * QEMU Error Objects - struct definition + * + * Copyright IBM, Corp. 2011 + * Copyright (C) 2011-2015 Red Hat, Inc. + * + * Authors: + * Anthony Liguori + * Markus Armbruster , + * + * This work is licensed under the terms of the GNU LGPL, version 2. See + * the COPYING.LIB file in the top-level directory. + */ + +#ifndef QAPI_ERROR_INTERNAL_H + +struct Error +{ + char *msg; + ErrorClass err_class; + + /* Used for error_abort only, may be NULL. */ + const char *func; + + /* + * src might be NUL-terminated or not. If it is, src_len is negative. + * If it is not, src_len is the length. + */ + const char *src; + int src_len; + int line; + GString *hint; +}; + +#endif diff --git a/include/qapi/error.h b/include/qapi/error.h index f21a231bb1a..41e38163804 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. * @@ -437,6 +437,8 @@ Error *error_copy(const Error *err); */ void error_free(Error *err); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(Error, error_free) + /* * Convenience function to assert that *@errp is set, then silently free it. */ @@ -466,6 +468,18 @@ void warn_reportf_err(Error *err, const char *fmt, ...) void error_reportf_err(Error *err, const char *fmt, ...) G_GNUC_PRINTF(2, 3); +/* + * Similar to warn_report_err(), except it prints the message just once. + * Return true when it prints, false otherwise. + */ +bool warn_report_err_once_cond(bool *printed, Error *err); + +#define warn_report_err_once(err) \ + ({ \ + static bool print_once_; \ + warn_report_err_once_cond(&print_once_, err); \ + }) + /* * Just like error_setg(), except you get to specify the error class. * Note: use of error classes other than ERROR_CLASS_GENERIC_ERROR is diff --git a/include/qapi/qmp-registry.h b/include/qapi/qmp-registry.h new file mode 100644 index 00000000000..e0ee1ad3ac6 --- /dev/null +++ b/include/qapi/qmp-registry.h @@ -0,0 +1,67 @@ +/* + * Core Definitions for QAPI/QMP Dispatch + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef QAPI_QMP_DISPATCH_H +#define QAPI_QMP_DISPATCH_H + +#include "monitor/monitor.h" +#include "qemu/queue.h" + +typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); + +typedef enum QmpCommandOptions +{ + QCO_NO_SUCCESS_RESP = (1U << 0), + QCO_ALLOW_OOB = (1U << 1), + QCO_ALLOW_PRECONFIG = (1U << 2), + QCO_COROUTINE = (1U << 3), +} QmpCommandOptions; + +typedef struct QmpCommand +{ + const char *name; + /* Runs in coroutine context if QCO_COROUTINE is set */ + QmpCommandFunc *fn; + QmpCommandOptions options; + uint64_t features; + QTAILQ_ENTRY(QmpCommand) node; + bool enabled; + const char *disable_reason; +} QmpCommand; + +typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList; + +void qmp_register_command(QmpCommandList *cmds, const char *name, + QmpCommandFunc *fn, QmpCommandOptions options, + uint64_t features); +const QmpCommand *qmp_find_command(const QmpCommandList *cmds, + const char *name); +void qmp_disable_command(QmpCommandList *cmds, const char *name, + const char *err_msg); +void qmp_enable_command(QmpCommandList *cmds, const char *name); + +bool qmp_command_is_enabled(const QmpCommand *cmd); +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 *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); + +void qmp_for_each_command(const QmpCommandList *cmds, qmp_cmd_callback_fn fn, + void *opaque); + +#endif diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h deleted file mode 100644 index f2e956813a1..00000000000 --- a/include/qapi/qmp/dispatch.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Core Definitions for QAPI/QMP Dispatch - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#ifndef QAPI_QMP_DISPATCH_H -#define QAPI_QMP_DISPATCH_H - -#include "monitor/monitor.h" -#include "qemu/queue.h" - -typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); - -typedef enum QmpCommandOptions -{ - QCO_NO_SUCCESS_RESP = (1U << 0), - QCO_ALLOW_OOB = (1U << 1), - QCO_ALLOW_PRECONFIG = (1U << 2), - QCO_COROUTINE = (1U << 3), -} QmpCommandOptions; - -typedef struct QmpCommand -{ - const char *name; - /* Runs in coroutine context if QCO_COROUTINE is set */ - QmpCommandFunc *fn; - QmpCommandOptions options; - unsigned special_features; - QTAILQ_ENTRY(QmpCommand) node; - bool enabled; - const char *disable_reason; -} QmpCommand; - -typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList; - -void qmp_register_command(QmpCommandList *cmds, const char *name, - QmpCommandFunc *fn, QmpCommandOptions options, - unsigned special_features); -const QmpCommand *qmp_find_command(const QmpCommandList *cmds, - const char *name); -void qmp_disable_command(QmpCommandList *cmds, const char *name, - const char *err_msg); -void qmp_enable_command(QmpCommandList *cmds, const char *name); - -bool qmp_command_is_enabled(const QmpCommand *cmd); -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 *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); - -void qmp_for_each_command(const QmpCommandList *cmds, qmp_cmd_callback_fn fn, - void *opaque); - -#endif diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 8dd9fcb071a..d1db6f18cd6 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -17,46 +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_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_UNSUPPORTED \ - "this feature or command is not currently supported" - #endif /* QERROR_H */ diff --git a/include/qapi/string-output-visitor.h b/include/qapi/string-output-visitor.h index 268dfe9986b..b1ee473b306 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 be1f1815264..fc8352cdec0 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 81a2b13a333..29bc4eb865a 100644 --- a/include/qapi/util.h +++ b/include/qapi/util.h @@ -18,7 +18,7 @@ typedef enum { typedef struct QEnumLookup { const char *const *array; - const unsigned char *const special_features; + const uint64_t *const features; const int size; } QEnumLookup; @@ -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-impl.h b/include/qapi/visitor-impl.h index 2badec5ba46..7beb0dbfa5d 100644 --- a/include/qapi/visitor-impl.h +++ b/include/qapi/visitor-impl.h @@ -115,11 +115,11 @@ struct Visitor /* Optional */ bool (*policy_reject)(Visitor *v, const char *name, - unsigned special_features, Error **errp); + uint64_t features, Error **errp); /* Optional */ bool (*policy_skip)(Visitor *v, const char *name, - unsigned special_features); + uint64_t features); /* Must be set */ VisitorType type; diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h index d53a84c9ba4..f6a9b0743f7 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: @@ -463,29 +463,29 @@ bool visit_optional(Visitor *v, const char *name, bool *present); /* * Should we reject member @name due to policy? * - * @special_features is the member's special features encoded as a - * bitset of QapiSpecialFeature. + * @features is the member's special features encoded as a + * bitset of QapiFeature. * * @name must not be NULL. This function is only useful between * visit_start_struct() and visit_end_struct(), since only objects * have deprecated members. */ bool visit_policy_reject(Visitor *v, const char *name, - unsigned special_features, Error **errp); + uint64_t features, Error **errp); /* * * Should we skip member @name due to policy? * - * @special_features is the member's special features encoded as a - * bitset of QapiSpecialFeature. + * @features is the member's special features encoded as a + * bitset of QapiFeature. * * @name must not be NULL. This function is only useful between * visit_start_struct() and visit_end_struct(), since only objects * have deprecated members. */ bool visit_policy_skip(Visitor *v, const char *name, - unsigned special_features); + uint64_t features); /* * Set policy for handling deprecated management interfaces. diff --git a/include/qemu-main.h b/include/qemu-main.h index 940960a7dbc..2ee83bedff3 100644 --- a/include/qemu-main.h +++ b/include/qemu-main.h @@ -5,7 +5,19 @@ #ifndef QEMU_MAIN_H #define QEMU_MAIN_H -int qemu_default_main(void); +/* + * The function to run on the main (initial) thread of the process. + * NULL means QEMU's main event loop. + * When non-NULL, QEMU's main event loop will run on a purposely created + * thread, after which the provided function pointer will be invoked on + * the initial thread. + * This is useful on platforms which treat the main thread as special + * (macOS/Darwin) and/or require all UI API calls to occur from the main + * thread. Those platforms can initialise it to a specific function, + * while UI implementations may reset it to NULL during their init if they + * will handle system and UI events on the main thread via QEMU's own main + * event loop. + */ extern int (*qemu_main)(void); #endif /* QEMU_MAIN_H */ diff --git a/include/qemu/accel.h b/include/qemu/accel.h index e84db2e3e50..fbd3d897fef 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -38,11 +38,13 @@ typedef struct AccelClass { const char *name; int (*init_machine)(MachineState *ms); -#ifndef CONFIG_USER_ONLY + bool (*cpu_common_realize)(CPUState *cpu, Error **errp); + void (*cpu_common_unrealize)(CPUState *cpu); + + /* system related hooks */ void (*setup_post)(MachineState *ms, AccelState *accel); bool (*has_memory)(MachineState *ms, AddressSpace *as, hwaddr start_addr, hwaddr size); -#endif /* gdbstub related hooks */ int (*gdbstub_supported_sstep_flags)(void); @@ -76,12 +78,10 @@ const char *current_accel_name(void); void accel_init_interfaces(AccelClass *ac); -#ifndef CONFIG_USER_ONLY int accel_init_machine(AccelState *accel, MachineState *ms); /* Called just before os_setup_post (ie just before drop OS privs) */ void accel_setup_post(MachineState *ms); -#endif /* !CONFIG_USER_ONLY */ /** * accel_cpu_instance_init: @@ -90,11 +90,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/atomic.h b/include/qemu/atomic.h index d95612f7a08..f80cba24cf7 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 @@ -98,25 +56,13 @@ */ #define signal_barrier() __atomic_signal_fence(__ATOMIC_SEQ_CST) -/* Sanity check that the size of an atomic operation isn't "overly large". +/* + * Sanity check that the size of an atomic operation isn't "overly large". * Despite the fact that e.g. i686 has 64-bit atomic operations, we do not * want to use them because we ought not need them, and this lets us do a * bit of sanity checking that other 32-bit hosts might build. - * - * That said, we have a problem on 64-bit ILP32 hosts in that in order to - * sync with TCG_OVERSIZED_GUEST, this must match TCG_TARGET_REG_BITS. - * We'd prefer not want to pull in everything else TCG related, so handle - * those few cases by hand. - * - * Note that x32 is fully detected with __x86_64__ + _ILP32, and that for - * Sparc we always force the use of sparcv9 in configure. MIPS n32 (ILP32) & - * n64 (LP64) ABIs are both detected using __mips64. */ -#if defined(__x86_64__) || defined(__sparc__) || defined(__mips64) -# define ATOMIC_REG_SIZE 8 -#else -# define ATOMIC_REG_SIZE sizeof(void *) -#endif +#define ATOMIC_REG_SIZE sizeof(void *) /* Weak atomic operations prevent the compiler moving other * loads/stores past the atomic operation load/store. However there is @@ -157,13 +103,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 +148,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, \ diff --git a/include/qemu/atomic128.h b/include/qemu/atomic128.h index 34554bf0acc..31e5c48d8fa 100644 --- a/include/qemu/atomic128.h +++ b/include/qemu/atomic128.h @@ -13,6 +13,7 @@ #ifndef QEMU_ATOMIC128_H #define QEMU_ATOMIC128_H +#include "qemu/atomic.h" #include "qemu/int128.h" /* @@ -43,8 +44,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 @@ -58,7 +59,7 @@ * Therefore, special case each platform. */ -#include "host/atomic128-cas.h" -#include "host/atomic128-ldst.h" +#include "host/atomic128-cas.h.inc" +#include "host/atomic128-ldst.h.inc" #endif /* QEMU_ATOMIC128_H */ diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h index 97806811eeb..0044333cb51 100644 --- a/include/qemu/bitmap.h +++ b/include/qemu/bitmap.h @@ -69,6 +69,14 @@ #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) \ ((nbits) <= BITS_PER_LONG) @@ -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 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 = 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 cb3526d1f44..c7b838a6283 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 diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index 933a66ee87e..9a117645360 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -38,12 +38,14 @@ static inline void bswap64s(uint64_t *s) #if HOST_BIG_ENDIAN #define be_bswap(v, 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(__builtin_bswap, size)(*p); } while (0) #else #define le_bswap(v, 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) @@ -138,6 +140,8 @@ 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,64}, except that gcc will figure the result is * a compile-time constant if you pass in a constant. So this can be @@ -145,14 +149,14 @@ CPU_CONVERT(le, 64, uint64_t) */ #if HOST_BIG_ENDIAN # define const_le64(_x) \ - ((((_x) & 0x00000000000000ffU) << 56) | \ - (((_x) & 0x000000000000ff00U) << 40) | \ - (((_x) & 0x0000000000ff0000U) << 24) | \ - (((_x) & 0x00000000ff000000U) << 8) | \ - (((_x) & 0x000000ff00000000U) >> 8) | \ - (((_x) & 0x0000ff0000000000U) >> 24) | \ - (((_x) & 0x00ff000000000000U) >> 40) | \ - (((_x) & 0xff00000000000000U) >> 56)) + ((((_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) | \ @@ -201,9 +205,6 @@ CPU_CONVERT(le, 64, uint64_t) * te : target endian * (except for byte accesses, which have no endian infix). * - * The target endian accessors are obviously only available to source - * files which are built per-target; they are defined in cpu-all.h. - * * In all cases these functions take a host pointer. * For accessors that take a guest address rather than a * host address, see the cpu_{ld,st}_* accessors defined in @@ -357,6 +358,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)); diff --git a/include/qemu/cacheflush.h b/include/qemu/cacheflush.h index ae20bcda733..76eb55d818d 100644 --- a/include/qemu/cacheflush.h +++ b/include/qemu/cacheflush.h @@ -26,6 +26,13 @@ static inline void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len) /* icache is coherent and does not require flushing. */ } +#elif defined(EMSCRIPTEN) + +static inline void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len) +{ + /* Wasm doesn't have executable region of memory. */ +} + #else void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len); diff --git a/include/qemu/chardev_open.h b/include/qemu/chardev_open.h new file mode 100644 index 00000000000..64e8fcfdcb2 --- /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 deleted file mode 100644 index ba06fb8c924..00000000000 --- a/include/qemu/clang-tsa.h +++ /dev/null @@ -1,114 +0,0 @@ -#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 78ca5850f8f..41be1a81312 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 a309f90c768..65b89958d36 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -22,12 +22,7 @@ #define QEMU_EXTERN_C extern #endif -#if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) -# define QEMU_PACKED __attribute__((gcc_struct, packed)) -#else -# define QEMU_PACKED __attribute__((packed)) -#endif - +#define QEMU_PACKED __attribute__((packed)) #define QEMU_ALIGNED(X) __attribute__((aligned(X))) #ifndef glue @@ -37,6 +32,9 @@ #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) @@ -197,4 +195,193 @@ #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 + +/* + * Disable -ftrivial-auto-var-init on a local variable. + * + * Use this in cases where there a method in the device I/O path (or other + * important hot paths), that has large variables on the stack. A rule of + * thumb is that "large" means a method with 4kb data in the local stack + * frame. Any variables which are KB in size, should be annotated with this + * attribute, to pre-emptively eliminate any potential overhead from the + * compiler's implicit zero'ing of memory. + * + * Given that this turns off a security hardening feature, when using this + * to flag variables, it is important that the code is double-checked to + * ensure there is no possible use of uninitialized data in the method. + */ +#if __has_attribute(uninitialized) +# define QEMU_UNINITIALIZED __attribute__((uninitialized)) +#else +# define QEMU_UNINITIALIZED +#endif + +/* + * 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__)) + +/* + * 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 b82a778123f..51b310fa3b4 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); diff --git a/include/qemu/coroutine-core.h b/include/qemu/coroutine-core.h index 230bb565177..503bad6e0e6 100644 --- a/include/qemu/coroutine-core.h +++ b/include/qemu/coroutine-core.h @@ -22,7 +22,7 @@ * 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. + * 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 diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index a65be6697f5..e545bbf620f 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -16,6 +16,7 @@ #define QEMU_COROUTINE_H #include "qemu/coroutine-core.h" +#include "qemu/atomic.h" #include "qemu/queue.h" #include "qemu/timer.h" @@ -26,7 +27,7 @@ * 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. + * 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 @@ -84,6 +85,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 @@ -281,8 +284,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 35325f1995f..b11161555b6 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 diff --git a/include/qemu/crc-ccitt.h b/include/qemu/crc-ccitt.h index 8918dafe078..ce28e297209 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 diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 92c927a6a35..36c68ce86c5 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -187,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 @@ -211,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. @@ -252,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 @@ -267,4 +302,19 @@ void qemu_hexdump_line(char *line, unsigned int b, const void *bufptr, void qemu_hexdump(FILE *fp, const char *prefix, const void *bufptr, size_t size); +/** + * qemu_hexdump_to_buffer: + * @buffer: output string buffer + * @buffer_size: amount of available space in buffer. Must be at least + * data_size*2+1. + * @data: input bytes + * @data_size: number of bytes in data + * + * Converts the @data_size bytes in @data into hex digit pairs, writing them to + * @buffer. Finally, a nul terminating character is written; @buffer therefore + * needs space for (data_size*2+1) chars. + */ +void qemu_hexdump_to_buffer(char *restrict buffer, size_t buffer_size, + const uint8_t *restrict data, size_t data_size); + #endif diff --git a/include/qemu/datadir.h b/include/qemu/datadir.h index 21f9097f582..cca32af3008 100644 --- a/include/qemu/datadir.h +++ b/include/qemu/datadir.h @@ -1,11 +1,16 @@ #ifndef QEMU_DATADIR_H #define QEMU_DATADIR_H -#define QEMU_FILE_TYPE_BIOS 0 -#define QEMU_FILE_TYPE_KEYMAP 1 +typedef enum { + QEMU_FILE_TYPE_BIOS, + QEMU_FILE_TYPE_DTB, + QEMU_FILE_TYPE_KEYMAP, +} QemuFileType; + /** * qemu_find_file: * @type: QEMU_FILE_TYPE_BIOS (for BIOS, VGA BIOS) + * QEMU_FILE_TYPE_DTB (for device tree blobs) * or QEMU_FILE_TYPE_KEYMAP (for keymaps). * @name: Relative or absolute file name * @@ -20,7 +25,7 @@ * * Returns: a path that can access @name, or NULL if no matching file exists. */ -char *qemu_find_file(int type, const char *name); +char *qemu_find_file(QemuFileType type, const char *name); void qemu_add_default_firmwarepath(void); void qemu_add_data_dir(char *path); void qemu_list_data_dirs(void); diff --git a/include/qemu/defer-call.h b/include/qemu/defer-call.h new file mode 100644 index 00000000000..e2c1d245728 --- /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 6006dfae44c..b2883f66599 100644 --- a/include/qemu/envlist.h +++ b/include/qemu/envlist.h @@ -7,8 +7,6 @@ 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 *); #endif /* ENVLIST_H */ diff --git a/include/qemu/fifo8.h b/include/qemu/fifo8.h index 16be02f361f..4f768d4ee38 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,7 +37,6 @@ 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); /** @@ -52,7 +49,6 @@ void fifo8_push(Fifo8 *fifo, uint8_t data); * 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 + * @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 @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 - * @num: actual number of returned bytes + * @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 + * 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/futex.h b/include/qemu/futex.h index 91ae88966e1..607613eec83 100644 --- a/include/qemu/futex.h +++ b/include/qemu/futex.h @@ -1,5 +1,5 @@ /* - * Wrappers around Linux futex syscall + * Wrappers around Linux futex syscall and similar * * Copyright Red Hat, Inc. 2017 * @@ -11,17 +11,35 @@ * */ +/* + * Note that a wake-up can also be caused by common futex usage patterns in + * unrelated code that happened to have previously used the futex word's + * memory location (e.g., typical futex-based implementations of Pthreads + * mutexes can cause this under some conditions). Therefore, qemu_futex_wait() + * callers should always conservatively assume that it is a spurious wake-up, + * and use the futex word's value (i.e., the user-space synchronization scheme) + * to decide whether to continue to block or not. + */ + #ifndef QEMU_FUTEX_H #define QEMU_FUTEX_H +#define HAVE_FUTEX + +#ifdef CONFIG_LINUX #include #include #define qemu_futex(...) syscall(__NR_futex, __VA_ARGS__) -static inline void qemu_futex_wake(void *f, int n) +static inline void qemu_futex_wake_all(void *f) { - qemu_futex(f, FUTEX_WAKE, n, NULL, NULL, 0); + qemu_futex(f, FUTEX_WAKE, INT_MAX, NULL, NULL, 0); +} + +static inline void qemu_futex_wake_single(void *f) +{ + qemu_futex(f, FUTEX_WAKE, 1, NULL, NULL, 0); } static inline void qemu_futex_wait(void *f, unsigned val) @@ -37,5 +55,25 @@ static inline void qemu_futex_wait(void *f, unsigned val) } } } +#elif defined(CONFIG_WIN32) +#include + +static inline void qemu_futex_wake_all(void *f) +{ + WakeByAddressAll(f); +} + +static inline void qemu_futex_wake_single(void *f) +{ + WakeByAddressSingle(f); +} + +static inline void qemu_futex_wait(void *f, unsigned val) +{ + WaitOnAddress(f, &val, sizeof(val), INFINITE); +} +#else +#undef HAVE_FUTEX +#endif #endif /* QEMU_FUTEX_H */ diff --git a/include/qemu/guest-random.h b/include/qemu/guest-random.h index 09ff9c22369..5060d49d609 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/help-texts.h b/include/qemu/help-texts.h index d0359f82e08..bc8fab91697 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-2023 " \ +#define QEMU_COPYRIGHT "Copyright (c) 2003-2025 " \ "Fabrice Bellard and the QEMU Project developers" /* Bug reporting information for --help arguments, About dialogs, etc */ diff --git a/include/qemu/host-pci-mmio.h b/include/qemu/host-pci-mmio.h new file mode 100644 index 00000000000..a8ed9938ac5 --- /dev/null +++ b/include/qemu/host-pci-mmio.h @@ -0,0 +1,136 @@ +/* + * API for host PCI MMIO accesses (e.g. Linux VFIO BARs) + * + * Copyright 2025 IBM Corp. + * Author(s): Farhan Ali + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HOST_PCI_MMIO_H +#define HOST_PCI_MMIO_H + +#include "qemu/bswap.h" +#include "qemu/s390x_pci_mmio.h" + +static inline uint8_t host_pci_ldub_p(const void *ioaddr) +{ + uint8_t ret = 0; +#ifdef __s390x__ + ret = s390x_pci_mmio_read_8(ioaddr); +#else + ret = ldub_p(ioaddr); +#endif + + return ret; +} + +static inline uint16_t host_pci_lduw_le_p(const void *ioaddr) +{ + uint16_t ret = 0; +#ifdef __s390x__ + ret = le16_to_cpu(s390x_pci_mmio_read_16(ioaddr)); +#else + ret = lduw_le_p(ioaddr); +#endif + + return ret; +} + +static inline uint32_t host_pci_ldl_le_p(const void *ioaddr) +{ + uint32_t ret = 0; +#ifdef __s390x__ + ret = le32_to_cpu(s390x_pci_mmio_read_32(ioaddr)); +#else + ret = ldl_le_p(ioaddr); +#endif + + return ret; +} + +static inline uint64_t host_pci_ldq_le_p(const void *ioaddr) +{ + uint64_t ret = 0; +#ifdef __s390x__ + ret = le64_to_cpu(s390x_pci_mmio_read_64(ioaddr)); +#else + ret = ldq_le_p(ioaddr); +#endif + + return ret; +} + +static inline void host_pci_stb_p(void *ioaddr, uint8_t val) +{ +#ifdef __s390x__ + s390x_pci_mmio_write_8(ioaddr, val); +#else + stb_p(ioaddr, val); +#endif +} + +static inline void host_pci_stw_le_p(void *ioaddr, uint16_t val) +{ +#ifdef __s390x__ + s390x_pci_mmio_write_16(ioaddr, cpu_to_le16(val)); +#else + stw_le_p(ioaddr, val); +#endif +} + +static inline void host_pci_stl_le_p(void *ioaddr, uint32_t val) +{ +#ifdef __s390x__ + s390x_pci_mmio_write_32(ioaddr, cpu_to_le32(val)); +#else + stl_le_p(ioaddr, val); +#endif +} + +static inline void host_pci_stq_le_p(void *ioaddr, uint64_t val) +{ +#ifdef __s390x__ + s390x_pci_mmio_write_64(ioaddr, cpu_to_le64(val)); +#else + stq_le_p(ioaddr, val); +#endif +} + +static inline uint64_t host_pci_ldn_le_p(const void *ioaddr, int sz) +{ + switch (sz) { + case 1: + return host_pci_ldub_p(ioaddr); + case 2: + return host_pci_lduw_le_p(ioaddr); + case 4: + return host_pci_ldl_le_p(ioaddr); + case 8: + return host_pci_ldq_le_p(ioaddr); + default: + g_assert_not_reached(); + } +} + +static inline void host_pci_stn_le_p(void *ioaddr, int sz, uint64_t v) +{ + switch (sz) { + case 1: + host_pci_stb_p(ioaddr, v); + break; + case 2: + host_pci_stw_le_p(ioaddr, v); + break; + case 4: + host_pci_stl_le_p(ioaddr, v); + break; + case 8: + host_pci_stq_le_p(ioaddr, v); + break; + default: + g_assert_not_reached(); + } +} + +#endif diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index ead97d354d6..4d28fa22cfa 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -313,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 73624e8be7c..174bd7dafb8 100644 --- a/include/qemu/int128.h +++ b/include/qemu/int128.h @@ -10,6 +10,7 @@ */ #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) { @@ -224,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 diff --git a/include/qemu/iov.h b/include/qemu/iov.h index 63a1c01965d..9535673c13d 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): @@ -30,7 +31,7 @@ size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt); * only part of data will be copied, up to the end of the iovec. * Number of bytes actually copied will be returned, which is * min(bytes, iov_size(iov)-offset) - * `Offset' must point to the inside of iovec. + * Returns 0 when `offset' points to the outside of iovec. */ size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt, size_t offset, const void *buf, size_t bytes); @@ -66,15 +67,42 @@ iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, /** * Set data bytes pointed out by iovec `iov' of size `iov_cnt' elements, * starting at byte offset `start', to value `fillc', repeating it - * `bytes' number of times. `Offset' must point to the inside of iovec. + * `bytes' number of times. * If `bytes' is large enough, only last bytes portion of iovec, * up to the end of it, will be filled with the specified value. * Function return actual number of bytes processed, which is * min(size, iov_size(iov) - offset). + * Returns 0 when `offset' points to the outside of iovec. */ 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 * diff --git a/include/qemu/iova-tree.h b/include/qemu/iova-tree.h index 2a10a7052e4..14e82a22d58 100644 --- a/include/qemu/iova-tree.h +++ b/include/qemu/iova-tree.h @@ -23,7 +23,7 @@ * for the thread safety issue. */ -#include "exec/memory.h" +#include "system/memory.h" #include "exec/hwaddr.h" #define IOVA_OK (0) @@ -40,6 +40,28 @@ typedef struct DMAMap { } QEMU_PACKED DMAMap; typedef gboolean (*iova_tree_iterator)(DMAMap *map); +/** + * gpa_tree_new: + * + * Create a new GPA->IOVA tree. + * + * Returns: the tree point on success, or NULL otherwise. + */ +IOVATree *gpa_tree_new(void); + +/** + * gpa_tree_insert: + * + * @tree: The GPA->IOVA tree we're inserting the mapping to + * @map: The GPA->IOVA mapping to insert + * + * Inserts a GPA range to the GPA->IOVA tree. If there are overlapped + * ranges, IOVA_ERR_OVERLAP will be returned. + * + * Return: 0 if successful, < 0 otherwise. + */ +int gpa_tree_insert(IOVATree *tree, const DMAMap *map); + /** * iova_tree_new: * @@ -111,31 +133,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 iterator 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 e502787dd87..a5a04155ea5 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. @@ -565,6 +545,9 @@ bool job_is_ready(Job *job); /* Same as job_is_ready(), but called with job lock held. */ bool job_is_ready_locked(Job *job); +/** Returns whether the job is paused. Called with job_mutex *not* held. */ +bool job_is_paused(Job *job); + /** * Request @job to pause at the next pause point. Must be paired with * job_resume(). If the job is supposed to be resumed by user action, call diff --git a/include/qemu/lockable.h b/include/qemu/lockable.h index 9823220446d..66713bd4292 100644 --- a/include/qemu/lockable.h +++ b/include/qemu/lockable.h @@ -18,11 +18,11 @@ 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 00000000000..5a2800e3f18 --- /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 HAVE_FUTEX + 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 df59bfabcd5..60da703e670 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -36,6 +36,8 @@ bool qemu_log_separate(void); #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) +#define LOG_INVALID_MEM (1 << 23) /* Lock/unlock output. */ diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 68e70e61aa5..4e2436b1968 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -27,7 +27,7 @@ #include "block/aio.h" #include "qom/object.h" -#include "sysemu/event-loop-base.h" +#include "system/event-loop-base.h" #define SIG_IPI SIGUSR1 @@ -248,19 +248,42 @@ GSource *iohandler_get_g_source(void); AioContext *iohandler_get_aio_context(void); /** - * qemu_mutex_iothread_locked: Return lock status of the main loop mutex. + * rust_bql_mock_lock: * - * The main loop mutex is the coarsest lock in QEMU, and as such it + * Called from Rust doctests to make bql_lock() return true. + * Do not touch. + */ +void rust_bql_mock_lock(void); + +/** + * bql_locked: Return lock status of the Big QEMU Lock (BQL) + * + * 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); + +/** + * bql_block: Allow/deny releasing the BQL + * + * The Big QEMU Lock (BQL) is used to provide interior mutability to + * Rust code, but this only works if other threads cannot run while + * the Rust code has an active borrow. This is because C code in + * other threads could come in and mutate data under the Rust code's + * feet. + * + * @increase: Whether to increase or decrease the blocking counter. + * Releasing the BQL while the counter is nonzero triggers + * an assertion failure. + */ +void bql_block_unlock(bool increase); /** * qemu_in_main_thread: return whether it's possible to safely access @@ -312,78 +335,76 @@ 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); /** - * QEMU_IOTHREAD_LOCK_GUARD + * BQL_LOCK_GUARD * - * Wrap a block of code in a conditional qemu_mutex_{lock,unlock}_iothread. + * Wrap a block of code in a conditional bql_{lock,unlock}. */ -typedef struct IOThreadLockAuto IOThreadLockAuto; +typedef struct BQLLockAuto BQLLockAuto; -static inline IOThreadLockAuto *qemu_iothread_auto_lock(const char *file, - int line) +static inline BQLLockAuto *bql_auto_lock(const char *file, int line) { - if (qemu_mutex_iothread_locked()) { + if (bql_locked()) { return NULL; } - qemu_mutex_lock_iothread_impl(file, line); + bql_lock_impl(file, line); /* Anything non-NULL causes the cleanup function to be called */ - return (IOThreadLockAuto *)(uintptr_t)1; + return (BQLLockAuto *)(uintptr_t)1; } -static inline void qemu_iothread_auto_unlock(IOThreadLockAuto *l) +static inline void bql_auto_unlock(BQLLockAuto *l) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } -G_DEFINE_AUTOPTR_CLEANUP_FUNC(IOThreadLockAuto, qemu_iothread_auto_unlock) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(BQLLockAuto, bql_auto_unlock) -#define QEMU_IOTHREAD_LOCK_GUARD() \ - g_autoptr(IOThreadLockAuto) _iothread_lock_auto __attribute__((unused)) \ - = qemu_iothread_auto_lock(__FILE__, __LINE__) +#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 */ diff --git a/include/qemu/notify.h b/include/qemu/notify.h index bcfa70fb2ed..abf18dbf595 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 b3498287823..01e673ae03f 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 2897720facd..96fe51bc390 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -8,7 +8,7 @@ * To avoid getting into possible circular include dependencies, this * file should not include any other QEMU headers, with the exceptions * of config-host.h, config-target.h, qemu/compiler.h, - * sysemu/os-posix.h, sysemu/os-win32.h, glib-compat.h and + * system/os-posix.h, system/os-win32.h, system/os-wasm.h, glib-compat.h and * qemu/typedefs.h, all of which are doing a similar job to this file * and are under similar constraints. * @@ -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" @@ -124,7 +128,7 @@ QEMU_EXTERN_C int daemon(int, int); #include #include #include -/* setjmp must be declared before sysemu/os-win32.h +/* setjmp must be declared before system/os-win32.h * because it is redefined there. */ #include #include @@ -157,11 +161,15 @@ QEMU_EXTERN_C int daemon(int, int); #include "glib-compat.h" #ifdef _WIN32 -#include "sysemu/os-win32.h" +#include "system/os-win32.h" #endif -#ifdef CONFIG_POSIX -#include "sysemu/os-posix.h" +#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN) +#include "system/os-posix.h" +#endif + +#if defined(EMSCRIPTEN) +#include "system/os-wasm.h" #endif #ifdef __cplusplus @@ -185,7 +193,7 @@ extern "C" { * } */ #ifdef __clang__ -#define coroutine_fn __attribute__((__annotate__("coroutine_fn"))) +#define coroutine_fn QEMU_ANNOTATE("coroutine_fn") #else #define coroutine_fn #endif @@ -195,7 +203,7 @@ extern "C" { * but can handle running in non-coroutine context too. */ #ifdef __clang__ -#define coroutine_mixed_fn __attribute__((__annotate__("coroutine_mixed_fn"))) +#define coroutine_mixed_fn QEMU_ANNOTATE("coroutine_mixed_fn") #else #define coroutine_mixed_fn #endif @@ -224,7 +232,7 @@ extern "C" { * } */ #ifdef __clang__ -#define no_coroutine_fn __attribute__((__annotate__("no_coroutine_fn"))) +#define no_coroutine_fn QEMU_ANNOTATE("no_coroutine_fn") #else #define no_coroutine_fn #endif @@ -293,6 +301,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 @@ -383,19 +395,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)) @@ -416,14 +437,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 @@ -488,14 +513,22 @@ int qemu_daemon(int nochdir, int noclose); void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared, bool noreserve); void qemu_anon_ram_free(void *ptr, size_t size); +int qemu_shm_alloc(size_t size, Error **errp); #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_PARALLEL 1 +#endif +#endif #if defined(__HAIKU__) #define SIGIO SIGPOLL @@ -530,6 +563,14 @@ 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 @@ -580,6 +621,8 @@ int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive); bool qemu_has_ofd_lock(void); #endif +bool qemu_has_direct_io(void); + #if defined(__HAIKU__) && defined(__i386__) #define FMT_pid "%ld" #elif defined(WIN64) @@ -592,6 +635,15 @@ bool qemu_write_pidfile(const char *pidfile, Error **errp); int qemu_get_thread_id(void); +/** + * qemu_kill_thread: + * @tid: thread id. + * @sig: host signal. + * + * Send @sig to one of QEMU's own threads with identifier @tid. + */ +int qemu_kill_thread(int tid, int sig); + #ifndef CONFIG_IOVEC struct iovec { void *iov_base; @@ -655,15 +707,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. + */ +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. */ -void qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, - ThreadContext *tc, Error **errp); +bool qemu_finish_async_prealloc_mem(Error **errp); /** * qemu_get_pid_name: @@ -705,6 +775,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 @@ -734,8 +815,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-memory.h b/include/qemu/plugin-memory.h index 43165f24523..6065ec7aaf7 100644 --- a/include/qemu/plugin-memory.h +++ b/include/qemu/plugin-memory.h @@ -9,21 +9,13 @@ #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; }; /** diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index bc0781cab88..9726a9ebf36 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -13,6 +13,7 @@ #include "qemu/queue.h" #include "qemu/option.h" #include "qemu/plugin-event.h" +#include "qemu/bitmap.h" #include "exec/memopidx.h" #include "hw/core/cpu.h" @@ -50,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 { @@ -66,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; }; /* @@ -83,107 +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; /* if set, the TB calls helpers that might access guest memory */ bool mem_helper; - GArray *cbs[PLUGIN_N_CB_SUBTYPES]; + 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; - - 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; -} +struct CPUPluginState { + DECLARE_BITMAP(event_mask, QEMU_PLUGIN_EV_MAX); +}; + +/** + * 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); @@ -197,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); @@ -207,7 +179,7 @@ void qemu_plugin_add_dyn_cb_arr(GArray *arr); static inline void qemu_plugin_disable_mem_helpers(CPUState *cpu) { - cpu->plugin_mem_cbs = NULL; + cpu->neg.plugin_mem_cbs = NULL; } /** @@ -242,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"); @@ -281,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 d2d7ad085cc..e12a67ba2c0 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/qemu-plugin.h b/include/qemu/qemu-plugin.h index 50a99572798..3a850aa2166 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_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,12 +633,14 @@ 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); /** @@ -513,6 +675,7 @@ typedef void (*qemu_plugin_vcpu_mem_cb_t) (unsigned int vcpu_index, * 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, @@ -520,23 +683,50 @@ void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, void *userdata); /** - * qemu_plugin_register_vcpu_mem_inline() - register an inline op to any memory access + * 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 - * @ptr: pointer memory for the op + * @entry: entry to run op * @imm: immediate data for @op * * This registers a inline op every memory access generated by the - * instruction. This provides for a lightweight but not thread-safe - * way of counting the number of operations done. + * instruction. */ -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_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, @@ -544,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); @@ -551,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); @@ -563,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); /** @@ -572,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); /** @@ -583,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); @@ -602,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); /** @@ -628,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); /** @@ -638,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); /** @@ -646,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); /** @@ -654,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); /** @@ -662,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 index 69fe74b50d0..dc2b14d2582 100644 --- a/include/qemu/qtree.h +++ b/include/qemu/qtree.h @@ -42,7 +42,6 @@ #ifndef QEMU_QTREE_H #define QEMU_QTREE_H -#include "qemu/osdep.h" #ifdef HAVE_GLIB_WITH_SLICE_ALLOCATOR diff --git a/include/qemu/range.h b/include/qemu/range.h index 7e2b1cc447a..d446ad885d2 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/reserved-region.h b/include/qemu/reserved-region.h new file mode 100644 index 00000000000..9026cf08fdf --- /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 "system/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/s390x_pci_mmio.h b/include/qemu/s390x_pci_mmio.h new file mode 100644 index 00000000000..c5f63ecefa1 --- /dev/null +++ b/include/qemu/s390x_pci_mmio.h @@ -0,0 +1,24 @@ +/* + * s390x PCI MMIO definitions + * + * Copyright 2025 IBM Corp. + * Author(s): Farhan Ali + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef S390X_PCI_MMIO_H +#define S390X_PCI_MMIO_H + +#ifdef __s390x__ +uint8_t s390x_pci_mmio_read_8(const void *ioaddr); +uint16_t s390x_pci_mmio_read_16(const void *ioaddr); +uint32_t s390x_pci_mmio_read_32(const void *ioaddr); +uint64_t s390x_pci_mmio_read_64(const void *ioaddr); + +void s390x_pci_mmio_write_8(void *ioaddr, uint8_t val); +void s390x_pci_mmio_write_16(void *ioaddr, uint16_t val); +void s390x_pci_mmio_write_32(void *ioaddr, uint32_t val); +void s390x_pci_mmio_write_64(void *ioaddr, uint64_t val); +#endif /* __s390x__ */ + +#endif /* S390X_PCI_MMIO_H */ diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index d935fd80da8..c562690d893 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -61,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); @@ -117,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/target-info-impl.h b/include/qemu/target-info-impl.h new file mode 100644 index 00000000000..1b51cbcfe1b --- /dev/null +++ b/include/qemu/target-info-impl.h @@ -0,0 +1,32 @@ +/* + * QEMU TargetInfo structure definition + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_TARGET_INFO_IMPL_H +#define QEMU_TARGET_INFO_IMPL_H + +#include "qemu/target-info.h" + +typedef struct TargetInfo { + /* runtime equivalent of TARGET_NAME definition */ + const char *target_name; + /* runtime equivalent of TARGET_LONG_BITS definition */ + unsigned long_bits; + /* runtime equivalent of CPU_RESOLVING_TYPE definition */ + const char *cpu_type; + /* QOM typename machines for this binary must implement */ + const char *machine_typename; +} TargetInfo; + +/** + * target_info: + * + * Returns: The TargetInfo structure definition for this target binary. + */ +const TargetInfo *target_info(void); + +#endif diff --git a/include/qemu/target-info.h b/include/qemu/target-info.h new file mode 100644 index 00000000000..850a2958b9c --- /dev/null +++ b/include/qemu/target-info.h @@ -0,0 +1,41 @@ +/* + * QEMU target info API + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_TARGET_INFO_H +#define QEMU_TARGET_INFO_H + +/** + * target_name: + * + * Returns: Canonical target name (i.e. "i386"). + */ +const char *target_name(void); + +/** + * target_long_bits: + * + * Returns: number of bits in a long type for this target (i.e. 64). + */ +unsigned target_long_bits(void); + +/** + * target_machine_typename: + * + * Returns: Name of the QOM interface implemented by machines + * usable on this target binary. + */ +const char *target_machine_typename(void); + +/** + * target_cpu_type: + * + * Returns: target CPU base QOM type name (i.e. TYPE_X86_CPU). + */ +const char *target_cpu_type(void); + +#endif diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h index 5f2f3d1386b..758808b705e 100644 --- a/include/qemu/thread-posix.h +++ b/include/qemu/thread-posix.h @@ -32,15 +32,6 @@ struct QemuSemaphore { unsigned int count; }; -struct QemuEvent { -#ifndef __linux__ - pthread_mutex_t lock; - pthread_cond_t cond; -#endif - unsigned value; - bool initialized; -}; - struct QemuThread { pthread_t thread; }; diff --git a/include/qemu/thread-win32.h b/include/qemu/thread-win32.h index d95af4498fc..da9e7322990 100644 --- a/include/qemu/thread-win32.h +++ b/include/qemu/thread-win32.h @@ -28,12 +28,6 @@ struct QemuSemaphore { bool initialized; }; -struct QemuEvent { - int value; - HANDLE event; - bool initialized; -}; - typedef struct QemuThreadData QemuThreadData; struct QemuThread { QemuThreadData *data; diff --git a/include/qemu/thread.h b/include/qemu/thread.h index dd3822d7cee..f0302ed01fd 100644 --- a/include/qemu/thread.h +++ b/include/qemu/thread.h @@ -3,14 +3,32 @@ #include "qemu/processor.h" #include "qemu/atomic.h" -#include "qemu/clang-tsa.h" +#include "qemu/futex.h" typedef struct QemuCond QemuCond; typedef struct QemuSemaphore QemuSemaphore; -typedef struct QemuEvent QemuEvent; typedef struct QemuLockCnt QemuLockCnt; typedef struct QemuThread QemuThread; +/* + * QemuEvent + * ========= + * + * QemuEvent is an implementation of Win32 manual-reset event object. + * For details, refer to: + * https://learn.microsoft.com/en-us/windows/win32/sync/using-event-objects + * + * QemuEvent is more lightweight than QemuSemaphore when HAVE_FUTEX is defined. + */ +typedef struct QemuEvent { +#ifndef HAVE_FUTEX + pthread_mutex_t lock; + pthread_cond_t cond; +#endif + unsigned value; + bool initialized; +} QemuEvent; + #ifdef _WIN32 #include "qemu/thread-win32.h" #else @@ -47,7 +65,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; @@ -293,115 +311,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 05f63461373..181245d29bc 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 08245e7a109..dfd8d653fa7 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 9a91cb1248a..abd2204f3be 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 @@ -513,6 +507,8 @@ static inline void timer_init_ms(QEMUTimer *ts, QEMUClockType type, * with an AioContext---each of them runs its timer callbacks in its own * AioContext thread. * + * The timer returned must be freed using timer_free(). + * * Returns: a pointer to the timer */ static inline QEMUTimer *timer_new_full(QEMUTimerListGroup *timer_list_group, @@ -536,6 +532,8 @@ static inline QEMUTimer *timer_new_full(QEMUTimerListGroup *timer_list_group, * and associate it with the default timer list for the clock type @type. * See timer_new_full for details. * + * The timer returned must be freed using timer_free(). + * * Returns: a pointer to the timer */ static inline QEMUTimer *timer_new(QEMUClockType type, int scale, @@ -554,6 +552,8 @@ static inline QEMUTimer *timer_new(QEMUClockType type, int scale, * associated with the clock. * See timer_new_full for details. * + * The timer returned must be freed using timer_free(). + * * Returns: a pointer to the newly created timer */ static inline QEMUTimer *timer_new_ns(QEMUClockType type, QEMUTimerCB *cb, @@ -572,6 +572,8 @@ static inline QEMUTimer *timer_new_ns(QEMUClockType type, QEMUTimerCB *cb, * associated with the clock. * See timer_new_full for details. * + * The timer returned must be freed using timer_free(). + * * Returns: a pointer to the newly created timer */ static inline QEMUTimer *timer_new_us(QEMUClockType type, QEMUTimerCB *cb, @@ -590,6 +592,8 @@ static inline QEMUTimer *timer_new_us(QEMUClockType type, QEMUTimerCB *cb, * associated with the clock. * See timer_new_full for details. * + * The timer returned must be freed using timer_free(). + * * Returns: a pointer to the newly created timer */ static inline QEMUTimer *timer_new_ms(QEMUClockType type, QEMUTimerCB *cb, @@ -979,6 +983,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 diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 834b0e47a0c..507f0814d5a 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -22,12 +22,11 @@ * Please keep this list in case-insensitive alphabetical order. */ typedef struct AccelCPUState AccelCPUState; +typedef struct AccelOpsClass AccelOpsClass; typedef struct AccelState AccelState; -typedef struct AdapterInfo AdapterInfo; 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; @@ -38,28 +37,21 @@ typedef struct BusClass BusClass; typedef struct BusState BusState; typedef struct Chardev Chardev; typedef struct Clock Clock; -typedef struct CompatProperty CompatProperty; typedef struct ConfidentialGuestSupport ConfidentialGuestSupport; -typedef struct CPUAddressSpace CPUAddressSpace; typedef struct CPUArchState CPUArchState; -typedef struct CpuInfoFast CpuInfoFast; -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 GraphicHwOps GraphicHwOps; typedef struct HostMemoryBackend HostMemoryBackend; typedef struct I2CBus I2CBus; typedef struct I2SCodec I2SCodec; @@ -79,31 +71,21 @@ 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 PCIEPort PCIEPort; typedef struct PCIESlot PCIESlot; -typedef struct PCIESriovPF PCIESriovPF; -typedef struct PCIESriovVF PCIESriovVF; typedef struct PCIExpressDevice PCIExpressDevice; typedef struct PCIExpressHost PCIExpressHost; typedef struct PCIHostDeviceAddress PCIHostDeviceAddress; typedef struct PCIHostState PCIHostState; -typedef struct PICCommonState PICCommonState; -typedef struct PostcopyDiscardState PostcopyDiscardState; typedef struct Property Property; typedef struct PropertyInfo PropertyInfo; typedef struct QBool QBool; @@ -112,9 +94,7 @@ 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; @@ -129,10 +109,12 @@ typedef struct QString QString; typedef struct RAMBlock RAMBlock; typedef struct Range Range; typedef struct ReservedRegion ReservedRegion; -typedef struct SavedIOTLB SavedIOTLB; +typedef struct SaveLiveCompletePrecopyThreadData SaveLiveCompletePrecopyThreadData; 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; @@ -150,8 +132,10 @@ 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); +typedef bool (*MigrationLoadThread)(void *opaque, bool *should_quit, + Error **errp); +typedef bool (*SaveLiveCompletePrecopyThreadHandler)(SaveLiveCompletePrecopyThreadData *d, + Error **errp); #endif /* QEMU_TYPEDEFS_H */ diff --git a/include/qemu/uri.h b/include/qemu/uri.h deleted file mode 100644 index 1855b764f2a..00000000000 --- a/include/qemu/uri.h +++ /dev/null @@ -1,103 +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, see . - * - * Authors: - * Richard W.M. Jones - * - * Utility functions to help parse and assemble query strings. - */ - -#ifndef QEMU_URI_H -#define QEMU_URI_H - -/** - * 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; - -QueryParams *query_params_new(int init_alloc); -QueryParams *query_params_parse(const char *query); -void query_params_free(QueryParams *ps); - -#endif /* QEMU_URI_H */ diff --git a/include/qemu/userfaultfd.h b/include/qemu/userfaultfd.h index 18a4314212d..a1979308d7e 100644 --- a/include/qemu/userfaultfd.h +++ b/include/qemu/userfaultfd.h @@ -39,7 +39,6 @@ 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 */ diff --git a/include/qemu/uuid.h b/include/qemu/uuid.h index dc40ee1fc99..869f84af09d 100644 --- a/include/qemu/uuid.h +++ b/include/qemu/uuid.h @@ -78,9 +78,10 @@ typedef struct { "%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); @@ -96,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 64ad7010157..0417ec05332 100644 --- a/include/qemu/vhost-user-server.h +++ b/include/qemu/vhost-user-server.h @@ -45,6 +45,7 @@ typedef struct { /* Protected by ctx lock */ 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 */ diff --git a/include/qemu/yank.h b/include/qemu/yank.h index 19071509336..3d88af6996f 100644 --- a/include/qemu/yank.h +++ b/include/qemu/yank.h @@ -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/qapi/qmp/json-parser.h b/include/qobject/json-parser.h similarity index 100% rename from include/qapi/qmp/json-parser.h rename to include/qobject/json-parser.h diff --git a/include/qapi/qmp/json-writer.h b/include/qobject/json-writer.h similarity index 100% rename from include/qapi/qmp/json-writer.h rename to include/qobject/json-writer.h diff --git a/include/qapi/qmp/qbool.h b/include/qobject/qbool.h similarity index 94% rename from include/qapi/qmp/qbool.h rename to include/qobject/qbool.h index 0d09726939b..b348e17867b 100644 --- a/include/qapi/qmp/qbool.h +++ b/include/qobject/qbool.h @@ -14,7 +14,7 @@ #ifndef QBOOL_H #define QBOOL_H -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" struct QBool { struct QObjectBase_ base; diff --git a/include/qapi/qmp/qdict.h b/include/qobject/qdict.h similarity index 98% rename from include/qapi/qmp/qdict.h rename to include/qobject/qdict.h index 82e90fc0722..903e6e5462f 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qobject/qdict.h @@ -13,7 +13,7 @@ #ifndef QDICT_H #define QDICT_H -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" #include "qemu/queue.h" #define QDICT_BUCKET_MAX 512 diff --git a/include/qapi/qmp/qjson.h b/include/qobject/qjson.h similarity index 100% rename from include/qapi/qmp/qjson.h rename to include/qobject/qjson.h diff --git a/include/qapi/qmp/qlist.h b/include/qobject/qlist.h similarity index 98% rename from include/qapi/qmp/qlist.h rename to include/qobject/qlist.h index e4e985d4356..0377bf824ec 100644 --- a/include/qapi/qmp/qlist.h +++ b/include/qobject/qlist.h @@ -13,7 +13,7 @@ #ifndef QLIST_H #define QLIST_H -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" #include "qemu/queue.h" typedef struct QListEntry { diff --git a/include/qapi/qmp/qlit.h b/include/qobject/qlit.h similarity index 100% rename from include/qapi/qmp/qlit.h rename to include/qobject/qlit.h diff --git a/include/qapi/qmp/qnull.h b/include/qobject/qnull.h similarity index 94% rename from include/qapi/qmp/qnull.h rename to include/qobject/qnull.h index 7feb7c7d830..4423836a0c6 100644 --- a/include/qapi/qmp/qnull.h +++ b/include/qobject/qnull.h @@ -13,7 +13,7 @@ #ifndef QNULL_H #define QNULL_H -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" struct QNull { struct QObjectBase_ base; diff --git a/include/qapi/qmp/qnum.h b/include/qobject/qnum.h similarity index 98% rename from include/qapi/qmp/qnum.h rename to include/qobject/qnum.h index e86788dd2e3..1ce24b36681 100644 --- a/include/qapi/qmp/qnum.h +++ b/include/qobject/qnum.h @@ -15,7 +15,7 @@ #ifndef QNUM_H #define QNUM_H -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" typedef enum { QNUM_I64, diff --git a/include/qapi/qmp/qobject.h b/include/qobject/qobject.h similarity index 90% rename from include/qapi/qmp/qobject.h rename to include/qobject/qobject.h index 9003b71fd32..a6244d0ce00 100644 --- a/include/qapi/qmp/qobject.h +++ b/include/qobject/qobject.h @@ -34,7 +34,7 @@ #include "qapi/qapi-builtin-types.h" -/* Not for use outside include/qapi/qmp/ */ +/* Not for use outside include/qobject/ */ struct QObjectBase_ { QType type; size_t refcnt; @@ -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/qmp/qstring.h b/include/qobject/qstring.h similarity index 96% rename from include/qapi/qmp/qstring.h rename to include/qobject/qstring.h index 318d815d6a4..1e2abe4032d 100644 --- a/include/qapi/qmp/qstring.h +++ b/include/qobject/qstring.h @@ -13,7 +13,7 @@ #ifndef QSTRING_H #define QSTRING_H -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" struct QString { struct QObjectBase_ base; diff --git a/include/qom/object.h b/include/qom/object.h index ef7258a5e14..26df6137b91 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -26,6 +26,7 @@ typedef struct InterfaceClass InterfaceClass; typedef struct InterfaceInfo InterfaceInfo; #define TYPE_OBJECT "object" +#define TYPE_CONTAINER "container" typedef struct ObjectProperty ObjectProperty; @@ -259,35 +260,27 @@ struct Object /** - * OBJECT_DEFINE_TYPE_EXTENDED: + * 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 macro is typically used in a source file, and will: - * - * - declare prototypes for _finalize, _class_init and _init methods - * - declare the TypeInfo struct instance - * - provide the constructor to register the type - * - * After using this macro, implementations of the _finalize, _class_init, - * and _init methods need to be written. Any of these can be zero-line - * no-op impls if no special logic is required for a given type. - * - * This macro should rarely be used, instead one of the more specialized - * macros is usually a better choice. + * This is the base macro used to implement all the OBJECT_DEFINE_* + * macros. It should never be used directly in a source file. */ -#define OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ - MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ - ABSTRACT, ...) \ +#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); \ + module_obj_name##_class_init(ObjectClass *oc, const void *data); \ static void \ module_obj_name##_init(Object *obj); \ \ @@ -298,10 +291,10 @@ struct Object .instance_align = __alignof__(ModuleObjName), \ .instance_init = module_obj_name##_init, \ .instance_finalize = module_obj_name##_finalize, \ - .class_size = sizeof(ModuleObjName##Class), \ + .class_size = CLASS_SIZE, \ .class_init = module_obj_name##_class_init, \ .abstract = ABSTRACT, \ - .interfaces = (InterfaceInfo[]) { __VA_ARGS__ } , \ + .interfaces = (const InterfaceInfo[]) { __VA_ARGS__ } , \ }; \ \ static void \ @@ -311,6 +304,37 @@ struct Object } \ type_init(module_obj_name##_register_types); +/** + * 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 + * @...: list of initializers for "InterfaceInfo" to declare implemented interfaces + * + * This macro is typically used in a source file, and will: + * + * - declare prototypes for _finalize, _class_init and _init methods + * - declare the TypeInfo struct instance + * - provide the constructor to register the type + * + * After using this macro, implementations of the _finalize, _class_init, + * and _init methods need to be written. Any of these can be zero-line + * no-op impls if no special logic is required for a given type. + * + * This macro should rarely be used, instead one of the more specialized + * macros is usually a better choice. + */ +#define OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ + MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ + ABSTRACT, ...) \ + 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: * @ModuleObjName: the object name with initial caps @@ -368,6 +392,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. @@ -382,7 +445,8 @@ struct Object * class will have already been initialized so the type is only responsible * for initializing its own members. * @instance_post_init: This function is called to finish initialization of - * an object, after all @instance_init functions were called. + * an object, after all @instance_init functions were called, as well as + * @instance_post_init functions for the parent classes. * @instance_finalize: This function is called during object destruction. This * is called before the parent @instance_finalize function has been called. * An object should only free the members that are unique to its type in this @@ -423,11 +487,11 @@ struct TypeInfo bool abstract; size_t class_size; - void (*class_init)(ObjectClass *klass, void *data); - void (*class_base_init)(ObjectClass *klass, void *data); - void *class_data; + void (*class_init)(ObjectClass *klass, const void *data); + void (*class_base_init)(ObjectClass *klass, const void *data); + const void *class_data; - InterfaceInfo *interfaces; + const InterfaceInfo *interfaces; }; /** @@ -510,12 +574,15 @@ struct InterfaceInfo { * * The class for all interfaces. Subclasses of this class should only add * virtual methods. + * + * Note that most of the fields of ObjectClass are unused (all except + * "type", in fact). They are only present in InterfaceClass to allow + * @object_class_dynamic_cast to work with both regular classes and interfaces. */ struct InterfaceClass { ObjectClass parent_class; /* private: */ - ObjectClass *concrete_class; Type interface_type; }; @@ -818,24 +885,10 @@ const char *object_get_typename(const Object *obj); * type_register_static: * @info: The #TypeInfo of the new type. * - * @info and all of the strings it points to should exist for the life time - * that the type is registered. - * * Returns: the new #Type. */ Type type_register_static(const TypeInfo *info); -/** - * type_register: - * @info: The #TypeInfo of the new type - * - * Unlike type_register_static(), this call does not require @info or its - * string members to continue to exist after the call returns. - * - * Returns: the new #Type. - */ -Type type_register(const TypeInfo *info); - /** * type_register_static_array: * @infos: The array of the new type #TypeInfo structures. @@ -1093,6 +1146,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 @@ -1453,6 +1514,16 @@ const char *object_property_get_type(Object *obj, const char *name, */ Object *object_get_root(void); +/** + * object_get_container: + * @name: the name of container to lookup + * + * Lookup a root level container. + * + * Returns: the container with @name. + */ +Object *object_get_container(const char *name); + /** * object_get_objects_root: @@ -1499,8 +1570,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. * @@ -1517,7 +1588,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); @@ -1525,10 +1596,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. @@ -1542,6 +1613,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 @@ -1937,25 +2021,18 @@ int object_child_foreach(Object *obj, int (*fn)(Object *child, void *opaque), int object_child_foreach_recursive(Object *obj, int (*fn)(Object *child, void *opaque), void *opaque); -/** - * container_get: - * @root: root of the #path, e.g., object_get_root() - * @path: path to the container - * - * Return a container object whose path is @path. Create more containers - * along the path if necessary. - * - * Returns: the container object. - */ -Object *container_get(Object *root, const char *path); /** - * object_type_get_instance_size: - * @typename: Name of the Type whose instance_size is required + * object_property_add_new_container: + * @obj: the parent object + * @name: the name of the parent object's property to add + * + * Add a newly created container object to a parent object. * - * Returns the instance_size of the given @typename. + * Returns: the newly created container object. Its reference count is 1, + * and the reference is owned by the parent object. */ -size_t object_type_get_instance_size(const char *typename); +Object *object_property_add_new_container(Object *obj, const char *name); /** * object_property_help: diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h index 81541e20801..02b11a7ef07 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/semihosting/console.h b/include/semihosting/console.h index bd78e5f03fc..1c12e178ee3 100644 --- a/include/semihosting/console.h +++ b/include/semihosting/console.h @@ -9,8 +9,6 @@ #ifndef SEMIHOST_CONSOLE_H #define SEMIHOST_CONSOLE_H -#include "cpu.h" - /** * qemu_semihosting_console_read: * @cs: CPUState diff --git a/include/semihosting/semihost.h b/include/semihosting/semihost.h index efd2efa25ae..b03e6375787 100644 --- a/include/semihosting/semihost.h +++ b/include/semihosting/semihost.h @@ -26,32 +26,6 @@ typedef enum SemihostingTarget { SEMIHOSTING_TARGET_GDB } SemihostingTarget; -#ifdef CONFIG_USER_ONLY -static inline bool semihosting_enabled(bool is_user) -{ - return true; -} - -static inline SemihostingTarget semihosting_get_target(void) -{ - return SEMIHOSTING_TARGET_AUTO; -} - -static inline const char *semihosting_get_arg(int i) -{ - return NULL; -} - -static inline int semihosting_get_argc(void) -{ - return 0; -} - -static inline const char *semihosting_get_cmdline(void) -{ - return NULL; -} -#else /* !CONFIG_USER_ONLY */ /** * semihosting_enabled: * @is_user: true if guest code is in usermode (i.e. not privileged) @@ -59,17 +33,18 @@ static inline const char *semihosting_get_cmdline(void) * Return true if guest code is allowed to make semihosting calls. */ bool semihosting_enabled(bool is_user); + SemihostingTarget semihosting_get_target(void); const char *semihosting_get_arg(int i); int semihosting_get_argc(void); const char *semihosting_get_cmdline(void); void semihosting_arg_fallback(const char *file, const char *cmd); + /* 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 */ void qemu_semihosting_guestfd_init(void); #endif /* SEMIHOST_H */ diff --git a/include/semihosting/softmmu-uaccess.h b/include/semihosting/softmmu-uaccess.h deleted file mode 100644 index 4f08dfc0986..00000000000 --- 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 3a5ec229ebf..6627c45fb28 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -9,6 +9,9 @@ #ifndef SEMIHOSTING_SYSCALLS_H #define SEMIHOSTING_SYSCALLS_H +#include "exec/cpu-defs.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 00000000000..6bc90b12d6f --- /dev/null +++ b/include/semihosting/uaccess.h @@ -0,0 +1,121 @@ +/* + * 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" + +/** + * get_user_u64: + * + * Returns: 0 on success, -1 on error. + */ +#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_; }) + +/** + * get_user_u32: + * + * Returns: 0 on success, -1 on error. + */ +#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_; }) + +/** + * get_user_u8: + * + * Returns: 0 on success, -1 on error. + */ +#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_; }) + +/** + * get_user_ual: + * + * Returns: 0 on success, -1 on error. + */ +#define get_user_ual(arg, p) get_user_u32(arg, p) + +/** + * put_user_u64: + * + * Returns: 0 on success, -1 on error. + */ +#define put_user_u64(val, addr) \ + ({ uint64_t val_ = tswap64(val); \ + cpu_memory_rw_debug(env_cpu(env), (addr), &val_, sizeof(val_), 1); }) + +/** + * put_user_u32: + * + * Returns: 0 on success, -1 on error. + */ +#define put_user_u32(val, addr) \ + ({ uint32_t val_ = tswap32(val); \ + cpu_memory_rw_debug(env_cpu(env), (addr), &val_, sizeof(val_), 1); }) + +/** + * put_user_ual: + * + * Returns: 0 on success, -1 on error. + */ +#define put_user_ual(arg, p) put_user_u32(arg, p) + +/** + * uaccess_lock_user: + * + * The returned pointer should be freed using uaccess_unlock_user(). + */ +void *uaccess_lock_user(CPUArchState *env, target_ulong addr, + target_ulong len, bool copy); +/** + * lock_user: + * + * The returned pointer should be freed using unlock_user(). + */ +#define lock_user(type, p, len, copy) uaccess_lock_user(env, p, len, copy) + +/** + * uaccess_lock_user_string: + * + * The returned string should be freed using uaccess_unlock_user(). + */ +char *uaccess_lock_user_string(CPUArchState *env, target_ulong addr); +/** + * uaccess_lock_user_string: + * + * The returned string should be freed using unlock_user(). + */ +#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 0b06d2bff1b..b582a105c08 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 f0235e58a1d..9a011d20f01 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 00000000000..2e446c1d858 --- /dev/null +++ b/include/standard-headers/asm-x86/setup_data.h @@ -0,0 +1,94 @@ +/* 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_KEXEC_KHO 10 +#define SETUP_ENUM_MAX SETUP_KEXEC_KHO + +#define SETUP_INDIRECT (1<<31) +#define SETUP_TYPE_MAX (SETUP_ENUM_MAX | SETUP_INDIRECT) + +#ifndef __ASSEMBLER__ + +#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; + +/* + * Locations of kexec handover metadata + */ +struct kho_data { + uint64_t fdt_addr; + uint64_t fdt_size; + uint64_t scratch_addr; + uint64_t scratch_size; +} QEMU_PACKED; + +#endif /* __ASSEMBLER__ */ + +#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 a5a1c8234ef..00000000000 --- 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 94d41b202c9..00000000000 --- 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 72279f4d25d..c8309d378bf 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 @@ -322,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 @@ -418,6 +420,8 @@ extern "C" { #define DRM_FORMAT_MOD_VENDOR_ARM 0x08 #define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09 #define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a +#define DRM_FORMAT_MOD_VENDOR_MTK 0x0b +#define DRM_FORMAT_MOD_VENDOR_APPLE 0x0c /* add more to the end as needed */ @@ -537,7 +541,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. @@ -699,6 +703,31 @@ extern "C" { */ #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 * @@ -1100,7 +1129,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. @@ -1416,7 +1445,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 @@ -1425,6 +1454,90 @@ drm_fourcc_canonicalize_nvidia_format_mod(uint64_t modifier) */ #define AMLOGIC_FBC_OPTION_MEM_SAVING (1ULL << 0) +/* MediaTek modifiers + * Bits Parameter Notes + * ----- ------------------------ --------------------------------------------- + * 7: 0 TILE LAYOUT Values are MTK_FMT_MOD_TILE_* + * 15: 8 COMPRESSION Values are MTK_FMT_MOD_COMPRESS_* + * 23:16 10 BIT LAYOUT Values are MTK_FMT_MOD_10BIT_LAYOUT_* + * + */ + +#define DRM_FORMAT_MOD_MTK(__flags) fourcc_mod_code(MTK, __flags) + +/* + * MediaTek Tiled Modifier + * The lowest 8 bits of the modifier is used to specify the tiling + * layout. Only the 16L_32S tiling is used for now, but we define an + * "untiled" version and leave room for future expansion. + */ +#define MTK_FMT_MOD_TILE_MASK 0xf +#define MTK_FMT_MOD_TILE_NONE 0x0 +#define MTK_FMT_MOD_TILE_16L32S 0x1 + +/* + * Bits 8-15 specify compression options + */ +#define MTK_FMT_MOD_COMPRESS_MASK (0xf << 8) +#define MTK_FMT_MOD_COMPRESS_NONE (0x0 << 8) +#define MTK_FMT_MOD_COMPRESS_V1 (0x1 << 8) + +/* + * Bits 16-23 specify how the bits of 10 bit formats are + * stored out in memory + */ +#define MTK_FMT_MOD_10BIT_LAYOUT_MASK (0xf << 16) +#define MTK_FMT_MOD_10BIT_LAYOUT_PACKED (0x0 << 16) +#define MTK_FMT_MOD_10BIT_LAYOUT_LSBTILED (0x1 << 16) +#define MTK_FMT_MOD_10BIT_LAYOUT_LSBRASTER (0x2 << 16) + +/* alias for the most common tiling format */ +#define DRM_FORMAT_MOD_MTK_16L_32S_TILE DRM_FORMAT_MOD_MTK(MTK_FMT_MOD_TILE_16L32S) + +/* + * Apple GPU-tiled layouts. + * + * Apple GPUs support nonlinear tilings with optional lossless compression. + * + * GPU-tiled images are divided into 16KiB tiles: + * + * Bytes per pixel Tile size + * --------------- --------- + * 1 128x128 + * 2 128x64 + * 4 64x64 + * 8 64x32 + * 16 32x32 + * + * Tiles are raster-order. Pixels within a tile are interleaved (Morton order). + * + * Compressed images pad the body to 128-bytes and are immediately followed by a + * metadata section. The metadata section rounds the image dimensions to + * powers-of-two and contains 8 bytes for each 16x16 compression subtile. + * Subtiles are interleaved (Morton order). + * + * All images are 128-byte aligned. + * + * These layouts fundamentally do not have meaningful strides. No matter how we + * specify strides for these layouts, userspace unaware of Apple image layouts + * will be unable to use correctly the specified stride for any purpose. + * Userspace aware of the image layouts do not use strides. The most "correct" + * convention would be setting the image stride to 0. Unfortunately, some + * software assumes the stride is at least (width * bytes per pixel). We + * therefore require that stride equals (width * bytes per pixel). Since the + * stride is arbitrary here, we pick the simplest convention. + * + * Although containing two sections, compressed image layouts are treated in + * software as a single plane. This is modelled after AFBC, a similar + * scheme. Attempting to separate the sections to be "explicit" in DRM would + * only generate more confusion, as software does not treat the image this way. + * + * For detailed information on the hardware image layouts, see + * https://docs.mesa3d.org/drivers/asahi.html#image-layouts + */ +#define DRM_FORMAT_MOD_APPLE_GPU_TILED fourcc_mod_code(APPLE, 1) +#define DRM_FORMAT_MOD_APPLE_GPU_TILED_COMPRESSED fourcc_mod_code(APPLE, 2) + /* * AMD modifiers * @@ -1473,6 +1586,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 @@ -1483,13 +1597,31 @@ 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_4K_D_X 22 #define AMD_FMT_MOD_TILE_GFX9_64K_S_X 25 #define AMD_FMT_MOD_TILE_GFX9_64K_D_X 26 #define AMD_FMT_MOD_TILE_GFX9_64K_R_X 27 #define AMD_FMT_MOD_TILE_GFX11_256K_R_X 31 +/* 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 1eb84b5087f..95ede233420 100644 --- a/include/standard-headers/linux/const.h +++ b/include/standard-headers/linux/const.h @@ -28,6 +28,23 @@ #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 __int128' 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)) diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index 99fcddf04f8..cef0d207a62 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h @@ -681,6 +681,8 @@ enum ethtool_link_ext_substate_module { * @ETH_SS_STATS_ETH_MAC: names of IEEE 802.3 MAC statistics * @ETH_SS_STATS_ETH_CTRL: names of IEEE 802.3 MAC Control statistics * @ETH_SS_STATS_RMON: names of RMON statistics + * @ETH_SS_STATS_PHY: names of PHY(dev) statistics + * @ETH_SS_TS_FLAGS: hardware timestamping flags * * @ETH_SS_COUNT: number of defined string sets */ @@ -706,6 +708,8 @@ enum ethtool_stringset { ETH_SS_STATS_ETH_MAC, ETH_SS_STATS_ETH_CTRL, ETH_SS_STATS_RMON, + ETH_SS_STATS_PHY, + ETH_SS_TS_FLAGS, /* add new constants above here */ ETH_SS_COUNT @@ -752,6 +756,252 @@ 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 @@ -822,6 +1072,24 @@ enum ethtool_mm_verify_status { 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 @@ -1266,6 +1534,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 @@ -1285,7 +1555,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[]; }; @@ -1787,6 +2058,25 @@ enum ethtool_link_mode_bit_indices { 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, + ETHTOOL_LINK_MODE_200000baseCR_Full_BIT = 103, + ETHTOOL_LINK_MODE_200000baseKR_Full_BIT = 104, + ETHTOOL_LINK_MODE_200000baseDR_Full_BIT = 105, + ETHTOOL_LINK_MODE_200000baseDR_2_Full_BIT = 106, + ETHTOOL_LINK_MODE_200000baseSR_Full_BIT = 107, + ETHTOOL_LINK_MODE_200000baseVR_Full_BIT = 108, + ETHTOOL_LINK_MODE_400000baseCR2_Full_BIT = 109, + ETHTOOL_LINK_MODE_400000baseKR2_Full_BIT = 110, + ETHTOOL_LINK_MODE_400000baseDR2_Full_BIT = 111, + ETHTOOL_LINK_MODE_400000baseDR2_2_Full_BIT = 112, + ETHTOOL_LINK_MODE_400000baseSR2_Full_BIT = 113, + ETHTOOL_LINK_MODE_400000baseVR2_Full_BIT = 114, + ETHTOOL_LINK_MODE_800000baseCR4_Full_BIT = 115, + ETHTOOL_LINK_MODE_800000baseKR4_Full_BIT = 116, + ETHTOOL_LINK_MODE_800000baseDR4_Full_BIT = 117, + ETHTOOL_LINK_MODE_800000baseDR4_2_Full_BIT = 118, + ETHTOOL_LINK_MODE_800000baseSR4_Full_BIT = 119, + ETHTOOL_LINK_MODE_800000baseVR4_Full_BIT = 120, /* must be last entry */ __ETHTOOL_LINK_MODE_MASK_NBITS @@ -1992,25 +2282,89 @@ static inline int ethtool_validate_duplex(uint8_t duplex) #define WOL_MODE_COUNT 8 -/* L2-L4 network traffic flow types */ -#define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */ -#define UDP_V4_FLOW 0x02 /* hash or spec (udp_ip4_spec) */ -#define SCTP_V4_FLOW 0x03 /* hash or spec (sctp_ip4_spec) */ -#define AH_ESP_V4_FLOW 0x04 /* hash only */ -#define TCP_V6_FLOW 0x05 /* hash or spec (tcp_ip6_spec; nfc only) */ -#define UDP_V6_FLOW 0x06 /* hash or spec (udp_ip6_spec; nfc only) */ -#define SCTP_V6_FLOW 0x07 /* hash or spec (sctp_ip6_spec; nfc only) */ -#define AH_ESP_V6_FLOW 0x08 /* hash only */ -#define AH_V4_FLOW 0x09 /* hash or spec (ah_ip4_spec) */ -#define ESP_V4_FLOW 0x0a /* hash or spec (esp_ip4_spec) */ -#define AH_V6_FLOW 0x0b /* hash or spec (ah_ip6_spec; nfc only) */ -#define ESP_V6_FLOW 0x0c /* hash or spec (esp_ip6_spec; nfc only) */ -#define IPV4_USER_FLOW 0x0d /* spec only (usr_ip4_spec) */ -#define IP_USER_FLOW IPV4_USER_FLOW -#define IPV6_USER_FLOW 0x0e /* spec only (usr_ip6_spec; nfc only) */ -#define IPV4_FLOW 0x10 /* hash only */ -#define IPV6_FLOW 0x11 /* hash only */ -#define ETHER_FLOW 0x12 /* spec only (ether_spec) */ +/* 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) +/* Similar to SYM_XOR, except that one copy of the XOR'ed fields is replaced by + * an OR of the same fields + */ +#define RXH_XFRM_SYM_OR_XOR (1 << 1) +#define RXH_XFRM_NO_CHANGE 0xff + +enum { + /* L2-L4 network traffic flow types */ + TCP_V4_FLOW = 0x01, /* hash or spec (tcp_ip4_spec) */ + UDP_V4_FLOW = 0x02, /* hash or spec (udp_ip4_spec) */ + SCTP_V4_FLOW = 0x03, /* hash or spec (sctp_ip4_spec) */ + AH_ESP_V4_FLOW = 0x04, /* hash only */ + TCP_V6_FLOW = 0x05, /* hash or spec (tcp_ip6_spec; nfc only) */ + UDP_V6_FLOW = 0x06, /* hash or spec (udp_ip6_spec; nfc only) */ + SCTP_V6_FLOW = 0x07, /* hash or spec (sctp_ip6_spec; nfc only) */ + AH_ESP_V6_FLOW = 0x08, /* hash only */ + AH_V4_FLOW = 0x09, /* hash or spec (ah_ip4_spec) */ + ESP_V4_FLOW = 0x0a, /* hash or spec (esp_ip4_spec) */ + AH_V6_FLOW = 0x0b, /* hash or spec (ah_ip6_spec; nfc only) */ + ESP_V6_FLOW = 0x0c, /* hash or spec (esp_ip6_spec; nfc only) */ + IPV4_USER_FLOW = 0x0d, /* spec only (usr_ip4_spec) */ + IP_USER_FLOW = IPV4_USER_FLOW, + IPV6_USER_FLOW = 0x0e, /* spec only (usr_ip6_spec; nfc only) */ + IPV4_FLOW = 0x10, /* hash only */ + IPV6_FLOW = 0x11, /* hash only */ + 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. + */ + GTPU_V4_FLOW = 0x13, /* hash only */ + 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). + */ + GTPC_V4_FLOW = 0x15, /* hash only */ + 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. + */ + GTPC_TEID_V4_FLOW = 0x17, /* hash only */ + 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. + */ + GTPU_EH_V4_FLOW = 0x19, /* hash only */ + 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. + */ + GTPU_UL_V4_FLOW = 0x1b, /* hash only */ + GTPU_UL_V6_FLOW = 0x1c, /* hash only */ + GTPU_DL_V4_FLOW = 0x1d, /* hash only */ + GTPU_DL_V6_FLOW = 0x1e, /* hash only */ + + __FLOW_TYPE_COUNT, +}; + /* Flag to enable additional fields in struct ethtool_rx_flow_spec */ #define FLOW_EXT 0x80000000 #define FLOW_MAC_EXT 0x40000000 @@ -2025,6 +2379,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 @@ -2128,18 +2483,6 @@ 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. @@ -2181,6 +2524,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; @@ -2198,6 +2556,11 @@ struct ethtool_link_settings { uint8_t master_slave_state; uint8_t rate_matching; uint32_t reserved[7]; + /* Linux builds with -Wflex-array-member-not-at-end but does + * not use the "link_mode_masks" member. Leave it defined for + * userspace for now, and when userspace wants to start using + * -Wfamnae, we'll need a new solution. + */ uint32_t link_mode_masks[]; /* layout of link_mode_masks fields: * uint32_t map_supported[link_mode_masks_nwords]; @@ -2205,4 +2568,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 6b9793842c9..d8b2fd67e16 100644 --- a/include/standard-headers/linux/fuse.h +++ b/include/standard-headers/linux/fuse.h @@ -209,8 +209,32 @@ * - add FUSE_HAS_EXPIRE_ONLY * * 7.39 - * - add FUSE_DIRECT_IO_RELAX + * - 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 + * 7.42 + * - Add FUSE_OVER_IO_URING and all other io-uring related flags and data + * structures: + * - struct fuse_uring_ent_in_out + * - struct fuse_uring_req_header + * - struct fuse_uring_cmd_req + * - FUSE_URING_IN_OUT_HEADER_SZ + * - FUSE_URING_OP_IN_OUT_SZ + * - enum fuse_uring_cmd + * + * 7.43 + * - add FUSE_REQUEST_TIMEOUT + * + * 7.44 + * - add FUSE_NOTIFY_INC_EPOCH */ #ifndef _LINUX_FUSE_H @@ -242,7 +266,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 39 +#define FUSE_KERNEL_MINOR_VERSION 44 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -349,6 +373,7 @@ struct fuse_file_lock { * 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) @@ -357,6 +382,7 @@ struct fuse_file_lock { #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 @@ -405,8 +431,14 @@ struct fuse_file_lock { * 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_RELAX: relax restrictions in FOPEN_DIRECT_IO mode, for now - * allow shared mmap + * 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 + * FUSE_OVER_IO_URING: Indicate that client supports io-uring + * FUSE_REQUEST_TIMEOUT: kernel supports timing out requests. + * init_out.request_timeout contains the timeout (in secs) */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) @@ -445,7 +477,15 @@ struct fuse_file_lock { #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_RELAX (1ULL << 36) +#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) +#define FUSE_OVER_IO_URING (1ULL << 41) +#define FUSE_REQUEST_TIMEOUT (1ULL << 42) /** * CUSE INIT request/reply flags @@ -629,6 +669,8 @@ enum fuse_notify_code { FUSE_NOTIFY_STORE = 4, FUSE_NOTIFY_RETRIEVE = 5, FUSE_NOTIFY_DELETE = 6, + FUSE_NOTIFY_RESEND = 7, + FUSE_NOTIFY_INC_EPOCH = 8, FUSE_NOTIFY_CODE_MAX, }; @@ -755,7 +797,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 { @@ -871,7 +913,9 @@ struct fuse_init_out { uint16_t max_pages; uint16_t map_alignment; uint32_t flags2; - uint32_t unused[7]; + uint32_t max_stack_depth; + uint16_t request_timeout; + uint16_t unused[11]; }; #define CUSE_INIT_INFO_MAX 4096 @@ -954,6 +998,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; @@ -1043,9 +1110,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; @@ -1147,4 +1223,67 @@ struct fuse_supp_groups { uint32_t groups[]; }; +/** + * Size of the ring buffer header + */ +#define FUSE_URING_IN_OUT_HEADER_SZ 128 +#define FUSE_URING_OP_IN_OUT_SZ 128 + +/* Used as part of the fuse_uring_req_header */ +struct fuse_uring_ent_in_out { + uint64_t flags; + + /* + * commit ID to be used in a reply to a ring request (see also + * struct fuse_uring_cmd_req) + */ + uint64_t commit_id; + + /* size of user payload buffer */ + uint32_t payload_sz; + uint32_t padding; + + uint64_t reserved; +}; + +/** + * Header for all fuse-io-uring requests + */ +struct fuse_uring_req_header { + /* struct fuse_in_header / struct fuse_out_header */ + char in_out[FUSE_URING_IN_OUT_HEADER_SZ]; + + /* per op code header */ + char op_in[FUSE_URING_OP_IN_OUT_SZ]; + + struct fuse_uring_ent_in_out ring_ent_in_out; +}; + +/** + * sqe commands to the kernel + */ +enum fuse_uring_cmd { + FUSE_IO_URING_CMD_INVALID = 0, + + /* register the request buffer and fetch a fuse request */ + FUSE_IO_URING_CMD_REGISTER = 1, + + /* commit fuse request result and fetch next request */ + FUSE_IO_URING_CMD_COMMIT_AND_FETCH = 2, +}; + +/** + * In the 80B command area of the SQE. + */ +struct fuse_uring_cmd_req { + uint64_t flags; + + /* entry identifier for commits */ + uint64_t commit_id; + + /* queue the command is for (queue index) */ + uint16_t qid; + uint8_t padding[6]; +}; + #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 f6bab08540d..a82ff795e06 100644 --- a/include/standard-headers/linux/input-event-codes.h +++ b/include/standard-headers/linux/input-event-codes.h @@ -519,6 +519,7 @@ #define KEY_NOTIFICATION_CENTER 0x1bc /* Show/hide the notification center */ #define KEY_PICKUP_PHONE 0x1bd /* Answer incoming call */ #define KEY_HANGUP_PHONE 0x1be /* Decline incoming call */ +#define KEY_LINK_PHONE 0x1bf /* AL Phone Syncing */ #define KEY_DEL_EOL 0x1c0 #define KEY_DEL_EOS 0x1c1 @@ -602,6 +603,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 */ @@ -617,6 +619,8 @@ #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 */ @@ -921,7 +925,8 @@ #define SW_MUTE_DEVICE 0x0e /* set = device disabled */ #define SW_PEN_INSERTED 0x0f /* set = pen inserted */ #define SW_MACHINE_COVER 0x10 /* set = cover closed */ -#define SW_MAX_ 0x10 +#define SW_USB_INSERT 0x11 /* set = USB audio device connected */ +#define SW_MAX_ 0x11 #define SW_CNT (SW_MAX_+1) /* diff --git a/include/standard-headers/linux/kvm_para.h b/include/standard-headers/linux/kvm_para.h new file mode 100644 index 00000000000..015c1663021 --- /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 e5f558d9649..a3a3e942ded 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 */ @@ -339,7 +340,8 @@ #define PCI_MSIX_ENTRY_UPPER_ADDR 0x4 /* Message Upper Address */ #define PCI_MSIX_ENTRY_DATA 0x8 /* Message Data */ #define PCI_MSIX_ENTRY_VECTOR_CTRL 0xc /* Vector Control */ -#define PCI_MSIX_ENTRY_CTRL_MASKBIT 0x00000001 +#define PCI_MSIX_ENTRY_CTRL_MASKBIT 0x00000001 /* Mask Bit */ +#define PCI_MSIX_ENTRY_CTRL_ST 0xffff0000 /* Steering Tag */ /* CompactPCI Hotswap Register */ @@ -484,6 +486,7 @@ #define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */ #define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ #define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ +#define PCI_EXP_FLAGS_FLIT 0x8000 /* Flit Mode Supported */ #define PCI_EXP_DEVCAP 0x04 /* Device capabilities */ #define PCI_EXP_DEVCAP_PAYLOAD 0x00000007 /* Max_Payload_Size */ #define PCI_EXP_DEVCAP_PHANTOM 0x00000018 /* Phantom functions */ @@ -531,7 +534,7 @@ #define PCI_EXP_DEVSTA_TRPND 0x0020 /* Transactions Pending */ #define PCI_CAP_EXP_RC_ENDPOINT_SIZEOF_V1 12 /* v1 endpoints without link end here */ #define PCI_EXP_LNKCAP 0x0c /* Link Capabilities */ -#define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */ +#define PCI_EXP_LNKCAP_SLS 0x0000000f /* Max Link Speed (prior to PCIe r3.0: Supported Link Speeds) */ #define PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */ #define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */ #define PCI_EXP_LNKCAP_SLS_8_0GB 0x00000003 /* LNKCAP2 SLS Vector bit 2 */ @@ -633,10 +636,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 */ /* @@ -655,10 +661,12 @@ #define PCI_EXP_DEVCAP2_ATOMIC_COMP64 0x00000100 /* 64b AtomicOp completion */ #define PCI_EXP_DEVCAP2_ATOMIC_COMP128 0x00000200 /* 128b AtomicOp completion */ #define PCI_EXP_DEVCAP2_LTR 0x00000800 /* Latency tolerance reporting */ +#define PCI_EXP_DEVCAP2_TPH_COMP_MASK 0x00003000 /* TPH completer support */ #define PCI_EXP_DEVCAP2_OBFF_MASK 0x000c0000 /* OBFF support mechanism */ #define PCI_EXP_DEVCAP2_OBFF_MSG 0x00040000 /* New message signaling */ #define PCI_EXP_DEVCAP2_OBFF_WAKE 0x00080000 /* Re-use WAKE# for OBFF */ #define PCI_EXP_DEVCAP2_EE_PREFIX 0x00200000 /* End-End TLP Prefix */ +#define PCI_EXP_DEVCAP2_EE_PREFIX_MAX 0x00c00000 /* Max End-End TLP Prefixes */ #define PCI_EXP_DEVCTL2 0x28 /* Device Control 2 */ #define PCI_EXP_DEVCTL2_COMP_TIMEOUT 0x000f /* Completion Timeout Value */ #define PCI_EXP_DEVCTL2_COMP_TMOUT_DIS 0x0010 /* Completion Timeout Disable */ @@ -674,6 +682,7 @@ #define PCI_EXP_DEVSTA2 0x2a /* Device Status 2 */ #define PCI_CAP_EXP_RC_ENDPOINT_SIZEOF_V2 0x2c /* end of v2 EPs w/o link */ #define PCI_EXP_LNKCAP2 0x2c /* Link Capabilities 2 */ +#define PCI_EXP_LNKCAP2_SLS 0x000000fe /* Supported Link Speeds Vector */ #define PCI_EXP_LNKCAP2_SLS_2_5GB 0x00000002 /* Supported Speed 2.5GT/s */ #define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5GT/s */ #define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8GT/s */ @@ -738,9 +747,11 @@ #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 +#define PCI_EXT_CAP_ID_PL_64GT 0x31 /* Physical Layer 64.0 GT/s */ +#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_64GT #define PCI_EXT_CAP_DSN_SIZEOF 12 #define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40 @@ -781,10 +792,13 @@ /* Same bits as above */ #define PCI_ERR_CAP 0x18 /* Advanced Error Capabilities & Ctrl*/ #define PCI_ERR_CAP_FEP(x) ((x) & 0x1f) /* First Error Pointer */ -#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */ -#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */ -#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ -#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ +#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */ +#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */ +#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ +#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ +#define PCI_ERR_CAP_PREFIX_LOG_PRESENT 0x00000800 /* TLP Prefix Log Present */ +#define PCI_ERR_CAP_TLP_LOG_FLIT 0x00040000 /* TLP was logged in Flit Mode */ +#define PCI_ERR_CAP_TLP_LOG_SIZE 0x00f80000 /* Logged TLP Size (only in Flit mode) */ #define PCI_ERR_HEADER_LOG 0x1c /* Header Log Register (16 bytes) */ #define PCI_ERR_ROOT_COMMAND 0x2c /* Root Error Command */ #define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 /* Correctable Err Reporting Enable */ @@ -800,6 +814,7 @@ #define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */ #define PCI_ERR_ROOT_AER_IRQ 0xf8000000 /* Advanced Error Interrupt Message Number */ #define PCI_ERR_ROOT_ERR_SRC 0x34 /* Error Source Identification */ +#define PCI_ERR_PREFIX_LOG 0x38 /* TLP Prefix LOG Register (up to 16 bytes) */ /* Virtual Channel */ #define PCI_VC_PORT_CAP1 0x04 @@ -930,12 +945,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 */ @@ -975,6 +991,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 */ @@ -990,9 +1008,6 @@ #define PCI_ACS_CTRL 0x06 /* ACS Control Register */ #define PCI_ACS_EGRESS_CTL_V 0x08 /* ACS Egress Control Vector */ -#define PCI_VSEC_HDR 4 /* extended cap - vendor-specific */ -#define PCI_VSEC_HDR_LEN_SHIFT 20 /* shift for length field */ - /* SATA capability */ #define PCI_SATA_REGS 4 /* SATA REGs specifier */ #define PCI_SATA_REGS_MASK 0xF /* location - BAR#/inline */ @@ -1002,7 +1017,7 @@ /* Resizable BARs */ #define PCI_REBAR_CAP 4 /* capability register */ -#define PCI_REBAR_CAP_SIZES 0x00FFFFF0 /* supported BAR sizes */ +#define PCI_REBAR_CAP_SIZES 0xFFFFFFF0 /* supported BAR sizes */ #define PCI_REBAR_CTRL 8 /* control register */ #define PCI_REBAR_CTRL_BAR_IDX 0x00000007 /* BAR index */ #define PCI_REBAR_CTRL_NBAR_MASK 0x000000E0 /* # of resizable BARs */ @@ -1015,15 +1030,34 @@ #define PCI_DPA_CAP_SUBSTATE_MASK 0x1F /* # substates - 1 */ #define PCI_DPA_BASE_SIZEOF 16 /* size with 0 substates */ +/* TPH Completer Support */ +#define PCI_EXP_DEVCAP2_TPH_COMP_NONE 0x0 /* None */ +#define PCI_EXP_DEVCAP2_TPH_COMP_TPH_ONLY 0x1 /* TPH only */ +#define PCI_EXP_DEVCAP2_TPH_COMP_EXT_TPH 0x3 /* TPH and Extended TPH */ + /* TPH Requester */ #define PCI_TPH_CAP 4 /* capability register */ -#define PCI_TPH_CAP_LOC_MASK 0x600 /* location mask */ -#define PCI_TPH_LOC_NONE 0x000 /* no location */ -#define PCI_TPH_LOC_CAP 0x200 /* in capability */ -#define PCI_TPH_LOC_MSIX 0x400 /* in MSI-X */ -#define PCI_TPH_CAP_ST_MASK 0x07FF0000 /* ST table mask */ -#define PCI_TPH_CAP_ST_SHIFT 16 /* ST table shift */ -#define PCI_TPH_BASE_SIZEOF 0xc /* size with no ST table */ +#define PCI_TPH_CAP_ST_NS 0x00000001 /* No ST Mode Supported */ +#define PCI_TPH_CAP_ST_IV 0x00000002 /* Interrupt Vector Mode Supported */ +#define PCI_TPH_CAP_ST_DS 0x00000004 /* Device Specific Mode Supported */ +#define PCI_TPH_CAP_EXT_TPH 0x00000100 /* Ext TPH Requester Supported */ +#define PCI_TPH_CAP_LOC_MASK 0x00000600 /* ST Table Location */ +#define PCI_TPH_LOC_NONE 0x00000000 /* Not present */ +#define PCI_TPH_LOC_CAP 0x00000200 /* In capability */ +#define PCI_TPH_LOC_MSIX 0x00000400 /* In MSI-X */ +#define PCI_TPH_CAP_ST_MASK 0x07FF0000 /* ST Table Size */ +#define PCI_TPH_CAP_ST_SHIFT 16 /* ST Table Size shift */ +#define PCI_TPH_BASE_SIZEOF 0xc /* Size with no ST table */ + +#define PCI_TPH_CTRL 8 /* control register */ +#define PCI_TPH_CTRL_MODE_SEL_MASK 0x00000007 /* ST Mode Select */ +#define PCI_TPH_ST_NS_MODE 0x0 /* No ST Mode */ +#define PCI_TPH_ST_IV_MODE 0x1 /* Interrupt Vector Mode */ +#define PCI_TPH_ST_DS_MODE 0x2 /* Device Specific Mode */ +#define PCI_TPH_CTRL_REQ_EN_MASK 0x00000300 /* TPH Requester Enable */ +#define PCI_TPH_REQ_DISABLE 0x0 /* No TPH requests allowed */ +#define PCI_TPH_REQ_TPH_ONLY 0x1 /* TPH only requests allowed */ +#define PCI_TPH_REQ_EXT_TPH 0x3 /* Extended TPH requests allowed */ /* Downstream Port Containment */ #define PCI_EXP_DPC_CAP 0x04 /* DPC Capability */ @@ -1031,8 +1065,9 @@ #define PCI_EXP_DPC_CAP_RP_EXT 0x0020 /* Root Port Extensions */ #define PCI_EXP_DPC_CAP_POISONED_TLP 0x0040 /* Poisoned TLP Egress Blocking Supported */ #define PCI_EXP_DPC_CAP_SW_TRIGGER 0x0080 /* Software Triggering Supported */ -#define PCI_EXP_DPC_RP_PIO_LOG_SIZE 0x0F00 /* RP PIO Log Size */ +#define PCI_EXP_DPC_RP_PIO_LOG_SIZE 0x0F00 /* RP PIO Log Size [3:0] */ #define PCI_EXP_DPC_CAP_DL_ACTIVE 0x1000 /* ERR_COR signal on DL_Active supported */ +#define PCI_EXP_DPC_RP_PIO_LOG_SIZE4 0x2000 /* RP PIO Log Size [4] */ #define PCI_EXP_DPC_CTL 0x06 /* DPC control */ #define PCI_EXP_DPC_CTL_EN_FATAL 0x0001 /* Enable trigger on ERR_FATAL message */ @@ -1042,9 +1077,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 */ @@ -1088,6 +1130,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 */ @@ -1101,12 +1145,55 @@ #define PCI_DLF_CAP 0x04 /* Capabilities Register */ #define PCI_DLF_EXCHANGE_ENABLE 0x80000000 /* Data Link Feature Exchange Enable */ +/* Secondary PCIe Capability 8.0 GT/s */ +#define PCI_SECPCI_LE_CTRL 0x0c /* Lane Equalization Control Register */ + /* Physical Layer 16.0 GT/s */ #define PCI_PL_16GT_LE_CTRL 0x20 /* Lane Equalization Control Register */ #define PCI_PL_16GT_LE_CTRL_DSP_TX_PRESET_MASK 0x0000000F #define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_MASK 0x000000F0 #define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_SHIFT 4 +/* Physical Layer 32.0 GT/s */ +#define PCI_PL_32GT_LE_CTRL 0x20 /* Lane Equalization Control Register */ + +/* Physical Layer 64.0 GT/s */ +#define PCI_PL_64GT_LE_CTRL 0x20 /* Lane Equalization Control Register */ + +/* 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 */ @@ -1130,8 +1217,17 @@ #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_TYPE 0x00ff0000 #define PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX 0xff000000 +/* Deprecated old name, replaced with PCI_DOE_DATA_OBJECT_DISC_RSP_3_TYPE */ +#define PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL PCI_DOE_DATA_OBJECT_DISC_RSP_3_TYPE + +/* 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 54b7485390d..00000000000 --- 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 5ad07e134ae..fd54044936f 100644 --- a/include/standard-headers/linux/vhost_types.h +++ b/include/standard-headers/linux/vhost_types.h @@ -185,5 +185,12 @@ struct vhost_vdpa_iova_range { * 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 f343bfefd82..3121cd2e0e1 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_bt.h b/include/standard-headers/linux/virtio_bt.h index a11ecc3f92d..6f0dee7e326 100644 --- a/include/standard-headers/linux/virtio_bt.h +++ b/include/standard-headers/linux/virtio_bt.h @@ -13,7 +13,6 @@ enum virtio_bt_config_type { VIRTIO_BT_CONFIG_TYPE_PRIMARY = 0, - VIRTIO_BT_CONFIG_TYPE_AMP = 1, }; enum virtio_bt_config_vendor { diff --git a/include/standard-headers/linux/virtio_config.h b/include/standard-headers/linux/virtio_config.h index 8a7d0dc8b00..45be0fa1bcd 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 @@ -103,8 +103,19 @@ */ #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_crypto.h b/include/standard-headers/linux/virtio_crypto.h index 68066dafb6d..4d350ae5957 100644 --- a/include/standard-headers/linux/virtio_crypto.h +++ b/include/standard-headers/linux/virtio_crypto.h @@ -329,6 +329,7 @@ struct virtio_crypto_op_header { VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x00) #define VIRTIO_CRYPTO_AKCIPHER_DECRYPT \ VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x01) + /* akcipher sign/verify opcodes are deprecated */ #define VIRTIO_CRYPTO_AKCIPHER_SIGN \ VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x02) #define VIRTIO_CRYPTO_AKCIPHER_VERIFY \ diff --git a/include/standard-headers/linux/virtio_gpu.h b/include/standard-headers/linux/virtio_gpu.h index 2da48d3d4c2..00cd3f04af9 100644 --- a/include/standard-headers/linux/virtio_gpu.h +++ b/include/standard-headers/linux/virtio_gpu.h @@ -309,6 +309,10 @@ struct virtio_gpu_cmd_submit { #define VIRTIO_GPU_CAPSET_VIRGL 1 #define VIRTIO_GPU_CAPSET_VIRGL2 2 +#define VIRTIO_GPU_CAPSET_GFXSTREAM_VULKAN 3 +#define VIRTIO_GPU_CAPSET_VENUS 4 +#define VIRTIO_GPU_CAPSET_CROSS_DOMAIN 5 +#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 18c74c527c8..6bfa41bd8b6 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 0f884177423..982e854f14e 100644 --- a/include/standard-headers/linux/virtio_net.h +++ b/include/standard-headers/linux/virtio_net.h @@ -56,6 +56,7 @@ #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. */ @@ -326,6 +327,19 @@ struct virtio_net_rss_config { uint8_t hash_key_data[/* hash_key_length */]; }; +struct virtio_net_rss_config_hdr { + uint32_t hash_types; + uint16_t indirection_table_mask; + uint16_t unclassified_queue; + uint16_t indirection_table[/* 1 + indirection_table_mask */]; +}; + +struct virtio_net_rss_config_trailer { + uint16_t max_tx_vq; + uint8_t hash_key_length; + uint8_t hash_key_data[/* hash_key_length */]; +}; + #define VIRTIO_NET_CTRL_MQ_RSS_CONFIG 1 /* @@ -406,4 +420,146 @@ struct virtio_net_ctrl_coal_vq { 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 be912cfc957..09e964e6eee 100644 --- a/include/standard-headers/linux/virtio_pci.h +++ b/include/standard-headers/linux/virtio_pci.h @@ -40,6 +40,7 @@ #define _LINUX_VIRTIO_PCI_H #include "standard-headers/linux/types.h" +#include "standard-headers/linux/kernel.h" #ifndef VIRTIO_PCI_NO_LEGACY @@ -115,6 +116,8 @@ #define VIRTIO_PCI_CAP_PCI_CFG 5 /* Additional shared memory capability */ #define VIRTIO_PCI_CAP_SHARED_MEMORY_CFG 8 +/* PCI vendor data configuration */ +#define VIRTIO_PCI_CAP_VENDOR_CFG 9 /* This is the PCI capability header: */ struct virtio_pci_cap { @@ -129,6 +132,18 @@ struct virtio_pci_cap { uint32_t length; /* Length of the structure, in bytes. */ }; +/* This is the PCI vendor data capability header: */ +struct virtio_pci_vndr_data { + uint8_t cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */ + uint8_t cap_next; /* Generic PCI field: next ptr. */ + uint8_t cap_len; /* Generic PCI field: capability length */ + uint8_t cfg_type; /* Identifies the structure. */ + uint16_t vendor_id; /* Identifies the vendor-specific format. */ + /* For Vendor Definition */ + /* Pads structure to a multiple of 4 bytes */ + /* Reads must not have side effects */ +}; + struct virtio_pci_cap64 { struct virtio_pci_cap cap; uint32_t offset_hi; /* Most sig 32 bits of offset */ @@ -166,6 +181,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 +233,203 @@ 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_SELF 0x0 +#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 + +/* Device parts access commands. */ +#define VIRTIO_ADMIN_CMD_CAP_ID_LIST_QUERY 0x7 +#define VIRTIO_ADMIN_CMD_DEVICE_CAP_GET 0x8 +#define VIRTIO_ADMIN_CMD_DRIVER_CAP_SET 0x9 +#define VIRTIO_ADMIN_CMD_RESOURCE_OBJ_CREATE 0xa +#define VIRTIO_ADMIN_CMD_RESOURCE_OBJ_DESTROY 0xd +#define VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_GET 0xe +#define VIRTIO_ADMIN_CMD_DEV_PARTS_GET 0xf +#define VIRTIO_ADMIN_CMD_DEV_PARTS_SET 0x10 +#define VIRTIO_ADMIN_CMD_DEV_MODE_SET 0x11 + +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]; +}; + +#define VIRTIO_DEV_PARTS_CAP 0x0000 + +struct virtio_dev_parts_cap { + uint8_t get_parts_resource_objects_limit; + uint8_t set_parts_resource_objects_limit; +}; + +#define MAX_CAP_ID __KERNEL_DIV_ROUND_UP(VIRTIO_DEV_PARTS_CAP + 1, 64) + +struct virtio_admin_cmd_query_cap_id_result { + uint64_t supported_caps[MAX_CAP_ID]; +}; + +struct virtio_admin_cmd_cap_get_data { + uint16_t id; + uint8_t reserved[6]; +}; + +struct virtio_admin_cmd_cap_set_data { + uint16_t id; + uint8_t reserved[6]; + uint8_t cap_specific_data[]; +}; + +struct virtio_admin_cmd_resource_obj_cmd_hdr { + uint16_t type; + uint8_t reserved[2]; + uint32_t id; /* Indicates unique resource object id per resource object type */ +}; + +struct virtio_admin_cmd_resource_obj_create_data { + struct virtio_admin_cmd_resource_obj_cmd_hdr hdr; + uint64_t flags; + uint8_t resource_obj_specific_data[]; +}; + +#define VIRTIO_RESOURCE_OBJ_DEV_PARTS 0 + +#define VIRTIO_RESOURCE_OBJ_DEV_PARTS_TYPE_GET 0 +#define VIRTIO_RESOURCE_OBJ_DEV_PARTS_TYPE_SET 1 + +struct virtio_resource_obj_dev_parts { + uint8_t type; + uint8_t reserved[7]; +}; + +#define VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_TYPE_SIZE 0 +#define VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_TYPE_COUNT 1 +#define VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_TYPE_LIST 2 + +struct virtio_admin_cmd_dev_parts_metadata_data { + struct virtio_admin_cmd_resource_obj_cmd_hdr hdr; + uint8_t type; + uint8_t reserved[7]; +}; + +#define VIRTIO_DEV_PART_F_OPTIONAL 0 + +struct virtio_dev_part_hdr { + uint16_t part_type; + uint8_t flags; + uint8_t reserved; + union { + struct { + uint32_t offset; + uint32_t reserved; + } pci_common_cfg; + struct { + uint16_t index; + uint8_t reserved[6]; + } vq_index; + } selector; + uint32_t length; +}; + +struct virtio_dev_part { + struct virtio_dev_part_hdr hdr; + uint8_t value[]; +}; + +struct virtio_admin_cmd_dev_parts_metadata_result { + union { + struct { + uint32_t size; + uint32_t reserved; + } parts_size; + struct { + uint32_t count; + uint32_t reserved; + } hdr_list_count; + struct { + uint32_t count; + uint32_t reserved; + struct virtio_dev_part_hdr hdrs[]; + } hdr_list; + }; +}; + +#define VIRTIO_ADMIN_CMD_DEV_PARTS_GET_TYPE_SELECTED 0 +#define VIRTIO_ADMIN_CMD_DEV_PARTS_GET_TYPE_ALL 1 + +struct virtio_admin_cmd_dev_parts_get_data { + struct virtio_admin_cmd_resource_obj_cmd_hdr hdr; + uint8_t type; + uint8_t reserved[7]; + struct virtio_dev_part_hdr hdr_list[]; +}; + +struct virtio_admin_cmd_dev_parts_set_data { + struct virtio_admin_cmd_resource_obj_cmd_hdr hdr; + struct virtio_dev_part parts[]; +}; + +#define VIRTIO_ADMIN_CMD_DEV_MODE_F_STOPPED 0 + +struct virtio_admin_cmd_dev_mode_set_data { + uint8_t flags; +}; + #endif diff --git a/include/standard-headers/linux/virtio_pmem.h b/include/standard-headers/linux/virtio_pmem.h index fc029de7988..1a2576d0178 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 1af96b9fc61..160d57899f8 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 (if VIRTIO_SND_F_CTLS) */ + 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/linux/vmclock-abi.h b/include/standard-headers/linux/vmclock-abi.h new file mode 100644 index 00000000000..15b0316cb4c --- /dev/null +++ b/include/standard-headers/linux/vmclock-abi.h @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */ + +/* + * This structure provides a vDSO-style clock to VM guests, exposing the + * relationship (or lack thereof) between the CPU clock (TSC, timebase, arch + * counter, etc.) and real time. It is designed to address the problem of + * live migration, which other clock enlightenments do not. + * + * When a guest is live migrated, this affects the clock in two ways. + * + * First, even between identical hosts the actual frequency of the underlying + * counter will change within the tolerances of its specification (typically + * ±50PPM, or 4 seconds a day). This frequency also varies over time on the + * same host, but can be tracked by NTP as it generally varies slowly. With + * live migration there is a step change in the frequency, with no warning. + * + * Second, there may be a step change in the value of the counter itself, as + * its accuracy is limited by the precision of the NTP synchronization on the + * source and destination hosts. + * + * So any calibration (NTP, PTP, etc.) which the guest has done on the source + * host before migration is invalid, and needs to be redone on the new host. + * + * In its most basic mode, this structure provides only an indication to the + * guest that live migration has occurred. This allows the guest to know that + * its clock is invalid and take remedial action. For applications that need + * reliable accurate timestamps (e.g. distributed databases), the structure + * can be mapped all the way to userspace. This allows the application to see + * directly for itself that the clock is disrupted and take appropriate + * action, even when using a vDSO-style method to get the time instead of a + * system call. + * + * In its more advanced mode. this structure can also be used to expose the + * precise relationship of the CPU counter to real time, as calibrated by the + * host. This means that userspace applications can have accurate time + * immediately after live migration, rather than having to pause operations + * and wait for NTP to recover. This mode does, of course, rely on the + * counter being reliable and consistent across CPUs. + * + * Note that this must be true UTC, never with smeared leap seconds. If a + * guest wishes to construct a smeared clock, it can do so. Presenting a + * smeared clock through this interface would be problematic because it + * actually messes with the apparent counter *period*. A linear smearing + * of 1 ms per second would effectively tweak the counter period by 1000PPM + * at the start/end of the smearing period, while a sinusoidal smear would + * basically be impossible to represent. + * + * This structure is offered with the intent that it be adopted into the + * nascent virtio-rtc standard, as a virtio-rtc that does not address the live + * migration problem seems a little less than fit for purpose. For that + * reason, certain fields use precisely the same numeric definitions as in + * the virtio-rtc proposal. The structure can also be exposed through an ACPI + * device with the CID "VMCLOCK", modelled on the "VMGENID" device except for + * the fact that it uses a real _CRS to convey the address of the structure + * (which should be a full page, to allow for mapping directly to userspace). + */ + +#ifndef __VMCLOCK_ABI_H__ +#define __VMCLOCK_ABI_H__ + +#include "standard-headers/linux/types.h" + +struct vmclock_abi { + /* CONSTANT FIELDS */ + uint32_t magic; +#define VMCLOCK_MAGIC 0x4b4c4356 /* "VCLK" */ + uint32_t size; /* Size of region containing this structure */ + uint16_t version; /* 1 */ + uint8_t counter_id; /* Matches VIRTIO_RTC_COUNTER_xxx except INVALID */ +#define VMCLOCK_COUNTER_ARM_VCNT 0 +#define VMCLOCK_COUNTER_X86_TSC 1 +#define VMCLOCK_COUNTER_INVALID 0xff + uint8_t time_type; /* Matches VIRTIO_RTC_TYPE_xxx */ +#define VMCLOCK_TIME_UTC 0 /* Since 1970-01-01 00:00:00z */ +#define VMCLOCK_TIME_TAI 1 /* Since 1970-01-01 00:00:00z */ +#define VMCLOCK_TIME_MONOTONIC 2 /* Since undefined epoch */ +#define VMCLOCK_TIME_INVALID_SMEARED 3 /* Not supported */ +#define VMCLOCK_TIME_INVALID_MAYBE_SMEARED 4 /* Not supported */ + + /* NON-CONSTANT FIELDS PROTECTED BY SEQCOUNT LOCK */ + uint32_t seq_count; /* Low bit means an update is in progress */ + /* + * This field changes to another non-repeating value when the CPU + * counter is disrupted, for example on live migration. This lets + * the guest know that it should discard any calibration it has + * performed of the counter against external sources (NTP/PTP/etc.). + */ + uint64_t disruption_marker; + uint64_t flags; + /* Indicates that the tai_offset_sec field is valid */ +#define VMCLOCK_FLAG_TAI_OFFSET_VALID (1 << 0) + /* + * Optionally used to notify guests of pending maintenance events. + * A guest which provides latency-sensitive services may wish to + * remove itself from service if an event is coming up. Two flags + * indicate the approximate imminence of the event. + */ +#define VMCLOCK_FLAG_DISRUPTION_SOON (1 << 1) /* About a day */ +#define VMCLOCK_FLAG_DISRUPTION_IMMINENT (1 << 2) /* About an hour */ +#define VMCLOCK_FLAG_PERIOD_ESTERROR_VALID (1 << 3) +#define VMCLOCK_FLAG_PERIOD_MAXERROR_VALID (1 << 4) +#define VMCLOCK_FLAG_TIME_ESTERROR_VALID (1 << 5) +#define VMCLOCK_FLAG_TIME_MAXERROR_VALID (1 << 6) + /* + * If the MONOTONIC flag is set then (other than leap seconds) it is + * guaranteed that the time calculated according this structure at + * any given moment shall never appear to be later than the time + * calculated via the structure at any *later* moment. + * + * In particular, a timestamp based on a counter reading taken + * immediately after setting the low bit of seq_count (and the + * associated memory barrier), using the previously-valid time and + * period fields, shall never be later than a timestamp based on + * a counter reading taken immediately before *clearing* the low + * bit again after the update, using the about-to-be-valid fields. + */ +#define VMCLOCK_FLAG_TIME_MONOTONIC (1 << 7) + + uint8_t pad[2]; + uint8_t clock_status; +#define VMCLOCK_STATUS_UNKNOWN 0 +#define VMCLOCK_STATUS_INITIALIZING 1 +#define VMCLOCK_STATUS_SYNCHRONIZED 2 +#define VMCLOCK_STATUS_FREERUNNING 3 +#define VMCLOCK_STATUS_UNRELIABLE 4 + + /* + * The time exposed through this device is never smeared. This field + * corresponds to the 'subtype' field in virtio-rtc, which indicates + * the smearing method. However in this case it provides a *hint* to + * the guest operating system, such that *if* the guest OS wants to + * provide its users with an alternative clock which does not follow + * UTC, it may do so in a fashion consistent with the other systems + * in the nearby environment. + */ + uint8_t leap_second_smearing_hint; /* Matches VIRTIO_RTC_SUBTYPE_xxx */ +#define VMCLOCK_SMEARING_STRICT 0 +#define VMCLOCK_SMEARING_NOON_LINEAR 1 +#define VMCLOCK_SMEARING_UTC_SLS 2 + uint16_t tai_offset_sec; /* Actually two's complement signed */ + uint8_t leap_indicator; + /* + * This field is based on the VIRTIO_RTC_LEAP_xxx values as defined + * in the current draft of virtio-rtc, but since smearing cannot be + * used with the shared memory device, some values are not used. + * + * The _POST_POS and _POST_NEG values allow the guest to perform + * its own smearing during the day or so after a leap second when + * such smearing may need to continue being applied for a leap + * second which is now theoretically "historical". + */ +#define VMCLOCK_LEAP_NONE 0x00 /* No known nearby leap second */ +#define VMCLOCK_LEAP_PRE_POS 0x01 /* Positive leap second at EOM */ +#define VMCLOCK_LEAP_PRE_NEG 0x02 /* Negative leap second at EOM */ +#define VMCLOCK_LEAP_POS 0x03 /* Set during 23:59:60 second */ +#define VMCLOCK_LEAP_POST_POS 0x04 +#define VMCLOCK_LEAP_POST_NEG 0x05 + + /* Bit shift for counter_period_frac_sec and its error rate */ + uint8_t counter_period_shift; + /* + * Paired values of counter and UTC at a given point in time. + */ + uint64_t counter_value; + /* + * Counter period, and error margin of same. The unit of these + * fields is 1/2^(64 + counter_period_shift) of a second. + */ + uint64_t counter_period_frac_sec; + uint64_t counter_period_esterror_rate_frac_sec; + uint64_t counter_period_maxerror_rate_frac_sec; + + /* + * Time according to time_type field above. + */ + uint64_t time_sec; /* Seconds since time_type epoch */ + uint64_t time_frac_sec; /* Units of 1/2^64 of a second */ + uint64_t time_esterror_nanosec; + uint64_t time_maxerror_nanosec; +}; + +#endif /* __VMCLOCK_ABI_H__ */ diff --git a/include/standard-headers/misc/pvpanic.h b/include/standard-headers/misc/pvpanic.h new file mode 100644 index 00000000000..b1150944316 --- /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 c30182a7ae7..00000000000 --- 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/standard-headers/uefi/uefi.h b/include/standard-headers/uefi/uefi.h new file mode 100644 index 00000000000..5256349ec0b --- /dev/null +++ b/include/standard-headers/uefi/uefi.h @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2025 Intel Corporation + * + * Author: Isaku Yamahata + * + * Xiaoyao Li + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_I386_UEFI_H +#define HW_I386_UEFI_H + +/***************************************************************************/ +/* + * basic EFI definitions + * supplemented with UEFI Specification Version 2.8 (Errata A) + * released February 2020 + */ +/* UEFI integer is little endian */ + +typedef struct { + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; +} EFI_GUID; + +typedef enum { + EfiReservedMemoryType, + EfiLoaderCode, + EfiLoaderData, + EfiBootServicesCode, + EfiBootServicesData, + EfiRuntimeServicesCode, + EfiRuntimeServicesData, + EfiConventionalMemory, + EfiUnusableMemory, + EfiACPIReclaimMemory, + EfiACPIMemoryNVS, + EfiMemoryMappedIO, + EfiMemoryMappedIOPortSpace, + EfiPalCode, + EfiPersistentMemory, + EfiUnacceptedMemoryType, + EfiMaxMemoryType +} EFI_MEMORY_TYPE; + +#define EFI_HOB_HANDOFF_TABLE_VERSION 0x0009 + +#define EFI_HOB_TYPE_HANDOFF 0x0001 +#define EFI_HOB_TYPE_MEMORY_ALLOCATION 0x0002 +#define EFI_HOB_TYPE_RESOURCE_DESCRIPTOR 0x0003 +#define EFI_HOB_TYPE_GUID_EXTENSION 0x0004 +#define EFI_HOB_TYPE_FV 0x0005 +#define EFI_HOB_TYPE_CPU 0x0006 +#define EFI_HOB_TYPE_MEMORY_POOL 0x0007 +#define EFI_HOB_TYPE_FV2 0x0009 +#define EFI_HOB_TYPE_LOAD_PEIM_UNUSED 0x000A +#define EFI_HOB_TYPE_UEFI_CAPSULE 0x000B +#define EFI_HOB_TYPE_FV3 0x000C +#define EFI_HOB_TYPE_UNUSED 0xFFFE +#define EFI_HOB_TYPE_END_OF_HOB_LIST 0xFFFF + +typedef struct { + uint16_t HobType; + uint16_t HobLength; + uint32_t Reserved; +} EFI_HOB_GENERIC_HEADER; + +typedef uint64_t EFI_PHYSICAL_ADDRESS; +typedef uint32_t EFI_BOOT_MODE; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + uint32_t Version; + EFI_BOOT_MODE BootMode; + EFI_PHYSICAL_ADDRESS EfiMemoryTop; + EFI_PHYSICAL_ADDRESS EfiMemoryBottom; + EFI_PHYSICAL_ADDRESS EfiFreeMemoryTop; + EFI_PHYSICAL_ADDRESS EfiFreeMemoryBottom; + EFI_PHYSICAL_ADDRESS EfiEndOfHobList; +} EFI_HOB_HANDOFF_INFO_TABLE; + +#define EFI_RESOURCE_SYSTEM_MEMORY 0x00000000 +#define EFI_RESOURCE_MEMORY_MAPPED_IO 0x00000001 +#define EFI_RESOURCE_IO 0x00000002 +#define EFI_RESOURCE_FIRMWARE_DEVICE 0x00000003 +#define EFI_RESOURCE_MEMORY_MAPPED_IO_PORT 0x00000004 +#define EFI_RESOURCE_MEMORY_RESERVED 0x00000005 +#define EFI_RESOURCE_IO_RESERVED 0x00000006 +#define EFI_RESOURCE_MEMORY_UNACCEPTED 0x00000007 +#define EFI_RESOURCE_MAX_MEMORY_TYPE 0x00000008 + +#define EFI_RESOURCE_ATTRIBUTE_PRESENT 0x00000001 +#define EFI_RESOURCE_ATTRIBUTE_INITIALIZED 0x00000002 +#define EFI_RESOURCE_ATTRIBUTE_TESTED 0x00000004 +#define EFI_RESOURCE_ATTRIBUTE_SINGLE_BIT_ECC 0x00000008 +#define EFI_RESOURCE_ATTRIBUTE_MULTIPLE_BIT_ECC 0x00000010 +#define EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_1 0x00000020 +#define EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_2 0x00000040 +#define EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED 0x00000080 +#define EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED 0x00000100 +#define EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED 0x00000200 +#define EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE 0x00000400 +#define EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE 0x00000800 +#define EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE 0x00001000 +#define EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE 0x00002000 +#define EFI_RESOURCE_ATTRIBUTE_16_BIT_IO 0x00004000 +#define EFI_RESOURCE_ATTRIBUTE_32_BIT_IO 0x00008000 +#define EFI_RESOURCE_ATTRIBUTE_64_BIT_IO 0x00010000 +#define EFI_RESOURCE_ATTRIBUTE_UNCACHED_EXPORTED 0x00020000 +#define EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTED 0x00040000 +#define EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTABLE 0x00080000 +#define EFI_RESOURCE_ATTRIBUTE_READ_PROTECTABLE 0x00100000 +#define EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTABLE 0x00200000 +#define EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTABLE 0x00400000 +#define EFI_RESOURCE_ATTRIBUTE_PERSISTENT 0x00800000 +#define EFI_RESOURCE_ATTRIBUTE_PERSISTABLE 0x01000000 +#define EFI_RESOURCE_ATTRIBUTE_MORE_RELIABLE 0x02000000 + +typedef uint32_t EFI_RESOURCE_TYPE; +typedef uint32_t EFI_RESOURCE_ATTRIBUTE_TYPE; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_GUID Owner; + EFI_RESOURCE_TYPE ResourceType; + EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute; + EFI_PHYSICAL_ADDRESS PhysicalStart; + uint64_t ResourceLength; +} EFI_HOB_RESOURCE_DESCRIPTOR; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_GUID Name; + + /* guid specific data follows */ +} EFI_HOB_GUID_TYPE; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_PHYSICAL_ADDRESS BaseAddress; + uint64_t Length; +} EFI_HOB_FIRMWARE_VOLUME; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_PHYSICAL_ADDRESS BaseAddress; + uint64_t Length; + EFI_GUID FvName; + EFI_GUID FileName; +} EFI_HOB_FIRMWARE_VOLUME2; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_PHYSICAL_ADDRESS BaseAddress; + uint64_t Length; + uint32_t AuthenticationStatus; + bool ExtractedFv; + EFI_GUID FvName; + EFI_GUID FileName; +} EFI_HOB_FIRMWARE_VOLUME3; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + uint8_t SizeOfMemorySpace; + uint8_t SizeOfIoSpace; + uint8_t Reserved[6]; +} EFI_HOB_CPU; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; +} EFI_HOB_MEMORY_POOL; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + + EFI_PHYSICAL_ADDRESS BaseAddress; + uint64_t Length; +} EFI_HOB_UEFI_CAPSULE; + +#define EFI_HOB_OWNER_ZERO \ + ((EFI_GUID){ 0x00000000, 0x0000, 0x0000, \ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }) + +#endif diff --git a/include/sysemu/accel-ops.h b/include/sysemu/accel-ops.h deleted file mode 100644 index 3c1fab4b1e6..00000000000 --- a/include/sysemu/accel-ops.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Accelerator OPS, used for cpus.c module - * - * 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_OPS_H -#define ACCEL_OPS_H - -#include "exec/cpu-common.h" -#include "qom/object.h" - -#define ACCEL_OPS_SUFFIX "-ops" -#define TYPE_ACCEL_OPS "accel" ACCEL_OPS_SUFFIX -#define ACCEL_OPS_NAME(name) (name "-" TYPE_ACCEL_OPS) - -typedef struct AccelOpsClass AccelOpsClass; -DECLARE_CLASS_CHECKERS(AccelOpsClass, ACCEL_OPS, TYPE_ACCEL_OPS) - -/* cpus.c operations interface */ -struct AccelOpsClass { - /*< private >*/ - ObjectClass parent_class; - /*< public >*/ - - /* initialization function called when accel is chosen */ - void (*ops_init)(AccelOpsClass *ops); - - bool (*cpus_are_resettable)(void); - - void (*create_vcpu_thread)(CPUState *cpu); /* MANDATORY NON-NULL */ - void (*kick_vcpu_thread)(CPUState *cpu); - bool (*cpu_thread_is_idle)(CPUState *cpu); - - void (*synchronize_post_reset)(CPUState *cpu); - void (*synchronize_post_init)(CPUState *cpu); - void (*synchronize_state)(CPUState *cpu); - void (*synchronize_pre_loadvm)(CPUState *cpu); - void (*synchronize_pre_resume)(bool step_pending); - - void (*handle_interrupt)(CPUState *cpu, int mask); - - int64_t (*get_virtual_clock)(void); - int64_t (*get_elapsed_ticks)(void); - - /* gdbstub hooks */ - bool (*supports_guest_debug)(void); - int (*update_guest_debug)(CPUState *cpu); - int (*insert_breakpoint)(CPUState *cpu, int type, vaddr addr, vaddr len); - int (*remove_breakpoint)(CPUState *cpu, int type, vaddr addr, vaddr len); - void (*remove_all_breakpoints)(CPUState *cpu); -}; - -#endif /* ACCEL_OPS_H */ diff --git a/include/sysemu/cpu-timers.h b/include/sysemu/cpu-timers.h deleted file mode 100644 index 2e786fe7fb1..00000000000 --- a/include/sysemu/cpu-timers.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * CPU timers state API - * - * 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 SYSEMU_CPU_TIMERS_H -#define SYSEMU_CPU_TIMERS_H - -#include "qemu/timer.h" - -/* init the whole cpu timers API, including icount, ticks, and cpu_throttle */ -void cpu_timers_init(void); - -/* icount - Instruction Counter API */ - -/* - * 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 - */ -#ifdef CONFIG_TCG -extern int use_icount; -#define icount_enabled() (use_icount) -#else -#define icount_enabled() 0 -#endif - -/* - * Update the icount with the executed instructions. Called by - * cpus-tcg vCPU thread so the main-loop can see time has moved forward. - */ -void icount_update(CPUState *cpu); - -/* get raw icount value */ -int64_t icount_get_raw(void); - -/* return the virtual CPU time in ns, based on the instruction counter. */ -int64_t icount_get(void); -/* - * convert an instruction counter value to ns, based on the icount shift. - * This shift is set as a fixed value with the icount "shift" option - * (precise mode), or it is constantly approximated and corrected at - * runtime in adaptive mode. - */ -int64_t icount_to_ns(int64_t icount); - -/* configure the icount options, including "shift" */ -void icount_configure(QemuOpts *opts, Error **errp); - -/* used by tcg vcpu thread to calc icount budget */ -int64_t icount_round(int64_t count); - -/* if the CPUs are idle, start accounting real time to virtual clock. */ -void icount_start_warp_timer(void); -void icount_account_warp_timer(void); -void icount_notify_exit(void); - -/* - * CPU Ticks and Clock - */ - -/* Caller must hold BQL */ -void cpu_enable_ticks(void); -/* Caller must hold BQL */ -void cpu_disable_ticks(void); - -/* - * return the time elapsed in VM between vm_start and vm_stop. - * cpu_get_ticks() uses units of the host CPU cycle counter. - */ -int64_t cpu_get_ticks(void); - -/* - * Returns the monotonic time elapsed in VM, i.e., - * the time between vm_start and vm_stop - */ -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 */ -int64_t cpus_get_virtual_clock(void); -int64_t cpus_get_elapsed_ticks(void); - -#endif /* SYSEMU_CPU_TIMERS_H */ diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h deleted file mode 100644 index ee9025f8e9e..00000000000 --- a/include/sysemu/kvm.h +++ /dev/null @@ -1,573 +0,0 @@ -/* - * QEMU KVM support - * - * Copyright IBM, Corp. 2008 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -/* header to be included in non-KVM-specific code */ - -#ifndef QEMU_KVM_H -#define QEMU_KVM_H - -#include "exec/memattrs.h" -#include "qemu/accel.h" -#include "qom/object.h" - -#ifdef NEED_CPU_H -# ifdef CONFIG_KVM -# include -# define CONFIG_KVM_IS_POSSIBLE -# endif -#else -# define CONFIG_KVM_IS_POSSIBLE -#endif - -#ifdef CONFIG_KVM_IS_POSSIBLE - -extern bool kvm_allowed; -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) -/** - * kvm_irqchip_in_kernel: - * - * Returns: true if an in-kernel irqchip was created. - * What this actually means is architecture and machine model - * specific: on PC, for instance, it means that the LAPIC - * is in kernel. This function should never be used from generic - * target-independent code: use one of the following functions or - * some other specific check instead. - */ -#define kvm_irqchip_in_kernel() (kvm_kernel_irqchip) - -/** - * kvm_irqchip_is_split: - * - * Returns: true if the irqchip implementation is split between - * user and kernel space. The details are architecture and - * machine specific. On PC, it means that the PIC, IOAPIC, and - * PIT are in user space while the LAPIC is in the kernel. - */ -#define kvm_irqchip_is_split() (kvm_split_irqchip) - -/** - * kvm_async_interrupts_enabled: - * - * Returns: true if we can deliver interrupts to KVM - * asynchronously (ie by ioctl from any thread at any time) - * rather than having to do interrupt delivery synchronously - * (where the vcpu must be stopped at a suitable point first). - */ -#define kvm_async_interrupts_enabled() (kvm_async_interrupts_allowed) - -/** - * kvm_halt_in_kernel - * - * Returns: true if halted cpus should still get a KVM_RUN ioctl to run - * inside of kernel space. This only works if MP state is implemented. - */ -#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). - */ -#define kvm_irqfds_enabled() (kvm_irqfds_allowed) - -/** - * kvm_resamplefds_enabled: - * - * Returns: true if we can use resamplefds to inject interrupts into - * a KVM CPU (ie the kernel supports resamplefds and we are running - * with a configuration where it is meaningful to use them). - */ -#define kvm_resamplefds_enabled() (kvm_resamplefds_allowed) - -/** - * kvm_msi_via_irqfd_enabled: - * - * Returns: true if we can route a PCI MSI (Message Signaled Interrupt) - * to a KVM CPU via an irqfd. This requires that the kernel supports - * this and that we're running in a configuration that permits it. - */ -#define kvm_msi_via_irqfd_enabled() (kvm_msi_via_irqfd_allowed) - -/** - * kvm_gsi_routing_enabled: - * - * Returns: true if GSI routing is enabled (ie the kernel supports - * it and we're running in a configuration that permits it). - */ -#define kvm_gsi_routing_enabled() (kvm_gsi_routing_allowed) - -/** - * kvm_gsi_direct_mapping: - * - * Returns: true if GSI direct mapping is enabled. - */ -#define kvm_gsi_direct_mapping() (kvm_gsi_direct_mapping) - -/** - * kvm_readonly_mem_enabled: - * - * Returns: true if KVM readonly memory is enabled (ie the kernel - * supports it and we're running in a configuration that permits it). - */ -#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 - * defining an MSI routing entry. - */ -#define kvm_msi_devid_required() (kvm_msi_use_devid) - -#else - -#define kvm_enabled() (0) -#define kvm_irqchip_in_kernel() (false) -#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_irq_routing_entry; - -typedef struct KVMCapabilityInfo { - const char *name; - int value; -} KVMCapabilityInfo; - -#define KVM_CAP_INFO(CAP) { "KVM_CAP_" stringify(CAP), KVM_CAP_##CAP } -#define KVM_CAP_LAST_INFO { NULL, 0 } - -struct KVMState; - -#define TYPE_KVM_ACCEL ACCEL_CLASS_NAME("kvm") -typedef struct KVMState KVMState; -DECLARE_INSTANCE_CHECKER(KVMState, KVM_STATE, - TYPE_KVM_ACCEL) - -extern KVMState *kvm_state; -typedef struct Notifier Notifier; - -typedef struct KVMRouteChange { - KVMState *s; - int changes; -} KVMRouteChange; - -/* external API */ - -bool kvm_has_free_slot(MachineState *ms); -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_many_ioeventfds(void); -int kvm_has_gsi_routing(void); -int kvm_has_intx_set_mask(void); - -/** - * kvm_arm_supports_user_irq - * - * Not all KVM implementations support notifications for kernel generated - * interrupt events to user space. This function indicates whether the current - * KVM implementation does support them. - * - * Returns: true if KVM supports using kernel generated IRQs from user space - */ -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 -#include "cpu.h" - -void kvm_flush_coalesced_mmio_buffer(void); - -/** - * kvm_update_guest_debug(): ensure KVM debug structures updated - * @cs: the CPUState for this cpu - * @reinject_trap: KVM trap injection control - * - * There are usually per-arch specifics which will be handled by - * calling down to kvm_arch_update_guest_debug after the generic - * fields have been set. - */ -#ifdef KVM_CAP_SET_GUEST_DEBUG -int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap); -#else -static inline int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap) -{ - return -EINVAL; -} -#endif - -/* internal API */ - -int kvm_ioctl(KVMState *s, int type, ...); - -int kvm_vm_ioctl(KVMState *s, int type, ...); - -int kvm_vcpu_ioctl(CPUState *cpu, int type, ...); - -/** - * kvm_device_ioctl - call an ioctl on a kvm device - * @fd: The KVM device file descriptor as returned from KVM_CREATE_DEVICE - * @type: The device-ctrl ioctl number - * - * Returns: -errno on error, nonnegative on success - */ -int kvm_device_ioctl(int fd, int type, ...); - -/** - * kvm_vm_check_attr - check for existence of a specific vm attribute - * @s: The KVMState pointer - * @group: the group - * @attr: the attribute of that group to query for - * - * Returns: 1 if the attribute exists - * 0 if the attribute either does not exist or if the vm device - * interface is unavailable - */ -int kvm_vm_check_attr(KVMState *s, uint32_t group, uint64_t attr); - -/** - * kvm_device_check_attr - check for existence of a specific device attribute - * @fd: The device file descriptor - * @group: the group - * @attr: the attribute of that group to query for - * - * Returns: 1 if the attribute exists - * 0 if the attribute either does not exist or if the vm device - * interface is unavailable - */ -int kvm_device_check_attr(int fd, uint32_t group, uint64_t attr); - -/** - * kvm_device_access - set or get value of a specific device attribute - * @fd: The device file descriptor - * @group: the group - * @attr: the attribute of that group to set or get - * @val: pointer to a storage area for the value - * @write: true for set and false for get operation - * @errp: error object handle - * - * Returns: 0 on success - * < 0 on error - * Use kvm_device_check_attr() in order to check for the availability - * of optional attributes. - */ -int kvm_device_access(int fd, int group, uint64_t attr, - void *val, bool write, Error **errp); - -/** - * kvm_create_device - create a KVM device for the device control API - * @KVMState: The KVMState pointer - * @type: The KVM device type (see Documentation/virtual/kvm/devices in the - * kernel source) - * @test: If true, only test if device can be created, but don't actually - * create the device. - * - * Returns: -errno on error, nonnegative on success: @test ? 0 : device fd; - */ -int kvm_create_device(KVMState *s, uint64_t type, bool test); - -/** - * kvm_device_supported - probe whether KVM supports specific device - * - * @vmfd: The fd handler for VM - * @type: type of device - * - * @return: true if supported, otherwise false. - */ -bool kvm_device_supported(int vmfd, uint64_t type); - -/* Arch specific hooks */ - -extern const KVMCapabilityInfo kvm_arch_required_capabilities[]; - -void kvm_arch_accel_class_init(ObjectClass *oc); - -void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run); -MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run); - -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); - -/* state subset only touched by the VCPU itself during runtime */ -#define KVM_PUT_RUNTIME_STATE 1 -/* state subset modified during VCPU reset */ -#define KVM_PUT_RESET_STATE 2 -/* 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_get_default_type(MachineState *ms); - -int kvm_arch_init(MachineState *ms, KVMState *s); - -int kvm_arch_init_vcpu(CPUState *cpu); -int kvm_arch_destroy_vcpu(CPUState *cpu); - -bool kvm_vcpu_id_is_valid(int vcpu_id); - -/* Returns VCPU ID to be used on KVM_CREATE_VCPU ioctl() */ -unsigned long kvm_arch_vcpu_id(CPUState *cpu); - -#ifdef KVM_HAVE_MCE_INJECTION -void kvm_arch_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); -#endif - -void kvm_arch_init_irq_routing(KVMState *s); - -int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, - uint64_t address, uint32_t data, PCIDevice *dev); - -/* Notify arch about newly added MSI routes */ -int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route, - int vector, PCIDevice *dev); -/* Notify arch about released MSI routes */ -int kvm_arch_release_virq_post(int virq); - -int kvm_arch_msi_data_to_gsi(uint32_t data); - -int kvm_set_irq(KVMState *s, int irq, int level); -int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg); - -void kvm_irqchip_add_irq_route(KVMState *s, int gsi, int irqchip, int pin); - -void kvm_irqchip_add_change_notifier(Notifier *n); -void kvm_irqchip_remove_change_notifier(Notifier *n); -void kvm_irqchip_change_notify(void); - -struct kvm_guest_debug; -struct kvm_debug_exit_arch; - -struct kvm_sw_breakpoint { - vaddr pc; - vaddr saved_insn; - int use_count; - QTAILQ_ENTRY(kvm_sw_breakpoint) entry; -}; - -struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu, - vaddr pc); - -int kvm_sw_breakpoints_active(CPUState *cpu); - -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(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); - -bool kvm_arch_stop_on_emulation_error(CPUState *cpu); - -int kvm_check_extension(KVMState *s, unsigned int extension); - -int kvm_vm_check_extension(KVMState *s, unsigned int extension); - -#define kvm_vm_enable_cap(s, capability, cap_flags, ...) \ - ({ \ - struct kvm_enable_cap cap = { \ - .cap = capability, \ - .flags = cap_flags, \ - }; \ - uint64_t args_tmp[] = { __VA_ARGS__ }; \ - size_t n = MIN(ARRAY_SIZE(args_tmp), ARRAY_SIZE(cap.args)); \ - memcpy(cap.args, args_tmp, n * sizeof(cap.args[0])); \ - kvm_vm_ioctl(s, KVM_ENABLE_CAP, &cap); \ - }) - -#define kvm_vcpu_enable_cap(cpu, capability, cap_flags, ...) \ - ({ \ - struct kvm_enable_cap cap = { \ - .cap = capability, \ - .flags = cap_flags, \ - }; \ - uint64_t args_tmp[] = { __VA_ARGS__ }; \ - size_t n = MIN(ARRAY_SIZE(args_tmp), ARRAY_SIZE(cap.args)); \ - memcpy(cap.args, args_tmp, n * sizeof(cap.args[0])); \ - kvm_vcpu_ioctl(cpu, KVM_ENABLE_CAP, &cap); \ - }) - -void kvm_set_sigmask_len(KVMState *s, unsigned int sigmask_len); - -int kvm_physical_memory_addr_from_host(KVMState *s, void *ram_addr, - hwaddr *phys_addr); - -#endif /* NEED_CPU_H */ - -void kvm_cpu_synchronize_state(CPUState *cpu); - -void kvm_init_cpu_signals(CPUState *cpu); - -/** - * kvm_irqchip_add_msi_route - Add MSI route for specific vector - * @c: KVMRouteChange instance. - * @vector: which vector to add. This can be either MSI/MSIX - * vector. The function will automatically detect whether - * MSI/MSIX is enabled, and fetch corresponding MSI - * message. - * @dev: Owner PCI device to add the route. If @dev is specified - * as @NULL, an empty MSI message will be inited. - * @return: virq (>=0) when success, errno (<0) when failed. - */ -int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev); -int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg, - PCIDevice *dev); -void kvm_irqchip_commit_routes(KVMState *s); - -static inline KVMRouteChange kvm_irqchip_begin_route_changes(KVMState *s) -{ - return (KVMRouteChange) { .s = s, .changes = 0 }; -} - -static inline void kvm_irqchip_commit_route_changes(KVMRouteChange *c) -{ - if (c->changes) { - kvm_irqchip_commit_routes(c->s); - c->changes = 0; - } -} - -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); - -int kvm_irqchip_add_irqfd_notifier_gsi(KVMState *s, EventNotifier *n, - EventNotifier *rn, int virq); -int kvm_irqchip_remove_irqfd_notifier_gsi(KVMState *s, EventNotifier *n, - int virq); -int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n, - EventNotifier *rn, qemu_irq irq); -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_init_irq_routing(KVMState *s); - -bool kvm_kernel_irqchip_allowed(void); -bool kvm_kernel_irqchip_required(void); -bool kvm_kernel_irqchip_split(void); - -/** - * kvm_arch_irqchip_create: - * @KVMState: The KVMState pointer - * - * Allow architectures to create an in-kernel irq chip themselves. - * - * Returns: < 0: error - * 0: irq chip was not created - * > 0: irq chip was created - */ -int kvm_arch_irqchip_create(KVMState *s); - -/** - * kvm_set_one_reg - set a register value in KVM via KVM_SET_ONE_REG ioctl - * @id: The register ID - * @source: The pointer to the value to be set. It must point to a variable - * of the correct type/size for the register being accessed. - * - * Returns: 0 on success, or a negative errno on failure. - */ -int kvm_set_one_reg(CPUState *cs, uint64_t id, void *source); - -/** - * kvm_get_one_reg - get a register value from KVM via KVM_GET_ONE_REG ioctl - * @id: The register ID - * @target: The pointer where the value is to be stored. It must point to a - * variable of the correct type/size for the register being accessed. - * - * 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); -#endif diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h deleted file mode 100644 index a5b9122cb82..00000000000 --- a/include/sysemu/kvm_int.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Internal definitions for a target's KVM support - * - * 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_KVM_INT_H -#define QEMU_KVM_INT_H - -#include "exec/memory.h" -#include "qapi/qapi-types-common.h" -#include "qemu/accel.h" -#include "qemu/queue.h" -#include "sysemu/kvm.h" - -typedef struct KVMSlot -{ - hwaddr start_addr; - ram_addr_t memory_size; - void *ram; - int slot; - int flags; - int old_flags; - /* Dirty bitmap cache for the slot */ - unsigned long *dirty_bmap; - unsigned long dirty_bmap_size; - /* Cache of the address space ID */ - int as_id; - /* Cache of the offset in ram address space */ - ram_addr_t ram_start_offset; -} KVMSlot; - -typedef struct KVMMemoryUpdate { - QSIMPLEQ_ENTRY(KVMMemoryUpdate) next; - MemoryRegionSection section; -} KVMMemoryUpdate; - -typedef struct KVMMemoryListener { - MemoryListener listener; - KVMSlot *slots; - int as_id; - QSIMPLEQ_HEAD(, KVMMemoryUpdate) transaction_add; - QSIMPLEQ_HEAD(, KVMMemoryUpdate) transaction_del; -} KVMMemoryListener; - -#define KVM_MSI_HASHTAB_SIZE 256 - -enum KVMDirtyRingReaperState { - KVM_DIRTY_RING_REAPER_NONE = 0, - /* The reaper is sleeping */ - KVM_DIRTY_RING_REAPER_WAIT, - /* The reaper is reaping for dirty pages */ - KVM_DIRTY_RING_REAPER_REAPING, -}; - -/* - * KVM reaper instance, responsible for collecting the KVM dirty bits - * via the dirty ring. - */ -struct KVMDirtyRingReaper { - /* The reaper thread */ - QemuThread reaper_thr; - volatile uint64_t reaper_iteration; /* iteration number of reaper thr */ - volatile enum KVMDirtyRingReaperState reaper_state; /* reap thr state */ -}; -struct KVMState -{ - AccelState parent_obj; - - int nr_slots; - int fd; - int vmfd; - int coalesced_mmio; - int coalesced_pio; - struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; - bool coalesced_flush_in_progress; - int vcpu_events; - int robust_singlestep; - int debugregs; -#ifdef KVM_CAP_SET_GUEST_DEBUG - QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; -#endif - int max_nested_state_len; - int many_ioeventfds; - int intx_set_mask; - int kvm_shadow_mem; - bool kernel_irqchip_allowed; - bool kernel_irqchip_required; - OnOffAuto kernel_irqchip_split; - bool sync_mmu; - uint64_t manual_dirty_log_protect; - /* The man page (and posix) say ioctl numbers are signed int, but - * they're not. Linux, glibc and *BSD all treat ioctl numbers as - * unsigned, and treating them as signed here can break things */ - unsigned irq_set_ioctl; - unsigned int sigmask_len; - GHashTable *gsimap; -#ifdef KVM_CAP_IRQ_ROUTING - struct kvm_irq_routing *irq_routes; - int nr_allocated_irq_routes; - unsigned long *used_gsi_bitmap; - unsigned int gsi_count; - QTAILQ_HEAD(, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; -#endif - KVMMemoryListener memory_listener; - QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; - - /* For "info mtree -f" to tell if an MR is registered in KVM */ - int nr_as; - struct KVMAs { - KVMMemoryListener *ml; - AddressSpace *as; - } *as; - uint64_t kvm_dirty_ring_bytes; /* Size of the per-vcpu dirty ring */ - uint32_t kvm_dirty_ring_size; /* Number of dirty GFNs per ring */ - bool kvm_dirty_ring_with_bitmap; - uint64_t kvm_eager_split_size; /* Eager Page Splitting chunk size */ - struct KVMDirtyRingReaper reaper; - NotifyVmexitOption notify_vmexit; - uint32_t notify_window; - uint32_t xen_version; - uint32_t xen_caps; - uint16_t xen_gnttab_max_frames; - uint16_t xen_evtchn_max_pirq; -}; - -void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, - AddressSpace *as, int as_id, const char *name); - -void kvm_set_max_memslot_size(hwaddr max_slot_size); - -/** - * kvm_hwpoison_page_add: - * - * Parameters: - * @ram_addr: the address in the RAM for the poisoned page - * - * Add a poisoned page to the list - * - * Return: None. - */ -void kvm_hwpoison_page_add(ram_addr_t ram_addr); -#endif diff --git a/include/sysemu/reset.h b/include/sysemu/reset.h deleted file mode 100644 index 609e4d50c26..00000000000 --- a/include/sysemu/reset.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef QEMU_SYSEMU_RESET_H -#define QEMU_SYSEMU_RESET_H - -#include "qapi/qapi-events-run-state.h" - -typedef void QEMUResetHandler(void *opaque); - -void qemu_register_reset(QEMUResetHandler *func, void *opaque); -void qemu_register_reset_nosnapshotload(QEMUResetHandler *func, void *opaque); -void qemu_unregister_reset(QEMUResetHandler *func, void *opaque); -void qemu_devices_reset(ShutdownCause reason); - -#endif diff --git a/include/sysemu/runstate.h b/include/sysemu/runstate.h deleted file mode 100644 index 08afb97695b..00000000000 --- a/include/sysemu/runstate.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef SYSEMU_RUNSTATE_H -#define SYSEMU_RUNSTATE_H - -#include "qapi/qapi-types-run-state.h" -#include "qemu/notify.h" - -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); - -typedef void VMChangeStateHandler(void *opaque, bool running, RunState state); - -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 - * - * @running: whether the VM is running or not. - * @state: the #RunState of the VM. - */ -void vm_state_notify(bool running, RunState state); - -static inline bool shutdown_caused_by_guest(ShutdownCause cause) -{ - return cause >= SHUTDOWN_CAUSE_GUEST_SHUTDOWN; -} - -void vm_start(void); - -/** - * vm_prepare_start: Prepare for starting/resuming the VM - * - * @step_pending: whether any of the CPUs is about to be single-stepped by gdb - */ -int vm_prepare_start(bool step_pending); -int vm_stop(RunState state); -int vm_stop_force_state(RunState state); -int vm_shutdown(void); - -typedef enum WakeupReason { - /* Always keep QEMU_WAKEUP_REASON_NONE = 0 */ - QEMU_WAKEUP_REASON_NONE = 0, - QEMU_WAKEUP_REASON_RTC, - QEMU_WAKEUP_REASON_PMTIMER, - QEMU_WAKEUP_REASON_OTHER, -} WakeupReason; - -void qemu_system_reset_request(ShutdownCause reason); -void qemu_system_suspend_request(void); -void qemu_register_suspend_notifier(Notifier *notifier); -bool qemu_wakeup_suspend_enabled(void); -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(ShutdownCause reason); -void qemu_system_powerdown_request(void); -void qemu_register_powerdown_notifier(Notifier *notifier); -void qemu_register_shutdown_notifier(Notifier *notifier); -void qemu_system_debug_request(void); -void qemu_system_vmstop_request(RunState reason); -void qemu_system_vmstop_request_prepare(void); -bool qemu_vmstop_requested(RunState *r); -ShutdownCause qemu_shutdown_requested_get(void); -ShutdownCause qemu_reset_requested_get(void); -void qemu_system_killed(int signal, pid_t pid); -void qemu_system_reset(ShutdownCause reason); -void qemu_system_guest_panicked(GuestPanicInformation *info); -void qemu_system_guest_crashloaded(GuestPanicInformation *info); -bool qemu_system_dump_in_progress(void); - -#endif - diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h deleted file mode 100644 index 25be2a692e6..00000000000 --- a/include/sysemu/sysemu.h +++ /dev/null @@ -1,118 +0,0 @@ -#ifndef SYSEMU_H -#define SYSEMU_H -/* Misc. things related to the system emulator. */ - -#include "qemu/timer.h" -#include "qemu/notify.h" -#include "qemu/uuid.h" - -/* vl.c */ - -extern int only_migratable; -extern const char *qemu_name; -extern QemuUUID qemu_uuid; -extern bool qemu_uuid_set; - -const char *qemu_get_vm_name(void); - -void qemu_add_exit_notifier(Notifier *notify); -void qemu_remove_exit_notifier(Notifier *notify); - -void qemu_add_machine_init_done_notifier(Notifier *notify); -void qemu_remove_machine_init_done_notifier(Notifier *notify); - -void configure_rtc(QemuOpts *opts); - -void qemu_init_subsystems(void); - -extern int autostart; - -typedef enum { - VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL, - VGA_TCX, VGA_CG3, VGA_DEVICE, VGA_VIRTIO, - VGA_TYPE_MAX, -} VGAInterfaceType; - -extern int vga_interface_type; -extern bool vga_interface_created; - -extern int graphic_width; -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; -extern bool enable_cpu_pm; -extern QEMUClockType rtc_clock; - -#define MAX_OPTION_ROMS 16 -typedef struct QEMUOptionRom { - const char *name; - int32_t bootindex; -} QEMUOptionRom; -extern QEMUOptionRom option_rom[MAX_OPTION_ROMS]; -extern int nb_option_roms; - -#define MAX_PROM_ENVS 128 -extern const char *prom_envs[MAX_PROM_ENVS]; -extern unsigned int nb_prom_envs; - -/* serial ports */ - -/* Return the Chardev for serial port i, or NULL if none */ -Chardev *serial_hd(int i); - -/* parallel ports */ - -#define MAX_PARALLEL_PORTS 3 - -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); - -DeviceState *get_boot_device(uint32_t position); -void check_boot_index(int32_t bootindex, Error **errp); -void del_boot_device_path(DeviceState *dev, const char *suffix); -void device_add_bootindex_property(Object *obj, int32_t *bootindex, - const char *name, const char *suffix, - DeviceState *dev); -void restore_boot_order(void *opaque); -void validate_bootdevices(const char *devices, Error **errp); -void add_boot_device_lchs(DeviceState *dev, const char *suffix, - uint32_t lcyls, uint32_t lheads, uint32_t lsecs); -void del_boot_device_lchs(DeviceState *dev, const char *suffix); -char *get_boot_devices_lchs_list(size_t *size); - -/* handler to set the boot_device order for a specific type of MachineClass */ -typedef void QEMUBootSetHandler(void *opaque, const char *boot_order, - Error **errp); -void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque); -void qemu_boot_set(const char *boot_order, Error **errp); - -bool defaults_enabled(void); - -void qemu_init(int argc, char **argv); -int qemu_main_loop(void); -void qemu_cleanup(void); - -extern QemuOptsList qemu_legacy_drive_opts; -extern QemuOptsList qemu_common_drive_opts; -extern QemuOptsList qemu_drive_opts; -extern QemuOptsList bdrv_runtime_opts; -extern QemuOptsList qemu_chardev_opts; -extern QemuOptsList qemu_device_opts; -extern QemuOptsList qemu_netdev_opts; -extern QemuOptsList qemu_nic_opts; -extern QemuOptsList qemu_net_opts; -extern QemuOptsList qemu_global_opts; -extern QemuOptsList qemu_semihosting_config_opts; - -#endif diff --git a/include/sysemu/tcg.h b/include/sysemu/tcg.h deleted file mode 100644 index 5e2ca9aab3d..00000000000 --- a/include/sysemu/tcg.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * QEMU TCG support - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * 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 - -#ifdef CONFIG_TCG -extern bool tcg_allowed; -#define tcg_enabled() (tcg_allowed) -#else -#define tcg_enabled() 0 -#endif - -#endif diff --git a/include/sysemu/xen-mapcache.h b/include/sysemu/xen-mapcache.h deleted file mode 100644 index c8e7c2f6cf5..00000000000 --- a/include/sysemu/xen-mapcache.h +++ /dev/null @@ -1,65 +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. - * - */ - -#ifndef XEN_MAPCACHE_H -#define XEN_MAPCACHE_H - -#include "exec/cpu-common.h" - -typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr phys_offset, - ram_addr_t size); -#ifdef CONFIG_XEN - -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); -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); -uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr, - hwaddr new_phys_addr, - hwaddr size); -#else - -static inline void xen_map_cache_init(phys_offset_to_gaddr_t f, - void *opaque) -{ -} - -static inline uint8_t *xen_map_cache(hwaddr phys_addr, - hwaddr size, - uint8_t lock, - bool dma) -{ - abort(); -} - -static inline ram_addr_t xen_ram_addr_from_mapcache(void *ptr) -{ - abort(); -} - -static inline void xen_invalidate_map_cache_entry(uint8_t *buffer) -{ -} - -static inline void xen_invalidate_map_cache(void) -{ -} - -static inline uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr, - hwaddr new_phys_addr, - hwaddr size) -{ - abort(); -} - -#endif - -#endif /* XEN_MAPCACHE_H */ diff --git a/include/sysemu/xen.h b/include/sysemu/xen.h deleted file mode 100644 index bc13ad56924..00000000000 --- a/include/sysemu/xen.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * QEMU Xen support - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * 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 - -#include "exec/cpu-common.h" - -#ifdef NEED_CPU_H -# ifdef CONFIG_XEN -# define CONFIG_XEN_IS_POSSIBLE -# endif -#else -# define CONFIG_XEN_IS_POSSIBLE -#endif - -#ifdef CONFIG_XEN_IS_POSSIBLE - -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 */ -} -static inline void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, - MemoryRegion *mr, Error **errp) -{ - g_assert_not_reached(); -} -#endif - -#endif /* CONFIG_XEN_IS_POSSIBLE */ - -#endif diff --git a/include/sysemu/accel-blocker.h b/include/system/accel-blocker.h similarity index 98% rename from include/sysemu/accel-blocker.h rename to include/system/accel-blocker.h index f07f3683585..e10099d6a9a 100644 --- a/include/sysemu/accel-blocker.h +++ b/include/system/accel-blocker.h @@ -14,7 +14,7 @@ #ifndef ACCEL_BLOCKER_H #define ACCEL_BLOCKER_H -#include "sysemu/cpus.h" +#include "system/cpus.h" void accel_blocker_init(void); diff --git a/include/system/accel-ops.h b/include/system/accel-ops.h new file mode 100644 index 00000000000..4c99d25aeff --- /dev/null +++ b/include/system/accel-ops.h @@ -0,0 +1,73 @@ +/* + * Accelerator OPS, used for cpus.c module + * + * 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_OPS_H +#define ACCEL_OPS_H + +#include "exec/vaddr.h" +#include "qom/object.h" + +#define ACCEL_OPS_SUFFIX "-ops" +#define TYPE_ACCEL_OPS "accel" ACCEL_OPS_SUFFIX +#define ACCEL_OPS_NAME(name) (name "-" TYPE_ACCEL_OPS) + +DECLARE_CLASS_CHECKERS(AccelOpsClass, ACCEL_OPS, TYPE_ACCEL_OPS) + +/** + * 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; + /*< public >*/ + + /* initialization function called when accel is chosen */ + 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); + bool (*cpu_thread_is_idle)(CPUState *cpu); + + void (*synchronize_post_reset)(CPUState *cpu); + void (*synchronize_post_init)(CPUState *cpu); + void (*synchronize_state)(CPUState *cpu); + void (*synchronize_pre_loadvm)(CPUState *cpu); + void (*synchronize_pre_resume)(bool step_pending); + + 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 (*update_guest_debug)(CPUState *cpu); + int (*insert_breakpoint)(CPUState *cpu, int type, vaddr addr, vaddr len); + int (*remove_breakpoint)(CPUState *cpu, int type, vaddr addr, vaddr len); + void (*remove_all_breakpoints)(CPUState *cpu); +}; + +#endif /* ACCEL_OPS_H */ diff --git a/include/exec/address-spaces.h b/include/system/address-spaces.h similarity index 89% rename from include/exec/address-spaces.h rename to include/system/address-spaces.h index 0d0aa61d689..72d17afb0ff 100644 --- a/include/exec/address-spaces.h +++ b/include/system/address-spaces.h @@ -11,16 +11,14 @@ * */ -#ifndef EXEC_ADDRESS_SPACES_H -#define EXEC_ADDRESS_SPACES_H +#ifndef SYSTEM_ADDRESS_SPACES_H +#define SYSTEM_ADDRESS_SPACES_H /* * Internal interfaces between memory.c/exec.c/vl.c. Do not #include unless * you're one of them. */ -#ifndef CONFIG_USER_ONLY - /* Get the root memory region. This interface should only be used temporarily * until a proper bus interface is available. */ @@ -35,5 +33,3 @@ extern AddressSpace address_space_memory; extern AddressSpace address_space_io; #endif - -#endif diff --git a/include/sysemu/arch_init.h b/include/system/arch_init.h similarity index 84% rename from include/sysemu/arch_init.h rename to include/system/arch_init.h index 8850cb1a14b..51e24c3091e 100644 --- a/include/sysemu/arch_init.h +++ b/include/system/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), @@ -27,8 +25,6 @@ enum { QEMU_ARCH_LOONGARCH = (1 << 23), }; -extern const uint32_t arch_type; - -void qemu_init_arch_modules(void); +bool qemu_arch_available(unsigned qemu_arch_mask); #endif diff --git a/include/sysemu/balloon.h b/include/system/balloon.h similarity index 100% rename from include/sysemu/balloon.h rename to include/system/balloon.h diff --git a/include/sysemu/block-backend-common.h b/include/system/block-backend-common.h similarity index 100% rename from include/sysemu/block-backend-common.h rename to include/system/block-backend-common.h diff --git a/include/sysemu/block-backend-global-state.h b/include/system/block-backend-global-state.h similarity index 87% rename from include/sysemu/block-backend-global-state.h rename to include/system/block-backend-global-state.h index 184e667ebd8..35b5e8380b9 100644 --- a/include/sysemu/block-backend-global-state.h +++ b/include/system/block-backend-global-state.h @@ -54,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); @@ -76,8 +74,6 @@ 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); @@ -90,10 +86,6 @@ bool blk_supports_write_perm(BlockBackend *blk); 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, @@ -105,7 +97,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/system/block-backend-io.h similarity index 98% rename from include/sysemu/block-backend-io.h rename to include/system/block-backend-io.h index be4dcef59d1..ba8dfcc7d04 100644 --- a/include/sysemu/block-backend-io.h +++ b/include/system/block-backend-io.h @@ -32,6 +32,13 @@ void blk_set_allow_aio_context_change(BlockBackend *blk, bool allow); void blk_set_disable_request_queuing(BlockBackend *blk, bool disable); bool blk_iostatus_is_enabled(const BlockBackend *blk); +/* + * Return the qdev ID, or if no ID is assigned the QOM path, + * of the block device attached to the BlockBackend. + * + * The caller is responsible for releasing the value returned + * with g_free() after use. + */ char *blk_get_attached_dev_id(BlockBackend *blk); BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, @@ -100,10 +107,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(void); -void blk_io_unplug(void); -void blk_io_plug_call(void (*fn)(void *), void *opaque); - AioContext *blk_get_aio_context(BlockBackend *blk); BlockAcctStats *blk_get_stats(BlockBackend *blk); void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, diff --git a/include/sysemu/block-backend.h b/include/system/block-backend.h similarity index 100% rename from include/sysemu/block-backend.h rename to include/system/block-backend.h diff --git a/include/sysemu/block-ram-registrar.h b/include/system/block-ram-registrar.h similarity index 100% rename from include/sysemu/block-ram-registrar.h rename to include/system/block-ram-registrar.h diff --git a/include/sysemu/blockdev.h b/include/system/blockdev.h similarity index 100% rename from include/sysemu/blockdev.h rename to include/system/blockdev.h diff --git a/include/system/confidential-guest-support.h b/include/system/confidential-guest-support.h new file mode 100644 index 00000000000..ea46b50c56c --- /dev/null +++ b/include/system/confidential-guest-support.h @@ -0,0 +1,95 @@ +/* + * QEMU Confidential Guest support + * This interface describes the common pieces between various + * schemes for protecting guest memory or other state against a + * compromised hypervisor. This includes memory encryption (AMD's + * SEV and Intel's MKTME) or special protection modes (PEF on POWER, + * or PV on s390x). + * + * Copyright Red Hat. + * + * Authors: + * David Gibson + * + * 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_CONFIDENTIAL_GUEST_SUPPORT_H +#define QEMU_CONFIDENTIAL_GUEST_SUPPORT_H + +#include "qom/object.h" + +#define TYPE_CONFIDENTIAL_GUEST_SUPPORT "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 + * guest + * + * The definition here is a bit fuzzy, because this is essentially + * part of a self-sanity-check, rather than a strict mechanism. + * + * It's not feasible to have a single point in the common machine + * init path to configure confidential guest support, because + * different mechanisms have different interdependencies requiring + * initialization in different places, often in arch or machine + * type specific code. It's also usually not possible to check + * for invalid configurations until that initialization code. + * That means it would be very easy to have a bug allowing CGS + * init to be bypassed entirely in certain configurations. + * + * Silently ignoring a requested security feature would be bad, so + * to avoid that we check late in init that this 'ready' flag is + * set if CGS was requested. If the CGS init hasn't happened, and + * so 'ready' is not set, we'll abort. + */ + bool ready; +}; + +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 /* QEMU_CONFIDENTIAL_GUEST_SUPPORT_H */ diff --git a/include/sysemu/cpu-throttle.h b/include/system/cpu-throttle.h similarity index 83% rename from include/sysemu/cpu-throttle.h rename to include/system/cpu-throttle.h index d65bdef6d03..44bf6a53893 100644 --- a/include/sysemu/cpu-throttle.h +++ b/include/system/cpu-throttle.h @@ -16,8 +16,8 @@ * */ -#ifndef SYSEMU_CPU_THROTTLE_H -#define SYSEMU_CPU_THROTTLE_H +#ifndef SYSTEM_CPU_THROTTLE_H +#define SYSTEM_CPU_THROTTLE_H #include "qemu/timer.h" @@ -65,4 +65,18 @@ bool cpu_throttle_active(void); */ int cpu_throttle_get_percentage(void); -#endif /* SYSEMU_CPU_THROTTLE_H */ +/** + * 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 /* SYSTEM_CPU_THROTTLE_H */ diff --git a/softmmu/timers-state.h b/include/system/cpu-timers-internal.h similarity index 100% rename from softmmu/timers-state.h rename to include/system/cpu-timers-internal.h diff --git a/include/system/cpu-timers.h b/include/system/cpu-timers.h new file mode 100644 index 00000000000..a1abed0d7a0 --- /dev/null +++ b/include/system/cpu-timers.h @@ -0,0 +1,46 @@ +/* + * CPU timers state API + * + * 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 SYSTEM_CPU_TIMERS_H +#define SYSTEM_CPU_TIMERS_H + +#include "qemu/timer.h" + +/* init the whole cpu timers API, including icount, ticks, and cpu_throttle */ +void cpu_timers_init(void); + +/* + * CPU Ticks and Clock + */ + +/* Caller must hold BQL */ +void cpu_enable_ticks(void); +/* Caller must hold BQL */ +void cpu_disable_ticks(void); + +/* + * return the time elapsed in VM between vm_start and vm_stop. + * cpu_get_ticks() uses units of the host CPU cycle counter. + */ +int64_t cpu_get_ticks(void); + +/* + * Returns the monotonic time elapsed in VM, i.e., + * the time between vm_start and vm_stop + */ +int64_t cpu_get_clock(void); + +void qemu_timer_notify_cb(void *opaque, QEMUClockType type); + +/* 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 /* SYSTEM_CPU_TIMERS_H */ diff --git a/include/sysemu/cpus.h b/include/system/cpus.h similarity index 86% rename from include/sysemu/cpus.h rename to include/system/cpus.h index 0535a4c68a3..3226c765d01 100644 --- a/include/sysemu/cpus.h +++ b/include/system/cpus.h @@ -1,8 +1,6 @@ #ifndef QEMU_CPUS_H #define QEMU_CPUS_H -#include "sysemu/accel-ops.h" - /* register accel-specific operations */ void cpus_register_accel(const AccelOpsClass *i); @@ -38,8 +36,6 @@ void resume_all_vcpus(void); void pause_all_vcpus(void); void cpu_stop_current(void); -extern int icount_align_option; - /* Unblock cpu */ void qemu_cpu_kick_self(void); @@ -50,11 +46,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-user.h b/include/system/cryptodev-vhost-user.h similarity index 97% rename from include/sysemu/cryptodev-vhost-user.h rename to include/system/cryptodev-vhost-user.h index 60710502c2d..5138c146fa2 100644 --- a/include/sysemu/cryptodev-vhost-user.h +++ b/include/system/cryptodev-vhost-user.h @@ -24,7 +24,7 @@ #ifndef CRYPTODEV_VHOST_USER_H #define CRYPTODEV_VHOST_USER_H -#include "sysemu/cryptodev-vhost.h" +#include "system/cryptodev-vhost.h" #define VHOST_USER_MAX_AUTH_KEY_LEN 512 #define VHOST_USER_MAX_CIPHER_KEY_LEN 64 diff --git a/include/sysemu/cryptodev-vhost.h b/include/system/cryptodev-vhost.h similarity index 99% rename from include/sysemu/cryptodev-vhost.h rename to include/system/cryptodev-vhost.h index 4c3c22acae5..b0bb09e70a3 100644 --- a/include/sysemu/cryptodev-vhost.h +++ b/include/system/cryptodev-vhost.h @@ -28,7 +28,7 @@ #include "hw/virtio/vhost-backend.h" #include "chardev/char.h" -#include "sysemu/cryptodev.h" +#include "system/cryptodev.h" typedef struct CryptoDevBackendVhostOptions { diff --git a/include/sysemu/cryptodev.h b/include/system/cryptodev.h similarity index 99% rename from include/sysemu/cryptodev.h rename to include/system/cryptodev.h index 96d3998b935..b20822df0dc 100644 --- a/include/sysemu/cryptodev.h +++ b/include/system/cryptodev.h @@ -178,7 +178,7 @@ typedef struct CryptoDevBackendAsymOpInfo { typedef void (*CryptoDevCompletionFunc) (void *opaque, int ret); typedef struct CryptoDevBackendOpInfo { - QCryptodevBackendAlgType algtype; + QCryptodevBackendAlgoType algtype; uint32_t op_code; uint32_t queue_index; CryptoDevCompletionFunc cb; diff --git a/include/sysemu/device_tree.h b/include/system/device_tree.h similarity index 95% rename from include/sysemu/device_tree.h rename to include/system/device_tree.h index ca5339beae8..49d8482ed4e 100644 --- a/include/sysemu/device_tree.h +++ b/include/system/device_tree.h @@ -126,18 +126,13 @@ 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: * @fdt: device tree blob diff --git a/include/sysemu/dirtylimit.h b/include/system/dirtylimit.h similarity index 100% rename from include/sysemu/dirtylimit.h rename to include/system/dirtylimit.h diff --git a/include/sysemu/dirtyrate.h b/include/system/dirtyrate.h similarity index 100% rename from include/sysemu/dirtyrate.h rename to include/system/dirtyrate.h diff --git a/include/sysemu/dma.h b/include/system/dma.h similarity index 95% rename from include/sysemu/dma.h rename to include/system/dma.h index a1ac5bc1b54..82e7ad54374 100644 --- a/include/sysemu/dma.h +++ b/include/system/dma.h @@ -10,8 +10,8 @@ #ifndef DMA_H #define DMA_H -#include "exec/memory.h" -#include "exec/address-spaces.h" +#include "system/memory.h" +#include "system/address-spaces.h" #include "block/block.h" #include "block/accounting.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 */ @@ -291,8 +290,7 @@ typedef BlockAIOCB *DMAIOFunc(int64_t offset, QEMUIOVector *iov, BlockCompletionFunc *cb, void *cb_opaque, void *opaque); -BlockAIOCB *dma_blk_io(AioContext *ctx, - QEMUSGList *sg, uint64_t offset, uint32_t align, +BlockAIOCB *dma_blk_io(QEMUSGList *sg, uint64_t offset, uint32_t align, DMAIOFunc *io_func, void *io_func_opaque, BlockCompletionFunc *cb, void *opaque, DMADirection dir); BlockAIOCB *dma_blk_read(BlockBackend *blk, diff --git a/include/sysemu/dump-arch.h b/include/system/dump-arch.h similarity index 96% rename from include/sysemu/dump-arch.h rename to include/system/dump-arch.h index 59bbc9be38c..743916e46ca 100644 --- a/include/sysemu/dump-arch.h +++ b/include/system/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/system/dump.h similarity index 98% rename from include/sysemu/dump.h rename to include/system/dump.h index 7008d43d04e..607bd7b220e 100644 --- a/include/sysemu/dump.h +++ b/include/system/dump.h @@ -39,8 +39,8 @@ #define DUMP_LEVEL (1) #define DISKDUMP_HEADER_BLOCKS (1) -#include "sysemu/dump-arch.h" -#include "sysemu/memory_mapping.h" +#include "system/dump-arch.h" +#include "system/memory_mapping.h" typedef struct QEMU_PACKED MakedumpfileHeader { char signature[16]; /* = "makedumpfile" */ @@ -137,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 */ @@ -157,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/system/event-loop-base.h similarity index 100% rename from include/sysemu/event-loop-base.h rename to include/system/event-loop-base.h diff --git a/include/system/host_iommu_device.h b/include/system/host_iommu_device.h new file mode 100644 index 00000000000..ab849a4a82d --- /dev/null +++ b/include/system/host_iommu_device.h @@ -0,0 +1,125 @@ +/* + * 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" +#ifdef CONFIG_LINUX +#include "linux/iommufd.h" + +typedef union VendorCaps { + struct iommu_hw_info_vtd vtd; + struct iommu_hw_info_arm_smmuv3 smmuv3; +} VendorCaps; + +/** + * 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) + * + * @vendor_caps: host platform IOMMU vendor specific capabilities (e.g. on + * IOMMUFD this represents a user-space buffer filled by kernel + * with host IOMMU @type specific hardware information data) + */ +typedef struct HostIOMMUDeviceCaps { + uint32_t type; + uint64_t hw_caps; + VendorCaps vendor_caps; +} HostIOMMUDeviceCaps; +#endif + +#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; +#ifdef CONFIG_LINUX + HostIOMMUDeviceCaps caps; +#endif +}; + +/** + * 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/system/hostmem.h similarity index 78% rename from include/sysemu/hostmem.h rename to include/system/hostmem.h index 39326f1d4f9..88fa791ac78 100644 --- a/include/sysemu/hostmem.h +++ b/include/system/hostmem.h @@ -10,13 +10,13 @@ * See the COPYING file in the top-level directory. */ -#ifndef SYSEMU_HOSTMEM_H -#define SYSEMU_HOSTMEM_H +#ifndef SYSTEM_HOSTMEM_H +#define SYSTEM_HOSTMEM_H -#include "sysemu/numa.h" +#include "system/numa.h" #include "qapi/qapi-types-machine.h" #include "qom/object.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/bitmap.h" #include "qemu/thread-context.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); @@ -82,4 +93,7 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend); size_t host_memory_backend_pagesize(HostMemoryBackend *memdev); char *host_memory_backend_get_name(HostMemoryBackend *backend); +long qemu_minrampagesize(void); +long qemu_maxrampagesize(void); + #endif diff --git a/include/sysemu/hvf.h b/include/system/hvf.h similarity index 80% rename from include/sysemu/hvf.h rename to include/system/hvf.h index 4037cd6a738..a9a502f0c8f 100644 --- a/include/sysemu/hvf.h +++ b/include/system/hvf.h @@ -14,22 +14,25 @@ #define HVF_H #include "qemu/accel.h" +#include "qemu/queue.h" +#include "exec/vaddr.h" #include "qom/object.h" +#include "exec/vaddr.h" -#ifdef NEED_CPU_H -#include "cpu.h" +#ifdef COMPILING_PER_TARGET +# ifdef CONFIG_HVF +# define CONFIG_HVF_IS_POSSIBLE +# endif /* !CONFIG_HVF */ +#else +# define CONFIG_HVF_IS_POSSIBLE +#endif /* COMPILING_PER_TARGET */ -#ifdef CONFIG_HVF -uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, - int reg); +#ifdef CONFIG_HVF_IS_POSSIBLE extern bool hvf_allowed; #define hvf_enabled() (hvf_allowed) -#else /* !CONFIG_HVF */ +#else /* !CONFIG_HVF_IS_POSSIBLE */ #define hvf_enabled() 0 -#define hvf_get_supported_cpuid(func, idx, reg) 0 -#endif /* !CONFIG_HVF */ - -#endif /* NEED_CPU_H */ +#endif /* !CONFIG_HVF_IS_POSSIBLE */ #define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf") @@ -37,7 +40,7 @@ typedef struct HVFState HVFState; DECLARE_INSTANCE_CHECKER(HVFState, HVF_STATE, TYPE_HVF_ACCEL) -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET struct hvf_sw_breakpoint { vaddr pc; vaddr saved_insn; @@ -69,6 +72,6 @@ void hvf_arch_update_guest_debug(CPUState *cpu); * Return whether the guest supports debugging. */ bool hvf_arch_supports_guest_debug(void); -#endif /* NEED_CPU_H */ +#endif /* COMPILING_PER_TARGET */ #endif diff --git a/include/sysemu/hvf_int.h b/include/system/hvf_int.h similarity index 77% rename from include/sysemu/hvf_int.h rename to include/system/hvf_int.h index 718beddcdde..d774e58df91 100644 --- a/include/sysemu/hvf_int.h +++ b/include/system/hvf_int.h @@ -11,10 +11,14 @@ #ifndef HVF_INT_H #define HVF_INT_H +#include "qemu/queue.h" + #ifdef __aarch64__ #include +typedef hv_vcpu_t hvf_vcpuid; #else #include +typedef hv_vcpuid_t hvf_vcpuid; #endif /* hvf_slot flags */ @@ -40,6 +44,7 @@ typedef struct hvf_vcpu_caps { struct HVFState { AccelState parent; + hvf_slot slots[32]; int num_slots; @@ -50,15 +55,20 @@ struct HVFState { extern HVFState *hvf_state; struct AccelCPUState { - uint64_t fd; + 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/system/hw_accel.h similarity index 83% rename from include/sysemu/hw_accel.h rename to include/system/hw_accel.h index c71b77e71f3..380e9e640b6 100644 --- a/include/sysemu/hw_accel.h +++ b/include/system/hw_accel.h @@ -12,10 +12,10 @@ #define QEMU_HW_ACCEL_H #include "hw/core/cpu.h" -#include "sysemu/kvm.h" -#include "sysemu/hvf.h" -#include "sysemu/whpx.h" -#include "sysemu/nvmm.h" +#include "system/kvm.h" +#include "system/hvf.h" +#include "system/whpx.h" +#include "system/nvmm.h" void cpu_synchronize_state(CPUState *cpu); void cpu_synchronize_post_reset(CPUState *cpu); diff --git a/include/system/iommufd.h b/include/system/iommufd.h new file mode 100644 index 00000000000..283861b9243 --- /dev/null +++ b/include/system/iommufd.h @@ -0,0 +1,120 @@ +/* + * 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 SYSTEM_IOMMUFD_H +#define SYSTEM_IOMMUFD_H + +#include "qom/object.h" +#include "exec/hwaddr.h" +#include "exec/cpu-common.h" +#include "system/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); +bool iommufd_backend_invalidate_cache(IOMMUFDBackend *be, uint32_t id, + uint32_t data_type, uint32_t entry_len, + uint32_t *entry_num, void *data, + Error **errp); + +#define TYPE_HOST_IOMMU_DEVICE_IOMMUFD TYPE_HOST_IOMMU_DEVICE "-iommufd" +OBJECT_DECLARE_TYPE(HostIOMMUDeviceIOMMUFD, HostIOMMUDeviceIOMMUFDClass, + HOST_IOMMU_DEVICE_IOMMUFD) + +/* Overload of the host IOMMU device for the iommufd backend */ +struct HostIOMMUDeviceIOMMUFD { + HostIOMMUDevice parent_obj; + + IOMMUFDBackend *iommufd; + uint32_t devid; + uint32_t hwpt_id; +}; + +struct HostIOMMUDeviceIOMMUFDClass { + HostIOMMUDeviceClass parent_class; + + /** + * @attach_hwpt: attach host IOMMU device to IOMMUFD hardware page table. + * VFIO and VDPA device can have different implementation. + * + * Mandatory callback. + * + * @idev: host IOMMU device backed by IOMMUFD backend. + * + * @hwpt_id: ID of IOMMUFD hardware page table. + * + * @errp: pass an Error out when attachment fails. + * + * Returns: true on success, false on failure. + */ + bool (*attach_hwpt)(HostIOMMUDeviceIOMMUFD *idev, uint32_t hwpt_id, + Error **errp); + /** + * @detach_hwpt: detach host IOMMU device from IOMMUFD hardware page table. + * VFIO and VDPA device can have different implementation. + * + * Mandatory callback. + * + * @idev: host IOMMU device backed by IOMMUFD backend. + * + * @errp: pass an Error out when attachment fails. + * + * Returns: true on success, false on failure. + */ + bool (*detach_hwpt)(HostIOMMUDeviceIOMMUFD *idev, Error **errp); +}; + +bool host_iommu_device_iommufd_attach_hwpt(HostIOMMUDeviceIOMMUFD *idev, + uint32_t hwpt_id, Error **errp); +bool host_iommu_device_iommufd_detach_hwpt(HostIOMMUDeviceIOMMUFD *idev, + Error **errp); +#endif diff --git a/include/exec/ioport.h b/include/system/ioport.h similarity index 90% rename from include/exec/ioport.h rename to include/system/ioport.h index e34f668998d..780ea5a6768 100644 --- a/include/exec/ioport.h +++ b/include/system/ioport.h @@ -21,10 +21,10 @@ * IO ports API */ -#ifndef IOPORT_H -#define IOPORT_H +#ifndef SYSTEM_IOPORT_H +#define SYSTEM_IOPORT_H -#include "exec/memory.h" +#include "system/memory.h" #define MAX_IOPORTS (64 * 1024) #define IOPORTS_MASK (MAX_IOPORTS - 1) @@ -35,14 +35,11 @@ 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() { } -#ifndef CONFIG_USER_ONLY extern const MemoryRegionOps unassigned_io_ops; -#endif void cpu_outb(uint32_t addr, uint8_t val); void cpu_outw(uint32_t addr, uint16_t val); @@ -55,6 +52,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 +69,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/sysemu/iothread.h b/include/system/iothread.h similarity index 97% rename from include/sysemu/iothread.h rename to include/system/iothread.h index 2102a90eca6..d95c17a6456 100644 --- a/include/sysemu/iothread.h +++ b/include/system/iothread.h @@ -17,7 +17,7 @@ #include "block/aio.h" #include "qemu/thread.h" #include "qom/object.h" -#include "sysemu/event-loop-base.h" +#include "system/event-loop-base.h" #define TYPE_IOTHREAD "iothread" diff --git a/include/system/kvm.h b/include/system/kvm.h new file mode 100644 index 00000000000..7cc60d26f24 --- /dev/null +++ b/include/system/kvm.h @@ -0,0 +1,589 @@ +/* + * QEMU KVM support + * + * Copyright IBM, Corp. 2008 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +/* header to be included in non-KVM-specific code */ + +#ifndef QEMU_KVM_H +#define QEMU_KVM_H + +#include "exec/memattrs.h" +#include "qemu/accel.h" +#include "qom/object.h" + +#ifdef COMPILING_PER_TARGET +# ifdef CONFIG_KVM +# include +# define CONFIG_KVM_IS_POSSIBLE +# endif +#else +# define CONFIG_KVM_IS_POSSIBLE +#endif + +#ifdef CONFIG_KVM_IS_POSSIBLE + +extern bool kvm_allowed; +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_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_msi_use_devid; +extern bool kvm_pre_fault_memory_supported; + +#define kvm_enabled() (kvm_allowed) +/** + * kvm_irqchip_in_kernel: + * + * Returns: true if an in-kernel irqchip was created. + * What this actually means is architecture and machine model + * specific: on PC, for instance, it means that the LAPIC + * is in kernel. This function should never be used from generic + * target-independent code: use one of the following functions or + * some other specific check instead. + */ +#define kvm_irqchip_in_kernel() (kvm_kernel_irqchip) + +/** + * kvm_irqchip_is_split: + * + * Returns: true if the irqchip implementation is split between + * user and kernel space. The details are architecture and + * machine specific. On PC, it means that the PIC, IOAPIC, and + * PIT are in user space while the LAPIC is in the kernel. + */ +#define kvm_irqchip_is_split() (kvm_split_irqchip) + +/** + * kvm_async_interrupts_enabled: + * + * Returns: true if we can deliver interrupts to KVM + * asynchronously (ie by ioctl from any thread at any time) + * rather than having to do interrupt delivery synchronously + * (where the vcpu must be stopped at a suitable point first). + */ +#define kvm_async_interrupts_enabled() (kvm_async_interrupts_allowed) + +/** + * kvm_halt_in_kernel + * + * Returns: true if halted cpus should still get a KVM_RUN ioctl to run + * inside of kernel space. This only works if MP state is implemented. + */ +#define kvm_halt_in_kernel() (kvm_halt_in_kernel_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_irqchip_in_kernel() + +/** + * kvm_resamplefds_enabled: + * + * Returns: true if we can use resamplefds to inject interrupts into + * a KVM CPU (ie the kernel supports resamplefds and we are running + * with a configuration where it is meaningful to use them). + */ +#define kvm_resamplefds_enabled() (kvm_resamplefds_allowed) + +/** + * kvm_msi_via_irqfd_enabled: + * + * Returns: true if we can route a PCI MSI (Message Signaled Interrupt) + * to a KVM CPU via an irqfd. This requires that the kernel supports + * this and that we're running in a configuration that permits it. + */ +#define kvm_msi_via_irqfd_enabled() (kvm_msi_via_irqfd_allowed) + +/** + * kvm_gsi_routing_enabled: + * + * Returns: true if GSI routing is enabled (ie the kernel supports + * it and we're running in a configuration that permits it). + */ +#define kvm_gsi_routing_enabled() (kvm_gsi_routing_allowed) + +/** + * kvm_gsi_direct_mapping: + * + * Returns: true if GSI direct mapping is enabled. + */ +#define kvm_gsi_direct_mapping() (kvm_gsi_direct_mapping) + +/** + * kvm_readonly_mem_enabled: + * + * Returns: true if KVM readonly memory is enabled (ie the kernel + * supports it and we're running in a configuration that permits it). + */ +#define kvm_readonly_mem_enabled() (kvm_readonly_mem_allowed) + +/** + * kvm_msi_devid_required: + * Returns: true if KVM requires a device id to be provided while + * defining an MSI routing entry. + */ +#define kvm_msi_devid_required() (kvm_msi_use_devid) + +#else + +#define kvm_enabled() (0) +#define kvm_irqchip_in_kernel() (false) +#define kvm_irqchip_is_split() (false) +#define kvm_async_interrupts_enabled() (false) +#define kvm_halt_in_kernel() (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_msi_devid_required() (false) + +#endif /* CONFIG_KVM_IS_POSSIBLE */ + +struct kvm_run; +struct kvm_irq_routing_entry; + +typedef struct KVMCapabilityInfo { + const char *name; + int value; +} KVMCapabilityInfo; + +#define KVM_CAP_INFO(CAP) { "KVM_CAP_" stringify(CAP), KVM_CAP_##CAP } +#define KVM_CAP_LAST_INFO { NULL, 0 } + +struct KVMState; + +#define TYPE_KVM_ACCEL ACCEL_CLASS_NAME("kvm") +typedef struct KVMState KVMState; +DECLARE_INSTANCE_CHECKER(KVMState, KVM_STATE, + TYPE_KVM_ACCEL) + +extern KVMState *kvm_state; +typedef struct Notifier Notifier; + +typedef struct KVMRouteChange { + KVMState *s; + int changes; +} KVMRouteChange; + +/* external API */ + +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_max_nested_state_length(void); +int kvm_has_gsi_routing(void); + +/** + * kvm_arm_supports_user_irq + * + * Not all KVM implementations support notifications for kernel generated + * interrupt events to user space. This function indicates whether the current + * KVM implementation does support them. + * + * Returns: true if KVM supports using kernel generated IRQs from user space + */ +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); + +int kvm_check_extension(KVMState *s, unsigned int extension); + +int kvm_vm_ioctl(KVMState *s, unsigned long type, ...); + +void kvm_flush_coalesced_mmio_buffer(void); + +#ifdef COMPILING_PER_TARGET +#include "cpu.h" + +/** + * kvm_update_guest_debug(): ensure KVM debug structures updated + * @cs: the CPUState for this cpu + * @reinject_trap: KVM trap injection control + * + * There are usually per-arch specifics which will be handled by + * calling down to kvm_arch_update_guest_debug after the generic + * fields have been set. + */ +#ifdef 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) +{ + return -EINVAL; +} +#endif + +/* internal API */ + +int kvm_ioctl(KVMState *s, unsigned long type, ...); + +int kvm_vcpu_ioctl(CPUState *cpu, unsigned long type, ...); + +/** + * kvm_device_ioctl - call an ioctl on a kvm device + * @fd: The KVM device file descriptor as returned from KVM_CREATE_DEVICE + * @type: The device-ctrl ioctl number + * + * Returns: -errno on error, nonnegative on success + */ +int kvm_device_ioctl(int fd, unsigned long type, ...); + +/** + * kvm_vm_check_attr - check for existence of a specific vm attribute + * @s: The KVMState pointer + * @group: the group + * @attr: the attribute of that group to query for + * + * Returns: 1 if the attribute exists + * 0 if the attribute either does not exist or if the vm device + * interface is unavailable + */ +int kvm_vm_check_attr(KVMState *s, uint32_t group, uint64_t attr); + +/** + * kvm_device_check_attr - check for existence of a specific device attribute + * @fd: The device file descriptor + * @group: the group + * @attr: the attribute of that group to query for + * + * Returns: 1 if the attribute exists + * 0 if the attribute either does not exist or if the vm device + * interface is unavailable + */ +int kvm_device_check_attr(int fd, uint32_t group, uint64_t attr); + +/** + * kvm_device_access - set or get value of a specific device attribute + * @fd: The device file descriptor + * @group: the group + * @attr: the attribute of that group to set or get + * @val: pointer to a storage area for the value + * @write: true for set and false for get operation + * @errp: error object handle + * + * Returns: 0 on success + * < 0 on error + * Use kvm_device_check_attr() in order to check for the availability + * of optional attributes. + */ +int kvm_device_access(int fd, int group, uint64_t attr, + void *val, bool write, Error **errp); + +/** + * kvm_create_device - create a KVM device for the device control API + * @KVMState: The KVMState pointer + * @type: The KVM device type (see Documentation/virtual/kvm/devices in the + * kernel source) + * @test: If true, only test if device can be created, but don't actually + * create the device. + * + * Returns: -errno on error, nonnegative on success: @test ? 0 : device fd; + */ +int kvm_create_device(KVMState *s, uint64_t type, bool test); + +/** + * kvm_device_supported - probe whether KVM supports specific device + * + * @vmfd: The fd handler for VM + * @type: type of device + * + * @return: true if supported, otherwise false. + */ +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[]; + +void kvm_arch_accel_class_init(ObjectClass *oc); + +void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run); +MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run); + +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, Error **errp); + +/* state subset only touched by the VCPU itself during runtime */ +#define KVM_PUT_RUNTIME_STATE 1 +/* state subset modified during VCPU reset */ +#define KVM_PUT_RESET_STATE 2 +/* full state set, modified during initialization or on vmload */ +#define KVM_PUT_FULL_STATE 3 + +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); + +int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp); +int kvm_arch_init_vcpu(CPUState *cpu); +int kvm_arch_destroy_vcpu(CPUState *cpu); + +#ifdef TARGET_KVM_HAVE_RESET_PARKED_VCPU +void kvm_arch_reset_parked_vcpu(unsigned long vcpu_id, int kvm_fd); +#else +static inline void kvm_arch_reset_parked_vcpu(unsigned long vcpu_id, int kvm_fd) +{ +} +#endif + +bool kvm_vcpu_id_is_valid(int vcpu_id); + +/* Returns VCPU ID to be used on KVM_CREATE_VCPU ioctl() */ +unsigned long kvm_arch_vcpu_id(CPUState *cpu); + +void kvm_arch_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); + +void kvm_arch_init_irq_routing(KVMState *s); + +int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, + uint64_t address, uint32_t data, PCIDevice *dev); + +/* Notify arch about newly added MSI routes */ +int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route, + int vector, PCIDevice *dev); +/* Notify arch about released MSI routes */ +int kvm_arch_release_virq_post(int virq); + +int kvm_arch_msi_data_to_gsi(uint32_t data); + +int kvm_set_irq(KVMState *s, int irq, int level); +int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg); + +void kvm_irqchip_add_irq_route(KVMState *s, int gsi, int irqchip, int pin); + +void kvm_irqchip_add_change_notifier(Notifier *n); +void kvm_irqchip_remove_change_notifier(Notifier *n); +void kvm_irqchip_change_notify(void); + +struct kvm_guest_debug; +struct kvm_debug_exit_arch; + +struct kvm_sw_breakpoint { + vaddr pc; + vaddr saved_insn; + int use_count; + QTAILQ_ENTRY(kvm_sw_breakpoint) entry; +}; + +struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu, + vaddr pc); + +int kvm_sw_breakpoints_active(CPUState *cpu); + +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(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); + +bool kvm_arch_stop_on_emulation_error(CPUState *cpu); + +int kvm_vm_check_extension(KVMState *s, unsigned int extension); + +#define kvm_vm_enable_cap(s, capability, cap_flags, ...) \ + ({ \ + struct kvm_enable_cap cap = { \ + .cap = capability, \ + .flags = cap_flags, \ + }; \ + uint64_t args_tmp[] = { __VA_ARGS__ }; \ + size_t n = MIN(ARRAY_SIZE(args_tmp), ARRAY_SIZE(cap.args)); \ + memcpy(cap.args, args_tmp, n * sizeof(cap.args[0])); \ + kvm_vm_ioctl(s, KVM_ENABLE_CAP, &cap); \ + }) + +#define kvm_vcpu_enable_cap(cpu, capability, cap_flags, ...) \ + ({ \ + struct kvm_enable_cap cap = { \ + .cap = capability, \ + .flags = cap_flags, \ + }; \ + uint64_t args_tmp[] = { __VA_ARGS__ }; \ + size_t n = MIN(ARRAY_SIZE(args_tmp), ARRAY_SIZE(cap.args)); \ + memcpy(cap.args, args_tmp, n * sizeof(cap.args[0])); \ + kvm_vcpu_ioctl(cpu, KVM_ENABLE_CAP, &cap); \ + }) + +void kvm_set_sigmask_len(KVMState *s, unsigned int sigmask_len); + +int kvm_physical_memory_addr_from_host(KVMState *s, void *ram_addr, + hwaddr *phys_addr); + +#endif /* COMPILING_PER_TARGET */ + +void kvm_cpu_synchronize_state(CPUState *cpu); + +void kvm_init_cpu_signals(CPUState *cpu); + +/** + * kvm_irqchip_add_msi_route - Add MSI route for specific vector + * @c: KVMRouteChange instance. + * @vector: which vector to add. This can be either MSI/MSIX + * vector. The function will automatically detect whether + * MSI/MSIX is enabled, and fetch corresponding MSI + * message. + * @dev: Owner PCI device to add the route. If @dev is specified + * as @NULL, an empty MSI message will be inited. + * @return: virq (>=0) when success, errno (<0) when failed. + */ +int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev); +int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg, + PCIDevice *dev); +void kvm_irqchip_commit_routes(KVMState *s); + +static inline KVMRouteChange kvm_irqchip_begin_route_changes(KVMState *s) +{ + return (KVMRouteChange) { .s = s, .changes = 0 }; +} + +static inline void kvm_irqchip_commit_route_changes(KVMRouteChange *c) +{ + if (c->changes) { + kvm_irqchip_commit_routes(c->s); + c->changes = 0; + } +} + +int kvm_irqchip_get_virq(KVMState *s); +void kvm_irqchip_release_virq(KVMState *s, int virq); + +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); +int kvm_irqchip_remove_irqfd_notifier_gsi(KVMState *s, EventNotifier *n, + int virq); +int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n, + EventNotifier *rn, qemu_irq irq); +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_init_irq_routing(KVMState *s); + +bool kvm_kernel_irqchip_allowed(void); +bool kvm_kernel_irqchip_required(void); +bool kvm_kernel_irqchip_split(void); + +/** + * kvm_arch_irqchip_create: + * @KVMState: The KVMState pointer + * + * Allow architectures to create an in-kernel irq chip themselves. + * + * Returns: < 0: error + * 0: irq chip was not created + * > 0: irq chip was created + */ +int kvm_arch_irqchip_create(KVMState *s); + +/** + * kvm_set_one_reg - set a register value in KVM via KVM_SET_ONE_REG ioctl + * @id: The register ID + * @source: The pointer to the value to be set. It must point to a variable + * of the correct type/size for the register being accessed. + * + * Returns: 0 on success, or a negative errno on failure. + */ +int kvm_set_one_reg(CPUState *cs, uint64_t id, void *source); + +/** + * kvm_get_one_reg - get a register value from KVM via KVM_GET_ONE_REG ioctl + * @id: The register ID + * @target: The pointer where the value is to be stored. It must point to a + * variable of the correct type/size for the register being accessed. + * + * Returns: 0 on success, or a negative errno on failure. + */ +int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target); + +/* Notify resamplefd for EOI of specific interrupts. */ +void kvm_resample_fd_notify(int gsi); + +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/system/kvm_int.h b/include/system/kvm_int.h new file mode 100644 index 00000000000..756a3c0a250 --- /dev/null +++ b/include/system/kvm_int.h @@ -0,0 +1,187 @@ +/* + * Internal definitions for a target's KVM support + * + * 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_KVM_INT_H +#define QEMU_KVM_INT_H + +#include "system/memory.h" +#include "qapi/qapi-types-common.h" +#include "qemu/accel.h" +#include "qemu/queue.h" +#include "system/kvm.h" +#include "hw/boards.h" +#include "hw/i386/topology.h" +#include "io/channel-socket.h" + +typedef struct KVMSlot +{ + hwaddr start_addr; + ram_addr_t memory_size; + void *ram; + int slot; + int flags; + int old_flags; + /* Dirty bitmap cache for the slot */ + unsigned long *dirty_bmap; + unsigned long dirty_bmap_size; + /* Cache of the address space ID */ + 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 */ + KVM_DIRTY_RING_REAPER_WAIT, + /* The reaper is reaping for dirty pages */ + KVM_DIRTY_RING_REAPER_REAPING, +}; + +/* + * KVM reaper instance, responsible for collecting the KVM dirty bits + * via the dirty ring. + */ +struct KVMDirtyRingReaper { + /* The reaper thread */ + QemuThread reaper_thr; + volatile uint64_t reaper_iteration; /* iteration number of reaper thr */ + volatile enum KVMDirtyRingReaperState reaper_state; /* reap thr state */ +}; +struct KVMState +{ + AccelState parent_obj; + /* Max number of KVM slots supported */ + int nr_slots_max; + int fd; + int vmfd; + int coalesced_mmio; + int coalesced_pio; + struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; + bool coalesced_flush_in_progress; + int vcpu_events; +#ifdef TARGET_KVM_HAVE_GUEST_DEBUG + QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; +#endif + int max_nested_state_len; + int kvm_shadow_mem; + bool kernel_irqchip_allowed; + bool kernel_irqchip_required; + OnOffAuto kernel_irqchip_split; + bool sync_mmu; + bool guest_state_protected; + uint64_t manual_dirty_log_protect; + /* + * 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 + struct kvm_irq_routing *irq_routes; + int nr_allocated_irq_routes; + unsigned long *used_gsi_bitmap; + unsigned int gsi_count; +#endif + KVMMemoryListener memory_listener; + QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; + + /* For "info mtree -f" to tell if an MR is registered in KVM */ + int nr_as; + struct KVMAs { + KVMMemoryListener *ml; + AddressSpace *as; + } *as; + uint64_t kvm_dirty_ring_bytes; /* Size of the per-vcpu dirty ring */ + uint32_t kvm_dirty_ring_size; /* Number of dirty GFNs per ring */ + bool kvm_dirty_ring_with_bitmap; + uint64_t kvm_eager_split_size; /* Eager Page Splitting chunk size */ + struct KVMDirtyRingReaper reaper; + 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, + AddressSpace *as, int as_id, const char *name); + +void kvm_set_max_memslot_size(hwaddr max_slot_size); + +/** + * kvm_hwpoison_page_add: + * + * Parameters: + * @ram_addr: the address in the RAM for the poisoned page + * + * Add a poisoned page to the list + * + * Return: None. + */ +void kvm_hwpoison_page_add(ram_addr_t ram_addr); +#endif diff --git a/include/sysemu/kvm_xen.h b/include/system/kvm_xen.h similarity index 90% rename from include/sysemu/kvm_xen.h rename to include/system/kvm_xen.h index 595abfbe402..7d0e69f1334 100644 --- a/include/sysemu/kvm_xen.h +++ b/include/system/kvm_xen.h @@ -9,8 +9,8 @@ * */ -#ifndef QEMU_SYSEMU_KVM_XEN_H -#define QEMU_SYSEMU_KVM_XEN_H +#ifndef QEMU_SYSTEM_KVM_XEN_H +#define QEMU_SYSTEM_KVM_XEN_H /* The KVM API uses these to indicate "no GPA" or "no GFN" */ #define INVALID_GPA UINT64_MAX @@ -22,6 +22,7 @@ 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); @@ -40,4 +41,4 @@ uint16_t kvm_xen_get_evtchn_max_pirq(void); #define XEN_SPECIAL_PFN(x) ((XEN_SPECIAL_AREA_ADDR >> TARGET_PAGE_BITS) + \ XEN_SPECIALPAGE_##x) -#endif /* QEMU_SYSEMU_KVM_XEN_H */ +#endif /* QEMU_SYSTEM_KVM_XEN_H */ diff --git a/include/exec/memory.h b/include/system/memory.h similarity index 89% rename from include/exec/memory.h rename to include/system/memory.h index 68284428f87..46248d4a52c 100644 --- a/include/exec/memory.h +++ b/include/system/memory.h @@ -11,19 +11,19 @@ * */ -#ifndef MEMORY_H -#define MEMORY_H - -#ifndef CONFIG_USER_ONLY +#ifndef SYSTEM_MEMORY_H +#define SYSTEM_MEMORY_H #include "exec/cpu-common.h" #include "exec/hwaddr.h" #include "exec/memattrs.h" #include "exec/memop.h" #include "exec/ramlist.h" +#include "exec/tswap.h" #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 +42,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 +79,7 @@ extern unsigned int global_dirty_tracking; typedef struct MemoryRegionOps MemoryRegionOps; struct ReservedRegion { - hwaddr low; - hwaddr high; + Range range; unsigned type; }; @@ -95,6 +94,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 +104,7 @@ struct MemoryRegionSection { hwaddr offset_within_address_space; bool readonly; bool nonvolatile; + bool unmergeable; }; typedef struct IOMMUTLBEntry IOMMUTLBEntry; @@ -182,6 +183,7 @@ struct IOMMUNotifier { hwaddr start; hwaddr end; int iommu_idx; + void *opaque; QLIST_ENTRY(IOMMUNotifier) node; }; typedef struct IOMMUNotifier IOMMUNotifier; @@ -235,6 +237,25 @@ typedef struct IOMMUTLBEvent { /* 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) + +/* + * In RAMBlock creation functions, if MAP_SHARED is 0 in the flags parameter, + * the implementation may still create a shared mapping if other conditions + * require it. Callers who specifically want a private mapping, eg objects + * specified by the user, must pass RAM_PRIVATE. + * After RAMBlock creation, MAP_SHARED in the block's flags indicates whether + * the block is shared or private, and MAP_PRIVATE is omitted. + */ +#define RAM_PRIVATE (1 << 13) + static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, IOMMUNotifierFlag flags, hwaddr start, hwaddr end, @@ -493,32 +514,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; @@ -581,8 +576,20 @@ static inline void ram_discard_listener_init(RamDiscardListener *rdl, rdl->double_discard_supported = double_discard_supported; } -typedef int (*ReplayRamPopulate)(MemoryRegionSection *section, void *opaque); -typedef void (*ReplayRamDiscard)(MemoryRegionSection *section, void *opaque); +/** + * typedef ReplayRamDiscardState: + * + * The callback handler for #RamDiscardManagerClass.replay_populated/ + * #RamDiscardManagerClass.replay_discarded to invoke on populated/discarded + * parts. + * + * @section: the #MemoryRegionSection of populated/discarded part + * @opaque: pointer to forward to the callback + * + * Returns 0 on success, or a negative error if failed. + */ +typedef int (*ReplayRamDiscardState)(MemoryRegionSection *section, + void *opaque); /* * RamDiscardManagerClass: @@ -593,8 +600,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 @@ -655,36 +663,38 @@ struct RamDiscardManagerClass { /** * @replay_populated: * - * Call the #ReplayRamPopulate callback for all populated parts within the - * #MemoryRegionSection via the #RamDiscardManager. + * Call the #ReplayRamDiscardState callback for all populated parts within + * the #MemoryRegionSection via the #RamDiscardManager. * * In case any call fails, no further calls are made. * * @rdm: the #RamDiscardManager * @section: the #MemoryRegionSection - * @replay_fn: the #ReplayRamPopulate callback + * @replay_fn: the #ReplayRamDiscardState callback * @opaque: pointer to forward to the callback * * Returns 0 on success, or a negative error if any notification failed. */ int (*replay_populated)(const RamDiscardManager *rdm, MemoryRegionSection *section, - ReplayRamPopulate replay_fn, void *opaque); + ReplayRamDiscardState replay_fn, void *opaque); /** * @replay_discarded: * - * Call the #ReplayRamDiscard callback for all discarded parts within the - * #MemoryRegionSection via the #RamDiscardManager. + * Call the #ReplayRamDiscardState callback for all discarded parts within + * the #MemoryRegionSection via the #RamDiscardManager. * * @rdm: the #RamDiscardManager * @section: the #MemoryRegionSection - * @replay_fn: the #ReplayRamDiscard callback + * @replay_fn: the #ReplayRamDiscardState callback * @opaque: pointer to forward to the callback + * + * Returns 0 on success, or a negative error if any notification failed. */ - void (*replay_discarded)(const RamDiscardManager *rdm, - MemoryRegionSection *section, - ReplayRamDiscard replay_fn, void *opaque); + int (*replay_discarded)(const RamDiscardManager *rdm, + MemoryRegionSection *section, + ReplayRamDiscardState replay_fn, void *opaque); /** * @register_listener: @@ -725,15 +735,41 @@ uint64_t ram_discard_manager_get_min_granularity(const RamDiscardManager *rdm, bool ram_discard_manager_is_populated(const RamDiscardManager *rdm, const MemoryRegionSection *section); +/** + * ram_discard_manager_replay_populated: + * + * A wrapper to call the #RamDiscardManagerClass.replay_populated callback + * of the #RamDiscardManager. + * + * @rdm: the #RamDiscardManager + * @section: the #MemoryRegionSection + * @replay_fn: the #ReplayRamDiscardState callback + * @opaque: pointer to forward to the callback + * + * Returns 0 on success, or a negative error if any notification failed. + */ int ram_discard_manager_replay_populated(const RamDiscardManager *rdm, MemoryRegionSection *section, - ReplayRamPopulate replay_fn, + ReplayRamDiscardState replay_fn, void *opaque); -void ram_discard_manager_replay_discarded(const RamDiscardManager *rdm, - MemoryRegionSection *section, - ReplayRamDiscard replay_fn, - void *opaque); +/** + * ram_discard_manager_replay_discarded: + * + * A wrapper to call the #RamDiscardManagerClass.replay_discarded callback + * of the #RamDiscardManager. + * + * @rdm: the #RamDiscardManager + * @section: the #MemoryRegionSection + * @replay_fn: the #ReplayRamDiscardState callback + * @opaque: pointer to forward to the callback + * + * Returns 0 on success, or a negative error if any notification failed. + */ +int ram_discard_manager_replay_discarded(const RamDiscardManager *rdm, + MemoryRegionSection *section, + ReplayRamDiscardState replay_fn, + void *opaque); void ram_discard_manager_register_listener(RamDiscardManager *rdm, RamDiscardListener *rdl, @@ -742,9 +778,21 @@ void ram_discard_manager_register_listener(RamDiscardManager *rdm, void ram_discard_manager_unregister_listener(RamDiscardManager *rdm, RamDiscardListener *rdl); -bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, - ram_addr_t *ram_addr, bool *read_only, - bool *mr_has_discard_manager); +/** + * memory_translate_iotlb: Extract addresses from a TLB entry. + * Called with rcu_read_lock held. + * + * @iotlb: pointer to an #IOMMUTLBEntry + * @xlat_p: return the offset of the entry from the start of the returned + * MemoryRegion. + * @errp: pointer to Error*, to store an error if it happens. + * + * Return: On success, return the MemoryRegion containing the @iotlb translated + * addr. The MemoryRegion must not be accessed after rcu_read_unlock. + * On failure, return NULL, setting @errp with error. + */ +MemoryRegion *memory_translate_iotlb(IOMMUTLBEntry *iotlb, hwaddr *xlat_p, + Error **errp); typedef struct CoalescedMemoryRange CoalescedMemoryRange; typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd; @@ -766,6 +814,7 @@ struct MemoryRegion { bool nonvolatile; bool rom_device; bool flush_coalesced_mmio; + bool unmergeable; uint8_t dirty_log_mask; bool is_iommu; RAMBlock *ram_block; @@ -784,7 +833,6 @@ struct MemoryRegion { bool terminates; bool ram_device; bool enabled; - bool warning_printed; /* For reservations */ uint8_t vga_logging_count; MemoryRegion *alias; hwaddr alias_offset; @@ -899,7 +947,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: @@ -918,7 +966,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: @@ -968,8 +1016,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: @@ -1076,6 +1127,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 */ @@ -1089,9 +1147,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; @@ -1168,7 +1242,7 @@ static inline bool MemoryRegionSection_eq(MemoryRegionSection *a, MemoryRegionSection *memory_region_section_new_copy(MemoryRegionSection *s); /** - * memory_region_section_new_copy: Free a copied memory region section + * memory_region_section_free_copy: Free a copied memory region section * * Free a copy of a memory section created via memory_region_section_new_copy(). * properly dropping references on all relevant members. @@ -1177,6 +1251,36 @@ MemoryRegionSection *memory_region_section_new_copy(MemoryRegionSection *s); */ void memory_region_section_free_copy(MemoryRegionSection *s); +/** + * memory_region_section_intersect_range: Adjust the memory section to cover + * the intersection with the given range. + * + * @s: the #MemoryRegionSection to be adjusted + * @offset: the offset of the given range in the memory region + * @size: the size of the given range + * + * Returns false if the intersection is empty, otherwise returns true. + */ +static inline bool memory_region_section_intersect_range(MemoryRegionSection *s, + uint64_t offset, + uint64_t size) +{ + uint64_t start = MAX(s->offset_within_region, offset); + Int128 end = int128_min(int128_add(int128_make64(s->offset_within_region), + s->size), + int128_add(int128_make64(offset), + int128_make64(size))); + + if (int128_le(end, int128_make64(start))) { + return false; + } + + s->offset_within_address_space += start - s->offset_within_region; + s->offset_within_region = start; + s->size = int128_sub(end, int128_make64(start)); + return true; +} + /** * memory_region_init: Initialize a memory region * @@ -1257,8 +1361,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, @@ -1274,13 +1380,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, @@ -1307,8 +1416,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, @@ -1331,16 +1442,18 @@ 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. * @offset: offset within the file referenced by path - * @readonly: true to open @path for reading, false for read/write. * @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, @@ -1348,7 +1461,6 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, uint32_t ram_flags, const char *path, ram_addr_t offset, - bool readonly, Error **errp); /** @@ -1360,15 +1472,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, @@ -1463,8 +1578,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, @@ -1486,8 +1603,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, @@ -1545,13 +1664,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. * @@ -1572,8 +1699,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, @@ -1603,8 +1732,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, @@ -1671,6 +1802,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 * @@ -1731,7 +1872,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 @@ -1746,7 +1887,7 @@ 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 @@ -1792,7 +1933,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. */ @@ -1833,18 +1974,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 * @@ -1919,7 +2048,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. * @@ -1936,7 +2065,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. * @@ -2102,7 +2231,7 @@ void memory_region_flush_rom_device(MemoryRegion *mr, hwaddr addr, hwaddr size); * only useful on RAM regions. * * @mr: the region being updated. - * @readonly: whether rhe region is to be ROM or RAM. + * @readonly: whether the region is to be ROM or RAM. */ void memory_region_set_readonly(MemoryRegion *mr, bool readonly); @@ -2113,7 +2242,7 @@ void memory_region_set_readonly(MemoryRegion *mr, bool readonly); * only useful on RAM regions. * * @mr: the region being updated. - * @nonvolatile: whether rhe region is to be non-volatile. + * @nonvolatile: whether the region is to be non-volatile. */ void memory_region_set_nonvolatile(MemoryRegion *mr, bool nonvolatile); @@ -2343,6 +2472,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 @@ -2391,13 +2539,13 @@ static inline bool memory_region_has_ram_discard_manager(MemoryRegion *mr) * * This function must not be called for a mapped #MemoryRegion, a #MemoryRegion * that does not cover RAM, or a #MemoryRegion that already has a - * #RamDiscardManager assigned. + * #RamDiscardManager assigned. Return 0 if the rdm is set successfully. * * @mr: the #MemoryRegion * @rdm: #RamDiscardManager to set */ -void memory_region_set_ram_discard_manager(MemoryRegion *mr, - RamDiscardManager *rdm); +int memory_region_set_ram_discard_manager(MemoryRegion *mr, + RamDiscardManager *rdm); /** * memory_region_find: translate an address/size relative to a @@ -2440,7 +2588,7 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr, void memory_global_dirty_log_sync(bool last_stage); /** - * memory_global_dirty_log_sync: synchronize the dirty log for all memory + * memory_global_after_dirty_log_sync: synchronize the dirty log for all memory * * Synchronizes the vCPUs with a thread that is reading the dirty bitmap. * This function must be called after the dirty log bitmap is cleared, and @@ -2485,8 +2633,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 @@ -2650,13 +2801,19 @@ MemTxResult address_space_write_rom(AddressSpace *as, hwaddr addr, #define ARG1_DECL AddressSpace *as #include "exec/memory_ldst.h.inc" +static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val) +{ + address_space_stl_notdirty(as, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + #define SUFFIX #define ARG1 as #define ARG1_DECL AddressSpace *as #include "exec/memory_ldst_phys.h.inc" struct MemoryRegionCache { - void *ptr; + uint8_t *ptr; hwaddr xlat; hwaddr len; FlatView *fv; @@ -2664,9 +2821,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 * @@ -2719,6 +2873,9 @@ static inline void address_space_stb_cached(MemoryRegionCache *cache, } } +#define ENDIANNESS +#include "exec/memory_ldst_cached.h.inc" + #define ENDIANNESS _le #include "exec/memory_ldst_cached.h.inc" @@ -2755,6 +2912,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 * @@ -2832,8 +3004,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 @@ -2858,6 +3030,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, @@ -2879,15 +3073,34 @@ MemTxResult address_space_write_cached_slow(MemoryRegionCache *cache, int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr); bool prepare_mmio_access(MemoryRegion *mr); -static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) +static inline bool memory_region_supports_direct_access(MemoryRegion *mr) { - if (is_write) { - return memory_region_is_ram(mr) && !mr->readonly && - !mr->rom_device && !memory_region_is_ram_device(mr); - } else { - return (memory_region_is_ram(mr) && !memory_region_is_ram_device(mr)) || - memory_region_is_romd(mr); + /* ROM DEVICE regions only allow direct access if in ROMD mode. */ + if (memory_region_is_romd(mr)) { + return true; + } + if (!memory_region_is_ram(mr)) { + return false; } + /* + * RAM DEVICE regions can be accessed directly using memcpy, but it might + * be MMIO and access using mempy can be wrong (e.g., using instructions not + * intended for MMIO access). So we treat this as IO. + */ + return !memory_region_is_ram_device(mr); +} + +static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write, + MemTxAttrs attrs) +{ + if (!memory_region_supports_direct_access(mr)) { + return false; + } + /* Debug access can write to ROM. */ + if (is_write && !attrs.debug) { + return !mr->readonly && !mr->rom_device; + } + return true; } /** @@ -2920,7 +3133,7 @@ MemTxResult address_space_read(AddressSpace *as, hwaddr addr, fv = address_space_to_flatview(as); l = len; mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); - if (len == l && memory_access_is_direct(mr, false)) { + if (len == l && memory_access_is_direct(mr, false, attrs)) { ptr = qemu_map_ram_ptr(mr->ram_block, addr1); memcpy(buf, ptr, len); } else { @@ -2993,26 +3206,6 @@ 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 -/* enum device_endian to MemOp. */ -static inline MemOp devend_memop(enum device_endian end) -{ - QEMU_BUILD_BUG_ON(DEVICE_HOST_ENDIAN != DEVICE_LITTLE_ENDIAN && - DEVICE_HOST_ENDIAN != DEVICE_BIG_ENDIAN); - -#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN - /* Swap if non-host endianness or native (target) endianness */ - return (end == DEVICE_HOST_ENDIAN) ? 0 : MO_BSWAP; -#else - const int non_host_endianness = - DEVICE_LITTLE_ENDIAN ^ DEVICE_BIG_ENDIAN ^ DEVICE_HOST_ENDIAN; - - /* In this case, native (target) endianness needs no swap. */ - return (end == non_host_endianness) ? MO_BSWAP : 0; -#endif -} -#endif - /* * Inhibit technologies that require discarding of pages in RAM blocks, e.g., * to manage the actual amount of memory consumed by the VM (then, the memory @@ -3052,7 +3245,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). */ @@ -3068,6 +3261,7 @@ bool ram_block_discard_is_disabled(void); */ bool ram_block_discard_is_required(void); -#endif +void ram_block_add_cpr_blocker(RAMBlock *rb, Error **errp); +void ram_block_del_cpr_blocker(RAMBlock *rb); #endif diff --git a/include/sysemu/memory_mapping.h b/include/system/memory_mapping.h similarity index 97% rename from include/sysemu/memory_mapping.h rename to include/system/memory_mapping.h index 3bbeb1bcb41..021e0a62309 100644 --- a/include/sysemu/memory_mapping.h +++ b/include/system/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/system/numa.h similarity index 95% rename from include/sysemu/numa.h rename to include/system/numa.h index 4173ef2afa7..1044b0eb6e9 100644 --- a/include/sysemu/numa.h +++ b/include/system/numa.h @@ -1,9 +1,8 @@ -#ifndef SYSEMU_NUMA_H -#define SYSEMU_NUMA_H +#ifndef SYSTEM_NUMA_H +#define SYSTEM_NUMA_H #include "qemu/bitmap.h" #include "qapi/qapi-types-machine.h" -#include "exec/cpu-common.h" struct CPUArchId; @@ -36,20 +35,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/system/nvmm.h similarity index 89% rename from include/sysemu/nvmm.h rename to include/system/nvmm.h index be7bc9a62d6..6971ddb3a5a 100644 --- a/include/sysemu/nvmm.h +++ b/include/system/nvmm.h @@ -12,7 +12,7 @@ #ifndef QEMU_NVMM_H #define QEMU_NVMM_H -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #ifdef CONFIG_NVMM @@ -24,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/system/os-posix.h similarity index 95% rename from include/sysemu/os-posix.h rename to include/system/os-posix.h index 6dfdcbb0863..ce5b3bccf8d 100644 --- a/include/sysemu/os-posix.h +++ b/include/system/os-posix.h @@ -49,10 +49,11 @@ void os_setup_signal_handling(void); int os_set_daemonize(bool d); bool is_daemonized(void); void os_daemonize(void); -bool os_set_runas(const char *optarg); -void os_set_chroot(const char *optarg); +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); +int os_mlock(bool on_fault); /** * qemu_alloc_stack: diff --git a/include/system/os-wasm.h b/include/system/os-wasm.h new file mode 100644 index 00000000000..3abb3aaa03b --- /dev/null +++ b/include/system/os-wasm.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: MIT */ +/* + * posix specific declarations forked from os-posix.h, removing functions not + * working on Emscripten + * + * 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_OS_WASM_H +#define QEMU_OS_WASM_H + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SYSMACROS +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +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_setup_limits(void); +void os_setup_post(void); +int os_mlock(bool on_fault); +static inline int os_set_daemonize(bool d) +{ + return -1; +}; +bool is_daemonized(void); +static inline void os_daemonize(void) {} + +/** + * qemu_alloc_stack: + * @sz: pointer to a size_t holding the requested usable stack size + * + * Allocate memory that can be used as a stack, for instance for + * coroutines. If the memory cannot be allocated, this function + * will abort (like g_malloc()). This function also inserts an + * additional guard page to catch a potential stack overflow. + * Note that the memory required for the guard page and alignment + * and minimal stack size restrictions will increase the value of sz. + * + * The allocated stack must be freed with qemu_free_stack(). + * + * Returns: pointer to (the lowest address of) the stack memory. + */ +void *qemu_alloc_stack(size_t *sz); + +/** + * qemu_free_stack: + * @stack: stack to free + * @sz: size of stack in bytes + * + * Free a stack allocated via qemu_alloc_stack(). Note that sz must + * be exactly the adjusted stack size returned by qemu_alloc_stack. + */ +void qemu_free_stack(void *stack, size_t sz); + +/* POSIX and Mingw32 differ in the name of the stdio lock functions. */ + +static inline void qemu_flockfile(FILE *f) +{ + flockfile(f); +} + +static inline void qemu_funlockfile(FILE *f) +{ + funlockfile(f); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/sysemu/os-win32.h b/include/system/os-win32.h similarity index 98% rename from include/sysemu/os-win32.h rename to include/system/os-win32.h index 1047d260cbd..3aa6cee4c23 100644 --- a/include/sysemu/os-win32.h +++ b/include/system/os-win32.h @@ -123,11 +123,15 @@ static inline bool is_daemonized(void) return false; } -static inline int os_mlock(void) +static inline int os_mlock(bool on_fault G_GNUC_UNUSED) { return -ENOSYS; } +static inline void os_setup_limits(void) +{ +} + #define fsync _commit #if !defined(lseek) diff --git a/include/sysemu/qtest.h b/include/system/qtest.h similarity index 91% rename from include/sysemu/qtest.h rename to include/system/qtest.h index 85f05b0e46a..84b1f8c6ee4 100644 --- a/include/sysemu/qtest.h +++ b/include/system/qtest.h @@ -23,7 +23,6 @@ static inline bool qtest_enabled(void) return qtest_allowed; } -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); @@ -34,6 +33,4 @@ 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 diff --git a/include/exec/ram_addr.h b/include/system/ram_addr.h similarity index 91% rename from include/exec/ram_addr.h rename to include/system/ram_addr.h index 9f2e3893f56..15a1b1a4fa2 100644 --- a/include/exec/ram_addr.h +++ b/include/system/ram_addr.h @@ -16,15 +16,20 @@ * The functions declared here will be removed soon. */ -#ifndef RAM_ADDR_H -#define RAM_ADDR_H +#ifndef SYSTEM_RAM_ADDR_H +#define SYSTEM_RAM_ADDR_H -#ifndef CONFIG_USER_ONLY -#include "cpu.h" -#include "sysemu/xen.h" -#include "sysemu/tcg.h" +#include "system/xen.h" +#include "system/tcg.h" +#include "exec/cputlb.h" #include "exec/ramlist.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" +#include "system/memory.h" +#include "exec/target_page.h" +#include "qemu/rcu.h" + +#include "exec/hwaddr.h" +#include "exec/cpu-common.h" extern uint64_t total_dirty_pages; @@ -96,9 +101,6 @@ static inline unsigned long int ramblock_recv_bitmap_offset(void *host_addr, bool ramblock_is_pmem(RAMBlock *rb); -long qemu_minrampagesize(void); -long qemu_maxrampagesize(void); - /** * qemu_ram_alloc_from_file, * qemu_ram_alloc_from_fd: Allocate a ram block from the specified backing @@ -106,33 +108,38 @@ long qemu_maxrampagesize(void); * * Parameters: * @size: the size in bytes of the ram block + * @max_size: the maximum size of the block after resizing * @mr: the memory region where the ram block is + * @resized: callback after calls to qemu_ram_resize * @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 * @offset: Offset into target file - * @readonly: true to open @path for reading, false for read/write. + * @grow: extend file if necessary (but an empty file is always extended). * @errp: pointer to Error*, to store an error if it happens * * Return: * On success, return a pointer to the ram block. * On failure, return NULL. */ +typedef void (*qemu_ram_resize_cb)(const char *, uint64_t length, void *host); + RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, uint32_t ram_flags, const char *mem_path, - off_t offset, bool readonly, Error **errp); -RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, + off_t offset, Error **errp); +RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, ram_addr_t max_size, + qemu_ram_resize_cb resized, MemoryRegion *mr, uint32_t ram_flags, int fd, off_t offset, - bool readonly, Error **errp); + bool grow, + Error **errp); RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, MemoryRegion *mr, Error **errp); RAMBlock *qemu_ram_alloc(ram_addr_t size, uint32_t ram_flags, MemoryRegion *mr, Error **errp); RAMBlock *qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t max_size, - void (*resized)(const char*, - uint64_t length, - void *host), + qemu_ram_resize_cb resized, MemoryRegion *mr, Error **errp); void qemu_ram_free(RAMBlock *block); @@ -330,7 +337,9 @@ static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, } } - xen_hvm_modified_memory(start, length); + if (xen_enabled()) { + xen_hvm_modified_memory(start, length); + } } #if !defined(_WIN32) @@ -406,7 +415,9 @@ uint64_t cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, } } - xen_hvm_modified_memory(start, pages << TARGET_PAGE_BITS); + if (xen_enabled()) { + xen_hvm_modified_memory(start, pages << TARGET_PAGE_BITS); + } } else { uint8_t clients = tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE; @@ -443,6 +454,14 @@ uint64_t cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, } #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); @@ -504,6 +523,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) { /* @@ -535,5 +557,5 @@ uint64_t cpu_physical_memory_sync_dirty_bitmap(RAMBlock *rb, return num_dirty; } -#endif + #endif diff --git a/include/system/ramblock.h b/include/system/ramblock.h new file mode 100644 index 00000000000..87e847e184a --- /dev/null +++ b/include/system/ramblock.h @@ -0,0 +1,116 @@ +/* + * Declarations for cpu physical memory functions + * + * Copyright 2011 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Avi Kivity + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + * + */ + +/* + * This header is for use by exec.c and memory.c ONLY. Do not include it. + * The functions declared here will be removed soon. + */ + +#ifndef SYSTEM_RAMBLOCK_H +#define SYSTEM_RAMBLOCK_H + +#include "exec/cpu-common.h" +#include "qemu/rcu.h" +#include "exec/ramlist.h" +#include "system/hostmem.h" + +#define TYPE_RAM_BLOCK_ATTRIBUTES "ram-block-attributes" +OBJECT_DECLARE_SIMPLE_TYPE(RamBlockAttributes, RAM_BLOCK_ATTRIBUTES) + +struct RAMBlock { + struct rcu_head rcu; + struct MemoryRegion *mr; + uint8_t *host; + uint8_t *colo_cache; /* For colo, VM's ram cache */ + ram_addr_t offset; + ram_addr_t used_length; + ram_addr_t max_length; + void (*resized)(const char*, uint64_t length, void *host); + uint32_t flags; + /* Protected by the BQL. */ + char idstr[256]; + /* RCU-enabled, writes protected by the ramlist lock */ + QLIST_ENTRY(RAMBlock) next; + QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers; + Error *cpr_blocker; + int fd; + uint64_t fd_offset; + int guest_memfd; + RamBlockAttributes *attributes; + size_t page_size; + /* dirty bitmap used during migration */ + unsigned long *bmap; + + /* + * Below fields are only used by mapped-ram migration + */ + /* bitmap of pages present in the migration file */ + unsigned long *file_bmap; + /* + * offset in the file pages belonging to this ramblock are saved, + * used only during migration to a file. + */ + off_t bitmap_offset; + uint64_t pages_offset; + + /* Bitmap of already received pages. Only used on destination side. */ + unsigned long *receivedmap; + + /* + * bitmap to track already cleared dirty bitmap. When the bit is + * set, it means the corresponding memory chunk needs a log-clear. + * Set this up to non-NULL to enable the capability to postpone + * and split clearing of dirty bitmap on the remote node (e.g., + * KVM). The bitmap will be set only when doing global sync. + * + * It is only used during src side of ram migration, and it is + * protected by the global ram_state.bitmap_mutex. + * + * NOTE: this bitmap is different comparing to the other bitmaps + * in that one bit can represent multiple guest pages (which is + * decided by the `clear_bmap_shift' variable below). On + * destination side, this should always be NULL, and the variable + * `clear_bmap_shift' is meaningless. + */ + unsigned long *clear_bmap; + uint8_t clear_bmap_shift; + + /* + * RAM block length that corresponds to the used_length on the migration + * source (after RAM block sizes were synchronized). Especially, after + * starting to run the guest, used_length and postcopy_length can differ. + * Used to register/unregister uffd handlers and as the size of the received + * bitmap. Receiving any page beyond this length will bail out, as it + * could not have been valid on the source. + */ + ram_addr_t postcopy_length; +}; + +struct RamBlockAttributes { + Object parent; + + RAMBlock *ram_block; + + /* 1-setting of the bitmap represents ram is populated (shared) */ + unsigned bitmap_size; + unsigned long *bitmap; + + QLIST_HEAD(, RamDiscardListener) rdl_list; +}; + +RamBlockAttributes *ram_block_attributes_create(RAMBlock *ram_block); +void ram_block_attributes_destroy(RamBlockAttributes *attr); +int ram_block_attributes_state_change(RamBlockAttributes *attr, uint64_t offset, + uint64_t size, bool to_discard); + +#endif diff --git a/include/sysemu/replay.h b/include/system/replay.h similarity index 97% rename from include/sysemu/replay.h rename to include/system/replay.h index 08aae5869fc..1c87c97fdd0 100644 --- a/include/sysemu/replay.h +++ b/include/system/replay.h @@ -1,6 +1,3 @@ -#ifndef SYSEMU_REPLAY_H -#define SYSEMU_REPLAY_H - /* * QEMU replay (system interface) * @@ -11,6 +8,8 @@ * See the COPYING file in the top-level directory. * */ +#ifndef SYSTEM_REPLAY_H +#define SYSTEM_REPLAY_H #include "exec/replay-core.h" #include "qapi/qapi-types-misc.h" @@ -79,12 +78,14 @@ 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 \ @@ -112,8 +113,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/system/reset.h b/include/system/reset.h new file mode 100644 index 00000000000..97131d94cfc --- /dev/null +++ b/include/system/reset.h @@ -0,0 +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_SYSTEM_RESET_H +#define QEMU_SYSTEM_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); + +/** + * 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/rng-random.h b/include/system/rng-random.h similarity index 100% rename from include/sysemu/rng-random.h rename to include/system/rng-random.h diff --git a/include/sysemu/rng.h b/include/system/rng.h similarity index 100% rename from include/sysemu/rng.h rename to include/system/rng.h diff --git a/include/sysemu/rtc.h b/include/system/rtc.h similarity index 98% rename from include/sysemu/rtc.h rename to include/system/rtc.h index 0fc8ad6fdf1..cde83fab15f 100644 --- a/include/sysemu/rtc.h +++ b/include/system/rtc.h @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#ifndef SYSEMU_RTC_H -#define SYSEMU_RTC_H +#ifndef SYSTEM_RTC_H +#define SYSTEM_RTC_H /** * qemu_get_timedate: Get the current RTC time diff --git a/include/sysemu/runstate-action.h b/include/system/runstate-action.h similarity index 92% rename from include/sysemu/runstate-action.h rename to include/system/runstate-action.h index cff45a047bf..db4e3099ae5 100644 --- a/include/sysemu/runstate-action.h +++ b/include/system/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/system/runstate.h b/include/system/runstate.h new file mode 100644 index 00000000000..fdd5c4a5172 --- /dev/null +++ b/include/system/runstate.h @@ -0,0 +1,119 @@ +#ifndef SYSTEM_RUNSTATE_H +#define SYSTEM_RUNSTATE_H + +#include "qapi/qapi-types-run-state.h" +#include "qemu/notify.h" + +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); +void runstate_replay_enable(void); + +typedef void VMChangeStateHandler(void *opaque, bool running, RunState state); +typedef int VMChangeStateHandlerWithRet(void *opaque, bool running, RunState state); + +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, + VMChangeStateHandlerWithRet *cb_ret, + void *opaque, int priority); +VMChangeStateEntry *qdev_add_vm_change_state_handler(DeviceState *dev, + VMChangeStateHandler *cb, + VMChangeStateHandlerWithRet *cb_ret, + void *opaque); +VMChangeStateEntry *qdev_add_vm_change_state_handler_full( + DeviceState *dev, VMChangeStateHandler *cb, VMChangeStateHandler *prepare_cb, + VMChangeStateHandlerWithRet *cb_ret, void *opaque); +void qemu_del_vm_change_state_handler(VMChangeStateEntry *e); +/** + * vm_state_notify: Notify the state of the VM + * + * @running: whether the VM is running or not. + * @state: the #RunState of the VM. + * + * Return the result of the callback which has return value. + * If no callback has return value, still return 0 and the + * upper layer should not do additional processing. + */ +int vm_state_notify(bool running, RunState state); + +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); + +/** + * vm_prepare_start: Prepare for starting/resuming the VM + * + * @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 */ + QEMU_WAKEUP_REASON_NONE = 0, + QEMU_WAKEUP_REASON_RTC, + QEMU_WAKEUP_REASON_PMTIMER, + QEMU_WAKEUP_REASON_OTHER, +} WakeupReason; + +void qemu_system_reset_request(ShutdownCause reason); +void qemu_system_suspend_request(void); +void qemu_register_suspend_notifier(Notifier *notifier); +bool qemu_wakeup_suspend_enabled(void); +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); +void qemu_register_shutdown_notifier(Notifier *notifier); +void qemu_system_debug_request(void); +void qemu_system_vmstop_request(RunState reason); +void qemu_system_vmstop_request_prepare(void); +bool qemu_vmstop_requested(RunState *r); +ShutdownCause qemu_shutdown_requested_get(void); +ShutdownCause qemu_reset_requested_get(void); +void qemu_system_killed(int signal, pid_t pid); +void qemu_system_reset(ShutdownCause reason); +void qemu_system_guest_panicked(GuestPanicInformation *info); +void qemu_system_guest_crashloaded(GuestPanicInformation *info); +void qemu_system_guest_pvshutdown(void); +bool qemu_system_dump_in_progress(void); + +#endif + diff --git a/include/sysemu/seccomp.h b/include/system/seccomp.h similarity index 100% rename from include/sysemu/seccomp.h rename to include/system/seccomp.h diff --git a/include/system/spdm-socket.h b/include/system/spdm-socket.h new file mode 100644 index 00000000000..5d8bd9aa4e1 --- /dev/null +++ b/include/system/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/sysemu/stats.h b/include/system/stats.h similarity index 100% rename from include/sysemu/stats.h rename to include/system/stats.h diff --git a/include/system/system.h b/include/system/system.h new file mode 100644 index 00000000000..a7effe7dfd8 --- /dev/null +++ b/include/system/system.h @@ -0,0 +1,125 @@ +#ifndef SYSTEM_H +#define SYSTEM_H +/* Misc. things related to the system emulator. */ + +#include "qemu/timer.h" +#include "qemu/notify.h" +#include "qemu/uuid.h" + +/* vl.c */ + +extern int only_migratable; +extern const char *qemu_name; +extern QemuUUID qemu_uuid; +extern bool qemu_uuid_set; + +const char *qemu_get_vm_name(void); + +/* Exit notifiers will run with BQL held. */ +void qemu_add_exit_notifier(Notifier *notify); +void qemu_remove_exit_notifier(Notifier *notify); + +void qemu_add_machine_init_done_notifier(Notifier *notify); +void qemu_remove_machine_init_done_notifier(Notifier *notify); + +void configure_rtc(QemuOpts *opts); + +void qemu_init_subsystems(void); + +extern int autostart; + +typedef enum { + VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL, + VGA_TCX, VGA_CG3, VGA_DEVICE, VGA_VIRTIO, + VGA_TYPE_MAX, +} VGAInterfaceType; + +extern int vga_interface_type; +extern bool vga_interface_created; + +extern int graphic_width; +extern int graphic_height; +extern int graphic_depth; +extern int display_opengl; +extern const char *keyboard_layout; +extern int old_param; +extern uint8_t *boot_splash_filedata; +extern bool enable_cpu_pm; +extern QEMUClockType rtc_clock; + +typedef enum { + MLOCK_OFF = 0, + MLOCK_ON, + MLOCK_ON_FAULT, +} MlockState; + +bool should_mlock(MlockState); +bool is_mlock_on_fault(MlockState); + +extern MlockState mlock_state; + +#define MAX_OPTION_ROMS 16 +typedef struct QEMUOptionRom { + const char *name; + int32_t bootindex; +} QEMUOptionRom; +extern QEMUOptionRom option_rom[MAX_OPTION_ROMS]; +extern int nb_option_roms; + +#define MAX_PROM_ENVS 128 +extern const char *prom_envs[MAX_PROM_ENVS]; +extern unsigned int nb_prom_envs; + +/* serial ports */ + +/* Return the Chardev for serial port i, or NULL if none */ +Chardev *serial_hd(int i); + +/* parallel ports */ + +#define MAX_PARALLEL_PORTS 3 + +extern Chardev *parallel_hds[MAX_PARALLEL_PORTS]; + +void add_boot_device_path(int32_t bootindex, DeviceState *dev, + const char *suffix); +char *get_boot_devices_list(size_t *size); + +DeviceState *get_boot_device(uint32_t position); +void check_boot_index(int32_t bootindex, Error **errp); +void del_boot_device_path(DeviceState *dev, const char *suffix); +void device_add_bootindex_property(Object *obj, int32_t *bootindex, + const char *name, const char *suffix, + DeviceState *dev); +void restore_boot_order(void *opaque); +void validate_bootdevices(const char *devices, Error **errp); +void add_boot_device_lchs(DeviceState *dev, const char *suffix, + uint32_t lcyls, uint32_t lheads, uint32_t lsecs); +void del_boot_device_lchs(DeviceState *dev, const char *suffix); +char *get_boot_devices_lchs_list(size_t *size); + +/* handler to set the boot_device order for a specific type of MachineClass */ +typedef void QEMUBootSetHandler(void *opaque, const char *boot_order, + Error **errp); +void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque); +void qemu_boot_set(const char *boot_order, Error **errp); + +bool defaults_enabled(void); + +void qemu_init(int argc, char **argv); +int qemu_main_loop(void); +void qemu_cleanup(int); + +extern QemuOptsList qemu_legacy_drive_opts; +extern QemuOptsList qemu_common_drive_opts; +extern QemuOptsList qemu_drive_opts; +extern QemuOptsList bdrv_runtime_opts; +extern QemuOptsList qemu_chardev_opts; +extern QemuOptsList qemu_device_opts; +extern QemuOptsList qemu_netdev_opts; +extern QemuOptsList qemu_nic_opts; +extern QemuOptsList qemu_net_opts; +extern QemuOptsList qemu_global_opts; +extern QemuOptsList qemu_semihosting_config_opts; + +#endif diff --git a/include/system/tcg.h b/include/system/tcg.h new file mode 100644 index 00000000000..7622dcea302 --- /dev/null +++ b/include/system/tcg.h @@ -0,0 +1,28 @@ +/* + * QEMU TCG support + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* header to be included in non-TCG-specific code */ + +#ifndef SYSTEM_TCG_H +#define SYSTEM_TCG_H + +#ifdef CONFIG_TCG +extern bool tcg_allowed; +#define tcg_enabled() (tcg_allowed) +#else +#define tcg_enabled() 0 +#endif + +/** + * qemu_tcg_mttcg_enabled: + * Check whether we are running MultiThread TCG or not. + * + * Returns: %true if we are in MTTCG mode %false otherwise. + */ +bool qemu_tcg_mttcg_enabled(void); + +#endif diff --git a/include/sysemu/tpm.h b/include/system/tpm.h similarity index 97% rename from include/sysemu/tpm.h rename to include/system/tpm.h index 66e3b45f306..1ee568b3b62 100644 --- a/include/sysemu/tpm.h +++ b/include/system/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); diff --git a/include/sysemu/tpm_backend.h b/include/system/tpm_backend.h similarity index 99% rename from include/sysemu/tpm_backend.h rename to include/system/tpm_backend.h index 7fabafefee1..01b11f629c4 100644 --- a/include/sysemu/tpm_backend.h +++ b/include/system/tpm_backend.h @@ -15,7 +15,7 @@ #include "qom/object.h" #include "qemu/option.h" -#include "sysemu/tpm.h" +#include "system/tpm.h" #include "qapi/error.h" #ifdef CONFIG_TPM diff --git a/include/sysemu/tpm_util.h b/include/system/tpm_util.h similarity index 94% rename from include/sysemu/tpm_util.h rename to include/system/tpm_util.h index 08f05172a71..18586932257 100644 --- a/include/sysemu/tpm_util.h +++ b/include/system/tpm_util.h @@ -19,10 +19,10 @@ * License along with this library; if not, see */ -#ifndef SYSEMU_TPM_UTIL_H -#define SYSEMU_TPM_UTIL_H +#ifndef SYSTEM_TPM_UTIL_H +#define SYSTEM_TPM_UTIL_H -#include "sysemu/tpm.h" +#include "system/tpm.h" #include "qemu/bswap.h" void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len); @@ -69,4 +69,4 @@ static inline void tpm_cmd_set_error(void *b, uint32_t error) void tpm_util_show_buffer(const unsigned char *buffer, size_t buffer_size, const char *string); -#endif /* SYSEMU_TPM_UTIL_H */ +#endif /* SYSTEM_TPM_UTIL_H */ diff --git a/include/sysemu/vhost-user-backend.h b/include/system/vhost-user-backend.h similarity index 93% rename from include/sysemu/vhost-user-backend.h rename to include/system/vhost-user-backend.h index 327b0b84f1f..5634ebdb2eb 100644 --- a/include/sysemu/vhost-user-backend.h +++ b/include/system/vhost-user-backend.h @@ -13,7 +13,7 @@ #define QEMU_VHOST_USER_BACKEND_H #include "qom/object.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/option.h" #include "qemu/bitmap.h" #include "hw/virtio/vhost.h" @@ -43,6 +43,6 @@ struct VhostUserBackend { int vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev, unsigned nvqs, Error **errp); void vhost_user_backend_start(VhostUserBackend *b); -void vhost_user_backend_stop(VhostUserBackend *b); +int vhost_user_backend_stop(VhostUserBackend *b); #endif diff --git a/include/sysemu/watchdog.h b/include/system/watchdog.h similarity index 100% rename from include/sysemu/watchdog.h rename to include/system/watchdog.h diff --git a/include/sysemu/whpx.h b/include/system/whpx.h similarity index 90% rename from include/sysemu/whpx.h rename to include/system/whpx.h index 781ca5b2b61..00ff409b682 100644 --- a/include/sysemu/whpx.h +++ b/include/system/whpx.h @@ -15,7 +15,7 @@ #ifndef QEMU_WHPX_H #define QEMU_WHPX_H -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #ifdef CONFIG_WHPX @@ -29,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/system/xen-mapcache.h b/include/system/xen-mapcache.h new file mode 100644 index 00000000000..bb454a7c96c --- /dev/null +++ b/include/system/xen-mapcache.h @@ -0,0 +1,30 @@ +/* + * 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. + * + */ + +#ifndef XEN_MAPCACHE_H +#define XEN_MAPCACHE_H + +#include "exec/cpu-common.h" +#include "system/xen.h" + +typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr phys_offset, + ram_addr_t size); +void xen_map_cache_init(phys_offset_to_gaddr_t f, + void *opaque); +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); +uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr, + hwaddr new_phys_addr, + hwaddr size); + +#endif /* XEN_MAPCACHE_H */ diff --git a/include/system/xen.h b/include/system/xen.h new file mode 100644 index 00000000000..c2f283d1c26 --- /dev/null +++ b/include/system/xen.h @@ -0,0 +1,35 @@ +/* + * QEMU Xen support + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* header to be included in non-Xen-specific code */ + +#ifndef SYSTEM_XEN_H +#define SYSTEM_XEN_H + +#include "exec/cpu-common.h" + +#ifdef COMPILING_PER_TARGET +# ifdef CONFIG_XEN +# define CONFIG_XEN_IS_POSSIBLE +# endif +#else +# define CONFIG_XEN_IS_POSSIBLE +#endif /* COMPILING_PER_TARGET */ + +#ifdef CONFIG_XEN_IS_POSSIBLE +extern bool xen_allowed; +#define xen_enabled() (xen_allowed) +#else /* !CONFIG_XEN_IS_POSSIBLE */ +#define xen_enabled() 0 +#endif /* CONFIG_XEN_IS_POSSIBLE */ + +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); +bool xen_mr_is_memory(MemoryRegion *mr); +bool xen_mr_is_grants(MemoryRegion *mr); +#endif diff --git a/accel/tcg/debuginfo.h b/include/tcg/debuginfo.h similarity index 96% rename from accel/tcg/debuginfo.h rename to include/tcg/debuginfo.h index f064e1c144b..858535b5da5 100644 --- a/accel/tcg/debuginfo.h +++ b/include/tcg/debuginfo.h @@ -4,8 +4,8 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef ACCEL_TCG_DEBUGINFO_H -#define ACCEL_TCG_DEBUGINFO_H +#ifndef TCG_DEBUGINFO_H +#define TCG_DEBUGINFO_H #include "qemu/bitops.h" diff --git a/include/tcg/helper-info.h b/include/tcg/helper-info.h index 7c27d6164a4..909fe73afa3 100644 --- a/include/tcg/helper-info.h +++ b/include/tcg/helper-info.h @@ -12,6 +12,9 @@ #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. diff --git a/include/tcg/insn-start-words.h b/include/tcg/insn-start-words.h index 50c18bd326d..c52aec50a70 100644 --- a/include/tcg/insn-start-words.h +++ b/include/tcg/insn-start-words.h @@ -1,17 +1,12 @@ /* SPDX-License-Identifier: MIT */ /* - * Define TARGET_INSN_START_WORDS + * Define INSN_START_WORDS * Copyright (c) 2008 Fabrice Bellard */ -#ifndef TARGET_INSN_START_WORDS +#ifndef TCG_INSN_START_WORDS +#define TCG_INSN_START_WORDS -#include "cpu.h" +#define INSN_START_WORDS 3 -#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 */ +#endif /* TCG_INSN_START_WORDS */ diff --git a/include/tcg/oversized-guest.h b/include/tcg/oversized-guest.h deleted file mode 100644 index 641b9749ffc..00000000000 --- a/include/tcg/oversized-guest.h +++ /dev/null @@ -1,23 +0,0 @@ -/* 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/accel/tcg/perf.h b/include/tcg/perf.h similarity index 95% rename from accel/tcg/perf.h rename to include/tcg/perf.h index f92dd52c699..c96b5920a3f 100644 --- a/accel/tcg/perf.h +++ b/include/tcg/perf.h @@ -4,8 +4,8 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef ACCEL_TCG_PERF_H -#define ACCEL_TCG_PERF_H +#ifndef TCG_PERF_H +#define TCG_PERF_H #if defined(CONFIG_TCG) && defined(CONFIG_LINUX) /* Start writing perf-.map. */ diff --git a/include/tcg/startup.h b/include/tcg/startup.h new file mode 100644 index 00000000000..95f574af2be --- /dev/null +++ b/include/tcg/startup.h @@ -0,0 +1,58 @@ +/* + * Tiny Code Generator for QEMU: definitions used by runtime startup + * + * Copyright (c) 2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef TCG_STARTUP_H +#define TCG_STARTUP_H + +/** + * tcg_init: Initialize the TCG runtime + * @tb_size: translation buffer size + * @splitwx: use separate rw and rx mappings + * @max_threads: number of vcpu threads in system mode + * + * Allocate and initialize TCG resources, especially the JIT buffer. + * In user-only mode, @max_threads is unused. + */ +void tcg_init(size_t tb_size, int splitwx, unsigned max_threads); + +/** + * 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 2a38a386d47..5cadbd6ff2c 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)); +} + +/* Must a comparison be considered signed? */ +static inline bool is_signed_cond(TCGCond c) +{ + return (c & (8 | 2)) == 2; +} + +/* Must a comparison be considered unsigned? */ +static inline bool is_unsigned_cond(TCGCond c) +{ + 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 c & 2 ? (TCGCond)(c ^ 6) : 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 c & 4 ? (TCGCond)(c ^ 6) : c; + return is_unsigned_cond(c) ? (TCGCond)(c - 8) : c; } -/* Must a comparison be considered unsigned? */ -static inline bool is_unsigned_cond(TCGCond 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 (c & 4) != 0; + 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-op-common.h b/include/tcg/tcg-op-common.h index a53b15933bb..e1071adebf2 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -12,236 +12,26 @@ #include "exec/helper-proto-common.h" #include "exec/helper-gen-common.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); -} - +TCGv_i32 tcg_constant_i32(int32_t val); +TCGv_i64 tcg_constant_i64(int64_t val); +TCGv_vaddr tcg_constant_vaddr(uintptr_t val); +TCGv_vec tcg_constant_vec(TCGType type, unsigned vece, int64_t val); +TCGv_vec tcg_constant_vec_matching(TCGv_vec match, unsigned vece, int64_t val); + +TCGv_i32 tcg_temp_new_i32(void); +TCGv_i64 tcg_temp_new_i64(void); +TCGv_ptr tcg_temp_new_ptr(void); +TCGv_i128 tcg_temp_new_i128(void); +TCGv_vec tcg_temp_new_vec(TCGType type); +TCGv_vec tcg_temp_new_vec_matching(TCGv_vec match); + +TCGv_i32 tcg_global_mem_new_i32(TCGv_ptr reg, intptr_t off, const char *name); +TCGv_i64 tcg_global_mem_new_i64(TCGv_ptr reg, intptr_t off, const char *name); +TCGv_ptr tcg_global_mem_new_ptr(TCGv_ptr reg, intptr_t off, const char *name); /* Generic ops. */ -static inline void gen_set_label(TCGLabel *l) -{ - l->present = 1; - tcg_gen_op1(INDEX_op_set_label, label_arg(l)); -} - +void gen_set_label(TCGLabel *l); void tcg_gen_br(TCGLabel *l); void tcg_gen_mb(TCGBar); @@ -265,7 +55,7 @@ void tcg_gen_exit_tb(const TranslationBlock *tb, unsigned idx); * * See tcg/README for more info about this TCG operation. * - * NOTE: In softmmu emulation, direct jumps with goto_tb are only safe within + * 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 @@ -285,16 +75,8 @@ void tcg_gen_goto_tb(unsigned idx); */ void tcg_gen_lookup_and_goto_ptr(void); -static inline void tcg_gen_plugin_cb_start(unsigned from, unsigned type, - unsigned wr) -{ - tcg_gen_op3(INDEX_op_plugin_cb_start, from, type, wr); -} - -static inline void tcg_gen_plugin_cb_end(void) -{ - tcg_emit_op(INDEX_op_plugin_cb_end, 0); -} +void tcg_gen_plugin_cb(unsigned from); +void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo); /* 32 bit ops */ @@ -346,12 +128,16 @@ 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_addcio_i32(TCGv_i32 r, TCGv_i32 co, + TCGv_i32 a, TCGv_i32 b, TCGv_i32 ci); 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); @@ -359,6 +145,7 @@ 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); @@ -371,128 +158,30 @@ void tcg_gen_abs_i32(TCGv_i32, TCGv_i32); /* Replicate a value of size @vece from @in to all the lanes in @out */ void tcg_gen_dup_i32(unsigned vece, TCGv_i32 out, TCGv_i32 in); -static inline void tcg_gen_discard_i32(TCGv_i32 arg) -{ - tcg_gen_op1_i32(INDEX_op_discard, arg); -} - -static inline void tcg_gen_mov_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (ret != arg) { - tcg_gen_op2_i32(INDEX_op_mov_i32, ret, arg); - } -} - -static inline void tcg_gen_ld8u_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld8u_i32, ret, arg2, offset); -} - -static inline void tcg_gen_ld8s_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld8s_i32, ret, arg2, offset); -} - -static inline void tcg_gen_ld16u_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld16u_i32, ret, arg2, offset); -} - -static inline void tcg_gen_ld16s_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld16s_i32, ret, arg2, offset); -} - -static inline void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_ld_i32, ret, arg2, offset); -} - -static inline void tcg_gen_st8_i32(TCGv_i32 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_st8_i32, arg1, arg2, offset); -} - -static inline void tcg_gen_st16_i32(TCGv_i32 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_st16_i32, arg1, arg2, offset); -} - -static inline void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, - tcg_target_long offset) -{ - tcg_gen_ldst_op_i32(INDEX_op_st_i32, arg1, arg2, offset); -} - -static inline void tcg_gen_add_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_add_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_sub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_sub_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_and_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_or_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_or_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_xor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_xor_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_shl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_shl_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_shr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_shr_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_sar_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_sar_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_mul_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) -{ - tcg_gen_op3_i32(INDEX_op_mul_i32, ret, arg1, arg2); -} - -static inline void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_neg_i32) { - tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg); - } else { - tcg_gen_subfi_i32(ret, 0, arg); - } -} - -static inline void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg) -{ - if (TCG_TARGET_HAS_not_i32) { - tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg); - } else { - tcg_gen_xori_i32(ret, arg, -1); - } -} +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 */ @@ -544,12 +233,16 @@ 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_addcio_i64(TCGv_i64 r, TCGv_i64 co, + TCGv_i64 a, TCGv_i64 b, TCGv_i64 ci); 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); @@ -560,6 +253,7 @@ 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); @@ -574,130 +268,6 @@ 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 */ 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); @@ -722,16 +292,8 @@ 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 */ +void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg); -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. */ @@ -742,15 +304,16 @@ 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_mov_i128(TCGv_i128 dst, TCGv_i128 src); 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); -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); -} +/* 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 */ @@ -917,6 +480,9 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); # 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); diff --git a/include/tcg/tcg-op-gvec-common.h b/include/tcg/tcg-op-gvec-common.h index e2683d487f5..ea0c87f4db6 100644 --- a/include/tcg/tcg-op-gvec-common.h +++ b/include/tcg/tcg-op-gvec-common.h @@ -183,6 +183,8 @@ typedef struct { 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 { @@ -225,25 +227,66 @@ typedef struct { bool prefer_i64; } GVecGen4i; +/* Expand (dbase+dofs) = op(abase+aofs), length @oprsz, clearing to @maxsz. */ +void tcg_gen_gvec_2_var(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen2 *op); +/* Similarly, expand (env+dofs) = op(env+aofs). */ void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz, const GVecGen2 *); + uint32_t oprsz, uint32_t maxsz, const GVecGen2 *op); +/* Similarly, expand (env+dofs) = op(env+aofs, c). */ void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz, - uint32_t maxsz, int64_t c, const GVecGen2i *); + uint32_t maxsz, int64_t c, const GVecGen2i *op); +/* Similarly, expand (env+dofs) = op(env+aofs, s). */ void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz, - uint32_t maxsz, TCGv_i64 c, const GVecGen2s *); + uint32_t maxsz, TCGv_i64 c, const GVecGen2s *op); + +/* + * Expand (dbase+dofs) = op(abase+aofs, bbase+bofs), + * length @oprsz, clearing to @maxsz. + */ +void tcg_gen_gvec_3_var(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen3 *op); +/* Similarly, expand (env+dofs) = op(env+aofs, env+bofs). */ void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t oprsz, uint32_t maxsz, const GVecGen3 *); + uint32_t oprsz, uint32_t maxsz, const GVecGen3 *op); + +/* + * Depending on op->load_dest and op->write_aofs, expand + * (env+dofs) = op(env+aofs, env+bofs, c) + * or + * (env+dofs) = op(env+dofs, env+aofs, env+bofs, c) + * or + * (env+dofs), (env+aofs) = op(env+aofs, env+bofs, c) + * or + * (env+dofs), (env+aofs) = op(env+dofs, env+aofs, env+bofs, c) + */ 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 *); + const GVecGen3i *op); + +/* + * Depending on op->write_aofs, expand + * (env+dofs) = op(env+aofs, env+bofs, env+cofs) + * or + * (env+dofs), (env+aofs) = op(env+aofs, env+bofs, env+cofs) + */ 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 *); + +/* Expand (env+dofs) = op(env+aofs, env+bofs, env+cofs, c). */ 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_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz); + 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, @@ -253,6 +296,15 @@ void tcg_gen_gvec_neg(unsigned vece, uint32_t dofs, uint32_t aofs, void tcg_gen_gvec_abs(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_add_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_sub_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + 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, @@ -334,6 +386,9 @@ void tcg_gen_gvec_dup_i32(unsigned vece, uint32_t dofs, uint32_t s, void tcg_gen_gvec_dup_i64(unsigned vece, uint32_t dofs, uint32_t s, uint32_t m, TCGv_i64); +void tcg_gen_gvec_dup_imm_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + uint32_t oprsz, uint32_t maxsz, uint64_t imm); + 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, @@ -374,6 +429,12 @@ void tcg_gen_gvec_rotrv(unsigned vece, uint32_t dofs, uint32_t aofs, 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). diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index 80cfcf8104b..c912578fdd2 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -9,6 +9,8 @@ #define TCG_TCG_OP_H #include "tcg/tcg-op-common.h" +#include "tcg/insn-start-words.h" +#include "exec/target_long.h" #ifndef TARGET_LONG_BITS #error must include QEMU headers @@ -22,24 +24,34 @@ # error #endif -#ifndef TARGET_INSN_START_EXTRA_WORDS +#if INSN_START_WORDS != 3 +# error Mismatch with insn-start-words.h +#endif + +#if TARGET_INSN_START_EXTRA_WORDS == 0 static inline void tcg_gen_insn_start(target_ulong pc) { - TCGOp *op = tcg_emit_op(INDEX_op_insn_start, 64 / TCG_TARGET_REG_BITS); + TCGOp *op = tcg_emit_op(INDEX_op_insn_start, + INSN_START_WORDS * 64 / TCG_TARGET_REG_BITS); tcg_set_insn_start_param(op, 0, pc); + tcg_set_insn_start_param(op, 1, 0); + tcg_set_insn_start_param(op, 2, 0); } #elif TARGET_INSN_START_EXTRA_WORDS == 1 static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1) { - TCGOp *op = tcg_emit_op(INDEX_op_insn_start, 2 * 64 / TCG_TARGET_REG_BITS); + TCGOp *op = tcg_emit_op(INDEX_op_insn_start, + INSN_START_WORDS * 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, 0); } #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); + TCGOp *op = tcg_emit_op(INDEX_op_insn_start, + INSN_START_WORDS * 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); @@ -52,7 +64,6 @@ static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1, 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_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 @@ -60,7 +71,6 @@ typedef TCGv_i32 TCGv; 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_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 @@ -201,6 +211,7 @@ DEF_ATOMIC2(tcg_gen_atomic_umax_fetch, 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 @@ -220,6 +231,7 @@ DEF_ATOMIC2(tcg_gen_atomic_umax_fetch, i64) #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 @@ -252,6 +264,7 @@ DEF_ATOMIC2(tcg_gen_atomic_umax_fetch, 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 +#define tcg_gen_addcio_tl tcg_gen_addcio_i64 #define tcg_gen_mulu2_tl tcg_gen_mulu2_i64 #define tcg_gen_muls2_tl tcg_gen_muls2_i64 #define tcg_gen_mulsu2_tl tcg_gen_mulsu2_i64 @@ -319,6 +332,7 @@ DEF_ATOMIC2(tcg_gen_atomic_umax_fetch, i64) #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 @@ -338,6 +352,7 @@ DEF_ATOMIC2(tcg_gen_atomic_umax_fetch, i64) #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 @@ -368,6 +383,7 @@ DEF_ATOMIC2(tcg_gen_atomic_umax_fetch, i64) #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 +#define tcg_gen_addcio_tl tcg_gen_addcio_i32 #define tcg_gen_mulu2_tl tcg_gen_mulu2_i32 #define tcg_gen_muls2_tl tcg_gen_muls2_i32 #define tcg_gen_mulsu2_tl tcg_gen_mulsu2_i32 diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 6eff3d9106c..e988edd93ab 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -33,286 +33,160 @@ DEF(set_label, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) /* variable number of parameters */ DEF(call, 0, 0, 3, TCG_OPF_CALL_CLOBBER | TCG_OPF_NOT_PRESENT) -DEF(br, 0, 0, 1, TCG_OPF_BB_END) - -#define IMPL(X) (__builtin_constant_p(X) && (X) <= 0 ? TCG_OPF_NOT_PRESENT : 0) -#if TCG_TARGET_REG_BITS == 32 -# define IMPL64 TCG_OPF_64BIT | TCG_OPF_NOT_PRESENT -#else -# define IMPL64 TCG_OPF_64BIT -#endif - -DEF(mb, 0, 0, 1, 0) - -DEF(mov_i32, 1, 1, 0, TCG_OPF_NOT_PRESENT) -DEF(setcond_i32, 1, 2, 1, 0) -DEF(negsetcond_i32, 1, 2, 1, IMPL(TCG_TARGET_HAS_negsetcond_i32)) -DEF(movcond_i32, 1, 4, 1, IMPL(TCG_TARGET_HAS_movcond_i32)) -/* load/store */ -DEF(ld8u_i32, 1, 1, 1, 0) -DEF(ld8s_i32, 1, 1, 1, 0) -DEF(ld16u_i32, 1, 1, 1, 0) -DEF(ld16s_i32, 1, 1, 1, 0) -DEF(ld_i32, 1, 1, 1, 0) -DEF(st8_i32, 0, 2, 1, 0) -DEF(st16_i32, 0, 2, 1, 0) -DEF(st_i32, 0, 2, 1, 0) -/* arith */ -DEF(add_i32, 1, 2, 0, 0) -DEF(sub_i32, 1, 2, 0, 0) -DEF(mul_i32, 1, 2, 0, 0) -DEF(div_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_div_i32)) -DEF(divu_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_div_i32)) -DEF(rem_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_rem_i32)) -DEF(remu_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_rem_i32)) -DEF(div2_i32, 2, 3, 0, IMPL(TCG_TARGET_HAS_div2_i32)) -DEF(divu2_i32, 2, 3, 0, IMPL(TCG_TARGET_HAS_div2_i32)) -DEF(and_i32, 1, 2, 0, 0) -DEF(or_i32, 1, 2, 0, 0) -DEF(xor_i32, 1, 2, 0, 0) -/* shifts/rotates */ -DEF(shl_i32, 1, 2, 0, 0) -DEF(shr_i32, 1, 2, 0, 0) -DEF(sar_i32, 1, 2, 0, 0) -DEF(rotl_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_rot_i32)) -DEF(rotr_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_rot_i32)) -DEF(deposit_i32, 1, 2, 2, IMPL(TCG_TARGET_HAS_deposit_i32)) -DEF(extract_i32, 1, 1, 2, IMPL(TCG_TARGET_HAS_extract_i32)) -DEF(sextract_i32, 1, 1, 2, IMPL(TCG_TARGET_HAS_sextract_i32)) -DEF(extract2_i32, 1, 2, 1, IMPL(TCG_TARGET_HAS_extract2_i32)) - -DEF(brcond_i32, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) - -DEF(add2_i32, 2, 4, 0, IMPL(TCG_TARGET_HAS_add2_i32)) -DEF(sub2_i32, 2, 4, 0, IMPL(TCG_TARGET_HAS_sub2_i32)) -DEF(mulu2_i32, 2, 2, 0, IMPL(TCG_TARGET_HAS_mulu2_i32)) -DEF(muls2_i32, 2, 2, 0, IMPL(TCG_TARGET_HAS_muls2_i32)) -DEF(muluh_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_muluh_i32)) -DEF(mulsh_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_mulsh_i32)) -DEF(brcond2_i32, 0, 4, 2, - TCG_OPF_BB_END | TCG_OPF_COND_BRANCH | IMPL(TCG_TARGET_REG_BITS == 32)) -DEF(setcond2_i32, 1, 4, 1, IMPL(TCG_TARGET_REG_BITS == 32)) - -DEF(ext8s_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_ext8s_i32)) -DEF(ext16s_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_ext16s_i32)) -DEF(ext8u_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_ext8u_i32)) -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(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)) -DEF(nand_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_nand_i32)) -DEF(nor_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_nor_i32)) -DEF(clz_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_clz_i32)) -DEF(ctz_i32, 1, 2, 0, IMPL(TCG_TARGET_HAS_ctz_i32)) -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(negsetcond_i64, 1, 2, 1, IMPL64 | IMPL(TCG_TARGET_HAS_negsetcond_i64)) -DEF(movcond_i64, 1, 4, 1, IMPL64 | IMPL(TCG_TARGET_HAS_movcond_i64)) -/* load/store */ -DEF(ld8u_i64, 1, 1, 1, IMPL64) -DEF(ld8s_i64, 1, 1, 1, IMPL64) -DEF(ld16u_i64, 1, 1, 1, IMPL64) -DEF(ld16s_i64, 1, 1, 1, IMPL64) -DEF(ld32u_i64, 1, 1, 1, IMPL64) -DEF(ld32s_i64, 1, 1, 1, IMPL64) -DEF(ld_i64, 1, 1, 1, IMPL64) -DEF(st8_i64, 0, 2, 1, IMPL64) -DEF(st16_i64, 0, 2, 1, IMPL64) -DEF(st32_i64, 0, 2, 1, IMPL64) -DEF(st_i64, 0, 2, 1, IMPL64) -/* arith */ -DEF(add_i64, 1, 2, 0, IMPL64) -DEF(sub_i64, 1, 2, 0, IMPL64) -DEF(mul_i64, 1, 2, 0, IMPL64) -DEF(div_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_div_i64)) -DEF(divu_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_div_i64)) -DEF(rem_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_rem_i64)) -DEF(remu_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_rem_i64)) -DEF(div2_i64, 2, 3, 0, IMPL64 | IMPL(TCG_TARGET_HAS_div2_i64)) -DEF(divu2_i64, 2, 3, 0, IMPL64 | IMPL(TCG_TARGET_HAS_div2_i64)) -DEF(and_i64, 1, 2, 0, IMPL64) -DEF(or_i64, 1, 2, 0, IMPL64) -DEF(xor_i64, 1, 2, 0, IMPL64) -/* shifts/rotates */ -DEF(shl_i64, 1, 2, 0, IMPL64) -DEF(shr_i64, 1, 2, 0, IMPL64) -DEF(sar_i64, 1, 2, 0, IMPL64) -DEF(rotl_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_rot_i64)) -DEF(rotr_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_rot_i64)) -DEF(deposit_i64, 1, 2, 2, IMPL64 | IMPL(TCG_TARGET_HAS_deposit_i64)) -DEF(extract_i64, 1, 1, 2, IMPL64 | IMPL(TCG_TARGET_HAS_extract_i64)) -DEF(sextract_i64, 1, 1, 2, IMPL64 | IMPL(TCG_TARGET_HAS_sextract_i64)) -DEF(extract2_i64, 1, 2, 1, IMPL64 | IMPL(TCG_TARGET_HAS_extract2_i64)) +DEF(br, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) +DEF(brcond, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH | TCG_OPF_INT) + +DEF(mb, 0, 0, 1, TCG_OPF_NOT_PRESENT) + +DEF(mov, 1, 1, 0, TCG_OPF_INT | TCG_OPF_NOT_PRESENT) + +DEF(add, 1, 2, 0, TCG_OPF_INT) +DEF(and, 1, 2, 0, TCG_OPF_INT) +DEF(andc, 1, 2, 0, TCG_OPF_INT) +DEF(bswap16, 1, 1, 1, TCG_OPF_INT) +DEF(bswap32, 1, 1, 1, TCG_OPF_INT) +DEF(bswap64, 1, 1, 1, TCG_OPF_INT) +DEF(clz, 1, 2, 0, TCG_OPF_INT) +DEF(ctpop, 1, 1, 0, TCG_OPF_INT) +DEF(ctz, 1, 2, 0, TCG_OPF_INT) +DEF(deposit, 1, 2, 2, TCG_OPF_INT) +DEF(divs, 1, 2, 0, TCG_OPF_INT) +DEF(divs2, 2, 3, 0, TCG_OPF_INT) +DEF(divu, 1, 2, 0, TCG_OPF_INT) +DEF(divu2, 2, 3, 0, TCG_OPF_INT) +DEF(eqv, 1, 2, 0, TCG_OPF_INT) +DEF(extract, 1, 1, 2, TCG_OPF_INT) +DEF(extract2, 1, 2, 1, TCG_OPF_INT) +DEF(ld8u, 1, 1, 1, TCG_OPF_INT) +DEF(ld8s, 1, 1, 1, TCG_OPF_INT) +DEF(ld16u, 1, 1, 1, TCG_OPF_INT) +DEF(ld16s, 1, 1, 1, TCG_OPF_INT) +DEF(ld32u, 1, 1, 1, TCG_OPF_INT) +DEF(ld32s, 1, 1, 1, TCG_OPF_INT) +DEF(ld, 1, 1, 1, TCG_OPF_INT) +DEF(movcond, 1, 4, 1, TCG_OPF_INT) +DEF(mul, 1, 2, 0, TCG_OPF_INT) +DEF(muls2, 2, 2, 0, TCG_OPF_INT) +DEF(mulsh, 1, 2, 0, TCG_OPF_INT) +DEF(mulu2, 2, 2, 0, TCG_OPF_INT) +DEF(muluh, 1, 2, 0, TCG_OPF_INT) +DEF(nand, 1, 2, 0, TCG_OPF_INT) +DEF(neg, 1, 1, 0, TCG_OPF_INT) +DEF(negsetcond, 1, 2, 1, TCG_OPF_INT) +DEF(nor, 1, 2, 0, TCG_OPF_INT) +DEF(not, 1, 1, 0, TCG_OPF_INT) +DEF(or, 1, 2, 0, TCG_OPF_INT) +DEF(orc, 1, 2, 0, TCG_OPF_INT) +DEF(rems, 1, 2, 0, TCG_OPF_INT) +DEF(remu, 1, 2, 0, TCG_OPF_INT) +DEF(rotl, 1, 2, 0, TCG_OPF_INT) +DEF(rotr, 1, 2, 0, TCG_OPF_INT) +DEF(sar, 1, 2, 0, TCG_OPF_INT) +DEF(setcond, 1, 2, 1, TCG_OPF_INT) +DEF(sextract, 1, 1, 2, TCG_OPF_INT) +DEF(shl, 1, 2, 0, TCG_OPF_INT) +DEF(shr, 1, 2, 0, TCG_OPF_INT) +DEF(st8, 0, 2, 1, TCG_OPF_INT) +DEF(st16, 0, 2, 1, TCG_OPF_INT) +DEF(st32, 0, 2, 1, TCG_OPF_INT) +DEF(st, 0, 2, 1, TCG_OPF_INT) +DEF(sub, 1, 2, 0, TCG_OPF_INT) +DEF(xor, 1, 2, 0, TCG_OPF_INT) + +DEF(addco, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_OUT) +DEF(addc1o, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_OUT) +DEF(addci, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_IN) +DEF(addcio, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_IN | TCG_OPF_CARRY_OUT) + +DEF(subbo, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_OUT) +DEF(subb1o, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_OUT) +DEF(subbi, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_IN) +DEF(subbio, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_IN | TCG_OPF_CARRY_OUT) + +DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) +DEF(setcond2_i32, 1, 4, 1, 0) /* size changing ops */ -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_extr_i64_i32) - | (TCG_TARGET_REG_BITS == 32 ? TCG_OPF_NOT_PRESENT : 0)) -DEF(extrh_i64_i32, 1, 1, 0, - 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) -DEF(ext8s_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ext8s_i64)) -DEF(ext16s_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ext16s_i64)) -DEF(ext32s_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ext32s_i64)) -DEF(ext8u_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ext8u_i64)) -DEF(ext16u_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ext16u_i64)) -DEF(ext32u_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ext32u_i64)) -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(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)) -DEF(nand_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_nand_i64)) -DEF(nor_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_nor_i64)) -DEF(clz_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_clz_i64)) -DEF(ctz_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ctz_i64)) -DEF(ctpop_i64, 1, 1, 0, IMPL64 | IMPL(TCG_TARGET_HAS_ctpop_i64)) - -DEF(add2_i64, 2, 4, 0, IMPL64 | IMPL(TCG_TARGET_HAS_add2_i64)) -DEF(sub2_i64, 2, 4, 0, IMPL64 | IMPL(TCG_TARGET_HAS_sub2_i64)) -DEF(mulu2_i64, 2, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_mulu2_i64)) -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)) +DEF(ext_i32_i64, 1, 1, 0, 0) +DEF(extu_i32_i64, 1, 1, 0, 0) +DEF(extrl_i64_i32, 1, 1, 0, 0) +DEF(extrh_i64_i32, 1, 1, 0, 0) #define DATA64_ARGS (TCG_TARGET_REG_BITS == 64 ? 1 : 2) -/* There are tcg_ctx->insn_start_words here, not just one. */ -DEF(insn_start, 0, 0, DATA64_ARGS, TCG_OPF_NOT_PRESENT) +DEF(insn_start, 0, 0, DATA64_ARGS * INSN_START_WORDS, 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(exit_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) +DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) -DEF(plugin_cb_start, 0, 0, 3, TCG_OPF_NOT_PRESENT) -DEF(plugin_cb_end, 0, 0, 0, TCG_OPF_NOT_PRESENT) - -/* 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_a32_i32, 0, 1 + 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_ld_a32_i64, DATA64_ARGS, 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_64BIT) -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) +DEF(plugin_cb, 0, 0, 1, TCG_OPF_NOT_PRESENT) +DEF(plugin_mem_cb, 0, 1, 1, TCG_OPF_NOT_PRESENT) -/* Only used by i386 to cope with stupid register constraints. */ -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)) +DEF(qemu_ld, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_INT) +DEF(qemu_st, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_INT) +DEF(qemu_ld2, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_INT) +DEF(qemu_st2, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_INT) /* Host vector support. */ -#define IMPLVEC TCG_OPF_VECTOR | IMPL(TCG_TARGET_MAYBE_vec) - DEF(mov_vec, 1, 1, 0, TCG_OPF_VECTOR | TCG_OPF_NOT_PRESENT) -DEF(dup_vec, 1, 1, 0, IMPLVEC) -DEF(dup2_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_REG_BITS == 32)) - -DEF(ld_vec, 1, 1, 1, IMPLVEC) -DEF(st_vec, 0, 2, 1, IMPLVEC) -DEF(dupm_vec, 1, 1, 1, IMPLVEC) - -DEF(add_vec, 1, 2, 0, IMPLVEC) -DEF(sub_vec, 1, 2, 0, IMPLVEC) -DEF(mul_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_mul_vec)) -DEF(neg_vec, 1, 1, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_neg_vec)) -DEF(abs_vec, 1, 1, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_abs_vec)) -DEF(ssadd_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_sat_vec)) -DEF(usadd_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_sat_vec)) -DEF(sssub_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_sat_vec)) -DEF(ussub_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_sat_vec)) -DEF(smin_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_minmax_vec)) -DEF(umin_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_minmax_vec)) -DEF(smax_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_minmax_vec)) -DEF(umax_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_minmax_vec)) - -DEF(and_vec, 1, 2, 0, IMPLVEC) -DEF(or_vec, 1, 2, 0, IMPLVEC) -DEF(xor_vec, 1, 2, 0, IMPLVEC) -DEF(andc_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_andc_vec)) -DEF(orc_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_orc_vec)) -DEF(nand_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_nand_vec)) -DEF(nor_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_nor_vec)) -DEF(eqv_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_eqv_vec)) -DEF(not_vec, 1, 1, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_not_vec)) - -DEF(shli_vec, 1, 1, 1, IMPLVEC | IMPL(TCG_TARGET_HAS_shi_vec)) -DEF(shri_vec, 1, 1, 1, IMPLVEC | IMPL(TCG_TARGET_HAS_shi_vec)) -DEF(sari_vec, 1, 1, 1, IMPLVEC | IMPL(TCG_TARGET_HAS_shi_vec)) -DEF(rotli_vec, 1, 1, 1, IMPLVEC | IMPL(TCG_TARGET_HAS_roti_vec)) - -DEF(shls_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_shs_vec)) -DEF(shrs_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_shs_vec)) -DEF(sars_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_shs_vec)) -DEF(rotls_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_rots_vec)) - -DEF(shlv_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_shv_vec)) -DEF(shrv_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_shv_vec)) -DEF(sarv_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_shv_vec)) -DEF(rotlv_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_rotv_vec)) -DEF(rotrv_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_rotv_vec)) - -DEF(cmp_vec, 1, 2, 1, IMPLVEC) - -DEF(bitsel_vec, 1, 3, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_bitsel_vec)) -DEF(cmpsel_vec, 1, 4, 1, IMPLVEC | IMPL(TCG_TARGET_HAS_cmpsel_vec)) +DEF(dup_vec, 1, 1, 0, TCG_OPF_VECTOR) +DEF(dup2_vec, 1, 2, 0, TCG_OPF_VECTOR) + +DEF(ld_vec, 1, 1, 1, TCG_OPF_VECTOR) +DEF(st_vec, 0, 2, 1, TCG_OPF_VECTOR) +DEF(dupm_vec, 1, 1, 1, TCG_OPF_VECTOR) + +DEF(add_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(sub_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(mul_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(neg_vec, 1, 1, 0, TCG_OPF_VECTOR) +DEF(abs_vec, 1, 1, 0, TCG_OPF_VECTOR) +DEF(ssadd_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(usadd_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(sssub_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(ussub_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(smin_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(umin_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(smax_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(umax_vec, 1, 2, 0, TCG_OPF_VECTOR) + +DEF(and_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(or_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(xor_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(andc_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(orc_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(nand_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(nor_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(eqv_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(not_vec, 1, 1, 0, TCG_OPF_VECTOR) + +DEF(shli_vec, 1, 1, 1, TCG_OPF_VECTOR) +DEF(shri_vec, 1, 1, 1, TCG_OPF_VECTOR) +DEF(sari_vec, 1, 1, 1, TCG_OPF_VECTOR) +DEF(rotli_vec, 1, 1, 1, TCG_OPF_VECTOR) + +DEF(shls_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(shrs_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(sars_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(rotls_vec, 1, 2, 0, TCG_OPF_VECTOR) + +DEF(shlv_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(shrv_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(sarv_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(rotlv_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(rotrv_vec, 1, 2, 0, TCG_OPF_VECTOR) + +DEF(cmp_vec, 1, 2, 1, TCG_OPF_VECTOR) + +DEF(bitsel_vec, 1, 3, 0, TCG_OPF_VECTOR) +DEF(cmpsel_vec, 1, 4, 1, TCG_OPF_VECTOR) DEF(last_generic, 0, 0, 0, TCG_OPF_NOT_PRESENT) -#if TCG_TARGET_MAYBE_vec -#include "tcg-target.opc.h" -#endif - -#ifdef TCG_TARGET_INTERPRETER -/* These opcodes are only for use between the tci generator and interpreter. */ -DEF(tci_movi, 1, 0, 1, TCG_OPF_NOT_PRESENT) -DEF(tci_movl, 1, 0, 1, TCG_OPF_NOT_PRESENT) -#endif +#include "tcg-target-opc.h.inc" #undef DATA64_ARGS -#undef IMPL -#undef IMPL64 -#undef IMPLVEC #undef DEF diff --git a/include/tcg/tcg-temp-internal.h b/include/tcg/tcg-temp-internal.h index dded2917e58..98f91e68b70 100644 --- a/include/tcg/tcg-temp-internal.h +++ b/include/tcg/tcg-temp-internal.h @@ -31,53 +31,21 @@ void tcg_temp_free_internal(TCGTemp *); -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_i128(TCGv_i128 arg) -{ - tcg_temp_free_internal(tcgv_i128_temp(arg)); -} - -static inline void tcg_temp_free_ptr(TCGv_ptr arg) -{ - tcg_temp_free_internal(tcgv_ptr_temp(arg)); -} +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); -static inline void tcg_temp_free_vec(TCGv_vec arg) -{ - tcg_temp_free_internal(tcgv_vec_temp(arg)); -} - -static inline TCGv_i32 tcg_temp_ebb_new_i32(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I32, TEMP_EBB); - return temp_tcgv_i32(t); -} - -static inline TCGv_i64 tcg_temp_ebb_new_i64(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I64, TEMP_EBB); - return temp_tcgv_i64(t); -} - -static inline TCGv_i128 tcg_temp_ebb_new_i128(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I128, TEMP_EBB); - return temp_tcgv_i128(t); -} +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); -static inline TCGv_ptr tcg_temp_ebb_new_ptr(void) +/* Forget all freed EBB temps, so that new allocations produce new temps. */ +static inline void tcg_temp_ebb_reset_freed(TCGContext *s) { - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_PTR, TEMP_EBB); - return temp_tcgv_ptr(t); + 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 c9c6d770d05..125323f153a 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -34,13 +34,12 @@ #include "tcg-target-reg-bits.h" #include "tcg-target.h" #include "tcg/tcg-cond.h" +#include "tcg/insn-start-words.h" #include "tcg/debug-assert.h" /* XXX: make safe guess about sizes */ #define MAX_OP_PER_INSTR 266 -#define MAX_CALL_IARGS 7 - #define CPU_TEMP_BUF_NLONGS 128 #define TCG_STATIC_FRAME_SIZE (CPU_TEMP_BUF_NLONGS * sizeof(long)) @@ -66,112 +65,6 @@ typedef uint64_t TCGRegSet; #error unsupported #endif -#if TCG_TARGET_REG_BITS == 32 -/* Turn some undef macros into false macros. */ -#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 -#define TCG_TARGET_HAS_rot_i64 0 -#define TCG_TARGET_HAS_ext8s_i64 0 -#define TCG_TARGET_HAS_ext16s_i64 0 -#define TCG_TARGET_HAS_ext32s_i64 0 -#define TCG_TARGET_HAS_ext8u_i64 0 -#define TCG_TARGET_HAS_ext16u_i64 0 -#define TCG_TARGET_HAS_ext32u_i64 0 -#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 -#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 0 -#define TCG_TARGET_HAS_ctz_i64 0 -#define TCG_TARGET_HAS_ctpop_i64 0 -#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_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 -#define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 0 -#define TCG_TARGET_HAS_mulsh_i64 0 -/* Turn some undef macros into true macros. */ -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 -#endif - -#ifndef TCG_TARGET_deposit_i32_valid -#define TCG_TARGET_deposit_i32_valid(ofs, len) 1 -#endif -#ifndef TCG_TARGET_deposit_i64_valid -#define TCG_TARGET_deposit_i64_valid(ofs, len) 1 -#endif -#ifndef TCG_TARGET_extract_i32_valid -#define TCG_TARGET_extract_i32_valid(ofs, len) 1 -#endif -#ifndef TCG_TARGET_extract_i64_valid -#define TCG_TARGET_extract_i64_valid(ofs, len) 1 -#endif - -/* Only one of DIV or DIV2 should be defined. */ -#if defined(TCG_TARGET_HAS_div_i32) -#define TCG_TARGET_HAS_div2_i32 0 -#elif defined(TCG_TARGET_HAS_div2_i32) -#define TCG_TARGET_HAS_div_i32 0 -#define TCG_TARGET_HAS_rem_i32 0 -#endif -#if defined(TCG_TARGET_HAS_div_i64) -#define TCG_TARGET_HAS_div2_i64 0 -#elif defined(TCG_TARGET_HAS_div2_i64) -#define TCG_TARGET_HAS_div_i64 0 -#define TCG_TARGET_HAS_rem_i64 0 -#endif - -#if !defined(TCG_TARGET_HAS_v64) \ - && !defined(TCG_TARGET_HAS_v128) \ - && !defined(TCG_TARGET_HAS_v256) -#define TCG_TARGET_MAYBE_vec 0 -#define TCG_TARGET_HAS_abs_vec 0 -#define TCG_TARGET_HAS_neg_vec 0 -#define TCG_TARGET_HAS_not_vec 0 -#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_roti_vec 0 -#define TCG_TARGET_HAS_rots_vec 0 -#define TCG_TARGET_HAS_rotv_vec 0 -#define TCG_TARGET_HAS_shi_vec 0 -#define TCG_TARGET_HAS_shs_vec 0 -#define TCG_TARGET_HAS_shv_vec 0 -#define TCG_TARGET_HAS_mul_vec 0 -#define TCG_TARGET_HAS_sat_vec 0 -#define TCG_TARGET_HAS_minmax_vec 0 -#define TCG_TARGET_HAS_bitsel_vec 0 -#define TCG_TARGET_HAS_cmpsel_vec 0 -#else -#define TCG_TARGET_MAYBE_vec 1 -#endif -#ifndef TCG_TARGET_HAS_v64 -#define TCG_TARGET_HAS_v64 0 -#endif -#ifndef TCG_TARGET_HAS_v128 -#define TCG_TARGET_HAS_v128 0 -#endif -#ifndef TCG_TARGET_HAS_v256 -#define TCG_TARGET_HAS_v256 0 -#endif - typedef enum TCGOpcode { #define DEF(name, oargs, iargs, cargs, flags) INDEX_op_ ## name, #include "tcg/tcg-opc.h" @@ -284,29 +177,6 @@ static inline int tcg_type_size(TCGType t) return 4 << i; } -/** - * get_alignment_bits - * @memop: MemOp value - * - * Extract the alignment size from the memop. - */ -static inline unsigned get_alignment_bits(MemOp memop) -{ - 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; -} - typedef tcg_target_ulong TCGArg; /* Define type and accessor macros for TCG variables. @@ -319,6 +189,7 @@ typedef tcg_target_ulong TCGArg; * TCGv_i64 : 64 bit integer type * TCGv_i128 : 128 bit integer type * TCGv_ptr : a host pointer type + * TCGv_vaddr: an integer type wide enough to hold a target 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 @@ -347,6 +218,14 @@ typedef struct TCGv_ptr_d *TCGv_ptr; typedef struct TCGv_vec_d *TCGv_vec; typedef TCGv_ptr TCGv_env; +#if __SIZEOF_POINTER__ == 4 +typedef TCGv_i32 TCGv_vaddr; +#elif __SIZEOF_POINTER__ == 8 +typedef TCGv_i64 TCGv_vaddr; +#else +# error "sizeof pointer is different from {4,8}" +#endif /* __SIZEOF_POINTER__ */ + /* call flags */ /* Helper does not read globals (either directly or through an exception). It implies TCG_CALL_NO_WRITE_GLOBALS. */ @@ -357,8 +236,6 @@ typedef TCGv_ptr TCGv_env; #define TCG_CALL_NO_SIDE_EFFECTS 0x0004 /* Helper is G_NORETURN. */ #define TCG_CALL_NO_RETURN 0x0008 -/* Helper is part of Plugins. */ -#define TCG_CALL_PLUGIN 0x0010 /* convenience version of most used call flags */ #define TCG_CALL_NO_RWG TCG_CALL_NO_READ_GLOBALS @@ -414,7 +291,7 @@ typedef struct TCGTemp { unsigned int mem_coherent:1; unsigned int mem_allocated:1; unsigned int temp_allocated:1; - unsigned int temp_subindex:1; + unsigned int temp_subindex:2; int64_t val; struct TCGTemp *mem_base; @@ -467,7 +344,8 @@ struct TCGOp { #define TCGOP_CALLI(X) (X)->param1 #define TCGOP_CALLO(X) (X)->param2 -#define TCGOP_VECL(X) (X)->param1 +#define TCGOP_TYPE(X) (X)->param1 +#define TCGOP_FLAGS(X) (X)->param2 #define TCGOP_VECE(X) (X)->param2 /* Make sure operands fit in the bitfields above. */ @@ -487,14 +365,6 @@ struct TCGContext { int nb_indirects; int nb_ops; TCGType addr_type; /* TCG_TYPE_I32 or TCG_TYPE_I64 */ - -#ifdef CONFIG_SOFTMMU - int tlb_fast_offset; - int page_mask; - uint8_t page_bits; - uint8_t tlb_dyn_max_bits; -#endif - uint8_t insn_start_words; TCGBar guest_mo; TCGRegSet reserved_regs; @@ -528,12 +398,8 @@ struct TCGContext { CPUState *cpu; /* *_trans */ /* These structures are private to tcg-target.c.inc. */ -#ifdef TCG_TARGET_NEED_LDST_LABELS QSIMPLEQ_HEAD(, TCGLabelQemuLdst) ldst_labels; -#endif -#ifdef TCG_TARGET_NEED_POOL_LABELS struct TCGLabelPoolData *pool_labels; -#endif TCGLabel *exitreq_label; @@ -546,11 +412,23 @@ 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 + /* + * During the tcg_reg_alloc_op loop, we are within a sequence of + * carry-using opcodes like addco+addci. + */ + bool carry_live; + GHashTable *const_table[TCG_TYPE_COUNT]; TCGTempSet free_temps[TCG_TYPE_COUNT]; TCGTemp temps[TCG_MAX_TEMPS]; /* globals first, temps after */ @@ -558,6 +436,12 @@ struct TCGContext { 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]; @@ -574,10 +458,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); @@ -692,23 +582,29 @@ static inline TCGv_ptr temp_tcgv_ptr(TCGTemp *t) return (TCGv_ptr)temp_tcgv_i32(t); } +static inline TCGv_vaddr temp_tcgv_vaddr(TCGTemp *t) +{ + return (TCGv_vaddr)temp_tcgv_i32(t); +} + static inline TCGv_vec temp_tcgv_vec(TCGTemp *t) { return (TCGv_vec)temp_tcgv_i32(t); } -static inline TCGArg tcg_get_insn_param(TCGOp *op, int arg) +static inline TCGArg tcg_get_insn_param(TCGOp *op, unsigned arg) { return op->args[arg]; } -static inline void tcg_set_insn_param(TCGOp *op, int arg, TCGArg v) +static inline void tcg_set_insn_param(TCGOp *op, unsigned arg, TCGArg v) { op->args[arg] = v; } -static inline uint64_t tcg_get_insn_start_param(TCGOp *op, int arg) +static inline uint64_t tcg_get_insn_start_param(TCGOp *op, unsigned arg) { + tcg_debug_assert(arg < INSN_START_WORDS); if (TCG_TARGET_REG_BITS == 64) { return tcg_get_insn_param(op, arg); } else { @@ -717,8 +613,9 @@ static inline uint64_t tcg_get_insn_start_param(TCGOp *op, int arg) } } -static inline void tcg_set_insn_start_param(TCGOp *op, int arg, uint64_t v) +static inline void tcg_set_insn_start_param(TCGOp *op, unsigned arg, uint64_t v) { + tcg_debug_assert(arg < INSN_START_WORDS); if (TCG_TARGET_REG_BITS == 64) { tcg_set_insn_param(op, arg, v); } else { @@ -758,10 +655,51 @@ void tcg_region_reset_all(void); size_t tcg_code_size(void); size_t tcg_code_capacity(void); +/** + * tcg_tb_insert: + * @tb: translation block to insert + * + * Insert @tb into the region trees. + */ void tcg_tb_insert(TranslationBlock *tb); + +/** + * tcg_tb_remove: + * @tb: translation block to remove + * + * Remove @tb from the region trees. + */ void tcg_tb_remove(TranslationBlock *tb); + +/** + * tcg_tb_lookup: + * @tc_ptr: host PC to look up + * + * Look up a translation block inside the region trees by @tc_ptr. This is + * useful for exception handling, but must not be used for the purposes of + * executing the returned translation block. See struct tb_tc for more + * information. + * + * Returns: a translation block previously inserted into the region trees, + * such that @tc_ptr points anywhere inside the code generated for it, or + * NULL. + */ TranslationBlock *tcg_tb_lookup(uintptr_t tc_ptr); + +/** + * tcg_tb_foreach: + * @func: callback + * @user_data: opaque value to pass to @callback + * + * Call @func for each translation block inserted into the region trees. + */ void tcg_tb_foreach(GTraverseFunc func, gpointer user_data); + +/** + * tcg_nb_tbs: + * + * Returns: the number of translation blocks inserted into the region trees. + */ size_t tcg_nb_tbs(void); /* user-mode: Called with mmap_lock held. */ @@ -783,9 +721,6 @@ static inline void *tcg_malloc(int size) } } -void tcg_init(size_t tb_size, int splitwx, unsigned max_cpus); -void tcg_register_thread(void); -void tcg_prologue_init(TCGContext *s); void tcg_func_start(TCGContext *s); int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start); @@ -795,61 +730,8 @@ void tb_target_set_jmp_target(const TranslationBlock *, int, 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, TCGTempKind); -TCGv_vec tcg_temp_new_vec(TCGType type); -TCGv_vec tcg_temp_new_vec_matching(TCGv_vec match); - -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, TEMP_TB); - 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, TEMP_TB); - return temp_tcgv_i64(t); -} - -static inline TCGv_i128 tcg_temp_new_i128(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I128, TEMP_TB); - return temp_tcgv_i128(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, TEMP_TB); - return temp_tcgv_ptr(t); -} - -void tcg_dump_info(GString *buf); -void tcg_dump_op_count(GString *buf); - -#define TCG_CT_CONST 1 /* any constant of register size */ +#define TCG_CT_CONST 1 /* any constant of register size */ +#define TCG_CT_REG_ZERO 2 /* zero, in TCG_REG_ZERO */ typedef struct TCGArgConstraint { unsigned ct : 16; @@ -876,54 +758,62 @@ enum { /* Instruction has side effects: it cannot be removed if its outputs are not used, and might trigger exceptions. */ TCG_OPF_SIDE_EFFECTS = 0x08, - /* Instruction operands are 64-bits (otherwise 32-bits). */ - TCG_OPF_64BIT = 0x10, + /* Instruction operands may be I32 or I64 */ + TCG_OPF_INT = 0x10, /* Instruction is optional and not implemented by the host, or insn is generic and should not be implemented by the host. */ TCG_OPF_NOT_PRESENT = 0x20, /* Instruction operands are vectors. */ TCG_OPF_VECTOR = 0x40, /* Instruction is a conditional branch. */ - TCG_OPF_COND_BRANCH = 0x80 + TCG_OPF_COND_BRANCH = 0x80, + /* Instruction produces carry out. */ + TCG_OPF_CARRY_OUT = 0x100, + /* Instruction consumes carry in. */ + TCG_OPF_CARRY_IN = 0x200, }; typedef struct TCGOpDef { const char *name; uint8_t nb_oargs, nb_iargs, nb_cargs, nb_args; - uint8_t flags; - TCGArgConstraint *args_ct; + uint16_t flags; } TCGOpDef; -extern TCGOpDef tcg_op_defs[]; +extern const TCGOpDef tcg_op_defs[]; extern const size_t tcg_op_defs_max; -typedef struct TCGTargetOpDef { - TCGOpcode op; - const char *args_ct_str[TCG_MAX_OP_ARGS]; -} TCGTargetOpDef; - -bool tcg_op_supported(TCGOpcode op); +/* + * tcg_op_supported: + * Query if @op, for @type and @flags, is supported by the host + * on which we are currently executing. + */ +bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags); +/* + * tcg_op_deposit_valid: + * Query if a deposit into (ofs, len) is supported for @type by + * the host on which we are currently executing. + */ +bool tcg_op_deposit_valid(TCGType type, unsigned ofs, unsigned len); -void tcg_gen_call0(TCGHelperInfo *, TCGTemp *ret); -void tcg_gen_call1(TCGHelperInfo *, TCGTemp *ret, TCGTemp *); -void tcg_gen_call2(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *); -void tcg_gen_call3(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, +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_call4(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, - TCGTemp *, TCGTemp *); -void tcg_gen_call5(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, +void tcg_gen_call3(void *func, TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, TCGTemp *); -void tcg_gen_call6(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, +void tcg_gen_call4(void *func, TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *); -void tcg_gen_call7(TCGHelperInfo *, TCGTemp *ret, 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, unsigned nargs); void tcg_op_remove(TCGContext *s, TCGOp *op); -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: @@ -937,32 +827,6 @@ void tcg_remove_ops_after(TCGOp *op); void tcg_optimize(TCGContext *s); -/* - * 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_constant_ptr(x) ((TCGv_ptr)tcg_constant_i32((intptr_t)(x))) -#else -# define tcg_constant_ptr(x) ((TCGv_ptr)tcg_constant_i64((intptr_t)(x))) -#endif - TCGLabel *gen_new_label(void); /** @@ -1108,17 +972,10 @@ extern tcg_prologue_fn *tcg_qemu_tb_exec; void tcg_register_jit(const void *buf, size_t buf_size); -#if TCG_TARGET_MAYBE_vec /* Return zero if the tuple (opc, type, vece) is unsupportable; return > 0 if it is directly supportable; return < 0 if we must call tcg_expand_vec_op. */ int tcg_can_emit_vec_op(TCGOpcode, TCGType, unsigned); -#else -static inline int tcg_can_emit_vec_op(TCGOpcode o, TCGType t, unsigned ve) -{ - return 0; -} -#endif /* Expand the tuple (opc, type, vece) on the given arguments. */ void tcg_expand_vec_op(TCGOpcode, TCGType, unsigned, TCGArg, ...); @@ -1147,5 +1004,6 @@ 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); #endif /* TCG_H */ diff --git a/include/ui/clipboard.h b/include/ui/clipboard.h index ab6acdbd8a4..62a96ce9ff5 100644 --- a/include/ui/clipboard.h +++ b/include/ui/clipboard.h @@ -2,6 +2,7 @@ #define QEMU_CLIPBOARD_H #include "qemu/notify.h" +#include "migration/vmstate.h" /** * DOC: Introduction @@ -25,6 +26,9 @@ typedef enum QemuClipboardSelection QemuClipboardSelection; typedef struct QemuClipboardPeer QemuClipboardPeer; typedef struct QemuClipboardNotify QemuClipboardNotify; typedef struct QemuClipboardInfo QemuClipboardInfo; +typedef struct QemuClipboardContent QemuClipboardContent; + +extern const VMStateDescription vmstate_cbinfo; /** * enum QemuClipboardType @@ -97,6 +101,24 @@ struct QemuClipboardNotify { }; }; + +/** + * struct QemuClipboardContent + * + * @available: whether the data is available + * @requested: whether the data was requested + * @size: the size of the @data + * @data: the clipboard data + * + * Clipboard content. + */ +struct QemuClipboardContent { + bool available; + bool requested; + uint32_t size; + void *data; +}; + /** * struct QemuClipboardInfo * @@ -112,15 +134,10 @@ struct QemuClipboardNotify { struct QemuClipboardInfo { uint32_t refcount; QemuClipboardPeer *owner; - QemuClipboardSelection selection; + int selection; /* QemuClipboardSelection */ bool has_serial; uint32_t serial; - struct { - bool available; - bool requested; - size_t size; - void *data; - } types[QEMU_CLIPBOARD_TYPE__COUNT]; + QemuClipboardContent types[QEMU_CLIPBOARD_TYPE__COUNT]; }; /** diff --git a/include/ui/console.h b/include/ui/console.h index 28882f15a53..46b31281850 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -7,6 +7,7 @@ #include "qapi/qapi-types-ui.h" #include "ui/input.h" #include "ui/surface.h" +#include "ui/dmabuf.h" #define TYPE_QEMU_CONSOLE "qemu-console" OBJECT_DECLARE_TYPE(QemuConsole, QemuConsoleClass, QEMU_CONSOLE) @@ -69,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); @@ -174,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; @@ -185,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 backing_width; - uint32_t backing_height; - bool y0_top; - void *sync; - int fence_fd; - bool allow_fences; - bool draw_submitted; -} QemuDmaBuf; - enum display_scanout { SCANOUT_NONE, SCANOUT_SURFACE, @@ -251,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); @@ -340,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); @@ -413,6 +391,7 @@ 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, @@ -422,7 +401,6 @@ 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); @@ -433,7 +411,6 @@ 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); @@ -463,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 00000000000..3decdca4979 --- /dev/null +++ b/include/ui/dmabuf.h @@ -0,0 +1,53 @@ +/* + * 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 + +#define DMABUF_MAX_PLANES 4 + +typedef struct QemuDmaBuf QemuDmaBuf; + +QemuDmaBuf *qemu_dmabuf_new(uint32_t width, uint32_t height, + const uint32_t *offset, const uint32_t *stride, + uint32_t x, uint32_t y, + uint32_t backing_width, uint32_t backing_height, + uint32_t fourcc, uint64_t modifier, + const int32_t *dmabuf_fd, uint32_t num_planes, + bool allow_fences, bool y0_top); +void qemu_dmabuf_free(QemuDmaBuf *dmabuf); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QemuDmaBuf, qemu_dmabuf_free); + +const int *qemu_dmabuf_get_fds(QemuDmaBuf *dmabuf, int *nfds); +void qemu_dmabuf_dup_fds(QemuDmaBuf *dmabuf, int *fds, int nfds); +void qemu_dmabuf_close(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_width(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_height(QemuDmaBuf *dmabuf); +const uint32_t *qemu_dmabuf_get_offsets(QemuDmaBuf *dmabuf, int *noffsets); +const uint32_t *qemu_dmabuf_get_strides(QemuDmaBuf *dmabuf, int *nstrides); +uint32_t qemu_dmabuf_get_num_planes(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); + +#endif diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h index 4b8c0d2281c..acf993fcf52 100644 --- a/include/ui/egl-helpers.h +++ b/include/ui/egl-helpers.h @@ -17,6 +17,8 @@ extern bool qemu_egl_angle_d3d; typedef struct egl_fb { int width; int height; + int x; + int y; GLuint texture; GLuint framebuffer; bool delete_texture; @@ -26,7 +28,7 @@ typedef struct 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_default(egl_fb *fb, int width, int height, int x, int y); void egl_fb_setup_for_tex(egl_fb *fb, int width, int height, GLuint texture, bool delete); void egl_fb_setup_new_tex(egl_fb *fb, int width, int height); @@ -46,8 +48,9 @@ extern int qemu_egl_rn_fd; extern struct gbm_device *qemu_egl_rn_gbm_dev; int egl_rendernode_init(const char *rendernode, DisplayGLMode mode); -int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc, - EGLuint64KHR *modifier); +bool egl_dmabuf_export_texture(uint32_t tex_id, int *fd, EGLint *offset, + EGLint *stride, EGLint *fourcc, int *num_planes, + EGLuint64KHR *modifier); void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf); void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf); diff --git a/include/ui/gtk.h b/include/ui/gtk.h index aa3d6370299..d3944046dbb 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -224,4 +224,6 @@ int gd_gl_area_make_current(DisplayGLCtx *dgc, /* gtk-clipboard.c */ void gd_clipboard_init(GtkDisplayState *gd); +void gd_update_scale(VirtualConsole *vc, int ww, int wh, int fbw, int fbh); + #endif /* UI_GTK_H */ diff --git a/include/ui/input.h b/include/ui/input.h index c29a730a71b..8f9aac562ed 100644 --- a/include/ui/input.h +++ b/include/ui/input.h @@ -30,7 +30,7 @@ struct QemuInputHandler { }; QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev, - QemuInputHandler *handler); + const QemuInputHandler *handler); void qemu_input_handler_activate(QemuInputHandlerState *s); void qemu_input_handler_deactivate(QemuInputHandlerState *s); void qemu_input_handler_unregister(QemuInputHandlerState *s); @@ -57,7 +57,7 @@ void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down); void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map, uint32_t button_old, uint32_t button_new); -bool qemu_input_is_absolute(void); +bool qemu_input_is_absolute(QemuConsole *con); int qemu_input_scale_axis(int value, int min_in, int max_in, int min_out, int max_out); diff --git a/include/ui/kbd-state.h b/include/ui/kbd-state.h index fb79776128c..1f37b932eb6 100644 --- a/include/ui/kbd-state.h +++ b/include/ui/kbd-state.h @@ -99,4 +99,15 @@ bool qkbd_state_modifier_get(QKbdState *kbd, QKbdModifier mod); */ void qkbd_state_lift_all_keys(QKbdState *kbd); +/** + * qkbd_state_switch_console: Switch console. + * + * This sends key up events to the previous console for all keys which are in + * down state to prevent keys being stuck, and remembers the new console. + * + * @kbd: state tracker state. + * @con: new QemuConsole for this state tracker. + */ +void qkbd_state_switch_console(QKbdState *kbd, QemuConsole *con); + #endif /* QEMU_UI_KBD_STATE_H */ diff --git a/include/ui/pixman-minimal.h b/include/ui/pixman-minimal.h new file mode 100644 index 00000000000..6dd7de1c7e7 --- /dev/null +++ b/include/ui/pixman-minimal.h @@ -0,0 +1,239 @@ +/* + * SPDX-License-Identifier: MIT + * + * Tiny subset of PIXMAN API commonly used by QEMU. + * + * Copyright 1987, 1988, 1989, 1998 The Open Group + * Copyright 1987, 1988, 1989 Digital Equipment Corporation + * Copyright 1999, 2004, 2008 Keith Packard + * Copyright 2000 SuSE, Inc. + * Copyright 2000 Keith Packard, member of The XFree86 Project, Inc. + * Copyright 2004, 2005, 2007, 2008, 2009, 2010 Red Hat, Inc. + * Copyright 2004 Nicholas Miell + * Copyright 2005 Lars Knoll & Zack Rusin, Trolltech + * Copyright 2005 Trolltech AS + * Copyright 2007 Luca Barbato + * Copyright 2008 Aaron Plattner, NVIDIA Corporation + * Copyright 2008 Rodrigo Kumpera + * Copyright 2008 André Tupinambá + * Copyright 2008 Mozilla Corporation + * Copyright 2008 Frederic Plourde + * Copyright 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright 2009, 2010 Nokia Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef PIXMAN_MINIMAL_H +#define PIXMAN_MINIMAL_H + +#define PIXMAN_TYPE_OTHER 0 +#define PIXMAN_TYPE_ARGB 2 +#define PIXMAN_TYPE_ABGR 3 +#define PIXMAN_TYPE_BGRA 8 +#define PIXMAN_TYPE_RGBA 9 + +#define PIXMAN_FORMAT(bpp, type, a, r, g, b) (((bpp) << 24) | \ + ((type) << 16) | \ + ((a) << 12) | \ + ((r) << 8) | \ + ((g) << 4) | \ + ((b))) + +#define PIXMAN_FORMAT_RESHIFT(val, ofs, num) \ + (((val >> (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 51f87093279..2ca0ed7029c 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,6 +49,8 @@ # 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) \ @@ -69,18 +75,18 @@ PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format); 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); +int qemu_pixman_get_type(int rshift, int gshift, int bshift, int endian); 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, int endian); 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); 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_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font, unsigned int ch); @@ -89,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/rect.h b/include/ui/rect.h new file mode 100644 index 00000000000..7ebf47ebcdc --- /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 e3acc7c82a7..dbe6e3d9739 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); diff --git a/include/ui/surface.h b/include/ui/surface.h index 4244e0ca4a3..f16f7be8be8 100644 --- a/include/ui/surface.h +++ b/include/ui/surface.h @@ -23,10 +23,8 @@ typedef struct DisplaySurface { GLenum gltype; GLuint texture; #endif -#ifdef WIN32 - HANDLE handle; - uint32_t handle_offset; -#endif + qemu_pixman_shareable share_handle; + uint32_t share_handle_offset; } DisplaySurface; PixelFormat qemu_default_pixelformat(int bpp); @@ -37,20 +35,20 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height, DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image); DisplaySurface *qemu_create_placeholder_surface(int w, int h, const char *msg); -#ifdef WIN32 -void qemu_displaysurface_win32_set_handle(DisplaySurface *surface, - HANDLE h, uint32_t offset); -#endif + +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 is_buffer_shared(DisplaySurface *surface) +static inline int surface_is_allocated(DisplaySurface *surface) { - return !(surface->flags & QEMU_ALLOCATED_FLAG); + return surface->flags & QEMU_ALLOCATED_FLAG; } -static inline int is_placeholder(DisplaySurface *surface) +static inline int surface_is_placeholder(DisplaySurface *surface) { return surface->flags & QEMU_PLACEHOLDER_FLAG; } diff --git a/include/exec/user/abitypes.h b/include/user/abitypes.h similarity index 87% rename from include/exec/user/abitypes.h rename to include/user/abitypes.h index 6178453d941..7528124b62f 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,18 +21,10 @@ #define ABI_LLONG_ALIGNMENT 2 #endif -#ifdef TARGET_CRIS -#define ABI_SHORT_ALIGNMENT 1 -#define ABI_INT_ALIGNMENT 1 -#define ABI_LONG_ALIGNMENT 1 -#define ABI_LLONG_ALIGNMENT 1 -#endif - #if (defined(TARGET_I386) && !defined(TARGET_X86_64)) \ || defined(TARGET_SH4) \ || defined(TARGET_OPENRISC) \ - || defined(TARGET_MICROBLAZE) \ - || defined(TARGET_NIOS2) + || defined(TARGET_MICROBLAZE) #define ABI_LLONG_ALIGNMENT 4 #endif diff --git a/include/user/cpu_loop.h b/include/user/cpu_loop.h new file mode 100644 index 00000000000..ad8a1d711f0 --- /dev/null +++ b/include/user/cpu_loop.h @@ -0,0 +1,88 @@ +/* + * 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 . + */ + +#ifndef USER_CPU_LOOP_H +#define USER_CPU_LOOP_H + +#include "exec/vaddr.h" +#include "exec/mmu-access-type.h" + + +/** + * adjust_signal_pc: + * @pc: raw pc from the host signal ucontext_t. + * @is_write: host memory operation was write, or read-modify-write. + * + * Alter @pc as required for unwinding. Return the type of the + * guest memory access -- host reads may be for guest execution. + */ +MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write); + +/** + * handle_sigsegv_accerr_write: + * @cpu: the cpu context + * @old_set: the sigset_t from the signal ucontext_t + * @host_pc: the host pc, adjusted for the signal + * @host_addr: the host address of the fault + * + * Return true if the write fault has been handled, and should be re-tried. + */ +bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, + uintptr_t host_pc, vaddr guest_addr); + +/** + * cpu_loop_exit_sigsegv: + * @cpu: the cpu context + * @addr: the guest address of the fault + * @access_type: access was read/write/execute + * @maperr: true for invalid page, false for permission fault + * @ra: host pc for unwinding + * + * Use the TCGCPUOps hook to record cpu state, do guest operating system + * specific things to raise SIGSEGV, and jump to the main cpu loop. + */ +G_NORETURN void cpu_loop_exit_sigsegv(CPUState *cpu, vaddr addr, + MMUAccessType access_type, + bool maperr, uintptr_t ra); + +/** + * cpu_loop_exit_sigbus: + * @cpu: the cpu context + * @addr: the guest address of the alignment fault + * @access_type: access was read/write/execute + * @ra: host pc for unwinding + * + * Use the TCGCPUOps hook to record cpu state, do guest operating system + * specific things to raise SIGBUS, and jump to the main cpu loop. + */ +G_NORETURN void cpu_loop_exit_sigbus(CPUState *cpu, vaddr addr, + MMUAccessType access_type, + uintptr_t ra); + +G_NORETURN void cpu_loop(CPUArchState *env); + +void target_exception_dump(CPUArchState *env, const char *fmt, int code); +#define EXCP_DUMP(env, fmt, code) \ + target_exception_dump(env, fmt, code) + +typedef struct target_pt_regs target_pt_regs; + +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs); + +#endif diff --git a/include/user/guest-base.h b/include/user/guest-base.h new file mode 100644 index 00000000000..055c1d14fe4 --- /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/guest-host.h b/include/user/guest-host.h new file mode 100644 index 00000000000..8f7ef758962 --- /dev/null +++ b/include/user/guest-host.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * guest <-> host helpers. + * + * Copyright (c) 2003 Fabrice Bellard + */ + +#ifndef USER_GUEST_HOST_H +#define USER_GUEST_HOST_H + +#include "exec/vaddr.h" +#include "user/guest-base.h" +#include "accel/tcg/cpu-ops.h" + +/* + * If non-zero, the guest virtual address space is a contiguous subset + * of the host virtual address space, i.e. '-R reserved_va' is in effect + * either from the command-line or by default. The value is the last + * byte of the guest address space e.g. UINT32_MAX. + * + * If zero, the host and guest virtual address spaces are intermingled. + */ +extern unsigned long reserved_va; + +/* + * The last byte of the guest address space. + * If reserved_va is non-zero, guest_addr_max matches. + * If reserved_va is zero, guest_addr_max equals the full guest space. + */ +extern unsigned long guest_addr_max; + +static inline vaddr cpu_untagged_addr(CPUState *cs, vaddr x) +{ + const TCGCPUOps *tcg_ops = cs->cc->tcg_ops; + if (tcg_ops->untagged_addr) { + return tcg_ops->untagged_addr(cs, x); + } + return x; +} + +/* All direct uses of g2h and h2g need to go away for usermode softmmu. */ +static inline void *g2h_untagged(vaddr x) +{ + return (void *)((uintptr_t)(x) + guest_base); +} + +static inline void *g2h(CPUState *cs, vaddr x) +{ + return g2h_untagged(cpu_untagged_addr(cs, x)); +} + +static inline bool guest_addr_valid_untagged(vaddr x) +{ + return x <= guest_addr_max; +} + +static inline bool guest_range_valid_untagged(vaddr start, vaddr len) +{ + return len - 1 <= guest_addr_max && start <= guest_addr_max - len + 1; +} + +#define h2g_valid(x) \ + ((uintptr_t)(x) - guest_base <= guest_addr_max) + +#define h2g_nocheck(x) ({ \ + uintptr_t __ret = (uintptr_t)(x) - guest_base; \ + (vaddr)__ret; \ +}) + +#define h2g(x) ({ \ + /* Check if given address fits target address space */ \ + assert(h2g_valid(x)); \ + h2g_nocheck(x); \ +}) + +#endif diff --git a/include/user/mmap.h b/include/user/mmap.h new file mode 100644 index 00000000000..4d5e9aac70a --- /dev/null +++ b/include/user/mmap.h @@ -0,0 +1,32 @@ +/* + * MMAP declarations for QEMU user emulation + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef USER_MMAP_H +#define USER_MMAP_H + +#include "user/abitypes.h" + +/* + * 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, 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_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong alignment); + +void TSA_NO_TSA mmap_fork_start(void); +void TSA_NO_TSA mmap_fork_end(int child); + +#endif diff --git a/include/user/page-protection.h b/include/user/page-protection.h new file mode 100644 index 00000000000..4bde664e4a7 --- /dev/null +++ b/include/user/page-protection.h @@ -0,0 +1,96 @@ +/* + * QEMU page protection declarations. + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1+ + */ +#ifndef USER_PAGE_PROTECTION_H +#define USER_PAGE_PROTECTION_H + +#ifndef CONFIG_USER_ONLY +#error Cannot include this header from system emulation +#endif + +#include "exec/vaddr.h" +#include "exec/translation-block.h" + +int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc); + +int page_get_flags(vaddr address); + +/** + * 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(vaddr start, vaddr last, int flags); + +void page_reset_target_data(vaddr start, vaddr 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(vaddr start, vaddr 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(vaddr start, vaddr 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. + */ +vaddr page_find_range_empty(vaddr min, vaddr max, vaddr len, vaddr align); + +/** + * page_get_target_data + * @address: guest virtual address + * @size: per-page size + * + * Return @size bytes of out-of-band data to associate + * with the guest page at @address, allocating it if necessary. The + * caller should already have verified that the address is valid. + * The value of @size must be the same for every call. + * + * The memory will be freed when the guest page is deallocated, + * e.g. with the munmap system call. + */ +__attribute__((returns_nonnull)) +void *page_get_target_data(vaddr address, size_t size); + +typedef int (*walk_memory_regions_fn)(void *, vaddr, vaddr, int); +int walk_memory_regions(void *, walk_memory_regions_fn); + +void page_dump(FILE *f); + +#endif diff --git a/include/user/safe-syscall.h b/include/user/safe-syscall.h index 27b71cdbd8e..aa075f4d5cd 100644 --- a/include/user/safe-syscall.h +++ b/include/user/safe-syscall.h @@ -134,7 +134,7 @@ 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/signal.h b/include/user/signal.h new file mode 100644 index 00000000000..7fa33b05d91 --- /dev/null +++ b/include/user/signal.h @@ -0,0 +1,25 @@ +/* + * Signal-related declarations. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef USER_SIGNAL_H +#define USER_SIGNAL_H + +#ifndef CONFIG_USER_ONLY +#error Cannot include this header from system emulation +#endif + +/** + * target_to_host_signal: + * @sig: target signal. + * + * On success, return the host signal between 0 (inclusive) and NSIG + * (exclusive) corresponding to the target signal @sig. Return any other value + * on failure. + */ +int target_to_host_signal(int sig); + +extern int host_interrupt_signal; + +#endif diff --git a/include/user/syscall-trace.h b/include/user/syscall-trace.h index 557f881a79b..9bd7ca19c84 100644 --- a/include/user/syscall-trace.h +++ b/include/user/syscall-trace.h @@ -10,7 +10,8 @@ #ifndef SYSCALL_TRACE_H #define SYSCALL_TRACE_H -#include "exec/user/abitypes.h" +#include "user/abitypes.h" +#include "gdbstub/user.h" #include "qemu/plugin.h" #include "trace/trace-root.h" @@ -20,7 +21,7 @@ * 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, @@ -29,11 +30,13 @@ static inline void record_syscall_start(void *cpu, int num, 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) { 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 96% rename from include/exec/user/thunk.h rename to include/user/thunk.h index 6eedef48d8c..2a2104b568f 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 *); diff --git a/include/user/tswap-target.h b/include/user/tswap-target.h new file mode 100644 index 00000000000..4719330dbbc --- /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 8096180f855..189fa67ac21 100644 --- a/io/channel-buffer.c +++ b/io/channel-buffer.c @@ -225,7 +225,7 @@ static GSource *qio_channel_buffer_create_watch(QIOChannel *ioc, static void qio_channel_buffer_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); diff --git a/io/channel-command.c b/io/channel-command.c index 6d5f64e146d..8966dd3a2ba 100644 --- a/io/channel-command.c +++ b/io/channel-command.c @@ -358,7 +358,7 @@ static GSource *qio_channel_command_create_watch(QIOChannel *ioc, static void qio_channel_command_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); diff --git a/io/channel-file.c b/io/channel-file.c index 4a12c618860..ca3f180cc2f 100644 --- a/io/channel-file.c +++ b/io/channel-file.c @@ -36,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, @@ -52,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; @@ -138,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) @@ -216,13 +290,17 @@ static GSource *qio_channel_file_create_watch(QIOChannel *ioc, } static void qio_channel_file_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(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 ef995863483..49f1c80a54e 100644 --- a/io/channel-null.c +++ b/io/channel-null.c @@ -207,7 +207,7 @@ qio_channel_null_create_watch(QIOChannel *ioc, static void qio_channel_null_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); diff --git a/io/channel-socket.c b/io/channel-socket.c index 02ffb51e995..3b7ca924ff3 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -78,6 +78,17 @@ qio_channel_socket_new(void) return sioc; } +int qio_channel_socket_set_send_buffer(QIOChannelSocket *ioc, + size_t size, + Error **errp) +{ + if (setsockopt(ioc->fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0) { + error_setg_errno(errp, errno, "Unable to set socket send buffer size"); + return -1; + } + + return 0; +} static int qio_channel_socket_set_fd(QIOChannelSocket *sioc, @@ -782,6 +793,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; @@ -836,6 +852,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, @@ -917,7 +960,7 @@ static GSource *qio_channel_socket_create_watch(QIOChannel *ioc, } static void qio_channel_socket_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); @@ -933,6 +976,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 58fe1aceeea..db2ac1deae6 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); } @@ -160,16 +162,17 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, GMainContext *context) { Error *err = NULL; - QCryptoTLSSessionHandshakeStatus status; + int status; + + status = qcrypto_tls_session_handshake(ioc->session, &err); - if (qcrypto_tls_session_handshake(ioc->session, &err) < 0) { + if (status < 0) { trace_qio_channel_tls_handshake_fail(ioc); qio_task_set_error(task, err); qio_task_complete(task); return; } - status = qcrypto_tls_session_get_handshake_status(ioc->session); if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { trace_qio_channel_tls_handshake_complete(ioc); if (qcrypto_tls_session_check_credentials(ioc->session, @@ -245,6 +248,85 @@ void qio_channel_tls_handshake(QIOChannelTLS *ioc, qio_channel_tls_handshake_task(ioc, task, context); } +static gboolean qio_channel_tls_bye_io(QIOChannel *ioc, GIOCondition condition, + gpointer user_data); + +static void qio_channel_tls_bye_task(QIOChannelTLS *ioc, QIOTask *task, + GMainContext *context) +{ + GIOCondition condition; + QIOChannelTLSData *data; + int status; + Error *err = NULL; + + status = qcrypto_tls_session_bye(ioc->session, &err); + + if (status < 0) { + trace_qio_channel_tls_bye_fail(ioc); + qio_task_set_error(task, err); + qio_task_complete(task); + return; + } + + if (status == QCRYPTO_TLS_BYE_COMPLETE) { + qio_task_complete(task); + return; + } + + data = g_new0(typeof(*data), 1); + data->task = task; + data->context = context; + + if (context) { + g_main_context_ref(context); + } + + if (status == QCRYPTO_TLS_BYE_SENDING) { + condition = G_IO_OUT; + } else { + condition = G_IO_IN; + } + + trace_qio_channel_tls_bye_pending(ioc, status); + ioc->bye_ioc_tag = qio_channel_add_watch_full(ioc->master, condition, + qio_channel_tls_bye_io, + data, NULL, context); +} + + +static gboolean qio_channel_tls_bye_io(QIOChannel *ioc, GIOCondition condition, + gpointer user_data) +{ + QIOChannelTLSData *data = user_data; + QIOTask *task = data->task; + GMainContext *context = data->context; + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(qio_task_get_source(task)); + + tioc->bye_ioc_tag = 0; + g_free(data); + qio_channel_tls_bye_task(tioc, task, context); + + if (context) { + g_main_context_unref(context); + } + + return FALSE; +} + +static void propagate_error(QIOTask *task, gpointer opaque) +{ + qio_task_propagate_error(task, opaque); +} + +void qio_channel_tls_bye(QIOChannelTLS *ioc, Error **errp) +{ + QIOTask *task; + + task = qio_task_new(OBJECT(ioc), propagate_error, errp, NULL); + + trace_qio_channel_tls_bye_start(ioc); + qio_channel_tls_bye_task(ioc, task, NULL); +} static void qio_channel_tls_init(Object *obj G_GNUC_UNUSED) { @@ -273,24 +355,20 @@ 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, + flags & QIO_CHANNEL_READ_FLAG_RELAXED_EOF || + 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; @@ -317,18 +395,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; @@ -381,9 +456,15 @@ 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); } + if (tioc->bye_ioc_tag) { + trace_qio_channel_tls_bye_cancel(ioc); + g_clear_handle_id(&tioc->bye_ioc_tag, g_source_remove); + } + return qio_channel_close(tioc->master, errp); } @@ -480,7 +561,7 @@ qio_channel_tls_get_session(QIOChannelTLS *ioc) } static void qio_channel_tls_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); diff --git a/io/channel-websock.c b/io/channel-websock.c index a12acc27cf2..08ddb274f0c 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); } @@ -1307,7 +1308,7 @@ static GSource *qio_channel_websock_create_watch(QIOChannel *ioc, } static void qio_channel_websock_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); diff --git a/io/channel.c b/io/channel.c index 86c5834510f..ebd93227651 100644 --- a/io/channel.c +++ b/io/channel.c @@ -115,7 +115,8 @@ int coroutine_mixed_fn qio_channel_readv_all_eof(QIOChannel *ioc, size_t niov, Error **errp) { - return qio_channel_readv_full_all_eof(ioc, iov, niov, NULL, NULL, errp); + return qio_channel_readv_full_all_eof(ioc, iov, niov, NULL, NULL, 0, + errp); } int coroutine_mixed_fn qio_channel_readv_all(QIOChannel *ioc, @@ -130,6 +131,7 @@ int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc, const struct iovec *iov, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp) { int ret = -1; @@ -155,7 +157,7 @@ int coroutine_mixed_fn 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, 0, errp); + local_nfds, flags, errp); if (len == QIO_CHANNEL_ERR_BLOCK) { if (qemu_in_coroutine()) { qio_channel_yield(ioc, G_IO_IN); @@ -222,7 +224,8 @@ int coroutine_mixed_fn qio_channel_readv_full_all(QIOChannel *ioc, int **fds, size_t *nfds, Error **errp) { - int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, errp); + int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, 0, + errp); if (ret == 0) { error_setg(errp, "Unexpected end-of-file before all data were read"); @@ -454,6 +457,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) @@ -490,6 +551,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, diff --git a/io/dns-resolver.c b/io/dns-resolver.c index 53b0e8407a9..3712438f829 100644 --- a/io/dns-resolver.c +++ b/io/dns-resolver.c @@ -111,22 +111,11 @@ static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver, uaddr, INET6_ADDRSTRLEN, uport, 32, NI_NUMERICHOST | NI_NUMERICSERV); - newaddr->u.inet = (InetSocketAddress){ - .host = g_strdup(uaddr), - .port = g_strdup(uport), - .has_numeric = true, - .numeric = true, - .has_to = iaddr->has_to, - .to = iaddr->to, - .has_ipv4 = iaddr->has_ipv4, - .ipv4 = iaddr->ipv4, - .has_ipv6 = iaddr->has_ipv6, - .ipv6 = iaddr->ipv6, -#ifdef HAVE_IPPROTO_MPTCP - .has_mptcp = iaddr->has_mptcp, - .mptcp = iaddr->mptcp, -#endif - }; + newaddr->u.inet = *iaddr; + newaddr->u.inet.host = g_strdup(uaddr), + newaddr->u.inet.port = g_strdup(uport), + newaddr->u.inet.has_numeric = true, + newaddr->u.inet.numeric = true, (*addrs)[i] = newaddr; } diff --git a/io/meson.build b/io/meson.build index 283b9b2bdbd..1164812f912 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 1c984d69c69..47405965a66 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 3cc5cf1efdf..dc3a63ba1fb 100644 --- a/io/trace-events +++ b/io/trace-events @@ -43,6 +43,12 @@ 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_bye_start(void *ioc) "TLS termination start ioc=%p" +qio_channel_tls_bye_pending(void *ioc, int status) "TLS termination pending ioc=%p status=%d" +qio_channel_tls_bye_fail(void *ioc) "TLS termination fail ioc=%p" +qio_channel_tls_bye_complete(void *ioc) "TLS termination complete ioc=%p" +qio_channel_tls_bye_cancel(void *ioc) "TLS termination 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 b753286414a..8810376dcea 100644 --- a/iothread.c +++ b/iothread.c @@ -17,8 +17,8 @@ #include "qemu/module.h" #include "block/aio.h" #include "block/block.h" -#include "sysemu/event-loop-base.h" -#include "sysemu/iothread.h" +#include "system/event-loop-base.h" +#include "system/iothread.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc.h" #include "qemu/error-report.h" @@ -170,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); @@ -293,7 +292,7 @@ static void iothread_set_poll_param(Object *obj, Visitor *v, } } -static void iothread_class_init(ObjectClass *klass, void *class_data) +static void iothread_class_init(ObjectClass *klass, const void *class_data) { EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(klass); @@ -405,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.c b/job.c index 72d57f09345..0653bc2ba69 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 */ @@ -250,6 +251,12 @@ bool job_is_cancelled_locked(Job *job) return job->force_cancel; } +bool job_is_paused(Job *job) +{ + JOB_LOCK_GUARD(); + return job->paused; +} + bool job_is_cancelled(Job *job) { JOB_LOCK_GUARD(); @@ -463,12 +470,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 +842,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 +854,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 +866,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 +884,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 +925,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 +972,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 38e5957526c..f4d9baafa1c 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -37,17 +37,12 @@ #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)) - struct kvm_regs { struct user_pt_regs regs; /* sp = sp_el0 */ @@ -76,11 +71,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 @@ -110,6 +105,7 @@ struct kvm_regs { #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 */ +#define KVM_ARM_VCPU_HAS_EL2_E2H0 8 /* Limit NV support to E2H RES0 */ struct kvm_vcpu_init { __u32 target; @@ -162,6 +158,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. @@ -365,6 +366,7 @@ enum { KVM_REG_ARM_STD_HYP_BIT_PV_TIME = 0, }; +/* Vendor hyper call function numbers 0-63 */ #define KVM_REG_ARM_VENDOR_HYP_BMAP KVM_REG_ARM_FW_FEAT_BMAP_REG(2) enum { @@ -372,6 +374,14 @@ enum { KVM_REG_ARM_VENDOR_HYP_BIT_PTP = 1, }; +/* Vendor hyper call function numbers 64-127 */ +#define KVM_REG_ARM_VENDOR_HYP_BMAP_2 KVM_REG_ARM_FW_FEAT_BMAP_REG(3) + +enum { + KVM_REG_ARM_VENDOR_HYP_BIT_DISCOVER_IMPL_VER = 0, + KVM_REG_ARM_VENDOR_HYP_BIT_DISCOVER_IMPL_CPUS = 1, +}; + /* Device Control API on vm fd */ #define KVM_ARM_VM_SMCCC_CTRL 0 #define KVM_ARM_VM_SMCCC_FILTER 0 @@ -394,6 +404,7 @@ enum { #define KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS 6 #define KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO 7 #define KVM_DEV_ARM_VGIC_GRP_ITS_REGS 8 +#define KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ 9 #define KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT 10 #define KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK \ (0x3fffffULL << KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT) @@ -408,10 +419,11 @@ enum { /* Device Control API on vcpu fd */ #define KVM_ARM_VCPU_PMU_V3_CTRL 0 -#define KVM_ARM_VCPU_PMU_V3_IRQ 0 -#define KVM_ARM_VCPU_PMU_V3_INIT 1 -#define KVM_ARM_VCPU_PMU_V3_FILTER 2 -#define KVM_ARM_VCPU_PMU_V3_SET_PMU 3 +#define KVM_ARM_VCPU_PMU_V3_IRQ 0 +#define KVM_ARM_VCPU_PMU_V3_INIT 1 +#define KVM_ARM_VCPU_PMU_V3_FILTER 2 +#define KVM_ARM_VCPU_PMU_V3_SET_PMU 3 +#define KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS 4 #define KVM_ARM_VCPU_TIMER_CTRL 1 #define KVM_ARM_VCPU_TIMER_IRQ_VTIMER 0 #define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1 @@ -470,6 +482,12 @@ enum { */ #define KVM_SYSTEM_EVENT_RESET_FLAG_PSCI_RESET2 (1ULL << 0) +/* + * Shutdown caused by a PSCI v1.3 SYSTEM_OFF2 call. + * Valid only when the system event has a type of KVM_SYSTEM_EVENT_SHUTDOWN. + */ +#define KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2 (1ULL << 0) + /* run->fail_entry.hardware_entry_failure_reason codes. */ #define KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED (1ULL << 0) @@ -491,6 +509,38 @@ struct kvm_smccc_filter { #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 d0dbfe95878..7b500a3a7be 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 1d0e3e1d095..d1b1ec8cb1f 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 ce2ee8f1e36..df36f23876e 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 00000000000..ee9aaebdf32 --- /dev/null +++ b/linux-headers/asm-arm64/unistd_64.h @@ -0,0 +1,329 @@ +#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 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 +#define __NR_open_tree_attr 467 + + +#endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-generic/bitsperlong.h b/linux-headers/asm-generic/bitsperlong.h index 75f320fa91e..1fb4f0c9f27 100644 --- a/linux-headers/asm-generic/bitsperlong.h +++ b/linux-headers/asm-generic/bitsperlong.h @@ -24,4 +24,8 @@ #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/mman-common.h b/linux-headers/asm-generic/mman-common.h index 6ce1f1ceb43..ef1c27fa3c5 100644 --- a/linux-headers/asm-generic/mman-common.h +++ b/linux-headers/asm-generic/mman-common.h @@ -79,9 +79,13 @@ #define MADV_COLLAPSE 25 /* Synchronous hugepage collapse */ +#define MADV_GUARD_INSTALL 102 /* fatal signal on access to range */ +#define MADV_GUARD_REMOVE 103 /* unguard range */ + /* compatibility flags */ #define MAP_FILE 0 +#define PKEY_UNRESTRICTED 0x0 #define PKEY_DISABLE_ACCESS 0x1 #define PKEY_DISABLE_WRITE 0x2 #define PKEY_ACCESS_MASK (PKEY_DISABLE_ACCESS |\ diff --git a/linux-headers/asm-generic/mman.h b/linux-headers/asm-generic/mman.h index 57e8195d0b5..5e3d61ddbd8 100644 --- a/linux-headers/asm-generic/mman.h +++ b/linux-headers/asm-generic/mman.h @@ -19,4 +19,8 @@ #define MCL_FUTURE 2 /* lock all future mappings */ #define MCL_ONFAULT 4 /* lock all pages that are faulted in */ +#define SHADOW_STACK_SET_TOKEN (1ULL << 0) /* Set up a restore token in the shadow stack */ +#define SHADOW_STACK_SET_MARKER (1ULL << 1) /* Set up a top of stack marker in the shadow stack */ + + #endif /* __ASM_GENERIC_MMAN_H */ diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h index abe087c53b4..2892a45023a 100644 --- a/linux-headers/asm-generic/unistd.h +++ b/linux-headers/asm-generic/unistd.h @@ -71,7 +71,7 @@ __SYSCALL(__NR_fremovexattr, sys_fremovexattr) #define __NR_getcwd 17 __SYSCALL(__NR_getcwd, sys_getcwd) #define __NR_lookup_dcookie 18 -__SC_COMP(__NR_lookup_dcookie, sys_lookup_dcookie, compat_sys_lookup_dcookie) +__SYSCALL(__NR_lookup_dcookie, sys_ni_syscall) #define __NR_eventfd2 19 __SYSCALL(__NR_eventfd2, sys_eventfd2) #define __NR_epoll_create1 20 @@ -737,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 @@ -776,12 +776,8 @@ __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 @@ -816,15 +812,48 @@ __SYSCALL(__NR_process_mrelease, sys_process_mrelease) __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) + +#define __NR_setxattrat 463 +__SYSCALL(__NR_setxattrat, sys_setxattrat) +#define __NR_getxattrat 464 +__SYSCALL(__NR_getxattrat, sys_getxattrat) +#define __NR_listxattrat 465 +__SYSCALL(__NR_listxattrat, sys_listxattrat) +#define __NR_removexattrat 466 +__SYSCALL(__NR_removexattrat, sys_removexattrat) +#define __NR_open_tree_attr 467 +__SYSCALL(__NR_open_tree_attr, sys_open_tree_attr) #undef __NR_syscalls -#define __NR_syscalls 453 +#define __NR_syscalls 468 /* * 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 00000000000..6dc0bb0c13b --- /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 00000000000..5f354f5c684 --- /dev/null +++ b/linux-headers/asm-loongarch/kvm.h @@ -0,0 +1,155 @@ +/* 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 + +#define __KVM_HAVE_IRQ_LINE + +/* + * 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 + +#define KVM_DEV_LOONGARCH_IPI_GRP_REGS 0x40000001 + +#define KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS 0x40000002 + +#define KVM_DEV_LOONGARCH_EXTIOI_GRP_SW_STATUS 0x40000003 +#define KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_NUM_CPU 0x0 +#define KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_FEATURE 0x1 +#define KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_STATE 0x2 + +#define KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL 0x40000004 +#define KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_NUM_CPU 0x0 +#define KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_FEATURE 0x1 +#define KVM_DEV_LOONGARCH_EXTIOI_CTRL_LOAD_FINISHED 0x3 + +#define KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS 0x40000005 +#define KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL 0x40000006 +#define KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT 0 + +#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 00000000000..fd7f40713d4 --- /dev/null +++ b/linux-headers/asm-loongarch/kvm_para.h @@ -0,0 +1,22 @@ +/* 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 +#define KVM_FEATURE_USER_HCALL 25 + +#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 00000000000..8eebf89f5ab --- /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 00000000000..1f01980f9c9 --- /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 00000000000..50d22df8f7c --- /dev/null +++ b/linux-headers/asm-loongarch/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_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 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 +#define __NR_open_tree_attr 467 + + +#endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-mips/kvm.h b/linux-headers/asm-mips/kvm.h index edcf717c432..9673dc9cb31 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 c6e1fc77c99..b700dae28c4 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 */ @@ -105,6 +105,9 @@ #define MADV_COLLAPSE 25 /* Synchronous hugepage collapse */ +#define MADV_GUARD_INSTALL 102 /* fatal signal on access to range */ +#define MADV_GUARD_REMOVE 103 /* unguard range */ + /* 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 46d8500654c..bdcc2f460ba 100644 --- a/linux-headers/asm-mips/unistd_n32.h +++ b/linux-headers/asm-mips/unistd_n32.h @@ -381,5 +381,20 @@ #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) +#define __NR_setxattrat (__NR_Linux + 463) +#define __NR_getxattrat (__NR_Linux + 464) +#define __NR_listxattrat (__NR_Linux + 465) +#define __NR_removexattrat (__NR_Linux + 466) +#define __NR_open_tree_attr (__NR_Linux + 467) #endif /* _ASM_UNISTD_N32_H */ diff --git a/linux-headers/asm-mips/unistd_n64.h b/linux-headers/asm-mips/unistd_n64.h index c2f7ac673bb..3b6b0193b69 100644 --- a/linux-headers/asm-mips/unistd_n64.h +++ b/linux-headers/asm-mips/unistd_n64.h @@ -357,5 +357,20 @@ #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) +#define __NR_setxattrat (__NR_Linux + 463) +#define __NR_getxattrat (__NR_Linux + 464) +#define __NR_listxattrat (__NR_Linux + 465) +#define __NR_removexattrat (__NR_Linux + 466) +#define __NR_open_tree_attr (__NR_Linux + 467) #endif /* _ASM_UNISTD_N64_H */ diff --git a/linux-headers/asm-mips/unistd_o32.h b/linux-headers/asm-mips/unistd_o32.h index 757c68f2add..4609a4b4d38 100644 --- a/linux-headers/asm-mips/unistd_o32.h +++ b/linux-headers/asm-mips/unistd_o32.h @@ -427,5 +427,20 @@ #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) +#define __NR_setxattrat (__NR_Linux + 463) +#define __NR_getxattrat (__NR_Linux + 464) +#define __NR_listxattrat (__NR_Linux + 465) +#define __NR_removexattrat (__NR_Linux + 466) +#define __NR_open_tree_attr (__NR_Linux + 467) #endif /* _ASM_UNISTD_O32_H */ diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 9f18fa090f1..eaeda001784 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 8ef94bbac13..5d38a427e0a 100644 --- a/linux-headers/asm-powerpc/unistd_32.h +++ b/linux-headers/asm-powerpc/unistd_32.h @@ -434,6 +434,21 @@ #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 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 +#define __NR_open_tree_attr 467 #endif /* _ASM_UNISTD_32_H */ diff --git a/linux-headers/asm-powerpc/unistd_64.h b/linux-headers/asm-powerpc/unistd_64.h index 0e7ee43e884..860a488e4d1 100644 --- a/linux-headers/asm-powerpc/unistd_64.h +++ b/linux-headers/asm-powerpc/unistd_64.h @@ -406,6 +406,21 @@ #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 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 +#define __NR_open_tree_attr 467 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-riscv/kvm.h b/linux-headers/asm-riscv/kvm.h index 992c5e40710..5f59fd226cc 100644 --- a/linux-headers/asm-riscv/kvm.h +++ b/linux-headers/asm-riscv/kvm.h @@ -16,7 +16,6 @@ #include #define __KVM_HAVE_IRQ_LINE -#define __KVM_HAVE_READONLY_MEM #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 @@ -80,6 +79,7 @@ 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 */ @@ -93,6 +93,11 @@ struct kvm_riscv_aia_csr { 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 */ struct kvm_riscv_timer { __u64 frequency; @@ -131,6 +136,54 @@ enum KVM_RISCV_ISA_EXT_ID { 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_SMNPM, + KVM_RISCV_ISA_EXT_SSNPM, + KVM_RISCV_ISA_EXT_SVADE, + KVM_RISCV_ISA_EXT_SVADU, + KVM_RISCV_ISA_EXT_SVVPTC, + KVM_RISCV_ISA_EXT_ZABHA, + KVM_RISCV_ISA_EXT_ZICCRSE, + KVM_RISCV_ISA_EXT_ZAAMO, + KVM_RISCV_ISA_EXT_ZALRSC, KVM_RISCV_ISA_EXT_MAX, }; @@ -148,16 +201,22 @@ enum KVM_RISCV_SBI_EXT_ID { 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_SUSP, 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 -#define KVM_REG_SIZE(id) \ - (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) - /* 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 @@ -178,10 +237,13 @@ enum KVM_RISCV_SBI_EXT_ID { #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) @@ -229,6 +291,12 @@ enum KVM_RISCV_SBI_EXT_ID { #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 diff --git a/linux-headers/asm-riscv/ptrace.h b/linux-headers/asm-riscv/ptrace.h new file mode 100644 index 00000000000..1e3166caca8 --- /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 950ab3fd440..81896bbbf72 100644 --- a/linux-headers/asm-riscv/unistd.h +++ b/linux-headers/asm-riscv/unistd.h @@ -14,41 +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) -#endif -__SYSCALL(__NR_riscv_flush_icache, sys_riscv_flush_icache) - -/* - * Allows userspace to query the kernel for CPU architecture and - * microarchitecture details across a given set of CPUs. - */ -#ifndef __NR_riscv_hwprobe -#define __NR_riscv_hwprobe (__NR_arch_specific_syscall + 14) +#if __BITS_PER_LONG == 64 +#include +#else +#include #endif -__SYSCALL(__NR_riscv_hwprobe, sys_riscv_hwprobe) diff --git a/linux-headers/asm-riscv/unistd_32.h b/linux-headers/asm-riscv/unistd_32.h new file mode 100644 index 00000000000..a5e769f1d99 --- /dev/null +++ b/linux-headers/asm-riscv/unistd_32.h @@ -0,0 +1,320 @@ +#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 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 +#define __NR_open_tree_attr 467 + + +#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 00000000000..8df4d64841c --- /dev/null +++ b/linux-headers/asm-riscv/unistd_64.h @@ -0,0 +1,330 @@ +#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 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 +#define __NR_open_tree_attr 467 + + +#endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h index 023a2763a97..ab5a6bce590 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 @@ -156,7 +469,8 @@ struct kvm_s390_vm_cpu_subfunc { __u8 kdsa[16]; /* with MSA9 */ __u8 sortl[32]; /* with STFLE.150 */ __u8 dfltcc[32]; /* with STFLE.151 */ - __u8 reserved[1728]; + __u8 pfcr[16]; /* with STFLE.201 */ + __u8 reserved[1712]; }; #define KVM_S390_VM_CPU_PROCESSOR_UV_FEAT_GUEST 6 diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h index 716fa368ca7..85eedbd18e3 100644 --- a/linux-headers/asm-s390/unistd_32.h +++ b/linux-headers/asm-s390/unistd_32.h @@ -425,5 +425,20 @@ #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 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 +#define __NR_open_tree_attr 467 #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 b2a11b1d139..c03b1b97010 100644 --- a/linux-headers/asm-s390/unistd_64.h +++ b/linux-headers/asm-s390/unistd_64.h @@ -373,5 +373,20 @@ #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 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 +#define __NR_open_tree_attr 467 #endif /* _ASM_S390_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index 2b3a8f7bd2c..cd275ae76d2 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -7,6 +7,8 @@ * */ +#include +#include #include #include #include @@ -40,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 @@ -49,7 +50,6 @@ #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 @@ -106,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 { @@ -436,6 +437,9 @@ 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_X86_QUIRK_STUFF_FEATURE_MSRS (1 << 8) +#define KVM_X86_QUIRK_IGNORE_GUEST_PAT (1 << 9) #define KVM_STATE_NESTED_FORMAT_VMX 0 #define KVM_STATE_NESTED_FORMAT_SVM 1 @@ -455,8 +459,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]; @@ -524,9 +533,363 @@ struct kvm_pmu_event_filter { #define KVM_PMU_EVENT_ALLOW 0 #define KVM_PMU_EVENT_DENY 1 -#define KVM_PMU_EVENT_FLAG_MASKED_EVENTS BIT(0) +#define KVM_PMU_EVENT_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) + +#define KVM_XEN_MSR_MIN_INDEX 0x40000000u +#define KVM_XEN_MSR_MAX_INDEX 0x4fffffffu + +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_PAGE_TYPE_INVALID 0x0 +#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 @@ -547,10 +910,10 @@ struct kvm_pmu_event_filter { ((__u64)(!!(exclude)) << 55)) #define KVM_PMU_MASKED_ENTRY_EVENT_SELECT \ - (GENMASK_ULL(7, 0) | GENMASK_ULL(35, 32)) -#define KVM_PMU_MASKED_ENTRY_UMASK_MASK (GENMASK_ULL(63, 56)) -#define KVM_PMU_MASKED_ENTRY_UMASK_MATCH (GENMASK_ULL(15, 8)) -#define KVM_PMU_MASKED_ENTRY_EXCLUDE (BIT_ULL(55)) + (__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 */ @@ -558,6 +921,83 @@ struct kvm_pmu_event_filter { #define KVM_VCPU_TSC_OFFSET 0 /* attribute for the TSC offset */ /* x86-specific KVM_EXIT_HYPERCALL flags. */ -#define KVM_EXIT_HYPERCALL_LONG_MODE BIT(0) +#define KVM_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 +#define KVM_X86_TDX_VM 5 + +/* Trust Domain eXtension sub-ioctl() commands. */ +enum kvm_tdx_cmd_id { + KVM_TDX_CAPABILITIES = 0, + KVM_TDX_INIT_VM, + KVM_TDX_INIT_VCPU, + KVM_TDX_INIT_MEM_REGION, + KVM_TDX_FINALIZE_VM, + KVM_TDX_GET_CPUID, + + KVM_TDX_CMD_NR_MAX, +}; + +struct kvm_tdx_cmd { + /* enum kvm_tdx_cmd_id */ + __u32 id; + /* flags for sub-commend. If sub-command doesn't use this, set zero. */ + __u32 flags; + /* + * data for each sub-command. An immediate or a pointer to the actual + * data in process virtual address. If sub-command doesn't use it, + * set zero. + */ + __u64 data; + /* + * Auxiliary error code. The sub-command may return TDX SEAMCALL + * status code in addition to -Exxx. + */ + __u64 hw_error; +}; + +struct kvm_tdx_capabilities { + __u64 supported_attrs; + __u64 supported_xfam; + __u64 reserved[254]; + + /* Configurable CPUID bits for userspace */ + struct kvm_cpuid2 cpuid; +}; + +struct kvm_tdx_init_vm { + __u64 attributes; + __u64 xfam; + __u64 mrconfigid[6]; /* sha384 digest */ + __u64 mrowner[6]; /* sha384 digest */ + __u64 mrownerconfig[6]; /* sha384 digest */ + + /* The total space for TD_PARAMS before the CPUIDs is 256 bytes */ + __u64 reserved[12]; + + /* + * Call KVM_TDX_INIT_VM before vcpu creation, thus before + * KVM_SET_CPUID2. + * This configuration supersedes KVM_SET_CPUID2s for VCPUs because the + * TDX module directly virtualizes those CPUIDs without VMM. The user + * space VMM, e.g. qemu, should make KVM_SET_CPUID2 consistent with + * those values. If it doesn't, KVM may have wrong idea of vCPUIDs of + * the guest, and KVM may wrongly emulate CPUIDs or MSRs that the TDX + * module doesn't virtualize. + */ + struct kvm_cpuid2 cpuid; +}; + +#define KVM_TDX_MEASURE_MEMORY_REGION _BITULL(0) + +struct kvm_tdx_init_mem_region { + __u64 source_addr; + __u64 gpa; + __u64 nr_pages; +}; #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 00000000000..1d3e0e0b07a --- /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 46cdc941f95..ac1e6277212 100644 --- a/linux-headers/asm-x86/mman.h +++ b/linux-headers/asm-x86/mman.h @@ -5,9 +5,6 @@ #define MAP_32BIT 0x40 /* only give out 32bit addresses */ #define MAP_ABOVE4G 0x80 /* only map above 4GB */ -/* Flags for map_shadow_stack(2) */ -#define SHADOW_STACK_SET_TOKEN (1ULL << 0) /* Set up a restore token in the shadow stack */ - #include #endif /* _ASM_X86_MMAN_H */ diff --git a/linux-headers/asm-x86/unistd_32.h b/linux-headers/asm-x86/unistd_32.h index d749ad1c24e..491d6b4eb68 100644 --- a/linux-headers/asm-x86/unistd_32.h +++ b/linux-headers/asm-x86/unistd_32.h @@ -443,6 +443,21 @@ #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 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 +#define __NR_open_tree_attr 467 #endif /* _ASM_UNISTD_32_H */ diff --git a/linux-headers/asm-x86/unistd_64.h b/linux-headers/asm-x86/unistd_64.h index cea67282ebf..7cf88bf9bda 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 @@ -366,6 +367,20 @@ #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 +#define __NR_setxattrat 463 +#define __NR_getxattrat 464 +#define __NR_listxattrat 465 +#define __NR_removexattrat 466 +#define __NR_open_tree_attr 467 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/unistd_x32.h b/linux-headers/asm-x86/unistd_x32.h index 5b2e79bf4c4..82959111e6a 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) @@ -318,6 +319,21 @@ #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_setxattrat (__X32_SYSCALL_BIT + 463) +#define __NR_getxattrat (__X32_SYSCALL_BIT + 464) +#define __NR_listxattrat (__X32_SYSCALL_BIT + 465) +#define __NR_removexattrat (__X32_SYSCALL_BIT + 466) +#define __NR_open_tree_attr (__X32_SYSCALL_BIT + 467) #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 00000000000..9243f389751 --- /dev/null +++ b/linux-headers/linux/bits.h @@ -0,0 +1,14 @@ +/* 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)) << (l)) & (~_UL(0) >> (__BITS_PER_LONG - 1 - (h)))) + +#define __GENMASK_ULL(h, l) (((~_ULL(0)) << (l)) & (~_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 index 1eb84b5087f..95ede233420 100644 --- a/linux-headers/linux/const.h +++ b/linux-headers/linux/const.h @@ -28,6 +28,23 @@ #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 __int128' 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)) diff --git a/linux-headers/linux/iommufd.h b/linux-headers/linux/iommufd.h new file mode 100644 index 00000000000..cb0f7d6b4de --- /dev/null +++ b/linux-headers/linux/iommufd.h @@ -0,0 +1,1144 @@ +/* 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, + IOMMUFD_CMD_IOAS_MAP_FILE = 0x8f, + IOMMUFD_CMD_VIOMMU_ALLOC = 0x90, + IOMMUFD_CMD_VDEVICE_ALLOC = 0x91, + IOMMUFD_CMD_IOAS_CHANGE_PROCESS = 0x92, + IOMMUFD_CMD_VEVENTQ_ALLOC = 0x93, +}; + +/** + * 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_map_file - ioctl(IOMMU_IOAS_MAP_FILE) + * @size: sizeof(struct iommu_ioas_map_file) + * @flags: same as for iommu_ioas_map + * @ioas_id: same as for iommu_ioas_map + * @fd: the memfd to map + * @start: byte offset from start of file to map from + * @length: same as for iommu_ioas_map + * @iova: same as for iommu_ioas_map + * + * Set an IOVA mapping from a memfd file. All other arguments and semantics + * match those of IOMMU_IOAS_MAP. + */ +struct iommu_ioas_map_file { + __u32 size; + __u32 flags; + __u32 ioas_id; + __s32 fd; + __aligned_u64 start; + __aligned_u64 length; + __aligned_u64 iova; +}; +#define IOMMU_IOAS_MAP_FILE _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_MAP_FILE) + +/** + * 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 accounting, 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. + * @IOMMU_HWPT_ALLOC_PASID: Requests a domain that can be used with PASID. The + * domain can be attached to any PASID on the device. + * Any domain attached to the non-PASID part of the + * device must also be flagged, otherwise attaching a + * PASID will blocked. + * For the user that wants to attach PASID, ioas is + * not recommended for both the non-PASID part + * and PASID part of the device. + * If IOMMU does not support PASID it will return + * error (-EOPNOTSUPP). + */ +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, + IOMMU_HWPT_ALLOC_PASID = 1 << 3, +}; + +/** + * 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; +}; + +/** + * struct iommu_hwpt_arm_smmuv3 - ARM SMMUv3 nested STE + * (IOMMU_HWPT_DATA_ARM_SMMUV3) + * + * @ste: The first two double words of the user space Stream Table Entry for + * the translation. Must be little-endian. + * Allowed fields: (Refer to "5.2 Stream Table Entry" in SMMUv3 HW Spec) + * - word-0: V, Cfg, S1Fmt, S1ContextPtr, S1CDMax + * - word-1: EATS, S1DSS, S1CIR, S1COR, S1CSH, S1STALLD + * + * -EIO will be returned if @ste is not legal or contains any non-allowed field. + * Cfg can be used to select a S1, Bypass or Abort configuration. A Bypass + * nested domain will translate the same as the nesting parent. The S1 will + * install a Context Descriptor Table pointing at userspace memory translated + * by the nesting parent. + */ +struct iommu_hwpt_arm_smmuv3 { + __aligned_le64 ste[2]; +}; + +/** + * 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 + * @IOMMU_HWPT_DATA_ARM_SMMUV3: ARM SMMUv3 Context Descriptor Table + */ +enum iommu_hwpt_data_type { + IOMMU_HWPT_DATA_NONE = 0, + IOMMU_HWPT_DATA_VTD_S1 = 1, + IOMMU_HWPT_DATA_ARM_SMMUV3 = 2, +}; + +/** + * 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 or vIOMMU 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 vIOMMU (wrapping a + * parent HWPT) or a 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. The device + * via @dev_id and the vIOMMU via @pt_id must be associated to the same IOMMU + * instance. + * + * 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; +}; + +/** + * struct iommu_hw_info_arm_smmuv3 - ARM SMMUv3 hardware information + * (IOMMU_HW_INFO_TYPE_ARM_SMMUV3) + * + * @flags: Must be set to 0 + * @__reserved: Must be 0 + * @idr: Implemented features for ARM SMMU Non-secure programming interface + * @iidr: Information about the implementation and implementer of ARM SMMU, + * and architecture version supported + * @aidr: ARM SMMU architecture version + * + * For the details of @idr, @iidr and @aidr, please refer to the chapters + * from 6.3.1 to 6.3.6 in the SMMUv3 Spec. + * + * This reports the raw HW capability, and not all bits are meaningful to be + * read by userspace. Only the following fields should be used: + * + * idr[0]: ST_LEVEL, TERM_MODEL, STALL_MODEL, TTENDIAN , CD2L, ASID16, TTF + * idr[1]: SIDSIZE, SSIDSIZE + * idr[3]: BBML, RIL + * idr[5]: VAX, GRAN64K, GRAN16K, GRAN4K + * + * - S1P should be assumed to be true if a NESTED HWPT can be created + * - VFIO/iommufd only support platforms with COHACC, it should be assumed to be + * true. + * - ATS is a per-device property. If the VMM describes any devices as ATS + * capable in ACPI/DT it should set the corresponding idr. + * + * This list may expand in future (eg E0PD, AIE, PBHA, D128, DS etc). It is + * important that VMMs do not read bits outside the list to allow for + * compatibility with future kernels. Several features in the SMMUv3 + * architecture are not currently supported by the kernel for nesting: HTTU, + * BTM, MPAM and others. + */ +struct iommu_hw_info_arm_smmuv3 { + __u32 flags; + __u32 __reserved; + __u32 idr[6]; + __u32 iidr; + __u32 aidr; +}; + +/** + * 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 + * @IOMMU_HW_INFO_TYPE_ARM_SMMUV3: ARM SMMUv3 iommu info type + */ +enum iommu_hw_info_type { + IOMMU_HW_INFO_TYPE_NONE = 0, + IOMMU_HW_INFO_TYPE_INTEL_VTD = 1, + IOMMU_HW_INFO_TYPE_ARM_SMMUV3 = 2, +}; + +/** + * 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 + * + * @IOMMU_HW_CAP_PCI_PASID_EXEC: Execute Permission Supported, user ignores it + * when the struct + * iommu_hw_info::out_max_pasid_log2 is zero. + * @IOMMU_HW_CAP_PCI_PASID_PRIV: Privileged Mode Supported, user ignores it + * when the struct + * iommu_hw_info::out_max_pasid_log2 is zero. + */ +enum iommufd_hw_capabilities { + IOMMU_HW_CAP_DIRTY_TRACKING = 1 << 0, + IOMMU_HW_CAP_PCI_PASID_EXEC = 1 << 1, + IOMMU_HW_CAP_PCI_PASID_PRIV = 1 << 2, +}; + +/** + * 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. + * @out_max_pasid_log2: Output the width of PASIDs. 0 means no PASID support. + * PCI devices turn to out_capabilities to check if the + * specific capabilities is supported or not. + * @__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; + __u8 out_max_pasid_log2; + __u8 __reserved[3]; + __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 + * @IOMMU_VIOMMU_INVALIDATE_DATA_ARM_SMMUV3: Invalidation data for ARM SMMUv3 + */ +enum iommu_hwpt_invalidate_data_type { + IOMMU_HWPT_INVALIDATE_DATA_VTD_S1 = 0, + IOMMU_VIOMMU_INVALIDATE_DATA_ARM_SMMUV3 = 1, +}; + +/** + * 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_viommu_arm_smmuv3_invalidate - ARM SMMUv3 cache invalidation + * (IOMMU_VIOMMU_INVALIDATE_DATA_ARM_SMMUV3) + * @cmd: 128-bit cache invalidation command that runs in SMMU CMDQ. + * Must be little-endian. + * + * Supported command list only when passing in a vIOMMU via @hwpt_id: + * CMDQ_OP_TLBI_NSNH_ALL + * CMDQ_OP_TLBI_NH_VA + * CMDQ_OP_TLBI_NH_VAA + * CMDQ_OP_TLBI_NH_ALL + * CMDQ_OP_TLBI_NH_ASID + * CMDQ_OP_ATC_INV + * CMDQ_OP_CFGI_CD + * CMDQ_OP_CFGI_CD_ALL + * + * -EIO will be returned if the command is not supported. + */ +struct iommu_viommu_arm_smmuv3_invalidate { + __aligned_le64 cmd[2]; +}; + +/** + * struct iommu_hwpt_invalidate - ioctl(IOMMU_HWPT_INVALIDATE) + * @size: sizeof(struct iommu_hwpt_invalidate) + * @hwpt_id: ID of a nested HWPT or a vIOMMU, 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 iommu cache for user-managed page table or vIOMMU. Modifications + * on a user-managed page table should be followed by this operation, if a HWPT + * is passed in via @hwpt_id. Other caches, such as device cache or descriptor + * cache can be flushed if a vIOMMU is passed in via the @hwpt_id field. + * + * 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 + * @__reserved: Must be 0. + * @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; + __u32 __reserved; + __aligned_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) + +/** + * enum iommu_viommu_type - Virtual IOMMU Type + * @IOMMU_VIOMMU_TYPE_DEFAULT: Reserved for future use + * @IOMMU_VIOMMU_TYPE_ARM_SMMUV3: ARM SMMUv3 driver specific type + */ +enum iommu_viommu_type { + IOMMU_VIOMMU_TYPE_DEFAULT = 0, + IOMMU_VIOMMU_TYPE_ARM_SMMUV3 = 1, +}; + +/** + * struct iommu_viommu_alloc - ioctl(IOMMU_VIOMMU_ALLOC) + * @size: sizeof(struct iommu_viommu_alloc) + * @flags: Must be 0 + * @type: Type of the virtual IOMMU. Must be defined in enum iommu_viommu_type + * @dev_id: The device's physical IOMMU will be used to back the virtual IOMMU + * @hwpt_id: ID of a nesting parent HWPT to associate to + * @out_viommu_id: Output virtual IOMMU ID for the allocated object + * + * Allocate a virtual IOMMU object, representing the underlying physical IOMMU's + * virtualization support that is a security-isolated slice of the real IOMMU HW + * that is unique to a specific VM. Operations global to the IOMMU are connected + * to the vIOMMU, such as: + * - Security namespace for guest owned ID, e.g. guest-controlled cache tags + * - Non-device-affiliated event reporting, e.g. invalidation queue errors + * - Access to a sharable nesting parent pagetable across physical IOMMUs + * - Virtualization of various platforms IDs, e.g. RIDs and others + * - Delivery of paravirtualized invalidation + * - Direct assigned invalidation queues + * - Direct assigned interrupts + */ +struct iommu_viommu_alloc { + __u32 size; + __u32 flags; + __u32 type; + __u32 dev_id; + __u32 hwpt_id; + __u32 out_viommu_id; +}; +#define IOMMU_VIOMMU_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VIOMMU_ALLOC) + +/** + * struct iommu_vdevice_alloc - ioctl(IOMMU_VDEVICE_ALLOC) + * @size: sizeof(struct iommu_vdevice_alloc) + * @viommu_id: vIOMMU ID to associate with the virtual device + * @dev_id: The physical device to allocate a virtual instance on the vIOMMU + * @out_vdevice_id: Object handle for the vDevice. Pass to IOMMU_DESTORY + * @virt_id: Virtual device ID per vIOMMU, e.g. vSID of ARM SMMUv3, vDeviceID + * of AMD IOMMU, and vRID of a nested Intel VT-d to a Context Table + * + * Allocate a virtual device instance (for a physical device) against a vIOMMU. + * This instance holds the device's information (related to its vIOMMU) in a VM. + */ +struct iommu_vdevice_alloc { + __u32 size; + __u32 viommu_id; + __u32 dev_id; + __u32 out_vdevice_id; + __aligned_u64 virt_id; +}; +#define IOMMU_VDEVICE_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VDEVICE_ALLOC) + +/** + * struct iommu_ioas_change_process - ioctl(VFIO_IOAS_CHANGE_PROCESS) + * @size: sizeof(struct iommu_ioas_change_process) + * @__reserved: Must be 0 + * + * This transfers pinned memory counts for every memory map in every IOAS + * in the context to the current process. This only supports maps created + * with IOMMU_IOAS_MAP_FILE, and returns EINVAL if other maps are present. + * If the ioctl returns a failure status, then nothing is changed. + * + * This API is useful for transferring operation of a device from one process + * to another, such as during userland live update. + */ +struct iommu_ioas_change_process { + __u32 size; + __u32 __reserved; +}; + +#define IOMMU_IOAS_CHANGE_PROCESS \ + _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_CHANGE_PROCESS) + +/** + * enum iommu_veventq_flag - flag for struct iommufd_vevent_header + * @IOMMU_VEVENTQ_FLAG_LOST_EVENTS: vEVENTQ has lost vEVENTs + */ +enum iommu_veventq_flag { + IOMMU_VEVENTQ_FLAG_LOST_EVENTS = (1U << 0), +}; + +/** + * struct iommufd_vevent_header - Virtual Event Header for a vEVENTQ Status + * @flags: Combination of enum iommu_veventq_flag + * @sequence: The sequence index of a vEVENT in the vEVENTQ, with a range of + * [0, INT_MAX] where the following index of INT_MAX is 0 + * + * Each iommufd_vevent_header reports a sequence index of the following vEVENT: + * + * +----------------------+-------+----------------------+-------+---+-------+ + * | header0 {sequence=0} | data0 | header1 {sequence=1} | data1 |...| dataN | + * +----------------------+-------+----------------------+-------+---+-------+ + * + * And this sequence index is expected to be monotonic to the sequence index of + * the previous vEVENT. If two adjacent sequence indexes has a delta larger than + * 1, it means that delta - 1 number of vEVENTs has lost, e.g. two lost vEVENTs: + * + * +-----+----------------------+-------+----------------------+-------+-----+ + * | ... | header3 {sequence=3} | data3 | header6 {sequence=6} | data6 | ... | + * +-----+----------------------+-------+----------------------+-------+-----+ + * + * If a vEVENT lost at the tail of the vEVENTQ and there is no following vEVENT + * providing the next sequence index, an IOMMU_VEVENTQ_FLAG_LOST_EVENTS header + * would be added to the tail, and no data would follow this header: + * + * +--+----------------------+-------+-----------------------------------------+ + * |..| header3 {sequence=3} | data3 | header4 {flags=LOST_EVENTS, sequence=4} | + * +--+----------------------+-------+-----------------------------------------+ + */ +struct iommufd_vevent_header { + __u32 flags; + __u32 sequence; +}; + +/** + * enum iommu_veventq_type - Virtual Event Queue Type + * @IOMMU_VEVENTQ_TYPE_DEFAULT: Reserved for future use + * @IOMMU_VEVENTQ_TYPE_ARM_SMMUV3: ARM SMMUv3 Virtual Event Queue + */ +enum iommu_veventq_type { + IOMMU_VEVENTQ_TYPE_DEFAULT = 0, + IOMMU_VEVENTQ_TYPE_ARM_SMMUV3 = 1, +}; + +/** + * struct iommu_vevent_arm_smmuv3 - ARM SMMUv3 Virtual Event + * (IOMMU_VEVENTQ_TYPE_ARM_SMMUV3) + * @evt: 256-bit ARM SMMUv3 Event record, little-endian. + * Reported event records: (Refer to "7.3 Event records" in SMMUv3 HW Spec) + * - 0x04 C_BAD_STE + * - 0x06 F_STREAM_DISABLED + * - 0x08 C_BAD_SUBSTREAMID + * - 0x0a C_BAD_CD + * - 0x10 F_TRANSLATION + * - 0x11 F_ADDR_SIZE + * - 0x12 F_ACCESS + * - 0x13 F_PERMISSION + * + * StreamID field reports a virtual device ID. To receive a virtual event for a + * device, a vDEVICE must be allocated via IOMMU_VDEVICE_ALLOC. + */ +struct iommu_vevent_arm_smmuv3 { + __aligned_le64 evt[4]; +}; + +/** + * struct iommu_veventq_alloc - ioctl(IOMMU_VEVENTQ_ALLOC) + * @size: sizeof(struct iommu_veventq_alloc) + * @flags: Must be 0 + * @viommu_id: virtual IOMMU ID to associate the vEVENTQ with + * @type: Type of the vEVENTQ. Must be defined in enum iommu_veventq_type + * @veventq_depth: Maximum number of events in the vEVENTQ + * @out_veventq_id: The ID of the new vEVENTQ + * @out_veventq_fd: The fd of the new vEVENTQ. User space must close the + * successfully returned fd after using it + * @__reserved: Must be 0 + * + * Explicitly allocate a virtual event queue interface for a vIOMMU. A vIOMMU + * can have multiple FDs for different types, but is confined to one per @type. + * User space should open the @out_veventq_fd to read vEVENTs out of a vEVENTQ, + * if there are vEVENTs available. A vEVENTQ will lose events due to overflow, + * if the number of the vEVENTs hits @veventq_depth. + * + * Each vEVENT in a vEVENTQ encloses a struct iommufd_vevent_header followed by + * a type-specific data structure, in a normal case: + * + * +-+---------+-------+---------+-------+-----+---------+-------+-+ + * | | header0 | data0 | header1 | data1 | ... | headerN | dataN | | + * +-+---------+-------+---------+-------+-----+---------+-------+-+ + * + * unless a tailing IOMMU_VEVENTQ_FLAG_LOST_EVENTS header is logged (refer to + * struct iommufd_vevent_header). + */ +struct iommu_veventq_alloc { + __u32 size; + __u32 flags; + __u32 viommu_id; + __u32 type; + __u32 veventq_depth; + __u32 out_veventq_id; + __u32 out_veventq_fd; + __u32 __reserved; +}; +#define IOMMU_VEVENTQ_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VEVENTQ_ALLOC) +#endif diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 0d74ee999aa..0690743944b 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -16,75 +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 *** */ - +/* + * Backwards-compatible definitions. + */ +#define __KVM_HAVE_GUEST_DEBUG /* for KVM_SET_USER_MEMORY_REGION */ struct kvm_userspace_memory_region { @@ -95,6 +30,19 @@ 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_userspace_memory_region::flags are visible for * userspace, other bits are reserved for kvm internal use which are defined @@ -102,6 +50,7 @@ struct kvm_userspace_memory_region { */ #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 { @@ -141,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 @@ -264,6 +176,9 @@ 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 +#define KVM_EXIT_TDX 40 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -278,11 +193,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 */ @@ -336,6 +260,13 @@ 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; @@ -360,11 +291,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 { @@ -444,6 +370,7 @@ struct kvm_run { #define KVM_SYSTEM_EVENT_WAKEUP 4 #define KVM_SYSTEM_EVENT_SUSPEND 5 #define KVM_SYSTEM_EVENT_SEV_TERM 6 +#define KVM_SYSTEM_EVENT_TDX_FATAL 7 __u32 type; __u32 ndata; union { @@ -506,6 +433,34 @@ 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; + /* KVM_EXIT_TDX */ + struct { + __u64 flags; + __u64 nr; + union { + struct { + __u64 ret; + __u64 data[5]; + } unknown; + struct { + __u64 ret; + __u64 gpa; + __u64 size; + } get_quote; + struct { + __u64 ret; + __u64 leaf; + __u64 r11, r12, r13, r14; + } get_tdvmcall_info; + }; + } tdx; /* Fix the size of the union. */ char padding[256]; }; @@ -572,43 +527,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 */ - __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) - /* for KVM_INTERRUPT */ struct kvm_interrupt { /* in */ @@ -673,124 +591,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 @@ -832,10 +632,6 @@ struct kvm_ioeventfd { #define KVM_X86_DISABLE_EXITS_HLT (1 << 1) #define KVM_X86_DISABLE_EXITS_PAUSE (1 << 2) #define KVM_X86_DISABLE_EXITS_CSTATE (1 << 3) -#define KVM_X86_DISABLE_VALID_EXITS (KVM_X86_DISABLE_EXITS_MWAIT | \ - KVM_X86_DISABLE_EXITS_HLT | \ - KVM_X86_DISABLE_EXITS_PAUSE | \ - KVM_X86_DISABLE_EXITS_CSTATE) /* for KVM_ENABLE_CAP */ struct kvm_enable_cap { @@ -846,50 +642,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 */ @@ -933,9 +685,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) @@ -962,9 +711,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 @@ -1188,8 +935,19 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_COUNTER_OFFSET 227 #define KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE 228 #define KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES 229 - -#ifdef KVM_CAP_IRQ_ROUTING +#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 +#define KVM_CAP_ARM_WRITABLE_IMP_ID_REGS 239 +#define KVM_CAP_ARM_EL2 240 +#define KVM_CAP_ARM_EL2_E2H0 241 +#define KVM_CAP_RISCV_MP_STATE_RESET 242 struct kvm_irq_routing_irqchip { __u32 irqchip; @@ -1255,41 +1013,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) -#define KVM_XEN_HVM_CONFIG_RUNSTATE_UPDATE_FLAG (1 << 6) - -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 @@ -1358,9 +1081,14 @@ 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 + +#define KVM_REG_SIZE(id) \ + (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) + #define KVM_REG_SIZE_U8 0x0000000000000000ULL #define KVM_REG_SIZE_U16 0x0010000000000000ULL #define KVM_REG_SIZE_U32 0x0020000000000000ULL @@ -1449,7 +1177,15 @@ enum kvm_device_type { #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_LOONGARCH_IPI, +#define KVM_DEV_TYPE_LOONGARCH_IPI KVM_DEV_TYPE_LOONGARCH_IPI + KVM_DEV_TYPE_LOONGARCH_EIOINTC, +#define KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_EIOINTC + KVM_DEV_TYPE_LOONGARCH_PCHPIC, +#define KVM_DEV_TYPE_LOONGARCH_PCHPIC KVM_DEV_TYPE_LOONGARCH_PCHPIC + KVM_DEV_TYPE_MAX, + }; struct kvm_vfio_spapr_tce { @@ -1469,13 +1205,10 @@ struct kvm_vfio_spapr_tce { 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) @@ -1493,20 +1226,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) @@ -1523,9 +1244,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 */ @@ -1546,9 +1264,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) @@ -1558,6 +1276,7 @@ struct kvm_s390_ucas_mapping { #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) @@ -1577,8 +1296,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) @@ -1686,89 +1403,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, - 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]; -}; - /* Available with KVM_CAP_S390_PROTECTED */ #define KVM_S390_PV_COMMAND _IOWR(KVMIO, 0xc5, struct kvm_pv_cmd) @@ -1782,58 +1416,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; - __u8 runstate_update_flag; - struct { - __u64 gfn; -#define KVM_XEN_INVALID_GFN ((__u64)-1) - } shared_info; - struct { - __u32 send_port; - __u32 type; /* EVTCHNSTAT_ipi / EVTCHNSTAT_interdomain */ - __u32 flags; -#define KVM_XEN_EVTCHN_DEASSIGN (1 << 0) -#define KVM_XEN_EVTCHN_UPDATE (1 << 1) -#define KVM_XEN_EVTCHN_RESET (1 << 2) - /* - * Events sent by the guest are either looped back to - * the guest itself (potentially on a different port#) - * or signalled via an eventfd. - */ - union { - struct { - __u32 port; - __u32 vcpu; - __u32 priority; - } port; - struct { - __u32 port; /* Zero for eventfd */ - __s32 fd; - } eventfd; - __u32 padding[4]; - } deliver; - } evtchn; - __u32 xen_version; - __u64 pad[8]; - } u; -}; - - -/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_SHARED_INFO */ -#define KVM_XEN_ATTR_TYPE_LONG_MODE 0x0 -#define KVM_XEN_ATTR_TYPE_SHARED_INFO 0x1 -#define KVM_XEN_ATTR_TYPE_UPCALL_VECTOR 0x2 -/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_EVTCHN_SEND */ -#define KVM_XEN_ATTR_TYPE_EVTCHN 0x3 -#define KVM_XEN_ATTR_TYPE_XEN_VERSION 0x4 -/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_RUNSTATE_UPDATE_FLAG */ -#define KVM_XEN_ATTR_TYPE_RUNSTATE_UPDATE_FLAG 0x5 - /* Per-vCPU Xen attributes */ #define KVM_XEN_VCPU_GET_ATTR _IOWR(KVMIO, 0xca, struct kvm_xen_vcpu_attr) #define KVM_XEN_VCPU_SET_ATTR _IOW(KVMIO, 0xcb, struct kvm_xen_vcpu_attr) @@ -1844,242 +1426,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; -#define KVM_XEN_INVALID_GPA ((__u64)-1) - __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) @@ -2225,31 +1571,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 00000000000..6a1e672259c --- /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/mman.h b/linux-headers/linux/mman.h index 4e8cb607805..2b83059586e 100644 --- a/linux-headers/linux/mman.h +++ b/linux-headers/linux/mman.h @@ -17,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 diff --git a/linux-headers/linux/psci.h b/linux-headers/linux/psci.h index 74f3cb5007c..a982afd4987 100644 --- a/linux-headers/linux/psci.h +++ b/linux-headers/linux/psci.h @@ -59,6 +59,7 @@ #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_3_FN_SYSTEM_OFF2 PSCI_0_2_FN(21) #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) @@ -68,6 +69,7 @@ #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) +#define PSCI_1_3_FN64_SYSTEM_OFF2 PSCI_0_2_FN64(21) /* PSCI v0.2 power state encoding for CPU_SUSPEND function */ #define PSCI_0_2_POWER_STATE_ID_MASK 0xffff @@ -100,6 +102,9 @@ #define PSCI_1_1_RESET_TYPE_SYSTEM_WARM_RESET 0 #define PSCI_1_1_RESET_TYPE_VENDOR_START 0x80000000U +/* PSCI v1.3 hibernate type for SYSTEM_OFF2 */ +#define PSCI_1_3_OFF_TYPE_HIBERNATE_OFF BIT(0) + /* PSCI version decoding (independent of PSCI version) */ #define PSCI_VERSION_MAJOR_SHIFT 16 #define PSCI_VERSION_MINOR_MASK \ diff --git a/linux-headers/linux/psp-sev.h b/linux-headers/linux/psp-sev.h index 12ccb70099d..113c4ceb784 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, }; @@ -47,6 +51,7 @@ typedef enum { 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, @@ -68,6 +73,20 @@ typedef enum { SEV_RET_INVALID_PARAM, SEV_RET_RESOURCE_LIMIT, SEV_RET_SECURE_DATA_INVALID, + SEV_RET_INVALID_PAGE_SIZE = 0x0019, + SEV_RET_INVALID_PAGE_STATE = 0x001A, + SEV_RET_INVALID_MDATA_ENTRY = 0x001B, + SEV_RET_INVALID_PAGE_OWNER = 0x001C, + SEV_RET_AEAD_OFLOW = 0x001D, + SEV_RET_EXIT_RING_BUFFER = 0x001F, + SEV_RET_RMP_INIT_REQUIRED = 0x0020, + SEV_RET_BAD_SVN = 0x0021, + SEV_RET_BAD_VERSION = 0x0022, + SEV_RET_SHUTDOWN_REQUIRED = 0x0023, + SEV_RET_UPDATE_FAILED = 0x0024, + SEV_RET_RESTORE_REQUIRED = 0x0025, + SEV_RET_RMP_INITIALIZATION_FAILED = 0x0026, + SEV_RET_INVALID_KEY = 0x0027, SEV_RET_MAX, } sev_ret_code; @@ -154,6 +173,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 index 9bb07083ac8..e1fcfcf3b33 100644 --- a/linux-headers/linux/stddef.h +++ b/linux-headers/linux/stddef.h @@ -8,6 +8,13 @@ #define __always_inline __inline__ #endif +/* Not all C++ standards support type declarations inside an anonymous union */ +#ifndef __cplusplus +#define __struct_group_tag(TAG) TAG +#else +#define __struct_group_tag(TAG) +#endif + /** * __struct_group() - Create a mirrored named and anonyomous struct * @@ -20,15 +27,20 @@ * 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. + * The named struct can also be explicitly tagged for layer reuse (C only), + * as well as both having struct attributes appended. */ #define __struct_group(TAG, NAME, ATTRS, MEMBERS...) \ union { \ struct { MEMBERS } ATTRS; \ - struct TAG { MEMBERS } ATTRS NAME; \ - } + struct __struct_group_tag(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 * @@ -49,3 +61,15 @@ #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 + +#define __kernel_nonstring + +#endif /* _LINUX_STDDEF_H */ diff --git a/linux-headers/linux/userfaultfd.h b/linux-headers/linux/userfaultfd.h index 59978fbaae3..4283de22d5b 100644 --- a/linux-headers/linux/userfaultfd.h +++ b/linux-headers/linux/userfaultfd.h @@ -40,7 +40,9 @@ UFFD_FEATURE_EXACT_ADDRESS | \ UFFD_FEATURE_WP_HUGETLBFS_SHMEM | \ UFFD_FEATURE_WP_UNPOPULATED | \ - UFFD_FEATURE_POISON) + UFFD_FEATURE_POISON | \ + UFFD_FEATURE_WP_ASYNC | \ + UFFD_FEATURE_MOVE) #define UFFD_API_IOCTLS \ ((__u64)1 << _UFFDIO_REGISTER | \ (__u64)1 << _UFFDIO_UNREGISTER | \ @@ -49,6 +51,7 @@ ((__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_POISON) @@ -72,6 +75,7 @@ #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) @@ -91,6 +95,8 @@ 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, \ @@ -216,6 +222,14 @@ struct uffdio_api { * (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) @@ -232,6 +246,8 @@ struct uffdio_api { #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; @@ -340,6 +356,24 @@ struct uffdio_poison { __s64 updated; }; +struct uffdio_move { + __u64 dst; + __u64 src; + __u64 len; + /* + * Especially if used to atomically remove memory from the + * address space the wake on the dst range is not needed. + */ +#define UFFDIO_MOVE_MODE_DONTWAKE ((__u64)1<<0) +#define UFFDIO_MOVE_MODE_ALLOW_SRC_HOLES ((__u64)1<<1) + __u64 mode; + /* + * "move" is written by the ioctl and must be at the end: the + * copy_from_user will not read the last 8 bytes. + */ + __s64 move; +}; + /* * Flags for the userfaultfd(2) system call itself. */ diff --git a/linux-headers/linux/vduse.h b/linux-headers/linux/vduse.h index 6d2ca064b5d..f46269af349 100644 --- a/linux-headers/linux/vduse.h +++ b/linux-headers/linux/vduse.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ #ifndef _VDUSE_H_ #define _VDUSE_H_ diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index acf72b4999f..79bf8c0cc5e 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -35,7 +35,7 @@ #define VFIO_EEH 5 /* Two-stage IOMMU */ -#define VFIO_TYPE1_NESTING_IOMMU 6 /* Implies v2 */ +#define __VFIO_RESERVED_TYPE1_NESTING_IOMMU 6 /* Implies v2 */ #define VFIO_SPAPR_TCE_v2_IOMMU 7 @@ -277,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) @@ -294,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 { @@ -450,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; }; /* @@ -476,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; }; /* @@ -671,6 +671,7 @@ enum { */ enum { VFIO_AP_REQ_IRQ_INDEX, + VFIO_AP_CFG_CHG_IRQ_INDEX, VFIO_AP_NUM_IRQS }; @@ -816,7 +817,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 */ @@ -829,6 +830,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) @@ -863,9 +865,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) @@ -929,29 +932,34 @@ struct vfio_device_bind_iommufd { * 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. + * @flags: Flags for attach. * @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. + * @pasid: The pasid to be attached, only meaningful when + * VFIO_DEVICE_ATTACH_PASID is set in @flags * * 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. + * If a vfio device or a pasid of this device is currently attached to a valid + * hw_pagetable (hwpt), without doing a VFIO_DEVICE_DETACH_IOMMUFD_PT, a second + * VFIO_DEVICE_ATTACH_IOMMUFD_PT ioctl passing in another hwpt id is allowed. + * This action, also known as a hw_pagetable replacement, will replace the + * currently attached hwpt of the device or the pasid of this device with a new + * hwpt corresponding to the given pt_id. * * Return: 0 on success, -errno on failure. */ struct vfio_device_attach_iommufd_pt { __u32 argsz; __u32 flags; +#define VFIO_DEVICE_ATTACH_PASID (1 << 0) __u32 pt_id; + __u32 pasid; }; #define VFIO_DEVICE_ATTACH_IOMMUFD_PT _IO(VFIO_TYPE, VFIO_BASE + 19) @@ -960,17 +968,21 @@ struct vfio_device_attach_iommufd_pt { * 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. + * @flags: Flags for detach. + * @pasid: The pasid to be detached, only meaningful when + * VFIO_DEVICE_DETACH_PASID is set in @flags * - * 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. + * Remove the association of the device or a pasid of the device and its current + * associated address space. After it, the device or the pasid 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_PASID (1 << 0) + __u32 pasid; }; #define VFIO_DEVICE_DETACH_IOMMUFD_PT _IO(VFIO_TYPE, VFIO_BASE + 20) @@ -1217,6 +1229,7 @@ enum vfio_device_mig_state { VFIO_DEVICE_STATE_RUNNING_P2P = 5, VFIO_DEVICE_STATE_PRE_COPY = 6, VFIO_DEVICE_STATE_PRE_COPY_P2P = 7, + VFIO_DEVICE_STATE_NR, }; /** @@ -1434,6 +1447,27 @@ struct vfio_device_feature_mig_data_size { #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 -------- */ /** @@ -1449,7 +1483,7 @@ 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; }; diff --git a/linux-headers/linux/vhost.h b/linux-headers/linux/vhost.h index f5c48b61ab6..d4b3e2ae131 100644 --- a/linux-headers/linux/vhost.h +++ b/linux-headers/linux/vhost.h @@ -28,10 +28,10 @@ /* Set current process as the (exclusive) owner of this file descriptor. This * must be called before any other vhost command. Further calls to - * VHOST_OWNER_SET fail until VHOST_OWNER_RESET is called. */ + * VHOST_SET_OWNER fail until VHOST_RESET_OWNER is called. */ #define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01) /* Give up ownership, and reset the device to default values. - * Allows subsequent call to VHOST_OWNER_SET to succeed. */ + * Allows subsequent call to VHOST_SET_OWNER to succeed. */ #define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02) /* Set up/modify memory layout */ @@ -179,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) @@ -219,4 +213,26 @@ */ #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 00000000000..c33a679c0f2 --- /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 2e2f7cf2188..fea43cefa6b 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -20,59 +20,12 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" #include "qemu/guest-random.h" #include "semihosting/common-semi.h" #include "target/arm/syndrome.h" - -#define get_user_code_u32(x, gaddr, env) \ - ({ abi_long __r = get_user_u32((x), (gaddr)); \ - if (!__r && bswap_code(arm_sctlr_b(env))) { \ - (x) = bswap32(x); \ - } \ - __r; \ - }) - -#define get_user_code_u16(x, gaddr, env) \ - ({ abi_long __r = get_user_u16((x), (gaddr)); \ - if (!__r && bswap_code(arm_sctlr_b(env))) { \ - (x) = bswap16(x); \ - } \ - __r; \ - }) - -#define get_user_data_u32(x, gaddr, env) \ - ({ abi_long __r = get_user_u32((x), (gaddr)); \ - if (!__r && arm_cpu_bswap_data(env)) { \ - (x) = bswap32(x); \ - } \ - __r; \ - }) - -#define get_user_data_u16(x, gaddr, env) \ - ({ abi_long __r = get_user_u16((x), (gaddr)); \ - if (!__r && arm_cpu_bswap_data(env)) { \ - (x) = bswap16(x); \ - } \ - __r; \ - }) - -#define put_user_data_u32(x, gaddr, env) \ - ({ typeof(x) __x = (x); \ - if (arm_cpu_bswap_data(env)) { \ - __x = bswap32(__x); \ - } \ - put_user_u32(__x, (gaddr)); \ - }) - -#define put_user_data_u16(x, gaddr, env) \ - ({ typeof(x) __x = (x); \ - if (arm_cpu_bswap_data(env)) { \ - __x = bswap16(__x); \ - } \ - put_user_u16(__x, (gaddr)); \ - }) +#include "target/arm/cpu-features.h" /* AArch64 main loop */ void cpu_loop(CPUARMState *env) @@ -184,11 +137,11 @@ void cpu_loop(CPUARMState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, 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 00000000000..f25a67a21ef --- /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 00000000000..a5b1c8503b7 --- /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 00000000000..0c53abda222 --- /dev/null +++ b/linux-user/aarch64/mte_user_helper.h @@ -0,0 +1,34 @@ +/* + * 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 + +#include "user/abitypes.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 b265cfd4706..bc7a13800da 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; @@ -669,7 +670,7 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, 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 00000000000..845e24eb372 --- /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 12ef002d60f..760302cb3ef 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 00000000000..dd6b586b1b7 --- /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_prctl.h b/linux-user/aarch64/target_prctl.h index 907c3141466..ed75b9e4b5a 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_signal.h b/linux-user/aarch64/target_signal.h index 40e399d9908..6f66a50bfd2 100644 --- a/linux-user/aarch64/target_signal.h +++ b/linux-user/aarch64/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #define TARGET_SEGV_MTEAERR 8 /* Asynchronous ARM MTE error */ #define TARGET_SEGV_MTESERR 9 /* Synchronous ARM MTE exception */ diff --git a/linux-user/aarch64/vdso-be.so b/linux-user/aarch64/vdso-be.so new file mode 100755 index 00000000000..d43c3b19cdf 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 00000000000..aaedc9d85e5 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 00000000000..a0ac1487b09 --- /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 00000000000..4c12f333526 --- /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/cpu_loop.c b/linux-user/alpha/cpu_loop.c index 2ea039aa71f..80ad536c5f2 100644 --- a/linux-user/alpha/cpu_loop.c +++ b/linux-user/alpha/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" void cpu_loop(CPUAlphaState *env) @@ -173,7 +173,7 @@ void cpu_loop(CPUAlphaState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; diff --git a/linux-user/alpha/signal.c b/linux-user/alpha/signal.c index 4ec42994d4a..896c2c148a1 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 3000a2e8ee2..54ee7aaa24f 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 55cafe6abf9..6da0c957e21 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 344e9f4d395..b77d638f6d4 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_proc.h b/linux-user/alpha/target_proc.h index dac37dffc9d..da437ee0e56 100644 --- a/linux-user/alpha/target_proc.h +++ b/linux-user/alpha/target_proc.h @@ -15,7 +15,7 @@ static int open_cpuinfo(CPUArchState *cpu_env, int fd) const char *p, *q; int t; - p = object_class_get_name(OBJECT_CLASS(CPU_GET_CLASS(env_cpu(cpu_env)))); + p = object_class_get_name(OBJECT_CLASS(env_cpu(cpu_env)->cc)); q = strchr(p, '-'); t = q - p; assert(t < sizeof(model)); diff --git a/linux-user/arm/Makefile.vdso b/linux-user/arm/Makefile.vdso new file mode 100644 index 00000000000..ede489e2363 --- /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 b404117ff30..33f63951a95 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -21,9 +21,12 @@ #include "qemu.h" #include "user-internals.h" #include "elf.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" #include "semihosting/common-semi.h" +#include "exec/page-protection.h" +#include "exec/mmap-lock.h" +#include "user/page-protection.h" #include "target/arm/syndrome.h" #define get_user_code_u32(x, gaddr, env) \ @@ -34,45 +37,10 @@ __r; \ }) -#define get_user_code_u16(x, gaddr, env) \ - ({ abi_long __r = get_user_u16((x), (gaddr)); \ - if (!__r && bswap_code(arm_sctlr_b(env))) { \ - (x) = bswap16(x); \ - } \ - __r; \ - }) - -#define get_user_data_u32(x, gaddr, env) \ - ({ abi_long __r = get_user_u32((x), (gaddr)); \ - if (!__r && arm_cpu_bswap_data(env)) { \ - (x) = bswap32(x); \ - } \ - __r; \ - }) - -#define get_user_data_u16(x, gaddr, env) \ - ({ abi_long __r = get_user_u16((x), (gaddr)); \ - if (!__r && arm_cpu_bswap_data(env)) { \ - (x) = bswap16(x); \ - } \ - __r; \ - }) - -#define put_user_data_u32(x, gaddr, env) \ - ({ typeof(x) __x = (x); \ - if (arm_cpu_bswap_data(env)) { \ - __x = bswap32(__x); \ - } \ - put_user_u32(__x, (gaddr)); \ - }) - -#define put_user_data_u16(x, gaddr, env) \ - ({ typeof(x) __x = (x); \ - if (arm_cpu_bswap_data(env)) { \ - __x = bswap16(__x); \ - } \ - put_user_u16(__x, (gaddr)); \ - }) +/* + * Note that if we need to do data accesses here, they should do a + * bswap if arm_cpu_bswap_data() returns true. + */ /* * Similar to code in accel/tcg/user-exec.c, but outside the execution loop. @@ -263,7 +231,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; @@ -395,6 +363,7 @@ void cpu_loop(CPUARMState *env) switch (n) { case ARM_NR_cacheflush: /* nop */ + env->regs[0] = 0; break; case ARM_NR_set_tls: cpu_set_tls(env, env->regs[0]); @@ -511,10 +480,10 @@ void cpu_loop(CPUARMState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, 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 5a93c925cf7..348ffb810d7 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 9a93610d245..0f1afbd91df 100644 --- a/linux-user/arm/nwfpe/fpa11.c +++ b/linux-user/arm/nwfpe/fpa11.c @@ -51,6 +51,29 @@ 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); + /* + * Use the same default NaN value as Arm VFP. This doesn't match + * the Linux kernel's nwfpe emulation, which uses an all-1s value. + */ + set_float_default_nan_pattern(0b01000000, &fpa11->fp_status); } void SetRoundingMode(const unsigned int opcode) diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index cf99fd7b8a6..8db1c4b2338 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; } + /* 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 { + write_arm_sigreturn(frame->retcode, + is_rt ? TARGET_NR_rt_sigreturn + : TARGET_NR_sigreturn); + } + 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]); - retcode = (sigreturn_fdpic_tramp + - retcode_idx * RETCODE_BYTES + thumb); - copy_retcode = true; } else { retcode = ka->sa_restorer; - copy_retcode = false; } - } else { - retcode = default_sigreturn + retcode_idx * RETCODE_BYTES + thumb; - copy_retcode = true; - } - - /* Copy the code to the stack slot for ABI compatibility. */ - if (copy_retcode) { - memcpy(frame->retcode, g2h_untagged(retcode & ~1), RETCODE_BYTES); } 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 28e03b5fec0..23c98203c40 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 4c952b2cfb2..692fd6a76e6 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_signal.h b/linux-user/arm/target_signal.h index 0e6351d9f78..ff1810b1fe0 100644 --- a/linux-user/arm/target_signal.h +++ b/linux-user/arm/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #define TARGET_ARCH_HAS_SETUP_FRAME #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 diff --git a/linux-user/arm/vdso-asmoffset.h b/linux-user/arm/vdso-asmoffset.h new file mode 100644 index 00000000000..252a95c46e1 --- /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 00000000000..b896d3d545e 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 00000000000..784b7bdb2a9 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 00000000000..38d3d510473 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 00000000000..b3bb6491dc2 --- /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 00000000000..3b00adf27a1 --- /dev/null +++ b/linux-user/arm/vdso.ld @@ -0,0 +1,67 @@ +/* + * Linker script for linux arm replacement vdso. + * + * Copyright 2023 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +VERSION { + LINUX_2.6 { + global: + __vdso_clock_gettime; + __vdso_gettimeofday; + __vdso_clock_getres; + __vdso_clock_gettime64; + + local: *; + }; +} + + +PHDRS { + phdr PT_PHDR FLAGS(4) PHDRS; + load PT_LOAD FLAGS(7) FILEHDR PHDRS; /* FLAGS=RWX */ + dynamic PT_DYNAMIC FLAGS(4); + eh_frame_hdr PT_GNU_EH_FRAME; + note PT_NOTE FLAGS(4); +} + +SECTIONS { + . = SIZEOF_HEADERS; + + /* + * The following, including the FILEHDRS and PHDRS, are modified + * when we relocate the binary. We want them to be initially + * writable for the relocation; we'll force them read-only after. + */ + .note : { *(.note*) } :load :note + .dynamic : { *(.dynamic) } :load :dynamic + .dynsym : { *(.dynsym) } :load + /* + * There ought not be any real read-write data. + * But since we manipulated the segment layout, + * we have to put these sections somewhere. + */ + .data : { + *(.data*) + *(.sdata*) + *(.got.plt) *(.got) + *(.gnu.linkonce.d.*) + *(.bss*) + *(.dynbss*) + *(.gnu.linkonce.b.*) + } + + .rodata : { *(.rodata*) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame_hdr : { *(.eh_frame_hdr) } :load :eh_frame_hdr + .eh_frame : { *(.eh_frame) } :load + + .text : { *(.text*) } :load +} diff --git a/linux-user/cpu_loop-common.h b/linux-user/cpu_loop-common.h deleted file mode 100644 index e644d2ef909..00000000000 --- a/linux-user/cpu_loop-common.h +++ /dev/null @@ -1,31 +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 . - */ - -#ifndef CPU_LOOP_COMMON_H -#define CPU_LOOP_COMMON_H - -#include "exec/log.h" -#include "special-errno.h" - -void target_exception_dump(CPUArchState *env, const char *fmt, int code); -#define EXCP_DUMP(env, fmt, code) \ - target_exception_dump(env, fmt, code) - -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs); -#endif diff --git a/linux-user/cris/cpu_loop.c b/linux-user/cris/cpu_loop.c deleted file mode 100644 index 01e6ff16fc9..00000000000 --- 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 4f532b2903d..00000000000 --- 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 0e4c8f012d7..00000000000 --- 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 4b6cf65c42b..00000000000 --- 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 7f6cade7b65..00000000000 --- 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 99eb4ec7046..00000000000 --- 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 1cf43b17a50..00000000000 --- 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 df0aceea344..00000000000 --- 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 9ace8ac2925..00000000000 --- a/linux-user/cris/target_mman.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * arch/cris/include/asm/processor.h: - * TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 3)) - * - * arch/cris/include/arch-v32/arch/processor.h - * TASK_SIZE 0xb0000000 - */ -#define TASK_UNMAPPED_BASE TARGET_PAGE_ALIGN(0xb0000000 / 3) - -/* arch/cris/include/uapi/asm/elf.h */ -#define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE * 2) - -#include "../generic/target_mman.h" diff --git a/linux-user/cris/target_prctl.h b/linux-user/cris/target_prctl.h deleted file mode 100644 index eb53b31ad55..00000000000 --- a/linux-user/cris/target_prctl.h +++ /dev/null @@ -1 +0,0 @@ -/* No special prctl support required. */ diff --git a/linux-user/cris/target_proc.h b/linux-user/cris/target_proc.h deleted file mode 100644 index 43fe29ca723..00000000000 --- a/linux-user/cris/target_proc.h +++ /dev/null @@ -1 +0,0 @@ -/* No target-specific /proc support */ diff --git a/linux-user/cris/target_resource.h b/linux-user/cris/target_resource.h deleted file mode 100644 index 227259594c0..00000000000 --- 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 ab0653fcdc0..00000000000 --- 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 3a06f373c35..00000000000 --- 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 0b5ebf1f028..00000000000 --- 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 0c8d8fc0516..00000000000 --- 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 a5b28fa3e7a..2add1665c75 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2,10 +2,18 @@ #include "qemu/osdep.h" #include +#include #include #include #include "qemu.h" +#include "user/tswap-target.h" +#include "user/page-protection.h" +#include "exec/page-protection.h" +#include "exec/mmap-lock.h" +#include "exec/translation-block.h" +#include "exec/tswap.h" +#include "user/guest-base.h" #include "user-internals.h" #include "signal-common.h" #include "loader.h" @@ -21,7 +29,11 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "target_signal.h" -#include "accel/tcg/debuginfo.h" +#include "tcg/debuginfo.h" + +#ifdef TARGET_ARM +#include "target/arm/cpu-features.h" +#endif #ifdef _ARCH_PPC64 #undef ARCH_DLINFO @@ -33,6 +45,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 */ @@ -182,7 +207,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); @@ -285,19 +310,34 @@ 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 @@ -402,6 +442,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 { @@ -410,6 +456,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 */ @@ -419,6 +467,7 @@ enum { static bool init_guest_commpage(void) { ARMCPU *cpu = ARM_CPU(thread_cpu); + int host_page_size = qemu_real_host_page_size(); abi_ptr commpage; void *want; void *addr; @@ -431,10 +480,12 @@ static bool init_guest_commpage(void) return true; } - commpage = HI_COMMPAGE & -qemu_host_page_size; + commpage = HI_COMMPAGE & -host_page_size; want = g2h_untagged(commpage); - addr = mmap(want, qemu_host_page_size, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + 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"); @@ -447,12 +498,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_mask, + page_set_flags(commpage, commpage | (host_page_size - 1), PAGE_READ | PAGE_EXEC | PAGE_VALID); return true; } @@ -498,20 +549,32 @@ 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; } -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; } @@ -540,6 +603,12 @@ const char *elf_hwcap_str(uint32_t bit) [__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; @@ -553,6 +622,8 @@ const char *elf_hwcap2_str(uint32_t bit) [__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; @@ -565,7 +636,7 @@ const char *elf_hwcap2_str(uint32_t bit) 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" @@ -592,6 +663,23 @@ 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 */ @@ -663,7 +751,23 @@ enum { 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_HWCAP_A64_PACG = 1ULL << 31, + ARM_HWCAP_A64_GCS = 1ULL << 32, + ARM_HWCAP_A64_CMPBR = 1ULL << 33, + ARM_HWCAP_A64_FPRCVT = 1ULL << 34, + ARM_HWCAP_A64_F8MM8 = 1ULL << 35, + ARM_HWCAP_A64_F8MM4 = 1ULL << 36, + ARM_HWCAP_A64_SVE_F16MM = 1ULL << 37, + ARM_HWCAP_A64_SVE_ELTPERM = 1ULL << 38, + ARM_HWCAP_A64_SVE_AES2 = 1ULL << 39, + ARM_HWCAP_A64_SVE_BFSCALE = 1ULL << 40, + ARM_HWCAP_A64_SVE2P2 = 1ULL << 41, + ARM_HWCAP_A64_SME2P2 = 1ULL << 42, + ARM_HWCAP_A64_SME_SBITPERM = 1ULL << 43, + ARM_HWCAP_A64_SME_AES = 1ULL << 44, + ARM_HWCAP_A64_SME_SFEXPA = 1ULL << 45, + ARM_HWCAP_A64_SME_STMOP = 1ULL << 46, + ARM_HWCAP_A64_SME_SMOP4 = 1ULL << 47, ARM_HWCAP2_A64_DCPODP = 1 << 0, ARM_HWCAP2_A64_SVE2 = 1 << 1, @@ -696,6 +800,39 @@ 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, + ARM_HWCAP2_A64_SVE_B16B16 = 1ULL << 45, + ARM_HWCAP2_A64_LRCPC3 = 1ULL << 46, + ARM_HWCAP2_A64_LSE128 = 1ULL << 47, + ARM_HWCAP2_A64_FPMR = 1ULL << 48, + ARM_HWCAP2_A64_LUT = 1ULL << 49, + ARM_HWCAP2_A64_FAMINMAX = 1ULL << 50, + ARM_HWCAP2_A64_F8CVT = 1ULL << 51, + ARM_HWCAP2_A64_F8FMA = 1ULL << 52, + ARM_HWCAP2_A64_F8DP4 = 1ULL << 53, + ARM_HWCAP2_A64_F8DP2 = 1ULL << 54, + ARM_HWCAP2_A64_F8E4M3 = 1ULL << 55, + ARM_HWCAP2_A64_F8E5M2 = 1ULL << 56, + ARM_HWCAP2_A64_SME_LUTV2 = 1ULL << 57, + ARM_HWCAP2_A64_SME_F8F16 = 1ULL << 58, + ARM_HWCAP2_A64_SME_F8F32 = 1ULL << 59, + ARM_HWCAP2_A64_SME_SF8FMA = 1ULL << 60, + ARM_HWCAP2_A64_SME_SF8DP4 = 1ULL << 61, + ARM_HWCAP2_A64_SME_SF8DP2 = 1ULL << 62, + ARM_HWCAP2_A64_POE = 1ULL << 63, }; #define ELF_HWCAP get_elf_hwcap() @@ -726,12 +863,14 @@ 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); @@ -742,10 +881,10 @@ uint32_t get_elf_hwcap(void) return hwcaps; } -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); @@ -765,6 +904,7 @@ 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 | @@ -773,13 +913,15 @@ 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[] = { + static const char * const hwcap_str[] = { [__builtin_ctz(ARM_HWCAP_A64_FP )] = "fp", [__builtin_ctz(ARM_HWCAP_A64_ASIMD )] = "asimd", [__builtin_ctz(ARM_HWCAP_A64_EVTSTRM )] = "evtstrm", @@ -812,6 +954,22 @@ const char *elf_hwcap_str(uint32_t bit) [__builtin_ctz(ARM_HWCAP_A64_SB )] = "sb", [__builtin_ctz(ARM_HWCAP_A64_PACA )] = "paca", [__builtin_ctz(ARM_HWCAP_A64_PACG )] = "pacg", + [__builtin_ctzll(ARM_HWCAP_A64_GCS )] = "gcs", + [__builtin_ctzll(ARM_HWCAP_A64_CMPBR )] = "cmpbr", + [__builtin_ctzll(ARM_HWCAP_A64_FPRCVT)] = "fprcvt", + [__builtin_ctzll(ARM_HWCAP_A64_F8MM8 )] = "f8mm8", + [__builtin_ctzll(ARM_HWCAP_A64_F8MM4 )] = "f8mm4", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_F16MM)] = "svef16mm", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_ELTPERM)] = "sveeltperm", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_AES2)] = "sveaes2", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_BFSCALE)] = "svebfscale", + [__builtin_ctzll(ARM_HWCAP_A64_SVE2P2)] = "sve2p2", + [__builtin_ctzll(ARM_HWCAP_A64_SME2P2)] = "sme2p2", + [__builtin_ctzll(ARM_HWCAP_A64_SME_SBITPERM)] = "smesbitperm", + [__builtin_ctzll(ARM_HWCAP_A64_SME_AES)] = "smeaes", + [__builtin_ctzll(ARM_HWCAP_A64_SME_SFEXPA)] = "smesfexpa", + [__builtin_ctzll(ARM_HWCAP_A64_SME_STMOP)] = "smestmop", + [__builtin_ctzll(ARM_HWCAP_A64_SME_SMOP4)] = "smesmop4", }; return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; @@ -819,7 +977,7 @@ const char *elf_hwcap_str(uint32_t bit) const char *elf_hwcap2_str(uint32_t bit) { - static const char *hwcap_str[] = { + static const char * const hwcap_str[] = { [__builtin_ctz(ARM_HWCAP2_A64_DCPODP )] = "dcpodp", [__builtin_ctz(ARM_HWCAP2_A64_SVE2 )] = "sve2", [__builtin_ctz(ARM_HWCAP2_A64_SVEAES )] = "sveaes", @@ -844,13 +1002,45 @@ const char *elf_hwcap2_str(uint32_t bit) [__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 )] = "sme_i16i64", - [__builtin_ctz(ARM_HWCAP2_A64_SME_F64F64 )] = "sme_f64f64", - [__builtin_ctz(ARM_HWCAP2_A64_SME_I8I32 )] = "sme_i8i32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_F16F32 )] = "sme_f16f32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_B16F32 )] = "sme_b16f32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_F32F32 )] = "sme_f32f32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_FA64 )] = "sme_fa64", + [__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", + [__builtin_ctzll(ARM_HWCAP2_A64_SVE_B16B16 )] = "sveb16b16", + [__builtin_ctzll(ARM_HWCAP2_A64_LRCPC3 )] = "lrcpc3", + [__builtin_ctzll(ARM_HWCAP2_A64_LSE128 )] = "lse128", + [__builtin_ctzll(ARM_HWCAP2_A64_FPMR )] = "fpmr", + [__builtin_ctzll(ARM_HWCAP2_A64_LUT )] = "lut", + [__builtin_ctzll(ARM_HWCAP2_A64_FAMINMAX )] = "faminmax", + [__builtin_ctzll(ARM_HWCAP2_A64_F8CVT )] = "f8cvt", + [__builtin_ctzll(ARM_HWCAP2_A64_F8FMA )] = "f8fma", + [__builtin_ctzll(ARM_HWCAP2_A64_F8DP4 )] = "f8dp4", + [__builtin_ctzll(ARM_HWCAP2_A64_F8DP2 )] = "f8dp2", + [__builtin_ctzll(ARM_HWCAP2_A64_F8E4M3 )] = "f8e4m3", + [__builtin_ctzll(ARM_HWCAP2_A64_F8E5M2 )] = "f8e5m2", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_LUTV2 )] = "smelutv2", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F16 )] = "smef8f16", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F32 )] = "smef8f32", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP4 )] = "smesf8dp4", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP2 )] = "smesf8dp2", + [__builtin_ctzll(ARM_HWCAP2_A64_POE )] = "poe", }; return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; @@ -858,28 +1048,58 @@ const char *elf_hwcap2_str(uint32_t bit) #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_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_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) @@ -1094,6 +1314,14 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en #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 @@ -1104,6 +1332,8 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en #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) { @@ -1179,6 +1409,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; } @@ -1383,101 +1621,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMBState *env #endif /* TARGET_MICROBLAZE */ -#ifdef TARGET_NIOS2 - -#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_MASK, - 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_ARCH EM_OPENRISC @@ -1594,21 +1737,6 @@ static uint32_t get_elf_hwcap(void) #endif -#ifdef TARGET_CRIS - -#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_CLASS ELFCLASS32 @@ -1783,6 +1911,8 @@ 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 @@ -1791,8 +1921,10 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #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() @@ -1828,11 +1960,13 @@ static inline void init_thread(struct target_pt_regs *regs, #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; @@ -1845,16 +1979,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; + } } /* @@ -2054,9 +2192,12 @@ static inline void memcpy_fromfs(void * to, const void * from, unsigned long n) memcpy(to, from, n); } -#ifdef BSWAP_NEEDED static void bswap_ehdr(struct elfhdr *ehdr) { + if (!target_needs_bswap()) { + return; + } + bswap16s(&ehdr->e_type); /* Object file type */ bswap16s(&ehdr->e_machine); /* Architecture */ bswap32s(&ehdr->e_version); /* Object file version */ @@ -2074,8 +2215,11 @@ static void bswap_ehdr(struct elfhdr *ehdr) static void bswap_phdr(struct elf_phdr *phdr, int phnum) { - int i; - for (i = 0; i < phnum; ++i, ++phdr) { + if (!target_needs_bswap()) { + return; + } + + for (int i = 0; i < phnum; ++i, ++phdr) { bswap32s(&phdr->p_type); /* Segment type */ bswap32s(&phdr->p_flags); /* Segment flags */ bswaptls(&phdr->p_offset); /* Segment file offset */ @@ -2089,8 +2233,11 @@ static void bswap_phdr(struct elf_phdr *phdr, int phnum) static void bswap_shdr(struct elf_shdr *shdr, int shnum) { - int i; - for (i = 0; i < shnum; ++i, ++shdr) { + if (!target_needs_bswap()) { + return; + } + + for (int i = 0; i < shnum; ++i, ++shdr) { bswap32s(&shdr->sh_name); bswap32s(&shdr->sh_type); bswaptls(&shdr->sh_flags); @@ -2106,6 +2253,10 @@ static void bswap_shdr(struct elf_shdr *shdr, int shnum) static void bswap_sym(struct elf_sym *sym) { + if (!target_needs_bswap()) { + return; + } + bswap32s(&sym->st_name); bswaptls(&sym->st_value); bswaptls(&sym->st_size); @@ -2115,6 +2266,10 @@ static void bswap_sym(struct elf_sym *sym) #ifdef TARGET_MIPS static void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) { + if (!target_needs_bswap()) { + return; + } + bswap16s(&abiflags->version); bswap32s(&abiflags->ases); bswap32s(&abiflags->isa_ext); @@ -2122,20 +2277,12 @@ static void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) bswap32s(&abiflags->flags2); } #endif -#else -static inline void bswap_ehdr(struct elfhdr *ehdr) { } -static inline void bswap_phdr(struct elf_phdr *phdr, int phnum) { } -static inline void bswap_shdr(struct elf_shdr *shdr, int shnum) { } -static inline void bswap_sym(struct elf_sym *sym) { } -#ifdef TARGET_MIPS -static inline void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) { } -#endif -#endif #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. */ @@ -2304,31 +2451,58 @@ 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). Return false on mapping failure. */ -static bool zero_bss(abi_ulong start_bss, abi_ulong end_bss, int prot) +static bool zero_bss(abi_ulong start_bss, abi_ulong end_bss, + int prot, Error **errp) { abi_ulong align_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; + } + align_bss = TARGET_PAGE_ALIGN(start_bss); end_bss = TARGET_PAGE_ALIGN(end_bss); if (start_bss < align_bss) { int flags = page_get_flags(start_bss); - if (!(flags & PAGE_VALID)) { - /* Map the start of the bss. */ + 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 if (flags & PAGE_WRITE) { - /* The page is already mapped writable. */ - memset(g2h_untagged(start_bss), 0, align_bss - start_bss); } else { - /* Read-only zeros? */ - g_assert_not_reached(); + /* + * 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); } } - return align_bss >= end_bss || - target_mmap(align_bss, end_bss - align_bss, prot, - MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0) != -1; + 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; } #if defined(TARGET_ARM) @@ -2377,7 +2551,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; @@ -2465,10 +2640,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) { size += 2; - if (k_platform) + } + if (vdso_info) { size += 2; + } #ifdef DLINFO_ARCH_ITEMS size += DLINFO_ARCH_ITEMS * 2; #endif @@ -2520,13 +2700,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); @@ -2550,6 +2724,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 @@ -2731,7 +2908,7 @@ static bool pgb_addr_set(PGBAddrs *ga, abi_ulong guest_loaddr, /* Add any HI_COMMPAGE not covered by reserved_va. */ if (reserved_va < HI_COMMPAGE) { - ga->bounds[n][0] = HI_COMMPAGE & qemu_host_page_mask; + ga->bounds[n][0] = HI_COMMPAGE & qemu_real_host_page_mask(); ga->bounds[n][1] = HI_COMMPAGE + TARGET_PAGE_SIZE - 1; n++; } @@ -2819,7 +2996,7 @@ static uintptr_t pgb_try_itree(const PGBAddrs *ga, uintptr_t base, static uintptr_t pgb_find_itree(const PGBAddrs *ga, IntervalTreeRoot *root, uintptr_t align, uintptr_t brk) { - uintptr_t last = mmap_min_addr; + uintptr_t last = sizeof(uintptr_t) == 4 ? MiB : GiB; uintptr_t base, skip; while (true) { @@ -2855,8 +3032,6 @@ static void pgb_dynamic(const char *image_name, uintptr_t guest_loaddr, uintptr_t brk, ret; PGBAddrs ga; - assert(QEMU_IS_ALIGNED(guest_loaddr, align)); - /* Try the identity map first. */ if (pgb_addr_set(&ga, guest_loaddr, guest_hiaddr, true)) { brk = (uintptr_t)sbrk(0); @@ -2913,7 +3088,7 @@ 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); + uintptr_t align = MAX(SHMLBA, TARGET_PAGE_SIZE); /* Sanity check the guest binary. */ if (reserved_va) { @@ -3009,10 +3184,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 { @@ -3040,40 +3214,34 @@ 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++) { - bswap32s(note.data + i); + if (target_needs_bswap()) { + for (int i = 4; i < n / 4; i++) { + bswap32s(note.data + i); + } } -#endif /* * Note that nhdr is 3 words, and that the "name" described by namesz * 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; @@ -3093,29 +3261,35 @@ static bool parse_elf_properties(int image_fd, } } -/* Load an ELF image into the address space. - - 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. */ +/** + * 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. + */ -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; @@ -3126,15 +3300,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); @@ -3148,12 +3318,12 @@ 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; } @@ -3162,7 +3332,7 @@ static void load_elf_image(const char *image_name, int image_fd, 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; @@ -3171,17 +3341,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"); @@ -3189,7 +3352,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) { @@ -3199,6 +3362,8 @@ static void load_elf_image(const char *image_name, int image_fd, load_addr = loaddr; + align = pow2ceil(align); + if (pinterp_name != NULL) { if (ehdr->e_type == ET_EXEC) { /* @@ -3207,8 +3372,6 @@ static void load_elf_image(const char *image_name, int image_fd, */ probe_guest_base(image_name, loaddr, hiaddr); } else { - abi_ulong align; - /* * The binary is dynamic, but we still need to * select guest_base. In this case we pass a size. @@ -3226,10 +3389,7 @@ static void load_elf_image(const char *image_name, int image_fd, * Since we do not have complete control over the guest * address space, we prefer the kernel to choose some address * rather than force the use of LOAD_ADDR via MAP_FIXED. - * But without MAP_FIXED we cannot guarantee alignment, - * only suggest it. */ - align = pow2ceil(info->alignment); if (align) { load_addr &= -align; } @@ -3253,13 +3413,35 @@ static void load_elf_image(const char *image_name, int image_fd, * In both cases, we will overwrite pages in this range with mappings * from the executable. */ - load_addr = target_mmap(load_addr, (size_t)hiaddr - loaddr + 1, 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_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 = TARGET_PAGE_ALIGN(align_addr + reserve_size); + abi_ulong load_end = TARGET_PAGE_ALIGN(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)) { @@ -3342,9 +3524,9 @@ static void load_elf_image(const char *image_name, int image_fd, * but no backing file segment. */ if (eppnt->p_filesz != 0) { - error = target_mmap(vaddr_ps, eppnt->p_filesz + vaddr_po, + error = imgsrc_mmap(vaddr_ps, eppnt->p_filesz + vaddr_po, elf_prot, MAP_PRIVATE | MAP_FIXED, - image_fd, eppnt->p_offset - vaddr_po); + src, eppnt->p_offset - vaddr_po); if (error == -1) { goto exit_mmap; } @@ -3352,8 +3534,8 @@ static void load_elf_image(const char *image_name, int image_fd, /* 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)) { - goto exit_mmap; + !zero_bss(vaddr_ef, vaddr_em, elf_prot, &err)) { + goto exit_errmsg; } /* Find the full program boundaries. */ @@ -3376,20 +3558,11 @@ static void load_elf_image(const char *image_name, int image_fd, #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 @@ -3402,23 +3575,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, image_fd, 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; @@ -3430,6 +3596,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; @@ -3447,11 +3615,59 @@ 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); + } + + /* 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; } - load_elf_image(filename, fd, info, NULL, bprm_buf); + /* 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) @@ -3487,7 +3703,7 @@ static const char *lookup_symbolxx(struct syminfo *s, uint64_t 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; @@ -3498,19 +3714,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; } @@ -3528,31 +3745,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; } - - segsz = shdr[sym_idx].sh_size; - syms = g_try_malloc(segsz); - if (!syms || pread(fd, syms, segsz, shdr[sym_idx].sh_offset) != segsz) { + if (!imgsrc_read(strings, shdr[str_idx].sh_offset, segsz, src, NULL)) { goto give_up; } + segsz = shdr[sym_idx].sh_size; 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. */ @@ -3577,10 +3796,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; @@ -3589,20 +3810,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; + { + 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; + s->disas_symtab.elf32 = syms; #else - s->disas_symtab.elf64 = syms; + s->disas_symtab.elf64 = syms; #endif - s->lookup_symbol = lookup_symbolxx; - s->next = syminfos; - syminfos = s; - + s->lookup_symbol = lookup_symbolxx; + s->next = syminfos; + syminfos = s; + } return; -give_up: - g_free(s); + give_up: g_free(strings); g_free(syms); } @@ -3644,8 +3868,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; @@ -3654,13 +3884,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) interp_info.fp_abi = MIPS_ABI_FP_UNKNOWN; #endif - 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 */ @@ -3723,8 +3947,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; @@ -3732,10 +3957,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); @@ -3747,8 +3976,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. @@ -3769,6 +3999,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) } #ifdef USE_ELF_CORE_DUMP + /* * Definitions to generate Intel SVR4-like core files. * These mostly have the same names as the SVR4 types with "target_elf_" @@ -3808,18 +4039,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 */ @@ -3859,80 +4078,12 @@ 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) { + if (!target_needs_bswap()) { + return; + } + prstatus->pr_info.si_signo = tswap32(prstatus->pr_info.si_signo); prstatus->pr_info.si_code = tswap32(prstatus->pr_info.si_code); prstatus->pr_info.si_errno = tswap32(prstatus->pr_info.si_errno); @@ -3950,6 +4101,10 @@ static void bswap_prstatus(struct target_elf_prstatus *prstatus) static void bswap_psinfo(struct target_elf_prpsinfo *psinfo) { + if (!target_needs_bswap()) { + return; + } + psinfo->pr_flag = tswapal(psinfo->pr_flag); psinfo->pr_uid = tswap16(psinfo->pr_uid); psinfo->pr_gid = tswap16(psinfo->pr_gid); @@ -3961,156 +4116,75 @@ static void bswap_psinfo(struct target_elf_prpsinfo *psinfo) static void bswap_note(struct elf_note *en) { + if (!target_needs_bswap()) { + return; + } + bswap32s(&en->n_namesz); bswap32s(&en->n_descsz); bswap32s(&en->n_type); } -#else -static inline void bswap_prstatus(struct target_elf_prstatus *p) { } -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(vaddr start, vaddr end, int 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; @@ -4128,95 +4202,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), + }; -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); + elf_core_copy_regs(&prstatus.pr_reg, cpu_env(cpu)); + bswap_prstatus(&prstatus); + memcpy(data, &prstatus, sizeof(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; - } - 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(); + 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] = ' '; + } + } 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); } /* @@ -4240,27 +4297,9 @@ static int dump_write(int fd, const void *ptr, size_t size) { const char *bufp = (const char *)ptr; ssize_t bytes_written, bytes_left; - struct rlimit dumpsize; - off_t pos; bytes_written = 0; - getrlimit(RLIMIT_CORE, &dumpsize); - if ((pos = lseek(fd, 0, SEEK_CUR))==-1) { - if (errno == ESPIPE) { /* not a seekable stream */ - bytes_left = size; - } else { - return pos; - } - } else { - if (dumpsize.rlim_cur <= pos) { - return -1; - } else if (dumpsize.rlim_cur == RLIM_INFINITY) { - bytes_left = size; - } else { - size_t limit_left=dumpsize.rlim_cur - pos; - bytes_left = limit_left >= size ? size : limit_left ; - } - } + bytes_left = size; /* * In normal conditions, single write(2) should do but @@ -4282,135 +4321,76 @@ static int dump_write(int fd, const void *ptr, size_t size) return (0); } -static int write_note(struct memelfnote *men, int fd) -{ - struct elf_note en; - - 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) +static int wmr_page_unprotect_regions(void *opaque, vaddr start, + vaddr end, int flags) { - CPUState *cpu = env_cpu((CPUArchState *)env); - TaskState *ts = (TaskState *)cpu->opaque; - struct elf_thread_status *ets; + if ((flags & (PAGE_WRITE | PAGE_WRITE_ORG)) == PAGE_WRITE_ORG) { + size_t step = MAX(TARGET_PAGE_SIZE, qemu_real_host_page_size()); - 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]); + while (1) { + page_unprotect(NULL, start, 0); + if (end - start <= step) { + break; + } + start += step; + } + } + return 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); -} +typedef struct { + unsigned count; + size_t size; +} CountAndSizeRegions; -static int fill_note_info(struct elf_note_info *info, - long signr, const CPUArchState *env) +static int wmr_count_and_size_regions(void *opaque, vaddr start, + vaddr end, int flags) { -#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); + CountAndSizeRegions *css = opaque; - /* - * 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 */ - WITH_QEMU_LOCK_GUARD(&qemu_cpu_list_lock) { - CPU_FOREACH(cpu) { - if (cpu == thread_cpu) { - continue; - } - fill_thread_info(info, cpu->env_ptr); - } - } - - return (0); + css->count++; + css->size += vma_dump_size(start, end, flags); + return 0; } -static void free_note_info(struct elf_note_info *info) +typedef struct { + struct elf_phdr *phdr; + off_t offset; +} FillRegionPhdr; + +static int wmr_fill_region_phdr(void *opaque, vaddr start, + vaddr end, int flags) { - struct elf_thread_status *ets; + FillRegionPhdr *d = opaque; + struct elf_phdr *phdr = d->phdr; - while (!QTAILQ_EMPTY(&info->thread_list)) { - ets = QTAILQ_FIRST(&info->thread_list); - QTAILQ_REMOVE(&info->thread_list, ets, ets_link); - g_free(ets); - } + 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; - g_free(info->prstatus); - g_free(info->psinfo); - g_free(info->notes); + bswap_phdr(phdr, 1); + d->phdr = phdr + 1; + return 0; } -static int write_note_info(struct elf_note_info *info, int fd) +static int wmr_write_region(void *opaque, vaddr start, + vaddr end, int flags) { - struct elf_thread_status *ets; - int i, error = 0; - - /* 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); + int fd = *(int *)opaque; + size_t size = vma_dump_size(start, end, flags); - /* write prstatus for each thread */ - QTAILQ_FOREACH(ets, &info->thread_list, ets_link) { - if ((error = write_note(&ets->notes[0], fd)) != 0) - return (error); + if (!size) { + return 0; } - - return (0); + return dump_write(fd, g2h_untagged(start), size); } /* @@ -4458,148 +4438,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); + if (prctl(PR_GET_DUMPABLE) == 0) { + return 0; + } - errno = 0; - getrlimit(RLIMIT_CORE, &dumpsize); - if (dumpsize.rlim_cur == 0) + if (getrlimit(RLIMIT_CORE, &dumpsize) < 0 || dumpsize.rlim_cur == 0) { return 0; + } - corefile = core_dump_filename(ts); + cpu_list_lock(); + mmap_lock(); - if ((fd = open(corefile, O_WRONLY | O_CREAT, - S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) - return (-errno); + /* 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; - - walk_memory_regions(mm, vma_walker); - segs = vma_get_mapping_count(mm); + memset(&css, 0, sizeof(css)); + walk_memory_regions(&css, wmr_count_and_size_regions); - /* - * Construct valid coredump ELF header. We also - * add one more segment for notes. - */ - fill_elf_header(&elf, segs + 1, ELF_MACHINE, 0); - if (dump_write(fd, &elf, sizeof (elf)) != 0) - goto out; + cpus = 0; + CPU_FOREACH(cpu_iter) { + cpus++; + } - /* fill in the in-memory version of notes */ - if (fill_note_info(&info, signr, env) < 0) - goto out; + offset = sizeof(struct elfhdr); + offset += (css.count + 1) * sizeof(struct elf_phdr); + note_offset = offset; - offset += sizeof (elf); /* elf header */ - offset += (segs + 1) * sizeof (struct elf_phdr); /* program headers */ + 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); - /* write out notes program header */ - fill_elf_note_phdr(&phdr, info.notes_size, offset); + /* 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; + } - offset += info.notes_size; - if (dump_write(fd, &phdr, sizeof (phdr)) != 0) + { + 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; + } /* - * ELF specification wants data to start at page boundary so - * we align it here. + * 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. */ - data_offset = offset = roundup(offset, ELF_EXEC_PAGESIZE); + { + g_autofree void *header = g_malloc0(data_offset); + FillRegionPhdr frp; + void *hptr, *dptr; + + /* Create elf file header. */ + hptr = header; + fill_elf_header(hptr, css.count + 1, ELF_MACHINE, 0); + hptr += sizeof(struct elfhdr); + + /* Create elf program headers. */ + fill_elf_note_phdr(hptr, note_size, note_offset); + hptr += sizeof(struct elf_phdr); + + frp.phdr = hptr; + frp.offset = data_offset; + walk_memory_regions(&frp, wmr_fill_region_phdr); + hptr = frp.phdr; + + /* Create the notes. */ + dptr = fill_note(&hptr, NT_AUXV, "CORE", ts->info->auxv_len); + fill_auxv_note(dptr, ts); + + dptr = fill_note(&hptr, NT_PRPSINFO, "CORE", + sizeof(struct target_elf_prpsinfo)); + fill_prpsinfo_note(dptr, ts); + + 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); + } - /* - * 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)); - - 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) - goto out; - - /* align data to page boundary */ - if (lseek(fd, data_offset, SEEK_SET) != data_offset) + if (walk_memory_regions(&fd, wmr_write_region) < 0) { 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 3017d28a3c3..1ff8fe4f072 100644 --- a/linux-user/exit.c +++ b/linux-user/exit.c @@ -17,14 +17,11 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" -#include "accel/tcg/perf.h" +#include "tcg/perf.h" #include "gdbstub/syscalls.h" #include "qemu.h" #include "user-internals.h" #include "qemu/plugin.h" -#ifdef CONFIG_GPROF -#include -#endif #ifdef CONFIG_GCOV extern void __gcov_dump(void); @@ -32,9 +29,6 @@ extern void __gcov_dump(void); void preexit_cleanup(CPUArchState *env, int code) { -#ifdef CONFIG_GPROF - _mcleanup(); -#endif #ifdef CONFIG_GCOV __gcov_dump(); #endif diff --git a/linux-user/fd-trans.c b/linux-user/fd-trans.c index c04a97c73a3..f83d1f79d51 100644 --- a/linux-user/fd-trans.c +++ b/linux-user/fd-trans.c @@ -25,12 +25,32 @@ #ifdef CONFIG_RTNETLINK #include #include +#include #endif #include "qemu.h" #include "user-internals.h" #include "fd-trans.h" #include "signal-common.h" +#define NDM_RTA(r) ((struct rtattr*)(((char*)(r)) + \ + NLMSG_ALIGN(sizeof(struct ndmsg)))) + +enum { + QEMU_IFA_UNSPEC, + QEMU_IFA_ADDRESS, + QEMU_IFA_LOCAL, + QEMU_IFA_LABEL, + QEMU_IFA_BROADCAST, + QEMU_IFA_ANYCAST, + QEMU_IFA_CACHEINFO, + QEMU_IFA_MULTICAST, + QEMU_IFA_FLAGS, + QEMU_IFA_RT_PRIORITY, + QEMU_IFA_TARGET_NETNSID, + QEMU_IFA_PROTO, + QEMU__IFA__MAX, +}; + enum { QEMU_IFLA_BR_UNSPEC, QEMU_IFLA_BR_FORWARD_DELAY, @@ -141,6 +161,14 @@ enum { QEMU_IFLA_PROTO_DOWN_REASON, QEMU_IFLA_PARENT_DEV_NAME, QEMU_IFLA_PARENT_DEV_BUS_NAME, + QEMU_IFLA_GRO_MAX_SIZE, + QEMU_IFLA_TSO_MAX_SIZE, + QEMU_IFLA_TSO_MAX_SEGS, + QEMU_IFLA_ALLMULTI, + QEMU_IFLA_DEVLINK_PORT, + QEMU_IFLA_GSO_IPV4_MAX_SIZE, + QEMU_IFLA_GRO_IPV4_MAX_SIZE, + QEMU_IFLA_DPLL_PIN, QEMU___IFLA_MAX }; @@ -982,6 +1010,22 @@ static abi_long host_to_target_data_vfinfo_nlattr(struct nlattr *nlattr, return 0; } +static abi_long host_to_target_data_prop_nlattr(struct nlattr *nlattr, + void *context) +{ + switch (nlattr->nla_type) { + /* string */ + case QEMU_IFLA_ALT_IFNAME: + break; + default: + qemu_log_mask(LOG_UNIMP, "Unknown host PROP type: %d\n", + nlattr->nla_type); + break; + } + return 0; +} + + static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) { uint32_t *u32; @@ -990,7 +1034,7 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) struct rtnl_link_ifmap *map; struct linkinfo_context li_context; - switch (rtattr->rta_type) { + switch (rtattr->rta_type & NLA_TYPE_MASK) { /* binary stream */ case QEMU_IFLA_ADDRESS: case QEMU_IFLA_BROADCAST: @@ -1028,6 +1072,12 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) case QEMU_IFLA_CARRIER_DOWN_COUNT: case QEMU_IFLA_MIN_MTU: case QEMU_IFLA_MAX_MTU: + case QEMU_IFLA_GRO_MAX_SIZE: + case QEMU_IFLA_TSO_MAX_SIZE: + case QEMU_IFLA_TSO_MAX_SEGS: + case QEMU_IFLA_ALLMULTI: + case QEMU_IFLA_GSO_IPV4_MAX_SIZE: + case QEMU_IFLA_GRO_IPV4_MAX_SIZE: u32 = RTA_DATA(rtattr); *u32 = tswap32(*u32); break; @@ -1123,6 +1173,10 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, NULL, host_to_target_data_vfinfo_nlattr); + case QEMU_IFLA_PROP_LIST: + return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, + NULL, + host_to_target_data_prop_nlattr); default: qemu_log_mask(LOG_UNIMP, "Unknown host QEMU_IFLA type: %d\n", rtattr->rta_type); @@ -1138,20 +1192,21 @@ static abi_long host_to_target_data_addr_rtattr(struct rtattr *rtattr) switch (rtattr->rta_type) { /* binary: depends on family type */ - case IFA_ADDRESS: - case IFA_LOCAL: + case QEMU_IFA_ADDRESS: + case QEMU_IFA_LOCAL: + case QEMU_IFA_PROTO: break; /* string */ - case IFA_LABEL: + case QEMU_IFA_LABEL: break; /* u32 */ - case IFA_FLAGS: - case IFA_BROADCAST: + case QEMU_IFA_FLAGS: + case QEMU_IFA_BROADCAST: u32 = RTA_DATA(rtattr); *u32 = tswap32(*u32); break; /* struct ifa_cacheinfo */ - case IFA_CACHEINFO: + case QEMU_IFA_CACHEINFO: ci = RTA_DATA(rtattr); ci->ifa_prefered = tswap32(ci->ifa_prefered); ci->ifa_valid = tswap32(ci->ifa_valid); @@ -1209,6 +1264,35 @@ static abi_long host_to_target_data_route_rtattr(struct rtattr *rtattr) return 0; } +static abi_long host_to_target_data_neigh_rtattr(struct rtattr *rtattr) +{ + struct nda_cacheinfo *ndac; + uint32_t *u32; + + switch (rtattr->rta_type) { + case NDA_UNSPEC: + case NDA_DST: + case NDA_LLADDR: + break; + case NDA_PROBES: + u32 = RTA_DATA(rtattr); + *u32 = tswap32(*u32); + break; + case NDA_CACHEINFO: + ndac = RTA_DATA(rtattr); + ndac->ndm_confirmed = tswap32(ndac->ndm_confirmed); + ndac->ndm_used = tswap32(ndac->ndm_used); + ndac->ndm_updated = tswap32(ndac->ndm_updated); + ndac->ndm_refcnt = tswap32(ndac->ndm_refcnt); + break; + default: + qemu_log_mask(LOG_UNIMP, "Unknown host to target NEIGH type: %d\n", + rtattr->rta_type); + break; + } + return 0; +} + static abi_long host_to_target_link_rtattr(struct rtattr *rtattr, uint32_t rtattr_len) { @@ -1230,12 +1314,20 @@ static abi_long host_to_target_route_rtattr(struct rtattr *rtattr, host_to_target_data_route_rtattr); } +static abi_long host_to_target_neigh_rtattr(struct rtattr *rtattr, + uint32_t rtattr_len) +{ + return host_to_target_for_each_rtattr(rtattr, rtattr_len, + host_to_target_data_neigh_rtattr); +} + static abi_long host_to_target_data_route(struct nlmsghdr *nlh) { uint32_t nlmsg_len; struct ifinfomsg *ifi; struct ifaddrmsg *ifa; struct rtmsg *rtm; + struct ndmsg *ndm; nlmsg_len = nlh->nlmsg_len; switch (nlh->nlmsg_type) { @@ -1262,6 +1354,17 @@ static abi_long host_to_target_data_route(struct nlmsghdr *nlh) nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); } break; + case RTM_NEWNEIGH: + case RTM_DELNEIGH: + case RTM_GETNEIGH: + if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ndm))) { + ndm = NLMSG_DATA(nlh); + ndm->ndm_ifindex = tswap32(ndm->ndm_ifindex); + ndm->ndm_state = tswap16(ndm->ndm_state); + host_to_target_neigh_rtattr(NDM_RTA(ndm), + nlmsg_len - NLMSG_LENGTH(sizeof(*ndm))); + } + break; case RTM_NEWROUTE: case RTM_DELROUTE: case RTM_GETROUTE: @@ -1398,8 +1501,8 @@ static abi_long target_to_host_data_addr_rtattr(struct rtattr *rtattr) { switch (rtattr->rta_type) { /* binary: depends on family type */ - case IFA_LOCAL: - case IFA_ADDRESS: + case QEMU_IFA_LOCAL: + case QEMU_IFA_ADDRESS: break; default: qemu_log_mask(LOG_UNIMP, "Unknown target IFA type: %d\n", @@ -1409,6 +1512,35 @@ static abi_long target_to_host_data_addr_rtattr(struct rtattr *rtattr) return 0; } +static abi_long target_to_host_data_neigh_rtattr(struct rtattr *rtattr) +{ + struct nda_cacheinfo *ndac; + uint32_t *u32; + + switch (rtattr->rta_type) { + case NDA_UNSPEC: + case NDA_DST: + case NDA_LLADDR: + break; + case NDA_PROBES: + u32 = RTA_DATA(rtattr); + *u32 = tswap32(*u32); + break; + case NDA_CACHEINFO: + ndac = RTA_DATA(rtattr); + ndac->ndm_confirmed = tswap32(ndac->ndm_confirmed); + ndac->ndm_used = tswap32(ndac->ndm_used); + ndac->ndm_updated = tswap32(ndac->ndm_updated); + ndac->ndm_refcnt = tswap32(ndac->ndm_refcnt); + break; + default: + qemu_log_mask(LOG_UNIMP, "Unknown target NEIGH type: %d\n", + rtattr->rta_type); + break; + } + return 0; +} + static abi_long target_to_host_data_route_rtattr(struct rtattr *rtattr) { uint32_t *u32; @@ -1447,6 +1579,13 @@ static void target_to_host_addr_rtattr(struct rtattr *rtattr, target_to_host_data_addr_rtattr); } +static void target_to_host_neigh_rtattr(struct rtattr *rtattr, + uint32_t rtattr_len) +{ + target_to_host_for_each_rtattr(rtattr, rtattr_len, + target_to_host_data_neigh_rtattr); +} + static void target_to_host_route_rtattr(struct rtattr *rtattr, uint32_t rtattr_len) { @@ -1459,6 +1598,7 @@ static abi_long target_to_host_data_route(struct nlmsghdr *nlh) struct ifinfomsg *ifi; struct ifaddrmsg *ifa; struct rtmsg *rtm; + struct ndmsg *ndm; switch (nlh->nlmsg_type) { case RTM_NEWLINK: @@ -1485,6 +1625,17 @@ static abi_long target_to_host_data_route(struct nlmsghdr *nlh) NLMSG_LENGTH(sizeof(*ifa))); } break; + case RTM_NEWNEIGH: + case RTM_DELNEIGH: + case RTM_GETNEIGH: + if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ndm))) { + ndm = NLMSG_DATA(nlh); + ndm->ndm_ifindex = tswap32(ndm->ndm_ifindex); + ndm->ndm_state = tswap16(ndm->ndm_state); + target_to_host_neigh_rtattr(NDM_RTA(ndm), nlh->nlmsg_len - + NLMSG_LENGTH(sizeof(*ndm))); + } + break; case RTM_NEWROUTE: case RTM_DELROUTE: case RTM_GETROUTE: diff --git a/linux-user/flat.h b/linux-user/flat.h index ed518e2013b..e374b73e268 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 4331a11bf01..4beb3ed1b96 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -29,13 +29,13 @@ * JAN/99 -- coded full program relocation (gerg@snapgear.com) */ -/* ??? ZFLAT and shared library support is currently disabled. */ - /****************************************************************************/ #include "qemu/osdep.h" #include "qemu.h" +#include "exec/page-protection.h" +#include "exec/mmap-lock.h" #include "user-internals.h" #include "loader.h" #include "user-mmap.h" @@ -64,10 +64,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 +104,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 +117,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 +241,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 @@ -463,7 +277,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 +297,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 +321,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 +458,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 +489,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 +500,15 @@ int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) /* Update data segment pointers for all libraries */ for (i=0; i0; 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; diff --git a/linux-user/gen-vdso-elfn.c.inc b/linux-user/gen-vdso-elfn.c.inc new file mode 100644 index 00000000000..b47019e136c --- /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 00000000000..fce9d5cbc3c --- /dev/null +++ b/linux-user/gen-vdso.c @@ -0,0 +1,228 @@ +/* + * 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 = NULL, *outf = NULL; + long total_len; + const char *prefix = "vdso"; + const char *inf_name; + const char *outf_name = NULL; + unsigned char *buf = NULL; + bool need_bswap; + int ret = EXIT_FAILURE; + + 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; + } + + /* + * 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"); + + ret = EXIT_SUCCESS; + + cleanup: + free(buf); + + if (outf && fclose(outf) != 0) { + ret = EXIT_FAILURE; + } + if (inf && fclose(inf) != 0) { + ret = EXIT_FAILURE; + } + return ret; + + perror_inf: + perror(inf_name); + goto cleanup; + + perror_outf: + perror(outf_name); + goto cleanup; +} diff --git a/linux-user/generic/signal.h b/linux-user/generic/signal.h index 6fd05b77bb3..b34740258ed 100644 --- a/linux-user/generic/signal.h +++ b/linux-user/generic/signal.h @@ -15,7 +15,6 @@ #define TARGET_SA_RESTART 0x10000000 #define TARGET_SA_NODEFER 0x40000000 #define TARGET_SA_RESETHAND 0x80000000 -#define TARGET_SA_RESTORER 0x04000000 #define TARGET_SIGHUP 1 #define TARGET_SIGINT 2 diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index 7f1499ed28b..e18a0183b50 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" #include "internal.h" @@ -42,7 +42,7 @@ void cpu_loop(CPUHexagonState *env) case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ break; - case HEX_EXCP_TRAP0: + case HEX_EVENT_TRAP0: syscallnum = env->gpr[6]; env->gpr[HEX_REG_PC] += 4; ret = do_syscall(env, @@ -60,6 +60,10 @@ void cpu_loop(CPUHexagonState *env) env->gpr[0] = ret; } break; + case HEX_CAUSE_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; @@ -75,7 +79,7 @@ void cpu_loop(CPUHexagonState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { env->gpr[HEX_REG_PC] = regs->sepc; env->gpr[HEX_REG_SP] = regs->sp; diff --git a/linux-user/hexagon/meson.build b/linux-user/hexagon/meson.build new file mode 100644 index 00000000000..d203c3ec929 --- /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 60fa7e1bcee..492b51f1550 100644 --- a/linux-user/hexagon/signal.c +++ b/linux-user/hexagon/signal.c @@ -162,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 diff --git a/linux-user/hexagon/syscall.tbl b/linux-user/hexagon/syscall.tbl new file mode 100644 index 00000000000..845e24eb372 --- /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 b047dbbf6df..00000000000 --- 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 00000000000..ed605c038ee --- /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/hppa/Makefile.vdso b/linux-user/hppa/Makefile.vdso new file mode 100644 index 00000000000..f4537ae7169 --- /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 8ab13351060..9abaad5ef81 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" static abi_ulong hppa_lws(CPUHPPAState *env) @@ -99,6 +99,8 @@ static abi_ulong hppa_lws(CPUHPPAState *env) #endif } break; + default: + g_assert_not_reached(); } break; } @@ -110,7 +112,7 @@ static abi_ulong hppa_lws(CPUHPPAState *env) void cpu_loop(CPUHPPAState *env) { CPUState *cs = env_cpu(env); - abi_ulong ret; + abi_ulong ret, si_code = 0; int trapnr; while (1) { @@ -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: @@ -170,11 +169,18 @@ void cpu_loop(CPUHPPAState *env) force_sig_fault(TARGET_SIGFPE, TARGET_FPE_CONDTRAP, env->iaoq_f); break; case EXCP_ASSIST: - force_sig_fault(TARGET_SIGFPE, 0, env->iaoq_f); + #define set_si_code(mask, val) \ + if (env->fr[0] & mask) { si_code = val; } + set_si_code(R_FPSR_FLG_I_MASK, TARGET_FPE_FLTRES); + set_si_code(R_FPSR_FLG_U_MASK, TARGET_FPE_FLTUND); + set_si_code(R_FPSR_FLG_O_MASK, TARGET_FPE_FLTOVF); + set_si_code(R_FPSR_FLG_Z_MASK, TARGET_FPE_FLTDIV); + set_si_code(R_FPSR_FLG_V_MASK, TARGET_FPE_FLTINV); + #undef set_si_code + force_sig_fault(TARGET_SIGFPE, si_code, 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); @@ -190,7 +196,7 @@ void cpu_loop(CPUHPPAState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; for (i = 1; i < 32; i++) { diff --git a/linux-user/hppa/meson.build b/linux-user/hppa/meson.build index 4709508a098..aa2d9a87a64 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 f253a158646..f6f094c9609 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 aabc37f8cae..647f08e67ac 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 ac91a957621..bf1c1d4f30b 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 aacf3e9e024..4b84422a905 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 82b4e9535e1..19cae8bd65d 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/vdso-asmoffset.h b/linux-user/hppa/vdso-asmoffset.h new file mode 100644 index 00000000000..c8b40c03324 --- /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 00000000000..5be14d2f700 --- /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 00000000000..b17ad974f3d --- /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 00000000000..e1ddd70c37e 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 00000000000..95bc616f6d6 --- /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 ef2dcb3d767..d96d5553faf 100644 --- a/linux-user/i386/cpu_loop.c +++ b/linux-user/i386/cpu_loop.c @@ -21,7 +21,7 @@ #include "qemu.h" #include "qemu/timer.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" #include "user-mmap.h" @@ -172,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); @@ -221,6 +222,7 @@ void cpu_loop(CPUX86State *env) 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], @@ -239,6 +241,7 @@ void cpu_loop(CPUX86State *env) #ifdef TARGET_X86_64 case EXCP_SYSCALL: /* 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], @@ -323,12 +326,12 @@ void cpu_loop(CPUX86State *env) static void target_cpu_free(void *obj) { - CPUArchState *env = ((CPUState *)obj)->env_ptr; - target_munmap(env->gdt.base, sizeof(uint64_t) * TARGET_GDT_ENTRIES); + 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) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { CPUState *cpu = env_cpu(env); bool is64 = (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) != 0; diff --git a/linux-user/i386/meson.build b/linux-user/i386/meson.build index ee523019a57..d42fc6cbc90 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 60fa07d6f9c..0f11dba831f 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; + cpu_x86_fxsave(env, fxstate, sizeof(*fxstate)); + __put_user(0, &sw->magic1); +} - /* - * extended_size is the offset from fpstate_addr to right after the end - * of the extended save states. On 32-bit that includes the legacy - * FSAVE area. - */ - uint32_t extended_size = TARGET_FPSTATE_FXSAVE_OFFSET - + xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE; - - /* fxsave_addr must be 64 byte aligned for xsave */ - assert(!(fxsave_addr & 0x3f)); - - /* Zero the header, XSAVE *adds* features to an existing save state. */ - memset(fxsave->xfeatures, 0, 64); - cpu_x86_xsave(env, fxsave_addr); - __put_user(TARGET_FP_XSTATE_MAGIC1, &fxsave->sw_reserved.magic1); - __put_user(extended_size, &fxsave->sw_reserved.extended_size); - __put_user(env->xcr0, &fxsave->sw_reserved.xfeatures); - __put_user(xstate_size, &fxsave->sw_reserved.xstate_size); - __put_user(TARGET_FP_XSTATE_MAGIC2, (uint32_t *) &fxsave->xfeatures[xfeatures_size]); - } +static void 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(struct target_sigcontext *sc, - struct target_fpstate *fpstate, CPUX86State *env, abi_ulong mask, - abi_ulong fpstate_addr) +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; - - frame_addr = get_sigframe(ka, env, TARGET_SIGFRAME_FXSAVE_OFFSET); + struct target_fregs_state *fpstate; + X86LegacyXSaveArea *fxstate; + unsigned total_size; + FPStateKind fpkind; + + 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; - - frame_addr = get_sigframe(ka, env, TARGET_RT_SIGFRAME_FXSAVE_OFFSET); + X86LegacyXSaveArea *fxstate; + struct target_fregs_state *fpstate; + unsigned total_size; + FPStateKind fpkind; + + 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) +/* + * Restore a signal frame. + */ + +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; + } + + /* + * 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, fxstate, sizeof(*fxstate)); + return true; +} + +#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) { - if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) { - uint32_t extended_size = tswapl(fxsave->sw_reserved.extended_size); - uint32_t xstate_size = tswapl(fxsave->sw_reserved.xstate_size); - uint32_t xfeatures_size = xstate_size - TARGET_FXSAVE_SIZE; - - /* Linux checks MAGIC2 using xstate_size, not extended_size. */ - if (tswapl(fxsave->sw_reserved.magic1) == TARGET_FP_XSTATE_MAGIC1 && - extended_size >= TARGET_FPSTATE_FXSAVE_OFFSET + xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE) { - if (!access_ok(env_cpu(env), VERIFY_READ, fxsave_addr, - extended_size - TARGET_FPSTATE_FXSAVE_OFFSET)) { - return 1; - } - if (tswapl(*(uint32_t *) &fxsave->xfeatures[xfeatures_size]) == TARGET_FP_XSTATE_MAGIC2) { - cpu_x86_xrstor(env, fxsave_addr); - return 0; - } + switch (fpkind) { + case FPSTATE_XSAVE: + if (!xrstor_sigcontext(env, fpkind, fxstate, fxstate_addr)) { + return false; } - /* fall through to fxrstor */ + break; + case FPSTATE_FXSAVE: + cpu_x86_fxrstor(env, fxstate, sizeof(*fxstate)); + break; + case FPSTATE_FSAVE: + break; + default: + g_assert_not_reached(); } - cpu_x86_fxrstor(env, fxsave_addr); - return 0; + /* + * 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 int -restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) +static bool restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) { - int err = 1; - abi_ulong fpstate_addr; - unsigned int tmpflags; + 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; - } + if (fpstate_addr == 0) { + return true; + } + + fpkind = get_fpstate_kind(env); + math_size = get_fpstate_size(env, fpkind); #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); + if (fpkind != FPSTATE_FSAVE) { + math_size += sizeof(struct target_fregs_state); + } #endif - unlock_user_struct(fpstate, fpstate_addr, 0); - } else { - err = 0; + fpstate = lock_user(VERIFY_READ, fpstate_addr, math_size, 1); + if (!fpstate) { + return false; } - return err; +#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 */ + 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(i = 1; i < TARGET_NSIG_WORDS; i++) { + 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 4bbc267fb36..534c74b14fa 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 b2eca96db7e..938a793d2a8 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_signal.h b/linux-user/i386/target_signal.h index 9315cba241c..eee792ef637 100644 --- a/linux-user/i386/target_signal.h +++ b/linux-user/i386/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #define TARGET_ARCH_HAS_SETUP_FRAME #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 diff --git a/linux-user/i386/vdso-asmoffset.h b/linux-user/i386/vdso-asmoffset.h new file mode 100644 index 00000000000..4e5ee0dd49f --- /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 00000000000..e7a1f333a1e --- /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 00000000000..326b7a8f98b --- /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 00000000000..bdece5dfcf8 Binary files /dev/null and b/linux-user/i386/vdso.so differ diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 071f7ca2537..3b41128fd73 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 745cce70abf..37f132be4af 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 (retval < 4) { + return -ENOEXEC; + } + 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); + } 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; - } + } else { + return -ENOEXEC; } - - if (retval >= 0) { - /* success. Initialize important registers */ - do_init_thread(regs, infop); + 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 324e5c872af..e102e6f4108 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); @@ -61,7 +103,7 @@ uint32_t get_elf_hwcap(void); const char *elf_hwcap_str(uint32_t bit); #endif #if defined(TARGET_AARCH64) || defined(TARGET_ARM) -uint32_t get_elf_hwcap2(void); +uint64_t get_elf_hwcap2(void); const char *elf_hwcap2_str(uint32_t bit); #endif diff --git a/linux-user/loongarch64/Makefile.vdso b/linux-user/loongarch64/Makefile.vdso new file mode 100644 index 00000000000..1d760b1e47e --- /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 894fdd111a8..ec8a06c88c7 100644 --- a/linux-user/loongarch64/cpu_loop.c +++ b/linux-user/loongarch64/cpu_loop.c @@ -8,9 +8,15 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" +/* Break codes */ +enum { + BRK_OVERFLOW = 6, + BRK_DIVZERO = 7 +}; + void cpu_loop(CPULoongArchState *env) { CPUState *cs = env_cpu(env); @@ -66,12 +72,42 @@ void cpu_loop(CPULoongArchState *env) force_sig_fault(TARGET_SIGFPE, si_code, env->pc); break; case EXCP_DEBUG: - case EXCCODE_BRK: force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); break; + case EXCCODE_BRK: + { + unsigned int opcode; + + get_user_u32(opcode, env->pc); + + switch (opcode & 0x7fff) { + case BRK_OVERFLOW: + force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTOVF, env->pc); + break; + case BRK_DIVZERO: + force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTDIV, env->pc); + break; + default: + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); + } + } + break; case EXCCODE_BCE: force_sig_fault(TARGET_SIGSYS, TARGET_SI_KERNEL, env->pc); break; + + /* + * Begin with LSX and LASX disabled, then enable on the first trap. + * In this way we can tell if the unit is in use. This is used to + * choose the layout of any signal frame. + */ + case EXCCODE_SXD: + env->CSR_EUEN |= R_CSR_EUEN_SXE_MASK; + break; + case EXCCODE_ASXD: + env->CSR_EUEN |= R_CSR_EUEN_ASXE_MASK; + break; + case EXCP_ATOMIC: cpu_exec_step_atomic(cs); break; @@ -84,7 +120,7 @@ void cpu_loop(CPULoongArchState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; diff --git a/linux-user/loongarch64/meson.build b/linux-user/loongarch64/meson.build new file mode 100644 index 00000000000..64cb537bf9e --- /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 bb8efb11729..1a322f96973 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].vreg.D(0), &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) { + if (extctx->lasx.haddr) { + struct target_lasx_context *lasx_ctx = + extctx->lasx.haddr + sizeof(struct target_sctx_info); + + for (i = 0; i < 32; ++i) { + __get_user(env->fpr[i].vreg.UD(0), &lasx_ctx->regs[4 * i]); + __get_user(env->fpr[i].vreg.UD(1), &lasx_ctx->regs[4 * i + 1]); + __get_user(env->fpr[i].vreg.UD(2), &lasx_ctx->regs[4 * i + 2]); + __get_user(env->fpr[i].vreg.UD(3), &lasx_ctx->regs[4 * i + 3]); + } + __get_user(fcc, &lasx_ctx->fcc); + write_fcc(env, fcc); + __get_user(env->fcsr0, &lasx_ctx->fcsr); + restore_fp_status(env); + } else if (extctx->lsx.haddr) { + struct target_lsx_context *lsx_ctx = + extctx->lsx.haddr + sizeof(struct target_sctx_info); + + for (i = 0; i < 32; ++i) { + __get_user(env->fpr[i].vreg.UD(0), &lsx_ctx->regs[2 * i]); + __get_user(env->fpr[i].vreg.UD(1), &lsx_ctx->regs[2 * i + 1]); + } + __get_user(fcc, &lsx_ctx->fcc); + write_fcc(env, fcc); + __get_user(env->fcsr0, &lsx_ctx->fcsr); + restore_fp_status(env); + } else if (extctx->fpu.haddr) { struct target_fpu_context *fpu_ctx = extctx->fpu.haddr + sizeof(struct target_sctx_info); - uint64_t fcc; for (i = 0; i < 32; ++i) { - __get_user(env->fpr[i].vreg.D(0), &fpu_ctx->regs[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 00000000000..845e24eb372 --- /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 be00915adf2..00000000000 --- 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 00000000000..3d8a993b424 --- /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_syscall.h b/linux-user/loongarch64/target_syscall.h index 8b5de521243..39f229bb9c8 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 00000000000..60d113822f3 --- /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 00000000000..780a5fda12f --- /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 00000000000..682446ed0cb --- /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 00000000000..7c2de6c50e7 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 caead1cb741..5da91b997ae 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" void cpu_loop(CPUM68KState *env) @@ -92,10 +92,10 @@ void cpu_loop(CPUM68KState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, 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 5f35354487b..77555781aa8 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 79c2d24c89d..b6094f8933b 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 eeb4d01d34d..39b11dd05e2 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 c3f288dfe83..4b40c09a8d6 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_signal.h b/linux-user/m68k/target_signal.h index 6e0f4b74e39..b05b9303b0d 100644 --- a/linux-user/m68k/target_signal.h +++ b/linux-user/m68k/target_signal.h @@ -3,6 +3,7 @@ #include "../generic/signal.h" +#define TARGET_ARCH_HAS_SA_RESTORER 1 #define TARGET_ARCH_HAS_SETUP_FRAME #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 diff --git a/linux-user/main.c b/linux-user/main.c index 96be354897d..5ac5b55dc65 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -38,23 +38,25 @@ #include "qemu/help_option.h" #include "qemu/module.h" #include "qemu/plugin.h" -#include "exec/exec-all.h" +#include "user/guest-base.h" +#include "user/page-protection.h" #include "exec/gdbstub.h" #include "gdbstub/user.h" -#include "tcg/tcg.h" +#include "tcg/startup.h" #include "qemu/timer.h" #include "qemu/envlist.h" #include "qemu/guest-random.h" #include "elf.h" #include "trace/control.h" #include "target_elf.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "crypto/init.h" #include "fd-trans.h" #include "signal-common.h" #include "loader.h" #include "user-mmap.h" -#include "accel/tcg/perf.h" +#include "tcg/perf.h" +#include "exec/page-vary.h" #ifdef CONFIG_SEMIHOSTING #include "semihosting/semihost.h" @@ -69,6 +71,7 @@ char *exec_path; char real_exec_path[PATH_MAX]; static bool opt_one_insn_per_tb; +static unsigned long opt_tb_size; static const char *argv0; static const char *gdbstub; static envlist_t *envlist; @@ -119,6 +122,7 @@ static const char *last_log_filename; #endif unsigned long reserved_va; +unsigned long guest_addr_max; static void usage(int exitcode); @@ -144,10 +148,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) { @@ -156,14 +163,15 @@ 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); + get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id(); } else { cpu_list_unlock(); } + 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 @@ -229,7 +237,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 */ @@ -332,11 +340,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); } } @@ -406,11 +414,25 @@ static void handle_arg_reserved_va(const char *arg) reserved_va = val ? val - 1 : 0; } +static const char *rtsig_map = CONFIG_QEMU_RTSIG_MAP; + +static void handle_arg_rtsig_map(const char *arg) +{ + rtsig_map = arg; +} + static void handle_arg_one_insn_per_tb(const char *arg) { opt_one_insn_per_tb = true; } +static void handle_arg_tb_size(const char *arg) +{ + if (qemu_strtoul(arg, NULL, 0, &opt_tb_size)) { + usage(EXIT_FAILURE); + } +} + static void handle_arg_strace(const char *arg) { enable_strace = true; @@ -488,6 +510,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)"}, @@ -496,12 +521,12 @@ 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'"}, + "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"}, - {"singlestep", "QEMU_SINGLESTEP", false, handle_arg_one_insn_per_tb, - "", "deprecated synonym for -one-insn-per-tb"}, + {"tb-size", "QEMU_TB_SIZE", true, handle_arg_tb_size, + "size", "TCG translation block cache size"}, {"strace", "QEMU_STRACE", false, handle_arg_strace, "", "log system calls"}, {"seed", "QEMU_RAND_SEED", true, handle_arg_seed, @@ -682,6 +707,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; @@ -750,8 +776,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)); @@ -783,7 +810,7 @@ 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 */ { AccelState *accel = current_accel(); AccelClass *ac = ACCEL_GET_CLASS(accel); @@ -791,23 +818,35 @@ int main(int argc, char **argv, char **envp) 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. + * 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) % qemu_host_page_size) { - char *s = size_to_str(qemu_host_page_size); + 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); @@ -820,6 +859,13 @@ int main(int argc, char **argv, char **envp) /* MAX_RESERVED_VA + 1 is a large power of 2, so is aligned. */ reserved_va = max_reserved_va; } + if (reserved_va != 0) { + guest_addr_max = reserved_va; + } else if (MIN(TARGET_VIRT_ADDR_SPACE_BITS, TARGET_ABI_BITS) <= 32) { + guest_addr_max = UINT32_MAX; + } else { + guest_addr_max = ~0ul; + } /* * Temporarily disable @@ -828,6 +874,7 @@ int main(int argc, char **argv, char **envp) */ #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. @@ -891,7 +938,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); } @@ -904,7 +951,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); @@ -914,11 +961,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 @@ -989,22 +1032,17 @@ 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); if (gdbstub) { - if (gdbserver_start(gdbstub) < 0) { - fprintf(stderr, "qemu: could not open gdbserver on %s\n", - gdbstub); - exit(EXIT_FAILURE); - } - gdb_handlesig(cpu, 0); + gdbserver_start(gdbstub, &error_fatal); } #ifdef CONFIG_SEMIHOSTING diff --git a/linux-user/meson.build b/linux-user/meson.build index 7171dc60be2..f47a213ca3d 100644 --- a/linux-user/meson.build +++ b/linux-user/meson.build @@ -27,19 +27,29 @@ 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')) - +linux_user_ss.add(when: 'CONFIG_TCG_PLUGINS', if_true: files('plugin-api.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 212e62d0a62..87236c166f2 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" void cpu_loop(CPUMBState *env) @@ -127,7 +127,7 @@ void cpu_loop(CPUMBState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { env->regs[0] = regs->r0; env->regs[1] = regs->r1; diff --git a/linux-user/microblaze/signal.c b/linux-user/microblaze/signal.c index 5188d740252..f6d47d76ff6 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 b11395a20c2..e3b643870e8 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 f55dce8a624..b42b6691546 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_signal.h b/linux-user/microblaze/target_signal.h index 7dc5c45f00a..ffe4442213a 100644 --- a/linux-user/microblaze/target_signal.h +++ b/linux-user/microblaze/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 #endif /* MICROBLAZE_TARGET_SIGNAL_H */ diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 8735e58bada..6405806eb02 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" #include "elf.h" #include "internal.h" @@ -180,7 +180,9 @@ void cpu_loop(CPUMIPSState *env) } 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. */ @@ -209,10 +211,10 @@ void cpu_loop(CPUMIPSState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, 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 58a9d7a8a32..d69a5d73ddd 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 a6a2c5c566c..780c0a8a495 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 d560c467a8c..360055c626b 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 761e3e47dda..cd7043ef5a8 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 b965e86b2bf..71a32315a85 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -12,9 +12,6 @@ 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"; } diff --git a/linux-user/mips64/syscall_n32.tbl b/linux-user/mips64/syscall_n32.tbl index 9220909526f..793eca6635c 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 9cd1c34f31b..ebff531accb 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 ed5a45165a4..a4339b2041b 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 5f2f2df29f7..502af9d2781 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/mmap.c b/linux-user/mmap.c index 8eaf57b208b..002e1e668e6 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -20,12 +20,21 @@ #include #include "trace.h" #include "exec/log.h" +#include "exec/page-protection.h" +#include "exec/mmap-lock.h" +#include "exec/tb-flush.h" +#include "exec/translation-block.h" #include "qemu.h" +#include "user/page-protection.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; @@ -113,7 +122,7 @@ static void shm_region_rm_complete(abi_ptr start, abi_ptr last) 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; + int page_flags = (prot & PAGE_RWX) | PAGE_VALID; #ifdef TARGET_AARCH64 { @@ -161,6 +170,7 @@ static int target_to_host_prot(int prot) /* NOTE: all the constants are the HOST ones, but addresses are target. */ int target_mprotect(abi_ulong start, abi_ulong len, int target_prot) { + int host_page_size = qemu_real_host_page_size(); abi_ulong starts[3]; abi_ulong lens[3]; int prots[3]; @@ -185,13 +195,13 @@ int target_mprotect(abi_ulong start, abi_ulong len, int target_prot) } last = start + len - 1; - host_start = start & qemu_host_page_mask; - host_last = HOST_PAGE_ALIGN(last) - 1; + host_start = start & -host_page_size; + host_last = ROUND_UP(last, host_page_size) - 1; nranges = 0; mmap_lock(); - if (host_last - host_start < qemu_host_page_size) { + 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) { @@ -201,7 +211,7 @@ int target_mprotect(abi_ulong start, abi_ulong len, int target_prot) prot1 |= page_get_flags(a + 1); } starts[nranges] = host_start; - lens[nranges] = qemu_host_page_size; + lens[nranges] = host_page_size; prots[nranges] = prot1; nranges++; } else { @@ -214,10 +224,10 @@ int target_mprotect(abi_ulong start, abi_ulong len, int target_prot) /* If the resulting sum differs, create a new range. */ if (prot1 != target_prot) { starts[nranges] = host_start; - lens[nranges] = qemu_host_page_size; + lens[nranges] = host_page_size; prots[nranges] = prot1; nranges++; - host_start += qemu_host_page_size; + host_start += host_page_size; } } @@ -229,9 +239,9 @@ int target_mprotect(abi_ulong start, abi_ulong len, int target_prot) } /* If the resulting sum differs, create a new range. */ if (prot1 != target_prot) { - host_last -= qemu_host_page_size; + host_last -= host_page_size; starts[nranges] = host_last + 1; - lens[nranges] = qemu_host_page_size; + lens[nranges] = host_page_size; prots[nranges] = prot1; nranges++; } @@ -262,10 +272,69 @@ int target_mprotect(abi_ulong start, abi_ulong len, int target_prot) return ret; } -/* map an incomplete host page */ +/* + * 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) +{ + 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; + } + return munmap(addr, len); +} + +/* + * 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. + * + * 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; @@ -282,7 +351,7 @@ static bool mmap_frag(abi_ulong real_start, abi_ulong start, abi_ulong last, return false; } - real_last = real_start + qemu_host_page_size - 1; + real_last = real_start + host_page_size - 1; host_start = g2h_untagged(real_start); /* Get the protection of the target pages outside the mapping. */ @@ -300,12 +369,12 @@ static bool mmap_frag(abi_ulong real_start, abi_ulong start, abi_ulong last, * 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, qemu_host_page_size, + 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) { - munmap(p, qemu_host_page_size); + do_munmap(p, host_page_size); errno = EEXIST; } return false; @@ -320,21 +389,20 @@ static bool mmap_frag(abi_ulong real_start, abi_ulong start, abi_ulong last, /* Adjust protection to be able to write. */ if (!(host_prot_old & PROT_WRITE)) { host_prot_old |= PROT_WRITE; - mprotect(host_start, qemu_host_page_size, host_prot_old); + mprotect(host_start, host_page_size, host_prot_old); } /* Read or zero the new guest pages. */ if (flags & MAP_ANONYMOUS) { memset(g2h_untagged(start), 0, last - start + 1); - } else { - if (pread(fd, g2h_untagged(start), last - start + 1, offset) == -1) { - return false; - } + } 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, qemu_host_page_size, host_prot_new); + mprotect(host_start, host_page_size, host_prot_new); } return true; } @@ -369,21 +437,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); @@ -484,302 +552,474 @@ 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, off_t 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, last, real_start, real_last, retaddr, host_len; - abi_ulong passthrough_start = -1, passthrough_last = 0; - int page_flags; - off_t host_offset; - - mmap_lock(); - trace_target_mmap(start, len, target_prot, flags, fd, offset); - - if (!len) { - errno = EINVAL; - goto fail; + if (flags & MAP_ANONYMOUS) { + page_flags |= PAGE_ANON; } - - page_flags = validate_prot_to_pageflags(target_prot); - if (!page_flags) { - errno = EINVAL; - goto fail; + page_flags |= PAGE_RESET; + if (passthrough_start > passthrough_last) { + page_set_flags(start, last, page_flags); + } else { + if (start < passthrough_start) { + page_set_flags(start, passthrough_start - 1, page_flags); + } + page_set_flags(passthrough_start, passthrough_last, + page_flags | PAGE_PASSTHROUGH); + if (passthrough_last < last) { + page_set_flags(passthrough_last + 1, last, page_flags); + } } - - /* Also check for overflows... */ - len = TARGET_PAGE_ALIGN(len); - if (!len) { - errno = ENOMEM; - goto fail; + shm_region_rm_complete(start, last); + trace_target_mmap_complete(start); + if (qemu_loglevel_mask(CPU_LOG_PAGE)) { + FILE *f = qemu_log_trylock(); + if (f) { + fprintf(f, "page layout changed following mmap\n"); + page_dump(f); + qemu_log_unlock(f); + } } + return start; +} - if (offset & ~TARGET_PAGE_MASK) { - errno = EINVAL; - goto fail; +/* + * 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); } - /* - * 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); - } + 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; } - real_start = start & qemu_host_page_mask; - host_offset = offset & qemu_host_page_mask; + start = h2g(p); + last = start + len - 1; + return mmap_end(start, last, start, last, flags, page_flags); +} - /* - * 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 | MAP_FIXED_NOREPLACE))) { - 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; - } +/* + * 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 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); } - /* - * 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)) { + if (!(flags & MAP_ANONYMOUS)) { struct stat sb; if (fstat(fd, &sb) == -1) { - goto fail; + return -1; } - - /* Are we trying to create a map beyond EOF?. */ - if (offset + len > sb.st_size) { + 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) { /* - * If so, truncate the file map at eof aligned with - * the hosts real pagesize. Additional anonymous maps - * will be created beyond EOF. + * A portion of the map is beyond the end of the file. + * Truncate the file portion of the allocation. */ - len = REAL_HOST_PAGE_ALIGN(sb.st_size - offset); + fileend_adj = offset + len - sb.st_size; } } - if (!(flags & (MAP_FIXED | MAP_FIXED_NOREPLACE))) { - uintptr_t host_start; - int host_prot; - void *p; + 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; + } - host_len = len + offset - host_offset; - host_len = HOST_PAGE_ALIGN(host_len); - host_prot = target_to_host_prot(target_prot); + 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; /* - * Note: we prefer to control the mapping address. It is - * especially important if qemu_host_page_size > - * qemu_real_host_page_size. + * Take care to align the host memory. Perform a larger anonymous + * allocation and extract the aligned portion. Remap the file on + * top of that. */ - p = mmap(g2h_untagged(start), host_len, host_prot, - flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0); + 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) { - goto fail; + 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; } - /* update start so that it points to the file position at 'offset' */ - host_start = (uintptr_t)p; + if (len < host_len) { + do_munmap(p + len, host_len - len); + } + 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; + 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; } - host_start += offset - host_offset; } - start = h2g(host_start); - last = start + len - 1; - passthrough_start = start; - passthrough_last = last; + + start = h2g(p); + } + + last = start + len - 1; + if (fileend_adj) { + pass_last = ROUND_UP(last - fileend_adj, host_page_size) - 1; } else { - if (start & ~TARGET_PAGE_MASK) { - errno = EINVAL; - goto fail; + 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; - real_last = HOST_PAGE_ALIGN(last) - 1; + return mmap_end(start, last, start, last, flags, page_flags); + } + + if (!(flags & MAP_ANONYMOUS)) { + misaligned_offset = (start ^ offset) & (host_page_size - 1); /* - * 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. + * The fallback for misalignment is a private mapping + read. + * This carries none of semantics required of MAP_SHARED. */ - if (last < start || !guest_range_valid_untagged(start, len)) { - errno = ENOMEM; - goto fail; + if (misaligned_offset && (flags & MAP_TYPE) != MAP_PRIVATE) { + errno = EINVAL; + return -1; } + } - if (flags & MAP_FIXED_NOREPLACE) { - /* Validate that the chosen range is empty. */ - if (!page_check_range_empty(start, last)) { - errno = EEXIST; - goto fail; - } + last = start + len - 1; + real_start = start & -host_page_size; + real_last = ROUND_UP(last, host_page_size) - 1; - /* - * With reserved_va, the entire address space is mmaped in the - * host to ensure it isn't accidentally used for something else. - * We have just checked that the guest address is not mapped - * within the guest, but need to replace the host reservation. - * - * Without reserved_va, despite the guest address check above, - * keep MAP_FIXED_NOREPLACE so that the guest does not overwrite - * any host address mappings. - */ - if (reserved_va) { - flags = (flags & ~MAP_FIXED_NOREPLACE) | MAP_FIXED; + /* + * 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); } - /* - * 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 - && (target_prot & PROT_WRITE)) { - errno = EINVAL; - goto fail; - } - retaddr = target_mmap(start, len, target_prot | PROT_WRITE, - (flags & (MAP_FIXED | MAP_FIXED_NOREPLACE)) - | MAP_PRIVATE | MAP_ANONYMOUS, - -1, 0); - if (retaddr == -1) { - goto fail; - } - if (pread(fd, g2h_untagged(start), len, offset) == -1) { - goto fail; - } - if (!(target_prot & PROT_WRITE)) { - ret = target_mprotect(start, len, target_prot); - assert(ret == 0); - } - goto the_end; + if (!mmap_frag(real_start, start, real_page_last, target_prot, + flags, fd, offset)) { + return -1; } + real_start = real_page_last + 1; + } - /* handle the start of the mapping */ - if (start > real_start) { - if (real_last == real_start + qemu_host_page_size - 1) { - /* one single host page */ - if (!mmap_frag(real_start, start, last, - target_prot, flags, fd, offset)) { - goto fail; - } - goto the_end1; - } - if (!mmap_frag(real_start, start, - real_start + qemu_host_page_size - 1, - target_prot, flags, fd, offset)) { - goto fail; - } - real_start += qemu_host_page_size; + 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; } - /* handle the end of the mapping */ - if (last < real_last) { - abi_ulong real_page = real_last - qemu_host_page_size + 1; - if (!mmap_frag(real_page, real_page, last, - target_prot, flags, fd, - offset + real_page - start)) { - goto fail; - } - real_last -= qemu_host_page_size; + 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; + } - /* map the middle (easier) */ - if (real_start < real_last) { - void *p, *want_p; - off_t offset1; - size_t len1; + 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); + } + } - if (flags & MAP_ANONYMOUS) { - offset1 = 0; - } else { - offset1 = offset + real_start - start; + 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; } - len1 = real_last - real_start + 1; - want_p = g2h_untagged(real_start); - - p = mmap(want_p, len1, target_to_host_prot(target_prot), - flags, fd, offset1); - if (p != want_p) { - if (p != MAP_FAILED) { - munmap(p, len1); - errno = EEXIST; - } - goto fail; + 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; } - passthrough_start = real_start; - passthrough_last = real_last; + start += offset - host_offset; + flags |= MAP_FIXED; } } - the_end1: - if (flags & MAP_ANONYMOUS) { - page_flags |= PAGE_ANON; - } - page_flags |= PAGE_RESET; - if (passthrough_start > passthrough_last) { - page_set_flags(start, last, page_flags); + + 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 { - if (start < passthrough_start) { - page_set_flags(start, passthrough_start - 1, page_flags); + 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; } - page_set_flags(passthrough_start, passthrough_last, - page_flags | PAGE_PASSTHROUGH); - if (passthrough_last < last) { - page_set_flags(passthrough_last + 1, last, page_flags); + if (!guest_range_valid_untagged(start, len)) { + errno = ENOMEM; + return -1; } } - shm_region_rm_complete(start, last); - the_end: - trace_target_mmap_complete(start); - if (qemu_loglevel_mask(CPU_LOG_PAGE)) { - FILE *f = qemu_log_trylock(); - if (f) { - fprintf(f, "page layout changed following mmap\n"); - page_dump(f); - qemu_log_unlock(f); + + 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); } } - mmap_unlock(); - return start; -fail: - mmap_unlock(); - return -1; + + return ret; } -static void mmap_reserve_or_unmap(abi_ulong start, abi_ulong len) +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_last; abi_ulong real_len; @@ -789,8 +1029,8 @@ static void mmap_reserve_or_unmap(abi_ulong start, abi_ulong len) int prot; last = start + len - 1; - real_start = start & qemu_host_page_mask; - real_last = HOST_PAGE_ALIGN(last) - 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, @@ -798,7 +1038,7 @@ static void mmap_reserve_or_unmap(abi_ulong start, abi_ulong len) * The single page special case is required for the last page, * lest real_start overflow to zero. */ - if (real_last - real_start < qemu_host_page_size) { + if (real_last - real_start < host_page_size) { prot = 0; for (a = real_start; a < start; a += TARGET_PAGE_SIZE) { prot |= page_get_flags(a); @@ -807,61 +1047,59 @@ static void mmap_reserve_or_unmap(abi_ulong start, abi_ulong len) prot |= page_get_flags(a + 1); } if (prot != 0) { - return; + 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 += qemu_host_page_size; + 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 -= qemu_host_page_size; + real_last -= host_page_size; } if (real_last < real_start) { - return; + return 0; } } real_len = real_last - real_start + 1; host_start = g2h_untagged(real_start); - if (reserved_va) { - void *ptr = mmap(host_start, real_len, PROT_NONE, - MAP_FIXED | MAP_ANONYMOUS - | MAP_PRIVATE | MAP_NORESERVE, -1, 0); - assert(ptr == host_start); - } else { - int ret = munmap(host_start, real_len); - assert(ret == 0); - } + return do_munmap(host_start, real_len); } int target_munmap(abi_ulong start, abi_ulong len) { + int ret; + trace_target_munmap(start, len); if (start & ~TARGET_PAGE_MASK) { - return -TARGET_EINVAL; + 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(); - mmap_reserve_or_unmap(start, len); - page_set_flags(start, start + len - 1, 0); - shm_region_rm_complete(start, start + len - 1); + 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 0; + return ret; } abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, @@ -910,16 +1148,16 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong 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); @@ -1047,69 +1285,161 @@ static inline abi_ulong target_shmlba(CPUArchState *cpu_env) } #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); - abi_ulong raddr; struct shmid_ds shm_info; int ret; - abi_ulong shmlba; + int h_pagesize; + int t_shmlba, h_shmlba, m_shmlba; + size_t t_len, h_len, m_len; /* shmat pointers are always untagged */ - /* find out the length of the shared memory segment */ + /* + * 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); - 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)) { + if (!guest_range_valid_untagged(shmaddr, m_len)) { return -TARGET_EINVAL; } WITH_MMAP_LOCK_GUARD() { - void *host_raddr; + bool mapped = false; + void *want, *test; abi_ulong last; - if (shmaddr) { - host_raddr = shmat(shmid, (void *)g2h_untagged(shmaddr), shmflg); + 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 { - abi_ulong mmap_start; + if (!page_check_range_empty(shmaddr, shmaddr + m_len - 1)) { + return -TARGET_EINVAL; + } + } - /* 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)); + /* All placement is now complete. */ + want = (void *)g2h_untagged(shmaddr); - if (mmap_start == -1) { - return -TARGET_ENOMEM; + /* + * 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; } - host_raddr = shmat(shmid, g2h_untagged(mmap_start), - shmflg | SHM_REMAP); + mapped = true; } - if (host_raddr == (void *)-1) { - return get_errno(-1); + 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; } - raddr = h2g(host_raddr); - last = raddr + shm_info.shm_segsz - 1; + assert(test == want); - page_set_flags(raddr, last, + last = shmaddr + m_len - 1; + page_set_flags(shmaddr, last, PAGE_VALID | PAGE_RESET | PAGE_READ | - (shmflg & SHM_RDONLY ? 0 : PAGE_WRITE)); + (shmflg & SHM_RDONLY ? 0 : PAGE_WRITE) | + (shmflg & SHM_EXEC ? PAGE_EXEC : 0)); - shm_region_rm_complete(raddr, last); - shm_region_add(raddr, last); + shm_region_rm_complete(shmaddr, last); + shm_region_add(shmaddr, last); } /* @@ -1118,12 +1448,20 @@ abi_ulong target_shmat(CPUArchState *cpu_env, int shmid, * 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; + if (!tcg_cflags_has(cpu, CF_PARALLEL)) { + tcg_cflags_set(cpu, CF_PARALLEL); tb_flush(cpu); } - return raddr; + 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) diff --git a/linux-user/nios2/cpu_loop.c b/linux-user/nios2/cpu_loop.c deleted file mode 100644 index da77ede76bd..00000000000 --- 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 32b3dc99c6e..00000000000 --- 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 0e4c8f012d7..00000000000 --- 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 11a37b32e8b..00000000000 --- 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 830b4c0741c..00000000000 --- 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 801e20afafa..00000000000 --- 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 28120013e24..00000000000 --- 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 714583215df..00000000000 --- 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 ab16ad4f03c..00000000000 --- a/linux-user/nios2/target_mman.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * arch/nios2/include/asm/processor.h: - * TASK_UNMAPPED_BASE PAGE_ALIGN(TASK_SIZE / 3) - * TASK_SIZE 0x7FFF0000UL - */ -#define TASK_UNMAPPED_BASE TARGET_PAGE_ALIGN(0x7FFF0000 / 3) - -/* arch/nios2/include/asm/elf.h */ -#define ELF_ET_DYN_BASE 0xD0000000 - -#include "../generic/target_mman.h" diff --git a/linux-user/nios2/target_prctl.h b/linux-user/nios2/target_prctl.h deleted file mode 100644 index eb53b31ad55..00000000000 --- a/linux-user/nios2/target_prctl.h +++ /dev/null @@ -1 +0,0 @@ -/* No special prctl support required. */ diff --git a/linux-user/nios2/target_proc.h b/linux-user/nios2/target_proc.h deleted file mode 100644 index 43fe29ca723..00000000000 --- a/linux-user/nios2/target_proc.h +++ /dev/null @@ -1 +0,0 @@ -/* No target-specific /proc support */ diff --git a/linux-user/nios2/target_resource.h b/linux-user/nios2/target_resource.h deleted file mode 100644 index 227259594c0..00000000000 --- 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 46ca5948cea..00000000000 --- 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 3a06f373c35..00000000000 --- 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 561b28d2816..00000000000 --- 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 b1d4f4fedbc..00000000000 --- a/linux-user/nios2/termbits.h +++ /dev/null @@ -1 +0,0 @@ -#include "../generic/termbits.h" diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c index a7aa586c8f9..306b4f8eb43 100644 --- a/linux-user/openrisc/cpu_loop.c +++ b/linux-user/openrisc/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" void cpu_loop(CPUOpenRISCState *env) @@ -83,7 +83,7 @@ void cpu_loop(CPUOpenRISCState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; diff --git a/linux-user/openrisc/meson.build b/linux-user/openrisc/meson.build new file mode 100644 index 00000000000..273e7a0c38a --- /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 be8b68784a2..cb74a9fe5e2 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 00000000000..845e24eb372 --- /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 f7faddb54c5..00000000000 --- 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 00000000000..047e9f77c75 --- /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/plugin-api.c b/linux-user/plugin-api.c new file mode 100644 index 00000000000..66755df5263 --- /dev/null +++ b/linux-user/plugin-api.c @@ -0,0 +1,15 @@ +/* + * QEMU Plugin API - linux-user-mode only implementations + * + * Common user-mode only APIs are in plugins/api-user. These helpers + * are only specific to linux-user. + * + * Copyright (C) 2017, Emilio G. Cota + * Copyright (C) 2019-2025, Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "common-user/plugin-api.c.inc" diff --git a/linux-user/ppc/Makefile.vdso b/linux-user/ppc/Makefile.vdso new file mode 100644 index 00000000000..e2b8facbb50 --- /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/cpu_loop.c b/linux-user/ppc/cpu_loop.c index 02204ad8beb..2a0efaffcd6 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -21,7 +21,7 @@ #include "qemu.h" #include "qemu/timer.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" static inline uint64_t cpu_ppc_get_tb(CPUPPCState *env) @@ -378,7 +378,7 @@ void cpu_loop(CPUPPCState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; diff --git a/linux-user/ppc/meson.build b/linux-user/ppc/meson.build index 19fead7bc83..80cacae396b 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 a616f20efbd..24e5a02a782 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) @@ -471,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); @@ -487,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]); } @@ -612,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); @@ -658,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 8f052ff4058..4b428a43ccd 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 6c44e0eaad2..6e8b93d673c 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_signal.h b/linux-user/ppc/target_signal.h index 5be24e152b7..53fae473f3c 100644 --- a/linux-user/ppc/target_signal.h +++ b/linux-user/ppc/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #if !defined(TARGET_PPC64) #define TARGET_ARCH_HAS_SETUP_FRAME #endif diff --git a/linux-user/ppc/vdso-32.ld b/linux-user/ppc/vdso-32.ld new file mode 100644 index 00000000000..69626965407 --- /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 00000000000..0dc55e0dddf 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 00000000000..a55c65ed543 --- /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 00000000000..ac1ab2582e4 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 00000000000..424abb4290b 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 00000000000..6844c8c81cf --- /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 00000000000..2e79ea98086 --- /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 12f638336ab..0b19fa43e65 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -2,14 +2,14 @@ #define QEMU_H #include "cpu.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" -#undef DEBUG_REMAP - -#include "exec/user/abitypes.h" +#include "user/abitypes.h" +#include "user/page-protection.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 @@ -32,6 +32,7 @@ struct image_info { abi_ulong brk; abi_ulong start_stack; abi_ulong stack_limit; + abi_ulong vdso; abi_ulong entry; abi_ulong code_offset; abi_ulong data_offset; @@ -44,7 +45,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. */ @@ -96,7 +96,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 @@ -113,6 +113,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 @@ -159,7 +163,7 @@ 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, @@ -313,6 +317,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 @@ -326,7 +339,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) { @@ -349,4 +362,7 @@ void *lock_user_string(abi_ulong guest_addr); #define unlock_user_struct(host_ptr, guest_addr, copy) \ unlock_user(host_ptr, guest_addr, (copy) ? sizeof(*host_ptr) : 0) +/* Clone cpu state */ +CPUArchState *cpu_copy(CPUArchState *env); + #endif /* QEMU_H */ diff --git a/linux-user/riscv/Makefile.vdso b/linux-user/riscv/Makefile.vdso new file mode 100644 index 00000000000..2c257dbfda5 --- /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 bffca7db127..3ac8bbfec1f 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -21,7 +21,7 @@ #include "qemu/error-report.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" #include "elf.h" #include "semihosting/common-semi.h" @@ -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; @@ -94,10 +94,10 @@ void cpu_loop(CPURISCVState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, 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 00000000000..b2e7df0f4f6 --- /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 f989f7f51f3..358fa1d82d2 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,6 +38,8 @@ 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 { abi_ulong uc_flags; abi_ptr uc_link; @@ -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 00000000000..845e24eb372 --- /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 412e58e5b2f..00000000000 --- a/linux-user/riscv/syscall32_nr.h +++ /dev/null @@ -1,308 +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_riscv_hwprobe (TARGET_NR_arch_specific_syscall + 14) -#define TARGET_NR_prlimit64 261 -#define TARGET_NR_fanotify_init 262 -#define TARGET_NR_fanotify_mark 263 -#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 29e1eb20753..00000000000 --- a/linux-user/riscv/syscall64_nr.h +++ /dev/null @@ -1,314 +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_riscv_hwprobe (TARGET_NR_arch_specific_syscall + 14) -#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 0a5a2f2fb16..00000000000 --- 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 00000000000..4069dc59b68 --- /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 9dd65652ee4..dedd5956f3a 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/vdso-32.so b/linux-user/riscv/vdso-32.so new file mode 100755 index 00000000000..c2ce2a47579 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 00000000000..ae49f5b043b 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 00000000000..123902ef61c --- /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 00000000000..c37275233a0 --- /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 00000000000..aabe2b0ab36 --- /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 00000000000..e82bf9e29ff --- /dev/null +++ b/linux-user/s390x/Makefile.vdso @@ -0,0 +1,11 @@ +include $(BUILD_DIR)/tests/tcg/s390x-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/s390x +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso.so + +$(SUBDIR)/vdso.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ -nostdlib -shared -Wl,-h,linux-vdso64.so.1 \ + -Wl,--build-id=sha1 -Wl,--hash-style=both \ + -Wl,-T,$(SUBDIR)/vdso.ld $< diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c index 8b7ac2879ef..c9124444ed2 100644 --- a/linux-user/s390x/cpu_loop.c +++ b/linux-user/s390x/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" @@ -180,7 +180,7 @@ void cpu_loop(CPUS390XState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; for (i = 0; i < 16; i++) { diff --git a/linux-user/s390x/meson.build b/linux-user/s390x/meson.build index 0781ccea1dc..a7a25ed9ce6 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 f72165576f3..df49c247080 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 0690263df1d..8e0d1f1c2aa 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 85a99c48dee..ac22d422b0b 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_signal.h b/linux-user/s390x/target_signal.h index 41e0e34a55d..738e0673f4a 100644 --- a/linux-user/s390x/target_signal.h +++ b/linux-user/s390x/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #define TARGET_ARCH_HAS_SETUP_FRAME #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 diff --git a/linux-user/s390x/vdso-asmoffset.h b/linux-user/s390x/vdso-asmoffset.h new file mode 100644 index 00000000000..27a062d6c1a --- /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 00000000000..33324924771 --- /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 00000000000..d3f1d1b1645 --- /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 00000000000..64130f6f335 Binary files /dev/null and b/linux-user/s390x/vdso.so differ diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c index c805f9db110..ee9eff3428a 100644 --- a/linux-user/sh4/cpu_loop.c +++ b/linux-user/sh4/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" void cpu_loop(CPUSH4State *env) @@ -81,7 +81,7 @@ void cpu_loop(CPUSH4State *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c index c4ba962708d..9ecc026fae7 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 0b91499ebdc..cf4ec0493d0 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 080790556ad..cb3a5de7110 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_signal.h b/linux-user/sh4/target_signal.h index eee6a1a7cda..0bde417fd1f 100644 --- a/linux-user/sh4/target_signal.h +++ b/linux-user/sh4/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #define TARGET_ARCH_HAS_SETUP_FRAME #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 diff --git a/linux-user/sh4/termbits.h b/linux-user/sh4/termbits.h index eeabd2d7a9c..28e79f2c9a1 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 3e2dc604c2f..196d2406f86 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,12 +56,11 @@ 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); void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo); -int target_to_host_signal(int sig); int host_to_target_signal(int sig); long do_sigreturn(CPUArchState *env); long do_rt_sigreturn(CPUArchState *env); @@ -113,7 +110,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 748a98f3e53..cd0e7398aa4 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -18,8 +18,10 @@ */ #include "qemu/osdep.h" #include "qemu/bitops.h" +#include "qemu/cutils.h" #include "gdbstub/user.h" -#include "hw/core/tcg-cpu-ops.h" +#include "exec/page-protection.h" +#include "accel/tcg/cpu-ops.h" #include #include @@ -31,7 +33,14 @@ #include "trace.h" #include "signal-common.h" #include "host-signal.h" +#include "user/cpu_loop.h" +#include "user/page-protection.h" #include "user/safe-syscall.h" +#include "user/signal.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 +52,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 +64,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 +71,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 +180,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 +202,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 +245,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 +254,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 +262,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 +273,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 +283,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 +292,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 +417,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 +500,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 +517,147 @@ static int core_dump_signal(int sig) } } -static void signal_table_init(void) +int host_interrupt_signal; + +static void signal_table_init(const char *rtsig_map) { - int host_sig, target_sig, count; + int hsig, tsig, count; + + if (rtsig_map) { + /* + * Map host RT signals to target RT signals according to the + * user-provided specification. + */ + const char *s = rtsig_map; + + while (true) { + int i; + + 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; + } + } + } 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 two signals for internal usage (see below). + */ + + hsig = SIGRTMIN + 2; + for (tsig = TARGET_SIGRTMIN; + hsig <= SIGRTMAX && tsig <= TARGET_NSIG; + hsig++, tsig++) { + host_to_target_signal_table[hsig] = tsig; + } + } /* - * 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. + * Remap the target SIGABRT, so that we can distinguish host abort + * from guest abort. When the guest registers a signal handler or + * calls raise(SIGABRT), the host will raise SIG_RTn. If the guest + * arrives at dump_core_and_abort(), we will map back to host SIGABRT + * so that the parent (native or emulated) sees the correct signal. + * Finally, also map host to guest SIGABRT so that the emulated + * parent sees the correct mapping from wait status. */ - for (host_sig = SIGRTMIN; host_sig <= SIGRTMAX; host_sig++) { - target_sig = host_sig - SIGRTMIN + TARGET_SIGRTMIN; - if (target_sig <= TARGET_NSIG) { - host_to_target_signal_table[host_sig] = target_sig; + + host_to_target_signal_table[SIGABRT] = 0; + for (hsig = SIGRTMIN; hsig <= SIGRTMAX; hsig++) { + if (!host_to_target_signal_table[hsig]) { + if (host_interrupt_signal) { + host_to_target_signal_table[hsig] = TARGET_SIGABRT; + break; + } else { + host_interrupt_signal = hsig; + } } } - - /* generate signal conversion tables */ - for (target_sig = 1; target_sig <= TARGET_NSIG; target_sig++) { - target_to_host_signal_table[target_sig] = _NSIG; /* poison */ + if (hsig > SIGRTMAX) { + fprintf(stderr, + "No rt signals left for interrupt and SIGABRT mapping\n"); + exit(EXIT_FAILURE); } - 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; + + /* 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; } } - 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++; - } + 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; } - trace_signal_table_init(count); } + 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,28 +665,39 @@ 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; } + + sigaction(host_interrupt_signal, &act, NULL); } /* Force a synchronously taken signal. The kernel force_sig() function @@ -618,7 +707,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 +714,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 +724,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 @@ -663,10 +750,10 @@ void force_sigsegv(int oldsig) } #endif -void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, +void cpu_loop_exit_sigsegv(CPUState *cpu, vaddr 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->cc->tcg_ops; if (tcg_ops->record_sigsegv) { tcg_ops->record_sigsegv(cpu, addr, access_type, maperr, ra); @@ -679,10 +766,10 @@ void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, cpu_loop_exit_restore(cpu, ra); } -void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, +void cpu_loop_exit_sigbus(CPUState *cpu, vaddr addr, MMUAccessType access_type, uintptr_t ra) { - const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; if (tcg_ops->record_sigbus) { tcg_ops->record_sigbus(cpu, addr, access_type, ra); @@ -695,15 +782,45 @@ void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, /* abort execution with signal */ static G_NORETURN -void dump_core_and_abort(CPUArchState *cpu_env, int target_sig) +void die_with_signal(int host_sig) { - CPUState *cpu = thread_cpu; - CPUArchState *env = cpu->env_ptr; - TaskState *ts = (TaskState *)cpu->opaque; + struct sigaction act = { + .sa_handler = SIG_DFL, + }; + + /* + * 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; - struct sigaction act; - host_sig = target_to_host_signal(target_sig); + /* 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,30 +841,8 @@ void dump_core_and_abort(CPUArchState *cpu_env, int target_sig) target_sig, strsignal(host_sig), "core dumped" ); } - preexit_cleanup(cpu_env, 128 + 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 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 @@ -756,7 +851,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); @@ -781,72 +876,201 @@ static inline void rewind_if_in_safe_syscall(void *puc) } } -static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) +static G_NORETURN +void die_from_signal(siginfo_t *info) { - CPUArchState *env = thread_cpu->env_ptr; - CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; - 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); + 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; /* - * Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special - * handling wrt signal blocking and unwinding. + * Convert forcefully to guest address space: addresses outside + * reserved_va are still valid to report via SEGV_MAPERR. */ - 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; + 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; + } - host_addr = (uintptr_t)info->si_addr; + /* + * 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) { /* - * Convert forcefully to guest address space: addresses outside - * reserved_va are still valid to report via SEGV_MAPERR. + * With reserved_va, the whole address space is PROT_NONE, + * which means that we may get ACCERR when we want MAPERR. */ - guest_addr = h2g_nocheck(host_addr); + if (page_get_flags(guest_addr) & PAGE_VALID) { + maperr = false; + } else { + info->si_code = SEGV_MAPERR; + } + } - pc = host_signal_pc(uc); - is_write = host_signal_write(info, uc); - access_type = adjust_signal_pc(&pc, is_write); + sigprocmask(SIG_SETMASK, host_signal_mask(uc), NULL); + cpu_loop_exit_sigsegv(cpu, guest_addr, access_type, maperr, pc); +} - if (host_sig == SIGSEGV) { - bool maperr = true; +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 (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; - } + /* + * 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); + } - /* - * 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; - } - } + 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, 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); - } - } + sigprocmask(SIG_SETMASK, host_signal_mask(uc), NULL); + cpu_loop_exit_sigbus(cpu, guest_addr, access_type, pc); + } + return pc; +} - sync_sig = true; +static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) +{ + 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; + + if (host_sig == host_interrupt_signal) { + ts->signal_pending = 1; + cpu_exit(thread_cpu); + return; + } + + /* + * Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special + * handling wrt signal blocking and unwinding. Non-spoofed SIGILL, + * SIGFPE, SIGTRAP are always host bugs. + */ + 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); + } } /* get target signal number */ @@ -887,6 +1111,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); @@ -942,7 +1167,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; @@ -1002,22 +1226,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); } @@ -1031,15 +1260,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; @@ -1049,7 +1290,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) { @@ -1115,7 +1356,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; @@ -1175,7 +1416,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 b36bb2574b9..68f1e8ecd43 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" #define SPARC64_STACK_BIAS 2047 @@ -197,10 +197,8 @@ static uint32_t do_getpsr(CPUSPARCState *env) /* Avoid ifdefs below for the abi32 and abi64 paths. */ #ifdef TARGET_ABI32 #define TARGET_TT_SYSCALL (TT_TRAP + 0x10) /* t_linux */ -#define syscall_cc psr #else #define TARGET_TT_SYSCALL (TT_TRAP + 0x6d) /* tl0_linux64 */ -#define syscall_cc xcc #endif /* Avoid ifdefs below for the v9 and pre-v9 hw traps. */ @@ -224,11 +222,6 @@ 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) { case TARGET_TT_SYSCALL: ret = do_syscall (env, env->gregs[1], @@ -240,10 +233,10 @@ void cpu_loop (CPUSPARCState *env) break; } if ((abi_ulong)ret >= (abi_ulong)(-515)) { - env->syscall_cc |= PSR_CARRY; + set_syscall_C(env, 1); ret = -ret; } else { - env->syscall_cc &= ~PSR_CARRY; + set_syscall_C(env, 0); } env->regwptr[0] = ret; /* next instruction */ @@ -300,7 +293,7 @@ void cpu_loop (CPUSPARCState *env) case TT_FP_EXCP: { int code = TARGET_FPE_FLTUNK; - target_ulong fsr = env->fsr; + target_ulong fsr = cpu_get_fsr(env); if ((fsr & FSR_FTT_MASK) == FSR_FTT_IEEE_EXCP) { if (fsr & FSR_NVC) { @@ -364,7 +357,7 @@ void cpu_loop (CPUSPARCState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; env->pc = regs->pc; diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index 2be9000b9e0..8181b8b92c1 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); @@ -544,11 +546,6 @@ void setup_sigtramp(abi_ulong sigtramp_page) 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, @@ -662,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 @@ -690,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); diff --git a/linux-user/sparc/syscall.tbl b/linux-user/sparc/syscall.tbl index e34cc30ef22..3bc83783b78 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 34a99dc8326..938a02bb481 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 1f4bed50f47..5f62c5eb755 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_proc.h b/linux-user/sparc/target_proc.h index 3bb3134a477..744fa107309 100644 --- a/linux-user/sparc/target_proc.h +++ b/linux-user/sparc/target_proc.h @@ -8,7 +8,25 @@ static int open_cpuinfo(CPUArchState *cpu_env, int fd) { - dprintf(fd, "type\t\t: sun4u\n"); + int i, num_cpus; + const char *cpu_type; + + num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + if (cpu_env->def.features & CPU_FEATURE_HYPV) { + cpu_type = "sun4v"; + } else { + cpu_type = "sun4u"; + } + + dprintf(fd, "cpu\t\t: %s (QEMU)\n", cpu_env->def.name); + dprintf(fd, "type\t\t: %s\n", cpu_type); + dprintf(fd, "ncpus probed\t: %d\n", num_cpus); + dprintf(fd, "ncpus active\t: %d\n", num_cpus); + dprintf(fd, "State:\n"); + for (i = 0; i < num_cpus; i++) { + dprintf(fd, "CPU%d:\t\t: online\n", i); + } + return 0; } #define HAVE_ARCH_PROC_CPUINFO diff --git a/linux-user/sparc/target_syscall.h b/linux-user/sparc/target_syscall.h index be77e44eb86..e4211653574 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 e0ab8046ec1..3b744ccd4a7 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" @@ -158,19 +161,20 @@ static const char * const target_signal_name[] = { }; static void -print_signal(abi_ulong arg, int last) +print_signal_1(abi_ulong arg) { - const char *signal_name = NULL; - if (arg < ARRAY_SIZE(target_signal_name)) { - signal_name = target_signal_name[arg]; + qemu_log("%s", target_signal_name[arg]); + } else { + qemu_log(TARGET_ABI_FMT_lu, arg); } +} - if (signal_name == NULL) { - print_raw_param("%ld", arg, last); - return; - } - qemu_log("%s%s", signal_name, get_comma(last)); +static void +print_signal(abi_ulong arg, int last) +{ + print_signal_1(arg); + qemu_log("%s", get_comma(last)); } static void print_si_code(int arg) @@ -367,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: { @@ -384,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: { @@ -415,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; } @@ -430,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 @@ -658,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, @@ -669,7 +671,26 @@ 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_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) +{ + static const struct flags shmat_flags[] = { + FLAG_GENERIC(SHM_RND), + FLAG_GENERIC(SHM_REMAP), + FLAG_GENERIC(SHM_RDONLY), + FLAG_GENERIC(SHM_EXEC), + FLAG_END + }; + + 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 static void @@ -679,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(" @@ -696,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 */ @@ -1044,6 +1112,18 @@ UNUSED static const struct flags open_flags[] = { FLAG_END, }; +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), @@ -1636,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. @@ -2723,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(")"); } @@ -2742,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(")"); @@ -3103,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 @@ -3241,11 +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_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 @@ -3464,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, @@ -3804,7 +3971,7 @@ print_mmap(CPUArchState *cpu_env, const struct syscallname *name, { return print_mmap_both(cpu_env, name, arg0, arg1, arg2, arg3, arg4, arg5, -#if defined(TARGET_NR_mmap2) +#ifdef TARGET_ARCH_WANT_SYS_OLD_MMAP true #else false @@ -4149,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 */ @@ -4177,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 6655d4f26d6..fdf94ef32ad 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -715,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 @@ -1135,7 +1138,7 @@ { 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 }, @@ -1186,7 +1189,8 @@ { 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 }, @@ -1285,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 }, @@ -1398,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 }, @@ -1659,13 +1663,15 @@ { TARGET_NR_vserver, "vserver" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_wait4 -{ TARGET_NR_wait4, "wait4" , "%s(%d,%p,%d,%p)", 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 }, diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 3521a2d70b0..fc37028597c 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -23,7 +23,12 @@ #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 "exec/mmap-lock.h" +#include "exec/tb-flush.h" +#include "exec/translation-block.h" #include #include #include @@ -52,7 +57,6 @@ #include #include #include -//#include #include #include #include @@ -134,15 +138,16 @@ #include "signal-common.h" #include "loader.h" #include "user-mmap.h" +#include "user/page-protection.h" #include "user/safe-syscall.h" +#include "user/signal.h" #include "qemu/guest-random.h" #include "qemu/selfmap.h" #include "user/syscall-trace.h" #include "special-errno.h" #include "qapi/error.h" #include "fd-trans.h" -#include "tcg/tcg.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #ifndef CLONE_IO #define CLONE_IO 0x80000000 /* Clone io context */ @@ -358,7 +363,8 @@ _syscall3(int, sys_sched_getaffinity, pid_t, pid, unsigned int, len, #define __NR_sys_sched_setaffinity __NR_sched_setaffinity _syscall3(int, sys_sched_setaffinity, pid_t, pid, unsigned int, len, unsigned long *, user_mask_ptr); -/* sched_attr is not defined in glibc */ +/* sched_attr is not defined in glibc < 2.41 */ +#ifndef SCHED_ATTR_SIZE_VER0 struct sched_attr { uint32_t size; uint32_t sched_policy; @@ -371,6 +377,7 @@ struct sched_attr { uint32_t sched_util_min; uint32_t sched_util_max; }; +#endif #define __NR_sys_sched_getattr __NR_sched_getattr _syscall4(int, sys_sched_getattr, pid_t, pid, struct sched_attr *, attr, unsigned int, size, unsigned int, flags); @@ -601,6 +608,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) \ { \ @@ -652,6 +686,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) @@ -758,10 +796,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__) @@ -831,7 +867,7 @@ abi_long do_brk(abi_ulong brk_val) return target_brk; } - /* Release heap if necesary */ + /* Release heap if necessary */ if (new_brk < old_brk) { target_munmap(new_brk, old_brk - new_brk); @@ -1615,24 +1651,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) @@ -1814,7 +1832,7 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, *dst = tswap32(*dst); } } else { - qemu_log_mask(LOG_UNIMP, "Unsupported ancillary data: %d/%d\n", + qemu_log_mask(LOG_UNIMP, "Unsupported target ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type); memcpy(data, target_data, len); } @@ -1985,6 +2003,16 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, (void *) &errh->offender, sizeof(errh->offender)); break; } + case IP_PKTINFO: + { + struct in_pktinfo *pkti = data; + struct target_in_pktinfo *target_pi = target_data; + + __put_user(pkti->ipi_ifindex, &target_pi->ipi_ifindex); + target_pi->ipi_spec_dst.s_addr = pkti->ipi_spec_dst.s_addr; + target_pi->ipi_addr.s_addr = pkti->ipi_addr.s_addr; + break; + } default: goto unimplemented; } @@ -2036,7 +2064,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, default: unimplemented: - qemu_log_mask(LOG_UNIMP, "Unsupported ancillary data: %d/%d\n", + qemu_log_mask(LOG_UNIMP, "Unsupported host ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type); memcpy(target_data, data, MIN(len, tgt_len)); if (tgt_len > len) { @@ -2067,8 +2095,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: @@ -2109,21 +2135,50 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, } ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val))); break; + case IP_MULTICAST_IF: case IP_ADD_MEMBERSHIP: case IP_DROP_MEMBERSHIP: - if (optlen < sizeof (struct target_ip_mreq) || - optlen > sizeof (struct target_ip_mreqn)) + { + struct ip_mreqn ip_mreq; + struct target_ip_mreqn *target_smreqn; + int min_size; + + QEMU_BUILD_BUG_ON(sizeof(struct ip_mreq) != + sizeof(struct target_ip_mreq)); + + if (optname == IP_MULTICAST_IF) { + min_size = sizeof(struct in_addr); + } else { + min_size = sizeof(struct target_ip_mreq); + } + if (optlen < min_size || + 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; + if (optlen >= sizeof(struct target_ip_mreq)) { + ip_mreq.imr_address.s_addr = target_smreqn->imr_address.s_addr; + if (optlen >= sizeof(struct target_ip_mreqn)) { + __put_user(target_smreqn->imr_ifindex, &ip_mreq.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; @@ -2134,7 +2189,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; } @@ -2277,18 +2332,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: @@ -2305,12 +2355,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; } @@ -2319,13 +2367,12 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, 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; @@ -5050,8 +5097,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++) { @@ -5059,7 +5106,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; @@ -5099,7 +5146,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) { @@ -5111,7 +5158,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; @@ -5127,8 +5174,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++) { @@ -5139,7 +5186,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; @@ -5167,8 +5214,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; @@ -5179,7 +5226,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; @@ -6287,15 +6334,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 @@ -6450,16 +6488,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: @@ -6515,7 +6565,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) @@ -6557,7 +6607,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; @@ -6577,8 +6627,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); } @@ -6669,7 +6719,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 @@ -6680,7 +6730,7 @@ 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) @@ -6700,8 +6750,8 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, } #endif put_user_u32(pid_fd, parent_tidptr); - } - fork_end(0); + } + fork_end(ret); } g_assert(!cpu_in_exclusive_context(cpu)); } @@ -6725,13 +6775,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; @@ -6747,13 +6797,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: @@ -6807,8 +6857,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 @@ -6841,7 +6891,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; @@ -6866,7 +6916,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; @@ -6885,8 +6935,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 { @@ -6897,7 +6947,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; @@ -6922,7 +6972,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; @@ -6942,7 +6992,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; @@ -6967,7 +7017,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; @@ -6988,7 +7038,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; @@ -7001,45 +7051,45 @@ 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: @@ -7203,11 +7253,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) { @@ -7264,7 +7332,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 @@ -7278,7 +7346,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 @@ -7946,7 +8014,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++) { @@ -7992,6 +8060,12 @@ static void open_self_maps_4(const struct open_self_maps_data *d, path = "[stack]"; } else if (start == info->brk) { path = "[heap]"; + } else if (start == info->vdso) { + path = "[vdso]"; +#ifdef TARGET_X86_64 + } else if (start == TARGET_VSYSCALL_PAGE) { + path = "[vsyscall]"; +#endif } /* Except null device (MAP_ANON), adjust offset for this fragment. */ @@ -8061,8 +8135,8 @@ static void open_self_maps_4(const struct open_self_maps_data *d, * 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 int open_self_maps_3(void *opaque, vaddr guest_start, + vaddr guest_end, int flags) { static const MapInfo mi = { .is_priv = true }; @@ -8073,13 +8147,25 @@ static int open_self_maps_3(void *opaque, target_ulong guest_start, /* * 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) +static int open_self_maps_2(void *opaque, vaddr guest_start, + vaddr guest_end, int flags) { const struct open_self_maps_data *d = opaque; uintptr_t host_start = (uintptr_t)g2h_untagged(guest_start); uintptr_t host_last = (uintptr_t)g2h_untagged(guest_end - 1); +#ifdef TARGET_X86_64 + /* + * Because of the extremely high position of the page within the guest + * virtual address space, this is not backed by host memory at all. + * Therefore the loop below would fail. This is the only instance + * of not having host backing memory. + */ + if (guest_start == TARGET_VSYSCALL_PAGE) { + return open_self_maps_3(opaque, guest_start, guest_end, flags); + } +#endif + while (1) { IntervalTreeNode *n = interval_tree_iter_first(d->host_maps, host_start, host_start); @@ -8100,18 +8186,20 @@ static int open_self_maps_2(void *opaque, target_ulong guest_start, static int open_self_maps_1(CPUArchState *env, int fd, bool smaps) { struct open_self_maps_data d = { - .ts = env_cpu(env)->opaque, - .host_maps = read_self_maps(), + .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; } @@ -8128,7 +8216,7 @@ static int open_self_smaps(CPUArchState *cpu_env, int fd) 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; @@ -8147,6 +8235,19 @@ static int open_self_stat(CPUArchState *cpu_env, int fd) } else if (i == 3) { /* ppid */ g_string_printf(buf, FMT_pid " ", getppid()); + } else if (i == 4) { + /* pgid */ + g_string_printf(buf, FMT_pid " ", getpgrp()); + } 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); @@ -8169,7 +8270,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; @@ -8303,8 +8404,9 @@ static int open_net_route(CPUArchState *cpu_env, int fd) } #endif -int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *fname, - int flags, mode_t mode, bool safe) +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; @@ -8341,6 +8443,12 @@ int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *fname, } if (is_proc_myself(pathname, "exe")) { + /* 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 { @@ -8387,6 +8495,17 @@ int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *fname, return fd; } + 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 { @@ -8394,6 +8513,49 @@ int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *fname, } } + +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; @@ -8635,7 +8797,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) { @@ -8688,7 +8850,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; } @@ -8722,7 +8884,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) { @@ -8764,7 +8926,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; } @@ -8790,13 +8952,43 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count) #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_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) @@ -8806,6 +8998,8 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count) #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; @@ -8853,11 +9047,75 @@ static void risc_hwprobe_fill_pairs(CPURISCVState *env, 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; @@ -8865,35 +9123,38 @@ static void risc_hwprobe_fill_pairs(CPURISCVState *env, } } -static int cpu_set_valid(abi_long arg3, abi_long arg4) +/* + * If the cpumask_t of (target_cpus, cpusetsize) cannot be read: -EFAULT. + * If the cpumast_t has no bits set: -EINVAL. + * Otherwise the cpumask_t contains some bit set: 0. + * Unlike the kernel, we do not mask cpumask_t by the set of online cpus, + * nor bound the search by cpumask_size(). + */ +static int nonempty_cpu_set(abi_ulong cpusetsize, abi_ptr target_cpus) { - 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); + unsigned char *p = lock_user(VERIFY_READ, target_cpus, cpusetsize, 1); + int ret = -TARGET_EFAULT; - 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; + if (p) { + ret = -TARGET_EINVAL; + /* + * Since we only care about the empty/non-empty state of the cpumask_t + * not the individual bits, we do not need to repartition the bits + * from target abi_ulong to host unsigned long. + * + * Note that the kernel does not round up cpusetsize to a multiple of + * sizeof(abi_ulong). After bounding cpusetsize by cpumask_size(), + * it copies exactly cpusetsize bytes into a zeroed buffer. + */ + for (abi_ulong i = 0; i < cpusetsize; ++i) { + if (p[i]) { + ret = 0; + break; + } } + unlock_user(p, target_cpus, 0); } - return -TARGET_EINVAL; + return ret; } static abi_long do_riscv_hwprobe(CPUArchState *cpu_env, abi_long arg1, @@ -8910,7 +9171,7 @@ static abi_long do_riscv_hwprobe(CPUArchState *cpu_env, abi_long arg1, /* check cpu_set */ if (arg3 != 0) { - ret = cpu_set_valid(arg3, arg4); + ret = nonempty_cpu_set(arg3, arg4); if (ret != 0) { return ret; } @@ -8988,7 +9249,7 @@ 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); if (ts->child_tidptr) { put_user_u32(0, ts->child_tidptr); @@ -9070,6 +9331,9 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, 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); @@ -9146,14 +9410,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; @@ -9415,7 +9689,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 @@ -9981,7 +10255,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; @@ -10345,10 +10619,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; @@ -10382,7 +10653,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 @@ -11177,14 +11448,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)) { @@ -11389,7 +11660,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: @@ -11406,7 +11677,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 @@ -11701,9 +11972,9 @@ 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; @@ -11761,7 +12032,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))); @@ -12033,11 +12304,11 @@ 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: @@ -12097,7 +12368,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 @@ -12266,7 +12537,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; @@ -12339,7 +12610,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) { @@ -12376,7 +12647,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) { @@ -12421,7 +12692,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) { @@ -12466,7 +12737,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) { @@ -12501,19 +12772,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; } @@ -12527,7 +12790,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 @@ -12652,7 +12915,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()); diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 77ba343c850..5d227599924 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -62,7 +62,7 @@ #if (defined(TARGET_I386) && defined(TARGET_ABI32)) \ || (defined(TARGET_ARM) && defined(TARGET_ABI32)) \ || (defined(TARGET_SPARC) && defined(TARGET_ABI32)) \ - || defined(TARGET_M68K) || defined(TARGET_SH4) || defined(TARGET_CRIS) + || defined(TARGET_M68K) || defined(TARGET_SH4) /* 16 bit uid wrappers emulation */ #define USE_UID16 #define target_id uint16_t @@ -71,9 +71,9 @@ #endif #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SH4) \ - || defined(TARGET_M68K) || defined(TARGET_CRIS) \ + || defined(TARGET_M68K) \ || defined(TARGET_S390X) || defined(TARGET_OPENRISC) \ - || defined(TARGET_NIOS2) || defined(TARGET_RISCV) \ + || defined(TARGET_RISCV) \ || defined(TARGET_XTENSA) || defined(TARGET_LOONGARCH64) #define TARGET_IOC_SIZEBITS 14 @@ -462,7 +462,7 @@ typedef struct { abi_ulong sig[TARGET_NSIG_WORDS]; } target_sigset_t; -#ifdef BSWAP_NEEDED +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN static inline void tswap_sigset(target_sigset_t *d, const target_sigset_t *s) { int i; @@ -943,6 +943,10 @@ struct target_rtc_pll_info { #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". @@ -1230,8 +1234,7 @@ struct target_winsize { #include "target_mman.h" #if (defined(TARGET_I386) && defined(TARGET_ABI32)) \ - || (defined(TARGET_ARM) && defined(TARGET_ABI32)) \ - || defined(TARGET_CRIS) + || (defined(TARGET_ARM) && defined(TARGET_ABI32)) #define TARGET_STAT_HAVE_NSEC struct target_stat { abi_ushort st_dev; @@ -1971,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 */ @@ -2082,11 +2085,6 @@ struct target_stat64 { abi_uint target_st_ctime_nsec; abi_ullong st_ino; }; - -#elif defined(TARGET_LOONGARCH64) - -/* LoongArch no newfstatat/fstat syscall. */ - #else #error unsupported CPU #endif @@ -2624,6 +2622,12 @@ struct target_ucred { abi_uint gid; }; +struct target_in_pktinfo { + abi_int ipi_ifindex; + struct target_in_addr ipi_spec_dst; + struct target_in_addr ipi_addr; +}; + typedef abi_int target_timer_t; #define TARGET_SIGEV_MAX_SIZE 64 @@ -2750,4 +2754,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 c3b43f80223..6dd7a80ce54 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 071aad4b5fc..3cd19e79c6c 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 diff --git a/linux-user/uaccess.c b/linux-user/uaccess.c index 425cbf677f7..27e841e6510 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 c63ef45fc78..691b9a1775f 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -18,9 +18,7 @@ #ifndef LINUX_USER_USER_INTERNALS_H #define LINUX_USER_USER_INTERNALS_H -#include "exec/user/thunk.h" -#include "exec/exec-all.h" -#include "exec/tb-flush.h" +#include "user/thunk.h" #include "qemu/log.h" extern char *exec_path; @@ -65,13 +63,12 @@ abi_long do_syscall(CPUArchState *cpu_env, int num, abi_long arg1, abi_long arg5, abi_long arg6, abi_long arg7, abi_long arg8); extern __thread CPUState *thread_cpu; -G_NORETURN void cpu_loop(CPUArchState *env); abi_long get_errno(abi_long ret); const char *target_strerror(int err); int get_osversion(void); void init_qemu_uname_release(void); void fork_start(void); -void fork_end(int child); +void fork_end(pid_t pid); /** * probe_guest_base: @@ -102,7 +99,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); diff --git a/linux-user/user-mmap.h b/linux-user/user-mmap.h index b94bcdcf83c..dfc4477a720 100644 --- a/linux-user/user-mmap.h +++ b/linux-user/user-mmap.h @@ -18,6 +18,8 @@ #ifndef LINUX_USER_USER_MMAP_H #define LINUX_USER_USER_MMAP_H +#include "user/mmap.h" + /* * Guest parameters for the ADDR_COMPAT_LAYOUT personality * (at present this is the only layout supported by QEMU). @@ -39,24 +41,7 @@ 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, 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); -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); diff --git a/linux-user/vm86.c b/linux-user/vm86.c index c2facf3fc2d..5091d53fb84 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 00000000000..26552b66dba --- /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 203af9a60cc..8c60da7a608 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 ce18119ea0d..7093ee21c0d 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 182be52a74f..988256b6c63 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_signal.h b/linux-user/x86_64/target_signal.h index 9d9717406f3..0af100c6611 100644 --- a/linux-user/x86_64/target_signal.h +++ b/linux-user/x86_64/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + /* For x86_64, use of SA_RESTORER is mandatory. */ #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 diff --git a/linux-user/x86_64/vdso.S b/linux-user/x86_64/vdso.S new file mode 100644 index 00000000000..47d16c00ab1 --- /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 00000000000..ca6001cc3c2 --- /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 00000000000..c873d6ea580 Binary files /dev/null and b/linux-user/x86_64/vdso.so differ diff --git a/linux-user/xtensa/cpu_loop.c b/linux-user/xtensa/cpu_loop.c index d51ce053926..c0fcf743e70 100644 --- a/linux-user/xtensa/cpu_loop.c +++ b/linux-user/xtensa/cpu_loop.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" -#include "cpu_loop-common.h" +#include "user/cpu_loop.h" #include "signal-common.h" static void xtensa_rfw(CPUXtensaState *env) @@ -238,7 +238,7 @@ void cpu_loop(CPUXtensaState *env) } } -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { int i; for (i = 0; i < 16; ++i) { diff --git a/linux-user/xtensa/signal.c b/linux-user/xtensa/signal.c index f5fb8b5cbeb..ef8b0c3a279 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,12 +233,14 @@ 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; give_sigsegv: force_sigsegv(sig); - return; } static void restore_sigcontext(CPUXtensaState *env, diff --git a/linux-user/xtensa/syscall.tbl b/linux-user/xtensa/syscall.tbl index fd2f30227d9..735a89b3bd8 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 eef0644c943..dc787fbbfec 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_signal.h b/linux-user/xtensa/target_signal.h index e4b1bea5cb5..8a198bf8ac8 100644 --- a/linux-user/xtensa/target_signal.h +++ b/linux-user/xtensa/target_signal.h @@ -3,6 +3,8 @@ #include "../generic/signal.h" +#define TARGET_SA_RESTORER 0x04000000 + #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 #endif diff --git a/meson.build b/meson.build index 5150a74831b..dbc97bfdf7a 100644 --- a/meson.build +++ b/meson.build @@ -1,35 +1,32 @@ -project('qemu', ['c'], meson_version: '>=0.63.0', +project('qemu', ['c'], meson_version: '>=1.5.0', default_options: ['warning_level=1', 'c_std=gnu11', 'cpp_std=gnu++11', 'b_colorout=auto', 'b_staticpic=false', 'stdsplit=false', 'optimization=2', 'b_pie=true'], version: files('VERSION')) -add_test_setup('quick', exclude_suites: ['slow', 'thorough'], is_default: true) -add_test_setup('slow', exclude_suites: ['thorough'], env: ['G_TEST_SLOW=1', 'SPEED=slow']) -add_test_setup('thorough', env: ['G_TEST_SLOW=1', 'SPEED=thorough']) +meson.add_devenv({ 'MESON_BUILD_ROOT' : meson.project_build_root() }) + +add_test_setup('quick', exclude_suites: ['slow', 'thorough'], is_default: true, + env: ['RUST_BACKTRACE=1']) +add_test_setup('slow', exclude_suites: ['thorough'], + env: ['G_TEST_SLOW=1', 'SPEED=slow', 'RUST_BACKTRACE=1']) +add_test_setup('thorough', + env: ['G_TEST_SLOW=1', 'SPEED=thorough', 'RUST_BACKTRACE=1']) 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') -targetos = host_machine.system() -sh = find_program('sh') +host_os = host_machine.system() config_host = keyval.load(meson.current_build_dir() / 'config-host.mak') -cc = meson.get_compiler('c') -all_languages = ['c'] -if targetos == 'windows' and add_languages('cpp', required: false, native: false) - all_languages += ['cpp'] - cxx = meson.get_compiler('cpp') -endif -if targetos == 'darwin' and \ - add_languages('objc', required: get_option('cocoa'), native: false) - all_languages += ['objc'] - objc = meson.get_compiler('objc') -endif - # Temporary directory used for files created while # configure runs. Since it is in the build directory # we can safely blow away any previous version of it @@ -49,18 +46,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_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux', 'emscripten'] supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv32', 'riscv64', 'x86', 'x86_64', - 'arm', 'aarch64', 'loongarch64', 'mips', 'mips64', 'sparc64'] + 'arm', 'aarch64', 'loongarch64', 'mips', 'mips64', 'sparc64', 'wasm32'] cpu = host_machine.cpu_family() target_dirs = config_host['TARGET_DIRS'].split() + +# type of binaries to build have_linux_user = false have_bsd_user = false have_system = false @@ -70,23 +68,208 @@ foreach target : target_dirs have_system = have_system or target.endswith('-softmmu') endforeach have_user = have_linux_user or have_bsd_user + +############ +# Programs # +############ + +sh = find_program('sh') +python = import('python').find_installation() + +cc = meson.get_compiler('c') +all_languages = ['c'] +if host_os == 'windows' and add_languages('cpp', required: false, native: false) + all_languages += ['cpp'] + cxx = meson.get_compiler('cpp') +endif +if host_os == 'darwin' and \ + add_languages('objc', required: true, native: false) + all_languages += ['objc'] + objc = meson.get_compiler('objc') +endif + +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.77.0') + if get_option('rust').enabled() + error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.77.0') + else + warning('rustc version ' + rustc.version() + ' is unsupported, disabling Rust compilation.') + message('Please upgrade to at least 1.77.0 to use Rust.') + have_rust = false + endif + endif +endif + +if have_rust + rustdoc = find_program('rustdoc', required: get_option('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 + rustc_args = [find_program('scripts/rust/rustc_args.py'), + '--rustc-version', rustc.version(), + '--workspace', meson.project_source_root() / 'rust'] + if get_option('strict_rust_lints') + rustc_args += ['--strict-lints'] + endif + + rustfmt = find_program('rustfmt', required: false) + + rustc_lint_args = run_command(rustc_args, '--lints', + capture: true, check: true).stdout().strip().splitlines() + + # 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_lint_args + + ['--cfg', 'MESON', '-C', 'default-linker-libraries'], + native: false, language: 'rust') + add_project_arguments(rustc_lint_args + ['--cfg', 'MESON'], + native: true, language: 'rust') +endif + +dtrace = not_found +stap = not_found +if 'dtrace' in get_option('trace_backends') + dtrace = find_program('dtrace', required: true) + stap = find_program('stap', required: false) + if stap.found() + # Workaround to avoid dtrace(1) producing a file with 'hidden' symbol + # visibility. Define STAP_SDT_V2 to produce 'default' symbol visibility + # instead. QEMU --enable-modules depends on this because the SystemTap + # semaphores are linked into the main binary and not the module's shared + # object. + add_global_arguments('-DSTAP_SDT_V2', + native: false, language: all_languages) + endif +endif + +if get_option('iasl') == '' + iasl = find_program('iasl', required: false) +else + iasl = find_program(get_option('iasl'), required: true) +endif + +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 +endforeach + +##################### +# Option validation # +##################### + +# Fuzzing +if get_option('fuzzing') and get_option('fuzzing_engine') == '' and \ + not cc.links(''' + #include + #include + int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { return 0; } + ''', + args: ['-Werror', '-fsanitize=fuzzer']) + error('Your compiler does not support -fsanitize=fuzzer') +endif + +# Tracing backends +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(''' + #include + int main(void) { + openlog("qemu", LOG_PID, LOG_DAEMON); + syslog(LOG_INFO, "configure"); + return 0; + }''') + error('syslog is not supported on this system') +endif + +# Miscellaneous Linux-only features +get_option('mpath') \ + .require(host_os == 'linux', error_message: 'Multipath is supported only on Linux') + +multiprocess_allowed = get_option('multiprocess') \ + .require(host_os == 'linux', error_message: 'Multiprocess QEMU is supported only on Linux') \ + .allowed() + +vfio_user_server_allowed = get_option('vfio_user_server') \ + .require(host_os == 'linux', error_message: 'vfio-user server is supported only on Linux') \ + .allowed() + +have_tpm = get_option('tpm') \ + .require(host_os != 'windows', error_message: 'TPM emulation only available on POSIX systems') \ + .allowed() + +# vhost +have_vhost_user = get_option('vhost_user') \ + .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(host_os == 'linux', + error_message: 'vhost-vdpa is only available on Linux').allowed() +have_vhost_kernel = get_option('vhost_kernel') \ + .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, + error_message: 'vhost-crypto requires vhost-user to be enabled').allowed() + +have_vhost = have_vhost_user or have_vhost_vdpa or have_vhost_kernel + +have_vhost_net_user = have_vhost_user and get_option('vhost_net').allowed() +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 + +have_tcg = get_option('tcg').allowed() and (have_system or have_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', 'netbsd', 'openbsd'], + .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(targetos != 'windows', + .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() -have_block = have_system or have_tools -python = import('python').find_installation() +####################################### +# Variables for host and accelerators # +####################################### if cpu not in supported_cpus host_arch = 'unknown' @@ -100,45 +283,53 @@ else host_arch = cpu endif -if cpu in ['x86', 'x86_64'] +if cpu == 'x86' + kvm_targets = ['i386-softmmu'] +elif cpu == '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'] +elif cpu == 'ppc' + kvm_targets = ['ppc-softmmu'] +elif cpu == 'ppc64' kvm_targets = ['ppc-softmmu', 'ppc64-softmmu'] -elif cpu in ['mips', 'mips64'] +elif cpu == 'mips' + kvm_targets = ['mips-softmmu', 'mipsel-softmmu'] +elif cpu == 'mips64' kvm_targets = ['mips-softmmu', 'mipsel-softmmu', 'mips64-softmmu', 'mips64el-softmmu'] -elif cpu in ['riscv32'] +elif cpu == 'riscv32' kvm_targets = ['riscv32-softmmu'] -elif cpu in ['riscv64'] +elif cpu == 'riscv64' kvm_targets = ['riscv64-softmmu'] +elif cpu == 'loongarch64' + kvm_targets = ['loongarch64-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'] - } +if cpu == 'x86' + xen_targets = ['i386-softmmu'] +elif cpu == 'x86_64' + xen_targets = ['i386-softmmu', 'x86_64-softmmu'] +elif cpu == 'arm' + # i386 emulator provides xenpv machine type for multiple architectures + xen_targets = ['i386-softmmu'] +elif cpu == '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 ['x86', 'x86_64', 'arm', 'aarch64'] - # i386 emulator provides xenpv machine type for multiple architectures +if cpu == 'aarch64' accelerator_targets += { - 'CONFIG_XEN': ['i386-softmmu', 'x86_64-softmmu', 'aarch64-softmmu'], + 'CONFIG_HVF': ['aarch64-softmmu'] } -endif -if cpu in ['x86', 'x86_64'] +elif cpu == 'x86_64' accelerator_targets += { 'CONFIG_HVF': ['x86_64-softmmu'], 'CONFIG_NVMM': ['i386-softmmu', 'x86_64-softmmu'], @@ -146,44 +337,6 @@ if cpu in ['x86', 'x86_64'] } endif -modular_tcg = [] -# Darwin does not support references to thread-local variables in modules -if targetos != 'darwin' - modular_tcg = ['i386-softmmu', 'x86_64-softmmu'] -endif - -edk2_targets = [ 'arm-softmmu', 'aarch64-softmmu', 'i386-softmmu', 'x86_64-softmmu' ] -unpack_edk2_blobs = false -foreach target : edk2_targets - if target in target_dirs - bzip2 = find_program('bzip2', required: get_option('install_blobs')) - unpack_edk2_blobs = bzip2.found() - break - endif -endforeach - -dtrace = not_found -stap = not_found -if 'dtrace' in get_option('trace_backends') - dtrace = find_program('dtrace', required: true) - stap = find_program('stap', required: false) - if stap.found() - # Workaround to avoid dtrace(1) producing a file with 'hidden' symbol - # visibility. Define STAP_SDT_V2 to produce 'default' symbol visibility - # instead. QEMU --enable-modules depends on this because the SystemTap - # semaphores are linked into the main binary and not the module's shared - # object. - add_global_arguments('-DSTAP_SDT_V2', - native: false, language: all_languages) - endif -endif - -if get_option('iasl') == '' - iasl = find_program('iasl', required: false) -else - iasl = find_program(get_option('iasl'), required: true) -endif - ################## # Compiler flags # ################## @@ -194,8 +347,8 @@ foreach lang : all_languages # ok elif compiler.get_id() == 'clang' and compiler.compiles(''' #ifdef __apple_build_version__ - # if __clang_major__ < 12 || (__clang_major__ == 12 && __clang_minor__ < 0) - # error You need at least XCode Clang v12.0 to compile QEMU + # 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) @@ -203,8 +356,10 @@ foreach lang : all_languages # endif #endif''') # ok + elif compiler.get_id() == 'emscripten' + # ok else - error('You either need GCC v7.4 or Clang v10.0 (or XCode Clang v12.0) to compile QEMU') + error('You either need GCC v7.4 or Clang v10.0 (or XCode Clang v15.0) to compile QEMU') endif endforeach @@ -220,21 +375,30 @@ qemu_common_flags = [ qemu_cflags = [] qemu_ldflags = [] -if targetos == 'darwin' +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 targetos == 'sunos' +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 targetos == 'haiku' +elif host_os == 'haiku' qemu_common_flags += ['-DB_USE_POSITIVE_POSIX_ERRORS', '-D_BSD_SOURCE', '-fPIC'] +elif host_os == 'windows' + # plugins use delaylib, and clang needs to be used with lld to make it work. + if compiler.get_id() == 'clang' and compiler.get_linker_id() != 'ld.lld' + error('On windows, you need to use lld with clang - use msys2 clang64/clangarm64 env') + 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 @@ -251,14 +415,47 @@ if host_arch == 'i386' and not cc.links(''' sfaa(&val); return val; }''') - qemu_common_flags = ['-march=i486'] + qemu_common_flags + qemu_isa_flags += ['-march=i486'] endif -if get_option('gprof') - qemu_common_flags += ['-p'] - qemu_ldflags += ['-p'] +# 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 @@ -278,7 +475,10 @@ endif # 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') +# +# Emscripten doesn't support -no-pie but meson can't catch the compiler +# warning. So explicitly omit the flag for Emscripten. +if not get_option('b_pie') and host_os != 'emscripten' qemu_common_flags += cc.get_supported_arguments('-fno-pie', '-no-pie') endif @@ -320,10 +520,12 @@ ucontext_probe = ''' # For POSIX prefer ucontext, but it's not always possible. The fallback # is sigcontext. supported_backends = [] -if targetos == 'windows' +if host_os == 'windows' supported_backends += ['windows'] +elif host_os == 'emscripten' + supported_backends += ['wasm'] else - if targetos != 'darwin' and cc.links(ucontext_probe) + if host_os != 'darwin' and cc.links(ucontext_probe) supported_backends += ['ucontext'] endif supported_backends += ['sigaltstack'] @@ -361,24 +563,38 @@ if get_option('safe_stack') and coroutine_backend != 'ucontext' error('SafeStack is only supported with the ucontext coroutine backend') endif -if get_option('sanitizers') +if 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 - # Detect static linking issue with ubsan - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84285 +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_cflags - qemu_ldflags = ['-fsanitize=undefined'] + qemu_ldflags + 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('sanitizers') + 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', @@ -386,7 +602,15 @@ if get_option('tsan') prefix: '#include ') error('Cannot enable TSAN due to missing fiber annotation interface') endif - qemu_cflags = ['-fsanitize=thread'] + qemu_cflags + 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 @@ -394,16 +618,11 @@ endif # 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' +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 -# Exclude --warn-common with TSan to suppress warnings from the TSan libraries. -if targetos != 'sunos' and not get_option('tsan') - qemu_ldflags += cc.get_supported_link_arguments('-Wl,--warn-common') -endif - if get_option('fuzzing') # Specify a filter to only instrument code that is directly related to # virtual-devices. @@ -435,42 +654,125 @@ if get_option('fuzzing') 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 warnings that we want to enable - +# Collect warning flags we want to set, sorted alphabetically warn_flags = [ - '-Wundef', - '-Wwrite-strings', - '-Wmissing-prototypes', - '-Wstrict-prototypes', - '-Wredundant-decls', - '-Wold-style-declaration', - '-Wold-style-definition', - '-Wtype-limits', - '-Wformat-security', - '-Wformat-y2k', - '-Winit-self', - '-Wignored-qualifiers', + # First enable interesting warnings '-Wempty-body', - '-Wnested-externs', '-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-typedef-redefinition', '-Wno-tautological-type-limit-compare', - '-Wno-psabi', - '-Wno-gnu-variable-sized-type-not-at-end', + '-Wno-typedef-redefinition', ] -if targetos != 'darwin' - warn_flags += ['-Wthread-safety'] +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 @@ -479,16 +781,6 @@ if 'cpp' in all_languages qemu_cxxflags = ['-D__STDC_LIMIT_MACROS', '-D__STDC_CONSTANT_MACROS', '-D__STDC_FORMAT_MACROS'] + qemu_cflags endif -# clang does not support glibc + FORTIFY_SOURCE (is it still true?) -if get_option('optimization') != '0' and targetos == 'linux' - if cc.get_id() == 'gcc' - qemu_cflags += ['-U_FORTIFY_SOURCE', '-D_FORTIFY_SOURCE=2'] - endif - if 'cpp' in all_languages and cxx.get_id() == 'gcc' - qemu_cxxflags += ['-U_FORTIFY_SOURCE', '-D_FORTIFY_SOURCE=2'] - endif -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 @@ -499,7 +791,7 @@ 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 targetos == 'linux' +if host_os == 'linux' add_project_arguments('-isystem', meson.current_source_dir() / 'linux-headers', '-isystem', 'linux-headers', language: all_languages) @@ -529,75 +821,10 @@ if sparse.found() '-Wno-non-pointer-null']) endif -########################################### -# Target-specific checks and dependencies # -########################################### - -# Fuzzing -if get_option('fuzzing') and get_option('fuzzing_engine') == '' and \ - not cc.links(''' - #include - #include - int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); - int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { return 0; } - ''', - args: ['-Werror', '-fsanitize=fuzzer']) - error('Your compiler does not support -fsanitize=fuzzer') -endif - -# Tracing backends -if 'ftrace' in get_option('trace_backends') and targetos != 'linux' - error('ftrace is supported only on Linux') -endif -if 'syslog' in get_option('trace_backends') and not cc.compiles(''' - #include - int main(void) { - openlog("qemu", LOG_PID, LOG_DAEMON); - syslog(LOG_INFO, "configure"); - return 0; - }''') - error('syslog is not supported on this system') -endif - -# Miscellaneous Linux-only features -get_option('mpath') \ - .require(targetos == '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') \ - .allowed() - -vfio_user_server_allowed = get_option('vfio_user_server') \ - .require(targetos == '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') \ - .allowed() - -# vhost -have_vhost_user = get_option('vhost_user') \ - .disable_auto_if(targetos != 'linux') \ - .require(targetos != 'windows', - error_message: 'vhost-user is not available on Windows').allowed() -have_vhost_vdpa = get_option('vhost_vdpa') \ - .require(targetos == 'linux', - error_message: 'vhost-vdpa is only available on Linux').allowed() -have_vhost_kernel = get_option('vhost_kernel') \ - .require(targetos == 'linux', - error_message: 'vhost-kernel is only available on Linux').allowed() -have_vhost_user_crypto = get_option('vhost_crypto') \ - .require(have_vhost_user, - error_message: 'vhost-crypto requires vhost-user to be enabled').allowed() - -have_vhost = have_vhost_user or have_vhost_vdpa or have_vhost_kernel +##################################### +# Host-specific libraries and flags # +##################################### -have_vhost_net_user = have_vhost_user and get_option('vhost_net').allowed() -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 libm = cc.find_library('m', required: false) threads = dependency('threads') util = cc.find_library('util', required: false) @@ -606,50 +833,58 @@ socket = [] version_res = [] coref = [] iokit = [] +pvg = not_found emulator_link_args = [] -nvmm =not_found -hvf = not_found midl = not_found widl = not_found pathcch = not_found +synchronization = not_found host_dsosuf = '.so' -if targetos == 'windows' +if host_os == 'windows' midl = find_program('midl', required: false) widl = find_program('widl', required: false) - pathcch = cc.find_library('pathcch') - socket = cc.find_library('ws2_32') - winmm = cc.find_library('winmm') + + # MinGW uses lowercase for library names + pathcch = cc.find_library('pathcch', required: true) + synchronization = cc.find_library('synchronization', required: true) + socket = cc.find_library('ws2_32', required: true) + winmm = cc.find_library('winmm', required: true) win = import('windows') version_res = win.compile_resources('version.rc', depend_files: files('pc-bios/qemu-nsis.ico'), include_directories: include_directories('.')) host_dsosuf = '.dll' -elif targetos == 'darwin' +elif host_os == 'darwin' coref = dependency('appleframeworks', modules: 'CoreFoundation') iokit = dependency('appleframeworks', modules: 'IOKit', required: false) host_dsosuf = '.dylib' -elif targetos == 'sunos' + pvg = dependency('appleframeworks', modules: ['ParavirtualizedGraphics', 'Metal'], + required: get_option('pvg')) +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' - if get_option('tcg').allowed() and target_dirs.length() > 0 +elif host_os == 'openbsd' + if have_tcg # 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 \ @@ -657,6 +892,8 @@ if get_option('whpx').allowed() and targetos == 'windows' accelerators += 'CONFIG_WHPX' endif endif + +hvf = not_found if get_option('hvf').allowed() hvf = dependency('appleframeworks', modules: 'Hypervisor', required: get_option('hvf')) @@ -664,7 +901,9 @@ if get_option('hvf').allowed() accelerators += 'CONFIG_HVF' 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' @@ -672,11 +911,15 @@ if targetos == 'netbsd' endif tcg_arch = host_arch -if get_option('tcg').allowed() +if have_tcg if host_arch == 'unknown' if not get_option('tcg_interpreter') error('Unsupported CPU @0@, try --enable-tcg-interpreter'.format(cpu)) endif + elif host_arch == 'wasm32' + if not get_option('tcg_interpreter') + error('WebAssembly host requires --enable-tcg-interpreter') + endif elif get_option('tcg_interpreter') warning('Use of the TCG interpreter is not recommended on this host') warning('architecture. There is a native TCG execution backend available') @@ -711,13 +954,94 @@ if 'CONFIG_WHPX' not in accelerators and get_option('whpx').enabled() error('WHPX not available on this platform') 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') + 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', + disabler: true), + dependency('xenforeignmemory', required: false, + method: 'pkg-config', + disabler: true), + dependency('xengnttab', required: false, + method: 'pkg-config', + disabler: true), + dependency('xenevtchn', required: false, + method: 'pkg-config', + disabler: true), + dependency('xendevicemodel', required: false, + method: 'pkg-config', + disabler: true), + # optional, no "disabler: true" + dependency('xentoolcore', required: false, + 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' ] + 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' ], + } + xen_deps = {} + foreach ver: xen_tests + # cache the various library tests to avoid polluting the logs + xen_test_deps = [] + foreach l: xen_libs[ver] + if l not in xen_deps + xen_deps += { l: cc.find_library(l, required: false) } + endif + xen_test_deps += xen_deps[l] + endforeach + + # Use -D to pick just one of the test programs in scripts/xen-detect.c + xen_version = ver.split('.') + xen_ctrl_version = xen_version[0] + \ + ('0' + xen_version[1]).substring(-2) + \ + ('0' + xen_version[2]).substring(-2) + if cc.links(files('scripts/xen-detect.c'), + args: '-DCONFIG_XEN_CTRL_INTERFACE_VERSION=' + xen_ctrl_version, + dependencies: xen_test_deps) + xen = declare_dependency(version: ver, dependencies: xen_test_deps) + break + endif + endforeach + endif + if xen.found() + accelerators += 'CONFIG_XEN' + elif get_option('xen').enabled() + error('could not compile and link Xen test program') + endif +endif +have_xen_pci_passthrough = get_option('xen_pci_passthrough') \ + .require(xen.found(), + error_message: 'Xen PCI passthrough requested but Xen not enabled') \ + .require(host_os == 'linux', + error_message: 'Xen PCI passthrough not available on this platform') \ + .require(cpu == 'x86' or cpu == 'x86_64', + error_message: 'Xen PCI passthrough not available on this platform') \ + .allowed() + ################ # Dependencies # ################ # When bumping glib minimum version, please check also whether to increase -# the _WIN32_WINNT setting in osdep.h according to the value from glib -glib_req_ver = '>=2.56.0' +# 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 = [] @@ -733,7 +1057,7 @@ 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 targetos == 'windows' and get_option('prefer_static') +if host_os == 'windows' and get_option('prefer_static') glib_cflags += ['-DGLIB_STATIC_COMPILATION'] endif @@ -757,21 +1081,7 @@ if not cc.compiles(''' You probably need to set PKG_CONFIG_LIBDIR" to point to the right pkg-config files for your build target.''') endif - -# Silence clang warnings triggered by glib < 2.57.2 -if not cc.compiles(''' - #include - typedef struct Foo { - int i; - } Foo; - static void foo_free(Foo *f) - { - g_free(f); - } - G_DEFINE_AUTOPTR_CLEANUP_FUNC(Foo, foo_free) - int main(void) { return 0; }''', dependencies: glib_pc, args: ['-Wunused-function', '-Werror']) - glib_cflags += cc.get_supported_arguments('-Wno-unused-function') -endif + glib = declare_dependency(dependencies: [glib_pc, gmodule], compile_args: glib_cflags, version: glib_pc.version()) @@ -780,6 +1090,9 @@ glib = declare_dependency(dependencies: [glib_pc, gmodule], # 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) @@ -807,7 +1120,7 @@ if not get_option('gio').auto() or have_system gio = not_found endif if gio.found() - gdbus_codegen = find_program(gio.get_variable('gdbus_codegen'), + gdbus_codegen = find_program('gdbus-codegen', required: get_option('gio')) gio_unix = dependency('gio-unix-2.0', required: get_option('gio'), method: 'pkg-config') @@ -828,10 +1141,11 @@ if 'ust' in get_option('trace_backends') method: 'pkg-config') endif pixman = not_found -if have_system or have_tools - pixman = dependency('pixman-1', required: have_system, version:'>=0.21.8', +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 @@ -858,7 +1172,7 @@ endif libnfs = not_found if not get_option('libnfs').auto() or have_block - libnfs = dependency('libnfs', version: '>=1.9.3', + libnfs = dependency('libnfs', version: ['>=1.9.3', '<6.0.0'], required: get_option('libnfs'), method: 'pkg-config') endif @@ -895,7 +1209,8 @@ if get_option('attr').allowed() endif endif -cocoa = dependency('appleframeworks', modules: ['Cocoa', 'CoreVideo'], +cocoa = dependency('appleframeworks', + modules: ['Cocoa', 'CoreVideo', 'QuartzCore'], required: get_option('cocoa')) vmnet = dependency('appleframeworks', modules: 'vmnet', required: get_option('vmnet')) @@ -993,12 +1308,12 @@ if vde.found() and not cc.links(''' endif pulse = not_found -if not get_option('pa').auto() or (targetos == 'linux' and have_system) +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 (targetos == 'linux' and have_system) +if not get_option('alsa').auto() or (host_os == 'linux' and have_system) alsa = dependency('alsa', required: get_option('alsa'), method: 'pkg-config') endif @@ -1008,7 +1323,7 @@ if not get_option('jack').auto() or have_system method: 'pkg-config') endif pipewire = not_found -if not get_option('pipewire').auto() or (targetos == 'linux' and have_system) +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') @@ -1026,7 +1341,11 @@ if not get_option('spice_protocol').auto() or have_system method: 'pkg-config') endif spice = not_found -if not get_option('spice').auto() or have_system +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') @@ -1047,19 +1366,45 @@ if not get_option('zstd').auto() or have_block 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 targetos == 'linux' and pixman.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')) - if virgl.found() - config_host_data.set('HAVE_VIRGL_D3D_INFO_EXT', - cc.has_member('struct virgl_renderer_resource_info_ext', 'd3d_tex2d', - prefix: '#include ', - dependencies: virgl)) - endif +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 @@ -1074,7 +1419,7 @@ if not get_option('curl').auto() or have_block required: get_option('curl')) endif libudev = not_found -if targetos == 'linux' and (have_system or have_tools) +if host_os == 'linux' and (have_system or have_tools) libudev = dependency('libudev', method: 'pkg-config', required: get_option('libudev')) @@ -1082,7 +1427,7 @@ endif mpathlibs = [libudev] mpathpersist = not_found -if targetos == 'linux' and have_tools and get_option('mpath').allowed() +if host_os == 'linux' and have_tools and get_option('mpath').allowed() mpath_test_source = ''' #include #include @@ -1137,7 +1482,7 @@ iconv = not_found curses = not_found if have_system and get_option('curses').allowed() curses_test = ''' - #if defined(__APPLE__) || defined(__OpenBSD__) + #ifdef __APPLE__ #define _XOPEN_SOURCE_EXTENDED 1 #endif #include @@ -1153,7 +1498,7 @@ if have_system and get_option('curses').allowed() return 0; }''' - curses_dep_list = targetos == 'windows' ? ['ncurses', 'ncursesw'] : ['ncursesw'] + curses_dep_list = host_os == 'windows' ? ['ncurses', 'ncursesw'] : ['ncursesw'] curses = dependency(curses_dep_list, required: false, method: 'pkg-config') @@ -1170,13 +1515,13 @@ if have_system and get_option('curses').allowed() 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 + 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 = (targetos == 'windows' ? ['pdcurses'] : ['ncursesw', 'cursesw']) + curses_libname_list = (host_os == 'windows' ? ['pdcurses'] : ['ncursesw', 'cursesw']) foreach curses_libname : curses_libname_list libcurses = cc.find_library(curses_libname, required: false) @@ -1332,6 +1677,30 @@ if not get_option('glusterfs').auto() or have_block 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', @@ -1375,7 +1744,7 @@ oss = not_found if get_option('oss').allowed() and have_system if not cc.has_header('sys/soundcard.h') # not found - elif targetos == 'netbsd' + elif host_os == 'netbsd' oss = cc.find_library('ossaudio', required: get_option('oss')) else oss = declare_dependency() @@ -1388,7 +1757,7 @@ if get_option('oss').allowed() and have_system endif endif dsound = not_found -if not get_option('dsound').auto() or (targetos == 'windows' and have_system) +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 @@ -1401,7 +1770,7 @@ if not get_option('dsound').auto() or (targetos == 'windows' and have_system) endif coreaudio = not_found -if not get_option('coreaudio').auto() or (targetos == 'darwin' and have_system) +if not get_option('coreaudio').auto() or (host_os == 'darwin' and have_system) coreaudio = dependency('appleframeworks', modules: 'CoreAudio', required: get_option('coreaudio')) endif @@ -1422,6 +1791,12 @@ if (have_system or have_tools) and (virgl.found() or opengl.found()) 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) @@ -1459,6 +1834,8 @@ endif 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() @@ -1473,7 +1850,6 @@ endif if not gnutls_crypto.found() if (not get_option('gcrypt').auto() or have_system) and not get_option('nettle').enabled() gcrypt = dependency('libgcrypt', version: '>=1.8', - method: 'config-tool', required: get_option('gcrypt')) # Debian has removed -lgpg-error from libgcrypt-config # as it "spreads unnecessary dependencies" which in @@ -1484,6 +1860,28 @@ if not gnutls_crypto.found() 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', @@ -1492,6 +1890,62 @@ if not gnutls_crypto.found() 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 @@ -1508,7 +1962,11 @@ gtkx11 = not_found vte = not_found have_gtk_clipboard = get_option('gtk_clipboard').enabled() -if not get_option('gtk').auto() or have_system +if get_option('gtk') \ + .disable_auto_if(not have_system) \ + .require(pixman.found(), + error_message: 'cannot enable GTK if pixman is not available') \ + .allowed() gtk = dependency('gtk+-3.0', version: '>=3.22.0', method: 'pkg-config', required: get_option('gtk')) @@ -1541,7 +1999,11 @@ endif vnc = not_found jpeg = not_found sasl = not_found -if get_option('vnc').allowed() and have_system +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') @@ -1626,13 +2088,39 @@ if numa.found() and not cc.links(''' 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 - libumad = cc.find_library('ibumad', required: get_option('rdma')) rdma_libs = [cc.find_library('rdmacm', has_headers: ['rdma/rdma_cma.h'], required: get_option('rdma')), - cc.find_library('ibverbs', required: get_option('rdma')), - libumad] + cc.find_library('ibverbs', required: get_option('rdma'))] rdma = declare_dependency(dependencies: rdma_libs) foreach lib: rdma_libs if not lib.found() @@ -1641,98 +2129,18 @@ if not get_option('rdma').auto() or have_system 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') - 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', - disabler: true), - dependency('xenforeignmemory', required: false, - method: 'pkg-config', - disabler: true), - dependency('xengnttab', required: false, - method: 'pkg-config', - disabler: true), - dependency('xenevtchn', required: false, - method: 'pkg-config', - disabler: true), - dependency('xendevicemodel', required: false, - method: 'pkg-config', - disabler: true), - # optional, no "disabler: true" - dependency('xentoolcore', required: false, - 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' ] - 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' ], - } - xen_deps = {} - foreach ver: xen_tests - # cache the various library tests to avoid polluting the logs - xen_test_deps = [] - foreach l: xen_libs[ver] - if l not in xen_deps - xen_deps += { l: cc.find_library(l, required: false) } - endif - xen_test_deps += xen_deps[l] - endforeach - - # Use -D to pick just one of the test programs in scripts/xen-detect.c - xen_version = ver.split('.') - xen_ctrl_version = xen_version[0] + \ - ('0' + xen_version[1]).substring(-2) + \ - ('0' + xen_version[2]).substring(-2) - if cc.links(files('scripts/xen-detect.c'), - args: '-DCONFIG_XEN_CTRL_INTERFACE_VERSION=' + xen_ctrl_version, - dependencies: xen_test_deps) - xen = declare_dependency(version: ver, dependencies: xen_test_deps) - break - endif - endforeach - endif - if xen.found() - accelerators += 'CONFIG_XEN' - elif get_option('xen').enabled() - error('could not compile and link Xen test program') - endif -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', - 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() - - 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') 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') 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') endif @@ -1760,11 +2168,13 @@ endif tasn1 = not_found if gnutls.found() tasn1 = dependency('libtasn1', + required: false, method: 'pkg-config') endif keyutils = not_found -if get_option('keyring').enabled() - keyutils = dependency('libkeyutils', required: false, method: 'pkg-config') +if not get_option('libkeyutils').auto() or have_block + keyutils = dependency('libkeyutils', required: get_option('libkeyutils'), + method: 'pkg-config') endif has_gettid = cc.has_function('gettid') @@ -1793,22 +2203,26 @@ if not has_malloc_trim and get_option('malloc_trim').enabled() endif endif -gnu_source_prefix = ''' +osdep_prefix = ''' #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif -''' -# 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 + #include + #include -has_statx_mnt_id = cc.has_header_symbol('sys/stat.h', 'STATX_MNT_ID', prefix: gnu_source_prefix) + #include + #include + /* Put unistd.h before time.h as that triggers localtime_r/gmtime_r + * function availability on recentish Mingw-w64 platforms. */ + #include + #include + #include + #include +''' 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') \ @@ -1836,18 +2250,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') @@ -1857,22 +2271,39 @@ 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 \ @@ -1886,6 +2317,9 @@ endif # 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 = { @@ -1906,7 +2340,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] @@ -1927,47 +2361,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 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 - -have_host_block_device = (targetos != 'darwin' or +have_host_block_device = (host_os != 'darwin' or cc.has_header('IOKit/storage/IOMedia.h')) dbus_display = get_option('dbus_display') \ @@ -1978,21 +2372,27 @@ dbus_display = get_option('dbus_display') \ .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(), + .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 = get_option('virtfs_proxy_helper') \ - .require(targetos != 'darwin', error_message: 'the virtfs proxy helper is incompatible with macOS') \ - .require(have_virtfs, error_message: 'the virtfs proxy helper requires that virtfs is enabled') \ - .disable_auto_if(not have_tools) \ - .require(libcap_ng.found(), error_message: 'the virtfs proxy helper requires libcap-ng') \ - .allowed() +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', '') @@ -2043,18 +2443,24 @@ if enable_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 == 'sunos' ? '/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 @@ -2063,21 +2469,26 @@ 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', targetos in bsd_oses) +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', targetos == 'darwin') +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', targetos == 'linux') -config_host_data.set('CONFIG_POSIX', targetos != 'windows') -config_host_data.set('CONFIG_WIN32', targetos == 'windows') +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_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()) @@ -2099,6 +2510,7 @@ config_host_data.set('CONFIG_HEXAGON_IDEF_PARSER', get_option('hexagon_idef_pars 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()) @@ -2117,6 +2529,7 @@ config_host_data.set('CONFIG_OPENGL', opengl.found()) 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()) @@ -2124,9 +2537,11 @@ 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', targetos == 'sunos') -if get_option('tcg').allowed() +config_host_data.set('CONFIG_SOLARIS', host_os == 'sunos') +if have_tcg config_host_data.set('CONFIG_TCG', 1) config_host_data.set('CONFIG_TCG_INTERPRETER', tcg_arch == 'tci') endif @@ -2134,6 +2549,7 @@ 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) @@ -2148,6 +2564,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()) @@ -2158,12 +2577,15 @@ 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()) @@ -2200,27 +2622,39 @@ 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_DEBUG_TCG', get_option('debug_tcg')) -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_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')) 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')) +valgrind = false +if get_option('valgrind').allowed() + if cc.has_header('valgrind/valgrind.h') + valgrind = true + else + if get_option('valgrind').enabled() + error('valgrind requested but valgrind.h not found') + endif + endif +endif +config_host_data.set('CONFIG_VALGRIND_H', valgrind) 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 @@ -2231,7 +2665,6 @@ 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() @@ -2247,11 +2680,13 @@ config_host_data.set('CONFIG_SETNS', cc.has_function('setns') and cc.has_functio config_host_data.set('CONFIG_SYNCFS', cc.has_function('syncfs')) 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('CONFIG_GETLOADAVG', cc.has_function('getloadavg')) 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_STRCHRNUL', cc.has_function('strchrnul', prefix: osdep_prefix)) config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include ')) if rbd.found() config_host_data.set('HAVE_RBD_NAMESPACE_EXISTS', @@ -2267,7 +2702,7 @@ if rdma.found() endif have_asan_fiber = false -if get_option('sanitizers') and \ +if get_option('asan') and \ not cc.has_function('__sanitizer_start_switch_fiber', args: '-fsanitize=address', prefix: '#include ') @@ -2278,6 +2713,22 @@ else 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_BLKZONED', cc.has_header_symbol('linux/blkzoned.h', 'BLKOPENZONE')) @@ -2291,13 +2742,11 @@ config_host_data.set('CONFIG_FALLOCATE_ZERO_RANGE', config_host_data.set('CONFIG_FIEMAP', cc.has_header('linux/fiemap.h') and cc.has_header_symbol('linux/fs.h', 'FS_IOC_FIEMAP')) +config_host_data.set('CONFIG_GETCPU', + cc.has_header_symbol('sched.h', 'getcpu', prefix: osdep_prefix)) 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_PRCTL_PR_SET_TIMERSLACK', cc.has_header_symbol('sys/prctl.h', 'PR_SET_TIMERSLACK')) config_host_data.set('CONFIG_RTNETLINK', @@ -2308,6 +2757,44 @@ 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')) +if libaio.found() + config_host_data.set('HAVE_IO_PREP_PWRITEV2', + cc.has_header_symbol('libaio.h', 'io_prep_pwritev2')) +endif +if linux_io_uring.found() + config_host_data.set('HAVE_IO_URING_PREP_WRITEV2', + cc.has_header_symbol('liburing.h', 'io_uring_prep_writev2')) +endif +config_host_data.set('HAVE_TCP_KEEPCNT', + cc.has_header_symbol('netinet/tcp.h', 'TCP_KEEPCNT') or + cc.compiles(''' + #include + #ifndef TCP_KEEPCNT + #error + #endif + int main(void) { return 0; }''', + name: 'Win32 TCP_KEEPCNT')) +# On Darwin TCP_KEEPIDLE is available under different name, TCP_KEEPALIVE. +# https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/bsd/man/man4/tcp.4#L172 +config_host_data.set('HAVE_TCP_KEEPIDLE', + cc.has_header_symbol('netinet/tcp.h', 'TCP_KEEPIDLE') or + cc.has_header_symbol('netinet/tcp.h', 'TCP_KEEPALIVE') or + cc.compiles(''' + #include + #ifndef TCP_KEEPIDLE + #error + #endif + int main(void) { return 0; }''', + name: 'Win32 TCP_KEEPIDLE')) +config_host_data.set('HAVE_TCP_KEEPINTVL', + cc.has_header_symbol('netinet/tcp.h', 'TCP_KEEPINTVL') or + cc.compiles(''' + #include + #ifndef TCP_KEEPINTVL + #error + #endif + int main(void) { return 0; }''', + name: 'Win32 TCP_KEEPINTVL')) # has_member config_host_data.set('HAVE_SIGEV_NOTIFY_THREAD_ID', @@ -2331,8 +2818,7 @@ config_host_data.set('HAVE_UTMPX', config_host_data.set('CONFIG_EVENTFD', cc.links(''' #include int main(void) { return eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); }''')) -config_host_data.set('CONFIG_FDATASYNC', cc.links(gnu_source_prefix + ''' - #include +config_host_data.set('CONFIG_FDATASYNC', cc.links(osdep_prefix + ''' int main(void) { #if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0 return fdatasync(0); @@ -2341,10 +2827,8 @@ config_host_data.set('CONFIG_FDATASYNC', cc.links(gnu_source_prefix + ''' #endif }''')) -has_madvise = cc.links(gnu_source_prefix + ''' - #include +has_madvise = cc.links(osdep_prefix + ''' #include - #include int main(void) { return madvise(NULL, 0, MADV_DONTNEED); }''') missing_madvise_proto = false if has_madvise @@ -2354,32 +2838,34 @@ if has_madvise # missing-prototype case, we try again with a definitely-bogus prototype. # This will only compile if the system headers don't provide the prototype; # otherwise the conflicting prototypes will cause a compiler error. - missing_madvise_proto = cc.links(gnu_source_prefix + ''' - #include + missing_madvise_proto = cc.links(osdep_prefix + '''> #include - #include extern int madvise(int); int main(void) { return madvise(0); }''') endif config_host_data.set('CONFIG_MADVISE', has_madvise) config_host_data.set('HAVE_MADVISE_WITHOUT_PROTOTYPE', missing_madvise_proto) -config_host_data.set('CONFIG_MEMFD', cc.links(gnu_source_prefix + ''' +config_host_data.set('CONFIG_MEMFD', cc.links(osdep_prefix + ''' #include int main(void) { return memfd_create("foo", MFD_ALLOW_SEALING); }''')) -config_host_data.set('CONFIG_OPEN_BY_HANDLE', cc.links(gnu_source_prefix + ''' - #include +config_host_data.set('CONFIG_OPEN_BY_HANDLE', cc.links(osdep_prefix + ''' #if !defined(AT_EMPTY_PATH) # error missing definition #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); }''')) -config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_W_TID', cc.links(gnu_source_prefix + ''' +# 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(osdep_prefix + ''' + #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(osdep_prefix + ''' #include static void *f(void *p) { return NULL; } @@ -2390,7 +2876,7 @@ config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_W_TID', cc.links(gnu_source_pref pthread_setname_np(thread, "QEMU"); return 0; }''', dependencies: threads)) -config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_WO_TID', cc.links(gnu_source_prefix + ''' +config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_WO_TID', cc.links(osdep_prefix + ''' #include static void *f(void *p) { pthread_setname_np("QEMU"); return NULL; } @@ -2400,7 +2886,7 @@ 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 + ''' +config_host_data.set('CONFIG_PTHREAD_SET_NAME_NP', cc.links(osdep_prefix + ''' #include #include @@ -2412,9 +2898,8 @@ config_host_data.set('CONFIG_PTHREAD_SET_NAME_NP', cc.links(gnu_source_prefix + pthread_set_name_np(thread, "QEMU"); return 0; }''', dependencies: threads)) -config_host_data.set('CONFIG_PTHREAD_CONDATTR_SETCLOCK', cc.links(gnu_source_prefix + ''' +config_host_data.set('CONFIG_PTHREAD_CONDATTR_SETCLOCK', cc.links(osdep_prefix + ''' #include - #include int main(void) { @@ -2423,7 +2908,7 @@ config_host_data.set('CONFIG_PTHREAD_CONDATTR_SETCLOCK', cc.links(gnu_source_pre pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); return 0; }''', dependencies: threads)) -config_host_data.set('CONFIG_PTHREAD_AFFINITY_NP', cc.links(gnu_source_prefix + ''' +config_host_data.set('CONFIG_PTHREAD_AFFINITY_NP', cc.links(osdep_prefix + ''' #include static void *f(void *p) { return NULL; } @@ -2440,15 +2925,10 @@ config_host_data.set('CONFIG_PTHREAD_AFFINITY_NP', cc.links(gnu_source_prefix + CPU_FREE(cpuset); return 0; }''', dependencies: threads)) -config_host_data.set('CONFIG_SIGNALFD', cc.links(gnu_source_prefix + ''' +config_host_data.set('CONFIG_SIGNALFD', cc.links(osdep_prefix + ''' #include - #include int main(void) { return signalfd(-1, NULL, SFD_CLOEXEC); }''')) -config_host_data.set('CONFIG_SPLICE', cc.links(gnu_source_prefix + ''' - #include - #include - #include - +config_host_data.set('CONFIG_SPLICE', cc.links(osdep_prefix + ''' int main(void) { int len, fd = 0; @@ -2457,16 +2937,22 @@ config_host_data.set('CONFIG_SPLICE', cc.links(gnu_source_prefix + ''' return 0; }''')) -config_host_data.set('HAVE_MLOCKALL', cc.links(gnu_source_prefix + ''' +config_host_data.set('HAVE_MLOCKALL', cc.links(osdep_prefix + ''' #include int main(void) { return mlockall(MCL_FUTURE); }''')) +config_host_data.set('HAVE_MLOCK_ONFAULT', cc.links(osdep_prefix + ''' + #include + int main(void) { + return mlockall(MCL_FUTURE | MCL_ONFAULT); + }''')) + have_l2tpv3 = false if get_option('l2tpv3').allowed() and have_system have_l2tpv3 = cc.has_type('struct mmsghdr', - prefix: gnu_source_prefix + ''' + prefix: osdep_prefix + ''' #include #include ''') endif @@ -2522,9 +3008,11 @@ config_host_data.set('CONFIG_ATOMIC64', cc.links(''' __atomic_exchange_n(&x, y, __ATOMIC_RELAXED); __atomic_fetch_add(&x, y, __ATOMIC_RELAXED); return 0; - }''')) + }''', args: qemu_isa_flags)) -has_int128_type = cc.compiles(''' +# has_int128_type is set to false on Emscripten to avoid errors by libffi +# during runtime. +has_int128_type = host_os != 'emscripten' and cc.compiles(''' __int128_t a; __uint128_t b; int main(void) { b = a; }''') @@ -2556,7 +3044,7 @@ if has_int128_type __atomic_compare_exchange_n(&p[4], &p[5], p[6], 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED); return 0; }''' - has_atomic128 = cc.links(atomic_test_128) + has_atomic128 = cc.links(atomic_test_128, args: qemu_isa_flags) config_host_data.set('CONFIG_ATOMIC128', has_atomic128) @@ -2565,7 +3053,8 @@ if has_int128_type # 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) + 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) if not has_atomic128_opt @@ -2576,17 +3065,25 @@ if has_int128_type __sync_val_compare_and_swap_16(&x, y, x); return 0; } - ''')) + ''', args: qemu_isa_flags)) endif endif endif -config_host_data.set('CONFIG_GETAUXVAL', cc.links(gnu_source_prefix + ''' +config_host_data.set('CONFIG_GETAUXVAL', cc.links(osdep_prefix + ''' #include int main(void) { return getauxval(AT_HWCAP) == 0; }''')) +config_host_data.set('CONFIG_ELF_AUX_INFO', cc.links(osdep_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 @@ -2601,7 +3098,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 @@ -2631,34 +3128,22 @@ have_cpuid_h = cc.links(''' }''') config_host_data.set('CONFIG_CPUID_H', have_cpuid_h) -config_host_data.set('CONFIG_AVX2_OPT', get_option('avx2') \ - .require(have_cpuid_h, error_message: 'cpuid.h not available, cannot enable AVX2') \ - .require(cc.links(''' - #include +# 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')) + +if have_cpuid_h + have_avx2 = cc.links(''' #include 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') \ - .require(cc.links(''' - #include - #include - static int __attribute__((target("avx512f"))) bar(void *a) { - __m512i x = *(__m512i *)a; - return _mm512_test_epi64_mask(x, x); - } - int main(int argc, char *argv[]) { return bar(argv[argc - 1]); } - '''), error_message: 'AVX512F not available').allowed()) - -config_host_data.set('CONFIG_AVX512BW_OPT', get_option('avx512bw') \ - .require(have_cpuid_h, error_message: 'cpuid.h not available, cannot enable AVX512BW') \ - .require(cc.links(''' - #include + ''') + have_avx512bw = cc.links(''' #include static int __attribute__((target("avx512bw"))) bar(void *a) { __m512i *x = a; @@ -2666,7 +3151,21 @@ config_host_data.set('CONFIG_AVX512BW_OPT', get_option('avx512bw') \ return res[1]; } int main(int argc, char *argv[]) { return bar(argv[0]); } - '''), error_message: 'AVX512BW not available').allowed()) + ''') + if get_option('x86_version') >= '3' and not have_avx2 + error('Cannot enable AVX optimizations due to missing intrinsics') + elif get_option('x86_version') >= '4' and not have_avx512bw + error('Cannot enable AVX512 optimizations due to missing intrinsics') + endif +else + have_avx2 = false + have_avx512bw = false + if get_option('x86_version') >= '3' + error('Cannot enable AVX optimizations due to missing cpuid.h') + endif +endif +config_host_data.set('CONFIG_AVX2_OPT', have_avx2) +config_host_data.set('CONFIG_AVX512BW_OPT', have_avx512bw) # For both AArch64 and AArch32, detect if builtins are available. config_host_data.set('CONFIG_ARM_AES_BUILTIN', cc.compiles(''' @@ -2677,42 +3176,11 @@ config_host_data.set('CONFIG_ARM_AES_BUILTIN', cc.compiles(''' void foo(uint8x16_t *p) { *p = vaesmcq_u8(*p); } ''')) -have_pvrdma = get_option('pvrdma') \ - .require(rdma.found(), error_message: 'PVRDMA requires OpenFabrics libraries') \ - .require(cc.compiles(gnu_source_prefix + ''' - #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 - 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 @@ -2729,9 +3197,7 @@ config_host_data.set('CONFIG_MEMBARRIER', get_option('membarrier') \ .allowed()) have_afalg = get_option('crypto_afalg') \ - .require(cc.compiles(gnu_source_prefix + ''' - #include - #include + .require(cc.compiles(osdep_prefix + ''' #include #include int main(void) { @@ -2749,7 +3215,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 'cpp' in all_languages +if host_os == 'windows' and 'cpp' in all_languages have_vss = cxx.compiles(''' #define __MIDL_user_allocate_free_DEFINED__ #include @@ -2760,7 +3226,7 @@ config_host_data.set('HAVE_VSS_SDK', have_vss_sdk) # 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) { @@ -2770,7 +3236,12 @@ if targetos == 'windows' }''', name: '_lock_file and _unlock_file')) endif -if targetos == 'windows' +if spice.found() + config_host_data.set('HAVE_SPICE_QXL_GL_SCANOUT2', + cc.has_function('spice_qxl_gl_scanout2', dependencies: spice)) +endif + +if host_os == 'windows' mingw_has_setjmp_longjmp = cc.links(''' #include int main(void) { @@ -2791,35 +3262,26 @@ if targetos == 'windows' endif endif +# Detect host pointer size for the target configuration loop. +host_long_bits = cc.sizeof('void *') * 8 + ######################## # Target configuration # ######################## minikconf = find_program('scripts/minikconf.py') -config_targetos = { - (targetos == 'windows' ? 'CONFIG_WIN32' : 'CONFIG_POSIX'): 'y' -} -if targetos == 'darwin' - config_targetos += {'CONFIG_DARWIN': 'y'} -elif targetos == 'linux' - config_targetos += {'CONFIG_LINUX': 'y'} -endif -if targetos in bsd_oses - config_targetos += {'CONFIG_BSD': 'y'} -endif -config_all = {} +config_all_accel = {} config_all_devices = {} -config_all_disas = {} config_devices_mak_list = [] config_devices_h = {} config_target_h = {} config_target_mak = {} +config_base_arch_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'], @@ -2827,7 +3289,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'], @@ -2843,18 +3304,23 @@ 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'] : []) + \ - (targetos == 'linux' ? ['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' ] @@ -2864,7 +3330,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 @@ -2872,7 +3338,7 @@ foreach target : target_dirs endif config_target += { 'CONFIG_LINUX_USER': 'y' } elif target.endswith('bsd-user') - if targetos not in bsd_oses + if host_os not in bsd_oses if default_targets continue endif @@ -2887,39 +3353,45 @@ foreach target : target_dirs 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 = [] + config_target += keyval.load('configs/targets' / target + '.mak') + + target_kconfig = [] foreach sym: accelerators + # Disallow 64-bit on 32-bit emulation and virtualization + if host_long_bits < config_target['TARGET_LONG_BITS'].to_int() + continue + endif if sym == 'CONFIG_TCG' or target in accelerator_targets.get(sym, []) config_target += { sym: 'y' } - config_all += { 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' ] + config_all_accel += { 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 + config_target += { 'TARGET_' + config_target['TARGET_ARCH'].to_upper(): 'y' } if 'TARGET_BASE_ARCH' not in config_target config_target += {'TARGET_BASE_ARCH': config_target['TARGET_ARCH']} endif @@ -2934,7 +3406,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 @@ -2965,6 +3436,15 @@ 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'] + + # PVG is not cross-architecture. Use accelerator_targets as a proxy to + # figure out which target can support PVG on this host + if pvg.found() and target in accelerator_targets.get('CONFIG_HVF', []) + target_kconfig += 'CONFIG_MAC_PVG=y' + endif + config_input = meson.get_external_property(target, 'default') config_devices_mak = target + '-config-devices.mak' config_devices_mak = configure_file( @@ -2975,8 +3455,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) @@ -2990,28 +3469,14 @@ foreach target : target_dirs config_all_devices += config_devices endif config_target_mak += {target: config_target} + + # build a merged config for all targets with the same TARGET_BASE_ARCH + target_base_arch = config_target['TARGET_BASE_ARCH'] + config_base_arch = config_base_arch_mak.get(target_base_arch, {}) + config_target + config_base_arch_mak += {target_base_arch: config_base_arch} 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_targetos -config_all += config_all_disas -config_all += { - 'CONFIG_XEN': xen.found(), - 'CONFIG_SYSTEM_ONLY': have_system, - 'CONFIG_USER_ONLY': have_user, - 'CONFIG_ALL': true, -} - target_configs_h = [] foreach target: target_dirs target_configs_h += config_target_h[target] @@ -3024,77 +3489,38 @@ 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', - 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 libvfio_user_proj = subproject('libvfio-user', required: true) libvfio_user_dep = libvfio_user_proj.get_variable('libvfio_user_dep') endif -fdt = not_found -fdt_opt = get_option('fdt') -if fdt_required.length() > 0 or fdt_opt == 'enabled' - if fdt_opt == 'disabled' - error('fdt disabled but required by targets ' + ', '.join(fdt_required)) - endif - - if fdt_opt in ['enabled', 'auto', 'system'] - 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' - error('system libfdt requested, but it is too old (1.5.1 or newer required)') - else - fdt_opt = 'internal' - fdt = not_found - endif - endif - if not fdt.found() - assert(fdt_opt == 'internal') - libfdt_proj = subproject('dtc', required: true, - default_options: ['tools=false', 'yaml=disabled', - 'python=disabled', 'default_library=static']) - fdt = libfdt_proj.get_variable('libfdt_dep') - endif -else - fdt_opt = 'disabled' +vhost_user = not_found +if host_os == 'linux' and have_vhost_user + libvhost_user = subproject('libvhost-user') + vhost_user = libvhost_user.get_variable('vhost_user_dep') endif -config_host_data.set('CONFIG_CAPSTONE', capstone.found()) -config_host_data.set('CONFIG_FDT', fdt.found()) -config_host_data.set('CONFIG_SLIRP', slirp.found()) +libvduse = not_found +if have_libvduse + libvduse_proj = subproject('libvduse') + libvduse = libvduse_proj.get_variable('libvduse_dep') +endif ##################### # Generated sources # ##################### -genh += configure_file(output: 'config-host.h', configuration: config_host_data) +config_host_h = configure_file(output: 'config-host.h', configuration: config_host_data) +genh += config_host_h hxtool = find_program('scripts/hxtool') shaderinclude = find_program('scripts/shaderinclude.py') @@ -3112,6 +3538,7 @@ qapi_gen_depends = [ meson.current_source_dir() / 'scripts/qapi/__init__.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/features.py', meson.current_source_dir() / 'scripts/qapi/visit.py', meson.current_source_dir() / 'scripts/qapi-gen.py' ] @@ -3138,7 +3565,6 @@ tracetool_depends = files( 'scripts/tracetool/format/log_stap.py', 'scripts/tracetool/format/stap.py', 'scripts/tracetool/__init__.py', - 'scripts/tracetool/vcpu.py' ) qemu_version_cmd = [find_program('scripts/qemu-version.sh'), @@ -3168,44 +3594,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() -system_ss = ss.source_set() -specific_fuzz_ss = ss.source_set() -specific_ss = ss.source_set() -stub_ss = ss.source_set() -trace_ss = ss.source_set() -user_ss = ss.source_set() -util_ss = ss.source_set() - -# accel modules -qtest_module_ss = ss.source_set() -tcg_module_ss = ss.source_set() - -modules = {} -target_modules = {} -hw_arch = {} -target_arch = {} -target_softmmu_arch = {} -target_user_arch = {} - ############### # Trace files # ############### @@ -3230,6 +3622,7 @@ if have_block trace_events_subdirs += [ 'authz', 'block', + 'chardev', 'io', 'nbd', 'scsi', @@ -3241,7 +3634,6 @@ if have_system 'audio', 'backends', 'backends/tpm', - 'chardev', 'ebpf', 'hw/9pfs', 'hw/acpi', @@ -3250,10 +3642,10 @@ 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', @@ -3275,28 +3667,31 @@ 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/uefi', 'hw/ufs', 'hw/usb', 'hw/vfio', + 'hw/vfio-user', 'hw/virtio', + 'hw/vmapple', 'hw/watchdog', 'hw/xen', 'hw/gpio', 'migration', 'net', - 'softmmu', + 'system', 'ui', 'hw/remote', ] @@ -3310,8 +3705,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', @@ -3320,17 +3715,42 @@ 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() + +modules = {} +target_modules = {} +plugin_modules = [] +hw_arch = {} +target_arch = {} +target_system_arch = {} +target_user_arch = {} +hw_common_arch = {} +target_common_arch = {} +target_common_system_arch = {} # NOTE: the trace/ subdirectory needs the qapi_trace_events variable # that is filled in by qapi/. @@ -3343,36 +3763,47 @@ subdir('qom') subdir('authz') subdir('crypto') subdir('ui') -subdir('hw') subdir('gdbstub') +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_targetos, 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]) + dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc]) +qemuutil_deps = [event_loop_base] +if host_os != 'windows' + qemuutil_deps += [rt] +endif qemuutil = declare_dependency(link_with: libqemuutil, sources: genh + version_res, - dependencies: [event_loop_base]) + dependencies: qemuutil_deps) if have_system or have_user decodetree = generator(find_program('scripts/decodetree.py'), @@ -3408,18 +3839,23 @@ 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')) - system_ss.add(when: 'CONFIG_WIN32', if_true: [files('os-win32.c')]) + if host_os == 'windows' + system_ss.add(files('os-win32.c')) + elif host_os == 'emscripten' + blockdev_ss.add(files('os-wasm.c')) + else + blockdev_ss.add(files('os-posix.c')) + endif endif -common_ss.add(files('cpus-common.c')) -specific_ss.add(files('cpu.c')) +common_ss.add(files('cpu-common.c')) +specific_ss.add(files('cpu-target.c')) -subdir('softmmu') +subdir('system') # Work around a gcc bug/misfeature wherein constant propagation looks # through an alias: @@ -3439,7 +3875,10 @@ 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')) + +common_ss.add(files('target-info.c')) +specific_ss.add(files('target-info-stub.c')) subdir('backends') subdir('disas') @@ -3455,6 +3894,10 @@ subdir('accel') subdir('plugins') subdir('ebpf') +if 'CONFIG_TCG' in config_all_accel + subdir('contrib/plugins') +endif + common_user_inc = [] subdir('common-user') @@ -3466,22 +3909,19 @@ subdir('tests/qtest/libqos') subdir('tests/qtest/fuzz') # accel modules -tcg_real_module_ss = ss.source_set() -tcg_real_module_ss.add_all(when: 'CONFIG_TCG_MODULAR', if_true: tcg_module_ss) -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 }} +target_modules += { 'accel' : { 'qtest': qtest_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 @@ -3489,25 +3929,27 @@ foreach d, list : modules foreach m, module_ss : list if enable_modules - module_ss = module_ss.apply(config_all, strict: false) + 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 - # unique when it comes to lookup in compile_commands.json. - # Depnds on a mesion version with - # https://github.com/mesonbuild/meson/pull/8900 modinfo_files += custom_target(d + '-' + m + '.modinfo', output: d + '-' + m + '.modinfo', - input: module_ss.sources() + genh, + input: sl.extract_all_objects(recursive: true), capture: true, - command: [modinfo_collect, module_ss.sources()]) + command: [modinfo_collect, '@INPUT@']) endif else if d == 'block' @@ -3522,12 +3964,12 @@ endforeach foreach d, list : target_modules foreach m, module_ss : list if enable_modules + module_ss.add(modulecommon) foreach target : target_dirs if target.endswith('-softmmu') config_target = config_target_mak[target] - config_target += config_targetos 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) @@ -3535,17 +3977,22 @@ 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 - # FIXME: Should use sl.extract_all_objects(recursive: true) too. + 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) modinfo_files += custom_target(module_name + '.modinfo', output: module_name + '.modinfo', - input: target_module_ss.sources() + genh, + input: sl.extract_all_objects(recursive: true), capture: true, - command: [modinfo_collect, '--target', target, target_module_ss.sources()]) + command: [modinfo_collect, '--target', target, '@INPUT@']) endif endif endforeach @@ -3573,6 +4020,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') @@ -3582,124 +4033,220 @@ 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_targetos, 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_targetos, 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_targetos, 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]) +migration = declare_dependency(objects: libmigration.extract_all_objects(recursive: false), + dependencies: [qom, io]) system_ss.add(migration) -block_ss = block_ss.apply(config_targetos, 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_targetos, 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_targetos, 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(), io]) -hwcore_ss = hwcore_ss.apply(config_targetos, 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 - system_ss.add(authz, blockdev, chardev, crypto, io, qmp) common_ss.add(qom, qemuutil) -common_ss.add_all(when: 'CONFIG_SYSTEM_ONLY', if_true: [system_ss]) -common_ss.add_all(when: 'CONFIG_USER_ONLY', if_true: user_ss) +libuser = static_library('user', + user_ss.all_sources() + genh, + c_args: ['-DCONFIG_USER_ONLY', + '-DCOMPILING_SYSTEM_VS_USER'], + include_directories: common_user_inc, + dependencies: user_ss.all_dependencies(), + build_by_default: false) + +libsystem = static_library('system', + system_ss.all_sources() + genh, + c_args: ['-DCONFIG_SOFTMMU', + '-DCOMPILING_SYSTEM_VS_USER'], + dependencies: system_ss.all_dependencies(), + build_by_default: false) -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, - include_directories: common_user_inc, + sources: common_ss.all_sources() + genh, implicit_include_directories: false, - dependencies: common_all.dependencies(), - name_suffix: 'fa') + dependencies: common_ss.all_dependencies()) + +# construct common libraries per base architecture +target_common_arch_libs = {} +target_common_system_arch_libs = {} +foreach target_base_arch, config_base_arch : config_base_arch_mak + target_inc = [include_directories('target' / target_base_arch)] + inc = [common_user_inc + target_inc] + + target_common = common_ss.apply(config_base_arch, strict: false) + target_system = system_ss.apply(config_base_arch, strict: false) + target_user = user_ss.apply(config_base_arch, strict: false) + common_deps = [] + system_deps = [] + user_deps = [] + foreach dep: target_common.dependencies() + common_deps += dep.partial_dependency(compile_args: true, includes: true) + endforeach + foreach dep: target_system.dependencies() + system_deps += dep.partial_dependency(compile_args: true, includes: true) + endforeach + foreach dep: target_user.dependencies() + user_deps += dep.partial_dependency(compile_args: true, includes: true) + endforeach + + # prevent common code to access cpu compile time definition, + # but still allow access to cpu.h + target_c_args = ['-DCPU_DEFS_H'] + target_system_c_args = target_c_args + ['-DCOMPILING_SYSTEM_VS_USER', '-DCONFIG_SOFTMMU'] + + if target_base_arch in target_common_arch + src = target_common_arch[target_base_arch] + lib = static_library( + 'common_' + target_base_arch, + build_by_default: false, + sources: src.all_sources() + genh, + include_directories: inc, + c_args: target_c_args, + dependencies: src.all_dependencies() + common_deps + + system_deps + user_deps) + target_common_arch_libs += {target_base_arch: lib} + endif + + # merge hw_common_arch in target_common_system_arch + if target_base_arch in hw_common_arch + hw_src = hw_common_arch[target_base_arch] + if target_base_arch in target_common_system_arch + target_common_system_arch[target_base_arch].add_all(hw_src) + else + target_common_system_arch += {target_base_arch: hw_src} + endif + endif + + if target_base_arch in target_common_system_arch + src = target_common_system_arch[target_base_arch] + lib = static_library( + 'system_' + target_base_arch, + build_by_default: false, + sources: src.all_sources() + genh, + include_directories: inc, + c_args: target_system_c_args, + dependencies: src.all_dependencies() + common_deps + system_deps) + target_common_system_arch_libs += {target_base_arch: lib} + endif +endforeach + +if have_rust + bindings_incdir = include_directories('.', 'include') + # 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_common = [ + '--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_common += ['--no-rustfmt-bindings'] + else + bindgen_args_common += ['--formatter', 'none'] + endif + endif + if bindgen.version().version_compare('>=0.66.0') + bindgen_args_common += ['--rust-target', '1.59'] + endif + if bindgen.version().version_compare('<0.61.0') + # default in 0.61+ + bindgen_args_common += ['--size_t-is-usize'] + else + bindgen_args_common += ['--merge-extern-blocks'] + endif + subdir('rust') +endif + -feature_to_c = find_program('scripts/feature_to_c.sh') +feature_to_c = find_program('scripts/feature_to_c.py') +rust_root_crate = find_program('scripts/rust/rust_root_crate.sh') -if targetos == 'darwin' +if host_os == 'darwin' entitlement = find_program('scripts/entitlement.sh') endif +traceable = [] emulators = {} foreach target : target_dirs config_target = config_target_mak[target] @@ -3707,27 +4254,29 @@ 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_targetos 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 + c_args += ['-DCONFIG_DEVICES="@0@-config-devices.h"'.format(target)] arch_srcs += config_devices_h[target] link_args += ['@block.syms', '@qemu.syms'] else @@ -3744,7 +4293,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') @@ -3778,35 +4327,86 @@ foreach target : target_dirs arch_deps += t.dependencies() target_common = common_ss.apply(config_target, strict: false) - objects = common_all.extract_objects(target_common.sources()) - deps = target_common.dependencies() + objects = [common_all.extract_objects(target_common.sources())] + arch_deps += target_common.dependencies() + if target_base_arch in target_common_arch_libs + src = target_common_arch[target_base_arch].apply(config_target, strict: false) + lib = target_common_arch_libs[target_base_arch] + objects += lib.extract_objects(src.sources()) + arch_deps += src.dependencies() + endif + if target_type == 'system' + src = system_ss.apply(config_target, strict: false) + objects += libsystem.extract_objects(src.sources()) + arch_deps += src.dependencies() + endif + if target_type == 'user' + src = user_ss.apply(config_target, strict: false) + objects += libuser.extract_objects(src.sources()) + arch_deps += src.dependencies() + endif + if target_type == 'system' and target_base_arch in target_common_system_arch_libs + src = target_common_system_arch[target_base_arch].apply(config_target, strict: false) + lib = target_common_system_arch_libs[target_base_arch] + objects += lib.extract_objects(src.sources()) + arch_deps += src.dependencies() + endif 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(), + structured_sources([], {'.': 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') @@ -3828,20 +4428,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_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 = [ @@ -3867,29 +4467,11 @@ 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 @@ -3897,6 +4479,11 @@ endforeach 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') @@ -3911,19 +4498,29 @@ 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'), - dependencies: qemuutil, + dependencies: [qemuutil, rt], install: true) if have_vhost_user @@ -3933,7 +4530,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, @@ -3943,6 +4540,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 @@ -3951,11 +4555,41 @@ 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') subdir('docs') -subdir('tests') +# Tests are disabled on emscripten because they rely on host features that aren't +# supported by emscripten (e.g. fork and unix socket). +if host_os != 'emscripten' + subdir('tests') +endif if gtk.found() subdir('po') endif @@ -4000,7 +4634,7 @@ 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')} @@ -4008,7 +4642,7 @@ 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 @@ -4021,12 +4655,17 @@ summary(summary_info, bool_yn: true, section: 'Directories') summary_info = {} 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 @@ -4053,8 +4692,8 @@ summary_info += {'Trace backends': ','.join(get_option('trace_backends'))} if 'simple' in get_option('trace_backends') summary_info += {'Trace output file': get_option('trace_file') + '-'} 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} @@ -4075,8 +4714,19 @@ if 'cpp' in all_languages 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 += {'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 += {'rustdoc': rustdoc} + summary_info += {'bindgen': bindgen.full_path()} + summary_info += {'bindgen version': bindgen.version()} endif option_cflags = (get_option('debug') ? ['-g'] : []) if get_option('optimization') != 'plain' @@ -4086,7 +4736,7 @@ summary_info += {'CFLAGS': ' '.join(get_option('c_args') + option_cfl if 'cpp' in all_languages summary_info += {'CXXFLAGS': ' '.join(get_option('cpp_args') + option_cflags)} endif -if targetos == 'darwin' +if 'objc' in all_languages summary_info += {'OBJCFLAGS': ' '.join(get_option('objc_args') + option_cflags)} endif link_args = get_option('c_link_args') @@ -4112,13 +4762,6 @@ summary_info += {'mutex debugging': get_option('debug_mutex')} summary_info += {'memory allocator': get_option('malloc')} summary_info += {'avx2 optimization': config_host_data.get('CONFIG_AVX2_OPT')} summary_info += {'avx512bw optimization': config_host_data.get('CONFIG_AVX512BW_OPT')} -summary_info += {'avx512f optimization': config_host_data.get('CONFIG_AVX512F_OPT')} -if get_option('gprof') - gprof_info = 'YES (deprecated)' -else - gprof_info = get_option('gprof') -endif -summary_info += {'gprof': gprof_info} summary_info += {'gcov': get_option('b_coverage')} summary_info += {'thread sanitizer': get_option('tsan')} summary_info += {'CFI support': get_option('cfi')} @@ -4127,7 +4770,7 @@ if get_option('cfi') 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 @@ -4150,18 +4793,18 @@ endif # Targets and accelerators summary_info = {} if have_system - summary_info += {'KVM support': config_all.has_key('CONFIG_KVM')} - 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.has_key('CONFIG_XEN_EMU')} + 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 @@ -4169,6 +4812,9 @@ if config_all.has_key('CONFIG_TCG') endif 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 @@ -4187,8 +4833,6 @@ if have_block 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 (9P) support': have_virtfs} - summary_info += {'VirtFS (9P) Proxy Helper support (deprecated)': have_virtfs_proxy_helper} - summary_info += {'Live block migration': config_host_data.get('CONFIG_LIVE_BLOCK_MIGRATION')} summary_info += {'replication support': config_host_data.get('CONFIG_REPLICATION')} summary_info += {'bochs support': get_option('bochs').allowed()} summary_info += {'cloop support': get_option('cloop').allowed()} @@ -4218,16 +4862,20 @@ 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') # UI summary_info = {} -if targetos == 'darwin' +if host_os == 'darwin' summary_info += {'Cocoa support': cocoa} endif +summary_info += {'D-Bus display': dbus_display} summary_info += {'SDL support': sdl} summary_info += {'SDL image support': sdl_image} summary_info += {'GTK support': gtk} @@ -4247,17 +4895,23 @@ 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 targetos not in ['darwin', 'haiku', 'windows'] +if host_os not in ['darwin', 'haiku', 'windows'] summary_info += {'OSS support': oss} summary_info += {'sndio support': sndio} -elif targetos == 'darwin' +elif host_os == 'darwin' summary_info += {'CoreAudio support': coreaudio} -elif targetos == 'windows' +elif host_os == 'windows' summary_info += {'DirectSound support': dsound} endif -if targetos == 'linux' +if host_os == 'linux' summary_info += {'ALSA support': alsa} summary_info += {'PulseAudio support': pulse} endif @@ -4267,9 +4921,10 @@ summary(summary_info, bool_yn: true, section: 'Audio backends') # Network backends summary_info = {} -if targetos == 'darwin' +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} @@ -4281,7 +4936,6 @@ summary_info = {} summary_info += {'libtasn1': tasn1} summary_info += {'PAM': pam} summary_info += {'iconv support': iconv} -summary_info += {'virgl support': virgl} summary_info += {'blkio support': blkio} summary_info += {'curl support': curl} summary_info += {'Multipath support': mpathpersist} @@ -4289,8 +4943,7 @@ 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 += {'fdt support': fdt_opt == 'internal' ? 'internal' : fdt} summary_info += {'libcap-ng support': libcap_ng} summary_info += {'bpf support': libbpf} summary_info += {'rbd support': rbd} @@ -4302,13 +4955,14 @@ summary_info += {'OpenGL support (epoxy)': opengl} summary_info += {'GBM': gbm} summary_info += {'libiscsi support': libiscsi} summary_info += {'libnfs support': libnfs} -if targetos == 'windows' +if host_os == 'windows' if have_ga summary_info += {'QGA VSS support': have_qga_vss} endif endif summary_info += {'seccomp support': seccomp} summary_info += {'GlusterFS support': glusterfs} +summary_info += {'hv-balloon support': hv_balloon} summary_info += {'TPM support': have_tpm} summary_info += {'libssh support': libssh} summary_info += {'lzo support': lzo} @@ -4316,15 +4970,26 @@ 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 += {'libdw': libdw} +if host_os == 'freebsd' + summary_info += {'libinotify-kqueue': inotify} +endif +if host_os == 'darwin' + summary_info += {'ParavirtualizedGraphics support': pvg} +endif +summary_info += {'valgrind': valgrind} summary(summary_info, bool_yn: true, section: 'Dependencies') if host_arch == 'unknown' @@ -4336,28 +5001,52 @@ if host_arch == 'unknown' 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 + if have_tcg 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_long_bits < 64 + message() + warning('DEPRECATED HOST CPU') + message() + message('Support for 32-bit CPU host architecture ' + cpu + ' is going') + message('to be dropped in a future QEMU release.') endif -if not supported_oses.contains(targetos) +if not supported_oses.contains(host_os) message() warning('UNSUPPORTED HOST OS') message() - message('Support for host OS ' + targetos + 'is not currently maintained.') + message('Support for host OS ' + host_os + 'is not currently maintained.') message('configure has succeeded and you can continue to build, but') 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 ' + targetos + '.') + message('will keep working on ' + host_os + '.') endif -if host_arch == 'unknown' or not supported_oses.contains(targetos) +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 f82d88b7c66..a442be29958 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -27,12 +27,14 @@ 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('coroutine_backend', type: 'combo', - choices: ['ucontext', 'sigaltstack', 'windows', 'auto'], + choices: ['ucontext', 'sigaltstack', 'windows', 'wasm', 'auto'], value: 'auto', description: 'coroutine backend to use') # Everything else can be set via --enable/--disable-* option @@ -85,12 +87,16 @@ 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('sanitizers', type: 'boolean', value: false, - description: 'enable default sanitizers') +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', @@ -101,27 +107,29 @@ option('cfi_debug', type: 'boolean', value: false, description: 'Verbose errors in case of CFI violation') option('multiprocess', type: 'feature', value: 'auto', description: 'Out of process device emulation support') +option('relocatable', type : 'boolean', value : true, + description: 'toggle relocatable install') option('vfio_user_server', type: 'feature', value: 'disabled', description: 'vfio-user server support') option('dbus_display', type: 'feature', value: 'auto', description: '-display dbus support') option('tpm', type : 'feature', value : 'auto', description: 'TPM support') +option('valgrind', type : 'feature', value: 'auto', + description: 'valgrind debug support for coroutine stacks') # Do not enable it by default even for Mingw32, because it doesn't # work on Wine. option('membarrier', type: 'feature', value: 'disabled', description: 'membarrier system call (for Linux 4.14+ or Windows') -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', @@ -144,6 +152,8 @@ 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', @@ -158,6 +168,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', @@ -184,14 +196,14 @@ option('lzfse', type : 'feature', value : 'auto', description: 'lzfse support for DMG images') option('lzo', type : 'feature', value : 'auto', description: 'lzo compression support') +option('pvg', type: 'feature', value: 'auto', + description: 'macOS paravirtualized graphics support') option('rbd', type : 'feature', value : 'auto', description: 'Ceph block device driver') option('opengl', type : 'feature', value : 'auto', 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 : 'auto', @@ -218,6 +230,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: 'auto', description: 'libslirp user mode network backend support') option('vde', type : 'feature', value : 'auto', @@ -226,6 +240,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', @@ -247,6 +263,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', @@ -287,8 +309,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('virtfs_proxy_helper', type: 'feature', value: 'auto', - description: 'virtio-9p proxy helper support') option('libvduse', type: 'feature', value: 'auto', description: 'build VDUSE Library') option('vduse_blk_export', type: 'feature', value: 'auto', @@ -302,8 +322,6 @@ 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', @@ -344,11 +362,23 @@ option('debug_stack_usage', type: 'boolean', value: false, description: 'measure coroutine stack usage') option('qom_cast_debug', type: 'boolean', value: true, description: 'cast debugging support') -option('gprof', type: 'boolean', value: false, - description: 'QEMU profiling with gprof', - deprecated: true) option('slirp_smbd', type : 'feature', value : 'auto', description: 'use smbd (at path --smbd=*) in slirp networking') +option('qemu_ga_manufacturer', type: 'string', value: 'QEMU', + description: '"manufacturer" name for qemu-ga registry entries') +option('qemu_ga_distro', type: 'string', value: 'Linux', + description: 'second path element in qemu-ga registry entries') +option('qemu_ga_version', type: 'string', value: '', + description: 'version number for qemu-ga installer') + option('hexagon_idef_parser', type : 'boolean', value : true, description: 'use idef-parser to automatically generate TCG code for the Hexagon frontend') + +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-active.c b/migration/block-active.c new file mode 100644 index 00000000000..40e986aadea --- /dev/null +++ b/migration/block-active.c @@ -0,0 +1,48 @@ +/* + * Block activation tracking for migration purpose + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (C) 2024 Red Hat, Inc. + */ +#include "qemu/osdep.h" +#include "block/block.h" +#include "qapi/error.h" +#include "migration/migration.h" +#include "qemu/error-report.h" +#include "trace.h" + +bool migration_block_activate(Error **errp) +{ + ERRP_GUARD(); + + assert(bql_locked()); + + trace_migration_block_activation("active"); + + bdrv_activate_all(errp); + if (*errp) { + error_report_err(error_copy(*errp)); + return false; + } + + return true; +} + +bool migration_block_inactivate(void) +{ + int ret; + + assert(bql_locked()); + + trace_migration_block_activation("inactive"); + + ret = bdrv_inactivate_all(); + if (ret) { + error_report("%s: bdrv_inactivate_all() failed: %d", + __func__, ret); + return false; + } + + return true; +} diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index 032fc5f4054..f2c352d4a71 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -62,8 +62,8 @@ #include "block/block.h" #include "block/block_int.h" #include "block/dirty-bitmap.h" -#include "sysemu/block-backend.h" -#include "sysemu/runstate.h" +#include "system/block-backend.h" +#include "system/runstate.h" #include "qemu/main-loop.h" #include "qemu/error-report.h" #include "migration/misc.h" @@ -464,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; @@ -479,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))); @@ -504,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; } @@ -525,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; } @@ -538,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; } @@ -558,9 +557,9 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, } } 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; @@ -598,8 +597,8 @@ 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; @@ -607,6 +606,10 @@ static int init_dirty_bitmap_migration(DBMSaveState *s) BlockBackend *blk; GHashTable *alias_map = NULL; + /* 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); @@ -639,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); @@ -652,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; } } @@ -738,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) { @@ -770,7 +774,7 @@ static void dirty_bitmap_state_pending(void *opaque, 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); @@ -780,7 +784,7 @@ static void dirty_bitmap_state_pending(void *opaque, pending += DIV_ROUND_UP(sectors * BDRV_SECTOR_SIZE, gran); } - qemu_mutex_unlock_iothread(); + bql_unlock(); trace_dirty_bitmap_state_pending(pending); @@ -1209,14 +1213,12 @@ static int dirty_bitmap_load(QEMUFile *f, void *opaque, int version_id) 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; } @@ -1224,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; } diff --git a/migration/block.c b/migration/block.c deleted file mode 100644 index 86c2256a2bf..00000000000 --- a/migration/block.c +++ /dev/null @@ -1,1027 +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 "block/dirty-bitmap.h" -#include "migration/misc.h" -#include "migration.h" -#include "migration-stats.h" -#include "migration/register.h" -#include "qemu-file.h" -#include "migration/vmstate.h" -#include "sysemu/block-backend.h" -#include "trace.h" -#include "options.h" - -#define BLK_MIG_BLOCK_SIZE (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 - -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 < bmds->total_sectors) { - 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) -{ - int64_t bitmap_size; - - bitmap_size = bmds->total_sectors + BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1; - bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8; - - bmds->aio_bitmap = g_malloc0(bitmap_size); -} - -/* 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) { - if (bmds->dirty_bitmap) { - 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_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); - trace_migration_block_progression(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 (migration_rate_exceeded(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; - BlockDriverState *bs; - AioContext *ctx; - - unset_dirty_tracking(); - - while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) { - QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry); - - bs = blk_bs(bmds->blk); - if (bs) { - bdrv_op_unblock_all(bs, bmds->blocker); - } - error_free(bmds->blocker); - - /* Save ctx, because bmds->blk can disappear during blk_unref. */ - ctx = blk_get_aio_context(bmds->blk); - aio_context_acquire(ctx); - blk_unref(bmds->blk); - aio_context_release(ctx); - - g_free(bmds->blk_name); - g_free(bmds->aio_bitmap); - g_free(bmds); - } -} - -/* Called with iothread lock taken. */ -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; - uint64_t last_bytes = qemu_file_transferred(f); - - 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 < - migration_rate_get() && - 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); - uint64_t delta_bytes = qemu_file_transferred(f) - last_bytes; - return (delta_bytes > 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_state_pending(void *opaque, uint64_t *must_precopy, - uint64_t *can_postcopy) -{ - /* Estimate pending number of bytes to send */ - uint64_t pending; - - qemu_mutex_lock_iothread(); - 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_state_pending(pending); - /* We don't do postcopy */ - *must_precopy += 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_block(); -} - -static SaveVMHandlers savevm_block_handlers = { - .save_setup = block_save_setup, - .save_live_iterate = block_save_iterate, - .save_live_complete_precopy = block_save_complete, - .state_pending_exact = block_state_pending, - .state_pending_estimate = block_state_pending, - .load_state = block_load, - .save_cleanup = block_migration_cleanup, - .is_active = block_is_active, -}; - -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 3178609dbd7..00000000000 --- 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 fff8d870942..97de5a691b5 100644 --- a/migration/channel-block.c +++ b/migration/channel-block.c @@ -123,7 +123,7 @@ qio_channel_block_seek(QIOChannel *ioc, bioc->offset = offset; break; case SEEK_CUR: - bioc->offset += whence; + bioc->offset += offset; break; case SEEK_END: error_setg(errp, "Size of VMstate region is unknown"); @@ -170,7 +170,7 @@ qio_channel_block_set_aio_fd_handler(QIOChannel *ioc, static void qio_channel_block_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); diff --git a/migration/channel.c b/migration/channel.c index ca3319a3098..a547b1fbfeb 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -33,6 +33,7 @@ void migration_channel_process_incoming(QIOChannel *ioc) { MigrationState *s = migrate_get_current(); + MigrationIncomingState *mis = migration_incoming_get_current(); Error *local_err = NULL; trace_migration_set_incoming_channel( @@ -47,6 +48,10 @@ void migration_channel_process_incoming(QIOChannel *ioc) if (local_err) { error_report_err(local_err); + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); + if (mis->exit_on_error) { + exit(EXIT_FAILURE); + } } } @@ -74,7 +79,7 @@ void migration_channel_connect(MigrationState *s, if (!error) { /* tls_channel_connect will call back to this * function after the TLS handshake, - * so we mustn't call migrate_fd_connect until then + * so we mustn't call migration_connect until then */ return; @@ -89,7 +94,7 @@ void migration_channel_connect(MigrationState *s, qemu_mutex_unlock(&s->qemu_file_lock); } } - migrate_fd_connect(s, error); + migration_connect(s, error); error_free(error); } @@ -117,9 +122,12 @@ int migration_channel_read_peek(QIOChannel *ioc, 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) { - error_setg(errp, - "Failed to peek at channel"); + if (len < 0 && len != QIO_CHANNEL_ERR_BLOCK) { + return -1; + } + + if (len == 0) { + error_setg(errp, "Failed to peek at channel"); return -1; } diff --git a/migration/colo-stubs.c b/migration/colo-stubs.c new file mode 100644 index 00000000000..e22ce65234d --- /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 72f4f7b37ea..e0f713c837f 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -11,14 +11,13 @@ */ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "qapi/qapi-commands-migration.h" #include "migration.h" #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" @@ -31,8 +30,8 @@ #include "net/colo.h" #include "block/block.h" #include "qapi/qapi-events-migration.h" -#include "sysemu/cpus.h" -#include "sysemu/runstate.h" +#include "system/cpus.h" +#include "system/runstate.h" #include "net/filter.h" #include "options.h" @@ -63,9 +62,9 @@ static bool colo_runstate_is_stopped(void) return runstate_check(RUN_STATE_COLO) || !runstate_is_running(); } -static void colo_checkpoint_notify(void *opaque) +static void colo_checkpoint_notify(void) { - MigrationState *s = opaque; + MigrationState *s = migrate_get_current(); int64_t next_notify_time; qemu_event_set(&s->colo_checkpoint_event); @@ -74,10 +73,15 @@ static void colo_checkpoint_notify(void *opaque) 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(migrate_get_current()); + colo_checkpoint_notify(); } } @@ -142,7 +146,7 @@ static void secondary_vm_do_failover(void) return; } /* Notify COLO incoming thread that failover work is finished */ - qemu_sem_post(&mis->colo_incoming_sem); + qemu_event_set(&mis->colo_incoming_event); /* For Secondary VM, jump to incoming co */ if (mis->colo_incoming_co) { @@ -162,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(), @@ -191,7 +195,7 @@ static void primary_vm_do_failover(void) } /* Notify COLO thread that failover work is finished */ - qemu_sem_post(&s->colo_exit_sem); + qemu_event_set(&s->colo_exit_event); } COLOMode get_colo_mode(void) @@ -314,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"); } @@ -335,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)); @@ -424,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(), @@ -439,23 +439,26 @@ static int colo_do_checkpoint_transaction(MigrationState *s, if (failover_get_state() != FAILOVER_STATUS_NONE) { goto out; } - qemu_mutex_lock_iothread(); + bql_lock(); replication_do_checkpoint_all(&local_err); if (local_err) { - qemu_mutex_unlock_iothread(); + bql_unlock(); goto out; } colo_send_message(s->to_dst_file, COLO_MESSAGE_VMSTATE_SEND, &local_err); if (local_err) { - qemu_mutex_unlock_iothread(); + bql_unlock(); goto out; } + + qemu_savevm_maybe_send_switchover_start(s->to_dst_file); + /* Note: device state is saved into buffer */ ret = qemu_save_device_state(fb); - qemu_mutex_unlock_iothread(); + bql_unlock(); if (ret < 0) { goto out; } @@ -483,8 +486,7 @@ static int colo_do_checkpoint_transaction(MigrationState *s, } qemu_put_buffer(s->to_dst_file, bioc->data, bioc->usage); - qemu_fflush(s->to_dst_file); - ret = qemu_file_get_error(s->to_dst_file); + ret = qemu_fflush(s->to_dst_file); if (ret < 0) { goto out; } @@ -509,9 +511,9 @@ static int colo_do_checkpoint_transaction(MigrationState *s, ret = 0; - qemu_mutex_lock_iothread(); + bql_lock(); vm_start(); - qemu_mutex_unlock_iothread(); + bql_unlock(); trace_colo_vm_state_change("stop", "run"); out: @@ -523,7 +525,7 @@ static int colo_do_checkpoint_transaction(MigrationState *s, static void colo_compare_notify_checkpoint(Notifier *notifier, void *data) { - colo_checkpoint_notify(data); + colo_checkpoint_notify(); } static void colo_process_checkpoint(MigrationState *s) @@ -562,15 +564,15 @@ static void colo_process_checkpoint(MigrationState *s) fb = qemu_file_new_output(QIO_CHANNEL(bioc)); object_unref(OBJECT(bioc)); - qemu_mutex_lock_iothread(); + bql_lock(); replication_start_all(REPLICATION_MODE_PRIMARY, &local_err); if (local_err) { - qemu_mutex_unlock_iothread(); + bql_unlock(); goto out; } 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) + @@ -618,8 +620,8 @@ static void colo_process_checkpoint(MigrationState *s) } /* Hope this not to be too long to wait here */ - qemu_sem_wait(&s->colo_exit_sem); - qemu_sem_destroy(&s->colo_exit_sem); + qemu_event_wait(&s->colo_exit_event); + qemu_event_destroy(&s->colo_exit_event); /* * It is safe to unregister notifier after failover finished. @@ -644,14 +646,14 @@ static void colo_process_checkpoint(MigrationState *s) 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); + qemu_event_init(&s->colo_exit_event, false); colo_process_checkpoint(s); - qemu_mutex_lock_iothread(); + bql_lock(); } static void colo_incoming_process_checkpoint(MigrationIncomingState *mis, @@ -662,9 +664,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 */ @@ -682,10 +684,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"); @@ -724,14 +726,14 @@ 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; } @@ -739,7 +741,7 @@ 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; } @@ -748,7 +750,7 @@ 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; } /* Notify all filters of all NIC to do checkpoint */ @@ -757,13 +759,13 @@ 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; } 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) { @@ -806,11 +808,11 @@ void colo_shutdown(void) case COLO_MODE_PRIMARY: s = migrate_get_current(); qemu_event_set(&s->colo_checkpoint_event); - qemu_sem_post(&s->colo_exit_sem); + qemu_event_set(&s->colo_exit_event); break; case COLO_MODE_SECONDARY: mis = migration_incoming_get_current(); - qemu_sem_post(&mis->colo_incoming_sem); + qemu_event_set(&mis->colo_incoming_event); break; default: break; @@ -825,7 +827,7 @@ static void *colo_process_incoming_thread(void *opaque) Error *local_err = NULL; rcu_register_thread(); - qemu_sem_init(&mis->colo_incoming_sem, 0); + qemu_event_init(&mis->colo_incoming_event, false); migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_COLO); @@ -835,6 +837,15 @@ static void *colo_process_incoming_thread(void *opaque) return NULL; } + /* Make sure all file formats throw away their mutable metadata */ + bql_lock(); + migration_block_activate(&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); @@ -856,14 +867,14 @@ static void *colo_process_incoming_thread(void *opaque) fb = qemu_file_new_input(QIO_CHANNEL(bioc)); object_unref(OBJECT(bioc)); - qemu_mutex_lock_iothread(); + bql_lock(); replication_start_all(REPLICATION_MODE_SECONDARY, &local_err); if (local_err) { - qemu_mutex_unlock_iothread(); + bql_unlock(); goto out; } 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, @@ -912,46 +923,34 @@ static void *colo_process_incoming_thread(void *opaque) } /* Hope this not to be too long to loop here */ - qemu_sem_wait(&mis->colo_incoming_sem); - qemu_sem_destroy(&mis->colo_incoming_sem); + qemu_event_wait(&mis->colo_incoming_event); + qemu_event_destroy(&mis->colo_incoming_event); rcu_unregister_thread(); return NULL; } -int coroutine_fn colo_incoming_co(void) +void coroutine_fn colo_incoming_co(void) { MigrationIncomingState *mis = migration_incoming_get_current(); - Error *local_err = NULL; QemuThread th; - assert(qemu_mutex_iothread_locked()); - - if (!migration_incoming_colo_enabled()) { - return 0; - } + assert(bql_locked()); + assert(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); - return -EINVAL; - } - - qemu_thread_create(&th, "COLO incoming", colo_process_incoming_thread, + 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; - qemu_mutex_unlock_iothread(); + bql_unlock(); /* Wait checkpoint incoming thread exit before free resource */ qemu_thread_join(&th); - qemu_mutex_lock_iothread(); + bql_lock(); - /* We hold the global iothread lock, so it is safe here */ + /* We hold the global BQL, so it is safe here */ colo_release_ram_cache(); - - return 0; } diff --git a/migration/cpr-transfer.c b/migration/cpr-transfer.c new file mode 100644 index 00000000000..00371d17c3a --- /dev/null +++ b/migration/cpr-transfer.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022, 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 "qapi/error.h" +#include "io/channel-file.h" +#include "io/channel-socket.h" +#include "io/net-listener.h" +#include "migration/cpr.h" +#include "migration/migration.h" +#include "migration/savevm.h" +#include "migration/qemu-file.h" +#include "migration/vmstate.h" +#include "trace.h" + +QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp) +{ + MigrationAddress *addr = channel->addr; + + if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET && + addr->u.socket.type == SOCKET_ADDRESS_TYPE_UNIX) { + + g_autoptr(QIOChannelSocket) sioc = qio_channel_socket_new(); + QIOChannel *ioc = QIO_CHANNEL(sioc); + SocketAddress *saddr = &addr->u.socket; + + if (qio_channel_socket_connect_sync(sioc, saddr, errp) < 0) { + return NULL; + } + trace_cpr_transfer_output(addr->u.socket.u.q_unix.path); + qio_channel_set_name(ioc, "cpr-out"); + return qemu_file_new_output(ioc); + + } else { + error_setg(errp, "bad cpr channel address; must be unix"); + return NULL; + } +} + +QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp) +{ + MigrationAddress *addr = channel->addr; + + if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET && + (addr->u.socket.type == SOCKET_ADDRESS_TYPE_UNIX || + addr->u.socket.type == SOCKET_ADDRESS_TYPE_FD)) { + + g_autoptr(QIOChannelSocket) sioc = NULL; + SocketAddress *saddr = &addr->u.socket; + g_autoptr(QIONetListener) listener = qio_net_listener_new(); + QIOChannel *ioc; + + qio_net_listener_set_name(listener, "cpr-socket-listener"); + if (qio_net_listener_open_sync(listener, saddr, 1, errp) < 0) { + return NULL; + } + + sioc = qio_net_listener_wait_client(listener); + ioc = QIO_CHANNEL(sioc); + trace_cpr_transfer_input( + addr->u.socket.type == SOCKET_ADDRESS_TYPE_UNIX ? + addr->u.socket.u.q_unix.path : addr->u.socket.u.fd.str); + qio_channel_set_name(ioc, "cpr-in"); + return qemu_file_new_input(ioc); + + } else { + error_setg(errp, "bad cpr channel socket type; must be unix"); + return NULL; + } +} diff --git a/migration/cpr.c b/migration/cpr.c new file mode 100644 index 00000000000..a50a57edca7 --- /dev/null +++ b/migration/cpr.c @@ -0,0 +1,266 @@ +/* + * 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 "qapi/error.h" +#include "migration/cpr.h" +#include "migration/misc.h" +#include "migration/options.h" +#include "migration/qemu-file.h" +#include "migration/savevm.h" +#include "migration/vmstate.h" +#include "system/runstate.h" +#include "trace.h" + +/*************************************************************************/ +/* cpr state container for all information to be saved. */ + +typedef QLIST_HEAD(CprFdList, CprFd) CprFdList; + +typedef struct CprState { + CprFdList fds; +} CprState; + +static CprState cpr_state; + +/****************************************************************************/ + +typedef struct CprFd { + char *name; + unsigned int namelen; + int id; + int fd; + QLIST_ENTRY(CprFd) next; +} CprFd; + +static const VMStateDescription vmstate_cpr_fd = { + .name = "cpr fd", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(namelen, CprFd), + VMSTATE_VBUFFER_ALLOC_UINT32(name, CprFd, 0, NULL, namelen), + VMSTATE_INT32(id, CprFd), + VMSTATE_FD(fd, CprFd), + VMSTATE_END_OF_LIST() + } +}; + +void cpr_save_fd(const char *name, int id, int fd) +{ + CprFd *elem = g_new0(CprFd, 1); + + trace_cpr_save_fd(name, id, fd); + elem->name = g_strdup(name); + elem->namelen = strlen(name) + 1; + elem->id = id; + elem->fd = fd; + QLIST_INSERT_HEAD(&cpr_state.fds, elem, next); +} + +static CprFd *find_fd(CprFdList *head, const char *name, int id) +{ + CprFd *elem; + + QLIST_FOREACH(elem, head, next) { + if (!strcmp(elem->name, name) && elem->id == id) { + return elem; + } + } + return NULL; +} + +void cpr_delete_fd(const char *name, int id) +{ + CprFd *elem = find_fd(&cpr_state.fds, name, id); + + if (elem) { + QLIST_REMOVE(elem, next); + g_free(elem->name); + g_free(elem); + } + + trace_cpr_delete_fd(name, id); +} + +int cpr_find_fd(const char *name, int id) +{ + CprFd *elem = find_fd(&cpr_state.fds, name, id); + int fd = elem ? elem->fd : -1; + + trace_cpr_find_fd(name, id, fd); + return fd; +} + +void cpr_resave_fd(const char *name, int id, int fd) +{ + CprFd *elem = find_fd(&cpr_state.fds, name, id); + int old_fd = elem ? elem->fd : -1; + + if (old_fd < 0) { + cpr_save_fd(name, id, fd); + } else if (old_fd != fd) { + error_setg(&error_fatal, + "internal error: cpr fd '%s' id %d value %d " + "already saved with a different value %d", + name, id, fd, old_fd); + } +} + +int cpr_open_fd(const char *path, int flags, const char *name, int id, + Error **errp) +{ + int fd = cpr_find_fd(name, id); + + if (fd < 0) { + fd = qemu_open(path, flags, errp); + if (fd >= 0) { + cpr_save_fd(name, id, fd); + } + } + return fd; +} + +/*************************************************************************/ +#define CPR_STATE "CprState" + +static const VMStateDescription vmstate_cpr_state = { + .name = CPR_STATE, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_QLIST_V(fds, CprState, 1, vmstate_cpr_fd, CprFd, next), + VMSTATE_END_OF_LIST() + } +}; +/*************************************************************************/ + +static QEMUFile *cpr_state_file; + +QIOChannel *cpr_state_ioc(void) +{ + return qemu_file_get_ioc(cpr_state_file); +} + +static MigMode incoming_mode = MIG_MODE_NONE; + +MigMode cpr_get_incoming_mode(void) +{ + return incoming_mode; +} + +void cpr_set_incoming_mode(MigMode mode) +{ + incoming_mode = mode; +} + +bool cpr_is_incoming(void) +{ + return incoming_mode != MIG_MODE_NONE; +} + +int cpr_state_save(MigrationChannel *channel, Error **errp) +{ + int ret; + QEMUFile *f; + MigMode mode = migrate_mode(); + + trace_cpr_state_save(MigMode_str(mode)); + + if (mode == MIG_MODE_CPR_TRANSFER) { + g_assert(channel); + f = cpr_transfer_output(channel, errp); + } else { + return 0; + } + if (!f) { + return -1; + } + + qemu_put_be32(f, QEMU_CPR_FILE_MAGIC); + qemu_put_be32(f, QEMU_CPR_FILE_VERSION); + + ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0); + if (ret) { + error_setg(errp, "vmstate_save_state error %d", ret); + qemu_fclose(f); + return ret; + } + + /* + * Close the socket only partially so we can later detect when the other + * end closes by getting a HUP event. + */ + qemu_fflush(f); + qio_channel_shutdown(qemu_file_get_ioc(f), QIO_CHANNEL_SHUTDOWN_WRITE, + NULL); + cpr_state_file = f; + return 0; +} + +int cpr_state_load(MigrationChannel *channel, Error **errp) +{ + int ret; + uint32_t v; + QEMUFile *f; + MigMode mode = 0; + + if (channel) { + mode = MIG_MODE_CPR_TRANSFER; + cpr_set_incoming_mode(mode); + f = cpr_transfer_input(channel, errp); + } else { + return 0; + } + if (!f) { + return -1; + } + + trace_cpr_state_load(MigMode_str(mode)); + + v = qemu_get_be32(f); + if (v != QEMU_CPR_FILE_MAGIC) { + error_setg(errp, "Not a migration stream (bad magic %x)", v); + qemu_fclose(f); + return -EINVAL; + } + v = qemu_get_be32(f); + if (v != QEMU_CPR_FILE_VERSION) { + error_setg(errp, "Unsupported migration stream version %d", v); + qemu_fclose(f); + return -ENOTSUP; + } + + ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1); + if (ret) { + error_setg(errp, "vmstate_load_state error %d", ret); + qemu_fclose(f); + return ret; + } + + /* + * Let the caller decide when to close the socket (and generate a HUP event + * for the sending side). + */ + cpr_state_file = f; + + return ret; +} + +void cpr_state_close(void) +{ + if (cpr_state_file) { + qemu_fclose(cpr_state_file); + cpr_state_file = NULL; + } +} + +bool cpr_incoming_needed(void *opaque) +{ + MigMode mode = migrate_mode(); + return mode == MIG_MODE_CPR_TRANSFER; +} diff --git a/migration/cpu-throttle.c b/migration/cpu-throttle.c new file mode 100644 index 00000000000..0642e6bdeaf --- /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 "system/cpus.h" +#include "system/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 bccb3515e3c..986624c79a1 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -12,10 +12,9 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include #include "hw/core/cpu.h" #include "qapi/error.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "exec/target_page.h" #include "qemu/rcu_queue.h" #include "qemu/main-loop.h" @@ -25,11 +24,12 @@ #include "dirtyrate.h" #include "monitor/hmp.h" #include "monitor/monitor.h" -#include "qapi/qmp/qdict.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" -#include "exec/memory.h" +#include "qobject/qdict.h" +#include "system/kvm.h" +#include "system/runstate.h" +#include "system/memory.h" #include "qemu/xxhash.h" +#include "migration.h" /* * total_dirty_pages is procted by BQL and is used @@ -90,13 +90,19 @@ static int64_t do_calculate_dirtyrate(DirtyPageRecord dirty_pages, 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(); } /* @@ -106,12 +112,12 @@ 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(); + 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) @@ -129,8 +135,7 @@ static DirtyPageRecord *vcpu_dirty_stat_alloc(VcpuStat *stat) 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; @@ -145,12 +150,12 @@ 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); @@ -158,7 +163,7 @@ int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms, 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(stat, records, true); + vcpu_dirty_stat_collect(records, true); } duration = dirty_stat_wait(calc_time_ms, init_time_ms); @@ -172,7 +177,7 @@ int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms, cpu_list_unlock(); goto retry; } - vcpu_dirty_stat_collect(stat, records, false); + vcpu_dirty_stat_collect(records, false); } for (i = 0; i < stat->nvcpu; i++) { @@ -189,10 +194,9 @@ int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms, 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; } @@ -216,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; @@ -225,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; @@ -259,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) { @@ -400,6 +437,7 @@ 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) * @@ -408,7 +446,9 @@ static void get_ramblock_dirty_info(RAMBlock *block, info->ramblock_pages = qemu_ram_get_used_length(block) >> 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) @@ -574,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 @@ -595,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. @@ -615,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; @@ -628,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++) { @@ -652,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(); @@ -718,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, @@ -727,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. @@ -737,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; } @@ -787,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; @@ -799,21 +841,25 @@ 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)); @@ -873,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 594a5c0bb64..35225c36b69 100644 --- a/migration/dirtyrate.h +++ b/migration/dirtyrate.h @@ -13,7 +13,7 @@ #ifndef QEMU_MIGRATION_DIRTYRATE_H #define QEMU_MIGRATION_DIRTYRATE_H -#include "sysemu/dirtyrate.h" +#include "system/dirtyrate.h" /* * Sample 512 pages per GB as default. @@ -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 2bf882bbe12..20e6cccf8c0 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qapi/type-helpers.h" #include "qemu/error-report.h" #include "channel.h" #include "exec.h" @@ -27,7 +28,6 @@ #include "qemu/cutils.h" #ifdef WIN32 -const char *exec_get_cmd_path(void); const char *exec_get_cmd_path(void) { g_autofree char *detected_path = g_new(char, MAX_PATH); @@ -40,20 +40,16 @@ const char *exec_get_cmd_path(void) } #endif -void exec_start_outgoing_migration(MigrationState *s, const char *command, Error **errp) +void exec_start_outgoing_migration(MigrationState *s, strList *command, + Error **errp) { - QIOChannel *ioc; - -#ifdef WIN32 - const char *argv[] = { exec_get_cmd_path(), "/c", command, NULL }; -#else - const char *argv[] = { "/bin/sh", "-c", command, NULL }; -#endif + 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(command); - ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv, - O_RDWR, - errp)); + trace_migration_exec_outgoing(new_command); + ioc = QIO_CHANNEL(qio_channel_command_new_spawn(args, O_RDWR, errp)); if (!ioc) { return; } @@ -72,20 +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; + 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); -#ifdef WIN32 - const char *argv[] = { exec_get_cmd_path(), "/c", command, NULL }; -#else - const char *argv[] = { "/bin/sh", "-c", command, NULL }; -#endif - - 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 b210ffde7a1..3107f205e34 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 0eb677dcae2..9bf9be6acb2 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) { @@ -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); diff --git a/migration/file.c b/migration/file.c new file mode 100644 index 00000000000..bb8031e3c77 --- /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 "system/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 00000000000..1a1115f7f10 --- /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 4e2a9d8ec0a..c1f90fce0f9 100644 --- a/migration/global_state.c +++ b/migration/global_state.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qemu/error-report.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "qapi/error.h" #include "migration.h" #include "migration/global_state.h" @@ -22,7 +22,16 @@ 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; @@ -35,6 +44,10 @@ static void global_state_do_store(RunState 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) @@ -59,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) @@ -93,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. @@ -110,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; } @@ -131,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 1ae28523a10..9aa48b290e2 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -11,34 +11,43 @@ migration_files = files( system_ss.add(files( 'block-dirty-bitmap.c', + 'block-active.c', 'channel.c', 'channel-block.c', + 'cpr.c', + 'cpr-transfer.c', + 'cpu-throttle.c', 'dirtyrate.c', 'exec.c', 'fd.c', + 'file.c', 'global_state.c', 'migration-hmp-cmds.c', 'migration.c', 'multifd.c', + 'multifd-device-state.c', + 'multifd-nocomp.c', 'multifd-zlib.c', - 'ram-compress.c', + 'multifd-zero-page.c', 'options.c', 'postcopy-ram.c', 'savevm.c', 'socket.c', 'tls.c', 'threadinfo.c', -), gnutls) +), gnutls, zlib) if get_option('replication').allowed() system_ss.add(files('colo-failover.c', 'colo.c')) +else + system_ss.add(files('colo-stubs.c')) endif system_ss.add(when: rdma, if_true: files('rdma.c')) -if get_option('live_block_migration').allowed() - system_ss.add(files('block.c')) -endif system_ss.add(when: zstd, if_true: files('multifd-zstd.c')) +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', diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index c115ef2d233..e8a563c7d8a 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -21,44 +21,44 @@ #include "qapi/error.h" #include "qapi/qapi-commands-migration.h" #include "qapi/qapi-visit-migration.h" -#include "qapi/qmp/qdict.h" +#include "qobject/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 "system/runstate.h" #include "ui/qemu-spice.h" -#include "sysemu/sysemu.h" +#include "system/system.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", + 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", + monitor_printf(mon, " only-migratable: %s\n", only_migratable ? "on" : "off"); - monitor_printf(mon, "send-configuration: %s\n", + monitor_printf(mon, " send-configuration: %s\n", ms->send_configuration ? "on" : "off"); - monitor_printf(mon, "send-section-footer: %s\n", + 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", + monitor_printf(mon, " send-switchover-start: %s\n", + ms->send_switchover_start ? "on" : "off"); + monitor_printf(mon, " clear-bitmap-shift: %u\n", ms->clear_bitmap_shift); } void hmp_info_migrate(Monitor *mon, const QDict *qdict) { + bool show_all = qdict_get_try_bool(qdict, "all", false); 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"); @@ -69,7 +69,7 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) } if (info->has_status) { - monitor_printf(mon, "Migration status: %s", + monitor_printf(mon, "Status: %s", MigrationStatus_str(info->status)); if (info->status == MIGRATION_STATUS_FAILED && info->error_desc) { monitor_printf(mon, " (%s)\n", info->error_desc); @@ -77,131 +77,130 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) 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->total_time) { + monitor_printf(mon, "Time (ms): total=%" PRIu64, + info->total_time); + if (info->has_setup_time) { + monitor_printf(mon, ", setup=%" PRIu64, + info->setup_time); + } + if (info->has_expected_downtime) { + monitor_printf(mon, ", exp_down=%" PRIu64, + info->expected_downtime); + } + if (info->has_downtime) { + monitor_printf(mon, ", down=%" PRIu64, + info->downtime); + } + monitor_printf(mon, "\n"); } - if (info->has_setup_time) { - monitor_printf(mon, "setup: %" PRIu64 " ms\n", - info->setup_time); + } + + if (info->has_socket_address) { + SocketAddressList *addr; + + monitor_printf(mon, "Sockets: [\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->ram) { - monitor_printf(mon, "transferred ram: %" PRIu64 " kbytes\n", - info->ram->transferred >> 10); - monitor_printf(mon, "throughput: %0.2f mbps\n", + monitor_printf(mon, "RAM info:\n"); + monitor_printf(mon, " Throughput (Mbps): %0.2f\n", info->ram->mbps); - monitor_printf(mon, "remaining ram: %" PRIu64 " kbytes\n", - info->ram->remaining >> 10); - monitor_printf(mon, "total ram: %" PRIu64 " kbytes\n", + monitor_printf(mon, " Sizes (KiB): pagesize=%" PRIu64 + ", total=%" PRIu64 ",\n", + info->ram->page_size >> 10, 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", + monitor_printf(mon, " transferred=%" PRIu64 + ", remain=%" PRIu64 ",\n", + info->ram->transferred >> 10, + info->ram->remaining >> 10); + monitor_printf(mon, " precopy=%" PRIu64 + ", multifd=%" PRIu64 + ", postcopy=%" PRIu64, + info->ram->precopy_bytes >> 10, + info->ram->multifd_bytes >> 10, + info->ram->postcopy_bytes >> 10); + + if (info->vfio) { + monitor_printf(mon, ", vfio=%" PRIu64, + info->vfio->transferred >> 10); + } + monitor_printf(mon, "\n"); + + monitor_printf(mon, " Pages: normal=%" PRIu64 ", zero=%" PRIu64 + ", rate_per_sec=%" PRIu64 "\n", + info->ram->normal, + info->ram->duplicate, info->ram->pages_per_second); + monitor_printf(mon, " Others: dirty_syncs=%" PRIu64, + info->ram->dirty_sync_count); if (info->ram->dirty_pages_rate) { - monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n", + monitor_printf(mon, ", dirty_pages_rate=%" PRIu64, info->ram->dirty_pages_rate); } if (info->ram->postcopy_requests) { - monitor_printf(mon, "postcopy request count: %" PRIu64 "\n", + monitor_printf(mon, ", postcopy_req=%" PRIu64, 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); + monitor_printf(mon, ", downtime_ram=%" PRIu64, + info->ram->downtime_bytes); } if (info->ram->dirty_sync_missed_zero_copy) { - monitor_printf(mon, - "Zero-copy-send fallbacks happened: %" PRIu64 " times\n", + monitor_printf(mon, ", zerocopy_fallbacks=%" PRIu64, info->ram->dirty_sync_missed_zero_copy); } + monitor_printf(mon, "\n"); } - if (info->disk) { - monitor_printf(mon, "transferred disk: %" PRIu64 " kbytes\n", - info->disk->transferred >> 10); - monitor_printf(mon, "remaining disk: %" PRIu64 " kbytes\n", - info->disk->remaining >> 10); - monitor_printf(mon, "total disk: %" PRIu64 " kbytes\n", - info->disk->total >> 10); + if (!show_all) { + goto out; } + migration_global_dump(mon); + 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", + monitor_printf(mon, "XBZRLE: size=%" PRIu64 + ", transferred=%" PRIu64 + ", pages=%" PRIu64 + ", miss=%" PRIu64 "\n" + " miss_rate=%0.2f" + ", encode_rate=%0.2f" + ", overflow=%" PRIu64 "\n", + info->xbzrle_cache->cache_size, + info->xbzrle_cache->bytes, + info->xbzrle_cache->pages, + info->xbzrle_cache->cache_miss, + info->xbzrle_cache->cache_miss_rate, + info->xbzrle_cache->encoding_rate, info->xbzrle_cache->overflow); } - if (info->compression) { - monitor_printf(mon, "compression pages: %" PRIu64 " pages\n", - info->compression->pages); - monitor_printf(mon, "compression busy: %" PRIu64 "\n", - info->compression->busy); - monitor_printf(mon, "compression busy rate: %0.2f\n", - info->compression->busy_rate); - monitor_printf(mon, "compressed size: %" PRIu64 " kbytes\n", - info->compression->compressed_size >> 10); - monitor_printf(mon, "compression rate: %0.2f\n", - info->compression->compression_rate); - } - if (info->has_cpu_throttle_percentage) { - monitor_printf(mon, "cpu throttle percentage: %" PRIu64 "\n", + monitor_printf(mon, "CPU Throttle (%%): %" 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", + monitor_printf(mon, "Dirty-limit Throttle (us): %" PRIu64 "\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", + monitor_printf(mon, "Dirty-limit Ring Full (us): %" PRIu64 "\n", info->dirty_limit_ring_full_time); } if (info->has_postcopy_blocktime) { - monitor_printf(mon, "postcopy blocktime: %u\n", + monitor_printf(mon, "Postcopy Blocktime (ms): %" PRIu32 "\n", info->postcopy_blocktime); } @@ -212,28 +211,12 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) visit_type_uint32List(v, NULL, &info->postcopy_vcpu_blocktime, &error_abort); visit_complete(v, &str); - monitor_printf(mon, "postcopy vcpu blocktime: %s\n", 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); - } +out: qapi_free_MigrationInfo(info); } @@ -273,22 +256,6 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) 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), @@ -321,6 +288,10 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) 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), @@ -329,16 +300,17 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %u ms\n", MigrationParameter_str(MIGRATION_PARAMETER_X_CHECKPOINT_DELAY), params->x_checkpoint_delay); - assert(params->has_block_incremental); - monitor_printf(mon, "%s: %s\n", - MigrationParameter_str(MIGRATION_PARAMETER_BLOCK_INCREMENTAL), - params->block_incremental ? "on" : "off"); monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_CHANNELS), params->multifd_channels); monitor_printf(mon, "%s: %s\n", MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_COMPRESSION), MultiFDCompression_str(params->multifd_compression)); + assert(params->has_zero_page_detection); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_ZERO_PAGE_DETECTION), + qapi_enum_lookup(&ZeroPageDetection_lookup, + params->zero_page_detection)); monitor_printf(mon, "%s: %" PRIu64 " bytes\n", MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE), params->xbzrle_cache_size); @@ -382,6 +354,18 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) 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); @@ -389,15 +373,17 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) void hmp_loadvm(Monitor *mon, const QDict *qdict) { - int saved_vm_running = runstate_is_running(); + 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) && saved_vm_running) { - vm_start(); + if (load_snapshot(name, NULL, false, NULL, &err)) { + load_snapshot_resume(saved_state); } + hmp_handle_error(mon, err); } @@ -441,9 +427,18 @@ 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(uri, &err); + qmp_migrate_incoming(NULL, true, caps, true, false, &err); + qapi_free_MigrationChannelList(caps); +end: hmp_handle_error(mon, err); } @@ -509,22 +504,6 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) } 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); @@ -574,6 +553,16 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) } 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); @@ -582,10 +571,6 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) 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); @@ -599,10 +584,18 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) 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)) { @@ -646,8 +639,16 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) 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: - assert(0); + g_assert_not_reached(); } if (err) { @@ -682,7 +683,6 @@ void hmp_x_colo_lost_heartbeat(Monitor *mon, const QDict *qdict) typedef struct HMPMigrationStatus { QEMUTimer *timer; Monitor *mon; - bool is_block_migration; } HMPMigrationStatus; static void hmp_migrate_status_cb(void *opaque) @@ -693,24 +693,8 @@ static void hmp_migrate_status_cb(void *opaque) info = qmp_query_migrate(NULL); if (!info->has_status || info->status == MIGRATION_STATUS_ACTIVE || info->status == MIGRATION_STATUS_SETUP) { - if (info->disk) { - int progress; - - if (info->disk->remaining) { - progress = info->disk->transferred * 100 / info->disk->total; - } else { - progress = 100; - } - - monitor_printf(status->mon, "Completed %d %%\r", progress); - monitor_flush(status->mon); - } - timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000); } else { - if (status->is_block_migration) { - monitor_printf(status->mon, "\n"); - } if (info->error_desc) { error_report("%s", info->error_desc); } @@ -725,14 +709,19 @@ static void hmp_migrate_status_cb(void *opaque) void hmp_migrate(Monitor *mon, const QDict *qdict) { bool detach = qdict_get_try_bool(qdict, "detach", false); - bool blk = qdict_get_try_bool(qdict, "blk", false); - bool inc = qdict_get_try_bool(qdict, "inc", false); bool resume = qdict_get_try_bool(qdict, "resume", false); const char *uri = qdict_get_str(qdict, "uri"); Error *err = NULL; + g_autoptr(MigrationChannelList) caps = NULL; + g_autoptr(MigrationChannel) channel = NULL; - qmp_migrate(uri, !!blk, blk, !!inc, inc, - false, false, true, resume, &err); + 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; } @@ -748,7 +737,6 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) 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)); @@ -794,19 +782,18 @@ static void vm_completion(ReadLineState *rs, const char *str) 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; - 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; } diff --git a/migration/migration-stats.c b/migration/migration-stats.c index 095d6d75bb1..f690b98a03e 100644 --- a/migration/migration-stats.c +++ b/migration/migration-stats.c @@ -24,14 +24,15 @@ bool migration_rate_exceeded(QEMUFile *f) return true; } - uint64_t rate_limit_start = stat64_get(&mig_stats.rate_limit_start); - uint64_t rate_limit_current = migration_transferred_bytes(f); - uint64_t rate_limit_used = rate_limit_current - rate_limit_start; - uint64_t rate_limit_max = stat64_get(&mig_stats.rate_limit_max); - + 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; } @@ -53,16 +54,17 @@ void migration_rate_set(uint64_t limit) stat64_set(&mig_stats.rate_limit_max, limit / XFER_LIMIT_RATIO); } -void migration_rate_reset(QEMUFile *f) +void migration_rate_reset(void) { - stat64_set(&mig_stats.rate_limit_start, migration_transferred_bytes(f)); + stat64_set(&mig_stats.rate_limit_start, migration_transferred_bytes()); } -uint64_t migration_transferred_bytes(QEMUFile *f) +uint64_t migration_transferred_bytes(void) { uint64_t multifd = stat64_get(&mig_stats.multifd_bytes); - uint64_t qemu_file = qemu_file_transferred(f); + 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); - return qemu_file + multifd; + 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 index ac2260e987c..05290ade760 100644 --- a/migration/migration-stats.h +++ b/migration/migration-stats.h @@ -81,6 +81,10 @@ typedef struct { * 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. */ @@ -90,9 +94,9 @@ typedef struct { */ Stat64 rate_limit_max; /* - * Total number of bytes transferred. + * Number of bytes sent through RDMA. */ - Stat64 transferred; + Stat64 rdma_bytes; /* * Number of pages transferred that were full of zeros. */ @@ -112,10 +116,8 @@ 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. - * - * @f: QEMUFile used for main migration channel */ -void migration_rate_reset(QEMUFile *f); +void migration_rate_reset(void); /** * migration_rate_set: Set the maximum amount that can be transferred. @@ -129,11 +131,9 @@ void migration_rate_set(uint64_t new_rate); /** * migration_transferred_bytes: Return number of bytes transferred * - * @f: QEMUFile used for main migration channel - * * 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(QEMUFile *f); +uint64_t migration_transferred_bytes(void); #endif diff --git a/migration/migration.c b/migration/migration.c index d61e5727429..4098870bce3 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -14,19 +14,21 @@ */ #include "qemu/osdep.h" +#include "qemu/ctype.h" #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "migration/blocker.h" #include "exec.h" #include "fd.h" +#include "file.h" #include "socket.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" -#include "sysemu/cpu-throttle.h" +#include "system/runstate.h" +#include "system/system.h" +#include "system/cpu-throttle.h" #include "rdma.h" #include "ram.h" -#include "ram-compress.h" +#include "migration/cpr.h" #include "migration/global_state.h" #include "migration/misc.h" #include "migration.h" @@ -43,9 +45,8 @@ #include "qapi/qapi-commands-migration.h" #include "qapi/qapi-events-migration.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "qemu/rcu.h" -#include "block.h" #include "postcopy-ram.h" #include "qemu/thread.h" #include "trace.h" @@ -60,14 +61,24 @@ #include "multifd.h" #include "threadinfo.h" #include "qemu/yank.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "yank_functions.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "options.h" -#include "sysemu/dirtylimit.h" +#include "system/dirtylimit.h" +#include "qemu/sockets.h" +#include "system/kvm.h" -static NotifierList migration_state_notifiers = - NOTIFIER_LIST_INITIALIZER(migration_state_notifiers); +#define NOTIFIER_ELEM_INIT(array, elem) \ + [elem] = NOTIFIER_WITH_RETURN_LIST_INITIALIZER((array)[elem]) + +#define INMIGRATE_DEFAULT_EXIT_ON_ERROR true + +static NotifierWithReturnList migration_state_notifiers[] = { + NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_NORMAL), + NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_CPR_REBOOT), + NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_CPR_TRANSFER), +}; /* Messages sent on the return path from destination to source */ enum mig_rp_message_type { @@ -84,6 +95,9 @@ enum mig_rp_message_type { MIG_RP_MSG_MAX }; +/* Migration channel types */ +enum { CH_MAIN, CH_MULTIFD, CH_POSTCOPY }; + /* When we add fault tolerance, we could have several migrations at once. For now we don't need to add dynamic creation of migration */ @@ -91,37 +105,181 @@ enum mig_rp_message_type { 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 migration_switchover_start(MigrationState *s, Error **errp); +static bool close_return_path_on_source(MigrationState *s); +static void migration_completion_end(MigrationState *s); +static void migrate_hup_delete(MigrationState *s); + +static void migration_downtime_start(MigrationState *s) +{ + trace_vmstate_downtime_checkpoint("src-downtime-start"); + s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); +} + +/* + * This is unfortunate: incoming migration actually needs the outgoing + * migration state (MigrationState) to be there too, e.g. to query + * capabilities, parameters, using locks, setup errors, etc. + * + * NOTE: when calling this, making sure current_migration exists and not + * been freed yet! Otherwise trying to access the refcount is already + * an use-after-free itself.. + * + * TODO: Move shared part of incoming / outgoing out into separate object. + * Then this is not needed. + */ +static void migrate_incoming_ref_outgoing_state(void) +{ + object_ref(migrate_get_current()); +} +static void migrate_incoming_unref_outgoing_state(void) +{ + object_unref(migrate_get_current()); +} + +static void migration_downtime_end(MigrationState *s) +{ + 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 void precopy_notify_complete(void) +{ + Error *local_err = NULL; + + if (precopy_notify(PRECOPY_NOTIFY_COMPLETE, &local_err)) { + error_report_err(local_err); + } + + trace_migration_precopy_complete(); +} static bool migration_needs_multiple_sockets(void) { return migrate_multifd() || migrate_postcopy_preempt(); } -static bool uri_supports_multi_channels(const char *uri) +static RunState migration_get_target_runstate(void) +{ + /* + * When the global state is not migrated, it means we don't know the + * runstate of the src QEMU. We don't have much choice but assuming + * the VM is running. NOTE: this is pretty rare case, so far only Xen + * uses it. + */ + if (!global_state_received()) { + return RUN_STATE_RUNNING; + } + + return global_state_get_runstate(); +} + +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) { - return strstart(uri, "tcp:", NULL) || strstart(uri, "unix:", NULL) || - strstart(uri, "vsock:", NULL); + /* file: works because QEMU can open it multiple times */ + return addr->transport == MIGRATION_ADDRESS_TYPE_FILE; } static bool -migration_channels_and_uri_compatible(const char *uri, Error **errp) +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() && - !uri_supports_multi_channels(uri)) { + !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; + } + + if (migrate_mode() == MIG_MODE_CPR_TRANSFER && + addr->transport == MIGRATION_ADDRESS_TYPE_FILE) { + error_setg(errp, "Migration requires streamable transport (eg unix)"); + return false; + } + + return true; +} + +static bool +migration_capabilities_and_transport_compatible(MigrationAddress *addr, + Error **errp) +{ + if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) { + return migrate_rdma_caps_check(migrate_get_current()->capabilities, + errp); + } + return true; } +static bool migration_transport_compatible(MigrationAddress *addr, Error **errp) +{ + return migration_channels_and_transport_compatible(addr, errp) && + migration_capabilities_and_transport_compatible(addr, errp); +} + static gint page_request_addr_cmp(gconstpointer ap, gconstpointer bp) { uintptr_t a = (uintptr_t) ap, b = (uintptr_t) bp; @@ -129,6 +287,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. */ @@ -153,24 +328,59 @@ void migration_object_init(void) 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_cancel(const Error *error) +void migration_bh_schedule(QEMUBHFunc *cb, void *opaque) { - 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); + 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_shutdown(void) @@ -185,7 +395,7 @@ void migration_shutdown(void) * Cancel the current migration - that will (eventually) * stop the migration using this structure */ - migration_cancel(NULL); + migration_cancel(); object_unref(OBJECT(current_migration)); /* @@ -233,8 +443,25 @@ void migration_incoming_state_destroy(void) { struct MigrationIncomingState *mis = migration_incoming_get_current(); - multifd_load_cleanup(); - compress_threads_load_cleanup(); + multifd_recv_cleanup(); + + /* + * RAM state cleanup needs to happen after multifd cleanup, because + * multifd threads can use some of its states (receivedmap). + * The VFIO load_cleanup() implementation is BQL-sensitive. It requires + * BQL must NOT be taken when recycling load threads, so that it won't + * block the load threads from making progress on address space + * modification operations. + * + * To make it work, we could try to not take BQL for all load_cleanup(), + * or conditionally unlock BQL only if bql_locked() in VFIO. + * + * Since most existing call sites take BQL for load_cleanup(), make + * it simple by taking BQL always as the rule, so that VFIO can unlock + * BQL and retake unconditionally. + */ + assert(bql_locked()); + qemu_loadvm_state_cleanup(mis); if (mis->to_src_file) { /* Tell source that we are done */ @@ -267,10 +494,11 @@ void migration_incoming_state_destroy(void) mis->postcopy_qemufile_dst = NULL; } + cpr_set_incoming_mode(MIG_MODE_NONE); yank_unregister_instance(MIGRATION_YANK_INSTANCE); } -static void migrate_generate_event(int new_state) +static void migrate_generate_event(MigrationStatus new_state) { if (migrate_events()) { qapi_event_send_migration(new_state); @@ -302,12 +530,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. @@ -367,7 +590,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); } } @@ -398,13 +621,13 @@ void migration_incoming_disable_colo(void) int migration_incoming_enable_colo(void) { #ifndef CONFIG_REPLICATION - error_report("ENABLE_COLO command come in migration stream, but COLO " - "module is not built in"); + 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 c-colo " + error_report("ENABLE_COLO command come in migration stream, but x-colo " "capability is not set"); return -EINVAL; } @@ -425,56 +648,167 @@ void migrate_add_address(SocketAddress *address) QAPI_CLONE(SocketAddress, address)); } -static void qemu_start_incoming_migration(const char *uri, Error **errp) +bool migrate_is_uri(const char *uri) +{ + while (*uri && *uri != ':') { + if (!qemu_isalpha(*uri++)) { + return false; + } + } + return *uri == ':'; +} + +bool migrate_uri_parse(const char *uri, MigrationChannel **channel, + Error **errp) +{ + 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; + + 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 + 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) { - const char *p = NULL; + 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 must have only one entry, " + "for type 'main'"); + 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_transport_compatible(addr, errp)) { + return; + } - /* URI is not suitable for migration? */ - if (!migration_channels_and_uri_compatible(uri, errp)) { + if (!migration_incoming_state_setup(mis, errp)) { return; } - qapi_event_send_migration(MIGRATION_STATUS_SETUP); - if (strstart(uri, "tcp:", &p) || - strstart(uri, "unix:", NULL) || - strstart(uri, "vsock:", NULL)) { - socket_start_incoming_migration(p ? p : uri, errp); + 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 (strstart(uri, "rdma:", &p)) { - rdma_start_incoming_migration(p, errp); + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) { + rdma_start_incoming_migration(&addr->u.rdma, errp); #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); + } 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); } + + /* Close cpr socket to tell source that we are listening */ + cpr_state_close(); } static void process_incoming_migration_bh(void *opaque) { - Error *local_err = NULL; MigrationIncomingState *mis = opaque; - /* 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. - * This causes file locking to happen; so we don't want it to happen - * unless we really are starting the VM. - */ - if (!migrate_late_block_activate() || - (autostart && (!global_state_received() || - global_state_get_runstate() == RUN_STATE_RUNNING))) { - /* 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); - if (local_err) { - error_report_err(local_err); - local_err = NULL; - autostart = false; - } - } + trace_vmstate_downtime_checkpoint("dst-precopy-bh-enter"); /* * This must happen after all error conditions are dealt with and @@ -482,14 +816,29 @@ static void process_incoming_migration_bh(void *opaque) */ qemu_announce_self(&mis->announce_timer, migrate_announce_params()); - multifd_load_shutdown(); + 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) { + if (runstate_is_live(migration_get_target_runstate())) { if (autostart) { - vm_start(); + /* + * Block activation is always delayed until VM starts, either + * here (which means we need to start the dest VM right now..), + * or until qmp_cont() later. + * + * We used to have cap 'late-block-activate' but now we do this + * unconditionally, as it has no harm but only benefit. E.g., + * it's not part of migration ABI on the time of disk activation. + * + * Make sure all file formats throw away their mutable + * metadata. If error, don't restart the VM yet. + */ + if (migration_block_activate(NULL)) { + vm_start(); + } } else { runstate_set(RUN_STATE_PAUSED); } @@ -499,6 +848,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 @@ -506,33 +856,31 @@ 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); - if (compress_threads_load_setup(mis->from_src_file)) { - error_report("Failed to setup decompress threads"); - goto fail; - } - mis->largest_page_size = qemu_ram_pagesize_largest(); postcopy_state_set(POSTCOPY_INCOMING_NONE); - migrate_set_state(&mis->state, MIGRATION_STATUS_NONE, + migrate_set_state(&mis->state, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_ACTIVE); mis->loadvm_co = qemu_coroutine_self(); ret = qemu_loadvm_state(mis->from_src_file); mis->loadvm_co = NULL; + trace_vmstate_downtime_checkpoint("dst-precopy-loadvm-completed"); + ps = postcopy_state_get(); trace_process_incoming_migration_co_end(ret, ps); if (ps != POSTCOPY_INCOMING_NONE) { @@ -549,50 +897,56 @@ process_incoming_migration_co(void *opaque) * postcopy thread. */ trace_process_incoming_migration_co_postcopy_end_main(); - return; + goto out; } /* Else if something went wrong then just fall out of the normal exit */ } 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; } - if (colo_incoming_co() < 0) { - goto fail; + if (migration_incoming_colo_enabled()) { + /* yield until COLO exit */ + colo_incoming_co(); } - mis->bh = qemu_bh_new(process_incoming_migration_bh, mis); - qemu_bh_schedule(mis->bh); - return; + migration_bh_schedule(process_incoming_migration_bh, mis); + goto out; + fail: migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_FAILED); - qemu_fclose(mis->from_src_file); + migrate_set_error(s, local_err); + error_free(local_err); - multifd_load_cleanup(); - compress_threads_load_cleanup(); + 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); + } +out: + /* Pairs with the refcount taken in qmp_migrate_incoming() */ + migrate_incoming_unref_outgoing_state(); } /** * 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 (!mis->from_src_file) { - mis->from_src_file = f; - } + assert(!mis->from_src_file); + mis->from_src_file = f; qemu_file_set_blocking(f, false); - return true; } void migration_incoming_process(void) @@ -634,39 +988,28 @@ static bool postcopy_try_recover(void) return false; } -void migration_fd_process_incoming(QEMUFile *f, Error **errp) +void migration_fd_process_incoming(QEMUFile *f) { - if (!migration_incoming_setup(f, errp)) { - return; - } + migration_incoming_setup(f); if (postcopy_try_recover()) { return; } migration_incoming_process(); } -/* - * Returns true when we want to start a new incoming migration process, - * false otherwise. - */ -static bool migration_should_start_incoming(bool main_channel) +static bool migration_has_main_and_multifd_channels(void) { - /* Multifd doesn't start unless all channels are established */ - if (migrate_multifd()) { - return migration_has_all_channels(); + MigrationIncomingState *mis = migration_incoming_get_current(); + if (!mis->from_src_file) { + /* main channel not established */ + return false; } - /* Preempt channel only starts when the main channel is created */ - if (migrate_postcopy_preempt()) { - return main_channel; + if (migrate_multifd() && !multifd_recv_all_channels_created()) { + return false; } - /* - * 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); + /* main and all multifd channels are established */ return true; } @@ -675,63 +1018,81 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) MigrationIncomingState *mis = migration_incoming_get_current(); Error *local_err = NULL; QEMUFile *f; - bool default_channel = true; + uint8_t channel; uint32_t channel_magic = 0; int ret = 0; - if (migrate_multifd() && !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), &local_err); + if (!migration_has_main_and_multifd_channels()) { + if (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 (ret != 0) { + return; + } - if (ret != 0) { - error_propagate(errp, local_err); + channel_magic = be32_to_cpu(channel_magic); + if (channel_magic == QEMU_VM_FILE_MAGIC) { + channel = CH_MAIN; + } else if (channel_magic == MULTIFD_MAGIC) { + assert(migrate_multifd()); + channel = CH_MULTIFD; + } else if (!mis->from_src_file && + mis->state == MIGRATION_STATUS_POSTCOPY_PAUSED) { + /* reconnect main channel for postcopy recovery */ + channel = CH_MAIN; + } else { + error_setg(errp, "unknown channel magic: %u", channel_magic); + return; + } + } else if (mis->from_src_file && migrate_multifd()) { + /* + * Non-peekable channels like tls/file are processed as + * multifd channels when multifd is enabled. + */ + channel = CH_MULTIFD; + } else if (!mis->from_src_file) { + channel = CH_MAIN; + } else { + error_setg(errp, "non-peekable channel used without multifd"); return; } - - default_channel = (channel_magic == cpu_to_be32(QEMU_VM_FILE_MAGIC)); } else { - default_channel = !mis->from_src_file; + assert(migrate_postcopy_preempt()); + channel = CH_POSTCOPY; } - if (multifd_load_setup(errp) != 0) { - error_setg(errp, "Failed to setup multifd channels"); + if (multifd_recv_setup(errp) != 0) { return; } - if (default_channel) { + if (channel == CH_MAIN) { f = qemu_file_new_input(ioc); - - if (!migration_incoming_setup(f, errp)) { - return; - } - } else { + migration_incoming_setup(f); + } else if (channel == CH_MULTIFD) { /* Multiple connections */ - assert(migration_needs_multiple_sockets()); - if (migrate_multifd()) { - multifd_recv_new_channel(ioc, &local_err); - } else { - assert(migrate_postcopy_preempt()); - f = qemu_file_new_input(ioc); - postcopy_preempt_new_channel(mis, f); - } + multifd_recv_new_channel(ioc, &local_err); if (local_err) { error_propagate(errp, local_err); return; } + } else if (channel == CH_POSTCOPY) { + assert(!mis->postcopy_qemufile_dst); + f = qemu_file_new_input(ioc); + postcopy_preempt_new_channel(mis, f); + return; } - if (migration_should_start_incoming(default_channel)) { + if (migration_has_main_and_multifd_channels()) { /* If it's a recovery, we're done */ if (postcopy_try_recover()) { return; @@ -748,18 +1109,13 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) */ bool migration_has_all_channels(void) { - MigrationIncomingState *mis = migration_incoming_get_current(); - - if (!mis->from_src_file) { + if (!migration_has_main_and_multifd_channels()) { return false; } - if (migrate_multifd()) { - return multifd_recv_all_channels_created(); - } - - if (migrate_postcopy_preempt()) { - return mis->postcopy_qemufile_dst != NULL; + MigrationIncomingState *mis = migration_incoming_get_current(); + if (migrate_postcopy_preempt() && !mis->postcopy_qemufile_dst) { + return false; } return true; @@ -844,50 +1200,40 @@ void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t value) migrate_send_rp_message(mis, MIG_RP_MSG_RESUME_ACK, sizeof(buf), &buf); } -/* - * Return true if we're already in the middle of a migration - * (i.e. any of the active or setup states) - */ -bool migration_is_setup_or_active(int state) +bool migration_is_running(void) { - 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; + MigrationState *s = current_migration; - 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 migration_is_active(void) +{ + MigrationState *s = current_migration; + + return (s->state == MIGRATION_STATUS_ACTIVE || + s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); +} + static bool migrate_show_downtime(MigrationState *s) { return (s->state == MIGRATION_STATUS_COMPLETED) || migration_in_postcopy(); @@ -922,11 +1268,9 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) size_t page_size = qemu_target_page_size(); info->ram = g_malloc0(sizeof(*info->ram)); - info->ram->transferred = stat64_get(&mig_stats.transferred); + info->ram->transferred = migration_transferred_bytes(); info->ram->total = ram_bytes_total(); info->ram->duplicate = stat64_get(&mig_stats.zero_pages); - /* legacy value. It is not used anymore */ - info->ram->skipped = 0; info->ram->normal = stat64_get(&mig_stats.normal_pages); info->ram->normal_bytes = info->ram->normal * page_size; info->ram->mbps = s->mbps; @@ -954,17 +1298,6 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) info->xbzrle_cache->overflow = xbzrle_counters.overflow; } - if (migrate_compress()) { - 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(); @@ -986,21 +1319,11 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) } } -static void populate_disk_info(MigrationInfo *info) -{ - if (blk_mig_active()) { - 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(); - } -} - 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; @@ -1034,11 +1357,11 @@ 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); migration_populate_vfio_info(info); break; case MIGRATION_STATUS_COLO: @@ -1052,9 +1375,6 @@ static void fill_source_migration_info(MigrationInfo *info) break; case MIGRATION_STATUS_FAILED: info->has_status = true; - if (s->error) { - info->error_desc = g_strdup(error_get_pretty(s->error)); - } break; case MIGRATION_STATUS_CANCELLED: info->has_status = true; @@ -1064,6 +1384,11 @@ static void fill_source_migration_info(MigrationInfo *info) break; } info->status = state; + + QEMU_LOCK_GUARD(&s->error_mutex); + if (s->error) { + info->error_desc = g_strdup(error_get_pretty(s->error)); + } } static void fill_destination_migration_info(MigrationInfo *info) @@ -1077,8 +1402,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: @@ -1094,8 +1417,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) @@ -1132,7 +1466,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) { @@ -1141,49 +1476,57 @@ void migrate_set_state(int *state, int old_state, int new_state) } } -static void migrate_fd_cleanup(MigrationState *s) +static void migration_cleanup_json_writer(MigrationState *s) { - qemu_bh_delete(s->cleanup_bh); - s->cleanup_bh = NULL; + g_clear_pointer(&s->vmdesc, json_writer_free); +} + +static void migration_cleanup(MigrationState *s) +{ + MigrationEventType type; + QEMUFile *tmp = NULL; + + trace_migration_cleanup(); + + migration_cleanup_json_writer(s); g_free(s->hostname); s->hostname = NULL; - json_writer_free(s->vmdesc); - s->vmdesc = NULL; qemu_savevm_state_cleanup(); + cpr_state_close(); + migrate_hup_delete(s); - 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, @@ -1194,36 +1537,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(); + 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) +static void migration_cleanup_bh(void *opaque) { - MigrationState *s = opaque; - migrate_fd_cleanup(s); - object_unref(OBJECT(s)); + migration_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); @@ -1233,20 +1575,46 @@ static void migrate_error_free(MigrationState *s) } } -static void migrate_fd_error(MigrationState *s, const Error *error) +static void migration_connect_set_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) +void migration_cancel(void) { + MigrationState *s = migrate_get_current(); int old_state ; - QEMUFile *f = migrate_get_current()->to_dst_file; - trace_migrate_fd_cancel(); + bool setup = (s->state == MIGRATION_STATUS_SETUP); + + trace_migration_cancel(); + + if (migrate_dirty_limit()) { + qmp_cancel_vcpu_dirty_limit(false, -1, NULL); + } WITH_QEMU_LOCK_GUARD(&s->qemu_file_lock) { if (s->rp_state.from_dst_file) { @@ -1257,12 +1625,12 @@ 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 */ if (old_state == MIGRATION_STATUS_PRE_SWITCHOVER) { - qemu_sem_post(&s->pause_sem); + qemu_event_set(&s->pause_event); } migrate_set_state(&s->state, old_state, MIGRATION_STATUS_CANCELLING); } while (s->state != MIGRATION_STATUS_CANCELLING); @@ -1271,42 +1639,60 @@ 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; - bdrv_activate_all(&local_err); - if (local_err) { - error_report_err(local_err); - } else { - s->block_inactive = false; - } + /* + * If qmp_migrate_finish has not been called, then there is no path that + * will complete the cancellation. Do it now. + */ + if (setup && !s->to_dst_file) { + migrate_set_state(&s->state, MIGRATION_STATUS_CANCELLING, + MIGRATION_STATUS_CANCELLED); + cpr_state_close(); + migrate_hup_delete(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) @@ -1322,6 +1708,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: @@ -1329,9 +1716,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) @@ -1350,46 +1743,21 @@ bool migration_incoming_postcopy_advised(void) bool migration_in_bg_snapshot(void) { - MigrationState *s = migrate_get_current(); - - return migrate_background_snapshot() && - migration_is_setup_or_active(s->state); + return migrate_background_snapshot() && migration_is_running(); } -bool migration_is_idle(void) +bool migration_thread_is_self(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; + return qemu_thread_is_self(&s->thread); } -bool migration_is_active(MigrationState *s) +bool migrate_mode_is_cpr(MigrationState *s) { - return (s->state == MIGRATION_STATUS_ACTIVE || - s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); + MigMode mode = s->parameters.mode; + return mode == MIG_MODE_CPR_REBOOT || + mode == MIG_MODE_CPR_TRANSFER; } int migrate_init(MigrationState *s, Error **errp) @@ -1406,23 +1774,22 @@ int migrate_init(MigrationState *s, Error **errp) * 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; + + if (should_send_vmdesc()) { + s->vmdesc = json_writer_new(false); + } migrate_set_state(&s->state, MIGRATION_STATUS_NONE, MIGRATION_STATUS_SETUP); @@ -1432,52 +1799,128 @@ int migrate_init(MigrationState *s, Error **errp) s->iteration_initial_bytes = 0; s->threshold_size = 0; s->switchover_acked = false; + s->rdma_migration = false; /* - * set mig_stats compression_counters memory to zero for a - * new migration + * set mig_stats memory to zero for a new migration */ memset(&mig_stats, 0, sizeof(mig_stats)); - memset(&compression_counters, 0, sizeof(compression_counters)); migration_reset_vfio_bytes_transferred(); return 0; } -int migrate_add_blocker_internal(Error *reason, Error **errp) +static bool is_busy(Error **reasonp, Error **errp) { + ERRP_GUARD(); + /* Snapshots are similar to migrations, so check RUN_STATE_SAVE_VM too. */ - if (runstate_check(RUN_STATE_SAVE_VM) || !migration_is_idle()) { - error_propagate_prepend(errp, error_copy(reason), + if (runstate_check(RUN_STATE_SAVE_VM) || migration_is_running()) { + error_propagate_prepend(errp, *reasonp, "disallowing migration blocker " "(migration/snapshot in progress) for: "); - return -EBUSY; + *reasonp = NULL; + return true; } - - migration_blockers = g_slist_prepend(migration_blockers, reason); - return 0; + return false; } -int migrate_add_blocker(Error *reason, Error **errp) +static bool is_only_migratable(Error **reasonp, Error **errp, int modes) { - if (only_migratable) { - error_propagate_prepend(errp, error_copy(reason), + ERRP_GUARD(); + + if (only_migratable && (modes & BIT(MIG_MODE_NORMAL))) { + error_propagate_prepend(errp, *reasonp, "disallowing migration blocker " "(--only-migratable) for: "); + *reasonp = NULL; + return true; + } + return false; +} + +static int get_modes(MigMode mode, va_list ap) +{ + 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; +} + +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; - return migrate_add_blocker_internal(reason, errp); + if (is_busy(reasonp, errp)) { + return -EBUSY; + } + return add_blockers(reasonp, errp, modes); } -void migrate_del_blocker(Error *reason) +void migrate_del_blocker(Error **reasonp) { - migration_blockers = g_slist_remove(migration_blockers, reason); + 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, Error **errp) +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"); @@ -1492,7 +1935,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); @@ -1500,6 +1946,17 @@ void qmp_migrate_incoming(const char *uri, Error **errp) return; } + /* + * Making sure MigrationState is available until incoming migration + * completes. + * + * NOTE: QEMU _might_ leak this refcount in some failure paths, but + * that's OK. This is the minimum change we need to at least making + * sure success case is clean on the refcount. We can try harder to + * make it accurate for any kind of failures, but it might be an + * overkill and doesn't bring us much benefit. + */ + migrate_incoming_ref_outgoing_state(); once = false; } @@ -1528,27 +1985,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"); @@ -1557,17 +2030,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; } @@ -1575,11 +2050,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 " @@ -1605,12 +2077,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; } @@ -1625,29 +2100,43 @@ 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()) { - error_setg(errp, "No disk migration is required in COLO mode"); - return false; - } - if (migrate_block() || migrate_block_incremental()) { - error_setg(errp, "Command options are incompatible with " - "current migration capabilities"); + if (migrate_mapped_ram()) { + if (migrate_tls()) { + error_setg(errp, "Cannot use TLS with mapped-ram"); return false; } - if (!migrate_cap_set(MIGRATION_CAPABILITY_BLOCK, true, &local_err)) { - error_propagate(errp, local_err); + + if (migrate_multifd_compression()) { + error_setg(errp, "Cannot use compression with mapped-ram"); return false; } - s->must_remove_block_options = true; } - if (blk_inc) { - migrate_set_block_incremental(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; + } } if (migrate_init(s, errp)) { @@ -1657,62 +2146,173 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, return true; } -void qmp_migrate(const char *uri, bool has_blk, bool blk, - bool has_inc, bool inc, bool has_detach, bool detach, +static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested, + Error **errp); + +static void migrate_hup_add(MigrationState *s, QIOChannel *ioc, GSourceFunc cb, + void *opaque) +{ + s->hup_source = qio_channel_create_watch(ioc, G_IO_HUP); + g_source_set_callback(s->hup_source, cb, opaque, NULL); + g_source_attach(s->hup_source, NULL); +} + +static void migrate_hup_delete(MigrationState *s) +{ + if (s->hup_source) { + g_source_destroy(s->hup_source); + g_source_unref(s->hup_source); + s->hup_source = NULL; + } +} + +static gboolean qmp_migrate_finish_cb(QIOChannel *channel, + GIOCondition cond, + void *opaque) +{ + MigrationAddress *addr = opaque; + + qmp_migrate_finish(addr, false, NULL); + + cpr_state_close(); + migrate_hup_delete(migrate_get_current()); + qapi_free_MigrationAddress(addr); + return G_SOURCE_REMOVE; +} + +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; + MigrationChannel *channelv[MIGRATION_CHANNEL_TYPE__MAX] = { NULL }; + MigrationChannel *cpr_channel = NULL; + + /* + * Having preliminary checks for uri and channel + */ + if (!uri == !channels) { + error_setg(errp, "need either 'uri' or 'channels' argument"); + return; + } + + if (channels) { + for ( ; channels; channels = channels->next) { + MigrationChannelType type = channels->value->channel_type; + + if (channelv[type]) { + error_setg(errp, "Channel list has more than one %s entry", + MigrationChannelType_str(type)); + return; + } + channelv[type] = channels->value; + } + cpr_channel = channelv[MIGRATION_CHANNEL_TYPE_CPR]; + addr = channelv[MIGRATION_CHANNEL_TYPE_MAIN]->addr; + if (!addr) { + error_setg(errp, "Channel list has no main entry"); + return; + } + } + + 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_transport_compatible(addr, errp)) { + return; + } - /* URI is not suitable for migration? */ - if (!migration_channels_and_uri_compatible(uri, errp)) { + if (s->parameters.mode == MIG_MODE_CPR_TRANSFER && !cpr_channel) { + error_setg(errp, "missing 'cpr' migration channel"); return; } resume_requested = has_resume && resume; - if (!migrate_prepare(s, has_blk && blk, has_inc && inc, - resume_requested, errp)) { + if (!migrate_prepare(s, resume_requested, errp)) { /* Error detected, put into errp */ return; } + if (cpr_state_save(cpr_channel, &local_err)) { + goto out; + } + + /* + * For cpr-transfer, the target may not be listening yet on the migration + * channel, because first it must finish cpr_load_state. The target tells + * us it is listening by closing the cpr-state socket. Wait for that HUP + * event before connecting in qmp_migrate_finish. + * + * The HUP could occur because the target fails while reading CPR state, + * in which case the target will not listen for the incoming migration + * connection, so qmp_migrate_finish will fail to connect, and then recover. + */ + if (s->parameters.mode == MIG_MODE_CPR_TRANSFER) { + migrate_hup_add(s, cpr_state_ioc(), (GSourceFunc)qmp_migrate_finish_cb, + QAPI_CLONE(MigrationAddress, addr)); + + } else { + qmp_migrate_finish(addr, resume_requested, errp); + } + +out: + if (local_err) { + migration_connect_set_error(s, local_err); + error_propagate(errp, local_err); + } +} + +static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested, + Error **errp) +{ + MigrationState *s = migrate_get_current(); + Error *local_err = NULL; + if (!resume_requested) { if (!yank_register_instance(MIGRATION_YANK_INSTANCE, errp)) { return; } } - if (strstart(uri, "tcp:", &p) || - strstart(uri, "unix:", NULL) || - strstart(uri, "vsock:", NULL)) { - socket_start_outgoing_migration(s, p ? p : uri, &local_err); + 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); + } #ifdef CONFIG_RDMA - } else if (strstart(uri, "rdma:", &p)) { - rdma_start_outgoing_migration(s, p, &local_err); + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) { + rdma_start_outgoing_migration(s, &addr->u.rdma, &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 (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 { - if (!resume_requested) { - yank_unregister_instance(MIGRATION_YANK_INSTANCE); - } 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(); - return; } if (local_err) { if (!resume_requested) { yank_unregister_instance(MIGRATION_YANK_INSTANCE); } - migrate_fd_error(s, local_err); + migration_connect_set_error(s, local_err); error_propagate(errp, local_err); return; } @@ -1720,7 +2320,18 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, void qmp_migrate_cancel(Error **errp) { - migration_cancel(NULL); + /* + * After postcopy migration has started, the source machine is not + * recoverable in case of a migration error. This also means the + * cancel command cannot be used as cancel should allow the + * machine to continue operation. + */ + if (migration_in_postcopy()) { + error_setg(errp, "Postcopy migration in progress, cannot cancel."); + return; + } + + migration_cancel(); } void qmp_migrate_continue(MigrationStatus state, Error **errp) @@ -1731,17 +2342,29 @@ void qmp_migrate_continue(MigrationStatus state, Error **errp) MigrationStatus_str(s->state)); return; } - qemu_sem_post(&s->pause_sem); + qemu_event_set(&s->pause_event); } -/* 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) +int migration_rp_wait(MigrationState *s) { - s->rp_state.error = true; + /* If migration has failure already, ignore the wait */ + if (migrate_has_error(s)) { + return -1; + } + + qemu_sem_wait(&s->rp_state.rp_sem); + + /* After wait, double check that there's no failure */ + if (migrate_has_error(s)) { + return -1; + } + + return 0; +} + +void migration_rp_kick(MigrationState *s) +{ + qemu_sem_post(&s->rp_state.rp_sem); } static struct rp_cmd_args { @@ -1764,8 +2387,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(); @@ -1777,50 +2401,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. */ @@ -1828,9 +2439,9 @@ 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; } /* @@ -1839,7 +2450,7 @@ static int migrate_handle_rp_resume_ack(MigrationState *s, uint32_t value) */ static void migration_release_dst_files(MigrationState *ms) { - QEMUFile *file; + QEMUFile *file = NULL; WITH_QEMU_LOCK_GUARD(&ms->qemu_file_lock) { /* @@ -1878,49 +2489,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; } @@ -1930,8 +2538,7 @@ static void *source_return_path_thread(void *opaque) 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 @@ -1949,7 +2556,10 @@ static void *source_return_path_thread(void *opaque) 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: @@ -1964,32 +2574,32 @@ static void *source_return_path_thread(void *opaque) 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; @@ -2005,38 +2615,33 @@ static void *source_return_path_thread(void *opaque) } 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_dst_files(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_dst_files(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) { @@ -2045,12 +2650,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; @@ -2059,27 +2659,34 @@ 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 (!ms->rp_state.rp_thread_created) { + return false; + } + + 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. + * 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. */ - 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); + 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); + } } - trace_await_return_path_close_on_source_joining(); + 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 @@ -2098,52 +2705,55 @@ 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); - uint64_t bandwidth = migrate_max_postcopy_bandwidth(); - bool restart_block = false; - int cur_state = MIGRATION_STATUS_ACTIVE; + + /* + * Now we're 100% sure to switch to postcopy, so JSON writer won't be + * useful anymore. Free the resources early if it is there. Clearing + * the vmdesc also means any follow up vmstate_save()s will start to + * skip all JSON operations, which can shrink postcopy downtime. + */ + migration_cleanup_json_writer(ms); 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); + if (ms->state != MIGRATION_STATUS_CANCELLING) { + migrate_set_state(&ms->state, ms->state, + MIGRATION_STATUS_FAILED); + } + error_setg(errp, "%s: Failed to establish preempt channel", + __func__); return -1; } } - if (!migrate_pause_before_switchover()) { - migrate_set_state(&ms->state, MIGRATION_STATUS_ACTIVE, - MIGRATION_STATUS_POSTCOPY_ACTIVE); + if (!qemu_savevm_state_postcopy_prepare(ms->to_dst_file, errp)) { + return -1; } 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); - if (ret < 0) { - goto fail; - } - - ret = migration_maybe_pause(ms, &cur_state, - MIGRATION_STATUS_POSTCOPY_ACTIVE); + 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 = bdrv_inactivate_all(); - if (ret < 0) { + if (!migration_switchover_start(ms, errp)) { goto fail; } - restart_block = true; /* * Cause any non-postcopiable, but iterative devices to * send out their final data. */ - qemu_savevm_state_complete_precopy(ms->to_dst_file, true, false); + ret = qemu_savevm_state_complete_precopy_iterable(ms->to_dst_file, true); + if (ret) { + error_setg(errp, "Postcopy save non-postcopiable iterables failed"); + goto fail; + } /* * in Finish migrate and with the io-lock held everything should @@ -2155,12 +2765,6 @@ static int postcopy_start(MigrationState *ms, Error **errp) ram_postcopy_send_discard_bitmap(ms); } - /* - * send rest of state - note things that are doing postcopy - * will notice we're in POSTCOPY_ACTIVE and not actually - * wrap their state up here - */ - 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); @@ -2188,7 +2792,12 @@ static int postcopy_start(MigrationState *ms, Error **errp) */ qemu_savevm_send_postcopy_listen(fb); - qemu_savevm_state_complete_precopy(fb, false, false); + ret = qemu_savevm_state_complete_precopy_non_iterable(fb, true); + if (ret) { + error_setg(errp, "Postcopy save non-iterable device states failed"); + goto fail_closefb; + } + if (migrate_postcopy_ram()) { qemu_savevm_send_ping(fb, 3); } @@ -2207,10 +2816,9 @@ static int postcopy_start(MigrationState *ms, Error **errp) goto fail_closefb; } - restart_block = false; - /* 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); @@ -2219,12 +2827,9 @@ static int postcopy_start(MigrationState *ms, Error **errp) * 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); - - ms->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - time_at_stop; + migration_call_notifiers(ms, MIG_EVENT_PRECOPY_DONE, NULL); - qemu_mutex_unlock_iothread(); + migration_downtime_end(ms); if (migrate_postcopy_ram()) { /* @@ -2240,13 +2845,23 @@ static int postcopy_start(MigrationState *ms, Error **errp) ret = qemu_file_get_error(ms->to_dst_file); if (ret) { - error_setg(errp, "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"); + goto fail; } - trace_postcopy_preempt_enabled(migrate_postcopy_preempt()); + /* + * Now postcopy officially started, switch to postcopy bandwidth that + * user specified. + */ + migration_rate_set(migrate_max_postcopy_bandwidth()); + + /* Now, switchover looks all fine, switching to postcopy-active */ + migrate_set_state(&ms->state, MIGRATION_STATUS_DEVICE, + MIGRATION_STATUS_POSTCOPY_ACTIVE); + + bql_unlock(); + return ret; fail_closefb: @@ -2254,62 +2869,144 @@ static int postcopy_start(MigrationState *ms, Error **errp) fail: migrate_set_state(&ms->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, MIGRATION_STATUS_FAILED); - if (restart_block) { - /* A failure happened early enough that we know the destination hasn't - * accessed block devices, so we're safe to recover. - */ - Error *local_err = NULL; - - bdrv_activate_all(&local_err); - if (local_err) { - error_report_err(local_err); - } - } - qemu_mutex_unlock_iothread(); + migration_block_activate(NULL); + 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 - * Returns: 0 on success + * @migration_switchover_prepare: Start VM switchover procedure + * + * @s: The migration state object pointer + * + * Prepares for the switchover, depending on "pause-before-switchover" + * capability. + * + * If cap set, state machine goes like: + * [postcopy-]active -> pre-switchover -> device + * + * If cap not set: + * [postcopy-]active -> device + * + * Returns: true on success, false on interruptions. */ -static int migration_maybe_pause(MigrationState *s, - int *current_active_state, - int new_state) +static bool migration_switchover_prepare(MigrationState *s) { + /* Concurrent cancellation? Quit */ + if (s->state == MIGRATION_STATUS_CANCELLING) { + return false; + } + + /* + * No matter precopy or postcopy, since we still hold BQL it must not + * change concurrently to CANCELLING, so it must be either ACTIVE or + * POSTCOPY_ACTIVE. + */ + assert(migration_is_active()); + + /* If the pre stage not requested, directly switch to DEVICE */ if (!migrate_pause_before_switchover()) { - return 0; + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_DEVICE); + return true; } - /* Since leaving this state is not atomic with posting the semaphore + /* + * Since leaving this state is not atomic with setting the event * it's possible that someone could have issued multiple migrate_continue - * and the semaphore is incorrectly positive at this point; - * the docs say it's undefined to reinit a semaphore that's already - * init'd, so use timedwait to eat up any existing posts. + * and the event is incorrectly set at this point so reset it. + */ + qemu_event_reset(&s->pause_event); + + /* Update [POSTCOPY_]ACTIVE to PRE_SWITCHOVER */ + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_PRE_SWITCHOVER); + bql_unlock(); + + qemu_event_wait(&s->pause_event); + + bql_lock(); + /* + * After BQL released and retaken, the state can be CANCELLING if it + * happend during sem_wait().. Only change the state if it's still + * pre-switchover. */ - while (qemu_sem_timedwait(&s->pause_sem, 1) == 0) { - /* This block intentionally left blank */ + migrate_set_state(&s->state, MIGRATION_STATUS_PRE_SWITCHOVER, + MIGRATION_STATUS_DEVICE); + + return s->state == MIGRATION_STATUS_DEVICE; +} + +static bool migration_switchover_start(MigrationState *s, Error **errp) +{ + ERRP_GUARD(); + + if (!migration_switchover_prepare(s)) { + error_setg(errp, "Switchover is interrupted"); + return false; + } + + /* Inactivate disks except in COLO */ + if (!migrate_colo()) { + /* + * Inactivate before sending QEMU_VM_EOF so that the + * bdrv_activate_all() on the other end won't fail. + */ + if (!migration_block_inactivate()) { + error_setg(errp, "Block inactivate failed during switchover"); + return false; + } + } + + migration_rate_set(RATE_LIMIT_DISABLED); + + precopy_notify_complete(); + + qemu_savevm_maybe_send_switchover_start(s->to_dst_file); + + return true; +} + +static int migration_completion_precopy(MigrationState *s) +{ + 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; + } } + if (!migration_switchover_start(s, NULL)) { + ret = -EFAULT; + goto out_unlock; + } + + ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false); +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(); + /* - * If the migration is cancelled when it is in the completion phase, - * the migration state is set to MIGRATION_STATUS_CANCELLING. - * So we don't need to wait a semaphore, otherwise we would always - * wait for the 'pause_sem' semaphore. + * 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 (s->state != MIGRATION_STATUS_CANCELLING) { - qemu_mutex_unlock_iothread(); - migrate_set_state(&s->state, *current_active_state, - MIGRATION_STATUS_PRE_SWITCHOVER); - qemu_sem_wait(&s->pause_sem); - migrate_set_state(&s->state, MIGRATION_STATUS_PRE_SWITCHOVER, - new_state); - *current_active_state = new_state; - qemu_mutex_lock_iothread(); + if (migrate_postcopy_preempt() && s->preempt_pre_7_2) { + postcopy_preempt_shutdown_file(s); } - return s->state == new_state ? 0 : -EINVAL; + trace_migration_completion_postcopy_end_after_complete(); } /** @@ -2320,75 +3017,23 @@ static int migration_maybe_pause(MigrationState *s, */ static void migration_completion(MigrationState *s) { - int ret; - int current_active_state = s->state; + int ret = 0; + 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_old_state = runstate_get(); - global_state_store(); - - 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(); - migration_rate_set(RATE_LIMIT_DISABLED); - 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); } 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. 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(); + 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)) { @@ -2401,33 +3046,24 @@ static void migration_completion(MigrationState *s) 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); + if (s->state != MIGRATION_STATUS_CANCELLING) { + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); + } } /** @@ -2450,7 +3086,7 @@ static void bg_migration_completion(MigrationState *s) qemu_put_buffer(s->to_dst_file, s->bioc->data, s->bioc->usage); qemu_fflush(s->to_dst_file); } else if (s->state == MIGRATION_STATUS_CANCELLING) { - goto fail; + return; } if (qemu_file_get_error(s->to_dst_file)) { @@ -2458,8 +3094,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: @@ -2481,7 +3116,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) { @@ -2546,6 +3183,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 @@ -2575,19 +3219,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 */ - /* - * 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! */ @@ -2608,6 +3246,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; @@ -2658,25 +3309,28 @@ static MigThrError migration_detect_error(MigrationState *s) } } -static void migration_calculate_complete(MigrationState *s) +static void migration_completion_end(MigrationState *s) { - uint64_t bytes = migration_transferred_bytes(s->to_dst_file); + 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) @@ -2686,7 +3340,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_transferred_bytes(s->to_dst_file); + s->iteration_initial_bytes = migration_transferred_bytes(); s->iteration_initial_pages = ram_get_total_transferred_pages(); } @@ -2695,17 +3349,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_transferred_bytes(s->to_dst_file); + 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 * migrate_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; @@ -2722,15 +3392,17 @@ static void migration_update_counters(MigrationState *s, if (stat64_get(&mig_stats.dirty_pages_rate) && transferred > 10000) { s->expected_downtime = - stat64_get(&mig_stats.dirty_bytes_last_sync) / bandwidth; + stat64_get(&mig_stats.dirty_bytes_last_sync) / expected_bw_per_ms; } - migration_rate_reset(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) @@ -2760,17 +3432,16 @@ typedef enum { */ static MigIterateState migration_iteration_run(MigrationState *s) { - uint64_t must_precopy, can_postcopy; + 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_estimate(&must_precopy, &can_postcopy); - uint64_t pending_size = must_precopy + can_postcopy; - + pending_size = must_precopy + can_postcopy; trace_migrate_pending_estimate(pending_size, must_precopy, can_postcopy); - if (must_precopy <= s->threshold_size) { + 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); @@ -2799,13 +3470,19 @@ static MigIterateState migration_iteration_run(MigrationState *s) 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: @@ -2816,7 +3493,12 @@ static void migration_iteration_finish(MigrationState *s) case MIGRATION_STATUS_FAILED: case MIGRATION_STATUS_CANCELLED: case MIGRATION_STATUS_CANCELLING: - if (s->vm_old_state == RUN_STATE_RUNNING) { + /* + * Re-activate the block drives if they're inactivated. Note, COLO + * shouldn't use block_active at all, so it should be no-op there. + */ + migration_block_activate(NULL); + if (runstate_is_live(s->vm_old_state)) { if (!runstate_check(RUN_STATE_SHUTDOWN)) { vm_start(); } @@ -2832,8 +3514,9 @@ 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(migration_cleanup_bh, s); + bql_unlock(); } static void bg_migration_iteration_finish(MigrationState *s) @@ -2845,12 +3528,9 @@ static void bg_migration_iteration_finish(MigrationState *s) */ ram_write_tracking_stop(); - qemu_mutex_lock_iothread(); + 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: @@ -2863,8 +3543,8 @@ static void bg_migration_iteration_finish(MigrationState *s) break; } - migrate_fd_cleanup_schedule(s); - qemu_mutex_unlock_iothread(); + migration_bh_schedule(migration_cleanup_bh, s); + bql_unlock(); } /* @@ -2978,15 +3658,23 @@ static void *migration_thread(void *opaque) 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("live_migration", qemu_get_thread_id()); + 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 @@ -3014,16 +3702,35 @@ static void *migration_thread(void *opaque) 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)) { + 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) { @@ -3053,6 +3760,7 @@ static void *migration_thread(void *opaque) urgent = migration_rate_limit(); } +out: trace_migration_thread_after_loop(); migration_iteration_finish(s); object_unref(OBJECT(s)); @@ -3065,11 +3773,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); } /** @@ -3094,9 +3799,10 @@ 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)); migration_rate_set(RATE_LIMIT_DISABLED); @@ -3125,37 +3831,37 @@ 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(); - - /* - * 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_old_state = runstate_get(); + bql_lock(); - global_state_store(); - /* 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; } - /* - * Put vCPUs in sync with shadow context structures, then - * save their state to channel-buffer along with devices. - */ - cpu_synchronize_all_states(); - if (qemu_savevm_state_complete_precopy_non_iterable(fb, false, false)) { + + if (qemu_savevm_state_complete_precopy_non_iterable(fb, false)) { goto fail; } /* @@ -3176,12 +3882,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; @@ -3208,9 +3912,10 @@ static void *bg_migration_thread(void *opaque) 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); @@ -3220,39 +3925,34 @@ static void *bg_migration_thread(void *opaque) return NULL; } -void migrate_fd_connect(MigrationState *s, Error *error_in) +void migration_connect(MigrationState *s, Error *error_in) { Error *local_err = NULL; uint64_t rate_limit; - bool resume = s->state == MIGRATION_STATUS_POSTCOPY_PAUSED; + bool resume = (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP); + int ret; /* * If there's a previous error, free it and prepare for another one. * Meanwhile if migration completes successfully, there won't have an error - * dumped when calling migrate_fd_cleanup(). + * dumped when calling migration_cleanup(). */ migrate_error_free(s); s->expected_downtime = migrate_downtime_limit(); - if (resume) { - assert(s->cleanup_bh); - } else { - assert(!s->cleanup_bh); - s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup_bh, s); - } if (error_in) { - migrate_fd_error(s, error_in); + migration_connect_set_error(s, error_in); if (resume) { /* * Don't do cleanup for resume if channel is invalid, but only dump * the error. We wait for another channel connect from the user. * The error_report still gives HMP user a hint on what failed. - * It's normally done in migrate_fd_cleanup(), but call it here + * It's normally done in migration_cleanup(), but call it here * explicitly. */ error_report_err(error_copy(s->error)); } else { - migrate_fd_cleanup(s); + migration_cleanup(s); } return; } @@ -3265,7 +3965,9 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) 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; + } } migration_rate_set(rate_limit); @@ -3277,13 +3979,9 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) * QEMU uses the return path. */ if (migrate_postcopy_ram() || migrate_return_path()) { - if (open_return_path_on_source(s, !resume)) { + if (open_return_path_on_source(s)) { error_setg(&local_err, "Unable to open return-path for postcopy"); - migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); - migrate_set_error(s, local_err); - error_report_err(local_err); - migrate_fd_cleanup(s); - return; + goto fail; } } @@ -3298,37 +3996,54 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) 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) { - migrate_set_error(s, local_err); - 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); + if (s->state != MIGRATION_STATUS_CANCELLING) { + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); + } + error_report_err(local_err); + migration_cleanup(s); } -static void migration_class_init(ObjectClass *klass, void *data) +static void migration_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->user_creatable = false; - device_class_set_props(dc, migration_properties); + device_class_set_props_n(dc, migration_properties, + migration_properties_count); } static void migration_instance_finalize(Object *obj) @@ -3339,9 +4054,8 @@ static void migration_instance_finalize(Object *obj) qemu_mutex_destroy(&ms->qemu_file_lock); qemu_sem_destroy(&ms->wait_unplug_sem); qemu_sem_destroy(&ms->rate_limit_sem); - qemu_sem_destroy(&ms->pause_sem); + qemu_event_destroy(&ms->pause_event); 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); @@ -3355,13 +4069,12 @@ static void migration_instance_init(Object *obj) ms->state = MIGRATION_STATUS_NONE; ms->mbps = -1; ms->pages_per_second = -1; - qemu_sem_init(&ms->pause_sem, 0); + qemu_event_init(&ms->pause_event, false); qemu_mutex_init(&ms->error_mutex); 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); diff --git a/migration/migration.h b/migration/migration.h index c390500604b..739289de934 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -17,17 +17,33 @@ #include "exec/cpu-common.h" #include "hw/qdev-core.h" #include "qapi/qapi-types-migration.h" -#include "qapi/qmp/json-writer.h" +#include "qobject/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 "system/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; +typedef struct ThreadPool ThreadPool; #define MIGRATION_RESUME_ACK_VALUE (1) @@ -82,9 +98,9 @@ struct MigrationIncomingState { void (*transport_cleanup)(void *data); /* * Used to sync thread creations. Note that we can't create threads in - * parallel with this sem. + * parallel with this event. */ - QemuSemaphore thread_sync_sem; + QemuEvent thread_sync_event; /* * Free at the start of the main state load, set as the main thread finishes * loading state. @@ -159,9 +175,7 @@ struct MigrationIncomingState { /* PostCopyFD's for external userfaultfds & handlers of shared memory */ GArray *postcopy_remote_fds; - QEMUBH *bh; - - int state; + MigrationStatus state; /* * The incoming migration coroutine, non-NULL during qemu_loadvm_state(). @@ -172,7 +186,11 @@ struct MigrationIncomingState { /* The coroutine we should enter (back) after failover */ Coroutine *colo_incoming_co; - QemuSemaphore colo_incoming_sem; + QemuEvent colo_incoming_event; + + /* Optional load threads pool and its thread exit request flag */ + ThreadPool *load_threads; + bool load_threads_abort; /* * PostcopyBlocktimeContext to keep information for postcopy @@ -196,7 +214,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 @@ -210,6 +231,14 @@ 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 @@ -217,6 +246,9 @@ struct MigrationIncomingState { * 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); @@ -244,8 +276,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 */ @@ -283,21 +313,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. @@ -305,6 +334,12 @@ 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 @@ -336,23 +371,18 @@ struct MigrationState { /* 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; - /* Flag set once the migration thread called bdrv_inactivate_all */ - bool block_inactive; - /* Migration is waiting for guest to unplug device */ QemuSemaphore wait_unplug_sem; /* Migration is paused due to pause-before-switchover */ - QemuSemaphore pause_sem; + QemuEvent pause_event; - /* The semaphore is used to notify COLO thread that failover is finished */ - QemuSemaphore colo_exit_sem; + /* The event is used to notify COLO thread that failover is finished */ + QemuEvent colo_exit_event; /* The event is used to notify COLO thread to do checkpoint */ QemuEvent colo_checkpoint_event; @@ -365,10 +395,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. @@ -379,17 +405,11 @@ struct MigrationState { bool send_configuration; /* Whether we send section footer during migration */ bool send_section_footer; + /* Whether we send switchover start notification during migration */ + bool send_switchover_start; /* 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. - */ - bool decompress_error_check; /* * This variable only affects behavior when postcopy preempt mode is * enabled. @@ -430,6 +450,39 @@ struct MigrationState { * Default value is false. (since 8.1) */ bool multifd_flush_after_each_section; + + /* + * This variable only makes sense when set on the machine that is + * the destination of a multifd migration with TLS enabled. It + * affects the behavior of the last send->recv iteration with + * regards to termination of the TLS session. + * + * When set: + * + * - the destination QEMU instance can expect to never get a + * GNUTLS_E_PREMATURE_TERMINATION error. Manifested as the error + * message: "The TLS connection was non-properly terminated". + * + * When clear: + * + * - the destination QEMU instance can expect to see a + * GNUTLS_E_PREMATURE_TERMINATION error in any multifd channel + * whenever the last recv() call of that channel happens after + * the source QEMU instance has already issued shutdown() on the + * channel. + * + * Commit 637280aeb2 (since 9.1) introduced a side effect that + * causes the destination instance to not be affected by the + * premature termination, while commit 1d457daf86 (since 10.0) + * causes the premature termination condition to be once again + * reachable. + * + * NOTE: Regardless of the state of this option, a premature + * termination of the TLS connection might happen due to error at + * any moment prior to the last send->recv iteration. + */ + bool multifd_clean_tls_termination; + /* * This decides the size of guest memory chunk that will be used * to track dirty bitmap clearing. The size of memory chunk will @@ -453,30 +506,37 @@ struct MigrationState { * switchover has been received. */ bool switchover_acked; + /* Is this a rdma migration */ + bool rdma_migration; + + GSource *hup_source; }; -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); +bool migrate_has_error(MigrationState *s); -void migrate_fd_connect(MigrationState *s, Error *error_in); +void migration_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); 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 migration_has_failed(MigrationState *); +bool migrate_mode_is_cpr(MigrationState *); uint64_t ram_get_total_transferred_pages(void); @@ -501,7 +561,6 @@ bool check_dirty_bitmap_mig_alias_map(const BitmapMigrationNodeAliasList *bbm, Error **errp); void migrate_add_address(SocketAddress *address); - int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque); #define qemu_ram_foreach_block \ @@ -510,10 +569,29 @@ 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_cancel(const Error *error); +void migration_bh_schedule(QEMUBHFunc *cb, void *opaque); +void migration_cancel(void); void migration_populate_vfio_info(MigrationInfo *info); void migration_reset_vfio_bytes_transferred(void); void postcopy_temp_page_reset(PostcopyTmpPage *tmp_page); +/* + * 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); +bool should_send_vmdesc(void); + #endif diff --git a/migration/multifd-device-state.c b/migration/multifd-device-state.c new file mode 100644 index 00000000000..94222d0eb0d --- /dev/null +++ b/migration/multifd-device-state.c @@ -0,0 +1,212 @@ +/* + * Multifd device state migration + * + * Copyright (C) 2024,2025 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/lockable.h" +#include "block/thread-pool.h" +#include "migration.h" +#include "migration/misc.h" +#include "multifd.h" +#include "options.h" + +static struct { + QemuMutex queue_job_mutex; + + MultiFDSendData *send_data; + + ThreadPool *threads; + bool threads_abort; +} *multifd_send_device_state; + +void multifd_device_state_send_setup(void) +{ + assert(!multifd_send_device_state); + multifd_send_device_state = g_malloc(sizeof(*multifd_send_device_state)); + + qemu_mutex_init(&multifd_send_device_state->queue_job_mutex); + + multifd_send_device_state->send_data = multifd_send_data_alloc(); + + multifd_send_device_state->threads = thread_pool_new(); + multifd_send_device_state->threads_abort = false; +} + +void multifd_device_state_send_cleanup(void) +{ + g_clear_pointer(&multifd_send_device_state->threads, thread_pool_free); + g_clear_pointer(&multifd_send_device_state->send_data, + multifd_send_data_free); + + qemu_mutex_destroy(&multifd_send_device_state->queue_job_mutex); + + g_clear_pointer(&multifd_send_device_state, g_free); +} + +void multifd_send_data_clear_device_state(MultiFDDeviceState_t *device_state) +{ + g_clear_pointer(&device_state->idstr, g_free); + g_clear_pointer(&device_state->buf, g_free); +} + +static void multifd_device_state_fill_packet(MultiFDSendParams *p) +{ + MultiFDDeviceState_t *device_state = &p->data->u.device_state; + MultiFDPacketDeviceState_t *packet = p->packet_device_state; + + packet->hdr.flags = cpu_to_be32(p->flags); + strncpy(packet->idstr, device_state->idstr, sizeof(packet->idstr) - 1); + packet->idstr[sizeof(packet->idstr) - 1] = 0; + packet->instance_id = cpu_to_be32(device_state->instance_id); + packet->next_packet_size = cpu_to_be32(p->next_packet_size); +} + +static void multifd_prepare_header_device_state(MultiFDSendParams *p) +{ + p->iov[0].iov_len = sizeof(*p->packet_device_state); + p->iov[0].iov_base = p->packet_device_state; + p->iovs_num++; +} + +void multifd_device_state_send_prepare(MultiFDSendParams *p) +{ + MultiFDDeviceState_t *device_state = &p->data->u.device_state; + + assert(multifd_payload_device_state(p->data)); + + multifd_prepare_header_device_state(p); + + assert(!(p->flags & MULTIFD_FLAG_SYNC)); + + p->next_packet_size = device_state->buf_len; + if (p->next_packet_size > 0) { + p->iov[p->iovs_num].iov_base = device_state->buf; + p->iov[p->iovs_num].iov_len = p->next_packet_size; + p->iovs_num++; + } + + p->flags |= MULTIFD_FLAG_NOCOMP | MULTIFD_FLAG_DEVICE_STATE; + + multifd_device_state_fill_packet(p); +} + +bool multifd_queue_device_state(char *idstr, uint32_t instance_id, + char *data, size_t len) +{ + /* Device state submissions can come from multiple threads */ + QEMU_LOCK_GUARD(&multifd_send_device_state->queue_job_mutex); + MultiFDDeviceState_t *device_state; + + assert(multifd_payload_empty(multifd_send_device_state->send_data)); + + multifd_set_payload_type(multifd_send_device_state->send_data, + MULTIFD_PAYLOAD_DEVICE_STATE); + device_state = &multifd_send_device_state->send_data->u.device_state; + device_state->idstr = g_strdup(idstr); + device_state->instance_id = instance_id; + device_state->buf = g_memdup2(data, len); + device_state->buf_len = len; + + if (!multifd_send(&multifd_send_device_state->send_data)) { + multifd_send_data_clear(multifd_send_device_state->send_data); + return false; + } + + return true; +} + +bool multifd_device_state_supported(void) +{ + return migrate_multifd() && !migrate_mapped_ram() && + migrate_multifd_compression() == MULTIFD_COMPRESSION_NONE; +} + +static void multifd_device_state_save_thread_data_free(void *opaque) +{ + SaveLiveCompletePrecopyThreadData *data = opaque; + + g_clear_pointer(&data->idstr, g_free); + g_free(data); +} + +static int multifd_device_state_save_thread(void *opaque) +{ + SaveLiveCompletePrecopyThreadData *data = opaque; + g_autoptr(Error) local_err = NULL; + + if (!data->hdlr(data, &local_err)) { + MigrationState *s = migrate_get_current(); + + /* + * Can't call abort_device_state_save_threads() here since new + * save threads could still be in process of being launched + * (if, for example, the very first save thread launched exited + * with an error very quickly). + */ + + assert(local_err); + + /* + * In case of multiple save threads failing which thread error + * return we end setting is purely arbitrary. + */ + migrate_set_error(s, local_err); + } + + return 0; +} + +bool multifd_device_state_save_thread_should_exit(void) +{ + return qatomic_read(&multifd_send_device_state->threads_abort); +} + +void +multifd_spawn_device_state_save_thread(SaveLiveCompletePrecopyThreadHandler hdlr, + char *idstr, uint32_t instance_id, + void *opaque) +{ + SaveLiveCompletePrecopyThreadData *data; + + assert(multifd_device_state_supported()); + assert(multifd_send_device_state); + + assert(!qatomic_read(&multifd_send_device_state->threads_abort)); + + data = g_new(SaveLiveCompletePrecopyThreadData, 1); + data->hdlr = hdlr; + data->idstr = g_strdup(idstr); + data->instance_id = instance_id; + data->handler_opaque = opaque; + + thread_pool_submit_immediate(multifd_send_device_state->threads, + multifd_device_state_save_thread, + data, + multifd_device_state_save_thread_data_free); +} + +void multifd_abort_device_state_save_threads(void) +{ + assert(multifd_device_state_supported()); + + qatomic_set(&multifd_send_device_state->threads_abort, true); +} + +bool multifd_join_device_state_save_threads(void) +{ + MigrationState *s = migrate_get_current(); + + assert(multifd_device_state_supported()); + + thread_pool_wait(multifd_send_device_state->threads); + + return !migrate_has_error(s); +} diff --git a/migration/multifd-nocomp.c b/migration/multifd-nocomp.c new file mode 100644 index 00000000000..b48eae3d861 --- /dev/null +++ b/migration/multifd-nocomp.c @@ -0,0 +1,468 @@ +/* + * 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 "system/ramblock.h" +#include "exec/target_page.h" +#include "file.h" +#include "migration-stats.h" +#include "multifd.h" +#include "options.h" +#include "migration.h" +#include "qapi/error.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "trace.h" +#include "qemu-file.h" + +static MultiFDSendData *multifd_ram_send; + +void multifd_ram_payload_alloc(MultiFDPages_t *pages) +{ + pages->offset = g_new0(ram_addr_t, multifd_ram_page_count()); +} + +void multifd_ram_payload_free(MultiFDPages_t *pages) +{ + g_clear_pointer(&pages->offset, g_free); +} + +void multifd_ram_save_setup(void) +{ + multifd_ram_send = multifd_send_data_alloc(); +} + +void multifd_ram_save_cleanup(void) +{ + g_clear_pointer(&multifd_ram_send, multifd_send_data_free); +} + +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; +} + +static void multifd_ram_prepare_header(MultiFDSendParams *p) +{ + p->iov[0].iov_len = p->packet_len; + p->iov[0].iov_base = p->packet; + p->iovs_num++; +} + +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_ram_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; + } + + stat64_add(&mig_stats.multifd_bytes, p->packet_len); + } + + 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; +} + +/* + * We have two modes for multifd flushes: + * + * - Per-section mode: this is the legacy way to flush, it requires one + * MULTIFD_FLAG_SYNC message for each RAM_SAVE_FLAG_EOS. + * + * - Per-round mode: this is the modern way to flush, it requires one + * MULTIFD_FLAG_SYNC message only for each round of RAM scan. Normally + * it's paired with a new RAM_SAVE_FLAG_MULTIFD_FLUSH message in network + * based migrations. + * + * One thing to mention is mapped-ram always use the modern way to sync. + */ + +/* Do we need a per-section multifd flush (legacy way)? */ +bool multifd_ram_sync_per_section(void) +{ + if (!migrate_multifd()) { + return false; + } + + if (migrate_mapped_ram()) { + return false; + } + + return migrate_multifd_flush_after_each_section(); +} + +/* Do we need a per-round multifd flush (modern way)? */ +bool multifd_ram_sync_per_round(void) +{ + if (!migrate_multifd()) { + return false; + } + + if (migrate_mapped_ram()) { + return true; + } + + return !migrate_multifd_flush_after_each_section(); +} + +int multifd_ram_flush_and_sync(QEMUFile *f) +{ + MultiFDSyncReq req; + int ret; + + if (!migrate_multifd() || migration_in_postcopy()) { + return 0; + } + + if (!multifd_payload_empty(multifd_ram_send)) { + if (!multifd_send(&multifd_ram_send)) { + error_report("%s: multifd_send fail", __func__); + return -1; + } + } + + /* File migrations only need to sync with threads */ + req = migrate_mapped_ram() ? MULTIFD_SYNC_LOCAL : MULTIFD_SYNC_ALL; + + ret = multifd_send_sync_main(req); + if (ret) { + return ret; + } + + /* If we don't need to sync with remote at all, nothing else to do */ + if (req == MULTIFD_SYNC_LOCAL) { + return 0; + } + + /* + * Old QEMUs don't understand RAM_SAVE_FLAG_MULTIFD_FLUSH, it relies + * on RAM_SAVE_FLAG_EOS instead. + */ + if (migrate_multifd_flush_after_each_section()) { + return 0; + } + + qemu_put_be64(f, RAM_SAVE_FLAG_MULTIFD_FLUSH); + qemu_fflush(f); + + return 0; +} + +bool multifd_send_prepare_common(MultiFDSendParams *p) +{ + MultiFDPages_t *pages = &p->data->u.ram; + multifd_ram_prepare_header(p); + multifd_send_zero_page_detect(p); + + if (!pages->normal_num) { + p->next_packet_size = 0; + return false; + } + + 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 00000000000..7419e5dc0d0 --- /dev/null +++ b/migration/multifd-qatzip.c @@ -0,0 +1,395 @@ +/* + * 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 "system/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); + ramblock_recv_bitmap_set_offset(p->block, p->normal[i]); + } + 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 00000000000..52902eb00cc --- /dev/null +++ b/migration/multifd-qpl.c @@ -0,0 +1,712 @@ +/* + * 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 "system/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]; + ramblock_recv_bitmap_set_offset(p->block, p->normal[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 00000000000..fd7cd9b5e8e --- /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 "system/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 = 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 00000000000..4cde8681594 --- /dev/null +++ b/migration/multifd-zero-page.c @@ -0,0 +1,112 @@ +/* + * 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 "system/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]; + bool received = + ramblock_recv_bitmap_test_byte_offset(p->block, p->zero[i]); + + /* + * During multifd migration zero page is written to the memory + * only if it is migrated more than once. + * + * It becomes a problem when both multifd & postcopy options are + * enabled. If the zero page which was skipped during multifd phase, + * is accessed during the postcopy phase of the migration, a page + * fault occurs. But this page fault is not served because the + * 'receivedmap' says the zero page is already received. Thus the + * thread accessing that page may hang. + * + * When postcopy is enabled, always write the zero page as and when + * it is migrated. + */ + if (migrate_postcopy_ram() || received) { + memset(page, 0, multifd_ram_page_size()); + } + if (!received) { + ramblock_recv_bitmap_set_offset(p->block, p->zero[i]); + } + } +} diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index 37ce48621e7..8820b2a787c 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include #include "qemu/rcu.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "exec/target_page.h" #include "qapi/error.h" #include "migration.h" @@ -34,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; @@ -69,64 +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; + 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,8 +119,8 @@ 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], p->page_size); - zs->avail_in = p->page_size; + memcpy(z->buf, pages->block->host + pages->offset[i], page_size); + zs->avail_in = page_size; zs->next_in = z->buf; zs->avail_out = available; @@ -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,26 @@ 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; + struct zlib_data *z = p->compress_data; z_stream *zs = &z->zs; uint32_t in_size = p->next_packet_size; /* we measure the change of total_out */ uint32_t out_size = zs->total_out; - uint32_t expected_size = p->normal_num * p->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; int ret; int i; @@ -256,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) { @@ -269,11 +236,12 @@ 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; } - zs->avail_out = p->page_size; + zs->avail_out = page_size; zs->next_out = p->host + p->normal[i]; /* @@ -287,8 +255,8 @@ static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp) do { ret = inflate(zs, flush); } while (ret == Z_OK && zs->avail_in - && (zs->total_out - start) < p->page_size); - if (ret == Z_OK && (zs->total_out - start) < p->page_size) { + && (zs->total_out - start) < page_size); + if (ret == Z_OK && (zs->total_out - start) < page_size) { error_setg(errp, "multifd %u: inflate generated too few output", p->id); return -1; @@ -305,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 b471daadcd0..3c2dcf76b0f 100644 --- a/migration/multifd-zstd.c +++ b/migration/multifd-zstd.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include #include "qemu/rcu.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "exec/target_page.h" #include "qapi/error.h" #include "migration.h" @@ -37,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); @@ -77,58 +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; + 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 = p->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,43 +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; - uint32_t expected_size = p->normal_num * p->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; @@ -252,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) { @@ -263,8 +228,9 @@ 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 = p->page_size; + z->out.size = page_size; z->out.pos = 0; /* @@ -277,9 +243,9 @@ static int zstd_recv_pages(MultiFDRecvParams *p, Error **errp) */ do { ret = ZSTD_decompressStream(z->zds, &z->out, &z->in); - } while (ret > 0 && (z->in.size - z->in.pos > 0) - && (z->out.pos < p->page_size)); - if (ret > 0 && (z->out.pos < p->page_size)) { + } 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", p->id); return -1; @@ -299,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 0f6b203877f..b2557788555 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -11,15 +11,19 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/iov.h" #include "qemu/rcu.h" #include "exec/target_page.h" -#include "sysemu/sysemu.h" -#include "exec/ramblock.h" +#include "system/system.h" +#include "system/ramblock.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "ram.h" +#include "file.h" +#include "migration/misc.h" #include "migration.h" #include "migration-stats.h" +#include "savevm.h" #include "socket.h" #include "tls.h" #include "qemu-file.h" @@ -28,14 +32,10 @@ #include "threadinfo.h" #include "options.h" #include "qemu/yank.h" +#include "io/channel-file.h" #include "io/channel-socket.h" #include "yank_functions.h" -/* Multiple fd's */ - -#define MULTIFD_MAGIC 0x11223344U -#define MULTIFD_VERSION 1 - typedef struct { uint32_t magic; uint32_t version; @@ -45,130 +45,117 @@ typedef struct { uint64_t unused2[4]; /* Reserved for future use */ } __attribute__((packed)) MultiFDInit_t; -/* Multifd without compression */ +struct { + MultiFDSendParams *params; -/** - * nocomp_send_setup: setup send side - * - * For no compression this function does nothing. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int nocomp_send_setup(MultiFDSendParams *p, Error **errp) -{ - return 0; -} + /* multifd_send() body is not thread safe, needs serialization */ + QemuMutex multifd_send_mutex; -/** - * 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) + /* + * 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; + +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; + MultiFDSendData *new = g_new0(MultiFDSendData, 1); + + multifd_ram_payload_alloc(&new->u.ram); + /* Device state allocates its payload on-demand */ + + return new; } -/** - * 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_data_clear(MultiFDSendData *data) { - MultiFDPages_t *pages = p->pages; + if (multifd_payload_empty(data)) { + return; + } - 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 = p->page_size; - p->iovs_num++; + switch (data->type) { + case MULTIFD_PAYLOAD_DEVICE_STATE: + multifd_send_data_clear_device_state(&data->u.device_state); + break; + default: + /* Nothing to do */ + break; } - p->next_packet_size = p->normal_num * p->page_size; - p->flags |= MULTIFD_FLAG_NOCOMP; - return 0; + data->type = MULTIFD_PAYLOAD_NONE; } -/** - * 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) +void multifd_send_data_free(MultiFDSendData *data) { - return 0; + if (!data) { + return; + } + + /* This also free's device state payload */ + multifd_send_data_clear(data); + + multifd_ram_payload_free(&data->u.ram); + + g_free(data); } -/** - * 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) +static bool multifd_use_packets(void) { + return !migrate_mapped_ram(); } -/** - * 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) +void multifd_send_channel_created(void) { - uint32_t 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; - } - for (int i = 0; i < p->normal_num; i++) { - p->iov[i].iov_base = p->host + p->normal[i]; - p->iov[i].iov_len = p->page_size; - } - return qio_channel_readv_all(p->c, p->iov, p->normal_num, errp); + qemu_sem_post(&multifd_send_state->channels_created); } -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, -}; +static const MultiFDMethods *multifd_ops[MULTIFD_COMPRESSION__MAX] = {}; -void multifd_register_ops(int method, MultiFDMethods *ops) +void multifd_register_ops(int method, const MultiFDMethods *ops) { - assert(0 < method && method < MULTIFD_COMPRESSION__MAX); + assert(0 <= method && method < MULTIFD_COMPRESSION__MAX); + assert(!multifd_ops[method]); multifd_ops[method] = ops; } @@ -188,7 +175,6 @@ static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp) return -1; } stat64_add(&mig_stats.multifd_bytes, size); - stat64_add(&mig_stats.transferred, size); return 0; } @@ -229,181 +215,157 @@ 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) +/* Fills a RAM multifd packet */ +void multifd_send_fill_packet(MultiFDSendParams *p) { - MultiFDPages_t *pages = g_new0(MultiFDPages_t, 1); - - pages->allocated = size; - pages->offset = g_new0(ram_addr_t, size); - - return pages; -} + MultiFDPacket_t *packet = p->packet; + uint64_t packet_num; + bool sync_packet = p->flags & MULTIFD_FLAG_SYNC; -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); -} + memset(packet, 0, p->packet_len); -static void multifd_send_fill_packet(MultiFDSendParams *p) -{ - MultiFDPacket_t *packet = p->packet; - int i; + packet->hdr.magic = cpu_to_be32(MULTIFD_MAGIC); + packet->hdr.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->hdr.flags = cpu_to_be32(p->flags); 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); - for (i = 0; i < p->normal_num; i++) { - /* there are architectures where ram_addr_t is 32 bit */ - uint64_t temp = p->normal[i]; + p->packets_sent++; - packet->offset[i] = cpu_to_be64(temp); + if (!sync_packet) { + multifd_ram_fill_packet(p); } + + trace_multifd_send_fill(p->id, packet_num, + p->flags, p->next_packet_size); } -static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) +static int multifd_recv_unfill_packet_header(MultiFDRecvParams *p, + const MultiFDPacketHdr_t *hdr, + Error **errp) { - MultiFDPacket_t *packet = p->packet; - int i; + uint32_t magic = be32_to_cpu(hdr->magic); + uint32_t version = be32_to_cpu(hdr->version); - 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); + p->flags = be32_to_cpu(hdr->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 > p->page_count) { - error_setg(errp, "multifd: received packet " - "with size %u and expected a size of %u", - packet->pages_alloc, p->page_count) ; - return -1; - } + return 0; +} - 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; - } +static int multifd_recv_unfill_packet_device_state(MultiFDRecvParams *p, + Error **errp) +{ + MultiFDPacketDeviceState_t *packet = p->packet_dev_state; + + packet->instance_id = be32_to_cpu(packet->instance_id); + p->next_packet_size = be32_to_cpu(packet->next_packet_size); + + return 0; +} + +static int multifd_recv_unfill_packet_ram(MultiFDRecvParams *p, Error **errp) +{ + const MultiFDPacket_t *packet = p->packet; + int ret = 0; p->next_packet_size = be32_to_cpu(packet->next_packet_size); p->packet_num = be64_to_cpu(packet->packet_num); - if (p->normal_num == 0) { - return 0; - } + /* Always unfill, old QEMUs (<9.0) send data along with SYNC */ + ret = multifd_ram_unfill_packet(p, errp); - /* 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; - } + trace_multifd_recv_unfill(p->id, p->packet_num, p->flags, + p->next_packet_size); - p->host = p->block->host; - for (i = 0; i < p->normal_num; i++) { - uint64_t offset = be64_to_cpu(packet->offset[i]); + return ret; +} - if (offset > (p->block->used_length - p->page_size)) { - error_setg(errp, "multifd: offset too long %" PRIu64 - " (max " RAM_ADDR_FMT ")", - offset, p->block->used_length); - return -1; - } - p->normal[i] = offset; +static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) +{ + p->packets_recved++; + + if (p->flags & MULTIFD_FLAG_DEVICE_STATE) { + return multifd_recv_unfill_packet_device_state(p, errp); } - return 0; + return multifd_recv_unfill_packet_ram(p, errp); } -struct { - MultiFDSendParams *params; - /* array of pages to sent */ - MultiFDPages_t *pages; - /* global number of generated multifd packets */ - uint64_t packet_num; - /* send channels ready */ - QemuSemaphore channels_ready; - /* - * Have we already run terminate threads. There is a race when it - * happens that we got one error while we are exiting. - * We will use atomic operations. Only valid values are 0 and 1. - */ - int exiting; - /* multifd ops */ - MultiFDMethods *ops; -} *multifd_send_state; +static bool multifd_send_should_exit(void) +{ + return qatomic_read(&multifd_send_state->exiting); +} + +static bool multifd_recv_should_exit(void) +{ + return qatomic_read(&multifd_recv_state->exiting); +} + +/* + * The migration thread can wait on either of the two semaphores. This + * function can be used to kick the main thread out of waiting on either of + * them. Should mostly only be called when something wrong happened with + * the current multifd send thread. + */ +static void multifd_send_kick_main(MultiFDSendParams *p) +{ + qemu_sem_post(&p->sem_sync); + qemu_sem_post(&multifd_send_state->channels_ready); +} /* - * How we use multifd_send_state->pages and channel->pages? + * 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. * - * 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 + * Switching is safe because both the migration thread and the channel + * thread have barriers in place to serialize access. * - * 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. + * Returns true if succeed, false otherwise. */ - -static int multifd_send_pages(QEMUFile *f) +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; + MultiFDSendData *tmp; - if (qatomic_read(&multifd_send_state->exiting)) { - return -1; + if (multifd_send_should_exit()) { + return false; } + QEMU_LOCK_GUARD(&multifd_send_state->multifd_send_mutex); + + /* 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 @@ -411,69 +373,58 @@ 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; - qemu_mutex_unlock(&p->mutex); - qemu_sem_post(&p->sem); - - return 1; -} - -int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset) -{ - MultiFDPages_t *pages = multifd_send_state->pages; - bool changed = false; - if (!pages->block) { - pages->block = block; - } - - if (pages->block == block) { - pages->offset[pages->num] = offset; - pages->num++; + /* + * Make sure we read p->pending_job before all the rest. Pairs with + * qatomic_store_release() in multifd_send_thread(). + */ + smp_mb_acquire(); - if (pages->num < pages->allocated) { - return 1; - } - } else { - changed = true; - } + assert(multifd_payload_empty(p->data)); - if (multifd_send_pages(f) < 0) { - return -1; - } + /* + * 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; - if (changed) { - return multifd_queue_page(f, block, offset); - } + /* + * 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; } -static void multifd_send_terminate_threads(Error *err) +/* Multifd send side hit an error; remember it and prepare to quit */ +static void multifd_send_set_error(Error *err) { - int i; - - trace_multifd_send_terminate_threads(err != NULL); + /* + * We don't want to exit each threads twice. Depending on where + * we get the error, or if there are two independent errors in two + * threads at the same time, we can end calling this function + * twice. + */ + if (qatomic_xchg(&multifd_send_state->exiting, 1)) { + return; + } if (err) { MigrationState *s = migrate_get_current(); @@ -486,81 +437,159 @@ 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); + } + + /* + * Finally recycle all the threads. + */ + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDSendParams *p = &multifd_send_state->params[i]; + + if (p->tls_thread_created) { + qemu_thread_join(&p->tls_thread); + } + + if (p->thread_created) { + qemu_thread_join(&p->thread); + } } } -void multifd_save_cleanup(void) +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 + * migration_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_clear_pointer(&p->data, multifd_send_data_free); + p->packet_len = 0; + g_clear_pointer(&p->packet_device_state, g_free); + 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(); + multifd_device_state_send_cleanup(); + qemu_sem_destroy(&multifd_send_state->channels_created); + qemu_sem_destroy(&multifd_send_state->channels_ready); + qemu_mutex_destroy(&multifd_send_state->multifd_send_mutex); + 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(NULL); + for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; - if (p->running) { - qemu_thread_join(&p->thread); + /* thread_created implies the TLS handshake has succeeded */ + if (p->tls_thread_created && p->thread_created) { + Error *local_err = NULL; + /* + * The destination expects the TLS session to always be + * properly terminated. This helps to detect a premature + * termination in the middle of the stream. Note that + * older QEMUs always break the connection on the source + * and the destination always sees + * GNUTLS_E_PREMATURE_TERMINATION. + */ + migration_tls_channel_end(p->c, &local_err); + + /* + * The above can return an error in case the migration has + * already failed. If the migration succeeded, errors are + * not expected but there's no need to kill the source. + */ + if (local_err && !migration_has_failed(migrate_get_current())) { + warn_report( + "multifd_send_%d: Failed to terminate TLS connection: %s", + p->id, error_get_pretty(local_err)); + break; + } } } + + 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) @@ -580,56 +609,39 @@ static int multifd_zero_copy_flush(QIOChannel *c) return ret; } -int multifd_send_sync_main(QEMUFile *f) +int multifd_send_sync_main(MultiFDSyncReq req) { int i; bool flush_zero_copy; - if (!migrate_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. - */ + assert(req != MULTIFD_SYNC_NONE); 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_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) == MULTIFD_SYNC_NONE); + qatomic_set(&p->pending_sync, req); 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); @@ -649,336 +661,395 @@ static void *multifd_send_thread(void *opaque) MigrationThread *thread = NULL; Error *local_err = NULL; int ret = 0; - bool use_zero_copy_send = migrate_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->normal_num = 0; + /* + * Read pending_job flag before p->data. Pairs with the + * qatomic_store_release() in multifd_send(). + */ + if (qatomic_load_acquire(&p->pending_job)) { + bool is_device_state = multifd_payload_device_state(p->data); + size_t total_size; + int write_flags_masked = 0; - if (use_zero_copy_send) { - p->iovs_num = 0; - } else { - p->iovs_num = 1; - } + p->flags = 0; + p->iovs_num = 0; + assert(!multifd_payload_empty(p->data)); - for (int i = 0; i < p->pages->num; i++) { - p->normal[p->normal_num] = p->pages->offset[i]; - p->normal_num++; - } + if (is_device_state) { + multifd_device_state_send_prepare(p); - if (p->normal_num) { + /* Device state packets cannot be sent via zerocopy */ + write_flags_masked |= QIO_CHANNEL_WRITE_FLAG_ZERO_COPY; + } else { ret = multifd_send_state->ops->send_prepare(p, &local_err); if (ret != 0) { - qemu_mutex_unlock(&p->mutex); break; } } - multifd_send_fill_packet(p); - flags = p->flags; - 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); - trace_multifd_send(p->id, packet_num, p->normal_num, flags, - p->next_packet_size); + /* + * The packet header in the zerocopy RAM case is accounted for + * in multifd_nocomp_send_prepare() - where it is actually + * being sent. + */ + total_size = iov_size(p->iov, p->iovs_num); - if (use_zero_copy_send) { - /* Send header first, without zerocopy */ - ret = qio_channel_write_all(p->c, (void *)p->packet, - p->packet_len, &local_err); - if (ret != 0) { - break; - } - stat64_add(&mig_stats.multifd_bytes, p->packet_len); - stat64_add(&mig_stats.transferred, p->packet_len); + if (migrate_mapped_ram()) { + assert(!is_device_state); + + ret = file_write_ramblock_iov(p->c, p->iov, p->iovs_num, + &p->data->u.ram, &local_err); } else { - /* Send header using the same writev call */ - p->iov[0].iov_len = p->packet_len; - p->iov[0].iov_base = p->packet; + ret = qio_channel_writev_full_all(p->c, p->iov, p->iovs_num, + NULL, 0, + p->write_flags & ~write_flags_masked, + &local_err); } - 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, p->next_packet_size); - stat64_add(&mig_stats.transferred, p->next_packet_size); - qemu_mutex_lock(&p->mutex); - p->pending_job--; - qemu_mutex_unlock(&p->mutex); + stat64_add(&mig_stats.multifd_bytes, total_size); - if (flags & MULTIFD_FLAG_SYNC) { - qemu_sem_post(&p->sem_sync); - } - } else if (p->quit) { - qemu_mutex_unlock(&p->mutex); - break; + p->next_packet_size = 0; + multifd_send_data_clear(p->data); + + /* + * 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 { - qemu_mutex_unlock(&p->mutex); - /* sometimes there are spurious wakeups */ + MultiFDSyncReq req = qatomic_read(&p->pending_sync); + + /* + * 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(req != MULTIFD_SYNC_NONE); + + /* Only push the SYNC message if it involves a remote sync */ + if (req == MULTIFD_SYNC_ALL) { + 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; + } + /* p->next_packet_size will always be zero for a SYNC packet */ + stat64_add(&mig_stats.multifd_bytes, p->packet_len); + } + + qatomic_set(&p->pending_sync, MULTIFD_SYNC_NONE); + 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(); migration_threads_remove(thread); - trace_multifd_send_thread_end(p->id, p->num_packets, p->total_normal_pages); + 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_tls_outgoing_handshake(QIOTask *task, - gpointer opaque) -{ - MultiFDSendParams *p = opaque; - QIOChannel *ioc = QIO_CHANNEL(qio_task_get_source(task)); - Error *err = NULL; +static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque); - 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(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, - 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); + args = g_new0(MultiFDTLSThreadArgs, 1); + args->tioc = tioc; + args->p = p; - if (error) { - return false; - } - 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); - } + 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; } -static void multifd_new_send_channel_cleanup(MultiFDSendParams *p, - QIOChannel *ioc, Error *err) +void multifd_channel_connect(MultiFDSendParams *p, QIOChannel *ioc) { - 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 need to judge whether it is running, so we need to mark - * its status. - */ - p->quit = true; - object_unref(OBJECT(ioc)); - error_free(err); + 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); } +/* + * 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)) { - p->c = sioc; - qio_channel_set_delay(p->c, false); - p->running = true; - if (multifd_channel_connect(p, sioc, local_err)) { + + if (qio_task_propagate_error(task, &local_err)) { + 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; + } + + 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); +} + +static bool multifd_new_send_channel_create(gpointer opaque, Error **errp) +{ + if (!multifd_use_packets()) { + return file_send_channel_create(opaque, errp); } - multifd_new_send_channel_cleanup(p, sioc, local_err); + socket_send_channel_create(multifd_new_send_channel_async, opaque); + return true; } -int multifd_save_setup(Error **errp) +bool multifd_send_setup(void) { - int thread_count; - uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); + 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_multifd()) { - return 0; + 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_mutex_init(&multifd_send_state->multifd_send_mutex); + 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->page_size = qemu_target_page_size(); - p->page_count = page_count; + p->data = multifd_send_data_alloc(); + + if (use_packets) { + p->packet_len = sizeof(MultiFDPacket_t) + + sizeof(uint64_t) * page_count; + p->packet = g_malloc0(p->packet_len); + p->packet_device_state = g_malloc0(sizeof(*p->packet_device_state)); + p->packet_device_state->hdr.magic = cpu_to_be32(MULTIFD_MAGIC); + p->packet_device_state->hdr.version = cpu_to_be32(MULTIFD_VERSION); + } + p->name = g_strdup_printf(MIGRATION_THREAD_SRC_MULTIFD, i); + p->write_flags = 0; - if (migrate_zero_copy_send()) { - p->write_flags = QIO_CHANNEL_WRITE_FLAG_ZERO_COPY; - } else { - p->write_flags = 0; + 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); + } - socket_send_channel_create(multifd_new_send_channel_async, p); + 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; + + multifd_device_state_send_setup(); + + 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) { @@ -986,6 +1057,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); @@ -999,8 +1074,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 @@ -1010,18 +1106,51 @@ 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); } } -void multifd_load_shutdown(void) +void multifd_recv_shutdown(void) { if (migrate_multifd()) { multifd_recv_terminate_threads(NULL); } } -void multifd_load_cleanup(void) +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_clear_pointer(&p->packet_dev_state, g_free); + 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; @@ -1032,56 +1161,65 @@ void multifd_load_cleanup(void) for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDRecvParams *p = &multifd_recv_state->params[i]; - if (p->running) { - /* - * 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); } - - 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; + 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_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) { @@ -1095,54 +1233,198 @@ void multifd_recv_sync_main(void) trace_multifd_recv_sync_main(multifd_recv_state->packet_num); } +static int multifd_device_state_recv(MultiFDRecvParams *p, Error **errp) +{ + g_autofree char *dev_state_buf = NULL; + int ret; + + dev_state_buf = g_malloc(p->next_packet_size); + + ret = qio_channel_read_all(p->c, dev_state_buf, p->next_packet_size, errp); + if (ret != 0) { + return ret; + } + + if (p->packet_dev_state->idstr[sizeof(p->packet_dev_state->idstr) - 1] + != 0) { + error_setg(errp, "unterminated multifd device state idstr"); + return -1; + } + + if (!qemu_loadvm_load_state_buffer(p->packet_dev_state->idstr, + p->packet_dev_state->instance_id, + dev_state_buf, p->next_packet_size, + errp)) { + ret = -1; + } + + return ret; +} + static void *multifd_recv_thread(void *opaque) { + MigrationState *s = migrate_get_current(); 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(); + if (!s->multifd_clean_tls_termination) { + p->read_flags = QIO_CHANNEL_READ_FLAG_RELAXED_EOF; + } + while (true) { - uint32_t flags; + MultiFDPacketHdr_t hdr; + uint32_t flags = 0; + bool is_device_state = false; + bool has_data = false; + uint8_t *pkt_buf; + size_t pkt_len; + + p->normal_num = 0; + + if (use_packets) { + struct iovec iov = { + .iov_base = (void *)&hdr, + .iov_len = sizeof(hdr) + }; + + if (multifd_recv_should_exit()) { + break; + } - if (p->quit) { - break; - } + ret = qio_channel_readv_full_all_eof(p->c, &iov, 1, NULL, NULL, + p->read_flags, &local_err); + if (!ret) { + /* EOF */ + assert(!local_err); + 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; - } + if (ret == -1) { + break; + } + + ret = multifd_recv_unfill_packet_header(p, &hdr, &local_err); + if (ret) { + break; + } + + is_device_state = p->flags & MULTIFD_FLAG_DEVICE_STATE; + if (is_device_state) { + pkt_buf = (uint8_t *)p->packet_dev_state + sizeof(hdr); + pkt_len = sizeof(*p->packet_dev_state) - sizeof(hdr); + } else { + pkt_buf = (uint8_t *)p->packet + sizeof(hdr); + pkt_len = p->packet_len - sizeof(hdr); + } + + ret = qio_channel_read_all_eof(p->c, (char *)pkt_buf, pkt_len, + &local_err); + if (!ret) { + /* EOF */ + error_setg(&local_err, "multifd: unexpected EOF after packet header"); + break; + } + + if (ret == -1) { + break; + } + + 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 (is_device_state) { + has_data = p->next_packet_size > 0; + } else { + /* + * Even if it's a SYNC packet, this needs to be set + * because older QEMUs (<9.0) still send data along with + * the SYNC packet. + */ + has_data = p->normal_num || p->zero_num; + } - qemu_mutex_lock(&p->mutex); - ret = multifd_recv_unfill_packet(p, &local_err); - if (ret) { 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) { + /* + * multifd thread should not be active and receive data + * when migration is in the Postcopy phase. Two threads + * writing the same memory area could easily corrupt + * the guest state. + */ + assert(!migration_in_postcopy()); + if (is_device_state) { + assert(use_packets); + ret = multifd_device_state_recv(p, &local_err); + } else { + ret = multifd_recv_state->ops->recv(p, &local_err); + } if (ret != 0) { break; } + } else if (is_device_state) { + error_setg(&local_err, + "multifd: received empty device state packet"); + 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) { + if (is_device_state) { + error_setg(&local_err, + "multifd: received SYNC device state packet"); + break; + } + + 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); } } @@ -1150,20 +1432,18 @@ 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; /* @@ -1177,7 +1457,12 @@ int multifd_load_setup(Error **errp) 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,26 +1471,30 @@ 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->packet_dev_state = g_malloc0(sizeof(*p->packet_dev_state)); + } + p->name = g_strdup_printf(MIGRATION_THREAD_DST_MULTIFD, i); p->normal = g_new0(ram_addr_t, page_count); - p->page_count = page_count; - p->page_size = qemu_target_page_size(); + 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; } } @@ -1236,18 +1525,23 @@ 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; + 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) { @@ -1259,10 +1553,8 @@ void multifd_recv_new_channel(QIOChannel *ioc, Error **errp) } 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); diff --git a/migration/multifd.h b/migration/multifd.h index a835643b48c..9b6d81e7ede 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -13,26 +13,65 @@ #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); -void multifd_load_cleanup(void); -void multifd_load_shutdown(void); +#include "exec/target_page.h" +#include "ram.h" + +typedef struct MultiFDRecvData MultiFDRecvData; +typedef struct MultiFDSendData MultiFDSendData; + +typedef enum { + /* No sync request */ + MULTIFD_SYNC_NONE = 0, + /* Sync locally on the sender threads without pushing messages */ + MULTIFD_SYNC_LOCAL, + /* + * Sync not only on the sender threads, but also push MULTIFD_FLAG_SYNC + * message to the wire for each iochannel (which is for a remote sync). + * + * When remote sync is used, need to be paired with a follow up + * RAM_SAVE_FLAG_EOS / RAM_SAVE_FLAG_MULTIFD_FLUSH message on the main + * channel. + */ + MULTIFD_SYNC_ALL, +} MultiFDSyncReq; + +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); 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(MultiFDSyncReq req); +bool multifd_queue_page(RAMBlock *block, ram_addr_t offset); +bool multifd_recv(void); +MultiFDRecvData *multifd_get_recv_data(void); + +/* Multiple fd's */ + +#define MULTIFD_MAGIC 0x11223344U +#define MULTIFD_VERSION 1 /* 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) + +/* + * If set it means that this packet contains device state + * (MultiFDPacketDeviceState_t), not RAM data (MultiFDPacket_t). + */ +#define MULTIFD_FLAG_DEVICE_STATE (32 << 1) /* This value needs to be a multiple of qemu_target_page_size() */ #define MULTIFD_PACKET_SIZE (512 * 1024) @@ -41,6 +80,11 @@ typedef struct { uint32_t magic; uint32_t version; uint32_t flags; +} __attribute__((packed)) MultiFDPacketHdr_t; + +typedef struct { + MultiFDPacketHdr_t hdr; + /* maximum number of allocated pages */ uint32_t pages_alloc; /* non zero pages */ @@ -48,23 +92,92 @@ 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 { + MultiFDPacketHdr_t hdr; + + char idstr[256]; + uint32_t instance_id; + + /* size of the next packet that contains the actual data */ + uint32_t next_packet_size; +} __attribute__((packed)) MultiFDPacketDeviceState_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; + /* + * Pointer to the ramblock. NOTE: it's caller's responsibility to make + * sure the pointer is always valid! + */ RAMBlock *block; + /* offset array of each page, managed by multifd */ + ram_addr_t *offset; } MultiFDPages_t; +struct MultiFDRecvData { + void *opaque; + size_t size; + /* for preadv */ + off_t file_offset; +}; + +typedef struct { + char *idstr; + uint32_t instance_id; + char *buf; + size_t buf_len; +} MultiFDDeviceState_t; + +typedef enum { + MULTIFD_PAYLOAD_NONE, + MULTIFD_PAYLOAD_RAM, + MULTIFD_PAYLOAD_DEVICE_STATE, +} MultiFDPayloadType; + +typedef struct MultiFDPayload { + MultiFDPages_t ram; + MultiFDDeviceState_t device_state; +} MultiFDPayload; + +struct MultiFDSendData { + MultiFDPayloadType type; + MultiFDPayload u; +}; + +static inline bool multifd_payload_empty(MultiFDSendData *data) +{ + return data->type == MULTIFD_PAYLOAD_NONE; +} + +static inline bool multifd_payload_device_state(MultiFDSendData *data) +{ + return data->type == MULTIFD_PAYLOAD_DEVICE_STATE; +} + +static inline void multifd_set_payload_type(MultiFDSendData *data, + MultiFDPayloadType type) +{ + assert(multifd_payload_empty(data)); + assert(type != MULTIFD_PAYLOAD_NONE); + + data->type = type; +} + typedef struct { /* Fields are only written at creating/deletion time */ /* No lock required for them, they are read only */ @@ -75,16 +188,13 @@ 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; - /* guest page size */ - uint32_t page_size; - /* number of pages in a full packet */ - uint32_t page_count; /* multifd flags for sending ram */ int write_flags; @@ -93,45 +203,37 @@ 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 field 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; + MultiFDSyncReq pending_sync; + + MultiFDSendData *data; /* thread local variables. No locking required */ - /* pointer to the packet */ + /* pointers to the possible packet types */ MultiFDPacket_t *packet; + MultiFDPacketDeviceState_t *packet_device_state; /* 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 { @@ -144,69 +246,173 @@ typedef struct { char *name; /* channel thread id */ QemuThread thread; + bool thread_created; /* communication channel */ QIOChannel *c; /* packet allocated len */ uint32_t packet_len; - /* guest page size */ - uint32_t page_size; - /* number of pages in a full packet */ - uint32_t page_count; /* syncs main thread and channels */ QemuSemaphore sem_sync; + /* sem where to wait for more work */ + QemuSemaphore sem; /* this mutex protects the following parameters */ QemuMutex mutex; - /* 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 */ - /* pointer to the packet */ + /* pointers to the possible packet types */ MultiFDPacket_t *packet; + MultiFDPacketDeviceState_t *packet_dev_state; /* 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; + /* Flags for the QIOChannel */ + int read_flags; } 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); -#endif +void multifd_channel_connect(MultiFDSendParams *p, QIOChannel *ioc); +bool multifd_send(MultiFDSendData **send_data); +MultiFDSendData *multifd_send_data_alloc(void); +void multifd_send_data_clear(MultiFDSendData *data); +void multifd_send_data_free(MultiFDSendData *data); + +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(QEMUFile *f); +bool multifd_ram_sync_per_round(void); +bool multifd_ram_sync_per_section(void); +void multifd_ram_payload_alloc(MultiFDPages_t *pages); +void multifd_ram_payload_free(MultiFDPages_t *pages); +void multifd_ram_fill_packet(MultiFDSendParams *p); +int multifd_ram_unfill_packet(MultiFDRecvParams *p, Error **errp); +void multifd_send_data_clear_device_state(MultiFDDeviceState_t *device_state); + +void multifd_device_state_send_setup(void); +void multifd_device_state_send_cleanup(void); + +void multifd_device_state_send_prepare(MultiFDSendParams *p); + +#endif diff --git a/migration/options.c b/migration/options.c index 1d1e1321b0a..162c72cda45 100644 --- a/migration/options.c +++ b/migration/options.c @@ -12,22 +12,24 @@ */ #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 "qobject/qnull.h" +#include "system/runstate.h" #include "migration/colo.h" +#include "migration/cpr.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" +#include "system/kvm.h" /* Maximum migrate downtime set to 2000 seconds */ #define MAX_MIGRATE_DOWNTIME_SECONDS 2000 @@ -39,13 +41,6 @@ * for sending the last part */ #define DEFAULT_MIGRATE_SET_DOWNTIME 300 -/* Default compression thread count */ -#define DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT 8 -/* Default decompression thread count, usually decompression is at - * least 4 times as fast as compression.*/ -#define DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT 2 -/*0: means nocompress, 1: best speed, ... 9: best compress ratio */ -#define DEFAULT_MIGRATE_COMPRESS_LEVEL 1 /* Define default autoconverge cpu throttle migration parameters */ #define DEFAULT_MIGRATE_THROTTLE_TRIGGER_THRESHOLD 50 #define DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL 20 @@ -61,6 +56,13 @@ #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 @@ -84,34 +86,25 @@ #define DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT_PERIOD 1000 /* milliseconds */ #define DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT 1 /* MB/s */ -Property migration_properties[] = { +const Property migration_properties[] = { DEFINE_PROP_BOOL("store-global-state", MigrationState, store_global_state, true), DEFINE_PROP_BOOL("send-configuration", MigrationState, send_configuration, true), DEFINE_PROP_BOOL("send-section-footer", MigrationState, send_section_footer, true), - DEFINE_PROP_BOOL("decompress-error-check", MigrationState, - decompress_error_check, true), + DEFINE_PROP_BOOL("send-switchover-start", MigrationState, + send_switchover_start, 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), + DEFINE_PROP_BOOL("multifd-clean-tls-termination", MigrationState, + multifd_clean_tls_termination, true), /* 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), @@ -125,6 +118,8 @@ Property migration_properties[] = { 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), @@ -140,6 +135,9 @@ Property migration_properties[] = { 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), @@ -173,20 +171,24 @@ Property migration_properties[] = { DEFINE_PROP_UINT64("vcpu-dirty-limit", MigrationState, parameters.vcpu_dirty_limit, DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT), + DEFINE_PROP_MIG_MODE("mode", MigrationState, + parameters.mode, + MIG_MODE_NORMAL), + DEFINE_PROP_ZERO_PAGE_DETECTION("zero-page-detection", MigrationState, + parameters.zero_page_detection, + ZERO_PAGE_DETECTION_MULTIFD), /* Migration capabilities */ DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE), DEFINE_PROP_MIG_CAP("x-rdma-pin-all", MIGRATION_CAPABILITY_RDMA_PIN_ALL), DEFINE_PROP_MIG_CAP("x-auto-converge", MIGRATION_CAPABILITY_AUTO_CONVERGE), DEFINE_PROP_MIG_CAP("x-zero-blocks", MIGRATION_CAPABILITY_ZERO_BLOCKS), - DEFINE_PROP_MIG_CAP("x-compress", MIGRATION_CAPABILITY_COMPRESS), DEFINE_PROP_MIG_CAP("x-events", MIGRATION_CAPABILITY_EVENTS), DEFINE_PROP_MIG_CAP("x-postcopy-ram", MIGRATION_CAPABILITY_POSTCOPY_RAM), DEFINE_PROP_MIG_CAP("x-postcopy-preempt", MIGRATION_CAPABILITY_POSTCOPY_PREEMPT), DEFINE_PROP_MIG_CAP("x-colo", MIGRATION_CAPABILITY_X_COLO), DEFINE_PROP_MIG_CAP("x-release-ram", MIGRATION_CAPABILITY_RELEASE_RAM), - DEFINE_PROP_MIG_CAP("x-block", MIGRATION_CAPABILITY_BLOCK), DEFINE_PROP_MIG_CAP("x-return-path", MIGRATION_CAPABILITY_RETURN_PATH), DEFINE_PROP_MIG_CAP("x-multifd", MIGRATION_CAPABILITY_MULTIFD), DEFINE_PROP_MIG_CAP("x-background-snapshot", @@ -198,8 +200,9 @@ Property migration_properties[] = { DEFINE_PROP_MIG_CAP("x-switchover-ack", MIGRATION_CAPABILITY_SWITCHOVER_ACK), DEFINE_PROP_MIG_CAP("x-dirty-limit", MIGRATION_CAPABILITY_DIRTY_LIMIT), - DEFINE_PROP_END_OF_LIST(), + DEFINE_PROP_MIG_CAP("mapped-ram", MIGRATION_CAPABILITY_MAPPED_RAM), }; +const size_t migration_properties_count = ARRAY_SIZE(migration_properties); bool migrate_auto_converge(void) { @@ -208,18 +211,18 @@ bool migrate_auto_converge(void) return s->capabilities[MIGRATION_CAPABILITY_AUTO_CONVERGE]; } -bool migrate_background_snapshot(void) +bool migrate_send_switchover_start(void) { MigrationState *s = migrate_get_current(); - return s->capabilities[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT]; + return s->send_switchover_start; } -bool migrate_block(void) +bool migrate_background_snapshot(void) { MigrationState *s = migrate_get_current(); - return s->capabilities[MIGRATION_CAPABILITY_BLOCK]; + return s->capabilities[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT]; } bool migrate_colo(void) @@ -229,13 +232,6 @@ bool migrate_colo(void) return s->capabilities[MIGRATION_CAPABILITY_X_COLO]; } -bool migrate_compress(void) -{ - MigrationState *s = migrate_get_current(); - - return s->capabilities[MIGRATION_CAPABILITY_COMPRESS]; -} - bool migrate_dirty_bitmaps(void) { MigrationState *s = migrate_get_current(); @@ -257,6 +253,13 @@ bool migrate_events(void) 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(); @@ -348,13 +351,6 @@ bool migrate_xbzrle(void) return s->capabilities[MIGRATION_CAPABILITY_XBZRLE]; } -bool migrate_zero_blocks(void) -{ - MigrationState *s = migrate_get_current(); - - return s->capabilities[MIGRATION_CAPABILITY_ZERO_BLOCKS]; -} - bool migrate_zero_copy_send(void) { MigrationState *s = migrate_get_current(); @@ -376,6 +372,13 @@ 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(); @@ -435,7 +438,6 @@ INITIALIZE_MIGRATE_CAPS_SET(check_caps_background_snapshot, 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, @@ -446,6 +448,24 @@ static bool migrate_incoming_started(void) return !!migration_incoming_get_current()->transport_data; } +bool migrate_rdma_caps_check(bool *caps, Error **errp) +{ + if (caps[MIGRATION_CAPABILITY_XBZRLE]) { + error_setg(errp, "RDMA and XBZRLE can't be used together"); + return false; + } + if (caps[MIGRATION_CAPABILITY_MULTIFD]) { + error_setg(errp, "RDMA and multifd can't be used together"); + return false; + } + if (caps[MIGRATION_CAPABILITY_POSTCOPY_RAM]) { + error_setg(errp, "RDMA and postcopy-ram can't be used together"); + return false; + } + + return true; +} + /** * @migration_caps_check - check capability compatibility * @@ -457,17 +477,12 @@ static bool migrate_incoming_started(void) */ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) { + ERRP_GUARD(); MigrationIncomingState *mis = migration_incoming_get_current(); - ERRP_GUARD(); -#ifndef CONFIG_LIVE_BLOCK_MIGRATION - if (new_caps[MIGRATION_CAPABILITY_BLOCK]) { - error_setg(errp, "QEMU compiled without old-style (blk/-b, inc/-i) " - "block migration"); - error_append_hint(errp, "Use drive_mirror+NBD instead.\n"); - return false; + if (new_caps[MIGRATION_CAPABILITY_ZERO_BLOCKS]) { + warn_report("zero-blocks capability is deprecated"); } -#endif #ifndef CONFIG_REPLICATION if (new_caps[MIGRATION_CAPABILITY_X_COLO]) { @@ -494,11 +509,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) 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]) { @@ -537,7 +547,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) #ifdef CONFIG_LINUX if (new_caps[MIGRATION_CAPABILITY_ZERO_COPY_SEND] && (!new_caps[MIGRATION_CAPABILITY_MULTIFD] || - new_caps[MIGRATION_CAPABILITY_COMPRESS] || new_caps[MIGRATION_CAPABILITY_XBZRLE] || migrate_multifd_compression() || migrate_tls())) { @@ -559,18 +568,7 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) return false; } - /* - * Preempt mode requires urgent pages to be sent in separate - * channel, OTOH compression logic will disorder all pages into - * different compression channels, which is not compatible with the - * preempt assumptions on channel assignments. - */ - if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { - error_setg(errp, "Postcopy preempt not compatible with compress"); - return false; - } - - if (migrate_incoming_started()) { + if (!migrate_postcopy_preempt() && migrate_incoming_started()) { error_setg(errp, "Postcopy preempt must be set before incoming starts"); return false; @@ -578,11 +576,7 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) } if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) { - if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) { - error_setg(errp, "Multifd is not compatible with compress"); - return false; - } - if (migrate_incoming_started()) { + if (!migrate_multifd() && migrate_incoming_started()) { error_setg(errp, "Multifd must be set before incoming starts"); return false; } @@ -609,26 +603,34 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) } } - return true; -} + if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) { + if (new_caps[MIGRATION_CAPABILITY_XBZRLE]) { + error_setg(errp, "Multifd is not compatible with xbzrle"); + return false; + } + } -bool migrate_cap_set(int cap, bool value, Error **errp) -{ - MigrationState *s = migrate_get_current(); - bool new_caps[MIGRATION_CAPABILITY__MAX]; + 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 (migration_is_running(s->state)) { - error_setg(errp, QERR_MIGRATION_ACTIVE); - return false; + if (new_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM]) { + error_setg(errp, + "Mapped-ram migration is incompatible with postcopy"); + return false; + } } - memcpy(new_caps, s->capabilities, sizeof(new_caps)); - new_caps[cap] = value; - - if (!migrate_caps_check(s->capabilities, new_caps, errp)) { + /* + * On destination side, check the cases that capability is being set + * after incoming thread has started. + */ + if (migrate_rdma() && !migrate_rdma_caps_check(new_caps, errp)) { return false; } - s->capabilities[cap] = value; return true; } @@ -640,11 +642,6 @@ MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp) int i; for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { -#ifndef CONFIG_LIVE_BLOCK_MIGRATION - if (i == MIGRATION_CAPABILITY_BLOCK) { - continue; - } -#endif caps = g_malloc0(sizeof(*caps)); caps->capability = i; caps->state = s->capabilities[i]; @@ -661,8 +658,8 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, MigrationCapabilityStatusList *cap; bool new_caps[MIGRATION_CAPABILITY__MAX]; - if (migration_is_running(s->state) || migration_in_colo_state()) { - error_setg(errp, QERR_MIGRATION_ACTIVE); + if (migration_is_running() || migration_in_colo_state()) { + error_setg(errp, "There's a migration process in progress"); return; } @@ -696,13 +693,6 @@ bool migrate_has_block_bitmap_mapping(void) return s->parameters.has_block_bitmap_mapping; } -bool migrate_block_incremental(void) -{ - MigrationState *s = migrate_get_current(); - - return s->parameters.block_incremental; -} - uint32_t migrate_checkpoint_delay(void) { MigrationState *s = migrate_get_current(); @@ -710,27 +700,6 @@ uint32_t migrate_checkpoint_delay(void) return s->parameters.x_checkpoint_delay; } -int migrate_compress_level(void) -{ - MigrationState *s = migrate_get_current(); - - return s->parameters.compress_level; -} - -int migrate_compress_threads(void) -{ - MigrationState *s = migrate_get_current(); - - return s->parameters.compress_threads; -} - -int migrate_compress_wait_thread(void) -{ - MigrationState *s = migrate_get_current(); - - return s->parameters.compress_wait_thread; -} - uint8_t migrate_cpu_throttle_increment(void) { MigrationState *s = migrate_get_current(); @@ -752,11 +721,23 @@ bool migrate_cpu_throttle_tailslow(void) return s->parameters.cpu_throttle_tailslow; } -int migrate_decompress_threads(void) +bool migrate_direct_io(void) { MigrationState *s = migrate_get_current(); - return s->parameters.decompress_threads; + /* + * 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) @@ -780,6 +761,13 @@ uint64_t migrate_max_bandwidth(void) 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(); @@ -787,6 +775,18 @@ uint64_t migrate_max_postcopy_bandwidth(void) return s->parameters.max_postcopy_bandwidth; } +MigMode migrate_mode(void) +{ + MigMode mode = cpr_get_incoming_mode(); + + if (mode == MIG_MODE_NONE) { + mode = migrate_get_current()->parameters.mode; + } + + assert(mode >= 0 && mode < MIG_MODE__MAX); + return mode; +} + int migrate_multifd_channels(void) { MigrationState *s = migrate_get_current(); @@ -809,6 +809,13 @@ int migrate_multifd_zlib_level(void) 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(); @@ -844,36 +851,29 @@ const char *migrate_tls_hostname(void) return s->parameters.tls_hostname; } -uint64_t migrate_xbzrle_cache_size(void) +uint64_t migrate_vcpu_dirty_limit_period(void) { MigrationState *s = migrate_get_current(); - return s->parameters.xbzrle_cache_size; + return s->parameters.x_vcpu_dirty_limit_period; } -/* parameter setters */ - -void migrate_set_block_incremental(bool value) +uint64_t migrate_xbzrle_cache_size(void) { MigrationState *s = migrate_get_current(); - s->parameters.block_incremental = value; + return s->parameters.xbzrle_cache_size; } -/* parameters helpers */ - -void block_cleanup_parameters(void) +ZeroPageDetection migrate_zero_page_detection(void) { MigrationState *s = migrate_get_current(); - if (s->must_remove_block_options) { - /* setting to false can never fail */ - migrate_cap_set(MIGRATION_CAPABILITY_BLOCK, false, &error_abort); - migrate_set_block_incremental(false); - s->must_remove_block_options = false; - } + return s->parameters.zero_page_detection; } +/* parameters helpers */ + AnnounceParameters *migrate_announce_params(void) { static AnnounceParameters ap; @@ -895,14 +895,6 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) /* 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; @@ -917,18 +909,20 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) s->parameters.tls_authz : ""); params->has_max_bandwidth = true; params->max_bandwidth = s->parameters.max_bandwidth; + params->has_avail_switchover_bandwidth = true; + params->avail_switchover_bandwidth = s->parameters.avail_switchover_bandwidth; params->has_downtime_limit = true; params->downtime_limit = s->parameters.downtime_limit; params->has_x_checkpoint_delay = true; params->x_checkpoint_delay = s->parameters.x_checkpoint_delay; - params->has_block_incremental = true; - params->block_incremental = s->parameters.block_incremental; params->has_multifd_channels = true; params->multifd_channels = s->parameters.multifd_channels; params->has_multifd_compression = true; params->multifd_compression = s->parameters.multifd_compression; params->has_multifd_zlib_level = true; params->multifd_zlib_level = s->parameters.multifd_zlib_level; + params->has_multifd_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; @@ -957,6 +951,12 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) 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; } @@ -967,10 +967,6 @@ void migrate_params_init(MigrationParameters *params) 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; @@ -978,10 +974,10 @@ void migrate_params_init(MigrationParameters *params) 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_qatzip_level = true; params->has_multifd_zstd_level = true; params->has_xbzrle_cache_size = true; params->has_max_postcopy_bandwidth = true; @@ -992,6 +988,9 @@ void migrate_params_init(MigrationParameters *params) 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; } /* @@ -1000,26 +999,7 @@ void migrate_params_init(MigrationParameters *params) */ 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; - } + ERRP_GUARD(); if (params->has_throttle_trigger_threshold && (params->throttle_trigger_threshold < 1 || @@ -1056,6 +1036,15 @@ bool migrate_params_check(MigrationParameters *params, Error **errp) 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, @@ -1081,6 +1070,14 @@ bool migrate_params_check(MigrationParameters *params, Error **errp) 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", @@ -1152,6 +1149,13 @@ bool migrate_params_check(MigrationParameters *params, Error **errp) } #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)) { @@ -1163,9 +1167,13 @@ bool migrate_params_check(MigrationParameters *params, Error **errp) if (params->has_vcpu_dirty_limit && (params->vcpu_dirty_limit < 1)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "vcpu_dirty_limit", - "is invalid, it must greater then 1 MB/s"); + 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; } @@ -1179,22 +1187,6 @@ static void migrate_params_test_apply(MigrateSetParameters *params, /* 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; } @@ -1221,10 +1213,19 @@ static void migrate_params_test_apply(MigrateSetParameters *params, dest->tls_hostname = params->tls_hostname->u.s; } + if (params->tls_authz) { + assert(params->tls_authz->type == QTYPE_QSTRING); + dest->tls_authz = params->tls_authz->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; } @@ -1233,15 +1234,21 @@ static void migrate_params_test_apply(MigrateSetParameters *params, dest->x_checkpoint_delay = params->x_checkpoint_delay; } - if (params->has_block_incremental) { - dest->block_incremental = params->block_incremental; - } if (params->has_multifd_channels) { dest->multifd_channels = params->multifd_channels; } if (params->has_multifd_compression) { dest->multifd_compression = params->multifd_compression; } + if (params->has_multifd_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; } @@ -1276,29 +1283,25 @@ static void migrate_params_test_apply(MigrateSetParameters *params, if (params->has_vcpu_dirty_limit) { dest->vcpu_dirty_limit = params->vcpu_dirty_limit; } -} - -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_mode) { + dest->mode = params->mode; } - if (params->has_compress_threads) { - s->parameters.compress_threads = params->compress_threads; + if (params->has_zero_page_detection) { + dest->zero_page_detection = params->zero_page_detection; } - if (params->has_compress_wait_thread) { - s->parameters.compress_wait_thread = params->compress_wait_thread; + if (params->has_direct_io) { + dest->direct_io = params->direct_io; } +} - if (params->has_decompress_threads) { - s->parameters.decompress_threads = params->decompress_threads; - } +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; @@ -1341,6 +1344,10 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) } } + 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; } @@ -1350,15 +1357,21 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) colo_checkpoint_delay_set(); } - 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_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); @@ -1402,26 +1415,43 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) 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 */ + /* 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(""); } - /* TODO Rewrite "" to null instead */ 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); diff --git a/migration/options.h b/migration/options.h index 045e2a41a28..82d839709e7 100644 --- a/migration/options.h +++ b/migration/options.h @@ -16,35 +16,31 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" +#include "migration/client-options.h" /* migration properties */ -extern Property migration_properties[]; +extern const Property migration_properties[]; +extern const size_t migration_properties_count; /* capabilities */ bool migrate_auto_converge(void); -bool migrate_background_snapshot(void); -bool migrate_block(void); bool migrate_colo(void); -bool migrate_compress(void); bool migrate_dirty_bitmaps(void); -bool migrate_dirty_limit(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_postcopy_ram(void); bool migrate_rdma_pin_all(void); bool migrate_release_ram(void); bool migrate_return_path(void); -bool migrate_switchover_ack(void); bool migrate_validate_uuid(void); bool migrate_xbzrle(void); -bool migrate_zero_blocks(void); bool migrate_zero_copy_send(void); /* @@ -56,49 +52,43 @@ bool migrate_zero_copy_send(void); bool migrate_multifd_flush_after_each_section(void); bool migrate_postcopy(void); +bool migrate_rdma(void); bool migrate_tls(void); /* capabilities helpers */ +bool migrate_rdma_caps_check(bool *caps, Error **errp); bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp); -bool migrate_cap_set(int cap, bool value, Error **errp); /* parameters */ const BitmapMigrationNodeAliasList *migrate_block_bitmap_mapping(void); bool migrate_has_block_bitmap_mapping(void); -bool migrate_block_incremental(void); uint32_t migrate_checkpoint_delay(void); -int migrate_compress_level(void); -int migrate_compress_threads(void); -int migrate_compress_wait_thread(void); uint8_t migrate_cpu_throttle_increment(void); uint8_t migrate_cpu_throttle_initial(void); bool migrate_cpu_throttle_tailslow(void); -int migrate_decompress_threads(void); +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); - -/* parameters setters */ - -void migrate_set_block_incremental(bool value); +ZeroPageDetection migrate_zero_page_detection(void); /* parameters helpers */ bool migrate_params_check(MigrationParameters *params, Error **errp); void migrate_params_init(MigrationParameters *params); -void block_cleanup_parameters(void); - #endif diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 29aea9456d6..75fd310fb2b 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -27,11 +27,11 @@ #include "qapi/error.h" #include "qemu/notify.h" #include "qemu/rcu.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/error-report.h" #include "trace.h" #include "hw/boards.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "socket.h" #include "yank_functions.h" #include "tls.h" @@ -44,7 +44,7 @@ */ #define MAX_DISCARDS_PER_COMMAND 12 -struct PostcopyDiscardState { +typedef struct PostcopyDiscardState { const char *ramblock_name; uint16_t cur_entry; /* @@ -54,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; @@ -77,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); } /* @@ -91,10 +90,10 @@ void postcopy_thread_create(MigrationIncomingState *mis, QemuThread *thread, const char *name, void *(*fn)(void *), int joinable) { - qemu_sem_init(&mis->thread_sync_sem, 0); + qemu_event_init(&mis->thread_sync_event, false); qemu_thread_create(thread, name, fn, mis, joinable); - qemu_sem_wait(&mis->thread_sync_sem); - qemu_sem_destroy(&mis->thread_sync_sem); + qemu_event_wait(&mis->thread_sync_event); + qemu_event_destroy(&mis->thread_sync_event); } /* Postcopy needs to detect accesses to pages that haven't yet been copied @@ -102,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) @@ -272,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)); @@ -286,10 +283,10 @@ static bool request_ufd_features(int ufd, uint64_t features) static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis, Error **errp) { + ERRP_GUARD(); uint64_t asked_features = 0; static uint64_t supported_features; - ERRP_GUARD(); /* * it's not possible to * request UFFD_API twice per one fd @@ -374,6 +371,7 @@ static int test_ramblock_postcopiable(RAMBlock *rb, Error **errp) */ 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 */ @@ -383,7 +381,6 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis, Error **errp) uint64_t feature_mask; RAMBlock *block; - ERRP_GUARD(); if (qemu_target_page_size() > pagesize) { error_setg(errp, "Target page size bigger than host page size"); goto out; @@ -462,9 +459,9 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis, Error **errp) 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_setg(errp, "Missing userfault map features: %" PRIx64, (uint64_t)(~reg_struct.ioctls & feature_mask)); @@ -599,6 +596,30 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) 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); } @@ -630,8 +651,8 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) mis->have_fault_thread = false; } - if (enable_mlock) { - if (os_mlock() < 0) { + if (should_mlock(mlock_state)) { + if (os_mlock(is_mlock_on_fault(mlock_state)) < 0) { error_report("mlock: %s", strerror(errno)); /* * It doesn't feel right to fail at this point, we have a valid @@ -709,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); } @@ -725,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, @@ -951,7 +964,7 @@ static void *postcopy_ram_fault_thread(void *opaque) trace_postcopy_ram_fault_thread_entry(); rcu_register_thread(); mis->last_rb = NULL; /* last RAMBlock we sent part of */ - qemu_sem_post(&mis->thread_sync_sem); + qemu_event_set(&mis->thread_sync_event); struct pollfd *pfd; size_t pfd_len = 2 + mis->postcopy_remote_fds->len; @@ -1217,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; @@ -1237,7 +1251,8 @@ 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->preempt_thread_status = PREEMPT_THREAD_CREATED; } @@ -1254,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); @@ -1277,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); @@ -1310,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); @@ -1343,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, @@ -1378,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 @@ -1708,7 +1716,7 @@ void *postcopy_preempt_thread(void *opaque) rcu_register_thread(); - qemu_sem_post(&mis->thread_sync_sem); + qemu_event_set(&mis->thread_sync_event); /* * The preempt channel is established in asynchronous way. Wait @@ -1737,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 442ab89752a..a6df1b2811b 100644 --- a/migration/postcopy-ram.h +++ b/migration/postcopy-ram.h @@ -13,6 +13,8 @@ #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, Error **errp); @@ -128,7 +130,6 @@ enum PostcopyNotifyReason { struct PostcopyNotifyData { enum PostcopyNotifyReason reason; - Error **errp; }; void postcopy_add_notifier(NotifierWithReturn *nn); @@ -194,5 +195,6 @@ enum PostcopyChannels { 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 19c33c99858..b6ac190034f 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include #include "qemu/madvise.h" #include "qemu/error-report.h" #include "qemu/iov.h" @@ -32,18 +31,21 @@ #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) +typedef struct FdEntry { + QTAILQ_ENTRY(FdEntry) entry; + int fd; +} FdEntry; + struct QEMUFile { - const QEMUFileHooks *hooks; QIOChannel *ioc; bool is_writable; - /* The sum of bytes transferred on the wire */ - uint64_t total_transferred; - int buf_index; int buf_size; /* 0 when writing */ uint8_t buf[IO_BUF_SIZE]; @@ -54,6 +56,9 @@ struct QEMUFile { int last_error; Error *last_error_obj; + + bool can_pass_fd; + QTAILQ_HEAD(, FdEntry) fds; }; /* @@ -65,6 +70,8 @@ struct QEMUFile { */ int qemu_file_shutdown(QEMUFile *f) { + Error *err = NULL; + /* * We must set qemufile error before the real shutdown(), otherwise * there can be a race window where we thought IO all went though @@ -93,7 +100,8 @@ int qemu_file_shutdown(QEMUFile *f) return -ENOSYS; } - if (qio_channel_shutdown(f->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL) < 0) { + if (qio_channel_shutdown(f->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, &err) < 0) { + error_report_err(err); return -EIO; } @@ -109,6 +117,8 @@ static QEMUFile *qemu_file_new_impl(QIOChannel *ioc, bool is_writable) object_ref(ioc); f->ioc = ioc; f->is_writable = is_writable; + f->can_pass_fd = qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS); + QTAILQ_INIT(&f->fds); return f; } @@ -132,25 +142,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. */ -static int qemu_file_get_error_obj(QEMUFile *f, Error **errp) +int qemu_file_get_error_obj(QEMUFile *f, Error **errp) { + if (!f->last_error) { + return 0; + } + + /* There is an error */ if (errp) { - *errp = f->last_error_obj ? error_copy(f->last_error_obj) : NULL; + 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; } @@ -203,7 +217,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; } /* @@ -254,6 +268,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 @@ -261,14 +279,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 (qemu_file_get_error(f)) { - return; + if (f->last_error) { + return f->last_error; } if (f->iovcnt > 0) { Error *local_error = NULL; @@ -278,7 +296,7 @@ void qemu_fflush(QEMUFile *f) qemu_file_set_error_obj(f, -EIO, local_error); } else { uint64_t size = iov_size(f->iov, f->iovcnt); - f->total_transferred += size; + stat64_add(&mig_stats.qemu_file_transferred, size); } qemu_iovec_release_ram(f); @@ -286,63 +304,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) -{ - if (f->hooks && f->hooks->hook_ram_load) { - int ret = f->hooks->hook_ram_load(f, flags, data); - if (ret < 0) { - 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_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; } /* @@ -358,6 +320,10 @@ static ssize_t coroutine_mixed_fn qemu_fill_buffer(QEMUFile *f) int len; int pending; Error *local_error = NULL; + g_autofree int *fds = NULL; + size_t nfd = 0; + int **pfds = f->can_pass_fd ? &fds : NULL; + size_t *pnfd = f->can_pass_fd ? &nfd : NULL; assert(!qemu_file_is_writable(f)); @@ -373,10 +339,9 @@ static ssize_t coroutine_mixed_fn qemu_fill_buffer(QEMUFile *f) } do { - len = qio_channel_read(f->ioc, - (char *)f->buf + pending, - IO_BUF_SIZE - pending, - &local_error); + struct iovec iov = { f->buf + pending, IO_BUF_SIZE - pending }; + len = qio_channel_readv_full(f->ioc, &iov, 1, pfds, pnfd, 0, + &local_error); if (len == QIO_CHANNEL_ERR_BLOCK) { if (qemu_in_coroutine()) { qio_channel_yield(f->ioc, G_IO_IN); @@ -390,19 +355,70 @@ static ssize_t coroutine_mixed_fn 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 { qemu_file_set_error_obj(f, len, local_error); } + for (int i = 0; i < nfd; i++) { + FdEntry *fde = g_new0(FdEntry, 1); + fde->fd = fds[i]; + QTAILQ_INSERT_TAIL(&f->fds, fde, entry); + } + return len; } -void qemu_file_credit_transfer(QEMUFile *f, size_t size) +int qemu_file_put_fd(QEMUFile *f, int fd) { - f->total_transferred += size; + int ret = 0; + QIOChannel *ioc = qemu_file_get_ioc(f); + Error *err = NULL; + struct iovec iov = { (void *)" ", 1 }; + + /* + * Send a dummy byte so qemu_fill_buffer on the receiving side does not + * fail with a len=0 error. Flush first to maintain ordering wrt other + * data. + */ + + qemu_fflush(f); + if (qio_channel_writev_full(ioc, &iov, 1, &fd, 1, 0, &err) < 1) { + error_report_err(error_copy(err)); + qemu_file_set_error_obj(f, -EIO, err); + ret = -1; + } + trace_qemu_file_put_fd(f->ioc->name, fd, ret); + return ret; +} + +int qemu_file_get_fd(QEMUFile *f) +{ + int fd = -1; + FdEntry *fde; + + if (!f->can_pass_fd) { + Error *err = NULL; + error_setg(&err, "%s does not support fd passing", f->ioc->name); + error_report_err(error_copy(err)); + qemu_file_set_error_obj(f, -EIO, err); + goto out; + } + + /* Force the dummy byte and its fd passenger to appear. */ + qemu_peek_byte(f, 0); + + fde = QTAILQ_FIRST(&f->fds); + if (fde) { + qemu_get_byte(f); /* Drop the dummy byte */ + fd = fde->fd; + QTAILQ_REMOVE(&f->fds, fde, entry); + g_free(fde); + } +out: + trace_qemu_file_get_fd(f->ioc->name, fd); + return fd; } /** Closes the file @@ -415,22 +431,18 @@ 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); + FdEntry *fde, *next; + 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; + QTAILQ_FOREACH_SAFE(fde, &f->fds, entry, next) { + warn_report("qemu_fclose: received fd %d was never claimed", fde->fd); + close(fde->fd); + g_free(fde); } + g_clear_pointer(&f->ioc, object_unref); error_free(f->last_error_obj); g_free(f); trace_qemu_file_fclose(); @@ -518,6 +530,105 @@ 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); +} + + +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) { @@ -680,11 +791,13 @@ int coroutine_mixed_fn qemu_get_byte(QEMUFile *f) return result; } -uint64_t qemu_file_transferred_noflush(QEMUFile *f) +uint64_t qemu_file_transferred(QEMUFile *f) { - uint64_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; } @@ -692,12 +805,6 @@ uint64_t qemu_file_transferred_noflush(QEMUFile *f) return ret; } -uint64_t qemu_file_transferred(QEMUFile *f) -{ - qemu_fflush(f); - return f->total_transferred; -} - void qemu_put_be16(QEMUFile *f, unsigned int v) { qemu_put_byte(f, v >> 8); @@ -744,84 +851,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; -} - -/* - * Check if the writable buffer is empty - */ - -bool qemu_file_buffer_empty(QEMUFile *file) -{ - assert(qemu_file_is_writable(file)); - - return !file->iovcnt; -} - /* * Get a string whose length is determined by a single preceding byte * A preallocated 256 byte buffer must be passed in. diff --git a/migration/qemu-file.h b/migration/qemu-file.h index 47015f52011..f5b9f430e04 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -29,72 +29,21 @@ #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_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 - */ -uint64_t qemu_file_transferred(QEMUFile *f); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QEMUFile, qemu_fclose) /* - * qemu_file_transferred_noflush: + * qemu_file_transferred: * - * As qemu_file_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 */ -uint64_t qemu_file_transferred_noflush(QEMUFile *f); +uint64_t qemu_file_transferred(QEMUFile *f); /* * put_buffer without copying the buffer. @@ -107,10 +56,6 @@ void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, size_t size, size_t coroutine_mixed_fn qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset); size_t coroutine_mixed_fn qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size); -ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, - const uint8_t *p, size_t size); -int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src); -bool qemu_file_buffer_empty(QEMUFile *file); /* * Note that you can only peek continuous bytes from where the current pointer @@ -119,40 +64,24 @@ bool qemu_file_buffer_empty(QEMUFile *file); */ 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); 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); +int qemu_file_put_fd(QEMUFile *f, int fd); +int qemu_file_get_fd(QEMUFile *f); #endif diff --git a/migration/ram-compress.c b/migration/ram-compress.c deleted file mode 100644 index 06254d8c698..00000000000 --- a/migration/ram-compress.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2003-2008 Fabrice Bellard - * Copyright (c) 2011-2015 Red Hat Inc - * - * Authors: - * Juan Quintela - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu/cutils.h" - -#include "ram-compress.h" - -#include "qemu/error-report.h" -#include "migration.h" -#include "options.h" -#include "io/channel-null.h" -#include "exec/target_page.h" -#include "exec/ramblock.h" - -CompressionStats compression_counters; - -static CompressParam *comp_param; -static QemuThread *compress_threads; -/* comp_done_cond is used to wake up the migration thread when - * one of the compression threads has finished the compression. - * comp_done_lock is used to co-work with comp_done_cond. - */ -static QemuMutex comp_done_lock; -static QemuCond comp_done_cond; - -struct DecompressParam { - bool done; - bool quit; - QemuMutex mutex; - QemuCond cond; - void *des; - uint8_t *compbuf; - int len; - z_stream stream; -}; -typedef struct DecompressParam DecompressParam; - -static QEMUFile *decomp_file; -static DecompressParam *decomp_param; -static QemuThread *decompress_threads; -static QemuMutex decomp_done_lock; -static QemuCond decomp_done_cond; - -static CompressResult do_compress_ram_page(QEMUFile *f, z_stream *stream, - RAMBlock *block, ram_addr_t offset, - uint8_t *source_buf); - -static void *do_data_compress(void *opaque) -{ - CompressParam *param = opaque; - RAMBlock *block; - ram_addr_t offset; - CompressResult result; - - qemu_mutex_lock(¶m->mutex); - while (!param->quit) { - if (param->trigger) { - block = param->block; - offset = param->offset; - param->trigger = false; - qemu_mutex_unlock(¶m->mutex); - - result = do_compress_ram_page(param->file, ¶m->stream, - block, offset, param->originbuf); - - qemu_mutex_lock(&comp_done_lock); - param->done = true; - param->result = result; - qemu_cond_signal(&comp_done_cond); - qemu_mutex_unlock(&comp_done_lock); - - qemu_mutex_lock(¶m->mutex); - } else { - qemu_cond_wait(¶m->cond, ¶m->mutex); - } - } - qemu_mutex_unlock(¶m->mutex); - - return NULL; -} - -void compress_threads_save_cleanup(void) -{ - int i, thread_count; - - if (!migrate_compress() || !comp_param) { - return; - } - - thread_count = migrate_compress_threads(); - for (i = 0; i < thread_count; i++) { - /* - * we use it as a indicator which shows if the thread is - * properly init'd or not - */ - if (!comp_param[i].file) { - break; - } - - qemu_mutex_lock(&comp_param[i].mutex); - comp_param[i].quit = true; - qemu_cond_signal(&comp_param[i].cond); - qemu_mutex_unlock(&comp_param[i].mutex); - - qemu_thread_join(compress_threads + i); - qemu_mutex_destroy(&comp_param[i].mutex); - qemu_cond_destroy(&comp_param[i].cond); - deflateEnd(&comp_param[i].stream); - g_free(comp_param[i].originbuf); - qemu_fclose(comp_param[i].file); - comp_param[i].file = NULL; - } - qemu_mutex_destroy(&comp_done_lock); - qemu_cond_destroy(&comp_done_cond); - g_free(compress_threads); - g_free(comp_param); - compress_threads = NULL; - comp_param = NULL; -} - -int compress_threads_save_setup(void) -{ - int i, thread_count; - - if (!migrate_compress()) { - return 0; - } - thread_count = migrate_compress_threads(); - compress_threads = g_new0(QemuThread, thread_count); - comp_param = g_new0(CompressParam, thread_count); - qemu_cond_init(&comp_done_cond); - qemu_mutex_init(&comp_done_lock); - for (i = 0; i < thread_count; i++) { - comp_param[i].originbuf = g_try_malloc(qemu_target_page_size()); - if (!comp_param[i].originbuf) { - goto exit; - } - - if (deflateInit(&comp_param[i].stream, - migrate_compress_level()) != Z_OK) { - g_free(comp_param[i].originbuf); - goto exit; - } - - /* comp_param[i].file is just used as a dummy buffer to save data, - * set its ops to empty. - */ - comp_param[i].file = qemu_file_new_output( - QIO_CHANNEL(qio_channel_null_new())); - comp_param[i].done = true; - comp_param[i].quit = false; - qemu_mutex_init(&comp_param[i].mutex); - qemu_cond_init(&comp_param[i].cond); - qemu_thread_create(compress_threads + i, "compress", - do_data_compress, comp_param + i, - QEMU_THREAD_JOINABLE); - } - return 0; - -exit: - compress_threads_save_cleanup(); - return -1; -} - -static CompressResult do_compress_ram_page(QEMUFile *f, z_stream *stream, - RAMBlock *block, ram_addr_t offset, - uint8_t *source_buf) -{ - uint8_t *p = block->host + offset; - size_t page_size = qemu_target_page_size(); - int ret; - - assert(qemu_file_buffer_empty(f)); - - if (buffer_is_zero(p, page_size)) { - return RES_ZEROPAGE; - } - - /* - * copy it to a internal buffer to avoid it being modified by VM - * so that we can catch up the error during compression and - * decompression - */ - memcpy(source_buf, p, page_size); - ret = qemu_put_compression_data(f, stream, source_buf, page_size); - if (ret < 0) { - qemu_file_set_error(migrate_get_current()->to_dst_file, ret); - error_report("compressed data failed!"); - qemu_fflush(f); - return RES_NONE; - } - return RES_COMPRESS; -} - -static inline void compress_reset_result(CompressParam *param) -{ - param->result = RES_NONE; - param->block = NULL; - param->offset = 0; -} - -void flush_compressed_data(int (send_queued_data(CompressParam *))) -{ - int idx, thread_count; - - 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) { - CompressParam *param = &comp_param[idx]; - send_queued_data(param); - assert(qemu_file_buffer_empty(param->file)); - compress_reset_result(param); - } - 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; - param->trigger = true; -} - -int compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset, - int (send_queued_data(CompressParam *))) -{ - int idx, thread_count, 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) { - CompressParam *param = &comp_param[idx]; - qemu_mutex_lock(¶m->mutex); - param->done = false; - send_queued_data(param); - assert(qemu_file_buffer_empty(param->file)); - compress_reset_result(param); - set_compress_params(param, block, offset); - - qemu_cond_signal(¶m->cond); - qemu_mutex_unlock(¶m->mutex); - pages = 1; - 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; -} - -/* return the size after decompression, or negative value on error */ -static int -qemu_uncompress_data(z_stream *stream, uint8_t *dest, size_t dest_len, - const uint8_t *source, size_t source_len) -{ - int err; - - err = inflateReset(stream); - if (err != Z_OK) { - return -1; - } - - stream->avail_in = source_len; - stream->next_in = (uint8_t *)source; - stream->avail_out = dest_len; - stream->next_out = dest; - - err = inflate(stream, Z_NO_FLUSH); - if (err != Z_STREAM_END) { - return -1; - } - - return stream->total_out; -} - -static void *do_data_decompress(void *opaque) -{ - DecompressParam *param = opaque; - unsigned long pagesize; - uint8_t *des; - int len, ret; - - qemu_mutex_lock(¶m->mutex); - while (!param->quit) { - if (param->des) { - des = param->des; - len = param->len; - param->des = 0; - qemu_mutex_unlock(¶m->mutex); - - pagesize = qemu_target_page_size(); - - ret = qemu_uncompress_data(¶m->stream, des, pagesize, - param->compbuf, len); - if (ret < 0 && migrate_get_current()->decompress_error_check) { - error_report("decompress data failed"); - qemu_file_set_error(decomp_file, ret); - } - - qemu_mutex_lock(&decomp_done_lock); - param->done = true; - qemu_cond_signal(&decomp_done_cond); - qemu_mutex_unlock(&decomp_done_lock); - - qemu_mutex_lock(¶m->mutex); - } else { - qemu_cond_wait(¶m->cond, ¶m->mutex); - } - } - qemu_mutex_unlock(¶m->mutex); - - return NULL; -} - -int wait_for_decompress_done(void) -{ - int idx, thread_count; - - if (!migrate_compress()) { - 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); -} - -void compress_threads_load_cleanup(void) -{ - int i, thread_count; - - if (!migrate_compress()) { - return; - } - thread_count = migrate_decompress_threads(); - for (i = 0; i < thread_count; i++) { - /* - * we use it as a indicator which shows if the thread is - * properly init'd or not - */ - if (!decomp_param[i].compbuf) { - break; - } - - qemu_mutex_lock(&decomp_param[i].mutex); - decomp_param[i].quit = true; - qemu_cond_signal(&decomp_param[i].cond); - qemu_mutex_unlock(&decomp_param[i].mutex); - } - for (i = 0; i < thread_count; i++) { - if (!decomp_param[i].compbuf) { - break; - } - - qemu_thread_join(decompress_threads + i); - qemu_mutex_destroy(&decomp_param[i].mutex); - qemu_cond_destroy(&decomp_param[i].cond); - inflateEnd(&decomp_param[i].stream); - g_free(decomp_param[i].compbuf); - decomp_param[i].compbuf = NULL; - } - g_free(decompress_threads); - g_free(decomp_param); - decompress_threads = NULL; - decomp_param = NULL; - decomp_file = NULL; -} - -int compress_threads_load_setup(QEMUFile *f) -{ - int i, thread_count; - - if (!migrate_compress()) { - return 0; - } - - thread_count = migrate_decompress_threads(); - decompress_threads = g_new0(QemuThread, thread_count); - decomp_param = g_new0(DecompressParam, thread_count); - qemu_mutex_init(&decomp_done_lock); - qemu_cond_init(&decomp_done_cond); - decomp_file = f; - for (i = 0; i < thread_count; i++) { - if (inflateInit(&decomp_param[i].stream) != Z_OK) { - goto exit; - } - - size_t compbuf_size = compressBound(qemu_target_page_size()); - decomp_param[i].compbuf = g_malloc0(compbuf_size); - qemu_mutex_init(&decomp_param[i].mutex); - qemu_cond_init(&decomp_param[i].cond); - decomp_param[i].done = true; - decomp_param[i].quit = false; - qemu_thread_create(decompress_threads + i, "decompress", - do_data_decompress, decomp_param + i, - QEMU_THREAD_JOINABLE); - } - return 0; -exit: - compress_threads_load_cleanup(); - return -1; -} - -void decompress_data_with_multi_threads(QEMUFile *f, void *host, int len) -{ - int 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); - } - } -} diff --git a/migration/ram-compress.h b/migration/ram-compress.h deleted file mode 100644 index 6f7fe2f472e..00000000000 --- a/migration/ram-compress.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2003-2008 Fabrice Bellard - * Copyright (c) 2011-2015 Red Hat Inc - * - * Authors: - * Juan Quintela - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QEMU_MIGRATION_COMPRESS_H -#define QEMU_MIGRATION_COMPRESS_H - -#include "qemu-file.h" - -enum CompressResult { - RES_NONE = 0, - RES_ZEROPAGE = 1, - RES_COMPRESS = 2 -}; -typedef enum CompressResult CompressResult; - -struct CompressParam { - bool done; - bool quit; - bool trigger; - CompressResult result; - QEMUFile *file; - QemuMutex mutex; - QemuCond cond; - RAMBlock *block; - ram_addr_t offset; - - /* internally used fields */ - z_stream stream; - uint8_t *originbuf; -}; -typedef struct CompressParam CompressParam; - -void compress_threads_save_cleanup(void); -int compress_threads_save_setup(void); - -void flush_compressed_data(int (send_queued_data(CompressParam *))); -int compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset, - int (send_queued_data(CompressParam *))); - -int wait_for_decompress_done(void); -void compress_threads_load_cleanup(void); -int compress_threads_load_setup(QEMUFile *f); -void decompress_data_with_multi_threads(QEMUFile *f, void *host, int len); - -#endif diff --git a/migration/ram.c b/migration/ram.c index 9040d66e615..2140785a056 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -33,7 +33,6 @@ #include "qemu/madvise.h" #include "qemu/main-loop.h" #include "xbzrle.h" -#include "ram-compress.h" #include "ram.h" #include "migration.h" #include "migration-stats.h" @@ -49,19 +48,19 @@ #include "qapi/qapi-commands-migration.h" #include "qapi/qmp/qerror.h" #include "trace.h" -#include "exec/ram_addr.h" +#include "system/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 "system/cpu-throttle.h" #include "savevm.h" #include "qemu/iov.h" #include "multifd.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" +#include "rdma.h" #include "options.h" -#include "sysemu/dirtylimit.h" -#include "sysemu/kvm.h" +#include "system/dirtylimit.h" +#include "system/kvm.h" #include "hw/boards.h" /* for machine_dump_guest_core() */ @@ -73,28 +72,55 @@ /* ram save/restore */ /* - * 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_SAVE_FLAG_COMPRESS_PAGE just rename it. + * 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 + /* - * RAM_SAVE_FLAG_FULL was obsoleted in 2009, it can be reused now + * When doing mapped-ram migration, this is the amount we read from + * the pages region in the migration file at a time. */ -#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 qemu-file.h for RAM_SAVE_FLAG_HOOK */ -#define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100 -#define RAM_SAVE_FLAG_MULTIFD_FLUSH 0x200 -/* We can't use any flag that is bigger than 0x200 */ +#define MAPPED_RAM_LOAD_BUF_SIZE 0x100000 XBZRLECacheStats xbzrle_counters; +/* + * This structure locates a specific location of a guest page. In QEMU, + * it's described in a tuple of (ramblock, offset). + */ +struct PageLocation { + RAMBlock *block; + unsigned long offset; +}; +typedef struct PageLocation PageLocation; + +/** + * PageLocationHint: describes a hint to a page location + * + * @valid set if the hint is vaild and to be consumed + * @location: the hint content + * + * In postcopy preempt mode, the urgent channel may provide hints to the + * background channel, so that QEMU source can try to migrate whatever is + * right after the requested urgent pages. + * + * This is based on the assumption that the VM (already running on the + * destination side) tends to access the memory with spatial locality. + * This is also the default behavior of vanilla postcopy (preempt off). + */ +struct PageLocationHint { + bool valid; + PageLocation location; +}; +typedef struct PageLocationHint PageLocationHint; + /* used by the search for pages to send */ struct PageSearchStatus { /* The migration channel used for a specific host page */ @@ -199,7 +225,9 @@ static bool postcopy_preempt_active(void) bool migrate_ram_is_ignored(RAMBlock *block) { + MigMode mode = migrate_mode(); return !qemu_ram_is_migratable(block) || + mode == MIG_MODE_CPR_TRANSFER || (migrate_ignore_shared() && qemu_ram_is_shared(block) && qemu_ram_is_named_file(block)); } @@ -256,6 +284,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) /* @@ -304,17 +336,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); @@ -368,13 +398,6 @@ struct RAMState { 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; @@ -394,6 +417,21 @@ struct RAMState { /* Queue of outstanding page requests from the destination */ QemuMutex src_page_req_mutex; QSIMPLEQ_HEAD(, RAMSrcPageRequest) src_page_requests; + + /* + * This is only used when postcopy is in recovery phase, to communicate + * between the migration thread and the return path thread on dirty + * bitmap synchronizations. This field is unused in other stages of + * RAM migration. + */ + unsigned int postcopy_bmap_sync_requested; + /* + * Page hint during postcopy when preempt mode is on. Return path + * thread sets it, while background migration thread consumes it. + * + * Protected by @bitmap_mutex. + */ + PageLocationHint page_hint; }; typedef struct RAMState RAMState; @@ -426,9 +464,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) @@ -446,16 +483,8 @@ void ram_transferred_add(uint64_t bytes) } else { stat64_add(&mig_stats.downtime_bytes, bytes); } - stat64_add(&mig_stats.transferred, bytes); } -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. */ @@ -555,13 +584,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 = stat64_get(&mig_stats.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. @@ -570,7 +598,7 @@ 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) { /* We don't care if this fails to allocate a new cache page * as long as it updated an old one */ @@ -672,8 +700,7 @@ static int save_xbzrle_page(RAMState *rs, PageSearchStatus *pss, 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; @@ -804,14 +831,22 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs, bool ret; /* - * Clear dirty bitmap if needed. This _must_ be called before we - * send any of the page in the chunk because we need to make sure - * we can capture further page content changes when we sync dirty - * log the next time. So as long as we are going to send any of - * the page in the chunk we clear the remote dirty bitmap for all. - * Clearing it earlier won't be a problem, but too late will. + * During the last stage (after source VM stopped), resetting the write + * protections isn't needed as we know there will be either (1) no + * further writes if migration will complete, or (2) migration fails + * at last then tracking isn't needed either. */ - migration_clear_memory_region_dirty_bitmap(rb, page); + if (!rs->last_stage) { + /* + * Clear dirty bitmap if needed. This _must_ be called before we + * send any of the page in the chunk because we need to make sure + * we can capture further page content changes when we sync dirty + * log the next time. So as long as we are going to send any of + * the page in the chunk we clear the remote dirty bitmap for all. + * Clearing it earlier won't be a problem, but too late will. + */ + migration_clear_memory_region_dirty_bitmap(rb, page); + } ret = test_and_clear_bit(page, rb->bmap); if (ret) { @@ -821,8 +856,8 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs, return ret; } -static void dirty_bitmap_clear_section(MemoryRegionSection *section, - void *opaque) +static int dirty_bitmap_clear_section(MemoryRegionSection *section, + void *opaque) { const hwaddr offset = section->offset_within_region; const hwaddr size = int128_get64(section->size); @@ -841,6 +876,7 @@ static void dirty_bitmap_clear_section(MemoryRegionSection *section, } *cleared_bits += bitmap_count_one_with_offset(rb->bmap, start, npages); bitmap_clear(rb->bmap, start, npages); + return 0; } /* @@ -931,13 +967,12 @@ uint64_t ram_get_total_transferred_pages(void) { return stat64_get(&mig_stats.normal_pages) + stat64_get(&mig_stats.zero_pages) + - compression_counters.pages + xbzrle_counters.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 */ stat64_set(&mig_stats.dirty_pages_rate, @@ -965,26 +1000,6 @@ 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_compress()) { - compression_counters.busy_rate = (double)(compression_counters.busy - - rs->compress_thread_busy_prev) / page_count; - rs->compress_thread_busy_prev = compression_counters.busy; - - 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; - } - } } /* @@ -1022,17 +1037,10 @@ static void migration_trigger_throttle(RAMState *rs) { uint64_t threshold = migrate_throttle_trigger_threshold(); uint64_t bytes_xfer_period = - stat64_get(&mig_stats.transferred) - rs->bytes_xfer_prev; + 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 (blk_mig_bulk_active()) { - return; - } - /* * The following detection logic can be refined later. For now: * Check to see if the ratio between dirtied bytes and the approx. @@ -1067,14 +1075,14 @@ static void migration_bitmap_sync(RAMState *rs, bool last_stage) trace_migration_bitmap_sync_start(); 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()); } - stat64_set(&mig_stats.dirty_bytes_last_sync, 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); @@ -1092,7 +1100,7 @@ static void migration_bitmap_sync(RAMState *rs, bool last_stage) /* reset period counters */ rs->time_last_bitmap_sync = end_time; rs->num_dirty_pages_period = 0; - rs->bytes_xfer_prev = stat64_get(&mig_stats.transferred); + rs->bytes_xfer_prev = migration_transferred_bytes(); } if (migrate_events()) { uint64_t generation = stat64_get(&mig_stats.dirty_sync_count); @@ -1100,9 +1108,10 @@ static void migration_bitmap_sync(RAMState *rs, bool last_stage) } } -static void migration_bitmap_sync_precopy(RAMState *rs, bool last_stage) +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 @@ -1113,7 +1122,7 @@ static void migration_bitmap_sync_precopy(RAMState *rs, bool last_stage) local_err = NULL; } - migration_bitmap_sync(rs, last_stage); + migration_bitmap_sync(ram_state, last_stage); if (precopy_notify(PRECOPY_NOTIFY_AFTER_BITMAP_SYNC, &local_err)) { error_report_err(local_err); @@ -1129,89 +1138,55 @@ 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 - * - * @pss: current PSS channel - * @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(PageSearchStatus *pss, 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(pss, 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 * @pss: current PSS channel - * @block: block that contains the page we want to send * @offset: offset inside the block for the page */ -static int save_zero_page(PageSearchStatus *pss, QEMUFile *f, RAMBlock *block, +static int save_zero_page(RAMState *rs, PageSearchStatus *pss, ram_addr_t offset) { - int len = save_zero_page_to_file(pss, f, block, offset); + uint8_t *p = pss->block->host + offset; + QEMUFile *file = pss->pss_channel; + int len = 0; - if (len) { - stat64_add(&mig_stats.zero_pages, 1); - ram_transferred_add(len); - return 1; + if (migrate_zero_page_detection() == ZERO_PAGE_DETECTION_NONE) { + return 0; } - return -1; -} - -/* - * @pages: the number of pages written by the control path, - * < 0 - error - * > 0 - number of pages written - * - * Return true if the pages has been saved, otherwise false is returned. - */ -static bool control_save_page(PageSearchStatus *pss, RAMBlock *block, - ram_addr_t offset, int *pages) -{ - uint64_t bytes_xmit = 0; - int ret; - *pages = -1; - ret = ram_control_save_page(pss->pss_channel, block->offset, offset, - TARGET_PAGE_SIZE, &bytes_xmit); - if (ret == RAM_SAVE_CONTROL_NOT_SUPP) { - return false; + if (!buffer_is_zero(p, TARGET_PAGE_SIZE)) { + return 0; } - if (bytes_xmit) { - ram_transferred_add(bytes_xmit); - *pages = 1; - } + stat64_add(&mig_stats.zero_pages, 1); - if (ret == RAM_SAVE_CONTROL_DELAYED) { - return true; + 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; } - if (bytes_xmit > 0) { - stat64_add(&mig_stats.normal_pages, 1); - } else if (bytes_xmit == 0) { - stat64_add(&mig_stats.zero_pages, 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 true; + return len; } /* @@ -1230,14 +1205,20 @@ static int save_normal_page(PageSearchStatus *pss, RAMBlock *block, { QEMUFile *file = pss->pss_channel; - 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()); + 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(file, 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); stat64_add(&mig_stats.normal_pages, 1); @@ -1290,78 +1271,15 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss) return pages; } -static int ram_save_multifd_page(QEMUFile *file, RAMBlock *block, - ram_addr_t offset) +static int ram_save_multifd_page(RAMBlock *block, ram_addr_t offset) { - if (multifd_queue_page(file, block, offset) < 0) { + if (!multifd_queue_page(block, offset)) { return -1; } - stat64_add(&mig_stats.normal_pages, 1); return 1; } -static void -update_compress_thread_counts(const CompressParam *param, int bytes_xmit) -{ - ram_transferred_add(bytes_xmit); - - if (param->result == RES_ZEROPAGE) { - stat64_add(&mig_stats.zero_pages, 1); - return; - } - - /* 8 means a header with RAM_SAVE_FLAG_CONTINUE. */ - compression_counters.compressed_size += bytes_xmit - 8; - compression_counters.pages++; -} - -static bool save_page_use_compression(RAMState *rs); - -static int send_queued_data(CompressParam *param) -{ - PageSearchStatus *pss = &ram_state->pss[RAM_CHANNEL_PRECOPY]; - MigrationState *ms = migrate_get_current(); - QEMUFile *file = ms->to_dst_file; - int len = 0; - - RAMBlock *block = param->block; - ram_addr_t offset = param->offset; - - if (param->result == RES_NONE) { - return 0; - } - - assert(block == pss->last_sent_block); - - if (param->result == RES_ZEROPAGE) { - assert(qemu_file_buffer_empty(param->file)); - len += save_page_header(pss, file, block, offset | RAM_SAVE_FLAG_ZERO); - qemu_put_byte(file, 0); - len += 1; - ram_release_page(block->idstr, offset); - } else if (param->result == RES_COMPRESS) { - assert(!qemu_file_buffer_empty(param->file)); - len += save_page_header(pss, file, block, - offset | RAM_SAVE_FLAG_COMPRESS_PAGE); - len += qemu_put_qemu_file(file, param->file); - } else { - abort(); - } - - update_compress_thread_counts(param, len); - - return len; -} - -static void ram_flush_compressed_data(RAMState *rs) -{ - if (!save_page_use_compression(rs)) { - return; - } - - flush_compressed_data(send_queued_data); -} #define PAGE_ALL_CLEAN 0 #define PAGE_TRY_AGAIN 1 @@ -1399,25 +1317,13 @@ static int find_dirty_block(RAMState *rs, PageSearchStatus *pss) pss->page = 0; pss->block = QLIST_NEXT_RCU(pss->block, next); if (!pss->block) { - if (!migrate_multifd_flush_after_each_section()) { + if (multifd_ram_sync_per_round()) { QEMUFile *f = rs->pss[RAM_CHANNEL_PRECOPY].pss_channel; - int ret = multifd_send_sync_main(f); + int ret = multifd_ram_flush_and_sync(f); if (ret < 0) { return ret; } - qemu_put_be64(f, RAM_SAVE_FLAG_MULTIFD_FLUSH); - qemu_fflush(f); } - /* - * If memory migration starts over, we will meet a dirtied page - * which may still exists in compression threads's ring, so we - * 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. - */ - ram_flush_compressed_data(rs); /* Hit the end of the list */ pss->block = QLIST_FIRST_RCU(&ram_list.blocks); @@ -1847,19 +1753,17 @@ 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__) */ @@ -1877,7 +1781,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); @@ -1963,7 +1867,8 @@ 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; @@ -1980,7 +1885,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 { @@ -1988,16 +1893,17 @@ 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; } @@ -2029,9 +1935,9 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) assert(len % page_size == 0); while (len) { if (ram_save_host_page_urgent(pss)) { - error_report("%s: ram_save_host_page_urgent() failed: " - "ramblock=%s, start_addr=0x"RAM_ADDR_FMT, - __func__, ramblock->idstr, start); + error_setg(errp, "ram_save_host_page_urgent() failed: " + "ramblock=%s, start_addr=0x"RAM_ADDR_FMT, + ramblock->idstr, start); ret = -1; break; } @@ -2066,102 +1972,38 @@ 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_compress()) { - 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_started) { - 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, PageSearchStatus *pss, - 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 != pss->last_sent_block) { - ram_flush_compressed_data(rs); - return false; - } - - if (compress_page_with_multi_thread(block, offset, send_queued_data) > 0) { - return true; - } - - compression_counters.busy++; - return false; -} - /** - * ram_save_target_page_legacy: save one target page - * - * Returns the number of pages written + * ram_save_target_page: save one target page to the precopy thread + * OR to multifd workers. * * @rs: current RAM state * @pss: data about the page we want to send */ -static int ram_save_target_page_legacy(RAMState *rs, PageSearchStatus *pss) +static int ram_save_target_page(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(pss, block, offset, &res)) { - return res; - } + /* Hand over to RDMA first */ + if (migrate_rdma()) { + res = rdma_control_save_page(pss->pss_channel, pss->block->offset, + offset, TARGET_PAGE_SIZE); - if (save_compress_page(rs, pss, block, offset)) { - return 1; + if (res == RAM_SAVE_CONTROL_DELAYED) { + res = 1; + } + return res; } - res = save_zero_page(pss, pss->pss_channel, block, offset); - if (res > 0) { - /* 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(rs, block->offset + offset); - XBZRLE_cache_unlock(); + if (!migrate_multifd() + || migrate_zero_page_detection() == ZERO_PAGE_DETECTION_LEGACY) { + if (save_zero_page(rs, pss, offset)) { + return 1; } - 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_multifd() && !migration_in_postcopy()) { - return ram_save_multifd_page(pss->pss_channel, block, offset); + return ram_save_multifd_page(pss->block, offset); } return ram_save_page(rs, pss); @@ -2222,6 +2064,21 @@ static void pss_host_page_finish(PageSearchStatus *pss) pss->host_page_start = pss->host_page_end = 0; } +static void ram_page_hint_update(RAMState *rs, PageSearchStatus *pss) +{ + PageLocationHint *hint = &rs->page_hint; + + /* If there's a pending hint not consumed, don't bother */ + if (hint->valid) { + return; + } + + /* Provide a hint to the background stream otherwise */ + hint->location.block = pss->block; + hint->location.offset = pss->page; + hint->valid = true; +} + /* * Send an urgent host page specified by `pss'. Need to be called with * bitmap_mutex held. @@ -2253,7 +2110,7 @@ static int ram_save_host_page_urgent(PageSearchStatus *pss) 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) { + if (ram_save_target_page(rs, pss) != 1) { error_report_once("%s: ram_save_target_page failed", __func__); ret = -1; goto out; @@ -2267,6 +2124,7 @@ static int ram_save_host_page_urgent(PageSearchStatus *pss) /* For urgent requests, flush immediately if sent */ if (sent) { qemu_fflush(pss->pss_channel); + ram_page_hint_update(rs, pss); } return ret; } @@ -2322,7 +2180,7 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss) if (preempt_active) { qemu_mutex_unlock(&rs->bitmap_mutex); } - tmppages = migration_ops->ram_save_target_page(rs, pss); + tmppages = ram_save_target_page(rs, pss); if (tmppages >= 0) { pages += tmppages; /* @@ -2354,6 +2212,30 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss) return (res < 0 ? res : pages); } +static bool ram_page_hint_valid(RAMState *rs) +{ + /* There's only page hint during postcopy preempt mode */ + if (!postcopy_preempt_active()) { + return false; + } + + return rs->page_hint.valid; +} + +static void ram_page_hint_collect(RAMState *rs, RAMBlock **block, + unsigned long *page) +{ + PageLocationHint *hint = &rs->page_hint; + + assert(hint->valid); + + *block = hint->location.block; + *page = hint->location.offset; + + /* Mark the hint consumed */ + hint->valid = false; +} + /** * ram_find_and_save_block: finds a dirty page and sends it to f * @@ -2370,6 +2252,8 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss) static int ram_find_and_save_block(RAMState *rs) { PageSearchStatus *pss = &rs->pss[RAM_CHANNEL_PRECOPY]; + unsigned long next_page; + RAMBlock *next_block; int pages = 0; /* No dirty page as there is zero RAM */ @@ -2389,7 +2273,14 @@ static int ram_find_and_save_block(RAMState *rs) rs->last_page = 0; } - pss_init(pss, rs->last_seen_block, rs->last_page); + if (ram_page_hint_valid(rs)) { + ram_page_hint_collect(rs, &next_block, &next_page); + } else { + next_block = rs->last_seen_block; + next_page = rs->last_page; + } + + pss_init(pss, next_block, next_page); while (true){ if (!get_queued_page(rs, pss)) { @@ -2482,14 +2373,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) { @@ -2502,18 +2406,18 @@ 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_page_hint_reset(PageLocationHint *hint) +{ + hint->location.block = NULL; + hint->location.offset = 0; + hint->valid = false; } static void ram_state_reset(RAMState *rs) @@ -2528,6 +2432,8 @@ static void ram_state_reset(RAMState *rs) rs->last_page = 0; rs->last_version = ram_list.version; rs->xbzrle_started = false; + + ram_page_hint_reset(&rs->page_hint); } #define MAX_WAIT 50 /* ms, half buffered_file limit */ @@ -2763,44 +2669,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_xbzrle()) { - return 0; + 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); @@ -2813,16 +2716,16 @@ static int xbzrle_init(void) 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); @@ -2838,7 +2741,7 @@ static int ram_state_init(RAMState **rsp) (*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) @@ -2874,6 +2777,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)); } @@ -2893,42 +2799,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, false); + 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; } @@ -2971,10 +2888,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; } @@ -3014,40 +2930,125 @@ void qemu_guest_free_page_hint(void *addr, size_t len) } } -/* - * Each of ram_save_setup, ram_save_iterate and ram_save_complete has - * long-running RCU critical section. When rcu-reclaims in the code - * start to become numerous it will be necessary to reduce the - * granularity of these critical sections. - */ +#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; -/** - * ram_save_setup: Setup RAM for migration - * - * Returns zero to indicate success and negative for error - * - * @f: QEMUFile where to send the data - * @opaque: RAMState pointer - */ -static int ram_save_setup(QEMUFile *f, void *opaque) +static void mapped_ram_setup_ramblock(QEMUFile *file, RAMBlock *block) { - RAMState **rsp = opaque; - RAMBlock *block; - int ret; + g_autofree MappedRamHeader *header = NULL; + size_t header_size, bitmap_size; + long num_pages; - if (compress_threads_save_setup()) { - return -1; + 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 + * start to become numerous it will be necessary to reduce the + * granularity of these critical sections. + */ + +/** + * ram_save_setup: Setup RAM for migration + * + * Returns zero to indicate success and negative for error + * + * @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, Error **errp) +{ + RAMState **rsp = opaque; + RAMBlock *block; + 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)->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_with_ignored() | RAM_SAVE_FLAG_MEM_SIZE); @@ -3056,34 +3057,108 @@ static int ram_save_setup(QEMUFile *f, void *opaque) qemu_put_byte(f, strlen(block->idstr)); qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); qemu_put_be64(f, block->used_length); - if (migrate_postcopy_ram() && block->page_size != - qemu_host_page_size) { + if (migrate_postcopy_ram() && + block->page_size != max_hg_page_size) { qemu_put_be64(f, block->page_size); } if (migrate_ignore_shared()) { qemu_put_be64(f, block->mr->addr); } + + if (migrate_mapped_ram()) { + mapped_ram_setup_ramblock(f, block); + } } } - ram_control_before_iterate(f, RAM_CONTROL_SETUP); - ram_control_after_iterate(f, RAM_CONTROL_SETUP); + ret = rdma_registration_start(f, RAM_CONTROL_SETUP); + if (ret < 0) { + error_setg(errp, "%s: failed to start RDMA registration", __func__); + qemu_file_set_error(f, ret); + return ret; + } - migration_ops = g_malloc0(sizeof(MigrationOps)); - migration_ops->ram_save_target_page = ram_save_target_page_legacy; - ret = multifd_send_sync_main(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; } - if (!migrate_multifd_flush_after_each_section()) { - qemu_put_be64(f, RAM_SAVE_FLAG_MULTIFD_FLUSH); + if (migrate_multifd()) { + multifd_ram_save_setup(); + } + + /* + * This operation is unfortunate.. + * + * For legacy QEMUs using per-section sync + * ======================================= + * + * This must exist because the EOS below requires the SYNC messages + * per-channel to work. + * + * For modern QEMUs using per-round sync + * ===================================== + * + * Logically such sync is not needed, and recv threads should not run + * until setup ready (using things like channels_ready on src). Then + * we should be all fine. + * + * However even if we add channels_ready to recv side in new QEMUs, old + * QEMU won't have them so this sync will still be needed to make sure + * multifd recv threads won't start processing guest pages early before + * ram_load_setup() is properly done. + * + * Let's stick with this. Fortunately the overhead is low to sync + * during setup because the VM is running, so at least it's not + * accounted as part of downtime. + */ + bql_unlock(); + ret = multifd_ram_flush_and_sync(f); + bql_lock(); + if (ret < 0) { + error_setg(errp, "%s: multifd synchronization failed", __func__); + return ret; } qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - qemu_fflush(f); + ret = qemu_fflush(f); + if (ret < 0) { + error_setg_errno(errp, -ret, "%s failed", __func__); + } + return ret; +} - return 0; +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); + } } /** @@ -3103,13 +3178,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 @@ -3117,89 +3185,85 @@ 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 = migration_rate_exceeded(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(); - if (pages < 0) { - qemu_file_set_error(f, pages); - break; + ret = rdma_registration_start(f, RAM_CONTROL_ROUND); + if (ret < 0) { + qemu_file_set_error(f, ret); + goto out; } - rs->target_page_count += pages; + t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + i = 0; + while ((ret = migration_rate_exceeded(f)) == 0 || + postcopy_has_request(rs)) { + int pages; - /* - * During postcopy, it is necessary to make sure one whole host - * page is sent in one chunk. - */ - if (migrate_postcopy_ram()) { - ram_flush_compressed_data(rs); - } + if (qemu_file_get_error(f)) { + break; + } - /* - * 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); + 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); /* * 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)) { - if (migrate_multifd_flush_after_each_section()) { - ret = multifd_send_sync_main(rs->pss[RAM_CHANNEL_PRECOPY].pss_channel); + if (ret >= 0 && migration_is_running()) { + if (multifd_ram_sync_per_section()) { + ret = multifd_ram_flush_and_sync(f); 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; @@ -3213,7 +3277,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) * * 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 @@ -3228,10 +3292,14 @@ static int ram_save_complete(QEMUFile *f, void *opaque) WITH_RCU_READ_LOCK_GUARD() { if (!migration_in_postcopy()) { - migration_bitmap_sync_precopy(rs, true); + 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 */ @@ -3246,32 +3314,44 @@ 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); - ram_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; + } } - if (ret < 0) { - return ret; + if (multifd_ram_sync_per_section()) { + /* + * Only the old dest QEMU will need this sync, because each EOS + * will require one SYNC message on each channel. + */ + ret = multifd_ram_flush_and_sync(f); + if (ret < 0) { + return ret; + } } - ret = multifd_send_sync_main(rs->pss[RAM_CHANNEL_PRECOPY].pss_channel); - if (ret < 0) { - return ret; - } + if (migrate_mapped_ram()) { + ram_save_file_bmap(f); - if (!migrate_multifd_flush_after_each_section()) { - qemu_put_be64(f, RAM_SAVE_FLAG_MULTIFD_FLUSH); + 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; + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + return qemu_fflush(f); } static void ram_state_pending_estimate(void *opaque, uint64_t *must_precopy, @@ -3293,21 +3373,20 @@ static void ram_state_pending_estimate(void *opaque, uint64_t *must_precopy, static void ram_state_pending_exact(void *opaque, uint64_t *must_precopy, uint64_t *can_postcopy) { - MigrationState *s = migrate_get_current(); RAMState **temp = opaque; RAMState *rs = *temp; + uint64_t remaining_size; - uint64_t remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE; - - if (!migration_in_postcopy() && remaining_size < s->threshold_size) { - qemu_mutex_lock_iothread(); + if (!migration_in_postcopy()) { + bql_lock(); WITH_RCU_READ_LOCK_GUARD() { - migration_bitmap_sync_precopy(rs, false); + 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 */ *can_postcopy += remaining_size; @@ -3458,7 +3537,7 @@ static inline void *colo_cache_from_block_offset(RAMBlock *block, } /** - * 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. @@ -3467,16 +3546,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); + 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); + } } /* @@ -3517,8 +3600,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); @@ -3533,8 +3614,10 @@ 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(false); @@ -3544,11 +3627,14 @@ void colo_incoming_start_dirty_log(void) /* 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 */ @@ -3580,8 +3666,9 @@ 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) { xbzrle_load_setup(); ramblock_recv_map_init(); @@ -3594,7 +3681,9 @@ static int ram_load_cleanup(void *opaque) RAMBlock *rb; RAMBLOCK_FOREACH_NOT_IGNORED(rb) { - qemu_ram_block_writeback(rb); + if (memory_region_is_nonvolatile(rb->mr)) { + qemu_ram_block_writeback(rb); + } } xbzrle_load_cleanup(); @@ -3648,7 +3737,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); @@ -3665,8 +3753,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; @@ -3729,16 +3816,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: @@ -3759,24 +3848,7 @@ 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); - break; - case RAM_SAVE_FLAG_MULTIFD_FLUSH: - multifd_recv_sync_main(); - break; case RAM_SAVE_FLAG_EOS: - /* normal exit */ - if (migrate_multifd_flush_after_each_section()) { - multifd_recv_sync_main(); - } break; default: error_report("Unknown combination of migration flags: 0x%x" @@ -3785,11 +3857,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); @@ -3867,6 +3934,218 @@ void colo_flush_ram_cache(void) 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); +} + +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 * @@ -3880,15 +4159,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 = migration_incoming_postcopy_advised(); - if (!migrate_compress()) { - 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; @@ -3904,20 +4184,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); @@ -3959,86 +4243,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 (migrate_ram_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 " @@ -4052,12 +4283,21 @@ static int ram_load_precopy(QEMUFile *f) break; case RAM_SAVE_FLAG_EOS: /* normal exit */ - if (migrate_multifd_flush_after_each_section()) { + 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: - ram_control_load_hook(f, RAM_CONTROL_HOOK, NULL); + 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); @@ -4071,7 +4311,6 @@ static int ram_load_precopy(QEMUFile *f) } } - ret |= wait_for_decompress_done(); return ret; } @@ -4097,6 +4336,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) { /* @@ -4133,21 +4373,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(); @@ -4155,31 +4397,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; } /* @@ -4195,30 +4435,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; } /* @@ -4239,16 +4474,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); + qatomic_dec(&rs->postcopy_bmap_sync_requested); + /* - * We succeeded to sync bitmap for current ramblock. If this is - * the last one to sync, we need to notify the main send thread. + * 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. */ - ram_dirty_bitmap_reload_notify(s); + migration_rp_kick(s); - ret = 0; -out: - g_free(le_bitmap); - return ret; + return true; } static int ram_resume_prepare(MigrationState *s, void *opaque) @@ -4266,6 +4503,42 @@ static int ram_resume_prepare(MigrationState *s, void *opaque) return 0; } +static bool ram_save_postcopy_prepare(QEMUFile *f, void *opaque, Error **errp) +{ + int ret; + + if (migrate_multifd()) { + /* + * When multifd is enabled, source QEMU needs to make sure all the + * pages queued before postcopy starts have been flushed. + * + * The load of these pages must happen before switching to postcopy. + * It's because loading of guest pages (so far) in multifd recv + * threads is still non-atomic, so the load cannot happen with vCPUs + * running on the destination side. + * + * This flush and sync will guarantee that those pages are loaded + * _before_ postcopy starts on the destination. The rationale is, + * this happens before VM stops (and before source QEMU sends all + * the rest of the postcopy messages). So when the destination QEMU + * receives the postcopy messages, it must have received the sync + * message on the main channel (either RAM_SAVE_FLAG_MULTIFD_FLUSH, + * or RAM_SAVE_FLAG_EOS), and such message would guarantee that + * all previous guest pages queued in the multifd channels are + * completely loaded. + */ + ret = multifd_ram_flush_and_sync(f); + if (ret < 0) { + error_setg(errp, "%s: multifd flush and sync failed", __func__); + return false; + } + } + + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + + return true; +} + void postcopy_preempt_shutdown_file(MigrationState *s) { qemu_put_be64(s->postcopy_qemufile_src, RAM_SAVE_FLAG_EOS); @@ -4285,6 +4558,7 @@ static SaveVMHandlers savevm_ram_handlers = { .load_setup = ram_load_setup, .load_cleanup = ram_load_cleanup, .resume_prepare = ram_resume_prepare, + .save_postcopy_prepare = ram_save_postcopy_prepare, }; static void ram_mig_ram_block_resized(RAMBlockNotifier *n, void *host, @@ -4295,11 +4569,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 (!rb) { + error_report("RAM block not found"); + return; + } + if (migrate_ram_is_ignored(rb)) { return; } - if (!migration_is_idle()) { + 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 @@ -4307,8 +4586,10 @@ static void ram_mig_ram_block_resized(RAMBlockNotifier *n, void *host, * Abort and indicate a proper reason. */ error_setg(&err, "RAM block '%s' resized during precopy.", rb->idstr); - migration_cancel(err); + migrate_set_error(migrate_get_current(), err); error_free(err); + + migration_cancel(); } switch (ps) { diff --git a/migration/ram.h b/migration/ram.h index 145c915ca75..921c39a2c5c 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -33,8 +33,35 @@ #include "exec/cpu-common.h" #include "io/channel.h" +/* + * 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_SAVE_FLAG_COMPRESS_PAGE just rename it. + * + * RAM_SAVE_FLAG_FULL (0x01) was obsoleted in 2009. + * + * RAM_SAVE_FLAG_COMPRESS_PAGE (0x100) was removed in QEMU 9.1. + * + * RAM_SAVE_FLAG_HOOK is only used in RDMA. Whenever this is found in the + * data stream, the flags will be passed to rdma functions in the + * incoming-migration side. + * + * We can't use any flag that is bigger than 0x200, because the flags are + * always assumed to be encoded in a ramblock address offset, which is + * multiple of PAGE_SIZE. Here it means QEMU supports migration with any + * architecture that has PAGE_SIZE>=1K (0x400). + */ +#define RAM_SAVE_FLAG_ZERO 0x002 +#define RAM_SAVE_FLAG_MEM_SIZE 0x004 +#define RAM_SAVE_FLAG_PAGE 0x008 +#define RAM_SAVE_FLAG_EOS 0x010 +#define RAM_SAVE_FLAG_CONTINUE 0x020 +#define RAM_SAVE_FLAG_XBZRLE 0x040 +#define RAM_SAVE_FLAG_HOOK 0x080 +#define RAM_SAVE_FLAG_MULTIFD_FLUSH 0x200 + extern XBZRLECacheStats xbzrle_counters; -extern CompressionStats compression_counters; /* Should be holding either ram_list.mutex, or the RCU lock. */ #define RAMBLOCK_FOREACH_NOT_IGNORED(block) \ @@ -45,13 +72,15 @@ extern CompressionStats compression_counters; 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); +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); @@ -60,7 +89,7 @@ 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); @@ -69,12 +98,15 @@ 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); diff --git a/migration/rdma.c b/migration/rdma.c index a2a3db35b1d..2d839fce6c4 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -30,7 +30,7 @@ #include "qemu/sockets.h" #include "qemu/bitmap.h" #include "qemu/coroutine.h" -#include "exec/memory.h" +#include "system/memory.h" #include #include #include @@ -40,17 +40,6 @@ #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. */ @@ -85,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: @@ -133,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 @@ -268,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; @@ -275,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) { @@ -319,7 +293,6 @@ typedef struct RDMALocalBlocks { typedef struct RDMAContext { char *host; int port; - char *host_port; RDMAWorkRequestData wr_data[RDMA_WRID_MAX]; @@ -371,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. @@ -458,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; @@ -535,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) @@ -567,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; @@ -578,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, @@ -623,8 +605,6 @@ static int rdma_add_block(RDMAContext *rdma, const char *block_name, block->nb_chunks); local->nb_blocks++; - - return 0; } /* @@ -638,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; } /* @@ -646,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; @@ -654,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; } @@ -710,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); } @@ -728,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--; } } @@ -748,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) { @@ -801,138 +767,6 @@ static void qemu_rdma_dump_gid(const char *who, struct rdma_cm_id *id) trace_qemu_rdma_dump_gid(who, sgid, dgid); } -/* - * As of now, IPv6 over RoCE / iWARP is not supported by linux. - * We will try the next addrinfo struct, and fail if there are - * no other valid addresses to bind against. - * - * If user is listening on '[::]', then we will not have a opened a device - * yet and have no way of verifying if the device is RoCE or not. - * - * In this case, the source VM will throw an error for ALL types of - * connections (both IPv4 and IPv6) if the destination machine does not have - * a regular infiniband network available for use. - * - * The only way to guarantee that an error is thrown for broken kernels is - * for the management software to choose a *specific* interface at bind time - * and validate what time of hardware it is. - * - * Unfortunately, this puts the user in a fix: - * - * If the source VM connects with an IPv4 address without knowing that the - * destination has bound to '[::]' the migration will unconditionally fail - * unless the management software is explicitly listening on the IPv4 - * address while using a RoCE-based device. - * - * If the source VM connects with an IPv6 address, then we're OK because we can - * throw an error on the source (and similarly on the destination). - * - * But in mixed environments, this will be broken for a while until it is fixed - * inside linux. - * - * We do provide a *tiny* bit of help in this function: We can list all of the - * devices in the system and check to see if all the devices are RoCE or - * Infiniband. - * - * If we detect that we have a *pure* RoCE environment, then we can safely - * thrown an error even if the management software has specified '[::]' as the - * bind address. - * - * However, if there is are multiple hetergeneous devices, then we cannot make - * this assumption and the user just has to be sure they know what they are - * doing. - * - * Patches are being reviewed on linux-rdma. - */ -static int qemu_rdma_broken_ipv6_kernel(struct ibv_context *verbs, Error **errp) -{ - /* This bug only exists in linux, to our knowledge. */ -#ifdef CONFIG_LINUX - struct ibv_port_attr port_attr; - - /* - * Verbs are only NULL if management has bound to '[::]'. - * - * Let's iterate through all the devices and see if there any pure IB - * devices (non-ethernet). - * - * If not, then we can safely proceed with the migration. - * Otherwise, there are no guarantees until the bug is fixed in linux. - */ - if (!verbs) { - int num_devices, x; - 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++) { - verbs = ibv_open_device(dev_list[x]); - if (!verbs) { - if (errno == EPERM) { - continue; - } else { - return -EINVAL; - } - } - - if (ibv_query_port(verbs, 1, &port_attr)) { - ibv_close_device(verbs); - ERROR(errp, "Could not query initial IB port"); - return -EINVAL; - } - - if (port_attr.link_layer == IBV_LINK_LAYER_INFINIBAND) { - ib_found = true; - } else if (port_attr.link_layer == IBV_LINK_LAYER_ETHERNET) { - roce_found = true; - } - - ibv_close_device(verbs); - - } - - 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"); - } 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; - } - } - - return 0; - } - - /* - * If we have a verbs context, that means that some other than '[::]' was - * used by the management software for binding. In which case we can - * actually warn the user about a potentially broken kernel. - */ - - /* 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; - } - - 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; - } - -#endif - - return 0; -} - /* * Figure out which RDMA device corresponds to the requested IP hostname * Also create the initial connection manager identifiers for opening @@ -945,24 +779,23 @@ static int qemu_rdma_resolve_host(RDMAContext *rdma, Error **errp) 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; } @@ -970,31 +803,28 @@ 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, exit loop on first success of resolving address */ + for (struct rdma_addrinfo *e = res; e != NULL; e = e->ai_next) { + 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 (e->ai_family == AF_INET6) { - ret = qemu_rdma_broken_ipv6_kernel(rdma->cm_id->verbs, errp); - if (ret) { - continue; - } - } + if (ret >= 0) { goto route; } } rdma_freeaddrinfo(res); - ERROR(errp, "could not resolve address %s", rdma->host); + error_setg(errp, "RDMA ERROR: could not resolve address %s", rdma->host); goto err_resolve_get_addr; route: @@ -1002,38 +832,37 @@ static int qemu_rdma_resolve_host(RDMAContext *rdma, Error **errp) 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); @@ -1048,25 +877,25 @@ static int qemu_rdma_resolve_host(RDMAContext *rdma, Error **errp) 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; } @@ -1076,21 +905,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; } @@ -1123,7 +952,6 @@ static int qemu_rdma_alloc_pd_cq(RDMAContext *rdma) 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; @@ -1133,8 +961,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; } @@ -1146,8 +973,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; } @@ -1176,15 +1003,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; @@ -1197,7 +1020,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; @@ -1219,16 +1047,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; @@ -1245,15 +1073,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, @@ -1265,8 +1091,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; } /* @@ -1309,6 +1133,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; @@ -1325,15 +1155,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++; @@ -1360,18 +1181,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. @@ -1385,6 +1197,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]; @@ -1434,17 +1248,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); @@ -1469,8 +1285,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; @@ -1484,24 +1300,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; } @@ -1512,7 +1323,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); @@ -1522,7 +1333,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; @@ -1540,7 +1351,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() @@ -1557,7 +1367,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; @@ -1576,19 +1386,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); } @@ -1600,30 +1405,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; } @@ -1641,10 +1445,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; @@ -1658,7 +1463,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; @@ -1667,8 +1472,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); } } @@ -1678,20 +1482,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; } @@ -1707,8 +1509,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); } } @@ -1728,8 +1529,8 @@ static int qemu_rdma_block_for_wrid(RDMAContext *rdma, int wrid_requested, ibv_ack_cq_events(cq, num_cq_events); } - rdma->error_state = ret; - return ret; + rdma->errored = true; + return -1; } /* @@ -1737,9 +1538,10 @@ static int qemu_rdma_block_for_wrid(RDMAContext *rdma, int wrid_requested, * 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 = { @@ -1777,23 +1579,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 = { @@ -1810,6 +1614,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; } @@ -1820,15 +1625,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); @@ -1840,22 +1646,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; @@ -1893,20 +1700,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; } } @@ -1914,31 +1725,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; } /* @@ -1947,18 +1754,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); @@ -1978,7 +1786,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, @@ -1990,21 +1798,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); @@ -2012,10 +1819,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; @@ -2027,9 +1833,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 }; @@ -2084,11 +1890,11 @@ static int qemu_rdma_write_one(QEMUFile *f, RDMAContext *rdma, 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; } } @@ -2116,15 +1922,24 @@ static int qemu_rdma_write_one(QEMUFile *f, RDMAContext *rdma, 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; } + /* + * 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; } @@ -2144,17 +1959,17 @@ static int qemu_rdma_write_one(QEMUFile *f, RDMAContext *rdma, 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 *) @@ -2172,8 +1987,8 @@ static int qemu_rdma_write_one(QEMUFile *f, RDMAContext *rdma, 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; } } @@ -2184,8 +1999,8 @@ static int qemu_rdma_write_one(QEMUFile *f, RDMAContext *rdma, 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; } } @@ -2218,22 +2033,32 @@ static int qemu_rdma_write_one(QEMUFile *f, RDMAContext *rdma, 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); 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); - qemu_file_credit_transfer(f, sge.length); rdma->total_writes++; return 0; @@ -2245,7 +2070,7 @@ static int qemu_rdma_write_one(QEMUFile *f, RDMAContext *rdma, * 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; @@ -2253,11 +2078,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) { @@ -2271,7 +2096,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; @@ -2279,11 +2104,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]); @@ -2291,29 +2116,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; } /* @@ -2326,30 +2151,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; } @@ -2359,7 +2178,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; @@ -2367,18 +2186,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); @@ -2392,12 +2213,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) { @@ -2455,16 +2276,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 @@ -2472,44 +2290,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; } } @@ -2517,7 +2328,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; } @@ -2538,20 +2348,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, @@ -2576,16 +2393,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; } @@ -2593,16 +2409,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; } @@ -2616,8 +2433,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; } @@ -2636,35 +2453,35 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp, bool return_path) static int qemu_rdma_dest_init(RDMAContext *rdma, Error **errp) { - int ret, idx; + 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; } @@ -2672,37 +2489,35 @@ 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 */ for (e = res; e != NULL; e = e->ai_next) { + 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) { - continue; - } - } break; } rdma_freeaddrinfo(res); if (!e) { - ERROR(errp, "Error: could not rdma_bind_addr!"); + error_setg(errp, "RDMA ERROR: Error: could not rdma_bind_addr!"); goto err_dest_init_bind_addr; } @@ -2715,19 +2530,17 @@ static int qemu_rdma_dest_init(RDMAContext *rdma, Error **errp) 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*/ @@ -2739,30 +2552,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; } @@ -2780,12 +2579,10 @@ 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); @@ -2795,24 +2592,27 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, 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; - error_setg(errp, "qemu_rdma_write_flush returned %d", 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; @@ -2820,11 +2620,11 @@ 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; - error_setg(errp, "qemu_rdma_exchange_send returned %d", ret); + rdma->errored = true; return -1; } @@ -2869,9 +2669,9 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, 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); @@ -2881,9 +2681,13 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, 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; @@ -2892,9 +2696,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; @@ -2910,20 +2714,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; - error_setg(errp, "qemu_rdma_exchange_recv returned %d", 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) { @@ -2940,19 +2744,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; } } @@ -3076,7 +2880,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, @@ -3187,21 +2991,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; } @@ -3223,30 +3027,28 @@ 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; - if (migration_in_postcopy()) { - return RAM_SAVE_CONTROL_NOT_SUPP; - } - RCU_READ_LOCK_GUARD(); rdma = qatomic_rcu_read(&rioc->rdmaout); if (!rdma) { - return -EIO; + return -1; } - CHECK_ERROR_STATE(); + if (rdma_errored(rdma)) { + return -1; + } qemu_fflush(f); @@ -3255,24 +3057,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. @@ -3282,9 +3072,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; } @@ -3297,9 +3088,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; } @@ -3311,8 +3103,24 @@ 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) +{ + assert(migrate_rdma()); + + int ret = qemu_rdma_save_page(f, block_offset, offset, size); + + if (ret != RAM_SAVE_CONTROL_DELAYED) { + if (ret < 0) { + qemu_file_set_error(f, ret); + } + } return ret; } @@ -3321,25 +3129,23 @@ 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); @@ -3353,6 +3159,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, @@ -3360,13 +3167,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; } @@ -3375,13 +3182,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() || migrate_return_path()) && !rdma->is_return_path) { - rdma_return_path = qemu_rdma_data_init(rdma->host_port, NULL); + 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; @@ -3395,10 +3205,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; } /* @@ -3428,35 +3238,31 @@ 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; } } @@ -3473,14 +3279,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; } @@ -3493,9 +3299,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; } @@ -3504,10 +3310,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) @@ -3527,7 +3333,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) +int rdma_registration_handle(QEMUFile *f) { RDMAControlHeader reg_resp = { .len = sizeof(RDMARegisterResult), .type = RDMA_CONTROL_REGISTER_RESULT, @@ -3539,7 +3345,8 @@ static int qemu_rdma_registration_handle(QEMUFile *f) }; RDMAControlHeader blocks = { .type = RDMA_CONTROL_RAM_BLOCKS_RESULT, .repeat = 1 }; - QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); + QIOChannelRDMA *rioc; + Error *err = NULL; RDMAContext *rdma; RDMALocalBlocks *local; RDMAControlHeader head; @@ -3549,34 +3356,39 @@ static int qemu_rdma_registration_handle(QEMUFile *f) 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; } @@ -3585,30 +3397,33 @@ static int qemu_rdma_registration_handle(QEMUFile *f) 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 @@ -3617,16 +3432,15 @@ static int qemu_rdma_registration_handle(QEMUFile *f) 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; } } @@ -3636,7 +3450,7 @@ static int qemu_rdma_registration_handle(QEMUFile *f) * 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); @@ -3648,7 +3462,7 @@ static int qemu_rdma_registration_handle(QEMUFile *f) 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, @@ -3661,21 +3475,22 @@ static int qemu_rdma_registration_handle(QEMUFile *f) 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; @@ -3684,15 +3499,14 @@ static int qemu_rdma_registration_handle(QEMUFile *f) 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) { @@ -3701,8 +3515,7 @@ static int qemu_rdma_registration_handle(QEMUFile *f) " 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)); @@ -3717,8 +3530,7 @@ static int qemu_rdma_registration_handle(QEMUFile *f) 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); @@ -3729,37 +3541,35 @@ static int qemu_rdma_registration_handle(QEMUFile *f) (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]); @@ -3768,61 +3578,58 @@ static int qemu_rdma_registration_handle(QEMUFile *f) 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(QEMUFile *f, const char *name) +int rdma_block_notification_handle(QEMUFile *f, const char *name) { - RDMAContext *rdma; - QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); 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 */ @@ -3835,7 +3642,7 @@ rdma_block_notification_handle(QEMUFile *f, 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; @@ -3845,72 +3652,57 @@ rdma_block_notification_handle(QEMUFile *f, const char *name) return 0; } -static int rdma_load_hook(QEMUFile *f, uint64_t flags, void *data) -{ - switch (flags) { - case RAM_CONTROL_BLOCK_REG: - return rdma_block_notification_handle(f, data); - - case RAM_CONTROL_HOOK: - return qemu_rdma_registration_handle(f); - - default: - /* Shouldn't be called with any other values */ - abort(); - } -} - -static int qemu_rdma_registration_start(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)); - RDMAContext *rdma; - - if (migration_in_postcopy()) { + if (!migrate_rdma()) { return 0; } + QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); RCU_READ_LOCK_GUARD(); - rdma = qatomic_rcu_read(&rioc->rdmaout); + RDMAContext *rdma = qatomic_rcu_read(&rioc->rdmaout); if (!rdma) { - return -EIO; + return -1; } - CHECK_ERROR_STATE(); + if (rdma_errored(rdma)) { + return -1; + } - trace_qemu_rdma_registration_start(flags); + trace_rdma_registration_start(flags); qemu_put_be64(f, RAM_SAVE_FLAG_HOOK); - qemu_fflush(f); - - return 0; + 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; - if (migration_in_postcopy()) { + if (!migrate_rdma()) { return 0; } RCU_READ_LOCK_GUARD(); + rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); rdma = qatomic_rcu_read(&rioc->rdmaout); if (!rdma) { - return -EIO; + return -1; } - CHECK_ERROR_STATE(); + 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; @@ -3919,10 +3711,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. @@ -3934,10 +3726,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); @@ -3955,28 +3748,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; @@ -3984,32 +3778,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); @@ -4026,7 +3810,7 @@ static void qio_channel_rdma_finalize(Object *obj) } static void qio_channel_rdma_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); @@ -4061,7 +3845,6 @@ static QEMUFile *rdma_new_input(RDMAContext *rdma) 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); return rioc->file; } @@ -4073,7 +3856,6 @@ static QEMUFile *rdma_new_output(RDMAContext *rdma) 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); return rioc->file; } @@ -4081,15 +3863,11 @@ static QEMUFile *rdma_new_output(RDMAContext *rdma) 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; } @@ -4101,23 +3879,21 @@ static void rdma_accept_incoming_migration(void *opaque) f = rdma_new_input(rdma); if (f == NULL) { - fprintf(stderr, "RDMA ERROR: could not open RDMA for input\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; - Error *local_err = NULL; trace_rdma_start_incoming_migration(); @@ -4127,14 +3903,13 @@ void rdma_start_incoming_migration(const char *host_port, Error **errp) return; } - rdma = qemu_rdma_data_init(host_port, &local_err); + rdma = qemu_rdma_data_init(host_port, errp); if (rdma == NULL) { goto err; } - ret = qemu_rdma_dest_init(rdma, &local_err); - - if (ret) { + ret = qemu_rdma_dest_init(rdma, errp); + if (ret < 0) { goto err; } @@ -4142,13 +3917,13 @@ void rdma_start_incoming_migration(const char *host_port, Error **errp) ret = rdma_listen(rdma->listen_id, 5); - if (ret) { - ERROR(errp, "listening on socket!"); + 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; @@ -4156,21 +3931,19 @@ void rdma_start_incoming_migration(const char *host_port, Error **errp) 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); } void rdma_start_outgoing_migration(void *opaque, - const char *host_port, Error **errp) + InetSocketAddress *host_port, Error **errp) { MigrationState *s = opaque; RDMAContext *rdma_return_path = NULL; RDMAContext *rdma; - int ret = 0; + int ret; /* Avoid ram_block_discard_disable(), cannot change during migration. */ if (ram_block_discard_is_required()) { @@ -4185,14 +3958,14 @@ void rdma_start_outgoing_migration(void *opaque, ret = qemu_rdma_source_init(rdma, migrate_rdma_pin_all(), errp); - if (ret) { + 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; } @@ -4207,13 +3980,13 @@ void rdma_start_outgoing_migration(void *opaque, ret = qemu_rdma_source_init(rdma_return_path, 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; } @@ -4225,7 +3998,8 @@ void rdma_start_outgoing_migration(void *opaque, trace_rdma_start_outgoing_migration_after_rdma_connect(); s->to_dst_file = rdma_new_output(rdma); - migrate_fd_connect(s, NULL); + s->rdma_migration = true; + migration_connect(s, NULL); return; return_path_err: qemu_rdma_cleanup(rdma); diff --git a/migration/rdma.h b/migration/rdma.h index de2ba09dc5a..f74f16a4599 100644 --- a/migration/rdma.h +++ b/migration/rdma.h @@ -14,12 +14,48 @@ * */ +#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 "system/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 + +#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) +{ + g_assert_not_reached(); +} +#endif #endif diff --git a/migration/savevm.c b/migration/savevm.c index bb3e99194c6..bb04a4520df 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -37,6 +37,7 @@ #include "migration/register.h" #include "migration/global_state.h" #include "migration/channel-block.h" +#include "multifd.h" #include "ram.h" #include "qemu-file.h" #include "savevm.h" @@ -45,29 +46,30 @@ #include "qapi/qapi-commands-migration.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 "system/cpus.h" +#include "system/memory.h" #include "exec/target_page.h" +#include "exec/page-vary.h" #include "trace.h" #include "qemu/iov.h" #include "qemu/job.h" #include "qemu/main-loop.h" #include "block/snapshot.h" +#include "block/thread-pool.h" #include "qemu/cutils.h" #include "io/channel-buffer.h" #include "io/channel-file.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" -#include "sysemu/xen.h" +#include "system/replay.h" +#include "system/runstate.h" +#include "system/system.h" +#include "system/xen.h" #include "migration/colo.h" #include "qemu/bitmap.h" #include "net/announce.h" #include "qemu/yank.h" #include "yank_functions.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "options.h" const unsigned int postcopy_ram_discard_version; @@ -91,6 +93,7 @@ enum qemu_vm_cmd { MIG_CMD_ENABLE_COLO, /* Enable COLO */ MIG_CMD_POSTCOPY_RESUME, /* resume postcopy on dest */ MIG_CMD_RECV_BITMAP, /* Request for recved bitmap on dst */ + MIG_CMD_SWITCHOVER_START, /* Switchover start notification */ MIG_CMD_MAX }; @@ -110,6 +113,7 @@ static struct mig_cmd_args { [MIG_CMD_POSTCOPY_RESUME] = { .len = 0, .name = "POSTCOPY_RESUME" }, [MIG_CMD_PACKAGED] = { .len = 4, .name = "PACKAGED" }, [MIG_CMD_RECV_BITMAP] = { .len = -1, .name = "RECV_BITMAP" }, + [MIG_CMD_SWITCHOVER_START] = { .len = 0, .name = "SWITCHOVER_START" }, [MIG_CMD_MAX] = { .len = -1, .name = "MAX" }, }; @@ -130,6 +134,35 @@ static struct mig_cmd_args { * generic extendable format with an exception for two old entities. */ +/***********************************************************/ +/* Optional load threads pool support */ + +static void qemu_loadvm_thread_pool_create(MigrationIncomingState *mis) +{ + assert(!mis->load_threads); + mis->load_threads = thread_pool_new(); + mis->load_threads_abort = false; +} + +static void qemu_loadvm_thread_pool_destroy(MigrationIncomingState *mis) +{ + qatomic_set(&mis->load_threads_abort, true); + + bql_unlock(); /* Load threads might be waiting for BQL */ + g_clear_pointer(&mis->load_threads, thread_pool_free); + bql_lock(); +} + +static bool qemu_loadvm_thread_pool_wait(MigrationState *s, + MigrationIncomingState *mis) +{ + bql_unlock(); /* Let load threads do work requiring BQL */ + thread_pool_wait(mis->load_threads); + bql_lock(); + + return !migrate_has_error(s); +} + /***********************************************************/ /* savevm/loadvm support */ @@ -233,16 +266,19 @@ typedef struct SaveState { static SaveState savevm_state = { .handlers = QTAILQ_HEAD_INITIALIZER(savevm_state.handlers), - .handler_pri_head = { [MIG_PRI_DEFAULT ... MIG_PRI_MAX] = NULL }, + .handler_pri_head = { [0 ... MIG_PRI_MAX] = NULL }, .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; @@ -304,7 +340,7 @@ static int configuration_pre_load(void *opaque) * predates the variable-target-page-bits support and is using the * minimum possible value for this CPU. */ - state->target_page_bits = qemu_target_page_bits_min(); + state->target_page_bits = migration_legacy_page_bits(); return 0; } @@ -427,8 +463,7 @@ static const VMStateInfo vmstate_info_capability = { */ static bool vmstate_target_page_bits_needed(void *opaque) { - return qemu_target_page_bits() - > qemu_target_page_bits_min(); + return qemu_target_page_bits() > migration_legacy_page_bits(); } static const VMStateDescription vmstate_target_page_bits = { @@ -436,7 +471,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() } @@ -452,7 +487,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, @@ -469,8 +504,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) { /* @@ -497,7 +532,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() } @@ -510,12 +545,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, @@ -549,11 +584,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); } } @@ -595,7 +630,7 @@ static void dump_vmstate_vmsd(FILE *out_file, 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, ""); @@ -604,7 +639,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; } @@ -702,7 +737,7 @@ static int calculate_compat_instance_id(const char *idstr) static inline MigrationPriority save_state_priority(SaveStateEntry *se) { - if (se->vmsd) { + if (se->vmsd && se->vmsd->priority) { return se->vmsd->priority; } return MIG_PRI_DEFAULT; @@ -716,6 +751,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) { @@ -817,7 +864,7 @@ void unregister_savevm(VMStateIf *obj, const char *idstr, void *opaque) static void vmstate_check(const VMStateDescription *vmsd) { const VMStateField *field = vmsd->fields; - const VMStateDescription **subsection = vmsd->subsections; + const VMStateDescription * const *subsection = vmsd->subsections; if (field) { while (field->name) { @@ -846,6 +893,7 @@ static void vmstate_check(const VMStateDescription *vmsd) } } + int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id, const VMStateDescription *vmsd, void *opaque, int alias_id, @@ -927,9 +975,9 @@ static int vmstate_load(QEMUFile *f, SaveStateEntry *se) static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc) { - uint64_t old_offset = qemu_file_transferred_noflush(f); + uint64_t old_offset = qemu_file_transferred(f); se->ops->save_state(f, se->opaque); - uint64_t size = qemu_file_transferred_noflush(f) - old_offset; + uint64_t size = qemu_file_transferred(f) - old_offset; if (vmdesc) { json_writer_int64(vmdesc, "size", size); @@ -976,14 +1024,15 @@ static void save_section_footer(QEMUFile *f, SaveStateEntry *se) } } -static int vmstate_save(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc) +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_save_needed(se->vmsd, se->opaque)) { + if (se->vmsd && !vmstate_section_needed(se->vmsd, se->opaque)) { trace_savevm_section_skip(se->idstr, se->section_id); return 0; } @@ -1000,7 +1049,8 @@ static int vmstate_save(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc) if (!se->vmsd) { vmstate_save_old_style(f, se, vmdesc); } else { - ret = vmstate_save_state(f, se->vmsd, se->opaque, vmdesc); + ret = vmstate_save_state_with_err(f, se->vmsd, se->opaque, vmdesc, + errp); if (ret) { return ret; } @@ -1068,10 +1118,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; } @@ -1180,6 +1234,19 @@ void qemu_savevm_send_recv_bitmap(QEMUFile *f, char *block_name) qemu_savevm_command_send(f, MIG_CMD_RECV_BITMAP, len + 1, (uint8_t *)buf); } +static void qemu_savevm_send_switchover_start(QEMUFile *f) +{ + trace_savevm_send_switchover_start(); + qemu_savevm_command_send(f, MIG_CMD_SWITCHOVER_START, 0, NULL); +} + +void qemu_savevm_maybe_send_switchover_start(QEMUFile *f) +{ + if (migrate_send_switchover_start()) { + qemu_savevm_send_switchover_start(f); + } +} + bool qemu_savevm_state_blocked(Error **errp) { SaveStateEntry *se; @@ -1209,13 +1276,31 @@ void qemu_savevm_non_migratable_list(strList **reasons) void qemu_savevm_state_header(QEMUFile *f) { + MigrationState *s = migrate_get_current(); + JSONWriter *vmdesc = s->vmdesc; + 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); + + if (vmdesc) { + /* + * 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(vmdesc, NULL); + json_writer_start_object(vmdesc, "configuration"); + } + + vmstate_save_state(f, &vmstate_configuration, &savevm_state, vmdesc); + + if (vmdesc) { + json_writer_end_object(vmdesc); + } } } @@ -1257,23 +1342,25 @@ int qemu_savevm_state_prepare(Error **errp) return 0; } -void qemu_savevm_state_setup(QEMUFile *f) +int qemu_savevm_state_setup(QEMUFile *f, Error **errp) { + ERRP_GUARD(); MigrationState *ms = migrate_get_current(); + JSONWriter *vmdesc = ms->vmdesc; SaveStateEntry *se; - Error *local_err = NULL; - int ret; + int ret = 0; - ms->vmdesc = json_writer_new(false); - json_writer_start_object(ms->vmdesc, NULL); - json_writer_int64(ms->vmdesc, "page_size", qemu_target_page_size()); - json_writer_start_array(ms->vmdesc, "devices"); + if (vmdesc) { + json_writer_int64(vmdesc, "page_size", qemu_target_page_size()); + json_writer_start_array(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); + ret = vmstate_save(f, se, vmdesc, errp); if (ret) { + migrate_set_error(ms, *errp); qemu_file_set_error(f, ret); break; } @@ -1290,7 +1377,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); @@ -1298,9 +1385,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) @@ -1337,7 +1427,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) { @@ -1378,23 +1469,19 @@ 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) +bool should_send_vmdesc(void) { MachineState *machine = MACHINE(qdev_get_machine()); - bool in_postcopy = migration_in_postcopy(); - return !machine->suppress_vmdesc && !in_postcopy; + + return !machine->suppress_vmdesc; } /* @@ -1436,11 +1523,62 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f) qemu_fflush(f); } -static +bool qemu_savevm_state_postcopy_prepare(QEMUFile *f, Error **errp) +{ + SaveStateEntry *se; + bool ret; + + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { + if (!se->ops || !se->ops->save_postcopy_prepare) { + continue; + } + + if (se->ops->is_active) { + if (!se->ops->is_active(se->opaque)) { + continue; + } + } + + trace_savevm_section_start(se->idstr, se->section_id); + + save_section_header(f, se, QEMU_VM_SECTION_PART); + ret = se->ops->save_postcopy_prepare(f, se->opaque, errp); + save_section_footer(f, se); + + trace_savevm_section_end(se->idstr, se->section_id, ret); + + if (!ret) { + assert(*errp); + return false; + } + } + + return true; +} + int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) { + int64_t start_ts_each, end_ts_each; SaveStateEntry *se; int ret; + bool multifd_device_state = multifd_device_state_supported(); + + if (multifd_device_state) { + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { + SaveLiveCompletePrecopyThreadHandler hdlr; + + if (!se->ops || (in_postcopy && se->ops->has_postcopy && + se->ops->has_postcopy(se->opaque)) || + !se->ops->save_live_complete_precopy_thread) { + continue; + } + + hdlr = se->ops->save_live_complete_precopy_thread; + multifd_spawn_device_state_save_thread(hdlr, + se->idstr, se->instance_id, + se->opaque); + } + } QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (!se->ops || @@ -1455,6 +1593,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); @@ -1464,104 +1604,109 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) save_section_footer(f, se); if (ret < 0) { qemu_file_set_error(f, ret); + goto ret_fail_abort_threads; + } + 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); + } + + if (multifd_device_state) { + if (migrate_has_error(migrate_get_current())) { + multifd_abort_device_state_save_threads(); + } + + if (!multifd_join_device_state_save_threads()) { + qemu_file_set_error(f, -EINVAL); return -1; } } + trace_vmstate_downtime_checkpoint("src-iterable-saved"); + return 0; + +ret_fail_abort_threads: + if (multifd_device_state) { + multifd_abort_device_state_save_threads(); + multifd_join_device_state_save_threads(); + } + + return -1; } int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, - bool in_postcopy, - bool inactivate_disks) + bool in_postcopy) { 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; + /* Making sure cpu states are synchronized before saving non-iterable */ + cpu_synchronize_all_states(); + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (se->vmsd && se->vmsd->early_setup) { /* Already saved during qemu_savevm_state_setup(). */ continue; } - ret = vmstate_save(f, se, vmdesc); - if (ret) { - qemu_file_set_error(f, ret); - return ret; - } - } + start_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); - if (inactivate_disks) { - /* Inactivate before sending QEMU_VM_EOF so that the - * bdrv_activate_all() on the other end won't fail. */ - ret = bdrv_inactivate_all(); + ret = vmstate_save(f, se, vmdesc, &local_err); if (ret) { - error_report("%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; } + + 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 (!in_postcopy) { /* Postcopy stream will still be going */ qemu_put_byte(f, QEMU_VM_EOF); - } - json_writer_end_array(vmdesc); - json_writer_end_object(vmdesc); - vmdesc_len = strlen(json_writer_get(vmdesc)); + if (vmdesc) { + json_writer_end_array(vmdesc); + json_writer_end_object(vmdesc); + vmdesc_len = strlen(json_writer_get(vmdesc)); - if (should_send_vmdesc()) { - qemu_put_byte(f, QEMU_VM_VMDESCRIPTION); - qemu_put_be32(f, vmdesc_len); - qemu_put_buffer(f, (uint8_t *)json_writer_get(vmdesc), vmdesc_len); + qemu_put_byte(f, QEMU_VM_VMDESCRIPTION); + qemu_put_be32(f, vmdesc_len); + 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; } -int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only, - bool inactivate_disks) +int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only) { int ret; - Error *local_err = NULL; - bool in_postcopy = migration_in_postcopy(); - if (precopy_notify(PRECOPY_NOTIFY_COMPLETE, &local_err)) { - error_report_err(local_err); + ret = qemu_savevm_state_complete_precopy_iterable(f, false); + if (ret) { + return ret; } - trace_savevm_state_complete_precopy(); - - cpu_synchronize_all_states(); - - if (!in_postcopy || iterable_only) { - ret = qemu_savevm_state_complete_precopy_iterable(f, in_postcopy); + if (!iterable_only) { + ret = qemu_savevm_state_complete_precopy_non_iterable(f, false); if (ret) { return ret; } } - if (iterable_only) { - goto flush; - } - - ret = qemu_savevm_state_complete_precopy_non_iterable(f, in_postcopy, - inactivate_disks); - if (ret) { - return ret; - } - -flush: - qemu_fflush(f); - return 0; + return qemu_fflush(f); } /* Give an estimate of the amount left to be transferred, @@ -1633,13 +1778,8 @@ 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); - return -EINVAL; - } - - if (migrate_block()) { - error_setg(errp, "Block migration and snapshots are incompatible"); + if (migration_is_running()) { + error_setg(errp, "There's a migration process in progress"); return -EINVAL; } @@ -1649,10 +1789,11 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) } ms->to_dst_file = f; - 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) { @@ -1662,13 +1803,15 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) ret = qemu_file_get_error(f); if (ret == 0) { - qemu_savevm_state_complete_precopy(f, false, false); + qemu_savevm_maybe_send_switchover_start(f); + qemu_savevm_state_complete_precopy(f, 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; @@ -1687,12 +1830,14 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) void qemu_savevm_live_state(QEMUFile *f) { /* save QEMU_VM_SECTION_END section */ - qemu_savevm_state_complete_precopy(f, true, false); + qemu_savevm_state_complete_precopy(f, true); qemu_put_byte(f, QEMU_VM_EOF); } int qemu_save_device_state(QEMUFile *f) { + MigrationState *ms = migrate_get_current(); + Error *local_err = NULL; SaveStateEntry *se; if (!migration_in_colo_state()) { @@ -1707,8 +1852,10 @@ int qemu_save_device_state(QEMUFile *f) if (se->is_ram) { continue; } - 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; } } @@ -1931,7 +2078,7 @@ static void *postcopy_ram_listen_thread(void *opaque) migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_POSTCOPY_ACTIVE); - qemu_sem_post(&mis->thread_sync_sem); + qemu_event_set(&mis->thread_sync_event); trace_postcopy_ram_listen_thread_start(); rcu_register_thread(); @@ -1940,6 +2087,8 @@ static void *postcopy_ram_listen_thread(void *opaque) * in qemu_file, and thus we must be blocking now. */ qemu_file_set_blocking(f, true); + + /* TODO: sanity check that only postcopiable data will be loaded here */ load_res = qemu_loadvm_state_main(f, mis); /* @@ -2000,8 +2149,9 @@ static void *postcopy_ram_listen_thread(void *opaque) * (If something broke then qemu will have to exit anyway since it's * got a bad migration state). */ + bql_lock(); migration_incoming_state_destroy(); - qemu_loadvm_state_cleanup(); + bql_unlock(); rcu_unregister_thread(); mis->have_listen_thread = false; @@ -2056,7 +2206,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"); @@ -2065,46 +2216,41 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) 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"); - - /* 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); - if (local_err) { - error_report_err(local_err); - local_err = NULL; - autostart = false; - } - - trace_loadvm_postcopy_handle_run_bh("after invalidate cache"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-announced"); dirty_bitmap_mig_before_vm_start(); if (autostart) { - /* Hold onto your hats, starting the CPU */ - vm_start(); + /* + * Make sure all file formats throw away their mutable metadata. + * If we get an error here, just don't restart the VM yet. + */ + bool success = migration_block_activate(NULL); + + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cache-invalidated"); + + if (success) { + vm_start(); + } } else { /* leave it paused and let management decide when to start the CPU */ 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 */ @@ -2119,8 +2265,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 @@ -2274,6 +2419,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); @@ -2338,6 +2504,26 @@ static int loadvm_process_enable_colo(MigrationIncomingState *mis) return ret; } +static int loadvm_postcopy_handle_switchover_start(void) +{ + SaveStateEntry *se; + + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { + int ret; + + if (!se->ops || !se->ops->switchover_start) { + continue; + } + + ret = se->ops->switchover_start(se->opaque); + if (ret < 0) { + return ret; + } + } + + return 0; +} + /* * Process an incoming 'QEMU_VM_COMMAND' * 0 just a normal return @@ -2436,6 +2622,9 @@ static int loadvm_process_command(QEMUFile *f) case MIG_CMD_ENABLE_COLO: return loadvm_process_enable_colo(mis); + + case MIG_CMD_SWITCHOVER_START: + return loadvm_postcopy_handle_switchover_start(); } return 0; @@ -2485,9 +2674,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; @@ -2536,12 +2727,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; } @@ -2550,8 +2752,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; @@ -2576,12 +2780,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; } @@ -2613,13 +2828,11 @@ 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; } } @@ -2643,8 +2856,9 @@ static void qemu_loadvm_state_switchover_ack_needed(MigrationIncomingState *mis) trace_loadvm_state_switchover_ack_needed(mis->switchover_ack_pending_num); } -static int qemu_loadvm_state_setup(QEMUFile *f) +static int qemu_loadvm_state_setup(QEMUFile *f, Error **errp) { + ERRP_GUARD(); SaveStateEntry *se; int ret; @@ -2659,26 +2873,79 @@ 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; } } return 0; } -void qemu_loadvm_state_cleanup(void) +struct LoadThreadData { + MigrationLoadThread function; + void *opaque; +}; + +static int qemu_loadvm_load_thread(void *thread_opaque) +{ + struct LoadThreadData *data = thread_opaque; + MigrationIncomingState *mis = migration_incoming_get_current(); + g_autoptr(Error) local_err = NULL; + + if (!data->function(data->opaque, &mis->load_threads_abort, &local_err)) { + MigrationState *s = migrate_get_current(); + + /* + * Can't set load_threads_abort here since processing of main migration + * channel data could still be happening, resulting in launching of new + * load threads. + */ + + assert(local_err); + + /* + * In case of multiple load threads failing which thread error + * return we end setting is purely arbitrary. + */ + migrate_set_error(s, local_err); + } + + return 0; +} + +void qemu_loadvm_start_load_thread(MigrationLoadThread function, + void *opaque) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + struct LoadThreadData *data; + + /* We only set it from this thread so it's okay to read it directly */ + assert(!mis->load_threads_abort); + + data = g_new(struct LoadThreadData, 1); + data->function = function; + data->opaque = opaque; + + thread_pool_submit_immediate(mis->load_threads, qemu_loadvm_load_thread, + data, g_free); +} + +void qemu_loadvm_state_cleanup(MigrationIncomingState *mis) { SaveStateEntry *se; trace_loadvm_state_cleanup(); + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (se->ops && se->ops->load_cleanup) { se->ops->load_cleanup(se->opaque); } } + + qemu_loadvm_thread_pool_destroy(mis); } /* Return true if we should continue the migration, or false. */ @@ -2723,7 +2990,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 */ @@ -2742,9 +3010,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(); @@ -2769,14 +3037,14 @@ int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) 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; } @@ -2828,6 +3096,7 @@ int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) int qemu_loadvm_state(QEMUFile *f) { + MigrationState *s = migrate_get_current(); MigrationIncomingState *mis = migration_incoming_get_current(); Error *local_err = NULL; int ret; @@ -2837,12 +3106,15 @@ int qemu_loadvm_state(QEMUFile *f) return -EINVAL; } + qemu_loadvm_thread_pool_create(mis); + ret = qemu_loadvm_state_header(f); if (ret) { 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; } @@ -2858,13 +3130,27 @@ 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; } + /* When reaching here, it must be precopy */ if (ret == 0) { - ret = qemu_file_get_error(f); + if (migrate_has_error(migrate_get_current()) || + !qemu_loadvm_thread_pool_wait(s, mis)) { + ret = -EINVAL; + } else { + ret = qemu_file_get_error(f); + } } + /* + * Set this flag unconditionally so we'll catch further attempts to + * start additional threads via an appropriate assert() + */ + qatomic_set(&mis->load_threads_abort, true); /* * Try to read in the VMDESC section as well, so that dumping tools that @@ -2901,7 +3187,6 @@ int qemu_loadvm_state(QEMUFile *f) } } - qemu_loadvm_state_cleanup(); cpu_synchronize_all_post_init(); return ret; @@ -2941,6 +3226,29 @@ int qemu_loadvm_approve_switchover(void) return migrate_send_rp_switchover_ack(mis); } +bool qemu_loadvm_load_state_buffer(const char *idstr, uint32_t instance_id, + char *buf, size_t len, Error **errp) +{ + SaveStateEntry *se; + + se = find_se(idstr, instance_id); + if (!se) { + error_setg(errp, + "Unknown idstr %s or instance id %u for load state buffer", + idstr, instance_id); + return false; + } + + if (!se->ops || !se->ops->load_state_buffer) { + error_setg(errp, + "idstr %s / instance %u has no load state buffer operation", + idstr, instance_id); + return false; + } + + return se->ops->load_state_buffer(se->opaque, buf, len, errp); +} + bool save_snapshot(const char *name, bool overwrite, const char *vmstate, bool has_devices, strList *devices, Error **errp) { @@ -2948,10 +3256,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(); @@ -2994,17 +3301,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(); 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 */ @@ -3031,7 +3333,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_transferred_noflush(f); + vm_state_size = qemu_file_transferred(f); ret2 = qemu_fclose(f); if (ret < 0) { goto the_end; @@ -3041,14 +3343,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) { @@ -3059,15 +3353,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; } @@ -3099,7 +3387,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 @@ -3108,11 +3396,7 @@ void qmp_xen_save_devices_state(const char *filename, bool has_live, bool live, * side of the migration take control of the images. */ if (live && !saved_vm_running) { - ret = bdrv_inactivate_all(); - if (ret) { - error_setg(errp, "%s: bdrv_inactivate_all() failed (%d)", - __func__, ret); - } + migration_block_inactivate(); } } @@ -3148,7 +3432,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(); } @@ -3160,7 +3444,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)) { @@ -3180,13 +3463,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 " @@ -3222,10 +3503,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(); @@ -3241,6 +3520,14 @@ bool load_snapshot(const char *name, const char *vmstate, 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) { @@ -3260,12 +3547,14 @@ void vmstate_register_ram(MemoryRegion *mr, DeviceState *dev) qemu_ram_set_idstr(mr->ram_block, memory_region_name(mr), dev); qemu_ram_set_migratable(mr->ram_block); + ram_block_add_cpr_blocker(mr->ram_block, &error_fatal); } void vmstate_unregister_ram(MemoryRegion *mr, DeviceState *dev) { qemu_ram_unset_idstr(mr->ram_block); qemu_ram_unset_migratable(mr->ram_block); + ram_block_del_cpr_blocker(mr->ram_block); } void vmstate_register_ram_global(MemoryRegion *mr) @@ -3305,16 +3594,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 74669733dd6..2d5e9c71668 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -32,19 +32,20 @@ bool qemu_savevm_state_blocked(Error **errp); void qemu_savevm_non_migratable_list(strList **reasons); int qemu_savevm_state_prepare(Error **errp); -void qemu_savevm_state_setup(QEMUFile *f); +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); int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy); 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); +int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_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); +int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy); +bool qemu_savevm_state_postcopy_prepare(QEMUFile *f, Error **errp); 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); @@ -53,6 +54,7 @@ void qemu_savevm_send_postcopy_listen(QEMUFile *f); void qemu_savevm_send_postcopy_run(QEMUFile *f); void qemu_savevm_send_postcopy_resume(QEMUFile *f); void qemu_savevm_send_recv_bitmap(QEMUFile *f, char *block_name); +void qemu_savevm_maybe_send_switchover_start(QEMUFile *f); void qemu_savevm_send_postcopy_ram_discard(QEMUFile *f, const char *name, uint16_t len, @@ -63,11 +65,14 @@ void qemu_savevm_live_state(QEMUFile *f); int qemu_save_device_state(QEMUFile *f); int qemu_loadvm_state(QEMUFile *f); -void qemu_loadvm_state_cleanup(void); +void qemu_loadvm_state_cleanup(MigrationIncomingState *mis); 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); + bool in_postcopy); + +bool qemu_loadvm_load_state_buffer(const char *idstr, uint32_t instance_id, + char *buf, size_t len, Error **errp); #endif diff --git a/migration/socket.c b/migration/socket.c index 1b6f5baefbc..5ec65b8c039 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -28,6 +28,8 @@ #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; @@ -40,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; @@ -108,19 +81,19 @@ static void socket_outgoing_migration(QIOTask *task, 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); @@ -135,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, @@ -172,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(); @@ -213,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 dc54df4e6cc..04ebbe95a13 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 a6ffa9a5ce3..12fd399f0c5 100644 --- a/migration/target.c +++ b/migration/target.c @@ -11,21 +11,21 @@ #include CONFIG_DEVICES #ifdef CONFIG_VFIO -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-migration.h" #endif #ifdef CONFIG_VFIO void migration_populate_vfio_info(MigrationInfo *info) { - if (vfio_mig_active()) { + if (vfio_migration_active()) { info->vfio = g_malloc0(sizeof(*info->vfio)); - info->vfio->transferred = vfio_mig_bytes_transferred(); + info->vfio->transferred = vfio_migration_bytes_transferred(); } } void migration_reset_vfio_bytes_transferred(void) { - vfio_reset_bytes_transferred(); + vfio_migration_reset_bytes_transferred(); } #else void migration_populate_vfio_info(MigrationInfo *info) diff --git a/migration/tls.c b/migration/tls.c index fa03d9136ca..5cbf9523836 100644 --- a/migration/tls.c +++ b/migration/tls.c @@ -156,6 +156,11 @@ void migration_tls_channel_connect(MigrationState *s, NULL); } +void migration_tls_channel_end(QIOChannel *ioc, Error **errp) +{ + qio_channel_tls_bye(QIO_CHANNEL_TLS(ioc), errp); +} + bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc) { if (!migrate_tls()) { diff --git a/migration/tls.h b/migration/tls.h index 5797d153cb0..58b25e12281 100644 --- a/migration/tls.h +++ b/migration/tls.h @@ -36,7 +36,7 @@ void migration_tls_channel_connect(MigrationState *s, QIOChannel *ioc, const char *hostname, Error **errp); - +void migration_tls_channel_end(QIOChannel *ioc, Error **errp); /* Whether the QIO channel requires further TLS handshake? */ bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc); diff --git a/migration/trace-events b/migration/trace-events index 4666f19325e..c506e11a2e1 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -17,7 +17,6 @@ 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) "" @@ -40,14 +39,17 @@ savevm_send_postcopy_run(void) "" savevm_send_postcopy_resume(void) "" savevm_send_colo_enable(void) "" savevm_send_recv_bitmap(char *name) "%s" +savevm_send_switchover_start(void) "" savevm_state_setup(void) "" savevm_state_resume_prepare(void) "" savevm_state_header(void) "" savevm_state_iterate(void) "" 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" @@ -56,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" @@ -66,6 +68,7 @@ vmstate_save_state_loop(const char *name, const char *field, int n_elems) "%s/%s vmstate_save_state_top(const char *idstr) "%s" vmstate_subsection_save_loop(const char *name, const char *sub) "%s/%s" vmstate_subsection_save_top(const char *idstr) "%s" +vmstate_field_exists(const char *vmsd, const char *name, int field_version, int version, int result) "%s:%s field_version %d version %d result %d" # vmstate-types.c get_qtailq(const char *name, int version_id) "%s v%d" @@ -85,6 +88,8 @@ put_qlist_end(const char *field_name, const char *vmsd_name) "%s(%s)" # qemu-file.c qemu_file_fclose(void) "" +qemu_file_put_fd(const char *name, int fd, int ret) "ioc %s, fd %d -> status %d" +qemu_file_get_fd(const char *name, int fd) "ioc %s -> fd %d" # ram.c get_queued_page(const char *block_name, uint64_t tmp_offset, unsigned long page_abs) "%s/0x%" PRIx64 " page_abs=0x%lx" @@ -112,6 +117,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" @@ -124,34 +130,34 @@ 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_fd_cancel(void) "" +migration_cleanup(void) "" +migrate_error(const char *error_desc) "error=%s" +migration_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_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 ")" @@ -164,7 +170,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) "" @@ -185,13 +191,14 @@ 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 transferred, 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_precopy_complete(void) "" # migration-stats -migration_transferred_bytes(uint64_t qemu_file, uint64_t multifd) "qemu_file %" PRIu64 " multifd %" PRIu64 +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" @@ -207,12 +214,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)" @@ -221,27 +230,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" @@ -260,6 +255,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) "" @@ -311,6 +320,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" @@ -332,6 +345,15 @@ colo_receive_message(const char *msg) "Receive '%s' message" # colo-failover.c colo_failover_set_state(const char *new_state) "new state %s" +# cpr.c +cpr_save_fd(const char *name, int id, int fd) "%s, id %d, fd %d" +cpr_delete_fd(const char *name, int id) "%s, id %d" +cpr_find_fd(const char *name, int id, int fd) "%s, id %d returns %d" +cpr_state_save(const char *mode) "%s mode" +cpr_state_load(const char *mode) "%s mode" +cpr_transfer_input(const char *path) "%s" +cpr_transfer_output(const char *path) "%s" + # block-dirty-bitmap.c send_bitmap_header_enter(void) "" send_bitmap_bits(uint32_t flags, uint64_t start_sector, uint32_t nr_sectors, uint64_t data_size) "flags: 0x%x, start_sector: %" PRIu64 ", nr_sectors: %" PRIu32 ", data_size: %" PRIu64 @@ -369,3 +391,10 @@ 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) "" + +# block-active.c +migration_block_activation(const char *name) "%s" diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c index e83bfccb9ec..741a588b7e1 100644 --- a/migration/vmstate-types.c +++ b/migration/vmstate-types.c @@ -15,6 +15,7 @@ #include "qemu-file.h" #include "migration.h" #include "migration/vmstate.h" +#include "migration/client-options.h" #include "qemu/error-report.h" #include "qemu/queue.h" #include "trace.h" @@ -314,6 +315,29 @@ const VMStateInfo vmstate_info_uint64 = { .put = put_uint64, }; +/* File descriptor communicated via SCM_RIGHTS */ + +static int get_fd(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + int32_t *v = pv; + *v = qemu_file_get_fd(f); + return 0; +} + +static int put_fd(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + int32_t *v = pv; + return qemu_file_put_fd(f, *v); +} + +const VMStateInfo vmstate_info_fd = { + .name = "fd", + .get = get_fd, + .put = put_fd, +}; + static int get_nullptr(QEMUFile *f, void *pv, size_t size, const VMStateField *field) @@ -338,7 +362,7 @@ static int put_nullptr(QEMUFile *f, void *pv, size_t size, } const VMStateInfo vmstate_info_nullptr = { - .name = "uint64", + .name = "nullptr", .get = get_nullptr, .put = put_nullptr, }; diff --git a/migration/vmstate.c b/migration/vmstate.c index 31842c3afb4..5feaa3244d2 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -14,17 +14,73 @@ #include "migration.h" #include "migration/vmstate.h" #include "savevm.h" -#include "qapi/qmp/json-writer.h" +#include "qapi/error.h" +#include "qobject/json-writer.h" #include "qemu-file.h" #include "qemu/bitops.h" #include "qemu/error-report.h" #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; +} + +/* + * Create a fake nullptr field when there's a NULL pointer detected in the + * array of a VMS_ARRAY_OF_POINTER VMSD field. It's needed because we + * can't dereference the NULL pointer. + */ +static const VMStateField * +vmsd_create_fake_nullptr_field(const VMStateField *field) +{ + VMStateField *fake = g_new0(VMStateField, 1); + + /* It can only happen on an array of pointers! */ + assert(field->flags & VMS_ARRAY_OF_POINTER); + + /* Some of fake's properties should match the original's */ + fake->name = field->name; + fake->version_id = field->version_id; + + /* Do not need "field_exists" check as it always exists (which is null) */ + fake->field_exists = NULL; + + /* See vmstate_info_nullptr - use 1 byte to represent nullptr */ + fake->size = 1; + fake->info = &vmstate_info_nullptr; + fake->flags = VMS_SINGLE; + + /* All the rest fields shouldn't matter.. */ + + return (const VMStateField *)fake; +} + static int vmstate_n_elems(void *opaque, const VMStateField *field) { int n_elems = 1; @@ -97,17 +153,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); @@ -119,23 +173,39 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, } for (i = 0; i < n_elems; i++) { void *curr_elem = first_elem + size * i; + const VMStateField *inner_field; if (field->flags & VMS_ARRAY_OF_POINTER) { curr_elem = *(void **)curr_elem; } + if (!curr_elem && size) { - /* if null pointer check placeholder and do not follow */ - assert(field->flags & VMS_ARRAY_OF_POINTER); - ret = vmstate_info_nullptr.get(f, curr_elem, size, NULL); - } else if (field->flags & VMS_STRUCT) { - ret = vmstate_load_state(f, field->vmsd, curr_elem, - field->vmsd->version_id); - } else if (field->flags & VMS_VSTRUCT) { - ret = vmstate_load_state(f, field->vmsd, curr_elem, - field->struct_version_id); + /* + * If null pointer found (which should only happen in + * an array of pointers), use null placeholder and do + * not follow. + */ + inner_field = vmsd_create_fake_nullptr_field(field); + } else { + inner_field = field; + } + + if (inner_field->flags & VMS_STRUCT) { + ret = vmstate_load_state(f, inner_field->vmsd, curr_elem, + inner_field->vmsd->version_id); + } else if (inner_field->flags & VMS_VSTRUCT) { + ret = vmstate_load_state(f, inner_field->vmsd, curr_elem, + inner_field->struct_version_id); } else { - ret = field->info->get(f, curr_elem, size, field); + ret = inner_field->info->get(f, curr_elem, size, + inner_field); } + + /* If we used a fake temp field.. free it now */ + if (inner_field != field) { + g_clear_pointer((gpointer *)&inner_field, g_free); + } + if (ret >= 0) { ret = qemu_file_get_error(f); } @@ -157,6 +227,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, 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) { @@ -286,7 +357,7 @@ static void vmsd_desc_field_start(const VMStateDescription *vmsd, static void vmsd_desc_field_end(const VMStateDescription *vmsd, JSONWriter *vmdesc, - const VMStateField *field, size_t size, int i) + const VMStateField *field, size_t size) { if (!vmdesc) { return; @@ -302,7 +373,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 */ @@ -315,11 +386,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; @@ -330,7 +407,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; } } @@ -342,58 +419,106 @@ 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); uint64_t old_offset, written_bytes; JSONWriter *vmdesc_loop = vmdesc; + bool is_prev_null = false; trace_vmstate_save_state_loop(vmsd->name, field->name, n_elems); if (field->flags & VMS_POINTER) { first_elem = *(void **)first_elem; assert(first_elem || !n_elems || !size); } + for (i = 0; i < n_elems; i++) { void *curr_elem = first_elem + size * i; + const VMStateField *inner_field; + bool is_null; + int max_elems = n_elems - i; - vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems); - old_offset = qemu_file_transferred_noflush(f); + old_offset = qemu_file_transferred(f); if (field->flags & VMS_ARRAY_OF_POINTER) { assert(curr_elem); curr_elem = *(void **)curr_elem; } + if (!curr_elem && size) { - /* if null pointer write placeholder and do not follow */ - assert(field->flags & VMS_ARRAY_OF_POINTER); - ret = vmstate_info_nullptr.put(f, curr_elem, size, NULL, - NULL); - } else if (field->flags & VMS_STRUCT) { - ret = vmstate_save_state(f, field->vmsd, curr_elem, - vmdesc_loop); - } else if (field->flags & VMS_VSTRUCT) { - ret = vmstate_save_state_v(f, field->vmsd, curr_elem, - vmdesc_loop, - field->struct_version_id); + /* + * If null pointer found (which should only happen in + * an array of pointers), use null placeholder and do + * not follow. + */ + inner_field = vmsd_create_fake_nullptr_field(field); + is_null = true; + } else { + inner_field = field; + is_null = false; + } + + /* + * This logic only matters when dumping VM Desc. + * + * Due to the fake nullptr handling above, if there's mixed + * null/non-null data, it doesn't make sense to emit a + * compressed array representation spanning the entire array + * because the field types will be different (e.g. struct + * vs. nullptr). Search ahead for the next null/non-null element + * and start a new compressed array if found. + */ + if (vmdesc && (field->flags & VMS_ARRAY_OF_POINTER) && + is_null != is_prev_null) { + + is_prev_null = is_null; + vmdesc_loop = vmdesc; + + for (int j = i + 1; j < n_elems; j++) { + void *elem = *(void **)(first_elem + size * j); + bool elem_is_null = !elem && size; + + if (is_null != elem_is_null) { + max_elems = j - i; + break; + } + } + } + + vmsd_desc_field_start(vmsd, vmdesc_loop, inner_field, + i, max_elems); + + if (inner_field->flags & VMS_STRUCT) { + ret = vmstate_save_state(f, inner_field->vmsd, + curr_elem, vmdesc_loop); + } else if (inner_field->flags & VMS_VSTRUCT) { + ret = vmstate_save_state_v(f, inner_field->vmsd, + curr_elem, vmdesc_loop, + inner_field->struct_version_id, + errp); } else { - ret = field->info->put(f, curr_elem, size, field, - vmdesc_loop); + ret = inner_field->info->put(f, curr_elem, size, + inner_field, vmdesc_loop); + } + + written_bytes = qemu_file_transferred(f) - old_offset; + vmsd_desc_field_end(vmsd, vmdesc_loop, inner_field, + written_bytes); + + /* If we used a fake temp field.. free it now */ + if (is_null) { + g_clear_pointer((gpointer *)&inner_field, g_free); } + 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_transferred_noflush(f) - old_offset; - vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes, i); - /* Compressed arrays only care about the first element */ if (vmdesc_loop && vmsd_can_compress(field)) { vmdesc_loop = NULL; @@ -414,25 +539,28 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, 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; } @@ -450,7 +578,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; } @@ -489,15 +617,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; @@ -517,7 +646,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/yank_functions.c b/migration/yank_functions.c index d5a710a3f2f..979e60c7620 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) diff --git a/monitor/fds.c b/monitor/fds.c index d86c2c674c4..cc35d2ec334 100644 --- a/monitor/fds.c +++ b/monitor/fds.c @@ -29,7 +29,7 @@ #include "qapi/qmp/qerror.h" #include "qemu/ctype.h" #include "qemu/cutils.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" /* file descriptors passed via SCM_RIGHTS */ typedef struct mon_fd_t mon_fd_t; @@ -43,7 +43,6 @@ struct mon_fd_t { typedef struct MonFdsetFd MonFdsetFd; struct MonFdsetFd { int fd; - bool removed; char *opaque; QLIST_ENTRY(MonFdsetFd) next; }; @@ -167,28 +166,32 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) return -1; } -static void monitor_fdset_cleanup(MonFdset *mon_fdset) +static void monitor_fdset_free(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); - } - } + 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)) { - QLIST_REMOVE(mon_fdset, next); - g_free(mon_fdset); + 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; @@ -196,7 +199,7 @@ void monitor_fdsets_cleanup(void) QEMU_LOCK_GUARD(&mon_fdsets_lock); QLIST_FOREACH_SAFE(mon_fdset, &mon_fdsets, next, mon_fdset_next) { - monitor_fdset_cleanup(mon_fdset); + monitor_fdset_free_if_empty(mon_fdset); } } @@ -263,7 +266,7 @@ void qmp_get_win32_socket(const char *infos, const char *fdname, Error **errp) void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp) { MonFdset *mon_fdset; - MonFdsetFd *mon_fdset_fd; + MonFdsetFd *mon_fdset_fd, *mon_fdset_fd_next; char fd_str[60]; QEMU_LOCK_GUARD(&mon_fdsets_lock); @@ -271,21 +274,22 @@ void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp) if (mon_fdset->id != fdset_id) { continue; } - QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { + QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, + mon_fdset_fd_next) { if (has_fd) { if (mon_fdset_fd->fd != fd) { continue; } - mon_fdset_fd->removed = true; + monitor_fdset_fd_free(mon_fdset_fd); break; } else { - mon_fdset_fd->removed = true; + monitor_fdset_fd_free(mon_fdset_fd); } } if (has_fd && !mon_fdset_fd) { goto error; } - monitor_fdset_cleanup(mon_fdset); + monitor_fdset_free_if_empty(mon_fdset); return; } @@ -395,7 +399,6 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, mon_fdset_fd = g_malloc0(sizeof(*mon_fdset_fd)); mon_fdset_fd->fd = fd; - mon_fdset_fd->removed = false; mon_fdset_fd->opaque = g_strdup(opaque); QLIST_INSERT_HEAD(&mon_fdset->fds, mon_fdset_fd, next); @@ -406,9 +409,10 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, return fdinfo; } -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) { #ifdef _WIN32 + error_setg(errp, "Platform does not support fd passing (fdset)"); return -ENOENT; #else MonFdset *mon_fdset; @@ -420,6 +424,11 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) 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; @@ -428,10 +437,12 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) 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 & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) { + if ((flags & mask) == (mon_fd_flags & mask)) { fd = mon_fdset_fd->fd; break; } @@ -439,11 +450,15 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) 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; } @@ -453,12 +468,13 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) return dup_fd; } + error_setg(errp, "Failed to find fdset /dev/fdset/%" PRId64, fdset_id); errno = ENOENT; return -1; #endif } -static int64_t monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove) +void monitor_fdset_dup_fd_remove(int dup_fd) { MonFdset *mon_fdset; MonFdsetFd *mon_fdset_fd_dup; @@ -467,31 +483,13 @@ static int64_t monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove) 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; - } + QLIST_REMOVE(mon_fdset_fd_dup, next); + g_free(mon_fdset_fd_dup); + monitor_fdset_free_if_empty(mon_fdset); + return; } } } - - 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) diff --git a/monitor/hmp-cmds-target.c b/monitor/hmp-cmds-target.c index 0d3e84d960a..8eaf70d9c9a 100644 --- a/monitor/hmp-cmds-target.c +++ b/monitor/hmp-cmds-target.c @@ -24,12 +24,14 @@ #include "qemu/osdep.h" #include "disas/disas.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" +#include "system/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" +#include "qobject/qdict.h" +#include "system/hw_accel.h" +#include "exec/target_page.h" /* Set the current CPU defined by the user. Callers must hold BQL. */ int monitor_set_cpu(Monitor *mon, int cpu_index) @@ -81,7 +83,7 @@ CPUArchState *mon_get_cpu_env(Monitor *mon) { CPUState *cs = mon_get_cpu(mon); - return cs ? cs->env_ptr : NULL; + return cs ? cpu_env(cs) : NULL; } int monitor_get_cpu_index(Monitor *mon) @@ -260,7 +262,7 @@ void *gpa2hva(MemoryRegion **p_mr, hwaddr addr, uint64_t size, Error **errp) } 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); + error_setg(errp, "Memory at address 0x%" HWADDR_PRIx " is not RAM", addr); memory_region_unref(mrs.mr); return NULL; } @@ -300,7 +302,6 @@ void hmp_gpa2hva(Monitor *mon, const QDict *qdict) 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; @@ -309,7 +310,7 @@ void hmp_gva2gpa(Monitor *mon, const QDict *qdict) return; } - gpa = cpu_get_phys_page_attrs_debug(cs, addr & TARGET_PAGE_MASK, &attrs); + gpa = cpu_get_phys_page_debug(cs, addr & TARGET_PAGE_MASK); if (gpa == -1) { monitor_printf(mon, "Unmapped\n"); } else { diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 6c559b48c8e..74a0f56566e 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -14,21 +14,21 @@ */ #include "qemu/osdep.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" +#include "system/ioport.h" #include "exec/gdbstub.h" -#include "exec/ioport.h" +#include "gdbstub/enums.h" #include "monitor/hmp.h" #include "qemu/help_option.h" #include "monitor/monitor-internal.h" #include "qapi/error.h" #include "qapi/qapi-commands-control.h" +#include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.h" +#include "qobject/qdict.h" #include "qemu/cutils.h" -#include "hw/intc/intc.h" #include "qemu/log.h" -#include "sysemu/sysemu.h" +#include "system/system.h" bool hmp_handle_error(Monitor *mon, Error *err) { @@ -82,32 +82,6 @@ void hmp_info_version(Monitor *mon, const QDict *qdict) qapi_free_VersionInfo(info); } -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_quit(Monitor *mon, const QDict *qdict) { monitor_suspend(mon); @@ -138,7 +112,8 @@ 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); } } @@ -310,7 +285,7 @@ void hmp_gdbserver(Monitor *mon, const QDict *qdict) device = "tcp::" DEFAULT_GDBSTUB_PORT; } - if (gdbserver_start(device) < 0) { + if (!gdbserver_start(device, &error_warn)) { monitor_printf(mon, "Could not open gdbserver on device '%s'\n", device); } else if (strcmp(device, "none") == 0) { @@ -443,3 +418,19 @@ void hmp_info_mtree(Monitor *mon, const QDict *qdict) mtree_info(flatview, dispatch_tree, owner, disabled); } + +#if defined(CONFIG_FDT) +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; + } + + monitor_printf(mon, "DTB dumped to '%s'\n", filename); +} +#endif diff --git a/monitor/hmp-target.c b/monitor/hmp-target.c index 1eb72ac1bf5..37dfd7fd4c6 100644 --- a/monitor/hmp-target.c +++ b/monitor/hmp-target.c @@ -26,7 +26,7 @@ #include "monitor-internal.h" #include "monitor/qdev.h" #include "net/slirp.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "monitor/hmp-target.h" #include "monitor/hmp.h" #include "block/block-hmp-cmds.h" diff --git a/monitor/hmp.c b/monitor/hmp.c index 69c1b7e98ab..34e2b8f748b 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -27,15 +27,15 @@ #include "hw/qdev-core.h" #include "monitor-internal.h" #include "monitor/hmp.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/config-file.h" #include "qemu/ctype.h" #include "qemu/cutils.h" #include "qemu/log.h" #include "qemu/option.h" #include "qemu/units.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "trace.h" static void monitor_command_cb(void *opaque, const char *cmdline, @@ -1437,11 +1437,9 @@ static void monitor_event(void *opaque, QEMUChrEvent event) monitor_resume(mon); } qemu_mutex_unlock(&mon->mon_lock); - mon_refcount++; break; case CHR_EVENT_CLOSED: - mon_refcount--; monitor_fdsets_cleanup(); break; diff --git a/monitor/meson.build b/monitor/meson.build index 5269492cf05..a71523a1ce8 100644 --- a/monitor/meson.build +++ b/monitor/meson.build @@ -4,6 +4,7 @@ system_ss.add(files( 'fds.c', 'hmp-cmds.c', 'hmp.c', + 'qemu-config-qmp.c', )) system_ss.add([spice_headers, files('qmp-cmds.c')]) diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index 252de856812..5676eb334e8 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -28,10 +28,10 @@ #include "chardev/char-fe.h" #include "monitor/monitor.h" #include "qapi/qapi-types-control.h" -#include "qapi/qmp/dispatch.h" -#include "qapi/qmp/json-parser.h" +#include "qapi/qmp-registry.h" +#include "qobject/json-parser.h" #include "qemu/readline.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" /* * Supported types: @@ -168,7 +168,6 @@ extern bool qmp_dispatcher_co_shutdown; extern QmpCommandList qmp_commands, qmp_cap_negotiation_commands; extern QemuMutex monitor_lock; extern MonitorList mon_list; -extern int mon_refcount; extern HMPCommand hmp_cmds[]; diff --git a/monitor/monitor.c b/monitor/monitor.c index 941f87815aa..c5a5d308774 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -28,10 +28,10 @@ #include "qapi/opts-visitor.h" #include "qapi/qapi-emit-events.h" #include "qapi/qapi-visit-control.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/error-report.h" #include "qemu/option.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "trace.h" /* @@ -71,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) @@ -309,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 }, }; /* @@ -493,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")); } @@ -519,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")); } diff --git a/monitor/qemu-config-qmp.c b/monitor/qemu-config-qmp.c new file mode 100644 index 00000000000..9a3b183602d --- /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 "qobject/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 f21506efa58..150ca9f5cb6 100644 --- a/monitor/qmp-cmds-control.c +++ b/monitor/qmp-cmds-control.c @@ -1,5 +1,5 @@ /* - * QMP commands related to the monitor (common to sysemu and tools) + * QMP commands related to the monitor (common to system and tools) * * Copyright (c) 2003-2004 Fabrice Bellard * diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index b0f948d3376..1ca44fbd72f 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -18,11 +18,11 @@ #include "monitor-internal.h" #include "monitor/qdev.h" #include "monitor/qmp-helpers.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" -#include "sysemu/runstate-action.h" -#include "sysemu/block-backend.h" +#include "system/system.h" +#include "system/kvm.h" +#include "system/runstate.h" +#include "system/runstate-action.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qapi/qapi-init-commands.h" #include "qapi/qapi-commands-control.h" @@ -31,7 +31,7 @@ #include "qapi/type-helpers.h" #include "hw/mem/memory-device.h" #include "hw/intc/intc.h" -#include "hw/rdma/rdma.h" +#include "migration/misc.h" NameInfo *qmp_query_name(Error **errp) { @@ -97,21 +97,18 @@ void qmp_cont(Error **errp) } } - /* Continuing after completed migration. Images have been inactivated to - * allow the destination to take control. Need to get control back now. - * - * If there are no inactive block nodes (e.g. because the VM was just - * paused rather than completing a migration), bdrv_inactivate_all() simply - * doesn't do anything. */ - bdrv_activate_all(&local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - if (runstate_check(RUN_STATE_INMIGRATE)) { autostart = 1; } else { + /* + * Continuing after completed migration. Images have been + * inactivated to allow the destination to take control. Need to + * get control back now. + */ + if (!migration_block_activate(&local_err)) { + error_propagate(errp, local_err); + return; + } vm_start(); } } diff --git a/monitor/qmp.c b/monitor/qmp.c index 6eee450fe40..cb99a12d941 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -28,9 +28,9 @@ #include "monitor-internal.h" #include "qapi/error.h" #include "qapi/qapi-commands-control.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qlist.h" #include "trace.h" /* @@ -321,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)) { @@ -355,15 +347,6 @@ void coroutine_fn monitor_qmp_dispatcher_co(void *data) } qmp_request_free(req_obj); - - /* - * 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(); } qatomic_set(&qmp_dispatcher_co, NULL); } @@ -373,7 +356,8 @@ 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)) { + if (!qatomic_xchg(&qmp_dispatcher_co_busy, true) && + qatomic_read(&qmp_dispatcher_co)) { aio_co_wake(qmp_dispatcher_co); } } @@ -483,7 +467,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: /* @@ -496,7 +479,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 53a6549914c..79ea97e4cc1 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. * @@ -31,6 +31,8 @@ #include "qapi/clone-visitor.h" #include "qemu/coroutine.h" +#include "nbd/nbd-internal.h" + struct NBDClientConnection { /* Initialization constants, never change */ SocketAddress *saddr; /* address to connect to */ @@ -93,7 +95,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 ?: "") @@ -140,6 +142,7 @@ static int nbd_connect(QIOChannelSocket *sioc, SocketAddress *addr, return ret; } + nbd_set_socket_send_buffer(sioc); qio_channel_set_delay(QIO_CHANNEL(sioc), false); if (!info) { @@ -410,7 +413,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 bd7e2001366..c89c7504673 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -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)); @@ -879,7 +899,7 @@ static int nbd_list_meta_contexts(QIOChannel *ioc, */ 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(); @@ -953,15 +973,23 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, 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 result ? NBD_MODE_STRUCTURED : NBD_MODE_SIMPLE; + return NBD_MODE_SIMPLE; } else { return NBD_MODE_EXPORT_NAME; } @@ -1022,20 +1050,20 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, trace_nbd_receive_negotiate_name(info->name); result = nbd_start_negotiate(ioc, tlscreds, hostname, outioc, - info->structured_reply, &zeroes, errp); + 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 ((NBDMode)result) { + switch (info->mode) { + case NBD_MODE_EXTENDED: case NBD_MODE_STRUCTURED: - info->structured_reply = true; if (base_allocation) { result = nbd_negotiate_simple_meta_context(ioc, info, errp); if (result < 0) { @@ -1144,8 +1172,8 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, QIOChannel *sioc = NULL; *info = NULL; - result = nbd_start_negotiate(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; } @@ -1156,6 +1184,7 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, 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. */ @@ -1176,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 == NBD_MODE_STRUCTURED; + array[count - 1].mode = result; } for (i = 0; i < count; i++) { @@ -1192,7 +1221,7 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, break; } - if (result == NBD_MODE_STRUCTURED && + if (result >= NBD_MODE_STRUCTURED && nbd_list_meta_contexts(ioc, &array[i], errp) < 0) { goto out; } @@ -1209,6 +1238,7 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, /* 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) { @@ -1218,7 +1248,7 @@ 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); } @@ -1346,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->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->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 @@ -1386,30 +1425,36 @@ static int nbd_receive_simple_reply(QIOChannel *ioc, NBDSimpleReply *reply, 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->cookie = be64_to_cpu(chunk->cookie); - 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 @@ -1417,11 +1462,20 @@ static int nbd_receive_structured_reply_chunk(QIOChannel *ioc, * this. Even if we stopped using REQ_ONE, sane servers will cap * the number of extents they return for block status. */ - if (chunk->length > NBD_MAX_BUFFER_SIZE + sizeof(NBDStructuredReadData)) { + 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->type, nbd_rep_lookup(chunk->type)); + chunk->structured.type, + nbd_rep_lookup(chunk->structured.type)); return -EINVAL; } + chunk->structured.length = payload_len; return 0; } @@ -1468,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) { @@ -1489,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->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.cookie, - 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 989fbe54a19..2a133a66c39 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -18,6 +18,9 @@ #include "qemu/osdep.h" #include "trace.h" +#include "io/channel-socket.h" +#include "qapi/error.h" +#include "qemu/units.h" #include "nbd-internal.h" /* Discard length bytes from channel. Return -errno on failure and 0 on @@ -47,17 +50,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 +71,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 +106,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 +168,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: @@ -261,7 +261,32 @@ const char *nbd_mode_lookup(NBDMode mode) return "simple headers"; case NBD_MODE_STRUCTURED: return "structured replies"; + case NBD_MODE_EXTENDED: + return "extended headers"; default: return ""; } } + +/* + * Testing shows that 2m send buffer is optimal. Changing the receive buffer + * size has no effect on performance. + * On Linux we need to increase net.core.wmem_max to make this effective. + */ +#if defined(__APPLE__) || defined(__linux__) +#define UNIX_STREAM_SOCKET_SEND_BUFFER_SIZE (2 * MiB) +#endif + +void nbd_set_socket_send_buffer(QIOChannelSocket *sioc) +{ +#ifdef UNIX_STREAM_SOCKET_SEND_BUFFER_SIZE + if (sioc->localAddr.ss_family == AF_UNIX) { + size_t size = UNIX_STREAM_SOCKET_SEND_BUFFER_SIZE; + Error *errp = NULL; + + if (qio_channel_socket_set_send_buffer(sioc, size, &errp) < 0) { + warn_report_err(errp); + } + } +#endif /* UNIX_STREAM_SOCKET_SEND_BUFFER_SIZE */ +} diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h index df42fef7066..6bafeef5ddc 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. @@ -10,7 +10,7 @@ #ifndef NBD_INTERNAL_H #define NBD_INTERNAL_H #include "block/nbd.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "io/channel-tls.h" #include "qemu/iov.h" @@ -34,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 */ @@ -44,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 @@ -70,16 +72,11 @@ 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); +/* nbd_set_socket_send_buffer + * Set the socket send buffer size for optimal performance. + */ +void nbd_set_socket_send_buffer(QIOChannelSocket *sioc); + #endif diff --git a/nbd/server.c b/nbd/server.c index b5f93a20c9c..d242be98115 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -105,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 */ @@ -117,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 @@ -191,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; @@ -207,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) { @@ -255,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, ...) { @@ -271,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) { @@ -284,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, ...) { @@ -298,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; @@ -315,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, @@ -339,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, @@ -362,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; @@ -398,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; @@ -440,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); @@ -455,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; @@ -482,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; @@ -500,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); @@ -517,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; } @@ -525,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; @@ -554,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; @@ -572,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; @@ -637,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) { @@ -687,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); @@ -725,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); @@ -760,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); @@ -786,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[] = { @@ -814,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"); @@ -832,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); @@ -848,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; @@ -868,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; @@ -933,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; @@ -973,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", @@ -995,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); @@ -1095,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; @@ -1119,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; @@ -1162,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; } @@ -1261,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: @@ -1309,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) { @@ -1341,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) { @@ -1379,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) { @@ -1408,11 +1497,13 @@ nbd_read_eof(NBDClient *client, void *buffer, size_t size, 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; } @@ -1420,13 +1511,21 @@ static int coroutine_fn nbd_receive_request(NBDClient *client, NBDRequest *reque return -EIO; } - /* 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 + /* + * 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); @@ -1434,13 +1533,20 @@ static int coroutine_fn nbd_receive_request(NBDClient *client, NBDRequest *reque request->type = lduw_be_p(buf + 6); 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; @@ -1448,14 +1554,17 @@ static int coroutine_fn nbd_receive_request(NBDClient *client, NBDRequest *reque #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. */ @@ -1471,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()); + + WITH_QEMU_LOCK_GUARD(&client->lock) { + if (client->closing) { + return; + } - client->closing = true; + client->closing = true; + } /* Force requests to finish. They will drop their own references, * then we'll close the socket and free the NBDClient. @@ -1496,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; @@ -1504,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; @@ -1525,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) @@ -1534,14 +1672,18 @@ 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) { - 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); + } } } @@ -1549,6 +1691,8 @@ static void blk_aio_detach(void *opaque) { NBDExport *exp = opaque; + assert(qemu_in_main_thread()); + trace_nbd_blk_aio_detach(exp->name, exp->common.ctx); exp->common.ctx = NULL; @@ -1559,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; + } } } @@ -1569,28 +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; + assert(qemu_in_main_thread()); + 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) { - qio_channel_wake_read(client->ioc); - } + 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; + return true; + } } } @@ -1601,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); } @@ -1636,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()) { @@ -1690,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++; } @@ -1772,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); @@ -1806,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 @@ -1850,6 +2026,7 @@ static void nbd_export_delete(BlockExport *blk_exp) const BlockExportDriver blk_exp_nbd = { .type = BLOCK_EXPORT_TYPE_NBD, .instance_size = sizeof(NBDExport), + .supports_inactive = true, .create = nbd_export_create, .delete = nbd_export_delete, .request_shutdown = nbd_export_request_shutdown, @@ -1884,7 +2061,7 @@ static int coroutine_fn nbd_co_send_simple_reply(NBDClient *client, NBDRequest *request, uint32_t error, void *data, - size_t len, + uint64_t len, Error **errp) { NBDSimpleReply reply; @@ -1895,7 +2072,10 @@ static int coroutine_fn nbd_co_send_simple_reply(NBDClient *client, }; assert(!len || !nbd_err); - assert(!client->structured_reply || request->type != NBD_CMD_READ); + 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); @@ -1915,8 +2095,6 @@ static inline void set_be_chunk(NBDClient *client, struct iovec *iov, size_t niov, uint16_t flags, uint16_t type, NBDRequest *request) { - /* TODO - handle structured vs. extended replies */ - NBDStructuredReplyChunk *chunk = iov->iov_base; size_t i, length = 0; for (i = 1; i < niov; i++) { @@ -1924,12 +2102,26 @@ static inline void set_be_chunk(NBDClient *client, struct iovec *iov, } assert(length <= NBD_MAX_BUFFER_SIZE + sizeof(NBDStructuredReadData)); - 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); + 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, @@ -1951,7 +2143,7 @@ static int coroutine_fn nbd_co_send_chunk_read(NBDClient *client, NBDRequest *request, uint64_t offset, void *data, - size_t size, + uint64_t size, bool final, Error **errp) { @@ -1963,7 +2155,7 @@ static int coroutine_fn nbd_co_send_chunk_read(NBDClient *client, {.iov_base = data, .iov_len = size} }; - assert(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); @@ -1971,7 +2163,7 @@ static int coroutine_fn nbd_co_send_chunk_read(NBDClient *client, return nbd_co_send_iov(client, iov, 3, errp); } -/*ebb*/ + static int coroutine_fn nbd_co_send_chunk_error(NBDClient *client, NBDRequest *request, uint32_t error, @@ -2006,13 +2198,14 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client, 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 = blk_co_block_status_above(exp->common.blk, NULL, @@ -2067,20 +2260,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; @@ -2099,13 +2296,34 @@ 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].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++) { - ea->extents[i].flags = cpu_to_be32(ea->extents[i].flags); - ea->extents[i].length = cpu_to_be32(ea->extents[i].length); + 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; } /* @@ -2118,19 +2336,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; @@ -2143,7 +2369,7 @@ 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; @@ -2212,20 +2438,39 @@ nbd_co_send_extents(NBDClient *client, NBDRequest *request, NBDExtentArray *ea, bool last, uint32_t context_id, Error **errp) { NBDReply hdr; - NBDStructuredMeta chunk; - struct iovec iov[] = { - {.iov_base = &hdr}, - {.iov_base = &chunk, .iov_len = sizeof(chunk)}, - {.iov_base = ea->extents, .iov_len = ea->count * sizeof(ea->extents[0])} - }; + NBDStructuredMeta meta; + NBDExtendedMeta meta_ext; + g_autofree NBDExtent32 *extents = NULL; + uint16_t type; + struct iovec iov[] = { {.iov_base = &hdr}, {0}, {0} }; + + if (client->mode >= NBD_MODE_EXTENDED) { + type = NBD_REPLY_TYPE_BLOCK_STATUS_EXT; + + 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); + + 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; - nbd_extent_array_convert_to_be(ea); + 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, - NBD_REPLY_TYPE_BLOCK_STATUS, request); - stl_be_p(&chunk.context_id, context_id); + set_be_chunk(client, iov, 3, last ? NBD_REPLY_FLAG_DONE : 0, type, + request); return nbd_co_send_iov(client, iov, 3, errp); } @@ -2234,13 +2479,14 @@ nbd_co_send_extents(NBDClient *client, NBDRequest *request, NBDExtentArray *ea, static int coroutine_fn nbd_co_send_block_status(NBDClient *client, NBDRequest *request, BlockBackend *blk, uint64_t offset, - uint32_t length, bool dont_fragment, + 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(blk, offset, length, ea); @@ -2263,11 +2509,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) { @@ -2291,18 +2538,103 @@ static int coroutine_fn nbd_co_send_bitmap(NBDClient *client, NBDRequest *request, BdrvDirtyBitmap *bitmap, uint64_t offset, - uint32_t length, bool dont_fragment, + 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, 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 * Collect a client request. Return 0 if request looks valid, -EIO to drop * connection right away, -EAGAIN to indicate we were interrupted and the @@ -2310,15 +2642,21 @@ static int coroutine_fn nbd_co_send_bitmap(NBDClient *client, * to the client (although the caller may still need to disconnect after * reporting the error). */ -static int coroutine_fn nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, +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; @@ -2326,60 +2664,136 @@ static int coroutine_fn nbd_co_receive_request(NBDRequestData *req, NBDRequest * 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->cookie, - request->len); + 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 || @@ -2396,14 +2810,6 @@ static int coroutine_fn nbd_co_receive_request(NBDRequestData *req, NBDRequest * 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); @@ -2423,8 +2829,10 @@ static coroutine_fn int nbd_send_generic_reply(NBDClient *client, const char *error_msg, Error **errp) { - if (client->structured_reply && ret < 0) { + 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, request, ret < 0 ? -ret : 0, NULL, 0, errp); @@ -2441,6 +2849,7 @@ 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) { @@ -2451,8 +2860,8 @@ static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, } } - 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, request->from, data, request->len, errp); @@ -2464,7 +2873,7 @@ static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, "reading from file failed", errp); } - if (client->structured_reply) { + if (client->mode >= NBD_MODE_STRUCTURED) { if (request->len) { return nbd_co_send_chunk_read(client, request, request->from, data, request->len, true, errp); @@ -2491,6 +2900,7 @@ 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); @@ -2511,6 +2921,22 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, NBDExport *exp = client->exp; char *msg; size_t i; + bool inactive; + + WITH_GRAPH_RDLOCK_GUARD() { + inactive = bdrv_is_inactive(blk_bs(exp->common.blk)); + if (inactive) { + switch (request->type) { + case NBD_CMD_READ: + /* These commands are allowed on inactive nodes */ + break; + default: + /* Return an error for the rest */ + return nbd_send_generic_reply(client, request, -EPERM, + "export is inactive", errp); + } + } + } switch (request->type) { case NBD_CMD_CACHE: @@ -2524,6 +2950,7 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, if (request->flags & NBD_CMD_FLAG_FUA) { flags |= BDRV_REQ_FUA; } + 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, @@ -2563,15 +2990,18 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, "discard failed", errp); case NBD_CMD_BLOCK_STATUS: - if (!request->len) { - return nbd_send_generic_reply(client, request, -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) { + 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, @@ -2584,7 +3014,7 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, } } - if (client->export_meta.allocation_depth) { + if (request->contexts->allocation_depth) { ret = nbd_co_send_block_status(client, request, exp->common.blk, request->from, request->len, @@ -2597,8 +3027,9 @@ 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, @@ -2614,6 +3045,10 @@ 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, -EINVAL, "CMD_BLOCK_STATUS not negotiated", @@ -2633,16 +3068,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) { @@ -2650,14 +3093,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) { @@ -2669,15 +3123,16 @@ 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) { @@ -2692,89 +3147,158 @@ static coroutine_fn void nbd_trip(void *opaque) } 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"); goto disconnect; } - qio_channel_set_cork(client->ioc, false); 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; + + nbd_set_socket_send_buffer(sioc); 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 f19a4d0db39..cbd0a4ab7e4 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 cookie, uint16_t flags, uint16_t type, const char *name) "Sending request to server: { .from = %" PRIu64", .len = %" PRIu32 ", .cookie = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) }" +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_structured_reply_chunk(uint16_t flags, uint16_t type, const char *name, uint64_t cookie, uint32_t length) "Got structured reply chunk: { flags = 0x%" PRIx16 ", type = %d (%s), cookie = %" PRIu64 ", length = %" PRIu32 " }" +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 cookie, uint32_t error, const char *errname, int len) "Send simple reply: cookie = %" PRIu64 ", error = %" PRIu32 " (%s), len = %d" +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, size_t size) "Send structured read data reply: cookie = %" PRIu64 ", offset = %" PRIu64 ", data = %p, len = %zu" -nbd_co_send_chunk_read_hole(uint64_t cookie, uint64_t offset, size_t size) "Send structured read hole reply: cookie = %" PRIu64 ", offset = %" PRIu64 ", len = %zu" +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, uint32_t len) "Payload received: cookie = %" 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_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 00000000000..01c5fb914ec --- /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/can/can_core.c b/net/can/can_core.c index 0115d787941..77fe2b8ba48 100644 --- a/net/can/can_core.c +++ b/net/can/can_core.c @@ -149,7 +149,7 @@ static bool can_bus_can_be_deleted(UserCreatable *uc) } static void can_bus_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { UserCreatableClass *uc_klass = USER_CREATABLE_CLASS(klass); @@ -162,7 +162,7 @@ static const TypeInfo can_bus_info = { .instance_size = sizeof(CanBusState), .instance_init = can_bus_instance_init, .class_init = can_bus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/net/can/can_host.c b/net/can/can_host.c index a3c84028c61..3f9bb33e474 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); @@ -78,7 +72,7 @@ static void can_host_complete(UserCreatable *uc, Error **errp) } static void can_host_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { UserCreatableClass *uc_klass = USER_CREATABLE_CLASS(klass); @@ -98,7 +92,7 @@ static const TypeInfo can_host_info = { .class_size = sizeof(CanHostClass), .abstract = true, .class_init = can_host_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/net/can/can_socketcan.c b/net/can/can_socketcan.c index c1a1ad05636..8a57ae07178 100644 --- a/net/can/can_socketcan.c +++ b/net/can/can_socketcan.c @@ -308,7 +308,7 @@ static void can_host_socketcan_instance_init(Object *obj) } static void can_host_socketcan_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { CanHostClass *chc = CAN_HOST_CLASS(klass); diff --git a/net/can/meson.build b/net/can/meson.build index 45693c82c9d..af3b27921cd 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 system_ss.add_all(when: 'CONFIG_CAN_BUS', if_true: can_ss) diff --git a/net/checksum.c b/net/checksum.c index 1a957e4c0b1..537457d89d0 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; @@ -101,7 +101,7 @@ void net_checksum_calculate(uint8_t *data, int length, int csum_flag) 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 ed8bdfff1e7..be53794582c 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 7f9e6f89ce0..0e1844ee4cb 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -25,10 +25,9 @@ #include "chardev/char-fe.h" #include "qemu/sockets.h" #include "colo.h" -#include "sysemu/iothread.h" +#include "system/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)) { @@ -1330,8 +1328,6 @@ static void colo_compare_complete(UserCreatable *uc, Error **errp) } QTAILQ_INSERT_TAIL(&net_compares, s, next); qemu_mutex_unlock(&colo_compare_mutex); - - return; } static void colo_flush_packets(void *opaque, void *user_data) @@ -1356,7 +1352,7 @@ static void colo_flush_packets(void *opaque, void *user_data) } } -static void colo_compare_class_init(ObjectClass *oc, void *data) +static void colo_compare_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); @@ -1439,12 +1435,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); @@ -1482,7 +1476,7 @@ static const TypeInfo colo_compare_info = { .instance_finalize = colo_compare_finalize, .class_size = sizeof(CompareClass), .class_init = colo_compare_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/stubs/colo-compare.c b/net/colo-stubs.c similarity index 100% rename from stubs/colo-compare.c rename to net/colo-stubs.c diff --git a/net/dump.c b/net/dump.c index 7d05f16ca7a..581234b7751 100644 --- a/net/dump.c +++ b/net/dump.c @@ -32,7 +32,7 @@ #include "qapi/visitor.h" #include "net/filter.h" #include "qom/object.h" -#include "sysemu/rtc.h" +#include "system/rtc.h" typedef struct DumpState { int64_t start_ts; @@ -68,7 +68,7 @@ static ssize_t dump_receive_iov(DumpState *s, const struct iovec *iov, int cnt, int64_t ts; int caplen; size_t size = iov_size(iov, cnt) - offset; - struct iovec dumpiov[cnt + 1]; + g_autofree struct iovec *dumpiov = g_new(struct iovec, cnt + 1); /* Early return in case of previous error. */ if (s->fd < 0) { @@ -154,10 +154,9 @@ static ssize_t filter_dump_receive_iov(NetFilterState *nf, NetClientState *sndr, int iovcnt, NetPacketSent *sent_cb) { NetFilterDumpState *nfds = FILTER_DUMP(nf); - int offset = qemu_get_using_vnet_hdr(nf->netdev) ? - qemu_get_vnet_hdr_len(nf->netdev) : 0; - dump_receive_iov(&nfds->ds, iov, iovcnt, offset); + dump_receive_iov(&nfds->ds, iov, iovcnt, flags & QEMU_NET_PACKET_FLAG_RAW ? + 0 : qemu_get_vnet_hdr_len(nf->netdev)); return 0; } @@ -235,7 +234,7 @@ static void filter_dump_instance_finalize(Object *obj) g_free(nfds->filename); } -static void filter_dump_class_init(ObjectClass *oc, void *data) +static void filter_dump_class_init(ObjectClass *oc, const void *data) { NetFilterClass *nfc = NETFILTER_CLASS(oc); diff --git a/net/eth.c b/net/eth.c index 649e66bb1fe..3f680cc033a 100644 --- a/net/eth.c +++ b/net/eth.c @@ -432,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-buffer.c b/net/filter-buffer.c index 283dc9cbe6b..a36be31dc8e 100644 --- a/net/filter-buffer.c +++ b/net/filter-buffer.c @@ -172,7 +172,7 @@ static void filter_buffer_set_interval(Object *obj, Visitor *v, s->interval = value; } -static void filter_buffer_class_init(ObjectClass *oc, void *data) +static void filter_buffer_class_init(ObjectClass *oc, const void *data) { NetFilterClass *nfc = NETFILTER_CLASS(oc); diff --git a/net/filter-mirror.c b/net/filter-mirror.c index 34a63b5dbbf..27734c91a71 100644 --- a/net/filter-mirror.c +++ b/net/filter-mirror.c @@ -410,7 +410,7 @@ static void filter_redirector_set_vnet_hdr(Object *obj, s->vnet_hdr = value; } -static void filter_mirror_class_init(ObjectClass *oc, void *data) +static void filter_mirror_class_init(ObjectClass *oc, const void *data) { NetFilterClass *nfc = NETFILTER_CLASS(oc); @@ -425,7 +425,7 @@ static void filter_mirror_class_init(ObjectClass *oc, void *data) nfc->receive_iov = filter_mirror_receive_iov; } -static void filter_redirector_class_init(ObjectClass *oc, void *data) +static void filter_redirector_class_init(ObjectClass *oc, const void *data) { NetFilterClass *nfc = NETFILTER_CLASS(oc); diff --git a/net/filter-replay.c b/net/filter-replay.c index 54690676ef0..451663c5a56 100644 --- a/net/filter-replay.c +++ b/net/filter-replay.c @@ -17,7 +17,7 @@ #include "qemu/timer.h" #include "qapi/visitor.h" #include "net/filter.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "qom/object.h" #define TYPE_FILTER_REPLAY "filter-replay" @@ -65,7 +65,7 @@ static void filter_replay_instance_finalize(Object *obj) replay_unregister_net(nfrs->rns); } -static void filter_replay_class_init(ObjectClass *oc, void *data) +static void filter_replay_class_init(ObjectClass *oc, const void *data) { NetFilterClass *nfc = NETFILTER_CLASS(oc); diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c index c18c4c20190..cdf85aa5eed 100644 --- a/net/filter-rewriter.c +++ b/net/filter-rewriter.c @@ -411,7 +411,7 @@ static void filter_rewriter_init(Object *obj) s->failover_mode = FAILOVER_MODE_OFF; } -static void colo_rewriter_class_init(ObjectClass *oc, void *data) +static void colo_rewriter_class_init(ObjectClass *oc, const void *data) { NetFilterClass *nfc = NETFILTER_CLASS(oc); diff --git a/net/filter.c b/net/filter.c index 33359087711..c7cc6615dc9 100644 --- a/net/filter.c +++ b/net/filter.c @@ -333,7 +333,7 @@ static void default_handle_event(NetFilterState *nf, int event, Error **errp) } } -static void netfilter_class_init(ObjectClass *oc, void *data) +static void netfilter_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); NetFilterClass *nfc = NETFILTER_CLASS(oc); @@ -363,7 +363,7 @@ static const TypeInfo netfilter_info = { .instance_size = sizeof(NetFilterState), .instance_init = netfilter_init, .instance_finalize = netfilter_finalize, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/net/hub.c b/net/hub.c index 4c8a469a50a..cba20ebd874 100644 --- a/net/hub.c +++ b/net/hub.c @@ -20,7 +20,7 @@ #include "hub.h" #include "qemu/iov.h" #include "qemu/error-report.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" /* * A hub broadcasts incoming packets to all its ports except the source port. @@ -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 */ diff --git a/net/meson.build b/net/meson.build index 51caa42c9d2..bb97b4dcbeb 100644 --- a/net/meson.build +++ b/net/meson.build @@ -20,6 +20,8 @@ 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() @@ -36,25 +38,26 @@ system_ss.add(when: vde, if_true: files('vde.c')) if have_netmap system_ss.add(files('netmap.c')) endif + +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')) - system_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-user-stub.c')) endif -if targetos == 'windows' +if host_os == 'windows' system_ss.add(files('tap-win32.c')) -elif targetos == 'linux' +elif host_os == 'linux' system_ss.add(files('tap.c', 'tap-linux.c')) -elif targetos in bsd_oses +elif host_os in bsd_oses system_ss.add(files('tap.c', 'tap-bsd.c')) -elif targetos == 'sunos' +elif host_os == 'sunos' system_ss.add(files('tap.c', 'tap-solaris.c')) else system_ss.add(files('tap.c', 'tap-stub.c')) endif if have_vhost_net_vdpa system_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost-vdpa.c'), if_false: files('vhost-vdpa-stub.c')) - system_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-vdpa-stub.c')) endif vmnet_files = files( diff --git a/net/net-hmp-cmds.c b/net/net-hmp-cmds.c index 41d326bf5f1..e7c55d27876 100644 --- a/net/net-hmp-cmds.c +++ b/net/net-hmp-cmds.c @@ -22,7 +22,7 @@ #include "qapi/clone-visitor.h" #include "qapi/qapi-commands-net.h" #include "qapi/qapi-visit-net.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/config-file.h" #include "qemu/help_option.h" #include "qemu/option.h" diff --git a/net/net.c b/net/net.c index 6492ad530e2..39d6f28158a 100644 --- a/net/net.c +++ b/net/net.c @@ -36,7 +36,7 @@ #include "qemu/help_option.h" #include "qapi/qapi-commands-net.h" #include "qapi/qapi-visit-net.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qemu/sockets.h" @@ -51,11 +51,12 @@ #include "qemu/keyval.h" #include "qapi/error.h" #include "qapi/opts-visitor.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "net/colo-compare.h" #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) @@ -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++) { @@ -373,9 +381,12 @@ NetClientState *qemu_get_peer(NetClientState *nc, int queue_index) return ncs->peer; } -static void qemu_cleanup_net_client(NetClientState *nc) +static void qemu_cleanup_net_client(NetClientState *nc, + bool remove_from_net_clients) { - QTAILQ_REMOVE(&net_clients, nc, next); + if (remove_from_net_clients) { + QTAILQ_REMOVE(&net_clients, nc, next); + } if (nc->info->cleanup) { nc->info->cleanup(nc); @@ -417,7 +428,13 @@ void qemu_del_net_client(NetClientState *nc) object_unparent(OBJECT(nf)); } - /* If there is a peer NIC, delete and cleanup client, but do not free. */ + /* + * If there is a peer NIC, transfer ownership to it. Delete the client + * from net_client list but do not cleanup nor free. This way NIC can + * still access to members of the backend. + * + * The cleanup and free will be done when the NIC is free. + */ if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_NIC) { NICState *nic = qemu_get_nic(nc->peer); if (nic->peer_deleted) { @@ -427,21 +444,18 @@ void qemu_del_net_client(NetClientState *nc) for (i = 0; i < queues; i++) { ncs[i]->peer->link_down = true; + QTAILQ_REMOVE(&net_clients, ncs[i], next); } if (nc->peer->info->link_status_changed) { nc->peer->info->link_status_changed(nc->peer); } - for (i = 0; i < queues; i++) { - qemu_cleanup_net_client(ncs[i]); - } - return; } for (i = 0; i < queues; i++) { - qemu_cleanup_net_client(ncs[i]); + qemu_cleanup_net_client(ncs[i], true); qemu_free_net_client(ncs[i]); } } @@ -454,8 +468,12 @@ void qemu_del_nic(NICState *nic) for (i = 0; i < queues; i++) { NetClientState *nc = qemu_get_subqueue(nic, i); - /* If this is a peer NIC and peer has already been deleted, free it now. */ + /* + * If this is a peer NIC and peer has already been deleted, clean it up + * and free it now. + */ if (nic->peer_deleted) { + qemu_cleanup_net_client(nc->peer, false); qemu_free_net_client(nc->peer); } else if (nc->peer) { /* if there are RX packets pending, complete them */ @@ -466,7 +484,7 @@ void qemu_del_nic(NICState *nic) for (i = queues - 1; i >= 0; i--) { NetClientState *nc = qemu_get_subqueue(nic, i); - qemu_cleanup_net_client(nc); + qemu_cleanup_net_client(nc, true); qemu_free_net_client(nc); } @@ -495,59 +513,50 @@ bool qemu_has_ufo(NetClientState *nc) return nc->info->has_ufo(nc); } -bool qemu_has_vnet_hdr(NetClientState *nc) +bool qemu_has_uso(NetClientState *nc) { - if (!nc || !nc->info->has_vnet_hdr) { + if (!nc || !nc->info->has_uso) { return false; } - return nc->info->has_vnet_hdr(nc); + return nc->info->has_uso(nc); } -bool qemu_has_vnet_hdr_len(NetClientState *nc, int len) +bool qemu_has_vnet_hdr(NetClientState *nc) { - if (!nc || !nc->info->has_vnet_hdr_len) { + if (!nc || !nc->info->has_vnet_hdr) { return false; } - return nc->info->has_vnet_hdr_len(nc, len); + return nc->info->has_vnet_hdr(nc); } -bool qemu_get_using_vnet_hdr(NetClientState *nc) +bool qemu_has_vnet_hdr_len(NetClientState *nc, int len) { - if (!nc || !nc->info->get_using_vnet_hdr) { + if (!nc || !nc->info->has_vnet_hdr_len) { return false; } - return nc->info->get_using_vnet_hdr(nc); -} - -void qemu_using_vnet_hdr(NetClientState *nc, bool enable) -{ - if (!nc || !nc->info->using_vnet_hdr) { - return; - } - - nc->info->using_vnet_hdr(nc, enable); + return nc->info->has_vnet_hdr_len(nc, len); } void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6, - int ecn, int ufo) + int ecn, int ufo, int uso4, int uso6) { if (!nc || !nc->info->set_offload) { return; } - nc->info->set_offload(nc, csum, tso4, tso6, ecn, ufo); + nc->info->set_offload(nc, csum, tso4, tso6, ecn, ufo, uso4, uso6); } int qemu_get_vnet_hdr_len(NetClientState *nc) { - if (!nc || !nc->info->get_vnet_hdr_len) { + if (!nc) { return 0; } - return nc->info->get_vnet_hdr_len(nc); + return nc->vnet_hdr_len; } void qemu_set_vnet_hdr_len(NetClientState *nc, int len) @@ -556,6 +565,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); } @@ -751,16 +764,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, @@ -788,11 +791,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; @@ -804,8 +803,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) { @@ -816,12 +818,33 @@ 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; + iovcnt++; + } + + 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; } @@ -951,51 +974,6 @@ GPtrArray *qemu_get_nic_models(const char *device_type) return nic_models; } -int qemu_show_nic_models(const char *arg, const char *const *models) -{ - int i; - - if (!arg || !is_help_option(arg)) { - return 0; - } - - printf("Available 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; -} - static int net_init_nic(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp) { @@ -1063,6 +1041,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, @@ -1082,6 +1261,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 @@ -1186,6 +1368,9 @@ void show_netdevs(void) #ifdef CONFIG_NETMAP "netmap", #endif +#ifdef CONFIG_AF_XDP + "af-xdp", +#endif #ifdef CONFIG_POSIX "vhost-user", #endif @@ -1212,7 +1397,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) { @@ -1232,8 +1417,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; } @@ -1484,18 +1669,48 @@ 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. + * + * However, the NIC may have peers that trust to be clean beyond this + * point. For example, if they have been removed with device_del. + * + * 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)); + NICState *nic = qemu_get_nic(nc); + + if (nic->peer_deleted) { + int queues = MAX(nic->conf->peers.queues, 1); + + for (int i = 0; i < queues; i++) { + nc = qemu_get_subqueue(nic, i); + qemu_cleanup_net_client(nc->peer, false); + } + } + + /* Skip NET_CLIENT_DRIVER_NIC entries */ + p = &QTAILQ_NEXT(nc, next); } else { qemu_del_net_client(nc); } @@ -1509,6 +1724,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) { @@ -1537,6 +1756,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); } @@ -1566,9 +1791,14 @@ static int net_param_nic(void *dummy, QemuOpts *opts, Error **errp) } if (is_help_option(type)) { GPtrArray *nic_models = qemu_get_nic_models(TYPE_DEVICE); + int i; show_netdevs(); printf("\n"); - qemu_show_nic_models(type, (const char **)nic_models->pdata); + 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); } @@ -1588,6 +1818,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) { @@ -1662,7 +1896,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; @@ -1674,13 +1908,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"); @@ -1696,12 +1930,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); @@ -1710,9 +1944,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 9e0cec58d37..297510e1908 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 c872d51df8b..fb33856c188 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 c33b3e02e70..9657e86a841 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -40,12 +40,13 @@ #include "qemu/sockets.h" #include #include "chardev/char-fe.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/cutils.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/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) @@ -246,7 +247,14 @@ static void net_slirp_timer_mod(void *timer, int64_t expire_timer, timer_mod(&t->timer, expire_timer); } -static void net_slirp_register_poll_fd(int fd, void *opaque) +#if !SLIRP_CHECK_VERSION(4, 9, 0) +# define slirp_os_socket int +# define slirp_pollfds_fill_socket slirp_pollfds_fill +# define register_poll_socket register_poll_fd +# define unregister_poll_socket unregister_poll_fd +#endif + +static void net_slirp_register_poll_sock(slirp_os_socket fd, void *opaque) { #ifdef WIN32 AioContext *ctxt = qemu_get_aio_context(); @@ -259,7 +267,7 @@ static void net_slirp_register_poll_fd(int fd, void *opaque) #endif } -static void net_slirp_unregister_poll_fd(int fd, void *opaque) +static void net_slirp_unregister_poll_sock(slirp_os_socket fd, void *opaque) { #ifdef WIN32 if (WSAEventSelect(fd, NULL, 0) != 0) { @@ -285,8 +293,8 @@ static const SlirpCb slirp_cb = { #endif .timer_free = net_slirp_timer_free, .timer_mod = net_slirp_timer_mod, - .register_poll_fd = net_slirp_register_poll_fd, - .unregister_poll_fd = net_slirp_unregister_poll_fd, + .register_poll_socket = net_slirp_register_poll_sock, + .unregister_poll_socket = net_slirp_unregister_poll_sock, .notify = net_slirp_notify, }; @@ -313,7 +321,7 @@ static int slirp_poll_to_gio(int events) return ret; } -static int net_slirp_add_poll(int fd, int events, void *opaque) +static int net_slirp_add_poll(slirp_os_socket fd, int events, void *opaque) { GArray *pollfds = opaque; GPollFD pfd = { @@ -362,8 +370,8 @@ static void net_slirp_poll_notify(Notifier *notifier, void *data) switch (poll->state) { case MAIN_LOOP_POLL_FILL: - slirp_pollfds_fill(s->slirp, &poll->timeout, - net_slirp_add_poll, poll->pollfds); + slirp_pollfds_fill_socket(s->slirp, &poll->timeout, + net_slirp_add_poll, poll->pollfds); break; case MAIN_LOOP_POLL_OK: case MAIN_LOOP_POLL_ERR: @@ -628,7 +636,9 @@ static int net_slirp_init(NetClientState *peer, const char *model, s = DO_UPCAST(SlirpState, nc, nc); - cfg.version = SLIRP_CHECK_VERSION(4,7,0) ? 4 : 1; + cfg.version = + SLIRP_CHECK_VERSION(4, 9, 0) ? 6 : + SLIRP_CHECK_VERSION(4, 7, 0) ? 4 : 1; cfg.restricted = restricted; cfg.in_enabled = ipv4; cfg.vnetwork = net; @@ -659,8 +669,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); @@ -717,7 +727,12 @@ static SlirpState *slirp_lookup(Monitor *mon, 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; @@ -754,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"); @@ -774,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; @@ -801,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; } @@ -810,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_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 (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr, - guest_port) < 0) { + if (err < 0) { error_setg(errp, "Could not set up host forwarding rule '%s'", redir_str); return -1; diff --git a/net/socket.c b/net/socket.c index 8e3702e1f3a..784dda686f5 100644 --- a/net/socket.c +++ b/net/socket.c @@ -157,7 +157,7 @@ static void net_socket_send(void *opaque) NetSocketState *s = opaque; int size; int ret; - uint8_t buf1[NET_BUFSIZE]; + QEMU_UNINITIALIZED uint8_t buf1[NET_BUFSIZE]; const uint8_t *buf; size = recv(s->fd, buf1, sizeof(buf1), 0); diff --git a/net/stream.c b/net/stream.c index 9204b4c96e4..6152d2a05e5 100644 --- a/net/stream.c +++ b/net/stream.c @@ -51,7 +51,7 @@ typedef struct NetStreamState { guint ioc_write_tag; SocketReadState rs; unsigned int send_index; /* number of bytes sent*/ - uint32_t reconnect; + uint32_t reconnect_ms; guint timer_tag; SocketAddress *addr; } NetStreamState; @@ -148,7 +148,7 @@ static gboolean net_stream_send(QIOChannel *ioc, NetStreamState *s = data; int size; int ret; - char buf1[NET_BUFSIZE]; + QEMU_UNINITIALIZED char buf1[NET_BUFSIZE]; const char *buf; size = qio_channel_read(s->ioc, buf1, sizeof(buf1), NULL); @@ -165,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); } @@ -173,7 +174,6 @@ 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); @@ -272,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; } @@ -292,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); @@ -309,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, @@ -325,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; } @@ -381,9 +387,9 @@ static gboolean net_stream_reconnect(gpointer data) static void net_stream_arm_reconnect(NetStreamState *s) { - if (s->reconnect && s->timer_tag == 0) { - s->timer_tag = g_timeout_add_seconds(s->reconnect, - net_stream_reconnect, 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); } } @@ -391,7 +397,7 @@ static int net_stream_client_init(NetClientState *peer, const char *model, const char *name, SocketAddress *addr, - uint32_t reconnect, + uint32_t reconnect_ms, Error **errp) { NetStreamState *s; @@ -400,12 +406,13 @@ 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 = reconnect; - if (reconnect) { + s->reconnect_ms = reconnect_ms; + if (reconnect_ms) { s->addr = QAPI_CLONE(SocketAddress, addr); } qio_channel_socket_connect_async(sioc, addr, @@ -424,13 +431,24 @@ int net_init_stream(const Netdev *netdev, const char *name, sock = &netdev->u.stream; if (!sock->has_server || !sock->server) { + 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, - sock->has_reconnect ? sock->reconnect : 0, - errp); + reconnect_ms, errp); } - if (sock->has_reconnect) { - error_setg(errp, "'reconnect' option is incompatible with " - "socket in server mode"); + 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 4c98fdd337e..b4c84441ba8 100644 --- a/net/tap-bsd.c +++ b/net/tap-bsd.c @@ -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 f54f308d359..22ec2f45d2b 100644 --- a/net/tap-linux.c +++ b/net/tap-linux.c @@ -45,10 +45,21 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int len = sizeof(struct virtio_net_hdr); unsigned int features; - fd = RETRY_ON_EINTR(open(PATH_NET_TUN, O_RDWR)); + + ret = if_nametoindex(ifname); + if (ret) { + g_autofree char *file = g_strdup_printf("/dev/tap%d", ret); + fd = open(file, O_RDWR); + } else { + fd = -1; + } + if (fd < 0) { - error_setg_errno(errp, errno, "could not open %s", PATH_NET_TUN); - return -1; + 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; + } } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; @@ -173,23 +184,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) { - return 0; - } - if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) { + unsigned offload; + + offload = TUN_F_CSUM | TUN_F_USO4 | TUN_F_USO6; + + if (ioctl(fd, TUNSETOFFLOAD, offload) < 0) { 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 +240,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 +259,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 bbbb62c2a75..9a58cecb7f4 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 38e15028bf3..51b7830bef1 100644 --- a/net/tap-solaris.c +++ b/net/tap-solaris.c @@ -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 a0fa25804b6..38673434cbd 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 f327d62ab07..671dee970f7 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, diff --git a/net/tap.c b/net/tap.c index 1bf085d4228..ae1c7e39832 100644 --- a/net/tap.c +++ b/net/tap.c @@ -36,7 +36,7 @@ #include "net/net.h" #include "clients.h" #include "monitor/monitor.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/error-report.h" @@ -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; @@ -117,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)); @@ -131,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]; - - 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; + struct iovec iov = { + .iov_base = (void *)buf, + .iov_len = size + }; - return tap_write_packet(s, iov, 1); + return tap_receive_iov(nc, &iov, 1); } #ifndef __sun__ @@ -217,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. */ @@ -237,29 +214,27 @@ static bool tap_has_ufo(NetClientState *nc) return s->has_ufo; } -static bool tap_has_vnet_hdr(NetClientState *nc) +static bool tap_has_uso(NetClientState *nc) { TAPState *s = DO_UPCAST(TAPState, nc, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); - return !!s->host_vnet_hdr_len; + return s->has_uso; } -static bool tap_has_vnet_hdr_len(NetClientState *nc, int len) +static bool tap_has_vnet_hdr(NetClientState *nc) { 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 !!s->host_vnet_hdr_len; } -static int tap_get_vnet_hdr_len(NetClientState *nc) +static bool tap_has_vnet_hdr_len(NetClientState *nc, int len) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); - - return s->host_vnet_hdr_len; + return tap_has_vnet_hdr(nc); } static void tap_set_vnet_hdr_len(NetClientState *nc, int len) @@ -267,29 +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 bool tap_get_using_vnet_hdr(NetClientState *nc) -{ - TAPState *s = DO_UPCAST(TAPState, nc, nc); - - return s->using_vnet_hdr; -} - -static void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) -{ - TAPState *s = DO_UPCAST(TAPState, nc, nc); - - 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) @@ -307,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) @@ -379,17 +335,14 @@ 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, - .get_using_vnet_hdr = tap_get_using_vnet_hdr, - .using_vnet_hdr = tap_using_vnet_hdr, .set_offload = tap_set_offload, - .get_vnet_hdr_len = tap_get_vnet_hdr_len, .set_vnet_hdr_len = tap_set_vnet_hdr_len, .set_vnet_le = tap_set_vnet_le, .set_vnet_be = tap_set_vnet_be, @@ -413,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); @@ -431,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) { @@ -446,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; @@ -536,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')) { @@ -730,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)) { @@ -745,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)) { @@ -764,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) { diff --git a/net/tap_int.h b/net/tap_int.h index 547f8a5a28f..8857ff299d2 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 823a071bdce..cda960f42bc 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 12555518e83..0b235e50c65 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -16,6 +16,7 @@ #include "chardev/char-fe.h" #include "qapi/error.h" #include "qapi/qapi-commands-net.h" +#include "qapi/qapi-events-net.h" #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qemu/option.h" @@ -271,6 +272,7 @@ static void chr_closed_bh(void *opaque) if (err) { error_report_err(err); } + qapi_event_send_netdev_vhost_user_disconnected(name); } static void net_vhost_user_event(void *opaque, QEMUChrEvent event) @@ -300,6 +302,7 @@ static void net_vhost_user_event(void *opaque, QEMUChrEvent event) net_vhost_user_watch, s); qmp_set_link(name, true, &err); s->started = true; + qapi_event_send_netdev_vhost_user_connected(name, chr->label); break; case CHR_EVENT_CLOSED: /* a close event may happen during a read/write, but vhost diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 34202ca0093..58d738945db 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -26,15 +26,15 @@ #include #include "standard-headers/linux/virtio_net.h" #include "monitor/monitor.h" -#include "migration/migration.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; - Notifier migration_state; + NotifierWithReturn migration_state; VHostNetState *vhost_net; /* Control commands shadow buffers */ @@ -62,6 +62,8 @@ const int vdpa_feature_bits[] = { 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_CTRL_GUEST_OFFLOADS, VIRTIO_NET_F_CTRL_MAC_ADDR, @@ -75,14 +77,18 @@ const int vdpa_feature_bits[] = { 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_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, @@ -111,12 +117,15 @@ static const uint64_t vdpa_svq_device_features = BIT_ULL(VIRTIO_NET_F_STATUS) | BIT_ULL(VIRTIO_NET_F_CTRL_VQ) | BIT_ULL(VIRTIO_NET_F_CTRL_RX) | + BIT_ULL(VIRTIO_NET_F_CTRL_VLAN) | BIT_ULL(VIRTIO_NET_F_CTRL_RX_EXTRA) | BIT_ULL(VIRTIO_NET_F_MQ) | BIT_ULL(VIRTIO_F_ANY_LAYOUT) | BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR) | /* VHOST_F_LOG_ALL is exposed by SVQ */ BIT_ULL(VHOST_F_LOG_ALL) | + BIT_ULL(VIRTIO_NET_F_HASH_REPORT) | + BIT_ULL(VIRTIO_NET_F_RSS) | BIT_ULL(VIRTIO_NET_F_RSC_EXT) | BIT_ULL(VIRTIO_NET_F_STANDBY) | BIT_ULL(VIRTIO_NET_F_SPEED_DUPLEX); @@ -215,14 +224,6 @@ static void vhost_vdpa_cleanup(NetClientState *nc) { VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); - /* - * If a peer NIC is attached, do not cleanup anything. - * Cleanup will happen as a part of qemu_cleanup() -> net_cleanup() - * when the guest is shutting down. - */ - if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_NIC) { - return; - } munmap(s->cvq_cmd_out_buffer, vhost_vdpa_net_cvq_cmd_page_len()); munmap(s->status, vhost_vdpa_net_cvq_cmd_page_len()); if (s->vhost_net) { @@ -230,10 +231,18 @@ static void vhost_vdpa_cleanup(NetClientState *nc) 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_clear_pointer(&s->vhost_vdpa.shared->iova_tree, vhost_iova_tree_delete); + 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) @@ -254,6 +263,18 @@ static bool vhost_vdpa_has_ufo(NetClientState *nc) } +/* + * FIXME: vhost_vdpa doesn't have an API to "set h/w endianness". But it's + * reasonable to assume that h/w is LE by default, because LE is what + * virtio 1.0 and later ask for. So, this function just says "yes, the h/w is + * LE". Otherwise, on a BE machine, higher-level code would mistakely think + * the h/w is BE and can't support VDPA for a virtio 1.0 client. + */ +static int vhost_vdpa_set_vnet_le(NetClientState *nc, bool enable) +{ + return 0; +} + static bool vhost_vdpa_check_peer_type(NetClientState *nc, ObjectClass *oc, Error **errp) { @@ -274,13 +295,19 @@ static ssize_t vhost_vdpa_receive(NetClientState *nc, const uint8_t *buf, return size; } -/** From any vdpa net client, get the netclient of the first queue pair */ -static VhostVDPAState *vhost_vdpa_net_first_nc_vdpa(VhostVDPAState *s) + +/** 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 *nc0 = qemu_get_peer(nic->ncs, 0); + NetClientState *nc_i = qemu_get_peer(nic->ncs, i); - return DO_UPCAST(VhostVDPAState, nc, nc0); + 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) @@ -304,6 +331,8 @@ static void vhost_vdpa_net_log_global_enable(VhostVDPAState *s, bool enable) 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 @@ -316,30 +345,26 @@ static void vhost_vdpa_net_log_global_enable(VhostVDPAState *s, bool enable) 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 void vdpa_net_migration_state_notifier(Notifier *notifier, void *data) +static int vdpa_net_migration_state_notifier(NotifierWithReturn *notifier, + MigrationEvent *e, Error **errp) { - MigrationState *migration = data; - VhostVDPAState *s = container_of(notifier, VhostVDPAState, - migration_state); + VhostVDPAState *s = container_of(notifier, VhostVDPAState, migration_state); - if (migration_in_setup(migration)) { + if (e->type == MIG_EVENT_PRECOPY_SETUP) { vhost_vdpa_net_log_global_enable(s, true); - } else if (migration_has_failed(migration)) { + } 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; - - add_migration_state_change_notifier(&s->migration_state); - if (v->shadow_vqs_enabled) { - v->iova_tree = vhost_iova_tree_new(v->iova_range.first, - v->iova_range.last); - } + migration_add_notifier(&s->migration_state, + vdpa_net_migration_state_notifier); } static int vhost_vdpa_net_data_start(NetClientState *nc) @@ -349,42 +374,48 @@ static int vhost_vdpa_net_data_start(NetClientState *nc) assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); - if (s->always_svq || - migration_is_setup_or_active(migrate_get_current()->state)) { + if (s->always_svq || migration_is_running()) { v->shadow_vqs_enabled = true; - v->shadow_data = true; } else { v->shadow_vqs_enabled = false; - v->shadow_data = false; } if (v->index == 0) { + v->shared->shadow_data = v->shadow_vqs_enabled; vhost_vdpa_net_data_start_first(s); return 0; } - if (v->shadow_vqs_enabled) { - VhostVDPAState *s0 = vhost_vdpa_net_first_nc_vdpa(s); - v->iova_tree = s0->vhost_vdpa.iova_tree; + 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) { - remove_migration_state_change_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.iova_tree, vhost_iova_tree_delete); + migration_remove_notifier(&s->migration_state); } } @@ -393,11 +424,14 @@ static NetClientInfo net_vhost_vdpa_info = { .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, + .set_vnet_le = vhost_vdpa_set_vnet_le, .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, @@ -427,7 +461,9 @@ static int vhost_vdpa_set_address_space_id(struct vhost_vdpa *v, }; int r; - r = ioctl(v->device_fd, VHOST_VDPA_SET_GROUP_ASID, &asid); + 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)); @@ -437,7 +473,7 @@ static int vhost_vdpa_set_address_space_id(struct vhost_vdpa *v, 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 @@ -453,7 +489,8 @@ static void vhost_vdpa_cvq_unmap_buf(struct vhost_vdpa *v, void *addr) return; } - r = vhost_vdpa_dma_unmap(v, v->address_space_id, 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); } @@ -466,18 +503,24 @@ static int vhost_vdpa_cvq_map_buf(struct vhost_vdpa *v, void *buf, size_t size, bool write) { DMAMap map = {}; + hwaddr taddr = (hwaddr)(uintptr_t)buf; int r; - map.translated_addr = (hwaddr)(uintptr_t)buf; map.size = size - 1; map.perm = write ? IOMMU_RW : IOMMU_RO, - r = vhost_iova_tree_map_alloc(v->iova_tree, &map); + r = vhost_iova_tree_map_alloc(v->shared->iova_tree, &map, taddr); if (unlikely(r != IOVA_OK)) { error_report("Cannot map injected element"); + + if (map.translated_addr == taddr) { + error_report("Insertion to IOVA->HVA tree failed"); + /* Remove the mapping from the IOVA-only tree */ + goto dma_map_err; + } return r; } - r = vhost_vdpa_dma_map(v, v->address_space_id, map.iova, + 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; @@ -486,7 +529,7 @@ 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; } @@ -504,11 +547,10 @@ static int vhost_vdpa_net_cvq_start(NetClientState *nc) v = &s->vhost_vdpa; s0 = vhost_vdpa_net_first_nc_vdpa(s); - v->shadow_data = s0->vhost_vdpa.shadow_vqs_enabled; - v->shadow_vqs_enabled = s->always_svq; + v->shadow_vqs_enabled = s0->vhost_vdpa.shadow_vqs_enabled; s->vhost_vdpa.address_space_id = VHOST_VDPA_GUEST_PA_ASID; - if (s->vhost_vdpa.shadow_data) { + if (v->shared->shadow_data) { /* SVQ is already configured for all virtqueues */ goto out; } @@ -525,7 +567,7 @@ static int vhost_vdpa_net_cvq_start(NetClientState *nc) return 0; } - cvq_group = vhost_vdpa_get_vring_group(v->device_fd, + cvq_group = vhost_vdpa_get_vring_group(v->shared->device_fd, v->dev->vq_index_end - 1, &err); if (unlikely(cvq_group < 0)) { @@ -546,26 +588,6 @@ static int vhost_vdpa_net_cvq_start(NetClientState *nc) return 0; } - if (s0->vhost_vdpa.iova_tree) { - /* - * SVQ is already configured for all virtqueues. Reuse IOVA tree 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. - */ - v->iova_tree = s0->vhost_vdpa.iova_tree; - } else { - v->iova_tree = vhost_iova_tree_new(v->iova_range.first, - v->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)) { @@ -595,39 +617,77 @@ static void vhost_vdpa_net_cvq_stop(NetClientState *nc) 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, NULL, in_sg, in_num, NULL, 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; +} + +/* + * 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(); } -static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class, +/* + * 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) { @@ -635,36 +695,74 @@ static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class, .class = class, .cmd = cmd, }; - size_t data_size = iov_size(data_sg, data_num); + size_t data_size = iov_size(data_sg, data_num), cmd_size; + struct iovec out, in; + ssize_t r; + unsigned dummy_cursor_iov_cnt; + VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0); assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl)); + cmd_size = sizeof(ctrl) + data_size; + trace_vhost_vdpa_net_load_cmd(s, class, cmd, data_num, data_size); + if (vhost_svq_available_slots(svq) < 2 || + iov_size(out_cursor, 1) < cmd_size) { + /* + * It is time to flush all pending control commands if SVQ is full + * or control commands shadow buffers are full. + * + * We can poll here since we've had BQL from the time + * we sent the descriptor. + */ + r = vhost_vdpa_net_svq_flush(s, in_cursor->iov_base - + (void *)s->status); + if (unlikely(r < 0)) { + return r; + } - /* pack the CVQ command header */ - memcpy(s->cvq_cmd_out_buffer, &ctrl, sizeof(ctrl)); + 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, - s->cvq_cmd_out_buffer + sizeof(ctrl), data_size); + out_cursor->iov_base + sizeof(ctrl), data_size); - return vhost_vdpa_net_cvq_add(s, data_size + sizeof(ctrl), - sizeof(virtio_net_ctrl_ack)); + /* 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) +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 dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MAC, - VIRTIO_NET_CTRL_MAC_ADDR_SET, - &data, 1); - if (unlikely(dev_written < 0)) { - return dev_written; - } - if (*s->status != VIRTIO_NET_OK) { - return -EIO; + 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; } } @@ -709,53 +807,166 @@ static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n) .iov_len = mul_macs_size, }, }; - ssize_t dev_written = vhost_vdpa_net_load_cmd(s, - VIRTIO_NET_CTRL_MAC, - VIRTIO_NET_CTRL_MAC_TABLE_SET, - data, ARRAY_SIZE(data)); - if (unlikely(dev_written < 0)) { - return dev_written; + 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; } - if (*s->status != VIRTIO_NET_OK) { - return -EIO; + + 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; - ssize_t dev_written; + ssize_t r; if (!virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_MQ)) { return 0; } + trace_vhost_vdpa_net_load_mq(s, n->curr_queue_pairs); + mq.virtqueue_pairs = cpu_to_le16(n->curr_queue_pairs); const struct iovec data = { .iov_base = &mq, .iov_len = sizeof(mq), }; - dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MQ, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, - &data, 1); - if (unlikely(dev_written < 0)) { - return dev_written; + r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_MQ, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, + &data, 1); + if (unlikely(r < 0)) { + return r; } - if (*s->status != VIRTIO_NET_OK) { - return -EIO; + + if (virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_RSS)) { + /* load the receive-side scaling state */ + r = vhost_vdpa_net_load_rss(s, n, out_cursor, in_cursor, true); + if (unlikely(r < 0)) { + return r; + } + } else if (virtio_vdev_has_feature(&n->parent_obj, + VIRTIO_NET_F_HASH_REPORT)) { + /* load the hash calculation state */ + r = vhost_vdpa_net_load_rss(s, n, out_cursor, in_cursor, false); + if (unlikely(r < 0)) { + return r; + } } return 0; } static int vhost_vdpa_net_load_offloads(VhostVDPAState *s, - const VirtIONet *n) + const VirtIONet *n, + struct iovec *out_cursor, + struct iovec *in_cursor) { uint64_t offloads; - ssize_t dev_written; + ssize_t r; if (!virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { @@ -783,20 +994,20 @@ static int vhost_vdpa_net_load_offloads(VhostVDPAState *s, .iov_base = &offloads, .iov_len = sizeof(offloads), }; - dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_GUEST_OFFLOADS, - VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, - &data, 1); - if (unlikely(dev_written < 0)) { - return dev_written; - } - if (*s->status != VIRTIO_NET_OK) { - return -EIO; + 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) { @@ -804,14 +1015,23 @@ static int vhost_vdpa_net_load_rx_mode(VhostVDPAState *s, .iov_base = &on, .iov_len = sizeof(on), }; - return vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_RX, - cmd, &data, 1); + 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) + const VirtIONet *n, + struct iovec *out_cursor, + struct iovec *in_cursor) { - ssize_t dev_written; + ssize_t r; if (!virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_CTRL_RX)) { return 0; @@ -836,13 +1056,10 @@ static int vhost_vdpa_net_load_rx(VhostVDPAState *s, * configuration only at live migration. */ if (!n->mac_table.uni_overflow && !n->promisc) { - dev_written = vhost_vdpa_net_load_rx_mode(s, - VIRTIO_NET_CTRL_RX_PROMISC, 0); - if (unlikely(dev_written < 0)) { - return dev_written; - } - if (*s->status != VIRTIO_NET_OK) { - return -EIO; + r = vhost_vdpa_net_load_rx_mode(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_RX_PROMISC, 0); + if (unlikely(r < 0)) { + return r; } } @@ -864,13 +1081,10 @@ static int vhost_vdpa_net_load_rx(VhostVDPAState *s, * configuration only at live migration. */ if (n->mac_table.multi_overflow || n->allmulti) { - dev_written = vhost_vdpa_net_load_rx_mode(s, - VIRTIO_NET_CTRL_RX_ALLMULTI, 1); - if (unlikely(dev_written < 0)) { - return dev_written; - } - if (*s->status != VIRTIO_NET_OK) { - return -EIO; + r = vhost_vdpa_net_load_rx_mode(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_RX_ALLMULTI, 1); + if (unlikely(r < 0)) { + return r; } } @@ -889,13 +1103,10 @@ static int vhost_vdpa_net_load_rx(VhostVDPAState *s, * configuration only at live migration. */ if (n->alluni) { - dev_written = vhost_vdpa_net_load_rx_mode(s, - VIRTIO_NET_CTRL_RX_ALLUNI, 1); - if (dev_written < 0) { - return dev_written; - } - if (*s->status != VIRTIO_NET_OK) { - return -EIO; + r = vhost_vdpa_net_load_rx_mode(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_RX_ALLUNI, 1); + if (r < 0) { + return r; } } @@ -910,13 +1121,10 @@ static int vhost_vdpa_net_load_rx(VhostVDPAState *s, * configuration only at live migration. */ if (n->nomulti) { - dev_written = vhost_vdpa_net_load_rx_mode(s, - VIRTIO_NET_CTRL_RX_NOMULTI, 1); - if (dev_written < 0) { - return dev_written; - } - if (*s->status != VIRTIO_NET_OK) { - return -EIO; + r = vhost_vdpa_net_load_rx_mode(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_RX_NOMULTI, 1); + if (r < 0) { + return r; } } @@ -931,13 +1139,10 @@ static int vhost_vdpa_net_load_rx(VhostVDPAState *s, * configuration only at live migration. */ if (n->nouni) { - dev_written = vhost_vdpa_net_load_rx_mode(s, - VIRTIO_NET_CTRL_RX_NOUNI, 1); - if (dev_written < 0) { - return dev_written; - } - if (*s->status != VIRTIO_NET_OK) { - return -EIO; + r = vhost_vdpa_net_load_rx_mode(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_RX_NOUNI, 1); + if (r < 0) { + return r; } } @@ -952,48 +1157,119 @@ static int vhost_vdpa_net_load_rx(VhostVDPAState *s, * configuration only at live migration. */ if (n->nobcast) { - dev_written = vhost_vdpa_net_load_rx_mode(s, - VIRTIO_NET_CTRL_RX_NOBCAST, 1); - if (dev_written < 0) { - return dev_written; + r = vhost_vdpa_net_load_rx_mode(s, out_cursor, in_cursor, + VIRTIO_NET_CTRL_RX_NOBCAST, 1); + if (r < 0) { + return r; } - if (*s->status != VIRTIO_NET_OK) { - return -EIO; + } + + 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_load(NetClientState *nc) +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; - } - r = vhost_vdpa_net_load_offloads(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; + } } - r = vhost_vdpa_net_load_rx(s, n); - 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; @@ -1004,12 +1280,13 @@ 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, }; /* @@ -1039,12 +1316,14 @@ static NetClientInfo net_vhost_vdpa_cvq_info = { */ static int vhost_vdpa_net_excessive_mac_filter_cvq_add(VhostVDPAState *s, VirtQueueElement *elem, - struct iovec *out) + 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); @@ -1092,10 +1371,25 @@ static int vhost_vdpa_net_excessive_mac_filter_cvq_add(VhostVDPAState *s, * filter table to the vdpa device, it should send the * VIRTIO_NET_CTRL_RX_PROMISC CVQ command to enable promiscuous mode */ - r = vhost_vdpa_net_load_rx_mode(s, VIRTIO_NET_CTRL_RX_PROMISC, 1); + 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); } @@ -1173,10 +1467,15 @@ static int vhost_vdpa_net_handle_ctrl_avail(VhostShadowVirtqueue *svq, .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, @@ -1205,15 +1504,23 @@ static int vhost_vdpa_net_handle_ctrl_avail(VhostShadowVirtqueue *svq, * the CVQ command directly. */ dev_written = vhost_vdpa_net_excessive_mac_filter_cvq_add(s, elem, - &out); + &out, &vdpa_in); if (unlikely(dev_written < 0)) { goto out; } } else { - dev_written = vhost_vdpa_net_cvq_add(s, out.iov_len, sizeof(status)); - if (unlikely(dev_written < 0)) { + 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))) { @@ -1226,7 +1533,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"); } @@ -1267,15 +1574,13 @@ static const VhostShadowVirtqueueOps vhost_vdpa_net_svq_ops = { 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 | - VIRTIO_CONFIG_S_FEATURES_OK; + VIRTIO_CONFIG_S_DRIVER; int r; - ERRP_GUARD(); - r = ioctl(device_fd, VHOST_GET_BACKEND_FEATURES, &backend_features); if (unlikely(r < 0)) { error_setg_errno(errp, errno, "Cannot get vdpa backend_features"); @@ -1286,14 +1591,22 @@ static int vhost_vdpa_probe_cvq_isolation(int device_fd, uint64_t features, 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, errno, "Cannot set features"); + 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 features"); + error_setg_errno(errp, -r, "Cannot set device status"); goto out; } @@ -1346,13 +1659,14 @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer, 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; + int cvq_isolated = 0; if (is_datapath) { nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, @@ -1371,16 +1685,19 @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer, 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 = vdpa_net_migration_state_notifier; + s->migration_state.notify = NULL; s->vhost_vdpa.shadow_vqs_enabled = svq; - s->vhost_vdpa.iova_range = iova_range; - s->vhost_vdpa.shadow_data = svq; if (queue_pair_index == 0) { vhost_vdpa_net_valid_svq_features(features, &s->vhost_vdpa.migration_blocker); + s->vhost_vdpa.shared = g_new0(VhostVDPAShared, 1); + s->vhost_vdpa.shared->device_fd = vdpa_device_fd; + s->vhost_vdpa.shared->iova_range = iova_range; + s->vhost_vdpa.shared->shadow_data = svq; + s->vhost_vdpa.shared->iova_tree = vhost_iova_tree_new(iova_range.first, + iova_range.last); } else if (!is_datapath) { s->cvq_cmd_out_buffer = mmap(NULL, vhost_vdpa_net_cvq_cmd_page_len(), PROT_READ | PROT_WRITE, @@ -1392,24 +1709,17 @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer, s->vhost_vdpa.shadow_vq_ops = &vhost_vdpa_net_svq_ops; s->vhost_vdpa.shadow_vq_ops_opaque = s; s->cvq_isolated = cvq_isolated; - - /* - * TODO: We cannot migrate devices with CVQ and no x-svq enabled as - * there is no way to set the device state (MAC, MQ, etc) before - * starting the datapath. - * - * Migration blocker ownership now belongs to s->vhost_vdpa. - */ - if (!svq) { - error_setg(&s->vhost_vdpa.migration_blocker, - "net vdpa cannot migrate with CVQ feature"); - } } + 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; } @@ -1459,6 +1769,7 @@ 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; @@ -1521,17 +1832,26 @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name, 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_range, features, errp); + 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_range, features, errp); + opts->x_svq, iova_range, features, shared, + errp); if (!nc) goto err; } diff --git a/net/vmnet-bridged.m b/net/vmnet-bridged.m index 76a28abe793..a04a14fa11e 100644 --- a/net/vmnet-bridged.m +++ b/net/vmnet-bridged.m @@ -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 29582834850..ab33ce2b0c2 100644 --- a/net/vmnet-common.m +++ b/net/vmnet-common.m @@ -17,7 +17,8 @@ #include "clients.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" +#include "net/eth.h" #include #include @@ -47,11 +48,8 @@ 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"; } @@ -96,7 +94,7 @@ ssize_t vmnet_receive_common(NetClientState *nc, if_status = vmnet_write(s->vmnet_if, &packet, &pkt_cnt); if (if_status != VMNET_SUCCESS) { - error_report("vmnet: write error: %s\n", + error_report("vmnet: write error: %s", vmnet_status_map_str(if_status)); return -1; } @@ -150,10 +148,26 @@ static int vmnet_read_packets(VmnetState *s) */ static void vmnet_write_packets_to_qemu(VmnetState *s) { + uint8_t *pkt; + size_t pktsz; + uint8_t min_pkt[ETH_ZLEN]; + size_t min_pktsz; + ssize_t size; + while (s->packets_send_current_pos < s->packets_send_end_pos) { - ssize_t size = qemu_send_packet_async(&s->nc, - s->iov_buf[s->packets_send_current_pos].iov_base, - s->packets_buf[s->packets_send_current_pos].vm_pkt_size, + pkt = s->iov_buf[s->packets_send_current_pos].iov_base; + pktsz = s->packets_buf[s->packets_send_current_pos].vm_pkt_size; + + if (net_peer_needs_padding(&s->nc)) { + min_pktsz = sizeof(min_pkt); + + if (eth_pad_short_frame(min_pkt, &min_pktsz, pkt, pktsz)) { + pkt = min_pkt; + pktsz = min_pktsz; + } + } + + size = qemu_send_packet_async(&s->nc, pkt, pktsz, vmnet_send_completed); if (size == 0) { diff --git a/net/vmnet-host.c b/net/vmnet-host.c index 1f95f7343a1..49fb25c224e 100644 --- a/net/vmnet-host.c +++ b/net/vmnet-host.c @@ -21,31 +21,13 @@ 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->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->net_uuid) { - error_setg(errp, - "vmnet-host.net-uuid feature is " - "unavailable: outdated vmnet.framework API"); - return false; - } -#endif if ((options->start_address || options->end_address || @@ -71,9 +53,6 @@ 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); @@ -85,7 +64,6 @@ static xpc_object_t build_if_desc(const Netdev *netdev) vmnet_network_identifier_key, net_uuid.data); } -#endif if (options->start_address) { xpc_dictionary_set_string(if_desc, diff --git a/net/vmnet-shared.c b/net/vmnet-shared.c index 40c7306a758..4726b072536 100644 --- a/net/vmnet-shared.c +++ b/net/vmnet-shared.c @@ -21,16 +21,6 @@ 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->start_address || options->end_address || options->subnet_mask) && @@ -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/os-posix.c b/os-posix.c index f90dfda9b0d..52925c23d3d 100644 --- a/os-posix.c +++ b/os-posix.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include #include #include #include @@ -31,7 +32,7 @@ #include "qemu/error-report.h" #include "qemu/log.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "qemu/cutils.h" #ifdef CONFIG_LINUX @@ -94,13 +95,13 @@ 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. optarg can be one of 3 forms: + * 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 *optarg) +bool os_set_runas(const char *user_id) { unsigned long lv; const char *ep; @@ -108,14 +109,14 @@ bool os_set_runas(const char *optarg) gid_t got_gid; int rc; - user_pwd = getpwnam(optarg); + user_pwd = getpwnam(user_id); if (user_pwd) { user_uid = -1; user_gid = -1; return true; } - rc = qemu_strtoul(optarg, &ep, 0, &lv); + 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; @@ -173,9 +174,9 @@ static void change_process_uid(void) static const char *chroot_dir; -void os_set_chroot(const char *optarg) +void os_set_chroot(const char *path) { - chroot_dir = optarg; + chroot_dir = path; } static void change_root(void) @@ -256,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; @@ -301,18 +327,29 @@ void os_set_line_buffering(void) setvbuf(stdout, NULL, _IOLBF, 0); } -int os_mlock(void) +int os_mlock(bool on_fault) { #ifdef HAVE_MLOCKALL int ret = 0; + int flags = MCL_CURRENT | MCL_FUTURE; + + if (on_fault) { +#ifdef HAVE_MLOCK_ONFAULT + flags |= MCL_ONFAULT; +#else + error_report("mlockall: on_fault not supported"); + return -EINVAL; +#endif + } - ret = mlockall(MCL_CURRENT | MCL_FUTURE); + ret = mlockall(flags); if (ret < 0) { error_report("mlockall: %s", strerror(errno)); } return ret; #else + (void)on_fault; return -ENOSYS; #endif } diff --git a/os-wasm.c b/os-wasm.c new file mode 100644 index 00000000000..d240c180c5f --- /dev/null +++ b/os-wasm.c @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: MIT */ +/* + * os-wasm.c + * Forked from os-posix.c, removing functions not working on Emscripten + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010 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 +#include +#include +#include +#include + +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "system/runstate.h" +#include "qemu/cutils.h" + +void os_setup_post(void){} +void os_set_line_buffering(void) +{ + setvbuf(stdout, NULL, _IOLBF, 0); +} +void os_setup_early_signal_handling(void) +{ + struct sigaction act; + sigfillset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); +} +void os_set_proc_name(const char *s) +{ + error_report("Change of process name not supported by your OS"); + exit(1); +} +static void termsig_handler(int signal, siginfo_t *info, void *c) +{ + qemu_system_killed(info->si_signo, info->si_pid); +} + +void os_setup_signal_handling(void) +{ + struct sigaction act; + + memset(&act, 0, sizeof(act)); + act.sa_sigaction = termsig_handler; + act.sa_flags = SA_SIGINFO; + sigaction(SIGINT, &act, NULL); + sigaction(SIGHUP, &act, NULL); + sigaction(SIGTERM, &act, NULL); +} +void os_setup_limits(void) +{ + struct rlimit nofile; + + if (getrlimit(RLIMIT_NOFILE, &nofile) < 0) { + warn_report("unable to query NOFILE limit: %s", strerror(errno)); + return; + } + + if (nofile.rlim_cur == nofile.rlim_max) { + return; + } + + nofile.rlim_cur = nofile.rlim_max; + + if (setrlimit(RLIMIT_NOFILE, &nofile) < 0) { + warn_report("unable to set NOFILE limit: %s", strerror(errno)); + return; + } +} +int os_mlock(bool on_fault) +{ +#ifdef HAVE_MLOCKALL + int ret = 0; + int flags = MCL_CURRENT | MCL_FUTURE; + + if (on_fault) { +#ifdef HAVE_MLOCK_ONFAULT + flags |= MCL_ONFAULT; +#else + error_report("mlockall: on_fault not supported"); + return -EINVAL; +#endif + } + + ret = mlockall(flags); + if (ret < 0) { + error_report("mlockall: %s", strerror(errno)); + } + + return ret; +#else + (void)on_fault; + return -ENOSYS; +#endif +} diff --git a/os-win32.c b/os-win32.c index 725ad652e8b..c1bff808b4b 100644 --- a/os-win32.c +++ b/os-win32.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include #include -#include "sysemu/runstate.h" +#include "system/runstate.h" static BOOL WINAPI qemu_ctrl_handler(DWORD type) { diff --git a/page-target.c b/page-target.c new file mode 100644 index 00000000000..8fcd5443b52 --- /dev/null +++ b/page-target.c @@ -0,0 +1,21 @@ +/* + * 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" + +/* 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-target.c b/page-vary-target.c new file mode 100644 index 00000000000..49a32b4fe51 --- /dev/null +++ b/page-vary-target.c @@ -0,0 +1,81 @@ +/* + * Variable page size handling -- target specific part. + * + * 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 . + */ + +#define IN_PAGE_VARY 1 + +#include "qemu/osdep.h" +#include "exec/page-vary.h" +#include "exec/target_page.h" + + +/* + * For system mode, the minimum comes from the number of bits + * required for maximum alignment (6) and the number of bits + * required for TLB_FLAGS_MASK (3). + * + * For user mode, TARGET_PAGE_BITS_VARY is a hack to allow the target + * page size to match the host page size. Mostly, this reduces the + * ordinary target page size to run on a host with 4KiB pages (i.e. x86). + * There is no true minimum required by the implementation, but keep the + * same minimum as for system mode for sanity. + * See linux-user/mmap.c, mmap_h_lt_g and mmap_h_gt_g. + */ +#define TARGET_PAGE_BITS_MIN 9 + +#ifndef TARGET_PAGE_BITS_VARY +QEMU_BUILD_BUG_ON(TARGET_PAGE_BITS < TARGET_PAGE_BITS_MIN); +#endif + +#ifndef CONFIG_USER_ONLY +#include "exec/tlb-flags.h" + +QEMU_BUILD_BUG_ON(TLB_FLAGS_MASK & ((1u < TARGET_PAGE_BITS_MIN) - 1)); + +int migration_legacy_page_bits(void) +{ +#ifdef TARGET_PAGE_BITS_VARY + QEMU_BUILD_BUG_ON(TARGET_PAGE_BITS_LEGACY < TARGET_PAGE_BITS_MIN); + return TARGET_PAGE_BITS_LEGACY; +#else + return TARGET_PAGE_BITS; +#endif +} +#endif + +bool set_preferred_target_page_bits(int bits) +{ + assert(bits >= TARGET_PAGE_BITS_MIN); +#ifdef TARGET_PAGE_BITS_VARY + return set_preferred_target_page_bits_common(bits); +#else + return true; +#endif +} + +void finalize_target_page_bits(void) +{ +#ifndef TARGET_PAGE_BITS_VARY + finalize_target_page_bits_common(TARGET_PAGE_BITS); +#elif defined(CONFIG_USER_ONLY) + assert(target_page.bits != 0); + finalize_target_page_bits_common(target_page.bits); +#else + finalize_target_page_bits_common(TARGET_PAGE_BITS_LEGACY); +#endif +} diff --git a/page-vary.c b/page-vary.c deleted file mode 100644 index 343b4adb95a..00000000000 --- a/page-vary.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Variable page size handling -- target specific part. - * - * 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 . - */ - -#define IN_PAGE_VARY 1 - -#include "qemu/osdep.h" -#include "exec/page-vary.h" -#include "exec/exec-all.h" - -bool set_preferred_target_page_bits(int bits) -{ -#ifdef TARGET_PAGE_BITS_VARY - assert(bits >= TARGET_PAGE_BITS_MIN); - return set_preferred_target_page_bits_common(bits); -#else - return true; -#endif -} - -void finalize_target_page_bits(void) -{ -#ifdef TARGET_PAGE_BITS_VARY - finalize_target_page_bits_common(TARGET_PAGE_BITS_MIN); -#endif -} diff --git a/pc-bios/README b/pc-bios/README index c555dd324ea..d009c378953 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -13,8 +13,8 @@ - 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. + https://gitlab.com/slof/slof, and the image currently in qemu is + built from git tag qemu-slof-20241106. - 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 @@ -43,6 +43,19 @@ run an hypervisor OS or simply a host OS on the "baremetal" platform, also known as the PowerNV (Non-Virtualized) platform. +- pnv-pnor.bin is a non-volatile RAM image used by PowerNV, which stores + NVRAM BIOS settings among other things. This image was created with the + following command (the ffspart tool can be found in the skiboot source tree): + + ffspart -s 0x1000 -c 34 -i pnv-pnor.in -p pnv-pnor.bin + + Where pnv-pnor.in contains the two lines (no leading whitespace): + + NVRAM,0x01000,0x00020000,,,/dev/zero + VERSION,0x21000,0x00001000,,,/dev/zero + + skiboot is then booted once to format the NVRAM partition. + - QemuMacDrivers (https://github.com/ozbenh/QemuMacDrivers) is a project to provide virtualised drivers for PPC MacOS guests. @@ -67,11 +80,23 @@ 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 - NPCM7xx BMC devices. It currently implements the bare minimum to load, parse, - initialize and run boot images stored in SPI flash, but may grow more - features over time as needed. The source code is available at: +- npcm{7xx,8xx}_bootrom.bin is a simplified, free (Apache 2.0) boot ROM for + Nuvoton NPCM7xx/8xx BMC devices. It currently implements the bare minimum to + load, parse, 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 + +- ast27x0_bootrom.bin is a simplified, free (Apache 2.0) boot ROM for + ASPEED AST27x0 BMC SOC. It currently implements the bare minimum to + load, parse, 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/ast27x0_bootrom.bin b/pc-bios/ast27x0_bootrom.bin new file mode 100644 index 00000000000..0b9b3a2360e Binary files /dev/null and b/pc-bios/ast27x0_bootrom.bin differ diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin index f70aa72c60d..509f3988e49 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 94792cf3f43..4870015d9ca 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 6a196cf72a0..4b81a969a9d 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 00000000000..f174a1fc9b2 --- /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/60-edk2-riscv64.json b/pc-bios/descriptors/60-edk2-riscv64.json new file mode 100644 index 00000000000..14811ca307f --- /dev/null +++ b/pc-bios/descriptors/60-edk2-riscv64.json @@ -0,0 +1,31 @@ +{ + "description": "UEFI firmware for riscv64", + "interface-types": [ + "uefi" + ], + "mapping": { + "device": "flash", + "executable": { + "filename": "@DATADIR@/edk2-riscv-code.fd", + "format": "raw" + }, + "nvram-template": { + "filename": "@DATADIR@/edk2-riscv-vars.fd", + "format": "raw" + } + }, + "targets": [ + { + "architecture": "riscv64", + "machines": [ + "virt*" + ] + } + ], + "features": [ + + ], + "tags": [ + + ] +} diff --git a/pc-bios/descriptors/60-edk2-x86_64.json b/pc-bios/descriptors/60-edk2-x86_64.json index 968cb65cf97..4599c63f14e 100644 --- a/pc-bios/descriptors/60-edk2-x86_64.json +++ b/pc-bios/descriptors/60-edk2-x86_64.json @@ -26,6 +26,7 @@ "features": [ "acpi-s3", "amd-sev", + "amd-sev-es", "verbose-dynamic" ], "tags": [ diff --git a/pc-bios/descriptors/meson.build b/pc-bios/descriptors/meson.build index 66f85d01c42..cdd0be01a35 100644 --- a/pc-bios/descriptors/meson.build +++ b/pc-bios/descriptors/meson.build @@ -5,7 +5,9 @@ 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', + '60-edk2-riscv64.json' ] configure_file(input: files(f), output: f, diff --git a/pc-bios/bamboo.dtb b/pc-bios/dtb/bamboo.dtb similarity index 100% rename from pc-bios/bamboo.dtb rename to pc-bios/dtb/bamboo.dtb diff --git a/pc-bios/bamboo.dts b/pc-bios/dtb/bamboo.dts similarity index 100% rename from pc-bios/bamboo.dts rename to pc-bios/dtb/bamboo.dts diff --git a/pc-bios/canyonlands.dtb b/pc-bios/dtb/canyonlands.dtb similarity index 100% rename from pc-bios/canyonlands.dtb rename to pc-bios/dtb/canyonlands.dtb diff --git a/pc-bios/canyonlands.dts b/pc-bios/dtb/canyonlands.dts similarity index 100% rename from pc-bios/canyonlands.dts rename to pc-bios/dtb/canyonlands.dts diff --git a/pc-bios/dtb/meson.build b/pc-bios/dtb/meson.build new file mode 100644 index 00000000000..993032949fe --- /dev/null +++ b/pc-bios/dtb/meson.build @@ -0,0 +1,23 @@ +dtbs = [ + 'bamboo.dtb', + 'canyonlands.dtb', + 'petalogix-ml605.dtb', + 'petalogix-s3adsp1800.dtb', +] + +dtc = find_program('dtc', required: false) +if dtc.found() + foreach out : dtbs + f = fs.replace_suffix(out, '.dts') + custom_target(out, + build_by_default: have_system, + input: files(f), + output: out, + install: get_option('install_blobs'), + install_dir: qemu_datadir / 'dtb', + command: [ dtc, '-q', '-I', 'dts', '-O', 'dtb', + '-o', '@OUTPUT@', '@INPUT0@' ]) + endforeach +else + install_data(dtbs, install_dir: qemu_datadir / 'dtb') +endif diff --git a/pc-bios/petalogix-ml605.dtb b/pc-bios/dtb/petalogix-ml605.dtb similarity index 100% rename from pc-bios/petalogix-ml605.dtb rename to pc-bios/dtb/petalogix-ml605.dtb diff --git a/pc-bios/petalogix-ml605.dts b/pc-bios/dtb/petalogix-ml605.dts similarity index 100% rename from pc-bios/petalogix-ml605.dts rename to pc-bios/dtb/petalogix-ml605.dts diff --git a/pc-bios/petalogix-s3adsp1800.dtb b/pc-bios/dtb/petalogix-s3adsp1800.dtb similarity index 100% rename from pc-bios/petalogix-s3adsp1800.dtb rename to pc-bios/dtb/petalogix-s3adsp1800.dtb diff --git a/pc-bios/petalogix-s3adsp1800.dts b/pc-bios/dtb/petalogix-s3adsp1800.dts similarity index 100% rename from pc-bios/petalogix-s3adsp1800.dts rename to pc-bios/dtb/petalogix-s3adsp1800.dts diff --git a/pc-bios/edk2-aarch64-code.fd.bz2 b/pc-bios/edk2-aarch64-code.fd.bz2 index 4bc824e48d9..2ce728c9ed6 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 7899fca426a..9b98490a801 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 a68ae4fa15a..50c9869960b 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 91936ebbc92..d58c16fb04b 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 00000000000..ba12bc9a066 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 00000000000..8a13571e28a 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 00000000000..f4e243d996f 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 00000000000..36435a10467 Binary files /dev/null and b/pc-bios/edk2-riscv-vars.fd.bz2 differ diff --git a/pc-bios/edk2-riscv.fd.bz2 b/pc-bios/edk2-riscv.fd.bz2 deleted file mode 100644 index ef566b374a8..00000000000 Binary files a/pc-bios/edk2-riscv.fd.bz2 and /dev/null differ diff --git a/pc-bios/edk2-x86_64-code.fd.bz2 b/pc-bios/edk2-x86_64-code.fd.bz2 index 35c2340e46d..cf043fcb629 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 742abf06c59..c2b04f82c69 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 311b7b91d71..50f5b3694dd 100644 Binary files a/pc-bios/edk2-x86_64-secure-code.fd.bz2 and b/pc-bios/edk2-x86_64-secure-code.fd.bz2 differ diff --git a/pc-bios/hppa-firmware.img b/pc-bios/hppa-firmware.img old mode 100644 new mode 100755 index 0fa3808f163..d5f6f2fcaf5 Binary files a/pc-bios/hppa-firmware.img and b/pc-bios/hppa-firmware.img differ diff --git a/pc-bios/hppa-firmware64.img b/pc-bios/hppa-firmware64.img new file mode 100755 index 00000000000..577b0a1b3f9 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 0bd8ce00775..a79a09b2765 100644 --- a/pc-bios/keymaps/meson.build +++ b/pc-bios/keymaps/meson.build @@ -39,19 +39,18 @@ else native_qemu_keymap = qemu_keymap endif +keymap_targets = [] if native_qemu_keymap.found() - t = [] foreach km, args: keymaps # generate with qemu-kvm - t += custom_target(km, - build_by_default: true, - output: km, - command: [native_qemu_keymap, '-f', '@OUTPUT@', args.split()], - install: have_system, - install_dir: qemu_datadir / 'keymaps') + keymap_targets += custom_target(km, + build_by_default: true, + output: km, + command: [native_qemu_keymap, '-f', '@OUTPUT@', args.split()], + install: have_system, + install_dir: qemu_datadir / 'keymaps') endforeach - - alias_target('update-keymaps', t) + alias_target('update-keymaps', keymap_targets) else install_data(keymaps.keys(), install_dir: qemu_datadir / 'keymaps') endif diff --git a/pc-bios/meson.build b/pc-bios/meson.build index e67fa433a1b..3c41620044a 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 @@ -24,6 +28,7 @@ if unpack_edk2_blobs endif blobs = [ + 'ast27x0_bootrom.bin', 'bios.bin', 'bios-256k.bin', 'bios-microvm.bin', @@ -64,46 +69,28 @@ blobs = [ 'kvmvapic.bin', 'pvh.bin', 's390-ccw.img', - 's390-netboot.img', 'slof.bin', 'skiboot.lid', + 'pnv-pnor.bin', 'palcode-clipper', 'u-boot.e500', 'u-boot-sam460-20100605.bin', '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', + 'npcm8xx_bootrom.bin', 'vof.bin', '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) + install_data(blobs, install_dir: qemu_datadir, install_mode: 'rw-r--r--') endif subdir('descriptors') +subdir('dtb') subdir('keymaps') diff --git a/pc-bios/npcm7xx_bootrom.bin b/pc-bios/npcm7xx_bootrom.bin index 38f89d1b97b..903f126636f 100644 Binary files a/pc-bios/npcm7xx_bootrom.bin and b/pc-bios/npcm7xx_bootrom.bin differ diff --git a/pc-bios/npcm8xx_bootrom.bin b/pc-bios/npcm8xx_bootrom.bin new file mode 100644 index 00000000000..6370d647563 Binary files /dev/null and b/pc-bios/npcm8xx_bootrom.bin differ diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc index 4af60026b40..6f472d4b11a 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 41b6a607fb6..96792482607 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 902b4b35080..0a13453efa0 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 9a2ba3f2a4d..b2e740010b2 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 5d4e812819e..018b4731a71 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 b1fff0ba6c8..30d07026c79 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/pnv-pnor.bin b/pc-bios/pnv-pnor.bin new file mode 100644 index 00000000000..3e6f7001440 Binary files /dev/null and b/pc-bios/pnv-pnor.bin differ diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index f0d9ef6d4d8..47240f0a74e 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 acfcd1e71a9..dc69dd484f9 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -3,7 +3,8 @@ 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 @@ -32,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 \ @@ -55,21 +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 -z noexecstack +# 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) -build-all: s390-ccw.img s390-netboot.img +LIBCOBJS := $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) sbrk.o -s390-ccw.elf: $(OBJECTS) - $(call quiet-command,$(CC) $(LDFLAGS) -o $@ $(OBJECTS),Linking) +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 -include $(SRC_PATH)/netboot.mak - -ALL_OBJS = $(sort $(OBJECTS) $(NETOBJS) $(LIBCOBJS) $(LIBNETOBJS)) +ALL_OBJS = $(sort $(OBJECTS) $(LIBCOBJS) $(LIBNETOBJS)) -include $(ALL_OBJS:%.o=%.d) clean: diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index a2137449dc3..0f8baa01985 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,15 +62,34 @@ 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, @@ -144,14 +164,17 @@ static block_number_t load_eckd_segments(block_number_t blk, bool ldipl, 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 = 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 @@ -162,15 +185,20 @@ static block_number_t load_eckd_segments(block_number_t blk, bool ldipl, } /* List directed pointer does not store block size */ - IPL_assert(ldipl || block_size_ok(bprs[j].xeckd.bptr.size), - "bad chunk block size"); + if (!ldipl && !block_size_ok(bprs[j].xeckd.bptr.size)) { + puts("Bad chunk block size"); + return ERROR_BLOCK_NR; + } if (!eckd_valid_address(&bprs[j].xeckd, ldipl)) { /* * If an invalid address is found during LD-IPL then break and - * retry as CCW + * retry as CCW-IPL, otherwise abort on error */ - IPL_assert(ldipl, "bad chunk ECKD addr"); + if (!ldipl) { + puts("Bad chunk ECKD address"); + return ERROR_BLOCK_NR; + } break; } @@ -188,7 +216,10 @@ static block_number_t load_eckd_segments(block_number_t blk, bool ldipl, * 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; } @@ -197,7 +228,10 @@ static block_number_t load_eckd_segments(block_number_t blk, bool ldipl, * to memory (address). */ rc = virtio_read_many(block_nr, (void *)(*address), count + 1); - IPL_assert(rc == 0, "code chunk read failed"); + if (rc != 0) { + puts("Code chunk read failed"); + return ERROR_BLOCK_NR; + } *address += (count + 1) * virtio_get_block_size(); } @@ -231,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)); @@ -243,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)) { /* @@ -251,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) { @@ -260,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); @@ -270,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; @@ -291,17 +335,27 @@ static void run_eckd_boot_script(block_number_t bmt_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) { + panic("loadparm value greater than max number of boot entries allowed"); + } 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 = gen_eckd_block_num(&bmt->entry[loadparm].xeckd, ldipl); - IPL_assert(block_nr != -1, "Cannot find Boot Map Table Entry"); + if (block_nr == NULL_BLOCK_NR) { + printf("The requested boot entry (%d) is invalid\n", loadparm); + panic("Invalid loadparm"); + } 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++) { @@ -316,21 +370,27 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr, do { block_nr = load_eckd_segments(block_nr, ldipl, &address); - } while (block_nr != -1); + if (block_nr == ERROR_BLOCK_NR) { + return ldipl ? 0 : -EIO; + } + } while (block_nr != NULL_BLOCK_NR); } if (ldipl && bms->entry[i].type != BOOT_SCRIPT_EXEC) { /* Abort LD-IPL and retry as CCW-IPL */ - return; + return 0; } - 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 (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; @@ -338,23 +398,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 */ @@ -364,19 +427,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) @@ -384,8 +449,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'; @@ -398,11 +463,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; @@ -414,12 +479,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); @@ -429,8 +497,7 @@ 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) @@ -440,7 +507,10 @@ static block_number_t eckd_find_bmt(ExtEckdBlockPtr *ptr) BootRecord *br; blockno = gen_eckd_block_num(ptr, 0); - read_block(blockno, tmp_sec, "Cannot read boot record"); + 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 */ @@ -466,10 +536,10 @@ static void print_eckd_msg(void) *p-- = ' '; } } - sclp_print(msg); + printf("%s", msg); } -static void ipl_eckd(void) +static int ipl_eckd(void) { IplVolumeLabel *vlbl = (void *)sec; LDL_VTOC *vtoc = (void *)sec; @@ -479,7 +549,10 @@ static void ipl_eckd(void) /* Block 2 can contain either the CDL VOL1 label or the LDL VTOC */ memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(2, vlbl, "Cannot read block 2"); + if (virtio_read(2, vlbl)) { + puts("Cannot read block 2"); + return -EIO; + } /* * First check for a list-directed-format pointer which would @@ -487,43 +560,60 @@ static void ipl_eckd(void) */ if (eckd_valid_address((ExtEckdBlockPtr *)&vlbl->f.br, 0)) { ldipl_bmt = eckd_find_bmt((ExtEckdBlockPtr *)&vlbl->f.br); - if (ldipl_bmt) { - sclp_print("List-Directed\n"); - /* LD-IPL does not use the S1B bock, just make it NULL */ - run_eckd_boot_script(ldipl_bmt, NULL_BLOCK_NR); - /* Only return in error, retry as CCW-IPL */ - sclp_print("Retrying IPL "); + 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)); - read_block(2, vtoc, "Cannot read block 2"); + if (virtio_read(2, vtoc)) { + puts("Cannot read block 2"); + return -EIO; + } } /* Not list-directed */ if (magic_match(vtoc->magic, VOL1_MAGIC)) { - ipl_eckd_cdl(); /* may return in error */ + if (ipl_eckd_cdl()) { + return -1; + } } if (magic_match(vtoc->magic, CMS1_MAGIC)) { - ipl_eckd_ldl(ECKD_CMS); /* no return */ + return ipl_eckd_ldl(ECKD_CMS); } if (magic_match(vtoc->magic, LNX1_MAGIC)) { - ipl_eckd_ldl(ECKD_LDL); /* no return */ + return ipl_eckd_ldl(ECKD_LDL); } - ipl_eckd_ldl(ECKD_LDL_UNLABELED); /* it still may return */ + 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; @@ -543,7 +633,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]; @@ -571,23 +664,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"); @@ -602,22 +709,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; @@ -628,22 +743,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 (virtio_read(0, mbr)) { + puts("Cannot read block 0"); + return -EIO; + } if (!magic_match(mbr->magic, ZIPL_MAGIC)) { - return; + return 0; } - sclp_print("Using SCSI scheme.\n"); + 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) { @@ -653,17 +780,26 @@ 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) { + panic("loadparm value greater than max number of boot entries allowed"); + } + + if (!valid_entries[loadparm]) { + printf("The requested boot entry (%d) is invalid\n", loadparm); + panic("Invalid loadparm"); + } - zipl_run(&prog_table->entry[loadparm].scsi); /* no return */ + return zipl_run(&prog_table->entry[loadparm].scsi); } /*********************************************************************** @@ -677,8 +813,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); @@ -691,28 +829,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) - @@ -743,7 +888,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); @@ -757,8 +902,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; + } } } @@ -773,19 +920,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(); } @@ -808,17 +960,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(); @@ -826,11 +979,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; } @@ -850,19 +1005,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; } /** @@ -884,7 +1045,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(); @@ -892,26 +1053,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(); } /*********************************************************************** @@ -924,14 +1089,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: @@ -941,8 +1112,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 d4690a88c28..95943441d3d 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' @@ -336,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) @@ -387,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 83ca27ab41d..5d543da73fc 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); - - 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]); + puts(msgline); + + 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 8b18153deb4..6a5e86ba01f 100644 --- a/pc-bios/s390-ccw/cio.h +++ b/pc-bios/s390-ccw/cio.h @@ -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 254bb1a15e3..babece95ea2 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 c3948289062..eb1898c84af 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/iplb.h b/pc-bios/s390-ccw/iplb.h index cb6ac8a880a..08f259ff319 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 - -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; +#ifndef QEMU_PACKED +#define QEMU_PACKED __attribute__((packed)) +#endif -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 78f5f46533a..86321d0f46c 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 3187923950e..00000000000 --- 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 bcdc45732dd..00000000000 --- 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 55067980982..76bf743900c 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" @@ -21,7 +23,7 @@ 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 */ @@ -36,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; } @@ -48,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) @@ -70,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 @@ -142,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; } @@ -174,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); @@ -208,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); @@ -238,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; @@ -253,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); } } @@ -287,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 d601952d3e1..84062e94af4 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 046aa35587a..00000000000 --- 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 5cd619b2d60..719a547ada0 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,10 +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) -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; @@ -155,19 +153,10 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len) return rc; } -static int net_init(filename_ip_t *fn_ip) +static int net_init_ip(filename_ip_t *fn_ip) { int rc; - memset(fn_ip, 0, sizeof(filename_ip_t)); - - rc = virtio_net_init(mac); - if (rc < 0) { - puts("Could not initialize network device"); - return -101; - } - fn_ip->fd = rc; - printf(" Using MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); @@ -179,6 +168,14 @@ static int net_init(filename_ip_t *fn_ip) if (fn_ip->ip_version == 4) { set_ipv4_address(fn_ip->own_ip); } + } else if (rc == -2) { + printf("ARP request to TFTP server (%d.%d.%d.%d) failed\n", + (fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF, + (fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF); + return -102; + } else if (rc == -4 || rc == -3) { + puts("Can't obtain TFTP server IP address"); + return -107; } else { puts("Could not get IP address"); return -101; @@ -194,17 +191,6 @@ static int net_init(filename_ip_t *fn_ip) printf(" Using IPv6 address: %s\n", ip6_str); } - if (rc == -2) { - printf("ARP request to TFTP server (%d.%d.%d.%d) failed\n", - (fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF, - (fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF); - return -102; - } - if (rc == -4 || rc == -3) { - puts("Can't obtain TFTP server IP address"); - return -107; - } - printf(" Using TFTP server: "); if (fn_ip->ip_version == 4) { printf("%d.%d.%d.%d\n", @@ -223,11 +209,33 @@ static int net_init(filename_ip_t *fn_ip) return rc; } +static int net_init(filename_ip_t *fn_ip) +{ + int rc; + + memset(fn_ip, 0, sizeof(filename_ip_t)); + + rc = virtio_net_init(mac); + if (rc < 0) { + puts("Could not initialize network device"); + return -101; + } + fn_ip->fd = rc; + + rc = net_init_ip(fn_ip); + if (rc < 0) { + virtio_net_deinit(); + } + + return rc; +} + static void net_release(filename_ip_t *fn_ip) { if (fn_ip->ip_version == 4) { dhcp_send_release(fn_ip->fd); } + virtio_net_deinit(); } /** @@ -293,7 +301,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; } @@ -438,15 +446,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; @@ -475,7 +474,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; @@ -489,7 +488,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); @@ -506,22 +505,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); @@ -535,9 +538,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 c977a52b501..6cdce3e5e5b 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -13,6 +13,11 @@ /* #define DEBUG */ +#include +#include +#include +#include + typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; @@ -25,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 @@ -53,6 +56,9 @@ void write_iplb_location(void); 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); @@ -72,7 +78,7 @@ 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 */ @@ -87,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(); } @@ -109,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 } @@ -147,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 7251f9af4df..4a07de018d2 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 061b06591cf..b70213e4124 100644 --- a/pc-bios/s390-ccw/start.S +++ b/pc-bios/s390-ccw/start.S @@ -112,9 +112,7 @@ io_new_code: lctlg %c6,%c6,0(%r15) br %r14 - .align 8 -bss_start_literal: - .quad __bss_start + .balign 8 disabled_wait_psw: .quad 0x0002000180000000,0x0000000000000000 enabled_wait_psw: @@ -124,8 +122,13 @@ external_new_mask: io_new_mask: .quad 0x0000000180000000 +.data + .balign 8 +bss_start_literal: + .quad __bss_start + .bss - .align 8 + .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 a81207b52eb..7b2d1e20f4d 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" @@ -59,7 +59,7 @@ int virtio_read_many(unsigned long 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; } @@ -73,13 +73,13 @@ unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list 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(); @@ -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 2fcb0a58c5b..301445bf978 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++) { @@ -135,3 +140,8 @@ int recv(int fd, void *buf, int maxlen, int flags) return len; } + +void virtio_net_deinit(void) +{ + virtio_reset(virtio_get_device()); +} diff --git a/pc-bios/s390-ccw/virtio-scsi.c b/pc-bios/s390-ccw/virtio-scsi.c index d1a84b937c8..71db75ce7b4 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,13 +154,13 @@ 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, +static int scsi_read_10(VDev *vdev, unsigned long sector, int sectors, void *data, unsigned int data_size) { @@ -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,7 +284,7 @@ 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; } @@ -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; + } + + if (vdev->config.scsi.cdb_size != VIRTIO_SCSI_CDB_SIZE) { + puts("Config: CDB size mismatch"); + return -EINVAL; + } - sclp_print("Using virtio-scsi.\n"); + puts("Using virtio-scsi."); return virtio_scsi_setup(vdev); } diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index 5edd058d880..cd6c99c7e32 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" @@ -217,26 +217,36 @@ int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd) return 0; } -void virtio_setup_ccw(VDev *vdev) +int virtio_reset(VDev *vdev) { - int i, rc, cfg_size = 0; + return run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false); +} + +int virtio_setup_ccw(VDev *vdev) +{ + 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... */ vdev->guessed_disk_nature = VIRTIO_GDN_NONE; - run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false); + virtio_reset(vdev); 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 +265,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 +309,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 85bd9d1695c..5c5e808a500 100644 --- a/pc-bios/s390-ccw/virtio.h +++ b/pc-bios/s390-ccw/virtio.h @@ -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,8 +274,10 @@ 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_reset(VDev *vdev); +int virtio_setup_ccw(VDev *vdev); int virtio_net_init(void *mac_addr); +void virtio_net_deinit(void); #endif /* VIRTIO_H */ diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img deleted file mode 100644 index 6908e49f068..00000000000 Binary files a/pc-bios/s390-netboot.img and /dev/null differ diff --git a/pc-bios/skiboot.lid b/pc-bios/skiboot.lid index 58ec5ec38ed..ffc77ee1112 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 ef9b81d6282..4314e17b9dd 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 9fb862777fe..011359e7c85 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 91969ae2701..1d233afe69c 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 c429540cde5..f7b06f214c4 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 088385f747b..50dfeb208a1 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 134c751642c..b72279fcf33 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 4cd0d52e772..5b48ca860af 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 976c78667c6..f580c33794a 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 119a2b188bc..03b6dbd77d2 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 cac6131e1b1..3f71aae1715 100644 Binary files a/pc-bios/vgabios.bin and b/pc-bios/vgabios.bin differ diff --git a/plugins/api-system.c b/plugins/api-system.c new file mode 100644 index 00000000000..cc190b167ea --- /dev/null +++ b/plugins/api-system.c @@ -0,0 +1,131 @@ +/* + * QEMU Plugin API - System specific implementations + * + * This provides the APIs that have a specific system implementation + * or are only relevant to system-mode. + * + * Copyright (C) 2017, Emilio G. Cota + * Copyright (C) 2019-2025, Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "qapi/error.h" +#include "migration/blocker.h" +#include "hw/boards.h" +#include "qemu/plugin-memory.h" +#include "qemu/plugin.h" + +/* + * In system mode we cannot trace the binary being executed so the + * helpers all return NULL/0. + */ +const char *qemu_plugin_path_to_binary(void) +{ + return NULL; +} + +uint64_t qemu_plugin_start_code(void) +{ + return 0; +} + +uint64_t qemu_plugin_end_code(void) +{ + return 0; +} + +uint64_t qemu_plugin_entry_code(void) +{ + return 0; +} + +/* + * Virtual Memory queries + */ + +static __thread struct qemu_plugin_hwaddr hwaddr_info; + +struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, + uint64_t vaddr) +{ + CPUState *cpu = current_cpu; + unsigned int mmu_idx = get_mmuidx(info); + enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info); + hwaddr_info.is_store = (rw & QEMU_PLUGIN_MEM_W) != 0; + + assert(mmu_idx < NB_MMU_MODES); + + if (!tlb_plugin_lookup(cpu, vaddr, mmu_idx, + hwaddr_info.is_store, &hwaddr_info)) { + error_report("invalid use of qemu_plugin_get_hwaddr"); + return NULL; + } + + return &hwaddr_info; +} + +bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr) +{ + return haddr->is_io; +} + +uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr) +{ + if (haddr) { + return haddr->phys_addr; + } + return 0; +} + +const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h) +{ + if (h && h->is_io) { + 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(mr->name); + } + } else { + return g_intern_static_string("RAM"); + } +} + +/* + * Time control + */ +static bool has_control; +static Error *migration_blocker; + +const void *qemu_plugin_request_time_control(void) +{ + if (!has_control) { + has_control = true; + error_setg(&migration_blocker, + "TCG plugin time control does not support migration"); + migrate_add_blocker(&migration_blocker, NULL); + return &has_control; + } + return NULL; +} + +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); +} + +void qemu_plugin_update_ns(const void *handle, int64_t new_time) +{ + 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)); + } +} diff --git a/plugins/api-user.c b/plugins/api-user.c new file mode 100644 index 00000000000..28704a89e89 --- /dev/null +++ b/plugins/api-user.c @@ -0,0 +1,57 @@ +/* + * QEMU Plugin API - user-mode only implementations + * + * This provides the APIs that have a user-mode specific + * implementations or are only relevant to user-mode. + * + * Copyright (C) 2017, Emilio G. Cota + * Copyright (C) 2019-2025, Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/plugin.h" +#include "exec/log.h" + +/* + * Virtual Memory queries - these are all NOPs for user-mode which + * only ever has visibility of virtual addresses. + */ + +struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, + uint64_t vaddr) +{ + return NULL; +} + +bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr) +{ + return false; +} + +uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr) +{ + return 0; +} + +const char *qemu_plugin_hwaddr_device_name(const struct qemu_plugin_hwaddr *h) +{ + return g_intern_static_string("Invalid"); +} + +/* + * Time control - for user mode the only real time is wall clock time + * so realistically all you can do in user mode is slow down execution + * which doesn't require the ability to mess with the clock. + */ + +const void *qemu_plugin_request_time_control(void) +{ + return NULL; +} + +void qemu_plugin_update_ns(const void *handle, int64_t new_time) +{ + qemu_log_mask(LOG_UNIMP, "user-mode can't control time"); +} diff --git a/plugins/api.c b/plugins/api.c index 2078b16edb0..3c9d4832e97 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,22 +36,16 @@ */ #include "qemu/osdep.h" +#include "qemu/main-loop.h" #include "qemu/plugin.h" #include "qemu/log.h" #include "tcg/tcg.h" -#include "exec/exec-all.h" -#include "exec/ram_addr.h" +#include "exec/gdbstub.h" +#include "exec/target_page.h" +#include "exec/translation-block.h" +#include "exec/translator.h" #include "disas/disas.h" #include "plugin.h" -#ifndef CONFIG_USER_ONLY -#include "qemu/plugin-memory.h" -#include "hw/boards.h" -#else -#include "qemu.h" -#ifdef CONFIG_LINUX -#include "loader.h" -#endif -#endif /* Uninstall and Reset handlers */ @@ -83,23 +78,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 +128,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_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 (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(struct qemu_plugin_insn *insn, - enum qemu_plugin_op op, - void *ptr, uint64_t imm) +void qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + struct qemu_plugin_insn *insn, + enum qemu_plugin_op op, + qemu_plugin_u64 entry, + uint64_t imm) { - if (!insn->mem_only) { - plugin_register_inline_op(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], - 0, op, ptr, imm); + if (!tb_is_mem_only()) { + plugin_register_inline_op_on_entry(&insn->insn_cbs, 0, op, entry, imm); } } @@ -135,17 +175,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,19 +230,17 @@ 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 * qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx) { - struct qemu_plugin_insn *insn; if (unlikely(idx >= tb->n)) { return NULL; } - insn = g_ptr_array_index(tb->insns, idx); - insn->mem_only = tb->mem_only; - return insn; + return g_ptr_array_index(tb->insns, idx); } /* @@ -212,14 +250,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 +271,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 | ~qemu_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,173 +337,179 @@ 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; +} + +int qemu_plugin_num_vcpus(void) +{ + return plugin_num_vcpus(); +} + /* - * Virtual Memory queries + * Plugin output */ +void qemu_plugin_outs(const char *string) +{ + qemu_log_mask(CPU_LOG_PLUGIN, "%s", string); +} + +bool qemu_plugin_bool_parse(const char *name, const char *value, bool *ret) +{ + return name && value && qapi_bool_parse(name, value, ret, NULL); +} -#ifdef CONFIG_SOFTMMU -static __thread struct qemu_plugin_hwaddr hwaddr_info; -#endif +/* + * 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. + */ -struct qemu_plugin_hwaddr *qemu_plugin_get_hwaddr(qemu_plugin_meminfo_t info, - uint64_t vaddr) +static GArray *create_register_handles(GArray *gdbstub_regs) { -#ifdef CONFIG_SOFTMMU - CPUState *cpu = current_cpu; - unsigned int mmu_idx = get_mmuidx(info); - enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info); - hwaddr_info.is_store = (rw & QEMU_PLUGIN_MEM_W) != 0; + GArray *find_data = g_array_new(true, true, + sizeof(qemu_plugin_reg_descriptor)); - assert(mmu_idx < NB_MMU_MODES); + for (int i = 0; i < gdbstub_regs->len; i++) { + GDBRegDesc *grd = &g_array_index(gdbstub_regs, GDBRegDesc, i); + qemu_plugin_reg_descriptor desc; - if (!tlb_plugin_lookup(cpu, vaddr, mmu_idx, - hwaddr_info.is_store, &hwaddr_info)) { - error_report("invalid use of qemu_plugin_get_hwaddr"); - return NULL; + /* 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 &hwaddr_info; -#else - return NULL; -#endif + return find_data; } -bool qemu_plugin_hwaddr_is_io(const struct qemu_plugin_hwaddr *haddr) +GArray *qemu_plugin_get_registers(void) { -#ifdef CONFIG_SOFTMMU - return haddr->is_io; -#else - return false; -#endif + g_assert(current_cpu); + + g_autoptr(GArray) regs = gdb_get_register_list(current_cpu); + return create_register_handles(regs); } -uint64_t qemu_plugin_hwaddr_phys_addr(const struct qemu_plugin_hwaddr *haddr) +bool qemu_plugin_read_memory_vaddr(uint64_t addr, GByteArray *data, size_t len) { -#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(); - } + g_assert(current_cpu); - return block->offset + offset + block->mr->addr; - } else { - MemoryRegionSection *mrs = haddr->v.io.section; - return mrs->offset_within_address_space + haddr->v.io.offset; - } + if (len == 0) { + return false; } -#endif - return 0; -} -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); - return g_intern_string(temp); - } else { - return g_intern_string(mrs->mr->name); - } - } else { - return g_intern_static_string("RAM"); + 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; } -#else - return g_intern_static_string("Invalid"); -#endif -} -/* - * Queries to the number and potential maximum number of vCPUs there - * will be. This helps the plugin dimension per-vcpu arrays. - */ + return true; +} -#ifndef CONFIG_USER_ONLY -static MachineState * get_ms(void) +int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf) { - return MACHINE(qdev_get_machine()); + g_assert(current_cpu); + + return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg) - 1); } -#endif -int qemu_plugin_n_vcpus(void) +struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size) { -#ifdef CONFIG_USER_ONLY - return -1; -#else - return get_ms()->smp.cpus; -#endif + return plugin_scoreboard_new(element_size); } -int qemu_plugin_n_max_vcpus(void) +void qemu_plugin_scoreboard_free(struct qemu_plugin_scoreboard *score) { -#ifdef CONFIG_USER_ONLY - return -1; -#else - return get_ms()->smp.max_cpus; -#endif + plugin_scoreboard_free(score); } -/* - * Plugin output - */ -void qemu_plugin_outs(const char *string) +void *qemu_plugin_scoreboard_find(struct qemu_plugin_scoreboard *score, + unsigned int vcpu_index) { - qemu_log_mask(CPU_LOG_PLUGIN, "%s", string); + 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); } -bool qemu_plugin_bool_parse(const char *name, const char *value, bool *ret) +static uint64_t *plugin_u64_address(qemu_plugin_u64 entry, + unsigned int vcpu_index) { - return name && value && qapi_bool_parse(name, value, ret, NULL); + char *ptr = qemu_plugin_scoreboard_find(entry.score, vcpu_index); + return (uint64_t *)(ptr + entry.offset); } -/* - * Binary path, start and end locations - */ -const char *qemu_plugin_path_to_binary(void) +void qemu_plugin_u64_add(qemu_plugin_u64 entry, unsigned int vcpu_index, + uint64_t added) { - char *path = NULL; -#ifdef CONFIG_USER_ONLY - TaskState *ts = (TaskState *) current_cpu->opaque; - path = g_strdup(ts->bprm->filename); -#endif - return path; + *plugin_u64_address(entry, vcpu_index) += added; } -uint64_t qemu_plugin_start_code(void) +uint64_t qemu_plugin_u64_get(qemu_plugin_u64 entry, + unsigned int vcpu_index) { - uint64_t start = 0; -#ifdef CONFIG_USER_ONLY - TaskState *ts = (TaskState *) current_cpu->opaque; - start = ts->info->start_code; -#endif - return start; + return *plugin_u64_address(entry, vcpu_index); } -uint64_t qemu_plugin_end_code(void) +void qemu_plugin_u64_set(qemu_plugin_u64 entry, unsigned int vcpu_index, + uint64_t val) { - uint64_t end = 0; -#ifdef CONFIG_USER_ONLY - TaskState *ts = (TaskState *) current_cpu->opaque; - end = ts->info->end_code; -#endif - return end; + *plugin_u64_address(entry, vcpu_index) = val; } -uint64_t qemu_plugin_entry_code(void) +uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry) { - uint64_t entry = 0; -#ifdef CONFIG_USER_ONLY - TaskState *ts = (TaskState *) current_cpu->opaque; - entry = ts->info->entry; -#endif - return entry; + uint64_t total = 0; + for (int i = 0, n = qemu_plugin_num_vcpus(); i < n; ++i) { + total += qemu_plugin_u64_get(entry, i); + } + return total; } + diff --git a/plugins/core.c b/plugins/core.c index 3c4e26c7ed6..eb9281fe547 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -12,23 +12,15 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qemu/config-file.h" -#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/tb-flush.h" -#include "tcg/tcg.h" -#include "tcg/tcg-op.h" +#include "tcg/tcg-op-common.h" #include "plugin.h" -#include "qemu/compiler.h" struct qemu_plugin_cb { struct qemu_plugin_ctx *ctx; @@ -55,7 +47,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 +57,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 +199,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 +322,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 +331,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 +364,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 +423,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)); + + 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)); - 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; + 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 +468,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 +491,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 +513,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 +526,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 +571,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 +594,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(); @@ -567,8 +724,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 809f3f9b13d..8f0d75c9049 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" @@ -28,12 +29,8 @@ #include "qemu/xxhash.h" #include "qemu/plugin.h" #include "qemu/memalign.h" -#include "hw/core/cpu.h" +#include "qemu/target-info.h" #include "exec/tb-flush.h" -#ifndef CONFIG_USER_ONLY -#include "hw/boards.h" -#endif -#include "qemu/compiler.h" #include "plugin.h" @@ -99,7 +96,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; @@ -123,7 +125,7 @@ static int plugin_add(void *opaque, const char *name, const char *value, /* Will treat arg="argname" as "argname=on" */ fullarg = g_strdup_printf("%s=%s", value, "on"); } else { - fullarg = g_strdup_printf("%s", value); + fullarg = g_strdup(value); } warn_report("using 'arg=%s' is deprecated", value); error_printf("Please use '%s' directly\n", fullarg); @@ -140,12 +142,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); } @@ -292,17 +294,11 @@ int qemu_plugin_load_list(QemuPluginList *head, Error **errp) struct qemu_plugin_desc *desc, *next; g_autofree qemu_info_t *info = g_new0(qemu_info_t, 1); - info->target_name = TARGET_NAME; + info->target_name = target_name(); info->version.min = QEMU_PLUGIN_MIN_VERSION; info->version.cur = QEMU_PLUGIN_VERSION; -#ifndef CONFIG_USER_ONLY - MachineState *ms = MACHINE(qdev_get_machine()); - info->system_emulation = true; - info->system.smp_vcpus = ms->smp.cpus; - info->system.max_vcpus = ms->smp.max_cpus; -#else - info->system_emulation = false; -#endif + + qemu_plugin_fillin_mode_info(info); QTAILQ_FOREACH_SAFE(desc, head, entry, next) { int err; @@ -374,7 +370,7 @@ static void plugin_reset_destroy(struct qemu_plugin_reset_data *data) { qemu_rec_mutex_lock(&plugin.lock); plugin_reset_destroy__locked(data); - qemu_rec_mutex_lock(&plugin.lock); + qemu_rec_mutex_unlock(&plugin.lock); } static void plugin_flush_destroy(CPUState *cpu, run_on_cpu_data arg) @@ -391,7 +387,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 71ed996ed31..62c991d87fc 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -1,22 +1,68 @@ -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'] + elif host_os == 'windows' and meson.get_compiler('c').get_id() == 'clang' + # LLVM/lld does not support exporting specific symbols. However, it works + # out of the box with dllexport/dllimport attribute we set in the code. 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 -if get_option('plugins') - specific_ss.add(files( - 'loader.c', - 'core.c', - 'api.c', - ), declare_dependency(link_args: plugin_ldflags)) +if host_os == 'windows' + # 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: [python, '-c', 'import fileinput, re; print("EXPORTS", end=""); [print(re.sub(r"[{};]", "", line), end="") for line in fileinput.input()]', '@INPUT@']) + + # then use dlltool to assemble a delaylib. + # The delaylib will have an "imaginary" name (qemu.exe), that is used by the + # linker file we add with plugins (win32_linker.c) to identify that we want + # to find missing symbols in current program. + win32_qemu_plugin_api_link_flags = ['-Lplugins', '-lqemu_plugin_api'] + if meson.get_compiler('c').get_id() == 'clang' + # With LLVM/lld, delaylib is specified at link time (-delayload) + dlltool = find_program('llvm-dlltool', required: true) + dlltool_cmd = [dlltool, '-d', '@INPUT@', '-l', '@OUTPUT@', '-D', 'qemu.exe'] + win32_qemu_plugin_api_link_flags += ['-Wl,-delayload=qemu.exe'] + else + # With gcc/ld, delay lib is built with a specific delay parameter. + dlltool = find_program('dlltool', required: true) + dlltool_cmd = [dlltool, '--input-def', '@INPUT@', + '--output-delaylib', '@OUTPUT@', '--dllname', 'qemu.exe'] + endif + win32_qemu_plugin_api_lib = configure_file( + input: win32_plugin_def, + output: 'libqemu_plugin_api.a', + command: dlltool_cmd + ) endif + +user_ss.add(files('user.c', 'api-user.c')) +system_ss.add(files('system.c', 'api-system.c')) + +user_ss.add(files('api.c', 'core.c')) +system_ss.add(files('api.c', 'core.c')) + +common_ss.add(files('loader.c')) + diff --git a/plugins/plugin.h b/plugins/plugin.h index 5eb2fdbc85e..6fbc443b96c 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -13,9 +13,10 @@ #define PLUGIN_H #include +#include "qemu/queue.h" #include "qemu/qht.h" -#define QEMU_PLUGIN_MIN_VERSION 0 +#define QEMU_PLUGIN_MIN_VERSION 2 /* global state */ struct qemu_plugin_state { @@ -31,6 +32,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 +47,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 +69,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 +94,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 +109,20 @@ 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); + +/** + * qemu_plugin_fillin_mode_info() - populate mode specific info + * info: pointer to qemu_info_t structure + */ +void qemu_plugin_fillin_mode_info(qemu_info_t *info); #endif /* PLUGIN_H */ diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols deleted file mode 100644 index 71f6c90549d..00000000000 --- 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/plugins/system.c b/plugins/system.c new file mode 100644 index 00000000000..b3ecc33ba52 --- /dev/null +++ b/plugins/system.c @@ -0,0 +1,24 @@ +/* + * QEMU Plugin system-emulation helpers + * + * Helpers that are specific to system emulation. + * + * Copyright (C) 2017, Emilio G. Cota + * Copyright (C) 2019-2025, Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/plugin.h" +#include "hw/boards.h" + +#include "plugin.h" + +void qemu_plugin_fillin_mode_info(qemu_info_t *info) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + info->system_emulation = true; + info->system.smp_vcpus = ms->smp.cpus; + info->system.max_vcpus = ms->smp.max_cpus; +} diff --git a/plugins/user.c b/plugins/user.c new file mode 100644 index 00000000000..250d5425020 --- /dev/null +++ b/plugins/user.c @@ -0,0 +1,19 @@ +/* + * QEMU Plugin user-mode helpers + * + * Helpers that are specific to user-mode. + * + * Copyright (C) 2017, Emilio G. Cota + * Copyright (C) 2019-2025, Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/plugin.h" +#include "plugin.h" + +void qemu_plugin_fillin_mode_info(qemu_info_t *info) +{ + info->system_emulation = false; +} diff --git a/po/it.po b/po/it.po index c6d95172070..363b9bddf2f 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/Makefile b/python/Makefile index 1fa4ba2498e..764b79ccb23 100644 --- a/python/Makefile +++ b/python/Makefile @@ -9,13 +9,13 @@ help: @echo "make check-minreqs:" @echo " Run tests in the minreqs virtual environment." @echo " These tests use the oldest dependencies." - @echo " Requires: Python 3.8" - @echo " Hint (Fedora): 'sudo dnf install python3.8'" + @echo " Requires: Python 3.9" + @echo " Hint (Fedora): 'sudo dnf install python3.9'" @echo "" @echo "make check-tox:" @echo " Run tests against multiple python versions." @echo " These tests use the newest dependencies." - @echo " Requires: Python 3.8 - 3.11, and tox." + @echo " Requires: Python 3.9 - 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". @@ -59,7 +59,7 @@ PIP_INSTALL = pip install --disable-pip-version-check 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) + @python3.9 -m venv $(QEMU_MINVENV_DIR) @( \ echo "ACTIVATE $(QEMU_MINVENV_DIR)"; \ . $(QEMU_MINVENV_DIR)/bin/activate; \ diff --git a/python/qemu/machine/console_socket.py b/python/qemu/machine/console_socket.py index 4e28ba9bb23..0a4e09ffc73 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 diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index c16a0b6fed7..ebb58d5b68c 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -127,7 +127,6 @@ def __init__(self, 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, @@ -141,7 +140,6 @@ def __init__(self, @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 @@ -159,9 +157,10 @@ def __init__(self, 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 self._monitor_address = monitor_address @@ -187,10 +186,8 @@ def __init__(self, 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}.con" - ) 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 @@ -300,9 +297,7 @@ def _base_args(self) -> List[str]: if self._qmp_set: if self._sock_pair: - fd = self._sock_pair[0].fileno() - os.set_inheritable(fd, True) - moncdev = f"socket,id=mon,fd={fd}" + 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 @@ def _base_args(self) -> List[str]: 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,13 +328,17 @@ def args(self) -> List[str]: """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) @@ -352,6 +352,10 @@ def _pre_launch(self) -> None: 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 @@ -369,6 +373,9 @@ def _pre_launch(self) -> None: 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: if self._sock_pair: self._qmp.connect() @@ -396,6 +403,11 @@ def _post_shutdown(self) -> None: 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() @@ -509,11 +521,21 @@ def _early_cleanup(self) -> None: # 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. @@ -686,21 +708,31 @@ def qmp(self, cmd: str, 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 @@ -874,12 +906,34 @@ def console_socket(self) -> socket.socket: 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: """ @@ -890,15 +944,6 @@ def temp_dir(self) -> str: 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 1c46138bd0c..4f5ede85b23 100644 --- a/python/qemu/machine/qtest.py +++ b/python/qemu/machine/qtest.py @@ -24,6 +24,7 @@ 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 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 @@ def connect(self) -> None: @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 @@ def __init__(self, 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 e1e93839783..22a2b5616ef 100644 --- a/python/qemu/qmp/legacy.py +++ b/python/qemu/qmp/legacy.py @@ -194,24 +194,20 @@ def cmd_obj(self, qmp_cmd: QMPMessage) -> QMPMessage: ) ) - 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/protocol.py b/python/qemu/qmp/protocol.py index 753182131fd..a4ffdfad51b 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -495,7 +495,6 @@ async def _stop_server(self) -> None: try: self.logger.debug("Stopping server.") self._server.close() - await self._server.wait_closed() self.logger.debug("Server stopped.") finally: self._server = None diff --git a/python/qemu/qmp/qmp_shell.py b/python/qemu/qmp/qmp_shell.py index 619ab42cedd..98e684e9e8a 100644 --- a/python/qemu/qmp/qmp_shell.py +++ b/python/qemu/qmp/qmp_shell.py @@ -91,14 +91,21 @@ 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 @@ def close(self) -> None: 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/utils/qemu_ga_client.py b/python/qemu/utils/qemu_ga_client.py index d8411bb2d0b..cf0fcf9a8bb 100644 --- a/python/qemu/utils/qemu_ga_client.py +++ b/python/qemu/utils/qemu_ga_client.py @@ -64,7 +64,7 @@ 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 @@ -174,7 +174,7 @@ def suspend(self, mode: str) -> None: # 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']: diff --git a/python/qemu/utils/qom.py b/python/qemu/utils/qom.py index bcf192f4774..426a0f245f2 100644 --- a/python/qemu/utils/qom.py +++ b/python/qemu/utils/qom.py @@ -84,7 +84,7 @@ def __init__(self, args: argparse.Namespace): 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 @@ def __init__(self, args: argparse.Namespace): 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 @@ def _list_node(self, path: str) -> None: 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 80da1b23041..dd2c8b1908c 100644 --- a/python/qemu/utils/qom_common.py +++ b/python/qemu/utils/qom_common.py @@ -140,7 +140,7 @@ def qom_list(self, path: str) -> List[ObjectPropertyInfo]: """ :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 8dcd59fcde6..cf7e344bd53 100644 --- a/python/qemu/utils/qom_fuse.py +++ b/python/qemu/utils/qom_fuse.py @@ -137,7 +137,7 @@ def read(self, path: str, size: int, offset: int, fh: IO[bytes]) -> bytes: 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 @@ def readlink(self, path: str) -> Union[bool, str]: 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 index d0b9c215ca2..8ac5b0b2a05 100644 --- a/python/scripts/mkvenv.py +++ b/python/scripts/mkvenv.py @@ -13,7 +13,6 @@ create create a venv post_init post-venv initialization - ensure Ensure that the specified package is installed. ensuregroup Ensure that the specified package group is installed. @@ -36,18 +35,6 @@ -------------------------------------------------- -usage: mkvenv ensure [-h] [--online] [--dir DIR] dep_spec... - -positional arguments: - dep_spec PEP 508 Dependency specification, e.g. 'meson>=0.61.5' - -options: - -h, --help show this help message and exit - --online Install packages from PyPI, if necessary. - --dir DIR Path to vendored packages where we may install from. - --------------------------------------------------- - usage: mkvenv ensuregroup [-h] [--online] [--dir DIR] file group... positional arguments: @@ -392,6 +379,9 @@ def make_venv( # pylint: disable=too-many-arguments 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. @@ -726,57 +716,6 @@ def _do_ensure( return None -def ensure( - dep_specs: Sequence[str], - online: bool = False, - wheels_dir: Optional[Union[str, Path]] = None, - prog: Optional[str] = None, -) -> None: - """ - Use pip to ensure we have the package specified by @dep_specs. - - If the package is already installed, do nothing. If online and - wheels_dir are both provided, prefer packages found in wheels_dir - first before connecting to PyPI. - - :param dep_specs: - PEP 508 dependency specifications. e.g. ['meson>=0.61.5']. - :param online: If True, fall back to PyPI. - :param wheels_dir: If specified, search this path for packages. - :param prog: - If specified, use this program name for error diagnostics that will - be presented to the user. e.g., 'sphinx-build' can be used as a - bellwether for the presence of 'sphinx'. - """ - - if not HAVE_DISTLIB: - raise Ouch("a usable distlib could not be found, please install it") - - # Convert the depspecs to a dictionary, as if they came - # from a section in a pythondeps.toml file - group: Dict[str, Dict[str, str]] = {} - for spec in dep_specs: - name = distlib.version.LegacyMatcher(spec).name - group[name] = {} - - spec = spec.strip() - pos = len(name) - ver = spec[pos:].strip() - if ver: - group[name]["accepted"] = ver - - if prog: - group[name]["canary"] = prog - prog = None - - result = _do_ensure(group, online, wheels_dir) - if result: - # Well, that's not good. - if result[1]: - raise Ouch(result[0]) - raise SystemExit(f"\n{result[0]}\n\n") - - def _parse_groups(file: str) -> Dict[str, Dict[str, Any]]: if not HAVE_TOMLLIB: if sys.version_info < (3, 11): @@ -888,39 +827,6 @@ def _add_ensuregroup_subcommand(subparsers: Any) -> None: ) -def _add_ensure_subcommand(subparsers: Any) -> None: - subparser = subparsers.add_parser( - "ensure", help="Ensure that the specified package is installed." - ) - subparser.add_argument( - "--online", - action="store_true", - help="Install packages from PyPI, if necessary.", - ) - subparser.add_argument( - "--dir", - type=str, - action="store", - help="Path to vendored packages where we may install from.", - ) - subparser.add_argument( - "--diagnose", - type=str, - action="store", - help=( - "Name of a shell utility to use for " - "diagnostics if this command fails." - ), - ) - subparser.add_argument( - "dep_specs", - type=str, - action="store", - help="PEP 508 Dependency specification, e.g. 'meson>=0.61.5'", - nargs="+", - ) - - def main() -> int: """CLI interface to make_qemu_venv. See module docstring.""" if os.environ.get("DEBUG") or os.environ.get("GITLAB_CI"): @@ -944,7 +850,6 @@ def main() -> int: _add_create_subcommand(subparsers) _add_post_init_subcommand(subparsers) - _add_ensure_subcommand(subparsers) _add_ensuregroup_subcommand(subparsers) args = parser.parse_args() @@ -957,13 +862,6 @@ def main() -> int: ) if args.command == "post_init": post_venv_setup() - if args.command == "ensure": - ensure( - dep_specs=args.dep_specs, - online=args.online, - wheels_dir=args.dir, - prog=args.diagnose, - ) if args.command == "ensuregroup": ensure_group( file=args.file, diff --git a/python/scripts/vendor.py b/python/scripts/vendor.py index 76274871170..b47db00743a 100755 --- a/python/scripts/vendor.py +++ b/python/scripts/vendor.py @@ -41,11 +41,8 @@ def main() -> int: parser.parse_args() packages = { - "meson==0.63.3": - "d677b809c4895dcbaac9bf6c43703fcb3609a4b24c6057c78f828590049cf43a", - - "tomli==2.0.1": - "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "meson==1.8.1": + "374bbf71247e629475fc10b0bd2ef66fc418c2d8f4890572f74de0f97d0d42da", } vendor_dir = Path(__file__, "..", "..", "wheels").resolve() diff --git a/python/setup.cfg b/python/setup.cfg index 8c67dce4579..d7f5dc7bafe 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -14,14 +14,15 @@ classifiers = Natural Language :: English Operating System :: OS Independent Programming Language :: Python :: 3 :: Only - 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.8 +python_requires = >= 3.9 packages = qemu.qmp qemu.machine @@ -40,10 +41,12 @@ devel = isort >= 5.1.2 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 Pygments >= 2.9.0 + sphinx >= 3.4.3 # Provides qom-fuse functionality fuse = @@ -75,8 +78,7 @@ exclude = __pycache__, [mypy] strict = True -python_version = 3.8 -warn_unused_configs = True +python_version = 3.9 namespace_packages = True warn_unused_ignores = False @@ -139,6 +141,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. @@ -182,7 +185,7 @@ multi_line_output=3 # of python available on your system to run this test. [tox:tox] -envlist = py38, py39, py310, py311 +envlist = py39, py310, py311, py312, py313 skip_missing_interpreters = true [testenv] diff --git a/python/tests/minreqs.txt b/python/tests/minreqs.txt index a3f423efd84..cd2e2a81c3d 100644 --- a/python/tests/minreqs.txt +++ b/python/tests/minreqs.txt @@ -1,5 +1,5 @@ # This file lists the ***oldest possible dependencies*** needed to run -# "make check" successfully under ***Python 3.8***. It is used primarily +# "make check" successfully under ***Python 3.9***. It is used primarily # by GitLab CI to ensure that our stated minimum versions in setup.cfg # are truthful and regularly validated. # @@ -11,6 +11,15 @@ # When adding new dependencies, pin the very oldest non-yanked version # on PyPI that allows the test suite to pass. +# For some reason, the presence of packaging==14.0 below requires us to +# also pin setuptools to version 70 or below. Otherwise, the +# installation of the QEMU package itself fails, failing to find +# setuptools. +setuptools<=70 + +# Dependencies for qapidoc/qapi_domain et al +sphinx==3.4.3 + # Dependencies for the TUI addon (Required for successful linting) urwid==2.1.2 urwid-readline==0.13 @@ -38,10 +47,32 @@ pyflakes==2.5.0 # Transitive mypy dependencies mypy-extensions==1.0.0 +tomli==1.1.0 typing-extensions==4.7.1 # Transitive pylint dependencies astroid==2.15.4 +dill==0.2 lazy-object-proxy==1.4.0 +platformdirs==2.2.0 toml==0.10.0 +tomlkit==0.10.1 wrapt==1.14.0 + +# Transitive sphinx dependencies +Jinja2==2.7 +MarkupSafe==1.1.0 +alabaster==0.7.1 +babel==1.3 +docutils==0.12 +imagesize==0.5.0 +packaging==14.0 +pytz==2011b0 +requests==2.5.0 +snowballstemmer==1.1 +sphinxcontrib-applehelp==1.0.0 +sphinxcontrib-devhelp==1.0.0 +sphinxcontrib-htmlhelp==1.0.0 +sphinxcontrib-jsmath==1.0.0 +sphinxcontrib-qthelp==1.0.0 +sphinxcontrib-serializinghtml==1.0.0 diff --git a/python/tests/qapi-flake8.sh b/python/tests/qapi-flake8.sh new file mode 100755 index 00000000000..c69f9ea2e00 --- /dev/null +++ b/python/tests/qapi-flake8.sh @@ -0,0 +1,6 @@ +#!/bin/sh -e +# SPDX-License-Identifier: GPL-2.0-or-later + +python3 -m flake8 ../scripts/qapi/ \ + ../docs/sphinx/qapidoc.py \ + ../docs/sphinx/qapi_domain.py diff --git a/python/tests/qapi-isort.sh b/python/tests/qapi-isort.sh new file mode 100755 index 00000000000..78dd947f68c --- /dev/null +++ b/python/tests/qapi-isort.sh @@ -0,0 +1,8 @@ +#!/bin/sh -e +# SPDX-License-Identifier: GPL-2.0-or-later + +python3 -m isort --sp . -c ../scripts/qapi/ +# Force isort to recognize "compat" as a local module and not third-party +python3 -m isort --sp . -c -p compat -p qapidoc_legacy \ + ../docs/sphinx/qapi_domain.py \ + ../docs/sphinx/qapidoc.py diff --git a/python/tests/qapi-mypy.sh b/python/tests/qapi-mypy.sh new file mode 100755 index 00000000000..363dbaf8c06 --- /dev/null +++ b/python/tests/qapi-mypy.sh @@ -0,0 +1,4 @@ +#!/bin/sh -e +# SPDX-License-Identifier: GPL-2.0-or-later + +python3 -m mypy ../scripts/qapi diff --git a/python/tests/qapi-pylint.sh b/python/tests/qapi-pylint.sh new file mode 100755 index 00000000000..8767d9d2a2d --- /dev/null +++ b/python/tests/qapi-pylint.sh @@ -0,0 +1,8 @@ +#!/bin/sh -e +# SPDX-License-Identifier: GPL-2.0-or-later + +SETUPTOOLS_USE_DISTUTILS=stdlib python3 -m pylint \ + --rcfile=../scripts/qapi/pylintrc \ + ../scripts/qapi/ \ + ../docs/sphinx/qapidoc.py \ + ../docs/sphinx/qapi_domain.py diff --git a/python/wheels/meson-0.63.3-py3-none-any.whl b/python/wheels/meson-0.63.3-py3-none-any.whl deleted file mode 100644 index 8a191e3a200..00000000000 Binary files a/python/wheels/meson-0.63.3-py3-none-any.whl and /dev/null differ diff --git a/python/wheels/meson-1.8.1-py3-none-any.whl b/python/wheels/meson-1.8.1-py3-none-any.whl new file mode 100644 index 00000000000..a885f0e18ce Binary files /dev/null and b/python/wheels/meson-1.8.1-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 00000000000..9c2c7d25936 Binary files /dev/null and b/python/wheels/pycotap-1.3.1-py3-none-any.whl differ diff --git a/python/wheels/tomli-2.0.1-py3-none-any.whl b/python/wheels/tomli-2.0.1-py3-none-any.whl deleted file mode 100644 index 29670b98d16..00000000000 Binary files a/python/wheels/tomli-2.0.1-py3-none-any.whl and /dev/null differ diff --git a/pythondeps.toml b/pythondeps.toml index 0a35ebcf9f0..7884ab521d1 100644 --- a/pythondeps.toml +++ b/pythondeps.toml @@ -10,7 +10,8 @@ # - 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, -# the minimum and maximum +# 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 @@ -18,15 +19,13 @@ [meson] # The install key should match the version in python/wheels/ -meson = { accepted = ">=0.63.0", installed = "0.63.3", canary = "meson" } +meson = { accepted = ">=1.5.0", installed = "1.8.1", canary = "meson" } +pycotap = { accepted = ">=1.1.0", installed = "1.3.1" } [docs] -sphinx = { accepted = ">=1.6", installed = "5.3.0", canary = "sphinx-build" } +# 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 = "(>=88.1, <93.0)", installed = "88.1", canary = "avocado" } -pycdlib = { accepted = ">=1.11.0" } +[testdeps] +qemu.qmp = { accepted = ">=0.0.3", installed = "0.0.3" } diff --git a/qapi/acpi.json b/qapi/acpi.json index e0739bd6ae9..2d53b823656 100644 --- a/qapi/acpi.json +++ b/qapi/acpi.json @@ -80,7 +80,7 @@ ## # @ACPIOSTInfo: # -# OSPM Status Indication for a device For description of possible +# 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. # @@ -111,14 +111,14 @@ # # Since: 2.1 # -# Example: +# .. 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} -# ]} +# -> { "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'] } @@ -131,12 +131,12 @@ # # Since: 2.1 # -# Example: +# .. 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": { "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 519697c0cd8..16de231f6d8 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -65,6 +65,26 @@ '*in': 'AudiodevPerDirectionOptions', '*out': 'AudiodevPerDirectionOptions' } } +## +# @AudiodevDBusOptions: +# +# Options of the D-Bus audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# @nsamples: set the number of samples per read/write calls (default to 480, +# 10ms at 48kHz). +# +# Since: 10.0 +## +{ 'struct': 'AudiodevDBusOptions', + 'data': { + '*in': 'AudiodevPerDirectionOptions', + '*out': 'AudiodevPerDirectionOptions', + '*nsamples': 'uint32'} } + ## # @AudiodevAlsaPerDirectionOptions: # @@ -76,7 +96,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 false) # # Since: 4.0 ## @@ -289,9 +309,9 @@ # # @name: name of the sink/source to use # -# @stream-name: name of the PulseAudio stream created by qemu. Can be +# @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 +# multiple PulseAudio devices or run multiple QEMU instances # (default: audiodev's id, since 4.2) # # @latency: latency you want PulseAudio to achieve in microseconds @@ -333,9 +353,9 @@ # # @name: name of the sink/source to use # -# @stream-name: name of the PipeWire stream created by qemu. Can be +# @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: +# PipeWire devices or run multiple QEMU instances (default: # audiodev's id) # # @latency: latency you want PipeWire to achieve in microseconds @@ -490,7 +510,7 @@ 'if': 'CONFIG_AUDIO_ALSA' }, 'coreaudio': { 'type': 'AudiodevCoreaudioOptions', 'if': 'CONFIG_AUDIO_COREAUDIO' }, - 'dbus': { 'type': 'AudiodevGenericOptions', + 'dbus': { 'type': 'AudiodevDBusOptions', 'if': 'CONFIG_DBUS_DISPLAY' }, 'dsound': { 'type': 'AudiodevDsoundOptions', 'if': 'CONFIG_AUDIO_DSOUND' }, @@ -513,7 +533,7 @@ ## # @query-audiodevs: # -# Returns information about audiodev configuration +# Return information about audiodev configuration # # Returns: array of @Audiodev # diff --git a/qapi/block-core.json b/qapi/block-core.json index 2b1d493d6e0..1df6644f0de 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -31,8 +31,8 @@ # @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) +# counter may be obtained through @query-replay command +# (since 5.2) # # Since: 1.3 ## @@ -196,6 +196,8 @@ ## # @ImageInfoSpecificQCow2Wrapper: # +# @data: image information specific to QCOW2 +# # Since: 1.7 ## { 'struct': 'ImageInfoSpecificQCow2Wrapper', @@ -204,6 +206,8 @@ ## # @ImageInfoSpecificVmdkWrapper: # +# @data: image information specific to VMDK +# # Since: 6.1 ## { 'struct': 'ImageInfoSpecificVmdkWrapper', @@ -212,6 +216,8 @@ ## # @ImageInfoSpecificLUKSWrapper: # +# @data: image information specific to LUKS +# # Since: 2.7 ## { 'struct': 'ImageInfoSpecificLUKSWrapper', @@ -223,6 +229,8 @@ ## # @ImageInfoSpecificRbdWrapper: # +# @data: image information specific to RBD +# # Since: 6.1 ## { 'struct': 'ImageInfoSpecificRbdWrapper', @@ -231,6 +239,8 @@ ## # @ImageInfoSpecificFileWrapper: # +# @data: image information specific to files +# # Since: 8.0 ## { 'struct': 'ImageInfoSpecificFileWrapper', @@ -242,6 +252,8 @@ # A discriminated record of image format specific information # structures. # +# @type: block driver name +# # Since: 1.7 ## { 'union': 'ImageInfoSpecific', @@ -409,6 +421,8 @@ # # @zero: whether the virtual blocks read as zeroes # +# @compressed: true if the data is stored compressed (since 8.2) +# # @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 @@ -426,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: @@ -472,6 +486,10 @@ # @backing_file_depth: number of files in the backing file chain # (since: 1.2) # +# @active: true if the backend is active; typical cases for inactive backends +# are on the migration source instance after migration completes and on the +# destination before it completes. (since: 10.0) +# # @encrypted: true if the backing device is encrypted # # @detect_zeroes: detect and optimize zero writes (Since 2.1) @@ -492,11 +510,11 @@ # # @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) @@ -542,7 +560,7 @@ { 'struct': 'BlockDeviceInfo', 'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str', '*backing_file': 'str', 'backing_file_depth': 'int', - 'encrypted': 'bool', + 'active': 'bool', 'encrypted': 'bool', 'detect_zeroes': 'BlockdevDetectZeroesOptions', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int', @@ -654,9 +672,7 @@ # @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: -# -# :: +# corresponding histogram looks like:: # # 5| * # 4| * @@ -752,89 +768,89 @@ # # Since: 0.14 # -# Example: -# -# -> { "execute": "query-block" } -# <- { -# "return":[ -# { -# "io-status": "ok", -# "device":"ide0-hd0", -# "locked":false, -# "removable":false, -# "inserted":{ -# "ro":false, -# "drv":"qcow2", -# "encrypted":false, -# "file":"disks/test.qcow2", -# "backing_file_depth":1, -# "bps":1000000, -# "bps_rd":0, -# "bps_wr":0, -# "iops":1000000, -# "iops_rd":0, -# "iops_wr":0, -# "bps_max": 8000000, -# "bps_rd_max": 0, -# "bps_wr_max": 0, -# "iops_max": 0, -# "iops_rd_max": 0, -# "iops_wr_max": 0, -# "iops_size": 0, -# "detect_zeroes": "on", -# "write_threshold": 0, -# "image":{ -# "filename":"disks/test.qcow2", -# "format":"qcow2", -# "virtual-size":2048000, -# "backing_file":"base.qcow2", -# "full-backing-filename":"disks/base.qcow2", -# "backing-filename-format":"qcow2", -# "snapshots":[ -# { -# "id": "1", -# "name": "snapshot1", -# "vm-state-size": 0, -# "date-sec": 10000200, -# "date-nsec": 12, -# "vm-clock-sec": 206, -# "vm-clock-nsec": 30 -# } -# ], -# "backing-image":{ -# "filename":"disks/base.qcow2", +# .. 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 -# } -# } -# }, -# "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 } @@ -935,11 +951,11 @@ # @unmap_operations: The number of unmap operations performed by the # device (Since 4.2) # -# @rd_total_time_ns: Total time spent on reads in nanoseconds (since -# 0.15). +# @rd_total_time_ns: Total time spent on reads in nanoseconds +# (since 0.15) # -# @wr_total_time_ns: Total time spent on writes in nanoseconds (since -# 0.15). +# @wr_total_time_ns: Total time spent on writes in nanoseconds +# (since 0.15) # # @zone_append_total_time_ns: Total time spent on zone append writes # in nanoseconds (since 8.1) @@ -1092,6 +1108,8 @@ # # Block driver specific statistics # +# @driver: block driver name +# # Since: 4.2 ## { 'union': 'BlockStatsSpecific', @@ -1144,117 +1162,117 @@ # # @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 +# 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) +# 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' }, @@ -1272,7 +1290,7 @@ # 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 +# 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. @@ -1304,8 +1322,8 @@ # @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. Behavior on +# completion is determined by the BitmapSyncMode. (since: 4.2) # # Since: 1.3 ## @@ -1319,7 +1337,7 @@ # 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' +# successful. This is the behavior always used for incremental # backups. # # @never: The bitmap is never synchronized with the operation, and is @@ -1350,6 +1368,20 @@ { '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: # @@ -1385,21 +1417,23 @@ # @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, and +# disappear. (since 2.12) # # @error: Error information if the job did not complete successfully. # 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: @@ -1426,17 +1460,16 @@ # # @size: new image size in bytes # -# Returns: -# - nothing on success +# Errors: # - If @device is not a valid block device, DeviceNotFound # # Since: 0.14 # -# Example: +# .. qmp-example:: # -# -> { "execute": "block_resize", -# "arguments": { "device": "scratch", "size": 1073741824 } } -# <- { "return": {} } +# -> { "execute": "block_resize", +# "arguments": { "device": "scratch", "size": 1073741824 } } +# <- { "return": {} } ## { 'command': 'block_resize', 'data': { '*device': 'str', @@ -1469,15 +1502,15 @@ # # @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-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'. # @@ -1522,11 +1555,16 @@ # 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: @@ -1545,13 +1583,13 @@ # 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" +# 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) +# 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) @@ -1564,32 +1602,37 @@ # default 'report' (no limitations, since this applies to a # different block device than @device). # +# @on-cbw-error: policy defining behavior on I/O errors in +# copy-before-write jobs; defaults to break-guest-write. (Since 10.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 +# after it has finished its work, waiting for @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) +# @job-dismiss. When true, this job will automatically disappear +# 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) # +# @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 ## @@ -1600,8 +1643,10 @@ '*compress': 'bool', '*on-source-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError', + '*on-cbw-error': 'OnCbwError', '*auto-finalize': 'bool', '*auto-dismiss': 'bool', '*filter-node-name': 'str', + '*discard-source': 'bool', '*x-perf': { 'type': 'BackupPerf', 'features': [ 'unstable' ] } } } @@ -1642,22 +1687,19 @@ # # Takes a synchronous snapshot of a block device. # -# For the arguments, see the documentation of BlockdevSnapshotSync. -# -# Returns: -# - nothing on success +# Errors: # - If @device is not a valid block device, DeviceNotFound # # Since: 0.14 # -# Example: +# .. qmp-example:: # -# -> { "execute": "blockdev-snapshot-sync", -# "arguments": { "device": "ide-hd0", -# "snapshot-file": -# "/some/place/my-image", -# "format": "qcow2" } } -# <- { "return": {} } +# -> { "execute": "blockdev-snapshot-sync", +# "arguments": { "device": "ide-hd0", +# "snapshot-file": +# "/some/place/my-image", +# "format": "qcow2" } } +# <- { "return": {} } ## { 'command': 'blockdev-snapshot-sync', 'data': 'BlockdevSnapshotSync', @@ -1669,12 +1711,10 @@ # 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 +# '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. -# # Features: # # @allow-write-only-overlay: If present, the check whether this @@ -1683,21 +1723,21 @@ # # 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": {} } +# <- { "return": {} } # -# -> { "execute": "blockdev-snapshot", -# "arguments": { "node": "ide-hd0", -# "overlay": "node1534" } } -# <- { "return": {} } +# -> { "execute": "blockdev-snapshot", +# "arguments": { "node": "ide-hd0", +# "overlay": "node1534" } } +# <- { "return": {} } ## { 'command': 'blockdev-snapshot', 'data': 'BlockdevSnapshot', @@ -1710,7 +1750,7 @@ # 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 +# 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 @@ -1724,8 +1764,7 @@ # 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 +# Errors: # - If "device" does not exist or cannot be determined, # DeviceNotFound # @@ -1745,8 +1784,7 @@ # 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) +# 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 @@ -1792,10 +1830,15 @@ # 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) +# 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 @@ -1803,42 +1846,40 @@ # 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 +# after it has finished its work, waiting for @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) +# @job-dismiss. When true, this job will automatically disappear +# 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 +# Errors: # - If @device does not exist, DeviceNotFound -# - Any other error returns a GenericError. # # Since: 1.3 # -# Example: +# .. qmp-example:: # -# -> { "execute": "block-commit", -# "arguments": { "device": "virtio0", -# "top": "/tmp/snap1.qcow2" } } -# <- { "return": {} } +# -> { "execute": "block-commit", +# "arguments": { "device": "virtio0", +# "top": "/tmp/snap1.qcow2" } } +# <- { "return": {} } ## { 'command': 'block-commit', 'data': { '*job-id': 'str', 'device': 'str', '*base-node': 'str', '*base': { 'type': 'str', 'features': [ 'deprecated' ] }, '*top-node': 'str', '*top': { 'type': 'str', 'features': [ 'deprecated' ] }, - '*backing-file': 'str', '*speed': 'int', + '*backing-file': 'str', '*backing-mask-protocol': 'bool', + '*speed': 'int', '*on-error': 'BlockdevOnError', '*filter-node-name': 'str', '*auto-finalize': 'bool', '*auto-dismiss': 'bool' }, @@ -1850,27 +1891,26 @@ # 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. +# 'backup'. The operation can be stopped before it has completed +# using the job-cancel or block-job-cancel command. # # Features: # # @deprecated: This command is deprecated. Use @blockdev-backup # instead. # -# Returns: -# - nothing on success +# Errors: # - If @device is not a valid block device, GenericError # # Since: 1.6 # -# Example: +# .. qmp-example:: # -# -> { "execute": "drive-backup", -# "arguments": { "device": "drive0", -# "sync": "full", -# "target": "backup.img" } } -# <- { "return": {} } +# -> { "execute": "drive-backup", +# "arguments": { "device": "drive0", +# "sync": "full", +# "target": "backup.img" } } +# <- { "return": {} } ## { 'command': 'drive-backup', 'boxed': true, 'data': 'DriveBackup', 'features': ['deprecated'], @@ -1882,22 +1922,21 @@ # 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. +# 'backup'. The operation can be stopped before it has completed +# using the job-cancel or block-job-cancel command. # -# Returns: -# - nothing on success +# Errors: # - If @device is not a valid block device, DeviceNotFound # # Since: 2.3 # -# Example: +# .. qmp-example:: # -# -> { "execute": "blockdev-backup", -# "arguments": { "device": "src-id", -# "sync": "full", -# "target": "tgt-id" } } -# <- { "return": {} } +# -> { "execute": "blockdev-backup", +# "arguments": { "device": "src-id", +# "sync": "full", +# "target": "tgt-id" } } +# <- { "return": {} } ## { 'command': 'blockdev-backup', 'boxed': true, 'data': 'BlockdevBackup', @@ -1915,54 +1954,54 @@ # # Since: 2.0 # -# 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", +# .. 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 -# } -# } } ] } +# "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' ], @@ -1988,7 +2027,7 @@ # # @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. +# identifiers in QEMU. # # @type: Type of graph node. Can be one of block-backend, block-job # or block-driver-state. @@ -2087,24 +2126,23 @@ # 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 +# 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 +# Errors: # - If @device is not a valid block device, GenericError # # Since: 1.3 # -# Example: +# .. qmp-example:: # -# -> { "execute": "drive-mirror", -# "arguments": { "device": "ide-hd0", -# "target": "/some/place/my-image", -# "sync": "full", -# "format": "qcow2" } } -# <- { "return": {} } +# -> { "execute": "drive-mirror", +# "arguments": { "device": "ide-hd0", +# "target": "/some/place/my-image", +# "sync": "full", +# "format": "qcow2" } } +# <- { "return": {} } ## { 'command': 'drive-mirror', 'boxed': true, 'data': 'DriveMirror', @@ -2128,8 +2166,8 @@ # @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 @@ -2171,16 +2209,15 @@ # '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 +# after it has finished its work, waiting for @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) +# @job-dismiss. When true, this job will automatically disappear +# without user intervention. Defaults to true. (Since 3.1) # # Since: 1.3 ## @@ -2268,18 +2305,17 @@ # Create a dirty bitmap with a name on the node, and start tracking # the writes. # -# Returns: -# - nothing on success +# Errors: # - If @node is not a valid block device or node, DeviceNotFound -# - If @name is already taken, GenericError with an explanation +# - If @name is already taken, GenericError # # Since: 2.4 # -# Example: +# .. qmp-example:: # -# -> { "execute": "block-dirty-bitmap-add", -# "arguments": { "node": "drive0", "name": "bitmap0" } } -# <- { "return": {} } +# -> { "execute": "block-dirty-bitmap-add", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-add', 'data': 'BlockDirtyBitmapAdd', @@ -2292,19 +2328,18 @@ # with block-dirty-bitmap-add. If the bitmap is persistent, remove it # from its storage too. # -# Returns: -# - nothing on success +# Errors: # - If @node is not a valid block device or node, DeviceNotFound -# - If @name is not found, GenericError with an explanation +# - If @name is not found, GenericError # - if @name is frozen by an operation, GenericError # # Since: 2.4 # -# Example: +# .. qmp-example:: # -# -> { "execute": "block-dirty-bitmap-remove", -# "arguments": { "node": "drive0", "name": "bitmap0" } } -# <- { "return": {} } +# -> { "execute": "block-dirty-bitmap-remove", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-remove', 'data': 'BlockDirtyBitmap', @@ -2317,18 +2352,17 @@ # backup from this point in time forward will only backup clusters # modified after this clear operation. # -# Returns: -# - nothing on success +# Errors: # - If @node is not a valid block device, DeviceNotFound -# - If @name is not found, GenericError with an explanation +# - If @name is not found, GenericError # # Since: 2.4 # -# Example: +# .. qmp-example:: # -# -> { "execute": "block-dirty-bitmap-clear", -# "arguments": { "node": "drive0", "name": "bitmap0" } } -# <- { "return": {} } +# -> { "execute": "block-dirty-bitmap-clear", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-clear', 'data': 'BlockDirtyBitmap', @@ -2339,18 +2373,17 @@ # # Enables a dirty bitmap so that it will begin tracking disk changes. # -# Returns: -# - nothing on success +# Errors: # - If @node is not a valid block device, DeviceNotFound -# - If @name is not found, GenericError with an explanation +# - If @name is not found, GenericError # # Since: 4.0 # -# Example: +# .. qmp-example:: # -# -> { "execute": "block-dirty-bitmap-enable", -# "arguments": { "node": "drive0", "name": "bitmap0" } } -# <- { "return": {} } +# -> { "execute": "block-dirty-bitmap-enable", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-enable', 'data': 'BlockDirtyBitmap', @@ -2361,18 +2394,17 @@ # # Disables a dirty bitmap so that it will stop tracking disk changes. # -# Returns: -# - nothing on success +# Errors: # - If @node is not a valid block device, DeviceNotFound -# - If @name is not found, GenericError with an explanation +# - If @name is not found, GenericError # # Since: 4.0 # -# Example: +# .. qmp-example:: # -# -> { "execute": "block-dirty-bitmap-disable", -# "arguments": { "node": "drive0", "name": "bitmap0" } } -# <- { "return": {} } +# -> { "execute": "block-dirty-bitmap-disable", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-disable', 'data': 'BlockDirtyBitmap', @@ -2391,8 +2423,7 @@ # 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 +# Errors: # - If @node is not a valid block device, DeviceNotFound # - If any bitmap in @bitmaps or @target is not found, # GenericError @@ -2401,12 +2432,12 @@ # # Since: 4.0 # -# Example: +# .. qmp-example:: # -# -> { "execute": "block-dirty-bitmap-merge", -# "arguments": { "node": "drive0", "target": "bitmap0", -# "bitmaps": ["bitmap1"] } } -# <- { "return": {} } +# -> { "execute": "block-dirty-bitmap-merge", +# "arguments": { "node": "drive0", "target": "bitmap0", +# "bitmaps": ["bitmap1"] } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-merge', 'data': 'BlockDirtyBitmapMerge', @@ -2434,10 +2465,11 @@ # @unstable: This command is meant for debugging. # # Returns: -# - BlockDirtyBitmapSha256 on success +# BlockDirtyBitmapSha256 +# +# Errors: # - If @node is not a valid block device, DeviceNotFound # - If @name is not found or if hashing has failed, GenericError -# with an explanation # # Since: 2.10 ## @@ -2495,28 +2527,30 @@ # '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 +# after it has finished its work, waiting for @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) +# @job-dismiss. When true, this job will automatically disappear +# without user intervention. Defaults to true. (Since 3.1) # -# Returns: nothing on success. +# @target-is-zero: Assume the destination reads as all zeroes before +# the mirror started. Setting this to true can speed up the +# mirror. Setting this to true when the destination is not +# actually all zero can corrupt the destination. (Since 10.1) # # Since: 2.6 # -# Example: +# .. qmp-example:: # -# -> { "execute": "blockdev-mirror", -# "arguments": { "device": "ide-hd0", -# "target": "target0", -# "sync": "full" } } -# <- { "return": {} } +# -> { "execute": "blockdev-mirror", +# "arguments": { "device": "ide-hd0", +# "target": "target0", +# "sync": "full" } } +# <- { "return": {} } ## { 'command': 'blockdev-mirror', 'data': { '*job-id': 'str', 'device': 'str', 'target': 'str', @@ -2527,7 +2561,8 @@ '*on-target-error': 'BlockdevOnError', '*filter-node-name': 'str', '*copy-mode': 'MirrorCopyMode', - '*auto-finalize': 'bool', '*auto-dismiss': 'bool' }, + '*auto-finalize': 'bool', '*auto-dismiss': 'bool', + '*target-is-zero': 'bool'}, 'allow-preconfig': true } ## @@ -2553,11 +2588,11 @@ # # @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) @@ -2570,27 +2605,27 @@ # # @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) +# 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) +# 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) +# 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) +# 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) +# 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) +# as well. Defaults to 1. (Since 2.6) # # @iops_size: an I/O size in bytes (Since 1.7) # @@ -2627,7 +2662,7 @@ # @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 +# in seconds. It must only be set if @iops-total-max is set as # well. # # @iops-read: limit read operations per second @@ -2635,14 +2670,14 @@ # @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. +# 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 +# in seconds. It must only be set if @iops-write-max is set as # well. # # @bps-total: limit total bytes per second @@ -2657,14 +2692,14 @@ # @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. +# 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. +# 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 # @@ -2749,12 +2784,12 @@ # 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. +# job-cancel or 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 +# 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'. # @@ -2802,9 +2837,14 @@ # 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' +# @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) # @@ -2814,33 +2854,33 @@ # 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 +# after it has finished its work, waiting for @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) +# @job-dismiss. When true, this job will automatically disappear +# without user intervention. Defaults to true. (Since 3.1) # -# Returns: -# - Nothing on success. +# Errors: # - If @device does not exist, DeviceNotFound. # # Since: 1.1 # -# Example: +# .. qmp-example:: # -# -> { "execute": "block-stream", -# "arguments": { "device": "virtio0", -# "base": "/tmp/master.qcow2" } } -# <- { "return": {} } +# -> { "execute": "block-stream", +# "arguments": { "device": "virtio0", +# "base": "/tmp/master.qcow2" } } +# <- { "return": {} } ## { 'command': 'block-stream', 'data': { '*job-id': 'str', 'device': 'str', '*base': 'str', - '*base-node': 'str', '*backing-file': 'str', '*bottom': 'str', + '*base-node': 'str', '*backing-file': 'str', + '*backing-mask-protocol': 'bool', + '*bottom': 'str', '*speed': 'int', '*on-error': 'BlockdevOnError', '*filter-node-name': 'str', '*auto-finalize': 'bool', '*auto-dismiss': 'bool' }, @@ -2862,8 +2902,7 @@ # @speed: the maximum speed, in bytes per second, or 0 for unlimited. # Defaults to 0. # -# Returns: -# - Nothing on success +# Errors: # - If no background operation is active on this device, # DeviceNotActive # @@ -2907,8 +2946,7 @@ # paused) instead of waiting for the destination to complete its # final synchronization (since 1.3) # -# Returns: -# - Nothing on success +# Errors: # - If no background operation is active on this device, # DeviceNotActive # @@ -2922,26 +2960,32 @@ # # 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 job for +# pausing. Pausing an already paused job is an error. # -# 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 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. # # @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 +# Features: +# +# @deprecated: This command is deprecated. Use @job-pause +# instead. +# +# Errors: # - If no background operation is active on this device, # DeviceNotActive # # Since: 1.3 ## { 'command': 'block-job-pause', 'data': { 'device': 'str' }, + 'features': ['deprecated'], 'allow-preconfig': true } ## @@ -2949,9 +2993,8 @@ # # 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 job. +# Resuming an already running job is an error. # # This command also clears the error status of the job. # @@ -2959,23 +3002,33 @@ # the name of the parameter), but since QEMU 2.7 it can have other # values. # -# Returns: -# - Nothing on success +# Features: +# +# @deprecated: This command is deprecated. Use @job-resume +# instead. +# +# Errors: # - If no background operation is active on this device, # DeviceNotActive # # Since: 1.3 ## { 'command': 'block-job-resume', 'data': { 'device': 'str' }, + 'features': ['deprecated'], 'allow-preconfig': true } ## # @block-job-complete: # -# Manually trigger completion of an active background block operation. -# This is supported for drive mirroring, where it also switches the -# device to write to the target path only. The ability to complete is -# signaled with a BLOCK_JOB_READY event. +# Manually trigger completion of an active job in the READY or STANDBY +# state. Completing the job in any other state is an error. +# +# This is supported only for drive mirroring, where it also switches +# the device to write to the target path only. Note that drive +# mirroring includes drive-mirror, blockdev-mirror and block-commit +# job (only in case of "active commit", when the node being commited +# is used by the guest). 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 @@ -2985,63 +3038,119 @@ # 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. # -# Returns: -# - Nothing on success +# Features: +# +# @deprecated: This command is deprecated. Use @job-complete +# instead. +# +# Errors: # - If no background operation is active on this device, # DeviceNotActive # # Since: 1.3 ## { 'command': 'block-job-complete', 'data': { 'device': 'str' }, + 'features': ['deprecated'], 'allow-preconfig': true } ## # @block-job-dismiss: # -# For jobs that have already concluded, remove them from the -# block-job-query list. This command only needs to be run for jobs -# which were started with QEMU 2.12+ job lifetime management -# semantics. +# 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. In turn, automatic dismiss may be enabled only +# for jobs that have @auto-dismiss option, which are drive-backup, +# blockdev-backup, drive-mirror, blockdev-mirror, block-commit and +# block-stream. @auto-dismiss is enabled by default for these +# jobs. # # 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. +# reached its terminal state, CONCLUDED. For jobs that make use of +# the BLOCK_JOB_READY event, job-cancel, block-job-cancel or +# job-complete will still need to be used as appropriate. # # @id: The job identifier. # -# Returns: Nothing on success +# Features: +# +# @deprecated: This command is deprecated. Use @job-dismiss +# instead. # # Since: 2.12 ## { 'command': 'block-job-dismiss', 'data': { 'id': 'str' }, + 'features': ['deprecated'], 'allow-preconfig': true } ## # @block-job-finalize: # -# Once a job that has manual=true reaches the pending state, it can be -# instructed to finalize any graph changes and do any necessary -# cleanup via this command. For jobs in a transaction, instructing -# one job to finalize will force ALL jobs in the transaction to -# finalize, so it is only necessary to instruct a single member job to -# finalize. +# 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. +# +# The command is applicable only to jobs which have @auto-finalize option +# and only when this option is set to false. # # @id: The job identifier. # -# Returns: Nothing on success +# Features: +# +# @deprecated: This command is deprecated. Use @job-finalize +# instead. # # Since: 2.12 ## { 'command': 'block-job-finalize', 'data': { 'id': 'str' }, + 'features': ['deprecated'], '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: # @@ -3081,7 +3190,7 @@ # # Selects the AIO backend to handle I/O requests # -# @threads: Use qemu's thread pool +# @threads: Use QEMU's thread pool # # @native: Use native AIO backend (only Linux and Windows) # @@ -3130,12 +3239,18 @@ # # @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', @@ -3294,14 +3409,17 @@ # 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. +# decryption key. Mandatory except when doing a metadata-only +# probe of the image. (since 2.6) +# +# @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: @@ -3343,14 +3461,31 @@ # @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. +# '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' # -# @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 ## @@ -3396,6 +3531,8 @@ ## # @BlockdevQcowEncryption: # +# @format: encryption format +# # Since: 2.10 ## { 'union': 'BlockdevQcowEncryption', @@ -3430,6 +3567,8 @@ ## # @BlockdevQcow2Encryption: # +# @format: encryption format +# # Since: 2.10 ## { 'union': 'BlockdevQcow2Encryption', @@ -3473,16 +3612,20 @@ # @pass-discard-other: whether discard requests for the data source # should be issued on other occasions where a cluster gets freed # -# @discard-no-unref: when enabled, discards from the guest will not -# cause cluster allocations to be relinquished. This prevents -# qcow2 fragmentation that would be caused by such discards. -# 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) +# @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) # # @overlap-check: which overlap checks to perform for writes to the # image, defaults to 'cached' (since 2.2) @@ -3506,8 +3649,8 @@ # 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) +# 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 @@ -3576,6 +3719,8 @@ ## # @SshHostKeyCheck: # +# @mode: How to check the host key +# # Since: 2.12 ## { 'union': 'SshHostKeyCheck', @@ -3659,7 +3804,7 @@ # # Since: 4.1 ## -{ 'enum': 'BlkdebugIOType', 'prefix': 'BLKDEBUG_IO_TYPE', +{ 'enum': 'BlkdebugIOType', 'data': [ 'read', 'write', 'write-zeroes', 'discard', 'flush', 'block-status' ] } @@ -3963,6 +4108,7 @@ # @path: path to the vhost-vdpa character device. # # Features: +# # @fdset: Member @path supports the special "/dev/fdset/N" path # (since 8.1) # @@ -3998,6 +4144,8 @@ ## # @BlockdevOptionsIscsi: # +# Driver specific block device options for iscsi +# # @transport: The iscsi transport type # # @portal: The address of the iscsi portal @@ -4022,8 +4170,6 @@ # @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 -# # Since: 2.9 ## { 'struct': 'BlockdevOptionsIscsi', @@ -4075,7 +4221,7 @@ ## { 'struct': 'RbdEncryptionCreateOptionsLUKSBase', 'base': 'RbdEncryptionOptionsLUKSBase', - 'data': { '*cipher-alg': 'QCryptoCipherAlgorithm' } } + 'data': { '*cipher-alg': 'QCryptoCipherAlgo' } } ## # @RbdEncryptionOptionsLUKS: @@ -4145,6 +4291,8 @@ ## # @RbdEncryptionCreateOptions: # +# @format: Encryption format. +# # Since: 6.1 ## { 'union': 'RbdEncryptionCreateOptions', @@ -4172,8 +4320,8 @@ # @user: Ceph id name. # # @auth-client-required: Acceptable authentication modes. This maps -# to Ceph configuration option "auth_client_required". (Since -# 3.0) +# 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". @@ -4338,7 +4486,7 @@ # 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 +# name2=content2;" as explained by CURLOPT_COOKIE(3). Defaults to # no cookies. # # @cookie-secret: ID of a QCryptoSecret object providing the cookie @@ -4358,7 +4506,7 @@ # 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 +# name2=content2;" as explained by CURLOPT_COOKIE(3). Defaults to # no cookies. # # @sslverify: Whether to verify the SSL certificate's validity @@ -4427,8 +4575,8 @@ # 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) +# 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. @@ -4547,14 +4695,20 @@ # 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) +# 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: @@ -4564,18 +4718,23 @@ # # @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. +# @node-name: the node name of the new node. 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. (Since 2.0) # # @discard: discard-related options (default: ignore) # # @cache: cache-related options # +# @active: whether the block node should be activated (default: true). +# Having inactive block nodes is useful primarily for migration because it +# allows opening an image on the destination while the source is still +# holding locks for it. (Since 10.0) +# # @read-only: whether the block device should be read-only (default: -# false). Note that some block drivers support only read-only +# 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. @@ -4593,8 +4752,6 @@ # @force-share: force share all permission on added nodes. Requires # read-only=true. (Since 2.10) # -# Remaining options are determined by the block driver. -# # Since: 2.9 ## { 'union': 'BlockdevOptions', @@ -4602,6 +4759,7 @@ '*node-name': 'str', '*discard': 'BlockdevDiscardOptions', '*cache': 'BlockdevCacheOptions', + '*active': 'bool', '*read-only': 'bool', '*auto-read-only': 'bool', '*force-share': 'bool', @@ -4710,43 +4868,45 @@ # # Since: 2.9 # -# Examples: +# .. qmp-example:: # -# -> { "execute": "blockdev-add", -# "arguments": { -# "driver": "qcow2", -# "node-name": "test1", -# "file": { -# "driver": "file", -# "filename": "test.qcow2" -# } -# } -# } -# <- { "return": {} } -# -# -> { "execute": "blockdev-add", -# "arguments": { -# "driver": "qcow2", -# "node-name": "node0", -# "discard": "unmap", -# "cache": { -# "direct": true -# }, -# "file": { -# "driver": "file", -# "filename": "/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 } @@ -4781,7 +4941,7 @@ # 3) A reference to a different node: the current child is replaced # with the specified one. # -# 4) NULL: the current child (if any) is detached. +# 4) null: the current child (if any) is detached. # # Options (1) and (2) are supported in all cases. Option (3) is # supported for @file and @backing, and option (4) for @backing only. @@ -4808,28 +4968,60 @@ # # Since: 2.9 # -# Example: +# .. qmp-example:: # -# -> { "execute": "blockdev-add", -# "arguments": { -# "driver": "qcow2", -# "node-name": "node0", -# "file": { -# "driver": "file", -# "filename": "test.qcow2" -# } -# } -# } -# <- { "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": {} } +# -> { "execute": "blockdev-del", +# "arguments": { "node-name": "node0" } +# } +# <- { "return": {} } ## { 'command': 'blockdev-del', 'data': { 'node-name': 'str' }, 'allow-preconfig': true } +## +# @blockdev-set-active: +# +# Activate or inactivate a block device. Use this to manage the handover of +# block devices on migration with qemu-storage-daemon. +# +# Activating a node automatically activates all of its child nodes first. +# Inactivating a node automatically inactivates any of its child nodes that are +# not in use by a still active node. +# +# @node-name: Name of the graph node to activate or inactivate. By default, all +# nodes are affected by the operation. +# +# @active: true if the nodes should be active when the command returns success, +# false if they should be inactive. +# +# Since: 10.0 +# +# .. qmp-example:: +# +# -> { "execute": "blockdev-set-active", +# "arguments": { +# "node-name": "node0", +# "active": false +# } +# } +# <- { "return": {} } +## +{ 'command': 'blockdev-set-active', + 'data': { '*node-name': 'str', 'active': 'bool' }, + 'allow-preconfig': true } + ## # @BlockdevCreateOptionsFile: # @@ -4882,7 +5074,10 @@ # # 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 # @@ -4893,7 +5088,8 @@ ## { 'struct': 'BlockdevCreateOptionsLUKS', 'base': 'QCryptoBlockCreateOptionsLUKS', - 'data': { 'file': 'BlockdevRef', + 'data': { '*file': 'BlockdevRef', + '*header': 'BlockdevRef', 'size': 'size', '*preallocation': 'PreallocMode' } } @@ -4955,10 +5151,10 @@ ## # @BlockdevQcow2Version: # -# @v2: The original QCOW2 format as introduced in qemu 0.10 (version +# @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) +# @v3: The extended QCOW2 format as introduced in QEMU 1.1 (version 3) # # Since: 2.12 ## @@ -5138,8 +5334,8 @@ # 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. +# 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". @@ -5151,7 +5347,7 @@ # Default: ide. # # @hwversion: Hardware version. The meaningful options are "4" or -# "6". Default: "4". +# "6". Default: "4". # # @toolsversion: VMware guest tools version. Default: "2147483647" # (Since 6.2) @@ -5347,7 +5543,7 @@ ## # @BlockdevAmendOptionsQcow2: # -# Driver specific image amend options for qcow2. For now, only +# Driver specific image amend options for qcow2. For now, only # encryption options can be amended # # @encrypt: Encryption options to be amended @@ -5378,7 +5574,7 @@ # @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 +# block device. The job is automatically finalized, but a manual # job-dismiss is required. # # @job-id: Identifier for the newly created job. @@ -5387,7 +5583,7 @@ # # @options: Options (driver specific) # -# @force: Allow unsafe operations, format specific For luks that +# @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) @@ -5450,15 +5646,15 @@ # 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 ## @@ -5475,6 +5671,8 @@ # # Emitted when a disk I/O error occurs # +# @qom-path: path to the device object in the QOM tree (since 9.2) +# # @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. @@ -5497,23 +5695,26 @@ # 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: +# .. qmp-example:: # -# <- { "event": "BLOCK_IO_ERROR", -# "data": { "device": "ide0-hd1", -# "node-name": "#block212", -# "operation": "write", -# "action": "stop", -# "reason": "No space left on device" }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +# <- { "event": "BLOCK_IO_ERROR", +# "data": { "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' } } @@ -5542,13 +5743,13 @@ # # Since: 1.1 # -# Example: +# .. 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": "stream", "device": "virtio-disk0", +# "len": 10737418240, "offset": 10737418240, +# "speed": 0 }, +# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } ## { 'event': 'BLOCK_JOB_COMPLETED', 'data': { 'type' : 'JobType', @@ -5577,13 +5778,13 @@ # # Since: 1.1 # -# Example: +# .. 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": "stream", "device": "virtio-disk0", +# "len": 10737418240, "offset": 134217728, +# "speed": 0 }, +# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } ## { 'event': 'BLOCK_JOB_CANCELLED', 'data': { 'type' : 'JobType', @@ -5606,13 +5807,13 @@ # # Since: 1.3 # -# Example: +# .. 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": "ide0-hd1", +# "operation": "write", +# "action": "stop" }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'BLOCK_JOB_ERROR', 'data': { 'device' : 'str', @@ -5636,17 +5837,17 @@ # # @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: +# .. 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": { "device": "drive0", "type": "mirror", "speed": 0, +# "len": 2097152, "offset": 2097152 }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'BLOCK_JOB_READY', 'data': { 'type' : 'JobType', @@ -5659,7 +5860,7 @@ # @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 +# finalize graph changes via @job-finalize. If this job is part # of a transaction, it will not emit this event until the transaction # has converged first. # @@ -5669,11 +5870,11 @@ # # Since: 2.12 # -# Example: +# .. qmp-example:: # -# <- { "event": "BLOCK_JOB_PENDING", -# "data": { "type": "mirror", "id": "backup_1" }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +# <- { "event": "BLOCK_JOB_PENDING", +# "data": { "type": "mirror", "id": "backup_1" }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'BLOCK_JOB_PENDING', 'data': { 'type' : 'JobType', @@ -5743,12 +5944,12 @@ # # Since: 2.3 # -# Example: +# .. qmp-example:: # -# -> { "execute": "block-set-write-threshold", -# "arguments": { "node-name": "mydev", -# "write-threshold": 17179869184 } } -# <- { "return": {} } +# -> { "execute": "block-set-write-threshold", +# "arguments": { "node-name": "mydev", +# "write-threshold": 17179869184 } } +# <- { "return": {} } ## { 'command': 'block-set-write-threshold', 'data': { 'node-name': 'str', 'write-threshold': 'uint64' }, @@ -5757,59 +5958,55 @@ ## # @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. # -# 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. +# Currently only supports adding and deleting quorum children. A +# child will be added at the end of the list of children. Its +# contents *must* be consistent with the other childrens' contents. +# Deleting a child that is not last in the list of children is +# problematic, because it "renumbers" the children following it. # # @parent: the id or name of the parent node. # -# @child: the name of a child under the given parent node. +# @child: the name of a child to be deleted. Mutually exclusive with +# @node. # -# @node: the name of the node that will be added. +# @node: the name of the node to be added. Mutually exclusive with +# @child. # # 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. +# @unstable: This command is experimental. # -# FIXME Removing children from a quorum node means introducing +# TODO: 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 # -# Examples: -# -# 1. Add a new node to a quorum -# -# -> { "execute": "blockdev-add", -# "arguments": { -# "driver": "raw", -# "node-name": "new_node", -# "file": { "driver": "file", -# "filename": "test.raw" } } } -# <- { "return": {} } -# -> { "execute": "x-blockdev-change", -# "arguments": { "parent": "disk1", -# "node": "new_node" } } -# <- { "return": {} } -# -# 2. Delete a quorum's node -# -# -> { "execute": "x-blockdev-change", -# "arguments": { "parent": "disk1", -# "child": "children.1" } } -# <- { "return": {} } +# .. qmp-example:: +# :title: 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": {} } +# +# .. 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', @@ -5840,21 +6037,21 @@ # # Since: 2.12 # -# Examples: +# .. 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": {} } +# .. qmp-example:: +# :title: Move a node into the main loop # -# 2. Move a node into the main loop -# -# -> { "execute": "x-blockdev-set-iothread", -# "arguments": { "node-name": "disk1", -# "iothread": null } } -# <- { "return": {} } +# -> { "execute": "x-blockdev-set-iothread", +# "arguments": { "node-name": "disk1", +# "iothread": null } } +# <- { "return": {} } ## { 'command': 'x-blockdev-set-iothread', 'data' : { 'node-name': 'str', @@ -5890,15 +6087,15 @@ # # @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:: # -# <- { "event": "QUORUM_FAILURE", -# "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 }, -# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } +# <- { "event": "QUORUM_FAILURE", +# "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 }, +# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } ## { 'event': 'QUORUM_FAILURE', 'data': { 'reference': 'str', 'sector-num': 'int', 'sectors-count': 'int' } } @@ -5921,25 +6118,25 @@ # # @sectors-count: failed read operation sector count # -# Note: This event is rate-limited. +# .. note:: This event is rate-limited. # # Since: 2.0 # -# Examples: -# -# 1. Read operation +# .. qmp-example:: +# :title: 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 +# .. 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": { "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', @@ -5953,10 +6150,6 @@ # # @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', @@ -5970,26 +6163,25 @@ # string, or a snapshot with name already exists, the operation will # fail. # -# For the arguments, see the documentation of -# BlockdevSnapshotInternal. -# -# Returns: -# - nothing on success +# 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 # +# .. note:: Only some image formats such as qcow2 and rbd support +# internal snapshots. +# # Since: 1.7 # -# Example: +# .. qmp-example:: # -# -> { "execute": "blockdev-snapshot-internal-sync", -# "arguments": { "device": "ide-hd0", -# "name": "snapshot0" } -# } -# <- { "return": {} } +# -> { "execute": "blockdev-snapshot-internal-sync", +# "arguments": { "device": "ide-hd0", +# "name": "snapshot0" } +# } +# <- { "return": {} } ## { 'command': 'blockdev-snapshot-internal-sync', 'data': 'BlockdevSnapshotInternal', @@ -6011,7 +6203,9 @@ # @name: optional the snapshot's name to be deleted # # Returns: -# - SnapshotInfo on success +# 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, @@ -6020,23 +6214,23 @@ # # 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'}, diff --git a/qapi/block-export.json b/qapi/block-export.json index 7874a49ba71..ed4deb54db2 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -9,13 +9,11 @@ { 'include': 'block-core.json' } ## -# @NbdServerOptions: -# -# Keep this type consistent with the nbd-server-start arguments. The -# only intended difference is using SocketAddress instead of -# SocketAddressLegacy. +# @NbdServerOptionsBase: # -# @addr: Address on which to listen. +# @handshake-max-seconds: Time limit, in seconds, at which a client +# that has not completed the negotiation handshake will be +# disconnected, or 0 for no limit (since 10.0; default: 10). # # @tls-creds: ID of the TLS credentials object (since 2.6). # @@ -28,52 +26,55 @@ # @max-connections: The maximum number of connections to allow at the # same time, 0 for unlimited. Setting this to 1 also stops the # server from advertising multiple client support (since 5.2; -# default: 0) -# -# Since: 4.2 +# default: 100). ## -{ 'struct': 'NbdServerOptions', - 'data': { 'addr': 'SocketAddress', +{ 'struct': 'NbdServerOptionsBase', + 'data': { '*handshake-max-seconds': 'uint32', '*tls-creds': 'str', '*tls-authz': 'str', '*max-connections': 'uint32' } } ## -# @nbd-server-start: +# @NbdServerOptions: # -# 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". +# Keep this type consistent with the NbdServerOptionsLegacy type. The +# only intended difference is using SocketAddress instead of +# SocketAddressLegacy. +# +# @addr: Address on which to listen (since 4.2). +## +{ 'struct': 'NbdServerOptions', + 'base': 'NbdServerOptionsBase', + 'data': { 'addr': 'SocketAddress' } } + +## +# @NbdServerOptionsLegacy: # # 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). +# @addr: Address on which to listen (since 1.3). +## +{ 'struct': 'NbdServerOptionsLegacy', + 'base': 'NbdServerOptionsBase', + 'data': { 'addr': 'SocketAddressLegacy' } } + +## +# @nbd-server-start: # -# @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). +# 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". # -# Returns: error if the server is already running. +# Errors: +# - if the server is already running # # Since: 1.3 ## { 'command': 'nbd-server-start', - 'data': { 'addr': 'SocketAddressLegacy', - '*tls-creds': 'str', - '*tls-authz': 'str', - '*max-connections': 'uint32' }, + 'data': 'NbdServerOptionsLegacy', 'allow-preconfig': true } ## @@ -162,16 +163,16 @@ # Options for exporting a block graph node on some (file) mountpoint # as a raw image. # -# @mountpoint: Path on which to export the block device via FUSE. This -# must point to an existing regular file. +# @mountpoint: Path on which to export the block device via FUSE. +# This must point to an existing regular file. # # @growable: Whether writes beyond the EOF should grow the block node # accordingly. (default: false) # -# @allow-other: If this is off, only qemu's user is allowed access to +# @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 +# 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 @@ -198,7 +199,7 @@ # @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. +# PAGE_SIZE] and must be power of 2. Defaults to 512 bytes. # # @serial: the serial number of virtio block device. Defaults to # empty string. @@ -247,8 +248,9 @@ # @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. +# Errors: +# - if the server is not running +# - if an export with the same name already exists # # Since: 1.3 ## @@ -266,13 +268,14 @@ # # @hard: Drop all connections immediately and remove export. # -# Potential additional modes to be added in the future: +# 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 ## @@ -293,11 +296,10 @@ # @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 +# Errors: +# - if the server is not running +# - if export is not found +# - if mode is 'safe' and there are existing connections # # Since: 2.12 ## @@ -345,6 +347,8 @@ # Describes a block export, i.e. how single node should be exported on # an external interface. # +# @type: Block export type +# # @id: A unique identifier for the block export (across all export # types) # @@ -368,6 +372,13 @@ # cannot be moved to the iothread. The default is false. # (since: 5.2) # +# @allow-inactive: If true, the export allows the exported node to be inactive. +# If it is created for an inactive block node, the node remains inactive. If +# the export type doesn't support running on an inactive node, an error is +# returned. If false, inactive block nodes are automatically activated before +# creating the export and trying to inactivate them later fails. +# (since: 10.0; default: false) +# # Since: 4.2 ## { 'union': 'BlockExportOptions', @@ -377,7 +388,8 @@ '*iothread': 'str', 'node-name': 'str', '*writable': 'bool', - '*writethrough': 'bool' }, + '*writethrough': 'bool', + '*allow-inactive': 'bool' }, 'discriminator': 'type', 'data': { 'nbd': 'BlockExportOptionsNbd', @@ -412,8 +424,10 @@ # @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 ## diff --git a/qapi/block.json b/qapi/block.json index 998008cfa8f..1490a1a17f8 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -48,7 +48,7 @@ ## # @FloppyDriveType: # -# Type of Floppy drive to be emulated by the Floppy Disk Controller. +# Type of floppy drive to be emulated by the Floppy Disk Controller. # # @144: 1.44MB 3.5" drive # @@ -83,7 +83,7 @@ ## # @query-pr-managers: # -# Returns a list of information about each persistent reservation +# Return a list of information about each persistent reservation # manager. # # Returns: a list of @PRManagerInfo for each persistent reservation @@ -110,18 +110,17 @@ # # @deprecated: Member @device is deprecated. Use @id instead. # -# Returns: -# - Nothing on success +# 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' ] }, @@ -162,19 +161,19 @@ # # 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 } } +# <- { "timestamp": { "seconds": 1418751016, +# "microseconds": 716996 }, +# "event": "DEVICE_TRAY_MOVED", +# "data": { "device": "ide1-cd0", +# "id": "ide0-1-0", +# "tray-open": true } } # -# <- { "return": {} } +# <- { "return": {} } ## { 'command': 'blockdev-open-tray', 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, @@ -200,19 +199,19 @@ # # 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 } } +# <- { "timestamp": { "seconds": 1418751345, +# "microseconds": 272147 }, +# "event": "DEVICE_TRAY_MOVED", +# "data": { "device": "ide1-cd0", +# "id": "ide0-1-0", +# "tray-open": false } } # -# <- { "return": {} } +# <- { "return": {} } ## { 'command': 'blockdev-close-tray', 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, @@ -232,30 +231,30 @@ # # 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" } } +# -> { "execute": "blockdev-remove-medium", +# "arguments": { "id": "ide0-1-0" } } # -# <- { "return": {} } +# <- { "return": {} } ## { 'command': 'blockdev-remove-medium', 'data': { 'id': 'str' } } @@ -273,21 +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" } } +# -> { "execute": "blockdev-insert-medium", +# "arguments": { "id": "ide0-1-0", +# "node-name": "node0" } } # -# <- { "return": {} } +# <- { "return": {} } ## { 'command': 'blockdev-insert-medium', 'data': { 'id': 'str', @@ -343,35 +342,35 @@ # # 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" } } # -# -> { "execute": "blockdev-change-medium", -# "arguments": { "id": "floppyA", -# "filename": "/srv/images/ro.img", -# "format": "raw", -# "read-only-mode": "read-only" } } -# -# <- { "return": {} } +# <- { "return": {} } ## { 'command': 'blockdev-change-medium', 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, @@ -398,14 +397,14 @@ # # Since: 1.1 # -# Example: +# .. 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": "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' } } @@ -422,13 +421,13 @@ # # Since: 3.0 # -# Example: +# .. 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": "pr-helper0", +# "connected": true +# }, +# "timestamp": { "seconds": 1519840375, "microseconds": 450486 } } ## { 'event': 'PR_MANAGER_STATUS_CHANGED', 'data': { 'id': 'str', 'connected': 'bool' } } @@ -455,53 +454,54 @@ # 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 +# 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 +# Errors: # - If @device is not a valid block device, DeviceNotFound # # Since: 1.1 # -# Examples: -# -# -> { "execute": "block_set_io_throttle", -# "arguments": { "id": "virtio-blk-pci0/virtio-backend", -# "bps": 0, -# "bps_rd": 0, -# "bps_wr": 0, -# "iops": 512, -# "iops_rd": 0, -# "iops_wr": 0, -# "bps_max": 0, -# "bps_rd_max": 0, -# "bps_wr_max": 0, -# "iops_max": 0, -# "iops_rd_max": 0, -# "iops_wr_max": 0, -# "bps_max_length": 0, -# "iops_size": 0 } } -# <- { "return": {} } -# -# -> { "execute": "block_set_io_throttle", -# "arguments": { "id": "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": "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": {} } +# +# .. 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', @@ -519,10 +519,10 @@ # @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 +# 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 @@ -540,50 +540,57 @@ # @boundaries-flush: list of interval boundary values for flush # latency histogram. # -# Returns: error if device is not found or any boundary arrays are -# invalid. +# Errors: +# - if device is not found or any boundary arrays are invalid. # # Since: 4.0 # -# Example: +# .. qmp-example:: +# :annotated: +# +# Set new histograms for all io types with intervals +# [0, 10), [10, 50), [50, 100), [100, +inf):: +# +# -> { "execute": "block-latency-histogram-set", +# "arguments": { "id": "drive0", +# "boundaries": [10, 50, 100] } } +# <- { "return": {} } # -# Set new histograms for all io types with intervals [0, 10), [10, -# 50), [50, 100), [100, +inf): +# .. qmp-example:: +# :annotated: # -# -> { "execute": "block-latency-histogram-set", -# "arguments": { "id": "drive0", -# "boundaries": [10, 50, 100] } } -# <- { "return": {} } +# Set new histogram only for write, other histograms will remain +# not changed (or not created):: # -# Example: +# -> { "execute": "block-latency-histogram-set", +# "arguments": { "id": "drive0", +# "boundaries-write": [10, 50, 100] } } +# <- { "return": {} } # -# Set new histogram only for write, other histograms will remain not -# changed (or not created): +# .. qmp-example:: +# :annotated: # -# -> { "execute": "block-latency-histogram-set", -# "arguments": { "id": "drive0", -# "boundaries-write": [10, 50, 100] } } -# <- { "return": {} } +# Set new histograms with the following intervals: # -# Example: +# - read, flush: [0, 10), [10, 50), [50, 100), [100, +inf) +# - write: [0, 1000), [1000, 5000), [5000, +inf) # -# 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": {} } +# -> { "execute": "block-latency-histogram-set", +# "arguments": { "id": "drive0", +# "boundaries": [10, 50, 100], +# "boundaries-write": [1000, 5000] } } +# <- { "return": {} } # -# Example: +# .. qmp-example:: +# :annotated: # -# Remove all latency histograms: +# Remove all latency histograms:: # -# -> { "execute": "block-latency-histogram-set", -# "arguments": { "id": "drive0" } } -# <- { "return": {} } +# -> { "execute": "block-latency-histogram-set", +# "arguments": { "id": "drive0" } } +# <- { "return": {} } ## { 'command': 'block-latency-histogram-set', 'data': {'id': 'str', diff --git a/qapi/char.json b/qapi/char.json index c1bab7b8550..df6e325e2e1 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -21,8 +21,8 @@ # 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 ## @@ -34,34 +34,34 @@ ## # @query-chardev: # -# Returns information about current character devices. +# Return information about current character devices. # # Returns: a list of @ChardevInfo # # 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 } @@ -80,31 +80,31 @@ ## # @query-chardev-backends: # -# Returns information about character device backends. +# Return information about character device backends. # # Returns: a list of @ChardevBackendInfo # # 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'] } @@ -139,17 +139,15 @@ # - data itself is always Unicode regardless of format, like any # other string. # -# Returns: Nothing on success -# # Since: 1.4 # -# Example: +# .. qmp-example:: # -# -> { "execute": "ringbuf-write", -# "arguments": { "device": "foo", -# "data": "abcdefgh", -# "format": "utf8" } } -# <- { "return": {} } +# -> { "execute": "ringbuf-write", +# "arguments": { "device": "foo", +# "data": "abcdefgh", +# "format": "utf8" } } +# <- { "return": {} } ## { 'command': 'ringbuf-write', 'data': { 'device': 'str', @@ -179,13 +177,13 @@ # # Since: 1.4 # -# Example: +# .. qmp-example:: # -# -> { "execute": "ringbuf-read", -# "arguments": { "device": "foo", -# "size": 1000, -# "format": "utf8" } } -# <- { "return": "abcdefgh" } +# -> { "execute": "ringbuf-read", +# "arguments": { "device": "foo", +# "size": 1000, +# "format": "utf8" } } +# <- { "return": "abcdefgh" } ## { 'command': 'ringbuf-read', 'data': {'device': 'str', 'size': 'int', '*format': 'DataFormat'}, @@ -260,7 +258,7 @@ # @server: create server socket (default: true) # # @wait: wait for incoming connection on server sockets (default: -# false). Silently ignored with server: false. This use is +# false). Silently ignored with server: false. This use is # deprecated. # # @nodelay: set TCP_NODELAY socket option (default: false) @@ -275,7 +273,19 @@ # # @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) +# 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 ## @@ -289,7 +299,8 @@ '*telnet': 'bool', '*tn3270': 'bool', '*websocket': 'bool', - '*reconnect': 'int' }, + '*reconnect': { 'type': 'int', 'features': [ 'deprecated' ] }, + '*reconnect-ms': 'int' }, 'base': 'ChardevCommon' } ## @@ -321,13 +332,26 @@ 'data': { 'chardev': 'str' }, 'base': 'ChardevCommon' } +## +# @ChardevHub: +# +# Configuration info for hub chardevs. +# +# @chardevs: IDs to be added to this hub (maximum 4 devices). +# +# Since: 10.0 +## +{ 'struct': 'ChardevHub', + 'data': { 'chardevs': ['str'] }, + 'base': 'ChardevCommon' } + ## # @ChardevStdio: # # Configuration info for stdio chardevs. # # @signal: Allow signals (such as SIGINT triggered by ^C) be delivered -# to qemu. Default: true. +# to QEMU. Default: true. # # Since: 1.5 ## @@ -390,9 +414,9 @@ # # @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. +# .. 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 ## @@ -419,7 +443,7 @@ ## # @ChardevQemuVDAgent: # -# Configuration info for qemu vdagent implementation. +# Configuration info for QEMU vdagent implementation. # # @mouse: enable/disable mouse, default is enabled. # @@ -433,71 +457,105 @@ '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 +# @file: regular files +# +# @serial: serial host device +# +# @parallel: parallel host device +# +# @pipe: pipes (since 1.5) # -# @udp: Since 1.5 +# @socket: stream socket # -# @mux: Since 1.5 +# @udp: datagram socket (since 1.5) # -# @msmouse: Since 1.5 +# @pty: pseudo-terminal # -# @wctablet: Since 2.9 +# @null: provides no input, throws away output # -# @braille: Since 1.5 +# @mux: (since 1.5) # -# @testdev: Since 2.2 +# @hub: (since 10.0) # -# @stdio: Since 1.5 +# @msmouse: emulated Microsoft serial mouse (since 1.5) # -# @console: Since 1.5 +# @wctablet: emulated Wacom Penpartner serial tablet (since 2.9) # -# @spicevmc: Since 1.5 +# @braille: Baum Braille device (since 1.5) # -# @spiceport: Since 1.5 +# @testdev: device for test-suite control (since 2.2) # -# @qemu-vdagent: Since 6.1 +# @stdio: standard I/O (since 1.5) # -# @dbus: Since 7.0 +# @console: Windows console (since 1.5) # -# @vc: v1.5 +# @spicevmc: spice vm channel (since 1.5) # -# @ringbuf: Since 1.6 +# @spiceport: Spice port channel (since 1.5) # -# @memory: 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', 'pty', 'null', 'mux', + 'hub', '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', @@ -506,6 +564,8 @@ ## # @ChardevHostdevWrapper: # +# @data: Configuration info for device and pipe chardevs +# # Since: 1.4 ## { 'struct': 'ChardevHostdevWrapper', @@ -514,6 +574,8 @@ ## # @ChardevSocketWrapper: # +# @data: Configuration info for (stream) socket chardevs +# # Since: 1.4 ## { 'struct': 'ChardevSocketWrapper', @@ -522,6 +584,8 @@ ## # @ChardevUdpWrapper: # +# @data: Configuration info for datagram socket chardevs +# # Since: 1.5 ## { 'struct': 'ChardevUdpWrapper', @@ -530,6 +594,8 @@ ## # @ChardevCommonWrapper: # +# @data: Configuration shared across all chardev backends +# # Since: 2.6 ## { 'struct': 'ChardevCommonWrapper', @@ -538,14 +604,28 @@ ## # @ChardevMuxWrapper: # +# @data: Configuration info for mux chardevs +# # Since: 1.5 ## { 'struct': 'ChardevMuxWrapper', 'data': { 'data': 'ChardevMux' } } +## +# @ChardevHubWrapper: +# +# @data: Configuration info for hub chardevs +# +# Since: 10.0 +## +{ 'struct': 'ChardevHubWrapper', + 'data': { 'data': 'ChardevHub' } } + ## # @ChardevStdioWrapper: # +# @data: Configuration info for stdio chardevs +# # Since: 1.5 ## { 'struct': 'ChardevStdioWrapper', @@ -554,6 +634,8 @@ ## # @ChardevSpiceChannelWrapper: # +# @data: Configuration info for spice vm channel chardevs +# # Since: 1.5 ## { 'struct': 'ChardevSpiceChannelWrapper', @@ -563,6 +645,8 @@ ## # @ChardevSpicePortWrapper: # +# @data: Configuration info for spice port chardevs +# # Since: 1.5 ## { 'struct': 'ChardevSpicePortWrapper', @@ -572,6 +656,8 @@ ## # @ChardevQemuVDAgentWrapper: # +# @data: Configuration info for QEMU vdagent implementation +# # Since: 6.1 ## { 'struct': 'ChardevQemuVDAgentWrapper', @@ -581,6 +667,8 @@ ## # @ChardevDBusWrapper: # +# @data: Configuration info for DBus chardevs +# # Since: 7.0 ## { 'struct': 'ChardevDBusWrapper', @@ -590,6 +678,8 @@ ## # @ChardevVCWrapper: # +# @data: Configuration info for virtual console chardevs +# # Since: 1.5 ## { 'struct': 'ChardevVCWrapper', @@ -598,36 +688,56 @@ ## # @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', + 'hub': 'ChardevHubWrapper', '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', @@ -638,7 +748,6 @@ 'if': 'CONFIG_DBUS_DISPLAY' }, 'vc': 'ChardevVCWrapper', 'ringbuf': 'ChardevRingbufWrapper', - # next one is just for compatibility 'memory': 'ChardevRingbufWrapper' } } ## @@ -667,23 +776,27 @@ # # Since: 1.4 # -# Examples: +# .. qmp-example:: +# +# -> { "execute" : "chardev-add", +# "arguments" : { "id" : "foo", +# "backend" : { "type" : "null", "data" : {} } } } +# <- { "return": {} } # -# -> { "execute" : "chardev-add", -# "arguments" : { "id" : "foo", -# "backend" : { "type" : "null", "data" : {} } } } -# <- { "return": {} } +# .. qmp-example:: # -# -> { "execute" : "chardev-add", -# "arguments" : { "id" : "bar", -# "backend" : { "type" : "file", -# "data" : { "out" : "/tmp/bar.log" } } } } -# <- { "return": {} } +# -> { "execute" : "chardev-add", +# "arguments" : { "id" : "bar", +# "backend" : { "type" : "file", +# "data" : { "out" : "/tmp/bar.log" } } } } +# <- { "return": {} } # -# -> { "execute" : "chardev-add", -# "arguments" : { "id" : "baz", -# "backend" : { "type" : "pty", "data" : {} } } } -# <- { "return": { "pty" : "/dev/pty/42" } } +# .. qmp-example:: +# +# -> { "execute" : "chardev-add", +# "arguments" : { "id" : "baz", +# "backend" : { "type" : "pty", "data" : {} } } } +# <- { "return": { "pty" : "/dev/pty/42" } } ## { 'command': 'chardev-add', 'data': { 'id': 'str', @@ -703,28 +816,30 @@ # # Since: 2.10 # -# Examples: -# -# -> { "execute" : "chardev-change", -# "arguments" : { "id" : "baz", -# "backend" : { "type" : "pty", "data" : {} } } } -# <- { "return": { "pty" : "/dev/pty/42" } } -# -# -> {"execute" : "chardev-change", -# "arguments" : { -# "id" : "charchannel2", -# "backend" : { -# "type" : "socket", -# "data" : { -# "addr" : { -# "type" : "unix" , -# "data" : { -# "path" : "/tmp/charchannel2.socket" -# } -# }, -# "server" : true, -# "wait" : false }}}} -# <- {"return": {}} +# .. qmp-example:: +# +# -> { "execute" : "chardev-change", +# "arguments" : { "id" : "baz", +# "backend" : { "type" : "pty", "data" : {} } } } +# <- { "return": { "pty" : "/dev/pty/42" } } +# +# .. 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', @@ -738,14 +853,12 @@ # # @id: the chardev's ID, must exist and not be in use # -# Returns: Nothing on success -# # Since: 1.4 # -# Example: +# .. qmp-example:: # -# -> { "execute": "chardev-remove", "arguments": { "id" : "foo" } } -# <- { "return": {} } +# -> { "execute": "chardev-remove", "arguments": { "id" : "foo" } } +# <- { "return": {} } ## { 'command': 'chardev-remove', 'data': { 'id': 'str' } } @@ -757,14 +870,12 @@ # # @id: the chardev's ID, must exist # -# Returns: Nothing on success -# # Since: 2.10 # -# Example: +# .. qmp-example:: # -# -> { "execute": "chardev-send-break", "arguments": { "id" : "foo" } } -# <- { "return": {} } +# -> { "execute": "chardev-send-break", "arguments": { "id" : "foo" } } +# <- { "return": {} } ## { 'command': 'chardev-send-break', 'data': { 'id': 'str' } } @@ -778,15 +889,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: +# .. qmp-example:: # -# <- { "event": "VSERPORT_CHANGE", -# "data": { "id": "channel0", "open": true }, -# "timestamp": { "seconds": 1401385907, "microseconds": 422329 } } +# <- { "event": "VSERPORT_CHANGE", +# "data": { "id": "channel0", "open": true }, +# "timestamp": { "seconds": 1401385907, "microseconds": 422329 } } ## { 'event': 'VSERPORT_CHANGE', 'data': { 'id': 'str', diff --git a/qapi/common.json b/qapi/common.json index 6fed9cde1a9..0e3a0bbbfb0 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: # @@ -118,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: @@ -190,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 ## @@ -207,3 +212,17 @@ ## { 'struct': 'HumanReadableText', 'data': { 'human-readable-text': 'str' } } + +## +# @EndianMode: +# +# @unspecified: Endianness not specified +# +# @little: Little endianness +# +# @big: Big endianness +# +# Since: 10.0 +## +{ 'enum': 'EndianMode', + 'data': [ 'unspecified', 'little', 'big' ] } diff --git a/qapi/compat.json b/qapi/compat.json index f4c19837eb5..42034d9368c 100644 --- a/qapi/compat.json +++ b/qapi/compat.json @@ -43,8 +43,8 @@ # 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. diff --git a/qapi/control.json b/qapi/control.json index a91fa334079..34b733f63b6 100644 --- a/qapi/control.json +++ b/qapi/control.json @@ -11,27 +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) # -# 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.rst) +# .. 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 ## @@ -93,26 +91,26 @@ ## # @query-version: # -# Returns the current version of QEMU. +# Return the current version of QEMU. # # Returns: A @VersionInfo object describing the current version of # QEMU. # # Since: 0.14 # -# Example: +# .. qmp-example:: # -# -> { "execute": "query-version" } -# <- { -# "return":{ -# "qemu":{ -# "major":0, -# "minor":11, -# "micro":5 -# }, -# "package":"" -# } -# } +# -> { "execute": "query-version" } +# <- { +# "return":{ +# "qemu":{ +# "major":0, +# "minor":11, +# "micro":5 +# }, +# "package":"" +# } +# } ## { 'command': 'query-version', 'returns': 'VersionInfo', 'allow-preconfig': true } @@ -137,22 +135,22 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # -# -> { "execute": "query-commands" } -# <- { -# "return":[ -# { -# "name":"query-balloon" -# }, -# { -# "name":"system_powerdown" -# } -# ] -# } +# -> { "execute": "query-commands" } +# <- { +# "return":[ +# { +# "name":"query-balloon" +# }, +# { +# "name":"system_powerdown" +# }, +# ... +# ] +# } # -# Note: This example has been shortened as the real response is too -# long. +# This example has been shortened as the real response is too long. ## { 'command': 'query-commands', 'returns': ['CommandInfo'], 'allow-preconfig': true } @@ -167,10 +165,10 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # -# -> { "execute": "quit" } -# <- { "return": {} } +# -> { "execute": "quit" } +# <- { "return": {} } ## { 'command': 'quit', 'allow-preconfig': true } diff --git a/qapi/crypto.json b/qapi/crypto.json index fd3d46ebd12..9ec6301e188 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -20,7 +20,6 @@ # Since: 2.5 ## { 'enum': 'QCryptoTLSCredsEndpoint', - 'prefix': 'QCRYPTO_TLS_CREDS_ENDPOINT', 'data': ['client', 'server']} ## @@ -36,36 +35,36 @@ # 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 +# @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 +# @sha1: SHA-1. Should not be used in any new code, legacy compat only # -# @sha224: SHA-224. (since 2.7) +# @sha224: SHA-224. (since 2.7) # -# @sha256: SHA-256. Current recommended strong hash. +# @sha256: SHA-256. Current recommended strong hash. # -# @sha384: SHA-384. (since 2.7) +# @sha384: SHA-384. (since 2.7) # -# @sha512: SHA-512. (since 2.7) +# @sha512: SHA-512. (since 2.7) # -# @ripemd160: RIPEMD-160. (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 # @@ -94,15 +93,17 @@ # # @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: @@ -120,11 +121,10 @@ # 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 @@ -140,8 +140,7 @@ # # Since: 2.6 ## -{ 'enum': 'QCryptoIVGenAlgorithm', - 'prefix': 'QCRYPTO_IVGEN_ALG', +{ 'enum': 'QCryptoIVGenAlgo', 'data': ['plain', 'plain64', 'essiv']} ## @@ -157,7 +156,6 @@ # Since: 2.6 ## { 'enum': 'QCryptoBlockFormat', -# 'prefix': 'QCRYPTO_BLOCK_FORMAT', 'data': ['qcow', 'luks']} ## @@ -205,34 +203,34 @@ # # The options that apply to LUKS encryption format initialization # -# @cipher-alg: the cipher algorithm for data encryption Currently +# @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' +# @cipher-mode: the cipher mode for data encryption. Currently +# defaults to 'xts' # -# @ivgen-alg: the initialization vector generator Currently defaults +# @ivgen-alg: the initialization vector generator. Currently defaults # to 'plain64' # -# @ivgen-hash-alg: the initialization vector generator hash Currently -# defaults to 'sha256' +# @ivgen-hash-alg: the initialization vector generator hash. +# Currently defaults to 'sha256' # -# @hash-alg: the master key hash algorithm Currently defaults to +# @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) +# 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: @@ -311,6 +309,8 @@ # # @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 @@ -322,11 +322,12 @@ # 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', @@ -370,11 +371,11 @@ # @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 +# @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 +# @iter-time: Optional (for activation only). Number of milliseconds to # spend in PBKDF passphrase processing for the newly activated # keyslot. Currently defaults to 2000. # @@ -420,11 +421,6 @@ # # 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) # @@ -434,19 +430,13 @@ # # @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 +# 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. -# # Since: 2.6 ## { 'struct': 'SecretCommonProperties', - 'data': { '*loaded': { 'type': 'bool', 'features': ['deprecated'] }, - '*format': 'QCryptoSecretFormat', + 'data': { '*format': 'QCryptoSecretFormat', '*keyid': 'str', '*iv': 'str' } } @@ -479,7 +469,8 @@ ## { 'struct': 'SecretKeyringProperties', 'base': 'SecretCommonProperties', - 'data': { 'serial': 'int32' } } + 'data': { 'serial': 'int32' }, + 'if': 'CONFIG_SECRET_KEYRING' } ## # @TlsCredsProperties: @@ -512,58 +503,32 @@ # # 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. -# # 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) # @@ -573,20 +538,14 @@ # 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. -# # 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 # @@ -594,8 +553,7 @@ # # Since: 7.1 ## -{ 'enum': 'QCryptoAkCipherAlgorithm', - 'prefix': 'QCRYPTO_AKCIPHER_ALG', +{ 'enum': 'QCryptoAkCipherAlgo', 'data': ['rsa']} ## @@ -603,14 +561,17 @@ # # 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. # @@ -620,8 +581,7 @@ # # Since: 7.1 ## -{ 'enum': 'QCryptoRSAPaddingAlgorithm', - 'prefix': 'QCRYPTO_RSA_PADDING_ALG', +{ 'enum': 'QCryptoRSAPaddingAlgo', 'data': ['raw', 'pkcs1']} ## @@ -629,15 +589,15 @@ # # Specific parameters for RSA algorithm. # -# @hash-alg: QCryptoHashAlgorithm +# @hash-alg: QCryptoHashAlgo # -# @padding-alg: QCryptoRSAPaddingAlgorithm +# @padding-alg: QCryptoRSAPaddingAlgo # # Since: 7.1 ## { 'struct': 'QCryptoAkCipherOptionsRSA', - 'data': { 'hash-alg':'QCryptoHashAlgorithm', - 'padding-alg': 'QCryptoRSAPaddingAlgorithm'}} + 'data': { 'hash-alg':'QCryptoHashAlgo', + 'padding-alg': 'QCryptoRSAPaddingAlgo'}} ## # @QCryptoAkCipherOptions: @@ -645,9 +605,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 index 68289f49840..b13db264034 100644 --- a/qapi/cryptodev.json +++ b/qapi/cryptodev.json @@ -9,18 +9,17 @@ ## ## -# @QCryptodevBackendAlgType: +# @QCryptodevBackendAlgoType: # # The supported algorithm types of a crypto device. # # @sym: symmetric encryption # -# @asym: asymmetric Encryption +# @asym: asymmetric encryption # # Since: 8.0 ## -{ 'enum': 'QCryptodevBackendAlgType', - 'prefix': 'QCRYPTODEV_BACKEND_ALG', +{ 'enum': 'QCryptodevBackendAlgoType', 'data': ['sym', 'asym']} ## @@ -28,10 +27,19 @@ # # 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', - 'prefix': 'QCRYPTODEV_BACKEND_SERVICE', 'data': ['cipher', 'hash', 'mac', 'aead', 'akcipher']} ## @@ -48,7 +56,6 @@ # Since: 8.0 ## { 'enum': 'QCryptodevBackendType', - 'prefix': 'QCRYPTODEV_BACKEND_TYPE', 'data': ['builtin', 'vhost-user', 'lkcf']} ## @@ -87,7 +94,7 @@ ## # @query-cryptodev: # -# Returns information about current crypto devices. +# Return information about current crypto devices. # # Returns: a list of @QCryptodevInfo # diff --git a/qapi/cxl.json b/qapi/cxl.json index 8cc4c72fa94..8f2e9237b19 100644 --- a/qapi/cxl.json +++ b/qapi/cxl.json @@ -117,7 +117,7 @@ # @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. +# a number of banks. # # @bank: Bank of the memory event location. A single bank is accessed # per read or write of the memory. @@ -144,8 +144,8 @@ # @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. +# 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 # @@ -326,6 +326,9 @@ # @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 @@ -361,3 +364,192 @@ ## {'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 4ae1f722a93..d0ba1f0596f 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -15,11 +15,23 @@ # # @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 +# +# @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) @@ -27,7 +39,11 @@ # 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: @@ -38,9 +54,9 @@ # @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: # @@ -61,7 +77,7 @@ # # @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). +# "query-dump". (since 2.6). # # @begin: if specified, the starting physical address. # @@ -74,17 +90,15 @@ # 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: +# .. qmp-example:: # -# -> { "execute": "dump-guest-memory", -# "arguments": { "paging": false, "protocol": "fd:dump" } } -# <- { "return": {} } +# -> { "execute": "dump-guest-memory", +# "arguments": { "paging": false, "protocol": "fd:dump" } } +# <- { "return": {} } ## { 'command': 'dump-guest-memory', 'data': { 'paging': 'bool', 'protocol': 'str', '*detach': 'bool', @@ -136,11 +150,11 @@ # # Since: 2.6 # -# Example: +# .. qmp-example:: # -# -> { "execute": "query-dump" } -# <- { "return": { "status": "active", "completed": 1024000, -# "total": 2048000 } } +# -> { "execute": "query-dump" } +# <- { "return": { "status": "active", "completed": 1024000, +# "total": 2048000 } } ## { 'command': 'query-dump', 'returns': 'DumpQueryResult' } @@ -157,12 +171,12 @@ # # Since: 2.6 # -# Example: +# .. qmp-example:: # -# <- { "event": "DUMP_COMPLETED", -# "data": { "result": { "total": 1090650112, "status": "completed", -# "completed": 1090650112 } }, -# "timestamp": { "seconds": 1648244171, "microseconds": 950316 } } +# <- { "event": "DUMP_COMPLETED", +# "data": { "result": { "total": 1090650112, "status": "completed", +# "completed": 1090650112 } }, +# "timestamp": { "seconds": 1648244171, "microseconds": 950316 } } ## { 'event': 'DUMP_COMPLETED' , 'data': { 'result': 'DumpQueryResult', '*error': 'str' } } @@ -170,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 ## @@ -181,18 +195,18 @@ ## # @query-dump-guest-memory-capability: # -# Returns the available formats for dump-guest-memory +# Return the available formats for dump-guest-memory # # Returns: A @DumpGuestMemoryCapability object listing available # formats for dump-guest-memory # # Since: 2.0 # -# Example: +# .. qmp-example:: # -# -> { "execute": "query-dump-guest-memory-capability" } -# <- { "return": { "formats": -# ["elf", "kdump-zlib", "kdump-lzo", "kdump-snappy"] } } +# -> { "execute": "query-dump-guest-memory-capability" } +# <- { "return": { "formats": +# ["elf", "kdump-zlib", "kdump-lzo", "kdump-snappy"] } } ## { 'command': 'query-dump-guest-memory-capability', 'returns': 'DumpGuestMemoryCapability' } diff --git a/qapi/ebpf.json b/qapi/ebpf.json new file mode 100644 index 00000000000..db19ae850fc --- /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/introspect.json b/qapi/introspect.json index 9173e60fdd0..e9e02972821 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -26,9 +26,9 @@ # the QAPI schema. # # Furthermore, while we strive to keep the QMP wire format -# backwards-compatible across qemu versions, the introspection output +# backwards-compatible across QEMU versions, the introspection output # is not guaranteed to have the same stability. For example, one -# version of qemu may list an object member as an optional +# version of QEMU may list an object member as an optional # non-variant, while another lists the same member only through the # object's variants; or the type of a member may change from a generic # string into a specific enum or from one specific type into an @@ -41,9 +41,9 @@ # 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 ## @@ -93,8 +93,6 @@ # particular order. (since 4.1 for object types, 4.2 for # commands, 5.0 for the rest) # -# Additional members depend on the value of @meta-type. -# # Since: 2.5 ## { 'union': 'SchemaInfo', @@ -129,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', @@ -140,8 +154,8 @@ # # 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 @@ -261,7 +275,7 @@ # # @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. +# :doc:`/devel/qapi-code-gen` section Alternate types. # # On the wire, this can be any of the members. # diff --git a/qapi/job.json b/qapi/job.json index 7f0ba090deb..126fa5ce602 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -20,14 +20,14 @@ # # @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) @@ -74,7 +74,7 @@ # 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 +# to false, the job will remain in this state until it is # dismissed via @job-dismiss. # # @null: The job is in the process of being dismantled. This state @@ -105,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: @@ -136,7 +138,7 @@ # # 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. +# READY. The corresponding JOB_STATUS_CHANGE event will be emitted. # # Cancelling a paused job automatically resumes it. # @@ -154,6 +156,9 @@ # This command returns immediately after resuming a paused job. # Resuming an already running job is an error. # +# This command also clears the error status for block-jobs (stream, +# commit, mirror, backup). +# # @id: The job identifier. # # Since: 3.0 @@ -182,7 +187,23 @@ ## # @job-complete: # -# Manually trigger completion of an active job in the READY state. +# Manually trigger completion of an active job in the READY or STANDBY +# state. Completing the job in any other state is an error. +# +# This is supported only for drive mirroring, where it also switches +# the device to write to the target path only. Note that drive +# mirroring includes drive-mirror, blockdev-mirror and block-commit +# job (only in case of "active commit", when the node being commited +# is used by the guest). 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. # # @id: The job identifier. # @@ -195,12 +216,16 @@ # # 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. +# dismiss enabled. In turn, automatic dismiss may be enabled only +# for jobs that have @auto-dismiss option, which are drive-backup, +# blockdev-backup, drive-mirror, blockdev-mirror, block-commit and +# block-stream. @auto-dismiss is enabled by default for these +# jobs. # # 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. +# reached its terminal state, CONCLUDED. For jobs that make use of +# the JOB_READY event, job-cancel or job-complete will still need to +# be used as appropriate. # # @id: The job identifier. # @@ -220,6 +245,9 @@ # force ALL jobs in the transaction to finalize, so it is only # necessary to instruct a single member job to finalize. # +# The command is applicable only to jobs which have @auto-finalize option +# and only when this option is set to false. +# # @id: The identifier of any job in the transaction, or of a job that # is not part of any transaction. # diff --git a/qapi/machine-common.json b/qapi/machine-common.json new file mode 100644 index 00000000000..298e51f373a --- /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-s390x.json b/qapi/machine-s390x.json new file mode 100644 index 00000000000..966dbd61d2e --- /dev/null +++ b/qapi/machine-s390x.json @@ -0,0 +1,121 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# +# 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': 'machine-common.json' } + +## +# @S390CpuPolarization: +# +# An enumeration of CPU polarization that can be assumed by a virtual +# S390 CPU +# +# Since: 8.2 +## +{ 'enum': 'S390CpuPolarization', + 'data': [ 'horizontal', 'vertical' ] +} + +## +# @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' ] +} + +## +# @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' ] +} + +## +# @CpuPolarizationInfo: +# +# The result of a CPU polarization query. +# +# @polarization: the CPU polarization +# +# Since: 8.2 +## +{ 'struct': 'CpuPolarizationInfo', + 'data': { 'polarization': 'S390CpuPolarization' } +} + +## +# @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' ] +} diff --git a/qapi/machine-target.json b/qapi/machine-target.json deleted file mode 100644 index f0a6b72414e..00000000000 --- a/qapi/machine-target.json +++ /dev/null @@ -1,363 +0,0 @@ -# -*- 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. - -## -# @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. -# -# @name: the name of the CPU definition the model is based on -# -# @props: a dictionary of QOM properties to be applied -# -# Since: 2.8 -## -{ 'struct': 'CpuModelInfo', - 'data': { 'name': 'str', - '*props': 'any' } } - -## -# @CpuModelExpansionType: -# -# 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. -# -# 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 -## -{ 'enum': 'CpuModelExpansionType', - 'data': [ 'static', 'full' ] } - -## -# @CpuModelCompareResult: -# -# 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. -# -# @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. -# -# @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 -## -{ 'enum': 'CpuModelCompareResult', - 'data': [ 'incompatible', 'identical', 'superset', 'subset' ] } - -## -# @CpuModelBaselineInfo: -# -# The result of a CPU model baseline. -# -# @model: the baselined CpuModelInfo. -# -# Since: 2.8 -## -{ 'struct': 'CpuModelBaselineInfo', - 'data': { 'model': 'CpuModelInfo' }, - 'if': 'TARGET_S390X' } - -## -# @CpuModelCompareInfo: -# -# 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 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. -# -# Since: 2.8 -## -{ 'struct': 'CpuModelCompareInfo', - 'data': { 'result': 'CpuModelCompareResult', - 'responsible-properties': ['str'] }, - 'if': 'TARGET_S390X' } - -## -# @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. -# -# 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. -# -# 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. -# -# Note: this command isn't specific to s390x, but is only implemented -# on this architecture currently. -# -# Since: 2.8 -## -{ 'command': 'query-cpu-model-comparison', - 'data': { 'modela': 'CpuModelInfo', 'modelb': 'CpuModelInfo' }, - 'returns': 'CpuModelCompareInfo', - 'if': 'TARGET_S390X' } - -## -# @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). -# -# 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. -# -# 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. -# -# Note: this command isn't specific to s390x, but is only implemented -# on this architecture currently. -# -# Since: 2.8 -## -{ 'command': 'query-cpu-model-baseline', - 'data': { 'modela': 'CpuModelInfo', - 'modelb': 'CpuModelInfo' }, - 'returns': 'CpuModelBaselineInfo', - 'if': 'TARGET_S390X' } - -## -# @CpuModelExpansionInfo: -# -# The result of a cpu model expansion. -# -# @model: the expanded CpuModelInfo. -# -# Since: 2.8 -## -{ 'struct': 'CpuModelExpansionInfo', - 'data': { 'model': 'CpuModelInfo' }, - 'if': { 'any': [ 'TARGET_S390X', - 'TARGET_I386', - 'TARGET_ARM' ] } } - -## -# @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. -# -# 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. -# -# 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. -# -# Since: 2.8 -## -{ 'command': 'query-cpu-model-expansion', - 'data': { 'type': 'CpuModelExpansionType', - 'model': 'CpuModelInfo' }, - 'returns': 'CpuModelExpansionInfo', - 'if': { 'any': [ 'TARGET_S390X', - 'TARGET_I386', - 'TARGET_ARM' ] } } - -## -# @CpuDefinitionInfo: -# -# Virtual CPU definition. -# -# @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) -# -# @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) -# -# @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) -# -# @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 -## -{ 'struct': 'CpuDefinitionInfo', - 'data': { 'name': 'str', - '*migration-safe': 'bool', - 'static': 'bool', - '*unavailable-features': [ 'str' ], - 'typename': 'str', - '*alias-of' : 'str', - 'deprecated' : 'bool' }, - 'if': { 'any': [ 'TARGET_PPC', - 'TARGET_ARM', - 'TARGET_I386', - 'TARGET_S390X', - 'TARGET_MIPS', - 'TARGET_LOONGARCH64', - 'TARGET_RISCV' ] } } - -## -# @query-cpu-definitions: -# -# Return a list of supported virtual CPU definitions -# -# Returns: a list of CpuDefinitionInfo -# -# Since: 1.2 -## -{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'], - 'if': { 'any': [ 'TARGET_PPC', - 'TARGET_ARM', - 'TARGET_I386', - 'TARGET_S390X', - 'TARGET_MIPS', - 'TARGET_LOONGARCH64', - 'TARGET_RISCV' ] } } diff --git a/qapi/machine.json b/qapi/machine.json index a08b6576cac..0650b8de71a 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -9,6 +9,7 @@ ## { 'include': 'common.json' } +{ 'include': 'machine-common.json' } ## # @SysEmuTarget: @@ -23,30 +24,31 @@ # # @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 # # Since: 2.12 ## -{ 'enum': 'CpuS390State', - 'prefix': 'S390_CPU_STATE', +{ 'enum': 'S390CpuState', 'data': [ 'uninitialized', 'stopped', 'check-stop', 'operating', 'load' ] } ## @@ -56,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: @@ -71,8 +80,7 @@ # # @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) @@ -91,43 +99,63 @@ ## # @query-cpus-fast: # -# Returns information about all virtual CPUs. +# Return information about all virtual CPUs. # # Returns: list of @CpuInfoFast # # Since: 2.12 # -# Example: -# -# -> { "execute": "query-cpus-fast" } -# <- { "return": [ -# { -# "thread-id": 25627, -# "props": { -# "core-id": 0, -# "thread-id": 0, -# "socket-id": 0 +# .. qmp-example:: +# +# -> { "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: # @@ -154,11 +182,19 @@ # @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 ## { 'struct': 'MachineInfo', @@ -166,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', 'acpi': 'bool' } } + '*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: @@ -204,15 +275,15 @@ { 'command': 'query-current-machine', 'returns': 'CurrentMachineParams' } ## -# @TargetInfo: +# @QemuTargetInfo: # -# Information describing the QEMU target. +# Information on the target configuration built into the QEMU binary. # # @arch: the target architecture # # Since: 1.2 ## -{ 'struct': 'TargetInfo', +{ 'struct': 'QemuTargetInfo', 'data': { 'arch': 'SysEmuTarget' } } ## @@ -220,11 +291,11 @@ # # Return information about the target for this QEMU # -# Returns: TargetInfo +# Returns: QemuTargetInfo # # Since: 1.2 ## -{ 'command': 'query-target', 'returns': 'TargetInfo' } +{ 'command': 'query-target', 'returns': 'QemuTargetInfo' } ## # @UuidInfo: @@ -235,8 +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'} } @@ -249,10 +320,10 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # -# -> { "execute": "query-uuid" } -# <- { "return": { "UUID": "550e8400-e29b-41d4-a716-446655440000" } } +# -> { "execute": "query-uuid" } +# <- { "return": { "UUID": "550e8400-e29b-41d4-a716-446655440000" } } ## { 'command': 'query-uuid', 'returns': 'UuidInfo', 'allow-preconfig': true } @@ -283,10 +354,10 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # -# -> { "execute": "system_reset" } -# <- { "return": {} } +# -> { "execute": "system_reset" } +# <- { "return": {} } ## { 'command': 'system_reset' } @@ -297,15 +368,15 @@ # # 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: +# .. qmp-example:: # -# -> { "execute": "system_powerdown" } -# <- { "return": {} } +# -> { "execute": "system_powerdown" } +# <- { "return": {} } ## { 'command': 'system_powerdown' } @@ -319,15 +390,13 @@ # # 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. +# .. qmp-example:: # -# Example: -# -# -> { "execute": "system_wakeup" } -# <- { "return": {} } +# -> { "execute": "system_wakeup" } +# <- { "return": {} } ## { 'command': 'system_wakeup' } @@ -367,20 +436,18 @@ # @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 +# all CPUs (ppc64). The command fails when the guest doesn't support # injecting. # -# Returns: If successful, nothing -# # 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: +# .. qmp-example:: # -# -> { "execute": "inject-nmi" } -# <- { "return": {} } +# -> { "execute": "inject-nmi" } +# <- { "return": {} } ## { 'command': 'inject-nmi' } @@ -400,16 +467,16 @@ ## # @query-kvm: # -# Returns information about KVM acceleration +# Return information about KVM acceleration # # Returns: @KvmInfo # # Since: 0.14 # -# Example: +# .. qmp-example:: # -# -> { "execute": "query-kvm" } -# <- { "return": { "enabled": true, "present": true } } +# -> { "execute": "query-kvm" } +# <- { "return": { "enabled": true, "present": true } } ## { 'command': 'query-kvm', 'returns': 'KvmInfo' } @@ -436,6 +503,8 @@ # # A discriminated record of NUMA options. (for OptsVisitor) # +# @type: NUMA option type +# # Since: 2.1 ## { 'union': 'NumaOptions', @@ -625,7 +694,7 @@ # 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. +# Table 5-146: Field "Data Type" of ACPI 6.3 spec. # # @access-latency: access latency (nanoseconds) # @@ -742,7 +811,7 @@ # # @policy: the write policy, none/write-back/write-through. # -# @line: the cache Line size in bytes. +# @line: the cache line size in bytes. # # Since: 5.0 ## @@ -769,22 +838,24 @@ # @cpu-index: the index of the virtual CPU to use for translating the # virtual address (defaults to CPU 0) # -# 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: +# .. qmp-example:: # -# -> { "execute": "memsave", -# "arguments": { "val": 10, -# "size": 100, -# "filename": "/tmp/virtual-mem-dump" } } -# <- { "return": {} } +# -> { "execute": "memsave", +# "arguments": { "val": 10, +# "size": 100, +# "filename": "/tmp/virtual-mem-dump" } } +# <- { "return": {} } ## { 'command': 'memsave', - 'data': {'val': 'int', 'size': 'int', 'filename': 'str', '*cpu-index': 'int'} } + 'data': { + 'val': 'uint64', + 'size': 'size', + 'filename': 'str', + '*cpu-index': 'int' } } ## # @pmemsave: @@ -797,22 +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: +# .. qmp-example:: # -# -> { "execute": "pmemsave", -# "arguments": { "val": 10, -# "size": 100, -# "filename": "/tmp/physical-mem-dump" } } -# <- { "return": {} } +# -> { "execute": "pmemsave", +# "arguments": { "val": 10, +# "size": 100, +# "filename": "/tmp/physical-mem-dump" } } +# <- { "return": {} } ## { 'command': 'pmemsave', - 'data': {'val': 'int', 'size': 'int', 'filename': 'str'} } + 'data': { + 'val': 'uint64', + 'size': 'size', + 'filename': 'str' } } ## # @Memdev: @@ -858,73 +930,88 @@ ## # @query-memdev: # -# Returns information for all memory backends. +# Return information for all memory backends. # # Returns: a list of @Memdev. # # Since: 2.1 # -# 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" +# .. 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" +# } +# ] # } -# ] -# } ## { '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 +# @drawer-id: drawer number within CPU topology the CPU belongs to +# (since 8.2) # -# @die-id: die number within socket the CPU belongs to (since 4.1) +# @book-id: book number within parent container the CPU belongs to +# (since 8.2) # -# @cluster-id: cluster number within die the CPU belongs to (since -# 7.1) +# @socket-id: socket number within parent container the CPU belongs to # -# @core-id: core number within cluster the CPU belongs to +# @die-id: die number within the parent container the CPU belongs to +# (since 4.1) # -# @thread-id: thread number within core the CPU belongs to +# @cluster-id: cluster number within the parent container the CPU +# belongs to (since 7.1) # -# 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. +# @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' } @@ -935,7 +1022,8 @@ # # @type: CPU object type for usage with device_add command # -# @props: list of properties to be used for hotplugging CPU +# @props: list of properties to pass for hotplugging a CPU with +# device_add # # @vcpus-count: number of logical VCPU threads @HotpluggableCPU # provides @@ -943,6 +1031,9 @@ # @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 ## { 'struct': 'HotpluggableCPU', @@ -962,49 +1053,56 @@ # # Since: 2.7 # -# Examples: -# -# 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]"} -# ]}' -# -# For pc machine type started with -smp 1,maxcpus=2: -# -# -> { "execute": "query-hotpluggable-cpus" } -# <- {"return": [ -# { -# "type": "qemu64-x86_64-cpu", "vcpus-count": 1, -# "props": {"core-id": 0, "socket-id": 1, "thread-id": 0} -# }, -# { -# "qom-path": "/machine/unattached/device[0]", -# "type": "qemu64-x86_64-cpu", "vcpus-count": 1, -# "props": {"core-id": 0, "socket-id": 0, "thread-id": 0} -# } -# ]} -# -# 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 } -# } -# ]} +# .. qmp-example:: +# :annotated: +# +# 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]"} +# ]} +# +# .. qmp-example:: +# :annotated: +# +# For pc machine type started with ``-smp 1,maxcpus=2``:: +# +# -> { "execute": "query-hotpluggable-cpus" } +# <- {"return": [ +# { +# "type": "qemu64-x86_64-cpu", "vcpus-count": 1, +# "props": {"core-id": 0, "socket-id": 1, "thread-id": 0} +# }, +# { +# "qom-path": "/machine/unattached/device[0]", +# "type": "qemu64-x86_64-cpu", "vcpus-count": 1, +# "props": {"core-id": 0, "socket-id": 0, "thread-id": 0} +# } +# ]} +# +# .. qmp-example:: +# :annotated: +# +# For s390x-virtio-ccw machine type started with +# ``-smp 1,maxcpus=2 -cpu qemu``:: +# +# -> { "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 } @@ -1034,24 +1132,26 @@ # # 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'} } @@ -1060,7 +1160,7 @@ # # Information about the guest balloon device. # -# @actual: the logical size of the VM in bytes Formula used: +# @actual: the logical size of the VM in bytes. Formula used: # logical_vm_size = vm_ram_size - balloon_size # # Since: 0.14 @@ -1073,20 +1173,22 @@ # 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 +# @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: +# .. qmp-example:: # -# -> { "execute": "query-balloon" } -# <- { "return": { -# "actual": 1073741824 -# } -# } +# -> { "execute": "query-balloon" } +# <- { "return": { +# "actual": 1073741824 +# } +# } ## { 'command': 'query-balloon', 'returns': 'BalloonInfo' } @@ -1097,22 +1199,85 @@ # is equivalent to the @actual field return by the 'query-balloon' # command # -# @actual: the logical size of the VM in bytes Formula used: +# @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: +# .. qmp-example:: # -# <- { "event": "BALLOON_CHANGE", -# "data": { "actual": 944766976 }, -# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } +# <- { "event": "BALLOON_CHANGE", +# "data": { "actual": 944766976 }, +# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } ## { 'event': 'BALLOON_CHANGE', 'data': { 'actual': 'int' } } +## +# @HvBalloonInfo: +# +# hv-balloon guest-provided memory status information. +# +# @committed: the amount of memory in use inside the guest plus the +# amount of the memory unusable inside the guest (ballooned out, +# offline, etc.) +# +# @available: the amount of the memory inside the guest available for +# new allocations ("free") +# +# Since: 8.2 +## +{ 'struct': 'HvBalloonInfo', + 'data': { 'committed': 'size', 'available': 'size' } } + +## +# @query-hv-balloon-status-report: +# +# Return 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: # @@ -1136,10 +1301,10 @@ # 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 ## @@ -1265,6 +1430,29 @@ } } +## +# @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: # @@ -1276,14 +1464,19 @@ # # @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', @@ -1292,6 +1485,8 @@ ## # @VirtioPMEMDeviceInfoWrapper: # +# @data: VirtioPMEM state information +# # Since: 2.1 ## { 'struct': 'VirtioPMEMDeviceInfoWrapper', @@ -1300,6 +1495,8 @@ ## # @VirtioMEMDeviceInfoWrapper: # +# @data: VirtioMEMDevice state information +# # Since: 2.1 ## { 'struct': 'VirtioMEMDeviceInfoWrapper', @@ -1308,16 +1505,30 @@ ## # @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 # +# @type: memory device type +# # Since: 2.1 ## { 'union': 'MemoryDeviceInfo', @@ -1327,7 +1538,8 @@ 'nvdimm': 'PCDIMMDeviceInfoWrapper', 'virtio-pmem': 'VirtioPMEMDeviceInfoWrapper', 'virtio-mem': 'VirtioMEMDeviceInfoWrapper', - 'sgx-epc': 'SgxEPCDeviceInfoWrapper' + 'sgx-epc': 'SgxEPCDeviceInfoWrapper', + 'hv-balloon': 'HvBalloonDeviceInfoWrapper' } } @@ -1368,20 +1580,20 @@ # # 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'] } @@ -1398,48 +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: +# .. 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": "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: # @@ -1478,29 +1662,49 @@ # Schema for CPU topology configuration. A missing value lets QEMU # figure out a suitable value based on the ones that are provided. # -# @cpus: number of virtual CPUs in the virtual machine -# -# @sockets: number of sockets in the CPU topology +# The members other than @cpus and @maxcpus define a topology of +# containers. # -# @dies: number of dies per socket in the CPU topology +# The ordering from highest/coarsest to lowest/finest is: @drawers, +# @books, @sockets, @dies, @clusters, @cores, @threads. # -# @clusters: number of clusters per die in the CPU topology (since -# 7.0) +# Different architectures support different subsets of topology +# containers. # -# @cores: number of cores per cluster in the CPU topology +# For example, s390x does not have clusters and dies, and the socket +# is the parent container of cores. # -# @threads: number of threads per core in the CPU topology +# @cpus: number of virtual CPUs in the virtual machine # # @maxcpus: maximum number of hotpluggable virtual CPUs in the virtual # machine # +# @drawers: number of drawers in the CPU topology (since 8.2) +# +# @books: number of books in the CPU topology (since 8.2) +# +# @sockets: number of sockets per parent container +# +# @dies: number of dies per parent container +# +# @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' } } @@ -1592,23 +1796,6 @@ '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: # @@ -1650,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: @@ -1682,12 +1872,410 @@ # # Since: 7.2 # -# Example: +# .. qmp-example:: # -# -> { "execute": "dumpdtb" } -# "arguments": { "filename": "fdt.dtb" } } -# <- { "return": {} } +# -> { "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' ]} + +## +# @dump-skeys: +# +# Dump the storage keys for an s390x guest +# +# @filename: the path to the file to dump to +# +# Since: 2.5 +# +# .. qmp-example:: +# +# -> { "execute": "dump-skeys", +# "arguments": { "filename": "/tmp/skeys" } } +# <- { "return": {} } +## +{ 'command': 'dump-skeys', + 'data': { 'filename': 'str' } } + +## +# @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. +# +# @name: the name of the CPU definition the model is based on +# +# @props: a dictionary of QOM properties to be applied +# +# Since: 2.8 +## +{ 'struct': 'CpuModelInfo', + 'data': { 'name': 'str', + '*props': 'any' } } + +## +# @CpuModelExpansionType: +# +# 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. +# +# .. 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 +## +{ 'enum': 'CpuModelExpansionType', + 'data': [ 'static', 'full' ] } + +## +# @CpuModelCompareResult: +# +# 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. +# +# @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. +# +# @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 +## +{ 'enum': 'CpuModelCompareResult', + 'data': [ 'incompatible', 'identical', 'superset', 'subset' ] } + +## +# @CpuModelBaselineInfo: +# +# The result of a CPU model baseline. +# +# @model: the baselined CpuModelInfo. +# +# Since: 2.8 +## +{ 'struct': 'CpuModelBaselineInfo', + 'data': { 'model': 'CpuModelInfo' } } + +## +# @CpuModelCompareInfo: +# +# 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 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. +# +# Since: 2.8 +## +{ 'struct': 'CpuModelCompareInfo', + 'data': { 'result': 'CpuModelCompareResult', + 'responsible-properties': ['str'] } } + +## +# @query-cpu-model-comparison: +# +# 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. +# +# 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. +# +# Some architectures may not support comparing CPU models. s390x +# supports comparing CPU models. +# +# @modela: description of the first CPU model to compare, referred to +# as "model A" in CpuModelCompareResult +# +# @modelb: description of the second CPU model to compare, referred to +# as "model B" in CpuModelCompareResult +# +# Returns: a CpuModelCompareInfo describing how both CPU models +# compare +# +# Errors: +# - if comparing CPU models is not supported by the target +# - if a model cannot be used +# - if a model contains an unknown cpu definition name, unknown +# properties or properties with wrong types. +# +# Since: 2.8 +## +{ 'command': 'query-cpu-model-comparison', + 'data': { 'modela': 'CpuModelInfo', 'modelb': 'CpuModelInfo' }, + 'returns': 'CpuModelCompareInfo' } + +## +# @query-cpu-model-baseline: +# +# 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. +# +# 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. +# +# Some architectures may not support baselining CPU models. s390x +# supports baselining CPU models. +# +# @modela: description of the first CPU model to baseline +# +# @modelb: description of the second CPU model to baseline +# +# Returns: a CpuModelBaselineInfo describing the baselined CPU model +# +# Errors: +# - if baselining CPU models is not supported by the target +# - if a model cannot be used +# - if a model contains an unknown cpu definition name, unknown +# properties or properties with wrong types. +# +# Since: 2.8 +## +{ 'command': 'query-cpu-model-baseline', + 'data': { 'modela': 'CpuModelInfo', + 'modelb': 'CpuModelInfo' }, + 'returns': 'CpuModelBaselineInfo' } + +## +# @CpuModelExpansionInfo: +# +# The result of a cpu model expansion. +# +# @model: the expanded CpuModelInfo. +# +# @deprecated-props: an optional 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: 10.1 -- since 9.1 on s390x --). +# +# Since: 2.8 +## +{ 'struct': 'CpuModelExpansionInfo', + 'data': { 'model': 'CpuModelInfo', + '*deprecated-props' : ['str'] } } + +## +# @query-cpu-model-expansion: +# +# 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. +# +# Some architectures may not support all expansion types. s390x +# supports "full" and "static". Arm only supports "full". +# +# @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 +## +{ 'command': 'query-cpu-model-expansion', + 'data': { 'type': 'CpuModelExpansionType', + 'model': 'CpuModelInfo' }, + 'returns': 'CpuModelExpansionInfo' } + +## +# @CpuDefinitionInfo: +# +# Virtual CPU definition. +# +# @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) +# +# @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) +# +# @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) +# +# @deprecated: If true, this CPU model is deprecated and may be +# removed 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 +## +{ 'struct': 'CpuDefinitionInfo', + 'data': { 'name': 'str', + '*migration-safe': 'bool', + 'static': 'bool', + '*unavailable-features': [ 'str' ], + 'typename': 'str', + '*alias-of' : 'str', + 'deprecated' : 'bool' } } + +## +# @query-cpu-definitions: +# +# Return a list of supported virtual CPU definitions +# +# Returns: a list of CpuDefinitionInfo +# +# Since: 1.2 +## +{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'] } diff --git a/qapi/meson.build b/qapi/meson.build index 60a668b3432..3b035aea339 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -33,14 +33,15 @@ qapi_all_modules = [ 'crypto', 'cxl', 'dump', + 'ebpf', 'error', 'introspect', 'job', + 'machine-common', 'machine', - 'machine-target', + 'machine-s390x', 'migration', 'misc', - 'misc-target', 'net', 'pragma', 'qom', @@ -50,6 +51,7 @@ qapi_all_modules = [ 'stats', 'trace', 'transaction', + 'vfio', 'virtio', 'yank', ] @@ -60,9 +62,11 @@ if have_system 'cryptodev', 'qdev', 'pci', - 'rdma', 'rocker', + 'misc-arm', + 'misc-i386', 'tpm', + 'uefi', ] endif if have_system or have_tools @@ -81,14 +85,12 @@ qapi_nonmodule_outputs = [ 'qapi-emit-events.c', 'qapi-emit-events.h', ] -# First build all sources -qapi_util_outputs = [ +qapi_outputs = qapi_nonmodule_outputs + [ 'qapi-builtin-types.c', 'qapi-builtin-visit.c', 'qapi-builtin-types.h', 'qapi-builtin-visit.h', ] qapi_inputs = [] -qapi_specific_outputs = [] foreach module : qapi_all_modules qapi_inputs += [ files(module + '.json') ] qapi_module_outputs = [ @@ -106,24 +108,17 @@ foreach module : qapi_all_modules 'qapi-commands-@0@.trace-events'.format(module), ] endif - if module.endswith('-target') - qapi_specific_outputs += qapi_module_outputs - else - qapi_util_outputs += qapi_module_outputs - endif + qapi_outputs += qapi_module_outputs endforeach qapi_files = custom_target('shared QAPI source files', - output: qapi_util_outputs + qapi_specific_outputs + qapi_nonmodule_outputs, + output: qapi_outputs, input: [ files('qapi-schema.json') ], command: [ qapi_gen, '-o', 'qapi', '-b', '@INPUT0@' ], depend_files: [ qapi_inputs, qapi_gen_depends ]) -# Now go through all the outputs and add them to the right sourceset. -# These loops must be synchronized with the output of the above custom target. - i = 0 -foreach output : qapi_util_outputs +foreach output : qapi_outputs if output.endswith('.h') genh += qapi_files[i] endif @@ -133,14 +128,3 @@ foreach output : qapi_util_outputs util_ss.add(qapi_files[i]) i = i + 1 endforeach - -foreach output : qapi_specific_outputs + qapi_nonmodule_outputs - if output.endswith('.h') - genh += qapi_files[i] - endif - if output.endswith('.trace-events') - qapi_trace_events += qapi_files[i] - endif - 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 8843e74b59c..4963f6ca127 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -23,9 +23,6 @@ # # @duplicate: number of duplicate (zero) pages (since 1.2) # -# @skipped: number of skipped zero pages. Always zero, only provided for -# compatibility (since 1.5) -# # @normal: number of normal pages (since 1.2) # # @normal-bytes: number of normal bytes sent (since 1.2) @@ -60,20 +57,14 @@ # # @dirty-sync-missed-zero-copy: Number of times dirty RAM # synchronization could not avoid copying dirty pages. This is -# between 0 and @dirty-sync-count * @multifd-channels. (since -# 7.1) -# -# Features: -# -# @deprecated: Member @skipped is always zero since 1.5.3 +# 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': { 'type': 'int', 'features': ['deprecated'] }, 'normal': 'int', 'normal-bytes': 'int', 'dirty-pages-rate': 'int', 'mbps': 'number', 'dirty-sync-count': 'int', @@ -146,13 +137,16 @@ # # @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-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. # @@ -164,8 +158,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 (also known as switchover phase). +# Before 9.2, this is only used when (1) in precopy, and (2) when +# pre-switchover capability is enabled. After 10.0, this state will +# always be present for every migration procedure as the switchover +# phase. (since 2.11) # # @wait-unplug: wait for device unplug request by guest OS to be # completed. (since 4.2) @@ -175,6 +172,7 @@ { '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' ] } ## @@ -202,9 +200,6 @@ # @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 -# # @xbzrle-cache: @XBZRLECacheStats containing detailed XBZRLE # migration statistics, only returned if XBZRLE feature is on and # status is 'active' or 'completed' (since 1.2) @@ -230,9 +225,8 @@ # 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 @@ -242,10 +236,6 @@ # 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) # @@ -258,10 +248,10 @@ # 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-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 @@ -274,7 +264,6 @@ ## { 'struct': 'MigrationInfo', 'data': {'*status': 'MigrationStatus', '*ram': 'MigrationStats', - '*disk': 'MigrationStats', '*vfio': 'VfioStats', '*xbzrle-cache': 'XBZRLECacheStats', '*total-time': 'int', @@ -286,7 +275,6 @@ '*blocked-reasons': ['str'], '*postcopy-blocktime': 'uint32', '*postcopy-vcpu-blocktime': ['uint32'], - '*compression': 'CompressionStats', '*socket-address': ['SocketAddress'], '*dirty-limit-throttle-time-per-round': 'uint64', '*dirty-limit-ring-full-time': 'uint64'} } @@ -294,123 +282,99 @@ ## # @query-migrate: # -# Returns information about current migration process. If migration +# Return 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 # -# Examples: -# -# 1. Before the first migration -# -# -> { "execute": "query-migrate" } -# <- { "return": {} } -# -# 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 -# } -# } -# } -# -# 5. Migration is being performed and is a block migration: -# -# -> { "execute": "query-migrate" } -# <- { -# "return":{ -# "status":"active", -# "total-time":12345, -# "setup-time":12345, -# "expected-downtime":12345, -# "ram":{ -# "total":1057024, -# "remaining":1053304, -# "transferred":3720, -# "duplicate":123, -# "normal":123, -# "normal-bytes":123456, -# "dirty-sync-count":15 -# }, -# "disk":{ -# "total":20971520, -# "remaining":20880384, -# "transferred":91136 +# .. qmp-example:: +# :title: Before the first migration +# +# -> { "execute": "query-migrate" } +# <- { "return": {} } +# +# .. qmp-example:: +# :title: 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 +# } # } -# } -# } -# -# 6. Migration is being performed and XBZRLE is active: -# -# -> { "execute": "query-migrate" } -# <- { -# "return":{ -# "status":"active", -# "total-time":12345, -# "setup-time":12345, -# "expected-downtime":12345, -# "ram":{ -# "total":1057024, -# "remaining":1053304, -# "transferred":3720, -# "duplicate":10, -# "normal":3333, -# "normal-bytes":3412992, -# "dirty-sync-count":15 -# }, -# "xbzrle-cache":{ -# "cache-size":67108864, -# "bytes":20971520, -# "pages":2444343, -# "cache-miss":2244, -# "cache-miss-rate":0.123, -# "encoding-rate":80.1, -# "overflow":34434 -# } -# } -# } +# } +# +# .. qmp-example:: +# :title: Migration is done and has failed +# +# -> { "execute": "query-migrate" } +# <- { "return": { "status": "failed" } } +# +# .. qmp-example:: +# :title: Migration is being performed +# +# -> { "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' } @@ -420,7 +384,7 @@ # Migration capabilities enumeration # # @xbzrle: Migration supports xbzrle (Xor Based Zero Run Length -# Encoding). This feature allows us to minimize migration traffic +# Encoding). This feature allows us to minimize migration traffic # for certain work loads, by sending compressed difference of the # pages # @@ -432,19 +396,10 @@ # 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 ) +# capability on the source VM. The feature is disabled by +# default. (since 1.6) # -# @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) @@ -452,7 +407,7 @@ # @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 +# 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 @@ -460,20 +415,15 @@ # 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 +# @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) -# # @return-path: If enabled, migration will use the return path even # for precopy. (since 2.10) # # @pause-before-switchover: Pause outgoing migration before -# serialising device state and before disabling block IO (since -# 2.11) +# serialising device state and before disabling block IO +# (since 2.11) # # @multifd: Use more than one fd for migration (since 4.0) # @@ -495,8 +445,8 @@ # # @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) +# 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 @@ -525,23 +475,30 @@ # 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', 'switchover-ack', - 'dirty-limit'] } + 'dirty-limit', 'mapped-ram'] } ## # @MigrationCapabilityStatus: @@ -566,11 +523,11 @@ # # Since: 1.2 # -# Example: +# .. qmp-example:: # -# -> { "execute": "migrate-set-capabilities" , "arguments": -# { "capabilities": [ { "capability": "xbzrle", "state": true } ] } } -# <- { "return": {} } +# -> { "execute": "migrate-set-capabilities" , "arguments": +# { "capabilities": [ { "capability": "xbzrle", "state": true } ] } } +# <- { "return": {} } ## { 'command': 'migrate-set-capabilities', 'data': { 'capabilities': ['MigrationCapabilityStatus'] } } @@ -578,25 +535,24 @@ ## # @query-migrate-capabilities: # -# Returns information about the current migration capabilities status +# Return information about the current migration capabilities status # # Returns: @MigrationCapabilityStatus # # Since: 1.2 # -# Example: +# .. 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": false, "capability": "compress"}, -# {"state": true, "capability": "events"}, -# {"state": false, "capability": "postcopy-ram"}, -# {"state": false, "capability": "x-colo"} -# ]} +# -> { "execute": "query-migrate-capabilities" } +# <- { "return": [ +# {"state": false, "capability": "xbzrle"}, +# {"state": false, "capability": "rdma-pin-all"}, +# {"state": false, "capability": "auto-converge"}, +# {"state": false, "capability": "zero-blocks"}, +# {"state": true, "capability": "events"}, +# {"state": false, "capability": "postcopy-ram"}, +# {"state": false, "capability": "x-colo"} +# ]} ## { 'command': 'query-migrate-capabilities', 'returns': ['MigrationCapabilityStatus']} @@ -611,11 +567,114 @@ # # @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) +# +# @cpr-transfer: This mode allows the user to transfer a guest to a +# new QEMU instance on the same host with minimal guest pause +# time by preserving guest RAM in place. Devices and their pinned +# pages will also be preserved in a future QEMU release. +# +# The user starts new QEMU on the same host as old QEMU, with +# command-line arguments to create the same machine, plus the +# -incoming option for the main migration channel, like normal +# live migration. In addition, the user adds a second -incoming +# option with channel type "cpr". This CPR channel must support +# file descriptor transfer with SCM_RIGHTS, i.e. it must be a +# UNIX domain socket. +# +# To initiate CPR, the user issues a migrate command to old QEMU, +# adding a second migration channel of type "cpr" in the channels +# argument. Old QEMU stops the VM, saves state to the migration +# channels, and enters the postmigrate state. Execution resumes +# in new QEMU. +# +# New QEMU reads the CPR channel before opening a monitor, hence +# the CPR channel cannot be specified in the list of channels for +# a migrate-incoming command. It may only be specified on the +# command line. +# +# The main channel address cannot be a file type, and for an +# inet socket, the port cannot be 0 (meaning dynamically choose +# a port). +# +# Memory-backend objects must have the share=on attribute, but +# memory-backend-epc is not supported. The VM must be started +# with the '-machine aux-ram-share=on' option. +# +# When using -incoming defer, you must issue the migrate command +# to old QEMU before issuing any monitor commands to new QEMU. +# However, new QEMU does not open and read the migration stream +# until you issue the migrate incoming command. +# +# (since 10.0) +## +{ 'enum': 'MigMode', + 'data': [ 'normal', 'cpr-reboot', 'cpr-transfer' ] } + +## +# @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: @@ -638,8 +697,8 @@ # @alias: An alias name for migration (for example the bitmap name on # 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 ## @@ -689,42 +748,21 @@ # @announce-step: Increase in delay (in milliseconds) between # subsequent packets in the announcement (Since 4.0) # -# @compress-level: Set the compression level to be used in live -# migration, the compression level is an integer between 0 and 9, -# where 0 means no compression, 1 means the best compression -# speed, and 9 means best compression ratio which will consume -# more CPU. -# -# @compress-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) +# 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) +# 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) +# 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 +# @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. @@ -732,23 +770,26 @@ # 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) +# 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) +# 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) +# +# Note: empty value works only since 2.9. # # @tls-authz: ID of the 'authz' object subclass that provides access # control checking of the TLS x509 certificate distinguished name. @@ -756,8 +797,19 @@ # and recreated on the fly while the migration server is active. # If missing, it will default to denying access (Since 4.0) # -# @max-bandwidth: to set maximum speed for migration. maximum speed -# in bytes per second. (Since 2.8) +# @max-bandwidth: maximum speed for migration, in bytes per second. +# (Since 2.8) +# +# @avail-switchover-bandwidth: to set the available bandwidth that +# migration can use during switchover phase. **Note:** this does +# not limit the bandwidth during switchover, but only for +# calculations when making decisions to switchover. By default, +# this value is zero, which means QEMU will estimate the bandwidth +# automatically. This can be set when the estimated value is not +# accurate, while the user is able to guarantee such bandwidth is +# available when switching over. When specified correctly, this +# can make the switchover decision much more accurate. +# (Since 8.2) # # @downtime-limit: set maximum tolerated downtime for migration. # maximum downtime in milliseconds (Since 2.8) @@ -765,13 +817,6 @@ # @x-checkpoint-delay: The delay time (in ms) between two COLO # checkpoints in periodic mode. (Since 2.8) # -# @block-incremental: Affects how much storage is migrated when the -# block migration capability is enabled. When false, the entire -# storage backing chain is migrated into a flattened image at the -# destination; when true, only the active qcow2 layer is migrated -# and the destination must already have access to the same backing -# chain as was used on the source. (since 2.10) -# # @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) @@ -794,13 +839,18 @@ # 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) +# 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) +# 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 @@ -819,37 +869,50 @@ # 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) +# 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: Members @x-checkpoint-delay and @x-vcpu-dirty-limit-period -# are 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', + 'multifd-qatzip-level', 'block-bitmap-mapping', { 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] }, - 'vcpu-dirty-limit'] } + 'vcpu-dirty-limit', + 'mode', + 'zero-page-detection', + 'direct-io'] } ## # @MigrateSetParameters: @@ -866,32 +929,21 @@ # @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) +# 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) +# 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) +# 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 +# @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. @@ -899,8 +951,8 @@ # 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) +# 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 @@ -909,34 +961,42 @@ # 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. -# -# @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. -# -# @max-bandwidth: to set maximum speed for migration. maximum speed -# in bytes per second. (Since 2.8) +# rather than TLS. This is the default. (Since 2.7) # -# @downtime-limit: set maximum tolerated downtime for migration. -# maximum downtime in milliseconds (Since 2.8) +# @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) # -# @x-checkpoint-delay: the delay time between two COLO checkpoints. +# Note: empty value works only since 2.9. +# +# @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) +# +# @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 @@ -950,8 +1010,8 @@ # 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) @@ -960,13 +1020,18 @@ # 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) +# 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) +# 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 @@ -985,16 +1050,27 @@ # 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) +# 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: Members @x-checkpoint-delay and @x-vcpu-dirty-limit-period -# are 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 @@ -1006,10 +1082,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', @@ -1018,21 +1090,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' ], '*x-vcpu-dirty-limit-period': { 'type': 'uint64', 'features': [ 'unstable' ] }, - '*vcpu-dirty-limit': 'uint64'} } + '*vcpu-dirty-limit': 'uint64', + '*mode': 'MigMode', + '*zero-page-detection': 'ZeroPageDetection', + '*direct-io': 'bool' } } ## # @migrate-set-parameters: @@ -1041,11 +1117,11 @@ # # Since: 2.4 # -# Example: +# .. qmp-example:: # -# -> { "execute": "migrate-set-parameters" , -# "arguments": { "compress-level": 1 } } -# <- { "return": {} } +# -> { "execute": "migrate-set-parameters" , +# "arguments": { "multifd-channels": 5 } } +# <- { "return": {} } ## { 'command': 'migrate-set-parameters', 'boxed': true, 'data': 'MigrateSetParameters' } @@ -1067,32 +1143,21 @@ # @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) +# 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) # -# @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 +# @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. @@ -1100,8 +1165,8 @@ # 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) +# 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 @@ -1109,24 +1174,33 @@ # 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. -# -# @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. +# than TLS. (Since 2.7) +# +# Note: 2.8 omits empty @tls-creds 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. (Since 2.7) +# +# Note: 2.8 omits empty @tls-hostname instead. # # @tls-authz: ID of the 'authz' object subclass that provides access # control checking of the TLS x509 certificate distinguished name. # (Since 4.0) # -# @max-bandwidth: to set maximum speed for migration. maximum speed -# in bytes per second. (Since 2.8) +# @max-bandwidth: maximum speed for migration, in bytes per second. +# (Since 2.8) +# +# @avail-switchover-bandwidth: to set the available bandwidth that +# migration can use during switchover phase. **Note:** this does +# not limit the bandwidth during switchover, but only for +# calculations when making decisions to switchover. By default, +# this value is zero, which means QEMU will estimate the bandwidth +# automatically. This can be set when the estimated value is not +# accurate, while the user is able to guarantee such bandwidth is +# available when switching over. When specified correctly, this +# can make the switchover decision much more accurate. +# (Since 8.2) # # @downtime-limit: set maximum tolerated downtime for migration. # maximum downtime in milliseconds (Since 2.8) @@ -1134,13 +1208,6 @@ # @x-checkpoint-delay: the delay time between two COLO checkpoints. # (Since 2.8) # -# @block-incremental: Affects how much storage is migrated when the -# block migration capability is enabled. When false, the entire -# storage backing chain is migrated into a flattened image at the -# destination; when true, only the active qcow2 layer is migrated -# and the destination must already have access to the same backing -# chain as was used on the source. (since 2.10) -# # @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) @@ -1163,13 +1230,18 @@ # 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) +# 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) +# 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 @@ -1188,16 +1260,27 @@ # 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) +# 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: Members @x-checkpoint-delay and @x-vcpu-dirty-limit-period -# are experimental. +# @unstable: Members @x-checkpoint-delay and +# @x-vcpu-dirty-limit-period are experimental. # # Since: 2.4 ## @@ -1206,10 +1289,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', @@ -1218,44 +1297,46 @@ '*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' ], '*x-vcpu-dirty-limit-period': { 'type': 'uint64', 'features': [ 'unstable' ] }, - '*vcpu-dirty-limit': 'uint64'} } + '*vcpu-dirty-limit': 'uint64', + '*mode': 'MigMode', + '*zero-page-detection': 'ZeroPageDetection', + '*direct-io': 'bool' } } ## # @query-migrate-parameters: # -# Returns information about the current migration parameters +# Return information about the current migration parameters # # Returns: @MigrationParameters # # Since: 2.4 # -# Example: +# .. qmp-example:: # -# -> { "execute": "query-migrate-parameters" } -# <- { "return": { -# "decompress-threads": 2, -# "cpu-throttle-increment": 10, -# "compress-threads": 8, -# "compress-level": 1, -# "cpu-throttle-initial": 20, -# "max-bandwidth": 33554432, -# "downtime-limit": 300 -# } -# } +# -> { "execute": "query-migrate-parameters" } +# <- { "return": { +# "multifd-channels": 2, +# "cpu-throttle-increment": 10, +# "cpu-throttle-initial": 20, +# "max-bandwidth": 33554432, +# "downtime-limit": 300 +# } +# } ## { 'command': 'query-migrate-parameters', 'returns': 'MigrationParameters' } @@ -1269,10 +1350,10 @@ # # Since: 2.5 # -# Example: +# .. qmp-example:: # -# -> { "execute": "migrate-start-postcopy" } -# <- { "return": {} } +# -> { "execute": "migrate-start-postcopy" } +# <- { "return": {} } ## { 'command': 'migrate-start-postcopy' } @@ -1285,11 +1366,11 @@ # # Since: 2.4 # -# Example: +# .. qmp-example:: # -# <- {"timestamp": {"seconds": 1432121972, "microseconds": 744001}, -# "event": "MIGRATION", -# "data": {"status": "completed"} } +# <- {"timestamp": {"seconds": 1432121972, "microseconds": 744001}, +# "event": "MIGRATION", +# "data": {"status": "completed"} } ## { 'event': 'MIGRATION', 'data': {'status': 'MigrationStatus'}} @@ -1304,10 +1385,10 @@ # # Since: 2.6 # -# Example: +# .. qmp-example:: # -# <- { "timestamp": {"seconds": 1449669631, "microseconds": 239225}, -# "event": "MIGRATION_PASS", "data": {"pass": 2} } +# <- { "timestamp": {"seconds": 1449669631, "microseconds": 239225}, +# "event": "MIGRATION_PASS", "data": {"pass": 2} } ## { 'event': 'MIGRATION_PASS', 'data': { 'pass': 'int' } } @@ -1388,10 +1469,10 @@ # # Since: 3.1 # -# Example: +# .. qmp-example:: # -# <- { "timestamp": {"seconds": 2032141960, "microseconds": 417172}, -# "event": "COLO_EXIT", "data": {"mode": "primary", "reason": "request" } } +# <- { "timestamp": {"seconds": 2032141960, "microseconds": 417172}, +# "event": "COLO_EXIT", "data": {"mode": "primary", "reason": "request" } } ## { 'event': 'COLO_EXIT', 'data': {'mode': 'COLOMode', 'reason': 'COLOExitReason' } } @@ -1419,7 +1500,7 @@ ## # @x-colo-lost-heartbeat: # -# Tell qemu that heartbeat is lost, request it to do takeover +# 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 @@ -1431,10 +1512,10 @@ # # Since: 2.8 # -# Example: +# .. qmp-example:: # -# -> { "execute": "x-colo-lost-heartbeat" } -# <- { "return": {} } +# -> { "execute": "x-colo-lost-heartbeat" } +# <- { "return": {} } ## { 'command': 'x-colo-lost-heartbeat', 'features': [ 'unstable' ], @@ -1443,19 +1524,19 @@ ## # @migrate_cancel: # -# Cancel the current executing migration process. -# -# Returns: nothing on success +# Cancel the currently executing migration process. Allows a new +# migration to be started right after. When postcopy-ram is in use, +# cancelling is not allowed after the postcopy phase has started. # -# 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: +# .. qmp-example:: # -# -> { "execute": "migrate_cancel" } -# <- { "return": {} } +# -> { "execute": "migrate_cancel" } +# <- { "return": {} } ## { 'command': 'migrate_cancel' } @@ -1466,89 +1547,256 @@ # # @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'} } ## -# @migrate: +# @MigrationAddressType: # -# Migrates the current running guest to another Virtual Machine. +# The migration stream transport mechanisms. # -# @uri: the Uniform Resource Identifier of the destination VM +# @socket: Migrate via socket. # -# @blk: do block migration (full disk copy) +# @exec: Direct the migration stream to another process. # -# @inc: incremental disk copy migration +# @rdma: Migrate via RDMA. # -# @detach: this argument exists only for compatibility reasons and is -# ignored by QEMU +# @file: Direct the migration stream to a file. +# +# Since: 8.2 +## +{ 'enum': 'MigrationAddressType', + 'data': [ 'socket', 'exec', 'rdma', 'file' ] } + +## +# @FileMigrationArgs: # -# @resume: resume one paused migration, default "off". (since 3.0) +# @filename: The file to receive the migration stream # -# Returns: nothing on success +# @offset: The file offset where the migration stream will start # -# Since: 0.14 +# 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: # -# Notes: +# Migration endpoint configuration. # -# 1. The 'query-migrate' command should be used to check migration's -# progress and final result (this information is provided by the -# 'status' member) +# @transport: The migration stream transport mechanism # -# 2. All boolean arguments default to false +# Since: 8.2 +## +{ 'union': 'MigrationAddress', + 'base': { 'transport' : 'MigrationAddressType'}, + 'discriminator': 'transport', + 'data': { + 'socket': 'SocketAddress', + 'exec': 'MigrationExecCommand', + 'rdma': 'InetSocketAddress', + 'file': 'FileMigrationArgs' } } + +## +# @MigrationChannelType: # -# 3. The user Monitor's "detach" argument is invalid in QMP and should -# not be used +# The migration channel-type request options. # -# Example: +# @main: Main outbound migration channel. +# @cpr: Checkpoint and restart state channel. # -# -> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } } -# <- { "return": {} } +# Since: 8.1 ## -{ 'command': 'migrate', - 'data': {'uri': 'str', '*blk': 'bool', '*inc': 'bool', - '*detach': 'bool', '*resume': 'bool' } } +{ 'enum': 'MigrationChannelType', + 'data': [ 'main', 'cpr' ] } ## -# @migrate-incoming: +# @MigrationChannel: # -# Start an incoming migration, the qemu must have been started with -# -incoming defer +# Migration stream channel parameters. # -# @uri: The Uniform Resource Identifier identifying the source or -# address to listen on +# @channel-type: Channel type for transferring packet information. # -# Returns: nothing on success +# @addr: Migration endpoint configuration on destination interface. # -# Since: 2.3 +# Since: 8.1 +## +{ 'struct': 'MigrationChannel', + 'data': { + 'channel-type': 'MigrationChannelType', + 'addr': 'MigrationAddress' } } + +## +# @migrate: # -# Notes: +# Migrates the current running guest to another Virtual Machine. # -# 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. +# @uri: the Uniform Resource Identifier of the destination VM # -# 2. QEMU must be started with -incoming defer to allow -# migrate-incoming to be used. +# @channels: list of migration stream channels with each stream in the +# list connected to a destination interface endpoint. # -# 3. The uri format is the same as for -incoming +# @detach: this argument exists only for compatibility reasons and is +# ignored by QEMU # -# Example: +# @resume: resume one paused migration, default "off". (since 3.0) # -# -> { "execute": "migrate-incoming", -# "arguments": { "uri": "tcp::4446" } } -# <- { "return": {} } +# Features: +# +# @deprecated: Argument @detach is deprecated. +# +# Since: 0.14 +# +# .. 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). +# +# 2. The uri argument should have the Uniform Resource Identifier +# of default destination VM. This connection will be bound to +# default network. +# +# 3. For now, number of migration streams is restricted to one, +# i.e. number of items in 'channels' list is just 1. +# +# 4. 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', + '*channels': [ 'MigrationChannel' ], + '*detach': { 'type': 'bool', 'features': [ 'deprecated' ] }, + '*resume': 'bool' } } + ## -{ 'command': 'migrate-incoming', 'data': {'uri': 'str' } } +# @migrate-incoming: +# +# Start an incoming migration. QEMU must have been started with +# -incoming defer. +# +# @uri: The Uniform Resource Identifier identifying the source or +# address to listen on +# +# @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 +# +# .. 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. +# +# 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 +# +# 4. For now, number of migration streams is restricted to one, +# i.e. number of items in 'channels' list is just 1. +# +# 5. The 'uri' and 'channels' arguments are mutually exclusive; +# exactly one of the two should be present. +# +# .. 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', + '*channels': [ 'MigrationChannel' ], + '*exit-on-error': 'bool' } } ## # @xen-save-devices-state: @@ -1563,15 +1811,13 @@ # @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 -# # Since: 1.1 # -# Example: +# .. qmp-example:: # -# -> { "execute": "xen-save-devices-state", -# "arguments": { "filename": "/tmp/save" } } -# <- { "return": {} } +# -> { "execute": "xen-save-devices-state", +# "arguments": { "filename": "/tmp/save" } } +# <- { "return": {} } ## { 'command': 'xen-save-devices-state', 'data': {'filename': 'str', '*live':'bool' } } @@ -1583,15 +1829,13 @@ # # @enable: true to enable, false to disable. # -# Returns: nothing -# # Since: 1.3 # -# Example: +# .. qmp-example:: # -# -> { "execute": "xen-set-global-dirty-log", -# "arguments": { "enable": true } } -# <- { "return": {} } +# -> { "execute": "xen-set-global-dirty-log", +# "arguments": { "enable": true } } +# <- { "return": {} } ## { 'command': 'xen-set-global-dirty-log', 'data': { 'enable': 'bool' } } @@ -1607,11 +1851,11 @@ # # Since: 2.7 # -# Example: +# .. qmp-example:: # -# -> { "execute": "xen-load-devices-state", -# "arguments": { "filename": "/tmp/resume" } } -# <- { "return": {} } +# -> { "execute": "xen-load-devices-state", +# "arguments": { "filename": "/tmp/resume" } } +# <- { "return": {} } ## { 'command': 'xen-load-devices-state', 'data': {'filename': 'str'} } @@ -1624,16 +1868,14 @@ # # @primary: true for primary or false for secondary. # -# @failover: true to do failover, false to stop. but cannot be -# specified if 'enable' is true. default value is false. -# -# Returns: nothing. +# @failover: true to do failover, false to stop. Cannot be specified +# if 'enable' is true. Default value is false. # -# Example: +# .. qmp-example:: # -# -> { "execute": "xen-set-replication", -# "arguments": {"enable": true, "primary": false} } -# <- { "return": {} } +# -> { "execute": "xen-set-replication", +# "arguments": {"enable": true, "primary": false} } +# <- { "return": {} } # # Since: 2.9 ## @@ -1664,10 +1906,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 ## @@ -1680,12 +1922,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 ## @@ -1720,10 +1960,10 @@ # # 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 ## @@ -1738,13 +1978,11 @@ # # @uri: the URI to be used for the recovery of migration stream. # -# Returns: nothing. -# -# Example: +# .. qmp-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 ## @@ -1757,12 +1995,10 @@ # # Pause a migration. Currently it only supports postcopy. # -# Returns: nothing. -# -# Example: +# .. qmp-example:: # -# -> { "execute": "migrate-pause" } -# <- { "return": {} } +# -> { "execute": "migrate-pause" } +# <- { "return": {} } # # Since: 3.0 ## @@ -1772,19 +2008,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: +# .. qmp-example:: # -# <- { "event": "UNPLUG_PRIMARY", -# "data": { "device-id": "hostdev0" }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +# <- { "event": "UNPLUG_PRIMARY", +# "data": { "device-id": "hostdev0" }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'UNPLUG_PRIMARY', 'data': { 'device-id': 'str' } } @@ -1836,6 +2072,20 @@ { '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: # @@ -1848,8 +2098,10 @@ # # @start-time: start time in units of second for calculation # -# @calc-time: time period for which dirty page rate was measured -# (in seconds) +# @calc-time: time period for which dirty page rate was measured, +# expressed and rounded down to @calc-time-unit. +# +# @calc-time-unit: time unit of @calc-time (Since 8.2) # # @sample-pages: number of sampled pages per GiB of guest memory. # Valid only in page-sampling mode (Since 6.1) @@ -1866,6 +2118,7 @@ 'status': 'DirtyRateStatus', 'start-time': 'int64', 'calc-time': 'int64', + 'calc-time-unit': 'TimeUnit', 'sample-pages': 'uint64', 'mode': 'DirtyRateMeasureMode', '*vcpu-dirty-rate': [ 'DirtyRateVcpu' ] } } @@ -1901,13 +2154,17 @@ # This mode tracks page modification per each vCPU separately. It # requires that KVM accelerator property "dirty-ring-size" is set. # -# @calc-time: time period in units of second for which dirty page rate -# is calculated. 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 +# @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 @@ -1919,13 +2176,24 @@ # # Since: 5.2 # -# Example: +# .. qmp-example:: +# +# -> {"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": 1, -# 'sample-pages': 512} } -# <- { "return": {} } +# -> {"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'} } @@ -1934,21 +2202,27 @@ # # Query results of the most recent invocation of @calc-dirty-rate. # -# Since: 5.2 +# @calc-time-unit: time unit in which to report calculation time. +# By default it is reported in seconds. (Since 8.2) # -# Examples: +# Since: 5.2 # -# 1. Measurement is in progress: +# .. qmp-example:: +# :title: Measurement is in progress # -# <- {"status": "measuring", "sample-pages": 512, -# "mode": "page-sampling", "start-time": 3665220, "calc-time": 10} +# <- {"status": "measuring", "sample-pages": 512, +# "mode": "page-sampling", "start-time": 1693900454, "calc-time": 10, +# "calc-time-unit": "second"} # -# 2. Measurement has been completed: +# .. qmp-example:: +# :title: Measurement has been completed # -# <- {"status": "measured", "sample-pages": 512, "dirty-rate": 108, -# "mode": "page-sampling", "start-time": 3665220, "calc-time": 10} +# <- {"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: @@ -1984,12 +2258,12 @@ # # Since: 7.1 # -# Example: +# .. qmp-example:: # -# -> {"execute": "set-vcpu-dirty-limit"} -# "arguments": { "dirty-rate": 200, -# "cpu-index": 1 } } -# <- { "return": {} } +# -> {"execute": "set-vcpu-dirty-limit"} +# "arguments": { "dirty-rate": 200, +# "cpu-index": 1 } } +# <- { "return": {} } ## { 'command': 'set-vcpu-dirty-limit', 'data': { '*cpu-index': 'int', @@ -2008,11 +2282,11 @@ # # Since: 7.1 # -# Example: +# .. qmp-example:: # -# -> {"execute": "cancel-vcpu-dirty-limit"}, -# "arguments": { "cpu-index": 1 } } -# <- { "return": {} } +# -> {"execute": "cancel-vcpu-dirty-limit"}, +# "arguments": { "cpu-index": 1 } } +# <- { "return": {} } ## { 'command': 'cancel-vcpu-dirty-limit', 'data': { '*cpu-index': 'int'} } @@ -2020,17 +2294,17 @@ ## # @query-vcpu-dirty-limit: # -# Returns information about virtual CPU dirty page rate limits, if +# Return information about virtual CPU dirty page rate limits, if # any. # # Since: 7.1 # -# Example: +# .. 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}]} +# -> {"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' ] } @@ -2053,16 +2327,19 @@ ## # @query-migrationthreads: # -# Returns information of migration threads +# Return information of migration threads # -# data: migration thread name +# Features: +# +# @deprecated: This command is deprecated with no replacement yet. # -# Returns: information about migration threads +# Returns: @MigrationThreadInfo # # Since: 7.2 ## { 'command': 'query-migrationthreads', - 'returns': ['MigrationThreadInfo'] } + 'returns': ['MigrationThreadInfo'], + 'features': ['deprecated'] } ## # @snapshot-save: @@ -2091,44 +2368,42 @@ # # If @tag already exists, an error will be reported # -# Returns: nothing -# -# Example: -# -# -> { "execute": "snapshot-save", -# "arguments": { -# "job-id": "snapsave0", -# "tag": "my-snap", -# "vmstate": "disk0", -# "devices": ["disk0", "disk1"] -# } -# } -# <- { "return": { } } -# <- {"event": "JOB_STATUS_CHANGE", -# "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"}]} +# .. qmp-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"}]} # # Since: 6.0 ## @@ -2163,44 +2438,42 @@ # device nodes that can have changed since the original @snapshot-save # command execution. # -# Returns: nothing -# -# 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"}]} +# .. qmp-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"}]} # # Since: 6.0 ## @@ -2226,39 +2499,37 @@ # to determine completion and to fetch details of any errors that # arise. # -# Returns: nothing -# -# 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"}]} +# .. qmp-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"}]} # # Since: 6.0 ## diff --git a/qapi/misc-arm.json b/qapi/misc-arm.json new file mode 100644 index 00000000000..f5341372f5a --- /dev/null +++ b/qapi/misc-arm.json @@ -0,0 +1,49 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# +# SPDX-License-Identifier: GPL-2.0-or-later + +## +# @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. +# +# @version: version of GIC to be described. Currently, only 2 and 3 +# are supported. +# +# @emulated: whether current QEMU/hardware supports emulated GIC +# device in user space. +# +# @kernel: whether current QEMU/hardware supports hardware accelerated +# GIC device in kernel. +# +# Since: 2.6 +## +{ 'struct': 'GICCapability', + 'data': { 'version': 'int', + 'emulated': 'bool', + 'kernel': 'bool' } } + +## +# @query-gic-capabilities: +# +# It will return a list of GICCapability objects that describe its +# capability bits. +# +# On non-ARM targets this command will report an error as the GIC +# technology is not applicable. +# +# Returns: a list of GICCapability objects. +# +# Since: 2.6 +# +# .. 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'] } diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json new file mode 100644 index 00000000000..5fefa0a4840 --- /dev/null +++ b/qapi/misc-i386.json @@ -0,0 +1,486 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# +# SPDX-License-Identifier: GPL-2.0-or-later + +## +# @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. +# +# Use of this command is only applicable for x86 machines with an RTC, +# and on other machines will silently return without performing any +# action. +# +# Since: 2.1 +# +# .. qmp-example:: +# +# -> { "execute": "rtc-reset-reinjection" } +# <- { "return": {} } +## +{ 'command': 'rtc-reset-reinjection' } + +## +# @SevState: +# +# An enumeration of SEV state information used during @query-sev. +# +# @uninit: The guest is uninitialized. +# +# @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. +# +# @running: The guest is fully launched or migrated in. +# +# @send-update: The guest is currently being migrated out to another +# machine. +# +# @receive-update: The guest is currently being migrated from another +# machine. +# +# Since: 2.12 +## +{ 'enum': 'SevState', + 'data': ['uninit', 'launch-update', 'launch-secret', 'running', + 'send-update', 'receive-update' ] } + +## +# @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' ] } + +## +# @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' } } + +## +# @SevSnpGuestInfo: +# +# Information specific to SEV-SNP guests. +# +# @snp-policy: SEV-SNP policy value +# +# Since: 9.1 +## +{ 'struct': 'SevSnpGuestInfo', + 'data': { 'snp-policy': 'uint64' } } + +## +# @SevInfo: +# +# Information about Secure Encrypted Virtualization (SEV) support +# +# @enabled: true if SEV is active +# +# @api-major: SEV API major version +# +# @api-minor: SEV API minor version +# +# @build-id: SEV FW build id +# +# @state: SEV guest state +# +# @sev-type: Type of SEV guest being run +# +# Since: 2.12 +## +{ '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' } } + + +## +# @query-sev: +# +# Return information about SEV/SEV-ES/SEV-SNP. +# +# If unavailable due to an incompatible configuration the returned +# @enabled field is set to 'false' and the state of all other fields +# is unspecified. +# +# Returns: @SevInfo +# +# Since: 2.12 +# +# .. 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' } + +## +# @SevLaunchMeasureInfo: +# +# SEV Guest Launch measurement information +# +# @data: the measurement value encoded in base64 +# +# Since: 2.12 +## +{ 'struct': 'SevLaunchMeasureInfo', 'data': {'data': 'str'} } + +## +# @query-sev-launch-measure: +# +# Query the SEV/SEV-ES guest launch information. +# +# This is only valid on x86 machines configured with KVM and the +# 'sev-guest' confidential virtualization object. The launch +# measurement for SEV-SNP guests is only available within the guest. +# +# Returns: The @SevLaunchMeasureInfo for the guest +# +# Errors: +# - If the launch measurement is unavailable, either due to an +# invalid guest configuration or if the guest has not reached +# the required SEV state, GenericError +# +# Since: 2.12 +# +# .. qmp-example:: +# +# -> { "execute": "query-sev-launch-measure" } +# <- { "return": { "data": "4l8LXeNlSPUDlXPJG5966/8%YZ" } } +## +{ 'command': 'query-sev-launch-measure', 'returns': 'SevLaunchMeasureInfo' } + +## +# @SevCapability: +# +# The struct describes capability for a Secure Encrypted +# Virtualization feature. +# +# @pdh: Platform Diffie-Hellman key (base64 encoded) +# +# @cert-chain: PDH certificate chain (base64 encoded) +# +# @cpu0-id: Unique ID of CPU0 (base64 encoded) (since 7.1) +# +# @cbitpos: C-bit location in page table entry +# +# @reduced-phys-bits: Number of physical address bit reduction when +# SEV is enabled +# +# Since: 2.12 +## +{ 'struct': 'SevCapability', + 'data': { 'pdh': 'str', + 'cert-chain': 'str', + 'cpu0-id': 'str', + 'cbitpos': 'int', + 'reduced-phys-bits': 'int'} } + +## +# @query-sev-capabilities: +# +# Get SEV capabilities. +# +# This is only supported on AMD X86 platforms with KVM enabled. +# +# Returns: SevCapability objects. +# +# Errors: +# - If SEV is not available on the platform, GenericError +# +# Since: 2.12 +# +# .. 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' } + +## +# @sev-inject-launch-secret: +# +# This command injects a secret blob into memory of a SEV/SEV-ES +# guest. +# +# This is only valid on x86 machines configured with KVM and the +# 'sev-guest' confidential virtualization object. SEV-SNP guests do +# not support launch secret injection. +# +# @packet-header: the launch secret packet header encoded in base64 +# +# @secret: the launch secret data to be injected encoded in base64 +# +# @gpa: the guest physical address where secret will be injected. +# +# Errors: +# - If launch secret injection is not possible, either due to +# an invalid guest configuration, or if the guest has not +# reached the required SEV state, GenericError +# +# Since: 6.0 +## +{ 'command': 'sev-inject-launch-secret', + 'data': { 'packet-header': 'str', 'secret': 'str', '*gpa': 'uint64' } } + +## +# @SevAttestationReport: +# +# The struct describes attestation report for a Secure Encrypted +# Virtualization feature. +# +# @data: guest attestation report (base64 encoded) +# +# Since: 6.1 +## +{ 'struct': 'SevAttestationReport', + 'data': { 'data': 'str'} } + +## +# @query-sev-attestation-report: +# +# This command is used to get the SEV attestation report. +# +# This is only valid on x86 machines configured with KVM and the +# 'sev-guest' confidential virtualization object. The attestation +# report for SEV-SNP guests is only available within the guest. +# +# @mnonce: a random 16 bytes value encoded in base64 (it will be +# included in report) +# +# Returns: SevAttestationReport objects. +# +# Errors: +# - This will return an error if the attestation report is +# unavailable, either due to an invalid guest configuration +# or if the guest has not reached the required SEV state, +# GenericError +# +# Since: 6.1 +# +# .. qmp-example:: +# +# -> { "execute" : "query-sev-attestation-report", +# "arguments": { "mnonce": "aaaaaaa" } } +# <- { "return" : { "data": "aaaaaaaabbbddddd"} } +## +{ 'command': 'query-sev-attestation-report', + 'data': { 'mnonce': 'str' }, + 'returns': 'SevAttestationReport' } + +## +# @SgxEpcSection: +# +# Information about intel SGX EPC section +# +# @node: the numa node +# +# @size: the size of EPC section +# +# Since: 7.0 +## +{ 'struct': 'SgxEpcSection', + 'data': { 'node': 'int', + 'size': 'uint64'}} + +## +# @SgxInfo: +# +# Information about intel Safe Guard eXtension (SGX) support +# +# @sgx: true if SGX is supported +# +# @sgx1: true if SGX1 is supported +# +# @sgx2: true if SGX2 is supported +# +# @flc: true if FLC is supported +# +# @sections: The EPC sections information (Since: 7.0) +# +# Since: 6.2 +## +{ 'struct': 'SgxInfo', + 'data': { 'sgx': 'bool', + 'sgx1': 'bool', + 'sgx2': 'bool', + 'flc': 'bool', + 'sections': ['SgxEpcSection']} } + +## +# @query-sgx: +# +# Return information about configured SGX capabilities of guest +# +# Returns: @SgxInfo +# +# Since: 6.2 +# +# .. 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' } + +## +# @query-sgx-capabilities: +# +# Return information about SGX capabilities of host +# +# Returns: @SgxInfo +# +# Since: 6.2 +# +# .. 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' } + +## +# @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'] } + +## +# @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'} } + + +## +# @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'] } + +## +# @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' } } diff --git a/qapi/misc-target.json b/qapi/misc-target.json deleted file mode 100644 index 88291453ba4..00000000000 --- a/qapi/misc-target.json +++ /dev/null @@ -1,489 +0,0 @@ -# -*- Mode: Python -*- -# vim: filetype=python -# - -## -# @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. -# -# Since: 2.1 -# -# Example: -# -# -> { "execute": "rtc-reset-reinjection" } -# <- { "return": {} } -## -{ 'command': 'rtc-reset-reinjection', - 'if': 'TARGET_I386' } - -## -# @SevState: -# -# An enumeration of SEV state information used during @query-sev. -# -# @uninit: The guest is uninitialized. -# -# @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. -# -# @running: The guest is fully launched or migrated in. -# -# @send-update: The guest is currently being migrated out to another -# machine. -# -# @receive-update: The guest is currently being migrated from another -# machine. -# -# Since: 2.12 -## -{ 'enum': 'SevState', - 'data': ['uninit', 'launch-update', 'launch-secret', 'running', - 'send-update', 'receive-update' ], - 'if': 'TARGET_I386' } - -## -# @SevInfo: -# -# Information about Secure Encrypted Virtualization (SEV) support -# -# @enabled: true if SEV is active -# -# @api-major: SEV API major version -# -# @api-minor: SEV API minor version -# -# @build-id: SEV FW build id -# -# @policy: SEV policy value -# -# @state: SEV guest state -# -# @handle: SEV firmware handle -# -# 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' -} - -## -# @query-sev: -# -# Returns information about SEV -# -# Returns: @SevInfo -# -# 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 } } -## -{ 'command': 'query-sev', 'returns': 'SevInfo', - 'if': 'TARGET_I386' } - -## -# @SevLaunchMeasureInfo: -# -# SEV Guest Launch measurement information -# -# @data: the measurement value encoded in base64 -# -# Since: 2.12 -## -{ 'struct': 'SevLaunchMeasureInfo', 'data': {'data': 'str'}, - 'if': 'TARGET_I386' } - -## -# @query-sev-launch-measure: -# -# Query the SEV guest launch information. -# -# Returns: The @SevLaunchMeasureInfo for the guest -# -# Since: 2.12 -# -# Example: -# -# -> { "execute": "query-sev-launch-measure" } -# <- { "return": { "data": "4l8LXeNlSPUDlXPJG5966/8%YZ" } } -## -{ 'command': 'query-sev-launch-measure', 'returns': 'SevLaunchMeasureInfo', - 'if': 'TARGET_I386' } - -## -# @SevCapability: -# -# The struct describes capability for a Secure Encrypted -# Virtualization feature. -# -# @pdh: Platform Diffie-Hellman key (base64 encoded) -# -# @cert-chain: PDH certificate chain (base64 encoded) -# -# @cpu0-id: Unique ID of CPU0 (base64 encoded) (since 7.1) -# -# @cbitpos: C-bit location in page table entry -# -# @reduced-phys-bits: Number of physical Address bit reduction when -# SEV is enabled -# -# Since: 2.12 -## -{ 'struct': 'SevCapability', - 'data': { 'pdh': 'str', - 'cert-chain': 'str', - 'cpu0-id': 'str', - 'cbitpos': 'int', - 'reduced-phys-bits': 'int'}, - 'if': 'TARGET_I386' } - -## -# @query-sev-capabilities: -# -# 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": 1}} -## -{ 'command': 'query-sev-capabilities', 'returns': 'SevCapability', - 'if': 'TARGET_I386' } - -## -# @sev-inject-launch-secret: -# -# This command injects a secret blob into memory of SEV guest. -# -# @packet-header: the launch secret packet header encoded in base64 -# -# @secret: the launch secret data to be injected encoded in base64 -# -# @gpa: the guest physical address where secret will be injected. -# -# Since: 6.0 -## -{ 'command': 'sev-inject-launch-secret', - 'data': { 'packet-header': 'str', 'secret': 'str', '*gpa': 'uint64' }, - 'if': 'TARGET_I386' } - -## -# @SevAttestationReport: -# -# The struct describes attestation report for a Secure Encrypted -# Virtualization feature. -# -# @data: guest attestation report (base64 encoded) -# -# Since: 6.1 -## -{ 'struct': 'SevAttestationReport', - 'data': { 'data': 'str'}, - 'if': 'TARGET_I386' } - -## -# @query-sev-attestation-report: -# -# This command is used to get the SEV attestation report, and is -# supported on AMD X86 platforms only. -# -# @mnonce: a random 16 bytes value encoded in base64 (it will be -# included in report) -# -# Returns: SevAttestationReport objects. -# -# Since: 6.1 -# -# Example: -# -# -> { "execute" : "query-sev-attestation-report", -# "arguments": { "mnonce": "aaaaaaa" } } -# <- { "return" : { "data": "aaaaaaaabbbddddd"} } -## -{ 'command': 'query-sev-attestation-report', - 'data': { 'mnonce': 'str' }, - 'returns': 'SevAttestationReport', - 'if': 'TARGET_I386' } - -## -# @dump-skeys: -# -# Dump guest's storage keys -# -# @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": {} } -## -{ 'command': 'dump-skeys', - 'data': { 'filename': 'str' }, - 'if': 'TARGET_S390X' } - -## -# @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. -# -# @version: version of GIC to be described. Currently, only 2 and 3 -# are supported. -# -# @emulated: whether current QEMU/hardware supports emulated GIC -# device in user space. -# -# @kernel: whether current QEMU/hardware supports hardware accelerated -# GIC device in kernel. -# -# Since: 2.6 -## -{ 'struct': 'GICCapability', - 'data': { 'version': 'int', - 'emulated': 'bool', - 'kernel': 'bool' }, - 'if': 'TARGET_ARM' } - -## -# @query-gic-capabilities: -# -# 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 } ] } -## -{ 'command': 'query-gic-capabilities', 'returns': ['GICCapability'], - 'if': 'TARGET_ARM' } - -## -# @SGXEPCSection: -# -# Information about intel SGX EPC section info -# -# @node: the numa node -# -# @size: the size of EPC section -# -# Since: 7.0 -## -{ 'struct': 'SGXEPCSection', - 'data': { 'node': 'int', - 'size': 'uint64'}} - -## -# @SGXInfo: -# -# Information about intel Safe Guard eXtension (SGX) support -# -# @sgx: true if SGX is supported -# -# @sgx1: true if SGX1 is supported -# -# @sgx2: true if SGX2 is supported -# -# @flc: true if FLC is supported -# -# @sections: The EPC sections info for guest (Since: 7.0) -# -# Since: 6.2 -## -{ 'struct': 'SGXInfo', - 'data': { 'sgx': 'bool', - 'sgx1': 'bool', - 'sgx2': 'bool', - 'flc': 'bool', - 'sections': ['SGXEPCSection']}, - 'if': 'TARGET_I386' } - -## -# @query-sgx: -# -# Returns information about SGX -# -# Returns: @SGXInfo -# -# Since: 6.2 -# -# 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' } - -## -# @query-sgx-capabilities: -# -# Returns information from host SGX capabilities -# -# Returns: @SGXInfo -# -# Since: 6.2 -# -# 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 -# -# 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 -# -# Returns: -# - Nothing on success. -# -# Since: 8.0 -# -# Example: -# -# -> { "execute": "xen-event-inject", "arguments": { "port": 1 } } -# <- { "return": { } } -## -{ 'command': 'xen-event-inject', - 'data': { 'port': 'uint32' }, - 'if': 'TARGET_I386' } diff --git a/qapi/misc.json b/qapi/misc.json index cda2effa815..4b9e601cfa5 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -26,17 +26,15 @@ # @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: +# .. qmp-example:: # -# -> { "execute": "add_client", "arguments": { "protocol": "vnc", -# "fdname": "myclient" } } -# <- { "return": {} } +# -> { "execute": "add_client", "arguments": { "protocol": "vnc", +# "fdname": "myclient" } } +# <- { "return": {} } ## { 'command': 'add_client', 'data': { 'protocol': 'str', 'fdname': 'str', '*skipauth': 'bool', @@ -62,10 +60,10 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # -# -> { "execute": "query-name" } -# <- { "return": { "name": "qemu-name" } } +# -> { "execute": "query-name" } +# <- { "return": { "name": "qemu-name" } } ## { 'command': 'query-name', 'returns': 'NameInfo', 'allow-preconfig': true } @@ -103,30 +101,30 @@ ## # @query-iothreads: # -# Returns a list of information about each iothread. +# Return 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,41 +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: +# .. qmp-example:: # -# -> { "execute": "cont" } -# <- { "return": {} } +# -> { "execute": "cont" } +# <- { "return": {} } ## { 'command': 'cont' } @@ -189,12 +192,10 @@ # # Since: 3.0 # -# Returns: nothing -# -# Example: +# .. qmp-example:: # -# -> { "execute": "x-exit-preconfig" } -# <- { "return": {} } +# -> { "execute": "x-exit-preconfig" } +# <- { "return": {} } ## { 'command': 'x-exit-preconfig', 'allow-preconfig': true, 'features': [ 'unstable' ] } @@ -218,24 +219,24 @@ # # 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: +# .. qmp-example:: # -# -> { "execute": "human-monitor-command", -# "arguments": { "command-line": "info kvm" } } -# <- { "return": "kvm support: enabled\r\n" } +# -> { "execute": "human-monitor-command", +# "arguments": { "command-line": "info kvm" } } +# <- { "return": "kvm support: enabled\r\n" } ## { 'command': 'human-monitor-command', 'data': {'command-line': 'str', '*cpu-index': 'int'}, @@ -249,20 +250,18 @@ # # @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: +# .. qmp-example:: # -# -> { "execute": "getfd", "arguments": { "fdname": "fd1" } } -# <- { "return": {} } +# -> { "execute": "getfd", "arguments": { "fdname": "fd1" } } +# <- { "return": {} } ## { 'command': 'getfd', 'data': {'fdname': 'str'}, 'if': 'CONFIG_POSIX' } @@ -278,20 +277,19 @@ # # @fdname: file descriptor name # -# Returns: Nothing on success -# # Since: 8.0 # -# 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: +# .. qmp-example:: # -# -> { "execute": "get-win32-socket", "arguments": { "info": "abcd123..", fdname": "skclient" } } -# <- { "return": {} } +# -> { "execute": "get-win32-socket", +# "arguments": { "info": "abcd123..", "fdname": "skclient" } } +# <- { "return": {} } ## { 'command': 'get-win32-socket', 'data': {'info': 'str', 'fdname': 'str'}, 'if': 'CONFIG_WIN32' } @@ -302,14 +300,12 @@ # # @fdname: file descriptor name # -# Returns: Nothing on success -# # Since: 0.14 # -# Example: +# .. qmp-example:: # -# -> { "execute": "closefd", "arguments": { "fdname": "fd1" } } -# <- { "return": {} } +# -> { "execute": "closefd", "arguments": { "fdname": "fd1" } } +# <- { "return": {} } ## { 'command': 'closefd', 'data': {'fdname': 'str'} } @@ -337,20 +333,23 @@ # @opaque: A free-form string that can be used to describe the fd. # # Returns: -# - @AddfdInfo on success -# - If file descriptor was not received, GenericError -# - If @fdset-id is a negative value, GenericError +# @AddfdInfo +# +# Errors: +# - If file descriptor was not received, GenericError +# - If @fdset-id is a negative value, GenericError # -# 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 @fdset-id is not specified, a new fd set will be created. +# .. note:: If @fdset-id is not specified, a new fd set will be +# created. # # Since: 1.2 # -# Example: +# .. qmp-example:: # -# -> { "execute": "add-fd", "arguments": { "fdset-id": 1 } } -# <- { "return": { "fdset-id": 1, "fd": 3 } } +# -> { "execute": "add-fd", "arguments": { "fdset-id": 1 } } +# <- { "return": { "fdset-id": 1, "fd": 3 } } ## { 'command': 'add-fd', 'data': { '*fdset-id': 'int', @@ -366,21 +365,20 @@ # # @fd: The file descriptor that is to be removed. # -# Returns: -# - Nothing on success -# - If @fdset-id or @fd is not found, GenericError +# 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: +# .. qmp-example:: # -# -> { "execute": "remove-fd", "arguments": { "fdset-id": 1, "fd": 3 } } -# <- { "return": {} } +# -> { "execute": "remove-fd", "arguments": { "fdset-id": 1, "fd": 3 } } +# <- { "return": {} } ## { 'command': 'remove-fd', 'data': {'fdset-id': 'int', '*fd': 'int'} } @@ -421,38 +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'] } @@ -519,31 +517,33 @@ # @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. +# 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'}, @@ -560,17 +560,17 @@ # # @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: +# .. qmp-example:: # -# <- { "event": "RTC_CHANGE", -# "data": { "offset": 78 }, -# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } +# <- { "event": "RTC_CHANGE", +# "data": { "offset": 78 }, +# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } ## { 'event': 'RTC_CHANGE', 'data': { 'offset': 'int', 'qom-path': 'str' } } @@ -593,14 +593,14 @@ # # Since: 7.1 # -# Example: +# .. 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": "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 313c8a606e9..97ea1839813 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -6,7 +6,6 @@ # = Net devices ## -{ 'include': 'common.json' } { 'include': 'sockets.json' } ## @@ -18,20 +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: +# .. qmp-example:: # -# -> { "execute": "set_link", -# "arguments": { "name": "e1000.0", "up": false } } -# <- { "return": {} } +# -> { "execute": "set_link", +# "arguments": { "name": "e1000.0", "up": false } } +# <- { "return": {} } ## { 'command': 'set_link', 'data': {'name': 'str', 'up': 'bool'} } @@ -44,15 +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: +# .. qmp-example:: # -# -> { "execute": "netdev_add", -# "arguments": { "type": "user", "id": "netdev1", -# "dnssearch": [ { "str": "example.org" } ] } } -# <- { "return": {} } +# -> { "execute": "netdev_add", +# "arguments": { "type": "user", "id": "netdev1", +# "dnssearch": [ { "str": "example.org" } ] } } +# <- { "return": {} } ## { 'command': 'netdev_add', 'data': 'Netdev', 'boxed': true, 'allow-preconfig': true } @@ -64,15 +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: +# .. qmp-example:: # -# -> { "execute": "netdev_del", "arguments": { "id": "netdev1" } } -# <- { "return": {} } +# -> { "execute": "netdev_del", "arguments": { "id": "netdev1" } } +# <- { "return": {} } ## { 'command': 'netdev_del', 'data': {'id': 'str'}, 'allow-preconfig': true } @@ -102,6 +101,17 @@ '*addr': 'str', '*vectors': 'uint32' } } +## +# @String: +# +# A fat type wrapping 'str', to be embedded in lists. +# +# Since: 1.2 +## +{ 'struct': 'String', + 'data': { + 'str': 'str' } } + ## # @NetdevUserOptions: # @@ -140,12 +150,12 @@ # @domainname: guest-visible domain name of the virtual nameserver # (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::). The network +# prefix is given in the usual hexadecimal IPv6 address notation. +# (since 2.6) # -# @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) # @@ -377,8 +387,8 @@ # # @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 ## @@ -393,7 +403,7 @@ # 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 +# 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 @@ -408,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: # @@ -443,8 +510,8 @@ # @queues: number of queues to be created for multiqueue vhost-vdpa # (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: # @@ -468,13 +535,13 @@ # 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 +# 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 +# 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 @@ -489,7 +556,7 @@ # 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. +# identified by the UUID. Requires at least macOS Big Sur 11.0. # # Since: 7.1 ## @@ -508,20 +575,20 @@ # vmnet (shared mode) network backend. # # Allows traffic originating from the vmnet interface to reach the -# Internet through a network address translator (NAT). The vmnet +# 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 +# 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 +# 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 @@ -583,15 +650,26 @@ # 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', - '*reconnect': 'uint32' } } + '*reconnect': { 'type': 'int', 'features': [ 'deprecated' ] }, + '*reconnect-ms': 'int' } } ## # @NetdevDgramOptions: @@ -636,19 +714,28 @@ # Available netdev drivers. # # @l2tpv3: since 2.1 +# # @vhost-vdpa: since 5.1 +# # @vmnet-host: since 7.1 +# # @vmnet-shared: since 7.1 +# # @vmnet-bridged: since 7.1 +# # @stream: since 7.2 +# # @dgram: since 7.2 # +# @af-xdp: since 8.2 +# # Since: 2.7 ## { 'enum': 'NetClientDriver', 'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'stream', 'dgram', 'vde', 'bridge', 'hubport', 'netmap', 'vhost-user', 'vhost-vdpa', + { 'name': 'af-xdp', 'if': 'CONFIG_AF_XDP' }, { 'name': 'vmnet-host', 'if': 'CONFIG_VMNET' }, { 'name': 'vmnet-shared', 'if': 'CONFIG_VMNET' }, { 'name': 'vmnet-bridged', 'if': 'CONFIG_VMNET' }] } @@ -679,6 +766,8 @@ 'bridge': 'NetdevBridgeOptions', 'hubport': 'NetdevHubPortOptions', 'netmap': 'NetdevNetmapOptions', + 'af-xdp': { 'type': 'NetdevAFXDPOptions', + 'if': 'CONFIG_AF_XDP' }, 'vhost-user': 'NetdevVhostUserOptions', 'vhost-vdpa': 'NetdevVhostVDPAOptions', 'vmnet-host': { 'type': 'NetdevVmnetHostOptions', @@ -757,40 +846,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' }, @@ -808,12 +899,12 @@ # # Since: 1.6 # -# Example: +# .. 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": "vnet0", +# "path": "/machine/peripheral/vnet0/virtio-backend" }, +# "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } ## { 'event': 'NIC_RX_FILTER_CHANGED', 'data': { '*name': 'str', 'path': 'str' } } @@ -857,13 +948,15 @@ # 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 ## @@ -882,11 +975,11 @@ # # Since: 4.2 # -# Example: +# .. qmp-example:: # -# <- { "event": "FAILOVER_NEGOTIATED", -# "data": { "device-id": "net1" }, -# "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } +# <- { "event": "FAILOVER_NEGOTIATED", +# "data": { "device-id": "net1" }, +# "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } ## { 'event': 'FAILOVER_NEGOTIATED', 'data': {'device-id': 'str'} } @@ -902,18 +995,20 @@ # # Since: 7.2 # -# Examples: +# .. 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 } } +# .. 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": "netdev0", +# "addr": { "path": "/tmp/qemu0", "type": "unix" } }, +# "timestamp": { "seconds": 1666269706, "microseconds": 413651 } } ## { 'event': 'NETDEV_STREAM_CONNECTED', 'data': { 'netdev-id': 'str', @@ -928,11 +1023,51 @@ # # Since: 7.2 # -# Example: +# .. qmp-example:: # -# <- { 'event': 'NETDEV_STREAM_DISCONNECTED', -# 'data': {'netdev-id': 'netdev0'}, -# 'timestamp': {'seconds': 1663330937, 'microseconds': 526695} } +# <- { "event": "NETDEV_STREAM_DISCONNECTED", +# "data": {"netdev-id": "netdev0"}, +# "timestamp": {"seconds": 1663330937, "microseconds": 526695} } ## { 'event': 'NETDEV_STREAM_DISCONNECTED', 'data': { 'netdev-id': 'str' } } + +## +# @NETDEV_VHOST_USER_CONNECTED: +# +# Emitted when the vhost-user chardev is connected +# +# @netdev-id: QEMU netdev id that is connected +# +# @chardev-id: The character device id used by the QEMU netdev +# +# Since: 10.0 +# +# .. qmp-example:: +# +# <- { "timestamp": {"seconds": 1739538638, "microseconds": 354181 }, +# "event": "NETDEV_VHOST_USER_CONNECTED", +# "data": { "netdev-id": "netdev0", "chardev-id": "chr0" } } +# +## +{ 'event': 'NETDEV_VHOST_USER_CONNECTED', + 'data': { 'netdev-id': 'str', 'chardev-id': 'str' } } + +## +# @NETDEV_VHOST_USER_DISCONNECTED: +# +# Emitted when the vhost-user chardev is disconnected +# +# @netdev-id: QEMU netdev id that is disconnected +# +# Since: 10.0 +# +# .. qmp-example:: +# +# <- { "timestamp": { "seconds": 1739538634, "microseconds": 920450 }, +# "event": "NETDEV_VHOST_USER_DISCONNECTED", +# "data": { "netdev-id": "netdev0" } } +# +## +{ 'event': 'NETDEV_VHOST_USER_DISCONNECTED', + 'data': { 'netdev-id': 'str' } } diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index 8f1efab8b9d..3d1a28b4191 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; diff --git a/qapi/pci.json b/qapi/pci.json index 086c7730528..dc85a41d28b 100644 --- a/qapi/pci.json +++ b/qapi/pci.json @@ -33,6 +33,8 @@ # - 'io' if the region is a PIO region # - 'memory' if the region is a MMIO region # +# @address: memory address +# # @size: memory size # # @prefetch: if @type is 'memory', true if the memory is prefetchable @@ -93,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 # @@ -146,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', @@ -182,136 +182,135 @@ # # Since: 0.14 # -# Example: -# -# -> { "execute": "query-pci" } -# <- { "return": [ -# { -# "bus": 0, -# "devices": [ -# { -# "bus": 0, -# "qdev_id": "", -# "slot": 0, -# "class_info": { -# "class": 1536, -# "desc": "Host bridge" -# }, -# "id": { -# "device": 32902, -# "vendor": 4663 -# }, -# "function": 0, -# "regions": [ -# ] -# }, -# { -# "bus": 0, -# "qdev_id": "", -# "slot": 1, -# "class_info": { -# "class": 1537, -# "desc": "ISA bridge" -# }, -# "id": { -# "device": 32902, -# "vendor": 28672 -# }, -# "function": 0, -# "regions": [ -# ] -# }, -# { -# "bus": 0, -# "qdev_id": "", -# "slot": 1, -# "class_info": { -# "class": 257, -# "desc": "IDE controller" -# }, -# "id": { -# "device": 32902, -# "vendor": 28688 -# }, -# "function": 1, -# "regions": [ -# { -# "bar": 4, -# "size": 16, -# "address": 49152, -# "type": "io" -# } -# ] -# }, -# { -# "bus": 0, -# "qdev_id": "", -# "slot": 2, -# "class_info": { -# "class": 768, -# "desc": "VGA controller" -# }, -# "id": { -# "device": 4115, -# "vendor": 184 -# }, -# "function": 0, -# "regions": [ -# { -# "prefetch": true, -# "mem_type_64": false, -# "bar": 0, -# "size": 33554432, -# "address": 4026531840, -# "type": "memory" -# }, -# { -# "prefetch": false, -# "mem_type_64": false, -# "bar": 1, -# "size": 4096, -# "address": 4060086272, -# "type": "memory" -# }, -# { -# "prefetch": false, -# "mem_type_64": false, -# "bar": 6, -# "size": 65536, -# "address": -1, -# "type": "memory" -# } -# ] -# }, -# { -# "bus": 0, -# "qdev_id": "", -# "irq": 11, -# "slot": 4, -# "class_info": { -# "class": 1280, -# "desc": "RAM controller" -# }, -# "id": { -# "device": 6900, -# "vendor": 4098 -# }, -# "function": 0, -# "regions": [ -# { -# "bar": 0, -# "size": 32, -# "address": 49280, -# "type": "io" -# } -# ] -# } -# ] -# } -# ] -# } -# -# Note: This example has been shortened as the real response is too -# long. +# .. 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" +# } +# ] +# } +# ] +# } +# ] +# } +# +# 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 7f810b0e977..023a2ef7bcd 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 c45c5caa3b8..30997638de5 100644 --- a/qapi/qapi-clone-visitor.c +++ b/qapi/qapi-clone-visitor.c @@ -12,7 +12,7 @@ #include "qapi/clone-visitor.h" #include "qapi/visitor-impl.h" #include "qapi/error.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" struct QapiCloneVisitor { Visitor visitor; @@ -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-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c index ef283f29660..57a2c904bb1 100644 --- a/qapi/qapi-dealloc-visitor.c +++ b/qapi/qapi-dealloc-visitor.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "qapi/dealloc-visitor.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "qapi/visitor-impl.h" struct QapiDeallocVisitor diff --git a/qapi/qapi-forward-visitor.c b/qapi/qapi-forward-visitor.c index e36d9bc9ba7..d91d921bdbd 100644 --- a/qapi/qapi-forward-visitor.c +++ b/qapi/qapi-forward-visitor.c @@ -14,14 +14,14 @@ #include "qapi/forward-visitor.h" #include "qapi/visitor-impl.h" #include "qemu/queue.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qemu/cutils.h" struct ForwardFieldVisitor { @@ -246,7 +246,7 @@ static void forward_field_optional(Visitor *v, const char *name, bool *present) } static bool forward_field_policy_reject(Visitor *v, const char *name, - unsigned special_features, + uint64_t features, Error **errp) { ForwardFieldVisitor *ffv = to_ffv(v); @@ -254,18 +254,18 @@ static bool forward_field_policy_reject(Visitor *v, const char *name, if (!forward_field_translate_name(ffv, &name, errp)) { return true; } - return visit_policy_reject(ffv->target, name, special_features, errp); + return visit_policy_reject(ffv->target, name, features, errp); } static bool forward_field_policy_skip(Visitor *v, const char *name, - unsigned special_features) + uint64_t features) { ForwardFieldVisitor *ffv = to_ffv(v); if (!forward_field_translate_name(ffv, &name, NULL)) { return true; } - return visit_policy_skip(ffv->target, name, special_features); + return visit_policy_skip(ffv->target, name, features); } static void forward_field_complete(Visitor *v, void *opaque) diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 6594afba312..a8f66163cb7 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -3,35 +3,24 @@ ## # = Introduction # -# This document describes all commands currently supported by QMP. +# This manual describes the commands and events supported by the QEMU +# Monitor Protocol (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. +# For locating a particular item, please see the `qapi-qmp-index`. # -# 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. +# The following notation is used in examples: # -# 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. +# .. qmp-example:: # -# Also, the following notation is used to denote data flow: +# -> ... text sent by client (commands) ... +# <- ... text sent by server (command responses and events) ... # -# Example: -# -# :: -# -# -> data issued by the Client -# <- Server data response +# Example text is formatted for readability. However, in real +# protocol usage, its commonly emitted as a single line. # # Please refer to the # :doc:`QEMU Machine Protocol Specification ` -# for detailed information on the Server command and response formats. +# for the general format of commands, responses, and events. ## { 'include': 'pragma.json' } @@ -53,7 +42,7 @@ { 'include': 'char.json' } { 'include': 'dump.json' } { 'include': 'net.json' } -{ 'include': 'rdma.json' } +{ 'include': 'ebpf.json' } { 'include': 'rocker.json' } { 'include': 'tpm.json' } { 'include': 'ui.json' } @@ -66,16 +55,20 @@ { 'include': 'introspect.json' } { 'include': 'qom.json' } { 'include': 'qdev.json' } +{ 'include': 'machine-common.json' } { 'include': 'machine.json' } -{ 'include': 'machine-target.json' } +{ 'include': 'machine-s390x.json' } { 'include': 'replay.json' } { 'include': 'yank.json' } { 'include': 'misc.json' } -{ 'include': 'misc-target.json' } +{ 'include': 'misc-arm.json' } +{ 'include': 'misc-i386.json' } { 'include': 'audio.json' } { 'include': 'acpi.json' } { 'include': 'pci.json' } { 'include': 'stats.json' } { 'include': 'virtio.json' } +{ 'include': 'vfio.json' } { 'include': 'cryptodev.json' } { 'include': 'cxl.json' } +{ 'include': 'uefi.json' } diff --git a/qapi/qapi-type-helpers.c b/qapi/qapi-type-helpers.c index f76b34f647e..266da013ad6 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 63596e11c56..3d849fe0347 100644 --- a/qapi/qapi-util.c +++ b/qapi/qapi-util.c @@ -37,19 +37,19 @@ static bool compat_policy_input_ok1(const char *adjective, } } -bool compat_policy_input_ok(unsigned special_features, +bool compat_policy_input_ok(uint64_t features, const CompatPolicy *policy, ErrorClass error_class, const char *kind, const char *name, Error **errp) { - if ((special_features & 1u << QAPI_DEPRECATED) + if ((features & 1u << QAPI_DEPRECATED) && !compat_policy_input_ok1("Deprecated", policy->deprecated_input, error_class, kind, name, errp)) { return false; } - if ((special_features & (1u << QAPI_UNSTABLE)) + if ((features & (1u << QAPI_UNSTABLE)) && !compat_policy_input_ok1("Unstable", policy->unstable_input, error_class, kind, name, errp)) { @@ -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/qapi-visit-core.c b/qapi/qapi-visit-core.c index 6c13510a2bc..706c61e0260 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -141,21 +141,21 @@ bool visit_optional(Visitor *v, const char *name, bool *present) } bool visit_policy_reject(Visitor *v, const char *name, - unsigned special_features, Error **errp) + uint64_t features, Error **errp) { trace_visit_policy_reject(v, name); if (v->policy_reject) { - return v->policy_reject(v, name, special_features, errp); + return v->policy_reject(v, name, features, errp); } return false; } bool visit_policy_skip(Visitor *v, const char *name, - unsigned special_features) + uint64_t features) { trace_visit_policy_skip(v, name); if (v->policy_skip) { - return v->policy_skip(v, name, special_features); + return v->policy_skip(v, name, features); } return false; } @@ -409,8 +409,8 @@ static bool input_type_enum(Visitor *v, const char *name, int *obj, return false; } - if (lookup->special_features - && !compat_policy_input_ok(lookup->special_features[value], + if (lookup->features + && !compat_policy_input_ok(lookup->features[value], &v->compat_policy, ERROR_CLASS_GENERIC_ERROR, "value", enum_str, errp)) { diff --git a/qapi/qdev.json b/qapi/qdev.json index 6bc5a733b86..32c7d100463 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -20,9 +20,9 @@ # 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 ## @@ -51,24 +51,24 @@ # supports JSON syntax without the reference counting leak that # broke hot-unplug # -# Notes: +# .. admonition:: Notes # -# 1. Additional arguments depend on the type. +# 1. Additional arguments depend on the type. # -# 2. For detailed information about this command, please refer to the -# 'docs/qdev-device-use.txt' file. +# 2. For detailed information about this command, please refer to +# the 'docs/qdev-device-use.txt' file. # -# 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 +# 3. It's possible to list device properties by running QEMU with +# the ``-device DEVICE,help`` command-line argument, where +# DEVICE is the device's name. # -# Example: +# .. qmp-example:: # -# -> { "execute": "device_add", -# "arguments": { "driver": "e1000", "id": "net1", -# "bus": "pci.0", -# "mac": "52:54:00:12:34:56" } } -# <- { "return": {} } +# -> { "execute": "device_add", +# "arguments": { "driver": "e1000", "id": "net1", +# "bus": "pci.0", +# "mac": "52:54:00:12:34:56" } } +# <- { "return": {} } # # TODO: This command effectively bypasses QAPI completely due to its # "additional arguments" business. It shouldn't have been added @@ -89,30 +89,32 @@ # # @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 # -# Examples: +# .. 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'} } @@ -121,7 +123,7 @@ # # 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 +# ID. Device removal can be initiated by the guest or by HMP/QMP # commands. # # @device: the device's ID if it has one @@ -130,12 +132,12 @@ # # Since: 1.5 # -# Example: +# .. 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": "virtio-net-pci-0", +# "path": "/machine/peripheral/virtio-net-pci-0" }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'DEVICE_DELETED', 'data': { '*device': 'str', 'path': 'str' } } @@ -152,12 +154,36 @@ # # Since: 6.2 # -# Example: +# .. 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": "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 +# capacity 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 555528b6bbd..e569224eaea 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -16,12 +16,12 @@ #include "block/aio.h" #include "qapi/compat-policy.h" #include "qapi/error.h" -#include "qapi/qmp/dispatch.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" +#include "qapi/qmp-registry.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" -#include "qapi/qmp/qbool.h" +#include "qobject/qbool.h" #include "qemu/coroutine.h" #include "qemu/main-loop.h" @@ -173,7 +173,7 @@ QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *requ "The command %s has not been found", command); goto out; } - if (!compat_policy_input_ok(cmd->special_features, &compat_policy, + if (!compat_policy_input_ok(cmd->features, &compat_policy, ERROR_CLASS_COMMAND_NOT_FOUND, "command", command, &err)) { goto out; @@ -206,9 +206,31 @@ QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *requ 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 *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *requ .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/qmp-event.c b/qapi/qmp-event.c index 0fe0d0a5a6e..11cb6ace995 100644 --- a/qapi/qmp-event.c +++ b/qapi/qmp-event.c @@ -14,9 +14,9 @@ #include "qemu/osdep.h" #include "qapi/qmp-event.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" static void timestamp_put(QDict *qdict) { diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index 485bc5e6fc7..e2623f2b78c 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -13,11 +13,11 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/dispatch.h" +#include "qapi/qmp-registry.h" void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options, - unsigned special_features) + uint64_t features) { QmpCommand *cmd = g_malloc0(sizeof(*cmd)); @@ -28,7 +28,7 @@ void qmp_register_command(QmpCommandList *cmds, const char *name, cmd->fn = fn; cmd->enabled = true; cmd->options = options; - cmd->special_features = special_features; + cmd->features = features; QTAILQ_INSERT_TAIL(cmds, cmd, node); } diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index 3e8aca6b159..c52d36997df 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -19,14 +19,14 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/visitor-impl.h" #include "qemu/queue.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qemu/cutils.h" #include "qemu/keyval.h" @@ -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(); @@ -664,10 +664,10 @@ static void qobject_input_optional(Visitor *v, const char *name, bool *present) } static bool qobject_input_policy_reject(Visitor *v, const char *name, - unsigned special_features, + uint64_t features, Error **errp) { - return !compat_policy_input_ok(special_features, &v->compat_policy, + return !compat_policy_input_ok(features, &v->compat_policy, ERROR_CLASS_GENERIC_ERROR, "parameter", name, errp); } diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c index 74770edd73c..de5b36bda55 100644 --- a/qapi/qobject-output-visitor.c +++ b/qapi/qobject-output-visitor.c @@ -17,12 +17,12 @@ #include "qapi/qobject-output-visitor.h" #include "qapi/visitor-impl.h" #include "qemu/queue.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" typedef struct QStackEntry { QObject *value; @@ -210,13 +210,13 @@ static bool qobject_output_type_null(Visitor *v, const char *name, } static bool qobject_output_policy_skip(Visitor *v, const char *name, - unsigned special_features) + uint64_t features) { CompatPolicy *pol = &v->compat_policy; - return ((special_features & 1u << QAPI_DEPRECATED) + return ((features & 1u << QAPI_DEPRECATED) && pol->deprecated_output == COMPAT_POLICY_OUTPUT_HIDE) - || ((special_features & 1u << QAPI_UNSTABLE) + || ((features & 1u << QAPI_UNSTABLE) && pol->unstable_output == COMPAT_POLICY_OUTPUT_HIDE); } diff --git a/qapi/qom.json b/qapi/qom.json index fa3e88c8e6a..b133b064471 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -59,14 +59,14 @@ # # Since: 1.2 # -# Example: +# .. 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" } ] } +# -> { "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' }, @@ -104,21 +104,21 @@ # # Since: 1.2 # -# Examples: +# .. 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 } +# .. qmp-example:: +# :title: Use partial path # -# 2. Use partial path -# -# -> { "execute": "qom-get", -# "arguments": { "path": "unattached/sysbus", -# "property": "type" } } -# <- { "return": "System" } +# -> { "execute": "qom-get", +# "arguments": { "path": "unattached/sysbus", +# "property": "type" } } +# <- { "return": "System" } ## { 'command': 'qom-get', 'data': { 'path': 'str', 'property': 'str' }, @@ -139,13 +139,13 @@ # # Since: 1.2 # -# Example: +# .. qmp-example:: # -# -> { "execute": "qom-set", -# "arguments": { "path": "/machine", -# "property": "graphics", -# "value": false } } -# <- { "return": {} } +# -> { "execute": "qom-set", +# "arguments": { "path": "/machine", +# "property": "graphics", +# "value": false } } +# <- { "return": {} } ## { 'command': 'qom-set', 'data': { 'path': 'str', 'property': 'str', 'value': 'any' }, @@ -195,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 # @@ -222,7 +222,8 @@ ## { 'struct': 'CanHostSocketcanProperties', 'data': { 'if': 'str', - 'canbus': 'str' } } + 'canbus': 'str' }, + 'if': 'CONFIG_LINUX' } ## # @ColoCompareProperties: @@ -305,7 +306,8 @@ ## { 'struct': 'CryptodevVhostUserProperties', 'base': 'CryptodevBackendProperties', - 'data': { 'chardev': 'str' } } + 'data': { 'chardev': 'str' }, + 'if': 'CONFIG_VHOST_CRYPTO' } ## # @DBusVMStateProperties: @@ -354,7 +356,7 @@ # 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 +# existing filters (default). "id=" means the filter is # inserted before or behind the filter specified by , # depending on the @insert property. (default: "tail") # @@ -514,7 +516,8 @@ 'data': { 'evdev': 'str', '*grab_all': 'bool', '*repeat': 'bool', - '*grab-toggle': 'GrabToggleKeys' } } + '*grab-toggle': 'GrabToggleKeys' }, + 'if': 'CONFIG_LINUX' } ## # @EventLoopBaseProperties: @@ -600,7 +603,9 @@ # preallocation threads (default: none) (since 7.2) # # @share: if false, the memory is private to QEMU; if true, it is -# shared (default: false) +# 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) @@ -612,12 +617,11 @@ # 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 ## @@ -642,21 +646,21 @@ # @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). +# /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) # # @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. +# file. Must be a multiple of the page size. # (default: 0) (since 8.1) # # @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) +# terminated using SIGKILL. (default: false) # # @mem-path: the path to either a shared memory or huge page # filesystem mount @@ -668,6 +672,21 @@ # @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', @@ -677,21 +696,20 @@ '*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) # # @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 +# 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 @@ -703,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 @@ -720,7 +752,8 @@ ## { 'struct': 'MemoryBackendEpcProperties', 'base': 'MemoryBackendProperties', - 'data': {} } + 'data': {}, + 'if': 'CONFIG_LINUX' } ## # @PrManagerHelperProperties: @@ -733,7 +766,8 @@ # Since: 2.11 ## { 'struct': 'PrManagerHelperProperties', - 'data': { 'path': 'str' } } + 'data': { 'path': 'str' }, + 'if': 'CONFIG_LINUX' } ## # @QtestProperties: @@ -779,6 +813,76 @@ { '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 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: # @@ -825,23 +929,16 @@ ## { 'struct': 'RngRandomProperties', 'base': 'RngProperties', - 'data': { '*filename': 'str' } } + 'data': { '*filename': 'str' }, + 'if': 'CONFIG_POSIX' } ## -# @SevGuestProperties: +# @SevCommonProperties: # -# Properties for sev-guest objects. +# Properties common to objects that are derivatives of sev-common. # # @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) -# -# @policy: SEV policy value (default: 0x1) -# -# @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 @@ -851,17 +948,143 @@ # 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. +# +# @dh-cert-file: guest owners DH certificate (encoded with base64) +# +# @session-file: guest owners session parameters (encoded with base64) +# +# @policy: SEV policy value (default: 0x1) +# +# @handle: SEV firmware handle (default: 0) +# +# @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' } } + +## +# @TdxGuestProperties: +# +# Properties for tdx-guest objects. +# +# @attributes: The 'attributes' of a TD guest that is passed to +# KVM_TDX_INIT_VM +# +# @sept-ve-disable: toggle bit 28 of TD attributes to control disabling +# of EPT violation conversion to #VE on guest TD access of PENDING +# pages. Some guest OS (e.g., Linux TD guest) may require this to +# be set, otherwise they refuse to boot. +# +# @mrconfigid: ID for non-owner-defined configuration of the guest TD, +# e.g., run-time or OS configuration (base64 encoded SHA384 digest). +# Defaults to all zeros. +# +# @mrowner: ID for the guest TD’s owner (base64 encoded SHA384 digest). +# Defaults to all zeros. +# +# @mrownerconfig: ID for owner-defined configuration of the guest TD, +# e.g., specific to the workload rather than the run-time or OS +# (base64 encoded SHA384 digest). Defaults to all zeros. +# +# @quote-generation-socket: socket address for Quote Generation +# Service (QGS). QGS is a daemon running on the host. Without +# it, the guest will not be able to get a TD quote for +# attestation. +# +# Since: 10.1 +## +{ 'struct': 'TdxGuestProperties', + 'data': { '*attributes': 'uint64', + '*sept-ve-disable': 'bool', + '*mrconfigid': 'str', + '*mrowner': 'str', + '*mrownerconfig': 'str', + '*quote-generation-socket': 'SocketAddress' } } ## # @ThreadContextProperties: @@ -890,12 +1113,15 @@ # # 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', @@ -919,6 +1145,7 @@ 'input-barrier', { 'name': 'input-linux', 'if': 'CONFIG_LINUX' }, + 'iommufd', 'iothread', 'main-loop', { 'name': 'memory-backend-epc', @@ -927,6 +1154,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' }, @@ -939,8 +1168,10 @@ { 'name': 'secret_keyring', 'if': 'CONFIG_SECRET_KEYRING' }, 'sev-guest', + 'sev-snp-guest', 'thread-context', 's390-pv-guest', + 'tdx-guest', 'throttle-group', 'tls-creds-anon', 'tls-creds-psk', @@ -966,6 +1197,8 @@ 'id': 'str' }, 'discriminator': 'qom-type', 'data': { + 'acpi-generic-initiator': 'AcpiGenericInitiatorProperties', + 'acpi-generic-port': 'AcpiGenericPortProperties', 'authz-list': 'AuthZListProperties', 'authz-listfile': 'AuthZListFileProperties', 'authz-pam': 'AuthZPAMProperties', @@ -988,6 +1221,7 @@ 'input-barrier': 'InputBarrierProperties', 'input-linux': { 'type': 'InputLinuxProperties', 'if': 'CONFIG_LINUX' }, + 'iommufd': 'IOMMUFDProperties', 'iothread': 'IothreadProperties', 'main-loop': 'MainLoopProperties', 'memory-backend-epc': { 'type': 'MemoryBackendEpcProperties', @@ -996,6 +1230,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', @@ -1007,6 +1243,8 @@ 'secret_keyring': { 'type': 'SecretKeyringProperties', 'if': 'CONFIG_SECRET_KEYRING' }, 'sev-guest': 'SevGuestProperties', + 'sev-snp-guest': 'SevSnpGuestProperties', + 'tdx-guest': 'TdxGuestProperties', 'thread-context': 'ThreadContextProperties', 'throttle-group': 'ThrottleGroupProperties', 'tls-creds-anon': 'TlsCredsAnonProperties', @@ -1022,17 +1260,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: +# .. qmp-example:: # -# -> { "execute": "object-add", -# "arguments": { "qom-type": "rng-random", "id": "rng1", -# "filename": "/dev/hwrng" } } -# <- { "return": {} } +# -> { "execute": "object-add", +# "arguments": { "qom-type": "rng-random", "id": "rng1", +# "filename": "/dev/hwrng" } } +# <- { "return": {} } ## { 'command': 'object-add', 'data': 'ObjectOptions', 'boxed': true, 'allow-preconfig': true } @@ -1044,15 +1282,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: +# .. qmp-example:: # -# -> { "execute": "object-del", "arguments": { "id": "rng1" } } -# <- { "return": {} } +# -> { "execute": "object-del", "arguments": { "id": "rng1" } } +# <- { "return": {} } ## { 'command': 'object-del', 'data': {'id': 'str'}, 'allow-preconfig': true } diff --git a/qapi/rdma.json b/qapi/rdma.json deleted file mode 100644 index 23ebcf7885e..00000000000 --- a/qapi/rdma.json +++ /dev/null @@ -1,38 +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 289b2d36580..35e0c4a6926 100644 --- a/qapi/replay.json +++ b/qapi/replay.json @@ -54,10 +54,10 @@ # # Since: 5.2 # -# Example: +# .. qmp-example:: # -# -> { "execute": "query-replay" } -# <- { "return": { "mode": "play", "filename": "log.rr", "icount": 220414 } } +# -> { "execute": "query-replay" } +# <- { "return": { "mode": "play", "filename": "log.rr", "icount": 220414 } } ## { 'command': 'query-replay', 'returns': 'ReplayInfo' } @@ -76,10 +76,10 @@ # # Since: 5.2 # -# Example: +# .. qmp-example:: # -# -> { "execute": "replay-break", "arguments": { "icount": 220414 } } -# <- { "return": {} } +# -> { "execute": "replay-break", "arguments": { "icount": 220414 } } +# <- { "return": {} } ## { 'command': 'replay-break', 'data': { 'icount': 'int' } } @@ -91,10 +91,10 @@ # # Since: 5.2 # -# Example: +# .. qmp-example:: # -# -> { "execute": "replay-delete-break" } -# <- { "return": {} } +# -> { "execute": "replay-delete-break" } +# <- { "return": {} } ## { 'command': 'replay-delete-break' } @@ -105,16 +105,16 @@ # 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. +# 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 } } -# <- { "return": {} } +# -> { "execute": "replay-seek", "arguments": { "icount": 220414 } } +# <- { "return": {} } ## { 'command': 'replay-seek', 'data': { 'icount': 'int' } } diff --git a/qapi/rocker.json b/qapi/rocker.json index 31ce0b36f69..0c7ef1f77c8 100644 --- a/qapi/rocker.json +++ b/qapi/rocker.json @@ -26,14 +26,16 @@ # # Return rocker switch information. # +# @name: switch name +# # Returns: @Rocker information # # Since: 2.4 # -# Example: +# .. qmp-example:: # -# -> { "execute": "query-rocker", "arguments": { "name": "sw1" } } -# <- { "return": {"name": "sw1", "ports": 2, "id": 1327446905938}} +# -> { "execute": "query-rocker", "arguments": { "name": "sw1" } } +# <- { "return": {"name": "sw1", "ports": 2, "id": 1327446905938}} ## { 'command': 'query-rocker', 'data': { 'name': 'str' }, @@ -42,7 +44,7 @@ ## # @RockerPortDuplex: # -# An eumeration of port duplex states. +# An enumeration of port duplex states. # # @half: half duplex # @@ -55,7 +57,7 @@ ## # @RockerPortAutoneg: # -# An eumeration of port autoneg states. +# An enumeration of port autoneg states. # # @off: autoneg is off # @@ -94,18 +96,20 @@ # # Return rocker switch port information. # +# @name: port name +# # Returns: a list of @RockerPort information # # Since: 2.4 # -# Example: +# .. 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} -# ]} +# -> { "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' }, @@ -138,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 ## @@ -168,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 ## @@ -195,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 ## @@ -240,18 +244,18 @@ # # 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' }, @@ -288,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 ## @@ -315,23 +319,23 @@ # # 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 f216ba54ec4..fd09beb35cb 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -62,7 +62,7 @@ ## # @ShutdownCause: # -# An enumeration of reasons for a Shutdown. +# An enumeration of reasons for a shutdown. # # @none: No shutdown request pending # @@ -91,7 +91,7 @@ # # @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) +# occur in QMP. (since 7.2) ## { 'enum': 'ShutdownCause', # Beware, shutdown_caused_by_guest() depends on enumeration order @@ -102,46 +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 using TCG with one guest instruction per -# translation block -# # @status: the virtual machine @RunState # -# Features: -# -# @deprecated: Member 'singlestep' is deprecated (with no -# replacement). -# # Since: 0.14 -# -# Notes: @singlestep is enabled on the command line with '-accel -# tcg,one-insn-per-tb=on', or with the HMP 'one-insn-per-tb' -# command. ## { 'struct': 'StatusInfo', 'data': {'running': 'bool', - 'singlestep': { 'type': 'bool', 'features': [ 'deprecated' ]}, '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: +# .. qmp-example:: # -# -> { "execute": "query-status" } -# <- { "return": { "running": true, -# "singlestep": false, -# "status": "running" } } +# -> { "execute": "query-status" } +# <- { "return": { "running": true, +# "status": "running" } } ## { 'command': 'query-status', 'returns': 'StatusInfo', 'allow-preconfig': true } @@ -149,28 +135,28 @@ ## # @SHUTDOWN: # -# Emitted when the virtual machine has shut down, indicating that qemu +# 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) +# 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: +# .. qmp-example:: # -# <- { "event": "SHUTDOWN", -# "data": { "guest": true, "reason": "guest-shutdown" }, -# "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } +# <- { "event": "SHUTDOWN", +# "data": { "guest": true, "reason": "guest-shutdown" }, +# "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } ## { 'event': 'SHUTDOWN', 'data': { 'guest': 'bool', 'reason': 'ShutdownCause' } } @@ -182,10 +168,10 @@ # # Since: 0.12 # -# Example: +# .. qmp-example:: # -# <- { "event": "POWERDOWN", -# "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } +# <- { "event": "POWERDOWN", +# "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } ## { 'event': 'POWERDOWN' } @@ -197,17 +183,17 @@ # @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) +# 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: +# .. qmp-example:: # -# <- { "event": "RESET", -# "data": { "guest": false, "reason": "guest-reset" }, -# "timestamp": { "seconds": 1267041653, "microseconds": 9518 } } +# <- { "event": "RESET", +# "data": { "guest": false, "reason": "guest-reset" }, +# "timestamp": { "seconds": 1267041653, "microseconds": 9518 } } ## { 'event': 'RESET', 'data': { 'guest': 'bool', 'reason': 'ShutdownCause' } } @@ -218,10 +204,10 @@ # # Since: 0.12 # -# Example: +# .. qmp-example:: # -# <- { "event": "STOP", -# "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } +# <- { "event": "STOP", +# "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } ## { 'event': 'STOP' } @@ -232,10 +218,10 @@ # # Since: 0.12 # -# Example: +# .. qmp-example:: # -# <- { "event": "RESUME", -# "timestamp": { "seconds": 1271770767, "microseconds": 582542 } } +# <- { "event": "RESUME", +# "timestamp": { "seconds": 1271770767, "microseconds": 582542 } } ## { 'event': 'RESUME' } @@ -247,10 +233,10 @@ # # Since: 1.1 # -# Example: +# .. qmp-example:: # -# <- { "event": "SUSPEND", -# "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } +# <- { "event": "SUSPEND", +# "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } ## { 'event': 'SUSPEND' } @@ -261,15 +247,15 @@ # 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: +# .. qmp-example:: # -# <- { "event": "SUSPEND_DISK", -# "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } +# <- { "event": "SUSPEND_DISK", +# "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } ## { 'event': 'SUSPEND_DISK' } @@ -281,10 +267,10 @@ # # Since: 1.1 # -# Example: +# .. qmp-example:: # -# <- { "event": "WAKEUP", -# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } +# <- { "event": "WAKEUP", +# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } ## { 'event': 'WAKEUP' } @@ -295,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: +# .. qmp-example:: # -# <- { "event": "WATCHDOG", -# "data": { "action": "reset" }, -# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } +# <- { "event": "WATCHDOG", +# "data": { "action": "reset" }, +# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } ## { 'event': 'WATCHDOG', 'data': { 'action': 'WatchdogAction' } } @@ -379,8 +365,8 @@ # @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 ## @@ -390,9 +376,17 @@ ## # @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'} } @@ -408,21 +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', @@ -442,11 +433,11 @@ # # Since: 1.5 # -# Example: +# .. qmp-example:: # -# <- { "event": "GUEST_PANICKED", -# "data": { "action": "pause" }, -# "timestamp": { "seconds": 1648245231, "microseconds": 900001 } } +# <- { "event": "GUEST_PANICKED", +# "data": { "action": "pause" }, +# "timestamp": { "seconds": 1648245231, "microseconds": 900001 } } ## { 'event': 'GUEST_PANICKED', 'data': { 'action': 'GuestPanicAction', '*info': 'GuestPanicInformation' } } @@ -462,15 +453,29 @@ # # Since: 5.0 # -# Example: +# .. qmp-example:: # -# <- { "event": "GUEST_CRASHLOADED", -# "data": { "action": "run" }, -# "timestamp": { "seconds": 1648245259, "microseconds": 893771 } } +# <- { "event": "GUEST_CRASHLOADED", +# "data": { "action": "run" }, +# "timestamp": { "seconds": 1648245259, "microseconds": 893771 } } ## { 'event': 'GUEST_CRASHLOADED', 'data': { 'action': 'GuestPanicAction', '*info': 'GuestPanicInformation' } } +## +# @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: # @@ -496,10 +501,12 @@ # # @s390: s390 guest panic information type (Since: 2.12) # +# @tdx: tdx guest panic information type (Since: 10.1) +# # Since: 2.9 ## { 'enum': 'GuestPanicInformationType', - 'data': [ 'hyper-v', 's390' ] } + 'data': [ 'hyper-v', 's390', 'tdx' ] } ## # @GuestPanicInformation: @@ -514,13 +521,30 @@ 'base': {'type': 'GuestPanicInformationType'}, 'discriminator': 'type', 'data': {'hyper-v': 'GuestPanicInformationHyperV', - 's390': 'GuestPanicInformationS390'}} + 's390': 'GuestPanicInformationS390', + 'tdx' : 'GuestPanicInformationTdx'}} ## # @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', @@ -577,6 +601,30 @@ 'psw-addr': 'uint64', 'reason': 'S390CrashReason'}} +## +# @GuestPanicInformationTdx: +# +# TDX Guest panic information specific to TDX, as specified in the +# "Guest-Hypervisor Communication Interface (GHCI) Specification", +# section TDG.VP.VMCALL. +# +# @error-code: TD-specific error code +# +# @message: Human-readable error message provided by the guest. Not +# to be trusted. +# +# @gpa: guest-physical address of a page that contains more verbose +# error information, as zero-terminated string. Present when the +# "GPA valid" bit (bit 63) is set in @error-code. +# +# +# Since: 10.1 +## +{'struct': 'GuestPanicInformationTdx', + 'data': {'error-code': 'uint32', + 'message': 'str', + '*gpa': 'uint64'}} + ## # @MEMORY_FAILURE: # @@ -584,22 +632,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: +# .. 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": "hypervisor", +# "action": "fatal", +# "flags": { "action-required": false, +# "recursive": false } }, +# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } ## { 'event': 'MEMORY_FAILURE', 'data': { 'recipient': 'MemoryFailureRecipient', diff --git a/qapi/sockets.json b/qapi/sockets.json index 62131545255..f9f559dabae 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', @@ -57,10 +56,26 @@ # @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/listening on this socket. +# (Since 4.2, not supported for listening sockets until 10.1) +# +# @keep-alive-count: number of keep-alive packets sent before the connection is +# closed. Only supported for TCP sockets on systems where TCP_KEEPCNT +# socket option is defined (this includes Linux, Windows, macOS, FreeBSD, +# but not OpenBSD). When set to 0, system setting is used. (Since 10.1) +# +# @keep-alive-idle: time in seconds the connection needs to be idle before +# sending a keepalive packet. Only supported for TCP sockets on systems +# where TCP_KEEPIDLE socket option is defined (this includes Linux, +# Windows, macOS, FreeBSD, but not OpenBSD). When set to 0, system setting +# is used. (Since 10.1) # -# @mptcp: enable multi-path TCP. (Since 6.1) +# @keep-alive-interval: time in seconds between keep-alive packets. Only +# supported for TCP sockets on systems where TCP_KEEPINTVL is defined (this +# includes Linux, Windows, macOS, FreeBSD, but not OpenBSD). When set to +# 0, system setting is used. (Since 10.1) +# +# @mptcp: enable multi-path TCP. (Since 6.1) # # Since: 1.3 ## @@ -72,6 +87,9 @@ '*ipv4': 'bool', '*ipv6': 'bool', '*keep-alive': 'bool', + '*keep-alive-count': { 'type': 'uint32', 'if': 'HAVE_TCP_KEEPCNT' }, + '*keep-alive-idle': { 'type': 'uint32', 'if': 'HAVE_TCP_KEEPIDLE' }, + '*keep-alive-interval': { 'type': 'uint32', 'if': 'HAVE_TCP_KEEPINTVL' }, '*mptcp': { 'type': 'bool', 'if': 'HAVE_IPPROTO_MPTCP' } } } ## @@ -106,8 +124,8 @@ # # @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 ## @@ -116,9 +134,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', @@ -127,6 +164,8 @@ ## # @UnixSocketAddressWrapper: # +# @data: UNIX domain socket address +# # Since: 1.3 ## { 'struct': 'UnixSocketAddressWrapper', @@ -135,18 +174,22 @@ ## # @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: @@ -154,9 +197,7 @@ # 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 ## @@ -167,7 +208,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: @@ -180,11 +224,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 ## @@ -194,7 +234,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 @@ -207,4 +247,4 @@ 'data': { 'inet': 'InetSocketAddress', 'unix': 'UnixSocketAddress', 'vsock': 'VsockSocketAddress', - 'fd': 'String' } } + 'fd': 'FdSocketAddress' } } diff --git a/qapi/stats.json b/qapi/stats.json index 01791e86d5f..8902ef94e08 100644 --- a/qapi/stats.json +++ b/qapi/stats.json @@ -114,11 +114,13 @@ # # 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: +# information for that target. # -# - which vCPUs to request statistics for -# - which providers to request statistics from -# - which named values to return within each provider +# @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 ## @@ -134,6 +136,8 @@ # # @scalar: single unsigned 64-bit integers. # +# @boolean: single boolean value. +# # @list: list of unsigned 64-bit integers (used for histograms). # # Since: 7.1 @@ -252,17 +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 197139c1c0c..f4eecc73d06 100644 --- a/qapi/string-input-visitor.c +++ b/qapi/string-input-visitor.c @@ -15,7 +15,7 @@ #include "qapi/string-input-visitor.h" #include "qapi/visitor-impl.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "qemu/option.h" #include "qemu/cutils.h" @@ -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 71ddc92b7b9..5115536b153 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 a754455ca55..a16a72edb98 100644 --- a/qapi/tpm.json +++ b/qapi/tpm.json @@ -31,10 +31,10 @@ # # Since: 1.5 # -# Example: +# .. qmp-example:: # -# -> { "execute": "query-tpm-models" } -# <- { "return": [ "tpm-tis", "tpm-crb", "tpm-spapr" ] } +# -> { "execute": "query-tpm-models" } +# <- { "return": [ "tpm-tis", "tpm-crb", "tpm-spapr" ] } ## { 'command': 'query-tpm-models', 'returns': ['TpmModel'], 'if': 'CONFIG_TPM' } @@ -62,10 +62,10 @@ # # Since: 1.5 # -# Example: +# .. qmp-example:: # -# -> { "execute": "query-tpm-types" } -# <- { "return": [ "passthrough", "emulator" ] } +# -> { "execute": "query-tpm-types" } +# <- { "return": [ "passthrough", "emulator" ] } ## { 'command': 'query-tpm-types', 'returns': ['TpmType'], 'if': 'CONFIG_TPM' } @@ -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', @@ -162,27 +166,25 @@ # # Return information about the TPM device # -# Returns: @TPMInfo on success -# # Since: 1.5 # -# Example: -# -# -> { "execute": "query-tpm" } -# <- { "return": -# [ -# { "model": "tpm-tis", -# "options": -# { "type": "passthrough", -# "data": -# { "cancel-path": "/sys/class/misc/tpm0/device/cancel", -# "path": "/dev/tpm0" -# } -# }, -# "id": "tpm0" +# .. 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" +# } +# ] # } -# ] -# } ## { 'command': 'query-tpm', 'returns': ['TPMInfo'], 'if': 'CONFIG_TPM' } diff --git a/qapi/trace.json b/qapi/trace.json index 2077d7e117b..eb5f63f5135 100644 --- a/qapi/trace.json +++ b/qapi/trace.json @@ -35,17 +35,10 @@ # # @state: Tracing state. # -# @vcpu: Whether this is a per-vCPU event (since 2.7). -# -# Features: -# -# @deprecated: Member @vcpu is deprecated, and always ignored. -# # Since: 2.2 ## { 'struct': 'TraceEventInfo', - 'data': {'name': 'str', 'state': 'TraceEventState', - 'vcpu': { 'type': 'bool', 'features': ['deprecated'] } } } + 'data': {'name': 'str', 'state': 'TraceEventState' } } ## # @trace-event-get-state: @@ -54,25 +47,18 @@ # # @name: Event name pattern (case-sensitive glob). # -# @vcpu: The vCPU to query (since 2.7). -# -# Features: -# -# @deprecated: Member @vcpu is deprecated, and always ignored. -# # Returns: a list of @TraceEventInfo for the matching events # # Since: 2.2 # -# Example: +# .. qmp-example:: # -# -> { "execute": "trace-event-get-state", -# "arguments": { "name": "qemu_memalign" } } -# <- { "return": [ { "name": "qemu_memalign", "state": "disabled", "vcpu": false } ] } +# -> { "execute": "trace-event-get-state", +# "arguments": { "name": "qemu_memalign" } } +# <- { "return": [ { "name": "qemu_memalign", "state": "disabled", "vcpu": false } ] } ## { 'command': 'trace-event-get-state', - 'data': {'name': 'str', - '*vcpu': {'type': 'int', 'features': ['deprecated'] } }, + 'data': {'name': 'str' }, 'returns': ['TraceEventInfo'] } ## @@ -86,20 +72,13 @@ # # @ignore-unavailable: Do not match unavailable events with @name. # -# @vcpu: The vCPU to act upon (all by default; since 2.7). -# -# Features: -# -# @deprecated: Member @vcpu is deprecated, and always ignored. -# # Since: 2.2 # -# Example: +# .. qmp-example:: # -# -> { "execute": "trace-event-set-state", -# "arguments": { "name": "qemu_memalign", "enable": true } } -# <- { "return": {} } +# -> { "execute": "trace-event-set-state", +# "arguments": { "name": "qemu_memalign", "enable": true } } +# <- { "return": {} } ## { 'command': 'trace-event-set-state', - 'data': {'name': 'str', 'enable': 'bool', '*ignore-unavailable': 'bool', - '*vcpu': {'type': 'int', 'features': ['deprecated'] } } } + 'data': {'name': 'str', 'enable': 'bool', '*ignore-unavailable': 'bool' } } diff --git a/qapi/transaction.json b/qapi/transaction.json index cffee2de28d..9d9e7af26cb 100644 --- a/qapi/transaction.json +++ b/qapi/transaction.json @@ -21,7 +21,7 @@ ## # @ActionCompletionMode: # -# An enumeration of Transactional completion modes. +# 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 @@ -158,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', @@ -221,7 +223,7 @@ # 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 +# 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. # @@ -232,37 +234,36 @@ # execution of the transaction. See @TransactionProperties for # additional detail. # -# Returns: nothing on success -# -# Errors depend on the operations of the transaction +# Errors: +# - Any errors from commands in 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/uefi.json b/qapi/uefi.json new file mode 100644 index 00000000000..6592183d6cf --- /dev/null +++ b/qapi/uefi.json @@ -0,0 +1,64 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# + +## +# = UEFI Variable Store +# +# The QEMU efi variable store implementation (hw/uefi/) uses this to +# store non-volatile variables in json format on disk. +# +# This is an existing format already supported by (at least) two other +# projects, specifically https://gitlab.com/kraxel/virt-firmware and +# https://github.com/awslabs/python-uefivars. +## + +## +# @UefiVariable: +# +# UEFI Variable. Check the UEFI specifification for more detailed +# information on the fields. +# +# @guid: variable namespace GUID +# +# @name: variable name, in UTF-8 encoding. +# +# @attr: variable attributes. +# +# @data: variable value, encoded as hex string. +# +# @time: variable modification time. EFI_TIME struct, encoded as hex +# string. Used only for authenticated variables, where the +# EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute bit +# is set. +# +# @digest: variable certificate digest. Used to verify the signature +# of updates for authenticated variables. UEFI has two kinds of +# authenticated variables. The secure boot variables ('PK', +# 'KEK', 'db' and 'dbx') have hard coded signature checking rules. +# For other authenticated variables the firmware stores a digest +# of the signing certificate at variable creation time, and any +# updates must be signed with the same certificate. +# +# Since: 10.0 +## +{ 'struct' : 'UefiVariable', + 'data' : { 'guid' : 'str', + 'name' : 'str', + 'attr' : 'int', + 'data' : 'str', + '*time' : 'str', + '*digest' : 'str'}} + +## +# @UefiVarStore: +# +# @version: currently always 2 +# +# @variables: list of UEFI variables +# +# Since: 10.0 +## +{ 'struct' : 'UefiVarStore', + 'data' : { 'version' : 'int', + 'variables' : [ 'UefiVariable' ] }} diff --git a/qapi/ui.json b/qapi/ui.json index 006616aa776..514fa159b10 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -48,8 +48,8 @@ # @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 ## @@ -63,7 +63,7 @@ ## # @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. @@ -78,17 +78,16 @@ # # Set the password of a remote display server. # -# Returns: -# - Nothing on success +# Errors: # - If Spice is not enabled, DeviceNotFound # # Since: 0.14 # -# Example: +# .. qmp-example:: # -# -> { "execute": "set_password", "arguments": { "protocol": "vnc", -# "password": "secret" } } -# <- { "return": {} } +# -> { "execute": "set_password", "arguments": { "protocol": "vnc", +# "password": "secret" } } +# <- { "return": {} } ## { 'command': 'set_password', 'boxed': true, 'data': 'SetPasswordOptions' } @@ -108,11 +107,11 @@ # - '+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 ## @@ -125,7 +124,7 @@ ## # @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. @@ -140,18 +139,17 @@ # # Expire the password of a remote display server. # -# Returns: -# - Nothing on success +# Errors: # - If @protocol is 'spice' and Spice is not active, # DeviceNotFound # # Since: 0.14 # -# Example: +# .. qmp-example:: # -# -> { "execute": "expire_password", "arguments": { "protocol": "vnc", -# "time": "+60" } } -# <- { "return": {} } +# -> { "execute": "expire_password", "arguments": { "protocol": "vnc", +# "time": "+60" } } +# <- { "return": {} } ## { 'command': 'expire_password', 'boxed': true, 'data': 'ExpirePasswordOptions' } @@ -177,30 +175,29 @@ # @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) +# 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) +# ID. (Since 2.12) # # @format: image format for screendump. (default: ppm) (Since 7.1) # -# Returns: Nothing on success -# # Since: 0.14 # -# Example: +# .. qmp-example:: # -# -> { "execute": "screendump", -# "arguments": { "filename": "/tmp/image" } } -# <- { "return": {} } +# -> { "execute": "screendump", +# "arguments": { "filename": "/tmp/image" } } +# <- { "return": {} } ## { 'command': 'screendump', 'data': {'filename': 'str', '*device': 'str', '*head': 'int', '*format': 'ImageFormat'}, - 'coroutine': true } + 'coroutine': true, + 'if': 'CONFIG_PIXMAN' } ## # == Spice @@ -277,8 +274,6 @@ # @unknown: No information is available about mouse mode used by the # spice server. # -# Note: spice/enums.h has a SpiceMouseMode already, hence the name. -# # Since: 1.1 ## { 'enum': 'SpiceQueryMouseMode', @@ -293,7 +288,7 @@ # @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. @@ -306,7 +301,7 @@ # # @auth: the current authentication type used by the server # -# - 'none' if no authentication is being used +# - 'none' if no authentication is being used # - 'spice' uses SASL or direct TLS authentication, depending on # command line options # @@ -328,46 +323,46 @@ ## # @query-spice: # -# Returns information about the current SPICE server +# Return information about the current SPICE server # # Returns: @SpiceInfo # # 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' } @@ -383,14 +378,14 @@ # # Since: 0.14 # -# Example: +# .. 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"} -# }} +# <- { "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', @@ -409,16 +404,16 @@ # # Since: 0.14 # -# Example: +# .. 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} -# }} +# <- { "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', @@ -436,14 +431,14 @@ # # Since: 0.14 # -# Example: +# .. 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"} -# }} +# <- { "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', @@ -457,10 +452,10 @@ # # Since: 1.3 # -# Example: +# .. qmp-example:: # -# <- { "timestamp": {"seconds": 1290688046, "microseconds": 417172}, -# "event": "SPICE_MIGRATE_COMPLETED" } +# <- { "timestamp": {"seconds": 1290688046, "microseconds": 417172}, +# "event": "SPICE_MIGRATE_COMPLETED" } ## { 'event': 'SPICE_MIGRATE_COMPLETED', 'if': 'CONFIG_SPICE' } @@ -630,7 +625,7 @@ # @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 +# 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. # @@ -659,38 +654,38 @@ ## # @query-vnc: # -# Returns information about the current VNC server +# Return information about the current VNC server # # Returns: @VncInfo # # 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' } ## # @query-vnc-servers: # -# Returns a list of vnc servers. The list can be empty. +# Return a list of vnc servers. The list can be empty. # # Returns: a list of @VncInfo2 # @@ -708,9 +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' }, @@ -725,20 +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: +# .. 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": { "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', @@ -757,15 +752,15 @@ # # Since: 0.13 # -# Example: +# .. 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": { "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', @@ -783,15 +778,15 @@ # # Since: 0.13 # -# Example: +# .. 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": { "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', @@ -825,30 +820,30 @@ ## # @query-mice: # -# Returns information about each active mouse device +# Return information about each active mouse device # # Returns: a list of @MouseInfo for each device # # 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'] } @@ -954,6 +949,7 @@ # 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', @@ -989,6 +985,8 @@ ## # @IntWrapper: # +# @data: a numeric key code +# # Since: 1.3 ## { 'struct': 'IntWrapper', @@ -997,6 +995,8 @@ ## # @QKeyCodeWrapper: # +# @data: An enumeration of key name +# # Since: 1.3 ## { 'struct': 'QKeyCodeWrapper', @@ -1007,6 +1007,8 @@ # # Represents a keyboard key. # +# @type: key encoding +# # Since: 1.3 ## { 'union': 'KeyValue', @@ -1029,19 +1031,18 @@ # @hold-time: time to delay key up events, milliseconds. Defaults to # 100 # -# Returns: -# - Nothing on success +# Errors: # - If key is unknown or redundant, GenericError # # Since: 1.3 # -# Example: +# .. qmp-example:: # -# -> { "execute": "send-key", -# "arguments": { "keys": [ { "type": "qcode", "data": "ctrl" }, -# { "type": "qcode", "data": "alt" }, -# { "type": "qcode", "data": "delete" } ] } } -# <- { "return": {} } +# -> { "execute": "send-key", +# "arguments": { "keys": [ { "type": "qcode", "data": "ctrl" }, +# { "type": "qcode", "data": "alt" }, +# { "type": "qcode", "data": "delete" } ] } } +# <- { "return": {} } ## { 'command': 'send-key', 'data': { 'keys': ['KeyValue'], '*hold-time': 'int' } } @@ -1078,6 +1079,16 @@ # # 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', @@ -1122,7 +1133,7 @@ # @axis: Which axis is referenced by @value. # # @value: Pointer position. For absolute coordinates the valid range -# is 0 -> 0x7ffff +# is 0 to 0x7fff. # # Since: 2.0 ## @@ -1135,6 +1146,8 @@ # # 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 @@ -1174,6 +1187,8 @@ ## # @InputKeyEventWrapper: # +# @data: Keyboard input event +# # Since: 2.0 ## { 'struct': 'InputKeyEventWrapper', @@ -1182,6 +1197,8 @@ ## # @InputBtnEventWrapper: # +# @data: Pointer button input event +# # Since: 2.0 ## { 'struct': 'InputBtnEventWrapper', @@ -1190,6 +1207,8 @@ ## # @InputMoveEventWrapper: # +# @data: Pointer motion input event +# # Since: 2.0 ## { 'struct': 'InputMoveEventWrapper', @@ -1198,6 +1217,8 @@ ## # @InputMultiTouchEventWrapper: # +# @data: MultiTouch input event +# # Since: 8.1 ## { 'struct': 'InputMultiTouchEventWrapper', @@ -1244,50 +1265,49 @@ # # @events: List of InputEvent union. # -# Returns: Nothing on success. -# # Since: 2.6 # -# Note: The consoles are visible in the qom tree, under -# /backend/console[$index]. They have a device link and head -# property, so it is possible to map which console belongs to -# which device and display. -# -# Examples: -# -# 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": false, "button": "left" } } ] } } -# <- { "return": {} } -# -# 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": {} } -# -# 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": {} } +# .. 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. +# +# .. qmp-example:: +# :title: 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": false, "button": "left" } } ] } } +# <- { "return": {} } +# +# .. qmp-example:: +# :title: 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": {} } +# +# .. 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', @@ -1306,7 +1326,7 @@ # 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) +# 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 @@ -1398,24 +1418,33 @@ # # @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" +# 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 +# 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) # +# @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 ## { 'struct': 'DisplayCocoa', 'data': { '*left-command-key': 'bool', '*full-grab': 'bool', - '*swap-opt-cmd': 'bool' + '*swap-opt-cmd': 'bool', + '*zoom-to-fit': 'bool', + '*zoom-interpolation': 'bool' } } ## @@ -1497,12 +1526,12 @@ # # Display (user interface) options. # -# @type: Which DisplayType qemu should use. +# @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 +# @window-close: Allow to quit QEMU with window close button # (default: on). # # @show-cursor: Force showing the mouse cursor (default: off). @@ -1533,7 +1562,7 @@ ## # @query-display-options: # -# Returns information about display configuration +# Return information about display configuration # # Returns: @DisplayOptions # @@ -1585,15 +1614,13 @@ # # Reload display configuration. # -# Returns: Nothing on success. -# # Since: 6.0 # -# Example: +# .. qmp-example:: # -# -> { "execute": "display-reload", -# "arguments": { "type": "vnc", "tls-certs": true } } -# <- { "return": {} } +# -> { "execute": "display-reload", +# "arguments": { "type": "vnc", "tls-certs": true } } +# <- { "return": {} } ## { 'command': 'display-reload', 'data': 'DisplayReloadOptions', @@ -1644,17 +1671,15 @@ # # Update display configuration. # -# Returns: Nothing on success. -# # Since: 7.1 # -# Example: +# .. qmp-example:: # -# -> { "execute": "display-update", -# "arguments": { "type": "vnc", "addresses": -# [ { "type": "inet", "host": "0.0.0.0", -# "port": "5901" } ] } } -# <- { "return": {} } +# -> { "execute": "display-update", +# "arguments": { "type": "vnc", "addresses": +# [ { "type": "inet", "host": "0.0.0.0", +# "port": "5901" } ] } } +# <- { "return": {} } ## { 'command': 'display-update', 'data': 'DisplayUpdateOptions', @@ -1679,13 +1704,13 @@ # # Since: 0.14 # -# Example: +# .. qmp-example:: # -# -> { "execute": "client_migrate_info", -# "arguments": { "protocol": "spice", -# "hostname": "virt42.lab.kraxel.org", -# "port": 1234 } } -# <- { "return": {} } +# -> { "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', diff --git a/qapi/vfio.json b/qapi/vfio.json new file mode 100644 index 00000000000..b53b7caecd7 --- /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 e6dcee7b83d..73df718a261 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -24,7 +24,7 @@ ## # @x-query-virtio: # -# Returns a list of all realized VirtIODevices +# Return a list of all realized VirtIODevices # # Features: # @@ -34,32 +34,32 @@ # # Since: 7.2 # -# Example: +# .. 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" -# } -# ] -# } +# -> { "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' ], @@ -203,231 +203,237 @@ # # Since: 7.2 # -# Examples: -# -# 1. Poll for the status of virtio-crypto (no vhost-crypto active) -# -# -> { "execute": "x-query-virtio-status", -# "arguments": { "path": "/machine/peripheral/crypto0/virtio-backend" } -# } -# <- { "return": { -# "device-endian": "little", -# "bus-name": "", -# "disable-legacy-check": false, -# "name": "virtio-crypto", -# "started": true, -# "device-id": 20, -# "backend-features": { -# "transports": [], -# "dev-features": [] -# }, -# "start-on-kick": false, -# "isr": 1, -# "broken": false, -# "status": { -# "statuses": [ -# "VIRTIO_CONFIG_S_ACKNOWLEDGE: Valid virtio device found", -# "VIRTIO_CONFIG_S_DRIVER: Guest OS compatible with device", -# "VIRTIO_CONFIG_S_FEATURES_OK: Feature negotiation complete", -# "VIRTIO_CONFIG_S_DRIVER_OK: Driver setup and ready" -# ] -# }, -# "num-vqs": 2, -# "guest-features": { -# "dev-features": [], -# "transports": [ -# "VIRTIO_RING_F_EVENT_IDX: Used & avail. event fields enabled", -# "VIRTIO_RING_F_INDIRECT_DESC: Indirect descriptors supported", -# "VIRTIO_F_VERSION_1: Device compliant for v1 spec (legacy)" -# ] -# }, -# "host-features": { -# "unknown-dev-features": 1073741824, -# "dev-features": [], -# "transports": [ -# "VIRTIO_RING_F_EVENT_IDX: Used & avail. event fields enabled", -# "VIRTIO_RING_F_INDIRECT_DESC: Indirect descriptors supported", -# "VIRTIO_F_VERSION_1: Device compliant for v1 spec (legacy)", -# "VIRTIO_F_ANY_LAYOUT: Device accepts arbitrary desc. layouts", -# "VIRTIO_F_NOTIFY_ON_EMPTY: Notify when device runs out of avail. descs. on VQ" -# ] -# }, -# "use-guest-notifier-mask": true, -# "vm-running": true, -# "queue-sel": 1, -# "disabled": false, -# "vhost-started": false, -# "use-started": true -# } -# } -# -# 2. Poll for the status of virtio-net (vhost-net is active) -# -# -> { "execute": "x-query-virtio-status", -# "arguments": { "path": "/machine/peripheral-anon/device[1]/virtio-backend" } -# } -# <- { "return": { -# "device-endian": "little", -# "bus-name": "", -# "disabled-legacy-check": false, -# "name": "virtio-net", -# "started": true, -# "device-id": 1, -# "vhost-dev": { -# "n-tmp-sections": 4, -# "n-mem-sections": 4, -# "max-queues": 1, -# "backend-cap": 2, -# "log-size": 0, +# .. qmp-example:: +# :annotated: +# +# 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": [] +# "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)" +# ] # }, -# "nvqs": 2, -# "protocol-features": { -# "protocols": [] +# "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" +# ] # }, -# "vq-index": 0, -# "log-enabled": false, -# "acked-features": { +# "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": [ -# "VIRTIO_NET_F_MRG_RXBUF: Driver can merge receive buffers" +# "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_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" # ] # }, -# "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": 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_F_LOG_ALL: Logging write descriptors supported", -# "VIRTIO_NET_F_MRG_RXBUF: Driver can merge receive buffers" +# "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_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": 2, +# "disabled": false, +# "vhost-started": true, +# "use-started": true +# } +# } ## { 'command': 'x-query-virtio-status', 'data': { 'path': 'str' }, @@ -559,63 +565,69 @@ # # 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: -# -# 1. Get VirtQueueStatus for virtio-vsock (vhost-vsock running) -# -# -> { "execute": "x-query-virtio-queue-status", -# "arguments": { "path": "/machine/peripheral/vsock0/virtio-backend", -# "queue": 1 } -# } -# <- { "return": { -# "signalled-used": 0, -# "inuse": 0, -# "name": "vhost-vsock", -# "vring-align": 4096, -# "vring-desc": 5217370112, -# "signalled-used-valid": false, -# "vring-num-default": 128, -# "vring-avail": 5217372160, -# "queue-index": 1, -# "last-avail-idx": 0, -# "vring-used": 5217372480, -# "used-idx": 0, -# "vring-num": 128 -# } -# } -# -# 2. Get VirtQueueStatus for virtio-serial (no vhost) -# -# -> { "execute": "x-query-virtio-queue-status", -# "arguments": { "path": "/machine/peripheral-anon/device[0]/virtio-backend", -# "queue": 20 } -# } -# <- { "return": { -# "signalled-used": 0, -# "inuse": 0, -# "name": "virtio-serial", -# "vring-align": 4096, -# "vring-desc": 5182074880, -# "signalled-used-valid": false, -# "vring-num-default": 128, -# "vring-avail": 5182076928, -# "queue-index": 20, -# "last-avail-idx": 0, -# "vring-used": 5182077248, -# "used-idx": 0, -# "shadow-avail-idx": 0, -# "vring-num": 128 -# } -# } +# .. qmp-example:: +# :annotated: +# +# 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 +# } +# } +# +# .. qmp-example:: +# :annotated: +# +# 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' }, @@ -642,15 +654,17 @@ # # @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 # @@ -688,53 +702,53 @@ # # Since: 7.2 # -# Examples: -# -# 1. Get vhost_virtqueue status for vhost-crypto -# -# -> { "execute": "x-query-virtio-vhost-queue-status", -# "arguments": { "path": "/machine/peripheral/crypto0/virtio-backend", -# "queue": 0 } -# } -# <- { "return": { -# "avail-phys": 5216124928, -# "name": "virtio-crypto", -# "used-phys": 5216127040, -# "avail-size": 2054, -# "desc-size": 16384, -# "used-size": 8198, -# "desc": 140141447430144, -# "num": 1024, -# "call": 0, -# "avail": 140141447446528, -# "desc-phys": 5216108544, -# "used": 140141447448640, -# "kick": 0 -# } -# } -# -# 2. Get vhost_virtqueue status for vhost-vsock -# -# -> { "execute": "x-query-virtio-vhost-queue-status", -# "arguments": { "path": "/machine/peripheral/vsock0/virtio-backend", -# "queue": 0 } -# } -# <- { "return": { -# "avail-phys": 5182261248, -# "name": "vhost-vsock", -# "used-phys": 5182261568, -# "avail-size": 262, -# "desc-size": 2048, -# "used-size": 1030, -# "desc": 140141413580800, -# "num": 128, -# "call": 0, -# "avail": 140141413582848, -# "desc-phys": 5182259200, -# "used": 140141413583168, -# "kick": 0 -# } -# } +# .. qmp-example:: +# :title: 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 +# } +# } +# +# .. 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' }, @@ -837,94 +851,158 @@ # # 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 -# -# -> { "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 +# } +# +# .. 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 +# } +# ], +# "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 -# -# -> { "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 +# } +# +# .. 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 +# } +# ], +# "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' ] } + +## +# @VMAppleVirtioBlkVariant: +# +# @unspecified: The default, not a valid setting. +# +# @root: Block device holding the root volume +# +# @aux: Block device holding auxiliary data required for boot +# +# Since: 9.2 +## +{ 'enum': 'VMAppleVirtioBlkVariant', + 'data': [ 'unspecified', 'root', 'aux' ] } diff --git a/qapi/yank.json b/qapi/yank.json index 87ec7cab968..30f46c97c98 100644 --- a/qapi/yank.json +++ b/qapi/yank.json @@ -49,6 +49,8 @@ # 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 @@ -74,21 +76,20 @@ # 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 ## @@ -103,13 +104,13 @@ # # 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 1b1dab5b176..c9dd70a8920 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 0756dbb8355..e75707180d2 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -32,8 +32,8 @@ #include "qapi/qapi-commands-block-core.h" #include "qapi/qapi-visit-block-core.h" #include "qapi/qobject-output-visitor.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qdict.h" #include "qemu/cutils.h" #include "qemu/config-file.h" #include "qemu/option.h" @@ -45,7 +45,7 @@ #include "qemu/units.h" #include "qemu/memalign.h" #include "qom/object_interfaces.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "block/block_int.h" #include "block/blockjob.h" #include "block/dirty-bitmap.h" @@ -235,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; @@ -262,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; } @@ -960,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; @@ -1050,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 { @@ -1065,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; } @@ -1274,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; @@ -1559,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)); @@ -1707,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; @@ -2093,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 @@ -2107,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; } @@ -2749,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; } @@ -3108,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); } @@ -3135,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. @@ -3172,6 +3190,7 @@ 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, @@ -3189,6 +3208,7 @@ 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->filename != !next->filename || @@ -3466,7 +3486,10 @@ 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': %s", snapshot_name, strerror(-ret)); @@ -3482,6 +3505,8 @@ static int img_snapshot(int argc, char **argv) break; case SNAPSHOT_DELETE: + bdrv_drain_all_begin(); + 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 " @@ -3495,6 +3520,8 @@ static int img_snapshot(int argc, char **argv) ret = 1; } } + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); break; } @@ -3512,17 +3539,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; @@ -3536,9 +3567,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; @@ -3586,6 +3618,9 @@ static int img_rebase(int argc, char **argv) case 'U': force_share = true; break; + case 'c': + compress = true; + break; } } @@ -3636,7 +3671,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) { @@ -3646,10 +3692,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(), @@ -3679,7 +3743,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 = @@ -3743,11 +3809,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) { @@ -3784,7 +3855,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); @@ -3801,11 +3873,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) { @@ -3813,38 +3887,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; @@ -3858,13 +3954,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", @@ -3874,6 +3971,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); } @@ -4116,6 +4216,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) { @@ -4254,9 +4356,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; } @@ -4276,6 +4380,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; @@ -4285,6 +4390,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; @@ -4383,7 +4490,11 @@ static void bench_cb(void *opaque, int ret) */ b->in_flight++; b->offset += b->step; - b->offset %= b->image_size; + if (b->image_size <= b->bufsize) { + b->offset = 0; + } else { + b->offset %= b->image_size - b->bufsize; + } if (b->write) { acb = blk_aio_pwritev(b->blk, offset, b->qiov, 0, bench_cb, b); } else { @@ -4462,7 +4573,7 @@ static int img_bench(int argc, char **argv) { unsigned long res; - if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > INT_MAX) { + if (qemu_strtoul(optarg, NULL, 0, &res) <= 0 || res > INT_MAX) { error_report("Invalid queue depth specified"); return 1; } diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 3f75d2f5a66..13e03301624 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -10,9 +10,9 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu-io.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "block/block.h" #include "block/block_int.h" /* for info_f() */ #include "block/qapi.h" @@ -1739,12 +1739,26 @@ 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; - nr_zones = cvtnum(argv[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); @@ -1780,8 +1794,16 @@ static int zone_open_f(BlockBackend *blk, int argc, char **argv) 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)); @@ -1805,8 +1827,16 @@ static int zone_close_f(BlockBackend *blk, int argc, char **argv) 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)); @@ -1830,8 +1860,16 @@ static int zone_finish_f(BlockBackend *blk, int argc, char **argv) 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)); @@ -1855,8 +1893,16 @@ static int zone_reset_f(BlockBackend *blk, int argc, char **argv) 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)); @@ -2037,6 +2083,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); } diff --git a/qemu-io.c b/qemu-io.c index 2bd7bfb6507..8f2de83f3c8 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -27,10 +27,10 @@ #include "qemu/readline.h" #include "qemu/log.h" #include "qemu/sockets.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qstring.h" +#include "qobject/qdict.h" #include "qom/object_interfaces.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "block/block_int.h" #include "trace/control.h" #include "crypto/init.h" @@ -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 8c80f7a4ed6..1c081db2870 100644 --- a/qemu-keymap.c +++ b/qemu-keymap.c @@ -116,7 +116,6 @@ static void walk_map(struct xkb_keymap *map, xkb_keycode_t code, void *data) if (kshift != kaltgrshift && kaltgr != kaltgrshift) { print_sym(kaltgrshift, qcode, " shift altgr"); } - return; } static void usage(FILE *out) @@ -213,6 +212,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); @@ -235,7 +235,7 @@ int main(int argc, char *argv[]) state = xkb_state_new(map); xkb_keymap_key_for_each(map, walk_map, state); xkb_state_unref(state); - state = NULL; + xkb_keymap_unref(map); /* add quirks */ fprintf(outfile, diff --git a/qemu-nbd.c b/qemu-nbd.c index 30eeb6f3c75..ed5895861bb 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -24,8 +24,8 @@ #include "qemu/help-texts.h" #include "qapi/error.h" #include "qemu/cutils.h" -#include "sysemu/block-backend.h" -#include "sysemu/runstate.h" /* for qemu_system_killed() prototype */ +#include "system/block-backend.h" +#include "system/runstate.h" /* for qemu_system_killed() prototype */ #include "block/block_int.h" #include "block/nbd.h" #include "qemu/main-loop.h" @@ -37,8 +37,8 @@ #include "qemu/log.h" #include "qemu/systemd.h" #include "block/snapshot.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qom/object_interfaces.h" #include "io/channel-socket.h" #include "io/net-listener.h" @@ -57,19 +57,20 @@ #define HAVE_NBD_DEVICE 0 #endif -#define SOCKET_PATH "/var/lock/qemu-nbd-%s" -#define QEMU_NBD_OPT_CACHE 256 -#define QEMU_NBD_OPT_AIO 257 -#define QEMU_NBD_OPT_DISCARD 258 -#define QEMU_NBD_OPT_DETECT_ZEROES 259 -#define QEMU_NBD_OPT_OBJECT 260 -#define QEMU_NBD_OPT_TLSCREDS 261 -#define QEMU_NBD_OPT_IMAGE_OPTS 262 -#define QEMU_NBD_OPT_FORK 263 -#define QEMU_NBD_OPT_TLSAUTHZ 264 -#define QEMU_NBD_OPT_PID_FILE 265 -#define QEMU_NBD_OPT_SELINUX_LABEL 266 -#define QEMU_NBD_OPT_TLSHOSTNAME 267 +#define SOCKET_PATH "/var/lock/qemu-nbd-%s" +#define QEMU_NBD_OPT_CACHE 256 +#define QEMU_NBD_OPT_AIO 257 +#define QEMU_NBD_OPT_DISCARD 258 +#define QEMU_NBD_OPT_DETECT_ZEROES 259 +#define QEMU_NBD_OPT_OBJECT 260 +#define QEMU_NBD_OPT_TLSCREDS 261 +#define QEMU_NBD_OPT_IMAGE_OPTS 262 +#define QEMU_NBD_OPT_FORK 263 +#define QEMU_NBD_OPT_TLSAUTHZ 264 +#define QEMU_NBD_OPT_PID_FILE 265 +#define QEMU_NBD_OPT_SELINUX_LABEL 266 +#define QEMU_NBD_OPT_TLSHOSTNAME 267 +#define QEMU_NBD_OPT_HANDSHAKE_LIMIT 268 #define MBR_SIZE 512 @@ -80,6 +81,7 @@ static int nb_fds; static QIONetListener *server; static QCryptoTLSCreds *tlscreds; static const char *tlsauthz; +static int handshake_limit = NBD_DEFAULT_HANDSHAKE_MAX_SECS; static void usage(const char *name) { @@ -101,6 +103,7 @@ static void usage(const char *name) " -v, --verbose display extra debugging information\n" " -x, --export-name=NAME expose export by name (default is empty string)\n" " -D, --description=TEXT export a human-readable description\n" +" --handshake-limit=N limit client's handshake to N seconds (default 10)\n" "\n" "Exposing part of the image:\n" " -o, --offset=OFFSET offset into the image\n" @@ -114,6 +117,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" @@ -219,6 +223,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); @@ -235,6 +240,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++) { @@ -295,7 +303,9 @@ static void *show_parts(void *arg) static void *nbd_client_thread(void *arg) { struct NbdClientOpts *opts = arg; - NBDExportInfo info = { .request_sizes = false, .name = g_strdup("") }; + /* 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; @@ -383,7 +393,8 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, nb_fds++; nbd_update_server_watch(); - nbd_client_new(cioc, tlscreds, tlsauthz, nbd_client_closed); + nbd_client_new(cioc, handshake_limit, + tlscreds, tlsauthz, nbd_client_closed, NULL); } static void nbd_update_server_watch(void) @@ -560,6 +571,8 @@ int main(int argc, char **argv) { "object", required_argument, NULL, QEMU_NBD_OPT_OBJECT }, { "export-name", required_argument, NULL, 'x' }, { "description", required_argument, NULL, 'D' }, + { "handshake-limit", required_argument, NULL, + QEMU_NBD_OPT_HANDSHAKE_LIMIT }, { "tls-creds", required_argument, NULL, QEMU_NBD_OPT_TLSCREDS }, { "tls-hostname", required_argument, NULL, QEMU_NBD_OPT_TLSHOSTNAME }, { "tls-authz", required_argument, NULL, QEMU_NBD_OPT_TLSAUTHZ }, @@ -581,7 +594,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; @@ -805,6 +819,13 @@ int main(int argc, char **argv) case QEMU_NBD_OPT_SELINUX_LABEL: selinux_label = optarg; break; + case QEMU_NBD_OPT_HANDSHAKE_LIMIT: + if (qemu_strtoi(optarg, NULL, 0, &handshake_limit) < 0 || + handshake_limit < 0) { + error_report("Invalid handshake limit '%s'", optarg); + exit(EXIT_FAILURE); + } + break; } } @@ -831,10 +852,6 @@ int main(int argc, char **argv) export_name = ""; } - if (!trace_init_backends()) { - exit(1); - } - trace_init_file(); qemu_set_log(LOG_TRACE, &error_fatal); socket_activation = check_socket_activation(); @@ -937,7 +954,6 @@ int main(int argc, char **argv) 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", @@ -1025,6 +1041,18 @@ int main(int argc, char **argv) #endif /* WIN32 */ } + /* + * trace_init must be done after daemonization. Why? Because at + * least the simple backend spins up a helper thread as well as an + * atexit() handler that waits on that thread, but the helper + * thread won't survive a fork, leading to deadlock in the child + * if we initialized pre-fork. + */ + if (!trace_init_backends()) { + exit(1); + } + trace_init_file(); + if (opts.device != NULL && sockpath == NULL) { sockpath = g_malloc(128); snprintf(sockpath, 128, SOCKET_PATH, basename(opts.device)); @@ -1118,9 +1146,7 @@ int main(int argc, char **argv) qdict_put_str(raw_opts, "file", bs->node_name); qdict_put_int(raw_opts, "offset", dev_offset); - aio_context_acquire(qemu_get_aio_context()); bs = bdrv_open(NULL, NULL, raw_opts, flags, &error_fatal); - aio_context_release(qemu_get_aio_context()); blk_remove_bs(blk); blk_insert_bs(blk, bs, &error_fatal); @@ -1170,7 +1196,6 @@ int main(int argc, char **argv) if (opts.device) { #if HAVE_NBD_DEVICE - int ret; ret = pthread_create(&client_thread, NULL, nbd_client_thread, &opts); if (ret != 0) { error_report("Failed to create client thread: %s", strerror(ret)); @@ -1217,9 +1242,10 @@ int main(int argc, char **argv) qemu_opts_del(sn_opts); if (opts.device) { - void *ret; - pthread_join(client_thread, &ret); - exit(ret != NULL); + 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 6be621c2324..1f862b19a67 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. @@ -36,8 +38,12 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " nvdimm=on|off controls NVDIMM support (default=off)\n" " memory-encryption=@var{} memory encryption object to use (default=none)\n" " hmat=on|off controls ACPI HMAT support (default=off)\n" +#ifdef CONFIG_POSIX + " aux-ram-share=on|off allocate auxiliary guest RAM as shared (default: off)\n" +#endif " memory-backend='backend-id' specifies explicitly provided backend for main RAM (default=none)\n" - " cxl-fmw.0.targets.0=firsttarget,cxl-fmw.0.targets.1=secondtarget,cxl-fmw.0.size=size[,cxl-fmw.0.interleave-granularity=granularity]\n", + " cxl-fmw.0.targets.0=firsttarget,cxl-fmw.0.targets.1=secondtarget,cxl-fmw.0.size=size[,cxl-fmw.0.interleave-granularity=granularity]\n" + " smp-cache.0.cache=cachename,smp-cache.0.topology=topologylevel\n", QEMU_ARCH_ALL) SRST ``-machine [type=]name[,prop=value[,...]]`` @@ -66,8 +72,8 @@ SRST ``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. @@ -99,6 +105,16 @@ SRST Enables or disables ACPI Heterogeneous Memory Attribute Table (HMAT) support. The default is off. + ``aux-ram-share=on|off`` + Allocate auxiliary guest RAM as an anonymous file that is + shareable with an external process. This option applies to + memory allocated as a side effect of creating various devices. + It does not apply to memory-backend-objects, whether explicitly + specified on the command line, or implicitly created by the -m + command line option. The default is off. + + To use the cpr-transfer migration mode, you must set aux-ram-share=on. + ``memory-backend='id'`` An alternative to legacy ``-mem-path`` and ``mem-prealloc`` options. Allows to use a memory backend as main RAM. @@ -149,14 +165,41 @@ 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 + + ``smp-cache.0.cache=cachename,smp-cache.0.topology=topologylevel`` + Define cache properties for SMP system. + + ``cache=cachename`` specifies the cache that the properties will be + applied on. This field is the combination of cache level and cache + type. It supports ``l1d`` (L1 data cache), ``l1i`` (L1 instruction + cache), ``l2`` (L2 unified cache) and ``l3`` (L3 unified cache). + + ``topology=topologylevel`` sets the cache topology level. It accepts + CPU topology levels including ``core``, ``module``, ``cluster``, ``die``, + ``socket``, ``book``, ``drawer`` and a special value ``default``. If + ``default`` is set, then the cache topology will follow the architecture's + default cache topology model. If another topology level is set, the cache + will be shared at corresponding CPU topology level. For example, + ``topology=core`` makes the cache shared by all threads within a core. + The omitting cache will default to using the ``default`` level. + + The default cache topology model for an i386 PC machine is as follows: + ``l1d``, ``l1i``, and ``l2`` caches are per ``core``, while the ``l3`` + cache is per ``die``. + + Example: + + :: + + -machine smp-cache.0.cache=l1d,smp-cache.0.topology=core,smp-cache.1.cache=l1i,smp-cache.1.topology=core ERST DEF("M", HAS_ARG, QEMU_OPTION_M, @@ -188,7 +231,8 @@ DEF("accel", HAS_ARG, QEMU_OPTION_accel, " 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 @@ -269,17 +313,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" @@ -295,7 +349,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 @@ -334,14 +388,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, @@ -727,31 +781,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: @@ -760,6 +806,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, @@ -908,7 +965,7 @@ SRST Sets the period length in microseconds. ``in|out.try-poll=on|off`` - Attempt to use poll mode with the device. Default is on. + Attempt to use poll mode with the device. Default is off. ``threshold=threshold`` Threshold (in microseconds) when playback starts. Default is 0. @@ -945,7 +1002,7 @@ SRST ``in|out.buffer-count=count`` Sets the count of the buffers. - ``in|out.try-poll=on|of`` + ``in|out.try-poll=on|off`` Attempt to use poll mode with the device. Default is on. ``try-mmap=on|off`` @@ -1086,7 +1143,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 @@ -1158,6 +1215,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, @@ -1451,9 +1519,13 @@ SRST (on/off; default: off) ``discard-no-unref`` - When enabled, discards from the guest will not cause cluster - allocations to be relinquished. This prevents qcow2 fragmentation - that would be caused by such discards. Besides potential + 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 @@ -1735,29 +1807,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). This - option is deprecated (since QEMU 8.1) and will be removed in a future - version of QEMU. Use ``local`` instead. - ``synth`` Synthetic filesystem, only used by QTests. @@ -1782,8 +1843,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 @@ -1796,16 +1855,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 @@ -1858,18 +1907,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 @@ -1886,11 +1929,6 @@ SRST ``local`` Accesses to the filesystem are done by QEMU. - ``proxy`` - Accesses to the filesystem are done by virtfs-proxy-helper(1). - This option is deprecated (since QEMU 8.1) and will be removed in a - future version of QEMU. Use ``local`` instead. - ``synth`` Synthetic filesystem, only used by QTests. @@ -1915,8 +1953,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 @@ -1929,16 +1965,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 @@ -1953,32 +1979,37 @@ SRST Specifies the tag name to be used by the guest to mount this export point. - ``multidevs=multidevs`` - Specifies how to deal with multiple devices being shared with a - 9p export. Supported behaviours are either "remap", "forbid" or - "warn". The latter is the default behaviour on which virtfs 9p - expects only one device to be shared with the same export, and - if more than one device is shared and accessed via the same 9p - export then only a warning message is logged (once) by qemu on - host side. In order to avoid file ID collisions on guest you - should either create a separate virtfs export for each device to - be shared with guests (recommended way) or you might use "remap" - instead which allows you to share multiple devices with only one - export instead, which is achieved by remapping the original - inode numbers from host to guest in a way that would prevent - such collisions. Remapping inodes in such use cases is required + ``multidevs=remap|forbid|warn`` + Specifies how to deal with multiple devices being shared with + the same 9p export in order to avoid file ID collisions on guest. + Supported behaviours are either "remap" (default), "forbid" or + "warn". + + ``remap`` : assumes the possibility that more than one device is + shared with the same 9p export. Therefore inode numbers from host + are remapped for guest in a way that would prevent file ID + collisions on guest. Remapping inodes in such cases is required because the original device IDs from host are never passed and exposed on guest. Instead all files of an export shared with - virtfs always share the same device id on guest. So two files + virtfs always share the same device ID on guest. So two files with identical inode numbers but from actually different devices on host would otherwise cause a file ID collision and hence - potential misbehaviours on guest. "forbid" on the other hand - assumes like "warn" that only one device is shared by the same - export, however it will not only log a warning message but also - deny access to additional devices on guest. Note though that - "forbid" does currently not block all possible file access - operations (e.g. readdir() would still return entries from other - devices). + potential severe misbehaviours on guest. + + ``warn`` : virtfs 9p expects only one device to be shared with + the same export. If however more than one device is shared and + accessed via the same 9p export then only a warning message is + logged (once) by qemu on host side. No further action is performed + in this case that would prevent file ID collisions on guest. This + could thus lead to severe misbehaviours in this case like wrong + files being accessed and data corruption on the exported tree. + + ``forbid`` : assumes like "warn" that only one device is shared + by the same 9p export, however it will not only log a warning + message but also deny access to additional devices on guest. Note + though that "forbid" does currently not block all possible file + access operations (e.g. readdir() would still return entries from + other devices). ERST DEF("iscsi", HAS_ARG, QEMU_OPTION_iscsi, @@ -2067,7 +2098,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" @@ -2077,6 +2108,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" @@ -2084,9 +2117,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" @@ -2181,10 +2211,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 @@ -2331,22 +2377,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) @@ -2418,8 +2448,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 @@ -2433,7 +2465,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. @@ -2470,6 +2502,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. @@ -2606,7 +2642,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, @@ -2615,23 +2652,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. Deprecated, use '-machine hpet=off' instead. + needed to boot from old floppy disks. Synonym of ``-m fd-bootchk=off``. ERST DEF("acpitable", HAS_ARG, QEMU_OPTION_acpitable, @@ -2667,7 +2688,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" @@ -2678,7 +2699,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_LOONGARCH) + QEMU_ARCH_I386 | QEMU_ARCH_ARM | QEMU_ARCH_LOONGARCH | QEMU_ARCH_RISCV) SRST ``-smbios file=binary`` Load SMBIOS entry from binary file. @@ -2695,9 +2716,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 @@ -2855,9 +2879,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][,reconnect=seconds]\n" - "-netdev stream,id=str[,server=on|off],addr.type=unix,addr.path=path[,abstract=on|off][,tight=on|off][,reconnect=seconds]\n" - "-netdev stream,id=str[,server=on|off],addr.type=fd,addr.str=file-descriptor[,reconnect=seconds]\n" + "-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" @@ -2882,6 +2906,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" @@ -2927,6 +2964,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 @@ -2955,6 +2995,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 @@ -2962,7 +3005,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 @@ -3062,6 +3105,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" @@ -3292,7 +3337,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 @@ -3307,7 +3541,7 @@ SRST ``dst=dstaddr`` destination address (mandatory) - ``udp`` + ``udp=on`` select udp encapsulation (default is ip). ``srcport=srcport`` @@ -3316,7 +3550,7 @@ SRST ``dstport=dstport`` destination udp port. - ``ipv6`` + ``ipv6=on`` force v6, otherwise defaults to v4. ``rxcookie=rxcookie``; \ ``txcookie=txcookie`` @@ -3324,7 +3558,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`` @@ -3358,7 +3592,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 @@ -3376,6 +3610,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 @@ -3442,9 +3725,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" @@ -3459,7 +3742,7 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-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 @@ -3483,7 +3766,7 @@ SRST 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``, + Backend is one of: ``null``, ``socket``, ``udp``, ``msmouse``, ``hub``, ``vc``, ``ringbuf``, ``file``, ``pipe``, ``console``, ``serial``, ``pty``, ``stdio``, ``braille``, ``parallel``, ``spicevmc``, ``spiceport``. The specific backend will determine the @@ -3540,9 +3823,10 @@ The general form of a character device option is: the QEMU monitor, and ``-nographic`` also multiplexes the console and the monitor to stdio. - There is currently no support for multiplexing in the other - direction (where a single QEMU front end takes input and output from - multiple chardevs). + If you need to aggregate data in the opposite direction (where one + QEMU frontend interface receives input and output from multiple + backend chardev devices), please refer to the paragraph below + regarding chardev ``hub`` aggregator device configuration. Every backend supports the ``logfile`` option, which supplies the path to a file to record all data transmitted via the backend. The @@ -3555,7 +3839,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 @@ -3572,9 +3856,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 @@ -3642,6 +3926,46 @@ The available backends are: Forward QEMU's emulated msmouse events to the guest. ``msmouse`` does not take any options. +``-chardev hub,id=id,chardevs.0=id[,chardevs.N=id]`` + Explicitly create chardev backend hub device with the possibility + to aggregate input from multiple backend devices and forward it to + a single frontend device. Additionally, ``hub`` device takes the + output from the frontend device and sends it back to all the + connected backend devices. This allows for seamless interaction + between different backend devices and a single frontend + interface. Aggregation supported for up to 4 chardev + devices. (Since 10.0) + + For example, the following is a use case of 2 backend devices: + virtual console ``vc0`` and a pseudo TTY ``pty0`` connected to + a single virtio hvc console frontend device with a hub ``hub0`` + help. Virtual console renders text to an image, which can be + shared over the VNC protocol. In turn, pty backend provides + bidirectional communication to the virtio hvc console over the + pseudo TTY file. The example configuration can be as follows: + + :: + + -chardev pty,path=/tmp/pty,id=pty0 \ + -chardev vc,id=vc0 \ + -chardev hub,id=hub0,chardevs.0=pty0,chardevs.1=vc0 \ + -device virtconsole,chardev=hub0 \ + -vnc 0.0.0.0:0 + + Once QEMU starts VNC client and any TTY emulator can be used to + control a single hvc console: + + :: + + # Start TTY emulator + tio /tmp/pty + + # Start VNC client and switch to virtual console Ctrl-Alt-2 + vncviewer :0 + + Several frontend devices is not supported. Stacking of multiplexers + and hub devices is not supported as well. + ``-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]`` Connect to a QEMU text console. ``vc`` may optionally be given a specific size. @@ -3698,12 +4022,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. @@ -3898,6 +4232,13 @@ SRST or in multiboot format. ERST +DEF("shim", HAS_ARG, QEMU_OPTION_shim, \ + "-shim shim.efi use 'shim.efi' to boot the kernel\n", QEMU_ARCH_ALL) +SRST +``-shim shim.efi`` + Use 'shim.efi' to boot the kernel +ERST + DEF("append", HAS_ARG, QEMU_OPTION_append, \ "-append cmdline use 'cmdline' as kernel command line\n", QEMU_ARCH_ALL) SRST @@ -3907,15 +4248,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, \ @@ -3998,9 +4347,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 @@ -4027,10 +4380,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: @@ -4048,14 +4402,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`` @@ -4112,14 +4484,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 @@ -4149,7 +4521,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. @@ -4266,14 +4638,6 @@ SRST from a script. ERST -DEF("singlestep", 0, QEMU_OPTION_singlestep, \ - "-singlestep deprecated synonym for -accel tcg,one-insn-per-tb=on\n", QEMU_ARCH_ALL) -SRST -``-singlestep`` - This is a deprecated synonym for the TCG accelerator property - ``one-insn-per-tb``. -ERST - DEF("preconfig", 0, QEMU_OPTION_preconfig, \ "--preconfig pause QEMU before machine is initialized (experimental)\n", QEMU_ARCH_ALL) @@ -4296,21 +4660,25 @@ SRST ERST DEF("overcommit", HAS_ARG, QEMU_OPTION_overcommit, - "-overcommit [mem-lock=on|off][cpu-pm=on|off]\n" + "-overcommit [mem-lock=on|off|on-fault][cpu-pm=on|off]\n" " run qemu with overcommit hints\n" - " mem-lock=on|off controls memory lock support (default: off)\n" + " mem-lock=on|off|on-fault controls memory lock support (default: off)\n" " cpu-pm=on|off controls cpu power management (default: off)\n", QEMU_ARCH_ALL) SRST -``-overcommit mem-lock=on|off`` +``-overcommit mem-lock=on|off|on-fault`` \ ``-overcommit cpu-pm=on|off`` Run qemu with hints about host resource overcommit. The default is to assume that host overcommits all resources. Locking qemu and guest memory can be enabled via ``mem-lock=on`` - (disabled by default). This works when host memory is not - overcommitted and reduces the worst-case latency for guest. + or ``mem-lock=on-fault`` (disabled by default). This works when + host memory is not overcommitted and reduces the worst-case latency for + guest. The on-fault option is better for reducing the memory footprint + since it makes allocations lazy, but the pages still get locked in place + once faulted by the guest or QEMU. Note that the two options are mutually + exclusive. Guest ability to manage power state of host cpus (increasing latency for other processes on the same host cpu, but decreasing latency for @@ -4494,7 +4862,7 @@ SRST Start right away with a saved state (``loadvm`` in monitor) ERST -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(EMSCRIPTEN) DEF("daemonize", 0, QEMU_OPTION_daemonize, \ "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL) #endif @@ -4568,13 +4936,13 @@ SRST with actual performance. When the virtual cpu is sleeping, the virtual time will advance at - default speed unless ``sleep=on`` is specified. With - ``sleep=on``, the virtual time will jump to the next timer + default speed unless ``sleep=off`` is specified. With + ``sleep=off``, the virtual time will jump to the next timer deadline instantly whenever the virtual cpu goes to sleep mode and will not advance if no timer is enabled. This behavior gives deterministic execution times from the guest point of view. - The default if icount is enabled is ``sleep=off``. - ``sleep=on`` cannot be used together with either ``shift=auto`` + The default if icount is enabled is ``sleep=on``. + ``sleep=off`` cannot be used together with either ``shift=auto`` or ``align=on``. ``align=on`` will activate the delay algorithm which will try to @@ -4648,13 +5016,22 @@ 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" \ + "-incoming \n" \ + " accept incoming migration on the migration channel\n" \ "-incoming defer\n" \ " wait for the URI to be specified via migrate_incoming\n", QEMU_ARCH_ALL) SRST +The -incoming option specifies the migration channel for an incoming +migration. It may be used multiple times to specify multiple +migration channel types. The channel type is specified in , +or is 'main' for all other forms of -incoming. If multiple -incoming +options are specified for a channel type, the last one takes precedence. + ``-incoming tcp:[host]:port[,to=maxport][,ipv4=on|off][,ipv6=on|off]`` \ ``-incoming rdma:host:port[,ipv4=on|off][,ipv6=on|off]`` @@ -4664,12 +5041,29 @@ 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 command. +``-incoming `` + Accept incoming migration on the migration channel. For the syntax + of , see the QAPI documentation of ``MigrationChannel``. + Examples: + :: + + -incoming '{"channel-type": "main", + "addr": { "transport": "socket", + "type": "unix", + "path": "my.sock" }}' + + -incoming main,addr.transport=socket,addr.type=unix,addr.path=my.sock + ``-incoming defer`` Wait for the URI to be specified via migrate\_incoming. The monitor can be used to change settings (such as migration parameters) prior @@ -4694,30 +5088,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 (deprecated)\n", - QEMU_ARCH_ALL) -#endif -SRST -``-chroot dir`` - Deprecated, use '-run-with chroot=...' instead. - 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" \ - " user can be numeric uid:gid instead\n", - QEMU_ARCH_ALL) -#endif -SRST -``-runas user`` - Immediately before starting guest execution, drop root privileges, - switching to the specified user. -ERST - DEF("prom-env", HAS_ARG, QEMU_OPTION_prom_env, "-prom-env variable=value\n" " set OpenBIOS nvram variables\n", @@ -4740,10 +5110,10 @@ 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 :ref:`Semihosting` mode (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V only). + Enable :ref:`Semihosting` mode (ARM, M68K, Xtensa, MIPS, RISC-V only). .. warning:: Note that this allows guest direct access to the host filesystem, so @@ -4756,10 +5126,10 @@ 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 :ref:`Semihosting` (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V + Enable and configure :ref:`Semihosting` (ARM, M68K, Xtensa, MIPS, RISC-V only). .. warning:: @@ -4879,25 +5249,17 @@ 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", - QEMU_ARCH_ALL) -SRST -``-async-teardown`` - This option is deprecated and should no longer be used. The new option - ``-run-with async-teardown=on`` is a replacement. -ERST -#endif -#ifdef CONFIG_POSIX +#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN) DEF("run-with", HAS_ARG, QEMU_OPTION_run_with, - "-run-with [async-teardown=on|off][,chroot=dir]\n" + "-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", + " 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) SRST -``-run-with [async-teardown=on|off][,chroot=dir]`` +``-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 @@ -4913,7 +5275,14 @@ SRST ``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. + in combination with ``user=...``. + + ``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 @@ -4995,7 +5364,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,offset=offset,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. @@ -5014,9 +5383,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 @@ -5085,6 +5451,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 @@ -5117,6 +5497,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 @@ -5362,7 +5770,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 @@ -5377,7 +5785,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 @@ -5388,7 +5796,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 @@ -5401,10 +5809,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 diff --git a/qemu.nsi b/qemu.nsi index 564d617d11f..d419986ca06 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 @@ -202,7 +204,6 @@ Section "Uninstall" Delete "$INSTDIR\*.bmp" Delete "$INSTDIR\*.bin" Delete "$INSTDIR\*.dll" - Delete "$INSTDIR\*.dtb" Delete "$INSTDIR\*.fd" Delete "$INSTDIR\*.img" Delete "$INSTDIR\*.lid" @@ -213,6 +214,7 @@ Section "Uninstall" Delete "$INSTDIR\qemu-io.exe" Delete "$INSTDIR\qemu.exe" Delete "$INSTDIR\qemu-system-*.exe" + RMDir /r "$INSTDIR\dtb" RMDir /r "$INSTDIR\doc" RMDir /r "$INSTDIR\share" ; Remove generated files diff --git a/qga/commands-bsd.c b/qga/commands-bsd.c index 17bddda1cfb..94ff6fee6ad 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" @@ -149,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 00000000000..537869fb98b --- /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 00000000000..14d955fa843 --- /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 8c1c56aac97..263e7c05251 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 214e408fcdd..9e8a934b9a6 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, @@ -43,6 +58,22 @@ static int dev_major_minor(const char *devpath, return -1; } +/* + * Check if we already have the devmajor:devminor in the mounts + * If thats the case return true. + */ +static bool dev_exists(FsMountList *mounts, unsigned int devmajor, unsigned int devminor) +{ + FsMount *mount; + + QTAILQ_FOREACH(mount, mounts, next) { + if (mount->devmajor == devmajor && mount->devminor == devminor) { + return true; + } + } + return false; +} + static bool build_fs_mount_list_from_mtab(FsMountList *mounts, Error **errp) { struct mntent *ment; @@ -73,6 +104,10 @@ static bool build_fs_mount_list_from_mtab(FsMountList *mounts, Error **errp) /* Skip bind mounts */ continue; } + if (dev_exists(mounts, devmajor, devminor)) { + /* Skip already existing devices (bind mounts) */ + continue; + } mount = g_new0(FsMount, 1); mount->dirname = g_strdup(ment->mnt_dir); @@ -156,6 +191,11 @@ bool build_fs_mount_list(FsMountList *mounts, Error **errp) } } + if (dev_exists(mounts, devmajor, devminor)) { + /* Skip already existing devices (bind mounts) */ + continue; + } + mount = g_new0(FsMount, 1); mount->dirname = g_strdup(line + dir_s); mount->devtype = g_strdup(dash + type_s); @@ -284,3 +324,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 236f80de441..246171d3236 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" }; @@ -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 6169bbf7a01..12bc086d79e 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -18,36 +18,27 @@ #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__) +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(CONFIG_SOLARIS) #include #include +#if !defined(ETHER_ADDR_LEN) && defined(ETHERADDRL) +#define ETHER_ADDR_LEN ETHERADDRL +#endif #else #include #endif @@ -56,7 +47,7 @@ #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; @@ -67,18 +58,165 @@ static void ga_wait_child(pid_t pid, int *status, Error **errp) 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; +} + +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"; @@ -107,67 +245,31 @@ void qmp_guest_shutdown(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) { @@ -198,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 { @@ -647,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; @@ -657,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; - } - - 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; - } + const char *argv[] = {hook, arg_str, NULL}; - ga_wait_child(pid, &status, &local_err); + slog("executing fsfreeze hook with arg '%s'", arg_str); + 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; - } } /* @@ -757,8 +805,10 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp) int ret; ret = qmp_guest_fsfreeze_do_thaw(errp); + if (ret >= 0) { ga_unset_frozen(ga_state); + slog("guest-fsthaw called"); execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp); } else { ret = 0; @@ -782,2049 +832,82 @@ 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) +#if defined(__linux__) || defined(__FreeBSD__) +void qmp_guest_set_user_password(const char *username, + const char *password, + bool crypted, + 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; - } + Error *local_err = NULL; + g_autofree char *rawpasswddata = NULL; + size_t rawpasswdlen; - g_debug("unsupported driver or sysfs path '%s'", syspath); - return false; + rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp); + if (!rawpasswddata) { + return; } + rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1); + rawpasswddata[rawpasswdlen] = '\0'; - 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; + if (strchr(rawpasswddata, '\n')) { + error_setg(errp, "forbidden characters in raw password"); + return; } - 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; - } + if (strchr(username, '\n') || + strchr(username, ':')) { + error_setg(errp, "forbidden characters in username"); + return; } - pciaddr->domain = pci[0]; - pciaddr->bus = pci[1]; - pciaddr->slot = pci[2]; - pciaddr->function = pci[3]; +#ifdef __FreeBSD__ + 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 + 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 - 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; + ga_run_command(argv, chpasswddata, "set user password", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; } - - ret = true; - -cleanup: - g_free(driver); - return ret; } +#endif /* __linux__ || __FreeBSD__ */ -/* - * 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) +#ifdef HAVE_GETIFADDRS +static GuestNetworkInterface * +guest_find_interface(GuestNetworkInterfaceList *head, + const char *name) { - 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; + for (; head; head = head->next) { + if (strcmp(head->value->name, name) == 0) { + return head->value; + } } - return true; + return NULL; } -/* - * 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) +static int guest_get_network_stats(const char *name, + GuestNetworkInterfaceStat *stats) { - 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; -} - -#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)); - 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) -{ - 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, - bool crypted, - Error **errp) -{ - Error *local_err = NULL; - char *passwd_path = NULL; - pid_t pid; - int status; - int datafd[2] = { -1, -1 }; - char *rawpasswddata = NULL; - size_t rawpasswdlen; - char *chpasswddata = NULL; - size_t chpasswdlen; - - rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp); - if (!rawpasswddata) { - return; - } - rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1); - rawpasswddata[rawpasswdlen] = '\0'; - - if (strchr(rawpasswddata, '\n')) { - error_setg(errp, "forbidden characters in raw password"); - goto out; - } - - if (strchr(username, '\n') || - strchr(username, ':')) { - error_setg(errp, "forbidden characters in username"); - goto out; - } - -#ifdef __FreeBSD__ - chpasswddata = g_strdup(rawpasswddata); - passwd_path = g_find_program_in_path("pw"); -#else - chpasswddata = g_strdup_printf("%s:%s\n", username, rawpasswddata); - passwd_path = g_find_program_in_path("chpasswd"); -#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); - if (local_err) { - error_propagate(errp, local_err); - goto out; - } - - 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, - const char *name) -{ - for (; head; head = head->next) { - if (strcmp(head->value->name, name) == 0) { - return head->value; - } - } - - return NULL; -} - -static int guest_get_network_stats(const char *name, - GuestNetworkInterfaceStat *stats) -{ -#ifdef CONFIG_LINUX - int name_len; - char const *devinfo = "/proc/net/dev"; - FILE *fp; - char *line = NULL, *colon; - size_t n = 0; - fp = fopen(devinfo, "r"); - if (!fp) { - g_debug("failed to open network stats %s: %s", devinfo, - g_strerror(errno)); - return -1; +#ifdef CONFIG_LINUX + int name_len; + char const *devinfo = "/proc/net/dev"; + FILE *fp; + char *line = NULL, *colon; + size_t n = 0; + fp = fopen(devinfo, "r"); + if (!fp) { + g_debug("failed to open network stats %s: %s", devinfo, + g_strerror(errno)); + return -1; } name_len = strlen(name); while (getline(&line, &n, fp) != -1) { @@ -3052,131 +1135,8 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) 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) { @@ -3239,15 +1199,7 @@ GuestUserList *qmp_guest_get_users(Error **errp) return head; } -#else - -GuestUserList *qmp_guest_get_users(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -#endif +#endif /* HAVE_UTMPX */ /* Replace escaped special characters with their real values. The replacement * is done in place -- returned value is in the original string. @@ -3384,13 +1336,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 @@ -3425,3 +1370,23 @@ char *qga_get_host_name(Error **errp) return g_steal_pointer(&hostname); } + +#ifdef CONFIG_GETLOADAVG +GuestLoadAverage *qmp_guest_get_load(Error **errp) +{ + double loadavg[3]; + GuestLoadAverage *ret = NULL; + + if (getloadavg(loadavg, G_N_ELEMENTS(loadavg)) < 0) { + error_setg_errno(errp, errno, + "cannot query load average"); + return NULL; + } + + ret = g_new0(GuestLoadAverage, 1); + ret->load1m = loadavg[0]; + ret->load5m = loadavg[1]; + ret->load15m = loadavg[2]; + return ret; +} +#endif diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 6beae659b79..82274808101 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "guest-agent-core.h" #include "vss-win32.h" @@ -119,6 +120,28 @@ static OpenFlags guest_file_open_modes[] = { {"a+b", FILE_GENERIC_APPEND | GENERIC_READ, OPEN_ALWAYS } }; +/* + * We use an exponentially weighted moving average, just like Unix systems do + * https://en.wikipedia.org/wiki/Load_(computing)#Unix-style_load_calculation + * + * These constants serve as the damping factor and are calculated with + * 1 / exp(sampling interval in seconds / window size in seconds) + * + * This formula comes from linux's include/linux/sched/loadavg.h + * https://github.com/torvalds/linux/blob/345671ea0f9258f410eb057b9ced9cefbbe5dc78/include/linux/sched/loadavg.h#L20-L23 + */ +#define LOADAVG_FACTOR_1F 0.9200444146293232478931553241 +#define LOADAVG_FACTOR_5F 0.9834714538216174894737477501 +#define LOADAVG_FACTOR_15F 0.9944598480048967508795473394 +/* + * The time interval in seconds between taking load counts, same as Linux + */ +#define LOADAVG_SAMPLING_INTERVAL 5 + +double load_avg_1m; +double load_avg_5m; +double load_avg_15m; + #define debug_error(msg) do { \ char *suffix = g_win32_error_message(GetLastError()); \ g_debug("%s: %s", (msg), suffix); \ @@ -217,6 +240,9 @@ int64_t qmp_guest_file_open(const char *path, const char *mode, Error **errp) 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; } @@ -244,10 +270,6 @@ int64_t qmp_guest_file_open(const char *path, const char *mode, Error **errp) 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; } @@ -279,8 +301,7 @@ static void acquire_privilege(const char *name, Error **errp) TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) { if (!LookupPrivilegeValue(NULL, name, &priv.Privileges[0].Luid)) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "no luid for requested privilege"); + error_setg(errp, "no luid for requested privilege"); goto out; } @@ -288,14 +309,12 @@ 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(errp, QERR_QGA_COMMAND_FAILED, - "unable to acquire requested privilege"); + error_setg(errp, "unable to acquire requested privilege"); goto out; } } else { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "failed to open privilege token"); + error_setg(errp, "failed to open privilege token"); } out: @@ -309,8 +328,7 @@ 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"); } } @@ -501,13 +519,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) { @@ -838,8 +849,6 @@ static void get_disk_properties(HANDLE vol_h, GuestDiskAddress *disk, } out_free: g_free(dev_desc); - - return; } static void get_single_disk_info(int disk_number, @@ -903,7 +912,6 @@ static void get_single_disk_info(int disk_number, err_close: CloseHandle(disk_h); - return; } /* VSS provider works with volumes, thus there is no difference if @@ -942,6 +950,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( @@ -1148,6 +1158,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 +1223,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 +1251,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,13 +1286,16 @@ 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; } qga_vss_fsfreeze(&i, false, NULL, errp); ga_unset_frozen(ga_state); + + slog("guest-fsthaw called"); + return i; } @@ -1423,22 +1437,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: @@ -1506,11 +1517,6 @@ void qmp_guest_suspend_ram(Error **errp) } } -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; @@ -1874,12 +1880,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) { @@ -1937,7 +1937,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; } @@ -1950,11 +1950,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; } @@ -1970,64 +1976,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) { @@ -2125,49 +2078,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) @@ -2180,26 +2131,25 @@ 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; } rtl_get_version_t rtl_get_version = (rtl_get_version_t)fun; rtl_get_version(info); - 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) { @@ -2518,14 +2468,127 @@ char *qga_get_host_name(Error **errp) return g_utf16_to_utf8(tmp, size, NULL, NULL, NULL); } -GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) + +static VOID CALLBACK load_avg_callback(PVOID hCounter, BOOLEAN timedOut) { - error_setg(errp, QERR_UNSUPPORTED); - return NULL; + PDH_FMT_COUNTERVALUE displayValue; + double currentLoad; + PDH_STATUS err; + + err = PdhGetFormattedCounterValue( + (PDH_HCOUNTER)hCounter, PDH_FMT_DOUBLE, 0, &displayValue); + /* Skip updating the load if we can't get the value successfully */ + if (err != ERROR_SUCCESS) { + slog("PdhGetFormattedCounterValue failed to get load value with 0x%lx", + err); + return; + } + currentLoad = displayValue.doubleValue; + + load_avg_1m = load_avg_1m * LOADAVG_FACTOR_1F + currentLoad * \ + (1.0 - LOADAVG_FACTOR_1F); + load_avg_5m = load_avg_5m * LOADAVG_FACTOR_5F + currentLoad * \ + (1.0 - LOADAVG_FACTOR_5F); + load_avg_15m = load_avg_15m * LOADAVG_FACTOR_15F + currentLoad * \ + (1.0 - LOADAVG_FACTOR_15F); } -GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) +static BOOL init_load_avg_counter(Error **errp) { - error_setg(errp, QERR_UNSUPPORTED); - return NULL; + CONST WCHAR *szCounterPath = L"\\System\\Processor Queue Length"; + PDH_STATUS status; + BOOL ret; + HQUERY hQuery; + HCOUNTER hCounter; + HANDLE event; + HANDLE waitHandle; + + status = PdhOpenQueryW(NULL, 0, &hQuery); + if (status != ERROR_SUCCESS) { + /* + * If the function fails, the return value is a system error code or + * a PDH error code. error_setg_win32 cant translate PDH error code + * properly, so just report it as is. + */ + error_setg_win32(errp, (DWORD)status, + "PdhOpenQueryW failed with 0x%lx", status); + return FALSE; + } + + status = PdhAddEnglishCounterW(hQuery, szCounterPath, 0, &hCounter); + if (status != ERROR_SUCCESS) { + error_setg_win32(errp, (DWORD)status, + "PdhAddEnglishCounterW failed with 0x%lx. Performance counters may be disabled.", + status); + PdhCloseQuery(hQuery); + return FALSE; + } + + event = CreateEventW(NULL, FALSE, FALSE, L"LoadUpdateEvent"); + if (event == NULL) { + error_setg_win32(errp, GetLastError(), "Create LoadUpdateEvent failed"); + PdhCloseQuery(hQuery); + return FALSE; + } + + status = PdhCollectQueryDataEx(hQuery, LOADAVG_SAMPLING_INTERVAL, event); + if (status != ERROR_SUCCESS) { + error_setg_win32(errp, (DWORD)status, + "PdhCollectQueryDataEx failed with 0x%lx", status); + CloseHandle(event); + PdhCloseQuery(hQuery); + return FALSE; + } + + ret = RegisterWaitForSingleObject( + &waitHandle, + event, + (WAITORTIMERCALLBACK)load_avg_callback, + (PVOID)hCounter, + INFINITE, + WT_EXECUTEDEFAULT); + + if (ret == 0) { + error_setg_win32(errp, GetLastError(), + "RegisterWaitForSingleObject failed"); + CloseHandle(event); + PdhCloseQuery(hQuery); + return FALSE; + } + + ga_set_load_avg_wait_handle(ga_state, waitHandle); + ga_set_load_avg_event(ga_state, event); + ga_set_load_avg_pdh_query(ga_state, hQuery); + + return TRUE; +} + +GuestLoadAverage *qmp_guest_get_load(Error **errp) +{ + /* + * The load average logic calls PerformaceCounterAPI, which can result + * in a performance penalty. This avoids running the load average logic + * until a management application actually requests it. The load average + * will not initially be very accurate, but assuming that any interested + * management application will request it repeatedly throughout the lifetime + * of the VM, this seems like a good mitigation. + */ + if (ga_get_load_avg_pdh_query(ga_state) == NULL) { + /* set initial values */ + load_avg_1m = 0; + load_avg_5m = 0; + load_avg_15m = 0; + + if (init_load_avg_counter(errp) == false) { + return NULL; + } + } + + GuestLoadAverage *ret = NULL; + + ret = g_new0(GuestLoadAverage, 1); + ret->load1m = load_avg_1m; + ret->load5m = load_avg_5m; + ret->load15m = load_avg_15m; + return ret; } diff --git a/qga/commands-windows-ssh.c b/qga/commands-windows-ssh.c new file mode 100644 index 00000000000..df45c17b757 --- /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 00000000000..40ac67c4d9e --- /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 09c683e2639..5a5fad31f89 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; } @@ -206,15 +205,15 @@ GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **errp) #endif if (gei->out.length > 0) { 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->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); @@ -475,7 +474,7 @@ GuestExec *qmp_guest_exec(const char *path, 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; } @@ -586,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; } diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h index b4e7c52c611..d9f3922adfe 100644 --- a/qga/guest-agent-core.h +++ b/qga/guest-agent-core.h @@ -13,7 +13,11 @@ #ifndef GUEST_AGENT_CORE_H #define GUEST_AGENT_CORE_H -#include "qapi/qmp/dispatch.h" +#ifdef _WIN32 +#include +#endif + +#include "qapi/qmp-registry.h" #include "qga-qapi-types.h" #define QGA_READ_COUNT_DEFAULT 4096 @@ -41,6 +45,12 @@ void ga_set_response_delimited(GAState *s); bool ga_is_frozen(GAState *s); void ga_set_frozen(GAState *s); void ga_unset_frozen(GAState *s); +#ifdef _WIN32 +void ga_set_load_avg_event(GAState *s, HANDLE event); +void ga_set_load_avg_wait_handle(GAState *s, HANDLE wait_handle); +void ga_set_load_avg_pdh_query(GAState *s, HQUERY query); +HQUERY ga_get_load_avg_pdh_query(GAState *s); +#endif const char *ga_fsfreeze_hook(GAState *s); int64_t ga_get_fd_handle(GAState *s, Error **errp); int ga_parse_whence(GuestFileWhence *whence, Error **errp); diff --git a/qga/main.c b/qga/main.c index 8668b9f3d39..6c02f3ec386 100644 --- a/qga/main.c +++ b/qga/main.c @@ -19,9 +19,9 @@ #include #endif #include "qemu/help-texts.h" -#include "qapi/qmp/json-parser.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" +#include "qobject/json-parser.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" #include "guest-agent-core.h" #include "qga-qapi-init-commands.h" #include "qapi/error.h" @@ -33,6 +33,7 @@ #include "qemu-version.h" #ifdef _WIN32 #include +#include #include "qga/service-win32.h" #include "qga/vss-win32.h" #endif @@ -70,6 +71,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,6 +106,9 @@ struct GAState { GAService service; HANDLE wakeup_event; HANDLE event_log; + HANDLE load_avg_wait_handle; + HANDLE load_avg_event; + HQUERY load_avg_pdh_query; #endif bool delimit_response; bool frozen; @@ -226,12 +252,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" @@ -261,9 +291,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 \"help\" to list available RPCs)\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" @@ -272,8 +302,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, @@ -397,60 +427,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_freeze(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"); } -} - -/* [re-]enable all commands, except those explicitly blocked by user */ -static void ga_enable_non_blocked(const QmpCommand *cmd, void *opaque) -{ - GAState *s = opaque; - GList *blockedrpcs = s->blockedrpcs; - GList *allowedrpcs = s->allowedrpcs; - const char *name = qmp_command_name(cmd); - if (g_list_find_custom(blockedrpcs, name, ga_strcmp) == NULL) { - if (qmp_command_is_enabled(cmd)) { - return; + /* + * 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 (allowedrpcs && - g_list_find_custom(allowedrpcs, name, ga_strcmp) == NULL) { - return; - } + /* + * If frozen, this filtering must take priority over + * absolutely everything + */ + if (state->frozen) { + allowed = false; - g_debug("enabling command: %s", name); - qmp_enable_command(&ga_commands, name); + while (ga_freeze_allowlist[i] != NULL) { + if (strcmp(name, ga_freeze_allowlist[i]) == 0) { + allowed = true; + } + i++; + } } + + return allowed; } -/* disable commands that aren't allowed */ -static void ga_disable_not_allowed(const QmpCommand *cmd, void *opaque) +static void ga_apply_command_filters_iter(const QmpCommand *cmd, void *opaque) { - GList *allowedrpcs = 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(allowedrpcs, name, ga_strcmp) == NULL) { + 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); @@ -483,15 +532,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_freeze, 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) @@ -523,12 +571,12 @@ void ga_unset_frozen(GAState *s) } /* enable all disabled, non-blocked and allowed commands */ - qmp_for_each_command(&ga_commands, ga_enable_non_blocked, s); s->frozen = false; if (!ga_delete_file(s->state_filepath_isfrozen)) { g_warning("unable to delete %s, fsfreeze may not function properly", s->state_filepath_isfrozen); } + ga_apply_command_filters(s); } #ifdef CONFIG_FSFREEZE @@ -538,6 +586,25 @@ const char *ga_fsfreeze_hook(GAState *s) } #endif +#ifdef _WIN32 +void ga_set_load_avg_wait_handle(GAState *s, HANDLE wait_handle) +{ + s->load_avg_wait_handle = wait_handle; +} +void ga_set_load_avg_event(GAState *s, HANDLE event) +{ + s->load_avg_event = event; +} +void ga_set_load_avg_pdh_query(GAState *s, HQUERY query) +{ + s->load_avg_pdh_query = query; +} +HQUERY ga_get_load_avg_pdh_query(GAState *s) +{ + return s->load_avg_pdh_query; +} +#endif + static void become_daemon(const char *pidfile) { #ifndef _WIN32 @@ -996,38 +1063,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 */ - gchar *aliststr; /* allowedrpcs may point to this string */ - GList *blockedrpcs; - GList *allowedrpcs; - 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)) { @@ -1071,14 +1114,9 @@ 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, ",")); } @@ -1089,19 +1127,12 @@ static void config_load(GAConfig *config) split_list(config->aliststr, ",")); } - if (g_key_file_has_key(keyfile, "general", blockrpcs_key, NULL) && - g_key_file_has_key(keyfile, "general", "allow-rpcs", NULL)) { - g_critical("wrong config, using 'block-rpcs' and 'allow-rpcs' keys at" - " the same time is not allowed"); - exit(EXIT_FAILURE); - } - end: g_key_file_free(keyfile); - 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); @@ -1173,12 +1204,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:a:s:t:Dr"; + const char *sopt = "hVvdc:m:p:l:f:F::b:a:s:t:Dr"; int opt_ind = 0, ch; - bool block_rpcs = false, allow_rpcs = false; const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, + { "config", 1, NULL, 'c' }, { "dump-conf", 0, NULL, 'D' }, { "logfile", 1, NULL, 'l' }, { "pidfile", 1, NULL, 'f' }, @@ -1190,7 +1221,6 @@ 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' }, @@ -1199,6 +1229,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) { @@ -1251,7 +1301,6 @@ static void config_parse(GAConfig *config, int argc, char **argv) } config->blockedrpcs = g_list_concat(config->blockedrpcs, split_list(optarg, ",")); - block_rpcs = true; break; } case 'a': { @@ -1261,7 +1310,6 @@ static void config_parse(GAConfig *config, int argc, char **argv) } config->allowedrpcs = g_list_concat(config->allowedrpcs, split_list(optarg, ",")); - allow_rpcs = true; break; } #ifdef _WIN32 @@ -1302,12 +1350,6 @@ static void config_parse(GAConfig *config, int argc, char **argv) exit(EXIT_FAILURE); } } - - if (block_rpcs && allow_rpcs) { - g_critical("wrong commandline, using --block-rpcs and --allow-rpcs at the" - " same time is not allowed"); - exit(EXIT_FAILURE); - } } static void config_free(GAConfig *config) @@ -1383,6 +1425,10 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) g_debug("Guest agent version %s started", QEMU_FULL_VERSION); #ifdef _WIN32 + s->load_avg_wait_handle = INVALID_HANDLE_VALUE; + s->load_avg_event = INVALID_HANDLE_VALUE; + s->load_avg_pdh_query = NULL; + s->event_log = RegisterEventSource(NULL, "qemu-ga"); if (!s->event_log) { g_autofree gchar *errmsg = g_win32_error_message(GetLastError()); @@ -1401,24 +1447,23 @@ 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)) { if (config->daemonize) { /* delay opening/locking of pidfile till filesystems are unfrozen */ s->deferred_options.pid_filepath = config->pid_filepath; - become_daemon(NULL); } if (config->log_filepath) { /* delay opening the log file till filesystems are unfrozen */ s->deferred_options.log_filepath = config->log_filepath; } ga_disable_logging(s); - qmp_for_each_command(&ga_commands, ga_disable_not_allowed_freeze, NULL); } else { - if (config->daemonize) { - become_daemon(config->pid_filepath); - } if (config->log_filepath) { FILE *log_file = ga_open_logfile(config->log_filepath); if (!log_file) { @@ -1438,25 +1483,6 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) return NULL; } - if (config->allowedrpcs) { - qmp_for_each_command(&ga_commands, ga_disable_not_allowed, config->allowedrpcs); - s->allowedrpcs = config->allowedrpcs; - } - - /* - * Some commands can be blocked due to system limitation. - * Initialize blockedrpcs list even if allowedrpcs specified. - */ - config->blockedrpcs = ga_command_init_blockedrpcs(config->blockedrpcs); - if (config->blockedrpcs) { - GList *l = config->blockedrpcs; - s->blockedrpcs = config->blockedrpcs; - do { - g_debug("disabling command: %s", (char *)l->data); - qmp_disable_command(&ga_commands, l->data, NULL); - 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); @@ -1482,6 +1508,22 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) } #endif + ga_apply_command_filters(s); + + if (!channel_init(s, s->config->method, s->config->channel_path, + s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { + g_critical("failed to initialize guest agent channel"); + return NULL; + } + + if (config->daemonize) { + if (ga_is_frozen(s)) { + become_daemon(NULL); + } else { + become_daemon(config->pid_filepath); + } + } + ga_state = s; return s; } @@ -1491,6 +1533,18 @@ static void cleanup_agent(GAState *s) #ifdef _WIN32 CloseHandle(s->wakeup_event); CloseHandle(s->event_log); + + if (s->load_avg_wait_handle != INVALID_HANDLE_VALUE) { + UnregisterWait(s->load_avg_wait_handle); + } + + if (s->load_avg_event != INVALID_HANDLE_VALUE) { + CloseHandle(s->load_avg_event); + } + + if (s->load_avg_pdh_query) { + PdhCloseQuery(s->load_avg_pdh_query); + } #endif if (s->command_state) { ga_command_state_cleanup_all(s->command_state); @@ -1508,16 +1562,18 @@ static void cleanup_agent(GAState *s) static int run_agent_once(GAState *s) { - if (!channel_init(s, s->config->method, s->config->channel_path, - s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { + if (!s->channel && + channel_init(s, s->config->method, s->config->channel_path, + s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { g_critical("failed to initialize guest agent channel"); return EXIT_FAILURE; } - g_main_loop_run(ga_state->main_loop); + g_main_loop_run(s->main_loop); if (s->channel) { ga_channel_free(s->channel); + s->channel = NULL; } return EXIT_SUCCESS; @@ -1574,7 +1630,7 @@ static void stop_agent(GAState *s, bool requested) int main(int argc, char **argv) { - int ret = EXIT_SUCCESS; + int ret = EXIT_FAILURE; GAState *s; GAConfig *config = g_new0(GAConfig, 1); int socket_activation; @@ -1585,7 +1641,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) { @@ -1603,7 +1658,6 @@ int main(int argc, char **argv) socket_activation = check_socket_activation(); if (socket_activation > 1) { g_critical("qemu-ga only supports listening on one socket"); - ret = EXIT_FAILURE; goto end; } if (socket_activation) { @@ -1627,7 +1681,6 @@ int main(int argc, char **argv) if (!config->method) { g_critical("unsupported listen fd type"); - ret = EXIT_FAILURE; goto end; } } else if (config->channel_path == NULL) { @@ -1639,13 +1692,13 @@ int main(int argc, char **argv) config->channel_path = g_strdup(QGA_SERIAL_PATH_DEFAULT); } else { g_critical("must specify a path for this channel"); - ret = EXIT_FAILURE; goto end; } } if (config->dumpconf) { config_dump(config); + ret = EXIT_SUCCESS; goto end; } @@ -1660,6 +1713,7 @@ int main(int argc, char **argv) SERVICE_TABLE_ENTRY service_table[] = { { (char *)QGA_SERVICE_NAME, service_main }, { NULL, NULL } }; StartServiceCtrlDispatcher(service_table); + ret = EXIT_SUCCESS; } else { ret = run_agent(s); } diff --git a/qga/meson.build b/qga/meson.build index 59cae0cc6ee..89a4a8f713d 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -7,7 +7,7 @@ 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('cpp' in all_languages, error_message: 'VSS support requires a C++ compiler') \ @@ -66,32 +66,36 @@ 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_targetos, 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', '-lpdh' ] if have_qga_vss qga_libs += ['-lole32', '-loleaut32', '-lshlwapi', '-lstdc++', '-Wl,--enable-stdcall-fixup'] subdir('vss-win32') @@ -99,7 +103,7 @@ if targetos == 'windows' endif qga_objs = [] -if targetos == 'windows' +if host_os == 'windows' windmc = find_program('windmc', required: true) windres = find_program('windres', required: true) @@ -121,7 +125,7 @@ qga = executable('qemu-ga', qga_ss.sources() + qga_objs, 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'] @@ -140,11 +144,14 @@ if targetos == 'windows' qemu_ga_msi_vss = ['-D', 'InstallVss'] deps += qga_vss endif - if glib.version() < '2.73.2' + 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), @@ -155,9 +162,9 @@ if targetos == 'windows' qemu_ga_msi_vss, '-D', 'BUILD_DIR=' + meson.project_build_root(), '-D', 'BIN_DIR=' + glib_pc.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', '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] @@ -176,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 targetos != 'windows' -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/qapi-schema.json b/qga/qapi-schema.json index b720dd43796..6d770f7b8e0 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -2,16 +2,24 @@ # vim: filetype=python ## -# = General note concerning the use of guest agent interfaces +# This manual describes the commands supported by the QEMU Guest +# Agent Protocol. # -# "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 +# For locating a particular item, please see the `qapi-qga-index`. +# +# The following notation is used in examples: +# +# .. qmp-example:: +# +# -> ... text sent by client (commands) ... +# <- ... text sent by server (command responses and events) ... +# +# Example text is formatted for readability. However, in real +# protocol usage, its commonly emitted as a single line. +# +# Please refer to the +# :doc:`QEMU Machine Protocol Specification ` +# for the general format of commands, responses, and events. ## { 'pragma': { 'doc-required': true } } @@ -33,7 +41,10 @@ '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: @@ -150,8 +161,6 @@ # @time: time of nanoseconds, relative to the Epoch of 1970-01-01 in # UTC. # -# Returns: Nothing on success. -# # Since: 1.5 ## { 'command': 'guest-set-time', @@ -227,7 +236,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 ## @@ -242,8 +251,6 @@ # # @handle: filehandle returned by guest-file-open # -# Returns: Nothing on success. -# # Since: 0.15.0 ## { 'command': 'guest-file-close', @@ -278,7 +285,7 @@ # @count: maximum number of bytes to read (default is 4KB, maximum is # 48MB) # -# Returns: @GuestFileRead on success. +# Returns: @GuestFileRead # # Since: 0.15.0 ## @@ -313,7 +320,7 @@ # @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 ## @@ -380,7 +387,7 @@ # # @whence: Symbolic or numeric code for interpreting offset # -# Returns: @GuestFileSeek on success. +# Returns: @GuestFileSeek # # Since: 0.15.0 ## @@ -396,8 +403,6 @@ # # @handle: filehandle returned by guest-file-open # -# Returns: Nothing on success. -# # Since: 0.15.0 ## { 'command': 'guest-file-flush', @@ -415,7 +420,8 @@ # Since: 0.15.0 ## { 'enum': 'GuestFsfreezeStatus', - 'data': [ 'thawed', 'frozen' ] } + 'data': [ 'thawed', 'frozen' ], + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } } ## # @guest-fsfreeze-status: @@ -425,13 +431,15 @@ # 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: @@ -440,19 +448,21 @@ # 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: @@ -460,18 +470,20 @@ # 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. # -# 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: @@ -480,15 +492,16 @@ # # 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: @@ -505,7 +518,8 @@ ## { 'struct': 'GuestFilesystemTrimResult', 'data': {'path': 'str', - '*trimmed': 'int', '*minimum': 'int', '*error': 'str'} } + '*trimmed': 'int', '*minimum': 'int', '*error': 'str'}, + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSTRIM'] } } ## # @GuestFilesystemTrimResponse: @@ -515,7 +529,8 @@ # Since: 2.4 ## { 'struct': 'GuestFilesystemTrimResponse', - 'data': {'paths': ['GuestFilesystemTrimResult']} } + 'data': {'paths': ['GuestFilesystemTrimResult']}, + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSTRIM'] } } ## # @guest-fstrim: @@ -537,7 +552,8 @@ ## { 'command': 'guest-fstrim', 'data': { '*minimum': 'int' }, - 'returns': 'GuestFilesystemTrimResponse' } + 'returns': 'GuestFilesystemTrimResponse', + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSTRIM'] } } ## # @guest-suspend-disk: @@ -558,16 +574,16 @@ # 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: @@ -595,16 +611,16 @@ # 2. Issue the query-status QMP command to confirm the VM status is # "suspended" # -# The following errors may be returned: -# -# - If suspend to ram is not supported, Unsupported +# Errors: +# - 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: @@ -631,16 +647,16 @@ # 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: @@ -654,7 +670,8 @@ # Since: 1.1 ## { 'enum': 'GuestIpAddressType', - 'data': [ 'ipv4', 'ipv6' ] } + 'data': [ 'ipv4', 'ipv6' ], + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } } ## # @GuestIpAddress: @@ -670,7 +687,8 @@ { 'struct': 'GuestIpAddress', 'data': {'ip-address': 'str', 'ip-address-type': 'GuestIpAddressType', - 'prefix': 'int'} } + 'prefix': 'int'}, + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } } ## # @GuestNetworkInterfaceStat: @@ -702,7 +720,8 @@ 'tx-packets': 'uint64', 'tx-errs': 'uint64', 'tx-dropped': 'uint64' - } } + }, + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } } ## # @GuestNetworkInterface: @@ -722,19 +741,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. # -# Returns: List of GuestNetworkInterface 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: @@ -753,7 +774,8 @@ { 'struct': 'GuestLogicalProcessor', 'data': {'logical-id': 'int', 'online': 'bool', - '*can-offline': 'bool'} } + '*can-offline': 'bool'}, + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } } ## # @guest-get-vcpus: @@ -768,7 +790,8 @@ # Since: 1.5 ## { 'command': 'guest-get-vcpus', - 'returns': ['GuestLogicalProcessor'] } + 'returns': ['GuestLogicalProcessor'], + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } } ## # @guest-set-vcpus: @@ -776,14 +799,15 @@ # 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. @@ -792,9 +816,6 @@ # - 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 @@ -804,11 +825,16 @@ # - 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: @@ -860,7 +886,8 @@ { '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' ] } } ## @@ -878,7 +905,8 @@ ## { 'struct': 'GuestPCIAddress', 'data': {'domain': 'int', 'bus': 'int', - 'slot': 'int', 'function': 'int'} } + 'slot': 'int', 'function': 'int'}, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @GuestCCWAddress: @@ -897,7 +925,8 @@ 'data': {'cssid': 'int', 'ssid': 'int', 'subchno': 'int', - 'devno': 'int'} } + 'devno': 'int'}, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @GuestDiskAddress: @@ -926,7 +955,8 @@ '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: @@ -934,6 +964,8 @@ # NVMe smart information, based on NVMe specification, section # # +# TODO: document members briefly +# # Since: 7.1 ## { 'struct': 'GuestNVMeSmart', @@ -961,21 +993,23 @@ '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: @@ -1000,7 +1034,8 @@ { '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: @@ -1013,7 +1048,8 @@ # Since: 5.2 ## { 'command': 'guest-get-disks', - 'returns': ['GuestDiskInfo'] } + 'returns': ['GuestDiskInfo'], + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } } ## # @GuestFilesystemInfo: @@ -1026,7 +1062,10 @@ # # @used-bytes: file system used bytes (since 3.0) # -# @total-bytes: non-root file system total bytes (since 3.0) +# @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 @@ -1036,7 +1075,8 @@ { '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: @@ -1049,7 +1089,8 @@ # Since: 2.2 ## { 'command': 'guest-get-fsinfo', - 'returns': ['GuestFilesystemInfo'] } + 'returns': ['GuestFilesystemInfo'], + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @guest-set-user-password: @@ -1073,12 +1114,11 @@ # transmission, even if already crypt()d, to ensure it is 8-bit safe # when passed as JSON. # -# Returns: Nothing on success. -# # 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: @@ -1098,7 +1138,8 @@ { 'struct': 'GuestMemoryBlock', 'data': {'phys-index': 'uint64', 'online': 'bool', - '*can-offline': 'bool'} } + '*can-offline': 'bool'}, + 'if': 'CONFIG_LINUX' } ## # @guest-get-memory-blocks: @@ -1114,7 +1155,8 @@ # Since: 2.3 ## { 'command': 'guest-get-memory-blocks', - 'returns': ['GuestMemoryBlock'] } + 'returns': ['GuestMemoryBlock'], + 'if': 'CONFIG_LINUX' } ## # @GuestMemoryBlockResponseType: @@ -1137,7 +1179,8 @@ ## { 'enum': 'GuestMemoryBlockResponseType', 'data': ['success', 'not-found', 'operation-not-supported', - 'operation-failed'] } + 'operation-failed'], + 'if': 'CONFIG_LINUX' } ## # @GuestMemoryBlockResponse: @@ -1155,7 +1198,8 @@ { 'struct': 'GuestMemoryBlockResponse', 'data': { 'phys-index': 'uint64', 'response': 'GuestMemoryBlockResponseType', - '*error-code': 'int' }} + '*error-code': 'int' }, + 'if': 'CONFIG_LINUX'} ## # @guest-set-memory-blocks: @@ -1163,28 +1207,31 @@ # 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. # -# 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: @@ -1196,7 +1243,8 @@ # Since: 2.3 ## { 'struct': 'GuestMemoryBlockInfo', - 'data': {'size': 'uint64'} } + 'data': {'size': 'uint64'}, + 'if': 'CONFIG_LINUX' } ## # @guest-get-memory-block-info: @@ -1208,7 +1256,8 @@ # Since: 2.3 ## { 'command': 'guest-get-memory-block-info', - 'returns': 'GuestMemoryBlockInfo' } + 'returns': 'GuestMemoryBlockInfo', + 'if': 'CONFIG_LINUX' } ## # @GuestExecStatus: @@ -1220,11 +1269,13 @@ # @signal: signal number (linux) or unhandled exception code (windows) # if the process was abnormally terminated. # -# @out-data: base64-encoded stdout of the process +# @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' +# @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. @@ -1247,7 +1298,7 @@ # # @pid: pid returned from guest-exec # -# Returns: GuestExecStatus on success. +# Returns: GuestExecStatus # # Since: 2.5 ## @@ -1271,12 +1322,16 @@ # 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. +# 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 ## @@ -1289,8 +1344,9 @@ # # Controls what guest-exec output gets captures. # -# @flag: captures both stdout and stderr if true. Equivalent -# to GuestExecCaptureOutputMode::all. (since 2.5) +# @flag: captures both stdout and stderr if true. Equivalent to +# GuestExecCaptureOutputMode::all. (since 2.5) +# # @mode: capture mode; preferred interface # # Since: 8.0 @@ -1313,9 +1369,9 @@ # @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. +# running process. Defaults to false. # -# Returns: PID on success. +# Returns: PID # # Since: 2.5 ## @@ -1344,7 +1400,7 @@ # 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 ## @@ -1367,7 +1423,8 @@ # 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: @@ -1379,7 +1436,8 @@ # Since: 2.10 ## { 'command': 'guest-get-users', - 'returns': ['GuestUser'] } + 'returns': ['GuestUser'], + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_UTMPX' ] } } ## # @GuestTimezone: @@ -1451,16 +1509,15 @@ # * POSIX: as defined by os-release(5) # * Windows: contains string "server" or "client" # -# Notes: On POSIX systems the fields @id, @name, @pretty-name, -# @version, @version-id, @variant and @variant-id follow the -# definition specified in os-release(5). Refer to the manual page -# for exact description of the fields. Their values are taken -# from the os-release file. If the file is not present in the -# system, or the values are not present in the file, the fields -# are not included. +# .. 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 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 ## @@ -1485,9 +1542,12 @@ ## # @GuestDeviceType: +# +# @pci: PCI device ## { 'enum': 'GuestDeviceType', - 'data': [ 'pci' ] } + 'data': [ 'pci' ], + 'if': 'CONFIG_WIN32' } ## # @GuestDeviceIdPCI: @@ -1499,19 +1559,23 @@ # 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 +# Id of the device +# +# @type: device type # # Since: 5.2 ## { 'union': 'GuestDeviceId', 'base': { 'type': 'GuestDeviceType' }, 'discriminator': 'type', - 'data': { 'pci': 'GuestDeviceIdPCI' } } + 'data': { 'pci': 'GuestDeviceIdPCI' }, + 'if': 'CONFIG_WIN32' } ## # @GuestDeviceInfo: @@ -1532,7 +1596,8 @@ '*driver-date': 'int', '*driver-version': 'str', '*id': 'GuestDeviceId' - } } + }, + 'if': 'CONFIG_WIN32' } ## # @guest-get-devices: @@ -1544,7 +1609,8 @@ # Since: 5.2 ## { 'command': 'guest-get-devices', - 'returns': ['GuestDeviceInfo'] } + 'returns': ['GuestDeviceInfo'], + 'if': 'CONFIG_WIN32' } ## # @GuestAuthorizedKeys: @@ -1556,30 +1622,32 @@ { '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). # +# @username: the user account to add the authorized keys +# # Returns: @GuestAuthorizedKeys # # Since: 5.2 ## { 'command': 'guest-ssh-get-authorized-keys', 'data': { 'username': 'str' }, - 'returns': 'GuestAuthorizedKeys', - 'if': 'CONFIG_POSIX' } + 'returns': 'GuestAuthorizedKeys' +} ## # @guest-ssh-add-authorized-keys: # +# Append public keys to user .ssh/authorized_keys on Unix systems (not +# implemented for other systems). +# # @username: the user account to add the authorized keys # # @keys: the public keys to add (in OpenSSH/sshd(8) authorized_keys @@ -1587,36 +1655,29 @@ # # @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. -# # 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: # -# @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. +# @username: the user account to remove the authorized keys +# +# @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: @@ -1676,7 +1737,8 @@ '*ios-pgr': 'uint64', '*total-ticks': 'uint64', '*weight-ticks': 'uint64' - } } + }, + 'if': 'CONFIG_LINUX' } ## # @GuestDiskStatsInfo: @@ -1686,12 +1748,15 @@ # @major: major 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: @@ -1703,18 +1768,22 @@ # 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' } ## @@ -1759,21 +1828,23 @@ '*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: @@ -1785,5 +1856,121 @@ # Since: 7.1 ## { 'command': 'guest-get-cpustats', - 'returns': ['GuestCpuStats'] + 'returns': ['GuestCpuStats'], + 'if': 'CONFIG_LINUX' +} + + +## +# @GuestLoadAverage: +# +# Statistics about process load information +# +# @load1m: 1-minute load avage +# +# @load5m: 5-minute load avage +# +# @load15m: 15-minute load avage +# +# Since: 10.0 +## +{ 'struct': 'GuestLoadAverage', + 'data': { + 'load1m': 'number', + 'load5m': 'number', + 'load15m': 'number' + }, + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_GETLOADAVG'] } +} + +## +# @guest-get-load: +# +# Retrieve CPU process load information +# +# .. note:: Windows does not have load average API, so QGA emulates it by +# calculating the average CPU usage in the last 1, 5, 15 minutes +# similar as Linux does this. +# Calculation starts from the first time this command is called. +# +# Returns: load information +# +# Since: 10.0 +## +{ 'command': 'guest-get-load', + 'returns': 'GuestLoadAverage', + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_GETLOADAVG'] } +} + +## +# @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 84944133f79..5cea5bcf747 100644 --- a/qga/vss-win32/install.cpp +++ b/qga/vss-win32/install.cpp @@ -39,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(). @@ -242,6 +242,7 @@ static HRESULT QGAProviderRemove(ICatalogCollection *coll, int i, void *arg) } /* Unregister this module from COM+ Applications Catalog */ +STDAPI COMUnregister(void); STDAPI COMUnregister(void) { qga_debug_begin; @@ -256,6 +257,7 @@ STDAPI COMUnregister(void) } /* Register this module to COM+ Applications Catalog */ +STDAPI COMRegister(void); STDAPI COMRegister(void) { qga_debug_begin; @@ -380,11 +382,13 @@ STDAPI COMRegister(void) 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(); diff --git a/qga/vss-win32/provider.cpp b/qga/vss-win32/provider.cpp index cc72e5ef1b9..a102a23fbf1 100644 --- a/qga/vss-win32/provider.cpp +++ b/qga/vss-win32/provider.cpp @@ -45,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); @@ -527,6 +527,9 @@ 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) { diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp index 9884c65e707..4401d55e3a4 100644 --- a/qga/vss-win32/requester.cpp +++ b/qga/vss-win32/requester.cpp @@ -254,8 +254,8 @@ static void AddComponents(ErrorSet *errset) qga_debug_end; } -DWORD get_reg_dword_value(HKEY baseKey, LPCSTR subKey, LPCSTR valueName, - DWORD defaultData) +static DWORD get_reg_dword_value(HKEY baseKey, LPCSTR subKey, LPCSTR valueName, + DWORD defaultData) { qga_debug_begin; @@ -272,12 +272,12 @@ DWORD get_reg_dword_value(HKEY baseKey, LPCSTR subKey, LPCSTR valueName, return dwordData; } -bool is_valid_vss_backup_type(VSS_BACKUP_TYPE vssBT) +static bool is_valid_vss_backup_type(VSS_BACKUP_TYPE vssBT) { return (vssBT > VSS_BT_UNDEFINED && vssBT < VSS_BT_OTHER); } -VSS_BACKUP_TYPE get_vss_backup_type( +static VSS_BACKUP_TYPE get_vss_backup_type( VSS_BACKUP_TYPE defaultVssBT = DEFAULT_VSS_BACKUP_TYPE) { qga_debug_begin; diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c index 4a83bda2c3b..d0e1c63cf6c 100644 --- a/qobject/block-qdict.c +++ b/qobject/block-qdict.c @@ -9,10 +9,10 @@ #include "qemu/osdep.h" #include "block/qdict.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qemu/cutils.h" #include "qapi/error.h" diff --git a/qobject/json-parser-int.h b/qobject/json-parser-int.h index 16a25d00bb6..8c01f236276 100644 --- a/qobject/json-parser-int.h +++ b/qobject/json-parser-int.h @@ -14,7 +14,7 @@ #ifndef JSON_PARSER_INT_H #define JSON_PARSER_INT_H -#include "qapi/qmp/json-parser.h" +#include "qobject/json-parser.h" typedef enum json_token_type { JSON_ERROR = 0, /* must be zero, see json_lexer[] */ diff --git a/qobject/json-parser.c b/qobject/json-parser.c index d498db6e702..7483e582fea 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -16,12 +16,12 @@ #include "qemu/cutils.h" #include "qemu/unicode.h" #include "qapi/error.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "json-parser-int.h" struct JSONToken { diff --git a/qobject/json-writer.c b/qobject/json-writer.c index 309a31d57a9..aac2c6ab71e 100644 --- a/qobject/json-writer.c +++ b/qobject/json-writer.c @@ -14,7 +14,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/json-writer.h" +#include "qobject/json-writer.h" #include "qemu/unicode.h" struct JSONWriter { diff --git a/qobject/qbool.c b/qobject/qbool.c index c7049c0c501..00d7066aae3 100644 --- a/qobject/qbool.c +++ b/qobject/qbool.c @@ -12,7 +12,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qbool.h" +#include "qobject/qbool.h" #include "qobject-internal.h" /** diff --git a/qobject/qdict.c b/qobject/qdict.c index 8faff230d39..a90ac9ae2f8 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -11,11 +11,11 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qbool.h" +#include "qobject/qnull.h" +#include "qobject/qstring.h" #include "qobject-internal.h" /** diff --git a/qobject/qjson.c b/qobject/qjson.c index 167fcb429c9..c858dafb5e8 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -13,14 +13,14 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/json-parser.h" -#include "qapi/qmp/json-writer.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/json-parser.h" +#include "qobject/json-writer.h" +#include "qobject/qjson.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" typedef struct JSONParsingState { JSONMessageParser parser; diff --git a/qobject/qlist.c b/qobject/qlist.c index 356ad946b00..41e6876d5b6 100644 --- a/qobject/qlist.c +++ b/qobject/qlist.c @@ -11,11 +11,11 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qemu/queue.h" #include "qobject-internal.h" diff --git a/qobject/qlit.c b/qobject/qlit.c index be8332136c2..a44f47eaa57 100644 --- a/qobject/qlit.c +++ b/qobject/qlit.c @@ -15,13 +15,13 @@ #include "qemu/osdep.h" -#include "qapi/qmp/qlit.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qlit.h" +#include "qobject/qbool.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" +#include "qobject/qnull.h" static bool qlit_equal_qdict(const QLitObject *lhs, const QDict *qdict) { @@ -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/qnull.c b/qobject/qnull.c index 445a5db7f36..0fb78cbd0a2 100644 --- a/qobject/qnull.c +++ b/qobject/qnull.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "qobject-internal.h" QNull qnull_ = { diff --git a/qobject/qnum.c b/qobject/qnum.c index 2bbeaedc7b4..a938b64537d 100644 --- a/qobject/qnum.c +++ b/qobject/qnum.c @@ -13,7 +13,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qnum.h" #include "qobject-internal.h" /** @@ -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/qobject/qobject-internal.h b/qobject/qobject-internal.h index b310c8e1b58..0c7679fe989 100644 --- a/qobject/qobject-internal.h +++ b/qobject/qobject-internal.h @@ -10,7 +10,7 @@ #ifndef QOBJECT_INTERNAL_H #define QOBJECT_INTERNAL_H -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" static inline void qobject_init(QObject *obj, QType type) { diff --git a/qobject/qobject.c b/qobject/qobject.c index d7077b8f2a9..78d1e057c12 100644 --- a/qobject/qobject.c +++ b/qobject/qobject.c @@ -8,12 +8,12 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" #include "qobject-internal.h" QEMU_BUILD_BUG_MSG( diff --git a/qobject/qstring.c b/qobject/qstring.c index 794f8c93578..d3166049149 100644 --- a/qobject/qstring.c +++ b/qobject/qstring.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "qobject-internal.h" /** diff --git a/qom/container.c b/qom/container.c index 455e8410c66..38a27ec1edd 100644 --- a/qom/container.c +++ b/qom/container.c @@ -15,7 +15,7 @@ #include "qemu/module.h" static const TypeInfo container_info = { - .name = "container", + .name = TYPE_CONTAINER, .parent = TYPE_OBJECT, }; @@ -24,29 +24,14 @@ static void container_register_types(void) type_register_static(&container_info); } -Object *container_get(Object *root, const char *path) +Object *object_property_add_new_container(Object *obj, const char *name) { - Object *obj, *child; - char **parts; - int i; + Object *child = object_new(TYPE_CONTAINER); - parts = g_strsplit(path, "/", 0); - assert(parts != NULL && parts[0] != NULL && !parts[0][0]); - obj = root; + object_property_add_child(obj, name, child); + object_unref(child); - for (i = 1; parts[i] != NULL; i++, obj = child) { - child = object_resolve_path_component(obj, parts[i]); - if (!child) { - child = object_new("container"); - object_property_add_child(obj, parts[i], child); - object_unref(child); - } - } - - g_strfreev(parts); - - return obj; + return child; } - type_init(container_register_types) diff --git a/qom/object.c b/qom/object.c index e25f1e96db1..1856bb36c74 100644 --- a/qom/object.c +++ b/qom/object.c @@ -23,16 +23,16 @@ #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 "qobject/qjson.h" #include "trace.h" /* TODO: replace QObject with a simpler visitor to avoid a dependency * of the QOM core on QObject? */ #include "qom/qom-qobject.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qemu/error-report.h" #define MAX_INTERFACES 32 @@ -54,10 +54,10 @@ struct TypeImpl size_t instance_size; size_t instance_align; - void (*class_init)(ObjectClass *klass, void *data); - void (*class_base_init)(ObjectClass *klass, void *data); + void (*class_init)(ObjectClass *klass, const void *data); + void (*class_base_init)(ObjectClass *klass, const void *data); - void *class_data; + const void *class_data; void (*instance_init)(Object *obj); void (*instance_post_init)(Object *obj); @@ -137,26 +137,50 @@ 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); return ti; } -TypeImpl *type_register(const TypeInfo *info) +TypeImpl *type_register_static(const TypeInfo *info) { assert(info->parent); return type_register_internal(info); } -TypeImpl *type_register_static(const TypeInfo *info) -{ - return type_register(info); -} - void type_register_static_array(const TypeInfo *infos, int nr_infos) { int i; @@ -166,7 +190,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 +199,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 +266,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) @@ -263,7 +314,6 @@ static void type_initialize_interface(TypeImpl *ti, TypeImpl *interface_type, g_free((char *)info.name); new_iface = (InterfaceClass *)iface_impl->class; - new_iface->concrete_class = ti->class; new_iface->interface_type = interface_type; ti->class->interfaces = g_slist_append(ti->class->interfaces, new_iface); @@ -293,6 +343,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 +379,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); @@ -380,13 +431,13 @@ static void object_init_with_type(Object *obj, TypeImpl *ti) static void object_post_init_with_type(Object *obj, TypeImpl *ti) { - if (ti->instance_post_init) { - ti->instance_post_init(obj); - } - if (type_has_parent(ti)) { object_post_init_with_type(obj, type_get_parent(ti)); } + + if (ti->instance_post_init) { + ti->instance_post_init(obj); + } } bool object_apply_global_props(Object *obj, const GPtrArray *props, @@ -434,7 +485,7 @@ bool object_apply_global_props(Object *obj, const GPtrArray *props, * Slot 0: accelerator's global property defaults * Slot 1: machine's global property defaults * Slot 2: global properties from legacy command line option - * Each is a GPtrArray of of GlobalProperty. + * Each is a GPtrArray of GlobalProperty. * Applied in order, later entries override earlier ones. */ static GPtrArray *object_compat_props[3]; @@ -522,23 +573,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 +784,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 +957,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 +1055,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 +1068,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) @@ -1162,7 +1191,7 @@ GSList *object_class_get_list(const char *implements_type, return list; } -static gint object_class_cmp(gconstpointer a, gconstpointer b) +static gint object_class_cmp(gconstpointer a, gconstpointer b, gpointer d) { return strcasecmp(object_class_get_name((ObjectClass *)a), object_class_get_name((ObjectClass *)b)); @@ -1171,8 +1200,9 @@ static gint object_class_cmp(gconstpointer a, gconstpointer b) GSList *object_class_get_list_sorted(const char *implements_type, bool include_abstract) { - return g_slist_sort(object_class_get_list(implements_type, include_abstract), - object_class_cmp); + return g_slist_sort_with_data( + object_class_get_list(implements_type, include_abstract), + object_class_cmp, NULL); } Object *object_ref(void *objptr) @@ -1443,7 +1473,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 +1535,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 +1569,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 +1607,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 +1644,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; } @@ -1690,12 +1729,44 @@ const char *object_property_get_type(Object *obj, const char *name, Error **errp return prop->type; } +static const char *const root_containers[] = { + "chardevs", + "objects", + "backend" +}; + +static Object *object_root_initialize(void) +{ + Object *root = object_new(TYPE_CONTAINER); + int i; + + /* + * Create all QEMU system containers. "machine" and its sub-containers + * are only created when machine initializes (qemu_create_machine()). + */ + for (i = 0; i < ARRAY_SIZE(root_containers); i++) { + object_property_add_new_container(root, root_containers[i]); + } + + return root; +} + +Object *object_get_container(const char *name) +{ + Object *container; + + container = object_resolve_path_component(object_get_root(), name); + assert(object_dynamic_cast(container, TYPE_CONTAINER)); + + return container; +} + Object *object_get_root(void) { static Object *root; if (!root) { - root = object_new("container"); + root = object_root_initialize(); } return root; @@ -1703,7 +1774,7 @@ Object *object_get_root(void) Object *object_get_objects_root(void) { - return container_get(object_get_root(), "/objects"); + return object_get_container("objects"); } Object *object_get_internal_root(void) @@ -1711,7 +1782,7 @@ Object *object_get_internal_root(void) static Object *internal_root; if (!internal_root) { - internal_root = object_new("container"); + internal_root = object_new(TYPE_CONTAINER); } return internal_root; @@ -1851,7 +1922,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 +2098,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 +2203,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 +2212,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 +2246,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 **); @@ -2802,7 +2892,7 @@ void object_class_property_set_description(ObjectClass *klass, op->description = g_strdup(description); } -static void object_class_init(ObjectClass *klass, void *data) +static void object_class_init(ObjectClass *klass, const void *data) { object_class_property_add_str(klass, "type", object_get_type, NULL); diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index 7d31589b047..1ffea1a7288 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -3,10 +3,12 @@ #include "qemu/cutils.h" #include "qapi/error.h" #include "qapi/qapi-visit-qom.h" -#include "qapi/qmp/qobject.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qobject.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qjson.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "qom/object_interfaces.h" @@ -90,7 +92,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 +110,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; @@ -177,9 +179,25 @@ char *object_property_help(const char *name, const char *type, g_string_append(str, description); } if (defval) { - g_autofree char *def_json = g_string_free(qobject_to_json(defval), - false); - g_string_append_printf(str, " (default: %s)", def_json); + g_autofree char *def_json = NULL; + const char *def; + + switch (qobject_type(defval)) { + case QTYPE_QSTRING: + def = qstring_get_str(qobject_to(QString, defval)); + break; + + case QTYPE_QBOOL: + def = qbool_get_bool(qobject_to(QBool, defval)) ? "on" : "off"; + break; + + default: + def_json = g_string_free(qobject_to_json(defval), false); + def = def_json; + break; + } + + g_string_append_printf(str, " (default: %s)", def); } return g_string_free(str, false); @@ -259,7 +277,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 +285,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 +313,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 +328,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 6e3a2175a4e..a00a564b1e2 100644 --- a/qom/qom-hmp-cmds.c +++ b/qom/qom-hmp-cmds.c @@ -11,8 +11,8 @@ #include "monitor/monitor.h" #include "qapi/error.h" #include "qapi/qapi-commands-qom.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" #include "qemu/readline.h" #include "qom/object.h" #include "qom/object_interfaces.h" diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c index 7c087299de9..293755f409e 100644 --- a/qom/qom-qmp-cmds.c +++ b/qom/qom-qmp-cmds.c @@ -20,7 +20,7 @@ #include "qapi/qapi-commands-qdev.h" #include "qapi/qapi-commands-qom.h" #include "qapi/qapi-visit-qom.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" @@ -28,15 +28,11 @@ #include "qom/object_interfaces.h" #include "qom/qom-qobject.h" -ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp) +static Object *qom_resolve_path(const char *path, Error **errp) { - Object *obj; bool ambiguous = false; - ObjectPropertyInfoList *props = NULL; - ObjectProperty *prop; - ObjectPropertyIterator iter; + Object *obj = object_resolve_path(path, &ambiguous); - obj = object_resolve_path(path, &ambiguous); if (obj == NULL) { if (ambiguous) { error_setg(errp, "Path '%s' is ambiguous", path); @@ -44,6 +40,19 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp) error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found", path); } + } + return obj; +} + +ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp) +{ + Object *obj; + ObjectPropertyInfoList *props = NULL; + ObjectProperty *prop; + ObjectPropertyIterator iter; + + obj = qom_resolve_path(path, errp); + if (obj == NULL) { return NULL; } @@ -141,7 +150,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))) { @@ -186,7 +195,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); @@ -212,6 +221,7 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename, info->name = g_strdup(prop->name); info->type = g_strdup(prop->type); info->description = g_strdup(prop->description); + info->default_value = qobject_ref(prop->defval); QAPI_LIST_PREPEND(prop_list, info); } diff --git a/replay/replay-audio.c b/replay/replay-audio.c index 91854f02ea0..ed2ba2164ba 100644 --- a/replay/replay-audio.c +++ b/replay/replay-audio.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" #include "audio/audio.h" diff --git a/replay/replay-char.c b/replay/replay-char.c index a31aded032e..81dc416e988 100644 --- a/replay/replay-char.c +++ b/replay/replay-char.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" #include "chardev/char.h" @@ -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 3e60549a4ae..11053640021 100644 --- a/replay/replay-debugging.c +++ b/replay/replay-debugging.c @@ -11,13 +11,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "system/replay.h" +#include "system/runstate.h" #include "replay-internal.h" #include "monitor/hmp.h" #include "monitor/monitor.h" #include "qapi/qapi-commands-replay.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/timer.h" #include "block/snapshot.h" #include "migration/snapshot.h" @@ -144,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; @@ -152,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 af0721cc1a1..8959da9f1fa 100644 --- a/replay/replay-events.c +++ b/replay/replay-events.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" #include "block/aio.h" #include "ui/input.h" @@ -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 bee3dbe5283..562bbf37175 100644 --- a/replay/replay-input.c +++ b/replay/replay-input.c @@ -10,7 +10,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" #include "qemu/notify.h" #include "ui/input.h" diff --git a/replay/replay-internal.c b/replay/replay-internal.c index 77d0c82327e..c2a7200339f 100644 --- a/replay/replay-internal.c +++ b/replay/replay-internal.c @@ -10,8 +10,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "system/replay.h" +#include "system/runstate.h" #include "replay-internal.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" @@ -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 b6836354ac5..75249b76936 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-net.c b/replay/replay-net.c index 3b70f71cf12..d4b197e91e3 100644 --- a/replay/replay-net.c +++ b/replay/replay-net.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" #include "net/net.h" #include "net/filter.h" diff --git a/replay/replay-random.c b/replay/replay-random.c index afc7a0fccca..7f4c46f74d9 100644 --- a/replay/replay-random.c +++ b/replay/replay-random.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" void replay_save_random(int ret, void *buf, size_t len) diff --git a/replay/replay-snapshot.c b/replay/replay-snapshot.c index 10a7cf79927..3c0a89442a9 100644 --- a/replay/replay-snapshot.c +++ b/replay/replay-snapshot.c @@ -11,10 +11,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" #include "monitor/monitor.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "qemu/error-report.h" #include "migration/vmstate.h" #include "migration/snapshot.h" @@ -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 ee0ebfcf09f..f3d62e11393 100644 --- a/replay/replay-time.c +++ b/replay/replay-time.c @@ -10,7 +10,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "replay-internal.h" #include "qemu/error-report.h" diff --git a/replay/replay.c b/replay/replay.c index 0f7d766efe8..a3e24c967ae 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -11,13 +11,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "exec/icount.h" +#include "system/replay.h" +#include "system/runstate.h" #include "replay-internal.h" #include "qemu/main-loop.h" #include "qemu/option.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "qemu/error-report.h" /* Current version of the replay mechanism. @@ -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; @@ -226,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; @@ -258,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 */ @@ -274,6 +385,8 @@ static void replay_enable(const char *fname, int mode) replay_fetch_data_kind(); } + runstate_replay_enable(); + replay_init_events(); } @@ -379,7 +492,7 @@ void replay_add_blocker(const char *feature) { Error *reason = NULL; - error_setg(&reason, "Record/replay feature is not supported for '%s'", + 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 50cefdb2d69..8f2b2d326e7 100644 --- a/replay/stubs-system.c +++ b/replay/stubs-system.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "ui/input.h" void replay_input_event(QemuConsole *src, InputEvent *evt) diff --git a/roms/Makefile b/roms/Makefile index 6859685290b..beff58d9d50 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -34,6 +34,7 @@ find-cross-gcc = $(firstword $(wildcard $(patsubst %ld,%gcc,$(call find-cross-ld # finally strip off path + toolname so we get the prefix find-cross-prefix = $(subst gcc,,$(notdir $(call find-cross-gcc,$(1)))) +aarch64_cross_prefix := $(call find-cross-prefix,aarch64) arm_cross_prefix := $(call find-cross-prefix,arm) powerpc64_cross_prefix := $(call find-cross-prefix,powerpc64) powerpc_cross_prefix := $(call find-cross-prefix,powerpc) @@ -41,8 +42,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,6 +53,8 @@ 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:" @@ -64,10 +67,12 @@ default help: @echo " u-boot.e500 -- update u-boot.e500" @echo " u-boot.sam460 -- update u-boot.sam460" @echo " npcm7xx_bootrom -- update vbootrom for npcm7xx" + @echo " npcm8xx_bootrom -- update vbootrom for npcm8xx" @echo " efi -- update UEFI (edk2) platform firmware" @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" @@ -90,12 +95,12 @@ 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 @@ -146,10 +151,24 @@ skiboot: $(MAKE) -C skiboot CROSS=$(powerpc64_cross_prefix) cp skiboot/skiboot.lid ../pc-bios/skiboot.lid -efi: - python3 edk2-build.py --config edk2-build.config \ - --version-override "edk2-stable202302-for-qemu" \ - --release-date "03/01/2023" +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 + +edk2-basetools: edk2-version + $(PYTHON) edk2-build.py --config edk2-build.config \ + --silent --no-logs \ + --match none # build only basetools + +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 @@ -177,10 +196,20 @@ npcm7xx_bootrom: $(MAKE) -C vbootrom CROSS_COMPILE=$(arm_cross_prefix) cp vbootrom/npcm7xx_bootrom.bin ../pc-bios/npcm7xx_bootrom.bin +npcm8xx_bootrom: + $(MAKE) -C vbootrom CROSS_COMPILE=$(aarch64_cross_prefix) + cp vbootrom/npcm8xx_bootrom.bin ../pc-bios/npcm8xx_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 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 @@ -189,3 +218,4 @@ clean: $(MAKE) -C opensbi clean $(MAKE) -C qboot clean $(MAKE) -C vbootrom clean + $(MAKE) -C seabios-hppa clean diff --git a/roms/SLOF b/roms/SLOF index 6b6c16b4b40..3a259df2449 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 d18c802c46e..0b144bb1de3 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_VIRTIO_BLK=n +CONFIG_VIRTIO_SCSI=n +CONFIG_PVSCSI=n CONFIG_ESP_SCSI=n +CONFIG_LSI_SCSI=n CONFIG_MEGASAS=n -CONFIG_PVSCSI=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 f80f052277c..4dfdca63a93 160000 --- a/roms/edk2 +++ b/roms/edk2 @@ -1 +1 @@ -Subproject commit f80f052277c88a67c55e107b550f504eeea947d3 +Subproject commit 4dfdca63a93497203f197ec98ba20e2327e4afe4 diff --git a/roms/edk2-build.config b/roms/edk2-build.config index 66ef9ffcb91..9e45361fb44 100644 --- a/roms/edk2-build.config +++ b/roms/edk2-build.config @@ -18,13 +18,20 @@ 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.broken.grub] +[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 @@ -63,11 +70,11 @@ cpy1 = FV/OVMF_CODE.fd edk2-x86_64-code.fd [build.ovmf.x86_64.secure] desc = ovmf build (64-bit, secure boot) -conf = OvmfPkg/OvmfPkgIa32X64.dsc -arch = IA32 X64 +conf = OvmfPkg/OvmfPkgX64.dsc +arch = X64 opts = common ovmf.sb.smm -plat = Ovmf3264 +plat = OvmfX64 dest = ../pc-bios cpy1 = FV/OVMF_CODE.fd edk2-x86_64-secure-code.fd @@ -89,7 +96,7 @@ conf = ArmVirtPkg/ArmVirtQemu.dsc arch = ARM opts = common armvirt.silent -pcds = nx.broken.grub +pcds = nx.broken.shim.grub plat = ArmVirtQemu-ARM dest = ../pc-bios cpy1 = FV/QEMU_EFI.fd edk2-arm-code.fd @@ -106,7 +113,7 @@ conf = ArmVirtPkg/ArmVirtQemu.dsc arch = AARCH64 opts = common armvirt.silent -pcds = nx.broken.grub +pcds = nx.broken.shim.grub plat = ArmVirtQemu-AARCH64 dest = ../pc-bios cpy1 = FV/QEMU_EFI.fd edk2-aarch64-code.fd @@ -120,5 +127,20 @@ conf = OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc arch = RISCV64 plat = RiscVVirtQemu dest = ../pc-bios -cpy1 = FV/RISCV_VIRT.fd edk2-riscv.fd -pad1 = edk2-riscv.fd 32m +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 index 870893f7c8e..e564765aaa7 100755 --- a/roms/edk2-build.py +++ b/roms/edk2-build.py @@ -6,6 +6,7 @@ """ import os import sys +import time import shutil import argparse import subprocess @@ -45,19 +46,28 @@ def get_coredir(cfg): return os.path.abspath(cfg['global']['core']) return os.getcwd() -def get_version(cfg): +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 - print('') - print(f'### version [override]: {version}') + 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') - print('') - print(f'### version [rpmbuild]: {version}') + if not silent: + print('') + print(f'### version [rpmbuild]: {version}') return version if os.path.exists(coredir + '/.git'): cmdline = [ 'git', 'describe', '--tags', '--abbrev=8', @@ -66,16 +76,17 @@ def get_version(cfg): stdout = subprocess.PIPE, check = True) version = result.stdout.decode().strip() - print('') - print(f'### version [git]: {version}') + 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): - version = get_version(cfg) +def pcd_version(cfg, silent = False): + version = get_version(cfg, silent) if version is None: return [] return [ '--pcd', pcd_string('PcdFirmwareVersionString', version) ] @@ -85,49 +96,58 @@ def pcd_release_date(): return [] return [ '--pcd', pcd_string('PcdFirmwareReleaseDateString', release_date) ] -def build_message(line, line2 = None): +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 = '') - print('') - print('###') - print(f'### {rebase_prefix}{line}') - if line2: - print(f'### {line2}') - print('###', flush = True) + 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): - print(cmdline, flush = True) +def build_run(cmdline, name, section, silent = False, nologs = False): if silent: - print('### building in silent mode ...', flush = True) + 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) - - logfile = f'{section}.log' - print(f'### writing log to {logfile} ...') - with open(logfile, 'wb') as f: - f.write(result.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: - print('### OK') + 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, dstdir, copy): - srcdir = f'Build/{plat}/{tgt}_GCC5' +def build_copy(plat, tgt, toolchain, dstdir, copy): + srcdir = f'Build/{plat}/{tgt}_{toolchain}' names = copy.split() srcfile = names[0] if len(names) > 1: @@ -156,66 +176,68 @@ def pad_file(dstdir, pad): subprocess.run(cmdline, check = True) # pylint: disable=too-many-branches -def build_one(cfg, build, jobs = None, silent = False): +def build_one(cfg, build, jobs = None, silent = False, nologs = False): + b = cfg[build] + cmdline = [ 'build' ] - cmdline += [ '-t', 'GCC5' ] - cmdline += [ '-p', cfg[build]['conf'] ] + cmdline += [ '-t', get_toolchain(cfg, build) ] + cmdline += [ '-p', b['conf'] ] - if (cfg[build]['conf'].startswith('OvmfPkg/') or - cfg[build]['conf'].startswith('ArmVirtPkg/')): - cmdline += pcd_version(cfg) + 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 cfg[build]['arch'].split(): + for arch in b['arch'].split(): cmdline += [ '-a', arch ] - if 'opts' in cfg[build]: - for name in cfg[build]['opts'].split(): + 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 cfg[build]: - for name in cfg[build]['pcds'].split(): + 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 cfg[build]: - tgts = cfg[build]['tgts'].split() + if 'tgts' in b: + tgts = b['tgts'].split() else: tgts = [ 'DEBUG' ] for tgt in tgts: desc = None - if 'desc' in cfg[build]: - desc = cfg[build]['desc'] - build_message(f'building: {cfg[build]["conf"]} ({cfg[build]["arch"]}, {tgt})', - f'description: {desc}') + 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 ], - cfg[build]['conf'], + b['conf'], build + '.' + tgt, - silent) + silent, + nologs) - if 'plat' in cfg[build]: + if 'plat' in b: # copy files - for cpy in cfg[build]: + for cpy in b: if not cpy.startswith('cpy'): continue - build_copy(cfg[build]['plat'], - tgt, - cfg[build]['dest'], - cfg[build][cpy]) + build_copy(b['plat'], tgt, + get_toolchain(cfg, build), + b['dest'], b[cpy]) # pad builds - for pad in cfg[build]: + for pad in b: if not pad.startswith('pad'): continue - pad_file(cfg[build]['dest'], - cfg[build][pad]) + pad_file(b['dest'], b[pad]) -def build_basetools(silent = False): - build_message('building: BaseTools') +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) + build_run(cmdline, 'BaseTools', 'build.basetools', silent, nologs) def binary_exists(name): for pdir in os.environ['PATH'].split(':'): @@ -223,7 +245,7 @@ def binary_exists(name): return True return False -def prepare_env(cfg): +def prepare_env(cfg, silent = False): """ mimic Conf/BuildEnv.sh """ workspace = os.getcwd() packages = [ workspace, ] @@ -253,7 +275,7 @@ def prepare_env(cfg): 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') + build_message('running BaseTools/BuildEnv', silent = silent) cmdline = [ 'bash', 'BaseTools/BuildEnv' ] subprocess.run(cmdline, cwd = coredir, check = True) @@ -267,20 +289,32 @@ def prepare_env(cfg): os.environ['PYTHONHASHSEED'] = '1' # for cross builds - if binary_exists('arm-linux-gnu-gcc'): + 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(): @@ -303,10 +337,12 @@ def main(): 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, + 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, + 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', @@ -316,6 +352,9 @@ def main(): 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)') @@ -323,6 +362,9 @@ def main(): 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') @@ -335,7 +377,7 @@ def main(): os.chdir(options.directory) if not os.path.exists(options.configfile): - print('config file "{options.configfile}" not found') + print(f'config file "{options.configfile}" not found') return 1 cfg = configparser.ConfigParser() @@ -344,7 +386,7 @@ def main(): if options.list: build_list(cfg) - return + return 0 if not cfg.has_section('global'): cfg.add_section('global') @@ -352,6 +394,8 @@ def main(): 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 @@ -361,18 +405,28 @@ def main(): if options.release_date: release_date = options.release_date - prepare_env(cfg) - build_basetools(options.silent) + 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 and options.match not in build: - print(f'# skipping "{build}" (not matching "{options.match}")') - continue - if options.exclude and options.exclude in build: - print(f'# skipping "{build}" (matching "{options.exclude}")') - continue - build_one(cfg, build, options.jobs, options.silent) + 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 diff --git a/roms/edk2-version b/roms/edk2-version new file mode 100644 index 00000000000..069f19f8bf4 --- /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 af97fd7af5e..c3a19c1e549 160000 --- a/roms/openbios +++ b/roms/openbios @@ -1 +1 @@ -Subproject commit af97fd7af5e7c18f591a7b987291d3db4ffb28b5 +Subproject commit c3a19c1e54977a53027d6232050e1e3e39a98a1b diff --git a/roms/opensbi b/roms/opensbi index 057eb10b6d5..43cace6c367 160000 --- a/roms/opensbi +++ b/roms/opensbi @@ -1 +1 @@ -Subproject commit 057eb10b6d523540012e6947d5c9f63e95244e94 +Subproject commit 43cace6c3671e5172d0df0a8963e552bb04b7b20 diff --git a/roms/seabios b/roms/seabios index ea1b7a07339..b52ca86e094 160000 --- a/roms/seabios +++ b/roms/seabios @@ -1 +1 @@ -Subproject commit ea1b7a0733906b8425d948ae94fba63c32b1d425 +Subproject commit b52ca86e094d19b58e2304417787e96b940e39c6 diff --git a/roms/seabios-hppa b/roms/seabios-hppa index 673d2595d4f..3391c580960 160000 --- a/roms/seabios-hppa +++ b/roms/seabios-hppa @@ -1 +1 @@ -Subproject commit 673d2595d4f773cc266cbf8dbaf2f475a6adb949 +Subproject commit 3391c580960febcb9fa8f686f9666adaa462c349 diff --git a/roms/skiboot b/roms/skiboot index 24a7eb35966..785a5e3070a 160000 --- a/roms/skiboot +++ b/roms/skiboot @@ -1 +1 @@ -Subproject commit 24a7eb35966d93455520bc2debdd7954314b638b +Subproject commit 785a5e3070a86e18521e62fe202b87209de30fa2 diff --git a/roms/vbootrom b/roms/vbootrom index 0c37a43527f..1287b6e42e8 160000 --- a/roms/vbootrom +++ b/roms/vbootrom @@ -1 +1 @@ -Subproject commit 0c37a43527f0ee2b9584e7fb2fdc805e902635ac +Subproject commit 1287b6e42e839ba2ab0f06268c5b53ae60df3537 diff --git a/rust/.gitignore b/rust/.gitignore new file mode 100644 index 00000000000..1bf71b1f68e --- /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 00000000000..b785c718f31 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,177 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[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 = "bits" +version = "0.1.0" +dependencies = [ + "qemu_api_macros", +] + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "foreign" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ca1b5be8c9d320daf386f1809c7acc0cb09accbae795c2001953fa50585846" +dependencies = [ + "libc", +] + +[[package]] +name = "hpet" +version = "0.1.0" +dependencies = [ + "qemu_api", + "qemu_api_macros", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" + +[[package]] +name = "pl011" +version = "0.1.0" +dependencies = [ + "bilge", + "bilge-impl", + "bits", + "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 = [ + "anyhow", + "foreign", + "libc", + "qemu_api_macros", +] + +[[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 00000000000..0868e1b4268 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,101 @@ +[workspace] +resolver = "2" +members = [ + "bits", + "qemu-api-macros", + "qemu-api", + "hw/char/pl011", + "hw/timer/hpet", +] + +[workspace.package] +edition = "2021" +homepage = "https://www.qemu.org" +license = "GPL-2.0-or-later" +repository = "https://gitlab.com/qemu-project/qemu/" +rust-version = "1.77.0" + +[workspace.lints.rust] +unexpected_cfgs = { level = "deny", check-cfg = [ + 'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)', +] } + +# 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 disables this +# and ensures that we do not have misspelled allow() attributes. +unknown_lints = "allow" + +# Prohibit code that is forbidden in Rust 2024 +unsafe_op_in_unsafe_fn = "deny" + +[workspace.lints.rustdoc] +private_intra_doc_links = "allow" + +broken_intra_doc_links = "deny" +invalid_html_tags = "deny" +invalid_rust_codeblocks = "deny" +bare_urls = "deny" +unescaped_backticks = "deny" +redundant_explicit_links = "deny" + +[workspace.lints.clippy] +# default-warn lints +result_unit_err = "allow" +should_implement_trait = "deny" +# can be for a reason, e.g. in callbacks +unused_self = "allow" +# common in device crates +upper_case_acronyms = "allow" + +# default-allow lints +as_ptr_cast_mut = "deny" +as_underscore = "deny" +assertions_on_result_states = "deny" +bool_to_int_with_if = "deny" +borrow_as_ptr = "deny" +cast_lossless = "deny" +dbg_macro = "deny" +debug_assert_with_mut_call = "deny" +derive_partial_eq_without_eq = "deny" +doc_markdown = "deny" +empty_structs_with_brackets = "deny" +ignored_unit_patterns = "deny" +implicit_clone = "deny" +macro_use_imports = "deny" +missing_safety_doc = "deny" +mut_mut = "deny" +needless_bitwise_bool = "deny" +needless_pass_by_ref_mut = "deny" +needless_update = "deny" +no_effect_underscore_binding = "deny" +option_option = "deny" +or_fun_call = "deny" +ptr_as_ptr = "deny" +ptr_cast_constness = "deny" +pub_underscore_fields = "deny" +redundant_clone = "deny" +redundant_closure_for_method_calls = "deny" +redundant_else = "deny" +redundant_pub_crate = "deny" +ref_binding_to_reference = "deny" +ref_option_ref = "deny" +return_self_not_must_use = "deny" +same_name_method = "deny" +semicolon_inside_block = "deny" +shadow_unrelated = "deny" +significant_drop_in_scrutinee = "deny" +significant_drop_tightening = "deny" +suspicious_operation_groupings = "deny" +transmute_ptr_to_ptr = "deny" +transmute_undefined_repr = "deny" +type_repetition_in_bounds = "deny" +uninlined_format_args = "deny" +used_underscore_binding = "deny" + +# nice to have, but cannot be enabled yet +#wildcard_imports = "deny" # still have many bindings::* imports + +# these may have false positives +#option_if_let_else = "deny" +cognitive_complexity = "deny" diff --git a/rust/Kconfig b/rust/Kconfig new file mode 100644 index 00000000000..f9f5c390988 --- /dev/null +++ b/rust/Kconfig @@ -0,0 +1 @@ +source hw/Kconfig diff --git a/rust/bits/Cargo.toml b/rust/bits/Cargo.toml new file mode 100644 index 00000000000..1ff38a41175 --- /dev/null +++ b/rust/bits/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "bits" +version = "0.1.0" +authors = ["Paolo Bonzini "] +description = "const-friendly bit flags" +resolver = "2" +publish = false + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +qemu_api_macros = { path = "../qemu-api-macros" } + +[lints] +workspace = true diff --git a/rust/bits/meson.build b/rust/bits/meson.build new file mode 100644 index 00000000000..2a41e138c54 --- /dev/null +++ b/rust/bits/meson.build @@ -0,0 +1,16 @@ +_bits_rs = static_library( + 'bits', + 'src/lib.rs', + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [qemu_api_macros], +) + +bits_rs = declare_dependency(link_with: _bits_rs) + +rust.test('rust-bits-tests', _bits_rs, + suite: ['unit', 'rust']) + +rust.doctest('rust-bits-doctests', _bits_rs, + dependencies: bits_rs, + suite: ['doc', 'rust']) diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs new file mode 100644 index 00000000000..d485d6bd110 --- /dev/null +++ b/rust/bits/src/lib.rs @@ -0,0 +1,443 @@ +// SPDX-License-Identifier: MIT or Apache-2.0 or GPL-2.0-or-later + +/// # Definition entry point +/// +/// Define a struct with a single field of type $type. Include public constants +/// for each element listed in braces. +/// +/// The unnamed element at the end, if present, can be used to enlarge the set +/// of valid bits. Bits that are valid but not listed are treated normally for +/// the purpose of arithmetic operations, and are printed with their hexadecimal +/// value. +/// +/// The struct implements the following traits: [`BitAnd`](std::ops::BitAnd), +/// [`BitOr`](std::ops::BitOr), [`BitXor`](std::ops::BitXor), +/// [`Not`](std::ops::Not), [`Sub`](std::ops::Sub); [`Debug`](std::fmt::Debug), +/// [`Display`](std::fmt::Display), [`Binary`](std::fmt::Binary), +/// [`Octal`](std::fmt::Octal), [`LowerHex`](std::fmt::LowerHex), +/// [`UpperHex`](std::fmt::UpperHex); [`From`]``/[`Into`]`` where +/// type is the type specified in the definition. +/// +/// ## Example +/// +/// ``` +/// # use bits::bits; +/// bits! { +/// pub struct Colors(u8) { +/// BLACK = 0, +/// RED = 1, +/// GREEN = 1 << 1, +/// BLUE = 1 << 2, +/// WHITE = (1 << 0) | (1 << 1) | (1 << 2), +/// } +/// } +/// ``` +/// +/// ``` +/// # use bits::bits; +/// # bits! { pub struct Colors(u8) { BLACK = 0, RED = 1, GREEN = 1 << 1, BLUE = 1 << 2, } } +/// +/// bits! { +/// pub struct Colors8(u8) { +/// BLACK = 0, +/// RED = 1, +/// GREEN = 1 << 1, +/// BLUE = 1 << 2, +/// WHITE = (1 << 0) | (1 << 1) | (1 << 2), +/// +/// _ = 255, +/// } +/// } +/// +/// // The previously defined struct ignores bits not explicitly defined. +/// assert_eq!( +/// Colors::from(255).into_bits(), +/// (Colors::RED | Colors::GREEN | Colors::BLUE).into_bits() +/// ); +/// +/// // Adding "_ = 255" makes it retain other bits as well. +/// assert_eq!(Colors8::from(255).into_bits(), 255); +/// +/// // all() does not include the additional bits, valid_bits() does +/// assert_eq!(Colors8::all().into_bits(), Colors::all().into_bits()); +/// assert_eq!(Colors8::valid_bits().into_bits(), 255); +/// ``` +/// +/// # Evaluation entry point +/// +/// Return a constant corresponding to the boolean expression `$expr`. +/// Identifiers in the expression correspond to values defined for the +/// type `$type`. Supported operators are `!` (unary), `-`, `&`, `^`, `|`. +/// +/// ## Examples +/// +/// ``` +/// # use bits::bits; +/// bits! { +/// pub struct Colors(u8) { +/// BLACK = 0, +/// RED = 1, +/// GREEN = 1 << 1, +/// BLUE = 1 << 2, +/// // same as "WHITE = 7", +/// WHITE = bits!(Self as u8: RED | GREEN | BLUE), +/// } +/// } +/// +/// let rgb = bits! { Colors: RED | GREEN | BLUE }; +/// assert_eq!(rgb, Colors::WHITE); +/// ``` +#[macro_export] +macro_rules! bits { + { + $(#[$struct_meta:meta])* + $struct_vis:vis struct $struct_name:ident($field_vis:vis $type:ty) { + $($(#[$const_meta:meta])* $const:ident = $val:expr),+ + $(,_ = $mask:expr)? + $(,)? + } + } => { + $(#[$struct_meta])* + #[derive(Clone, Copy, PartialEq, Eq)] + #[repr(transparent)] + $struct_vis struct $struct_name($field_vis $type); + + impl $struct_name { + $( #[allow(dead_code)] $(#[$const_meta])* + pub const $const: $struct_name = $struct_name($val); )+ + + #[doc(hidden)] + const VALID__: $type = $( Self::$const.0 )|+ $(|$mask)?; + + #[allow(dead_code)] + #[inline(always)] + pub const fn empty() -> Self { + Self(0) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn all() -> Self { + Self($( Self::$const.0 )|+) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn valid_bits() -> Self { + Self(Self::VALID__) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn valid(val: $type) -> bool { + (val & !Self::VALID__) == 0 + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn any_set(self, mask: Self) -> bool { + (self.0 & mask.0) != 0 + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn all_set(self, mask: Self) -> bool { + (self.0 & mask.0) == mask.0 + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn none_set(self, mask: Self) -> bool { + (self.0 & mask.0) == 0 + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn from_bits(value: $type) -> Self { + $struct_name(value) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn into_bits(self) -> $type { + self.0 + } + + #[allow(dead_code)] + #[inline(always)] + pub fn set(&mut self, rhs: Self) { + self.0 |= rhs.0; + } + + #[allow(dead_code)] + #[inline(always)] + pub fn clear(&mut self, rhs: Self) { + self.0 &= !rhs.0; + } + + #[allow(dead_code)] + #[inline(always)] + pub fn toggle(&mut self, rhs: Self) { + self.0 ^= rhs.0; + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn intersection(self, rhs: Self) -> Self { + $struct_name(self.0 & rhs.0) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn difference(self, rhs: Self) -> Self { + $struct_name(self.0 & !rhs.0) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn symmetric_difference(self, rhs: Self) -> Self { + $struct_name(self.0 ^ rhs.0) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn union(self, rhs: Self) -> Self { + $struct_name(self.0 | rhs.0) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn invert(self) -> Self { + $struct_name(self.0 ^ Self::VALID__) + } + } + + impl ::std::fmt::Binary for $struct_name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + // If no width, use the highest valid bit + let width = f.width().unwrap_or((Self::VALID__.ilog2() + 1) as usize); + write!(f, "{:0>width$.precision$b}", self.0, + width = width, + precision = f.precision().unwrap_or(width)) + } + } + + impl ::std::fmt::LowerHex for $struct_name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + <$type as ::std::fmt::LowerHex>::fmt(&self.0, f) + } + } + + impl ::std::fmt::Octal for $struct_name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + <$type as ::std::fmt::Octal>::fmt(&self.0, f) + } + } + + impl ::std::fmt::UpperHex for $struct_name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + <$type as ::std::fmt::UpperHex>::fmt(&self.0, f) + } + } + + impl ::std::fmt::Debug for $struct_name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "{}({})", stringify!($struct_name), self) + } + } + + impl ::std::fmt::Display for $struct_name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + use ::std::fmt::Display; + let mut first = true; + let mut left = self.0; + $(if Self::$const.0.is_power_of_two() && (self & Self::$const).0 != 0 { + if first { first = false } else { Display::fmt(&'|', f)?; } + Display::fmt(stringify!($const), f)?; + left -= Self::$const.0; + })+ + if first { + Display::fmt(&'0', f) + } else if left != 0 { + write!(f, "|{left:#x}") + } else { + Ok(()) + } + } + } + + impl ::std::cmp::PartialEq<$type> for $struct_name { + fn eq(&self, rhs: &$type) -> bool { + self.0 == *rhs + } + } + + impl ::std::ops::BitAnd<$struct_name> for &$struct_name { + type Output = $struct_name; + fn bitand(self, rhs: $struct_name) -> Self::Output { + $struct_name(self.0 & rhs.0) + } + } + + impl ::std::ops::BitAndAssign<$struct_name> for $struct_name { + fn bitand_assign(&mut self, rhs: $struct_name) { + self.0 = self.0 & rhs.0 + } + } + + impl ::std::ops::BitXor<$struct_name> for &$struct_name { + type Output = $struct_name; + fn bitxor(self, rhs: $struct_name) -> Self::Output { + $struct_name(self.0 ^ rhs.0) + } + } + + impl ::std::ops::BitXorAssign<$struct_name> for $struct_name { + fn bitxor_assign(&mut self, rhs: $struct_name) { + self.0 = self.0 ^ rhs.0 + } + } + + impl ::std::ops::BitOr<$struct_name> for &$struct_name { + type Output = $struct_name; + fn bitor(self, rhs: $struct_name) -> Self::Output { + $struct_name(self.0 | rhs.0) + } + } + + impl ::std::ops::BitOrAssign<$struct_name> for $struct_name { + fn bitor_assign(&mut self, rhs: $struct_name) { + self.0 = self.0 | rhs.0 + } + } + + impl ::std::ops::Sub<$struct_name> for &$struct_name { + type Output = $struct_name; + fn sub(self, rhs: $struct_name) -> Self::Output { + $struct_name(self.0 & !rhs.0) + } + } + + impl ::std::ops::SubAssign<$struct_name> for $struct_name { + fn sub_assign(&mut self, rhs: $struct_name) { + self.0 = self.0 - rhs.0 + } + } + + impl ::std::ops::Not for &$struct_name { + type Output = $struct_name; + fn not(self) -> Self::Output { + $struct_name(self.0 ^ $struct_name::VALID__) + } + } + + impl ::std::ops::BitAnd<$struct_name> for $struct_name { + type Output = Self; + fn bitand(self, rhs: Self) -> Self::Output { + $struct_name(self.0 & rhs.0) + } + } + + impl ::std::ops::BitXor<$struct_name> for $struct_name { + type Output = Self; + fn bitxor(self, rhs: Self) -> Self::Output { + $struct_name(self.0 ^ rhs.0) + } + } + + impl ::std::ops::BitOr<$struct_name> for $struct_name { + type Output = Self; + fn bitor(self, rhs: Self) -> Self::Output { + $struct_name(self.0 | rhs.0) + } + } + + impl ::std::ops::Sub<$struct_name> for $struct_name { + type Output = Self; + fn sub(self, rhs: Self) -> Self::Output { + $struct_name(self.0 & !rhs.0) + } + } + + impl ::std::ops::Not for $struct_name { + type Output = Self; + fn not(self) -> Self::Output { + $struct_name(self.0 ^ Self::VALID__) + } + } + + impl From<$struct_name> for $type { + fn from(x: $struct_name) -> $type { + x.0 + } + } + + impl From<$type> for $struct_name { + fn from(x: $type) -> Self { + $struct_name(x & Self::VALID__) + } + } + }; + + { $type:ty: $expr:expr } => { + ::qemu_api_macros::bits_const_internal! { $type @ ($expr) } + }; + + { $type:ty as $int_type:ty: $expr:expr } => { + (::qemu_api_macros::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type + }; +} + +#[cfg(test)] +mod test { + bits! { + pub struct InterruptMask(u32) { + 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, + + E = bits!(Self as u32: OE | BE | PE | FE), + MS = bits!(Self as u32: RI | DSR | DCD | CTS), + } + } + + #[test] + pub fn test_not() { + assert_eq!( + !InterruptMask::from(InterruptMask::RT.0), + InterruptMask::E | InterruptMask::MS | InterruptMask::TX | InterruptMask::RX + ); + } + + #[test] + pub fn test_and() { + assert_eq!( + InterruptMask::from(0), + InterruptMask::MS & InterruptMask::OE + ) + } + + #[test] + pub fn test_or() { + assert_eq!( + InterruptMask::E, + InterruptMask::OE | InterruptMask::BE | InterruptMask::PE | InterruptMask::FE + ); + } + + #[test] + pub fn test_xor() { + assert_eq!( + InterruptMask::E ^ InterruptMask::BE, + InterruptMask::OE | InterruptMask::PE | InterruptMask::FE + ); + } +} diff --git a/rust/hw/Kconfig b/rust/hw/Kconfig new file mode 100644 index 00000000000..36f92ec0287 --- /dev/null +++ b/rust/hw/Kconfig @@ -0,0 +1,3 @@ +# devices Kconfig +source char/Kconfig +source timer/Kconfig diff --git a/rust/hw/char/Kconfig b/rust/hw/char/Kconfig new file mode 100644 index 00000000000..5fe800c4806 --- /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 00000000000..5716dc43ef6 --- /dev/null +++ b/rust/hw/char/meson.build @@ -0,0 +1 @@ +subdir('pl011') diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml new file mode 100644 index 00000000000..003ef9613d4 --- /dev/null +++ b/rust/hw/char/pl011/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "pl011" +version = "0.1.0" +authors = ["Manos Pitsidianakis "] +description = "pl011 device model for QEMU" +resolver = "2" +publish = false + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lib] +crate-type = ["staticlib"] + +[dependencies] +bilge = { version = "0.2.0" } +bilge-impl = { version = "0.2.0" } +bits = { path = "../../../bits" } +qemu_api = { path = "../../../qemu-api" } +qemu_api_macros = { path = "../../../qemu-api-macros" } + +[lints] +workspace = true diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build new file mode 100644 index 00000000000..2a1be329abc --- /dev/null +++ b/rust/hw/char/pl011/meson.build @@ -0,0 +1,21 @@ +_libpl011_rs = static_library( + 'pl011', + files('src/lib.rs'), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [ + bilge_rs, + bilge_impl_rs, + bits_rs, + 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_rs, 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 00000000000..5b53f2649f1 --- /dev/null +++ b/rust/hw/char/pl011/src/device.rs @@ -0,0 +1,714 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{ffi::CStr, mem::size_of}; + +use qemu_api::{ + chardev::{CharBackend, Chardev, Event}, + impl_vmstate_forward, + irq::{IRQState, InterruptSource}, + log::Log, + log_mask_ln, + memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, + prelude::*, + qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, + qom::{ObjectImpl, Owned, ParentField, ParentInit}, + static_assert, + sysbus::{SysBusDevice, SysBusDeviceImpl}, + uninit_field_mut, + vmstate::VMStateDescription, +}; + +use crate::{ + device_class, + registers::{self, Interrupt, RegisterOffset}, +}; + +// 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 + +/// Integer Baud Rate Divider, `UARTIBRD` +const IBRD_MASK: u32 = 0xffff; + +/// Fractional Baud Rate Divider, `UARTFBRD` +const FBRD_MASK: u32 = 0x3f; + +/// QEMU sourced constant. +pub const PL011_FIFO_DEPTH: u32 = 16; + +#[derive(Clone, Copy)] +struct DeviceId(&'static [u8; 8]); + +impl std::ops::Index for DeviceId { + type Output = u8; + + fn index(&self, idx: hwaddr) -> &Self::Output { + &self.0[idx as usize] + } +} + +// FIFOs use 32-bit indices instead of usize, for compatibility with +// the migration stream produced by the C version of this device. +#[repr(transparent)] +#[derive(Debug, Default)] +pub struct Fifo([registers::Data; PL011_FIFO_DEPTH as usize]); +impl_vmstate_forward!(Fifo); + +impl Fifo { + const fn len(&self) -> u32 { + self.0.len() as u32 + } +} + +impl std::ops::IndexMut for Fifo { + fn index_mut(&mut self, idx: u32) -> &mut Self::Output { + &mut self.0[idx as usize] + } +} + +impl std::ops::Index for Fifo { + type Output = registers::Data; + + fn index(&self, idx: u32) -> &Self::Output { + &self.0[idx as usize] + } +} + +#[repr(C)] +#[derive(Debug, Default)] +pub struct PL011Registers { + #[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: Interrupt, + pub int_level: Interrupt, + pub read_fifo: Fifo, + pub ilpr: u32, + pub ibrd: u32, + pub fbrd: u32, + pub ifl: u32, + pub read_pos: u32, + pub read_count: u32, + pub read_trigger: u32, +} + +#[repr(C)] +#[derive(qemu_api_macros::Object)] +/// PL011 Device Model in QEMU +pub struct PL011State { + pub parent_obj: ParentField, + pub iomem: MemoryRegion, + #[doc(alias = "chr")] + pub char_backend: CharBackend, + pub regs: BqlRefCell, + /// 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: [InterruptSource; IRQMASK.len()], + #[doc(alias = "clk")] + pub clock: Owned, + #[doc(alias = "migrate_clk")] + pub migrate_clock: bool, +} + +// Some C users of this device embed its state struct into their own +// structs, so the size of the Rust version must not be any larger +// than the size of the C one. If this assert triggers you need to +// expand the padding_for_rust[] array in the C PL011State struct. +static_assert!(size_of::() <= size_of::()); + +qom_isa!(PL011State : SysBusDevice, DeviceState, Object); + +#[repr(C)] +pub struct PL011Class { + parent_class: ::Class, + /// The byte string that identifies the device. + device_id: DeviceId, +} + +trait PL011Impl: SysBusDeviceImpl + IsA { + const DEVICE_ID: DeviceId; +} + +impl PL011Class { + fn class_init(&mut self) { + self.device_id = T::DEVICE_ID; + self.parent_class.class_init::(); + } +} + +unsafe impl ObjectType for PL011State { + type Class = PL011Class; + const TYPE_NAME: &'static CStr = crate::TYPE_PL011; +} + +impl PL011Impl for PL011State { + const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]); +} + +impl ObjectImpl for PL011State { + type ParentType = SysBusDevice; + + const INSTANCE_INIT: Option)> = Some(Self::init); + const INSTANCE_POST_INIT: Option = Some(Self::post_init); + const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::; +} + +impl DeviceImpl for PL011State { + fn properties() -> &'static [Property] { + &device_class::PL011_PROPERTIES + } + fn vmsd() -> Option<&'static VMStateDescription> { + Some(&device_class::VMSTATE_PL011) + } + const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); +} + +impl ResettablePhasesImpl for PL011State { + const HOLD: Option = Some(Self::reset_hold); +} + +impl SysBusDeviceImpl for PL011State {} + +impl PL011Registers { + pub(self) fn read(&mut self, offset: RegisterOffset) -> (bool, u32) { + use RegisterOffset::*; + + let mut update = false; + let result = match offset { + DR => self.read_data_register(&mut update), + RSR => u32::from(self.receive_status_error_clear), + FR => u32::from(self.flags), + FBRD => self.fbrd, + ILPR => self.ilpr, + IBRD => self.ibrd, + LCR_H => u32::from(self.line_control), + CR => u32::from(self.control), + FLS => self.ifl, + IMSC => u32::from(self.int_enabled), + RIS => u32::from(self.int_level), + MIS => u32::from(self.int_level & self.int_enabled), + 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 + } + DMACR => self.dmacr, + }; + (update, result) + } + + pub(self) fn write( + &mut self, + offset: RegisterOffset, + value: u32, + char_backend: &CharBackend, + ) -> bool { + // eprintln!("write offset {offset} value {value}"); + use RegisterOffset::*; + match offset { + DR => return self.write_data_register(value), + RSR => { + self.receive_status_error_clear = 0.into(); + } + FR => { + // flag writes are ignored + } + ILPR => { + self.ilpr = value; + } + IBRD => { + self.ibrd = value; + } + FBRD => { + self.fbrd = value; + } + LCR_H => { + let new_val: registers::LineControl = value.into(); + // Reset the FIFO state on FIFO enable or disable + if self.line_control.fifos_enabled() != new_val.fifos_enabled() { + self.reset_rx_fifo(); + self.reset_tx_fifo(); + } + let update = (self.line_control.send_break() != new_val.send_break()) && { + let break_enable = new_val.send_break(); + let _ = char_backend.send_break(break_enable); + self.loopback_break(break_enable) + }; + self.line_control = new_val; + self.set_read_trigger(); + return update; + } + CR => { + // ??? Need to implement the enable bit. + self.control = value.into(); + return self.loopback_mdmctrl(); + } + FLS => { + self.ifl = value; + self.set_read_trigger(); + } + IMSC => { + self.int_enabled = Interrupt::from(value); + return true; + } + RIS => {} + MIS => {} + ICR => { + self.int_level &= !Interrupt::from(value); + return true; + } + DMACR => { + self.dmacr = value; + if value & 3 > 0 { + log_mask_ln!(Log::Unimp, "pl011: DMA not implemented"); + } + } + } + false + } + + fn read_data_register(&mut self, update: &mut bool) -> u32 { + 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 &= !Interrupt::RX; + } + self.receive_status_error_clear.set_from_data(c); + *update = true; + u32::from(c) + } + + fn write_data_register(&mut self, value: u32) -> bool { + if !self.control.enable_uart() { + log_mask_ln!(Log::GuestError, "PL011 data written to disabled UART"); + } + if !self.control.enable_transmit() { + log_mask_ln!(Log::GuestError, "PL011 data written to disabled TX UART"); + } + // interrupts always checked + let _ = self.loopback_tx(value.into()); + self.int_level |= Interrupt::TX; + true + } + + #[inline] + #[must_use] + fn loopback_tx(&mut self, value: registers::Data) -> bool { + // 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.loopback_enabled() && self.fifo_rx_put(value) + } + + #[must_use] + fn loopback_mdmctrl(&mut self) -> bool { + if !self.loopback_enabled() { + return false; + } + + /* + * 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; + } + if self.flags.data_carrier_detect() { + il |= Interrupt::DCD; + } + if self.flags.clear_to_send() { + il |= Interrupt::CTS; + } + if self.flags.ring_indicator() { + il |= Interrupt::RI; + } + self.int_level = il; + true + } + + fn loopback_break(&mut self, enable: bool) -> bool { + enable && self.loopback_tx(registers::Data::BREAK) + } + + fn set_read_trigger(&mut self) { + self.read_trigger = 1; + } + + pub fn reset(&mut self) { + self.line_control.reset(); + self.receive_status_error_clear.reset(); + self.dmacr = 0; + self.int_enabled = 0.into(); + self.int_level = 0.into(); + self.ilpr = 0; + self.ibrd = 0; + self.fbrd = 0; + self.read_trigger = 1; + self.ifl = 0x12; + self.control.reset(); + self.flags.reset(); + self.reset_rx_fifo(); + self.reset_tx_fifo(); + } + + pub fn reset_rx_fifo(&mut self) { + self.read_count = 0; + self.read_pos = 0; + + // Reset FIFO flags + self.flags.set_receive_fifo_full(false); + self.flags.set_receive_fifo_empty(true); + } + + pub fn reset_tx_fifo(&mut self) { + // Reset FIFO flags + self.flags.set_transmit_fifo_full(false); + self.flags.set_transmit_fifo_empty(true); + } + + #[inline] + pub fn fifo_enabled(&self) -> bool { + 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) -> u32 { + // Note: FIFO depth is expected to be power-of-2 + if self.fifo_enabled() { + return PL011_FIFO_DEPTH; + } + 1 + } + + #[must_use] + pub fn fifo_rx_put(&mut self, value: registers::Data) -> bool { + 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 |= Interrupt::RX; + return true; + } + false + } + + pub fn post_load(&mut self) -> 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(()) + } +} + +impl PL011State { + /// Initializes a pre-allocated, uninitialized 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 uninitialized + /// values with the sole exception of `parent_obj`. + unsafe fn init(mut this: ParentInit) { + static PL011_OPS: MemoryRegionOps = MemoryRegionOpsBuilder::::new() + .read(&PL011State::read) + .write(&PL011State::write) + .native_endian() + .impl_sizes(4, 4) + .build(); + + // SAFETY: this and this.iomem are guaranteed to be valid at this point + MemoryRegion::init_io( + &mut uninit_field_mut!(*this, iomem), + &PL011_OPS, + "pl011", + 0x1000, + ); + + uninit_field_mut!(*this, regs).write(Default::default()); + + let clock = DeviceState::init_clock_in( + &mut this, + "clk", + &Self::clock_update, + ClockEvent::ClockUpdate, + ); + uninit_field_mut!(*this, clock).write(clock); + } + + const fn clock_update(&self, _event: ClockEvent) { + /* pl011_trace_baudrate_change(s); */ + } + + fn post_init(&self) { + self.init_mmio(&self.iomem); + for irq in self.interrupts.iter() { + self.init_irq(irq); + } + } + + fn read(&self, offset: hwaddr, _size: u32) -> u64 { + match RegisterOffset::try_from(offset) { + Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { + let device_id = self.get_class().device_id; + u64::from(device_id[(offset - 0xfe0) >> 2]) + } + Err(_) => { + log_mask_ln!(Log::GuestError, "PL011State::read: Bad offset {offset}"); + 0 + } + Ok(field) => { + let (update_irq, result) = self.regs.borrow_mut().read(field); + if update_irq { + self.update(); + self.char_backend.accept_input(); + } + result.into() + } + } + } + + fn write(&self, offset: hwaddr, value: u64, _size: u32) { + let mut update_irq = false; + if let Ok(field) = RegisterOffset::try_from(offset) { + // qemu_chr_fe_write_all() calls into the can_receive + // callback, so handle writes before entering PL011Registers. + if field == RegisterOffset::DR { + // ??? Check if transmitter is enabled. + let ch: [u8; 1] = [value as u8]; + // XXX this blocks entire thread. Rewrite to use + // qemu_chr_fe_write and background I/O callbacks + let _ = self.char_backend.write_all(&ch); + } + + update_irq = self + .regs + .borrow_mut() + .write(field, value as u32, &self.char_backend); + } else { + log_mask_ln!( + Log::GuestError, + "PL011State::write: Bad offset {offset} value {value}" + ); + } + if update_irq { + self.update(); + } + } + + fn can_receive(&self) -> u32 { + let regs = self.regs.borrow(); + // trace_pl011_can_receive(s->lcr, s->read_count, r); + regs.fifo_depth() - regs.read_count + } + + fn receive(&self, buf: &[u8]) { + let mut regs = self.regs.borrow_mut(); + if regs.loopback_enabled() { + // 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. + return; + } + + let mut update_irq = false; + for &c in buf { + let c: u32 = c.into(); + update_irq |= regs.fifo_rx_put(c.into()); + } + + // Release the BqlRefCell before calling self.update() + drop(regs); + if update_irq { + self.update(); + } + } + + fn event(&self, event: Event) { + let mut update_irq = false; + let mut regs = self.regs.borrow_mut(); + if event == Event::CHR_EVENT_BREAK && !regs.loopback_enabled() { + update_irq = regs.fifo_rx_put(registers::Data::BREAK); + } + // Release the BqlRefCell before calling self.update() + drop(regs); + + if update_irq { + self.update() + } + } + + fn realize(&self) -> qemu_api::Result<()> { + self.char_backend + .enable_handlers(self, Self::can_receive, Self::receive, Self::event); + Ok(()) + } + + fn reset_hold(&self, _type: ResetType) { + self.regs.borrow_mut().reset(); + } + + fn update(&self) { + let regs = self.regs.borrow(); + let flags = regs.int_level & regs.int_enabled; + for (irq, i) in self.interrupts.iter().zip(IRQMASK) { + irq.set(flags.any_set(i)); + } + } + + pub fn post_load(&self, _version_id: u32) -> Result<(), ()> { + self.regs.borrow_mut().post_load() + } +} + +/// Which bits in the interrupt status matter for each outbound IRQ line ? +const IRQMASK: [Interrupt; 6] = [ + Interrupt::all(), + Interrupt::RX, + Interrupt::TX, + Interrupt::RT, + Interrupt::MS, + Interrupt::E, +]; + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer for `chr` +/// and `irq`. +#[no_mangle] +pub unsafe extern "C" fn pl011_create( + addr: u64, + irq: *mut IRQState, + chr: *mut Chardev, +) -> *mut DeviceState { + // SAFETY: The callers promise that they have owned references. + // They do not gift them to pl011_create, so use `Owned::from`. + let irq = unsafe { Owned::::from(&*irq) }; + + let dev = PL011State::new(); + if !chr.is_null() { + let chr = unsafe { Owned::::from(&*chr) }; + dev.prop_set_chr("chardev", &chr); + } + dev.sysbus_realize(); + dev.mmio_map(0, addr); + dev.connect_irq(0, &irq); + + // The pointer is kept alive by the QOM tree; drop the owned ref + dev.as_mut_ptr() +} + +#[repr(C)] +#[derive(qemu_api_macros::Object)] +/// PL011 Luminary device model. +pub struct PL011Luminary { + parent_obj: ParentField, +} + +qom_isa!(PL011Luminary : PL011State, SysBusDevice, DeviceState, Object); + +unsafe impl ObjectType for PL011Luminary { + type Class = ::Class; + const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY; +} + +impl ObjectImpl for PL011Luminary { + type ParentType = PL011State; + + const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::; +} + +impl PL011Impl for PL011Luminary { + const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]); +} + +impl DeviceImpl for PL011Luminary {} +impl ResettablePhasesImpl for PL011Luminary {} +impl SysBusDeviceImpl for PL011Luminary {} 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 00000000000..d328d846323 --- /dev/null +++ b/rust/hw/char/pl011/src/device_class.rs @@ -0,0 +1,103 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{ + ffi::{c_int, c_void}, + ptr::NonNull, +}; + +use qemu_api::{ + bindings::{qdev_prop_bool, qdev_prop_chr}, + prelude::*, + vmstate::VMStateDescription, + vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused, + zeroable::Zeroable, +}; + +use crate::device::{PL011Registers, PL011State}; + +extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { + let state = NonNull::new(opaque).unwrap().cast::(); + unsafe { state.as_ref().migrate_clock } +} + +/// Migration subsection for [`PL011State`] clock. +static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { + name: c"pl011/clock".as_ptr(), + version_id: 1, + minimum_version_id: 1, + needed: Some(pl011_clock_needed), + fields: vmstate_fields! { + vmstate_clock!(PL011State, clock), + }, + ..Zeroable::ZERO +}; + +extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { + let state = NonNull::new(opaque).unwrap().cast::(); + let result = unsafe { state.as_ref().post_load(version_id as u32) }; + if result.is_err() { + -1 + } else { + 0 + } +} + +static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription { + name: c"pl011/regs".as_ptr(), + version_id: 2, + minimum_version_id: 2, + fields: vmstate_fields! { + vmstate_of!(PL011Registers, flags), + vmstate_of!(PL011Registers, line_control), + vmstate_of!(PL011Registers, receive_status_error_clear), + vmstate_of!(PL011Registers, control), + vmstate_of!(PL011Registers, dmacr), + vmstate_of!(PL011Registers, int_enabled), + vmstate_of!(PL011Registers, int_level), + vmstate_of!(PL011Registers, read_fifo), + vmstate_of!(PL011Registers, ilpr), + vmstate_of!(PL011Registers, ibrd), + vmstate_of!(PL011Registers, fbrd), + vmstate_of!(PL011Registers, ifl), + vmstate_of!(PL011Registers, read_pos), + vmstate_of!(PL011Registers, read_count), + vmstate_of!(PL011Registers, read_trigger), + }, + ..Zeroable::ZERO +}; + +pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { + name: c"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_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell), + }, + subsections: vmstate_subsections! { + VMSTATE_PL011_CLOCK + }, + ..Zeroable::ZERO +}; + +qemu_api::declare_properties! { + PL011_PROPERTIES, + qemu_api::define_property!( + c"chardev", + PL011State, + char_backend, + unsafe { &qdev_prop_chr }, + CharBackend + ), + qemu_api::define_property!( + c"migrate-clk", + PL011State, + migrate_clock, + unsafe { &qdev_prop_bool }, + bool, + default = true + ), +} diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs new file mode 100644 index 00000000000..5c4fbc9d148 --- /dev/null +++ b/rust/hw/char/pl011/src/lib.rs @@ -0,0 +1,22 @@ +// 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. +//! +//! # Library crate +//! +//! See [`PL011State`](crate::device::PL011State) for the device model type and +//! the [`registers`] module for register types. + +mod device; +mod device_class; +mod registers; + +pub use device::pl011_create; + +pub const TYPE_PL011: &::std::ffi::CStr = c"pl011"; +pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c"pl011_luminary"; diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs new file mode 100644 index 00000000000..7ececd39f86 --- /dev/null +++ b/rust/hw/char/pl011/src/registers.rs @@ -0,0 +1,350 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Device registers exposed as typed structs which are backed by arbitrary +//! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. + +// For more detail see the PL011 Technical Reference Manual DDI0183: +// https://developer.arm.com/documentation/ddi0183/latest/ + +use bilge::prelude::*; +use bits::bits; +use qemu_api::{impl_vmstate_bitsized, impl_vmstate_forward}; + +/// Offset of each register from the base memory address of the device. +#[doc(alias = "offset")] +#[allow(non_camel_case_types)] +#[repr(u64)] +#[derive(Debug, Eq, PartialEq, qemu_api_macros::TryInto)] +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, +} + +/// Receive Status Register / Data Register common error bits +/// +/// The `UARTRSR` register is updated only when a read occurs +/// from the `UARTDR` register with the same status information +/// that can also be obtained by reading the `UARTDR` register +#[bitsize(8)] +#[derive(Clone, Copy, Default, DebugBits, FromBits)] +pub struct Errors { + pub framing_error: bool, + pub parity_error: bool, + pub break_error: bool, + pub overrun_error: bool, + _reserved_unpredictable: u4, +} + +/// Data Register, `UARTDR` +/// +/// The `UARTDR` register is the data register; write for TX and +/// read for RX. It is a 12-bit register, where bits 7..0 are the +/// character and bits 11..8 are error bits. +#[bitsize(32)] +#[derive(Clone, Copy, Default, DebugBits, FromBits)] +#[doc(alias = "UARTDR")] +pub struct Data { + pub data: u8, + pub errors: Errors, + _reserved: u16, +} +impl_vmstate_bitsized!(Data); + +impl Data { + // bilge is not very const-friendly, unfortunately + pub const BREAK: Self = Self { value: 1 << 10 }; +} + +/// Receive Status Register / Error Clear Register, `UARTRSR/UARTECR` +/// +/// This register provides a different way to read the four receive +/// status error bits that can be found in bits 11..8 of the UARTDR +/// on a read. It gets updated when the guest reads UARTDR, and the +/// status bits correspond to that character that was just read. +/// +/// The TRM confusingly describes this offset as UARTRSR for reads +/// and UARTECR for writes, but really it's a single error status +/// register where writing anything to the register clears the error +/// bits. +#[bitsize(32)] +#[derive(Clone, Copy, DebugBits, FromBits)] +pub struct ReceiveStatusErrorClear { + pub errors: Errors, + _reserved_unpredictable: u24, +} +impl_vmstate_bitsized!(ReceiveStatusErrorClear); + +impl ReceiveStatusErrorClear { + pub fn set_from_data(&mut self, data: Data) { + self.set_errors(data.errors()); + } + + pub fn reset(&mut self) { + // All the bits are cleared to 0 on reset. + *self = Self::default(); + } +} + +impl Default for ReceiveStatusErrorClear { + fn default() -> Self { + 0.into() + } +} + +#[bitsize(32)] +#[derive(Clone, Copy, DebugBits, FromBits)] +/// Flag Register, `UARTFR` +/// +/// This has the usual inbound RS232 modem-control signals, plus flags +/// for RX and TX FIFO fill levels and a BUSY flag. +#[doc(alias = "UARTFR")] +pub struct Flags { + /// CTS: Clear to send + pub clear_to_send: bool, + /// DSR: Data set ready + pub data_set_ready: bool, + /// DCD: Data carrier detect + pub data_carrier_detect: bool, + /// BUSY: UART busy. In real hardware, set while the UART is + /// busy transmitting data. QEMU's implementation never sets BUSY. + pub busy: bool, + /// RXFE: Receive FIFO empty + pub receive_fifo_empty: bool, + /// TXFF: Transmit FIFO full + pub transmit_fifo_full: bool, + /// RXFF: Receive FIFO full + pub receive_fifo_full: bool, + /// TXFE: Transmit FIFO empty + pub transmit_fifo_empty: bool, + /// RI: Ring indicator + pub ring_indicator: bool, + _reserved_zero_no_modify: u23, +} +impl_vmstate_bitsized!(Flags); + +impl Flags { + pub fn reset(&mut self) { + *self = Self::default(); + } +} + +impl Default for Flags { + fn default() -> Self { + let mut ret: Self = 0.into(); + // After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1 + ret.set_receive_fifo_empty(true); + ret.set_transmit_fifo_empty(true); + ret + } +} + +#[bitsize(32)] +#[derive(Clone, Copy, DebugBits, FromBits)] +/// Line Control Register, `UARTLCR_H` +#[doc(alias = "UARTLCR_H")] +pub struct LineControl { + /// BRK: Send break + pub send_break: bool, + /// PEN: Parity enable + pub parity_enabled: bool, + /// EPS: Even parity select + pub parity: Parity, + /// STP2: Two stop bits select + pub two_stops_bits: bool, + /// FEN: Enable FIFOs + pub fifos_enabled: Mode, + /// WLEN: Word length in bits + /// b11 = 8 bits + /// b10 = 7 bits + /// b01 = 6 bits + /// b00 = 5 bits. + pub word_length: WordLength, + /// SPS Stick parity select + pub sticky_parity: bool, + /// 31:8 - Reserved, do not modify, read as zero. + _reserved_zero_no_modify: u24, +} +impl_vmstate_bitsized!(LineControl); + +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 { + Odd = 0, + 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, +} + +#[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. It contains various +/// enable bits, and the bits to write to set the usual outbound RS232 +/// modem control signals. All bits reset to 0 except TXE and RXE. +#[bitsize(32)] +#[doc(alias = "UARTCR")] +#[derive(Clone, Copy, DebugBits, FromBits)] +pub struct Control { + /// `UARTEN` UART enable: 0 = UART is disabled. + pub enable_uart: bool, + /// `SIREN` `SIR` enable: disable or enable IrDA SIR ENDEC. + /// QEMU does not model this. + pub enable_sir: bool, + /// `SIRLP` SIR low-power IrDA mode. QEMU does not model this. + pub sir_lowpower_irda_mode: u1, + /// Reserved, do not modify, read as zero. + _reserved_zero_no_modify: u4, + /// `LBE` Loopback enable: feed UART output back to the input + pub enable_loopback: bool, + /// `TXE` Transmit enable + pub enable_transmit: bool, + /// `RXE` Receive enable + pub enable_receive: bool, + /// `DTR` Data transmit ready + pub data_transmit_ready: bool, + /// `RTS` Request to send + pub request_to_send: bool, + /// `Out1` UART Out1 signal; can be used as DCD + pub out_1: bool, + /// `Out2` UART Out2 signal; can be used as RI + pub out_2: bool, + /// `RTSEn` RTS hardware flow control enable + pub rts_hardware_flow_control_enable: bool, + /// `CTSEn` CTS hardware flow control enable + pub cts_hardware_flow_control_enable: bool, + /// 31:16 - Reserved, do not modify, read as zero. + _reserved_zero_no_modify2: u16, +} +impl_vmstate_bitsized!(Control); + +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 + } +} + +bits! { + /// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC + #[derive(Default)] + pub struct Interrupt(u32) { + 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, + + E = bits!(Self as u32: OE | BE | PE | FE), + MS = bits!(Self as u32: RI | DSR | DCD | CTS), + } +} +impl_vmstate_forward!(Interrupt); diff --git a/rust/hw/meson.build b/rust/hw/meson.build new file mode 100644 index 00000000000..9749d4adfc9 --- /dev/null +++ b/rust/hw/meson.build @@ -0,0 +1,2 @@ +subdir('char') +subdir('timer') diff --git a/rust/hw/timer/Kconfig b/rust/hw/timer/Kconfig new file mode 100644 index 00000000000..afd98033503 --- /dev/null +++ b/rust/hw/timer/Kconfig @@ -0,0 +1,2 @@ +config X_HPET_RUST + bool diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml new file mode 100644 index 00000000000..6f075027843 --- /dev/null +++ b/rust/hw/timer/hpet/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "hpet" +version = "0.1.0" +authors = ["Zhao Liu "] +description = "IA-PC High Precision Event Timer emulation in Rust" + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lib] +crate-type = ["staticlib"] + +[dependencies] +qemu_api = { path = "../../../qemu-api" } +qemu_api_macros = { path = "../../../qemu-api-macros" } + +[lints] +workspace = true diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build new file mode 100644 index 00000000000..c2d7c0532ca --- /dev/null +++ b/rust/hw/timer/hpet/meson.build @@ -0,0 +1,18 @@ +_libhpet_rs = static_library( + 'hpet', + files('src/lib.rs'), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [ + qemu_api, + qemu_api_macros, + ], +) + +rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency( + link_whole: [_libhpet_rs], + # Putting proc macro crates in `dependencies` is necessary for Meson to find + # them when compiling the root per-target static rust lib. + dependencies: [qemu_api_macros], + variables: {'crate': 'hpet'}, +)]) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs new file mode 100644 index 00000000000..acf7251029e --- /dev/null +++ b/rust/hw/timer/hpet/src/device.rs @@ -0,0 +1,1050 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{ + ffi::{c_int, c_void, CStr}, + mem::MaybeUninit, + pin::Pin, + ptr::{addr_of_mut, null_mut, NonNull}, + slice::from_ref, +}; + +use qemu_api::{ + bindings::{ + address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, + qdev_prop_uint32, qdev_prop_usize, + }, + cell::{BqlCell, BqlRefCell}, + irq::InterruptSource, + memory::{ + hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED, + }, + prelude::*, + qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, + qom::{ObjectImpl, ObjectType, ParentField, ParentInit}, + qom_isa, + sysbus::{SysBusDevice, SysBusDeviceImpl}, + timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}, + uninit_field_mut, + vmstate::VMStateDescription, + vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate, + zeroable::Zeroable, +}; + +use crate::fw_cfg::HPETFwConfig; + +/// Register space for each timer block (`HPET_BASE` is defined in hpet.h). +const HPET_REG_SPACE_LEN: u64 = 0x400; // 1024 bytes + +/// Minimum recommended hardware implementation. +const HPET_MIN_TIMERS: usize = 3; +/// Maximum timers in each timer block. +const HPET_MAX_TIMERS: usize = 32; + +/// Flags that HPETState.flags supports. +const HPET_FLAG_MSI_SUPPORT_SHIFT: usize = 0; + +const HPET_NUM_IRQ_ROUTES: usize = 32; +const HPET_LEGACY_PIT_INT: u32 = 0; // HPET_LEGACY_RTC_INT isn't defined here. +const RTC_ISA_IRQ: usize = 8; + +const HPET_CLK_PERIOD: u64 = 10; // 10 ns +const FS_PER_NS: u64 = 1000000; // 1000000 femtoseconds == 1 ns + +/// Revision ID (bits 0:7). Revision 1 is implemented (refer to v1.0a spec). +const HPET_CAP_REV_ID_VALUE: u64 = 0x1; +const HPET_CAP_REV_ID_SHIFT: usize = 0; +/// Number of Timers (bits 8:12) +const HPET_CAP_NUM_TIM_SHIFT: usize = 8; +/// Counter Size (bit 13) +const HPET_CAP_COUNT_SIZE_CAP_SHIFT: usize = 13; +/// Legacy Replacement Route Capable (bit 15) +const HPET_CAP_LEG_RT_CAP_SHIFT: usize = 15; +/// Vendor ID (bits 16:31) +const HPET_CAP_VENDER_ID_VALUE: u64 = 0x8086; +const HPET_CAP_VENDER_ID_SHIFT: usize = 16; +/// Main Counter Tick Period (bits 32:63) +const HPET_CAP_CNT_CLK_PERIOD_SHIFT: usize = 32; + +/// Overall Enable (bit 0) +const HPET_CFG_ENABLE_SHIFT: usize = 0; +/// Legacy Replacement Route (bit 1) +const HPET_CFG_LEG_RT_SHIFT: usize = 1; +/// Other bits are reserved. +const HPET_CFG_WRITE_MASK: u64 = 0x003; + +/// bit 0, 7, and bits 16:31 are reserved. +/// bit 4, 5, 15, and bits 32:64 are read-only. +const HPET_TN_CFG_WRITE_MASK: u64 = 0x7f4e; +/// Timer N Interrupt Type (bit 1) +const HPET_TN_CFG_INT_TYPE_SHIFT: usize = 1; +/// Timer N Interrupt Enable (bit 2) +const HPET_TN_CFG_INT_ENABLE_SHIFT: usize = 2; +/// Timer N Type (Periodic enabled or not, bit 3) +const HPET_TN_CFG_PERIODIC_SHIFT: usize = 3; +/// Timer N Periodic Interrupt Capable (support Periodic or not, bit 4) +const HPET_TN_CFG_PERIODIC_CAP_SHIFT: usize = 4; +/// Timer N Size (timer size is 64-bits or 32 bits, bit 5) +const HPET_TN_CFG_SIZE_CAP_SHIFT: usize = 5; +/// Timer N Value Set (bit 6) +const HPET_TN_CFG_SETVAL_SHIFT: usize = 6; +/// Timer N 32-bit Mode (bit 8) +const HPET_TN_CFG_32BIT_SHIFT: usize = 8; +/// Timer N Interrupt Rout (bits 9:13) +const HPET_TN_CFG_INT_ROUTE_MASK: u64 = 0x3e00; +const HPET_TN_CFG_INT_ROUTE_SHIFT: usize = 9; +/// Timer N FSB Interrupt Enable (bit 14) +const HPET_TN_CFG_FSB_ENABLE_SHIFT: usize = 14; +/// Timer N FSB Interrupt Delivery (bit 15) +const HPET_TN_CFG_FSB_CAP_SHIFT: usize = 15; +/// Timer N Interrupt Routing Capability (bits 32:63) +const HPET_TN_CFG_INT_ROUTE_CAP_SHIFT: usize = 32; + +#[derive(qemu_api_macros::TryInto)] +#[repr(u64)] +#[allow(non_camel_case_types)] +/// Timer registers, masked by 0x18 +enum TimerRegister { + /// Timer N Configuration and Capability Register + CFG = 0, + /// Timer N Comparator Value Register + CMP = 8, + /// Timer N FSB Interrupt Route Register + ROUTE = 16, +} + +#[derive(qemu_api_macros::TryInto)] +#[repr(u64)] +#[allow(non_camel_case_types)] +/// Global registers +enum GlobalRegister { + /// General Capabilities and ID Register + CAP = 0, + /// General Configuration Register + CFG = 0x10, + /// General Interrupt Status Register + INT_STATUS = 0x20, + /// Main Counter Value Register + COUNTER = 0xF0, +} + +enum HPETRegister<'a> { + /// Global register in the range from `0` to `0xff` + Global(GlobalRegister), + + /// Register in the timer block `0x100`...`0x3ff` + Timer(&'a BqlRefCell, TimerRegister), + + /// Invalid address + #[allow(dead_code)] + Unknown(hwaddr), +} + +struct HPETAddrDecode<'a> { + shift: u32, + len: u32, + reg: HPETRegister<'a>, +} + +const fn hpet_next_wrap(cur_tick: u64) -> u64 { + (cur_tick | 0xffffffff) + 1 +} + +const fn hpet_time_after(a: u64, b: u64) -> bool { + ((b - a) as i64) < 0 +} + +const fn ticks_to_ns(value: u64) -> u64 { + value * HPET_CLK_PERIOD +} + +const fn ns_to_ticks(value: u64) -> u64 { + value / HPET_CLK_PERIOD +} + +// Avoid touching the bits that cannot be written. +const fn hpet_fixup_reg(new: u64, old: u64, mask: u64) -> u64 { + (new & mask) | (old & !mask) +} + +const fn activating_bit(old: u64, new: u64, shift: usize) -> bool { + let mask: u64 = 1 << shift; + (old & mask == 0) && (new & mask != 0) +} + +const fn deactivating_bit(old: u64, new: u64, shift: usize) -> bool { + let mask: u64 = 1 << shift; + (old & mask != 0) && (new & mask == 0) +} + +fn timer_handler(timer_cell: &BqlRefCell) { + timer_cell.borrow_mut().callback() +} + +/// HPET Timer Abstraction +#[repr(C)] +#[derive(Debug)] +pub struct HPETTimer { + /// timer N index within the timer block (`HPETState`) + #[doc(alias = "tn")] + index: u8, + qemu_timer: Timer, + /// timer block abstraction containing this timer + state: NonNull, + + // Memory-mapped, software visible timer registers + /// Timer N Configuration and Capability Register + config: u64, + /// Timer N Comparator Value Register + cmp: u64, + /// Timer N FSB Interrupt Route Register + fsb: u64, + + // Hidden register state + /// comparator (extended to counter width) + cmp64: u64, + /// Last value written to comparator + period: u64, + /// timer pop will indicate wrap for one-shot 32-bit + /// mode. Next pop will be actual timer expiration. + wrap_flag: u8, + /// last value armed, to avoid timer storms + last: u64, +} + +impl HPETTimer { + fn new(index: u8, state: *const HPETState) -> HPETTimer { + HPETTimer { + index, + // SAFETY: the HPETTimer will only be used after the timer + // is initialized below. + qemu_timer: unsafe { Timer::new() }, + state: NonNull::new(state.cast_mut()).unwrap(), + config: 0, + cmp: 0, + fsb: 0, + cmp64: 0, + period: 0, + wrap_flag: 0, + last: 0, + } + } + + fn init_timer_with_cell(cell: &BqlRefCell) { + let mut timer = cell.borrow_mut(); + // SAFETY: HPETTimer is only used as part of HPETState, which is + // always pinned. + let qemu_timer = unsafe { Pin::new_unchecked(&mut timer.qemu_timer) }; + qemu_timer.init_full(None, CLOCK_VIRTUAL, Timer::NS, 0, timer_handler, cell); + } + + fn get_state(&self) -> &HPETState { + // SAFETY: + // the pointer is convertible to a reference + unsafe { self.state.as_ref() } + } + + fn is_int_active(&self) -> bool { + self.get_state().is_timer_int_active(self.index.into()) + } + + const fn is_fsb_route_enabled(&self) -> bool { + self.config & (1 << HPET_TN_CFG_FSB_ENABLE_SHIFT) != 0 + } + + const fn is_periodic(&self) -> bool { + self.config & (1 << HPET_TN_CFG_PERIODIC_SHIFT) != 0 + } + + const fn is_int_enabled(&self) -> bool { + self.config & (1 << HPET_TN_CFG_INT_ENABLE_SHIFT) != 0 + } + + const fn is_32bit_mod(&self) -> bool { + self.config & (1 << HPET_TN_CFG_32BIT_SHIFT) != 0 + } + + const fn is_valset_enabled(&self) -> bool { + self.config & (1 << HPET_TN_CFG_SETVAL_SHIFT) != 0 + } + + fn clear_valset(&mut self) { + self.config &= !(1 << HPET_TN_CFG_SETVAL_SHIFT); + } + + /// True if timer interrupt is level triggered; otherwise, edge triggered. + const fn is_int_level_triggered(&self) -> bool { + self.config & (1 << HPET_TN_CFG_INT_TYPE_SHIFT) != 0 + } + + /// calculate next value of the general counter that matches the + /// target (either entirely, or the low 32-bit only depending on + /// the timer mode). + fn calculate_cmp64(&self, cur_tick: u64, target: u64) -> u64 { + if self.is_32bit_mod() { + let mut result: u64 = cur_tick.deposit(0, 32, target); + if result < cur_tick { + result += 0x100000000; + } + result + } else { + target + } + } + + const fn get_individual_route(&self) -> usize { + ((self.config & HPET_TN_CFG_INT_ROUTE_MASK) >> HPET_TN_CFG_INT_ROUTE_SHIFT) as usize + } + + fn get_int_route(&self) -> usize { + if self.index <= 1 && self.get_state().is_legacy_mode() { + // If LegacyReplacement Route bit is set, HPET specification requires + // timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC, + // timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC. + // + // If the LegacyReplacement Route bit is set, the individual routing + // bits for timers 0 and 1 (APIC or FSB) will have no impact. + // + // FIXME: Consider I/O APIC case. + if self.index == 0 { + 0 + } else { + RTC_ISA_IRQ + } + } else { + // (If the LegacyReplacement Route bit is set) Timer 2-n will be + // routed as per the routing in the timer n config registers. + // ... + // If the LegacyReplacement Route bit is not set, the individual + // routing bits for each of the timers are used. + self.get_individual_route() + } + } + + fn set_irq(&mut self, set: bool) { + let route = self.get_int_route(); + + if set && self.is_int_enabled() && self.get_state().is_hpet_enabled() { + if self.is_fsb_route_enabled() { + // SAFETY: + // the parameters are valid. + unsafe { + address_space_stl_le( + addr_of_mut!(address_space_memory), + self.fsb >> 32, // Timer N FSB int addr + self.fsb as u32, // Timer N FSB int value, truncate! + MEMTXATTRS_UNSPECIFIED, + null_mut(), + ); + } + } else if self.is_int_level_triggered() { + self.get_state().irqs[route].raise(); + } else { + self.get_state().irqs[route].pulse(); + } + } else if !self.is_fsb_route_enabled() { + self.get_state().irqs[route].lower(); + } + } + + fn update_irq(&mut self, set: bool) { + // If Timer N Interrupt Enable bit is 0, "the timer will + // still operate and generate appropriate status bits, but + // will not cause an interrupt" + self.get_state() + .update_int_status(self.index.into(), set && self.is_int_level_triggered()); + self.set_irq(set); + } + + fn arm_timer(&mut self, tick: u64) { + let mut ns = self.get_state().get_ns(tick); + + // Clamp period to reasonable min value (1 us) + if self.is_periodic() && ns - self.last < 1000 { + ns = self.last + 1000; + } + + self.last = ns; + self.qemu_timer.modify(self.last); + } + + fn set_timer(&mut self) { + let cur_tick: u64 = self.get_state().get_ticks(); + + self.wrap_flag = 0; + self.cmp64 = self.calculate_cmp64(cur_tick, self.cmp); + if self.is_32bit_mod() { + // HPET spec says in one-shot 32-bit mode, generate an interrupt when + // counter wraps in addition to an interrupt with comparator match. + if !self.is_periodic() && self.cmp64 > hpet_next_wrap(cur_tick) { + self.wrap_flag = 1; + self.arm_timer(hpet_next_wrap(cur_tick)); + return; + } + } + self.arm_timer(self.cmp64); + } + + fn del_timer(&mut self) { + // Just remove the timer from the timer_list without destroying + // this timer instance. + self.qemu_timer.delete(); + + if self.is_int_active() { + // For level-triggered interrupt, this leaves interrupt status + // register set but lowers irq. + self.update_irq(true); + } + } + + /// Configuration and Capability Register + fn set_tn_cfg_reg(&mut self, shift: u32, len: u32, val: u64) { + // TODO: Add trace point - trace_hpet_ram_write_tn_cfg(addr & 4) + let old_val: u64 = self.config; + let mut new_val: u64 = old_val.deposit(shift, len, val); + new_val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); + + // Switch level-type interrupt to edge-type. + if deactivating_bit(old_val, new_val, HPET_TN_CFG_INT_TYPE_SHIFT) { + // Do this before changing timer.config; otherwise, if + // HPET_TN_FSB is set, update_irq will not lower the qemu_irq. + self.update_irq(false); + } + + self.config = new_val; + + if activating_bit(old_val, new_val, HPET_TN_CFG_INT_ENABLE_SHIFT) && self.is_int_active() { + self.update_irq(true); + } + + if self.is_32bit_mod() { + self.cmp = u64::from(self.cmp as u32); // truncate! + self.period = u64::from(self.period as u32); // truncate! + } + + if self.get_state().is_hpet_enabled() { + self.set_timer(); + } + } + + /// Comparator Value Register + fn set_tn_cmp_reg(&mut self, shift: u32, len: u32, val: u64) { + let mut length = len; + let mut value = val; + + // TODO: Add trace point - trace_hpet_ram_write_tn_cmp(addr & 4) + if self.is_32bit_mod() { + // High 32-bits are zero, leave them untouched. + if shift != 0 { + // TODO: Add trace point - trace_hpet_ram_write_invalid_tn_cmp() + return; + } + length = 64; + value = u64::from(value as u32); // truncate! + } + + if !self.is_periodic() || self.is_valset_enabled() { + self.cmp = self.cmp.deposit(shift, length, value); + } + + if self.is_periodic() { + self.period = self.period.deposit(shift, length, value); + } + + self.clear_valset(); + if self.get_state().is_hpet_enabled() { + self.set_timer(); + } + } + + /// FSB Interrupt Route Register + fn set_tn_fsb_route_reg(&mut self, shift: u32, len: u32, val: u64) { + self.fsb = self.fsb.deposit(shift, len, val); + } + + fn reset(&mut self) { + self.del_timer(); + self.cmp = u64::MAX; // Comparator Match Registers reset to all 1's. + self.config = (1 << HPET_TN_CFG_PERIODIC_CAP_SHIFT) | (1 << HPET_TN_CFG_SIZE_CAP_SHIFT); + if self.get_state().has_msi_flag() { + self.config |= 1 << HPET_TN_CFG_FSB_CAP_SHIFT; + } + // advertise availability of ioapic int + self.config |= + (u64::from(self.get_state().int_route_cap)) << HPET_TN_CFG_INT_ROUTE_CAP_SHIFT; + self.period = 0; + self.wrap_flag = 0; + } + + /// timer expiration callback + fn callback(&mut self) { + let period: u64 = self.period; + let cur_tick: u64 = self.get_state().get_ticks(); + + if self.is_periodic() && period != 0 { + while hpet_time_after(cur_tick, self.cmp64) { + self.cmp64 += period; + } + if self.is_32bit_mod() { + self.cmp = u64::from(self.cmp64 as u32); // truncate! + } else { + self.cmp = self.cmp64; + } + self.arm_timer(self.cmp64); + } else if self.wrap_flag != 0 { + self.wrap_flag = 0; + self.arm_timer(self.cmp64); + } + self.update_irq(true); + } + + const fn read(&self, reg: TimerRegister) -> u64 { + use TimerRegister::*; + match reg { + CFG => self.config, // including interrupt capabilities + CMP => self.cmp, // comparator register + ROUTE => self.fsb, + } + } + + fn write(&mut self, reg: TimerRegister, value: u64, shift: u32, len: u32) { + use TimerRegister::*; + match reg { + CFG => self.set_tn_cfg_reg(shift, len, value), + CMP => self.set_tn_cmp_reg(shift, len, value), + ROUTE => self.set_tn_fsb_route_reg(shift, len, value), + } + } +} + +/// HPET Event Timer Block Abstraction +#[repr(C)] +#[derive(qemu_api_macros::Object)] +pub struct HPETState { + parent_obj: ParentField, + iomem: MemoryRegion, + + // HPET block Registers: Memory-mapped, software visible registers + /// General Capabilities and ID Register + capability: BqlCell, + /// General Configuration Register + config: BqlCell, + /// General Interrupt Status Register + #[doc(alias = "isr")] + int_status: BqlCell, + /// Main Counter Value Register + #[doc(alias = "hpet_counter")] + counter: BqlCell, + + // Internal state + /// Capabilities that QEMU HPET supports. + /// bit 0: MSI (or FSB) support. + flags: u32, + + /// Offset of main counter relative to qemu clock. + hpet_offset: BqlCell, + hpet_offset_saved: bool, + + irqs: [InterruptSource; HPET_NUM_IRQ_ROUTES], + rtc_irq_level: BqlCell, + pit_enabled: InterruptSource, + + /// Interrupt Routing Capability. + /// This field indicates to which interrupts in the I/O (x) APIC + /// the timers' interrupt can be routed, and is encoded in the + /// bits 32:64 of timer N's config register: + #[doc(alias = "intcap")] + int_route_cap: u32, + + /// HPET timer array managed by this timer block. + #[doc(alias = "timer")] + timers: [BqlRefCell; HPET_MAX_TIMERS], + num_timers: usize, + num_timers_save: BqlCell, + + /// Instance id (HPET timer block ID). + hpet_id: BqlCell, +} + +impl HPETState { + const fn has_msi_flag(&self) -> bool { + self.flags & (1 << HPET_FLAG_MSI_SUPPORT_SHIFT) != 0 + } + + fn is_legacy_mode(&self) -> bool { + self.config.get() & (1 << HPET_CFG_LEG_RT_SHIFT) != 0 + } + + fn is_hpet_enabled(&self) -> bool { + self.config.get() & (1 << HPET_CFG_ENABLE_SHIFT) != 0 + } + + fn is_timer_int_active(&self, index: usize) -> bool { + self.int_status.get() & (1 << index) != 0 + } + + fn get_ticks(&self) -> u64 { + ns_to_ticks(CLOCK_VIRTUAL.get_ns() + self.hpet_offset.get()) + } + + fn get_ns(&self, tick: u64) -> u64 { + ticks_to_ns(tick) - self.hpet_offset.get() + } + + fn handle_legacy_irq(&self, irq: u32, level: u32) { + if irq == HPET_LEGACY_PIT_INT { + if !self.is_legacy_mode() { + self.irqs[0].set(level != 0); + } + } else { + self.rtc_irq_level.set(level); + if !self.is_legacy_mode() { + self.irqs[RTC_ISA_IRQ].set(level != 0); + } + } + } + + fn init_timers(this: &mut MaybeUninit) { + let state = this.as_ptr(); + for index in 0..HPET_MAX_TIMERS { + let mut timer = uninit_field_mut!(*this, timers[index]); + + // Initialize in two steps, to avoid calling Timer::init_full on a + // temporary that can be moved. + let timer = timer.write(BqlRefCell::new(HPETTimer::new( + index.try_into().unwrap(), + state, + ))); + HPETTimer::init_timer_with_cell(timer); + } + } + + fn update_int_status(&self, index: u32, level: bool) { + self.int_status + .set(self.int_status.get().deposit(index, 1, u64::from(level))); + } + + /// General Configuration Register + fn set_cfg_reg(&self, shift: u32, len: u32, val: u64) { + let old_val = self.config.get(); + let mut new_val = old_val.deposit(shift, len, val); + + new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); + self.config.set(new_val); + + if activating_bit(old_val, new_val, HPET_CFG_ENABLE_SHIFT) { + // Enable main counter and interrupt generation. + self.hpet_offset + .set(ticks_to_ns(self.counter.get()) - CLOCK_VIRTUAL.get_ns()); + + for timer in self.timers.iter().take(self.num_timers) { + let mut t = timer.borrow_mut(); + + if t.is_int_enabled() && t.is_int_active() { + t.update_irq(true); + } + t.set_timer(); + } + } else if deactivating_bit(old_val, new_val, HPET_CFG_ENABLE_SHIFT) { + // Halt main counter and disable interrupt generation. + self.counter.set(self.get_ticks()); + + for timer in self.timers.iter().take(self.num_timers) { + timer.borrow_mut().del_timer(); + } + } + + // i8254 and RTC output pins are disabled when HPET is in legacy mode + if activating_bit(old_val, new_val, HPET_CFG_LEG_RT_SHIFT) { + self.pit_enabled.set(false); + self.irqs[0].lower(); + self.irqs[RTC_ISA_IRQ].lower(); + } else if deactivating_bit(old_val, new_val, HPET_CFG_LEG_RT_SHIFT) { + // TODO: Add irq binding: qemu_irq_lower(s->irqs[0]) + self.irqs[0].lower(); + self.pit_enabled.set(true); + self.irqs[RTC_ISA_IRQ].set(self.rtc_irq_level.get() != 0); + } + } + + /// General Interrupt Status Register: Read/Write Clear + fn set_int_status_reg(&self, shift: u32, _len: u32, val: u64) { + let new_val = val << shift; + let cleared = new_val & self.int_status.get(); + + for (index, timer) in self.timers.iter().take(self.num_timers).enumerate() { + if cleared & (1 << index) != 0 { + timer.borrow_mut().update_irq(false); + } + } + } + + /// Main Counter Value Register + fn set_counter_reg(&self, shift: u32, len: u32, val: u64) { + if self.is_hpet_enabled() { + // TODO: Add trace point - + // trace_hpet_ram_write_counter_write_while_enabled() + // + // HPET spec says that writes to this register should only be + // done while the counter is halted. So this is an undefined + // behavior. There's no need to forbid it, but when HPET is + // enabled, the changed counter value will not affect the + // tick count (i.e., the previously calculated offset will + // not be changed as well). + } + self.counter + .set(self.counter.get().deposit(shift, len, val)); + } + + unsafe fn init(mut this: ParentInit) { + static HPET_RAM_OPS: MemoryRegionOps = + MemoryRegionOpsBuilder::::new() + .read(&HPETState::read) + .write(&HPETState::write) + .native_endian() + .valid_sizes(4, 8) + .impl_sizes(4, 8) + .build(); + + MemoryRegion::init_io( + &mut uninit_field_mut!(*this, iomem), + &HPET_RAM_OPS, + "hpet", + HPET_REG_SPACE_LEN, + ); + + Self::init_timers(&mut this); + } + + fn post_init(&self) { + self.init_mmio(&self.iomem); + for irq in self.irqs.iter() { + self.init_irq(irq); + } + } + + fn realize(&self) -> qemu_api::Result<()> { + if self.num_timers < HPET_MIN_TIMERS || self.num_timers > HPET_MAX_TIMERS { + Err(format!( + "hpet.num_timers must be between {HPET_MIN_TIMERS} and {HPET_MAX_TIMERS}" + ))?; + } + if self.int_route_cap == 0 { + Err("hpet.hpet-intcap property not initialized")?; + } + + self.hpet_id.set(HPETFwConfig::assign_hpet_id()?); + + // 64-bit General Capabilities and ID Register; LegacyReplacementRoute. + self.capability.set( + HPET_CAP_REV_ID_VALUE << HPET_CAP_REV_ID_SHIFT | + 1 << HPET_CAP_COUNT_SIZE_CAP_SHIFT | + 1 << HPET_CAP_LEG_RT_CAP_SHIFT | + HPET_CAP_VENDER_ID_VALUE << HPET_CAP_VENDER_ID_SHIFT | + ((self.num_timers - 1) as u64) << HPET_CAP_NUM_TIM_SHIFT | // indicate the last timer + (HPET_CLK_PERIOD * FS_PER_NS) << HPET_CAP_CNT_CLK_PERIOD_SHIFT, // 10 ns + ); + + self.init_gpio_in(2, HPETState::handle_legacy_irq); + self.init_gpio_out(from_ref(&self.pit_enabled)); + Ok(()) + } + + fn reset_hold(&self, _type: ResetType) { + for timer in self.timers.iter().take(self.num_timers) { + timer.borrow_mut().reset(); + } + + self.counter.set(0); + self.config.set(0); + self.pit_enabled.set(true); + self.hpet_offset.set(0); + + HPETFwConfig::update_hpet_cfg( + self.hpet_id.get(), + self.capability.get() as u32, + self.mmio_addr(0).unwrap(), + ); + + // to document that the RTC lowers its output on reset as well + self.rtc_irq_level.set(0); + } + + fn decode(&self, mut addr: hwaddr, size: u32) -> HPETAddrDecode<'_> { + let shift = ((addr & 4) * 8) as u32; + let len = std::cmp::min(size * 8, 64 - shift); + + addr &= !4; + let reg = if (0..=0xff).contains(&addr) { + GlobalRegister::try_from(addr).map(HPETRegister::Global) + } else { + let timer_id: usize = ((addr - 0x100) / 0x20) as usize; + if timer_id < self.num_timers { + // TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id) + TimerRegister::try_from(addr & 0x18) + .map(|reg| HPETRegister::Timer(&self.timers[timer_id], reg)) + } else { + // TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id) + Err(addr) + } + }; + + // reg is now a Result + // convert the Err case into HPETRegister as well + let reg = reg.unwrap_or_else(HPETRegister::Unknown); + HPETAddrDecode { shift, len, reg } + } + + fn read(&self, addr: hwaddr, size: u32) -> u64 { + // TODO: Add trace point - trace_hpet_ram_read(addr) + let HPETAddrDecode { shift, reg, .. } = self.decode(addr, size); + + use GlobalRegister::*; + use HPETRegister::*; + (match reg { + Timer(timer, tn_reg) => timer.borrow_mut().read(tn_reg), + Global(CAP) => self.capability.get(), /* including HPET_PERIOD 0x004 */ + Global(CFG) => self.config.get(), + Global(INT_STATUS) => self.int_status.get(), + Global(COUNTER) => { + // TODO: Add trace point + // trace_hpet_ram_read_reading_counter(addr & 4, cur_tick) + if self.is_hpet_enabled() { + self.get_ticks() + } else { + self.counter.get() + } + } + Unknown(_) => { + // TODO: Add trace point- trace_hpet_ram_read_invalid() + 0 + } + }) >> shift + } + + fn write(&self, addr: hwaddr, value: u64, size: u32) { + let HPETAddrDecode { shift, len, reg } = self.decode(addr, size); + + // TODO: Add trace point - trace_hpet_ram_write(addr, value) + use GlobalRegister::*; + use HPETRegister::*; + match reg { + Timer(timer, tn_reg) => timer.borrow_mut().write(tn_reg, value, shift, len), + Global(CAP) => {} // General Capabilities and ID Register: Read Only + Global(CFG) => self.set_cfg_reg(shift, len, value), + Global(INT_STATUS) => self.set_int_status_reg(shift, len, value), + Global(COUNTER) => self.set_counter_reg(shift, len, value), + Unknown(_) => { + // TODO: Add trace point - trace_hpet_ram_write_invalid() + } + } + } + + fn pre_save(&self) -> i32 { + if self.is_hpet_enabled() { + self.counter.set(self.get_ticks()); + } + + /* + * The number of timers must match on source and destination, but it was + * also added to the migration stream. Check that it matches the value + * that was configured. + */ + self.num_timers_save.set(self.num_timers as u8); + 0 + } + + fn post_load(&self, _version_id: u8) -> i32 { + for timer in self.timers.iter().take(self.num_timers) { + let mut t = timer.borrow_mut(); + + t.cmp64 = t.calculate_cmp64(t.get_state().counter.get(), t.cmp); + t.last = CLOCK_VIRTUAL.get_ns() - NANOSECONDS_PER_SECOND; + } + + // Recalculate the offset between the main counter and guest time + if !self.hpet_offset_saved { + self.hpet_offset + .set(ticks_to_ns(self.counter.get()) - CLOCK_VIRTUAL.get_ns()); + } + + 0 + } + + fn is_rtc_irq_level_needed(&self) -> bool { + self.rtc_irq_level.get() != 0 + } + + fn is_offset_needed(&self) -> bool { + self.is_hpet_enabled() && self.hpet_offset_saved + } + + fn validate_num_timers(&self, _version_id: u8) -> bool { + self.num_timers == self.num_timers_save.get().into() + } +} + +qom_isa!(HPETState: SysBusDevice, DeviceState, Object); + +unsafe impl ObjectType for HPETState { + // No need for HPETClass. Just like OBJECT_DECLARE_SIMPLE_TYPE in C. + type Class = ::Class; + const TYPE_NAME: &'static CStr = crate::TYPE_HPET; +} + +impl ObjectImpl for HPETState { + type ParentType = SysBusDevice; + + const INSTANCE_INIT: Option)> = Some(Self::init); + const INSTANCE_POST_INIT: Option = Some(Self::post_init); + const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::; +} + +// TODO: Make these properties user-configurable! +qemu_api::declare_properties! { + HPET_PROPERTIES, + qemu_api::define_property!( + c"timers", + HPETState, + num_timers, + unsafe { &qdev_prop_usize }, + u8, + default = HPET_MIN_TIMERS + ), + qemu_api::define_property!( + c"msi", + HPETState, + flags, + unsafe { &qdev_prop_bit }, + u32, + bit = HPET_FLAG_MSI_SUPPORT_SHIFT as u8, + default = false, + ), + qemu_api::define_property!( + c"hpet-intcap", + HPETState, + int_route_cap, + unsafe { &qdev_prop_uint32 }, + u32, + default = 0 + ), + qemu_api::define_property!( + c"hpet-offset-saved", + HPETState, + hpet_offset_saved, + unsafe { &qdev_prop_bool }, + bool, + default = true + ), +} + +unsafe extern "C" fn hpet_rtc_irq_level_needed(opaque: *mut c_void) -> bool { + // SAFETY: + // the pointer is convertible to a reference + let state: &HPETState = unsafe { NonNull::new(opaque.cast::()).unwrap().as_ref() }; + state.is_rtc_irq_level_needed() +} + +unsafe extern "C" fn hpet_offset_needed(opaque: *mut c_void) -> bool { + // SAFETY: + // the pointer is convertible to a reference + let state: &HPETState = unsafe { NonNull::new(opaque.cast::()).unwrap().as_ref() }; + state.is_offset_needed() +} + +unsafe extern "C" fn hpet_pre_save(opaque: *mut c_void) -> c_int { + // SAFETY: + // the pointer is convertible to a reference + let state: &mut HPETState = + unsafe { NonNull::new(opaque.cast::()).unwrap().as_mut() }; + state.pre_save() as c_int +} + +unsafe extern "C" fn hpet_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { + // SAFETY: + // the pointer is convertible to a reference + let state: &mut HPETState = + unsafe { NonNull::new(opaque.cast::()).unwrap().as_mut() }; + let version: u8 = version_id.try_into().unwrap(); + state.post_load(version) as c_int +} + +static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription = VMStateDescription { + name: c"hpet/rtc_irq_level".as_ptr(), + version_id: 1, + minimum_version_id: 1, + needed: Some(hpet_rtc_irq_level_needed), + fields: vmstate_fields! { + vmstate_of!(HPETState, rtc_irq_level), + }, + ..Zeroable::ZERO +}; + +static VMSTATE_HPET_OFFSET: VMStateDescription = VMStateDescription { + name: c"hpet/offset".as_ptr(), + version_id: 1, + minimum_version_id: 1, + needed: Some(hpet_offset_needed), + fields: vmstate_fields! { + vmstate_of!(HPETState, hpet_offset), + }, + ..Zeroable::ZERO +}; + +static VMSTATE_HPET_TIMER: VMStateDescription = VMStateDescription { + name: c"hpet_timer".as_ptr(), + version_id: 1, + minimum_version_id: 1, + fields: vmstate_fields! { + vmstate_of!(HPETTimer, index), + vmstate_of!(HPETTimer, config), + vmstate_of!(HPETTimer, cmp), + vmstate_of!(HPETTimer, fsb), + vmstate_of!(HPETTimer, period), + vmstate_of!(HPETTimer, wrap_flag), + vmstate_of!(HPETTimer, qemu_timer), + }, + ..Zeroable::ZERO +}; + +const VALIDATE_TIMERS_NAME: &CStr = c"num_timers must match"; + +static VMSTATE_HPET: VMStateDescription = VMStateDescription { + name: c"hpet".as_ptr(), + version_id: 2, + minimum_version_id: 2, + pre_save: Some(hpet_pre_save), + post_load: Some(hpet_post_load), + fields: vmstate_fields! { + vmstate_of!(HPETState, config), + vmstate_of!(HPETState, int_status), + vmstate_of!(HPETState, counter), + vmstate_of!(HPETState, num_timers_save), + vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers), + vmstate_struct!(HPETState, timers[0 .. num_timers_save], &VMSTATE_HPET_TIMER, BqlRefCell, HPETState::validate_num_timers).with_version_id(0), + }, + subsections: vmstate_subsections! { + VMSTATE_HPET_RTC_IRQ_LEVEL, + VMSTATE_HPET_OFFSET, + }, + ..Zeroable::ZERO +}; + +impl DeviceImpl for HPETState { + fn properties() -> &'static [Property] { + &HPET_PROPERTIES + } + + fn vmsd() -> Option<&'static VMStateDescription> { + Some(&VMSTATE_HPET) + } + + const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); +} + +impl ResettablePhasesImpl for HPETState { + const HOLD: Option = Some(Self::reset_hold); +} + +impl SysBusDeviceImpl for HPETState {} diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs new file mode 100644 index 00000000000..619d662ee1e --- /dev/null +++ b/rust/hw/timer/hpet/src/fw_cfg.rs @@ -0,0 +1,68 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::ptr::addr_of_mut; + +use qemu_api::{cell::bql_locked, zeroable::Zeroable}; + +/// Each `HPETState` represents a Event Timer Block. The v1 spec supports +/// up to 8 blocks. QEMU only uses 1 block (in PC machine). +const HPET_MAX_NUM_EVENT_TIMER_BLOCK: usize = 8; + +#[repr(C, packed)] +#[derive(Copy, Clone, Default)] +pub struct HPETFwEntry { + pub event_timer_block_id: u32, + pub address: u64, + pub min_tick: u16, + pub page_prot: u8, +} +unsafe impl Zeroable for HPETFwEntry {} + +#[repr(C, packed)] +#[derive(Copy, Clone, Default)] +pub struct HPETFwConfig { + pub count: u8, + pub hpet: [HPETFwEntry; HPET_MAX_NUM_EVENT_TIMER_BLOCK], +} +unsafe impl Zeroable for HPETFwConfig {} + +#[allow(non_upper_case_globals)] +#[no_mangle] +pub static mut hpet_fw_cfg: HPETFwConfig = HPETFwConfig { + count: u8::MAX, + ..Zeroable::ZERO +}; + +impl HPETFwConfig { + pub(crate) fn assign_hpet_id() -> Result { + assert!(bql_locked()); + // SAFETY: all accesses go through these methods, which guarantee + // that the accesses are protected by the BQL. + let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) }; + + if fw_cfg.count == u8::MAX { + // first instance + fw_cfg.count = 0; + } + + if fw_cfg.count == 8 { + Err("Only 8 instances of HPET are allowed")?; + } + + let id: usize = fw_cfg.count.into(); + fw_cfg.count += 1; + Ok(id) + } + + pub(crate) fn update_hpet_cfg(hpet_id: usize, timer_block_id: u32, address: u64) { + assert!(bql_locked()); + // SAFETY: all accesses go through these methods, which guarantee + // that the accesses are protected by the BQL. + let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) }; + + fw_cfg.hpet[hpet_id].event_timer_block_id = timer_block_id; + fw_cfg.hpet[hpet_id].address = address; + } +} diff --git a/rust/hw/timer/hpet/src/lib.rs b/rust/hw/timer/hpet/src/lib.rs new file mode 100644 index 00000000000..a95cf14ac98 --- /dev/null +++ b/rust/hw/timer/hpet/src/lib.rs @@ -0,0 +1,13 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +//! # HPET QEMU Device Model +//! +//! This library implements a device model for the IA-PC HPET (High +//! Precision Event Timers) device in QEMU. + +pub mod device; +pub mod fw_cfg; + +pub const TYPE_HPET: &::std::ffi::CStr = c"hpet"; diff --git a/rust/hw/timer/meson.build b/rust/hw/timer/meson.build new file mode 100644 index 00000000000..22a84f15536 --- /dev/null +++ b/rust/hw/timer/meson.build @@ -0,0 +1 @@ +subdir('hpet') diff --git a/rust/meson.build b/rust/meson.build new file mode 100644 index 00000000000..331f11b7e72 --- /dev/null +++ b/rust/meson.build @@ -0,0 +1,39 @@ +subproject('anyhow-1-rs', required: true) +subproject('bilge-0.2-rs', required: true) +subproject('bilge-impl-0.2-rs', required: true) +subproject('foreign-0.3-rs', required: true) +subproject('libc-0.2-rs', required: true) + +anyhow_rs = dependency('anyhow-1-rs') +bilge_rs = dependency('bilge-0.2-rs') +bilge_impl_rs = dependency('bilge-impl-0.2-rs') +foreign_rs = dependency('foreign-0.3-rs') +libc_rs = dependency('libc-0.2-rs') + +subproject('proc-macro2-1-rs', required: true) +subproject('quote-1-rs', required: true) +subproject('syn-2-rs', required: true) + +quote_rs_native = dependency('quote-1-rs', native: true) +syn_rs_native = dependency('syn-2-rs', native: true) +proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true) + +qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true) + +genrs = [] + +subdir('qemu-api-macros') +subdir('bits') +subdir('qemu-api') + +subdir('hw') + +cargo = find_program('cargo', required: false) + +if cargo.found() + run_target('rustfmt', + command: [config_host['MESON'], 'devenv', + '--workdir', '@CURRENT_SOURCE_DIR@', + cargo, 'fmt'], + depends: genrs) +endif diff --git a/rust/qemu-api-macros/Cargo.toml b/rust/qemu-api-macros/Cargo.toml new file mode 100644 index 00000000000..0cd40c8e168 --- /dev/null +++ b/rust/qemu-api-macros/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "qemu_api_macros" +version = "0.1.0" +authors = ["Manos Pitsidianakis "] +description = "Rust bindings for QEMU - Utility macros" +resolver = "2" +publish = false + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1" +quote = "1" +syn = { version = "2", features = ["extra-traits"] } + +[lints] +workspace = true diff --git a/rust/qemu-api-macros/meson.build b/rust/qemu-api-macros/meson.build new file mode 100644 index 00000000000..8610ce1c844 --- /dev/null +++ b/rust/qemu-api-macros/meson.build @@ -0,0 +1,19 @@ +_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_rs_native, + quote_rs_native, + syn_rs_native, + ], +) + +qemu_api_macros = declare_dependency( + link_with: _qemu_api_macros_rs, +) diff --git a/rust/qemu-api-macros/src/bits.rs b/rust/qemu-api-macros/src/bits.rs new file mode 100644 index 00000000000..5ba84757ee0 --- /dev/null +++ b/rust/qemu-api-macros/src/bits.rs @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: MIT or Apache-2.0 or GPL-2.0-or-later + +// shadowing is useful together with "if let" +#![allow(clippy::shadow_unrelated)] + +use proc_macro2::{ + Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree, TokenTree as TT, +}; + +use crate::utils::MacroError; + +pub struct BitsConstInternal { + typ: TokenTree, +} + +fn paren(ts: TokenStream) -> TokenTree { + TT::Group(Group::new(Delimiter::Parenthesis, ts)) +} + +fn ident(s: &'static str) -> TokenTree { + TT::Ident(Ident::new(s, Span::call_site())) +} + +fn punct(ch: char) -> TokenTree { + TT::Punct(Punct::new(ch, Spacing::Alone)) +} + +/// Implements a recursive-descent parser that translates Boolean expressions on +/// bitmasks to invocations of `const` functions defined by the `bits!` macro. +impl BitsConstInternal { + // primary ::= '(' or ')' + // | ident + // | '!' ident + fn parse_primary( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ) -> Result, MacroError> { + let next = match tok { + TT::Group(ref g) => { + if g.delimiter() != Delimiter::Parenthesis && g.delimiter() != Delimiter::None { + return Err(MacroError::Message("expected parenthesis".into(), g.span())); + } + let mut stream = g.stream().into_iter(); + let Some(first_tok) = stream.next() else { + return Err(MacroError::Message( + "expected operand, found ')'".into(), + g.span(), + )); + }; + let mut output = TokenStream::new(); + // start from the lowest precedence + let next = self.parse_or(first_tok, &mut stream, &mut output)?; + if let Some(tok) = next { + return Err(MacroError::Message( + format!("unexpected token {tok}"), + tok.span(), + )); + } + out.extend(Some(paren(output))); + it.next() + } + TT::Ident(_) => { + let mut output = TokenStream::new(); + output.extend([ + self.typ.clone(), + TT::Punct(Punct::new(':', Spacing::Joint)), + TT::Punct(Punct::new(':', Spacing::Joint)), + tok, + ]); + out.extend(Some(paren(output))); + it.next() + } + TT::Punct(ref p) => { + if p.as_char() != '!' { + return Err(MacroError::Message("expected operand".into(), p.span())); + } + let Some(rhs_tok) = it.next() else { + return Err(MacroError::Message( + "expected operand at end of input".into(), + p.span(), + )); + }; + let next = self.parse_primary(rhs_tok, it, out)?; + out.extend([punct('.'), ident("invert"), paren(TokenStream::new())]); + next + } + _ => { + return Err(MacroError::Message("unexpected literal".into(), tok.span())); + } + }; + Ok(next) + } + + fn parse_binop< + F: Fn( + &Self, + TokenTree, + &mut dyn Iterator, + &mut TokenStream, + ) -> Result, MacroError>, + >( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ch: char, + f: F, + method: &'static str, + ) -> Result, MacroError> { + let mut next = f(self, tok, it, out)?; + while next.is_some() { + let op = next.as_ref().unwrap(); + let TT::Punct(ref p) = op else { break }; + if p.as_char() != ch { + break; + } + + let Some(rhs_tok) = it.next() else { + return Err(MacroError::Message( + "expected operand at end of input".into(), + p.span(), + )); + }; + let mut rhs = TokenStream::new(); + next = f(self, rhs_tok, it, &mut rhs)?; + out.extend([punct('.'), ident(method), paren(rhs)]); + } + Ok(next) + } + + // sub ::= primary ('-' primary)* + pub fn parse_sub( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ) -> Result, MacroError> { + self.parse_binop(tok, it, out, '-', Self::parse_primary, "difference") + } + + // and ::= sub ('&' sub)* + fn parse_and( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ) -> Result, MacroError> { + self.parse_binop(tok, it, out, '&', Self::parse_sub, "intersection") + } + + // xor ::= and ('&' and)* + fn parse_xor( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ) -> Result, MacroError> { + self.parse_binop(tok, it, out, '^', Self::parse_and, "symmetric_difference") + } + + // or ::= xor ('|' xor)* + pub fn parse_or( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ) -> Result, MacroError> { + self.parse_binop(tok, it, out, '|', Self::parse_xor, "union") + } + + pub fn parse( + it: &mut dyn Iterator, + ) -> Result { + let mut pos = Span::call_site(); + let mut typ = proc_macro2::TokenStream::new(); + + // Gobble everything up to an `@` sign, which is followed by a + // parenthesized expression; that is, all token trees except the + // last two form the type. + let next = loop { + let tok = it.next(); + if let Some(ref t) = tok { + pos = t.span(); + } + match tok { + None => break None, + Some(TT::Punct(ref p)) if p.as_char() == '@' => { + let tok = it.next(); + if let Some(ref t) = tok { + pos = t.span(); + } + break tok; + } + Some(x) => typ.extend(Some(x)), + } + }; + + let Some(tok) = next else { + return Err(MacroError::Message( + "expected expression, do not call this macro directly".into(), + pos, + )); + }; + let TT::Group(ref _group) = tok else { + return Err(MacroError::Message( + "expected parenthesis, do not call this macro directly".into(), + tok.span(), + )); + }; + let mut out = TokenStream::new(); + let state = Self { + typ: TT::Group(Group::new(Delimiter::None, typ)), + }; + + let next = state.parse_primary(tok, it, &mut out)?; + + // A parenthesized expression is a single production of the grammar, + // so the input must have reached the last token. + if let Some(tok) = next { + return Err(MacroError::Message( + format!("unexpected token {tok}"), + tok.span(), + )); + } + Ok(out) + } +} diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs new file mode 100644 index 00000000000..c18bb4e036f --- /dev/null +++ b/rust/qemu-api-macros/src/lib.rs @@ -0,0 +1,262 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use proc_macro::TokenStream; +use quote::quote; +use syn::{ + parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data, + DeriveInput, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Variant, +}; + +mod utils; +use utils::MacroError; + +mod bits; +use bits::BitsConstInternal; + +fn get_fields<'a>( + input: &'a DeriveInput, + msg: &str, +) -> Result<&'a Punctuated, MacroError> { + let Data::Struct(ref s) = &input.data else { + return Err(MacroError::Message( + format!("Struct required for {msg}"), + input.ident.span(), + )); + }; + let Fields::Named(ref fs) = &s.fields else { + return Err(MacroError::Message( + format!("Named fields required for {msg}"), + input.ident.span(), + )); + }; + Ok(&fs.named) +} + +fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, MacroError> { + let Data::Struct(ref s) = &input.data else { + return Err(MacroError::Message( + format!("Struct required for {msg}"), + input.ident.span(), + )); + }; + let Fields::Unnamed(FieldsUnnamed { ref unnamed, .. }) = &s.fields else { + return Err(MacroError::Message( + format!("Tuple struct required for {msg}"), + s.fields.span(), + )); + }; + if unnamed.len() != 1 { + return Err(MacroError::Message( + format!("A single field is required for {msg}"), + s.fields.span(), + )); + } + Ok(&unnamed[0]) +} + +fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> { + let expected = parse_quote! { #[repr(C)] }; + + if input.attrs.iter().any(|attr| attr == &expected) { + Ok(()) + } else { + Err(MacroError::Message( + format!("#[repr(C)] required for {msg}"), + input.ident.span(), + )) + } +} + +fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> { + let expected = parse_quote! { #[repr(transparent)] }; + + if input.attrs.iter().any(|attr| attr == &expected) { + Ok(()) + } else { + Err(MacroError::Message( + format!("#[repr(transparent)] required for {msg}"), + input.ident.span(), + )) + } +} + +fn derive_object_or_error(input: DeriveInput) -> Result { + is_c_repr(&input, "#[derive(Object)]")?; + + let name = &input.ident; + let parent = &get_fields(&input, "#[derive(Object)]")?[0].ident; + + Ok(quote! { + ::qemu_api::assert_field_type!(#name, #parent, + ::qemu_api::qom::ParentField<<#name as ::qemu_api::qom::ObjectImpl>::ParentType>); + + ::qemu_api::module_init! { + MODULE_INIT_QOM => unsafe { + ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO); + } + } + }) +} + +#[proc_macro_derive(Object)] +pub fn derive_object(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let expanded = derive_object_or_error(input).unwrap_or_else(Into::into); + + TokenStream::from(expanded) +} + +fn derive_opaque_or_error(input: DeriveInput) -> Result { + is_transparent_repr(&input, "#[derive(Wrapper)]")?; + + let name = &input.ident; + let field = &get_unnamed_field(&input, "#[derive(Wrapper)]")?; + let typ = &field.ty; + + // TODO: how to add "::qemu_api"? For now, this is only used in the + // qemu_api crate so it's not a problem. + Ok(quote! { + unsafe impl crate::cell::Wrapper for #name { + type Wrapped = <#typ as crate::cell::Wrapper>::Wrapped; + } + impl #name { + pub unsafe fn from_raw<'a>(ptr: *mut ::Wrapped) -> &'a Self { + let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::(); + unsafe { ptr.as_ref() } + } + + pub const fn as_mut_ptr(&self) -> *mut ::Wrapped { + self.0.as_mut_ptr() + } + + pub const fn as_ptr(&self) -> *const ::Wrapped { + self.0.as_ptr() + } + + pub const fn as_void_ptr(&self) -> *mut ::core::ffi::c_void { + self.0.as_void_ptr() + } + + pub const fn raw_get(slot: *mut Self) -> *mut ::Wrapped { + slot.cast() + } + } + }) +} + +#[proc_macro_derive(Wrapper)] +pub fn derive_opaque(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let expanded = derive_opaque_or_error(input).unwrap_or_else(Into::into); + + TokenStream::from(expanded) +} + +#[allow(non_snake_case)] +fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result { + let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr")); + if let Some(repr) = repr { + let nested = repr.parse_args_with(Punctuated::::parse_terminated)?; + for meta in nested { + match meta { + Meta::Path(path) if path.is_ident("u8") => return Ok(path), + Meta::Path(path) if path.is_ident("u16") => return Ok(path), + Meta::Path(path) if path.is_ident("u32") => return Ok(path), + Meta::Path(path) if path.is_ident("u64") => return Ok(path), + _ => {} + } + } + } + + Err(MacroError::Message( + format!("#[repr(u8/u16/u32/u64) required for {msg}"), + input.ident.span(), + )) +} + +fn get_variants(input: &DeriveInput) -> Result<&Punctuated, MacroError> { + let Data::Enum(ref e) = &input.data else { + return Err(MacroError::Message( + "Cannot derive TryInto for union or struct.".to_string(), + input.ident.span(), + )); + }; + if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) { + return Err(MacroError::Message( + "Cannot derive TryInto for enum with non-unit variants.".to_string(), + v.fields.span(), + )); + } + Ok(&e.variants) +} + +#[rustfmt::skip::macros(quote)] +fn derive_tryinto_body( + name: &Ident, + variants: &Punctuated, + repr: &Path, +) -> Result { + let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect(); + + Ok(quote! { + #(const #discriminants: #repr = #name::#discriminants as #repr;)*; + match value { + #(#discriminants => core::result::Result::Ok(#name::#discriminants),)* + _ => core::result::Result::Err(value), + } + }) +} + +#[rustfmt::skip::macros(quote)] +fn derive_tryinto_or_error(input: DeriveInput) -> Result { + let repr = get_repr_uN(&input, "#[derive(TryInto)]")?; + let name = &input.ident; + let body = derive_tryinto_body(name, get_variants(&input)?, &repr)?; + let errmsg = format!("invalid value for {name}"); + + Ok(quote! { + impl #name { + #[allow(dead_code)] + pub const fn into_bits(self) -> #repr { + self as #repr + } + + #[allow(dead_code)] + pub const fn from_bits(value: #repr) -> Self { + match ({ + #body + }) { + Ok(x) => x, + Err(_) => panic!(#errmsg) + } + } + } + impl core::convert::TryFrom<#repr> for #name { + type Error = #repr; + + #[allow(ambiguous_associated_items)] + fn try_from(value: #repr) -> Result { + #body + } + } + }) +} + +#[proc_macro_derive(TryInto)] +pub fn derive_tryinto(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let expanded = derive_tryinto_or_error(input).unwrap_or_else(Into::into); + + TokenStream::from(expanded) +} + +#[proc_macro] +pub fn bits_const_internal(ts: TokenStream) -> TokenStream { + let ts = proc_macro2::TokenStream::from(ts); + let mut it = ts.into_iter(); + + let expanded = BitsConstInternal::parse(&mut it).unwrap_or_else(Into::into); + TokenStream::from(expanded) +} diff --git a/rust/qemu-api-macros/src/utils.rs b/rust/qemu-api-macros/src/utils.rs new file mode 100644 index 00000000000..02c91aed7f6 --- /dev/null +++ b/rust/qemu-api-macros/src/utils.rs @@ -0,0 +1,26 @@ +// Procedural macro utilities. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +use proc_macro2::Span; +use quote::quote_spanned; + +pub enum MacroError { + Message(String, Span), + ParseError(syn::Error), +} + +impl From for MacroError { + fn from(err: syn::Error) -> Self { + MacroError::ParseError(err) + } +} + +impl From for proc_macro2::TokenStream { + fn from(err: MacroError) -> Self { + match err { + MacroError::Message(msg, span) => quote_spanned! { span => compile_error!(#msg); }, + MacroError::ParseError(err) => err.into_compile_error(), + } + } +} diff --git a/rust/qemu-api/.gitignore b/rust/qemu-api/.gitignore new file mode 100644 index 00000000000..df6c2163e03 --- /dev/null +++ b/rust/qemu-api/.gitignore @@ -0,0 +1,2 @@ +# Ignore generated bindings file overrides. +/src/bindings.inc.rs diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml new file mode 100644 index 00000000000..db7000dee44 --- /dev/null +++ b/rust/qemu-api/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "qemu_api" +version = "0.1.0" +authors = ["Manos Pitsidianakis "] +description = "Rust bindings for QEMU" +readme = "README.md" +resolver = "2" +publish = false + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +qemu_api_macros = { path = "../qemu-api-macros" } +anyhow = "~1.0" +libc = "0.2.162" +foreign = "~0.3.1" + +[features] +default = ["debug_cell"] +allocator = [] +debug_cell = [] + +[lints] +workspace = true diff --git a/rust/qemu-api/README.md b/rust/qemu-api/README.md new file mode 100644 index 00000000000..ed1b7ab263d --- /dev/null +++ b/rust/qemu-api/README.md @@ -0,0 +1,19 @@ +# 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 +$ make bindings.inc.rs +``` + +## Generate Rust documentation + +Common Cargo tasks can be performed from the QEMU build directory + +```console +$ make clippy +$ make rustfmt +$ make rustdoc +``` diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs new file mode 100644 index 00000000000..7849486c1ba --- /dev/null +++ b/rust/qemu-api/build.rs @@ -0,0 +1,41 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +#[cfg(unix)] +use std::os::unix::fs::symlink as symlink_file; +#[cfg(windows)] +use std::os::windows::fs::symlink_file; +use std::{env, fs::remove_file, io::Result, path::Path}; + +fn main() -> Result<()> { + // Placing bindings.inc.rs in the source directory is supported + // but not documented or encouraged. + let path = env::var("MESON_BUILD_ROOT") + .unwrap_or_else(|_| format!("{}/src", env!("CARGO_MANIFEST_DIR"))); + + let file = format!("{path}/rust/qemu-api/bindings.inc.rs"); + let file = Path::new(&file); + if !Path::new(&file).exists() { + panic!(concat!( + "\n", + " No generated C bindings found! Maybe you wanted one of\n", + " `make clippy`, `make rustfmt`, `make rustdoc`?\n", + "\n", + " For other uses of `cargo`, start a subshell with\n", + " `pyvenv/bin/meson devenv`, or point MESON_BUILD_ROOT to\n", + " the top of the build tree." + )); + } + + let out_dir = env::var("OUT_DIR").unwrap(); + let dest_path = format!("{out_dir}/bindings.inc.rs"); + let dest_path = Path::new(&dest_path); + if dest_path.symlink_metadata().is_ok() { + remove_file(dest_path)?; + } + symlink_file(file, dest_path)?; + + println!("cargo:rerun-if-changed=build.rs"); + Ok(()) +} diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build new file mode 100644 index 00000000000..a090297c458 --- /dev/null +++ b/rust/qemu-api/meson.build @@ -0,0 +1,114 @@ +_qemu_api_cfg = run_command(rustc_args, + '--config-headers', config_host_h, '--features', files('Cargo.toml'), + capture: true, check: true).stdout().strip().splitlines() + +# _qemu_api_cfg += ['--cfg', 'feature="allocator"'] +if get_option('debug_mutex') + _qemu_api_cfg += ['--cfg', 'feature="debug_cell"'] +endif + +c_enums = [ + 'DeviceCategory', + 'GpioPolarity', + 'MachineInitPhase', + 'MemoryDeviceInfoKind', + 'MigrationPolicy', + 'MigrationPriority', + 'QEMUChrEvent', + 'QEMUClockType', + 'ResetType', + 'device_endian', + 'module_init_type', +] +_qemu_api_bindgen_args = [] +foreach enum : c_enums + _qemu_api_bindgen_args += ['--rustified-enum', enum] +endforeach +c_bitfields = [ + 'ClockEvent', + 'VMStateFlags', +] +foreach enum : c_bitfields + _qemu_api_bindgen_args += ['--bitfield-enum', enum] +endforeach + +# 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 +_qemu_api_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common + _qemu_api_bindgen_args, + ) + +_qemu_api_rs = static_library( + 'qemu_api', + structured_sources( + [ + 'src/lib.rs', + 'src/assertions.rs', + 'src/bindings.rs', + 'src/bitops.rs', + 'src/callbacks.rs', + 'src/cell.rs', + 'src/chardev.rs', + 'src/errno.rs', + 'src/error.rs', + 'src/irq.rs', + 'src/log.rs', + 'src/memory.rs', + 'src/module.rs', + 'src/prelude.rs', + 'src/qdev.rs', + 'src/qom.rs', + 'src/sysbus.rs', + 'src/timer.rs', + 'src/uninit.rs', + 'src/vmstate.rs', + 'src/zeroable.rs', + ], + {'.' : _qemu_api_bindings_inc_rs}, + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + rust_args: _qemu_api_cfg, + dependencies: [anyhow_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, + qom, hwcore, chardev, migration], +) + +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, qom, hwcore, chardev, migration]) + +# Doctests are essentially integration tests, so they need the same dependencies. +# Note that running them requires the object files for C code, so place them +# in a separate suite that is run by the "build" CI jobs rather than "check". +rust.doctest('rust-qemu-api-doctests', + _qemu_api_rs, + protocol: 'rust', + dependencies: qemu_api, + suite: ['doc', 'rust']) + +test('rust-qemu-api-integration', + executable( + 'rust-qemu-api-integration', + files('tests/tests.rs', 'tests/vmstate_tests.rs'), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_args: ['--test'], + install: false, + dependencies: [qemu_api]), + args: [ + '--test', '--test-threads', '1', + '--format', 'pretty', + ], + protocol: 'rust', + suite: ['unit', 'rust']) diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs new file mode 100644 index 00000000000..a2d38c877df --- /dev/null +++ b/rust/qemu-api/src/assertions.rs @@ -0,0 +1,152 @@ +// Copyright 2024, Red Hat Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +#![doc(hidden)] +//! This module provides macros to check the equality of types and +//! the type of `struct` fields. This can be useful to ensure that +//! types match the expectations of C code. +//! +//! Documentation is hidden because it only exposes macros, which +//! are exported directly from `qemu_api`. + +// Based on https://stackoverflow.com/questions/64251852/x/70978292#70978292 +// (stackoverflow answers are released under MIT license). + +#[doc(hidden)] +pub trait EqType { + type Itself; +} + +impl EqType for T { + type Itself = T; +} + +/// Assert that two types are the same. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::assert_same_type; +/// # use std::ops::Deref; +/// assert_same_type!(u32, u32); +/// assert_same_type!( as Deref>::Target, u32); +/// ``` +/// +/// Different types will cause a compile failure +/// +/// ```compile_fail +/// # use qemu_api::assert_same_type; +/// assert_same_type!(&Box, &u32); +/// ``` +#[macro_export] +macro_rules! assert_same_type { + ($t1:ty, $t2:ty) => { + const _: () = { + #[allow(unused)] + fn assert_same_type(v: $t1) { + fn types_must_be_equal(_: T) + where + T: $crate::assertions::EqType, + { + } + types_must_be_equal::<_, $t2>(v); + } + }; + }; +} + +/// Assert that a field of a struct has the given type. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::assert_field_type; +/// pub struct A { +/// field1: u32, +/// } +/// +/// assert_field_type!(A, field1, u32); +/// ``` +/// +/// Different types will cause a compile failure +/// +/// ```compile_fail +/// # use qemu_api::assert_field_type; +/// # pub struct A { field1: u32 } +/// assert_field_type!(A, field1, i32); +/// ``` +#[macro_export] +macro_rules! assert_field_type { + (@internal $param_name:ident, $ti:ty, $t:ty, $($field:tt)*) => { + const _: () = { + #[allow(unused)] + fn assert_field_type($param_name: &$t) { + fn types_must_be_equal(_: &T) + where + T: $crate::assertions::EqType, + { + } + types_must_be_equal::<_, $ti>(&$($field)*); + } + }; + }; + + ($t:ty, $i:tt, $ti:ty) => { + $crate::assert_field_type!(@internal v, $ti, $t, v.$i); + }; + + ($t:ty, $i:tt, $ti:ty, num = $num:ident) => { + $crate::assert_field_type!(@internal v, $ti, $t, v.$i[0]); + }; +} + +/// Assert that an expression matches a pattern. This can also be +/// useful to compare enums that do not implement `Eq`. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::assert_match; +/// // JoinHandle does not implement `Eq`, therefore the result +/// // does not either. +/// let result: Result, u32> = Err(42); +/// assert_match!(result, Err(42)); +/// ``` +#[macro_export] +macro_rules! assert_match { + ($a:expr, $b:pat) => { + assert!( + match $a { + $b => true, + _ => false, + }, + "{} = {:?} does not match {}", + stringify!($a), + $a, + stringify!($b) + ); + }; +} + +/// Assert at compile time that an expression is true. This is similar +/// to `const { assert!(...); }` but it works outside functions, as well as +/// on versions of Rust before 1.79. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::static_assert; +/// static_assert!("abc".len() == 3); +/// ``` +/// +/// ```compile_fail +/// # use qemu_api::static_assert; +/// static_assert!("abc".len() == 2); // does not compile +/// ``` +#[macro_export] +macro_rules! static_assert { + ($x:expr) => { + const _: () = assert!($x); + }; +} diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs new file mode 100644 index 00000000000..057de4b6467 --- /dev/null +++ b/rust/qemu-api/src/bindings.rs @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![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::pedantic, + clippy::restriction, + clippy::style, + clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, + clippy::useless_transmute, + clippy::missing_safety_doc +)] + +//! `bindgen`-generated declarations. + +#[cfg(MESON)] +include!("bindings.inc.rs"); + +#[cfg(not(MESON))] +include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs")); + +// SAFETY: these are implemented in C; the bindings need to assert that the +// BQL is taken, either directly or via `BqlCell` and `BqlRefCell`. +// When bindings for character devices are introduced, this can be +// moved to the Opaque<> wrapper in src/chardev.rs. +unsafe impl Send for CharBackend {} +unsafe impl Sync for CharBackend {} + +// SAFETY: this is a pure data struct +unsafe impl Send for CoalescedMemoryRange {} +unsafe impl Sync for CoalescedMemoryRange {} + +// SAFETY: these are constants and vtables; the Send and Sync requirements +// are deferred to the unsafe callbacks that they contain +unsafe impl Send for MemoryRegionOps {} +unsafe impl Sync for MemoryRegionOps {} + +unsafe impl Send for Property {} +unsafe impl Sync for Property {} + +unsafe impl Send for TypeInfo {} +unsafe impl Sync for TypeInfo {} + +unsafe impl Send for VMStateDescription {} +unsafe impl Sync for VMStateDescription {} + +unsafe impl Send for VMStateField {} +unsafe impl Sync for VMStateField {} + +unsafe impl Send for VMStateInfo {} +unsafe impl Sync for VMStateInfo {} diff --git a/rust/qemu-api/src/bitops.rs b/rust/qemu-api/src/bitops.rs new file mode 100644 index 00000000000..b1e3a530ab5 --- /dev/null +++ b/rust/qemu-api/src/bitops.rs @@ -0,0 +1,119 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +//! This module provides bit operation extensions to integer types. +//! It is usually included via the `qemu_api` prelude. + +use std::ops::{ + Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, + Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, +}; + +/// Trait for extensions to integer types +pub trait IntegerExt: + Add + AddAssign + + BitAnd + BitAndAssign + + BitOr + BitOrAssign + + BitXor + BitXorAssign + + Copy + + Div + DivAssign + + Eq + + Mul + MulAssign + + Not + Ord + PartialOrd + + Rem + RemAssign + + Shl + ShlAssign + + Shl + ShlAssign + // add more as needed + Shr + ShrAssign + + Shr + ShrAssign // add more as needed +{ + const BITS: u32; + const MAX: Self; + const MIN: Self; + const ONE: Self; + const ZERO: Self; + + #[inline] + #[must_use] + fn bit(start: u32) -> Self + { + debug_assert!(start < Self::BITS); + + Self::ONE << start + } + + #[inline] + #[must_use] + fn mask(start: u32, length: u32) -> Self + { + /* FIXME: Implement a more elegant check with error handling support? */ + debug_assert!(start < Self::BITS && length > 0 && length <= Self::BITS - start); + + (Self::MAX >> (Self::BITS - length)) << start + } + + #[inline] + #[must_use] + fn deposit(self, start: u32, length: u32, + fieldval: U) -> Self + where Self: From + { + debug_assert!(length <= U::BITS); + + let mask = Self::mask(start, length); + (self & !mask) | ((Self::from(fieldval) << start) & mask) + } + + #[inline] + #[must_use] + fn extract(self, start: u32, length: u32) -> Self + { + let mask = Self::mask(start, length); + (self & mask) >> start + } +} + +macro_rules! impl_num_ext { + ($type:ty) => { + impl IntegerExt for $type { + const BITS: u32 = <$type>::BITS; + const MAX: Self = <$type>::MAX; + const MIN: Self = <$type>::MIN; + const ONE: Self = 1; + const ZERO: Self = 0; + } + }; +} + +impl_num_ext!(u8); +impl_num_ext!(u16); +impl_num_ext!(u32); +impl_num_ext!(u64); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_deposit() { + assert_eq!(15u32.deposit(8, 8, 1u32), 256 + 15); + assert_eq!(15u32.deposit(8, 1, 255u8), 256 + 15); + } + + #[test] + fn test_extract() { + assert_eq!(15u32.extract(2, 4), 3); + } + + #[test] + fn test_bit() { + assert_eq!(u8::bit(7), 128); + assert_eq!(u32::bit(16), 0x10000); + } + + #[test] + fn test_mask() { + assert_eq!(u8::mask(7, 1), 128); + assert_eq!(u32::mask(8, 8), 0xff00); + } +} diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs new file mode 100644 index 00000000000..9642a16eb89 --- /dev/null +++ b/rust/qemu-api/src/callbacks.rs @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: MIT + +//! Utility functions to deal with callbacks from C to Rust. + +use std::{mem, ptr::NonNull}; + +/// Trait for functions (types implementing [`Fn`]) that can be used as +/// callbacks. These include both zero-capture closures and function pointers. +/// +/// In Rust, calling a function through the `Fn` trait normally requires a +/// `self` parameter, even though for zero-sized functions (including function +/// pointers) the type itself contains all necessary information to call the +/// function. This trait provides a `call` function that doesn't require `self`, +/// allowing zero-sized functions to be called using only their type. +/// +/// This enables zero-sized functions to be passed entirely through generic +/// parameters and resolved at compile-time. A typical use is a function +/// receiving an unused parameter of generic type `F` and calling it via +/// `F::call` or passing it to another function via `func::`. +/// +/// QEMU uses this trick to create wrappers to C callbacks. The wrappers +/// are needed to convert an opaque `*mut c_void` into a Rust reference, +/// but they only have a single opaque that they can use. The `FnCall` +/// trait makes it possible to use that opaque for `self` or any other +/// reference: +/// +/// ```ignore +/// // The compiler creates a new `rust_bh_cb` wrapper for each function +/// // passed to `qemu_bh_schedule_oneshot` below. +/// unsafe extern "C" fn rust_bh_cb FnCall<(&'a T,)>>( +/// opaque: *mut c_void, +/// ) { +/// // SAFETY: the opaque was passed as a reference to `T`. +/// F::call((unsafe { &*(opaque.cast::()) }, )) +/// } +/// +/// // The `_f` parameter is unused but it helps the compiler build the appropriate `F`. +/// // Using a reference allows usage in const context. +/// fn qemu_bh_schedule_oneshot FnCall<(&'a T,)>>(_f: &F, opaque: &T) { +/// let cb: unsafe extern "C" fn(*mut c_void) = rust_bh_cb::; +/// unsafe { +/// bindings::qemu_bh_schedule_oneshot(cb, opaque as *const T as *const c_void as *mut c_void) +/// } +/// } +/// ``` +/// +/// Each wrapper is a separate instance of `rust_bh_cb` and is therefore +/// compiled to a separate function ("monomorphization"). If you wanted +/// to pass `self` as the opaque value, the generic parameters would be +/// `rust_bh_cb::`. +/// +/// `Args` is a tuple type whose types are the arguments of the function, +/// while `R` is the returned type. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::callbacks::FnCall; +/// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { +/// F::call((s,)) +/// } +/// +/// let s: String = call_it(&str::to_owned, "hello world"); +/// assert_eq!(s, "hello world"); +/// ``` +/// +/// Note that the compiler will produce a different version of `call_it` for +/// each function that is passed to it. Therefore the argument is not really +/// used, except to decide what is `F` and what `F::call` does. +/// +/// Attempting to pass a non-zero-sized closure causes a compile-time failure: +/// +/// ```compile_fail +/// # use qemu_api::callbacks::FnCall; +/// # fn call_it<'a, F: FnCall<(&'a str,), String>>(_f: &F, s: &'a str) -> String { +/// # F::call((s,)) +/// # } +/// let x: &'static str = "goodbye world"; +/// call_it(&move |_| String::from(x), "hello workd"); +/// ``` +/// +/// `()` can be used to indicate "no function": +/// +/// ``` +/// # use qemu_api::callbacks::FnCall; +/// fn optional FnCall<(&'a str,), String>>(_f: &F, s: &str) -> Option { +/// if F::IS_SOME { +/// Some(F::call((s,))) +/// } else { +/// None +/// } +/// } +/// +/// assert!(optional(&(), "hello world").is_none()); +/// ``` +/// +/// Invoking `F::call` will then be a run-time error. +/// +/// ```should_panic +/// # use qemu_api::callbacks::FnCall; +/// # fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { +/// # F::call((s,)) +/// # } +/// let s: String = call_it(&(), "hello world"); // panics +/// ``` +/// +/// # Safety +/// +/// Because `Self` is a zero-sized type, all instances of the type are +/// equivalent. However, in addition to this, `Self` must have no invariants +/// that could be violated by creating a reference to it. +/// +/// This is always true for zero-capture closures and function pointers, as long +/// as the code is able to name the function in the first place. +pub unsafe trait FnCall: 'static + Sync + Sized { + /// Referring to this internal constant asserts that the `Self` type is + /// zero-sized. Can be replaced by an inline const expression in + /// Rust 1.79.0+. + const ASSERT_ZERO_SIZED: () = { assert!(mem::size_of::() == 0) }; + + /// Referring to this constant asserts that the `Self` type is an actual + /// function type, which can be used to catch incorrect use of `()` + /// at compile time. + /// + /// # Examples + /// + /// ```compile_fail + /// # use qemu_api::callbacks::FnCall; + /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { + /// let _: () = F::ASSERT_IS_SOME; + /// F::call((s,)) + /// } + /// + /// let s: String = call_it((), "hello world"); // does not compile + /// ``` + /// + /// Note that this can be more simply `const { assert!(F::IS_SOME) }` in + /// Rust 1.79.0 or newer. + const ASSERT_IS_SOME: () = { assert!(Self::IS_SOME) }; + + /// `true` if `Self` is an actual function type and not `()`. + /// + /// # Examples + /// + /// You can use `IS_SOME` to catch this at compile time: + /// + /// ```compile_fail + /// # use qemu_api::callbacks::FnCall; + /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -> String { + /// const { assert!(F::IS_SOME) } + /// F::call((s,)) + /// } + /// + /// let s: String = call_it((), "hello world"); // does not compile + /// ``` + const IS_SOME: bool; + + /// `false` if `Self` is an actual function type, `true` if it is `()`. + fn is_none() -> bool { + !Self::IS_SOME + } + + /// `true` if `Self` is an actual function type, `false` if it is `()`. + fn is_some() -> bool { + Self::IS_SOME + } + + /// Call the function with the arguments in args. + fn call(a: Args) -> R; +} + +/// `()` acts as a "null" callback. Using `()` and `function` is nicer +/// than `None` and `Some(function)`, because the compiler is unable to +/// infer the type of just `None`. Therefore, the trait itself acts as the +/// option type, with functions [`FnCall::is_some`] and [`FnCall::is_none`]. +unsafe impl FnCall for () { + const IS_SOME: bool = false; + + /// Call the function with the arguments in args. + fn call(_a: Args) -> R { + panic!("callback not specified") + } +} + +macro_rules! impl_call { + ($($args:ident,)* ) => ( + // SAFETY: because each function is treated as a separate type, + // accessing `FnCall` is only possible in code that would be + // allowed to call the function. + unsafe impl FnCall<($($args,)*), R> for F + where + F: 'static + Sync + Sized + Fn($($args, )*) -> R, + { + const IS_SOME: bool = true; + + #[inline(always)] + fn call(a: ($($args,)*)) -> R { + let _: () = Self::ASSERT_ZERO_SIZED; + + // SAFETY: the safety of this method is the condition for implementing + // `FnCall`. As to the `NonNull` idiom to create a zero-sized type, + // see https://github.com/rust-lang/libs-team/issues/292. + let f: &'static F = unsafe { &*NonNull::::dangling().as_ptr() }; + let ($($args,)*) = a; + f($($args,)*) + } + } + ) +} + +impl_call!(_1, _2, _3, _4, _5,); +impl_call!(_1, _2, _3, _4,); +impl_call!(_1, _2, _3,); +impl_call!(_1, _2,); +impl_call!(_1,); +impl_call!(); + +#[cfg(test)] +mod tests { + use super::*; + + // The `_f` parameter is unused but it helps the compiler infer `F`. + fn do_test_call<'a, F: FnCall<(&'a str,), String>>(_f: &F) -> String { + F::call(("hello world",)) + } + + #[test] + fn test_call() { + assert_eq!(do_test_call(&str::to_owned), "hello world") + } + + // The `_f` parameter is unused but it helps the compiler infer `F`. + fn do_test_is_some<'a, F: FnCall<(&'a str,), String>>(_f: &F) { + assert!(F::is_some()); + } + + #[test] + fn test_is_some() { + do_test_is_some(&str::to_owned); + } +} diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs new file mode 100644 index 00000000000..27063b049d5 --- /dev/null +++ b/rust/qemu-api/src/cell.rs @@ -0,0 +1,1101 @@ +// SPDX-License-Identifier: MIT +// +// This file is based on library/core/src/cell.rs from +// Rust 1.82.0. +// +// 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. + +//! QEMU-specific mutable containers +//! +//! Rust memory safety is based on this rule: Given an object `T`, it is only +//! possible to have one of the following: +//! +//! - Having several immutable references (`&T`) to the object (also known as +//! **aliasing**). +//! - Having one mutable reference (`&mut T`) to the object (also known as +//! **mutability**). +//! +//! This is enforced by the Rust compiler. However, there are situations where +//! this rule is not flexible enough. Sometimes it is required to have multiple +//! references to an object and yet mutate it. In particular, QEMU objects +//! usually have their pointer shared with the "outside world very early in +//! their lifetime", for example when they create their +//! [`MemoryRegion`s](crate::bindings::MemoryRegion). Therefore, individual +//! parts of a device must be made mutable in a controlled manner; this module +//! provides the tools to do so. +//! +//! ## Cell types +//! +//! [`BqlCell`] and [`BqlRefCell`] allow doing this via the Big QEMU Lock. +//! While they are essentially the same single-threaded primitives that are +//! available in `std::cell`, the BQL allows them to be used from a +//! multi-threaded context and to share references across threads, while +//! maintaining Rust's safety guarantees. For this reason, unlike +//! their `std::cell` counterparts, `BqlCell` and `BqlRefCell` implement the +//! `Sync` trait. +//! +//! BQL checks are performed in debug builds but can be optimized away in +//! release builds, providing runtime safety during development with no overhead +//! in production. +//! +//! The two provide different ways of handling interior mutability. +//! `BqlRefCell` is best suited for data that is primarily accessed by the +//! device's own methods, where multiple reads and writes can be grouped within +//! a single borrow and a mutable reference can be passed around. Instead, +//! [`BqlCell`] is a better choice when sharing small pieces of data with +//! external code (especially C code), because it provides simple get/set +//! operations that can be used one at a time. +//! +//! Warning: While `BqlCell` and `BqlRefCell` are similar to their `std::cell` +//! counterparts, they are not interchangeable. Using `std::cell` types in +//! QEMU device implementations is usually incorrect and can lead to +//! thread-safety issues. +//! +//! ### Example +//! +//! ``` +//! # use qemu_api::prelude::*; +//! # use qemu_api::{cell::BqlRefCell, irq::InterruptSource, irq::IRQState}; +//! # use qemu_api::{sysbus::SysBusDevice, qom::Owned, qom::ParentField}; +//! # const N_GPIOS: usize = 8; +//! # struct PL061Registers { /* ... */ } +//! # unsafe impl ObjectType for PL061State { +//! # type Class = ::Class; +//! # const TYPE_NAME: &'static std::ffi::CStr = c"pl061"; +//! # } +//! struct PL061State { +//! parent_obj: ParentField, +//! +//! // Configuration is read-only after initialization +//! pullups: u32, +//! pulldowns: u32, +//! +//! // Single values shared with C code use BqlCell, in this case via InterruptSource +//! out: [InterruptSource; N_GPIOS], +//! interrupt: InterruptSource, +//! +//! // Larger state accessed by device methods uses BqlRefCell or Mutex +//! registers: BqlRefCell, +//! } +//! ``` +//! +//! ### `BqlCell` +//! +//! [`BqlCell`] implements interior mutability by moving values in and out of +//! the cell. That is, an `&mut T` to the inner value can never be obtained as +//! long as the cell is shared. The value itself cannot be directly obtained +//! without copying it, cloning it, or replacing it with something else. This +//! type provides the following methods, all of which can be called only while +//! the BQL is held: +//! +//! - For types that implement [`Copy`], the [`get`](BqlCell::get) method +//! retrieves the current interior value by duplicating it. +//! - For types that implement [`Default`], the [`take`](BqlCell::take) method +//! replaces the current interior value with [`Default::default()`] and +//! returns the replaced value. +//! - All types have: +//! - [`replace`](BqlCell::replace): replaces the current interior value and +//! returns the replaced value. +//! - [`set`](BqlCell::set): this method replaces the interior value, +//! dropping the replaced value. +//! +//! ### `BqlRefCell` +//! +//! [`BqlRefCell`] uses Rust's lifetimes to implement "dynamic borrowing", a +//! process whereby one can claim temporary, exclusive, mutable access to the +//! inner value: +//! +//! ```ignore +//! fn clear_interrupts(&self, val: u32) { +//! // A mutable borrow gives read-write access to the registers +//! let mut regs = self.registers.borrow_mut(); +//! let old = regs.interrupt_status(); +//! regs.update_interrupt_status(old & !val); +//! } +//! ``` +//! +//! Borrows for `BqlRefCell`s are tracked at _runtime_, unlike Rust's native +//! reference types which are entirely tracked statically, at compile time. +//! Multiple immutable borrows are allowed via [`borrow`](BqlRefCell::borrow), +//! or a single mutable borrow via [`borrow_mut`](BqlRefCell::borrow_mut). The +//! thread will panic if these rules are violated or if the BQL is not held. +//! +//! ## Opaque wrappers +//! +//! The cell types from the previous section are useful at the boundaries +//! of code that requires interior mutability. When writing glue code that +//! interacts directly with C structs, however, it is useful to operate +//! at a lower level. +//! +//! C functions often violate Rust's fundamental assumptions about memory +//! safety by modifying memory even if it is shared. Furthermore, C structs +//! often start their life uninitialized and may be populated lazily. +//! +//! For this reason, this module provides the [`Opaque`] type to opt out +//! of Rust's usual guarantees about the wrapped type. Access to the wrapped +//! value is always through raw pointers, obtained via methods like +//! [`as_mut_ptr()`](Opaque::as_mut_ptr) and [`as_ptr()`](Opaque::as_ptr). These +//! pointers can then be passed to C functions or dereferenced; both actions +//! require `unsafe` blocks, making it clear where safety guarantees must be +//! manually verified. For example +//! +//! ```ignore +//! unsafe { +//! let state = Opaque::::uninit(); +//! qemu_struct_init(state.as_mut_ptr()); +//! } +//! ``` +//! +//! [`Opaque`] will usually be wrapped one level further, so that +//! bridge methods can be added to the wrapper: +//! +//! ```ignore +//! pub struct MyStruct(Opaque); +//! +//! impl MyStruct { +//! fn new() -> Pin> { +//! let result = Box::pin(unsafe { Opaque::uninit() }); +//! unsafe { qemu_struct_init(result.as_mut_ptr()) }; +//! result +//! } +//! } +//! ``` +//! +//! This pattern of wrapping bindgen-generated types in [`Opaque`] provides +//! several advantages: +//! +//! * The choice of traits to be implemented is not limited by the +//! bindgen-generated code. For example, [`Drop`] can be added without +//! disabling [`Copy`] on the underlying bindgen type +//! +//! * [`Send`] and [`Sync`] implementations can be controlled by the wrapper +//! type rather than being automatically derived from the C struct's layout +//! +//! * Methods can be implemented in a separate crate from the bindgen-generated +//! bindings +//! +//! * [`Debug`](std::fmt::Debug) and [`Display`](std::fmt::Display) +//! implementations can be customized to be more readable than the raw C +//! struct representation +//! +//! The [`Opaque`] type does not include BQL validation; it is possible to +//! assert in the code that the right lock is taken, to use it together +//! with a custom lock guard type, or to let C code take the lock, as +//! appropriate. It is also possible to use it with non-thread-safe +//! types, since by default (unlike [`BqlCell`] and [`BqlRefCell`] +//! it is neither `Sync` nor `Send`. +//! +//! While [`Opaque`] is necessary for C interop, it should be used sparingly +//! and only at FFI boundaries. For QEMU-specific types that need interior +//! mutability, prefer [`BqlCell`] or [`BqlRefCell`]. + +use std::{ + cell::{Cell, UnsafeCell}, + cmp::Ordering, + fmt, + marker::{PhantomData, PhantomPinned}, + mem::{self, MaybeUninit}, + ops::{Deref, DerefMut}, + ptr::NonNull, +}; + +use crate::bindings; + +/// An internal function that is used by doctests. +pub fn bql_start_test() { + // SAFETY: integration tests are run with --test-threads=1, while + // unit tests and doctests are not multithreaded and do not have + // any BQL-protected data. Just set bql_locked to true. + unsafe { + bindings::rust_bql_mock_lock(); + } +} + +pub fn bql_locked() -> bool { + // SAFETY: the function does nothing but return a thread-local bool + unsafe { bindings::bql_locked() } +} + +fn bql_block_unlock(increase: bool) { + // SAFETY: this only adjusts a counter + unsafe { + bindings::bql_block_unlock(increase); + } +} + +/// A mutable memory location that is protected by the Big QEMU Lock. +/// +/// # Memory layout +/// +/// `BqlCell` has the same in-memory representation as its inner type `T`. +#[repr(transparent)] +pub struct BqlCell { + value: UnsafeCell, +} + +// SAFETY: Same as for std::sync::Mutex. In the end this *is* a Mutex, +// except it is stored out-of-line +unsafe impl Send for BqlCell {} +unsafe impl Sync for BqlCell {} + +impl Clone for BqlCell { + #[inline] + fn clone(&self) -> BqlCell { + BqlCell::new(self.get()) + } +} + +impl Default for BqlCell { + /// Creates a `BqlCell`, with the `Default` value for T. + #[inline] + fn default() -> BqlCell { + BqlCell::new(Default::default()) + } +} + +impl PartialEq for BqlCell { + #[inline] + fn eq(&self, other: &BqlCell) -> bool { + self.get() == other.get() + } +} + +impl Eq for BqlCell {} + +impl PartialOrd for BqlCell { + #[inline] + fn partial_cmp(&self, other: &BqlCell) -> Option { + self.get().partial_cmp(&other.get()) + } +} + +impl Ord for BqlCell { + #[inline] + fn cmp(&self, other: &BqlCell) -> Ordering { + self.get().cmp(&other.get()) + } +} + +impl From for BqlCell { + /// Creates a new `BqlCell` containing the given value. + fn from(t: T) -> BqlCell { + BqlCell::new(t) + } +} + +impl fmt::Debug for BqlCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} + +impl fmt::Display for BqlCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} + +impl BqlCell { + /// Creates a new `BqlCell` containing the given value. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); + /// + /// let c = BqlCell::new(5); + /// ``` + #[inline] + pub const fn new(value: T) -> BqlCell { + BqlCell { + value: UnsafeCell::new(value), + } + } + + /// Sets the contained value. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); + /// + /// let c = BqlCell::new(5); + /// + /// c.set(10); + /// ``` + #[inline] + pub fn set(&self, val: T) { + self.replace(val); + } + + /// Replaces the contained value with `val`, and returns the old contained + /// value. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); + /// + /// let cell = BqlCell::new(5); + /// assert_eq!(cell.get(), 5); + /// assert_eq!(cell.replace(10), 5); + /// assert_eq!(cell.get(), 10); + /// ``` + #[inline] + pub fn replace(&self, val: T) -> T { + assert!(bql_locked()); + // SAFETY: This can cause data races if called from multiple threads, + // but it won't happen as long as C code accesses the value + // under BQL protection only. + mem::replace(unsafe { &mut *self.value.get() }, val) + } + + /// Unwraps the value, consuming the cell. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); + /// + /// let c = BqlCell::new(5); + /// let five = c.into_inner(); + /// + /// assert_eq!(five, 5); + /// ``` + pub fn into_inner(self) -> T { + assert!(bql_locked()); + self.value.into_inner() + } +} + +impl BqlCell { + /// Returns a copy of the contained value. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); + /// + /// let c = BqlCell::new(5); + /// + /// let five = c.get(); + /// ``` + #[inline] + pub fn get(&self) -> T { + assert!(bql_locked()); + // SAFETY: This can cause data races if called from multiple threads, + // but it won't happen as long as C code accesses the value + // under BQL protection only. + unsafe { *self.value.get() } + } +} + +impl BqlCell { + /// Returns a raw pointer to the underlying data in this cell. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); + /// + /// let c = BqlCell::new(5); + /// + /// let ptr = c.as_ptr(); + /// ``` + #[inline] + pub const fn as_ptr(&self) -> *mut T { + self.value.get() + } +} + +impl BqlCell { + /// Takes the value of the cell, leaving `Default::default()` in its place. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlCell; + /// # qemu_api::cell::bql_start_test(); + /// + /// let c = BqlCell::new(5); + /// let five = c.take(); + /// + /// assert_eq!(five, 5); + /// assert_eq!(c.into_inner(), 0); + /// ``` + pub fn take(&self) -> T { + self.replace(Default::default()) + } +} + +/// A mutable memory location with dynamically checked borrow rules, +/// protected by the Big QEMU Lock. +/// +/// See the [module-level documentation](self) for more. +/// +/// # Memory layout +/// +/// `BqlRefCell` starts with the same in-memory representation as its +/// inner type `T`. +#[repr(C)] +pub struct BqlRefCell { + // It is important that this is the first field (which is not the case + // for std::cell::BqlRefCell), so that we can use offset_of! on it. + // UnsafeCell and repr(C) both prevent usage of niches. + value: UnsafeCell, + borrow: Cell, + // Stores the location of the earliest currently active borrow. + // This gets updated whenever we go from having zero borrows + // to having a single borrow. When a borrow occurs, this gets included + // in the panic message + #[cfg(feature = "debug_cell")] + borrowed_at: Cell>>, +} + +// Positive values represent the number of `BqlRef` active. Negative values +// represent the number of `BqlRefMut` active. Right now QEMU's implementation +// does not allow to create `BqlRefMut`s that refer to distinct, nonoverlapping +// components of a `BqlRefCell` (e.g., different ranges of a slice). +// +// `BqlRef` and `BqlRefMut` are both two words in size, and so there will likely +// never be enough `BqlRef`s or `BqlRefMut`s in existence to overflow half of +// the `usize` range. Thus, a `BorrowFlag` will probably never overflow or +// underflow. However, this is not a guarantee, as a pathological program could +// repeatedly create and then mem::forget `BqlRef`s or `BqlRefMut`s. Thus, all +// code must explicitly check for overflow and underflow in order to avoid +// unsafety, or at least behave correctly in the event that overflow or +// underflow happens (e.g., see BorrowRef::new). +type BorrowFlag = isize; +const UNUSED: BorrowFlag = 0; + +#[inline(always)] +const fn is_writing(x: BorrowFlag) -> bool { + x < UNUSED +} + +#[inline(always)] +const fn is_reading(x: BorrowFlag) -> bool { + x > UNUSED +} + +impl BqlRefCell { + /// Creates a new `BqlRefCell` containing `value`. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlRefCell; + /// + /// let c = BqlRefCell::new(5); + /// ``` + #[inline] + pub const fn new(value: T) -> BqlRefCell { + BqlRefCell { + value: UnsafeCell::new(value), + borrow: Cell::new(UNUSED), + #[cfg(feature = "debug_cell")] + borrowed_at: Cell::new(None), + } + } +} + +// This ensures the panicking code is outlined from `borrow_mut` for +// `BqlRefCell`. +#[inline(never)] +#[cold] +#[cfg(feature = "debug_cell")] +fn panic_already_borrowed(source: &Cell>>) -> ! { + // If a borrow occurred, then we must already have an outstanding borrow, + // so `borrowed_at` will be `Some` + panic!("already borrowed at {:?}", source.take().unwrap()) +} + +#[inline(never)] +#[cold] +#[cfg(not(feature = "debug_cell"))] +fn panic_already_borrowed() -> ! { + panic!("already borrowed") +} + +impl BqlRefCell { + #[inline] + #[allow(clippy::unused_self)] + fn panic_already_borrowed(&self) -> ! { + #[cfg(feature = "debug_cell")] + { + panic_already_borrowed(&self.borrowed_at) + } + #[cfg(not(feature = "debug_cell"))] + { + panic_already_borrowed() + } + } + + /// Immutably borrows the wrapped value. + /// + /// The borrow lasts until the returned `BqlRef` exits scope. Multiple + /// immutable borrows can be taken out at the same time. + /// + /// # Panics + /// + /// Panics if the value is currently mutably borrowed. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); + /// + /// let c = BqlRefCell::new(5); + /// + /// let borrowed_five = c.borrow(); + /// let borrowed_five2 = c.borrow(); + /// ``` + /// + /// An example of panic: + /// + /// ```should_panic + /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); + /// + /// let c = BqlRefCell::new(5); + /// + /// let m = c.borrow_mut(); + /// let b = c.borrow(); // this causes a panic + /// ``` + #[inline] + #[track_caller] + pub fn borrow(&self) -> BqlRef<'_, T> { + if let Some(b) = BorrowRef::new(&self.borrow) { + // `borrowed_at` is always the *first* active borrow + if b.borrow.get() == 1 { + #[cfg(feature = "debug_cell")] + self.borrowed_at.set(Some(std::panic::Location::caller())); + } + + bql_block_unlock(true); + + // SAFETY: `BorrowRef` ensures that there is only immutable access + // to the value while borrowed. + let value = unsafe { NonNull::new_unchecked(self.value.get()) }; + BqlRef { value, borrow: b } + } else { + self.panic_already_borrowed() + } + } + + /// Mutably borrows the wrapped value. + /// + /// The borrow lasts until the returned `BqlRefMut` or all `BqlRefMut`s + /// derived from it exit scope. The value cannot be borrowed while this + /// borrow is active. + /// + /// # Panics + /// + /// Panics if the value is currently borrowed. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); + /// + /// let c = BqlRefCell::new("hello".to_owned()); + /// + /// *c.borrow_mut() = "bonjour".to_owned(); + /// + /// assert_eq!(&*c.borrow(), "bonjour"); + /// ``` + /// + /// An example of panic: + /// + /// ```should_panic + /// use qemu_api::cell::BqlRefCell; + /// # qemu_api::cell::bql_start_test(); + /// + /// let c = BqlRefCell::new(5); + /// let m = c.borrow(); + /// + /// let b = c.borrow_mut(); // this causes a panic + /// ``` + #[inline] + #[track_caller] + pub fn borrow_mut(&self) -> BqlRefMut<'_, T> { + if let Some(b) = BorrowRefMut::new(&self.borrow) { + #[cfg(feature = "debug_cell")] + { + self.borrowed_at.set(Some(std::panic::Location::caller())); + } + + // SAFETY: this only adjusts a counter + bql_block_unlock(true); + + // SAFETY: `BorrowRefMut` guarantees unique access. + let value = unsafe { NonNull::new_unchecked(self.value.get()) }; + BqlRefMut { + value, + _borrow: b, + marker: PhantomData, + } + } else { + self.panic_already_borrowed() + } + } + + /// Returns a raw pointer to the underlying data in this cell. + /// + /// # Examples + /// + /// ``` + /// use qemu_api::cell::BqlRefCell; + /// + /// let c = BqlRefCell::new(5); + /// + /// let ptr = c.as_ptr(); + /// ``` + #[inline] + pub const fn as_ptr(&self) -> *mut T { + self.value.get() + } +} + +// SAFETY: Same as for std::sync::Mutex. In the end this is a Mutex that is +// stored out-of-line. Even though BqlRefCell includes Cells, they are +// themselves protected by the Big QEMU Lock. Furtheremore, the Big QEMU +// Lock cannot be released while any borrows is active. +unsafe impl Send for BqlRefCell where T: Send {} +unsafe impl Sync for BqlRefCell {} + +impl Clone for BqlRefCell { + /// # Panics + /// + /// Panics if the value is currently mutably borrowed. + #[inline] + #[track_caller] + fn clone(&self) -> BqlRefCell { + BqlRefCell::new(self.borrow().clone()) + } + + /// # Panics + /// + /// Panics if `source` is currently mutably borrowed. + #[inline] + #[track_caller] + fn clone_from(&mut self, source: &Self) { + self.value.get_mut().clone_from(&source.borrow()) + } +} + +impl Default for BqlRefCell { + /// Creates a `BqlRefCell`, with the `Default` value for T. + #[inline] + fn default() -> BqlRefCell { + BqlRefCell::new(Default::default()) + } +} + +impl PartialEq for BqlRefCell { + /// # Panics + /// + /// Panics if the value in either `BqlRefCell` is currently mutably + /// borrowed. + #[inline] + fn eq(&self, other: &BqlRefCell) -> bool { + *self.borrow() == *other.borrow() + } +} + +impl Eq for BqlRefCell {} + +impl PartialOrd for BqlRefCell { + /// # Panics + /// + /// Panics if the value in either `BqlRefCell` is currently mutably + /// borrowed. + #[inline] + fn partial_cmp(&self, other: &BqlRefCell) -> Option { + self.borrow().partial_cmp(&*other.borrow()) + } +} + +impl Ord for BqlRefCell { + /// # Panics + /// + /// Panics if the value in either `BqlRefCell` is currently mutably + /// borrowed. + #[inline] + fn cmp(&self, other: &BqlRefCell) -> Ordering { + self.borrow().cmp(&*other.borrow()) + } +} + +impl From for BqlRefCell { + /// Creates a new `BqlRefCell` containing the given value. + fn from(t: T) -> BqlRefCell { + BqlRefCell::new(t) + } +} + +struct BorrowRef<'b> { + borrow: &'b Cell, +} + +impl<'b> BorrowRef<'b> { + #[inline] + fn new(borrow: &'b Cell) -> Option> { + let b = borrow.get().wrapping_add(1); + if !is_reading(b) { + // Incrementing borrow can result in a non-reading value (<= 0) in these cases: + // 1. It was < 0, i.e. there are writing borrows, so we can't allow a read + // borrow due to Rust's reference aliasing rules + // 2. It was isize::MAX (the max amount of reading borrows) and it overflowed + // into isize::MIN (the max amount of writing borrows) so we can't allow an + // additional read borrow because isize can't represent so many read borrows + // (this can only happen if you mem::forget more than a small constant amount + // of `BqlRef`s, which is not good practice) + None + } else { + // Incrementing borrow can result in a reading value (> 0) in these cases: + // 1. It was = 0, i.e. it wasn't borrowed, and we are taking the first read + // borrow + // 2. It was > 0 and < isize::MAX, i.e. there were read borrows, and isize is + // large enough to represent having one more read borrow + borrow.set(b); + Some(BorrowRef { borrow }) + } + } +} + +impl Drop for BorrowRef<'_> { + #[inline] + fn drop(&mut self) { + let borrow = self.borrow.get(); + debug_assert!(is_reading(borrow)); + self.borrow.set(borrow - 1); + bql_block_unlock(false) + } +} + +impl Clone for BorrowRef<'_> { + #[inline] + fn clone(&self) -> Self { + BorrowRef::new(self.borrow).unwrap() + } +} + +/// Wraps a borrowed reference to a value in a `BqlRefCell` box. +/// A wrapper type for an immutably borrowed value from a `BqlRefCell`. +/// +/// See the [module-level documentation](self) for more. +pub struct BqlRef<'b, T: 'b> { + // NB: we use a pointer instead of `&'b T` to avoid `noalias` violations, because a + // `BqlRef` argument doesn't hold immutability for its whole scope, only until it drops. + // `NonNull` is also covariant over `T`, just like we would have with `&T`. + value: NonNull, + borrow: BorrowRef<'b>, +} + +impl Deref for BqlRef<'_, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + // SAFETY: the value is accessible as long as we hold our borrow. + unsafe { self.value.as_ref() } + } +} + +impl<'b, T> BqlRef<'b, T> { + /// Copies a `BqlRef`. + /// + /// The `BqlRefCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `BqlRef::clone(...)`. A `Clone` implementation or a method would + /// interfere with the widespread use of `r.borrow().clone()` to clone + /// the contents of a `BqlRefCell`. + #[must_use] + #[inline] + #[allow(clippy::should_implement_trait)] + pub fn clone(orig: &BqlRef<'b, T>) -> BqlRef<'b, T> { + BqlRef { + value: orig.value, + borrow: orig.borrow.clone(), + } + } +} + +impl fmt::Debug for BqlRef<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl fmt::Display for BqlRef<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +struct BorrowRefMut<'b> { + borrow: &'b Cell, +} + +impl<'b> BorrowRefMut<'b> { + #[inline] + fn new(borrow: &'b Cell) -> Option> { + // There must currently be no existing references when borrow_mut() is + // called, so we explicitly only allow going from UNUSED to UNUSED - 1. + match borrow.get() { + UNUSED => { + borrow.set(UNUSED - 1); + Some(BorrowRefMut { borrow }) + } + _ => None, + } + } +} + +impl Drop for BorrowRefMut<'_> { + #[inline] + fn drop(&mut self) { + let borrow = self.borrow.get(); + debug_assert!(is_writing(borrow)); + self.borrow.set(borrow + 1); + bql_block_unlock(false) + } +} + +/// A wrapper type for a mutably borrowed value from a `BqlRefCell`. +/// +/// See the [module-level documentation](self) for more. +pub struct BqlRefMut<'b, T: 'b> { + // NB: we use a pointer instead of `&'b mut T` to avoid `noalias` violations, because a + // `BqlRefMut` argument doesn't hold exclusivity for its whole scope, only until it drops. + value: NonNull, + _borrow: BorrowRefMut<'b>, + // `NonNull` is covariant over `T`, so we need to reintroduce invariance. + marker: PhantomData<&'b mut T>, +} + +impl Deref for BqlRefMut<'_, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + // SAFETY: the value is accessible as long as we hold our borrow. + unsafe { self.value.as_ref() } + } +} + +impl DerefMut for BqlRefMut<'_, T> { + #[inline] + fn deref_mut(&mut self) -> &mut T { + // SAFETY: the value is accessible as long as we hold our borrow. + unsafe { self.value.as_mut() } + } +} + +impl fmt::Debug for BqlRefMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl fmt::Display for BqlRefMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +/// Stores an opaque value that is shared with C code. +/// +/// Often, C structs can changed when calling a C function even if they are +/// behind a shared Rust reference, or they can be initialized lazily and have +/// invalid bit patterns (e.g. `3` for a [`bool`]). This goes against Rust's +/// strict aliasing rules, which normally prevent mutation through shared +/// references. +/// +/// Wrapping the struct with `Opaque` ensures that the Rust compiler does not +/// assume the usual constraints that Rust structs require, and allows using +/// shared references on the Rust side. +/// +/// `Opaque` is `#[repr(transparent)]`, so that it matches the memory layout +/// of `T`. +#[repr(transparent)] +pub struct Opaque { + value: UnsafeCell>, + // PhantomPinned also allows multiple references to the `Opaque`, i.e. + // one `&mut Opaque` can coexist with a `&mut T` or any number of `&T`; + // see https://docs.rs/pinned-aliasable/latest/pinned_aliasable/. + _pin: PhantomPinned, +} + +impl Opaque { + /// Creates a new shared reference from a C pointer + /// + /// # Safety + /// + /// The pointer must be valid, though it need not point to a valid value. + pub unsafe fn from_raw<'a>(ptr: *mut T) -> &'a Self { + let ptr = NonNull::new(ptr).unwrap().cast::(); + // SAFETY: Self is a transparent wrapper over T + unsafe { ptr.as_ref() } + } + + /// Creates a new opaque object with uninitialized contents. + /// + /// # Safety + /// + /// Ultimately the pointer to the returned value will be dereferenced + /// in another `unsafe` block, for example when passing it to a C function, + /// but the functions containing the dereference are usually safe. The + /// value returned from `uninit()` must be initialized and pinned before + /// calling them. + #[allow(clippy::missing_const_for_fn)] + pub unsafe fn uninit() -> Self { + Self { + value: UnsafeCell::new(MaybeUninit::uninit()), + _pin: PhantomPinned, + } + } + + /// Creates a new opaque object with zeroed contents. + /// + /// # Safety + /// + /// Ultimately the pointer to the returned value will be dereferenced + /// in another `unsafe` block, for example when passing it to a C function, + /// but the functions containing the dereference are usually safe. The + /// value returned from `uninit()` must be pinned (and possibly initialized) + /// before calling them. + #[allow(clippy::missing_const_for_fn)] + pub unsafe fn zeroed() -> Self { + Self { + value: UnsafeCell::new(MaybeUninit::zeroed()), + _pin: PhantomPinned, + } + } + + /// Returns a raw mutable pointer to the opaque data. + pub const fn as_mut_ptr(&self) -> *mut T { + UnsafeCell::get(&self.value).cast() + } + + /// Returns a raw pointer to the opaque data. + pub const fn as_ptr(&self) -> *const T { + self.as_mut_ptr().cast_const() + } + + /// Returns a raw pointer to the opaque data that can be passed to a + /// C function as `void *`. + pub const fn as_void_ptr(&self) -> *mut std::ffi::c_void { + UnsafeCell::get(&self.value).cast() + } + + /// Converts a raw pointer to the wrapped type. + pub const fn raw_get(slot: *mut Self) -> *mut T { + // Compare with Linux's raw_get method, which goes through an UnsafeCell + // because it takes a *const Self instead. + slot.cast() + } +} + +impl fmt::Debug for Opaque { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut name: String = "Opaque<".to_string(); + name += std::any::type_name::(); + name += ">"; + f.debug_tuple(&name).field(&self.as_ptr()).finish() + } +} + +impl Opaque { + /// Creates a new opaque object with default contents. + /// + /// # Safety + /// + /// Ultimately the pointer to the returned value will be dereferenced + /// in another `unsafe` block, for example when passing it to a C function, + /// but the functions containing the dereference are usually safe. The + /// value returned from `uninit()` must be pinned before calling them. + pub unsafe fn new() -> Self { + Self { + value: UnsafeCell::new(MaybeUninit::new(T::default())), + _pin: PhantomPinned, + } + } +} + +/// Annotates [`Self`] as a transparent wrapper for another type. +/// +/// Usually defined via the [`qemu_api_macros::Wrapper`] derive macro. +/// +/// # Examples +/// +/// ``` +/// # use std::mem::ManuallyDrop; +/// # use qemu_api::cell::Wrapper; +/// #[repr(transparent)] +/// pub struct Example { +/// inner: ManuallyDrop, +/// } +/// +/// unsafe impl Wrapper for Example { +/// type Wrapped = String; +/// } +/// ``` +/// +/// # Safety +/// +/// `Self` must be a `#[repr(transparent)]` wrapper for the `Wrapped` type, +/// whether directly or indirectly. +/// +/// # Methods +/// +/// By convention, types that implement Wrapper also implement the following +/// methods: +/// +/// ```ignore +/// pub const unsafe fn from_raw<'a>(value: *mut Self::Wrapped) -> &'a Self; +/// pub const unsafe fn as_mut_ptr(&self) -> *mut Self::Wrapped; +/// pub const unsafe fn as_ptr(&self) -> *const Self::Wrapped; +/// pub const unsafe fn raw_get(slot: *mut Self) -> *const Self::Wrapped; +/// ``` +/// +/// They are not defined here to allow them to be `const`. +pub unsafe trait Wrapper { + type Wrapped; +} + +unsafe impl Wrapper for Opaque { + type Wrapped = T; +} diff --git a/rust/qemu-api/src/chardev.rs b/rust/qemu-api/src/chardev.rs new file mode 100644 index 00000000000..6e0590d758e --- /dev/null +++ b/rust/qemu-api/src/chardev.rs @@ -0,0 +1,260 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings for character devices +//! +//! Character devices in QEMU can run under the big QEMU lock or in a separate +//! `GMainContext`. Here we only support the former, because the bindings +//! enforce that the BQL is taken whenever the functions in [`CharBackend`] are +//! called. + +use std::{ + ffi::{c_int, c_void, CStr}, + fmt::{self, Debug}, + io::{self, ErrorKind, Write}, + marker::PhantomPinned, + ptr::addr_of_mut, + slice, +}; + +use crate::{ + bindings, + callbacks::FnCall, + cell::{BqlRefMut, Opaque}, + prelude::*, +}; + +/// A safe wrapper around [`bindings::Chardev`]. +#[repr(transparent)] +#[derive(qemu_api_macros::Wrapper)] +pub struct Chardev(Opaque); + +pub type ChardevClass = bindings::ChardevClass; +pub type Event = bindings::QEMUChrEvent; + +/// A safe wrapper around [`bindings::CharBackend`], denoting the character +/// back-end that is used for example by a device. Compared to the +/// underlying C struct it adds BQL protection, and is marked as pinned +/// because the QOM object ([`bindings::Chardev`]) contains a pointer to +/// the `CharBackend`. +pub struct CharBackend { + inner: BqlRefCell, + _pin: PhantomPinned, +} + +impl Write for BqlRefMut<'_, bindings::CharBackend> { + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write(&mut self, buf: &[u8]) -> io::Result { + let chr: &mut bindings::CharBackend = self; + + let len = buf.len().try_into().unwrap(); + let r = unsafe { bindings::qemu_chr_fe_write(addr_of_mut!(*chr), buf.as_ptr(), len) }; + errno::into_io_result(r).map(|cnt| cnt as usize) + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + let chr: &mut bindings::CharBackend = self; + + let len = buf.len().try_into().unwrap(); + let r = unsafe { bindings::qemu_chr_fe_write_all(addr_of_mut!(*chr), buf.as_ptr(), len) }; + errno::into_io_result(r).and_then(|cnt| { + if cnt as usize == buf.len() { + Ok(()) + } else { + Err(ErrorKind::WriteZero.into()) + } + }) + } +} + +impl Debug for CharBackend { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // SAFETY: accessed just to print the values + let chr = self.inner.as_ptr(); + Debug::fmt(unsafe { &*chr }, f) + } +} + +// FIXME: use something like PinnedDrop from the pinned_init crate +impl Drop for CharBackend { + fn drop(&mut self) { + self.disable_handlers(); + } +} + +impl CharBackend { + /// Enable the front-end's character device handlers, if there is an + /// associated `Chardev`. + pub fn enable_handlers< + 'chardev, + 'owner: 'chardev, + T, + CanReceiveFn: for<'a> FnCall<(&'a T,), u32>, + ReceiveFn: for<'a, 'b> FnCall<(&'a T, &'b [u8])>, + EventFn: for<'a> FnCall<(&'a T, Event)>, + >( + // When "self" is dropped, the handlers are automatically disabled. + // However, this is not necessarily true if the owner is dropped. + // So require the owner to outlive the character device. + &'chardev self, + owner: &'owner T, + _can_receive: CanReceiveFn, + _receive: ReceiveFn, + _event: EventFn, + ) { + unsafe extern "C" fn rust_can_receive_cb FnCall<(&'a T,), u32>>( + opaque: *mut c_void, + ) -> c_int { + // SAFETY: the values are safe according to the contract of + // enable_handlers() and qemu_chr_fe_set_handlers() + let owner: &T = unsafe { &*(opaque.cast::()) }; + let r = F::call((owner,)); + r.try_into().unwrap() + } + + unsafe extern "C" fn rust_receive_cb FnCall<(&'a T, &'b [u8])>>( + opaque: *mut c_void, + buf: *const u8, + size: c_int, + ) { + // SAFETY: the values are safe according to the contract of + // enable_handlers() and qemu_chr_fe_set_handlers() + let owner: &T = unsafe { &*(opaque.cast::()) }; + let buf = unsafe { slice::from_raw_parts(buf, size.try_into().unwrap()) }; + F::call((owner, buf)) + } + + unsafe extern "C" fn rust_event_cb FnCall<(&'a T, Event)>>( + opaque: *mut c_void, + event: Event, + ) { + // SAFETY: the values are safe according to the contract of + // enable_handlers() and qemu_chr_fe_set_handlers() + let owner: &T = unsafe { &*(opaque.cast::()) }; + F::call((owner, event)) + } + + let _: () = CanReceiveFn::ASSERT_IS_SOME; + let receive_cb: Option = + if ReceiveFn::is_some() { + Some(rust_receive_cb::) + } else { + None + }; + let event_cb: Option = if EventFn::is_some() { + Some(rust_event_cb::) + } else { + None + }; + + let mut chr = self.inner.borrow_mut(); + // SAFETY: the borrow promises that the BQL is taken + unsafe { + bindings::qemu_chr_fe_set_handlers( + addr_of_mut!(*chr), + Some(rust_can_receive_cb::), + receive_cb, + event_cb, + None, + (owner as *const T).cast_mut().cast::(), + core::ptr::null_mut(), + true, + ); + } + } + + /// Disable the front-end's character device handlers. + pub fn disable_handlers(&self) { + let mut chr = self.inner.borrow_mut(); + // SAFETY: the borrow promises that the BQL is taken + unsafe { + bindings::qemu_chr_fe_set_handlers( + addr_of_mut!(*chr), + None, + None, + None, + None, + core::ptr::null_mut(), + core::ptr::null_mut(), + true, + ); + } + } + + /// Notify that the frontend is ready to receive data. + pub fn accept_input(&self) { + let mut chr = self.inner.borrow_mut(); + // SAFETY: the borrow promises that the BQL is taken + unsafe { bindings::qemu_chr_fe_accept_input(addr_of_mut!(*chr)) } + } + + /// Temporarily borrow the character device, allowing it to be used + /// as an implementor of `Write`. Note that it is not valid to drop + /// the big QEMU lock while the character device is borrowed, as + /// that might cause C code to write to the character device. + pub fn borrow_mut(&self) -> impl Write + '_ { + self.inner.borrow_mut() + } + + /// Send a continuous stream of zero bits on the line if `enabled` is + /// true, or a short stream if `enabled` is false. + pub fn send_break(&self, long: bool) -> io::Result<()> { + let mut chr = self.inner.borrow_mut(); + let mut duration: c_int = long.into(); + // SAFETY: the borrow promises that the BQL is taken + let r = unsafe { + bindings::qemu_chr_fe_ioctl( + addr_of_mut!(*chr), + bindings::CHR_IOCTL_SERIAL_SET_BREAK as i32, + addr_of_mut!(duration).cast::(), + ) + }; + + errno::into_io_result(r).map(|_| ()) + } + + /// Write data to a character backend from the front end. This function + /// will send data from the front end to the back end. Unlike + /// `write`, this function will block if the back end cannot + /// consume all of the data attempted to be written. + /// + /// Returns the number of bytes consumed (0 if no associated Chardev) or an + /// error. + pub fn write(&self, buf: &[u8]) -> io::Result { + let len = buf.len().try_into().unwrap(); + // SAFETY: qemu_chr_fe_write is thread-safe + let r = unsafe { bindings::qemu_chr_fe_write(self.inner.as_ptr(), buf.as_ptr(), len) }; + errno::into_io_result(r).map(|cnt| cnt as usize) + } + + /// Write data to a character backend from the front end. This function + /// will send data from the front end to the back end. Unlike + /// `write`, this function will block if the back end cannot + /// consume all of the data attempted to be written. + /// + /// Returns the number of bytes consumed (0 if no associated Chardev) or an + /// error. + pub fn write_all(&self, buf: &[u8]) -> io::Result<()> { + let len = buf.len().try_into().unwrap(); + // SAFETY: qemu_chr_fe_write_all is thread-safe + let r = unsafe { bindings::qemu_chr_fe_write_all(self.inner.as_ptr(), buf.as_ptr(), len) }; + errno::into_io_result(r).and_then(|cnt| { + if cnt as usize == buf.len() { + Ok(()) + } else { + Err(ErrorKind::WriteZero.into()) + } + }) + } +} + +unsafe impl ObjectType for Chardev { + type Class = ChardevClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CHARDEV) }; +} +qom_isa!(Chardev: Object); diff --git a/rust/qemu-api/src/errno.rs b/rust/qemu-api/src/errno.rs new file mode 100644 index 00000000000..18d101448b9 --- /dev/null +++ b/rust/qemu-api/src/errno.rs @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Utility functions to convert `errno` to and from +//! [`io::Error`]/[`io::Result`] +//! +//! QEMU C functions often have a "positive success/negative `errno`" calling +//! convention. This module provides functions to portably convert an integer +//! into an [`io::Result`] and back. + +use std::{convert::TryFrom, io, io::ErrorKind}; + +/// An `errno` value that can be converted into an [`io::Error`] +pub struct Errno(pub u16); + +// On Unix, from_raw_os_error takes an errno value and OS errors +// are printed using strerror. On Windows however it takes a +// GetLastError() value; therefore we need to convert errno values +// into io::Error by hand. This is the same mapping that the +// standard library uses to retrieve the kind of OS errors +// (`std::sys::pal::unix::decode_error_kind`). +impl From for ErrorKind { + fn from(value: Errno) -> ErrorKind { + use ErrorKind::*; + let Errno(errno) = value; + match i32::from(errno) { + libc::EPERM | libc::EACCES => PermissionDenied, + libc::ENOENT => NotFound, + libc::EINTR => Interrupted, + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, + libc::ENOMEM => OutOfMemory, + libc::EEXIST => AlreadyExists, + libc::EINVAL => InvalidInput, + libc::EPIPE => BrokenPipe, + libc::EADDRINUSE => AddrInUse, + libc::EADDRNOTAVAIL => AddrNotAvailable, + libc::ECONNABORTED => ConnectionAborted, + libc::ECONNREFUSED => ConnectionRefused, + libc::ECONNRESET => ConnectionReset, + libc::ENOTCONN => NotConnected, + libc::ENOTSUP => Unsupported, + libc::ETIMEDOUT => TimedOut, + _ => Other, + } + } +} + +// This is used on Windows for all io::Errors, but also on Unix if the +// io::Error does not have a raw OS error. This is the reversed +// mapping of the above; EIO is returned for unknown ErrorKinds. +impl From for Errno { + fn from(value: io::ErrorKind) -> Errno { + use ErrorKind::*; + let errno = match value { + // can be both EPERM or EACCES :( pick one + PermissionDenied => libc::EPERM, + NotFound => libc::ENOENT, + Interrupted => libc::EINTR, + WouldBlock => libc::EAGAIN, + OutOfMemory => libc::ENOMEM, + AlreadyExists => libc::EEXIST, + InvalidInput => libc::EINVAL, + BrokenPipe => libc::EPIPE, + AddrInUse => libc::EADDRINUSE, + AddrNotAvailable => libc::EADDRNOTAVAIL, + ConnectionAborted => libc::ECONNABORTED, + ConnectionRefused => libc::ECONNREFUSED, + ConnectionReset => libc::ECONNRESET, + NotConnected => libc::ENOTCONN, + Unsupported => libc::ENOTSUP, + TimedOut => libc::ETIMEDOUT, + _ => libc::EIO, + }; + Errno(errno as u16) + } +} + +impl From for io::Error { + #[cfg(unix)] + fn from(value: Errno) -> io::Error { + let Errno(errno) = value; + io::Error::from_raw_os_error(errno.into()) + } + + #[cfg(windows)] + fn from(value: Errno) -> io::Error { + let error_kind: ErrorKind = value.into(); + error_kind.into() + } +} + +impl From for Errno { + fn from(value: io::Error) -> Errno { + if cfg!(unix) { + if let Some(errno) = value.raw_os_error() { + return Errno(u16::try_from(errno).unwrap()); + } + } + value.kind().into() + } +} + +/// Internal traits; used to enable [`into_io_result`] and [`into_neg_errno`] +/// for the "right" set of types. +mod traits { + use super::Errno; + + /// A signed type that can be converted into an + /// [`io::Result`](std::io::Result) + pub trait GetErrno { + /// Unsigned variant of `Self`, used as the type for the `Ok` case. + type Out; + + /// Return `Ok(self)` if positive, `Err(Errno(-self))` if negative + fn into_errno_result(self) -> Result; + } + + /// A type that can be taken out of an [`io::Result`](std::io::Result) and + /// converted into "positive success/negative `errno`" convention. + pub trait MergeErrno { + /// Signed variant of `Self`, used as the return type of + /// [`into_neg_errno`](super::into_neg_errno). + type Out: From + std::ops::Neg; + + /// Return `self`, asserting that it is in range + fn map_ok(self) -> Self::Out; + } + + macro_rules! get_errno { + ($t:ty, $out:ty) => { + impl GetErrno for $t { + type Out = $out; + fn into_errno_result(self) -> Result { + match self { + 0.. => Ok(self as $out), + -65535..=-1 => Err(Errno(-self as u16)), + _ => panic!("{self} is not a negative errno"), + } + } + } + }; + } + + get_errno!(i32, u32); + get_errno!(i64, u64); + get_errno!(isize, usize); + + macro_rules! merge_errno { + ($t:ty, $out:ty) => { + impl MergeErrno for $t { + type Out = $out; + fn map_ok(self) -> Self::Out { + self.try_into().unwrap() + } + } + }; + } + + merge_errno!(u8, i32); + merge_errno!(u16, i32); + merge_errno!(u32, i32); + merge_errno!(u64, i64); + + impl MergeErrno for () { + type Out = i32; + fn map_ok(self) -> i32 { + 0 + } + } +} + +use traits::{GetErrno, MergeErrno}; + +/// Convert an integer value into a [`io::Result`]. +/// +/// Positive values are turned into an `Ok` result; negative values +/// are interpreted as negated `errno` and turned into an `Err`. +/// +/// ``` +/// # use qemu_api::errno::into_io_result; +/// # use std::io::ErrorKind; +/// let ok = into_io_result(1i32).unwrap(); +/// assert_eq!(ok, 1u32); +/// +/// let err = into_io_result(-1i32).unwrap_err(); // -EPERM +/// assert_eq!(err.kind(), ErrorKind::PermissionDenied); +/// ``` +/// +/// # Panics +/// +/// Since the result is an unsigned integer, negative values must +/// be close to 0; values that are too far away are considered +/// likely overflows and will panic: +/// +/// ```should_panic +/// # use qemu_api::errno::into_io_result; +/// # #[allow(dead_code)] +/// let err = into_io_result(-0x1234_5678i32); // panic +/// ``` +pub fn into_io_result(value: T) -> io::Result { + value.into_errno_result().map_err(Into::into) +} + +/// Convert a [`Result`] into an integer value, using negative `errno` +/// values to report errors. +/// +/// ``` +/// # use qemu_api::errno::into_neg_errno; +/// # use std::io::{self, ErrorKind}; +/// let ok: io::Result<()> = Ok(()); +/// assert_eq!(into_neg_errno(ok), 0); +/// +/// let err: io::Result<()> = Err(ErrorKind::InvalidInput.into()); +/// assert_eq!(into_neg_errno(err), -22); // -EINVAL +/// ``` +/// +/// Since this module also provides the ability to convert [`io::Error`] +/// to an `errno` value, [`io::Result`] is the most commonly used type +/// for the argument of this function: +/// +/// # Panics +/// +/// Since the result is a signed integer, integer `Ok` values must remain +/// positive: +/// +/// ```should_panic +/// # use qemu_api::errno::into_neg_errno; +/// # use std::io; +/// let err: io::Result = Ok(0x8899_AABB); +/// into_neg_errno(err) // panic +/// # ; +/// ``` +pub fn into_neg_errno>(value: Result) -> T::Out { + match value { + Ok(x) => x.map_ok(), + Err(err) => -T::Out::from(err.into().0), + } +} + +#[cfg(test)] +mod tests { + use std::io::ErrorKind; + + use super::*; + use crate::assert_match; + + #[test] + pub fn test_from_u8() { + let ok: io::Result<_> = Ok(42u8); + assert_eq!(into_neg_errno(ok), 42); + + let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_from_u16() { + let ok: io::Result<_> = Ok(1234u16); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_i32() { + assert_match!(into_io_result(1234i32), Ok(1234)); + + let err = into_io_result(-1i32).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(1)); + assert_match!(err.kind(), ErrorKind::PermissionDenied); + } + + #[test] + pub fn test_from_u32() { + let ok: io::Result<_> = Ok(1234u32); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_i64() { + assert_match!(into_io_result(1234i64), Ok(1234)); + + let err = into_io_result(-22i64).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(22)); + assert_match!(err.kind(), ErrorKind::InvalidInput); + } + + #[test] + pub fn test_from_u64() { + let ok: io::Result<_> = Ok(1234u64); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result = Err(io::ErrorKind::InvalidInput.into()); + assert_eq!(into_neg_errno(err), -22); + + if cfg!(unix) { + let os_err: io::Result = Err(io::Error::from_raw_os_error(6)); + assert_eq!(into_neg_errno(os_err), -6); + } + } + + #[test] + pub fn test_isize() { + assert_match!(into_io_result(1234isize), Ok(1234)); + + let err = into_io_result(-4isize).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(4)); + assert_match!(err.kind(), ErrorKind::Interrupted); + } + + #[test] + pub fn test_from_unit() { + let ok: io::Result<_> = Ok(()); + assert_eq!(into_neg_errno(ok), 0); + + let err: io::Result<()> = Err(io::ErrorKind::OutOfMemory.into()); + assert_eq!(into_neg_errno(err), -12); + + if cfg!(unix) { + let os_err: io::Result<()> = Err(io::Error::from_raw_os_error(2)); + assert_eq!(into_neg_errno(os_err), -2); + } + } +} diff --git a/rust/qemu-api/src/error.rs b/rust/qemu-api/src/error.rs new file mode 100644 index 00000000000..e114fc4178b --- /dev/null +++ b/rust/qemu-api/src/error.rs @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Error propagation for QEMU Rust code +//! +//! This module contains [`Error`], the bridge between Rust errors and +//! [`Result`](std::result::Result)s and QEMU's C [`Error`](bindings::Error) +//! struct. +//! +//! For FFI code, [`Error`] provides functions to simplify conversion between +//! the Rust ([`Result<>`](std::result::Result)) and C (`Error**`) conventions: +//! +//! * [`ok_or_propagate`](crate::Error::ok_or_propagate), +//! [`bool_or_propagate`](crate::Error::bool_or_propagate), +//! [`ptr_or_propagate`](crate::Error::ptr_or_propagate) can be used to build +//! a C return value while also propagating an error condition +//! +//! * [`err_or_else`](crate::Error::err_or_else) and +//! [`err_or_unit`](crate::Error::err_or_unit) can be used to build a `Result` +//! +//! This module is most commonly used at the boundary between C and Rust code; +//! other code will usually access it through the +//! [`qemu_api::Result`](crate::Result) type alias, and will use the +//! [`std::error::Error`] interface to let C errors participate in Rust's error +//! handling functionality. +//! +//! Rust code can also create use this module to create an error object that +//! will be passed up to C code, though in most cases this will be done +//! transparently through the `?` operator. Errors can be constructed from a +//! simple error string, from an [`anyhow::Error`] to pass any other Rust error +//! type up to C code, or from a combination of the two. +//! +//! The third case, corresponding to [`Error::with_error`], is the only one that +//! requires mentioning [`qemu_api::Error`](crate::Error) explicitly. Similar +//! to how QEMU's C code handles errno values, the string and the +//! `anyhow::Error` object will be concatenated with `:` as the separator. + +use std::{ + borrow::Cow, + ffi::{c_char, c_int, c_void, CStr}, + fmt::{self, Display}, + panic, ptr, +}; + +use foreign::{prelude::*, OwnedPointer}; + +use crate::bindings; + +pub type Result = std::result::Result; + +#[derive(Debug)] +pub struct Error { + msg: Option>, + /// Appends the print string of the error to the msg if not None + cause: Option, + file: &'static str, + line: u32, +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.cause.as_ref().map(AsRef::as_ref) + } + + #[allow(deprecated)] + fn description(&self) -> &str { + self.msg + .as_deref() + .or_else(|| self.cause.as_deref().map(std::error::Error::description)) + .expect("no message nor cause?") + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut prefix = ""; + if let Some(ref msg) = self.msg { + write!(f, "{msg}")?; + prefix = ": "; + } + if let Some(ref cause) = self.cause { + write!(f, "{prefix}{cause}")?; + } else if prefix.is_empty() { + panic!("no message nor cause?"); + } + Ok(()) + } +} + +impl From for Error { + #[track_caller] + fn from(msg: String) -> Self { + let location = panic::Location::caller(); + Error { + msg: Some(Cow::Owned(msg)), + cause: None, + file: location.file(), + line: location.line(), + } + } +} + +impl From<&'static str> for Error { + #[track_caller] + fn from(msg: &'static str) -> Self { + let location = panic::Location::caller(); + Error { + msg: Some(Cow::Borrowed(msg)), + cause: None, + file: location.file(), + line: location.line(), + } + } +} + +impl From for Error { + #[track_caller] + fn from(error: anyhow::Error) -> Self { + let location = panic::Location::caller(); + Error { + msg: None, + cause: Some(error), + file: location.file(), + line: location.line(), + } + } +} + +impl Error { + /// Create a new error, prepending `msg` to the + /// description of `cause` + #[track_caller] + pub fn with_error(msg: impl Into>, cause: impl Into) -> Self { + let location = panic::Location::caller(); + Error { + msg: Some(msg.into()), + cause: Some(cause.into()), + file: location.file(), + line: location.line(), + } + } + + /// Consume a result, returning `false` if it is an error and + /// `true` if it is successful. The error is propagated into + /// `errp` like the C API `error_propagate` would do. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; + /// typically it is received from C code and need not be + /// checked further at the Rust↔C boundary. + pub unsafe fn bool_or_propagate(result: Result<()>, errp: *mut *mut bindings::Error) -> bool { + // SAFETY: caller guarantees errp is valid + unsafe { Self::ok_or_propagate(result, errp) }.is_some() + } + + /// Consume a result, returning a `NULL` pointer if it is an error and + /// a C representation of the contents if it is successful. This is + /// similar to the C API `error_propagate`, but it panics if `*errp` + /// is not `NULL`. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; + /// typically it is received from C code and need not be + /// checked further at the Rust↔C boundary. + /// + /// See [`propagate`](Error::propagate) for more information. + #[must_use] + pub unsafe fn ptr_or_propagate( + result: Result, + errp: *mut *mut bindings::Error, + ) -> *mut T::Foreign { + // SAFETY: caller guarantees errp is valid + unsafe { Self::ok_or_propagate(result, errp) }.clone_to_foreign_ptr() + } + + /// Consume a result in the same way as `self.ok()`, but also propagate + /// a possible error into `errp`. This is similar to the C API + /// `error_propagate`, but it panics if `*errp` is not `NULL`. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; + /// typically it is received from C code and need not be + /// checked further at the Rust↔C boundary. + /// + /// See [`propagate`](Error::propagate) for more information. + pub unsafe fn ok_or_propagate( + result: Result, + errp: *mut *mut bindings::Error, + ) -> Option { + result.map_err(|err| unsafe { err.propagate(errp) }).ok() + } + + /// Equivalent of the C function `error_propagate`. Fill `*errp` + /// with the information container in `self` if `errp` is not NULL; + /// then consume it. + /// + /// This is similar to the C API `error_propagate`, but it panics if + /// `*errp` is not `NULL`. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; it can be + /// `NULL` or it can point to any of: + /// * `error_abort` + /// * `error_fatal` + /// * a local variable of (C) type `Error *` + /// + /// Typically `errp` is received from C code and need not be + /// checked further at the Rust↔C boundary. + pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) { + if errp.is_null() { + return; + } + + // SAFETY: caller guarantees that errp and *errp are valid + unsafe { + assert_eq!(*errp, ptr::null_mut()); + bindings::error_propagate(errp, self.clone_to_foreign_ptr()); + } + } + + /// Convert a C `Error*` into a Rust `Result`, using + /// `Ok(())` if `c_error` is NULL. Free the `Error*`. + /// + /// # Safety + /// + /// `c_error` must be `NULL` or valid; typically it was initialized + /// with `ptr::null_mut()` and passed by reference to a C function. + pub unsafe fn err_or_unit(c_error: *mut bindings::Error) -> Result<()> { + // SAFETY: caller guarantees c_error is valid + unsafe { Self::err_or_else(c_error, || ()) } + } + + /// Convert a C `Error*` into a Rust `Result`, calling `f()` to + /// obtain an `Ok` value if `c_error` is NULL. Free the `Error*`. + /// + /// # Safety + /// + /// `c_error` must be `NULL` or point to a valid C [`struct + /// Error`](bindings::Error); typically it was initialized with + /// `ptr::null_mut()` and passed by reference to a C function. + pub unsafe fn err_or_else T>( + c_error: *mut bindings::Error, + f: F, + ) -> Result { + // SAFETY: caller guarantees c_error is valid + let err = unsafe { Option::::from_foreign(c_error) }; + match err { + None => Ok(f()), + Some(err) => Err(err), + } + } +} + +impl FreeForeign for Error { + type Foreign = bindings::Error; + + unsafe fn free_foreign(p: *mut bindings::Error) { + // SAFETY: caller guarantees p is valid + unsafe { + bindings::error_free(p); + } + } +} + +impl CloneToForeign for Error { + fn clone_to_foreign(&self) -> OwnedPointer { + // SAFETY: all arguments are controlled by this function + unsafe { + let err: *mut c_void = libc::malloc(std::mem::size_of::()); + let err: &mut bindings::Error = &mut *err.cast(); + *err = bindings::Error { + msg: format!("{self}").clone_to_foreign_ptr(), + err_class: bindings::ERROR_CLASS_GENERIC_ERROR, + src_len: self.file.len() as c_int, + src: self.file.as_ptr().cast::(), + line: self.line as c_int, + func: ptr::null_mut(), + hint: ptr::null_mut(), + }; + OwnedPointer::new(err) + } + } +} + +impl FromForeign for Error { + unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self { + // SAFETY: caller guarantees c_error is valid + unsafe { + let error = &*c_error; + let file = if error.src_len < 0 { + // NUL-terminated + CStr::from_ptr(error.src).to_str() + } else { + // Can become str::from_utf8 with Rust 1.87.0 + std::str::from_utf8(std::slice::from_raw_parts( + &*error.src.cast::(), + error.src_len as usize, + )) + }; + + Error { + msg: FromForeign::cloned_from_foreign(error.msg), + cause: None, + file: file.unwrap(), + line: error.line as u32, + } + } + } +} + +#[cfg(test)] +mod tests { + use std::ffi::CStr; + + use anyhow::anyhow; + use foreign::OwnedPointer; + + use super::*; + use crate::{assert_match, bindings}; + + #[track_caller] + fn error_for_test(msg: &CStr) -> OwnedPointer { + // SAFETY: all arguments are controlled by this function + let location = panic::Location::caller(); + unsafe { + let err: *mut c_void = libc::malloc(std::mem::size_of::()); + let err: &mut bindings::Error = &mut *err.cast(); + *err = bindings::Error { + msg: msg.clone_to_foreign_ptr(), + err_class: bindings::ERROR_CLASS_GENERIC_ERROR, + src_len: location.file().len() as c_int, + src: location.file().as_ptr().cast::(), + line: location.line() as c_int, + func: ptr::null_mut(), + hint: ptr::null_mut(), + }; + OwnedPointer::new(err) + } + } + + unsafe fn error_get_pretty<'a>(local_err: *mut bindings::Error) -> &'a CStr { + unsafe { CStr::from_ptr(bindings::error_get_pretty(local_err)) } + } + + #[test] + #[allow(deprecated)] + fn test_description() { + use std::error::Error; + + assert_eq!(super::Error::from("msg").description(), "msg"); + assert_eq!(super::Error::from("msg".to_owned()).description(), "msg"); + } + + #[test] + fn test_display() { + assert_eq!(&*format!("{}", Error::from("msg")), "msg"); + assert_eq!(&*format!("{}", Error::from("msg".to_owned())), "msg"); + assert_eq!(&*format!("{}", Error::from(anyhow!("msg"))), "msg"); + + assert_eq!( + &*format!("{}", Error::with_error("msg", anyhow!("cause"))), + "msg: cause" + ); + } + + #[test] + fn test_bool_or_propagate() { + unsafe { + let mut local_err: *mut bindings::Error = ptr::null_mut(); + + assert!(Error::bool_or_propagate(Ok(()), &mut local_err)); + assert_eq!(local_err, ptr::null_mut()); + + let my_err = Error::from("msg"); + assert!(!Error::bool_or_propagate(Err(my_err), &mut local_err)); + assert_ne!(local_err, ptr::null_mut()); + assert_eq!(error_get_pretty(local_err), c"msg"); + bindings::error_free(local_err); + } + } + + #[test] + fn test_ptr_or_propagate() { + unsafe { + let mut local_err: *mut bindings::Error = ptr::null_mut(); + + let ret = Error::ptr_or_propagate(Ok("abc".to_owned()), &mut local_err); + assert_eq!(String::from_foreign(ret), "abc"); + assert_eq!(local_err, ptr::null_mut()); + + let my_err = Error::from("msg"); + assert_eq!( + Error::ptr_or_propagate(Err::(my_err), &mut local_err), + ptr::null_mut() + ); + assert_ne!(local_err, ptr::null_mut()); + assert_eq!(error_get_pretty(local_err), c"msg"); + bindings::error_free(local_err); + } + } + + #[test] + fn test_err_or_unit() { + unsafe { + let result = Error::err_or_unit(ptr::null_mut()); + assert_match!(result, Ok(())); + + let err = error_for_test(c"msg"); + let err = Error::err_or_unit(err.into_inner()).unwrap_err(); + assert_eq!(&*format!("{err}"), "msg"); + } + } +} diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs new file mode 100644 index 00000000000..1526e6f63a1 --- /dev/null +++ b/rust/qemu-api/src/irq.rs @@ -0,0 +1,115 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings for interrupt sources + +use std::{ + ffi::{c_int, CStr}, + marker::PhantomData, + ptr, +}; + +use crate::{ + bindings::{self, qemu_set_irq}, + cell::Opaque, + prelude::*, + qom::ObjectClass, +}; + +/// An opaque wrapper around [`bindings::IRQState`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct IRQState(Opaque); + +/// Interrupt sources are used by devices to pass changes to a value (typically +/// a boolean). The interrupt sink is usually an interrupt controller or +/// GPIO controller. +/// +/// As far as devices are concerned, interrupt sources are always active-high: +/// for example, `InterruptSource`'s [`raise`](InterruptSource::raise) +/// method sends a `true` value to the sink. If the guest has to see a +/// different polarity, that change is performed by the board between the +/// device and the interrupt controller. +/// +/// Interrupts are implemented as a pointer to the interrupt "sink", which has +/// type [`IRQState`]. A device exposes its source as a QOM link property using +/// a function such as [`SysBusDeviceMethods::init_irq`], and +/// initially leaves the pointer to a NULL value, representing an unconnected +/// interrupt. To connect it, whoever creates the device fills the pointer with +/// the sink's `IRQState *`, for example using `sysbus_connect_irq`. Because +/// devices are generally shared objects, interrupt sources are an example of +/// the interior mutability pattern. +/// +/// Interrupt sources can only be triggered under the Big QEMU Lock; `BqlCell` +/// allows access from whatever thread has it. +#[derive(Debug)] +#[repr(transparent)] +pub struct InterruptSource +where + c_int: From, +{ + cell: BqlCell<*mut bindings::IRQState>, + _marker: PhantomData, +} + +// SAFETY: the implementation asserts via `BqlCell` that the BQL is taken +unsafe impl Sync for InterruptSource where c_int: From {} + +impl InterruptSource { + /// Send a low (`false`) value to the interrupt sink. + pub fn lower(&self) { + self.set(false); + } + + /// Send a high-low pulse to the interrupt sink. + pub fn pulse(&self) { + self.set(true); + self.set(false); + } + + /// Send a high (`true`) value to the interrupt sink. + pub fn raise(&self) { + self.set(true); + } +} + +impl InterruptSource +where + c_int: From, +{ + /// Send `level` to the interrupt sink. + pub fn set(&self, level: T) { + let ptr = self.cell.get(); + // SAFETY: the pointer is retrieved under the BQL and remains valid + // until the BQL is released, which is after qemu_set_irq() is entered. + unsafe { + qemu_set_irq(ptr, level.into()); + } + } + + pub(crate) const fn as_ptr(&self) -> *mut *mut bindings::IRQState { + self.cell.as_ptr() + } + + pub(crate) const fn slice_as_ptr(slice: &[Self]) -> *mut *mut bindings::IRQState { + assert!(!slice.is_empty()); + slice[0].as_ptr() + } +} + +impl Default for InterruptSource { + fn default() -> Self { + InterruptSource { + cell: BqlCell::new(ptr::null_mut()), + _marker: PhantomData, + } + } +} + +unsafe impl ObjectType for IRQState { + type Class = ObjectClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_IRQ) }; +} +qom_isa!(IRQState: Object); diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs new file mode 100644 index 00000000000..86dcd8ef17a --- /dev/null +++ b/rust/qemu-api/src/lib.rs @@ -0,0 +1,170 @@ +// 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"))] +#![deny(clippy::missing_const_for_fn)] + +#[rustfmt::skip] +pub mod bindings; + +// preserve one-item-per-"use" syntax, it is clearer +// for prelude-like modules +#[rustfmt::skip] +pub mod prelude; + +pub mod assertions; +pub mod bitops; +pub mod callbacks; +pub mod cell; +pub mod chardev; +pub mod errno; +pub mod error; +pub mod irq; +pub mod log; +pub mod memory; +pub mod module; +pub mod qdev; +pub mod qom; +pub mod sysbus; +pub mod timer; +pub mod uninit; +pub mod vmstate; +pub mod zeroable; + +use std::{ + alloc::{GlobalAlloc, Layout}, + ffi::c_void, +}; + +pub use error::{Error, Result}; + +#[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::<_>()) } + } + } + } +} diff --git a/rust/qemu-api/src/log.rs b/rust/qemu-api/src/log.rs new file mode 100644 index 00000000000..d6c3d6c1b63 --- /dev/null +++ b/rust/qemu-api/src/log.rs @@ -0,0 +1,73 @@ +// Copyright 2025 Bernhard Beschow +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings for QEMU's logging infrastructure + +#[repr(u32)] +/// Represents specific error categories within QEMU's logging system. +/// +/// The `Log` enum provides a Rust abstraction for logging errors, corresponding +/// to a subset of the error categories defined in the C implementation. +pub enum Log { + /// Log invalid access caused by the guest. + /// Corresponds to `LOG_GUEST_ERROR` in the C implementation. + GuestError = crate::bindings::LOG_GUEST_ERROR, + + /// Log guest access of unimplemented functionality. + /// Corresponds to `LOG_UNIMP` in the C implementation. + Unimp = crate::bindings::LOG_UNIMP, +} + +/// A macro to log messages conditionally based on a provided mask. +/// +/// The `log_mask_ln` macro checks whether the given mask matches the current +/// log level and, if so, formats and logs the message. It is the Rust +/// counterpart of the `qemu_log_mask()` macro in the C implementation. +/// +/// # Parameters +/// +/// - `$mask`: A log level mask. This should be a variant of the `Log` enum. +/// - `$fmt`: A format string following the syntax and rules of the `format!` +/// macro. It specifies the structure of the log message. +/// - `$args`: Optional arguments to be interpolated into the format string. +/// +/// # Example +/// +/// ``` +/// use qemu_api::{log::Log, log_mask_ln}; +/// +/// let error_address = 0xbad; +/// log_mask_ln!(Log::GuestError, "Address 0x{error_address:x} out of range"); +/// ``` +/// +/// It is also possible to use printf-style formatting, as well as having a +/// trailing `,`: +/// +/// ``` +/// use qemu_api::{log::Log, log_mask_ln}; +/// +/// let error_address = 0xbad; +/// log_mask_ln!( +/// Log::GuestError, +/// "Address 0x{:x} out of range", +/// error_address, +/// ); +/// ``` +#[macro_export] +macro_rules! log_mask_ln { + ($mask:expr, $fmt:tt $($args:tt)*) => {{ + // Type assertion to enforce type `Log` for $mask + let _: Log = $mask; + + if unsafe { + (::qemu_api::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0 + } { + let formatted_string = format!("{}\n", format_args!($fmt $($args)*)); + let c_string = std::ffi::CString::new(formatted_string).unwrap(); + + unsafe { + ::qemu_api::bindings::qemu_log(c_string.as_ptr()); + } + } + }}; +} diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs new file mode 100644 index 00000000000..e40fad6cf19 --- /dev/null +++ b/rust/qemu-api/src/memory.rs @@ -0,0 +1,204 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings for `MemoryRegion`, `MemoryRegionOps` and `MemTxAttrs` + +use std::{ + ffi::{c_uint, c_void, CStr, CString}, + marker::PhantomData, +}; + +pub use bindings::{hwaddr, MemTxAttrs}; + +use crate::{ + bindings::{self, device_endian, memory_region_init_io}, + callbacks::FnCall, + cell::Opaque, + prelude::*, + uninit::MaybeUninitField, + zeroable::Zeroable, +}; + +pub struct MemoryRegionOps( + bindings::MemoryRegionOps, + // Note: quite often you'll see PhantomData mentioned when discussing + // covariance and contravariance; you don't need any of those to understand + // this usage of PhantomData. Quite simply, MemoryRegionOps *logically* + // holds callbacks that take an argument of type &T, except the type is erased + // before the callback is stored in the bindings::MemoryRegionOps field. + // The argument of PhantomData is a function pointer in order to represent + // that relationship; while that will also provide desirable and safe variance + // for T, variance is not the point but just a consequence. + PhantomData, +); + +// SAFETY: When a *const T is passed to the callbacks, the call itself +// is done in a thread-safe manner. The invocation is okay as long as +// T itself is `Sync`. +unsafe impl Sync for MemoryRegionOps {} + +#[derive(Clone)] +pub struct MemoryRegionOpsBuilder(bindings::MemoryRegionOps, PhantomData); + +unsafe extern "C" fn memory_region_ops_read_cb FnCall<(&'a T, hwaddr, u32), u64>>( + opaque: *mut c_void, + addr: hwaddr, + size: c_uint, +) -> u64 { + F::call((unsafe { &*(opaque.cast::()) }, addr, size)) +} + +unsafe extern "C" fn memory_region_ops_write_cb FnCall<(&'a T, hwaddr, u64, u32)>>( + opaque: *mut c_void, + addr: hwaddr, + data: u64, + size: c_uint, +) { + F::call((unsafe { &*(opaque.cast::()) }, addr, data, size)) +} + +impl MemoryRegionOpsBuilder { + #[must_use] + pub const fn read FnCall<(&'a T, hwaddr, u32), u64>>(mut self, _f: &F) -> Self { + self.0.read = Some(memory_region_ops_read_cb::); + self + } + + #[must_use] + pub const fn write FnCall<(&'a T, hwaddr, u64, u32)>>(mut self, _f: &F) -> Self { + self.0.write = Some(memory_region_ops_write_cb::); + self + } + + #[must_use] + pub const fn big_endian(mut self) -> Self { + self.0.endianness = device_endian::DEVICE_BIG_ENDIAN; + self + } + + #[must_use] + pub const fn little_endian(mut self) -> Self { + self.0.endianness = device_endian::DEVICE_LITTLE_ENDIAN; + self + } + + #[must_use] + pub const fn native_endian(mut self) -> Self { + self.0.endianness = device_endian::DEVICE_NATIVE_ENDIAN; + self + } + + #[must_use] + pub const fn valid_sizes(mut self, min: u32, max: u32) -> Self { + self.0.valid.min_access_size = min; + self.0.valid.max_access_size = max; + self + } + + #[must_use] + pub const fn valid_unaligned(mut self) -> Self { + self.0.valid.unaligned = true; + self + } + + #[must_use] + pub const fn impl_sizes(mut self, min: u32, max: u32) -> Self { + self.0.impl_.min_access_size = min; + self.0.impl_.max_access_size = max; + self + } + + #[must_use] + pub const fn impl_unaligned(mut self) -> Self { + self.0.impl_.unaligned = true; + self + } + + #[must_use] + pub const fn build(self) -> MemoryRegionOps { + MemoryRegionOps::(self.0, PhantomData) + } + + #[must_use] + pub const fn new() -> Self { + Self(bindings::MemoryRegionOps::ZERO, PhantomData) + } +} + +impl Default for MemoryRegionOpsBuilder { + fn default() -> Self { + Self::new() + } +} + +/// A safe wrapper around [`bindings::MemoryRegion`]. +#[repr(transparent)] +#[derive(qemu_api_macros::Wrapper)] +pub struct MemoryRegion(Opaque); + +unsafe impl Send for MemoryRegion {} +unsafe impl Sync for MemoryRegion {} + +impl MemoryRegion { + // inline to ensure that it is not included in tests, which only + // link to hwcore and qom. FIXME: inlining is actually the opposite + // of what we want, since this is the type-erased version of the + // init_io function below. Look into splitting the qemu_api crate. + #[inline(always)] + unsafe fn do_init_io( + slot: *mut bindings::MemoryRegion, + owner: *mut bindings::Object, + ops: &'static bindings::MemoryRegionOps, + name: &'static str, + size: u64, + ) { + unsafe { + let cstr = CString::new(name).unwrap(); + memory_region_init_io( + slot, + owner, + ops, + owner.cast::(), + cstr.as_ptr(), + size, + ); + } + } + + pub fn init_io>( + this: &mut MaybeUninitField<'_, T, Self>, + ops: &'static MemoryRegionOps, + name: &'static str, + size: u64, + ) { + unsafe { + Self::do_init_io( + this.as_mut_ptr().cast(), + MaybeUninitField::parent_mut(this).cast(), + &ops.0, + name, + size, + ); + } + } +} + +unsafe impl ObjectType for MemoryRegion { + type Class = bindings::MemoryRegionClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_MEMORY_REGION) }; +} +qom_isa!(MemoryRegion: Object); + +/// A special `MemTxAttrs` constant, used to indicate that no memory +/// attributes are specified. +/// +/// Bus masters which don't specify any attributes will get this, +/// which has all attribute bits clear except the topmost one +/// (so that we can distinguish "all attributes deliberately clear" +/// from "didn't specify" if necessary). +pub const MEMTXATTRS_UNSPECIFIED: MemTxAttrs = MemTxAttrs { + unspecified: true, + ..Zeroable::ZERO +}; diff --git a/rust/qemu-api/src/module.rs b/rust/qemu-api/src/module.rs new file mode 100644 index 00000000000..fa5cea3598f --- /dev/null +++ b/rust/qemu-api/src/module.rs @@ -0,0 +1,43 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Macro to register blocks of code that run as QEMU starts up. + +#[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 } } + } + }; +} diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs new file mode 100644 index 00000000000..8f9e23ee2c5 --- /dev/null +++ b/rust/qemu-api/src/prelude.rs @@ -0,0 +1,31 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Commonly used traits and types for QEMU. + +pub use crate::bitops::IntegerExt; + +pub use crate::cell::BqlCell; +pub use crate::cell::BqlRefCell; + +pub use crate::errno; + +pub use crate::log_mask_ln; + +pub use crate::qdev::DeviceMethods; + +pub use crate::qom::InterfaceType; +pub use crate::qom::IsA; +pub use crate::qom::Object; +pub use crate::qom::ObjectCast; +pub use crate::qom::ObjectDeref; +pub use crate::qom::ObjectClassMethods; +pub use crate::qom::ObjectMethods; +pub use crate::qom::ObjectType; + +pub use crate::qom_isa; + +pub use crate::sysbus::SysBusDeviceMethods; + +pub use crate::vmstate::VMState; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs new file mode 100644 index 00000000000..36f02fb57db --- /dev/null +++ b/rust/qemu-api/src/qdev.rs @@ -0,0 +1,410 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings to create devices and access device functionality from Rust. + +use std::{ + ffi::{c_int, c_void, CStr, CString}, + ptr::NonNull, +}; + +pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; + +use crate::{ + bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, + callbacks::FnCall, + cell::{bql_locked, Opaque}, + chardev::Chardev, + error::{Error, Result}, + irq::InterruptSource, + prelude::*, + qom::{ObjectClass, ObjectImpl, Owned, ParentInit}, + vmstate::VMStateDescription, +}; + +/// A safe wrapper around [`bindings::Clock`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct Clock(Opaque); + +unsafe impl Send for Clock {} +unsafe impl Sync for Clock {} + +/// A safe wrapper around [`bindings::DeviceState`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct DeviceState(Opaque); + +unsafe impl Send for DeviceState {} +unsafe impl Sync for DeviceState {} + +/// Trait providing the contents of the `ResettablePhases` struct, +/// which is part of the QOM `Resettable` interface. +pub trait ResettablePhasesImpl { + /// If not None, this is called when the object enters reset. It + /// can reset local state of the object, but it must not do anything that + /// has a side-effect on other objects, such as raising or lowering an + /// [`InterruptSource`], or reading or writing guest memory. It takes the + /// reset's type as argument. + const ENTER: Option = None; + + /// If not None, this is called when the object for entry into reset, once + /// every object in the system which is being reset has had its + /// `ResettablePhasesImpl::ENTER` method called. At this point devices + /// can do actions that affect other objects. + /// + /// If in doubt, implement this method. + const HOLD: Option = None; + + /// If not None, this phase is called when the object leaves the reset + /// state. Actions affecting other objects are permitted. + const EXIT: Option = None; +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_resettable_enter_fn( + obj: *mut bindings::Object, + typ: ResetType, +) { + let state = NonNull::new(obj).unwrap().cast::(); + T::ENTER.unwrap()(unsafe { state.as_ref() }, typ); +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_resettable_hold_fn( + obj: *mut bindings::Object, + typ: ResetType, +) { + let state = NonNull::new(obj).unwrap().cast::(); + T::HOLD.unwrap()(unsafe { state.as_ref() }, typ); +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_resettable_exit_fn( + obj: *mut bindings::Object, + typ: ResetType, +) { + let state = NonNull::new(obj).unwrap().cast::(); + T::EXIT.unwrap()(unsafe { state.as_ref() }, typ); +} + +/// Trait providing the contents of [`DeviceClass`]. +pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA { + /// _Realization_ is the second stage of device creation. It contains + /// all operations that depend on device properties and can fail (note: + /// this is not yet supported for Rust devices). + /// + /// If not `None`, the parent class's `realize` method is overridden + /// with the function pointed to by `REALIZE`. + const REALIZE: Option Result<()>> = None; + + /// An array providing the properties that the user can set on the + /// device. Not a `const` because referencing statics in constants + /// is unstable until Rust 1.83.0. + fn properties() -> &'static [Property] { + &[] + } + + /// A `VMStateDescription` providing the migration format for the device + /// Not a `const` because referencing statics in constants is unstable + /// until Rust 1.83.0. + fn vmsd() -> Option<&'static VMStateDescription> { + None + } +} + +/// # Safety +/// +/// This function is only called through the QOM machinery and +/// used by `DeviceClass::class_init`. +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_realize_fn( + dev: *mut bindings::DeviceState, + errp: *mut *mut bindings::Error, +) { + let state = NonNull::new(dev).unwrap().cast::(); + let result = T::REALIZE.unwrap()(unsafe { state.as_ref() }); + unsafe { + Error::ok_or_propagate(result, errp); + } +} + +unsafe impl InterfaceType for ResettableClass { + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_RESETTABLE_INTERFACE) }; +} + +impl ResettableClass { + /// Fill in the virtual methods of `ResettableClass` based on the + /// definitions in the `ResettablePhasesImpl` trait. + pub fn class_init(&mut self) { + if ::ENTER.is_some() { + self.phases.enter = Some(rust_resettable_enter_fn::); + } + if ::HOLD.is_some() { + self.phases.hold = Some(rust_resettable_hold_fn::); + } + if ::EXIT.is_some() { + self.phases.exit = Some(rust_resettable_exit_fn::); + } + } +} + +impl DeviceClass { + /// Fill in the virtual methods of `DeviceClass` based on the definitions in + /// the `DeviceImpl` trait. + pub fn class_init(&mut self) { + if ::REALIZE.is_some() { + self.realize = Some(rust_realize_fn::); + } + if let Some(vmsd) = ::vmsd() { + self.vmsd = vmsd; + } + let prop = ::properties(); + if !prop.is_empty() { + unsafe { + bindings::device_class_set_props_n(self, prop.as_ptr(), prop.len()); + } + } + + ResettableClass::cast::(self).class_init::(); + self.parent_class.class_init::(); + } +} + +#[macro_export] +macro_rules! define_property { + ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, bit = $bitnr:expr, default = $defval:expr$(,)*) => { + $crate::bindings::Property { + // use associated function syntax for type checking + name: ::std::ffi::CStr::as_ptr($name), + info: $prop, + offset: ::std::mem::offset_of!($state, $field) as isize, + bitnr: $bitnr, + 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:ty, default = $defval:expr$(,)*) => { + $crate::bindings::Property { + // use associated function syntax for type checking + name: ::std::ffi::CStr::as_ptr($name), + info: $prop, + offset: ::std::mem::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:ty$(,)*) => { + $crate::bindings::Property { + // use associated function syntax for type checking + name: ::std::ffi::CStr::as_ptr($name), + info: $prop, + offset: ::std::mem::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 = 0; + $({ + _ = stringify!($prop); + len += 1; + })* + len + }] = [ + $($prop),*, + ]; + }; +} + +unsafe impl ObjectType for DeviceState { + type Class = DeviceClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; +} +qom_isa!(DeviceState: Object); + +/// Initialization methods take a [`ParentInit`] and can be called as +/// associated functions. +impl DeviceState { + /// Add an input clock named `name`. Invoke the callback with + /// `self` as the first parameter for the events that are requested. + /// + /// The resulting clock is added as a child of `self`, but it also + /// stays alive until after `Drop::drop` is called because C code + /// keeps an extra reference to it until `device_finalize()` calls + /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in + /// which Rust code has a reference to a child object) it would be + /// possible for this function to return a `&Clock` too. + #[inline] + pub fn init_clock_in FnCall<(&'a T, ClockEvent)>>( + this: &mut ParentInit, + name: &str, + _cb: &F, + events: ClockEvent, + ) -> Owned + where + T::ParentType: IsA, + { + fn do_init_clock_in( + dev: &DeviceState, + name: &str, + cb: Option, + events: ClockEvent, + ) -> Owned { + assert!(bql_locked()); + + // SAFETY: the clock is heap allocated, but qdev_init_clock_in() + // does not gift the reference to its caller; so use Owned::from to + // add one. The callback is disabled automatically when the clock + // is unparented, which happens before the device is finalized. + unsafe { + let cstr = CString::new(name).unwrap(); + let clk = bindings::qdev_init_clock_in( + dev.0.as_mut_ptr(), + cstr.as_ptr(), + cb, + dev.0.as_void_ptr(), + events.0, + ); + + let clk: &Clock = Clock::from_raw(clk); + Owned::from(clk) + } + } + + let cb: Option = if F::is_some() { + unsafe extern "C" fn rust_clock_cb FnCall<(&'a T, ClockEvent)>>( + opaque: *mut c_void, + event: ClockEvent, + ) { + // SAFETY: the opaque is "this", which is indeed a pointer to T + F::call((unsafe { &*(opaque.cast::()) }, event)) + } + Some(rust_clock_cb::) + } else { + None + }; + + do_init_clock_in(unsafe { this.upcast_mut() }, name, cb, events) + } + + /// Add an output clock named `name`. + /// + /// The resulting clock is added as a child of `self`, but it also + /// stays alive until after `Drop::drop` is called because C code + /// keeps an extra reference to it until `device_finalize()` calls + /// `qdev_finalize_clocklist()`. Therefore (unlike most cases in + /// which Rust code has a reference to a child object) it would be + /// possible for this function to return a `&Clock` too. + #[inline] + pub fn init_clock_out(this: &mut ParentInit, name: &str) -> Owned + where + T::ParentType: IsA, + { + unsafe { + let cstr = CString::new(name).unwrap(); + let dev: &mut DeviceState = this.upcast_mut(); + let clk = bindings::qdev_init_clock_out(dev.0.as_mut_ptr(), cstr.as_ptr()); + + let clk: &Clock = Clock::from_raw(clk); + Owned::from(clk) + } + } +} + +/// Trait for methods exposed by the [`DeviceState`] class. The methods can be +/// called on all objects that have the trait `IsA`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA`. +pub trait DeviceMethods: ObjectDeref +where + Self::Target: IsA, +{ + fn prop_set_chr(&self, propname: &str, chr: &Owned) { + assert!(bql_locked()); + let c_propname = CString::new(propname).unwrap(); + let chr: &Chardev = chr; + unsafe { + bindings::qdev_prop_set_chr( + self.upcast().as_mut_ptr(), + c_propname.as_ptr(), + chr.as_mut_ptr(), + ); + } + } + + fn init_gpio_in FnCall<(&'a Self::Target, u32, u32)>>( + &self, + num_lines: u32, + _cb: F, + ) { + fn do_init_gpio_in( + dev: &DeviceState, + num_lines: u32, + gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int), + ) { + unsafe { + qdev_init_gpio_in(dev.as_mut_ptr(), Some(gpio_in_cb), num_lines as c_int); + } + } + + let _: () = F::ASSERT_IS_SOME; + unsafe extern "C" fn rust_irq_handler FnCall<(&'a T, u32, u32)>>( + opaque: *mut c_void, + line: c_int, + level: c_int, + ) { + // SAFETY: the opaque was passed as a reference to `T` + F::call((unsafe { &*(opaque.cast::()) }, line as u32, level as u32)) + } + + let gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int) = + rust_irq_handler::; + + do_init_gpio_in(self.upcast(), num_lines, gpio_in_cb); + } + + fn init_gpio_out(&self, pins: &[InterruptSource]) { + unsafe { + qdev_init_gpio_out( + self.upcast().as_mut_ptr(), + InterruptSource::slice_as_ptr(pins), + pins.len() as c_int, + ); + } + } +} + +impl DeviceMethods for R where R::Target: IsA {} + +unsafe impl ObjectType for Clock { + type Class = ObjectClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_CLOCK) }; +} +qom_isa!(Clock: Object); diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs new file mode 100644 index 00000000000..e20ee014cb1 --- /dev/null +++ b/rust/qemu-api/src/qom.rs @@ -0,0 +1,950 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings to access QOM functionality from Rust. +//! +//! The QEMU Object Model (QOM) provides inheritance and dynamic typing for QEMU +//! devices. This module makes QOM's features available in Rust through three +//! main mechanisms: +//! +//! * Automatic creation and registration of `TypeInfo` for classes that are +//! written in Rust, as well as mapping between Rust traits and QOM vtables. +//! +//! * Type-safe casting between parent and child classes, through the [`IsA`] +//! trait and methods such as [`upcast`](ObjectCast::upcast) and +//! [`downcast`](ObjectCast::downcast). +//! +//! * Automatic delegation of parent class methods to child classes. When a +//! trait uses [`IsA`] as a bound, its contents become available to all child +//! classes through blanket implementations. This works both for class methods +//! and for instance methods accessed through references or smart pointers. +//! +//! # Structure of a class +//! +//! A leaf class only needs a struct holding instance state. The struct must +//! implement the [`ObjectType`] and [`IsA`] traits, as well as any `*Impl` +//! traits that exist for its superclasses. +//! +//! If a class has subclasses, it will also provide a struct for instance data, +//! with the same characteristics as for concrete classes, but it also needs +//! additional components to support virtual methods: +//! +//! * a struct for class data, for example `DeviceClass`. This corresponds to +//! the C "class struct" and holds the vtable that is used by instances of the +//! class and its subclasses. It must start with its parent's class struct. +//! +//! * a trait for virtual method implementations, for example `DeviceImpl`. +//! Child classes implement this trait to provide their own behavior for +//! virtual methods. The trait's methods take `&self` to access instance data. +//! The traits have the appropriate specialization of `IsA<>` as a supertrait, +//! for example `IsA` for `DeviceImpl`. +//! +//! * a trait for instance methods, for example `DeviceMethods`. This trait is +//! automatically implemented for any reference or smart pointer to a device +//! instance. It calls into the vtable provides access across all subclasses +//! to methods defined for the class. +//! +//! * optionally, a trait for class methods, for example `DeviceClassMethods`. +//! This provides access to class-wide functionality that doesn't depend on +//! instance data. Like instance methods, these are automatically inherited by +//! child classes. +//! +//! # Class structures +//! +//! Each QOM class that has virtual methods describes them in a +//! _class struct_. Class structs include a parent field corresponding +//! to the vtable of the parent class, all the way up to [`ObjectClass`]. +//! +//! As mentioned above, virtual methods are defined via traits such as +//! `DeviceImpl`. Class structs do not define any trait but, conventionally, +//! all of them have a `class_init` method to initialize the virtual methods +//! based on the trait and then call the same method on the superclass. +//! +//! ```ignore +//! impl YourSubclassClass +//! { +//! pub fn class_init(&mut self) { +//! ... +//! klass.parent_class::class_init(); +//! } +//! } +//! ``` +//! +//! If a class implements a QOM interface. In that case, the function must +//! contain, for each interface, an extra forwarding call as follows: +//! +//! ```ignore +//! ResettableClass::cast::(self).class_init::(); +//! ``` +//! +//! These `class_init` functions are methods on the class rather than a trait, +//! because the bound on `T` (`DeviceImpl` in this case), will change for every +//! class struct. The functions are pointed to by the +//! [`ObjectImpl::CLASS_INIT`] function pointer. While there is no default +//! implementation, in most cases it will be enough to write it as follows: +//! +//! ```ignore +//! const CLASS_INIT: fn(&mut Self::Class)> = Self::Class::class_init::; +//! ``` +//! +//! This design incurs a small amount of code duplication but, by not using +//! traits, it allows the flexibility of implementing bindings in any crate, +//! without incurring into violations of orphan rules for traits. + +use std::{ + ffi::{c_void, CStr}, + fmt, + marker::PhantomData, + mem::{ManuallyDrop, MaybeUninit}, + ops::{Deref, DerefMut}, + ptr::NonNull, +}; + +pub use bindings::ObjectClass; + +use crate::{ + bindings::{ + self, object_class_dynamic_cast, object_dynamic_cast, object_get_class, + object_get_typename, object_new, object_ref, object_unref, TypeInfo, + }, + cell::{bql_locked, Opaque}, +}; + +/// A safe wrapper around [`bindings::Object`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct Object(Opaque); + +unsafe impl Send for Object {} +unsafe impl Sync for Object {} + +/// Marker trait: `Self` can be statically upcasted to `P` (i.e. `P` is a direct +/// or indirect parent of `Self`). +/// +/// # Safety +/// +/// The struct `Self` must be `#[repr(C)]` and must begin, directly or +/// indirectly, with a field of type `P`. This ensures that invalid casts, +/// which rely on `IsA<>` for static checking, are rejected at compile time. +pub unsafe trait IsA: ObjectType {} + +// SAFETY: it is always safe to cast to your own type +unsafe impl IsA for T {} + +/// Macro to mark superclasses of QOM classes. This enables type-safe +/// up- and downcasting. +/// +/// # Safety +/// +/// This macro is a thin wrapper around the [`IsA`] trait and performs +/// no checking whatsoever of what is declared. It is the caller's +/// responsibility to have $struct begin, directly or indirectly, with +/// a field of type `$parent`. +#[macro_export] +macro_rules! qom_isa { + ($struct:ty : $($parent:ty),* ) => { + $( + // SAFETY: it is the caller responsibility to have $parent as the + // first field + unsafe impl $crate::qom::IsA<$parent> for $struct {} + + impl AsRef<$parent> for $struct { + fn as_ref(&self) -> &$parent { + // SAFETY: follows the same rules as for IsA, which is + // declared above. + let ptr: *const Self = self; + unsafe { &*ptr.cast::<$parent>() } + } + } + )* + }; +} + +/// This is the same as [`ManuallyDrop`](std::mem::ManuallyDrop), though +/// it hides the standard methods of `ManuallyDrop`. +/// +/// The first field of an `ObjectType` must be of type `ParentField`. +/// (Technically, this is only necessary if there is at least one Rust +/// superclass in the hierarchy). This is to ensure that the parent field is +/// dropped after the subclass; this drop order is enforced by the C +/// `object_deinit` function. +/// +/// # Examples +/// +/// ```ignore +/// #[repr(C)] +/// #[derive(qemu_api_macros::Object)] +/// pub struct MyDevice { +/// parent: ParentField, +/// ... +/// } +/// ``` +#[derive(Debug)] +#[repr(transparent)] +pub struct ParentField(std::mem::ManuallyDrop); + +impl Deref for ParentField { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ParentField { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl fmt::Display for ParentField { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} + +/// This struct knows that the superclasses of the object have already been +/// initialized. +/// +/// The declaration of `ParentInit` is.. *"a kind of magic"*. It uses a +/// technique that is found in several crates, the main ones probably being +/// `ghost-cell` (in fact it was introduced by the [`GhostCell` paper](https://plv.mpi-sws.org/rustbelt/ghostcell/)) +/// and `generativity`. +/// +/// The `PhantomData` makes the `ParentInit` type *invariant* with respect to +/// the lifetime argument `'init`. This, together with the `for<'...>` in +/// `[ParentInit::with]`, block any attempt of the compiler to be creative when +/// operating on types of type `ParentInit` and to extend their lifetimes. In +/// particular, it ensures that the `ParentInit` cannot be made to outlive the +/// `rust_instance_init()` function that creates it, and therefore that the +/// `&'init T` reference is valid. +/// +/// This implementation of the same concept, without the QOM baggage, can help +/// understanding the effect: +/// +/// ``` +/// use std::marker::PhantomData; +/// +/// #[derive(PartialEq, Eq)] +/// pub struct Jail<'closure, T: Copy>(&'closure T, PhantomData &'closure ()>); +/// +/// impl<'closure, T: Copy> Jail<'closure, T> { +/// fn get(&self) -> T { +/// *self.0 +/// } +/// +/// #[inline] +/// fn with(v: T, f: impl for<'id> FnOnce(Jail<'id, T>) -> U) -> U { +/// let parent_init = Jail(&v, PhantomData); +/// f(parent_init) +/// } +/// } +/// ``` +/// +/// It's impossible to escape the `Jail`; `token1` cannot be moved out of the +/// closure: +/// +/// ```ignore +/// let x = 42; +/// let escape = Jail::with(&x, |token1| { +/// println!("{}", token1.get()); +/// // fails to compile... +/// token1 +/// }); +/// // ... so you cannot do this: +/// println!("{}", escape.get()); +/// ``` +/// +/// Likewise, in the QOM case the `ParentInit` cannot be moved out of +/// `instance_init()`. Without this trick it would be possible to stash a +/// `ParentInit` and use it later to access uninitialized memory. +/// +/// Here is another example, showing how separately-created "identities" stay +/// isolated: +/// +/// ```ignore +/// impl<'closure, T: Copy> Clone for Jail<'closure, T> { +/// fn clone(&self) -> Jail<'closure, T> { +/// Jail(self.0, PhantomData) +/// } +/// } +/// +/// fn main() { +/// Jail::with(42, |token1| { +/// // this works and returns true: the clone has the same "identity" +/// println!("{}", token1 == token1.clone()); +/// Jail::with(42, |token2| { +/// // here the outer token remains accessible... +/// println!("{}", token1.get()); +/// // ... but the two are separate: this fails to compile: +/// println!("{}", token1 == token2); +/// }); +/// }); +/// } +/// ``` +pub struct ParentInit<'init, T>( + &'init mut MaybeUninit, + PhantomData &'init ()>, +); + +impl<'init, T> ParentInit<'init, T> { + #[inline] + pub fn with(obj: &'init mut MaybeUninit, f: impl for<'id> FnOnce(ParentInit<'id, T>)) { + let parent_init = ParentInit(obj, PhantomData); + f(parent_init) + } +} + +impl ParentInit<'_, T> { + /// Return the receiver as a mutable raw pointer to Object. + /// + /// # Safety + /// + /// Fields beyond `Object` could be uninitialized and it's your + /// responsibility to avoid that they're used when the pointer is + /// dereferenced, either directly or through a cast. + pub fn as_object_mut_ptr(&self) -> *mut bindings::Object { + self.as_object_ptr().cast_mut() + } + + /// Return the receiver as a mutable raw pointer to Object. + /// + /// # Safety + /// + /// Fields beyond `Object` could be uninitialized and it's your + /// responsibility to avoid that they're used when the pointer is + /// dereferenced, either directly or through a cast. + pub fn as_object_ptr(&self) -> *const bindings::Object { + self.0.as_ptr().cast() + } +} + +impl<'a, T: ObjectImpl> ParentInit<'a, T> { + /// Convert from a derived type to one of its parent types, which + /// have already been initialized. + /// + /// # Safety + /// + /// Structurally this is always a safe operation; the [`IsA`] trait + /// provides static verification trait that `Self` dereferences to `U` or + /// a child of `U`, and only parent types of `T` are allowed. + /// + /// However, while the fields of the resulting reference are initialized, + /// calls might use uninitialized fields of the subclass. It is your + /// responsibility to avoid this. + pub unsafe fn upcast(&self) -> &'a U + where + T::ParentType: IsA, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait; + // the parent has been initialized before `instance_init `is called + unsafe { &*(self.0.as_ptr().cast::()) } + } + + /// Convert from a derived type to one of its parent types, which + /// have already been initialized. + /// + /// # Safety + /// + /// Structurally this is always a safe operation; the [`IsA`] trait + /// provides static verification trait that `Self` dereferences to `U` or + /// a child of `U`, and only parent types of `T` are allowed. + /// + /// However, while the fields of the resulting reference are initialized, + /// calls might use uninitialized fields of the subclass. It is your + /// responsibility to avoid this. + pub unsafe fn upcast_mut(&mut self) -> &'a mut U + where + T::ParentType: IsA, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait; + // the parent has been initialized before `instance_init `is called + unsafe { &mut *(self.0.as_mut_ptr().cast::()) } + } +} + +impl Deref for ParentInit<'_, T> { + type Target = MaybeUninit; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl DerefMut for ParentInit<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } +} + +unsafe extern "C" fn rust_instance_init(obj: *mut bindings::Object) { + let mut state = NonNull::new(obj).unwrap().cast::>(); + + // SAFETY: obj is an instance of T, since rust_instance_init + // is called from QOM core as the instance_init function + // for class T + unsafe { + ParentInit::with(state.as_mut(), |parent_init| { + T::INSTANCE_INIT.unwrap()(parent_init); + }); + } +} + +unsafe extern "C" fn rust_instance_post_init(obj: *mut bindings::Object) { + let state = NonNull::new(obj).unwrap().cast::(); + // SAFETY: obj is an instance of T, since rust_instance_post_init + // is called from QOM core as the instance_post_init function + // for class T + T::INSTANCE_POST_INIT.unwrap()(unsafe { state.as_ref() }); +} + +unsafe extern "C" fn rust_class_init( + klass: *mut ObjectClass, + _data: *const c_void, +) { + let mut klass = NonNull::new(klass) + .unwrap() + .cast::<::Class>(); + // SAFETY: klass is a T::Class, since rust_class_init + // is called from QOM core as the class_init function + // for class T + ::CLASS_INIT(unsafe { klass.as_mut() }) +} + +unsafe extern "C" fn drop_object(obj: *mut bindings::Object) { + // SAFETY: obj is an instance of T, since drop_object is called + // from the QOM core function object_deinit() as the instance_finalize + // function for class T. Note that while object_deinit() will drop the + // superclass field separately after this function returns, `T` must + // implement the unsafe trait ObjectType; the safety rules for the + // trait mandate that the parent field is manually dropped. + unsafe { std::ptr::drop_in_place(obj.cast::()) } +} + +/// Trait exposed by all structs corresponding to QOM objects. +/// +/// # Safety +/// +/// For classes declared in C: +/// +/// - `Class` and `TYPE` must match the data in the `TypeInfo`; +/// +/// - the first field of the struct must be of the instance type corresponding +/// to the superclass, as declared in the `TypeInfo` +/// +/// - likewise, the first field of the `Class` struct must be of the class type +/// corresponding to the superclass +/// +/// For classes declared in Rust and implementing [`ObjectImpl`]: +/// +/// - the struct must be `#[repr(C)]`; +/// +/// - the first field of the struct must be of type +/// [`ParentField`](ParentField), where `T` is the parent type +/// [`ObjectImpl::ParentType`] +/// +/// - the first field of the `Class` must be of the class struct corresponding +/// to the superclass, which is `ObjectImpl::ParentType::Class`. `ParentField` +/// is not needed here. +/// +/// In both cases, having a separate class type is not necessary if the subclass +/// does not add any field. +pub unsafe trait ObjectType: Sized { + /// The QOM class object corresponding to this struct. This is used + /// to automatically generate a `class_init` method. + type Class; + + /// The name of the type, which can be passed to `object_new()` to + /// generate an instance of this type. + const TYPE_NAME: &'static CStr; + + /// Return the receiver as an Object. This is always safe, even + /// if this type represents an interface. + fn as_object(&self) -> &Object { + unsafe { &*self.as_ptr().cast() } + } + + /// Return the receiver as a const raw pointer to Object. + /// This is preferable to `as_object_mut_ptr()` if a C + /// function only needs a `const Object *`. + fn as_object_ptr(&self) -> *const bindings::Object { + self.as_object().as_ptr() + } + + /// Return the receiver as a mutable raw pointer to Object. + /// + /// # Safety + /// + /// This cast is always safe, but because the result is mutable + /// and the incoming reference is not, this should only be used + /// for calls to C functions, and only if needed. + unsafe fn as_object_mut_ptr(&self) -> *mut bindings::Object { + self.as_object().as_mut_ptr() + } +} + +/// Trait exposed by all structs corresponding to QOM interfaces. +/// Unlike `ObjectType`, it is implemented on the class type (which provides +/// the vtable for the interfaces). +/// +/// # Safety +/// +/// `TYPE` must match the contents of the `TypeInfo` as found in the C code; +/// right now, interfaces can only be declared in C. +pub unsafe trait InterfaceType: Sized { + /// The name of the type, which can be passed to + /// `object_class_dynamic_cast()` to obtain the pointer to the vtable + /// for this interface. + const TYPE_NAME: &'static CStr; + + /// Return the vtable for the interface; `U` is the type that + /// lists the interface in its `TypeInfo`. + /// + /// # Examples + /// + /// This function is usually called by a `class_init` method in `U::Class`. + /// For example, `DeviceClass::class_init` initializes its `Resettable` + /// interface as follows: + /// + /// ```ignore + /// ResettableClass::cast::(self).class_init::(); + /// ``` + /// + /// where `T` is the concrete subclass that is being initialized. + /// + /// # Panics + /// + /// Panic if the incoming argument if `T` does not implement the interface. + fn cast(klass: &mut U::Class) -> &mut Self { + unsafe { + // SAFETY: upcasting to ObjectClass is always valid, and the + // return type is either NULL or the argument itself + let result: *mut Self = object_class_dynamic_cast( + (klass as *mut U::Class).cast(), + Self::TYPE_NAME.as_ptr(), + ) + .cast(); + result.as_mut().unwrap() + } + } +} + +/// This trait provides safe casting operations for QOM objects to raw pointers, +/// to be used for example for FFI. The trait can be applied to any kind of +/// reference or smart pointers, and enforces correctness through the [`IsA`] +/// trait. +pub trait ObjectDeref: Deref +where + Self::Target: ObjectType, +{ + /// Convert to a const Rust pointer, to be used for example for FFI. + /// The target pointer type must be the type of `self` or a superclass + fn as_ptr(&self) -> *const U + where + Self::Target: IsA, + { + let ptr: *const Self::Target = self.deref(); + ptr.cast::() + } + + /// Convert to a mutable Rust pointer, to be used for example for FFI. + /// The target pointer type must be the type of `self` or a superclass. + /// Used to implement interior mutability for objects. + /// + /// # Safety + /// + /// This method is safe because only the actual dereference of the pointer + /// has to be unsafe. Bindings to C APIs will use it a lot, but care has + /// to be taken because it overrides the const-ness of `&self`. + fn as_mut_ptr(&self) -> *mut U + where + Self::Target: IsA, + { + #[allow(clippy::as_ptr_cast_mut)] + { + self.as_ptr::().cast_mut() + } + } +} + +/// Trait that adds extra functionality for `&T` where `T` is a QOM +/// object type. Allows conversion to/from C objects in generic code. +pub trait ObjectCast: ObjectDeref + Copy +where + Self::Target: ObjectType, +{ + /// Safely convert from a derived type to one of its parent types. + /// + /// This is always safe; the [`IsA`] trait provides static verification + /// trait that `Self` dereferences to `U` or a child of `U`. + fn upcast<'a, U: ObjectType>(self) -> &'a U + where + Self::Target: IsA, + Self: 'a, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait + unsafe { self.unsafe_cast::() } + } + + /// Attempt to convert to a derived type. + /// + /// Returns `None` if the object is not actually of type `U`. This is + /// verified at runtime by checking the object's type information. + fn downcast<'a, U: IsA>(self) -> Option<&'a U> + where + Self: 'a, + { + self.dynamic_cast::() + } + + /// Attempt to convert between any two types in the QOM hierarchy. + /// + /// Returns `None` if the object is not actually of type `U`. This is + /// verified at runtime by checking the object's type information. + fn dynamic_cast<'a, U: ObjectType>(self) -> Option<&'a U> + where + Self: 'a, + { + unsafe { + // SAFETY: upcasting to Object is always valid, and the + // return type is either NULL or the argument itself + let result: *const U = + object_dynamic_cast(self.as_object_mut_ptr(), U::TYPE_NAME.as_ptr()).cast(); + + result.as_ref() + } + } + + /// Convert to any QOM type without verification. + /// + /// # Safety + /// + /// What safety? You need to know yourself that the cast is correct; only + /// use when performance is paramount. It is still better than a raw + /// pointer `cast()`, which does not even check that you remain in the + /// realm of QOM `ObjectType`s. + /// + /// `unsafe_cast::()` is always safe. + unsafe fn unsafe_cast<'a, U: ObjectType>(self) -> &'a U + where + Self: 'a, + { + unsafe { &*(self.as_ptr::().cast::()) } + } +} + +impl ObjectDeref for &T {} +impl ObjectCast for &T {} + +impl ObjectDeref for &mut T {} + +/// Trait a type must implement to be registered with QEMU. +pub trait ObjectImpl: ObjectType + IsA { + /// The parent of the type. This should match the first field of the + /// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper. + type ParentType: ObjectType; + + /// Whether the object can be instantiated + const ABSTRACT: bool = false; + + /// Function that is called to initialize an object. The parent class will + /// have already been initialized so the type is only responsible for + /// initializing its own members. + /// + /// FIXME: The argument is not really a valid reference. `&mut + /// MaybeUninit` would be a better description. + const INSTANCE_INIT: Option)> = None; + + /// Function that is called to finish initialization of an object, once + /// `INSTANCE_INIT` functions have been called. + const INSTANCE_POST_INIT: Option = None; + + /// Called on descendant classes after all parent class initialization + /// has occurred, but before the class itself is initialized. This + /// is only useful if a class is not a leaf, and can be used to undo + /// the effects of copying the contents of the parent's class struct + /// to the descendants. + const CLASS_BASE_INIT: Option< + unsafe extern "C" fn(klass: *mut ObjectClass, data: *const c_void), + > = None; + + const TYPE_INFO: TypeInfo = TypeInfo { + name: Self::TYPE_NAME.as_ptr(), + parent: Self::ParentType::TYPE_NAME.as_ptr(), + instance_size: core::mem::size_of::(), + instance_align: core::mem::align_of::(), + instance_init: match Self::INSTANCE_INIT { + None => None, + Some(_) => Some(rust_instance_init::), + }, + instance_post_init: match Self::INSTANCE_POST_INIT { + None => None, + Some(_) => Some(rust_instance_post_init::), + }, + instance_finalize: Some(drop_object::), + abstract_: Self::ABSTRACT, + class_size: core::mem::size_of::(), + class_init: Some(rust_class_init::), + class_base_init: Self::CLASS_BASE_INIT, + class_data: core::ptr::null(), + interfaces: core::ptr::null(), + }; + + // methods on ObjectClass + const UNPARENT: Option = None; + + /// Store into the argument the virtual method implementations + /// for `Self`. On entry, the virtual method pointers are set to + /// the default values coming from the parent classes; the function + /// can change them to override virtual methods of a parent class. + /// + /// Usually defined simply as `Self::Class::class_init::`; + /// however a default implementation cannot be included here, because the + /// bounds that the `Self::Class::class_init` method places on `Self` are + /// not known in advance. + /// + /// # Safety + /// + /// While `klass`'s parent class is initialized on entry, the other fields + /// are all zero; it is therefore assumed that all fields in `T` can be + /// zeroed, otherwise it would not be possible to provide the class as a + /// `&mut T`. TODO: it may be possible to add an unsafe trait that checks + /// that all fields *after the parent class* (but not the parent class + /// itself) are Zeroable. This unsafe trait can be added via a derive + /// macro. + const CLASS_INIT: fn(&mut Self::Class); +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer that +/// can be downcasted to type `T`. We also expect the device is +/// readable/writeable from one thread at any time. +unsafe extern "C" fn rust_unparent_fn(dev: *mut bindings::Object) { + let state = NonNull::new(dev).unwrap().cast::(); + T::UNPARENT.unwrap()(unsafe { state.as_ref() }); +} + +impl ObjectClass { + /// Fill in the virtual methods of `ObjectClass` based on the definitions in + /// the `ObjectImpl` trait. + pub fn class_init(&mut self) { + if ::UNPARENT.is_some() { + self.unparent = Some(rust_unparent_fn::); + } + } +} + +unsafe impl ObjectType for Object { + type Class = ObjectClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_OBJECT) }; +} + +/// A reference-counted pointer to a QOM object. +/// +/// `Owned` wraps `T` with automatic reference counting. It increases the +/// reference count when created via [`Owned::from`] or cloned, and decreases +/// it when dropped. This ensures that the reference count remains elevated +/// as long as any `Owned` references to it exist. +/// +/// `Owned` can be used for two reasons: +/// * because the lifetime of the QOM object is unknown and someone else could +/// take a reference (similar to `Arc`, for example): in this case, the +/// object can escape and outlive the Rust struct that contains the `Owned` +/// field; +/// +/// * to ensure that the object stays alive until after `Drop::drop` is called +/// on the Rust struct: in this case, the object will always die together with +/// the Rust struct that contains the `Owned` field. +/// +/// Child properties are an example of the second case: in C, an object that +/// is created with `object_initialize_child` will die *before* +/// `instance_finalize` is called, whereas Rust expects the struct to have valid +/// contents when `Drop::drop` is called. Therefore Rust structs that have +/// child properties need to keep a reference to the child object. Right now +/// this can be done with `Owned`; in the future one might have a separate +/// `Child<'parent, T>` smart pointer that keeps a reference to a `T`, like +/// `Owned`, but does not allow cloning. +/// +/// Note that dropping an `Owned` requires the big QEMU lock to be taken. +#[repr(transparent)] +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Owned(NonNull); + +// The following rationale for safety is taken from Linux's kernel::sync::Arc. + +// SAFETY: It is safe to send `Owned` to another thread when the underlying +// `T` is `Sync` because it effectively means sharing `&T` (which is safe +// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any +// thread that has an `Owned` may ultimately access `T` using a +// mutable reference when the reference count reaches zero and `T` is dropped. +unsafe impl Send for Owned {} + +// SAFETY: It is safe to send `&Owned` to another thread when the underlying +// `T` is `Sync` because it effectively means sharing `&T` (which is safe +// because `T` is `Sync`); additionally, it needs `T` to be `Send` because any +// thread that has a `&Owned` may clone it and get an `Owned` on that +// thread, so the thread may ultimately access `T` using a mutable reference +// when the reference count reaches zero and `T` is dropped. +unsafe impl Sync for Owned {} + +impl Owned { + /// Convert a raw C pointer into an owned reference to the QOM + /// object it points to. The object's reference count will be + /// decreased when the `Owned` is dropped. + /// + /// # Panics + /// + /// Panics if `ptr` is NULL. + /// + /// # Safety + /// + /// The caller must indeed own a reference to the QOM object. + /// The object must not be embedded in another unless the outer + /// object is guaranteed to have a longer lifetime. + /// + /// A raw pointer obtained via [`Owned::into_raw()`] can always be passed + /// back to `from_raw()` (assuming the original `Owned` was valid!), + /// since the owned reference remains there between the calls to + /// `into_raw()` and `from_raw()`. + pub unsafe fn from_raw(ptr: *const T) -> Self { + // SAFETY NOTE: while NonNull requires a mutable pointer, only + // Deref is implemented so the pointer passed to from_raw + // remains const + Owned(NonNull::new(ptr.cast_mut()).unwrap()) + } + + /// Obtain a raw C pointer from a reference. `src` is consumed + /// and the reference is leaked. + #[allow(clippy::missing_const_for_fn)] + pub fn into_raw(src: Owned) -> *mut T { + let src = ManuallyDrop::new(src); + src.0.as_ptr() + } + + /// Increase the reference count of a QOM object and return + /// a new owned reference to it. + /// + /// # Safety + /// + /// The object must not be embedded in another, unless the outer + /// object is guaranteed to have a longer lifetime. + pub unsafe fn from(obj: &T) -> Self { + unsafe { + object_ref(obj.as_object_mut_ptr().cast::()); + + // SAFETY NOTE: while NonNull requires a mutable pointer, only + // Deref is implemented so the reference passed to from_raw + // remains shared + Owned(NonNull::new_unchecked(obj.as_mut_ptr())) + } + } +} + +impl Clone for Owned { + fn clone(&self) -> Self { + // SAFETY: creation method is unsafe; whoever calls it has + // responsibility that the pointer is valid, and remains valid + // throughout the lifetime of the `Owned` and its clones. + unsafe { Owned::from(self.deref()) } + } +} + +impl Deref for Owned { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: creation method is unsafe; whoever calls it has + // responsibility that the pointer is valid, and remains valid + // throughout the lifetime of the `Owned` and its clones. + // With that guarantee, reference counting ensures that + // the object remains alive. + unsafe { &*self.0.as_ptr() } + } +} +impl ObjectDeref for Owned {} + +impl Drop for Owned { + fn drop(&mut self) { + assert!(bql_locked()); + // SAFETY: creation method is unsafe, and whoever calls it has + // responsibility that the pointer is valid, and remains valid + // throughout the lifetime of the `Owned` and its clones. + unsafe { + object_unref(self.as_object_mut_ptr().cast::()); + } + } +} + +impl> fmt::Debug for Owned { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.deref().debug_fmt(f) + } +} + +/// Trait for class methods exposed by the Object class. The methods can be +/// called on all objects that have the trait `IsA`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA` +pub trait ObjectClassMethods: IsA { + /// Return a new reference counted instance of this class + fn new() -> Owned { + assert!(bql_locked()); + // SAFETY: the object created by object_new is allocated on + // the heap and has a reference count of 1 + unsafe { + let raw_obj = object_new(Self::TYPE_NAME.as_ptr()); + let obj = Object::from_raw(raw_obj).unsafe_cast::(); + Owned::from_raw(obj) + } + } +} + +/// Trait for methods exposed by the Object class. The methods can be +/// called on all objects that have the trait `IsA`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA` +pub trait ObjectMethods: ObjectDeref +where + Self::Target: IsA, +{ + /// Return the name of the type of `self` + fn typename(&self) -> std::borrow::Cow<'_, str> { + let obj = self.upcast::(); + // SAFETY: safety of this is the requirement for implementing IsA + // The result of the C API has static lifetime + unsafe { + let p = object_get_typename(obj.as_mut_ptr()); + CStr::from_ptr(p).to_string_lossy() + } + } + + fn get_class(&self) -> &'static ::Class { + let obj = self.upcast::(); + + // SAFETY: all objects can call object_get_class; the actual class + // type is guaranteed by the implementation of `ObjectType` and + // `ObjectImpl`. + let klass: &'static ::Class = + unsafe { &*object_get_class(obj.as_mut_ptr()).cast() }; + + klass + } + + /// Convenience function for implementing the Debug trait + fn debug_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple(&self.typename()) + .field(&(self as *const Self)) + .finish() + } +} + +impl ObjectClassMethods for T where T: IsA {} +impl ObjectMethods for R where R::Target: IsA {} diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs new file mode 100644 index 00000000000..e92502a8fe6 --- /dev/null +++ b/rust/qemu-api/src/sysbus.rs @@ -0,0 +1,122 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings to access `sysbus` functionality from Rust. + +use std::{ffi::CStr, ptr::addr_of_mut}; + +pub use bindings::SysBusDeviceClass; + +use crate::{ + bindings, + cell::{bql_locked, Opaque}, + irq::{IRQState, InterruptSource}, + memory::MemoryRegion, + prelude::*, + qdev::{DeviceImpl, DeviceState}, + qom::Owned, +}; + +/// A safe wrapper around [`bindings::SysBusDevice`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct SysBusDevice(Opaque); + +unsafe impl Send for SysBusDevice {} +unsafe impl Sync for SysBusDevice {} + +unsafe impl ObjectType for SysBusDevice { + type Class = SysBusDeviceClass; + const TYPE_NAME: &'static CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) }; +} +qom_isa!(SysBusDevice: DeviceState, Object); + +// TODO: add virtual methods +pub trait SysBusDeviceImpl: DeviceImpl + IsA {} + +impl SysBusDeviceClass { + /// Fill in the virtual methods of `SysBusDeviceClass` based on the + /// definitions in the `SysBusDeviceImpl` trait. + pub fn class_init(self: &mut SysBusDeviceClass) { + self.parent_class.class_init::(); + } +} + +/// Trait for methods of [`SysBusDevice`] and its subclasses. +pub trait SysBusDeviceMethods: ObjectDeref +where + Self::Target: IsA, +{ + /// Expose a memory region to the board so that it can give it an address + /// in guest memory. Note that the ordering of calls to `init_mmio` is + /// important, since whoever creates the sysbus device will refer to the + /// region with a number that corresponds to the order of calls to + /// `init_mmio`. + fn init_mmio(&self, iomem: &MemoryRegion) { + assert!(bql_locked()); + unsafe { + bindings::sysbus_init_mmio(self.upcast().as_mut_ptr(), iomem.as_mut_ptr()); + } + } + + /// Expose an interrupt source outside the device as a qdev GPIO output. + /// Note that the ordering of calls to `init_irq` is important, since + /// whoever creates the sysbus device will refer to the interrupts with + /// a number that corresponds to the order of calls to `init_irq`. + fn init_irq(&self, irq: &InterruptSource) { + assert!(bql_locked()); + unsafe { + bindings::sysbus_init_irq(self.upcast().as_mut_ptr(), irq.as_ptr()); + } + } + + // TODO: do we want a type like GuestAddress here? + fn mmio_addr(&self, id: u32) -> Option { + assert!(bql_locked()); + // SAFETY: the BQL ensures that no one else writes to sbd.mmio[], and + // the SysBusDevice must be initialized to get an IsA. + let sbd = unsafe { *self.upcast().as_ptr() }; + let id: usize = id.try_into().unwrap(); + if sbd.mmio[id].memory.is_null() { + None + } else { + Some(sbd.mmio[id].addr) + } + } + + // TODO: do we want a type like GuestAddress here? + fn mmio_map(&self, id: u32, addr: u64) { + assert!(bql_locked()); + let id: i32 = id.try_into().unwrap(); + unsafe { + bindings::sysbus_mmio_map(self.upcast().as_mut_ptr(), id, addr); + } + } + + // Owned<> is used here because sysbus_connect_irq (via + // object_property_set_link) adds a reference to the IRQState, + // which can prolong its life + fn connect_irq(&self, id: u32, irq: &Owned) { + assert!(bql_locked()); + let id: i32 = id.try_into().unwrap(); + let irq: &IRQState = irq; + unsafe { + bindings::sysbus_connect_irq(self.upcast().as_mut_ptr(), id, irq.as_mut_ptr()); + } + } + + fn sysbus_realize(&self) { + // TODO: return an Error + assert!(bql_locked()); + unsafe { + bindings::sysbus_realize( + self.upcast().as_mut_ptr(), + addr_of_mut!(bindings::error_fatal), + ); + } + } +} + +impl SysBusDeviceMethods for R where R::Target: IsA {} diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs new file mode 100644 index 00000000000..0a2d111d490 --- /dev/null +++ b/rust/qemu-api/src/timer.rs @@ -0,0 +1,125 @@ +// Copyright (C) 2024 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{ + ffi::{c_int, c_void}, + pin::Pin, +}; + +use crate::{ + bindings::{self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType}, + callbacks::FnCall, + cell::Opaque, +}; + +/// A safe wrapper around [`bindings::QEMUTimer`]. +#[repr(transparent)] +#[derive(Debug, qemu_api_macros::Wrapper)] +pub struct Timer(Opaque); + +unsafe impl Send for Timer {} +unsafe impl Sync for Timer {} + +#[repr(transparent)] +#[derive(qemu_api_macros::Wrapper)] +pub struct TimerListGroup(Opaque); + +unsafe impl Send for TimerListGroup {} +unsafe impl Sync for TimerListGroup {} + +impl Timer { + pub const MS: u32 = bindings::SCALE_MS; + pub const US: u32 = bindings::SCALE_US; + pub const NS: u32 = bindings::SCALE_NS; + + /// Create a `Timer` struct without initializing it. + /// + /// # Safety + /// + /// The timer must be initialized before it is armed with + /// [`modify`](Self::modify). + pub unsafe fn new() -> Self { + // SAFETY: requirements relayed to callers of Timer::new + Self(unsafe { Opaque::zeroed() }) + } + + /// Create a new timer with the given attributes. + pub fn init_full<'timer, 'opaque: 'timer, T, F>( + self: Pin<&'timer mut Self>, + timer_list_group: Option<&TimerListGroup>, + clk_type: ClockType, + scale: u32, + attributes: u32, + _cb: F, + opaque: &'opaque T, + ) where + F: for<'a> FnCall<(&'a T,)>, + { + let _: () = F::ASSERT_IS_SOME; + + /// timer expiration callback + unsafe extern "C" fn rust_timer_handler FnCall<(&'a T,)>>( + opaque: *mut c_void, + ) { + // SAFETY: the opaque was passed as a reference to `T`. + F::call((unsafe { &*(opaque.cast::()) },)) + } + + let timer_cb: unsafe extern "C" fn(*mut c_void) = rust_timer_handler::; + + // SAFETY: the opaque outlives the timer + unsafe { + timer_init_full( + self.as_mut_ptr(), + if let Some(g) = timer_list_group { + g as *const TimerListGroup as *mut _ + } else { + ::core::ptr::null_mut() + }, + clk_type.id, + scale as c_int, + attributes as c_int, + Some(timer_cb), + (opaque as *const T).cast::().cast_mut(), + ) + } + } + + pub fn modify(&self, expire_time: u64) { + // SAFETY: the only way to obtain a Timer safely is via methods that + // take a Pin<&mut Self>, therefore the timer is pinned + unsafe { timer_mod(self.as_mut_ptr(), expire_time as i64) } + } + + pub fn delete(&self) { + // SAFETY: the only way to obtain a Timer safely is via methods that + // take a Pin<&mut Self>, therefore the timer is pinned + unsafe { timer_del(self.as_mut_ptr()) } + } +} + +// FIXME: use something like PinnedDrop from the pinned_init crate +impl Drop for Timer { + fn drop(&mut self) { + self.delete() + } +} + +pub struct ClockType { + id: QEMUClockType, +} + +impl ClockType { + pub fn get_ns(&self) -> u64 { + // SAFETY: cannot be created outside this module, therefore id + // is valid + (unsafe { qemu_clock_get_ns(self.id) }) as u64 + } +} + +pub const CLOCK_VIRTUAL: ClockType = ClockType { + id: QEMUClockType::QEMU_CLOCK_VIRTUAL, +}; + +pub const NANOSECONDS_PER_SECOND: u64 = 1000000000; diff --git a/rust/qemu-api/src/uninit.rs b/rust/qemu-api/src/uninit.rs new file mode 100644 index 00000000000..04123b4ae99 --- /dev/null +++ b/rust/qemu-api/src/uninit.rs @@ -0,0 +1,85 @@ +//! Access fields of a [`MaybeUninit`] + +use std::{ + mem::MaybeUninit, + ops::{Deref, DerefMut}, +}; + +pub struct MaybeUninitField<'a, T, U> { + parent: &'a mut MaybeUninit, + child: *mut U, +} + +impl<'a, T, U> MaybeUninitField<'a, T, U> { + #[doc(hidden)] + pub fn new(parent: &'a mut MaybeUninit, child: *mut U) -> Self { + MaybeUninitField { parent, child } + } + + /// Return a constant pointer to the containing object of the field. + /// + /// Because the `MaybeUninitField` remembers the containing object, + /// it is possible to use it in foreign APIs that initialize the + /// child. + pub fn parent(f: &Self) -> *const T { + f.parent.as_ptr() + } + + /// Return a mutable pointer to the containing object. + /// + /// Because the `MaybeUninitField` remembers the containing object, + /// it is possible to use it in foreign APIs that initialize the + /// child. + pub fn parent_mut(f: &mut Self) -> *mut T { + f.parent.as_mut_ptr() + } +} + +impl<'a, T, U> Deref for MaybeUninitField<'a, T, U> { + type Target = MaybeUninit; + + fn deref(&self) -> &MaybeUninit { + // SAFETY: self.child was obtained by dereferencing a valid mutable + // reference; the content of the memory may be invalid or uninitialized + // but MaybeUninit<_> makes no assumption on it + unsafe { &*(self.child.cast()) } + } +} + +impl<'a, T, U> DerefMut for MaybeUninitField<'a, T, U> { + fn deref_mut(&mut self) -> &mut MaybeUninit { + // SAFETY: self.child was obtained by dereferencing a valid mutable + // reference; the content of the memory may be invalid or uninitialized + // but MaybeUninit<_> makes no assumption on it + unsafe { &mut *(self.child.cast()) } + } +} + +/// ``` +/// #[derive(Debug)] +/// struct S { +/// x: u32, +/// y: u32, +/// } +/// +/// # use std::mem::MaybeUninit; +/// # use qemu_api::{assert_match, uninit_field_mut}; +/// +/// let mut s: MaybeUninit = MaybeUninit::zeroed(); +/// uninit_field_mut!(s, x).write(5); +/// let s = unsafe { s.assume_init() }; +/// assert_match!(s, S { x: 5, y: 0 }); +/// ``` +#[macro_export] +macro_rules! uninit_field_mut { + ($container:expr, $($field:tt)+) => {{ + let container__: &mut ::std::mem::MaybeUninit<_> = &mut $container; + let container_ptr__ = container__.as_mut_ptr(); + + // SAFETY: the container is not used directly, only through a MaybeUninit<>, + // so the safety is delegated to the caller and to final invocation of + // assume_init() + let target__ = unsafe { std::ptr::addr_of_mut!((*container_ptr__).$($field)+) }; + $crate::uninit::MaybeUninitField::new(container__, target__) + }}; +} diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs new file mode 100644 index 00000000000..812f390d780 --- /dev/null +++ b/rust/qemu-api/src/vmstate.rs @@ -0,0 +1,604 @@ +// 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. +//! +//! This module includes four families of macros: +//! +//! * [`vmstate_unused!`](crate::vmstate_unused) and +//! [`vmstate_of!`](crate::vmstate_of), which are used to express the +//! migration format for a struct. This is based on the [`VMState`] trait, +//! which is defined by all migratable types. +//! +//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward) and +//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), which help with +//! the definition of the [`VMState`] trait (respectively for transparent +//! structs and for `bilge`-defined types) +//! +//! * helper macros to declare a device model state struct, in particular +//! [`vmstate_subsections`](crate::vmstate_subsections) and +//! [`vmstate_fields`](crate::vmstate_fields). +//! +//! * direct equivalents to the C macros declared in +//! `include/migration/vmstate.h`. These are not type-safe and only provide +//! functionality that is missing from `vmstate_of!`. + +use core::{marker::PhantomData, mem, ptr::NonNull}; +use std::ffi::{c_int, c_void}; + +pub use crate::bindings::{VMStateDescription, VMStateField}; +use crate::{ + bindings::VMStateFlags, callbacks::FnCall, prelude::*, qom::Owned, zeroable::Zeroable, +}; + +/// This macro is used to call a function with a generic argument bound +/// to the type of a field. The function must take a +/// [`PhantomData`]`` argument; `T` is the type of +/// field `$field` in the `$typ` type. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::call_func_with_field; +/// # use core::marker::PhantomData; +/// const fn size_of_field(_: PhantomData) -> usize { +/// std::mem::size_of::() +/// } +/// +/// struct Foo { +/// x: u16, +/// }; +/// // calls size_of_field::() +/// assert_eq!(call_func_with_field!(size_of_field, Foo, x), 2); +/// ``` +#[macro_export] +macro_rules! call_func_with_field { + // Based on the answer by user steffahn (Frank Steffahn) at + // https://users.rust-lang.org/t/inferring-type-of-field/122857 + // and used under MIT license + ($func:expr, $typ:ty, $($field:tt).+) => { + $func(loop { + #![allow(unreachable_code)] + const fn phantom__(_: &T) -> ::core::marker::PhantomData { ::core::marker::PhantomData } + // Unreachable code is exempt from checks on uninitialized values. + // Use that trick to infer the type of this PhantomData. + break ::core::marker::PhantomData; + break phantom__(&{ let value__: $typ; value__.$($field).+ }); + }) + }; +} + +/// Workaround for lack of `const_refs_static`: references to global variables +/// can be included in a `static`, but not in a `const`; unfortunately, this +/// is exactly what would go in the `VMStateField`'s `info` member. +/// +/// This enum contains the contents of the `VMStateField`'s `info` member, +/// but as an `enum` instead of a pointer. +#[allow(non_camel_case_types)] +pub enum VMStateFieldType { + null, + vmstate_info_bool, + vmstate_info_int8, + vmstate_info_int16, + vmstate_info_int32, + vmstate_info_int64, + vmstate_info_uint8, + vmstate_info_uint16, + vmstate_info_uint32, + vmstate_info_uint64, + vmstate_info_timer, +} + +/// Workaround for lack of `const_refs_static`. Converts a `VMStateFieldType` +/// to a `*const VMStateInfo`, for inclusion in a `VMStateField`. +#[macro_export] +macro_rules! info_enum_to_ref { + ($e:expr) => { + unsafe { + match $e { + $crate::vmstate::VMStateFieldType::null => ::core::ptr::null(), + $crate::vmstate::VMStateFieldType::vmstate_info_bool => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_bool) + } + $crate::vmstate::VMStateFieldType::vmstate_info_int8 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_int8) + } + $crate::vmstate::VMStateFieldType::vmstate_info_int16 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_int16) + } + $crate::vmstate::VMStateFieldType::vmstate_info_int32 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_int32) + } + $crate::vmstate::VMStateFieldType::vmstate_info_int64 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_int64) + } + $crate::vmstate::VMStateFieldType::vmstate_info_uint8 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint8) + } + $crate::vmstate::VMStateFieldType::vmstate_info_uint16 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint16) + } + $crate::vmstate::VMStateFieldType::vmstate_info_uint32 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32) + } + $crate::vmstate::VMStateFieldType::vmstate_info_uint64 => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint64) + } + $crate::vmstate::VMStateFieldType::vmstate_info_timer => { + ::core::ptr::addr_of!($crate::bindings::vmstate_info_timer) + } + } + } + }; +} + +/// A trait for types that can be included in a device's migration stream. It +/// provides the base contents of a `VMStateField` (minus the name and offset). +/// +/// # Safety +/// +/// The contents of this trait go straight into structs that are parsed by C +/// code and used to introspect into other structs. Generally, you don't need +/// to implement it except via macros that do it for you, such as +/// `impl_vmstate_bitsized!`. +pub unsafe trait VMState { + /// The `info` member of a `VMStateField` is a pointer and as such cannot + /// yet be included in the [`BASE`](VMState::BASE) associated constant; + /// this is only allowed by Rust 1.83.0 and newer. For now, include the + /// member as an enum which is stored in a separate constant. + const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::null; + + /// The base contents of a `VMStateField` (minus the name and offset) for + /// the type that is implementing the trait. + const BASE: VMStateField; + + /// A flag that is added to another field's `VMStateField` to specify the + /// length's type in a variable-sized array. If this is not a supported + /// type for the length (i.e. if it is not `u8`, `u16`, `u32`), using it + /// in a call to [`vmstate_of!`](crate::vmstate_of) will cause a + /// compile-time error. + const VARRAY_FLAG: VMStateFlags = { + panic!("invalid type for variable-sized array"); + }; +} + +/// Internal utility function to retrieve a type's `VMStateFieldType`; +/// used by [`vmstate_of!`](crate::vmstate_of). +pub const fn vmstate_scalar_type(_: PhantomData) -> VMStateFieldType { + T::SCALAR_TYPE +} + +/// Internal utility function to retrieve a type's `VMStateField`; +/// used by [`vmstate_of!`](crate::vmstate_of). +pub const fn vmstate_base(_: PhantomData) -> VMStateField { + T::BASE +} + +/// Internal utility function to retrieve a type's `VMStateFlags` when it +/// is used as the element count of a `VMSTATE_VARRAY`; used by +/// [`vmstate_of!`](crate::vmstate_of). +pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags { + T::VARRAY_FLAG +} + +/// Return the `VMStateField` for a field of a struct. The field must be +/// visible in the current scope. +/// +/// Only a limited set of types is supported out of the box: +/// * scalar types (integer and `bool`) +/// * the C struct `QEMUTimer` +/// * a transparent wrapper for any of the above (`Cell`, `UnsafeCell`, +/// [`BqlCell`], [`BqlRefCell`] +/// * a raw pointer to any of the above +/// * a `NonNull` pointer, a `Box` or an [`Owned`] for any of the above +/// * an array of any of the above +/// +/// In order to support other types, the trait `VMState` must be implemented +/// for them. The macros +/// [`impl_vmstate_bitsized!`](crate::impl_vmstate_bitsized) +/// and [`impl_vmstate_forward!`](crate::impl_vmstate_forward) help with this. +#[macro_export] +macro_rules! vmstate_of { + ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => { + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), "\0") + .as_bytes() + .as_ptr() as *const ::std::os::raw::c_char, + offset: ::std::mem::offset_of!($struct_name, $field_name), + $(num_offset: ::std::mem::offset_of!($struct_name, $num),)? + $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)? + // The calls to `call_func_with_field!` are the magic that + // computes most of the VMStateField from the type of the field. + info: $crate::info_enum_to_ref!($crate::call_func_with_field!( + $crate::vmstate::vmstate_scalar_type, + $struct_name, + $field_name + )), + ..$crate::call_func_with_field!( + $crate::vmstate::vmstate_base, + $struct_name, + $field_name + )$(.with_varray_flag($crate::call_func_with_field!( + $crate::vmstate::vmstate_varray_flag, + $struct_name, + $num)) + $(.with_varray_multiply($factor))?)? + } + }; +} + +impl VMStateFlags { + const VMS_VARRAY_FLAGS: VMStateFlags = VMStateFlags( + VMStateFlags::VMS_VARRAY_INT32.0 + | VMStateFlags::VMS_VARRAY_UINT8.0 + | VMStateFlags::VMS_VARRAY_UINT16.0 + | VMStateFlags::VMS_VARRAY_UINT32.0, + ); +} + +// Add a couple builder-style methods to VMStateField, allowing +// easy derivation of VMStateField constants from other types. +impl VMStateField { + #[must_use] + pub const fn with_version_id(mut self, version_id: i32) -> Self { + assert!(version_id >= 0); + self.version_id = version_id; + self + } + + #[must_use] + pub const fn with_array_flag(mut self, num: usize) -> Self { + assert!(num <= 0x7FFF_FFFFusize); + assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) == 0); + assert!((self.flags.0 & VMStateFlags::VMS_VARRAY_FLAGS.0) == 0); + if (self.flags.0 & VMStateFlags::VMS_POINTER.0) != 0 { + self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_POINTER.0); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0); + // VMS_ARRAY_OF_POINTER flag stores the size of pointer. + // FIXME: *const, *mut, NonNull and Box<> have the same size as usize. + // Resize if more smart pointers are supported. + self.size = std::mem::size_of::(); + } + self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_SINGLE.0); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY.0); + self.num = num as i32; + self + } + + #[must_use] + pub const fn with_pointer_flag(mut self) -> Self { + assert!((self.flags.0 & VMStateFlags::VMS_POINTER.0) == 0); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_POINTER.0); + self + } + + #[must_use] + pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> VMStateField { + self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0); + self.flags = VMStateFlags(self.flags.0 | flag.0); + self.num = 0; // varray uses num_offset instead of num. + self + } + + #[must_use] + #[allow(unused_mut)] + pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> VMStateField { + assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0); + self.with_varray_flag_unchecked(flag) + } + + #[must_use] + pub const fn with_varray_multiply(mut self, num: u32) -> VMStateField { + assert!(num <= 0x7FFF_FFFFu32); + self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0); + self.num = num as i32; + self + } +} + +/// This macro can be used (by just passing it a type) to forward the `VMState` +/// trait to the first field of a tuple. This is a workaround for lack of +/// support of nested [`offset_of`](core::mem::offset_of) until Rust 1.82.0. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::impl_vmstate_forward; +/// pub struct Fifo([u8; 16]); +/// impl_vmstate_forward!(Fifo); +/// ``` +#[macro_export] +macro_rules! impl_vmstate_forward { + // This is similar to impl_vmstate_transparent below, but it + // uses the same trick as vmstate_of! to obtain the type of + // the first field of the tuple + ($tuple:ty) => { + unsafe impl $crate::vmstate::VMState for $tuple { + const SCALAR_TYPE: $crate::vmstate::VMStateFieldType = + $crate::call_func_with_field!($crate::vmstate::vmstate_scalar_type, $tuple, 0); + const BASE: $crate::bindings::VMStateField = + $crate::call_func_with_field!($crate::vmstate::vmstate_base, $tuple, 0); + } + }; +} + +// Transparent wrappers: just use the internal type + +macro_rules! impl_vmstate_transparent { + ($type:ty where $base:tt: VMState $($where:tt)*) => { + unsafe impl<$base> VMState for $type where $base: VMState $($where)* { + const SCALAR_TYPE: VMStateFieldType = <$base as VMState>::SCALAR_TYPE; + const BASE: VMStateField = VMStateField { + size: mem::size_of::<$type>(), + ..<$base as VMState>::BASE + }; + const VARRAY_FLAG: VMStateFlags = <$base as VMState>::VARRAY_FLAG; + } + }; +} + +impl_vmstate_transparent!(std::cell::Cell where T: VMState); +impl_vmstate_transparent!(std::cell::UnsafeCell where T: VMState); +impl_vmstate_transparent!(std::pin::Pin where T: VMState); +impl_vmstate_transparent!(crate::cell::BqlCell where T: VMState); +impl_vmstate_transparent!(crate::cell::BqlRefCell where T: VMState); +impl_vmstate_transparent!(crate::cell::Opaque where T: VMState); + +#[macro_export] +macro_rules! impl_vmstate_bitsized { + ($type:ty) => { + unsafe impl $crate::vmstate::VMState for $type { + const SCALAR_TYPE: $crate::vmstate::VMStateFieldType = + <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt + as ::bilge::prelude::Number>::UnderlyingType + as $crate::vmstate::VMState>::SCALAR_TYPE; + const BASE: $crate::bindings::VMStateField = + <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt + as ::bilge::prelude::Number>::UnderlyingType + as $crate::vmstate::VMState>::BASE; + const VARRAY_FLAG: $crate::bindings::VMStateFlags = + <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt + as ::bilge::prelude::Number>::UnderlyingType + as $crate::vmstate::VMState>::VARRAY_FLAG; + } + }; +} + +// Scalar types using predefined VMStateInfos + +macro_rules! impl_vmstate_scalar { + ($info:ident, $type:ty$(, $varray_flag:ident)?) => { + unsafe impl VMState for $type { + const SCALAR_TYPE: VMStateFieldType = VMStateFieldType::$info; + const BASE: VMStateField = VMStateField { + size: mem::size_of::<$type>(), + flags: VMStateFlags::VMS_SINGLE, + ..Zeroable::ZERO + }; + $(const VARRAY_FLAG: VMStateFlags = VMStateFlags::$varray_flag;)? + } + }; +} + +impl_vmstate_scalar!(vmstate_info_bool, bool); +impl_vmstate_scalar!(vmstate_info_int8, i8); +impl_vmstate_scalar!(vmstate_info_int16, i16); +impl_vmstate_scalar!(vmstate_info_int32, i32); +impl_vmstate_scalar!(vmstate_info_int64, i64); +impl_vmstate_scalar!(vmstate_info_uint8, u8, VMS_VARRAY_UINT8); +impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY_UINT16); +impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32); +impl_vmstate_scalar!(vmstate_info_uint64, u64); +impl_vmstate_scalar!(vmstate_info_timer, crate::timer::Timer); + +// Pointer types using the underlying type's VMState plus VMS_POINTER +// Note that references are not supported, though references to cells +// could be allowed. + +macro_rules! impl_vmstate_pointer { + ($type:ty where $base:tt: VMState $($where:tt)*) => { + unsafe impl<$base> VMState for $type where $base: VMState $($where)* { + const SCALAR_TYPE: VMStateFieldType = ::SCALAR_TYPE; + const BASE: VMStateField = <$base as VMState>::BASE.with_pointer_flag(); + } + }; +} + +impl_vmstate_pointer!(*const T where T: VMState); +impl_vmstate_pointer!(*mut T where T: VMState); +impl_vmstate_pointer!(NonNull where T: VMState); + +// Unlike C pointers, Box is always non-null therefore there is no need +// to specify VMS_ALLOC. +impl_vmstate_pointer!(Box where T: VMState); +impl_vmstate_pointer!(Owned where T: VMState + ObjectType); + +// Arrays using the underlying type's VMState plus +// VMS_ARRAY/VMS_ARRAY_OF_POINTER + +unsafe impl VMState for [T; N] { + const SCALAR_TYPE: VMStateFieldType = ::SCALAR_TYPE; + const BASE: VMStateField = ::BASE.with_array_flag(N); +} + +#[doc(alias = "VMSTATE_UNUSED")] +#[macro_export] +macro_rules! vmstate_unused { + ($size:expr) => {{ + $crate::bindings::VMStateField { + name: c"unused".as_ptr(), + size: $size, + info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) }, + flags: $crate::bindings::VMStateFlags::VMS_BUFFER, + ..$crate::zeroable::Zeroable::ZERO + } + }}; +} + +pub extern "C" fn rust_vms_test_field_exists FnCall<(&'a T, u8), bool>>( + opaque: *mut c_void, + version_id: c_int, +) -> bool { + // SAFETY: the opaque was passed as a reference to `T`. + let owner: &T = unsafe { &*(opaque.cast::()) }; + let version: u8 = version_id.try_into().unwrap(); + F::call((owner, version)) +} + +pub type VMSFieldExistCb = unsafe extern "C" fn( + opaque: *mut std::os::raw::c_void, + version_id: std::os::raw::c_int, +) -> bool; + +#[macro_export] +macro_rules! vmstate_exist_fn { + ($struct_name:ty, $test_fn:expr) => {{ + const fn test_cb_builder__ $crate::callbacks::FnCall<(&'a T, u8), bool>>( + _phantom: ::core::marker::PhantomData, + ) -> $crate::vmstate::VMSFieldExistCb { + let _: () = F::ASSERT_IS_SOME; + $crate::vmstate::rust_vms_test_field_exists:: + } + + const fn phantom__(_: &T) -> ::core::marker::PhantomData { + ::core::marker::PhantomData + } + Some(test_cb_builder__::<$struct_name, _>(phantom__(&$test_fn))) + }}; +} + +// FIXME: including the `vmsd` field in a `const` is not possible without +// the const_refs_static feature (stabilized in Rust 1.83.0). Without it, +// it is not possible to use VMS_STRUCT in a transparent manner using +// `vmstate_of!`. While VMSTATE_CLOCK can at least try to be type-safe, +// VMSTATE_STRUCT includes $type only for documentation purposes; it +// is checked against $field_name and $struct_name, but not against $vmsd +// which is what really would matter. +#[doc(alias = "VMSTATE_STRUCT")] +#[macro_export] +macro_rules! vmstate_struct { + ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?, $vmsd:expr, $type:ty $(, $test_fn:expr)? $(,)?) => { + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), "\0") + .as_bytes() + .as_ptr() as *const ::std::os::raw::c_char, + $(num_offset: ::std::mem::offset_of!($struct_name, $num),)? + offset: { + $crate::assert_field_type!($struct_name, $field_name, $type $(, num = $num)?); + ::std::mem::offset_of!($struct_name, $field_name) + }, + size: ::core::mem::size_of::<$type>(), + flags: $crate::bindings::VMStateFlags::VMS_STRUCT, + vmsd: $vmsd, + $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)? + ..$crate::zeroable::Zeroable::ZERO + } $(.with_varray_flag_unchecked( + $crate::call_func_with_field!( + $crate::vmstate::vmstate_varray_flag, + $struct_name, + $num + ) + ) + $(.with_varray_multiply($factor))?)? + }; +} + +#[doc(alias = "VMSTATE_CLOCK")] +#[macro_export] +macro_rules! vmstate_clock { + ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?) => {{ + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), "\0") + .as_bytes() + .as_ptr() as *const ::std::os::raw::c_char, + offset: { + $crate::assert_field_type!( + $struct_name, + $field_name, + $crate::qom::Owned<$crate::qdev::Clock> $(, num = $num)? + ); + ::std::mem::offset_of!($struct_name, $field_name) + }, + size: ::core::mem::size_of::<*const $crate::qdev::Clock>(), + flags: $crate::bindings::VMStateFlags( + $crate::bindings::VMStateFlags::VMS_STRUCT.0 + | $crate::bindings::VMStateFlags::VMS_POINTER.0, + ), + vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) }, + ..$crate::zeroable::Zeroable::ZERO + } $(.with_varray_flag_unchecked( + $crate::call_func_with_field!( + $crate::vmstate::vmstate_varray_flag, + $struct_name, + $num + ) + ) + $(.with_varray_multiply($factor))?)? + }}; +} + +/// 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 { + flags: $crate::bindings::VMStateFlags::VMS_END, + ..$crate::zeroable::Zeroable::ZERO + } + ]; + _FIELDS.as_ptr() + }} +} + +#[doc(alias = "VMSTATE_VALIDATE")] +#[macro_export] +macro_rules! vmstate_validate { + ($struct_name:ty, $test_name:expr, $test_fn:expr $(,)?) => { + $crate::bindings::VMStateField { + name: ::std::ffi::CStr::as_ptr($test_name), + field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn), + flags: $crate::bindings::VMStateFlags( + $crate::bindings::VMStateFlags::VMS_MUST_EXIST.0 + | $crate::bindings::VMStateFlags::VMS_ARRAY.0, + ), + num: 0, // 0 elements: no data, only run test_fn callback + ..$crate::zeroable::Zeroable::ZERO + } + }; +} + +/// A transparent wrapper type for the `subsections` field of +/// [`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`]) +/// 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 00000000000..d8239d08563 --- /dev/null +++ b/rust/qemu-api/src/zeroable.rs @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Defines a trait for structs that can be safely initialized with zero bytes. + +/// Encapsulates the requirement that +/// `MaybeUninit::::zeroed().assume_init()` does not cause undefined +/// behavior. +/// +/// # Safety +/// +/// Do not add this trait to a type unless all-zeroes is a valid value for the +/// type. In particular, raw pointers can be zero, but references and +/// `NonNull` cannot. +pub unsafe trait Zeroable: Default { + /// Return a value of Self whose memory representation consists of all + /// zeroes, with the possible exclusion of padding bytes. + const ZERO: Self = unsafe { ::core::mem::MaybeUninit::::zeroed().assume_init() }; +} + +// bindgen does not derive Default here +#[allow(clippy::derivable_impls)] +impl Default for crate::bindings::VMStateFlags { + fn default() -> Self { + Self(0) + } +} + +unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 {} +unsafe impl Zeroable for crate::bindings::Property {} +unsafe impl Zeroable for crate::bindings::VMStateFlags {} +unsafe impl Zeroable for crate::bindings::VMStateField {} +unsafe impl Zeroable for crate::bindings::VMStateDescription {} +unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 {} +unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 {} +unsafe impl Zeroable for crate::bindings::MemoryRegionOps {} +unsafe impl Zeroable for crate::bindings::MemTxAttrs {} +unsafe impl Zeroable for crate::bindings::CharBackend {} diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs new file mode 100644 index 00000000000..a658a49fcfd --- /dev/null +++ b/rust/qemu-api/tests/tests.rs @@ -0,0 +1,180 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{ffi::CStr, ptr::addr_of}; + +use qemu_api::{ + bindings::{module_call_init, module_init_type, qdev_prop_bool}, + cell::{self, BqlCell}, + declare_properties, define_property, + prelude::*, + qdev::{DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, + qom::{ObjectImpl, ParentField}, + sysbus::SysBusDevice, + vmstate::VMStateDescription, + zeroable::Zeroable, +}; + +mod vmstate_tests; + +// Test that macros can compile. +pub static VMSTATE: VMStateDescription = VMStateDescription { + name: c"name".as_ptr(), + unmigratable: true, + ..Zeroable::ZERO +}; + +#[repr(C)] +#[derive(qemu_api_macros::Object)] +pub struct DummyState { + parent: ParentField, + migrate_clock: bool, +} + +qom_isa!(DummyState: Object, DeviceState); + +pub struct DummyClass { + parent_class: ::Class, +} + +impl DummyClass { + pub fn class_init(self: &mut DummyClass) { + self.parent_class.class_init::(); + } +} + +declare_properties! { + DUMMY_PROPERTIES, + define_property!( + c"migrate-clk", + DummyState, + migrate_clock, + unsafe { &qdev_prop_bool }, + bool + ), +} + +unsafe impl ObjectType for DummyState { + type Class = DummyClass; + const TYPE_NAME: &'static CStr = c"dummy"; +} + +impl ObjectImpl for DummyState { + type ParentType = DeviceState; + const ABSTRACT: bool = false; + const CLASS_INIT: fn(&mut DummyClass) = DummyClass::class_init::; +} + +impl ResettablePhasesImpl for DummyState {} + +impl DeviceImpl for DummyState { + fn properties() -> &'static [Property] { + &DUMMY_PROPERTIES + } + fn vmsd() -> Option<&'static VMStateDescription> { + Some(&VMSTATE) + } +} + +#[repr(C)] +#[derive(qemu_api_macros::Object)] +pub struct DummyChildState { + parent: ParentField, +} + +qom_isa!(DummyChildState: Object, DeviceState, DummyState); + +pub struct DummyChildClass { + parent_class: ::Class, +} + +unsafe impl ObjectType for DummyChildState { + type Class = DummyChildClass; + const TYPE_NAME: &'static CStr = c"dummy_child"; +} + +impl ObjectImpl for DummyChildState { + type ParentType = DummyState; + const ABSTRACT: bool = false; + const CLASS_INIT: fn(&mut DummyChildClass) = DummyChildClass::class_init::; +} + +impl ResettablePhasesImpl for DummyChildState {} +impl DeviceImpl for DummyChildState {} + +impl DummyChildClass { + pub fn class_init(self: &mut DummyChildClass) { + self.parent_class.class_init::(); + } +} + +fn init_qom() { + static ONCE: BqlCell = BqlCell::new(false); + + cell::bql_start_test(); + if !ONCE.get() { + unsafe { + module_call_init(module_init_type::MODULE_INIT_QOM); + } + ONCE.set(true); + } +} + +#[test] +/// Create and immediately drop an instance. +fn test_object_new() { + init_qom(); + drop(DummyState::new()); + drop(DummyChildState::new()); +} + +#[test] +#[allow(clippy::redundant_clone)] +/// Create, clone and then drop an instance. +fn test_clone() { + init_qom(); + let p = DummyState::new(); + assert_eq!(p.clone().typename(), "dummy"); + drop(p); +} + +#[test] +/// Try invoking a method on an object. +fn test_typename() { + init_qom(); + let p = DummyState::new(); + assert_eq!(p.typename(), "dummy"); +} + +// a note on all "cast" tests: usually, especially for downcasts the desired +// class would be placed on the right, for example: +// +// let sbd_ref = p.dynamic_cast::(); +// +// Here I am doing the opposite to check that the resulting type is correct. + +#[test] +#[allow(clippy::shadow_unrelated)] +/// Test casts on shared references. +fn test_cast() { + init_qom(); + let p = DummyState::new(); + let p_ptr: *mut DummyState = p.as_mut_ptr(); + let p_ref: &mut DummyState = unsafe { &mut *p_ptr }; + + let obj_ref: &Object = p_ref.upcast(); + assert_eq!(addr_of!(*obj_ref), p_ptr.cast()); + + let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast(); + assert!(sbd_ref.is_none()); + + let dev_ref: Option<&DeviceState> = obj_ref.downcast(); + assert_eq!(addr_of!(*dev_ref.unwrap()), p_ptr.cast()); + + // SAFETY: the cast is wrong, but the value is only used for comparison + unsafe { + let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast(); + assert_eq!(addr_of!(*sbd_ref), p_ptr.cast()); + } +} diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs new file mode 100644 index 00000000000..bded836eb60 --- /dev/null +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -0,0 +1,505 @@ +// Copyright (C) 2025 Intel Corporation. +// Author(s): Zhao Liu +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{ + ffi::{c_void, CStr}, + mem::size_of, + ptr::NonNull, + slice, +}; + +use qemu_api::{ + bindings::{ + vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8, + vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, + }, + cell::{BqlCell, Opaque}, + impl_vmstate_forward, + vmstate::{VMStateDescription, VMStateField}, + vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate, + zeroable::Zeroable, +}; + +const FOO_ARRAY_MAX: usize = 3; + +// =========================== Test VMSTATE_FOOA =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOA: +// - VMSTATE_U16 +// - VMSTATE_UNUSED +// - VMSTATE_VARRAY_UINT16_UNSAFE +// - VMSTATE_VARRAY_MULTIPLY +#[repr(C)] +#[derive(Default)] +struct FooA { + arr: [u8; FOO_ARRAY_MAX], + num: u16, + arr_mul: [i8; FOO_ARRAY_MAX], + num_mul: u32, + elem: i8, +} + +static VMSTATE_FOOA: VMStateDescription = VMStateDescription { + name: c"foo_a".as_ptr(), + version_id: 1, + minimum_version_id: 1, + fields: vmstate_fields! { + vmstate_of!(FooA, elem), + vmstate_unused!(size_of::()), + vmstate_of!(FooA, arr[0 .. num]).with_version_id(0), + vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]), + }, + ..Zeroable::ZERO +}; + +#[test] +fn test_vmstate_uint16() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + + // 1st VMStateField ("elem") in VMSTATE_FOOA (corresponding to VMSTATE_UINT16) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"elem\0" + ); + assert_eq!(foo_fields[0].offset, 16); + assert_eq!(foo_fields[0].num_offset, 0); + assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int8 }); + assert_eq!(foo_fields[0].version_id, 0); + assert_eq!(foo_fields[0].size, 1); + assert_eq!(foo_fields[0].num, 0); + assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); + assert!(foo_fields[0].vmsd.is_null()); + assert!(foo_fields[0].field_exists.is_none()); +} + +#[test] +fn test_vmstate_unused() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + + // 2nd VMStateField ("unused") in VMSTATE_FOOA (corresponding to VMSTATE_UNUSED) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), + b"unused\0" + ); + assert_eq!(foo_fields[1].offset, 0); + assert_eq!(foo_fields[1].num_offset, 0); + assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_unused_buffer }); + assert_eq!(foo_fields[1].version_id, 0); + assert_eq!(foo_fields[1].size, 8); + assert_eq!(foo_fields[1].num, 0); + assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_BUFFER); + assert!(foo_fields[1].vmsd.is_null()); + assert!(foo_fields[1].field_exists.is_none()); +} + +#[test] +fn test_vmstate_varray_uint16_unsafe() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + + // 3rd VMStateField ("arr") in VMSTATE_FOOA (corresponding to + // VMSTATE_VARRAY_UINT16_UNSAFE) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"arr\0" + ); + assert_eq!(foo_fields[2].offset, 0); + assert_eq!(foo_fields[2].num_offset, 4); + assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); + assert_eq!(foo_fields[2].version_id, 0); + assert_eq!(foo_fields[2].size, 1); + assert_eq!(foo_fields[2].num, 0); + assert_eq!(foo_fields[2].flags, VMStateFlags::VMS_VARRAY_UINT16); + assert!(foo_fields[2].vmsd.is_null()); + assert!(foo_fields[2].field_exists.is_none()); +} + +#[test] +fn test_vmstate_varray_multiply() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + + // 4th VMStateField ("arr_mul") in VMSTATE_FOOA (corresponding to + // VMSTATE_VARRAY_MULTIPLY) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), + b"arr_mul\0" + ); + assert_eq!(foo_fields[3].offset, 6); + assert_eq!(foo_fields[3].num_offset, 12); + assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_int8 }); + assert_eq!(foo_fields[3].version_id, 0); + assert_eq!(foo_fields[3].size, 1); + assert_eq!(foo_fields[3].num, 16); + assert_eq!( + foo_fields[3].flags.0, + VMStateFlags::VMS_VARRAY_UINT32.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 + ); + assert!(foo_fields[3].vmsd.is_null()); + assert!(foo_fields[3].field_exists.is_none()); + + // The last VMStateField in VMSTATE_FOOA. + assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); +} + +// =========================== Test VMSTATE_FOOB =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOB: +// - VMSTATE_BOOL_V +// - VMSTATE_U64 +// - VMSTATE_STRUCT_VARRAY_UINT8 +// - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32 +// - VMSTATE_ARRAY +// - VMSTATE_STRUCT_VARRAY_UINT8 with BqlCell wrapper & test_fn +#[repr(C)] +#[derive(Default)] +struct FooB { + arr_a: [FooA; FOO_ARRAY_MAX], + num_a: u8, + arr_a_mul: [FooA; FOO_ARRAY_MAX], + num_a_mul: u32, + wrap: BqlCell, + val: bool, + // FIXME: Use Timer array. Now we can't since it's hard to link savevm.c to test. + arr_i64: [i64; FOO_ARRAY_MAX], + arr_a_wrap: [FooA; FOO_ARRAY_MAX], + num_a_wrap: BqlCell, +} + +fn validate_foob(_state: &FooB, _version_id: u8) -> bool { + true +} + +static VMSTATE_FOOB: VMStateDescription = VMStateDescription { + name: c"foo_b".as_ptr(), + version_id: 2, + minimum_version_id: 1, + fields: vmstate_fields! { + vmstate_of!(FooB, val).with_version_id(2), + vmstate_of!(FooB, wrap), + vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1), + vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2), + vmstate_of!(FooB, arr_i64), + vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob), + }, + ..Zeroable::ZERO +}; + +#[test] +fn test_vmstate_bool_v() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; + + // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"val\0" + ); + assert_eq!(foo_fields[0].offset, 136); + assert_eq!(foo_fields[0].num_offset, 0); + assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_bool }); + assert_eq!(foo_fields[0].version_id, 2); + assert_eq!(foo_fields[0].size, 1); + assert_eq!(foo_fields[0].num, 0); + assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); + assert!(foo_fields[0].vmsd.is_null()); + assert!(foo_fields[0].field_exists.is_none()); +} + +#[test] +fn test_vmstate_uint64() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; + + // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), + b"wrap\0" + ); + assert_eq!(foo_fields[1].offset, 128); + assert_eq!(foo_fields[1].num_offset, 0); + assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_uint64 }); + assert_eq!(foo_fields[1].version_id, 0); + assert_eq!(foo_fields[1].size, 8); + assert_eq!(foo_fields[1].num, 0); + assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_SINGLE); + assert!(foo_fields[1].vmsd.is_null()); + assert!(foo_fields[1].field_exists.is_none()); +} + +#[test] +fn test_vmstate_struct_varray_uint8() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; + + // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to + // VMSTATE_STRUCT_VARRAY_UINT8) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"arr_a\0" + ); + assert_eq!(foo_fields[2].offset, 0); + assert_eq!(foo_fields[2].num_offset, 60); + assert!(foo_fields[2].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. + assert_eq!(foo_fields[2].version_id, 1); + assert_eq!(foo_fields[2].size, 20); + assert_eq!(foo_fields[2].num, 0); + assert_eq!( + foo_fields[2].flags.0, + VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_VARRAY_UINT8.0 + ); + assert_eq!(foo_fields[2].vmsd, &VMSTATE_FOOA); + assert!(foo_fields[2].field_exists.is_none()); +} + +#[test] +fn test_vmstate_struct_varray_uint32_multiply() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; + + // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to + // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), + b"arr_a_mul\0" + ); + assert_eq!(foo_fields[3].offset, 64); + assert_eq!(foo_fields[3].num_offset, 124); + assert!(foo_fields[3].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. + assert_eq!(foo_fields[3].version_id, 2); + assert_eq!(foo_fields[3].size, 20); + assert_eq!(foo_fields[3].num, 32); + assert_eq!( + foo_fields[3].flags.0, + VMStateFlags::VMS_STRUCT.0 + | VMStateFlags::VMS_VARRAY_UINT32.0 + | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 + ); + assert_eq!(foo_fields[3].vmsd, &VMSTATE_FOOA); + assert!(foo_fields[3].field_exists.is_none()); +} + +#[test] +fn test_vmstate_macro_array() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; + + // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to + // VMSTATE_ARRAY) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[4].name) }.to_bytes_with_nul(), + b"arr_i64\0" + ); + assert_eq!(foo_fields[4].offset, 144); + assert_eq!(foo_fields[4].num_offset, 0); + assert_eq!(foo_fields[4].info, unsafe { &vmstate_info_int64 }); + assert_eq!(foo_fields[4].version_id, 0); + assert_eq!(foo_fields[4].size, 8); + assert_eq!(foo_fields[4].num, FOO_ARRAY_MAX as i32); + assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY); + assert!(foo_fields[4].vmsd.is_null()); + assert!(foo_fields[4].field_exists.is_none()); +} + +#[test] +fn test_vmstate_struct_varray_uint8_wrapper() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; + let mut foo_b: FooB = Default::default(); + let foo_b_p = std::ptr::addr_of_mut!(foo_b).cast::(); + + // 6th VMStateField ("arr_a_wrap") in VMSTATE_FOOB (corresponding to + // VMSTATE_STRUCT_VARRAY_UINT8). Other fields are checked in + // test_vmstate_struct_varray_uint8. + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[5].name) }.to_bytes_with_nul(), + b"arr_a_wrap\0" + ); + assert_eq!(foo_fields[5].num_offset, 228); + assert!(unsafe { foo_fields[5].field_exists.unwrap()(foo_b_p, 0) }); + + // The last VMStateField in VMSTATE_FOOB. + assert_eq!(foo_fields[6].flags, VMStateFlags::VMS_END); +} + +// =========================== Test VMSTATE_FOOC =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOC: +// - VMSTATE_POINTER +// - VMSTATE_ARRAY_OF_POINTER +struct FooCWrapper([Opaque<*mut u8>; FOO_ARRAY_MAX]); // Though Opaque<> array is almost impossible. + +impl_vmstate_forward!(FooCWrapper); + +#[repr(C)] +struct FooC { + ptr: *const i32, + ptr_a: NonNull, + arr_ptr: [Box; FOO_ARRAY_MAX], + arr_ptr_wrap: FooCWrapper, +} + +static VMSTATE_FOOC: VMStateDescription = VMStateDescription { + name: c"foo_c".as_ptr(), + version_id: 3, + minimum_version_id: 1, + fields: vmstate_fields! { + vmstate_of!(FooC, ptr).with_version_id(2), + // FIXME: Currently vmstate_struct doesn't support the pointer to structure. + // VMSTATE_STRUCT_POINTER: vmstate_struct!(FooC, ptr_a, VMSTATE_FOOA, NonNull) + vmstate_unused!(size_of::>()), + vmstate_of!(FooC, arr_ptr), + vmstate_of!(FooC, arr_ptr_wrap), + }, + ..Zeroable::ZERO +}; + +const PTR_SIZE: usize = size_of::<*mut ()>(); + +#[test] +fn test_vmstate_pointer() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; + + // 1st VMStateField ("ptr") in VMSTATE_FOOC (corresponding to VMSTATE_POINTER) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"ptr\0" + ); + assert_eq!(foo_fields[0].offset, 0); + assert_eq!(foo_fields[0].num_offset, 0); + assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int32 }); + assert_eq!(foo_fields[0].version_id, 2); + assert_eq!(foo_fields[0].size, 4); + assert_eq!(foo_fields[0].num, 0); + assert_eq!( + foo_fields[0].flags.0, + VMStateFlags::VMS_SINGLE.0 | VMStateFlags::VMS_POINTER.0 + ); + assert!(foo_fields[0].vmsd.is_null()); + assert!(foo_fields[0].field_exists.is_none()); +} + +#[test] +fn test_vmstate_macro_array_of_pointer() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; + + // 3rd VMStateField ("arr_ptr") in VMSTATE_FOOC (corresponding to + // VMSTATE_ARRAY_OF_POINTER) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"arr_ptr\0" + ); + assert_eq!(foo_fields[2].offset, 2 * PTR_SIZE); + assert_eq!(foo_fields[2].num_offset, 0); + assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); + assert_eq!(foo_fields[2].version_id, 0); + assert_eq!(foo_fields[2].size, PTR_SIZE); + assert_eq!(foo_fields[2].num, FOO_ARRAY_MAX as i32); + assert_eq!( + foo_fields[2].flags.0, + VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 + ); + assert!(foo_fields[2].vmsd.is_null()); + assert!(foo_fields[2].field_exists.is_none()); +} + +#[test] +fn test_vmstate_macro_array_of_pointer_wrapped() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; + + // 4th VMStateField ("arr_ptr_wrap") in VMSTATE_FOOC (corresponding to + // VMSTATE_ARRAY_OF_POINTER) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), + b"arr_ptr_wrap\0" + ); + assert_eq!(foo_fields[3].offset, (FOO_ARRAY_MAX + 2) * PTR_SIZE); + assert_eq!(foo_fields[3].num_offset, 0); + assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_uint8 }); + assert_eq!(foo_fields[3].version_id, 0); + assert_eq!(foo_fields[3].size, PTR_SIZE); + assert_eq!(foo_fields[3].num, FOO_ARRAY_MAX as i32); + assert_eq!( + foo_fields[3].flags.0, + VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 + ); + assert!(foo_fields[3].vmsd.is_null()); + assert!(foo_fields[3].field_exists.is_none()); + + // The last VMStateField in VMSTATE_FOOC. + assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); +} + +// =========================== Test VMSTATE_FOOD =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOD: +// - VMSTATE_VALIDATE + +// Add more member fields when vmstate_of/vmstate_struct support "test" +// parameter. +struct FooD; + +impl FooD { + fn validate_food_0(&self, _version_id: u8) -> bool { + true + } + + fn validate_food_1(_state: &FooD, _version_id: u8) -> bool { + false + } +} + +fn validate_food_2(_state: &FooD, _version_id: u8) -> bool { + true +} + +static VMSTATE_FOOD: VMStateDescription = VMStateDescription { + name: c"foo_d".as_ptr(), + version_id: 3, + minimum_version_id: 1, + fields: vmstate_fields! { + vmstate_validate!(FooD, c"foo_d_0", FooD::validate_food_0), + vmstate_validate!(FooD, c"foo_d_1", FooD::validate_food_1), + vmstate_validate!(FooD, c"foo_d_2", validate_food_2), + }, + ..Zeroable::ZERO +}; + +#[test] +fn test_vmstate_validate() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOD.fields, 4) }; + let mut foo_d = FooD; + let foo_d_p = std::ptr::addr_of_mut!(foo_d).cast::(); + + // 1st VMStateField in VMSTATE_FOOD + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"foo_d_0\0" + ); + assert_eq!(foo_fields[0].offset, 0); + assert_eq!(foo_fields[0].num_offset, 0); + assert!(foo_fields[0].info.is_null()); + assert_eq!(foo_fields[0].version_id, 0); + assert_eq!(foo_fields[0].size, 0); + assert_eq!(foo_fields[0].num, 0); + assert_eq!( + foo_fields[0].flags.0, + VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_MUST_EXIST.0 + ); + assert!(foo_fields[0].vmsd.is_null()); + assert!(unsafe { foo_fields[0].field_exists.unwrap()(foo_d_p, 0) }); + + // 2nd VMStateField in VMSTATE_FOOD + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), + b"foo_d_1\0" + ); + assert!(!unsafe { foo_fields[1].field_exists.unwrap()(foo_d_p, 1) }); + + // 3rd VMStateField in VMSTATE_FOOD + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"foo_d_2\0" + ); + assert!(unsafe { foo_fields[2].field_exists.unwrap()(foo_d_p, 2) }); + + // The last VMStateField in VMSTATE_FOOD. + assert_eq!(foo_fields[3].flags, VMStateFlags::VMS_END); +} diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h new file mode 100644 index 00000000000..15a1b19847f --- /dev/null +++ b/rust/qemu-api/wrapper.h @@ -0,0 +1,71 @@ +/* + * 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/log.h" +#include "qemu/log-for-trace.h" +#include "qemu/module.h" +#include "qemu-io.h" +#include "system/system.h" +#include "hw/sysbus.h" +#include "system/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 "qapi/error-internal.h" +#include "migration/vmstate.h" +#include "chardev/char-serial.h" +#include "exec/memattrs.h" +#include "qemu/timer.h" +#include "system/address-spaces.h" +#include "hw/char/pl011.h" diff --git a/rust/rustfmt.toml b/rust/rustfmt.toml new file mode 100644 index 00000000000..ebecb99fe09 --- /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/scripts/analyse-locks-simpletrace.py b/scripts/analyse-locks-simpletrace.py index 63c11f4fce2..d650dd71408 100755 --- a/scripts/analyse-locks-simpletrace.py +++ b/scripts/analyse-locks-simpletrace.py @@ -75,7 +75,7 @@ def get_args(): (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 45c821de32b..d2c566667df 100644 --- a/scripts/analyze-inclusions +++ b/scripts/analyze-inclusions @@ -53,7 +53,7 @@ echo $(grep_include -F 'trace/generated-tracers.h') files include generated-trac echo $(grep_include -F 'qapi/error.h') files include qapi/error.h echo $(grep_include -F 'qom/object.h') files include qom/object.h echo $(grep_include -F 'block/aio.h') files include block/aio.h -echo $(grep_include -F 'exec/memory.h') files include exec/memory.h +echo $(grep_include -F 'system/memory.h') files include system/memory.h echo $(grep_include -F 'fpu/softfloat.h') files include fpu/softfloat.h echo $(grep_include -F 'qemu/bswap.h') files include qemu/bswap.h echo @@ -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 b82a1b0c58c..67631ac43e9 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -38,13 +38,13 @@ def __init__(self, filename): 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) @@ -65,6 +65,9 @@ def readvar(self, size = None): def tell(self): return self.file.tell() + def seek(self, a, b): + return self.file.seek(a, b) + # The VMSD description is at the end of the file, after EOF. Look for # the last NULL byte, then for the beginning brace of JSON. def read_migration_debug_json(self): @@ -111,6 +114,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 +126,7 @@ def __init__(self, file, version_id, ramargs, section_key): 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 +154,12 @@ def read(self): 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 +168,8 @@ def read(self): 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 +208,8 @@ def read(self): 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 +261,83 @@ def getDict(self): return "" +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): + pos = 0 + while True: + addr_flags = self.file.read64() + flags = addr_flags & 0xfff + + if flags & self.STATTR_FLAG_DONE: + pos = self.file.tell() + continue + elif flags & self.STATTR_FLAG_EOS: + return + else: + # No EOS came after DONE, that's OK, but rewind the + # stream because this is not our data. + if pos: + self.file.seek(pos, os.SEEK_SET) + return + raise Exception("Unknown flags %x", flags) + + 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): + 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): - name_len = self.file.read32() - name = self.file.readstr(len = name_len) + 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 +359,23 @@ def read(self): 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) @@ -325,6 +417,28 @@ def __init__(self, desc, file): super(VMSDFieldIntLE, self).__init__(desc, file) self.dtype = ' "$tar_file" test $? -ne 0 && error "failed to archive qemu" for sp in $subprojects; do meson subprojects download $sp test $? -ne 0 && error "failed to download subproject $sp" - tar --append --file "$tar_file" --exclude=.git subprojects/$sp + tar --append --file "$tar_file" --exclude=.git subprojects/"$(subproject_dir $sp)" test $? -ne 0 && error "failed to append subproject $sp to $tar_file" done exit 0 diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py index d4a183db61e..dbbde99e39e 100644 --- a/scripts/block-coroutine-wrapper.py +++ b/scripts/block-coroutine-wrapper.py @@ -71,10 +71,13 @@ def __init__(self, wrapper_type: str, return_type: str, name: str, 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: @@ -84,12 +87,11 @@ def __init__(self, wrapper_type: str, return_type: str, name: str, 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: - raise ValueError(f"no_co function can't be rdlock: {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.ctx = self.gen_ctx() - self.get_result = 's->ret = ' self.ret = 'return s.ret;' self.co_ret = 'return ' @@ -102,12 +104,13 @@ def __init__(self, wrapper_type: str, return_type: str, name: str, 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}bs)' + return f'bdrv_get_aio_context({prefix}{name})' elif t == 'BdrvChild *': - return f'bdrv_get_aio_context({prefix}child->bs)' + return f'bdrv_get_aio_context({prefix}{name}->bs)' elif t == 'BlockBackend *': - return f'blk_get_aio_context({prefix}blk)' + return f'blk_get_aio_context({prefix}{name})' else: return 'qemu_get_aio_context()' @@ -162,7 +165,7 @@ def create_mixed_wrapper(func: FuncDecl) -> str: {func.co_ret}{name}({ func.gen_list('{name}') }); }} else {{ {struct_name} s = {{ - .poll_state.ctx = {func.ctx}, + .poll_state.ctx = qemu_get_current_aio_context(), .poll_state.in_progress = true, { func.gen_block(' .{name} = {name},') } @@ -186,7 +189,7 @@ def create_co_wrapper(func: FuncDecl) -> str: {func.return_type} {func.name}({ func.gen_list('{decl}') }) {{ {struct_name} s = {{ - .poll_state.ctx = {func.ctx}, + .poll_state.ctx = qemu_get_current_aio_context(), .poll_state.in_progress = true, { func.gen_block(' .{name} = {name},') } @@ -250,6 +253,15 @@ def gen_no_co_wrapper(func: FuncDecl) -> str: 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} @@ -264,11 +276,10 @@ def gen_no_co_wrapper(func: FuncDecl) -> str: static void {name}_bh(void *opaque) {{ {struct_name} *s = opaque; - AioContext *ctx = {func.gen_ctx('s->')}; - aio_context_acquire(ctx); +{graph_lock} {func.get_result}{name}({ func.gen_list('s->{name}') }); - aio_context_release(ctx); +{graph_unlock} aio_co_wake(s->co); }} diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 1ad9ccb74ba..833f20f5555 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -35,6 +35,9 @@ 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 @@ sub help { 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 @@ sub help { } 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,48 @@ sub help { qr{guintptr}, ); +# Match text found in common license boilerplate comments: +# for new files the SPDX-License-Identifier line is sufficient. +our @LICENSE_BOILERPLATE = ( + "licensed under the terms of the GNU GPL", + "under the terms of the GNU General Public License", + "under the terms of the GNU Lesser General Public", + "Permission is hereby granted, free of charge", + "GNU GPL, version 2 or later", + "See the COPYING file" +); +our $LICENSE_BOILERPLATE_RE = join("|", @LICENSE_BOILERPLATE); + +# 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 +447,8 @@ sub build_types { 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 +472,7 @@ sub build_types { "-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 +536,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 +547,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) = @_; @@ -1260,26 +1342,179 @@ sub WARN { } } -# According to tests/qtest/bios-tables-test.c: do not -# change expected file in the same commit with adding test -sub checkfilename { - my ($name, $acpi_testexpected, $acpi_nontestexpected) = @_; - - # Note: shell script that rebuilds the expected files is in the same - # directory as files themselves. - # Note: allowed diff list can be changed both when changing expected - # files and when changing tests. - if ($name =~ m#^tests/data/acpi/# and not $name =~ m#^\.sh$#) { - $$acpi_testexpected = $name; - } elsif ($name !~ m#^tests/qtest/bios-tables-test-allowed-diff.h$#) { - $$acpi_nontestexpected = $name; +sub checkspdx { + my ($file, $expr) = @_; + + # Imported Linux headers probably have SPDX tags, but if they + # don't we're not requiring contributors to fix this, as these + # files are not expected to be modified locally in QEMU. + # Also don't accidentally detect own checking code. + if ($file =~ m,include/standard-headers, || + $file =~ m,linux-headers, || + $file =~ m,checkpatch.pl,) { + return; + } + + my $origexpr = $expr; + + # Flatten sub-expressions + $expr =~ s/\(|\)/ /g; + $expr =~ s/OR|AND/ /g; + + # Merge WITH exceptions to the license + $expr =~ s/\s+WITH\s+/-WITH-/g; + + # Cull more leading/trailing whitespace + $expr =~ s/^\s*//g; + $expr =~ s/\s*$//g; + + my @bits = split / +/, $expr; + + my $prefer = "GPL-2.0-or-later"; + my @valid = qw( + GPL-2.0-only + LGPL-2.1-only + LGPL-2.1-or-later + BSD-2-Clause + BSD-3-Clause + MIT + ); + + my $nonpreferred = 0; + my @unknown = (); + foreach my $bit (@bits) { + if ($bit eq $prefer) { + next; + } + if (grep /^$bit$/, @valid) { + $nonpreferred = 1; + } else { + push @unknown, $bit; + } + } + if (@unknown) { + ERROR("Saw unacceptable licenses '" . join(',', @unknown) . + "', valid choices for QEMU are:\n" . join("\n", $prefer, @valid)); + } + + if ($nonpreferred) { + WARN("Saw acceptable license '$origexpr' but note '$prefer' is " . + "preferred for new files unless the code is derived from a " . + "source file with an existing declared license that must be " . + "retained. Please explain the license choice in the commit " . + "message."); + } +} + +# All three of the methods below take a 'file info' record +# which is a hash ref containing +# +# 'isgit': 1 if an enhanced git diff or 0 for a plain diff +# 'githeader': 1 if still parsing git patch header, 0 otherwise +# 'linestart': line number of start of file diff +# 'lineend': line number of end of file diff +# 'filenew': the new filename +# 'fileold': the old filename (same as 'new filename' except +# for renames in git diffs) +# 'action': one of 'modified' (always) or 'new' or 'deleted' or +# 'renamed' (git diffs only) +# 'mode': file mode for new/deleted files (git diffs only) +# 'similarity': file similarity when renamed (git diffs only) +# 'facts': hash ref for storing any metadata related to checks +# + +# Called at the end of each patch, with the list of +# real filenames that were seen in the patch +sub process_file_list { + my @fileinfos = @_; + + # According to tests/qtest/bios-tables-test.c: do not + # change expected file in the same commit with adding test + my @acpi_testexpected; + my @acpi_nontestexpected; + + foreach my $fileinfo (@fileinfos) { + # Note: shell script that rebuilds the expected files is in + # the same directory as files themselves. + # Note: allowed diff list can be changed both when changing + # expected files and when changing tests. + if ($fileinfo->{filenew} =~ m#^tests/data/acpi/# && + $fileinfo->{filenew} !~ m#^\.sh$#) { + push @acpi_testexpected, $fileinfo->{filenew}; + } elsif ($fileinfo->{filenew} !~ + m#^tests/qtest/bios-tables-test-allowed-diff.h$#) { + push @acpi_nontestexpected, $fileinfo->{filenew}; + } } - if (defined $$acpi_testexpected and defined $$acpi_nontestexpected) { + if (int(@acpi_testexpected) > 0 and int(@acpi_nontestexpected) > 0) { ERROR("Do not add expected files together with tests, " . "follow instructions in " . - "tests/qtest/bios-tables-test.c: both " . - $$acpi_testexpected . " and " . - $$acpi_nontestexpected . " found\n"); + "tests/qtest/bios-tables-test.c. Files\n\n " . + join("\n ", @acpi_testexpected) . + "\n\nand\n\n " . + join("\n ", @acpi_nontestexpected) . + "\n\nfound in the same patch\n"); + } + + my $sawmaintainers = 0; + my @maybemaintainers; + foreach my $fileinfo (@fileinfos) { + if ($fileinfo->{action} ne "modified" && + $fileinfo->{filenew} !~ m#^tests/data/acpi/#) { + push @maybemaintainers, $fileinfo->{filenew}; + } + if ($fileinfo->{filenew} eq "MAINTAINERS") { + $sawmaintainers = 1; + } + } + + # If we don't see a MAINTAINERS update, prod the user to check + if (int(@maybemaintainers) > 0 && !$sawmaintainers) { + WARN("added, moved or deleted file(s):\n\n " . + join("\n ", @maybemaintainers) . + "\n\nDoes MAINTAINERS need updating?\n"); + } +} + +# Called at the start of processing a diff hunk for a file +sub process_start_of_file { + my $fileinfo = shift; + + # Check for incorrect file permissions + if ($fileinfo->{action} eq "new" && ($fileinfo->{mode} & 0111)) { + my $permhere = $fileinfo->{linestart} . "FILE: " . + $fileinfo->{filenew} . "\n"; + if ($fileinfo->{filenew} =~ + /(\bMakefile.*|\.(c|cc|cpp|h|mak|s|S))$/) { + ERROR("do not set execute permissions for source " . + "files\n" . $permhere); + } + } +} + +# Called at the end of processing a diff hunk for a file +sub process_end_of_file { + my $fileinfo = shift; + + if ($fileinfo->{action} eq "new" && + !exists $fileinfo->{facts}->{sawspdx}) { + if ($fileinfo->{filenew} =~ + /(\.(c|h|py|pl|sh|json|inc|rs)|Makefile.*)$/) { + # source code files MUST have SPDX license declared + ERROR("New file '" . $fileinfo->{filenew} . + "' requires 'SPDX-License-Identifier'"); + } else { + # Other files MAY have SPDX license if appropriate + WARN("Does new file '" . $fileinfo->{filenew} . + "' need 'SPDX-License-Identifier'?"); + } + } + if ($fileinfo->{action} eq "new" && + exists $fileinfo->{facts}->{sawboilerplate}) { + ERROR("New file '" . $fileinfo->{filenew} . "' must " . + "not have license boilerplate header text, only " . + "the SPDX-License-Identifier, unless this file was " . + "copied from existing code already having such text."); } } @@ -1303,7 +1538,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 = (); @@ -1316,7 +1553,10 @@ sub process { my $realfile = ''; my $realline = 0; my $realcnt = 0; + my $fileinfo; + my @fileinfolist; my $here = ''; + my $oldhere = ''; my $in_comment = 0; my $comment_edge = 0; my $first_line = 0; @@ -1329,9 +1569,6 @@ sub process { my %suppress_whiletrailers; my %suppress_export; - my $acpi_testexpected; - my $acpi_nontestexpected; - # Pre-scan the patch sanitizing the lines. sanitise_line_reset(); @@ -1454,18 +1691,54 @@ sub process { $prefix = "$filename:$realline: " if ($emacs && $file); $prefix = "$filename:$linenr: " if ($emacs && !$file); + $oldhere = $here; $here = "#$linenr: " if (!$file); $here = "#$realline: " if ($file); # extract the filename as it passes - if ($line =~ /^diff --git.*?(\S+)$/) { - $realfile = $1; - $realfile =~ s@^([^/]*)/@@ if (!$file); - checkfilename($realfile, \$acpi_testexpected, \$acpi_nontestexpected); + if ($line =~ /^diff --git\s+(\S+)\s+(\S+)$/) { + my $fileold = $1; + my $filenew = $2; + + if (defined $fileinfo) { + $fileinfo->{lineend} = $oldhere; + process_end_of_file($fileinfo) + } + $fileold =~ s@^([^/]*)/@@ if (!$file); + $filenew =~ s@^([^/]*)/@@ if (!$file); + $realfile = $filenew; + + $fileinfo = { + "isgit" => 1, + "githeader" => 1, + "linestart" => $here, + "lineend" => 0, + "fileold" => $fileold, + "filenew" => $filenew, + "action" => "modified", + "mode" => 0, + "similarity" => 0, + "facts" => {}, + }; + push @fileinfolist, $fileinfo; + } elsif (defined $fileinfo && $fileinfo->{githeader} && + $line =~ /^(new|deleted) (?:file )?mode\s+([0-7]+)$/) { + $fileinfo->{action} = $1; + $fileinfo->{mode} = oct($2); + } elsif (defined $fileinfo && $fileinfo->{githeader} && + $line =~ /^similarity index (\d+)%/) { + $fileinfo->{similarity} = int($1); + } elsif (defined $fileinfo && $fileinfo->{githeader} && + $line =~ /^rename (from|to) [\w\/\.\-]+\s*$/) { + $fileinfo->{action} = "renamed"; + # For a no-change rename, we'll never have any "+++..." + # lines, so trigger actions now + if ($1 eq "to" && $fileinfo->{similarity} == 100) { + process_start_of_file($fileinfo); + } } elsif ($line =~ /^\+\+\+\s+(\S+)/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@ if (!$file); - checkfilename($realfile, \$acpi_testexpected, \$acpi_nontestexpected); $p1_prefix = $1; if (!$file && $tree && $p1_prefix ne '' && @@ -1473,6 +1746,30 @@ sub process { WARN("patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); } + if (defined $fileinfo && !$fileinfo->{isgit}) { + $fileinfo->{lineend} = $oldhere; + process_end_of_file($fileinfo); + } + + if (!defined $fileinfo || !$fileinfo->{isgit}) { + $fileinfo = { + "isgit" => 0, + "githeader" => 0, + "linestart" => $here, + "lineend" => 0, + "fileold" => $realfile, + "filenew" => $realfile, + "action" => "modified", + "mode" => 0, + "similarity" => 0, + "facts" => {}, + }; + push @fileinfolist, $fileinfo; + } else { + $fileinfo->{githeader} = 0; + } + process_start_of_file($fileinfo); + next; } @@ -1484,14 +1781,6 @@ sub process { $cnt_lines++ if ($realcnt != 0); -# Check for incorrect file permissions - if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { - my $permhere = $here . "FILE: $realfile\n"; - if ($realfile =~ /(\bMakefile(?:\.objs)?|\.c|\.cc|\.cpp|\.h|\.mak|\.[sS])$/) { - ERROR("do not set execute permissions for source files\n" . $permhere); - } - } - # Only allow Python 3 interpreter if ($realline == 1 && $line =~ /^\+#!\ *\/usr\/bin\/(?:env )?python$/) { @@ -1503,7 +1792,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); } @@ -1523,23 +1812,27 @@ sub process { } } -# Check if MAINTAINERS is being updated. If so, there's probably no need to -# emit the "does MAINTAINERS need updating?" message on file add/move/delete - if ($line =~ /^\s*MAINTAINERS\s*\|/) { - $reported_maintainer_file = 1; +# Check SPDX-License-Identifier references a permitted license + if ($rawline =~ m,SPDX-License-Identifier: (.*?)(\*/)?\s*$,) { + $fileinfo->{facts}->{sawspdx} = 1; + &checkspdx($realfile, $1); } -# Check for added, moved or deleted files - if (!$reported_maintainer_file && !$in_commit_log && - ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || - $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || - ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && - (defined($1) || defined($2)))) && - !(($realfile ne '') && - defined($acpi_testexpected) && - ($realfile eq $acpi_testexpected))) { - $reported_maintainer_file = 1; - WARN("added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); + if ($rawline =~ /$LICENSE_BOILERPLATE_RE/) { + $fileinfo->{facts}->{sawboilerplate} = 1; + } + + if ($rawline =~ m,(SPDX-[a-zA-Z0-9-_]+):,) { + my $tag = $1; + my @permitted = qw( + SPDX-License-Identifier + ); + + unless (grep { /^$tag$/ } @permitted) { + ERROR("Tag $tag not permitted in QEMU code, " . + "valid choices are: " . + join(", ", @permitted)); + } } # Check for wrappage within a valid hunk of the file @@ -1585,9 +1878,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)\//); @@ -2084,7 +2413,7 @@ sub process { # missing space after union, struct or enum definition if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?(?:\s+$Ident)?[=\{]/) { - ERROR("missing space after $1 definition\n" . $herecurr); + ERROR("missing space after $1 definition\n" . $herecurr); } # check for spacing round square brackets; allowed: @@ -2137,7 +2466,7 @@ sub process { } } # Check operator spacing. - if (!($line=~/\#\s*include/)) { + if (!($line=~/\#\s*(include|import)/)) { my $ops = qr{ <<=|>>=|<=|>=|==|!=| \+=|-=|\*=|\/=|%=|\^=|\|=|&=| @@ -2379,7 +2708,7 @@ sub process { if ($line =~ /^.\s*(Q(?:S?LIST|SIMPLEQ|TAILQ)_HEAD)\s*\(\s*[^,]/ && $line !~ /^.typedef/) { - ERROR("named $1 should be typedefed separately\n" . $herecurr); + ERROR("named $1 should be typedefed separately\n" . $herecurr); } # Need a space before open parenthesis after if, while etc @@ -2928,48 +3257,50 @@ sub process { # Qemu error function tests - # Find newlines in error messages - my $qemu_error_funcs = qr{error_setg| - error_setg_errno| - error_setg_win32| - error_setg_file_open| - error_set| - error_prepend| - warn_reportf_err| - error_reportf_err| - error_vreport| - warn_vreport| - info_vreport| - error_report| - warn_report| - info_report| - g_test_message}x; - - if ($rawline =~ /\b(?:$qemu_error_funcs)\s*\(.*\".*\\n/) { - ERROR("Error messages should not contain newlines\n" . $herecurr); - } + # Find newlines in error messages + my $qemu_error_funcs = qr{error_setg| + error_setg_errno| + error_setg_win32| + error_setg_file_open| + error_set| + error_prepend| + warn_reportf_err| + error_reportf_err| + error_vreport| + warn_vreport| + info_vreport| + error_report| + warn_report| + info_report| + g_test_message}x; + + if ($rawline =~ /\b(?:$qemu_error_funcs)\s*\(.*\".*\\n/) { + ERROR("Error messages should not contain newlines\n" . $herecurr); + } - # Continue checking for error messages that contains newlines. This - # check handles cases where string literals are spread over multiple lines. - # Example: - # error_report("Error msg line #1" - # "Error msg line #2\n"); - my $quoted_newline_regex = qr{\+\s*\".*\\n.*\"}; - my $continued_str_literal = qr{\+\s*\".*\"}; + # Continue checking for error messages that contains newlines. + # This check handles cases where string literals are spread + # over multiple lines. + # Example: + # error_report("Error msg line #1" + # "Error msg line #2\n"); + my $quoted_newline_regex = qr{\+\s*\".*\\n.*\"}; + my $continued_str_literal = qr{\+\s*\".*\"}; - if ($rawline =~ /$quoted_newline_regex/) { - # Backtrack to first line that does not contain only a quoted literal - # and assume that it is the start of the statement. - my $i = $linenr - 2; + if ($rawline =~ /$quoted_newline_regex/) { + # Backtrack to first line that does not contain only + # a quoted literal and assume that it is the start + # of the statement. + my $i = $linenr - 2; - while (($i >= 0) & $rawlines[$i] =~ /$continued_str_literal/) { - $i--; - } + while (($i >= 0) & $rawlines[$i] =~ /$continued_str_literal/) { + $i--; + } - if ($rawlines[$i] =~ /\b(?:$qemu_error_funcs)\s*\(/) { - ERROR("Error messages should not contain newlines\n" . $herecurr); + if ($rawlines[$i] =~ /\b(?:$qemu_error_funcs)\s*\(/) { + ERROR("Error messages should not contain newlines\n" . $herecurr); + } } - } # check for non-portable libc calls that have portable alternatives in QEMU if ($line =~ /\bffs\(/) { @@ -2993,6 +3324,13 @@ sub process { 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| @@ -3016,6 +3354,11 @@ sub process { } } + if (defined $fileinfo) { + process_end_of_file($fileinfo); + } + process_file_list(@fileinfolist); + if ($is_patch && $chk_signoff && $signoff == 0) { ERROR("Missing Signed-off-by: line(s)\n"); } diff --git a/scripts/ci/gitlab-ci-section b/scripts/ci/gitlab-ci-section new file mode 100644 index 00000000000..9bbe80420d6 --- /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-pipeline-status b/scripts/ci/gitlab-pipeline-status index e3343b05108..39f3c22c665 100755 --- a/scripts/ci/gitlab-pipeline-status +++ b/scripts/ci/gitlab-pipeline-status @@ -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 1ead77e2cbf..00000000000 --- a/scripts/ci/org.centos/stream/8/build-environment.yml +++ /dev/null @@ -1,82 +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 EPEL repo on CentOS Stream 8 - dnf: - name: - - epel-release - state: present - when: - - centos_stream_8 - - - name: Enable PowerTools repo on CentOS Stream 8 - ini_file: - path: /etc/yum.repos.d/CentOS-Stream-PowerTools.repo - section: powertools - option: enabled - value: "1" - when: - - centos_stream_8 - - - name: Install basic packages to build QEMU on CentOS Stream 8 - dnf: - name: - - bzip2 - - bzip2-devel - - capstone-devel - - dbus-daemon - - device-mapper-multipath-devel - - diffutils - - gcc - - gcc-c++ - - genisoimage - - gettext - - git - - glib2-devel - - glusterfs-api-devel - - gnutls-devel - - libaio-devel - - libcap-ng-devel - - libcurl-devel - - libepoxy-devel - - libfdt-devel - - libgcrypt-devel - - libiscsi-devel - - libpmem-devel - - librados-devel - - librbd-devel - - libseccomp-devel - - libslirp-devel - - libssh-devel - - libxkbcommon-devel - - lzo-devel - - make - - mesa-libEGL-devel - - nettle-devel - - ninja-build - - nmap-ncat - - numactl-devel - - pixman-devel - - python38 - - python3-sphinx - - rdma-core-devel - - redhat-rpm-config - - snappy-devel - - spice-glib-devel - - spice-server-devel - - systemd-devel - - systemtap-sdt-devel - - tar - - zlib-devel - state: present - when: - - 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 131f8ee5f3f..00000000000 --- a/scripts/ci/org.centos/stream/8/x86_64/configure +++ /dev/null @@ -1,198 +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 \ ---python=/usr/bin/python3.8 \ ---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 \ ---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 \ ---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-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-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-backends=dtrace \ ---enable-usb-redir \ ---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 73e7a1a3126..00000000000 --- a/scripts/ci/org.centos/stream/8/x86_64/test-avocado +++ /dev/null @@ -1,65 +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: -# -# ./pyvenv/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 -# -# * 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 -./pyvenv/bin/avocado run \ - --job-results-dir=tests/results/ \ - tests/avocado/boot_linux.py:BootLinuxX8664.test_pc_i440fx_kvm \ - tests/avocado/boot_linux.py:BootLinuxX8664.test_pc_q35_kvm \ - 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/netdev-ethtool.py:NetDevEthtool.test_igb \ - tests/avocado/netdev-ethtool.py:NetDevEthtool.test_igb_nomsi \ - tests/avocado/info_usernet.py:InfoUsernet.test_hostfwd \ - tests/avocado/intel_iommu.py:IntelIOMMU.test_intel_iommu \ - tests/avocado/intel_iommu.py:IntelIOMMU.test_intel_iommu_pt \ - 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 e3eadfe3eaa..00000000000 --- 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 f344d1a8509..00000000000 --- a/scripts/ci/setup/build-environment.yml +++ /dev/null @@ -1,274 +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' - - - name: Update apt cache / upgrade packages via apt - apt: - update_cache: yes - upgrade: yes - when: - - ansible_facts['distribution'] == 'Ubuntu' - - # lcitool variables -f json ubuntu-2204 qemu | jq -r '.pkgs[]' | xargs -n 1 echo "-" - - name: Install basic packages to build QEMU on Ubuntu 22.04 - package: - name: - - bash - - bc - - bison - - bsdextrautils - - bzip2 - - ca-certificates - - ccache - - clang - - dbus - - debianutils - - diffutils - - exuberant-ctags - - findutils - - flex - - g++ - - gcc - - gcovr - - genisoimage - - gettext - - git - - hostname - - libaio-dev - - libasan5 - - libasound2-dev - - libattr1-dev - - libbpf-dev - - libbrlapi-dev - - libbz2-dev - - libc6-dev - - libcacard-dev - - libcap-ng-dev - - libcapstone-dev - - libcmocka-dev - - libcurl4-gnutls-dev - - libdaxctl-dev - - libdrm-dev - - libepoxy-dev - - libfdt-dev - - libffi-dev - - libgbm-dev - - libgcrypt20-dev - - libglib2.0-dev - - libglusterfs-dev - - libgnutls28-dev - - libgtk-3-dev - - libibumad-dev - - libibverbs-dev - - libiscsi-dev - - libjemalloc-dev - - libjpeg-turbo8-dev - - libjson-c-dev - - liblttng-ust-dev - - liblzo2-dev - - 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 - - libslirp-dev - - libsnappy-dev - - libspice-protocol-dev - - libspice-server-dev - - libssh-dev - - libsystemd-dev - - libtasn1-6-dev - - libubsan1 - - libudev-dev - - liburing-dev - - libusb-1.0-0-dev - - libusbredirhost-dev - - libvdeplug-dev - - libvirglrenderer-dev - - libvte-2.91-dev - - libxen-dev - - libxml2-dev - - libzstd-dev - - llvm - - locales - - make - - meson - - multipath-tools - - ncat - - nettle-dev - - ninja-build - - openssh-client - - pkgconf - - python3 - - python3-numpy - - python3-opencv - - python3-pillow - - python3-pip - - python3-sphinx - - python3-sphinx-rtd-theme - - python3-venv - - python3-yaml - - rpm2cpio - - sed - - sparse - - systemtap-sdt-dev - - tar - - tesseract-ocr - - tesseract-ocr-eng - - texinfo - - xfslibs-dev - - zlib1g-dev - state: present - when: - - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['distribution_version'] == '22.04' - - - name: Install 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' - - - name: Enable EPEL repo on EL8 - dnf: - name: - - epel-release - state: present - when: - - ansible_facts['distribution_file_variety'] in ['RedHat', 'CentOS'] - - ansible_facts['distribution_major_version'] == '8' - - - name: Enable PowerTools repo on CentOS 8 - ini_file: - path: /etc/yum.repos.d/CentOS-Stream-PowerTools.repo - section: powertools - option: enabled - value: "1" - when: - - ansible_facts['distribution_file_variety'] == 'CentOS' - - ansible_facts['distribution_major_version'] == '8' - - - name: Install basic packages to build QEMU on EL8 - dnf: - # This list of packages start with tests/docker/dockerfiles/centos8.docker - # but only include files that are common to all distro variants and present - # in the standard repos (no add-ons) - name: - - bzip2 - - bzip2-devel - - capstone-devel - - dbus-daemon - - device-mapper-multipath-devel - - diffutils - - gcc - - gcc-c++ - - genisoimage - - gettext - - git - - glib2-devel - - glusterfs-api-devel - - gnutls-devel - - libaio-devel - - libcap-ng-devel - - libcurl-devel - - libepoxy-devel - - libfdt-devel - - libgcrypt-devel - - libiscsi-devel - - libpmem-devel - - librados-devel - - librbd-devel - - libseccomp-devel - - libssh-devel - - libxkbcommon-devel - - lzo-devel - - make - - mesa-libEGL-devel - - nettle-devel - - ninja-build - - nmap-ncat - - numactl-devel - - pixman-devel - - python38 - - python3-sphinx - - rdma-core-devel - - redhat-rpm-config - - snappy-devel - - spice-glib-devel - - systemd-devel - - systemtap-sdt-devel - - tar - - zlib-devel - state: present - when: - - ansible_facts['distribution_file_variety'] in ['RedHat', 'CentOS'] - - ansible_facts['distribution_version'] == '8' - - - name: Install packages only available on x86 and aarch64 - dnf: - # Spice server not available in ppc64le - name: - - spice-server - - spice-server-devel - state: present - when: - - ansible_facts['distribution_file_variety'] in ['RedHat', 'CentOS'] - - ansible_facts['distribution_version'] == '8' - - ansible_facts['architecture'] == 'aarch64' or ansible_facts['architecture'] == 'x86_64' - - - name: Check whether the Python runtime version is managed by alternatives - stat: - path: /etc/alternatives/python3 - register: python3 - - - name: Set default Python runtime to 3.8 on EL8 - command: alternatives --set python3 /usr/bin/python3.8 - when: - - ansible_facts['distribution_file_variety'] in ['RedHat', 'CentOS'] - - ansible_facts['distribution_version'] == '8' - - python3.stat.islnk and python3.stat.lnk_target != '/usr/bin/python3.8' diff --git a/scripts/ci/setup/gitlab-runner.yml b/scripts/ci/setup/gitlab-runner.yml index 7bdafab5115..57e7faebf10 100644 --- a/scripts/ci/setup/gitlab-runner.yml +++ b/scripts/ci/setup/gitlab-runner.yml @@ -49,30 +49,51 @@ - debug: msg: gitlab-runner arch is {{ gitlab_runner_arch }} - - name: Download the matching gitlab-runner (DEB) + # Debian/Ubuntu setup + - name: Get gitlab-runner repo setup script (DEB) get_url: dest: "/root/" - url: "https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_{{ gitlab_runner_arch }}.deb" + url: "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" + mode: 0755 when: - ansible_facts['distribution'] == 'Ubuntu' - - name: Download the matching gitlab-runner (RPM) + - 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://gitlab-runner-downloads.s3.amazonaws.com/latest/rpm/gitlab-runner_{{ gitlab_runner_arch }}.rpm" + url: "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" + mode: 0755 when: - ansible_facts['distribution'] == 'CentOS' - - name: Install gitlab-runner via package manager (DEB) - apt: deb="/root/gitlab-runner_{{ gitlab_runner_arch }}.deb" + - name: Run gitlab-runner repo setup script (RPM) + shell: "/root/script.rpm.sh" when: - - ansible_facts['distribution'] == 'Ubuntu' + - ansible_facts['distribution'] == 'CentOS' - - name: Install gitlab-runner via package manager (RPM) - yum: name="/root/gitlab-runner_{{ gitlab_runner_arch }}.rpm" + - 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/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\"] }})'" diff --git a/scripts/ci/setup/ubuntu/build-environment.yml b/scripts/ci/setup/ubuntu/build-environment.yml new file mode 100644 index 00000000000..56b51609e38 --- /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 00000000000..f11e9808267 --- /dev/null +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml @@ -0,0 +1,130 @@ +# 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-1.77 + - sed + - socat + - sparse + - swtpm + - systemtap-sdt-dev + - tar + - tesseract-ocr + - tesseract-ocr-eng + - vulkan-tools + - 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 00000000000..0cc34cd10b9 --- /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 00000000000..6559cb29343 --- /dev/null +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml @@ -0,0 +1,128 @@ +# 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-1.77 + - sed + - socat + - sparse + - swtpm + - systemtap-sdt-dev + - tar + - tesseract-ocr + - tesseract-ocr-eng + - vulkan-tools + - xorriso + - zlib1g-dev + - zstd + diff --git a/scripts/clean-includes b/scripts/clean-includes index 58e1607a82e..25dbf16c021 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 @@ -130,8 +130,8 @@ for f in "$@"; do *include/qemu/compiler.h | \ *include/qemu/qemu-plugin.h | \ *include/glib-compat.h | \ - *include/sysemu/os-posix.h | \ - *include/sysemu/os-win32.h | \ + *include/system/os-posix.h | \ + *include/system/os-win32.h | \ *include/standard-headers/ ) # Removing include lines from osdep.h itself would be counterproductive. echo "SKIPPING $f (special case header)" @@ -174,7 +174,7 @@ for f in "$@"; do - "sysemu/os-posix.h, sysemu/os-win32.h "glib-compat.h" + "system/os-posix.h, system/os-win32.h "glib-compat.h" "qemu/typedefs.h" ))' "$f" diff --git a/scripts/cocci-macro-file.h b/scripts/cocci-macro-file.h index d247a5086e9..c64831d5408 100644 --- a/scripts/cocci-macro-file.h +++ b/scripts/cocci-macro-file.h @@ -23,11 +23,7 @@ #define G_GNUC_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #define G_GNUC_NULL_TERMINATED __attribute__((sentinel)) -#if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) -# define QEMU_PACKED __attribute__((gcc_struct, packed)) -#else -# define QEMU_PACKED __attribute__((packed)) -#endif +#define QEMU_PACKED __attribute__((packed)) #define cat(x,y) x ## y #define cat2(x,y) cat(x,y) diff --git a/scripts/coccinelle/device-reset.cocci b/scripts/coccinelle/device-reset.cocci new file mode 100644 index 00000000000..510042afcca --- /dev/null +++ b/scripts/coccinelle/device-reset.cocci @@ -0,0 +1,30 @@ +// Convert opencoded DeviceClass::reset assignments to calls to +// device_class_set_legacy_reset() +// +// Copyright Linaro Ltd 2024 +// This work is licensed under the terms of the GNU GPLv2 or later. +// +// spatch --macro-file scripts/cocci-macro-file.h \ +// --sp-file scripts/coccinelle/device-reset.cocci \ +// --keep-comments --smpl-spacing --in-place --include-headers --dir hw +// +// For simplicity we assume some things about the code we're modifying +// that happen to be true for all our targets: +// * all cpu_class_set_parent_reset() callsites have a 'DeviceClass *dc' local +// * the parent reset field in the target CPU class is 'parent_reset' +// * no reset function already has a 'dev' local + +@@ +identifier dc, resetfn; +@@ + DeviceClass *dc; + ... +- dc->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 00000000000..14abdd7bd0c --- /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/codeconverter/codeconverter/qom_type_info.py b/scripts/codeconverter/codeconverter/qom_type_info.py index 255cb59923d..22a25560760 100644 --- a/scripts/codeconverter/codeconverter/qom_type_info.py +++ b/scripts/codeconverter/codeconverter/qom_type_info.py @@ -798,7 +798,8 @@ def gen_patches(self) -> Iterable[Patch]: # # # if 'class_init' not in fields: -# yield self.prepend(('static void %s_class_init(ObjectClass *oc, void *data)\n' +# yield self.prepend(('static void %s_class_init(ObjectClass *oc,\n' +# 'const void *data)\n' # '{\n' # '}\n\n') % (ids.lowercase)) # yield self.append_field('class_init', ids.lowercase+'_class_init') @@ -901,26 +902,6 @@ class TypeRegisterCall(FileMatch): regexp = S(r'^[ \t]*', NAMED('func_name', 'type_register'), r'\s*\(&\s*', NAMED('name', RE_IDENTIFIER), r'\s*\);[ \t]*\n') -class MakeTypeRegisterStatic(TypeRegisterCall): - """Make type_register() call static if variable is static const""" - def gen_patches(self): - var = self.file.find_match(TypeInfoVar, self.name) - if var is None: - self.warn("can't find TypeInfo var declaration for %s", self.name) - return - if var.is_static() and var.is_const(): - yield self.group_match('func_name').make_patch('type_register_static') - -class MakeTypeRegisterNotStatic(TypeRegisterStaticCall): - """Make type_register() call static if variable is static const""" - def gen_patches(self): - var = self.file.find_match(TypeInfoVar, self.name) - if var is None: - self.warn("can't find TypeInfo var declaration for %s", self.name) - return - if not var.is_static() or not var.is_const(): - yield self.group_match('func_name').make_patch('type_register') - class TypeInfoMacro(FileMatch): """TYPE_INFO macro usage""" regexp = S(r'^[ \t]*TYPE_INFO\s*\(\s*', NAMED('name', RE_IDENTIFIER), r'\s*\)[ \t]*;?[ \t]*\n') diff --git a/scripts/codeconverter/codeconverter/test_regexps.py b/scripts/codeconverter/codeconverter/test_regexps.py index a445634d88a..4526268ae80 100644 --- a/scripts/codeconverter/codeconverter/test_regexps.py +++ b/scripts/codeconverter/codeconverter/test_regexps.py @@ -70,15 +70,15 @@ def fullmatch(regexp, s): .name = armsse_variants[i].name, .parent = TYPE_ARMSSE, .class_init = armsse_class_init, - .class_data = (void *)&armsse_variants[i], + .class_data = &armsse_variants[i], };''', re.MULTILINE) print(RE_ARRAY_ITEM) assert fullmatch(RE_ARRAY_ITEM, '{ TYPE_HOTPLUG_HANDLER },') assert fullmatch(RE_ARRAY_ITEM, '{ TYPE_ACPI_DEVICE_IF },') assert fullmatch(RE_ARRAY_ITEM, '{ }') - assert fullmatch(RE_ARRAY_CAST, '(InterfaceInfo[])') - assert fullmatch(RE_ARRAY, '''(InterfaceInfo[]) { + assert fullmatch(RE_ARRAY_CAST, '(const InterfaceInfo[])') + assert fullmatch(RE_ARRAY, '''(const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, { } @@ -98,7 +98,7 @@ def fullmatch(regexp, s): .parent = TYPE_DEVICE, .instance_size = sizeof(CRBState), .class_init = tpm_crb_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TPM_IF }, { } } @@ -134,7 +134,7 @@ def fullmatch(regexp, s): .instance_size = sizeof(AcpiGedState), .instance_init = acpi_ged_initfn, .class_init = acpi_ged_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, { } @@ -164,7 +164,7 @@ def fullmatch(regexp, s): .parent = TYPE_DEVICE, .instance_size = sizeof(CRBState), .class_init = tpm_crb_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TPM_IF }, { } } @@ -269,7 +269,7 @@ def test_initial_includes(): #include "hw/pci/pci.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "sysemu/dma.h" +#include "system/dma.h" /* Missing stuff: SCTRL_P[12](END|ST)INC @@ -278,5 +278,5 @@ def test_initial_includes(): m = InitialIncludes.domatch(c) assert m print(repr(m.group(0))) - assert m.group(0).endswith('#include "sysemu/dma.h"\n') + assert m.group(0).endswith('#include "system/dma.h"\n') diff --git a/scripts/compare-machine-types.py b/scripts/compare-machine-types.py new file mode 100755 index 00000000000..2af3995eb82 --- /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/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index 883da95aff2..72995903ff9 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -1,160 +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/.*) - -cris - ~ (/qemu)?((/include)?/hw/cris/.*|/target/cris/.*) + ~ .*/qemu((/include)?/hw/avr/.*|/target/avr/.*) hexagon-gen (component should be ignored in analysis) - ~ (/qemu)?(/target/hexagon/.*generated.*) + ~ .*/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) + ~ .*/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/.*) + ~ .*/qemu((/include)?/hw/m68k/.*|/target/m68k/.*|(/include)?/hw(/.*)?/mcf.*|(/include)?/hw/nubus/.*) microblaze - ~ (/qemu)?((/include)?/hw/microblaze/.*|/target/microblaze/.*) + ~ .*/qemu((/include)?/hw/microblaze/.*|/target/microblaze/.*) mips - ~ (/qemu)?((/include)?/hw/mips/.*|/target/mips/.*) - -nios2 - ~ (/qemu)?((/include)?/hw/nios2/.*|/target/nios2/.*) + ~ .*/qemu((/include)?/hw/mips/.*|/target/mips/.*) openrisc - ~ (/qemu)?((/include)?/hw/openrisc/.*|/target/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).*) + ~ .*/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/.*) + ~ .*/qemu((/include)?/hw/rx/.*|/target/rx/.*) s390 - ~ (/qemu)?((/include)?/hw/s390x/.*|/target/s390x/.*|/hw/.*/s390_.*) + ~ .*/qemu((/include)?/hw/s390x/.*|/target/s390x/.*|/hw/.*/s390_.*) sh4 - ~ (/qemu)?((/include)?/hw/sh4/.*|/target/sh4/.*) + ~ .*/qemu((/include)?/hw/sh4/.*|/target/sh4/.*) sparc - ~ (/qemu)?((/include)?/hw/sparc(64)?.*|/target/sparc/.*|/hw/.*/grlib.*|/hw/display/cg3.c) + ~ .*/qemu((/include)?/hw/sparc(64)?.*|/target/sparc/.*|/hw/.*/grlib.*|/hw/display/cg3.c) tricore - ~ (/qemu)?((/include)?/hw/tricore/.*|/target/tricore/.*) + ~ .*/qemu((/include)?/hw/tricore/.*|/target/tricore/.*) xtensa - ~ (/qemu)?((/include)?/hw/xtensa/.*|/target/xtensa/.*) + ~ .*/qemu((/include)?/hw/xtensa/.*|/target/xtensa/.*) 9pfs - ~ (/qemu)?(/hw/9pfs/.*|/fsdev/.*) + ~ .*/qemu(/hw/9pfs/.*|/fsdev/.*) audio - ~ (/qemu)?((/include)?/(audio|hw/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).*) + ~ .*/qemu(/block.*|(/include?)/(block|storage-daemon)/.*|(/include)?/hw/(block|ide|nvme)/.*|/qemu-(img|io).*|/util/(aio|async|thread-pool).*) char - ~ (/qemu)?(/qemu-char\.c|/include/sysemu/char\.h|(/include)?/hw/char/.*) + ~ .*/qemu((/include)?/hw/char/.*) + +chardev + ~ .*/qemu((/include)?/chardev/.*) crypto - ~ (/qemu)?((/include)?/crypto/.*|/hw/.*/.*crypto.*|(/include/sysemu|/backends)/cryptodev.*) + ~ .*/qemu((/include)?/crypto/.*|/hw/.*/.*crypto.*|(/include/system|/backends)/cryptodev.*|/host/include/.*/host/crypto/.*) disas - ~ (/qemu)?((/include)?/disas.*) + ~ .*/qemu((/include)?/disas.*) fpu - ~ (/qemu)?((/include)?(/fpu|/libdecnumber)/.*) + ~ .*/qemu((/include)?(/fpu|/libdecnumber)/.*) io - ~ (/qemu)?((/include)?/io/.*) + ~ .*/qemu((/include)?/io/.*) ipmi - ~ (/qemu)?((/include)?/hw/ipmi/.*) + ~ .*/qemu((/include)?/hw/ipmi/.*) migration - ~ (/qemu)?((/include)?/migration/.*) + ~ .*/qemu((/include)?/migration/.*) monitor - ~ (/qemu)?(/qapi.*|/qobject/.*|/monitor\..*|/[hq]mp\..*) + ~ .*/qemu((/include)?/(qapi|qobject|monitor)/.*|/job-qmp.c) nbd - ~ (/qemu)?(/nbd/.*|/include/block/nbd.*|/qemu-nbd\.c) + ~ .*/qemu(/nbd/.*|/include/block/nbd.*|/qemu-nbd\.c) net - ~ (/qemu)?((/include)?(/hw)?/(net|rdma)/.*) + ~ .*/qemu((/include)?(/hw)?/(net|rdma)/.*) pci - ~ (/qemu)?(/include)?/hw/(cxl/|pci).* + ~ .*/qemu(/include)?/hw/(cxl/|pci).* qemu-ga - ~ (/qemu)?(/qga/.*) + ~ .*/qemu(/qga/.*) scsi - ~ (/qemu)?(/scsi/.*|/hw/scsi/.*|/include/hw/scsi/.*) + ~ .*/qemu(/scsi/.*|/hw/scsi/.*|/include/hw/scsi/.*) trace - ~ (/qemu)?(/.*trace.*\.[ch]) + ~ .*/qemu(/.*trace.*\.[ch]) ui - ~ (/qemu)?((/include)?(/ui|/hw/display|/hw/input)/.*) + ~ .*/qemu((/include)?(/ui|/hw/display|/hw/input)/.*) usb - ~ (/qemu)?(/hw/usb/.*|/include/hw/usb/.*) + ~ .*/qemu(/hw/usb/.*|/include/hw/usb/.*) user - ~ (/qemu)?(/linux-user/.*|/bsd-user/.*|/user-exec\.c|/thunk\.c|/include/exec/user/.*) + ~ .*/qemu(/linux-user/.*|/bsd-user/.*|/user-exec\.c|/thunk\.c|/include/user/.*) util - ~ (/qemu)?(/util/.*|/include/qemu/.*) + ~ .*/qemu(/util/.*|/include/qemu/.*) vfio - ~ (/qemu)?(/include)?/hw/vfio/.* + ~ .*/qemu(/include)?/hw/vfio/.* virtio - ~ (/qemu)?(/include)?/hw/virtio/.* + ~ .*/qemu(/include)?/hw/virtio/.* xen - ~ (/qemu)?(.*/xen.*) + ~ .*/qemu(.*/xen.*) hvf - ~ (/qemu)?(.*/hvf.*) + ~ .*/qemu(.*/hvf.*) kvm - ~ (/qemu)?(.*/kvm.*) + ~ .*/qemu(.*/kvm.*) tcg - ~ (/qemu)?(/accel/tcg|/replay|/tcg)/.* + ~ .*/qemu(/accel/tcg|/replay|/tcg)/.* -sysemu - ~ (/qemu)?(/softmmu/.*|/accel/.*) +system + ~ .*/qemu(/system/.*|/accel/.*) (headers) - ~ (/qemu)?(/include/.*) + ~ .*/qemu(/include/.*) testlibs - ~ (/qemu)?(/tests/qtest(/libqos/.*|/libqtest.*)) + ~ .*/qemu(/tests/qtest(/libqos/.*|/libqtest.*|/libqmp.*)) tests - ~ (/qemu)?(/tests/.*) + ~ .*/qemu(/tests/.*) diff --git a/scripts/coverity-scan/model.c b/scripts/coverity-scan/model.c index 686d1a30089..a064d840849 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 129672c86fa..43cf770f5e3 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 82ff07582f8..7360e55c6e0 100644 --- a/scripts/cpu-x86-uarch-abi.py +++ b/scripts/cpu-x86-uarch-abi.py @@ -85,7 +85,7 @@ names = [] -for model in models["return"]: +for model in models: if "alias-of" in model: continue names.append(model["name"]) @@ -94,11 +94,11 @@ 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 @@ 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/device-crash-test b/scripts/device-crash-test index 353aa575d7b..da8b56edd99 100755 --- a/scripts/device-crash-test +++ b/scripts/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 00000000000..807af0e685c --- /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 c1f67c8f6a5..00000000000 --- 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/gensyscalls.sh b/scripts/gensyscalls.sh deleted file mode 100755 index a2f7664b7bb..00000000000 --- 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 e5499b94b4f..00a0870b267 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/kernel-doc b/scripts/kernel-doc index 240923d509a..fec83f53eda 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 ce27f5e635a..508be19c758 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/make-release b/scripts/make-release index c5db87b3f91..4509a9fabf5 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -10,6 +10,28 @@ # This work is licensed under the terms of the GNU GPLv2 or later. # See the COPYING file in the top-level directory. +function subproject_dir() { + if test ! -f "$src/subprojects/$1.wrap"; then + echo "scripts/archive-source.sh should only process wrap subprojects" + exit 1 + 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 '}' \ + "$src/subprojects/$1.wrap") + + echo "${dir:-$1}" +} + if [ $# -ne 2 ]; then echo "Usage:" echo " $0 gitrepo version" @@ -17,7 +39,12 @@ if [ $# -ne 2 ]; then fi # Only include wraps that are invoked with subproject() -SUBPROJECTS="dtc libvfio-user keycodemapdb berkeley-softfloat-3 berkeley-testfloat-3" +SUBPROJECTS="libvfio-user keycodemapdb berkeley-softfloat-3 + berkeley-testfloat-3 anyhow-1-rs arbitrary-int-1-rs bilge-0.2-rs + bilge-impl-0.2-rs either-1-rs foreign-0.3-rs itertools-0.11-rs + libc-0.2-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" @@ -47,5 +74,13 @@ meson subprojects download $SUBPROJECTS CryptoPkg/Library/OpensslLib/openssl \ MdeModulePkg/Library/BrotliCustomDecompressLib/brotli) popd -tar --exclude=.git -cjf ${destination}.tar.bz2 ${destination} + +exclude=(--exclude=.git) +# include the tarballs in subprojects/packagecache but not their expansion +for sp in $SUBPROJECTS; do + if grep -xqF "[wrap-file]" $src/subprojects/$sp.wrap; then + exclude+=(--exclude=subprojects/"$(subproject_dir $sp)") + fi +done +tar "${exclude[@]}" -cJf ${destination}.tar.xz ${destination} rm -rf ${destination} diff --git a/scripts/meson-buildoptions.py b/scripts/meson-buildoptions.py index 8d2e526132a..a3e22471b2f 100644 --- a/scripts/meson-buildoptions.py +++ b/scripts/meson-buildoptions.py @@ -25,13 +25,15 @@ 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", @@ -40,13 +42,25 @@ "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", @@ -55,8 +69,10 @@ "localedir", "localstatedir", "mandir", + "prefix", "strip", "sysconfdir", + "werror", } LINE_WIDTH = 76 @@ -168,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, @@ -176,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): @@ -185,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("}") @@ -223,8 +241,14 @@ def print_parse(options): print(" esac") print("}") - -options = load_options(json.load(sys.stdin)) +json_data = sys.stdin.read() +try: + options = load_options(json.loads(json_data)) +except: + print("Failure in scripts/meson-buildoptions.py parsing stdin as json", + file=sys.stderr) + print(json_data, file=sys.stderr) + sys.exit(1) print("# This file is generated by meson-buildoptions.py, do not edit!") print_help(options) print_parse(options) diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index e1d178370c2..73e0770f42b 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -3,6 +3,7 @@ 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/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)' @@ -17,8 +18,10 @@ meson_options_help() { 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' @@ -27,6 +30,7 @@ meson_options_help() { 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' @@ -34,24 +38,24 @@ meson_options_help() { 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-plugins TCG plugins via shared library loading' 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-sanitizers enable default sanitizers' + 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' @@ -63,25 +67,36 @@ meson_options_help() { 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" ' --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" ' --with-coroutine=CHOICE coroutine backend to use (choices:' - printf "%s\n" ' auto/sigaltstack/ucontext/windows)' + printf "%s\n" ' auto/sigaltstack/ucontext/wasm/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" ' avx512bw AVX512BW optimizations' - printf "%s\n" ' avx512f AVX512F optimizations' printf "%s\n" ' blkio libblkio block device driver' printf "%s\n" ' bochs bochs image format support' printf "%s\n" ' bpf eBPF support' @@ -112,15 +127,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" ' 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' @@ -129,8 +147,6 @@ 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' @@ -147,14 +163,20 @@ meson_options_help() { 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" ' pvg macOS paravirtualized graphics 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" ' 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' @@ -172,7 +194,9 @@ meson_options_help() { 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" ' valgrind valgrind debug support for coroutine stacks' printf "%s\n" ' vde vde network backend support' printf "%s\n" ' vdi vdi image format support' printf "%s\n" ' vduse-blk-export' @@ -189,8 +213,6 @@ 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" ' virtfs-proxy-helper' - printf "%s\n" ' virtio-9p proxy helper support' printf "%s\n" ' vmdk vmdk image format support' printf "%s\n" ' vmnet vmnet.framework network backend support' printf "%s\n" ' vnc VNC server' @@ -199,6 +221,7 @@ meson_options_help() { 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' @@ -208,23 +231,22 @@ 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" ;; --enable-auth-pam) printf "%s" -Dauth_pam=enabled ;; --disable-auth-pam) printf "%s" -Dauth_pam=disabled ;; - --enable-avx2) printf "%s" -Davx2=enabled ;; - --disable-avx2) printf "%s" -Davx2=disabled ;; - --enable-avx512bw) printf "%s" -Davx512bw=enabled ;; - --disable-avx512bw) printf "%s" -Davx512bw=disabled ;; - --enable-avx512f) printf "%s" -Davx512f=enabled ;; - --disable-avx512f) printf "%s" -Davx512f=disabled ;; --enable-gcov) printf "%s" -Db_coverage=true ;; --disable-gcov) printf "%s" -Db_coverage=false ;; --enable-lto) printf "%s" -Db_lto=true ;; --disable-lto) printf "%s" -Db_lto=false ;; + --bindir=*) quote_sh "-Dbindir=$2" ;; --enable-blkio) printf "%s" -Dblkio=enabled ;; --disable-blkio) printf "%s" -Dblkio=disabled ;; --block-drv-ro-whitelist=*) quote_sh "-Dblock_drv_ro_whitelist=$2" ;; @@ -275,6 +297,8 @@ _meson_option_parse() { --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 ;; @@ -305,8 +329,6 @@ _meson_option_parse() { --disable-glusterfs) printf "%s" -Dglusterfs=disabled ;; --enable-gnutls) printf "%s" -Dgnutls=enabled ;; --disable-gnutls) printf "%s" -Dgnutls=disabled ;; - --enable-gprof) printf "%s" -Dgprof=true ;; - --disable-gprof) printf "%s" -Dgprof=false ;; --enable-gtk) printf "%s" -Dgtk=enabled ;; --disable-gtk) printf "%s" -Dgtk=disabled ;; --enable-gtk-clipboard) printf "%s" -Dgtk_clipboard=enabled ;; @@ -317,6 +339,8 @@ _meson_option_parse() { --disable-guest-agent-msi) printf "%s" -Dguest_agent_msi=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" ;; @@ -334,6 +358,8 @@ _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" ;; @@ -342,6 +368,8 @@ _meson_option_parse() { --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 ;; @@ -358,8 +386,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 ;; @@ -398,34 +424,50 @@ _meson_option_parse() { --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-pvrdma) printf "%s" -Dpvrdma=enabled ;; - --disable-pvrdma) printf "%s" -Dpvrdma=disabled ;; + --prefix=*) quote_sh "-Dprefix=$2" ;; + --enable-pvg) printf "%s" -Dpvg=enabled ;; + --disable-pvg) printf "%s" -Dpvg=disabled ;; + --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-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-sanitizers) printf "%s" -Dsanitizers=true ;; - --disable-sanitizers) printf "%s" -Dsanitizers=false ;; --enable-sdl) printf "%s" -Dsdl=enabled ;; --disable-sdl) printf "%s" -Dsdl=disabled ;; --enable-sdl-image) printf "%s" -Dsdl_image=enabled ;; @@ -440,6 +482,7 @@ _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 ;; @@ -452,6 +495,8 @@ _meson_option_parse() { --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" ;; @@ -470,8 +515,14 @@ _meson_option_parse() { --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-valgrind) printf "%s" -Dvalgrind=enabled ;; + --disable-valgrind) printf "%s" -Dvalgrind=disabled ;; --enable-vde) printf "%s" -Dvde=enabled ;; --disable-vde) printf "%s" -Dvde=disabled ;; --enable-vdi) printf "%s" -Dvdi=enabled ;; @@ -498,8 +549,6 @@ _meson_option_parse() { --disable-virglrenderer) printf "%s" -Dvirglrenderer=disabled ;; --enable-virtfs) printf "%s" -Dvirtfs=enabled ;; --disable-virtfs) printf "%s" -Dvirtfs=disabled ;; - --enable-virtfs-proxy-helper) printf "%s" -Dvirtfs_proxy_helper=enabled ;; - --disable-virtfs-proxy-helper) printf "%s" -Dvirtfs_proxy_helper=disabled ;; --enable-vmdk) printf "%s" -Dvmdk=enabled ;; --disable-vmdk) printf "%s" -Dvmdk=disabled ;; --enable-vmnet) printf "%s" -Dvmnet=enabled ;; @@ -516,8 +565,11 @@ _meson_option_parse() { --disable-vte) printf "%s" -Dvte=disabled ;; --enable-vvfat) printf "%s" -Dvvfat=enabled ;; --disable-vvfat) printf "%s" -Dvvfat=disabled ;; + --enable-werror) printf "%s" -Dwerror=true ;; + --disable-werror) printf "%s" -Dwerror=false ;; --enable-whpx) printf "%s" -Dwhpx=enabled ;; --disable-whpx) printf "%s" -Dwhpx=disabled ;; + --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/minikconf.py b/scripts/minikconf.py index bcd91015d34..6f7f43b2918 100644 --- a/scripts/minikconf.py +++ b/scripts/minikconf.py @@ -112,7 +112,7 @@ def has_value(self): 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 index 4e7584df667..48bd92bd618 100644 --- a/scripts/modinfo-collect.py +++ b/scripts/modinfo-collect.py @@ -7,15 +7,6 @@ import shlex import subprocess -def find_command(src, target, compile_commands): - for command in compile_commands: - if command['file'] != src: - continue - if target != '' and command['command'].find(target) == -1: - continue - return command['command'] - return 'false' - def process_command(src, command): skip = False out = [] @@ -43,14 +34,22 @@ def main(args): print("MODINFO_DEBUG target %s" % target) arch = target[:-8] # cut '-softmmu' print("MODINFO_START arch \"%s\" MODINFO_END" % arch) + with open('compile_commands.json') as f: - compile_commands = json.load(f) - for src in args: + compile_commands_json = json.load(f) + compile_commands = { x['output']: x for x in compile_commands_json } + + for obj in args: + entry = compile_commands.get(obj, None) + if not entry: + sys.stderr.print('modinfo: Could not find object file', obj) + sys.exit(1) + src = entry['file'] if not src.endswith('.c'): print("MODINFO_DEBUG skip %s" % src) continue + command = entry['command'] print("MODINFO_DEBUG src %s" % src) - command = find_command(src, target, compile_commands) cmdline = process_command(src, command) print("MODINFO_DEBUG cmd", cmdline) result = subprocess.run(cmdline, stdout = subprocess.PIPE, diff --git a/scripts/mtest2make.py b/scripts/mtest2make.py index 179dd548718..2ef375fc6fb 100644 --- a/scripts/mtest2make.py +++ b/scripts/mtest2make.py @@ -27,7 +27,8 @@ def names(self, base): .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 diff --git a/scripts/nsis.py b/scripts/nsis.py index 03ed7608a2c..8f469634eb7 100644 --- a/scripts/nsis.py +++ b/scripts/nsis.py @@ -23,7 +23,7 @@ def find_deps(exe_or_dll, search_path, analyzed_deps): output = subprocess.check_output(["objdump", "-p", exe_or_dll], text=True) output = output.split("\n") for line in output: - if not line.startswith("\tDLL Name: "): + if not line.lstrip().startswith("DLL Name: "): continue dep = line.split("DLL Name: ")[1].strip() @@ -37,10 +37,10 @@ def find_deps(exe_or_dll, search_path, analyzed_deps): analyzed_deps.add(dep) # locate the dll dependencies recursively - rdeps = find_deps(dll, search_path, analyzed_deps) + analyzed_deps, rdeps = find_deps(dll, search_path, analyzed_deps) deps.extend(rdeps) - return deps + return analyzed_deps, deps def main(): parser = argparse.ArgumentParser(description="QEMU NSIS build helper.") @@ -92,18 +92,18 @@ def main(): dlldir = os.path.join(destdir + prefix, "dll") os.mkdir(dlldir) + analyzed_deps = set() for exe in glob.glob(os.path.join(destdir + prefix, "*.exe")): signcode(exe) # find all dll dependencies - deps = set(find_deps(exe, search_path, set())) + analyzed_deps, deps = find_deps(exe, search_path, analyzed_deps) + deps = set(deps) deps.remove(exe) # copy all dlls to the DLLDIR for dep in deps: dllfile = os.path.join(dlldir, os.path.basename(dep)) - if (os.path.exists(dllfile)): - continue print("Copying '%s' to '%s'" % (dep, dllfile)) shutil.copy(dep, dllfile) diff --git a/scripts/oss-fuzz/build.sh b/scripts/oss-fuzz/build.sh index 3bda0d72c72..73982981733 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/probe-gdb-support.py b/scripts/probe-gdb-support.py index 57552559662..6bcadce1500 100644 --- a/scripts/probe-gdb-support.py +++ b/scripts/probe-gdb-support.py @@ -19,60 +19,61 @@ import argparse import re -from subprocess import check_output, STDOUT +from subprocess import check_output, STDOUT, CalledProcessError +import sys -# mappings from gdb arch to QEMU target -mappings = { - "alpha" : "alpha", +# Mappings from gdb arch to QEMU target +MAP = { + "alpha" : ["alpha"], "aarch64" : ["aarch64", "aarch64_be"], - "armv7": "arm", + "armv7": ["arm"], "armv8-a" : ["aarch64", "aarch64_be"], - "avr" : "avr", - "cris" : "cris", + "avr" : ["avr"], # no hexagon in upstream gdb - "hppa1.0" : "hppa", - "i386" : "i386", - "i386:x86-64" : "x86_64", - "Loongarch64" : "loongarch64", - "m68k" : "m68k", - "MicroBlaze" : "microblaze", + "hppa1.0" : ["hppa"], + "i386" : ["i386"], + "i386:x86-64" : ["x86_64"], + "Loongarch64" : ["loongarch64"], + "m68k" : ["m68k"], + "MicroBlaze" : ["microblaze"], "mips:isa64" : ["mips64", "mips64el"], - "nios2" : "nios2", - "or1k" : "or1k", - "powerpc:common" : "ppc", + "or1k" : ["or1k"], + "powerpc:common" : ["ppc"], "powerpc:common64" : ["ppc64", "ppc64le"], - "riscv:rv32" : "riscv32", - "riscv:rv64" : "riscv64", - "s390:64-bit" : "s390x", + "riscv:rv32" : ["riscv32"], + "riscv:rv64" : ["riscv64"], + "s390:64-bit" : ["s390x"], "sh4" : ["sh4", "sh4eb"], - "sparc": "sparc", - "sparc:v8plus": "sparc32plus", - "sparc:v9a" : "sparc64", + "sparc": ["sparc"], + "sparc:v8plus": ["sparc32plus"], + "sparc:v9a" : ["sparc64"], # no tricore in upstream gdb "xtensa" : ["xtensa", "xtensaeb"] } + def do_probe(gdb): - gdb_out = check_output([gdb, - "-ex", "set architecture", - "-ex", "quit"], stderr=STDOUT) + 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) - m = re.search(r"Valid arguments are (.*)", - gdb_out.decode("utf-8")) + 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] - valid_arches = set() + targets = {target for arch in mapped_gdb_archs for target in MAP[arch]} - if m.group(1): - for arch in m.group(1).split(", "): - if arch in mappings: - mapping = mappings[arch] - if isinstance(mapping, str): - valid_arches.add(mapping) - else: - for entry in mapping: - valid_arches.add(entry) + # QEMU targets + return targets - return valid_arches def main() -> None: parser = argparse.ArgumentParser(description='Probe GDB Architectures') diff --git a/scripts/python_qmp_updater.py b/scripts/python_qmp_updater.py new file mode 100755 index 00000000000..494a1698124 --- /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 deleted file mode 100644 index a873ff67309..00000000000 --- a/scripts/qapi/.flake8 +++ /dev/null @@ -1,3 +0,0 @@ -[flake8] -# Prefer pylint's bare-except checks to flake8's -extend-ignore = E722 diff --git a/scripts/qapi/.isort.cfg b/scripts/qapi/.isort.cfg deleted file mode 100644 index 643caa1fbd6..00000000000 --- a/scripts/qapi/.isort.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[settings] -force_grid_wrap=4 -force_sort_within_sections=True -include_trailing_comma=True -line_length=72 -lines_after_imports=2 -multi_line_output=3 diff --git a/scripts/qapi/backend.py b/scripts/qapi/backend.py new file mode 100644 index 00000000000..49ae6ecdd33 --- /dev/null +++ b/scripts/qapi/backend.py @@ -0,0 +1,65 @@ +# 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 abc import ABC, abstractmethod + +from .commands import gen_commands +from .events import gen_events +from .features import gen_features +from .introspect import gen_introspect +from .schema import QAPISchema +from .types import gen_types +from .visit import gen_visit + + +class QAPIBackend(ABC): + # pylint: disable=too-few-public-methods + + @abstractmethod + def generate(self, + schema: QAPISchema, + output_dir: str, + prefix: str, + unmask: bool, + builtins: bool, + gen_tracing: bool) -> None: + """ + Generate code for the given schema into the target directory. + + :param schema: The primary QAPI schema object. + :param output_dir: The output directory to store generated code. + :param prefix: Optional C-code prefix for symbol names. + :param unmask: Expose non-ABI names through introspection? + :param builtins: Generate code for built-in types? + + :raise QAPIError: On failures. + """ + + +class QAPICBackend(QAPIBackend): + # pylint: disable=too-few-public-methods + + def generate(self, + schema: QAPISchema, + output_dir: str, + prefix: str, + unmask: bool, + builtins: bool, + gen_tracing: bool) -> None: + """ + Generate C code for the given schema into the target directory. + + :param schema_file: The primary QAPI schema file. + :param output_dir: The output directory to store generated code. + :param prefix: Optional C-code prefix for symbol names. + :param unmask: Expose non-ABI names through introspection? + :param builtins: Generate code for built-in types? + + :raise QAPIError: On failures. + """ + gen_types(schema, output_dir, prefix, builtins) + gen_features(schema, output_dir, prefix) + gen_visit(schema, output_dir, prefix, builtins) + gen_commands(schema, output_dir, prefix, gen_tracing) + gen_events(schema, output_dir, prefix) + gen_introspect(schema, output_dir, prefix, unmask) diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index d1fdf4182c9..79142273828 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -25,7 +25,7 @@ QAPIGenC, QAPISchemaModularCVisitor, build_params, - gen_special_features, + gen_features, ifcontext, ) from .schema import ( @@ -64,7 +64,7 @@ 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: assert not memb.ifcond.is_present() if memb.need_has(): @@ -298,7 +298,7 @@ def gen_register_command(name: str, ''', name=name, c_name=c_name(name), opts=' | '.join(options) or 0, - feats=gen_special_features(features)) + feats=gen_features(features)) return ret @@ -320,7 +320,7 @@ def _begin_user_module(self, name: str) -> None: #include "qemu/osdep.h" #include "qapi/compat-policy.h" #include "qapi/visitor.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/dealloc-visitor.h" #include "qapi/error.h" #include "%(visit)s.h" @@ -330,7 +330,7 @@ def _begin_user_module(self, name: str) -> None: if self._gen_tracing and commands != 'qapi-commands': self._genc.add(mcgen(''' -#include "qapi/qmp/qjson.h" +#include "qobject/qjson.h" #include "trace/trace-%(nm)s_trace_events.h" ''', nm=c_name(commands, protect=False))) @@ -346,7 +346,7 @@ def _begin_user_module(self, name: str) -> None: def visit_begin(self, schema: QAPISchema) -> None: self._add_module('./init', ' * QAPI Commands initialization') self._genh.add(mcgen(''' -#include "qapi/qmp/dispatch.h" +#include "qapi/qmp-registry.h" void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); ''', @@ -355,6 +355,7 @@ def visit_begin(self, schema: QAPISchema) -> None: #include "qemu/osdep.h" #include "%(prefix)sqapi-commands.h" #include "%(prefix)sqapi-init-commands.h" +#include "%(prefix)sqapi-features.h" void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) { diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 737b059e629..d7c8aa3365c 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 - - 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() + ret = value[0] + upc = value[0].isupper() + + # 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 3cf01e96b68..d179b0ed695 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 = { ''', @@ -194,7 +194,7 @@ def _begin_user_module(self, name: str) -> None: #include "%(visit)s.h" #include "qapi/compat-policy.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qmp-event.h" ''', events=events, visit=visit, diff --git a/scripts/qapi/features.py b/scripts/qapi/features.py new file mode 100644 index 00000000000..57563207a82 --- /dev/null +++ b/scripts/qapi/features.py @@ -0,0 +1,48 @@ +""" +QAPI features generator + +Copyright 2024 Red Hat + +This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. +""" + +from typing import ValuesView + +from .common import c_enum_const, c_name +from .gen import QAPISchemaMonolithicCVisitor +from .schema import QAPISchema, QAPISchemaFeature + + +class QAPISchemaGenFeatureVisitor(QAPISchemaMonolithicCVisitor): + + def __init__(self, prefix: str): + super().__init__( + prefix, 'qapi-features', + ' * Schema-defined QAPI features', + __doc__) + + self.features: ValuesView[QAPISchemaFeature] + + def visit_begin(self, schema: QAPISchema) -> None: + self.features = schema.features() + self._genh.add("#include \"qapi/util.h\"\n\n") + + def visit_end(self) -> None: + self._genh.add("typedef enum {\n") + for f in self.features: + self._genh.add(f" {c_enum_const('qapi_feature', f.name)}") + if f.name in QAPISchemaFeature.SPECIAL_NAMES: + self._genh.add(f" = {c_enum_const('qapi', f.name)},\n") + else: + self._genh.add(",\n") + + self._genh.add("} " + c_name('QapiFeature') + ";\n") + + +def gen_features(schema: QAPISchema, + output_dir: str, + prefix: str) -> None: + vis = QAPISchemaGenFeatureVisitor(prefix) + schema.visit(vis) + vis.write(output_dir) diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index bf5716b5f30..d3c56d45c89 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -13,8 +13,8 @@ from contextlib import contextmanager import os -import sys import re +import sys from typing import ( Dict, Iterator, @@ -24,6 +24,7 @@ ) from .common import ( + c_enum_const, c_fname, c_name, guardend, @@ -40,10 +41,10 @@ from .source import QAPISourceInfo -def gen_special_features(features: Sequence[QAPISchemaFeature]) -> str: - special_features = [f"1u << QAPI_{feat.name.upper()}" - for feat in features if feat.is_special()] - return ' | '.join(special_features) or '0' +def gen_features(features: Sequence[QAPISchemaFeature]) -> str: + feats = [f"1u << {c_enum_const('qapi_feature', feat.name)}" + for feat in features] + return ' | '.join(feats) or '0' class QAPIGen: @@ -118,7 +119,7 @@ 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 diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 67c7d89aae0..89ee5d5f176 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -11,6 +11,7 @@ See the COPYING file in the top-level directory. """ +from dataclasses import dataclass from typing import ( Any, Dict, @@ -26,7 +27,9 @@ from .gen import QAPISchemaMonolithicCVisitor from .schema import ( QAPISchema, + QAPISchemaAlternatives, QAPISchemaArrayType, + QAPISchemaBranches, QAPISchemaBuiltinType, QAPISchemaEntity, QAPISchemaEnumMember, @@ -36,7 +39,6 @@ QAPISchemaObjectTypeMember, QAPISchemaType, QAPISchemaVariant, - QAPISchemaVariants, ) from .source import QAPISourceInfo @@ -78,19 +80,16 @@ _ValueT = TypeVar('_ValueT', bound=_Value) +@dataclass class Annotated(Generic[_ValueT]): """ Annotated generally contains a SchemaInfo-like type (as a dict), But it also used to wrap comments/ifconds around scalar leaf values, for the benefit of features and enums. """ - # TODO: Remove after Python 3.7 adds @dataclass: - # pylint: disable=too-few-public-methods - def __init__(self, value: _ValueT, ifcond: QAPISchemaIfCond, - comment: Optional[str] = None): - self.value = value - self.comment: Optional[str] = comment - self.ifcond = ifcond + value: _ValueT + ifcond: QAPISchemaIfCond + comment: Optional[str] = None def _tree_to_qlit(obj: JSONValue, @@ -196,7 +195,7 @@ def visit_end(self) -> None: # generate C name = c_name(self._prefix, protect=False) + 'qmp_schema_qlit' self._genh.add(mcgen(''' -#include "qapi/qmp/qlit.h" +#include "qobject/qlit.h" extern const QLitObject %(c_name)s; ''', @@ -227,10 +226,14 @@ def _use_type(self, typ: QAPISchemaType) -> str: # 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 +334,24 @@ def visit_object_type_flat(self, name: str, info: Optional[QAPISourceInfo], 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 316736b6a29..0e2a6ae3f07 100644 --- a/scripts/qapi/main.py +++ b/scripts/qapi/main.py @@ -8,17 +8,14 @@ """ import argparse +from importlib import import_module import sys from typing import Optional -from .commands import gen_commands +from .backend import QAPIBackend, QAPICBackend from .common import must_match from .error import QAPIError -from .events import gen_events -from .introspect import gen_introspect from .schema import QAPISchema -from .types import gen_types -from .visit import gen_visit def invalid_prefix_char(prefix: str) -> Optional[str]: @@ -28,31 +25,36 @@ def invalid_prefix_char(prefix: str) -> Optional[str]: return None -def generate(schema_file: str, - output_dir: str, - prefix: str, - unmask: bool = False, - builtins: bool = False, - gen_tracing: bool = False) -> None: - """ - Generate C code for the given schema into the target directory. +def create_backend(path: str) -> QAPIBackend: + if path is None: + return QAPICBackend() - :param schema_file: The primary QAPI schema file. - :param output_dir: The output directory to store generated code. - :param prefix: Optional C-code prefix for symbol names. - :param unmask: Expose non-ABI names through introspection? - :param builtins: Generate code for built-in types? + module_path, dot, class_name = path.rpartition('.') + if not dot: + raise QAPIError("argument of -B must be of the form MODULE.CLASS") - :raise QAPIError: On failures. - """ - assert invalid_prefix_char(prefix) is None + try: + mod = import_module(module_path) + except Exception as ex: + raise QAPIError(f"unable to import '{module_path}': {ex}") from ex + + try: + klass = getattr(mod, class_name) + except AttributeError as ex: + raise QAPIError( + f"module '{module_path}' has no class '{class_name}'") from ex + + try: + backend = klass() + except Exception as ex: + raise QAPIError( + f"backend '{path}' cannot be instantiated: {ex}") from ex + + if not isinstance(backend, QAPIBackend): + raise QAPIError( + f"backend '{path}' must be an instance of QAPIBackend") - schema = QAPISchema(schema_file) - gen_types(schema, output_dir, prefix, builtins) - gen_visit(schema, output_dir, prefix, builtins) - gen_commands(schema, output_dir, prefix, gen_tracing) - gen_events(schema, output_dir, prefix) - gen_introspect(schema, output_dir, prefix, unmask) + return backend def main() -> int: @@ -75,6 +77,8 @@ def main() -> int: parser.add_argument('-u', '--unmask-non-abi-names', action='store_true', dest='unmask', help="expose non-ABI names in introspection") + parser.add_argument('-B', '--backend', default=None, + help="Python module name for code generator") # Option --suppress-tracing exists so we can avoid solving build system # problems. TODO Drop it when we no longer need it. @@ -91,12 +95,14 @@ def main() -> int: return 1 try: - generate(args.schema, - output_dir=args.output_dir, - prefix=args.prefix, - unmask=args.unmask, - builtins=args.builtins, - gen_tracing=not args.suppress_tracing) + schema = QAPISchema(args.schema) + backend = create_backend(args.backend) + backend.generate(schema, + output_dir=args.output_dir, + prefix=args.prefix, + unmask=args.unmask, + builtins=args.builtins, + gen_tracing=not args.suppress_tracing) except QAPIError as err: print(err, file=sys.stderr) return 1 diff --git a/scripts/qapi/mypy.ini b/scripts/qapi/mypy.ini deleted file mode 100644 index 56e0dfb1327..00000000000 --- a/scripts/qapi/mypy.ini +++ /dev/null @@ -1,9 +0,0 @@ -[mypy] -strict = True -disallow_untyped_calls = False -python_version = 3.8 - -[mypy-qapi.schema] -disallow_untyped_defs = False -disallow_incomplete_defs = False -check_untyped_defs = False diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 22e7bcc4b1b..949d9e8bff7 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -14,14 +14,16 @@ # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. -from collections import OrderedDict +import enum import os import re from typing import ( TYPE_CHECKING, + Any, Dict, List, Mapping, + Match, Optional, Set, Union, @@ -42,7 +44,7 @@ _ExprValue = Union[List[object], Dict[str, object], str, bool] -class QAPIExpression(Dict[str, object]): +class QAPIExpression(Dict[str, Any]): # pylint: disable=too-few-public-methods def __init__(self, data: Mapping[str, object], @@ -70,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. @@ -133,8 +135,8 @@ def _parse(self) -> None: 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() @@ -152,7 +154,7 @@ def _parse(self) -> None: "value of 'include' must be a string") incl_fname = os.path.join(os.path.dirname(self._fname), include) - self._add_expr(OrderedDict({'include': incl_fname}), info) + self._add_expr({'include': incl_fname}, info) exprs_include = self._include(include, info, incl_fname, self._included) if exprs_include: @@ -237,6 +239,8 @@ def check_list_str(name: str, value: object) -> List[str]: 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: @@ -351,7 +355,7 @@ def accept(self, skip_comment: bool = True) -> None: raise QAPIParseError(self, "stray '%s'" % match.group(0)) def get_members(self) -> Dict[str, object]: - expr: Dict[str, object] = OrderedDict() + expr: Dict[str, object] = {} if self.tok == '}': self.accept() return expr @@ -411,39 +415,208 @@ def get_expr(self) -> _ExprValue: 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 != '##': + 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, + QAPIDoc.Kind.from_string(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, - "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: + "unexpected '=' markup in definition documentation") + else: + # plain 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, - "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) + "'@%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: @@ -465,282 +638,174 @@ class QAPIDoc: Free-form documentation blocks consist only of a body section. """ + class Kind(enum.Enum): + PLAIN = 0 + MEMBER = 1 + FEATURE = 2 + RETURNS = 3 + ERRORS = 4 + SINCE = 5 + TODO = 6 + + @staticmethod + def from_string(kind: str) -> 'QAPIDoc.Kind': + return QAPIDoc.Kind[kind.upper()] + + def __str__(self) -> str: + return self.name.title() + class Section: # pylint: disable=too-few-public-methods - def __init__(self, parser: QAPISchemaParser, - name: Optional[str] = None): - # parser, for error messages about indentation - self._parser = parser - # optional section name (argument/member or section name) - self.name = name - # section text without section name + def __init__( + self, + info: QAPISourceInfo, + kind: 'QAPIDoc.Kind', + ): + # section source info, i.e. where it begins + self.info = info + # section kind + self.kind = kind + # section text without tag self.text = '' - # indentation to strip (None means indeterminate) - self._indent = None if self.name else 0 - - def append(self, line: str) -> None: - line = line.rstrip() - - if line: - indent = must_match(r'\s*', line).end() - if self._indent is None: - # indeterminate indentation - if self.text != '': - # non-blank, non-first line determines indentation - self._indent = indent - elif indent < self._indent: - raise QAPIParseError( - self._parser, - "unexpected de-indent (expected at least %d spaces)" % - self._indent) - line = line[self._indent:] + def __repr__(self) -> str: + return f"" + + def append_line(self, line: str) -> None: self.text += line + '\n' class ArgSection(Section): - def __init__(self, parser: QAPISchemaParser, - name: str): - super().__init__(parser, name) + def __init__( + self, + info: QAPISourceInfo, + kind: 'QAPIDoc.Kind', + name: str + ): + super().__init__(info, kind) + self.name = name 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, QAPIDoc.Kind.PLAIN) + ] + # 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 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) - return - - if line[0] != ' ': - raise QAPIParseError(self._parser, "missing space after #") - line = line[1:] - self._append_line(line) - - def end_comment(self) -> None: - self._switch_section(QAPIDoc.NullSection(self._parser)) - @staticmethod - def _match_at_name_colon(string: str): - return re.match(r'@([^:]*): *', string) - - @staticmethod - def _match_section_tag(string: str): - return re.match(r'(Returns|Since|Notes?|Examples?|TODO): *', string) - - 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. - """ - # 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 self._match_at_name_colon(line): - self._append_line = self._append_args_line - self._append_args_line(line) - elif line == 'Features:': - self._append_line = self._append_features_line - elif self._match_section_tag(line): - 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. - - """ - match = self._match_at_name_colon(line) - if match: - line = line[match.end():] - self._start_args_section(match.group(1)) - elif self._match_section_tag(line): - 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: - match = self._match_at_name_colon(line) - if match: - line = line[match.end():] - self._start_features_section(match.group(1)) - elif self._match_section_tag(line): - 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) + def end(self) -> None: + for section in self.all_sections: + section.text = section.text.strip('\n') + if section.kind != QAPIDoc.Kind.PLAIN and section.text == '': + raise QAPISemError( + section.info, "text required after '%s:'" % section.kind) + + def ensure_untagged_section(self, info: QAPISourceInfo) -> None: + kind = QAPIDoc.Kind.PLAIN + + if self.all_sections and self.all_sections[-1].kind == kind: + # extend current section + section = self.all_sections[-1] + if not section.text: + # Section is empty so far; update info to start *here*. + section.info = info + section.text += '\n' 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. - """ - match = self._match_at_name_colon(line) - if match: - raise QAPIParseError(self._parser, - "description of '@%s:' follows a section" - % match.group(1)) - match = self._match_section_tag(line) - if match: - line = line[match.end():] - self._start_section(match.group(1)) - - self._append_freeform(line) - - def _start_symbol_section( - self, - symbols_dict: Dict[str, 'QAPIDoc.ArgSection'], - name: str) -> None: - # FIXME invalid names other than the empty string aren't flagged + # start new section + section = self.Section(info, kind) + self.sections.append(section) + self.all_sections.append(section) + + def new_tagged_section( + self, + info: QAPISourceInfo, + kind: 'QAPIDoc.Kind', + ) -> None: + section = self.Section(info, kind) + if kind == QAPIDoc.Kind.RETURNS: + if self.returns: + raise QAPISemError( + info, "duplicated '%s' section" % kind) + self.returns = section + elif kind == QAPIDoc.Kind.ERRORS: + if self.errors: + raise QAPISemError( + info, "duplicated '%s' section" % kind) + self.errors = section + elif kind == QAPIDoc.Kind.SINCE: + if self.since: + raise QAPISemError( + info, "duplicated '%s' section" % kind) + self.since = section + self.sections.append(section) + self.all_sections.append(section) + + def _new_description( + self, + info: QAPISourceInfo, + name: str, + kind: 'QAPIDoc.Kind', + 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) - self._switch_section(new_section) - symbols_dict[name] = new_section - - def _start_args_section(self, name: str) -> None: - self._start_symbol_section(self.args, name) - - def _start_features_section(self, name: str) -> None: - self._start_symbol_section(self.features, name) - - def _start_section(self, name: Optional[str] = None) -> 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) - 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('\n') - - # 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) + raise QAPISemError(info, "invalid parameter name") + if name in desc: + raise QAPISemError(info, "'%s' parameter name duplicated" % name) + section = self.ArgSection(info, kind, name) + self.all_sections.append(section) + desc[name] = section + + def new_argument(self, info: QAPISourceInfo, name: str) -> None: + self._new_description(info, name, QAPIDoc.Kind.MEMBER, self.args) - self._section = new_section + def new_feature(self, info: QAPISourceInfo, name: str) -> None: + self._new_description(info, name, QAPIDoc.Kind.FEATURE, self.features) - 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)) + # Insert stub documentation section for missing member docs. + # TODO: drop when undocumented members are outlawed + + section = QAPIDoc.ArgSection( + self.info, QAPIDoc.Kind.MEMBER, member.name) + self.args[member.name] = section + + # Determine where to insert stub doc - it should go at the + # end of the members section(s), if any. Note that index 0 + # is assumed to be an untagged intro section, even if it is + # empty. + index = 1 + if len(self.all_sections) > 1: + while self.all_sections[index].kind == QAPIDoc.Kind.MEMBER: + index += 1 + self.all_sections.insert(index, section) + self.args[member.name].connect(member) def connect_feature(self, feature: 'QAPISchemaFeature') -> None: @@ -751,9 +816,20 @@ def connect_feature(self, feature: 'QAPISchemaFeature') -> None: self.features[feature.name].connect(feature) def check_expr(self, expr: QAPIExpression) -> None: - if self.has_section('Returns') and 'command' not in expr: - raise QAPISemError(self.info, - "'Returns:' is only valid for commands") + 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: @@ -764,7 +840,7 @@ def check_args_section( 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 90546df5345..e16283ada3d 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,15 @@ 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-positional-arguments, + too-many-statements, + unknown-option-value, useless-option-value, [REPORTS] diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 231ebf61ba3..cbe3b5aa91e 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 collections import OrderedDict +from __future__ import annotations + +from abc import ABC, abstractmethod import os import re -from typing import List, Optional +from typing import ( + Any, + Callable, + Dict, + List, + Optional, + Union, + ValuesView, + cast, +) from .common import ( POINTER_SUFFIX, @@ -29,138 +42,230 @@ ) from .error import QAPIError, QAPISemError, QAPISourceError from .expr import check_exprs -from .parser import QAPIExpression, 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 - - 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 + """ + A schema entity. + + 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._checked = False + + def __repr__(self) -> str: + return "<%s at 0x%x>" % (type(self).__name__, id(self)) + + def check(self, schema: QAPISchema) -> None: + # pylint: disable=unused-argument + self._checked = True + + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: + pass + + 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: 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 [] - self._checked = False - def c_name(self): + 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): + def check(self, schema: QAPISchema) -> None: assert not self._checked - seen = {} + super().check(schema) + seen: Dict[str, QAPISchemaMember] = {} for f in self.features: f.check_clash(self.info, seen) - self._checked = 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: for f in self.features: doc.connect_feature(f) - def check_doc(self): - if self.doc: - self.doc.check() - - def _set_module(self, schema, info): - assert self._checked - fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME - self._module = schema.module_by_fname(fname) - self._module.add_entity(self) - - def set_module(self, schema): - self._set_module(schema, self.info) - @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 @@ def is_builtin_module(cls, name: str) -> bool: """ 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 @@ def visit(self, visitor): 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,17 +355,17 @@ def alternate_qtype(self): } 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 need_has_if_optional(self): + 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): + def check(self, schema: QAPISchema) -> None: super().check(schema) for feat in self.features: if feat.is_special(): @@ -266,40 +373,38 @@ def check(self, schema): 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()) @@ -307,41 +412,48 @@ def visit(self, visitor): 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, @@ -351,82 +463,89 @@ def visit(self, visitor): 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 need_has_if_optional(self): + 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): + 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: @@ -435,14 +554,14 @@ def check(self, schema): "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() + seen = {} 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" @@ -452,25 +571,34 @@ def check(self, schema): 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())) + + if self.branches: + self.branches.check(schema, seen) + self.branches.check_clash(self.info, seen) - self.members = members # mark completed + 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 for m in self.members: m.check_clash(info, seen) - if self.variants: - self.variants.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(): @@ -478,65 +606,70 @@ def connect_doc(self, doc=None): 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 has_conditional_members(self): - assert self.members is not None + def has_conditional_members(self) -> bool: return any(m.ifcond.is_present() for m in self.members) - def c_name(self): + 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: @@ -564,87 +697,102 @@ def check(self, schema): % (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: @@ -652,11 +800,11 @@ def check(self, schema, seen): # 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())) + % (v.name, tag_member.type.describe())) if not isinstance(v.type, QAPISchemaObjectType): raise QAPISemError( self.info, @@ -664,29 +812,61 @@ def check(self, schema, seen): % (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( @@ -695,11 +875,11 @@ def check_clash(self, info, seen): % (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 @@ -722,6 +902,7 @@ def describe(self, info): else: assert False + 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) @@ -730,14 +911,19 @@ def describe(self, info): 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: @@ -747,36 +933,43 @@ def connect_doc(self, doc): class QAPISchemaFeature(QAPISchemaMember): role = 'feature' - def is_special(self): - return self.name in ('deprecated', 'unstable') + # Features which are standardized across all schemas + SPECIAL_NAMES = ['deprecated', 'unstable'] + + def is_special(self) -> bool: + return self.name in QAPISchemaFeature.SPECIAL_NAMES 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 need_has(self): - assert self.type + def need_has(self) -> bool: return self.optional and self.type.need_has_if_optional() - def check(self, schema): + 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: @@ -786,24 +979,40 @@ def connect_doc(self, doc): 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 @@ -811,17 +1020,19 @@ def __init__(self, name, info, doc, ifcond, features, 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" @@ -837,22 +1048,21 @@ def check(self, schema): 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, @@ -861,27 +1071,36 @@ def visit(self, visitor): 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" @@ -892,14 +1111,14 @@ def check(self, schema): 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, @@ -907,7 +1126,7 @@ def visit(self, visitor): class QAPISchema: - def __init__(self, fname): + def __init__(self, fname: str): self.fname = fname try: @@ -919,9 +1138,19 @@ def __init__(self, fname): 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] = {} + # NB, values in the dict will identify the first encountered + # usage of a named feature only + self._feature_dict: Dict[str, QAPISchemaFeature] = {} + + # All schemas get the names defined in the QapiSpecialFeature enum. + # Rely on dict iteration order matching insertion order so that + # the special names are emitted first when generating code. + for f in QAPISchemaFeature.SPECIAL_NAMES: + self._feature_dict[f] = QAPISchemaFeature(f, None) + self._schema_dir = os.path.dirname(fname) self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME) self._make_module(fname) @@ -931,37 +1160,47 @@ def __init__(self, fname): 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 features(self) -> ValuesView[QAPISchemaFeature]: + return self._feature_dict.values() + + 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 - - 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_type(self, name): - return self.lookup_entity(name, QAPISchemaType) - - def resolve_type(self, name, info, what): + defn.info, "%s is already defined" % other_defn.describe()) + self._entity_dict[defn.name] = defn + + def lookup_entity(self, name: str) -> Optional[QAPISchemaEntity]: + return self._entity_dict.get(name) + + 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: 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( @@ -973,31 +1212,33 @@ def _module_name(self, fname: str) -> str: 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: QAPIExpression): + def _def_include(self, expr: QAPIExpression) -> None: include = expr['include'] 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'), @@ -1016,67 +1257,102 @@ def _def_predefineds(self): 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 [] + + for f in features: + feat = QAPISchemaFeature(f['name'], info) + if feat.name not in self._feature_dict: + self._feature_dict[feat.name] = feat + 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: QAPIExpression): + 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( + 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:] @@ -1087,31 +1363,41 @@ def _make_member(self, name, typ, ifcond, features, info): 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: QAPIExpression): + 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( + 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: QAPIExpression): + def _def_union_type(self, expr: QAPIExpression) -> None: name = expr['union'] base = expr['base'] tag_name = expr['discriminator'] @@ -1130,13 +1416,13 @@ def _def_union_type(self, expr: QAPIExpression): info) for (key, value) in data.items()] members: List[QAPISchemaObjectTypeMember] = [] - self._def_entity( + 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: QAPIExpression): + def _def_alternate_type(self, expr: QAPIExpression) -> None: name = expr['alternate'] data = expr['data'] assert isinstance(data, dict) @@ -1149,12 +1435,12 @@ def _def_alternate_type(self, expr: QAPIExpression): info) for (key, value) in data.items()] tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) - self._def_entity( + self._def_definition( QAPISchemaAlternateType( name, info, expr.doc, ifcond, features, - QAPISchemaVariants(None, info, tag_member, variants))) + QAPISchemaAlternatives(info, variants, tag_member))) - def _def_command(self, expr: QAPIExpression): + def _def_command(self, expr: QAPIExpression) -> None: name = expr['command'] data = expr.get('data') rets = expr.get('returns') @@ -1167,34 +1453,33 @@ def _def_command(self, expr: QAPIExpression): ifcond = QAPISchemaIfCond(expr.get('if')) info = expr.info features = self._make_features(expr.get('features'), info) - if isinstance(data, OrderedDict): + if isinstance(data, dict): data = self._make_implicit_object_type( name, info, ifcond, 'arg', self._make_members(data, info)) if isinstance(rets, list): assert len(rets) == 1 rets = self._make_array_type(rets[0], info) - self._def_entity(QAPISchemaCommand(name, info, expr.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: QAPIExpression): + 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): + if isinstance(data, dict): data = self._make_implicit_object_type( name, info, ifcond, 'arg', self._make_members(data, info)) - self._def_entity(QAPISchemaEvent(name, info, expr.doc, ifcond, - features, data, boxed)) + self._def_definition(QAPISchemaEvent(name, info, expr.doc, ifcond, + features, data, boxed)) - def _def_exprs(self, exprs): + def _def_exprs(self, exprs: List[QAPIExpression]) -> None: for expr in exprs: if 'enum' in expr: self._def_enum_type(expr) @@ -1213,15 +1498,22 @@ def _def_exprs(self, exprs): 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() + + features = list(self._feature_dict.values()) + if len(features) > 64: + raise QAPISemError( + features[64].info, + "Maximum of 64 schema features is permitted") - 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 04193cc9643..ffdc3f482ac 100644 --- a/scripts/qapi/source.py +++ b/scripts/qapi/source.py @@ -24,6 +24,8 @@ def __init__(self) -> None: 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] = [] @@ -45,9 +47,9 @@ def set_defn(self, meta: str, name: str) -> None: self.defn_meta = meta self.defn_name = name - def next_line(self: T) -> T: + def next_line(self: T, n: int = 1) -> T: info = copy.copy(self) - info.line += 1 + info.line += n return info def loc(self) -> str: diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index c39d054d2cd..2bf75338283 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -16,13 +16,11 @@ from typing import List, Optional from .common import c_enum_const, c_name, mcgen -from .gen import ( - QAPISchemaModularCVisitor, - gen_special_features, - ifcontext, -) +from .gen import QAPISchemaModularCVisitor, gen_features, ifcontext from .schema import ( QAPISchema, + QAPISchemaAlternatives, + QAPISchemaBranches, QAPISchemaEnumMember, QAPISchemaFeature, QAPISchemaIfCond, @@ -59,17 +57,17 @@ def gen_enum_lookup(name: str, index=index, name=memb.name) ret += memb.ifcond.gen_endif() - special_features = gen_special_features(memb.features) - if special_features != '0': + features = gen_features(memb.features) + if features != '0': feats += mcgen(''' - [%(index)s] = %(special_features)s, + [%(index)s] = %(features)s, ''', - index=index, special_features=special_features) + index=index, features=features) if feats: ret += mcgen(''' }, - .special_features = (const unsigned char[%(max_index)s]) { + .features = (const uint64_t[%(max_index)s]) { ''', max_index=max_index) ret += feats @@ -169,7 +167,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(''' @@ -306,11 +304,14 @@ def _begin_user_module(self, name: str) -> None: #include "qapi/dealloc-visitor.h" #include "%(types)s.h" #include "%(visit)s.h" +#include "%(prefix)sqapi-features.h" ''', - types=types, visit=visit)) + types=types, visit=visit, + prefix=self._prefix)) self._genh.preamble_add(mcgen(''' #include "qapi/qapi-builtin-types.h" -''')) +''', + prefix=self._prefix)) def visit_begin(self, schema: QAPISchema) -> None: # gen_object() is recursive, ensure it doesn't visit the empty type @@ -348,13 +349,13 @@ def visit_object_type(self, 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 +370,11 @@ def visit_alternate_type(self, 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 c56ea4d7242..36e240967b6 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -21,13 +21,11 @@ indent, mcgen, ) -from .gen import ( - QAPISchemaModularCVisitor, - gen_special_features, - ifcontext, -) +from .gen import QAPISchemaModularCVisitor, gen_features, ifcontext from .schema import ( QAPISchema, + QAPISchemaAlternatives, + QAPISchemaBranches, QAPISchemaEnumMember, QAPISchemaEnumType, QAPISchemaFeature, @@ -35,7 +33,6 @@ QAPISchemaObjectType, QAPISchemaObjectTypeMember, QAPISchemaType, - QAPISchemaVariants, ) from .source import QAPISourceInfo @@ -63,7 +60,7 @@ def gen_visit_members_decl(name: str) -> str: 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) @@ -102,15 +99,15 @@ def gen_visit_object_members(name: str, ''', name=memb.name, has=has) indent.increase() - special_features = gen_special_features(memb.features) - if special_features != '0': + features = gen_features(memb.features) + if features != '0': ret += mcgen(''' - if (visit_policy_reject(v, "%(name)s", %(special_features)s, errp)) { + if (visit_policy_reject(v, "%(name)s", %(features)s, errp)) { return false; } - if (!visit_policy_skip(v, "%(name)s", %(special_features)s)) { + if (!visit_policy_skip(v, "%(name)s", %(features)s)) { ''', - name=memb.name, special_features=special_features) + name=memb.name, features=features) indent.increase() ret += mcgen(''' if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) { @@ -119,7 +116,7 @@ def gen_visit_object_members(name: str, ''', c_type=memb.type.c_name(), name=memb.name, c_name=c_name(memb.name)) - if special_features != '0': + if features != '0': indent.decrease() ret += mcgen(''' } @@ -131,8 +128,8 @@ def gen_visit_object_members(name: str, ''') 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(''' @@ -140,7 +137,7 @@ def gen_visit_object_members(name: str, ''', 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() @@ -222,7 +219,8 @@ def gen_visit_enum(name: str) -> str: 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, @@ -244,7 +242,7 @@ def gen_visit_alternate(name: str, variants: QAPISchemaVariants) -> str: ''', 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: @@ -278,8 +276,9 @@ def gen_visit_alternate(name: str, variants: QAPISchemaVariants) -> str: 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; @@ -356,10 +355,10 @@ def _begin_user_module(self, name: str) -> None: self._genc.preamble_add(mcgen(''' #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "%(visit)s.h" +#include "%(prefix)sqapi-features.h" ''', - visit=visit)) + visit=visit, prefix=self._prefix)) self._genh.preamble_add(mcgen(''' #include "qapi/qapi-builtin-visit.h" #include "%(types)s.h" @@ -394,14 +393,14 @@ def visit_object_type(self, 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_'): @@ -414,10 +413,10 @@ def visit_alternate_type(self, 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/qcow2-to-stdout.py b/scripts/qcow2-to-stdout.py new file mode 100755 index 00000000000..06b7c13ccbb --- /dev/null +++ b/scripts/qcow2-to-stdout.py @@ -0,0 +1,449 @@ +#!/usr/bin/env python3 + +# This tool reads a disk image in any format and converts it to qcow2, +# writing the result directly to stdout. +# +# Copyright (C) 2024 Igalia, S.L. +# +# Authors: Alberto Garcia +# Madeeha Javed +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# qcow2 files produced by this script are always arranged like this: +# +# - qcow2 header +# - refcount table +# - refcount blocks +# - L1 table +# - L2 tables +# - Data clusters +# +# A note about variable names: in qcow2 there is one refcount table +# and one (active) L1 table, although each can occupy several +# clusters. For the sake of simplicity the code sometimes talks about +# refcount tables and L1 tables when referring to those clusters. + +import argparse +import errno +import math +import os +import signal +import struct +import subprocess +import sys +import tempfile +import time +from contextlib import contextmanager + +QCOW2_DEFAULT_CLUSTER_SIZE = 65536 +QCOW2_DEFAULT_REFCOUNT_BITS = 16 +QCOW2_FEATURE_NAME_TABLE = 0x6803F857 +QCOW2_DATA_FILE_NAME_STRING = 0x44415441 +QCOW2_V3_HEADER_LENGTH = 112 # Header length in QEMU 9.0. Must be a multiple of 8 +QCOW2_INCOMPAT_DATA_FILE_BIT = 2 +QCOW2_AUTOCLEAR_DATA_FILE_RAW_BIT = 1 +QCOW_OFLAG_COPIED = 1 << 63 +QEMU_STORAGE_DAEMON = "qemu-storage-daemon" + + +def bitmap_set(bitmap, idx): + bitmap[idx // 8] |= 1 << (idx % 8) + + +def bitmap_is_set(bitmap, idx): + return (bitmap[idx // 8] & (1 << (idx % 8))) != 0 + + +def bitmap_iterator(bitmap, length): + for idx in range(length): + if bitmap_is_set(bitmap, idx): + yield idx + + +def align_up(num, d): + return d * math.ceil(num / d) + + +# Holes in the input file contain only zeroes so we can skip them and +# save time. This function returns the indexes of the clusters that +# are known to contain data. Those are the ones that we need to read. +def clusters_with_data(fd, cluster_size): + data_to = 0 + while True: + try: + data_from = os.lseek(fd, data_to, os.SEEK_DATA) + data_to = align_up(os.lseek(fd, data_from, os.SEEK_HOLE), cluster_size) + for idx in range(data_from // cluster_size, data_to // cluster_size): + yield idx + except OSError as err: + if err.errno == errno.ENXIO: # End of file reached + break + raise err + + +# write_qcow2_content() expects a raw input file. If we have a different +# format we can use qemu-storage-daemon to make it appear as raw. +@contextmanager +def get_input_as_raw_file(input_file, input_format): + if input_format == "raw": + yield input_file + return + try: + temp_dir = tempfile.mkdtemp() + pid_file = os.path.join(temp_dir, "pid") + raw_file = os.path.join(temp_dir, "raw") + open(raw_file, "wb").close() + ret = subprocess.run( + [ + QEMU_STORAGE_DAEMON, + "--daemonize", + "--pidfile", pid_file, + "--blockdev", f"driver=file,node-name=file0,driver=file,filename={input_file},read-only=on", + "--blockdev", f"driver={input_format},node-name=disk0,file=file0,read-only=on", + "--export", f"type=fuse,id=export0,node-name=disk0,mountpoint={raw_file},writable=off", + ], + capture_output=True, + ) + if ret.returncode != 0: + sys.exit("[Error] Could not start the qemu-storage-daemon:\n" + + ret.stderr.decode().rstrip('\n')) + yield raw_file + finally: + # Kill the storage daemon on exit + # and remove all temporary files + if os.path.exists(pid_file): + with open(pid_file, "r") as f: + pid = int(f.readline()) + os.kill(pid, signal.SIGTERM) + while os.path.exists(pid_file): + time.sleep(0.1) + os.unlink(raw_file) + os.rmdir(temp_dir) + + +def write_features(cluster, offset, data_file_name): + if data_file_name is not None: + encoded_name = data_file_name.encode("utf-8") + padded_name_len = align_up(len(encoded_name), 8) + struct.pack_into(f">II{padded_name_len}s", cluster, offset, + QCOW2_DATA_FILE_NAME_STRING, + len(encoded_name), + encoded_name) + offset += 8 + padded_name_len + + qcow2_features = [ + # Incompatible + (0, 0, "dirty bit"), + (0, 1, "corrupt bit"), + (0, 2, "external data file"), + (0, 3, "compression type"), + (0, 4, "extended L2 entries"), + # Compatible + (1, 0, "lazy refcounts"), + # Autoclear + (2, 0, "bitmaps"), + (2, 1, "raw external data"), + ] + struct.pack_into(">I", cluster, offset, QCOW2_FEATURE_NAME_TABLE) + struct.pack_into(">I", cluster, offset + 4, len(qcow2_features) * 48) + offset += 8 + for feature_type, feature_bit, feature_name in qcow2_features: + struct.pack_into(">BB46s", cluster, offset, + feature_type, feature_bit, feature_name.encode("ascii")) + offset += 48 + + +def write_qcow2_content(input_file, cluster_size, refcount_bits, data_file_name, data_file_raw): + # Some basic values + l1_entries_per_table = cluster_size // 8 + l2_entries_per_table = cluster_size // 8 + refcounts_per_table = cluster_size // 8 + refcounts_per_block = cluster_size * 8 // refcount_bits + + # Virtual disk size, number of data clusters and L1 entries + disk_size = align_up(os.path.getsize(input_file), 512) + total_data_clusters = math.ceil(disk_size / cluster_size) + l1_entries = math.ceil(total_data_clusters / l2_entries_per_table) + allocated_l1_tables = math.ceil(l1_entries / l1_entries_per_table) + + # Max L1 table size is 32 MB (QCOW_MAX_L1_SIZE in block/qcow2.h) + if (l1_entries * 8) > (32 * 1024 * 1024): + sys.exit("[Error] The image size is too large. Try using a larger cluster size.") + + # Two bitmaps indicating which L1 and L2 entries are set + l1_bitmap = bytearray(allocated_l1_tables * l1_entries_per_table // 8) + l2_bitmap = bytearray(l1_entries * l2_entries_per_table // 8) + allocated_l2_tables = 0 + allocated_data_clusters = 0 + + if data_file_raw: + # If data_file_raw is set then all clusters are allocated and + # we don't need to read the input file at all. + allocated_l2_tables = l1_entries + for idx in range(l1_entries): + bitmap_set(l1_bitmap, idx) + for idx in range(total_data_clusters): + bitmap_set(l2_bitmap, idx) + else: + # Open the input file for reading + fd = os.open(input_file, os.O_RDONLY) + zero_cluster = bytes(cluster_size) + # Read all the clusters that contain data + for idx in clusters_with_data(fd, cluster_size): + cluster = os.pread(fd, cluster_size, cluster_size * idx) + # If the last cluster is smaller than cluster_size pad it with zeroes + if len(cluster) < cluster_size: + cluster += bytes(cluster_size - len(cluster)) + # If a cluster has non-zero data then it must be allocated + # in the output file and its L2 entry must be set + if cluster != zero_cluster: + bitmap_set(l2_bitmap, idx) + allocated_data_clusters += 1 + # Allocated data clusters also need their corresponding L1 entry and L2 table + l1_idx = math.floor(idx / l2_entries_per_table) + if not bitmap_is_set(l1_bitmap, l1_idx): + bitmap_set(l1_bitmap, l1_idx) + allocated_l2_tables += 1 + + # Total amount of allocated clusters excluding the refcount blocks and table + total_allocated_clusters = 1 + allocated_l1_tables + allocated_l2_tables + if data_file_name is None: + total_allocated_clusters += allocated_data_clusters + + # Clusters allocated for the refcount blocks and table + allocated_refcount_blocks = math.ceil(total_allocated_clusters / refcounts_per_block) + allocated_refcount_tables = math.ceil(allocated_refcount_blocks / refcounts_per_table) + + # Now we have a problem because allocated_refcount_blocks and allocated_refcount_tables... + # (a) increase total_allocated_clusters, and + # (b) need to be recalculated when total_allocated_clusters is increased + # So we need to repeat the calculation as long as the numbers change + while True: + new_total_allocated_clusters = total_allocated_clusters + allocated_refcount_tables + allocated_refcount_blocks + new_allocated_refcount_blocks = math.ceil(new_total_allocated_clusters / refcounts_per_block) + if new_allocated_refcount_blocks > allocated_refcount_blocks: + allocated_refcount_blocks = new_allocated_refcount_blocks + allocated_refcount_tables = math.ceil(allocated_refcount_blocks / refcounts_per_table) + else: + break + + # Now that we have the final numbers we can update total_allocated_clusters + total_allocated_clusters += allocated_refcount_tables + allocated_refcount_blocks + + # At this point we have the exact number of clusters that the output + # image is going to use so we can calculate all the offsets. + current_cluster_idx = 1 + + refcount_table_offset = current_cluster_idx * cluster_size + current_cluster_idx += allocated_refcount_tables + + refcount_block_offset = current_cluster_idx * cluster_size + current_cluster_idx += allocated_refcount_blocks + + l1_table_offset = current_cluster_idx * cluster_size + current_cluster_idx += allocated_l1_tables + + l2_table_offset = current_cluster_idx * cluster_size + current_cluster_idx += allocated_l2_tables + + data_clusters_offset = current_cluster_idx * cluster_size + + # Calculate some values used in the qcow2 header + if allocated_l1_tables == 0: + l1_table_offset = 0 + + hdr_cluster_bits = int(math.log2(cluster_size)) + hdr_refcount_bits = int(math.log2(refcount_bits)) + hdr_length = QCOW2_V3_HEADER_LENGTH + hdr_incompat_features = 0 + if data_file_name is not None: + hdr_incompat_features |= 1 << QCOW2_INCOMPAT_DATA_FILE_BIT + hdr_autoclear_features = 0 + if data_file_raw: + hdr_autoclear_features |= 1 << QCOW2_AUTOCLEAR_DATA_FILE_RAW_BIT + + ### Write qcow2 header + cluster = bytearray(cluster_size) + struct.pack_into(">4sIQIIQIIQQIIQQQQII", cluster, 0, + b"QFI\xfb", # QCOW magic string + 3, # version + 0, # backing file offset + 0, # backing file sizes + hdr_cluster_bits, + disk_size, + 0, # encryption method + l1_entries, + l1_table_offset, + refcount_table_offset, + allocated_refcount_tables, + 0, # number of snapshots + 0, # snapshot table offset + hdr_incompat_features, + 0, # compatible features + hdr_autoclear_features, + hdr_refcount_bits, + hdr_length, + ) + + write_features(cluster, hdr_length, data_file_name) + + sys.stdout.buffer.write(cluster) + + ### Write refcount table + cur_offset = refcount_block_offset + remaining_refcount_table_entries = allocated_refcount_blocks # Each entry is a pointer to a refcount block + while remaining_refcount_table_entries > 0: + cluster = bytearray(cluster_size) + to_write = min(remaining_refcount_table_entries, refcounts_per_table) + remaining_refcount_table_entries -= to_write + for idx in range(to_write): + struct.pack_into(">Q", cluster, idx * 8, cur_offset) + cur_offset += cluster_size + sys.stdout.buffer.write(cluster) + + ### Write refcount blocks + remaining_refcount_block_entries = total_allocated_clusters # One entry for each allocated cluster + for tbl in range(allocated_refcount_blocks): + cluster = bytearray(cluster_size) + to_write = min(remaining_refcount_block_entries, refcounts_per_block) + remaining_refcount_block_entries -= to_write + # All refcount entries contain the number 1. The only difference + # is their bit width, defined when the image is created. + for idx in range(to_write): + if refcount_bits == 64: + struct.pack_into(">Q", cluster, idx * 8, 1) + elif refcount_bits == 32: + struct.pack_into(">L", cluster, idx * 4, 1) + elif refcount_bits == 16: + struct.pack_into(">H", cluster, idx * 2, 1) + elif refcount_bits == 8: + cluster[idx] = 1 + elif refcount_bits == 4: + cluster[idx // 2] |= 1 << ((idx % 2) * 4) + elif refcount_bits == 2: + cluster[idx // 4] |= 1 << ((idx % 4) * 2) + elif refcount_bits == 1: + cluster[idx // 8] |= 1 << (idx % 8) + sys.stdout.buffer.write(cluster) + + ### Write L1 table + cur_offset = l2_table_offset + for tbl in range(allocated_l1_tables): + cluster = bytearray(cluster_size) + for idx in range(l1_entries_per_table): + l1_idx = tbl * l1_entries_per_table + idx + if bitmap_is_set(l1_bitmap, l1_idx): + struct.pack_into(">Q", cluster, idx * 8, cur_offset | QCOW_OFLAG_COPIED) + cur_offset += cluster_size + sys.stdout.buffer.write(cluster) + + ### Write L2 tables + cur_offset = data_clusters_offset + for tbl in range(l1_entries): + # Skip the empty L2 tables. We can identify them because + # there is no L1 entry pointing at them. + if bitmap_is_set(l1_bitmap, tbl): + cluster = bytearray(cluster_size) + for idx in range(l2_entries_per_table): + l2_idx = tbl * l2_entries_per_table + idx + if bitmap_is_set(l2_bitmap, l2_idx): + if data_file_name is None: + struct.pack_into(">Q", cluster, idx * 8, cur_offset | QCOW_OFLAG_COPIED) + cur_offset += cluster_size + else: + struct.pack_into(">Q", cluster, idx * 8, (l2_idx * cluster_size) | QCOW_OFLAG_COPIED) + sys.stdout.buffer.write(cluster) + + ### Write data clusters + if data_file_name is None: + for idx in bitmap_iterator(l2_bitmap, total_data_clusters): + cluster = os.pread(fd, cluster_size, cluster_size * idx) + # If the last cluster is smaller than cluster_size pad it with zeroes + if len(cluster) < cluster_size: + cluster += bytes(cluster_size - len(cluster)) + sys.stdout.buffer.write(cluster) + + if not data_file_raw: + os.close(fd) + + +def main(): + # Command-line arguments + parser = argparse.ArgumentParser( + description="This program converts a QEMU disk image to qcow2 " + "and writes it to the standard output" + ) + parser.add_argument("input_file", help="name of the input file") + parser.add_argument( + "-f", + dest="input_format", + metavar="input_format", + help="format of the input file (default: raw)", + default="raw", + ) + parser.add_argument( + "-c", + dest="cluster_size", + metavar="cluster_size", + help=f"qcow2 cluster size (default: {QCOW2_DEFAULT_CLUSTER_SIZE})", + default=QCOW2_DEFAULT_CLUSTER_SIZE, + type=int, + choices=[1 << x for x in range(9, 22)], + ) + parser.add_argument( + "-r", + dest="refcount_bits", + metavar="refcount_bits", + help=f"width of the reference count entries (default: {QCOW2_DEFAULT_REFCOUNT_BITS})", + default=QCOW2_DEFAULT_REFCOUNT_BITS, + type=int, + choices=[1 << x for x in range(7)], + ) + parser.add_argument( + "-d", + dest="data_file", + help="create an image with input_file as an external data file", + action="store_true", + ) + parser.add_argument( + "-R", + dest="data_file_raw", + help="enable data_file_raw on the generated image (implies -d)", + action="store_true", + ) + args = parser.parse_args() + + if args.data_file_raw: + args.data_file = True + + if not os.path.isfile(args.input_file): + sys.exit(f"[Error] {args.input_file} does not exist or is not a regular file.") + + if args.data_file and args.input_format != "raw": + sys.exit("[Error] External data files can only be used with raw input images") + + # A 512 byte header is too small for the data file name extension + if args.data_file and args.cluster_size == 512: + sys.exit("[Error] External data files require a larger cluster size") + + if sys.stdout.isatty(): + sys.exit("[Error] Refusing to write to a tty. Try redirecting stdout.") + + if args.data_file: + data_file_name = args.input_file + else: + data_file_name = None + + with get_input_as_raw_file(args.input_file, args.input_format) as raw_file: + write_qcow2_content( + raw_file, + args.cluster_size, + args.refcount_bits, + data_file_name, + args.data_file_raw, + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index 6ef9f118d9f..5fd462b1d1c 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -144,35 +144,35 @@ loongarch64_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x loongarch64_mask='\xff\xff\xff\xff\xff\xff\xff\xfc\x00\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' loongarch64_family=loongarch -qemu_get_family() { - cpu=${HOST_ARCH:-$(uname -m)} +# Converts the name of a host CPU architecture to the corresponding QEMU +# target. +# +# FIXME: This can probably be simplified a lot by dropping most entries. +# Remember that the script is only used on Linux, so we only need to +# handle the strings Linux uses to report the host CPU architecture. +qemu_normalize() { + cpu="$1" case "$cpu" in - amd64|i386|i486|i586|i686|i86pc|BePC|x86_64) + i[3-6]86) echo "i386" ;; - mips*) - echo "mips" + amd64) + echo "x86_64" ;; - "Power Macintosh"|ppc64|powerpc|ppc) + powerpc) echo "ppc" ;; - ppc64el|ppc64le) - echo "ppcle" + ppc64el) + echo "ppc64le" ;; - arm|armel|armhf|arm64|armv[4-9]*l|aarch64) + armel|armhf|armv[4-9]*l) echo "arm" ;; - armeb|armv[4-9]*b|aarch64_be) + armv[4-9]*b) echo "armeb" ;; - sparc*) - echo "sparc" - ;; - riscv*) - echo "riscv" - ;; - loongarch*) - echo "loongarch" + arm64) + echo "aarch64" ;; *) echo "$cpu" @@ -205,6 +205,9 @@ Usage: qemu-binfmt-conf.sh [--qemu-path PATH][--debian][--systemd CPU] --persistent: if yes, the interpreter is loaded when binfmt is configured and remains in memory. All future uses are cloned from the open file. + --ignore-family: if yes, it is assumed that the host CPU (e.g. riscv64) + can't natively run programs targeting a CPU that is + part of the same family (e.g. riscv32). --preserve-argv0 preserve argv[0] To import templates with update-binfmts, use : @@ -309,7 +312,13 @@ EOF qemu_set_binfmts() { # probe cpu type - host_family=$(qemu_get_family) + host_cpu=$(qemu_normalize ${HOST_ARCH:-$(uname -m)}) + host_family=$(eval echo \$${host_cpu}_family) + + if [ "$host_family" = "" ] ; then + echo "INTERNAL ERROR: unknown host cpu $host_cpu" 1>&2 + exit 1 + fi # register the interpreter for each cpu except for the native one @@ -318,20 +327,28 @@ qemu_set_binfmts() { mask=$(eval echo \$${cpu}_mask) family=$(eval echo \$${cpu}_family) + target="$cpu" + if [ "$cpu" = "i486" ] ; then + target="i386" + fi + + qemu="$QEMU_PATH/qemu-$target$QEMU_SUFFIX" + if [ "$magic" = "" ] || [ "$mask" = "" ] || [ "$family" = "" ] ; then echo "INTERNAL ERROR: unknown cpu $cpu" 1>&2 continue fi - qemu="$QEMU_PATH/qemu-$cpu" - if [ "$cpu" = "i486" ] ; then - qemu="$QEMU_PATH/qemu-i386" + if [ "$host_family" = "$family" ] ; then + # When --ignore-family is used, we have to generate rules even + # for targets that are in the same family as the host CPU. The + # only exception is of course when the CPU types exactly match + if [ "$target" = "$host_cpu" ] || [ "$IGNORE_FAMILY" = "no" ] ; then + continue + fi fi - qemu="$qemu$QEMU_SUFFIX" - if [ "$host_family" != "$family" ] ; then - $BINFMT_SET - fi + $BINFMT_SET done } @@ -346,10 +363,11 @@ CREDENTIAL=no PERSISTENT=no PRESERVE_ARG0=no QEMU_SUFFIX="" +IGNORE_FAMILY=no _longopts="debian,systemd:,qemu-path:,qemu-suffix:,exportdir:,help,credential:,\ -persistent:,preserve-argv0:" -options=$(getopt -o ds:Q:S:e:hc:p:g:F: -l ${_longopts} -- "$@") +persistent:,preserve-argv0:,ignore-family:" +options=$(getopt -o ds:Q:S:e:hc:p:g:F:i: -l ${_longopts} -- "$@") eval set -- "$options" while true ; do @@ -409,6 +427,10 @@ while true ; do shift PRESERVE_ARG0="$1" ;; + -i|--ignore-family) + shift + IGNORE_FAMILY="$1" + ;; *) break ;; diff --git a/scripts/qemu-gdb.py b/scripts/qemu-gdb.py index 4d2a9f6c430..cfae94a2e90 100644 --- a/scripts/qemu-gdb.py +++ b/scripts/qemu-gdb.py @@ -45,3 +45,5 @@ def __init__(self): # Default to silently passing through SIGUSR1, because QEMU sends it # to itself a lot. gdb.execute('handle SIGUSR1 pass noprint nostop') +# Always print full stack for python errors, easier to debug and report issues +gdb.execute('set python print-stack full') diff --git a/scripts/qemu-guest-agent/fsfreeze-hook b/scripts/qemu-guest-agent/fsfreeze-hook index 13aafd48451..c1feb6f5cee 100755 --- a/scripts/qemu-guest-agent/fsfreeze-hook +++ b/scripts/qemu-guest-agent/fsfreeze-hook @@ -19,15 +19,43 @@ is_ignored_file() { return 1 } +USE_SYSLOG=0 +# if log file is not writable, fallback to syslog +[ ! -w "$LOGFILE" ] && USE_SYSLOG=1 +# try to update log file and fallback to syslog if it fails +touch "$LOGFILE" &>/dev/null || USE_SYSLOG=1 + +# Ensure the log file is writable, fallback to syslog if not +log_message() { + local message="$1" + if [ "$USE_SYSLOG" -eq 0 ]; then + printf "%s: %s\n" "$(date)" "$message" >>"$LOGFILE" + else + logger -t qemu-ga-freeze-hook "$message" + fi +} + # Iterate executables in directory "fsfreeze-hook.d" with the specified args [ ! -d "$FSFREEZE_D" ] && exit 0 + for file in "$FSFREEZE_D"/* ; do is_ignored_file "$file" && continue [ -x "$file" ] || continue - printf "$(date): execute $file $@\n" >>$LOGFILE - "$file" "$@" >>$LOGFILE 2>&1 - STATUS=$? - printf "$(date): $file finished with status=$STATUS\n" >>$LOGFILE + + log_message "Executing $file $@" + if [ "$USE_SYSLOG" -eq 0 ]; then + "$file" "$@" >>"$LOGFILE" 2>&1 + STATUS=$? + else + "$file" "$@" 2>&1 | logger -t qemu-ga-freeze-hook + STATUS=${PIPESTATUS[0]} + fi + + if [ $STATUS -ne 0 ]; then + log_message "Error: $file finished with status=$STATUS" + else + log_message "$file finished successfully" + fi done exit 0 diff --git a/scripts/qemu-plugin-symbols.py b/scripts/qemu-plugin-symbols.py new file mode 100755 index 00000000000..e285ebb8f9e --- /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/qemu-trace-stap b/scripts/qemu-trace-stap index eb6e951ff23..e983460ee75 100755 --- a/scripts/qemu-trace-stap +++ b/scripts/qemu-trace-stap @@ -56,6 +56,7 @@ def tapset_dir(binary): def cmd_run(args): + stap = which("stap") prefix = probe_prefix(args.binary) tapsets = tapset_dir(args.binary) @@ -76,7 +77,7 @@ def cmd_run(args): # We request an 8MB buffer, since the stap default 1MB buffer # can be easily overflowed by frequently firing QEMU traces - stapargs = ["stap", "-s", "8", "-I", tapsets ] + stapargs = [stap, "-s", "8", "-I", tapsets ] if args.pid is not None: stapargs.extend(["-x", args.pid]) stapargs.extend(["-e", script]) @@ -84,6 +85,7 @@ def cmd_run(args): def cmd_list(args): + stap = which("stap") tapsets = tapset_dir(args.binary) if args.verbose: @@ -96,7 +98,7 @@ def cmd_list(args): if verbose: print("Listing probes with name '%s'" % script) - proc = subprocess.Popen(["stap", "-I", tapsets, "-l", script], + proc = subprocess.Popen([stap, "-I", tapsets, "-l", script], stdout=subprocess.PIPE, universal_newlines=True) out, err = proc.communicate() diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py index 7db46d4b684..e98fc48a4b2 100644 --- a/scripts/qemugdb/coroutine.py +++ b/scripts/qemugdb/coroutine.py @@ -13,28 +13,9 @@ VOID_PTR = gdb.lookup_type('void').pointer() -def get_fs_base(): - '''Fetch %fs base value using arch_prctl(ARCH_GET_FS). This is - pthread_self().''' - # %rsp - 120 is scratch space according to the SystemV ABI - old = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') - gdb.execute('call (int)arch_prctl(0x1003, $rsp - 120)', False, True) - fs_base = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') - gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True) - return fs_base - def pthread_self(): - '''Fetch pthread_self() from the glibc start_thread function.''' - f = gdb.newest_frame() - while f.name() != 'start_thread': - f = f.older() - if f is None: - return get_fs_base() - - try: - return f.read_var("arg") - except ValueError: - return get_fs_base() + '''Fetch the base address of TLS.''' + return gdb.parse_and_eval("$fs_base") def get_glibc_pointer_guard(): '''Fetch glibc pointer guard value''' @@ -65,9 +46,60 @@ def get_jmpbuf_regs(jmpbuf): 'r15': jmpbuf[JB_R15], 'rip': glibc_ptr_demangle(jmpbuf[JB_PC], pointer_guard) } -def bt_jmpbuf(jmpbuf): - '''Backtrace a jmpbuf''' - regs = get_jmpbuf_regs(jmpbuf) +def symbol_lookup(addr): + # Example: "__clone3 + 44 in section .text of /lib64/libc.so.6" + result = gdb.execute(f"info symbol {hex(addr)}", to_string=True).strip() + try: + if "+" in result: + (func, result) = result.split(" + ") + (offset, result) = result.split(" in ") + else: + offset = "0" + (func, result) = result.split(" in ") + func_str = f"{func}<+{offset}> ()" + except: + return f"??? ({result})" + + # Example: Line 321 of "../util/coroutine-ucontext.c" starts at address + # 0x55cf3894d993 and ends at 0x55cf3894d9ab + # . + result = gdb.execute(f"info line *{hex(addr)}", to_string=True).strip() + if not result.startswith("Line "): + return func_str + result = result[5:] + + try: + result = result.split(" starts ")[0] + (line, path) = result.split(" of ") + path = path.replace("\"", "") + except: + return func_str + + return f"{func_str} at {path}:{line}" + +def dump_backtrace(regs): + ''' + Backtrace dump with raw registers, mimic GDB command 'bt'. + ''' + # Here only rbp and rip that matter.. + rbp = regs['rbp'] + rip = regs['rip'] + i = 0 + + while rbp: + # For all return addresses on stack, we want to look up symbol/line + # on the CALL command, because the return address is the next + # instruction instead of the CALL. Here -1 would work for any + # sized CALL instruction. + print(f"#{i} {hex(rip)} in {symbol_lookup(rip if i == 0 else rip-1)}") + rip = gdb.parse_and_eval(f"*(uint64_t *)(uint64_t)({hex(rbp)} + 8)") + rbp = gdb.parse_and_eval(f"*(uint64_t *)(uint64_t)({hex(rbp)})") + i += 1 + +def dump_backtrace_live(regs): + ''' + Backtrace dump with gdb's 'bt' command, only usable in a live session. + ''' old = dict() # remember current stack frame and select the topmost @@ -88,6 +120,17 @@ def bt_jmpbuf(jmpbuf): selected_frame.select() +def bt_jmpbuf(jmpbuf): + '''Backtrace a jmpbuf''' + regs = get_jmpbuf_regs(jmpbuf) + try: + # This reuses gdb's "bt" command, which can be slightly prettier + # but only works with live sessions. + dump_backtrace_live(regs) + except: + # If above doesn't work, fallback to poor man's unwind + dump_backtrace(regs) + def co_cast(co): return co.cast(gdb.lookup_type('CoroutineUContext').pointer()) @@ -120,10 +163,15 @@ def invoke(self, arg, from_tty): gdb.execute("bt") - if gdb.parse_and_eval("qemu_in_coroutine()") == False: - return + try: + # This only works with a live session + co_ptr = gdb.parse_and_eval("qemu_coroutine_self()") + except: + # Fallback to use hard-coded ucontext vars if it's coredump + co_ptr = gdb.parse_and_eval("co_tls_current") - co_ptr = gdb.parse_and_eval("qemu_coroutine_self()") + if co_ptr == False: + return while True: co = co_cast(co_ptr) diff --git a/scripts/qom-cast-macro-clean-cocci-gen.py b/scripts/qom-cast-macro-clean-cocci-gen.py index 2fa8438a146..5aa51d0c18e 100644 --- a/scripts/qom-cast-macro-clean-cocci-gen.py +++ b/scripts/qom-cast-macro-clean-cocci-gen.py @@ -13,8 +13,11 @@ # --in-place \ # --dir . # -# SPDX-FileContributor: Philippe Mathieu-Daudé -# SPDX-FileCopyrightText: 2023 Linaro Ltd. +# Copyright (c) 2023 Linaro Ltd. +# +# Authors: +# Philippe Mathieu-Daudé +# # SPDX-License-Identifier: GPL-2.0-or-later import re diff --git a/scripts/rdma-migration-helper.sh b/scripts/rdma-migration-helper.sh new file mode 100755 index 00000000000..d784d1566a9 --- /dev/null +++ b/scripts/rdma-migration-helper.sh @@ -0,0 +1,109 @@ +#!/bin/bash + +# Copied from blktests +get_ipv4_addr() +{ + ip -4 -o addr show dev "$1" | + sed -n 's/.*[[:blank:]]inet[[:blank:]]*\([^[:blank:]/]*\).*/\1/p' | + head -1 | tr -d '\n' +} + +get_ipv6_addr() { + ipv6=$(ip -6 -o addr show dev "$1" | + sed -n 's/.*[[:blank:]]inet6[[:blank:]]*\([^[:blank:]/]*\).*/\1/p' | + head -1 | tr -d '\n') + + [ $? -eq 0 ] || return + + if [[ "$ipv6" =~ ^fe80: ]]; then + echo -n "[$ipv6%$1]" + else + echo -n "[$ipv6]" + fi +} + +# existing rdma interfaces +rdma_interfaces() +{ + rdma link show | sed -nE 's/^link .* netdev ([^ ]+).*$/\1 /p' | + grep -Ev '^(lo|tun|tap)' +} + +# existing valid ipv4 interfaces +ipv4_interfaces() +{ + ip -o addr show | awk '/inet / {print $2}' | grep -Ev '^(lo|tun|tap)' +} + +ipv6_interfaces() +{ + ip -o addr show | awk '/inet6 / {print $2}' | grep -Ev '^(lo|tun|tap)' +} + +rdma_rxe_detect() +{ + family=$1 + for r in $(rdma_interfaces) + do + "$family"_interfaces | grep -qw $r && get_"$family"_addr $r && return + done + + return 1 +} + +rdma_rxe_setup() +{ + family=$1 + for i in $("$family"_interfaces) + do + if rdma_interfaces | grep -qw $i; then + echo "$family: Reuse the existing rdma/rxe ${i}_rxe" \ + "for $i with $(get_"$family"_addr $i)" + return + fi + + rdma link add "${i}_rxe" type rxe netdev "$i" && { + echo "$family: Setup new rdma/rxe ${i}_rxe" \ + "for $i with $(get_"$family"_addr $i)" + return + } + done + + echo "$family: Failed to setup any new rdma/rxe link" >&2 + return 1 +} + +rdma_rxe_clean() +{ + modprobe -r rdma_rxe +} + +IP_FAMILY=${IP_FAMILY:-ipv4} +if [ "$IP_FAMILY" != "ipv6" ] && [ "$IP_FAMILY" != "ipv4" ]; then + echo "Unknown ip family '$IP_FAMILY', only ipv4 or ipv6 is supported." >&2 + exit 1 +fi + +operation=${1:-detect} + +command -v rdma >/dev/null || { + echo "Command 'rdma' is not available, please install it first." >&2 + exit 1 +} + +if [ "$operation" == "setup" ] || [ "$operation" == "clean" ]; then + [ "$UID" == 0 ] || { + echo "Root privilege is required to setup/clean a rdma/rxe link" >&2 + exit 1 + } + if [ "$operation" == "setup" ]; then + rdma_rxe_setup ipv4 + rdma_rxe_setup ipv6 + else + rdma_rxe_clean + fi +elif [ "$operation" == "detect" ]; then + rdma_rxe_detect "$IP_FAMILY" +else + echo "Usage: $0 [setup | detect | clean]" +fi diff --git a/scripts/render_block_graph.py b/scripts/render_block_graph.py index 8f731a5cfe1..3e1a2e3fa71 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 b89dc29555a..4ce7ff51cc7 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) @@ -113,7 +122,12 @@ def print_event(eid, name, string=None, event_count=None): def decode_unimp(eid, name, _unused_dumpfile): "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 @@ def swallow_async_qword(eid, name, dumpfile): 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 @@ def decode_clock(eid, name, dumpfile): # 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 @@ def decode_clock(eid, name, dumpfile): 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 @@ def decode_clock(eid, name, dumpfile): 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 @@ def decode_clock(eid, name, dumpfile): 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 00000000000..975bddf7f1a --- /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 +from dataclasses import dataclass +import logging +from pathlib import Path +from typing import Any, Iterable, List, Mapping, Optional, Set + +try: + import tomllib +except ImportError: + import tomli as tomllib + +STRICT_LINTS = {"unknown_lints", "warnings"} + + +class CargoTOML: + tomldata: Mapping[Any, Any] + workspace_data: Mapping[Any, Any] + check_cfg: Set[str] + + def __init__(self, path: Optional[str], workspace: Optional[str]): + if path is not None: + with open(path, 'rb') as f: + self.tomldata = tomllib.load(f) + else: + self.tomldata = {"lints": {"workspace": True}} + + if workspace is not None: + with open(workspace, 'rb') as f: + self.workspace_data = tomllib.load(f) + if "workspace" not in self.workspace_data: + self.workspace_data["workspace"] = {} + + self.check_cfg = set(self.find_check_cfg()) + + def find_check_cfg(self) -> Iterable[str]: + toml_lints = self.lints + rust_lints = toml_lints.get("rust", {}) + cfg_lint = rust_lints.get("unexpected_cfgs", {}) + return cfg_lint.get("check-cfg", []) + + @property + def lints(self) -> Mapping[Any, Any]: + return self.get_table("lints", True) + + def get_table(self, key: str, can_be_workspace: bool = False) -> Mapping[Any, Any]: + table = self.tomldata.get(key, {}) + if can_be_workspace and table.get("workspace", False) is True: + table = self.workspace_data["workspace"].get(key, {}) + + return table + + +@dataclass +class LintFlag: + flags: List[str] + priority: int + + +def generate_lint_flags(cargo_toml: CargoTOML, strict_lints: bool) -> Iterable[str]: + """Converts Cargo.toml lints to rustc -A/-D/-F/-W flags.""" + + toml_lints = cargo_toml.lints + + lint_list = [] + for k, v in toml_lints.items(): + prefix = "" if k == "rust" else k + "::" + for lint, data in v.items(): + level = data if isinstance(data, str) else data["level"] + priority = 0 if isinstance(data, str) else data.get("priority", 0) + if level == "deny": + flag = "-D" + elif level == "allow": + flag = "-A" + elif level == "warn": + flag = "-W" + elif level == "forbid": + flag = "-F" + else: + raise Exception(f"invalid level {level} for {prefix}{lint}") + + if not (strict_lints and lint in STRICT_LINTS): + lint_list.append(LintFlag(flags=[flag, prefix + lint], priority=priority)) + + if strict_lints: + for lint in STRICT_LINTS: + lint_list.append(LintFlag(flags=["-D", lint], priority=1000000)) + + lint_list.sort(key=lambda x: x.priority) + for lint in lint_list: + yield from lint.flags + + +def generate_cfg_flags(header: str, cargo_toml: CargoTOML) -> Iterable[str]: + """Converts defines from config[..].h headers to rustc --cfg flags.""" + + 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[0] + if f'cfg({name})' not in cargo_toml.check_cfg: + continue + if len(cfg) >= 2 and cfg[1] != "1": + continue + cfg_list.append("--cfg") + cfg_list.append(name) + return cfg_list + + +def main() -> None: + 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=[], + ) + parser.add_argument( + metavar="TOML_FILE", + action="store", + dest="cargo_toml", + help="path to Cargo.toml file", + nargs='?', + ) + parser.add_argument( + "--workspace", + metavar="DIR", + action="store", + dest="workspace", + help="path to root of the workspace", + required=False, + default=None, + ) + parser.add_argument( + "--features", + action="store_true", + dest="features", + help="generate --check-cfg arguments for features", + required=False, + default=None, + ) + parser.add_argument( + "--lints", + action="store_true", + dest="lints", + help="generate arguments from [lints] table", + required=False, + default=None, + ) + parser.add_argument( + "--rustc-version", + metavar="VERSION", + dest="rustc_version", + action="store", + help="version of rustc", + required=False, + default="1.0.0", + ) + parser.add_argument( + "--strict-lints", + action="store_true", + dest="strict_lints", + help="apply stricter checks (for nightly Rust)", + default=False, + ) + args = parser.parse_args() + if args.verbose: + logging.basicConfig(level=logging.DEBUG) + logging.debug("args: %s", args) + + rustc_version = tuple((int(x) for x in args.rustc_version.split('.')[0:2])) + if args.workspace: + workspace_cargo_toml = Path(args.workspace, "Cargo.toml").resolve() + cargo_toml = CargoTOML(args.cargo_toml, str(workspace_cargo_toml)) + else: + cargo_toml = CargoTOML(args.cargo_toml, None) + + if args.lints: + for tok in generate_lint_flags(cargo_toml, args.strict_lints): + print(tok) + + if rustc_version >= (1, 80): + if args.lints: + print("--check-cfg") + print("cfg(test)") + for cfg in sorted(cargo_toml.check_cfg): + print("--check-cfg") + print(cfg) + if args.features: + for feature in cargo_toml.get_table("features"): + if feature != "default": + print("--check-cfg") + print(f'cfg(feature,values("{feature}"))') + + for header in args.config_headers: + for tok in generate_cfg_flags(header, cargo_toml): + print(tok) + + +if __name__ == "__main__": + main() diff --git a/scripts/simpletrace.py b/scripts/simpletrace.py index 1f6d1ae1f3e..cef81b0707f 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 @@ 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)) - - log_version = header[2] + _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}') + 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) - -def read_trace_records(edict, idtoname, fobj): - """Deserialize trace records from a file, yielding record tuples (event_num, timestamp, pid, arg1, ..., arg6). + raise ValueError(f'Log format {log_version} not supported with this QEMU release!') - 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] + + 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.' + ) + + 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 rec + yield (event_mapping[event_name], event_name, timestamp_ns, pid) + tuple(args) -class Analyzer(object): - """A trace file analyzer which processes trace records. +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 @@ def catchall(self, event, rec): """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 build_fn(analyzer, event): # 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) - events = read_events(open(sys.argv[1], 'r'), sys.argv[1]) - process(events, sys.argv[2], analyzer, read_header=read_header) + 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') + + 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 - - 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 - - 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)) - - run(Formatter()) + self.last_timestamp_ns = None + + 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 = [ + 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)) + + 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 8ed97e3c943..b72563895c5 100644 --- a/scripts/symlink-install-tree.py +++ b/scripts/symlink-install-tree.py @@ -4,6 +4,7 @@ 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) diff --git a/scripts/tracetool.py b/scripts/tracetool.py index ab7653a5ce0..5de9ce96d30 100755 --- a/scripts/tracetool.py +++ b/scripts/tracetool.py @@ -44,12 +44,9 @@ def error_opt(msg = None): --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 33cf85e2b04..6dfcbf71e1b 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -12,12 +12,14 @@ __email__ = "stefanha@redhat.com" +import os import re import sys import weakref +from pathlib import PurePath -import tracetool.format import tracetool.backend +import tracetool.format def error_write(*lines): @@ -36,7 +38,7 @@ def error(*lines): def out_open(filename): global out_filename, out_fobj - out_filename = filename + out_filename = posix_relpath(filename) out_fobj = open(filename, 'wt') def out(*lines, **kwargs): @@ -91,7 +93,7 @@ def out(*lines, **kwargs): 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": @@ -210,12 +212,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"]) @@ -301,18 +303,14 @@ def build(line_str, lineno, filename): 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, posix_relpath(filename)) def __repr__(self): """Evaluable string representation for this object.""" @@ -326,7 +324,7 @@ def __repr__(self): 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.""" @@ -451,3 +449,10 @@ def generate(events, group, format, backends, tracetool.backend.dtrace.PROBEPREFIX = probe_prefix tracetool.format.generate(events, format, backend, group) + +def posix_relpath(path, start=None): + try: + path = os.path.relpath(path, start) + except ValueError: + pass + return PurePath(path).as_posix() diff --git a/scripts/tracetool/backend/ftrace.py b/scripts/tracetool/backend/ftrace.py index baed2ae61cb..5fa30ccc08e 100644 --- a/scripts/tracetool/backend/ftrace.py +++ b/scripts/tracetool/backend/ftrace.py @@ -12,8 +12,6 @@ __email__ = "stefanha@redhat.com" -import os.path - from tracetool import out @@ -47,7 +45,7 @@ def generate_h(event, group): args=event.args, event_id="TRACE_" + event.name.upper(), event_lineno=event.lineno, - event_filename=os.path.relpath(event.filename), + event_filename=event.filename, fmt=event.fmt.rstrip("\n"), argnames=argnames) diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index de27b7e62e4..17ba1cd90eb 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -12,8 +12,6 @@ __email__ = "stefanha@redhat.com" -import os.path - from tracetool import out @@ -55,7 +53,7 @@ def generate_h(event, group): ' }', cond=cond, event_lineno=event.lineno, - event_filename=os.path.relpath(event.filename), + event_filename=event.filename, name=event.name, fmt=event.fmt.rstrip("\n"), argnames=argnames) diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py index a74d61fcd64..2688d4b64b3 100644 --- a/scripts/tracetool/backend/simple.py +++ b/scripts/tracetool/backend/simple.py @@ -36,8 +36,17 @@ def generate_h_begin(events, group): def generate_h(event, group): - out(' _simple_%(api)s(%(args)s);', + event_id = 'TRACE_' + event.name.upper() + if "vcpu" in event.properties: + # already checked on the generic format code + cond = "true" + else: + cond = "trace_event_get_state(%s)" % event_id + out(' if (%(cond)s) {', + ' _simple_%(api)s(%(args)s);', + ' }', api=event.api(), + cond=cond, args=", ".join(event.args.names())) @@ -72,22 +81,10 @@ def generate_c(event, group): if len(event.args) == 0: sizestr = '0' - event_id = 'TRACE_' + event.name.upper() - if "vcpu" in event.properties: - # already checked on the generic format code - cond = "true" - else: - cond = "trace_event_get_state(%s)" % event_id - out('', - ' if (!%(cond)s) {', - ' return;', - ' }', - '', ' if (trace_record_start(&rec, %(event_obj)s.id, %(size_str)s)) {', ' return; /* Trace Buffer Full, Event Dropped ! */', ' }', - cond=cond, event_obj=event.api(event.QEMU_EVENT), size_str=sizestr) diff --git a/scripts/tracetool/backend/syslog.py b/scripts/tracetool/backend/syslog.py index 012970f6cc0..5a3a00fe310 100644 --- a/scripts/tracetool/backend/syslog.py +++ b/scripts/tracetool/backend/syslog.py @@ -12,8 +12,6 @@ __email__ = "stefanha@redhat.com" -import os.path - from tracetool import out @@ -43,7 +41,7 @@ def generate_h(event, group): ' }', cond=cond, event_lineno=event.lineno, - event_filename=os.path.relpath(event.filename), + event_filename=event.filename, name=event.name, fmt=event.fmt.rstrip("\n"), argnames=argnames) diff --git a/scripts/tracetool/format/log_stap.py b/scripts/tracetool/format/log_stap.py index 0b6549d534a..b49afababd6 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/vcpu.py b/scripts/tracetool/vcpu.py deleted file mode 100644 index d232cb1d065..00000000000 --- 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 35a64bb5019..b43b8ef75a6 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" \ + sed -e '/__ASSEMBLER__/,/__ASSEMBLER__/d' \ + "$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 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; do - cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" + 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,29 @@ 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/vmclock-abi.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 2d23e568007..f0927c544db 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 9c0e6b81f21..2335e25f94c 100755 --- a/scripts/vmstate-static-checker.py +++ b/scripts/vmstate-static-checker.py @@ -42,6 +42,7 @@ def check_fields_match(name, s_field, d_field): # Some fields changed names between qemu versions. This list # is used to allow such changes in each section / description. changed_names = { + 'acpi-ghes': ['ghes_addr_le', 'hw_error_le'], 'apic': ['timer', 'timer_expiry'], 'e1000': ['dev', 'parent_obj'], 'ehci': ['dev', 'pcidev'], @@ -90,6 +91,7 @@ def check_fields_match(name, s_field, d_field): 'mem_win_size', 'mig_mem_win_size', 'io_win_addr', 'mig_io_win_addr', 'io_win_size', 'mig_io_win_size'], + 'hpet': ['num_timers', 'num_timers_save'], } if not name in changed_names: diff --git a/scripts/xml-preprocess.py b/scripts/xml-preprocess.py old mode 100755 new mode 100644 diff --git a/scsi/meson.build b/scsi/meson.build index 53f3a1f7169..cdb91e11b0e 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-helper.c b/scsi/pr-manager-helper.c index 3be52a98d58..6b86f01b01f 100644 --- a/scsi/pr-manager-helper.c +++ b/scsi/pr-manager-helper.c @@ -300,7 +300,7 @@ static void pr_manager_helper_instance_init(Object *obj) } static void pr_manager_helper_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { PRManagerClass *prmgr_klass = PR_MANAGER_CLASS(klass); UserCreatableClass *uc_klass = USER_CREATABLE_CLASS(klass); diff --git a/scsi/pr-manager.c b/scsi/pr-manager.c index fb5fc297309..40e1210eb21 100644 --- a/scsi/pr-manager.c +++ b/scsi/pr-manager.c @@ -21,7 +21,7 @@ #include "qemu/module.h" #include "qapi/qapi-commands-block.h" -#define PR_MANAGER_PATH "/objects" +#define PR_MANAGER_PATH "objects" typedef struct PRManagerData { PRManager *pr_mgr; @@ -77,7 +77,7 @@ static const TypeInfo pr_manager_info = { .name = TYPE_PR_MANAGER, .class_size = sizeof(PRManagerClass), .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } @@ -135,7 +135,7 @@ PRManagerInfoList *qmp_query_pr_managers(Error **errp) { PRManagerInfoList *head = NULL; PRManagerInfoList **prev = &head; - Object *container = container_get(object_get_root(), PR_MANAGER_PATH); + Object *container = object_get_container(PR_MANAGER_PATH); object_child_foreach(container, query_one_pr_manager, &prev); return head; diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index c6c6347e9b6..b69dd982d6a 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -47,7 +47,7 @@ #include "qemu/log.h" #include "qemu/systemd.h" #include "qapi/util.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "io/channel-socket.h" #include "trace/control.h" #include "qemu-version.h" diff --git a/scsi/utils.c b/scsi/utils.c index 357b0366716..545956f4f95 100644 --- a/scsi/utils.c +++ b/scsi/utils.c @@ -587,20 +587,27 @@ int scsi_sense_from_errno(int errno_value, SCSISense *sense) return GOOD; case EDOM: return TASK_SET_FULL; +#if ENODEV != ENOMEDIUM + case ENODEV: + /* + * Some of the BSDs have ENODEV and ENOMEDIUM as synonyms. For + * everyone else, give a more severe sense code for ENODEV. + */ +#endif #ifdef CONFIG_LINUX /* These errno mapping are specific to Linux. For more information: * - scsi_check_sense and scsi_decide_disposition in drivers/scsi/scsi_error.c * - scsi_result_to_blk_status in drivers/scsi/scsi_lib.c * - blk_errors[] in block/blk-core.c */ + case EREMOTEIO: + *sense = SENSE_CODE(TARGET_FAILURE); + return CHECK_CONDITION; case EBADE: return RESERVATION_CONFLICT; case ENODATA: *sense = SENSE_CODE(READ_ERROR); return CHECK_CONDITION; - case EREMOTEIO: - *sense = SENSE_CODE(TARGET_FAILURE); - return CHECK_CONDITION; #endif case ENOMEDIUM: *sense = SENSE_CODE(NO_MEDIUM); diff --git a/semihosting/Kconfig b/semihosting/Kconfig index eaf3a20ef5b..fbe6ac87f9d 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 564fe17f75c..86e5260e504 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -166,6 +166,7 @@ static LayoutInfo common_semi_find_bases(CPUState *cs) #endif +#include "cpu.h" #include "common-semi-target.h" /* @@ -202,19 +203,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 @@ -226,7 +227,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; @@ -251,7 +252,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); @@ -322,7 +323,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)) { @@ -361,13 +362,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; @@ -587,7 +587,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); @@ -665,7 +665,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); @@ -725,6 +725,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, @@ -752,6 +755,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 8ca569735d0..56283b5c3c3 100644 --- a/semihosting/config.c +++ b/semihosting/config.c @@ -12,7 +12,7 @@ * 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 5d61e8207e2..c3683a15668 100644 --- a/semihosting/console.c +++ b/semihosting/console.c @@ -18,14 +18,15 @@ #include "qemu/osdep.h" #include "semihosting/semihost.h" #include "semihosting/console.h" +#include "exec/cpu-common.h" #include "exec/gdbstub.h" -#include "exec/exec-all.h" #include "qemu/log.h" #include "chardev/char.h" #include "chardev/char-fe.h" #include "qemu/main-loop.h" #include "qapi/error.h" #include "qemu/fifo8.h" +#include "hw/core/cpu.h" /* Access to this structure is protected by the BQL */ typedef struct SemihostingConsole { @@ -43,7 +44,7 @@ static SemihostingConsole console; static int console_can_read(void *opaque) { SemihostingConsole *c = opaque; - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); return (int)fifo8_num_free(&c->fifo); } @@ -58,7 +59,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++); } @@ -70,7 +71,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); } @@ -78,7 +79,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 acb86b50ddc..d3241434c51 100644 --- a/semihosting/guestfd.c +++ b/semihosting/guestfd.c @@ -12,10 +12,7 @@ #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 b07cbd980f2..f3d38dda91d 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -4,9 +4,16 @@ specific_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( )) specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SYSTEM_ONLY'], if_true: files( + 'uaccess.c', +)) + +common_ss.add(when: 'CONFIG_SEMIHOSTING', if_false: files('stubs-all.c')) +user_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files('user.c')) +system_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( 'config.c', 'console.c', - 'uaccess.c', +), if_false: files( + 'stubs-system.c', )) specific_ss.add(when: ['CONFIG_ARM_COMPATIBLE_SEMIHOSTING'], diff --git a/semihosting/stubs-all.c b/semihosting/stubs-all.c new file mode 100644 index 00000000000..c001c845749 --- /dev/null +++ b/semihosting/stubs-all.c @@ -0,0 +1,23 @@ +/* + * 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" + +/* Queries to config status default to off */ +bool semihosting_enabled(bool is_user) +{ + return false; +} + +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 00000000000..989789f373a --- /dev/null +++ b/semihosting/stubs-system.c @@ -0,0 +1,59 @@ +/* + * 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 */ } + }, +}; + +/* + * 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 d27574a1e2b..f6451d9bb0e 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "cpu.h" #include "gdbstub/syscalls.h" #include "semihosting/guestfd.h" @@ -15,7 +16,7 @@ #ifdef CONFIG_USER_ONLY #include "qemu.h" #else -#include "semihosting/softmmu-uaccess.h" +#include "semihosting/uaccess.h" #endif @@ -24,7 +25,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) { @@ -54,7 +55,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) { @@ -74,7 +75,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 || @@ -258,7 +259,7 @@ 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 = O_BINARY; @@ -287,6 +288,7 @@ static void host_open(CPUState *cs, gdb_syscall_complete_cb complete, ret = open(p, host_flags, mode); if (ret < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to open %s\n", __func__, p); complete(cs, -1, errno); } else { int guestfd = alloc_guestfd(); @@ -316,7 +318,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; @@ -337,7 +339,7 @@ static void host_read(CPUState *cs, gdb_syscall_complete_cb complete, 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; @@ -411,7 +413,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; @@ -440,7 +442,7 @@ static void host_stat(CPUState *cs, gdb_syscall_complete_cb complete, 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; @@ -459,7 +461,7 @@ 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; @@ -484,7 +486,7 @@ static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete, 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; @@ -502,7 +504,7 @@ static void host_system(CPUState *cs, gdb_syscall_complete_cb complete, 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; @@ -547,7 +549,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; @@ -605,7 +607,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; @@ -622,7 +624,7 @@ static void console_read(CPUState *cs, gdb_syscall_complete_cb complete, 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; diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c index 7505eb6d1ba..4554844e15b 100644 --- a/semihosting/uaccess.c +++ b/semihosting/uaccess.c @@ -8,10 +8,13 @@ */ #include "qemu/osdep.h" -#include "exec/exec-all.h" -#include "semihosting/softmmu-uaccess.h" +#include "accel/tcg/cpu-mmu-index.h" +#include "accel/tcg/probe.h" +#include "exec/target_page.h" +#include "exec/tlb-flags.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 +27,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) { @@ -72,16 +75,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/semihosting/user.c b/semihosting/user.c new file mode 100644 index 00000000000..98c144cb456 --- /dev/null +++ b/semihosting/user.c @@ -0,0 +1,21 @@ +/* + * Semihosting for user emulation + * + * Copyright (c) 2019 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "semihosting/semihost.h" + +bool semihosting_enabled(bool is_user) +{ + assert(is_user); + return true; +} + +SemihostingTarget semihosting_get_target(void) +{ + return SEMIHOSTING_TARGET_AUTO; +} diff --git a/softmmu/arch_init.c b/softmmu/arch_init.c deleted file mode 100644 index 79716f959ba..00000000000 --- a/softmmu/arch_init.c +++ /dev/null @@ -1,50 +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/module.h" -#include "sysemu/arch_init.h" - -#ifdef TARGET_SPARC -int graphic_width = 1024; -int graphic_height = 768; -int graphic_depth = 8; -#elif defined(TARGET_M68K) -int graphic_width = 800; -int graphic_height = 600; -int graphic_depth = 8; -#else -int graphic_width = 800; -int graphic_height = 600; -int graphic_depth = 32; -#endif - -const uint32_t arch_type = QEMU_ARCH; - -void qemu_init_arch_modules(void) -{ -#ifdef CONFIG_MODULES - module_init_info(qemu_modinfo); - module_allow_arch(TARGET_NAME); -#endif -} diff --git a/softmmu/cpu-throttle.c b/softmmu/cpu-throttle.c deleted file mode 100644 index d9bb30a223d..00000000000 --- 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 144e24829cd..00000000000 --- 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 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) { - 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/main.c b/softmmu/main.c deleted file mode 100644 index 694388bd7f7..00000000000 --- a/softmmu/main.c +++ /dev/null @@ -1,49 +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" - -#ifdef CONFIG_SDL -#include -#endif - -int qemu_default_main(void) -{ - int status; - - status = qemu_main_loop(); - qemu_cleanup(); - - return status; -} - -int (*qemu_main)(void) = qemu_default_main; - -int main(int argc, char **argv) -{ - qemu_init(argc, argv); - return qemu_main(); -} diff --git a/softmmu/meson.build b/softmmu/meson.build deleted file mode 100644 index c18b7ad7382..00000000000 --- a/softmmu/meson.build +++ /dev/null @@ -1,40 +0,0 @@ -specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: [files( - 'arch_init.c', - 'ioport.c', - 'memory.c', - 'physmem.c', - 'watchpoint.c', -)]) - -specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: [files( - 'icount.c', -)]) - -system_ss.add(files( - 'balloon.c', - 'bootdevice.c', - 'cpus.c', - 'cpu-throttle.c', - 'cpu-timers.c', - 'datadir.c', - 'dirtylimit.c', - 'dma-helpers.c', - 'globals.c', - 'memory_mapping.c', - 'qdev-monitor.c', - 'qtest.c', - 'rtc.c', - 'runstate-action.c', - 'runstate-hmp-cmds.c', - 'runstate.c', - 'tpm-hmp-cmds.c', - 'vl.c', -), sdl, libpmem, libdaxctl) - -if have_tpm - system_ss.add(files('tpm.c')) -endif - -system_ss.add(when: seccomp, if_true: files('qemu-seccomp.c')) -system_ss.add(when: fdt, if_true: files('device_tree.c')) -system_ss.add(when: 'CONFIG_LINUX', if_true: files('async-teardown.c')) diff --git a/softmmu/trace-events b/softmmu/trace-events deleted file mode 100644 index 22606dc27b3..00000000000 --- 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 2ad10115726..00000000000 --- a/softmmu/trace.h +++ /dev/null @@ -1 +0,0 @@ -#include "trace/trace-softmmu.h" diff --git a/softmmu/watchpoint.c b/softmmu/watchpoint.c deleted file mode 100644 index 53501633858..00000000000 --- a/softmmu/watchpoint.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * 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" - -/* Add a watchpoint. */ -int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, - int flags, CPUWatchpoint **watchpoint) -{ - CPUWatchpoint *wp; - vaddr in_page; - - /* forbid ranges which are empty or run off the end of the address space */ - if (len == 0 || (addr + len - 1) < addr) { - error_report("tried to set invalid watchpoint at %" - VADDR_PRIx ", len=%" VADDR_PRIu, addr, len); - return -EINVAL; - } - wp = g_malloc(sizeof(*wp)); - - wp->vaddr = addr; - wp->len = len; - wp->flags = flags; - - /* keep all GDB-injected watchpoints in front */ - if (flags & BP_GDB) { - QTAILQ_INSERT_HEAD(&cpu->watchpoints, wp, entry); - } else { - QTAILQ_INSERT_TAIL(&cpu->watchpoints, wp, entry); - } - - in_page = -(addr | TARGET_PAGE_MASK); - if (len <= in_page) { - tlb_flush_page(cpu, addr); - } else { - tlb_flush(cpu); - } - - if (watchpoint) { - *watchpoint = wp; - } - return 0; -} - -/* Remove a specific watchpoint. */ -int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, vaddr len, - int flags) -{ - CPUWatchpoint *wp; - - QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { - if (addr == wp->vaddr && len == wp->len - && flags == (wp->flags & ~BP_WATCHPOINT_HIT)) { - cpu_watchpoint_remove_by_ref(cpu, wp); - return 0; - } - } - return -ENOENT; -} - -/* Remove a specific watchpoint by reference. */ -void cpu_watchpoint_remove_by_ref(CPUState *cpu, CPUWatchpoint *watchpoint) -{ - QTAILQ_REMOVE(&cpu->watchpoints, watchpoint, entry); - - tlb_flush_page(cpu, watchpoint->vaddr); - - g_free(watchpoint); -} - -/* Remove all matching watchpoints. */ -void cpu_watchpoint_remove_all(CPUState *cpu, int mask) -{ - CPUWatchpoint *wp, *next; - - QTAILQ_FOREACH_SAFE(wp, &cpu->watchpoints, entry, next) { - if (wp->flags & mask) { - cpu_watchpoint_remove_by_ref(cpu, wp); - } - } -} - -#ifdef CONFIG_TCG - -/* - * Return true if this watchpoint address matches the specified - * access (ie the address range covered by the watchpoint overlaps - * partially or completely with the address range covered by the - * access). - */ -static inline bool watchpoint_address_matches(CPUWatchpoint *wp, - vaddr addr, vaddr len) -{ - /* - * We know the lengths are non-zero, but a little caution is - * required to avoid errors in the case where the range ends - * exactly at the top of the address space and so addr + len - * wraps round to zero. - */ - vaddr wpend = wp->vaddr + wp->len - 1; - vaddr addrend = addr + len - 1; - - return !(addr > wpend || wp->vaddr > addrend); -} - -/* Return flags for watchpoints that match addr + prot. */ -int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len) -{ - CPUWatchpoint *wp; - int ret = 0; - - QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) { - if (watchpoint_address_matches(wp, addr, len)) { - ret |= wp->flags; - } - } - return ret; -} - -/* Generate a debug exception if a watchpoint has been hit. */ -void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, - MemTxAttrs attrs, int flags, uintptr_t ra) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - CPUWatchpoint *wp; - - assert(tcg_enabled()); - if (cpu->watchpoint_hit) { - /* - * We re-entered the check after replacing the TB. - * Now raise the debug interrupt so that it will - * trigger after the current instruction. - */ - qemu_mutex_lock_iothread(); - cpu_interrupt(cpu, CPU_INTERRUPT_DEBUG); - qemu_mutex_unlock_iothread(); - return; - } - - if (cc->tcg_ops->adjust_watchpoint_address) { - /* this is currently used only by ARM BE32 */ - addr = cc->tcg_ops->adjust_watchpoint_address(cpu, addr, len); - } - - 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->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; - } - - 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_LAST_IO | CF_NOIRQ - | curr_cflags(cpu); - mmap_unlock(); - cpu_loop_exit_noexc(cpu); - } - } else { - wp->flags &= ~BP_WATCHPOINT_HIT; - } - } -} - -#endif /* CONFIG_TCG */ diff --git a/stats/stats-hmp-cmds.c b/stats/stats-hmp-cmds.c index 1f91bf8bd54..b93b471b1b2 100644 --- a/stats/stats-hmp-cmds.c +++ b/stats/stats-hmp-cmds.c @@ -11,7 +11,7 @@ #include "monitor/monitor.h" #include "qemu/cutils.h" #include "hw/core/cpu.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/error.h" static void print_stats_schema_value(Monitor *mon, StatsSchemaValue *value) diff --git a/stats/stats-qmp-cmds.c b/stats/stats-qmp-cmds.c index e214b964fda..884674ee323 100644 --- a/stats/stats-qmp-cmds.c +++ b/stats/stats-qmp-cmds.c @@ -6,7 +6,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/stats.h" +#include "system/stats.h" #include "qapi/qapi-commands-stats.h" #include "qemu/queue.h" #include "qapi/error.h" diff --git a/storage-daemon/meson.build b/storage-daemon/meson.build index 5e90cd32b40..5e61a9d1bdf 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_targetos, 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 f10c9494906..0427594c984 100644 --- a/storage-daemon/qapi/qapi-schema.json +++ b/storage-daemon/qapi/qapi-schema.json @@ -13,6 +13,30 @@ # the array type in the main schema, even if it is unused outside of the # storage daemon. +## +# = Introduction +# +# This manual describes the commands and events supported by the QEMU +# storage daemon QMP. +# +# For locating a particular item, please see the `qapi-qsd-index`. +# +# The following notation is used in examples: +# +# .. qmp-example:: +# +# -> ... text sent by client (commands) ... +# <- ... text sent by server (command responses and events) ... +# +# Example text is formatted for readability. However, in real +# protocol usage, its commonly emitted as a single line. +# +# Please refer to the +# :doc:`QEMU Machine Protocol Specification ` +# for the general format of commands, responses, and events. +## + + { 'include': '../../qapi/pragma.json' } # Documentation generated with qapi-gen.py is in source order, with diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c index 0e9354faa65..eb725613582 100644 --- a/storage-daemon/qemu-storage-daemon.c +++ b/storage-daemon/qemu-storage-daemon.c @@ -38,8 +38,8 @@ #include "qapi/qapi-visit-block-core.h" #include "qapi/qapi-visit-block-export.h" #include "qapi/qapi-visit-control.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qemu/help-texts.h" @@ -58,7 +58,7 @@ #include "storage-daemon/qapi/qapi-commands.h" #include "storage-daemon/qapi/qapi-init-commands.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "trace/control.h" static const char *pid_file; diff --git a/stubs/blk-commit-all.c b/stubs/blk-commit-all.c index e156c57f8d7..76b08275a5c 100644 --- a/stubs/blk-commit-all.c +++ b/stubs/blk-commit-all.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" int blk_commit_all(void) { diff --git a/stubs/blk-exp-close-all.c b/stubs/blk-exp-close-all.c index 1c713167639..2f68e06d7d0 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/change-state-handler.c b/stubs/change-state-handler.c index d1ed46bfb0f..002d248abf2 100644 --- a/stubs/change-state-handler.c +++ b/stubs/change-state-handler.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, void *opaque) diff --git a/stubs/colo.c b/stubs/colo.c deleted file mode 100644 index 08c9f982d58..00000000000 --- a/stubs/colo.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu/notify.h" -#include "net/colo-compare.h" -#include "migration/colo.h" -#include "migration/migration.h" -#include "qemu/error-report.h" -#include "qapi/qapi-commands-migration.h" - -void colo_shutdown(void) -{ -} - -int coroutine_fn colo_incoming_co(void) -{ - return 0; -} - -void colo_checkpoint_delay_set(void) -{ -} - -void migrate_start_colo_process(MigrationState *s) -{ - error_report("Impossible happened: trying to start COLO when COLO " - "module is not built in"); - abort(); -} - -bool migration_in_colo_state(void) -{ - return false; -} - -bool migration_incoming_in_colo_state(void) -{ - return false; -} diff --git a/stubs/cpu-get-clock.c b/stubs/cpu-get-clock.c index 9e924048169..53b9c83d768 100644 --- a/stubs/cpu-get-clock.c +++ b/stubs/cpu-get-clock.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "qemu/main-loop.h" int64_t cpu_get_clock(void) diff --git a/stubs/cpu-synchronize-state.c b/stubs/cpu-synchronize-state.c index d9211da66ce..2ed09ff3ed6 100644 --- a/stubs/cpu-synchronize-state.c +++ b/stubs/cpu-synchronize-state.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" void cpu_synchronize_state(CPUState *cpu) { diff --git a/stubs/cpus-get-virtual-clock.c b/stubs/cpus-get-virtual-clock.c deleted file mode 100644 index fd447d53f3c..00000000000 --- 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 00000000000..0b83a925334 --- /dev/null +++ b/stubs/cpus-virtual-clock.c @@ -0,0 +1,13 @@ +#include "qemu/osdep.h" +#include "system/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/dump.c b/stubs/dump.c index 1f28ec2be3f..df7897b72b1 100644 --- a/stubs/dump.c +++ b/stubs/dump.c @@ -12,7 +12,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/dump-arch.h" +#include "system/dump-arch.h" int cpu_get_dump_info(ArchDumpInfo *info, const struct GuestPhysBlockList *guest_phys_blocks) diff --git a/stubs/fdset.c b/stubs/fdset.c index 56b3663d588..2950fd91fd3 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) +void monitor_fdset_dup_fd_remove(int dupfd) { - 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 2b7aee50d30..580e20702b2 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/get-vm-name.c b/stubs/get-vm-name.c index 0906303f730..4cfac484e53 100644 --- a/stubs/get-vm-name.c +++ b/stubs/get-vm-name.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" +#include "system/system.h" const char *qemu_get_vm_name(void) { 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 6df8c2bf7d4..ceb73b4fc2c 100644 --- a/stubs/icount.c +++ b/stubs/icount.c @@ -1,40 +1,23 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/cpu-timers.h" +#include "exec/icount.h" /* 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 5b45b7fc8b9..6050c081f53 100644 --- a/stubs/iothread-lock.c +++ b/stubs/iothread-lock.c @@ -1,15 +1,36 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" -bool qemu_mutex_iothread_locked(void) +static bool bql_is_locked = false; +static uint32_t bql_unlock_blocked; + +bool bql_locked(void) { - return false; + return bql_is_locked; } -void qemu_mutex_lock_iothread_impl(const char *file, int line) +void rust_bql_mock_lock(void) { + bql_is_locked = true; } -void qemu_mutex_unlock_iothread(void) +void bql_lock_impl(const char *file, int line) { } + +void bql_unlock(void) +{ + assert(!bql_unlock_blocked); +} + +void bql_block_unlock(bool increase) +{ + uint32_t new_value; + + assert(bql_locked()); + + /* check for overflow! */ + new_value = bql_unlock_blocked + increase - !increase; + assert((new_value > bql_unlock_blocked) == increase); + bql_unlock_blocked = new_value; +} diff --git a/stubs/isa-bus.c b/stubs/isa-bus.c deleted file mode 100644 index 522f448997d..00000000000 --- 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 ef6e39a64d8..cef046e6854 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -1,68 +1,99 @@ -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')) -stub_ss.add(files('graph-lock.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('colo.c')) -stub_ss.add(files('colo-compare.c')) -stub_ss.add(files('vmstate.c')) -stub_ss.add(files('vm-stop.c')) -stub_ss.add(files('win32-kbd-hook.c')) -stub_ss.add(files('cpu-synchronize-state.c')) + +if have_block + 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 builds. + if not have_system + stub_ss.add(files('qdev.c')) + endif + + stub_ss.add(files('monitor-internal.c')) +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')) - stub_ss.add(files('virtio-md-pci.c')) -else - stub_ss.add(files('qdev.c')) + stub_ss.add(files('monitor-arm-gic.c')) + stub_ss.add(files('monitor-i386-rtc.c')) + stub_ss.add(files('monitor-i386-sev.c')) + stub_ss.add(files('monitor-i386-sgx.c')) + stub_ss.add(files('monitor-i386-xen.c')) + stub_ss.add(files('monitor-cpu.c')) + stub_ss.add(files('monitor-cpu-s390x.c')) + stub_ss.add(files('monitor-cpu-s390x-kvm.c')) +endif + +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 -stub_ss.add(files('semihost-all.c')) -stub_ss.add(when: 'CONFIG_VFIO_USER_SERVER', if_false: files('vfio-user-obj.c')) diff --git a/stubs/migr-blocker.c b/stubs/migr-blocker.c index 5676a2f93c8..11cbff268fd 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 5412429ea86..00000000000 --- 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-arm-gic.c b/stubs/monitor-arm-gic.c new file mode 100644 index 00000000000..b3429243ef8 --- /dev/null +++ b/stubs/monitor-arm-gic.c @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-arm.h" + + +GICCapabilityList *qmp_query_gic_capabilities(Error **errp) +{ + error_setg(errp, "GIC hardware is not available on this target"); + return NULL; +} diff --git a/stubs/monitor-core.c b/stubs/monitor-core.c index afa477aae65..1894cdfe1f8 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-cpu-s390x-kvm.c b/stubs/monitor-cpu-s390x-kvm.c new file mode 100644 index 00000000000..8683dd2d4c6 --- /dev/null +++ b/stubs/monitor-cpu-s390x-kvm.c @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine-s390x.h" + +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) +{ + error_setg(errp, "CPU topology change is not supported on this target"); +} + +CpuPolarizationInfo *qmp_query_s390x_cpu_polarization(Error **errp) +{ + error_setg(errp, "CPU polarization is not supported on this target"); + return NULL; +} diff --git a/stubs/monitor-cpu-s390x.c b/stubs/monitor-cpu-s390x.c new file mode 100644 index 00000000000..71e794482b5 --- /dev/null +++ b/stubs/monitor-cpu-s390x.c @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" + +CpuModelCompareInfo * +qmp_query_cpu_model_comparison(CpuModelInfo *infoa, + CpuModelInfo *infob, + Error **errp) +{ + error_setg(errp, "CPU model comparison is not supported on this target"); + return NULL; +} + +CpuModelBaselineInfo * +qmp_query_cpu_model_baseline(CpuModelInfo *infoa, + CpuModelInfo *infob, + Error **errp) +{ + error_setg(errp, "CPU model baseline is not supported on this target"); + return NULL; +} diff --git a/stubs/monitor-cpu.c b/stubs/monitor-cpu.c new file mode 100644 index 00000000000..a8c7ee89b9d --- /dev/null +++ b/stubs/monitor-cpu.c @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" + +CpuModelExpansionInfo * +qmp_query_cpu_model_expansion(CpuModelExpansionType type, + CpuModelInfo *model, + Error **errp) +{ + error_setg(errp, "CPU model expansion is not supported on this target"); + return NULL; +} + +CpuDefinitionInfoList * +qmp_query_cpu_definitions(Error **errp) +{ + error_setg(errp, "CPU model definitions are not supported on this target"); + return NULL; +} diff --git a/stubs/monitor-i386-rtc.c b/stubs/monitor-i386-rtc.c new file mode 100644 index 00000000000..8420d7c93c2 --- /dev/null +++ b/stubs/monitor-i386-rtc.c @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-i386.h" + +void qmp_rtc_reset_reinjection(Error **errp) +{ + error_setg(errp, + "RTC interrupt reinjection backlog reset is not available for" + "this machine"); +} diff --git a/stubs/monitor-i386-sev.c b/stubs/monitor-i386-sev.c new file mode 100644 index 00000000000..d4f024128ca --- /dev/null +++ b/stubs/monitor-i386-sev.c @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-i386.h" + +SevInfo *qmp_query_sev(Error **errp) +{ + error_setg(errp, "SEV is not available in this QEMU"); + return NULL; +} + +SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp) +{ + error_setg(errp, "SEV is not available in this QEMU"); + return NULL; +} + +SevCapability *qmp_query_sev_capabilities(Error **errp) +{ + error_setg(errp, "SEV is not available in this QEMU"); + return NULL; +} + +void qmp_sev_inject_launch_secret(const char *packet_header, const char *secret, + bool has_gpa, uint64_t gpa, Error **errp) +{ + error_setg(errp, "SEV is not available in this QEMU"); +} + +SevAttestationReport *qmp_query_sev_attestation_report(const char *mnonce, + Error **errp) +{ + error_setg(errp, "SEV is not available in this QEMU"); + return NULL; +} diff --git a/stubs/monitor-i386-sgx.c b/stubs/monitor-i386-sgx.c new file mode 100644 index 00000000000..00e081d52dd --- /dev/null +++ b/stubs/monitor-i386-sgx.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-i386.h" + +SgxInfo *qmp_query_sgx(Error **errp) +{ + error_setg(errp, "SGX support is not compiled in"); + return NULL; +} + +SgxInfo *qmp_query_sgx_capabilities(Error **errp) +{ + error_setg(errp, "SGX support is not compiled in"); + return NULL; +} diff --git a/stubs/monitor-i386-xen.c b/stubs/monitor-i386-xen.c new file mode 100644 index 00000000000..95b826f9795 --- /dev/null +++ b/stubs/monitor-i386-xen.c @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-i386.h" + +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"); +} diff --git a/stubs/monitor-internal.c b/stubs/monitor-internal.c new file mode 100644 index 00000000000..4fece49d531 --- /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 20786ac4ffb..00000000000 --- 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 a8932fa9325..00000000000 --- 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 6869f6f90a2..7e957b3e524 100644 --- a/stubs/qdev.c +++ b/stubs/qdev.c @@ -20,9 +20,3 @@ void qapi_event_send_device_deleted(const char *device, { /* Nothing to do. */ } - -void qapi_event_send_device_unplug_guest_error(const char *device, - const char *path) -{ - /* Nothing to do. */ -} diff --git a/stubs/qemu-timer-notify-cb.c b/stubs/qemu-timer-notify-cb.c index 845e46f8e03..b57b983c6f4 100644 --- a/stubs/qemu-timer-notify-cb.c +++ b/stubs/qemu-timer-notify-cb.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "qemu/main-loop.h" void qemu_timer_notify_cb(void *opaque, QEMUClockType type) diff --git a/stubs/qmp-command-available.c b/stubs/qmp-command-available.c index 46540af7bfc..8851faced13 100644 --- a/stubs/qmp-command-available.c +++ b/stubs/qmp-command-available.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "qapi/qmp/dispatch.h" +#include "qapi/qmp-registry.h" bool qmp_command_available(const QmpCommand *cmd, Error **errp) { diff --git a/stubs/qmp-quit.c b/stubs/qmp-quit.c index a3ff47f7bdb..8fb523e9050 100644 --- a/stubs/qmp-quit.c +++ b/stubs/qmp-quit.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" #include "qapi/qapi-commands-control.h" -#include "qapi/qmp/dispatch.h" +#include "qapi/qmp-registry.h" void qmp_quit(Error **errp) { diff --git a/stubs/qmp_memory_device.c b/stubs/qmp_memory_device.c deleted file mode 100644 index e75cac62dc6..00000000000 --- 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 4666a49d7d1..6c397259274 100644 --- a/stubs/qtest.c +++ b/stubs/qtest.c @@ -9,17 +9,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" /* 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/ram-block.c b/stubs/ram-block.c index 108197683bb..e88fab31a5d 100644 --- a/stubs/ram-block.c +++ b/stubs/ram-block.c @@ -1,7 +1,7 @@ #include "qemu/osdep.h" #include "exec/ramlist.h" #include "exec/cpu-common.h" -#include "exec/memory.h" +#include "system/memory.h" void *qemu_ram_get_host_addr(RAMBlock *rb) { diff --git a/stubs/ramfb.c b/stubs/ramfb.c deleted file mode 100644 index 48143f33542..00000000000 --- 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 00000000000..439d97e4a87 --- /dev/null +++ b/stubs/replay-mode.c @@ -0,0 +1,4 @@ +#include "qemu/osdep.h" +#include "system/replay.h" + +ReplayMode replay_mode; diff --git a/stubs/replay-tools.c b/stubs/replay-tools.c index 3e8ca3212d9..c537485f401 100644 --- a/stubs/replay-tools.c +++ b/stubs/replay-tools.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "block/aio.h" bool replay_events_enabled(void) diff --git a/stubs/replay.c b/stubs/replay.c index 42c92e4acb8..b4dd6a566e8 100644 --- a/stubs/replay.c +++ b/stubs/replay.c @@ -1,8 +1,6 @@ #include "qemu/osdep.h" #include "exec/replay-core.h" -ReplayMode replay_mode; - void replay_finish(void) { } diff --git a/stubs/runstate-check.c b/stubs/runstate-check.c index 2ccda2b70fa..c47abdf84b7 100644 --- a/stubs/runstate-check.c +++ b/stubs/runstate-check.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" bool runstate_check(RunState state) { return state == RUN_STATE_PRELAUNCH; diff --git a/stubs/semihost-all.c b/stubs/semihost-all.c deleted file mode 100644 index a2a1fc9c6fe..00000000000 --- a/stubs/semihost-all.c +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Semihosting Stubs for all targets - * - * Copyright (c) 2023 Linaro Ltd - * - * Stubs for all targets that don't actually do semihosting. - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "semihosting/semihost.h" - -SemihostingTarget semihosting_get_target(void) -{ - return SEMIHOSTING_TARGET_AUTO; -} diff --git a/stubs/semihost.c b/stubs/semihost.c deleted file mode 100644 index aad7a703532..00000000000 --- a/stubs/semihost.c +++ /dev/null @@ -1,65 +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; -} - -/* - * 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 ac07b19064c..35a0a342772 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/usb-dev-stub.c b/stubs/usb-dev-stub.c deleted file mode 100644 index aa557692b71..00000000000 --- 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/vm-stop.c b/stubs/vm-stop.c index 7f8a9da8a59..e139aabf7be 100644 --- a/stubs/vm-stop.c +++ b/stubs/vm-stop.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" void qemu_system_vmstop_request_prepare(void) { abort(); diff --git a/stubs/vmstate.c b/stubs/vmstate.c index 8513d9204e4..c190762d7c1 100644 --- a/stubs/vmstate.c +++ b/stubs/vmstate.c @@ -1,5 +1,7 @@ #include "qemu/osdep.h" #include "migration/vmstate.h" +#include "qapi/qapi-types-migration.h" +#include "migration/client-options.h" int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id, @@ -21,3 +23,8 @@ bool vmstate_check_only_migratable(const VMStateDescription *vmsd) { return true; } + +MigMode migrate_mode(void) +{ + return MIG_MODE_NORMAL; +} diff --git a/stubs/xen-hw-stub.c b/stubs/xen-hw-stub.c index 7d7ffe83a93..6cf0e9a4c1d 100644 --- a/stubs/xen-hw-stub.c +++ b/stubs/xen-hw-stub.c @@ -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 adca0266be6..f4281934ce1 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -6,3 +6,17 @@ /keycodemapdb /libvfio-user /slirp +/anyhow-1.0.98 +/arbitrary-int-1.2.7 +/bilge-0.2.0 +/bilge-impl-0.2.0 +/either-1.12.0 +/foreign-0.3.1 +/itertools-0.11.0 +/libc-0.2.162 +/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 diff --git a/subprojects/anyhow-1-rs.wrap b/subprojects/anyhow-1-rs.wrap new file mode 100644 index 00000000000..a69a3645b49 --- /dev/null +++ b/subprojects/anyhow-1-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = anyhow-1.0.98 +source_url = https://crates.io/api/v1/crates/anyhow/1.0.98/download +source_filename = anyhow-1.0.98.tar.gz +source_hash = e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487 +#method = cargo +patch_directory = anyhow-1-rs diff --git a/subprojects/arbitrary-int-1-rs.wrap b/subprojects/arbitrary-int-1-rs.wrap new file mode 100644 index 00000000000..a1838b20b0f --- /dev/null +++ b/subprojects/arbitrary-int-1-rs.wrap @@ -0,0 +1,10 @@ +[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 + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/berkeley-softfloat-3.wrap b/subprojects/berkeley-softfloat-3.wrap index a8fd87740b1..c3e356d42fb 100644 --- a/subprojects/berkeley-softfloat-3.wrap +++ b/subprojects/berkeley-softfloat-3.wrap @@ -1,5 +1,5 @@ [wrap-git] -url = https://gitlab.com/qemu-project/berkeley-softfloat-3 +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 index c86dc078a8e..b8b12e76294 100644 --- a/subprojects/berkeley-testfloat-3.wrap +++ b/subprojects/berkeley-testfloat-3.wrap @@ -1,5 +1,5 @@ [wrap-git] -url = https://gitlab.com/qemu-project/berkeley-testfloat-3 +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 00000000000..900bb1497b9 --- /dev/null +++ b/subprojects/bilge-0.2-rs.wrap @@ -0,0 +1,10 @@ +[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 + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/bilge-impl-0.2-rs.wrap b/subprojects/bilge-impl-0.2-rs.wrap new file mode 100644 index 00000000000..4f84eca1ccd --- /dev/null +++ b/subprojects/bilge-impl-0.2-rs.wrap @@ -0,0 +1,10 @@ +[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 + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/either-1-rs.wrap b/subprojects/either-1-rs.wrap new file mode 100644 index 00000000000..352e11cfee6 --- /dev/null +++ b/subprojects/either-1-rs.wrap @@ -0,0 +1,10 @@ +[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 + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/foreign-0.3-rs.wrap b/subprojects/foreign-0.3-rs.wrap new file mode 100644 index 00000000000..0d218ec2c25 --- /dev/null +++ b/subprojects/foreign-0.3-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = foreign-0.3.1 +source_url = https://crates.io/api/v1/crates/foreign/0.3.1/download +source_filename = foreign-0.3.1.tar.gz +source_hash = 17ca1b5be8c9d320daf386f1809c7acc0cb09accbae795c2001953fa50585846 +#method = cargo +patch_directory = foreign-0.3-rs diff --git a/subprojects/itertools-0.11-rs.wrap b/subprojects/itertools-0.11-rs.wrap new file mode 100644 index 00000000000..ee12d0053bc --- /dev/null +++ b/subprojects/itertools-0.11-rs.wrap @@ -0,0 +1,10 @@ +[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 + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/libc-0.2-rs.wrap b/subprojects/libc-0.2-rs.wrap new file mode 100644 index 00000000000..bbe08f87883 --- /dev/null +++ b/subprojects/libc-0.2-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = libc-0.2.162 +source_url = https://crates.io/api/v1/crates/libc/0.2.162/download +source_filename = libc-0.2.162.tar.gz +source_hash = 18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398 +#method = cargo +patch_directory = libc-0.2-rs diff --git a/subprojects/libvduse/libvduse.c b/subprojects/libvduse/libvduse.c index 377959a0b4f..21ffbb5b8d1 100644 --- a/subprojects/libvduse/libvduse.c +++ b/subprojects/libvduse/libvduse.c @@ -1031,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/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index 0469a50101d..9c630c2170d 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -43,6 +43,8 @@ #include #include #include +#include +#include #ifdef __NR_userfaultfd #include @@ -161,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 @@ -194,30 +197,58 @@ 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) { - unsigned 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. */ @@ -239,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) { @@ -322,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; } @@ -385,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) { @@ -610,28 +863,13 @@ 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) { 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; /* @@ -682,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; @@ -708,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); @@ -742,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, @@ -837,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; - unsigned int i; - bool found = false; + unsigned int idx; + VuDevRegion *r; if (vmsg->fd_num > 1) { vmsg_close_fds(vmsg); @@ -865,35 +1039,31 @@ 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); - } + 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; + } - /* - * 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--; + /* + * 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); - found = true; + munmap((void *)(uintptr_t)r->mmap_addr, r->size + r->mmap_offset); - /* Continue the search for eventual duplicates. */ - } - } - - if (!found) { - vu_panic(dev, "Specified region not found\n"); - } + 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); @@ -901,139 +1071,59 @@ vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { } static bool -vu_set_mem_table_exec_postcopy(VuDev *dev, VhostUserMsg *vmsg) +vu_get_shared_object(VuDev *dev, VhostUserMsg *vmsg) { - unsigned 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]); - } - - /* 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; + 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]); } - - /* 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; + if (dmabuf_fd != -1) { + DPRINT("dmabuf_fd found for requested UUID\n"); + vmsg->fds[fd_num++] = dmabuf_fd; } + vmsg->fd_num = fd_num; - /* 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) { - unsigned 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; + vu_remove_all_mem_regs(dev); - if (m) { - munmap(m, r->size + r->mmap_offset); - } + DPRINT("Nregions: %u\n", memory->nregions); + for (i = 0; i < memory->nregions; i++) { + _vu_add_mem_reg(dev, &memory->regions[i], vmsg->fds[i]); + close(vmsg->fds[i]); } - dev->nregions = memory->nregions; if (dev->postcopy_listening) { - return vu_set_mem_table_exec_postcopy(dev, vmsg); - } - - 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]; + /* 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; + } - 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); + /* + * 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; } - close(vmsg->fds[i]); + /* 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++) { @@ -1403,6 +1493,105 @@ bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd, 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) { @@ -1485,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; } @@ -1943,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); @@ -1991,14 +2193,7 @@ vu_deinit(VuDev *dev) { 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]; @@ -2050,6 +2245,8 @@ vu_deinit(VuDev *dev) free(dev->vq); dev->vq = NULL; + free(dev->regions); + dev->regions = NULL; } bool @@ -2084,9 +2281,17 @@ vu_init(VuDev *dev, 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; } @@ -2253,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; } @@ -2369,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; } @@ -2409,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; } @@ -2735,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; } @@ -2893,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; } @@ -2923,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 708370c5f5f..2ffc58c11b1 100644 --- a/subprojects/libvhost-user/libvhost-user.h +++ b/subprojects/libvhost-user/libvhost-user.h @@ -31,10 +31,12 @@ #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) @@ -64,7 +66,9 @@ enum VhostUserProtocolFeature { 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 }; @@ -109,6 +113,7 @@ 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; @@ -119,6 +124,9 @@ typedef enum VhostUserBackendRequest { 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; @@ -172,11 +180,13 @@ typedef struct VhostUserInflight { uint16_t queue_size; } VhostUserInflight; -#if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) -# define VU_PACKED __attribute__((gcc_struct, packed)) -#else -# define VU_PACKED __attribute__((packed)) -#endif +#define UUID_LEN 16 + +typedef struct VhostUserShared { + unsigned char uuid[UUID_LEN]; +} VhostUserShared; + +#define VU_PACKED __attribute__((packed)) typedef struct VhostUserMsg { int request; @@ -199,6 +209,7 @@ typedef struct VhostUserMsg { VhostUserConfig config; VhostUserVringArea area; VhostUserInflight inflight; + VhostUserShared object; } payload; int fds[VHOST_MEMORY_BASELINE_NREGIONS]; @@ -232,6 +243,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 +270,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); @@ -382,7 +396,7 @@ 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; @@ -541,6 +555,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 diff --git a/subprojects/packagefiles/anyhow-1-rs/meson.build b/subprojects/packagefiles/anyhow-1-rs/meson.build new file mode 100644 index 00000000000..348bab98b9f --- /dev/null +++ b/subprojects/packagefiles/anyhow-1-rs/meson.build @@ -0,0 +1,33 @@ +project('anyhow-1-rs', 'rust', + meson_version: '>=1.5.0', + version: '1.0.98', + license: 'MIT OR Apache-2.0', + default_options: []) + +rustc = meson.get_compiler('rust') + +rust_args = ['--cap-lints', 'allow'] +rust_args += ['--cfg', 'feature="std"'] +if rustc.version().version_compare('<1.65.0') + error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.65.0') +endif +rust_args += [ '--cfg', 'std_backtrace' ] # >= 1.65.0 +if rustc.version().version_compare('<1.81.0') + rust_args += [ '--cfg', 'anyhow_no_core_error' ] +endif + +_anyhow_rs = static_library( + 'anyhow', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2018', 'build.rust_std=2018'], + rust_abi: 'rust', + rust_args: rust_args, + dependencies: [], +) + +anyhow_dep = declare_dependency( + link_with: _anyhow_rs, +) + +meson.override_dependency('anyhow-1-rs', anyhow_dep) 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 00000000000..00733d1faab --- /dev/null +++ b/subprojects/packagefiles/arbitrary-int-1-rs/meson.build @@ -0,0 +1,21 @@ +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_args: ['--cap-lints', 'allow'], + 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/bilge-0.2-rs/meson.build b/subprojects/packagefiles/bilge-0.2-rs/meson.build new file mode 100644 index 00000000000..ce13d0fe80f --- /dev/null +++ b/subprojects/packagefiles/bilge-0.2-rs/meson.build @@ -0,0 +1,31 @@ +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', + rust_args: ['--cap-lints', 'allow'], + 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 00000000000..42b03dcd53c --- /dev/null +++ b/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build @@ -0,0 +1,47 @@ +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: [ + '--cap-lints', 'allow', + '--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/either-1-rs/meson.build b/subprojects/packagefiles/either-1-rs/meson.build new file mode 100644 index 00000000000..04c96cc5fb3 --- /dev/null +++ b/subprojects/packagefiles/either-1-rs/meson.build @@ -0,0 +1,26 @@ +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: [ + '--cap-lints', 'allow', + '--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/foreign-0.3-rs/meson.build b/subprojects/packagefiles/foreign-0.3-rs/meson.build new file mode 100644 index 00000000000..0901c02c527 --- /dev/null +++ b/subprojects/packagefiles/foreign-0.3-rs/meson.build @@ -0,0 +1,26 @@ +project('foreign-0.3-rs', 'rust', + meson_version: '>=1.5.0', + version: '0.2.0', + license: 'MIT OR Apache-2.0', + default_options: []) + +subproject('libc-0.2-rs', required: true) +libc_rs = dependency('libc-0.2-rs') + +_foreign_rs = static_library( + 'foreign', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + rust_args: [ + '--cap-lints', 'allow', + ], + dependencies: [libc_rs], +) + +foreign_dep = declare_dependency( + link_with: _foreign_rs, +) + +meson.override_dependency('foreign-0.3-rs', foreign_dep) 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 00000000000..2a3fbe9ee5a --- /dev/null +++ b/subprojects/packagefiles/itertools-0.11-rs/meson.build @@ -0,0 +1,32 @@ +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: [ + '--cap-lints', 'allow', + '--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/libc-0.2-rs/meson.build b/subprojects/packagefiles/libc-0.2-rs/meson.build new file mode 100644 index 00000000000..ac4f80dba98 --- /dev/null +++ b/subprojects/packagefiles/libc-0.2-rs/meson.build @@ -0,0 +1,37 @@ +project('libc-0.2-rs', 'rust', + meson_version: '>=1.5.0', + version: '0.2.162', + license: 'MIT OR Apache-2.0', + default_options: []) + +_libc_rs = static_library( + 'libc', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2015', 'build.rust_std=2015'], + rust_abi: 'rust', + rust_args: [ + '--cap-lints', 'allow', + '--cfg', 'freebsd11', + '--cfg', 'libc_priv_mod_use', + '--cfg', 'libc_union', + '--cfg', 'libc_const_size_of', + '--cfg', 'libc_align', + '--cfg', 'libc_int128', + '--cfg', 'libc_core_cvoid', + '--cfg', 'libc_packedN', + '--cfg', 'libc_cfg_target_vendor', + '--cfg', 'libc_non_exhaustive', + '--cfg', 'libc_long_array', + '--cfg', 'libc_ptr_addr_of', + '--cfg', 'libc_underscore_const_names', + '--cfg', 'libc_const_extern_fn', + ], + dependencies: [], +) + +libc_dep = declare_dependency( + link_with: _libc_rs, +) + +meson.override_dependency('libc-0.2-rs', libc_dep) 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 00000000000..10c2741085c --- /dev/null +++ b/subprojects/packagefiles/proc-macro-error-1-rs/meson.build @@ -0,0 +1,42 @@ +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: [ + '--cap-lints', 'allow', + '--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 00000000000..c4c4c5e397c --- /dev/null +++ b/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build @@ -0,0 +1,34 @@ +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: [ + '--cap-lints', 'allow', + '--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 00000000000..5759df3ecc9 --- /dev/null +++ b/subprojects/packagefiles/proc-macro2-1-rs/meson.build @@ -0,0 +1,35 @@ +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: [ + '--cap-lints', 'allow', + '--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 00000000000..bf41fad99bb --- /dev/null +++ b/subprojects/packagefiles/quote-1-rs/meson.build @@ -0,0 +1,31 @@ +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: [ + '--cap-lints', 'allow', + '--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 00000000000..a0094174084 --- /dev/null +++ b/subprojects/packagefiles/syn-2-rs/meson.build @@ -0,0 +1,43 @@ +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: [ + '--cap-lints', 'allow', + '--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 00000000000..11a5dab97df --- /dev/null +++ b/subprojects/packagefiles/unicode-ident-1-rs/meson.build @@ -0,0 +1,22 @@ +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', + rust_args: ['--cap-lints', 'allow'], + 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 00000000000..59f892f7825 --- /dev/null +++ b/subprojects/proc-macro-error-1-rs.wrap @@ -0,0 +1,10 @@ +[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 + +# bump this version number on every change to meson.build or the patches: +# v2 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 00000000000..5aeb224a103 --- /dev/null +++ b/subprojects/proc-macro-error-attr-1-rs.wrap @@ -0,0 +1,10 @@ +[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 + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/proc-macro2-1-rs.wrap b/subprojects/proc-macro2-1-rs.wrap new file mode 100644 index 00000000000..6c9369f0df3 --- /dev/null +++ b/subprojects/proc-macro2-1-rs.wrap @@ -0,0 +1,10 @@ +[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 + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/quote-1-rs.wrap b/subprojects/quote-1-rs.wrap new file mode 100644 index 00000000000..8b721dfa00b --- /dev/null +++ b/subprojects/quote-1-rs.wrap @@ -0,0 +1,10 @@ +[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 + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/slirp.wrap b/subprojects/slirp.wrap index 08291a4cf99..a93b0489621 100644 --- a/subprojects/slirp.wrap +++ b/subprojects/slirp.wrap @@ -1,5 +1,5 @@ [wrap-git] -url = https://gitlab.freedesktop.org/slirp/libslirp +url = https://gitlab.freedesktop.org/slirp/libslirp.git revision = 26be815b86e8d49add8c9a8b320239b9594ff03d [provide] diff --git a/subprojects/syn-2-rs.wrap b/subprojects/syn-2-rs.wrap new file mode 100644 index 00000000000..d79cf750fb4 --- /dev/null +++ b/subprojects/syn-2-rs.wrap @@ -0,0 +1,10 @@ +[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 + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/subprojects/unicode-ident-1-rs.wrap b/subprojects/unicode-ident-1-rs.wrap new file mode 100644 index 00000000000..50988f612e2 --- /dev/null +++ b/subprojects/unicode-ident-1-rs.wrap @@ -0,0 +1,10 @@ +[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 + +# bump this version number on every change to meson.build or the patches: +# v2 diff --git a/system/arch_init.c b/system/arch_init.c new file mode 100644 index 00000000000..e85736884c9 --- /dev/null +++ b/system/arch_init.c @@ -0,0 +1,30 @@ +/* + * 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 "system/arch_init.h" + +bool qemu_arch_available(unsigned qemu_arch_mask) +{ + return qemu_arch_mask & QEMU_ARCH; +} diff --git a/softmmu/async-teardown.c b/system/async-teardown.c similarity index 76% rename from softmmu/async-teardown.c rename to system/async-teardown.c index 62cdeb0f20a..9148ee8d041 100644 --- a/softmmu/async-teardown.c +++ b/system/async-teardown.c @@ -26,40 +26,6 @@ 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. */ @@ -85,9 +51,8 @@ static int async_teardown_fn(void *arg) /* * 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(); + qemu_close_all_open_fd(NULL, 0); /* Set up a handler for SIGHUP and unblock SIGHUP. */ sigaction(SIGHUP, &sa, NULL); @@ -121,10 +86,7 @@ static void *new_stack_for_clone(void) /* 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; } diff --git a/softmmu/balloon.c b/system/balloon.c similarity index 90% rename from softmmu/balloon.c rename to system/balloon.c index e0e8969a4b9..311fa5058b9 100644 --- a/softmmu/balloon.c +++ b/system/balloon.c @@ -26,8 +26,8 @@ #include "qemu/osdep.h" #include "qemu/atomic.h" -#include "sysemu/kvm.h" -#include "sysemu/balloon.h" +#include "system/kvm.h" +#include "system/balloon.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" #include "qapi/qmp/qerror.h" @@ -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 95% rename from softmmu/bootdevice.c rename to system/bootdevice.c index 2106f1026ff..1845be4507e 100644 --- a/softmmu/bootdevice.c +++ b/system/bootdevice.c @@ -24,10 +24,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/visitor.h" #include "qemu/error-report.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/qdev-core.h" #include "hw/boards.h" @@ -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 93% rename from softmmu/cpu-timers.c rename to system/cpu-timers.c index 117408cb83a..cb35fa62b8a 100644 --- a/softmmu/cpu-timers.c +++ b/system/cpu-timers.c @@ -27,16 +27,16 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "qemu/main-loop.h" #include "qemu/option.h" #include "qemu/seqlock.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "system/replay.h" +#include "system/runstate.h" #include "hw/core/cpu.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/cpu-throttle.h" -#include "timers-state.h" +#include "system/cpu-timers.h" +#include "system/cpu-timers-internal.h" +#include "exec/icount.h" /* clock and ticks */ @@ -154,7 +154,7 @@ static bool adjust_timers_state_needed(void *opaque) static bool icount_shift_state_needed(void *opaque) { - return icount_enabled() == 2; + return icount_enabled() == ICOUNT_ADAPTATIVE; } /* @@ -165,7 +165,7 @@ static const VMStateDescription icount_vmstate_warp_timer = { .version_id = 1, .minimum_version_id = 1, .needed = warp_timer_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT64(vm_clock_warp_start, TimersState), VMSTATE_TIMER_PTR(icount_warp_timer, TimersState), VMSTATE_END_OF_LIST() @@ -177,7 +177,7 @@ static const VMStateDescription icount_vmstate_adjust_timers = { .version_id = 1, .minimum_version_id = 1, .needed = adjust_timers_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER_PTR(icount_rt_timer, TimersState), VMSTATE_TIMER_PTR(icount_vm_timer, TimersState), VMSTATE_END_OF_LIST() @@ -189,7 +189,7 @@ static const VMStateDescription icount_vmstate_shift = { .version_id = 2, .minimum_version_id = 2, .needed = icount_shift_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT16(icount_time_shift, TimersState), VMSTATE_INT64(last_delta, TimersState), VMSTATE_END_OF_LIST() @@ -204,12 +204,12 @@ static const VMStateDescription icount_vmstate_timers = { .version_id = 1, .minimum_version_id = 1, .needed = icount_state_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT64(qemu_icount_bias, TimersState), VMSTATE_INT64(qemu_icount, TimersState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &icount_vmstate_warp_timer, &icount_vmstate_adjust_timers, &icount_vmstate_shift, @@ -221,13 +221,13 @@ static const VMStateDescription vmstate_timers = { .name = "timer", .version_id = 2, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT64(cpu_ticks_offset, TimersState), VMSTATE_UNUSED(8), VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &icount_vmstate_timers, NULL } @@ -272,6 +272,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 81% rename from softmmu/cpus.c rename to system/cpus.c index 0848e0dbdb3..d16b0dff989 100644 --- a/softmmu/cpus.c +++ b/system/cpus.c @@ -31,18 +31,19 @@ #include "qapi/qapi-events-run-state.h" #include "qapi/qmp/qerror.h" #include "exec/gdbstub.h" -#include "sysemu/hw_accel.h" +#include "system/accel-ops.h" +#include "system/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 "system/cpus.h" #include "qemu/guest-random.h" #include "hw/nmi.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/whpx.h" +#include "system/replay.h" +#include "system/runstate.h" +#include "system/cpu-timers.h" +#include "system/whpx.h" #include "hw/boards.h" #include "hw/hw.h" #include "trace.h" @@ -65,7 +66,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. @@ -201,6 +203,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) { /* @@ -221,6 +230,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 @@ -252,22 +272,45 @@ 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(); - vm_state_notify(0, state); + if (oldstate == RUN_STATE_RUNNING) { + pause_all_vcpus(); + } + ret = vm_state_notify(0, state); if (send_stop) { qapi_event_send_stop(); } } bdrv_drain_all(); - ret = bdrv_flush_all(); + /* + * Even if vm_state_notify() return failure, + * it would be better to flush as before. + */ + ret |= bdrv_flush_all(); trace_vm_stop_flush_all(ret); return ret; @@ -382,14 +425,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) @@ -421,7 +464,7 @@ 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); @@ -474,46 +517,67 @@ 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) + +static uint32_t bql_unlock_blocked; -bool qemu_mutex_iothread_locked(void) +void bql_block_unlock(bool increase) { - return get_iothread_locked(); + uint32_t new_value; + + assert(bql_locked()); + + /* check for overflow! */ + new_value = bql_unlock_blocked + increase - !increase; + assert((new_value > bql_unlock_blocked) == increase); + bql_unlock_blocked = new_value; +} + +bool bql_locked(void) +{ + return get_bql_locked(); } bool qemu_in_main_thread(void) { - return qemu_mutex_iothread_locked(); + return bql_locked(); +} + +void rust_bql_mock_lock(void) +{ + error_report("This function should be used only from tests"); + abort(); } /* * 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()); + g_assert(!bql_unlock_blocked); + 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 */ @@ -530,6 +594,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) { @@ -550,12 +630,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 @@ -564,22 +639,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) @@ -601,9 +669,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) @@ -624,7 +692,6 @@ void qemu_init_vcpu(CPUState *cpu) { MachineState *ms = MACHINE(qdev_get_machine()); - cpu->nr_cores = ms->smp.cores; cpu->nr_threads = ms->smp.threads; cpu->stopped = true; cpu->random_seed = qemu_guest_random_seed_thread_part1(); @@ -642,7 +709,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); } } @@ -672,11 +739,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); @@ -707,9 +776,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) @@ -719,11 +789,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; @@ -738,14 +817,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; @@ -769,12 +848,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; @@ -785,11 +865,11 @@ void qmp_memsave(int64_t addr, int64_t size, const char *filename, 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"); @@ -804,7 +884,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 95% rename from softmmu/datadir.c rename to system/datadir.c index c9237cb5d4a..f96f8fc2646 100644 --- a/softmmu/datadir.c +++ b/system/datadir.c @@ -30,7 +30,7 @@ static const char *data_dir[16]; static int data_dir_idx; -char *qemu_find_file(int type, const char *name) +char *qemu_find_file(QemuFileType type, const char *name) { int i; const char *subdir; @@ -46,6 +46,9 @@ char *qemu_find_file(int type, const char *name) case QEMU_FILE_TYPE_BIOS: subdir = ""; break; + case QEMU_FILE_TYPE_DTB: + subdir = "dtb/"; + break; case QEMU_FILE_TYPE_KEYMAP: subdir = "keymaps/"; break; diff --git a/system/device_tree-stub.c b/system/device_tree-stub.c new file mode 100644 index 00000000000..428330b0fec --- /dev/null +++ b/system/device_tree-stub.c @@ -0,0 +1,13 @@ +#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) +{ + ERRP_GUARD(); + + error_setg(errp, "This machine doesn't have an FDT"); + error_append_hint(errp, "(this machine type definitely doesn't use FDT)\n"); +} +#endif diff --git a/softmmu/device_tree.c b/system/device_tree.c similarity index 94% rename from softmmu/device_tree.c rename to system/device_tree.c index 30aa3aea9fa..aa3fe9516f3 100644 --- a/softmmu/device_tree.c +++ b/system/device_tree.c @@ -23,12 +23,12 @@ #include "qemu/bswap.h" #include "qemu/cutils.h" #include "qemu/guest-random.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/loader.h" #include "hw/boards.h" #include "qemu/config-file.h" #include "qapi/qapi-commands-machine.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "monitor/hmp.h" #include @@ -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); @@ -594,21 +594,6 @@ int qemu_fdt_add_path(void *fdt, const char *path) return retval; } -void qemu_fdt_dumpdtb(void *fdt, int size) -{ - const char *dumpdtb = current_machine->dumpdtb; - - if (dumpdtb) { - /* Dump the dtb to a file and quit */ - if (g_file_set_contents(dumpdtb, fdt, size, NULL)) { - info_report("dtb dumped to %s. Exiting.", dumpdtb); - exit(0); - } - error_report("%s: Failed dumping dtb to %s", __func__, dumpdtb); - exit(1); - } -} - int qemu_fdt_setprop_sized_cells_from_array(void *fdt, const char *node_path, const char *property, @@ -650,11 +635,16 @@ int qemu_fdt_setprop_sized_cells_from_array(void *fdt, void qmp_dumpdtb(const char *filename, Error **errp) { + ERRP_GUARD(); + g_autoptr(GError) err = NULL; uint32_t size; if (!current_machine->fdt) { - error_setg(errp, "This machine doesn't have a FDT"); + error_setg(errp, "This machine doesn't have an FDT"); + error_append_hint(errp, + "(Perhaps it doesn't support FDT at all, or perhaps " + "you need to provide an FDT with the -fdt option?)\n"); return; } @@ -668,20 +658,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 94% rename from softmmu/dirtylimit.c rename to system/dirtylimit.c index fa959d77435..b48c0d4b3da 100644 --- a/softmmu/dirtylimit.c +++ b/system/dirtylimit.c @@ -13,20 +13,18 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qapi/qapi-commands-migration.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/error.h" -#include "sysemu/dirtyrate.h" -#include "sysemu/dirtylimit.h" +#include "system/dirtyrate.h" +#include "system/dirtylimit.h" #include "monitor/hmp.h" #include "monitor/monitor.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/target_page.h" #include "hw/boards.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "trace.h" #include "migration/misc.h" -#include "migration/migration.h" -#include "migration/options.h" /* * Dirtylimit stop working if dirty page rate error @@ -78,14 +76,12 @@ static bool dirtylimit_quit; static void vcpu_dirty_rate_stat_collect(void) { - MigrationState *s = migrate_get_current(); VcpuStat stat; int i = 0; int64_t period = DIRTYLIMIT_CALC_TIME_MS; - if (migrate_dirty_limit() && - migration_is_active(s)) { - period = s->parameters.x_vcpu_dirty_limit_period; + if (migrate_dirty_limit() && migration_is_running()) { + period = migrate_vcpu_dirty_limit_period(); } /* calculate vcpu dirtyrate */ @@ -148,9 +144,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(); } @@ -341,8 +337,6 @@ static void dirtylimit_adjust_throttle(CPUState *cpu) if (!dirtylimit_done(quota, current)) { dirtylimit_set_throttle(cpu, quota, current); } - - return; } void dirtylimit_process(void) @@ -411,12 +405,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(); } } @@ -442,10 +444,8 @@ static void dirtylimit_cleanup(void) */ static bool dirtylimit_is_allowed(void) { - MigrationState *ms = migrate_get_current(); - - if (migration_is_running(ms->state) && - (!qemu_thread_is_self(&ms->thread)) && + if (migration_is_running() && + !migration_thread_is_self() && migrate_dirty_limit() && dirtylimit_in_service()) { return false; @@ -644,10 +644,6 @@ 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(); } diff --git a/softmmu/dma-helpers.c b/system/dma-helpers.c similarity index 93% rename from softmmu/dma-helpers.c rename to system/dma-helpers.c index 24639648056..0d592f64680 100644 --- a/softmmu/dma-helpers.c +++ b/system/dma-helpers.c @@ -8,12 +8,12 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" -#include "sysemu/dma.h" -#include "trace/trace-root.h" +#include "system/block-backend.h" +#include "system/dma.h" +#include "trace.h" #include "qemu/thread.h" #include "qemu/main-loop.h" -#include "sysemu/cpu-timers.h" +#include "exec/icount.h" #include "qemu/range.h" /* #define DEBUG_IOMMU */ @@ -119,13 +119,15 @@ static void dma_blk_cb(void *opaque, int ret) trace_dma_blk_cb(dbs, ret); - aio_context_acquire(ctx); + /* 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; if (dbs->sg_cur_index == dbs->sg->nsg || ret < 0) { dma_complete(dbs, ret); - goto out; + return; } dma_blk_unmap(dbs); @@ -167,8 +169,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(ctx, reschedule_dma, dbs); - cpu_register_map_client(dbs->bh); - goto out; + address_space_register_map_client(dbs->sg->as, dbs->bh); + return; } if (!QEMU_IS_ALIGNED(dbs->iov.size, dbs->align)) { @@ -179,8 +181,6 @@ static void dma_blk_cb(void *opaque, int ret) dbs->acb = dbs->io_func(dbs->offset, &dbs->iov, dma_blk_cb, dbs, dbs->io_func_opaque); assert(dbs->acb); -out: - aio_context_release(ctx); } static void dma_aio_cancel(BlockAIOCB *acb) @@ -197,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; } @@ -206,20 +206,12 @@ 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, +BlockAIOCB *dma_blk_io( QEMUSGList *sg, uint64_t offset, uint32_t align, DMAIOFunc *io_func, void *io_func_opaque, BlockCompletionFunc *cb, @@ -231,7 +223,7 @@ BlockAIOCB *dma_blk_io(AioContext *ctx, dbs->acb = NULL; dbs->sg = sg; - dbs->ctx = ctx; + dbs->ctx = qemu_get_current_aio_context(); dbs->offset = offset; dbs->align = align; dbs->sg_cur_index = 0; @@ -259,7 +251,7 @@ BlockAIOCB *dma_blk_read(BlockBackend *blk, QEMUSGList *sg, uint64_t offset, uint32_t align, void (*cb)(void *opaque, int ret), void *opaque) { - return dma_blk_io(blk_get_aio_context(blk), sg, offset, align, + return dma_blk_io(sg, offset, align, dma_blk_read_io_func, blk, cb, opaque, DMA_DIRECTION_FROM_DEVICE); } @@ -277,7 +269,7 @@ BlockAIOCB *dma_blk_write(BlockBackend *blk, QEMUSGList *sg, uint64_t offset, uint32_t align, void (*cb)(void *opaque, int ret), void *opaque) { - return dma_blk_io(blk_get_aio_context(blk), sg, offset, align, + return dma_blk_io(sg, offset, align, dma_blk_write_io_func, blk, cb, opaque, DMA_DIRECTION_TO_DEVICE); } diff --git a/system/globals-target.c b/system/globals-target.c new file mode 100644 index 00000000000..989720591e7 --- /dev/null +++ b/system/globals-target.c @@ -0,0 +1,24 @@ +/* + * Global variables that should not exist (target specific) + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" +#include "system/system.h" + +#ifdef TARGET_SPARC +int graphic_width = 1024; +int graphic_height = 768; +int graphic_depth = 8; +#elif defined(TARGET_M68K) +int graphic_width = 800; +int graphic_height = 600; +int graphic_depth = 8; +#else +int graphic_width = 800; +int graphic_height = 600; +int graphic_depth = 32; +#endif diff --git a/softmmu/globals.c b/system/globals.c similarity index 89% rename from softmmu/globals.c rename to system/globals.c index e83b5428d12..9640c9511e9 100644 --- a/softmmu/globals.c +++ b/system/globals.c @@ -28,23 +28,28 @@ #include "hw/loader.h" #include "hw/xen/xen.h" #include "net/net.h" -#include "sysemu/cpus.h" -#include "sysemu/sysemu.h" +#include "system/cpus.h" +#include "system/system.h" + +bool should_mlock(MlockState state) +{ + return state == MLOCK_ON || state == MLOCK_ON_FAULT; +} + +bool is_mlock_on_fault(MlockState state) +{ + return state == MLOCK_ON_FAULT; +} enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB; int display_opengl; const char* keyboard_layout; -bool enable_mlock; +MlockState mlock_state; 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 fd_bootchk = 1; -int graphic_rotate; QEMUOptionRom option_rom[MAX_OPTION_ROMS]; int nb_option_roms; int old_param; @@ -53,7 +58,6 @@ unsigned int nb_prom_envs; const char *prom_envs[MAX_PROM_ENVS]; uint8_t *boot_splash_filedata; int only_migratable; /* turn it off unless user states otherwise */ -int icount_align_option; /* The bytes in qemu_uuid are in the order specified by RFC4122, _not_ in the * little-endian "wire format" described in the SMBIOS 2.6 specification. @@ -64,6 +68,7 @@ bool qemu_uuid_set; uint32_t xen_domid; 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; diff --git a/softmmu/ioport.c b/system/ioport.c similarity index 87% rename from softmmu/ioport.c rename to system/ioport.c index 1824aa808c7..4f96e9119fc 100644 --- a/softmmu/ioport.c +++ b/system/ioport.c @@ -26,10 +26,9 @@ */ #include "qemu/osdep.h" -#include "cpu.h" -#include "exec/ioport.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" +#include "system/ioport.h" +#include "system/memory.h" +#include "system/address-spaces.h" #include "trace.h" struct MemoryRegionPortioList { @@ -133,6 +132,7 @@ void portio_list_init(PortioList *piolist, piolist->nr = 0; piolist->regions = g_new0(MemoryRegion *, n); piolist->address_space = NULL; + piolist->addr = 0; piolist->opaque = opaque; piolist->owner = owner; piolist->name = name; @@ -181,13 +181,13 @@ static uint64_t portio_read(void *opaque, hwaddr addr, unsigned size) data = ((uint64_t)1 << (size * 8)) - 1; if (mrp) { - data = mrp->read(mrpio->portio_opaque, mrp->base + addr); + data = mrp->read(mrpio->portio_opaque, mrpio->mr.addr + addr); } else if (size == 2) { mrp = find_portio(mrpio, addr, 1, false); if (mrp) { - data = mrp->read(mrpio->portio_opaque, mrp->base + addr); + data = mrp->read(mrpio->portio_opaque, mrpio->mr.addr + addr); if (addr + 1 < mrp->offset + mrp->len) { - data |= mrp->read(mrpio->portio_opaque, mrp->base + addr + 1) << 8; + data |= mrp->read(mrpio->portio_opaque, mrpio->mr.addr + addr + 1) << 8; } else { data |= 0xff00; } @@ -203,13 +203,13 @@ static void portio_write(void *opaque, hwaddr addr, uint64_t data, const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, true); if (mrp) { - mrp->write(mrpio->portio_opaque, mrp->base + addr, data); + mrp->write(mrpio->portio_opaque, mrpio->mr.addr + addr, data); } else if (size == 2) { mrp = find_portio(mrpio, addr, 1, true); if (mrp) { - mrp->write(mrpio->portio_opaque, mrp->base + addr, data & 0xff); + mrp->write(mrpio->portio_opaque, mrpio->mr.addr + addr, data & 0xff); if (addr + 1 < mrp->offset + mrp->len) { - mrp->write(mrpio->portio_opaque, mrp->base + addr + 1, data >> 8); + mrp->write(mrpio->portio_opaque, mrpio->mr.addr + addr + 1, data >> 8); } } } @@ -244,7 +244,6 @@ static void portio_list_add_1(PortioList *piolist, /* 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; } /* @@ -258,7 +257,7 @@ static void portio_list_add_1(PortioList *piolist, object_ref(&mrpio->mr); object_unparent(OBJECT(&mrpio->mr)); if (!piolist->owner) { - owner = container_get(qdev_get_machine(), "/unattached"); + owner = machine_get_container("unattached"); } else { owner = piolist->owner; } @@ -283,6 +282,7 @@ void portio_list_add(PortioList *piolist, unsigned int off_low, off_high, off_last, count; piolist->address_space = address_space; + piolist->addr = start; /* Handle the first entry specially. */ off_last = off_low = pio_start->offset; @@ -323,6 +323,32 @@ void portio_list_del(PortioList *piolist) } } +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); diff --git a/system/main.c b/system/main.c new file mode 100644 index 00000000000..b8f7157cc34 --- /dev/null +++ b/system/main.c @@ -0,0 +1,96 @@ +/* + * 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 "qemu/main-loop.h" +#include "system/replay.h" +#include "system/system.h" + +#ifdef CONFIG_SDL +/* + * SDL insists on wrapping the main() function with its own implementation on + * some platforms; it does so via a macro that renames our main function, so + * must be #included here even with no SDL code called from this file. + */ +#include +#endif + +#ifdef CONFIG_DARWIN +#include +#endif + +static void *qemu_default_main(void *opaque) +{ + int status; + + replay_mutex_lock(); + bql_lock(); + status = qemu_main_loop(); + qemu_cleanup(status); + bql_unlock(); + replay_mutex_unlock(); + + exit(status); +} + +int (*qemu_main)(void); + +#ifdef CONFIG_DARWIN +static int os_darwin_cfrunloop_main(void) +{ + CFRunLoopRun(); + g_assert_not_reached(); +} +int (*qemu_main)(void) = os_darwin_cfrunloop_main; +#endif + +int main(int argc, char **argv) +{ + qemu_init(argc, argv); + + /* + * qemu_init acquires the BQL and replay mutex lock. BQL is acquired when + * initializing cpus, to block associated threads until initialization is + * complete. Replay_mutex lock is acquired on initialization, because it + * must be held when configuring icount_mode. + * + * On MacOS, qemu main event loop runs in a background thread, as main + * thread must be reserved for UI. Thus, we need to transfer lock ownership, + * and the simplest way to do that is to release them, and reacquire them + * from qemu_default_main. + */ + bql_unlock(); + replay_mutex_unlock(); + + if (qemu_main) { + QemuThread main_loop_thread; + qemu_thread_create(&main_loop_thread, "qemu_main", + qemu_default_main, NULL, QEMU_THREAD_DETACHED); + return qemu_main(); + } else { + qemu_default_main(NULL); + g_assert_not_reached(); + } +} diff --git a/include/exec/memory-internal.h b/system/memory-internal.h similarity index 75% rename from include/exec/memory-internal.h rename to system/memory-internal.h index 100c1237ac2..46f758fa7e4 100644 --- a/include/exec/memory-internal.h +++ b/system/memory-internal.h @@ -11,17 +11,9 @@ * */ -/* - * This header is for use by exec.c, memory.c and accel/tcg/cputlb.c ONLY, - * for declarations which are shared between the memory subsystem's - * internals and the TCG TLB code. Do not include it from elsewhere. - */ - #ifndef MEMORY_INTERNAL_H #define MEMORY_INTERNAL_H -#include "cpu.h" - #ifndef CONFIG_USER_ONLY static inline AddressSpaceDispatch *flatview_to_dispatch(FlatView *fv) { @@ -45,5 +37,21 @@ void address_space_dispatch_free(AddressSpaceDispatch *d); void mtree_print_dispatch(struct AddressSpaceDispatch *d, MemoryRegion *root); + +/* returns true if end is big endian. */ +static inline bool devend_big_endian(enum device_endian end) +{ + if (end == DEVICE_NATIVE_ENDIAN) { + return target_big_endian(); + } + return end == DEVICE_BIG_ENDIAN; +} + +/* enum device_endian to MemOp. */ +static inline MemOp devend_memop(enum device_endian end) +{ + return devend_big_endian(end) ? MO_BE : MO_LE; +} + #endif #endif diff --git a/softmmu/memory.c b/system/memory.c similarity index 90% rename from softmmu/memory.c rename to system/memory.c index 7d9494ce702..76b44b8220f 100644 --- a/softmmu/memory.c +++ b/system/memory.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qapi/error.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qapi/visitor.h" #include "qemu/bitops.h" #include "qemu/error-report.h" @@ -24,16 +24,16 @@ #include "qemu/qemu-print.h" #include "qom/object.h" #include "trace.h" - -#include "exec/memory-internal.h" -#include "exec/ram_addr.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" -#include "sysemu/tcg.h" +#include "system/ram_addr.h" +#include "system/kvm.h" +#include "system/runstate.h" +#include "system/tcg.h" #include "qemu/accel.h" #include "hw/boards.h" #include "migration/vmstate.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" + +#include "memory-internal.h" //#define DEBUG_UNASSIGNED @@ -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 */ @@ -349,15 +353,6 @@ static void flatview_simplify(FlatView *view) } } -static bool memory_region_big_endian(MemoryRegion *mr) -{ -#if TARGET_BIG_ENDIAN - return mr->ops->endianness != DEVICE_LITTLE_ENDIAN; -#else - return mr->ops->endianness == DEVICE_BIG_ENDIAN; -#endif -} - static void adjust_endianness(MemoryRegion *mr, uint64_t *data, MemOp op) { if ((op & MO_BSWAP) != devend_memop(mr->ops->endianness)) { @@ -559,7 +554,7 @@ static MemTxResult access_with_adjusted_size(hwaddr addr, /* FIXME: support unaligned access? */ access_size = MAX(MIN(size, access_size_max), access_size_min); access_mask = MAKE_64BIT_MASK(0, access_size * 8); - if (memory_region_big_endian(mr)) { + if (devend_big_endian(mr->ops->endianness)) { for (i = 0; i < size; i += access_size) { r |= access_fn(mr, addr + i, value, access_size, (size - access_size - i) * 8, access_mask, attrs); @@ -599,7 +594,8 @@ static void render_memory_region(FlatView *view, Int128 base, AddrRange clip, bool readonly, - bool nonvolatile) + bool nonvolatile, + bool unmergeable) { MemoryRegion *subregion; unsigned i; @@ -616,6 +612,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); @@ -629,14 +626,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) { @@ -652,6 +649,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) { @@ -753,7 +751,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); @@ -842,6 +840,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 @@ -930,6 +932,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, @@ -1108,7 +1142,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) { @@ -1195,7 +1229,7 @@ static void memory_region_do_init(MemoryRegion *mr, char *name_array = g_strdup_printf("%s[*]", escaped_name); if (!owner) { - owner = container_get(qdev_get_machine(), "/unattached"); + owner = machine_get_container("unattached"); } object_property_add_child(owner, name_array, OBJECT(mr)); @@ -1328,22 +1362,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); @@ -1357,26 +1376,13 @@ 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 = { .read = memory_region_ram_device_read, .write = memory_region_ram_device_write, - .endianness = DEVICE_HOST_ENDIAN, + .endianness = HOST_BIG_ENDIAN ? DEVICE_BIG_ENDIAN : DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, .max_access_size = 8, @@ -1397,7 +1403,7 @@ bool memory_region_access_valid(MemoryRegion *mr, { if (mr->ops->valid.accepts && !mr->ops->valid.accepts(mr->opaque, addr, size, is_write, attrs)) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid %s at addr 0x%" HWADDR_PRIX + qemu_log_mask(LOG_INVALID_MEM, "Invalid %s at addr 0x%" HWADDR_PRIX ", size %u, region '%s', reason: rejected\n", is_write ? "write" : "read", addr, size, memory_region_name(mr)); @@ -1405,7 +1411,7 @@ bool memory_region_access_valid(MemoryRegion *mr, } if (!mr->ops->valid.unaligned && (addr & (size - 1))) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid %s at addr 0x%" HWADDR_PRIX + qemu_log_mask(LOG_INVALID_MEM, "Invalid %s at addr 0x%" HWADDR_PRIX ", size %u, region '%s', reason: unaligned\n", is_write ? "write" : "read", addr, size, memory_region_name(mr)); @@ -1419,7 +1425,7 @@ bool memory_region_access_valid(MemoryRegion *mr, if (size > mr->ops->valid.max_access_size || size < mr->ops->valid.min_access_size) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid %s at addr 0x%" HWADDR_PRIX + qemu_log_mask(LOG_INVALID_MEM, "Invalid %s at addr 0x%" HWADDR_PRIX ", size %u, region '%s', reason: invalid size " "(min:%u max:%u)\n", is_write ? "write" : "read", @@ -1524,7 +1530,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; } @@ -1558,16 +1569,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, @@ -1584,10 +1596,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, @@ -1608,11 +1622,13 @@ 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, +#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN) +bool memory_region_init_ram_from_file(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, @@ -1620,26 +1636,27 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, uint32_t ram_flags, const char *path, ram_addr_t offset, - bool readonly, 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, - offset, 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, @@ -1651,15 +1668,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); + mr->ram_block = qemu_ram_alloc_from_fd(size, size, NULL, mr, ram_flags, fd, + offset, false, &err); if (err) { mr->size = int128_zero(); object_unparent(OBJECT(mr)); error_propagate(errp, err); + return false; } + return true; } #endif @@ -1676,7 +1696,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, @@ -1695,7 +1715,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, @@ -1710,17 +1730,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, @@ -1741,7 +1766,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, @@ -1846,6 +1873,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; @@ -1892,19 +1924,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) { @@ -1984,9 +2003,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; @@ -2030,7 +2049,7 @@ void memory_region_unmap_iommu_notifier_range(IOMMUNotifier *notifier) void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, int iommu_idx, - IOMMUTLBEvent event) + const IOMMUTLBEvent event) { IOMMUNotifier *iommu_notifier; @@ -2081,18 +2100,22 @@ 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; } -void memory_region_set_ram_discard_manager(MemoryRegion *mr, - RamDiscardManager *rdm) +int memory_region_set_ram_discard_manager(MemoryRegion *mr, + RamDiscardManager *rdm) { - g_assert(memory_region_is_ram(mr) && !memory_region_is_mapped(mr)); - g_assert(!rdm || !mr->rdm); + g_assert(memory_region_is_ram(mr)); + if (mr->rdm && rdm) { + return -EBUSY; + } + mr->rdm = rdm; + return 0; } uint64_t ram_discard_manager_get_min_granularity(const RamDiscardManager *rdm, @@ -2115,7 +2138,7 @@ bool ram_discard_manager_is_populated(const RamDiscardManager *rdm, int ram_discard_manager_replay_populated(const RamDiscardManager *rdm, MemoryRegionSection *section, - ReplayRamPopulate replay_fn, + ReplayRamDiscardState replay_fn, void *opaque) { RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm); @@ -2124,15 +2147,15 @@ int ram_discard_manager_replay_populated(const RamDiscardManager *rdm, return rdmc->replay_populated(rdm, section, replay_fn, opaque); } -void ram_discard_manager_replay_discarded(const RamDiscardManager *rdm, - MemoryRegionSection *section, - ReplayRamDiscard replay_fn, - void *opaque) +int ram_discard_manager_replay_discarded(const RamDiscardManager *rdm, + MemoryRegionSection *section, + ReplayRamDiscardState replay_fn, + void *opaque) { RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm); g_assert(rdmc->replay_discarded); - rdmc->replay_discarded(rdm, section, replay_fn, opaque); + return rdmc->replay_discarded(rdm, section, replay_fn, opaque); } void ram_discard_manager_register_listener(RamDiscardManager *rdm, @@ -2155,18 +2178,14 @@ 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) +MemoryRegion *memory_translate_iotlb(IOMMUTLBEntry *iotlb, hwaddr *xlat_p, + Error **errp) { MemoryRegion *mr; hwaddr xlat; hwaddr len = iotlb->addr_mask + 1; bool writable = iotlb->perm & IOMMU_WO; - if (mr_has_discard_manager) { - *mr_has_discard_manager = false; - } /* * The IOMMU TLB entry we have just covers translation through * this IOMMU to its immediate target. We need to translate @@ -2175,8 +2194,8 @@ 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); - return false; + error_setg(errp, "iommu map to non memory area %" HWADDR_PRIx "", xlat); + return NULL; } else if (memory_region_has_ram_discard_manager(mr)) { RamDiscardManager *rdm = memory_region_get_ram_discard_manager(mr); MemoryRegionSection tmp = { @@ -2184,9 +2203,6 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, .offset_within_region = xlat, .size = int128_make64(len), }; - if (mr_has_discard_manager) { - *mr_has_discard_manager = true; - } /* * Malicious VMs can map memory into the IOMMU, which is expected * to remain discarded. vfio will pin all pages, populating memory. @@ -2194,10 +2210,10 @@ 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; + return NULL; } } @@ -2206,23 +2222,12 @@ 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"); - return false; - } - - if (vaddr) { - *vaddr = memory_region_get_ram_ptr(mr) + xlat; - } - - if (ram_addr) { - *ram_addr = memory_region_get_ram_addr(mr) + xlat; - } - - if (read_only) { - *read_only = !writable || mr->readonly; + error_setg(errp, "iommu has granularity incompatible with target AS"); + return NULL; } - return true; + *xlat_p = xlat; + return mr; } void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) @@ -2539,8 +2544,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, @@ -2557,15 +2560,9 @@ 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); + MemOp mop = (target_big_endian() ? MO_BE : MO_LE) | size_memop(size); + adjust_endianness(mr, &mrfd.data, mop); } memory_region_transaction_begin(); for (i = 0; i < mr->ioeventfd_nb; ++i) { @@ -2600,7 +2597,8 @@ void memory_region_del_eventfd(MemoryRegion *mr, unsigned i; if (size) { - adjust_endianness(mr, &mrfd.data, size_memop(size) | MO_TE); + MemOp mop = (target_big_endian() ? MO_BE : MO_LE) | size_memop(size); + adjust_endianness(mr, &mrfd.data, mop); } memory_region_transaction_begin(); for (i = 0; i < mr->ioeventfd_nb; ++i) { @@ -2751,6 +2749,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; @@ -2894,7 +2904,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; @@ -2908,7 +2941,7 @@ void memory_global_dirty_log_start(unsigned int flags) flags &= ~global_dirty_tracking; if (!flags) { - return; + return true; } old_flags = global_dirty_tracking; @@ -2916,11 +2949,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) @@ -2987,15 +3026,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); } } @@ -3006,10 +3054,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); } @@ -3019,8 +3091,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); @@ -3032,10 +3106,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); } @@ -3075,6 +3172,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) @@ -3083,6 +3184,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); @@ -3105,6 +3210,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); @@ -3112,6 +3221,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); @@ -3233,7 +3346,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 */ @@ -3551,19 +3663,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 @@ -3573,21 +3682,44 @@ void memory_region_init_ram(MemoryRegion *mr, */ owner_dev = DEVICE(owner); vmstate_register_ram(mr, owner_dev); + + return true; } -void memory_region_init_rom(MemoryRegion *mr, +bool memory_region_init_ram_guest_memfd(MemoryRegion *mr, + Object *owner, + const char *name, + uint64_t size, + Error **errp) +{ + DeviceState *owner_dev; + + 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 + * 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(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, Error **errp) { DeviceState *owner_dev; - Error *err = NULL; - memory_region_init_rom_nomigrate(mr, owner, name, size, &err); - if (err) { - error_propagate(errp, err); - return; + if (!memory_region_init_rom_nomigrate(mr, owner, name, size, errp)) { + return false; } /* This will assert if owner is neither NULL nor a DeviceState. * We only want the owner here for the purposes of defining a @@ -3597,9 +3729,11 @@ void memory_region_init_rom(MemoryRegion *mr, */ owner_dev = DEVICE(owner); vmstate_register_ram(mr, owner_dev); + + return true; } -void memory_region_init_rom_device(MemoryRegion *mr, +bool memory_region_init_rom_device(MemoryRegion *mr, Object *owner, const MemoryRegionOps *ops, void *opaque, @@ -3608,13 +3742,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 @@ -3624,10 +3755,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 94% rename from memory_ldst.c.inc rename to system/memory_ldst.c.inc index 84b868f2946..7f32d3d9ff3 100644 --- a/memory_ldst.c.inc +++ b/system/memory_ldst.c.inc @@ -34,7 +34,7 @@ static inline uint32_t glue(address_space_ldl_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, false, attrs); - if (l < 4 || !memory_access_is_direct(mr, false)) { + if (l < 4 || !memory_access_is_direct(mr, false, attrs)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ @@ -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; @@ -103,7 +103,7 @@ static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, false, attrs); - if (l < 8 || !memory_access_is_direct(mr, false)) { + if (l < 8 || !memory_access_is_direct(mr, false, attrs)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ @@ -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; @@ -170,7 +170,7 @@ uint8_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, false, attrs); - if (!memory_access_is_direct(mr, false)) { + if (!memory_access_is_direct(mr, false, attrs)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ @@ -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; @@ -207,7 +207,7 @@ static inline uint16_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, false, attrs); - if (l < 2 || !memory_access_is_direct(mr, false)) { + if (l < 2 || !memory_access_is_direct(mr, false, attrs)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ @@ -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; @@ -277,7 +277,7 @@ void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, true, attrs); - if (l < 4 || !memory_access_is_direct(mr, true)) { + if (l < 4 || !memory_access_is_direct(mr, true, attrs)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, MO_32, attrs); @@ -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(); } @@ -314,7 +314,7 @@ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, true, attrs); - if (l < 4 || !memory_access_is_direct(mr, true)) { + if (l < 4 || !memory_access_is_direct(mr, true, attrs)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, MO_32 | devend_memop(endian), attrs); @@ -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(); } @@ -377,7 +377,7 @@ void glue(address_space_stb, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, true, attrs); - if (!memory_access_is_direct(mr, true)) { + if (!memory_access_is_direct(mr, true, attrs)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, MO_8, attrs); } else { @@ -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(); } @@ -410,7 +410,7 @@ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, true, attrs); - if (l < 2 || !memory_access_is_direct(mr, true)) { + if (l < 2 || !memory_access_is_direct(mr, true, attrs)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, MO_16 | devend_memop(endian), attrs); @@ -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(); } @@ -474,7 +474,7 @@ static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, RCU_READ_LOCK(); mr = TRANSLATE(addr, &addr1, &l, true, attrs); - if (l < 8 || !memory_access_is_direct(mr, true)) { + if (l < 8 || !memory_access_is_direct(mr, true, attrs)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, MO_64 | devend_memop(endian), attrs); @@ -499,7 +499,7 @@ static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, *result = r; } if (release_lock) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } RCU_READ_UNLOCK(); } diff --git a/softmmu/memory_mapping.c b/system/memory_mapping.c similarity index 95% rename from softmmu/memory_mapping.c rename to system/memory_mapping.c index d7f1d096e04..da708a08ab7 100644 --- a/softmmu/memory_mapping.c +++ b/system/memory_mapping.c @@ -12,11 +12,12 @@ */ #include "qemu/osdep.h" +#include "qemu/range.h" #include "qapi/error.h" -#include "sysemu/memory_mapping.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" +#include "system/memory_mapping.h" +#include "system/memory.h" +#include "system/address-spaces.h" #include "hw/core/cpu.h" //#define DEBUG_GUEST_PHYS_REGION_ADD @@ -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 00000000000..6d21ff9faa7 --- /dev/null +++ b/system/meson.build @@ -0,0 +1,44 @@ +specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: [files( + 'arch_init.c', + 'globals-target.c', +)]) + +system_ss.add(files( + 'vl.c', +), sdl, libpmem, libdaxctl) + +system_ss.add(files( + 'balloon.c', + 'bootdevice.c', + 'cpus.c', + 'cpu-timers.c', + 'datadir.c', + 'dirtylimit.c', + 'dma-helpers.c', + 'globals.c', + 'ioport.c', + 'ram-block-attributes.c', + 'memory_mapping.c', + 'memory.c', + 'physmem.c', + 'qdev-monitor.c', + 'qtest.c', + 'rtc.c', + 'runstate-action.c', + 'runstate-hmp-cmds.c', + 'runstate.c', + 'tpm-hmp-cmds.c', + 'watchpoint.c', +)) + +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 76% rename from softmmu/physmem.c rename to system/physmem.c index 18277ddd674..ff0ca40222d 100644 --- a/softmmu/physmem.c +++ b/system/physmem.c @@ -25,33 +25,38 @@ #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" +#include "accel/tcg/cpu-ops.h" +#include "accel/tcg/iommu.h" #endif /* CONFIG_TCG */ -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "exec/page-protection.h" #include "exec/target_page.h" +#include "exec/translation-block.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "hw/boards.h" -#include "hw/xen/xen.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" -#include "sysemu/qtest.h" +#include "system/xen.h" +#include "system/kvm.h" +#include "system/tcg.h" +#include "system/qtest.h" #include "qemu/timer.h" #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" #include "qemu/log.h" #include "qemu/memalign.h" -#include "exec/memory.h" -#include "exec/ioport.h" -#include "sysemu/dma.h" -#include "sysemu/hostmem.h" -#include "sysemu/hw_accel.h" -#include "sysemu/xen-mapcache.h" -#include "trace/trace-root.h" +#include "qemu/memfd.h" +#include "system/memory.h" +#include "system/ioport.h" +#include "system/dma.h" +#include "system/hostmem.h" +#include "system/hw_accel.h" +#include "system/xen-mapcache.h" +#include "trace.h" #ifdef CONFIG_FALLOCATE_PUNCH_HOLE #include @@ -59,14 +64,16 @@ #include "qemu/rcu_queue.h" #include "qemu/main-loop.h" -#include "exec/translate-all.h" -#include "sysemu/replay.h" +#include "system/replay.h" -#include "exec/memory-internal.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "qemu/pmem.h" +#include "qapi/qapi-types-migration.h" +#include "migration/blocker.h" +#include "migration/cpr.h" +#include "migration/options.h" #include "migration/vmstate.h" #include "qemu/range.h" @@ -80,6 +87,8 @@ #include #endif +#include "memory-internal.h" + //#define DEBUG_SUBPAGE /* ram_list is read under rcu_read_lock()/rcu_read_unlock(). Writes @@ -150,6 +159,7 @@ static void io_mem_init(void); static void memory_map_init(void); static void tcg_log_global_after_sync(MemoryListener *listener); static void tcg_commit(MemoryListener *listener); +static bool ram_is_cpr_compatible(RAMBlock *rb); /** * CPUAddressSpace: all the information a CPU needs about an AddressSpace @@ -158,12 +168,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; @@ -569,7 +579,7 @@ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, is_write, true, &as, attrs); mr = section.mr; - if (xen_enabled() && memory_access_is_direct(mr, is_write)) { + if (xen_enabled() && memory_access_is_direct(mr, is_write, attrs)) { hwaddr page = ((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr; *plen = MIN(page, *plen); } @@ -577,6 +587,8 @@ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, return mr; } +#ifdef CONFIG_TCG + typedef struct TCGIOMMUNotifier { IOMMUNotifier n; MemoryRegion *mr; @@ -736,6 +748,33 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr orig_addr, return &d->map.sections[PHYS_SECTION_UNASSIGNED]; } +MemoryRegionSection *iotlb_to_section(CPUState *cpu, + hwaddr index, MemTxAttrs attrs) +{ + int asidx = cpu_asidx_from_attrs(cpu, attrs); + CPUAddressSpace *cpuas = &cpu->cpu_ases[asidx]; + AddressSpaceDispatch *d = cpuas->memory_dispatch; + int section_index = index & ~TARGET_PAGE_MASK; + MemoryRegionSection *ret; + + assert(section_index < d->map.sections_nb); + ret = d->map.sections + section_index; + assert(ret->mr); + assert(ret->mr->ops); + + return ret; +} + +/* Called from RCU critical section */ +hwaddr memory_region_section_get_iotlb(CPUState *cpu, + MemoryRegionSection *section) +{ + AddressSpaceDispatch *d = flatview_to_dispatch(section->fv); + return section - d->map.sections; +} + +#endif /* CONFIG_TCG */ + void cpu_address_space_init(CPUState *cpu, int asidx, const char *prefix, MemoryRegion *mr) { @@ -761,6 +800,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,6 +814,34 @@ 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 */ @@ -799,7 +867,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 @@ -819,7 +887,7 @@ static RAMBlock *qemu_get_ram_block(ram_addr_t addr) 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; @@ -881,8 +949,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; @@ -892,13 +960,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; @@ -913,25 +987,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); @@ -959,14 +1031,6 @@ bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, return false; } -/* Called from RCU critical section */ -hwaddr memory_region_section_get_iotlb(CPUState *cpu, - MemoryRegionSection *section) -{ - AddressSpaceDispatch *d = flatview_to_dispatch(section->fv); - return section - d->map.sections; -} - static int subpage_register(subpage_t *mmio, uint32_t start, uint32_t end, uint16_t section); static subpage_t *subpage_init(FlatView *fv, hwaddr base); @@ -1200,7 +1264,7 @@ long qemu_maxrampagesize(void) return pagesize; } -#ifdef CONFIG_POSIX +#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN) static int64_t get_file_size(int fd) { int64_t size; @@ -1288,8 +1352,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; @@ -1300,10 +1363,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) { @@ -1333,10 +1419,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 @@ -1350,7 +1433,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) @@ -1408,7 +1490,7 @@ static void *file_ram_alloc(RAMBlock *block, 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; @@ -1481,18 +1563,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; @@ -1503,7 +1573,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"); } } } @@ -1579,7 +1649,7 @@ 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; @@ -1607,7 +1677,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 @@ -1619,6 +1689,18 @@ void qemu_ram_unset_idstr(RAMBlock *block) } } +static char *cpr_name(MemoryRegion *mr) +{ + const char *mr_name = memory_region_name(mr); + g_autofree char *id = mr->dev ? qdev_get_dev_path(mr->dev) : NULL; + + if (id) { + return g_strdup_printf("%s/%s", id, mr_name); + } else { + return g_strdup(mr_name); + } +} + size_t qemu_ram_pagesize(RAMBlock *rb) { return rb->page_size; @@ -1662,7 +1744,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) { /* @@ -1745,13 +1828,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 */ @@ -1783,6 +1864,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) @@ -1791,11 +1874,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); @@ -1820,14 +1902,69 @@ 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; + + if (!kvm_enabled()) { + error_setg(errp, "cannot set up private guest memory for %s: KVM required", + object_get_typename(OBJECT(current_machine->cgs))); + goto out_free; + } + assert(new_block->guest_memfd < 0); + + ret = ram_block_coordinated_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; + } + + /* + * The attribute bitmap of the RamBlockAttributes is default to + * discarded, which mimics the behavior of kvm_set_phys_mem() when it + * calls kvm_set_memory_attributes_private(). This leads to a brief + * period of inconsistency between the creation of the RAMBlock and its + * mapping into the physical address space. However, this is not + * problematic, as no users rely on the attribute status to perform + * any actions during this interval. + */ + new_block->attributes = ram_block_attributes_create(new_block); + if (!new_block->attributes) { + error_setg(errp, "Failed to create ram block attribute"); + close(new_block->guest_memfd); + ram_block_coordinated_discard_require(false); + qemu_mutex_unlock_ramlist(); + goto out_free; + } + + /* + * Add a specific guest_memfd blocker if a generic one would not be + * added by ram_block_add_cpr_blocker. + */ + if (ram_is_cpr_compatible(new_block)) { + error_setg(&new_block->cpr_blocker, + "Memory region %s uses guest_memfd, " + "which is not supported with CPR.", + memory_region_name(new_block->mr)); + migrate_add_blocker_modes(&new_block->cpr_blocker, errp, + MIG_MODE_CPR_TRANSFER, -1); + } } + + 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. @@ -1871,20 +2008,37 @@ 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, +#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN) +RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, ram_addr_t max_size, + qemu_ram_resize_cb resized, MemoryRegion *mr, uint32_t ram_flags, int fd, off_t offset, - bool readonly, Error **errp) + bool grow, + Error **errp) { + ERRP_GUARD(); RAMBlock *new_block; Error *local_err = NULL; - int64_t file_size, file_align; + int64_t file_size, file_align, share_flags; + + share_flags = ram_flags & (RAM_PRIVATE | RAM_SHARED); + assert(share_flags != (RAM_SHARED | RAM_PRIVATE)); + ram_flags &= ~RAM_PRIVATE; /* Just support these ram flags by now. */ assert((ram_flags & ~(RAM_SHARED | RAM_PMEM | RAM_NORESERVE | - RAM_PROTECTED | RAM_NAMED_FILE)) == 0); + RAM_PROTECTED | RAM_NAMED_FILE | RAM_READONLY | + RAM_READONLY_FD | RAM_GUEST_MEMFD | + RAM_RESIZEABLE)) == 0); + assert(max_size >= size); if (xen_enabled()) { error_setg(errp, "-mem-path not supported with Xen"); @@ -1897,12 +2051,18 @@ 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); + max_size = TARGET_PAGE_ALIGN(max_size); + max_size = REAL_HOST_PAGE_ALIGN(max_size); + file_size = get_file_size(fd); - 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); + if (file_size && file_size < offset + max_size && !grow) { + error_setg(errp, "%s backing store size 0x%" PRIx64 + " is too small for 'size' option 0x" RAM_ADDR_FMT + " plus 'offset' option 0x%" PRIx64, + memory_region_name(mr), file_size, max_size, + (uint64_t)offset); return NULL; } @@ -1917,10 +2077,13 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, new_block = g_malloc0(sizeof(*new_block)); new_block->mr = mr; new_block->used_length = size; - new_block->max_length = size; + new_block->max_length = max_size; + new_block->resized = resized; 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, max_size, fd, + file_size < offset + max_size, + offset, errp); if (!new_block->host) { g_free(new_block); return NULL; @@ -1939,20 +2102,41 @@ 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, - off_t offset, 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, offset, readonly, - errp); + block = qemu_ram_alloc_from_fd(size, size, NULL, mr, ram_flags, fd, offset, + false, errp); if (!block) { if (created) { unlink(mem_path); @@ -1965,30 +2149,111 @@ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, } #endif +#ifdef CONFIG_POSIX +/* + * Create MAP_SHARED RAMBlocks by mmap'ing a file descriptor, so it can be + * shared with another process if CPR is being used. Use memfd if available + * because it has no size limits, else use POSIX shm. + */ +static int qemu_ram_get_shared_fd(const char *name, bool *reused, Error **errp) +{ + int fd = cpr_find_fd(name, 0); + + if (fd >= 0) { + *reused = true; + return fd; + } + + if (qemu_memfd_check(0)) { + fd = qemu_memfd_create(name, 0, 0, 0, 0, errp); + } else { + fd = qemu_shm_alloc(0, errp); + } + + if (fd >= 0) { + cpr_save_fd(name, 0, fd); + } + *reused = false; + return fd; +} +#endif + static RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, - void (*resized)(const char*, - uint64_t length, - void *host), + qemu_ram_resize_cb resized, void *host, uint32_t ram_flags, MemoryRegion *mr, Error **errp) { RAMBlock *new_block; Error *local_err = NULL; + int align, share_flags; + + share_flags = ram_flags & (RAM_PRIVATE | RAM_SHARED); + assert(share_flags != (RAM_SHARED | RAM_PRIVATE)); + ram_flags &= ~RAM_PRIVATE; assert((ram_flags & ~(RAM_SHARED | RAM_RESIZEABLE | RAM_PREALLOC | - RAM_NORESERVE)) == 0); + RAM_NORESERVE | RAM_GUEST_MEMFD)) == 0); assert(!host ^ (ram_flags & RAM_PREALLOC)); + assert(max_size >= size); + + /* ignore RAM_SHARED for Windows and emscripten*/ +#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN) + if (!host) { + if (!share_flags && current_machine->aux_ram_share) { + ram_flags |= RAM_SHARED; + } + if (ram_flags & RAM_SHARED) { + bool reused; + g_autofree char *name = cpr_name(mr); + int fd = qemu_ram_get_shared_fd(name, &reused, errp); + + if (fd < 0) { + return NULL; + } + + /* Use same alignment as qemu_anon_ram_alloc */ + mr->align = QEMU_VMALLOC_ALIGN; + + /* + * This can fail if the shm mount size is too small, or alloc from + * fd is not supported, but previous QEMU versions that called + * qemu_anon_ram_alloc for anonymous shared memory could have + * succeeded. Quietly fail and fall back. + * + * After cpr-transfer, new QEMU could create a memory region + * with a larger max size than old, so pass reused to grow the + * region if necessary. The extra space will be usable after a + * guest reset. + */ + new_block = qemu_ram_alloc_from_fd(size, max_size, resized, mr, + ram_flags, fd, 0, reused, NULL); + if (new_block) { + trace_qemu_ram_alloc_shared(name, new_block->used_length, + new_block->max_length, fd, + new_block->host); + return new_block; + } + + cpr_delete_fd(name, 0); + close(fd); + /* fall back to anon allocation */ + } + } +#endif + + align = qemu_real_host_page_size(); + align = MAX(align, TARGET_PAGE_SIZE); + size = ROUND_UP(size, align); + max_size = ROUND_UP(max_size, align); - size = HOST_PAGE_ALIGN(size); - max_size = HOST_PAGE_ALIGN(max_size); new_block = g_malloc0(sizeof(*new_block)); new_block->mr = mr; new_block->resized = resized; new_block->used_length = 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; @@ -2011,15 +2276,14 @@ 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 | + RAM_PRIVATE)) == 0); return qemu_ram_alloc_internal(size, size, NULL, NULL, ram_flags, mr, errp); } RAMBlock *qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz, - void (*resized)(const char*, - uint64_t length, - void *host), - MemoryRegion *mr, Error **errp) + qemu_ram_resize_cb resized, + MemoryRegion *mr, Error **errp) { return qemu_ram_alloc_internal(size, maxsz, resized, NULL, RAM_RESIZEABLE, mr, errp); @@ -2031,7 +2295,7 @@ static void reclaim_ramblock(RAMBlock *block) ; } else if (xen_enabled()) { xen_invalidate_map_cache_entry(block->host); -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(EMSCRIPTEN) } else if (block->fd >= 0) { qemu_ram_munmap(block->fd, block->host, block->max_length); close(block->fd); @@ -2039,11 +2303,20 @@ static void reclaim_ramblock(RAMBlock *block) } else { qemu_anon_ram_free(block->host, block->max_length); } + + if (block->guest_memfd >= 0) { + ram_block_attributes_destroy(block->attributes); + close(block->guest_memfd); + ram_block_coordinated_discard_require(false); + } + g_free(block); } void qemu_ram_free(RAMBlock *block) { + g_autofree char *name = NULL; + if (!block) { return; } @@ -2054,6 +2327,8 @@ void qemu_ram_free(RAMBlock *block) } qemu_mutex_lock_ramlist(); + name = cpr_name(block->mr); + cpr_delete_fd(name, 0); QLIST_REMOVE_RCU(block, next); ram_list.mru_block = NULL; /* Write list before version */ @@ -2064,110 +2339,150 @@ void qemu_ram_free(RAMBlock *block) } #ifndef _WIN32 -void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) +/* Simply remap the given VM memory location from start to start+length */ +static int qemu_ram_remap_mmap(RAMBlock *block, uint64_t start, size_t length) +{ + int flags, prot; + void *area; + void *host_startaddr = block->host + start; + + assert(block->fd < 0); + flags = MAP_FIXED | MAP_ANONYMOUS; + 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; + area = mmap(host_startaddr, length, prot, flags, -1, 0); + return area != host_startaddr ? -errno : 0; +} + +/* + * qemu_ram_remap - remap a single RAM page + * + * @addr: address in ram_addr_t address space. + * + * This function will try remapping a single page of guest RAM identified by + * @addr, essentially discarding memory to recover from previously poisoned + * memory (MCE). The page size depends on the RAMBlock (i.e., hugetlb). @addr + * does not have to point at the start of the page. + * + * This function is only to be used during system resets; it will kill the + * VM if remapping failed. + */ +void qemu_ram_remap(ram_addr_t addr) { RAMBlock *block; - ram_addr_t offset; - int flags; - void *area, *vaddr; + uint64_t offset; + void *vaddr; + size_t page_size; RAMBLOCK_FOREACH(block) { offset = addr - block->offset; if (offset < block->max_length) { + /* Respect the pagesize of our RAMBlock */ + page_size = qemu_ram_pagesize(block); + offset = QEMU_ALIGN_DOWN(offset, page_size); + vaddr = ramblock_ptr(block, offset); if (block->flags & RAM_PREALLOC) { ; } else if (xen_enabled()) { abort(); } else { - flags = MAP_FIXED; - flags |= block->flags & RAM_SHARED ? - MAP_SHARED : MAP_PRIVATE; - flags |= block->flags & RAM_NORESERVE ? MAP_NORESERVE : 0; - if (block->fd >= 0) { - area = mmap(vaddr, length, PROT_READ | PROT_WRITE, - flags, block->fd, offset + block->fd_offset); - } else { - flags |= MAP_ANONYMOUS; - area = mmap(vaddr, length, PROT_READ | PROT_WRITE, - flags, -1, 0); - } - if (area != vaddr) { - error_report("Could not remap addr: " - RAM_ADDR_FMT "@" RAM_ADDR_FMT "", - length, addr); - exit(1); + if (ram_block_discard_range(block, offset, page_size) != 0) { + /* + * Fall back to using mmap() only for anonymous mapping, + * as if a backing file is associated we may not be able + * to recover the memory in all cases. + * So don't take the risk of using only mmap and fail now. + */ + if (block->fd >= 0) { + error_report("Could not remap RAM %s:%" PRIx64 "+%" + PRIx64 " +%zx", block->idstr, offset, + block->fd_offset, page_size); + exit(1); + } + if (qemu_ram_remap_mmap(block, offset, page_size) != 0) { + error_report("Could not remap RAM %s:%" PRIx64 " +%zx", + block->idstr, offset, page_size); + exit(1); + } } - memory_try_enable_merging(vaddr, length); - qemu_ram_setup_dump(vaddr, length); + memory_try_enable_merging(vaddr, page_size); + qemu_ram_setup_dump(vaddr, page_size); } + + break; } } } #endif /* !_WIN32 */ -/* 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. +/* + * 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. */ -void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr) +static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr, + hwaddr *size, bool lock, + bool is_write) { - RAMBlock *block = ram_block; + 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 until the end of the page. + * In that case just map the requested area. */ - if (block->offset == 0) { - return xen_map_cache(addr, 0, 0, false); + 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->offset, block->max_length, 1, false); + 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 guest's ram. Similar to qemu_map_ram_ptr - * but takes a size argument. +/* + * 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. * * Called within RCU critical section. */ -static void *qemu_ram_ptr_length(RAMBlock *ram_block, ram_addr_t addr, - hwaddr *size, bool lock) +void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr) { - 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 */ @@ -2180,23 +2495,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) { @@ -2207,6 +2505,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; @@ -2260,8 +2562,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; @@ -2406,23 +2710,6 @@ static uint16_t dummy_section(PhysPageMap *map, FlatView *fv, MemoryRegion *mr) return phys_section_add(map, §ion); } -MemoryRegionSection *iotlb_to_section(CPUState *cpu, - hwaddr index, MemTxAttrs attrs) -{ - int asidx = cpu_asidx_from_attrs(cpu, attrs); - CPUAddressSpace *cpuas = &cpu->cpu_ases[asidx]; - AddressSpaceDispatch *d = cpuas->memory_dispatch; - int section_index = index & ~TARGET_PAGE_MASK; - MemoryRegionSection *ret; - - 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) { memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL, @@ -2551,7 +2838,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 @@ -2563,7 +2854,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 - 1); + tb_invalidate_phys_range(NULL, addr, addr + length - 1); dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE); } cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask); @@ -2613,8 +2904,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) { @@ -2644,49 +2935,76 @@ static bool flatview_access_allowed(MemoryRegion *mr, MemTxAttrs attrs, if (memory_region_is_ram(mr)) { return true; } - qemu_log_mask(LOG_GUEST_ERROR, + qemu_log_mask(LOG_INVALID_MEM, "Invalid access to non-RAM device at " "addr 0x%" HWADDR_PRIX ", size %" HWADDR_PRIu ", " "region '%s'\n", addr, len, memory_region_name(mr)); 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, attrs)) { + 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; 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); - memmove(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; @@ -2697,7 +3015,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; @@ -2708,52 +3026,76 @@ static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, const void *buf, hwaddr len) { hwaddr l; - hwaddr addr1; + hwaddr mr_addr; MemoryRegion *mr; l = len; - mr = flatview_translate(fv, addr, &addr1, &l, true, attrs); + mr = flatview_translate(fv, addr, &mr_addr, &l, true, attrs); if (!flatview_access_allowed(mr, attrs, addr, len)) { return MEMTX_ACCESS_ERROR; } return flatview_write_continue(fv, addr, attrs, buf, len, - addr1, l, mr); + mr_addr, l, mr); +} + +static MemTxResult flatview_read_continue_step(MemTxAttrs attrs, uint8_t *buf, + hwaddr len, hwaddr mr_addr, + hwaddr *l, + MemoryRegion *mr) +{ + if (!flatview_access_allowed(mr, attrs, mr_addr, *l)) { + return MEMTX_ACCESS_ERROR; + } + + if (!memory_access_is_direct(mr, false, attrs)) { + /* 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; 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; @@ -2764,7 +3106,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; @@ -2775,16 +3117,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, @@ -2877,8 +3219,7 @@ static inline MemTxResult address_space_write_rom_internal(AddressSpace *as, l = len; mr = address_space_translate(as, addr, &addr1, &l, true, attrs); - if (!(memory_region_is_ram(mr) || - memory_region_is_romd(mr))) { + if (!memory_region_supports_direct_access(mr)) { l = memory_access_size(mr, l, addr1); } else { /* ROM/RAM case */ @@ -2926,55 +3267,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); - /* Write map_client_list before reading in_use. */ + QLIST_INSERT_HEAD(&as->map_client_list, client, link); + /* Write map_client_list before reading bounce_buffer_size. */ smp_mb(); - if (!qatomic_read(&bounce.in_use)) { - cpu_notify_map_clients_locked(); + 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) @@ -2990,28 +3326,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, @@ -3023,7 +3356,7 @@ static bool flatview_access_valid(FlatView *fv, hwaddr addr, hwaddr len, while (len > 0) { l = len; mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs); - if (!memory_access_is_direct(mr, is_write)) { + if (!memory_access_is_direct(mr, is_write, attrs)) { l = memory_access_size(mr, l, addr); if (!memory_region_access_valid(mr, xlat, l, is_write, attrs)) { return false; @@ -3078,8 +3411,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, @@ -3092,6 +3425,8 @@ void *address_space_map(AddressSpace *as, MemoryRegion *mr; FlatView *fv; + trace_address_space_map(as, addr, len, is_write, *(uint32_t *) &attrs); + if (len == 0) { return NULL; } @@ -3101,34 +3436,46 @@ void *address_space_map(AddressSpace *as, fv = address_space_to_flatview(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)) { + if (!memory_access_is_direct(mr, is_write, attrs)) { + 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); - return qemu_ram_ptr_length(mr->ram_block, xlat, plen, true); + return qemu_ram_ptr_length(mr->ram_block, xlat, plen, true, is_write); } /* Unmaps a memory region previously mapped by address_space_map(). @@ -3138,12 +3485,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); } @@ -3153,16 +3499,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); - /* Clear in_use before reading map_client_list. */ - qatomic_set_mb(&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, @@ -3216,7 +3569,7 @@ int64_t address_space_cache_init(MemoryRegionCache *cache, mr = cache->mrs.mr; memory_region_ref(mr); - if (memory_access_is_direct(mr, is_write)) { + if (memory_access_is_direct(mr, is_write, MEMTXATTRS_UNSPECIFIED)) { /* We don't care about the memory attributes here as we're only * doing this if we found actual RAM, which behaves the same * regardless of attributes; so UNSPECIFIED is fine. @@ -3224,7 +3577,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; } @@ -3289,6 +3643,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. */ @@ -3296,15 +3703,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 @@ -3314,15 +3720,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 @@ -3357,13 +3762,8 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, if (l > len) l = len; phys_addr += (addr & ~TARGET_PAGE_MASK); - if (is_write) { - res = address_space_write_rom(cpu->cpu_ases[asidx].as, phys_addr, - attrs, buf, l); - } else { - res = address_space_read(cpu->cpu_ases[asidx].as, phys_addr, - attrs, buf, l); - } + res = address_space_rw(cpu->cpu_ases[asidx].as, phys_addr, attrs, buf, + l, is_write); if (res != MEMTX_OK) { return -1; } @@ -3374,41 +3774,6 @@ 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_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); -} - bool cpu_physical_memory_is_io(hwaddr phys_addr) { MemoryRegion*mr; @@ -3452,16 +3817,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; } @@ -3472,7 +3836,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 @@ -3480,6 +3844,16 @@ 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 @@ -3491,27 +3865,27 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) * file. */ if (!qemu_ram_is_shared(rb)) { - warn_report_once("ram_block_discard_range: Discarding RAM" + 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"); + " users of the file", __func__); } ret = fallocate(rb->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, - start, length); + start + rb->fd_offset, 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 "+%" PRIx64 + " +%zx (%d)", __func__, rb->idstr, start, + rb->fd_offset, length, ret); goto err; } #else ret = -ENOSYS; - error_report("ram_block_discard_range: fallocate not available/file" - "%s:%" PRIx64 " +%zx (%d)", - rb->idstr, start, length, ret); + error_report("%s: fallocate not available/file" + "%s:%" PRIx64 "+%" PRIx64 " +%zx (%d)", __func__, + rb->idstr, start, rb->fd_offset, length, ret); goto err; #endif } @@ -3529,31 +3903,53 @@ 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 + /* ignore fd_offset with guest_memfd */ + 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; @@ -3741,3 +4137,58 @@ bool ram_block_discard_is_required(void) return qatomic_read(&ram_block_discard_required_cnt) || qatomic_read(&ram_block_coordinated_discard_required_cnt); } + +/* + * Return true if ram is compatible with CPR. Do not exclude rom, + * because the rom file could change in new QEMU. + */ +static bool ram_is_cpr_compatible(RAMBlock *rb) +{ + MemoryRegion *mr = rb->mr; + + if (!mr || !memory_region_is_ram(mr)) { + return true; + } + + /* Ram device is remapped in new QEMU */ + if (memory_region_is_ram_device(mr)) { + return true; + } + + /* + * A file descriptor is passed to new QEMU and remapped, or its backing + * file is reopened and mapped. It must be shared to avoid COW. + */ + if (rb->fd >= 0 && qemu_ram_is_shared(rb)) { + return true; + } + + return false; +} + +/* + * Add a blocker for each volatile ram block. This function should only be + * called after we know that the block is migratable. Non-migratable blocks + * are either re-created in new QEMU, or are handled specially, or are covered + * by a device-level CPR blocker. + */ +void ram_block_add_cpr_blocker(RAMBlock *rb, Error **errp) +{ + assert(qemu_ram_is_migratable(rb)); + + if (ram_is_cpr_compatible(rb)) { + return; + } + + error_setg(&rb->cpr_blocker, + "Memory region %s is not compatible with CPR. share=on is " + "required for memory-backend objects, and aux-ram-share=on is " + "required.", memory_region_name(rb->mr)); + migrate_add_blocker_modes(&rb->cpr_blocker, errp, MIG_MODE_CPR_TRANSFER, + -1); +} + +void ram_block_del_cpr_blocker(RAMBlock *rb) +{ + migrate_del_blocker(&rb->cpr_blocker); +} diff --git a/softmmu/qdev-monitor.c b/system/qdev-monitor.c similarity index 84% rename from softmmu/qdev-monitor.c rename to system/qdev-monitor.c index 74f4e413384..5588ed2047d 100644 --- a/softmmu/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -22,13 +22,14 @@ #include "monitor/hmp.h" #include "monitor/monitor.h" #include "monitor/qdev.h" -#include "sysemu/arch_init.h" +#include "system/arch_init.h" +#include "system/runstate.h" #include "qapi/error.h" #include "qapi/qapi-commands-qdev.h" -#include "qapi/qmp/dispatch.h" -#include "qapi/qmp/qdict.h" +#include "qapi/qmp-registry.h" +#include "qobject/qdict.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -36,9 +37,8 @@ #include "qemu/option.h" #include "qemu/qemu-print.h" #include "qemu/option_int.h" -#include "sysemu/block-backend.h" +#include "system/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 }, @@ -121,7 +132,7 @@ static const char *qdev_class_get_alias(DeviceClass *dc) for (i = 0; qdev_alias_table[i].typename; i++) { if (qdev_alias_table[i].arch_mask && - !(qdev_alias_table[i].arch_mask & arch_type)) { + !qemu_arch_available(qdev_alias_table[i].arch_mask)) { continue; } @@ -207,7 +218,7 @@ static const char *find_typename_by_alias(const char *alias) for (i = 0; qdev_alias_table[i].alias; i++) { if (qdev_alias_table[i].arch_mask && - !(qdev_alias_table[i].arch_mask & arch_type)) { + !qemu_arch_available(qdev_alias_table[i].arch_mask)) { continue; } @@ -252,8 +263,7 @@ static DeviceClass *qdev_get_device_class(const char **driver, Error **errp) } dc = DEVICE_CLASS(oc); - if (!dc->user_creatable || - (phase_check(PHASE_MACHINE_READY) && !dc->hotpluggable)) { + if (!dc->user_creatable) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver", "a pluggable device type"); return NULL; @@ -337,7 +347,7 @@ static Object *qdev_get_peripheral(void) static Object *dev; if (dev == NULL) { - dev = container_get(qdev_get_machine(), "/peripheral"); + dev = machine_get_container("peripheral"); } return dev; @@ -348,7 +358,7 @@ static Object *qdev_get_peripheral_anon(void) static Object *dev; if (dev == NULL) { - dev = container_get(qdev_get_machine(), "/peripheral-anon"); + dev = machine_get_container("peripheral-anon"); } return dev; @@ -620,6 +630,7 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, char *id; DeviceState *dev = NULL; BusState *bus = NULL; + QDict *properties; driver = qdict_get_try_str(opts, "driver"); if (!driver) { @@ -656,19 +667,15 @@ 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) { return NULL; } - if (phase_check(PHASE_MACHINE_READY) && bus && !qbus_is_hotpluggable(bus)) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name); - return NULL; - } - - if (!migration_is_idle()) { + if (migration_is_running()) { error_setg(errp, "device_add not allowed while migrating"); return NULL; } @@ -677,17 +684,9 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, dev = qdev_new(driver); /* Check whether the hotplug is allowed by the machine */ - if (phase_check(PHASE_MACHINE_READY)) { - if (!qdev_hotplug_allowed(dev, errp)) { - goto err_del_dev; - } - - if (!bus && !qdev_get_machine_hotplug_handler(dev)) { - /* No bus, no machine hotplug handler --> device is not hotpluggable */ - error_setg(errp, "Device '%s' can not be hotplugged on this machine", - driver); - goto err_del_dev; - } + if (phase_check(PHASE_MACHINE_READY) && + !qdev_hotplug_allowed(dev, bus, errp)) { + goto err_del_dev; } /* @@ -700,13 +699,14 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, } /* set properties */ - dev->opts = qdict_clone_shallow(opts); - qdict_del(dev->opts, "driver"); - qdict_del(dev->opts, "bus"); - qdict_del(dev->opts, "id"); + properties = qdict_clone_shallow(opts); + qdict_del(properties, "driver"); + qdict_del(properties, "bus"); + qdict_del(properties, "id"); - object_set_properties_from_keyval(&dev->parent_obj, dev->opts, from_json, + object_set_properties_from_keyval(&dev->parent_obj, properties, from_json, errp); + qobject_unref(properties); if (*errp) { goto err_del_dev; } @@ -739,21 +739,19 @@ 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, DeviceClass *dc, int indent) { - if (!props) - return; - for (; props->name; props++) { + for (int i = 0, n = dc->props_count_; i < n; ++i) { + const Property *prop = &dc->props_[i]; char *value; - char *legacy_name = g_strdup_printf("legacy-%s", props->name); + char *legacy_name = g_strdup_printf("legacy-%s", prop->name); if (object_property_get_type(OBJECT(dev), legacy_name, NULL)) { value = object_property_get_str(OBJECT(dev), legacy_name, NULL); } else { - value = object_property_print(OBJECT(dev), props->name, true, + value = object_property_print(OBJECT(dev), prop->name, true, NULL); } g_free(legacy_name); @@ -761,7 +759,7 @@ static void qdev_print_props(Monitor *mon, DeviceState *dev, Property *props, if (!value) { continue; } - qdev_printf("%s = %s\n", props->name, + qdev_printf("%s = %s\n", prop->name, *value ? value : ""); g_free(value); } @@ -779,13 +777,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 : "", @@ -805,16 +799,13 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent) } class = object_get_class(OBJECT(dev)); do { - qdev_print_props(mon, dev, DEVICE_CLASS(class)->props_, indent); + qdev_print_props(mon, dev, DEVICE_CLASS(class), 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 +813,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 +843,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; } @@ -894,27 +890,15 @@ static DeviceState *find_device_state(const char *id, Error **errp) void qdev_unplug(DeviceState *dev, Error **errp) { - DeviceClass *dc = DEVICE_GET_CLASS(dev); HotplugHandler *hotplug_ctrl; HotplugHandlerClass *hdc; Error *local_err = NULL; - if (qdev_unplug_blocked(dev, errp)) { - return; - } - - if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); - return; - } - - if (!dc->hotpluggable) { - error_setg(errp, QERR_DEVICE_NO_HOTPLUG, - object_get_typename(OBJECT(dev))); + if (!qdev_hotunplug_allowed(dev, errp)) { 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 +926,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 +940,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); } @@ -1026,7 +1073,7 @@ static GSList *qdev_build_hotpluggable_device_list(Object *peripheral) static void peripheral_device_del_completion(ReadLineState *rs, const char *str) { - Object *peripheral = container_get(qdev_get_machine(), "/peripheral"); + Object *peripheral = machine_get_container("peripheral"); GSList *list, *item; list = qdev_build_hotpluggable_device_list(peripheral); @@ -1062,7 +1109,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 d66a2a12262..f8e1238b914 100644 --- a/softmmu/qemu-seccomp.c +++ b/system/qemu-seccomp.c @@ -20,7 +20,7 @@ #include "qemu/module.h" #include #include -#include "sysemu/seccomp.h" +#include "system/seccomp.h" #include /* For some architectures (notably ARM) cacheflush is not supported until @@ -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 35b643a274c..301b03be2d3 100644 --- a/softmmu/qtest.c +++ b/system/qtest.c @@ -13,16 +13,17 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/qtest.h" -#include "sysemu/runstate.h" +#include "system/qtest.h" +#include "system/runstate.h" #include "chardev/char-fe.h" -#include "exec/ioport.h" -#include "exec/memory.h" +#include "system/ioport.h" +#include "system/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 "system/cpu-timers.h" #include "qemu/config-file.h" #include "qemu/option.h" #include "qemu/error-report.h" @@ -77,6 +78,11 @@ static void *qtest_server_send_opaque; * let you adjust the value of the clock (monotonically). All the commands * return the current value of the clock in nanoseconds. * + * If the commands FAIL then time wasn't advanced which is likely + * because the machine was in a paused state or no timer events exist + * in the future. This will cause qtest to abort and the test will + * need to check its assumptions. + * * .. code-block:: none * * > clock_step @@ -259,7 +265,7 @@ static int hex2nib(char ch) } } -void qtest_send_prefix(CharBackend *chr) +static void qtest_log_timestamp(void) { if (!qtest_log_fp || !qtest_opened) { return; @@ -276,7 +282,7 @@ static void G_GNUC_PRINTF(1, 2) qtest_log_send(const char *fmt, ...) return; } - qtest_send_prefix(NULL); + qtest_log_timestamp(); va_start(ap, fmt); vfprintf(qtest_log_fp, fmt, ap); @@ -295,6 +301,7 @@ static void qtest_server_char_be_send(void *opaque, const char *str) static void qtest_send(CharBackend *chr, const char *str) { + qtest_log_timestamp(); qtest_server_send(qtest_server_send_opaque, str); } @@ -318,44 +325,11 @@ static void qtest_irq_handler(void *opaque, int n, int level) if (irq_levels[n] != level) { CharBackend *chr = &qtest->qtest_chr; irq_levels[n] = level; - qtest_send_prefix(chr); qtest_sendf(chr, "IRQ %s %d\n", level ? "raise" : "lower", n); } } -static int64_t qtest_clock_counter; - -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 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); -} - static bool (*process_command_cb)(CharBackend *chr, gchar **words); void qtest_set_command_cb(bool (*pc_cb)(CharBackend *chr, gchar **words)) @@ -406,19 +380,16 @@ static void qtest_process_command(CharBackend *chr, gchar **words) is_outbound = words[0][14] == 'o'; dev = DEVICE(object_resolve_path(words[1], NULL)); if (!dev) { - qtest_send_prefix(chr); qtest_send(chr, "FAIL Unknown device\n"); 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) { qtest_send(chr, "FAIL IRQ intercept already enabled\n"); } else { @@ -445,7 +416,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) } } - qtest_send_prefix(chr); if (interception_succeeded) { irq_intercept_dev = dev; qtest_send(chr, "OK\n"); @@ -464,7 +434,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) dev = DEVICE(object_resolve_path(words[1], NULL)); if (!dev) { - qtest_send_prefix(chr); qtest_send(chr, "FAIL Unknown device\n"); return; } @@ -483,7 +452,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) irq = qdev_get_gpio_in_named(dev, name, num); qemu_set_irq(irq, level); - qtest_send_prefix(chr); qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "outb") == 0 || strcmp(words[0], "outw") == 0 || @@ -506,7 +474,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) } else if (words[0][3] == 'l') { cpu_outl(addr, value); } - qtest_send_prefix(chr); qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "inb") == 0 || strcmp(words[0], "inw") == 0 || @@ -527,7 +494,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) } else if (words[0][2] == 'l') { value = cpu_inl(addr); } - qtest_send_prefix(chr); qtest_sendf(chr, "OK 0x%04x\n", value); } else if (strcmp(words[0], "writeb") == 0 || strcmp(words[0], "writew") == 0 || @@ -563,7 +529,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, &data, 8); } - qtest_send_prefix(chr); qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "readb") == 0 || strcmp(words[0], "readw") == 0 || @@ -597,12 +562,11 @@ static void qtest_process_command(CharBackend *chr, gchar **words) &value, 8); tswap64s(&value); } - 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]); @@ -617,16 +581,11 @@ 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; @@ -643,7 +602,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data, len); b64_data = g_base64_encode(data, len); - qtest_send_prefix(chr); qtest_sendf(chr, "OK %s\n", b64_data); g_free(data); @@ -679,7 +637,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) len); g_free(data); - qtest_send_prefix(chr); qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "memset") == 0) { uint64_t addr, len; @@ -703,7 +660,6 @@ static void qtest_process_command(CharBackend *chr, gchar **words) g_free(data); } - qtest_send_prefix(chr); qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "b64write") == 0) { uint64_t addr, len; @@ -735,17 +691,16 @@ static void qtest_process_command(CharBackend *chr, gchar **words) address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data, len); - qtest_send_prefix(chr); qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "endianness") == 0) { - qtest_send_prefix(chr); - if (target_words_bigendian()) { + if (target_big_endian()) { 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; + int64_t old_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t ns, new_ns; if (words[1]) { int ret = qemu_strtoi64(words[1], NULL, 0, &ns); @@ -753,17 +708,24 @@ static void qtest_process_command(CharBackend *chr, gchar **words) } else { ns = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, QEMU_TIMER_ATTR_ALL); + if (ns < 0) { + qtest_send(chr, "FAIL " + "cannot advance clock to the next deadline " + "because there is no pending deadline\n"); + return; + } + } + new_ns = qemu_clock_advance_virtual_time(old_ns + ns); + if (new_ns > old_ns) { + qtest_sendf(chr, "OK %"PRIi64"\n", new_ns); + } else { + qtest_sendf(chr, "FAIL could not advance time\n"); } - qtest_clock_warp(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)); } else if (strcmp(words[0], "module_load") == 0) { Error *local_err = NULL; int rv; g_assert(words[1] && words[2]); - qtest_send_prefix(chr); rv = module_load(words[1], words[2], &local_err); if (rv > 0) { qtest_sendf(chr, "OK\n"); @@ -774,43 +736,37 @@ static void qtest_process_command(CharBackend *chr, gchar **words) qtest_sendf(chr, "FAIL\n"); } } else if (qtest_enabled() && strcmp(words[0], "clock_set") == 0) { - int64_t ns; + int64_t ns, new_ns; int ret; g_assert(words[1]); ret = qemu_strtoi64(words[1], NULL, 0, &ns); g_assert(ret == 0); - qtest_clock_warp(ns); - qtest_send_prefix(chr); - qtest_sendf(chr, "OK %"PRIi64"\n", - (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + new_ns = qemu_clock_advance_virtual_time(ns); + qtest_sendf(chr, "%s %"PRIi64"\n", + new_ns == ns ? "OK" : "FAIL", new_ns); } 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]); } } +/* + * Process as much of @inbuf as we can in newline terminated chunks. + * Remove the processed commands from @inbuf as we go. + */ static void qtest_process_inbuf(CharBackend *chr, GString *inbuf) { char *end; while ((end = strchr(inbuf->str, '\n')) != NULL) { - size_t offset; - GString *cmd; - gchar **words; + size_t len = end - inbuf->str; + g_autofree char *cmd = g_strndup(inbuf->str, len); + g_auto(GStrv) words = g_strsplit(cmd, " ", 0); - offset = end - inbuf->str; - - cmd = g_string_new_len(inbuf->str, offset); - g_string_erase(inbuf, 0, offset + 1); - - words = g_strsplit(cmd->str, " ", 0); + g_string_erase(inbuf, 0, len + 1); qtest_process_command(chr, words); - g_strfreev(words); - - g_string_free(cmd, TRUE); } } @@ -866,7 +822,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) { @@ -875,18 +831,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", chr->label, &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) @@ -1038,7 +994,7 @@ static char *qtest_get_chardev(Object *obj, Error **errp) return g_strdup(q->chr_name); } -static void qtest_class_init(ObjectClass *oc, void *data) +static void qtest_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); @@ -1056,7 +1012,7 @@ static const TypeInfo qtest_info = { .parent = TYPE_OBJECT, .class_init = qtest_class_init, .instance_size = sizeof(QTest), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/system/ram-block-attributes.c b/system/ram-block-attributes.c new file mode 100644 index 00000000000..68e8a027032 --- /dev/null +++ b/system/ram-block-attributes.c @@ -0,0 +1,444 @@ +/* + * QEMU ram block attributes + * + * Copyright Intel + * + * Author: + * Chenyi Qiang + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "system/ramblock.h" +#include "trace.h" + +OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RamBlockAttributes, + ram_block_attributes, + RAM_BLOCK_ATTRIBUTES, + OBJECT, + { TYPE_RAM_DISCARD_MANAGER }, + { }) + +static size_t +ram_block_attributes_get_block_size(const RamBlockAttributes *attr) +{ + /* + * Because page conversion could be manipulated in the size of at least 4K + * or 4K aligned, Use the host page size as the granularity to track the + * memory attribute. + */ + g_assert(attr && attr->ram_block); + g_assert(attr->ram_block->page_size == qemu_real_host_page_size()); + return attr->ram_block->page_size; +} + + +static bool +ram_block_attributes_rdm_is_populated(const RamDiscardManager *rdm, + const MemoryRegionSection *section) +{ + const RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm); + const size_t block_size = ram_block_attributes_get_block_size(attr); + const uint64_t first_bit = section->offset_within_region / block_size; + const uint64_t last_bit = + first_bit + int128_get64(section->size) / block_size - 1; + unsigned long first_discarded_bit; + + first_discarded_bit = find_next_zero_bit(attr->bitmap, last_bit + 1, + first_bit); + return first_discarded_bit > last_bit; +} + +typedef int (*ram_block_attributes_section_cb)(MemoryRegionSection *s, + void *arg); + +static int +ram_block_attributes_notify_populate_cb(MemoryRegionSection *section, + void *arg) +{ + RamDiscardListener *rdl = arg; + + return rdl->notify_populate(rdl, section); +} + +static int +ram_block_attributes_notify_discard_cb(MemoryRegionSection *section, + void *arg) +{ + RamDiscardListener *rdl = arg; + + rdl->notify_discard(rdl, section); + return 0; +} + +static int +ram_block_attributes_for_each_populated_section(const RamBlockAttributes *attr, + MemoryRegionSection *section, + void *arg, + ram_block_attributes_section_cb cb) +{ + unsigned long first_bit, last_bit; + uint64_t offset, size; + const size_t block_size = ram_block_attributes_get_block_size(attr); + int ret = 0; + + first_bit = section->offset_within_region / block_size; + first_bit = find_next_bit(attr->bitmap, attr->bitmap_size, + first_bit); + + while (first_bit < attr->bitmap_size) { + MemoryRegionSection tmp = *section; + + offset = first_bit * block_size; + last_bit = find_next_zero_bit(attr->bitmap, attr->bitmap_size, + first_bit + 1) - 1; + size = (last_bit - first_bit + 1) * block_size; + + if (!memory_region_section_intersect_range(&tmp, offset, size)) { + break; + } + + ret = cb(&tmp, arg); + if (ret) { + error_report("%s: Failed to notify RAM discard listener: %s", + __func__, strerror(-ret)); + break; + } + + first_bit = find_next_bit(attr->bitmap, attr->bitmap_size, + last_bit + 2); + } + + return ret; +} + +static int +ram_block_attributes_for_each_discarded_section(const RamBlockAttributes *attr, + MemoryRegionSection *section, + void *arg, + ram_block_attributes_section_cb cb) +{ + unsigned long first_bit, last_bit; + uint64_t offset, size; + const size_t block_size = ram_block_attributes_get_block_size(attr); + int ret = 0; + + first_bit = section->offset_within_region / block_size; + first_bit = find_next_zero_bit(attr->bitmap, attr->bitmap_size, + first_bit); + + while (first_bit < attr->bitmap_size) { + MemoryRegionSection tmp = *section; + + offset = first_bit * block_size; + last_bit = find_next_bit(attr->bitmap, attr->bitmap_size, + first_bit + 1) - 1; + size = (last_bit - first_bit + 1) * block_size; + + if (!memory_region_section_intersect_range(&tmp, offset, size)) { + break; + } + + ret = cb(&tmp, arg); + if (ret) { + error_report("%s: Failed to notify RAM discard listener: %s", + __func__, strerror(-ret)); + break; + } + + first_bit = find_next_zero_bit(attr->bitmap, + attr->bitmap_size, + last_bit + 2); + } + + return ret; +} + +static uint64_t +ram_block_attributes_rdm_get_min_granularity(const RamDiscardManager *rdm, + const MemoryRegion *mr) +{ + const RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm); + + g_assert(mr == attr->ram_block->mr); + return ram_block_attributes_get_block_size(attr); +} + +static void +ram_block_attributes_rdm_register_listener(RamDiscardManager *rdm, + RamDiscardListener *rdl, + MemoryRegionSection *section) +{ + RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm); + int ret; + + g_assert(section->mr == attr->ram_block->mr); + rdl->section = memory_region_section_new_copy(section); + + QLIST_INSERT_HEAD(&attr->rdl_list, rdl, next); + + ret = ram_block_attributes_for_each_populated_section(attr, section, rdl, + ram_block_attributes_notify_populate_cb); + if (ret) { + error_report("%s: Failed to register RAM discard listener: %s", + __func__, strerror(-ret)); + exit(1); + } +} + +static void +ram_block_attributes_rdm_unregister_listener(RamDiscardManager *rdm, + RamDiscardListener *rdl) +{ + RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm); + int ret; + + g_assert(rdl->section); + g_assert(rdl->section->mr == attr->ram_block->mr); + + if (rdl->double_discard_supported) { + rdl->notify_discard(rdl, rdl->section); + } else { + ret = ram_block_attributes_for_each_populated_section(attr, + rdl->section, rdl, ram_block_attributes_notify_discard_cb); + if (ret) { + error_report("%s: Failed to unregister RAM discard listener: %s", + __func__, strerror(-ret)); + exit(1); + } + } + + memory_region_section_free_copy(rdl->section); + rdl->section = NULL; + QLIST_REMOVE(rdl, next); +} + +typedef struct RamBlockAttributesReplayData { + ReplayRamDiscardState fn; + void *opaque; +} RamBlockAttributesReplayData; + +static int ram_block_attributes_rdm_replay_cb(MemoryRegionSection *section, + void *arg) +{ + RamBlockAttributesReplayData *data = arg; + + return data->fn(section, data->opaque); +} + +static int +ram_block_attributes_rdm_replay_populated(const RamDiscardManager *rdm, + MemoryRegionSection *section, + ReplayRamDiscardState replay_fn, + void *opaque) +{ + RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm); + RamBlockAttributesReplayData data = { .fn = replay_fn, .opaque = opaque }; + + g_assert(section->mr == attr->ram_block->mr); + return ram_block_attributes_for_each_populated_section(attr, section, &data, + ram_block_attributes_rdm_replay_cb); +} + +static int +ram_block_attributes_rdm_replay_discarded(const RamDiscardManager *rdm, + MemoryRegionSection *section, + ReplayRamDiscardState replay_fn, + void *opaque) +{ + RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm); + RamBlockAttributesReplayData data = { .fn = replay_fn, .opaque = opaque }; + + g_assert(section->mr == attr->ram_block->mr); + return ram_block_attributes_for_each_discarded_section(attr, section, &data, + ram_block_attributes_rdm_replay_cb); +} + +static bool +ram_block_attributes_is_valid_range(RamBlockAttributes *attr, uint64_t offset, + uint64_t size) +{ + MemoryRegion *mr = attr->ram_block->mr; + + g_assert(mr); + + uint64_t region_size = memory_region_size(mr); + const size_t block_size = ram_block_attributes_get_block_size(attr); + + if (!QEMU_IS_ALIGNED(offset, block_size) || + !QEMU_IS_ALIGNED(size, block_size)) { + return false; + } + if (offset + size <= offset) { + return false; + } + if (offset + size > region_size) { + return false; + } + return true; +} + +static void ram_block_attributes_notify_discard(RamBlockAttributes *attr, + uint64_t offset, + uint64_t size) +{ + RamDiscardListener *rdl; + + QLIST_FOREACH(rdl, &attr->rdl_list, next) { + MemoryRegionSection tmp = *rdl->section; + + if (!memory_region_section_intersect_range(&tmp, offset, size)) { + continue; + } + rdl->notify_discard(rdl, &tmp); + } +} + +static int +ram_block_attributes_notify_populate(RamBlockAttributes *attr, + uint64_t offset, uint64_t size) +{ + RamDiscardListener *rdl; + int ret = 0; + + QLIST_FOREACH(rdl, &attr->rdl_list, next) { + MemoryRegionSection tmp = *rdl->section; + + if (!memory_region_section_intersect_range(&tmp, offset, size)) { + continue; + } + ret = rdl->notify_populate(rdl, &tmp); + if (ret) { + break; + } + } + + return ret; +} + +int ram_block_attributes_state_change(RamBlockAttributes *attr, + uint64_t offset, uint64_t size, + bool to_discard) +{ + const size_t block_size = ram_block_attributes_get_block_size(attr); + const unsigned long first_bit = offset / block_size; + const unsigned long nbits = size / block_size; + const unsigned long last_bit = first_bit + nbits - 1; + const bool is_discarded = find_next_bit(attr->bitmap, attr->bitmap_size, + first_bit) > last_bit; + const bool is_populated = find_next_zero_bit(attr->bitmap, + attr->bitmap_size, first_bit) > last_bit; + unsigned long bit; + int ret = 0; + + if (!ram_block_attributes_is_valid_range(attr, offset, size)) { + error_report("%s, invalid range: offset 0x%" PRIx64 ", size " + "0x%" PRIx64, __func__, offset, size); + return -EINVAL; + } + + trace_ram_block_attributes_state_change(offset, size, + is_discarded ? "discarded" : + is_populated ? "populated" : + "mixture", + to_discard ? "discarded" : + "populated"); + if (to_discard) { + if (is_discarded) { + /* Already private */ + } else if (is_populated) { + /* Completely shared */ + bitmap_clear(attr->bitmap, first_bit, nbits); + ram_block_attributes_notify_discard(attr, offset, size); + } else { + /* Unexpected mixture: process individual blocks */ + for (bit = first_bit; bit < first_bit + nbits; bit++) { + if (!test_bit(bit, attr->bitmap)) { + continue; + } + clear_bit(bit, attr->bitmap); + ram_block_attributes_notify_discard(attr, bit * block_size, + block_size); + } + } + } else { + if (is_populated) { + /* Already shared */ + } else if (is_discarded) { + /* Completely private */ + bitmap_set(attr->bitmap, first_bit, nbits); + ret = ram_block_attributes_notify_populate(attr, offset, size); + } else { + /* Unexpected mixture: process individual blocks */ + for (bit = first_bit; bit < first_bit + nbits; bit++) { + if (test_bit(bit, attr->bitmap)) { + continue; + } + set_bit(bit, attr->bitmap); + ret = ram_block_attributes_notify_populate(attr, + bit * block_size, + block_size); + if (ret) { + break; + } + } + } + } + + return ret; +} + +RamBlockAttributes *ram_block_attributes_create(RAMBlock *ram_block) +{ + const int block_size = qemu_real_host_page_size(); + RamBlockAttributes *attr; + MemoryRegion *mr = ram_block->mr; + + attr = RAM_BLOCK_ATTRIBUTES(object_new(TYPE_RAM_BLOCK_ATTRIBUTES)); + + attr->ram_block = ram_block; + if (memory_region_set_ram_discard_manager(mr, RAM_DISCARD_MANAGER(attr))) { + object_unref(OBJECT(attr)); + return NULL; + } + attr->bitmap_size = + ROUND_UP(int128_get64(mr->size), block_size) / block_size; + attr->bitmap = bitmap_new(attr->bitmap_size); + + return attr; +} + +void ram_block_attributes_destroy(RamBlockAttributes *attr) +{ + g_assert(attr); + + g_free(attr->bitmap); + memory_region_set_ram_discard_manager(attr->ram_block->mr, NULL); + object_unref(OBJECT(attr)); +} + +static void ram_block_attributes_init(Object *obj) +{ + RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(obj); + + QLIST_INIT(&attr->rdl_list); +} + +static void ram_block_attributes_finalize(Object *obj) +{ +} + +static void ram_block_attributes_class_init(ObjectClass *klass, + const void *data) +{ + RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_CLASS(klass); + + rdmc->get_min_granularity = ram_block_attributes_rdm_get_min_granularity; + rdmc->register_listener = ram_block_attributes_rdm_register_listener; + rdmc->unregister_listener = ram_block_attributes_rdm_unregister_listener; + rdmc->is_populated = ram_block_attributes_rdm_is_populated; + rdmc->replay_populated = ram_block_attributes_rdm_replay_populated; + rdmc->replay_discarded = ram_block_attributes_rdm_replay_discarded; +} diff --git a/softmmu/rtc.c b/system/rtc.c similarity index 97% rename from softmmu/rtc.c rename to system/rtc.c index 4904581abeb..56951288c40 100644 --- a/softmmu/rtc.c +++ b/system/rtc.c @@ -25,14 +25,13 @@ #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" #include "qom/object.h" -#include "sysemu/replay.h" -#include "sysemu/sysemu.h" -#include "sysemu/rtc.h" +#include "system/replay.h" +#include "system/system.h" +#include "system/rtc.h" #include "hw/rtc/mc146818rtc.h" static enum { @@ -63,7 +62,7 @@ static time_t qemu_ref_timedate(QEMUClockType clock) } break; default: - assert(0); + g_assert_not_reached(); } return value; } diff --git a/softmmu/runstate-action.c b/system/runstate-action.c similarity index 94% rename from softmmu/runstate-action.c rename to system/runstate-action.c index ae0761a9c3a..f912bc837f2 100644 --- a/softmmu/runstate-action.c +++ b/system/runstate-action.c @@ -7,8 +7,8 @@ */ #include "qemu/osdep.h" -#include "sysemu/runstate-action.h" -#include "sysemu/watchdog.h" +#include "system/runstate-action.h" +#include "system/watchdog.h" #include "qemu/config-file.h" #include "qapi/error.h" #include "qemu/option_int.h" diff --git a/softmmu/runstate-hmp-cmds.c b/system/runstate-hmp-cmds.c similarity index 98% rename from softmmu/runstate-hmp-cmds.c rename to system/runstate-hmp-cmds.c index 2df670f0c06..be1d6769926 100644 --- a/softmmu/runstate-hmp-cmds.c +++ b/system/runstate-hmp-cmds.c @@ -19,7 +19,7 @@ #include "monitor/monitor.h" #include "qapi/error.h" #include "qapi/qapi-commands-run-state.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/accel.h" void hmp_info_status(Monitor *mon, const QDict *qdict) diff --git a/softmmu/runstate.c b/system/runstate.c similarity index 80% rename from softmmu/runstate.c rename to system/runstate.c index 1652ed0439b..38900c935a3 100644 --- a/softmmu/runstate.c +++ b/system/runstate.c @@ -32,6 +32,7 @@ #include "exec/cpu-common.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" @@ -45,20 +46,19 @@ #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" -#include "sysemu/cpus.h" -#include "sysemu/qtest.h" -#include "sysemu/replay.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/runstate-action.h" -#include "sysemu/sysemu.h" -#include "sysemu/tpm.h" +#include "system/cpus.h" +#include "system/qtest.h" +#include "system/replay.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/runstate-action.h" +#include "system/system.h" +#include "system/tpm.h" #include "trace.h" static NotifierList exit_notifiers = @@ -77,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 }, @@ -108,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 }, @@ -131,6 +133,7 @@ static const RunStateTransition runstate_transitions_def[] = { { 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 }, @@ -149,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 }, @@ -161,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 }, @@ -174,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) @@ -181,14 +195,33 @@ bool runstate_check(RunState state) return current_run_state == state; } -static void runstate_init(void) +static void transitions_set_valid(const RunStateTransition *rst) { const RunStateTransition *p; - memset(&runstate_valid_transitions, 0, sizeof(runstate_valid_transitions)); - for (p = &runstate_transitions_def[0]; p->from != RUN_STATE__MAX; p++) { + 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]); + } +} + +static void runstate_init(void) +{ + memset(&runstate_valid_transitions, 0, sizeof(runstate_valid_transitions)); + + transitions_set_valid(&runstate_transitions_def[0]); qemu_mutex_init(&vmstop_lock); } @@ -234,15 +267,7 @@ bool runstate_needs_reset(void) StatusInfo *qmp_query_status(Error **errp) { StatusInfo *info = g_malloc0(sizeof(*info)); - AccelState *accel = current_accel(); - /* - * We ignore errors, which will happen if the accelerator - * is not TCG. "singlestep" is meaningless for other accelerators, - * so we will set the StatusInfo field to false for those. - */ - info->singlestep = object_property_get_bool(OBJECT(accel), - "one-insn-per-tb", NULL); info->running = runstate_is_running(); info->status = current_run_state; @@ -272,6 +297,7 @@ void qemu_system_vmstop_request(RunState state) struct VMChangeStateEntry { VMChangeStateHandler *cb; VMChangeStateHandler *prepare_cb; + VMChangeStateHandlerWithRet *cb_ret; void *opaque; QTAILQ_ENTRY(VMChangeStateEntry) entries; int priority; @@ -295,14 +321,15 @@ 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); + return qemu_add_vm_change_state_handler_prio_full(cb, NULL, 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 + * @cb_ret: the main callback to invoke with return value * @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 @@ -319,6 +346,7 @@ VMChangeStateEntry *qemu_add_vm_change_state_handler_prio( VMChangeStateEntry * qemu_add_vm_change_state_handler_prio_full(VMChangeStateHandler *cb, VMChangeStateHandler *prepare_cb, + VMChangeStateHandlerWithRet *cb_ret, void *opaque, int priority) { VMChangeStateEntry *e; @@ -327,6 +355,7 @@ qemu_add_vm_change_state_handler_prio_full(VMChangeStateHandler *cb, e = g_malloc0(sizeof(*e)); e->cb = cb; e->prepare_cb = prepare_cb; + e->cb_ret = cb_ret; e->opaque = opaque; e->priority = priority; @@ -354,9 +383,10 @@ void qemu_del_vm_change_state_handler(VMChangeStateEntry *e) g_free(e); } -void vm_state_notify(bool running, RunState state) +int vm_state_notify(bool running, RunState state) { VMChangeStateEntry *e, *next; + int ret = 0; trace_vm_state_notify(running, state, RunState_str(state)); @@ -368,7 +398,17 @@ void vm_state_notify(bool running, RunState state) } QTAILQ_FOREACH_SAFE(e, &vm_change_state_head, entries, next) { - e->cb(e->opaque, running, state); + if (e->cb) { + e->cb(e->opaque, running, state); + } else if (e->cb_ret) { + /* + * Here ignore the return value of cb_ret because + * we only care about the stopping the device during + * the VM live migration to indicate whether the + * connection between qemu and backend is normal. + */ + e->cb_ret(e->opaque, running, state); + } } } else { QTAILQ_FOREACH_REVERSE_SAFE(e, &vm_change_state_head, entries, next) { @@ -378,13 +418,24 @@ void vm_state_notify(bool running, RunState state) } QTAILQ_FOREACH_REVERSE_SAFE(e, &vm_change_state_head, entries, next) { - e->cb(e->opaque, running, state); + if (e->cb) { + e->cb(e->opaque, running, state); + } else if (e->cb_ret) { + /* + * We should execute all registered callbacks even if + * one of them returns failure, otherwise, some cleanup + * work of the device will be skipped. + */ + ret |= e->cb_ret(e->opaque, running, state); + } } } + return ret; } 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; @@ -482,15 +533,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: @@ -500,7 +559,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); } /* @@ -517,6 +590,58 @@ static void qemu_system_wakeup(void) } } +static char *tdx_parse_panic_message(char *message) +{ + bool printable = false; + char *buf = NULL; + int len = 0, i; + + /* + * Although message is defined as a json string, we shouldn't + * unconditionally treat it as is because the guest generated it and + * it's not necessarily trustable. + */ + if (message) { + /* The caller guarantees the NULL-terminated string. */ + len = strlen(message); + + printable = len > 0; + for (i = 0; i < len; i++) { + if (!(0x20 <= message[i] && message[i] <= 0x7e)) { + printable = false; + break; + } + } + } + + if (len == 0) { + buf = g_malloc(1); + buf[0] = '\0'; + } else { + if (!printable) { + /* 3 = length of "%02x " */ + buf = g_malloc(len * 3); + for (i = 0; i < len; i++) { + if (message[i] == '\0') { + break; + } else { + sprintf(buf + 3 * i, "%02x ", message[i]); + } + } + if (i > 0) { + /* replace the last ' '(space) to NULL */ + buf[i * 3 - 1] = '\0'; + } else { + buf[0] = '\0'; + } + } else { + buf = g_strdup(message); + } + } + + return buf; +} + void qemu_system_guest_panicked(GuestPanicInformation *info) { qemu_log_mask(LOG_GUEST_ERROR, "Guest crashed"); @@ -558,7 +683,20 @@ void qemu_system_guest_panicked(GuestPanicInformation *info) S390CrashReason_str(info->u.s390.reason), info->u.s390.psw_mask, info->u.s390.psw_addr); + } else if (info->type == GUEST_PANIC_INFORMATION_TYPE_TDX) { + char *message = tdx_parse_panic_message(info->u.tdx.message); + qemu_log_mask(LOG_GUEST_ERROR, + "\nTDX guest reports fatal error." + " error code: 0x%" PRIx32 " error message:\"%s\"\n", + info->u.tdx.error_code, message); + g_free(message); + if (info->u.tdx.gpa != -1ull) { + qemu_log_mask(LOG_GUEST_ERROR, "Additional error information " + "can be found at gpa page: 0x%" PRIx64 "\n", + info->u.tdx.gpa); + } } + qapi_free_GuestPanicInformation(info); } } @@ -570,6 +708,12 @@ void qemu_system_guest_crashloaded(GuestPanicInformation *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) { if (reboot_action == REBOOT_ACTION_SHUTDOWN && @@ -664,6 +808,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); @@ -725,7 +876,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; } @@ -787,6 +940,7 @@ void qemu_remove_exit_notifier(Notifier *notify) static void qemu_run_exit_notifiers(void) { + BQL_LOCK_GUARD(); notifier_list_notify(&exit_notifiers, NULL); } @@ -800,7 +954,7 @@ void qemu_init_subsystems(void) qemu_init_cpu_list(); qemu_init_cpu_loop(); - qemu_mutex_lock_iothread(); + bql_lock(); atexit(qemu_run_exit_notifiers); @@ -824,9 +978,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 diff --git a/softmmu/tpm-hmp-cmds.c b/system/tpm-hmp-cmds.c similarity index 100% rename from softmmu/tpm-hmp-cmds.c rename to system/tpm-hmp-cmds.c diff --git a/softmmu/tpm.c b/system/tpm.c similarity index 95% rename from softmmu/tpm.c rename to system/tpm.c index 578563f05a7..8df0f6e72b3 100644 --- a/softmmu/tpm.c +++ b/system/tpm.c @@ -17,8 +17,8 @@ #include "qapi/error.h" #include "qapi/qapi-commands-tpm.h" #include "qapi/qmp/qerror.h" -#include "sysemu/tpm_backend.h" -#include "sysemu/tpm.h" +#include "system/tpm_backend.h" +#include "system/tpm.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -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 00000000000..82856e44f2e --- /dev/null +++ b/system/trace-events @@ -0,0 +1,57 @@ +# 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" +qemu_ram_alloc_shared(const char *name, size_t size, size_t max_size, int fd, void *host) "%s size %zu max_size %zu fd %d host %p" + +# 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" + +# ram-block-attributes.c +ram_block_attributes_state_change(uint64_t offset, uint64_t size, const char *from, const char *to) "offset 0x%"PRIx64" size 0x%"PRIx64" from '%s' to '%s'" diff --git a/system/trace.h b/system/trace.h new file mode 100644 index 00000000000..cd0136dcdc1 --- /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 88% rename from softmmu/vl.c rename to system/vl.c index 3db4fd26808..3b7057e6c66 100644 --- a/softmmu/vl.c +++ b/system/vl.c @@ -26,25 +26,28 @@ #include "qemu/help-texts.h" #include "qemu/datadir.h" #include "qemu/units.h" +#include "qemu/module.h" +#include "qemu/target-info.h" #include "exec/cpu-common.h" #include "exec/page-vary.h" #include "hw/qdev-properties.h" #include "qapi/compat-policy.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" +#include "qobject/qjson.h" #include "qemu-version.h" #include "qemu/cutils.h" #include "qemu/help_option.h" #include "qemu/hw-version.h" #include "qemu/uuid.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/runstate-action.h" -#include "sysemu/seccomp.h" -#include "sysemu/tcg.h" -#include "sysemu/xen.h" +#include "qemu/target-info.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/runstate-action.h" +#include "system/seccomp.h" +#include "system/tcg.h" +#include "system/xen.h" #include "qemu/error-report.h" #include "qemu/sockets.h" @@ -53,6 +56,7 @@ #include "hw/usb.h" #include "hw/isa/isa.h" #include "hw/scsi/scsi.h" +#include "hw/sd/sd.h" #include "hw/display/vga.h" #include "hw/firmware/smbios.h" #include "hw/acpi/acpi.h" @@ -64,29 +68,32 @@ #include "monitor/monitor.h" #include "ui/console.h" #include "ui/input.h" -#include "sysemu/sysemu.h" -#include "sysemu/numa.h" -#include "sysemu/hostmem.h" +#include "system/system.h" +#include "system/numa.h" +#include "system/hostmem.h" #include "exec/gdbstub.h" +#include "gdbstub/enums.h" #include "qemu/timer.h" #include "chardev/char.h" #include "qemu/bitmap.h" #include "qemu/log.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "hw/block/block.h" #include "hw/i386/x86.h" #include "hw/i386/pc.h" +#include "migration/cpr.h" #include "migration/misc.h" #include "migration/snapshot.h" -#include "sysemu/tpm.h" -#include "sysemu/dma.h" +#include "system/tpm.h" +#include "system/dma.h" #include "hw/audio/soundhw.h" #include "audio/audio.h" -#include "sysemu/cpus.h" -#include "sysemu/cpu-timers.h" +#include "system/cpus.h" +#include "system/cpu-timers.h" +#include "exec/icount.h" #include "migration/colo.h" #include "migration/postcopy-ram.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qapi/qobject-input-visitor.h" #include "qemu/option.h" #include "qemu/config-file.h" @@ -94,9 +101,9 @@ #ifdef CONFIG_VIRTFS #include "fsdev/qemu-fsdev.h" #endif -#include "sysemu/qtest.h" +#include "system/qtest.h" #ifdef CONFIG_TCG -#include "accel/tcg/perf.h" +#include "tcg/perf.h" #endif #include "disas/disas.h" @@ -105,8 +112,8 @@ #include "trace/control.h" #include "qemu/plugin.h" #include "qemu/queue.h" -#include "sysemu/arch_init.h" -#include "exec/confidential-guest-support.h" +#include "system/arch_init.h" +#include "system/confidential-guest-support.h" #include "ui/qemu-spice.h" #include "qapi/string-input-visitor.h" @@ -115,13 +122,14 @@ #include "qom/object_interfaces.h" #include "semihosting/semihost.h" #include "crypto/init.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "qapi/qapi-events-run-state.h" #include "qapi/qapi-types-audio.h" #include "qapi/qapi-visit-audio.h" #include "qapi/qapi-visit-block-core.h" #include "qapi/qapi-visit-compat.h" #include "qapi/qapi-visit-machine.h" +#include "qapi/qapi-visit-migration.h" #include "qapi/qapi-visit-ui.h" #include "qapi/qapi-commands-block-core.h" #include "qapi/qapi-commands-migration.h" @@ -130,7 +138,7 @@ #include "qapi/qapi-commands-ui.h" #include "block/qdict.h" #include "qapi/qmp/qerror.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" #include "qemu/guest-random.h" #include "qemu/keyval.h" @@ -158,6 +166,8 @@ typedef struct DeviceOption { static const char *cpu_option; static const char *mem_path; static const char *incoming; +static const char *incoming_str[MIGRATION_CHANNEL_TYPE__MAX]; +static MigrationChannel *incoming_channels[MIGRATION_CHANNEL_TYPE__MAX]; static const char *loadvm; static const char *accelerators; static bool have_custom_ram_size; @@ -181,22 +191,23 @@ static const char *log_file; static bool list_data_dirs; static const char *qtest_chrdev; static const char *qtest_log; -static bool opt_one_insn_per_tb; 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; static int default_floppy = 1; static int default_cdrom = 1; -static int default_sdcard = 1; +static bool auto_create_sdcard = true; 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 }, @@ -215,6 +226,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 = { @@ -344,7 +356,7 @@ static QemuOptsList qemu_overcommit_opts = { .desc = { { .name = "mem-lock", - .type = QEMU_OPT_BOOL, + .type = QEMU_OPT_STRING, }, { .name = "cpu-pm", @@ -711,7 +723,7 @@ static void configure_blockdev(BlockdevOptionsQueue *bdo_queue, default_drive(default_cdrom, snapshot, machine_class->block_default_type, 2, CDROM_OPTS); default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS); - default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS); + default_drive(auto_create_sdcard, snapshot, IF_SD, 0, SD_OPTS); } @@ -724,6 +736,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, @@ -733,6 +751,9 @@ static QemuOptsList qemu_smp_opts = { }, { .name = "clusters", .type = QEMU_OPT_NUMBER, + }, { + .name = "modules", + .type = QEMU_OPT_NUMBER, }, { .name = "cores", .type = QEMU_OPT_NUMBER, @@ -747,7 +768,7 @@ static QemuOptsList qemu_smp_opts = { }, }; -#if defined(CONFIG_POSIX) +#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN) static QemuOptsList qemu_run_with_opts = { .name = "run-with", .head = QTAILQ_HEAD_INITIALIZER(qemu_run_with_opts.head), @@ -762,6 +783,10 @@ static QemuOptsList qemu_run_with_opts = { .name = "chroot", .type = QEMU_OPT_STRING, }, + { + .name = "user", + .type = QEMU_OPT_STRING, + }, { /* end of list */ } }, }; @@ -776,8 +801,8 @@ static QemuOptsList qemu_run_with_opts = { static void realtime_init(void) { - if (enable_mlock) { - if (os_mlock() < 0) { + if (should_mlock(mlock_state)) { + if (os_mlock(is_mlock_on_fault(mlock_state)) < 0) { error_report("locking memory failed"); exit(1); } @@ -795,29 +820,15 @@ static void configure_msg(QemuOpts *opts) /***********************************************************/ /* USB devices */ -static int usb_device_add(const char *devname) +static bool usb_parse(const char *cmdline, Error **errp) { - USBDevice *dev = NULL; - - if (!machine_usb(current_machine)) { - return -1; - } - - dev = usbdevice_create(devname); - if (!dev) - return -1; - - return 0; -} + g_assert(machine_usb(current_machine)); -static int usb_parse(const char *cmdline) -{ - int r; - r = usb_device_add(cmdline); - if (r < 0) { - error_report("could not add USB device '%s'", cmdline); + if (!usbdevice_create(cmdline)) { + error_setg(errp, "could not add USB device '%s'", cmdline); + return false; } - return r; + return true; } /***********************************************************/ @@ -869,11 +880,11 @@ static void help(int exitcode) g_get_prgname()); #define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \ - if ((arch_mask) & arch_type) \ + if (qemu_arch_available(arch_mask)) \ fputs(opt_help, stdout); #define ARCHHEADING(text, arch_mask) \ - if ((arch_mask) & arch_type) \ + if (qemu_arch_available(arch_mask)) \ puts(stringify(text)); #define DEFHEADING(text) ARCHHEADING(text, QEMU_ARCH_ALL) @@ -883,7 +894,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" @@ -984,9 +995,16 @@ 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 * @@ -1066,12 +1084,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); @@ -1087,13 +1105,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 @@ -1101,13 +1120,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) @@ -1157,7 +1179,8 @@ static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp) size = strlen(str); /* NUL terminator NOT included in fw_cfg blob */ buf = g_memdup(str, size); } else if (nonempty_str(gen_id)) { - if (!fw_cfg_add_from_generator(fw_cfg, name, gen_id, errp)) { + if (!fw_cfg_add_file_from_generator(fw_cfg, object_get_objects_root(), + gen_id, name, errp)) { return -1; } return 0; @@ -1169,10 +1192,7 @@ static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp) return -1; } } - /* For legacy, keep user files in a specific global order. */ - fw_cfg_set_order_override(fw_cfg, FW_CFG_ORDER_OVERRIDE_USER); fw_cfg_add_file(fw_cfg, name, buf, size); - fw_cfg_reset_order_override(fw_cfg); return 0; } @@ -1221,21 +1241,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); } } @@ -1279,22 +1299,27 @@ static void add_device_config(int type, const char *cmdline) QTAILQ_INSERT_TAIL(&device_configs, conf, next); } -static int foreach_device_config(int type, int (*func)(const char *cmdline)) +/** + * foreach_device_config_or_exit(): process per-device configs + * @type: device_config type + * @func: device specific config function, returning pass/fail + * + * @func is called with the &error_fatal handler so device specific + * error messages can be reported on failure. + */ +static void foreach_device_config_or_exit(int type, + bool (*func)(const char *cmdline, + Error **errp)) { struct device_config *conf; - int rc; QTAILQ_FOREACH(conf, &device_configs, next) { if (conf->type != type) continue; loc_push_restore(&conf->loc); - rc = func(conf->cmdline); + func(conf->cmdline, &error_fatal); loc_pop(&conf->loc); - if (rc) { - return rc; - } } - return 0; } static void qemu_disable_default_devices(void) @@ -1323,10 +1348,11 @@ static void qemu_disable_default_devices(void) if (!has_defaults || machine_class->no_cdrom) { default_cdrom = 0; } - if (!has_defaults || machine_class->no_sdcard) { - default_sdcard = 0; + if (!has_defaults || !machine_class->auto_create_sdcard) { + auto_create_sdcard = false; } if (!has_defaults) { + default_audio = 0; default_monitor = 0; default_net = 0; default_vga = 0; @@ -1340,9 +1366,28 @@ static void qemu_disable_default_devices(void) } } +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 @@ -1362,23 +1407,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) { @@ -1389,23 +1440,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); @@ -1415,24 +1449,28 @@ static void qemu_create_default_devices(void) } } -static int serial_parse(const char *devname) +static bool serial_parse(const char *devname, Error **errp) { 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_setg(errp, "could not connect serial device" + " to character backend '%s'", devname); + return false; + } } num_serial_hds++; - return 0; + return true; } Chardev *serial_hd(int i) @@ -1444,47 +1482,47 @@ Chardev *serial_hd(int i) return NULL; } -static int parallel_parse(const char *devname) +static bool parallel_parse(const char *devname, Error **errp) { static int index = 0; char label[32]; if (strcmp(devname, "none") == 0) - return 0; + return true; if (index == MAX_PARALLEL_PORTS) { - error_report("too many parallel ports"); - exit(1); + error_setg(errp, "too many parallel ports"); + return false; } snprintf(label, sizeof(label), "parallel%d", index); parallel_hds[index] = qemu_chr_new_mux_mon(label, devname, NULL); if (!parallel_hds[index]) { - error_report("could not connect parallel device" - " to character backend '%s'", devname); - return -1; + error_setg(errp, "could not connect parallel device" + " to character backend '%s'", devname); + return false; } index++; - return 0; + return true; } -static int debugcon_parse(const char *devname) +static bool debugcon_parse(const char *devname, Error **errp) { QemuOpts *opts; if (!qemu_chr_new_mux_mon("debugcon", devname, NULL)) { - error_report("invalid character backend '%s'", devname); - exit(1); + error_setg(errp, "invalid character backend '%s'", devname); + return false; } opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1, NULL); if (!opts) { - error_report("already have a debugcon device"); - exit(1); + error_setg(errp, "already have a debugcon device"); + return false; } qemu_opt_set(opts, "driver", "isa-debugcon", &error_abort); qemu_opt_set(opts, "chardev", "debugcon", &error_abort); - return 0; + return true; } -static gint machine_class_cmp(gconstpointer a, gconstpointer b) +static gint machine_class_cmp(gconstpointer a, gconstpointer b, gpointer d) { const MachineClass *mc1 = a, *mc2 = b; int res; @@ -1520,10 +1558,11 @@ 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); + machines = object_class_get_list(target_machine_typename(), false); if (type) { ObjectClass *machine_class = OBJECT_CLASS(find_machine(type, machines)); if (machine_class) { @@ -1533,7 +1572,7 @@ static void machine_help_func(const QDict *qdict) } printf("Supported machines are:\n"); - machines = g_slist_sort(machines, machine_class_cmp); + machines = g_slist_sort_with_data(machines, machine_class_cmp, NULL); for (el = machines; el; el = el->next) { MachineClass *mc = el->data; if (mc->alias) { @@ -1631,28 +1670,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; } @@ -1781,27 +1819,52 @@ 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 incoming_option_parse(const char *str) +{ + MigrationChannelType type = MIGRATION_CHANNEL_TYPE_MAIN; + MigrationChannel *channel; + Visitor *v; + + if (!strcmp(str, "defer")) { + channel = NULL; + } else if (migrate_is_uri(str)) { + migrate_uri_parse(str, &channel, &error_fatal); + } else { + v = qobject_input_visitor_new_str(str, "channel-type", &error_fatal); + visit_type_MigrationChannel(v, NULL, &channel, &error_fatal); + visit_free(v); + type = channel->channel_type; + } + + /* New incoming spec replaces the previous */ + qapi_free_MigrationChannel(incoming_channels[type]); + incoming_channels[type] = channel; + incoming_str[type] = str; + incoming = incoming_str[MIGRATION_CHANNEL_TYPE_MAIN]; +} + +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); @@ -1814,6 +1877,44 @@ static void object_option_parse(const char *optarg) visit_free(v); } +static void overcommit_parse(const char *str) +{ + QemuOpts *opts; + const char *mem_lock_opt; + + opts = qemu_opts_parse_noisily(qemu_find_opts("overcommit"), + str, false); + if (!opts) { + exit(1); + } + + enable_cpu_pm = qemu_opt_get_bool(opts, "cpu-pm", enable_cpu_pm); + + mem_lock_opt = qemu_opt_get(opts, "mem-lock"); + if (!mem_lock_opt) { + return; + } + + if (strcmp(mem_lock_opt, "on") == 0) { + mlock_state = MLOCK_ON; + return; + } + + if (strcmp(mem_lock_opt, "off") == 0) { + mlock_state = MLOCK_OFF; + return; + } + + if (strcmp(mem_lock_opt, "on-fault") == 0) { + mlock_state = MLOCK_ON_FAULT; + return; + } + + error_report("parameter 'mem-lock' expects one of " + "'on', 'off', 'on-fault'"); + exit(1); +} + /* * Very early object creation, before the sandbox options have been activated. */ @@ -1888,7 +1989,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; @@ -1907,7 +2007,7 @@ static void qemu_apply_machine_options(QDict *qdict) } if (current_machine->smp.cpus > 1) { - replay_add_blocker("smp"); + replay_add_blocker("multiple CPUs"); } } @@ -1930,14 +2030,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); } @@ -1962,8 +2062,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(); } } @@ -1987,6 +2088,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); } @@ -1994,12 +2103,9 @@ static void qemu_create_late_backends(void) qemu_opts_foreach(qemu_find_opts("mon"), mon_init_func, NULL, &error_fatal); - if (foreach_device_config(DEV_SERIAL, serial_parse) < 0) - exit(1); - if (foreach_device_config(DEV_PARALLEL, parallel_parse) < 0) - exit(1); - if (foreach_device_config(DEV_DEBUGCON, debugcon_parse) < 0) - exit(1); + foreach_device_config_or_exit(DEV_SERIAL, serial_parse); + foreach_device_config_or_exit(DEV_PARALLEL, parallel_parse); + foreach_device_config_or_exit(DEV_DEBUGCON, debugcon_parse); /* now chardevs have been created we may have semihosting to connect */ qemu_semihosting_chardev_init(); @@ -2063,6 +2169,19 @@ static void parse_memory_options(void) loc_pop(&loc); } +static void qemu_create_machine_containers(Object *machine) +{ + static const char *const containers[] = { + "unattached", + "peripheral", + "peripheral-anon", + }; + + for (unsigned i = 0; i < ARRAY_SIZE(containers); i++) { + object_property_add_new_container(machine, containers[i]); + } +} + static void qemu_create_machine(QDict *qdict) { MachineClass *machine_class = select_machine(qdict, &error_fatal); @@ -2071,8 +2190,8 @@ static void qemu_create_machine(QDict *qdict) current_machine = MACHINE(object_new_with_class(OBJECT_CLASS(machine_class))); object_property_add_child(object_get_root(), "machine", OBJECT(current_machine)); - object_property_add_child(container_get(OBJECT(current_machine), - "/unattached"), + qemu_create_machine_containers(OBJECT(current_machine)); + object_property_add_child(machine_get_container("unattached"), "sysbus", OBJECT(sysbus_get_default())); if (machine_class->minimum_page_bits) { @@ -2085,7 +2204,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); @@ -2125,6 +2243,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")) { @@ -2140,6 +2259,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 @@ -2237,8 +2365,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, @@ -2275,19 +2402,7 @@ static int do_configure_accelerator(void *opaque, QemuOpts *opts, Error **errp) qemu_opt_foreach(opts, accelerator_set_property, accel, &error_fatal); - /* - * If legacy -singlestep option is set, honour it for TCG and - * silently ignore for any other accelerator (which is how this - * option has always behaved). - */ - if (opt_one_insn_per_tb) { - /* - * This will always succeed for TCG, and we want to ignore - * the error from trying to set a nonexistent property - * on any other accelerator. - */ - object_property_set_bool(OBJECT(accel), "one-insn-per-tb", true, NULL); - } + ret = accel_init_machine(accel, current_machine); if (ret < 0) { if (!qtest_with_kvm || ret != -ENOENT) { @@ -2317,6 +2432,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")) { @@ -2329,6 +2445,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"); @@ -2378,21 +2496,31 @@ static void configure_accelerators(const char *progname) static void qemu_validate_options(const QDict *machine_opts) { const char *kernel_filename = qdict_get_try_str(machine_opts, "kernel"); + const char *shim_filename = qdict_get_try_str(machine_opts, "shim"); const char *initrd_filename = qdict_get_try_str(machine_opts, "initrd"); 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 (shim_filename != NULL) { + error_report("-shim 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"); @@ -2573,12 +2701,27 @@ static void qemu_init_displays(void) static void qemu_init_board(void) { + MachineClass *machine_class = MACHINE_GET_CLASS(current_machine); + /* process plugin before CPUs are created, but once -smp has been parsed */ qemu_plugin_load_list(&plugin_list, &error_fatal); /* From here on we enter MACHINE_PHASE_INITIALIZED. */ machine_run_board_init(current_machine, mem_path, &error_fatal); + if (machine_class->auto_create_sdcard) { + bool ambigous; + + /* Ensure there is a SD bus available to create SD card on */ + Object *obj = object_resolve_path_type("", TYPE_SD_BUS, &ambigous); + if (!obj && !ambigous) { + fprintf(stderr, "Can not create sd-card on '%s' machine" + " because it lacks a sd-bus\n", + machine_class->name); + abort(); + } + } + drive_check_orphaned(); realtime_init(); @@ -2595,32 +2738,23 @@ static void qemu_create_cli_devices(void) /* init USB devices */ if (machine_usb(current_machine)) { - if (foreach_device_config(DEV_USB, usb_parse) < 0) - exit(1); + foreach_device_config_or_exit(DEV_USB, usb_parse); } /* init generic devices */ - rom_set_order_override(FW_CFG_ORDER_OVERRIDE_DEVICE); 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()); @@ -2643,22 +2777,21 @@ 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 (foreach_device_config(DEV_GDB, gdbserver_start) < 0) { + if (machine->cgs && !machine->cgs->ready) { + error_setg(errp, "accelerator does not support confidential guest %s", + object_get_typename(OBJECT(machine->cgs))); exit(1); } + + foreach_device_config_or_exit(DEV_GDB, gdbserver_start); + if (!vga_interface_created && !default_vga && vga_interface_type != VGA_NONE) { warn_report("A -vga option was passed but this machine " "type does not use that option; " "No VGA device has been created"); } + return true; } void qmp_x_exit_preconfig(Error **errp) @@ -2670,10 +2803,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(); @@ -2682,7 +2819,11 @@ void qmp_x_exit_preconfig(Error **errp) if (incoming) { Error *local_err = NULL; if (strcmp(incoming, "defer") != 0) { - qmp_migrate_incoming(incoming, &local_err); + g_autofree MigrationChannelList *channels = + g_new0(MigrationChannelList, 1); + + channels->value = incoming_channels[MIGRATION_CHANNEL_TYPE_MAIN]; + qmp_migrate_incoming(NULL, true, channels, true, true, &local_err); if (local_err) { error_reportf_err(local_err, "-incoming %s: ", incoming); exit(1); @@ -2741,7 +2882,12 @@ void qemu_init(int argc, char **argv) error_init(argv[0]); qemu_init_exec_dir(argv[0]); - qemu_init_arch_modules(); + os_setup_limits(); + +#ifdef CONFIG_MODULES + module_init_info(qemu_modinfo); + module_allow_arch(target_name()); +#endif qemu_init_subsystems(); @@ -2780,7 +2926,7 @@ void qemu_init(int argc, char **argv) const QEMUOption *popt; popt = lookup_opt(argc, argv, &optarg, &optind); - if (!(popt->arch_mask & arch_type)) { + if (!qemu_arch_available(popt->arch_mask)) { error_report("Option not supported for this target"); exit(1); } @@ -2854,20 +3000,12 @@ 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; + case QEMU_OPTION_shim: + qdict_put_str(machine_opts_dict, "shim", optarg); + break; case QEMU_OPTION_initrd: qdict_put_str(machine_opts_dict, "initrd", optarg); break; @@ -2889,7 +3027,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; @@ -2916,19 +3054,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(); @@ -2937,22 +3073,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: @@ -3017,9 +3156,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: - opt_one_insn_per_tb = true; - break; case QEMU_OPTION_S: autostart = 0; break; @@ -3204,7 +3340,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; @@ -3230,7 +3365,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"), @@ -3324,17 +3459,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: - warn_report("-no-acpi is deprecated, use '-machine acpi=off' instead"); - qdict_put_str(machine_opts_dict, "acpi", "off"); - break; - case QEMU_OPTION_no_hpet: - warn_report("-no-hpet is deprecated, use '-machine hpet=off' instead"); - 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); @@ -3395,6 +3525,7 @@ void qemu_init(int argc, char **argv) nb_prom_envs++; break; case QEMU_OPTION_old_param: + warn_report("-old-param is deprecated"); old_param = 1; break; case QEMU_OPTION_rtc: @@ -3415,7 +3546,7 @@ void qemu_init(int argc, char **argv) if (!incoming) { runstate_set(RUN_STATE_INMIGRATE); } - incoming = optarg; + incoming_option_parse(optarg); break; case QEMU_OPTION_only_migratable: only_migratable = 1; @@ -3455,12 +3586,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); } @@ -3505,26 +3631,20 @@ void qemu_init(int argc, char **argv) object_option_parse(optarg); break; case QEMU_OPTION_overcommit: - opts = qemu_opts_parse_noisily(qemu_find_opts("overcommit"), - optarg, false); - 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); + overcommit_parse(optarg); 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; } @@ -3554,29 +3674,10 @@ void qemu_init(int argc, char **argv) case QEMU_OPTION_nouserconfig: /* Nothing to be parsed here. Especially, do not error out below. */ break; -#if defined(CONFIG_POSIX) - case QEMU_OPTION_runas: - if (!os_set_runas(optarg)) { - error_report("User \"%s\" doesn't exist" - " (and is not :)", - optarg); - exit(1); - } - break; - case QEMU_OPTION_chroot: - warn_report("option is deprecated," - " use '-run-with chroot=...' instead"); - os_set_chroot(optarg); - break; +#if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN) case QEMU_OPTION_daemonize: os_set_daemonize(true); break; -#if defined(CONFIG_LINUX) - /* deprecated */ - case QEMU_OPTION_asyncteardown: - init_async_teardown(); - break; -#endif case QEMU_OPTION_run_with: { const char *str; opts = qemu_opts_parse_noisily(qemu_find_opts("run-with"), @@ -3593,6 +3694,16 @@ void qemu_init(int argc, char **argv) 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 */ @@ -3647,9 +3758,16 @@ void qemu_init(int argc, char **argv) qemu_create_machine(machine_opts_dict); + /* + * Load incoming CPR state before any devices are created, because it + * contains file descriptors that are needed in device initialization code. + */ + cpr_state_load(incoming_channels[MIGRATION_CHANNEL_TYPE_CPR], &error_fatal); + suspend_mux_open(); qemu_disable_default_devices(); + qemu_setup_display(); qemu_create_default_devices(); qemu_create_early_backends(); @@ -3693,6 +3811,7 @@ void qemu_init(int argc, char **argv) * 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 @@ -3701,7 +3820,7 @@ void qemu_init(int argc, char **argv) migration_object_init(); /* 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 00000000000..21d0bb36cae --- /dev/null +++ b/system/watchpoint.c @@ -0,0 +1,102 @@ +/* + * 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/cputlb.h" +#include "exec/target_page.h" +#include "exec/watchpoint.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-info-stub.c b/target-info-stub.c new file mode 100644 index 00000000000..fecc0e71286 --- /dev/null +++ b/target-info-stub.c @@ -0,0 +1,25 @@ +/* + * QEMU target info stubs (target specific) + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/target-info.h" +#include "qemu/target-info-impl.h" +#include "hw/boards.h" +#include "cpu.h" + +static const TargetInfo target_info_stub = { + .target_name = TARGET_NAME, + .long_bits = TARGET_LONG_BITS, + .cpu_type = CPU_RESOLVING_TYPE, + .machine_typename = TYPE_MACHINE, +}; + +const TargetInfo *target_info(void) +{ + return &target_info_stub; +} diff --git a/target-info.c b/target-info.c new file mode 100644 index 00000000000..16fdca7aaaf --- /dev/null +++ b/target-info.c @@ -0,0 +1,31 @@ +/* + * QEMU target info helpers + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/target-info.h" +#include "qemu/target-info-impl.h" + +const char *target_name(void) +{ + return target_info()->target_name; +} + +unsigned target_long_bits(void) +{ + return target_info()->long_bits; +} + +const char *target_cpu_type(void) +{ + return target_info()->cpu_type; +} + +const char *target_machine_typename(void) +{ + return target_info()->machine_typename; +} diff --git a/target/Kconfig b/target/Kconfig index 83da0bd2938..d0c7b59d9c7 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/clk_helper.c b/target/alpha/clk_helper.c new file mode 100644 index 00000000000..26ffc231cda --- /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 68c46f7998a..a799f42db31 100644 --- a/target/alpha/cpu-param.h +++ b/target/alpha/cpu-param.h @@ -2,17 +2,28 @@ * 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) + +#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_VIRT_ADDR_SPACE_BITS 63 +#else +# define TARGET_PAGE_BITS 13 +# define TARGET_VIRT_ADDR_SPACE_BITS (30 + TARGET_PAGE_BITS) +#endif + +#define TARGET_INSN_START_EXTRA_WORDS 0 #endif diff --git a/target/alpha/cpu-qom.h b/target/alpha/cpu-qom.h index 1f200724b67..1b32b18d34c 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 270ae787b18..2082db45eaf 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -23,32 +23,60 @@ #include "qapi/error.h" #include "qemu/qemu-print.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/translation-block.h" +#include "exec/target_page.h" +#include "accel/tcg/cpu-ops.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; +} + +static TCGTBCPUState alpha_get_tb_cpu_state(CPUState *cs) +{ + CPUAlphaState *env = cpu_env(cs); + uint32_t flags = env->flags & ENV_FLAG_TB_MASK; + +#ifdef CONFIG_USER_ONLY + flags |= TB_FLAG_UNALIGN * !cs->prctl_unalign_sigbus; +#endif - return cpu->env.pc; + return (TCGTBCPUState){ .pc = env->pc, .flags = flags }; +} + +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]; + } } +#ifndef CONFIG_USER_ONLY static bool alpha_cpu_has_work(CPUState *cs) { /* Here we are checking to see if the CPU should wake up from HALT. @@ -63,9 +91,16 @@ static bool alpha_cpu_has_work(CPUState *cs) | CPU_INTERRUPT_SMP | CPU_INTERRUPT_MCHK); } +#endif /* !CONFIG_USER_ONLY */ + +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->endian = BFD_ENDIAN_LITTLE; info->mach = bfd_mach_alpha_ev6; info->print_insn = print_insn_alpha; } @@ -76,6 +111,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 +127,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 +149,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 +164,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 +198,33 @@ 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); + /* Default NaN: sign bit clear, msb frac bit set */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); + /* + * TODO: this is incorrect. The Alpha Architecture Handbook version 4 + * section 4.7.7.11 says that we flush to zero for underflow cases, so + * this should be float_ftz_after_rounding to match the + * tininess_after_rounding (which is specified in section 4.7.5). + */ + set_float_ftz_detection(float_ftz_before_rounding, &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 @@ -226,29 +239,39 @@ static void alpha_cpu_initfn(Object *obj) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps alpha_sysemu_ops = { + .has_work = alpha_cpu_has_work, .get_phys_page_debug = alpha_cpu_get_phys_page_debug, }; #endif -#include "hw/core/tcg-cpu-ops.h" +static const TCGCPUOps alpha_tcg_ops = { + /* Alpha processors have a weak memory model */ + .guest_default_memory_order = 0, + .mttcg_supported = true, -static const struct TCGCPUOps alpha_tcg_ops = { .initialize = alpha_translate_init, + .translate_code = alpha_translate_code, + .get_tb_cpu_state = alpha_get_tb_cpu_state, + .synchronize_from_tb = alpha_cpu_synchronize_from_tb, .restore_state_to_opc = alpha_restore_state_to_opc, + .mmu_index = alpha_cpu_mmu_index, #ifdef CONFIG_USER_ONLY .record_sigsegv = alpha_cpu_record_sigsegv, .record_sigbus = alpha_cpu_record_sigbus, #else .tlb_fill = alpha_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_notreached, .cpu_exec_interrupt = alpha_cpu_exec_interrupt, + .cpu_exec_halt = alpha_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = alpha_cpu_do_interrupt, .do_transaction_failed = alpha_cpu_do_transaction_failed, .do_unaligned_access = alpha_cpu_do_unaligned_access, #endif /* !CONFIG_USER_ONLY */ }; -static void alpha_cpu_class_init(ObjectClass *oc, void *data) +static void alpha_cpu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); @@ -258,7 +281,6 @@ static void alpha_cpu_class_init(ObjectClass *oc, void *data) &acc->parent_realize); cc->class_by_name = alpha_cpu_class_by_name; - cc->has_work = alpha_cpu_has_work; cc->dump_state = alpha_cpu_dump_state; cc->set_pc = alpha_cpu_set_pc; cc->get_pc = alpha_cpu_get_pc; @@ -286,6 +308,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 13306665aff..45944e46b54 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -21,12 +21,11 @@ #define ALPHA_CPU_H #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.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 @@ -259,17 +258,25 @@ 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; @@ -282,10 +289,6 @@ void alpha_cpu_dump_state(CPUState *cs, FILE *f, int flags); 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 { FEATURE_ASN = 0x00000001, FEATURE_SPS = 0x00000002, @@ -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) { @@ -428,12 +431,11 @@ enum { }; void alpha_translate_init(void); +void alpha_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); -#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,17 +464,6 @@ 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, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) -{ - *pc = env->pc; - *cs_base = 0; - *pflags = env->flags & ENV_FLAG_TB_MASK; -#ifdef CONFIG_USER_ONLY - *pflags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; -#endif -} - #ifdef CONFIG_USER_ONLY /* Copied from linux ieee_swcr_to_fpcr. */ static inline uint64_t alpha_ieee_swcr_to_fpcr(uint64_t swcr) diff --git a/target/alpha/fpu_helper.c b/target/alpha/fpu_helper.c index 63d9e9ce39c..30f3c7fd185 100644 --- a/target/alpha/fpu_helper.c +++ b/target/alpha/fpu_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" @@ -455,26 +454,27 @@ static uint64_t do_cvttq(CPUAlphaState *env, uint64_t a, int roundmode) { float64 fa; int64_t ret; - uint32_t exc; + uint32_t exc = 0; + int flags; fa = t_to_float64(a); ret = float64_to_int64_modulo(fa, roundmode, &FP_STATUS); - exc = get_float_exception_flags(&FP_STATUS); - if (unlikely(exc)) { + flags = get_float_exception_flags(&FP_STATUS); + if (unlikely(flags)) { set_float_exception_flags(0, &FP_STATUS); /* We need to massage the resulting exceptions. */ - if (exc & float_flag_invalid_cvti) { + if (flags & float_flag_invalid_cvti) { /* Overflow, either normal or infinity. */ if (float64_is_infinity(fa)) { exc = FPCR_INV; } else { exc = FPCR_IOV | FPCR_INE; } - } else if (exc & float_flag_invalid) { + } else if (flags & float_flag_invalid) { exc = FPCR_INV; - } else if (exc & float_flag_inexact) { + } else if (flags & float_flag_inexact) { exc = FPCR_INE; } } diff --git a/target/alpha/gdbstub.c b/target/alpha/gdbstub.c index 0f8fa150f89..1a7e2dd9202 100644 --- a/target/alpha/gdbstub.c +++ b/target/alpha/gdbstub.c @@ -23,8 +23,7 @@ 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 970c8697715..096eac34458 100644 --- a/target/alpha/helper.c +++ b/target/alpha/helper.c @@ -20,10 +20,13 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" #include "fpu/softfloat-types.h" #include "exec/helper-proto.h" #include "qemu/qemu-print.h" +#include "system/memory.h" #define CONVERT_BIT(X, SRC, DST) \ @@ -124,7 +127,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 +158,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 +289,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 +300,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 +326,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 +435,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 +486,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/int_helper.c b/target/alpha/int_helper.c index 5672696f6f1..6bfe63500e0 100644 --- a/target/alpha/int_helper.c +++ b/target/alpha/int_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" diff --git a/target/alpha/machine.c b/target/alpha/machine.c index 2b7c8148ff5..5f302b166da 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,8 +73,8 @@ static const VMStateDescription vmstate_env = { .fields = vmstate_env_fields, }; -static VMStateField vmstate_cpu_fields[] = { - VMSTATE_CPU(), +static const VMStateField vmstate_cpu_fields[] = { + VMSTATE_STRUCT(parent_obj, AlphaCPU, 0, vmstate_cpu_common, CPUState), 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 a39b52c5dd6..2113fe33ae2 100644 --- a/target/alpha/mem_helper.c +++ b/target/alpha/mem_helper.c @@ -20,8 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" static void do_unaligned_access(CPUAlphaState *env, vaddr addr, uintptr_t retaddr) { @@ -42,18 +41,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 +62,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 3f5253c0027..7dbbd557171 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_system_ss = ss.source_set() -alpha_system_ss.add(files('machine.c')) +alpha_system_ss.add(files( + 'machine.c', + 'sys_helper.c', +)) target_arch += {'alpha': alpha_ss} -target_softmmu_arch += {'alpha': alpha_system_ss} +target_system_arch += {'alpha': alpha_system_ss} diff --git a/target/alpha/sys_helper.c b/target/alpha/sys_helper.c index c83c92dd4ce..51e32544287 100644 --- a/target/alpha/sys_helper.c +++ b/target/alpha/sys_helper.c @@ -19,31 +19,15 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/tb-flush.h" #include "exec/helper-proto.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.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)); @@ -89,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 9be912c50c5..cebab0318cf 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -19,14 +19,14 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "sysemu/cpus.h" -#include "disas/disas.h" +#include "system/cpus.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" +#include "exec/translation-block.h" +#include "exec/target_page.h" #include "exec/log.h" #define HELPER_H "helper.h" @@ -54,6 +54,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; @@ -131,13 +134,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 +149,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 +158,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); } } @@ -244,12 +247,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) @@ -258,12 +271,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; } @@ -425,128 +438,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_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); + 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); - 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); - 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. */ - tcg_gen_movcond_i64(TCG_COND_NE, dest, src, tcg_constant_i64(mzero), - src, tcg_constant_i64(0)); - break; + 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); - 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)); + 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 */ @@ -582,7 +574,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; } @@ -591,7 +583,7 @@ 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); @@ -611,7 +603,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. */ @@ -619,7 +611,7 @@ 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); @@ -636,16 +628,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 } } @@ -678,9 +670,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); } } @@ -705,7 +697,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); } @@ -732,10 +724,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); } @@ -754,10 +746,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); } } @@ -797,7 +789,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); } @@ -826,7 +818,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); } @@ -1059,12 +1051,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: @@ -1088,17 +1080,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; @@ -1116,7 +1108,7 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode) } /* 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: @@ -1126,23 +1118,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); @@ -1163,19 +1155,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)); - + entry = ctx->palbr; entry += (palcode & 0x80 ? 0x2000 + (palcode - 0x80) * 64 : 0x1000 + palcode * 64); @@ -1254,9 +1244,9 @@ static DisasJumpType gen_mfpr(DisasContext *ctx, TCGv va, int regno) if (data == 0) { tcg_gen_movi_i64(va, 0); } else if (data & PR_LONG) { - tcg_gen_ld32s_i64(va, cpu_env, data & ~PR_LONG); + tcg_gen_ld32s_i64(va, tcg_env, data & ~PR_LONG); } else { - tcg_gen_ld_i64(va, cpu_env, data); + tcg_gen_ld_i64(va, tcg_env, data); } break; } @@ -1272,17 +1262,17 @@ static DisasJumpType gen_mtpr(DisasContext *ctx, TCGv vb, int regno) switch (regno) { case 255: /* TBIA */ - gen_helper_tbia(cpu_env); + gen_helper_tbia(tcg_env); break; case 254: /* TBIS */ - gen_helper_tbis(cpu_env, vb); + gen_helper_tbis(tcg_env, vb); break; case 253: /* WAIT */ - tcg_gen_st_i32(tcg_constant_i32(1), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(1), tcg_env, -offsetof(AlphaCPU, env) + offsetof(CPUState, halted)); return gen_excp(ctx, EXCP_HALTED, 0); @@ -1296,16 +1286,16 @@ static DisasJumpType gen_mtpr(DisasContext *ctx, TCGv vb, int regno) 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: @@ -1327,9 +1317,9 @@ static DisasJumpType gen_mtpr(DisasContext *ctx, TCGv vb, int regno) data = cpu_pr_data(regno); if (data != 0) { if (data & PR_LONG) { - tcg_gen_st32_i64(vb, cpu_env, data & ~PR_LONG); + tcg_gen_st32_i64(vb, tcg_env, data & ~PR_LONG); } else { - tcg_gen_st_i64(vb, cpu_env, data); + tcg_gen_st_i64(vb, tcg_env, data); } } break; @@ -1392,7 +1382,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); @@ -1594,7 +1584,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tcg_gen_ext32s_i64(vc, vb); tcg_gen_add_i64(tmp, tmp, vc); tcg_gen_ext32s_i64(vc, tmp); - gen_helper_check_overflow(cpu_env, vc, tmp); + gen_helper_check_overflow(tcg_env, vc, tmp); break; case 0x49: /* SUBL/V */ @@ -1603,7 +1593,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tcg_gen_ext32s_i64(vc, vb); tcg_gen_sub_i64(tmp, tmp, vc); tcg_gen_ext32s_i64(vc, tmp); - gen_helper_check_overflow(cpu_env, vc, tmp); + gen_helper_check_overflow(tcg_env, vc, tmp); break; case 0x4D: /* CMPLT */ @@ -1620,7 +1610,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tcg_gen_and_i64(tmp, tmp, tmp2); tcg_gen_shri_i64(tmp, tmp, 63); tcg_gen_movi_i64(tmp2, 0); - gen_helper_check_overflow(cpu_env, tmp, tmp2); + gen_helper_check_overflow(tcg_env, tmp, tmp2); break; case 0x69: /* SUBQ/V */ @@ -1633,7 +1623,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tcg_gen_and_i64(tmp, tmp, tmp2); tcg_gen_shri_i64(tmp, tmp, 63); tcg_gen_movi_i64(tmp2, 0); - gen_helper_check_overflow(cpu_env, tmp, tmp2); + gen_helper_check_overflow(tcg_env, tmp, tmp2); break; case 0x6D: /* CMPLE */ @@ -1683,16 +1673,12 @@ 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)); 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)); break; case 0x20: @@ -1924,7 +1910,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tcg_gen_ext32s_i64(vc, vb); tcg_gen_mul_i64(tmp, tmp, vc); tcg_gen_ext32s_i64(vc, tmp); - gen_helper_check_overflow(cpu_env, vc, tmp); + gen_helper_check_overflow(tcg_env, vc, tmp); break; case 0x60: /* MULQ/V */ @@ -1932,7 +1918,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) tmp2 = tcg_temp_new(); tcg_gen_muls2_i64(vc, tmp, va, vb); tcg_gen_sari_i64(tmp2, vc, 63); - gen_helper_check_overflow(cpu_env, tmp, tmp2); + gen_helper_check_overflow(tcg_env, tmp, tmp2); break; default: goto invalid_opc; @@ -1957,7 +1943,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) 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 */ @@ -1986,7 +1972,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) REQUIRE_REG_31(ra); REQUIRE_FEN; vb = load_fpr(ctx, rb); - gen_helper_sqrtg(vc, cpu_env, vb); + gen_helper_sqrtg(vc, tcg_env, vb); break; case 0x02B: /* SQRTT */ @@ -2009,22 +1995,22 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) case 0x00: /* ADDF */ REQUIRE_FEN; - gen_helper_addf(vc, cpu_env, va, vb); + gen_helper_addf(vc, tcg_env, va, vb); break; case 0x01: /* SUBF */ REQUIRE_FEN; - gen_helper_subf(vc, cpu_env, va, vb); + gen_helper_subf(vc, tcg_env, va, vb); break; case 0x02: /* MULF */ REQUIRE_FEN; - gen_helper_mulf(vc, cpu_env, va, vb); + gen_helper_mulf(vc, tcg_env, va, vb); break; case 0x03: /* DIVF */ REQUIRE_FEN; - gen_helper_divf(vc, cpu_env, va, vb); + gen_helper_divf(vc, tcg_env, va, vb); break; case 0x1E: /* CVTDG -- TODO */ @@ -2033,43 +2019,43 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) case 0x20: /* ADDG */ REQUIRE_FEN; - gen_helper_addg(vc, cpu_env, va, vb); + gen_helper_addg(vc, tcg_env, va, vb); break; case 0x21: /* SUBG */ REQUIRE_FEN; - gen_helper_subg(vc, cpu_env, va, vb); + gen_helper_subg(vc, tcg_env, va, vb); break; case 0x22: /* MULG */ REQUIRE_FEN; - gen_helper_mulg(vc, cpu_env, va, vb); + gen_helper_mulg(vc, tcg_env, va, vb); break; case 0x23: /* DIVG */ REQUIRE_FEN; - gen_helper_divg(vc, cpu_env, va, vb); + gen_helper_divg(vc, tcg_env, va, vb); break; case 0x25: /* CMPGEQ */ REQUIRE_FEN; - gen_helper_cmpgeq(vc, cpu_env, va, vb); + gen_helper_cmpgeq(vc, tcg_env, va, vb); break; case 0x26: /* CMPGLT */ REQUIRE_FEN; - gen_helper_cmpglt(vc, cpu_env, va, vb); + gen_helper_cmpglt(vc, tcg_env, va, vb); break; case 0x27: /* CMPGLE */ REQUIRE_FEN; - gen_helper_cmpgle(vc, cpu_env, va, vb); + gen_helper_cmpgle(vc, tcg_env, va, vb); break; case 0x2C: /* CVTGF */ REQUIRE_REG_31(ra); REQUIRE_FEN; - gen_helper_cvtgf(vc, cpu_env, vb); + gen_helper_cvtgf(vc, tcg_env, vb); break; case 0x2D: /* CVTGD -- TODO */ @@ -2079,19 +2065,19 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) /* CVTGQ */ REQUIRE_REG_31(ra); REQUIRE_FEN; - gen_helper_cvtgq(vc, cpu_env, vb); + gen_helper_cvtgq(vc, tcg_env, vb); break; case 0x3C: /* CVTQF */ REQUIRE_REG_31(ra); REQUIRE_FEN; - gen_helper_cvtqf(vc, cpu_env, vb); + gen_helper_cvtqf(vc, tcg_env, vb); break; case 0x3E: /* CVTQG */ REQUIRE_REG_31(ra); REQUIRE_FEN; - gen_helper_cvtqg(vc, cpu_env, vb); + gen_helper_cvtqg(vc, tcg_env, vb); break; default: goto invalid_opc; @@ -2242,7 +2228,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) /* MT_FPCR */ REQUIRE_FEN; va = load_fpr(ctx, ra); - gen_helper_store_fpcr(cpu_env, va); + gen_helper_store_fpcr(tcg_env, va); if (ctx->tb_rm == QUAL_RM_D) { /* Re-do the copy of the rounding mode to fp_status the next time we use dynamic rounding. */ @@ -2253,7 +2239,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) /* MF_FPCR */ REQUIRE_FEN; va = dest_fpr(ctx, ra); - gen_helper_load_fpcr(va, cpu_env); + gen_helper_load_fpcr(va, tcg_env); break; case 0x02A: /* FCMOVEQ */ @@ -2292,7 +2278,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) REQUIRE_FEN; vc = dest_fpr(ctx, rc); vb = load_fpr(ctx, rb); - gen_helper_cvtql(vc, cpu_env, vb); + gen_helper_cvtql(vc, tcg_env, vb); gen_fp_exc_raise(rc, fn11); break; default: @@ -2332,7 +2318,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) if (translator_io_start(&ctx->base)) { ret = DISAS_PC_STALE; } - gen_helper_load_pcc(va, cpu_env); + gen_helper_load_pcc(va, tcg_env); break; case 0xE000: /* RC */ @@ -2373,9 +2359,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; @@ -2628,7 +2618,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); } @@ -2827,35 +2817,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); @@ -2871,11 +2861,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; @@ -2911,14 +2902,20 @@ 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); @@ -2934,14 +2931,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(); @@ -2954,24 +2947,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 alpha_translate_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/alpha/vax_helper.c b/target/alpha/vax_helper.c index f94fb519dbd..c1d201e7b4d 100644 --- a/target/alpha/vax_helper.c +++ b/target/alpha/vax_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" diff --git a/target/arm/Kconfig b/target/arm/Kconfig index bf57d739cd1..7f8a2217ae1 100644 --- a/target/arm/Kconfig +++ b/target/arm/Kconfig @@ -6,6 +6,10 @@ config ARM # 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 2d8e41ab8a3..1dd79849c13 100644 --- a/target/arm/arch_dump.c +++ b/target/arm/arch_dump.c @@ -21,7 +21,9 @@ #include "qemu/osdep.h" #include "cpu.h" #include "elf.h" -#include "sysemu/dump.h" +#include "system/dump.h" +#include "cpu-features.h" +#include "internals.h" /* struct user_pt_regs from arch/arm64/include/uapi/asm/ptrace.h */ struct aarch64_user_regs { @@ -141,7 +143,6 @@ static int aarch64_write_elf64_prfpreg(WriteCoreDumpFunction f, return 0; } -#ifdef TARGET_AARCH64 static off_t sve_zreg_offset(uint32_t vq, int n) { off_t off = sizeof(struct aarch64_user_sve_header); @@ -229,7 +230,6 @@ static int aarch64_write_elf64_sve(WriteCoreDumpFunction f, return 0; } -#endif int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, DumpState *s) @@ -271,11 +271,9 @@ int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, return ret; } -#ifdef TARGET_AARCH64 if (cpu_isar_feature(aa64_sve, cpu)) { ret = aarch64_write_elf64_sve(f, env, cpuid, s); } -#endif return ret; } @@ -449,11 +447,9 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) if (class == ELFCLASS64) { note_size = AARCH64_PRSTATUS_NOTE_SIZE; note_size += AARCH64_PRFPREG_NOTE_SIZE; -#ifdef TARGET_AARCH64 if (cpu_isar_feature(aa64_sve, cpu)) { note_size += AARCH64_SVE_NOTE_SIZE(&cpu->env); } -#endif } else { note_size = ARM_PRSTATUS_NOTE_SIZE; if (cpu_isar_feature(aa32_vfp_simd, cpu)) { diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c index 326a03153df..20c70c7d6bb 100644 --- a/target/arm/arm-powerctl.c +++ b/target/arm/arm-powerctl.c @@ -15,7 +15,8 @@ #include "arm-powerctl.h" #include "qemu/log.h" #include "qemu/main-loop.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" +#include "target/arm/multiprocessing.h" #ifndef DEBUG_ARM_POWERCTL #define DEBUG_ARM_POWERCTL 0 @@ -37,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; } } @@ -65,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)); @@ -139,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; } @@ -150,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, @@ -247,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; } @@ -256,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); @@ -298,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; @@ -309,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); @@ -345,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 index b53d5efe13d..cefd2352638 100644 --- a/target/arm/arm-qmp-cmds.c +++ b/target/arm/arm-qmp-cmds.c @@ -26,11 +26,11 @@ #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 "qapi/qapi-commands-machine.h" +#include "qapi/qapi-commands-misc-arm.h" +#include "qobject/qdict.h" #include "qom/qom-qobject.h" +#include "cpu.h" static GICCapability *gic_cap_new(int version) { @@ -47,7 +47,7 @@ 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)) { + if (!kvm_arm_create_scratch_host_vcpu(fdarray, NULL)) { return; } @@ -95,7 +95,7 @@ static const char *cpu_model_advertised_features[] = { "sve640", "sve768", "sve896", "sve1024", "sve1152", "sve1280", "sve1408", "sve1536", "sve1664", "sve1792", "sve1920", "sve2048", "kvm-no-adjvtime", "kvm-steal-time", - "pauth", "pauth-impdef", "pauth-qarma3", + "pauth", "pauth-impdef", "pauth-qarma3", "pauth-qarma5", NULL }; @@ -104,7 +104,7 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, Error **errp) { CpuModelExpansionInfo *expansion_info; - const QDict *qdict_in = NULL; + const QDict *qdict_in; QDict *qdict_out; ObjectClass *oc; Object *obj; @@ -151,27 +151,20 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, } } - if (model->props) { - qdict_in = qobject_to(QDict, model->props); - if (!qdict_in) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict"); - return NULL; - } - } - obj = object_new(object_class_get_name(oc)); - if (qdict_in) { + if (model->props) { Visitor *visitor; Error *err = NULL; visitor = qobject_input_visitor_new(model->props); - if (!visit_start_struct(visitor, NULL, NULL, 0, errp)) { + if (!visit_start_struct(visitor, "model.props", NULL, 0, errp)) { visit_free(visitor); object_unref(obj); return NULL; } + qdict_in = qobject_to(QDict, model->props); i = 0; while ((name = cpu_model_advertised_features[i++]) != NULL) { if (qdict_get(qdict_in, name)) { @@ -237,8 +230,7 @@ static void arm_cpu_add_definition(gpointer data, gpointer user_data) typename = object_class_get_name(oc); info = g_malloc0(sizeof(*info)); - info->name = g_strndup(typename, - strlen(typename) - strlen("-" TYPE_ARM_CPU)); + info->name = cpu_model_from_type(typename); info->q_typename = g_strdup(typename); QAPI_LIST_PREPEND(*cpu_list, info); diff --git a/target/arm/common-semi-target.h b/target/arm/common-semi-target.h index 629d75ca5a7..da51f2d7f54 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/cpregs.h b/target/arm/cpregs.h index f1293d16c07..c1a7ae37356 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -21,6 +21,10 @@ #ifndef TARGET_ARM_CPREGS_H #define TARGET_ARM_CPREGS_H +#include "hw/registerfields.h" +#include "target/arm/kvm-consts.h" +#include "cpu.h" + /* * ARMCPRegInfo type field bits: */ @@ -118,6 +122,19 @@ 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, + /* + * Flag: this is a TLBI insn which (when FEAT_XS is present) also has + * an NXS variant at the same encoding except that crn is 1 greater, + * so when registering this cpreg automatically also register one + * for the TLBI NXS variant. (For QEMU the NXS variant behaves + * identically to the normal one, other than FGT trapping handling.) + */ + ARM_CP_ADD_TLBI_NXS = 1 << 21, }; /* @@ -312,20 +329,23 @@ typedef enum CPAccessResult { * Access fails due to a configurable trap or enable which would * result in a categorized exception syndrome giving information about * the failing instruction (ie syndrome category 0x3, 0x4, 0x5, 0x6, - * 0xc or 0x18). + * 0xc or 0x18). These traps are always to a specified target EL, + * never to the usual target EL. */ - CP_ACCESS_TRAP = (1 << 2), - CP_ACCESS_TRAP_EL2 = CP_ACCESS_TRAP | 2, - CP_ACCESS_TRAP_EL3 = CP_ACCESS_TRAP | 3, + CP_ACCESS_TRAP_BIT = (1 << 2), + CP_ACCESS_TRAP_EL1 = CP_ACCESS_TRAP_BIT | 1, + CP_ACCESS_TRAP_EL2 = CP_ACCESS_TRAP_BIT | 2, + CP_ACCESS_TRAP_EL3 = CP_ACCESS_TRAP_BIT | 3, /* - * Access fails and results in an exception syndrome 0x0 ("uncategorized"). + * Access fails with UNDEFINED, i.e. an exception syndrome 0x0 + * ("uncategorized"), which is what an undefined insn produces. * 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_UNDEFINED = (2 << 2), } CPAccessResult; /* Indexes into fgt_read[] */ @@ -613,6 +633,7 @@ FIELD(HDFGWTR_EL2, NBRBCTL, 60, 1) FIELD(HDFGWTR_EL2, NBRBDATA, 61, 1) FIELD(HDFGWTR_EL2, NPMSNEVFR_EL1, 62, 1) +FIELD(FGT, NXS, 13, 1) /* Honour HCR_EL2.FGTnXS to suppress FGT */ /* Which fine-grained trap bit register to check, if any */ FIELD(FGT, TYPE, 10, 3) FIELD(FGT, REV, 9, 1) /* Is bit sense reversed? */ @@ -631,6 +652,17 @@ FIELD(FGT, BITPOS, 0, 6) /* Bit position within the uint64_t */ #define DO_REV_BIT(REG, BITNAME) \ FGT_##BITNAME = FGT_##REG | FGT_REV | R_##REG##_EL2_##BITNAME##_SHIFT +/* + * The FGT bits for TLBI maintenance instructions accessible at EL1 always + * affect the "normal" TLBI insns; they affect the corresponding TLBI insns + * with the nXS qualifier only if HCRX_EL2.FGTnXS is 0. We define e.g. + * FGT_TLBIVAE1 to use for the normal insn, and FGT_TLBIVAE1NXS to use + * for the nXS qualified insn. + */ +#define DO_TLBINXS_BIT(REG, BITNAME) \ + FGT_##BITNAME = FGT_##REG | R_##REG##_EL2_##BITNAME##_SHIFT, \ + FGT_##BITNAME##NXS = FGT_##BITNAME | R_FGT_NXS_MASK + typedef enum FGTBit { /* * These bits tell us which register arrays to use: @@ -764,36 +796,36 @@ typedef enum FGTBit { 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_TLBINXS_BIT(HFGITR, TLBIVMALLE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIVAE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIASIDE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIVAAE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIVALE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIVAALE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIRVAE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIRVAAE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIRVALE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIRVAALE1OS), + DO_TLBINXS_BIT(HFGITR, TLBIVMALLE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIVAE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIASIDE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIVAAE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIVALE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIVAALE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIRVAE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIRVAAE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIRVALE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIRVAALE1IS), + DO_TLBINXS_BIT(HFGITR, TLBIRVAE1), + DO_TLBINXS_BIT(HFGITR, TLBIRVAAE1), + DO_TLBINXS_BIT(HFGITR, TLBIRVALE1), + DO_TLBINXS_BIT(HFGITR, TLBIRVAALE1), + DO_TLBINXS_BIT(HFGITR, TLBIVMALLE1), + DO_TLBINXS_BIT(HFGITR, TLBIVAE1), + DO_TLBINXS_BIT(HFGITR, TLBIASIDE1), + DO_TLBINXS_BIT(HFGITR, TLBIVAAE1), + DO_TLBINXS_BIT(HFGITR, TLBIVALE1), + DO_TLBINXS_BIT(HFGITR, TLBIVAALE1), DO_BIT(HFGITR, CFPRCTX), DO_BIT(HFGITR, DVPRCTX), DO_BIT(HFGITR, CPPRCTX), @@ -821,6 +853,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) */ @@ -862,6 +899,13 @@ struct ARMCPRegInfo { * 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 @@ -937,7 +981,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 @@ -948,6 +992,7 @@ struct ARMCPRegInfo { */ CPReadFn *orig_readfn; CPWriteFn *orig_writefn; + CPAccessFn *orig_accessfn; }; /* @@ -1079,4 +1124,66 @@ void define_cortex_a72_a57_a53_cp_reginfo(ARMCPU *cpu); 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; +} + +/* Macros for accessing a specified CP register bank */ +#define A32_BANKED_REG_GET(_env, _regname, _secure) \ + ((_secure) ? (_env)->cp15._regname##_s : (_env)->cp15._regname##_ns) + +#define A32_BANKED_REG_SET(_env, _regname, _secure, _val) \ + do { \ + if (_secure) { \ + (_env)->cp15._regname##_s = (_val); \ + } else { \ + (_env)->cp15._regname##_ns = (_val); \ + } \ + } while (0) + +/* + * Macros for automatically accessing a specific CP register bank depending on + * the current secure state of the system. These macros are not intended for + * supporting instruction translation reads/writes as these are dependent + * solely on the SCR.NS bit and not the mode. + */ +#define A32_BANKED_CURRENT_REG_GET(_env, _regname) \ + A32_BANKED_REG_GET((_env), _regname, \ + (arm_is_secure(_env) && !arm_el_is_aa64((_env), 3))) + +#define A32_BANKED_CURRENT_REG_SET(_env, _regname, _val) \ + A32_BANKED_REG_SET((_env), _regname, \ + (arm_is_secure(_env) && !arm_el_is_aa64((_env), 3)), \ + (_val)) + #endif /* TARGET_ARM_CPREGS_H */ diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h new file mode 100644 index 00000000000..4452e7c21e3 --- /dev/null +++ b/target/arm/cpu-features.h @@ -0,0 +1,1107 @@ +/* + * 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" +#include "cpu.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; +} + +static inline bool isar_feature_aa64_xs(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, XS) != 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_rpres(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, RPRES); +} + +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_afp(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, AFP) != 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 b3b35f7aa13..8b46c7c5708 100644 --- a/target/arm/cpu-param.h +++ b/target/arm/cpu-param.h @@ -2,47 +2,41 @@ * 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 #define ARM_CPU_PARAM_H #ifdef TARGET_AARCH64 -# define TARGET_LONG_BITS 64 # define TARGET_PHYS_ADDR_SPACE_BITS 52 # define TARGET_VIRT_ADDR_SPACE_BITS 52 #else -# define TARGET_LONG_BITS 32 # define TARGET_PHYS_ADDR_SPACE_BITS 40 # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif #ifdef CONFIG_USER_ONLY -#define TARGET_PAGE_BITS 12 -# ifdef TARGET_AARCH64 -# define TARGET_TAGGED_ADDRESSES +# if defined(TARGET_AARCH64) && defined(CONFIG_LINUX) +/* Allow user-only to vary page size from 4k */ +# define TARGET_PAGE_BITS_VARY +# else +# define TARGET_PAGE_BITS 12 # endif -#else +#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 +# define TARGET_PAGE_BITS_LEGACY 10 +#endif /* !CONFIG_USER_ONLY */ /* - * 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. + * ARM-specific extra insn start words: + * 1: Conditional execution bits + * 2: Partial exception syndrome for data aborts */ -# define TARGET_PAGE_ENTRY_EXTRA \ - uint8_t pte_attrs; \ - uint8_t shareability; \ - bool guarded; -#endif +#define TARGET_INSN_START_EXTRA_WORDS 2 #endif diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index 514c22ced9b..2fcb0e12525 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,31 @@ 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_phases: The parent class' reset phase handlers. - * - * An ARM CPU model. +#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). */ -struct ARMCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - const ARMCPUInfo *info; - DeviceRealize parent_realize; - ResettablePhases parent_phases; +enum { + M_REG_NS = 0, + M_REG_S = 1, + M_REG_NUM_BANKS = 2, }; - -#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 >*/ -}; - -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 b9e09a702d6..e025e241eda 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -23,15 +23,18 @@ #include "qemu/timer.h" #include "qemu/log.h" #include "exec/page-vary.h" +#include "exec/tswap.h" #include "target/arm/idau.h" #include "qemu/module.h" #include "qapi/error.h" #include "cpu.h" #ifdef CONFIG_TCG -#include "hw/core/tcg-cpu-ops.h" +#include "exec/translation-block.h" +#include "accel/tcg/cpu-ops.h" #endif /* CONFIG_TCG */ #include "internals.h" -#include "exec/exec-all.h" +#include "cpu-features.h" +#include "exec/target_page.h" #include "hw/qdev-properties.h" #if !defined(CONFIG_USER_ONLY) #include "hw/loader.h" @@ -40,13 +43,15 @@ #include "hw/intc/armv7m_nvic.h" #endif /* CONFIG_TCG */ #endif /* !CONFIG_USER_ONLY */ -#include "sysemu/tcg.h" -#include "sysemu/qtest.h" -#include "sysemu/hw_accel.h" +#include "system/tcg.h" +#include "system/qtest.h" +#include "system/hw_accel.h" #include "kvm_arm.h" #include "disas/capstone.h" #include "fpu/softfloat.h" #include "cpregs.h" +#include "target/arm/cpu-qom.h" +#include "target/arm/gtimer.h" static void arm_cpu_set_pc(CPUState *cs, vaddr value) { @@ -80,7 +85,7 @@ void arm_cpu_synchronize_from_tb(CPUState *cs, { /* The program counter is always up to date with CF_PCREL. */ if (!(tb_cflags(tb) & CF_PCREL)) { - CPUARMState *env = cs->env_ptr; + 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. @@ -97,7 +102,7 @@ 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 (tb_cflags(tb) & CF_PCREL) { @@ -117,8 +122,22 @@ void arm_restore_state_to_opc(CPUState *cs, env->exception.syndrome = data[2] << ARM_INSN_START_WORD2_SHIFT; } } + +int arm_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return arm_env_mmu_index(cpu_env(cs)); +} + #endif /* CONFIG_TCG */ +#ifndef CONFIG_USER_ONLY +/* + * 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); @@ -126,9 +145,11 @@ 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); } +#endif /* !CONFIG_USER_ONLY */ void arm_register_pre_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, void *opaque) @@ -204,15 +225,15 @@ static void cp_reg_check_reset(gpointer key, gpointer value, gpointer opaque) assert(oldvalue == newvalue); } -static void arm_cpu_reset_hold(Object *obj) +static void arm_cpu_reset_hold(Object *obj, ResetType type) { - CPUState *s = CPU(obj); - 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; if (acc->parent_phases.hold) { - acc->parent_phases.hold(obj); + acc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUARMState, end_reset_fields)); @@ -225,7 +246,7 @@ static void arm_cpu_reset_hold(Object *obj) 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'; @@ -295,6 +316,8 @@ static void arm_cpu_reset_hold(Object *obj) 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)) { @@ -428,7 +451,7 @@ static void arm_cpu_reset_hold(Object *obj) /* 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. @@ -441,8 +464,8 @@ static void arm_cpu_reset_hold(Object *obj) * 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, @@ -527,18 +550,21 @@ static void arm_cpu_reset_hold(Object *obj) env->sau.ctrl = 0; } - set_flush_to_zero(1, &env->vfp.standard_fp_status); - 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); + set_flush_to_zero(1, &env->vfp.fp_status[FPST_STD]); + set_flush_inputs_to_zero(1, &env->vfp.fp_status[FPST_STD]); + set_default_nan_mode(1, &env->vfp.fp_status[FPST_STD]); + set_default_nan_mode(1, &env->vfp.fp_status[FPST_STD_F16]); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A32]); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64]); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_STD]); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A32_F16]); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_STD_F16]); + arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_AH]); + set_flush_to_zero(1, &env->vfp.fp_status[FPST_AH]); + set_flush_inputs_to_zero(1, &env->vfp.fp_status[FPST_AH]); + arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_AH_F16]); + #ifndef CONFIG_USER_ONLY if (kvm_enabled()) { kvm_arm_reset_vcpu(cpu); @@ -553,6 +579,101 @@ static void arm_cpu_reset_hold(Object *obj) } } +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, @@ -560,9 +681,10 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, 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. @@ -573,13 +695,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: @@ -587,13 +732,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. */ @@ -689,8 +834,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); @@ -699,6 +843,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); @@ -747,7 +933,7 @@ static bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) found: cs->exception_index = excp_idx; env->exception.target_el = target_el; - cc->tcg_ops->do_interrupt(cs); + cs->cc->tcg_ops->do_interrupt(cs); return true; } @@ -762,7 +948,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)) { @@ -783,7 +970,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)) { @@ -795,6 +983,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) { /* @@ -824,7 +1054,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) && @@ -850,8 +1082,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 { @@ -863,53 +1099,51 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) } } -static void arm_cpu_kvm_set_irq(void *opaque, int irq, int level) +static bool arm_cpu_virtio_is_big_endian(CPUState *cs) { -#ifdef CONFIG_KVM - ARMCPU *cpu = opaque; + ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; - CPUState *cs = CPU(cpu); - uint32_t linestate_bit; - int irq_id; - switch (irq) { - case ARM_CPU_IRQ: - irq_id = KVM_ARM_IRQ_CPU_IRQ; - linestate_bit = CPU_INTERRUPT_HARD; - break; - case ARM_CPU_FIQ: - irq_id = KVM_ARM_IRQ_CPU_FIQ; - linestate_bit = CPU_INTERRUPT_FIQ; - break; - default: - g_assert_not_reached(); - } + cpu_synchronize_state(cs); + return arm_cpu_data_is_big_endian(env); +} - if (level) { - env->irq_line_state |= linestate_bit; - } else { - env->irq_line_state &= ~linestate_bit; +#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); + } } - kvm_arm_set_irq(cs->cpu_index, KVM_ARM_IRQ_TYPE_CPU, irq_id, !!level); -#endif + return leave_halt; } +#endif -static bool arm_cpu_virtio_is_big_endian(CPUState *cs) +static void arm_wfxt_timer_cb(void *opaque) { - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; + ARMCPU *cpu = opaque; + CPUState *cs = CPU(cpu); - cpu_synchronize_state(cs); - return arm_cpu_data_is_big_endian(env); + /* + * 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) { ARMCPU *ac = ARM_CPU(cpu); CPUARMState *env = &ac->env; - bool sctlr_b; + bool sctlr_b = arm_sctlr_b(env); if (is_a64(env)) { info->cap_arch = CS_ARCH_ARM64; @@ -936,13 +1170,9 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) info->cap_mode = cap_mode; } - sctlr_b = arm_sctlr_b(env); + info->endian = BFD_ENDIAN_LITTLE; if (bswap_code(sctlr_b)) { -#if TARGET_BIG_ENDIAN - info->endian = BFD_ENDIAN_LITTLE; -#else - info->endian = BFD_ENDIAN_BIG; -#endif + info->endian = target_big_endian() ? BFD_ENDIAN_LITTLE : BFD_ENDIAN_BIG; } info->flags &= ~INSN_ARM_BE32; #ifndef CONFIG_USER_ONLY @@ -952,8 +1182,6 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) #endif } -#ifdef TARGET_AARCH64 - static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) { ARMCPU *cpu = ARM_CPU(cs); @@ -961,6 +1189,7 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) uint32_t psr = pstate_read(env); int i, j; int el = arm_current_el(env); + uint64_t hcr = arm_hcr_el2_eff(env); const char *ns_status; bool sve; @@ -998,6 +1227,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; @@ -1106,15 +1339,6 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } -#else - -static inline void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) -{ - g_assert_not_reached(); -} - -#endif - static void arm_cpu_dump_state(CPUState *cs, FILE *f, int flags) { ARMCPU *cpu = ARM_CPU(cs); @@ -1204,18 +1428,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); @@ -1236,12 +1464,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, @@ -1267,40 +1496,43 @@ static void arm_cpu_initfn(Object *obj) } } -static Property arm_cpu_gt_cntfrq_property = - DEFINE_PROP_UINT64("cntfrq", ARMCPU, gt_cntfrq_hz, - NANOSECONDS_PER_SECOND / GTIMER_SCALE); +/* + * 0 means "unset, use the default value". That default might vary depending + * on the CPU type, and is set in the realize fn. + */ +static const Property arm_cpu_gt_cntfrq_property = + DEFINE_PROP_UINT64("cntfrq", ARMCPU, gt_cntfrq_hz, 0); -static Property arm_cpu_reset_cbar_property = +static const Property arm_cpu_reset_cbar_property = DEFINE_PROP_UINT64("reset-cbar", ARMCPU, reset_cbar, 0); -static Property arm_cpu_reset_hivecs_property = +static const Property arm_cpu_reset_hivecs_property = DEFINE_PROP_BOOL("reset-hivecs", ARMCPU, reset_hivecs, false); #ifndef CONFIG_USER_ONLY -static Property arm_cpu_has_el2_property = +static const Property arm_cpu_has_el2_property = DEFINE_PROP_BOOL("has_el2", ARMCPU, has_el2, true); -static Property arm_cpu_has_el3_property = +static const Property arm_cpu_has_el3_property = DEFINE_PROP_BOOL("has_el3", ARMCPU, has_el3, true); #endif -static Property arm_cpu_cfgend_property = +static const Property arm_cpu_cfgend_property = DEFINE_PROP_BOOL("cfgend", ARMCPU, cfgend, false); -static Property arm_cpu_has_vfp_property = +static const Property arm_cpu_has_vfp_property = DEFINE_PROP_BOOL("vfp", ARMCPU, has_vfp, true); -static Property arm_cpu_has_vfp_d32_property = +static const Property arm_cpu_has_vfp_d32_property = DEFINE_PROP_BOOL("vfp-d32", ARMCPU, has_vfp_d32, true); -static Property arm_cpu_has_neon_property = +static const Property arm_cpu_has_neon_property = DEFINE_PROP_BOOL("neon", ARMCPU, has_neon, true); -static Property arm_cpu_has_dsp_property = +static const Property arm_cpu_has_dsp_property = DEFINE_PROP_BOOL("dsp", ARMCPU, has_dsp, true); -static Property arm_cpu_has_mpu_property = +static const Property arm_cpu_has_mpu_property = DEFINE_PROP_BOOL("has-mpu", ARMCPU, has_mpu, true); /* This is like DEFINE_PROP_UINT32 but it doesn't set the default value, @@ -1308,7 +1540,7 @@ static Property arm_cpu_has_mpu_property = * the right value for that particular CPU type, and we don't want * to override that with an incorrect constant value. */ -static Property arm_cpu_pmsav7_dregion_property = +static const Property arm_cpu_pmsav7_dregion_property = DEFINE_PROP_UNSIGNED_NODEFAULT("pmsav7-dregion", ARMCPU, pmsav7_dregion, qdev_prop_uint32, uint32_t); @@ -1336,6 +1568,35 @@ static void arm_set_pmu(Object *obj, bool value, Error **errp) cpu->has_pmu = value; } +static bool aarch64_cpu_get_aarch64(Object *obj, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + + return arm_feature(&cpu->env, ARM_FEATURE_AARCH64); +} + +static void aarch64_cpu_set_aarch64(Object *obj, bool value, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + + /* + * At this time, this property is only allowed if KVM is enabled. This + * restriction allows us to avoid fixing up functionality that assumes a + * uniform execution state like do_interrupt. + */ + if (value == false) { + if (!kvm_enabled() || !kvm_arm_aarch32_supported()) { + error_setg(errp, "'aarch64' feature cannot be disabled " + "unless KVM is enabled and 32-bit EL1 " + "is supported"); + return; + } + unset_feature(&cpu->env, ARM_FEATURE_AARCH64); + } else { + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + } +} + unsigned int gt_cntfrq_period_ns(ARMCPU *cpu) { /* @@ -1463,6 +1724,13 @@ void arm_cpu_post_init(Object *obj) */ arm_cpu_propagate_feature_implications(cpu); + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + object_property_add_bool(obj, "aarch64", aarch64_cpu_get_aarch64, + aarch64_cpu_set_aarch64); + object_property_set_description(obj, "aarch64", + "Set on/off to enable/disable aarch64 " + "execution state "); + } if (arm_feature(&cpu->env, ARM_FEATURE_CBAR) || arm_feature(&cpu->env, ARM_FEATURE_CBAR_RO)) { qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_cbar_property); @@ -1518,6 +1786,10 @@ void arm_cpu_post_init(Object *obj) } } else if (cpu_isar_feature(aa32_vfp, cpu)) { cpu->has_vfp = true; + 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; /* @@ -1589,7 +1861,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 @@ -1631,6 +1903,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 } @@ -1638,7 +1913,6 @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp) { Error *local_err = NULL; -#ifdef TARGET_AARCH64 if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { arm_cpu_sve_finalize(cpu, &local_err); if (local_err != NULL) { @@ -1646,6 +1920,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); @@ -1664,7 +1948,6 @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp) return; } } -#endif if (kvm_enabled()) { kvm_arm_steal_time_finalize(cpu, &local_err); @@ -1681,12 +1964,11 @@ 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; +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) /* Use pc-relative instructions in system-mode */ -#ifndef CONFIG_USER_ONLY - cs->tcg_cflags |= CF_PCREL; + tcg_cflags_set(cs, CF_PCREL); #endif /* If we needed to query the host kernel for the CPU features @@ -1702,6 +1984,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 @@ -1750,18 +2052,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); @@ -1773,6 +2064,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) arm_gt_stimer_cb, cpu); cpu->gt_timer[GTIMER_HYPVIRT] = timer_new(QEMU_CLOCK_VIRTUAL, scale, arm_gt_hvtimer_cb, cpu); + cpu->gt_timer[GTIMER_S_EL2_PHYS] = timer_new(QEMU_CLOCK_VIRTUAL, scale, + arm_gt_sel2timer_cb, cpu); + cpu->gt_timer[GTIMER_S_EL2_VIRT] = timer_new(QEMU_CLOCK_VIRTUAL, scale, + arm_gt_sel2vtimer_cb, cpu); } #endif @@ -1968,32 +2263,40 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * We rely on no XScale CPU having VFP so we can use the same bits in the * TB flags field for VECSTRIDE and XSCALE_CPAR. */ - assert(arm_feature(&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 @@ -2001,8 +2304,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) { @@ -2010,7 +2313,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; @@ -2086,17 +2389,32 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) #ifndef CONFIG_USER_ONLY /* - * If we do not have tag-memory provided by the machine, - * reduce MTE support to instructions enabled at EL0. + * 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 (cpu->tag_memory == NULL) { + 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 some architectural features in the ID registers @@ -2131,9 +2449,6 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) /* FEAT_MPAM (Memory Partitioning and Monitoring Extension) */ cpu->isar.id_aa64pfr0 = FIELD_DP64(cpu->isar.id_aa64pfr0, ID_AA64PFR0, MPAM, 0); - /* FEAT_NV (Nested Virtualization) */ - cpu->isar.id_aa64mmfr2 = - FIELD_DP64(cpu->isar.id_aa64mmfr2, ID_AA64MMFR2, NV, 0); } /* MPU can be configured out of a PMSA CPU either by setting has-mpu @@ -2210,6 +2525,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) register_cp_regs_for_features(cpu); arm_cpu_register_gdb_regs_for_features(cpu); + arm_cpu_register_gdb_commands(cpu); init_cpreg_list(cpu); @@ -2304,37 +2620,79 @@ 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; } -static Property arm_cpu_properties[] = { +static const Property arm_cpu_properties[] = { DEFINE_PROP_UINT64("midr", ARMCPU, midr, 0), DEFINE_PROP_UINT64("mp-affinity", ARMCPU, 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), - DEFINE_PROP_END_OF_LIST() + /* True to default to the backward-compat old CNTFRQ rather than 1Ghz */ + DEFINE_PROP_BOOL("backcompat-cntfrq", ARMCPU, backcompat_cntfrq, false), + DEFINE_PROP_BOOL("backcompat-pauth-default-use-qarma5", ARMCPU, + backcompat_pauth_default_use_qarma5, false), }; -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_gdbstub_is_aarch64(cpu)) { + return "aarch64"; + } if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - return g_strdup("iwmmxt"); + return "iwmmxt"; } - return g_strdup("arm"); + return "arm"; } -#ifndef CONFIG_USER_ONLY +static const char *arm_gdb_get_core_xml_file(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + if (arm_gdbstub_is_aarch64(cpu)) { + return "aarch64-core.xml"; + } + if (arm_feature(env, ARM_FEATURE_M)) { + return "arm-m-profile.xml"; + } + return "arm-core.xml"; +} + +#ifdef CONFIG_USER_ONLY +/** + * aarch64_untagged_addr: + * + * Remove any address tag from @x. This is explicitly related to the + * linux syscall TIF_TAGGED_ADDR setting, not TBI in general. + * + * There should be a better place to put this, but we need this in + * include/exec/cpu_ldst.h, and not some place linux-user specific. + * + * Note that arm-*-user will never set tagged_addr_enable. + */ +static vaddr aarch64_untagged_addr(CPUState *cs, vaddr x) +{ + 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. + */ + x &= sextract64(x, 0, 56); + } + return x; +} +#else #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps arm_sysemu_ops = { + .has_work = arm_cpu_has_work, .get_phys_page_attrs_debug = arm_cpu_get_phys_page_attrs_debug, .asidx_from_attrs = arm_asidx_from_attrs, .write_elf32_note = arm_cpu_write_elf32_note, @@ -2345,18 +2703,52 @@ static const struct SysemuCPUOps arm_sysemu_ops = { #endif #ifdef CONFIG_TCG -static const struct TCGCPUOps arm_tcg_ops = { +#ifndef CONFIG_USER_ONLY +static vaddr aprofile_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + /* + * The Stage2 and Phys indexes are only used for ptw on arm32, + * and all pte's are aligned, so we never produce a wrap for these. + * Double check that we're not truncating a 40-bit physical address. + */ + assert((unsigned)mmu_idx < (ARMMMUIdx_Stage2_S & ARM_MMU_IDX_COREIDX_MASK)); + + if (!is_a64(cpu_env(cs))) { + return (uint32_t)result; + } + + /* + * TODO: For FEAT_CPA2, decide how to we want to resolve + * Unpredictable_CPACHECK in AddressIncrement. + */ + return result; +} +#endif /* !CONFIG_USER_ONLY */ + +static const TCGCPUOps arm_tcg_ops = { + .mttcg_supported = true, + /* ARM processors have a weak memory model */ + .guest_default_memory_order = 0, + .initialize = arm_translate_init, + .translate_code = arm_translate_code, + .get_tb_cpu_state = arm_get_tb_cpu_state, .synchronize_from_tb = arm_cpu_synchronize_from_tb, .debug_excp_handler = arm_debug_excp_handler, .restore_state_to_opc = arm_restore_state_to_opc, + .mmu_index = arm_cpu_mmu_index, #ifdef CONFIG_USER_ONLY .record_sigsegv = arm_cpu_record_sigsegv, .record_sigbus = arm_cpu_record_sigbus, + .untagged_addr = aarch64_untagged_addr, #else - .tlb_fill = arm_cpu_tlb_fill, + .tlb_fill_align = arm_cpu_tlb_fill_align, + .pointer_wrap = aprofile_pointer_wrap, .cpu_exec_interrupt = arm_cpu_exec_interrupt, + .cpu_exec_halt = arm_cpu_exec_halt, + .cpu_exec_reset = cpu_reset, .do_interrupt = arm_cpu_do_interrupt, .do_transaction_failed = arm_cpu_do_transaction_failed, .do_unaligned_access = arm_cpu_do_unaligned_access, @@ -2367,7 +2759,7 @@ static const struct TCGCPUOps arm_tcg_ops = { }; #endif /* CONFIG_TCG */ -static void arm_cpu_class_init(ObjectClass *oc, void *data) +static void arm_cpu_class_init(ObjectClass *oc, const void *data) { ARMCPUClass *acc = ARM_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(acc); @@ -2383,7 +2775,6 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) &acc->parent_phases); cc->class_by_name = arm_cpu_class_by_name; - cc->has_work = arm_cpu_has_work; cc->dump_state = arm_cpu_dump_state; cc->set_pc = arm_cpu_set_pc; cc->get_pc = arm_cpu_get_pc; @@ -2392,10 +2783,8 @@ 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_get_core_xml_file = arm_gdb_get_core_xml_file; cc->gdb_stop_before_watchpoint = true; cc->disas_set_info = arm_disas_set_info; @@ -2412,27 +2801,28 @@ static void arm_cpu_instance_init(Object *obj) arm_cpu_post_init(obj); } -static void cpu_register_class_init(ObjectClass *oc, void *data) +static void cpu_register_class_init(ObjectClass *oc, const void *data) { ARMCPUClass *acc = ARM_CPU_CLASS(oc); + CPUClass *cc = CPU_CLASS(acc); acc->info = data; + if (acc->info->deprecation_note) { + cc->deprecation_note = acc->info->deprecation_note; + } } 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, + .class_data = info, }; type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name); - type_register(&type_info); + type_register_static(&type_info); g_free((void *)type_info.name); } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index f2e3dc49a66..302c24e2324 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -24,15 +24,14 @@ #include "qemu/cpu-float.h" #include "hw/registerfields.h" #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.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) - -#ifdef TARGET_AARCH64 -#define KVM_HAVE_MCE_INJECTION 1 -#endif +#include "target/arm/multiprocessing.h" +#include "target/arm/gtimer.h" #define EXCP_UDEF 1 /* undefined instruction */ #define EXCP_SWI 2 /* software interrupt */ @@ -58,6 +57,10 @@ #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 +#define EXCP_MON_TRAP 29 /* AArch32 trap to Monitor mode */ /* NB: add new EXCP_ defines to the array in arm_log_exception() too */ #define ARMV7M_EXCP_RESET 1 @@ -72,26 +75,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 @@ -107,25 +98,13 @@ 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 - */ -#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. @@ -136,23 +115,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 { @@ -160,18 +137,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. @@ -198,17 +163,12 @@ typedef struct ARMGenericTimer { * Align the data for use with TCG host vector operations. */ -#ifdef TARGET_AARCH64 -# define ARM_MAX_VQ 16 -#else -# define ARM_MAX_VQ 1 -#endif +#define ARM_MAX_VQ 16 typedef struct ARMVectorReg { uint64_t d[2 * ARM_MAX_VQ] QEMU_ALIGNED(16); } ARMVectorReg; -#ifdef TARGET_AARCH64 /* In AArch32 mode, predicate registers do not exist at all. */ typedef struct ARMPredicateReg { uint64_t p[DIV_ROUND_UP(2 * ARM_MAX_VQ, 8)] QEMU_ALIGNED(16); @@ -218,18 +178,72 @@ typedef struct ARMPredicateReg { typedef struct ARMPACKey { uint64_t lo, hi; } ARMPACKey; -#endif /* See the commentary above the TBFLAG field definitions. */ typedef struct CPUARMTBFlags { uint32_t flags; - target_ulong flags2; + uint64_t flags2; } CPUARMTBFlags; typedef struct ARMMMUFaultInfo ARMMMUFaultInfo; typedef struct NVICState NVICState; +/* + * Enum for indexing vfp.fp_status[]. + * + * FPST_A32: is the "normal" fp status for AArch32 insns + * FPST_A64: is the "normal" fp status for AArch64 insns + * FPST_A32_F16: used for AArch32 half-precision calculations + * FPST_A64_F16: used for AArch64 half-precision calculations + * FPST_STD: the ARM "Standard FPSCR Value" + * FPST_STD_F16: used for half-precision + * calculations with the ARM "Standard FPSCR Value" + * FPST_AH: used for the A64 insns which change behaviour + * when FPCR.AH == 1 (bfloat16 conversions and multiplies, + * and the reciprocal and square root estimate/step insns) + * FPST_AH_F16: used for the A64 insns which change behaviour + * when FPCR.AH == 1 (bfloat16 conversions and multiplies, + * and the reciprocal and square root estimate/step insns); + * for half-precision + * + * Half-precision operations are governed by a separate + * flush-to-zero control bit in FPSCR:FZ16. We pass a separate + * status structure to control this. + * + * The "Standard FPSCR", ie default-NaN, flush-to-zero, + * round-to-nearest and is used by any operations (generally + * Neon) which the architecture defines as controlled by the + * standard FPSCR value rather than the FPSCR. + * + * The "standard FPSCR but for fp16 ops" is needed because + * the "standard FPSCR" tracks the FPSCR.FZ16 bit rather than + * using a fixed value for it. + * + * FPST_AH is needed because some insns have different + * behaviour when FPCR.AH == 1: they don't update cumulative + * exception flags, they act like FPCR.{FZ,FIZ} = {1,1} and + * they ignore FPCR.RMode. But they don't ignore FPCR.FZ16, + * which means we need an FPST_AH_F16 as well. + * + * To avoid having to transfer exception bits around, we simply + * say that the FPSCR cumulative exception flags are the logical + * OR of the flags in the four fp statuses. This relies on the + * only thing which needs to read the exception flags being + * an explicit FPSCR read. + */ +typedef enum ARMFPStatusFlavour { + FPST_A32, + FPST_A64, + FPST_A32_F16, + FPST_A64_F16, + FPST_AH, + FPST_AH_F16, + FPST_STD, + FPST_STD_F16, +} ARMFPStatusFlavour; +#define FPST_COUNT 8 + typedef struct CPUArchState { /* Regs for current mode. */ uint32_t regs[16]; @@ -484,6 +498,7 @@ typedef struct CPUArchState { uint64_t c14_cntkctl; /* Timer 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. */ @@ -547,6 +562,9 @@ typedef struct CPUArchState { uint64_t gpccr_el3; uint64_t gptbr_el3; uint64_t mfar_el3; + + /* NV2 register */ + uint64_t vncr_el2; } cp15; struct { @@ -630,55 +648,31 @@ typedef struct CPUArchState { struct { ARMVectorReg zregs[32]; -#ifdef TARGET_AARCH64 /* Store FFR as pregs[16] to make it easier to treat as any other. */ #define FFR_PRED_NUM 16 ARMPredicateReg pregs[17]; /* Scratch space for aa64 sve predicate temporary. */ ARMPredicateReg preg_tmp; -#endif /* We store these fpcsr fields separately for convenience. */ uint32_t qc[4] QEMU_ALIGNED(16); 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. */ uint32_t scratch[8]; - /* There are a number of distinct float control structures: - * - * fp_status: is the "normal" fp status. - * fp_status_fp16: used for half-precision calculations - * standard_fp_status : the ARM "Standard FPSCR Value" - * standard_fp_status_fp16 : used for half-precision - * calculations with the ARM "Standard FPSCR Value" - * - * Half-precision operations are governed by a separate - * flush-to-zero control bit in FPSCR:FZ16. We pass a separate - * status structure to control this. - * - * The "Standard FPSCR", ie default-NaN, flush-to-zero, - * round-to-nearest and is used by any operations (generally - * Neon) which the architecture defines as controlled by the - * standard FPSCR value rather than the FPSCR. - * - * The "standard FPSCR but for fp16 ops" is needed because - * the "standard FPSCR" tracks the FPSCR.FZ16 bit rather than - * using a fixed value for it. - * - * To avoid having to transfer exception bits around, we simply - * say that the FPSCR cumulative exception flags are the logical - * OR of the flags in the four fp statuses. This relies on the - * only thing which needs to read the exception flags being - * an explicit FPSCR read. - */ - float_status fp_status; - float_status fp_status_f16; - float_status standard_fp_status; - float_status standard_fp_status_f16; + /* There are a number of distinct float control structures. */ + float_status fp_status[FPST_COUNT]; uint64_t zcr_el[4]; /* ZCR_EL[1-3] */ uint64_t smcr_el[4]; /* SMCR_EL[1-3] */ @@ -703,7 +697,6 @@ typedef struct CPUArchState { uint32_t cregs[16]; } iwmmxt; -#ifdef TARGET_AARCH64 struct { ARMPACKey apia; ARMPACKey apib; @@ -735,7 +728,6 @@ typedef struct CPUArchState { * to keep the offsets into the rest of the structure smaller. */ ARMVectorReg zarray[ARM_MAX_VQ * 16]; -#endif struct CPUBreakpoint *cpu_breakpoint[16]; struct CPUWatchpoint *cpu_watchpoint[16]; @@ -791,12 +783,9 @@ typedef struct CPUArchState { #else /* CONFIG_USER_ONLY */ /* For usermode syscall translation. */ bool eabi; -#endif /* CONFIG_USER_ONLY */ - -#ifdef TARGET_TAGGED_ADDRESSES /* Linux syscall tagged address support */ bool tagged_addr_enable; -#endif +#endif /* CONFIG_USER_ONLY */ } CPUARMState; static inline void set_feature(CPUARMState *env, int feature) @@ -852,11 +841,8 @@ typedef struct { * An ARM CPU core. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUARMState env; /* Coprocessor information */ @@ -881,10 +867,10 @@ struct ArchCPU { uint64_t *cpreg_vmstate_values; int32_t cpreg_vmstate_array_len; - DynamicGDBXMLInfo dyn_sysreg_xml; - DynamicGDBXMLInfo dyn_svereg_xml; - DynamicGDBXMLInfo dyn_m_systemreg_xml; - DynamicGDBXMLInfo dyn_m_secextreg_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]; @@ -893,6 +879,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 */ @@ -939,6 +928,8 @@ 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 */ @@ -961,7 +952,6 @@ struct ArchCPU { */ uint32_t kvm_target; -#ifdef CONFIG_KVM /* KVM init features for this CPU */ uint32_t kvm_init_features[7]; @@ -974,7 +964,6 @@ struct ArchCPU { /* KVM steal time */ OnOffAuto kvm_steal_time; -#endif /* CONFIG_KVM */ /* Uniprocessor system with MP extensions */ bool mp_is_up; @@ -984,6 +973,12 @@ struct ArchCPU { */ bool host_cpu_probe_failed; + /* QOM property to indicate we should use the back-compat CNTFRQ default */ + bool backcompat_cntfrq; + + /* QOM property to indicate we should use the back-compat QARMA5 default */ + bool backcompat_pauth_default_use_qarma5; + /* Specify the number of cores in this CPU cluster. Used for the L2CTLR * register. */ @@ -1039,6 +1034,7 @@ struct ArchCPU { 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; @@ -1073,6 +1069,7 @@ struct ArchCPU { bool prop_pauth; bool prop_pauth_impdef; bool prop_pauth_qarma3; + bool prop_pauth_qarma5; bool prop_lpa2; /* DCZ blocksize, in log_2(words), ie low 4 bits of DCZID_EL0 */ @@ -1119,12 +1116,58 @@ struct ArchCPU { uint64_t gt_cntfrq_hz; }; +typedef struct ARMCPUInfo { + const char *name; + const char *deprecation_note; + void (*initfn)(Object *obj); + void (*class_init)(ObjectClass *oc, const 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; +}; + +/* 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); +void arm_gt_sel2timer_cb(void *opaque); +void arm_gt_sel2vtimer_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; @@ -1139,18 +1182,33 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, 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); -/* 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); -#ifdef TARGET_AARCH64 +/** + * 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); + 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); @@ -1182,13 +1240,6 @@ static inline uint64_t *sve_bswap64(uint64_t *dst, uint64_t *src, int nr) #endif } -#else -static inline void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq) { } -static inline void aarch64_sve_change_el(CPUARMState *env, int o, - int n, bool a) -{ } -#endif - void aarch64_sync_32_to_64(CPUARMState *env); void aarch64_sync_64_to_32(CPUARMState *env); @@ -1315,6 +1366,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 */ @@ -1338,73 +1391,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) @@ -1426,6 +1412,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 \ @@ -1451,41 +1439,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. @@ -1499,6 +1452,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) @@ -1693,21 +1647,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) @@ -1746,71 +1685,107 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_GPF (1ULL << 48) #define SCR_NSE (1ULL << 62) -#define HSTR_TTEE (1 << 16) -#define HSTR_TJDBX (1 << 17) - -#define CNTHCTL_CNTVMASK (1 << 18) -#define CNTHCTL_CNTPMASK (1 << 19) - /* 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. - */ -#define FPSR_MASK 0xf800009f -#define FPCR_MASK 0x07ff9f00 - + * 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. + */ + +/* FPCR bits */ +#define FPCR_FIZ (1 << 0) /* Flush Inputs to Zero (FEAT_AFP) */ +#define FPCR_AH (1 << 1) /* Alternate Handling (FEAT_AFP) */ +#define FPCR_NEP (1 << 2) /* SIMD scalar ops preserve elts (FEAT_AFP) */ #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) + +/* 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 */ + +/* Cumulative exception status bits */ +#define FPSR_CEXC_MASK (FPSR_IOC | FPSR_DZC | FPSR_OFC | FPSR_UFC | FPSR_IXC | FPSR_IDC) + +#define FPSR_NZCV_MASK (FPSR_N | FPSR_Z | FPSR_C | FPSR_V) +#define FPSR_NZCVQC_MASK (FPSR_NZCV_MASK | FPSR_QC) + +/* 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); -static inline uint32_t vfp_get_fpsr(CPUARMState *env) -{ - return vfp_get_fpscr(env) & FPSR_MASK; -} +/** + * vfp_get_fpsr: read the AArch64 FPSR + * @env: CPU context + * + * Return the current AArch64 FPSR value + */ +uint32_t vfp_get_fpsr(CPUARMState *env); -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); -} +/** + * vfp_get_fpcr: read the AArch64 FPCR + * @env: CPU context + * + * Return the current AArch64 FPCR value + */ +uint32_t vfp_get_fpcr(CPUARMState *env); -static inline uint32_t vfp_get_fpcr(CPUARMState *env) -{ - return vfp_get_fpscr(env) & FPCR_MASK; -} +/** + * vfp_set_fpsr: write the AArch64 FPSR + * @env: CPU context + * @value: new value + */ +void vfp_set_fpsr(CPUARMState *env, uint32_t value); -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); -} +/** + * 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, @@ -2166,6 +2141,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) @@ -2200,6 +2176,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) @@ -2227,6 +2210,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) @@ -2258,6 +2247,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) @@ -2275,11 +2265,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) @@ -2287,12 +2295,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) @@ -2300,9 +2310,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) @@ -2336,6 +2350,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) @@ -2417,6 +2433,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) @@ -2539,6 +2563,11 @@ static inline bool arm_is_secure_below_el3(CPUARMState *env) return false; } +static inline bool arm_is_el3_or_mon(CPUARMState *env) +{ + return false; +} + static inline ARMSecuritySpace arm_security_space(CPUARMState *env) { return ARMSS_NonSecure; @@ -2571,83 +2600,16 @@ 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); -/* Return true if the specified exception level is running in AArch64 state. */ -static inline bool arm_el_is_aa64(CPUARMState *env, int el) -{ - /* This isn't valid for EL0 (if we're in EL0, is_a64() is what you want, - * and if we're not in EL0 then the state of EL0 isn't well defined.) - */ - assert(el >= 1 && el <= 3); - bool aa64 = arm_feature(env, ARM_FEATURE_AARCH64); - - /* The highest exception level is always at the maximum supported - * register width, and then lower levels have a register width controlled - * by bits in the SCR or HCR registers. - */ - if (el == 3) { - return aa64; - } - - if (arm_feature(env, ARM_FEATURE_EL3) && - ((env->cp15.scr_el3 & SCR_NS) || !(env->cp15.scr_el3 & SCR_EEL2))) { - aa64 = aa64 && (env->cp15.scr_el3 & SCR_RW); - } - - if (el == 2) { - return aa64; - } - - if (arm_is_el2_enabled(env)) { - aa64 = aa64 && (env->cp15.hcr_el2 & HCR_RW); - } - - return aa64; -} - -/* Function for determining 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 * it doesn't exist at all) then there is no register banking, and all * accesses are to the non-secure version. */ -static inline bool access_secure_reg(CPUARMState *env) -{ - bool ret = (arm_feature(env, ARM_FEATURE_EL3) && - !arm_el_is_aa64(env, 3) && - !(env->cp15.scr_el3 & SCR_NS)); - - return ret; -} - -/* Macros for accessing a specified CP register bank */ -#define A32_BANKED_REG_GET(_env, _regname, _secure) \ - ((_secure) ? (_env)->cp15._regname##_s : (_env)->cp15._regname##_ns) - -#define A32_BANKED_REG_SET(_env, _regname, _secure, _val) \ - do { \ - if (_secure) { \ - (_env)->cp15._regname##_s = (_val); \ - } else { \ - (_env)->cp15._regname##_ns = (_val); \ - } \ - } while (0) - -/* Macros for automatically accessing a specific CP register bank depending on - * the current secure state of the system. These macros are not intended for - * supporting instruction translation reads/writes as these are dependent - * solely on the SCR.NS bit and not the mode. - */ -#define A32_BANKED_CURRENT_REG_GET(_env, _regname) \ - A32_BANKED_REG_GET((_env), _regname, \ - (arm_is_secure(_env) && !arm_el_is_aa64((_env), 3))) - -#define A32_BANKED_CURRENT_REG_SET(_env, _regname, _val) \ - A32_BANKED_REG_SET((_env), _regname, \ - (arm_is_secure(_env) && !arm_el_is_aa64((_env), 3)), \ - (_val)) +bool access_secure_reg(CPUARMState *env); -void arm_cpu_list(void); uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, uint32_t cur_el, bool secure); @@ -2669,39 +2631,6 @@ static inline bool arm_v7m_is_handler_mode(CPUARMState *env) return env->v7m.exception != 0; } -/* Return the current Exception Level (as per ARMv8; note that this differs - * from the ARMv7 Privilege Level). - */ -static inline int arm_current_el(CPUARMState *env) -{ - if (arm_feature(env, ARM_FEATURE_M)) { - return arm_v7m_is_handler_mode(env) || - !(env->v7m.control[env->v7m.secure] & 1); - } - - if (is_a64(env)) { - return extract32(env->pstate, 2, 2); - } - - switch (env->uncached_cpsr & 0x1f) { - case ARM_CPU_MODE_USR: - return 0; - case ARM_CPU_MODE_HYP: - return 2; - case ARM_CPU_MODE_MON: - return 3; - default: - if (arm_is_secure(env) && !arm_el_is_aa64(env, 3)) { - /* If EL3 is 32-bit then all secure privileged modes run in - * EL3 - */ - return 3; - } - - return 1; - } -} - /** * write_list_to_cpustate * @cpu: ARMCPU @@ -2744,14 +2673,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: @@ -2759,14 +2684,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: @@ -2792,29 +2722,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: @@ -2874,6 +2809,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, + 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 @@ -2881,14 +2818,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_S = 8 | ARM_MMU_IDX_A, - ARMMMUIdx_Stage2 = 9 | 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 = 10 | ARM_MMU_IDX_A, - ARMMMUIdx_Phys_NS = 11 | ARM_MMU_IDX_A, - ARMMMUIdx_Phys_Root = 12 | ARM_MMU_IDX_A, - ARMMMUIdx_Phys_Realm = 13 | ARM_MMU_IDX_A, + 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 @@ -2927,6 +2864,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), @@ -2992,60 +2931,15 @@ static inline bool arm_sctlr_b(CPUARMState *env) uint64_t arm_sctlr(CPUARMState *env, int el); -static inline bool arm_cpu_data_is_big_endian_a32(CPUARMState *env, - bool sctlr_b) -{ -#ifdef CONFIG_USER_ONLY - /* - * In system mode, BE32 is modelled in line with the - * architecture (as word-invariant big-endianness), where loads - * and stores are done little endian but from addresses which - * are adjusted by XORing with the appropriate constant. So the - * endianness to use for the raw data access is not affected by - * SCTLR.B. - * In user mode, however, we model BE32 as byte-invariant - * big-endianness (because user-only code cannot tell the - * difference), and so we need to use a data access endianness - * that depends on SCTLR.B. - */ - if (sctlr_b) { - return true; - } -#endif - /* In 32bit endianness is determined by looking at CPSR's E bit */ - return env->uncached_cpsr & CPSR_E; -} - -static inline bool arm_cpu_data_is_big_endian_a64(int el, uint64_t sctlr) -{ - return sctlr & (el ? SCTLR_EE : SCTLR_E0E); -} - -/* Return true if the processor is in big-endian mode. */ -static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) -{ - if (!is_a64(env)) { - return arm_cpu_data_is_big_endian_a32(env, arm_sctlr_b(env)); - } else { - int cur_el = arm_current_el(env); - uint64_t sctlr = arm_sctlr(env, cur_el); - return arm_cpu_data_is_big_endian_a64(cur_el, sctlr); - } -} - -#include "exec/cpu-all.h" - /* * We have more than 32-bits worth of state per TB, so we split the data * between tb->flags and tb->cs_base, which is otherwise unused for ARM. * We collect these two parts in CPUARMTBFlags where they are named * flags and flags2 respectively. * - * The flags that are shared between all execution modes, TBFLAG_ANY, - * are stored in flags. The flags that are specific to a given mode - * are stores in flags2. Since cs_base is sized on the configured - * address size, flags2 always has 64-bits for A64, and a minimum of - * 32-bits for A32 and M32. + * The flags that are shared between all execution modes, TBFLAG_ANY, are stored + * in flags. The flags that are specific to a given mode are stored in flags2. + * flags2 always has 64-bits, even though only 32-bits are used for A32 and M32. * * The bits for 32-bit A-profile and M-profile partially overlap: * @@ -3145,16 +3039,28 @@ FIELD(TBFLAG_A64, PSTATE_ZA, 23, 1) FIELD(TBFLAG_A64, SVL, 24, 4) /* Indicates that SME Streaming mode is active, and SMCR_ELx.FA64 is not. */ FIELD(TBFLAG_A64, SME_TRAP_NONSTREAMING, 28, 1) -FIELD(TBFLAG_A64, FGT_ERET, 29, 1) +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) +FIELD(TBFLAG_A64, AH, 37, 1) /* FPCR.AH */ +FIELD(TBFLAG_A64, NEP, 38, 1) /* FPCR.NEP */ /* - * 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) \ @@ -3163,24 +3069,11 @@ FIELD(TBFLAG_A64, NAA, 30, 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 @@ -3219,16 +3112,6 @@ static inline bool bswap_code(bool sctlr_b) #endif } -#ifdef CONFIG_USER_ONLY -static inline bool arm_cpu_bswap_data(CPUARMState *env) -{ - return TARGET_BIG_ENDIAN ^ arm_cpu_data_is_big_endian(env); -} -#endif - -void cpu_get_tb_cpu_state(CPUARMState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags); - enum { QEMU_PSCI_CONDUIT_DISABLED = 0, QEMU_PSCI_CONDUIT_SMC = 1, @@ -3326,995 +3209,4 @@ extern const uint64_t pred_esz_masks[5]; #define LOG2_TAG_GRANULE 4 #define TAG_GRANULE (1 << LOG2_TAG_GRANULE) -#ifdef CONFIG_USER_ONLY -#define TARGET_PAGE_DATA_SIZE (TARGET_PAGE_SIZE >> (LOG2_TAG_GRANULE + 1)) -#endif - -#ifdef TARGET_TAGGED_ADDRESSES -/** - * cpu_untagged_addr: - * @cs: CPU context - * @x: tagged address - * - * Remove any address tag from @x. This is explicitly related to the - * linux syscall TIF_TAGGED_ADDR setting, not TBI in general. - * - * There should be a better place to put this, but we need this in - * include/exec/cpu_ldst.h, and not some place linux-user specific. - */ -static inline target_ulong cpu_untagged_addr(CPUState *cs, target_ulong x) -{ - ARMCPU *cpu = ARM_CPU(cs); - if (cpu->env.tagged_addr_enable) { - /* - * TBI is enabled for userspace but not kernelspace addresses. - * Only clear the tag if bit 55 is clear. - */ - x &= sextract64(x, 0, 56); - } - return 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_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_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_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_rme(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RME) != 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_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_aa64mmfr2, ID_AA64MMFR1, TIDCP1) != 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_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_fgt(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, FGT) != 0; -} - -static inline bool isar_feature_aa64_ccidx(const ARMISARegisters *id) -{ - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, CCIDX) != 0; -} - -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); -} - -static inline bool isar_feature_any_half_evt(const ARMISARegisters *id) -{ - return isar_feature_aa64_half_evt(id) || isar_feature_aa32_half_evt(id); -} - -static inline bool isar_feature_any_evt(const ARMISARegisters *id) -{ - return isar_feature_aa64_evt(id) || isar_feature_aa32_evt(id); -} - -/* - * Forward to the above feature tests given an ARMCPU pointer. - */ -#define cpu_isar_feature(name, cpu) \ - ({ ARMCPU *cpu_ = (cpu); isar_feature_##name(&cpu_->isar); }) - #endif diff --git a/target/arm/cpu32-stubs.c b/target/arm/cpu32-stubs.c new file mode 100644 index 00000000000..81be44d8462 --- /dev/null +++ b/target/arm/cpu32-stubs.c @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "target/arm/cpu.h" +#include "target/arm/internals.h" +#include + +void arm_cpu_sme_finalize(ARMCPU *cpu, Error **errp) +{ + g_assert_not_reached(); +} + +void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) +{ + g_assert_not_reached(); +} + +void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) +{ + g_assert_not_reached(); +} + +void arm_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp) +{ + g_assert_not_reached(); +} diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index f3d87e001f9..200da1c489b 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -23,15 +23,17 @@ #include "cpu.h" #include "cpregs.h" #include "qemu/module.h" -#include "sysemu/kvm.h" -#include "sysemu/hvf.h" -#include "sysemu/qtest.h" -#include "sysemu/tcg.h" +#include "qemu/units.h" +#include "system/kvm.h" +#include "system/hvf.h" +#include "system/qtest.h" +#include "system/tcg.h" #include "kvm_arm.h" #include "hvf_arm.h" #include "qapi/visitor.h" #include "hw/qdev-properties.h" #include "internals.h" +#include "cpu-features.h" #include "cpregs.h" void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) @@ -65,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)); @@ -108,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; } @@ -432,7 +438,7 @@ 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); } @@ -457,7 +463,7 @@ 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); } @@ -514,25 +520,40 @@ void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) } if (cpu->prop_pauth) { - if (cpu->prop_pauth_impdef && cpu->prop_pauth_qarma3) { + if ((cpu->prop_pauth_impdef && cpu->prop_pauth_qarma3) || + (cpu->prop_pauth_impdef && cpu->prop_pauth_qarma5) || + (cpu->prop_pauth_qarma3 && cpu->prop_pauth_qarma5)) { error_setg(errp, - "cannot enable both pauth-impdef and pauth-qarma3"); + "cannot enable pauth-impdef, pauth-qarma3 and " + "pauth-qarma5 at the same time"); return; } - if (cpu->prop_pauth_impdef) { - isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, API, features); - isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, GPI, 1); + bool use_default = !cpu->prop_pauth_qarma5 && + !cpu->prop_pauth_qarma3 && + !cpu->prop_pauth_impdef; + + if (cpu->prop_pauth_qarma5 || + (use_default && + cpu->backcompat_pauth_default_use_qarma5)) { + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, APA, features); + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, GPA, 1); } else if (cpu->prop_pauth_qarma3) { isar2 = FIELD_DP64(isar2, ID_AA64ISAR2, APA3, features); isar2 = FIELD_DP64(isar2, ID_AA64ISAR2, GPA3, 1); + } else if (cpu->prop_pauth_impdef || + (use_default && + !cpu->backcompat_pauth_default_use_qarma5)) { + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, API, features); + isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, GPI, 1); } else { - isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, APA, features); - isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, GPA, 1); + g_assert_not_reached(); } - } else if (cpu->prop_pauth_impdef || cpu->prop_pauth_qarma3) { - error_setg(errp, "cannot enable pauth-impdef or " - "pauth-qarma3 without pauth"); + } else if (cpu->prop_pauth_impdef || + cpu->prop_pauth_qarma3 || + cpu->prop_pauth_qarma5) { + error_setg(errp, "cannot enable pauth-impdef, pauth-qarma3 or " + "pauth-qarma5 without pauth"); error_append_hint(errp, "Add pauth=on to the CPU property list.\n"); } } @@ -541,12 +562,14 @@ void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) cpu->isar.id_aa64isar2 = isar2; } -static Property arm_cpu_pauth_property = +static const Property arm_cpu_pauth_property = DEFINE_PROP_BOOL("pauth", ARMCPU, prop_pauth, true); -static Property arm_cpu_pauth_impdef_property = +static const Property arm_cpu_pauth_impdef_property = DEFINE_PROP_BOOL("pauth-impdef", ARMCPU, prop_pauth_impdef, false); -static Property arm_cpu_pauth_qarma3_property = +static const Property arm_cpu_pauth_qarma3_property = DEFINE_PROP_BOOL("pauth-qarma3", ARMCPU, prop_pauth_qarma3, false); +static Property arm_cpu_pauth_qarma5_property = + DEFINE_PROP_BOOL("pauth-qarma5", ARMCPU, prop_pauth_qarma5, false); void aarch64_add_pauth_properties(Object *obj) { @@ -567,6 +590,7 @@ void aarch64_add_pauth_properties(Object *obj) } else { qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_impdef_property); qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_qarma3_property); + qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_qarma5_property); } } @@ -598,6 +622,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); @@ -636,9 +661,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; @@ -655,6 +683,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); @@ -662,7 +691,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; @@ -693,9 +722,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; @@ -749,109 +781,12 @@ static const ARMCPUInfo aarch64_cpus[] = { #endif }; -static bool aarch64_cpu_get_aarch64(Object *obj, Error **errp) -{ - ARMCPU *cpu = ARM_CPU(obj); - - return arm_feature(&cpu->env, ARM_FEATURE_AARCH64); -} - -static void aarch64_cpu_set_aarch64(Object *obj, bool value, Error **errp) -{ - ARMCPU *cpu = ARM_CPU(obj); - - /* At this time, this property is only allowed if KVM is enabled. This - * restriction allows us to avoid fixing up functionality that assumes a - * uniform execution state like do_interrupt. - */ - if (value == false) { - if (!kvm_enabled() || !kvm_arm_aarch32_supported()) { - error_setg(errp, "'aarch64' feature cannot be disabled " - "unless KVM is enabled and 32-bit EL1 " - "is supported"); - return; - } - unset_feature(&cpu->env, ARM_FEATURE_AARCH64); - } else { - set_feature(&cpu->env, ARM_FEATURE_AARCH64); - } -} - -static void aarch64_cpu_finalizefn(Object *obj) -{ -} - -static gchar *aarch64_gdb_arch_name(CPUState *cs) -{ - return g_strdup("aarch64"); -} - -static void aarch64_cpu_class_init(ObjectClass *oc, void *data) -{ - CPUClass *cc = CPU_CLASS(oc); - - 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; - - object_class_property_add_bool(oc, "aarch64", aarch64_cpu_get_aarch64, - aarch64_cpu_set_aarch64); - object_class_property_set_description(oc, "aarch64", - "Set on/off to enable/disable aarch64 " - "execution state "); -} - -static void aarch64_cpu_instance_init(Object *obj) -{ - ARMCPUClass *acc = ARM_CPU_GET_CLASS(obj); - - acc->info->initfn(obj); - arm_cpu_post_init(obj); -} - -static void cpu_register_class_init(ObjectClass *oc, void *data) -{ - ARMCPUClass *acc = ARM_CPU_CLASS(oc); - - acc->info = data; -} - -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, - }; - - type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name); - type_register(&type_info); - g_free((void *)type_info.name); -} - -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, -}; - static void aarch64_cpu_register_types(void) { size_t i; - type_register_static(&aarch64_cpu_type_info); - for (i = 0; i < ARRAY_SIZE(aarch64_cpus); ++i) { - aarch64_cpu_register(&aarch64_cpus[i]); + arm_cpu_register(&aarch64_cpus[i]); } } diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index abe72e35ae6..69fb1d0d9ff 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -9,10 +9,13 @@ #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" +#include "exec/watchpoint.h" +#include "system/tcg.h" + +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" #ifdef CONFIG_TCG /* Return the Exception Level targeted by debug exceptions. */ @@ -377,7 +380,7 @@ bool arm_debug_check_breakpoint(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; - target_ulong pc; + vaddr pc; int n; /* @@ -843,6 +846,16 @@ static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri, 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 @@ -864,12 +877,13 @@ static CPAccessResult access_tdcc(CPUARMState *env, const ARMCPRegInfo *ri, (env->cp15.mdcr_el3 & MDCR_TDCC); if (el < 1 && mdscr_el1_tdcc) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } 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)) { + if (!arm_is_el3_or_mon(env) && + ((env->cp15.mdcr_el3 & MDCR_TDA) || mdcr_el3_tdcc)) { return CP_ACCESS_TRAP_EL3; } return CP_ACCESS_OK; @@ -936,19 +950,20 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { */ { .name = "DBGDRAR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL0_R, .accessfn = access_tdra, - .type = ARM_CP_CONST, .resetvalue = 0 }, + .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, .resetvalue = 0 }, + .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 }, /* @@ -1024,15 +1039,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { { .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 }, + .type = ARM_CP_CONST, .resetvalue = 0 }, /* * Dummy MDCCINT_EL1, since we don't implement the Debug Communications * Channel but Linux may try to access this register. The 32-bit @@ -1041,7 +1048,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { { .name = "MDCCINT_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0, .access = PL1_RW, .accessfn = access_tdcc, - .type = ARM_CP_NOP }, + .type = ARM_CP_CONST, .resetvalue = 0 }, /* * Dummy DBGCLAIM registers. * "The architecture does not define any functionality for the CLAIM tag bits.", @@ -1061,12 +1068,27 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { .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_CONST | ARM_CP_EL3_NO_EL2_KEEP, + .resetvalue = 0 }, +}; + 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 }, + .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, .resetvalue = 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, @@ -1204,6 +1226,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); diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index 8fc8351df78..ce4497ad7c3 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -21,15 +21,17 @@ #include "cpu.h" #include "exec/gdbstub.h" #include "gdbstub/helpers.h" -#include "sysemu/tcg.h" +#include "gdbstub/commands.h" +#include "system/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 @@ -42,25 +44,17 @@ int arm_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; +#ifdef TARGET_AARCH64 + if (arm_gdbstub_is_aarch64(cpu)) { + return aarch64_cpu_gdb_read_register(cs, mem_buf, n); + } +#endif + if (n < 16) { /* 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)); @@ -78,6 +72,12 @@ int arm_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) CPUARMState *env = &cpu->env; uint32_t tmp; +#ifdef TARGET_AARCH64 + if (arm_gdbstub_is_aarch64(cpu)) { + return aarch64_cpu_gdb_write_register(cs, mem_buf, n); + } +#endif + tmp = ldl_p(mem_buf); /* @@ -100,21 +100,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)) { /* @@ -133,9 +119,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. */ @@ -157,9 +144,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) { @@ -183,8 +171,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]); @@ -194,8 +185,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); @@ -207,8 +201,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); @@ -217,8 +214,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); @@ -237,13 +237,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)) { @@ -255,39 +256,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) { @@ -296,32 +295,32 @@ 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++); } } } } } -static 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}; - - 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; + RegisterSysregFeatureParam param = {cs}; + gsize num_regs = g_hash_table_size(cpu->cp_regs); + + 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; } #ifdef CONFIG_TCG @@ -396,8 +395,11 @@ static int m_sysreg_get(CPUARMState *env, GByteArray *buf, return gdb_get_reg32(buf, *ptr); } -static int arm_gdb_get_m_systemreg(CPUARMState *env, GByteArray *buf, int reg) +static int arm_gdb_get_m_systemreg(CPUState *cs, GByteArray *buf, int reg) { + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + /* * Here, we emulate MRS instruction, where CONTROL has a mix of * banked and non-banked bits. @@ -408,36 +410,34 @@ static int arm_gdb_get_m_systemreg(CPUARMState *env, GByteArray *buf, int reg) return m_sysreg_get(env, buf, reg, env->v7m.secure); } -static int arm_gdb_set_m_systemreg(CPUARMState *env, uint8_t *buf, int reg) +static int arm_gdb_set_m_systemreg(CPUState *cs, uint8_t *buf, int reg) { return 0; /* TODO */ } -static int arm_gen_dynamic_m_systemreg_xml(CPUState *cs, int orig_base_reg) +static GDBFeature *arm_gen_dynamic_m_systemreg_feature(CPUState *cs, + int base_reg) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; - GString *s = g_string_new(NULL); - int base_reg = orig_base_reg; + GDBFeatureBuilder builder; + int reg = 0; int i; - g_string_printf(s, ""); - g_string_append_printf(s, ""); - g_string_append_printf(s, "\n"); + 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)) { - g_string_append_printf(s, - "\n", - m_sysreg_def[i].name, base_reg++); + gdb_feature_builder_append_reg(&builder, m_sysreg_def[i].name, 32, + reg++, "int", NULL); } } - g_string_append_printf(s, ""); - cpu->dyn_m_systemreg_xml.desc = g_string_free(s, false); - cpu->dyn_m_systemreg_xml.num = base_reg - orig_base_reg; + gdb_feature_builder_end(&builder); - return cpu->dyn_m_systemreg_xml.num; + return &cpu->dyn_m_systemreg_feature.desc; } #ifndef CONFIG_USER_ONLY @@ -445,61 +445,75 @@ static int arm_gen_dynamic_m_systemreg_xml(CPUState *cs, int orig_base_reg) * 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(CPUARMState *env, GByteArray *buf, int reg) +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(CPUARMState *env, uint8_t *buf, int reg) +static int arm_gdb_set_m_secextreg(CPUState *cs, uint8_t *buf, int reg) { return 0; /* TODO */ } -static int arm_gen_dynamic_m_secextreg_xml(CPUState *cs, int orig_base_reg) +static GDBFeature *arm_gen_dynamic_m_secextreg_feature(CPUState *cs, + int base_reg) { ARMCPU *cpu = ARM_CPU(cs); - GString *s = g_string_new(NULL); - int base_reg = orig_base_reg; + GDBFeatureBuilder builder; + char *name; + int reg = 0; int i; - g_string_printf(s, ""); - g_string_append_printf(s, ""); - g_string_append_printf(s, "\n"); + 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++) { - g_string_append_printf(s, - "\n", - m_sysreg_def[i].name, base_reg++); - g_string_append_printf(s, - "\n", - m_sysreg_def[i].name, base_reg++); + 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); } - g_string_append_printf(s, ""); - cpu->dyn_m_secextreg_xml.desc = g_string_free(s, false); - cpu->dyn_m_secextreg_xml.num = base_reg - orig_base_reg; + gdb_feature_builder_end(&builder); - return cpu->dyn_m_secextreg_xml.num; + return &cpu->dyn_m_secextreg_feature.desc; } #endif #endif /* CONFIG_TCG */ -const char *arm_gdb_get_dynamic_xml(CPUState *cs, const char *xmlname) +void arm_cpu_register_gdb_commands(ARMCPU *cpu) { - ARMCPU *cpu = ARM_CPU(cs); + 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 + } - 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; - } else if (strcmp(xmlname, "arm-m-system.xml") == 0) { - return cpu->dyn_m_systemreg_xml.desc; -#ifndef CONFIG_USER_ONLY - } else if (strcmp(xmlname, "arm-m-secext.xml") == 0) { - return cpu->dyn_m_secextreg_xml.desc; -#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) @@ -514,14 +528,14 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) */ #ifdef TARGET_AARCH64 if (isar_feature_aa64_sve(&cpu->isar)) { - int nreg = arm_gen_dynamic_svereg_xml(cs, cs->gdb_num_regs); + 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, nreg, - "sve-registers.xml", 0); + aarch64_gdb_set_sve_reg, feature, 0); } else { gdb_register_coprocessor(cs, aarch64_gdb_get_fpu_reg, aarch64_gdb_set_fpu_reg, - 34, "aarch64-fpu.xml", 0); + gdb_find_static_feature("aarch64-fpu.xml"), + 0); } /* * Note that we report pauth information via the feature name @@ -532,19 +546,32 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) if (isar_feature_aa64_pauth(&cpu->isar)) { gdb_register_coprocessor(cs, aarch64_gdb_get_pauth_reg, aarch64_gdb_set_pauth_reg, - 4, "aarch64-pauth.xml", 0); + 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)) { /* @@ -552,29 +579,29 @@ 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) && 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_xml(cs, cs->gdb_num_regs), - "arm-m-system.xml", 0); + 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_xml(cs, cs->gdb_num_regs), - "arm-m-secext.xml", 0); + arm_gen_dynamic_m_secextreg_feature(cs, cs->gdb_num_regs), 0); } #endif } diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index d7b79a6589b..64ee9b3b567 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -21,6 +21,16 @@ #include "cpu.h" #include "internals.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 +#ifdef CONFIG_TCG +#include "accel/tcg/cpu-mmu-index.h" +#include "exec/target_page.h" +#endif int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { @@ -72,8 +82,11 @@ int aarch64_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 0; } -int aarch64_gdb_get_fpu_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 +105,11 @@ int aarch64_gdb_get_fpu_reg(CPUARMState *env, GByteArray *buf, int reg) } } -int aarch64_gdb_set_fpu_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 +132,10 @@ int aarch64_gdb_set_fpu_reg(CPUARMState *env, uint8_t *buf, int reg) } } -int aarch64_gdb_get_sve_reg(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 +181,10 @@ int aarch64_gdb_get_sve_reg(CPUARMState *env, GByteArray *buf, int reg) return 0; } -int aarch64_gdb_set_sve_reg(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) { @@ -210,8 +228,11 @@ int aarch64_gdb_set_sve_reg(CPUARMState *env, uint8_t *buf, int reg) return 0; } -int aarch64_gdb_get_pauth_reg(CPUARMState *env, GByteArray *buf, int reg) +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 */ @@ -241,13 +262,13 @@ int aarch64_gdb_get_pauth_reg(CPUARMState *env, GByteArray *buf, int reg) } } -int aarch64_gdb_set_pauth_reg(CPUARMState *env, uint8_t *buf, int reg) +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(GString *s, int reg_width, +static void output_vector_union_type(GDBFeatureBuilder *builder, int reg_width, const char *name) { struct TypeSize { @@ -282,10 +303,10 @@ static void output_vector_union_type(GString *s, int reg_width, /* First define types and totals in a whole VL */ for (i = 0; i < ARRAY_SIZE(vec_lanes); i++) { - g_string_append_printf(s, - "", - name, vec_lanes[i].sz, vec_lanes[i].suffix, - vec_lanes[i].gdb_type, reg_width / vec_lanes[i].size); + 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); } /* @@ -296,86 +317,308 @@ static void output_vector_union_type(GString *s, int reg_width, for (i = 0; i < ARRAY_SIZE(suf); i++) { int bits = 8 << i; - g_string_append_printf(s, "", name, suf[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) { - g_string_append_printf(s, "", - vec_lanes[j].suffix, name, - vec_lanes[j].sz, vec_lanes[j].suffix); + gdb_feature_builder_append_tag( + builder, "", + vec_lanes[j].suffix, name, + vec_lanes[j].sz, vec_lanes[j].suffix); } } - g_string_append(s, ""); + gdb_feature_builder_append_tag(builder, ""); } /* And now the final union of unions */ - g_string_append_printf(s, "", name); + gdb_feature_builder_append_tag(builder, "", name); for (i = ARRAY_SIZE(suf) - 1; i >= 0; i--) { - g_string_append_printf(s, "", - suf[i], name, suf[i]); + gdb_feature_builder_append_tag(builder, + "", + suf[i], name, suf[i]); } - g_string_append(s, ""); + gdb_feature_builder_append_tag(builder, ""); } -int arm_gen_dynamic_svereg_xml(CPUState *cs, int orig_base_reg) +GDBFeature *arm_gen_dynamic_svereg_feature(CPUState *cs, int base_reg) { ARMCPU *cpu = ARM_CPU(cs); - GString *s = g_string_new(NULL); - DynamicGDBXMLInfo *info = &cpu->dyn_svereg_xml; int reg_width = cpu->sve_max_vq * 128; int pred_width = cpu->sve_max_vq * 16; - int base_reg = orig_base_reg; + GDBFeatureBuilder builder; + char *name; + int reg = 0; int i; - g_string_printf(s, ""); - g_string_append_printf(s, ""); - g_string_append_printf(s, ""); + 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(s, reg_width, "svev"); + output_vector_union_type(&builder, reg_width, "svev"); /* Create the predicate vector type. */ - g_string_append_printf(s, - "", - pred_width / 8); + gdb_feature_builder_append_tag( + &builder, "", + pred_width / 8); /* Define the vector registers. */ for (i = 0; i < 32; i++) { - g_string_append_printf(s, - "", - i, reg_width, base_reg++); + name = g_strdup_printf("z%d", i); + gdb_feature_builder_append_reg(&builder, name, reg_width, reg++, + "svev", NULL); } /* fpscr & status registers */ - g_string_append_printf(s, "", base_reg++); - g_string_append_printf(s, "", base_reg++); + 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++) { - g_string_append_printf(s, - "", - i, pred_width, base_reg++); + name = g_strdup_printf("p%d", i); + gdb_feature_builder_append_reg(&builder, name, pred_width, reg++, + "svep", NULL); } - g_string_append_printf(s, - "", - pred_width, base_reg++); + gdb_feature_builder_append_reg(&builder, "ffr", pred_width, reg++, + "svep", "vector"); /* Define the vector length pseudo-register. */ - g_string_append_printf(s, - "", - base_reg++); + 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); - g_string_append_printf(s, ""); + tcf0 = extract64(env->cp15.sctlr_el[1], 38, 2); - info->desc = g_string_free(s, false); - info->num = base_reg - orig_base_reg; - return info->num; + 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 00000000000..d49c63cbf87 --- /dev/null +++ b/target/arm/gtimer.h @@ -0,0 +1,23 @@ +/* + * 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, /* CNTP_* ; EL1 physical timer */ + GTIMER_VIRT = 1, /* CNTV_* ; EL1 virtual timer */ + GTIMER_HYP = 2, /* CNTHP_* ; EL2 physical timer */ + GTIMER_SEC = 3, /* CNTPS_* ; EL3 physical timer */ + GTIMER_HYPVIRT = 4, /* CNTHV_* ; EL2 virtual timer ; only if FEAT_VHE */ + GTIMER_S_EL2_PHYS = 5, /* CNTHPS_* ; only if FEAT_SEL2 */ + GTIMER_S_EL2_VIRT = 6, /* CNTHVS_* ; only if FEAT_SEL2 */ +#define NUM_GTIMERS 7 +}; + +#endif diff --git a/target/arm/helper.c b/target/arm/helper.c index 3b22596eabf..889d3088079 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11,24 +11,32 @@ #include "trace.h" #include "cpu.h" #include "internals.h" -#include "exec/helper-proto.h" +#include "cpu-features.h" +#include "exec/page-protection.h" +#include "exec/mmap-lock.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 "exec/cputlb.h" +#include "exec/translation-block.h" #include "hw/irq.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/cpu-timers.h" +#include "exec/icount.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "qapi/error.h" #include "qemu/guest-random.h" #ifdef CONFIG_TCG +#include "accel/tcg/probe.h" +#include "accel/tcg/getpc.h" #include "semihosting/common-semi.h" #endif #include "cpregs.h" +#include "target/arm/gtimer.h" + +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ @@ -217,7 +225,7 @@ static void count_cpreg(gpointer key, gpointer opaque) } } -static gint cpreg_key_compare(gconstpointer a, gconstpointer b) +static gint cpreg_key_compare(gconstpointer a, gconstpointer b, gpointer d) { uint64_t aidx = cpreg_to_kvm_id((uintptr_t)a); uint64_t bidx = cpreg_to_kvm_id((uintptr_t)b); @@ -241,7 +249,7 @@ void init_cpreg_list(ARMCPU *cpu) int arraylen; keys = g_hash_table_get_keys(cpu->cp_regs); - keys = g_list_sort(keys, cpreg_key_compare); + keys = g_list_sort_with_data(keys, cpreg_key_compare, NULL); cpu->cpreg_array_len = 0; @@ -262,6 +270,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. */ @@ -271,7 +291,7 @@ static CPAccessResult access_el3_aa32ns(CPUARMState *env, { if (!is_a64(env) && arm_current_el(env) == 3 && arm_is_secure_below_el3(env)) { - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } return CP_ACCESS_OK; } @@ -296,7 +316,7 @@ static CPAccessResult access_trap_aa32s_el1(CPUARMState *env, return CP_ACCESS_TRAP_EL3; } /* This will be EL1 NS and EL2 NS, which just UNDEF */ - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } /* @@ -351,40 +371,6 @@ static CPAccessResult access_tacr(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } -/* Check for traps from EL1 due to HCR_EL2.TTLB. */ -static CPAccessResult access_ttlb(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TTLB)) { - return CP_ACCESS_TRAP_EL2; - } - return CP_ACCESS_OK; -} - -/* 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); @@ -424,12 +410,15 @@ static void contextidr_write(CPUARMState *env, const ARMCPRegInfo *ri, raw_write(env, ri, value); } -static int alle1_tlbmask(CPUARMState *env) +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 | @@ -438,174 +427,6 @@ static int alle1_tlbmask(CPUARMState *env) ARMMMUIdxBit_Stage2_S); } - -/* IS variants of TLB operations must affect all cores */ -static void tlbiall_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_all_cpus_synced(cs); -} - -static void tlbiasid_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_all_cpus_synced(cs); -} - -static void tlbimva_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK); -} - -static void tlbimvaa_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK); -} - -/* - * Non-IS variants of TLB operations are upgraded to - * IS versions if we are at EL1 and HCR_EL2.FB is effectively set to - * force broadcast of these operations. - */ -static bool tlb_force_broadcast(CPUARMState *env) -{ - return arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_FB); -} - -static void tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Invalidate all (TLBIALL) */ - CPUState *cs = env_cpu(env); - - if (tlb_force_broadcast(env)) { - tlb_flush_all_cpus_synced(cs); - } else { - tlb_flush(cs); - } -} - -static void tlbimva_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Invalidate single TLB entry by MVA and ASID (TLBIMVA) */ - CPUState *cs = env_cpu(env); - - value &= TARGET_PAGE_MASK; - if (tlb_force_broadcast(env)) { - tlb_flush_page_all_cpus_synced(cs, value); - } else { - tlb_flush_page(cs, value); - } -} - -static void tlbiasid_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Invalidate by ASID (TLBIASID) */ - CPUState *cs = env_cpu(env); - - if (tlb_force_broadcast(env)) { - tlb_flush_all_cpus_synced(cs); - } else { - tlb_flush(cs); - } -} - -static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Invalidate single entry by MVA, all ASIDs (TLBIMVAA) */ - CPUState *cs = env_cpu(env); - - value &= TARGET_PAGE_MASK; - if (tlb_force_broadcast(env)) { - tlb_flush_page_all_cpus_synced(cs, value); - } else { - tlb_flush_page(cs, value); - } -} - -static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_by_mmuidx(cs, alle1_tlbmask(env)); -} - -static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_by_mmuidx_all_cpus_synced(cs, alle1_tlbmask(env)); -} - - -static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E2); -} - -static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E2); -} - -static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); - - tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E2); -} - -static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); - - tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - ARMMMUIdxBit_E2); -} - -static void tlbiipas2_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - uint64_t pageaddr = (value & MAKE_64BIT_MASK(0, 28)) << 12; - - tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_Stage2); -} - -static void tlbiipas2is_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - uint64_t pageaddr = (value & MAKE_64BIT_MASK(0, 28)) << 12; - - tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, ARMMMUIdxBit_Stage2); -} - static const ARMCPRegInfo cp_reginfo[] = { /* * Define the secure and non-secure FCSE identifier CP registers @@ -634,6 +455,7 @@ static const ARMCPRegInfo cp_reginfo[] = { .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, }, @@ -714,22 +536,6 @@ static const ARMCPRegInfo not_v7_cp_reginfo[] = { */ { .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 - * the unified TLB ops but also the dside/iside/inner-shareable variants. - */ - { .name = "TLBIALL", .cp = 15, .crn = 8, .crm = CP_ANY, - .opc1 = CP_ANY, .opc2 = 0, .access = PL1_W, .writefn = tlbiall_write, - .type = ARM_CP_NO_RAW }, - { .name = "TLBIMVA", .cp = 15, .crn = 8, .crm = CP_ANY, - .opc1 = CP_ANY, .opc2 = 1, .access = PL1_W, .writefn = tlbimva_write, - .type = ARM_CP_NO_RAW }, - { .name = "TLBIASID", .cp = 15, .crn = 8, .crm = CP_ANY, - .opc1 = CP_ANY, .opc2 = 2, .access = PL1_W, .writefn = tlbiasid_write, - .type = ARM_CP_NO_RAW }, - { .name = "TLBIMVAA", .cp = 15, .crn = 8, .crm = CP_ANY, - .opc1 = CP_ANY, .opc2 = 3, .access = PL1_W, .writefn = tlbimvaa_write, - .type = ARM_CP_NO_RAW }, { .name = "PRRR", .cp = 15, .crn = 10, .crm = 2, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_NOP }, { .name = "NMRR", .cp = 15, .crn = 10, .crm = 2, @@ -870,6 +676,7 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { { .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 }, }; @@ -933,16 +740,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 @@ -1077,7 +887,7 @@ static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); if (el == 0 && !(env->cp15.c9_pmuserenr & 1)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } if (el < 2 && (mdcr_el2 & MDCR_TPM)) { return CP_ACCESS_TRAP_EL2; @@ -1168,13 +978,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; @@ -1283,7 +1101,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; } } @@ -1474,6 +1292,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) { @@ -1880,6 +1714,9 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) 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)) { @@ -1975,16 +1812,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; @@ -2054,7 +1904,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), .accessfn = pmreg_access, .fgt = FGT_PMCNTEN, - .writefn = pmcntenclr_write, + .writefn = pmcntenclr_write, .raw_writefn = raw_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, @@ -2062,7 +1912,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .fgt = FGT_PMCNTEN, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), - .writefn = pmcntenclr_write }, + .writefn = pmcntenclr_write, .raw_writefn = raw_write }, { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3, .access = PL0_RW, .type = ARM_CP_IO, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), @@ -2179,16 +2029,16 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { { .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, + .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), - .writefn = pmintenclr_write, }, + .writefn = pmintenclr_write, .raw_writefn = raw_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, + .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), - .writefn = pmintenclr_write }, + .writefn = pmintenclr_write, .raw_writefn = raw_write }, { .name = "CCSIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0, .access = PL1_R, @@ -2221,11 +2071,13 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .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 @@ -2235,6 +2087,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .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, @@ -2266,55 +2119,6 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .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, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbiall_write }, - { .name = "ITLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbimva_write }, - { .name = "ITLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 2, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbiasid_write }, - /* 32 bit DTLB invalidates */ - { .name = "DTLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 0, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbiall_write }, - { .name = "DTLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbimva_write }, - { .name = "DTLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 2, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbiasid_write }, - /* 32 bit TLB invalidates */ - { .name = "TLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbiall_write }, - { .name = "TLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbimva_write }, - { .name = "TLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbiasid_write }, - { .name = "TLBIMVAA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbimvaa_write }, -}; - -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_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_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_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_ttlbis, - .writefn = tlbimvaa_is_write }, }; static const ARMCPRegInfo pmovsset_cp_reginfo[] = { @@ -2361,7 +2165,7 @@ static CPAccessResult teehbr_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { if (arm_current_el(env) == 0 && (env->teecr & 1)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } return teecr_access(env, ri, isread); } @@ -2412,6 +2216,13 @@ 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, @@ -2434,14 +2245,14 @@ static CPAccessResult gt_cntfrq_access(CPUARMState *env, const ARMCPRegInfo *ri, cntkctl = env->cp15.c14_cntkctl; } if (!extract32(cntkctl, 0, 2)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } break; case 1: if (!isread && ri->state == ARM_CP_STATE_AA32 && arm_is_secure_below_el3(env)) { /* Accesses from 32-bit Secure EL1 UNDEF (*not* trap to EL3!) */ - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } break; case 2: @@ -2450,7 +2261,7 @@ static CPAccessResult gt_cntfrq_access(CPUARMState *env, const ARMCPRegInfo *ri, } if (!isread && el < arm_highest_el(env)) { - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } return CP_ACCESS_OK; @@ -2473,24 +2284,9 @@ static CPAccessResult gt_counter_access(CPUARMState *env, int timeridx, /* CNT[PV]CT: not visible from PL0 if EL0[PV]CTEN is zero */ if (!extract32(env->cp15.c14_cntkctl, timeridx, 1)) { - return CP_ACCESS_TRAP; - } - - /* If HCR_EL2. == '10': check CNTHCTL_EL2.EL1PCTEN. */ - if (hcr & HCR_E2H) { - if (timeridx == GTIMER_PHYS && - !extract32(env->cp15.cnthctl_el2, 10, 1)) { - return CP_ACCESS_TRAP_EL2; - } - } else { - /* If HCR_EL2. == 0: check CNTHCTL_EL2.EL1PCEN. */ - if (has_el2 && timeridx == GTIMER_PHYS && - !extract32(env->cp15.cnthctl_el2, 1, 1)) { - return CP_ACCESS_TRAP_EL2; - } + return CP_ACCESS_TRAP_EL1; } - break; - + /* fall through */ case 1: /* Check CNTHCTL_EL2.EL1PCTEN, which changes location based on E2H. */ if (has_el2 && timeridx == GTIMER_PHYS && @@ -2499,6 +2295,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; @@ -2524,7 +2325,7 @@ static CPAccessResult gt_timer_access(CPUARMState *env, int timeridx, * EL0 if EL0[PV]TEN is zero. */ if (!extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } /* fall through */ @@ -2542,6 +2343,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; @@ -2585,7 +2391,10 @@ static CPAccessResult gt_stimer_access(CPUARMState *env, switch (arm_current_el(env)) { case 1: if (!arm_is_secure(env)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_UNDEFINED; + } + if (arm_is_el2_enabled(env)) { + return CP_ACCESS_UNDEFINED; } if (!(env->cp15.scr_el3 & SCR_ST)) { return CP_ACCESS_TRAP_EL3; @@ -2593,7 +2402,7 @@ static CPAccessResult gt_stimer_access(CPUARMState *env, return CP_ACCESS_OK; case 0: case 2: - return CP_ACCESS_TRAP; + return CP_ACCESS_UNDEFINED; case 3: return CP_ACCESS_OK; default: @@ -2601,7 +2410,46 @@ static CPAccessResult gt_stimer_access(CPUARMState *env, } } -static uint64_t gt_get_countervalue(CPUARMState *env) +static CPAccessResult gt_sel2timer_access(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + /* + * The AArch64 register view of the secure EL2 timers are mostly + * accessible from EL3 and EL2 although can also be trapped to EL2 + * from EL1 depending on nested virt config. + */ + switch (arm_current_el(env)) { + case 0: /* UNDEFINED */ + return CP_ACCESS_UNDEFINED; + case 1: + if (!arm_is_secure(env)) { + /* UNDEFINED */ + return CP_ACCESS_UNDEFINED; + } else if (arm_hcr_el2_eff(env) & HCR_NV) { + /* Aarch64.SystemAccessTrap(EL2, 0x18) */ + return CP_ACCESS_TRAP_EL2; + } + /* UNDEFINED */ + return CP_ACCESS_UNDEFINED; + case 2: + if (!arm_is_secure(env)) { + /* UNDEFINED */ + return CP_ACCESS_UNDEFINED; + } + return CP_ACCESS_OK; + case 3: + if (env->cp15.scr_el3 & SCR_EEL2) { + return CP_ACCESS_OK; + } else { + return CP_ACCESS_UNDEFINED; + } + default: + g_assert_not_reached(); + } +} + +uint64_t gt_get_countervalue(CPUARMState *env) { ARMCPU *cpu = env_archcpu(env); @@ -2621,8 +2469,8 @@ static void gt_update_irq(ARMCPU *cpu, int timeridx) * It is RES0 in Secure and NonSecure state. */ if ((ss == ARMSS_Root || ss == ARMSS_Realm) && - ((timeridx == GTIMER_VIRT && (cnthctl & CNTHCTL_CNTVMASK)) || - (timeridx == GTIMER_PHYS && (cnthctl & CNTHCTL_CNTPMASK)))) { + ((timeridx == GTIMER_VIRT && (cnthctl & R_CNTHCTL_CNTVMASK_MASK)) || + (timeridx == GTIMER_PHYS && (cnthctl & R_CNTHCTL_CNTPMASK_MASK)))) { irqstate = 0; } @@ -2641,30 +2489,133 @@ void gt_rme_post_el_change(ARMCPU *cpu, void *ignored) gt_update_irq(cpu, GTIMER_PHYS); } -static void gt_recalc_timer(ARMCPU *cpu, int timeridx) +static uint64_t gt_phys_raw_cnt_offset(CPUARMState *env) { - ARMGenericTimer *gt = &cpu->env.cp15.c14_timer[timeridx]; + 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; +} - if (gt->ctl & 1) { - /* - * 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; - 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; +static uint64_t gt_indirect_access_timer_offset(CPUARMState *env, int timeridx) +{ + /* + * Return the timer offset to use for indirect accesses to the timer. + * This is the Offset value as defined in D12.2.4.1 "Operation of the + * CompareValue views of the timers". + * + * The condition here is not always the same as the condition for + * whether to apply an offset register when doing a direct read of + * the counter sysreg; those conditions are described in the + * access pseudocode for each counter register. + */ + switch (timeridx) { + case GTIMER_PHYS: + return gt_phys_raw_cnt_offset(env); + case GTIMER_VIRT: + return env->cp15.cntvoff_el2; + case GTIMER_HYP: + case GTIMER_SEC: + case GTIMER_HYPVIRT: + case GTIMER_S_EL2_PHYS: + case GTIMER_S_EL2_VIRT: + return 0; + default: + g_assert_not_reached(); + } +} - gt->ctl = deposit32(gt->ctl, 2, 1, istatus); +uint64_t gt_direct_access_timer_offset(CPUARMState *env, int timeridx) +{ + /* + * Return the timer offset to use for direct accesses to the + * counter registers CNTPCT and CNTVCT, and for direct accesses + * to the CNT*_TVAL registers. + * + * This isn't exactly the same as the indirect-access offset, + * because here we also care about what EL the register access + * is being made from. + * + * This corresponds to the access pseudocode for the registers. + */ + uint64_t hcr; - if (istatus) { - /* Next transition is when count rolls back over to zero */ - nexttick = UINT64_MAX; - } else { - /* Next transition is when we hit cval */ - nexttick = gt->cval + offset; + switch (timeridx) { + case GTIMER_PHYS: + if (arm_current_el(env) >= 2) { + return 0; + } + return gt_phys_raw_cnt_offset(env); + case GTIMER_VIRT: + switch (arm_current_el(env)) { + case 2: + hcr = arm_hcr_el2_eff(env); + if (hcr & HCR_E2H) { + return 0; + } + break; + case 0: + hcr = arm_hcr_el2_eff(env); + if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { + return 0; + } + break; + } + return env->cp15.cntvoff_el2; + case GTIMER_HYP: + case GTIMER_SEC: + case GTIMER_HYPVIRT: + case GTIMER_S_EL2_PHYS: + case GTIMER_S_EL2_VIRT: + return 0; + default: + g_assert_not_reached(); + } +} + +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 + * reset timer to when ISTATUS next has to change + */ + uint64_t offset = gt_indirect_access_timer_offset(&cpu->env, timeridx); + 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; + + gt->ctl = deposit32(gt->ctl, 2, 1, istatus); + + if (istatus) { + /* + * 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 (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 @@ -2697,34 +2648,14 @@ 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); -} - -static uint64_t gt_virt_cnt_offset(CPUARMState *env) -{ - uint64_t hcr; - - switch (arm_current_el(env)) { - case 2: - hcr = arm_hcr_el2_eff(env); - if (hcr & HCR_E2H) { - return 0; - } - break; - case 0: - hcr = arm_hcr_el2_eff(env); - if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { - return 0; - } - break; - } - - return env->cp15.cntvoff_el2; + uint64_t offset = gt_direct_access_timer_offset(env, GTIMER_PHYS); + return gt_get_countervalue(env) - offset; } static uint64_t gt_virt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) { - return gt_get_countervalue(env) - gt_virt_cnt_offset(env); + uint64_t offset = gt_direct_access_timer_offset(env, GTIMER_VIRT); + return gt_get_countervalue(env) - offset; } static void gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -2736,41 +2667,38 @@ static void gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, gt_recalc_timer(env_archcpu(env), timeridx); } -static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, - int timeridx) +static uint64_t do_tval_read(CPUARMState *env, int timeridx, uint64_t offset) { - uint64_t offset = 0; - - switch (timeridx) { - case GTIMER_VIRT: - case GTIMER_HYPVIRT: - offset = gt_virt_cnt_offset(env); - break; - } - return (uint32_t)(env->cp15.c14_timer[timeridx].cval - (gt_get_countervalue(env) - offset)); } -static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, - int timeridx, - uint64_t value) +static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, + int timeridx) { - uint64_t offset = 0; + uint64_t offset = gt_direct_access_timer_offset(env, timeridx); - switch (timeridx) { - case GTIMER_VIRT: - case GTIMER_HYPVIRT: - offset = gt_virt_cnt_offset(env); - break; - } + return do_tval_read(env, timeridx, offset); +} +static void do_tval_write(CPUARMState *env, int timeridx, uint64_t value, + uint64_t offset) +{ trace_arm_gt_tval_write(timeridx, value); env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) - offset + sextract64(value, 0, 32); gt_recalc_timer(env_archcpu(env), timeridx); } +static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + int timeridx, + uint64_t value) +{ + uint64_t offset = gt_direct_access_timer_offset(env, timeridx); + + do_tval_write(env, timeridx, value, offset); +} + static void gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, int timeridx, uint64_t value) @@ -2900,13 +2828,21 @@ static void gt_virt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t gt_virt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) { - return gt_tval_read(env, ri, GTIMER_VIRT); + /* + * This is CNTV_TVAL_EL02; unlike the underlying CNTV_TVAL_EL0 + * we always apply CNTVOFF_EL2. Special case that here rather + * than going into the generic gt_tval_read() and then having + * to re-detect that it's this register. + * Note that the accessfn/perms mean we know we're at EL2 or EL3 here. + */ + return do_tval_read(env, GTIMER_VIRT, env->cp15.cntvoff_el2); } static void gt_virt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - gt_tval_write(env, ri, GTIMER_VIRT, value); + /* Similarly for writes to CNTV_TVAL_EL02 */ + do_tval_write(env, GTIMER_VIRT, value, env->cp15.cntvoff_el2); } static void gt_virt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -2920,12 +2856,40 @@ static void gt_cnthctl_write(CPUARMState *env, const ARMCPRegInfo *ri, { 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) & CNTHCTL_CNTVMASK) { + if ((oldval ^ value) & R_CNTHCTL_CNTVMASK_MASK) { gt_update_irq(cpu, GTIMER_VIRT); - } else if ((oldval ^ value) & CNTHCTL_CNTPMASK) { + } else if ((oldval ^ value) & R_CNTHCTL_CNTPMASK_MASK) { gt_update_irq(cpu, GTIMER_PHYS); } } @@ -3038,6 +3002,62 @@ static void gt_sec_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, gt_ctl_write(env, ri, GTIMER_SEC, value); } +static void gt_sec_pel2_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + gt_timer_reset(env, ri, GTIMER_S_EL2_PHYS); +} + +static void gt_sec_pel2_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_cval_write(env, ri, GTIMER_S_EL2_PHYS, value); +} + +static uint64_t gt_sec_pel2_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return gt_tval_read(env, ri, GTIMER_S_EL2_PHYS); +} + +static void gt_sec_pel2_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_tval_write(env, ri, GTIMER_S_EL2_PHYS, value); +} + +static void gt_sec_pel2_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_ctl_write(env, ri, GTIMER_S_EL2_PHYS, value); +} + +static void gt_sec_vel2_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + gt_timer_reset(env, ri, GTIMER_S_EL2_VIRT); +} + +static void gt_sec_vel2_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_cval_write(env, ri, GTIMER_S_EL2_VIRT, value); +} + +static uint64_t gt_sec_vel2_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return gt_tval_read(env, ri, GTIMER_S_EL2_VIRT); +} + +static void gt_sec_vel2_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_tval_write(env, ri, GTIMER_S_EL2_VIRT, value); +} + +static void gt_sec_vel2_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + gt_ctl_write(env, ri, GTIMER_S_EL2_VIRT, value); +} + static void gt_hv_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) { gt_timer_reset(env, ri, GTIMER_HYPVIRT); @@ -3094,18 +3114,25 @@ void arm_gt_stimer_cb(void *opaque) gt_recalc_timer(cpu, GTIMER_SEC); } -void arm_gt_hvtimer_cb(void *opaque) +void arm_gt_sel2timer_cb(void *opaque) { ARMCPU *cpu = opaque; - gt_recalc_timer(cpu, GTIMER_HYPVIRT); + gt_recalc_timer(cpu, GTIMER_S_EL2_PHYS); } -static void arm_gt_cntfrq_reset(CPUARMState *env, const ARMCPRegInfo *opaque) +void arm_gt_sel2vtimer_cb(void *opaque) { - ARMCPU *cpu = env_archcpu(env); + ARMCPU *cpu = opaque; - cpu->env.cp15.c14_cntfrq = cpu->gt_cntfrq_hz; + gt_recalc_timer(cpu, GTIMER_S_EL2_VIRT); +} + +void arm_gt_hvtimer_cb(void *opaque) +{ + ARMCPU *cpu = opaque; + + gt_recalc_timer(cpu, GTIMER_HYPVIRT); } static const ARMCPRegInfo generic_timer_cp_reginfo[] = { @@ -3155,6 +3182,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, @@ -3172,6 +3200,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, @@ -3251,6 +3280,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, @@ -3268,6 +3298,7 @@ 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, @@ -3302,15 +3333,63 @@ 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 /* @@ -3335,7 +3414,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, @@ -3344,6 +3423,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) @@ -3377,7 +3468,7 @@ static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri, } return CP_ACCESS_TRAP_EL3; } - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } } return CP_ACCESS_OK; @@ -3408,11 +3499,12 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, GetPhysAddrResult res = {}; /* - * I_MXTJT: Granule protection checks are not performed on the final address - * of a successful translation. + * 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, mmu_idx, ss, - &res, &fi); + 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 @@ -3584,13 +3676,17 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) /* stage 1 current state PL1: ATS1CPR, ATS1CPW, ATS1CPRP, ATS1CPWP */ switch (el) { case 3: - mmu_idx = ARMMMUIdx_E3; + if (ri->crm == 9 && arm_pan_enabled(env)) { + mmu_idx = ARMMMUIdx_E30_3_PAN; + } else { + mmu_idx = ARMMMUIdx_E3; + } break; case 2: 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; @@ -3604,7 +3700,7 @@ 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; + mmu_idx = ARMMMUIdx_E30_0; break; case 2: g_assert(ss != ARMSS_Secure); /* ARMv8.4-SecEL2 is 64-bit only */ @@ -3669,7 +3765,7 @@ static CPAccessResult at_e012_access(CPUARMState *env, const ARMCPRegInfo *ri, * 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_UNDEFINED; } return CP_ACCESS_OK; } @@ -3679,7 +3775,16 @@ static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri, { if (arm_current_el(env) == 3 && !(env->cp15.scr_el3 & (SCR_NS | SCR_EEL2))) { - return CP_ACCESS_TRAP; + return CP_ACCESS_UNDEFINED; + } + 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); } @@ -3692,12 +3797,14 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, ARMMMUIdx mmu_idx; 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 { @@ -3709,6 +3816,7 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, break; case 6: /* AT S1E3R, AT S1E3W */ mmu_idx = ARMMMUIdx_E3; + for_el3 = true; break; default: g_assert_not_reached(); @@ -3727,8 +3835,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, arm_security_space(env)); + 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(); @@ -3736,20 +3844,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) { @@ -4247,6 +4341,7 @@ static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = { .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, }, }; @@ -4256,11 +4351,13 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .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, .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) } }, @@ -4268,6 +4365,7 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 1, .access = PL1_RW, .accessfn = access_tvm_trvm, .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) } }, @@ -4275,6 +4373,7 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .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, @@ -4514,6 +4613,7 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { .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, @@ -4563,7 +4663,7 @@ static CPAccessResult aa64_daif_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { if (arm_current_el(env) == 0 && !(arm_sctlr(env, 0) & SCTLR_UMA)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } return CP_ACCESS_OK; } @@ -4653,9 +4753,9 @@ static CPAccessResult aa64_cacheop_poc_access(CPUARMState *env, /* Cache invalidate/clean to Point of Coherency or Persistence... */ switch (arm_current_el(env)) { case 0: - /* ... EL0 must UNDEF unless SCTLR_EL1.UCI is set. */ + /* ... EL0 must trap to EL1 unless SCTLR_EL1.UCI is set. */ if (!(arm_sctlr(env, 0) & SCTLR_UCI)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } /* fall through */ case 1: @@ -4665,523 +4765,40 @@ static CPAccessResult aa64_cacheop_poc_access(CPUARMState *env, } break; } - return CP_ACCESS_OK; -} - -static CPAccessResult do_cacheop_pou_access(CPUARMState *env, uint64_t hcrflags) -{ - /* Cache invalidate/clean to Point of Unification... */ - switch (arm_current_el(env)) { - case 0: - /* ... EL0 must UNDEF unless SCTLR_EL1.UCI is set. */ - if (!(arm_sctlr(env, 0) & SCTLR_UCI)) { - return CP_ACCESS_TRAP; - } - /* fall through */ - case 1: - /* ... EL1 must trap to EL2 if relevant HCR_EL2 flags are set. */ - if (arm_hcr_el2_eff(env) & hcrflags) { - return CP_ACCESS_TRAP_EL2; - } - break; - } - return CP_ACCESS_OK; -} - -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) - */ - -static int vae1_tlbmask(CPUARMState *env) -{ - uint64_t hcr = arm_hcr_el2_eff(env); - uint16_t mask; - - if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { - mask = ARMMMUIdxBit_E20_2 | - ARMMMUIdxBit_E20_2_PAN | - ARMMMUIdxBit_E20_0; - } else { - mask = ARMMMUIdxBit_E10_1 | - ARMMMUIdxBit_E10_1_PAN | - ARMMMUIdxBit_E10_0; - } - 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) -{ - uint64_t tcr = regime_tcr(env, mmu_idx); - int tbi = aa64_va_parameter_tbi(tcr, mmu_idx); - int select = extract64(addr, 55, 1); - - return (tbi >> select) & 1 ? 56 : 64; -} - -static int vae1_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. */ - if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { - mmu_idx = ARMMMUIdx_E20_0; - } else { - mmu_idx = ARMMMUIdx_E10_0; - } - - 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) -{ - CPUState *cs = env_cpu(env); - int mask = vae1_tlbmask(env); - - tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); -} - -static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = vae1_tlbmask(env); - - if (tlb_force_broadcast(env)) { - tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); - } else { - tlb_flush_by_mmuidx(cs, mask); - } -} - -static int e2_tlbmask(CPUARMState *env) -{ - return (ARMMMUIdxBit_E20_0 | - ARMMMUIdxBit_E20_2 | - ARMMMUIdxBit_E20_2_PAN | - ARMMMUIdxBit_E2); -} - -static void tlbi_aa64_alle1_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = alle1_tlbmask(env); - - tlb_flush_by_mmuidx(cs, mask); -} - -static void tlbi_aa64_alle2_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = e2_tlbmask(env); - - tlb_flush_by_mmuidx(cs, mask); -} - -static void tlbi_aa64_alle3_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - ARMCPU *cpu = env_archcpu(env); - CPUState *cs = CPU(cpu); - - tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E3); -} - -static void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = alle1_tlbmask(env); - - tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); -} - -static void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = e2_tlbmask(env); - - tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); -} - -static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - - tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E3); -} - -static void tlbi_aa64_vae2_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA, EL2 - * Currently handles both VAE2 and VALE2, since we don't support - * flush-last-level-only. - */ - CPUState *cs = env_cpu(env); - int mask = vae2_tlbmask(env); - uint64_t pageaddr = sextract64(value << 12, 0, 56); - int bits = vae2_tlbbits(env, pageaddr); - - 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 - * Currently handles both VAE3 and VALE3, since we don't support - * flush-last-level-only. - */ - ARMCPU *cpu = env_archcpu(env); - CPUState *cs = CPU(cpu); - uint64_t pageaddr = sextract64(value << 12, 0, 56); - - tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E3); -} - -static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = vae1_tlbmask(env); - uint64_t pageaddr = sextract64(value << 12, 0, 56); - int bits = vae1_tlbbits(env, pageaddr); - - tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); -} - -static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * 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. - */ - CPUState *cs = env_cpu(env); - int mask = vae1_tlbmask(env); - uint64_t pageaddr = sextract64(value << 12, 0, 56); - int bits = vae1_tlbbits(env, pageaddr); - - if (tlb_force_broadcast(env)) { - tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); - } else { - tlb_flush_page_bits_by_mmuidx(cs, pageaddr, mask, bits); - } -} - -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 = vae2_tlbbits(env, pageaddr); - - tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); -} - -static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - uint64_t pageaddr = sextract64(value << 12, 0, 56); - int bits = tlbbits_for_regime(env, ARMMMUIdx_E3, pageaddr); - - tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, - ARMMMUIdxBit_E3, bits); -} - -static int ipas2e1_tlbmask(CPUARMState *env, int64_t value) -{ - /* - * The MSB of value is the NS field, which only applies if SEL2 - * is implemented and SCR_EL3.NS is not set (i.e. in secure mode). - */ - return (value >= 0 - && cpu_isar_feature(aa64_sel2, env_archcpu(env)) - && arm_is_secure_below_el3(env) - ? ARMMMUIdxBit_Stage2_S - : ARMMMUIdxBit_Stage2); -} - -static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = ipas2e1_tlbmask(env, value); - uint64_t pageaddr = sextract64(value << 12, 0, 56); - - if (tlb_force_broadcast(env)) { - tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, mask); - } else { - tlb_flush_page_by_mmuidx(cs, pageaddr, mask); - } -} - -static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = env_cpu(env); - int mask = ipas2e1_tlbmask(env, value); - uint64_t pageaddr = sextract64(value << 12, 0, 56); - - tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, mask); -} - -#ifdef TARGET_AARCH64 -typedef struct { - uint64_t base; - uint64_t length; -} TLBIRange; - -static ARMGranuleSize tlbi_range_tg_to_gran_size(int tg) -{ - /* - * Note that the TLBI range TG field encoding differs from both - * TG0 and TG1 encodings. - */ - switch (tg) { - case 1: - return Gran4K; - case 2: - return Gran16K; - case 3: - return Gran64K; - default: - return GranInvalid; - } -} - -static TLBIRange tlbi_aa64_get_range(CPUARMState *env, ARMMMUIdx mmuidx, - uint64_t value) -{ - unsigned int page_size_granule, page_shift, num, scale, exponent; - /* Extract one bit to represent the va selector in use. */ - uint64_t select = sextract64(value, 36, 1); - ARMVAParameters param = aa64_va_parameters(env, select, mmuidx, true, false); - TLBIRange ret = { }; - ARMGranuleSize gran; - - page_size_granule = extract64(value, 46, 2); - gran = tlbi_range_tg_to_gran_size(page_size_granule); - - /* The granule encoded in value must match the granule in use. */ - if (gran != param.gran) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid tlbi page size granule %d\n", - page_size_granule); - return ret; - } - - page_shift = arm_granule_bits(gran); - num = extract64(value, 39, 5); - scale = extract64(value, 44, 2); - exponent = (5 * scale) + 1; - - ret.length = (num + 1) << (exponent + page_shift); - - if (param.select) { - ret.base = sextract64(value, 0, 37); - } else { - ret.base = extract64(value, 0, 37); - } - if (param.ds) { - /* - * With DS=1, BaseADDR is always shifted 16 so that it is able - * to address all 52 va bits. The input address is perforce - * aligned on a 64k boundary regardless of translation granule. - */ - page_shift = 16; - } - ret.base <<= page_shift; - - return ret; -} - -static void do_rvae_write(CPUARMState *env, uint64_t value, - int idxmap, bool synced) -{ - ARMMMUIdx one_idx = ARM_MMU_IDX_A | ctz32(idxmap); - TLBIRange range; - int bits; - - range = tlbi_aa64_get_range(env, one_idx, value); - bits = tlbbits_for_regime(env, one_idx, range.base); - - if (synced) { - tlb_flush_range_by_mmuidx_all_cpus_synced(env_cpu(env), - range.base, - range.length, - idxmap, - bits); - } else { - tlb_flush_range_by_mmuidx(env_cpu(env), range.base, - range.length, idxmap, bits); - } -} - -static void tlbi_aa64_rvae1_write(CPUARMState *env, - const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA range, EL1&0. - * Currently handles all of RVAE1, RVAAE1, RVAALE1 and RVALE1, - * since we don't support flush-for-specific-ASID-only or - * flush-last-level-only. - */ - - do_rvae_write(env, value, vae1_tlbmask(env), - tlb_force_broadcast(env)); -} - -static void tlbi_aa64_rvae1is_write(CPUARMState *env, - const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA range, Inner/Outer Shareable EL1&0. - * Currently handles all of RVAE1IS, RVAE1OS, RVAAE1IS, RVAAE1OS, - * RVAALE1IS, RVAALE1OS, RVALE1IS and RVALE1OS, since we don't support - * flush-for-specific-ASID-only, flush-last-level-only or inner/outer - * shareable specific flushes. - */ - - do_rvae_write(env, value, vae1_tlbmask(env), true); -} - -static void tlbi_aa64_rvae2_write(CPUARMState *env, - const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA range, EL2. - * Currently handles all of RVAE2 and RVALE2, - * since we don't support flush-for-specific-ASID-only or - * flush-last-level-only. - */ - - do_rvae_write(env, value, vae2_tlbmask(env), - tlb_force_broadcast(env)); - - -} - -static void tlbi_aa64_rvae2is_write(CPUARMState *env, - const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA range, Inner/Outer Shareable, EL2. - * Currently handles all of RVAE2IS, RVAE2OS, RVALE2IS and RVALE2OS, - * since we don't support flush-for-specific-ASID-only, - * flush-last-level-only or inner/outer shareable specific flushes. - */ - - do_rvae_write(env, value, vae2_tlbmask(env), true); - -} - -static void tlbi_aa64_rvae3_write(CPUARMState *env, - const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA range, EL3. - * Currently handles all of RVAE3 and RVALE3, - * since we don't support flush-for-specific-ASID-only or - * flush-last-level-only. - */ - - do_rvae_write(env, value, ARMMMUIdxBit_E3, tlb_force_broadcast(env)); -} - -static void tlbi_aa64_rvae3is_write(CPUARMState *env, - const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * Invalidate by VA range, EL3, Inner/Outer Shareable. - * Currently handles all of RVAE3IS, RVAE3OS, RVALE3IS and RVALE3OS, - * since we don't support flush-for-specific-ASID-only, - * flush-last-level-only or inner/outer specific flushes. - */ + return CP_ACCESS_OK; +} - do_rvae_write(env, value, ARMMMUIdxBit_E3, true); +static CPAccessResult do_cacheop_pou_access(CPUARMState *env, uint64_t hcrflags) +{ + /* Cache invalidate/clean to Point of Unification... */ + switch (arm_current_el(env)) { + case 0: + /* ... EL0 must trap to EL1 unless SCTLR_EL1.UCI is set. */ + if (!(arm_sctlr(env, 0) & SCTLR_UCI)) { + return CP_ACCESS_TRAP_EL1; + } + /* fall through */ + case 1: + /* ... 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; + } + return CP_ACCESS_OK; } -static void tlbi_aa64_ripas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +static CPAccessResult access_ticab(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { - do_rvae_write(env, value, ipas2e1_tlbmask(env, value), - tlb_force_broadcast(env)); + return do_cacheop_pou_access(env, HCR_TICAB | HCR_TPU); } -static void tlbi_aa64_ripas2e1is_write(CPUARMState *env, - const ARMCPRegInfo *ri, - uint64_t value) +static CPAccessResult access_tocu(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { - do_rvae_write(env, value, ipas2e1_tlbmask(env, value), true); + return do_cacheop_pou_access(env, HCR_TOCU | HCR_TPU); } -#endif static CPAccessResult aa64_zva_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) @@ -5198,7 +4815,7 @@ static CPAccessResult aa64_zva_access(CPUARMState *env, const ARMCPRegInfo *ri, } } else { if (!(env->cp15.sctlr_el[1] & SCTLR_DZE)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } if (hcr & HCR_TDZ) { return CP_ACCESS_TRAP_EL2; @@ -5231,7 +4848,7 @@ static CPAccessResult sp_el0_access(CPUARMState *env, const ARMCPRegInfo *ri, * Access to SP_EL0 is undefined if it's being used as * the stack pointer. */ - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } return CP_ACCESS_OK; } @@ -5336,6 +4953,19 @@ 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 @@ -5360,7 +4990,7 @@ static void ic_ivau_write(CPUARMState *env, const ARMCPRegInfo *ri, mmap_lock(); - tb_invalidate_phys_range(start_address, end_address); + tb_invalidate_phys_range(env_cpu(env), start_address, end_address); mmap_unlock(); } @@ -5382,7 +5012,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .writefn = aa64_daif_write, .resetfn = arm_cp_reset_ignore }, { .name = "FPCR", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 0, .crn = 4, .crm = 4, - .access = PL0_RW, .type = ARM_CP_FPU | ARM_CP_SUPPRESS_TB_END, + .access = PL0_RW, .type = ARM_CP_FPU, .readfn = aa64_fpcr_read, .writefn = aa64_fpcr_write }, { .name = "FPSR", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 4, .crm = 4, @@ -5464,121 +5094,28 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2, .fgt = FGT_DCCISW, .access = PL1_W, .accessfn = access_tsw, .type = ARM_CP_NOP }, - /* TLBI operations */ - { .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVMALLE1IS, - .writefn = tlbi_aa64_vmalle1is_write }, - { .name = "TLBI_VAE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAE1IS, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_ASIDE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIASIDE1IS, - .writefn = tlbi_aa64_vmalle1is_write }, - { .name = "TLBI_VAAE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAAE1IS, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_VALE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVALE1IS, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_VAALE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAALE1IS, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_VMALLE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVMALLE1, - .writefn = tlbi_aa64_vmalle1_write }, - { .name = "TLBI_VAE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAE1, - .writefn = tlbi_aa64_vae1_write }, - { .name = "TLBI_ASIDE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIASIDE1, - .writefn = tlbi_aa64_vmalle1_write }, - { .name = "TLBI_VAAE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAAE1, - .writefn = tlbi_aa64_vae1_write }, - { .name = "TLBI_VALE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVALE1, - .writefn = tlbi_aa64_vae1_write }, - { .name = "TLBI_VAALE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAALE1, - .writefn = tlbi_aa64_vae1_write }, - { .name = "TLBI_IPAS2E1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ipas2e1is_write }, - { .name = "TLBI_IPAS2LE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ipas2e1is_write }, - { .name = "TLBI_ALLE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle1is_write }, - { .name = "TLBI_VMALLS12E1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle1is_write }, - { .name = "TLBI_IPAS2E1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ipas2e1_write }, - { .name = "TLBI_IPAS2LE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ipas2e1_write }, - { .name = "TLBI_ALLE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle1_write }, - { .name = "TLBI_VMALLS12E1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle1is_write }, #ifndef CONFIG_USER_ONLY /* 64 bit address translation operations */ { .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .fgt = FGT_ATS1E1R, - .accessfn = at_e012_access, .writefn = ats_write64 }, + .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, .fgt = FGT_ATS1E1W, - .accessfn = at_e012_access, .writefn = ats_write64 }, + .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, .fgt = FGT_ATS1E0R, - .accessfn = at_e012_access, .writefn = ats_write64 }, + .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, .fgt = FGT_ATS1E0W, - .accessfn = at_e012_access, .writefn = ats_write64 }, + .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, @@ -5612,42 +5149,6 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .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_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_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, - .writefn = tlbimva_write }, - { .name = "TLBIMVAAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, - .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, - .writefn = tlbimvaa_write }, - { .name = "TLBIMVALH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 5, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbimva_hyp_write }, - { .name = "TLBIMVALHIS", - .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 5, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbimva_hyp_is_write }, - { .name = "TLBIIPAS2", - .cp = 15, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiipas2_hyp_write }, - { .name = "TLBIIPAS2IS", - .cp = 15, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiipas2is_hyp_write }, - { .name = "TLBIIPAS2L", - .cp = 15, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 5, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiipas2_hyp_write }, - { .name = "TLBIIPAS2LIS", - .cp = 15, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 5, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiipas2is_hyp_write }, /* 32 bit cache operations */ { .name = "ICIALLUIS", .cp = 15, .opc1 = 0, .crn = 7, .crm = 1, .opc2 = 0, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_ticab }, @@ -5684,12 +5185,14 @@ 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 @@ -5703,26 +5206,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, @@ -5757,6 +5247,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); @@ -5806,6 +5314,12 @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) 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)) { @@ -5817,6 +5331,11 @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) /* Clear RES0 bits. */ value &= valid_mask; + /* RW is RAO/WI if EL1 is AArch64 only */ + if (!cpu_isar_feature(aa64_aa32_el1, cpu)) { + value |= HCR_RW; + } + /* * These bits change the MMU setup: * HCR_VM enables stage 2 translation @@ -5824,9 +5343,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; @@ -5835,17 +5355,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) @@ -5869,6 +5393,12 @@ static void hcr_writelow(CPUARMState *env, const ARMCPRegInfo *ri, do_hcr_write(env, value, MAKE_64BIT_MASK(32, 32)); } +static void hcr_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* hcr_write will set the RES1 bits on an AArch64-only CPU */ + hcr_write(env, ri, 0); +} + /* * Return the effective value of HCR_EL2, at the given security state. * Bits that are not included here: @@ -5978,18 +5508,52 @@ 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; + } + /* FEAT_XS adds FGTnXS, FnXS */ + if (cpu_isar_feature(aa64_xs, cpu)) { + valid_mask |= HCRX_FGTNXS | HCRX_FNXS; + } /* 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; @@ -5999,8 +5563,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), }; @@ -6009,13 +5575,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. - */ - if (!arm_is_el2_enabled(env) - || (arm_feature(env, ARM_FEATURE_EL3) - && !(env->cp15.scr_el3 & SCR_HXEN))) { + * 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)) { + 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; @@ -6056,6 +5633,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), + .nv2_redirect_offset = 0x78, + .resetfn = hcr_reset, .writefn = hcr_write, .raw_writefn = raw_write }, { .name = "HCR", .state = ARM_CP_STATE_AA32, .type = ARM_CP_ALIAS | ARM_CP_IO, @@ -6066,14 +5645,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, @@ -6082,7 +5663,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]) }, @@ -6138,6 +5719,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, @@ -6149,6 +5731,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "VTTBR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 0, .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, @@ -6157,6 +5740,7 @@ 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, @@ -6166,50 +5750,6 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "HTTBR", .cp = 15, .opc1 = 4, .crm = 2, .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) }, - { .name = "TLBIALLNSNH", - .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiall_nsnh_write }, - { .name = "TLBIALLNSNHIS", - .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiall_nsnh_is_write }, - { .name = "TLBIALLH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiall_hyp_write }, - { .name = "TLBIALLHIS", .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 0, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbiall_hyp_is_write }, - { .name = "TLBIMVAH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbimva_hyp_write }, - { .name = "TLBIMVAHIS", .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL2_W, - .writefn = tlbimva_hyp_is_write }, - { .name = "TLBI_ALLE2", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_alle2_write }, - { .name = "TLBI_VAE2", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_vae2_write }, - { .name = "TLBI_VALE2", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_vae2_write }, - { .name = "TLBI_ALLE2IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 0, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_alle2is_write }, - { .name = "TLBI_VAE2IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_vae2is_write }, - { .name = "TLBI_VALE2IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 5, - .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 @@ -6252,6 +5792,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .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, @@ -6290,6 +5831,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) }, }; @@ -6308,18 +5850,70 @@ static CPAccessResult sel2_access(CPUARMState *env, const ARMCPRegInfo *ri, if (arm_current_el(env) == 3 || arm_is_secure_below_el3(env)) { return CP_ACCESS_OK; } - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } 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) }, +#ifndef CONFIG_USER_ONLY + /* Secure EL2 Physical Timer */ + { .name = "CNTHPS_TVAL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 5, .opc2 = 0, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .readfn = gt_sec_pel2_tval_read, + .writefn = gt_sec_pel2_tval_write, + .resetfn = gt_sec_pel2_timer_reset, + }, + { .name = "CNTHPS_CTL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 5, .opc2 = 1, + .type = ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_PHYS].ctl), + .resetvalue = 0, + .writefn = gt_sec_pel2_ctl_write, .raw_writefn = raw_write, + }, + { .name = "CNTHPS_CVAL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 5, .opc2 = 2, + .type = ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_PHYS].cval), + .writefn = gt_sec_pel2_cval_write, .raw_writefn = raw_write, + }, + /* Secure EL2 Virtual Timer */ + { .name = "CNTHVS_TVAL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 4, .opc2 = 0, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .readfn = gt_sec_vel2_tval_read, + .writefn = gt_sec_vel2_tval_write, + .resetfn = gt_sec_vel2_timer_reset, + }, + { .name = "CNTHVS_CTL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 4, .opc2 = 1, + .type = ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_VIRT].ctl), + .resetvalue = 0, + .writefn = gt_sec_vel2_ctl_write, .raw_writefn = raw_write, + }, + { .name = "CNTHVS_CVAL_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 4, .opc2 = 2, + .type = ARM_CP_IO, .access = PL2_RW, + .accessfn = gt_sel2timer_access, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_VIRT].cval), + .writefn = gt_sec_vel2_cval_write, .raw_writefn = raw_write, + }, +#endif }; static CPAccessResult nsacr_access(CPUARMState *env, const ARMCPRegInfo *ri, @@ -6342,7 +5936,7 @@ static CPAccessResult nsacr_access(CPUARMState *env, const ARMCPRegInfo *ri, if (isread) { return CP_ACCESS_OK; } - return CP_ACCESS_TRAP_UNCATEGORIZED; + return CP_ACCESS_UNDEFINED; } static const ARMCPRegInfo el3_cp_reginfo[] = { @@ -6418,33 +6012,47 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { .opc0 = 3, .opc1 = 6, .crn = 5, .crm = 1, .opc2 = 1, .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "TLBI_ALLE3IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 0, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle3is_write }, - { .name = "TLBI_VAE3IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vae3is_write }, - { .name = "TLBI_VALE3IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vae3is_write }, - { .name = "TLBI_ALLE3", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 0, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle3_write }, - { .name = "TLBI_VAE3", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vae3_write }, - { .name = "TLBI_VALE3", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vae3_write }, }; #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_UNDEFINED; + } + 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) { @@ -6486,6 +6094,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_UNDEFINED; + } + 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 { @@ -6585,6 +6229,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); @@ -6620,7 +6299,7 @@ static CPAccessResult ctr_el0_access(CPUARMState *env, const ARMCPRegInfo *ri, } } else { if (!(env->cp15.sctlr_el[1] & SCTLR_UCT)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } if (hcr & HCR_TID2) { return CP_ACCESS_TRAP_EL2; @@ -6650,7 +6329,7 @@ static CPAccessResult access_terr(CPUARMState *env, const ARMCPRegInfo *ri, if (el < 2 && (arm_hcr_el2_eff(env) & HCR_TERR)) { return CP_ACCESS_TRAP_EL2; } - if (el < 3 && (env->cp15.scr_el3 & SCR_TERR)) { + if (!arm_is_el3_or_mon(env) && (env->cp15.scr_el3 & SCR_TERR)) { return CP_ACCESS_TRAP_EL3; } return CP_ACCESS_OK; @@ -6718,9 +6397,11 @@ static const ARMCPRegInfo minimal_ras_reginfo[] = { .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) }, }; @@ -6846,7 +6527,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)) { @@ -6892,6 +6573,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 }, @@ -6907,7 +6589,6 @@ static const ARMCPRegInfo zcr_reginfo[] = { .writefn = zcr_write, .raw_writefn = raw_write }, }; -#ifdef TARGET_AARCH64 static CPAccessResult access_tpidr2(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -6916,7 +6597,7 @@ static CPAccessResult access_tpidr2(CPUARMState *env, const ARMCPRegInfo *ri, if (el == 0) { uint64_t sctlr = arm_sctlr(env, el); if (!(sctlr & SCTLR_EnTP2)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } } /* TODO: FEAT_FGT */ @@ -6928,10 +6609,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)) { @@ -6946,7 +6638,7 @@ 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); + vfp_set_fpsr(env, 0x0800009f); } void aarch64_set_svcr(CPUARMState *env, uint64_t new, uint64_t mask) @@ -7022,6 +6714,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 }, @@ -7050,23 +6743,16 @@ 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) { @@ -7084,14 +6770,6 @@ static void gpccr_reset(CPUARMState *env, const ARMCPRegInfo *ri) 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, @@ -7103,28 +6781,6 @@ static const ARMCPRegInfo rme_reginfo[] = { { .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 }, @@ -7135,7 +6791,37 @@ static const ARMCPRegInfo rme_mte_reginfo[] = { .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 14, .opc2 = 5, .access = PL3_W, .type = ARM_CP_NOP }, }; -#endif /* TARGET_AARCH64 */ + +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 }, +}; static void define_pmu_regs(ARMCPU *cpu) { @@ -7151,8 +6837,9 @@ static void define_pmu_regs(ARMCPU *cpu) .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, @@ -7162,6 +6849,7 @@ static void define_pmu_regs(ARMCPU *cpu) .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, }; @@ -7286,8 +6974,8 @@ static CPAccessResult access_lor_other(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { if (arm_is_secure_below_el3(env)) { - /* Access denied in secure mode. */ - return CP_ACCESS_TRAP; + /* UNDEF if SCR_EL3.NS == 0 */ + return CP_ACCESS_UNDEFINED; } return access_lor_ns(env, ri, isread); } @@ -7325,7 +7013,6 @@ static const ARMCPRegInfo lor_reginfo[] = { .type = ARM_CP_CONST, .resetvalue = 0 }, }; -#ifdef TARGET_AARCH64 static CPAccessResult access_pauth(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -7397,210 +7084,6 @@ static const ARMCPRegInfo pauth_reginfo[] = { .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_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAE1IS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVAAE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAAE1IS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVALE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVALE1IS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVAALE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlbis, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAALE1IS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVAE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAE1OS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVAAE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAAE1OS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVALE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVALE1OS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVAALE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAALE1OS, - .writefn = tlbi_aa64_rvae1is_write }, - { .name = "TLBI_RVAE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAE1, - .writefn = tlbi_aa64_rvae1_write }, - { .name = "TLBI_RVAAE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAAE1, - .writefn = tlbi_aa64_rvae1_write }, - { .name = "TLBI_RVALE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVALE1, - .writefn = tlbi_aa64_rvae1_write }, - { .name = "TLBI_RVAALE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIRVAALE1, - .writefn = tlbi_aa64_rvae1_write }, - { .name = "TLBI_RIPAS2E1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 2, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ripas2e1is_write }, - { .name = "TLBI_RIPAS2LE1IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ripas2e1is_write }, - { .name = "TLBI_RVAE2IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 2, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_rvae2is_write }, - { .name = "TLBI_RVALE2IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 2, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_rvae2is_write }, - { .name = "TLBI_RIPAS2E1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 2, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ripas2e1_write }, - { .name = "TLBI_RIPAS2LE1", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_ripas2e1_write }, - { .name = "TLBI_RVAE2OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 5, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_rvae2is_write }, - { .name = "TLBI_RVALE2OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 5, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_rvae2is_write }, - { .name = "TLBI_RVAE2", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 6, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_rvae2_write }, - { .name = "TLBI_RVALE2", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 6, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_rvae2_write }, - { .name = "TLBI_RVAE3IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 2, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_rvae3is_write }, - { .name = "TLBI_RVALE3IS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 2, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_rvae3is_write }, - { .name = "TLBI_RVAE3OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 5, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_rvae3is_write }, - { .name = "TLBI_RVALE3OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 5, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_rvae3is_write }, - { .name = "TLBI_RVAE3", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 6, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_rvae3_write }, - { .name = "TLBI_RVALE3", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 6, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_rvae3_write }, -}; - -static const ARMCPRegInfo tlbios_reginfo[] = { - { .name = "TLBI_VMALLE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 0, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVMALLE1OS, - .writefn = tlbi_aa64_vmalle1is_write }, - { .name = "TLBI_VAE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 1, - .fgt = FGT_TLBIVAE1OS, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_ASIDE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 2, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIASIDE1OS, - .writefn = tlbi_aa64_vmalle1is_write }, - { .name = "TLBI_VAAE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 3, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAAE1OS, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_VALE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 5, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVALE1OS, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_VAALE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 7, - .access = PL1_W, .accessfn = access_ttlbos, .type = ARM_CP_NO_RAW, - .fgt = FGT_TLBIVAALE1OS, - .writefn = tlbi_aa64_vae1is_write }, - { .name = "TLBI_ALLE2OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 0, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_alle2is_write }, - { .name = "TLBI_VAE2OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 1, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_vae2is_write }, - { .name = "TLBI_ALLE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 4, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle1is_write }, - { .name = "TLBI_VALE2OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = tlbi_aa64_vae2is_write }, - { .name = "TLBI_VMALLS12E1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle1is_write }, - { .name = "TLBI_IPAS2E1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 0, - .access = PL2_W, .type = ARM_CP_NOP }, - { .name = "TLBI_RIPAS2E1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 3, - .access = PL2_W, .type = ARM_CP_NOP }, - { .name = "TLBI_IPAS2LE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 4, - .access = PL2_W, .type = ARM_CP_NOP }, - { .name = "TLBI_RIPAS2LE1OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 7, - .access = PL2_W, .type = ARM_CP_NOP }, - { .name = "TLBI_ALLE3OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 0, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_alle3is_write }, - { .name = "TLBI_VAE3OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vae3is_write }, - { .name = "TLBI_VALE3OS", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 5, - .access = PL3_W, .type = ARM_CP_NO_RAW, - .writefn = tlbi_aa64_vae3is_write }, -}; - static uint64_t rndr_readfn(CPUARMState *env, const ARMCPRegInfo *ri) { Error *err = NULL; @@ -7642,13 +7125,14 @@ static const ARMCPRegInfo rndr_reginfo[] = { 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()); @@ -7666,6 +7150,10 @@ static void dccvap_writefn(CPUARMState *env, const ARMCPRegInfo *opaque, } #endif /*CONFIG_USER_ONLY*/ } +#else + /* Handled by hardware accelerator. */ + g_assert_not_reached(); +#endif /* CONFIG_TCG */ } static const ARMCPRegInfo dcpop_reg[] = { @@ -7698,7 +7186,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))) { @@ -7730,11 +7257,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, @@ -7866,7 +7395,7 @@ static CPAccessResult access_scxtnum(CPUARMState *env, const ARMCPRegInfo *ri, if (hcr & HCR_TGE) { return CP_ACCESS_TRAP_EL2; } - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } } else if (el < 2 && (env->cp15.sctlr_el[2] & SCTLR_TSCXT)) { return CP_ACCESS_TRAP_EL2; @@ -7882,6 +7411,18 @@ 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, @@ -7890,8 +7431,9 @@ static const ARMCPRegInfo scxtnum_reginfo[] = { .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, @@ -7916,26 +7458,52 @@ static CPAccessResult access_fgt(CPUARMState *env, const ARMCPRegInfo *ri, 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]) }, }; -#endif /* TARGET_AARCH64 */ + +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) }, +}; static CPAccessResult access_predinv(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) @@ -7945,7 +7513,7 @@ static CPAccessResult access_predinv(CPUARMState *env, const ARMCPRegInfo *ri, if (el == 0) { uint64_t sctlr = arm_sctlr(env, el); if (!(sctlr & SCTLR_EnRCTX)) { - return CP_ACCESS_TRAP; + return CP_ACCESS_TRAP_EL1; } } else if (el == 1) { uint64_t hcr = arm_hcr_el2_eff(env); @@ -8094,13 +7662,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, @@ -8117,13 +7687,15 @@ 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 }; @@ -8134,12 +7706,12 @@ static const ARMCPRegInfo ats1e1_reginfo[] = { .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .fgt = FGT_ATS1E1RP, - .accessfn = at_e012_access, .writefn = ats_write64 }, + .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, .fgt = FGT_ATS1E1WP, - .accessfn = at_e012_access, .writefn = ats_write64 }, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, }; static const ARMCPRegInfo ats1cp_reginfo[] = { @@ -8192,6 +7764,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, not_v8_cp_reginfo); } +#ifndef CONFIG_USER_ONLY + define_tlb_insn_regs(cpu); +#endif + if (arm_feature(env, ARM_FEATURE_V6)) { /* The ID registers all have impdef reset values */ ARMCPRegInfo v6_idregs[] = { @@ -8297,10 +7873,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_V6K)) { define_arm_cp_regs(cpu, v6k_cp_reginfo); } - if (arm_feature(env, ARM_FEATURE_V7MP) && - !arm_feature(env, ARM_FEATURE_PMSA)) { - define_arm_cp_regs(cpu, v7mp_cp_reginfo); - } if (arm_feature(env, ARM_FEATURE_V7VE)) { define_arm_cp_regs(cpu, pmovsset_cp_reginfo); } @@ -8480,11 +8052,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, @@ -8614,6 +8186,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) 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 | @@ -8621,11 +8194,16 @@ void register_cp_regs_for_features(ARMCPU *cpu) 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", .exported_bits = R_ID_AA64MMFR0_ECV_MASK, @@ -8635,6 +8213,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) .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 }, { .name = "ID_AA64DFR0_EL1", @@ -8676,7 +8256,11 @@ void register_cp_regs_for_features(ARMCPU *cpu) .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_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 }, }; @@ -8704,6 +8288,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) } 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++) { /* @@ -8751,6 +8338,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, @@ -8762,6 +8350,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) }, }; /* @@ -8894,7 +8483,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)) { @@ -8984,7 +8602,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .type = ARM_CP_CONST, .resetvalue = cpu->revidr }, }; ARMCPRegInfo id_v8_midr_alias_cp_reginfo = { - .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST, + .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 }; @@ -9170,6 +8788,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, @@ -9200,7 +8819,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); @@ -9239,7 +8858,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 }, @@ -9254,6 +8875,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .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, @@ -9325,7 +8947,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_one_arm_cp_reg(cpu, &hcrx_el2_reginfo); } -#ifdef TARGET_AARCH64 if (cpu_isar_feature(aa64_sme, cpu)) { define_arm_cp_regs(cpu, sme_reginfo); } @@ -9335,12 +8956,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa64_rndr, cpu)) { define_arm_cp_regs(cpu, rndr_reginfo); } - if (cpu_isar_feature(aa64_tlbirange, cpu)) { - define_arm_cp_regs(cpu, tlbirange_reginfo); - } - if (cpu_isar_feature(aa64_tlbios, cpu)) { - define_arm_cp_regs(cpu, tlbios_reginfo); - } /* Data Cache clean instructions up to PoP */ if (cpu_isar_feature(aa64_dcpop, cpu)) { define_one_arm_cp_reg(cpu, dcpop_reg); @@ -9384,7 +8999,14 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, rme_mte_reginfo); } } -#endif + + 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); + } if (cpu_isar_feature(any_predinv, cpu)) { define_arm_cp_regs(cpu, predinv_reginfo); @@ -9405,52 +9027,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); -} - /* * Private utility function for define_one_arm_cp_reg_with_opaque(): * add a single reginfo struct to the hash table. @@ -9791,6 +9367,31 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, if (r->state != state && r->state != ARM_CP_STATE_BOTH) { continue; } + if ((r->type & ARM_CP_ADD_TLBI_NXS) && + cpu_isar_feature(aa64_xs, cpu)) { + /* + * This is a TLBI insn which has an NXS variant. The + * NXS variant is at the same encoding except that + * crn is +1, and has the same behaviour except for + * fine-grained trapping. Add the NXS insn here and + * then fall through to add the normal register. + * add_cpreg_to_hashtable() copies the cpreg struct + * and name that it is passed, so it's OK to use + * a local struct here. + */ + ARMCPRegInfo nxs_ri = *r; + g_autofree char *name = g_strdup_printf("%sNXS", r->name); + + assert(state == ARM_CP_STATE_AA64); + assert(nxs_ri.crn < 0xf); + nxs_ri.crn++; + if (nxs_ri.fgt) { + nxs_ri.fgt |= R_FGT_NXS_MASK; + } + add_cpreg_to_hashtable(cpu, &nxs_ri, opaque, state, + ARM_CP_SECSTATE_NS, + crm, opc1, opc2, name); + } if (state == ARM_CP_STATE_AA32) { /* * Under AArch32 CP registers can be common @@ -10102,61 +9703,6 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, } } -/* 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) @@ -10274,7 +9820,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; @@ -10284,7 +9830,7 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, uint64_t hcr_el2; if (arm_feature(env, ARM_FEATURE_EL3)) { - rw = ((env->cp15.scr_el3 & SCR_RW) == SCR_RW); + rw = arm_scr_rw_eff(env); } else { /* * Either EL2 is the highest EL (and so the EL2 register width @@ -10297,6 +9843,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; @@ -10355,6 +9902,10 @@ void arm_log_exception(CPUState *cs) [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", + [EXCP_MON_TRAP] = "Monitor Trap", }; if (idx >= 0 && idx < ARRAY_SIZE(excnames)) { @@ -10790,6 +10341,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; } @@ -10903,6 +10472,16 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs) mask = CPSR_A | CPSR_I | CPSR_F; offset = 0; break; + case EXCP_MON_TRAP: + new_mode = ARM_CPU_MODE_MON; + addr = 0x04; + mask = CPSR_A | CPSR_I | CPSR_F; + if (env->thumb) { + offset = 2; + } else { + offset = 4; + } + break; default: cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); return; /* Never happens. Keep compiler happy. */ @@ -11036,7 +10615,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; unsigned int new_el = env->exception.target_el; - target_ulong addr = env->cp15.vbar_el[new_el]; + vaddr addr = env->cp15.vbar_el[new_el]; unsigned int new_mode = aarch64_pstate_mode(new_el, true); unsigned int old_mode; unsigned int cur_el = arm_current_el(env); @@ -11060,7 +10639,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) switch (new_el) { case 3: - is_aa64 = (env->cp15.scr_el3 & SCR_RW) != 0; + is_aa64 = arm_scr_rw_eff(env); break; case 2: hcr = arm_hcr_el2_eff(env); @@ -11152,10 +10731,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: @@ -11172,6 +10754,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]; @@ -11182,6 +10778,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]); @@ -11217,6 +10814,14 @@ 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); @@ -11311,7 +10916,7 @@ void arm_cpu_do_interrupt(CPUState *cs) * 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); @@ -11332,10 +10937,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]; } @@ -11599,289 +11214,6 @@ 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. - */ - -/* Signed saturating arithmetic. */ - -/* Perform 16-bit signed saturating addition. */ -static inline uint16_t add16_sat(uint16_t a, uint16_t b) -{ - uint16_t res; - - res = a + b; - if (((res ^ a) & 0x8000) && !((a ^ b) & 0x8000)) { - if (a & 0x8000) { - res = 0x8000; - } else { - res = 0x7fff; - } - } - return res; -} - -/* Perform 8-bit signed saturating addition. */ -static inline uint8_t add8_sat(uint8_t a, uint8_t b) -{ - uint8_t res; - - res = a + b; - if (((res ^ a) & 0x80) && !((a ^ b) & 0x80)) { - if (a & 0x80) { - res = 0x80; - } else { - res = 0x7f; - } - } - return res; -} - -/* Perform 16-bit signed saturating subtraction. */ -static inline uint16_t sub16_sat(uint16_t a, uint16_t b) -{ - uint16_t res; - - res = a - b; - if (((res ^ a) & 0x8000) && ((a ^ b) & 0x8000)) { - if (a & 0x8000) { - res = 0x8000; - } else { - res = 0x7fff; - } - } - return res; -} - -/* Perform 8-bit signed saturating subtraction. */ -static inline uint8_t sub8_sat(uint8_t a, uint8_t b) -{ - uint8_t res; - - res = a - b; - if (((res ^ a) & 0x80) && ((a ^ b) & 0x80)) { - if (a & 0x80) { - res = 0x80; - } else { - res = 0x7f; - } - } - return res; -} - -#define ADD16(a, b, n) RESULT(add16_sat(a, b), n, 16); -#define SUB16(a, b, n) RESULT(sub16_sat(a, b), n, 16); -#define ADD8(a, b, n) RESULT(add8_sat(a, b), n, 8); -#define SUB8(a, b, n) RESULT(sub8_sat(a, b), n, 8); -#define PFX q - -#include "op_addsub.h" - -/* Unsigned saturating arithmetic. */ -static inline uint16_t add16_usat(uint16_t a, uint16_t b) -{ - uint16_t res; - res = a + b; - if (res < a) { - res = 0xffff; - } - return res; -} - -static inline uint16_t sub16_usat(uint16_t a, uint16_t b) -{ - if (a > b) { - return a - b; - } else { - return 0; - } -} - -static inline uint8_t add8_usat(uint8_t a, uint8_t b) -{ - uint8_t res; - res = a + b; - if (res < a) { - res = 0xff; - } - return res; -} - -static inline uint8_t sub8_usat(uint8_t a, uint8_t b) -{ - if (a > b) { - return a - b; - } else { - return 0; - } -} - -#define ADD16(a, b, n) RESULT(add16_usat(a, b), n, 16); -#define SUB16(a, b, n) RESULT(sub16_usat(a, b), n, 16); -#define ADD8(a, b, n) RESULT(add8_usat(a, b), n, 8); -#define SUB8(a, b, n) RESULT(sub8_usat(a, b), n, 8); -#define PFX uq - -#include "op_addsub.h" - -/* Signed modulo arithmetic. */ -#define SARITH16(a, b, n, op) do { \ - int32_t sum; \ - sum = (int32_t)(int16_t)(a) op (int32_t)(int16_t)(b); \ - RESULT(sum, n, 16); \ - if (sum >= 0) \ - ge |= 3 << (n * 2); \ - } while (0) - -#define SARITH8(a, b, n, op) do { \ - int32_t sum; \ - sum = (int32_t)(int8_t)(a) op (int32_t)(int8_t)(b); \ - RESULT(sum, n, 8); \ - if (sum >= 0) \ - ge |= 1 << n; \ - } while (0) - - -#define ADD16(a, b, n) SARITH16(a, b, n, +) -#define SUB16(a, b, n) SARITH16(a, b, n, -) -#define ADD8(a, b, n) SARITH8(a, b, n, +) -#define SUB8(a, b, n) SARITH8(a, b, n, -) -#define PFX s -#define ARITH_GE - -#include "op_addsub.h" - -/* Unsigned modulo arithmetic. */ -#define ADD16(a, b, n) do { \ - uint32_t sum; \ - sum = (uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b); \ - RESULT(sum, n, 16); \ - if ((sum >> 16) == 1) \ - ge |= 3 << (n * 2); \ - } while (0) - -#define ADD8(a, b, n) do { \ - uint32_t sum; \ - sum = (uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b); \ - RESULT(sum, n, 8); \ - if ((sum >> 8) == 1) \ - ge |= 1 << n; \ - } while (0) - -#define SUB16(a, b, n) do { \ - uint32_t sum; \ - sum = (uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b); \ - RESULT(sum, n, 16); \ - if ((sum >> 16) == 0) \ - ge |= 3 << (n * 2); \ - } while (0) - -#define SUB8(a, b, n) do { \ - uint32_t sum; \ - sum = (uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b); \ - RESULT(sum, n, 8); \ - if ((sum >> 8) == 0) \ - ge |= 1 << n; \ - } while (0) - -#define PFX u -#define ARITH_GE - -#include "op_addsub.h" - -/* Halved signed arithmetic. */ -#define ADD16(a, b, n) \ - RESULT(((int32_t)(int16_t)(a) + (int32_t)(int16_t)(b)) >> 1, n, 16) -#define SUB16(a, b, n) \ - RESULT(((int32_t)(int16_t)(a) - (int32_t)(int16_t)(b)) >> 1, n, 16) -#define ADD8(a, b, n) \ - RESULT(((int32_t)(int8_t)(a) + (int32_t)(int8_t)(b)) >> 1, n, 8) -#define SUB8(a, b, n) \ - RESULT(((int32_t)(int8_t)(a) - (int32_t)(int8_t)(b)) >> 1, n, 8) -#define PFX sh - -#include "op_addsub.h" - -/* Halved unsigned arithmetic. */ -#define ADD16(a, b, n) \ - RESULT(((uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b)) >> 1, n, 16) -#define SUB16(a, b, n) \ - RESULT(((uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b)) >> 1, n, 16) -#define ADD8(a, b, n) \ - RESULT(((uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b)) >> 1, n, 8) -#define SUB8(a, b, n) \ - RESULT(((uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b)) >> 1, n, 8) -#define PFX uh - -#include "op_addsub.h" - -static inline uint8_t do_usad(uint8_t a, uint8_t b) -{ - if (a > b) { - return a - b; - } else { - return b - a; - } -} - -/* Unsigned sum of absolute byte differences. */ -uint32_t HELPER(usad8)(uint32_t a, uint32_t b) -{ - uint32_t sum; - sum = do_usad(a, b); - sum += do_usad(a >> 8, b >> 8); - sum += do_usad(a >> 16, b >> 16); - sum += do_usad(a >> 24, b >> 24); - return sum; -} - -/* For ARMv6 SEL instruction. */ -uint32_t HELPER(sel_flags)(uint32_t flags, uint32_t a, uint32_t b) -{ - uint32_t mask; - - mask = 0; - if (flags & 1) { - mask |= 0xff; - } - if (flags & 2) { - mask |= 0xff00; - } - if (flags & 4) { - mask |= 0xff0000; - } - if (flags & 8) { - mask |= 0xff000000; - } - return (a & mask) | (b & ~mask); -} - -/* - * CRC helpers. - * The upper bytes of val (above the number specified by 'bytes') must have - * been zeroed out by the caller. - */ -uint32_t HELPER(crc32)(uint32_t acc, uint32_t val, uint32_t bytes) -{ - uint8_t buf[4]; - - stl_le_p(buf, val); - - /* zlib crc32 converts the accumulator and output to one's complement. */ - return crc32(acc ^ 0xffffffff, buf, bytes) ^ 0xffffffff; -} - -uint32_t HELPER(crc32c)(uint32_t acc, uint32_t val, uint32_t bytes) -{ - uint8_t buf[4]; - - stl_le_p(buf, val); - - /* Linux crc32c converts the output to one's complement. */ - return crc32c(acc, buf, bytes) ^ 0xffffffff; -} /* * Return the exception level to which FP-disabled exceptions should @@ -12003,6 +11335,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: @@ -12012,6 +11345,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(); @@ -12025,15 +11359,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; @@ -12049,6 +11374,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; } @@ -12073,6 +11401,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(); @@ -12086,116 +11417,6 @@ ARMMMUIdx arm_mmu_idx(CPUARMState *env) return arm_mmu_idx_el(env, arm_current_el(env)); } -static bool mve_no_pred(CPUARMState *env) -{ - /* - * Return true if there is definitely no predication of MVE - * instructions by VPR or LTPSIZE. (Returning false even if there - * isn't any predication is OK; generated code will just be - * a little worse.) - * If the CPU does not implement MVE then this TB flag is always 0. - * - * NOTE: if you change this logic, the "recalculate s->mve_no_pred" - * logic in gen_update_fp_context() needs to be updated to match. - * - * We do not include the effect of the ECI bits here -- they are - * tracked in other TB flags. This simplifies the logic for - * "when did we emit code that changes the MVE_NO_PRED TB flag - * and thus need to end the TB?". - */ - if (cpu_isar_feature(aa32_mve, env_archcpu(env))) { - return false; - } - if (env->v7m.vpr) { - return false; - } - if (env->v7m.ltpsize < 4) { - return false; - } - return true; -} - -void cpu_get_tb_cpu_state(CPUARMState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) -{ - CPUARMTBFlags flags; - - assert_hflags_rebuild_correctly(env); - flags = env->hflags; - - if (EX_TBFLAG_ANY(flags, AARCH64_STATE)) { - *pc = env->pc; - if (cpu_isar_feature(aa64_bti, env_archcpu(env))) { - DP_TBFLAG_A64(flags, BTYPE, env->btype); - } - } else { - *pc = env->regs[15]; - - if (arm_feature(env, ARM_FEATURE_M)) { - if (arm_feature(env, ARM_FEATURE_M_SECURITY) && - FIELD_EX32(env->v7m.fpccr[M_REG_S], V7M_FPCCR, S) - != env->v7m.secure) { - DP_TBFLAG_M32(flags, FPCCR_S_WRONG, 1); - } - - if ((env->v7m.fpccr[env->v7m.secure] & R_V7M_FPCCR_ASPEN_MASK) && - (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) || - (env->v7m.secure && - !(env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)))) { - /* - * ASPEN is set, but FPCA/SFPA indicate that there is no - * active FP context; we must create a new FP context before - * executing any FP insn. - */ - DP_TBFLAG_M32(flags, NEW_FP_CTXT_NEEDED, 1); - } - - bool is_secure = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK; - if (env->v7m.fpccr[is_secure] & R_V7M_FPCCR_LSPACT_MASK) { - DP_TBFLAG_M32(flags, LSPACT, 1); - } - - if (mve_no_pred(env)) { - DP_TBFLAG_M32(flags, MVE_NO_PRED, 1); - } - } else { - /* - * Note that XSCALE_CPAR shares bits with VECSTRIDE. - * Note that VECLEN+VECSTRIDE are RES0 for M-profile. - */ - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - DP_TBFLAG_A32(flags, XSCALE_CPAR, env->cp15.c15_cpar); - } else { - DP_TBFLAG_A32(flags, VECLEN, env->vfp.vec_len); - DP_TBFLAG_A32(flags, VECSTRIDE, env->vfp.vec_stride); - } - if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) { - DP_TBFLAG_A32(flags, VFPEN, 1); - } - } - - DP_TBFLAG_AM32(flags, THUMB, env->thumb); - DP_TBFLAG_AM32(flags, CONDEXEC, env->condexec_bits); - } - - /* - * The SS_ACTIVE and PSTATE_SS bits correspond to the state machine - * states defined in the ARM ARM for software singlestep: - * SS_ACTIVE PSTATE.SS State - * 0 x Inactive (the TB flag for SS is always 0) - * 1 0 Active-pending - * 1 1 Active-not-pending - * SS_ACTIVE is set in hflags; PSTATE__SS is computed every TB. - */ - if (EX_TBFLAG_ANY(flags, SS_ACTIVE) && (env->pstate & PSTATE_SS)) { - DP_TBFLAG_ANY(flags, PSTATE__SS, 1); - } - - *pflags = flags.flags; - *cs_base = flags.flags2; -} - -#ifdef TARGET_AARCH64 /* * The manual says that when SVE is enabled and VQ is widened the * implementation is allowed to zero the previously inaccessible @@ -12310,7 +11531,6 @@ void aarch64_sve_change_el(CPUARMState *env, int old_el, aarch64_sve_narrow_vq(env, new_len + 1); } } -#endif #ifndef CONFIG_USER_ONLY ARMSecuritySpace arm_security_space(CPUARMState *env) diff --git a/target/arm/helper.h b/target/arm/helper.h index 2b027333053..f340a49a28a 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -1,1046 +1,6 @@ -DEF_HELPER_FLAGS_1(sxtb16, TCG_CALL_NO_RWG_SE, i32, i32) -DEF_HELPER_FLAGS_1(uxtb16, TCG_CALL_NO_RWG_SE, i32, i32) +/* SPDX-License-Identifier: GPL-2.0-or-later */ -DEF_HELPER_3(add_setq, i32, env, i32, i32) -DEF_HELPER_3(add_saturate, i32, env, i32, i32) -DEF_HELPER_3(sub_saturate, i32, env, i32, i32) -DEF_HELPER_3(add_usaturate, i32, env, i32, i32) -DEF_HELPER_3(sub_usaturate, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(sdiv, TCG_CALL_NO_RWG, s32, env, s32, s32) -DEF_HELPER_FLAGS_3(udiv, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_1(rbit, TCG_CALL_NO_RWG_SE, i32, i32) - -#define PAS_OP(pfx) \ - DEF_HELPER_3(pfx ## add8, i32, i32, i32, ptr) \ - DEF_HELPER_3(pfx ## sub8, i32, i32, i32, ptr) \ - DEF_HELPER_3(pfx ## sub16, i32, i32, i32, ptr) \ - DEF_HELPER_3(pfx ## add16, i32, i32, i32, ptr) \ - DEF_HELPER_3(pfx ## addsubx, i32, i32, i32, ptr) \ - DEF_HELPER_3(pfx ## subaddx, i32, i32, i32, ptr) - -PAS_OP(s) -PAS_OP(u) -#undef PAS_OP - -#define PAS_OP(pfx) \ - DEF_HELPER_2(pfx ## add8, i32, i32, i32) \ - DEF_HELPER_2(pfx ## sub8, i32, i32, i32) \ - DEF_HELPER_2(pfx ## sub16, i32, i32, i32) \ - DEF_HELPER_2(pfx ## add16, i32, i32, i32) \ - DEF_HELPER_2(pfx ## addsubx, i32, i32, i32) \ - DEF_HELPER_2(pfx ## subaddx, i32, i32, i32) -PAS_OP(q) -PAS_OP(sh) -PAS_OP(uq) -PAS_OP(uh) -#undef PAS_OP - -DEF_HELPER_3(ssat, i32, env, i32, i32) -DEF_HELPER_3(usat, i32, env, i32, i32) -DEF_HELPER_3(ssat16, i32, env, i32, i32) -DEF_HELPER_3(usat16, i32, env, i32, i32) - -DEF_HELPER_FLAGS_2(usad8, TCG_CALL_NO_RWG_SE, i32, i32, i32) - -DEF_HELPER_FLAGS_3(sel_flags, TCG_CALL_NO_RWG_SE, - i32, i32, i32, i32) -DEF_HELPER_2(exception_internal, noreturn, env, i32) -DEF_HELPER_3(exception_with_syndrome, noreturn, env, i32, i32) -DEF_HELPER_4(exception_with_syndrome_el, noreturn, env, i32, i32, i32) -DEF_HELPER_2(exception_bkpt_insn, noreturn, env, i32) -DEF_HELPER_2(exception_swstep, noreturn, env, i32) -DEF_HELPER_2(exception_pc_alignment, noreturn, env, tl) -DEF_HELPER_1(setend, void, env) -DEF_HELPER_2(wfi, void, env, i32) -DEF_HELPER_1(wfe, void, env) -DEF_HELPER_1(yield, void, env) -DEF_HELPER_1(pre_hvc, void, env) -DEF_HELPER_2(pre_smc, void, env, i32) -DEF_HELPER_1(vesb, void, env) - -DEF_HELPER_3(cpsr_write, void, env, i32, i32) -DEF_HELPER_2(cpsr_write_eret, void, env, i32) -DEF_HELPER_1(cpsr_read, i32, env) - -DEF_HELPER_3(v7m_msr, void, env, i32, i32) -DEF_HELPER_2(v7m_mrs, i32, env, i32) - -DEF_HELPER_2(v7m_bxns, void, env, i32) -DEF_HELPER_2(v7m_blxns, void, env, i32) - -DEF_HELPER_3(v7m_tt, i32, env, i32, i32) - -DEF_HELPER_1(v7m_preserve_fp_state, void, env) - -DEF_HELPER_2(v7m_vlstm, void, env, i32) -DEF_HELPER_2(v7m_vlldm, void, env, i32) - -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, 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) - -DEF_HELPER_3(mrs_banked, i32, env, i32, i32) -DEF_HELPER_4(msr_banked, void, env, i32, i32, i32) - -DEF_HELPER_2(get_user_reg, i32, env, i32) -DEF_HELPER_3(set_user_reg, void, env, i32, i32) - -DEF_HELPER_FLAGS_1(rebuild_hflags_m32_newel, TCG_CALL_NO_RWG, void, env) -DEF_HELPER_FLAGS_2(rebuild_hflags_m32, TCG_CALL_NO_RWG, void, env, int) -DEF_HELPER_FLAGS_1(rebuild_hflags_a32_newel, TCG_CALL_NO_RWG, void, env) -DEF_HELPER_FLAGS_2(rebuild_hflags_a32, TCG_CALL_NO_RWG, void, env, int) -DEF_HELPER_FLAGS_2(rebuild_hflags_a64, TCG_CALL_NO_RWG, void, env, int) - -DEF_HELPER_FLAGS_5(probe_access, TCG_CALL_NO_WG, void, env, tl, i32, i32, i32) - -DEF_HELPER_1(vfp_get_fpscr, i32, env) -DEF_HELPER_2(vfp_set_fpscr, void, env, i32) - -DEF_HELPER_3(vfp_addh, f16, f16, f16, ptr) -DEF_HELPER_3(vfp_adds, f32, f32, f32, ptr) -DEF_HELPER_3(vfp_addd, f64, f64, f64, ptr) -DEF_HELPER_3(vfp_subh, f16, f16, f16, ptr) -DEF_HELPER_3(vfp_subs, f32, f32, f32, ptr) -DEF_HELPER_3(vfp_subd, f64, f64, f64, ptr) -DEF_HELPER_3(vfp_mulh, f16, f16, f16, ptr) -DEF_HELPER_3(vfp_muls, f32, f32, f32, ptr) -DEF_HELPER_3(vfp_muld, f64, f64, f64, ptr) -DEF_HELPER_3(vfp_divh, f16, f16, f16, ptr) -DEF_HELPER_3(vfp_divs, f32, f32, f32, ptr) -DEF_HELPER_3(vfp_divd, f64, f64, f64, ptr) -DEF_HELPER_3(vfp_maxh, f16, f16, f16, ptr) -DEF_HELPER_3(vfp_maxs, f32, f32, f32, ptr) -DEF_HELPER_3(vfp_maxd, f64, f64, f64, ptr) -DEF_HELPER_3(vfp_minh, f16, f16, f16, ptr) -DEF_HELPER_3(vfp_mins, f32, f32, f32, ptr) -DEF_HELPER_3(vfp_mind, f64, f64, f64, ptr) -DEF_HELPER_3(vfp_maxnumh, f16, f16, f16, ptr) -DEF_HELPER_3(vfp_maxnums, f32, f32, f32, ptr) -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) -DEF_HELPER_3(vfp_cmph, void, f16, f16, env) -DEF_HELPER_3(vfp_cmps, void, f32, f32, env) -DEF_HELPER_3(vfp_cmpd, void, f64, f64, env) -DEF_HELPER_3(vfp_cmpeh, void, f16, f16, env) -DEF_HELPER_3(vfp_cmpes, void, f32, f32, env) -DEF_HELPER_3(vfp_cmped, void, f64, f64, env) - -DEF_HELPER_2(vfp_fcvtds, f64, f32, env) -DEF_HELPER_2(vfp_fcvtsd, f32, f64, env) -DEF_HELPER_FLAGS_2(bfcvt, TCG_CALL_NO_RWG, i32, f32, ptr) -DEF_HELPER_FLAGS_2(bfcvt_pair, TCG_CALL_NO_RWG, i32, i64, ptr) - -DEF_HELPER_2(vfp_uitoh, f16, i32, ptr) -DEF_HELPER_2(vfp_uitos, f32, i32, ptr) -DEF_HELPER_2(vfp_uitod, f64, i32, ptr) -DEF_HELPER_2(vfp_sitoh, f16, i32, ptr) -DEF_HELPER_2(vfp_sitos, f32, i32, ptr) -DEF_HELPER_2(vfp_sitod, f64, i32, ptr) - -DEF_HELPER_2(vfp_touih, i32, f16, ptr) -DEF_HELPER_2(vfp_touis, i32, f32, ptr) -DEF_HELPER_2(vfp_touid, i32, f64, ptr) -DEF_HELPER_2(vfp_touizh, i32, f16, ptr) -DEF_HELPER_2(vfp_touizs, i32, f32, ptr) -DEF_HELPER_2(vfp_touizd, i32, f64, ptr) -DEF_HELPER_2(vfp_tosih, s32, f16, ptr) -DEF_HELPER_2(vfp_tosis, s32, f32, ptr) -DEF_HELPER_2(vfp_tosid, s32, f64, ptr) -DEF_HELPER_2(vfp_tosizh, s32, f16, ptr) -DEF_HELPER_2(vfp_tosizs, s32, f32, ptr) -DEF_HELPER_2(vfp_tosizd, s32, f64, ptr) - -DEF_HELPER_3(vfp_toshh_round_to_zero, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_toslh_round_to_zero, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_touhh_round_to_zero, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_toulh_round_to_zero, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_toshs_round_to_zero, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_tosls_round_to_zero, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_touhs_round_to_zero, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_touls_round_to_zero, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_toshd_round_to_zero, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_tosld_round_to_zero, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_touhd_round_to_zero, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_tould_round_to_zero, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_touhh, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_toshh, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_toulh, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_toslh, i32, f16, i32, ptr) -DEF_HELPER_3(vfp_touqh, i64, f16, i32, ptr) -DEF_HELPER_3(vfp_tosqh, i64, f16, i32, ptr) -DEF_HELPER_3(vfp_toshs, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_tosls, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_tosqs, i64, f32, i32, ptr) -DEF_HELPER_3(vfp_touhs, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_touls, i32, f32, i32, ptr) -DEF_HELPER_3(vfp_touqs, i64, f32, i32, ptr) -DEF_HELPER_3(vfp_toshd, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_tosld, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_tosqd, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_touhd, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_tould, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_touqd, i64, f64, i32, ptr) -DEF_HELPER_3(vfp_shtos, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_sltos, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_sqtos, f32, i64, i32, ptr) -DEF_HELPER_3(vfp_uhtos, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_ultos, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_uqtos, f32, i64, i32, ptr) -DEF_HELPER_3(vfp_shtod, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_sltod, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_sqtod, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_uhtod, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_ultod, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_uqtod, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_shtoh, f16, i32, i32, ptr) -DEF_HELPER_3(vfp_uhtoh, f16, i32, i32, ptr) -DEF_HELPER_3(vfp_sltoh, f16, i32, i32, ptr) -DEF_HELPER_3(vfp_ultoh, f16, i32, i32, ptr) -DEF_HELPER_3(vfp_sqtoh, f16, i64, i32, ptr) -DEF_HELPER_3(vfp_uqtoh, f16, i64, i32, ptr) - -DEF_HELPER_3(vfp_shtos_round_to_nearest, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_sltos_round_to_nearest, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_uhtos_round_to_nearest, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_ultos_round_to_nearest, f32, i32, i32, ptr) -DEF_HELPER_3(vfp_shtod_round_to_nearest, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_sltod_round_to_nearest, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_uhtod_round_to_nearest, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_ultod_round_to_nearest, f64, i64, i32, ptr) -DEF_HELPER_3(vfp_shtoh_round_to_nearest, f16, i32, i32, ptr) -DEF_HELPER_3(vfp_uhtoh_round_to_nearest, f16, i32, i32, ptr) -DEF_HELPER_3(vfp_sltoh_round_to_nearest, f16, i32, i32, ptr) -DEF_HELPER_3(vfp_ultoh_round_to_nearest, f16, i32, i32, ptr) - -DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, ptr) - -DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f32, TCG_CALL_NO_RWG, f32, f16, ptr, i32) -DEF_HELPER_FLAGS_3(vfp_fcvt_f32_to_f16, TCG_CALL_NO_RWG, f16, f32, ptr, i32) -DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f64, TCG_CALL_NO_RWG, f64, f16, ptr, i32) -DEF_HELPER_FLAGS_3(vfp_fcvt_f64_to_f16, TCG_CALL_NO_RWG, f16, f64, ptr, i32) - -DEF_HELPER_4(vfp_muladdd, f64, f64, f64, f64, ptr) -DEF_HELPER_4(vfp_muladds, f32, f32, f32, f32, ptr) -DEF_HELPER_4(vfp_muladdh, f16, f16, f16, f16, ptr) - -DEF_HELPER_FLAGS_2(recpe_f16, TCG_CALL_NO_RWG, f16, f16, ptr) -DEF_HELPER_FLAGS_2(recpe_f32, TCG_CALL_NO_RWG, f32, f32, ptr) -DEF_HELPER_FLAGS_2(recpe_f64, TCG_CALL_NO_RWG, f64, f64, ptr) -DEF_HELPER_FLAGS_2(rsqrte_f16, TCG_CALL_NO_RWG, f16, f16, ptr) -DEF_HELPER_FLAGS_2(rsqrte_f32, TCG_CALL_NO_RWG, f32, f32, ptr) -DEF_HELPER_FLAGS_2(rsqrte_f64, TCG_CALL_NO_RWG, f64, f64, ptr) -DEF_HELPER_FLAGS_1(recpe_u32, TCG_CALL_NO_RWG, i32, i32) -DEF_HELPER_FLAGS_1(rsqrte_u32, TCG_CALL_NO_RWG, i32, i32) -DEF_HELPER_FLAGS_4(neon_tbl, TCG_CALL_NO_RWG, i64, env, i32, i64, i64) - -DEF_HELPER_3(shl_cc, i32, env, i32, i32) -DEF_HELPER_3(shr_cc, i32, env, i32, i32) -DEF_HELPER_3(sar_cc, i32, env, i32, i32) -DEF_HELPER_3(ror_cc, i32, env, i32, i32) - -DEF_HELPER_FLAGS_2(rinth_exact, TCG_CALL_NO_RWG, f16, f16, ptr) -DEF_HELPER_FLAGS_2(rints_exact, TCG_CALL_NO_RWG, f32, f32, ptr) -DEF_HELPER_FLAGS_2(rintd_exact, TCG_CALL_NO_RWG, f64, f64, ptr) -DEF_HELPER_FLAGS_2(rinth, TCG_CALL_NO_RWG, f16, f16, ptr) -DEF_HELPER_FLAGS_2(rints, TCG_CALL_NO_RWG, f32, f32, ptr) -DEF_HELPER_FLAGS_2(rintd, TCG_CALL_NO_RWG, f64, f64, ptr) - -DEF_HELPER_FLAGS_2(vjcvt, TCG_CALL_NO_RWG, i32, f64, env) -DEF_HELPER_FLAGS_2(fjcvtzs, TCG_CALL_NO_RWG, i64, f64, ptr) - -DEF_HELPER_FLAGS_3(check_hcr_el2_trap, TCG_CALL_NO_WG, void, env, i32, i32) - -/* neon_helper.c */ -DEF_HELPER_FLAGS_3(neon_qadd_u8, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_qadd_s8, TCG_CALL_NO_RWG, i32, env, i32, i32) -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) -DEF_HELPER_2(neon_pmin_s16, i32, i32, i32) -DEF_HELPER_2(neon_pmax_u8, i32, i32, i32) -DEF_HELPER_2(neon_pmax_s8, i32, i32, i32) -DEF_HELPER_2(neon_pmax_u16, i32, i32, i32) -DEF_HELPER_2(neon_pmax_s16, i32, i32, i32) - -DEF_HELPER_2(neon_shl_u16, i32, i32, i32) -DEF_HELPER_2(neon_shl_s16, i32, i32, i32) -DEF_HELPER_2(neon_rshl_u8, i32, i32, i32) -DEF_HELPER_2(neon_rshl_s8, i32, i32, i32) -DEF_HELPER_2(neon_rshl_u16, i32, i32, i32) -DEF_HELPER_2(neon_rshl_s16, i32, i32, i32) -DEF_HELPER_2(neon_rshl_u32, i32, i32, i32) -DEF_HELPER_2(neon_rshl_s32, i32, i32, i32) -DEF_HELPER_2(neon_rshl_u64, i64, i64, i64) -DEF_HELPER_2(neon_rshl_s64, i64, i64, i64) -DEF_HELPER_3(neon_qshl_u8, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_s8, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_u16, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_s16, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_u32, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_s32, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_u64, i64, env, i64, i64) -DEF_HELPER_3(neon_qshl_s64, i64, env, i64, i64) -DEF_HELPER_3(neon_qshlu_s8, i32, env, i32, i32) -DEF_HELPER_3(neon_qshlu_s16, i32, env, i32, i32) -DEF_HELPER_3(neon_qshlu_s32, i32, env, i32, i32) -DEF_HELPER_3(neon_qshlu_s64, i64, env, i64, i64) -DEF_HELPER_3(neon_qrshl_u8, i32, env, i32, i32) -DEF_HELPER_3(neon_qrshl_s8, i32, env, i32, i32) -DEF_HELPER_3(neon_qrshl_u16, i32, env, i32, i32) -DEF_HELPER_3(neon_qrshl_s16, i32, env, i32, i32) -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_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) -DEF_HELPER_2(neon_mul_u16, i32, i32, i32) - -DEF_HELPER_2(neon_tst_u8, i32, i32, i32) -DEF_HELPER_2(neon_tst_u16, i32, i32, i32) -DEF_HELPER_2(neon_tst_u32, i32, i32, i32) - -DEF_HELPER_1(neon_clz_u8, i32, i32) -DEF_HELPER_1(neon_clz_u16, i32, i32) -DEF_HELPER_1(neon_cls_s8, i32, i32) -DEF_HELPER_1(neon_cls_s16, i32, i32) -DEF_HELPER_1(neon_cls_s32, i32, i32) -DEF_HELPER_1(neon_cnt_u8, i32, i32) -DEF_HELPER_FLAGS_1(neon_rbit_u8, TCG_CALL_NO_RWG_SE, i32, i32) - -DEF_HELPER_3(neon_qdmulh_s16, i32, env, i32, i32) -DEF_HELPER_3(neon_qrdmulh_s16, i32, env, i32, i32) -DEF_HELPER_4(neon_qrdmlah_s16, i32, env, i32, i32, i32) -DEF_HELPER_4(neon_qrdmlsh_s16, i32, env, i32, i32, i32) -DEF_HELPER_3(neon_qdmulh_s32, i32, env, i32, i32) -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_high_u8, i32, i64) -DEF_HELPER_1(neon_narrow_high_u16, i32, i64) -DEF_HELPER_1(neon_narrow_round_high_u8, i32, i64) -DEF_HELPER_1(neon_narrow_round_high_u16, i32, i64) -DEF_HELPER_1(neon_widen_u8, i64, i32) -DEF_HELPER_1(neon_widen_s8, i64, i32) -DEF_HELPER_1(neon_widen_u16, i64, i32) -DEF_HELPER_1(neon_widen_s16, i64, i32) - -DEF_HELPER_2(neon_addl_u16, i64, i64, i64) -DEF_HELPER_2(neon_addl_u32, i64, i64, i64) -DEF_HELPER_2(neon_paddl_u16, i64, i64, i64) -DEF_HELPER_2(neon_paddl_u32, i64, i64, i64) -DEF_HELPER_2(neon_subl_u16, i64, i64, i64) -DEF_HELPER_2(neon_subl_u32, i64, i64, i64) -DEF_HELPER_3(neon_addl_saturate_s32, i64, env, i64, i64) -DEF_HELPER_3(neon_addl_saturate_s64, i64, env, i64, i64) -DEF_HELPER_2(neon_abdl_u16, i64, i32, i32) -DEF_HELPER_2(neon_abdl_s16, i64, i32, i32) -DEF_HELPER_2(neon_abdl_u32, i64, i32, i32) -DEF_HELPER_2(neon_abdl_s32, i64, i32, i32) -DEF_HELPER_2(neon_abdl_u64, i64, i32, i32) -DEF_HELPER_2(neon_abdl_s64, i64, i32, i32) -DEF_HELPER_2(neon_mull_u8, i64, i32, i32) -DEF_HELPER_2(neon_mull_s8, i64, i32, i32) -DEF_HELPER_2(neon_mull_u16, i64, i32, i32) -DEF_HELPER_2(neon_mull_s16, i64, i32, i32) - -DEF_HELPER_1(neon_negl_u16, i64, i64) -DEF_HELPER_1(neon_negl_u32, i64, i64) - -DEF_HELPER_FLAGS_2(neon_qabs_s8, TCG_CALL_NO_RWG, i32, env, i32) -DEF_HELPER_FLAGS_2(neon_qabs_s16, TCG_CALL_NO_RWG, i32, env, i32) -DEF_HELPER_FLAGS_2(neon_qabs_s32, TCG_CALL_NO_RWG, i32, env, i32) -DEF_HELPER_FLAGS_2(neon_qabs_s64, TCG_CALL_NO_RWG, i64, env, i64) -DEF_HELPER_FLAGS_2(neon_qneg_s8, TCG_CALL_NO_RWG, i32, env, i32) -DEF_HELPER_FLAGS_2(neon_qneg_s16, TCG_CALL_NO_RWG, i32, env, i32) -DEF_HELPER_FLAGS_2(neon_qneg_s32, TCG_CALL_NO_RWG, i32, env, i32) -DEF_HELPER_FLAGS_2(neon_qneg_s64, TCG_CALL_NO_RWG, i64, env, i64) - -DEF_HELPER_3(neon_ceq_f32, i32, i32, i32, ptr) -DEF_HELPER_3(neon_cge_f32, i32, i32, i32, ptr) -DEF_HELPER_3(neon_cgt_f32, i32, i32, i32, ptr) -DEF_HELPER_3(neon_acge_f32, i32, i32, i32, ptr) -DEF_HELPER_3(neon_acgt_f32, i32, i32, i32, ptr) -DEF_HELPER_3(neon_acge_f64, i64, i64, i64, ptr) -DEF_HELPER_3(neon_acgt_f64, i64, i64, i64, ptr) - -/* iwmmxt_helper.c */ -DEF_HELPER_2(iwmmxt_maddsq, i64, i64, i64) -DEF_HELPER_2(iwmmxt_madduq, i64, i64, i64) -DEF_HELPER_2(iwmmxt_sadb, i64, i64, i64) -DEF_HELPER_2(iwmmxt_sadw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_mulslw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_mulshw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_mululw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_muluhw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_macsw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_macuw, i64, i64, i64) -DEF_HELPER_1(iwmmxt_setpsr_nz, i32, i64) - -#define DEF_IWMMXT_HELPER_SIZE_ENV(name) \ -DEF_HELPER_3(iwmmxt_##name##b, i64, env, i64, i64) \ -DEF_HELPER_3(iwmmxt_##name##w, i64, env, i64, i64) \ -DEF_HELPER_3(iwmmxt_##name##l, i64, env, i64, i64) \ - -DEF_IWMMXT_HELPER_SIZE_ENV(unpackl) -DEF_IWMMXT_HELPER_SIZE_ENV(unpackh) - -DEF_HELPER_2(iwmmxt_unpacklub, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackluw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklul, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhub, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhuw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhul, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklsb, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklsw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklsl, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhsb, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhsw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhsl, i64, env, i64) - -DEF_IWMMXT_HELPER_SIZE_ENV(cmpeq) -DEF_IWMMXT_HELPER_SIZE_ENV(cmpgtu) -DEF_IWMMXT_HELPER_SIZE_ENV(cmpgts) - -DEF_IWMMXT_HELPER_SIZE_ENV(mins) -DEF_IWMMXT_HELPER_SIZE_ENV(minu) -DEF_IWMMXT_HELPER_SIZE_ENV(maxs) -DEF_IWMMXT_HELPER_SIZE_ENV(maxu) - -DEF_IWMMXT_HELPER_SIZE_ENV(subn) -DEF_IWMMXT_HELPER_SIZE_ENV(addn) -DEF_IWMMXT_HELPER_SIZE_ENV(subu) -DEF_IWMMXT_HELPER_SIZE_ENV(addu) -DEF_IWMMXT_HELPER_SIZE_ENV(subs) -DEF_IWMMXT_HELPER_SIZE_ENV(adds) - -DEF_HELPER_3(iwmmxt_avgb0, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_avgb1, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_avgw0, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_avgw1, i64, env, i64, i64) - -DEF_HELPER_3(iwmmxt_align, i64, i64, i64, i32) -DEF_HELPER_4(iwmmxt_insr, i64, i64, i32, i32, i32) - -DEF_HELPER_1(iwmmxt_bcstb, i64, i32) -DEF_HELPER_1(iwmmxt_bcstw, i64, i32) -DEF_HELPER_1(iwmmxt_bcstl, i64, i32) - -DEF_HELPER_1(iwmmxt_addcb, i64, i64) -DEF_HELPER_1(iwmmxt_addcw, i64, i64) -DEF_HELPER_1(iwmmxt_addcl, i64, i64) - -DEF_HELPER_1(iwmmxt_msbb, i32, i64) -DEF_HELPER_1(iwmmxt_msbw, i32, i64) -DEF_HELPER_1(iwmmxt_msbl, i32, i64) - -DEF_HELPER_3(iwmmxt_srlw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_srll, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_srlq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sllw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_slll, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sllq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sraw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sral, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sraq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_rorw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_rorl, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_rorq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_shufh, i64, env, i64, i32) - -DEF_HELPER_3(iwmmxt_packuw, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packul, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packuq, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packsw, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packsl, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packsq, i64, env, i64, i64) - -DEF_HELPER_3(iwmmxt_muladdsl, i64, i64, i32, i32) -DEF_HELPER_3(iwmmxt_muladdsw, i64, i64, i32, i32) -DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32) - -DEF_HELPER_FLAGS_2(neon_unzip8, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_unzip16, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_qunzip8, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_qunzip16, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_qunzip32, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_zip8, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_zip16, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_qzip8, TCG_CALL_NO_RWG, void, ptr, ptr) -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) -DEF_HELPER_FLAGS_4(crypto_sha1p, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sha1m, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(crypto_sha1h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(crypto_sha1su1, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(crypto_sha256h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sha256h2, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(crypto_sha256su0, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sha256su1, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(crypto_sha512h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sha512h2, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(crypto_sha512su0, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sha512su1, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(crypto_sm3tt1a, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sm3tt1b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sm3tt2a, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sm3tt2b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sm3partw1, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sm3partw2, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(crypto_sm4e, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sm4ekey, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(crypto_rax1, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) -DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) - -DEF_HELPER_FLAGS_5(gvec_qrdmlah_s16, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_qrdmlsh_s16, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_qrdmlah_s32, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_qrdmlsh_s32, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(sve2_sqrdmlah_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2_sqrdmlah_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2_sqrdmlah_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2_sqrdmlah_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_sdot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_udot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sdot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_udot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_usdot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_sdot_idx_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_udot_idx_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sdot_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_udot_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sudot_idx_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_usdot_idx_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_fcaddh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fcadds, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fcaddd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_6(gvec_fcmlah, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_6(gvec_fcmlah_idx, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_6(gvec_fcmlas, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_6(gvec_fcmlas_idx, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) -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) -DEF_HELPER_FLAGS_4(gvec_uitos, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_tosszh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_tosizs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_touszh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_touizs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_vcvt_sf, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_uf, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_fs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_fu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_vcvt_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_uh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_hs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ss, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_us, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_uh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_vrint_rm_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vrint_rm_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_vrintx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_vrintx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_frecpe_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_frecpe_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_frecpe_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_frsqrte_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_frsqrte_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_frsqrte_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_fcgt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_fcgt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_fcge0_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_fcge0_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_fceq0_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_fceq0_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_fcle0_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_fcle0_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_fclt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_fclt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_fsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_fmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmul_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -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_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_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_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_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_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_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_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_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_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_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) - -DEF_HELPER_FLAGS_5(gvec_rsqrts_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_rsqrts_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_fmla_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmla_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_fmls_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -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_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_ftsmul_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_ftsmul_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_ftsmul_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_fmul_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmul_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmul_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_fmla_nf_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmla_nf_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_fmls_nf_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmls_nf_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_6(gvec_fmla_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_6(gvec_fmla_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_6(gvec_fmla_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_uqadd_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uqadd_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uqadd_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uqadd_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sqadd_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sqadd_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sqadd_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sqadd_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uqsub_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uqsub_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uqsub_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uqsub_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sqsub_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sqsub_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -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_fmlal_a32, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmlal_a64, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a32, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a64, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_2(frint32_s, TCG_CALL_NO_RWG, f32, f32, ptr) -DEF_HELPER_FLAGS_2(frint64_s, TCG_CALL_NO_RWG, f32, f32, ptr) -DEF_HELPER_FLAGS_2(frint32_d, TCG_CALL_NO_RWG, f64, f64, ptr) -DEF_HELPER_FLAGS_2(frint64_d, TCG_CALL_NO_RWG, f64, f64, ptr) - -DEF_HELPER_FLAGS_3(gvec_ceq0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_ceq0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_clt0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_clt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_cle0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_cle0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_cgt0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_cgt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_cge0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_cge0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_smulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_smulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_smulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_smulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_umulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_umulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_umulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_umulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_sshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_sshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_ushl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_ushl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_pmul_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_pmull_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(neon_pmull_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_ssra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_ssra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_ssra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_ssra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_usra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_usra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_usra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_usra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_srshr_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_srshr_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_srshr_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_srshr_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_urshr_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_urshr_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_urshr_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_urshr_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_srsra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_srsra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_srsra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_srsra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_ursra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_ursra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_ursra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_ursra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_sri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_sri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_sri_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_sri_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_sli_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_sli_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_sli_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_sli_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_sabd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_sabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_sabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_sabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_uabd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_uabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_uabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_uabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_saba_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_saba_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_saba_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_saba_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_uaba_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_uaba_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_uaba_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_uaba_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_mul_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_mul_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_mul_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_mla_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_mla_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_mla_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_mls_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_mls_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_mls_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(neon_sqdmulh_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_sqdmulh_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(neon_sqrdmulh_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_sqrdmulh_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) -DEF_HELPER_FLAGS_4(sve2_sqdmulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(sve2_sqrdmulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqrdmulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqrdmulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqrdmulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(sve2_sqdmulh_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqdmulh_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqdmulh_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(sve2_sqrdmulh_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqrdmulh_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqrdmulh_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_6(sve2_fmlal_zzzw_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_6(sve2_fmlal_zzxw_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_xar_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_smmla_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_ummla_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -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_5(gvec_bfmmla, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_6(gvec_bfmlal, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_6(gvec_bfmlal_idx, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_sclamp_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sclamp_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sclamp_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sclamp_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_uclamp_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uclamp_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uclamp_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uclamp_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) +#include "tcg/helper.h" #ifdef TARGET_AARCH64 #include "tcg/helper-a64.h" diff --git a/target/arm/hvf-stub.c b/target/arm/hvf-stub.c new file mode 100644 index 00000000000..ff137267a03 --- /dev/null +++ b/target/arm/hvf-stub.c @@ -0,0 +1,20 @@ +/* + * QEMU Hypervisor.framework (HVF) stubs for ARM + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hvf_arm.h" + +uint32_t hvf_arm_get_default_ipa_bit_size(void) +{ + g_assert_not_reached(); +} + +uint32_t hvf_arm_get_max_ipa_bit_size(void) +{ + g_assert_not_reached(); +} diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 546c0e817f4..42258cc2d88 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -11,32 +11,37 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "qemu/log.h" -#include "sysemu/runstate.h" -#include "sysemu/hvf.h" -#include "sysemu/hvf_int.h" -#include "sysemu/hw_accel.h" +#include "system/runstate.h" +#include "system/hvf.h" +#include "system/hvf_int.h" +#include "system/hw_accel.h" #include "hvf_arm.h" #include "cpregs.h" #include -#include "exec/address-spaces.h" +#include "system/address-spaces.h" +#include "system/memory.h" +#include "hw/boards.h" #include "hw/irq.h" #include "qemu/main-loop.h" -#include "sysemu/cpus.h" +#include "system/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 "exec/gdbstub.h" +#include "gdbstub/enums.h" #define MDSCR_EL1_SS_SHIFT 0 #define MDSCR_EL1_MDE_SHIFT 15 -static uint16_t dbgbcr_regs[] = { +static const uint16_t dbgbcr_regs[] = { HV_SYS_REG_DBGBCR0_EL1, HV_SYS_REG_DBGBCR1_EL1, HV_SYS_REG_DBGBCR2_EL1, @@ -54,7 +59,8 @@ static uint16_t dbgbcr_regs[] = { HV_SYS_REG_DBGBCR14_EL1, HV_SYS_REG_DBGBCR15_EL1, }; -static uint16_t dbgbvr_regs[] = { + +static const uint16_t dbgbvr_regs[] = { HV_SYS_REG_DBGBVR0_EL1, HV_SYS_REG_DBGBVR1_EL1, HV_SYS_REG_DBGBVR2_EL1, @@ -72,7 +78,8 @@ static uint16_t dbgbvr_regs[] = { HV_SYS_REG_DBGBVR14_EL1, HV_SYS_REG_DBGBVR15_EL1, }; -static uint16_t dbgwcr_regs[] = { + +static const uint16_t dbgwcr_regs[] = { HV_SYS_REG_DBGWCR0_EL1, HV_SYS_REG_DBGWCR1_EL1, HV_SYS_REG_DBGWCR2_EL1, @@ -90,7 +97,8 @@ static uint16_t dbgwcr_regs[] = { HV_SYS_REG_DBGWCR14_EL1, HV_SYS_REG_DBGWCR15_EL1, }; -static uint16_t dbgwvr_regs[] = { + +static const uint16_t dbgwvr_regs[] = { HV_SYS_REG_DBGWVR0_EL1, HV_SYS_REG_DBGWVR1_EL1, HV_SYS_REG_DBGWVR2_EL1, @@ -145,7 +153,6 @@ void hvf_arm_init_debug(void) #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 @@ -179,6 +186,7 @@ void hvf_arm_init_debug(void) #define SYSREG_OSLSR_EL1 SYSREG(2, 0, 1, 1, 4) #define SYSREG_OSDLR_EL1 SYSREG(2, 0, 1, 3, 4) #define SYSREG_CNTPCT_EL0 SYSREG(3, 3, 14, 0, 1) +#define SYSREG_CNTP_CTL_EL0 SYSREG(3, 3, 14, 2, 1) #define SYSREG_PMCR_EL0 SYSREG(3, 3, 9, 12, 0) #define SYSREG_PMUSERENR_EL0 SYSREG(3, 3, 9, 14, 0) #define SYSREG_PMCNTENSET_EL0 SYSREG(3, 3, 9, 12, 1) @@ -293,6 +301,8 @@ void hvf_arm_init_debug(void) 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; @@ -392,85 +402,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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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 /* @@ -482,7 +492,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) }, @@ -493,6 +503,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) }, @@ -801,9 +812,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; } } @@ -834,6 +845,16 @@ static uint64_t hvf_get_reg(CPUState *cpu, int rt) 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 = {}; @@ -851,6 +872,7 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) { 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; @@ -876,6 +898,20 @@ 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); + + /* + * Disable SME, which is not properly handled by QEMU hvf yet. + * To allow this through we would need to: + * - make sure that the SME state is correctly handled in the + * get_registers/put_registers functions + * - get the SME-specific CPU properties to work with accelerators + * other than TCG + * - fix any assumptions we made that SME implies SVE (since + * on the M4 there is SME but not SVE) + */ + host_isar.id_aa64pfr1 &= ~R_ID_AA64PFR1_SME_MASK; + ahcf->isar = host_isar; /* @@ -898,6 +934,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) { @@ -923,6 +983,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); @@ -989,6 +1068,11 @@ int hvf_arch_init_vcpu(CPUState *cpu) &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; } @@ -1013,7 +1097,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); } @@ -1042,7 +1126,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: @@ -1193,57 +1277,61 @@ static bool hvf_sysreg_read_cp(CPUState *cpu, uint32_t reg, uint64_t *val) return false; } -static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) +static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val) { ARMCPU *arm_cpu = ARM_CPU(cpu); CPUARMState *env = &arm_cpu->env; - uint64_t val = 0; + + 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 */ - break; + return 0; case SYSREG_ICC_AP0R0_EL1: case SYSREG_ICC_AP0R1_EL1: case SYSREG_ICC_AP0R2_EL1: @@ -1270,8 +1358,8 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) case SYSREG_ICC_SRE_EL1: case SYSREG_ICC_CTLR_EL1: /* Call the TCG sysreg handler. This is only safe for GICv3 regs. */ - if (!hvf_sysreg_read_cp(cpu, reg, &val)) { - hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + if (hvf_sysreg_read_cp(cpu, reg, val)) { + return 0; } break; case SYSREG_DBGBVR0_EL1: @@ -1290,8 +1378,8 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) case SYSREG_DBGBVR13_EL1: case SYSREG_DBGBVR14_EL1: case SYSREG_DBGBVR15_EL1: - val = env->cp15.dbgbvr[SYSREG_CRM(reg)]; - break; + *val = env->cp15.dbgbvr[SYSREG_CRM(reg)]; + return 0; case SYSREG_DBGBCR0_EL1: case SYSREG_DBGBCR1_EL1: case SYSREG_DBGBCR2_EL1: @@ -1308,8 +1396,8 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) case SYSREG_DBGBCR13_EL1: case SYSREG_DBGBCR14_EL1: case SYSREG_DBGBCR15_EL1: - val = env->cp15.dbgbcr[SYSREG_CRM(reg)]; - break; + *val = env->cp15.dbgbcr[SYSREG_CRM(reg)]; + return 0; case SYSREG_DBGWVR0_EL1: case SYSREG_DBGWVR1_EL1: case SYSREG_DBGWVR2_EL1: @@ -1326,8 +1414,8 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) case SYSREG_DBGWVR13_EL1: case SYSREG_DBGWVR14_EL1: case SYSREG_DBGWVR15_EL1: - val = env->cp15.dbgwvr[SYSREG_CRM(reg)]; - break; + *val = env->cp15.dbgwvr[SYSREG_CRM(reg)]; + return 0; case SYSREG_DBGWCR0_EL1: case SYSREG_DBGWCR1_EL1: case SYSREG_DBGWCR2_EL1: @@ -1344,35 +1432,25 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) case SYSREG_DBGWCR13_EL1: case SYSREG_DBGWCR14_EL1: case SYSREG_DBGWCR15_EL1: - val = env->cp15.dbgwcr[SYSREG_CRM(reg)]; - break; + *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) @@ -1491,70 +1569,82 @@ 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 (val & PMCRC) { - /* The counter has been reset */ - env->cp15.c15_ccnt = 0; - } + 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 & 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); + 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; + } + } - 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; + switch (reg) { case SYSREG_OSLAR_EL1: env->cp15.oslsr_el1 = val & 1; - break; + return 0; + case SYSREG_CNTP_CTL_EL0: + /* + * Guests should not rely on the physical counter, but macOS emits + * disable writes to it. Let it do so, but ignore the requests. + */ + qemu_log_mask(LOG_UNIMP, "Unsupported write to CNTP_CTL_EL0\n"); + return 0; case SYSREG_OSDLR_EL1: /* Dummy register */ - break; + return 0; case SYSREG_ICC_AP0R0_EL1: case SYSREG_ICC_AP0R1_EL1: case SYSREG_ICC_AP0R2_EL1: @@ -1581,13 +1671,13 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_ICC_SGI1R_EL1: case SYSREG_ICC_SRE_EL1: /* Call the TCG sysreg handler. This is only safe for GICv3 regs. */ - if (!hvf_sysreg_write_cp(cpu, reg, val)) { - hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + if (hvf_sysreg_write_cp(cpu, reg, val)) { + return 0; } break; case SYSREG_MDSCR_EL1: env->cp15.mdscr_el1 = val; - break; + return 0; case SYSREG_DBGBVR0_EL1: case SYSREG_DBGBVR1_EL1: case SYSREG_DBGBVR2_EL1: @@ -1605,7 +1695,7 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_DBGBVR14_EL1: case SYSREG_DBGBVR15_EL1: env->cp15.dbgbvr[SYSREG_CRM(reg)] = val; - break; + return 0; case SYSREG_DBGBCR0_EL1: case SYSREG_DBGBCR1_EL1: case SYSREG_DBGBCR2_EL1: @@ -1623,7 +1713,7 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_DBGBCR14_EL1: case SYSREG_DBGBCR15_EL1: env->cp15.dbgbcr[SYSREG_CRM(reg)] = val; - break; + return 0; case SYSREG_DBGWVR0_EL1: case SYSREG_DBGWVR1_EL1: case SYSREG_DBGWVR2_EL1: @@ -1641,7 +1731,7 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_DBGWVR14_EL1: case SYSREG_DBGWVR15_EL1: env->cp15.dbgwvr[SYSREG_CRM(reg)] = val; - break; + return 0; case SYSREG_DBGWCR0_EL1: case SYSREG_DBGWCR1_EL1: case SYSREG_DBGWCR2_EL1: @@ -1659,20 +1749,18 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_DBGWCR14_EL1: case SYSREG_DBGWCR15_EL1: env->cp15.dbgwcr[SYSREG_CRM(reg)] = val; - break; - default: - cpu_synchronize_state(cpu); - trace_hvf_unhandled_sysreg_write(env->pc, reg, - 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; } - 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) @@ -1718,9 +1806,9 @@ static void hvf_wait_for_ipi(CPUState *cpu, struct timespec *ts) * sleeping. */ qatomic_set_mb(&cpu->thread_kicked, false); - qemu_mutex_unlock_iothread(); + bql_unlock(); pselect(0, 0, 0, 0, ts, &cpu->accel->unblock_ipi_mask); - qemu_mutex_lock_iothread(); + bql_lock(); } static void hvf_wfi(CPUState *cpu) @@ -1821,7 +1909,7 @@ int hvf_vcpu_exec(CPUState *cpu) flush_cpu_state(cpu); - qemu_mutex_unlock_iothread(); + bql_unlock(); assert_hvf_ok(hv_vcpu_run(cpu->accel->fd)); /* handle VMEXIT */ @@ -1830,7 +1918,7 @@ int hvf_vcpu_exec(CPUState *cpu) uint32_t ec = syn_get_ec(syndrome); ret = 0; - qemu_mutex_lock_iothread(); + bql_lock(); switch (exit_reason) { case HV_EXIT_REASON_EXCEPTION: /* This is the main one, handle below. */ @@ -1896,6 +1984,7 @@ int hvf_vcpu_exec(CPUState *cpu) bool isv = syndrome & ARM_EL_ISV; bool iswrite = (syndrome >> 6) & 1; bool s1ptw = (syndrome >> 7) & 1; + bool sse = (syndrome >> 21) & 1; uint32_t sas = (syndrome >> 22) & 3; uint32_t len = 1 << sas; uint32_t srt = (syndrome >> 16) & 0x1f; @@ -1923,6 +2012,9 @@ int hvf_vcpu_exec(CPUState *cpu) address_space_read(&address_space_memory, hvf_exit->exception.physical_address, MEMTXATTRS_UNSPECIFIED, &val, len); + if (sse) { + val = sextract64(val, 0, len * 8); + } hvf_set_reg(cpu, srt, val); } @@ -1934,16 +2026,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: @@ -2010,7 +2112,7 @@ 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() }, @@ -2176,28 +2278,23 @@ 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) +static void hvf_arch_set_traps(CPUState *cpu) { - 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); + should_enable_traps |= cpu->accel->guest_debug_enabled; + /* 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); - } + /* 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) @@ -2238,10 +2335,10 @@ void hvf_arch_update_guest_debug(CPUState *cpu) deposit64(env->cp15.mdscr_el1, MDSCR_EL1_MDE_SHIFT, 1, 0); } - hvf_arch_set_traps(); + hvf_arch_set_traps(cpu); } -inline bool hvf_arch_supports_guest_debug(void) +bool hvf_arch_supports_guest_debug(void) { return true; } diff --git a/target/arm/hvf/trace.h b/target/arm/hvf/trace.h new file mode 100644 index 00000000000..04a19c1d752 --- /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 e848c1d27d4..ea82f2691df 100644 --- a/target/arm/hvf_arm.h +++ b/target/arm/hvf_arm.h @@ -11,7 +11,7 @@ #ifndef QEMU_HVF_ARM_H #define QEMU_HVF_ARM_H -#include "cpu.h" +#include "target/arm/cpu-qom.h" /** * hvf_arm_init_debug() - initialize guest debug capabilities @@ -22,4 +22,7 @@ void hvf_arm_init_debug(void); void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu); +uint32_t hvf_arm_get_default_ipa_bit_size(void); +uint32_t hvf_arm_get_max_ipa_bit_size(void); + #endif diff --git a/target/arm/hyp_gdbstub.c b/target/arm/hyp_gdbstub.c index ebde2899cd8..bb5969720ce 100644 --- a/target/arm/hyp_gdbstub.c +++ b/target/arm/hyp_gdbstub.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" -#include "exec/gdbstub.h" +#include "gdbstub/enums.h" /* Maximum and current break/watch point counts */ int max_hw_bps, max_hw_wps; @@ -54,7 +54,7 @@ GArray *hw_breakpoints, *hw_watchpoints; * here so future PC comparisons will work properly. */ -int insert_hw_breakpoint(target_ulong addr) +int insert_hw_breakpoint(vaddr addr) { HWBreakpoint brk = { .bcr = 0x1, /* BCR E=1, enable */ @@ -80,7 +80,7 @@ int insert_hw_breakpoint(target_ulong addr) * Delete a breakpoint and shuffle any above down */ -int delete_hw_breakpoint(target_ulong pc) +int delete_hw_breakpoint(vaddr pc) { int i; for (i = 0; i < hw_breakpoints->len; i++) { @@ -125,7 +125,7 @@ int delete_hw_breakpoint(target_ulong pc) * 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) +int insert_hw_watchpoint(vaddr addr, vaddr len, int type) { HWWatchpoint wp = { .wcr = R_DBGWCR_E_MASK, /* E=1, enable */ @@ -158,7 +158,6 @@ int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type) break; default: g_assert_not_reached(); - break; } if (len <= 8) { /* we align the address and set the bits in BAS */ @@ -183,7 +182,7 @@ int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type) return 0; } -bool check_watchpoint_in_range(int i, target_ulong addr) +bool check_watchpoint_in_range(int i, vaddr addr) { HWWatchpoint *wp = get_hw_wp(i); uint64_t addr_top, addr_bottom = wp->wvr; @@ -215,7 +214,7 @@ bool check_watchpoint_in_range(int i, target_ulong addr) * Delete a breakpoint and shuffle any above down */ -int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type) +int delete_hw_watchpoint(vaddr addr, vaddr len, int type) { int i; for (i = 0; i < cur_hw_wps; i++) { @@ -227,7 +226,7 @@ int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type) return -ENOENT; } -bool find_hw_breakpoint(CPUState *cpu, target_ulong pc) +bool find_hw_breakpoint(CPUState *cpu, vaddr pc) { int i; @@ -240,7 +239,7 @@ bool find_hw_breakpoint(CPUState *cpu, target_ulong pc) return false; } -CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr) +CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, vaddr addr) { int i; diff --git a/target/arm/internals.h b/target/arm/internals.h index 5f5393b25c4..3360de9150f 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -25,9 +25,15 @@ #ifndef TARGET_ARM_INTERNALS_H #define TARGET_ARM_INTERNALS_H +#include "exec/hwaddr.h" +#include "exec/vaddr.h" +#include "exec/breakpoint.h" +#include "accel/tcg/tb-cpu-state.h" #include "hw/registerfields.h" #include "tcg/tcg-gvec-desc.h" +#include "system/memory.h" #include "syndrome.h" +#include "cpu-features.h" /* register banks for CPU modes */ #define BANK_USRSYS 0 @@ -39,6 +45,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 +64,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 +113,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,15 +353,31 @@ 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 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_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); + +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 +TCGTBCPUState arm_get_tb_cpu_state(CPUState *cs); 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); +int arm_cpu_mmu_index(CPUState *cs, bool ifetch); #endif /* CONFIG_TCG */ typedef enum ARMFPRounding { @@ -210,6 +397,141 @@ static inline FloatRoundMode arm_rmode_to_sf(ARMFPRounding rmode) return arm_rmode_to_sf_map[rmode]; } +/* Return the effective value of SCR_EL3.RW */ +static inline bool arm_scr_rw_eff(CPUARMState *env) +{ + /* + * SCR_EL3.RW has an effective value of 1 if: + * - we are NS and EL2 is implemented but doesn't support AArch32 + * - we are S and EL2 is enabled (in which case it must be AArch64) + */ + ARMCPU *cpu = env_archcpu(env); + + if (env->cp15.scr_el3 & SCR_RW) { + return true; + } + if (env->cp15.scr_el3 & SCR_NS) { + return arm_feature(env, ARM_FEATURE_EL2) && + !cpu_isar_feature(aa64_aa32_el2, cpu); + } else { + return env->cp15.scr_el3 & SCR_EEL2; + } +} + +/* Return true if the specified exception level is running in AArch64 state. */ +static inline bool arm_el_is_aa64(CPUARMState *env, int el) +{ + /* + * This isn't valid for EL0 (if we're in EL0, is_a64() is what you want, + * and if we're not in EL0 then the state of EL0 isn't well defined.) + */ + assert(el >= 1 && el <= 3); + bool aa64 = arm_feature(env, ARM_FEATURE_AARCH64); + + /* + * The highest exception level is always at the maximum supported + * register width, and then lower levels have a register width controlled + * by bits in the SCR or HCR registers. + */ + if (el == 3) { + return aa64; + } + + if (arm_feature(env, ARM_FEATURE_EL3)) { + aa64 = aa64 && arm_scr_rw_eff(env); + } + + if (el == 2) { + return aa64; + } + + if (arm_is_el2_enabled(env)) { + aa64 = aa64 && (env->cp15.hcr_el2 & HCR_RW); + } + + return aa64; +} + +/* + * Return the current Exception Level (as per ARMv8; note that this differs + * from the ARMv7 Privilege Level). + */ +static inline int arm_current_el(CPUARMState *env) +{ + if (arm_feature(env, ARM_FEATURE_M)) { + return arm_v7m_is_handler_mode(env) || + !(env->v7m.control[env->v7m.secure] & 1); + } + + if (is_a64(env)) { + return extract32(env->pstate, 2, 2); + } + + switch (env->uncached_cpsr & 0x1f) { + case ARM_CPU_MODE_USR: + return 0; + case ARM_CPU_MODE_HYP: + return 2; + case ARM_CPU_MODE_MON: + return 3; + default: + if (arm_is_secure(env) && !arm_el_is_aa64(env, 3)) { + /* If EL3 is 32-bit then all secure privileged modes run in EL3 */ + return 3; + } + + return 1; + } +} + +static inline bool arm_cpu_data_is_big_endian_a32(CPUARMState *env, + bool sctlr_b) +{ +#ifdef CONFIG_USER_ONLY + /* + * In system mode, BE32 is modelled in line with the + * architecture (as word-invariant big-endianness), where loads + * and stores are done little endian but from addresses which + * are adjusted by XORing with the appropriate constant. So the + * endianness to use for the raw data access is not affected by + * SCTLR.B. + * In user mode, however, we model BE32 as byte-invariant + * big-endianness (because user-only code cannot tell the + * difference), and so we need to use a data access endianness + * that depends on SCTLR.B. + */ + if (sctlr_b) { + return true; + } +#endif + /* In 32bit endianness is determined by looking at CPSR's E bit */ + return env->uncached_cpsr & CPSR_E; +} + +static inline bool arm_cpu_data_is_big_endian_a64(int el, uint64_t sctlr) +{ + return sctlr & (el ? SCTLR_EE : SCTLR_E0E); +} + +/* Return true if the processor is in big-endian mode. */ +static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) +{ + if (!is_a64(env)) { + return arm_cpu_data_is_big_endian_a32(env, arm_sctlr_b(env)); + } else { + int cur_el = arm_current_el(env); + uint64_t sctlr = arm_sctlr(env, cur_el); + return arm_cpu_data_is_big_endian_a64(cur_el, sctlr); + } +} + +#ifdef CONFIG_USER_ONLY +static inline bool arm_cpu_bswap_data(CPUARMState *env) +{ + return TARGET_BIG_ENDIAN ^ arm_cpu_data_is_big_endian(env); +} +#endif + static inline void aarch64_save_sp(CPUARMState *env, int el) { if (env->pstate & PSTATE_SP) { @@ -256,6 +578,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. @@ -388,8 +729,8 @@ typedef struct ARMMMUFaultInfo ARMMMUFaultInfo; struct ARMMMUFaultInfo { ARMFaultType type; ARMGPCF gpcf; - target_ulong s2addr; - target_ulong paddr; + hwaddr s2addr; + hwaddr paddr; ARMSecuritySpace paddr_space; int level; int domain; @@ -603,9 +944,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) @@ -672,7 +1013,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) { @@ -697,6 +1047,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; @@ -720,10 +1071,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: @@ -745,7 +1097,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: @@ -754,10 +1108,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(); } } @@ -933,7 +1283,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); @@ -942,10 +1292,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 * @@ -1066,6 +1434,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; } @@ -1180,7 +1551,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. */ @@ -1194,6 +1564,7 @@ typedef struct GetPhysAddrResult { * @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 * @result: set on translation success. * @fi: set to fault info if the translation fails @@ -1211,8 +1582,8 @@ typedef struct GetPhysAddrResult { * * for PSMAv5 based systems we don't bother to return a full FSR format * value. */ -bool get_phys_addr(CPUARMState *env, target_ulong address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, +bool get_phys_addr(CPUARMState *env, vaddr address, + MMUAccessType access_type, MemOp memop, ARMMMUIdx mmu_idx, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) __attribute__((nonnull)); @@ -1222,6 +1593,7 @@ bool get_phys_addr(CPUARMState *env, target_ulong 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. @@ -1230,8 +1602,8 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, * Similar to get_phys_addr, but use the given security space and don't perform * a Granule Protection Check on the resulting address. */ -bool get_phys_addr_with_space_nogpc(CPUARMState *env, target_ulong address, - MMUAccessType access_type, +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) @@ -1267,11 +1639,66 @@ FIELD(MTEDESC, TBI, 4, 2) FIELD(MTEDESC, TCMA, 6, 2) FIELD(MTEDESC, WRITE, 8, 1) FIELD(MTEDESC, ALIGN, 9, 3) -FIELD(MTEDESC, SIZEM1, 12, SIMD_DATA_BITS - 12) /* size - 1 */ +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); @@ -1384,14 +1811,15 @@ static inline uint64_t pmu_counter_mask(CPUARMState *env) return (1ULL << 31) | ((1ULL << pmu_num_counters(env)) - 1); } -#ifdef TARGET_AARCH64 -int arm_gen_dynamic_svereg_xml(CPUState *cpu, int base_reg); -int aarch64_gdb_get_sve_reg(CPUARMState *env, GByteArray *buf, int reg); -int aarch64_gdb_set_sve_reg(CPUARMState *env, uint8_t *buf, int reg); -int aarch64_gdb_get_fpu_reg(CPUARMState *env, GByteArray *buf, int reg); -int aarch64_gdb_set_fpu_reg(CPUARMState *env, uint8_t *buf, int reg); -int aarch64_gdb_get_pauth_reg(CPUARMState *env, GByteArray *buf, int reg); -int aarch64_gdb_set_pauth_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); @@ -1400,7 +1828,12 @@ 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 + +/* Return true if the gdbstub is presenting an AArch64 CPU */ +static inline bool arm_gdbstub_is_aarch64(ARMCPU *cpu) +{ + return arm_feature(&cpu->env, ARM_FEATURE_AARCH64); +} /* Read the CONTROL register as the MRS instruction would. */ uint32_t arm_v7m_mrs_control(CPUARMState *env, uint32_t secure); @@ -1440,6 +1873,9 @@ static inline uint64_t pauth_ptr_mask(ARMVAParameters param) /* Add the cpreg definitions for debug related system registers */ void define_debug_regs(ARMCPU *cpu); +/* Add the cpreg definitions for TLBI instructions */ +void define_tlb_insn_regs(ARMCPU *cpu); + /* Effective value of MDCR_EL2 */ static inline uint64_t arm_mdcr_el2_eff(CPUARMState *env) { @@ -1471,8 +1907,6 @@ static inline bool arm_fgt_active(CPUARMState *env, int el) (!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 @@ -1514,12 +1948,42 @@ extern GArray *hw_breakpoints, *hw_watchpoints; #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 find_hw_breakpoint(CPUState *cpu, vaddr pc); +int insert_hw_breakpoint(vaddr pc); +int delete_hw_breakpoint(vaddr pc); + +bool check_watchpoint_in_range(int i, vaddr addr); +CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, vaddr addr); +int insert_hw_watchpoint(vaddr addr, vaddr len, int type); +int delete_hw_watchpoint(vaddr addr, vaddr 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 the counter for the specified timer, as used for direct register + * accesses. + */ +uint64_t gt_direct_access_timer_offset(CPUARMState *env, int timeridx); + +/* + * Return mask of ARMMMUIdxBit values corresponding to an "invalidate + * all EL1" scope; this covers stage 1 and stage 2. + */ +int alle1_tlbmask(CPUARMState *env); + +/* Set the float_status behaviour to match the Arm defaults */ +void arm_set_default_fp_behaviours(float_status *s); +/* Set the float_status behaviour to match Arm FPCR.AH=1 behaviour */ +void arm_set_ah_fp_behaviours(float_status *s); +/* Read the float_status info and return the appropriate FPSR value */ +uint32_t vfp_get_fpsr_from_host(CPUARMState *env); +/* Clear the exception status flags from all float_status fields */ +void vfp_clear_float_status_exc_flags(CPUARMState *env); +/* + * Update float_status fields to handle the bits of the FPCR + * specified by mask changing to the values in val. + */ +void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask); -bool check_watchpoint_in_range(int i, target_ulong addr); -CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr); -int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type); -int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type); #endif diff --git a/target/arm/kvm-consts.h b/target/arm/kvm-consts.h index 7c6adc14f6e..c44d23dbe79 100644 --- a/target/arm/kvm-consts.h +++ b/target/arm/kvm-consts.h @@ -14,13 +14,13 @@ #ifndef ARM_KVM_CONSTS_H #define ARM_KVM_CONSTS_H -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #ifdef CONFIG_KVM #include #include #define MISMATCH_CHECK(X, Y) QEMU_BUILD_BUG_ON(X != Y) #endif -#endif +#endif /* COMPILING_PER_TARGET */ #ifndef MISMATCH_CHECK #define MISMATCH_CHECK(X, Y) QEMU_BUILD_BUG_ON(0) diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c index 965a486b320..34e57fab011 100644 --- a/target/arm/kvm-stub.c +++ b/target/arm/kvm-stub.c @@ -22,3 +22,100 @@ bool write_list_to_kvmstate(ARMCPU *cpu, int level) { g_assert_not_reached(); } + +/* + * It's safe to call these functions without KVM support. + * They should either do nothing or return "not supported". + */ +bool kvm_arm_aarch32_supported(void) +{ + return false; +} + +bool kvm_arm_pmu_supported(void) +{ + return false; +} + +bool kvm_arm_sve_supported(void) +{ + return false; +} + +bool kvm_arm_mte_supported(void) +{ + return false; +} + +/* + * These functions should never actually be called without KVM support. + */ +void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void kvm_arm_add_vcpu_properties(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +int kvm_arm_get_max_vm_ipa_size(MachineState *ms, bool *fixed_ipa) +{ + g_assert_not_reached(); +} + +int kvm_arm_vgic_probe(void) +{ + g_assert_not_reached(); +} + +void kvm_arm_pmu_set_irq(ARMCPU *cpu, int irq) +{ + g_assert_not_reached(); +} + +void kvm_arm_pmu_init(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void kvm_arm_pvtime_init(ARMCPU *cpu, uint64_t ipa) +{ + g_assert_not_reached(); +} + +void kvm_arm_steal_time_finalize(ARMCPU *cpu, Error **errp) +{ + g_assert_not_reached(); +} + +uint32_t kvm_arm_sve_get_vls(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void kvm_arm_enable_mte(Object *cpuobj, Error **errp) +{ + g_assert_not_reached(); +} + +void kvm_arm_reset_vcpu(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void arm_cpu_kvm_set_irq(void *arm_cpu, int irq, int level) +{ + g_assert_not_reached(); +} + +void kvm_arm_cpu_pre_save(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +bool kvm_arm_cpu_post_load(ARMCPU *cpu) +{ + g_assert_not_reached(); +} diff --git a/target/arm/kvm.c b/target/arm/kvm.c index b66b936a958..74fda8b8090 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. @@ -18,22 +20,29 @@ #include "qemu/main-loop.h" #include "qom/object.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_int.h" +#include "system/system.h" +#include "system/runstate.h" +#include "system/kvm.h" +#include "system/kvm_int.h" #include "kvm_arm.h" #include "cpu.h" #include "trace.h" #include "internals.h" #include "hw/pci/pci.h" #include "exec/memattrs.h" -#include "exec/address-spaces.h" +#include "system/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 }; @@ -41,32 +50,57 @@ 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) -{ - return kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_FINALIZE, &feature); -} - -void kvm_arm_init_serror_injection(CPUState *cs) +/** + * 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) { - 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, - int *fdarray, +bool kvm_arm_create_scratch_host_vcpu(int *fdarray, struct kvm_vcpu_init *init) { int ret = 0, kvmfd = -1, vmfd = -1, cpufd = -1; @@ -86,6 +120,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; @@ -100,40 +149,13 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, struct kvm_vcpu_init preferred; ret = ioctl(vmfd, KVM_ARM_PREFERRED_TARGET, &preferred); - if (!ret) { - init->target = preferred.target; - } - } - if (ret >= 0) { - ret = ioctl(cpufd, KVM_ARM_VCPU_INIT, init); - if (ret < 0) { - goto err; - } - } else if (cpus_to_try) { - /* Old kernel which doesn't know about the - * PREFERRED_TARGET ioctl: we know it will only support - * creating one kind of guest CPU which is its preferred - * CPU type. - */ - struct kvm_vcpu_init try; - - while (*cpus_to_try != QEMU_KVM_ARM_TARGET_NONE) { - try.target = *cpus_to_try++; - memcpy(try.features, init->features, sizeof(init->features)); - ret = ioctl(cpufd, KVM_ARM_VCPU_INIT, &try); - if (ret >= 0) { - break; - } - } if (ret < 0) { goto err; } - init->target = try.target; - } else { - /* Treat a NULL cpus_to_try argument the same as an empty - * list, which means we will fail the call since this must - * be an old kernel which doesn't support PREFERRED_TARGET. - */ + init->target = preferred.target; + } + ret = ioctl(cpufd, KVM_ARM_VCPU_INIT, init); + if (ret < 0) { goto err; } @@ -167,6 +189,251 @@ 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; + + /* + * 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(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; @@ -210,10 +477,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; @@ -271,6 +538,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 " @@ -308,7 +579,13 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } } - kvm_arm_init_debug(s); + 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; } @@ -376,19 +653,11 @@ 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", @@ -469,11 +738,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; @@ -546,6 +840,28 @@ int kvm_arm_init_cpreg_list(ARMCPU *cpu) 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); @@ -553,24 +869,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(); @@ -589,7 +900,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; @@ -598,19 +908,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 @@ -630,13 +938,24 @@ void kvm_arm_cpu_pre_save(ARMCPU *cpu) } } -void kvm_arm_cpu_post_load(ARMCPU *cpu) +bool kvm_arm_cpu_post_load(ARMCPU *cpu) { + if (!write_list_to_kvmstate(cpu, KVM_PUT_FULL_STATE)) { + return false; + } + /* Note that it's OK for the TCG side not to know about + * every register in the list; KVM is authoritative if + * we're using it. + */ + write_list_to_cpustate(cpu); + /* KVM virtual time adjustment */ if (cpu->kvm_adjvtime) { cpu->kvm_vtime = *kvm_arm_get_cpreg_ptr(cpu, KVM_REG_ARM_TIMER_CNT); cpu->kvm_vtime_dirty = true; } + + return true; } void kvm_arm_reset_vcpu(ARMCPU *cpu) @@ -646,7 +965,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(); @@ -668,58 +987,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(); @@ -728,20 +1039,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(); @@ -750,7 +1062,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; @@ -779,7 +1099,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; @@ -803,6 +1131,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); @@ -817,7 +1202,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 " @@ -849,7 +1234,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], @@ -878,41 +1263,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 @@ -928,7 +1311,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; } @@ -941,19 +1324,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: @@ -974,12 +1435,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); } @@ -1085,11 +1581,6 @@ int kvm_arch_msi_data_to_gsi(uint32_t data) return (data - 32) & 0xffff; } -bool kvm_arch_cpu_check_are_resettable(void) -{ - return true; -} - static void kvm_arch_get_eager_split_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -1133,3 +1624,852 @@ void kvm_arch_accel_class_init(ObjectClass *oc) 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(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_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + return 0; +} + +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) { + 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_memory_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; + } +} + +void arm_cpu_kvm_set_irq(void *arm_cpu, int irq, int level) +{ + ARMCPU *cpu = arm_cpu; + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + uint32_t linestate_bit; + int irq_id; + + switch (irq) { + case ARM_CPU_IRQ: + irq_id = KVM_ARM_IRQ_CPU_IRQ; + linestate_bit = CPU_INTERRUPT_HARD; + break; + case ARM_CPU_FIQ: + irq_id = KVM_ARM_IRQ_CPU_FIQ; + linestate_bit = CPU_INTERRUPT_FIQ; + break; + default: + g_assert_not_reached(); + } + + if (level) { + env->irq_line_state |= linestate_bit; + } else { + env->irq_line_state &= ~linestate_bit; + } + kvm_arm_set_irq(cs->cpu_index, KVM_ARM_IRQ_TYPE_CPU, irq_id, !!level); +} diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c deleted file mode 100644 index 5e95c496bb9..00000000000 --- a/target/arm/kvm64.c +++ /dev/null @@ -1,1347 +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; - -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; -} - -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); - } -} - -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 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_aa64isar2, - ARM64_SYS_REG(3, 0, 0, 6, 2)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr0, - ARM64_SYS_REG(3, 0, 0, 7, 0)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr1, - ARM64_SYS_REG(3, 0, 0, 7, 1)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr2, - ARM64_SYS_REG(3, 0, 0, 7, 2)); - - /* - * Note that if AArch32 support is not present in the host, - * the AArch32 sysregs are present to be read, but will - * return UNKNOWN values. This is neither better nor worse - * than skipping the reads and leaving 0, as we must avoid - * considering the values in every case. - */ - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_pfr0, - ARM64_SYS_REG(3, 0, 0, 1, 0)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_pfr1, - ARM64_SYS_REG(3, 0, 0, 1, 1)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_dfr0, - ARM64_SYS_REG(3, 0, 0, 1, 2)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr0, - ARM64_SYS_REG(3, 0, 0, 1, 4)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr1, - ARM64_SYS_REG(3, 0, 0, 1, 5)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr2, - ARM64_SYS_REG(3, 0, 0, 1, 6)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr3, - ARM64_SYS_REG(3, 0, 0, 1, 7)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar0, - ARM64_SYS_REG(3, 0, 0, 2, 0)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar1, - ARM64_SYS_REG(3, 0, 0, 2, 1)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar2, - ARM64_SYS_REG(3, 0, 0, 2, 2)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar3, - ARM64_SYS_REG(3, 0, 0, 2, 3)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar4, - ARM64_SYS_REG(3, 0, 0, 2, 4)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar5, - ARM64_SYS_REG(3, 0, 0, 2, 5)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr4, - ARM64_SYS_REG(3, 0, 0, 2, 6)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar6, - ARM64_SYS_REG(3, 0, 0, 2, 7)); - - err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr0, - ARM64_SYS_REG(3, 0, 0, 3, 0)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr1, - ARM64_SYS_REG(3, 0, 0, 3, 1)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr2, - ARM64_SYS_REG(3, 0, 0, 3, 2)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_pfr2, - ARM64_SYS_REG(3, 0, 0, 3, 4)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_dfr1, - ARM64_SYS_REG(3, 0, 0, 3, 5)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr5, - ARM64_SYS_REG(3, 0, 0, 3, 6)); - - /* - * DBGDIDR is a bit complicated because the kernel doesn't - * provide an accessor for it in 64-bit mode, which is what this - * scratch VM is in, and there's no architected "64-bit sysreg - * which reads the same as the 32-bit register" the way there is - * for other ID registers. Instead we synthesize a value from the - * AArch64 ID_AA64DFR0, the same way the kernel code in - * arch/arm64/kvm/sys_regs.c:trap_dbgidr() does. - * We only do this if the CPU supports AArch32 at EL1. - */ - if (FIELD_EX32(ahcf->isar.id_aa64pfr0, ID_AA64PFR0, EL1) >= 2) { - int wrps = FIELD_EX64(ahcf->isar.id_aa64dfr0, ID_AA64DFR0, WRPS); - int brps = FIELD_EX64(ahcf->isar.id_aa64dfr0, ID_AA64DFR0, BRPS); - int ctx_cmps = - FIELD_EX64(ahcf->isar.id_aa64dfr0, ID_AA64DFR0, CTX_CMPS); - int version = 6; /* ARMv8 debug architecture */ - bool has_el3 = - !!FIELD_EX32(ahcf->isar.id_aa64pfr0, ID_AA64PFR0, EL3); - uint32_t dbgdidr = 0; - - dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, WRPS, wrps); - dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, BRPS, brps); - dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, CTX_CMPS, ctx_cmps); - dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, VERSION, version); - dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, NSUHD_IMP, has_el3); - dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, SE_IMP, has_el3); - dbgdidr |= (1 << 15); /* RES1 bit */ - ahcf->isar.dbgdidr = dbgdidr; - } - - if (pmu_supported) { - /* PMCR_EL0 is only accessible if the vCPU has feature PMU_V3 */ - err |= read_sys_reg64(fdarray[2], &ahcf->isar.reset_pmcr_el0, - ARM64_SYS_REG(3, 3, 9, 12, 0)); - } - - if (sve_supported) { - /* - * There is a range of kernels between kernel commit 73433762fcae - * and f81cb2c3ad41 which have a bug where the kernel doesn't - * expose SYS_ID_AA64ZFR0_EL1 via the ONE_REG API unless the VM has - * enabled SVE support, which resulted in an error rather than RAZ. - * So only read the register if we set KVM_ARM_VCPU_SVE above. - */ - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64zfr0, - ARM64_SYS_REG(3, 0, 0, 4, 4)); - } - } - - kvm_arm_destroy_scratch_host_vcpu(fdarray); - - if (err < 0) { - return false; - } - - /* - * We can assume any KVM supporting CPU is at least a v8 - * with VFPv4+Neon; this in turn implies most of the other - * feature bits. - */ - features |= 1ULL << ARM_FEATURE_V8; - features |= 1ULL << ARM_FEATURE_NEON; - features |= 1ULL << ARM_FEATURE_AARCH64; - features |= 1ULL << ARM_FEATURE_PMU; - features |= 1ULL << ARM_FEATURE_GENERIC_TIMER; - - ahcf->features = features; - - return true; -} - -void kvm_arm_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 }, - { KVM_REG_ARM_PTIMER_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 051a0da41c4..7dc83caed5c 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -11,99 +11,31 @@ #ifndef QEMU_KVM_ARM_H #define QEMU_KVM_ARM_H -#include "sysemu/kvm.h" -#include "exec/memory.h" -#include "qemu/error-report.h" +#include "system/kvm.h" +#include "target/arm/cpu-qom.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 @@ -152,8 +84,10 @@ void kvm_arm_cpu_pre_save(ARMCPU *cpu); * @cpu: ARMCPU * * Called from cpu_post_load() to update KVM CPU state from the cpreg list. + * + * Returns: true on success, or false if write_list_to_kvmstate failed. */ -void kvm_arm_cpu_post_load(ARMCPU *cpu); +bool kvm_arm_cpu_post_load(ARMCPU *cpu); /** * kvm_arm_reset_vcpu: @@ -163,41 +97,9 @@ 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 +struct kvm_vcpu_init; /** * kvm_arm_create_scratch_host_vcpu: - * @cpus_to_try: array of QEMU_KVM_ARM_TARGET_* values (terminated with - * QEMU_KVM_ARM_TARGET_NONE) to try as fallback if the kernel does not - * know the PREFERRED_TARGET ioctl. Passing NULL is the same as passing - * an empty array. * @fdarray: filled in with kvmfd, vmfd, cpufd file descriptors in that order * @init: filled in with the necessary values for creating a host * vcpu. If NULL is provided, will not init the vCPU (though the cpufd @@ -210,8 +112,7 @@ int kvm_put_vcpu_events(ARMCPU *cpu); * Returns: true on success (and fdarray and init are filled in), * false on failure (and fdarray and init are not valid). */ -bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, - int *fdarray, +bool kvm_arm_create_scratch_host_vcpu(int *fdarray, struct kvm_vcpu_init *init); /** @@ -222,37 +123,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 +144,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 +161,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 +184,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,171 +201,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); -#else - -/* - * It's safe to call these functions without KVM support. - * They should either do nothing or return "not supported". - */ -static inline bool kvm_arm_aarch32_supported(void) -{ - return false; -} - -static inline bool kvm_arm_pmu_supported(void) -{ - return false; -} - -static inline bool kvm_arm_sve_supported(void) -{ - return false; -} +void kvm_arm_enable_mte(Object *cpuobj, Error **errp); -static inline bool kvm_arm_steal_time_supported(void) -{ - return false; -} - -/* - * These functions should never actually be called without KVM support. - */ -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) -{ - g_assert_not_reached(); -} - -static inline int kvm_arm_get_max_vm_ipa_size(MachineState *ms, bool *fixed_ipa) -{ - g_assert_not_reached(); -} - -static inline int kvm_arm_vgic_probe(void) -{ - g_assert_not_reached(); -} - -static inline void kvm_arm_pmu_set_irq(CPUState *cs, int irq) -{ - g_assert_not_reached(); -} - -static inline void kvm_arm_pmu_init(CPUState *cs) -{ - g_assert_not_reached(); -} - -static inline void kvm_arm_pvtime_init(CPUState *cs, uint64_t ipa) -{ - g_assert_not_reached(); -} - -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) -{ - g_assert_not_reached(); -} - -#endif - -/** - * 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); +void arm_cpu_kvm_set_irq(void *arm_cpu, int irq, int level); #endif diff --git a/target/arm/machine.c b/target/arm/machine.c index fc4a4a40644..e442d485241 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -1,11 +1,14 @@ #include "qemu/osdep.h" #include "cpu.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "kvm_arm.h" #include "internals.h" -#include "migration/cpu.h" +#include "cpu-features.h" +#include "migration/qemu-file-types.h" +#include "migration/vmstate.h" +#include "target/arm/gtimer.h" static bool vfp_needed(void *opaque) { @@ -16,6 +19,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) { @@ -23,7 +55,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; } @@ -32,8 +67,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; } @@ -43,12 +79,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), @@ -98,6 +214,10 @@ static const VMStateDescription vmstate_vfp = { .offset = 0, }, VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_vfp_fpcr_fpsr, + NULL } }; @@ -114,14 +234,13 @@ 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() } }; -#ifdef TARGET_AARCH64 /* The expression ARM_MAX_VQ - 2 is 0 for pure AArch32 build, * and ARMPredicateReg is actively empty. This triggers errors * in the expansion of the VMSTATE macros. @@ -139,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() } @@ -149,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() } @@ -160,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, @@ -173,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() } @@ -195,13 +314,12 @@ 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() } }; -#endif /* AARCH64 */ static bool serror_needed(void *opaque) { @@ -216,7 +334,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), @@ -234,12 +352,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; @@ -253,7 +390,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() @@ -288,7 +425,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() @@ -300,7 +437,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() } @@ -311,7 +448,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() } @@ -330,7 +467,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() @@ -342,7 +479,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), @@ -364,7 +501,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() @@ -376,7 +513,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), @@ -390,7 +527,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, @@ -415,7 +552,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() @@ -444,7 +581,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, @@ -473,7 +610,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() } @@ -503,7 +640,7 @@ static const VMStateDescription vmstate_pmsav8r = { .version_id = 1, .minimum_version_id = 1, .needed = pmsav8r_needed, - .fields = (VMStateField[]) { + .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, @@ -517,7 +654,7 @@ static const VMStateDescription vmstate_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, @@ -526,7 +663,7 @@ static const VMStateDescription vmstate_pmsav8 = { VMSTATE_UINT32(env.pmsav8.mair1[M_REG_NS], ARMCPU), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_pmsav8r, NULL } @@ -559,7 +696,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), @@ -763,6 +900,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 @@ -771,7 +922,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; @@ -825,15 +976,9 @@ static int cpu_post_load(void *opaque, int version_id) } if (kvm_enabled()) { - if (!write_list_to_kvmstate(cpu, KVM_PUT_FULL_STATE)) { + if (!kvm_arm_cpu_post_load(cpu)) { return -1; } - /* Note that it's OK for the TCG side not to know about - * every register in the list; KVM is authoritative if - * we're using it. - */ - write_list_to_cpustate(cpu); - kvm_arm_cpu_post_load(cpu); } else { if (!write_list_to_cpustate(cpu)) { return -1; @@ -869,11 +1014,11 @@ static int cpu_post_load(void *opaque, int version_id) } if (!kvm_enabled()) { - pmu_op_finish(&cpu->env); + pmu_op_finish(env); } if (tcg_enabled()) { - arm_rebuild_hflags(&cpu->env); + arm_rebuild_hflags(env); } return 0; @@ -887,7 +1032,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), @@ -936,7 +1081,7 @@ const VMStateDescription vmstate_arm_cpu = { }, VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_vfp, &vmstate_iwmmxt, &vmstate_m, @@ -949,12 +1094,11 @@ const VMStateDescription vmstate_arm_cpu = { &vmstate_pmsav7, &vmstate_pmsav8, &vmstate_m_security, -#ifdef TARGET_AARCH64 &vmstate_sve, &vmstate_za, -#endif &vmstate_serror, &vmstate_irq_line_state, + &vmstate_wfxt_timer, NULL } }; diff --git a/target/arm/meson.build b/target/arm/meson.build index e645e456da4..7aa81e30aba 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -1,38 +1,58 @@ arm_ss = ss.source_set() +arm_common_ss = ss.source_set() arm_ss.add(files( - 'cpu.c', - 'debug_helper.c', 'gdbstub.c', - 'helper.c', - 'vfp_helper.c', )) -arm_ss.add(zlib) - -arm_ss.add(when: 'CONFIG_KVM', if_true: files('hyp_gdbstub.c', 'kvm.c', 'kvm64.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', -)) + 'gdbstub64.c')) arm_system_ss = ss.source_set() +arm_common_system_ss = ss.source_set() arm_system_ss.add(files( + 'arm-qmp-cmds.c', +)) +arm_system_ss.add(when: 'CONFIG_KVM', if_true: files('hyp_gdbstub.c', 'kvm.c')) +arm_system_ss.add(when: 'CONFIG_HVF', if_true: files('hyp_gdbstub.c')) + +arm_user_ss = ss.source_set() +arm_user_ss.add(files('cpu.c')) +arm_user_ss.add(when: 'TARGET_AARCH64', if_false: files( + 'cpu32-stubs.c', +)) +arm_user_ss.add(files( + 'debug_helper.c', + 'helper.c', + 'vfp_fpscr.c', +)) + +arm_common_system_ss.add(files('cpu.c')) +arm_common_system_ss.add(when: 'TARGET_AARCH64', if_false: files( + 'cpu32-stubs.c')) +arm_common_system_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) +arm_common_system_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c')) +arm_common_system_ss.add(files( 'arch_dump.c', 'arm-powerctl.c', - 'arm-qmp-cmds.c', 'cortex-regs.c', + 'debug_helper.c', + 'helper.c', 'machine.c', 'ptw.c', + 'vfp_fpscr.c', )) subdir('hvf') -if 'CONFIG_TCG' in config_all +if 'CONFIG_TCG' in config_all_accel subdir('tcg') else - arm_ss.add(files('tcg-stubs.c')) + arm_common_system_ss.add(files('tcg-stubs.c')) endif target_arch += {'arm': arm_ss} -target_softmmu_arch += {'arm': arm_system_ss} +target_system_arch += {'arm': arm_system_ss} +target_user_arch += {'arm': arm_user_ss} +target_common_arch += {'arm': arm_common_ss} +target_common_system_arch += {'arm': arm_common_system_ss} diff --git a/target/arm/multiprocessing.h b/target/arm/multiprocessing.h new file mode 100644 index 00000000000..81715d345c2 --- /dev/null +++ b/target/arm/multiprocessing.h @@ -0,0 +1,16 @@ +/* + * ARM multiprocessor CPU helpers + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef TARGET_ARM_MULTIPROCESSING_H +#define TARGET_ARM_MULTIPROCESSING_H + +#include "target/arm/cpu-qom.h" + +uint64_t arm_cpu_mp_affinity(ARMCPU *cpu); + +#endif diff --git a/target/arm/ptw.c b/target/arm/ptw.c index bfbab26b9b4..44170d831cc 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -10,13 +10,14 @@ #include "qemu/log.h" #include "qemu/range.h" #include "qemu/main-loop.h" -#include "exec/exec-all.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" +#include "exec/tlb-flags.h" +#include "accel/tcg/probe.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 { /* @@ -72,17 +73,21 @@ typedef struct S1Translate { } S1Translate; static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw, - target_ulong address, - MMUAccessType access_type, + vaddr address, + MMUAccessType access_type, MemOp memop, GetPhysAddrResult *result, ARMMMUFaultInfo *fi); static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw, - target_ulong address, - MMUAccessType access_type, + 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[] = { [0] = 32, @@ -94,7 +99,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)) { @@ -109,13 +132,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 */ @@ -265,6 +283,8 @@ 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_S: @@ -472,6 +492,16 @@ static bool granule_protection_check(CPUARMState *env, uint64_t paddress, 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) { /* @@ -554,7 +584,7 @@ static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw, }; GetPhysAddrResult s2 = { }; - if (get_phys_addr_gpc(env, &s2ptw, addr, MMU_DATA_LOAD, &s2, fi)) { + if (get_phys_addr_gpc(env, &s2ptw, addr, MMU_DATA_LOAD, 0, &s2, fi)) { goto fail; } @@ -579,7 +609,7 @@ 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_attrs = full->extra.arm.pte_attrs; ptw->out_space = full->attrs.space; #else g_assert_not_reached(); @@ -707,13 +737,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) +#if defined(CONFIG_ATOMIC64) && defined(CONFIG_TCG) uint64_t cur_val; void *host = ptw->out_host; if (unlikely(!host)) { - fi->type = ARMFault_UnsuppAtomicUpdate; - 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; } /* @@ -749,7 +839,6 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val, ptw->out_rw = true; } -#ifdef CONFIG_ATOMIC64 if (ptw->out_be) { old_val = cpu_to_be64(old_val); new_val = cpu_to_be64(new_val); @@ -761,36 +850,6 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val, cur_val = qatomic_cmpxchg__nocheck((uint64_t *)host, old_val, new_val); cur_val = le64_to_cpu(cur_val); } -#else - /* - * We can't support the full 64-bit atomic cmpxchg on the host. - * Because this is only used for FEAT_HAFDBS, which is only for AA64, - * we know that TCG_OVERSIZED_GUEST is set, which means that we are - * running in round-robin mode and could only race with dma i/o. - */ -#if !TCG_OVERSIZED_GUEST -# error "Unexpected configuration" -#endif - bool locked = qemu_mutex_iothread_locked(); - if (!locked) { - qemu_mutex_lock_iothread(); - } - if (ptw->out_be) { - cur_val = ldq_be_p(host); - if (cur_val == old_val) { - stq_be_p(host, new_val); - } - } else { - cur_val = ldq_le_p(host); - if (cur_val == old_val) { - stq_le_p(host, new_val); - } - } - if (!locked) { - qemu_mutex_unlock_iothread(); - } -#endif - return cur_val; #else /* AArch32 does not have FEAT_HADFS; non-TCG guests only use debug-mode. */ @@ -1061,7 +1120,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. */ @@ -1153,16 +1212,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)) { @@ -1172,37 +1234,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 = ARMSS_NonSecure; } + 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: @@ -1270,25 +1318,24 @@ 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]) + * @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 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 { @@ -1306,8 +1353,6 @@ static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64, regime_is_pan(env, mmu_idx) && (regime_sctlr(env, mmu_idx) & SCTLR_EPAN) && !xn) { prot_rw = 0; - } else { - prot_rw = simple_ap_to_rw_prot_is_user(ap, false); } } @@ -1580,6 +1625,12 @@ static bool lpae_block_desc_valid(ARMCPU *cpu, bool ds, } } +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); +} + /** * get_phys_addr_lpae: perform one stage of page table walk, LPAE format * @@ -1593,12 +1644,13 @@ static bool lpae_block_desc_valid(ARMCPU *cpu, bool ds, * @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 + * @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, + MMUAccessType access_type, MemOp memop, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { ARMCPU *cpu = env_archcpu(env); @@ -1608,7 +1660,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, uint64_t ttbr; hwaddr descaddr, indexmask, indexmask_grainsize; uint32_t tableattrs; - target_ulong page_size; + uint64_t page_size; uint64_t attrs; int32_t stride; int addrsize, inputsize, outputsize; @@ -1619,6 +1671,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, bool aarch64 = arm_el_is_aa64(env, el); uint64_t descriptor, new_descriptor; ARMSecuritySpace out_space; + bool device; /* TODO: This code does not support shareability levels. */ if (aarch64) { @@ -1680,7 +1733,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, * validation to do here. */ if (inputsize < addrsize) { - target_ulong top_bits = sextract64(address, inputsize, + uint64_t top_bits = sextract64(address, inputsize, addrsize - inputsize); if (-top_bits != param.select) { /* The gap between the two regions is a Translation fault */ @@ -1936,8 +1989,21 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, 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 { int nse, ns = extract32(attrs, 5, 1); + uint8_t attrindx; + uint64_t mair; + int user_rw, prot_rw; + switch (out_space) { case ARMSS_Root: /* @@ -1988,12 +2054,73 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, xn = extract64(attrs, 54, 1); pxn = extract64(attrs, 53, 1); + if (el == 1 && nv_nv1_enabled(env, ptw)) { + /* + * With FEAT_NV, when HCR_EL2.{NV,NV1} == {1,1}, the block/page + * descriptor bit 54 holds PXN, 53 is RES0, and the effective value + * of UXN is 0. Similarly for bits 59 and 60 in table descriptors + * (which we have already folded into bits 53 and 54 of attrs). + * AP[1] (descriptor bit 6, our ap bit 0) is treated as 0. + * Similarly, APTable[0] from the table descriptor is treated as 0; + * we already folded this into AP[1] and squashing that to 0 does + * the right thing. + */ + pxn = xn; + xn = 0; + ap &= ~1; + } + + 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, ap, xn, pxn, - result->f.attrs.space, out_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))) { @@ -2023,23 +2150,6 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, result->f.attrs.space = out_space; result->f.attrs.secure = arm_space_is_secure(out_space); - if (regime_is_stage2(mmu_idx)) { - result->cacheattrs.is_s2_format = true; - result->cacheattrs.attrs = extract32(attrs, 2, 4); - } 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); - - /* 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 */ - } - } - /* * For FEAT_LPA2 and effective DS, the SH field in the attributes * was re-purposed for output address bits. The SH attribute in @@ -3031,7 +3141,6 @@ static ARMCacheAttrs combine_cacheattrs(uint64_t hcr, assert(!s1.is_s2_format); ret.is_s2_format = false; - ret.guarded = s1.guarded; if (s1.attrs == 0xf0) { tagged = true; @@ -3083,7 +3192,7 @@ static ARMCacheAttrs combine_cacheattrs(uint64_t hcr, */ static bool get_phys_addr_disabled(CPUARMState *env, S1Translate *ptw, - target_ulong address, + vaddr address, MMUAccessType access_type, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) @@ -3166,20 +3275,21 @@ static bool get_phys_addr_disabled(CPUARMState *env, } 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; ARMSecuritySpace in_space = ptw->in_space; - bool ret, ipa_secure; + bool ret, ipa_secure, s1_guarded; ARMCacheAttrs cacheattrs1; ARMSecuritySpace ipa_space; uint64_t hcr; - ret = get_phys_addr_nogpc(env, ptw, address, access_type, result, fi); + ret = get_phys_addr_nogpc(env, ptw, address, access_type, + memop, result, fi); /* If S1 fails, return early. */ if (ret) { @@ -3201,10 +3311,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_nogpc(env, ptw, ipa, access_type, result, fi); + ret = get_phys_addr_nogpc(env, ptw, ipa, access_type, + memop, result, fi); fi->s2addr = ipa; /* Combine the S1 and S2 perms. */ @@ -3251,6 +3363,9 @@ 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. @@ -3267,8 +3382,8 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, } static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw, - target_ulong address, - MMUAccessType access_type, + vaddr address, + MMUAccessType access_type, MemOp memop, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { @@ -3331,7 +3446,7 @@ static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw, if (arm_feature(env, ARM_FEATURE_EL2) && !regime_translation_disabled(env, ARMMMUIdx_Stage2, ptw->in_space)) { return get_phys_addr_twostage(env, ptw, address, access_type, - result, fi); + memop, result, fi); } /* fall through */ @@ -3394,7 +3509,8 @@ static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw, } if (regime_using_lpae_format(env, mmu_idx)) { - return get_phys_addr_lpae(env, ptw, address, access_type, 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); @@ -3404,12 +3520,13 @@ static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw, } static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw, - target_ulong address, - MMUAccessType access_type, + vaddr address, + MMUAccessType access_type, MemOp memop, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { - if (get_phys_addr_nogpc(env, ptw, address, access_type, result, 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, @@ -3420,8 +3537,8 @@ static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw, return false; } -bool get_phys_addr_with_space_nogpc(CPUARMState *env, target_ulong address, - MMUAccessType access_type, +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) @@ -3430,16 +3547,13 @@ bool get_phys_addr_with_space_nogpc(CPUARMState *env, target_ulong address, .in_mmu_idx = mmu_idx, .in_space = space, }; - return get_phys_addr_nogpc(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, - GetPhysAddrResult *result, ARMMMUFaultInfo *fi) +static ARMSecuritySpace +arm_mmu_idx_to_security_space(CPUARMState *env, ARMMMUIdx mmu_idx) { - S1Translate ptw = { - .in_mmu_idx = mmu_idx, - }; ARMSecuritySpace ss; switch (mmu_idx) { @@ -3481,6 +3595,8 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, 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; @@ -3498,27 +3614,33 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, g_assert_not_reached(); } - ptw.in_space = ss; - return get_phys_addr_gpc(env, &ptw, address, access_type, result, fi); + return ss; } -hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, - MemTxAttrs *attrs) +bool get_phys_addr(CPUARMState *env, vaddr address, + MMUAccessType access_type, MemOp memop, ARMMMUIdx mmu_idx, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { - 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 = mmu_idx, - .in_space = ss, + .in_space = arm_mmu_idx_to_security_space(env, mmu_idx), + }; + + return get_phys_addr_gpc(env, &ptw, address, access_type, + memop, result, fi); +} + +static hwaddr arm_cpu_get_phys_page(CPUARMState *env, vaddr addr, + MemTxAttrs *attrs, ARMMMUIdx mmu_idx) +{ + S1Translate ptw = { + .in_mmu_idx = mmu_idx, + .in_space = arm_mmu_idx_to_security_space(env, mmu_idx), .in_debug = true, }; GetPhysAddrResult res = {}; ARMMMUFaultInfo fi = {}; - bool ret; - - ret = get_phys_addr_gpc(env, &ptw, addr, MMU_DATA_LOAD, &res, &fi); + bool ret = get_phys_addr_gpc(env, &ptw, addr, MMU_DATA_LOAD, 0, &res, &fi); *attrs = res.f.attrs; if (ret) { @@ -3526,3 +3648,33 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, } return res.f.phys_addr; } + +hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, + MemTxAttrs *attrs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + ARMMMUIdx mmu_idx = arm_mmu_idx(env); + + hwaddr res = arm_cpu_get_phys_page(env, addr, attrs, mmu_idx); + + if (res != -1) { + return res; + } + + /* + * Memory may be accessible for an "unprivileged load/store" variant. + * In this case, get_a64_user_mem_index function generates an op using an + * unprivileged mmu idx, so we need to try with it. + */ + switch (mmu_idx) { + case ARMMMUIdx_E10_1: + case ARMMMUIdx_E10_1_PAN: + return arm_cpu_get_phys_page(env, addr, attrs, ARMMMUIdx_E10_0); + case ARMMMUIdx_E20_2: + case ARMMMUIdx_E20_2_PAN: + return arm_cpu_get_phys_page(env, addr, attrs, ARMMMUIdx_E20_0); + default: + return -1; + } +} diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index 8a6b8f8162a..3244e0740dd 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, @@ -58,6 +60,7 @@ enum arm_exception_class { 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, @@ -79,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 @@ -215,7 +227,7 @@ 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; } /* @@ -241,12 +253,12 @@ static inline uint32_t syn_pacfail(bool data, int keynumber) 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) @@ -255,13 +267,12 @@ 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, +static inline uint32_t syn_gpc(int s2ptw, int ind, int gpcsc, int vncr, int cm, int s1ptw, int wnr, int fsc) { - /* TODO: FEAT_NV2 adds VNCR */ return (EC_GPC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (s2ptw << 21) - | (ind << 20) | (gpcsc << 14) | (cm << 8) | (s1ptw << 7) - | (wnr << 6) | fsc; + | (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) @@ -294,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) @@ -334,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 index 152b172e243..5e5166c0490 100644 --- a/target/arm/tcg-stubs.c +++ b/target/arm/tcg-stubs.c @@ -21,7 +21,30 @@ void raise_exception_ra(CPUARMState *env, uint32_t excp, uint32_t syndrome, { g_assert_not_reached(); } -/* Temporarily while cpu_get_tb_cpu_state() is still in common code */ -void assert_hflags_rebuild_correctly(CPUARMState *env) + +/* TLBI insns are only used by TCG, so we don't need to do anything for KVM */ +void define_tlb_insn_regs(ARMCPU *cpu) +{ +} + +/* With KVM, we never use float_status, so these can be no-ops */ +void arm_set_default_fp_behaviours(float_status *s) +{ +} + +void arm_set_ah_fp_behaviours(float_status *s) +{ +} + +uint32_t vfp_get_fpsr_from_host(CPUARMState *env) +{ + return 0; +} + +void vfp_clear_float_status_exc_flags(CPUARMState *env) +{ +} + +void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) { } diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index ef64a3f9cba..8c798cde2b4 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -19,11 +19,80 @@ # This file is processed by scripts/decodetree.py # -&r rn -&ri rd imm -&rri_sf rd rn imm sf -&i imm - +%rd 0:5 +%esz_sd 22:1 !function=plus_2 +%esz_hs 22:1 !function=plus_1 +%esz_hsd 22:2 !function=xor_2 +%hl 11:1 21:1 +%hlm 11:1 20:2 + +&r rn +&rrr rd rn rm +&ri rd imm +&rr rd rn +&rr_sf rd rn sf +&rri_sf rd rn imm sf +&rrr_sf rd rn rm 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_s ........ ... ..... ...... rn:5 rd:5 &rr_e esz=2 +@rr_d ........ ... ..... ...... rn:5 rd:5 &rr_e esz=3 +@rr_e ........ esz:2 . ..... ...... rn:5 rd:5 &rr_e +@rr_sd ........ ... ..... ...... rn:5 rd:5 &rr_e esz=%esz_sd +@rr_hsd ........ ... ..... ...... rn:5 rd:5 &rr_e esz=%esz_hsd + +@rrr_b ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=0 +@rrr_h ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=1 +@rrr_s ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=2 +@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_b . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=0 +@qrr_h . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=1 +@qrr_s . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=2 +@qrr_bh . q:1 ...... . esz:1 ...... ...... rn:5 rd:5 &qrr_e +@qrr_hs . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=%esz_hs +@qrr_sd . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=%esz_sd +@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 @@ -107,7 +176,7 @@ UBFM . 10 100110 . ...... ...... ..... ..... @bitfield_32 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 +### Branches %imm26 0:s26 !function=times_4 @branch . ..... .......................... &i imm=%imm26 @@ -126,7 +195,8 @@ CBZ sf:1 011010 nz:1 ................... rt:5 &cbz imm=%imm19 TBZ . 011011 nz:1 ..... .............. rt:5 &tbz imm=%imm14 bitpos=%imm31_19 -B_cond 0101010 0 ................... 0 cond:4 imm=%imm19 +# 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 @@ -182,10 +252,17 @@ ERETA 1101011 0100 11111 00001 m:1 11111 11111 &reta # ERETAA, ERETAB 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 +# For the DSB nXS variant, types always equals MBReqTypes_All and we ignore the +# domain bits. +DSB_nXS 1101 0101 0000 0011 0011 -- 10 001 11111 ISB 1101 0101 0000 0011 0011 ---- 110 11111 SB 1101 0101 0000 0011 0011 0000 111 11111 @@ -206,6 +283,7 @@ 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 @@ -231,7 +309,7 @@ HLT 1101 0100 010 ................ 000 00 @i16 # DCPS2 1101 0100 101 ................ 000 10 @i16 # DCPS3 1101 0100 101 ................ 000 11 @i16 -# Loads and stores +### Loads and stores &stxr rn rt rt2 rs sz lasr &stlr rn rt sz lasr @@ -461,12 +539,12 @@ 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_2 +%ldra_imm 22:s1 12:9 !function=times_8 LDRA 11 111 0 00 m:1 . 1 ......... w:1 1 rn:5 rt:5 imm=%ldra_imm &ldapr_stlr_i rn rt imm sz sign ext -@ldapr_stlr_i .. ...... .. . imm:9 .. rn:5 rt:5 &ldapr_stlr_i +@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 @@ -553,3 +631,1266 @@ 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 + +### Data Processing (register) + +# Data Processing (2-source) + +@rrr . .......... rm:5 ...... rn:5 rd:5 &rrr +@rrr_sf sf:1 .......... rm:5 ...... rn:5 rd:5 &rrr_sf + +UDIV . 00 11010110 ..... 00001 0 ..... ..... @rrr_sf +SDIV . 00 11010110 ..... 00001 1 ..... ..... @rrr_sf +LSLV . 00 11010110 ..... 00100 0 ..... ..... @rrr_sf +LSRV . 00 11010110 ..... 00100 1 ..... ..... @rrr_sf +ASRV . 00 11010110 ..... 00101 0 ..... ..... @rrr_sf +RORV . 00 11010110 ..... 00101 1 ..... ..... @rrr_sf + +CRC32 0 00 11010110 ..... 0100 00 ..... ..... @rrr_b +CRC32 0 00 11010110 ..... 0100 01 ..... ..... @rrr_h +CRC32 0 00 11010110 ..... 0100 10 ..... ..... @rrr_s +CRC32 1 00 11010110 ..... 0100 11 ..... ..... @rrr_d + +CRC32C 0 00 11010110 ..... 0101 00 ..... ..... @rrr_b +CRC32C 0 00 11010110 ..... 0101 01 ..... ..... @rrr_h +CRC32C 0 00 11010110 ..... 0101 10 ..... ..... @rrr_s +CRC32C 1 00 11010110 ..... 0101 11 ..... ..... @rrr_d + +SUBP 1 00 11010110 ..... 000000 ..... ..... @rrr +SUBPS 1 01 11010110 ..... 000000 ..... ..... @rrr +IRG 1 00 11010110 ..... 000100 ..... ..... @rrr +GMI 1 00 11010110 ..... 000101 ..... ..... @rrr + +PACGA 1 00 11010110 ..... 001100 ..... ..... @rrr + +# Data Processing (1-source) + +@rr . .......... ..... ...... rn:5 rd:5 &rr +@rr_sf sf:1 .......... ..... ...... rn:5 rd:5 &rr_sf + +RBIT . 10 11010110 00000 000000 ..... ..... @rr_sf +REV16 . 10 11010110 00000 000001 ..... ..... @rr_sf +REV32 . 10 11010110 00000 000010 ..... ..... @rr_sf +REV64 1 10 11010110 00000 000011 ..... ..... @rr + +CLZ . 10 11010110 00000 000100 ..... ..... @rr_sf +CLS . 10 11010110 00000 000101 ..... ..... @rr_sf + +&pacaut rd rn z +@pacaut . .. ........ ..... .. z:1 ... rn:5 rd:5 &pacaut + +PACIA 1 10 11010110 00001 00.000 ..... ..... @pacaut +PACIB 1 10 11010110 00001 00.001 ..... ..... @pacaut +PACDA 1 10 11010110 00001 00.010 ..... ..... @pacaut +PACDB 1 10 11010110 00001 00.011 ..... ..... @pacaut + +AUTIA 1 10 11010110 00001 00.100 ..... ..... @pacaut +AUTIB 1 10 11010110 00001 00.101 ..... ..... @pacaut +AUTDA 1 10 11010110 00001 00.110 ..... ..... @pacaut +AUTDB 1 10 11010110 00001 00.111 ..... ..... @pacaut + +XPACI 1 10 11010110 00001 010000 11111 rd:5 +XPACD 1 10 11010110 00001 010001 11111 rd:5 + +# Logical (shifted reg) + +&logic_shift rd rn rm sf sa st n +@logic_shift sf:1 .. ..... st:2 n:1 rm:5 sa:6 rn:5 rd:5 &logic_shift + +AND_r . 00 01010 .. . ..... ...... ..... ..... @logic_shift +ORR_r . 01 01010 .. . ..... ...... ..... ..... @logic_shift +EOR_r . 10 01010 .. . ..... ...... ..... ..... @logic_shift +ANDS_r . 11 01010 .. . ..... ...... ..... ..... @logic_shift + +# Add/subtract (shifted reg) + +&addsub_shift rd rn rm sf sa st +@addsub_shift sf:1 .. ..... st:2 . rm:5 sa:6 rn:5 rd:5 &addsub_shift + +ADD_r . 00 01011 .. 0 ..... ...... ..... ..... @addsub_shift +SUB_r . 10 01011 .. 0 ..... ...... ..... ..... @addsub_shift +ADDS_r . 01 01011 .. 0 ..... ...... ..... ..... @addsub_shift +SUBS_r . 11 01011 .. 0 ..... ...... ..... ..... @addsub_shift + +# Add/subtract (extended reg) + +&addsub_ext rd rn rm sf sa st +@addsub_ext sf:1 .. ........ rm:5 st:3 sa:3 rn:5 rd:5 &addsub_ext + +ADD_ext . 00 01011001 ..... ... ... ..... ..... @addsub_ext +SUB_ext . 10 01011001 ..... ... ... ..... ..... @addsub_ext +ADDS_ext . 01 01011001 ..... ... ... ..... ..... @addsub_ext +SUBS_ext . 11 01011001 ..... ... ... ..... ..... @addsub_ext + +# Add/subtract (carry) + +ADC . 00 11010000 ..... 000000 ..... ..... @rrr_sf +ADCS . 01 11010000 ..... 000000 ..... ..... @rrr_sf +SBC . 10 11010000 ..... 000000 ..... ..... @rrr_sf +SBCS . 11 11010000 ..... 000000 ..... ..... @rrr_sf + +# Rotate right into flags + +RMIF 1 01 11010000 imm:6 00001 rn:5 0 mask:4 + +# Evaluate into flags + +SETF8 0 01 11010000 00000 000010 rn:5 01101 +SETF16 0 01 11010000 00000 010010 rn:5 01101 + +# Conditional compare + +CCMP sf:1 op:1 1 11010010 y:5 cond:4 imm:1 0 rn:5 0 nzcv:4 + +# Conditional select + +CSEL sf:1 else_inv:1 011010100 rm:5 cond:4 0 else_inc:1 rn:5 rd:5 + +# Data Processing (3-source) + +&rrrr rd rn rm ra +@rrrr . .. ........ rm:5 . ra:5 rn:5 rd:5 &rrrr + +MADD_w 0 00 11011000 ..... 0 ..... ..... ..... @rrrr +MSUB_w 0 00 11011000 ..... 1 ..... ..... ..... @rrrr +MADD_x 1 00 11011000 ..... 0 ..... ..... ..... @rrrr +MSUB_x 1 00 11011000 ..... 1 ..... ..... ..... @rrrr + +SMADDL 1 00 11011001 ..... 0 ..... ..... ..... @rrrr +SMSUBL 1 00 11011001 ..... 1 ..... ..... ..... @rrrr +UMADDL 1 00 11011101 ..... 0 ..... ..... ..... @rrrr +UMSUBL 1 00 11011101 ..... 1 ..... ..... ..... @rrrr + +SMULH 1 00 11011010 ..... 0 11111 ..... ..... @rrr +UMULH 1 00 11011110 ..... 0 11111 ..... ..... @rrr + +### 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 + +# Conversion between floating-point and fixed-point (general register) + +&fcvt rd rn esz sf shift +%fcvt_shift32 10:5 !function=rsub_32 +%fcvt_shift64 10:6 !function=rsub_64 + +@fcvt32 0 ....... .. ...... 1..... rn:5 rd:5 \ + &fcvt sf=0 esz=%esz_hsd shift=%fcvt_shift32 +@fcvt64 1 ....... .. ...... ...... rn:5 rd:5 \ + &fcvt sf=1 esz=%esz_hsd shift=%fcvt_shift64 + +SCVTF_g . 0011110 .. 000010 ...... ..... ..... @fcvt32 +SCVTF_g . 0011110 .. 000010 ...... ..... ..... @fcvt64 +UCVTF_g . 0011110 .. 000011 ...... ..... ..... @fcvt32 +UCVTF_g . 0011110 .. 000011 ...... ..... ..... @fcvt64 + +FCVTZS_g . 0011110 .. 011000 ...... ..... ..... @fcvt32 +FCVTZS_g . 0011110 .. 011000 ...... ..... ..... @fcvt64 +FCVTZU_g . 0011110 .. 011001 ...... ..... ..... @fcvt32 +FCVTZU_g . 0011110 .. 011001 ...... ..... ..... @fcvt64 + +# Conversion between floating-point and integer (general register) + +@icvt sf:1 ....... .. ...... ...... rn:5 rd:5 \ + &fcvt esz=%esz_hsd shift=0 + +SCVTF_g . 0011110 .. 100010 000000 ..... ..... @icvt +UCVTF_g . 0011110 .. 100011 000000 ..... ..... @icvt + +FCVTNS_g . 0011110 .. 100000 000000 ..... ..... @icvt +FCVTNU_g . 0011110 .. 100001 000000 ..... ..... @icvt +FCVTPS_g . 0011110 .. 101000 000000 ..... ..... @icvt +FCVTPU_g . 0011110 .. 101001 000000 ..... ..... @icvt +FCVTMS_g . 0011110 .. 110000 000000 ..... ..... @icvt +FCVTMU_g . 0011110 .. 110001 000000 ..... ..... @icvt +FCVTZS_g . 0011110 .. 111000 000000 ..... ..... @icvt +FCVTZU_g . 0011110 .. 111001 000000 ..... ..... @icvt +FCVTAS_g . 0011110 .. 100100 000000 ..... ..... @icvt +FCVTAU_g . 0011110 .. 100101 000000 ..... ..... @icvt + +FJCVTZS 0 0011110 01 111110 000000 ..... ..... @rr + +FMOV_ws 0 0011110 00 100110 000000 ..... ..... @rr +FMOV_sw 0 0011110 00 100111 000000 ..... ..... @rr + +FMOV_xd 1 0011110 01 100110 000000 ..... ..... @rr +FMOV_dx 1 0011110 01 100111 000000 ..... ..... @rr + +# Move to/from upper half of 128-bit +FMOV_xu 1 0011110 10 101110 000000 ..... ..... @rr +FMOV_ux 1 0011110 10 101111 000000 ..... ..... @rr + +# Half-precision allows both sf=0 and sf=1 with identical results +FMOV_xh - 0011110 11 100110 000000 ..... ..... @rr +FMOV_hx - 0011110 11 100111 000000 ..... ..... @rr + +# Floating-point data processing (1 source) + +FMOV_s 00011110 .. 1 000000 10000 ..... ..... @rr_hsd +FABS_s 00011110 .. 1 000001 10000 ..... ..... @rr_hsd +FNEG_s 00011110 .. 1 000010 10000 ..... ..... @rr_hsd +FSQRT_s 00011110 .. 1 000011 10000 ..... ..... @rr_hsd + +FRINTN_s 00011110 .. 1 001000 10000 ..... ..... @rr_hsd +FRINTP_s 00011110 .. 1 001001 10000 ..... ..... @rr_hsd +FRINTM_s 00011110 .. 1 001010 10000 ..... ..... @rr_hsd +FRINTZ_s 00011110 .. 1 001011 10000 ..... ..... @rr_hsd +FRINTA_s 00011110 .. 1 001100 10000 ..... ..... @rr_hsd +FRINTX_s 00011110 .. 1 001110 10000 ..... ..... @rr_hsd +FRINTI_s 00011110 .. 1 001111 10000 ..... ..... @rr_hsd + +BFCVT_s 00011110 01 1 000110 10000 ..... ..... @rr_s + +FRINT32Z_s 00011110 0. 1 010000 10000 ..... ..... @rr_sd +FRINT32X_s 00011110 0. 1 010001 10000 ..... ..... @rr_sd +FRINT64Z_s 00011110 0. 1 010010 10000 ..... ..... @rr_sd +FRINT64X_s 00011110 0. 1 010011 10000 ..... ..... @rr_sd + +FCVT_s_ds 00011110 00 1 000101 10000 ..... ..... @rr +FCVT_s_hs 00011110 00 1 000111 10000 ..... ..... @rr +FCVT_s_sd 00011110 01 1 000100 10000 ..... ..... @rr +FCVT_s_hd 00011110 01 1 000111 10000 ..... ..... @rr +FCVT_s_sh 00011110 11 1 000100 10000 ..... ..... @rr +FCVT_s_dh 00011110 11 1 000101 10000 ..... ..... @rr + +# Floating-point Immediate + +FMOVI_s 0001 1110 .. 1 imm:8 100 00000 rd:5 esz=%esz_hsd + +# Floating-point Compare + +FCMP 00011110 .. 1 rm:5 001000 rn:5 e:1 z:1 000 esz=%esz_hsd + +# Floating-point Conditional Compare + +FCCMP 00011110 .. 1 rm:5 cond:4 01 rn:5 e:1 nzcv:4 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 + +# Advanced SIMD scalar two-register miscellaneous + +SQABS_s 0101 1110 ..1 00000 01111 0 ..... ..... @rr_e +SQNEG_s 0111 1110 ..1 00000 01111 0 ..... ..... @rr_e +ABS_s 0101 1110 111 00000 10111 0 ..... ..... @rr +NEG_s 0111 1110 111 00000 10111 0 ..... ..... @rr +CMGT0_s 0101 1110 111 00000 10001 0 ..... ..... @rr +CMGE0_s 0111 1110 111 00000 10001 0 ..... ..... @rr +CMEQ0_s 0101 1110 111 00000 10011 0 ..... ..... @rr +CMLE0_s 0111 1110 111 00000 10011 0 ..... ..... @rr +CMLT0_s 0101 1110 111 00000 10101 0 ..... ..... @rr + +SQXTUN_s 0111 1110 ..1 00001 00101 0 ..... ..... @rr_e +SQXTN_s 0101 1110 ..1 00001 01001 0 ..... ..... @rr_e +UQXTN_s 0111 1110 ..1 00001 01001 0 ..... ..... @rr_e + +FCVTXN_s 0111 1110 011 00001 01101 0 ..... ..... @rr_s + +FCMGT0_s 0101 1110 111 11000 11001 0 ..... ..... @rr_h +FCMGT0_s 0101 1110 1.1 00000 11001 0 ..... ..... @rr_sd + +FCMGE0_s 0111 1110 111 11000 11001 0 ..... ..... @rr_h +FCMGE0_s 0111 1110 1.1 00000 11001 0 ..... ..... @rr_sd + +FCMEQ0_s 0101 1110 111 11000 11011 0 ..... ..... @rr_h +FCMEQ0_s 0101 1110 1.1 00000 11011 0 ..... ..... @rr_sd + +FCMLE0_s 0111 1110 111 11000 11011 0 ..... ..... @rr_h +FCMLE0_s 0111 1110 1.1 00000 11011 0 ..... ..... @rr_sd + +FCMLT0_s 0101 1110 111 11000 11101 0 ..... ..... @rr_h +FCMLT0_s 0101 1110 1.1 00000 11101 0 ..... ..... @rr_sd + +FRECPE_s 0101 1110 111 11001 11011 0 ..... ..... @rr_h +FRECPE_s 0101 1110 1.1 00001 11011 0 ..... ..... @rr_sd + +FRECPX_s 0101 1110 111 11001 11111 0 ..... ..... @rr_h +FRECPX_s 0101 1110 1.1 00001 11111 0 ..... ..... @rr_sd + +FRSQRTE_s 0111 1110 111 11001 11011 0 ..... ..... @rr_h +FRSQRTE_s 0111 1110 1.1 00001 11011 0 ..... ..... @rr_sd + +@icvt_h . ....... .. ...... ...... rn:5 rd:5 \ + &fcvt sf=0 esz=1 shift=0 +@icvt_sd . ....... .. ...... ...... rn:5 rd:5 \ + &fcvt sf=0 esz=%esz_sd shift=0 + +SCVTF_f 0101 1110 011 11001 11011 0 ..... ..... @icvt_h +SCVTF_f 0101 1110 0.1 00001 11011 0 ..... ..... @icvt_sd + +UCVTF_f 0111 1110 011 11001 11011 0 ..... ..... @icvt_h +UCVTF_f 0111 1110 0.1 00001 11011 0 ..... ..... @icvt_sd + +FCVTNS_f 0101 1110 011 11001 10101 0 ..... ..... @icvt_h +FCVTNS_f 0101 1110 0.1 00001 10101 0 ..... ..... @icvt_sd +FCVTNU_f 0111 1110 011 11001 10101 0 ..... ..... @icvt_h +FCVTNU_f 0111 1110 0.1 00001 10101 0 ..... ..... @icvt_sd + +FCVTPS_f 0101 1110 111 11001 10101 0 ..... ..... @icvt_h +FCVTPS_f 0101 1110 1.1 00001 10101 0 ..... ..... @icvt_sd +FCVTPU_f 0111 1110 111 11001 10101 0 ..... ..... @icvt_h +FCVTPU_f 0111 1110 1.1 00001 10101 0 ..... ..... @icvt_sd + +FCVTMS_f 0101 1110 011 11001 10111 0 ..... ..... @icvt_h +FCVTMS_f 0101 1110 0.1 00001 10111 0 ..... ..... @icvt_sd +FCVTMU_f 0111 1110 011 11001 10111 0 ..... ..... @icvt_h +FCVTMU_f 0111 1110 0.1 00001 10111 0 ..... ..... @icvt_sd + +FCVTZS_f 0101 1110 111 11001 10111 0 ..... ..... @icvt_h +FCVTZS_f 0101 1110 1.1 00001 10111 0 ..... ..... @icvt_sd +FCVTZU_f 0111 1110 111 11001 10111 0 ..... ..... @icvt_h +FCVTZU_f 0111 1110 1.1 00001 10111 0 ..... ..... @icvt_sd + +FCVTAS_f 0101 1110 011 11001 11001 0 ..... ..... @icvt_h +FCVTAS_f 0101 1110 0.1 00001 11001 0 ..... ..... @icvt_sd +FCVTAU_f 0111 1110 011 11001 11001 0 ..... ..... @icvt_h +FCVTAU_f 0111 1110 0.1 00001 11001 0 ..... ..... @icvt_sd + +%fcvt_f_sh_h 16:4 !function=rsub_16 +%fcvt_f_sh_s 16:5 !function=rsub_32 +%fcvt_f_sh_d 16:6 !function=rsub_64 + +@fcvt_fixed_h .... .... . 001 .... ...... rn:5 rd:5 \ + &fcvt sf=0 esz=1 shift=%fcvt_f_sh_h +@fcvt_fixed_s .... .... . 01 ..... ...... rn:5 rd:5 \ + &fcvt sf=0 esz=2 shift=%fcvt_f_sh_s +@fcvt_fixed_d .... .... . 1 ...... ...... rn:5 rd:5 \ + &fcvt sf=0 esz=3 shift=%fcvt_f_sh_d + +SCVTF_f 0101 1111 0 ....... 111001 ..... ..... @fcvt_fixed_h +SCVTF_f 0101 1111 0 ....... 111001 ..... ..... @fcvt_fixed_s +SCVTF_f 0101 1111 0 ....... 111001 ..... ..... @fcvt_fixed_d + +UCVTF_f 0111 1111 0 ....... 111001 ..... ..... @fcvt_fixed_h +UCVTF_f 0111 1111 0 ....... 111001 ..... ..... @fcvt_fixed_s +UCVTF_f 0111 1111 0 ....... 111001 ..... ..... @fcvt_fixed_d + +FCVTZS_f 0101 1111 0 ....... 111111 ..... ..... @fcvt_fixed_h +FCVTZS_f 0101 1111 0 ....... 111111 ..... ..... @fcvt_fixed_s +FCVTZS_f 0101 1111 0 ....... 111111 ..... ..... @fcvt_fixed_d + +FCVTZU_f 0111 1111 0 ....... 111111 ..... ..... @fcvt_fixed_h +FCVTZU_f 0111 1111 0 ....... 111111 ..... ..... @fcvt_fixed_s +FCVTZU_f 0111 1111 0 ....... 111111 ..... ..... @fcvt_fixed_d + +# Advanced SIMD two-register miscellaneous + +SQABS_v 0.00 1110 ..1 00000 01111 0 ..... ..... @qrr_e +SQNEG_v 0.10 1110 ..1 00000 01111 0 ..... ..... @qrr_e +ABS_v 0.00 1110 ..1 00000 10111 0 ..... ..... @qrr_e +NEG_v 0.10 1110 ..1 00000 10111 0 ..... ..... @qrr_e +CLS_v 0.00 1110 ..1 00000 01001 0 ..... ..... @qrr_e +CLZ_v 0.10 1110 ..1 00000 01001 0 ..... ..... @qrr_e +CNT_v 0.00 1110 001 00000 01011 0 ..... ..... @qrr_b +NOT_v 0.10 1110 001 00000 01011 0 ..... ..... @qrr_b +RBIT_v 0.10 1110 011 00000 01011 0 ..... ..... @qrr_b +CMGT0_v 0.00 1110 ..1 00000 10001 0 ..... ..... @qrr_e +CMGE0_v 0.10 1110 ..1 00000 10001 0 ..... ..... @qrr_e +CMEQ0_v 0.00 1110 ..1 00000 10011 0 ..... ..... @qrr_e +CMLE0_v 0.10 1110 ..1 00000 10011 0 ..... ..... @qrr_e +CMLT0_v 0.00 1110 ..1 00000 10101 0 ..... ..... @qrr_e + +REV16_v 0.00 1110 001 00000 00011 0 ..... ..... @qrr_b +REV32_v 0.10 1110 0.1 00000 00001 0 ..... ..... @qrr_bh +REV64_v 0.00 1110 ..1 00000 00001 0 ..... ..... @qrr_e + +SADDLP_v 0.00 1110 ..1 00000 00101 0 ..... ..... @qrr_e +UADDLP_v 0.10 1110 ..1 00000 00101 0 ..... ..... @qrr_e +SADALP_v 0.00 1110 ..1 00000 01101 0 ..... ..... @qrr_e +UADALP_v 0.10 1110 ..1 00000 01101 0 ..... ..... @qrr_e + +XTN 0.00 1110 ..1 00001 00101 0 ..... ..... @qrr_e +SQXTUN_v 0.10 1110 ..1 00001 00101 0 ..... ..... @qrr_e +SQXTN_v 0.00 1110 ..1 00001 01001 0 ..... ..... @qrr_e +UQXTN_v 0.10 1110 ..1 00001 01001 0 ..... ..... @qrr_e + +FCVTN_v 0.00 1110 0.1 00001 01101 0 ..... ..... @qrr_hs +FCVTXN_v 0.10 1110 011 00001 01101 0 ..... ..... @qrr_s +BFCVTN_v 0.00 1110 101 00001 01101 0 ..... ..... @qrr_h + +SHLL_v 0.10 1110 ..1 00001 00111 0 ..... ..... @qrr_e + +FABS_v 0.00 1110 111 11000 11111 0 ..... ..... @qrr_h +FABS_v 0.00 1110 1.1 00000 11111 0 ..... ..... @qrr_sd + +FNEG_v 0.10 1110 111 11000 11111 0 ..... ..... @qrr_h +FNEG_v 0.10 1110 1.1 00000 11111 0 ..... ..... @qrr_sd + +FSQRT_v 0.10 1110 111 11001 11111 0 ..... ..... @qrr_h +FSQRT_v 0.10 1110 1.1 00001 11111 0 ..... ..... @qrr_sd + +FRINTN_v 0.00 1110 011 11001 10001 0 ..... ..... @qrr_h +FRINTN_v 0.00 1110 0.1 00001 10001 0 ..... ..... @qrr_sd + +FRINTM_v 0.00 1110 011 11001 10011 0 ..... ..... @qrr_h +FRINTM_v 0.00 1110 0.1 00001 10011 0 ..... ..... @qrr_sd + +FRINTP_v 0.00 1110 111 11001 10001 0 ..... ..... @qrr_h +FRINTP_v 0.00 1110 1.1 00001 10001 0 ..... ..... @qrr_sd + +FRINTZ_v 0.00 1110 111 11001 10011 0 ..... ..... @qrr_h +FRINTZ_v 0.00 1110 1.1 00001 10011 0 ..... ..... @qrr_sd + +FRINTA_v 0.10 1110 011 11001 10001 0 ..... ..... @qrr_h +FRINTA_v 0.10 1110 0.1 00001 10001 0 ..... ..... @qrr_sd + +FRINTX_v 0.10 1110 011 11001 10011 0 ..... ..... @qrr_h +FRINTX_v 0.10 1110 0.1 00001 10011 0 ..... ..... @qrr_sd + +FRINTI_v 0.10 1110 111 11001 10011 0 ..... ..... @qrr_h +FRINTI_v 0.10 1110 1.1 00001 10011 0 ..... ..... @qrr_sd + +FRINT32Z_v 0.00 1110 0.1 00001 11101 0 ..... ..... @qrr_sd +FRINT32X_v 0.10 1110 0.1 00001 11101 0 ..... ..... @qrr_sd +FRINT64Z_v 0.00 1110 0.1 00001 11111 0 ..... ..... @qrr_sd +FRINT64X_v 0.10 1110 0.1 00001 11111 0 ..... ..... @qrr_sd + +SCVTF_vi 0.00 1110 011 11001 11011 0 ..... ..... @qrr_h +SCVTF_vi 0.00 1110 0.1 00001 11011 0 ..... ..... @qrr_sd + +UCVTF_vi 0.10 1110 011 11001 11011 0 ..... ..... @qrr_h +UCVTF_vi 0.10 1110 0.1 00001 11011 0 ..... ..... @qrr_sd + +FCVTNS_vi 0.00 1110 011 11001 10101 0 ..... ..... @qrr_h +FCVTNS_vi 0.00 1110 0.1 00001 10101 0 ..... ..... @qrr_sd +FCVTNU_vi 0.10 1110 011 11001 10101 0 ..... ..... @qrr_h +FCVTNU_vi 0.10 1110 0.1 00001 10101 0 ..... ..... @qrr_sd + +FCVTPS_vi 0.00 1110 111 11001 10101 0 ..... ..... @qrr_h +FCVTPS_vi 0.00 1110 1.1 00001 10101 0 ..... ..... @qrr_sd +FCVTPU_vi 0.10 1110 111 11001 10101 0 ..... ..... @qrr_h +FCVTPU_vi 0.10 1110 1.1 00001 10101 0 ..... ..... @qrr_sd + +FCVTMS_vi 0.00 1110 011 11001 10111 0 ..... ..... @qrr_h +FCVTMS_vi 0.00 1110 0.1 00001 10111 0 ..... ..... @qrr_sd +FCVTMU_vi 0.10 1110 011 11001 10111 0 ..... ..... @qrr_h +FCVTMU_vi 0.10 1110 0.1 00001 10111 0 ..... ..... @qrr_sd + +FCVTZS_vi 0.00 1110 111 11001 10111 0 ..... ..... @qrr_h +FCVTZS_vi 0.00 1110 1.1 00001 10111 0 ..... ..... @qrr_sd +FCVTZU_vi 0.10 1110 111 11001 10111 0 ..... ..... @qrr_h +FCVTZU_vi 0.10 1110 1.1 00001 10111 0 ..... ..... @qrr_sd + +FCVTAS_vi 0.00 1110 011 11001 11001 0 ..... ..... @qrr_h +FCVTAS_vi 0.00 1110 0.1 00001 11001 0 ..... ..... @qrr_sd +FCVTAU_vi 0.10 1110 011 11001 11001 0 ..... ..... @qrr_h +FCVTAU_vi 0.10 1110 0.1 00001 11001 0 ..... ..... @qrr_sd + +FCMGT0_v 0.00 1110 111 11000 11001 0 ..... ..... @qrr_h +FCMGT0_v 0.00 1110 1.1 00000 11001 0 ..... ..... @qrr_sd + +FCMGE0_v 0.10 1110 111 11000 11001 0 ..... ..... @qrr_h +FCMGE0_v 0.10 1110 1.1 00000 11001 0 ..... ..... @qrr_sd + +FCMEQ0_v 0.00 1110 111 11000 11011 0 ..... ..... @qrr_h +FCMEQ0_v 0.00 1110 1.1 00000 11011 0 ..... ..... @qrr_sd + +FCMLE0_v 0.10 1110 111 11000 11011 0 ..... ..... @qrr_h +FCMLE0_v 0.10 1110 1.1 00000 11011 0 ..... ..... @qrr_sd + +FCMLT0_v 0.00 1110 111 11000 11101 0 ..... ..... @qrr_h +FCMLT0_v 0.00 1110 1.1 00000 11101 0 ..... ..... @qrr_sd + +FRECPE_v 0.00 1110 111 11001 11011 0 ..... ..... @qrr_h +FRECPE_v 0.00 1110 1.1 00001 11011 0 ..... ..... @qrr_sd + +FRSQRTE_v 0.10 1110 111 11001 11011 0 ..... ..... @qrr_h +FRSQRTE_v 0.10 1110 1.1 00001 11011 0 ..... ..... @qrr_sd + +URECPE_v 0.00 1110 101 00001 11001 0 ..... ..... @qrr_s +URSQRTE_v 0.10 1110 101 00001 11001 0 ..... ..... @qrr_s + +FCVTL_v 0.00 1110 0.1 00001 01111 0 ..... ..... @qrr_sd + +&fcvt_q rd rn esz q shift +@fcvtq_h . q:1 . ...... 001 .... ...... rn:5 rd:5 \ + &fcvt_q esz=1 shift=%fcvt_f_sh_h +@fcvtq_s . q:1 . ...... 01 ..... ...... rn:5 rd:5 \ + &fcvt_q esz=2 shift=%fcvt_f_sh_s +@fcvtq_d . q:1 . ...... 1 ...... ...... rn:5 rd:5 \ + &fcvt_q esz=3 shift=%fcvt_f_sh_d + +SCVTF_vf 0.00 11110 ....... 111001 ..... ..... @fcvtq_h +SCVTF_vf 0.00 11110 ....... 111001 ..... ..... @fcvtq_s +SCVTF_vf 0.00 11110 ....... 111001 ..... ..... @fcvtq_d + +UCVTF_vf 0.10 11110 ....... 111001 ..... ..... @fcvtq_h +UCVTF_vf 0.10 11110 ....... 111001 ..... ..... @fcvtq_s +UCVTF_vf 0.10 11110 ....... 111001 ..... ..... @fcvtq_d + +FCVTZS_vf 0.00 11110 ....... 111111 ..... ..... @fcvtq_h +FCVTZS_vf 0.00 11110 ....... 111111 ..... ..... @fcvtq_s +FCVTZS_vf 0.00 11110 ....... 111111 ..... ..... @fcvtq_d + +FCVTZU_vf 0.10 11110 ....... 111111 ..... ..... @fcvtq_h +FCVTZU_vf 0.10 11110 ....... 111111 ..... ..... @fcvtq_s +FCVTZU_vf 0.10 11110 ....... 111111 ..... ..... @fcvtq_d diff --git a/target/arm/tcg/arith_helper.c b/target/arm/tcg/arith_helper.c new file mode 100644 index 00000000000..670139819df --- /dev/null +++ b/target/arm/tcg/arith_helper.c @@ -0,0 +1,297 @@ +/* + * ARM generic helpers for various arithmetical operations. + * + * This code is licensed under the GNU GPL v2 or later. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "qemu/crc32c.h" +#include /* for crc32 */ + +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" + +/* + * 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. */ + +/* Perform 16-bit signed saturating addition. */ +static inline uint16_t add16_sat(uint16_t a, uint16_t b) +{ + uint16_t res; + + res = a + b; + if (((res ^ a) & 0x8000) && !((a ^ b) & 0x8000)) { + if (a & 0x8000) { + res = 0x8000; + } else { + res = 0x7fff; + } + } + return res; +} + +/* Perform 8-bit signed saturating addition. */ +static inline uint8_t add8_sat(uint8_t a, uint8_t b) +{ + uint8_t res; + + res = a + b; + if (((res ^ a) & 0x80) && !((a ^ b) & 0x80)) { + if (a & 0x80) { + res = 0x80; + } else { + res = 0x7f; + } + } + return res; +} + +/* Perform 16-bit signed saturating subtraction. */ +static inline uint16_t sub16_sat(uint16_t a, uint16_t b) +{ + uint16_t res; + + res = a - b; + if (((res ^ a) & 0x8000) && ((a ^ b) & 0x8000)) { + if (a & 0x8000) { + res = 0x8000; + } else { + res = 0x7fff; + } + } + return res; +} + +/* Perform 8-bit signed saturating subtraction. */ +static inline uint8_t sub8_sat(uint8_t a, uint8_t b) +{ + uint8_t res; + + res = a - b; + if (((res ^ a) & 0x80) && ((a ^ b) & 0x80)) { + if (a & 0x80) { + res = 0x80; + } else { + res = 0x7f; + } + } + return res; +} + +#define ADD16(a, b, n) RESULT(add16_sat(a, b), n, 16); +#define SUB16(a, b, n) RESULT(sub16_sat(a, b), n, 16); +#define ADD8(a, b, n) RESULT(add8_sat(a, b), n, 8); +#define SUB8(a, b, n) RESULT(sub8_sat(a, b), n, 8); +#define PFX q + +#include "op_addsub.c.inc" + +/* Unsigned saturating arithmetic. */ +static inline uint16_t add16_usat(uint16_t a, uint16_t b) +{ + uint16_t res; + res = a + b; + if (res < a) { + res = 0xffff; + } + return res; +} + +static inline uint16_t sub16_usat(uint16_t a, uint16_t b) +{ + if (a > b) { + return a - b; + } else { + return 0; + } +} + +static inline uint8_t add8_usat(uint8_t a, uint8_t b) +{ + uint8_t res; + res = a + b; + if (res < a) { + res = 0xff; + } + return res; +} + +static inline uint8_t sub8_usat(uint8_t a, uint8_t b) +{ + if (a > b) { + return a - b; + } else { + return 0; + } +} + +#define ADD16(a, b, n) RESULT(add16_usat(a, b), n, 16); +#define SUB16(a, b, n) RESULT(sub16_usat(a, b), n, 16); +#define ADD8(a, b, n) RESULT(add8_usat(a, b), n, 8); +#define SUB8(a, b, n) RESULT(sub8_usat(a, b), n, 8); +#define PFX uq + +#include "op_addsub.c.inc" + +/* Signed modulo arithmetic. */ +#define SARITH16(a, b, n, op) do { \ + int32_t sum; \ + sum = (int32_t)(int16_t)(a) op (int32_t)(int16_t)(b); \ + RESULT(sum, n, 16); \ + if (sum >= 0) \ + ge |= 3 << (n * 2); \ + } while (0) + +#define SARITH8(a, b, n, op) do { \ + int32_t sum; \ + sum = (int32_t)(int8_t)(a) op (int32_t)(int8_t)(b); \ + RESULT(sum, n, 8); \ + if (sum >= 0) \ + ge |= 1 << n; \ + } while (0) + + +#define ADD16(a, b, n) SARITH16(a, b, n, +) +#define SUB16(a, b, n) SARITH16(a, b, n, -) +#define ADD8(a, b, n) SARITH8(a, b, n, +) +#define SUB8(a, b, n) SARITH8(a, b, n, -) +#define PFX s +#define ARITH_GE + +#include "op_addsub.c.inc" + +/* Unsigned modulo arithmetic. */ +#define ADD16(a, b, n) do { \ + uint32_t sum; \ + sum = (uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b); \ + RESULT(sum, n, 16); \ + if ((sum >> 16) == 1) \ + ge |= 3 << (n * 2); \ + } while (0) + +#define ADD8(a, b, n) do { \ + uint32_t sum; \ + sum = (uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b); \ + RESULT(sum, n, 8); \ + if ((sum >> 8) == 1) \ + ge |= 1 << n; \ + } while (0) + +#define SUB16(a, b, n) do { \ + uint32_t sum; \ + sum = (uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b); \ + RESULT(sum, n, 16); \ + if ((sum >> 16) == 0) \ + ge |= 3 << (n * 2); \ + } while (0) + +#define SUB8(a, b, n) do { \ + uint32_t sum; \ + sum = (uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b); \ + RESULT(sum, n, 8); \ + if ((sum >> 8) == 0) \ + ge |= 1 << n; \ + } while (0) + +#define PFX u +#define ARITH_GE + +#include "op_addsub.c.inc" + +/* Halved signed arithmetic. */ +#define ADD16(a, b, n) \ + RESULT(((int32_t)(int16_t)(a) + (int32_t)(int16_t)(b)) >> 1, n, 16) +#define SUB16(a, b, n) \ + RESULT(((int32_t)(int16_t)(a) - (int32_t)(int16_t)(b)) >> 1, n, 16) +#define ADD8(a, b, n) \ + RESULT(((int32_t)(int8_t)(a) + (int32_t)(int8_t)(b)) >> 1, n, 8) +#define SUB8(a, b, n) \ + RESULT(((int32_t)(int8_t)(a) - (int32_t)(int8_t)(b)) >> 1, n, 8) +#define PFX sh + +#include "op_addsub.c.inc" + +/* Halved unsigned arithmetic. */ +#define ADD16(a, b, n) \ + RESULT(((uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b)) >> 1, n, 16) +#define SUB16(a, b, n) \ + RESULT(((uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b)) >> 1, n, 16) +#define ADD8(a, b, n) \ + RESULT(((uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b)) >> 1, n, 8) +#define SUB8(a, b, n) \ + RESULT(((uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b)) >> 1, n, 8) +#define PFX uh + +#include "op_addsub.c.inc" + +static inline uint8_t do_usad(uint8_t a, uint8_t b) +{ + if (a > b) { + return a - b; + } else { + return b - a; + } +} + +/* Unsigned sum of absolute byte differences. */ +uint32_t HELPER(usad8)(uint32_t a, uint32_t b) +{ + uint32_t sum; + sum = do_usad(a, b); + sum += do_usad(a >> 8, b >> 8); + sum += do_usad(a >> 16, b >> 16); + sum += do_usad(a >> 24, b >> 24); + return sum; +} + +/* For ARMv6 SEL instruction. */ +uint32_t HELPER(sel_flags)(uint32_t flags, uint32_t a, uint32_t b) +{ + uint32_t mask; + + mask = 0; + if (flags & 1) { + mask |= 0xff; + } + if (flags & 2) { + mask |= 0xff00; + } + if (flags & 4) { + mask |= 0xff0000; + } + if (flags & 8) { + mask |= 0xff000000; + } + return (a & mask) | (b & ~mask); +} + +/* + * CRC helpers. + * The upper bytes of val (above the number specified by 'bytes') must have + * been zeroed out by the caller. + */ +uint32_t HELPER(crc32)(uint32_t acc, uint32_t val, uint32_t bytes) +{ + uint8_t buf[4]; + + stl_le_p(buf, val); + + /* zlib crc32 converts the accumulator and output to one's complement. */ + return crc32(acc ^ 0xffffffff, buf, bytes) ^ 0xffffffff; +} + +uint32_t HELPER(crc32c)(uint32_t acc, uint32_t val, uint32_t bytes) +{ + uint8_t buf[4]; + + stl_le_p(buf, val); + + /* Linux crc32c converts the output to one's complement. */ + return crc32c(acc, buf, bytes) ^ 0xffffffff; +} diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c new file mode 100644 index 00000000000..8e1a083b911 --- /dev/null +++ b/target/arm/tcg/cpu-v7m.c @@ -0,0 +1,298 @@ +/* + * 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 "accel/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) +{ + 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; + cs->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 = { + /* ARM processors have a weak memory model */ + .guest_default_memory_order = 0, + .mttcg_supported = true, + + .initialize = arm_translate_init, + .translate_code = arm_translate_code, + .get_tb_cpu_state = arm_get_tb_cpu_state, + .synchronize_from_tb = arm_cpu_synchronize_from_tb, + .debug_excp_handler = arm_debug_excp_handler, + .restore_state_to_opc = arm_restore_state_to_opc, + .mmu_index = arm_cpu_mmu_index, + +#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, + .pointer_wrap = cpu_pointer_wrap_uint32, + .cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt, + .cpu_exec_halt = arm_cpu_exec_halt, + .cpu_exec_reset = cpu_reset, + .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, const void *data) +{ + ARMCPUClass *acc = ARM_CPU_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); + + acc->info = data; + cc->tcg_ops = &arm_v7m_tcg_ops; +} + +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 index 1f918ff5375..2c45b7eddda 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -10,16 +10,13 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "hw/core/tcg-cpu-ops.h" +#include "accel/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" -#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG) -#include "hw/intc/armv7m_nvic.h" -#endif /* Share AArch32 -cpu max features with AArch64. */ @@ -70,11 +67,11 @@ void aa32_max_features(ARMCPU *cpu) cpu->isar.id_mmfr4 = t; t = cpu->isar.id_mmfr5; - t = FIELD_DP32(t, ID_MMFR5, ETS, 1); /* FEAT_ETS */ + 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_CVS2 */ + 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; @@ -85,41 +82,47 @@ void aa32_max_features(ARMCPU *cpu) 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, 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) -#if !defined(CONFIG_USER_ONLY) -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 arm926_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -347,6 +350,7 @@ static void cortex_a8_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); cpu->midr = 0x410fc080; cpu->reset_fpsid = 0x410330c0; cpu->isar.mvfr0 = 0x11110222; @@ -414,6 +418,7 @@ static void cortex_a9_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); /* * Note that A9 supports the MP extensions even for * A9UP and single-core A9MP (which are both different @@ -480,6 +485,7 @@ static void cortex_a7_initfn(Object *obj) 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); @@ -528,6 +534,7 @@ static void cortex_a15_initfn(Object *obj) 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); @@ -565,200 +572,11 @@ static void cortex_a15_initfn(Object *obj) define_arm_cp_regs(cpu, cortexa15_cp_reginfo); } -static void cortex_m0_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - set_feature(&cpu->env, ARM_FEATURE_V6); - set_feature(&cpu->env, ARM_FEATURE_M); - - cpu->midr = 0x410cc200; - - /* - * These ID register values are not guest visible, because - * we do not implement the Main Extension. They must be set - * to values corresponding to the Cortex-M0's implemented - * features, because QEMU generally controls its emulation - * by looking at ID register fields. We use the same values as - * for the M3. - */ - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000200; - cpu->isar.id_dfr0 = 0x00100000; - cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00000030; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x00000000; - cpu->isar.id_mmfr3 = 0x00000000; - cpu->isar.id_isar0 = 0x01141110; - cpu->isar.id_isar1 = 0x02111000; - cpu->isar.id_isar2 = 0x21112231; - cpu->isar.id_isar3 = 0x01111110; - cpu->isar.id_isar4 = 0x01310102; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; -} - -static void cortex_m3_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - set_feature(&cpu->env, ARM_FEATURE_V7); - set_feature(&cpu->env, ARM_FEATURE_M); - set_feature(&cpu->env, ARM_FEATURE_M_MAIN); - cpu->midr = 0x410fc231; - cpu->pmsav7_dregion = 8; - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000200; - cpu->isar.id_dfr0 = 0x00100000; - cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00000030; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x00000000; - cpu->isar.id_mmfr3 = 0x00000000; - cpu->isar.id_isar0 = 0x01141110; - cpu->isar.id_isar1 = 0x02111000; - cpu->isar.id_isar2 = 0x21112231; - cpu->isar.id_isar3 = 0x01111110; - cpu->isar.id_isar4 = 0x01310102; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; -} - -static void cortex_m4_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - set_feature(&cpu->env, ARM_FEATURE_V7); - set_feature(&cpu->env, ARM_FEATURE_M); - set_feature(&cpu->env, ARM_FEATURE_M_MAIN); - set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); - cpu->midr = 0x410fc240; /* r0p0 */ - cpu->pmsav7_dregion = 8; - cpu->isar.mvfr0 = 0x10110021; - cpu->isar.mvfr1 = 0x11000011; - cpu->isar.mvfr2 = 0x00000000; - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000200; - cpu->isar.id_dfr0 = 0x00100000; - cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00000030; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x00000000; - cpu->isar.id_mmfr3 = 0x00000000; - cpu->isar.id_isar0 = 0x01141110; - cpu->isar.id_isar1 = 0x02111000; - cpu->isar.id_isar2 = 0x21112231; - cpu->isar.id_isar3 = 0x01111110; - cpu->isar.id_isar4 = 0x01310102; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; -} - -static void cortex_m7_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - set_feature(&cpu->env, ARM_FEATURE_V7); - set_feature(&cpu->env, ARM_FEATURE_M); - set_feature(&cpu->env, ARM_FEATURE_M_MAIN); - set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); - cpu->midr = 0x411fc272; /* r1p2 */ - cpu->pmsav7_dregion = 8; - cpu->isar.mvfr0 = 0x10110221; - cpu->isar.mvfr1 = 0x12000011; - cpu->isar.mvfr2 = 0x00000040; - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000200; - cpu->isar.id_dfr0 = 0x00100000; - cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00100030; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x01000000; - cpu->isar.id_mmfr3 = 0x00000000; - cpu->isar.id_isar0 = 0x01101110; - cpu->isar.id_isar1 = 0x02112000; - cpu->isar.id_isar2 = 0x20232231; - cpu->isar.id_isar3 = 0x01111131; - cpu->isar.id_isar4 = 0x01310132; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; -} - -static void cortex_m33_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_M); - set_feature(&cpu->env, ARM_FEATURE_M_MAIN); - set_feature(&cpu->env, ARM_FEATURE_M_SECURITY); - set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); - cpu->midr = 0x410fd213; /* r0p3 */ - cpu->pmsav7_dregion = 16; - cpu->sau_sregion = 8; - cpu->isar.mvfr0 = 0x10110021; - cpu->isar.mvfr1 = 0x11000011; - cpu->isar.mvfr2 = 0x00000040; - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000210; - cpu->isar.id_dfr0 = 0x00200000; - cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00101F40; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x01000000; - cpu->isar.id_mmfr3 = 0x00000000; - cpu->isar.id_isar0 = 0x01101110; - cpu->isar.id_isar1 = 0x02212000; - cpu->isar.id_isar2 = 0x20232232; - cpu->isar.id_isar3 = 0x01111131; - cpu->isar.id_isar4 = 0x01310132; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; - cpu->clidr = 0x00000000; - cpu->ctr = 0x8000c000; -} - -static void cortex_m55_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_V8_1M); - set_feature(&cpu->env, ARM_FEATURE_M); - set_feature(&cpu->env, ARM_FEATURE_M_MAIN); - set_feature(&cpu->env, ARM_FEATURE_M_SECURITY); - set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); - cpu->midr = 0x410fd221; /* r0p1 */ - cpu->revidr = 0; - cpu->pmsav7_dregion = 16; - cpu->sau_sregion = 8; - /* These are the MVFR* values for the FPU + full MVE configuration */ - cpu->isar.mvfr0 = 0x10110221; - cpu->isar.mvfr1 = 0x12100211; - cpu->isar.mvfr2 = 0x00000040; - cpu->isar.id_pfr0 = 0x20000030; - cpu->isar.id_pfr1 = 0x00000230; - cpu->isar.id_dfr0 = 0x10200000; - cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00111040; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x01000000; - cpu->isar.id_mmfr3 = 0x00000011; - cpu->isar.id_isar0 = 0x01103110; - cpu->isar.id_isar1 = 0x02212000; - cpu->isar.id_isar2 = 0x20232232; - cpu->isar.id_isar3 = 0x01111131; - cpu->isar.id_isar4 = 0x01310132; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; - cpu->clidr = 0x00000000; /* caches not implemented */ - cpu->ctr = 0x8303c003; -} - static const ARMCPRegInfo cortexr5_cp_reginfo[] = { /* Dummy the TCM region regs for the moment */ - { .name = "ATCM", .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 0, + { .name = "BTCM", .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, + { .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 }, @@ -794,6 +612,111 @@ static void cortex_r5_initfn(Object *obj) define_arm_cp_regs(cpu, cortexr5_cp_reginfo); } +static const ARMCPRegInfo cortex_r52_cp_reginfo[] = { + { .name = "CPUACTLR", .cp = 15, .opc1 = 0, .crm = 15, + .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, + { .name = "IMP_ATCMREGIONR", + .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_BTCMREGIONR", + .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_CTCMREGIONR", + .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_CSCTLR", + .cp = 15, .opc1 = 1, .crn = 9, .crm = 1, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_BPCTLR", + .cp = 15, .opc1 = 1, .crn = 9, .crm = 1, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_MEMPROTCLR", + .cp = 15, .opc1 = 1, .crn = 9, .crm = 1, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_SLAVEPCTLR", + .cp = 15, .opc1 = 0, .crn = 11, .crm = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_PERIPHREGIONR", + .cp = 15, .opc1 = 0, .crn = 15, .crm = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_FLASHIFREGIONR", + .cp = 15, .opc1 = 0, .crn = 15, .crm = 0, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_BUILDOPTR", + .cp = 15, .opc1 = 0, .crn = 15, .crm = 2, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_PINOPTR", + .cp = 15, .opc1 = 0, .crn = 15, .crm = 2, .opc2 = 7, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_QOSR", + .cp = 15, .opc1 = 1, .crn = 15, .crm = 3, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_BUSTIMEOUTR", + .cp = 15, .opc1 = 1, .crn = 15, .crm = 3, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_INTMONR", + .cp = 15, .opc1 = 1, .crn = 15, .crm = 3, .opc2 = 4, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_ICERR0", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_ICERR1", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 0, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_DCERR0", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 1, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_DCERR1", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 1, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_TCMERR0", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 2, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_TCMERR1", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 2, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_TCMSYNDR0", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 2, .opc2 = 2, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_TCMSYNDR1", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 2, .opc2 = 3, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_FLASHERR0", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 3, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_FLASHERR1", + .cp = 15, .opc1 = 2, .crn = 15, .crm = 3, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_CDBGDR0", + .cp = 15, .opc1 = 3, .crn = 15, .crm = 0, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_CBDGBR1", + .cp = 15, .opc1 = 3, .crn = 15, .crm = 0, .opc2 = 1, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_TESTR0", + .cp = 15, .opc1 = 4, .crn = 15, .crm = 0, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "IMP_TESTR1", + .cp = 15, .opc1 = 4, .crn = 15, .crm = 0, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NOP, .resetvalue = 0 }, + { .name = "IMP_CDBGDCI", + .cp = 15, .opc1 = 0, .crn = 15, .crm = 15, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NOP, .resetvalue = 0 }, + { .name = "IMP_CDBGDCT", + .cp = 15, .opc1 = 3, .crn = 15, .crm = 2, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NOP, .resetvalue = 0 }, + { .name = "IMP_CDBGICT", + .cp = 15, .opc1 = 3, .crn = 15, .crm = 2, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NOP, .resetvalue = 0 }, + { .name = "IMP_CDBGDCD", + .cp = 15, .opc1 = 3, .crn = 15, .crm = 4, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NOP, .resetvalue = 0 }, + { .name = "IMP_CDBGICD", + .cp = 15, .opc1 = 3, .crn = 15, .crm = 4, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NOP, .resetvalue = 0 }, +}; + + static void cortex_r52_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -803,6 +726,9 @@ static void cortex_r52_initfn(Object *obj) 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; @@ -833,6 +759,8 @@ static void cortex_r52_initfn(Object *obj) cpu->pmsav7_dregion = 16; cpu->pmsav8r_hdregion = 16; + + define_arm_cp_regs(cpu, cortex_r52_cp_reginfo); } static void cortex_r5f_initfn(Object *obj) @@ -1012,37 +940,6 @@ static void pxa270c5_initfn(Object *obj) cpu->reset_sctlr = 0x00000078; } -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 */ -}; - -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"; -} - #ifndef TARGET_AARCH64 /* * -cpu max: a CPU with as many features enabled as our emulation supports. @@ -1058,6 +955,7 @@ static void arm_max_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_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); set_feature(&cpu->env, ARM_FEATURE_EL3); @@ -1085,9 +983,6 @@ static void arm_max_initfn(Object *obj) 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 */ @@ -1125,37 +1020,37 @@ static const ARMCPUInfo arm_tcg_cpus[] = { { .name = "cortex-a8", .initfn = cortex_a8_initfn }, { .name = "cortex-a9", .initfn = cortex_a9_initfn }, { .name = "cortex-a15", .initfn = cortex_a15_initfn }, - { .name = "cortex-m0", .initfn = cortex_m0_initfn, - .class_init = arm_v7m_class_init }, - { .name = "cortex-m3", .initfn = cortex_m3_initfn, - .class_init = arm_v7m_class_init }, - { .name = "cortex-m4", .initfn = cortex_m4_initfn, - .class_init = arm_v7m_class_init }, - { .name = "cortex-m7", .initfn = cortex_m7_initfn, - .class_init = arm_v7m_class_init }, - { .name = "cortex-m33", .initfn = cortex_m33_initfn, - .class_init = arm_v7m_class_init }, - { .name = "cortex-m55", .initfn = cortex_m55_initfn, - .class_init = arm_v7m_class_init }, { .name = "cortex-r5", .initfn = cortex_r5_initfn }, { .name = "cortex-r5f", .initfn = cortex_r5f_initfn }, { .name = "cortex-r52", .initfn = cortex_r52_initfn }, { .name = "ti925t", .initfn = ti925t_initfn }, { .name = "sa1100", .initfn = sa1100_initfn }, { .name = "sa1110", .initfn = sa1110_initfn }, - { .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 }, + { .name = "pxa250", .initfn = pxa250_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa255", .initfn = pxa255_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa260", .initfn = pxa260_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa261", .initfn = pxa261_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa262", .initfn = pxa262_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, /* "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 }, + { .name = "pxa270", .initfn = pxa270a0_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa270-a0", .initfn = pxa270a0_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa270-a1", .initfn = pxa270a1_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa270-b0", .initfn = pxa270b0_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa270-b1", .initfn = pxa270b1_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa270-c0", .initfn = pxa270c0_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, + { .name = "pxa270-c5", .initfn = pxa270c5_initfn, + .deprecation_note = "iwMMXt CPUs are no longer supported", }, #ifndef TARGET_AARCH64 { .name = "max", .initfn = arm_max_initfn }, #endif diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 7264ab5ead1..5d8ed2794d3 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -26,34 +26,9 @@ #include "hw/qdev-properties.h" #include "qemu/units.h" #include "internals.h" +#include "cpu-features.h" #include "cpregs.h" -static uint64_t make_ccsidr64(unsigned assoc, unsigned linesize, - unsigned cachesize) -{ - unsigned lg_linesize = ctz32(linesize); - unsigned sets; - - /* - * The 64-bit CCSIDR_EL1 format is: - * [55:32] number of sets - 1 - * [23:3] associativity - 1 - * [2:0] log2(linesize) - 4 - * so 0 == 16 bytes, 1 == 32 bytes, 2 == 64 bytes, etc - */ - assert(assoc != 0); - assert(is_power_of_2(linesize)); - assert(lg_linesize >= 4 && lg_linesize <= 7 + 4); - - /* sets * associativity * linesize == cachesize. */ - sets = cachesize / (assoc * linesize); - assert(cachesize % (assoc * linesize) == 0); - - return ((uint64_t)(sets - 1) << 32) - | ((assoc - 1) << 3) - | (lg_linesize - 4); -} - static void aarch64_a35_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -62,6 +37,7 @@ static void aarch64_a35_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); @@ -104,9 +80,12 @@ static void aarch64_a35_initfn(Object *obj) 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 */ + /* 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; @@ -219,7 +198,7 @@ static void cpu_max_get_l0gptsz(Object *obj, Visitor *v, const char *name, visit_type_uint32(v, name, &value, errp); } -static Property arm_cpu_lpa2_property = +static const Property arm_cpu_lpa2_property = DEFINE_PROP_BOOL("lpa2", ARMCPU, prop_lpa2, true); static void aarch64_a55_initfn(Object *obj) @@ -230,6 +209,7 @@ static void aarch64_a55_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); @@ -269,9 +249,12 @@ static void aarch64_a55_initfn(Object *obj) cpu->revidr = 0; /* From B2.23 CCSIDR_EL1 */ - cpu->ccsidr[0] = 0x700fe01a; /* 32KB L1 dcache */ - cpu->ccsidr[1] = 0x200fe01a; /* 32KB L1 icache */ - cpu->ccsidr[2] = 0x703fe07a; /* 512KB L2 cache */ + /* 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; @@ -298,6 +281,7 @@ static void aarch64_a72_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); @@ -334,9 +318,12 @@ static void aarch64_a72_initfn(Object *obj) 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 */ + /* 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; @@ -353,6 +340,7 @@ static void aarch64_a76_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); @@ -392,9 +380,12 @@ static void aarch64_a76_initfn(Object *obj) 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 */ + /* 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; @@ -422,6 +413,7 @@ static void aarch64_a64fx_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_EL2); set_feature(&cpu->env, ARM_FEATURE_EL3); @@ -443,9 +435,12 @@ static void aarch64_a64fx_initfn(Object *obj) 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 */ + /* 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; @@ -591,6 +586,7 @@ static void aarch64_neoverse_n1_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); @@ -630,9 +626,12 @@ static void aarch64_neoverse_n1_initfn(Object *obj) 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 */ + /* 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; @@ -662,6 +661,7 @@ static void aarch64_neoverse_v1_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); @@ -677,7 +677,7 @@ static void aarch64_neoverse_v1_initfn(Object *obj) cpu->isar.id_aa64dfr0 = 0x000001f210305519ull; cpu->isar.id_aa64dfr1 = 0x00000000; cpu->isar.id_aa64isar0 = 0x1011111110212120ull; /* with FEAT_RNG */ - cpu->isar.id_aa64isar1 = 0x0111000001211032ull; + cpu->isar.id_aa64isar1 = 0x0011100001211032ull; cpu->isar.id_aa64mmfr0 = 0x0000000000101125ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; cpu->isar.id_aa64mmfr2 = 0x0220011102101011ull; @@ -713,9 +713,12 @@ static void aarch64_neoverse_v1_initfn(Object *obj) * L2: 8-way set associative, 64 byte line size, either 512K or 1MB. * L3: No L3 (this matches the CLIDR_EL1 value). */ - cpu->ccsidr[0] = make_ccsidr64(4, 64, 64 * KiB); /* L1 dcache */ - cpu->ccsidr[1] = cpu->ccsidr[0]; /* L1 icache */ - cpu->ccsidr[2] = make_ccsidr64(8, 64, 1 * MiB); /* L2 cache */ + /* 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; @@ -840,6 +843,13 @@ static const ARMCPRegInfo cortex_a710_cp_reginfo[] = { { .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, @@ -877,6 +887,7 @@ static void aarch64_a710_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); @@ -909,12 +920,12 @@ static void aarch64_a710_initfn(Object *obj) 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 = 0x000011f010305611ull; + 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 = 0x0010111101211032ull; + cpu->isar.id_aa64isar1 = 0x0010111101211052ull; cpu->isar.id_aa64mmfr0 = 0x0000022200101122ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; cpu->isar.id_aa64mmfr2 = 0x1221011110101011ull; @@ -943,9 +954,12 @@ static void aarch64_a710_initfn(Object *obj) * L1: 4-way set associative 64-byte line size, total either 32K or 64K. * L2: 8-way set associative 64 byte line size, total either 256K or 512K. */ - cpu->ccsidr[0] = make_ccsidr64(4, 64, 64 * KiB); /* L1 dcache */ - cpu->ccsidr[1] = cpu->ccsidr[0]; /* L1 icache */ - cpu->ccsidr[2] = make_ccsidr64(8, 64, 512 * KiB); /* L2 cache */ + /* 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; @@ -956,6 +970,111 @@ static void aarch64_a710_initfn(Object *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; @@ -967,6 +1086,15 @@ void aarch64_max_tcg_initfn(Object *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 @@ -995,6 +1123,16 @@ void aarch64_max_tcg_initfn(Object *obj) 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 */ @@ -1022,11 +1160,19 @@ void aarch64_max_tcg_initfn(Object *obj) 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, 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 */ + t = FIELD_DP64(t, ID_AA64ISAR1, XS, 1); /* FEAT_XS */ cpu->isar.id_aa64isar1 = t; + t = cpu->isar.id_aa64isar2; + t = FIELD_DP64(t, ID_AA64ISAR2, RPRES, 1); /* FEAT_RPRES */ + 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 */ @@ -1034,7 +1180,7 @@ void aarch64_max_tcg_initfn(Object *obj) 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, CSV2, 3); /* FEAT_CSV2_3 */ t = FIELD_DP64(t, ID_AA64PFR0, CSV3, 1); /* FEAT_CSV3 */ cpu->isar.id_aa64pfr0 = t; @@ -1049,7 +1195,8 @@ void aarch64_max_tcg_initfn(Object *obj) 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 */ + 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; @@ -1059,6 +1206,7 @@ void aarch64_max_tcg_initfn(Object *obj) 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; @@ -1069,9 +1217,11 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR1, LO, 1); /* FEAT_LOR */ t = FIELD_DP64(t, ID_AA64MMFR1, PAN, 3); /* FEAT_PAN3 */ t = FIELD_DP64(t, ID_AA64MMFR1, XNX, 1); /* FEAT_XNX */ - t = FIELD_DP64(t, ID_AA64MMFR1, ETS, 1); /* FEAT_ETS */ + t = FIELD_DP64(t, ID_AA64MMFR1, ETS, 2); /* FEAT_ETS2 */ t = FIELD_DP64(t, ID_AA64MMFR1, HCX, 1); /* FEAT_HCX */ + t = FIELD_DP64(t, ID_AA64MMFR1, AFP, 1); /* FEAT_AFP */ 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; @@ -1079,6 +1229,7 @@ void aarch64_max_tcg_initfn(Object *obj) 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 */ @@ -1089,11 +1240,15 @@ void aarch64_max_tcg_initfn(Object *obj) 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, 1); /* FEAT_BF16 */ + 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 */ @@ -1102,8 +1257,9 @@ void aarch64_max_tcg_initfn(Object *obj) 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, 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; @@ -1152,6 +1308,7 @@ static const ARMCPUInfo aarch64_cpus[] = { { .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) @@ -1159,7 +1316,7 @@ 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]); + arm_cpu_register(&aarch64_cpus[i]); } } diff --git a/target/arm/tcg/crypto_helper.c b/target/arm/tcg/crypto_helper.c index 7cadd61e124..3428bd1bf0b 100644 --- a/target/arm/tcg/crypto_helper.c +++ b/target/arm/tcg/crypto_helper.c @@ -10,14 +10,16 @@ */ #include "qemu/osdep.h" +#include "qemu/bitops.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" +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" + union CRYPTO_STATE { uint8_t bytes[16]; uint32_t words[4]; diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c new file mode 100644 index 00000000000..01867f8ace3 --- /dev/null +++ b/target/arm/tcg/gengvec.c @@ -0,0 +1,2729 @@ +/* + * 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]); +} + +void gen_gvec_cls(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const GVecGen2 g[] = { + { .fni4 = gen_helper_neon_cls_s8, + .vece = MO_8 }, + { .fni4 = gen_helper_neon_cls_s16, + .vece = MO_16 }, + { .fni4 = tcg_gen_clrsb_i32, + .vece = MO_32 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_clz32_i32(TCGv_i32 d, TCGv_i32 n) +{ + tcg_gen_clzi_i32(d, n, 32); +} + +void gen_gvec_clz(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const GVecGen2 g[] = { + { .fni4 = gen_helper_neon_clz_u8, + .vece = MO_8 }, + { .fni4 = gen_helper_neon_clz_u16, + .vece = MO_16 }, + { .fni4 = gen_clz32_i32, + .vece = MO_32 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); +} + +void gen_gvec_cnt(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + assert(vece == MO_8); + tcg_gen_gvec_2_ool(rd_ofs, rn_ofs, opr_sz, max_sz, 0, + gen_helper_gvec_cnt_b); +} + +void gen_gvec_rbit(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + assert(vece == MO_8); + tcg_gen_gvec_2_ool(rd_ofs, rn_ofs, opr_sz, max_sz, 0, + gen_helper_gvec_rbit_b); +} + +void gen_gvec_rev16(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + assert(vece == MO_8); + tcg_gen_gvec_rotli(MO_16, rd_ofs, rn_ofs, 8, opr_sz, max_sz); +} + +static void gen_bswap32_i64(TCGv_i64 d, TCGv_i64 n) +{ + tcg_gen_bswap64_i64(d, n); + tcg_gen_rotli_i64(d, d, 32); +} + +void gen_gvec_rev32(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const GVecGen2 g = { + .fni8 = gen_bswap32_i64, + .fni4 = tcg_gen_bswap32_i32, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .vece = MO_32 + }; + + switch (vece) { + case MO_16: + tcg_gen_gvec_rotli(MO_32, rd_ofs, rn_ofs, 16, opr_sz, max_sz); + break; + case MO_8: + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g); + break; + default: + g_assert_not_reached(); + } +} + +void gen_gvec_rev64(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const GVecGen2 g[] = { + { .fni8 = tcg_gen_bswap64_i64, + .vece = MO_64 }, + { .fni8 = tcg_gen_hswap_i64, + .vece = MO_64 }, + }; + + switch (vece) { + case MO_32: + tcg_gen_gvec_rotli(MO_64, rd_ofs, rn_ofs, 32, opr_sz, max_sz); + break; + case MO_8: + case MO_16: + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); + break; + default: + g_assert_not_reached(); + } +} + +static void gen_saddlp_vec(unsigned vece, TCGv_vec d, TCGv_vec n) +{ + int half = 4 << vece; + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_shli_vec(vece, t, n, half); + tcg_gen_sari_vec(vece, d, n, half); + tcg_gen_sari_vec(vece, t, t, half); + tcg_gen_add_vec(vece, d, d, t); +} + +static void gen_saddlp_s_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_ext32s_i64(t, n); + tcg_gen_sari_i64(d, n, 32); + tcg_gen_add_i64(d, d, t); +} + +void gen_gvec_saddlp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_shli_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2 g[] = { + { .fniv = gen_saddlp_vec, + .fni8 = gen_helper_neon_addlp_s8, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fniv = gen_saddlp_vec, + .fni8 = gen_helper_neon_addlp_s16, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fniv = gen_saddlp_vec, + .fni8 = gen_saddlp_s_i64, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_sadalp_vec(unsigned vece, TCGv_vec d, TCGv_vec n) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + gen_saddlp_vec(vece, t, n); + tcg_gen_add_vec(vece, d, d, t); +} + +static void gen_sadalp_b_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_helper_neon_addlp_s8(t, n); + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_sadalp_h_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_helper_neon_addlp_s16(t, n); + tcg_gen_vec_add32_i64(d, d, t); +} + +static void gen_sadalp_s_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_saddlp_s_i64(t, n); + tcg_gen_add_i64(d, d, t); +} + +void gen_gvec_sadalp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_shli_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2 g[] = { + { .fniv = gen_sadalp_vec, + .fni8 = gen_sadalp_b_i64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_16 }, + { .fniv = gen_sadalp_vec, + .fni8 = gen_sadalp_h_i64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_32 }, + { .fniv = gen_sadalp_vec, + .fni8 = gen_sadalp_s_i64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_64 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_uaddlp_vec(unsigned vece, TCGv_vec d, TCGv_vec n) +{ + int half = 4 << vece; + TCGv_vec t = tcg_temp_new_vec_matching(d); + TCGv_vec m = tcg_constant_vec_matching(d, vece, MAKE_64BIT_MASK(0, half)); + + tcg_gen_shri_vec(vece, t, n, half); + tcg_gen_and_vec(vece, d, n, m); + tcg_gen_add_vec(vece, d, d, t); +} + +static void gen_uaddlp_b_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 m = tcg_constant_i64(dup_const(MO_16, 0xff)); + + tcg_gen_shri_i64(t, n, 8); + tcg_gen_and_i64(d, n, m); + tcg_gen_and_i64(t, t, m); + /* No carry between widened unsigned elements. */ + tcg_gen_add_i64(d, d, t); +} + +static void gen_uaddlp_h_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 m = tcg_constant_i64(dup_const(MO_32, 0xffff)); + + tcg_gen_shri_i64(t, n, 16); + tcg_gen_and_i64(d, n, m); + tcg_gen_and_i64(t, t, m); + /* No carry between widened unsigned elements. */ + tcg_gen_add_i64(d, d, t); +} + +static void gen_uaddlp_s_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_ext32u_i64(t, n); + tcg_gen_shri_i64(d, n, 32); + tcg_gen_add_i64(d, d, t); +} + +void gen_gvec_uaddlp(unsigned vece, uint32_t rd_ofs, uint32_t rn_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 GVecGen2 g[] = { + { .fniv = gen_uaddlp_vec, + .fni8 = gen_uaddlp_b_i64, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fniv = gen_uaddlp_vec, + .fni8 = gen_uaddlp_h_i64, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fniv = gen_uaddlp_vec, + .fni8 = gen_uaddlp_s_i64, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_uadalp_vec(unsigned vece, TCGv_vec d, TCGv_vec n) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + gen_uaddlp_vec(vece, t, n); + tcg_gen_add_vec(vece, d, d, t); +} + +static void gen_uadalp_b_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_uaddlp_b_i64(t, n); + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_uadalp_h_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_uaddlp_h_i64(t, n); + tcg_gen_vec_add32_i64(d, d, t); +} + +static void gen_uadalp_s_i64(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_uaddlp_s_i64(t, n); + tcg_gen_add_i64(d, d, t); +} + +void gen_gvec_uadalp(unsigned vece, uint32_t rd_ofs, uint32_t rn_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 GVecGen2 g[] = { + { .fniv = gen_uadalp_vec, + .fni8 = gen_uadalp_b_i64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fniv = gen_uadalp_vec, + .fni8 = gen_uadalp_h_i64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fniv = gen_uadalp_vec, + .fni8 = gen_uadalp_s_i64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_2(rd_ofs, rn_ofs, opr_sz, max_sz, &g[vece]); +} + +void gen_gvec_fabs(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz) +{ + uint64_t s_bit = 1ull << ((8 << vece) - 1); + tcg_gen_gvec_andi(vece, dofs, aofs, s_bit - 1, oprsz, maxsz); +} + +void gen_gvec_fneg(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz) +{ + uint64_t s_bit = 1ull << ((8 << vece) - 1); + tcg_gen_gvec_xori(vece, dofs, aofs, s_bit, oprsz, maxsz); +} + +void gen_gvec_urecpe(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + assert(vece == MO_32); + tcg_gen_gvec_2_ool(rd_ofs, rn_ofs, opr_sz, max_sz, 0, + gen_helper_gvec_urecpe_s); +} + +void gen_gvec_ursqrte(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + assert(vece == MO_32); + tcg_gen_gvec_2_ool(rd_ofs, rn_ofs, opr_sz, max_sz, 0, + gen_helper_gvec_ursqrte_s); +} diff --git a/target/arm/tcg/gengvec64.c b/target/arm/tcg/gengvec64.c new file mode 100644 index 00000000000..2617cde0a5f --- /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 index 0cf56f6dc44..4f618ae390e 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -28,12 +28,20 @@ #include "qemu/bitops.h" #include "internals.h" #include "qemu/crc32c.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "exec/cpu-common.h" +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/helper-retaddr.h" +#include "accel/tcg/probe.h" +#include "exec/target_page.h" +#include "exec/tlb-flags.h" #include "qemu/int128.h" #include "qemu/atomic128.h" #include "fpu/softfloat.h" -#include /* For crc32 */ +#include /* for crc32 */ +#ifdef CONFIG_USER_ONLY +#include "user/page-protection.h" +#endif +#include "vec_internal.h" /* C2.4.7 Multiply and divide */ /* special cases for 0 and LLONG_MIN are mandated by the standard */ @@ -66,6 +74,18 @@ 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) { @@ -118,40 +138,38 @@ static inline uint32_t float_rel_to_flags(int res) return flags; } -uint64_t HELPER(vfp_cmph_a64)(uint32_t x, uint32_t y, void *fp_status) +uint64_t HELPER(vfp_cmph_a64)(uint32_t x, uint32_t y, float_status *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) +uint64_t HELPER(vfp_cmpeh_a64)(uint32_t x, uint32_t y, float_status *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) +uint64_t HELPER(vfp_cmps_a64)(float32 x, float32 y, float_status *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) +uint64_t HELPER(vfp_cmpes_a64)(float32 x, float32 y, float_status *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) +uint64_t HELPER(vfp_cmpd_a64)(float64 x, float64 y, float_status *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) +uint64_t HELPER(vfp_cmped_a64)(float64 x, float64 y, float_status *fp_status) { return float_rel_to_flags(float64_compare(x, y, fp_status)); } -float32 HELPER(vfp_mulxs)(float32 a, float32 b, void *fpstp) +float32 HELPER(vfp_mulxs)(float32 a, float32 b, float_status *fpst) { - float_status *fpst = fpstp; - a = float32_squash_input_denormal(a, fpst); b = float32_squash_input_denormal(b, fpst); @@ -164,10 +182,8 @@ float32 HELPER(vfp_mulxs)(float32 a, float32 b, void *fpstp) return float32_mul(a, b, fpst); } -float64 HELPER(vfp_mulxd)(float64 a, float64 b, void *fpstp) +float64 HELPER(vfp_mulxd)(float64 a, float64 b, float_status *fpst) { - float_status *fpst = fpstp; - a = float64_squash_input_denormal(a, fpst); b = float64_squash_input_denormal(b, fpst); @@ -181,184 +197,71 @@ float64 HELPER(vfp_mulxd)(float64 a, float64 b, void *fpstp) } /* 64bit/double versions of the neon float compare functions */ -uint64_t HELPER(neon_ceq_f64)(float64 a, float64 b, void *fpstp) +uint64_t HELPER(neon_ceq_f64)(float64 a, float64 b, float_status *fpst) { - float_status *fpst = fpstp; return -float64_eq_quiet(a, b, fpst); } -uint64_t HELPER(neon_cge_f64)(float64 a, float64 b, void *fpstp) +uint64_t HELPER(neon_cge_f64)(float64 a, float64 b, float_status *fpst) { - float_status *fpst = fpstp; return -float64_le(b, a, fpst); } -uint64_t HELPER(neon_cgt_f64)(float64 a, float64 b, void *fpstp) +uint64_t HELPER(neon_cgt_f64)(float64 a, float64 b, float_status *fpst) { - float_status *fpst = fpstp; return -float64_lt(b, a, fpst); } -/* Reciprocal step and sqrt step. Note that unlike the A32/T32 +/* + * Reciprocal step and sqrt step. Note that unlike the A32/T32 * versions, these do a fully fused multiply-add or * multiply-add-and-halve. + * The FPCR.AH == 1 versions need to avoid flipping the sign of NaN. */ - -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; +#define DO_RECPS(NAME, CTYPE, FLOATTYPE, CHSFN) \ + CTYPE HELPER(NAME)(CTYPE a, CTYPE b, float_status *fpst) \ + { \ + a = FLOATTYPE ## _squash_input_denormal(a, fpst); \ + b = FLOATTYPE ## _squash_input_denormal(b, fpst); \ + a = FLOATTYPE ## _ ## CHSFN(a); \ + if ((FLOATTYPE ## _is_infinity(a) && FLOATTYPE ## _is_zero(b)) || \ + (FLOATTYPE ## _is_infinity(b) && FLOATTYPE ## _is_zero(a))) { \ + return FLOATTYPE ## _two; \ + } \ + return FLOATTYPE ## _muladd(a, b, FLOATTYPE ## _two, 0, fpst); \ } - 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; -} +DO_RECPS(recpsf_f16, uint32_t, float16, chs) +DO_RECPS(recpsf_f32, float32, float32, chs) +DO_RECPS(recpsf_f64, float64, float64, chs) +DO_RECPS(recpsf_ah_f16, uint32_t, float16, ah_chs) +DO_RECPS(recpsf_ah_f32, float32, float32, ah_chs) +DO_RECPS(recpsf_ah_f64, float64, float64, ah_chs) + +#define DO_RSQRTSF(NAME, CTYPE, FLOATTYPE, CHSFN) \ + CTYPE HELPER(NAME)(CTYPE a, CTYPE b, float_status *fpst) \ + { \ + a = FLOATTYPE ## _squash_input_denormal(a, fpst); \ + b = FLOATTYPE ## _squash_input_denormal(b, fpst); \ + a = FLOATTYPE ## _ ## CHSFN(a); \ + if ((FLOATTYPE ## _is_infinity(a) && FLOATTYPE ## _is_zero(b)) || \ + (FLOATTYPE ## _is_infinity(b) && FLOATTYPE ## _is_zero(a))) { \ + return FLOATTYPE ## _one_point_five; \ + } \ + return FLOATTYPE ## _muladd_scalbn(a, b, FLOATTYPE ## _three, \ + -1, 0, fpst); \ + } \ + +DO_RSQRTSF(rsqrtsf_f16, uint32_t, float16, chs) +DO_RSQRTSF(rsqrtsf_f32, float32, float32, chs) +DO_RSQRTSF(rsqrtsf_f64, float64, float64, chs) +DO_RSQRTSF(rsqrtsf_ah_f16, uint32_t, float16, ah_chs) +DO_RSQRTSF(rsqrtsf_ah_f32, float32, float32, ah_chs) +DO_RSQRTSF(rsqrtsf_ah_f64, float64, float64, ah_chs) /* Floating-point reciprocal exponent - see FPRecpX in ARM ARM */ -uint32_t HELPER(frecpx_f16)(uint32_t a, void *fpstp) +uint32_t HELPER(frecpx_f16)(uint32_t a, float_status *fpst) { - float_status *fpst = fpstp; uint16_t val16, sbit; int16_t exp; @@ -389,9 +292,8 @@ uint32_t HELPER(frecpx_f16)(uint32_t a, void *fpstp) } } -float32 HELPER(frecpx_f32)(float32 a, void *fpstp) +float32 HELPER(frecpx_f32)(float32 a, float_status *fpst) { - float_status *fpst = fpstp; uint32_t val32, sbit; int32_t exp; @@ -422,9 +324,8 @@ float32 HELPER(frecpx_f32)(float32 a, void *fpstp) } } -float64 HELPER(frecpx_f64)(float64 a, void *fpstp) +float64 HELPER(frecpx_f64)(float64 a, float_status *fpst) { - float_status *fpst = fpstp; uint64_t val64, sbit; int64_t exp; @@ -455,28 +356,53 @@ float64 HELPER(frecpx_f64)(float64 a, void *fpstp) } } -float32 HELPER(fcvtx_f64_to_f32)(float64 a, CPUARMState *env) +float32 HELPER(fcvtx_f64_to_f32)(float64 a, float_status *fpst) { - /* 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); + int old = get_float_rounding_mode(fpst); + + set_float_rounding_mode(float_round_to_odd, fpst); + r = float64_to_float32(a, fpst); + set_float_rounding_mode(old, fpst); return r; } +/* + * AH=1 min/max have some odd special cases: + * comparing two zeroes (regardless of sign), (NaN, anything), + * or (anything, NaN) should return the second argument (possibly + * squashed to zero). + * Also, denormal outputs are not squashed to zero regardless of FZ or FZ16. + */ +#define AH_MINMAX_HELPER(NAME, CTYPE, FLOATTYPE, MINMAX) \ + CTYPE HELPER(NAME)(CTYPE a, CTYPE b, float_status *fpst) \ + { \ + bool save; \ + CTYPE r; \ + a = FLOATTYPE ## _squash_input_denormal(a, fpst); \ + b = FLOATTYPE ## _squash_input_denormal(b, fpst); \ + if (FLOATTYPE ## _is_zero(a) && FLOATTYPE ## _is_zero(b)) { \ + return b; \ + } \ + if (FLOATTYPE ## _is_any_nan(a) || \ + FLOATTYPE ## _is_any_nan(b)) { \ + float_raise(float_flag_invalid, fpst); \ + return b; \ + } \ + save = get_flush_to_zero(fpst); \ + set_flush_to_zero(false, fpst); \ + r = FLOATTYPE ## _ ## MINMAX(a, b, fpst); \ + set_flush_to_zero(save, fpst); \ + return r; \ + } + +AH_MINMAX_HELPER(vfp_ah_minh, dh_ctype_f16, float16, min) +AH_MINMAX_HELPER(vfp_ah_mins, float32, float32, min) +AH_MINMAX_HELPER(vfp_ah_mind, float64, float64, min) +AH_MINMAX_HELPER(vfp_ah_maxh, dh_ctype_f16, float16, max) +AH_MINMAX_HELPER(vfp_ah_maxs, float32, float32, max) +AH_MINMAX_HELPER(vfp_ah_maxd, float64, float64, max) + /* 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 @@ -512,27 +438,17 @@ uint64_t HELPER(crc32c_64)(uint64_t acc, uint64_t val, uint32_t bytes) #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) \ +uint32_t ADVSIMD_HELPER(name, h)(uint32_t a, uint32_t b, float_status *fpst) \ { \ - 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) \ +uint32_t ADVSIMD_HELPER(name, 2h)(uint32_t two_a, uint32_t two_b, \ + float_status *fpst) \ { \ 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); \ @@ -552,10 +468,8 @@ ADVSIMD_TWOHALFOP(minnum) ADVSIMD_TWOHALFOP(maxnum) /* Data processing - scalar floating-point and advanced SIMD */ -static float16 float16_mulx(float16 a, float16 b, void *fpstp) +static float16 float16_mulx(float16 a, float16 b, float_status *fpst) { - float_status *fpst = fpstp; - a = float16_squash_input_denormal(a, fpst); b = float16_squash_input_denormal(b, fpst); @@ -573,16 +487,14 @@ 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) { - 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) + uint32_t two_c, float_status *fpst) { - float_status *fpst = fpstp; float16 a1, a2, b1, b2, c1, c2; uint32_t r1, r2; a1 = extract32(two_a, 0, 16); @@ -604,31 +516,27 @@ uint32_t HELPER(advsimd_muladd2h)(uint32_t two_a, uint32_t two_b, #define ADVSIMD_CMPRES(test) (test) ? 0xffff : 0 -uint32_t HELPER(advsimd_ceq_f16)(uint32_t a, uint32_t b, void *fpstp) +uint32_t HELPER(advsimd_ceq_f16)(uint32_t a, uint32_t b, float_status *fpst) { - 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) +uint32_t HELPER(advsimd_cge_f16)(uint32_t a, uint32_t b, float_status *fpst) { - 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) +uint32_t HELPER(advsimd_cgt_f16)(uint32_t a, uint32_t b, float_status *fpst) { - 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) +uint32_t HELPER(advsimd_acge_f16)(uint32_t a, uint32_t b, float_status *fpst) { - float_status *fpst = fpstp; float16 f0 = float16_abs(a); float16 f1 = float16_abs(b); int compare = float16_compare(f0, f1, fpst); @@ -636,9 +544,8 @@ uint32_t HELPER(advsimd_acge_f16)(uint32_t a, uint32_t b, void *fpstp) compare == float_relation_equal); } -uint32_t HELPER(advsimd_acgt_f16)(uint32_t a, uint32_t b, void *fpstp) +uint32_t HELPER(advsimd_acgt_f16)(uint32_t a, uint32_t b, float_status *fpst) { - float_status *fpst = fpstp; float16 f0 = float16_abs(a); float16 f1 = float16_abs(b); int compare = float16_compare(f0, f1, fpst); @@ -646,12 +553,12 @@ uint32_t HELPER(advsimd_acgt_f16)(uint32_t a, uint32_t b, void *fpstp) } /* round to integral */ -uint32_t HELPER(advsimd_rinth_exact)(uint32_t x, void *fp_status) +uint32_t HELPER(advsimd_rinth_exact)(uint32_t x, float_status *fp_status) { return float16_round_to_int(x, fp_status); } -uint32_t HELPER(advsimd_rinth)(uint32_t x, void *fp_status) +uint32_t HELPER(advsimd_rinth)(uint32_t x, float_status *fp_status) { int old_flags = get_float_exception_flags(fp_status), new_flags; float16 ret; @@ -667,38 +574,6 @@ uint32_t HELPER(advsimd_rinth)(uint32_t x, void *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, @@ -759,6 +634,7 @@ static void cpsr_write_from_spsr_elx(CPUARMState *env, void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) { + ARMCPU *cpu = env_archcpu(env); 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]; @@ -805,13 +681,18 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) goto illegal_return; } + if (!return_to_aa64 && !cpu_isar_feature(aa64_aa32, cpu)) { + /* Return to AArch32 when CPU is AArch64-only */ + 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(); + bql_lock(); + arm_call_pre_el_change_hook(cpu); + bql_unlock(); if (!return_to_aa64) { env->aarch64 = false; @@ -838,7 +719,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) int tbii; env->aarch64 = true; - spsr &= aarch64_pstate_valid_mask(&env_archcpu(env)->isar); + spsr &= aarch64_pstate_valid_mask(&cpu->isar); pstate_write(env, spsr); if (!arm_singlestep_active(env)) { env->pstate &= ~PSTATE_SS; @@ -856,7 +737,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) 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); + 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 { @@ -876,9 +757,9 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) */ 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(); + bql_lock(); + arm_call_el_change_hook(cpu); + bql_unlock(); return; @@ -892,8 +773,8 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) */ env->pstate |= PSTATE_IL; env->pc = new_pc; - spsr &= PSTATE_NZCV | PSTATE_DAIF; - spsr |= pstate_read(env) & ~(PSTATE_NZCV | PSTATE_DAIF); + 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; @@ -903,19 +784,10 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) "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) @@ -925,7 +797,7 @@ void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in) */ int blocklen = 4 << env_archcpu(env)->dcz_blocksize; uint64_t vaddr = vaddr_in & ~(blocklen - 1); - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); void *mem; /* @@ -936,8 +808,6 @@ void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in) #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 @@ -959,7 +829,9 @@ void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in) } #endif + set_helper_retaddr(ra); memset(mem, 0, blocklen); + clear_helper_retaddr(); } void HELPER(unaligned_access)(CPUARMState *env, uint64_t addr, @@ -968,3 +840,935 @@ void HELPER(unaligned_access)(CPUARMState *env, uint64_t addr, 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; +} + +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; +} + +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/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h index 57cfd68569e..85023465b76 100644 --- a/target/arm/tcg/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -22,64 +22,63 @@ 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_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) -DEF_HELPER_3(vfp_cmpes_a64, i64, f32, f32, ptr) -DEF_HELPER_3(vfp_cmpd_a64, i64, f64, f64, ptr) -DEF_HELPER_3(vfp_cmped_a64, i64, f64, f64, ptr) -DEF_HELPER_FLAGS_4(simd_tblx, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vfp_mulxs, TCG_CALL_NO_RWG, f32, f32, f32, ptr) -DEF_HELPER_FLAGS_3(vfp_mulxd, TCG_CALL_NO_RWG, f64, f64, f64, ptr) -DEF_HELPER_FLAGS_3(neon_ceq_f64, TCG_CALL_NO_RWG, i64, i64, i64, ptr) -DEF_HELPER_FLAGS_3(neon_cge_f64, TCG_CALL_NO_RWG, i64, i64, i64, ptr) -DEF_HELPER_FLAGS_3(neon_cgt_f64, TCG_CALL_NO_RWG, i64, i64, i64, ptr) -DEF_HELPER_FLAGS_3(recpsf_f16, TCG_CALL_NO_RWG, f16, f16, f16, ptr) -DEF_HELPER_FLAGS_3(recpsf_f32, TCG_CALL_NO_RWG, f32, f32, f32, ptr) -DEF_HELPER_FLAGS_3(recpsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, ptr) -DEF_HELPER_FLAGS_3(rsqrtsf_f16, TCG_CALL_NO_RWG, f16, f16, f16, ptr) -DEF_HELPER_FLAGS_3(rsqrtsf_f32, TCG_CALL_NO_RWG, f32, f32, f32, ptr) -DEF_HELPER_FLAGS_3(rsqrtsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, ptr) -DEF_HELPER_FLAGS_1(neon_addlp_s8, TCG_CALL_NO_RWG_SE, i64, i64) -DEF_HELPER_FLAGS_1(neon_addlp_u8, TCG_CALL_NO_RWG_SE, i64, i64) -DEF_HELPER_FLAGS_1(neon_addlp_s16, TCG_CALL_NO_RWG_SE, i64, i64) -DEF_HELPER_FLAGS_1(neon_addlp_u16, TCG_CALL_NO_RWG_SE, i64, i64) -DEF_HELPER_FLAGS_2(frecpx_f64, TCG_CALL_NO_RWG, f64, f64, ptr) -DEF_HELPER_FLAGS_2(frecpx_f32, TCG_CALL_NO_RWG, f32, f32, ptr) -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_1(msr_set_allint_el1, void, env) +DEF_HELPER_3(vfp_cmph_a64, i64, f16, f16, fpst) +DEF_HELPER_3(vfp_cmpeh_a64, i64, f16, f16, fpst) +DEF_HELPER_3(vfp_cmps_a64, i64, f32, f32, fpst) +DEF_HELPER_3(vfp_cmpes_a64, i64, f32, f32, fpst) +DEF_HELPER_3(vfp_cmpd_a64, i64, f64, f64, fpst) +DEF_HELPER_3(vfp_cmped_a64, i64, f64, f64, fpst) +DEF_HELPER_FLAGS_4(simd_tblx, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_3(vfp_mulxs, TCG_CALL_NO_RWG, f32, f32, f32, fpst) +DEF_HELPER_FLAGS_3(vfp_mulxd, TCG_CALL_NO_RWG, f64, f64, f64, fpst) +DEF_HELPER_FLAGS_3(neon_ceq_f64, TCG_CALL_NO_RWG, i64, i64, i64, fpst) +DEF_HELPER_FLAGS_3(neon_cge_f64, TCG_CALL_NO_RWG, i64, i64, i64, fpst) +DEF_HELPER_FLAGS_3(neon_cgt_f64, TCG_CALL_NO_RWG, i64, i64, i64, fpst) +DEF_HELPER_FLAGS_3(recpsf_f16, TCG_CALL_NO_RWG, f16, f16, f16, fpst) +DEF_HELPER_FLAGS_3(recpsf_f32, TCG_CALL_NO_RWG, f32, f32, f32, fpst) +DEF_HELPER_FLAGS_3(recpsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, fpst) +DEF_HELPER_FLAGS_3(recpsf_ah_f16, TCG_CALL_NO_RWG, f16, f16, f16, fpst) +DEF_HELPER_FLAGS_3(recpsf_ah_f32, TCG_CALL_NO_RWG, f32, f32, f32, fpst) +DEF_HELPER_FLAGS_3(recpsf_ah_f64, TCG_CALL_NO_RWG, f64, f64, f64, fpst) +DEF_HELPER_FLAGS_3(rsqrtsf_f16, TCG_CALL_NO_RWG, f16, f16, f16, fpst) +DEF_HELPER_FLAGS_3(rsqrtsf_f32, TCG_CALL_NO_RWG, f32, f32, f32, fpst) +DEF_HELPER_FLAGS_3(rsqrtsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, fpst) +DEF_HELPER_FLAGS_3(rsqrtsf_ah_f16, TCG_CALL_NO_RWG, f16, f16, f16, fpst) +DEF_HELPER_FLAGS_3(rsqrtsf_ah_f32, TCG_CALL_NO_RWG, f32, f32, f32, fpst) +DEF_HELPER_FLAGS_3(rsqrtsf_ah_f64, TCG_CALL_NO_RWG, f64, f64, f64, fpst) +DEF_HELPER_FLAGS_2(frecpx_f64, TCG_CALL_NO_RWG, f64, f64, fpst) +DEF_HELPER_FLAGS_2(frecpx_f32, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(frecpx_f16, TCG_CALL_NO_RWG, f16, f16, fpst) +DEF_HELPER_FLAGS_2(fcvtx_f64_to_f32, TCG_CALL_NO_RWG, f32, f64, fpst) 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_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) -DEF_HELPER_FLAGS_3(advsimd_minnumh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) -DEF_HELPER_3(advsimd_addh, f16, f16, f16, ptr) -DEF_HELPER_3(advsimd_subh, f16, f16, f16, ptr) -DEF_HELPER_3(advsimd_mulh, f16, f16, f16, ptr) -DEF_HELPER_3(advsimd_divh, f16, f16, f16, ptr) -DEF_HELPER_3(advsimd_ceq_f16, i32, f16, f16, ptr) -DEF_HELPER_3(advsimd_cge_f16, i32, f16, f16, ptr) -DEF_HELPER_3(advsimd_cgt_f16, i32, f16, f16, ptr) -DEF_HELPER_3(advsimd_acge_f16, i32, f16, f16, ptr) -DEF_HELPER_3(advsimd_acgt_f16, i32, f16, f16, ptr) -DEF_HELPER_3(advsimd_mulxh, f16, f16, f16, ptr) -DEF_HELPER_4(advsimd_muladdh, f16, f16, f16, f16, ptr) -DEF_HELPER_3(advsimd_add2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_sub2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_mul2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_div2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_max2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_min2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_maxnum2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_minnum2h, i32, i32, i32, ptr) -DEF_HELPER_3(advsimd_mulx2h, i32, i32, i32, ptr) -DEF_HELPER_4(advsimd_muladd2h, i32, i32, i32, i32, ptr) -DEF_HELPER_2(advsimd_rinth_exact, f16, f16, ptr) -DEF_HELPER_2(advsimd_rinth, f16, f16, ptr) -DEF_HELPER_2(advsimd_f16tosinth, i32, f16, ptr) -DEF_HELPER_2(advsimd_f16touinth, i32, f16, ptr) -DEF_HELPER_2(sqrt_f16, f16, f16, ptr) +DEF_HELPER_3(advsimd_ceq_f16, i32, f16, f16, fpst) +DEF_HELPER_3(advsimd_cge_f16, i32, f16, f16, fpst) +DEF_HELPER_3(advsimd_cgt_f16, i32, f16, f16, fpst) +DEF_HELPER_3(advsimd_acge_f16, i32, f16, f16, fpst) +DEF_HELPER_3(advsimd_acgt_f16, i32, f16, f16, fpst) +DEF_HELPER_3(advsimd_mulxh, f16, f16, f16, fpst) +DEF_HELPER_4(advsimd_muladdh, f16, f16, f16, f16, fpst) +DEF_HELPER_3(advsimd_add2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_sub2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_mul2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_div2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_max2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_min2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_maxnum2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_minnum2h, i32, i32, i32, fpst) +DEF_HELPER_3(advsimd_mulx2h, i32, i32, i32, fpst) +DEF_HELPER_4(advsimd_muladd2h, i32, i32, i32, i32, fpst) +DEF_HELPER_2(advsimd_rinth_exact, f16, f16, fpst) +DEF_HELPER_2(advsimd_rinth, f16, f16, fpst) + +DEF_HELPER_3(vfp_ah_minh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_ah_mins, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_ah_mind, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_ah_maxh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_ah_maxs, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_ah_maxd, f64, f64, f64, fpst) DEF_HELPER_2(exception_return, void, env, i64) DEF_HELPER_FLAGS_2(dc_zva, TCG_CALL_NO_WG, void, env, i64) @@ -117,3 +116,32 @@ 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, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fdiv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmulx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmulx_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 27eef49a11e..858d69188fb 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -121,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) + void, ptr, ptr, ptr, ptr, ptr, fpst, 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) + void, ptr, ptr, ptr, ptr, ptr, fpst, 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/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index cc4e1d89481..0b1b5887834 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -541,10 +541,18 @@ DEF_HELPER_FLAGS_4(sve_fabs_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_fabs_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_fabs_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ah_fabs_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ah_fabs_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ah_fabs_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(sve_fneg_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_fneg_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_fneg_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ah_fneg_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ah_fneg_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ah_fneg_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(sve_not_zpz_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_not_zpz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_not_zpz_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) @@ -959,433 +967,545 @@ DEF_HELPER_FLAGS_4(sve_umini_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(sve_umini_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_5(gvec_recps_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_recps_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_recps_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_rsqrts_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_rsqrts_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_rsqrts_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_ah_recps_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_recps_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_recps_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_ah_rsqrts_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_rsqrts_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_rsqrts_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_ah_fmax_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmax_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmax_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_ah_fmin_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmin_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmin_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_ah_fmaxp_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmaxp_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmaxp_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_ah_fminp_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fminp_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fminp_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_faddv_h, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_faddv_s, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_faddv_d, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fmaxnmv_h, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fmaxnmv_s, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fmaxnmv_d, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fminnmv_h, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fminnmv_s, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fminnmv_d, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fmaxv_h, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fmaxv_s, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fmaxv_d, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fminv_h, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fminv_s, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve_fminv_d, TCG_CALL_NO_RWG, - i64, ptr, ptr, ptr, i32) + i64, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(sve_ah_fmaxv_h, TCG_CALL_NO_RWG, + i64, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sve_ah_fmaxv_s, TCG_CALL_NO_RWG, + i64, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sve_ah_fmaxv_d, TCG_CALL_NO_RWG, + i64, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(sve_ah_fminv_h, TCG_CALL_NO_RWG, + i64, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sve_ah_fminv_s, TCG_CALL_NO_RWG, + i64, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sve_ah_fminv_d, TCG_CALL_NO_RWG, + i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fadda_h, TCG_CALL_NO_RWG, - i64, i64, ptr, ptr, ptr, i32) + i64, i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fadda_s, TCG_CALL_NO_RWG, - i64, i64, ptr, ptr, ptr, i32) + i64, i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fadda_d, TCG_CALL_NO_RWG, - i64, i64, ptr, ptr, ptr, i32) + i64, i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmge0_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmge0_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmge0_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmgt0_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmgt0_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmgt0_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmlt0_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmlt0_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmlt0_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmle0_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmle0_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmle0_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmeq0_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmeq0_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmeq0_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmne0_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmne0_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcmne0_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadd_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadd_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadd_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsub_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsub_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsub_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmul_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmul_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmul_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fdiv_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fdiv_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fdiv_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmin_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmin_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmin_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmax_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmax_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmax_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(sve_ah_fmin_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmin_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmin_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(sve_ah_fmax_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmax_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmax_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnum_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnum_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnum_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnum_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnum_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnum_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fabd_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fabd_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fabd_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(sve_ah_fabd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fabd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fabd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fscalbn_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fscalbn_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fscalbn_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmulx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmulx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmulx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadds_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadds_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadds_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsubs_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsubs_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsubs_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmuls_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmuls_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmuls_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsubrs_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsubrs_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsubrs_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnms_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnms_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnms_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnms_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnms_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnms_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxs_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxs_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxs_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmins_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmins_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmins_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i64, ptr, i32) + void, ptr, ptr, ptr, i64, fpst, i32) + +DEF_HELPER_FLAGS_6(sve_ah_fmaxs_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmaxs_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmaxs_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, fpst, i32) + +DEF_HELPER_FLAGS_6(sve_ah_fmins_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmins_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmins_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvt_sh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvt_dh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvt_hs, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvt_ds, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvt_hd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvt_sd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_bfcvt, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzs_hh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzs_hs, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzs_ss, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzs_ds, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzs_hd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzs_sd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzs_dd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzu_hh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzu_hs, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzu_ss, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzu_ds, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzu_hd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzu_sd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fcvtzu_dd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frint_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frint_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frint_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frintx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frintx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frintx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frecpx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frecpx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_frecpx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fsqrt_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fsqrt_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fsqrt_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_scvt_hh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_scvt_sh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_scvt_dh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_scvt_ss, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_scvt_sd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_scvt_ds, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_scvt_dd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_ucvt_hh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_ucvt_sh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_ucvt_dh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_ucvt_ss, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_ucvt_sd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_ucvt_ds, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_ucvt_dd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmge_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmge_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmge_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmgt_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmgt_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmgt_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmeq_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmeq_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmeq_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmne_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmne_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmne_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmuo_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmuo_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcmuo_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_facge_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_facge_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_facge_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_facgt_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_facgt_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_facgt_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcadd_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcadd_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fcadd_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_7(sve_ah_fnmls_zpzzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fnmls_zpzzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fnmls_zpzzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fcmla_zpzzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fcmla_zpzzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fcmla_zpzzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(sve_ftmad_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve_ftmad_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve_ftmad_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ftmad_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve_ftmad_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve_ftmad_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve2_saddl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_saddl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) @@ -2582,39 +2702,39 @@ DEF_HELPER_FLAGS_4(sve2_xar_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_xar_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_6(sve2_faddp_zpzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_faddp_zpzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_faddp_zpzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fmaxnmp_zpzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fmaxnmp_zpzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fmaxnmp_zpzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fminnmp_zpzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fminnmp_zpzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fminnmp_zpzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fmaxp_zpzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fmaxp_zpzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fmaxp_zpzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fminp_zpzz_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fminp_zpzz_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve2_fminp_zpzz_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve2_eor3, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve2_bcax, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) @@ -2682,8 +2802,8 @@ DEF_HELPER_FLAGS_5(sve2_sqrdcmlah_zzzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve2_sqrdcmlah_zzzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_6(fmmla_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_6(fmmla_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(fmmla_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(fmmla_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve2_sqrdmlah_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) @@ -2755,20 +2875,20 @@ DEF_HELPER_FLAGS_5(sve2_cdot_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve2_fcvtnt_sh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve2_fcvtnt_ds, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_bfcvtnt, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve2_fcvtlt_hs, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve2_fcvtlt_sd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) + void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(flogb_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(flogb_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(flogb_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(flogb_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(flogb_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(flogb_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sve2_sqshl_zpzi_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h new file mode 100644 index 00000000000..80db7c2c37a --- /dev/null +++ b/target/arm/tcg/helper.h @@ -0,0 +1,1153 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +DEF_HELPER_FLAGS_1(sxtb16, TCG_CALL_NO_RWG_SE, i32, i32) +DEF_HELPER_FLAGS_1(uxtb16, TCG_CALL_NO_RWG_SE, i32, i32) + +DEF_HELPER_3(add_setq, i32, env, i32, i32) +DEF_HELPER_3(add_saturate, i32, env, i32, i32) +DEF_HELPER_3(sub_saturate, i32, env, i32, i32) +DEF_HELPER_3(add_usaturate, i32, env, i32, i32) +DEF_HELPER_3(sub_usaturate, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(sdiv, TCG_CALL_NO_RWG, s32, env, s32, s32) +DEF_HELPER_FLAGS_3(udiv, TCG_CALL_NO_RWG, i32, env, i32, i32) +DEF_HELPER_FLAGS_1(rbit, TCG_CALL_NO_RWG_SE, i32, i32) + +#define PAS_OP(pfx) \ + DEF_HELPER_3(pfx ## add8, i32, i32, i32, ptr) \ + DEF_HELPER_3(pfx ## sub8, i32, i32, i32, ptr) \ + DEF_HELPER_3(pfx ## sub16, i32, i32, i32, ptr) \ + DEF_HELPER_3(pfx ## add16, i32, i32, i32, ptr) \ + DEF_HELPER_3(pfx ## addsubx, i32, i32, i32, ptr) \ + DEF_HELPER_3(pfx ## subaddx, i32, i32, i32, ptr) + +PAS_OP(s) +PAS_OP(u) +#undef PAS_OP + +#define PAS_OP(pfx) \ + DEF_HELPER_2(pfx ## add8, i32, i32, i32) \ + DEF_HELPER_2(pfx ## sub8, i32, i32, i32) \ + DEF_HELPER_2(pfx ## sub16, i32, i32, i32) \ + DEF_HELPER_2(pfx ## add16, i32, i32, i32) \ + DEF_HELPER_2(pfx ## addsubx, i32, i32, i32) \ + DEF_HELPER_2(pfx ## subaddx, i32, i32, i32) +PAS_OP(q) +PAS_OP(sh) +PAS_OP(uq) +PAS_OP(uh) +#undef PAS_OP + +DEF_HELPER_3(ssat, i32, env, i32, i32) +DEF_HELPER_3(usat, i32, env, i32, i32) +DEF_HELPER_3(ssat16, i32, env, i32, i32) +DEF_HELPER_3(usat16, i32, env, i32, i32) + +DEF_HELPER_FLAGS_2(usad8, TCG_CALL_NO_RWG_SE, i32, i32, i32) + +DEF_HELPER_FLAGS_3(sel_flags, TCG_CALL_NO_RWG_SE, + i32, i32, i32, i32) +DEF_HELPER_2(exception_internal, noreturn, env, i32) +DEF_HELPER_3(exception_with_syndrome, noreturn, env, i32, i32) +DEF_HELPER_4(exception_with_syndrome_el, noreturn, env, i32, i32, i32) +DEF_HELPER_2(exception_bkpt_insn, noreturn, env, i32) +DEF_HELPER_2(exception_swstep, noreturn, env, i32) +DEF_HELPER_2(exception_pc_alignment, noreturn, env, vaddr) +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) +DEF_HELPER_1(vesb, void, env) + +DEF_HELPER_3(cpsr_write, void, env, i32, i32) +DEF_HELPER_2(cpsr_write_eret, void, env, i32) +DEF_HELPER_1(cpsr_read, i32, env) + +DEF_HELPER_3(v7m_msr, void, env, i32, i32) +DEF_HELPER_2(v7m_mrs, i32, env, i32) + +DEF_HELPER_2(v7m_bxns, void, env, i32) +DEF_HELPER_2(v7m_blxns, void, env, i32) + +DEF_HELPER_3(v7m_tt, i32, env, i32, i32) + +DEF_HELPER_1(v7m_preserve_fp_state, void, env) + +DEF_HELPER_2(v7m_vlstm, void, env, i32) +DEF_HELPER_2(v7m_vlldm, void, env, i32) + +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, 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) + +DEF_HELPER_3(mrs_banked, i32, env, i32, i32) +DEF_HELPER_4(msr_banked, void, env, i32, i32, i32) + +DEF_HELPER_2(get_user_reg, i32, env, i32) +DEF_HELPER_3(set_user_reg, void, env, i32, i32) + +DEF_HELPER_FLAGS_1(rebuild_hflags_m32_newel, TCG_CALL_NO_RWG, void, env) +DEF_HELPER_FLAGS_2(rebuild_hflags_m32, TCG_CALL_NO_RWG, void, env, int) +DEF_HELPER_FLAGS_1(rebuild_hflags_a32_newel, TCG_CALL_NO_RWG, void, env) +DEF_HELPER_FLAGS_2(rebuild_hflags_a32, TCG_CALL_NO_RWG, void, env, int) +DEF_HELPER_FLAGS_2(rebuild_hflags_a64, TCG_CALL_NO_RWG, void, env, int) + +DEF_HELPER_FLAGS_5(probe_access, TCG_CALL_NO_WG, void, env, vaddr, i32, i32, i32) + +DEF_HELPER_1(vfp_get_fpscr, i32, env) +DEF_HELPER_2(vfp_set_fpscr, void, env, i32) + +DEF_HELPER_3(vfp_addh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_adds, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_addd, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_subh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_subs, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_subd, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_mulh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_muls, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_muld, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_divh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_divs, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_divd, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_maxh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_maxs, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_maxd, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_minh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_mins, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_mind, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_maxnumh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_maxnums, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_maxnumd, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_minnumh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_minnums, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_minnumd, f64, f64, f64, fpst) +DEF_HELPER_2(vfp_sqrth, f16, f16, fpst) +DEF_HELPER_2(vfp_sqrts, f32, f32, fpst) +DEF_HELPER_2(vfp_sqrtd, f64, f64, fpst) +DEF_HELPER_3(vfp_cmph, void, f16, f16, env) +DEF_HELPER_3(vfp_cmps, void, f32, f32, env) +DEF_HELPER_3(vfp_cmpd, void, f64, f64, env) +DEF_HELPER_3(vfp_cmpeh, void, f16, f16, env) +DEF_HELPER_3(vfp_cmpes, void, f32, f32, env) +DEF_HELPER_3(vfp_cmped, void, f64, f64, env) + +DEF_HELPER_2(vfp_fcvtds, f64, f32, fpst) +DEF_HELPER_2(vfp_fcvtsd, f32, f64, fpst) +DEF_HELPER_FLAGS_2(bfcvt, TCG_CALL_NO_RWG, i32, f32, fpst) +DEF_HELPER_FLAGS_2(bfcvt_pair, TCG_CALL_NO_RWG, i32, i64, fpst) + +DEF_HELPER_2(vfp_uitoh, f16, i32, fpst) +DEF_HELPER_2(vfp_uitos, f32, i32, fpst) +DEF_HELPER_2(vfp_uitod, f64, i32, fpst) +DEF_HELPER_2(vfp_sitoh, f16, i32, fpst) +DEF_HELPER_2(vfp_sitos, f32, i32, fpst) +DEF_HELPER_2(vfp_sitod, f64, i32, fpst) + +DEF_HELPER_2(vfp_touih, i32, f16, fpst) +DEF_HELPER_2(vfp_touis, i32, f32, fpst) +DEF_HELPER_2(vfp_touid, i32, f64, fpst) +DEF_HELPER_2(vfp_touizh, i32, f16, fpst) +DEF_HELPER_2(vfp_touizs, i32, f32, fpst) +DEF_HELPER_2(vfp_touizd, i32, f64, fpst) +DEF_HELPER_2(vfp_tosih, s32, f16, fpst) +DEF_HELPER_2(vfp_tosis, s32, f32, fpst) +DEF_HELPER_2(vfp_tosid, s32, f64, fpst) +DEF_HELPER_2(vfp_tosizh, s32, f16, fpst) +DEF_HELPER_2(vfp_tosizs, s32, f32, fpst) +DEF_HELPER_2(vfp_tosizd, s32, f64, fpst) + +DEF_HELPER_3(vfp_toshh_round_to_zero, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toslh_round_to_zero, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_touhh_round_to_zero, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toulh_round_to_zero, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toshs_round_to_zero, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_tosls_round_to_zero, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_touhs_round_to_zero, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_touls_round_to_zero, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_toshd_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tosld_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tosqd_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_touhd_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tould_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_touqd_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_touhh, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toshh, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toulh, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toslh, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_touqh, i64, f16, i32, fpst) +DEF_HELPER_3(vfp_tosqh, i64, f16, i32, fpst) +DEF_HELPER_3(vfp_toshs, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_tosls, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_tosqs, i64, f32, i32, fpst) +DEF_HELPER_3(vfp_touhs, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_touls, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_touqs, i64, f32, i32, fpst) +DEF_HELPER_3(vfp_toshd, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tosld, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tosqd, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_touhd, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tould, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_touqd, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_shtos, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_sltos, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_sqtos, f32, i64, i32, fpst) +DEF_HELPER_3(vfp_uhtos, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_ultos, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_uqtos, f32, i64, i32, fpst) +DEF_HELPER_3(vfp_shtod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_sltod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_sqtod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_uhtod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_ultod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_uqtod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_shtoh, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_uhtoh, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_sltoh, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_ultoh, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_sqtoh, f16, i64, i32, fpst) +DEF_HELPER_3(vfp_uqtoh, f16, i64, i32, fpst) + +DEF_HELPER_3(vfp_shtos_round_to_nearest, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_sltos_round_to_nearest, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_uhtos_round_to_nearest, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_ultos_round_to_nearest, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_shtod_round_to_nearest, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_sltod_round_to_nearest, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_uhtod_round_to_nearest, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_ultod_round_to_nearest, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_shtoh_round_to_nearest, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_uhtoh_round_to_nearest, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_sltoh_round_to_nearest, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_ultoh_round_to_nearest, f16, i32, i32, fpst) + +DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, fpst) + +DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f32, TCG_CALL_NO_RWG, f32, f16, fpst, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f32_to_f16, TCG_CALL_NO_RWG, f16, f32, fpst, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f64, TCG_CALL_NO_RWG, f64, f16, fpst, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f64_to_f16, TCG_CALL_NO_RWG, f16, f64, fpst, i32) + +DEF_HELPER_4(vfp_muladdd, f64, f64, f64, f64, fpst) +DEF_HELPER_4(vfp_muladds, f32, f32, f32, f32, fpst) +DEF_HELPER_4(vfp_muladdh, f16, f16, f16, f16, fpst) + +DEF_HELPER_FLAGS_2(recpe_f16, TCG_CALL_NO_RWG, f16, f16, fpst) +DEF_HELPER_FLAGS_2(recpe_f32, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(recpe_rpres_f32, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(recpe_f64, TCG_CALL_NO_RWG, f64, f64, fpst) +DEF_HELPER_FLAGS_2(rsqrte_f16, TCG_CALL_NO_RWG, f16, f16, fpst) +DEF_HELPER_FLAGS_2(rsqrte_f32, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(rsqrte_rpres_f32, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(rsqrte_f64, TCG_CALL_NO_RWG, f64, f64, fpst) +DEF_HELPER_FLAGS_1(recpe_u32, TCG_CALL_NO_RWG, i32, i32) +DEF_HELPER_FLAGS_1(rsqrte_u32, TCG_CALL_NO_RWG, i32, i32) +DEF_HELPER_FLAGS_4(neon_tbl, TCG_CALL_NO_RWG, i64, env, i32, i64, i64) + +DEF_HELPER_3(shl_cc, i32, env, i32, i32) +DEF_HELPER_3(shr_cc, i32, env, i32, i32) +DEF_HELPER_3(sar_cc, i32, env, i32, i32) +DEF_HELPER_3(ror_cc, i32, env, i32, i32) + +DEF_HELPER_FLAGS_2(rinth_exact, TCG_CALL_NO_RWG, f16, f16, fpst) +DEF_HELPER_FLAGS_2(rints_exact, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(rintd_exact, TCG_CALL_NO_RWG, f64, f64, fpst) +DEF_HELPER_FLAGS_2(rinth, TCG_CALL_NO_RWG, f16, f16, fpst) +DEF_HELPER_FLAGS_2(rints, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(rintd, TCG_CALL_NO_RWG, f64, f64, fpst) + +DEF_HELPER_FLAGS_2(vjcvt, TCG_CALL_NO_RWG, i32, f64, env) +DEF_HELPER_FLAGS_2(fjcvtzs, TCG_CALL_NO_RWG, i64, f64, fpst) + +DEF_HELPER_FLAGS_3(check_hcr_el2_trap, TCG_CALL_NO_WG, void, env, i32, i32) + +/* neon_helper.c */ +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) +DEF_HELPER_2(neon_pmin_s16, i32, i32, i32) +DEF_HELPER_2(neon_pmax_u8, i32, i32, i32) +DEF_HELPER_2(neon_pmax_s8, i32, i32, i32) +DEF_HELPER_2(neon_pmax_u16, i32, i32, i32) +DEF_HELPER_2(neon_pmax_s16, i32, i32, i32) + +DEF_HELPER_2(neon_shl_u16, i32, i32, i32) +DEF_HELPER_2(neon_shl_s16, i32, i32, i32) +DEF_HELPER_2(neon_rshl_u8, i32, i32, i32) +DEF_HELPER_2(neon_rshl_s8, i32, i32, i32) +DEF_HELPER_2(neon_rshl_u16, i32, i32, i32) +DEF_HELPER_2(neon_rshl_s16, i32, i32, i32) +DEF_HELPER_2(neon_rshl_u32, i32, i32, i32) +DEF_HELPER_2(neon_rshl_s32, i32, i32, i32) +DEF_HELPER_2(neon_rshl_u64, i64, i64, i64) +DEF_HELPER_2(neon_rshl_s64, i64, i64, i64) +DEF_HELPER_3(neon_qshl_u8, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_s8, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_u16, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_u32, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_s32, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_u64, i64, env, i64, i64) +DEF_HELPER_3(neon_qshl_s64, i64, env, i64, i64) +DEF_HELPER_3(neon_qshlu_s8, i32, env, i32, i32) +DEF_HELPER_3(neon_qshlu_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qshlu_s32, i32, env, i32, i32) +DEF_HELPER_3(neon_qshlu_s64, i64, env, i64, i64) +DEF_HELPER_3(neon_qrshl_u8, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_s8, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_u16, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_s16, i32, env, i32, i32) +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, env, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, 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_sub_u8, i32, i32, i32) +DEF_HELPER_2(neon_sub_u16, i32, i32, i32) +DEF_HELPER_2(neon_mul_u8, i32, i32, i32) +DEF_HELPER_2(neon_mul_u16, i32, i32, i32) + +DEF_HELPER_2(neon_tst_u8, i32, i32, i32) +DEF_HELPER_2(neon_tst_u16, i32, i32, i32) +DEF_HELPER_2(neon_tst_u32, i32, i32, i32) + +DEF_HELPER_1(neon_clz_u8, i32, i32) +DEF_HELPER_1(neon_clz_u16, i32, i32) +DEF_HELPER_1(neon_cls_s8, i32, i32) +DEF_HELPER_1(neon_cls_s16, i32, i32) +DEF_HELPER_1(neon_cls_s32, i32, i32) +DEF_HELPER_FLAGS_3(gvec_cnt_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_rbit_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_3(neon_qdmulh_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qrdmulh_s16, i32, env, i32, i32) +DEF_HELPER_4(neon_qrdmlah_s16, i32, env, i32, i32, i32) +DEF_HELPER_4(neon_qrdmlsh_s16, i32, env, i32, i32, i32) +DEF_HELPER_3(neon_qdmulh_s32, i32, env, i32, i32) +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, 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) +DEF_HELPER_1(neon_narrow_round_high_u16, i32, i64) +DEF_HELPER_1(neon_widen_u8, i64, i32) +DEF_HELPER_1(neon_widen_s8, i64, i32) +DEF_HELPER_1(neon_widen_u16, i64, i32) +DEF_HELPER_1(neon_widen_s16, i64, i32) + +DEF_HELPER_FLAGS_1(neon_addlp_s8, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_1(neon_addlp_s16, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_3(neon_addl_saturate_s32, i64, env, i64, i64) +DEF_HELPER_3(neon_addl_saturate_s64, i64, env, i64, i64) +DEF_HELPER_2(neon_abdl_u16, i64, i32, i32) +DEF_HELPER_2(neon_abdl_s16, i64, i32, i32) +DEF_HELPER_2(neon_abdl_u32, i64, i32, i32) +DEF_HELPER_2(neon_abdl_s32, i64, i32, i32) +DEF_HELPER_2(neon_abdl_u64, i64, i32, i32) +DEF_HELPER_2(neon_abdl_s64, i64, i32, i32) +DEF_HELPER_2(neon_mull_u8, i64, i32, i32) +DEF_HELPER_2(neon_mull_s8, i64, i32, i32) +DEF_HELPER_2(neon_mull_u16, i64, i32, i32) +DEF_HELPER_2(neon_mull_s16, i64, i32, i32) + +DEF_HELPER_1(neon_negl_u16, i64, i64) +DEF_HELPER_1(neon_negl_u32, i64, i64) + +DEF_HELPER_FLAGS_2(neon_qabs_s8, TCG_CALL_NO_RWG, i32, env, i32) +DEF_HELPER_FLAGS_2(neon_qabs_s16, TCG_CALL_NO_RWG, i32, env, i32) +DEF_HELPER_FLAGS_2(neon_qabs_s32, TCG_CALL_NO_RWG, i32, env, i32) +DEF_HELPER_FLAGS_2(neon_qabs_s64, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_2(neon_qneg_s8, TCG_CALL_NO_RWG, i32, env, i32) +DEF_HELPER_FLAGS_2(neon_qneg_s16, TCG_CALL_NO_RWG, i32, env, i32) +DEF_HELPER_FLAGS_2(neon_qneg_s32, TCG_CALL_NO_RWG, i32, env, i32) +DEF_HELPER_FLAGS_2(neon_qneg_s64, TCG_CALL_NO_RWG, i64, env, i64) + +DEF_HELPER_3(neon_ceq_f32, i32, i32, i32, fpst) +DEF_HELPER_3(neon_cge_f32, i32, i32, i32, fpst) +DEF_HELPER_3(neon_cgt_f32, i32, i32, i32, fpst) +DEF_HELPER_3(neon_acge_f32, i32, i32, i32, fpst) +DEF_HELPER_3(neon_acgt_f32, i32, i32, i32, fpst) +DEF_HELPER_3(neon_acge_f64, i64, i64, i64, fpst) +DEF_HELPER_3(neon_acgt_f64, i64, i64, i64, fpst) + +/* iwmmxt_helper.c */ +DEF_HELPER_2(iwmmxt_maddsq, i64, i64, i64) +DEF_HELPER_2(iwmmxt_madduq, i64, i64, i64) +DEF_HELPER_2(iwmmxt_sadb, i64, i64, i64) +DEF_HELPER_2(iwmmxt_sadw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_mulslw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_mulshw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_mululw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_muluhw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_macsw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_macuw, i64, i64, i64) +DEF_HELPER_1(iwmmxt_setpsr_nz, i32, i64) + +#define DEF_IWMMXT_HELPER_SIZE_ENV(name) \ +DEF_HELPER_3(iwmmxt_##name##b, i64, env, i64, i64) \ +DEF_HELPER_3(iwmmxt_##name##w, i64, env, i64, i64) \ +DEF_HELPER_3(iwmmxt_##name##l, i64, env, i64, i64) \ + +DEF_IWMMXT_HELPER_SIZE_ENV(unpackl) +DEF_IWMMXT_HELPER_SIZE_ENV(unpackh) + +DEF_HELPER_2(iwmmxt_unpacklub, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackluw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklul, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhub, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhuw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhul, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklsb, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklsw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklsl, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhsb, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhsw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhsl, i64, env, i64) + +DEF_IWMMXT_HELPER_SIZE_ENV(cmpeq) +DEF_IWMMXT_HELPER_SIZE_ENV(cmpgtu) +DEF_IWMMXT_HELPER_SIZE_ENV(cmpgts) + +DEF_IWMMXT_HELPER_SIZE_ENV(mins) +DEF_IWMMXT_HELPER_SIZE_ENV(minu) +DEF_IWMMXT_HELPER_SIZE_ENV(maxs) +DEF_IWMMXT_HELPER_SIZE_ENV(maxu) + +DEF_IWMMXT_HELPER_SIZE_ENV(subn) +DEF_IWMMXT_HELPER_SIZE_ENV(addn) +DEF_IWMMXT_HELPER_SIZE_ENV(subu) +DEF_IWMMXT_HELPER_SIZE_ENV(addu) +DEF_IWMMXT_HELPER_SIZE_ENV(subs) +DEF_IWMMXT_HELPER_SIZE_ENV(adds) + +DEF_HELPER_3(iwmmxt_avgb0, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_avgb1, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_avgw0, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_avgw1, i64, env, i64, i64) + +DEF_HELPER_3(iwmmxt_align, i64, i64, i64, i32) +DEF_HELPER_4(iwmmxt_insr, i64, i64, i32, i32, i32) + +DEF_HELPER_1(iwmmxt_bcstb, i64, i32) +DEF_HELPER_1(iwmmxt_bcstw, i64, i32) +DEF_HELPER_1(iwmmxt_bcstl, i64, i32) + +DEF_HELPER_1(iwmmxt_addcb, i64, i64) +DEF_HELPER_1(iwmmxt_addcw, i64, i64) +DEF_HELPER_1(iwmmxt_addcl, i64, i64) + +DEF_HELPER_1(iwmmxt_msbb, i32, i64) +DEF_HELPER_1(iwmmxt_msbw, i32, i64) +DEF_HELPER_1(iwmmxt_msbl, i32, i64) + +DEF_HELPER_3(iwmmxt_srlw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_srll, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_srlq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sllw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_slll, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sllq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sraw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sral, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sraq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_rorw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_rorl, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_rorq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_shufh, i64, env, i64, i32) + +DEF_HELPER_3(iwmmxt_packuw, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packul, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packuq, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packsw, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packsl, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packsq, i64, env, i64, i64) + +DEF_HELPER_3(iwmmxt_muladdsl, i64, i64, i32, i32) +DEF_HELPER_3(iwmmxt_muladdsw, i64, i64, i32, i32) +DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32) + +DEF_HELPER_FLAGS_2(neon_unzip8, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_unzip16, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_qunzip8, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_qunzip16, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_qunzip32, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_zip8, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_zip16, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_qzip8, TCG_CALL_NO_RWG, void, ptr, ptr) +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) +DEF_HELPER_FLAGS_4(crypto_sha1p, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sha1m, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(crypto_sha1h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(crypto_sha1su1, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(crypto_sha256h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sha256h2, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(crypto_sha256su0, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sha256su1, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(crypto_sha512h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sha512h2, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(crypto_sha512su0, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sha512su1, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(crypto_sm3tt1a, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sm3tt1b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sm3tt2a, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sm3tt2b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sm3partw1, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sm3partw2, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(crypto_sm4e, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sm4ekey, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(crypto_rax1, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) +DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) + +DEF_HELPER_FLAGS_5(gvec_qrdmlah_s16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_qrdmlsh_s16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_qrdmlah_s32, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_qrdmlsh_s32, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve2_sqrdmlah_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve2_sqrdmlah_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve2_sqrdmlah_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve2_sqrdmlah_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_sdot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_udot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sdot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_udot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usdot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_sdot_idx_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_udot_idx_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sdot_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_udot_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sudot_idx_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usdot_idx_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fcaddh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcadds, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcaddd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(gvec_fcmlah, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fcmlah_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fcmlas, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fcmlas_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fcmlad, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_sstoh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_sitos, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_ustoh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_uitos, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_tosszh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_tosizs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_touszh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_touizs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_vcvt_sf, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_uf, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_fs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_fu, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_vcvt_sh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_uh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_hs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_hu, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_vcvt_sd, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_ud, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_ds, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_du, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_sd, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ud, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ss, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_us, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_sh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_uh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_vrint_rm_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vrint_rm_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_vrintx_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vrintx_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_frecpe_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_rpres_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_frsqrte_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frsqrte_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frsqrte_rpres_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frsqrte_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_fcgt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcgt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcgt0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_fcge0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcge0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcge0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_fceq0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fceq0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fceq0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_fcle0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcle0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcle0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_fclt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fclt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fclt0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_ah_fabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fceq_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fceq_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fceq_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fcge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fcgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_facge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_facge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_facge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_facgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_facgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_facgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmax_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmax_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmin_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmin_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmaxnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fminnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_recps_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_recps_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_rsqrts_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_rsqrts_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmla_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmla_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmls_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmls_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_vfma_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_vfma_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_vfma_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_vfms_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_vfms_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_vfms_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_ah_vfms_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_vfms_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_vfms_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_ftsmul_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ftsmul_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ftsmul_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmul_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmla_nf_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmla_nf_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmls_nf_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmls_nf_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(gvec_fmla_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fmla_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fmla_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(gvec_fmls_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fmls_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fmls_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(gvec_ah_fmls_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_ah_fmls_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_ah_fmls_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_uqadd_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqadd_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqsub_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqsub_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqsub_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqsub_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqsub_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqsub_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +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, env, i32) +DEF_HELPER_FLAGS_5(gvec_fmlal_a64, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a32, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a64, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_2(frint32_s, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(frint64_s, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(frint32_d, TCG_CALL_NO_RWG, f64, f64, fpst) +DEF_HELPER_FLAGS_2(frint64_d, TCG_CALL_NO_RWG, f64, f64, fpst) + +DEF_HELPER_FLAGS_3(gvec_ceq0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ceq0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_clt0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_clt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_cle0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_cle0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_cgt0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_cgt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_cge0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_cge0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_smulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_umulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_sshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_ushl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_ushl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_pmul_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_pmull_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(neon_pmull_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_ssra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ssra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ssra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ssra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_usra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_usra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_usra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_usra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_srshr_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_srshr_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_srshr_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_srshr_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_urshr_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_urshr_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_urshr_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_urshr_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_srsra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_srsra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_srsra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_srsra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_ursra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ursra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ursra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ursra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_sri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_sri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_sri_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_sri_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_sli_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_sli_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_sli_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_sli_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_sabd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_uabd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_saba_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_saba_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_saba_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_saba_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_uaba_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uaba_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uaba_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uaba_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_mul_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_mul_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_mul_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_mla_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_mla_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_mla_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_mls_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_mls_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_mls_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(neon_sqdmulh_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqdmulh_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(neon_sqrdmulh_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +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) +DEF_HELPER_FLAGS_4(sve2_sqdmulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve2_sqrdmulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqrdmulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqrdmulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqrdmulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve2_sqdmulh_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqdmulh_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqdmulh_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve2_sqrdmulh_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqrdmulh_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqrdmulh_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve2_fmlal_zzzw_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(sve2_fmlal_zzxw_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(gvec_xar_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_smmla_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_ummla_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usmmla_b, 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_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, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_bfmlal_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_sclamp_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sclamp_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sclamp_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sclamp_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_uclamp_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uclamp_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uclamp_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uclamp_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_faddp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_faddp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_faddp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmaxnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fminnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, 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) + +DEF_HELPER_FLAGS_3(gvec_urecpe_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ursqrte_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index 616c5fa7237..1ccec63bbd4 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -8,9 +8,14 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" -#include "exec/helper-proto.h" +#include "cpu-features.h" +#include "exec/translation-block.h" +#include "accel/tcg/cpu-ops.h" #include "cpregs.h" +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" + static inline bool fgt_svc(CPUARMState *env, int el) { /* @@ -25,6 +30,52 @@ static inline bool fgt_svc(CPUARMState *env, int el) 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 +} + +bool access_secure_reg(CPUARMState *env) +{ + bool ret = (arm_feature(env, ARM_FEATURE_EL3) && + !arm_el_is_aa64(env, 3) && + !(env->cp15.scr_el3 & SCR_NS)); + + return ret; +} + static CPUARMTBFlags rebuild_hflags_common(CPUARMState *env, int fp_el, ARMMMUIdx mmu_idx, CPUARMTBFlags flags) @@ -120,8 +171,9 @@ static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, { CPUARMTBFlags flags = {}; int el = arm_current_el(env); + uint64_t sctlr = arm_sctlr(env, el); - if (arm_sctlr(env, el) & SCTLR_A) { + if (aprofile_require_alignment(env, el, sctlr)) { DP_TBFLAG_ANY(flags, ALIGN_MEM, 1); } @@ -168,6 +220,7 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, 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; @@ -221,7 +274,7 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, sctlr = regime_sctlr(env, stage1); - if (sctlr & SCTLR_A) { + if (aprofile_require_alignment(env, el, sctlr)) { DP_TBFLAG_ANY(flags, ALIGN_MEM, 1); } @@ -259,8 +312,10 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, switch (mmu_idx) { case ARMMMUIdx_E10_1: case ARMMMUIdx_E10_1_PAN: - /* TODO: ARMv8.3-NV */ - DP_TBFLAG_A64(flags, UNPRIV, 1); + /* 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: @@ -284,13 +339,34 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, if (arm_fgt_active(env, el)) { DP_TBFLAG_ANY(flags, FGT_ACTIVE, 1); if (FIELD_EX64(env->cp15.fgt_exec[FGTREG_HFGITR], HFGITR_EL2, ERET)) { - DP_TBFLAG_A64(flags, FGT_ERET, 1); + 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 @@ -306,6 +382,15 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, && !(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. */ @@ -316,10 +401,35 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, && 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)); } + if (env->vfp.fpcr & FPCR_AH) { + DP_TBFLAG_A64(flags, AH, 1); + } + if (env->vfp.fpcr & FPCR_NEP) { + /* + * In streaming-SVE without FA64, NEP behaves as if zero; + * compare pseudocode IsMerging() + */ + if (!(EX_TBFLAG_A64(flags, PSTATE_SM) && !sme_fa64(env, el))) { + DP_TBFLAG_A64(flags, NEP, 1); + } + } + return rebuild_hflags_common(env, fp_el, mmu_idx, flags); } @@ -392,7 +502,7 @@ void HELPER(rebuild_hflags_a64)(CPUARMState *env, int el) env->hflags = rebuild_hflags_a64(env, el, fp_el, mmu_idx); } -void assert_hflags_rebuild_correctly(CPUARMState *env) +static void assert_hflags_rebuild_correctly(CPUARMState *env) { #ifdef CONFIG_DEBUG_TCG CPUARMTBFlags c = env->hflags; @@ -400,10 +510,123 @@ void assert_hflags_rebuild_correctly(CPUARMState *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", + "(current:(0x%08x,0x%016" PRIx64 ")" + " rebuilt:(0x%08x,0x%016" PRIx64 ")\n", c.flags, c.flags2, r.flags, r.flags2); abort(); } #endif } + +static bool mve_no_pred(CPUARMState *env) +{ + /* + * Return true if there is definitely no predication of MVE + * instructions by VPR or LTPSIZE. (Returning false even if there + * isn't any predication is OK; generated code will just be + * a little worse.) + * If the CPU does not implement MVE then this TB flag is always 0. + * + * NOTE: if you change this logic, the "recalculate s->mve_no_pred" + * logic in gen_update_fp_context() needs to be updated to match. + * + * We do not include the effect of the ECI bits here -- they are + * tracked in other TB flags. This simplifies the logic for + * "when did we emit code that changes the MVE_NO_PRED TB flag + * and thus need to end the TB?". + */ + if (cpu_isar_feature(aa32_mve, env_archcpu(env))) { + return false; + } + if (env->v7m.vpr) { + return false; + } + if (env->v7m.ltpsize < 4) { + return false; + } + return true; +} + +TCGTBCPUState arm_get_tb_cpu_state(CPUState *cs) +{ + CPUARMState *env = cpu_env(cs); + CPUARMTBFlags flags; + vaddr pc; + + assert_hflags_rebuild_correctly(env); + flags = env->hflags; + + if (EX_TBFLAG_ANY(flags, AARCH64_STATE)) { + pc = env->pc; + if (cpu_isar_feature(aa64_bti, env_archcpu(env))) { + DP_TBFLAG_A64(flags, BTYPE, env->btype); + } + } else { + pc = env->regs[15]; + + if (arm_feature(env, ARM_FEATURE_M)) { + if (arm_feature(env, ARM_FEATURE_M_SECURITY) && + FIELD_EX32(env->v7m.fpccr[M_REG_S], V7M_FPCCR, S) + != env->v7m.secure) { + DP_TBFLAG_M32(flags, FPCCR_S_WRONG, 1); + } + + if ((env->v7m.fpccr[env->v7m.secure] & R_V7M_FPCCR_ASPEN_MASK) && + (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) || + (env->v7m.secure && + !(env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)))) { + /* + * ASPEN is set, but FPCA/SFPA indicate that there is no + * active FP context; we must create a new FP context before + * executing any FP insn. + */ + DP_TBFLAG_M32(flags, NEW_FP_CTXT_NEEDED, 1); + } + + bool is_secure = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK; + if (env->v7m.fpccr[is_secure] & R_V7M_FPCCR_LSPACT_MASK) { + DP_TBFLAG_M32(flags, LSPACT, 1); + } + + if (mve_no_pred(env)) { + DP_TBFLAG_M32(flags, MVE_NO_PRED, 1); + } + } else { + /* + * Note that XSCALE_CPAR shares bits with VECSTRIDE. + * Note that VECLEN+VECSTRIDE are RES0 for M-profile. + */ + if (arm_feature(env, ARM_FEATURE_XSCALE)) { + DP_TBFLAG_A32(flags, XSCALE_CPAR, env->cp15.c15_cpar); + } else { + DP_TBFLAG_A32(flags, VECLEN, env->vfp.vec_len); + DP_TBFLAG_A32(flags, VECSTRIDE, env->vfp.vec_stride); + } + if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) { + DP_TBFLAG_A32(flags, VFPEN, 1); + } + } + + DP_TBFLAG_AM32(flags, THUMB, env->thumb); + DP_TBFLAG_AM32(flags, CONDEXEC, env->condexec_bits); + } + + /* + * The SS_ACTIVE and PSTATE_SS bits correspond to the state machine + * states defined in the ARM ARM for software singlestep: + * SS_ACTIVE PSTATE.SS State + * 0 x Inactive (the TB flag for SS is always 0) + * 1 0 Active-pending + * 1 1 Active-not-pending + * SS_ACTIVE is set in hflags; PSTATE__SS is computed every TB. + */ + if (EX_TBFLAG_ANY(flags, SS_ACTIVE) && (env->pstate & PSTATE_SS)) { + DP_TBFLAG_ANY(flags, PSTATE__SS, 1); + } + + return (TCGTBCPUState){ + .pc = pc, + .flags = flags.flags, + .cs_base = flags.flags2, + }; +} diff --git a/target/arm/tcg/iwmmxt_helper.c b/target/arm/tcg/iwmmxt_helper.c index 610b1b2103e..ba054b6b4db 100644 --- a/target/arm/tcg/iwmmxt_helper.c +++ b/target/arm/tcg/iwmmxt_helper.c @@ -22,7 +22,9 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/helper-proto.h" + +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" /* iwMMXt macros extracted from GNU gdb. */ diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index 0045c18f80f..6614719832e 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -9,14 +9,15 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" +#include "cpu-features.h" #include "gdbstub/helpers.h" #include "exec/helper-proto.h" #include "qemu/main-loop.h" #include "qemu/bitops.h" #include "qemu/log.h" -#include "exec/exec-all.h" +#include "exec/page-protection.h" #ifdef CONFIG_TCG -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "semihosting/common-semi.h" #endif #if !defined(CONFIG_USER_ONLY) @@ -220,7 +221,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) { @@ -309,7 +310,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, @@ -372,8 +373,8 @@ void HELPER(v7m_preserve_fp_state)(CPUARMState *env) bool ts = is_secure && (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK); bool take_exception; - /* Take the iothread lock as we are going to touch the NVIC */ - qemu_mutex_lock_iothread(); + /* Take the BQL as we are going to touch the NVIC */ + bql_lock(); /* Check the background context had access to the FPU */ if (!v7m_cpacr_pass(env, is_secure, is_priv)) { @@ -427,7 +428,7 @@ void HELPER(v7m_preserve_fp_state)(CPUARMState *env) take_exception = !stacked_ok && armv7m_nvic_can_take_pending_exception(env->nvic); - qemu_mutex_unlock_iothread(); + bql_unlock(); if (take_exception) { raise_exception_ra(env, EXCP_LAZYFP, 0, 1, GETPC()); @@ -2007,7 +2008,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); @@ -2043,7 +2044,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, diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 6fca38f2ccb..c59f0f03a1b 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -24,24 +24,21 @@ 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', @@ -55,3 +52,29 @@ arm_ss.add(when: 'TARGET_AARCH64', if_true: files( 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')) + +arm_common_ss.add(zlib) + +arm_common_ss.add(files( + 'arith_helper.c', + 'crypto_helper.c', +)) + +arm_common_system_ss.add(files( + 'hflags.c', + 'iwmmxt_helper.c', + 'neon_helper.c', + 'tlb_helper.c', + 'tlb-insns.c', + 'vfp_helper.c', +)) +arm_user_ss.add(files( + 'hflags.c', + 'iwmmxt_helper.c', + 'neon_helper.c', + 'tlb_helper.c', + 'vfp_helper.c', +)) diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index b23d11563ab..0efc18a181e 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -21,14 +21,21 @@ #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/page-protection.h" +#ifdef CONFIG_USER_ONLY +#include "user/cpu_loop.h" +#include "user/page-protection.h" +#else +#include "system/ram_addr.h" +#endif +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "exec/helper-proto.h" -#include "hw/core/tcg-cpu-ops.h" +#include "exec/tlb-flags.h" +#include "accel/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) { @@ -49,43 +56,24 @@ static int choose_nonexcluded_tag(int tag, int offset, uint16_t exclude) 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) +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 + const size_t page_data_size = TARGET_PAGE_SIZE >> (LOG2_TAG_GRANULE + 1); 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); } @@ -95,7 +83,7 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, return NULL; } - tags = page_get_target_data(clean_ptr); + tags = page_get_target_data(clean_ptr, page_data_size); index = extract32(ptr, LOG2_TAG_GRANULE + 1, TARGET_PAGE_BITS - LOG2_TAG_GRANULE - 1); @@ -115,16 +103,20 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, * exception for inaccessible pages, and resolves the virtual address * into the softmmu tlb. * - * When RA == 0, this is for mte_probe. The page is expected to be - * valid. Indicate to probe_access_flags no-fault, then assert that - * we received a valid page. + * When RA == 0, this is either a pure probe or a no-fault-expected probe. + * Indicate to probe_access_flags no-fault, then either return NULL + * for the pure probe, or assert that we received a valid page for the + * no-fault-expected probe. */ flags = probe_access_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->pte_attrs != 0xf0) { + if (full->extra.arm.pte_attrs != 0xf0) { return NULL; } @@ -161,7 +153,7 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, } /* Any debug exception has priority over a tag check exception. */ - if (unlikely(flags & TLB_WATCHPOINT)) { + if (!probe && unlikely(flags & TLB_WATCHPOINT)) { int wp = ptr_access == MMU_DATA_LOAD ? BP_MEM_READ : BP_MEM_WRITE; assert(ra != 0); cpu_check_watchpoint(env_cpu(env), ptr, ptr_size, attrs, wp, ra); @@ -203,6 +195,15 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, #endif } +static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx, + uint64_t ptr, MMUAccessType ptr_access, + int ptr_size, MMUAccessType tag_access, + uintptr_t ra) +{ + return allocation_tag_mem_probe(env, ptr_mmu_idx, ptr, ptr_access, + ptr_size, tag_access, false, ra); +} + uint64_t HELPER(irg)(CPUARMState *env, uint64_t rn, uint64_t rm) { uint16_t exclude = extract32(rm | env->cp15.gcr_el1, 0, 16); @@ -261,7 +262,7 @@ uint64_t HELPER(addsubg)(CPUARMState *env, uint64_t ptr, return address_with_allocation_tag(ptr + offset, rtag); } -static int load_tag1(uint64_t ptr, uint8_t *mem) +int load_tag1(uint64_t ptr, uint8_t *mem) { int ofs = extract32(ptr, LOG2_TAG_GRANULE, 1) * 4; return extract32(*mem, ofs, 4); @@ -269,13 +270,13 @@ static int load_tag1(uint64_t ptr, uint8_t *mem) uint64_t HELPER(ldg)(CPUARMState *env, uint64_t ptr, uint64_t xt) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); uint8_t *mem; int rtag = 0; /* Trap if accessing an invalid page. */ mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_LOAD, 1, - MMU_DATA_LOAD, 1, GETPC()); + MMU_DATA_LOAD, GETPC()); /* Load if page supports tags. */ if (mem) { @@ -289,13 +290,13 @@ static void check_tag_aligned(CPUARMState *env, uint64_t ptr, uintptr_t ra) { if (unlikely(!QEMU_IS_ALIGNED(ptr, TAG_GRANULE))) { arm_cpu_do_unaligned_access(env_cpu(env), ptr, MMU_DATA_STORE, - cpu_mmu_index(env, false), ra); + arm_env_mmu_index(env), ra); g_assert_not_reached(); } } /* For use in a non-parallel context, store to the given nibble. */ -static void store_tag1(uint64_t ptr, uint8_t *mem, int tag) +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); @@ -322,14 +323,14 @@ typedef void stg_store1(uint64_t, uint8_t *, int); static inline void do_stg(CPUARMState *env, uint64_t ptr, uint64_t xt, uintptr_t ra, stg_store1 store1) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); uint8_t *mem; check_tag_aligned(env, ptr, ra); /* Trap if accessing an invalid page. */ mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, TAG_GRANULE, - MMU_DATA_STORE, 1, ra); + MMU_DATA_STORE, ra); /* Store if page supports tags. */ if (mem) { @@ -349,7 +350,7 @@ void HELPER(stg_parallel)(CPUARMState *env, uint64_t ptr, uint64_t xt) void HELPER(stg_stub)(CPUARMState *env, uint64_t ptr) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); uintptr_t ra = GETPC(); check_tag_aligned(env, ptr, ra); @@ -359,7 +360,7 @@ void HELPER(stg_stub)(CPUARMState *env, uint64_t ptr) static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt, uintptr_t ra, stg_store1 store1) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); int tag = allocation_tag_from_addr(xt); uint8_t *mem1, *mem2; @@ -372,10 +373,10 @@ static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt, if (ptr & TAG_GRANULE) { /* Two stores unaligned mod TAG_GRANULE*2 -- modify two bytes. */ mem1 = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, - TAG_GRANULE, MMU_DATA_STORE, 1, ra); + TAG_GRANULE, MMU_DATA_STORE, ra); mem2 = allocation_tag_mem(env, mmu_idx, ptr + TAG_GRANULE, MMU_DATA_STORE, TAG_GRANULE, - MMU_DATA_STORE, 1, ra); + MMU_DATA_STORE, ra); /* Store if page(s) support tags. */ if (mem1) { @@ -387,7 +388,7 @@ static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt, } else { /* Two stores aligned mod TAG_GRANULE*2 -- modify one byte. */ mem1 = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, - 2 * TAG_GRANULE, MMU_DATA_STORE, 1, ra); + 2 * TAG_GRANULE, MMU_DATA_STORE, ra); if (mem1) { tag |= tag << 4; qatomic_set(mem1, tag); @@ -407,7 +408,7 @@ void HELPER(st2g_parallel)(CPUARMState *env, uint64_t ptr, uint64_t xt) void HELPER(st2g_stub)(CPUARMState *env, uint64_t ptr) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); uintptr_t ra = GETPC(); int in_page = -(ptr | TARGET_PAGE_MASK); @@ -423,7 +424,7 @@ void HELPER(st2g_stub)(CPUARMState *env, uint64_t ptr) uint64_t HELPER(ldgm)(CPUARMState *env, uint64_t ptr) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); uintptr_t ra = GETPC(); int gm_bs = env_archcpu(env)->gm_blocksize; int gm_bs_bytes = 4 << gm_bs; @@ -435,8 +436,7 @@ uint64_t HELPER(ldgm)(CPUARMState *env, uint64_t ptr) /* Trap if accessing an invalid page. */ tag_mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_LOAD, - gm_bs_bytes, MMU_DATA_LOAD, - gm_bs_bytes / (2 * TAG_GRANULE), ra); + gm_bs_bytes, MMU_DATA_LOAD, ra); /* The tag is squashed to zero if the page does not support tags. */ if (!tag_mem) { @@ -484,7 +484,7 @@ uint64_t HELPER(ldgm)(CPUARMState *env, uint64_t ptr) void HELPER(stgm)(CPUARMState *env, uint64_t ptr, uint64_t val) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = arm_env_mmu_index(env); uintptr_t ra = GETPC(); int gm_bs = env_archcpu(env)->gm_blocksize; int gm_bs_bytes = 4 << gm_bs; @@ -495,8 +495,7 @@ void HELPER(stgm)(CPUARMState *env, uint64_t ptr, uint64_t val) /* Trap if accessing an invalid page. */ tag_mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, - gm_bs_bytes, MMU_DATA_LOAD, - gm_bs_bytes / (2 * TAG_GRANULE), ra); + gm_bs_bytes, MMU_DATA_LOAD, ra); /* * Tag store only happens if the page support tags, @@ -535,7 +534,7 @@ void HELPER(stgm)(CPUARMState *env, uint64_t ptr, uint64_t 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 mmu_idx = arm_env_mmu_index(env); int log2_dcz_bytes, log2_tag_bytes; intptr_t dcz_bytes, tag_bytes; uint8_t *mem; @@ -552,7 +551,7 @@ void HELPER(stzgm_tags)(CPUARMState *env, uint64_t ptr, uint64_t val) ptr &= -dcz_bytes; mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, dcz_bytes, - MMU_DATA_STORE, tag_bytes, ra); + MMU_DATA_STORE, ra); if (mem) { int tag_pair = (val & 0xf) * 0x11; memset(mem, tag_pair, tag_bytes); @@ -597,8 +596,8 @@ static void mte_async_check_fail(CPUARMState *env, uint64_t dirty_ptr, } /* Record a tag check failure. */ -static void mte_check_fail(CPUARMState *env, uint32_t desc, - uint64_t dirty_ptr, uintptr_t ra) +void mte_check_fail(CPUARMState *env, uint32_t desc, + uint64_t dirty_ptr, uintptr_t ra) { int mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); ARMMMUIdx arm_mmu_idx = core_to_aa64_mmu_idx(mmu_idx); @@ -714,6 +713,55 @@ static int checkN(uint8_t *mem, int odd, int cmp, int count) return n; } +/** + * checkNrev: + * @tag: tag memory to test + * @odd: true to begin testing at tags at odd nibble + * @cmp: the tag to compare against + * @count: number of tags to test + * + * Return the number of successful tests. + * Thus a return value < @count indicates a failure. + * + * This is like checkN, but it runs backwards, checking the + * tags starting with @tag and then the tags preceding it. + * This is needed by the backwards-memory-copying operations. + */ +static int checkNrev(uint8_t *mem, int odd, int cmp, int count) +{ + int n = 0, diff; + + /* Replicate the test tag and compare. */ + cmp *= 0x11; + diff = *mem-- ^ cmp; + + if (!odd) { + goto start_even; + } + + while (1) { + /* Test odd tag. */ + if (unlikely((diff) & 0xf0)) { + break; + } + if (++n == count) { + break; + } + + start_even: + /* Test even tag. */ + if (unlikely((diff) & 0x0f)) { + break; + } + if (++n == count) { + break; + } + + diff = *mem-- ^ cmp; + } + return n; +} + /** * mte_probe_int() - helper for mte_probe and mte_check * @env: CPU environment @@ -732,8 +780,7 @@ static int mte_probe_int(CPUARMState *env, uint32_t desc, uint64_t ptr, int mmu_idx, ptr_tag, bit55; uint64_t ptr_last, prev_page, next_page; uint64_t tag_first, tag_last; - uint64_t tag_byte_first, tag_byte_last; - uint32_t sizem1, tag_count, tag_size, n, c; + uint32_t sizem1, tag_count, n, c; uint8_t *mem1, *mem2; MMUAccessType type; @@ -763,19 +810,14 @@ static int mte_probe_int(CPUARMState *env, uint32_t desc, uint64_t ptr, tag_last = QEMU_ALIGN_DOWN(ptr_last, TAG_GRANULE); tag_count = ((tag_last - tag_first) / TAG_GRANULE) + 1; - /* Round the bounds to twice the tag granule, and compute the bytes. */ - tag_byte_first = QEMU_ALIGN_DOWN(ptr, 2 * TAG_GRANULE); - tag_byte_last = QEMU_ALIGN_DOWN(ptr_last, 2 * TAG_GRANULE); - /* Locate the page boundaries. */ prev_page = ptr & TARGET_PAGE_MASK; next_page = prev_page + TARGET_PAGE_SIZE; if (likely(tag_last - prev_page < TARGET_PAGE_SIZE)) { /* Memory access stays on one page. */ - tag_size = ((tag_byte_last - tag_byte_first) / (2 * TAG_GRANULE)) + 1; mem1 = allocation_tag_mem(env, mmu_idx, ptr, type, sizem1 + 1, - MMU_DATA_LOAD, tag_size, ra); + MMU_DATA_LOAD, ra); if (!mem1) { return 1; } @@ -783,14 +825,12 @@ static int mte_probe_int(CPUARMState *env, uint32_t desc, uint64_t ptr, n = checkN(mem1, ptr & TAG_GRANULE, ptr_tag, tag_count); } else { /* Memory access crosses to next page. */ - tag_size = (next_page - tag_byte_first) / (2 * TAG_GRANULE); mem1 = allocation_tag_mem(env, mmu_idx, ptr, type, next_page - ptr, - MMU_DATA_LOAD, tag_size, ra); + MMU_DATA_LOAD, ra); - tag_size = ((tag_byte_last - next_page) / (2 * TAG_GRANULE)) + 1; mem2 = allocation_tag_mem(env, mmu_idx, next_page, type, ptr_last - next_page + 1, - MMU_DATA_LOAD, tag_size, ra); + MMU_DATA_LOAD, ra); /* * Perform all of the comparisons. @@ -918,7 +958,7 @@ uint64_t HELPER(mte_check_zva)(CPUARMState *env, uint32_t desc, uint64_t ptr) mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); (void) probe_write(env, ptr, 1, mmu_idx, ra); mem = allocation_tag_mem(env, mmu_idx, align_ptr, MMU_DATA_STORE, - dcz_bytes, MMU_DATA_LOAD, tag_bytes, ra); + dcz_bytes, MMU_DATA_LOAD, ra); if (!mem) { goto done; } @@ -979,3 +1019,151 @@ uint64_t HELPER(mte_check_zva)(CPUARMState *env, uint32_t desc, uint64_t ptr) done: return useronly_clean_ptr(ptr); } + +uint64_t mte_mops_probe(CPUARMState *env, uint64_t ptr, uint64_t size, + uint32_t desc) +{ + int mmu_idx, tag_count; + uint64_t ptr_tag, tag_first, tag_last; + void *mem; + bool w = FIELD_EX32(desc, MTEDESC, WRITE); + uint32_t n; + + mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); + /* True probe; this will never fault */ + mem = allocation_tag_mem_probe(env, mmu_idx, ptr, + w ? MMU_DATA_STORE : MMU_DATA_LOAD, + size, MMU_DATA_LOAD, true, 0); + if (!mem) { + return size; + } + + /* + * TODO: checkN() is not designed for checks of the size we expect + * for FEAT_MOPS operations, so we should implement this differently. + * Maybe we should do something like + * if (region start and size are aligned nicely) { + * do direct loads of 64 tag bits at a time; + * } else { + * call checkN() + * } + */ + /* Round the bounds to the tag granule, and compute the number of tags. */ + ptr_tag = allocation_tag_from_addr(ptr); + tag_first = QEMU_ALIGN_DOWN(ptr, TAG_GRANULE); + tag_last = QEMU_ALIGN_DOWN(ptr + size - 1, TAG_GRANULE); + tag_count = ((tag_last - tag_first) / TAG_GRANULE) + 1; + n = checkN(mem, ptr & TAG_GRANULE, ptr_tag, tag_count); + if (likely(n == tag_count)) { + return size; + } + + /* + * Failure; for the first granule, it's at @ptr. Otherwise + * it's at the first byte of the nth granule. Calculate how + * many bytes we can access without hitting that failure. + */ + if (n == 0) { + return 0; + } else { + return n * TAG_GRANULE - (ptr - tag_first); + } +} + +uint64_t mte_mops_probe_rev(CPUARMState *env, uint64_t ptr, uint64_t size, + uint32_t desc) +{ + int mmu_idx, tag_count; + uint64_t ptr_tag, tag_first, tag_last; + void *mem; + bool w = FIELD_EX32(desc, MTEDESC, WRITE); + uint32_t n; + + mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); + /* + * True probe; this will never fault. Note that our caller passes + * us a pointer to the end of the region, but allocation_tag_mem_probe() + * wants a pointer to the start. Because we know we don't span a page + * boundary and that allocation_tag_mem_probe() doesn't otherwise care + * about the size, pass in a size of 1 byte. This is simpler than + * adjusting the ptr to point to the start of the region and then having + * to adjust the returned 'mem' to get the end of the tag memory. + */ + mem = allocation_tag_mem_probe(env, mmu_idx, ptr, + w ? MMU_DATA_STORE : MMU_DATA_LOAD, + 1, MMU_DATA_LOAD, true, 0); + if (!mem) { + return size; + } + + /* + * TODO: checkNrev() is not designed for checks of the size we expect + * for FEAT_MOPS operations, so we should implement this differently. + * Maybe we should do something like + * if (region start and size are aligned nicely) { + * do direct loads of 64 tag bits at a time; + * } else { + * call checkN() + * } + */ + /* Round the bounds to the tag granule, and compute the number of tags. */ + ptr_tag = allocation_tag_from_addr(ptr); + tag_first = QEMU_ALIGN_DOWN(ptr - (size - 1), TAG_GRANULE); + tag_last = QEMU_ALIGN_DOWN(ptr, TAG_GRANULE); + tag_count = ((tag_last - tag_first) / TAG_GRANULE) + 1; + n = checkNrev(mem, ptr & TAG_GRANULE, ptr_tag, tag_count); + if (likely(n == tag_count)) { + return size; + } + + /* + * Failure; for the first granule, it's at @ptr. Otherwise + * it's at the last byte of the nth granule. Calculate how + * many bytes we can access without hitting that failure. + */ + if (n == 0) { + return 0; + } else { + return (n - 1) * TAG_GRANULE + ((ptr + 1) - tag_last); + } +} + +void mte_mops_set_tags(CPUARMState *env, uint64_t ptr, uint64_t size, + uint32_t desc) +{ + int mmu_idx, tag_count; + uint64_t ptr_tag; + void *mem; + + if (!desc) { + /* Tags not actually enabled */ + return; + } + + mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); + /* True probe: this will never fault */ + mem = allocation_tag_mem_probe(env, mmu_idx, ptr, MMU_DATA_STORE, size, + MMU_DATA_STORE, true, 0); + if (!mem) { + return; + } + + /* + * We know that ptr and size are both TAG_GRANULE aligned; store + * the tag from the pointer value into the tag memory. + */ + ptr_tag = allocation_tag_from_addr(ptr); + tag_count = size / TAG_GRANULE; + if (ptr & TAG_GRANULE) { + /* Not 2*TAG_GRANULE-aligned: store tag to first nibble */ + store_tag1_parallel(TAG_GRANULE, mem, ptr_tag); + mem++; + tag_count--; + } + memset(mem, ptr_tag | (ptr_tag << 4), tag_count / 2); + if (tag_count & 1) { + /* Final trailing unaligned nibble */ + mem += tag_count / 2; + store_tag1_parallel(0, mem, ptr_tag); + } +} diff --git a/target/arm/tcg/mte_helper.h b/target/arm/tcg/mte_helper.h new file mode 100644 index 00000000000..1f471fb69b1 --- /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/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 403b345ea3b..506d1c34757 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -22,10 +22,10 @@ #include "internals.h" #include "vec_internal.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" -#include "exec/exec-all.h" +#include "accel/tcg/cpu-ldst.h" #include "tcg/tcg.h" #include "fpu/softfloat.h" +#include "crypto/clmul.h" static uint16_t mve_eci_mask(CPUARMState *env) { @@ -924,8 +924,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 +984,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 +1114,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 +1249,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 +1291,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 +2453,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 +2485,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; } @@ -2820,8 +2813,7 @@ DO_VMAXMINA(vminaw, 4, int32_t, uint32_t, DO_MIN) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ - &env->vfp.standard_fp_status; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -2894,8 +2886,7 @@ DO_2OP_FP_ALL(vminnma, minnuma) r[e] = 0; \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ - &env->vfp.standard_fp_status; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(tm & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -2932,8 +2923,7 @@ DO_VCADD_FP(vfcadd270s, 4, float32, float32_add, float32_sub) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ - &env->vfp.standard_fp_status; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -2970,8 +2960,7 @@ DO_VFMA(vfmss, 4, float32, true) if ((mask & MAKE_64BIT_MASK(0, ESIZE * 2)) == 0) { \ continue; \ } \ - fpst0 = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ - &env->vfp.standard_fp_status; \ + fpst0 = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ fpst1 = fpst0; \ if (!(mask & 1)) { \ scratch_fpst = *fpst0; \ @@ -3055,8 +3044,7 @@ DO_VCMLA(vcmla270s, 4, float32, 3, DO_VCMLAS) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ - &env->vfp.standard_fp_status; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -3090,8 +3078,7 @@ DO_2OP_FP_SCALAR_ALL(vfmul_scalar, mul) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ - &env->vfp.standard_fp_status; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -3122,9 +3109,8 @@ DO_2OP_FP_ACC_SCALAR(vfmas_scalars, 4, float32, DO_VFMAS_SCALARS) unsigned e; \ TYPE *m = vm; \ TYPE ra = (TYPE)ra_in; \ - float_status *fpst = (ESIZE == 2) ? \ - &env->vfp.standard_fp_status_f16 : \ - &env->vfp.standard_fp_status; \ + float_status *fpst = \ + &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ if (mask & 1) { \ TYPE v = m[H##ESIZE(e)]; \ @@ -3174,8 +3160,7 @@ DO_FP_VMAXMINV(vminnmavs, 4, float32, true, float32_minnum) if ((mask & emask) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ - &env->vfp.standard_fp_status; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & (1 << (e * ESIZE)))) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -3208,8 +3193,7 @@ DO_FP_VMAXMINV(vminnmavs, 4, float32, true, float32_minnum) if ((mask & emask) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ - &env->vfp.standard_fp_status; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & (1 << (e * ESIZE)))) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -3273,8 +3257,7 @@ DO_VCMP_FP_BOTH(vfcmples, vfcmple_scalars, 4, float32, !DO_GT32) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ - &env->vfp.standard_fp_status; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ @@ -3306,9 +3289,8 @@ DO_VCVT_FIXED(vcvt_fu, 4, uint32_t, helper_vfp_touls_round_to_zero) unsigned e; \ float_status *fpst; \ float_status scratch_fpst; \ - float_status *base_fpst = (ESIZE == 2) ? \ - &env->vfp.standard_fp_status_f16 : \ - &env->vfp.standard_fp_status; \ + float_status *base_fpst = \ + &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ uint32_t prev_rmode = get_float_rounding_mode(base_fpst); \ set_float_rounding_mode(rmode, base_fpst); \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \ @@ -3349,11 +3331,11 @@ 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; - float_status *base_fpst = &env->vfp.standard_fp_status; + float_status *base_fpst = &env->vfp.fp_status[FPST_STD]; bool old_fz = get_flush_to_zero(base_fpst); set_flush_to_zero(false, base_fpst); for (e = 0; e < 16 / 4; e++, mask >>= 4) { @@ -3379,11 +3361,11 @@ 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; - float_status *base_fpst = &env->vfp.standard_fp_status; + float_status *base_fpst = &env->vfp.fp_status[FPST_STD]; bool old_fiz = get_flush_inputs_to_zero(base_fpst); set_flush_inputs_to_zero(false, base_fpst); for (e = 0; e < 16 / 4; e++, mask >>= 4) { @@ -3433,8 +3415,7 @@ void HELPER(mve_vcvtt_hs)(CPUARMState *env, void *vd, void *vm) if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \ continue; \ } \ - fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \ - &env->vfp.standard_fp_status; \ + fpst = &env->vfp.fp_status[ESIZE == 2 ? FPST_STD_F16 : FPST_STD]; \ if (!(mask & 1)) { \ /* We need the result but without updating flags */ \ scratch_fpst = *fpst; \ diff --git a/target/arm/tcg/neon-dp.decode b/target/arm/tcg/neon-dp.decode index fd3a01bfa0b..e883c6ab585 100644 --- a/target/arm/tcg/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/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index bc6c4a54e9d..2cc8241f1e4 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -6,13 +6,16 @@ * * This code is licensed under the GNU GPL v2. */ -#include "qemu/osdep.h" +#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 HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" + #define SIGNBIT (uint32_t)0x80000000 #define SIGNBIT64 ((uint64_t)1 << 63) @@ -117,6 +120,40 @@ NEON_VOP_BODY(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, CPUARMState *env, 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_VOP2i_ENV(name, vtype) \ +void HELPER(name)(void *vd, void *vn, CPUARMState *env, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + int imm = simd_data(desc); \ + vtype *d = vd, *n = vn; \ + 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. */ @@ -155,414 +192,6 @@ uint32_t HELPER(glue(neon_,name))(uint32_t arg) \ 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) @@ -590,11 +219,23 @@ NEON_VOP(shl_s16, neon_s16, 2) #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) @@ -610,11 +251,23 @@ uint64_t HELPER(neon_rshl_s64)(uint64_t val, uint64_t shift) #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) @@ -630,11 +283,27 @@ uint64_t HELPER(neon_rshl_u64)(uint64_t val, uint64_t shift) #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) @@ -650,11 +319,27 @@ uint64_t HELPER(neon_qshl_u64)(CPUARMState *env, uint64_t val, uint64_t shift) #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) @@ -670,11 +355,13 @@ uint64_t HELPER(neon_qshl_s64)(CPUARMState *env, uint64_t val, uint64_t shift) #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) @@ -687,14 +374,36 @@ 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) @@ -710,11 +419,23 @@ uint64_t HELPER(neon_qrshl_u64)(CPUARMState *env, uint64_t val, uint64_t shift) #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) @@ -745,11 +466,6 @@ uint32_t HELPER(neon_add_u16)(uint32_t a, uint32_t b) 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) @@ -809,27 +525,6 @@ uint32_t HELPER(neon_cls_s32)(uint32_t x) 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) { \ @@ -882,13 +577,15 @@ NEON_VOP_ENV(qrdmulh_s32, neon_s32, 1) #undef NEON_FN #undef NEON_QDMULH32 -uint32_t HELPER(neon_narrow_u8)(uint64_t x) +/* 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); } -uint32_t HELPER(neon_narrow_u16)(uint64_t x) +/* Only the low 32-bits of output are significant. */ +uint64_t HELPER(neon_narrow_u16)(uint64_t x) { return (x & 0xffffu) | ((x >> 16) & 0xffff0000u); } @@ -919,7 +616,8 @@ uint32_t HELPER(neon_narrow_round_high_u16)(uint64_t x) return ((x >> 16) & 0xffff) | ((x >> 32) & 0xffff0000); } -uint32_t HELPER(neon_unarrow_sat8)(CPUARMState *env, uint64_t x) +/* 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; @@ -946,7 +644,8 @@ uint32_t HELPER(neon_unarrow_sat8)(CPUARMState *env, uint64_t x) return res; } -uint32_t HELPER(neon_narrow_sat_u8)(CPUARMState *env, uint64_t x) +/* 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; @@ -969,7 +668,8 @@ uint32_t HELPER(neon_narrow_sat_u8)(CPUARMState *env, uint64_t x) return res; } -uint32_t HELPER(neon_narrow_sat_s8)(CPUARMState *env, uint64_t x) +/* 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; @@ -992,7 +692,8 @@ uint32_t HELPER(neon_narrow_sat_s8)(CPUARMState *env, uint64_t x) return res; } -uint32_t HELPER(neon_unarrow_sat16)(CPUARMState *env, uint64_t x) +/* 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; @@ -1012,10 +713,11 @@ uint32_t HELPER(neon_unarrow_sat16)(CPUARMState *env, uint64_t x) high = 0xffff; SET_QC(); } - return low | (high << 16); + return deposit32(low, 16, 16, high); } -uint32_t HELPER(neon_narrow_sat_u16)(CPUARMState *env, uint64_t x) +/* 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; @@ -1029,10 +731,11 @@ uint32_t HELPER(neon_narrow_sat_u16)(CPUARMState *env, uint64_t x) high = 0xffff; SET_QC(); } - return low | (high << 16); + return deposit32(low, 16, 16, high); } -uint32_t HELPER(neon_narrow_sat_s16)(CPUARMState *env, uint64_t x) +/* 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; @@ -1046,10 +749,11 @@ uint32_t HELPER(neon_narrow_sat_s16)(CPUARMState *env, uint64_t x) high = (high >> 31) ^ 0x7fff; SET_QC(); } - return (uint16_t)low | (high << 16); + return deposit32(low, 16, 16, high); } -uint32_t HELPER(neon_unarrow_sat32)(CPUARMState *env, uint64_t x) +/* 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(); @@ -1062,7 +766,8 @@ uint32_t HELPER(neon_unarrow_sat32)(CPUARMState *env, uint64_t x) return x; } -uint32_t HELPER(neon_narrow_sat_u32)(CPUARMState *env, uint64_t 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(); @@ -1071,13 +776,14 @@ uint32_t HELPER(neon_narrow_sat_u32)(CPUARMState *env, uint64_t x) return x; } -uint32_t HELPER(neon_narrow_sat_s32)(CPUARMState *env, uint64_t 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 ((int64_t)x >> 63) ^ 0x7fffffff; + return (uint32_t)((int64_t)x >> 63) ^ 0x7fffffff; } - return x; + return (uint32_t)x; } uint64_t HELPER(neon_widen_u8)(uint32_t x) @@ -1120,62 +826,47 @@ uint64_t HELPER(neon_widen_s16)(uint32_t x) 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; +/* 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; - 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); + return res; } -uint64_t HELPER(neon_paddl_u32)(uint64_t a, uint64_t b) +uint64_t HELPER(neon_addlp_s16)(uint64_t a) { - uint32_t low = a + (a >> 32); - uint32_t high = b + (b >> 32); - return low + ((uint64_t)high << 32); -} + int32_t reslo, reshi; -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; -} + 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); -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; + return (uint32_t)reslo | (((uint64_t)reshi) << 32); } uint64_t HELPER(neon_addl_saturate_s32)(CPUARMState *env, uint64_t a, uint64_t b) @@ -1489,51 +1180,44 @@ uint64_t HELPER(neon_qneg_s64)(CPUARMState *env, uint64_t x) * 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) +uint32_t HELPER(neon_ceq_f32)(uint32_t a, uint32_t b, float_status *fpst) { - 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) +uint32_t HELPER(neon_cge_f32)(uint32_t a, uint32_t b, float_status *fpst) { - 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) +uint32_t HELPER(neon_cgt_f32)(uint32_t a, uint32_t b, float_status *fpst) { - 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) +uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b, float_status *fpst) { - 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) +uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b, float_status *fpst) { - 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) +uint64_t HELPER(neon_acge_f64)(uint64_t a, uint64_t b, float_status *fpst) { - 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) +uint64_t HELPER(neon_acgt_f64)(uint64_t a, uint64_t b, float_status *fpst) { - float_status *fpst = fpstp; float64 f0 = float64_abs(make_float64(a)); float64 f1 = float64_abs(make_float64(b)); return -float64_lt(f1, f0, fpst); diff --git a/target/arm/op_addsub.h b/target/arm/tcg/op_addsub.c.inc similarity index 100% rename from target/arm/op_addsub.h rename to target/arm/tcg/op_addsub.c.inc diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index 403f8b09d36..575e566280b 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -20,9 +20,11 @@ #include "qemu/main-loop.h" #include "cpu.h" #include "exec/helper-proto.h" +#include "exec/target_page.h" #include "internals.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "cpu-features.h" +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "cpregs.h" #define SIGNBIT (uint32_t)0x80000000 @@ -120,6 +122,61 @@ void HELPER(v8m_stackcheck)(CPUARMState *env, uint32_t newvalue) } } +/* Sign/zero extend */ +uint32_t HELPER(sxtb16)(uint32_t x) +{ + uint32_t res; + res = (uint16_t)(int8_t)x; + res |= (uint32_t)(int8_t)(x >> 16) << 16; + return res; +} + +static void handle_possible_div0_trap(CPUARMState *env, uintptr_t ra) +{ + /* + * Take a division-by-zero exception if necessary; otherwise return + * to get the usual non-trapping division behaviour (result of 0) + */ + if (arm_feature(env, ARM_FEATURE_M) + && (env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_DIV_0_TRP_MASK)) { + raise_exception_ra(env, EXCP_DIVBYZERO, 0, 1, ra); + } +} + +uint32_t HELPER(uxtb16)(uint32_t x) +{ + uint32_t res; + res = (uint16_t)(uint8_t)x; + res |= (uint32_t)(uint8_t)(x >> 16) << 16; + return res; +} + +int32_t HELPER(sdiv)(CPUARMState *env, int32_t num, int32_t den) +{ + if (den == 0) { + handle_possible_div0_trap(env, GETPC()); + return 0; + } + if (num == INT_MIN && den == -1) { + return INT_MIN; + } + return num / den; +} + +uint32_t HELPER(udiv)(CPUARMState *env, uint32_t num, uint32_t den) +{ + if (den == 0) { + handle_possible_div0_trap(env, GETPC()); + return 0; + } + return num / den; +} + +uint32_t HELPER(rbit)(uint32_t x) +{ + return revbit32(x); +} + uint32_t HELPER(add_setq)(CPUARMState *env, uint32_t a, uint32_t b) { uint32_t res = a + b; @@ -257,15 +314,19 @@ void HELPER(check_bxj_trap)(CPUARMState *env, uint32_t rm) } #ifndef CONFIG_USER_ONLY -/* Function checks whether WFx (WFI/WFE) instructions are set up to be trapped. +/* + * 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. + * For a trap, *excp is updated with the EXCP_* trap type to use. */ -static inline int check_wfx_trap(CPUARMState *env, bool is_wfe) +static inline int check_wfx_trap(CPUARMState *env, bool is_wfe, uint32_t *excp) { int cur_el = arm_current_el(env); uint64_t mask; + *excp = EXCP_UDEF; + if (arm_feature(env, ARM_FEATURE_M)) { /* M profile cores can never trap WFI/WFE. */ return 0; @@ -275,18 +336,9 @@ static inline int check_wfx_trap(CPUARMState *env, bool is_wfe) * 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; + if (!(arm_sctlr(env, cur_el) & mask)) { + return exception_target_el(env); } } @@ -302,9 +354,12 @@ static inline int check_wfx_trap(CPUARMState *env, bool is_wfe) } /* We are not trapping to EL1 or EL2; trap to EL3 if SCR_EL3 requires it */ - if (cur_el < 3) { + if (arm_feature(env, ARM_FEATURE_V8) && !arm_is_el3_or_mon(env)) { mask = (is_wfe) ? SCR_TWE : SCR_TWI; if (env->cp15.scr_el3 & mask) { + if (!arm_el_is_aa64(env, 3)) { + *excp = EXCP_MON_TRAP; + } return 3; } } @@ -327,7 +382,8 @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len) return; #else CPUState *cs = env_cpu(env); - int target_el = check_wfx_trap(env, false); + uint32_t excp; + int target_el = check_wfx_trap(env, false, &excp); if (cpu_has_work(cs)) { /* Don't bother to go into our "low power state" if @@ -343,7 +399,7 @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len) env->regs[15] -= insn_len; } - raise_exception(env, EXCP_UDEF, syn_wfx(1, 0xe, 0, insn_len == 2), + raise_exception(env, excp, syn_wfx(1, 0xe, 0, insn_len == 2), target_el); } @@ -353,6 +409,66 @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len) #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); + uint32_t excp; + int target_el = check_wfx_trap(env, false, &excp); + /* The WFIT should time out when CNTVCT_EL0 >= the specified value. */ + uint64_t cntval = gt_get_countervalue(env); + /* + * We want the value that we would get if we read CNTVCT_EL0 from + * the current exception level, so the direct_access offset, not + * the indirect_access one. Compare the pseudocode LocalTimeoutEvent(), + * which calls VirtualCounterTimer(). + */ + uint64_t offset = gt_direct_access_timer_offset(env, GTIMER_VIRT); + 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, 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 @@ -426,9 +542,9 @@ void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val) { uint32_t mask; - qemu_mutex_lock_iothread(); + bql_lock(); arm_call_pre_el_change_hook(env_archcpu(env)); - qemu_mutex_unlock_iothread(); + bql_unlock(); mask = aarch32_cpsr_valid_mask(env->features, &env_archcpu(env)->isar); cpsr_write(env, val, mask, CPSRWriteExceptionReturn); @@ -441,9 +557,9 @@ void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val) env->regs[15] &= (env->thumb ? ~1 : ~3); arm_rebuild_hflags(env); - qemu_mutex_lock_iothread(); + bql_lock(); arm_call_el_change_hook(env_archcpu(env)); - qemu_mutex_unlock_iothread(); + bql_unlock(); } /* Access to user mode registers from privileged modes. */ @@ -514,10 +630,24 @@ static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode, */ int curmode = env->uncached_cpsr & CPSR_M; - if (regno == 17) { - /* ELR_Hyp: a special case because access from tgtmode is OK */ - if (curmode != ARM_CPU_MODE_HYP && curmode != ARM_CPU_MODE_MON) { - goto undef; + if (tgtmode == ARM_CPU_MODE_HYP) { + /* + * Handle Hyp target regs first because some are special cases + * which don't want the usual "not accessible from tgtmode" check. + */ + switch (regno) { + case 16 ... 17: /* ELR_Hyp, SPSR_Hyp */ + if (curmode != ARM_CPU_MODE_HYP && curmode != ARM_CPU_MODE_MON) { + goto undef; + } + break; + case 13: + if (curmode != ARM_CPU_MODE_MON) { + goto undef; + } + break; + default: + g_assert_not_reached(); } return; } @@ -548,13 +678,6 @@ static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode, } } - if (tgtmode == ARM_CPU_MODE_HYP) { - /* SPSR_Hyp, r13_hyp: accessible from Monitor mode only */ - if (curmode != ARM_CPU_MODE_MON) { - goto undef; - } - } - return; undef: @@ -569,7 +692,12 @@ void HELPER(msr_banked)(CPUARMState *env, uint32_t value, uint32_t tgtmode, switch (regno) { case 16: /* SPSRs */ - env->banked_spsr[bank_number(tgtmode)] = value; + if (tgtmode == (env->uncached_cpsr & CPSR_M)) { + /* Only happens for SPSR_Hyp access in Hyp mode */ + env->spsr = value; + } else { + env->banked_spsr[bank_number(tgtmode)] = value; + } break; case 17: /* ELR_Hyp */ env->elr_el[2] = value; @@ -603,7 +731,12 @@ uint32_t HELPER(mrs_banked)(CPUARMState *env, uint32_t tgtmode, uint32_t regno) switch (regno) { case 16: /* SPSRs */ - return env->banked_spsr[bank_number(tgtmode)]; + if (tgtmode == (env->uncached_cpsr & CPSR_M)) { + /* Only happens for SPSR_Hyp access in Hyp mode */ + return env->spsr; + } else { + return env->banked_spsr[bank_number(tgtmode)]; + } case 17: /* ELR_Hyp */ return env->elr_el[2]; case 13: @@ -631,12 +764,13 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, const ARMCPRegInfo *ri = get_arm_cp_reginfo(cpu->cp_regs, key); CPAccessResult res = CP_ACCESS_OK; int target_el; + uint32_t excp; 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; + res = CP_ACCESS_UNDEFINED; goto fail; } @@ -653,7 +787,7 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, * 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) && + if (res != CP_ACCESS_OK && ((res & CP_ACCESS_EL_MASK) < 2) && arm_current_el(env) == 0) { goto fail; } @@ -690,6 +824,7 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, 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 nxs = FIELD_EX32(ri->fgt, FGT, NXS); bool trapbit; if (ri->fgt & FGT_EXEC) { @@ -703,7 +838,15 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, trapword = env->cp15.fgt_write[idx]; } - trapbit = extract64(trapword, bitpos, 1); + if (nxs && (arm_hcrx_el2_eff(env) & HCRX_FGTNXS)) { + /* + * If HCRX_EL2.FGTnXS is 1 then the fine-grained trap for + * TLBI maintenance insns does *not* apply to the nXS variant. + */ + trapbit = 0; + } else { + trapbit = extract64(trapword, bitpos, 1); + } if (trapbit != rev) { res = CP_ACCESS_TRAP_EL2; goto fail; @@ -715,12 +858,25 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, } fail: - switch (res & ~CP_ACCESS_EL_MASK) { - case CP_ACCESS_TRAP: + excp = EXCP_UDEF; + switch (res) { + /* CP_ACCESS_TRAP* traps are always direct to a specified EL */ + case CP_ACCESS_TRAP_EL3: + /* + * If EL3 is AArch32 then there's no syndrome register; the cases + * where we would raise a SystemAccessTrap to AArch64 EL3 all become + * raising a Monitor trap exception. (Because there's no visible + * syndrome it doesn't matter what we pass to raise_exception().) + */ + if (!arm_el_is_aa64(env, 3)) { + excp = EXCP_MON_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); + case CP_ACCESS_TRAP_EL2: + case CP_ACCESS_TRAP_EL1: + break; + case CP_ACCESS_UNDEFINED: + /* CP_ACCESS_UNDEFINED is never direct to a specified EL */ if (cpu_isar_feature(aa64_ids, cpu) && isread && arm_cpreg_in_idspace(ri)) { /* @@ -740,6 +896,9 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, case 0: target_el = exception_target_el(env); break; + case 1: + assert(arm_current_el(env) < 2); + break; case 2: assert(arm_current_el(env) != 3); assert(arm_is_el2_enabled(env)); @@ -748,11 +907,10 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, 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); + raise_exception(env, excp, syndrome, target_el); } const void *HELPER(lookup_cp_reg)(CPUARMState *env, uint32_t key) @@ -785,7 +943,19 @@ void HELPER(tidcp_el0)(CPUARMState *env, uint32_t syndrome) { /* See arm_sctlr(), but we also need the sctlr el. */ ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, 0); - int target_el = mmu_idx == ARMMMUIdx_E20_0 ? 2 : 1; + 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 @@ -802,9 +972,9 @@ void HELPER(set_cp_reg)(CPUARMState *env, const void *rip, uint32_t value) const ARMCPRegInfo *ri = rip; if (ri->type & ARM_CP_IO) { - qemu_mutex_lock_iothread(); + bql_lock(); ri->writefn(env, ri, value); - qemu_mutex_unlock_iothread(); + bql_unlock(); } else { ri->writefn(env, ri, value); } @@ -816,9 +986,9 @@ uint32_t HELPER(get_cp_reg)(CPUARMState *env, const void *rip) uint32_t res; if (ri->type & ARM_CP_IO) { - qemu_mutex_lock_iothread(); + bql_lock(); res = ri->readfn(env, ri); - qemu_mutex_unlock_iothread(); + bql_unlock(); } else { res = ri->readfn(env, ri); } @@ -831,9 +1001,9 @@ void HELPER(set_cp_reg64)(CPUARMState *env, const void *rip, uint64_t value) const ARMCPRegInfo *ri = rip; if (ri->type & ARM_CP_IO) { - qemu_mutex_lock_iothread(); + bql_lock(); ri->writefn(env, ri, value); - qemu_mutex_unlock_iothread(); + bql_unlock(); } else { ri->writefn(env, ri, value); } @@ -845,9 +1015,9 @@ uint64_t HELPER(get_cp_reg64)(CPUARMState *env, const void *rip) uint64_t res; if (ri->type & ARM_CP_IO) { - qemu_mutex_lock_iothread(); + bql_lock(); res = ri->readfn(env, ri); - qemu_mutex_unlock_iothread(); + bql_unlock(); } else { res = ri->readfn(env, ri); } @@ -929,7 +1099,14 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) * * Conduit SMC, valid call Trap to EL2 PSCI Call * Conduit SMC, inval call Trap to EL2 Undef insn - * Conduit not SMC Undef insn Undef insn + * Conduit not SMC Undef or trap[1] Undef insn + * + * [1] In this case: + * - if HCR_EL2.NV == 1 we must trap to EL2 + * - if HCR_EL2.NV == 0 then newer architecture revisions permit + * AArch64 (but not AArch32) to trap to EL2 as an IMPDEF choice + * - otherwise we must UNDEF + * We take the IMPDEF choice to always UNDEF if HCR_EL2.NV == 0. */ /* On ARMv8 with EL3 AArch64, SMD applies to both S and NS state. @@ -943,9 +1120,12 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) : smd_flag && !secure; if (!arm_feature(env, ARM_FEATURE_EL3) && + !(arm_hcr_el2_eff(env) & HCR_NV) && cpu->psci_conduit != QEMU_PSCI_CONDUIT_SMC) { - /* If we have no EL3 then SMC always UNDEFs and can't be - * trapped to EL2. PSCI-via-SMC is a sort of ersatz EL3 + /* + * If we have no EL3 then traditionally SMC always UNDEFs and can't be + * trapped to EL2. For nested virtualization, SMC can be trapped to + * the outer hypervisor. PSCI-via-SMC is a sort of ersatz EL3 * firmware within QEMU, and we want an EL2 guest to be able * to forbid its EL1 from making PSCI calls into QEMU's * "firmware" via HCR.TSC, so for these purposes treat @@ -1042,7 +1222,7 @@ uint32_t HELPER(ror_cc)(CPUARMState *env, uint32_t x, uint32_t i) } } -void HELPER(probe_access)(CPUARMState *env, target_ulong ptr, +void HELPER(probe_access)(CPUARMState *env, vaddr ptr, uint32_t access_type, uint32_t mmu_idx, uint32_t size) { diff --git a/target/arm/tcg/pauth_helper.c b/target/arm/tcg/pauth_helper.c index 4da2962ad5b..c591c3052c3 100644 --- a/target/arm/tcg/pauth_helper.c +++ b/target/arm/tcg/pauth_helper.c @@ -20,8 +20,8 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "cpu-features.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/helper-proto.h" #include "tcg/tcg-gvec-desc.h" #include "qemu/xxhash.h" diff --git a/target/arm/tcg/psci.c b/target/arm/tcg/psci.c index 6c1239bb968..cabed43e8a8 100644 --- a/target/arm/tcg/psci.c +++ b/target/arm/tcg/psci.c @@ -21,9 +21,10 @@ #include "exec/helper-proto.h" #include "kvm-consts.h" #include "qemu/main-loop.h" -#include "sysemu/runstate.h" +#include "system/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 @@ void arm_handle_psci_call(ARMCPU *cpu) return; cpu_off: - ret = arm_set_cpu_off(cpu->mp_affinity); + ret = arm_set_cpu_off(arm_cpu_mp_affinity(cpu)); /* notreached */ /* sanity check in case something failed */ assert(ret == QEMU_ARM_POWERCTL_RET_SUCCESS); diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 296826ffe6a..de0c6e54d4b 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -22,8 +22,8 @@ #include "internals.h" #include "tcg/tcg-gvec-desc.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" -#include "exec/exec-all.h" +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/helper-retaddr.h" #include "qemu/int128.h" #include "fpu/softfloat.h" #include "vec_internal.h" @@ -517,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 { @@ -529,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. @@ -543,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 { @@ -554,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(); } } @@ -573,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; } @@ -701,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 { @@ -711,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. @@ -725,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 { @@ -734,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(); } } @@ -750,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; } @@ -888,7 +904,7 @@ void HELPER(sme_addva_d)(void *vzda, void *vzn, void *vpn, } void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, - void *vpm, void *vst, uint32_t desc) + void *vpm, float_status *fpst_in, uint32_t desc) { intptr_t row, col, oprsz = simd_maxsz(desc); uint32_t neg = simd_data(desc) << 31; @@ -900,7 +916,7 @@ void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, * update the cumulative fp exception status. It also produces * default nans. */ - fpst = *(float_status *)vst; + fpst = *fpst_in; set_default_nan_mode(true, &fpst); for (row = 0; row < oprsz; ) { @@ -916,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; @@ -930,13 +946,13 @@ void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, } void HELPER(sme_fmopa_d)(void *vza, void *vzn, void *vzm, void *vpn, - void *vpm, void *vst, uint32_t desc) + void *vpm, float_status *fpst_in, uint32_t desc) { intptr_t row, col, oprsz = simd_oprsz(desc) / 8; uint64_t neg = (uint64_t)simd_data(desc) << 63; uint64_t *za = vza, *zn = vzn, *zm = vzm; uint8_t *pn = vpn, *pm = vpm; - float_status fpst = *(float_status *)vst; + float_status fpst = *fpst_in; set_default_nan_mode(true, &fpst); @@ -976,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; @@ -1003,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 the fp status fields we use, 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[FPST_A64_F16]; + fpst_std = env->vfp.fp_status[FPST_A64]; 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); @@ -1036,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; @@ -1049,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)); + 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); + 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); + } + } 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); + } 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); + + 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); + } + } +} -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) +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); @@ -1109,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) \ @@ -1136,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; \ } @@ -1153,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); } - -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) +#define DEF_IMOPH(NAME, S) \ + void HELPER(sme_##NAME##_##S)(void *vza, void *vzn, void *vzm, \ + void *vpn, void *vpm, uint32_t desc) \ + { do_imopa_##S(vza, vzn, vzm, vpn, vpm, desc, NAME##_##S); } + +DEF_IMOPH(smopa, s) +DEF_IMOPH(umopa, s) +DEF_IMOPH(sumopa, s) +DEF_IMOPH(usmopa, s) + +DEF_IMOPH(smopa, d) +DEF_IMOPH(umopa, d) +DEF_IMOPH(sumopa, d) +DEF_IMOPH(usmopa, d) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 7c103fc9f7e..a2c363a4e17 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -20,14 +20,22 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" -#include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/helper-proto.h" +#include "exec/target_page.h" +#include "exec/tlb-flags.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" +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/helper-retaddr.h" +#include "accel/tcg/cpu-ops.h" +#include "accel/tcg/probe.h" +#ifdef CONFIG_USER_ONLY +#include "user/page-protection.h" +#endif /* Return a value for NZCV as per the ARM PredTest pseudofunction. @@ -729,7 +737,7 @@ DO_ZPZZ_PAIR_D(sve2_sminp_zpzz_d, int64_t, DO_MIN) #define DO_ZPZZ_PAIR_FP(NAME, TYPE, H, OP) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, \ - void *status, uint32_t desc) \ + float_status *status, uint32_t desc) \ { \ intptr_t i, opr_sz = simd_oprsz(desc); \ for (i = 0; i < opr_sz; ) { \ @@ -875,12 +883,28 @@ DO_ZPZ(sve_fabs_h, uint16_t, H1_2, DO_FABS) DO_ZPZ(sve_fabs_s, uint32_t, H1_4, DO_FABS) DO_ZPZ_D(sve_fabs_d, uint64_t, DO_FABS) +#define DO_AH_FABS_H(N) (float16_is_any_nan(N) ? (N) : DO_FABS(N)) +#define DO_AH_FABS_S(N) (float32_is_any_nan(N) ? (N) : DO_FABS(N)) +#define DO_AH_FABS_D(N) (float64_is_any_nan(N) ? (N) : DO_FABS(N)) + +DO_ZPZ(sve_ah_fabs_h, uint16_t, H1_2, DO_AH_FABS_H) +DO_ZPZ(sve_ah_fabs_s, uint32_t, H1_4, DO_AH_FABS_S) +DO_ZPZ_D(sve_ah_fabs_d, uint64_t, DO_AH_FABS_D) + #define DO_FNEG(N) (N ^ ~((__typeof(N))-1 >> 1)) DO_ZPZ(sve_fneg_h, uint16_t, H1_2, DO_FNEG) DO_ZPZ(sve_fneg_s, uint32_t, H1_4, DO_FNEG) DO_ZPZ_D(sve_fneg_d, uint64_t, DO_FNEG) +#define DO_AH_FNEG_H(N) (float16_is_any_nan(N) ? (N) : DO_FNEG(N)) +#define DO_AH_FNEG_S(N) (float32_is_any_nan(N) ? (N) : DO_FNEG(N)) +#define DO_AH_FNEG_D(N) (float64_is_any_nan(N) ? (N) : DO_FNEG(N)) + +DO_ZPZ(sve_ah_fneg_h, uint16_t, H1_2, DO_AH_FNEG_H) +DO_ZPZ(sve_ah_fneg_s, uint32_t, H1_4, DO_AH_FNEG_S) +DO_ZPZ_D(sve_ah_fneg_d, uint64_t, DO_AH_FNEG_D) + #define DO_NOT(N) (~N) DO_ZPZ(sve_not_zpz_b, uint8_t, H1, DO_NOT) @@ -2535,6 +2559,7 @@ void HELPER(sve_fexpa_d)(void *vd, void *vn, uint32_t desc) void HELPER(sve_ftssel_h)(void *vd, void *vn, void *vm, uint32_t desc) { intptr_t i, opr_sz = simd_oprsz(desc) / 2; + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT, 1); uint16_t *d = vd, *n = vn, *m = vm; for (i = 0; i < opr_sz; i += 1) { uint16_t nn = n[i]; @@ -2542,13 +2567,17 @@ void HELPER(sve_ftssel_h)(void *vd, void *vn, void *vm, uint32_t desc) if (mm & 1) { nn = float16_one; } - d[i] = nn ^ (mm & 2) << 14; + if (mm & 2) { + nn = float16_maybe_ah_chs(nn, fpcr_ah); + } + d[i] = nn; } } void HELPER(sve_ftssel_s)(void *vd, void *vn, void *vm, uint32_t desc) { intptr_t i, opr_sz = simd_oprsz(desc) / 4; + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT, 1); uint32_t *d = vd, *n = vn, *m = vm; for (i = 0; i < opr_sz; i += 1) { uint32_t nn = n[i]; @@ -2556,13 +2585,17 @@ void HELPER(sve_ftssel_s)(void *vd, void *vn, void *vm, uint32_t desc) if (mm & 1) { nn = float32_one; } - d[i] = nn ^ (mm & 2) << 30; + if (mm & 2) { + nn = float32_maybe_ah_chs(nn, fpcr_ah); + } + d[i] = nn; } } void HELPER(sve_ftssel_d)(void *vd, void *vn, void *vm, uint32_t desc) { intptr_t i, opr_sz = simd_oprsz(desc) / 8; + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT, 1); uint64_t *d = vd, *n = vn, *m = vm; for (i = 0; i < opr_sz; i += 1) { uint64_t nn = n[i]; @@ -2570,7 +2603,10 @@ void HELPER(sve_ftssel_d)(void *vd, void *vn, void *vm, uint32_t desc) if (mm & 1) { nn = float64_one; } - d[i] = nn ^ (mm & 2) << 62; + if (mm & 2) { + nn = float64_maybe_ah_chs(nn, fpcr_ah); + } + d[i] = nn; } } @@ -4186,10 +4222,10 @@ static TYPE NAME##_reduce(TYPE *data, float_status *status, uintptr_t n) \ uintptr_t half = n / 2; \ TYPE lo = NAME##_reduce(data, status, half); \ TYPE hi = NAME##_reduce(data + half, status, half); \ - return TYPE##_##FUNC(lo, hi, status); \ + return FUNC(lo, hi, status); \ } \ } \ -uint64_t HELPER(NAME)(void *vn, void *vg, void *vs, uint32_t desc) \ +uint64_t HELPER(NAME)(void *vn, void *vg, float_status *s, uint32_t desc) \ { \ uintptr_t i, oprsz = simd_oprsz(desc), maxsz = simd_data(desc); \ TYPE data[sizeof(ARMVectorReg) / sizeof(TYPE)]; \ @@ -4204,34 +4240,45 @@ uint64_t HELPER(NAME)(void *vn, void *vg, void *vs, uint32_t desc) \ for (; i < maxsz; i += sizeof(TYPE)) { \ *(TYPE *)((void *)data + i) = IDENT; \ } \ - return NAME##_reduce(data, vs, maxsz / sizeof(TYPE)); \ + return NAME##_reduce(data, s, maxsz / sizeof(TYPE)); \ } -DO_REDUCE(sve_faddv_h, float16, H1_2, add, float16_zero) -DO_REDUCE(sve_faddv_s, float32, H1_4, add, float32_zero) -DO_REDUCE(sve_faddv_d, float64, H1_8, add, float64_zero) +DO_REDUCE(sve_faddv_h, float16, H1_2, float16_add, float16_zero) +DO_REDUCE(sve_faddv_s, float32, H1_4, float32_add, float32_zero) +DO_REDUCE(sve_faddv_d, float64, H1_8, float64_add, float64_zero) /* Identity is floatN_default_nan, without the function call. */ -DO_REDUCE(sve_fminnmv_h, float16, H1_2, minnum, 0x7E00) -DO_REDUCE(sve_fminnmv_s, float32, H1_4, minnum, 0x7FC00000) -DO_REDUCE(sve_fminnmv_d, float64, H1_8, minnum, 0x7FF8000000000000ULL) +DO_REDUCE(sve_fminnmv_h, float16, H1_2, float16_minnum, 0x7E00) +DO_REDUCE(sve_fminnmv_s, float32, H1_4, float32_minnum, 0x7FC00000) +DO_REDUCE(sve_fminnmv_d, float64, H1_8, float64_minnum, 0x7FF8000000000000ULL) + +DO_REDUCE(sve_fmaxnmv_h, float16, H1_2, float16_maxnum, 0x7E00) +DO_REDUCE(sve_fmaxnmv_s, float32, H1_4, float32_maxnum, 0x7FC00000) +DO_REDUCE(sve_fmaxnmv_d, float64, H1_8, float64_maxnum, 0x7FF8000000000000ULL) -DO_REDUCE(sve_fmaxnmv_h, float16, H1_2, maxnum, 0x7E00) -DO_REDUCE(sve_fmaxnmv_s, float32, H1_4, maxnum, 0x7FC00000) -DO_REDUCE(sve_fmaxnmv_d, float64, H1_8, maxnum, 0x7FF8000000000000ULL) +DO_REDUCE(sve_fminv_h, float16, H1_2, float16_min, float16_infinity) +DO_REDUCE(sve_fminv_s, float32, H1_4, float32_min, float32_infinity) +DO_REDUCE(sve_fminv_d, float64, H1_8, float64_min, float64_infinity) -DO_REDUCE(sve_fminv_h, float16, H1_2, min, float16_infinity) -DO_REDUCE(sve_fminv_s, float32, H1_4, min, float32_infinity) -DO_REDUCE(sve_fminv_d, float64, H1_8, min, float64_infinity) +DO_REDUCE(sve_fmaxv_h, float16, H1_2, float16_max, float16_chs(float16_infinity)) +DO_REDUCE(sve_fmaxv_s, float32, H1_4, float32_max, float32_chs(float32_infinity)) +DO_REDUCE(sve_fmaxv_d, float64, H1_8, float64_max, float64_chs(float64_infinity)) -DO_REDUCE(sve_fmaxv_h, float16, H1_2, max, float16_chs(float16_infinity)) -DO_REDUCE(sve_fmaxv_s, float32, H1_4, max, float32_chs(float32_infinity)) -DO_REDUCE(sve_fmaxv_d, float64, H1_8, max, float64_chs(float64_infinity)) +DO_REDUCE(sve_ah_fminv_h, float16, H1_2, helper_vfp_ah_minh, float16_infinity) +DO_REDUCE(sve_ah_fminv_s, float32, H1_4, helper_vfp_ah_mins, float32_infinity) +DO_REDUCE(sve_ah_fminv_d, float64, H1_8, helper_vfp_ah_mind, float64_infinity) + +DO_REDUCE(sve_ah_fmaxv_h, float16, H1_2, helper_vfp_ah_maxh, + float16_chs(float16_infinity)) +DO_REDUCE(sve_ah_fmaxv_s, float32, H1_4, helper_vfp_ah_maxs, + float32_chs(float32_infinity)) +DO_REDUCE(sve_ah_fmaxv_d, float64, H1_8, helper_vfp_ah_maxd, + float64_chs(float64_infinity)) #undef DO_REDUCE uint64_t HELPER(sve_fadda_h)(uint64_t nn, void *vm, void *vg, - void *status, uint32_t desc) + float_status *status, uint32_t desc) { intptr_t i = 0, opr_sz = simd_oprsz(desc); float16 result = nn; @@ -4251,7 +4298,7 @@ uint64_t HELPER(sve_fadda_h)(uint64_t nn, void *vm, void *vg, } uint64_t HELPER(sve_fadda_s)(uint64_t nn, void *vm, void *vg, - void *status, uint32_t desc) + float_status *status, uint32_t desc) { intptr_t i = 0, opr_sz = simd_oprsz(desc); float32 result = nn; @@ -4271,7 +4318,7 @@ uint64_t HELPER(sve_fadda_s)(uint64_t nn, void *vm, void *vg, } uint64_t HELPER(sve_fadda_d)(uint64_t nn, void *vm, void *vg, - void *status, uint32_t desc) + float_status *status, uint32_t desc) { intptr_t i = 0, opr_sz = simd_oprsz(desc) / 8; uint64_t *m = vm; @@ -4291,7 +4338,7 @@ uint64_t HELPER(sve_fadda_d)(uint64_t nn, void *vm, void *vg, */ #define DO_ZPZZ_FP(NAME, TYPE, H, OP) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, \ - void *status, uint32_t desc) \ + float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc); \ uint64_t *g = vg; \ @@ -4332,6 +4379,14 @@ DO_ZPZZ_FP(sve_fmax_h, uint16_t, H1_2, float16_max) DO_ZPZZ_FP(sve_fmax_s, uint32_t, H1_4, float32_max) DO_ZPZZ_FP(sve_fmax_d, uint64_t, H1_8, float64_max) +DO_ZPZZ_FP(sve_ah_fmin_h, uint16_t, H1_2, helper_vfp_ah_minh) +DO_ZPZZ_FP(sve_ah_fmin_s, uint32_t, H1_4, helper_vfp_ah_mins) +DO_ZPZZ_FP(sve_ah_fmin_d, uint64_t, H1_8, helper_vfp_ah_mind) + +DO_ZPZZ_FP(sve_ah_fmax_h, uint16_t, H1_2, helper_vfp_ah_maxh) +DO_ZPZZ_FP(sve_ah_fmax_s, uint32_t, H1_4, helper_vfp_ah_maxs) +DO_ZPZZ_FP(sve_ah_fmax_d, uint64_t, H1_8, helper_vfp_ah_maxd) + DO_ZPZZ_FP(sve_fminnum_h, uint16_t, H1_2, float16_minnum) DO_ZPZZ_FP(sve_fminnum_s, uint32_t, H1_4, float32_minnum) DO_ZPZZ_FP(sve_fminnum_d, uint64_t, H1_8, float64_minnum) @@ -4355,9 +4410,31 @@ static inline float64 abd_d(float64 a, float64 b, float_status *s) return float64_abs(float64_sub(a, b, s)); } +/* ABD when FPCR.AH = 1: avoid flipping sign bit of a NaN result */ +static float16 ah_abd_h(float16 op1, float16 op2, float_status *stat) +{ + float16 r = float16_sub(op1, op2, stat); + return float16_is_any_nan(r) ? r : float16_abs(r); +} + +static float32 ah_abd_s(float32 op1, float32 op2, float_status *stat) +{ + float32 r = float32_sub(op1, op2, stat); + return float32_is_any_nan(r) ? r : float32_abs(r); +} + +static float64 ah_abd_d(float64 op1, float64 op2, float_status *stat) +{ + float64 r = float64_sub(op1, op2, stat); + return float64_is_any_nan(r) ? r : float64_abs(r); +} + DO_ZPZZ_FP(sve_fabd_h, uint16_t, H1_2, abd_h) DO_ZPZZ_FP(sve_fabd_s, uint32_t, H1_4, abd_s) DO_ZPZZ_FP(sve_fabd_d, uint64_t, H1_8, abd_d) +DO_ZPZZ_FP(sve_ah_fabd_h, uint16_t, H1_2, ah_abd_h) +DO_ZPZZ_FP(sve_ah_fabd_s, uint32_t, H1_4, ah_abd_s) +DO_ZPZZ_FP(sve_ah_fabd_d, uint64_t, H1_8, ah_abd_d) static inline float64 scalbn_d(float64 a, int64_t b, float_status *s) { @@ -4380,7 +4457,7 @@ DO_ZPZZ_FP(sve_fmulx_d, uint64_t, H1_8, helper_vfp_mulxd) */ #define DO_ZPZS_FP(NAME, TYPE, H, OP) \ void HELPER(NAME)(void *vd, void *vn, void *vg, uint64_t scalar, \ - void *status, uint32_t desc) \ + float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc); \ uint64_t *g = vg; \ @@ -4444,11 +4521,20 @@ DO_ZPZS_FP(sve_fmins_h, float16, H1_2, float16_min) DO_ZPZS_FP(sve_fmins_s, float32, H1_4, float32_min) DO_ZPZS_FP(sve_fmins_d, float64, H1_8, float64_min) +DO_ZPZS_FP(sve_ah_fmaxs_h, float16, H1_2, helper_vfp_ah_maxh) +DO_ZPZS_FP(sve_ah_fmaxs_s, float32, H1_4, helper_vfp_ah_maxs) +DO_ZPZS_FP(sve_ah_fmaxs_d, float64, H1_8, helper_vfp_ah_maxd) + +DO_ZPZS_FP(sve_ah_fmins_h, float16, H1_2, helper_vfp_ah_minh) +DO_ZPZS_FP(sve_ah_fmins_s, float32, H1_4, helper_vfp_ah_mins) +DO_ZPZS_FP(sve_ah_fmins_d, float64, H1_8, helper_vfp_ah_mind) + /* Fully general two-operand expander, controlled by a predicate, * With the extra float_status parameter. */ #define DO_ZPZ_FP(NAME, TYPE, H, OP) \ -void HELPER(NAME)(void *vd, void *vn, void *vg, void *status, uint32_t desc) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, \ + float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc); \ uint64_t *g = vg; \ @@ -4653,7 +4739,7 @@ static int16_t do_float16_logb_as_int(float16 a, float_status *s) return -15 - clz32(frac); } /* flush to zero */ - float_raise(float_flag_input_denormal, s); + float_raise(float_flag_input_denormal_flushed, s); } } else if (unlikely(exp == 0x1f)) { if (frac == 0) { @@ -4681,7 +4767,7 @@ static int32_t do_float32_logb_as_int(float32 a, float_status *s) return -127 - clz32(frac); } /* flush to zero */ - float_raise(float_flag_input_denormal, s); + float_raise(float_flag_input_denormal_flushed, s); } } else if (unlikely(exp == 0xff)) { if (frac == 0) { @@ -4709,7 +4795,7 @@ static int64_t do_float64_logb_as_int(float64 a, float_status *s) return -1023 - clz64(frac); } /* flush to zero */ - float_raise(float_flag_input_denormal, s); + float_raise(float_flag_input_denormal_flushed, s); } } else if (unlikely(exp == 0x7ff)) { if (frac == 0) { @@ -4732,7 +4818,7 @@ DO_ZPZ_FP(flogb_d, float64, H1_8, do_float64_logb_as_int) static void do_fmla_zpzzz_h(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc, - uint16_t neg1, uint16_t neg3) + uint16_t neg1, uint16_t neg3, int flags) { intptr_t i = simd_oprsz(desc); uint64_t *g = vg; @@ -4747,7 +4833,7 @@ static void do_fmla_zpzzz_h(void *vd, void *vn, void *vm, void *va, void *vg, e1 = *(uint16_t *)(vn + H1_2(i)) ^ neg1; e2 = *(uint16_t *)(vm + H1_2(i)); e3 = *(uint16_t *)(va + H1_2(i)) ^ neg3; - r = float16_muladd(e1, e2, e3, 0, status); + r = float16_muladd(e1, e2, e3, flags, status); *(uint16_t *)(vd + H1_2(i)) = r; } } while (i & 63); @@ -4755,32 +4841,53 @@ static void do_fmla_zpzzz_h(void *vd, void *vn, void *vm, void *va, void *vg, } void HELPER(sve_fmla_zpzzz_h)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0); + do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0, 0); } void HELPER(sve_fmls_zpzzz_h)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0x8000, 0); + do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0x8000, 0, 0); } void HELPER(sve_fnmla_zpzzz_h)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0x8000, 0x8000); + do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0x8000, 0x8000, 0); } void HELPER(sve_fnmls_zpzzz_h)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0x8000, 0); +} + +void HELPER(sve_ah_fmls_zpzzz_h)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product); +} + +void HELPER(sve_ah_fnmla_zpzzz_h)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0x8000); + do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product | float_muladd_negate_c); +} + +void HELPER(sve_ah_fnmls_zpzzz_h)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_h(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_c); } static void do_fmla_zpzzz_s(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc, - uint32_t neg1, uint32_t neg3) + uint32_t neg1, uint32_t neg3, int flags) { intptr_t i = simd_oprsz(desc); uint64_t *g = vg; @@ -4795,7 +4902,7 @@ static void do_fmla_zpzzz_s(void *vd, void *vn, void *vm, void *va, void *vg, e1 = *(uint32_t *)(vn + H1_4(i)) ^ neg1; e2 = *(uint32_t *)(vm + H1_4(i)); e3 = *(uint32_t *)(va + H1_4(i)) ^ neg3; - r = float32_muladd(e1, e2, e3, 0, status); + r = float32_muladd(e1, e2, e3, flags, status); *(uint32_t *)(vd + H1_4(i)) = r; } } while (i & 63); @@ -4803,32 +4910,53 @@ static void do_fmla_zpzzz_s(void *vd, void *vn, void *vm, void *va, void *vg, } void HELPER(sve_fmla_zpzzz_s)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0); + do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0, 0); } void HELPER(sve_fmls_zpzzz_s)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0x80000000, 0); + do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0x80000000, 0, 0); } void HELPER(sve_fnmla_zpzzz_s)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0x80000000, 0x80000000); + do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0x80000000, 0x80000000, 0); } void HELPER(sve_fnmls_zpzzz_s)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0x80000000, 0); +} + +void HELPER(sve_ah_fmls_zpzzz_s)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product); +} + +void HELPER(sve_ah_fnmla_zpzzz_s)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product | float_muladd_negate_c); +} + +void HELPER(sve_ah_fnmls_zpzzz_s)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0x80000000); + do_fmla_zpzzz_s(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_c); } static void do_fmla_zpzzz_d(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc, - uint64_t neg1, uint64_t neg3) + uint64_t neg1, uint64_t neg3, int flags) { intptr_t i = simd_oprsz(desc); uint64_t *g = vg; @@ -4843,7 +4971,7 @@ static void do_fmla_zpzzz_d(void *vd, void *vn, void *vm, void *va, void *vg, e1 = *(uint64_t *)(vn + i) ^ neg1; e2 = *(uint64_t *)(vm + i); e3 = *(uint64_t *)(va + i) ^ neg3; - r = float64_muladd(e1, e2, e3, 0, status); + r = float64_muladd(e1, e2, e3, flags, status); *(uint64_t *)(vd + i) = r; } } while (i & 63); @@ -4851,27 +4979,48 @@ static void do_fmla_zpzzz_d(void *vd, void *vn, void *vm, void *va, void *vg, } void HELPER(sve_fmla_zpzzz_d)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, 0); + do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, 0, 0); } void HELPER(sve_fmls_zpzzz_d)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, INT64_MIN, 0); + do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, INT64_MIN, 0, 0); } void HELPER(sve_fnmla_zpzzz_d)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, INT64_MIN, INT64_MIN); + do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, INT64_MIN, INT64_MIN, 0); } void HELPER(sve_fnmls_zpzzz_d)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, INT64_MIN, 0); +} + +void HELPER(sve_ah_fmls_zpzzz_d)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product); +} + +void HELPER(sve_ah_fnmla_zpzzz_d)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product | float_muladd_negate_c); +} + +void HELPER(sve_ah_fnmls_zpzzz_d)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) { - do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, INT64_MIN); + do_fmla_zpzzz_d(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_c); } /* Two operand floating-point comparison controlled by a predicate. @@ -4881,7 +5030,7 @@ void HELPER(sve_fnmls_zpzzz_d)(void *vd, void *vn, void *vm, void *va, */ #define DO_FPCMP_PPZZ(NAME, TYPE, H, OP) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, \ - void *status, uint32_t desc) \ + float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc), j = (i - 1) >> 6; \ uint64_t *d = vd, *g = vg; \ @@ -4943,7 +5092,7 @@ DO_FPCMP_PPZZ_ALL(sve_facgt, DO_FACGT) */ #define DO_FPCMP_PPZ0(NAME, TYPE, H, OP) \ void HELPER(NAME)(void *vd, void *vn, void *vg, \ - void *status, uint32_t desc) \ + float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc), j = (i - 1) >> 6; \ uint64_t *d = vd, *g = vg; \ @@ -4981,27 +5130,37 @@ DO_FPCMP_PPZ0_ALL(sve_fcmne0, DO_FCMNE) /* FP Trig Multiply-Add. */ -void HELPER(sve_ftmad_h)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) +void HELPER(sve_ftmad_h)(void *vd, void *vn, void *vm, + float_status *s, uint32_t desc) { static const float16 coeff[16] = { 0x3c00, 0xb155, 0x2030, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3c00, 0xb800, 0x293a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; intptr_t i, opr_sz = simd_oprsz(desc) / sizeof(float16); - intptr_t x = simd_data(desc); + intptr_t x = extract32(desc, SIMD_DATA_SHIFT, 3); + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 3, 1); float16 *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i++) { float16 mm = m[i]; intptr_t xx = x; + int flags = 0; + if (float16_is_neg(mm)) { - mm = float16_abs(mm); + if (fpcr_ah) { + flags = float_muladd_negate_product; + } else { + mm = float16_abs(mm); + } xx += 8; } - d[i] = float16_muladd(n[i], mm, coeff[xx], 0, vs); + d[i] = float16_muladd(n[i], mm, coeff[xx], flags, s); } } -void HELPER(sve_ftmad_s)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) +void HELPER(sve_ftmad_s)(void *vd, void *vn, void *vm, + float_status *s, uint32_t desc) { static const float32 coeff[16] = { 0x3f800000, 0xbe2aaaab, 0x3c088886, 0xb95008b9, @@ -5010,20 +5169,29 @@ void HELPER(sve_ftmad_s)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) 0x37cd37cc, 0x00000000, 0x00000000, 0x00000000, }; intptr_t i, opr_sz = simd_oprsz(desc) / sizeof(float32); - intptr_t x = simd_data(desc); + intptr_t x = extract32(desc, SIMD_DATA_SHIFT, 3); + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 3, 1); float32 *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i++) { float32 mm = m[i]; intptr_t xx = x; + int flags = 0; + if (float32_is_neg(mm)) { - mm = float32_abs(mm); + if (fpcr_ah) { + flags = float_muladd_negate_product; + } else { + mm = float32_abs(mm); + } xx += 8; } - d[i] = float32_muladd(n[i], mm, coeff[xx], 0, vs); + d[i] = float32_muladd(n[i], mm, coeff[xx], flags, s); } } -void HELPER(sve_ftmad_d)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) +void HELPER(sve_ftmad_d)(void *vd, void *vn, void *vm, + float_status *s, uint32_t desc) { static const float64 coeff[16] = { 0x3ff0000000000000ull, 0xbfc5555555555543ull, @@ -5036,16 +5204,24 @@ void HELPER(sve_ftmad_d)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) 0x3e21ee96d2641b13ull, 0xbda8f76380fbb401ull, }; intptr_t i, opr_sz = simd_oprsz(desc) / sizeof(float64); - intptr_t x = simd_data(desc); + intptr_t x = extract32(desc, SIMD_DATA_SHIFT, 3); + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 3, 1); float64 *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i++) { float64 mm = m[i]; intptr_t xx = x; + int flags = 0; + if (float64_is_neg(mm)) { - mm = float64_abs(mm); + if (fpcr_ah) { + flags = float_muladd_negate_product; + } else { + mm = float64_abs(mm); + } xx += 8; } - d[i] = float64_muladd(n[i], mm, coeff[xx], 0, vs); + d[i] = float64_muladd(n[i], mm, coeff[xx], flags, s); } } @@ -5054,12 +5230,12 @@ void HELPER(sve_ftmad_d)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) */ void HELPER(sve_fcadd_h)(void *vd, void *vn, void *vm, void *vg, - void *vs, uint32_t desc) + float_status *s, uint32_t desc) { intptr_t j, i = simd_oprsz(desc); uint64_t *g = vg; - float16 neg_imag = float16_set_sign(0, simd_data(desc)); - float16 neg_real = float16_chs(neg_imag); + bool rot = extract32(desc, SIMD_DATA_SHIFT, 1); + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 1, 1); do { uint64_t pg = g[(i - 1) >> 6]; @@ -5071,27 +5247,33 @@ void HELPER(sve_fcadd_h)(void *vd, void *vn, void *vm, void *vg, i -= 2 * sizeof(float16); e0 = *(float16 *)(vn + H1_2(i)); - e1 = *(float16 *)(vm + H1_2(j)) ^ neg_real; + e1 = *(float16 *)(vm + H1_2(j)); e2 = *(float16 *)(vn + H1_2(j)); - e3 = *(float16 *)(vm + H1_2(i)) ^ neg_imag; + e3 = *(float16 *)(vm + H1_2(i)); + + if (rot) { + e3 = float16_maybe_ah_chs(e3, fpcr_ah); + } else { + e1 = float16_maybe_ah_chs(e1, fpcr_ah); + } if (likely((pg >> (i & 63)) & 1)) { - *(float16 *)(vd + H1_2(i)) = float16_add(e0, e1, vs); + *(float16 *)(vd + H1_2(i)) = float16_add(e0, e1, s); } if (likely((pg >> (j & 63)) & 1)) { - *(float16 *)(vd + H1_2(j)) = float16_add(e2, e3, vs); + *(float16 *)(vd + H1_2(j)) = float16_add(e2, e3, s); } } while (i & 63); } while (i != 0); } void HELPER(sve_fcadd_s)(void *vd, void *vn, void *vm, void *vg, - void *vs, uint32_t desc) + float_status *s, uint32_t desc) { intptr_t j, i = simd_oprsz(desc); uint64_t *g = vg; - float32 neg_imag = float32_set_sign(0, simd_data(desc)); - float32 neg_real = float32_chs(neg_imag); + bool rot = extract32(desc, SIMD_DATA_SHIFT, 1); + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 1, 1); do { uint64_t pg = g[(i - 1) >> 6]; @@ -5103,27 +5285,33 @@ void HELPER(sve_fcadd_s)(void *vd, void *vn, void *vm, void *vg, i -= 2 * sizeof(float32); e0 = *(float32 *)(vn + H1_2(i)); - e1 = *(float32 *)(vm + H1_2(j)) ^ neg_real; + e1 = *(float32 *)(vm + H1_2(j)); e2 = *(float32 *)(vn + H1_2(j)); - e3 = *(float32 *)(vm + H1_2(i)) ^ neg_imag; + e3 = *(float32 *)(vm + H1_2(i)); + + if (rot) { + e3 = float32_maybe_ah_chs(e3, fpcr_ah); + } else { + e1 = float32_maybe_ah_chs(e1, fpcr_ah); + } if (likely((pg >> (i & 63)) & 1)) { - *(float32 *)(vd + H1_2(i)) = float32_add(e0, e1, vs); + *(float32 *)(vd + H1_2(i)) = float32_add(e0, e1, s); } if (likely((pg >> (j & 63)) & 1)) { - *(float32 *)(vd + H1_2(j)) = float32_add(e2, e3, vs); + *(float32 *)(vd + H1_2(j)) = float32_add(e2, e3, s); } } while (i & 63); } while (i != 0); } void HELPER(sve_fcadd_d)(void *vd, void *vn, void *vm, void *vg, - void *vs, uint32_t desc) + float_status *s, uint32_t desc) { intptr_t j, i = simd_oprsz(desc); uint64_t *g = vg; - float64 neg_imag = float64_set_sign(0, simd_data(desc)); - float64 neg_real = float64_chs(neg_imag); + bool rot = extract32(desc, SIMD_DATA_SHIFT, 1); + bool fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 1, 1); do { uint64_t pg = g[(i - 1) >> 6]; @@ -5135,15 +5323,21 @@ void HELPER(sve_fcadd_d)(void *vd, void *vn, void *vm, void *vg, i -= 2 * sizeof(float64); e0 = *(float64 *)(vn + H1_2(i)); - e1 = *(float64 *)(vm + H1_2(j)) ^ neg_real; + e1 = *(float64 *)(vm + H1_2(j)); e2 = *(float64 *)(vn + H1_2(j)); - e3 = *(float64 *)(vm + H1_2(i)) ^ neg_imag; + e3 = *(float64 *)(vm + H1_2(i)); + + if (rot) { + e3 = float64_maybe_ah_chs(e3, fpcr_ah); + } else { + e1 = float64_maybe_ah_chs(e1, fpcr_ah); + } if (likely((pg >> (i & 63)) & 1)) { - *(float64 *)(vd + H1_2(i)) = float64_add(e0, e1, vs); + *(float64 *)(vd + H1_2(i)) = float64_add(e0, e1, s); } if (likely((pg >> (j & 63)) & 1)) { - *(float64 *)(vd + H1_2(j)) = float64_add(e2, e3, vs); + *(float64 *)(vd + H1_2(j)) = float64_add(e2, e3, s); } } while (i & 63); } while (i != 0); @@ -5154,16 +5348,21 @@ void HELPER(sve_fcadd_d)(void *vd, void *vn, void *vm, void *vg, */ void HELPER(sve_fcmla_zpzzz_h)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { intptr_t j, i = simd_oprsz(desc); - unsigned rot = simd_data(desc); - bool flip = rot & 1; - float16 neg_imag, neg_real; + bool flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + uint32_t negf_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t negf_real = flip ^ negf_imag; + float16 negx_imag, negx_real; uint64_t *g = vg; - neg_imag = float16_set_sign(0, (rot & 2) != 0); - neg_real = float16_set_sign(0, rot == 1 || rot == 2); + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (negf_real & ~fpcr_ah) << 15; + negx_imag = (negf_imag & ~fpcr_ah) << 15; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); do { uint64_t pg = g[(i - 1) >> 6]; @@ -5180,18 +5379,18 @@ void HELPER(sve_fcmla_zpzzz_h)(void *vd, void *vn, void *vm, void *va, mi = *(float16 *)(vm + H1_2(j)); e2 = (flip ? ni : nr); - e1 = (flip ? mi : mr) ^ neg_real; + e1 = (flip ? mi : mr) ^ negx_real; e4 = e2; - e3 = (flip ? mr : mi) ^ neg_imag; + e3 = (flip ? mr : mi) ^ negx_imag; if (likely((pg >> (i & 63)) & 1)) { d = *(float16 *)(va + H1_2(i)); - d = float16_muladd(e2, e1, d, 0, status); + d = float16_muladd(e2, e1, d, negf_real, status); *(float16 *)(vd + H1_2(i)) = d; } if (likely((pg >> (j & 63)) & 1)) { d = *(float16 *)(va + H1_2(j)); - d = float16_muladd(e4, e3, d, 0, status); + d = float16_muladd(e4, e3, d, negf_imag, status); *(float16 *)(vd + H1_2(j)) = d; } } while (i & 63); @@ -5199,16 +5398,21 @@ void HELPER(sve_fcmla_zpzzz_h)(void *vd, void *vn, void *vm, void *va, } void HELPER(sve_fcmla_zpzzz_s)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { intptr_t j, i = simd_oprsz(desc); - unsigned rot = simd_data(desc); - bool flip = rot & 1; - float32 neg_imag, neg_real; + bool flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + uint32_t negf_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t negf_real = flip ^ negf_imag; + float32 negx_imag, negx_real; uint64_t *g = vg; - neg_imag = float32_set_sign(0, (rot & 2) != 0); - neg_real = float32_set_sign(0, rot == 1 || rot == 2); + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (negf_real & ~fpcr_ah) << 31; + negx_imag = (negf_imag & ~fpcr_ah) << 31; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); do { uint64_t pg = g[(i - 1) >> 6]; @@ -5225,18 +5429,18 @@ void HELPER(sve_fcmla_zpzzz_s)(void *vd, void *vn, void *vm, void *va, mi = *(float32 *)(vm + H1_2(j)); e2 = (flip ? ni : nr); - e1 = (flip ? mi : mr) ^ neg_real; + e1 = (flip ? mi : mr) ^ negx_real; e4 = e2; - e3 = (flip ? mr : mi) ^ neg_imag; + e3 = (flip ? mr : mi) ^ negx_imag; if (likely((pg >> (i & 63)) & 1)) { d = *(float32 *)(va + H1_2(i)); - d = float32_muladd(e2, e1, d, 0, status); + d = float32_muladd(e2, e1, d, negf_real, status); *(float32 *)(vd + H1_2(i)) = d; } if (likely((pg >> (j & 63)) & 1)) { d = *(float32 *)(va + H1_2(j)); - d = float32_muladd(e4, e3, d, 0, status); + d = float32_muladd(e4, e3, d, negf_imag, status); *(float32 *)(vd + H1_2(j)) = d; } } while (i & 63); @@ -5244,16 +5448,21 @@ void HELPER(sve_fcmla_zpzzz_s)(void *vd, void *vn, void *vm, void *va, } void HELPER(sve_fcmla_zpzzz_d)(void *vd, void *vn, void *vm, void *va, - void *vg, void *status, uint32_t desc) + void *vg, float_status *status, uint32_t desc) { intptr_t j, i = simd_oprsz(desc); - unsigned rot = simd_data(desc); - bool flip = rot & 1; - float64 neg_imag, neg_real; + bool flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + uint32_t negf_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t negf_real = flip ^ negf_imag; + float64 negx_imag, negx_real; uint64_t *g = vg; - neg_imag = float64_set_sign(0, (rot & 2) != 0); - neg_real = float64_set_sign(0, rot == 1 || rot == 2); + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (uint64_t)(negf_real & ~fpcr_ah) << 63; + negx_imag = (uint64_t)(negf_imag & ~fpcr_ah) << 63; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); do { uint64_t pg = g[(i - 1) >> 6]; @@ -5270,18 +5479,18 @@ void HELPER(sve_fcmla_zpzzz_d)(void *vd, void *vn, void *vm, void *va, mi = *(float64 *)(vm + H1_2(j)); e2 = (flip ? ni : nr); - e1 = (flip ? mi : mr) ^ neg_real; + e1 = (flip ? mi : mr) ^ negx_real; e4 = e2; - e3 = (flip ? mr : mi) ^ neg_imag; + e3 = (flip ? mr : mi) ^ negx_imag; if (likely((pg >> (i & 63)) & 1)) { d = *(float64 *)(va + H1_2(i)); - d = float64_muladd(e2, e1, d, 0, status); + d = float64_muladd(e2, e1, d, negf_real, status); *(float64 *)(vd + H1_2(i)) = d; } if (likely((pg >> (j & 63)) & 1)) { d = *(float64 *)(va + H1_2(j)); - d = float64_muladd(e4, e3, d, 0, status); + d = float64_muladd(e4, e3, d, negf_imag, status); *(float64 *)(vd + H1_2(j)) = d; } } while (i & 63); @@ -5373,7 +5582,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. */ @@ -5481,7 +5690,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; @@ -5737,6 +5946,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 { @@ -5751,6 +5962,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. @@ -5770,6 +5983,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 { @@ -5783,6 +5998,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(); } } @@ -5800,8 +6017,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; } @@ -5933,15 +6150,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 @@ -6092,6 +6305,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 { @@ -6100,9 +6315,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); @@ -6112,6 +6329,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. @@ -6156,8 +6375,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; } @@ -6306,9 +6525,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, @@ -6339,7 +6555,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]; @@ -6347,6 +6562,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 { @@ -6361,6 +6578,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. @@ -6380,6 +6599,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 { @@ -6393,6 +6614,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(); } } @@ -6410,8 +6633,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; } @@ -6529,7 +6752,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; @@ -6559,7 +6782,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. */ @@ -6715,7 +6940,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; @@ -6781,7 +7006,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); @@ -6920,7 +7147,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]; @@ -6985,7 +7212,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); @@ -7368,7 +7597,7 @@ void HELPER(sve2_xar_s)(void *vd, void *vn, void *vm, uint32_t desc) } void HELPER(fmmla_s)(void *vd, void *vn, void *vm, void *va, - void *status, uint32_t desc) + float_status *status, uint32_t desc) { intptr_t s, opr_sz = simd_oprsz(desc) / (sizeof(float32) * 4); @@ -7406,7 +7635,7 @@ void HELPER(fmmla_s)(void *vd, void *vn, void *vm, void *va, } void HELPER(fmmla_d)(void *vd, void *vn, void *vm, void *va, - void *status, uint32_t desc) + float_status *status, uint32_t desc) { intptr_t s, opr_sz = simd_oprsz(desc) / (sizeof(float64) * 4); @@ -7442,7 +7671,8 @@ void HELPER(fmmla_d)(void *vd, void *vn, void *vm, void *va, } #define DO_FCVTNT(NAME, TYPEW, TYPEN, HW, HN, OP) \ -void HELPER(NAME)(void *vd, void *vn, void *vg, void *status, uint32_t desc) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, \ + float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc); \ uint64_t *g = vg; \ @@ -7463,7 +7693,8 @@ DO_FCVTNT(sve2_fcvtnt_sh, uint32_t, uint16_t, H1_4, H1_2, sve_f32_to_f16) DO_FCVTNT(sve2_fcvtnt_ds, uint64_t, uint32_t, H1_8, H1_4, float64_to_float32) #define DO_FCVTLT(NAME, TYPEW, TYPEN, HW, HN, OP) \ -void HELPER(NAME)(void *vd, void *vn, void *vg, void *status, uint32_t desc) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, \ + float_status *status, uint32_t desc) \ { \ intptr_t i = simd_oprsz(desc); \ uint64_t *g = vg; \ diff --git a/target/arm/tcg/sve_ldst_internal.h b/target/arm/tcg/sve_ldst_internal.h index 4f159ec4adf..f2243daf370 100644 --- a/target/arm/tcg/sve_ldst_internal.h +++ b/target/arm/tcg/sve_ldst_internal.h @@ -20,7 +20,7 @@ #ifndef TARGET_ARM_SVE_LDST_INTERNAL_H #define TARGET_ARM_SVE_LDST_INTERNAL_H -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" /* * Load one element into @vd + @reg_off from @host. diff --git a/target/arm/tcg/t32.decode b/target/arm/tcg/t32.decode index f21ad0167ab..d327178829d 100644 --- a/target/arm/tcg/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-insns.c b/target/arm/tcg/tlb-insns.c new file mode 100644 index 00000000000..95c26c6d463 --- /dev/null +++ b/target/arm/tcg/tlb-insns.c @@ -0,0 +1,1306 @@ +/* + * Helpers for TLBI insns + * + * This code is licensed under the GNU GPL v2 or later. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "exec/cputlb.h" +#include "exec/target_page.h" +#include "cpu.h" +#include "internals.h" +#include "cpu-features.h" +#include "cpregs.h" + +/* Check for traps from EL1 due to HCR_EL2.TTLB. */ +static CPAccessResult access_ttlb(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TTLB)) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_OK; +} + +/* 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; +} + +/* 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; +} + +/* IS variants of TLB operations must affect all cores */ +static void tlbiall_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_all_cpus_synced(cs); +} + +static void tlbiasid_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_all_cpus_synced(cs); +} + +static void tlbimva_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK); +} + +static void tlbimvaa_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK); +} + +/* + * Non-IS variants of TLB operations are upgraded to + * IS versions if we are at EL1 and HCR_EL2.FB is effectively set to + * force broadcast of these operations. + */ +static bool tlb_force_broadcast(CPUARMState *env) +{ + return arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_FB); +} + +static void tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate all (TLBIALL) */ + CPUState *cs = env_cpu(env); + + if (tlb_force_broadcast(env)) { + tlb_flush_all_cpus_synced(cs); + } else { + tlb_flush(cs); + } +} + +static void tlbimva_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate single TLB entry by MVA and ASID (TLBIMVA) */ + CPUState *cs = env_cpu(env); + + value &= TARGET_PAGE_MASK; + if (tlb_force_broadcast(env)) { + tlb_flush_page_all_cpus_synced(cs, value); + } else { + tlb_flush_page(cs, value); + } +} + +static void tlbiasid_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate by ASID (TLBIASID) */ + CPUState *cs = env_cpu(env); + + if (tlb_force_broadcast(env)) { + tlb_flush_all_cpus_synced(cs); + } else { + tlb_flush(cs); + } +} + +static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate single entry by MVA, all ASIDs (TLBIMVAA) */ + CPUState *cs = env_cpu(env); + + value &= TARGET_PAGE_MASK; + if (tlb_force_broadcast(env)) { + tlb_flush_page_all_cpus_synced(cs, value); + } else { + tlb_flush_page(cs, value); + } +} + +static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); + + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E2); +} + +static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); + + tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, + ARMMMUIdxBit_E2); +} + +static void tlbiipas2_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + uint64_t pageaddr = (value & MAKE_64BIT_MASK(0, 28)) << 12; + + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_Stage2); +} + +static void tlbiipas2is_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + uint64_t pageaddr = (value & MAKE_64BIT_MASK(0, 28)) << 12; + + tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, ARMMMUIdxBit_Stage2); +} + +static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_by_mmuidx(cs, alle1_tlbmask(env)); +} + +static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_by_mmuidx_all_cpus_synced(cs, alle1_tlbmask(env)); +} + + +static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E2); +} + +static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E2); +} + +/* + * See: D4.7.2 TLB maintenance requirements and the TLB maintenance instructions + * Page D4-1736 (DDI0487A.b) + */ + +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; + } + 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) +{ + uint64_t tcr = regime_tcr(env, mmu_idx); + int tbi = aa64_va_parameter_tbi(tcr, mmu_idx); + int select = extract64(addr, 55, 1); + + return (tbi >> select) & 1 ? 56 : 64; +} + +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; + } else { + mmu_idx = ARMMMUIdx_E10_0; + } + + 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) +{ + CPUState *cs = env_cpu(env); + int mask = vae1_tlbmask(env); + + tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); +} + +static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = vae1_tlbmask(env); + + if (tlb_force_broadcast(env)) { + tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); + } else { + tlb_flush_by_mmuidx(cs, mask); + } +} + +static int e2_tlbmask(CPUARMState *env) +{ + return (ARMMMUIdxBit_E20_0 | + ARMMMUIdxBit_E20_2 | + ARMMMUIdxBit_E20_2_PAN | + ARMMMUIdxBit_E2); +} + +static void tlbi_aa64_alle1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = alle1_tlbmask(env); + + tlb_flush_by_mmuidx(cs, mask); +} + +static void tlbi_aa64_alle2_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = e2_tlbmask(env); + + tlb_flush_by_mmuidx(cs, mask); +} + +static void tlbi_aa64_alle3_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + CPUState *cs = CPU(cpu); + + tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E3); +} + +static void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = alle1_tlbmask(env); + + tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); +} + +static void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = e2_tlbmask(env); + + tlb_flush_by_mmuidx_all_cpus_synced(cs, mask); +} + +static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E3); +} + +static void tlbi_aa64_vae2_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA, EL2 + * Currently handles both VAE2 and VALE2, since we don't support + * flush-last-level-only. + */ + CPUState *cs = env_cpu(env); + int mask = vae2_tlbmask(env); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + int bits = vae2_tlbbits(env, pageaddr); + + 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 + * Currently handles both VAE3 and VALE3, since we don't support + * flush-last-level-only. + */ + ARMCPU *cpu = env_archcpu(env); + CPUState *cs = CPU(cpu); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E3); +} + +static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = vae1_tlbmask(env); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + int bits = vae1_tlbbits(env, pageaddr); + + tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); +} + +static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * 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. + */ + CPUState *cs = env_cpu(env); + int mask = vae1_tlbmask(env); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + int bits = vae1_tlbbits(env, pageaddr); + + if (tlb_force_broadcast(env)) { + tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); + } else { + tlb_flush_page_bits_by_mmuidx(cs, pageaddr, mask, bits); + } +} + +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 = vae2_tlbbits(env, pageaddr); + + tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); +} + +static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + int bits = tlbbits_for_regime(env, ARMMMUIdx_E3, pageaddr); + + tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, + ARMMMUIdxBit_E3, bits); +} + +static int ipas2e1_tlbmask(CPUARMState *env, int64_t value) +{ + /* + * The MSB of value is the NS field, which only applies if SEL2 + * is implemented and SCR_EL3.NS is not set (i.e. in secure mode). + */ + return (value >= 0 + && cpu_isar_feature(aa64_sel2, env_archcpu(env)) + && arm_is_secure_below_el3(env) + ? ARMMMUIdxBit_Stage2_S + : ARMMMUIdxBit_Stage2); +} + +static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = ipas2e1_tlbmask(env, value); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + + if (tlb_force_broadcast(env)) { + tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, mask); + } else { + tlb_flush_page_by_mmuidx(cs, pageaddr, mask); + } +} + +static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + int mask = ipas2e1_tlbmask(env, value); + uint64_t pageaddr = sextract64(value << 12, 0, 56); + + tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, mask); +} + +static const ARMCPRegInfo tlbi_not_v7_cp_reginfo[] = { + /* + * 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, + .opc1 = CP_ANY, .opc2 = 0, .access = PL1_W, .writefn = tlbiall_write, + .type = ARM_CP_NO_RAW }, + { .name = "TLBIMVA", .cp = 15, .crn = 8, .crm = CP_ANY, + .opc1 = CP_ANY, .opc2 = 1, .access = PL1_W, .writefn = tlbimva_write, + .type = ARM_CP_NO_RAW }, + { .name = "TLBIASID", .cp = 15, .crn = 8, .crm = CP_ANY, + .opc1 = CP_ANY, .opc2 = 2, .access = PL1_W, .writefn = tlbiasid_write, + .type = ARM_CP_NO_RAW }, + { .name = "TLBIMVAA", .cp = 15, .crn = 8, .crm = CP_ANY, + .opc1 = CP_ANY, .opc2 = 3, .access = PL1_W, .writefn = tlbimvaa_write, + .type = ARM_CP_NO_RAW }, +}; + +static const ARMCPRegInfo tlbi_v7_cp_reginfo[] = { + /* 32 bit ITLB invalidates */ + { .name = "ITLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 0, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiall_write }, + { .name = "ITLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimva_write }, + { .name = "ITLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 2, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiasid_write }, + /* 32 bit DTLB invalidates */ + { .name = "DTLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 0, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiall_write }, + { .name = "DTLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimva_write }, + { .name = "DTLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 2, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiasid_write }, + /* 32 bit TLB invalidates */ + { .name = "TLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiall_write }, + { .name = "TLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimva_write }, + { .name = "TLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiasid_write }, + { .name = "TLBIMVAA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimvaa_write }, +}; + +static const ARMCPRegInfo tlbi_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_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_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_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_ttlbis, + .writefn = tlbimvaa_is_write }, +}; + +static const ARMCPRegInfo tlbi_v8_cp_reginfo[] = { + /* AArch32 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_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_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, + .writefn = tlbimva_write }, + { .name = "TLBIMVAAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimvaa_write }, + { .name = "TLBIMVALH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 5, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbimva_hyp_write }, + { .name = "TLBIMVALHIS", + .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 5, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbimva_hyp_is_write }, + { .name = "TLBIIPAS2", + .cp = 15, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiipas2_hyp_write }, + { .name = "TLBIIPAS2IS", + .cp = 15, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiipas2is_hyp_write }, + { .name = "TLBIIPAS2L", + .cp = 15, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 5, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiipas2_hyp_write }, + { .name = "TLBIIPAS2LIS", + .cp = 15, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 5, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiipas2is_hyp_write }, + /* AArch64 TLBI operations */ + { .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIVMALLE1IS, + .writefn = tlbi_aa64_vmalle1is_write }, + { .name = "TLBI_VAE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIVAE1IS, + .writefn = tlbi_aa64_vae1is_write }, + { .name = "TLBI_ASIDE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIASIDE1IS, + .writefn = tlbi_aa64_vmalle1is_write }, + { .name = "TLBI_VAAE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIVAAE1IS, + .writefn = tlbi_aa64_vae1is_write }, + { .name = "TLBI_VALE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIVALE1IS, + .writefn = tlbi_aa64_vae1is_write }, + { .name = "TLBI_VAALE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .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 | ARM_CP_ADD_TLBI_NXS, + .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 | ARM_CP_ADD_TLBI_NXS, + .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 | ARM_CP_ADD_TLBI_NXS, + .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 | ARM_CP_ADD_TLBI_NXS, + .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 | ARM_CP_ADD_TLBI_NXS, + .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 | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIVAALE1, + .writefn = tlbi_aa64_vae1_write }, + { .name = "TLBI_IPAS2E1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 1, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_ipas2e1is_write }, + { .name = "TLBI_IPAS2LE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_ipas2e1is_write }, + { .name = "TLBI_ALLE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_alle1is_write }, + { .name = "TLBI_VMALLS12E1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 6, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_alle1is_write }, + { .name = "TLBI_IPAS2E1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 1, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_ipas2e1_write }, + { .name = "TLBI_IPAS2LE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_ipas2e1_write }, + { .name = "TLBI_ALLE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_alle1_write }, + { .name = "TLBI_VMALLS12E1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 6, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_alle1is_write }, +}; + +static const ARMCPRegInfo tlbi_el2_cp_reginfo[] = { + { .name = "TLBIALLNSNH", + .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiall_nsnh_write }, + { .name = "TLBIALLNSNHIS", + .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiall_nsnh_is_write }, + { .name = "TLBIALLH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiall_hyp_write }, + { .name = "TLBIALLHIS", .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 0, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbiall_hyp_is_write }, + { .name = "TLBIMVAH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbimva_hyp_write }, + { .name = "TLBIMVAHIS", .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 1, + .type = ARM_CP_NO_RAW, .access = PL2_W, + .writefn = tlbimva_hyp_is_write }, + { .name = "TLBI_ALLE2", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_alle2_write }, + { .name = "TLBI_VAE2", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 1, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_vae2_write }, + { .name = "TLBI_VALE2", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 5, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_vae2_write }, + { .name = "TLBI_ALLE2IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 0, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_alle2is_write }, + { .name = "TLBI_VAE2IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 1, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_vae2is_write }, + { .name = "TLBI_VALE2IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 5, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_vae2is_write }, +}; + +static const ARMCPRegInfo tlbi_el3_cp_reginfo[] = { + { .name = "TLBI_ALLE3IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 0, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_alle3is_write }, + { .name = "TLBI_VAE3IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_vae3is_write }, + { .name = "TLBI_VALE3IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 3, .opc2 = 5, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_vae3is_write }, + { .name = "TLBI_ALLE3", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 0, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_alle3_write }, + { .name = "TLBI_VAE3", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_vae3_write }, + { .name = "TLBI_VALE3", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 5, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_vae3_write }, +}; + +typedef struct { + uint64_t base; + uint64_t length; +} TLBIRange; + +static ARMGranuleSize tlbi_range_tg_to_gran_size(int tg) +{ + /* + * Note that the TLBI range TG field encoding differs from both + * TG0 and TG1 encodings. + */ + switch (tg) { + case 1: + return Gran4K; + case 2: + return Gran16K; + case 3: + return Gran64K; + default: + return GranInvalid; + } +} + +static TLBIRange tlbi_aa64_get_range(CPUARMState *env, ARMMMUIdx mmuidx, + uint64_t value) +{ + unsigned int page_size_granule, page_shift, num, scale, exponent; + /* Extract one bit to represent the va selector in use. */ + uint64_t select = sextract64(value, 36, 1); + ARMVAParameters param = aa64_va_parameters(env, select, mmuidx, true, false); + TLBIRange ret = { }; + ARMGranuleSize gran; + + page_size_granule = extract64(value, 46, 2); + gran = tlbi_range_tg_to_gran_size(page_size_granule); + + /* The granule encoded in value must match the granule in use. */ + if (gran != param.gran) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid tlbi page size granule %d\n", + page_size_granule); + return ret; + } + + page_shift = arm_granule_bits(gran); + num = extract64(value, 39, 5); + scale = extract64(value, 44, 2); + exponent = (5 * scale) + 1; + + ret.length = (num + 1) << (exponent + page_shift); + + if (param.select) { + ret.base = sextract64(value, 0, 37); + } else { + ret.base = extract64(value, 0, 37); + } + if (param.ds) { + /* + * With DS=1, BaseADDR is always shifted 16 so that it is able + * to address all 52 va bits. The input address is perforce + * aligned on a 64k boundary regardless of translation granule. + */ + page_shift = 16; + } + ret.base <<= page_shift; + + return ret; +} + +static void do_rvae_write(CPUARMState *env, uint64_t value, + int idxmap, bool synced) +{ + ARMMMUIdx one_idx = ARM_MMU_IDX_A | ctz32(idxmap); + TLBIRange range; + int bits; + + range = tlbi_aa64_get_range(env, one_idx, value); + bits = tlbbits_for_regime(env, one_idx, range.base); + + if (synced) { + tlb_flush_range_by_mmuidx_all_cpus_synced(env_cpu(env), + range.base, + range.length, + idxmap, + bits); + } else { + tlb_flush_range_by_mmuidx(env_cpu(env), range.base, + range.length, idxmap, bits); + } +} + +static void tlbi_aa64_rvae1_write(CPUARMState *env, + const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA range, EL1&0. + * Currently handles all of RVAE1, RVAAE1, RVAALE1 and RVALE1, + * since we don't support flush-for-specific-ASID-only or + * flush-last-level-only. + */ + + do_rvae_write(env, value, vae1_tlbmask(env), + tlb_force_broadcast(env)); +} + +static void tlbi_aa64_rvae1is_write(CPUARMState *env, + const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA range, Inner/Outer Shareable EL1&0. + * Currently handles all of RVAE1IS, RVAE1OS, RVAAE1IS, RVAAE1OS, + * RVAALE1IS, RVAALE1OS, RVALE1IS and RVALE1OS, since we don't support + * flush-for-specific-ASID-only, flush-last-level-only or inner/outer + * shareable specific flushes. + */ + + do_rvae_write(env, value, vae1_tlbmask(env), true); +} + +static void tlbi_aa64_rvae2_write(CPUARMState *env, + const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA range, EL2. + * Currently handles all of RVAE2 and RVALE2, + * since we don't support flush-for-specific-ASID-only or + * flush-last-level-only. + */ + + do_rvae_write(env, value, vae2_tlbmask(env), + tlb_force_broadcast(env)); + + +} + +static void tlbi_aa64_rvae2is_write(CPUARMState *env, + const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA range, Inner/Outer Shareable, EL2. + * Currently handles all of RVAE2IS, RVAE2OS, RVALE2IS and RVALE2OS, + * since we don't support flush-for-specific-ASID-only, + * flush-last-level-only or inner/outer shareable specific flushes. + */ + + do_rvae_write(env, value, vae2_tlbmask(env), true); + +} + +static void tlbi_aa64_rvae3_write(CPUARMState *env, + const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA range, EL3. + * Currently handles all of RVAE3 and RVALE3, + * since we don't support flush-for-specific-ASID-only or + * flush-last-level-only. + */ + + do_rvae_write(env, value, ARMMMUIdxBit_E3, tlb_force_broadcast(env)); +} + +static void tlbi_aa64_rvae3is_write(CPUARMState *env, + const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Invalidate by VA range, EL3, Inner/Outer Shareable. + * Currently handles all of RVAE3IS, RVAE3OS, RVALE3IS and RVALE3OS, + * since we don't support flush-for-specific-ASID-only, + * flush-last-level-only or inner/outer specific flushes. + */ + + do_rvae_write(env, value, ARMMMUIdxBit_E3, true); +} + +static void tlbi_aa64_ripas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + do_rvae_write(env, value, ipas2e1_tlbmask(env, value), + tlb_force_broadcast(env)); +} + +static void tlbi_aa64_ripas2e1is_write(CPUARMState *env, + const ARMCPRegInfo *ri, + uint64_t value) +{ + do_rvae_write(env, value, ipas2e1_tlbmask(env, value), true); +} + +static const ARMCPRegInfo tlbirange_reginfo[] = { + { .name = "TLBI_RVAE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 1, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIRVAE1IS, + .writefn = tlbi_aa64_rvae1is_write }, + { .name = "TLBI_RVAAE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 3, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIRVAAE1IS, + .writefn = tlbi_aa64_rvae1is_write }, + { .name = "TLBI_RVALE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 5, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIRVALE1IS, + .writefn = tlbi_aa64_rvae1is_write }, + { .name = "TLBI_RVAALE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 2, .opc2 = 7, + .access = PL1_W, .accessfn = access_ttlbis, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIRVAALE1IS, + .writefn = tlbi_aa64_rvae1is_write }, + { .name = "TLBI_RVAE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIRVAE1OS, + .writefn = tlbi_aa64_rvae1is_write }, + { .name = "TLBI_RVAAE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 3, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIRVAAE1OS, + .writefn = tlbi_aa64_rvae1is_write }, + { .name = "TLBI_RVALE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 5, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIRVALE1OS, + .writefn = tlbi_aa64_rvae1is_write }, + { .name = "TLBI_RVAALE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 7, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .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 | ARM_CP_ADD_TLBI_NXS, + .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 | ARM_CP_ADD_TLBI_NXS, + .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 | ARM_CP_ADD_TLBI_NXS, + .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 | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIRVAALE1, + .writefn = tlbi_aa64_rvae1_write }, + { .name = "TLBI_RIPAS2E1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 2, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_ripas2e1is_write }, + { .name = "TLBI_RIPAS2LE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 6, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_ripas2e1is_write }, + { .name = "TLBI_RVAE2IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 2, .opc2 = 1, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_rvae2is_write }, + { .name = "TLBI_RVALE2IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 2, .opc2 = 5, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_rvae2is_write }, + { .name = "TLBI_RIPAS2E1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 2, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_ripas2e1_write }, + { .name = "TLBI_RIPAS2LE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 6, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_ripas2e1_write }, + { .name = "TLBI_RVAE2OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 5, .opc2 = 1, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_rvae2is_write }, + { .name = "TLBI_RVALE2OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 5, .opc2 = 5, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_rvae2is_write }, + { .name = "TLBI_RVAE2", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 6, .opc2 = 1, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_rvae2_write }, + { .name = "TLBI_RVALE2", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 6, .opc2 = 5, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_rvae2_write }, + { .name = "TLBI_RVAE3IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 2, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_rvae3is_write }, + { .name = "TLBI_RVALE3IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 2, .opc2 = 5, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_rvae3is_write }, + { .name = "TLBI_RVAE3OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 5, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_rvae3is_write }, + { .name = "TLBI_RVALE3OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 5, .opc2 = 5, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_rvae3is_write }, + { .name = "TLBI_RVAE3", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 6, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_rvae3_write }, + { .name = "TLBI_RVALE3", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 6, .opc2 = 5, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_rvae3_write }, +}; + +static const ARMCPRegInfo tlbios_reginfo[] = { + { .name = "TLBI_VMALLE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 0, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIVMALLE1OS, + .writefn = tlbi_aa64_vmalle1is_write }, + { .name = "TLBI_VAE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 1, + .fgt = FGT_TLBIVAE1OS, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_vae1is_write }, + { .name = "TLBI_ASIDE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 2, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIASIDE1OS, + .writefn = tlbi_aa64_vmalle1is_write }, + { .name = "TLBI_VAAE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 3, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIVAAE1OS, + .writefn = tlbi_aa64_vae1is_write }, + { .name = "TLBI_VALE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 5, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .fgt = FGT_TLBIVALE1OS, + .writefn = tlbi_aa64_vae1is_write }, + { .name = "TLBI_VAALE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 1, .opc2 = 7, + .access = PL1_W, .accessfn = access_ttlbos, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .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, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_alle2is_write }, + { .name = "TLBI_VAE2OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 1, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_vae2is_write }, + { .name = "TLBI_ALLE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 4, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_alle1is_write }, + { .name = "TLBI_VALE2OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 5, + .access = PL2_W, + .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = tlbi_aa64_vae2is_write }, + { .name = "TLBI_VMALLS12E1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 1, .opc2 = 6, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_alle1is_write }, + { .name = "TLBI_IPAS2E1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 0, + .access = PL2_W, .type = ARM_CP_NOP | ARM_CP_ADD_TLBI_NXS }, + { .name = "TLBI_RIPAS2E1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 3, + .access = PL2_W, .type = ARM_CP_NOP | ARM_CP_ADD_TLBI_NXS }, + { .name = "TLBI_IPAS2LE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 4, + .access = PL2_W, .type = ARM_CP_NOP | ARM_CP_ADD_TLBI_NXS }, + { .name = "TLBI_RIPAS2LE1OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 7, + .access = PL2_W, .type = ARM_CP_NOP | ARM_CP_ADD_TLBI_NXS }, + { .name = "TLBI_ALLE3OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 0, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_alle3is_write }, + { .name = "TLBI_VAE3OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_vae3is_write }, + { .name = "TLBI_VALE3OS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 5, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_ADD_TLBI_NXS, + .writefn = tlbi_aa64_vae3is_write }, +}; + +static void tlbi_aa64_paall_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = env_cpu(env); + + tlb_flush(cs); +} + +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 tlbi_rme_reginfo[] = { + { .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 }, +}; + +void define_tlb_insn_regs(ARMCPU *cpu) +{ + CPUARMState *env = &cpu->env; + + if (!arm_feature(env, ARM_FEATURE_V7)) { + define_arm_cp_regs(cpu, tlbi_not_v7_cp_reginfo); + } else { + define_arm_cp_regs(cpu, tlbi_v7_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_V7MP) && + !arm_feature(env, ARM_FEATURE_PMSA)) { + define_arm_cp_regs(cpu, tlbi_v7mp_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_V8)) { + define_arm_cp_regs(cpu, tlbi_v8_cp_reginfo); + } + /* + * We retain the existing logic for when to register these TLBI + * ops (i.e. matching the condition for el2_cp_reginfo[] in + * helper.c), but we will be able to simplify this later. + */ + if (arm_feature(env, ARM_FEATURE_EL2)) { + define_arm_cp_regs(cpu, tlbi_el2_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_EL3)) { + define_arm_cp_regs(cpu, tlbi_el3_cp_reginfo); + } + if (cpu_isar_feature(aa64_tlbirange, cpu)) { + define_arm_cp_regs(cpu, tlbirange_reginfo); + } + if (cpu_isar_feature(aa64_tlbios, cpu)) { + define_arm_cp_regs(cpu, tlbios_reginfo); + } + if (cpu_isar_feature(aa64_rme, cpu)) { + define_arm_cp_regs(cpu, tlbi_rme_reginfo); + } +} diff --git a/target/arm/tcg/tlb_helper.c b/target/arm/tcg/tlb_helper.c index b22b2a4c6e7..23c72a99f5c 100644 --- a/target/arm/tcg/tlb_helper.c +++ b/target/arm/tcg/tlb_helper.c @@ -8,9 +8,10 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" -#include "exec/exec-all.h" -#include "exec/helper-proto.h" +#include "cpu-features.h" +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" /* * Returns true if the stage 1 translation regime is using LPAE format page @@ -49,7 +50,15 @@ static inline uint32_t merge_syn_data_abort(uint32_t template_syn, * ST64BV, or ST64BV0 insns report syndrome info even for stage-1 * faults and regardless of the target EL. */ - if (!(template_syn & ARM_EL_ISV) || target_el != 2 + 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); @@ -168,6 +177,20 @@ void arm_deliver_fault(ARMCPU *cpu, vaddr addr, 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; @@ -176,7 +199,8 @@ void arm_deliver_fault(ARMCPU *cpu, vaddr addr, syn = syn_gpc(fi->stage2 && fi->type == ARMFault_GPCFOnWalk, access_type == MMU_INST_FETCH, - encode_gpcsc(fi), 0, fi->s1ptw, + encode_gpcsc(fi), is_vncr, + 0, fi->s1ptw, access_type == MMU_DATA_STORE, fsc); env->cp15.mfar_el3 = fi->paddr; @@ -253,11 +277,11 @@ void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, arm_deliver_fault(cpu, vaddr, access_type, mmu_idx, &fi); } -void helper_exception_pc_alignment(CPUARMState *env, target_ulong pc) +void helper_exception_pc_alignment(CPUARMState *env, vaddr pc) { ARMMMUFaultInfo fi = { .type = ARMFault_Alignment }; int target_el = exception_target_el(env); - int mmu_idx = cpu_mmu_index(env, true); + int mmu_idx = arm_env_mmu_index(env); uint32_t fsc; env->exception.vaddress = pc; @@ -294,14 +318,13 @@ void arm_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, 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) +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; - int ret; /* * Allow S1_ptw_translate to see any fault generated here. @@ -315,37 +338,27 @@ bool arm_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } /* - * 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. + * Per R_XCHFJ, alignment fault not due to memory type has + * highest precedence. Otherwise, walk the page table and + * and collect the page description. */ - 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); + 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; - } else if (probe) { + } + 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); } + + /* 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, diff --git a/target/arm/tcg/translate-a32.h b/target/arm/tcg/translate-a32.h index 48a15379d22..0b1fa57965c 100644 --- a/target/arm/tcg/translate-a32.h +++ b/target/arm/tcg/translate-a32.h @@ -55,7 +55,7 @@ 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; } @@ -83,6 +83,13 @@ void store_cpu_offset(TCGv_i32 var, int offset, int size); 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 index 1b6fbb61e2b..ac80f572a2d 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -17,11 +17,10 @@ * License along with this library; if not, see . */ #include "qemu/osdep.h" - +#include "exec/target_page.h" #include "translate.h" #include "translate-a64.h" #include "qemu/log.h" -#include "disas/disas.h" #include "arm_ldst.h" #include "semihosting/semihost.h" #include "cpregs.h" @@ -75,39 +74,36 @@ static int scale_by_log2_tag_granule(DisasContext *s, int x) #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(cpu_env, + cpu_pc = tcg_global_mem_new_i64(tcg_env, offsetof(CPUARMState, pc), "pc"); for (i = 0; i < 32; i++) { - cpu_X[i] = tcg_global_mem_new_i64(cpu_env, + cpu_X[i] = tcg_global_mem_new_i64(tcg_env, offsetof(CPUARMState, xregs[i]), regnames[i]); } - cpu_exclusive_high = tcg_global_mem_new_i64(cpu_env, + cpu_exclusive_high = tcg_global_mem_new_i64(tcg_env, offsetof(CPUARMState, exclusive_high), "exclusive_high"); } /* - * Return the core mmu_idx to use for A64 "unprivileged load/store" insns + * Return the core mmu_idx to use for A64 load/store insns which + * have a "unprivileged load/store" variant. Those insns access + * EL0 if executed from an EL which has control over EL0 (usually + * EL1) but behave like normal loads and stores if executed from + * elsewhere (eg EL3). + * + * @unpriv : true for the unprivileged encoding; false for the + * normal encoding (in which case we will return the same + * thing as get_mem_index(). */ -static int get_a64_user_mem_index(DisasContext *s) +static int get_a64_user_mem_index(DisasContext *s, bool unpriv) { /* * If AccType_UNPRIV is not used, the insn uses AccType_NORMAL, @@ -115,7 +111,7 @@ static int get_a64_user_mem_index(DisasContext *s) */ ARMMMUIdx useridx = s->mmu_idx; - if (s->unpriv) { + if (unpriv && s->unpriv) { /* * We have pre-computed the condition for AccType_UNPRIV. * Therefore we should never get here with a mmu_idx for @@ -139,7 +135,7 @@ static int get_a64_user_mem_index(DisasContext *s) static void set_btype_raw(int val) { - tcg_gen_st_i32(tcg_constant_i32(val), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(val), tcg_env, offsetof(CPUARMState, btype)); } @@ -261,7 +257,7 @@ static void gen_address_with_allocation_tag0(TCGv_i64 dst, TCGv_i64 src) static void gen_probe_access(DisasContext *s, TCGv_i64 ptr, MMUAccessType acc, int log2_size) { - gen_helper_probe_access(cpu_env, ptr, + gen_helper_probe_access(tcg_env, ptr, tcg_constant_i32(acc), tcg_constant_i32(get_mem_index(s)), tcg_constant_i32(1 << log2_size)); @@ -286,11 +282,11 @@ static TCGv_i64 gen_mte_check1_mmuidx(DisasContext *s, TCGv_i64 addr, desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); - desc = FIELD_DP32(desc, MTEDESC, ALIGN, get_alignment_bits(memop)); + 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, cpu_env, tcg_constant_i32(desc), addr); + gen_helper_mte_check(ret, tcg_env, tcg_constant_i32(desc), addr); return ret; } @@ -318,11 +314,11 @@ TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write, desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); - desc = FIELD_DP32(desc, MTEDESC, ALIGN, get_alignment_bits(single_mop)); + 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, cpu_env, tcg_constant_i32(desc), addr); + gen_helper_mte_check(ret, tcg_env, tcg_constant_i32(desc), addr); return ret; } @@ -358,7 +354,7 @@ static void check_lse2_align(DisasContext *s, int rn, int imm, type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD, mmu_idx = get_mem_index(s); - gen_helper_unaligned_access(cpu_env, addr, tcg_constant_i32(type), + gen_helper_unaligned_access(tcg_env, addr, tcg_constant_i32(type), tcg_constant_i32(mmu_idx)); gen_set_label(over_label); @@ -434,13 +430,13 @@ static void a64_test_cc(DisasCompare64 *c64, int cc) static void gen_rebuild_hflags(DisasContext *s) { - gen_helper_rebuild_hflags_a64(cpu_env, tcg_constant_i32(s->current_el)); + gen_helper_rebuild_hflags_a64(tcg_env, tcg_constant_i32(s->current_el)); } static void gen_exception_internal(int excp) { assert(excp_is_internal(excp)); - gen_helper_exception_internal(cpu_env, tcg_constant_i32(excp)); + gen_helper_exception_internal(tcg_env, tcg_constant_i32(excp)); } static void gen_exception_internal_insn(DisasContext *s, int excp) @@ -453,7 +449,7 @@ static void gen_exception_internal_insn(DisasContext *s, int excp) 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)); + gen_helper_exception_bkpt_insn(tcg_env, tcg_constant_i32(syndrome)); s->base.is_jmp = DISAS_NORETURN; } @@ -600,7 +596,7 @@ static TCGv_i64 read_fp_dreg(DisasContext *s, int reg) { TCGv_i64 v = tcg_temp_new_i64(); - tcg_gen_ld_i64(v, cpu_env, fp_reg_offset(s, reg, MO_64)); + tcg_gen_ld_i64(v, tcg_env, fp_reg_offset(s, reg, MO_64)); return v; } @@ -608,7 +604,7 @@ static TCGv_i32 read_fp_sreg(DisasContext *s, int reg) { TCGv_i32 v = tcg_temp_new_i32(); - tcg_gen_ld_i32(v, cpu_env, fp_reg_offset(s, reg, MO_32)); + tcg_gen_ld_i32(v, tcg_env, fp_reg_offset(s, reg, MO_32)); return v; } @@ -616,11 +612,20 @@ static TCGv_i32 read_fp_hreg(DisasContext *s, int reg) { TCGv_i32 v = tcg_temp_new_i32(); - tcg_gen_ld16u_i32(v, cpu_env, fp_reg_offset(s, reg, MO_16)); + tcg_gen_ld16u_i32(v, tcg_env, fp_reg_offset(s, reg, MO_16)); return v; } -/* Clear the bits above an N-bit vector, for N = (is_q ? 128 : 64). +static void clear_vec(DisasContext *s, int rd) +{ + unsigned ofs = fp_reg_offset(s, rd, MO_64); + unsigned vsz = vec_full_reg_size(s); + + tcg_gen_gvec_dup_imm(MO_64, ofs, vsz, vsz, 0); +} + +/* + * 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) @@ -636,7 +641,7 @@ void write_fp_dreg(DisasContext *s, int reg, TCGv_i64 v) { unsigned ofs = fp_reg_offset(s, reg, MO_64); - tcg_gen_st_i64(v, cpu_env, ofs); + tcg_gen_st_i64(v, tcg_env, ofs); clear_vec_high(s, false, reg); } @@ -648,6 +653,68 @@ static void write_fp_sreg(DisasContext *s, int reg, TCGv_i32 v) write_fp_dreg(s, reg, tmp); } +/* + * Write a double result to 128 bit vector register reg, honouring FPCR.NEP: + * - if FPCR.NEP == 0, clear the high elements of reg + * - if FPCR.NEP == 1, set the high elements of reg from mergereg + * (i.e. merge the result with those high elements) + * In either case, SVE register bits above 128 are zeroed (per R_WKYLB). + */ +static void write_fp_dreg_merging(DisasContext *s, int reg, int mergereg, + TCGv_i64 v) +{ + if (!s->fpcr_nep) { + write_fp_dreg(s, reg, v); + return; + } + + /* + * Move from mergereg to reg; this sets the high elements and + * clears the bits above 128 as a side effect. + */ + tcg_gen_gvec_mov(MO_64, vec_full_reg_offset(s, reg), + vec_full_reg_offset(s, mergereg), + 16, vec_full_reg_size(s)); + tcg_gen_st_i64(v, tcg_env, vec_full_reg_offset(s, reg)); +} + +/* + * Write a single-prec result, but only clear the higher elements + * of the destination register if FPCR.NEP is 0; otherwise preserve them. + */ +static void write_fp_sreg_merging(DisasContext *s, int reg, int mergereg, + TCGv_i32 v) +{ + if (!s->fpcr_nep) { + write_fp_sreg(s, reg, v); + return; + } + + tcg_gen_gvec_mov(MO_64, vec_full_reg_offset(s, reg), + vec_full_reg_offset(s, mergereg), + 16, vec_full_reg_size(s)); + tcg_gen_st_i32(v, tcg_env, fp_reg_offset(s, reg, MO_32)); +} + +/* + * Write a half-prec result, but only clear the higher elements + * of the destination register if FPCR.NEP is 0; otherwise preserve them. + * The caller must ensure that the top 16 bits of v are zero. + */ +static void write_fp_hreg_merging(DisasContext *s, int reg, int mergereg, + TCGv_i32 v) +{ + if (!s->fpcr_nep) { + write_fp_sreg(s, reg, v); + return; + } + + tcg_gen_gvec_mov(MO_64, vec_full_reg_offset(s, reg), + vec_full_reg_offset(s, mergereg), + 16, vec_full_reg_size(s)); + tcg_gen_st16_i32(v, tcg_env, fp_reg_offset(s, reg, MO_16)); +} + /* 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) @@ -706,29 +773,16 @@ static void gen_gvec_op3_ool(DisasContext *s, bool is_q, int rd, * 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, + int rm, ARMFPStatusFlavour fpsttype, int data, gen_helper_gvec_3_ptr *fn) { - TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR); + TCGv_ptr fpst = fpstatus_ptr(fpsttype); 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 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); -} - /* 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) @@ -740,15 +794,32 @@ static void gen_gvec_op4_ool(DisasContext *s, bool is_q, int rd, int rn, 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, + int rm, int ra, ARMFPStatusFlavour fpsttype, + int data, gen_helper_gvec_4_ptr *fn) { - TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR); + TCGv_ptr fpst = fpstatus_ptr(fpsttype); tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), vec_full_reg_offset(s, rm), @@ -756,6 +827,111 @@ static void gen_gvec_op4_fpst(DisasContext *s, bool is_q, int rd, int rn, is_q ? 16 : 8, vec_full_reg_size(s), data, fn); } +/* + * When FPCR.AH == 1, NEG and ABS do not flip the sign bit of a NaN. + * These functions implement + * d = floatN_is_any_nan(s) ? s : floatN_chs(s) + * which for float32 is + * d = (s & ~(1 << 31)) > 0x7f800000UL) ? s : (s ^ (1 << 31)) + * and similarly for the other float sizes. + */ +static void gen_vfp_ah_negh(TCGv_i32 d, TCGv_i32 s) +{ + TCGv_i32 abs_s = tcg_temp_new_i32(), chs_s = tcg_temp_new_i32(); + + gen_vfp_negh(chs_s, s); + gen_vfp_absh(abs_s, s); + tcg_gen_movcond_i32(TCG_COND_GTU, d, + abs_s, tcg_constant_i32(0x7c00), + s, chs_s); +} + +static void gen_vfp_ah_negs(TCGv_i32 d, TCGv_i32 s) +{ + TCGv_i32 abs_s = tcg_temp_new_i32(), chs_s = tcg_temp_new_i32(); + + gen_vfp_negs(chs_s, s); + gen_vfp_abss(abs_s, s); + tcg_gen_movcond_i32(TCG_COND_GTU, d, + abs_s, tcg_constant_i32(0x7f800000UL), + s, chs_s); +} + +static void gen_vfp_ah_negd(TCGv_i64 d, TCGv_i64 s) +{ + TCGv_i64 abs_s = tcg_temp_new_i64(), chs_s = tcg_temp_new_i64(); + + gen_vfp_negd(chs_s, s); + gen_vfp_absd(abs_s, s); + tcg_gen_movcond_i64(TCG_COND_GTU, d, + abs_s, tcg_constant_i64(0x7ff0000000000000ULL), + s, chs_s); +} + +/* + * These functions implement + * d = floatN_is_any_nan(s) ? s : floatN_abs(s) + * which for float32 is + * d = (s & ~(1 << 31)) > 0x7f800000UL) ? s : (s & ~(1 << 31)) + * and similarly for the other float sizes. + */ +static void gen_vfp_ah_absh(TCGv_i32 d, TCGv_i32 s) +{ + TCGv_i32 abs_s = tcg_temp_new_i32(); + + gen_vfp_absh(abs_s, s); + tcg_gen_movcond_i32(TCG_COND_GTU, d, + abs_s, tcg_constant_i32(0x7c00), + s, abs_s); +} + +static void gen_vfp_ah_abss(TCGv_i32 d, TCGv_i32 s) +{ + TCGv_i32 abs_s = tcg_temp_new_i32(); + + gen_vfp_abss(abs_s, s); + tcg_gen_movcond_i32(TCG_COND_GTU, d, + abs_s, tcg_constant_i32(0x7f800000UL), + s, abs_s); +} + +static void gen_vfp_ah_absd(TCGv_i64 d, TCGv_i64 s) +{ + TCGv_i64 abs_s = tcg_temp_new_i64(); + + gen_vfp_absd(abs_s, s); + tcg_gen_movcond_i64(TCG_COND_GTU, d, + abs_s, tcg_constant_i64(0x7ff0000000000000ULL), + s, abs_s); +} + +static void gen_vfp_maybe_ah_negh(DisasContext *dc, TCGv_i32 d, TCGv_i32 s) +{ + if (dc->fpcr_ah) { + gen_vfp_ah_negh(d, s); + } else { + gen_vfp_negh(d, s); + } +} + +static void gen_vfp_maybe_ah_negs(DisasContext *dc, TCGv_i32 d, TCGv_i32 s) +{ + if (dc->fpcr_ah) { + gen_vfp_ah_negs(d, s); + } else { + gen_vfp_negs(d, s); + } +} + +static void gen_vfp_maybe_ah_negd(DisasContext *dc, TCGv_i64 d, TCGv_i64 s) +{ + if (dc->fpcr_ah) { + gen_vfp_ah_negd(d, s); + } else { + gen_vfp_negd(d, s); + } +} + /* Set ZF and NF based on a 64 bit result. This is alas fiddlier * than the 32 bit equivalent. */ @@ -899,11 +1075,9 @@ static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) 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_addcio_i64(result, cf_64, t0, t1, cf_64); tcg_gen_extrl_i64_i32(cpu_CF, cf_64); gen_set_NZ64(result); @@ -917,12 +1091,10 @@ static void gen_adc_CC(int sf, 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(); - 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_addcio_i32(cpu_NF, cpu_CF, t0_32, t1_32, cpu_CF); tcg_gen_mov_i32(cpu_ZF, cpu_NF); tcg_gen_xor_i32(cpu_VF, cpu_NF, t0_32); @@ -1017,7 +1189,7 @@ 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, cpu_env, fp_reg_offset(s, srcidx, MO_64)); + 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); @@ -1025,7 +1197,7 @@ static void do_fp_st(DisasContext *s, int srcidx, TCGv_i64 tcg_addr, MemOp mop) TCGv_i64 tmphi = tcg_temp_new_i64(); TCGv_i128 t16 = tcg_temp_new_i128(); - tcg_gen_ld_i64(tmphi, cpu_env, fp_reg_hi_offset(s, srcidx)); + tcg_gen_ld_i64(tmphi, tcg_env, fp_reg_hi_offset(s, srcidx)); tcg_gen_concat_i64_i128(t16, tmplo, tmphi); tcg_gen_qemu_st_i128(t16, tcg_addr, get_mem_index(s), mop); @@ -1052,10 +1224,10 @@ static void do_fp_ld(DisasContext *s, int destidx, TCGv_i64 tcg_addr, MemOp mop) tcg_gen_extr_i128_i64(tmplo, tmphi, t16); } - tcg_gen_st_i64(tmplo, cpu_env, fp_reg_offset(s, destidx, MO_64)); + tcg_gen_st_i64(tmplo, tcg_env, fp_reg_offset(s, destidx, MO_64)); if (tmphi) { - tcg_gen_st_i64(tmphi, cpu_env, fp_reg_hi_offset(s, destidx)); + tcg_gen_st_i64(tmphi, tcg_env, fp_reg_hi_offset(s, destidx)); } clear_vec_high(s, tmphi != NULL, destidx); } @@ -1079,26 +1251,26 @@ static void read_vec_element(DisasContext *s, TCGv_i64 tcg_dest, int srcidx, int vect_off = vec_reg_offset(s, srcidx, element, memop & MO_SIZE); switch ((unsigned)memop) { case MO_8: - tcg_gen_ld8u_i64(tcg_dest, cpu_env, vect_off); + tcg_gen_ld8u_i64(tcg_dest, tcg_env, vect_off); break; case MO_16: - tcg_gen_ld16u_i64(tcg_dest, cpu_env, vect_off); + tcg_gen_ld16u_i64(tcg_dest, tcg_env, vect_off); break; case MO_32: - tcg_gen_ld32u_i64(tcg_dest, cpu_env, vect_off); + tcg_gen_ld32u_i64(tcg_dest, tcg_env, vect_off); break; case MO_8|MO_SIGN: - tcg_gen_ld8s_i64(tcg_dest, cpu_env, vect_off); + tcg_gen_ld8s_i64(tcg_dest, tcg_env, vect_off); break; case MO_16|MO_SIGN: - tcg_gen_ld16s_i64(tcg_dest, cpu_env, vect_off); + tcg_gen_ld16s_i64(tcg_dest, tcg_env, vect_off); break; case MO_32|MO_SIGN: - tcg_gen_ld32s_i64(tcg_dest, cpu_env, vect_off); + tcg_gen_ld32s_i64(tcg_dest, tcg_env, vect_off); break; case MO_64: case MO_64|MO_SIGN: - tcg_gen_ld_i64(tcg_dest, cpu_env, vect_off); + tcg_gen_ld_i64(tcg_dest, tcg_env, vect_off); break; default: g_assert_not_reached(); @@ -1111,20 +1283,20 @@ static void read_vec_element_i32(DisasContext *s, TCGv_i32 tcg_dest, int srcidx, int vect_off = vec_reg_offset(s, srcidx, element, memop & MO_SIZE); switch (memop) { case MO_8: - tcg_gen_ld8u_i32(tcg_dest, cpu_env, vect_off); + tcg_gen_ld8u_i32(tcg_dest, tcg_env, vect_off); break; case MO_16: - tcg_gen_ld16u_i32(tcg_dest, cpu_env, vect_off); + tcg_gen_ld16u_i32(tcg_dest, tcg_env, vect_off); break; case MO_8|MO_SIGN: - tcg_gen_ld8s_i32(tcg_dest, cpu_env, vect_off); + tcg_gen_ld8s_i32(tcg_dest, tcg_env, vect_off); break; case MO_16|MO_SIGN: - tcg_gen_ld16s_i32(tcg_dest, cpu_env, vect_off); + tcg_gen_ld16s_i32(tcg_dest, tcg_env, vect_off); break; case MO_32: case MO_32|MO_SIGN: - tcg_gen_ld_i32(tcg_dest, cpu_env, vect_off); + tcg_gen_ld_i32(tcg_dest, tcg_env, vect_off); break; default: g_assert_not_reached(); @@ -1138,16 +1310,16 @@ static void write_vec_element(DisasContext *s, TCGv_i64 tcg_src, int destidx, int vect_off = vec_reg_offset(s, destidx, element, memop & MO_SIZE); switch (memop) { case MO_8: - tcg_gen_st8_i64(tcg_src, cpu_env, vect_off); + tcg_gen_st8_i64(tcg_src, tcg_env, vect_off); break; case MO_16: - tcg_gen_st16_i64(tcg_src, cpu_env, vect_off); + tcg_gen_st16_i64(tcg_src, tcg_env, vect_off); break; case MO_32: - tcg_gen_st32_i64(tcg_src, cpu_env, vect_off); + tcg_gen_st32_i64(tcg_src, tcg_env, vect_off); break; case MO_64: - tcg_gen_st_i64(tcg_src, cpu_env, vect_off); + tcg_gen_st_i64(tcg_src, tcg_env, vect_off); break; default: g_assert_not_reached(); @@ -1160,13 +1332,13 @@ static void write_vec_element_i32(DisasContext *s, TCGv_i32 tcg_src, int vect_off = vec_reg_offset(s, destidx, element, memop & MO_SIZE); switch (memop) { case MO_8: - tcg_gen_st8_i32(tcg_src, cpu_env, vect_off); + tcg_gen_st8_i32(tcg_src, tcg_env, vect_off); break; case MO_16: - tcg_gen_st16_i32(tcg_src, cpu_env, vect_off); + tcg_gen_st16_i32(tcg_src, tcg_env, vect_off); break; case MO_32: - tcg_gen_st_i32(tcg_src, cpu_env, vect_off); + tcg_gen_st_i32(tcg_src, tcg_env, vect_off); break; default: g_assert_not_reached(); @@ -1204,14 +1376,14 @@ static bool fp_access_check_only(DisasContext *s) { if (s->fp_excp_el) { assert(!s->fp_access_checked); - s->fp_access_checked = true; + s->fp_access_checked = -1; 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; + s->fp_access_checked = 1; return true; } @@ -1228,6 +1400,49 @@ static bool fp_access_check(DisasContext *s) return true; } +/* + * Return <0 for non-supported element sizes, with MO_16 controlled by + * FEAT_FP16; return 0 for fp disabled; otherwise return >0 for success. + */ +static int fp_access_check_scalar_hsd(DisasContext *s, MemOp esz) +{ + switch (esz) { + case MO_64: + case MO_32: + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return -1; + } + break; + default: + return -1; + } + return fp_access_check(s); +} + +/* Likewise, but vector MO_64 must have two elements. */ +static int fp_access_check_vector_hsd(DisasContext *s, bool is_q, MemOp esz) +{ + switch (esz) { + case MO_64: + if (!is_q) { + return -1; + } + break; + case MO_32: + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return -1; + } + break; + default: + return -1; + } + return fp_access_check(s); +} + /* * Check that SVE access is enabled. If it is, return true. * If not, emit code to generate an appropriate exception and return false. @@ -1236,23 +1451,23 @@ static bool fp_access_check(DisasContext *s) bool sve_access_check(DisasContext *s) { if (s->pstate_sm || !dc_isar_feature(aa64_sve, s)) { + bool ret; + assert(dc_isar_feature(aa64_sme, s)); - if (!sme_sm_enabled_check(s)) { - goto fail_exit; - } - } else if (s->sve_excp_el) { + ret = sme_sm_enabled_check(s); + s->sve_access_checked = (ret ? 1 : -1); + return ret; + } + if (s->sve_excp_el) { + /* Assert that we only raise one exception per instruction. */ + assert(!s->sve_access_checked); gen_exception_insn_el(s, 0, EXCP_UDEF, syn_sve_access_trap(), s->sve_excp_el); - goto fail_exit; + s->sve_access_checked = -1; + return false; } - s->sve_access_checked = true; + s->sve_access_checked = 1; 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; } /* @@ -1280,8 +1495,9 @@ bool sme_enabled_check(DisasContext *s) * 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); + bool ret = sme_access_check(s); + s->fp_access_checked = (ret ? 1 : -1); + return ret; } return fp_access_check_only(s); } @@ -1305,6 +1521,75 @@ bool sme_enabled_check_with_svcr(DisasContext *s, unsigned req) 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 @@ -1316,41 +1601,8 @@ static void ext_and_shift_reg(TCGv_i64 tcg_out, TCGv_i64 tcg_in, int extsize = extract32(option, 0, 2); bool is_signed = extract32(option, 2, 1); - if (is_signed) { - switch (extsize) { - case 0: - tcg_gen_ext8s_i64(tcg_out, tcg_in); - break; - case 1: - tcg_gen_ext16s_i64(tcg_out, tcg_in); - break; - case 2: - tcg_gen_ext32s_i64(tcg_out, tcg_in); - break; - case 3: - tcg_gen_mov_i64(tcg_out, tcg_in); - break; - } - } else { - switch (extsize) { - case 0: - tcg_gen_ext8u_i64(tcg_out, tcg_in); - break; - case 1: - tcg_gen_ext16u_i64(tcg_out, tcg_in); - break; - case 2: - tcg_gen_ext32u_i64(tcg_out, tcg_in); - break; - case 3: - tcg_gen_mov_i64(tcg_out, tcg_in); - break; - } - } - - if (shift) { - tcg_gen_shli_i64(tcg_out, tcg_out, shift); - } + tcg_gen_ext_i64(tcg_out, tcg_in, extsize | (is_signed ? MO_SIGN : 0)); + tcg_gen_shli_i64(tcg_out, tcg_out, shift); } static inline void gen_check_sp_alignment(DisasContext *s) @@ -1366,31 +1618,6 @@ static inline void gen_check_sp_alignment(DisasContext *s) */ } -/* - * 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 @@ -1453,6 +1680,10 @@ static bool trans_TBZ(DisasContext *s, arg_tbz *a) 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 */ @@ -1472,7 +1703,14 @@ 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. */ - set_btype(s, rn == 16 || rn == 17 || !s->guarded_page ? 1 : 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; + } } } @@ -1486,8 +1724,8 @@ static void set_btype_for_blr(DisasContext *s) static bool trans_BR(DisasContext *s, arg_r *a) { - gen_a64_set_pc(s, cpu_reg(s, a->rn)); set_btype_for_br(s, a->rn); + gen_a64_set_pc(s, cpu_reg(s, a->rn)); s->base.is_jmp = DISAS_JUMP; return true; } @@ -1530,9 +1768,9 @@ static TCGv_i64 auth_branch_target(DisasContext *s, TCGv_i64 dst, truedst = tcg_temp_new_i64(); if (use_key_a) { - gen_helper_autia_combined(truedst, cpu_env, dst, modifier); + gen_helper_autia_combined(truedst, tcg_env, dst, modifier); } else { - gen_helper_autib_combined(truedst, cpu_env, dst, modifier); + gen_helper_autib_combined(truedst, tcg_env, dst, modifier); } return truedst; } @@ -1546,8 +1784,8 @@ static bool trans_BRAZ(DisasContext *s, arg_braz *a) } dst = auth_branch_target(s, cpu_reg(s, a->rn), tcg_constant_i64(0), !a->m); - gen_a64_set_pc(s, dst); set_btype_for_br(s, a->rn); + gen_a64_set_pc(s, dst); s->base.is_jmp = DISAS_JUMP; return true; } @@ -1626,17 +1864,17 @@ static bool trans_ERET(DisasContext *s, arg_ERET *a) if (s->current_el == 0) { return false; } - if (s->fgt_eret) { - gen_exception_insn_el(s, 0, EXCP_UDEF, 0, 2); + 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, cpu_env, + tcg_gen_ld_i64(dst, tcg_env, offsetof(CPUARMState, elr_el[s->current_el])); translator_io_start(&s->base); - gen_helper_exception_return(cpu_env, dst); + gen_helper_exception_return(tcg_env, dst); /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; return true; @@ -1653,19 +1891,19 @@ static bool trans_ERETA(DisasContext *s, arg_reta *a) return false; } /* The FGT trap takes precedence over an auth trap. */ - if (s->fgt_eret) { - gen_exception_insn_el(s, 0, EXCP_UDEF, a->m ? 3 : 2, 2); + 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, cpu_env, + 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(cpu_env, dst); + gen_helper_exception_return(tcg_env, dst); /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; return true; @@ -1710,10 +1948,51 @@ static bool trans_WFE(DisasContext *s, arg_WFI *a) 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], cpu_env, cpu_X[30]); + gen_helper_xpaci(cpu_X[30], tcg_env, cpu_X[30]); } return true; } @@ -1721,7 +2000,7 @@ static bool trans_XPACLRI(DisasContext *s, arg_XPACLRI *a) static bool trans_PACIA1716(DisasContext *s, arg_PACIA1716 *a) { if (s->pauth_active) { - gen_helper_pacia(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); + gen_helper_pacia(cpu_X[17], tcg_env, cpu_X[17], cpu_X[16]); } return true; } @@ -1729,7 +2008,7 @@ static bool trans_PACIA1716(DisasContext *s, arg_PACIA1716 *a) static bool trans_PACIB1716(DisasContext *s, arg_PACIB1716 *a) { if (s->pauth_active) { - gen_helper_pacib(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); + gen_helper_pacib(cpu_X[17], tcg_env, cpu_X[17], cpu_X[16]); } return true; } @@ -1737,7 +2016,7 @@ static bool trans_PACIB1716(DisasContext *s, arg_PACIB1716 *a) static bool trans_AUTIA1716(DisasContext *s, arg_AUTIA1716 *a) { if (s->pauth_active) { - gen_helper_autia(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); + gen_helper_autia(cpu_X[17], tcg_env, cpu_X[17], cpu_X[16]); } return true; } @@ -1745,7 +2024,7 @@ static bool trans_AUTIA1716(DisasContext *s, arg_AUTIA1716 *a) static bool trans_AUTIB1716(DisasContext *s, arg_AUTIB1716 *a) { if (s->pauth_active) { - gen_helper_autib(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); + gen_helper_autib(cpu_X[17], tcg_env, cpu_X[17], cpu_X[16]); } return true; } @@ -1764,7 +2043,7 @@ static bool trans_ESB(DisasContext *s, arg_ESB *a) * Test for EL2 present, and defer test for SEL2 to runtime. */ if (s->current_el <= 1 && arm_dc_feature(s, ARM_FEATURE_EL2)) { - gen_helper_vesb(cpu_env); + gen_helper_vesb(tcg_env); } } return true; @@ -1773,7 +2052,7 @@ static bool trans_ESB(DisasContext *s, arg_ESB *a) static bool trans_PACIAZ(DisasContext *s, arg_PACIAZ *a) { if (s->pauth_active) { - gen_helper_pacia(cpu_X[30], cpu_env, cpu_X[30], tcg_constant_i64(0)); + gen_helper_pacia(cpu_X[30], tcg_env, cpu_X[30], tcg_constant_i64(0)); } return true; } @@ -1781,7 +2060,7 @@ static bool trans_PACIAZ(DisasContext *s, arg_PACIAZ *a) static bool trans_PACIASP(DisasContext *s, arg_PACIASP *a) { if (s->pauth_active) { - gen_helper_pacia(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); + gen_helper_pacia(cpu_X[30], tcg_env, cpu_X[30], cpu_X[31]); } return true; } @@ -1789,7 +2068,7 @@ static bool trans_PACIASP(DisasContext *s, arg_PACIASP *a) static bool trans_PACIBZ(DisasContext *s, arg_PACIBZ *a) { if (s->pauth_active) { - gen_helper_pacib(cpu_X[30], cpu_env, cpu_X[30], tcg_constant_i64(0)); + gen_helper_pacib(cpu_X[30], tcg_env, cpu_X[30], tcg_constant_i64(0)); } return true; } @@ -1797,7 +2076,7 @@ static bool trans_PACIBZ(DisasContext *s, arg_PACIBZ *a) static bool trans_PACIBSP(DisasContext *s, arg_PACIBSP *a) { if (s->pauth_active) { - gen_helper_pacib(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); + gen_helper_pacib(cpu_X[30], tcg_env, cpu_X[30], cpu_X[31]); } return true; } @@ -1805,7 +2084,7 @@ static bool trans_PACIBSP(DisasContext *s, arg_PACIBSP *a) static bool trans_AUTIAZ(DisasContext *s, arg_AUTIAZ *a) { if (s->pauth_active) { - gen_helper_autia(cpu_X[30], cpu_env, cpu_X[30], tcg_constant_i64(0)); + gen_helper_autia(cpu_X[30], tcg_env, cpu_X[30], tcg_constant_i64(0)); } return true; } @@ -1813,7 +2092,7 @@ static bool trans_AUTIAZ(DisasContext *s, arg_AUTIAZ *a) static bool trans_AUTIASP(DisasContext *s, arg_AUTIASP *a) { if (s->pauth_active) { - gen_helper_autia(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); + gen_helper_autia(cpu_X[30], tcg_env, cpu_X[30], cpu_X[31]); } return true; } @@ -1821,7 +2100,7 @@ static bool trans_AUTIASP(DisasContext *s, arg_AUTIASP *a) static bool trans_AUTIBZ(DisasContext *s, arg_AUTIBZ *a) { if (s->pauth_active) { - gen_helper_autib(cpu_X[30], cpu_env, cpu_X[30], tcg_constant_i64(0)); + gen_helper_autib(cpu_X[30], tcg_env, cpu_X[30], tcg_constant_i64(0)); } return true; } @@ -1829,7 +2108,7 @@ static bool trans_AUTIBZ(DisasContext *s, arg_AUTIBZ *a) static bool trans_AUTIBSP(DisasContext *s, arg_AUTIBSP *a) { if (s->pauth_active) { - gen_helper_autib(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); + gen_helper_autib(cpu_X[30], tcg_env, cpu_X[30], cpu_X[31]); } return true; } @@ -1860,6 +2139,15 @@ static bool trans_DSB_DMB(DisasContext *s, arg_DSB_DMB *a) return true; } +static bool trans_DSB_nXS(DisasContext *s, arg_DSB_nXS *a) +{ + if (!dc_isar_feature(aa64_xs, s)) { + return false; + } + tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); + return true; +} + static bool trans_ISB(DisasContext *s, arg_ISB *a) { /* @@ -1984,7 +2272,7 @@ static bool trans_MSR_i_SPSEL(DisasContext *s, arg_i *a) if (s->current_el == 0) { return false; } - gen_helper_msr_i_spsel(cpu_env, tcg_constant_i32(a->imm & PSTATE_SP)); + gen_helper_msr_i_spsel(tcg_env, tcg_constant_i32(a->imm & PSTATE_SP)); s->base.is_jmp = DISAS_TOO_MANY; return true; } @@ -2043,31 +2331,50 @@ static bool trans_MSR_i_TCO(DisasContext *s, arg_i *a) static bool trans_MSR_i_DAIFSET(DisasContext *s, arg_i *a) { - gen_helper_msr_i_daifset(cpu_env, tcg_constant_i32(a->imm)); + 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(cpu_env, tcg_constant_i32(a->imm)); + gen_helper_msr_i_daifclear(tcg_env, tcg_constant_i32(a->imm)); /* Exit the cpu loop to re-evaluate pending IRQs. */ s->base.is_jmp = DISAS_UPDATE_EXIT; return true; } -static bool trans_MSR_i_SVCR(DisasContext *s, arg_MSR_i_SVCR *a) +static bool trans_MSR_i_ALLINT(DisasContext *s, arg_i *a) { - if (!dc_isar_feature(aa64_sme, s) || a->mask == 0) { + if (!dc_isar_feature(aa64_nmi, s) || s->current_el == 0) { return false; } - if (sme_access_check(s)) { - int old = s->pstate_sm | (s->pstate_za << 1); + + 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(cpu_env, tcg_constant_i32(new), + gen_helper_set_svcr(tcg_env, tcg_constant_i32(new), tcg_constant_i32(a->mask)); s->base.is_jmp = DISAS_TOO_MANY; } @@ -2152,24 +2459,27 @@ static void handle_sys(DisasContext *s, bool isread, 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; + 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. */ - syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread); switch (s->current_el) { case 0: if (dc_isar_feature(aa64_tidcp1, s)) { - gen_helper_tidcp_el0(cpu_env, tcg_constant_i32(syndrome)); + gen_helper_tidcp_el0(tcg_env, tcg_constant_i32(syndrome)); } break; case 1: - gen_helper_tidcp_el1(cpu_env, tcg_constant_i32(syndrome)); + gen_helper_tidcp_el1(tcg_env, tcg_constant_i32(syndrome)); break; } } @@ -2185,20 +2495,68 @@ static void handle_sys(DisasContext *s, bool isread, 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)) { - gen_sysreg_undef(s, isread, op0, op1, op2, crn, crm, rt); - return; + /* + * 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. */ - syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread); gen_a64_update_pc(s, 0); tcg_ri = tcg_temp_new_ptr(); - gen_helper_access_check_cp_reg(tcg_ri, cpu_env, + gen_helper_access_check_cp_reg(tcg_ri, tcg_env, tcg_constant_i32(key), tcg_constant_i32(syndrome), tcg_constant_i32(isread)); @@ -2210,6 +2568,78 @@ static void handle_sys(DisasContext *s, bool isread, 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: @@ -2225,12 +2655,17 @@ static void handle_sys(DisasContext *s, bool isread, } return; case ARM_CP_CURRENTEL: - /* Reads as current EL value from pstate, which is + { + /* + * Reads as current EL value from pstate, which is * guaranteed to be constant by the tb flags. + * For nested virt we should report EL2. */ + int el = s->nv ? 2 : s->current_el; tcg_rt = cpu_reg(s, rt); - tcg_gen_movi_i64(tcg_rt, s->current_el << 2); + tcg_gen_movi_i64(tcg_rt, el << 2); return; + } case ARM_CP_DC_ZVA: /* Writes clear the aligned block of memory which rt points into. */ if (s->mte_active[0]) { @@ -2241,12 +2676,12 @@ static void handle_sys(DisasContext *s, bool isread, desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); tcg_rt = tcg_temp_new_i64(); - gen_helper_mte_check_zva(tcg_rt, cpu_env, + gen_helper_mte_check_zva(tcg_rt, tcg_env, tcg_constant_i32(desc), cpu_reg(s, rt)); } else { tcg_rt = clean_data_tbi(s, cpu_reg(s, rt)); } - gen_helper_dc_zva(cpu_env, tcg_rt); + gen_helper_dc_zva(tcg_env, tcg_rt); return; case ARM_CP_DC_GVA: { @@ -2260,11 +2695,11 @@ static void handle_sys(DisasContext *s, bool isread, clean_addr = clean_data_tbi(s, tcg_rt); gen_probe_access(s, clean_addr, MMU_DATA_STORE, MO_8); - if (s->ata) { + if (s->ata[0]) { /* Extract the tag from the register to match STZGM. */ tag = tcg_temp_new_i64(); tcg_gen_shri_i64(tag, tcg_rt, 56); - gen_helper_stzgm_tags(cpu_env, clean_addr, tag); + gen_helper_stzgm_tags(tcg_env, clean_addr, tag); } } return; @@ -2275,26 +2710,19 @@ static void handle_sys(DisasContext *s, bool isread, /* For DC_GZVA, we can rely on DC_ZVA for the proper fault. */ tcg_rt = cpu_reg(s, rt); clean_addr = clean_data_tbi(s, tcg_rt); - gen_helper_dc_zva(cpu_env, clean_addr); + gen_helper_dc_zva(tcg_env, clean_addr); - if (s->ata) { + if (s->ata[0]) { /* Extract the tag from the register to match STZGM. */ tag = tcg_temp_new_i64(); tcg_gen_shri_i64(tag, tcg_rt, 56); - gen_helper_stzgm_tags(cpu_env, clean_addr, tag); + gen_helper_stzgm_tags(tcg_env, clean_addr, 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 (ri->type & ARM_CP_IO) { /* I/O operations must end the TB here (whether read or write) */ @@ -2310,9 +2738,9 @@ static void handle_sys(DisasContext *s, bool isread, if (!tcg_ri) { tcg_ri = gen_lookup_cp_reg(key); } - gen_helper_get_cp_reg64(tcg_rt, cpu_env, tcg_ri); + gen_helper_get_cp_reg64(tcg_rt, tcg_env, tcg_ri); } else { - tcg_gen_ld_i64(tcg_rt, cpu_env, ri->fieldoffset); + tcg_gen_ld_i64(tcg_rt, tcg_env, ri->fieldoffset); } } else { if (ri->type & ARM_CP_CONST) { @@ -2322,9 +2750,9 @@ static void handle_sys(DisasContext *s, bool isread, if (!tcg_ri) { tcg_ri = gen_lookup_cp_reg(key); } - gen_helper_set_cp_reg64(cpu_env, tcg_ri, tcg_rt); + gen_helper_set_cp_reg64(tcg_env, tcg_ri, tcg_rt); } else { - tcg_gen_st_i64(tcg_rt, cpu_env, ri->fieldoffset); + tcg_gen_st_i64(tcg_rt, tcg_env, ri->fieldoffset); } } @@ -2372,6 +2800,8 @@ static bool trans_SVC(DisasContext *s, arg_i *a) 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; @@ -2381,10 +2811,10 @@ static bool trans_HVC(DisasContext *s, arg_i *a) * as an undefined insn by runtime configuration. */ gen_a64_update_pc(s, 0); - gen_helper_pre_hvc(cpu_env); + 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), 2); + gen_exception_insn_el(s, 4, EXCP_HVC, syn_aa64_hvc(a->imm), target_el); return true; } @@ -2395,7 +2825,7 @@ static bool trans_SMC(DisasContext *s, arg_i *a) return true; } gen_a64_update_pc(s, 0); - gen_helper_pre_smc(cpu_env, tcg_constant_i32(syn_aa64_smc(a->imm))); + 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); @@ -3058,11 +3488,11 @@ static bool trans_STGP(DisasContext *s, arg_ldstpair *a) tcg_gen_qemu_st_i128(tmp, clean_addr, get_mem_index(s), mop); /* Perform the tag store, if tag access enabled. */ - if (s->ata) { + if (s->ata[0]) { if (tb_cflags(s->base.tb) & CF_PARALLEL) { - gen_helper_stg_parallel(cpu_env, dirty_addr, dirty_addr); + gen_helper_stg_parallel(tcg_env, dirty_addr, dirty_addr); } else { - gen_helper_stg(cpu_env, dirty_addr, dirty_addr); + gen_helper_stg(tcg_env, dirty_addr, dirty_addr); } } @@ -3084,7 +3514,7 @@ static void op_addr_ldst_imm_pre(DisasContext *s, arg_ldst_imm *a, if (!a->p) { tcg_gen_addi_i64(*dirty_addr, *dirty_addr, offset); } - memidx = a->unpriv ? get_a64_user_mem_index(s) : get_mem_index(s); + 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); @@ -3105,7 +3535,7 @@ 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 = a->unpriv ? get_a64_user_mem_index(s) : get_mem_index(s); + 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); @@ -3123,7 +3553,7 @@ 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 = a->unpriv ? get_a64_user_mem_index(s) : get_mem_index(s); + 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); @@ -3325,7 +3755,7 @@ static bool trans_LDAPR(DisasContext *s, arg_LDAPR *a) if (a->rn == 31) { gen_check_sp_alignment(s); } - mop = check_atomic_align(s, a->rn, a->sz); + 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); /* @@ -3358,10 +3788,10 @@ static bool trans_LDRA(DisasContext *s, arg_LDRA *a) if (s->pauth_active) { if (!a->m) { - gen_helper_autda_combined(dirty_addr, cpu_env, dirty_addr, + gen_helper_autda_combined(dirty_addr, tcg_env, dirty_addr, tcg_constant_i64(0)); } else { - gen_helper_autdb_combined(dirty_addr, cpu_env, dirty_addr, + gen_helper_autdb_combined(dirty_addr, tcg_env, dirty_addr, tcg_constant_i64(0)); } } @@ -3756,8 +4186,8 @@ static bool trans_STZGM(DisasContext *s, arg_ldst_tag *a) tcg_gen_addi_i64(addr, addr, a->imm); tcg_rt = cpu_reg(s, a->rt); - if (s->ata) { - gen_helper_stzgm_tags(cpu_env, addr, tcg_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, @@ -3765,7 +4195,7 @@ static bool trans_STZGM(DisasContext *s, arg_ldst_tag *a) */ clean_addr = clean_data_tbi(s, addr); tcg_gen_andi_i64(clean_addr, clean_addr, -size); - gen_helper_dc_zva(cpu_env, clean_addr); + gen_helper_dc_zva(tcg_env, clean_addr); return true; } @@ -3788,8 +4218,8 @@ static bool trans_STGM(DisasContext *s, arg_ldst_tag *a) tcg_gen_addi_i64(addr, addr, a->imm); tcg_rt = cpu_reg(s, a->rt); - if (s->ata) { - gen_helper_stgm(cpu_env, addr, tcg_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; @@ -3820,8 +4250,8 @@ static bool trans_LDGM(DisasContext *s, arg_ldst_tag *a) tcg_gen_addi_i64(addr, addr, a->imm); tcg_rt = cpu_reg(s, a->rt); - if (s->ata) { - gen_helper_ldgm(tcg_rt, cpu_env, addr); + if (s->ata[0]) { + gen_helper_ldgm(tcg_rt, tcg_env, addr); } else { MMUAccessType acc = MMU_DATA_LOAD; int size = 4 << s->gm_blocksize; @@ -3855,8 +4285,8 @@ static bool trans_LDG(DisasContext *s, arg_ldst_tag *a) tcg_gen_andi_i64(addr, addr, -TAG_GRANULE); tcg_rt = cpu_reg(s, a->rt); - if (s->ata) { - gen_helper_ldg(tcg_rt, cpu_env, addr, tcg_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 @@ -3892,28 +4322,28 @@ static bool do_STG(DisasContext *s, arg_ldst_tag *a, bool is_zero, bool is_pair) tcg_gen_addi_i64(addr, addr, a->imm); } tcg_rt = cpu_reg_sp(s, a->rt); - if (!s->ata) { + 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(cpu_env, addr); + gen_helper_st2g_stub(tcg_env, addr); } else { - gen_helper_stg_stub(cpu_env, addr); + gen_helper_stg_stub(tcg_env, addr); } } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { if (is_pair) { - gen_helper_st2g_parallel(cpu_env, addr, tcg_rt); + gen_helper_st2g_parallel(tcg_env, addr, tcg_rt); } else { - gen_helper_stg_parallel(cpu_env, addr, tcg_rt); + gen_helper_stg_parallel(tcg_env, addr, tcg_rt); } } else { if (is_pair) { - gen_helper_st2g(cpu_env, addr, tcg_rt); + gen_helper_st2g(tcg_env, addr, tcg_rt); } else { - gen_helper_stg(cpu_env, addr, tcg_rt); + gen_helper_stg(tcg_env, addr, tcg_rt); } } @@ -3950,6 +4380,123 @@ 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, @@ -4012,8 +4559,8 @@ static bool gen_add_sub_imm_with_tags(DisasContext *s, arg_rri_tag *a, tcg_rn = cpu_reg_sp(s, a->rn); tcg_rd = cpu_reg_sp(s, a->rd); - if (s->ata) { - gen_helper_addsubg(tcg_rd, cpu_env, tcg_rn, + if (s->ata[0]) { + gen_helper_addsubg(tcg_rd, tcg_env, tcg_rn, tcg_constant_i32(imm), tcg_constant_i32(a->uimm4)); } else { @@ -4322,9419 +4869,5165 @@ static bool trans_EXTR(DisasContext *s, arg_extract *a) return true; } -/* 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) +static bool trans_TBL_TBX(DisasContext *s, arg_TBL_TBX *a) { - 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 (fp_access_check(s)) { + int len = (a->len + 1) * 16; - if (!sf) { /* zero extend final result */ - tcg_gen_ext32u_i64(dst, dst); + 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; } -/* 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) +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) { - assert(shift_i < (sf ? 64 : 32)); + MemOp esz = a->esz; + int datasize = a->q ? 16 : 8; + int elements = datasize >> esz; + TCGv_i64 tcg_res[2], tcg_ele; - if (shift_i == 0) { - tcg_gen_mov_i64(dst, src); - } else { - shift_reg(dst, src, sf, shift_type, tcg_constant_i64(shift_i)); + if (esz == MO_64 && !a->q) { + return false; } -} - -/* 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; + if (!fp_access_check(s)) { + return true; } - tcg_rd = cpu_reg(s, rd); + tcg_res[0] = tcg_temp_new_i64(); + tcg_res[1] = a->q ? tcg_temp_new_i64() : NULL; + tcg_ele = tcg_temp_new_i64(); - 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); - } + 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 { - if (sf) { - tcg_gen_mov_i64(tcg_rd, tcg_rm); - } else { - tcg_gen_ext32u_i64(tcg_rd, tcg_rm); - } + tcg_gen_deposit_i64(tcg_res[w], tcg_res[w], tcg_ele, o, 8 << esz); } - return; } - tcg_rm = read_cpu_reg(s, rm, sf); - - if (shift_amount) { - shift_reg_imm(tcg_rm, tcg_rm, sf, shift_type, shift_amount); + 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; +} - tcg_rn = cpu_reg(s, rn); +static int permute_load_uzp(int i, int part, int elements) +{ + return 2 * i + part; +} - 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; - } +TRANS(UZP1, do_simd_permute, a, permute_load_uzp, 0) +TRANS(UZP2, do_simd_permute, a, permute_load_uzp, 1) - if (!sf) { - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); - } +static int permute_load_trn(int i, int part, int elements) +{ + return (i & 1) * elements + (i & ~1) + part; +} - if (opc == 3) { - gen_logic_CC(sf, tcg_rd); - } +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) + /* - * 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) + * Cryptographic AES, SHA, SHA512 */ -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); +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) - tcg_rm = read_cpu_reg(s, rm, sf); - ext_and_shift_reg(tcg_rm, tcg_rm, option, imm3); +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) - tcg_result = tcg_temp_new_i64(); +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) - 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); - } - } +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) - if (sf) { - tcg_gen_mov_i64(tcg_rd, tcg_result); - } else { - tcg_gen_ext32u_i64(tcg_rd, tcg_result); - } -} +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) -/* - * 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; - } +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) - 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); +TRANS_FEAT(EOR3, aa64_sha3, do_gvec_fn4, a, gen_gvec_eor3) +TRANS_FEAT(BCAX, aa64_sha3, do_gvec_fn4, a, gen_gvec_bcax) - 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; +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(); - tcg_op1 = tcg_temp_new_i64(); - tcg_op2 = tcg_temp_new_i64(); - tcg_tmp = tcg_temp_new_i64(); + 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); - 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)); - } - } + 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); - 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); - } + /* Clear the whole register first, then store bits [127:96]. */ + clear_vec(s, a->rd); + write_vec_element_i32(s, tcg_res, a->rd, 3, MO_32); } + return true; +} - if (!sf) { - tcg_gen_ext32u_i64(cpu_reg(s, rd), cpu_reg(s, rd)); +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) -/* 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) +static bool trans_XAR(DisasContext *s, arg_XAR *a) { - 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 (!dc_isar_feature(aa64_sha3, s)) { + return false; } - - if (setflags) { - gen_adc_CC(sf, tcg_rd, tcg_rn, tcg_y); - } else { - gen_adc(sf, tcg_rd, tcg_rn, tcg_y); + 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; } /* - * 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 | - * +--+--+--+-----------------+--------+-----------+------+--+------+ + * Advanced SIMD copy */ -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; +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; +} - 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); +static bool trans_DUP_element_s(DisasContext *s, arg_DUP_element_s *a) +{ + MemOp esz; + unsigned idx; - 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 (!decode_esz_idx(a->imm, &esz, &idx)) { + return false; } - if (mask & 1) { /* V */ - tcg_gen_shli_i32(cpu_VF, nzcv, 31 - 0); + 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; } -/* - * 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) +static bool trans_DUP_element_v(DisasContext *s, arg_DUP_element_v *a) { - 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; + MemOp esz; + unsigned idx; - if (sf_op_s != 1 || o2 != 0 || o3_mask != 0xd || - !dc_isar_feature(aa64_condm_4, s)) { - unallocated_encoding(s); - return; + if (!decode_esz_idx(a->imm, &esz, &idx)) { + return false; } - 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); + 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; } -/* 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) +static bool trans_DUP_general(DisasContext *s, arg_DUP_general *a) { - 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; + MemOp esz; + unsigned idx; - if (!extract32(insn, 29, 1)) { - unallocated_encoding(s); - return; - } - if (insn & (1 << 10 | 1 << 4)) { - unallocated_encoding(s); - return; + if (!decode_esz_idx(a->imm, &esz, &idx)) { + return false; } - 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); + if (esz == MO_64 && !a->q) { + return false; } - 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 (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; +} - /* 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); +static bool do_smov_umov(DisasContext *s, arg_SMOV *a, MemOp is_signed) +{ + MemOp esz; + unsigned idx; - 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 (!decode_esz_idx(a->imm, &esz, &idx)) { + return false; } - 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); + if (is_signed) { + if (esz == MO_64 || (esz == MO_32 && !a->q)) { + return false; } } 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 (esz == MO_64 ? !a->q : a->q) { + return false; } } - 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); + 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; } -/* 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); +TRANS(SMOV, do_smov_umov, a, MO_SIGN) +TRANS(UMOV, do_smov_umov, a, 0) - a64_test_cc(&c, cond); - zero = tcg_constant_i64(0); +static bool trans_INS_general(DisasContext *s, arg_INS_general *a) +{ + MemOp esz; + unsigned idx; - 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 (!decode_esz_idx(a->imm, &esz, &idx)) { + return false; } - - if (!sf) { - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + 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 void handle_clz(DisasContext *s, unsigned int sf, - unsigned int rn, unsigned int rd) +static bool trans_INS_element(DisasContext *s, arg_INS_element *a) { - TCGv_i64 tcg_rd, tcg_rn; - tcg_rd = cpu_reg(s, rd); - tcg_rn = cpu_reg(s, rn); + MemOp esz; + unsigned didx, sidx; - 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); + 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(); -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); + read_vec_element(s, tmp, a->rn, sidx, esz); + write_vec_element(s, tmp, a->rd, didx, esz); - 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); + /* INS is considered a 128-bit write for SVE. */ + clear_vec_high(s, true, a->rd); } + return true; } -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); +/* + * Advanced SIMD three same + */ - 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); +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_with_fpsttype(DisasContext *s, arg_rrr_e *a, + const FPScalar *f, int mergereg, + ARMFPStatusFlavour fpsttype) +{ + 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(fpsttype)); + write_fp_dreg_merging(s, a->rd, mergereg, 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(fpsttype)); + write_fp_sreg_merging(s, a->rd, mergereg, 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(fpsttype)); + write_fp_hreg_merging(s, a->rd, mergereg, t0); + } + break; + default: + return false; } + return true; } -/* REV with sf==1, opcode==3 ("REV64") */ -static void handle_rev64(DisasContext *s, unsigned int sf, - unsigned int rn, unsigned int rd) +static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f, + int mergereg) { - if (!sf) { - unallocated_encoding(s); - return; - } - tcg_gen_bswap64_i64(cpu_reg(s, rd), cpu_reg(s, rn)); + return do_fp3_scalar_with_fpsttype(s, a, f, mergereg, + a->esz == MO_16 ? + FPST_A64_F16 : FPST_A64); } -/* 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) +static bool do_fp3_scalar_ah_2fn(DisasContext *s, arg_rrr_e *a, + const FPScalar *fnormal, const FPScalar *fah, + int mergereg) { - TCGv_i64 tcg_rd = cpu_reg(s, rd); - TCGv_i64 tcg_rn = cpu_reg(s, rn); + return do_fp3_scalar_with_fpsttype(s, a, s->fpcr_ah ? fah : fnormal, + mergereg, select_ah_fpst(s, a->esz)); +} - 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); - } +/* Some insns need to call different helpers when FPCR.AH == 1 */ +static bool do_fp3_scalar_2fn(DisasContext *s, arg_rrr_e *a, + const FPScalar *fnormal, + const FPScalar *fah, + int mergereg) +{ + return do_fp3_scalar(s, a, s->fpcr_ah ? fah : fnormal, mergereg); } -/* 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); +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, a->rn) - 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); -} +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, a->rn) -/* 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; +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, a->rn) - if (extract32(insn, 29, 1)) { - unallocated_encoding(s); - return; - } +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, a->rn) - 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); +static const FPScalar f_scalar_fmax = { + gen_helper_vfp_maxh, + gen_helper_vfp_maxs, + gen_helper_vfp_maxd, +}; +static const FPScalar f_scalar_fmax_ah = { + gen_helper_vfp_ah_maxh, + gen_helper_vfp_ah_maxs, + gen_helper_vfp_ah_maxd, +}; +TRANS(FMAX_s, do_fp3_scalar_2fn, a, &f_scalar_fmax, &f_scalar_fmax_ah, a->rn) -#define MAP(SF, O2, O1) ((SF) | (O1 << 1) | (O2 << 7)) +static const FPScalar f_scalar_fmin = { + gen_helper_vfp_minh, + gen_helper_vfp_mins, + gen_helper_vfp_mind, +}; +static const FPScalar f_scalar_fmin_ah = { + gen_helper_vfp_ah_minh, + gen_helper_vfp_ah_mins, + gen_helper_vfp_ah_mind, +}; +TRANS(FMIN_s, do_fp3_scalar_2fn, a, &f_scalar_fmin, &f_scalar_fmin_ah, a->rn) - 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, 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, cpu_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, cpu_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, cpu_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, cpu_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, cpu_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, cpu_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, cpu_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, 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; - } +static const FPScalar f_scalar_fmaxnm = { + gen_helper_vfp_maxnumh, + gen_helper_vfp_maxnums, + gen_helper_vfp_maxnumd, +}; +TRANS(FMAXNM_s, do_fp3_scalar, a, &f_scalar_fmaxnm, a->rn) -#undef MAP -} +static const FPScalar f_scalar_fminnm = { + gen_helper_vfp_minnumh, + gen_helper_vfp_minnums, + gen_helper_vfp_minnumd, +}; +TRANS(FMINNM_s, do_fp3_scalar, a, &f_scalar_fminnm, a->rn) + +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, a->rn) -static void handle_div(DisasContext *s, bool is_signed, unsigned int sf, - unsigned int rm, unsigned int rn, unsigned int rd) +static void gen_fnmul_h(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) { - TCGv_i64 tcg_n, tcg_m, tcg_rd; - tcg_rd = cpu_reg(s, rd); + gen_helper_vfp_mulh(d, n, m, s); + gen_vfp_negh(d, d); +} - 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); - } +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); +} - if (is_signed) { - gen_helper_sdiv64(tcg_rd, tcg_n, tcg_m); - } else { - gen_helper_udiv64(tcg_rd, tcg_n, tcg_m); - } +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); +} - if (!sf) { /* zero extend final result */ - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); - } +static void gen_fnmul_ah_h(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) +{ + gen_helper_vfp_mulh(d, n, m, s); + gen_vfp_ah_negh(d, d); } -/* 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) +static void gen_fnmul_ah_s(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) { - 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); + gen_helper_vfp_muls(d, n, m, s); + gen_vfp_ah_negs(d, d); +} - tcg_gen_andi_i64(tcg_shift, cpu_reg(s, rm), sf ? 63 : 31); - shift_reg(tcg_rd, tcg_rn, sf, shift_type, tcg_shift); +static void gen_fnmul_ah_d(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_ptr s) +{ + gen_helper_vfp_muld(d, n, m, s); + gen_vfp_ah_negd(d, d); } -/* 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) +static const FPScalar f_scalar_fnmul = { + gen_fnmul_h, + gen_fnmul_s, + gen_fnmul_d, +}; +static const FPScalar f_scalar_ah_fnmul = { + gen_fnmul_ah_h, + gen_fnmul_ah_s, + gen_fnmul_ah_d, +}; +TRANS(FNMUL_s, do_fp3_scalar_2fn, a, &f_scalar_fnmul, &f_scalar_ah_fnmul, a->rn) + +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, a->rm) + +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, a->rm) + +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, a->rm) + +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, a->rm) + +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, a->rm) + +static void gen_fabd_h(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) { - TCGv_i64 tcg_acc, tcg_val; - TCGv_i32 tcg_bytes; + gen_helper_vfp_subh(d, n, m, s); + gen_vfp_absh(d, d); +} - if (!dc_isar_feature(aa64_crc32, s) - || (sf == 1 && sz != 3) - || (sf == 0 && sz == 3)) { - unallocated_encoding(s); - return; - } +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); +} - 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); - } +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); +} - tcg_acc = cpu_reg(s, rn); - tcg_bytes = tcg_constant_i32(1 << sz); +static void gen_fabd_ah_h(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) +{ + gen_helper_vfp_subh(d, n, m, s); + gen_vfp_ah_absh(d, d); +} - 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); - } +static void gen_fabd_ah_s(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) +{ + gen_helper_vfp_subs(d, n, m, s); + gen_vfp_ah_abss(d, d); } -/* 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) +static void gen_fabd_ah_d(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_ptr s) { - 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); + gen_helper_vfp_subd(d, n, m, s); + gen_vfp_ah_absd(d, d); +} - if (setflag && opcode != 0) { - unallocated_encoding(s); - return; - } +static const FPScalar f_scalar_fabd = { + gen_fabd_h, + gen_fabd_s, + gen_fabd_d, +}; +static const FPScalar f_scalar_ah_fabd = { + gen_fabd_ah_h, + gen_fabd_ah_s, + gen_fabd_ah_d, +}; +TRANS(FABD_s, do_fp3_scalar_2fn, a, &f_scalar_fabd, &f_scalar_ah_fabd, a->rn) - 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; +static const FPScalar f_scalar_frecps = { + gen_helper_recpsf_f16, + gen_helper_recpsf_f32, + gen_helper_recpsf_f64, +}; +static const FPScalar f_scalar_ah_frecps = { + gen_helper_recpsf_ah_f16, + gen_helper_recpsf_ah_f32, + gen_helper_recpsf_ah_f64, +}; +TRANS(FRECPS_s, do_fp3_scalar_ah_2fn, a, + &f_scalar_frecps, &f_scalar_ah_frecps, a->rn) - 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); +static const FPScalar f_scalar_frsqrts = { + gen_helper_rsqrtsf_f16, + gen_helper_rsqrtsf_f32, + gen_helper_rsqrtsf_f64, +}; +static const FPScalar f_scalar_ah_frsqrts = { + gen_helper_rsqrtsf_ah_f16, + gen_helper_rsqrtsf_ah_f32, + gen_helper_rsqrtsf_ah_f64, +}; +TRANS(FRSQRTS_s, do_fp3_scalar_ah_2fn, a, + &f_scalar_frsqrts, &f_scalar_ah_frsqrts, a->rn) - if (setflag) { - gen_sub_CC(true, tcg_d, tcg_n, tcg_m); +static bool do_fcmp0_s(DisasContext *s, arg_rr_e *a, + const FPScalar *f, bool swap) +{ + switch (a->esz) { + case MO_64: + if (fp_access_check(s)) { + TCGv_i64 t0 = read_fp_dreg(s, a->rn); + TCGv_i64 t1 = tcg_constant_i64(0); + if (swap) { + f->gen_d(t0, t1, t0, fpstatus_ptr(FPST_A64)); } else { - tcg_gen_sub_i64(tcg_d, tcg_n, tcg_m); + f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_A64)); } + write_fp_dreg(s, a->rd, t0); } 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)); + case MO_32: + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_sreg(s, a->rn); + TCGv_i32 t1 = tcg_constant_i32(0); + if (swap) { + f->gen_s(t0, t1, t0, fpstatus_ptr(FPST_A64)); + } else { + f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_A64)); + } + write_fp_sreg(s, a->rd, t0); } 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); + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; } - 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; + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_hreg(s, a->rn); + TCGv_i32 t1 = tcg_constant_i32(0); + if (swap) { + f->gen_h(t0, t1, t0, fpstatus_ptr(FPST_A64_F16)); + } else { + f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_A64_F16)); + } + write_fp_sreg(s, a->rd, t0); } - 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; + return false; } + return true; } -/* - * 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; - } +TRANS(FCMEQ0_s, do_fcmp0_s, a, &f_scalar_fcmeq, false) +TRANS(FCMGT0_s, do_fcmp0_s, a, &f_scalar_fcmgt, false) +TRANS(FCMGE0_s, do_fcmp0_s, a, &f_scalar_fcmge, false) +TRANS(FCMLT0_s, do_fcmp0_s, a, &f_scalar_fcmgt, true) +TRANS(FCMLE0_s, do_fcmp0_s, a, &f_scalar_fcmge, true) - switch (op2) { - case 0x0: - switch (op3) { - case 0x00: /* Add/subtract (with carry) */ - disas_adc_sbc(s, insn); - break; +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; - case 0x01: /* Rotate right into flags */ - case 0x21: - disas_rotate_right_into_flags(s, insn); - break; + if (!fp_access_check(s)) { + return true; + } - case 0x02: /* Evaluate into flags */ - case 0x12: - case 0x22: - case 0x32: - disas_evaluate_into_flags(s, insn); - break; + 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)); - default: - goto do_unallocated; - } - break; + 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); + } - case 0x2: /* Conditional compare */ - disas_cc(s, insn); /* both imm and reg forms */ - break; + write_fp_dreg(s, a->rd, t0); + tcg_gen_st_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); + return true; +} - case 0x4: /* Conditional select */ - disas_cond_select(s, insn); - break; +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) - 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; +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(); - default: - do_unallocated: - unallocated_encoding(s); - break; + 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; } -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); +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) - if (size == MO_64) { - TCGv_i64 tcg_vn, tcg_vm; +typedef struct ENVScalar2 { + NeonGenTwoOpEnvFn *gen_bhs[3]; + NeonGenTwo64OpEnvFn *gen_d; +} ENVScalar2; - 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); - } +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 tcg_vn = tcg_temp_new_i32(); - TCGv_i32 tcg_vm = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = 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); - } + 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; +} - 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(); - } - } +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) - gen_set_nzcv(tcg_flags); -} +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) -/* 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; +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) - 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); +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) - if (mos || op || op2r) { - unallocated_encoding(s); - return; +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; +} - 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; - } +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; + return true; } - handle_fp_compare(s, size, rn, rm, opc & 1, opc & 2); + 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; } -/* 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; +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) - 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); +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) - if (mos) { - unallocated_encoding(s); - return; +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; +} - 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; - } +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) - if (!fp_access_check(s)) { - return; - } +static bool do_fp3_vector_with_fpsttype(DisasContext *s, arg_qrrr_e *a, + int data, + gen_helper_gvec_3_ptr * const fns[3], + ARMFPStatusFlavour fpsttype) +{ + MemOp esz = a->esz; + int check = fp_access_check_vector_hsd(s, a->q, esz); - 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); + if (check <= 0) { + return check == 0; } - handle_fp_compare(s, size, rn, rm, false, op); + gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm, fpsttype, + data, fns[esz - 1]); + return true; +} - if (cond < 0x0e) { - gen_set_label(label_continue); - } +static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, int data, + gen_helper_gvec_3_ptr * const fns[3]) +{ + return do_fp3_vector_with_fpsttype(s, a, data, fns, + a->esz == MO_16 ? + FPST_A64_F16 : FPST_A64); } -/* 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) +static bool do_fp3_vector_2fn(DisasContext *s, arg_qrrr_e *a, int data, + gen_helper_gvec_3_ptr * const fnormal[3], + gen_helper_gvec_3_ptr * const fah[3]) { - unsigned int mos, type, rm, cond, rn, rd; - TCGv_i64 t_true, t_false; - DisasCompare64 c; - MemOp sz; + return do_fp3_vector(s, a, data, s->fpcr_ah ? fah : fnormal); +} - 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); +static bool do_fp3_vector_ah_2fn(DisasContext *s, arg_qrrr_e *a, int data, + gen_helper_gvec_3_ptr * const fnormal[3], + gen_helper_gvec_3_ptr * const fah[3]) +{ + return do_fp3_vector_with_fpsttype(s, a, data, s->fpcr_ah ? fah : fnormal, + select_ah_fpst(s, a->esz)); +} - if (mos) { - unallocated_encoding(s); - return; - } +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) - 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; - } +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) - if (!fp_access_check(s)) { - return; - } +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) - /* 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); +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) - a64_test_cc(&c, cond); - tcg_gen_movcond_i64(c.cond, t_true, c.value, tcg_constant_i64(0), - t_true, t_false); +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, +}; +static gen_helper_gvec_3_ptr * const f_vector_fmax_ah[3] = { + gen_helper_gvec_ah_fmax_h, + gen_helper_gvec_ah_fmax_s, + gen_helper_gvec_ah_fmax_d, +}; +TRANS(FMAX_v, do_fp3_vector_2fn, a, 0, f_vector_fmax, f_vector_fmax_ah) - /* 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); -} +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, +}; +static gen_helper_gvec_3_ptr * const f_vector_fmin_ah[3] = { + gen_helper_gvec_ah_fmin_h, + gen_helper_gvec_ah_fmin_s, + gen_helper_gvec_ah_fmin_d, +}; +TRANS(FMIN_v, do_fp3_vector_2fn, a, 0, f_vector_fmin, f_vector_fmin_ah) -/* 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(); +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) - 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; +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) - 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(); - } +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) - write_fp_sreg(s, rd, tcg_res); -} +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, +}; +static gen_helper_gvec_3_ptr * const f_vector_fmls_ah[3] = { + gen_helper_gvec_ah_vfms_h, + gen_helper_gvec_ah_vfms_s, + gen_helper_gvec_ah_vfms_d, +}; +TRANS(FMLS_v, do_fp3_vector_2fn, a, 0, f_vector_fmls, f_vector_fmls_ah) + +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, +}; +static gen_helper_gvec_3_ptr * const f_vector_ah_fabd[3] = { + gen_helper_gvec_ah_fabd_h, + gen_helper_gvec_ah_fabd_s, + gen_helper_gvec_ah_fabd_d, +}; +TRANS(FABD_v, do_fp3_vector_2fn, a, 0, f_vector_fabd, f_vector_ah_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, +}; +static gen_helper_gvec_3_ptr * const f_vector_ah_frecps[3] = { + gen_helper_gvec_ah_recps_h, + gen_helper_gvec_ah_recps_s, + gen_helper_gvec_ah_recps_d, +}; +TRANS(FRECPS_v, do_fp3_vector_ah_2fn, a, 0, f_vector_frecps, f_vector_ah_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, +}; +static gen_helper_gvec_3_ptr * const f_vector_ah_frsqrts[3] = { + gen_helper_gvec_ah_rsqrts_h, + gen_helper_gvec_ah_rsqrts_s, + gen_helper_gvec_ah_rsqrts_d, +}; +TRANS(FRSQRTS_v, do_fp3_vector_ah_2fn, a, 0, f_vector_frsqrts, f_vector_ah_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, +}; +static gen_helper_gvec_3_ptr * const f_vector_ah_fmaxp[3] = { + gen_helper_gvec_ah_fmaxp_h, + gen_helper_gvec_ah_fmaxp_s, + gen_helper_gvec_ah_fmaxp_d, +}; +TRANS(FMAXP_v, do_fp3_vector_2fn, a, 0, f_vector_fmaxp, f_vector_ah_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, +}; +static gen_helper_gvec_3_ptr * const f_vector_ah_fminp[3] = { + gen_helper_gvec_ah_fminp_h, + gen_helper_gvec_ah_fminp_s, + gen_helper_gvec_ah_fminp_d, +}; +TRANS(FMINP_v, do_fp3_vector_2fn, a, 0, f_vector_fminp, f_vector_ah_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) -/* Floating-point data-processing (1 source) - single precision */ -static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) +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) { - 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 = 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(); + 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; +} - 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); - } +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) - done: - write_fp_sreg(s, rd, tcg_res); -} +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) -/* Floating-point data-processing (1 source) - double precision */ -static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) +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) { - void (*gen_fpst)(TCGv_i64, TCGv_i64, TCGv_ptr); - TCGv_i64 tcg_op, tcg_res; - TCGv_ptr fpst; - int rmode = -1; + if (fp_access_check(s)) { + gen_gvec_fn4(s, is_q, d, a, b, c, tcg_gen_gvec_bitsel, 0); + } + return true; +} - switch (opcode) { - case 0x0: /* FMOV */ - gen_gvec_fn2(s, false, rd, rn, tcg_gen_gvec_mov, 0); - return; +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; +} - tcg_op = read_fp_dreg(s, rn); - tcg_res = tcg_temp_new_i64(); +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) - 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 = 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(); - } +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) - 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); +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); } - - done: - write_fp_dreg(s, rd, tcg_res); + return true; } -static void handle_fp_fcvt(DisasContext *s, int opcode, - int rd, int rn, int dtype, int ntype) +static bool do_dot_vector_env(DisasContext *s, arg_qrrr_e *a, + gen_helper_gvec_4_ptr *fn) { - 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); - } 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, 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 */ - } - write_fp_sreg(s, rd, tcg_rd); - break; + if (fp_access_check(s)) { + gen_gvec_op4_env(s, a->q, a->rd, a->rn, a->rm, a->rd, 0, fn); } - 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; + 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; } - default: - g_assert_not_reached(); + 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, + s->fpcr_ah ? FPST_AH : FPST_A64, a->q, + gen_helper_gvec_bfmlal); } + return true; } -/* 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 gen_helper_gvec_3_ptr * const f_vector_fcadd[3] = { + gen_helper_gvec_fcaddh, + gen_helper_gvec_fcadds, + gen_helper_gvec_fcaddd, +}; +/* + * Encode FPCR.AH into the data so the helper knows whether the + * negations it does should avoid flipping the sign bit on a NaN */ -static void disas_fp_1src(DisasContext *s, uint32_t insn) +TRANS_FEAT(FCADD_90, aa64_fcma, do_fp3_vector, a, 0 | (s->fpcr_ah << 1), + f_vector_fcadd) +TRANS_FEAT(FCADD_270, aa64_fcma, do_fp3_vector, a, 1 | (s->fpcr_ah << 1), + f_vector_fcadd) + +static bool trans_FCMLA_v(DisasContext *s, arg_FCMLA_v *a) { - 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); + static gen_helper_gvec_4_ptr * const fn[] = { + [MO_16] = gen_helper_gvec_fcmlah, + [MO_32] = gen_helper_gvec_fcmlas, + [MO_64] = gen_helper_gvec_fcmlad, + }; + int check; - if (mos) { - goto do_unallocated; + if (!dc_isar_feature(aa64_fcma, s)) { + return false; } - 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; + check = fp_access_check_vector_hsd(s, a->q, a->esz); + if (check <= 0) { + return check == 0; } - 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; - } + gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64, + a->rot | (s->fpcr_ah << 2), fn[a->esz]); + return true; +} - if (!fp_access_check(s)) { - return; - } - handle_fp_1src_half(s, opcode, rd, rn); - break; - default: - goto do_unallocated; - } - break; +/* + * 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. + */ - 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; +/* 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; - default: - do_unallocated: - unallocated_encoding(s); - break; + /* There are no 64x64->128 bit operations. */ + if (esz >= MO_64) { + return false; + } + if (!fp_access_check(s)) { + return true; } -} -/* 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; + 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; - 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); + for (int elt_fwd = 0; elt_fwd < half; ++elt_fwd) { + int elt = elt_fwd ^ top_swap; - 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; + 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); } - - write_fp_sreg(s, rd, tcg_res); + clear_vec_high(s, 1, rd); + return true; } -/* 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; +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) - 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); +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) - 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; - } +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(); - write_fp_dreg(s, rd, tcg_res); + 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); } -/* Floating-point data-processing (2 source) - half precision */ -static void handle_fp_2src_half(DisasContext *s, int opcode, - int rd, int rn, int rm) +static void gen_uabd_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) { - 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); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); - 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(); - } + 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); +} - write_fp_sreg(s, rd, tcg_res); +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); } -/* 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) +static void gen_uaba_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) { - 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); + TCGv_i64 t = tcg_temp_new_i64(); + gen_uabd_i64(t, n, m); + tcg_gen_add_i64(d, d, t); +} - if (opcode > 8 || mos) { - unallocated_encoding(s); - return; - } +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) - 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); - } +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); } -/* 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) +static void gen_sqdmlal_h(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) { - TCGv_i32 tcg_op1, tcg_op2, tcg_op3; - TCGv_i32 tcg_res = tcg_temp_new_i32(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + TCGv_i64 t = tcg_temp_new_i64(); - tcg_op1 = read_fp_sreg(s, rn); - tcg_op2 = read_fp_sreg(s, rm); - tcg_op3 = read_fp_sreg(s, ra); + 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); +} - /* 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); - } +static void gen_sqdmlal_s(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); - if (o0 != o1) { - gen_helper_vfp_negs(tcg_op1, tcg_op1); - } + 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); +} - gen_helper_vfp_muladds(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst); +static void gen_sqdmlsl_h(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); - write_fp_sreg(s, rd, tcg_res); + 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); } -/* 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) +static void gen_sqdmlsl_s(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) { - TCGv_i64 tcg_op1, tcg_op2, tcg_op3; - TCGv_i64 tcg_res = tcg_temp_new_i64(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + TCGv_i64 t = tcg_temp_new_i64(); - tcg_op1 = read_fp_dreg(s, rn); - tcg_op2 = read_fp_dreg(s, rm); - tcg_op3 = read_fp_dreg(s, ra); + 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); +} - /* 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); - } +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; - if (o0 != o1) { - gen_helper_vfp_negd(tcg_op1, tcg_op1); + /* 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(); - gen_helper_vfp_muladdd(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst); + for (int elt_fwd = 0; elt_fwd < half; ++elt_fwd) { + int elt = elt_fwd ^ top_swap; - write_fp_dreg(s, rd, tcg_res); + 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; } -/* 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) +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_i32 tcg_op1, tcg_op2, tcg_op3; - TCGv_i32 tcg_res = tcg_temp_new_i32(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR_F16); + 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; - tcg_op1 = read_fp_hreg(s, rn); - tcg_op2 = read_fp_hreg(s, rm); - tcg_op3 = read_fp_hreg(s, ra); + /* 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(); - /* 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. + /* + * 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. */ - if (o1 == true) { - tcg_gen_xori_i32(tcg_op3, tcg_op3, 0x8000); - } + top_swap = top ? half - 1 : 0; + top_half = top ? half : 0; - if (o0 != o1) { - tcg_gen_xori_i32(tcg_op1, tcg_op1, 0x8000); + 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; +} - gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst); +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) - write_fp_sreg(s, rd, tcg_res); +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; } -/* 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 | - * +---+---+---+-----------+------+----+------+----+------+------+------+ +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 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; +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_A64)); + write_fp_dreg_merging(s, a->rd, a->rn, t0); } - handle_fp_3src_single(s, o0, o1, rd, rn, rm, ra); break; - case 1: - if (!fp_access_check(s)) { - return; + 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_A64)); + write_fp_sreg_merging(s, a->rd, a->rn, t0); } - handle_fp_3src_double(s, o0, o1, rd, rn, rm, ra); break; - case 3: + case MO_16: if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; + return false; } - if (!fp_access_check(s)) { - return; + 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_A64_F16)); + write_fp_hreg_merging(s, a->rd, a->rn, t0); } - handle_fp_3src_half(s, o0, o1, rd, rn, rm, ra); break; default: - unallocated_encoding(s); + g_assert_not_reached(); } + return true; } -/* 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; +TRANS(FMUL_si, do_fp3_scalar_idx, a, &f_scalar_fmul) +TRANS(FMULX_si, do_fp3_scalar_idx, a, &f_scalar_fmulx) - if (mos || imm5) { - unallocated_encoding(s); - return; - } +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(); - switch (type) { - case 0: - sz = MO_32; + read_vec_element(s, t2, a->rm, a->idx, MO_64); + if (neg) { + gen_vfp_maybe_ah_negd(s, t1, t1); + } + gen_helper_vfp_muladdd(t0, t1, t2, t0, fpstatus_ptr(FPST_A64)); + write_fp_dreg_merging(s, a->rd, a->rd, t0); + } break; - case 1: - sz = MO_64; + 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_maybe_ah_negs(s, t1, t1); + } + gen_helper_vfp_muladds(t0, t1, t2, t0, fpstatus_ptr(FPST_A64)); + write_fp_sreg_merging(s, a->rd, a->rd, t0); + } break; - case 3: - sz = MO_16; - if (dc_isar_feature(aa64_fp16, s)) { - break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; } - /* fallthru */ + 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_maybe_ah_negh(s, t1, t1); + } + gen_helper_advsimd_muladdh(t0, t1, t2, t0, + fpstatus_ptr(FPST_A64_F16)); + write_fp_hreg_merging(s, a->rd, a->rd, t0); + } + break; default: - unallocated_encoding(s); - return; + g_assert_not_reached(); } + return true; +} - if (!fp_access_check(s)) { - return; +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(); - imm = vfp_expand_imm(sz, imm8); - write_fp_dreg(s, rd, tcg_constant_i64(imm)); + 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; } -/* 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; +TRANS(SQDMULH_si, do_env_scalar2_idx_hs, a, &f_scalar_sqdmulh) +TRANS(SQRDMULH_si, do_env_scalar2_idx_hs, a, &f_scalar_sqrdmulh) - tcg_fpstatus = fpstatus_ptr(type == 3 ? FPST_FPCR_F16 : FPST_FPCR); +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(); - tcg_shift = tcg_constant_i32(64 - scale); + 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; +} - if (itof) { - TCGv_i64 tcg_int = cpu_reg(s, rn); - if (!sf) { - TCGv_i64 tcg_extend = tcg_temp_new_i64(); +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) - if (is_signed) { - tcg_gen_ext32s_i64(tcg_extend, tcg_int); - } else { - tcg_gen_ext32u_i64(tcg_extend, tcg_int); - } +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(); - tcg_int = tcg_extend; + 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); - 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; + /* Clear the whole register first, then store scalar. */ + clear_vec(s, a->rd); + write_vec_element(s, t0, a->rd, 0, a->esz + 1); + } + return true; +} - 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; +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) - default: - g_assert_not_reached(); - } - } else { - TCGv_i64 tcg_int = cpu_reg(s, rd); - TCGv_i32 tcg_rmode; +static bool do_fp3_vector_idx(DisasContext *s, arg_qrrx_e *a, + gen_helper_gvec_3_ptr * const fns[3]) +{ + MemOp esz = a->esz; + int check = fp_access_check_vector_hsd(s, a->q, esz); - 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; + if (check <= 0) { + return check == 0; + } - 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; + gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm, + esz == MO_16 ? FPST_A64_F16 : FPST_A64, + a->idx, fns[esz - 1]); + return true; +} - 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; +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) - default: - g_assert_not_reached(); - } +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][3] = { + { gen_helper_gvec_fmla_idx_h, + gen_helper_gvec_fmla_idx_s, + gen_helper_gvec_fmla_idx_d }, + { gen_helper_gvec_fmls_idx_h, + gen_helper_gvec_fmls_idx_s, + gen_helper_gvec_fmls_idx_d }, + { gen_helper_gvec_ah_fmls_idx_h, + gen_helper_gvec_ah_fmls_idx_s, + gen_helper_gvec_ah_fmls_idx_d }, + }; + MemOp esz = a->esz; + int check = fp_access_check_vector_hsd(s, a->q, esz); - gen_restore_rmode(tcg_rmode, tcg_fpstatus); + if (check <= 0) { + return check == 0; } + + gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, + esz == MO_16 ? FPST_A64_F16 : FPST_A64, + a->idx, fns[neg ? 1 + s->fpcr_ah : 0][esz - 1]); + return true; } -/* 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; - } +TRANS(FMLA_vi, do_fmla_vector_idx, a, false) +TRANS(FMLS_vi, do_fmla_vector_idx, a, true) - 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; +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; +} - 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; - } +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) - if (!fp_access_check(s)) { - return; +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; +} - handle_fpfpcvt(s, rd, rn, opcode, itof, FPROUNDING_ZERO, scale, sf, type); +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; } -static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) +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]) { - /* FMOV: gpr to or from float, double, or top half of quad fp reg, - * without conversion. - */ + 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; +} - if (itof) { - TCGv_i64 tcg_rn = cpu_reg(s, rn); - TCGv_i64 tmp; +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) - 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, 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); - break; - default: - g_assert_not_reached(); - } - } else { - TCGv_i64 tcg_rd = cpu_reg(s, rd); +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) - 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 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 void handle_fjcvtzs(DisasContext *s, int rd, int rn) +static bool do_dot_vector_idx_env(DisasContext *s, arg_qrrx_e *a, + gen_helper_gvec_4_ptr *fn) { - TCGv_i64 t = read_fp_dreg(s, rn); - TCGv_ptr fpstatus = fpstatus_ptr(FPST_FPCR); + 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; +} - gen_helper_fjcvtzs(t, t, fpstatus); +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) - 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); +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, + s->fpcr_ah ? FPST_AH : FPST_A64, + (a->idx << 1) | a->q, + gen_helper_gvec_bfmlal_idx); + } + return true; } -/* 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; +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; } - handle_fpfpcvt(s, rd, rn, opcode, itof, rmode, 64, sf, type); + fn = gen_helper_gvec_fcmlah_idx; + break; + case MO_32: + fn = gen_helper_gvec_fcmlas_idx; 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; + 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 ? FPST_A64_F16 : FPST_A64, + (s->fpcr_ah << 4) | (a->idx << 2) | a->rot, fn); + } + return true; +} - 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; +/* + * Advanced SIMD scalar pairwise + */ - default: - do_unallocated: - unallocated_encoding(s); - return; +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_A64)); + 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(); -/* 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; + 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_A64)); + 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_A64_F16)); + write_fp_sreg(s, a->rd, t0); } + break; + default: + g_assert_not_reached(); } + return true; } -static void do_ext64(DisasContext *s, TCGv_i64 tcg_left, TCGv_i64 tcg_right, - int pos) +static bool do_fp3_scalar_pair_2fn(DisasContext *s, arg_rr_e *a, + const FPScalar *fnormal, + const FPScalar *fah) { - /* 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); + return do_fp3_scalar_pair(s, a, s->fpcr_ah ? fah : fnormal); +} + +TRANS(FADDP_s, do_fp3_scalar_pair, a, &f_scalar_fadd) +TRANS(FMAXP_s, do_fp3_scalar_pair_2fn, a, &f_scalar_fmax, &f_scalar_fmax_ah) +TRANS(FMINP_s, do_fp3_scalar_pair_2fn, a, &f_scalar_fmin, &f_scalar_fmin_ah) +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(); - 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); + 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; } -/* 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 | - * +---+---+-------------+-----+---+------+---+------+---+------+------+ +/* + * Floating-point conditional select */ -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; +static bool trans_FCSEL(DisasContext *s, arg_FCSEL *a) +{ + TCGv_i64 t_true, t_false; + DisasCompare64 c; + int check = fp_access_check_scalar_hsd(s, a->esz); + + if (check <= 0) { + return check == 0; } - tcg_resh = tcg_temp_new_i64(); - tcg_resl = tcg_temp_new_i64(); + /* 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); - /* 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. + /* + * Note that sregs & hregs write back zeros to the high bits, + * and we've already done the zero-extension. */ - 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; + write_fp_dreg(s, a->rd, t_true); + return true; +} - if (pos >= 64) { - elt++; - pos -= 64; - } +/* + * Advanced SIMD Extract + */ - 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); +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); } - - write_vec_element(s, tcg_resl, rd, 0, MO_64); - if (is_q) { - write_vec_element(s, tcg_resh, rd, 1, MO_64); - } - clear_vec_high(s, is_q, rd); + return true; } -/* 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) +static bool trans_EXT_q(DisasContext *s, arg_EXT_q *a) { - 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; - } + TCGv_i64 lo, hi; + int pos = (a->imm & 7) * 8; + int elt = a->imm >> 3; if (!fp_access_check(s)) { - return; + return true; } - 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); -} + lo = tcg_temp_new_i64(); + hi = tcg_temp_new_i64(); -/* 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; - int datasize = is_q ? 128 : 64; - int elements = datasize / esize; - TCGv_i64 tcg_res[2], tcg_ele; + 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 (opcode == 0 || (size == 3 && !is_q)) { - unallocated_encoding(s); - return; + 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); } - if (!fp_access_check(s)) { - return; - } + 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; +} - tcg_res[0] = tcg_temp_new_i64(); - tcg_res[1] = is_q ? tcg_temp_new_i64() : NULL; - tcg_ele = tcg_temp_new_i64(); +/* + * Floating-point data-processing (3 source) + */ - for (i = 0; i < elements; i++) { - int o, w; +static bool do_fmadd(DisasContext *s, arg_rrrr_e *a, bool neg_a, bool neg_n) +{ + TCGv_ptr fpst; - switch (opcode) { - case 1: /* UZP1/2 */ - { - int midpoint = elements / 2; - if (i < midpoint) { - read_vec_element(s, tcg_ele, rn, 2 * i + part, size); - } else { - read_vec_element(s, tcg_ele, rm, - 2 * (i - midpoint) + part, size); + /* + * 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_maybe_ah_negd(s, ta, ta); } - break; + if (neg_n) { + gen_vfp_maybe_ah_negd(s, tn, tn); + } + fpst = fpstatus_ptr(FPST_A64); + gen_helper_vfp_muladdd(ta, tn, tm, ta, fpst); + write_fp_dreg_merging(s, a->rd, a->ra, ta); } - case 2: /* TRN1/2 */ - if (i & 1) { - read_vec_element(s, tcg_ele, rm, (i & ~1) + part, size); - } else { - read_vec_element(s, tcg_ele, rn, (i & ~1) + part, size); + 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_maybe_ah_negs(s, ta, ta); } - break; - case 3: /* ZIP1/2 */ - { - int base = part * elements / 2; - if (i & 1) { - read_vec_element(s, tcg_ele, rm, base + (i >> 1), size); - } else { - read_vec_element(s, tcg_ele, rn, base + (i >> 1), size); + if (neg_n) { + gen_vfp_maybe_ah_negs(s, tn, tn); } - break; + fpst = fpstatus_ptr(FPST_A64); + gen_helper_vfp_muladds(ta, tn, tm, ta, fpst); + write_fp_sreg_merging(s, a->rd, a->ra, ta); } - default: - g_assert_not_reached(); + 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); - w = (i * esize) / 64; - o = (i * esize) % 64; - if (o == 0) { - tcg_gen_mov_i64(tcg_res[w], tcg_ele); - } else { - tcg_gen_shli_i64(tcg_ele, tcg_ele, o); - tcg_gen_or_i64(tcg_res[w], tcg_res[w], tcg_ele); + if (neg_a) { + gen_vfp_maybe_ah_negh(s, ta, ta); + } + if (neg_n) { + gen_vfp_maybe_ah_negh(s, tn, tn); + } + fpst = fpstatus_ptr(FPST_A64_F16); + gen_helper_advsimd_muladdh(ta, tn, tm, ta, fpst); + write_fp_hreg_merging(s, a->rd, a->ra, ta); } - } + break; - for (i = 0; i <= is_q; ++i) { - write_vec_element(s, tcg_res[i], rd, i, MO_64); + default: + return false; } - clear_vec_high(s, is_q, rd); + 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) + /* - * 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. + * Advanced SIMD Across Lanes */ -static TCGv_i32 do_reduction_op(DisasContext *s, int fpopcode, int rn, - int esize, int size, int vmap, TCGv_ptr fpst) + +static bool do_int_reduction(DisasContext *s, arg_qrr_e *a, bool widen, + MemOp src_sign, NeonGenTwo64OpFn *fn) { - if (esize == size) { - int element; - MemOp msize = esize == 16 ? MO_16 : MO_32; - TCGv_i32 tcg_elem; + TCGv_i64 tcg_res, tcg_elt; + MemOp src_mop = a->esz | src_sign; + int elements = (a->q ? 16 : 8) >> a->esz; - /* We should have one register left here */ - assert(ctpop8(vmap) == 1); - element = ctz32(vmap); - assert(element < 8); + /* 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_elem = tcg_temp_new_i32(); - read_vec_element_i32(s, tcg_elem, rn, element, msize); + 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 bits = size / 2; - int shift = ctpop8(vmap) / 2; - int vmap_lo = (vmap >> shift) & vmap; - int vmap_hi = (vmap & ~vmap_lo); + int half = ecount >> 1; 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_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(); - 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(); - } + fn(tcg_res, tcg_lo, tcg_hi, fpst); 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 bool do_fp_reduction(DisasContext *s, arg_qrr_e *a, + NeonGenTwoSingleOpFn *fnormal, + NeonGenTwoSingleOpFn *fah) +{ + 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_A64_F16 : FPST_A64); + TCGv_i32 res = do_reduction_op(s, a->rn, esz, 0, elts, fpst, + s->fpcr_ah ? fah : fnormal); + write_fp_sreg(s, a->rd, res); + } + return true; +} + +TRANS_FEAT(FMAXNMV_h, aa64_fp16, do_fp_reduction, a, + gen_helper_vfp_maxnumh, gen_helper_vfp_maxnumh) +TRANS_FEAT(FMINNMV_h, aa64_fp16, do_fp_reduction, a, + gen_helper_vfp_minnumh, gen_helper_vfp_minnumh) +TRANS_FEAT(FMAXV_h, aa64_fp16, do_fp_reduction, a, + gen_helper_vfp_maxh, gen_helper_vfp_ah_maxh) +TRANS_FEAT(FMINV_h, aa64_fp16, do_fp_reduction, a, + gen_helper_vfp_minh, gen_helper_vfp_ah_minh) + +TRANS(FMAXNMV_s, do_fp_reduction, a, + gen_helper_vfp_maxnums, gen_helper_vfp_maxnums) +TRANS(FMINNMV_s, do_fp_reduction, a, + gen_helper_vfp_minnums, gen_helper_vfp_minnums) +TRANS(FMAXV_s, do_fp_reduction, a, gen_helper_vfp_maxs, gen_helper_vfp_ah_maxs) +TRANS(FMINV_s, do_fp_reduction, a, gen_helper_vfp_mins, gen_helper_vfp_ah_mins) + +/* + * Floating-point Immediate */ -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; - } +static bool trans_FMOVI_s(DisasContext *s, arg_FMOVI_s *a) +{ + int check = fp_access_check_scalar_hsd(s, a->esz); + uint64_t imm; - if (!fp_access_check(s)) { - return; + if (check <= 0) { + return check == 0; } - esize = 8 << size; - elements = (is_q ? 128 : 64) / esize; + imm = vfp_expand_imm(a->esz, a->imm); + write_fp_dreg(s, a->rd, tcg_constant_i64(imm)); + return true; +} - tcg_res = tcg_temp_new_i64(); - tcg_elt = tcg_temp_new_i64(); +/* + * Floating point compare, conditional compare + */ - /* 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(); - } +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_A64_F16 : FPST_A64); + + 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 { - /* 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); - } + TCGv_i32 tcg_vn = tcg_temp_new_i32(); + TCGv_i32 tcg_vm = tcg_temp_new_i32(); - /* Now truncate the result to the width required for the final output */ - if (opcode == 0x03) { - /* SADDLV, UADDLV: result is 2*esize */ - size++; - } + 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 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(); + 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(); + } } - write_fp_dreg(s, rd, tcg_res); + gen_set_nzcv(tcg_flags); } -/* 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) +/* FCMP, FCMPE */ +static bool trans_FCMP(DisasContext *s, arg_FCMP *a) { - int size = ctz32(imm5); - int index; - - if (size > 3 || (size == 3 && !is_q)) { - unallocated_encoding(s); - return; - } + int check = fp_access_check_scalar_hsd(s, a->esz); - if (!fp_access_check(s)) { - return; + if (check <= 0) { + return check == 0; } - 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)); + handle_fp_compare(s, a->esz, a->rn, a->rm, a->z, a->e); + return true; } -/* 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) +/* FCCMP, FCCMPE */ +static bool trans_FCCMP(DisasContext *s, arg_FCCMP *a) { - int size = ctz32(imm5); - int index; - TCGv_i64 tmp; + TCGLabel *label_continue = NULL; + int check = fp_access_check_scalar_hsd(s, a->esz); - if (size > 3) { - unallocated_encoding(s); - return; + if (check <= 0) { + return check == 0; } - if (!fp_access_check(s)) { - return; + if (a->cond < 0x0e) { /* not always */ + TCGLabel *label_match = gen_new_label(); + label_continue = gen_new_label(); + arm_gen_test_cc(a->cond, label_match); + /* nomatch: */ + gen_set_nzcv(tcg_constant_i64(a->nzcv << 28)); + tcg_gen_br(label_continue); + gen_set_label(label_match); } - index = imm5 >> (size + 1); + handle_fp_compare(s, a->esz, a->rn, a->rm, false, a->e); - /* 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); + if (label_continue) { + gen_set_label(label_continue); + } + return true; } -/* 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()) +/* + * Advanced SIMD Modified Immediate */ -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; +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)) { - return; + 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; +} - 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)); +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); } -/* 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) +static bool trans_Vimm(DisasContext *s, arg_Vimm *a) { - int size = ctz32(imm5); - int src_index, dst_index; - TCGv_i64 tmp; + GVecGen2iFn *fn; - if (size > 3) { - unallocated_encoding(s); - return; + /* 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)) { - return; + 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; +} - 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); +/* + * Advanced SIMD Shift by Immediate + */ - /* INS is considered a 128-bit write for SVE. */ - clear_vec_high(s, true, rd); +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) -/* 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) +static bool do_vec_shift_imm_wide(DisasContext *s, arg_qrri_e *a, bool is_u) { - int size = ctz32(imm5); - int idx; - - if (size > 3) { - unallocated_encoding(s); - return; - } + TCGv_i64 tcg_rn, tcg_rd; + int esz = a->esz; + int esize; if (!fp_access_check(s)) { - return; + return true; } - idx = extract32(imm5, 1 + size, 4 - size); - write_vec_element(s, cpu_reg(s, rn), rd, idx, size); + /* + * 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); - /* INS is considered a 128-bit write for SVE. */ - clear_vec_high(s, true, rd); + 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; } -/* - * 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) +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) { - int size = ctz32(imm5); - int element; - TCGv_i64 tcg_rd; + assert(shift >= 0 && shift <= 64); + tcg_gen_sari_i64(dst, src, MIN(shift, 63)); +} - /* Check for UnallocatedEncodings */ - if (is_signed) { - if (size > 2 || (size == 2 && !is_q)) { - unallocated_encoding(s); - return; - } +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 { - if (size > 3 - || (size < 3 && is_q) - || (size == 3 && !is_q)) { - unallocated_encoding(s); - return; - } + tcg_gen_shri_i64(dst, src, shift); } +} - if (!fp_access_check(s)) { - return; - } +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); +} - element = extract32(imm5, 1+size, 4); +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); +} - 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); +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); } } -/* 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); - } +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 { - 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; - } + tcg_gen_mov_i64(dst, src); } } -/* 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; +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); } +} - 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); +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 { - imm = asimd_imm_const(abcdefgh, cmode, is_neg); + 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); } +} - 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); - } - } +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); } -/* 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) +static void gen_ursra_d(TCGv_i64 dst, TCGv_i64 src, int64_t shift) { - 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); + gen_urshr_d(src, src, shift); + tcg_gen_add_i64(dst, dst, src); +} - if (op != 0 || imm4 != 0) { - unallocated_encoding(s); - return; +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); } - - /* 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) +static void gen_sli_d(TCGv_i64 dst, TCGv_i64 src, int64_t shift) { - 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; + tcg_gen_deposit_i64(dst, dst, src, shift, 64 - shift); +} - /* 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); +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; - switch (opcode) { - case 0x3b: /* ADDP */ - if (u || size != 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } + tcg_debug_assert(esz >= MO_8 && esz <= MO_32); - 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 true; + } - if (!fp_access_check(s)) { - return; - } + tcg_rn = tcg_temp_new_i64(); + tcg_rd = tcg_temp_new_i64(); + tcg_gen_movi_i64(tcg_rd, 0); - fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); - break; - default: - unallocated_encoding(s); - return; + 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); } - 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); + write_vec_element(s, tcg_rd, a->rd, a->q, MO_64); + clear_vec_high(s, a->q, a->rd); + return true; +} - 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(); - } +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); +} - write_fp_dreg(s, rd, 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(); +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); +} - 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(); - } - } +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); +} - write_fp_sreg(s, rd, tcg_res); - } +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); } -/* - * 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; - } - } +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); +} - /* 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); - } - } +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); +} - /* 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); - } - } - } +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); +} - if (accumulate) { - tcg_gen_add_i64(tcg_res, tcg_res, tcg_src); - } else { - tcg_gen_mov_i64(tcg_res, tcg_src); - } +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); } -/* 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) +static void gen_sqshrun_s(TCGv_i64 d, TCGv_i64 s, int64_t i) { - 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; + gen_sshr_d(d, s, i); + gen_helper_neon_unarrow_sat32(d, tcg_env, d); +} - if (!extract32(immh, 3, 1)) { - unallocated_encoding(s); - return; - } +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); +} - if (!fp_access_check(s)) { - return; - } +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); +} - 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; - } +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); +} - if (round) { - tcg_round = tcg_constant_i64(1ULL << (shift - 1)); - } else { - tcg_round = NULL; - } +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); +} - tcg_rn = read_fp_dreg(s, rn); - tcg_rd = (accumulate || insert) ? read_fp_dreg(s, rd) : tcg_temp_new_i64(); +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); +} - 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); - } +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); +} - write_fp_dreg(s, rd, tcg_rd); +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); } -/* 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) +static void gen_sqrshrun_h(TCGv_i64 d, TCGv_i64 s, int64_t i) { - int size = 32 - clz32(immh) - 1; - int immhb = immh << 3 | immb; - int shift = immhb - (8 << size); - TCGv_i64 tcg_rn; - TCGv_i64 tcg_rd; + gen_srshr_bhs(d, s, i); + tcg_gen_ext32u_i64(d, d); + gen_helper_neon_unarrow_sat16(d, tcg_env, d); +} - if (!extract32(immh, 3, 1)) { - unallocated_encoding(s); - return; - } +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); +} - if (!fp_access_check(s)) { - return; - } +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) - tcg_rn = read_fp_dreg(s, rn); - tcg_rd = insert ? read_fp_dreg(s, rd) : tcg_temp_new_i64(); +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) - 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); -} - -/* 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; +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) - int i; +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) - assert(size < 4); +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) - if (extract32(immh, 3, 1)) { - unallocated_encoding(s); - return; - } +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) - if (!fp_access_check(s)) { - return; - } +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) - if (is_u_shift) { - narrowfn = unsigned_narrow_fns[size]; - } else { - narrowfn = signed_narrow_fns[size][is_u_narrow ? 1 : 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) - tcg_rn = tcg_temp_new_i64(); - tcg_rd = tcg_temp_new_i64(); - tcg_rd_narrowed = tcg_temp_new_i32(); - tcg_final = tcg_temp_new_i64(); +/* + * Advanced SIMD Scalar Shift by Immediate + */ - if (round) { - tcg_round = tcg_constant_i64(1ULL << (shift - 1)); - } else { - tcg_round = NULL; - } +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(); - 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); - if (i == 0) { - tcg_gen_mov_i64(tcg_final, tcg_rd); - } else { - tcg_gen_deposit_i64(tcg_final, tcg_final, tcg_rd, esize * i, esize); + 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; +} - if (!is_q) { - write_vec_element(s, tcg_final, rd, 0, MO_64); - } else { - write_vec_element(s, tcg_final, rd, 1, MO_64); - } - clear_vec_high(s, is_q, rd); +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); } -/* 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) +static void gen_sqshli_b(TCGv_i64 d, TCGv_i64 s, int64_t i) { - int immhb = immh << 3 | immb; - int size = 32 - clz32(immh) - 1; - int shift = immhb - (8 << size); - int pass; + trunc_i64_env_imm(d, s, i, gen_helper_neon_qshl_s8); +} - assert(immh != 0); - assert(!(scalar && is_q)); +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); +} - if (!scalar) { - if (!is_q && extract32(immh, 3, 1)) { - unallocated_encoding(s); - return; - } +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); +} - /* 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(); - } - } +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)); +} - if (!fp_access_check(s)) { - return; - } +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); +} - 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; +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); +} - for (pass = 0; pass < maxpass; pass++) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); +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); +} - 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); - } - 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); - } - } +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)); +} - if (!scalar) { - clear_vec_high(s, is_q, rd); - } - } +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); } -/* 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) +static void gen_sqshlui_h(TCGv_i64 d, TCGv_i64 s, int64_t i) { - TCGv_ptr tcg_fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); - TCGv_i32 tcg_shift = NULL; + trunc_i64_env_imm(d, s, i, gen_helper_neon_qshlu_s16); +} - MemOp mop = size | (is_signed ? MO_SIGN : 0); - int pass; +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); +} - if (fracbits || size == MO_64) { - tcg_shift = tcg_constant_i32(fracbits); - } +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)); +} - if (size == MO_64) { - TCGv_i64 tcg_int64 = tcg_temp_new_i64(); - TCGv_i64 tcg_double = tcg_temp_new_i64(); +static WideShiftImmFn * const f_scalar_sqshli[] = { + gen_sqshli_b, gen_sqshli_h, gen_sqshli_s, gen_sqshli_d +}; - for (pass = 0; pass < elements; pass++) { - read_vec_element(s, tcg_int64, rn, pass, mop); +static WideShiftImmFn * const f_scalar_uqshli[] = { + gen_uqshli_b, gen_uqshli_h, gen_uqshli_s, gen_uqshli_d +}; - 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(); - } +static WideShiftImmFn * const f_scalar_sqshlui[] = { + gen_sqshlui_b, gen_sqshlui_h, gen_sqshlui_s, gen_sqshlui_d +}; - if (elements == 1) { - write_fp_sreg(s, rd, tcg_float); - } else { - write_vec_element_i32(s, tcg_float, rd, pass, size); - } +/* 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); } - - clear_vec_high(s, elements << size == 16, rd); + return true; } -/* 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; +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) - 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; - } +static bool do_div(DisasContext *s, arg_rrr_sf *a, bool is_signed) +{ + TCGv_i64 tcg_n, tcg_m, tcg_rd; + tcg_rd = cpu_reg(s, a->rd); + + if (!a->sf && is_signed) { + tcg_n = tcg_temp_new_i64(); + tcg_m = tcg_temp_new_i64(); + tcg_gen_ext32s_i64(tcg_n, cpu_reg(s, a->rn)); + tcg_gen_ext32s_i64(tcg_m, cpu_reg(s, a->rm)); } else { - /* immh == 0 would be a failure of the decode logic */ - g_assert(immh == 1); - unallocated_encoding(s); - return; + tcg_n = read_cpu_reg(s, a->rn, a->sf); + tcg_m = read_cpu_reg(s, a->rm, a->sf); } - if (is_scalar) { - elements = 1; + if (is_signed) { + gen_helper_sdiv64(tcg_rd, tcg_n, tcg_m); } else { - elements = (8 << is_q) >> size; + gen_helper_udiv64(tcg_rd, tcg_n, tcg_m); } - fracbits = (16 << size) - immhb; - if (!fp_access_check(s)) { - return; + if (!a->sf) { /* zero extend final result */ + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } - - handle_simd_intfp_conv(s, rd, rn, elements, !is_u, fracbits, size); + return true; } -/* 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; +TRANS(SDIV, do_div, a, true) +TRANS(UDIV, do_div, a, false) - if (immh & 0x8) { - size = MO_64; - if (!is_scalar && !is_q) { - unallocated_encoding(s); - return; +/* 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); } - } 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; + 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); } - } else { - /* Should have split out AdvSIMD modified immediate earlier. */ - assert(immh == 1); - unallocated_encoding(s); - return; + break; + default: + assert(FALSE); /* all shift types should be handled */ + break; } - if (!fp_access_check(s)) { - return; + if (!sf) { /* zero extend final result */ + tcg_gen_ext32u_i64(dst, dst); } +} - 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); +/* 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 (size == MO_64) { - int maxpass = is_scalar ? 1 : 2; + if (shift_i == 0) { + tcg_gen_mov_i64(dst, src); + } else { + shift_reg(dst, src, sf, shift_type, tcg_constant_i64(shift_i)); + } +} - for (pass = 0; pass < maxpass; pass++) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); +static bool do_shift_reg(DisasContext *s, arg_rrr_sf *a, + enum a64_shift_type shift_type) +{ + TCGv_i64 tcg_shift = tcg_temp_new_i64(); + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + TCGv_i64 tcg_rn = read_cpu_reg(s, a->rn, a->sf); - 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); + tcg_gen_andi_i64(tcg_shift, cpu_reg(s, a->rm), a->sf ? 63 : 31); + shift_reg(tcg_rd, tcg_rn, a->sf, shift_type, tcg_shift); + return true; +} - 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(); - } +TRANS(LSLV, do_shift_reg, a, A64_SHIFT_TYPE_LSL) +TRANS(LSRV, do_shift_reg, a, A64_SHIFT_TYPE_LSR) +TRANS(ASRV, do_shift_reg, a, A64_SHIFT_TYPE_ASR) +TRANS(RORV, do_shift_reg, a, A64_SHIFT_TYPE_ROR) - for (pass = 0; pass < maxpass; pass++) { - TCGv_i32 tcg_op = tcg_temp_new_i32(); +static bool do_crc32(DisasContext *s, arg_rrr_e *a, bool crc32c) +{ + TCGv_i64 tcg_acc, tcg_val, tcg_rd; + TCGv_i32 tcg_bytes; - 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); - } - } - if (!is_scalar) { - clear_vec_high(s, is_q, rd); - } + switch (a->esz) { + case MO_8: + case MO_16: + case MO_32: + tcg_val = tcg_temp_new_i64(); + tcg_gen_extract_i64(tcg_val, cpu_reg(s, a->rm), 0, 8 << a->esz); + break; + case MO_64: + tcg_val = cpu_reg(s, a->rm); + break; + default: + g_assert_not_reached(); } + tcg_acc = cpu_reg(s, a->rn); + tcg_bytes = tcg_constant_i32(1 << a->esz); + tcg_rd = cpu_reg(s, a->rd); - gen_restore_rmode(tcg_rmode, tcg_fpstatus); + if (crc32c) { + gen_helper_crc32c_64(tcg_rd, tcg_acc, tcg_val, tcg_bytes); + } else { + gen_helper_crc32_64(tcg_rd, tcg_acc, tcg_val, tcg_bytes); + } + return true; } -/* 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) +TRANS_FEAT(CRC32, aa64_crc32, do_crc32, a, false) +TRANS_FEAT(CRC32C, aa64_crc32, do_crc32, a, true) + +static bool do_subp(DisasContext *s, arg_rrr *a, bool setflag) { - 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); + TCGv_i64 tcg_n = read_cpu_reg_sp(s, a->rn, true); + TCGv_i64 tcg_m = read_cpu_reg_sp(s, a->rm, true); + TCGv_i64 tcg_d = cpu_reg(s, a->rd); - if (immh == 0) { - unallocated_encoding(s); - return; - } + tcg_gen_sextract_i64(tcg_n, tcg_n, 0, 56); + tcg_gen_sextract_i64(tcg_m, tcg_m, 0, 56); - 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; + if (setflag) { + gen_sub_CC(true, tcg_d, tcg_n, tcg_m); + } else { + tcg_gen_sub_i64(tcg_d, tcg_n, tcg_m); } + return true; } -/* 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) +TRANS_FEAT(SUBP, aa64_mte_insn_reg, do_subp, a, false) +TRANS_FEAT(SUBPS, aa64_mte_insn_reg, do_subp, a, true) + +static bool trans_IRG(DisasContext *s, arg_rrr *a) { - 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 (dc_isar_feature(aa64_mte_insn_reg, s)) { + TCGv_i64 tcg_rd = cpu_reg_sp(s, a->rd); + TCGv_i64 tcg_rn = cpu_reg_sp(s, a->rn); - if (is_u) { - unallocated_encoding(s); - return; + if (s->ata[0]) { + gen_helper_irg(tcg_rd, tcg_env, tcg_rn, cpu_reg(s, a->rm)); + } else { + gen_address_with_allocation_tag0(tcg_rd, tcg_rn); + } + return true; } + return false; +} - 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; +static bool trans_GMI(DisasContext *s, arg_rrr *a) +{ + if (dc_isar_feature(aa64_mte_insn_reg, s)) { + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_extract_i64(t, cpu_reg_sp(s, a->rn), 56, 4); + tcg_gen_shl_i64(t, tcg_constant_i64(1), t); + tcg_gen_or_i64(cpu_reg(s, a->rd), cpu_reg(s, a->rm), t); + return true; } + return false; +} - if (!fp_access_check(s)) { - return; +static bool trans_PACGA(DisasContext *s, arg_rrr *a) +{ + if (dc_isar_feature(aa64_pauth, s)) { + gen_helper_pacga(cpu_reg(s, a->rd), tcg_env, + cpu_reg(s, a->rn), cpu_reg_sp(s, a->rm)); + return true; } + return false; +} - 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(); +typedef void ArithOneOp(TCGv_i64, TCGv_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); +static bool gen_rr(DisasContext *s, int rd, int rn, ArithOneOp fn) +{ + fn(cpu_reg(s, rd), cpu_reg(s, rn)); + return true; +} - tcg_gen_mul_i64(tcg_res, tcg_op1, tcg_op2); - gen_helper_neon_addl_saturate_s64(tcg_res, cpu_env, tcg_res, tcg_res); +static void gen_rbit32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + TCGv_i32 t32 = tcg_temp_new_i32(); - 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(); - } + tcg_gen_extrl_i64_i32(t32, tcg_rn); + gen_helper_rbit(t32, t32); + tcg_gen_extu_i32_i64(tcg_rd, t32); +} - write_fp_dreg(s, rd, 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(); +static void gen_rev16_xx(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i64 mask) +{ + TCGv_i64 tcg_tmp = 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); + 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); +} - 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); - break; - } - default: - g_assert_not_reached(); - } +static void gen_rev16_32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + gen_rev16_xx(tcg_rd, tcg_rn, tcg_constant_i64(0x00ff00ff)); +} - tcg_gen_ext32u_i64(tcg_res, tcg_res); - write_fp_dreg(s, rd, tcg_res); - } +static void gen_rev16_64(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + gen_rev16_xx(tcg_rd, tcg_rn, tcg_constant_i64(0x00ff00ff00ff00ffull)); } -static void handle_3same_64(DisasContext *s, int opcode, bool u, - TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i64 tcg_rm) +static void gen_rev_32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) { - /* 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; + tcg_gen_bswap32_i64(tcg_rd, tcg_rn, TCG_BSWAP_OZ); +} - 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 */ - cond = u ? TCG_COND_GTU : TCG_COND_GT; - do_cmop: - /* 64 bit integer comparison, result = test ? -1 : 0. */ - tcg_gen_negsetcond_i64(cond, tcg_rd, tcg_rn, tcg_rm); - break; - case 0x7: /* CMGE, CMHS */ - cond = u ? TCG_COND_GEU : TCG_COND_GE; - 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(); - } +static void gen_rev32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + tcg_gen_bswap64_i64(tcg_rd, tcg_rn); + tcg_gen_rotri_i64(tcg_rd, tcg_rd, 32); } -/* 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) +TRANS(RBIT, gen_rr, a->rd, a->rn, a->sf ? gen_helper_rbit64 : gen_rbit32) +TRANS(REV16, gen_rr, a->rd, a->rn, a->sf ? gen_rev16_64 : gen_rev16_32) +TRANS(REV32, gen_rr, a->rd, a->rn, a->sf ? gen_rev32 : gen_rev_32) +TRANS(REV64, gen_rr, a->rd, a->rn, tcg_gen_bswap64_i64) + +static void gen_clz32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) { - 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(); - } + TCGv_i32 t32 = tcg_temp_new_i32(); - write_vec_element(s, tcg_res, rd, pass, MO_64); - } 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(); - } + tcg_gen_extrl_i64_i32(t32, tcg_rn); + tcg_gen_clzi_i32(t32, t32, 32); + tcg_gen_extu_i32_i64(tcg_rd, t32); +} - if (elements == 1) { - /* scalar single so clear high part */ - TCGv_i64 tcg_tmp = tcg_temp_new_i64(); +static void gen_clz64(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + tcg_gen_clzi_i64(tcg_rd, tcg_rn, 64); +} - tcg_gen_extu_i32_i64(tcg_tmp, tcg_res); - write_vec_element(s, tcg_tmp, rd, pass, MO_64); - } else { - write_vec_element_i32(s, tcg_res, rd, pass, MO_32); - } - } - } +static void gen_cls32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + TCGv_i32 t32 = tcg_temp_new_i32(); - clear_vec_high(s, elements * (size ? 8 : 4) > 8, rd); + tcg_gen_extrl_i64_i32(t32, tcg_rn); + tcg_gen_clrsb_i32(t32, t32); + tcg_gen_extu_i32_i64(tcg_rd, t32); } -/* 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; - } +TRANS(CLZ, gen_rr, a->rd, a->rn, a->sf ? gen_clz64 : gen_clz32) +TRANS(CLS, gen_rr, a->rd, a->rn, a->sf ? tcg_gen_clrsb_i64 : gen_cls32) - handle_3same_float(s, extract32(size, 0, 1), 1, fpopcode, rd, rn, rm); - return; - } +static bool gen_pacaut(DisasContext *s, arg_pacaut *a, NeonGenTwo64OpEnvFn fn) +{ + TCGv_i64 tcg_rd, tcg_rn; - 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; + if (a->z) { + if (a->rn != 31) { + return false; } - break; - default: - unallocated_encoding(s); - return; + tcg_rn = tcg_constant_i64(0); + } else { + tcg_rn = cpu_reg_sp(s, a->rn); } - - if (!fp_access_check(s)) { - return; + if (s->pauth_active) { + tcg_rd = cpu_reg(s, a->rd); + fn(tcg_rd, tcg_env, tcg_rd, tcg_rn); } + return true; +} - tcg_rd = tcg_temp_new_i64(); +TRANS_FEAT(PACIA, aa64_pauth, gen_pacaut, a, gen_helper_pacia) +TRANS_FEAT(PACIB, aa64_pauth, gen_pacaut, a, gen_helper_pacib) +TRANS_FEAT(PACDA, aa64_pauth, gen_pacaut, a, gen_helper_pacda) +TRANS_FEAT(PACDB, aa64_pauth, gen_pacaut, a, gen_helper_pacdb) - if (size == 3) { - TCGv_i64 tcg_rn = read_fp_dreg(s, rn); - TCGv_i64 tcg_rm = read_fp_dreg(s, rm); +TRANS_FEAT(AUTIA, aa64_pauth, gen_pacaut, a, gen_helper_autia) +TRANS_FEAT(AUTIB, aa64_pauth, gen_pacaut, a, gen_helper_autib) +TRANS_FEAT(AUTDA, aa64_pauth, gen_pacaut, a, gen_helper_autda) +TRANS_FEAT(AUTDB, aa64_pauth, gen_pacaut, a, gen_helper_autdb) - handle_3same_64(s, opcode, u, tcg_rd, tcg_rn, 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(); +static bool do_xpac(DisasContext *s, int rd, NeonGenOne64OpEnvFn *fn) +{ + if (s->pauth_active) { + TCGv_i64 tcg_rd = cpu_reg(s, rd); + fn(tcg_rd, tcg_env, tcg_rd); + } + return true; +} - read_vec_element_i32(s, tcg_rn, rn, 0, size); - read_vec_element_i32(s, tcg_rm, rm, 0, size); +TRANS_FEAT(XPACI, aa64_pauth, do_xpac, a->rd, gen_helper_xpaci) +TRANS_FEAT(XPACD, aa64_pauth, do_xpac, a->rd, gen_helper_xpacd) - 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(); - } +static bool do_logic_reg(DisasContext *s, arg_logic_shift *a, + ArithTwoOp *fn, ArithTwoOp *inv_fn, bool setflags) +{ + TCGv_i64 tcg_rd, tcg_rn, tcg_rm; - genenvfn(tcg_rd32, cpu_env, tcg_rn, tcg_rm); - tcg_gen_extu_i32_i64(tcg_rd, tcg_rd32); + if (!a->sf && (a->sa & (1 << 5))) { + return false; } - write_fp_dreg(s, rd, tcg_rd); -} + tcg_rd = cpu_reg(s, a->rd); + tcg_rn = cpu_reg(s, a->rn); -/* 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; + tcg_rm = read_cpu_reg(s, a->rm, a->sf); + if (a->sa) { + shift_reg_imm(tcg_rm, tcg_rm, a->sf, a->st, a->sa); } - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); + (a->n ? inv_fn : fn)(tcg_rd, tcg_rn, tcg_rm); + if (!a->sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } - - if (!fp_access_check(s)) { - return; + if (setflags) { + gen_logic_CC(a->sf, tcg_rd); } + return true; +} - 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(); +static bool trans_ORR_r(DisasContext *s, arg_logic_shift *a) +{ + /* + * Unshifted ORR and ORN with WZR/XZR is the standard encoding for + * register-register MOV and MVN, so it is worth special casing. + */ + if (a->sa == 0 && a->st == 0 && a->rn == 31) { + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + TCGv_i64 tcg_rm = cpu_reg(s, a->rm); - 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(); + if (a->n) { + tcg_gen_not_i64(tcg_rd, tcg_rm); + if (!a->sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + } else { + if (a->sf) { + tcg_gen_mov_i64(tcg_rd, tcg_rm); + } else { + tcg_gen_ext32u_i64(tcg_rd, tcg_rm); + } + } + return true; } - write_fp_sreg(s, rd, tcg_res); + return do_logic_reg(s, a, tcg_gen_or_i64, tcg_gen_orc_i64, false); } -/* 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; +TRANS(AND_r, do_logic_reg, a, tcg_gen_and_i64, tcg_gen_andc_i64, false) +TRANS(ANDS_r, do_logic_reg, a, tcg_gen_and_i64, tcg_gen_andc_i64, true) +TRANS(EOR_r, do_logic_reg, a, tcg_gen_xor_i64, tcg_gen_eqv_i64, false) + +static bool do_addsub_ext(DisasContext *s, arg_addsub_ext *a, + bool sub_op, bool setflags) +{ + TCGv_i64 tcg_rm, tcg_rn, tcg_rd, tcg_result; + + if (a->sa > 4) { + return false; } - if (!fp_access_check(s)) { - return; + + /* non-flag setting ops may use SP */ + if (!setflags) { + tcg_rd = cpu_reg_sp(s, a->rd); + } else { + tcg_rd = cpu_reg(s, a->rd); } + tcg_rn = read_cpu_reg_sp(s, a->rn, a->sf); - /* 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); + tcg_rm = read_cpu_reg(s, a->rm, a->sf); + ext_and_shift_reg(tcg_rm, tcg_rm, a->st, a->sa); + + tcg_result = tcg_temp_new_i64(); + if (!setflags) { + if (sub_op) { + tcg_gen_sub_i64(tcg_result, tcg_rn, tcg_rm); } else { - gen_helper_neon_qrdmlah_s32(ele3, cpu_env, ele1, ele2, ele3); + tcg_gen_add_i64(tcg_result, tcg_rn, tcg_rm); } - break; - case 0x1: /* SQRDMLSH */ - if (size == 1) { - gen_helper_neon_qrdmlsh_s16(ele3, cpu_env, ele1, ele2, ele3); + } else { + if (sub_op) { + gen_sub_CC(a->sf, tcg_result, tcg_rn, tcg_rm); } else { - gen_helper_neon_qrdmlsh_s32(ele3, cpu_env, ele1, ele2, ele3); + gen_add_CC(a->sf, tcg_result, tcg_rn, tcg_rm); } - break; - default: - g_assert_not_reached(); } - res = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(res, ele3); - write_fp_dreg(s, rd, res); + if (a->sf) { + tcg_gen_mov_i64(tcg_rd, tcg_result); + } else { + tcg_gen_ext32u_i64(tcg_rd, tcg_result); + } + return true; } -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) +TRANS(ADD_ext, do_addsub_ext, a, false, false) +TRANS(SUB_ext, do_addsub_ext, a, true, false) +TRANS(ADDS_ext, do_addsub_ext, a, false, true) +TRANS(SUBS_ext, do_addsub_ext, a, true, true) + +static bool do_addsub_reg(DisasContext *s, arg_addsub_shift *a, + bool sub_op, bool setflags) { - /* 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; + TCGv_i64 tcg_rd, tcg_rn, tcg_rm, tcg_result; - 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); + if (a->st == 3 || (!a->sf && (a->sa & 32))) { + return false; + } + + tcg_rd = cpu_reg(s, a->rd); + tcg_rn = read_cpu_reg(s, a->rn, a->sf); + tcg_rm = read_cpu_reg(s, a->rm, a->sf); + + shift_reg_imm(tcg_rm, tcg_rm, a->sf, a->st, a->sa); + + tcg_result = tcg_temp_new_i64(); + if (!setflags) { + if (sub_op) { + tcg_gen_sub_i64(tcg_result, tcg_rn, tcg_rm); } else { - gen_helper_neon_qabs_s64(tcg_rd, cpu_env, tcg_rn); + tcg_gen_add_i64(tcg_result, tcg_rn, tcg_rm); } - 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 { + if (sub_op) { + gen_sub_CC(a->sf, tcg_result, tcg_rn, tcg_rm); } else { - tcg_gen_abs_i64(tcg_rd, tcg_rn); + gen_add_CC(a->sf, tcg_result, tcg_rn, tcg_rm); } - 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(); } + + if (a->sf) { + tcg_gen_mov_i64(tcg_rd, tcg_result); + } else { + tcg_gen_ext32u_i64(tcg_rd, tcg_result); + } + return true; } -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; +TRANS(ADD_r, do_addsub_reg, a, false, false) +TRANS(SUB_r, do_addsub_reg, a, true, false) +TRANS(ADDS_r, do_addsub_reg, a, false, true) +TRANS(SUBS_r, do_addsub_reg, a, true, true) - if (!fp_access_check(s)) { - return; - } +static bool do_mulh(DisasContext *s, arg_rrr *a, + void (*fn)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 discard = tcg_temp_new_i64(); + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + TCGv_i64 tcg_rn = cpu_reg(s, a->rn); + TCGv_i64 tcg_rm = cpu_reg(s, a->rm); - fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + fn(discard, tcg_rd, tcg_rn, tcg_rm); + return true; +} - 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; +TRANS(SMULH, do_mulh, a, tcg_gen_muls2_i64) +TRANS(UMULH, do_mulh, a, tcg_gen_mulu2_i64) - 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(); - } +static bool do_muladd(DisasContext *s, arg_rrrr *a, + bool sf, bool is_sub, MemOp mop) +{ + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + TCGv_i64 tcg_op1, tcg_op2; - 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); - } + if (mop == MO_64) { + tcg_op1 = cpu_reg(s, a->rn); + tcg_op2 = cpu_reg(s, a->rm); + } else { + tcg_op1 = tcg_temp_new_i64(); + tcg_op2 = tcg_temp_new_i64(); + tcg_gen_ext_i64(tcg_op1, cpu_reg(s, a->rn), mop); + tcg_gen_ext_i64(tcg_op2, cpu_reg(s, a->rm), mop); + } - clear_vec_high(s, !is_scalar, rd); + if (a->ra == 31 && !is_sub) { + /* Special-case MADD with rA == XZR; it is the standard MUL alias */ + tcg_gen_mul_i64(tcg_rd, tcg_op1, tcg_op2); } 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(); - } - } + TCGv_i64 tcg_tmp = tcg_temp_new_i64(); + TCGv_i64 tcg_ra = cpu_reg(s, a->ra); - if (is_scalar) { - maxpasses = 1; + tcg_gen_mul_i64(tcg_tmp, tcg_op1, tcg_op2); + if (is_sub) { + tcg_gen_sub_i64(tcg_rd, tcg_ra, tcg_tmp); } 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_gen_add_i64(tcg_rd, tcg_ra, tcg_tmp); } + } - if (!is_scalar) { - clear_vec_high(s, is_q, rd); - } + if (!sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } + return true; } -static void handle_2misc_reciprocal(DisasContext *s, int opcode, - bool is_scalar, bool is_u, bool is_q, - int size, int rn, int rd) +TRANS(MADD_w, do_muladd, a, false, false, MO_64) +TRANS(MSUB_w, do_muladd, a, false, true, MO_64) +TRANS(MADD_x, do_muladd, a, true, false, MO_64) +TRANS(MSUB_x, do_muladd, a, true, true, MO_64) + +TRANS(SMADDL, do_muladd, a, true, false, MO_SL) +TRANS(SMSUBL, do_muladd, a, true, true, MO_SL) +TRANS(UMADDL, do_muladd, a, true, false, MO_UL) +TRANS(UMSUBL, do_muladd, a, true, true, MO_UL) + +static bool do_adc_sbc(DisasContext *s, arg_rrr_sf *a, + bool is_sub, bool setflags) { - bool is_double = (size == 3); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + TCGv_i64 tcg_y, tcg_rn, tcg_rd; - if (is_double) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - int pass; + tcg_rd = cpu_reg(s, a->rd); + tcg_rn = cpu_reg(s, a->rn); - 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); + if (is_sub) { + tcg_y = tcg_temp_new_i64(); + tcg_gen_not_i64(tcg_y, cpu_reg(s, a->rm)); } 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(); - } + tcg_y = cpu_reg(s, a->rm); + } - 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); - } + if (setflags) { + gen_adc_CC(a->sf, tcg_rd, tcg_rn, tcg_y); + } else { + gen_adc(a->sf, tcg_rd, tcg_rn, tcg_y); } + return true; } -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; +TRANS(ADC, do_adc_sbc, a, false, false) +TRANS(SBC, do_adc_sbc, a, true, false) +TRANS(ADCS, do_adc_sbc, a, false, true) +TRANS(SBCS, do_adc_sbc, a, true, true) - if (scalar) { - tcg_res[1] = tcg_constant_i32(0); - } +static bool trans_RMIF(DisasContext *s, arg_RMIF *a) +{ + int mask = a->mask; + TCGv_i64 tcg_rn; + TCGv_i32 nzcv; - for (pass = 0; pass < passes; pass++) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - NeonGenNarrowFn *genfn = NULL; - NeonGenNarrowEnvFn *genenvfn = NULL; + if (!dc_isar_feature(aa64_condm_4, s)) { + return false; + } - 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(); + tcg_rn = read_cpu_reg(s, a->rn, 1); + tcg_gen_rotri_i64(tcg_rn, tcg_rn, a->imm); - 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); - } - break; - case 0x36: /* BFCVTN, BFCVTN2 */ - { - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - gen_helper_bfcvt_pair(tcg_res[pass], tcg_op, 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(); - } + nzcv = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(nzcv, tcg_rn); - if (genfn) { - genfn(tcg_res[pass], tcg_op); - } else if (genenvfn) { - genenvfn(tcg_res[pass], cpu_env, tcg_op); - } + if (mask & 8) { /* N */ + tcg_gen_shli_i32(cpu_NF, nzcv, 31 - 3); } - - for (pass = 0; pass < 2; pass++) { - write_vec_element_i32(s, tcg_res[pass], rd, destelt + pass, MO_32); + 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); } - clear_vec_high(s, is_q, rd); + return true; } -/* 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) +static bool do_setf(DisasContext *s, int rn, int shift) { - bool is_double = (size == 3); + TCGv_i32 tmp = tcg_temp_new_i32(); - if (is_double) { - TCGv_i64 tcg_rn = tcg_temp_new_i64(); - TCGv_i64 tcg_rd = tcg_temp_new_i64(); - int pass; + 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); + return true; +} - 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); +TRANS_FEAT(SETF8, aa64_condm_4, do_setf, a->rn, 24) +TRANS_FEAT(SETF16, aa64_condm_4, do_setf, a->rn, 16) - 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); - } - clear_vec_high(s, !is_scalar, rd); +/* CCMP, CCMN */ +static bool trans_CCMP(DisasContext *s, arg_CCMP *a) +{ + TCGv_i32 tcg_t0 = tcg_temp_new_i32(); + TCGv_i32 tcg_t1 = tcg_temp_new_i32(); + TCGv_i32 tcg_t2 = tcg_temp_new_i32(); + TCGv_i64 tcg_tmp = tcg_temp_new_i64(); + TCGv_i64 tcg_rn, tcg_y; + DisasCompare c; + unsigned nzcv; + bool has_andc; + + /* Set T0 = !COND. */ + arm_test_cc(&c, a->cond); + tcg_gen_setcondi_i32(tcg_invert_cond(c.cond), tcg_t0, c.value, 0); + + /* Load the arguments for the new comparison. */ + if (a->imm) { + tcg_y = tcg_constant_i64(a->y); } else { - TCGv_i32 tcg_rn = tcg_temp_new_i32(); - TCGv_i32 tcg_rd = tcg_temp_new_i32(); - int pass, maxpasses; + tcg_y = cpu_reg(s, a->y); + } + tcg_rn = cpu_reg(s, a->rn); + + /* Set the flags for the new comparison. */ + if (a->op) { + gen_sub_CC(a->sf, tcg_tmp, tcg_rn, tcg_y); + } else { + gen_add_CC(a->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_gen_neg_i32(tcg_t1, tcg_t0); + tcg_gen_subi_i32(tcg_t2, tcg_t0, 1); - if (is_scalar) { - maxpasses = 1; + nzcv = a->nzcv; + has_andc = tcg_op_supported(INDEX_op_andc, TCG_TYPE_I32, 0); + if (nzcv & 8) { /* N */ + tcg_gen_or_i32(cpu_NF, cpu_NF, tcg_t1); + } else { + if (has_andc) { + 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 (has_andc) { + 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 (has_andc) { + 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 (has_andc) { + tcg_gen_andc_i32(cpu_VF, cpu_VF, tcg_t1); } else { - maxpasses = is_q ? 4 : 2; + tcg_gen_and_i32(cpu_VF, cpu_VF, tcg_t2); } + } + return true; +} - 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); - } +static bool trans_CSEL(DisasContext *s, arg_CSEL *a) +{ + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + TCGv_i64 zero = tcg_constant_i64(0); + DisasCompare64 c; - 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(); - } - } + a64_test_cc(&c, a->cond); - 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); + if (a->rn == 31 && a->rm == 31 && (a->else_inc ^ a->else_inv)) { + /* CSET & CSETM. */ + if (a->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, a->rn); + TCGv_i64 t_false = read_cpu_reg(s, a->rm, 1); + + if (a->else_inv && a->else_inc) { + tcg_gen_neg_i64(t_false, t_false); + } else if (a->else_inv) { + tcg_gen_not_i64(t_false, t_false); + } else if (a->else_inc) { + tcg_gen_addi_i64(t_false, t_false, 1); } - clear_vec_high(s, is_q, rd); + tcg_gen_movcond_i64(c.cond, tcg_rd, c.value, zero, t_true, t_false); + } + + if (!a->sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } + return true; } -/* 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; +typedef struct FPScalar1Int { + void (*gen_h)(TCGv_i32, TCGv_i32); + void (*gen_s)(TCGv_i32, TCGv_i32); + void (*gen_d)(TCGv_i64, TCGv_i64); +} FPScalar1Int; - switch (opcode) { - case 0x3: /* USQADD / SUQADD*/ - if (!fp_access_check(s)) { - return; +static bool do_fp1_scalar_int(DisasContext *s, arg_rr_e *a, + const FPScalar1Int *f, + bool merging) +{ + switch (a->esz) { + case MO_64: + if (fp_access_check(s)) { + TCGv_i64 t = read_fp_dreg(s, a->rn); + f->gen_d(t, t); + if (merging) { + write_fp_dreg_merging(s, a->rd, a->rd, t); + } else { + write_fp_dreg(s, a->rd, t); + } } - 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; + case MO_32: + if (fp_access_check(s)) { + TCGv_i32 t = read_fp_sreg(s, a->rn); + f->gen_s(t, t); + if (merging) { + write_fp_sreg_merging(s, a->rd, a->rd, t); + } else { + write_fp_sreg(s, a->rd, t); + } } 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 MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; } - 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; + if (fp_access_check(s)) { + TCGv_i32 t = read_fp_hreg(s, a->rn); + f->gen_h(t, t); + if (merging) { + write_fp_hreg_merging(s, a->rd, a->rd, t); + } else { + write_fp_sreg(s, a->rd, t); } - 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_fpstatus = fpstatus_ptr(FPST_FPCR); - tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); - } else { - tcg_fpstatus = NULL; - tcg_rmode = NULL; + return false; } + return true; +} - 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(); +static bool do_fp1_scalar_int_2fn(DisasContext *s, arg_rr_e *a, + const FPScalar1Int *fnormal, + const FPScalar1Int *fah) +{ + return do_fp1_scalar_int(s, a, s->fpcr_ah ? fah : fnormal, true); +} - read_vec_element_i32(s, tcg_rn, rn, 0, size); +static const FPScalar1Int f_scalar_fmov = { + tcg_gen_mov_i32, + tcg_gen_mov_i32, + tcg_gen_mov_i64, +}; +TRANS(FMOV_s, do_fp1_scalar_int, a, &f_scalar_fmov, false) - 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(); - } +static const FPScalar1Int f_scalar_fabs = { + gen_vfp_absh, + gen_vfp_abss, + gen_vfp_absd, +}; +static const FPScalar1Int f_scalar_ah_fabs = { + gen_vfp_ah_absh, + gen_vfp_ah_abss, + gen_vfp_ah_absd, +}; +TRANS(FABS_s, do_fp1_scalar_int_2fn, a, &f_scalar_fabs, &f_scalar_ah_fabs) - write_fp_sreg(s, rd, tcg_rd); - } +static const FPScalar1Int f_scalar_fneg = { + gen_vfp_negh, + gen_vfp_negs, + gen_vfp_negd, +}; +static const FPScalar1Int f_scalar_ah_fneg = { + gen_vfp_ah_negh, + gen_vfp_ah_negs, + gen_vfp_ah_negd, +}; +TRANS(FNEG_s, do_fp1_scalar_int_2fn, a, &f_scalar_fneg, &f_scalar_ah_fneg) - if (is_fcvt) { - gen_restore_rmode(tcg_rmode, tcg_fpstatus); - } -} +typedef struct FPScalar1 { + void (*gen_h)(TCGv_i32, TCGv_i32, TCGv_ptr); + void (*gen_s)(TCGv_i32, TCGv_i32, TCGv_ptr); + void (*gen_d)(TCGv_i64, TCGv_i64, TCGv_ptr); +} FPScalar1; -/* 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) +static bool do_fp1_scalar_with_fpsttype(DisasContext *s, arg_rr_e *a, + const FPScalar1 *f, int rmode, + ARMFPStatusFlavour fpsttype) { - int size = 32 - clz32(immh) - 1; - int immhb = immh << 3 | immb; - int shift = 2 * (8 << size) - immhb; - GVecGen2iFn *gvec_fn; + TCGv_i32 tcg_rmode = NULL; + TCGv_ptr fpst; + TCGv_i64 t64; + TCGv_i32 t32; + int check = fp_access_check_scalar_hsd(s, a->esz); - if (extract32(immh, 3, 1) && !is_q) { - unallocated_encoding(s); - return; + if (check <= 0) { + return check == 0; } - tcg_debug_assert(size <= 3); - if (!fp_access_check(s)) { - return; + fpst = fpstatus_ptr(fpsttype); + if (rmode >= 0) { + tcg_rmode = gen_set_rmode(rmode, fpst); } - 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; - } + switch (a->esz) { + case MO_64: + t64 = read_fp_dreg(s, a->rn); + f->gen_d(t64, t64, fpst); + write_fp_dreg_merging(s, a->rd, a->rd, t64); break; - - case 0x04: /* SRSHR / URSHR (rounding) */ - gvec_fn = is_u ? gen_gvec_urshr : gen_gvec_srshr; + case MO_32: + t32 = read_fp_sreg(s, a->rn); + f->gen_s(t32, t32, fpst); + write_fp_sreg_merging(s, a->rd, a->rd, t32); break; - - case 0x06: /* SRSRA / URSRA (accum + rounding) */ - gvec_fn = is_u ? gen_gvec_ursra : gen_gvec_srsra; + case MO_16: + t32 = read_fp_hreg(s, a->rn); + f->gen_h(t32, t32, fpst); + write_fp_hreg_merging(s, a->rd, a->rd, t32); break; - default: g_assert_not_reached(); } - gen_gvec_fn2i(s, is_q, rd, rn, shift, gvec_fn, size); + if (rmode >= 0) { + gen_restore_rmode(tcg_rmode, fpst); + } + return true; } -/* 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) +static bool do_fp1_scalar(DisasContext *s, arg_rr_e *a, + const FPScalar1 *f, int rmode) { - int size = 32 - clz32(immh) - 1; - int immhb = immh << 3 | immb; - int shift = immhb - (8 << size); + return do_fp1_scalar_with_fpsttype(s, a, f, rmode, + a->esz == MO_16 ? + FPST_A64_F16 : FPST_A64); +} - /* Range of size is limited by decode: immh is a non-zero 4 bit field */ - assert(size >= 0 && size <= 3); +static bool do_fp1_scalar_ah(DisasContext *s, arg_rr_e *a, + const FPScalar1 *f, int rmode) +{ + return do_fp1_scalar_with_fpsttype(s, a, f, rmode, select_ah_fpst(s, a->esz)); +} - if (extract32(immh, 3, 1) && !is_q) { - unallocated_encoding(s); - return; - } +static const FPScalar1 f_scalar_fsqrt = { + gen_helper_vfp_sqrth, + gen_helper_vfp_sqrts, + gen_helper_vfp_sqrtd, +}; +TRANS(FSQRT_s, do_fp1_scalar, a, &f_scalar_fsqrt, -1) - if (!fp_access_check(s)) { - return; - } +static const FPScalar1 f_scalar_frint = { + gen_helper_advsimd_rinth, + gen_helper_rints, + gen_helper_rintd, +}; +TRANS(FRINTN_s, do_fp1_scalar, a, &f_scalar_frint, FPROUNDING_TIEEVEN) +TRANS(FRINTP_s, do_fp1_scalar, a, &f_scalar_frint, FPROUNDING_POSINF) +TRANS(FRINTM_s, do_fp1_scalar, a, &f_scalar_frint, FPROUNDING_NEGINF) +TRANS(FRINTZ_s, do_fp1_scalar, a, &f_scalar_frint, FPROUNDING_ZERO) +TRANS(FRINTA_s, do_fp1_scalar, a, &f_scalar_frint, FPROUNDING_TIEAWAY) +TRANS(FRINTI_s, do_fp1_scalar, a, &f_scalar_frint, -1) + +static const FPScalar1 f_scalar_frintx = { + gen_helper_advsimd_rinth_exact, + gen_helper_rints_exact, + gen_helper_rintd_exact, +}; +TRANS(FRINTX_s, do_fp1_scalar, a, &f_scalar_frintx, -1) - 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); +static bool trans_BFCVT_s(DisasContext *s, arg_rr_e *a) +{ + ARMFPStatusFlavour fpsttype = s->fpcr_ah ? FPST_AH : FPST_A64; + TCGv_i32 t32; + int check; + + if (!dc_isar_feature(aa64_bf16, s)) { + return false; } -} -/* 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 = tcg_temp_new_i64(); - TCGv_i64 tcg_rd = tcg_temp_new_i64(); - int i; + check = fp_access_check_scalar_hsd(s, a->esz); - if (size >= 3) { - unallocated_encoding(s); - return; + if (check <= 0) { + return check == 0; } - 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); - } - - 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; - } + t32 = read_fp_sreg(s, a->rn); + gen_helper_bfcvt(t32, t32, fpstatus_ptr(fpsttype)); + write_fp_hreg_merging(s, a->rd, a->rd, t32); + return true; } -/* 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); +static const FPScalar1 f_scalar_frint32 = { + NULL, + gen_helper_frint32_s, + gen_helper_frint32_d, +}; +TRANS_FEAT(FRINT32Z_s, aa64_frint, do_fp1_scalar, a, + &f_scalar_frint32, FPROUNDING_ZERO) +TRANS_FEAT(FRINT32X_s, aa64_frint, do_fp1_scalar, a, &f_scalar_frint32, -1) + +static const FPScalar1 f_scalar_frint64 = { + NULL, + gen_helper_frint64_s, + gen_helper_frint64_d, +}; +TRANS_FEAT(FRINT64Z_s, aa64_frint, do_fp1_scalar, a, + &f_scalar_frint64, FPROUNDING_ZERO) +TRANS_FEAT(FRINT64X_s, aa64_frint, do_fp1_scalar, a, &f_scalar_frint64, -1) + +static const FPScalar1 f_scalar_frecpe = { + gen_helper_recpe_f16, + gen_helper_recpe_f32, + gen_helper_recpe_f64, +}; +static const FPScalar1 f_scalar_frecpe_rpres = { + gen_helper_recpe_f16, + gen_helper_recpe_rpres_f32, + gen_helper_recpe_f64, +}; +TRANS(FRECPE_s, do_fp1_scalar_ah, a, + s->fpcr_ah && dc_isar_feature(aa64_rpres, s) ? + &f_scalar_frecpe_rpres : &f_scalar_frecpe, -1) + +static const FPScalar1 f_scalar_frecpx = { + gen_helper_frecpx_f16, + gen_helper_frecpx_f32, + gen_helper_frecpx_f64, +}; +TRANS(FRECPX_s, do_fp1_scalar_ah, a, &f_scalar_frecpx, -1) - genfn = fns[size][is_sub]; - genfn(tcg_res, tcg_op1, tcg_op2); -} +static const FPScalar1 f_scalar_frsqrte = { + gen_helper_rsqrte_f16, + gen_helper_rsqrte_f32, + gen_helper_rsqrte_f64, +}; +static const FPScalar1 f_scalar_frsqrte_rpres = { + gen_helper_rsqrte_f16, + gen_helper_rsqrte_rpres_f32, + gen_helper_rsqrte_f64, +}; +TRANS(FRSQRTE_s, do_fp1_scalar_ah, a, + s->fpcr_ah && dc_isar_feature(aa64_rpres, s) ? + &f_scalar_frsqrte_rpres : &f_scalar_frsqrte, -1) -static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, - int opcode, int rd, int rn, int rm) +static bool trans_FCVT_s_ds(DisasContext *s, arg_rr *a) { - /* 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); - 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); - } - } - } 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); - 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(); - } + if (fp_access_check(s)) { + TCGv_i32 tcg_rn = read_fp_sreg(s, a->rn); + TCGv_i64 tcg_rd = tcg_temp_new_i64(); + TCGv_ptr fpst = fpstatus_ptr(FPST_A64); - 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); - } - } - } + gen_helper_vfp_fcvtds(tcg_rd, tcg_rn, fpst); + write_fp_dreg_merging(s, a->rd, a->rd, tcg_rd); } - - write_vec_element(s, tcg_res[0], rd, 0, MO_64); - write_vec_element(s, tcg_res[1], rd, 1, MO_64); + return true; } -static void handle_3rd_wide(DisasContext *s, int is_q, int is_u, int size, - int opcode, int rd, int rn, int rm) +static bool trans_FCVT_s_hs(DisasContext *s, arg_rr *a) { - 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_res[pass] = tcg_temp_new_i64(); - gen_neon_addl(size, (opcode == 3), - tcg_res[pass], tcg_op1, tcg_op2_wide); - } + if (fp_access_check(s)) { + TCGv_i32 tmp = read_fp_sreg(s, a->rn); + TCGv_i32 ahp = get_ahp_flag(); + TCGv_ptr fpst = fpstatus_ptr(FPST_A64); - for (pass = 0; pass < 2; pass++) { - write_vec_element(s, tcg_res[pass], rd, pass, MO_64); + gen_helper_vfp_fcvt_f32_to_f16(tmp, tmp, fpst, ahp); + /* write_fp_hreg_merging is OK here because top half of result is zero */ + write_fp_hreg_merging(s, a->rd, a->rd, tmp); } + return true; } -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) +static bool trans_FCVT_s_sd(DisasContext *s, arg_rr *a) { - 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_res[pass] = tcg_temp_new_i32(); - gennarrow(tcg_res[pass], tcg_wideres); - } + if (fp_access_check(s)) { + TCGv_i64 tcg_rn = read_fp_dreg(s, a->rn); + TCGv_i32 tcg_rd = tcg_temp_new_i32(); + TCGv_ptr fpst = fpstatus_ptr(FPST_A64); - for (pass = 0; pass < 2; pass++) { - write_vec_element_i32(s, tcg_res[pass], rd, pass + part, MO_32); + gen_helper_vfp_fcvtsd(tcg_rd, tcg_rn, fpst); + write_fp_sreg_merging(s, a->rd, a->rd, tcg_rd); } - clear_vec_high(s, is_q, rd); + return true; } -/* 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; - } +static bool trans_FCVT_s_hd(DisasContext *s, arg_rr *a) +{ + if (fp_access_check(s)) { + TCGv_i64 tcg_rn = read_fp_dreg(s, a->rn); + TCGv_i32 tcg_rd = tcg_temp_new_i32(); + TCGv_i32 ahp = get_ahp_flag(); + TCGv_ptr fpst = fpstatus_ptr(FPST_A64); - handle_3rd_widening(s, is_q, is_u, size, opcode, rd, rn, rm); - break; - default: - /* opcode 15 not allocated */ - unallocated_encoding(s); - break; + gen_helper_vfp_fcvt_f64_to_f16(tcg_rd, tcg_rn, fpst, ahp); + /* write_fp_hreg_merging is OK here because top half of tcg_rd is zero */ + write_fp_hreg_merging(s, a->rd, a->rd, tcg_rd); } + return true; } -/* Logic op (opcode == 3) subgroup of C3.6.16. */ -static void disas_simd_3same_logic(DisasContext *s, uint32_t insn) +static bool trans_FCVT_s_sh(DisasContext *s, arg_rr *a) { - 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; + if (fp_access_check(s)) { + TCGv_i32 tcg_rn = read_fp_hreg(s, a->rn); + TCGv_i32 tcg_rd = tcg_temp_new_i32(); + TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_A64_F16); + TCGv_i32 tcg_ahp = get_ahp_flag(); - default: - g_assert_not_reached(); + gen_helper_vfp_fcvt_f16_to_f32(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); + write_fp_sreg_merging(s, a->rd, a->rd, tcg_rd); } + return true; } -/* 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) +static bool trans_FCVT_s_dh(DisasContext *s, arg_rr *a) { - 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(); - } - } - - for (pass = 0; pass < 2; pass++) { - write_vec_element(s, tcg_res[pass], rd, pass, MO_64); - } - } 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); - } - } + if (fp_access_check(s)) { + TCGv_i32 tcg_rn = read_fp_hreg(s, a->rn); + TCGv_i64 tcg_rd = tcg_temp_new_i64(); + TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_A64_F16); + TCGv_i32 tcg_ahp = get_ahp_flag(); - for (pass = 0; pass < maxpass; pass++) { - write_vec_element_i32(s, tcg_res[pass], rd, pass, MO_32); - } - clear_vec_high(s, is_q, rd); + gen_helper_vfp_fcvt_f16_to_f64(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); + write_fp_dreg_merging(s, a->rd, a->rd, tcg_rd); } + return true; } -/* Floating point op subgroup of C3.6.16. */ -static void disas_simd_3same_float(DisasContext *s, uint32_t insn) +static bool do_cvtf_scalar(DisasContext *s, MemOp esz, int rd, int shift, + TCGv_i64 tcg_int, bool is_signed) { - /* 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); - } - } 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); - } - } - 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); - } - } 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); - } - } - - 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); - } - 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 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; - 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: - 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, 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); - } - } - 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); - } -} - -/* 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; + TCGv_ptr tcg_fpstatus; + TCGv_i32 tcg_shift, tcg_single; + TCGv_i64 tcg_double; + + tcg_fpstatus = fpstatus_ptr(esz == MO_16 ? FPST_A64_F16 : FPST_A64); + tcg_shift = tcg_constant_i32(shift); + + switch (esz) { + case MO_64: + 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_merging(s, rd, rd, tcg_double); break; - default: /* integer */ - switch (size) { - case MO_8: - case MO_64: - unallocated_encoding(s); - return; + case MO_32: + 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_merging(s, rd, rd, tcg_single); 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; + 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); } - index = h; - rm |= m << 4; + write_fp_hreg_merging(s, rd, rd, tcg_single); break; + default: g_assert_not_reached(); } + return true; +} - if (!fp_access_check(s)) { - return; +static bool do_cvtf_g(DisasContext *s, arg_fcvt *a, bool is_signed) +{ + TCGv_i64 tcg_int; + int check = fp_access_check_scalar_hsd(s, a->esz); + + if (check <= 0) { + return check == 0; } - if (is_fp) { - fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR); + if (a->sf) { + tcg_int = cpu_reg(s, a->rn); } else { - fpst = NULL; + tcg_int = read_cpu_reg(s, a->rn, true); + if (is_signed) { + tcg_gen_ext32s_i64(tcg_int, tcg_int); + } else { + tcg_gen_ext32u_i64(tcg_int, tcg_int); + } } + return do_cvtf_scalar(s, a->esz, a->rd, a->shift, tcg_int, is_signed); +} - 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); - } - return; +TRANS(SCVTF_g, do_cvtf_g, a, true) +TRANS(UCVTF_g, do_cvtf_g, a, false) - 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; +/* + * [US]CVTF (vector), scalar version. + * Which sounds weird, but really just means input from fp register + * instead of input from general register. Input and output element + * size are always equal. + */ +static bool do_cvtf_f(DisasContext *s, arg_fcvt *a, bool is_signed) +{ + TCGv_i64 tcg_int; + int check = fp_access_check_scalar_hsd(s, a->esz); - 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; + if (check <= 0) { + return check == 0; + } + + tcg_int = tcg_temp_new_i64(); + read_vec_element(s, tcg_int, a->rn, 0, a->esz | (is_signed ? MO_SIGN : 0)); + return do_cvtf_scalar(s, a->esz, a->rd, a->shift, tcg_int, is_signed); +} + +TRANS(SCVTF_f, do_cvtf_f, a, true) +TRANS(UCVTF_f, do_cvtf_f, a, false) + +static void do_fcvt_scalar(DisasContext *s, MemOp out, MemOp esz, + TCGv_i64 tcg_out, int shift, int rn, + ARMFPRounding rmode) +{ + TCGv_ptr tcg_fpstatus; + TCGv_i32 tcg_shift, tcg_rmode, tcg_single; + + tcg_fpstatus = fpstatus_ptr(esz == MO_16 ? FPST_A64_F16 : FPST_A64); + tcg_shift = tcg_constant_i32(shift); + tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); + + switch (esz) { + case MO_64: + read_vec_element(s, tcg_out, rn, 0, MO_64); + switch (out) { + case MO_64 | MO_SIGN: + gen_helper_vfp_tosqd(tcg_out, tcg_out, tcg_shift, tcg_fpstatus); + break; + case MO_64: + gen_helper_vfp_touqd(tcg_out, tcg_out, tcg_shift, tcg_fpstatus); + break; + case MO_32 | MO_SIGN: + gen_helper_vfp_tosld(tcg_out, tcg_out, tcg_shift, tcg_fpstatus); + break; + case MO_32: + gen_helper_vfp_tould(tcg_out, tcg_out, tcg_shift, tcg_fpstatus); + break; + default: + g_assert_not_reached(); } 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; + case MO_32: + tcg_single = read_fp_sreg(s, rn); + switch (out) { + case MO_64 | MO_SIGN: + gen_helper_vfp_tosqs(tcg_out, tcg_single, tcg_shift, tcg_fpstatus); + break; + case MO_64: + gen_helper_vfp_touqs(tcg_out, tcg_single, tcg_shift, tcg_fpstatus); + break; + case MO_32 | MO_SIGN: + gen_helper_vfp_tosls(tcg_single, tcg_single, + tcg_shift, tcg_fpstatus); + tcg_gen_extu_i32_i64(tcg_out, tcg_single); + break; + case MO_32: + gen_helper_vfp_touls(tcg_single, tcg_single, + tcg_shift, tcg_fpstatus); + tcg_gen_extu_i32_i64(tcg_out, tcg_single); + break; + default: + g_assert_not_reached(); } 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; + case MO_16: + tcg_single = read_fp_hreg(s, rn); + switch (out) { + case MO_64 | MO_SIGN: + gen_helper_vfp_tosqh(tcg_out, tcg_single, tcg_shift, tcg_fpstatus); + break; + case MO_64: + gen_helper_vfp_touqh(tcg_out, tcg_single, tcg_shift, tcg_fpstatus); + break; + case MO_32 | MO_SIGN: + gen_helper_vfp_toslh(tcg_single, tcg_single, + tcg_shift, tcg_fpstatus); + tcg_gen_extu_i32_i64(tcg_out, tcg_single); + break; + case MO_32: + gen_helper_vfp_toulh(tcg_single, tcg_single, + tcg_shift, tcg_fpstatus); + tcg_gen_extu_i32_i64(tcg_out, tcg_single); + break; + case MO_16 | MO_SIGN: + gen_helper_vfp_toshh(tcg_single, tcg_single, + tcg_shift, tcg_fpstatus); + tcg_gen_extu_i32_i64(tcg_out, tcg_single); + break; + case MO_16: + gen_helper_vfp_touhh(tcg_single, tcg_single, + tcg_shift, tcg_fpstatus); + tcg_gen_extu_i32_i64(tcg_out, tcg_single); + break; + default: + g_assert_not_reached(); } break; + + default: + g_assert_not_reached(); } - 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(); - } + gen_restore_rmode(tcg_rmode, tcg_fpstatus); +} - write_vec_element(s, tcg_res, rd, pass, MO_64); - } +static bool do_fcvt_g(DisasContext *s, arg_fcvt *a, + ARMFPRounding rmode, bool is_signed) +{ + TCGv_i64 tcg_int; + int check = fp_access_check_scalar_hsd(s, a->esz); - 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 (check <= 0) { + return check == 0; + } - if (is_scalar) { - maxpasses = 1; - } else { - maxpasses = is_q ? 4 : 2; - } + tcg_int = cpu_reg(s, a->rd); + do_fcvt_scalar(s, (a->sf ? MO_64 : MO_32) | (is_signed ? MO_SIGN : 0), + a->esz, tcg_int, a->shift, a->rn, rmode); - read_vec_element_i32(s, tcg_idx, rm, index, size); + if (!a->sf) { + tcg_gen_ext32u_i64(tcg_int, tcg_int); + } + return true; +} - 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(); - } +TRANS(FCVTNS_g, do_fcvt_g, a, FPROUNDING_TIEEVEN, true) +TRANS(FCVTNU_g, do_fcvt_g, a, FPROUNDING_TIEEVEN, false) +TRANS(FCVTPS_g, do_fcvt_g, a, FPROUNDING_POSINF, true) +TRANS(FCVTPU_g, do_fcvt_g, a, FPROUNDING_POSINF, false) +TRANS(FCVTMS_g, do_fcvt_g, a, FPROUNDING_NEGINF, true) +TRANS(FCVTMU_g, do_fcvt_g, a, FPROUNDING_NEGINF, false) +TRANS(FCVTZS_g, do_fcvt_g, a, FPROUNDING_ZERO, true) +TRANS(FCVTZU_g, do_fcvt_g, a, FPROUNDING_ZERO, false) +TRANS(FCVTAS_g, do_fcvt_g, a, FPROUNDING_TIEAWAY, true) +TRANS(FCVTAU_g, do_fcvt_g, a, FPROUNDING_TIEAWAY, false) - if (is_scalar) { - write_fp_sreg(s, rd, tcg_res); - } else { - write_vec_element_i32(s, tcg_res, rd, pass, MO_32); - } - } +/* + * FCVT* (vector), scalar version. + * Which sounds weird, but really just means output to fp register + * instead of output to general register. Input and output element + * size are always equal. + */ +static bool do_fcvt_f(DisasContext *s, arg_fcvt *a, + ARMFPRounding rmode, bool is_signed) +{ + TCGv_i64 tcg_int; + int check = fp_access_check_scalar_hsd(s, a->esz); - 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 (check <= 0) { + return check == 0; + } - if (satop || !u) { - memop |= MO_SIGN; - } + tcg_int = tcg_temp_new_i64(); + do_fcvt_scalar(s, a->esz | (is_signed ? MO_SIGN : 0), + a->esz, tcg_int, a->shift, a->rn, rmode); - 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); - - 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(); - } - } + if (!s->fpcr_nep) { + clear_vec(s, a->rd); + } + write_vec_element(s, tcg_int, a->rd, 0, a->esz); + return true; +} - clear_vec_high(s, !is_scalar, rd); - } else { - TCGv_i32 tcg_idx = tcg_temp_new_i32(); +TRANS(FCVTNS_f, do_fcvt_f, a, FPROUNDING_TIEEVEN, true) +TRANS(FCVTNU_f, do_fcvt_f, a, FPROUNDING_TIEEVEN, false) +TRANS(FCVTPS_f, do_fcvt_f, a, FPROUNDING_POSINF, true) +TRANS(FCVTPU_f, do_fcvt_f, a, FPROUNDING_POSINF, false) +TRANS(FCVTMS_f, do_fcvt_f, a, FPROUNDING_NEGINF, true) +TRANS(FCVTMU_f, do_fcvt_f, a, FPROUNDING_NEGINF, false) +TRANS(FCVTZS_f, do_fcvt_f, a, FPROUNDING_ZERO, true) +TRANS(FCVTZU_f, do_fcvt_f, a, FPROUNDING_ZERO, false) +TRANS(FCVTAS_f, do_fcvt_f, a, FPROUNDING_TIEAWAY, true) +TRANS(FCVTAU_f, do_fcvt_f, a, FPROUNDING_TIEAWAY, false) - assert(size == 1); - read_vec_element_i32(s, tcg_idx, rm, index, size); +static bool trans_FJCVTZS(DisasContext *s, arg_FJCVTZS *a) +{ + if (!dc_isar_feature(aa64_jscvt, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i64 t = read_fp_dreg(s, a->rn); + TCGv_ptr fpstatus = fpstatus_ptr(FPST_A64); - 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); - } + gen_helper_fjcvtzs(t, t, fpstatus); - 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); - } - - 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_gen_ext32u_i64(cpu_reg(s, a->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); + } + return true; +} - if (is_scalar) { - tcg_gen_ext32u_i64(tcg_res[0], tcg_res[0]); - } - } +static bool trans_FMOV_hx(DisasContext *s, arg_rr *a) +{ + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i64 tcg_rn = cpu_reg(s, a->rn); + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_ext16u_i64(tmp, tcg_rn); + write_fp_dreg(s, a->rd, tmp); + } + return true; +} - if (is_scalar) { - tcg_res[1] = tcg_constant_i64(0); - } +static bool trans_FMOV_sw(DisasContext *s, arg_rr *a) +{ + if (fp_access_check(s)) { + TCGv_i64 tcg_rn = cpu_reg(s, a->rn); + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(tmp, tcg_rn); + write_fp_dreg(s, a->rd, tmp); + } + return true; +} - for (pass = 0; pass < 2; pass++) { - write_vec_element(s, tcg_res[pass], rd, pass, MO_64); - } +static bool trans_FMOV_dx(DisasContext *s, arg_rr *a) +{ + if (fp_access_check(s)) { + TCGv_i64 tcg_rn = cpu_reg(s, a->rn); + write_fp_dreg(s, a->rd, tcg_rn); } + return true; } -/* 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) +static bool trans_FMOV_ux(DisasContext *s, arg_rr *a) +{ + if (fp_access_check(s)) { + TCGv_i64 tcg_rn = cpu_reg(s, a->rn); + tcg_gen_st_i64(tcg_rn, tcg_env, fp_reg_hi_offset(s, a->rd)); + clear_vec_high(s, true, a->rd); + } + return true; +} + +static bool trans_FMOV_xh(DisasContext *s, arg_rr *a) { - 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 *genfn2 = NULL; - gen_helper_gvec_3 *genfn3 = NULL; + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + tcg_gen_ld16u_i64(tcg_rd, tcg_env, fp_reg_offset(s, a->rn, MO_16)); + } + return true; +} - if (!dc_isar_feature(aa64_aes, s) || size != 0) { - unallocated_encoding(s); - return; +static bool trans_FMOV_ws(DisasContext *s, arg_rr *a) +{ + if (fp_access_check(s)) { + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + tcg_gen_ld32u_i64(tcg_rd, tcg_env, fp_reg_offset(s, a->rn, MO_32)); } + return true; +} - switch (opcode) { - case 0x4: /* AESE */ - genfn3 = gen_helper_crypto_aese; - break; - case 0x6: /* AESMC */ - genfn2 = gen_helper_crypto_aesmc; - break; - case 0x5: /* AESD */ - genfn3 = gen_helper_crypto_aesd; - break; - case 0x7: /* AESIMC */ - genfn2 = gen_helper_crypto_aesimc; - break; - default: - unallocated_encoding(s); - return; +static bool trans_FMOV_xd(DisasContext *s, arg_rr *a) +{ + if (fp_access_check(s)) { + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + tcg_gen_ld_i64(tcg_rd, tcg_env, fp_reg_offset(s, a->rn, MO_64)); + } + return true; +} + +static bool trans_FMOV_xu(DisasContext *s, arg_rr *a) +{ + if (fp_access_check(s)) { + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + tcg_gen_ld_i64(tcg_rd, tcg_env, fp_reg_hi_offset(s, a->rn)); } + return true; +} + +typedef struct ENVScalar1 { + NeonGenOneOpEnvFn *gen_bhs[3]; + NeonGenOne64OpEnvFn *gen_d; +} ENVScalar1; +static bool do_env_scalar1(DisasContext *s, arg_rr_e *a, const ENVScalar1 *f) +{ if (!fp_access_check(s)) { - return; + return true; } - if (genfn2) { - gen_gvec_op2_ool(s, true, rd, rn, 0, genfn2); + if (a->esz == MO_64) { + TCGv_i64 t = read_fp_dreg(s, a->rn); + f->gen_d(t, tcg_env, t); + write_fp_dreg(s, a->rd, t); } else { - gen_gvec_op3_ool(s, true, rd, rd, rn, 0, genfn3); + TCGv_i32 t = tcg_temp_new_i32(); + + read_vec_element_i32(s, t, a->rn, 0, a->esz); + f->gen_bhs[a->esz](t, tcg_env, t); + write_fp_sreg(s, a->rd, t); } + return true; } -/* 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) +static bool do_env_vector1(DisasContext *s, arg_qrr_e *a, const ENVScalar1 *f) { - 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 (a->esz == MO_64 && !a->q) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + if (a->esz == MO_64) { + TCGv_i64 t = tcg_temp_new_i64(); - if (size != 0) { - unallocated_encoding(s); - return; + for (int i = 0; i < 2; ++i) { + read_vec_element(s, t, a->rn, i, MO_64); + f->gen_d(t, tcg_env, t); + write_vec_element(s, t, a->rd, i, MO_64); + } + } else { + TCGv_i32 t = tcg_temp_new_i32(); + int n = (a->q ? 16 : 8) >> a->esz; + + for (int i = 0; i < n; ++i) { + read_vec_element_i32(s, t, a->rn, i, a->esz); + f->gen_bhs[a->esz](t, tcg_env, t); + write_vec_element_i32(s, t, a->rd, i, a->esz); + } } + clear_vec_high(s, a->q, a->rd); + return true; +} - 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; +static const ENVScalar1 f_scalar_sqabs = { + { gen_helper_neon_qabs_s8, + gen_helper_neon_qabs_s16, + gen_helper_neon_qabs_s32 }, + gen_helper_neon_qabs_s64, +}; +TRANS(SQABS_s, do_env_scalar1, a, &f_scalar_sqabs) +TRANS(SQABS_v, do_env_vector1, a, &f_scalar_sqabs) + +static const ENVScalar1 f_scalar_sqneg = { + { gen_helper_neon_qneg_s8, + gen_helper_neon_qneg_s16, + gen_helper_neon_qneg_s32 }, + gen_helper_neon_qneg_s64, +}; +TRANS(SQNEG_s, do_env_scalar1, a, &f_scalar_sqneg) +TRANS(SQNEG_v, do_env_vector1, a, &f_scalar_sqneg) + +static bool do_scalar1_d(DisasContext *s, arg_rr *a, ArithOneOp *f) +{ + if (fp_access_check(s)) { + TCGv_i64 t = read_fp_dreg(s, a->rn); + f(t, t); + write_fp_dreg(s, a->rd, t); } + return true; +} - if (!feature) { - unallocated_encoding(s); - return; +TRANS(ABS_s, do_scalar1_d, a, tcg_gen_abs_i64) +TRANS(NEG_s, do_scalar1_d, a, tcg_gen_neg_i64) + +static bool do_cmop0_d(DisasContext *s, arg_rr *a, TCGCond cond) +{ + if (fp_access_check(s)) { + TCGv_i64 t = read_fp_dreg(s, a->rn); + tcg_gen_negsetcond_i64(cond, t, t, tcg_constant_i64(0)); + write_fp_dreg(s, a->rd, t); + } + return true; +} + +TRANS(CMGT0_s, do_cmop0_d, a, TCG_COND_GT) +TRANS(CMGE0_s, do_cmop0_d, a, TCG_COND_GE) +TRANS(CMLE0_s, do_cmop0_d, a, TCG_COND_LE) +TRANS(CMLT0_s, do_cmop0_d, a, TCG_COND_LT) +TRANS(CMEQ0_s, do_cmop0_d, a, TCG_COND_EQ) + +static bool do_2misc_narrow_scalar(DisasContext *s, arg_rr_e *a, + ArithOneOp * const fn[3]) +{ + if (a->esz == MO_64) { + return false; } + if (fp_access_check(s)) { + TCGv_i64 t = tcg_temp_new_i64(); - if (!fp_access_check(s)) { - return; + read_vec_element(s, t, a->rn, 0, a->esz + 1); + fn[a->esz](t, t); + clear_vec(s, a->rd); + write_vec_element(s, t, a->rd, 0, a->esz); + } + return true; +} + +#define WRAP_ENV(NAME) \ + static void gen_##NAME(TCGv_i64 d, TCGv_i64 n) \ + { gen_helper_##NAME(d, tcg_env, n); } + +WRAP_ENV(neon_unarrow_sat8) +WRAP_ENV(neon_unarrow_sat16) +WRAP_ENV(neon_unarrow_sat32) + +static ArithOneOp * const f_scalar_sqxtun[] = { + gen_neon_unarrow_sat8, + gen_neon_unarrow_sat16, + gen_neon_unarrow_sat32, +}; +TRANS(SQXTUN_s, do_2misc_narrow_scalar, a, f_scalar_sqxtun) + +WRAP_ENV(neon_narrow_sat_s8) +WRAP_ENV(neon_narrow_sat_s16) +WRAP_ENV(neon_narrow_sat_s32) + +static ArithOneOp * const f_scalar_sqxtn[] = { + gen_neon_narrow_sat_s8, + gen_neon_narrow_sat_s16, + gen_neon_narrow_sat_s32, +}; +TRANS(SQXTN_s, do_2misc_narrow_scalar, a, f_scalar_sqxtn) + +WRAP_ENV(neon_narrow_sat_u8) +WRAP_ENV(neon_narrow_sat_u16) +WRAP_ENV(neon_narrow_sat_u32) + +static ArithOneOp * const f_scalar_uqxtn[] = { + gen_neon_narrow_sat_u8, + gen_neon_narrow_sat_u16, + gen_neon_narrow_sat_u32, +}; +TRANS(UQXTN_s, do_2misc_narrow_scalar, a, f_scalar_uqxtn) + +static bool trans_FCVTXN_s(DisasContext *s, arg_rr_e *a) +{ + if (fp_access_check(s)) { + /* + * 64 bit to 32 bit float conversion + * with von Neumann rounding (round to odd) + */ + TCGv_i64 src = read_fp_dreg(s, a->rn); + TCGv_i32 dst = tcg_temp_new_i32(); + gen_helper_fcvtx_f64_to_f32(dst, src, fpstatus_ptr(FPST_A64)); + write_fp_sreg_merging(s, a->rd, a->rd, dst); } - gen_gvec_op3_ool(s, true, rd, rn, rm, 0, genfn); + return true; } -/* 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; +#undef WRAP_ENV - if (size != 0) { - unallocated_encoding(s); - return; +static bool do_gvec_fn2(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) +{ + if (!a->q && a->esz == MO_64) { + return false; } + if (fp_access_check(s)) { + gen_gvec_fn2(s, a->q, a->rd, a->rn, fn, a->esz); + } + return true; +} - 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; +TRANS(ABS_v, do_gvec_fn2, a, tcg_gen_gvec_abs) +TRANS(NEG_v, do_gvec_fn2, a, tcg_gen_gvec_neg) +TRANS(NOT_v, do_gvec_fn2, a, tcg_gen_gvec_not) +TRANS(CNT_v, do_gvec_fn2, a, gen_gvec_cnt) +TRANS(RBIT_v, do_gvec_fn2, a, gen_gvec_rbit) +TRANS(CMGT0_v, do_gvec_fn2, a, gen_gvec_cgt0) +TRANS(CMGE0_v, do_gvec_fn2, a, gen_gvec_cge0) +TRANS(CMLT0_v, do_gvec_fn2, a, gen_gvec_clt0) +TRANS(CMLE0_v, do_gvec_fn2, a, gen_gvec_cle0) +TRANS(CMEQ0_v, do_gvec_fn2, a, gen_gvec_ceq0) +TRANS(REV16_v, do_gvec_fn2, a, gen_gvec_rev16) +TRANS(REV32_v, do_gvec_fn2, a, gen_gvec_rev32) +TRANS(URECPE_v, do_gvec_fn2, a, gen_gvec_urecpe) +TRANS(URSQRTE_v, do_gvec_fn2, a, gen_gvec_ursqrte) + +static bool do_gvec_fn2_bhs(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) +{ + if (a->esz == MO_64) { + return false; } + if (fp_access_check(s)) { + gen_gvec_fn2(s, a->q, a->rd, a->rn, fn, a->esz); + } + return true; +} - if (!feature) { - unallocated_encoding(s); - return; +TRANS(CLS_v, do_gvec_fn2_bhs, a, gen_gvec_cls) +TRANS(CLZ_v, do_gvec_fn2_bhs, a, gen_gvec_clz) +TRANS(REV64_v, do_gvec_fn2_bhs, a, gen_gvec_rev64) +TRANS(SADDLP_v, do_gvec_fn2_bhs, a, gen_gvec_saddlp) +TRANS(UADDLP_v, do_gvec_fn2_bhs, a, gen_gvec_uaddlp) +TRANS(SADALP_v, do_gvec_fn2_bhs, a, gen_gvec_sadalp) +TRANS(UADALP_v, do_gvec_fn2_bhs, a, gen_gvec_uadalp) + +static bool do_2misc_narrow_vector(DisasContext *s, arg_qrr_e *a, + ArithOneOp * const fn[3]) +{ + if (a->esz == MO_64) { + return false; } + if (fp_access_check(s)) { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); - if (!fp_access_check(s)) { - return; + read_vec_element(s, t0, a->rn, 0, MO_64); + read_vec_element(s, t1, a->rn, 1, MO_64); + fn[a->esz](t0, t0); + fn[a->esz](t1, t1); + write_vec_element(s, t0, a->rd, a->q ? 2 : 0, MO_32); + write_vec_element(s, t1, a->rd, a->q ? 3 : 1, MO_32); + clear_vec_high(s, a->q, a->rd); } - gen_gvec_op2_ool(s, true, rd, rn, 0, genfn); + return true; } -static void gen_rax1_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +static ArithOneOp * const f_scalar_xtn[] = { + gen_helper_neon_narrow_u8, + gen_helper_neon_narrow_u16, + tcg_gen_ext32u_i64, +}; +TRANS(XTN, do_2misc_narrow_vector, a, f_scalar_xtn) +TRANS(SQXTUN_v, do_2misc_narrow_vector, a, f_scalar_sqxtun) +TRANS(SQXTN_v, do_2misc_narrow_vector, a, f_scalar_sqxtn) +TRANS(UQXTN_v, do_2misc_narrow_vector, a, f_scalar_uqxtn) + +static void gen_fcvtn_hs(TCGv_i64 d, TCGv_i64 n) { - tcg_gen_rotli_i64(d, m, 1); - tcg_gen_xor_i64(d, d, n); + TCGv_i32 tcg_lo = tcg_temp_new_i32(); + TCGv_i32 tcg_hi = tcg_temp_new_i32(); + TCGv_ptr fpst = fpstatus_ptr(FPST_A64); + TCGv_i32 ahp = get_ahp_flag(); + + tcg_gen_extr_i64_i32(tcg_lo, tcg_hi, n); + 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(d, tcg_lo); } -static void gen_rax1_vec(unsigned vece, TCGv_vec d, TCGv_vec n, TCGv_vec m) +static void gen_fcvtn_sd(TCGv_i64 d, TCGv_i64 n) { - tcg_gen_rotli_vec(vece, d, m, 1); - tcg_gen_xor_vec(vece, d, d, n); + TCGv_i32 tmp = tcg_temp_new_i32(); + TCGv_ptr fpst = fpstatus_ptr(FPST_A64); + + gen_helper_vfp_fcvtsd(tmp, n, fpst); + tcg_gen_extu_i32_i64(d, tmp); } -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 void gen_fcvtxn_sd(TCGv_i64 d, TCGv_i64 n) { - 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); + /* + * 64 bit to 32 bit float conversion + * with von Neumann rounding (round to odd) + */ + TCGv_i32 tmp = tcg_temp_new_i32(); + gen_helper_fcvtx_f64_to_f32(tmp, n, fpstatus_ptr(FPST_A64)); + tcg_gen_extu_i32_i64(d, tmp); } -/* 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; - } - } +static ArithOneOp * const f_vector_fcvtn[] = { + NULL, + gen_fcvtn_hs, + gen_fcvtn_sd, +}; +static ArithOneOp * const f_scalar_fcvtxn[] = { + NULL, + NULL, + gen_fcvtxn_sd, +}; +TRANS(FCVTN_v, do_2misc_narrow_vector, a, f_vector_fcvtn) +TRANS(FCVTXN_v, do_2misc_narrow_vector, a, f_scalar_fcvtxn) - if (!feature) { - unallocated_encoding(s); - return; - } +static void gen_bfcvtn_hs(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_ptr fpst = fpstatus_ptr(FPST_A64); + TCGv_i32 tmp = tcg_temp_new_i32(); + gen_helper_bfcvt_pair(tmp, n, fpst); + tcg_gen_extu_i32_i64(d, tmp); +} - if (!fp_access_check(s)) { - return; - } +static void gen_bfcvtn_ah_hs(TCGv_i64 d, TCGv_i64 n) +{ + TCGv_ptr fpst = fpstatus_ptr(FPST_AH); + TCGv_i32 tmp = tcg_temp_new_i32(); + gen_helper_bfcvt_pair(tmp, n, fpst); + tcg_gen_extu_i32_i64(d, tmp); +} - 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); +static ArithOneOp * const f_vector_bfcvtn[2][3] = { + { + NULL, + gen_bfcvtn_hs, + NULL, + }, { + NULL, + gen_bfcvtn_ah_hs, + NULL, } -} +}; +TRANS_FEAT(BFCVTN_v, aa64_bf16, do_2misc_narrow_vector, a, + f_vector_bfcvtn[s->fpcr_ah]) -/* 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) +static bool trans_SHLL_v(DisasContext *s, arg_qrr_e *a) { - int opcode = extract32(insn, 10, 2); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - bool feature; + static NeonGenWidenFn * const widenfns[3] = { + gen_helper_neon_widen_u8, + gen_helper_neon_widen_u16, + tcg_gen_extu_i32_i64, + }; + NeonGenWidenFn *widenfn; + TCGv_i64 tcg_res[2]; + TCGv_i32 tcg_op; + int part, pass; - 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 (a->esz == MO_64) { + return false; } - - if (!feature) { - unallocated_encoding(s); - return; + if (!fp_access_check(s)) { + return true; } - if (!fp_access_check(s)) { - return; + tcg_op = tcg_temp_new_i32(); + widenfn = widenfns[a->esz]; + part = a->q ? 2 : 0; + + for (pass = 0; pass < 2; pass++) { + read_vec_element_i32(s, tcg_op, a->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 << a->esz); } - 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(); + for (pass = 0; pass < 2; pass++) { + write_vec_element(s, tcg_res[pass], a->rd, pass, MO_64); } + return true; } -/* 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; - } +static bool do_fabs_fneg_v(DisasContext *s, arg_qrr_e *a, GVecGen2Fn *fn) +{ + int check = fp_access_check_vector_hsd(s, a->q, a->esz); - if (!feature) { - unallocated_encoding(s); - return; + if (check <= 0) { + return check == 0; } - if (!fp_access_check(s)) { - return; - } + gen_gvec_fn2(s, a->q, a->rd, a->rn, fn, a->esz); + return true; +} - if (op0 < 2) { - TCGv_i64 tcg_op1, tcg_op2, tcg_op3, tcg_res[2]; - int pass; +TRANS(FABS_v, do_fabs_fneg_v, a, gen_gvec_fabs) +TRANS(FNEG_v, do_fabs_fneg_v, a, gen_gvec_fneg) - 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(); +static bool do_fp1_vector(DisasContext *s, arg_qrr_e *a, + const FPScalar1 *f, int rmode) +{ + TCGv_i32 tcg_rmode = NULL; + TCGv_ptr fpst; + int check = fp_access_check_vector_hsd(s, a->q, a->esz); - 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 (check <= 0) { + return check == 0; + } - 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); - } else { - TCGv_i32 tcg_op1, tcg_op2, tcg_op3, tcg_res, tcg_zero; + fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); + if (rmode >= 0) { + tcg_rmode = gen_set_rmode(rmode, fpst); + } - 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); + if (a->esz == MO_64) { + TCGv_i64 t64 = tcg_temp_new_i64(); - 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); + for (int pass = 0; pass < 2; ++pass) { + read_vec_element(s, t64, a->rn, pass, MO_64); + f->gen_d(t64, t64, fpst); + write_vec_element(s, t64, a->rd, pass, MO_64); + } + } else { + TCGv_i32 t32 = tcg_temp_new_i32(); + void (*gen)(TCGv_i32, TCGv_i32, TCGv_ptr) + = (a->esz == MO_16 ? f->gen_h : f->gen_s); - 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); + for (int pass = 0, n = (a->q ? 16 : 8) >> a->esz; pass < n; ++pass) { + read_vec_element_i32(s, t32, a->rn, pass, a->esz); + gen(t32, t32, fpst); + write_vec_element_i32(s, t32, a->rd, pass, a->esz); + } + } + clear_vec_high(s, a->q, a->rd); - 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); + if (rmode >= 0) { + gen_restore_rmode(tcg_rmode, fpst); } + return true; } -/* 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) +TRANS(FSQRT_v, do_fp1_vector, a, &f_scalar_fsqrt, -1) + +TRANS(FRINTN_v, do_fp1_vector, a, &f_scalar_frint, FPROUNDING_TIEEVEN) +TRANS(FRINTP_v, do_fp1_vector, a, &f_scalar_frint, FPROUNDING_POSINF) +TRANS(FRINTM_v, do_fp1_vector, a, &f_scalar_frint, FPROUNDING_NEGINF) +TRANS(FRINTZ_v, do_fp1_vector, a, &f_scalar_frint, FPROUNDING_ZERO) +TRANS(FRINTA_v, do_fp1_vector, a, &f_scalar_frint, FPROUNDING_TIEAWAY) +TRANS(FRINTI_v, do_fp1_vector, a, &f_scalar_frint, -1) +TRANS(FRINTX_v, do_fp1_vector, a, &f_scalar_frintx, -1) + +TRANS_FEAT(FRINT32Z_v, aa64_frint, do_fp1_vector, a, + &f_scalar_frint32, FPROUNDING_ZERO) +TRANS_FEAT(FRINT32X_v, aa64_frint, do_fp1_vector, a, &f_scalar_frint32, -1) +TRANS_FEAT(FRINT64Z_v, aa64_frint, do_fp1_vector, a, + &f_scalar_frint64, FPROUNDING_ZERO) +TRANS_FEAT(FRINT64X_v, aa64_frint, do_fp1_vector, a, &f_scalar_frint64, -1) + +static bool do_gvec_op2_fpst_with_fpsttype(DisasContext *s, MemOp esz, + bool is_q, int rd, int rn, int data, + gen_helper_gvec_2_ptr * const fns[3], + ARMFPStatusFlavour fpsttype) { - int rm = extract32(insn, 16, 5); - int imm6 = extract32(insn, 10, 6); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); + int check = fp_access_check_vector_hsd(s, is_q, esz); + TCGv_ptr fpst; - if (!dc_isar_feature(aa64_sha3, s)) { - unallocated_encoding(s); - return; + if (check <= 0) { + return check == 0; } - if (!fp_access_check(s)) { - return; - } + fpst = fpstatus_ptr(fpsttype); + tcg_gen_gvec_2_ptr(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), fpst, + is_q ? 16 : 8, vec_full_reg_size(s), + data, fns[esz - 1]); + return true; +} - 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)); +static bool do_gvec_op2_fpst(DisasContext *s, MemOp esz, bool is_q, + int rd, int rn, int data, + gen_helper_gvec_2_ptr * const fns[3]) +{ + return do_gvec_op2_fpst_with_fpsttype(s, esz, is_q, rd, rn, data, fns, + esz == MO_16 ? FPST_A64_F16 : + FPST_A64); } -/* 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 bool do_gvec_op2_ah_fpst(DisasContext *s, MemOp esz, bool is_q, + int rd, int rn, int data, + gen_helper_gvec_2_ptr * const fns[3]) { - 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); + return do_gvec_op2_fpst_with_fpsttype(s, esz, is_q, rd, rn, data, + fns, select_ah_fpst(s, esz)); +} - if (!dc_isar_feature(aa64_sm3, s)) { - unallocated_encoding(s); - return; - } +static gen_helper_gvec_2_ptr * const f_scvtf_v[] = { + gen_helper_gvec_vcvt_sh, + gen_helper_gvec_vcvt_sf, + gen_helper_gvec_vcvt_sd, +}; +TRANS(SCVTF_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, 0, f_scvtf_v) +TRANS(SCVTF_vf, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, a->shift, f_scvtf_v) + +static gen_helper_gvec_2_ptr * const f_ucvtf_v[] = { + gen_helper_gvec_vcvt_uh, + gen_helper_gvec_vcvt_uf, + gen_helper_gvec_vcvt_ud, +}; +TRANS(UCVTF_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, 0, f_ucvtf_v) +TRANS(UCVTF_vf, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, a->shift, f_ucvtf_v) + +static gen_helper_gvec_2_ptr * const f_fcvtzs_vf[] = { + gen_helper_gvec_vcvt_rz_hs, + gen_helper_gvec_vcvt_rz_fs, + gen_helper_gvec_vcvt_rz_ds, +}; +TRANS(FCVTZS_vf, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, a->shift, f_fcvtzs_vf) - if (!fp_access_check(s)) { - return; - } +static gen_helper_gvec_2_ptr * const f_fcvtzu_vf[] = { + gen_helper_gvec_vcvt_rz_hu, + gen_helper_gvec_vcvt_rz_fu, + gen_helper_gvec_vcvt_rz_du, +}; +TRANS(FCVTZU_vf, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, a->shift, f_fcvtzu_vf) - gen_gvec_op3_ool(s, true, rd, rn, rm, imm2, fns[opcode]); -} +static gen_helper_gvec_2_ptr * const f_fcvt_s_vi[] = { + gen_helper_gvec_vcvt_rm_sh, + gen_helper_gvec_vcvt_rm_ss, + gen_helper_gvec_vcvt_rm_sd, +}; -/* 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 gen_helper_gvec_2_ptr * const f_fcvt_u_vi[] = { + gen_helper_gvec_vcvt_rm_uh, + gen_helper_gvec_vcvt_rm_us, + gen_helper_gvec_vcvt_rm_ud, +}; + +TRANS(FCVTNS_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_nearest_even, f_fcvt_s_vi) +TRANS(FCVTNU_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_nearest_even, f_fcvt_u_vi) +TRANS(FCVTPS_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_up, f_fcvt_s_vi) +TRANS(FCVTPU_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_up, f_fcvt_u_vi) +TRANS(FCVTMS_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_down, f_fcvt_s_vi) +TRANS(FCVTMU_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_down, f_fcvt_u_vi) +TRANS(FCVTZS_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_to_zero, f_fcvt_s_vi) +TRANS(FCVTZU_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_to_zero, f_fcvt_u_vi) +TRANS(FCVTAS_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_ties_away, f_fcvt_s_vi) +TRANS(FCVTAU_vi, do_gvec_op2_fpst, + a->esz, a->q, a->rd, a->rn, float_round_ties_away, f_fcvt_u_vi) + +static gen_helper_gvec_2_ptr * const f_fceq0[] = { + gen_helper_gvec_fceq0_h, + gen_helper_gvec_fceq0_s, + gen_helper_gvec_fceq0_d, +}; +TRANS(FCMEQ0_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_fceq0) + +static gen_helper_gvec_2_ptr * const f_fcgt0[] = { + gen_helper_gvec_fcgt0_h, + gen_helper_gvec_fcgt0_s, + gen_helper_gvec_fcgt0_d, }; +TRANS(FCMGT0_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_fcgt0) -static void disas_data_proc_simd(DisasContext *s, uint32_t insn) +static gen_helper_gvec_2_ptr * const f_fcge0[] = { + gen_helper_gvec_fcge0_h, + gen_helper_gvec_fcge0_s, + gen_helper_gvec_fcge0_d, +}; +TRANS(FCMGE0_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_fcge0) + +static gen_helper_gvec_2_ptr * const f_fclt0[] = { + gen_helper_gvec_fclt0_h, + gen_helper_gvec_fclt0_s, + gen_helper_gvec_fclt0_d, +}; +TRANS(FCMLT0_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_fclt0) + +static gen_helper_gvec_2_ptr * const f_fcle0[] = { + gen_helper_gvec_fcle0_h, + gen_helper_gvec_fcle0_s, + gen_helper_gvec_fcle0_d, +}; +TRANS(FCMLE0_v, do_gvec_op2_fpst, a->esz, a->q, a->rd, a->rn, 0, f_fcle0) + +static gen_helper_gvec_2_ptr * const f_frecpe[] = { + gen_helper_gvec_frecpe_h, + gen_helper_gvec_frecpe_s, + gen_helper_gvec_frecpe_d, +}; +static gen_helper_gvec_2_ptr * const f_frecpe_rpres[] = { + gen_helper_gvec_frecpe_h, + gen_helper_gvec_frecpe_rpres_s, + gen_helper_gvec_frecpe_d, +}; +TRANS(FRECPE_v, do_gvec_op2_ah_fpst, a->esz, a->q, a->rd, a->rn, 0, + s->fpcr_ah && dc_isar_feature(aa64_rpres, s) ? f_frecpe_rpres : f_frecpe) + +static gen_helper_gvec_2_ptr * const f_frsqrte[] = { + gen_helper_gvec_frsqrte_h, + gen_helper_gvec_frsqrte_s, + gen_helper_gvec_frsqrte_d, +}; +static gen_helper_gvec_2_ptr * const f_frsqrte_rpres[] = { + gen_helper_gvec_frsqrte_h, + gen_helper_gvec_frsqrte_rpres_s, + gen_helper_gvec_frsqrte_d, +}; +TRANS(FRSQRTE_v, do_gvec_op2_ah_fpst, a->esz, a->q, a->rd, a->rn, 0, + s->fpcr_ah && dc_isar_feature(aa64_rpres, s) ? f_frsqrte_rpres : f_frsqrte) + +static bool trans_FCVTL_v(DisasContext *s, arg_qrr_e *a) { - /* 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. + /* 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. */ - AArch64DecodeFn *fn = lookup_disas_fn(&data_proc_simd[0], insn); - if (fn) { - fn(s, insn); - } else { - unallocated_encoding(s); + int pass; + TCGv_ptr fpst; + + if (!fp_access_check(s)) { + return true; } -} -/* 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); + if (a->esz == MO_64) { + /* 32 -> 64 bit fp conversion */ + TCGv_i64 tcg_res[2]; + TCGv_i32 tcg_op = tcg_temp_new_i32(); + int srcelt = a->q ? 2 : 0; + + fpst = fpstatus_ptr(FPST_A64); + + for (pass = 0; pass < 2; pass++) { + tcg_res[pass] = tcg_temp_new_i64(); + read_vec_element_i32(s, tcg_op, a->rn, srcelt + pass, MO_32); + gen_helper_vfp_fcvtds(tcg_res[pass], tcg_op, fpst); + } + for (pass = 0; pass < 2; pass++) { + write_vec_element(s, tcg_res[pass], a->rd, pass, MO_64); + } } else { - /* SIMD, including crypto */ - disas_data_proc_simd(s, insn); + /* 16 -> 32 bit fp conversion */ + int srcelt = a->q ? 4 : 0; + TCGv_i32 tcg_res[4]; + TCGv_i32 ahp = get_ahp_flag(); + + fpst = fpstatus_ptr(FPST_A64_F16); + + for (pass = 0; pass < 4; pass++) { + tcg_res[pass] = tcg_temp_new_i32(); + read_vec_element_i32(s, tcg_res[pass], a->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], a->rd, pass, MO_32); + } } + clear_vec_high(s, true, a->rd); + return true; } static bool trans_OK(DisasContext *s, arg_OK *a) @@ -13748,37 +10041,6 @@ static bool trans_FAIL(DisasContext *s, arg_OK *a) 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, 0, 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 @@ -13831,29 +10093,11 @@ static bool btype_destination_ok(uint32_t insn, bool bt, int btype) return false; } -/* C3.1 A64 instruction index by encoding */ -static void disas_a64_legacy(DisasContext *s, uint32_t insn) -{ - switch (extract32(insn, 25, 4)) { - case 0x5: - case 0xd: /* Data processing - register */ - disas_data_proc_reg(s, insn); - break; - case 0x7: - case 0xf: /* Data processing - SIMD and floating point */ - disas_data_proc_simd_fp(s, insn); - break; - default: - unallocated_encoding(s); - break; - } -} - static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUARMState *env = cpu->env_ptr; + CPUARMState *env = cpu_env(cpu); ARMCPU *arm_cpu = env_archcpu(env); CPUARMTBFlags tb_flags = arm_tbflags_from_tb(dc->base.tb); int bound, core_mmu_idx; @@ -13881,7 +10125,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->pstate_il = EX_TBFLAG_ANY(tb_flags, PSTATE__IL); dc->fgt_active = EX_TBFLAG_ANY(tb_flags, FGT_ACTIVE); dc->fgt_svc = EX_TBFLAG_ANY(tb_flags, FGT_SVC); - dc->fgt_eret = EX_TBFLAG_A64(tb_flags, FGT_ERET); + dc->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; @@ -13890,13 +10134,21 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->bt = EX_TBFLAG_A64(tb_flags, BT); dc->btype = EX_TBFLAG_A64(tb_flags, BTYPE); dc->unpriv = EX_TBFLAG_A64(tb_flags, UNPRIV); - dc->ata = EX_TBFLAG_A64(tb_flags, ATA); + dc->ata[0] = EX_TBFLAG_A64(tb_flags, ATA); + dc->ata[1] = EX_TBFLAG_A64(tb_flags, ATA0); dc->mte_active[0] = EX_TBFLAG_A64(tb_flags, MTE_ACTIVE); dc->mte_active[1] = EX_TBFLAG_A64(tb_flags, MTE0_ACTIVE); dc->pstate_sm = EX_TBFLAG_A64(tb_flags, PSTATE_SM); dc->pstate_za = EX_TBFLAG_A64(tb_flags, PSTATE_ZA); dc->sme_trap_nonstreaming = EX_TBFLAG_A64(tb_flags, SME_TRAP_NONSTREAMING); dc->naa = EX_TBFLAG_A64(tb_flags, NAA); + dc->nv = EX_TBFLAG_A64(tb_flags, NV); + dc->nv1 = EX_TBFLAG_A64(tb_flags, NV1); + dc->nv2 = EX_TBFLAG_A64(tb_flags, NV2); + dc->nv2_mem_e20 = EX_TBFLAG_A64(tb_flags, NV2_MEM_E20); + dc->nv2_mem_be = EX_TBFLAG_A64(tb_flags, NV2_MEM_BE); + dc->fpcr_ah = EX_TBFLAG_A64(tb_flags, AH); + dc->fpcr_nep = EX_TBFLAG_A64(tb_flags, NEP); dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = arm_cpu->cp_regs; @@ -13953,13 +10205,13 @@ static void aarch64_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) pc_arg &= ~TARGET_PAGE_MASK; } tcg_gen_insn_start(pc_arg, 0, 0); - dc->insn_start = tcg_last_op(); + dc->insn_start_updated = false; } static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *s = container_of(dcbase, DisasContext, base); - CPUARMState *env = cpu->env_ptr; + CPUARMState *env = cpu_env(cpu); uint64_t pc = s->base.pc_next; uint32_t insn; @@ -13990,7 +10242,7 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) * start of the TB. */ assert(s->base.num_insns == 1); - gen_helper_exception_pc_alignment(cpu_env, tcg_constant_tl(pc)); + gen_helper_exception_pc_alignment(tcg_env, tcg_constant_vaddr(pc)); s->base.is_jmp = DISAS_NORETURN; s->base.pc_next = QEMU_ALIGN_UP(pc, 4); return; @@ -14001,8 +10253,8 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) s->insn = insn; s->base.pc_next = pc + 4; - s->fp_access_checked = false; - s->sve_access_checked = false; + s->fp_access_checked = 0; + s->sve_access_checked = 0; if (s->pstate_il) { /* @@ -14015,19 +10267,6 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) 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); @@ -14036,12 +10275,13 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) * 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 - && s->guarded_page && !btype_destination_ok(insn, s->bt, s->btype)) { - gen_exception_insn(s, 0, EXCP_UDEF, syn_btitrap(s->btype)); - return; + gen_helper_guarded_page_check(tcg_env); } } else { /* Not the first insn: btype must be 0. */ @@ -14057,7 +10297,7 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) if (!disas_a64(s, insn) && !disas_sme(s, insn) && !disas_sve(s, insn)) { - disas_a64_legacy(s, insn); + unallocated_encoding(s); } /* @@ -14114,11 +10354,11 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) break; case DISAS_WFE: gen_a64_update_pc(dc, 4); - gen_helper_wfe(cpu_env); + gen_helper_wfe(tcg_env); break; case DISAS_YIELD: gen_a64_update_pc(dc, 4); - gen_helper_yield(cpu_env); + gen_helper_yield(tcg_env); break; case DISAS_WFI: /* @@ -14126,7 +10366,7 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) * the CPU if trying to debug across a WFI. */ gen_a64_update_pc(dc, 4); - gen_helper_wfi(cpu_env, tcg_constant_i32(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. @@ -14137,20 +10377,10 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -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/tcg/translate-a64.h b/target/arm/tcg/translate-a64.h index b55dc435fc5..b2420f59ebe 100644 --- a/target/arm/tcg/translate-a64.h +++ b/target/arm/tcg/translate-a64.h @@ -28,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) @@ -63,7 +65,7 @@ TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write, static inline void assert_fp_access_checked(DisasContext *s) { #ifdef CONFIG_DEBUG_TCG - if (unlikely(!s->fp_access_checked || s->fp_excp_el)) { + if (unlikely(s->fp_access_checked <= 0)) { fprintf(stderr, "target-arm: FP access check missing for " "instruction 0x%08x\n", s->insn); abort(); @@ -115,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; } @@ -179,10 +181,23 @@ 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; } +/* + * Return the ARMFPStatusFlavour to use based on element size and + * whether FPCR.AH is set. + */ +static inline ARMFPStatusFlavour select_ah_fpst(DisasContext *s, MemOp esz) +{ + if (s->fpcr_ah) { + return esz == MO_16 ? FPST_AH_F16 : FPST_AH; + } else { + return esz == MO_16 ? FPST_A64_F16 : FPST_A64; + } +} + bool disas_sve(DisasContext *, uint32_t); bool disas_sme(DisasContext *, uint32_t); @@ -191,6 +206,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/tcg/translate-m-nocp.c b/target/arm/tcg/translate-m-nocp.c index 33f6478bb9f..b92773b4af5 100644 --- a/target/arm/tcg/translate-m-nocp.c +++ b/target/arm/tcg/translate-m-nocp.c @@ -85,9 +85,9 @@ 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); } clear_eci_state(s); @@ -322,7 +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); + gen_helper_vfp_set_fpscr(tcg_env, tmp); gen_lookup_tb(s); break; case ARM_VFP_FPSCR_NZCVQC: @@ -332,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. @@ -340,11 +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]); + store_cpu_field_low32(fpscr, vfp.fpsr); break; } case ARM_VFP_FPCXT_NS: @@ -390,8 +390,8 @@ 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; break; } @@ -451,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: @@ -465,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: @@ -475,8 +475,8 @@ 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); @@ -493,7 +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); + gen_helper_vfp_set_fpscr(tcg_env, fpscr); lookup_tb = true; break; } @@ -506,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); @@ -528,8 +528,8 @@ 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); @@ -540,7 +540,7 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, 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); + gen_helper_vfp_set_fpscr(tcg_env, fpscr); break; } case ARM_VFP_VPR: @@ -643,7 +643,7 @@ 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) { @@ -682,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) { diff --git a/target/arm/tcg/translate-mve.c b/target/arm/tcg/translate-mve.c index 17d8e6804e6..b1a8d6a65c0 100644 --- a/target/arm/tcg/translate-mve.c +++ b/target/arm/tcg/translate-mve.c @@ -56,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; } @@ -173,7 +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); + fn(tcg_env, qreg, addr); /* * Writeback always happens after the last beat of the insn, @@ -234,7 +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); + fn(tcg_env, qd, qm, addr); mve_update_eci(s); return true; } @@ -330,7 +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)); + fn(tcg_env, qd, qm, tcg_constant_i32(offset)); mve_update_eci(s); return true; } @@ -397,7 +397,7 @@ 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); @@ -491,7 +491,7 @@ 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); + gen_helper_mve_vdup(tcg_env, qd, rt); } mve_update_eci(s); return true; @@ -517,7 +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); + fn(tcg_env, qd, qm); } mve_update_eci(s); return true; @@ -612,7 +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))); + fn(tcg_env, qd, qm, tcg_constant_i32(arm_rmode_to_sf(rmode))); mve_update_eci(s); return true; } @@ -800,7 +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); + fn(tcg_env, qd, qn, qm); } mve_update_eci(s); return true; @@ -1052,7 +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); + fn(tcg_env, qd, qn, rm); mve_update_eci(s); return true; } @@ -1183,7 +1183,7 @@ static bool do_long_dual_acc(DisasContext *s, arg_vmlaldav *a, rda_i = tcg_constant_i64(0); } - fn(rda_o, cpu_env, qn, qm, rda_i); + fn(rda_o, tcg_env, qn, qm, rda_i); rdalo = tcg_temp_new_i32(); rdahi = tcg_temp_new_i32(); @@ -1281,7 +1281,7 @@ static bool do_dual_acc(DisasContext *s, arg_vmladav *a, MVEGenDualAccOpFn *fn) rda_o = tcg_temp_new_i32(); } - fn(rda_o, cpu_env, qn, qm, rda_i); + fn(rda_o, tcg_env, qn, qm, rda_i); store_reg(s, a->rda, rda_o); mve_update_eci(s); @@ -1377,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); @@ -1419,7 +1419,7 @@ static bool trans_VADDV(DisasContext *s, arg_VADDV *a) } qm = mve_qreg_ptr(a->qm); - fns[a->size][a->u](rda_o, cpu_env, qm, rda_i); + fns[a->size][a->u](rda_o, tcg_env, qm, rda_i); store_reg(s, a->rda, rda_o); mve_update_eci(s); @@ -1471,9 +1471,9 @@ static bool trans_VADDLV(DisasContext *s, arg_VADDLV *a) qm = mve_qreg_ptr(a->qm); if (a->u) { - gen_helper_mve_vaddlv_u(rda_o, cpu_env, qm, rda_i); + gen_helper_mve_vaddlv_u(rda_o, tcg_env, qm, rda_i); } else { - gen_helper_mve_vaddlv_s(rda_o, cpu_env, qm, rda_i); + gen_helper_mve_vaddlv_s(rda_o, tcg_env, qm, rda_i); } rdalo = tcg_temp_new_i32(); @@ -1508,7 +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)); + fn(tcg_env, qd, tcg_constant_i64(imm)); } mve_update_eci(s); return true; @@ -1580,7 +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)); + fn(tcg_env, qd, qm, tcg_constant_i32(shift)); } mve_update_eci(s); return true; @@ -1685,7 +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); + fn(tcg_env, qda, qda, rm); mve_update_eci(s); return true; } @@ -1827,7 +1827,7 @@ 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); mve_update_eci(s); return true; @@ -1856,7 +1856,7 @@ 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); mve_update_eci(s); return true; @@ -1891,7 +1891,7 @@ 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); mve_update_eci(s); return true; @@ -1957,7 +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); + fn(tcg_env, qn, qm); if (a->mask) { /* VPT */ gen_vpst(s, a->mask); @@ -1988,7 +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); + fn(tcg_env, qn, rm); if (a->mask) { /* VPT */ gen_vpst(s, a->mask); @@ -2089,7 +2089,7 @@ 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); mve_update_eci(s); return true; @@ -2153,7 +2153,7 @@ 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); mve_update_eci(s); return true; diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 8de4ceb2039..c4fecb8fd6c 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -32,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; } @@ -42,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(); @@ -61,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(); @@ -83,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(); @@ -102,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(); @@ -148,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) @@ -266,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) @@ -296,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; } @@ -360,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) @@ -390,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; @@ -794,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) \ @@ -830,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, \ @@ -907,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(). @@ -961,146 +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); \ - } - -#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; + FUNC(d, tcg_env, n, m); \ } - 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); - - 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, \ @@ -1144,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) @@ -1180,58 +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); - - 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. */ @@ -1276,144 +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); - } - 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); - } - 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; @@ -1440,26 +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); + 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; @@ -1514,16 +1211,16 @@ static bool do_2shift_narrow_32(DisasContext *s, arg_2reg_shift *a, tcg_gen_concat_i32_i64(rtmp, rm1, rm2); - narrowfn(rm1, cpu_env, rtmp); - write_neon_element32(rm1, a->vd, 0, MO_32); + 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); - narrowfn(rm3, cpu_env, rtmp); - write_neon_element32(rm3, a->vd, 1, MO_32); + narrowfn(rtmp, tcg_env, rtmp); + write_neon_element64(rtmp, a->vd, 1, MO_32); return true; } @@ -1538,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); } @@ -1712,13 +1409,13 @@ static bool do_fp_2sh(DisasContext *s, arg_2reg_shift *a, DO_FP_2SH(VCVT_SF, gen_helper_gvec_vcvt_sf) DO_FP_2SH(VCVT_UF, gen_helper_gvec_vcvt_uf) -DO_FP_2SH(VCVT_FS, gen_helper_gvec_vcvt_fs) -DO_FP_2SH(VCVT_FU, gen_helper_gvec_vcvt_fu) +DO_FP_2SH(VCVT_FS, gen_helper_gvec_vcvt_rz_fs) +DO_FP_2SH(VCVT_FU, gen_helper_gvec_vcvt_rz_fu) DO_FP_2SH(VCVT_SH, gen_helper_gvec_vcvt_sh) DO_FP_2SH(VCVT_UH, gen_helper_gvec_vcvt_uh) -DO_FP_2SH(VCVT_HS, gen_helper_gvec_vcvt_hs) -DO_FP_2SH(VCVT_HU, gen_helper_gvec_vcvt_hu) +DO_FP_2SH(VCVT_HS, gen_helper_gvec_vcvt_rz_hs) +DO_FP_2SH(VCVT_HU, gen_helper_gvec_vcvt_rz_hu) static bool do_1reg_imm(DisasContext *s, arg_1reg_imm *a, GVecGen2iFn *fn) @@ -1863,8 +1560,8 @@ static bool do_prewiden_3d(DisasContext *s, arg_3diff *a, NULL, NULL, \ }; \ static NeonGenTwo64OpFn * const addfn[] = { \ - gen_helper_neon_##OP##l_u16, \ - gen_helper_neon_##OP##l_u32, \ + tcg_gen_vec_##OP##16_i64, \ + tcg_gen_vec_##OP##32_i64, \ tcg_gen_##OP##_i64, \ NULL, \ }; \ @@ -1942,8 +1639,8 @@ static bool do_narrow_3d(DisasContext *s, arg_3diff *a, static bool trans_##INSN##_3d(DisasContext *s, arg_3diff *a) \ { \ static NeonGenTwo64OpFn * const addfn[] = { \ - gen_helper_neon_##OP##l_u16, \ - gen_helper_neon_##OP##l_u32, \ + tcg_gen_vec_##OP##16_i64, \ + tcg_gen_vec_##OP##32_i64, \ tcg_gen_##OP##_i64, \ NULL, \ }; \ @@ -2064,8 +1761,8 @@ static bool trans_VABAL_S_3d(DisasContext *s, arg_3diff *a) NULL, }; static NeonGenTwo64OpFn * const addfn[] = { - gen_helper_neon_addl_u16, - gen_helper_neon_addl_u32, + tcg_gen_vec_add16_i64, + tcg_gen_vec_add32_i64, tcg_gen_add_i64, NULL, }; @@ -2082,8 +1779,8 @@ static bool trans_VABAL_U_3d(DisasContext *s, arg_3diff *a) NULL, }; static NeonGenTwo64OpFn * const addfn[] = { - gen_helper_neon_addl_u16, - gen_helper_neon_addl_u32, + tcg_gen_vec_add16_i64, + tcg_gen_vec_add32_i64, tcg_gen_add_i64, NULL, }; @@ -2143,8 +1840,8 @@ static bool trans_VMULL_U_3d(DisasContext *s, arg_3diff *a) NULL, \ }; \ static NeonGenTwo64OpFn * const accfn[] = { \ - gen_helper_neon_##ACC##l_u16, \ - gen_helper_neon_##ACC##l_u32, \ + tcg_gen_vec_##ACC##16_i64, \ + tcg_gen_vec_##ACC##32_i64, \ tcg_gen_##ACC##_i64, \ NULL, \ }; \ @@ -2159,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) @@ -2182,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) @@ -2211,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) @@ -2550,7 +2247,7 @@ 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); } return true; @@ -2674,7 +2371,7 @@ static bool trans_VMULL_U_2sc(DisasContext *s, arg_2scalar *a) }; \ static NeonGenTwo64OpFn * const accfn[] = { \ NULL, \ - gen_helper_neon_##ACC##l_u32, \ + tcg_gen_vec_##ACC##32_i64, \ tcg_gen_##ACC##_i64, \ NULL, \ }; \ @@ -2837,7 +2534,7 @@ 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); return true; } @@ -2868,204 +2565,6 @@ static bool trans_VDUP_scalar(DisasContext *s, arg_VDUP_scalar *a) return true; } -static bool trans_VREV64(DisasContext *s, arg_VREV64 *a) -{ - int pass, half; - TCGv_i32 tmp[2]; - - 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->vd | a->vm) & a->q) { - return false; - } - - if (a->size == 3) { - return false; - } - - if (!vfp_access_check(s)) { - return true; - } - - tmp[0] = tcg_temp_new_i32(); - tmp[1] = tcg_temp_new_i32(); - - for (pass = 0; pass < (a->q ? 2 : 1); pass++) { - for (half = 0; half < 2; half++) { - read_neon_element32(tmp[half], a->vm, pass * 2 + half, MO_32); - switch (a->size) { - case 0: - tcg_gen_bswap32_i32(tmp[half], tmp[half]); - break; - case 1: - gen_swap_half(tmp[half], tmp[half]); - break; - case 2: - break; - default: - g_assert_not_reached(); - } - } - write_neon_element32(tmp[1], a->vd, pass * 2, MO_32); - write_neon_element32(tmp[0], a->vd, pass * 2 + 1, MO_32); - } - return true; -} - -static bool do_2misc_pairwise(DisasContext *s, arg_2misc *a, - NeonGenWidenFn *widenfn, - NeonGenTwo64OpFn *opfn, - NeonGenTwo64OpFn *accfn) -{ - /* - * Pairwise long operations: widen both halves of the pair, - * combine the pairs with the opfn, and then possibly accumulate - * into the destination with the accfn. - */ - 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->vd | a->vm) & a->q) { - return false; - } - - if (!widenfn) { - return false; - } - - if (!vfp_access_check(s)) { - return true; - } - - for (pass = 0; pass < a->q + 1; pass++) { - TCGv_i32 tmp; - TCGv_i64 rm0_64, rm1_64, rd_64; - - rm0_64 = tcg_temp_new_i64(); - rm1_64 = tcg_temp_new_i64(); - rd_64 = tcg_temp_new_i64(); - - tmp = tcg_temp_new_i32(); - read_neon_element32(tmp, a->vm, pass * 2, MO_32); - widenfn(rm0_64, tmp); - read_neon_element32(tmp, a->vm, pass * 2 + 1, MO_32); - widenfn(rm1_64, tmp); - - opfn(rd_64, rm0_64, 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); - } - write_neon_element64(rd_64, a->vd, pass, MO_64); - } - return true; -} - -static bool trans_VPADDL_S(DisasContext *s, arg_2misc *a) -{ - static NeonGenWidenFn * const widenfn[] = { - gen_helper_neon_widen_s8, - gen_helper_neon_widen_s16, - tcg_gen_ext_i32_i64, - NULL, - }; - static NeonGenTwo64OpFn * const opfn[] = { - gen_helper_neon_paddl_u16, - gen_helper_neon_paddl_u32, - tcg_gen_add_i64, - NULL, - }; - - return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size], NULL); -} - -static bool trans_VPADDL_U(DisasContext *s, arg_2misc *a) -{ - static NeonGenWidenFn * const widenfn[] = { - gen_helper_neon_widen_u8, - gen_helper_neon_widen_u16, - tcg_gen_extu_i32_i64, - NULL, - }; - static NeonGenTwo64OpFn * const opfn[] = { - gen_helper_neon_paddl_u16, - gen_helper_neon_paddl_u32, - tcg_gen_add_i64, - NULL, - }; - - return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size], NULL); -} - -static bool trans_VPADAL_S(DisasContext *s, arg_2misc *a) -{ - static NeonGenWidenFn * const widenfn[] = { - gen_helper_neon_widen_s8, - gen_helper_neon_widen_s16, - tcg_gen_ext_i32_i64, - NULL, - }; - static NeonGenTwo64OpFn * const opfn[] = { - gen_helper_neon_paddl_u16, - gen_helper_neon_paddl_u32, - tcg_gen_add_i64, - NULL, - }; - static NeonGenTwo64OpFn * const accfn[] = { - gen_helper_neon_addl_u16, - gen_helper_neon_addl_u32, - tcg_gen_add_i64, - NULL, - }; - - return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size], - accfn[a->size]); -} - -static bool trans_VPADAL_U(DisasContext *s, arg_2misc *a) -{ - static NeonGenWidenFn * const widenfn[] = { - gen_helper_neon_widen_u8, - gen_helper_neon_widen_u16, - tcg_gen_extu_i32_i64, - NULL, - }; - static NeonGenTwo64OpFn * const opfn[] = { - gen_helper_neon_paddl_u16, - gen_helper_neon_paddl_u32, - tcg_gen_add_i64, - NULL, - }; - static NeonGenTwo64OpFn * const accfn[] = { - gen_helper_neon_addl_u16, - gen_helper_neon_addl_u32, - tcg_gen_add_i64, - NULL, - }; - - return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size], - accfn[a->size]); -} - typedef void ZipFn(TCGv_ptr, TCGv_ptr); static bool do_zip_uzp(DisasContext *s, arg_2misc *a, @@ -3139,10 +2638,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; @@ -3167,22 +2665,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); + 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, \ @@ -3424,6 +2922,13 @@ DO_2MISC_VEC(VCGT0, gen_gvec_cgt0) DO_2MISC_VEC(VCLE0, gen_gvec_cle0) DO_2MISC_VEC(VCGE0, gen_gvec_cge0) DO_2MISC_VEC(VCLT0, gen_gvec_clt0) +DO_2MISC_VEC(VCLS, gen_gvec_cls) +DO_2MISC_VEC(VCLZ, gen_gvec_clz) +DO_2MISC_VEC(VREV64, gen_gvec_rev64) +DO_2MISC_VEC(VPADDL_S, gen_gvec_saddlp) +DO_2MISC_VEC(VPADDL_U, gen_gvec_uaddlp) +DO_2MISC_VEC(VPADAL_S, gen_gvec_sadalp) +DO_2MISC_VEC(VPADAL_U, gen_gvec_uadalp) static bool trans_VMVN(DisasContext *s, arg_2misc *a) { @@ -3433,6 +2938,30 @@ static bool trans_VMVN(DisasContext *s, arg_2misc *a) return do_2misc_vec(s, a, tcg_gen_gvec_not); } +static bool trans_VCNT(DisasContext *s, arg_2misc *a) +{ + if (a->size != 0) { + return false; + } + return do_2misc_vec(s, a, gen_gvec_cnt); +} + +static bool trans_VREV16(DisasContext *s, arg_2misc *a) +{ + if (a->size != 0) { + return false; + } + return do_2misc_vec(s, a, gen_gvec_rev16); +} + +static bool trans_VREV32(DisasContext *s, arg_2misc *a) +{ + if (a->size != 0 && a->size != 1) { + return false; + } + return do_2misc_vec(s, a, gen_gvec_rev32); +} + #define WRAP_2M_3_OOL_FN(WRAPNAME, FUNC, DATA) \ static void WRAPNAME(unsigned vece, uint32_t rd_ofs, \ uint32_t rm_ofs, uint32_t oprsz, \ @@ -3512,68 +3041,6 @@ static bool do_2misc(DisasContext *s, arg_2misc *a, NeonGenOneOpFn *fn) return true; } -static bool trans_VREV32(DisasContext *s, arg_2misc *a) -{ - static NeonGenOneOpFn * const fn[] = { - tcg_gen_bswap32_i32, - gen_swap_half, - NULL, - NULL, - }; - return do_2misc(s, a, fn[a->size]); -} - -static bool trans_VREV16(DisasContext *s, arg_2misc *a) -{ - if (a->size != 0) { - return false; - } - return do_2misc(s, a, gen_rev16); -} - -static bool trans_VCLS(DisasContext *s, arg_2misc *a) -{ - static NeonGenOneOpFn * const fn[] = { - gen_helper_neon_cls_s8, - gen_helper_neon_cls_s16, - gen_helper_neon_cls_s32, - NULL, - }; - return do_2misc(s, a, fn[a->size]); -} - -static void do_VCLZ_32(TCGv_i32 rd, TCGv_i32 rm) -{ - tcg_gen_clzi_i32(rd, rm, 32); -} - -static bool trans_VCLZ(DisasContext *s, arg_2misc *a) -{ - static NeonGenOneOpFn * const fn[] = { - gen_helper_neon_clz_u8, - gen_helper_neon_clz_u16, - do_VCLZ_32, - NULL, - }; - return do_2misc(s, a, fn[a->size]); -} - -static bool trans_VCNT(DisasContext *s, arg_2misc *a) -{ - if (a->size != 0) { - return false; - } - return do_2misc(s, a, gen_helper_neon_cnt_u8); -} - -static void gen_VABS_F(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - uint32_t oprsz, uint32_t maxsz) -{ - tcg_gen_gvec_andi(vece, rd_ofs, rm_ofs, - vece == MO_16 ? 0x7fff : 0x7fffffff, - oprsz, maxsz); -} - static bool trans_VABS_F(DisasContext *s, arg_2misc *a) { if (a->size == MO_16) { @@ -3583,15 +3050,7 @@ static bool trans_VABS_F(DisasContext *s, arg_2misc *a) } else if (a->size != MO_32) { return false; } - return do_2misc_vec(s, a, gen_VABS_F); -} - -static void gen_VNEG_F(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - uint32_t oprsz, uint32_t maxsz) -{ - tcg_gen_gvec_xori(vece, rd_ofs, rm_ofs, - vece == MO_16 ? 0x8000 : 0x80000000, - oprsz, maxsz); + return do_2misc_vec(s, a, gen_gvec_fabs); } static bool trans_VNEG_F(DisasContext *s, arg_2misc *a) @@ -3603,7 +3062,7 @@ static bool trans_VNEG_F(DisasContext *s, arg_2misc *a) } else if (a->size != MO_32) { return false; } - return do_2misc_vec(s, a, gen_VNEG_F); + return do_2misc_vec(s, a, gen_gvec_fneg); } static bool trans_VRECPE(DisasContext *s, arg_2misc *a) @@ -3611,7 +3070,7 @@ static bool trans_VRECPE(DisasContext *s, arg_2misc *a) if (a->size != 2) { return false; } - return do_2misc(s, a, gen_helper_recpe_u32); + return do_2misc_vec(s, a, gen_gvec_urecpe); } static bool trans_VRSQRTE(DisasContext *s, arg_2misc *a) @@ -3619,13 +3078,13 @@ static bool trans_VRSQRTE(DisasContext *s, arg_2misc *a) if (a->size != 2) { return false; } - return do_2misc(s, a, gen_helper_rsqrte_u32); + return do_2misc_vec(s, a, gen_gvec_ursqrte); } #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) @@ -3907,8 +3366,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/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 6038b0a06f1..fcbb3500161 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -49,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 @@ -90,7 +98,7 @@ 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_gen_add_ptr(addr, addr, cpu_env); + tcg_gen_add_ptr(addr, addr, tcg_env); return addr; } @@ -106,7 +114,7 @@ static TCGv_ptr get_tile(DisasContext *s, int esz, int tile) offset = tile * sizeof(ARMVectorReg) + offsetof(CPUARMState, zarray); - tcg_gen_addi_ptr(addr, cpu_env, offset); + tcg_gen_addi_ptr(addr, tcg_env, offset); return addr; } @@ -116,7 +124,7 @@ static bool trans_ZERO(DisasContext *s, arg_ZERO *a) 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; @@ -206,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]; @@ -224,20 +232,13 @@ 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)); return true; } @@ -311,6 +312,7 @@ static bool do_outprod(DisasContext *s, arg_op *a, MemOp esz, } 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); @@ -326,18 +328,41 @@ static bool do_outprod_fpst(DisasContext *s, arg_op *a, MemOp esz, 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)); 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; + + 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_A64, gen_helper_sme_fmopa_s) +TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, + MO_64, FPST_A64, gen_helper_sme_fmopa_d) -/* TODO: FEAT_EBF16 */ -TRANS_FEAT(BFMOPA, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_bfmopa) +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/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 2ba5efadfd6..f3cf028cb92 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -50,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. */ @@ -123,11 +137,11 @@ static bool gen_gvec_fpst_zz(DisasContext *s, gen_helper_gvec_2_ptr *fn, return true; } -static bool gen_gvec_fpst_arg_zz(DisasContext *s, gen_helper_gvec_2_ptr *fn, - arg_rr_esz *a, int data) +static bool gen_gvec_fpst_ah_arg_zz(DisasContext *s, gen_helper_gvec_2_ptr *fn, + arg_rr_esz *a, int data) { return gen_gvec_fpst_zz(s, fn, a->rd, a->rn, data, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + select_ah_fpst(s, a->esz)); } /* Invoke an out-of-line helper on 3 Zregs. */ @@ -177,7 +191,14 @@ static bool gen_gvec_fpst_arg_zzz(DisasContext *s, gen_helper_gvec_3_ptr *fn, arg_rrr_esz *a, int data) { return gen_gvec_fpst_zzz(s, fn, a->rd, a->rn, a->rm, data, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); +} + +static bool gen_gvec_fpst_ah_arg_zzz(DisasContext *s, gen_helper_gvec_3_ptr *fn, + arg_rrr_esz *a, int data) +{ + return gen_gvec_fpst_zzz(s, fn, a->rd, a->rn, a->rm, data, + select_ah_fpst(s, a->esz)); } /* Invoke an out-of-line helper on 4 Zregs. */ @@ -238,6 +259,25 @@ static bool gen_gvec_fpst_zzzz(DisasContext *s, gen_helper_gvec_4_ptr *fn, 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, @@ -364,7 +404,7 @@ static bool gen_gvec_fpst_arg_zpzz(DisasContext *s, gen_helper_gvec_4_ptr *fn, arg_rprr_esz *a) { return gen_gvec_fpst_zzzp(s, fn, a->rd, a->rn, a->rm, a->pg, 0, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); } /* Invoke a vector expander on two Zregs and an immediate. */ @@ -497,8 +537,8 @@ 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)); @@ -527,94 +567,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); -} - -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 bool trans_XAR(DisasContext *s, arg_rrri_esz *a) { if (a->esz < 0 || !dc_isar_feature(aa64_sve2, s)) { @@ -629,61 +581,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) @@ -704,14 +603,8 @@ static void gen_bsl1n_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k) static void gen_bsl1n_vec(unsigned vece, TCGv_vec d, TCGv_vec n, TCGv_vec m, TCGv_vec k) { - if (TCG_TARGET_HAS_bitsel_vec) { - tcg_gen_not_vec(vece, n, n); - tcg_gen_bitsel_vec(vece, d, k, n, m); - } else { - tcg_gen_andc_vec(vece, n, k, n); - tcg_gen_andc_vec(vece, m, m, k); - tcg_gen_or_vec(vece, d, n, m); - } + tcg_gen_not_vec(vece, n, n); + tcg_gen_bitsel_vec(vece, d, k, n, m); } static void gen_bsl1n(unsigned vece, uint32_t d, uint32_t n, uint32_t m, @@ -736,7 +629,7 @@ static void gen_bsl2n_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k) * = | ~(m | k) */ tcg_gen_and_i64(n, n, k); - if (TCG_TARGET_HAS_orc_i64) { + if (tcg_op_supported(INDEX_op_orc, TCG_TYPE_I64, 0)) { tcg_gen_or_i64(m, m, k); tcg_gen_orc_i64(d, n, m); } else { @@ -748,14 +641,8 @@ static void gen_bsl2n_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k) static void gen_bsl2n_vec(unsigned vece, TCGv_vec d, TCGv_vec n, TCGv_vec m, TCGv_vec k) { - if (TCG_TARGET_HAS_bitsel_vec) { - tcg_gen_not_vec(vece, m, m); - tcg_gen_bitsel_vec(vece, d, k, n, m); - } else { - tcg_gen_and_vec(vece, n, n, k); - tcg_gen_or_vec(vece, m, m, k); - tcg_gen_orc_vec(vece, d, n, m); - } + tcg_gen_not_vec(vece, m, m); + tcg_gen_bitsel_vec(vece, d, k, n, m); } static void gen_bsl2n(unsigned vece, uint32_t d, uint32_t n, uint32_t m, @@ -896,13 +783,23 @@ static gen_helper_gvec_3 * const fabs_fns[4] = { NULL, gen_helper_sve_fabs_h, gen_helper_sve_fabs_s, gen_helper_sve_fabs_d, }; -TRANS_FEAT(FABS, aa64_sve, gen_gvec_ool_arg_zpz, fabs_fns[a->esz], a, 0) +static gen_helper_gvec_3 * const fabs_ah_fns[4] = { + NULL, gen_helper_sve_ah_fabs_h, + gen_helper_sve_ah_fabs_s, gen_helper_sve_ah_fabs_d, +}; +TRANS_FEAT(FABS, aa64_sve, gen_gvec_ool_arg_zpz, + s->fpcr_ah ? fabs_ah_fns[a->esz] : fabs_fns[a->esz], a, 0) static gen_helper_gvec_3 * const fneg_fns[4] = { NULL, gen_helper_sve_fneg_h, gen_helper_sve_fneg_s, gen_helper_sve_fneg_d, }; -TRANS_FEAT(FNEG, aa64_sve, gen_gvec_ool_arg_zpz, fneg_fns[a->esz], a, 0) +static gen_helper_gvec_3 * const fneg_ah_fns[4] = { + NULL, gen_helper_sve_ah_fneg_h, + gen_helper_sve_ah_fneg_s, gen_helper_sve_ah_fneg_d, +}; +TRANS_FEAT(FNEG, aa64_sve, gen_gvec_ool_arg_zpz, + s->fpcr_ah ? fneg_ah_fns[a->esz] : fneg_fns[a->esz], a, 0) static gen_helper_gvec_3 * const sxtb_fns[4] = { NULL, gen_helper_sve_sxtb_h, @@ -956,8 +853,8 @@ 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); write_fp_dreg(s, a->rd, temp); @@ -1209,7 +1106,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 { @@ -1341,14 +1238,14 @@ static gen_helper_gvec_2 * const fexpa_fns[4] = { gen_helper_sve_fexpa_s, gen_helper_sve_fexpa_d, }; TRANS_FEAT_NONSTREAMING(FEXPA, aa64_sve, gen_gvec_ool_zz, - fexpa_fns[a->esz], a->rd, a->rn, 0) + fexpa_fns[a->esz], a->rd, a->rn, s->fpcr_ah) static gen_helper_gvec_3 * const ftssel_fns[4] = { NULL, gen_helper_sve_ftssel_h, gen_helper_sve_ftssel_s, gen_helper_sve_ftssel_d, }; TRANS_FEAT_NONSTREAMING(FTSSEL, aa64_sve, gen_gvec_ool_arg_zzz, - ftssel_fns[a->esz], a, 0) + ftssel_fns[a->esz], a, s->fpcr_ah) /* *** SVE Predicate Logical Operations Group @@ -1379,12 +1276,12 @@ 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); } else { @@ -1654,8 +1551,8 @@ 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); } else { do_predtest(s, nofs, gofs, words); @@ -1736,7 +1633,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; } @@ -1755,17 +1652,17 @@ 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); } } @@ -1822,8 +1719,8 @@ 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)); @@ -1919,8 +1816,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) { @@ -2163,9 +2060,9 @@ 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); } @@ -2310,8 +2207,8 @@ 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); } @@ -2323,7 +2220,7 @@ 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); } return true; @@ -2409,9 +2306,9 @@ 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)); return true; @@ -2429,8 +2326,8 @@ 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); @@ -2525,7 +2422,7 @@ 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)); } @@ -2602,7 +2499,7 @@ 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); return load_esz(p, vec_full_reg_offset(s, rm), esz); } @@ -2674,7 +2571,7 @@ 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. */ @@ -2690,7 +2587,7 @@ 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); @@ -2794,7 +2691,7 @@ 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); } return true; @@ -2847,10 +2744,10 @@ 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))); @@ -2920,9 +2817,9 @@ 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))); @@ -2971,10 +2868,10 @@ 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(); @@ -3001,9 +2898,9 @@ 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(); @@ -3044,10 +2941,10 @@ 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); } @@ -3066,8 +2963,8 @@ 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)); } @@ -3291,7 +3188,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)); @@ -3354,7 +3251,7 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *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)); gen_helper_sve_whilel(t2, ptr, t2, tcg_constant_i32(desc)); do_pred_flags(t2); @@ -3627,21 +3524,24 @@ DO_SVE2_RRXR_ROT(CDOT_zzxw_d, gen_helper_sve2_cdot_idx_d) *** SVE Floating Point Multiply-Add Indexed Group */ -static bool do_FMLA_zzxz(DisasContext *s, arg_rrxr_esz *a, bool sub) -{ - static gen_helper_gvec_4_ptr * const fns[4] = { - NULL, - gen_helper_gvec_fmla_idx_h, - gen_helper_gvec_fmla_idx_s, - gen_helper_gvec_fmla_idx_d, - }; - return gen_gvec_fpst_zzzz(s, fns[a->esz], a->rd, a->rn, a->rm, a->ra, - (a->index << 1) | sub, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); -} +static gen_helper_gvec_4_ptr * const fmla_idx_fns[4] = { + NULL, gen_helper_gvec_fmla_idx_h, + gen_helper_gvec_fmla_idx_s, gen_helper_gvec_fmla_idx_d +}; +TRANS_FEAT(FMLA_zzxz, aa64_sve, gen_gvec_fpst_zzzz, + fmla_idx_fns[a->esz], a->rd, a->rn, a->rm, a->ra, a->index, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) -TRANS_FEAT(FMLA_zzxz, aa64_sve, do_FMLA_zzxz, a, false) -TRANS_FEAT(FMLS_zzxz, aa64_sve, do_FMLA_zzxz, a, true) +static gen_helper_gvec_4_ptr * const fmls_idx_fns[4][2] = { + { NULL, NULL }, + { gen_helper_gvec_fmls_idx_h, gen_helper_gvec_ah_fmls_idx_h }, + { gen_helper_gvec_fmls_idx_s, gen_helper_gvec_ah_fmls_idx_s }, + { gen_helper_gvec_fmls_idx_d, gen_helper_gvec_ah_fmls_idx_d }, +}; +TRANS_FEAT(FMLS_zzxz, aa64_sve, gen_gvec_fpst_zzzz, + fmls_idx_fns[a->esz][s->fpcr_ah], + a->rd, a->rn, a->rm, a->ra, a->index, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) /* *** SVE Floating Point Multiply Indexed Group @@ -3653,7 +3553,7 @@ static gen_helper_gvec_3_ptr * const fmul_idx_fns[4] = { }; TRANS_FEAT(FMUL_zzx, aa64_sve, gen_gvec_fpst_zzz, fmul_idx_fns[a->esz], a->rd, a->rn, a->rm, a->index, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) /* *** SVE Floating Point Fast Reduction Group @@ -3684,9 +3584,9 @@ 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)); - status = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + 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_A64_F16 : FPST_A64); fn(temp, t_zn, t_pg, status, t_desc); @@ -3701,11 +3601,23 @@ static bool do_reduce(DisasContext *s, arg_rpr_esz *a, }; \ TRANS_FEAT(NAME, aa64_sve, do_reduce, a, name##_fns[a->esz]) +#define DO_VPZ_AH(NAME, name) \ + static gen_helper_fp_reduce * const name##_fns[4] = { \ + NULL, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, gen_helper_sve_##name##_d, \ + }; \ + static gen_helper_fp_reduce * const name##_ah_fns[4] = { \ + NULL, gen_helper_sve_ah_##name##_h, \ + gen_helper_sve_ah_##name##_s, gen_helper_sve_ah_##name##_d, \ + }; \ + TRANS_FEAT(NAME, aa64_sve, do_reduce, a, \ + s->fpcr_ah ? name##_ah_fns[a->esz] : name##_fns[a->esz]) + DO_VPZ(FADDV, faddv) DO_VPZ(FMINNMV, fminnmv) DO_VPZ(FMAXNMV, fmaxnmv) -DO_VPZ(FMINV, fminv) -DO_VPZ(FMAXV, fmaxv) +DO_VPZ_AH(FMINV, fminv) +DO_VPZ_AH(FMAXV, fmaxv) #undef DO_VPZ @@ -3717,13 +3629,25 @@ static gen_helper_gvec_2_ptr * const frecpe_fns[] = { NULL, gen_helper_gvec_frecpe_h, gen_helper_gvec_frecpe_s, gen_helper_gvec_frecpe_d, }; -TRANS_FEAT(FRECPE, aa64_sve, gen_gvec_fpst_arg_zz, frecpe_fns[a->esz], a, 0) +static gen_helper_gvec_2_ptr * const frecpe_rpres_fns[] = { + NULL, gen_helper_gvec_frecpe_h, + gen_helper_gvec_frecpe_rpres_s, gen_helper_gvec_frecpe_d, +}; +TRANS_FEAT(FRECPE, aa64_sve, gen_gvec_fpst_ah_arg_zz, + s->fpcr_ah && dc_isar_feature(aa64_rpres, s) ? + frecpe_rpres_fns[a->esz] : frecpe_fns[a->esz], a, 0) static gen_helper_gvec_2_ptr * const frsqrte_fns[] = { NULL, gen_helper_gvec_frsqrte_h, gen_helper_gvec_frsqrte_s, gen_helper_gvec_frsqrte_d, }; -TRANS_FEAT(FRSQRTE, aa64_sve, gen_gvec_fpst_arg_zz, frsqrte_fns[a->esz], a, 0) +static gen_helper_gvec_2_ptr * const frsqrte_rpres_fns[] = { + NULL, gen_helper_gvec_frsqrte_h, + gen_helper_gvec_frsqrte_rpres_s, gen_helper_gvec_frsqrte_d, +}; +TRANS_FEAT(FRSQRTE, aa64_sve, gen_gvec_fpst_ah_arg_zz, + s->fpcr_ah && dc_isar_feature(aa64_rpres, s) ? + frsqrte_rpres_fns[a->esz] : frsqrte_fns[a->esz], a, 0) /* *** SVE Floating Point Compare with Zero Group @@ -3738,7 +3662,7 @@ static bool do_ppz_fp(DisasContext *s, arg_rpr_esz *a, if (sve_access_check(s)) { unsigned vsz = vec_full_reg_size(s); TCGv_ptr status = - fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + fpstatus_ptr(a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); tcg_gen_gvec_3_ptr(pred_full_reg_offset(s, a->rd), vec_full_reg_offset(s, a->rn), @@ -3773,8 +3697,9 @@ static gen_helper_gvec_3_ptr * const ftmad_fns[4] = { gen_helper_sve_ftmad_s, gen_helper_sve_ftmad_d, }; TRANS_FEAT_NONSTREAMING(FTMAD, aa64_sve, gen_gvec_fpst_zzz, - ftmad_fns[a->esz], a->rd, a->rn, a->rm, a->imm, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + ftmad_fns[a->esz], a->rd, a->rn, a->rm, + a->imm | (s->fpcr_ah << 3), + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) /* *** SVE Floating Point Accumulating Reduction Group @@ -3802,12 +3727,12 @@ 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)); - t_fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + 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_A64_F16 : FPST_A64); 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); @@ -3827,11 +3752,23 @@ static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a) }; \ TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_arg_zzz, name##_fns[a->esz], a, 0) +#define DO_FP3_AH(NAME, name) \ + static gen_helper_gvec_3_ptr * const name##_fns[4] = { \ + NULL, gen_helper_gvec_##name##_h, \ + gen_helper_gvec_##name##_s, gen_helper_gvec_##name##_d \ + }; \ + static gen_helper_gvec_3_ptr * const name##_ah_fns[4] = { \ + NULL, gen_helper_gvec_ah_##name##_h, \ + gen_helper_gvec_ah_##name##_s, gen_helper_gvec_ah_##name##_d \ + }; \ + TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_ah_arg_zzz, \ + s->fpcr_ah ? name##_ah_fns[a->esz] : name##_fns[a->esz], a, 0) + DO_FP3(FADD_zzz, fadd) DO_FP3(FSUB_zzz, fsub) DO_FP3(FMUL_zzz, fmul) -DO_FP3(FRECPS, recps) -DO_FP3(FRSQRTS, rsqrts) +DO_FP3_AH(FRECPS, recps) +DO_FP3_AH(FRSQRTS, rsqrts) #undef DO_FP3 @@ -3853,14 +3790,27 @@ TRANS_FEAT_NONSTREAMING(FTSMUL, aa64_sve, gen_gvec_fpst_arg_zzz, }; \ TRANS_FEAT(NAME, FEAT, gen_gvec_fpst_arg_zpzz, name##_zpzz_fns[a->esz], a) +#define DO_ZPZZ_AH_FP(NAME, FEAT, name, ah_name) \ + static gen_helper_gvec_4_ptr * const name##_zpzz_fns[4] = { \ + NULL, gen_helper_##name##_h, \ + gen_helper_##name##_s, gen_helper_##name##_d \ + }; \ + static gen_helper_gvec_4_ptr * const name##_ah_zpzz_fns[4] = { \ + NULL, gen_helper_##ah_name##_h, \ + gen_helper_##ah_name##_s, gen_helper_##ah_name##_d \ + }; \ + TRANS_FEAT(NAME, FEAT, gen_gvec_fpst_arg_zpzz, \ + s->fpcr_ah ? name##_ah_zpzz_fns[a->esz] : \ + name##_zpzz_fns[a->esz], a) + DO_ZPZZ_FP(FADD_zpzz, aa64_sve, sve_fadd) DO_ZPZZ_FP(FSUB_zpzz, aa64_sve, sve_fsub) DO_ZPZZ_FP(FMUL_zpzz, aa64_sve, sve_fmul) -DO_ZPZZ_FP(FMIN_zpzz, aa64_sve, sve_fmin) -DO_ZPZZ_FP(FMAX_zpzz, aa64_sve, sve_fmax) +DO_ZPZZ_AH_FP(FMIN_zpzz, aa64_sve, sve_fmin, sve_ah_fmin) +DO_ZPZZ_AH_FP(FMAX_zpzz, aa64_sve, sve_fmax, sve_ah_fmax) DO_ZPZZ_FP(FMINNM_zpzz, aa64_sve, sve_fminnum) DO_ZPZZ_FP(FMAXNM_zpzz, aa64_sve, sve_fmaxnum) -DO_ZPZZ_FP(FABD, aa64_sve, sve_fabd) +DO_ZPZZ_AH_FP(FABD, aa64_sve, sve_fabd, sve_ah_fabd) DO_ZPZZ_FP(FSCALE, aa64_sve, sve_fscalbn) DO_ZPZZ_FP(FDIV, aa64_sve, sve_fdiv) DO_ZPZZ_FP(FMULX, aa64_sve, sve_fmulx) @@ -3878,11 +3828,11 @@ 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); + status = fpstatus_ptr(is_fp16 ? FPST_A64_F16 : FPST_A64); desc = tcg_constant_i32(simd_desc(vsz, vsz, 0)); fn(t_zd, t_zn, t_pg, scalar, status, desc); } @@ -3915,14 +3865,35 @@ static bool do_fp_imm(DisasContext *s, arg_rpri_esz *a, uint64_t imm, TRANS_FEAT(NAME##_zpzi, aa64_sve, do_fp_imm, a, \ name##_const[a->esz][a->imm], name##_fns[a->esz]) +#define DO_FP_AH_IMM(NAME, name, const0, const1) \ + static gen_helper_sve_fp2scalar * const name##_fns[4] = { \ + NULL, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, \ + gen_helper_sve_##name##_d \ + }; \ + static gen_helper_sve_fp2scalar * const name##_ah_fns[4] = { \ + NULL, gen_helper_sve_ah_##name##_h, \ + gen_helper_sve_ah_##name##_s, \ + gen_helper_sve_ah_##name##_d \ + }; \ + static uint64_t const name##_const[4][2] = { \ + { -1, -1 }, \ + { float16_##const0, float16_##const1 }, \ + { float32_##const0, float32_##const1 }, \ + { float64_##const0, float64_##const1 }, \ + }; \ + TRANS_FEAT(NAME##_zpzi, aa64_sve, do_fp_imm, a, \ + name##_const[a->esz][a->imm], \ + s->fpcr_ah ? name##_ah_fns[a->esz] : name##_fns[a->esz]) + DO_FP_IMM(FADD, fadds, half, one) DO_FP_IMM(FSUB, fsubs, half, one) DO_FP_IMM(FMUL, fmuls, half, two) DO_FP_IMM(FSUBR, fsubrs, half, one) DO_FP_IMM(FMAXNM, fmaxnms, zero, one) DO_FP_IMM(FMINNM, fminnms, zero, one) -DO_FP_IMM(FMAX, fmaxs, zero, one) -DO_FP_IMM(FMIN, fmins, zero, one) +DO_FP_AH_IMM(FMAX, fmaxs, zero, one) +DO_FP_AH_IMM(FMIN, fmins, zero, one) #undef DO_FP_IMM @@ -3934,7 +3905,7 @@ static bool do_fp_cmp(DisasContext *s, arg_rprr_esz *a, } if (sve_access_check(s)) { unsigned vsz = vec_full_reg_size(s); - TCGv_ptr status = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + TCGv_ptr status = fpstatus_ptr(a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); tcg_gen_gvec_4_ptr(pred_full_reg_offset(s, a->rd), vec_full_reg_offset(s, a->rn), vec_full_reg_offset(s, a->rm), @@ -3966,22 +3937,28 @@ static gen_helper_gvec_4_ptr * const fcadd_fns[] = { gen_helper_sve_fcadd_s, gen_helper_sve_fcadd_d, }; TRANS_FEAT(FCADD, aa64_sve, gen_gvec_fpst_zzzp, fcadd_fns[a->esz], - a->rd, a->rn, a->rm, a->pg, a->rot, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a->rd, a->rn, a->rm, a->pg, a->rot | (s->fpcr_ah << 1), + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) -#define DO_FMLA(NAME, name) \ +#define DO_FMLA(NAME, name, ah_name) \ static gen_helper_gvec_5_ptr * const name##_fns[4] = { \ NULL, gen_helper_sve_##name##_h, \ gen_helper_sve_##name##_s, gen_helper_sve_##name##_d \ }; \ - TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_zzzzp, name##_fns[a->esz], \ + static gen_helper_gvec_5_ptr * const name##_ah_fns[4] = { \ + NULL, gen_helper_sve_##ah_name##_h, \ + gen_helper_sve_##ah_name##_s, gen_helper_sve_##ah_name##_d \ + }; \ + TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_zzzzp, \ + s->fpcr_ah ? name##_ah_fns[a->esz] : name##_fns[a->esz], \ a->rd, a->rn, a->rm, a->ra, a->pg, 0, \ - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) -DO_FMLA(FMLA_zpzzz, fmla_zpzzz) -DO_FMLA(FMLS_zpzzz, fmls_zpzzz) -DO_FMLA(FNMLA_zpzzz, fnmla_zpzzz) -DO_FMLA(FNMLS_zpzzz, fnmls_zpzzz) +/* We don't need an ah_fmla_zpzzz because fmla doesn't negate anything */ +DO_FMLA(FMLA_zpzzz, fmla_zpzzz, fmla_zpzzz) +DO_FMLA(FMLS_zpzzz, fmls_zpzzz, ah_fmls_zpzzz) +DO_FMLA(FNMLA_zpzzz, fnmla_zpzzz, ah_fnmla_zpzzz) +DO_FMLA(FNMLS_zpzzz, fnmls_zpzzz, ah_fnmls_zpzzz) #undef DO_FMLA @@ -3990,67 +3967,68 @@ static gen_helper_gvec_5_ptr * const fcmla_fns[4] = { gen_helper_sve_fcmla_zpzzz_s, gen_helper_sve_fcmla_zpzzz_d, }; TRANS_FEAT(FCMLA_zpzzz, aa64_sve, gen_gvec_fpst_zzzzp, fcmla_fns[a->esz], - a->rd, a->rn, a->rm, a->ra, a->pg, a->rot, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a->rd, a->rn, a->rm, a->ra, a->pg, a->rot | (s->fpcr_ah << 2), + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) static gen_helper_gvec_4_ptr * const fcmla_idx_fns[4] = { NULL, gen_helper_gvec_fcmlah_idx, gen_helper_gvec_fcmlas_idx, NULL }; TRANS_FEAT(FCMLA_zzxz, aa64_sve, gen_gvec_fpst_zzzz, fcmla_idx_fns[a->esz], a->rd, a->rn, a->rm, a->ra, a->index * 4 + a->rot, - a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) /* *** SVE Floating Point Unary Operations Predicated Group */ TRANS_FEAT(FCVT_sh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvt_sh, a, 0, FPST_FPCR) + gen_helper_sve_fcvt_sh, a, 0, FPST_A64) TRANS_FEAT(FCVT_hs, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvt_hs, a, 0, FPST_FPCR) + gen_helper_sve_fcvt_hs, a, 0, FPST_A64_F16) TRANS_FEAT(BFCVT, aa64_sve_bf16, gen_gvec_fpst_arg_zpz, - gen_helper_sve_bfcvt, a, 0, FPST_FPCR) + gen_helper_sve_bfcvt, a, 0, + s->fpcr_ah ? FPST_AH : FPST_A64) TRANS_FEAT(FCVT_dh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvt_dh, a, 0, FPST_FPCR) + gen_helper_sve_fcvt_dh, a, 0, FPST_A64) TRANS_FEAT(FCVT_hd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvt_hd, a, 0, FPST_FPCR) + gen_helper_sve_fcvt_hd, a, 0, FPST_A64_F16) TRANS_FEAT(FCVT_ds, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvt_ds, a, 0, FPST_FPCR) + gen_helper_sve_fcvt_ds, a, 0, FPST_A64) TRANS_FEAT(FCVT_sd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvt_sd, a, 0, FPST_FPCR) + gen_helper_sve_fcvt_sd, a, 0, FPST_A64) TRANS_FEAT(FCVTZS_hh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzs_hh, a, 0, FPST_FPCR_F16) + gen_helper_sve_fcvtzs_hh, a, 0, FPST_A64_F16) TRANS_FEAT(FCVTZU_hh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzu_hh, a, 0, FPST_FPCR_F16) + gen_helper_sve_fcvtzu_hh, a, 0, FPST_A64_F16) TRANS_FEAT(FCVTZS_hs, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzs_hs, a, 0, FPST_FPCR_F16) + gen_helper_sve_fcvtzs_hs, a, 0, FPST_A64_F16) TRANS_FEAT(FCVTZU_hs, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzu_hs, a, 0, FPST_FPCR_F16) + gen_helper_sve_fcvtzu_hs, a, 0, FPST_A64_F16) TRANS_FEAT(FCVTZS_hd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzs_hd, a, 0, FPST_FPCR_F16) + gen_helper_sve_fcvtzs_hd, a, 0, FPST_A64_F16) TRANS_FEAT(FCVTZU_hd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzu_hd, a, 0, FPST_FPCR_F16) + gen_helper_sve_fcvtzu_hd, a, 0, FPST_A64_F16) TRANS_FEAT(FCVTZS_ss, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzs_ss, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzs_ss, a, 0, FPST_A64) TRANS_FEAT(FCVTZU_ss, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzu_ss, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzu_ss, a, 0, FPST_A64) TRANS_FEAT(FCVTZS_sd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzs_sd, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzs_sd, a, 0, FPST_A64) TRANS_FEAT(FCVTZU_sd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzu_sd, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzu_sd, a, 0, FPST_A64) TRANS_FEAT(FCVTZS_ds, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzs_ds, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzs_ds, a, 0, FPST_A64) TRANS_FEAT(FCVTZU_ds, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzu_ds, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzu_ds, a, 0, FPST_A64) TRANS_FEAT(FCVTZS_dd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzs_dd, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzs_dd, a, 0, FPST_A64) TRANS_FEAT(FCVTZU_dd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_fcvtzu_dd, a, 0, FPST_FPCR) + gen_helper_sve_fcvtzu_dd, a, 0, FPST_A64) static gen_helper_gvec_3_ptr * const frint_fns[] = { NULL, @@ -4059,7 +4037,7 @@ static gen_helper_gvec_3_ptr * const frint_fns[] = { gen_helper_sve_frint_d }; TRANS_FEAT(FRINTI, aa64_sve, gen_gvec_fpst_arg_zpz, frint_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) static gen_helper_gvec_3_ptr * const frintx_fns[] = { NULL, @@ -4068,7 +4046,7 @@ static gen_helper_gvec_3_ptr * const frintx_fns[] = { gen_helper_sve_frintx_d }; 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); + a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, ARMFPRounding mode, gen_helper_gvec_3_ptr *fn) @@ -4085,7 +4063,7 @@ static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, } vsz = vec_full_reg_size(s); - status = fpstatus_ptr(a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + status = fpstatus_ptr(a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); tmode = gen_set_rmode(mode, status); tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), @@ -4113,48 +4091,48 @@ static gen_helper_gvec_3_ptr * const frecpx_fns[] = { gen_helper_sve_frecpx_s, gen_helper_sve_frecpx_d, }; TRANS_FEAT(FRECPX, aa64_sve, gen_gvec_fpst_arg_zpz, frecpx_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a, 0, select_ah_fpst(s, a->esz)) static gen_helper_gvec_3_ptr * const fsqrt_fns[] = { NULL, gen_helper_sve_fsqrt_h, gen_helper_sve_fsqrt_s, gen_helper_sve_fsqrt_d, }; TRANS_FEAT(FSQRT, aa64_sve, gen_gvec_fpst_arg_zpz, fsqrt_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) TRANS_FEAT(SCVTF_hh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_scvt_hh, a, 0, FPST_FPCR_F16) + gen_helper_sve_scvt_hh, a, 0, FPST_A64_F16) TRANS_FEAT(SCVTF_sh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_scvt_sh, a, 0, FPST_FPCR_F16) + gen_helper_sve_scvt_sh, a, 0, FPST_A64_F16) TRANS_FEAT(SCVTF_dh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_scvt_dh, a, 0, FPST_FPCR_F16) + gen_helper_sve_scvt_dh, a, 0, FPST_A64_F16) TRANS_FEAT(SCVTF_ss, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_scvt_ss, a, 0, FPST_FPCR) + gen_helper_sve_scvt_ss, a, 0, FPST_A64) TRANS_FEAT(SCVTF_ds, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_scvt_ds, a, 0, FPST_FPCR) + gen_helper_sve_scvt_ds, a, 0, FPST_A64) TRANS_FEAT(SCVTF_sd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_scvt_sd, a, 0, FPST_FPCR) + gen_helper_sve_scvt_sd, a, 0, FPST_A64) TRANS_FEAT(SCVTF_dd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_scvt_dd, a, 0, FPST_FPCR) + gen_helper_sve_scvt_dd, a, 0, FPST_A64) TRANS_FEAT(UCVTF_hh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_ucvt_hh, a, 0, FPST_FPCR_F16) + gen_helper_sve_ucvt_hh, a, 0, FPST_A64_F16) TRANS_FEAT(UCVTF_sh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_ucvt_sh, a, 0, FPST_FPCR_F16) + gen_helper_sve_ucvt_sh, a, 0, FPST_A64_F16) TRANS_FEAT(UCVTF_dh, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_ucvt_dh, a, 0, FPST_FPCR_F16) + gen_helper_sve_ucvt_dh, a, 0, FPST_A64_F16) TRANS_FEAT(UCVTF_ss, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_ucvt_ss, a, 0, FPST_FPCR) + gen_helper_sve_ucvt_ss, a, 0, FPST_A64) TRANS_FEAT(UCVTF_ds, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_ucvt_ds, a, 0, FPST_FPCR) + gen_helper_sve_ucvt_ds, a, 0, FPST_A64) TRANS_FEAT(UCVTF_sd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_ucvt_sd, a, 0, FPST_FPCR) + gen_helper_sve_ucvt_sd, a, 0, FPST_A64) TRANS_FEAT(UCVTF_dd, aa64_sve, gen_gvec_fpst_arg_zpz, - gen_helper_sve_ucvt_dd, a, 0, FPST_FPCR) + gen_helper_sve_ucvt_dd, a, 0, FPST_A64) /* *** SVE Memory - 32-bit Gather and Unsized Contiguous Group @@ -4228,7 +4206,7 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, /* * 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(); @@ -4294,7 +4272,7 @@ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); t16 = tcg_temp_new_i128(); - for (i = 0; i < len_align; i += 8) { + for (i = 0; i < len_align; i += 16) { tcg_gen_ld_i64(t0, base, vofs + i); tcg_gen_ld_i64(t1, base, vofs + i + 8); tcg_gen_concat_i64_i128(t16, t0, t1); @@ -4320,7 +4298,8 @@ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, t16 = tcg_temp_new_i128(); tcg_gen_concat_i64_i128(t16, t0, t1); - tcg_gen_qemu_st_i128(t16, clean_addr, midx, MO_LEUQ); + 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); @@ -4370,7 +4349,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; } @@ -4383,7 +4362,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; } @@ -4396,7 +4375,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; } @@ -4409,7 +4388,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; } @@ -4436,37 +4415,51 @@ static const uint8_t dtype_esz[16] = { 3, 2, 1, 3 }; -static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, - int dtype, uint32_t mte_n, bool is_write, - gen_helper_gvec_mem *fn) +uint32_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, + uint32_t msz, bool is_write, uint32_t data) { - unsigned vsz = vec_full_reg_size(s); - TCGv_ptr t_pg; - int desc = 0; + uint32_t sizem1; + uint32_t desc = 0; - /* - * 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); + /* 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, (mte_n << msz) - 1); + desc = FIELD_DP32(desc, MTEDESC, SIZEM1, sizem1); desc <<= SVE_MTEDESC_SHIFT; - } else { + } + 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 nregs, bool is_write, + gen_helper_gvec_mem *fn) +{ + TCGv_ptr t_pg; + uint32_t desc; + + if (!s->mte_active[0]) { addr = clean_data_tbi(s, addr); } - desc = simd_desc(vsz, vsz, zt | desc); + /* + * 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. + */ + 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_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] */ @@ -4599,7 +4592,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) @@ -4846,8 +4839,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) { /* @@ -4860,18 +4858,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_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))); + 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) { @@ -4914,6 +4913,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) { /* @@ -4926,6 +4926,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) { @@ -4939,18 +4942,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_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))); + 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. @@ -5027,7 +5031,7 @@ 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); } else { @@ -5167,14 +5171,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) @@ -5222,26 +5225,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)); + 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]. */ @@ -6173,9 +6167,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_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); @@ -6230,18 +6224,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_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_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); @@ -6409,18 +6403,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_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] = { @@ -6441,19 +6431,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_gen_bitsel_vec(vece, d, tcg_constant_vec_matching(d, vece, mask), d, n); } static const GVecGen2 sqxtnt_ops[3] = { @@ -6481,12 +6467,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_gen_umin_vec(vece, d, n, tcg_constant_vec_matching(d, vece, max)); } static const GVecGen2 uqxtnb_ops[3] = { @@ -6507,14 +6491,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_gen_bitsel_vec(vece, d, maxv, d, n); } static const GVecGen2 uqxtnt_ops[3] = { @@ -6542,14 +6525,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_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] = { @@ -6570,16 +6550,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_gen_bitsel_vec(vece, d, maxv, d, n); } static const GVecGen2 sqxtunt_ops[3] = { @@ -6643,13 +6621,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_gen_and_vec(vece, d, n, tcg_constant_vec_matching(d, vece, mask)); } static const TCGOpcode shrnb_vec_list[] = { INDEX_op_shri_vec, 0 }; @@ -6701,13 +6677,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_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 }; @@ -6750,14 +6724,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_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[] = { @@ -6782,16 +6754,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_gen_bitsel_vec(vece, d, maxv, d, n); } static const TCGOpcode sqshrunt_vec_list[] = { @@ -6834,18 +6805,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_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[] = { @@ -6870,19 +6838,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_gen_bitsel_vec(vece, d, tcg_constant_vec_matching(d, vece, mask), d, n); } static const TCGOpcode sqshrnt_vec_list[] = { @@ -6925,12 +6890,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_gen_umin_vec(vece, d, n, tcg_constant_vec_matching(d, vece, max)); } static const TCGOpcode uqshrnb_vec_list[] = { @@ -6955,14 +6919,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_gen_bitsel_vec(vece, d, maxv, d, n); } static const TCGOpcode uqshrnt_vec_list[] = { @@ -7050,10 +7014,10 @@ DO_ZPZZ_FP(FMINP, aa64_sve2, sve2_fminp_zpzz) TRANS_FEAT_NONSTREAMING(FMMLA_s, aa64_sve_f32mm, gen_gvec_fpst_zzzz, gen_helper_fmmla_s, a->rd, a->rn, a->rm, a->ra, - 0, FPST_FPCR) + 0, FPST_A64) TRANS_FEAT_NONSTREAMING(FMMLA_d, aa64_sve_f64mm, gen_gvec_fpst_zzzz, gen_helper_fmmla_d, a->rd, a->rn, a->rm, a->ra, - 0, FPST_FPCR) + 0, FPST_A64) static gen_helper_gvec_4 * const sqdmlal_zzzw_fns[] = { NULL, gen_helper_sve2_sqdmlal_zzzw_h, @@ -7169,17 +7133,18 @@ TRANS_FEAT_NONSTREAMING(RAX1, aa64_sve2_sha3, gen_gvec_fn_arg_zzz, gen_gvec_rax1, a) TRANS_FEAT(FCVTNT_sh, aa64_sve2, gen_gvec_fpst_arg_zpz, - gen_helper_sve2_fcvtnt_sh, a, 0, FPST_FPCR) + gen_helper_sve2_fcvtnt_sh, a, 0, FPST_A64) TRANS_FEAT(FCVTNT_ds, aa64_sve2, gen_gvec_fpst_arg_zpz, - gen_helper_sve2_fcvtnt_ds, a, 0, FPST_FPCR) + gen_helper_sve2_fcvtnt_ds, a, 0, FPST_A64) TRANS_FEAT(BFCVTNT, aa64_sve_bf16, gen_gvec_fpst_arg_zpz, - gen_helper_sve_bfcvtnt, a, 0, FPST_FPCR) + gen_helper_sve_bfcvtnt, a, 0, + s->fpcr_ah ? FPST_AH : FPST_A64) TRANS_FEAT(FCVTLT_hs, aa64_sve2, gen_gvec_fpst_arg_zpz, - gen_helper_sve2_fcvtlt_hs, a, 0, FPST_FPCR) + gen_helper_sve2_fcvtlt_hs, a, 0, FPST_A64) TRANS_FEAT(FCVTLT_sd, aa64_sve2, gen_gvec_fpst_arg_zpz, - gen_helper_sve2_fcvtlt_sd, a, 0, FPST_FPCR) + gen_helper_sve2_fcvtlt_sd, a, 0, FPST_A64) TRANS_FEAT(FCVTX_ds, aa64_sve2, do_frint_mode, a, FPROUNDING_ODD, gen_helper_sve_fcvt_ds) @@ -7191,13 +7156,13 @@ static gen_helper_gvec_3_ptr * const flogb_fns[] = { gen_helper_flogb_s, gen_helper_flogb_d }; TRANS_FEAT(FLOGB, aa64_sve2, gen_gvec_fpst_arg_zpz, flogb_fns[a->esz], - a, 0, a->esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR) + a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) 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) @@ -7209,7 +7174,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) @@ -7224,18 +7189,19 @@ 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) { return gen_gvec_fpst_zzzz(s, gen_helper_gvec_bfmlal, - a->rd, a->rn, a->rm, a->ra, sel, FPST_FPCR); + a->rd, a->rn, a->rm, a->ra, sel, + s->fpcr_ah ? FPST_AH : FPST_A64); } TRANS_FEAT(BFMLALB_zzzw, aa64_sve_bf16, do_BFMLAL_zzzw, a, false) @@ -7245,7 +7211,8 @@ static bool do_BFMLAL_zzxw(DisasContext *s, arg_rrxr_esz *a, bool sel) { return gen_gvec_fpst_zzzz(s, gen_helper_gvec_bfmlal_idx, a->rd, a->rn, a->rm, a->ra, - (a->index << 1) | sel, FPST_FPCR); + (a->index << 1) | sel, + s->fpcr_ah ? FPST_AH : FPST_A64); } TRANS_FEAT(BFMLALB_zzxw, aa64_sve_bf16, do_BFMLAL_zzxw, a, false) @@ -7289,7 +7256,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. */ diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index d3e89fda918..8d9d1ab877a 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -30,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); } /* @@ -116,7 +122,7 @@ static void gen_preserve_fp_state(DisasContext *s, bool skip_context_update) if (translator_io_start(&s->base)) { s->base.is_jmp = DISAS_UPDATE_EXIT; } - 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 @@ -172,7 +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); + gen_helper_vfp_set_fpscr(tcg_env, fpscr); if (dc_isar_feature(aa32_mve, s)) { store_cpu_field(tcg_constant_i32(0), v7m.vpr); } @@ -454,9 +460,9 @@ static bool trans_VRINT(DisasContext *s, arg_VRINT *a) } if (sz == 1) { - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_F16); } else { - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); } tcg_rmode = gen_set_rmode(rounding, fpst); @@ -521,9 +527,9 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a) } if (sz == 1) { - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_F16); } else { - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); } tcg_shift = tcg_constant_i32(0); @@ -815,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)); } @@ -827,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: @@ -855,7 +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); + gen_helper_vfp_set_fpscr(tcg_env, tmp); gen_lookup_tb(s); break; case ARM_VFP_FPEXC: @@ -902,8 +908,7 @@ 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 */ @@ -1169,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; @@ -1252,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; @@ -1393,7 +1398,7 @@ static bool do_vfp_3op_sp(DisasContext *s, VFPGen3OpSPFn *fn, f0 = tcg_temp_new_i32(); f1 = tcg_temp_new_i32(); fd = tcg_temp_new_i32(); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); vfp_load_reg32(f0, vn); vfp_load_reg32(f1, vm); @@ -1428,7 +1433,7 @@ static bool do_vfp_3op_hp(DisasContext *s, VFPGen3OpSPFn *fn, /* * Do a half-precision operation. Functionally this is * the same as do_vfp_3op_sp(), except: - * - it uses the FPST_FPCR_F16 + * - it uses the FPST_A32_F16 * - it doesn't need the VFP vector handling (fp16 is a * v8 feature, and in v8 VFP vectors don't exist) * - it does the aa32_fp16_arith feature test @@ -1451,13 +1456,13 @@ static bool do_vfp_3op_hp(DisasContext *s, VFPGen3OpSPFn *fn, f0 = tcg_temp_new_i32(); f1 = tcg_temp_new_i32(); fd = tcg_temp_new_i32(); - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_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); @@ -1512,7 +1517,7 @@ static bool do_vfp_3op_dp(DisasContext *s, VFPGen3OpDPFn *fn, f0 = tcg_temp_new_i64(); f1 = tcg_temp_new_i64(); fd = tcg_temp_new_i64(); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); vfp_load_reg64(f0, vn); vfp_load_reg64(f1, vm); @@ -1633,7 +1638,7 @@ 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); @@ -1763,7 +1768,7 @@ 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); } @@ -1781,7 +1786,7 @@ 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); } @@ -1799,7 +1804,7 @@ 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); } @@ -1819,7 +1824,7 @@ 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); } @@ -1839,7 +1844,7 @@ 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); } @@ -1859,7 +1864,7 @@ 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); } @@ -1874,8 +1879,8 @@ 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); } @@ -1890,8 +1895,8 @@ 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); } @@ -1906,8 +1911,8 @@ 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); } @@ -1935,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) @@ -1947,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) @@ -1959,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) @@ -2106,18 +2111,18 @@ 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); + fpst = fpstatus_ptr(FPST_A32_F16); gen_helper_vfp_muladdh(vd, vn, vm, vd, fpst); vfp_store_reg32(vd, a->vd); return true; @@ -2169,14 +2174,14 @@ 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); + fpst = fpstatus_ptr(FPST_A32); gen_helper_vfp_muladds(vd, vn, vm, vd, fpst); vfp_store_reg32(vd, a->vd); return true; @@ -2185,8 +2190,8 @@ static bool do_vfm_sp(DisasContext *s, arg_VFMA_sp *a, bool neg_n, bool neg_d) 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) * @@ -2234,14 +2239,14 @@ 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); + fpst = fpstatus_ptr(FPST_A32); gen_helper_vfp_muladdd(vd, vn, vm, vd, fpst); vfp_store_reg64(vd, a->vd); return true; @@ -2257,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) @@ -2409,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, fpstatus_ptr(FPST_A32_F16)); } 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, fpstatus_ptr(FPST_A32)); } 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, fpstatus_ptr(FPST_A32)); } DO_VFP_2OP(VSQRT, hp, gen_VSQRT_hp, aa32_fp16_arith) @@ -2456,17 +2461,17 @@ 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); } return true; } @@ -2499,9 +2504,9 @@ 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); } return true; } @@ -2539,9 +2544,9 @@ 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); } return true; } @@ -2560,11 +2565,11 @@ static bool trans_VCVT_f32_f16(DisasContext *s, arg_VCVT_f32_f16 *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); 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); return true; @@ -2594,11 +2599,11 @@ static bool trans_VCVT_f64_f16(DisasContext *s, arg_VCVT_f64_f16 *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); 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); @@ -2618,12 +2623,12 @@ static bool trans_VCVT_b16_f32(DisasContext *s, arg_VCVT_b16_f32 *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); tmp = tcg_temp_new_i32(); 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_gen_st16_i32(tmp, tcg_env, vfp_f16_offset(a->vd, a->t)); return true; } @@ -2641,13 +2646,13 @@ static bool trans_VCVT_f16_f32(DisasContext *s, arg_VCVT_f16_f32 *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); ahp_mode = get_ahp_flag(); tmp = tcg_temp_new_i32(); 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_gen_st16_i32(tmp, tcg_env, vfp_f16_offset(a->vd, a->t)); return true; } @@ -2675,14 +2680,14 @@ static bool trans_VCVT_f16_f64(DisasContext *s, arg_VCVT_f16_f64 *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); ahp_mode = get_ahp_flag(); tmp = tcg_temp_new_i32(); vm = tcg_temp_new_i64(); vfp_load_reg64(vm, a->vm); gen_helper_vfp_fcvt_f64_to_f16(tmp, vm, fpst, ahp_mode); - tcg_gen_st16_i32(tmp, cpu_env, vfp_f16_offset(a->vd, a->t)); + tcg_gen_st16_i32(tmp, tcg_env, vfp_f16_offset(a->vd, a->t)); return true; } @@ -2700,8 +2705,8 @@ static bool trans_VRINTR_hp(DisasContext *s, arg_VRINTR_sp *a) } tmp = tcg_temp_new_i32(); - vfp_load_reg32(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR_F16); + vfp_load_reg16(tmp, a->vm); + fpst = fpstatus_ptr(FPST_A32_F16); gen_helper_rinth(tmp, tmp, fpst); vfp_store_reg32(tmp, a->vd); return true; @@ -2722,7 +2727,7 @@ static bool trans_VRINTR_sp(DisasContext *s, arg_VRINTR_sp *a) tmp = tcg_temp_new_i32(); vfp_load_reg32(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); gen_helper_rints(tmp, tmp, fpst); vfp_store_reg32(tmp, a->vd); return true; @@ -2752,7 +2757,7 @@ static bool trans_VRINTR_dp(DisasContext *s, arg_VRINTR_dp *a) tmp = tcg_temp_new_i64(); vfp_load_reg64(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); gen_helper_rintd(tmp, tmp, fpst); vfp_store_reg64(tmp, a->vd); return true; @@ -2773,8 +2778,8 @@ static bool trans_VRINTZ_hp(DisasContext *s, arg_VRINTZ_sp *a) } tmp = tcg_temp_new_i32(); - vfp_load_reg32(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR_F16); + vfp_load_reg16(tmp, a->vm); + fpst = fpstatus_ptr(FPST_A32_F16); tcg_rmode = gen_set_rmode(FPROUNDING_ZERO, fpst); gen_helper_rinth(tmp, tmp, fpst); gen_restore_rmode(tcg_rmode, fpst); @@ -2798,7 +2803,7 @@ 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); + fpst = fpstatus_ptr(FPST_A32); tcg_rmode = gen_set_rmode(FPROUNDING_ZERO, fpst); gen_helper_rints(tmp, tmp, fpst); gen_restore_rmode(tcg_rmode, fpst); @@ -2831,7 +2836,7 @@ 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); + fpst = fpstatus_ptr(FPST_A32); tcg_rmode = gen_set_rmode(FPROUNDING_ZERO, fpst); gen_helper_rintd(tmp, tmp, fpst); gen_restore_rmode(tcg_rmode, fpst); @@ -2853,8 +2858,8 @@ static bool trans_VRINTX_hp(DisasContext *s, arg_VRINTX_sp *a) } tmp = tcg_temp_new_i32(); - vfp_load_reg32(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR_F16); + vfp_load_reg16(tmp, a->vm); + fpst = fpstatus_ptr(FPST_A32_F16); gen_helper_rinth_exact(tmp, tmp, fpst); vfp_store_reg32(tmp, a->vd); return true; @@ -2875,7 +2880,7 @@ static bool trans_VRINTX_sp(DisasContext *s, arg_VRINTX_sp *a) tmp = tcg_temp_new_i32(); vfp_load_reg32(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); gen_helper_rints_exact(tmp, tmp, fpst); vfp_store_reg32(tmp, a->vd); return true; @@ -2905,7 +2910,7 @@ static bool trans_VRINTX_dp(DisasContext *s, arg_VRINTX_dp *a) tmp = tcg_temp_new_i64(); vfp_load_reg64(tmp, a->vm); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); gen_helper_rintd_exact(tmp, tmp, fpst); vfp_store_reg64(tmp, a->vd); return true; @@ -2932,7 +2937,7 @@ 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, fpstatus_ptr(FPST_A32)); vfp_store_reg64(vd, a->vd); return true; } @@ -2958,7 +2963,7 @@ 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, fpstatus_ptr(FPST_A32)); vfp_store_reg32(vd, a->vd); return true; } @@ -2978,7 +2983,7 @@ static bool trans_VCVT_int_hp(DisasContext *s, arg_VCVT_int_sp *a) vm = tcg_temp_new_i32(); vfp_load_reg32(vm, a->vm); - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_F16); if (a->s) { /* i32 -> f16 */ gen_helper_vfp_sitoh(vm, vm, fpst); @@ -3005,7 +3010,7 @@ static bool trans_VCVT_int_sp(DisasContext *s, arg_VCVT_int_sp *a) vm = tcg_temp_new_i32(); vfp_load_reg32(vm, a->vm); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); if (a->s) { /* i32 -> f32 */ gen_helper_vfp_sitos(vm, vm, fpst); @@ -3039,7 +3044,7 @@ static bool trans_VCVT_int_dp(DisasContext *s, arg_VCVT_int_dp *a) vm = tcg_temp_new_i32(); vd = tcg_temp_new_i64(); vfp_load_reg32(vm, a->vm); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); if (a->s) { /* i32 -> f64 */ gen_helper_vfp_sitod(vd, vm, fpst); @@ -3076,7 +3081,7 @@ 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); return true; } @@ -3100,7 +3105,7 @@ static bool trans_VCVT_fix_hp(DisasContext *s, arg_VCVT_fix_sp *a) vd = tcg_temp_new_i32(); vfp_load_reg32(vd, a->vd); - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_F16); shift = tcg_constant_i32(frac_bits); /* Switch on op:U:sx bits */ @@ -3156,7 +3161,7 @@ static bool trans_VCVT_fix_sp(DisasContext *s, arg_VCVT_fix_sp *a) vd = tcg_temp_new_i32(); vfp_load_reg32(vd, a->vd); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); shift = tcg_constant_i32(frac_bits); /* Switch on op:U:sx bits */ @@ -3218,7 +3223,7 @@ static bool trans_VCVT_fix_dp(DisasContext *s, arg_VCVT_fix_dp *a) vd = tcg_temp_new_i64(); vfp_load_reg64(vd, a->vd); - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); shift = tcg_constant_i32(frac_bits); /* Switch on op:U:sx bits */ @@ -3268,9 +3273,9 @@ static bool trans_VCVT_hp_int(DisasContext *s, arg_VCVT_sp_int *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR_F16); + fpst = fpstatus_ptr(FPST_A32_F16); vm = tcg_temp_new_i32(); - vfp_load_reg32(vm, a->vm); + vfp_load_reg16(vm, a->vm); if (a->s) { if (a->rz) { @@ -3302,7 +3307,7 @@ static bool trans_VCVT_sp_int(DisasContext *s, arg_VCVT_sp_int *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); vm = tcg_temp_new_i32(); vfp_load_reg32(vm, a->vm); @@ -3342,7 +3347,7 @@ static bool trans_VCVT_dp_int(DisasContext *s, arg_VCVT_dp_int *a) return true; } - fpst = fpstatus_ptr(FPST_FPCR); + fpst = fpstatus_ptr(FPST_A32); vm = tcg_temp_new_i64(); vd = tcg_temp_new_i32(); vfp_load_reg64(vm, a->vm); @@ -3383,8 +3388,8 @@ 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); return true; diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 976b7042008..9962f43b1d0 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -23,11 +23,11 @@ #include "translate.h" #include "translate-a32.h" #include "qemu/log.h" -#include "disas/disas.h" #include "arm_ldst.h" #include "semihosting/semihost.h" #include "cpregs.h" #include "exec/helper-proto.h" +#include "exec/target_page.h" #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" @@ -63,18 +63,18 @@ void arm_translate_init(void) int i; for (i = 0; i < 16; i++) { - cpu_R[i] = tcg_global_mem_new_i32(cpu_env, + cpu_R[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUARMState, regs[i]), regnames[i]); } - cpu_CF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, CF), "CF"); - cpu_NF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, NF), "NF"); - cpu_VF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, VF), "VF"); - cpu_ZF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, ZF), "ZF"); + cpu_CF = tcg_global_mem_new_i32(tcg_env, offsetof(CPUARMState, CF), "CF"); + cpu_NF = tcg_global_mem_new_i32(tcg_env, offsetof(CPUARMState, NF), "NF"); + cpu_VF = tcg_global_mem_new_i32(tcg_env, offsetof(CPUARMState, VF), "VF"); + cpu_ZF = tcg_global_mem_new_i32(tcg_env, offsetof(CPUARMState, ZF), "ZF"); - cpu_exclusive_addr = tcg_global_mem_new_i64(cpu_env, + cpu_exclusive_addr = tcg_global_mem_new_i64(tcg_env, offsetof(CPUARMState, exclusive_addr), "exclusive_addr"); - cpu_exclusive_val = tcg_global_mem_new_i64(cpu_env, + cpu_exclusive_val = tcg_global_mem_new_i64(tcg_env, offsetof(CPUARMState, exclusive_val), "exclusive_val"); a64_translate_init(); @@ -179,10 +179,10 @@ void store_cpu_offset(TCGv_i32 var, int offset, int size) { switch (size) { case 1: - tcg_gen_st8_i32(var, cpu_env, offset); + tcg_gen_st8_i32(var, tcg_env, offset); break; case 4: - tcg_gen_st_i32(var, cpu_env, offset); + tcg_gen_st_i32(var, tcg_env, offset); break; default: g_assert_not_reached(); @@ -229,6 +229,9 @@ static inline int get_a32_user_mem_index(DisasContext *s) */ 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: @@ -329,7 +332,7 @@ static void store_sp_checked(DisasContext *s, TCGv_i32 var) { #ifndef CONFIG_USER_ONLY if (s->v8m_stackcheck) { - gen_helper_v8m_stackcheck(cpu_env, var); + gen_helper_v8m_stackcheck(tcg_env, var); } #endif store_reg(s, 13, var); @@ -346,7 +349,7 @@ static void store_sp_checked(DisasContext *s, TCGv_i32 var) void gen_set_cpsr(TCGv_i32 var, uint32_t mask) { - gen_helper_cpsr_write(cpu_env, var, tcg_constant_i32(mask)); + gen_helper_cpsr_write(tcg_env, var, tcg_constant_i32(mask)); } static void gen_rebuild_hflags(DisasContext *s, bool new_el) @@ -355,16 +358,16 @@ static void gen_rebuild_hflags(DisasContext *s, bool new_el) if (new_el) { if (m_profile) { - gen_helper_rebuild_hflags_m32_newel(cpu_env); + gen_helper_rebuild_hflags_m32_newel(tcg_env); } else { - gen_helper_rebuild_hflags_a32_newel(cpu_env); + gen_helper_rebuild_hflags_a32_newel(tcg_env); } } else { TCGv_i32 tcg_el = tcg_constant_i32(s->current_el); if (m_profile) { - gen_helper_rebuild_hflags_m32(cpu_env, tcg_el); + gen_helper_rebuild_hflags_m32(tcg_env, tcg_el); } else { - gen_helper_rebuild_hflags_a32(cpu_env, tcg_el); + gen_helper_rebuild_hflags_a32(tcg_env, tcg_el); } } } @@ -372,7 +375,7 @@ static void gen_rebuild_hflags(DisasContext *s, bool new_el) static void gen_exception_internal(int excp) { assert(excp_is_internal(excp)); - gen_helper_exception_internal(cpu_env, tcg_constant_i32(excp)); + gen_helper_exception_internal(tcg_env, tcg_constant_i32(excp)); } static void gen_singlestep_exception(DisasContext *s) @@ -491,20 +494,9 @@ static void gen_add_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) 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_addcio_i32(cpu_NF, cpu_CF, t0, t1, cpu_CF); + tcg_gen_mov_i32(cpu_ZF, cpu_NF); tcg_gen_xor_i32(cpu_VF, cpu_NF, t0); tcg_gen_xor_i32(tmp, t0, t1); @@ -617,10 +609,10 @@ static inline void gen_arm_shift_reg(TCGv_i32 var, int shiftop, { if (flags) { switch (shiftop) { - case 0: gen_helper_shl_cc(var, cpu_env, var, shift); break; - case 1: gen_helper_shr_cc(var, cpu_env, var, shift); break; - case 2: gen_helper_sar_cc(var, cpu_env, var, shift); break; - case 3: gen_helper_ror_cc(var, cpu_env, var, shift); break; + case 0: gen_helper_shl_cc(var, tcg_env, var, shift); break; + case 1: gen_helper_shr_cc(var, tcg_env, var, shift); break; + case 2: gen_helper_sar_cc(var, tcg_env, var, shift); break; + case 3: gen_helper_ror_cc(var, tcg_env, var, shift); break; } } else { switch (shiftop) { @@ -849,7 +841,7 @@ static inline void gen_bxns(DisasContext *s, int rm) * is correct in the non-UNPREDICTABLE cases, and we can choose * "zeroes the IT bits" as our UNPREDICTABLE behaviour otherwise. */ - gen_helper_v7m_bxns(cpu_env, var); + gen_helper_v7m_bxns(tcg_env, var); s->base.is_jmp = DISAS_EXIT; } @@ -862,7 +854,7 @@ static inline void gen_blxns(DisasContext *s, int rm) * The blxns helper may throw an exception. */ gen_update_pc(s, curr_insn_len(s)); - gen_helper_v7m_blxns(cpu_env, var); + gen_helper_v7m_blxns(tcg_env, var); s->base.is_jmp = DISAS_EXIT; } @@ -900,13 +892,7 @@ static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) MemOp pow2_align(unsigned i) { static const MemOp mop_align[] = { - 0, MO_ALIGN_2, MO_ALIGN_4, MO_ALIGN_8, MO_ALIGN_16, - /* - * FIXME: TARGET_PAGE_BITS_MIN affects TLB_FLAGS_MASK such - * that 256-bit alignment (MO_ALIGN_32) cannot be supported: - * see get_alignment_bits(). Enforce only 128-bit alignment for now. - */ - MO_ALIGN_16 + 0, MO_ALIGN_2, MO_ALIGN_4, MO_ALIGN_8, MO_ALIGN_16, MO_ALIGN_32 }; g_assert(i < ARRAY_SIZE(mop_align)); return mop_align[i]; @@ -1024,7 +1010,7 @@ static inline void gen_hvc(DisasContext *s, int imm16) * the insn really executes). */ gen_update_pc(s, 0); - gen_helper_pre_hvc(cpu_env); + 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 @@ -1041,7 +1027,7 @@ static inline void gen_smc(DisasContext *s) * the insn executes. */ gen_update_pc(s, 0); - gen_helper_pre_smc(cpu_env, tcg_constant_i32(syn_aa32_smc())); + 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; } @@ -1056,7 +1042,7 @@ static void gen_exception_internal_insn(DisasContext *s, int excp) 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), + gen_helper_exception_with_syndrome_el(tcg_env, tcg_constant_i32(excp), tcg_constant_i32(syndrome), tcg_el); } @@ -1067,7 +1053,7 @@ static void gen_exception_el(int excp, uint32_t syndrome, uint32_t target_el) static void gen_exception(int excp, uint32_t syndrome) { - gen_helper_exception_with_syndrome(cpu_env, tcg_constant_i32(excp), + gen_helper_exception_with_syndrome(tcg_env, tcg_constant_i32(excp), tcg_constant_i32(syndrome)); } @@ -1108,7 +1094,7 @@ 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)); + gen_helper_exception_bkpt_insn(tcg_env, tcg_constant_i32(syn)); s->base.is_jmp = DISAS_NORETURN; } @@ -1192,20 +1178,20 @@ void read_neon_element32(TCGv_i32 dest, int reg, int ele, MemOp memop) switch (memop) { case MO_SB: - tcg_gen_ld8s_i32(dest, cpu_env, off); + tcg_gen_ld8s_i32(dest, tcg_env, off); break; case MO_UB: - tcg_gen_ld8u_i32(dest, cpu_env, off); + tcg_gen_ld8u_i32(dest, tcg_env, off); break; case MO_SW: - tcg_gen_ld16s_i32(dest, cpu_env, off); + tcg_gen_ld16s_i32(dest, tcg_env, off); break; case MO_UW: - tcg_gen_ld16u_i32(dest, cpu_env, off); + tcg_gen_ld16u_i32(dest, tcg_env, off); break; case MO_UL: case MO_SL: - tcg_gen_ld_i32(dest, cpu_env, off); + tcg_gen_ld_i32(dest, tcg_env, off); break; default: g_assert_not_reached(); @@ -1218,13 +1204,13 @@ void read_neon_element64(TCGv_i64 dest, int reg, int ele, MemOp memop) switch (memop) { case MO_SL: - tcg_gen_ld32s_i64(dest, cpu_env, off); + tcg_gen_ld32s_i64(dest, tcg_env, off); break; case MO_UL: - tcg_gen_ld32u_i64(dest, cpu_env, off); + tcg_gen_ld32u_i64(dest, tcg_env, off); break; case MO_UQ: - tcg_gen_ld_i64(dest, cpu_env, off); + tcg_gen_ld_i64(dest, tcg_env, off); break; default: g_assert_not_reached(); @@ -1237,13 +1223,13 @@ void write_neon_element32(TCGv_i32 src, int reg, int ele, MemOp memop) switch (memop) { case MO_8: - tcg_gen_st8_i32(src, cpu_env, off); + tcg_gen_st8_i32(src, tcg_env, off); break; case MO_16: - tcg_gen_st16_i32(src, cpu_env, off); + tcg_gen_st16_i32(src, tcg_env, off); break; case MO_32: - tcg_gen_st_i32(src, cpu_env, off); + tcg_gen_st_i32(src, tcg_env, off); break; default: g_assert_not_reached(); @@ -1256,10 +1242,10 @@ void write_neon_element64(TCGv_i64 src, int reg, int ele, MemOp memop) switch (memop) { case MO_32: - tcg_gen_st32_i64(src, cpu_env, off); + tcg_gen_st32_i64(src, tcg_env, off); break; case MO_64: - tcg_gen_st_i64(src, cpu_env, off); + tcg_gen_st_i64(src, tcg_env, off); break; default: g_assert_not_reached(); @@ -1270,24 +1256,24 @@ void write_neon_element64(TCGv_i64 src, int reg, int ele, MemOp memop) static inline void iwmmxt_load_reg(TCGv_i64 var, int reg) { - tcg_gen_ld_i64(var, cpu_env, offsetof(CPUARMState, iwmmxt.regs[reg])); + tcg_gen_ld_i64(var, tcg_env, offsetof(CPUARMState, iwmmxt.regs[reg])); } static inline void iwmmxt_store_reg(TCGv_i64 var, int reg) { - tcg_gen_st_i64(var, cpu_env, offsetof(CPUARMState, iwmmxt.regs[reg])); + tcg_gen_st_i64(var, tcg_env, offsetof(CPUARMState, iwmmxt.regs[reg])); } static inline TCGv_i32 iwmmxt_load_creg(int reg) { TCGv_i32 var = tcg_temp_new_i32(); - tcg_gen_ld_i32(var, cpu_env, offsetof(CPUARMState, iwmmxt.cregs[reg])); + tcg_gen_ld_i32(var, tcg_env, offsetof(CPUARMState, iwmmxt.cregs[reg])); return var; } static inline void iwmmxt_store_creg(int reg, TCGv_i32 var) { - tcg_gen_st_i32(var, cpu_env, offsetof(CPUARMState, iwmmxt.cregs[reg])); + tcg_gen_st_i32(var, tcg_env, offsetof(CPUARMState, iwmmxt.cregs[reg])); } static inline void gen_op_iwmmxt_movq_wRn_M0(int rn) @@ -1329,7 +1315,7 @@ static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \ static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \ { \ iwmmxt_load_reg(cpu_V1, rn); \ - gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0, cpu_V1); \ + gen_helper_iwmmxt_##name(cpu_M0, tcg_env, cpu_M0, cpu_V1); \ } #define IWMMXT_OP_ENV_SIZE(name) \ @@ -1340,7 +1326,7 @@ IWMMXT_OP_ENV(name##l) #define IWMMXT_OP_ENV1(name) \ static inline void gen_op_iwmmxt_##name##_M0(void) \ { \ - gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0); \ + gen_helper_iwmmxt_##name(cpu_M0, tcg_env, cpu_M0); \ } IWMMXT_OP(maddsq) @@ -2113,13 +2099,13 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) } switch ((insn >> 22) & 3) { case 1: - gen_helper_iwmmxt_srlw(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_srlw(cpu_M0, tcg_env, cpu_M0, tmp); break; case 2: - gen_helper_iwmmxt_srll(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_srll(cpu_M0, tcg_env, cpu_M0, tmp); break; case 3: - gen_helper_iwmmxt_srlq(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_srlq(cpu_M0, tcg_env, cpu_M0, tmp); break; } gen_op_iwmmxt_movq_wRn_M0(wrd); @@ -2139,13 +2125,13 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) } switch ((insn >> 22) & 3) { case 1: - gen_helper_iwmmxt_sraw(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_sraw(cpu_M0, tcg_env, cpu_M0, tmp); break; case 2: - gen_helper_iwmmxt_sral(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_sral(cpu_M0, tcg_env, cpu_M0, tmp); break; case 3: - gen_helper_iwmmxt_sraq(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_sraq(cpu_M0, tcg_env, cpu_M0, tmp); break; } gen_op_iwmmxt_movq_wRn_M0(wrd); @@ -2165,13 +2151,13 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) } switch ((insn >> 22) & 3) { case 1: - gen_helper_iwmmxt_sllw(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_sllw(cpu_M0, tcg_env, cpu_M0, tmp); break; case 2: - gen_helper_iwmmxt_slll(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_slll(cpu_M0, tcg_env, cpu_M0, tmp); break; case 3: - gen_helper_iwmmxt_sllq(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_sllq(cpu_M0, tcg_env, cpu_M0, tmp); break; } gen_op_iwmmxt_movq_wRn_M0(wrd); @@ -2191,19 +2177,19 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) if (gen_iwmmxt_shift(insn, 0xf, tmp)) { return 1; } - gen_helper_iwmmxt_rorw(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_rorw(cpu_M0, tcg_env, cpu_M0, tmp); break; case 2: if (gen_iwmmxt_shift(insn, 0x1f, tmp)) { return 1; } - gen_helper_iwmmxt_rorl(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_rorl(cpu_M0, tcg_env, cpu_M0, tmp); break; case 3: if (gen_iwmmxt_shift(insn, 0x3f, tmp)) { return 1; } - gen_helper_iwmmxt_rorq(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_rorq(cpu_M0, tcg_env, cpu_M0, tmp); break; } gen_op_iwmmxt_movq_wRn_M0(wrd); @@ -2335,7 +2321,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) rd0 = (insn >> 16) & 0xf; gen_op_iwmmxt_movq_M0_wRn(rd0); tmp = tcg_constant_i32(((insn >> 16) & 0xf0) | (insn & 0x0f)); - gen_helper_iwmmxt_shufh(cpu_M0, cpu_env, cpu_M0, tmp); + gen_helper_iwmmxt_shufh(cpu_M0, tcg_env, cpu_M0, tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); @@ -2790,1752 +2776,133 @@ static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn, } } - /* 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: - /* - * 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)); - 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); -} - -/* 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(cpu_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 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); -} - -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_negsetcond_i32(COND, d, a, tcg_constant_i32(0)); \ - } \ - static void gen_##NAME##0_i64(TCGv_i64 d, TCGv_i64 a) \ - { \ - tcg_gen_negsetcond_i64(COND, d, a, tcg_constant_i64(0)); \ - } \ - 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_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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); - 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); -} - -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_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); -} - -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_negsetcond_i32(TCG_COND_NE, d, d, tcg_constant_i32(0)); -} - -void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - tcg_gen_and_i64(d, a, b); - tcg_gen_negsetcond_i64(TCG_COND_NE, d, d, tcg_constant_i64(0)); -} - -static void gen_cmtst_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) -{ - 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); -} - -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 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); - } - - /* - * 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); -} - -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); -} - -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 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. + /* Catch the 'accessing inaccessible register' cases we can detect + * at translate time. */ - 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); + 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; - /* Select in-bound left shift. */ - tcg_gen_andc_vec(vece, lval, lval, tmp); + 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); + } - /* 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); + 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; } -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); -} - -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); + return true; - tcg_gen_umin_vec(vece, t, a, b); - tcg_gen_umax_vec(vece, d, a, b); - tcg_gen_sub_vec(vece, d, d, t); +undef: + /* If we get here then some access check did not pass */ + gen_exception_insn(s, 0, EXCP_UDEF, syn_uncategorized()); + return false; } -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 void gen_msr_banked(DisasContext *s, int r, int sysm, int rn) { - 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]); -} + TCGv_i32 tcg_reg; + int tgtmode = 0, regno = 0; -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); -} + if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) { + return; + } -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); + /* 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_saba_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +static void gen_mrs_banked(DisasContext *s, int r, int sysm, int rn) { - TCGv_vec t = tcg_temp_new_vec_matching(d); - gen_sabd_vec(vece, t, a, b); - tcg_gen_add_vec(vece, d, d, t); -} + TCGv_i32 tcg_reg; + int tgtmode = 0, regno = 0; -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]); -} + if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) { + return; + } -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); + /* 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; } -static void gen_uaba_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +/* 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) { - TCGv_i64 t = tcg_temp_new_i64(); - gen_uabd_i64(t, a, b); - tcg_gen_add_i64(d, d, t); + tcg_gen_mov_i32(cpu_R[15], pc); } -static void gen_uaba_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +/* Generate a v6 exception return. Marks both values as dead. */ +static void gen_rfe(DisasContext *s, TCGv_i32 pc, TCGv_i32 cpsr) { - TCGv_vec t = tcg_temp_new_vec_matching(d); - gen_uabd_vec(vece, t, a, b); - tcg_gen_add_vec(vece, d, d, t); + 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; } -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) +/* Generate an old-style exception return. Marks pc as dead. */ +static void gen_exception_return(DisasContext *s, TCGv_i32 pc) { - 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]); + gen_rfe(s, pc, load_cpu_field(spsr)); } static bool aa32_cpreg_encoding_in_impdef_space(uint8_t crn, uint8_t crm) @@ -4622,7 +2989,7 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, tcg_gen_andi_i32(t, t, 1u << maskbit); tcg_gen_brcondi_i32(TCG_COND_EQ, t, 0, over.label); - gen_exception_insn(s, 0, EXCP_UDEF, syndrome); + 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 @@ -4643,11 +3010,11 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, case 0: if (arm_dc_feature(s, ARM_FEATURE_AARCH64) && dc_isar_feature(aa64_tidcp1, s)) { - gen_helper_tidcp_el0(cpu_env, tcg_constant_i32(syndrome)); + gen_helper_tidcp_el0(tcg_env, tcg_constant_i32(syndrome)); } break; case 1: - gen_helper_tidcp_el1(cpu_env, tcg_constant_i32(syndrome)); + gen_helper_tidcp_el1(tcg_env, tcg_constant_i32(syndrome)); break; } } @@ -4692,7 +3059,7 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, gen_set_condexec(s); gen_update_pc(s, 0); tcg_ri = tcg_temp_new_ptr(); - gen_helper_access_check_cp_reg(tcg_ri, cpu_env, + gen_helper_access_check_cp_reg(tcg_ri, tcg_env, tcg_constant_i32(key), tcg_constant_i32(syndrome), tcg_constant_i32(isread)); @@ -4740,10 +3107,10 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, tcg_ri = gen_lookup_cp_reg(key); } tmp64 = tcg_temp_new_i64(); - gen_helper_get_cp_reg64(tmp64, cpu_env, tcg_ri); + gen_helper_get_cp_reg64(tmp64, tcg_env, tcg_ri); } else { tmp64 = tcg_temp_new_i64(); - tcg_gen_ld_i64(tmp64, cpu_env, ri->fieldoffset); + tcg_gen_ld_i64(tmp64, tcg_env, ri->fieldoffset); } tmp = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(tmp, tmp64); @@ -4760,7 +3127,7 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, tcg_ri = gen_lookup_cp_reg(key); } tmp = tcg_temp_new_i32(); - gen_helper_get_cp_reg(tmp, cpu_env, tcg_ri); + gen_helper_get_cp_reg(tmp, tcg_env, tcg_ri); } else { tmp = load_cpu_offset(ri->fieldoffset); } @@ -4790,9 +3157,9 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, if (!tcg_ri) { tcg_ri = gen_lookup_cp_reg(key); } - gen_helper_set_cp_reg64(cpu_env, tcg_ri, tmp64); + gen_helper_set_cp_reg64(tcg_env, tcg_ri, tmp64); } else { - tcg_gen_st_i64(tmp64, cpu_env, ri->fieldoffset); + tcg_gen_st_i64(tmp64, tcg_env, ri->fieldoffset); } } else { TCGv_i32 tmp = load_reg(s, rt); @@ -4800,7 +3167,7 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, if (!tcg_ri) { tcg_ri = gen_lookup_cp_reg(key); } - gen_helper_set_cp_reg(cpu_env, tcg_ri, tmp); + gen_helper_set_cp_reg(tcg_env, tcg_ri, tmp); } else { store_cpu_offset(tmp, ri->fieldoffset, 4); } @@ -5066,7 +3433,7 @@ static void gen_srs(DisasContext *s, /* 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)); + gen_helper_get_r13_banked(addr, tcg_env, tcg_constant_i32(mode)); switch (amode) { case 0: /* DA */ offset = -4; @@ -5107,7 +3474,7 @@ static void gen_srs(DisasContext *s, g_assert_not_reached(); } tcg_gen_addi_i32(addr, addr, offset); - gen_helper_set_r13_banked(cpu_env, tcg_constant_i32(mode), addr); + gen_helper_set_r13_banked(tcg_env, tcg_constant_i32(mode), addr); } s->base.is_jmp = DISAS_UPDATE_EXIT; } @@ -5133,7 +3500,7 @@ static int t32_expandimm_rot(DisasContext *s, int x) /* Return the unrotated immediate from T32ExpandImm. */ static int t32_expandimm_imm(DisasContext *s, int x) { - int imm = extract32(x, 0, 8); + uint32_t imm = extract32(x, 0, 8); switch (extract32(x, 8, 4)) { case 0: /* XY */ @@ -5656,7 +4023,7 @@ static bool trans_LSRL_ri(DisasContext *s, arg_mve_shl_ri *a) static void gen_mve_sqshll(TCGv_i64 r, TCGv_i64 n, int64_t shift) { - gen_helper_mve_sqshll(r, cpu_env, n, tcg_constant_i32(shift)); + gen_helper_mve_sqshll(r, tcg_env, n, tcg_constant_i32(shift)); } static bool trans_SQSHLL_ri(DisasContext *s, arg_mve_shl_ri *a) @@ -5666,7 +4033,7 @@ static bool trans_SQSHLL_ri(DisasContext *s, arg_mve_shl_ri *a) static void gen_mve_uqshll(TCGv_i64 r, TCGv_i64 n, int64_t shift) { - gen_helper_mve_uqshll(r, cpu_env, n, tcg_constant_i32(shift)); + gen_helper_mve_uqshll(r, tcg_env, n, tcg_constant_i32(shift)); } static bool trans_UQSHLL_ri(DisasContext *s, arg_mve_shl_ri *a) @@ -5712,7 +4079,7 @@ static bool do_mve_shl_rr(DisasContext *s, arg_mve_shl_rr *a, WideShiftFn *fn) tcg_gen_concat_i32_i64(rda, rdalo, rdahi); /* The helper takes care of the sign-extension of the low 8 bits of Rm */ - fn(rda, cpu_env, rda, cpu_R[a->rm]); + fn(rda, tcg_env, rda, cpu_R[a->rm]); tcg_gen_extrl_i64_i32(rdalo, rda); tcg_gen_extrh_i64_i32(rdahi, rda); @@ -5786,7 +4153,7 @@ static bool trans_SRSHR_ri(DisasContext *s, arg_mve_sh_ri *a) static void gen_mve_sqshl(TCGv_i32 r, TCGv_i32 n, int32_t shift) { - gen_helper_mve_sqshl(r, cpu_env, n, tcg_constant_i32(shift)); + gen_helper_mve_sqshl(r, tcg_env, n, tcg_constant_i32(shift)); } static bool trans_SQSHL_ri(DisasContext *s, arg_mve_sh_ri *a) @@ -5796,7 +4163,7 @@ static bool trans_SQSHL_ri(DisasContext *s, arg_mve_sh_ri *a) static void gen_mve_uqshl(TCGv_i32 r, TCGv_i32 n, int32_t shift) { - gen_helper_mve_uqshl(r, cpu_env, n, tcg_constant_i32(shift)); + gen_helper_mve_uqshl(r, tcg_env, n, tcg_constant_i32(shift)); } static bool trans_UQSHL_ri(DisasContext *s, arg_mve_sh_ri *a) @@ -5820,7 +4187,7 @@ static bool do_mve_sh_rr(DisasContext *s, arg_mve_sh_rr *a, ShiftFn *fn) } /* The helper takes care of the sign-extension of the low 8 bits of Rm */ - fn(cpu_R[a->rda], cpu_env, cpu_R[a->rda], cpu_R[a->rm]); + fn(cpu_R[a->rda], tcg_env, cpu_R[a->rda], cpu_R[a->rm]); return true; } @@ -5966,12 +4333,12 @@ static bool op_qaddsub(DisasContext *s, arg_rrr *a, bool add, bool doub) t0 = load_reg(s, a->rm); t1 = load_reg(s, a->rn); if (doub) { - gen_helper_add_saturate(t1, cpu_env, t1, t1); + gen_helper_add_saturate(t1, tcg_env, t1, t1); } if (add) { - gen_helper_add_saturate(t0, cpu_env, t0, t1); + gen_helper_add_saturate(t0, tcg_env, t0, t1); } else { - gen_helper_sub_saturate(t0, cpu_env, t0, t1); + gen_helper_sub_saturate(t0, tcg_env, t0, t1); } store_reg(s, a->rd, t0); return true; @@ -6015,7 +4382,7 @@ static bool op_smlaxxx(DisasContext *s, arg_rrrr *a, break; case 1: t1 = load_reg(s, a->ra); - gen_helper_add_setq(t0, cpu_env, t0, t1); + gen_helper_add_setq(t0, tcg_env, t0, t1); store_reg(s, a->rd, t0); break; case 2: @@ -6079,7 +4446,7 @@ static bool op_smlawx(DisasContext *s, arg_rrrr *a, bool add, bool mt) tcg_gen_muls2_i32(t0, t1, t0, t1); if (add) { t0 = load_reg(s, a->ra); - gen_helper_add_setq(t1, cpu_env, t1, t0); + gen_helper_add_setq(t1, tcg_env, t1, t0); } store_reg(s, a->rd, t1); return true; @@ -6158,7 +4525,7 @@ static bool trans_ESB(DisasContext *s, arg_ESB *a) * Test for EL2 present, and defer test for SEL2 to runtime. */ if (s->current_el <= 1 && arm_dc_feature(s, ARM_FEATURE_EL2)) { - gen_helper_vesb(cpu_env); + gen_helper_vesb(tcg_env); } } return true; @@ -6266,7 +4633,7 @@ static bool trans_MRS_reg(DisasContext *s, arg_MRS_reg *a) tmp = load_cpu_field(spsr); } else { tmp = tcg_temp_new_i32(); - gen_helper_cpsr_read(tmp, cpu_env); + gen_helper_cpsr_read(tmp, tcg_env); } store_reg(s, a->rd, tmp); return true; @@ -6295,7 +4662,7 @@ static bool trans_MRS_v7m(DisasContext *s, arg_MRS_v7m *a) return false; } tmp = tcg_temp_new_i32(); - gen_helper_v7m_mrs(tmp, cpu_env, tcg_constant_i32(a->sysm)); + gen_helper_v7m_mrs(tmp, tcg_env, tcg_constant_i32(a->sysm)); store_reg(s, a->rd, tmp); return true; } @@ -6309,7 +4676,7 @@ static bool trans_MSR_v7m(DisasContext *s, arg_MSR_v7m *a) } addr = tcg_constant_i32((a->mask << 10) | a->sysm); reg = load_reg(s, a->rn); - gen_helper_v7m_msr(cpu_env, addr, reg); + 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); @@ -6340,7 +4707,7 @@ static bool trans_BXJ(DisasContext *s, arg_BXJ *a) if (!arm_dc_feature(s, ARM_FEATURE_V8) && arm_dc_feature(s, ARM_FEATURE_EL2) && s->current_el < 2 && s->ns) { - gen_helper_check_bxj_trap(cpu_env, tcg_constant_i32(a->rm)); + gen_helper_check_bxj_trap(tcg_env, tcg_constant_i32(a->rm)); } /* Trivial implementation equivalent to bx. */ gen_bx(s, load_reg(s, a->rm)); @@ -6518,7 +4885,7 @@ static bool trans_TT(DisasContext *s, arg_TT *a) addr = load_reg(s, a->rn); tmp = tcg_temp_new_i32(); - gen_helper_v7m_tt(tmp, cpu_env, addr, tcg_constant_i32((a->A << 1) | a->T)); + gen_helper_v7m_tt(tmp, tcg_env, addr, tcg_constant_i32((a->A << 1) | a->T)); store_reg(s, a->rd, tmp); return true; } @@ -6548,7 +4915,7 @@ static TCGv_i32 op_addr_rr_pre(DisasContext *s, arg_ldst_rr *a) TCGv_i32 addr = load_reg(s, a->rn); if (s->v8m_stackcheck && a->rn == 13 && a->w) { - gen_helper_v8m_stackcheck(cpu_env, addr); + gen_helper_v8m_stackcheck(tcg_env, addr); } if (a->p) { @@ -6564,7 +4931,7 @@ static TCGv_i32 op_addr_rr_pre(DisasContext *s, arg_ldst_rr *a) } static void op_addr_rr_post(DisasContext *s, arg_ldst_rr *a, - TCGv_i32 addr, int address_offset) + TCGv_i32 addr) { if (!a->p) { TCGv_i32 ofs = load_reg(s, a->rm); @@ -6577,7 +4944,6 @@ static void op_addr_rr_post(DisasContext *s, arg_ldst_rr *a, } else if (!a->w) { return; } - tcg_gen_addi_i32(addr, addr, address_offset); store_reg(s, a->rn, addr); } @@ -6597,7 +4963,7 @@ static bool op_load_rr(DisasContext *s, arg_ldst_rr *a, * Perform base writeback before the loaded value to * ensure correct behavior with overlapping index registers. */ - op_addr_rr_post(s, a, addr, 0); + op_addr_rr_post(s, a, addr); store_reg_from_load(s, a->rt, tmp); return true; } @@ -6622,14 +4988,53 @@ static bool op_store_rr(DisasContext *s, arg_ldst_rr *a, 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); + op_addr_rr_post(s, a, addr); return true; } -static bool trans_LDRD_rr(DisasContext *s, arg_ldst_rr *a) +static void do_ldrd_load(DisasContext *s, TCGv_i32 addr, int rt, int rt2) { + /* + * LDRD is required to be an atomic 64-bit access if the + * address is 8-aligned, two atomic 32-bit accesses if + * it's only 4-aligned, and to give an alignment fault + * if it's not 4-aligned. This is MO_ALIGN_4 | MO_ATOM_SUBALIGN. + * Rt is always the word from the lower address, and Rt2 the + * data from the higher address, regardless of endianness. + * So (like gen_load_exclusive) we avoid gen_aa32_ld_i64() + * so we don't get its SCTLR_B check, and instead do a 64-bit access + * using MO_BE if appropriate and then split the two halves. + * + * For M-profile, and for A-profile before LPAE, the 64-bit + * atomicity is not required. We could model that using + * the looser MO_ATOM_IFALIGN_PAIR, but providing a higher + * level of atomicity than required is harmless (we would not + * currently generate better code for IFALIGN_PAIR here). + * + * This also gives us the correct behaviour of not updating + * rt if the load of rt2 faults; this is required for cases + * like "ldrd r2, r3, [r2]" where rt is also the base register. + */ int mem_idx = get_mem_index(s); - TCGv_i32 addr, tmp; + MemOp opc = MO_64 | MO_ALIGN_4 | MO_ATOM_SUBALIGN | s->be_data; + TCGv taddr = gen_aa32_addr(s, addr, opc); + TCGv_i64 t64 = tcg_temp_new_i64(); + TCGv_i32 tmp = tcg_temp_new_i32(); + TCGv_i32 tmp2 = tcg_temp_new_i32(); + + tcg_gen_qemu_ld_i64(t64, taddr, mem_idx, opc); + 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, rt, tmp); + store_reg(s, rt2, tmp2); +} + +static bool trans_LDRD_rr(DisasContext *s, arg_ldst_rr *a) +{ + TCGv_i32 addr; if (!ENABLE_ARCH_5TE) { return false; @@ -6640,25 +5045,49 @@ static bool trans_LDRD_rr(DisasContext *s, arg_ldst_rr *a) } 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); + do_ldrd_load(s, addr, a->rt, a->rt + 1); /* LDRD w/ base writeback is undefined if the registers overlap. */ - op_addr_rr_post(s, a, addr, -4); + op_addr_rr_post(s, a, addr); return true; } -static bool trans_STRD_rr(DisasContext *s, arg_ldst_rr *a) +static void do_strd_store(DisasContext *s, TCGv_i32 addr, int rt, int rt2) { + /* + * STRD is required to be an atomic 64-bit access if the + * address is 8-aligned, two atomic 32-bit accesses if + * it's only 4-aligned, and to give an alignment fault + * if it's not 4-aligned. + * Rt is always the word from the lower address, and Rt2 the + * data from the higher address, regardless of endianness. + * So (like gen_store_exclusive) we avoid gen_aa32_ld_i64() + * so we don't get its SCTLR_B check, and instead do a 64-bit access + * using MO_BE if appropriate, using a value constructed + * by putting the two halves together in the right order. + * + * As with LDRD, the 64-bit atomicity is not required for + * M-profile, or for A-profile before LPAE, and we provide + * the higher guarantee always for simplicity. + */ int mem_idx = get_mem_index(s); - TCGv_i32 addr, tmp; + MemOp opc = MO_64 | MO_ALIGN_4 | MO_ATOM_SUBALIGN | s->be_data; + TCGv taddr = gen_aa32_addr(s, addr, opc); + TCGv_i32 t1 = load_reg(s, rt); + TCGv_i32 t2 = load_reg(s, rt2); + TCGv_i64 t64 = tcg_temp_new_i64(); + + if (s->be_data == MO_BE) { + tcg_gen_concat_i32_i64(t64, t2, t1); + } else { + tcg_gen_concat_i32_i64(t64, t1, t2); + } + tcg_gen_qemu_st_i64(t64, taddr, mem_idx, opc); +} + +static bool trans_STRD_rr(DisasContext *s, arg_ldst_rr *a) +{ + TCGv_i32 addr; if (!ENABLE_ARCH_5TE) { return false; @@ -6669,15 +5098,9 @@ static bool trans_STRD_rr(DisasContext *s, arg_ldst_rr *a) } 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); + do_strd_store(s, addr, a->rt, a->rt + 1); - op_addr_rr_post(s, a, addr, -4); + op_addr_rr_post(s, a, addr); return true; } @@ -6703,9 +5126,9 @@ static TCGv_i32 op_addr_ri_pre(DisasContext *s, arg_ldst_ri *a) if (!a->u) { TCGv_i32 newsp = tcg_temp_new_i32(); tcg_gen_addi_i32(newsp, cpu_R[13], ofs); - gen_helper_v8m_stackcheck(cpu_env, newsp); + gen_helper_v8m_stackcheck(tcg_env, newsp); } else { - gen_helper_v8m_stackcheck(cpu_env, cpu_R[13]); + gen_helper_v8m_stackcheck(tcg_env, cpu_R[13]); } } @@ -6713,13 +5136,14 @@ static TCGv_i32 op_addr_ri_pre(DisasContext *s, arg_ldst_ri *a) } static void op_addr_ri_post(DisasContext *s, arg_ldst_ri *a, - TCGv_i32 addr, int address_offset) + TCGv_i32 addr) { + int address_offset = 0; if (!a->p) { if (a->u) { - address_offset += a->imm; + address_offset = a->imm; } else { - address_offset -= a->imm; + address_offset = -a->imm; } } else if (!a->w) { return; @@ -6744,7 +5168,7 @@ static bool op_load_ri(DisasContext *s, arg_ldst_ri *a, * Perform base writeback before the loaded value to * ensure correct behavior with overlapping index registers. */ - op_addr_ri_post(s, a, addr, 0); + op_addr_ri_post(s, a, addr); store_reg_from_load(s, a->rt, tmp); return true; } @@ -6769,29 +5193,20 @@ static bool op_store_ri(DisasContext *s, arg_ldst_ri *a, 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); + op_addr_ri_post(s, a, addr); 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; + TCGv_i32 addr; 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); + do_ldrd_load(s, addr, a->rt, rt2); /* LDRD w/ base writeback is undefined if the registers overlap. */ - op_addr_ri_post(s, a, addr, -4); + op_addr_ri_post(s, a, addr); return true; } @@ -6814,20 +5229,13 @@ static bool trans_LDRD_ri_t32(DisasContext *s, arg_ldst_ri2 *a) static bool op_strd_ri(DisasContext *s, arg_ldst_ri *a, int rt2) { - int mem_idx = get_mem_index(s); - TCGv_i32 addr, tmp; + TCGv_i32 addr; 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); + do_strd_store(s, addr, a->rt, rt2); - op_addr_ri_post(s, a, addr, -4); + op_addr_ri_post(s, a, addr); return true; } @@ -7357,7 +5765,7 @@ static bool op_par_addsub_ge(DisasContext *s, arg_rrr *a, t1 = load_reg(s, a->rm); ge = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(ge, cpu_env, offsetof(CPUARMState, GE)); + tcg_gen_addi_ptr(ge, tcg_env, offsetof(CPUARMState, GE)); gen(t0, t0, t1, ge); store_reg(s, a->rd, t0); @@ -7471,7 +5879,7 @@ static bool op_sat(DisasContext *s, arg_sat *a, tcg_gen_shli_i32(tmp, tmp, shift); } - gen(tmp, cpu_env, tmp, tcg_constant_i32(a->satimm)); + gen(tmp, tcg_env, tmp, tcg_constant_i32(a->satimm)); store_reg(s, a->rd, tmp); return true; @@ -7578,7 +5986,7 @@ static bool trans_SEL(DisasContext *s, arg_rrr *a) t1 = load_reg(s, a->rn); t2 = load_reg(s, a->rm); t3 = tcg_temp_new_i32(); - tcg_gen_ld_i32(t3, cpu_env, offsetof(CPUARMState, GE)); + tcg_gen_ld_i32(t3, tcg_env, offsetof(CPUARMState, GE)); gen_helper_sel_flags(t1, t3, t1, t2); store_reg(s, a->rd, t1); return true; @@ -7656,11 +6064,11 @@ static bool op_smlad(DisasContext *s, arg_rrrr *a, bool m_swap, bool sub) if (a->ra != 15) { t2 = load_reg(s, a->ra); - gen_helper_add_setq(t1, cpu_env, t1, t2); + gen_helper_add_setq(t1, tcg_env, t1, t2); } } else if (a->ra == 15) { /* Single saturation-checking addition */ - gen_helper_add_setq(t1, cpu_env, t1, t2); + gen_helper_add_setq(t1, tcg_env, t1, t2); } else { /* * We need to add the products and Ra together and then @@ -7842,9 +6250,9 @@ static bool op_div(DisasContext *s, arg_rrr *a, bool u) t1 = load_reg(s, a->rn); t2 = load_reg(s, a->rm); if (u) { - gen_helper_udiv(t1, cpu_env, t1, t2); + gen_helper_udiv(t1, tcg_env, t1, t2); } else { - gen_helper_sdiv(t1, cpu_env, t1, t2); + gen_helper_sdiv(t1, tcg_env, t1, t2); } store_reg(s, a->rd, t1); return true; @@ -7893,7 +6301,7 @@ static TCGv_i32 op_addr_block_pre(DisasContext *s, arg_ldst_block *a, int n) * either the original SP (if incrementing) or our * final SP (if decrementing), so that's what we check. */ - gen_helper_v8m_stackcheck(cpu_env, addr); + gen_helper_v8m_stackcheck(tcg_env, addr); } return addr; @@ -7920,7 +6328,7 @@ static void op_addr_block_post(DisasContext *s, arg_ldst_block *a, } } -static bool op_stm(DisasContext *s, arg_ldst_block *a, int min_n) +static bool op_stm(DisasContext *s, arg_ldst_block *a) { int i, j, n, list, mem_idx; bool user = a->u; @@ -7937,7 +6345,14 @@ static bool op_stm(DisasContext *s, arg_ldst_block *a, int min_n) list = a->list; n = ctpop16(list); - if (n < min_n || a->rn == 15) { + /* + * This is UNPREDICTABLE for n < 1 in all encodings, and we choose + * to UNDEF. In the T32 STM encoding n == 1 is also UNPREDICTABLE, + * but hardware treats it like the A32 version and implements the + * single-register-store, and some in-the-wild (buggy) software + * assumes that, so we don't UNDEF on that case. + */ + if (n < 1 || a->rn == 15) { unallocated_encoding(s); return true; } @@ -7954,7 +6369,7 @@ static bool op_stm(DisasContext *s, arg_ldst_block *a, int min_n) if (user && i != 15) { tmp = tcg_temp_new_i32(); - gen_helper_get_user_reg(tmp, cpu_env, tcg_constant_i32(i)); + gen_helper_get_user_reg(tmp, tcg_env, tcg_constant_i32(i)); } else { tmp = load_reg(s, i); } @@ -7973,8 +6388,7 @@ static bool op_stm(DisasContext *s, arg_ldst_block *a, int min_n) static bool trans_STM(DisasContext *s, arg_ldst_block *a) { - /* BitCount(list) < 1 is UNPREDICTABLE */ - return op_stm(s, a, 1); + return op_stm(s, a); } static bool trans_STM_t32(DisasContext *s, arg_ldst_block *a) @@ -7984,11 +6398,10 @@ static bool trans_STM_t32(DisasContext *s, arg_ldst_block *a) unallocated_encoding(s); return true; } - /* BitCount(list) < 2 is UNPREDICTABLE */ - return op_stm(s, a, 2); + return op_stm(s, a); } -static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n) +static bool do_ldm(DisasContext *s, arg_ldst_block *a) { int i, j, n, list, mem_idx; bool loaded_base; @@ -8017,7 +6430,14 @@ static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n) list = a->list; n = ctpop16(list); - if (n < min_n || a->rn == 15) { + /* + * This is UNPREDICTABLE for n < 1 in all encodings, and we choose + * to UNDEF. In the T32 LDM encoding n == 1 is also UNPREDICTABLE, + * but hardware treats it like the A32 version and implements the + * single-register-load, and some in-the-wild (buggy) software + * assumes that, so we don't UNDEF on that case. + */ + if (n < 1 || a->rn == 15) { unallocated_encoding(s); return true; } @@ -8037,7 +6457,7 @@ static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n) tmp = tcg_temp_new_i32(); gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); if (user) { - gen_helper_set_user_reg(cpu_env, tcg_constant_i32(i), tmp); + gen_helper_set_user_reg(tcg_env, tcg_constant_i32(i), tmp); } else if (i == a->rn) { loaded_var = tmp; loaded_base = true; @@ -8064,7 +6484,7 @@ static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n) /* Restore CPSR from SPSR. */ tmp = load_cpu_field(spsr); translator_io_start(&s->base); - gen_helper_cpsr_write_eret(cpu_env, tmp); + gen_helper_cpsr_write_eret(tcg_env, tmp); /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; } @@ -8083,8 +6503,7 @@ static bool trans_LDM_a32(DisasContext *s, arg_ldst_block *a) unallocated_encoding(s); return true; } - /* BitCount(list) < 1 is UNPREDICTABLE */ - return do_ldm(s, a, 1); + return do_ldm(s, a); } static bool trans_LDM_t32(DisasContext *s, arg_ldst_block *a) @@ -8094,16 +6513,14 @@ static bool trans_LDM_t32(DisasContext *s, arg_ldst_block *a) unallocated_encoding(s); return true; } - /* BitCount(list) < 2 is UNPREDICTABLE */ - return do_ldm(s, a, 2); + return do_ldm(s, a); } static bool trans_LDM_t16(DisasContext *s, arg_ldst_block *a) { /* Writeback is conditional on the base register not being loaded. */ a->w = !(a->list & (1 << a->rn)); - /* BitCount(list) < 1 is UNPREDICTABLE */ - return do_ldm(s, a, 1); + return do_ldm(s, a); } static bool trans_CLRM(DisasContext *s, arg_CLRM *a) @@ -8138,7 +6555,7 @@ static bool trans_CLRM(DisasContext *s, arg_CLRM *a) * Clear APSR (by calling the MSR helper with the same argument * as for "MSR APSR_nzcvqg, Rn": mask = 0b1100, SYSM=0) */ - gen_helper_v7m_msr(cpu_env, tcg_constant_i32(0xc00), zero); + gen_helper_v7m_msr(tcg_env, tcg_constant_i32(0xc00), zero); } clear_eci_state(s); return true; @@ -8525,7 +6942,7 @@ static bool trans_VCTP(DisasContext *s, arg_VCTP *a) tcg_gen_movcond_i32(TCG_COND_LEU, masklen, masklen, tcg_constant_i32(1 << (4 - a->size)), rn_shifted, tcg_constant_i32(16)); - gen_helper_mve_vctp(cpu_env, masklen); + gen_helper_mve_vctp(tcg_env, masklen); /* This insn updates predication bits */ s->base.is_jmp = DISAS_UPDATE_NOCHAIN; mve_update_eci(s); @@ -8703,12 +7120,12 @@ static bool trans_CPS_v7m(DisasContext *s, arg_CPS_v7m *a) /* FAULTMASK */ if (a->F) { addr = tcg_constant_i32(19); - gen_helper_v7m_msr(cpu_env, addr, tmp); + gen_helper_v7m_msr(tcg_env, addr, tmp); } /* PRIMASK */ if (a->I) { addr = tcg_constant_i32(16); - gen_helper_v7m_msr(cpu_env, addr, tmp); + gen_helper_v7m_msr(tcg_env, addr, tmp); } gen_rebuild_hflags(s, false); gen_lookup_tb(s); @@ -8778,7 +7195,7 @@ static bool trans_SETEND(DisasContext *s, arg_SETEND *a) return false; } if (a->E != (s->be_data == MO_BE)) { - gen_helper_setend(cpu_env); + gen_helper_setend(tcg_env); s->base.is_jmp = DISAS_UPDATE_EXIT; } return true; @@ -8794,12 +7211,12 @@ static bool trans_PLD(DisasContext *s, arg_PLD *a) return ENABLE_ARCH_5TE; } -static bool trans_PLDW(DisasContext *s, arg_PLD *a) +static bool trans_PLDW(DisasContext *s, arg_PLDW *a) { return arm_dc_feature(s, ARM_FEATURE_V7MP); } -static bool trans_PLI(DisasContext *s, arg_PLD *a) +static bool trans_PLI(DisasContext *s, arg_PLI *a) { return ENABLE_ARCH_7; } @@ -9127,7 +7544,7 @@ static bool insn_crosses_page(CPUARMState *env, DisasContext *s) static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUARMState *env = cs->env_ptr; + CPUARMState *env = cpu_env(cs); ARMCPU *cpu = env_archcpu(env); CPUARMTBFlags tb_flags = arm_tbflags_from_tb(dc->base.tb); uint32_t condexec, core_mmu_idx; @@ -9301,7 +7718,7 @@ static void arm_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) 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(); + dc->insn_start_updated = false; } static bool arm_check_kernelpage(DisasContext *dc) @@ -9343,7 +7760,8 @@ static bool arm_check_ss_active(DisasContext *dc) static void arm_post_translate_insn(DisasContext *dc) { - if (dc->condjmp && dc->base.is_jmp == DISAS_NEXT) { + if (dc->condjmp && + (dc->base.is_jmp == DISAS_NEXT || dc->base.is_jmp == DISAS_TOO_MANY)) { if (dc->pc_save != dc->condlabel.pc_save) { gen_update_pc(dc, dc->condlabel.pc_save - dc->pc_save); } @@ -9355,7 +7773,7 @@ static void arm_post_translate_insn(DisasContext *dc) static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUARMState *env = cpu->env_ptr; + CPUARMState *env = cpu_env(cpu); uint32_t pc = dc->base.pc_next; unsigned int insn; @@ -9373,7 +7791,7 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) * be possible after an indirect branch, at the start of the TB. */ assert(dc->base.num_insns == 1); - gen_helper_exception_pc_alignment(cpu_env, tcg_constant_tl(pc)); + gen_helper_exception_pc_alignment(tcg_env, tcg_constant_vaddr(pc)); dc->base.is_jmp = DISAS_NORETURN; dc->base.pc_next = QEMU_ALIGN_UP(pc, 4); return; @@ -9445,7 +7863,7 @@ static bool thumb_insn_is_unconditional(DisasContext *s, uint32_t insn) static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUARMState *env = cpu->env_ptr; + CPUARMState *env = cpu_env(cpu); uint32_t pc = dc->base.pc_next; uint32_t insn; bool is_16bit; @@ -9653,7 +8071,7 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) /* nothing more to generate */ break; case DISAS_WFI: - gen_helper_wfi(cpu_env, tcg_constant_i32(curr_insn_len(dc))); + 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. @@ -9661,10 +8079,10 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) tcg_gen_exit_tb(NULL, 0); break; case DISAS_WFE: - gen_helper_wfe(cpu_env); + gen_helper_wfe(tcg_env); break; case DISAS_YIELD: - gen_helper_yield(cpu_env); + gen_helper_yield(tcg_env); break; case DISAS_SWI: gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb)); @@ -9691,22 +8109,12 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -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 = { @@ -9715,12 +8123,10 @@ static const TranslatorOps thumb_translator_ops = { .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) +void arm_translate_code(CPUState *cpu, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc = { }; const TranslatorOps *ops = &arm_translator_ops; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index f748ba6f394..1bfdb0fb9bb 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -4,11 +4,11 @@ #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/translation-block.h" #include "exec/helper-gen.h" #include "internals.h" - +#include "cpu-features.h" /* internal defines */ @@ -91,15 +91,19 @@ typedef struct DisasContext { bool aarch64; bool thumb; bool lse2; - /* Because unallocated encodings generate different exception syndrome + /* + * 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. + * 0: not checked, + * 1: checked, access ok + * -1: checked, access denied */ - bool fp_access_checked; - bool sve_access_checked; + int8_t fp_access_checked; + int8_t sve_access_checked; /* ARMv8 single-step state (this is distinct from the QEMU gdbstub * single-step support). */ @@ -114,8 +118,8 @@ typedef struct DisasContext { bool unpriv; /* True if v8.3-PAuth is active. */ bool pauth_active; - /* True if v8.5-MTE access to tags is enabled. */ - bool ata; + /* True if v8.5-MTE access to tags is enabled; index with is_unpriv. */ + bool ata[2]; /* True if v8.5-MTE tag checks affect the PE; index with is_unpriv. */ bool mte_active[2]; /* True with v8.5-BTI and SCTLR_ELx.BT* set. */ @@ -138,12 +142,26 @@ typedef struct DisasContext { bool mve_no_pred; /* True if fine-grained traps are active */ bool fgt_active; - /* True if fine-grained trap on ERET is enabled */ - bool fgt_eret; /* True if fine-grained trap on SVC is enabled */ bool fgt_svc; + /* 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; + /* True if FPCR.AH is 1 (alternate floating point handling) */ + bool fpcr_ah; + /* True if FPCR.NEP is 1 (FEAT_AFP scalar upper-element result handling) */ + bool fpcr_nep; /* * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. * < 0, set by the current instruction. @@ -153,12 +171,12 @@ typedef struct DisasContext { uint8_t dcz_blocksize; /* A copy of cpu->gm_blocksize. */ uint8_t gm_blocksize; - /* True if this page is guarded. */ - bool guarded_page; + /* True if the current insn_start has been updated. */ + bool insn_start_updated; /* Bottom two bits of XScale c15_cpar coprocessor access control reg */ int c15_cpar; - /* TCG op of the current insn_start. */ - TCGOp *insn_start; + /* Offset from VNCR_EL2 when FEAT_NV2 redirects this reg to memory */ + uint32_t nv2_redirect_offset; } DisasContext; typedef struct DisasCompare { @@ -205,6 +223,11 @@ static inline int times_4(DisasContext *s, int x) return x * 4; } +static inline int times_8(DisasContext *s, int x) +{ + return x * 8; +} + static inline int times_2_plus_1(DisasContext *s, int x) { return x * 2 + 1; @@ -235,6 +258,11 @@ 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 */ @@ -259,10 +287,10 @@ static inline void disas_set_insn_syndrome(DisasContext *s, uint32_t syn) syn &= ARM_INSN_START_WORD2_MASK; syn >>= ARM_INSN_START_WORD2_SHIFT; - /* We check and clear insn_start_idx to catch multiple updates. */ - assert(s->insn_start != NULL); - tcg_set_insn_start_param(s->insn_start, 2, syn); - s->insn_start = NULL; + /* Check for multiple updates. */ + assert(!s->insn_start_updated); + s->insn_start_updated = true; + tcg_set_insn_start_param(s->base.insn_start, 2, syn); } static inline int curr_insn_len(DisasContext *s) @@ -329,8 +357,7 @@ 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_ld_i32(ret, tcg_env, offsetoflow32(CPUARMState, vfp.fpcr)); tcg_gen_extract_i32(ret, ret, 26, 1); return ret; @@ -343,9 +370,9 @@ static inline void set_pstate_bits(uint32_t bits) tcg_debug_assert(!(bits & CACHED_PSTATE_BITS)); - tcg_gen_ld_i32(p, cpu_env, offsetof(CPUARMState, pstate)); + tcg_gen_ld_i32(p, tcg_env, offsetof(CPUARMState, pstate)); tcg_gen_ori_i32(p, p, bits); - tcg_gen_st_i32(p, cpu_env, offsetof(CPUARMState, pstate)); + tcg_gen_st_i32(p, tcg_env, offsetof(CPUARMState, pstate)); } /* Clear bits within PSTATE. */ @@ -355,9 +382,9 @@ static inline void clear_pstate_bits(uint32_t bits) tcg_debug_assert(!(bits & CACHED_PSTATE_BITS)); - tcg_gen_ld_i32(p, cpu_env, offsetof(CPUARMState, pstate)); + tcg_gen_ld_i32(p, tcg_env, offsetof(CPUARMState, pstate)); tcg_gen_andi_i32(p, p, ~bits); - tcg_gen_st_i32(p, cpu_env, offsetof(CPUARMState, pstate)); + tcg_gen_st_i32(p, tcg_env, offsetof(CPUARMState, pstate)); } /* If the singlestep state is Active-not-pending, advance to Active-pending. */ @@ -374,7 +401,7 @@ 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)); + gen_helper_exception_swstep(tcg_env, tcg_constant_i32(syn)); } /* @@ -384,6 +411,36 @@ static inline void gen_swstep_exception(DisasContext *s, int isv, int ex) */ 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); @@ -407,6 +464,38 @@ 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); @@ -414,20 +503,45 @@ 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, @@ -442,6 +556,10 @@ void gen_gvec_sri(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, 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, @@ -457,6 +575,52 @@ void gen_gvec_saba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, 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); + +void gen_gvec_cls(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_clz(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_cnt(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_rbit(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_rev16(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_rev32(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_rev64(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); + +void gen_gvec_saddlp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_sadalp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_uaddlp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_uadalp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); + +/* These exclusively manipulate the sign bit. */ +void gen_gvec_fabs(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz); +void gen_gvec_fneg(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz); + +void gen_gvec_urecpe(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_ursqrte(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t opr_sz, uint32_t max_sz); + /* * Forward to the isar_feature_* tests given a DisasContext pointer. */ @@ -482,13 +646,13 @@ typedef void NeonGenThreeOpEnvFn(TCGv_i32, TCGv_env, 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 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); @@ -509,55 +673,19 @@ 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); + int offset = offsetof(CPUARMState, vfp.fp_status[flavour]); + + tcg_gen_addi_ptr(statusptr, tcg_env, offset); return statusptr; } @@ -679,7 +807,7 @@ static inline void set_disas_label(DisasContext *s, DisasLabel l) static inline TCGv_ptr gen_lookup_cp_reg(uint32_t key) { TCGv_ptr ret = tcg_temp_new_ptr(); - gen_helper_lookup_cp_reg(ret, cpu_env, tcg_constant_i32(key)); + gen_helper_lookup_cp_reg(ret, tcg_env, tcg_constant_i32(key)); return ret; } diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 6712a2c790e..986eaf8ffa4 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -23,6 +23,7 @@ #include "tcg/tcg-gvec-desc.h" #include "fpu/softfloat.h" #include "qemu/int128.h" +#include "crypto/clmul.h" #include "vec_internal.h" /* @@ -310,6 +311,78 @@ void HELPER(neon_sqrdmulh_h)(void *vd, void *vn, void *vm, 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) { @@ -473,6 +546,78 @@ void HELPER(neon_sqrdmulh_s)(void *vd, void *vn, void *vm, 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) { @@ -691,6 +836,13 @@ 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; \ @@ -708,7 +860,7 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ n[i * 4 + 2] * m2 + \ n[i * 4 + 3] * m3); \ } while (++i < segend); \ - segend = i + 4; \ + segend = i + (16 / sizeof(TYPED)); \ } while (i < opr_sz_n); \ clear_tail(d, opr_sz, simd_maxsz(desc)); \ } @@ -721,26 +873,27 @@ 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) + float_status *fpst, 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; + bool rot = extract32(desc, SIMD_DATA_SHIFT, 1); + bool fpcr_ah = extract64(desc, SIMD_DATA_SHIFT + 1, 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 e1 = m[H2(i + 1)]; float16 e2 = n[H2(i + 1)]; - float16 e3 = m[H2(i)] ^ neg_real; + float16 e3 = m[H2(i)]; + + if (rot) { + e3 = float16_maybe_ah_chs(e3, fpcr_ah); + } else { + e1 = float16_maybe_ah_chs(e1, fpcr_ah); + } d[H2(i)] = float16_add(e0, e1, fpst); d[H2(i + 1)] = float16_add(e2, e3, fpst); @@ -749,26 +902,27 @@ void HELPER(gvec_fcaddh)(void *vd, void *vn, void *vm, } void HELPER(gvec_fcadds)(void *vd, void *vn, void *vm, - void *vfpst, uint32_t desc) + float_status *fpst, 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; + bool rot = extract32(desc, SIMD_DATA_SHIFT, 1); + bool fpcr_ah = extract64(desc, SIMD_DATA_SHIFT + 1, 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 e1 = m[H4(i + 1)]; float32 e2 = n[H4(i + 1)]; - float32 e3 = m[H4(i)] ^ neg_real; + float32 e3 = m[H4(i)]; + + if (rot) { + e3 = float32_maybe_ah_chs(e3, fpcr_ah); + } else { + e1 = float32_maybe_ah_chs(e1, fpcr_ah); + } d[H4(i)] = float32_add(e0, e1, fpst); d[H4(i + 1)] = float32_add(e2, e3, fpst); @@ -777,26 +931,27 @@ void HELPER(gvec_fcadds)(void *vd, void *vn, void *vm, } void HELPER(gvec_fcaddd)(void *vd, void *vn, void *vm, - void *vfpst, uint32_t desc) + float_status *fpst, 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; + bool rot = extract32(desc, SIMD_DATA_SHIFT, 1); + bool fpcr_ah = extract64(desc, SIMD_DATA_SHIFT + 1, 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 e1 = m[i + 1]; float64 e2 = n[i + 1]; - float64 e3 = m[i] ^ neg_real; + float64 e3 = m[i]; + + if (rot) { + e3 = float64_maybe_ah_chs(e3, fpcr_ah); + } else { + e1 = float64_maybe_ah_chs(e1, fpcr_ah); + } d[i] = float64_add(e0, e1, fpst); d[i + 1] = float64_add(e2, e3, fpst); @@ -805,152 +960,167 @@ void HELPER(gvec_fcaddd)(void *vd, void *vn, void *vm, } void HELPER(gvec_fcmlah)(void *vd, void *vn, void *vm, void *va, - void *vfpst, uint32_t desc) + float_status *fpst, 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; + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + uint32_t negf_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t negf_real = flip ^ negf_imag; + float16 negx_imag, negx_real; uintptr_t i; - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 15; - neg_imag <<= 15; + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (negf_real & ~fpcr_ah) << 15; + negx_imag = (negf_imag & ~fpcr_ah) << 15; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); for (i = 0; i < opr_sz / 2; i += 2) { float16 e2 = n[H2(i + flip)]; - float16 e1 = m[H2(i + flip)] ^ neg_real; + float16 e1 = m[H2(i + flip)] ^ negx_real; float16 e4 = e2; - float16 e3 = m[H2(i + 1 - flip)] ^ neg_imag; + float16 e3 = m[H2(i + 1 - flip)] ^ negx_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); + d[H2(i)] = float16_muladd(e2, e1, a[H2(i)], negf_real, fpst); + d[H2(i + 1)] = float16_muladd(e4, e3, a[H2(i + 1)], negf_imag, 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) + float_status *fpst, 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 negf_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; + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 4, 1); + uint32_t negf_real = flip ^ negf_imag; intptr_t elements = opr_sz / sizeof(float16); - intptr_t eltspersegment = 16 / sizeof(float16); + intptr_t eltspersegment = MIN(16 / sizeof(float16), elements); + float16 negx_imag, negx_real; intptr_t i, j; - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 15; - neg_imag <<= 15; + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (negf_real & ~fpcr_ah) << 15; + negx_imag = (negf_imag & ~fpcr_ah) << 15; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); 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); + float16 e1 = negx_real ^ (flip ? mi : mr); + float16 e3 = negx_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); + d[H2(j)] = float16_muladd(e2, e1, a[H2(j)], negf_real, fpst); + d[H2(j + 1)] = float16_muladd(e4, e3, a[H2(j + 1)], negf_imag, 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) + float_status *fpst, 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; + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + uint32_t negf_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t negf_real = flip ^ negf_imag; + float32 negx_imag, negx_real; uintptr_t i; - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 31; - neg_imag <<= 31; + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (negf_real & ~fpcr_ah) << 31; + negx_imag = (negf_imag & ~fpcr_ah) << 31; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); for (i = 0; i < opr_sz / 4; i += 2) { float32 e2 = n[H4(i + flip)]; - float32 e1 = m[H4(i + flip)] ^ neg_real; + float32 e1 = m[H4(i + flip)] ^ negx_real; float32 e4 = e2; - float32 e3 = m[H4(i + 1 - flip)] ^ neg_imag; + float32 e3 = m[H4(i + 1 - flip)] ^ negx_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); + d[H4(i)] = float32_muladd(e2, e1, a[H4(i)], negf_real, fpst); + d[H4(i + 1)] = float32_muladd(e4, e3, a[H4(i + 1)], negf_imag, 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) + float_status *fpst, 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 negf_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; + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 4, 1); + uint32_t negf_real = flip ^ negf_imag; intptr_t elements = opr_sz / sizeof(float32); - intptr_t eltspersegment = 16 / sizeof(float32); + intptr_t eltspersegment = MIN(16 / sizeof(float32), elements); + float32 negx_imag, negx_real; intptr_t i, j; - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 31; - neg_imag <<= 31; + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (negf_real & ~fpcr_ah) << 31; + negx_imag = (negf_imag & ~fpcr_ah) << 31; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); 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); + float32 e1 = negx_real ^ (flip ? mi : mr); + float32 e3 = negx_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); + d[H4(j)] = float32_muladd(e2, e1, a[H4(j)], negf_real, fpst); + d[H4(j + 1)] = float32_muladd(e4, e3, a[H4(j + 1)], negf_imag, 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) + float_status *fpst, 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; + uint32_t fpcr_ah = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + uint32_t negf_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t negf_real = flip ^ negf_imag; + float64 negx_real, negx_imag; uintptr_t i; - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 63; - neg_imag <<= 63; + /* With AH=0, use negx; with AH=1 use negf. */ + negx_real = (uint64_t)(negf_real & ~fpcr_ah) << 63; + negx_imag = (uint64_t)(negf_imag & ~fpcr_ah) << 63; + negf_real = (negf_real & fpcr_ah ? float_muladd_negate_product : 0); + negf_imag = (negf_imag & fpcr_ah ? float_muladd_negate_product : 0); for (i = 0; i < opr_sz / 8; i += 2) { float64 e2 = n[i + flip]; - float64 e1 = m[i + flip] ^ neg_real; + float64 e1 = m[i + flip] ^ negx_real; float64 e4 = e2; - float64 e3 = m[i + 1 - flip] ^ neg_imag; + float64 e3 = m[i + 1 - flip] ^ negx_imag; - d[i] = float64_muladd(e2, e1, a[i], 0, fpst); - d[i + 1] = float64_muladd(e4, e3, a[i + 1], 0, fpst); + d[i] = float64_muladd(e2, e1, a[i], negf_real, fpst); + d[i + 1] = float64_muladd(e4, e3, a[i + 1], negf_imag, fpst); } clear_tail(d, opr_sz, simd_maxsz(desc)); } @@ -970,6 +1140,11 @@ 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); @@ -980,6 +1155,11 @@ 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); @@ -990,6 +1170,11 @@ 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); @@ -1000,6 +1185,11 @@ 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); @@ -1010,9 +1200,13 @@ 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) +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, float_status *fpst) { - float_status *fpst = fpstp; if (float16_is_any_nan(x)) { float_raise(float_flag_invalid, fpst); return 0; @@ -1020,9 +1214,8 @@ static int16_t vfp_tosszh(float16 x, void *fpstp) return float16_to_int16_round_to_zero(x, fpst); } -static uint16_t vfp_touszh(float16 x, void *fpstp) +static uint16_t vfp_touszh(float16 x, float_status *fpst) { - float_status *fpst = fpstp; if (float16_is_any_nan(x)) { float_raise(float_flag_invalid, fpst); return 0; @@ -1031,7 +1224,7 @@ static uint16_t vfp_touszh(float16 x, void *fpstp) } #define DO_2OP(NAME, FUNC, TYPE) \ -void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ +void HELPER(NAME)(void *vd, void *vn, float_status *stat, uint32_t desc) \ { \ intptr_t i, oprsz = simd_oprsz(desc); \ TYPE *d = vd, *n = vn; \ @@ -1043,10 +1236,12 @@ void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ DO_2OP(gvec_frecpe_h, helper_recpe_f16, float16) DO_2OP(gvec_frecpe_s, helper_recpe_f32, float32) +DO_2OP(gvec_frecpe_rpres_s, helper_recpe_rpres_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_rpres_s, helper_rsqrte_rpres_f32, float32) DO_2OP(gvec_frsqrte_d, helper_rsqrte_f64, float64) DO_2OP(gvec_vrintx_h, float16_round_to_int, float16) @@ -1076,8 +1271,10 @@ DO_2OP(gvec_touszh, vfp_touszh, float16) #define DO_2OP_CMP0(FN, CMPOP, DIRN) \ WRAP_CMP0_##DIRN(FN, CMPOP, float16) \ WRAP_CMP0_##DIRN(FN, CMPOP, float32) \ + WRAP_CMP0_##DIRN(FN, CMPOP, float64) \ DO_2OP(gvec_f##FN##0_h, float16_##FN##0, float16) \ - DO_2OP(gvec_f##FN##0_s, float32_##FN##0, float32) + DO_2OP(gvec_f##FN##0_s, float32_##FN##0, float32) \ + DO_2OP(gvec_f##FN##0_d, float64_##FN##0, float64) DO_2OP_CMP0(cgt, cgt, FWD) DO_2OP_CMP0(cge, cge, FWD) @@ -1128,6 +1325,30 @@ 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)); +} + +/* ABD when FPCR.AH = 1: avoid flipping sign bit of a NaN result */ +static float16 float16_ah_abd(float16 op1, float16 op2, float_status *stat) +{ + float16 r = float16_sub(op1, op2, stat); + return float16_is_any_nan(r) ? r : float16_abs(r); +} + +static float32 float32_ah_abd(float32 op1, float32 op2, float_status *stat) +{ + float32 r = float32_sub(op1, op2, stat); + return float32_is_any_nan(r) ? r : float32_abs(r); +} + +static float64 float64_ah_abd(float64 op1, float64 op2, float_status *stat) +{ + float64 r = float64_sub(op1, op2, stat); + return float64_is_any_nan(r) ? r : float64_abs(r); +} + /* * Reciprocal step. These are the AArch32 version which uses a * non-fused multiply-and-subtract. @@ -1184,7 +1405,8 @@ static float32 float32_rsqrts_nf(float32 op1, float32 op2, float_status *stat) } #define DO_3OP(NAME, FUNC, TYPE) \ -void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, \ + float_status *stat, uint32_t desc) \ { \ intptr_t i, oprsz = simd_oprsz(desc); \ TYPE *d = vd, *n = vn, *m = vm; \ @@ -1212,33 +1434,47 @@ 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_ah_fabd_h, float16_ah_abd, float16) +DO_3OP(gvec_ah_fabd_s, float32_ah_abd, float32) +DO_3OP(gvec_ah_fabd_d, float64_ah_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) @@ -1247,6 +1483,13 @@ 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) @@ -1256,6 +1499,22 @@ 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) +DO_3OP(gvec_ah_recps_h, helper_recpsf_ah_f16, float16) +DO_3OP(gvec_ah_recps_s, helper_recpsf_ah_f32, float32) +DO_3OP(gvec_ah_recps_d, helper_recpsf_ah_f64, float64) + +DO_3OP(gvec_ah_rsqrts_h, helper_rsqrtsf_ah_f16, float16) +DO_3OP(gvec_ah_rsqrts_s, helper_rsqrtsf_ah_f32, float32) +DO_3OP(gvec_ah_rsqrts_d, helper_rsqrtsf_ah_f64, float64) + +DO_3OP(gvec_ah_fmax_h, helper_vfp_ah_maxh, float16) +DO_3OP(gvec_ah_fmax_s, helper_vfp_ah_maxs, float32) +DO_3OP(gvec_ah_fmax_d, helper_vfp_ah_maxd, float64) + +DO_3OP(gvec_ah_fmin_h, helper_vfp_ah_minh, float16) +DO_3OP(gvec_ah_fmin_s, helper_vfp_ah_mins, float32) +DO_3OP(gvec_ah_fmin_d, helper_vfp_ah_mind, float64) + #endif #undef DO_3OP @@ -1297,6 +1556,12 @@ static float32 float32_muladd_f(float32 dest, float32 op1, float32 op2, 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) { @@ -1309,8 +1574,33 @@ static float32 float32_mulsub_f(float32 dest, float32 op1, float32 op2, 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) \ +static float64 float64_mulsub_f(float64 dest, float64 op1, float64 op2, + float_status *stat) +{ + return float64_muladd(float64_chs(op1), op2, dest, 0, stat); +} + +static float16 float16_ah_mulsub_f(float16 dest, float16 op1, float16 op2, + float_status *stat) +{ + return float16_muladd(op1, op2, dest, float_muladd_negate_product, stat); +} + +static float32 float32_ah_mulsub_f(float32 dest, float32 op1, float32 op2, + float_status *stat) +{ + return float32_muladd(op1, op2, dest, float_muladd_negate_product, stat); +} + +static float64 float64_ah_mulsub_f(float64 dest, float64 op1, float64 op2, + float_status *stat) +{ + return float64_muladd(op1, op2, dest, float_muladd_negate_product, stat); +} + +#define DO_MULADD(NAME, FUNC, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, \ + float_status *stat, uint32_t desc) \ { \ intptr_t i, oprsz = simd_oprsz(desc); \ TYPE *d = vd, *n = vn, *m = vm; \ @@ -1328,9 +1618,15 @@ 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) + +DO_MULADD(gvec_ah_vfms_h, float16_ah_mulsub_f, float16) +DO_MULADD(gvec_ah_vfms_s, float32_ah_mulsub_f, float32) +DO_MULADD(gvec_ah_vfms_d, float64_ah_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. @@ -1384,8 +1680,9 @@ 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) \ +#define DO_FMUL_IDX(NAME, ADD, MUL, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, \ + float_status *stat, uint32_t desc) \ { \ intptr_t i, j, oprsz = simd_oprsz(desc); \ intptr_t segment = MIN(16, oprsz) / sizeof(TYPE); \ @@ -1394,58 +1691,68 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ 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); \ + d[i + j] = ADD(d[i + j], 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) +#define 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) +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, 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 +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) \ +#define DO_FMLA_IDX(NAME, TYPE, H, NEGX, NEGF) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, \ - void *stat, uint32_t desc) \ + float_status *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); \ + intptr_t idx = simd_data(desc); \ 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); \ + d[i + j] = TYPE##_muladd(n[i + j] ^ NEGX, mm, \ + a[i + j], NEGF, 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) +DO_FMLA_IDX(gvec_fmla_idx_h, float16, H2, 0, 0) +DO_FMLA_IDX(gvec_fmla_idx_s, float32, H4, 0, 0) +DO_FMLA_IDX(gvec_fmla_idx_d, float64, H8, 0, 0) + +DO_FMLA_IDX(gvec_fmls_idx_h, float16, H2, INT16_MIN, 0) +DO_FMLA_IDX(gvec_fmls_idx_s, float32, H4, INT32_MIN, 0) +DO_FMLA_IDX(gvec_fmls_idx_d, float64, H8, INT64_MIN, 0) + +DO_FMLA_IDX(gvec_ah_fmls_idx_h, float16, H2, 0, float_muladd_negate_product) +DO_FMLA_IDX(gvec_ah_fmls_idx_s, float32, H4, 0, float_muladd_negate_product) +DO_FMLA_IDX(gvec_ah_fmls_idx_d, float64, H8, 0, float_muladd_negate_product) #undef DO_FMLA_IDX @@ -1489,6 +1796,14 @@ 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, @@ -1579,6 +1894,62 @@ void HELPER(gvec_sqsub_d)(void *vd, void *vq, void *vn, 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) \ @@ -1754,135 +2125,171 @@ static uint64_t load4_f16(uint64_t *ptr, int is_q, int is_2) * 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) +static void do_fmlal(float32 *d, void *vn, void *vm, + CPUARMState *env, uint32_t desc, + ARMFPStatusFlavour fpst_idx, + uint64_t negx, int negf) { + float_status *fpst = &env->vfp.fp_status[fpst_idx]; + bool fz16 = env->vfp.fpcr & FPCR_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); + /* + * Pre-load all of the f16 data, avoiding overlap issues. + * Negate all inputs for AH=0 FMLSL at once. + */ + n_4 = load4_f16(vn, is_q, is_2) ^ negx; 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); + d[H4(i)] = float32_muladd(n_1, m_1, d[H4(i)], negf, 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, 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)); + bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); + uint64_t negx = is_s ? 0x8000800080008000ull : 0; + + do_fmlal(vd, vn, vm, env, desc, FPST_STD, negx, 0); } void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, - void *venv, uint32_t desc) + CPUARMState *env, 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)); + bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); + uint64_t negx = 0; + int negf = 0; + + if (is_s) { + if (env->vfp.fpcr & FPCR_AH) { + negf = float_muladd_negate_product; + } else { + negx = 0x8000800080008000ull; + } + } + do_fmlal(vd, vn, vm, env, desc, FPST_A64, negx, negf); } void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, - void *venv, uint32_t desc) + CPUARMState *env, uint32_t desc) { intptr_t i, oprsz = simd_oprsz(desc); - uint16_t negn = extract32(desc, SIMD_DATA_SHIFT, 1) << 15; + bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); 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); + float_status *status = &env->vfp.fp_status[FPST_A64]; + bool fz16 = env->vfp.fpcr & FPCR_FZ16; + int negx = 0, negf = 0; + + if (is_s) { + if (env->vfp.fpcr & FPCR_AH) { + negf = float_muladd_negate_product; + } else { + negx = 0x8000; + } + } for (i = 0; i < oprsz; i += sizeof(float32)) { - float16 nn_16 = *(float16 *)(vn + H1_2(i + sel)) ^ negn; + float16 nn_16 = *(float16 *)(vn + H1_2(i + sel)) ^ negx; 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); + *(float32 *)(vd + H1_4(i)) = float32_muladd(nn, mm, aa, negf, status); } } -static void do_fmlal_idx(float32 *d, void *vn, void *vm, float_status *fpst, - uint32_t desc, bool fz16) +static void do_fmlal_idx(float32 *d, void *vn, void *vm, + CPUARMState *env, uint32_t desc, + ARMFPStatusFlavour fpst_idx, + uint64_t negx, int negf) { + float_status *fpst = &env->vfp.fp_status[fpst_idx]; + bool fz16 = env->vfp.fpcr & FPCR_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; - } - + /* + * Pre-load all of the f16 data, avoiding overlap issues. + * Negate all inputs for AH=0 FMLSL at once. + */ + n_4 = load4_f16(vn, is_q, is_2) ^ negx; 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); + d[H4(i)] = float32_muladd(n_1, m_1, d[H4(i)], negf, 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, 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)); + bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); + uint64_t negx = is_s ? 0x8000800080008000ull : 0; + + do_fmlal_idx(vd, vn, vm, env, desc, FPST_STD, negx, 0); } void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, - void *venv, uint32_t desc) + CPUARMState *env, 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)); + bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); + uint64_t negx = 0; + int negf = 0; + + if (is_s) { + if (env->vfp.fpcr & FPCR_AH) { + negf = float_muladd_negate_product; + } else { + negx = 0x8000800080008000ull; + } + } + do_fmlal_idx(vd, vn, vm, env, desc, FPST_A64, negx, negf); } void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, - void *venv, uint32_t desc) + CPUARMState *env, uint32_t desc) { intptr_t i, j, oprsz = simd_oprsz(desc); - uint16_t negn = extract32(desc, SIMD_DATA_SHIFT, 1) << 15; + bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); 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); + float_status *status = &env->vfp.fp_status[FPST_A64]; + bool fz16 = env->vfp.fpcr & FPCR_FZ16; + int negx = 0, negf = 0; + if (is_s) { + if (env->vfp.fpcr & FPCR_AH) { + negf = float_muladd_negate_product; + } else { + negx = 0x8000; + } + } 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; + float16 nn_16 = *(float16 *)(vn + H1_2(i + j + sel)) ^ negx; 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); + float32_muladd(nn, mm, aa, negf, status); } } } @@ -1986,21 +2393,11 @@ void HELPER(gvec_ushl_h)(void *vd, void *vn, void *vm, uint32_t desc) */ void HELPER(gvec_pmul_b)(void *vd, void *vn, void *vm, uint32_t desc) { - intptr_t i, j, opr_sz = simd_oprsz(desc); + intptr_t i, opr_sz = simd_oprsz(desc); uint64_t *d = vd, *n = vn, *m = vm; for (i = 0; i < opr_sz / 8; ++i) { - uint64_t nn = n[i]; - uint64_t mm = m[i]; - uint64_t rr = 0; - - for (j = 0; j < 8; ++j) { - uint64_t mask = (nn & 0x0101010101010101ull) * 0xff; - rr ^= mm & mask; - mm = (mm << 1) & 0xfefefefefefefefeull; - nn >>= 1; - } - d[i] = rr; + d[i] = clmul_8x8_low(n[i], m[i]); } clear_tail(d, opr_sz, simd_maxsz(desc)); } @@ -2012,84 +2409,28 @@ void HELPER(gvec_pmul_b)(void *vd, void *vn, void *vm, uint32_t desc) */ void HELPER(gvec_pmull_q)(void *vd, void *vn, void *vm, uint32_t desc) { - intptr_t i, j, opr_sz = simd_oprsz(desc); + intptr_t i, opr_sz = simd_oprsz(desc); intptr_t hi = simd_data(desc); uint64_t *d = vd, *n = vn, *m = vm; for (i = 0; i < opr_sz / 8; i += 2) { - uint64_t nn = n[i + hi]; - uint64_t mm = m[i + hi]; - uint64_t rhi = 0; - uint64_t rlo = 0; - - /* Bit 0 can only influence the low 64-bit result. */ - if (nn & 1) { - rlo = mm; - } - - for (j = 1; j < 64; ++j) { - uint64_t mask = -((nn >> j) & 1); - rlo ^= (mm << j) & mask; - rhi ^= (mm >> (64 - j)) & mask; - } - d[i] = rlo; - d[i + 1] = rhi; + Int128 r = clmul_64(n[i + hi], m[i + hi]); + d[i] = int128_getlo(r); + d[i + 1] = int128_gethi(r); } clear_tail(d, opr_sz, simd_maxsz(desc)); } -/* - * 8x8->16 polynomial multiply. - * - * The byte inputs are expanded to (or extracted from) half-words. - * Note that neon and sve2 get the inputs from different positions. - * This allows 4 bytes to be processed in parallel with uint64_t. - */ - -static uint64_t expand_byte_to_half(uint64_t x) -{ - return (x & 0x000000ff) - | ((x & 0x0000ff00) << 8) - | ((x & 0x00ff0000) << 16) - | ((x & 0xff000000) << 24); -} - -uint64_t pmull_w(uint64_t op1, uint64_t op2) -{ - uint64_t result = 0; - int i; - for (i = 0; i < 16; ++i) { - uint64_t mask = (op1 & 0x0000000100000001ull) * 0xffffffff; - result ^= op2 & mask; - op1 >>= 1; - op2 <<= 1; - } - return result; -} - -uint64_t pmull_h(uint64_t op1, uint64_t op2) -{ - uint64_t result = 0; - int i; - for (i = 0; i < 8; ++i) { - uint64_t mask = (op1 & 0x0001000100010001ull) * 0xffff; - result ^= op2 & mask; - op1 >>= 1; - op2 <<= 1; - } - return result; -} - void HELPER(neon_pmull_h)(void *vd, void *vn, void *vm, uint32_t desc) { int hi = simd_data(desc); uint64_t *d = vd, *n = vn, *m = vm; uint64_t nn = n[hi], mm = m[hi]; - d[0] = pmull_h(expand_byte_to_half(nn), expand_byte_to_half(mm)); + d[0] = clmul_8x4_packed(nn, mm); nn >>= 32; mm >>= 32; - d[1] = pmull_h(expand_byte_to_half(nn), expand_byte_to_half(mm)); + d[1] = clmul_8x4_packed(nn, mm); clear_tail(d, 16, simd_maxsz(desc)); } @@ -2102,25 +2443,10 @@ void HELPER(sve2_pmull_h)(void *vd, void *vn, void *vm, uint32_t desc) uint64_t *d = vd, *n = vn, *m = vm; for (i = 0; i < opr_sz / 8; ++i) { - uint64_t nn = (n[i] >> shift) & 0x00ff00ff00ff00ffull; - uint64_t mm = (m[i] >> shift) & 0x00ff00ff00ff00ffull; - - d[i] = pmull_h(nn, mm); + d[i] = clmul_8x4_even(n[i] >> shift, m[i] >> shift); } } -static uint64_t pmull_d(uint64_t op1, uint64_t op2) -{ - uint64_t result = 0; - int i; - - for (i = 0; i < 32; ++i) { - uint64_t mask = -((op1 >> i) & 1); - result ^= (op2 << i) & mask; - } - return result; -} - void HELPER(sve2_pmull_d)(void *vd, void *vn, void *vm, uint32_t desc) { intptr_t sel = H4(simd_data(desc)); @@ -2129,7 +2455,7 @@ void HELPER(sve2_pmull_d)(void *vd, void *vn, void *vm, uint32_t desc) uint64_t *d = vd; for (i = 0; i < opr_sz / 8; ++i) { - d[i] = pmull_d(n[2 * i + sel], m[2 * i + sel]); + d[i] = clmul_32(n[2 * i + sel], m[2 * i + sel]); } } #endif @@ -2207,53 +2533,104 @@ 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_3OP_PAIR(NAME, FUNC, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, \ + float_status *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, ) + +#ifdef TARGET_AARCH64 +DO_3OP_PAIR(gvec_ah_fmaxp_h, helper_vfp_ah_maxh, float16, H2) +DO_3OP_PAIR(gvec_ah_fmaxp_s, helper_vfp_ah_maxs, float32, H4) +DO_3OP_PAIR(gvec_ah_fmaxp_d, helper_vfp_ah_maxd, float64, ) + +DO_3OP_PAIR(gvec_ah_fminp_h, helper_vfp_ah_minh, float16, H2) +DO_3OP_PAIR(gvec_ah_fminp_s, helper_vfp_ah_mins, float32, H4) +DO_3OP_PAIR(gvec_ah_fminp_d, helper_vfp_ah_mind, float64, ) +#endif + +#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) \ + void HELPER(NAME)(void *vd, void *vn, float_status *stat, uint32_t desc) \ { \ intptr_t i, oprsz = simd_oprsz(desc); \ int shift = simd_data(desc); \ @@ -2265,21 +2642,25 @@ DO_NEON_PAIRWISE(neon_pmin, min) clear_tail(d, oprsz, simd_maxsz(desc)); \ } +DO_VCVT_FIXED(gvec_vcvt_sd, helper_vfp_sqtod, uint64_t) +DO_VCVT_FIXED(gvec_vcvt_ud, helper_vfp_uqtod, uint64_t) 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) + +DO_VCVT_FIXED(gvec_vcvt_rz_ds, helper_vfp_tosqd_round_to_zero, uint64_t) +DO_VCVT_FIXED(gvec_vcvt_rz_du, helper_vfp_touqd_round_to_zero, uint64_t) +DO_VCVT_FIXED(gvec_vcvt_rz_fs, helper_vfp_tosls_round_to_zero, uint32_t) +DO_VCVT_FIXED(gvec_vcvt_rz_fu, helper_vfp_touls_round_to_zero, uint32_t) +DO_VCVT_FIXED(gvec_vcvt_rz_hs, helper_vfp_toshh_round_to_zero, uint16_t) +DO_VCVT_FIXED(gvec_vcvt_rz_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) \ + void HELPER(NAME)(void *vd, void *vn, float_status *fpst, 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); \ @@ -2292,6 +2673,8 @@ DO_VCVT_FIXED(gvec_vcvt_hu, helper_vfp_touhh_round_to_zero, uint16_t) clear_tail(d, oprsz, simd_maxsz(desc)); \ } +DO_VCVT_RMODE(gvec_vcvt_rm_sd, helper_vfp_tosqd, uint64_t) +DO_VCVT_RMODE(gvec_vcvt_rm_ud, helper_vfp_touqd, uint64_t) 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) @@ -2300,9 +2683,8 @@ 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) \ + void HELPER(NAME)(void *vd, void *vn, float_status *fpst, 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); \ @@ -2321,10 +2703,9 @@ 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) +void HELPER(simd_tblx)(void *vd, void *vm, CPUARMState *env, 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); @@ -2557,44 +2938,109 @@ DO_MMLA_B(gvec_usmmla_b, do_usmmla_b) * BFloat16 Dot Product */ -float32 bfdotadd(float32 sum, uint32_t e1, uint32_t e2) +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 = env->vfp.fp_status[is_a64(env) ? FPST_A64 : FPST_A32]; + set_default_nan_mode(true, statusp); + + if (ebf) { + /* EBF=1 needs to do a step with round-to-odd semantics */ + *oddstatusp = *statusp; + set_float_rounding_mode(float_round_to_odd, oddstatusp); + } else { + set_flush_to_zero(true, statusp); + set_flush_inputs_to_zero(true, statusp); + set_float_rounding_mode(float_round_to_odd_inf, statusp); + } + return ebf; +} + +float32 bfdotadd(float32 sum, uint32_t e1, uint32_t e2, float_status *fpst) { - /* 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); + 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; } -void HELPER(gvec_bfdot)(void *vd, void *vn, void *vm, void *va, uint32_t desc) +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; - for (i = 0; i < opr_sz / 4; ++i) { - d[i] = bfdotadd(a[i], n[i], m[i]); + 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, uint32_t desc) + void *va, CPUARMState *env, uint32_t desc) { intptr_t i, j, opr_sz = simd_oprsz(desc); intptr_t index = simd_data(desc); @@ -2602,59 +3048,106 @@ void HELPER(gvec_bfdot_idx)(void *vd, void *vn, void *vm, intptr_t eltspersegment = MIN(16 / 4, elements); float32 *d = vd, *a = va; uint32_t *n = vn, *m = vm; + float_status fpst, fpst_odd; - for (i = 0; i < elements; i += eltspersegment) { - uint32_t m_idx = m[i + H4(index)]; + 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(a[j], n[j], m_idx); + 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, uint32_t 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; - for (s = 0; s < opr_sz / 4; s += 4) { - float32 sum00, sum01, sum10, sum11; + 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(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)]); + /* + * 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; - d[s + H4(0 + 0)] = sum00; - d[s + H4(0 + 1)] = sum01; - d[s + H4(2 + 0)] = sum10; - d[s + H4(2 + 1)] = 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) + float_status *stat, uint32_t desc) { intptr_t i, opr_sz = simd_oprsz(desc); intptr_t sel = simd_data(desc); @@ -2670,7 +3163,7 @@ void HELPER(gvec_bfmlal)(void *vd, void *vn, void *vm, void *va, } void HELPER(gvec_bfmlal_idx)(void *vd, void *vn, void *vm, - void *va, void *stat, uint32_t desc) + void *va, float_status *stat, uint32_t desc) { intptr_t i, j, opr_sz = simd_oprsz(desc); intptr_t sel = extract32(desc, SIMD_DATA_SHIFT, 1); @@ -2714,3 +3207,49 @@ 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) + +/* Bit count in each 8-bit word. */ +void HELPER(gvec_cnt_b)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint8_t *d = vd, *n = vn; + + for (i = 0; i < opr_sz; ++i) { + d[i] = ctpop8(n[i]); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +/* Reverse bits in each 8 bit word */ +void HELPER(gvec_rbit_b)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn; + + for (i = 0; i < opr_sz / 8; ++i) { + d[i] = revbit64(bswap64(n[i])); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_urecpe_s)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint32_t *d = vd, *n = vn; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = helper_recpe_u32(n[i]); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_ursqrte_s)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint32_t *d = vd, *n = vn; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = helper_rsqrte_u32(n[i]); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h index 1f4ed80ff76..c02f9c37f83 100644 --- a/target/arm/tcg/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -20,6 +20,10 @@ #ifndef TARGET_ARM_VEC_INTERNAL_H #define TARGET_ARM_VEC_INTERNAL_H +#include "fpu/softfloat.h" + +typedef struct CPUArchState CPUARMState; + /* * Note that vector data is stored in host-endian 64-bit chunks, * so addressing units smaller than that needs a host-endian fixup. @@ -219,28 +223,83 @@ 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, specialized + * for the FPCR.EBF == 0 case. + */ +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. + * Corresponds to the ARM pseudocode function BFDotAdd, specialized + * for the FPCR.EBF == 1 case. */ -float32 bfdotadd(float32 sum, uint32_t e1, uint32_t e2); +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); + +/* + * Negate as for FPCR.AH=1 -- do not negate NaNs. + */ +static inline float16 float16_ah_chs(float16 a) +{ + return float16_is_any_nan(a) ? a : float16_chs(a); +} + +static inline float32 float32_ah_chs(float32 a) +{ + return float32_is_any_nan(a) ? a : float32_chs(a); +} + +static inline float64 float64_ah_chs(float64 a) +{ + return float64_is_any_nan(a) ? a : float64_chs(a); +} + +static inline float16 float16_maybe_ah_chs(float16 a, bool fpcr_ah) +{ + return fpcr_ah && float16_is_any_nan(a) ? a : float16_chs(a); +} + +static inline float32 float32_maybe_ah_chs(float32 a, bool fpcr_ah) +{ + return fpcr_ah && float32_is_any_nan(a) ? a : float32_chs(a); +} + +static inline float64 float64_maybe_ah_chs(float64 a, bool fpcr_ah) +{ + return fpcr_ah && float64_is_any_nan(a) ? a : float64_chs(a); +} #endif /* TARGET_ARM_VEC_INTERNAL_H */ diff --git a/target/arm/tcg/vfp.decode b/target/arm/tcg/vfp.decode index 5405e80197b..2dd87a27089 100644 --- a/target/arm/tcg/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/tcg/vfp_helper.c b/target/arm/tcg/vfp_helper.c new file mode 100644 index 00000000000..b1324c5c0a6 --- /dev/null +++ b/target/arm/tcg/vfp_helper.c @@ -0,0 +1,1370 @@ +/* + * ARM VFP floating-point operations + * + * 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 "cpu.h" +#include "internals.h" +#include "cpu-features.h" +#include "fpu/softfloat.h" +#include "qemu/log.h" + +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" + +/* + * 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) + * * 3-input NaN propagation prefers SNaN over QNaN, and then + * operand C over A over B (see FPProcessNaNs3() pseudocode, + * but note that for QEMU muladd is a * b + c, whereas for + * the pseudocode function the arguments are in the order c, a, b. + * * 0 * Inf + NaN returns the default NaN if the input NaN is quiet, + * and the input NaN if it is signalling + * * Default NaN has sign bit clear, msb frac bit set + */ +void arm_set_default_fp_behaviours(float_status *s) +{ + set_float_detect_tininess(float_tininess_before_rounding, s); + set_float_ftz_detection(float_ftz_before_rounding, s); + set_float_2nan_prop_rule(float_2nan_prop_s_ab, s); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, s); + set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, s); + set_float_default_nan_pattern(0b01000000, s); +} + +/* + * Set the float_status behaviour to match the FEAT_AFP + * FPCR.AH=1 requirements: + * * tininess-after-rounding + * * 2-input NaN propagation prefers the first NaN + * * 3-input NaN propagation prefers a over b over c + * * 0 * Inf + NaN always returns the input NaN and doesn't + * set Invalid for a QNaN + * * default NaN has sign bit set, msb frac bit set + */ +void arm_set_ah_fp_behaviours(float_status *s) +{ + set_float_detect_tininess(float_tininess_after_rounding, s); + set_float_ftz_detection(float_ftz_after_rounding, s); + set_float_2nan_prop_rule(float_2nan_prop_ab, s); + set_float_3nan_prop_rule(float_3nan_prop_abc, s); + set_float_infzeronan_rule(float_infzeronan_dnan_never | + float_infzeronan_suppress_invalid, s); + set_float_default_nan_pattern(0b11000000, s); +} + +/* Convert host exception flags to vfp form. */ +static inline uint32_t vfp_exceptbits_from_host(int host_bits, bool ah) +{ + uint32_t target_bits = 0; + + if (host_bits & float_flag_invalid) { + target_bits |= FPSR_IOC; + } + if (host_bits & float_flag_divbyzero) { + target_bits |= FPSR_DZC; + } + if (host_bits & float_flag_overflow) { + target_bits |= FPSR_OFC; + } + if (host_bits & (float_flag_underflow | float_flag_output_denormal_flushed)) { + target_bits |= FPSR_UFC; + } + if (host_bits & float_flag_inexact) { + target_bits |= FPSR_IXC; + } + if (host_bits & float_flag_input_denormal_flushed) { + target_bits |= FPSR_IDC; + } + /* + * With FPCR.AH, IDC is set when an input denormal is used, + * and flushing an output denormal to zero sets both IXC and UFC. + */ + if (ah && (host_bits & float_flag_input_denormal_used)) { + target_bits |= FPSR_IDC; + } + if (ah && (host_bits & float_flag_output_denormal_flushed)) { + target_bits |= FPSR_IXC; + } + return target_bits; +} + +uint32_t vfp_get_fpsr_from_host(CPUARMState *env) +{ + uint32_t a32_flags = 0, a64_flags = 0; + + a32_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_A32]); + a32_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_STD]); + /* FZ16 does not generate an input denormal exception. */ + a32_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_A32_F16]) + & ~float_flag_input_denormal_flushed); + a32_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_STD_F16]) + & ~float_flag_input_denormal_flushed); + + a64_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_A64]); + a64_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_A64_F16]) + & ~(float_flag_input_denormal_flushed | float_flag_input_denormal_used)); + /* + * We do not merge in flags from FPST_AH or FPST_AH_F16, because + * they are used for insns that must not set the cumulative exception bits. + */ + + /* + * Flushing an input denormal *only* because FPCR.FIZ == 1 does + * not set FPSR.IDC; if FPCR.FZ is also set then this takes + * precedence and IDC is set (see the FPUnpackBase pseudocode). + * So squash it unless (FPCR.AH == 0 && FPCR.FZ == 1). + * We only do this for the a64 flags because FIZ has no effect + * on AArch32 even if it is set. + */ + if ((env->vfp.fpcr & (FPCR_FZ | FPCR_AH)) != FPCR_FZ) { + a64_flags &= ~float_flag_input_denormal_flushed; + } + return vfp_exceptbits_from_host(a64_flags, env->vfp.fpcr & FPCR_AH) | + vfp_exceptbits_from_host(a32_flags, false); +} + +void vfp_clear_float_status_exc_flags(CPUARMState *env) +{ + /* + * 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[FPST_A32]); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_A64]); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_A32_F16]); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_A64_F16]); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD]); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD_F16]); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_AH]); + set_float_exception_flags(0, &env->vfp.fp_status[FPST_AH_F16]); +} + +static void vfp_sync_and_clear_float_status_exc_flags(CPUARMState *env) +{ + /* + * Synchronize any pending exception-flag information in the + * float_status values into env->vfp.fpsr, and then clear out + * the float_status data. + */ + env->vfp.fpsr |= vfp_get_fpsr_from_host(env); + vfp_clear_float_status_exc_flags(env); +} + +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)) { + int i = (val >> 22) & 3; + switch (i) { + case FPROUNDING_TIEEVEN: + i = float_round_nearest_even; + break; + case FPROUNDING_POSINF: + i = float_round_up; + break; + case FPROUNDING_NEGINF: + i = float_round_down; + break; + case FPROUNDING_ZERO: + i = float_round_to_zero; + break; + } + set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A32]); + set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64]); + set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A32_F16]); + set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64_F16]); + } + if (changed & FPCR_FZ16) { + bool ftz_enabled = val & FPCR_FZ16; + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32_F16]); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64_F16]); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32_F16]); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64_F16]); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]); + } + if (changed & FPCR_FZ) { + bool ftz_enabled = val & FPCR_FZ; + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32]); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64]); + /* FIZ is A64 only so FZ always makes A32 code flush inputs to zero */ + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32]); + } + if (changed & (FPCR_FZ | FPCR_AH | FPCR_FIZ)) { + /* + * A64: Flush denormalized inputs to zero if FPCR.FIZ = 1, or + * both FPCR.AH = 0 and FPCR.FZ = 1. + */ + bool fitz_enabled = (val & FPCR_FIZ) || + (val & (FPCR_FZ | FPCR_AH)) == FPCR_FZ; + set_flush_inputs_to_zero(fitz_enabled, &env->vfp.fp_status[FPST_A64]); + } + if (changed & FPCR_DN) { + bool dnan_enabled = val & FPCR_DN; + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A32]); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A64]); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A32_F16]); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A64_F16]); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH]); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH_F16]); + } + if (changed & FPCR_AH) { + bool ah_enabled = val & FPCR_AH; + + if (ah_enabled) { + /* Change behaviours for A64 FP operations */ + arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_A64]); + arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); + } else { + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64]); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); + } + } + /* + * If any bits changed that we look at in vfp_get_fpsr_from_host(), + * we must sync the float_status flags into vfp.fpsr now (under the + * old regime) before we update vfp.fpcr. + */ + if (changed & (FPCR_FZ | FPCR_AH | FPCR_FIZ)) { + vfp_sync_and_clear_float_status_exc_flags(env); + } +} + +/* + * VFP support. We follow the convention used for VFP instructions: + * Single precision routines have a "s" suffix, double precision a + * "d" suffix. + */ + +#define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p)) + +#define VFP_BINOP(name) \ +dh_ctype_f16 VFP_HELPER(name, h)(dh_ctype_f16 a, dh_ctype_f16 b, float_status *fpst) \ +{ \ + return float16_ ## name(a, b, fpst); \ +} \ +float32 VFP_HELPER(name, s)(float32 a, float32 b, float_status *fpst) \ +{ \ + return float32_ ## name(a, b, fpst); \ +} \ +float64 VFP_HELPER(name, d)(float64 a, float64 b, float_status *fpst) \ +{ \ + return float64_ ## name(a, b, fpst); \ +} +VFP_BINOP(add) +VFP_BINOP(sub) +VFP_BINOP(mul) +VFP_BINOP(div) +VFP_BINOP(min) +VFP_BINOP(max) +VFP_BINOP(minnum) +VFP_BINOP(maxnum) +#undef VFP_BINOP + +dh_ctype_f16 VFP_HELPER(sqrt, h)(dh_ctype_f16 a, float_status *fpst) +{ + return float16_sqrt(a, fpst); +} + +float32 VFP_HELPER(sqrt, s)(float32 a, float_status *fpst) +{ + return float32_sqrt(a, fpst); +} + +float64 VFP_HELPER(sqrt, d)(float64 a, float_status *fpst) +{ + return float64_sqrt(a, fpst); +} + +static void softfloat_to_vfp_compare(CPUARMState *env, FloatRelation cmp) +{ + uint32_t flags; + switch (cmp) { + case float_relation_equal: + flags = 0x6; + break; + case float_relation_less: + flags = 0x8; + break; + case float_relation_greater: + flags = 0x2; + break; + case float_relation_unordered: + flags = 0x3; + break; + default: + g_assert_not_reached(); + } + env->vfp.fpsr = deposit64(env->vfp.fpsr, 28, 4, flags); /* NZCV */ +} + +/* XXX: check quiet/signaling case */ +#define DO_VFP_cmp(P, FLOATTYPE, ARGTYPE, FPST) \ +void VFP_HELPER(cmp, P)(ARGTYPE a, ARGTYPE b, CPUARMState *env) \ +{ \ + softfloat_to_vfp_compare(env, \ + FLOATTYPE ## _compare_quiet(a, b, &env->vfp.fp_status[FPST])); \ +} \ +void VFP_HELPER(cmpe, P)(ARGTYPE a, ARGTYPE b, CPUARMState *env) \ +{ \ + softfloat_to_vfp_compare(env, \ + FLOATTYPE ## _compare(a, b, &env->vfp.fp_status[FPST])); \ +} +DO_VFP_cmp(h, float16, dh_ctype_f16, FPST_A32_F16) +DO_VFP_cmp(s, float32, float32, FPST_A32) +DO_VFP_cmp(d, float64, float64, FPST_A32) +#undef DO_VFP_cmp + +/* Integer to float and float to integer conversions */ + +#define CONV_ITOF(name, ftype, fsz, sign) \ +ftype HELPER(name)(uint32_t x, float_status *fpst) \ +{ \ + return sign##int32_to_##float##fsz((sign##int32_t)x, fpst); \ +} + +#define CONV_FTOI(name, ftype, fsz, sign, round) \ +sign##int32_t HELPER(name)(ftype x, float_status *fpst) \ +{ \ + if (float##fsz##_is_any_nan(x)) { \ + float_raise(float_flag_invalid, fpst); \ + return 0; \ + } \ + return float##fsz##_to_##sign##int32##round(x, fpst); \ +} + +#define FLOAT_CONVS(name, p, ftype, fsz, sign) \ + CONV_ITOF(vfp_##name##to##p, ftype, fsz, sign) \ + CONV_FTOI(vfp_to##name##p, ftype, fsz, sign, ) \ + CONV_FTOI(vfp_to##name##z##p, ftype, fsz, sign, _round_to_zero) + +FLOAT_CONVS(si, h, uint32_t, 16, ) +FLOAT_CONVS(si, s, float32, 32, ) +FLOAT_CONVS(si, d, float64, 64, ) +FLOAT_CONVS(ui, h, uint32_t, 16, u) +FLOAT_CONVS(ui, s, float32, 32, u) +FLOAT_CONVS(ui, d, float64, 64, u) + +#undef CONV_ITOF +#undef CONV_FTOI +#undef FLOAT_CONVS + +/* floating point conversion */ +float64 VFP_HELPER(fcvtd, s)(float32 x, float_status *status) +{ + return float32_to_float64(x, status); +} + +float32 VFP_HELPER(fcvts, d)(float64 x, float_status *status) +{ + return float64_to_float32(x, status); +} + +uint32_t HELPER(bfcvt)(float32 x, float_status *status) +{ + return float32_to_bfloat16(x, status); +} + +uint32_t HELPER(bfcvt_pair)(uint64_t pair, float_status *status) +{ + bfloat16 lo = float32_to_bfloat16(extract64(pair, 0, 32), status); + bfloat16 hi = float32_to_bfloat16(extract64(pair, 32, 32), status); + return deposit32(lo, 16, 16, hi); +} + +/* + * VFP3 fixed point conversion. The AArch32 versions of fix-to-float + * must always round-to-nearest; the AArch64 ones honour the FPSCR + * rounding mode. (For AArch32 Neon the standard-FPSCR is set to + * round-to-nearest so either helper will work.) AArch32 float-to-fix + * must round-to-zero. + */ +#define VFP_CONV_FIX_FLOAT(name, p, fsz, ftype, isz, itype) \ +ftype HELPER(vfp_##name##to##p)(uint##isz##_t x, uint32_t shift, \ + float_status *fpst) \ +{ return itype##_to_##float##fsz##_scalbn(x, -shift, fpst); } + +#define VFP_CONV_FIX_FLOAT_ROUND(name, p, fsz, ftype, isz, itype) \ + ftype HELPER(vfp_##name##to##p##_round_to_nearest)(uint##isz##_t x, \ + uint32_t shift, \ + float_status *fpst) \ + { \ + ftype ret; \ + FloatRoundMode oldmode = fpst->float_rounding_mode; \ + fpst->float_rounding_mode = float_round_nearest_even; \ + ret = itype##_to_##float##fsz##_scalbn(x, -shift, fpst); \ + fpst->float_rounding_mode = oldmode; \ + return ret; \ + } + +#define VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, ROUND, suff) \ +uint##isz##_t HELPER(vfp_to##name##p##suff)(ftype x, uint32_t shift, \ + float_status *fpst) \ +{ \ + if (unlikely(float##fsz##_is_any_nan(x))) { \ + float_raise(float_flag_invalid, fpst); \ + return 0; \ + } \ + return float##fsz##_to_##itype##_scalbn(x, ROUND, shift, fpst); \ +} + +#define VFP_CONV_FIX(name, p, fsz, ftype, isz, itype) \ +VFP_CONV_FIX_FLOAT(name, p, fsz, ftype, isz, itype) \ +VFP_CONV_FIX_FLOAT_ROUND(name, p, fsz, ftype, isz, itype) \ +VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, \ + float_round_to_zero, _round_to_zero) \ +VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, \ + get_float_rounding_mode(fpst), ) + +#define VFP_CONV_FIX_A64(name, p, fsz, ftype, isz, itype) \ +VFP_CONV_FIX_FLOAT(name, p, fsz, ftype, isz, itype) \ +VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, \ + get_float_rounding_mode(fpst), ) + +VFP_CONV_FIX(sh, d, 64, float64, 64, int16) +VFP_CONV_FIX(sl, d, 64, float64, 64, int32) +VFP_CONV_FIX_A64(sq, d, 64, float64, 64, int64) +VFP_CONV_FIX(uh, d, 64, float64, 64, uint16) +VFP_CONV_FIX(ul, d, 64, float64, 64, uint32) +VFP_CONV_FIX_A64(uq, d, 64, float64, 64, uint64) +VFP_CONV_FIX(sh, s, 32, float32, 32, int16) +VFP_CONV_FIX(sl, s, 32, float32, 32, int32) +VFP_CONV_FIX_A64(sq, s, 32, float32, 64, int64) +VFP_CONV_FIX(uh, s, 32, float32, 32, uint16) +VFP_CONV_FIX(ul, s, 32, float32, 32, uint32) +VFP_CONV_FIX_A64(uq, s, 32, float32, 64, uint64) +VFP_CONV_FIX(sh, h, 16, dh_ctype_f16, 32, int16) +VFP_CONV_FIX(sl, h, 16, dh_ctype_f16, 32, int32) +VFP_CONV_FIX_A64(sq, h, 16, dh_ctype_f16, 64, int64) +VFP_CONV_FIX(uh, h, 16, dh_ctype_f16, 32, uint16) +VFP_CONV_FIX(ul, h, 16, dh_ctype_f16, 32, uint32) +VFP_CONV_FIX_A64(uq, h, 16, dh_ctype_f16, 64, uint64) +VFP_CONV_FLOAT_FIX_ROUND(sq, d, 64, float64, 64, int64, + float_round_to_zero, _round_to_zero) +VFP_CONV_FLOAT_FIX_ROUND(uq, d, 64, float64, 64, uint64, + float_round_to_zero, _round_to_zero) + +#undef VFP_CONV_FIX +#undef VFP_CONV_FIX_FLOAT +#undef VFP_CONV_FLOAT_FIX_ROUND +#undef VFP_CONV_FIX_A64 + +/* Set the current fp rounding mode and return the old one. + * The argument is a softfloat float_round_ value. + */ +uint32_t HELPER(set_rmode)(uint32_t rmode, float_status *fp_status) +{ + uint32_t prev_rmode = get_float_rounding_mode(fp_status); + set_float_rounding_mode(rmode, fp_status); + + return prev_rmode; +} + +/* Half precision conversions. */ +float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, float_status *fpst, + uint32_t ahp_mode) +{ + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing input denormals. + */ + bool save = get_flush_inputs_to_zero(fpst); + set_flush_inputs_to_zero(false, fpst); + float32 r = float16_to_float32(a, !ahp_mode, fpst); + set_flush_inputs_to_zero(save, fpst); + return r; +} + +uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, float_status *fpst, + uint32_t ahp_mode) +{ + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing output denormals. + */ + bool save = get_flush_to_zero(fpst); + set_flush_to_zero(false, fpst); + float16 r = float32_to_float16(a, !ahp_mode, fpst); + set_flush_to_zero(save, fpst); + return r; +} + +float64 HELPER(vfp_fcvt_f16_to_f64)(uint32_t a, float_status *fpst, + uint32_t ahp_mode) +{ + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing input denormals. + */ + bool save = get_flush_inputs_to_zero(fpst); + set_flush_inputs_to_zero(false, fpst); + float64 r = float16_to_float64(a, !ahp_mode, fpst); + set_flush_inputs_to_zero(save, fpst); + return r; +} + +uint32_t HELPER(vfp_fcvt_f64_to_f16)(float64 a, float_status *fpst, + uint32_t ahp_mode) +{ + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing output denormals. + */ + bool save = get_flush_to_zero(fpst); + set_flush_to_zero(false, fpst); + float16 r = float64_to_float16(a, !ahp_mode, fpst); + set_flush_to_zero(save, fpst); + return r; +} + +/* NEON helpers. */ + +/* Constants 256 and 512 are used in some helpers; we avoid relying on + * int->float conversions at run-time. */ +#define float64_256 make_float64(0x4070000000000000LL) +#define float64_512 make_float64(0x4080000000000000LL) +#define float16_maxnorm make_float16(0x7bff) +#define float32_maxnorm make_float32(0x7f7fffff) +#define float64_maxnorm make_float64(0x7fefffffffffffffLL) + +/* Reciprocal functions + * + * The algorithm that must be used to calculate the estimate + * is specified by the ARM ARM, see FPRecipEstimate()/RecipEstimate + */ + +/* See RecipEstimate() + * + * input is a 9 bit fixed point number + * input range 256 .. 511 for a number from 0.5 <= x < 1.0. + * result range 256 .. 511 for a number from 1.0 to 511/256. + */ + +static int recip_estimate(int input) +{ + int a, b, r; + assert(256 <= input && input < 512); + a = (input * 2) + 1; + b = (1 << 19) / a; + r = (b + 1) >> 1; + assert(256 <= r && r < 512); + return r; +} + +/* + * Increased precision version: + * input is a 13 bit fixed point number + * input range 2048 .. 4095 for a number from 0.5 <= x < 1.0. + * result range 4096 .. 8191 for a number from 1.0 to 2.0 + */ +static int recip_estimate_incprec(int input) +{ + int a, b, r; + assert(2048 <= input && input < 4096); + a = (input * 2) + 1; + /* + * The pseudocode expresses this as an operation on infinite + * precision reals where it calculates 2^25 / a and then looks + * at the error between that and the rounded-down-to-integer + * value to see if it should instead round up. We instead + * follow the same approach as the pseudocode for the 8-bit + * precision version, and calculate (2 * (2^25 / a)) as an + * integer so we can do the "add one and halve" to round it. + * So the 1 << 26 here is correct. + */ + b = (1 << 26) / a; + r = (b + 1) >> 1; + assert(4096 <= r && r < 8192); + return r; +} + +/* + * Common wrapper to call recip_estimate + * + * The parameters are exponent and 64 bit fraction (without implicit + * bit) where the binary point is nominally at bit 52. Returns a + * float64 which can then be rounded to the appropriate size by the + * callee. + */ + +static uint64_t call_recip_estimate(int *exp, int exp_off, uint64_t frac, + bool increasedprecision) +{ + uint32_t scaled, estimate; + uint64_t result_frac; + int result_exp; + + /* Handle sub-normals */ + if (*exp == 0) { + if (extract64(frac, 51, 1) == 0) { + *exp = -1; + frac <<= 2; + } else { + frac <<= 1; + } + } + + if (increasedprecision) { + /* scaled = UInt('1':fraction<51:41>) */ + scaled = deposit32(1 << 11, 0, 11, extract64(frac, 41, 11)); + estimate = recip_estimate_incprec(scaled); + } else { + /* scaled = UInt('1':fraction<51:44>) */ + scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); + estimate = recip_estimate(scaled); + } + + result_exp = exp_off - *exp; + if (increasedprecision) { + result_frac = deposit64(0, 40, 12, estimate); + } else { + result_frac = deposit64(0, 44, 8, estimate); + } + if (result_exp == 0) { + result_frac = deposit64(result_frac >> 1, 51, 1, 1); + } else if (result_exp == -1) { + result_frac = deposit64(result_frac >> 2, 50, 2, 1); + result_exp = 0; + } + + *exp = result_exp; + + return result_frac; +} + +static bool round_to_inf(float_status *fpst, bool sign_bit) +{ + switch (fpst->float_rounding_mode) { + case float_round_nearest_even: /* Round to Nearest */ + return true; + case float_round_up: /* Round to +Inf */ + return !sign_bit; + case float_round_down: /* Round to -Inf */ + return sign_bit; + case float_round_to_zero: /* Round to Zero */ + return false; + default: + g_assert_not_reached(); + } +} + +uint32_t HELPER(recpe_f16)(uint32_t input, float_status *fpst) +{ + float16 f16 = float16_squash_input_denormal(input, fpst); + uint32_t f16_val = float16_val(f16); + uint32_t f16_sign = float16_is_neg(f16); + int f16_exp = extract32(f16_val, 10, 5); + uint32_t f16_frac = extract32(f16_val, 0, 10); + uint64_t f64_frac; + + if (float16_is_any_nan(f16)) { + float16 nan = f16; + if (float16_is_signaling_nan(f16, fpst)) { + float_raise(float_flag_invalid, fpst); + if (!fpst->default_nan_mode) { + nan = float16_silence_nan(f16, fpst); + } + } + if (fpst->default_nan_mode) { + nan = float16_default_nan(fpst); + } + return nan; + } else if (float16_is_infinity(f16)) { + return float16_set_sign(float16_zero, float16_is_neg(f16)); + } else if (float16_is_zero(f16)) { + float_raise(float_flag_divbyzero, fpst); + return float16_set_sign(float16_infinity, float16_is_neg(f16)); + } else if (float16_abs(f16) < (1 << 8)) { + /* Abs(value) < 2.0^-16 */ + float_raise(float_flag_overflow | float_flag_inexact, fpst); + if (round_to_inf(fpst, f16_sign)) { + return float16_set_sign(float16_infinity, f16_sign); + } else { + return float16_set_sign(float16_maxnorm, f16_sign); + } + } else if (f16_exp >= 29 && fpst->flush_to_zero) { + float_raise(float_flag_underflow, fpst); + return float16_set_sign(float16_zero, float16_is_neg(f16)); + } + + f64_frac = call_recip_estimate(&f16_exp, 29, + ((uint64_t) f16_frac) << (52 - 10), false); + + /* result = sign : result_exp<4:0> : fraction<51:42> */ + f16_val = deposit32(0, 15, 1, f16_sign); + f16_val = deposit32(f16_val, 10, 5, f16_exp); + f16_val = deposit32(f16_val, 0, 10, extract64(f64_frac, 52 - 10, 10)); + return make_float16(f16_val); +} + +/* + * FEAT_RPRES means the f32 FRECPE has an "increased precision" variant + * which is used when FPCR.AH == 1. + */ +static float32 do_recpe_f32(float32 input, float_status *fpst, bool rpres) +{ + float32 f32 = float32_squash_input_denormal(input, fpst); + uint32_t f32_val = float32_val(f32); + bool f32_sign = float32_is_neg(f32); + int f32_exp = extract32(f32_val, 23, 8); + uint32_t f32_frac = extract32(f32_val, 0, 23); + uint64_t f64_frac; + + if (float32_is_any_nan(f32)) { + float32 nan = f32; + if (float32_is_signaling_nan(f32, fpst)) { + float_raise(float_flag_invalid, fpst); + if (!fpst->default_nan_mode) { + nan = float32_silence_nan(f32, fpst); + } + } + if (fpst->default_nan_mode) { + nan = float32_default_nan(fpst); + } + return nan; + } else if (float32_is_infinity(f32)) { + return float32_set_sign(float32_zero, float32_is_neg(f32)); + } else if (float32_is_zero(f32)) { + float_raise(float_flag_divbyzero, fpst); + return float32_set_sign(float32_infinity, float32_is_neg(f32)); + } else if (float32_abs(f32) < (1ULL << 21)) { + /* Abs(value) < 2.0^-128 */ + float_raise(float_flag_overflow | float_flag_inexact, fpst); + if (round_to_inf(fpst, f32_sign)) { + return float32_set_sign(float32_infinity, f32_sign); + } else { + return float32_set_sign(float32_maxnorm, f32_sign); + } + } else if (f32_exp >= 253 && fpst->flush_to_zero) { + float_raise(float_flag_underflow, fpst); + return float32_set_sign(float32_zero, float32_is_neg(f32)); + } + + f64_frac = call_recip_estimate(&f32_exp, 253, + ((uint64_t) f32_frac) << (52 - 23), rpres); + + /* result = sign : result_exp<7:0> : fraction<51:29> */ + f32_val = deposit32(0, 31, 1, f32_sign); + f32_val = deposit32(f32_val, 23, 8, f32_exp); + f32_val = deposit32(f32_val, 0, 23, extract64(f64_frac, 52 - 23, 23)); + return make_float32(f32_val); +} + +float32 HELPER(recpe_f32)(float32 input, float_status *fpst) +{ + return do_recpe_f32(input, fpst, false); +} + +float32 HELPER(recpe_rpres_f32)(float32 input, float_status *fpst) +{ + return do_recpe_f32(input, fpst, true); +} + +float64 HELPER(recpe_f64)(float64 input, float_status *fpst) +{ + float64 f64 = float64_squash_input_denormal(input, fpst); + uint64_t f64_val = float64_val(f64); + bool f64_sign = float64_is_neg(f64); + int f64_exp = extract64(f64_val, 52, 11); + uint64_t f64_frac = extract64(f64_val, 0, 52); + + /* Deal with any special cases */ + if (float64_is_any_nan(f64)) { + float64 nan = f64; + if (float64_is_signaling_nan(f64, fpst)) { + float_raise(float_flag_invalid, fpst); + if (!fpst->default_nan_mode) { + nan = float64_silence_nan(f64, fpst); + } + } + if (fpst->default_nan_mode) { + nan = float64_default_nan(fpst); + } + return nan; + } else if (float64_is_infinity(f64)) { + return float64_set_sign(float64_zero, float64_is_neg(f64)); + } else if (float64_is_zero(f64)) { + float_raise(float_flag_divbyzero, fpst); + return float64_set_sign(float64_infinity, float64_is_neg(f64)); + } else if ((f64_val & ~(1ULL << 63)) < (1ULL << 50)) { + /* Abs(value) < 2.0^-1024 */ + float_raise(float_flag_overflow | float_flag_inexact, fpst); + if (round_to_inf(fpst, f64_sign)) { + return float64_set_sign(float64_infinity, f64_sign); + } else { + return float64_set_sign(float64_maxnorm, f64_sign); + } + } else if (f64_exp >= 2045 && fpst->flush_to_zero) { + float_raise(float_flag_underflow, fpst); + return float64_set_sign(float64_zero, float64_is_neg(f64)); + } + + f64_frac = call_recip_estimate(&f64_exp, 2045, f64_frac, false); + + /* result = sign : result_exp<10:0> : fraction<51:0>; */ + f64_val = deposit64(0, 63, 1, f64_sign); + f64_val = deposit64(f64_val, 52, 11, f64_exp); + f64_val = deposit64(f64_val, 0, 52, f64_frac); + return make_float64(f64_val); +} + +/* The algorithm that must be used to calculate the estimate + * is specified by the ARM ARM. + */ + +static int do_recip_sqrt_estimate(int a) +{ + int b, estimate; + + assert(128 <= a && a < 512); + if (a < 256) { + a = a * 2 + 1; + } else { + a = (a >> 1) << 1; + a = (a + 1) * 2; + } + b = 512; + while (a * (b + 1) * (b + 1) < (1 << 28)) { + b += 1; + } + estimate = (b + 1) / 2; + assert(256 <= estimate && estimate < 512); + + return estimate; +} + +static int do_recip_sqrt_estimate_incprec(int a) +{ + /* + * The Arm ARM describes the 12-bit precision version of RecipSqrtEstimate + * in terms of an infinite-precision floating point calculation of a + * square root. We implement this using the same kind of pure integer + * algorithm as the 8-bit mantissa, to get the same bit-for-bit result. + */ + int64_t b, estimate; + + assert(1024 <= a && a < 4096); + if (a < 2048) { + a = a * 2 + 1; + } else { + a = (a >> 1) << 1; + a = (a + 1) * 2; + } + b = 8192; + while (a * (b + 1) * (b + 1) < (1ULL << 39)) { + b += 1; + } + estimate = (b + 1) / 2; + + assert(4096 <= estimate && estimate < 8192); + + return estimate; +} + +static uint64_t recip_sqrt_estimate(int *exp , int exp_off, uint64_t frac, + bool increasedprecision) +{ + int estimate; + uint32_t scaled; + + if (*exp == 0) { + while (extract64(frac, 51, 1) == 0) { + frac = frac << 1; + *exp -= 1; + } + frac = extract64(frac, 0, 51) << 1; + } + + if (increasedprecision) { + if (*exp & 1) { + /* scaled = UInt('01':fraction<51:42>) */ + scaled = deposit32(1 << 10, 0, 10, extract64(frac, 42, 10)); + } else { + /* scaled = UInt('1':fraction<51:41>) */ + scaled = deposit32(1 << 11, 0, 11, extract64(frac, 41, 11)); + } + estimate = do_recip_sqrt_estimate_incprec(scaled); + } else { + if (*exp & 1) { + /* scaled = UInt('01':fraction<51:45>) */ + scaled = deposit32(1 << 7, 0, 7, extract64(frac, 45, 7)); + } else { + /* scaled = UInt('1':fraction<51:44>) */ + scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); + } + estimate = do_recip_sqrt_estimate(scaled); + } + + *exp = (exp_off - *exp) / 2; + if (increasedprecision) { + return extract64(estimate, 0, 12) << 40; + } else { + return extract64(estimate, 0, 8) << 44; + } +} + +uint32_t HELPER(rsqrte_f16)(uint32_t input, float_status *s) +{ + float16 f16 = float16_squash_input_denormal(input, s); + uint16_t val = float16_val(f16); + bool f16_sign = float16_is_neg(f16); + int f16_exp = extract32(val, 10, 5); + uint16_t f16_frac = extract32(val, 0, 10); + uint64_t f64_frac; + + if (float16_is_any_nan(f16)) { + float16 nan = f16; + if (float16_is_signaling_nan(f16, s)) { + float_raise(float_flag_invalid, s); + if (!s->default_nan_mode) { + nan = float16_silence_nan(f16, s); + } + } + if (s->default_nan_mode) { + nan = float16_default_nan(s); + } + return nan; + } else if (float16_is_zero(f16)) { + float_raise(float_flag_divbyzero, s); + return float16_set_sign(float16_infinity, f16_sign); + } else if (f16_sign) { + float_raise(float_flag_invalid, s); + return float16_default_nan(s); + } else if (float16_is_infinity(f16)) { + return float16_zero; + } + + /* Scale and normalize to a double-precision value between 0.25 and 1.0, + * preserving the parity of the exponent. */ + + f64_frac = ((uint64_t) f16_frac) << (52 - 10); + + f64_frac = recip_sqrt_estimate(&f16_exp, 44, f64_frac, false); + + /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(2) */ + val = deposit32(0, 15, 1, f16_sign); + val = deposit32(val, 10, 5, f16_exp); + val = deposit32(val, 2, 8, extract64(f64_frac, 52 - 8, 8)); + return make_float16(val); +} + +/* + * FEAT_RPRES means the f32 FRSQRTE has an "increased precision" variant + * which is used when FPCR.AH == 1. + */ +static float32 do_rsqrte_f32(float32 input, float_status *s, bool rpres) +{ + float32 f32 = float32_squash_input_denormal(input, s); + uint32_t val = float32_val(f32); + uint32_t f32_sign = float32_is_neg(f32); + int f32_exp = extract32(val, 23, 8); + uint32_t f32_frac = extract32(val, 0, 23); + uint64_t f64_frac; + + if (float32_is_any_nan(f32)) { + float32 nan = f32; + if (float32_is_signaling_nan(f32, s)) { + float_raise(float_flag_invalid, s); + if (!s->default_nan_mode) { + nan = float32_silence_nan(f32, s); + } + } + if (s->default_nan_mode) { + nan = float32_default_nan(s); + } + return nan; + } else if (float32_is_zero(f32)) { + float_raise(float_flag_divbyzero, s); + return float32_set_sign(float32_infinity, float32_is_neg(f32)); + } else if (float32_is_neg(f32)) { + float_raise(float_flag_invalid, s); + return float32_default_nan(s); + } else if (float32_is_infinity(f32)) { + return float32_zero; + } + + /* Scale and normalize to a double-precision value between 0.25 and 1.0, + * preserving the parity of the exponent. */ + + f64_frac = ((uint64_t) f32_frac) << 29; + + f64_frac = recip_sqrt_estimate(&f32_exp, 380, f64_frac, rpres); + + /* + * result = sign : result_exp<7:0> : estimate<7:0> : Zeros(15) + * or for increased precision + * result = sign : result_exp<7:0> : estimate<11:0> : Zeros(11) + */ + val = deposit32(0, 31, 1, f32_sign); + val = deposit32(val, 23, 8, f32_exp); + if (rpres) { + val = deposit32(val, 11, 12, extract64(f64_frac, 52 - 12, 12)); + } else { + val = deposit32(val, 15, 8, extract64(f64_frac, 52 - 8, 8)); + } + return make_float32(val); +} + +float32 HELPER(rsqrte_f32)(float32 input, float_status *s) +{ + return do_rsqrte_f32(input, s, false); +} + +float32 HELPER(rsqrte_rpres_f32)(float32 input, float_status *s) +{ + return do_rsqrte_f32(input, s, true); +} + +float64 HELPER(rsqrte_f64)(float64 input, float_status *s) +{ + float64 f64 = float64_squash_input_denormal(input, s); + uint64_t val = float64_val(f64); + bool f64_sign = float64_is_neg(f64); + int f64_exp = extract64(val, 52, 11); + uint64_t f64_frac = extract64(val, 0, 52); + + if (float64_is_any_nan(f64)) { + float64 nan = f64; + if (float64_is_signaling_nan(f64, s)) { + float_raise(float_flag_invalid, s); + if (!s->default_nan_mode) { + nan = float64_silence_nan(f64, s); + } + } + if (s->default_nan_mode) { + nan = float64_default_nan(s); + } + return nan; + } else if (float64_is_zero(f64)) { + float_raise(float_flag_divbyzero, s); + return float64_set_sign(float64_infinity, float64_is_neg(f64)); + } else if (float64_is_neg(f64)) { + float_raise(float_flag_invalid, s); + return float64_default_nan(s); + } else if (float64_is_infinity(f64)) { + return float64_zero; + } + + f64_frac = recip_sqrt_estimate(&f64_exp, 3068, f64_frac, false); + + /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(44) */ + val = deposit64(0, 61, 1, f64_sign); + val = deposit64(val, 52, 11, f64_exp); + val = deposit64(val, 44, 8, extract64(f64_frac, 52 - 8, 8)); + return make_float64(val); +} + +uint32_t HELPER(recpe_u32)(uint32_t a) +{ + int input, estimate; + + if ((a & 0x80000000) == 0) { + return 0xffffffff; + } + + input = extract32(a, 23, 9); + estimate = recip_estimate(input); + + return deposit32(0, (32 - 9), 9, estimate); +} + +uint32_t HELPER(rsqrte_u32)(uint32_t a) +{ + int estimate; + + if ((a & 0xc0000000) == 0) { + return 0xffffffff; + } + + estimate = do_recip_sqrt_estimate(extract32(a, 23, 9)); + + return deposit32(0, 23, 9, estimate); +} + +/* VFPv4 fused multiply-accumulate */ +dh_ctype_f16 VFP_HELPER(muladd, h)(dh_ctype_f16 a, dh_ctype_f16 b, + dh_ctype_f16 c, float_status *fpst) +{ + return float16_muladd(a, b, c, 0, fpst); +} + +float32 VFP_HELPER(muladd, s)(float32 a, float32 b, float32 c, + float_status *fpst) +{ + return float32_muladd(a, b, c, 0, fpst); +} + +float64 VFP_HELPER(muladd, d)(float64 a, float64 b, float64 c, + float_status *fpst) +{ + return float64_muladd(a, b, c, 0, fpst); +} + +/* ARMv8 round to integral */ +dh_ctype_f16 HELPER(rinth_exact)(dh_ctype_f16 x, float_status *fp_status) +{ + return float16_round_to_int(x, fp_status); +} + +float32 HELPER(rints_exact)(float32 x, float_status *fp_status) +{ + return float32_round_to_int(x, fp_status); +} + +float64 HELPER(rintd_exact)(float64 x, float_status *fp_status) +{ + return float64_round_to_int(x, fp_status); +} + +dh_ctype_f16 HELPER(rinth)(dh_ctype_f16 x, float_status *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; +} + +float32 HELPER(rints)(float32 x, float_status *fp_status) +{ + int old_flags = get_float_exception_flags(fp_status), new_flags; + float32 ret; + + ret = float32_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; +} + +float64 HELPER(rintd)(float64 x, float_status *fp_status) +{ + int old_flags = get_float_exception_flags(fp_status), new_flags; + float64 ret; + + ret = float64_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; +} + +/* Convert ARM rounding mode to softfloat */ +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; + * the result is supplied modulo 2^32. + */ +uint64_t HELPER(fjcvtzs)(float64 value, float_status *status) +{ + uint32_t frac, e_old, e_new; + bool inexact; + + 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); + + /* Normal inexact, denormal with flush-to-zero, or overflow or NaN */ + inexact = e_new & (float_flag_inexact | + float_flag_input_denormal_flushed | + float_flag_invalid); + + /* 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); +} + +uint32_t HELPER(vjcvt)(float64 value, CPUARMState *env) +{ + uint64_t pair = HELPER(fjcvtzs)(value, &env->vfp.fp_status[FPST_A32]); + uint32_t result = pair; + uint32_t z = (pair >> 32) == 0; + + /* Store Z, clear NCV, in FPSCR.NZCV. */ + env->vfp.fpsr = (env->vfp.fpsr & ~FPSR_NZCV_MASK) | (z * FPSR_Z); + + return result; +} + +/* Round a float32 to an integer that fits in int32_t or int64_t. */ +static float32 frint_s(float32 f, float_status *fpst, int intsize) +{ + int old_flags = get_float_exception_flags(fpst); + uint32_t exp = extract32(f, 23, 8); + + if (unlikely(exp == 0xff)) { + /* NaN or Inf. */ + goto overflow; + } + + /* Round and re-extract the exponent. */ + f = float32_round_to_int(f, fpst); + exp = extract32(f, 23, 8); + + /* Validate the range of the result. */ + if (exp < 126 + intsize) { + /* abs(F) <= INT{N}_MAX */ + return f; + } + if (exp == 126 + intsize) { + uint32_t sign = extract32(f, 31, 1); + uint32_t frac = extract32(f, 0, 23); + if (sign && frac == 0) { + /* F == INT{N}_MIN */ + return f; + } + } + + overflow: + /* + * Raise Invalid and return INT{N}_MIN as a float. Revert any + * inexact exception float32_round_to_int may have raised. + */ + set_float_exception_flags(old_flags | float_flag_invalid, fpst); + return (0x100u + 126u + intsize) << 23; +} + +float32 HELPER(frint32_s)(float32 f, float_status *fpst) +{ + return frint_s(f, fpst, 32); +} + +float32 HELPER(frint64_s)(float32 f, float_status *fpst) +{ + return frint_s(f, fpst, 64); +} + +/* Round a float64 to an integer that fits in int32_t or int64_t. */ +static float64 frint_d(float64 f, float_status *fpst, int intsize) +{ + int old_flags = get_float_exception_flags(fpst); + uint32_t exp = extract64(f, 52, 11); + + if (unlikely(exp == 0x7ff)) { + /* NaN or Inf. */ + goto overflow; + } + + /* Round and re-extract the exponent. */ + f = float64_round_to_int(f, fpst); + exp = extract64(f, 52, 11); + + /* Validate the range of the result. */ + if (exp < 1022 + intsize) { + /* abs(F) <= INT{N}_MAX */ + return f; + } + if (exp == 1022 + intsize) { + uint64_t sign = extract64(f, 63, 1); + uint64_t frac = extract64(f, 0, 52); + if (sign && frac == 0) { + /* F == INT{N}_MIN */ + return f; + } + } + + overflow: + /* + * Raise Invalid and return INT{N}_MIN as a float. Revert any + * inexact exception float64_round_to_int may have raised. + */ + set_float_exception_flags(old_flags | float_flag_invalid, fpst); + return (uint64_t)(0x800 + 1022 + intsize) << 52; +} + +float64 HELPER(frint32_d)(float64 f, float_status *fpst) +{ + return frint_d(f, fpst, 32); +} + +float64 HELPER(frint64_d)(float64 f, float_status *fpst) +{ + return frint_d(f, fpst, 64); +} + +void HELPER(check_hcr_el2_trap)(CPUARMState *env, uint32_t rt, uint32_t reg) +{ + uint32_t syndrome; + + switch (reg) { + case ARM_VFP_MVFR0: + case ARM_VFP_MVFR1: + case ARM_VFP_MVFR2: + if (!(arm_hcr_el2_eff(env) & HCR_TID3)) { + return; + } + break; + case ARM_VFP_FPSID: + if (!(arm_hcr_el2_eff(env) & HCR_TID0)) { + return; + } + break; + default: + g_assert_not_reached(); + } + + syndrome = ((EC_FPIDTRAP << ARM_EL_EC_SHIFT) + | ARM_EL_IL + | (1 << 24) | (0xe << 20) | (7 << 14) + | (reg << 10) | (rt << 5) | 1); + + raise_exception(env, EXCP_HYP_TRAP, syndrome, 2); +} + +uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) +{ + return vfp_get_fpscr(env); +} + +void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) +{ + vfp_set_fpscr(env, val); +} diff --git a/target/arm/trace-events b/target/arm/trace-events index 48cc0512dbe..4438dce7bec 100644 --- a/target/arm/trace-events +++ b/target/arm/trace-events @@ -8,6 +8,7 @@ arm_gt_tval_write(int timer, uint64_t value) "gt_tval_write: timer %d value 0x%" arm_gt_ctl_write(int timer, uint64_t value) "gt_ctl_write: timer %d value 0x%" PRIx64 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 diff --git a/target/arm/vfp_fpscr.c b/target/arm/vfp_fpscr.c new file mode 100644 index 00000000000..92ea60ebbf2 --- /dev/null +++ b/target/arm/vfp_fpscr.c @@ -0,0 +1,155 @@ +/* + * ARM VFP floating-point: handling of FPSCR/FPCR/FPSR + * + * 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 "cpu.h" +#include "internals.h" +#include "cpu-features.h" + +uint32_t vfp_get_fpcr(CPUARMState *env) +{ + uint32_t fpcr = env->vfp.fpcr + | (env->vfp.vec_len << 16) + | (env->vfp.vec_stride << 20); + + /* + * 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. + */ + fpcr |= env->v7m.ltpsize << 16; + + 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]; + fpsr |= i ? FPSR_QC : 0; + return fpsr; +} + +uint32_t vfp_get_fpscr(CPUARMState *env) +{ + return (vfp_get_fpcr(env) & FPSCR_FPCR_MASK) | + (vfp_get_fpsr(env) & FPSCR_FPSR_MASK); +} + +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; + } + if (!cpu_isar_feature(aa64_afp, cpu)) { + val &= ~(FPCR_FIZ | FPCR_AH | FPCR_NEP); + } + + if (!cpu_isar_feature(aa64_ebf16, cpu)) { + val &= ~FPCR_EBF; + } + + 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 FPCR bits we keep in vfp.fpcr are AHP, DN, FZ, RMode, EBF, FZ16, + * FIZ, AH, and NEP. + * 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. + */ + val &= FPCR_AHP | FPCR_DN | FPCR_FZ | FPCR_RMODE_MASK | FPCR_FZ16 | + FPCR_EBF | FPCR_FIZ | FPCR_AH | FPCR_NEP; + 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 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); +} diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c deleted file mode 100644 index 789bba36ccb..00000000000 --- a/target/arm/vfp_helper.c +++ /dev/null @@ -1,1282 +0,0 @@ -/* - * ARM VFP floating-point operations - * - * 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 "cpu.h" -#include "exec/helper-proto.h" -#include "internals.h" -#ifdef CONFIG_TCG -#include "qemu/log.h" -#include "fpu/softfloat.h" -#endif - -/* VFP support. We follow the convention used for VFP instructions: - Single precision routines have a "s" suffix, double precision a - "d" suffix. */ - -#ifdef CONFIG_TCG - -/* Convert host exception flags to vfp form. */ -static inline int vfp_exceptbits_from_host(int host_bits) -{ - int target_bits = 0; - - if (host_bits & float_flag_invalid) { - target_bits |= 1; - } - if (host_bits & float_flag_divbyzero) { - target_bits |= 2; - } - if (host_bits & float_flag_overflow) { - target_bits |= 4; - } - if (host_bits & (float_flag_underflow | float_flag_output_denormal)) { - target_bits |= 8; - } - if (host_bits & float_flag_inexact) { - target_bits |= 0x10; - } - if (host_bits & float_flag_input_denormal) { - target_bits |= 0x80; - } - 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) -{ - uint32_t i; - - i = get_float_exception_flags(&env->vfp.fp_status); - i |= get_float_exception_flags(&env->vfp.standard_fp_status); - /* FZ16 does not generate an input denormal exception. */ - i |= (get_float_exception_flags(&env->vfp.fp_status_f16) - & ~float_flag_input_denormal); - i |= (get_float_exception_flags(&env->vfp.standard_fp_status_f16) - & ~float_flag_input_denormal); - return vfp_exceptbits_from_host(i); -} - -static void vfp_set_fpscr_to_host(CPUARMState *env, uint32_t val) -{ - int i; - uint32_t changed = env->vfp.xregs[ARM_VFP_FPSCR]; - - changed ^= val; - if (changed & (3 << 22)) { - i = (val >> 22) & 3; - switch (i) { - case FPROUNDING_TIEEVEN: - i = float_round_nearest_even; - break; - case FPROUNDING_POSINF: - i = float_round_up; - break; - case FPROUNDING_NEGINF: - i = float_round_down; - break; - case FPROUNDING_ZERO: - i = float_round_to_zero; - break; - } - set_float_rounding_mode(i, &env->vfp.fp_status); - set_float_rounding_mode(i, &env->vfp.fp_status_f16); - } - if (changed & FPCR_FZ16) { - bool ftz_enabled = val & FPCR_FZ16; - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16); - set_flush_to_zero(ftz_enabled, &env->vfp.standard_fp_status_f16); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.standard_fp_status_f16); - } - if (changed & FPCR_FZ) { - bool ftz_enabled = val & FPCR_FZ; - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status); - } - if (changed & FPCR_DN) { - bool dnan_enabled = val & FPCR_DN; - 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) -{ - return 0; -} - -static void vfp_set_fpscr_to_host(CPUARMState *env, uint32_t val) -{ -} - -#endif - -uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) -{ - uint32_t i, fpscr; - - fpscr = env->vfp.xregs[ARM_VFP_FPSCR] - | (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. - */ - fpscr |= env->v7m.ltpsize << 16; - - fpscr |= vfp_get_fpscr_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; - - return fpscr; -} - -uint32_t vfp_get_fpscr(CPUARMState *env) -{ - return HELPER(vfp_get_fpscr)(env); -} - -void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) -{ - 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 (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; - } - - /* - * 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]. - */ - env->vfp.xregs[ARM_VFP_FPSCR] = val & 0xf7c80000; -} - -void vfp_set_fpscr(CPUARMState *env, uint32_t val) -{ - HELPER(vfp_set_fpscr)(env, val); -} - -#ifdef CONFIG_TCG - -#define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p)) - -#define VFP_BINOP(name) \ -dh_ctype_f16 VFP_HELPER(name, h)(dh_ctype_f16 a, dh_ctype_f16 b, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - return float16_ ## name(a, b, fpst); \ -} \ -float32 VFP_HELPER(name, s)(float32 a, float32 b, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - return float32_ ## name(a, b, fpst); \ -} \ -float64 VFP_HELPER(name, d)(float64 a, float64 b, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - return float64_ ## name(a, b, fpst); \ -} -VFP_BINOP(add) -VFP_BINOP(sub) -VFP_BINOP(mul) -VFP_BINOP(div) -VFP_BINOP(min) -VFP_BINOP(max) -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); -} - -float32 VFP_HELPER(sqrt, s)(float32 a, CPUARMState *env) -{ - return float32_sqrt(a, &env->vfp.fp_status); -} - -float64 VFP_HELPER(sqrt, d)(float64 a, CPUARMState *env) -{ - return float64_sqrt(a, &env->vfp.fp_status); -} - -static void softfloat_to_vfp_compare(CPUARMState *env, FloatRelation cmp) -{ - uint32_t flags; - switch (cmp) { - case float_relation_equal: - flags = 0x6; - break; - case float_relation_less: - flags = 0x8; - break; - case float_relation_greater: - flags = 0x2; - break; - case float_relation_unordered: - flags = 0x3; - break; - default: - g_assert_not_reached(); - } - env->vfp.xregs[ARM_VFP_FPSCR] = - deposit32(env->vfp.xregs[ARM_VFP_FPSCR], 28, 4, flags); -} - -/* XXX: check quiet/signaling case */ -#define DO_VFP_cmp(P, FLOATTYPE, ARGTYPE, FPST) \ -void VFP_HELPER(cmp, P)(ARGTYPE a, ARGTYPE b, CPUARMState *env) \ -{ \ - softfloat_to_vfp_compare(env, \ - FLOATTYPE ## _compare_quiet(a, b, &env->vfp.FPST)); \ -} \ -void VFP_HELPER(cmpe, P)(ARGTYPE a, ARGTYPE b, CPUARMState *env) \ -{ \ - softfloat_to_vfp_compare(env, \ - FLOATTYPE ## _compare(a, b, &env->vfp.FPST)); \ -} -DO_VFP_cmp(h, float16, dh_ctype_f16, fp_status_f16) -DO_VFP_cmp(s, float32, float32, fp_status) -DO_VFP_cmp(d, float64, float64, fp_status) -#undef DO_VFP_cmp - -/* Integer to float and float to integer conversions */ - -#define CONV_ITOF(name, ftype, fsz, sign) \ -ftype HELPER(name)(uint32_t x, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - return sign##int32_to_##float##fsz((sign##int32_t)x, fpst); \ -} - -#define CONV_FTOI(name, ftype, fsz, sign, round) \ -sign##int32_t HELPER(name)(ftype x, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - if (float##fsz##_is_any_nan(x)) { \ - float_raise(float_flag_invalid, fpst); \ - return 0; \ - } \ - return float##fsz##_to_##sign##int32##round(x, fpst); \ -} - -#define FLOAT_CONVS(name, p, ftype, fsz, sign) \ - CONV_ITOF(vfp_##name##to##p, ftype, fsz, sign) \ - CONV_FTOI(vfp_to##name##p, ftype, fsz, sign, ) \ - CONV_FTOI(vfp_to##name##z##p, ftype, fsz, sign, _round_to_zero) - -FLOAT_CONVS(si, h, uint32_t, 16, ) -FLOAT_CONVS(si, s, float32, 32, ) -FLOAT_CONVS(si, d, float64, 64, ) -FLOAT_CONVS(ui, h, uint32_t, 16, u) -FLOAT_CONVS(ui, s, float32, 32, u) -FLOAT_CONVS(ui, d, float64, 64, u) - -#undef CONV_ITOF -#undef CONV_FTOI -#undef FLOAT_CONVS - -/* floating point conversion */ -float64 VFP_HELPER(fcvtd, s)(float32 x, CPUARMState *env) -{ - return float32_to_float64(x, &env->vfp.fp_status); -} - -float32 VFP_HELPER(fcvts, d)(float64 x, CPUARMState *env) -{ - return float64_to_float32(x, &env->vfp.fp_status); -} - -uint32_t HELPER(bfcvt)(float32 x, void *status) -{ - return float32_to_bfloat16(x, status); -} - -uint32_t HELPER(bfcvt_pair)(uint64_t pair, void *status) -{ - bfloat16 lo = float32_to_bfloat16(extract64(pair, 0, 32), status); - bfloat16 hi = float32_to_bfloat16(extract64(pair, 32, 32), status); - return deposit32(lo, 16, 16, hi); -} - -/* - * VFP3 fixed point conversion. The AArch32 versions of fix-to-float - * must always round-to-nearest; the AArch64 ones honour the FPSCR - * rounding mode. (For AArch32 Neon the standard-FPSCR is set to - * round-to-nearest so either helper will work.) AArch32 float-to-fix - * must round-to-zero. - */ -#define VFP_CONV_FIX_FLOAT(name, p, fsz, ftype, isz, itype) \ -ftype HELPER(vfp_##name##to##p)(uint##isz##_t x, uint32_t shift, \ - void *fpstp) \ -{ return itype##_to_##float##fsz##_scalbn(x, -shift, fpstp); } - -#define VFP_CONV_FIX_FLOAT_ROUND(name, p, fsz, ftype, isz, itype) \ - ftype HELPER(vfp_##name##to##p##_round_to_nearest)(uint##isz##_t x, \ - uint32_t shift, \ - void *fpstp) \ - { \ - ftype ret; \ - float_status *fpst = fpstp; \ - FloatRoundMode oldmode = fpst->float_rounding_mode; \ - fpst->float_rounding_mode = float_round_nearest_even; \ - ret = itype##_to_##float##fsz##_scalbn(x, -shift, fpstp); \ - fpst->float_rounding_mode = oldmode; \ - return ret; \ - } - -#define VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, ROUND, suff) \ -uint##isz##_t HELPER(vfp_to##name##p##suff)(ftype x, uint32_t shift, \ - void *fpst) \ -{ \ - if (unlikely(float##fsz##_is_any_nan(x))) { \ - float_raise(float_flag_invalid, fpst); \ - return 0; \ - } \ - return float##fsz##_to_##itype##_scalbn(x, ROUND, shift, fpst); \ -} - -#define VFP_CONV_FIX(name, p, fsz, ftype, isz, itype) \ -VFP_CONV_FIX_FLOAT(name, p, fsz, ftype, isz, itype) \ -VFP_CONV_FIX_FLOAT_ROUND(name, p, fsz, ftype, isz, itype) \ -VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, \ - float_round_to_zero, _round_to_zero) \ -VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, \ - get_float_rounding_mode(fpst), ) - -#define VFP_CONV_FIX_A64(name, p, fsz, ftype, isz, itype) \ -VFP_CONV_FIX_FLOAT(name, p, fsz, ftype, isz, itype) \ -VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, ftype, isz, itype, \ - get_float_rounding_mode(fpst), ) - -VFP_CONV_FIX(sh, d, 64, float64, 64, int16) -VFP_CONV_FIX(sl, d, 64, float64, 64, int32) -VFP_CONV_FIX_A64(sq, d, 64, float64, 64, int64) -VFP_CONV_FIX(uh, d, 64, float64, 64, uint16) -VFP_CONV_FIX(ul, d, 64, float64, 64, uint32) -VFP_CONV_FIX_A64(uq, d, 64, float64, 64, uint64) -VFP_CONV_FIX(sh, s, 32, float32, 32, int16) -VFP_CONV_FIX(sl, s, 32, float32, 32, int32) -VFP_CONV_FIX_A64(sq, s, 32, float32, 64, int64) -VFP_CONV_FIX(uh, s, 32, float32, 32, uint16) -VFP_CONV_FIX(ul, s, 32, float32, 32, uint32) -VFP_CONV_FIX_A64(uq, s, 32, float32, 64, uint64) -VFP_CONV_FIX(sh, h, 16, dh_ctype_f16, 32, int16) -VFP_CONV_FIX(sl, h, 16, dh_ctype_f16, 32, int32) -VFP_CONV_FIX_A64(sq, h, 16, dh_ctype_f16, 64, int64) -VFP_CONV_FIX(uh, h, 16, dh_ctype_f16, 32, uint16) -VFP_CONV_FIX(ul, h, 16, dh_ctype_f16, 32, uint32) -VFP_CONV_FIX_A64(uq, h, 16, dh_ctype_f16, 64, uint64) - -#undef VFP_CONV_FIX -#undef VFP_CONV_FIX_FLOAT -#undef VFP_CONV_FLOAT_FIX_ROUND -#undef VFP_CONV_FIX_A64 - -/* Set the current fp rounding mode and return the old one. - * The argument is a softfloat float_round_ value. - */ -uint32_t HELPER(set_rmode)(uint32_t rmode, void *fpstp) -{ - float_status *fp_status = fpstp; - - uint32_t prev_rmode = get_float_rounding_mode(fp_status); - set_float_rounding_mode(rmode, fp_status); - - return prev_rmode; -} - -/* Half precision conversions. */ -float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, void *fpstp, uint32_t ahp_mode) -{ - /* Squash FZ16 to 0 for the duration of conversion. In this case, - * it would affect flushing input denormals. - */ - float_status *fpst = fpstp; - bool save = get_flush_inputs_to_zero(fpst); - set_flush_inputs_to_zero(false, fpst); - float32 r = float16_to_float32(a, !ahp_mode, fpst); - set_flush_inputs_to_zero(save, fpst); - return r; -} - -uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, void *fpstp, uint32_t ahp_mode) -{ - /* Squash FZ16 to 0 for the duration of conversion. In this case, - * it would affect flushing output denormals. - */ - float_status *fpst = fpstp; - bool save = get_flush_to_zero(fpst); - set_flush_to_zero(false, fpst); - float16 r = float32_to_float16(a, !ahp_mode, fpst); - set_flush_to_zero(save, fpst); - return r; -} - -float64 HELPER(vfp_fcvt_f16_to_f64)(uint32_t a, void *fpstp, uint32_t ahp_mode) -{ - /* Squash FZ16 to 0 for the duration of conversion. In this case, - * it would affect flushing input denormals. - */ - float_status *fpst = fpstp; - bool save = get_flush_inputs_to_zero(fpst); - set_flush_inputs_to_zero(false, fpst); - float64 r = float16_to_float64(a, !ahp_mode, fpst); - set_flush_inputs_to_zero(save, fpst); - return r; -} - -uint32_t HELPER(vfp_fcvt_f64_to_f16)(float64 a, void *fpstp, uint32_t ahp_mode) -{ - /* Squash FZ16 to 0 for the duration of conversion. In this case, - * it would affect flushing output denormals. - */ - float_status *fpst = fpstp; - bool save = get_flush_to_zero(fpst); - set_flush_to_zero(false, fpst); - float16 r = float64_to_float16(a, !ahp_mode, fpst); - set_flush_to_zero(save, fpst); - return r; -} - -/* NEON helpers. */ - -/* Constants 256 and 512 are used in some helpers; we avoid relying on - * int->float conversions at run-time. */ -#define float64_256 make_float64(0x4070000000000000LL) -#define float64_512 make_float64(0x4080000000000000LL) -#define float16_maxnorm make_float16(0x7bff) -#define float32_maxnorm make_float32(0x7f7fffff) -#define float64_maxnorm make_float64(0x7fefffffffffffffLL) - -/* Reciprocal functions - * - * The algorithm that must be used to calculate the estimate - * is specified by the ARM ARM, see FPRecipEstimate()/RecipEstimate - */ - -/* See RecipEstimate() - * - * input is a 9 bit fixed point number - * input range 256 .. 511 for a number from 0.5 <= x < 1.0. - * result range 256 .. 511 for a number from 1.0 to 511/256. - */ - -static int recip_estimate(int input) -{ - int a, b, r; - assert(256 <= input && input < 512); - a = (input * 2) + 1; - b = (1 << 19) / a; - r = (b + 1) >> 1; - assert(256 <= r && r < 512); - return r; -} - -/* - * Common wrapper to call recip_estimate - * - * The parameters are exponent and 64 bit fraction (without implicit - * bit) where the binary point is nominally at bit 52. Returns a - * float64 which can then be rounded to the appropriate size by the - * callee. - */ - -static uint64_t call_recip_estimate(int *exp, int exp_off, uint64_t frac) -{ - uint32_t scaled, estimate; - uint64_t result_frac; - int result_exp; - - /* Handle sub-normals */ - if (*exp == 0) { - if (extract64(frac, 51, 1) == 0) { - *exp = -1; - frac <<= 2; - } else { - frac <<= 1; - } - } - - /* scaled = UInt('1':fraction<51:44>) */ - scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); - estimate = recip_estimate(scaled); - - result_exp = exp_off - *exp; - result_frac = deposit64(0, 44, 8, estimate); - if (result_exp == 0) { - result_frac = deposit64(result_frac >> 1, 51, 1, 1); - } else if (result_exp == -1) { - result_frac = deposit64(result_frac >> 2, 50, 2, 1); - result_exp = 0; - } - - *exp = result_exp; - - return result_frac; -} - -static bool round_to_inf(float_status *fpst, bool sign_bit) -{ - switch (fpst->float_rounding_mode) { - case float_round_nearest_even: /* Round to Nearest */ - return true; - case float_round_up: /* Round to +Inf */ - return !sign_bit; - case float_round_down: /* Round to -Inf */ - return sign_bit; - case float_round_to_zero: /* Round to Zero */ - return false; - default: - g_assert_not_reached(); - } -} - -uint32_t HELPER(recpe_f16)(uint32_t input, void *fpstp) -{ - float_status *fpst = fpstp; - float16 f16 = float16_squash_input_denormal(input, fpst); - uint32_t f16_val = float16_val(f16); - uint32_t f16_sign = float16_is_neg(f16); - int f16_exp = extract32(f16_val, 10, 5); - uint32_t f16_frac = extract32(f16_val, 0, 10); - uint64_t f64_frac; - - if (float16_is_any_nan(f16)) { - float16 nan = f16; - if (float16_is_signaling_nan(f16, fpst)) { - float_raise(float_flag_invalid, fpst); - if (!fpst->default_nan_mode) { - nan = float16_silence_nan(f16, fpst); - } - } - if (fpst->default_nan_mode) { - nan = float16_default_nan(fpst); - } - return nan; - } else if (float16_is_infinity(f16)) { - return float16_set_sign(float16_zero, float16_is_neg(f16)); - } else if (float16_is_zero(f16)) { - float_raise(float_flag_divbyzero, fpst); - return float16_set_sign(float16_infinity, float16_is_neg(f16)); - } else if (float16_abs(f16) < (1 << 8)) { - /* Abs(value) < 2.0^-16 */ - float_raise(float_flag_overflow | float_flag_inexact, fpst); - if (round_to_inf(fpst, f16_sign)) { - return float16_set_sign(float16_infinity, f16_sign); - } else { - return float16_set_sign(float16_maxnorm, f16_sign); - } - } else if (f16_exp >= 29 && fpst->flush_to_zero) { - float_raise(float_flag_underflow, fpst); - return float16_set_sign(float16_zero, float16_is_neg(f16)); - } - - f64_frac = call_recip_estimate(&f16_exp, 29, - ((uint64_t) f16_frac) << (52 - 10)); - - /* result = sign : result_exp<4:0> : fraction<51:42> */ - f16_val = deposit32(0, 15, 1, f16_sign); - f16_val = deposit32(f16_val, 10, 5, f16_exp); - f16_val = deposit32(f16_val, 0, 10, extract64(f64_frac, 52 - 10, 10)); - return make_float16(f16_val); -} - -float32 HELPER(recpe_f32)(float32 input, void *fpstp) -{ - float_status *fpst = fpstp; - float32 f32 = float32_squash_input_denormal(input, fpst); - uint32_t f32_val = float32_val(f32); - bool f32_sign = float32_is_neg(f32); - int f32_exp = extract32(f32_val, 23, 8); - uint32_t f32_frac = extract32(f32_val, 0, 23); - uint64_t f64_frac; - - if (float32_is_any_nan(f32)) { - float32 nan = f32; - if (float32_is_signaling_nan(f32, fpst)) { - float_raise(float_flag_invalid, fpst); - if (!fpst->default_nan_mode) { - nan = float32_silence_nan(f32, fpst); - } - } - if (fpst->default_nan_mode) { - nan = float32_default_nan(fpst); - } - return nan; - } else if (float32_is_infinity(f32)) { - return float32_set_sign(float32_zero, float32_is_neg(f32)); - } else if (float32_is_zero(f32)) { - float_raise(float_flag_divbyzero, fpst); - return float32_set_sign(float32_infinity, float32_is_neg(f32)); - } else if (float32_abs(f32) < (1ULL << 21)) { - /* Abs(value) < 2.0^-128 */ - float_raise(float_flag_overflow | float_flag_inexact, fpst); - if (round_to_inf(fpst, f32_sign)) { - return float32_set_sign(float32_infinity, f32_sign); - } else { - return float32_set_sign(float32_maxnorm, f32_sign); - } - } else if (f32_exp >= 253 && fpst->flush_to_zero) { - float_raise(float_flag_underflow, fpst); - return float32_set_sign(float32_zero, float32_is_neg(f32)); - } - - f64_frac = call_recip_estimate(&f32_exp, 253, - ((uint64_t) f32_frac) << (52 - 23)); - - /* result = sign : result_exp<7:0> : fraction<51:29> */ - f32_val = deposit32(0, 31, 1, f32_sign); - f32_val = deposit32(f32_val, 23, 8, f32_exp); - f32_val = deposit32(f32_val, 0, 23, extract64(f64_frac, 52 - 23, 23)); - return make_float32(f32_val); -} - -float64 HELPER(recpe_f64)(float64 input, void *fpstp) -{ - float_status *fpst = fpstp; - float64 f64 = float64_squash_input_denormal(input, fpst); - uint64_t f64_val = float64_val(f64); - bool f64_sign = float64_is_neg(f64); - int f64_exp = extract64(f64_val, 52, 11); - uint64_t f64_frac = extract64(f64_val, 0, 52); - - /* Deal with any special cases */ - if (float64_is_any_nan(f64)) { - float64 nan = f64; - if (float64_is_signaling_nan(f64, fpst)) { - float_raise(float_flag_invalid, fpst); - if (!fpst->default_nan_mode) { - nan = float64_silence_nan(f64, fpst); - } - } - if (fpst->default_nan_mode) { - nan = float64_default_nan(fpst); - } - return nan; - } else if (float64_is_infinity(f64)) { - return float64_set_sign(float64_zero, float64_is_neg(f64)); - } else if (float64_is_zero(f64)) { - float_raise(float_flag_divbyzero, fpst); - return float64_set_sign(float64_infinity, float64_is_neg(f64)); - } else if ((f64_val & ~(1ULL << 63)) < (1ULL << 50)) { - /* Abs(value) < 2.0^-1024 */ - float_raise(float_flag_overflow | float_flag_inexact, fpst); - if (round_to_inf(fpst, f64_sign)) { - return float64_set_sign(float64_infinity, f64_sign); - } else { - return float64_set_sign(float64_maxnorm, f64_sign); - } - } else if (f64_exp >= 2045 && fpst->flush_to_zero) { - float_raise(float_flag_underflow, fpst); - return float64_set_sign(float64_zero, float64_is_neg(f64)); - } - - f64_frac = call_recip_estimate(&f64_exp, 2045, f64_frac); - - /* result = sign : result_exp<10:0> : fraction<51:0>; */ - f64_val = deposit64(0, 63, 1, f64_sign); - f64_val = deposit64(f64_val, 52, 11, f64_exp); - f64_val = deposit64(f64_val, 0, 52, f64_frac); - return make_float64(f64_val); -} - -/* The algorithm that must be used to calculate the estimate - * is specified by the ARM ARM. - */ - -static int do_recip_sqrt_estimate(int a) -{ - int b, estimate; - - assert(128 <= a && a < 512); - if (a < 256) { - a = a * 2 + 1; - } else { - a = (a >> 1) << 1; - a = (a + 1) * 2; - } - b = 512; - while (a * (b + 1) * (b + 1) < (1 << 28)) { - b += 1; - } - estimate = (b + 1) / 2; - assert(256 <= estimate && estimate < 512); - - return estimate; -} - - -static uint64_t recip_sqrt_estimate(int *exp , int exp_off, uint64_t frac) -{ - int estimate; - uint32_t scaled; - - if (*exp == 0) { - while (extract64(frac, 51, 1) == 0) { - frac = frac << 1; - *exp -= 1; - } - frac = extract64(frac, 0, 51) << 1; - } - - if (*exp & 1) { - /* scaled = UInt('01':fraction<51:45>) */ - scaled = deposit32(1 << 7, 0, 7, extract64(frac, 45, 7)); - } else { - /* scaled = UInt('1':fraction<51:44>) */ - scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); - } - estimate = do_recip_sqrt_estimate(scaled); - - *exp = (exp_off - *exp) / 2; - return extract64(estimate, 0, 8) << 44; -} - -uint32_t HELPER(rsqrte_f16)(uint32_t input, void *fpstp) -{ - float_status *s = fpstp; - float16 f16 = float16_squash_input_denormal(input, s); - uint16_t val = float16_val(f16); - bool f16_sign = float16_is_neg(f16); - int f16_exp = extract32(val, 10, 5); - uint16_t f16_frac = extract32(val, 0, 10); - uint64_t f64_frac; - - if (float16_is_any_nan(f16)) { - float16 nan = f16; - if (float16_is_signaling_nan(f16, s)) { - float_raise(float_flag_invalid, s); - if (!s->default_nan_mode) { - nan = float16_silence_nan(f16, fpstp); - } - } - if (s->default_nan_mode) { - nan = float16_default_nan(s); - } - return nan; - } else if (float16_is_zero(f16)) { - float_raise(float_flag_divbyzero, s); - return float16_set_sign(float16_infinity, f16_sign); - } else if (f16_sign) { - float_raise(float_flag_invalid, s); - return float16_default_nan(s); - } else if (float16_is_infinity(f16)) { - return float16_zero; - } - - /* Scale and normalize to a double-precision value between 0.25 and 1.0, - * preserving the parity of the exponent. */ - - f64_frac = ((uint64_t) f16_frac) << (52 - 10); - - f64_frac = recip_sqrt_estimate(&f16_exp, 44, f64_frac); - - /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(2) */ - val = deposit32(0, 15, 1, f16_sign); - val = deposit32(val, 10, 5, f16_exp); - val = deposit32(val, 2, 8, extract64(f64_frac, 52 - 8, 8)); - return make_float16(val); -} - -float32 HELPER(rsqrte_f32)(float32 input, void *fpstp) -{ - float_status *s = fpstp; - float32 f32 = float32_squash_input_denormal(input, s); - uint32_t val = float32_val(f32); - uint32_t f32_sign = float32_is_neg(f32); - int f32_exp = extract32(val, 23, 8); - uint32_t f32_frac = extract32(val, 0, 23); - uint64_t f64_frac; - - if (float32_is_any_nan(f32)) { - float32 nan = f32; - if (float32_is_signaling_nan(f32, s)) { - float_raise(float_flag_invalid, s); - if (!s->default_nan_mode) { - nan = float32_silence_nan(f32, fpstp); - } - } - if (s->default_nan_mode) { - nan = float32_default_nan(s); - } - return nan; - } else if (float32_is_zero(f32)) { - float_raise(float_flag_divbyzero, s); - return float32_set_sign(float32_infinity, float32_is_neg(f32)); - } else if (float32_is_neg(f32)) { - float_raise(float_flag_invalid, s); - return float32_default_nan(s); - } else if (float32_is_infinity(f32)) { - return float32_zero; - } - - /* Scale and normalize to a double-precision value between 0.25 and 1.0, - * preserving the parity of the exponent. */ - - f64_frac = ((uint64_t) f32_frac) << 29; - - f64_frac = recip_sqrt_estimate(&f32_exp, 380, f64_frac); - - /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(15) */ - val = deposit32(0, 31, 1, f32_sign); - val = deposit32(val, 23, 8, f32_exp); - val = deposit32(val, 15, 8, extract64(f64_frac, 52 - 8, 8)); - return make_float32(val); -} - -float64 HELPER(rsqrte_f64)(float64 input, void *fpstp) -{ - float_status *s = fpstp; - float64 f64 = float64_squash_input_denormal(input, s); - uint64_t val = float64_val(f64); - bool f64_sign = float64_is_neg(f64); - int f64_exp = extract64(val, 52, 11); - uint64_t f64_frac = extract64(val, 0, 52); - - if (float64_is_any_nan(f64)) { - float64 nan = f64; - if (float64_is_signaling_nan(f64, s)) { - float_raise(float_flag_invalid, s); - if (!s->default_nan_mode) { - nan = float64_silence_nan(f64, fpstp); - } - } - if (s->default_nan_mode) { - nan = float64_default_nan(s); - } - return nan; - } else if (float64_is_zero(f64)) { - float_raise(float_flag_divbyzero, s); - return float64_set_sign(float64_infinity, float64_is_neg(f64)); - } else if (float64_is_neg(f64)) { - float_raise(float_flag_invalid, s); - return float64_default_nan(s); - } else if (float64_is_infinity(f64)) { - return float64_zero; - } - - f64_frac = recip_sqrt_estimate(&f64_exp, 3068, f64_frac); - - /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(44) */ - val = deposit64(0, 61, 1, f64_sign); - val = deposit64(val, 52, 11, f64_exp); - val = deposit64(val, 44, 8, extract64(f64_frac, 52 - 8, 8)); - return make_float64(val); -} - -uint32_t HELPER(recpe_u32)(uint32_t a) -{ - int input, estimate; - - if ((a & 0x80000000) == 0) { - return 0xffffffff; - } - - input = extract32(a, 23, 9); - estimate = recip_estimate(input); - - return deposit32(0, (32 - 9), 9, estimate); -} - -uint32_t HELPER(rsqrte_u32)(uint32_t a) -{ - int estimate; - - if ((a & 0xc0000000) == 0) { - return 0xffffffff; - } - - estimate = do_recip_sqrt_estimate(extract32(a, 23, 9)); - - return deposit32(0, 23, 9, estimate); -} - -/* VFPv4 fused multiply-accumulate */ -dh_ctype_f16 VFP_HELPER(muladd, h)(dh_ctype_f16 a, dh_ctype_f16 b, - dh_ctype_f16 c, void *fpstp) -{ - float_status *fpst = fpstp; - return float16_muladd(a, b, c, 0, fpst); -} - -float32 VFP_HELPER(muladd, s)(float32 a, float32 b, float32 c, void *fpstp) -{ - float_status *fpst = fpstp; - return float32_muladd(a, b, c, 0, fpst); -} - -float64 VFP_HELPER(muladd, d)(float64 a, float64 b, float64 c, void *fpstp) -{ - float_status *fpst = fpstp; - return float64_muladd(a, b, c, 0, fpst); -} - -/* ARMv8 round to integral */ -dh_ctype_f16 HELPER(rinth_exact)(dh_ctype_f16 x, void *fp_status) -{ - return float16_round_to_int(x, fp_status); -} - -float32 HELPER(rints_exact)(float32 x, void *fp_status) -{ - return float32_round_to_int(x, fp_status); -} - -float64 HELPER(rintd_exact)(float64 x, void *fp_status) -{ - return float64_round_to_int(x, fp_status); -} - -dh_ctype_f16 HELPER(rinth)(dh_ctype_f16 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; -} - -float32 HELPER(rints)(float32 x, void *fp_status) -{ - int old_flags = get_float_exception_flags(fp_status), new_flags; - float32 ret; - - ret = float32_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; -} - -float64 HELPER(rintd)(float64 x, void *fp_status) -{ - int old_flags = get_float_exception_flags(fp_status), new_flags; - float64 ret; - - ret = float64_round_to_int(x, fp_status); - - new_flags = get_float_exception_flags(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; -} - -/* Convert ARM rounding mode to softfloat */ -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; - * the result is supplied modulo 2^32. - */ -uint64_t HELPER(fjcvtzs)(float64 value, void *vstatus) -{ - float_status *status = vstatus; - uint32_t inexact, frac; - uint32_t e_old, e_new; - - 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 (value == float64_chs(float64_zero)) { - /* While not inexact for IEEE FP, -0.0 is inexact for JavaScript. */ - inexact = 1; - } else { - /* Normal inexact or overflow or NaN */ - inexact = e_new & (float_flag_inexact | float_flag_invalid); - } - - /* Pack the result and the env->ZF representation of Z together. */ - return deposit64(frac, 32, 32, inexact); -} - -uint32_t HELPER(vjcvt)(float64 value, CPUARMState *env) -{ - uint64_t pair = HELPER(fjcvtzs)(value, &env->vfp.fp_status); - uint32_t result = pair; - 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); - - return result; -} - -/* Round a float32 to an integer that fits in int32_t or int64_t. */ -static float32 frint_s(float32 f, float_status *fpst, int intsize) -{ - int old_flags = get_float_exception_flags(fpst); - uint32_t exp = extract32(f, 23, 8); - - if (unlikely(exp == 0xff)) { - /* NaN or Inf. */ - goto overflow; - } - - /* Round and re-extract the exponent. */ - f = float32_round_to_int(f, fpst); - exp = extract32(f, 23, 8); - - /* Validate the range of the result. */ - if (exp < 126 + intsize) { - /* abs(F) <= INT{N}_MAX */ - return f; - } - if (exp == 126 + intsize) { - uint32_t sign = extract32(f, 31, 1); - uint32_t frac = extract32(f, 0, 23); - if (sign && frac == 0) { - /* F == INT{N}_MIN */ - return f; - } - } - - overflow: - /* - * Raise Invalid and return INT{N}_MIN as a float. Revert any - * inexact exception float32_round_to_int may have raised. - */ - set_float_exception_flags(old_flags | float_flag_invalid, fpst); - return (0x100u + 126u + intsize) << 23; -} - -float32 HELPER(frint32_s)(float32 f, void *fpst) -{ - return frint_s(f, fpst, 32); -} - -float32 HELPER(frint64_s)(float32 f, void *fpst) -{ - return frint_s(f, fpst, 64); -} - -/* Round a float64 to an integer that fits in int32_t or int64_t. */ -static float64 frint_d(float64 f, float_status *fpst, int intsize) -{ - int old_flags = get_float_exception_flags(fpst); - uint32_t exp = extract64(f, 52, 11); - - if (unlikely(exp == 0x7ff)) { - /* NaN or Inf. */ - goto overflow; - } - - /* Round and re-extract the exponent. */ - f = float64_round_to_int(f, fpst); - exp = extract64(f, 52, 11); - - /* Validate the range of the result. */ - if (exp < 1022 + intsize) { - /* abs(F) <= INT{N}_MAX */ - return f; - } - if (exp == 1022 + intsize) { - uint64_t sign = extract64(f, 63, 1); - uint64_t frac = extract64(f, 0, 52); - if (sign && frac == 0) { - /* F == INT{N}_MIN */ - return f; - } - } - - overflow: - /* - * Raise Invalid and return INT{N}_MIN as a float. Revert any - * inexact exception float64_round_to_int may have raised. - */ - set_float_exception_flags(old_flags | float_flag_invalid, fpst); - return (uint64_t)(0x800 + 1022 + intsize) << 52; -} - -float64 HELPER(frint32_d)(float64 f, void *fpst) -{ - return frint_d(f, fpst, 32); -} - -float64 HELPER(frint64_d)(float64 f, void *fpst) -{ - return frint_d(f, fpst, 64); -} - -void HELPER(check_hcr_el2_trap)(CPUARMState *env, uint32_t rt, uint32_t reg) -{ - uint32_t syndrome; - - switch (reg) { - case ARM_VFP_MVFR0: - case ARM_VFP_MVFR1: - case ARM_VFP_MVFR2: - if (!(arm_hcr_el2_eff(env) & HCR_TID3)) { - return; - } - break; - case ARM_VFP_FPSID: - if (!(arm_hcr_el2_eff(env) & HCR_TID0)) { - return; - } - break; - default: - g_assert_not_reached(); - } - - syndrome = ((EC_FPIDTRAP << ARM_EL_EC_SHIFT) - | ARM_EL_IL - | (1 << 24) | (0xe << 20) | (7 << 14) - | (reg << 10) | (rt << 5) | 1); - - raise_exception(env, EXCP_HYP_TRAP, syndrome, 2); -} - -#endif diff --git a/target/avr/cpu-param.h b/target/avr/cpu-param.h index 9a92bc74fc9..f74bfc25804 100644 --- a/target/avr/cpu-param.h +++ b/target/avr/cpu-param.h @@ -21,15 +21,10 @@ #ifndef AVR_CPU_PARAM_H #define AVR_CPU_PARAM_H -#define TARGET_LONG_BITS 32 -/* - * TARGET_PAGE_BITS cannot be more than 8 bits because - * 1. all IO registers occupy [0x0000 .. 0x00ff] address range, and they - * should be implemented as a device and not memory - * 2. SRAM starts at the address 0x0100 - */ -#define TARGET_PAGE_BITS 8 +#define TARGET_PAGE_BITS 10 #define TARGET_PHYS_ADDR_SPACE_BITS 24 #define TARGET_VIRT_ADDR_SPACE_BITS 24 +#define TARGET_INSN_START_EXTRA_WORDS 0 + #endif diff --git a/target/avr/cpu-qom.h b/target/avr/cpu-qom.h index 01ea5f160b6..38dbcc05359 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_phases: The parent class' reset phase handlers. - * - * A AVR CPU model. - */ -struct AVRCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; - +#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 8f741f258c6..6995de6a12b 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -21,10 +21,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/qemu-print.h" -#include "exec/exec-all.h" +#include "exec/translation-block.h" +#include "system/address-spaces.h" #include "cpu.h" #include "disas/dis-asm.h" #include "tcg/debug-assert.h" +#include "hw/qdev-properties.h" +#include "accel/tcg/cpu-ops.h" static void avr_cpu_set_pc(CPUState *cs, vaddr value) { @@ -42,42 +45,53 @@ 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 TCGTBCPUState avr_get_tb_cpu_state(CPUState *cs) +{ + CPUAVRState *env = cpu_env(cs); + uint32_t flags = 0; + + if (env->fullacc) { + flags |= TB_FLAGS_FULL_ACCESS; + } + if (env->skip) { + flags |= TB_FLAGS_SKIP; + } + + return (TCGTBCPUState){ .pc = env->pc_w * 2, .flags = flags }; } static void avr_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - AVRCPU *cpu = AVR_CPU(cs); - CPUAVRState *env = &cpu->env; - - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); - env->pc_w = tb->pc / 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_hold(Object *obj) +static void avr_cpu_reset_hold(Object *obj, ResetType type) { 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; if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } env->pc_w = 0; @@ -95,7 +109,7 @@ static void avr_cpu_reset_hold(Object *obj) env->rampY = 0; env->rampZ = 0; env->eind = 0; - env->sp = 0; + env->sp = cpu->init_sp; env->skip = 0; @@ -104,6 +118,7 @@ static void avr_cpu_reset_hold(Object *obj) static void avr_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) { + info->endian = BFD_ENDIAN_LITTLE; info->mach = bfd_arch_avr; info->print_insn = avr_print_insn; } @@ -111,6 +126,8 @@ static void avr_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) static void avr_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); + CPUAVRState *env = cpu_env(cs); + AVRCPU *cpu = env_archcpu(env); AVRCPUClass *mcc = AVR_CPU_GET_CLASS(dev); Error *local_err = NULL; @@ -123,6 +140,19 @@ static void avr_cpu_realizefn(DeviceState *dev, Error **errp) cpu_reset(cs); mcc->parent_realize(dev, errp); + + /* + * Two blocks in the low data space loop back into cpu registers. + */ + memory_region_init_io(&cpu->cpu_reg1, OBJECT(cpu), &avr_cpu_reg1, env, + "avr-cpu-reg1", 32); + memory_region_add_subregion(get_system_memory(), + OFFSET_DATA, &cpu->cpu_reg1); + + memory_region_init_io(&cpu->cpu_reg2, OBJECT(cpu), &avr_cpu_reg2, env, + "avr-cpu-reg2", 8); + memory_region_add_subregion(get_system_memory(), + OFFSET_DATA + 0x58, &cpu->cpu_reg2); } static void avr_cpu_set_int(void *opaque, int irq, int level) @@ -147,29 +177,23 @@ 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 const Property avr_cpu_properties[] = { + DEFINE_PROP_UINT32("init-sp", AVRCPU, init_sp, 0), +}; + 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"); @@ -208,21 +232,33 @@ static void avr_cpu_dump_state(CPUState *cs, FILE *f, int flags) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps avr_sysemu_ops = { + .has_work = avr_cpu_has_work, .get_phys_page_debug = avr_cpu_get_phys_page_debug, }; -#include "hw/core/tcg-cpu-ops.h" - -static const struct TCGCPUOps avr_tcg_ops = { +static const TCGCPUOps avr_tcg_ops = { + .guest_default_memory_order = 0, + .mttcg_supported = false, .initialize = avr_cpu_tcg_init, + .translate_code = avr_cpu_translate_code, + .get_tb_cpu_state = avr_get_tb_cpu_state, .synchronize_from_tb = avr_cpu_synchronize_from_tb, .restore_state_to_opc = avr_restore_state_to_opc, + .mmu_index = avr_cpu_mmu_index, .cpu_exec_interrupt = avr_cpu_exec_interrupt, + .cpu_exec_halt = avr_cpu_has_work, + .cpu_exec_reset = cpu_reset, .tlb_fill = avr_cpu_tlb_fill, .do_interrupt = avr_cpu_do_interrupt, + /* + * TODO: code and data wrapping are different, but for the most part + * AVR only references bytes or aligned code fetches. But we use + * non-aligned MO_16 accesses for stack push/pop. + */ + .pointer_wrap = cpu_pointer_wrap_uint32, }; -static void avr_cpu_class_init(ObjectClass *oc, void *data) +static void avr_cpu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); @@ -231,12 +267,13 @@ static void avr_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, avr_cpu_realizefn, &mcc->parent_realize); + 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->dump_state = avr_cpu_dump_state; cc->set_pc = avr_cpu_set_pc; cc->get_pc = avr_cpu_get_pc; @@ -246,7 +283,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; } @@ -277,8 +313,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); @@ -306,8 +341,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); @@ -336,8 +370,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); @@ -363,21 +396,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, \ @@ -390,6 +408,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 72251746681..518e243d812 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -22,18 +22,17 @@ #define QEMU_AVR_CPU_H #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" +#include "system/memory.h" #ifdef CONFIG_USER_ONLY #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 @@ -48,8 +47,16 @@ /* Number of CPU registers */ #define NUMBER_OF_CPU_REGISTERS 32 -/* Number of IO registers accessible by ld/st/in/out */ -#define NUMBER_OF_IO_REGISTERS 64 + +/* CPU registers mapped into i/o ports 0x38-0x3f. */ +#define REG_38_RAMPD 0 +#define REG_38_RAMPX 1 +#define REG_38_RAMPY 2 +#define REG_38_RAMPZ 3 +#define REG_38_EIDN 4 +#define REG_38_SPL 5 +#define REG_38_SPH 6 +#define REG_38_SREG 7 /* * Offsets of AVR memory regions in host memory space. @@ -64,8 +71,6 @@ #define OFFSET_CODE 0x00000000 /* CPU registers, IO registers, and SRAM */ #define OFFSET_DATA 0x00800000 -/* CPU registers specifically, these are mapped at the start of data */ -#define OFFSET_CPU_REGISTERS OFFSET_DATA /* * IO registers, including status register, stack pointer, and memory * mapped peripherals, mapped just after CPU registers @@ -144,12 +149,29 @@ typedef struct CPUArchState { * A AVR CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUAVRState env; + + MemoryRegion cpu_reg1; + MemoryRegion cpu_reg2; + + /* 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 +194,10 @@ 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_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); -void avr_cpu_list(void); int cpu_avr_exec(CPUState *cpu); enum { @@ -190,24 +205,6 @@ enum { TB_FLAGS_SKIP = 2, }; -static inline void cpu_get_tb_cpu_state(CPUAVRState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) -{ - uint32_t flags = 0; - - *pc = env->pc_w * 2; - *cs_base = 0; - - if (env->fullacc) { - flags |= TB_FLAGS_FULL_ACCESS; - } - if (env->skip) { - flags |= TB_FLAGS_SKIP; - } - - *pflags = flags; -} - static inline int cpu_interrupts_enabled(CPUAVRState *env) { return env->sregI != 0; @@ -241,6 +238,7 @@ bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); -#include "exec/cpu-all.h" +extern const MemoryRegionOps avr_cpu_reg1; +extern const MemoryRegionOps avr_cpu_reg2; #endif /* QEMU_AVR_CPU_H */ diff --git a/target/avr/disas.c b/target/avr/disas.c index b7689e8d7cd..d341030174f 100644 --- a/target/avr/disas.c +++ b/target/avr/disas.c @@ -68,28 +68,35 @@ static bool decode_insn(DisasContext *ctx, uint16_t insn); int avr_print_insn(bfd_vma addr, disassemble_info *info) { - DisasContext ctx; + DisasContext ctx = { info }; DisasContext *pctx = &ctx; bfd_byte buffer[4]; uint16_t insn; int status; - ctx.info = info; - - status = info->read_memory_func(addr, buffer, 4, info); + status = info->read_memory_func(addr, buffer, 2, info); if (status != 0) { info->memory_error_func(status, addr, info); return -1; } insn = bfd_getl16(buffer); - ctx.next_word = bfd_getl16(buffer + 2); - ctx.next_word_used = false; + + status = info->read_memory_func(addr + 2, buffer + 2, 2, info); + if (status == 0) { + ctx.next_word = bfd_getl16(buffer + 2); + } if (!decode_insn(&ctx, insn)) { output(".db", "0x%02x, 0x%02x", buffer[0], buffer[1]); } - return ctx.next_word_used ? 4 : 2; + if (!ctx.next_word_used) { + return 2; + } else if (status == 0) { + return 4; + } + info->memory_error_func(status, addr + 2, info); + return -1; } diff --git a/target/avr/gdbstub.c b/target/avr/gdbstub.c index 150344d8b94..aea71282a58 100644 --- a/target/avr/gdbstub.c +++ b/target/avr/gdbstub.c @@ -20,11 +20,11 @@ #include "qemu/osdep.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 fdc9884ea0e..b9cd6d5ef27 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -22,16 +22,16 @@ #include "qemu/log.h" #include "qemu/error-report.h" #include "cpu.h" -#include "hw/core/tcg-cpu-ops.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "exec/address-spaces.h" +#include "accel/tcg/cpu-ops.h" +#include "exec/cputlb.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" +#include "accel/tcg/cpu-ldst.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, @@ -67,10 +67,14 @@ bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) return false; } +static void do_stb(CPUAVRState *env, uint32_t addr, uint8_t data, uintptr_t ra) +{ + cpu_stb_mmuidx_ra(env, addr, data, MMU_DATA_IDX, ra); +} + 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; @@ -84,14 +88,14 @@ void avr_cpu_do_interrupt(CPUState *cs) } if (avr_feature(env, AVR_FEATURE_3_BYTE_PC)) { - cpu_stb_data(env, env->sp--, (ret & 0x0000ff)); - cpu_stb_data(env, env->sp--, (ret & 0x00ff00) >> 8); - cpu_stb_data(env, env->sp--, (ret & 0xff0000) >> 16); + do_stb(env, env->sp--, ret, 0); + do_stb(env, env->sp--, ret >> 8, 0); + do_stb(env, env->sp--, ret >> 16, 0); } else if (avr_feature(env, AVR_FEATURE_2_BYTE_PC)) { - cpu_stb_data(env, env->sp--, (ret & 0x0000ff)); - cpu_stb_data(env, env->sp--, (ret & 0x00ff00) >> 8); + do_stb(env, env->sp--, ret, 0); + do_stb(env, env->sp--, ret >> 8, 0); } else { - cpu_stb_data(env, env->sp--, (ret & 0x0000ff)); + do_stb(env, env->sp--, ret, 0); } env->pc_w = base + vector * size; @@ -109,7 +113,7 @@ bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { - int prot, page_size = TARGET_PAGE_SIZE; + int prot; uint32_t paddr; address &= TARGET_PAGE_MASK; @@ -134,25 +138,9 @@ bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size, /* Access to memory. */ paddr = OFFSET_DATA + address; prot = PAGE_READ | PAGE_WRITE; - if (address < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) { - /* - * Access to CPU registers, exit and rebuilt this TB to use - * full access in case it touches specially handled registers - * like SREG or SP. For probing, set page_size = 1, in order - * to force tlb_fill to be called for the next access. - */ - if (probe) { - page_size = 1; - } else { - AVRCPU *cpu = AVR_CPU(cs); - CPUAVRState *env = &cpu->env; - env->fullacc = 1; - cpu_loop_exit_restore(cs, retaddr); - } - } } - tlb_set_page(cs, address, paddr, prot, mmu_idx, page_size); + tlb_set_page(cs, address, paddr, prot, mmu_idx, TARGET_PAGE_SIZE); return true; } @@ -206,156 +194,129 @@ void helper_wdr(CPUAVRState *env) } /* - * This function implements IN instruction - * - * It does the following - * a. if an IO register belongs to CPU, its value is read and returned - * b. otherwise io address is translated to mem address and physical memory - * is read. - * c. it caches the value for sake of SBI, SBIC, SBIS & CBI implementation - * + * The first 32 bytes of the data space are mapped to the cpu regs. + * We cannot write these from normal store operations because TCG + * does not expect global temps to be modified -- a global may be + * live in a host cpu register across the store. We can however + * read these, as TCG does make sure the global temps are saved + * in case the load operation traps. */ -target_ulong helper_inb(CPUAVRState *env, uint32_t port) + +static uint64_t avr_cpu_reg1_read(void *opaque, hwaddr addr, unsigned size) { - target_ulong data = 0; + CPUAVRState *env = opaque; - switch (port) { - case 0x38: /* RAMPD */ - data = 0xff & (env->rampD >> 16); - break; - case 0x39: /* RAMPX */ - data = 0xff & (env->rampX >> 16); - break; - case 0x3a: /* RAMPY */ - data = 0xff & (env->rampY >> 16); - break; - case 0x3b: /* RAMPZ */ - data = 0xff & (env->rampZ >> 16); - break; - case 0x3c: /* EIND */ - data = 0xff & (env->eind >> 16); - break; - case 0x3d: /* SPL */ - data = env->sp & 0x00ff; - break; - case 0x3e: /* SPH */ - data = env->sp >> 8; - break; - case 0x3f: /* SREG */ - data = cpu_get_sreg(env); - break; - default: - /* not a special register, pass to normal memory access */ - data = address_space_ldub(&address_space_memory, - OFFSET_IO_REGISTERS + port, - MEMTXATTRS_UNSPECIFIED, NULL); + assert(addr < 32); + return env->r[addr]; +} + +/* + * The range 0x38-0x3f of the i/o space is mapped to cpu regs. + * As above, we cannot write these from normal store operations. + */ + +static uint64_t avr_cpu_reg2_read(void *opaque, hwaddr addr, unsigned size) +{ + CPUAVRState *env = opaque; + + switch (addr) { + case REG_38_RAMPD: + return 0xff & (env->rampD >> 16); + case REG_38_RAMPX: + return 0xff & (env->rampX >> 16); + case REG_38_RAMPY: + return 0xff & (env->rampY >> 16); + case REG_38_RAMPZ: + return 0xff & (env->rampZ >> 16); + case REG_38_EIDN: + return 0xff & (env->eind >> 16); + case REG_38_SPL: + return env->sp & 0x00ff; + case REG_38_SPH: + return 0xff & (env->sp >> 8); + case REG_38_SREG: + return cpu_get_sreg(env); } + g_assert_not_reached(); +} - return data; +static void avr_cpu_trap_write(void *opaque, hwaddr addr, + uint64_t data64, unsigned size) +{ + CPUAVRState *env = opaque; + CPUState *cs = env_cpu(env); + + env->fullacc = true; + cpu_loop_exit_restore(cs, cs->mem_io_pc); } +const MemoryRegionOps avr_cpu_reg1 = { + .read = avr_cpu_reg1_read, + .write = avr_cpu_trap_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 1, +}; + +const MemoryRegionOps avr_cpu_reg2 = { + .read = avr_cpu_reg2_read, + .write = avr_cpu_trap_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 1, +}; + /* - * This function implements OUT instruction - * - * It does the following - * a. if an IO register belongs to CPU, its value is written into the register - * b. otherwise io address is translated to mem address and physical memory - * is written. - * c. it caches the value for sake of SBI, SBIC, SBIS & CBI implementation - * + * this function implements ST instruction when there is a possibility to write + * into a CPU register */ -void helper_outb(CPUAVRState *env, uint32_t port, uint32_t data) +void helper_fullwr(CPUAVRState *env, uint32_t data, uint32_t addr) { - data &= 0x000000ff; + env->fullacc = false; - switch (port) { - case 0x38: /* RAMPD */ + switch (addr) { + case 0 ... 31: + /* CPU registers */ + env->r[addr] = data; + break; + + case REG_38_RAMPD + 0x38 + NUMBER_OF_CPU_REGISTERS: if (avr_feature(env, AVR_FEATURE_RAMPD)) { - env->rampD = (data & 0xff) << 16; + env->rampD = data << 16; } break; - case 0x39: /* RAMPX */ + case REG_38_RAMPX + 0x38 + NUMBER_OF_CPU_REGISTERS: if (avr_feature(env, AVR_FEATURE_RAMPX)) { - env->rampX = (data & 0xff) << 16; + env->rampX = data << 16; } break; - case 0x3a: /* RAMPY */ + case REG_38_RAMPY + 0x38 + NUMBER_OF_CPU_REGISTERS: if (avr_feature(env, AVR_FEATURE_RAMPY)) { - env->rampY = (data & 0xff) << 16; + env->rampY = data << 16; } break; - case 0x3b: /* RAMPZ */ + case REG_38_RAMPZ + 0x38 + NUMBER_OF_CPU_REGISTERS: if (avr_feature(env, AVR_FEATURE_RAMPZ)) { - env->rampZ = (data & 0xff) << 16; + env->rampZ = data << 16; } break; - case 0x3c: /* EIDN */ - env->eind = (data & 0xff) << 16; + case REG_38_EIDN + 0x38 + NUMBER_OF_CPU_REGISTERS: + env->eind = data << 16; break; - case 0x3d: /* SPL */ - env->sp = (env->sp & 0xff00) | (data); + case REG_38_SPL + 0x38 + NUMBER_OF_CPU_REGISTERS: + env->sp = (env->sp & 0xff00) | data; break; - case 0x3e: /* SPH */ + case REG_38_SPH + 0x38 + NUMBER_OF_CPU_REGISTERS: if (avr_feature(env, AVR_FEATURE_2_BYTE_SP)) { env->sp = (env->sp & 0x00ff) | (data << 8); } break; - case 0x3f: /* SREG */ + case REG_38_SREG + 0x38 + NUMBER_OF_CPU_REGISTERS: cpu_set_sreg(env, data); break; - default: - /* not a special register, pass to normal memory access */ - address_space_stb(&address_space_memory, OFFSET_IO_REGISTERS + port, - data, MEMTXATTRS_UNSPECIFIED, NULL); - } -} - -/* - * this function implements LD instruction when there is a possibility to read - * from a CPU register - */ -target_ulong helper_fullrd(CPUAVRState *env, uint32_t addr) -{ - uint8_t data; - - env->fullacc = false; - - if (addr < NUMBER_OF_CPU_REGISTERS) { - /* CPU registers */ - data = env->r[addr]; - } else if (addr < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) { - /* IO registers */ - data = helper_inb(env, addr - NUMBER_OF_CPU_REGISTERS); - } else { - /* memory */ - data = address_space_ldub(&address_space_memory, OFFSET_DATA + addr, - MEMTXATTRS_UNSPECIFIED, NULL); - } - return data; -} -/* - * this function implements ST instruction when there is a possibility to write - * into a CPU register - */ -void helper_fullwr(CPUAVRState *env, uint32_t data, uint32_t addr) -{ - env->fullacc = false; - - /* Following logic assumes this: */ - assert(OFFSET_CPU_REGISTERS == OFFSET_DATA); - assert(OFFSET_IO_REGISTERS == OFFSET_CPU_REGISTERS + - NUMBER_OF_CPU_REGISTERS); - - if (addr < NUMBER_OF_CPU_REGISTERS) { - /* CPU registers */ - env->r[addr] = data; - } else if (addr < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) { - /* IO registers */ - helper_outb(env, addr - NUMBER_OF_CPU_REGISTERS, data); - } else { - /* memory */ - address_space_stb(&address_space_memory, OFFSET_DATA + addr, data, - MEMTXATTRS_UNSPECIFIED, NULL); + default: + do_stb(env, addr, data, GETPC()); + break; } } diff --git a/target/avr/helper.h b/target/avr/helper.h index 4d02e648fae..e8d13e925f4 100644 --- a/target/avr/helper.h +++ b/target/avr/helper.h @@ -23,7 +23,4 @@ DEF_HELPER_1(debug, noreturn, env) DEF_HELPER_1(break, noreturn, env) DEF_HELPER_1(sleep, noreturn, env) DEF_HELPER_1(unsupported, noreturn, env) -DEF_HELPER_3(outb, void, env, i32, i32) -DEF_HELPER_2(inb, tl, env, i32) DEF_HELPER_3(fullwr, void, env, i32, i32) -DEF_HELPER_2(fullrd, tl, env, i32) diff --git a/target/avr/insn.decode b/target/avr/insn.decode index 482c23ad0cf..cc302249db7 100644 --- a/target/avr/insn.decode +++ b/target/avr/insn.decode @@ -118,11 +118,8 @@ BRBC 1111 01 ....... ... @op_bit_imm @io_rd_imm .... . .. ..... .... &rd_imm rd=%rd imm=%io_imm @ldst_d .. . . .. . rd:5 . ... &rd_imm imm=%ldst_d_imm -# The 16-bit immediate is completely in the next word. -# Fields cannot be defined with no bits, so we cannot play -# the same trick and append to a zero-bit value. -# Defer reading the immediate until trans_{LDS,STS}. -@ldst_s .... ... rd:5 .... imm=0 +%ldst_imm !function=next_word +@ldst_s .... ... rd:5 .... imm=%ldst_imm MOV 0010 11 . ..... .... @op_rd_rr MOVW 0000 0001 .... .... &rd_rr rd=%rd_d rr=%rr_d diff --git a/target/avr/machine.c b/target/avr/machine.c index 16f7a3e031d..4402862fb96 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 a24cf6d26de..3e172bde1ce 100644 --- a/target/avr/meson.build +++ b/target/avr/meson.build @@ -17,4 +17,4 @@ avr_ss.add(files( avr_system_ss.add(files('machine.c')) target_arch += {'avr': avr_ss} -target_softmmu_arch += {'avr': avr_system_ss} +target_system_arch += {'avr': avr_system_ss} diff --git a/target/avr/translate.c b/target/avr/translate.c index ef2edd7415d..804b0b21dbd 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -22,13 +22,13 @@ #include "qemu/qemu-print.h" #include "tcg/tcg.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/translation-block.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/target_page.h" #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" @@ -127,25 +127,25 @@ void avr_cpu_tcg_init(void) int i; #define AVR_REG_OFFS(x) offsetof(CPUAVRState, x) - cpu_pc = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(pc_w), "pc"); - cpu_Cf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregC), "Cf"); - cpu_Zf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregZ), "Zf"); - cpu_Nf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregN), "Nf"); - cpu_Vf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregV), "Vf"); - cpu_Sf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregS), "Sf"); - cpu_Hf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregH), "Hf"); - cpu_Tf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregT), "Tf"); - cpu_If = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregI), "If"); - cpu_rampD = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampD), "rampD"); - cpu_rampX = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampX), "rampX"); - cpu_rampY = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampY), "rampY"); - cpu_rampZ = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampZ), "rampZ"); - cpu_eind = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(eind), "eind"); - cpu_sp = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sp), "sp"); - cpu_skip = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(skip), "skip"); + cpu_pc = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(pc_w), "pc"); + cpu_Cf = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregC), "Cf"); + cpu_Zf = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregZ), "Zf"); + cpu_Nf = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregN), "Nf"); + cpu_Vf = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregV), "Vf"); + cpu_Sf = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregS), "Sf"); + cpu_Hf = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregH), "Hf"); + cpu_Tf = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregT), "Tf"); + cpu_If = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sregI), "If"); + cpu_rampD = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(rampD), "rampD"); + cpu_rampX = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(rampX), "rampX"); + cpu_rampY = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(rampY), "rampY"); + cpu_rampZ = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(rampZ), "rampZ"); + cpu_eind = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(eind), "eind"); + cpu_sp = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(sp), "sp"); + cpu_skip = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(skip), "skip"); for (i = 0; i < NUMBER_OF_CPU_REGISTERS; i++) { - cpu_r[i] = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(r[i]), + cpu_r[i] = tcg_global_mem_new_i32(tcg_env, AVR_REG_OFFS(r[i]), reg_names[i]); } #undef AVR_REG_OFFS @@ -173,7 +173,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) @@ -184,7 +184,7 @@ static int append_16(DisasContext *ctx, int x) static bool avr_have_feature(DisasContext *ctx, int feature) { if (!avr_feature(ctx->env, feature)) { - gen_helper_unsupported(cpu_env); + gen_helper_unsupported(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; return false; } @@ -194,6 +194,9 @@ static bool avr_have_feature(DisasContext *ctx, int feature) static bool decode_insn(DisasContext *ctx, uint16_t insn); #include "decode-insn.c.inc" +static void gen_inb(DisasContext *ctx, TCGv data, int port); +static void gen_outb(DisasContext *ctx, TCGv data, int port); + /* * Arithmetic Instructions */ @@ -1293,9 +1296,8 @@ static bool trans_SBRS(DisasContext *ctx, arg_SBRS *a) static bool trans_SBIC(DisasContext *ctx, arg_SBIC *a) { TCGv data = tcg_temp_new_i32(); - TCGv port = tcg_constant_i32(a->reg); - gen_helper_inb(data, cpu_env, port); + gen_inb(ctx, data, a->reg); tcg_gen_andi_tl(data, data, 1 << a->bit); ctx->skip_cond = TCG_COND_EQ; ctx->skip_var0 = data; @@ -1311,9 +1313,8 @@ static bool trans_SBIC(DisasContext *ctx, arg_SBIC *a) static bool trans_SBIS(DisasContext *ctx, arg_SBIS *a) { TCGv data = tcg_temp_new_i32(); - TCGv port = tcg_constant_i32(a->reg); - gen_helper_inb(data, cpu_env, port); + gen_inb(ctx, data, a->reg); tcg_gen_andi_tl(data, data, 1 << a->bit); ctx->skip_cond = TCG_COND_NE; ctx->skip_var0 = data; @@ -1494,7 +1495,7 @@ 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_st_tl(data, addr, MMU_DATA_IDX, MO_UB); } @@ -1502,11 +1503,18 @@ static void gen_data_store(DisasContext *ctx, TCGv data, TCGv addr) 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); - } else { - tcg_gen_qemu_ld_tl(data, addr, MMU_DATA_IDX, MO_UB); - } + tcg_gen_qemu_ld_tl(data, addr, MMU_DATA_IDX, MO_UB); +} + +static void gen_inb(DisasContext *ctx, TCGv data, int port) +{ + gen_data_load(ctx, data, tcg_constant_i32(port + NUMBER_OF_CPU_REGISTERS)); +} + +static void gen_outb(DisasContext *ctx, TCGv data, int port) +{ + gen_helper_fullwr(tcg_env, data, + tcg_constant_i32(port + NUMBER_OF_CPU_REGISTERS)); } /* @@ -1578,7 +1586,6 @@ static bool trans_LDS(DisasContext *ctx, arg_LDS *a) TCGv Rd = cpu_r[a->rd]; TCGv addr = tcg_temp_new_i32(); TCGv H = cpu_rampD; - a->imm = next_word(ctx); tcg_gen_mov_tl(addr, H); /* addr = H:M:L */ tcg_gen_shli_tl(addr, addr, 16); @@ -1783,7 +1790,6 @@ static bool trans_STS(DisasContext *ctx, arg_STS *a) TCGv Rd = cpu_r[a->rd]; TCGv addr = tcg_temp_new_i32(); TCGv H = cpu_rampD; - a->imm = next_word(ctx); tcg_gen_mov_tl(addr, H); /* addr = H:M:L */ tcg_gen_shli_tl(addr, addr, 16); @@ -2128,9 +2134,8 @@ 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_constant_i32(a->imm); - gen_helper_inb(Rd, cpu_env, port); + gen_inb(ctx, Rd, a->imm); return true; } @@ -2141,9 +2146,8 @@ 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_constant_i32(a->imm); - gen_helper_outb(cpu_env, port, Rd); + gen_outb(ctx, Rd, a->imm); return true; } @@ -2409,11 +2413,10 @@ 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_constant_i32(a->reg); - gen_helper_inb(data, cpu_env, port); + gen_inb(ctx, data, a->reg); tcg_gen_ori_tl(data, data, 1 << a->bit); - gen_helper_outb(cpu_env, port, data); + gen_outb(ctx, data, a->reg); return true; } @@ -2424,11 +2427,10 @@ 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_constant_i32(a->reg); - gen_helper_inb(data, cpu_env, port); + gen_inb(ctx, data, a->reg); tcg_gen_andi_tl(data, data, ~(1 << a->bit)); - gen_helper_outb(cpu_env, port, data); + gen_outb(ctx, data, a->reg); return true; } @@ -2551,7 +2553,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 */ @@ -2577,7 +2579,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; } @@ -2589,7 +2591,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; } @@ -2599,7 +2601,7 @@ static bool trans_WDR(DisasContext *ctx, arg_WDR *a) * * - translate() * - canonicalize_skip() - * - gen_intermediate_code() + * - translate_code() * - restore_state_to_opc() * */ @@ -2608,7 +2610,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; } } @@ -2657,11 +2659,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; @@ -2788,24 +2789,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 avr_cpu_translate_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 3fdc309fbbd..00000000000 --- 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 b31b742c0db..00000000000 --- a/target/cris/cpu-param.h +++ /dev/null @@ -1,16 +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 - -#endif diff --git a/target/cris/cpu-qom.h b/target/cris/cpu-qom.h deleted file mode 100644 index 431a1d536a9..00000000000 --- 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_phases: The parent class' reset phase handlers. - * @vr: Version Register value. - * - * A CRIS CPU model. - */ -struct CRISCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - ResettablePhases parent_phases; - - uint32_t vr; -}; - - -#endif diff --git a/target/cris/cpu.c b/target/cris/cpu.c deleted file mode 100644 index a6a93c23595..00000000000 --- a/target/cris/cpu.c +++ /dev/null @@ -1,361 +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_hold(Object *obj) -{ - CPUState *s = CPU(obj); - CRISCPU *cpu = CRIS_CPU(s); - CRISCPUClass *ccc = CRIS_CPU_GET_CLASS(cpu); - CPUCRISState *env = &cpu->env; - uint32_t vr; - - if (ccc->parent_phases.hold) { - ccc->parent_phases.hold(obj); - } - - 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); - ResettableClass *rc = RESETTABLE_CLASS(oc); - - device_class_set_parent_realize(dc, cris_cpu_realizefn, - &ccc->parent_realize); - - resettable_class_set_parent_phases(rc, NULL, cris_cpu_reset_hold, NULL, - &ccc->parent_phases); - - cc->class_by_name = cris_cpu_class_by_name; - cc->has_work = cris_cpu_has_work; - cc->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 8e37c6e50d1..00000000000 --- a/target/cris/cpu.h +++ /dev/null @@ -1,282 +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); -hwaddr cris_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -#endif - -void cris_cpu_dump_state(CPUState *cs, FILE *f, int flags); - -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, vaddr *pc, - uint64_t *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 9c531f36b42..00000000000 --- 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 fa0a7f0d63a..00000000000 --- 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 25c0ca33a5e..00000000000 --- 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 "gdbstub/helpers.h" - -int crisv10_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) -{ - CRISCPU *cpu = CRIS_CPU(cs); - CPUCRISState *env = &cpu->env; - - 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 c0bf987e3e8..00000000000 --- 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 generated by the core itself. - ERP should point to the insn following the brk. */ - ex_vec = env->trap_vector; - env->pregs[PRV10_BRP] = env->pc; - 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 generated by the core itself. - ERP should point to the insn following the brk. */ - ex_vec = env->trap_vector; - env->pregs[PR_ERP] = env->pc; - 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 spurious 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 3abf6086828..00000000000 --- 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 f370f33486d..00000000000 --- 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 07dc3a5682f..00000000000 --- 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_system_ss = ss.source_set() -cris_system_ss.add(files( - 'helper.c', - 'machine.c', - 'mmu.c', -)) - -target_arch += {'cris': cris_ss} -target_softmmu_arch += {'cris': cris_system_ss} diff --git a/target/cris/mmu.c b/target/cris/mmu.c deleted file mode 100644 index b574ec6e5b9..00000000000 --- 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 d57386ec6cd..00000000000 --- 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 98a9aaf5046..00000000000 --- a/target/cris/op_helper.c +++ /dev/null @@ -1,580 +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" - -//#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 arithmetic, 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 40509c88db1..00000000000 --- 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 42103b55580..00000000000 --- a/target/cris/translate.c +++ /dev/null @@ -1,3279 +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 HELPER_H "helper.h" -#include "exec/helper-info.c.inc" -#undef HELPER_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; - -/* 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) \ - t_gen_mov_env_TN(member, tcg_constant_tl(c)) - -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) -{ - gen_helper_raise_exception(cpu_env, tcg_constant_i32(index)); -} - -static void t_gen_lsl(TCGv d, TCGv a, TCGv b) -{ - TCGv t0, t_31; - - t0 = tcg_temp_new(); - t_31 = tcg_constant_tl(31); - tcg_gen_shl_tl(d, a, b); - - tcg_gen_sub_tl(t0, t_31, b); - tcg_gen_sar_tl(t0, t0, t_31); - tcg_gen_and_tl(t0, t0, d); - tcg_gen_xor_tl(d, d, t0); -} - -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); -} - -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); -} - -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); -} - -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); -} - -/* Extended arithmetic 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); -} - -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); - } -} - -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); - } -} - -/* 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); -} - -/* 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); -} - -/* - * Reverse the bits within each byte. - * - * T0 = ((T0 << 7) & 0x80808080) - * | ((T0 << 5) & 0x40404040) - * | ((T0 << 3) & 0x20202020) - * | ((T0 << 1) & 0x10101010) - * | ((T0 >> 1) & 0x08080808) - * | ((T0 >> 3) & 0x04040404) - * | ((T0 >> 5) & 0x02020202) - * | ((T0 >> 7) & 0x01010101); - */ -static void t_gen_swapr(TCGv d, TCGv s) -{ - 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); - } -} - -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 arithmetic. */ - 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 arithmetic. */ - 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 arithmetic. */ - 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 arithmetic. */ - 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); - } -} - -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); - } - 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); - } - 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); - } - 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_constant_tl(dc->op1); - cris_alu(dc, CC_OP_ADD, - cpu_R[dc->op2], cpu_R[dc->op2], c, 4); - 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_constant_tl(dc->op1); - cris_alu(dc, CC_OP_SUB, - cpu_R[dc->op2], cpu_R[dc->op2], c, 4); - 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_constant_tl(imm); - cris_alu(dc, CC_OP_CMP, - cpu_R[dc->op2], cpu_R[dc->op2], c, 4); - 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_constant_tl(imm); - cris_alu(dc, CC_OP_AND, - cpu_R[dc->op2], cpu_R[dc->op2], c, 4); - 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_constant_tl(imm); - cris_alu(dc, CC_OP_OR, - cpu_R[dc->op2], cpu_R[dc->op2], c, 4); - 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_constant_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]); - 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); - } - 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 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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_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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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_constant_tl(dc->op1); - c2 = tcg_constant_tl(dc->op2); - cris_cc_mask(dc, 0); - gen_helper_movl_sreg_reg(cpu_env, c2, c1); - return 2; -} -static int dec_move_sr(CPUCRISState *env, DisasContext *dc) -{ - TCGv c2, c1; - LOG_DIS("move $s%u, $r%u\n", dc->op2, dc->op1); - c1 = tcg_constant_tl(dc->op1); - c2 = tcg_constant_tl(dc->op2); - cris_cc_mask(dc, 0); - gen_helper_movl_reg_sreg(cpu_env, c1, 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]); - } - } 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; - } - 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]); - } - 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); - } - 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 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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_constant_tl(0); - cris_alu(dc, CC_OP_CMP, - cpu_R[dc->op2], t[1], c, memsize_zz(dc)); - do_postinc(dc, memsize); - 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); - 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); - 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); - 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_new(); - l[1] = tcg_temp_new(); - insn_len = dec_prep_alu_m(env, dc, 0, memsize, l[0], l[1]); - cris_cc_mask(dc, CC_MASK_NZ); - cris_alu(dc, CC_OP_BOUND, cpu_R[dc->op2], l[0], l[1], 4); - do_postinc(dc, memsize); - 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); - 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); - 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); - 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); - 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); - - 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; - } - - 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]); - } - if (nr & 1) { - tcg_gen_mov_tl(cpu_R[dc->op2], 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); - 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_constant_tl(dc->pc + 4); - t_gen_mov_preg_TN(dc, dc->op2, 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_constant_tl(dc->pc + 8); - /* Store the return address in Pd. */ - t_gen_mov_preg_TN(dc, dc->op2, 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_constant_tl(dc->pc + 8 + 4); - /* Store the return address in Pd. */ - t_gen_mov_preg_TN(dc, dc->op2, 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_constant_tl(dc->pc + 4 + 4); - t_gen_mov_preg_TN(dc, dc->op2, 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_constant_tl(dc->pc + 8); - /* Store the return address in Pd. */ - t_gen_mov_preg_TN(dc, dc->op2, 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_constant_tl(dc->pc + 12); - /* Store the return address in Pd. */ - t_gen_mov_preg_TN(dc, dc->op2, 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_constant_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 containing the branch insn will set up env->btarget and evaluate - * env->btaken. When the translation loop exits we will note that the branch - * sequence is broken and let env->dslot be the size of the branch insn (those - * vary in length). - * - * The TB containing the delayslot will have the PC of its real insn (i.e no lsb - * set). It will also expect to have env->dslot setup with the size of the - * delay slot so that env->pc - env->dslot point to the branch insn. This TB - * will execute the dslot and take the branch, either to btarget or just one - * 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 interrupts 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 b7b05179829..00000000000 --- a/target/cris/translate_v10.c.inc +++ /dev/null @@ -1,1276 +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_new(); - TCGv tval = tcg_temp_new(); - TCGv t1 = tcg_temp_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); - - tcg_gen_qemu_st_tl(tval, taddr, mem_index, ctz32(size) | MO_TE); - - gen_set_label(l1); - tcg_gen_shri_tl(t1, t1, 1); /* shift F to P position */ - tcg_gen_or_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], t1); /*P=F*/ -} - -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; - } - - tcg_gen_qemu_st_tl(val, addr, mem_index, ctz32(size) | MO_TE); -} - - -/* 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); - } - - 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_constant_tl(simm); - cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst], - cpu_R[dc->dst], c, 4); - break; - case CRISV10_QIMM_CMPQ: - LOG_DIS("cmpq %d, $r%d\n", simm, dc->dst); - - cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_constant_tl(simm); - cris_alu(dc, CC_OP_CMP, cpu_R[dc->dst], - cpu_R[dc->dst], c, 4); - break; - case CRISV10_QIMM_ADDQ: - LOG_DIS("addq %d, $r%d\n", imm, dc->dst); - - cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_constant_tl(imm); - cris_alu(dc, CC_OP_ADD, cpu_R[dc->dst], - cpu_R[dc->dst], c, 4); - break; - case CRISV10_QIMM_ANDQ: - LOG_DIS("andq %d, $r%d\n", simm, dc->dst); - - cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_constant_tl(simm); - cris_alu(dc, CC_OP_AND, cpu_R[dc->dst], - cpu_R[dc->dst], c, 4); - 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_constant_tl(imm); - if (op) { - cris_alu(dc, CC_OP_ASR, cpu_R[dc->dst], - cpu_R[dc->dst], c, 4); - } else { - /* BTST */ - cris_update_cc_op(dc, CC_OP_FLAGS, 4); - gen_helper_btst(cpu_PR[PR_CCS], cpu_env, cpu_R[dc->dst], - c, cpu_PR[PR_CCS]); - } - 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_constant_tl(imm); - cris_alu(dc, op, cpu_R[dc->dst], - cpu_R[dc->dst], c, 4); - break; - case CRISV10_QIMM_SUBQ: - LOG_DIS("subq %d, $r%d\n", imm, dc->dst); - - cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_constant_tl(imm); - cris_alu(dc, CC_OP_SUB, cpu_R[dc->dst], - cpu_R[dc->dst], c, 4); - break; - case CRISV10_QIMM_ORQ: - LOG_DIS("andq %d, $r%d\n", simm, dc->dst); - - cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_constant_tl(simm); - cris_alu(dc, CC_OP_OR, cpu_R[dc->dst], - cpu_R[dc->dst], c, 4); - 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); -} - -static void dec10_reg_bound(DisasContext *dc, int size) -{ - TCGv t; - - t = tcg_temp_new(); - t_gen_zext(t, cpu_R[dc->src], size); - cris_alu(dc, CC_OP_BOUND, cpu_R[dc->dst], cpu_R[dc->dst], t, 4); -} - -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); -} - - -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); -} - -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); -} - -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); -} - -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); -} - -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); - 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); - } - 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; - } - - 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); - - 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; - - LOG_DIS("move.%d $p%d, [$r%d]\n", dc->size, dc->dst, dc->src); - cris_lock_irq(dc); - - 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; - } - 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); - } else { - gen_store_v10(dc, addr, cpu_PR[dc->dst], size); - } - insn_len += crisv10_post_memaddr(dc, size); - 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); - } -} - -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); - } -} - -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; - } - 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_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; - } - - 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; - } - - 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_constant_tl(0); - cris_alu(dc, CC_OP_CMP, cpu_R[dc->dst], - t[0], c, size); - 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_constant_tl(dc->pc + insn_len); - t_gen_mov_preg_TN(dc, dc->dst, 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_constant_tl(dc->src + 2); - t_gen_mov_env_TN(trap_vector, 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_constant_tl(dc->pc + insn_len); - t_gen_mov_preg_TN(dc, dc->dst, 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. */ - } - 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_constant_tl(dc->pc + insn_len); - t_gen_mov_preg_TN(dc, dc->dst, 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 e757bcb64a6..ca617e33643 100644 --- a/target/hexagon/README +++ b/target/hexagon/README @@ -43,11 +43,9 @@ 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 @@ -86,7 +84,7 @@ tcg_funcs_generated.c.inc 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_helper_A2_add(RdV, tcg_env, RsV, RtV); gen_log_reg_write(ctx, RdN, RdV); } @@ -143,7 +141,7 @@ istruction. const intptr_t VdV_off = ctx_future_vreg_off(ctx, VdN, 1, true); TCGv_ptr VdV = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(VdV, cpu_env, VdV_off); + 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); @@ -152,9 +150,9 @@ istruction. const intptr_t VvV_off = vreg_src_off(ctx, VvN); TCGv_ptr VvV = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(VuV, cpu_env, VuV_off); - tcg_gen_addi_ptr(VvV, cpu_env, VvV_off); - gen_helper_V6_vaddw(cpu_env, VdV, VuV, VvV); + 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 @@ -183,17 +181,24 @@ when the override is present. } We also generate an analyze_ function for each instruction. Currently, -these functions record the writes to registers by calling ctx_log_*. During -gen_start_packet, we invoke the analyze_ function for each instruction in -the packet, and we mark the implicit writes. After the analysis is performed, -we initialize the result register for each of the predicated assignments. +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 *** @@ -277,10 +282,6 @@ For Hexagon Vector eXtensions (HVX), the following fields are used *** Debugging *** -You can turn on a lot of debugging by changing the HEX_DEBUG macro to 1 in -internal.h. This will stream a lot of information as it generates TCG and -executes the code. - To track down nasty issues with Hexagon->TCG generation, we compare the execution results with actual hardware running on a Hexagon Linux target. Run qemu with the "-d cpu" option. Then, we can diff the results and figure @@ -300,8 +301,3 @@ Here are some handy places to set breakpoints The helper function for each instruction is named helper_, so here's an example that will set a breakpoint at the start br helper_A2_add - If you have the HEX_DEBUG macro set, the following will be useful - 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 this_PC == 0xdeadbeef diff --git a/target/hexagon/attribs_def.h.inc b/target/hexagon/attribs_def.h.inc index 21d457fa4a4..9e3a05f8828 100644 --- a/target/hexagon/attribs_def.h.inc +++ b/target/hexagon/attribs_def.h.inc @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2023 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 @@ -117,7 +117,7 @@ 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", "", "") diff --git a/target/hexagon/cpu-param.h b/target/hexagon/cpu-param.h index 71b4a9b83ec..635d509e743 100644 --- a/target/hexagon/cpu-param.h +++ b/target/hexagon/cpu-param.h @@ -19,9 +19,10 @@ #define HEXAGON_CPU_PARAM_H #define TARGET_PAGE_BITS 16 /* 64K pages */ -#define TARGET_LONG_BITS 32 #define TARGET_PHYS_ADDR_SPACE_BITS 36 #define TARGET_VIRT_ADDR_SPACE_BITS 32 +#define TARGET_INSN_START_EXTRA_WORDS 0 + #endif diff --git a/target/hexagon/cpu-qom.h b/target/hexagon/cpu-qom.h new file mode 100644 index 00000000000..0b149bd5fea --- /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 f1559362894..a5a04173ab8 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -19,39 +19,21 @@ #include "qemu/qemu-print.h" #include "cpu.h" #include "internal.h" -#include "exec/exec-all.h" +#include "exec/translation-block.h" #include "qapi/error.h" #include "hw/qdev-properties.h" #include "fpu/softfloat-helpers.h" #include "tcg/tcg.h" #include "exec/gdbstub.h" +#include "accel/tcg/cpu-ops.h" +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 void hexagon_cpu_list_entry(gpointer data, gpointer user_data) -{ - ObjectClass *oc = data; - char *name = g_strdup(object_class_get_name(oc)); - if (g_str_has_suffix(name, HEXAGON_CPU_TYPE_SUFFIX)) { - name[strlen(name) - strlen(HEXAGON_CPU_TYPE_SUFFIX)] = '\0'; - } - qemu_printf(" %s\n", name); - g_free(name); -} - -void hexagon_cpu_list(void) -{ - GSList *list; - list = object_class_get_list_sorted(TYPE_HEXAGON_CPU, false); - qemu_printf("Available CPUs:\n"); - g_slist_foreach(list, hexagon_cpu_list_entry, NULL); - g_slist_free(list); -} - static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model) { ObjectClass *oc; @@ -63,20 +45,16 @@ 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_short_circuit_property = - DEFINE_PROP_BOOL("short-circuit", HexagonCPU, short_circuit, true); +static const 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), +}; const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", @@ -259,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) @@ -272,60 +247,64 @@ 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) +static TCGTBCPUState hexagon_get_tb_cpu_state(CPUState *cs) { - HexagonCPU *cpu = HEXAGON_CPU(cs); - CPUHexagonState *env = &cpu->env; - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); - env->gpr[HEX_REG_PC] = tb->pc; + CPUHexagonState *env = cpu_env(cs); + vaddr pc = env->gpr[HEX_REG_PC]; + uint32_t hex_flags = 0; + + if (pc == env->gpr[HEX_REG_SA0]) { + hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP, 1); + } + if (pc & PCALIGN_MASK) { + hexagon_raise_exception_err(env, HEX_CAUSE_PC_NOT_ALIGNED, 0); + } + + return (TCGTBCPUState){ .pc = pc, .flags = hex_flags }; } -static bool hexagon_cpu_has_work(CPUState *cs) +static void hexagon_cpu_synchronize_from_tb(CPUState *cs, + const TranslationBlock *tb) { - return true; + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); + cpu_env(cs)->gpr[HEX_REG_PC] = tb->pc; } 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_hold(Object *obj) +static void hexagon_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); - HexagonCPU *cpu = HEXAGON_CPU(cs); - HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(cpu); - CPUHexagonState *env = &cpu->env; + HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(obj); + CPUHexagonState *env = cpu_env(cs); if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + 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); + /* Default NaN value: sign bit set, all frac bits set */ + set_float_default_nan_pattern(0b11111111, &env->fp_status); } static void hexagon_cpu_disas_set_info(CPUState *s, disassemble_info *info) { info->print_insn = print_insn_hexagon; + info->endian = BFD_ENDIAN_LITTLE; } static void hexagon_cpu_realize(DeviceState *dev, Error **errp) @@ -342,8 +321,7 @@ static void hexagon_cpu_realize(DeviceState *dev, Error **errp) gdb_register_coprocessor(cs, hexagon_hvx_gdb_read_register, hexagon_hvx_gdb_write_register, - NUM_VREGS + NUM_QREGS, - "hexagon-hvx.xml", 0); + gdb_find_static_feature("hexagon-hvx.xml"), 0); qemu_init_vcpu(cs); cpu_reset(cs); @@ -351,25 +329,28 @@ static void hexagon_cpu_realize(DeviceState *dev, Error **errp) mcc->parent_realize(dev, errp); } -static void hexagon_cpu_init(Object *obj) +static int hexagon_cpu_mmu_index(CPUState *cs, bool ifetch) { - HexagonCPU *cpu = HEXAGON_CPU(obj); - - cpu_set_cpustate_pointers(cpu); - qdev_property_add_static(DEVICE(obj), &hexagon_lldb_compat_property); - qdev_property_add_static(DEVICE(obj), &hexagon_lldb_stack_adjust_property); - qdev_property_add_static(DEVICE(obj), &hexagon_short_circuit_property); + return MMU_USER_IDX; } -#include "hw/core/tcg-cpu-ops.h" +static void hexagon_cpu_init(Object *obj) +{ +} -static const struct TCGCPUOps hexagon_tcg_ops = { +static const TCGCPUOps hexagon_tcg_ops = { + /* MTTCG not yet supported: require strict ordering */ + .guest_default_memory_order = TCG_MO_ALL, + .mttcg_supported = false, .initialize = hexagon_translate_init, + .translate_code = hexagon_translate_code, + .get_tb_cpu_state = hexagon_get_tb_cpu_state, .synchronize_from_tb = hexagon_cpu_synchronize_from_tb, .restore_state_to_opc = hexagon_restore_state_to_opc, + .mmu_index = hexagon_cpu_mmu_index, }; -static void hexagon_cpu_class_init(ObjectClass *c, void *data) +static void hexagon_cpu_class_init(ObjectClass *c, const void *data) { HexagonCPUClass *mcc = HEXAGON_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); @@ -379,17 +360,16 @@ static void hexagon_cpu_class_init(ObjectClass *c, void *data) device_class_set_parent_realize(dc, hexagon_cpu_realize, &mcc->parent_realize); + 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; cc->dump_state = hexagon_dump_state; cc->set_pc = hexagon_cpu_set_pc; cc->get_pc = hexagon_cpu_get_pc; cc->gdb_read_register = hexagon_gdb_read_register; cc->gdb_write_register = hexagon_gdb_write_register; - cc->gdb_num_core_regs = TOTAL_PER_THREAD_REGS; cc->gdb_stop_before_watchpoint = true; cc->gdb_core_xml_file = "hexagon-core.xml"; cc->disas_set_info = hexagon_cpu_disas_set_info; @@ -408,11 +388,13 @@ 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), diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index daef5c3f006..43a854f5172 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -20,13 +20,17 @@ #include "fpu/softfloat-types.h" +#include "cpu-qom.h" +#include "exec/cpu-common.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" +#ifndef CONFIG_USER_ONLY +#error "Hexagon does not support system emulation" +#endif + #define NUM_PREGS 4 #define TOTAL_PER_THREAD_REGS 64 @@ -36,21 +40,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 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") - -void hexagon_cpu_list(void); -#define cpu_list hexagon_cpu_list - #define MMU_USER_IDX 0 typedef struct { @@ -93,12 +84,6 @@ typedef struct CPUArchState { uint8_t slot_cancelled; 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 reg_written[TOTAL_PER_THREAD_REGS]; - MemLog mem_log_stores[STORES_MAX]; float_status fp_status; @@ -127,21 +112,16 @@ 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; ResettablePhases parent_phases; } HexagonCPUClass; struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; + CPUHexagonState env; bool lldb_compat; @@ -153,31 +133,14 @@ struct ArchCPU { FIELD(TB_FLAGS, IS_TIGHT_LOOP, 0, 1) -static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - uint32_t hex_flags = 0; - *pc = env->gpr[HEX_REG_PC]; - *cs_base = 0; - if (*pc == env->gpr[HEX_REG_SA0]) { - hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP, 1); - } - *flags = hex_flags; -} - -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 -} +G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env, + uint32_t exception, + uintptr_t pc); typedef HexagonCPU ArchCPU; void hexagon_translate_init(void); - -#include "exec/cpu-all.h" +void hexagon_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); #endif /* HEXAGON_CPU_H */ diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h index 96fef71729f..ff596e2a94c 100644 --- a/target/hexagon/cpu_bits.h +++ b/target/hexagon/cpu_bits.h @@ -20,13 +20,24 @@ #include "qemu/bitops.h" -#define HEX_EXCP_FETCH_NO_UPAGE 0x012 -#define HEX_EXCP_INVALID_PACKET 0x015 -#define HEX_EXCP_INVALID_OPCODE 0x015 -#define HEX_EXCP_PRIV_NO_UREAD 0x024 -#define HEX_EXCP_PRIV_NO_UWRITE 0x025 +#define PCALIGN 4 +#define PCALIGN_MASK (PCALIGN - 1) -#define HEX_EXCP_TRAP0 0x172 +enum hex_event { + HEX_EVENT_NONE = -1, + HEX_EVENT_TRAP0 = 0x008, +}; + +enum hex_cause { + HEX_CAUSE_NONE = -1, + HEX_CAUSE_TRAP0 = 0x172, + HEX_CAUSE_FETCH_NO_UPAGE = 0x012, + HEX_CAUSE_INVALID_PACKET = 0x015, + HEX_CAUSE_INVALID_OPCODE = 0x015, + HEX_CAUSE_PC_NOT_ALIGNED = 0x01e, + HEX_CAUSE_PRIV_NO_UREAD = 0x024, + HEX_CAUSE_PRIV_NO_UWRITE = 0x025, +}; #define PACKET_WORDS_MAX 4 diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index 946c55cc71d..23deba2426f 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c @@ -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; - -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]; +/* 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) -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; - -#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 */ - -#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__) +/* Include the generated decoder for 32 bit insn */ +#include "decode_normal_generated.c.inc" +#include "decode_hvx_generated.c.inc" -static const DecodeITableEntry decode_legacy_itable[XX_LAST_OPCODE] = { -#include "dectree_generated.h.inc" -}; +/* 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" -#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]; /* @@ -495,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) { @@ -550,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); @@ -593,186 +422,96 @@ 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; \ - /* - * 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. + * 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 void -decode_op(Insn *insn, Opcode tag, uint32_t encoding) +static uint32_t get_duplex_iclass(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); - } - - switch (tag) { -#include "dectree_generated.h.inc" - default: - break; - } - - insn->generate = opcode_genptr[tag]; - - insn->iclass = iclass_bits(encoding); + uint32_t iclass = extract32(encoding, 13, 1); + iclass = deposit32(iclass, 1, 3, extract32(encoding, 29, 3)); + return iclass; } -#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; - } -} +/* + * 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 + */ +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; + +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 */ +}; -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; - } -} - -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; + g_assert_not_reached(); } - return decode_insns_tablewalk(insn, table, encoding); } static void decode_add_endloop_insn(Insn *insn, int loopnum) @@ -916,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; @@ -930,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 @@ -1006,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 c66f5ea64d7..3f3012b978d 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 3b32948a04a..00000000000 --- a/target/hexagon/dectree.py +++ /dev/null @@ -1,403 +0,0 @@ -#!/usr/bin/env python3 - -## -## 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 . -## - -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 05a56d8c10b..c557141f113 100644 --- a/target/hexagon/fma_emu.c +++ b/target/hexagon/fma_emu.c @@ -43,112 +43,51 @@ #define WAY_BIG_EXP 4096 -typedef union { - double f; - uint64_t i; - struct { - uint64_t mant:52; - uint64_t exp:11; - uint64_t sign:1; - }; -} Double; - -typedef union { - float f; - uint32_t i; - struct { - uint32_t mant:23; - uint32_t exp:8; - uint32_t sign:1; - }; -} Float; - static uint64_t float64_getmant(float64 f64) { - Double a = { .i = f64 }; + uint64_t mant = extract64(f64, 0, 52); if (float64_is_normal(f64)) { - return a.mant | 1ULL << 52; + return mant | 1ULL << 52; } if (float64_is_zero(f64)) { return 0; } if (float64_is_denormal(f64)) { - return a.mant; + return mant; } return ~0ULL; } int32_t float64_getexp(float64 f64) { - Double a = { .i = f64 }; + int exp = extract64(f64, 52, 11); if (float64_is_normal(f64)) { - return a.exp; + return exp; } if (float64_is_denormal(f64)) { - return a.exp + 1; + return exp + 1; } return -1; } -static uint64_t float32_getmant(float32 f32) -{ - Float a = { .i = f32 }; - if (float32_is_normal(f32)) { - return a.mant | 1ULL << 23; - } - if (float32_is_zero(f32)) { - return 0; - } - if (float32_is_denormal(f32)) { - return a.mant; - } - return ~0ULL; -} - int32_t float32_getexp(float32 f32) { - Float a = { .i = f32 }; + int exp = float32_getexp_raw(f32); if (float32_is_normal(f32)) { - return a.exp; + return exp; } if (float32_is_denormal(f32)) { - return a.exp + 1; + return exp + 1; } return -1; } -static uint32_t int128_getw0(Int128 x) -{ - return int128_getlo(x); -} - -static uint32_t int128_getw1(Int128 x) -{ - return int128_getlo(x) >> 32; -} - static Int128 int128_mul_6464(uint64_t ai, uint64_t bi) { - Int128 a, b; - uint64_t pp0, pp1a, pp1b, pp1s, pp2; - - a = int128_make64(ai); - b = int128_make64(bi); - pp0 = (uint64_t)int128_getw0(a) * (uint64_t)int128_getw0(b); - pp1a = (uint64_t)int128_getw1(a) * (uint64_t)int128_getw0(b); - pp1b = (uint64_t)int128_getw1(b) * (uint64_t)int128_getw0(a); - pp2 = (uint64_t)int128_getw1(a) * (uint64_t)int128_getw1(b); - - pp1s = pp1a + pp1b; - if ((pp1s < pp1a) || (pp1s < pp1b)) { - pp2 += (1ULL << 32); - } - uint64_t ret_low = pp0 + (pp1s << 32); - if ((ret_low < pp0) || (ret_low < (pp1s << 32))) { - pp2 += 1; - } + uint64_t l, h; - return int128_make128(ret_low, pp2 + (pp1s >> 32)); + mulu64(&l, &h, ai, bi); + return int128_make128(l, h); } static Int128 int128_sub_borrow(Int128 a, Int128 b, int borrow) @@ -369,298 +308,129 @@ float32 infinite_float32(uint8_t sign) } /* Return a maximum finite value with the requested sign */ -static float32 maxfinite_float32(uint8_t sign) +static float64 accum_round_float64(Accum a, float_status *fp_status) { - if (sign) { - return make_float32(SF_MINUS_MAXF); - } else { - return make_float32(SF_MAXF); - } -} - -/* Return a zero value with requested sign */ -static float32 zero_float32(uint8_t sign) -{ - if (sign) { - return make_float32(0x80000000); - } else { - return float32_zero; + uint64_t ret; + + if ((int128_gethi(a.mant) == 0) && (int128_getlo(a.mant) == 0) + && ((a.guard | a.round | a.sticky) == 0)) { + /* result zero */ + switch (fp_status->float_rounding_mode) { + case float_round_down: + return zero_float64(1); + default: + return zero_float64(0); + } } -} - -#define GEN_XF_ROUND(SUFFIX, MANTBITS, INF_EXP, INTERNAL_TYPE) \ -static SUFFIX accum_round_##SUFFIX(Accum a, float_status * fp_status) \ -{ \ - if ((int128_gethi(a.mant) == 0) && (int128_getlo(a.mant) == 0) \ - && ((a.guard | a.round | a.sticky) == 0)) { \ - /* result zero */ \ - switch (fp_status->float_rounding_mode) { \ - case float_round_down: \ - return zero_##SUFFIX(1); \ - default: \ - return zero_##SUFFIX(0); \ - } \ - } \ - /* Normalize right */ \ - /* We want MANTBITS bits of mantissa plus the leading one. */ \ - /* That means that we want MANTBITS+1 bits, or 0x000000000000FF_FFFF */ \ - /* So we need to normalize right while the high word is non-zero and \ - * while the low word is nonzero when masked with 0xffe0_0000_0000_0000 */ \ - while ((int128_gethi(a.mant) != 0) || \ - ((int128_getlo(a.mant) >> (MANTBITS + 1)) != 0)) { \ - a = accum_norm_right(a, 1); \ - } \ - /* \ - * OK, now normalize left \ - * 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 \ - * should be 0 \ - */ \ - while ((int128_getlo(a.mant) & (1ULL << MANTBITS)) == 0) { \ - a = accum_norm_left(a); \ - } \ - /* \ - * OK, now we might need to denormalize because of potential underflow. \ - * We need to do this before rounding, and rounding might make us normal \ - * again \ - */ \ - while (a.exp <= 0) { \ - a = accum_norm_right(a, 1 - a.exp); \ - /* \ - * Do we have underflow? \ - * That's when we get an inexact answer because we ran out of bits \ - * in a denormal. \ - */ \ - if (a.guard || a.round || a.sticky) { \ - float_raise(float_flag_underflow, fp_status); \ - } \ - } \ - /* OK, we're relatively canonical... now we need to round */ \ - if (a.guard || a.round || a.sticky) { \ - float_raise(float_flag_inexact, fp_status); \ - switch (fp_status->float_rounding_mode) { \ - case float_round_to_zero: \ - /* Chop and we're done */ \ - break; \ - case float_round_up: \ - if (a.sign == 0) { \ - a.mant = int128_add(a.mant, int128_one()); \ - } \ - break; \ - case float_round_down: \ - if (a.sign != 0) { \ - a.mant = int128_add(a.mant, int128_one()); \ - } \ - break; \ - default: \ - if (a.round || a.sticky) { \ - /* round up if guard is 1, down if guard is zero */ \ - a.mant = int128_add(a.mant, int128_make64(a.guard)); \ - } else if (a.guard) { \ - /* exactly .5, round up if odd */ \ - a.mant = int128_add(a.mant, int128_and(a.mant, int128_one())); \ - } \ - break; \ - } \ - } \ - /* \ - * OK, now we might have carried all the way up. \ - * So we might need to shr once \ - * at least we know that the lsb should be zero if we rounded and \ - * got a carry out... \ - */ \ - if ((int128_getlo(a.mant) >> (MANTBITS + 1)) != 0) { \ - a = accum_norm_right(a, 1); \ - } \ - /* Overflow? */ \ - if (a.exp >= INF_EXP) { \ - /* Yep, inf result */ \ - float_raise(float_flag_overflow, fp_status); \ - float_raise(float_flag_inexact, fp_status); \ - switch (fp_status->float_rounding_mode) { \ - case float_round_to_zero: \ - return maxfinite_##SUFFIX(a.sign); \ - case float_round_up: \ - if (a.sign == 0) { \ - return infinite_##SUFFIX(a.sign); \ - } else { \ - return maxfinite_##SUFFIX(a.sign); \ - } \ - case float_round_down: \ - if (a.sign != 0) { \ - return infinite_##SUFFIX(a.sign); \ - } else { \ - return maxfinite_##SUFFIX(a.sign); \ - } \ - default: \ - return infinite_##SUFFIX(a.sign); \ - } \ - } \ - /* Underflow? */ \ - if (int128_getlo(a.mant) & (1ULL << MANTBITS)) { \ - /* Leading one means: No, we're normal. So, we should be done... */ \ - INTERNAL_TYPE ret; \ - ret.i = 0; \ - ret.sign = a.sign; \ - ret.exp = a.exp; \ - ret.mant = int128_getlo(a.mant); \ - return ret.i; \ - } \ - assert(a.exp == 1); \ - INTERNAL_TYPE ret; \ - ret.i = 0; \ - ret.sign = a.sign; \ - ret.exp = 0; \ - ret.mant = int128_getlo(a.mant); \ - return ret.i; \ -} - -GEN_XF_ROUND(float64, DF_MANTBITS, DF_INF_EXP, Double) -GEN_XF_ROUND(float32, SF_MANTBITS, SF_INF_EXP, Float) - -static bool is_inf_prod(float64 a, float64 b) -{ - return ((float64_is_infinity(a) && float64_is_infinity(b)) || - (float64_is_infinity(a) && is_finite(b) && (!float64_is_zero(b))) || - (float64_is_infinity(b) && is_finite(a) && (!float64_is_zero(a)))); -} - -static float64 special_fma(float64 a, float64 b, float64 c, - float_status *fp_status) -{ - float64 ret = make_float64(0); - /* - * If A multiplied by B is an exact infinity and C is also an infinity - * but with the opposite sign, FMA returns NaN and raises invalid. + * Normalize right + * We want DF_MANTBITS bits of mantissa plus the leading one. + * That means that we want DF_MANTBITS+1 bits, or 0x000000000000FF_FFFF + * So we need to normalize right while the high word is non-zero and + * while the low word is nonzero when masked with 0xffe0_0000_0000_0000 */ - uint8_t a_sign = float64_is_neg(a); - uint8_t b_sign = float64_is_neg(b); - uint8_t c_sign = float64_is_neg(c); - if (is_inf_prod(a, b) && float64_is_infinity(c)) { - if ((a_sign ^ b_sign) != c_sign) { - ret = make_float64(DF_NAN); - float_raise(float_flag_invalid, fp_status); - return ret; - } - } - if ((float64_is_infinity(a) && float64_is_zero(b)) || - (float64_is_zero(a) && float64_is_infinity(b))) { - ret = make_float64(DF_NAN); - float_raise(float_flag_invalid, fp_status); - return ret; + while ((int128_gethi(a.mant) != 0) || + ((int128_getlo(a.mant) >> (DF_MANTBITS + 1)) != 0)) { + a = accum_norm_right(a, 1); } /* - * If none of the above checks are true and C is a NaN, - * a NaN shall be returned - * If A or B are NaN, a NAN shall be returned. + * OK, now normalize left + * 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 + * should be 0 */ - if (float64_is_any_nan(a) || - float64_is_any_nan(b) || - float64_is_any_nan(c)) { - if (float64_is_any_nan(a) && (fGETBIT(51, a) == 0)) { - float_raise(float_flag_invalid, fp_status); - } - if (float64_is_any_nan(b) && (fGETBIT(51, b) == 0)) { - float_raise(float_flag_invalid, fp_status); - } - if (float64_is_any_nan(c) && (fGETBIT(51, c) == 0)) { - float_raise(float_flag_invalid, fp_status); - } - ret = make_float64(DF_NAN); - return ret; + while ((int128_getlo(a.mant) & (1ULL << DF_MANTBITS)) == 0) { + a = accum_norm_left(a); } /* - * We have checked for adding opposite-signed infinities. - * Other infinities return infinity with the correct sign + * OK, now we might need to denormalize because of potential underflow. + * We need to do this before rounding, and rounding might make us normal + * again */ - if (float64_is_infinity(c)) { - ret = infinite_float64(c_sign); - return ret; + while (a.exp <= 0) { + a = accum_norm_right(a, 1 - a.exp); + /* + * Do we have underflow? + * That's when we get an inexact answer because we ran out of bits + * in a denormal. + */ + if (a.guard || a.round || a.sticky) { + float_raise(float_flag_underflow, fp_status); + } } - if (float64_is_infinity(a) || float64_is_infinity(b)) { - ret = infinite_float64(a_sign ^ b_sign); - return ret; + /* OK, we're relatively canonical... now we need to round */ + if (a.guard || a.round || a.sticky) { + float_raise(float_flag_inexact, fp_status); + switch (fp_status->float_rounding_mode) { + case float_round_to_zero: + /* Chop and we're done */ + break; + case float_round_up: + if (a.sign == 0) { + a.mant = int128_add(a.mant, int128_one()); + } + break; + case float_round_down: + if (a.sign != 0) { + a.mant = int128_add(a.mant, int128_one()); + } + break; + default: + if (a.round || a.sticky) { + /* round up if guard is 1, down if guard is zero */ + a.mant = int128_add(a.mant, int128_make64(a.guard)); + } else if (a.guard) { + /* exactly .5, round up if odd */ + a.mant = int128_add(a.mant, int128_and(a.mant, int128_one())); + } + break; + } } - g_assert_not_reached(); -} - -static float32 special_fmaf(float32 a, float32 b, float32 c, - float_status *fp_status) -{ - float64 aa, bb, cc; - aa = float32_to_float64(a, fp_status); - bb = float32_to_float64(b, fp_status); - cc = float32_to_float64(c, fp_status); - return float64_to_float32(special_fma(aa, bb, cc, fp_status), fp_status); -} - -float32 internal_fmafx(float32 a, float32 b, float32 c, int scale, - float_status *fp_status) -{ - Accum prod; - Accum acc; - Accum result; - accum_init(&prod); - accum_init(&acc); - accum_init(&result); - - uint8_t a_sign = float32_is_neg(a); - uint8_t b_sign = float32_is_neg(b); - uint8_t c_sign = float32_is_neg(c); - if (float32_is_infinity(a) || - float32_is_infinity(b) || - float32_is_infinity(c)) { - return special_fmaf(a, b, c, fp_status); - } - if (float32_is_any_nan(a) || - float32_is_any_nan(b) || - float32_is_any_nan(c)) { - return special_fmaf(a, b, c, fp_status); - } - if ((scale == 0) && (float32_is_zero(a) || float32_is_zero(b))) { - float32 tmp = float32_mul(a, b, fp_status); - tmp = float32_add(tmp, c, fp_status); - return tmp; - } - - /* (a * 2**b) * (c * 2**d) == a*c * 2**(b+d) */ - prod.mant = int128_mul_6464(float32_getmant(a), float32_getmant(b)); - /* - * Note: extracting the mantissa into an int is multiplying by - * 2**23, so adjust here + * OK, now we might have carried all the way up. + * So we might need to shr once + * at least we know that the lsb should be zero if we rounded and + * got a carry out... */ - prod.exp = float32_getexp(a) + float32_getexp(b) - SF_BIAS - 23; - prod.sign = a_sign ^ b_sign; - if (float32_is_zero(a) || float32_is_zero(b)) { - prod.exp = -2 * WAY_BIG_EXP; - } - if ((scale > 0) && float32_is_denormal(c)) { - acc.mant = int128_mul_6464(0, 0); - acc.exp = -WAY_BIG_EXP; - acc.sign = c_sign; - acc.sticky = 1; - result = accum_add(prod, acc); - } else if (!float32_is_zero(c)) { - acc.mant = int128_mul_6464(float32_getmant(c), 1); - acc.exp = float32_getexp(c); - acc.sign = c_sign; - result = accum_add(prod, acc); - } else { - result = prod; + if ((int128_getlo(a.mant) >> (DF_MANTBITS + 1)) != 0) { + a = accum_norm_right(a, 1); + } + /* Overflow? */ + if (a.exp >= DF_INF_EXP) { + /* Yep, inf result */ + float_raise(float_flag_overflow, fp_status); + float_raise(float_flag_inexact, fp_status); + switch (fp_status->float_rounding_mode) { + case float_round_to_zero: + return maxfinite_float64(a.sign); + case float_round_up: + if (a.sign == 0) { + return infinite_float64(a.sign); + } else { + return maxfinite_float64(a.sign); + } + case float_round_down: + if (a.sign != 0) { + return infinite_float64(a.sign); + } else { + return maxfinite_float64(a.sign); + } + default: + return infinite_float64(a.sign); + } } - result.exp += scale; - return accum_round_float32(result, fp_status); -} - -float32 internal_mpyf(float32 a, float32 b, float_status *fp_status) -{ - if (float32_is_zero(a) || float32_is_zero(b)) { - return float32_mul(a, b, fp_status); + /* Underflow? */ + ret = int128_getlo(a.mant); + if (ret & (1ULL << DF_MANTBITS)) { + /* Leading one means: No, we're normal. So, we should be done... */ + ret = deposit64(ret, 52, 11, a.exp); + } else { + assert(a.exp == 1); + ret = deposit64(ret, 52, 11, 0); } - return internal_fmafx(a, b, float32_zero, 0, fp_status); + ret = deposit64(ret, 63, 1, a.sign); + return ret; } float64 internal_mpyhh(float64 a, float64 b, @@ -685,7 +455,7 @@ float64 internal_mpyhh(float64 a, float64 b, float64_is_infinity(b)) { return float64_mul(a, b, fp_status); } - x.mant = int128_mul_6464(accumulated, 1); + x.mant = int128_make64(accumulated); x.sticky = sticky; prod = fGETUWORD(1, float64_getmant(a)) * fGETUWORD(1, float64_getmant(b)); x.mant = int128_add(x.mant, int128_mul_6464(prod, 0x100000000ULL)); diff --git a/target/hexagon/fma_emu.h b/target/hexagon/fma_emu.h index 91591d60503..fed054b609f 100644 --- a/target/hexagon/fma_emu.h +++ b/target/hexagon/fma_emu.h @@ -30,9 +30,6 @@ static inline uint32_t float32_getexp_raw(float32 f32) } int32_t float32_getexp(float32 f32); float32 infinite_float32(uint8_t sign); -float32 internal_fmafx(float32 a, float32 b, float32 c, - int scale, float_status *fp_status); -float32 internal_mpyf(float32 a, float32 b, float_status *fp_status); float64 internal_mpyhh(float64 a, float64 b, unsigned long long int accumulated, float_status *fp_status); diff --git a/target/hexagon/gdbstub.c b/target/hexagon/gdbstub.c index 54d37e006e0..12d6b3bbcbb 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 @@ -22,8 +22,7 @@ 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; @@ -37,16 +36,23 @@ int hexagon_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) 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) { - HexagonCPU *cpu = HEXAGON_CPU(cs); - CPUHexagonState *env = &cpu->env; + CPUHexagonState *env = cpu_env(cs); if (n == HEX_REG_P3_0_ALIASED) { - uint32_t p3_0 = ldtul_p(mem_buf); + 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); } @@ -54,10 +60,19 @@ int hexagon_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) } if (n < TOTAL_PER_THREAD_REGS) { - env->gpr[n] = ldtul_p(mem_buf); + 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(); } @@ -81,8 +96,11 @@ static int gdb_get_qreg(CPUHexagonState *env, GByteArray *mem_buf, int n) return total; } -int hexagon_hvx_gdb_read_register(CPUHexagonState *env, GByteArray *mem_buf, int n) +int hexagon_hvx_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { + HexagonCPU *cpu = HEXAGON_CPU(cs); + CPUHexagonState *env = &cpu->env; + if (n < NUM_VREGS) { return gdb_get_vreg(env, mem_buf, n); } @@ -99,7 +117,7 @@ static int gdb_put_vreg(CPUHexagonState *env, uint8_t *mem_buf, int n) { int i; for (i = 0; i < ARRAY_SIZE(env->VRegs[n].uw); i++) { - env->VRegs[n].uw[i] = ldtul_p(mem_buf); + env->VRegs[n].uw[i] = ldl_le_p(mem_buf); mem_buf += 4; } return MAX_VEC_SIZE_BYTES; @@ -109,14 +127,17 @@ static int gdb_put_qreg(CPUHexagonState *env, uint8_t *mem_buf, int n) { int i; for (i = 0; i < ARRAY_SIZE(env->QRegs[n].uw); i++) { - env->QRegs[n].uw[i] = ldtul_p(mem_buf); + 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(CPUHexagonState *env, uint8_t *mem_buf, int n) +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); } diff --git a/target/hexagon/gen_analyze_funcs.py b/target/hexagon/gen_analyze_funcs.py index c3b521abef7..3ac7cc2cfe5 100755 --- a/target/hexagon/gen_analyze_funcs.py +++ b/target/hexagon/gen_analyze_funcs.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2022-2023 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 @@ -23,162 +23,6 @@ import hex_common -## -## Helpers for gen_analyze_func -## -def is_predicated(tag): - return "A_CONDEXEC" in hex_common.attribdict[tag] - - -def analyze_opn_old(f, tag, regtype, regid, regno): - regN = f"{regtype}{regid}N" - predicated = "true" if is_predicated(tag) else "false" - if regtype == "R": - if regid in {"ss", "tt"}: - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" ctx_log_reg_read_pair(ctx, {regN});\n") - elif regid in {"dd", "ee", "xx", "yy"}: - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" ctx_log_reg_write_pair(ctx, {regN}, {predicated});\n") - elif regid in {"s", "t", "u", "v"}: - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" ctx_log_reg_read(ctx, {regN});\n") - elif regid in {"d", "e", "x", "y"}: - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" ctx_log_reg_write(ctx, {regN}, {predicated});\n") - else: - hex_common.bad_register(regtype, regid) - elif regtype == "P": - if regid in {"s", "t", "u", "v"}: - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" ctx_log_pred_read(ctx, {regN});\n") - elif regid in {"d", "e", "x"}: - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" ctx_log_pred_write(ctx, {regN});\n") - else: - hex_common.bad_register(regtype, regid) - elif regtype == "C": - if regid == "ss": - f.write( - f" const int {regN} = insn->regno[{regno}] " - "+ HEX_REG_SA0;\n" - ) - f.write(f" ctx_log_reg_read_pair(ctx, {regN});\n") - elif regid == "dd": - f.write(f" const int {regN} = insn->regno[{regno}] " "+ HEX_REG_SA0;\n") - f.write(f" ctx_log_reg_write_pair(ctx, {regN}, {predicated});\n") - elif regid == "s": - f.write( - f" const int {regN} = insn->regno[{regno}] " - "+ HEX_REG_SA0;\n" - ) - f.write(f" ctx_log_reg_read(ctx, {regN});\n") - elif regid == "d": - f.write(f" const int {regN} = insn->regno[{regno}] " "+ HEX_REG_SA0;\n") - f.write(f" ctx_log_reg_write(ctx, {regN}, {predicated});\n") - else: - hex_common.bad_register(regtype, regid) - elif regtype == "M": - if regid == "u": - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" ctx_log_reg_read(ctx, {regN});\n") - else: - hex_common.bad_register(regtype, regid) - elif regtype == "V": - newv = "EXT_DFL" - if hex_common.is_new_result(tag): - newv = "EXT_NEW" - elif hex_common.is_tmp_result(tag): - newv = "EXT_TMP" - if regid in {"dd", "xx"}: - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write( - f" ctx_log_vreg_write_pair(ctx, {regN}, {newv}, " f"{predicated});\n" - ) - elif regid in {"uu", "vv"}: - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" ctx_log_vreg_read_pair(ctx, {regN});\n") - elif regid in {"s", "u", "v", "w"}: - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" ctx_log_vreg_read(ctx, {regN});\n") - elif regid in {"d", "x", "y"}: - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" ctx_log_vreg_write(ctx, {regN}, {newv}, " f"{predicated});\n") - else: - hex_common.bad_register(regtype, regid) - elif regtype == "Q": - if regid in {"d", "e", "x"}: - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" ctx_log_qreg_write(ctx, {regN});\n") - elif regid in {"s", "t", "u", "v"}: - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" ctx_log_qreg_read(ctx, {regN});\n") - else: - hex_common.bad_register(regtype, regid) - elif regtype == "G": - if regid in {"dd"}: - f.write(f"// const int {regN} = insn->regno[{regno}];\n") - elif regid in {"d"}: - f.write(f"// const int {regN} = insn->regno[{regno}];\n") - elif regid in {"ss"}: - f.write(f"// const int {regN} = insn->regno[{regno}];\n") - elif regid in {"s"}: - f.write(f"// const int {regN} = insn->regno[{regno}];\n") - else: - hex_common.bad_register(regtype, regid) - elif regtype == "S": - if regid in {"dd"}: - f.write(f"// const int {regN} = insn->regno[{regno}];\n") - elif regid in {"d"}: - f.write(f"// const int {regN} = insn->regno[{regno}];\n") - elif regid in {"ss"}: - f.write(f"// const int {regN} = insn->regno[{regno}];\n") - elif regid in {"s"}: - f.write(f"// const int {regN} = insn->regno[{regno}];\n") - else: - hex_common.bad_register(regtype, regid) - else: - hex_common.bad_register(regtype, regid) - - -def analyze_opn_new(f, tag, regtype, regid, regno): - regN = f"{regtype}{regid}N" - if regtype == "N": - if regid in {"s", "t"}: - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" ctx_log_reg_read(ctx, {regN});\n") - else: - hex_common.bad_register(regtype, regid) - elif regtype == "P": - if regid in {"t", "u", "v"}: - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" ctx_log_pred_read(ctx, {regN});\n") - else: - hex_common.bad_register(regtype, regid) - elif regtype == "O": - if regid == "s": - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" ctx_log_vreg_read(ctx, {regN});\n") - else: - hex_common.bad_register(regtype, regid) - else: - hex_common.bad_register(regtype, regid) - - -def analyze_opn(f, tag, regtype, regid, i): - if hex_common.is_pair(regid): - analyze_opn_old(f, tag, regtype, regid, i) - elif hex_common.is_single(regid): - if hex_common.is_old_val(regtype, regid, tag): - analyze_opn_old(f, tag, regtype, regid, i) - elif hex_common.is_new_val(regtype, regid, tag): - analyze_opn_new(f, tag, regtype, regid, i) - else: - hex_common.bad_register(regtype, regid) - else: - hex_common.bad_register(regtype, regid) - - ## ## Generate the code to analyze the instruction ## For A2_add: Rd32=add(Rs32,Rt32), { RdV=RsV+RtV;} @@ -199,54 +43,55 @@ def gen_analyze_func(f, tag, regs, imms): 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" + ) - i = 0 - ## Analyze all the registers - for regtype, regid in regs: - analyze_opn(f, tag, regtype, regid, i) - i += 1 - - has_generated_helper = not hex_common.skip_qemu_helper( - tag - ) and not hex_common.is_idef_parser_enabled(tag) - - ## Mark HVX instructions with generated helpers - if (has_generated_helper and - "A_CVI" in hex_common.attribdict[tag]): - f.write(" ctx->has_hvx_helper = true;\n") + ## 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_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) - hex_common.read_overrides_file(sys.argv[3]) - hex_common.read_overrides_file(sys.argv[4]) - ## Whether or not idef-parser is enabled is - ## determined by the number of arguments to - ## this script: - ## - ## 5 args. -> not enabled, - ## 6 args. -> idef-parser enabled. - ## - ## The 6:th arg. then holds a list of the successfully - ## parsed instructions. - is_idef_parser_enabled = len(sys.argv) > 6 - if is_idef_parser_enabled: - hex_common.read_idef_parser_enabled_file(sys.argv[5]) - hex_common.calculate_attribs() + args = hex_common.parse_common_args( + "Emit functions analyzing register accesses" + ) tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[-1], "w") as f: - f.write("#ifndef HEXAGON_TCG_FUNCS_H\n") - f.write("#define HEXAGON_TCG_FUNCS_H\n\n") + with open(args.out, "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_TCG_FUNCS_H */\n") + f.write("#endif /* HEXAGON_ANALYZE_FUNCS_C_INC */\n") if __name__ == "__main__": diff --git a/target/hexagon/gen_decodetree.py b/target/hexagon/gen_decodetree.py new file mode 100755 index 00000000000..ce703af41d4 --- /dev/null +++ b/target/hexagon/gen_decodetree.py @@ -0,0 +1,209 @@ +#!/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 +import argparse + +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") + + +def main(): + parser = argparse.ArgumentParser( + description="Emit opaque macro calls with instruction semantics" + ) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("class_to_decode", help="instruction class to decode") + parser.add_argument("out", help="output file") + args = parser.parse_args() + + hex_common.read_semantics_file(args.semantics) + with open(args.out, "w") as f: + gen_decodetree_file(f, args.class_to_decode) + +if __name__ == "__main__": + main() diff --git a/target/hexagon/gen_dectree_import.c b/target/hexagon/gen_dectree_import.c index ee354677fd6..87f20c14f19 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 ce21d3b688e..c1f806ac4b2 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-2023 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,181 +23,14 @@ 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(f"int32_t {regtype}{regid}V") - - -def gen_helper_arg_new(f, regtype, regid, regno): - if regno >= 0: - f.write(", ") - f.write(f"int32_t {regtype}{regid}N") - - -def gen_helper_arg_pair(f, regtype, regid, regno): - if regno >= 0: - f.write(", ") - f.write(f"int64_t {regtype}{regid}V") - - -def gen_helper_arg_ext(f, regtype, regid, regno): - if regno > 0: - f.write(", ") - f.write(f"void *{regtype}{regid}V_void") - - -def gen_helper_arg_ext_pair(f, regtype, regid, regno): - if regno > 0: - f.write(", ") - f.write(f"void *{regtype}{regid}V_void") - - -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: - hex_common.bad_register(regtype, regid) - else: - hex_common.bad_register(regtype, regid) - - -def gen_helper_arg_imm(f, immlett): - f.write(f", int32_t {hex_common.imm_name(immlett)}") - - -def gen_helper_dest_decl(f, regtype, regid, regno, subfield=""): - f.write(f" int32_t {regtype}{regid}V{subfield} = 0;\n") - - -def gen_helper_dest_decl_pair(f, regtype, regid, regno, subfield=""): - f.write(f" int64_t {regtype}{regid}V{subfield} = 0;\n") - - -def gen_helper_dest_decl_ext(f, regtype, regid): - if regtype == "Q": - f.write( - f" /* {regtype}{regid}V is *(MMQReg *)" f"({regtype}{regid}V_void) */\n" - ) - else: - f.write( - f" /* {regtype}{regid}V is *(MMVector *)" - f"({regtype}{regid}V_void) */\n" - ) - - -def gen_helper_dest_decl_ext_pair(f, regtype, regid, regno): - f.write( - f" /* {regtype}{regid}V is *(MMVectorPair *))" - f"{regtype}{regid}V_void) */\n" - ) - - -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: - hex_common.bad_register(regtype, regid) - - -def gen_helper_src_var_ext(f, regtype, regid): - if regtype == "Q": - f.write( - f" /* {regtype}{regid}V is *(MMQReg *)" f"({regtype}{regid}V_void) */\n" - ) - else: - f.write( - f" /* {regtype}{regid}V is *(MMVector *)" - f"({regtype}{regid}V_void) */\n" - ) - - -def gen_helper_src_var_ext_pair(f, regtype, regid, regno): - f.write( - f" /* {regtype}{regid}V{regno} is *(MMVectorPair *)" - f"({regtype}{regid}V{regno}_void) */\n" - ) - - -def gen_helper_return(f, regtype, regid, regno): - f.write(f" return {regtype}{regid}V;\n") - - -def gen_helper_return_pair(f, regtype, regid, regno): - f.write(f" return {regtype}{regid}V;\n") - - -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: - hex_common.bad_register(regtype, regid) - - ## ## Generate the TCG code to call the helper ## For A2_add: Rd32=add(Rs32,Rt32), { RdV=RsV+RtV;} ## 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; ## } ## @@ -205,176 +38,77 @@ def gen_helper_function(f, tag, tagregs, tagimms): regs = tagregs[tag] imms = tagimms[tag] - numresults = 0 - numscalarresults = 0 - numscalarreadwrite = 0 - for regtype, regid 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 - - if numscalarresults > 1: - ## The helper is bogus when there is more than one result - f.write( - f"void HELPER({tag})(CPUHexagonState *env) " f"{{ BOGUS_HELPER({tag}); }}\n" - ) - else: - ## The return type of the function is the type of the destination - ## register (if scalar) - i = 0 + ret_type = hex_common.helper_ret_type(tag, regs).func_arg + + declared = [] + for arg in hex_common.helper_args(tag, regs, imms): + declared.append(arg.func_arg) + + 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: - 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: - hex_common.bad_register(regtype, regid) - i += 1 - - if numscalarresults == 0: - f.write("void") - f.write(f" HELPER({tag})(CPUHexagonState *env") + 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 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: - hex_common.bad_register(regtype, regid) - i += 1 - - ## For conditional instructions, we pass in the destination register - if "A_CONDEXEC" in hex_common.attribdict[tag]: - for regtype, regid in regs: - if hex_common.is_writeonly(regid) and not hex_common.is_hvx_reg( - regtype - ): - gen_helper_arg_opn(f, regtype, regid, i, tag) - i += 1 - - ## Arguments to the helper function are the source regs and immediates - for regtype, regid 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_pkt_has_multi_cof(tag): - f.write(", uint32_t pkt_has_multi_cof") - if (hex_common.need_pkt_need_commit(tag)): - f.write(", uint32_t pkt_need_commit") - - if hex_common.need_PC(tag): - if i > 0: - f.write(", ") - f.write("target_ulong PC") - i += 1 - if hex_common.helper_needs_next_PC(tag): - if i > 0: - f.write(", ") - f.write("target_ulong next_PC") - i += 1 - if hex_common.need_slot(tag): - if i > 0: - f.write(", ") - f.write("uint32_t slotval") - i += 1 - if hex_common.need_part1(tag): - if i > 0: - f.write(", ") - f.write("uint32_t part1") - f.write(")\n{\n") - if hex_common.need_ea(tag): - gen_decl_ea(f) - ## Declare the return variable - i = 0 - if "A_CONDEXEC" not in hex_common.attribdict[tag]: - for regtype, regid in regs: - if hex_common.is_writeonly(regid): - gen_helper_dest_decl_opn(f, regtype, regid, i) - i += 1 - - for regtype, regid 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: - hex_common.bad_register(regtype, regid) - - if hex_common.need_slot(tag): - if "A_LOAD" in hex_common.attribdict[tag]: - f.write(" bool pkt_has_store_s1 = slotval & 0x1;\n") - f.write(" uint32_t slot = slotval >> 1;\n") - - if "A_FPOP" in hex_common.attribdict[tag]: - f.write(" arch_fpop_start(env);\n") - - f.write(f" {hex_common.semdict[tag]}\n") - - if "A_FPOP" in hex_common.attribdict[tag]: - f.write(" arch_fpop_end(env);\n") + ## 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) + + 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 "A_FPOP" in hex_common.attribdict[tag]: + f.write(hex_common.code_fmt(f"""\ + arch_fpop_start(env); + """)) + + f.write(hex_common.code_fmt(f"""\ + {hex_common.semdict[tag]} + """)) + + if "A_FPOP" in hex_common.attribdict[tag]: + f.write(hex_common.code_fmt(f"""\ + arch_fpop_end(env); + """)) + + ## 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()}; + """)) - ## Save/return the return variable - for regtype, regid 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 + f.write("}\n\n") + ## End of the helper definition def main(): - hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) - hex_common.read_overrides_file(sys.argv[3]) - hex_common.read_overrides_file(sys.argv[4]) - ## Whether or not idef-parser is enabled is - ## determined by the number of arguments to - ## this script: - ## - ## 5 args. -> not enabled, - ## 6 args. -> idef-parser enabled. - ## - ## The 6:th arg. then holds a list of the successfully - ## parsed instructions. - is_idef_parser_enabled = len(sys.argv) > 6 - if is_idef_parser_enabled: - hex_common.read_idef_parser_enabled_file(sys.argv[5]) - hex_common.calculate_attribs() + args = hex_common.parse_common_args( + "Emit helper function definitions for each instruction" + ) tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - output_file = sys.argv[-1] - with open(output_file, "w") as f: + with open(args.out, "w") as f: for tag in hex_common.tags: ## Skip the priv instructions if "A_PRIV" in hex_common.attribdict[tag]: diff --git a/target/hexagon/gen_helper_protos.py b/target/hexagon/gen_helper_protos.py index 131043795a7..77f8e0a6a32 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-2023 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,39 +22,6 @@ 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, i): - if hex_common.is_pair(regid): - f.write(f", {def_helper_types_pair[regtype]}") - elif hex_common.is_single(regid): - f.write(f", {def_helper_types[regtype]}") - else: - hex_common.bad_register(regtype, regid) - - ## ## Generate the DEF_HELPER prototype for an instruction ## For A2_add: Rd32=add(Rs32,Rt32) @@ -65,141 +32,33 @@ def gen_helper_prototype(f, tag, tagregs, tagimms): regs = tagregs[tag] imms = tagimms[tag] - numresults = 0 - numscalarresults = 0 - numscalarreadwrite = 0 - for regtype, regid 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 - - if numscalarresults > 1: - ## The helper is bogus when there is more than one result - f.write(f"DEF_HELPER_1({tag}, void, env)\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_pkt_has_multi_cof(tag): - def_helper_size += 1 - if hex_common.need_pkt_need_commit(tag): - def_helper_size += 1 - if hex_common.need_part1(tag): - def_helper_size += 1 - if hex_common.need_slot(tag): - def_helper_size += 1 - if hex_common.need_PC(tag): - def_helper_size += 1 - if hex_common.helper_needs_next_PC(tag): - def_helper_size += 1 - if hex_common.need_condexec_reg(tag, regs): - def_helper_size += 1 - f.write(f"DEF_HELPER_{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_pkt_has_multi_cof(tag): - def_helper_size += 1 - if hex_common.need_pkt_need_commit(tag): - def_helper_size += 1 - if hex_common.need_part1(tag): - def_helper_size += 1 - if hex_common.need_slot(tag): - def_helper_size += 1 - if hex_common.need_PC(tag): - def_helper_size += 1 - if hex_common.need_condexec_reg(tag, regs): - def_helper_size += 1 - if hex_common.helper_needs_next_PC(tag): - def_helper_size += 1 - f.write(f"DEF_HELPER_{def_helper_size}({tag}") - - ## 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 in regs: - if hex_common.is_written(regid): - if not hex_common.is_hvx_reg(regtype): - gen_def_helper_opn(f, tag, regtype, regid, i) - i += 1 + declared = [] + ret_type = hex_common.helper_ret_type(tag, regs).proto_arg + declared.append(ret_type) - ## Put the env between the outputs and inputs - f.write(", env") - i += 1 + for arg in hex_common.helper_args(tag, regs, imms): + declared.append(arg.proto_arg) - # Second pass - for regtype, regid in regs: - if hex_common.is_written(regid): - if hex_common.is_hvx_reg(regtype): - gen_def_helper_opn(f, tag, regtype, regid, i) - i += 1 + arguments = ", ".join(declared) - ## For conditional instructions, we pass in the destination register - if "A_CONDEXEC" in hex_common.attribdict[tag]: - for regtype, regid in regs: - if hex_common.is_writeonly(regid) and not hex_common.is_hvx_reg( - regtype - ): - gen_def_helper_opn(f, tag, regtype, regid, i) - i += 1 - - ## Generate the qemu type for each input operand (regs and immediates) - for regtype, regid 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, i) - i += 1 - for immlett, bits, immshift in imms: - f.write(", s32") - - ## Add the arguments for the instruction pkt_has_multi_cof, - ## pkt_needs_commit, PC, next_PC, slot, and part1 (if needed) - if hex_common.need_pkt_has_multi_cof(tag): - f.write(", i32") - if hex_common.need_pkt_need_commit(tag): - f.write(', i32') - if hex_common.need_PC(tag): - f.write(", i32") - if hex_common.helper_needs_next_PC(tag): - f.write(", i32") - if hex_common.need_slot(tag): - f.write(", i32") - if hex_common.need_part1(tag): - f.write(" , i32") - f.write(")\n") + ## 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: + f.write(f"DEF_HELPER_FLAGS_{len(declared) - 1}({tag}, " + f"TCG_CALL_NO_RWG_SE, {arguments})\n") def main(): - hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) - hex_common.read_overrides_file(sys.argv[3]) - hex_common.read_overrides_file(sys.argv[4]) - ## Whether or not idef-parser is enabled is - ## determined by the number of arguments to - ## this script: - ## - ## 5 args. -> not enabled, - ## 6 args. -> idef-parser enabled. - ## - ## The 6:th arg. then holds a list of the successfully - ## parsed instructions. - is_idef_parser_enabled = len(sys.argv) > 6 - if is_idef_parser_enabled: - hex_common.read_idef_parser_enabled_file(sys.argv[5]) - hex_common.calculate_attribs() + args = hex_common.parse_common_args( + "Emit helper function prototypes for each instruction" + ) tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - output_file = sys.argv[-1] - with open(output_file, "w") as f: + with open(args.out, "w") as f: for tag in hex_common.tags: ## Skip the priv instructions if "A_PRIV" in hex_common.attribdict[tag]: diff --git a/target/hexagon/gen_idef_parser_funcs.py b/target/hexagon/gen_idef_parser_funcs.py index f4518e653f5..2f6e826f76d 100644 --- a/target/hexagon/gen_idef_parser_funcs.py +++ b/target/hexagon/gen_idef_parser_funcs.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2023 rev.ng Labs Srl. All Rights Reserved. +## 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 @@ -20,6 +20,7 @@ import sys import re import string +import argparse from io import StringIO import hex_common @@ -43,14 +44,20 @@ ## them are inputs ("in" prefix), while some others are outputs. ## def main(): - hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) + parser = argparse.ArgumentParser( + "Emit instruction implementations that can be fed to idef-parser" + ) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("out", help="output file") + args = parser.parse_args() + hex_common.read_semantics_file(args.semantics) hex_common.calculate_attribs() + hex_common.init_registers() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[3], "w") as f: - f.write('#include "macros.inc"\n\n') + with open(args.out, "w") as f: + f.write('#include "macros.h.inc"\n\n') for tag in hex_common.tags: ## Skip the priv instructions @@ -132,22 +139,9 @@ def main(): arguments = [] for regtype, regid in regs: - prefix = "in " if hex_common.is_read(regid) else "" - - is_pair = hex_common.is_pair(regid) - is_single_old = hex_common.is_single(regid) and hex_common.is_old_val( - regtype, regid, tag - ) - is_single_new = hex_common.is_single(regid) and hex_common.is_new_val( - regtype, regid, tag - ) - - if is_pair or is_single_old: - arguments.append(f"{prefix}{regtype}{regid}V") - elif is_single_new: - arguments.append(f"{prefix}{regtype}{regid}N") - else: - hex_common.bad_register(regtype, regid) + 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)) diff --git a/target/hexagon/gen_op_attribs.py b/target/hexagon/gen_op_attribs.py index 41074b85738..bbbb02df3a2 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-2023 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 @@ -21,17 +21,23 @@ import re import string import hex_common +import argparse def main(): - hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) + parser = argparse.ArgumentParser( + "Emit opaque macro calls containing instruction attributes" + ) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("out", help="output file") + args = parser.parse_args() + hex_common.read_semantics_file(args.semantics) hex_common.calculate_attribs() ## ## Generate all the attributes associated with each instruction ## - with open(sys.argv[3], "w") as f: + with open(args.out, "w") as f: for tag in hex_common.tags: f.write( f"OP_ATTRIB({tag},ATTRIBS(" diff --git a/target/hexagon/gen_op_regs.py b/target/hexagon/gen_op_regs.py deleted file mode 100755 index a8a7712129c..00000000000 --- a/target/hexagon/gen_op_regs.py +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env python3 - -## -## 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 . -## - -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(full=True) - 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, _, 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( - f'REGINFO({tag},"{regids}",\t/*RD:*/\t"{",".join(rregs)}",' - f'\t/*WR:*/\t"{",".join(wregs)}")\n' - ) - - for tag in hex_common.tags: - imms = tagimms[tag] - f.write(f"IMMINFO({tag}") - if not imms: - f.write(""",'u',0,0,'U',0,0""") - for sign, size, shamt in imms: - if sign == "r": - sign = "s" - if not shamt: - shamt = "0" - f.write(f""",'{sign}',{size},{shamt}""") - if len(imms) == 1: - if sign.isupper(): - myu = "u" - else: - myu = "U" - f.write(f""",'{myu}',0,0""") - f.write(")\n") - - -if __name__ == "__main__": - main() diff --git a/target/hexagon/gen_opcodes_def.py b/target/hexagon/gen_opcodes_def.py index cddd868fe33..94a19ff412e 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-2023 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 @@ -21,15 +21,22 @@ import re import string import hex_common +import argparse def main(): - hex_common.read_semantics_file(sys.argv[1]) + parser = argparse.ArgumentParser( + description="Emit opaque macro calls with instruction names" + ) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("out", help="output file") + args = parser.parse_args() + hex_common.read_semantics_file(args.semantics) ## ## Generate a list of all the opcodes ## - with open(sys.argv[3], "w") as f: + with open(args.out, "w") as f: for tag in hex_common.tags: f.write(f"OPCODE({tag}),\n") diff --git a/target/hexagon/gen_printinsn.py b/target/hexagon/gen_printinsn.py index e570bd7c6af..d5f969960ad 100755 --- a/target/hexagon/gen_printinsn.py +++ b/target/hexagon/gen_printinsn.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2023 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 @@ -21,6 +21,7 @@ import re import string import hex_common +import argparse ## @@ -96,12 +97,17 @@ def spacify(s): def main(): - hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) + parser = argparse.ArgumentParser( + "Emit opaque macro calls with information for printing string representations of instrucions" + ) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("out", help="output file") + args = parser.parse_args() + hex_common.read_semantics_file(args.semantics) immext_casere = re.compile(r"IMMEXT\(([A-Za-z])") - with open(sys.argv[3], "w") as f: + with open(args.out, "w") as f: for tag in hex_common.tags: if not hex_common.behdict[tag]: continue diff --git a/target/hexagon/gen_shortcode.py b/target/hexagon/gen_shortcode.py deleted file mode 100755 index deb94446c4b..00000000000 --- a/target/hexagon/gen_shortcode.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python3 - -## -## 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 . -## - -import sys -import re -import string -import hex_common - - -def gen_shortcode(f, tag): - f.write(f"DEF_SHORTCODE({tag}, {hex_common.semdict[tag]})\n") - - -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 d78d99d1559..8a3b801287c 100644 --- a/target/hexagon/gen_tcg.h +++ b/target/hexagon/gen_tcg.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2023 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,15 +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]); \ + gen_helper_fcircadd(RxV, RxV, ireg, MuV, CS); \ } while (0) /* Instructions with multiple definitions */ @@ -113,7 +112,7 @@ 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; \ } while (0) @@ -427,7 +426,7 @@ 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; \ } while (0) @@ -591,8 +590,8 @@ */ #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) @@ -614,7 +613,7 @@ #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); \ } while (0) @@ -629,7 +628,7 @@ #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); \ } while (0) @@ -1205,122 +1204,122 @@ /* 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) \ @@ -1366,7 +1365,10 @@ 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(cpu_env, excp); \ + TCGv excp = tcg_constant_tl(HEX_EVENT_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 f998ef09920..299a39b1aa0 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-2023 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 @@ -21,16 +21,22 @@ import re import string import hex_common +import argparse def main(): - hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) + parser = argparse.ArgumentParser( + "Emit opaque macro calls with instruction semantics" + ) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("out", help="output file") + args = parser.parse_args() + hex_common.read_semantics_file(args.semantics) hex_common.calculate_attribs() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[3], "w") as f: + with open(args.out, "w") as f: f.write("#ifndef HEXAGON_FUNC_TABLE_H\n") f.write("#define HEXAGON_FUNC_TABLE_H\n\n") diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py index fe29d83d4d3..c2ba91ddc04 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-2023 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,461 +23,13 @@ import hex_common -## -## Helpers for gen_tcg_func -## -def gen_decl_ea_tcg(f, tag): - f.write(" TCGv EA G_GNUC_UNUSED = tcg_temp_new();\n") - - -def genptr_decl_pair_writable(f, tag, regtype, regid, regno): - regN = f"{regtype}{regid}N" - if regtype == "R": - f.write(f" const int {regN} = insn->regno[{regno}];\n") - elif regtype == "C": - f.write(f" const int {regN} = insn->regno[{regno}] + HEX_REG_SA0;\n") - else: - hex_common.bad_register(regtype, regid) - f.write(f" TCGv_i64 {regtype}{regid}V = " f"get_result_gpr_pair(ctx, {regN});\n") - - -def genptr_decl_writable(f, tag, regtype, regid, regno): - regN = f"{regtype}{regid}N" - if regtype == "R": - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" TCGv {regtype}{regid}V = get_result_gpr(ctx, {regN});\n") - elif regtype == "C": - f.write(f" const int {regN} = insn->regno[{regno}] + HEX_REG_SA0;\n") - f.write(f" TCGv {regtype}{regid}V = get_result_gpr(ctx, {regN});\n") - elif regtype == "P": - f.write(f" const int {regN} = insn->regno[{regno}];\n") - f.write(f" TCGv {regtype}{regid}V = tcg_temp_new();\n") - else: - hex_common.bad_register(regtype, regid) - - -def genptr_decl(f, tag, regtype, regid, regno): - regN = f"{regtype}{regid}N" - if regtype == "R": - if regid in {"ss", "tt"}: - f.write(f" TCGv_i64 {regtype}{regid}V = tcg_temp_new_i64();\n") - f.write(f" const int {regN} = insn->regno[{regno}];\n") - 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( - f" TCGv {regtype}{regid}V = " f"hex_gpr[insn->regno[{regno}]];\n" - ) - elif regid in {"d", "e", "x", "y"}: - genptr_decl_writable(f, tag, regtype, regid, regno) - else: - hex_common.bad_register(regtype, regid) - elif regtype == "P": - if regid in {"s", "t", "u", "v"}: - f.write( - f" TCGv {regtype}{regid}V = " f"hex_pred[insn->regno[{regno}]];\n" - ) - elif regid in {"d", "e", "x"}: - genptr_decl_writable(f, tag, regtype, regid, regno) - else: - hex_common.bad_register(regtype, regid) - elif regtype == "C": - if regid == "ss": - f.write(f" TCGv_i64 {regtype}{regid}V = " f"tcg_temp_new_i64();\n") - f.write(f" const int {regN} = insn->regno[{regno}] + " "HEX_REG_SA0;\n") - elif regid == "dd": - genptr_decl_pair_writable(f, tag, regtype, regid, regno) - elif regid == "s": - f.write(f" TCGv {regtype}{regid}V = tcg_temp_new();\n") - f.write( - f" const int {regtype}{regid}N = insn->regno[{regno}] + " - "HEX_REG_SA0;\n" - ) - elif regid == "d": - genptr_decl_writable(f, tag, regtype, regid, regno) - else: - hex_common.bad_register(regtype, regid) - elif regtype == "M": - if regid == "u": - f.write(f" const int {regtype}{regid}N = " f"insn->regno[{regno}];\n") - f.write( - f" TCGv {regtype}{regid}V = hex_gpr[{regtype}{regid}N + " - "HEX_REG_M0];\n" - ) - else: - hex_common.bad_register(regtype, regid) - elif regtype == "V": - if regid in {"dd"}: - f.write(f" const int {regtype}{regid}N = " f"insn->regno[{regno}];\n") - f.write(f" const intptr_t {regtype}{regid}V_off =\n") - if hex_common.is_tmp_result(tag): - f.write( - f" ctx_tmp_vreg_off(ctx, {regtype}{regid}N, 2, " "true);\n" - ) - else: - f.write(f" ctx_future_vreg_off(ctx, {regtype}{regid}N,") - f.write(" 2, true);\n") - if not hex_common.skip_qemu_helper(tag): - f.write(f" TCGv_ptr {regtype}{regid}V = " "tcg_temp_new_ptr();\n") - f.write( - f" tcg_gen_addi_ptr({regtype}{regid}V, cpu_env, " - f"{regtype}{regid}V_off);\n" - ) - elif regid in {"uu", "vv", "xx"}: - f.write(f" const int {regtype}{regid}N = " f"insn->regno[{regno}];\n") - f.write(f" const intptr_t {regtype}{regid}V_off =\n") - f.write(f" offsetof(CPUHexagonState, {regtype}{regid}V);\n") - if not hex_common.skip_qemu_helper(tag): - f.write(f" TCGv_ptr {regtype}{regid}V = " "tcg_temp_new_ptr();\n") - f.write( - f" tcg_gen_addi_ptr({regtype}{regid}V, cpu_env, " - f"{regtype}{regid}V_off);\n" - ) - elif regid in {"s", "u", "v", "w"}: - f.write(f" const int {regtype}{regid}N = " f"insn->regno[{regno}];\n") - f.write(f" const intptr_t {regtype}{regid}V_off =\n") - f.write(f" vreg_src_off(ctx, {regtype}{regid}N);\n") - if not hex_common.skip_qemu_helper(tag): - f.write(f" TCGv_ptr {regtype}{regid}V = " "tcg_temp_new_ptr();\n") - elif regid in {"d", "x", "y"}: - f.write(f" const int {regtype}{regid}N = " f"insn->regno[{regno}];\n") - f.write(f" const intptr_t {regtype}{regid}V_off =\n") - if regid == "y": - f.write(" offsetof(CPUHexagonState, vtmp);\n") - elif hex_common.is_tmp_result(tag): - f.write( - f" ctx_tmp_vreg_off(ctx, {regtype}{regid}N, 1, " "true);\n" - ) - else: - f.write(f" ctx_future_vreg_off(ctx, {regtype}{regid}N,") - f.write(" 1, true);\n") - - if not hex_common.skip_qemu_helper(tag): - f.write(f" TCGv_ptr {regtype}{regid}V = " "tcg_temp_new_ptr();\n") - f.write( - f" tcg_gen_addi_ptr({regtype}{regid}V, cpu_env, " - f"{regtype}{regid}V_off);\n" - ) - else: - hex_common.bad_register(regtype, regid) - elif regtype == "Q": - if regid in {"d", "e", "x"}: - f.write(f" const int {regtype}{regid}N = " f"insn->regno[{regno}];\n") - f.write(f" const intptr_t {regtype}{regid}V_off =\n") - f.write(f" get_result_qreg(ctx, {regtype}{regid}N);\n") - if not hex_common.skip_qemu_helper(tag): - f.write(f" TCGv_ptr {regtype}{regid}V = " "tcg_temp_new_ptr();\n") - f.write( - f" tcg_gen_addi_ptr({regtype}{regid}V, cpu_env, " - f"{regtype}{regid}V_off);\n" - ) - elif regid in {"s", "t", "u", "v"}: - f.write(f" const int {regtype}{regid}N = " f"insn->regno[{regno}];\n") - f.write(f" const intptr_t {regtype}{regid}V_off =\n") - f.write( - f" offsetof(CPUHexagonState, " f"QRegs[{regtype}{regid}N]);\n" - ) - if not hex_common.skip_qemu_helper(tag): - f.write(f" TCGv_ptr {regtype}{regid}V = " "tcg_temp_new_ptr();\n") - else: - hex_common.bad_register(regtype, regid) - else: - hex_common.bad_register(regtype, regid) - - -def genptr_decl_new(f, tag, regtype, regid, regno): - if regtype == "N": - if regid in {"s", "t"}: - f.write( - f" TCGv {regtype}{regid}N = " - f"get_result_gpr(ctx, insn->regno[{regno}]);\n" - ) - else: - hex_common.bad_register(regtype, regid) - elif regtype == "P": - if regid in {"t", "u", "v"}: - f.write( - f" TCGv {regtype}{regid}N = " - f"ctx->new_pred_value[insn->regno[{regno}]];\n" - ) - else: - hex_common.bad_register(regtype, regid) - elif regtype == "O": - if regid == "s": - f.write( - f" const intptr_t {regtype}{regid}N_num = " - f"insn->regno[{regno}];\n" - ) - if hex_common.skip_qemu_helper(tag): - f.write(f" const intptr_t {regtype}{regid}N_off =\n") - f.write(" ctx_future_vreg_off(ctx, " f"{regtype}{regid}N_num,") - f.write(" 1, true);\n") - else: - f.write( - f" TCGv {regtype}{regid}N = " - f"tcg_constant_tl({regtype}{regid}N_num);\n" - ) - else: - hex_common.bad_register(regtype, regid) - else: - hex_common.bad_register(regtype, regid) - - -def genptr_decl_opn(f, tag, regtype, regid, 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: - hex_common.bad_register(regtype, regid) - else: - hex_common.bad_register(regtype, regid) - - -def genptr_decl_imm(f, immlett): - if immlett.isupper(): - i = 1 - else: - i = 0 - f.write(f" int {hex_common.imm_name(immlett)} = insn->immed[{i}];\n") - - -def genptr_src_read(f, tag, regtype, regid): - if regtype == "R": - if regid in {"ss", "tt", "xx", "yy"}: - f.write( - f" tcg_gen_concat_i32_i64({regtype}{regid}V, " - f"hex_gpr[{regtype}{regid}N],\n" - ) - f.write( - f" hex_gpr[{regtype}" - f"{regid}N + 1]);\n" - ) - elif regid in {"x", "y"}: - ## For read/write registers, we need to get the original value into - ## the result TCGv. For conditional instructions, this is done in - ## gen_start_packet. For unconditional instructions, we do it here. - if "A_CONDEXEC" not in hex_common.attribdict[tag]: - f.write( - f" tcg_gen_mov_tl({regtype}{regid}V, " - f"hex_gpr[{regtype}{regid}N]);\n" - ) - elif regid not in {"s", "t", "u", "v"}: - hex_common.bad_register(regtype, regid) - elif regtype == "P": - if regid == "x": - f.write( - f" tcg_gen_mov_tl({regtype}{regid}V, " - f"hex_pred[{regtype}{regid}N]);\n" - ) - elif regid not in {"s", "t", "u", "v"}: - hex_common.bad_register(regtype, regid) - elif regtype == "C": - if regid == "ss": - f.write( - f" gen_read_ctrl_reg_pair(ctx, {regtype}{regid}N, " - f"{regtype}{regid}V);\n" - ) - elif regid == "s": - f.write( - f" gen_read_ctrl_reg(ctx, {regtype}{regid}N, " - f"{regtype}{regid}V);\n" - ) - else: - hex_common.bad_register(regtype, regid) - elif regtype == "M": - if regid != "u": - hex_common.bad_register(regtype, regid) - elif regtype == "V": - if regid in {"uu", "vv", "xx"}: - f.write(f" tcg_gen_gvec_mov(MO_64, {regtype}{regid}V_off,\n") - f.write(f" vreg_src_off(ctx, {regtype}{regid}N),\n") - f.write(" sizeof(MMVector), sizeof(MMVector));\n") - f.write(" tcg_gen_gvec_mov(MO_64,\n") - f.write(f" {regtype}{regid}V_off + sizeof(MMVector),\n") - f.write(f" vreg_src_off(ctx, {regtype}{regid}N ^ 1),\n") - f.write(" sizeof(MMVector), sizeof(MMVector));\n") - elif regid in {"s", "u", "v", "w"}: - if not hex_common.skip_qemu_helper(tag): - f.write( - f" tcg_gen_addi_ptr({regtype}{regid}V, cpu_env, " - f"{regtype}{regid}V_off);\n" - ) - elif regid in {"x", "y"}: - f.write(f" tcg_gen_gvec_mov(MO_64, {regtype}{regid}V_off,\n") - f.write(f" vreg_src_off(ctx, {regtype}{regid}N),\n") - f.write(" sizeof(MMVector), sizeof(MMVector));\n") - else: - hex_common.bad_register(regtype, regid) - elif regtype == "Q": - if regid in {"s", "t", "u", "v"}: - if not hex_common.skip_qemu_helper(tag): - f.write( - f" tcg_gen_addi_ptr({regtype}{regid}V, cpu_env, " - f"{regtype}{regid}V_off);\n" - ) - elif regid in {"x"}: - f.write(f" tcg_gen_gvec_mov(MO_64, {regtype}{regid}V_off,\n") - f.write( - f" offsetof(CPUHexagonState, " f"QRegs[{regtype}{regid}N]),\n" - ) - f.write(" sizeof(MMQReg), sizeof(MMQReg));\n") - else: - hex_common.bad_register(regtype, regid) - else: - hex_common.bad_register(regtype, regid) - - -def genptr_src_read_new(f, regtype, regid): - if regtype == "N": - if regid not in {"s", "t"}: - hex_common.bad_register(regtype, regid) - elif regtype == "P": - if regid not in {"t", "u", "v"}: - hex_common.bad_register(regtype, regid) - elif regtype == "O": - if regid != "s": - hex_common.bad_register(regtype, regid) - else: - hex_common.bad_register(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: - hex_common.bad_register(regtype, regid) - else: - hex_common.bad_register(regtype, regid) - - -def gen_helper_call_opn(f, tag, regtype, regid, i): - if i > 0: - f.write(", ") - if hex_common.is_pair(regid): - f.write(f"{regtype}{regid}V") - elif hex_common.is_single(regid): - if hex_common.is_old_val(regtype, regid, tag): - f.write(f"{regtype}{regid}V") - elif hex_common.is_new_val(regtype, regid, tag): - f.write(f"{regtype}{regid}N") - else: - hex_common.bad_register(regtype, regid) - else: - hex_common.bad_register(regtype, regid) - - -def gen_helper_decl_imm(f, immlett): - f.write( - f" TCGv tcgv_{hex_common.imm_name(immlett)} = " - f"tcg_constant_tl({hex_common.imm_name(immlett)});\n" - ) - - -def gen_helper_call_imm(f, immlett): - f.write(f", tcgv_{hex_common.imm_name(immlett)}") - - -def genptr_dst_write_pair(f, tag, regtype, regid): - f.write(f" gen_log_reg_write_pair(ctx, {regtype}{regid}N, " - f"{regtype}{regid}V);\n") - - -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"}: - f.write( - f" gen_log_reg_write(ctx, {regtype}{regid}N, " - f"{regtype}{regid}V);\n" - ) - else: - hex_common.bad_register(regtype, regid) - elif regtype == "P": - if regid in {"d", "e", "x"}: - f.write( - f" gen_log_pred_write(ctx, {regtype}{regid}N, " - f"{regtype}{regid}V);\n" - ) - else: - hex_common.bad_register(regtype, regid) - elif regtype == "C": - if regid == "dd": - f.write( - f" gen_write_ctrl_reg_pair(ctx, {regtype}{regid}N, " - f"{regtype}{regid}V);\n" - ) - elif regid == "d": - f.write( - f" gen_write_ctrl_reg(ctx, {regtype}{regid}N, " - f"{regtype}{regid}V);\n" - ) - else: - hex_common.bad_register(regtype, regid) - else: - hex_common.bad_register(regtype, regid) - - -def genptr_dst_write_ext(f, tag, regtype, regid, newv="EXT_DFL"): - if regtype == "V": - if regid in {"xx"}: - f.write( - f" gen_log_vreg_write_pair(ctx, {regtype}{regid}V_off, " - f"{regtype}{regid}N, {newv});\n" - ) - elif regid in {"y"}: - f.write( - f" gen_log_vreg_write(ctx, {regtype}{regid}V_off, " - f"{regtype}{regid}N, {newv});\n" - ) - elif regid not in {"dd", "d", "x"}: - hex_common.bad_register(regtype, regid) - elif regtype == "Q": - if regid not in {"d", "e", "x"}: - hex_common.bad_register(regtype, regid) - else: - hex_common.bad_register(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: - hex_common.bad_register(regtype, regid) - - ## ## 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(DisasContext *ctx) ## { -## Insn *insn __attribute__((unused)) = ctx->insn; +## 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]]; @@ -490,50 +42,33 @@ def genptr_dst_write_opn(f, regtype, regid, 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(f"static void generate_{tag}(DisasContext *ctx)\n") f.write("{\n") - f.write(" Insn *insn __attribute__((unused)) = ctx->insn;\n") + f.write(" Insn *insn G_GNUC_UNUSED = ctx->insn;\n") if hex_common.need_ea(tag): - gen_decl_ea_tcg(f, tag) - i = 0 + f.write(" TCGv EA G_GNUC_UNUSED = tcg_temp_new();\n") + ## Declare all the operands (regs and immediates) + i = 0 for regtype, regid in regs: - genptr_decl_opn(f, tag, regtype, regid, i) + 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) - - if "A_PRIV" in hex_common.attribdict[tag]: - f.write(" fCHECKFORPRIV();\n") - if "A_GUEST" in hex_common.attribdict[tag]: - f.write(" fCHECKFORGUEST();\n") - - ## Read all the inputs - for regtype, regid in regs: - if hex_common.is_read(regid): - genptr_src_read_opn(f, regtype, regid, tag) + i = 1 if immlett.isupper() else 0 + f.write(f" int {hex_common.imm_name(immlett)} = insn->immed[{i}];\n") if hex_common.is_idef_parser_enabled(tag): declared = [] ## Handle registers for regtype, regid in regs: - if hex_common.is_pair(regid) or ( - hex_common.is_single(regid) - and hex_common.is_old_val(regtype, regid, tag) - ): - declared.append(f"{regtype}{regid}V") - if regtype == "M": - declared.append(f"{regtype}{regid}N") - elif hex_common.is_new_val(regtype, regid, tag): - declared.append(f"{regtype}{regid}N") - else: - hex_common.bad_register(regtype, regid) - + 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)) @@ -545,76 +80,22 @@ def gen_tcg_func(f, tag, regs, imms): 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_pkt_has_multi_cof(tag): - f.write(" TCGv pkt_has_multi_cof = ") - f.write("tcg_constant_tl(ctx->pkt->pkt_has_multi_cof);\n") - if hex_common.need_pkt_need_commit(tag): - f.write(" TCGv pkt_need_commit = ") - f.write("tcg_constant_tl(ctx->need_commit);\n") - 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 slotval = gen_slotval(ctx);\n") - if hex_common.need_PC(tag): - f.write(" TCGv PC = tcg_constant_tl(ctx->pkt->pc);\n") - if hex_common.helper_needs_next_PC(tag): - f.write(" TCGv next_PC = tcg_constant_tl(ctx->next_PC);\n") - f.write(f" gen_helper_{tag}(") - i = 0 - ## If there is a scalar result, it is the return type - for regtype, regid in regs: - if hex_common.is_written(regid): - if hex_common.is_hvx_reg(regtype): - continue - gen_helper_call_opn(f, tag, regtype, regid, i) - i += 1 - if i > 0: - f.write(", ") - f.write("cpu_env") - i = 1 - ## For conditional instructions, we pass in the destination register - if "A_CONDEXEC" in hex_common.attribdict[tag]: - for regtype, regid in regs: - if hex_common.is_writeonly(regid) and not hex_common.is_hvx_reg( - regtype - ): - gen_helper_call_opn(f, tag, regtype, regid, i) - i += 1 - for regtype, regid 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, i) - i += 1 - for regtype, regid 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, 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_pkt_has_multi_cof(tag): - f.write(", pkt_has_multi_cof") - if hex_common.need_pkt_need_commit(tag): - f.write(", pkt_need_commit") - if hex_common.need_PC(tag): - f.write(", PC") - if hex_common.helper_needs_next_PC(tag): - f.write(", next_PC") - if hex_common.need_slot(tag): - f.write(", slotval") - 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 in regs: - if hex_common.is_written(regid): - genptr_dst_write_opn(f, regtype, regid, tag) + reg = hex_common.get_register(tag, regtype, regid) + if reg.is_written(): + reg.log_write(f, tag) f.write("}\n\n") @@ -627,31 +108,16 @@ def gen_def_tcg_func(f, tag, tagregs, tagimms): 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() - ## Whether or not idef-parser is enabled is - ## determined by the number of arguments to - ## this script: - ## - ## 5 args. -> not enabled, - ## 6 args. -> idef-parser enabled. - ## - ## The 6:th arg. then holds a list of the successfully - ## parsed instructions. - is_idef_parser_enabled = len(sys.argv) > 6 - if is_idef_parser_enabled: - hex_common.read_idef_parser_enabled_file(sys.argv[5]) + args = hex_common.parse_common_args( + "Emit functions calling generated code implementing instruction semantics (helpers, idef-parser)" + ) tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - output_file = sys.argv[-1] - with open(output_file, "w") as f: + with open(args.out, "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: + if args.idef_parser: f.write('#include "idef-generated-emitter.h.inc"\n\n') for tag in hex_common.tags: diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index 44bae53f8dd..0da64d467e6 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -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) diff --git a/target/hexagon/gen_trans_funcs.py b/target/hexagon/gen_trans_funcs.py new file mode 100755 index 00000000000..45da1b7b5df --- /dev/null +++ b/target/hexagon/gen_trans_funcs.py @@ -0,0 +1,155 @@ +#!/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 +import argparse + +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} + """)) + + +def main(): + parser = argparse.ArgumentParser( + description="Emit trans_*() functions to be called by " \ + "instruction decoder" + ) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("out", help="output file") + args = parser.parse_args() + hex_common.read_semantics_file(args.semantics) + hex_common.init_registers() + with open(args.out, "w") as f: + gen_trans_funcs(f) + + +if __name__ == "__main__": + main() diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 217bc7bb5a2..08fc5413de7 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -100,10 +100,6 @@ void gen_log_reg_write(DisasContext *ctx, int rnum, TCGv val) 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_reg_write_pair(DisasContext *ctx, int rnum, TCGv_i64 val) @@ -151,9 +147,6 @@ void gen_log_pred_write(DisasContext *ctx, int pnum, TCGv val) } else { tcg_gen_and_tl(pred, pred, base_val); } - if (HEX_DEBUG) { - tcg_gen_ori_tl(ctx->pred_written, ctx->pred_written, 1 << pnum); - } set_bit(pnum, ctx->pregs_written); } @@ -336,14 +329,14 @@ void gen_set_byte_i64(int N, TCGv_i64 result, TCGv src) static inline void gen_load_locked4u(TCGv dest, TCGv vaddr, int mem_index) { - tcg_gen_qemu_ld_tl(dest, vaddr, mem_index, MO_TEUL); + tcg_gen_qemu_ld_tl(dest, vaddr, mem_index, MO_LE | MO_UL); 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_ld_i64(dest, vaddr, mem_index, MO_TEUQ); + tcg_gen_qemu_ld_i64(dest, vaddr, mem_index, MO_LE | MO_UQ); tcg_gen_mov_tl(hex_llsc_addr, vaddr); tcg_gen_mov_i64(hex_llsc_val_i64, dest); } @@ -414,50 +407,50 @@ void gen_store32(TCGv vaddr, TCGv src, int width, uint32_t slot) tcg_gen_mov_tl(hex_store_val32[slot], src); } -void gen_store1(TCGv_env cpu_env, TCGv vaddr, TCGv src, uint32_t slot) +void gen_store1(TCGv_env tcg_env, TCGv vaddr, TCGv src, uint32_t slot) { gen_store32(vaddr, src, 1, slot); } -void gen_store1i(TCGv_env cpu_env, TCGv vaddr, int32_t src, uint32_t 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); } -void gen_store2(TCGv_env cpu_env, TCGv vaddr, TCGv src, uint32_t slot) +void gen_store2(TCGv_env tcg_env, TCGv vaddr, TCGv src, uint32_t slot) { gen_store32(vaddr, src, 2, slot); } -void gen_store2i(TCGv_env cpu_env, TCGv vaddr, int32_t src, uint32_t 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); } -void gen_store4(TCGv_env cpu_env, TCGv vaddr, TCGv src, uint32_t slot) +void gen_store4(TCGv_env tcg_env, TCGv vaddr, TCGv src, uint32_t slot) { gen_store32(vaddr, src, 4, slot); } -void gen_store4i(TCGv_env cpu_env, TCGv vaddr, int32_t src, uint32_t 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); } -void gen_store8(TCGv_env cpu_env, TCGv vaddr, TCGv_i64 src, uint32_t 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); } -void gen_store8i(TCGv_env cpu_env, TCGv vaddr, int64_t src, uint32_t 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); } TCGv gen_8bitsof(TCGv result, TCGv value) @@ -763,7 +756,7 @@ 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); + tcg_gen_qemu_ld_i64(frame, EA, ctx->mem_idx, MO_LE | MO_UQ); } #ifndef CONFIG_HEXAGON_IDEF_PARSER @@ -783,7 +776,7 @@ static void gen_allocframe(DisasContext *ctx, TCGv r29, int framesize) TCGv_i64 frame; tcg_gen_addi_tl(r30, r29, -8); frame = gen_frame_scramble(); - gen_store8(cpu_env, r30, frame, ctx->insn->slot); + 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); @@ -1237,9 +1230,9 @@ 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_ld_i64(tmp, src, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_ld_i64(tmp, src, ctx->mem_idx, MO_LE | MO_UQ); 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); } } @@ -1251,7 +1244,7 @@ static void gen_vreg_store(DisasContext *ctx, TCGv EA, intptr_t srcoff, 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; } @@ -1301,7 +1294,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) { @@ -1310,7 +1303,7 @@ 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); } } @@ -1318,7 +1311,7 @@ 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); } /* diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h index fa0ebaf7c83..f8baa599c88 100644 --- a/target/hexagon/helper.h +++ b/target/hexagon/helper.h @@ -19,9 +19,6 @@ #include "helper_protos_generated.h.inc" 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_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) diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index dce1b852a7b..758e5fd12df 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2023 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,19 +20,19 @@ import sys import re import string +import textwrap +import argparse behdict = {} # tag ->behavior semdict = {} # tag -> semantics attribdict = {} # tag -> attributes macros = {} # macro -> macro information... -attribinfo = {} # Register information and misc +registers = {} # register -> register functions +new_registers = {} tags = [] # list of all tags overrides = {} # tags with helper overrides idef_parser_enabled = {} # tags enabled for idef-parser -def bad_register(regtype, regid): - raise Exception(f"Bad register parse: regtype '{regtype}' regid '{regid}'") - # We should do this as a hash for performance, # but to keep order let's keep it as a list. def uniquify(seq): @@ -45,7 +45,7 @@ def uniquify(seq): immre = re.compile(r"[#]([rRsSuUm])(\d+)(?:[:](\d+))?") reg_or_immre = re.compile( r"(((?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 parse_common_args(desc): + parser = argparse.ArgumentParser(desc) + parser.add_argument("semantics", help="semantics file") + parser.add_argument("overrides", help="overrides file") + parser.add_argument("overrides_vec", help="vector overrides file") + parser.add_argument("out", help="output file") + parser.add_argument("--idef-parser", + help="file of instructions translated by idef-parser") + args = parser.parse_args() + read_semantics_file(args.semantics) + read_overrides_file(args.overrides) + read_overrides_file(args.overrides_vec) + if args.idef_parser: + read_idef_parser_enabled_file(args.idef_parser) + calculate_attribs() + init_registers() + return args diff --git a/target/hexagon/idef-parser/README.rst b/target/hexagon/idef-parser/README.rst index d0aa34309bc..7199177ee33 100644 --- a/target/hexagon/idef-parser/README.rst +++ b/target/hexagon/idef-parser/README.rst @@ -138,7 +138,7 @@ we obtain the pseudo code with macros such as ``fJUMPR`` intact. The second step is to expand macros into a form suitable for our parser. -These macros are defined in ``idef-parser/macros.inc`` and the step is +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``. @@ -266,7 +266,7 @@ in plain C is defined as #define fABS(A) (((A) < 0) ? (-(A)) : (A)) and returns the absolute value of the argument ``A``. This macro is not included -in ``idef-parser/macros.inc`` and as such is not expanded and kept as a "call" +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 diff --git a/target/hexagon/idef-parser/idef-parser.h b/target/hexagon/idef-parser/idef-parser.h index 3faa1deecd6..8594cbe3a24 100644 --- a/target/hexagon/idef-parser/idef-parser.h +++ b/target/hexagon/idef-parser/idef-parser.h @@ -23,16 +23,6 @@ #include #include -#define TCGV_NAME_SIZE 7 -#define MAX_WRITTEN_REGS 32 -#define OFFSET_STR_LEN 32 -#define ALLOC_LIST_LEN 32 -#define ALLOC_NAME_SIZE 32 -#define INIT_LIST_LEN 32 -#define OUT_BUF_LEN (1024 * 1024) -#define SIGNATURE_BUF_LEN (128 * 1024) -#define HEADER_BUF_LEN (128 * 1024) - /* Variadic macros to wrap the buffer printing functions */ #define EMIT(c, ...) \ do { \ diff --git a/target/hexagon/idef-parser/idef-parser.y b/target/hexagon/idef-parser/idef-parser.y index cd2612eb8c0..c6f17c6afa7 100644 --- a/target/hexagon/idef-parser/idef-parser.y +++ b/target/hexagon/idef-parser/idef-parser.y @@ -233,8 +233,6 @@ code : '{' statements '}' argument_decl : REG { emit_arg(c, &@1, &$1); - /* Enqueue register into initialization list */ - g_array_append_val(c->inst.init_list, $1); } | PRED { @@ -802,7 +800,6 @@ rvalue : FAIL lvalue : FAIL { - @1.last_column = @1.last_column; yyassert(c, &@1, false, "Encountered a FAIL token as lvalue.\n"); } | REG diff --git a/target/hexagon/idef-parser/macros.h.inc b/target/hexagon/idef-parser/macros.h.inc new file mode 100644 index 00000000000..94975d95832 --- /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/macros.inc b/target/hexagon/idef-parser/macros.inc deleted file mode 100644 index 7478d4db171..00000000000 --- a/target/hexagon/idef-parser/macros.inc +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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-relavant 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 index ec433438010..542af8d0a65 100644 --- a/target/hexagon/idef-parser/parser-helpers.c +++ b/target/hexagon/idef-parser/parser-helpers.c @@ -1541,10 +1541,8 @@ void gen_circ_op(Context *c, HexValue *increment, HexValue *modifier) { - HexValue cs = gen_tmp(c, locp, 32, UNSIGNED); HexValue increment_m = *increment; increment_m = rvalue_materialize(c, locp, &increment_m); - OUT(c, locp, "gen_read_reg(", &cs, ", HEX_REG_CS0 + MuN);\n"); OUT(c, locp, "gen_helper_fcircadd(", @@ -1555,7 +1553,7 @@ void gen_circ_op(Context *c, &increment_m, ", ", modifier); - OUT(c, locp, ", ", &cs, ");\n"); + OUT(c, locp, ", CS);\n"); } HexValue gen_locnt_op(Context *c, YYLTYPE *locp, HexValue *src) @@ -1654,26 +1652,28 @@ void gen_inst(Context *c, GString *iname) /* - * Initialize declared but uninitialized registers, but only for - * non-conditional instructions + * 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++) { - HexValue *val = &g_array_index(c->inst.init_list, HexValue, i); - if (val->type == REGISTER_ARG) { - /* Nothing to do here */ - } else if (val->type == PREDICATE) { - char suffix = val->is_dotnew ? 'N' : 'V'; - EMIT_HEAD(c, "tcg_gen_movi_i%u(P%c%c, 0);\n", val->bit_width, - val->pred.id, suffix); - } else { - yyassert(c, locp, false, "Invalid arg type!"); - } + 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 */ @@ -1761,7 +1761,7 @@ void gen_load(Context *c, YYLTYPE *locp, HexValue *width, if (signedness == SIGNED) { OUT(c, locp, " | MO_SIGN"); } - OUT(c, locp, " | MO_TE);\n"); + OUT(c, locp, " | MO_LE);\n"); } void gen_store(Context *c, YYLTYPE *locp, HexValue *width, HexValue *ea, @@ -1773,7 +1773,7 @@ void gen_store(Context *c, YYLTYPE *locp, HexValue *width, HexValue *ea, /* 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, "(cpu_env, ", ea, ", ", &src_m); + OUT(c, locp, "gen_store", &mem_width, "(tcg_env, ", ea, ", ", &src_m); OUT(c, locp, ", insn->slot);\n"); } @@ -2080,9 +2080,9 @@ void emit_arg(Context *c, YYLTYPE *locp, HexValue *arg) char reg_id[5]; reg_compose(c, locp, &(arg->reg), reg_id); EMIT_SIG(c, ", %s %s", type, reg_id); - /* MuV register requires also MuN to provide its index */ + /* MuV register requires also CS for circular addressing*/ if (arg->reg.type == MODIFIER) { - EMIT_SIG(c, ", int MuN"); + EMIT_SIG(c, ", TCGv CS"); } } break; @@ -2123,9 +2123,16 @@ void free_instruction(Context *c) 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 variables and registers */ + /* Free declared TCGv variables */ g_array_free(c->inst.allocated, TRUE); /* Initialize instruction-specific portion of the context */ memset(&(c->inst), 0, sizeof(Inst)); diff --git a/target/hexagon/idef-parser/parser-helpers.h b/target/hexagon/idef-parser/parser-helpers.h index 7c580871698..2087d534a93 100644 --- a/target/hexagon/idef-parser/parser-helpers.h +++ b/target/hexagon/idef-parser/parser-helpers.h @@ -143,8 +143,6 @@ void commit(Context *c); #define OUT(c, locp, ...) FOR_EACH((c), (locp), OUT_IMPL, __VA_ARGS__) -const char *cmp_swap(Context *c, YYLTYPE *locp, const char *type); - /** * Temporary values creation */ @@ -236,8 +234,6 @@ HexValue gen_extract_op(Context *c, HexValue *index, HexExtract *extract); -HexValue gen_read_reg(Context *c, YYLTYPE *locp, HexValue *reg); - void gen_write_reg(Context *c, YYLTYPE *locp, HexValue *reg, HexValue *value); void gen_assign(Context *c, @@ -274,13 +270,6 @@ HexValue gen_ctpop_op(Context *c, YYLTYPE *locp, HexValue *src); HexValue gen_rotl(Context *c, YYLTYPE *locp, HexValue *src, HexValue *n); -HexValue gen_deinterleave(Context *c, YYLTYPE *locp, HexValue *mixed); - -HexValue gen_interleave(Context *c, - YYLTYPE *locp, - HexValue *odd, - HexValue *even); - HexValue gen_carry_from_add(Context *c, YYLTYPE *locp, HexValue *op1, @@ -349,8 +338,6 @@ HexValue gen_rvalue_ternary(Context *c, YYLTYPE *locp, HexValue *cond, const char *cond_to_str(TCGCond cond); -void emit_header(Context *c); - void emit_arg(Context *c, YYLTYPE *locp, HexValue *arg); void emit_footer(Context *c); diff --git a/target/hexagon/idef-parser/prepare b/target/hexagon/idef-parser/prepare index 72d6fcbd21a..cb3622d4f8d 100755 --- a/target/hexagon/idef-parser/prepare +++ b/target/hexagon/idef-parser/prepare @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright(c) 2019-2021 rev.ng Labs Srl. All Rights Reserved. diff --git a/target/hexagon/imported/alu.idef b/target/hexagon/imported/alu.idef index 12d2aac5d4c..b8556769891 100644 --- a/target/hexagon/imported/alu.idef +++ b/target/hexagon/imported/alu.idef @@ -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/mmvec/ext.idef b/target/hexagon/imported/mmvec/ext.idef index 98daabfb07c..03d31f6181d 100644 --- a/target/hexagon/imported/mmvec/ext.idef +++ b/target/hexagon/imported/mmvec/ext.idef @@ -2855,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 3e7a22c91ef..24dcf7fe9f3 100644 --- a/target/hexagon/insn.h +++ b/target/hexagon/insn.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 @@ -39,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. diff --git a/target/hexagon/internal.h b/target/hexagon/internal.h index d732b6bb3c7..32e96f00d97 100644 --- a/target/hexagon/internal.h +++ b/target/hexagon/internal.h @@ -20,21 +20,10 @@ #include "qemu/log.h" -/* - * Change HEX_DEBUG to 1 to turn on debugging output - */ -#define HEX_DEBUG 0 -#define HEX_DEBUG_LOG(...) \ - do { \ - if (HEX_DEBUG) { \ - qemu_log(__VA_ARGS__); \ - } \ - } while (0) - 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(CPUHexagonState *env, GByteArray *mem_buf, int n); -int hexagon_hvx_gdb_write_register(CPUHexagonState *env, uint8_t *mem_buf, int n); +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 5451b061eef..9ba9be408db 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2023 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 @@ -21,9 +21,7 @@ #include "cpu.h" #include "hex_regs.h" #include "reg_fields.h" - -#define PCALIGN 4 -#define PCALIGN_MASK (PCALIGN - 1) +#include "accel/tcg/getpc.h" #define GET_FIELD(FIELD, REGIN) \ fEXTRACTU_BITS(REGIN, reg_field_info[FIELD].width, \ @@ -93,13 +91,13 @@ #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 && ctx->pkt->pkt_has_store_s1) { \ probe_noshuf_load(EA, SIZE, ctx->mem_idx); \ } \ - gen_set_label(label); \ + gen_set_label(noshuf_label); \ if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \ process_store(ctx, 1); \ } \ @@ -118,27 +116,27 @@ #define MEM_LOAD2s(DST, VA) \ do { \ CHECK_NOSHUF(VA, 2); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TESW); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_SW); \ } while (0) #define MEM_LOAD2u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 2); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TEUW); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_UW); \ } while (0) #define MEM_LOAD4s(DST, VA) \ do { \ CHECK_NOSHUF(VA, 4); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TESL); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_SL); \ } while (0) #define MEM_LOAD4u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 4); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TEUL); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_UL); \ } while (0) #define MEM_LOAD8u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 8); \ - tcg_gen_qemu_ld_i64(DST, VA, ctx->mem_idx, MO_TEUQ); \ + tcg_gen_qemu_ld_i64(DST, VA, ctx->mem_idx, MO_LE | MO_UQ); \ } while (0) #define MEM_STORE1_FUNC(X) \ @@ -147,7 +145,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), \ @@ -155,7 +153,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), \ @@ -163,7 +161,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), \ @@ -171,17 +169,8 @@ __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, pkt_has_store_s1, slot, VA)) -#define MEM_LOAD1u(VA) ((uint8_t)mem_load1(env, pkt_has_store_s1, slot, VA)) -#define MEM_LOAD2s(VA) ((int16_t)mem_load2(env, pkt_has_store_s1, slot, VA)) -#define MEM_LOAD2u(VA) ((uint16_t)mem_load2(env, pkt_has_store_s1, slot, VA)) -#define MEM_LOAD4s(VA) ((int32_t)mem_load4(env, pkt_has_store_s1, slot, VA)) -#define MEM_LOAD4u(VA) ((uint32_t)mem_load4(env, pkt_has_store_s1, slot, VA)) -#define MEM_LOAD8s(VA) ((int64_t)mem_load8(env, pkt_has_store_s1, slot, VA)) -#define MEM_LOAD8u(VA) ((uint64_t)mem_load8(env, pkt_has_store_s1, 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) @@ -352,7 +341,7 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #define fREAD_LR() (env->gpr[HEX_REG_LR]) -#define fREAD_SP() (env->gpr[HEX_REG_SP]) +#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]) @@ -367,7 +356,7 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #endif #define fREAD_PC() (PC) -#define fREAD_P0() (env->pred[0]) +#define fREAD_P0() (P0) #define fCHECK_PCALIGN(A) @@ -471,8 +460,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) @@ -530,8 +518,16 @@ 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) diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index da8e608d006..bb4ebaae816 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -1,5 +1,5 @@ ## -## Copyright(c) 2020-2023 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,7 +18,6 @@ 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' @@ -42,28 +41,17 @@ hexagon_ss.add(semantics_generated) # # Step 2 # We use Python scripts to generate the following files -# shortcode_generated.h.inc # tcg_func_table_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) - 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) @@ -71,26 +59,17 @@ 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) @@ -98,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) @@ -110,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( @@ -122,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 +# +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('gen_decodetree.py'), semantics_generated, 'NORMAL', '@OUTPUT@'], +) +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 # -dectree_generated = custom_target( - 'dectree_generated.h.inc', - output: 'dectree_generated.h.inc', - depends: [iset_py], +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('dectree.py'), '@OUTPUT@'], + command: [python, files('gen_trans_funcs.py'), semantics_generated, '@OUTPUT@'], ) -hexagon_ss.add(dectree_generated) +hexagon_ss.add(decodetree_trans_funcs_generated) hexagon_ss.add(files( 'cpu.c', @@ -165,14 +277,14 @@ if idef_parser_enabled and 'hexagon-linux-user' in target_dirs output: 'idef_parser_input.h.inc', depends: [semantics_generated], depend_files: [hex_common_py], - command: [python, files('gen_idef_parser_funcs.py'), semantics_generated, attribs_def, '@OUTPUT@'], + 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.inc'], + depend_files: [idef_parser_dir / 'macros.h.inc'], command: [idef_parser_dir / 'prepare', '@INPUT@', '-I' + idef_parser_dir, '-o', '@OUTPUT@'], ) @@ -188,7 +300,7 @@ if idef_parser_enabled and 'hexagon-linux-user' in target_dirs arguments: ['@INPUT@', '--defines=@OUTPUT1@', '--output=@OUTPUT0@'] ) - glib_dep = dependency('glib-2.0', native: true) + glib_dep = dependency('glib-2.0', native: true, static: false) idef_parser = executable( 'idef-parser', @@ -234,12 +346,12 @@ if idef_parser_enabled and 'hexagon-linux-user' in target_dirs # Setup input and dependencies for the next step, this depends on whether or # not idef-parser is enabled helper_dep = [semantics_generated, idef_generated_tcg_c, idef_generated_tcg] - helper_in = [semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h, idef_generated_list] + helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h, '--idef-parser', idef_generated_list] else # Setup input and dependencies for the next step, this depends on whether or # not idef-parser is enabled helper_dep = [semantics_generated] - helper_in = [semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h] + helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h] endif # @@ -253,7 +365,7 @@ helper_protos_generated = custom_target( 'helper_protos_generated.h.inc', output: 'helper_protos_generated.h.inc', depends: helper_dep, - depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], + 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) @@ -262,7 +374,7 @@ helper_funcs_generated = custom_target( 'helper_funcs_generated.c.inc', output: 'helper_funcs_generated.c.inc', depends: helper_dep, - depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], + 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) @@ -271,7 +383,7 @@ tcg_funcs_generated = custom_target( 'tcg_funcs_generated.c.inc', output: 'tcg_funcs_generated.c.inc', depends: helper_dep, - depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], + 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) @@ -280,7 +392,7 @@ analyze_funcs_generated = custom_target( 'analyze_funcs_generated.c.inc', output: 'analyze_funcs_generated.c.inc', depends: helper_dep, - depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h], + 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) diff --git a/target/hexagon/mmvec/decode_ext_mmvec.c b/target/hexagon/mmvec/decode_ext_mmvec.c index 174eb3b78b2..f850d0154d6 100644 --- a/target/hexagon/mmvec/decode_ext_mmvec.c +++ b/target/hexagon/mmvec/decode_ext_mmvec.c @@ -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 diff --git a/target/hexagon/mmvec/macros.h b/target/hexagon/mmvec/macros.h index a655634fd15..c7840fbf2e1 100644 --- a/target/hexagon/mmvec/macros.h +++ b/target/hexagon/mmvec/macros.h @@ -21,28 +21,30 @@ #include "qemu/host-utils.h" #include "arch.h" #include "mmvec/system_ext_mmvec.h" +#include "accel/tcg/getpc.h" +#include "accel/tcg/probe.h" #ifndef QEMU_GENERATE -#define VdV (*(MMVector *)(VdV_void)) -#define VsV (*(MMVector *)(VsV_void)) -#define VuV (*(MMVector *)(VuV_void)) -#define VvV (*(MMVector *)(VvV_void)) -#define VwV (*(MMVector *)(VwV_void)) -#define VxV (*(MMVector *)(VxV_void)) -#define VyV (*(MMVector *)(VyV_void)) +#define VdV (*(MMVector *restrict)(VdV_void)) +#define VsV (*(MMVector *restrict)(VsV_void)) +#define VuV (*(MMVector *restrict)(VuV_void)) +#define VvV (*(MMVector *restrict)(VvV_void)) +#define VwV (*(MMVector *restrict)(VwV_void)) +#define VxV (*(MMVector *restrict)(VxV_void)) +#define VyV (*(MMVector *restrict)(VyV_void)) -#define VddV (*(MMVectorPair *)(VddV_void)) -#define VuuV (*(MMVectorPair *)(VuuV_void)) -#define VvvV (*(MMVectorPair *)(VvvV_void)) -#define VxxV (*(MMVectorPair *)(VxxV_void)) +#define VddV (*(MMVectorPair *restrict)(VddV_void)) +#define VuuV (*(MMVectorPair *restrict)(VuuV_void)) +#define VvvV (*(MMVectorPair *restrict)(VvvV_void)) +#define VxxV (*(MMVectorPair *restrict)(VxxV_void)) -#define QeV (*(MMQReg *)(QeV_void)) -#define QdV (*(MMQReg *)(QdV_void)) -#define QsV (*(MMQReg *)(QsV_void)) -#define QtV (*(MMQReg *)(QtV_void)) -#define QuV (*(MMQReg *)(QuV_void)) -#define QvV (*(MMQReg *)(QvV_void)) -#define QxV (*(MMQReg *)(QxV_void)) +#define QeV (*(MMQReg *restrict)(QeV_void)) +#define QdV (*(MMQReg *restrict)(QdV_void)) +#define QsV (*(MMQReg *restrict)(QsV_void)) +#define QtV (*(MMQReg *restrict)(QtV_void)) +#define QuV (*(MMQReg *restrict)(QuV_void)) +#define QvV (*(MMQReg *restrict)(QvV_void)) +#define QxV (*(MMQReg *restrict)(QxV_void)) #endif #define LOG_VTCM_BYTE(VA, MASK, VAL, IDX) \ @@ -201,7 +203,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; \ diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 12967ac21e0..444799d3ade 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2023 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 @@ -17,8 +17,8 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" #include "cpu.h" @@ -36,10 +36,9 @@ #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); @@ -49,15 +48,12 @@ 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); } 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", - width, addr, val, val); env->mem_log_stores[slot].va = addr; env->mem_log_stores[slot].width = width; env->mem_log_stores[slot].data32 = val; @@ -66,38 +62,13 @@ void log_store32(CPUHexagonState *env, target_ulong addr, 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", - width, addr, val, val); env->mem_log_stores[slot].va = addr; env->mem_log_stores[slot].width = width; env->mem_log_stores[slot].data64 = val; } -/* Handy place to set a breakpoint */ -void HELPER(debug_start_packet)(CPUHexagonState *env) +static void commit_store(CPUHexagonState *env, int slot_num, uintptr_t ra) { - HEX_DEBUG_LOG("Start packet: pc = 0x" TARGET_FMT_lx "\n", - env->gpr[HEX_REG_PC]); - - for (int i = 0; i < TOTAL_PER_THREAD_REGS; i++) { - env->reg_written[i] = 0; - } -} - -/* Checks for bookkeeping errors between disassembly context and runtime */ -void HELPER(debug_check_store_width)(CPUHexagonState *env, int slot, int check) -{ - if (env->mem_log_stores[slot].width != check) { - HEX_DEBUG_LOG("ERROR: %d != %d\n", - env->mem_log_stores[slot].width, check); - g_assert_not_reached(); - } -} - -void HELPER(commit_store)(CPUHexagonState *env, int slot_num) -{ - uintptr_t ra = GETPC(); uint8_t width = env->mem_log_stores[slot_num].width; target_ulong va = env->mem_log_stores[slot_num].va; @@ -119,6 +90,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); @@ -127,10 +104,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; @@ -157,7 +133,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); @@ -170,91 +146,6 @@ void HELPER(commit_hvx_stores)(CPUHexagonState *env) } } -static void print_store(CPUHexagonState *env, int slot) -{ - if (!(env->slot_cancelled & (1 << slot))) { - uint8_t width = env->mem_log_stores[slot].width; - if (width == 1) { - uint32_t data = env->mem_log_stores[slot].data32 & 0xff; - HEX_DEBUG_LOG("\tmemb[0x" TARGET_FMT_lx "] = %" PRId32 - " (0x%02" PRIx32 ")\n", - env->mem_log_stores[slot].va, data, data); - } else if (width == 2) { - uint32_t data = env->mem_log_stores[slot].data32 & 0xffff; - HEX_DEBUG_LOG("\tmemh[0x" TARGET_FMT_lx "] = %" PRId32 - " (0x%04" PRIx32 ")\n", - env->mem_log_stores[slot].va, data, data); - } else if (width == 4) { - uint32_t data = env->mem_log_stores[slot].data32; - HEX_DEBUG_LOG("\tmemw[0x" TARGET_FMT_lx "] = %" PRId32 - " (0x%08" PRIx32 ")\n", - env->mem_log_stores[slot].va, data, data); - } else if (width == 8) { - HEX_DEBUG_LOG("\tmemd[0x" TARGET_FMT_lx "] = %" PRId64 - " (0x%016" PRIx64 ")\n", - env->mem_log_stores[slot].va, - env->mem_log_stores[slot].data64, - env->mem_log_stores[slot].data64); - } else { - HEX_DEBUG_LOG("\tBad store width %d\n", width); - g_assert_not_reached(); - } - } -} - -/* This function is a handy place to set a breakpoint */ -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", this_PC); - HEX_DEBUG_LOG("slot_cancelled = %d\n", env->slot_cancelled); - - for (i = 0; i < TOTAL_PER_THREAD_REGS; i++) { - if (env->reg_written[i]) { - if (!reg_printed) { - HEX_DEBUG_LOG("Regs written\n"); - reg_printed = true; - } - HEX_DEBUG_LOG("\tr%d = " TARGET_FMT_ld " (0x" TARGET_FMT_lx ")\n", - i, env->gpr[i], env->gpr[i]); - } - } - - for (i = 0; i < NUM_PREGS; 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->pred[i]); - } - } - - if (has_st0 || has_st1) { - HEX_DEBUG_LOG("Stores\n"); - if (has_st0) { - print_store(env, 0); - } - if (has_st1) { - print_store(env, 1); - } - } - - 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", - env->gpr[HEX_REG_QEMU_PKT_CNT], - env->gpr[HEX_REG_QEMU_INSN_CNT], - env->gpr[HEX_REG_QEMU_HVX_CNT]); - -} - int32_t HELPER(fcircadd)(int32_t RxV, int32_t offset, int32_t M, int32_t CS) { uint32_t K_const = extract32(M, 24, 4); @@ -467,13 +358,12 @@ int32_t HELPER(cabacdecbin_pred)(int64_t RssV, int64_t RttV) } static void probe_store(CPUHexagonState *env, int slot, int mmu_idx, - bool is_predicated) + 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); } } @@ -494,16 +384,15 @@ void HELPER(probe_pkt_scalar_store_s0)(CPUHexagonState *env, int args) int mmu_idx = FIELD_EX32(args, PROBE_PKT_SCALAR_STORE_S0, MMU_IDX); bool is_predicated = FIELD_EX32(args, PROBE_PKT_SCALAR_STORE_S0, IS_PREDICATED); - probe_store(env, 0, mmu_idx, 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; @@ -538,6 +427,12 @@ void HELPER(probe_hvx_stores)(CPUHexagonState *env, int mmu_idx) } } +void HELPER(probe_hvx_stores)(CPUHexagonState *env, int mmu_idx) +{ + 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); @@ -547,18 +442,20 @@ void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask) 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, s0_is_pred); + probe_store(env, 0, mmu_idx, s0_is_pred, ra); } if (has_st1) { - probe_store(env, 1, mmu_idx, s1_is_pred); + 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 @@ -567,46 +464,16 @@ void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask) * wasn't cancelled), we have to do the store first. */ static void check_noshuf(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr, int size) + uint32_t slot, target_ulong vaddr, int size, + uintptr_t ra) { 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); } } - -uint8_t mem_load1(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr) -{ - uintptr_t ra = GETPC(); - check_noshuf(env, pkt_has_store_s1, slot, vaddr, 1); - return cpu_ldub_data_ra(env, vaddr, ra); -} - -uint16_t mem_load2(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr) -{ - uintptr_t ra = GETPC(); - check_noshuf(env, pkt_has_store_s1, slot, vaddr, 2); - return cpu_lduw_data_ra(env, vaddr, ra); -} - -uint32_t mem_load4(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr) -{ - uintptr_t ra = GETPC(); - check_noshuf(env, pkt_has_store_s1, slot, vaddr, 4); - return cpu_ldl_data_ra(env, vaddr, ra); -} - -uint64_t mem_load8(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr) -{ - uintptr_t ra = GETPC(); - check_noshuf(env, pkt_has_store_s1, slot, vaddr, 8); - return cpu_ldq_data_ra(env, vaddr, ra); -} +#endif /* Floating point */ float64 HELPER(conv_sf2df)(CPUHexagonState *env, float32 RsV) @@ -704,7 +571,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 { @@ -734,7 +601,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 { @@ -764,7 +631,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 { @@ -794,7 +661,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 { @@ -824,7 +691,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 { @@ -854,7 +721,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 { @@ -884,7 +751,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 { @@ -914,7 +781,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 { @@ -1178,7 +1045,7 @@ float32 HELPER(sfmpy)(CPUHexagonState *env, float32 RsV, float32 RtV) { float32 RdV; arch_fpop_start(env); - RdV = internal_mpyf(RsV, RtV, &env->fp_status); + RdV = float32_mul(RsV, RtV, &env->fp_status); arch_fpop_end(env); return RdV; } @@ -1187,41 +1054,18 @@ float32 HELPER(sffma)(CPUHexagonState *env, float32 RxV, float32 RsV, float32 RtV) { arch_fpop_start(env); - RxV = internal_fmafx(RsV, RtV, RxV, 0, &env->fp_status); + RxV = float32_muladd(RsV, RtV, RxV, 0, &env->fp_status); arch_fpop_end(env); return RxV; } -static bool is_zero_prod(float32 a, float32 b) -{ - return ((float32_is_zero(a) && is_finite(b)) || - (float32_is_zero(b) && is_finite(a))); -} - -static float32 check_nan(float32 dst, float32 x, float_status *fp_status) -{ - float32 ret = dst; - if (float32_is_any_nan(x)) { - if (extract32(x, 22, 1) == 0) { - float_raise(float_flag_invalid, fp_status); - } - ret = make_float32(0xffffffff); /* nan */ - } - return ret; -} - float32 HELPER(sffma_sc)(CPUHexagonState *env, float32 RxV, float32 RsV, float32 RtV, float32 PuV) { - size4s_t tmp; arch_fpop_start(env); - RxV = check_nan(RxV, RxV, &env->fp_status); - RxV = check_nan(RxV, RsV, &env->fp_status); - RxV = check_nan(RxV, RtV, &env->fp_status); - tmp = internal_fmafx(RsV, RtV, RxV, fSXTN(8, 64, PuV), &env->fp_status); - if (!(float32_is_zero(RxV) && is_zero_prod(RsV, RtV))) { - RxV = tmp; - } + RxV = float32_muladd_scalbn(RsV, RtV, RxV, fSXTN(8, 64, PuV), + float_muladd_suppress_add_product_zero, + &env->fp_status); arch_fpop_end(env); return RxV; } @@ -1229,86 +1073,50 @@ float32 HELPER(sffma_sc)(CPUHexagonState *env, float32 RxV, float32 HELPER(sffms)(CPUHexagonState *env, float32 RxV, float32 RsV, float32 RtV) { - float32 neg_RsV; arch_fpop_start(env); - neg_RsV = float32_set_sign(RsV, float32_is_neg(RsV) ? 0 : 1); - RxV = internal_fmafx(neg_RsV, RtV, RxV, 0, &env->fp_status); + RxV = float32_muladd(RsV, RtV, RxV, float_muladd_negate_product, + &env->fp_status); arch_fpop_end(env); return RxV; } -static bool is_inf_prod(int32_t a, int32_t b) +static float32 do_sffma_lib(CPUHexagonState *env, float32 RxV, + float32 RsV, float32 RtV, int negate) { - return (float32_is_infinity(a) && float32_is_infinity(b)) || - (float32_is_infinity(a) && is_finite(b) && !float32_is_zero(b)) || - (float32_is_infinity(b) && is_finite(a) && !float32_is_zero(a)); -} - -float32 HELPER(sffma_lib)(CPUHexagonState *env, float32 RxV, - float32 RsV, float32 RtV) -{ - bool infinp; - bool infminusinf; - float32 tmp; + int flags; arch_fpop_start(env); - set_float_rounding_mode(float_round_nearest_even, &env->fp_status); - infminusinf = float32_is_infinity(RxV) && - is_inf_prod(RsV, RtV) && - (fGETBIT(31, RsV ^ RxV ^ RtV) != 0); - infinp = float32_is_infinity(RxV) || - float32_is_infinity(RtV) || - float32_is_infinity(RsV); - RxV = check_nan(RxV, RxV, &env->fp_status); - RxV = check_nan(RxV, RsV, &env->fp_status); - RxV = check_nan(RxV, RtV, &env->fp_status); - tmp = internal_fmafx(RsV, RtV, RxV, 0, &env->fp_status); - if (!(float32_is_zero(RxV) && is_zero_prod(RsV, RtV))) { - RxV = tmp; - } - set_float_exception_flags(0, &env->fp_status); - if (float32_is_infinity(RxV) && !infinp) { - RxV = RxV - 1; - } - if (infminusinf) { - RxV = 0; + + set_float_rounding_mode(float_round_nearest_even_max, &env->fp_status); + RxV = float32_muladd(RsV, RtV, RxV, + negate | float_muladd_suppress_add_product_zero, + &env->fp_status); + + flags = get_float_exception_flags(&env->fp_status); + if (flags) { + /* Flags are suppressed by this instruction. */ + set_float_exception_flags(0, &env->fp_status); + + /* Return 0 for Inf - Inf. */ + if (flags & float_flag_invalid_isi) { + RxV = 0; + } } + arch_fpop_end(env); return RxV; } -float32 HELPER(sffms_lib)(CPUHexagonState *env, float32 RxV, +float32 HELPER(sffma_lib)(CPUHexagonState *env, float32 RxV, float32 RsV, float32 RtV) { - bool infinp; - bool infminusinf; - float32 tmp; + return do_sffma_lib(env, RxV, RsV, RtV, 0); +} - arch_fpop_start(env); - set_float_rounding_mode(float_round_nearest_even, &env->fp_status); - infminusinf = float32_is_infinity(RxV) && - is_inf_prod(RsV, RtV) && - (fGETBIT(31, RsV ^ RxV ^ RtV) == 0); - infinp = float32_is_infinity(RxV) || - float32_is_infinity(RtV) || - float32_is_infinity(RsV); - RxV = check_nan(RxV, RxV, &env->fp_status); - RxV = check_nan(RxV, RsV, &env->fp_status); - RxV = check_nan(RxV, RtV, &env->fp_status); - float32 minus_RsV = float32_sub(float32_zero, RsV, &env->fp_status); - tmp = internal_fmafx(minus_RsV, RtV, RxV, 0, &env->fp_status); - if (!(float32_is_zero(RxV) && is_zero_prod(RsV, RtV))) { - RxV = tmp; - } - set_float_exception_flags(0, &env->fp_status); - if (float32_is_infinity(RxV) && !infinp) { - RxV = RxV - 1; - } - if (infminusinf) { - RxV = 0; - } - arch_fpop_end(env); - return RxV; +float32 HELPER(sffms_lib)(CPUHexagonState *env, float32 RxV, + float32 RsV, float32 RtV) +{ + return do_sffma_lib(env, RxV, RsV, RtV, float_muladd_negate_product); } float64 HELPER(dfmpyfix)(CPUHexagonState *env, float64 RssV, float64 RttV) diff --git a/target/hexagon/op_helper.h b/target/hexagon/op_helper.h index 8f3764d15ee..66119cf3d4c 100644 --- a/target/hexagon/op_helper.h +++ b/target/hexagon/op_helper.h @@ -19,15 +19,6 @@ #define HEXAGON_OP_HELPER_H /* Misc functions */ -uint8_t mem_load1(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr); -uint16_t mem_load2(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr); -uint32_t mem_load4(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr); -uint64_t mem_load8(CPUHexagonState *env, bool pkt_has_store_s1, - uint32_t slot, target_ulong vaddr); - void log_store64(CPUHexagonState *env, target_ulong addr, int64_t val, int width, int slot); void log_store32(CPUHexagonState *env, target_ulong addr, diff --git a/target/hexagon/opcodes.c b/target/hexagon/opcodes.c index 35d790cdd5b..c8bde2f9e9c 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 6e90e00fe22..0ee11bd4459 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 c00254e4d5b..02fd40c160f 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2023 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,6 +23,7 @@ #include "exec/helper-gen.h" #include "exec/helper-proto.h" #include "exec/translation-block.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/log.h" #include "internal.h" #include "attribs.h" @@ -49,7 +50,6 @@ TCGv hex_gpr[TOTAL_PER_THREAD_REGS]; TCGv hex_pred[NUM_PREGS]; TCGv hex_slot_cancelled; TCGv hex_new_value_usr; -TCGv hex_reg_written[TOTAL_PER_THREAD_REGS]; TCGv hex_store_addr[STORES_MAX]; TCGv hex_store_width[STORES_MAX]; TCGv hex_store_val32[STORES_MAX]; @@ -115,7 +115,7 @@ intptr_t ctx_tmp_vreg_off(DisasContext *ctx, int regnum, static void gen_exception_raw(int excp) { - gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp)); } static void gen_exec_counters(DisasContext *ctx) @@ -194,21 +194,6 @@ static void gen_exception_end_tb(DisasContext *ctx, int excp) } -#define PACKET_BUFFER_LEN 1028 -static void print_pkt(Packet *pkt) -{ - GString *buf = g_string_sized_new(PACKET_BUFFER_LEN); - snprint_a_pkt_debug(buf, pkt); - HEX_DEBUG_LOG("%s", buf->str); - g_string_free(buf, true); -} -#define HEX_DEBUG_PRINT_PKT(pkt) \ - do { \ - if (HEX_DEBUG) { \ - print_pkt(pkt); \ - } \ - } while (0) - static int read_packet_words(CPUHexagonState *env, DisasContext *ctx, uint32_t words[]) { @@ -234,13 +219,6 @@ 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(" words = { "); - for (int i = 0; i < nwords; i++) { - HEX_DEBUG_LOG("0x%x, ", words[i]); - } - HEX_DEBUG_LOG("}\n"); - return nwords; } @@ -378,70 +356,8 @@ static bool need_commit(DisasContext *ctx) return true; } - if (pkt->num_insns == 1) { - if (pkt->pkt_has_hvx) { - /* - * The HVX instructions with generated helpers use - * pass-by-reference, so they need the read/write overlap - * check below. - * The HVX instructions with overrides are OK. - */ - if (!ctx->has_hvx_helper) { - return false; - } - } else { - return false; - } - } - - /* Check for overlap between register reads and writes */ - for (int i = 0; i < ctx->reg_log_idx; i++) { - int rnum = ctx->reg_log[i]; - if (test_bit(rnum, ctx->regs_read)) { - return true; - } - } - - /* Check for overlap between predicate reads and writes */ - for (int i = 0; i < ctx->preg_log_idx; i++) { - int pnum = ctx->preg_log[i]; - if (test_bit(pnum, ctx->pregs_read)) { - return true; - } - } - - /* Check for overlap between HVX reads and writes */ - for (int i = 0; i < ctx->vreg_log_idx; i++) { - int vnum = ctx->vreg_log[i]; - if (test_bit(vnum, ctx->vregs_read)) { - return true; - } - } - if (!bitmap_empty(ctx->vregs_updated_tmp, NUM_VREGS)) { - int i = find_first_bit(ctx->vregs_updated_tmp, NUM_VREGS); - while (i < NUM_VREGS) { - if (test_bit(i, ctx->vregs_read)) { - return true; - } - i = find_next_bit(ctx->vregs_updated_tmp, NUM_VREGS, i + 1); - } - } - if (!bitmap_empty(ctx->vregs_select, NUM_VREGS)) { - int i = find_first_bit(ctx->vregs_select, NUM_VREGS); - while (i < NUM_VREGS) { - if (test_bit(i, ctx->vregs_read)) { - return true; - } - i = find_next_bit(ctx->vregs_select, NUM_VREGS, i + 1); - } - } - - /* Check for overlap between HVX predicate reads and writes */ - for (int i = 0; i < ctx->qreg_log_idx; i++) { - int qnum = ctx->qreg_log[i]; - if (test_bit(qnum, ctx->qregs_read)) { - return true; - } + if (ctx->read_after_write || ctx->has_hvx_overlap) { + return true; } return false; @@ -465,7 +381,8 @@ static void mark_implicit_pred_reads(DisasContext *ctx) static void analyze_packet(DisasContext *ctx) { Packet *pkt = ctx->pkt; - ctx->has_hvx_helper = false; + 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; @@ -490,21 +407,19 @@ static void gen_start_packet(DisasContext *ctx) ctx->next_PC = next_PC; ctx->reg_log_idx = 0; bitmap_zero(ctx->regs_written, TOTAL_PER_THREAD_REGS); - bitmap_zero(ctx->regs_read, TOTAL_PER_THREAD_REGS); bitmap_zero(ctx->predicated_regs, TOTAL_PER_THREAD_REGS); ctx->preg_log_idx = 0; bitmap_zero(ctx->pregs_written, NUM_PREGS); - bitmap_zero(ctx->pregs_read, NUM_PREGS); ctx->future_vregs_idx = 0; ctx->tmp_vregs_idx = 0; ctx->vreg_log_idx = 0; + bitmap_zero(ctx->vregs_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->vregs_read, NUM_VREGS); - bitmap_zero(ctx->qregs_read, NUM_QREGS); + bitmap_zero(ctx->qregs_written, NUM_QREGS); ctx->qreg_log_idx = 0; for (i = 0; i < STORES_MAX; i++) { ctx->store_width[i] = 0; @@ -526,11 +441,6 @@ static void gen_start_packet(DisasContext *ctx) */ 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); - } - /* Initialize the runtime state for packet semantics */ if (need_slot_cancelled(pkt)) { tcg_gen_movi_tl(hex_slot_cancelled, 0); @@ -545,15 +455,11 @@ static void gen_start_packet(DisasContext *ctx) tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], next_PC); } } - if (HEX_DEBUG) { - ctx->pred_written = tcg_temp_new(); - tcg_gen_movi_tl(ctx->pred_written, 0); - } /* Preload the predicated registers into get_result_gpr(ctx, i) */ if (ctx->need_commit && !bitmap_empty(ctx->predicated_regs, TOTAL_PER_THREAD_REGS)) { - int i = find_first_bit(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, @@ -566,7 +472,7 @@ static void gen_start_packet(DisasContext *ctx) * Only endloop instructions conditionally write to pred registers */ if (ctx->need_commit && pkt->pkt_has_endloop) { - for (int i = 0; i < ctx->preg_log_idx; i++) { + 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]); @@ -575,7 +481,7 @@ static void gen_start_packet(DisasContext *ctx) /* Preload the predicated HVX registers into future_VRegs and tmp_VRegs */ if (!bitmap_empty(ctx->predicated_future_vregs, NUM_VREGS)) { - int i = find_first_bit(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); @@ -588,7 +494,7 @@ static void gen_start_packet(DisasContext *ctx) } } if (!bitmap_empty(ctx->predicated_tmp_vregs, NUM_VREGS)) { - int i = find_first_bit(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); @@ -652,7 +558,7 @@ static void gen_insn(DisasContext *ctx) ctx->insn->generate(ctx); mark_store_width(ctx); } else { - gen_exception_end_tb(ctx, HEX_EXCP_INVALID_OPCODE); + gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_OPCODE); } } @@ -696,15 +602,6 @@ static void gen_pred_writes(DisasContext *ctx) } } -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); - } -} - static bool slot_is_predicated(Packet *pkt, int slot_num) { for (int i = 0; i < pkt->num_insns; i++) { @@ -752,28 +649,24 @@ void process_store(DisasContext *ctx, int slot_num) */ switch (ctx->store_width[slot_num]) { case 1: - gen_check_store_width(ctx, slot_num); 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_st_tl(hex_store_val32[slot_num], hex_store_addr[slot_num], - ctx->mem_idx, MO_TEUW); + ctx->mem_idx, MO_LE | MO_UW); break; case 4: - gen_check_store_width(ctx, slot_num); tcg_gen_qemu_st_tl(hex_store_val32[slot_num], hex_store_addr[slot_num], - ctx->mem_idx, MO_TEUL); + ctx->mem_idx, MO_LE | MO_UL); break; case 8: - gen_check_store_width(ctx, slot_num); tcg_gen_qemu_st_i64(hex_store_val64[slot_num], hex_store_addr[slot_num], - ctx->mem_idx, MO_TEUQ); + ctx->mem_idx, MO_LE | MO_UQ); break; default: { @@ -783,7 +676,7 @@ void process_store(DisasContext *ctx, 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); } } } @@ -882,7 +775,7 @@ static void gen_commit_hvx(DisasContext *ctx) } if (pkt_has_hvx_store(ctx->pkt)) { - gen_helper_commit_hvx_stores(cpu_env); + gen_helper_commit_hvx_stores(tcg_env); } } @@ -942,7 +835,7 @@ static void gen_commit_packet(DisasContext *ctx) } else if (has_hvx_store) { if (!has_store_s0 && !has_store_s1) { TCGv mem_idx = tcg_constant_tl(ctx->mem_idx); - gen_helper_probe_hvx_stores(cpu_env, mem_idx); + gen_helper_probe_hvx_stores(tcg_env, mem_idx); } else { int mask = 0; @@ -971,7 +864,7 @@ static void gen_commit_packet(DisasContext *ctx) } mask = FIELD_DP32(mask, PROBE_PKT_SCALAR_HVX_STORES, MMU_IDX, ctx->mem_idx); - gen_helper_probe_pkt_scalar_hvx_stores(cpu_env, + gen_helper_probe_pkt_scalar_hvx_stores(tcg_env, tcg_constant_tl(mask)); } } else if (has_store_s0 && has_store_s1) { @@ -987,7 +880,7 @@ static void gen_commit_packet(DisasContext *ctx) 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(cpu_env, args_tcgv); + gen_helper_probe_pkt_scalar_store_s0(tcg_env, args_tcgv); } process_store_log(ctx); @@ -998,16 +891,6 @@ static void gen_commit_packet(DisasContext *ctx) gen_commit_hvx(ctx); } update_exec_counters(ctx); - if (HEX_DEBUG) { - TCGv has_st0 = - tcg_constant_tl(pkt->pkt_has_store_s0 && !pkt->pkt_has_dczeroa); - TCGv has_st1 = - 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, tcg_constant_tl(ctx->pkt->pc), - ctx->pred_written, has_st0, has_st1); - } if (pkt->vhist_insn != NULL) { ctx->pre_commit = false; @@ -1029,14 +912,13 @@ static void decode_and_translate_packet(CPUHexagonState *env, DisasContext *ctx) nwords = read_packet_words(env, ctx, words); if (!nwords) { - gen_exception_end_tb(ctx, HEX_EXCP_INVALID_PACKET); + gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_PACKET); 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); - ctx->pkt = &pkt; gen_start_packet(ctx); for (i = 0; i < pkt.num_insns; i++) { ctx->insn = &pkt.insn[i]; @@ -1045,7 +927,7 @@ static void decode_and_translate_packet(CPUHexagonState *env, DisasContext *ctx) gen_commit_packet(ctx); ctx->base.pc_next += pkt.encod_pkt_size_in_bytes; } else { - gen_exception_end_tb(ctx, HEX_EXCP_INVALID_PACKET); + gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_PACKET); } } @@ -1053,7 +935,7 @@ static void hexagon_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - HexagonCPU *hex_cpu = env_archcpu(cs->env_ptr); + HexagonCPU *hex_cpu = env_archcpu(cpu_env(cs)); uint32_t hex_flags = dcbase->tb->flags; ctx->mem_idx = MMU_USER_IDX; @@ -1083,7 +965,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); } @@ -1094,7 +976,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); @@ -1136,25 +1018,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 hexagon_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext ctx; @@ -1163,7 +1036,6 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, } #define NAME_LEN 64 -static char reg_written_names[TOTAL_PER_THREAD_REGS][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]; @@ -1179,68 +1051,60 @@ 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]); - - 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, - offsetof(CPUHexagonState, reg_written[i]), - reg_written_names[i]); - } } - hex_new_value_usr = tcg_global_mem_new(cpu_env, + 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]); } - 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_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"); 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 4dd59c6726b..d251e2233fd 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2023 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 @@ -38,12 +38,10 @@ typedef struct DisasContext { int reg_log[REG_WRITES_MAX]; int reg_log_idx; DECLARE_BITMAP(regs_written, TOTAL_PER_THREAD_REGS); - DECLARE_BITMAP(regs_read, TOTAL_PER_THREAD_REGS); DECLARE_BITMAP(predicated_regs, TOTAL_PER_THREAD_REGS); int preg_log[PRED_WRITES_MAX]; int preg_log_idx; DECLARE_BITMAP(pregs_written, NUM_PREGS); - DECLARE_BITMAP(pregs_read, NUM_PREGS); uint8_t store_width[STORES_MAX]; bool s1_store_processed; int future_vregs_idx; @@ -52,29 +50,35 @@ typedef struct DisasContext { int tmp_vregs_num[VECTOR_TEMPS_MAX]; int vreg_log[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(vregs_read, NUM_VREGS); + DECLARE_BITMAP(insn_vregs_read, NUM_VREGS); int qreg_log[NUM_QREGS]; int qreg_log_idx; - DECLARE_BITMAP(qregs_read, NUM_QREGS); + 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 has_hvx_helper; + 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; +bool is_gather_store_insn(DisasContext *ctx); + static inline void ctx_log_pred_write(DisasContext *ctx, int pnum) { if (!test_bit(pnum, ctx->pregs_written)) { @@ -86,7 +90,14 @@ static inline void ctx_log_pred_write(DisasContext *ctx, int pnum) static inline void ctx_log_pred_read(DisasContext *ctx, int pnum) { - set_bit(pnum, ctx->pregs_read); + 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, @@ -117,7 +128,14 @@ static inline void ctx_log_reg_write_pair(DisasContext *ctx, int rnum, static inline void ctx_log_reg_read(DisasContext *ctx, int rnum) { - set_bit(rnum, ctx->regs_read); + 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) @@ -131,10 +149,25 @@ 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) { if (!test_bit(rnum, ctx->vregs_updated)) { ctx->vreg_log[ctx->vreg_log_idx] = rnum; @@ -160,40 +193,83 @@ static inline void ctx_log_vreg_write(DisasContext *ctx, 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) +static inline void ctx_log_vreg_read(DisasContext *ctx, int rnum, + bool has_helper) { - set_bit(rnum, ctx->vregs_read); + 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_pair(DisasContext *ctx, int rnum) +static inline void ctx_log_vreg_read_new(DisasContext *ctx, int rnum, + bool has_helper) { - ctx_log_vreg_read(ctx, rnum ^ 0); - ctx_log_vreg_read(ctx, rnum ^ 1); + 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) + 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_log_idx++; } -static inline void ctx_log_qreg_read(DisasContext *ctx, int qnum) +static inline void ctx_log_qreg_read(DisasContext *ctx, + int qnum, bool has_helper) { - set_bit(qnum, ctx->qregs_read); + 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_slot_cancelled; extern TCGv hex_new_value_usr; -extern TCGv hex_reg_written[TOTAL_PER_THREAD_REGS]; extern TCGv hex_store_addr[STORES_MAX]; extern TCGv hex_store_width[STORES_MAX]; extern TCGv hex_store_val32[STORES_MAX]; @@ -205,7 +281,6 @@ 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(DisasContext *ctx); void process_store(DisasContext *ctx, int slot_num); FIELD(PROBE_PKT_SCALAR_STORE_S0, MMU_IDX, 0, 2) diff --git a/target/hppa/cpu-param.h b/target/hppa/cpu-param.h index c2791ae5f2f..9bf7ac76d0c 100644 --- a/target/hppa/cpu-param.h +++ b/target/hppa/cpu-param.h @@ -2,32 +2,23 @@ * 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_VIRT_ADDR_SPACE_BITS 32 +#if defined(CONFIG_USER_ONLY) && defined(TARGET_ABI32) # define TARGET_PHYS_ADDR_SPACE_BITS 32 +# define TARGET_VIRT_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 TARGET_INSN_START_EXTRA_WORDS 2 + #endif diff --git a/target/hppa/cpu-qom.h b/target/hppa/cpu-qom.h index b96e0318c77..5c454bf543b 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 11022f9c99a..24777727e62 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -24,78 +24,136 @@ #include "qemu/timer.h" #include "cpu.h" #include "qemu/module.h" -#include "exec/exec-all.h" +#include "exec/translation-block.h" +#include "exec/target_page.h" #include "fpu/softfloat.h" #include "tcg/tcg.h" +#include "hw/hppa/hppa_hardware.h" +#include "accel/tcg/cpu-ops.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_mask(env->gva_offset_mask, + (env->psw & PSW_C ? env->iasq_f : 0), + env->iaoq_f & -4); } -static void hppa_cpu_synchronize_from_tb(CPUState *cs, - const TranslationBlock *tb) +static TCGTBCPUState hppa_get_tb_cpu_state(CPUState *cs) { - HPPACPU *cpu = HPPA_CPU(cs); + CPUHPPAState *env = cpu_env(cs); + uint32_t flags = 0; + uint64_t cs_base = 0; + vaddr pc; + + /* + * 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; + } - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + /* ??? 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 - cpu->env.iaoq_f = tb->pc; - cpu->env.iaoq_b = tb->cs_base; + flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; #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 & ~iasq_f) + priv; - if (diff) { - cpu->env.iaoq_b = cpu->env.iaoq_f + 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; + } + if ((env->psw & PSW_W) && + (env->dr[2] & HPPA64_DIAG_SPHASH_ENABLE)) { + flags |= TB_FLAG_SPHASH; } #endif + return (TCGTBCPUState){ .pc = pc, .flags = flags, .cs_base = cs_base }; +} + +static void hppa_cpu_synchronize_from_tb(CPUState *cs, + const TranslationBlock *tb) +{ + HPPACPU *cpu = HPPA_CPU(cs); + + /* 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; } +#ifndef CONFIG_USER_ONLY static bool hppa_cpu_has_work(CPUState *cs) { return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } +#endif /* !CONFIG_USER_ONLY */ + +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; + info->endian = BFD_ENDIAN_BIG; info->print_insn = print_insn_hppa; } @@ -109,13 +167,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 */ @@ -137,63 +192,107 @@ 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) { + CPUHPPAState *env = cpu_env(CPU(obj)); + + env->is_pa20 = !!object_dynamic_cast(obj, TYPE_HPPA64_CPU); +} + +static void hppa_cpu_reset_hold(Object *obj, ResetType type) +{ + HPPACPUClass *scc = HPPA_CPU_GET_CLASS(obj); CPUState *cs = CPU(obj); HPPACPU *cpu = HPPA_CPU(obj); CPUHPPAState *env = &cpu->env; - cpu_set_cpustate_pointers(cpu); + if (scc->parent_phases.hold) { + scc->parent_phases.hold(obj, type); + } cs->exception_index = -1; + cs->halted = 0; + cpu_set_pc(cs, 0xf0000004); + + memset(env, 0, offsetof(CPUHPPAState, end_reset_fields)); + cpu_hppa_loaded_fr0(env); - cpu_hppa_put_psw(env, PSW_W); + + /* 64-bit machines start with space-register hashing enabled in %dr2 */ + env->dr[2] = hppa_is_pa20(env) ? HPPA64_DIAG_SPHASH_ENABLE : 0; + + cpu_hppa_put_psw(env, PSW_M); } 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 #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps hppa_sysemu_ops = { + .has_work = hppa_cpu_has_work, .get_phys_page_debug = hppa_cpu_get_phys_page_debug, }; #endif -#include "hw/core/tcg-cpu-ops.h" +static const TCGCPUOps hppa_tcg_ops = { + /* 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. + */ + .guest_default_memory_order = TCG_MO_ALL, + .mttcg_supported = true, -static const struct TCGCPUOps hppa_tcg_ops = { .initialize = hppa_translate_init, + .translate_code = hppa_translate_code, + .get_tb_cpu_state = hppa_get_tb_cpu_state, .synchronize_from_tb = hppa_cpu_synchronize_from_tb, .restore_state_to_opc = hppa_restore_state_to_opc, + .mmu_index = hppa_cpu_mmu_index, #ifndef CONFIG_USER_ONLY - .tlb_fill = hppa_cpu_tlb_fill, + .tlb_fill_align = hppa_cpu_tlb_fill_align, + .pointer_wrap = cpu_pointer_wrap_notreached, .cpu_exec_interrupt = hppa_cpu_exec_interrupt, + .cpu_exec_halt = hppa_cpu_has_work, + .cpu_exec_reset = cpu_reset, .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 */ }; -static void hppa_cpu_class_init(ObjectClass *oc, void *data) +static void hppa_cpu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); HPPACPUClass *acc = HPPA_CPU_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); device_class_set_parent_realize(dc, hppa_cpu_realizefn, &acc->parent_realize); + resettable_class_set_parent_phases(rc, NULL, hppa_cpu_reset_hold, NULL, + &acc->parent_phases); + cc->class_by_name = hppa_cpu_class_by_name; - cc->has_work = hppa_cpu_has_work; cc->dump_state = hppa_cpu_dump_state; cc->set_pc = hppa_cpu_set_pc; cc->get_pc = hppa_cpu_get_pc; @@ -208,19 +307,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 fa13694dab9..11d59d11ca8 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -21,30 +21,44 @@ #define HPPA_CPU_H #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" +#include "system/memory.h" #include "qemu/cpu-float.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_KERNEL_IDX 11 -#define MMU_PL1_IDX 12 -#define MMU_PL2_IDX 13 -#define MMU_USER_IDX 14 -#define MMU_PHYS_IDX 15 - -#define PRIV_TO_MMU_IDX(priv) (MMU_KERNEL_IDX + (priv)) -#define MMU_IDX_TO_PRIV(mmu_idx) ((mmu_idx) - MMU_KERNEL_IDX) - -#define TARGET_INSN_START_EXTRA_WORDS 1 - -/* No need to flush MMU_PHYS_IDX */ +#include "qemu/interval-tree.h" +#include "hw/registerfields.h" + +#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_IDX_MMU_DISABLED(MIDX) ((MIDX) < MMU_KERNEL_IDX) +#define MMU_IDX_TO_PRIV(MIDX) (((MIDX) - MMU_KERNEL_IDX) / 2) +#define MMU_IDX_TO_P(MIDX) (((MIDX) - MMU_KERNEL_IDX) & 1) +#define PRIV_P_TO_MMU_IDX(PRIV, P) ((PRIV) * 2 + !!(P) + MMU_KERNEL_IDX) + +#define PRIV_KERNEL 0 +#define PRIV_USER 3 + +/* No need to flush MMU_ABS*_IDX */ #define HPPA_MMU_FLUSH_MASK \ - (1 << MMU_KERNEL_IDX | 1 << MMU_PL1_IDX | \ - 1 << MMU_PL2_IDX | 1 << MMU_USER_IDX) + (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 */ @@ -107,11 +121,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 */ @@ -124,15 +134,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 @@ -150,45 +157,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 iaoq_f; /* front */ - target_ureg iaoq_b; /* back, aka next instruction */ + target_ulong iaoq_f; /* front */ + target_ulong iaoq_b; /* back, aka next instruction */ - target_ureg gr[32]; + 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. @@ -197,27 +221,60 @@ 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 */ + uint64_t gva_offset_mask; /* cached address mask based on PSW and %dr2 */ 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 */ + target_ulong dr[32]; /* diagnose 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]; + + /* Fields up to this point are cleared by a CPU reset */ + struct {} end_reset_fields; + + bool is_pa20; + + target_ulong kernel_entry; /* Linux kernel was loaded here */ + target_ulong cmdline_or_bootorder; + target_ulong initrd_base, initrd_end; } CPUHPPAState; /** @@ -227,50 +284,61 @@ typedef struct CPUArchState { * An HPPA CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUHPPAState env; QEMUTimer *alarm_timer; }; -#include "exec/cpu-all.h" +/** + * HPPACPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * + * An HPPA CPU model. + */ +struct HPPACPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + ResettablePhases parent_phases; +}; -static inline int cpu_mmu_index(CPUHPPAState *env, bool ifetch) +static inline bool hppa_is_pa20(const CPUHPPAState *env) { -#ifdef CONFIG_USER_ONLY - return MMU_USER_IDX; -#else - if (env->psw & (ifetch ? PSW_C : PSW_D)) { - return PRIV_TO_MMU_IDX(env->iaoq_f & 3); - } - return MMU_PHYS_IDX; /* mmu disabled */ -#endif + return env->is_pa20; +} + +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); +void hppa_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); #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 target_ulong hppa_form_gva_mask(uint64_t gva_offset_mask, + uint64_t spc, target_ulong off) { #ifdef CONFIG_USER_ONLY - return off; + return off & gva_offset_mask; #else - off &= (psw & PSW_W ? 0x3fffffffffffffffull : 0xffffffffull); - return spc | off; + return spc | (off & gva_offset_mask); #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); + return hppa_form_gva_mask(env->gva_offset_mask, 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 @@ -279,52 +347,13 @@ 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 TB_FLAG_SPHASH 0x800 +#define CS_BASE_DIFFPAGE (1 << 12) +#define CS_BASE_DIFFSPACE (1 << 13) -static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) -{ - uint32_t flags = env->psw_n * PSW_N; - - /* TB lookup assumes that PC contains the complete virtual address. - If we leave space+offset separate, we'll get ITLB misses to an - incomplete virtual address. This also means that we must separate - out current cpu privilege from the low bits of IAOQ_F. */ -#ifdef CONFIG_USER_ONLY - *pc = env->iaoq_f & -4; - *cs_base = env->iaoq_b & -4; - flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; -#else - /* ??? E, T, H, L, B, P bits need to be here, when implemented. */ - flags |= env->psw & (PSW_W | PSW_C | PSW_D); - 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 update_gva_offset_mask(CPUHPPAState *env); void cpu_hppa_loaded_fr0(CPUHPPAState *env); #ifdef CONFIG_USER_ONLY @@ -337,18 +366,24 @@ int hppa_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int hppa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void hppa_cpu_dump_state(CPUState *cs, FILE *f, int); #ifndef CONFIG_USER_ONLY +void hppa_ptlbe(CPUHPPAState *env); hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr); -bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); +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); diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c index 576f283b04b..45353202fae 100644 --- a/target/hppa/fpu_helper.c +++ b/target/hppa/fpu_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" @@ -30,7 +29,7 @@ void HELPER(loaded_fr0)(CPUHPPAState *env) env->fr0_shadow = shadow; - switch (extract32(shadow, 9, 2)) { + switch (FIELD_EX32(shadow, FPSR, RM)) { default: rm = float_round_nearest_even; break; @@ -46,9 +45,39 @@ void HELPER(loaded_fr0)(CPUHPPAState *env) } set_float_rounding_mode(rm, &env->fp_status); - d = extract32(shadow, 5, 1); + 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); + /* + * TODO: The HPPA architecture reference only documents its NaN + * propagation rule for 2-operand operations. Testing on real hardware + * might be necessary to confirm whether this order for muladd is correct. + * Not preferring the SNaN is almost certainly incorrect as it diverges + * from the documented rules for 2-operand operations. + */ + set_float_3nan_prop_rule(float_3nan_prop_abc, &env->fp_status); + /* For inf * 0 + NaN, return the input NaN */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); + /* Default NaN: sign bit clear, msb-1 frac bit set */ + set_float_default_nan_pattern(0b00100000, &env->fp_status); + set_snan_bit_is_one(true, &env->fp_status); + /* + * "PA-RISC 2.0 Architecture" says it is IMPDEF whether the flushing + * enabled by FPSR.D happens before or after rounding. We pick "before" + * for consistency with tininess detection. + */ + set_float_ftz_detection(float_ftz_before_rounding, &env->fp_status); + /* + * TODO: "PA-RISC 2.0 Architecture" chapter 10 says that we should + * detect tininess before rounding, but we don't set that here so we + * get the default tininess after rounding. + */ } void cpu_hppa_loaded_fr0(CPUHPPAState *env) @@ -57,7 +86,7 @@ void cpu_hppa_loaded_fr0(CPUHPPAState *env) } #define CONVERT_BIT(X, SRC, DST) \ - ((SRC) > (DST) \ + ((unsigned)(SRC) > (unsigned)(DST) \ ? (X) / ((SRC) / (DST)) & (DST) \ : ((X) & (SRC)) * ((DST) / (SRC))) @@ -65,7 +94,8 @@ 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; + uint32_t shadow = env->fr0_shadow & 0x3ffffff; + uint32_t fr1 = 0; if (likely(soft_exp == 0)) { env->fr[0] = (uint64_t)shadow << 32; @@ -73,14 +103,27 @@ static void update_fr0_op(CPUHPPAState *env, uintptr_t ra) } 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); + 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); + if (hard_exp & shadow) { + shadow = FIELD_DP32(shadow, FPSR, T, 1); + /* fill exception register #1, which is lower 32-bits of fr[0] */ +#if !defined(CONFIG_USER_ONLY) + if (hard_exp & (R_FPSR_ENA_O_MASK | R_FPSR_ENA_U_MASK)) { + /* over- and underflow both set overflow flag only */ + fr1 = FIELD_DP32(fr1, FPSR, C, 1); + fr1 = FIELD_DP32(fr1, FPSR, FLG_O, 1); + } else +#endif + { + fr1 |= hard_exp << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT); + } + } env->fr0_shadow = shadow; - env->fr[0] = (uint64_t)shadow << 32; + env->fr[0] = (uint64_t)shadow << 32 | fr1; if (hard_exp & shadow) { hppa_dynamic_excp(env, EXCP_ASSIST, ra); @@ -378,15 +421,15 @@ static void update_fr0_cmp(CPUHPPAState *env, uint32_t y, if (y) { /* targeted comparison */ /* set fpsr[ca[y - 1]] to current compare */ - shadow = deposit32(shadow, 21 - (y - 1), 1, c); + shadow = deposit32(shadow, R_FPSR_CA0_SHIFT - (y - 1), 1, c); } else { /* queued comparison */ /* shift cq right by one place */ - shadow = deposit32(shadow, 11, 10, extract32(shadow, 12, 10)); + shadow = (shadow & ~R_FPSR_CQ_MASK) | ((shadow >> 1) & R_FPSR_CQ_MASK); /* move fpsr[c] to fpsr[cq[0]] */ - shadow = deposit32(shadow, 21, 1, extract32(shadow, 26, 1)); + shadow = FIELD_DP32(shadow, FPSR, CQ0, FIELD_EX32(shadow, FPSR, C)); /* set fpsr[c] to current compare */ - shadow = deposit32(shadow, 26, 1, c); + shadow = FIELD_DP32(shadow, FPSR, C, c); } env->fr0_shadow = shadow; diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c index 48a514384f8..0daa52f7af2 100644 --- a/target/hppa/gdbstub.c +++ b/target/hppa/gdbstub.c @@ -21,11 +21,16 @@ #include "cpu.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 a8d3f456eee..d7f8495d982 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -21,44 +21,91 @@ #include "qemu/log.h" #include "cpu.h" #include "fpu/softfloat.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qemu/qemu-print.h" +#include "hw/hppa/hppa_hardware.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 update_gva_offset_mask(CPUHPPAState *env) +{ + uint64_t gom; + + if (env->psw & PSW_W) { + gom = (env->dr[2] & HPPA64_DIAG_SPHASH_ENABLE) + ? MAKE_64BIT_MASK(0, 62) & + ~((uint64_t)HPPA64_PDC_CACHE_RET_SPID_VAL << 48) + : MAKE_64BIT_MASK(0, 62); + } else { + gom = MAKE_64BIT_MASK(0, 32); + } + + env->gva_offset_mask = gom; +} + +void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw) { - target_ureg old_psw = env->psw; - target_ureg cb = 0; + uint64_t reserved; + target_ulong cb = 0; + + /* Do not allow reserved bits to be set. */ + if (hppa_is_pa20(env)) { + reserved = MAKE_64BIT_MASK(40, 24) | MAKE_64BIT_MASK(28, 4); + reserved |= PSW_G; /* PA1.x only */ + reserved |= PSW_E; /* not implemented */ + } else { + reserved = MAKE_64BIT_MASK(32, 32) | MAKE_64BIT_MASK(28, 2); + reserved |= PSW_O | PSW_W; /* PA2.0 only */ + reserved |= PSW_E | PSW_Y | PSW_Z; /* not implemented */ + } + psw &= ~reserved; - env->psw = psw & ~(PSW_N | PSW_V | PSW_CB); + 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; @@ -68,28 +115,47 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw) 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), HPPA_MMU_FLUSH_MASK); -#endif - } + update_gva_offset_mask(env); } 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; +#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 + + CPUHPPAState *env = cpu_env(cs); + target_ulong psw = cpu_hppa_get_psw(env); + target_ulong psw_cb; char psw_c[20]; - int i; + int i, w; + uint64_t m; + + if (hppa_is_pa20(env)) { + w = 16; + m = UINT64_MAX; + } else { + w = 8; + m = UINT32_MAX; + } - qemu_fprintf(f, "IA_F " TARGET_FMT_lx " IA_B " TARGET_FMT_lx - " IIR " TREG_FMT_lx "\n", - 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]); + 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_mask(env->gva_offset_mask, env->iasq_f, + env->iaoq_f), + env->iasq_b >> 32, w, m & env->iaoq_b, + hppa_form_gva_mask(env->gva_offset_mask, env->iasq_b, + env->iaoq_b)); psw_c[0] = (psw & PSW_W ? 'W' : '-'); psw_c[1] = (psw & PSW_E ? 'E' : '-'); @@ -110,22 +176,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 c7e35ce8c7d..8369855d78e 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,27 @@ 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_FLAGS_1(update_gva_offset_mask, 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 27341d27b28..4eaac750ea8 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 - -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 - -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 +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_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_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 - -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 - -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 +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 + +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 + +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,7 +492,7 @@ 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 @@ -528,4 +636,20 @@ 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 + diag_mfdiag 000101 dr:5 rt:5 0000 0110 0000 0000 + diag_mtdiag 000101 dr:5 r1:5 0001 0110 0000 0000 + + # For 64-bit PA8700 (PCX-W2) + diag_mfdiag 000101 dr:5 0 0000 0000 1000 101 rt:5 + diag_mtdiag 000101 dr:5 r1:5 0001 1000 0100 0000 + ] + diag_unimp 000101 i:26 +} diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index bebc732c974..191ae19404b 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -28,7 +28,7 @@ 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); @@ -52,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; + + /* 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; - cpu->env.cr[CR_EIRR] |= (target_ureg)1 << le_bit; + env->cr[CR_EIRR] |= 1ull << le_bit; eval_interrupt(cpu); } @@ -73,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) @@ -94,25 +94,40 @@ 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, old_gva_offset_mask; /* As documented in pa2.0 -- interruption handling. */ /* step 1 */ env->cr[CR_IPSW] = old_psw = cpu_hppa_get_psw(env); + old_gva_offset_mask = env->gva_offset_mask; - /* 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_mask(old_gva_offset_mask, env->iasq_f, env->iaoq_f) >> 32; + env->cr_back[0] = + hppa_form_gva_mask(old_gva_offset_mask, 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 */ @@ -120,13 +135,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: @@ -145,16 +160,16 @@ 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_mask(old_gva_offset_mask, + 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; @@ -162,6 +177,10 @@ void hppa_cpu_do_interrupt(CPUState *cs) } } env->cr[CR_IIR] = ldl_phys(cs->as, paddr); + if (i == EXCP_ASSIST) { + /* stuff insn code into bits of FP exception register #1 */ + env->fr[0] |= (env->cr[CR_IIR] & 0x03ffffff); + } } break; @@ -182,14 +201,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; @@ -228,25 +247,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]; - } - if (!name) { - snprintf(unknown, sizeof(unknown), "unknown %d", i); - name = unknown; + 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); } - 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; } @@ -265,7 +281,9 @@ 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; diff --git a/target/hppa/machine.c b/target/hppa/machine.c index 905991d7f9b..13e555151a6 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; - - memset(ent, 0, sizeof(*ent)); - - 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; + HPPATLBEntry *ent = opaque; + uint64_t val; + + 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); + + 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,29 +192,31 @@ 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_UINT64_ARRAY(dr, CPUHPPAState, 32), 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 = 4, + .minimum_version_id = 4, .fields = vmstate_env_fields, + .subsections = vmstate_env_subsections, }; -static VMStateField vmstate_cpu_fields[] = { - VMSTATE_CPU(), +static const VMStateField vmstate_cpu_fields[] = { + VMSTATE_STRUCT(parent_obj, HPPACPU, 0, vmstate_cpu_common, CPUState), 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 46c3dcaf152..9bdd0a6f23d 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -20,73 +20,216 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "accel/tcg/cpu-mmu-index.h" +#include "accel/tcg/probe.h" +#include "exec/page-protection.h" +#include "exec/target_page.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; + + if (!ent->entry_valid) { + return; + } - trace_hppa_tlb_flush_ent(env, ent, ent->va_b, ent->va_e, ent->pa); + trace_hppa_tlb_flush_ent(env, ent, ent->itree.start, + ent->itree.last, ent->pa); - for (i = 0; i < n; ++i, addr += TARGET_PAGE_SIZE) { - tlb_flush_page_by_mmuidx(cs, addr, HPPA_MMU_FLUSH_MASK); + 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; + + i = interval_tree_iter_first(&env->tlb_root, va_b, va_e); + for (; i ; i = n) { + HPPATLBEntry *ent = container_of(i, HPPATLBEntry, itree); + + /* + * 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); + } +} - env->tlb_last = (i == ARRAY_SIZE(env->tlb) - 1 ? 0 : i + 1); - ent = &env->tlb[i]; +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; - hppa_flush_tlb_ent(env, ent); + 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, priv; - hppa_tlb_entry *ent; + 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; @@ -94,7 +237,7 @@ 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. */ priv = MMU_IDX_TO_PRIV(mmu_idx); @@ -119,57 +262,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. */ + /* access_id == 0 means public page and no check is performed */ + if (ent->access_id && MMU_IDX_TO_P(mmu_idx)) { + int access_prot = (hppa_is_pa20(env) + ? match_prot_id64(env, ent->access_id) + : match_prot_id32(env, ent->access_id)); + if (unlikely(!(type & access_prot))) { + /* Not allowed -- Inst/Data Memory Protection Id Fault. */ + ret = type & PAGE_EXEC ? EXCP_IMP : EXCP_DMPI; + goto egress; + } + /* Otherwise exclude permissions not allowed (i.e WD). */ + prot &= access_prot; + } + + /* + * 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; + } + } 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; } - prot &= PAGE_READ | PAGE_EXEC; } if (unlikely(ent->b)) { + prot &= PAGE_READ | PAGE_EXEC; if (type & PAGE_WRITE) { - /* The B bit is set -- Data Memory Break Fault. */ - ret = EXCP_DMB; + /* + * 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; + } } - prot &= PAGE_READ | PAGE_EXEC; } - if (unlikely(ent->t)) { - if (!(type & PAGE_EXEC)) { - /* The T bit is set -- Page Reference Fault. */ - ret = EXCP_PAGE_REF; - } - prot &= PAGE_EXEC; + + egress_align: + if (addr & ((1u << memop_alignment_bits(mop)) - 1)) { + ret = EXCP_UNALIGN; } egress: @@ -183,16 +342,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 +359,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 +447,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); - /* If we didn't see an empty entry, evict one. */ - if (empty == NULL) { - empty = hppa_alloc_tlb_ent(env); + ent = env->tlb_partial; + if (ent == NULL) { + ent = hppa_alloc_tlb_ent(env); + env->tlb_partial = ent; } - /* 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 +513,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 +673,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), HPPA_MMU_FLUSH_MASK); + 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), HPPA_MMU_FLUSH_MASK); - } + tlb_flush_by_mmuidx(env_cpu(env), HPPA_MMU_FLUSH_P_MASK); } void HELPER(change_prot_id)(CPUHPPAState *env) @@ -350,32 +687,148 @@ 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) +{ + 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; +} + +void HELPER(update_gva_offset_mask)(CPUHPPAState *env) { - hppa_tlb_entry *ent = hppa_find_tlb(env, vaddr); - return ent ? ent->ar_type : -1; + update_gva_offset_mask(env); } diff --git a/target/hppa/meson.build b/target/hppa/meson.build index 59b68e82e2b..f47e54f5fa9 100644 --- a/target/hppa/meson.build +++ b/target/hppa/meson.build @@ -20,4 +20,4 @@ hppa_system_ss.add(files( )) target_arch += {'hppa': hppa_ss} -target_softmmu_arch += {'hppa': hppa_system_ss} +target_system_arch += {'hppa': hppa_system_ss} diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index f25a5a72aa0..0458378abb2 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -20,11 +20,14 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "qemu/timer.h" #include "trace.h" +#ifdef CONFIG_USER_ONLY +#include "user/page-protection.h" +#endif G_NORETURN void HELPER(excp)(CPUHPPAState *env, int excp) { @@ -42,46 +45,61 @@ 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()); + 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); + if (vaddr == NULL) { + cpu_loop_exit_atomic(env_cpu(env), ra); } -} + haddr = (uint32_t *)((uintptr_t)vaddr & -4); + mask = addr & 1 ? 0x00ffffffu : 0xffffff00u; -void HELPER(tcond)(CPUHPPAState *env, target_ureg cond) -{ - if (unlikely(cond)) { - hppa_dynamic_excp(env, EXCP_COND, GETPC()); + old = *haddr; + while (1) { + new = be32_to_cpu((cpu_to_be32(old) & ~mask) | (val & mask)); + cmp = qatomic_cmpxchg(haddr, old, new); + if (cmp == old) { + return; + } + old = cmp; } } -static void atomic_store_3(CPUHPPAState *env, target_ulong addr, - uint32_t val, uintptr_t ra) +static void atomic_store_mask64(CPUHPPAState *env, target_ulong addr, + uint64_t val, uint64_t mask, + int size, uintptr_t ra) { - int mmu_idx = cpu_mmu_index(env, 0); - uint32_t old, new, cmp, mask, *haddr; +#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, 3, MMU_DATA_STORE, mmu_idx, ra); + vaddr = probe_access(env, addr, size, MMU_DATA_STORE, mmu_idx, ra); if (vaddr == NULL) { cpu_loop_exit_atomic(env_cpu(env), ra); } - haddr = (uint32_t *)((uintptr_t)vaddr & -4); - mask = addr & 1 ? 0x00ffffffu : 0xffffff00u; + 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(haddr, old, new); + 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_ureg val, +static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ulong val, bool parallel, uintptr_t ra) { switch (addr & 3) { @@ -94,7 +112,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); @@ -106,25 +124,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); @@ -139,22 +224,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)) { @@ -164,13 +321,13 @@ void HELPER(ldc_check)(target_ulong addr) } } -target_ureg HELPER(probe)(CPUHPPAState *env, target_ulong addr, +target_ulong HELPER(probe)(CPUHPPAState *env, target_ulong addr, uint32_t level, uint32_t want) { #ifdef CONFIG_USER_ONLY return page_check_range(addr, 1, want); #else - int prot, excp; + int prot, excp, mmu_idx; hwaddr phys; trace_hppa_tlb_probe(addr, level, want); @@ -179,23 +336,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 } -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. @@ -208,3 +363,113 @@ target_ureg HELPER(read_interval_timer)(void) return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) >> 2; #endif } + +uint64_t HELPER(hadd_ss)(uint64_t r1, uint64_t r2) +{ + 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; +} + +uint64_t HELPER(hadd_us)(uint64_t r1, uint64_t r2) +{ + 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; +} + +uint64_t HELPER(havg)(uint64_t r1, uint64_t r2) +{ + 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; +} + +uint64_t HELPER(hsub_ss)(uint64_t r1, uint64_t r2) +{ + 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; +} + +uint64_t HELPER(hsub_us)(uint64_t r1, uint64_t r2) +{ + 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; +} + +uint64_t HELPER(hshladd)(uint64_t r1, uint64_t r2, uint32_t sh) +{ + 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; +} + +uint64_t HELPER(hshradd)(uint64_t r1, uint64_t r2, uint32_t sh) +{ + 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; +} diff --git a/target/hppa/sys_helper.c b/target/hppa/sys_helper.c index 4bb4cf611c0..6e65fadcc7b 100644 --- a/target/hppa/sys_helper.c +++ b/target/hppa/sys_helper.c @@ -18,13 +18,15 @@ */ #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 "system/runstate.h" +#include "system/system.h" +#include "chardev/char-fe.h" -void HELPER(write_interval_timer)(CPUHPPAState *env, target_ureg val) +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); @@ -58,7 +60,7 @@ void HELPER(reset)(CPUHPPAState *env) helper_excp(env, EXCP_HLT); } -target_ureg HELPER(swap_system_mask)(CPUHPPAState *env, target_ureg nsm) +target_ulong HELPER(swap_system_mask)(CPUHPPAState *env, target_ulong nsm) { target_ulong psw = env->psw; /* @@ -70,20 +72,41 @@ target_ureg HELPER(swap_system_mask)(CPUHPPAState *env, target_ureg nsm) * 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); + cpu_hppa_put_psw(env, (psw & ~PSW_SM) | (nsm & PSW_SM)); return psw & PSW_SM; } void HELPER(rfi)(CPUHPPAState *env) { - env->iasq_f = (uint64_t)env->cr[CR_IIASQ] << 32; - env->iasq_b = (uint64_t)env->cr_back[0] << 32; + 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 = env->gva_offset_mask; + env->iaoq_f = env->cr[CR_IIAOQ]; env->iaoq_b = env->cr_back[1]; - cpu_hppa_put_psw(env, env->cr[CR_IPSW]); + 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); + } + } } -void HELPER(getshadowregs)(CPUHPPAState *env) +static void getshadowregs(CPUHPPAState *env) { env->gr[1] = env->shadow[0]; env->gr[8] = env->shadow[1]; @@ -96,6 +119,40 @@ void HELPER(getshadowregs)(CPUHPPAState *env) void HELPER(rfi_r)(CPUHPPAState *env) { - helper_getshadowregs(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 8931517890d..a10ba73d5d4 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 c04dc152280..7a81cfcb887 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -19,253 +19,77 @@ #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 "tcg/tcg-op-gvec.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" +#include "exec/translation-block.h" +#include "exec/target_page.h" #include "exec/log.h" #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" #undef HELPER_H - -/* Since we have a distinction between register size and address size, - we need to redefine all of these. */ - -#undef TCGv +/* Choose to use explicit sizes within this file. */ #undef tcg_temp_new -#undef tcg_global_mem_new - -#if TARGET_LONG_BITS == 64 -#define TCGv_tl TCGv_i64 -#define tcg_temp_new_tl tcg_temp_new_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_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_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_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_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_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; + uint64_t gva_offset_mask; 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; @@ -273,19 +97,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) MO_ALIGN +#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; } @@ -320,18 +149,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" @@ -350,24 +254,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; +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), @@ -396,32 +301,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){ @@ -445,107 +360,67 @@ static DisasCond cond_make_n(void) return (DisasCond){ .c = TCG_COND_NE, .a0 = cpu_psw_n, - .a1 = tcg_constant_reg(0) - }; -} - -static DisasCond cond_make_0_tmp(TCGCond c, TCGv_reg a0) -{ - assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS); - return (DisasCond){ - .c = c, .a0 = a0, .a1 = tcg_constant_reg(0) + .a1 = tcg_constant_i64(0) }; } -static DisasCond cond_make_0(TCGCond c, TCGv_reg a0) -{ - TCGv_reg tmp = tcg_temp_new(); - tcg_gen_mov_reg(tmp, a0); - return cond_make_0_tmp(c, tmp); -} - -static DisasCond cond_make(TCGCond c, TCGv_reg a0, TCGv_reg a1) +static DisasCond cond_make_tt(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) { - 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; + return (DisasCond){ .c = c, .a0 = a0, .a1 = a1 }; } -static void cond_free(DisasCond *cond) +static DisasCond cond_make_ti(TCGCond c, TCGv_i64 a0, uint64_t imm) { - switch (cond->c) { - default: - cond->a0 = NULL; - cond->a1 = NULL; - /* fallthru */ - case TCG_COND_ALWAYS: - cond->c = TCG_COND_NEVER; - break; - case TCG_COND_NEVER: - break; - } + return cond_make_tt(c, a0, tcg_constant_i64(imm)); } -static TCGv_reg get_temp(DisasContext *ctx) +static DisasCond cond_make_vi(TCGCond c, TCGv_i64 a0, uint64_t imm) { - unsigned i = ctx->ntempr++; - g_assert(i < ARRAY_SIZE(ctx->tempr)); - return ctx->tempr[i] = tcg_temp_new(); + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_mov_i64(tmp, a0); + return cond_make_ti(c, tmp, imm); } -#ifndef CONFIG_USER_ONLY -static TCGv_tl get_temp_tl(DisasContext *ctx) +static DisasCond cond_make_vv(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) { - unsigned i = ctx->ntempl++; - g_assert(i < ARRAY_SIZE(ctx->templ)); - return ctx->templ[i] = tcg_temp_new_tl(); -} -#endif + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); -static TCGv_reg load_const(DisasContext *ctx, target_sreg v) -{ - TCGv_reg t = get_temp(ctx); - tcg_gen_movi_reg(t, v); - return t; + tcg_gen_mov_i64(t0, a0); + tcg_gen_mov_i64(t1, a1); + return cond_make_tt(c, t0, t1); } -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); @@ -563,7 +438,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 +461,7 @@ static TCGv_i64 load_frw0_i64(unsigned rt) if (rt == 0) { tcg_gen_movi_i64(ret, 0); } else { - 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)); } @@ -595,7 +470,7 @@ static TCGv_i64 load_frw0_i64(unsigned rt) 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)); } @@ -606,7 +481,7 @@ 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; } @@ -623,7 +498,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) @@ -636,11 +511,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) @@ -653,20 +547,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(); } } @@ -675,16 +569,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 @@ -693,7 +587,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); } } @@ -708,6 +602,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 @@ -736,41 +632,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) +{ + 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) { - if (unlikely(ival == -1)) { - tcg_gen_mov_reg(dest, vval); + 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) @@ -790,9 +786,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, @@ -801,21 +800,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(); } } @@ -835,23 +833,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) */ /* @@ -859,32 +873,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(); @@ -900,35 +911,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); + return do_cond(ctx, cf, d, res, NULL, sv); } + if (cf & 1) { - cond.c = tcg_invert_cond(cond.c); + tc = tcg_invert_cond(tc); } + if (!d) { + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); - return cond; + 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); } /* @@ -940,46 +971,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) -{ - 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); +static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, bool d, + TCGv_i64 res) +{ + 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; @@ -992,162 +1028,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); - } + 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); + 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); - break; - - case 4: /* SDC / NDC */ - tcg_gen_andi_reg(cb, cb, 0x88888888u); - cond = cond_make_0(TCG_COND_NE, cb); + ones = d_repl * 0x00010001u; + sgns = ones << 15; break; + } + if (ones == 0) { + /* Undefined, or 0/1 (never/always). */ + return cf & 1 ? cond_make_t() : cond_make_f(); + } - case 6: /* SBC / NBC */ - tcg_gen_andi_reg(cb, cb, 0x80808080u); - cond = cond_make_0(TCG_COND_NE, cb); - break; + /* + * 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); - case 7: /* SHC / NHC */ - tcg_gen_andi_reg(cb, cb, 0x80008000u); - cond = cond_make_0(TCG_COND_NE, cb); - break; + return cond_make_ti(cf & 1 ? TCG_COND_TSTEQ : TCG_COND_TSTNE, tmp, sgns); +} - default: - g_assert_not_reached(); - } - if (cf & 1) { - cond.c = tcg_invert_cond(cond.c); +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; +} - return cond; +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_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_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) +{ + 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_reg dest, cb, cb_msb, sv, tmp; + TCGv_i64 dest, cb, cb_msb, in1, uv, sv, tmp; unsigned c = cf >> 1; DisasCond cond; - dest = tcg_temp_new(); + dest = tcg_temp_new_i64(); cb = NULL; cb_msb = NULL; + in1 = orig_in1; if (shift) { - tmp = get_temp(ctx); - tcg_gen_shli_reg(tmp, in1, shift); + tmp = tcg_temp_new_i64(); + tcg_gen_shli_i64(tmp, in1, shift); in1 = tmp; } if (!is_l || cond_need_cb(c)) { - TCGv_reg zero = tcg_constant_reg(0); - cb_msb = get_temp(ctx); - tcg_gen_add2_reg(dest, cb_msb, in1, zero, in2, zero); + cb_msb = tcg_temp_new_i64(); + cb = tcg_temp_new_i64(); + 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_addcio_i64(dest, cb_msb, in1, in2, get_psw_carry(ctx, d)); + } else { + tcg_gen_add2_i64(dest, cb_msb, in1, ctx->zero, in2, 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); + gen_tc(ctx, &cond); } /* Write back the result. */ @@ -1158,65 +1251,74 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, save_gpr(ctx, rt, 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_addcio_i64(dest, cb_msb, in1, cb, get_psw_carry(ctx, d)); + 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. */ @@ -1224,22 +1326,20 @@ 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); + gen_tc(ctx, &cond); } /* Write back the result. */ @@ -1248,45 +1348,45 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, save_gpr(ctx, rt, dest); /* 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; @@ -1295,76 +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); /* 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); + /* 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 */ + } + 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; + } - cond = do_unit_cond(cf, dest, in1, in2); + 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 (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); + 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); } - save_gpr(ctx, rt, dest); - cond_free(&ctx->null_cond); - ctx->null_cond = cond; + 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 @@ -1372,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; } @@ -1391,52 +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); + /* 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])); 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, + ctx->gva_offset_mask); +#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 } @@ -1446,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); @@ -1482,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); @@ -1500,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); @@ -1538,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; @@ -1559,7 +1695,7 @@ static bool do_floadw(DisasContext *ctx, unsigned rt, unsigned rb, save_frw_i32(rt, tmp); if (rt == 0) { - gen_helper_loaded_fr0(cpu_env); + gen_helper_loaded_fr0(tcg_env); } return nullify_end(ctx); @@ -1572,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; @@ -1584,7 +1720,7 @@ static bool do_floadd(DisasContext *ctx, unsigned rt, unsigned rb, save_frd(rt, tmp); if (rt == 0) { - gen_helper_loaded_fr0(cpu_env); + gen_helper_loaded_fr0(tcg_env); } return nullify_end(ctx); @@ -1597,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; @@ -1626,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; @@ -1653,7 +1789,7 @@ 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); return nullify_end(ctx); @@ -1669,7 +1805,7 @@ 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); save_frw_i32(rt, dst); return nullify_end(ctx); @@ -1683,7 +1819,7 @@ 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); return nullify_end(ctx); @@ -1699,7 +1835,7 @@ 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); save_frd(rt, dst); return nullify_end(ctx); @@ -1715,7 +1851,7 @@ 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); save_frw_i32(rt, a); return nullify_end(ctx); @@ -1731,7 +1867,7 @@ 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); save_frd(rt, a); return nullify_end(ctx); @@ -1739,36 +1875,43 @@ static bool do_fop_dedd(DisasContext *ctx, unsigned rt, /* 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; @@ -1776,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; @@ -1788,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. */ @@ -1840,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; + ctx->iaq_n = &ctx->iaq_j; + ctx->psw_b_next = true; + return true; + } - if (link != 0) { - tcg_gen_movcond_reg(c, cpu_gr[link], a0, a1, cpu_gr[link], tmp); - } + nullify_over(ctx); - 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); - } + 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); } - return true; + + tcg_gen_lookup_and_goto_ptr(); + ctx->base.is_jmp = DISAS_NORETURN; + return nullify_end(ctx); } /* Implement @@ -1929,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; @@ -1961,6 +2060,8 @@ 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 privileged page. */ @@ -1968,7 +2069,7 @@ static void do_page_zero(DisasContext *ctx) 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 @@ -1976,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; @@ -1996,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 */ @@ -2018,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; } @@ -2032,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; } @@ -2052,15 +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); + save_gpr(ctx, rt, t0); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2068,19 +2170,17 @@ 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 */ @@ -2088,11 +2188,9 @@ static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a) nullify_over(ctx); tmp = dest_gpr(ctx, rt); if (translator_io_start(&ctx->base)) { - gen_helper_read_interval_timer(tmp); 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: @@ -2104,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; } @@ -2117,22 +2215,21 @@ 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); } return nullify_end(ctx); @@ -2141,16 +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); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2159,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; @@ -2177,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; @@ -2189,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); @@ -2205,48 +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); - 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_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. */ @@ -2259,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. */ @@ -2279,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; @@ -2299,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); @@ -2325,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 @@ -2336,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); @@ -2380,15 +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); + gen_helper_probe(dest, tcg_env, addr, level, want); save_gpr(ctx, a->t, dest); return nullify_end(ctx); @@ -2396,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. */ @@ -2419,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) { @@ -2454,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); @@ -2465,30 +2654,54 @@ 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_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); + } + + /* 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_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; @@ -2501,15 +2714,15 @@ 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) { @@ -2529,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; @@ -2608,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 @@ -2632,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; @@ -2649,142 +2864,173 @@ 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); - - /* 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); + 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 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_gen_addcio_i64(dest, cpu_psw_cb_msb, add1, add2, addc); /* 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); } return nullify_end(ctx); @@ -2820,59 +3066,276 @@ 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 (unlikely(TARGET_REGISTER_BITS == 32 && a->size > MO_32)) { + 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); - } else { - 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); } + 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); } static bool trans_st(DisasContext *ctx, arg_ldst *a) { assert(a->x == 0 && a->scale == 0); - if (unlikely(TARGET_REGISTER_BITS == 32 && a->size > MO_32)) { + if (!ctx->is_pa20 && a->size > MO_32) { return gen_illegal(ctx); - } else { - return do_store(ctx, a->t, a->b, a->disp, a->sp, a->m, a->size | MO_TE); } + 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. @@ -2884,42 +3347,75 @@ 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); + } + save_gpr(ctx, a->t, dest); + + return nullify_end(ctx); +} + +static bool trans_stby(DisasContext *ctx, arg_stby *a) +{ + 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, + 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(tcg_env, addr, val); + } else { + gen_helper_stby_e(tcg_env, addr, val); + } + } else { + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + gen_helper_stby_b_parallel(tcg_env, addr, val); + } else { + gen_helper_stby_b(tcg_env, addr, val); + } + } if (a->m) { + tcg_gen_andi_i64(ofs, ofs, ~3); save_gpr(ctx, a->b, ofs); } - save_gpr(ctx, a->t, dest); return nullify_end(ctx); } -static bool trans_stby(DisasContext *ctx, arg_stby *a) +static bool trans_stdby(DisasContext *ctx, arg_stby *a) { - TCGv_reg ofs, val; - TCGv_tl addr; + 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, - 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_stdby_e_parallel(tcg_env, addr, val); } else { - gen_helper_stby_e(cpu_env, addr, val); + gen_helper_stdby_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_stdby_b_parallel(tcg_env, addr, val); } else { - gen_helper_stby_b(cpu_env, addr, val); + gen_helper_stdby_b(tcg_env, addr, val); } } if (a->m) { - tcg_gen_andi_reg(ofs, ofs, ~3); + tcg_gen_andi_i64(ofs, ofs, ~7); save_gpr(ctx, a->b, ofs); } @@ -2931,7 +3427,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; @@ -2942,7 +3438,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; @@ -2950,96 +3446,120 @@ 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_add2_i64(dest, cb_msb, in1, ctx->zero, in2, ctx->zero); + 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); return do_cbranch(ctx, disp, n, &cond); } @@ -3053,213 +3573,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); + 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); + 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); + 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_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_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); + 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); } 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); @@ -3267,275 +3845,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_temp_new(); - tcg_gen_movi_reg(mask, 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); } 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_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); - - nullify_over(ctx); + int64_t disp = a->disp; + bool indirect = false; - /* 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; - -#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)); + TCGv_i64 b = 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; } /* @@ -3551,7 +4085,7 @@ static bool trans_fid_f(DisasContext *ctx, arg_fid_f *a) { uint64_t ret; - if (TARGET_REGISTER_BITS == 64) { + if (ctx->is_pa20) { ret = 0x13080000000000ULL; /* PA8700 (PCX-W2) */ } else { ret = 0x0f080000000000ULL; /* PA7300LC (PCX-L2) */ @@ -3806,7 +4340,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); + gen_helper_fcmp_s(tcg_env, ta, tb, ty, tc); return nullify_end(ctx); } @@ -3823,70 +4357,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); + 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); + mask = R_FPSR_CA0_MASK >> cbit; } - done: + ctx->null_cond = cond_make_ti(tc, t, mask); return nullify_end(ctx); } @@ -4012,9 +4534,9 @@ 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); } save_frw_i32(a->t, x); @@ -4031,60 +4553,124 @@ 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); } save_frd(a->t, 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) +{ + 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_putshadowregs_pa1(DisasContext *ctx, arg_empty *a) +{ + return !ctx->is_pa20 && do_putshadowregs(ctx); +} + +static bool trans_diag_mfdiag(DisasContext *ctx, arg_diag_mfdiag *a) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); + nullify_over(ctx); + TCGv_i64 dest = dest_gpr(ctx, a->rt); + tcg_gen_ld_i64(dest, tcg_env, + offsetof(CPUHPPAState, dr[a->dr])); + save_gpr(ctx, a->rt, dest); + return nullify_end(ctx); +} + +static bool trans_diag_mtdiag(DisasContext *ctx, arg_diag_mtdiag *a) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); + nullify_over(ctx); + tcg_gen_st_i64(load_gpr(ctx, a->r1), tcg_env, + offsetof(CPUHPPAState, dr[a->dr])); +#ifndef CONFIG_USER_ONLY + if (ctx->is_pa20 && (a->dr == 2)) { + /* Update gva_offset_mask from the new value of %dr2 */ + gen_helper_update_gva_offset_mask(tcg_env); + /* Exit to capture the new value for the next TB. */ + ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT; + } +#endif + return nullify_end(ctx); +} + +static bool trans_diag_unimp(DisasContext *ctx, arg_diag_unimp *a) { - qemu_log_mask(LOG_UNIMP, "DIAG opcode ignored\n"); - cond_free(&ctx->null_cond); + 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); + ctx->gva_offset_mask = cpu_env(cs)->gva_offset_mask; #ifdef CONFIG_USER_ONLY - ctx->privilege = MMU_IDX_TO_PRIV(MMU_USER_IDX); + ctx->privilege = PRIV_USER; ctx->mmu_idx = MMU_USER_IDX; - ctx->iaoq_f = ctx->base.pc_first | ctx->privilege; - ctx->iaoq_b = ctx->base.tb->cs_base | ctx->privilege; ctx->unalign = (ctx->tb_flags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN); #else ctx->privilege = (ctx->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3; - ctx->mmu_idx = (ctx->tb_flags & PSW_D ? - PRIV_TO_MMU_IDX(ctx->privilege) : MMU_PHYS_IDX); + ctx->mmu_idx = (ctx->tb_flags & PSW_D + ? PRIV_P_TO_MMU_IDX(ctx->privilege, ctx->tb_flags & PSW_P) + : ctx->tb_flags & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX); +#endif - /* 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; + cs_base = ctx->base.tb->cs_base; + ctx->iaoq_first = ctx->base.pc_first + ctx->privilege; - ctx->iaoq_f = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; - ctx->iaoq_b = (diff ? ctx->iaoq_f + diff : -1); -#endif - ctx->iaoq_n = -1; - ctx->iaoq_n_var = NULL; + 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) @@ -4104,16 +4690,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 @@ -4128,16 +4729,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; @@ -4150,61 +4748,47 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ret = ctx->base.is_jmp; assert(ctx->null_lab == NULL); } + + if (ret != DISAS_NORETURN) { + set_psw_xb(ctx, ctx->psw_b_next ? PSW_B : 0); + } } - /* Forget any temporaries allocated. */ - for (i = 0, n = ctx->ntempr; i < n; ++i) { - ctx->tempr[i] = NULL; + /* If the TranslationBlock must end, do so. */ + ctx->base.pc_next += 4; + if (ret != DISAS_NEXT) { + return; } - for (i = 0, n = ctx->ntempl; i < n; ++i) { - ctx->templ[i] = NULL; + /* 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; } - 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; - } + /* + * 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; } - ctx->iaoq_f = ctx->iaoq_b; - ctx->iaoq_b = ctx->iaoq_n; - ctx->base.pc_next += 4; - - 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(); + /* + * 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; } } @@ -4212,56 +4796,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: + /* The insn queue has not been advanced. */ + f = &ctx->iaq_f; + b = &ctx->iaq_b; + /* FALLTHRU */ 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: - 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, f, b); nullify_save(ctx); - /* FALLTHRU */ - case DISAS_IAQ_N_UPDATED: - if (is_jmp != DISAS_IAQ_N_STALE_EXIT) { - tcg_gen_lookup_and_goto_ptr(); + 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, @@ -4269,12 +4879,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 hppa_translate_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 ce6968906ee..6b0feef0299 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_dump.c b/target/i386/arch_dump.c index c290910a04b..16e47c4747d 100644 --- a/target/i386/arch_dump.c +++ b/target/i386/arch_dump.c @@ -13,9 +13,9 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "sysemu/dump.h" +#include "system/dump.h" #include "elf.h" -#include "sysemu/memory_mapping.h" +#include "system/memory_mapping.h" #define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \ ((DIV_ROUND_UP((hdr_size), 4) \ diff --git a/target/i386/arch_memory_mapping.c b/target/i386/arch_memory_mapping.c index 271cb5e41b4..a2398c21732 100644 --- a/target/i386/arch_memory_mapping.c +++ b/target/i386/arch_memory_mapping.c @@ -13,7 +13,8 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "sysemu/memory_mapping.h" +#include "system/memory_mapping.h" +#include "system/memory.h" /* PAE Paging or IA-32e Paging */ static void walk_pte(MemoryMappingList *list, AddressSpace *as, @@ -266,7 +267,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 +276,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 +311,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 00000000000..cfb71bf0349 --- /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, const 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 00000000000..48b88dbd313 --- /dev/null +++ b/target/i386/confidential-guest.h @@ -0,0 +1,107 @@ +/* + * 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 "system/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); + void (*cpu_instance_init)(X86ConfidentialGuest *cg, CPUState *cpu); + uint32_t (*adjust_cpuid_features)(X86ConfidentialGuest *cg, uint32_t feature, + uint32_t index, int reg, uint32_t value); + int (*check_features)(X86ConfidentialGuest *cg, CPUState *cs); +}; + +/** + * x86_confidential_guest_kvm_type: + * + * Calls #X86ConfidentialGuestClass.kvm_type() callback. + */ +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; + } +} + +static inline void x86_confidential_guest_cpu_instance_init(X86ConfidentialGuest *cg, + CPUState *cpu) +{ + X86ConfidentialGuestClass *klass = X86_CONFIDENTIAL_GUEST_GET_CLASS(cg); + + if (klass->cpu_instance_init) { + klass->cpu_instance_init(cg, cpu); + } +} + +/** + * x86_confidential_guest_adjust_cpuid_features: + * + * Adjust the supported features from a confidential guest's CPUID values, + * returns the adjusted value. There are bits being removed that are not + * supported by the confidential computing firmware or bits being added that + * are forcibly exposed to guest by the confidential computing firmware. + */ +static inline int x86_confidential_guest_adjust_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->adjust_cpuid_features) { + return klass->adjust_cpuid_features(cg, feature, index, reg, value); + } else { + return value; + } +} + +static inline int x86_confidential_guest_check_features(X86ConfidentialGuest *cg, + CPUState *cs) +{ + X86ConfidentialGuestClass *klass = X86_CONFIDENTIAL_GUEST_GET_CLASS(cg); + + if (klass->check_features) { + return klass->check_features(cg, cs); + } + + return 0; +} + +#endif diff --git a/target/i386/cpu-apic.c b/target/i386/cpu-apic.c new file mode 100644 index 00000000000..242a05fdbe9 --- /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 "qobject/qdict.h" +#include "qapi/error.h" +#include "monitor/monitor.h" +#include "monitor/hmp-target.h" +#include "system/hw_accel.h" +#include "system/kvm.h" +#include "system/xen.h" +#include "system/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 40697064d92..a72ed93bd2f 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", - - "MULB", - "MULW", - "MULL", - "MULQ", - - "ADDB", - "ADDW", - "ADDL", - "ADDQ", - - "ADCB", - "ADCW", - "ADCL", - "ADCQ", - - "SUBB", - "SUBW", - "SUBL", - "SUBQ", - - "SBBB", - "SBBW", - "SBBL", - "SBBQ", - - "LOGICB", - "LOGICW", - "LOGICL", - "LOGICQ", - - "INCB", - "INCW", - "INCL", - "INCQ", - - "DECB", - "DECW", - "DECL", - "DECQ", - - "SHLB", - "SHLW", - "SHLL", - "SHLQ", - - "SARB", - "SARW", - "SARL", - "SARQ", - - "BMILGB", - "BMILGW", - "BMILGL", - "BMILGQ", - - "ADCX", - "ADOX", - "ADCOX", - - "CLR", +static const char * const cc_op_str[] = { + [CC_OP_DYNAMIC] = "DYNAMIC", + + [CC_OP_EFLAGS] = "EFLAGS", + [CC_OP_ADCX] = "ADCX", + [CC_OP_ADOX] = "ADOX", + [CC_OP_ADCOX] = "ADCOX", + + [CC_OP_MULB] = "MULB", + [CC_OP_MULW] = "MULW", + [CC_OP_MULL] = "MULL", + [CC_OP_MULQ] = "MULQ", + + [CC_OP_ADDB] = "ADDB", + [CC_OP_ADDW] = "ADDW", + [CC_OP_ADDL] = "ADDL", + [CC_OP_ADDQ] = "ADDQ", + + [CC_OP_ADCB] = "ADCB", + [CC_OP_ADCW] = "ADCW", + [CC_OP_ADCL] = "ADCL", + [CC_OP_ADCQ] = "ADCQ", + + [CC_OP_SUBB] = "SUBB", + [CC_OP_SUBW] = "SUBW", + [CC_OP_SUBL] = "SUBL", + [CC_OP_SUBQ] = "SUBQ", + + [CC_OP_SBBB] = "SBBB", + [CC_OP_SBBW] = "SBBW", + [CC_OP_SBBL] = "SBBL", + [CC_OP_SBBQ] = "SBBQ", + + [CC_OP_LOGICB] = "LOGICB", + [CC_OP_LOGICW] = "LOGICW", + [CC_OP_LOGICL] = "LOGICL", + [CC_OP_LOGICQ] = "LOGICQ", + + [CC_OP_INCB] = "INCB", + [CC_OP_INCW] = "INCW", + [CC_OP_INCL] = "INCL", + [CC_OP_INCQ] = "INCQ", + + [CC_OP_DECB] = "DECB", + [CC_OP_DECW] = "DECW", + [CC_OP_DECL] = "DECL", + [CC_OP_DECQ] = "DECQ", + + [CC_OP_SHLB] = "SHLB", + [CC_OP_SHLW] = "SHLW", + [CC_OP_SHLL] = "SHLL", + [CC_OP_SHLQ] = "SHLQ", + + [CC_OP_SARB] = "SARB", + [CC_OP_SARW] = "SARW", + [CC_OP_SARL] = "SARL", + [CC_OP_SARQ] = "SARQ", + + [CC_OP_BMILGB] = "BMILGB", + [CC_OP_BMILGW] = "BMILGW", + [CC_OP_BMILGL] = "BMILGL", + [CC_OP_BMILGQ] = "BMILGQ", + + [CC_OP_POPCNT] = "POPCNT", }; static void @@ -346,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); @@ -455,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-internal.h b/target/i386/cpu-internal.h index 9baac5c0b45..37c61a1bc3d 100644 --- a/target/i386/cpu-internal.h +++ b/target/i386/cpu-internal.h @@ -1,5 +1,5 @@ /* - * i386 CPU internal definitions to be shared between cpu.c and cpu-sysemu.c + * i386 CPU internal definitions to be shared between cpu.c and cpu-system.c * * Copyright (c) 2003 Fabrice Bellard * diff --git a/target/i386/cpu-param.h b/target/i386/cpu-param.h index 911b4cd51b2..ebb844bcc83 100644 --- a/target/i386/cpu-param.h +++ b/target/i386/cpu-param.h @@ -2,14 +2,13 @@ * 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 #define I386_CPU_PARAM_H #ifdef TARGET_X86_64 -# define TARGET_LONG_BITS 64 # define TARGET_PHYS_ADDR_SPACE_BITS 52 /* * ??? This is really 48 bits, sign-extended, but the only thing @@ -18,10 +17,11 @@ */ # define TARGET_VIRT_ADDR_SPACE_BITS 47 #else -# define TARGET_LONG_BITS 32 # define TARGET_PHYS_ADDR_SPACE_BITS 36 # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif #define TARGET_PAGE_BITS 12 +#define TARGET_INSN_START_EXTRA_WORDS 1 + #endif diff --git a/target/i386/cpu-qom.h b/target/i386/cpu-qom.h index 2350f4ae609..d4e216d000c 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_phases: The parent class' reset phase handlers. - * - * 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; - ResettablePhases parent_phases; -}; - +#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 deleted file mode 100644 index 2375e48178f..00000000000 --- a/target/i386/cpu-sysemu.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - * i386 CPUID, CPU class, definitions, models: sysemu-only code - * - * 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 "cpu.h" -#include "sysemu/kvm.h" -#include "sysemu/xen.h" -#include "sysemu/whpx.h" -#include "qapi/error.h" -#include "qapi/qapi-visit-run-state.h" -#include "qapi/qmp/qdict.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" - -/* Return a QDict containing keys for all properties that can be included - * in static expansion of CPU models. All properties set by x86_cpu_load_model() - * must be included in the dictionary. - */ -static QDict *x86_cpu_static_props(void) -{ - FeatureWord w; - int i; - static const char *props[] = { - "min-level", - "min-xlevel", - "family", - "model", - "stepping", - "model-id", - "vendor", - "lmce", - NULL, - }; - static QDict *d; - - if (d) { - return d; - } - - d = qdict_new(); - for (i = 0; props[i]; i++) { - qdict_put_null(d, props[i]); - } - - for (w = 0; w < FEATURE_WORDS; w++) { - FeatureWordInfo *fi = &feature_word_info[w]; - int bit; - for (bit = 0; bit < 64; bit++) { - if (!fi->feat_names[bit]) { - continue; - } - qdict_put_null(d, fi->feat_names[bit]); - } - } - - return d; -} - -/* Add an entry to @props dict, with the value for property. */ -static void x86_cpu_expand_prop(X86CPU *cpu, QDict *props, const char *prop) -{ - QObject *value = object_property_get_qobject(OBJECT(cpu), prop, - &error_abort); - - qdict_put_obj(props, prop, value); -} - -/* Convert CPU model data from X86CPU object to a property dictionary - * that can recreate exactly the same CPU model. - */ -static void x86_cpu_to_dict(X86CPU *cpu, QDict *props) -{ - QDict *sprops = x86_cpu_static_props(); - const QDictEntry *e; - - for (e = qdict_first(sprops); e; e = qdict_next(sprops, e)) { - const char *prop = qdict_entry_key(e); - x86_cpu_expand_prop(cpu, props, prop); - } -} - -/* Convert CPU model data from X86CPU object to a property dictionary - * that can recreate exactly the same CPU model, including every - * writable QOM property. - */ -static void x86_cpu_to_dict_full(X86CPU *cpu, QDict *props) -{ - ObjectPropertyIterator iter; - ObjectProperty *prop; - - object_property_iter_init(&iter, OBJECT(cpu)); - while ((prop = object_property_iter_next(&iter))) { - /* skip read-only or write-only properties */ - if (!prop->get || !prop->set) { - continue; - } - - /* "hotplugged" is the only property that is configurable - * on the command-line but will be set differently on CPUs - * created using "-cpu ... -smp ..." and by CPUs created - * on the fly by x86_cpu_from_model() for querying. Skip it. - */ - if (!strcmp(prop->name, "hotplugged")) { - continue; - } - x86_cpu_expand_prop(cpu, props, prop->name); - } -} - -static void object_apply_props(Object *obj, QDict *props, Error **errp) -{ - 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; - } - } -} - -/* Create X86CPU object according to model+props specification */ -static X86CPU *x86_cpu_from_model(const char *model, QDict *props, Error **errp) -{ - X86CPU *xc = NULL; - X86CPUClass *xcc; - Error *err = NULL; - - xcc = X86_CPU_CLASS(cpu_class_by_name(TYPE_X86_CPU, model)); - if (xcc == NULL) { - error_setg(&err, "CPU model '%s' not found", model); - goto out; - } - - xc = X86_CPU(object_new_with_class(OBJECT_CLASS(xcc))); - if (props) { - object_apply_props(OBJECT(xc), props, &err); - if (err) { - goto out; - } - } - - x86_cpu_expand_features(xc, &err); - if (err) { - goto out; - } - -out: - if (err) { - error_propagate(errp, err); - object_unref(OBJECT(xc)); - xc = NULL; - } - return xc; -} - -CpuModelExpansionInfo * -qmp_query_cpu_model_expansion(CpuModelExpansionType type, - CpuModelInfo *model, - Error **errp) -{ - X86CPU *xc = NULL; - Error *err = NULL; - CpuModelExpansionInfo *ret = g_new0(CpuModelExpansionInfo, 1); - QDict *props = NULL; - const char *base_name; - - xc = x86_cpu_from_model(model->name, qobject_to(QDict, model->props), - &err); - if (err) { - goto out; - } - - props = qdict_new(); - ret->model = g_new0(CpuModelInfo, 1); - ret->model->props = QOBJECT(props); - - switch (type) { - case CPU_MODEL_EXPANSION_TYPE_STATIC: - /* Static expansion will be based on "base" only */ - base_name = "base"; - x86_cpu_to_dict(xc, props); - break; - case CPU_MODEL_EXPANSION_TYPE_FULL: - /* As we don't return every single property, full expansion needs - * to keep the original model name+props, and add extra - * properties on top of that. - */ - base_name = model->name; - x86_cpu_to_dict_full(xc, props); - break; - default: - error_setg(&err, "Unsupported expansion type"); - goto out; - } - - x86_cpu_to_dict(xc, props); - - ret->model->name = g_strdup(base_name); - -out: - object_unref(OBJECT(xc)); - if (err) { - error_propagate(errp, err); - qapi_free_CpuModelExpansionInfo(ret); - ret = NULL; - } - return ret; -} - -void cpu_clear_apic_feature(CPUX86State *env) -{ - env->features[FEAT_1_EDX] &= ~CPUID_APIC; -} - -bool cpu_is_bsp(X86CPU *cpu) -{ - return cpu_get_apic_base(cpu->apic_state) & MSR_IA32_APICBASE_BSP; -} - -/* TODO: remove me, when reset over QOM tree is implemented */ -void x86_cpu_machine_reset_cb(void *opaque) -{ - X86CPU *cpu = opaque; - cpu_reset(CPU(cpu)); -} - -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)); - - 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); - CPUX86State *env = &cpu->env; - GuestPanicInformation *panic_info = NULL; - - if (hyperv_feat_enabled(cpu, HYPERV_FEAT_CRASH)) { - panic_info = g_new0(GuestPanicInformation, 1); - - panic_info->type = GUEST_PANIC_INFORMATION_TYPE_HYPER_V; - - assert(HV_CRASH_PARAMS >= 5); - panic_info->u.hyper_v.arg1 = env->msr_hv_crash_params[0]; - panic_info->u.hyper_v.arg2 = env->msr_hv_crash_params[1]; - panic_info->u.hyper_v.arg3 = env->msr_hv_crash_params[2]; - panic_info->u.hyper_v.arg4 = env->msr_hv_crash_params[3]; - panic_info->u.hyper_v.arg5 = env->msr_hv_crash_params[4]; - } - - return panic_info; -} -void x86_cpu_get_crash_info_qom(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - CPUState *cs = CPU(obj); - GuestPanicInformation *panic_info; - - if (!cs->crash_occurred) { - error_setg(errp, "No crash occurred"); - return; - } - - panic_info = x86_cpu_get_crash_info(cs); - if (panic_info == NULL) { - error_setg(errp, "No crash information"); - return; - } - - visit_type_GuestPanicInformation(v, "crash-information", &panic_info, - errp); - qapi_free_GuestPanicInformation(panic_info); -} - diff --git a/target/i386/cpu-system.c b/target/i386/cpu-system.c new file mode 100644 index 00000000000..b1494aa6740 --- /dev/null +++ b/target/i386/cpu-system.c @@ -0,0 +1,322 @@ +/* + * i386 CPUID, CPU class, definitions, models: system-only code + * + * 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 "cpu.h" +#include "qapi/error.h" +#include "qapi/qapi-visit-run-state.h" +#include "qobject/qdict.h" +#include "qapi/qobject-input-visitor.h" +#include "qom/qom-qobject.h" +#include "qapi/qapi-commands-machine.h" + +#include "cpu-internal.h" + +/* Return a QDict containing keys for all properties that can be included + * in static expansion of CPU models. All properties set by x86_cpu_load_model() + * must be included in the dictionary. + */ +static QDict *x86_cpu_static_props(void) +{ + FeatureWord w; + int i; + static const char *props[] = { + "min-level", + "min-xlevel", + "family", + "model", + "stepping", + "model-id", + "vendor", + "lmce", + NULL, + }; + static QDict *d; + + if (d) { + return d; + } + + d = qdict_new(); + for (i = 0; props[i]; i++) { + qdict_put_null(d, props[i]); + } + + for (w = 0; w < FEATURE_WORDS; w++) { + FeatureWordInfo *fi = &feature_word_info[w]; + int bit; + for (bit = 0; bit < 64; bit++) { + if (!fi->feat_names[bit]) { + continue; + } + qdict_put_null(d, fi->feat_names[bit]); + } + } + + return d; +} + +/* Add an entry to @props dict, with the value for property. */ +static void x86_cpu_expand_prop(X86CPU *cpu, QDict *props, const char *prop) +{ + QObject *value = object_property_get_qobject(OBJECT(cpu), prop, + &error_abort); + + qdict_put_obj(props, prop, value); +} + +/* Convert CPU model data from X86CPU object to a property dictionary + * that can recreate exactly the same CPU model. + */ +static void x86_cpu_to_dict(X86CPU *cpu, QDict *props) +{ + QDict *sprops = x86_cpu_static_props(); + const QDictEntry *e; + + for (e = qdict_first(sprops); e; e = qdict_next(sprops, e)) { + const char *prop = qdict_entry_key(e); + x86_cpu_expand_prop(cpu, props, prop); + } +} + +/* Convert CPU model data from X86CPU object to a property dictionary + * that can recreate exactly the same CPU model, including every + * writable QOM property. + */ +static void x86_cpu_to_dict_full(X86CPU *cpu, QDict *props) +{ + ObjectPropertyIterator iter; + ObjectProperty *prop; + + object_property_iter_init(&iter, OBJECT(cpu)); + while ((prop = object_property_iter_next(&iter))) { + /* skip read-only or write-only properties */ + if (!prop->get || !prop->set) { + continue; + } + + /* "hotplugged" is the only property that is configurable + * on the command-line but will be set differently on CPUs + * created using "-cpu ... -smp ..." and by CPUs created + * on the fly by x86_cpu_from_model() for querying. Skip it. + */ + if (!strcmp(prop->name, "hotplugged")) { + continue; + } + x86_cpu_expand_prop(cpu, props, prop->name); + } +} + +static void object_apply_props(Object *obj, QObject *props, + const char *props_arg_name, Error **errp) +{ + Visitor *visitor; + QDict *qdict; + const QDictEntry *prop; + + 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, QObject *props, + const char *props_arg_name, Error **errp) +{ + X86CPU *xc = NULL; + X86CPUClass *xcc; + Error *err = NULL; + + xcc = X86_CPU_CLASS(cpu_class_by_name(TYPE_X86_CPU, model)); + if (xcc == NULL) { + error_setg(&err, "CPU model '%s' not found", model); + goto out; + } + + xc = X86_CPU(object_new_with_class(OBJECT_CLASS(xcc))); + if (props) { + object_apply_props(OBJECT(xc), props, props_arg_name, &err); + if (err) { + goto out; + } + } + + x86_cpu_expand_features(xc, &err); + if (err) { + goto out; + } + +out: + if (err) { + error_propagate(errp, err); + object_unref(OBJECT(xc)); + xc = NULL; + } + return xc; +} + +CpuModelExpansionInfo * +qmp_query_cpu_model_expansion(CpuModelExpansionType type, + CpuModelInfo *model, + Error **errp) +{ + X86CPU *xc = NULL; + Error *err = NULL; + CpuModelExpansionInfo *ret = g_new0(CpuModelExpansionInfo, 1); + QDict *props = NULL; + const char *base_name; + + xc = x86_cpu_from_model(model->name, model->props, "model.props", &err); + if (err) { + goto out; + } + + props = qdict_new(); + ret->model = g_new0(CpuModelInfo, 1); + ret->model->props = QOBJECT(props); + + switch (type) { + case CPU_MODEL_EXPANSION_TYPE_STATIC: + /* Static expansion will be based on "base" only */ + base_name = "base"; + x86_cpu_to_dict(xc, props); + break; + case CPU_MODEL_EXPANSION_TYPE_FULL: + /* As we don't return every single property, full expansion needs + * to keep the original model name+props, and add extra + * properties on top of that. + */ + base_name = model->name; + x86_cpu_to_dict_full(xc, props); + break; + default: + error_setg(&err, "Unsupported expansion type"); + goto out; + } + + x86_cpu_to_dict(xc, props); + + ret->model->name = g_strdup(base_name); + +out: + object_unref(OBJECT(xc)); + if (err) { + error_propagate(errp, err); + qapi_free_CpuModelExpansionInfo(ret); + ret = NULL; + } + return ret; +} + +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; +} + +/* TODO: remove me, when reset over QOM tree is implemented */ +void x86_cpu_machine_reset_cb(void *opaque) +{ + X86CPU *cpu = opaque; + cpu_reset(CPU(cpu)); +} + +GuestPanicInformation *x86_cpu_get_crash_info(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + GuestPanicInformation *panic_info = NULL; + + if (hyperv_feat_enabled(cpu, HYPERV_FEAT_CRASH)) { + panic_info = g_new0(GuestPanicInformation, 1); + + panic_info->type = GUEST_PANIC_INFORMATION_TYPE_HYPER_V; + + assert(HV_CRASH_PARAMS >= 5); + panic_info->u.hyper_v.arg1 = env->msr_hv_crash_params[0]; + panic_info->u.hyper_v.arg2 = env->msr_hv_crash_params[1]; + panic_info->u.hyper_v.arg3 = env->msr_hv_crash_params[2]; + panic_info->u.hyper_v.arg4 = env->msr_hv_crash_params[3]; + panic_info->u.hyper_v.arg5 = env->msr_hv_crash_params[4]; + } + + return panic_info; +} +void x86_cpu_get_crash_info_qom(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CPUState *cs = CPU(obj); + GuestPanicInformation *panic_info; + + if (!cs->crash_occurred) { + error_setg(errp, "No crash occurred"); + return; + } + + panic_info = x86_cpu_get_crash_info(cs); + if (panic_info == NULL) { + error_setg(errp, "No crash information"); + return; + } + + visit_type_GuestPanicInformation(v, "crash-information", &panic_info, + errp); + qapi_free_GuestPanicInformation(panic_info); +} + +uint64_t cpu_x86_get_msr_core_thread_count(X86CPU *cpu) +{ + CPUX86State *env = &cpu->env; + uint64_t val; + + val = x86_threads_per_pkg(&env->topo_info); /* thread count, bits 15..0 */ + val |= x86_cores_per_pkg(&env->topo_info) << 16; /* core count, bits 31..16 */ + + return val; +} diff --git a/target/i386/cpu.c b/target/i386/cpu.c index b2a20365e10..0d35e95430f 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -24,28 +24,35 @@ #include "qemu/hw-version.h" #include "cpu.h" #include "tcg/helper-tcg.h" -#include "sysemu/reset.h" -#include "sysemu/hvf.h" +#include "exec/translation-block.h" +#include "system/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 "standard-headers/asm-x86/kvm_para.h" #include "hw/qdev-properties.h" #include "hw/i386/topology.h" +#include "exec/watchpoint.h" #ifndef CONFIG_USER_ONLY -#include "qapi/qapi-commands-machine-target.h" -#include "exec/address-spaces.h" +#include "confidential-guest.h" +#include "system/reset.h" +#include "qapi/qapi-commands-machine.h" +#include "system/address-spaces.h" #include "hw/boards.h" #include "hw/i386/sgx-epc.h" #endif +#include "tcg/tcg-cpu.h" #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: */ @@ -234,22 +241,56 @@ 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_MODULE: + num_ids = 1 << apicid_module_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, 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); @@ -268,6 +309,123 @@ 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 x86_threads_per_module(topo_info); + case CPU_TOPOLOGY_LEVEL_DIE: + return x86_threads_per_die(topo_info); + case CPU_TOPOLOGY_LEVEL_SOCKET: + return x86_threads_per_pkg(topo_info); + 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) { @@ -330,20 +488,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); @@ -397,12 +547,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. @@ -411,8 +558,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; } @@ -434,6 +585,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 */ @@ -448,6 +600,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: */ @@ -461,6 +614,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 */ @@ -475,6 +629,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: */ @@ -488,6 +643,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 */ @@ -497,6 +653,7 @@ static CPUCacheInfo legacy_l2_cache_cpuid2 = { .size = 2 * MiB, .line_size = 64, .associativity = 8, + .share_level = CPU_TOPOLOGY_LEVEL_INVALID, }; @@ -510,6 +667,7 @@ static CPUCacheInfo legacy_l2_cache_amd = { .associativity = 16, .sets = 512, .partitions = 1, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }; /* Level 3 unified cache: */ @@ -525,6 +683,7 @@ static CPUCacheInfo legacy_l3_cache = { .self_init = true, .inclusive = true, .complex_indexing = true, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, }; /* TLB definitions: */ @@ -618,11 +777,12 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, 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_ACPI | CPUID_MMX | \ - CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS | CPUID_DE) + CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS | CPUID_DE | \ + CPUID_HT) /* partly implemented: CPUID_MTRR, CPUID_MCA, CPUID_CLFLUSH (needed for Win64) */ /* missing: - CPUID_VME, CPUID_DTS, CPUID_SS, CPUID_HT, CPUID_TM, CPUID_PBE */ + CPUID_VME, CPUID_DTS, CPUID_SS, CPUID_TM, CPUID_PBE */ /* * Kernel-only features that can be shown to usermode programs even if @@ -630,8 +790,8 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, * 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 | \ - CPUID_EXT_X2APIC) +#define CPUID_EXT_KERNEL_FEATURES \ + (CPUID_EXT_PCID | CPUID_EXT_TSC_DEADLINE_TIMER) #else #define CPUID_EXT_KERNEL_FEATURES 0 #endif @@ -641,12 +801,13 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, 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_KERNEL_FEATURES) + 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_LM @@ -689,7 +850,8 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, #define TCG_EXT3_FEATURES (CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | \ CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A | \ - CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_KERNEL_FEATURES) + CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_KERNEL_FEATURES | \ + CPUID_EXT3_CMP_LEG) #define TCG_EXT4_FEATURES 0 @@ -710,15 +872,15 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, #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_RDSEED | \ - CPUID_7_0_EBX_KERNEL_FEATURES) + 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 */ -#if defined CONFIG_SOFTMMU || defined CONFIG_LINUX +#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 @@ -737,7 +899,8 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, #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_FSRC | CPUID_7_1_EAX_CMPCCXADD) +#define TCG_7_1_ECX_FEATURES 0 #define TCG_7_1_EDX_FEATURES 0 #define TCG_7_2_EDX_FEATURES 0 #define TCG_APM_FEATURES 0 @@ -749,6 +912,7 @@ 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 | \ @@ -762,6 +926,17 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, #define TCG_8000_0008_EBX (CPUID_8000_0008_EBX_XSAVEERPTR | \ CPUID_8000_0008_EBX_WBNOINVD | CPUID_8000_0008_EBX_KERNEL_FEATURES) +#if defined CONFIG_USER_ONLY +#define CPUID_8000_0021_EAX_KERNEL_FEATURES CPUID_8000_0021_EAX_AUTO_IBRS +#else +#define CPUID_8000_0021_EAX_KERNEL_FEATURES 0 +#endif + +#define TCG_8000_0021_EAX_FEATURES ( \ + CPUID_8000_0021_EAX_NO_NESTED_DATA_BP | \ + CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE | \ + CPUID_8000_0021_EAX_KERNEL_FEATURES) + FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_1_EDX] = { .type = CPUID_FEATURE_WORD, @@ -777,6 +952,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, @@ -854,7 +1030,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, @@ -903,9 +1079,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", @@ -959,13 +1135,13 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_7_1_EAX] = { .type = CPUID_FEATURE_WORD, .feat_names = { - NULL, NULL, NULL, NULL, + "sha512", "sm3", "sm4", NULL, "avx-vnni", "avx512-bf16", NULL, "cmpccxadd", NULL, NULL, "fzrm", "fsrs", "fsrc", NULL, NULL, NULL, - NULL, NULL, NULL, NULL, + NULL, "fred", "lkgs", "wrmsrns", NULL, "amx-fp16", NULL, "avx-ifma", - NULL, NULL, NULL, NULL, + NULL, NULL, "lam", NULL, NULL, NULL, NULL, NULL, }, .cpuid = { @@ -975,14 +1151,33 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { }, .tcg_features = TCG_7_1_EAX_FEATURES, }, + [FEAT_7_1_ECX] = { + .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL, NULL, NULL, NULL, + NULL, "msr-imm", NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid = { + .eax = 7, + .needs_ecx = true, .ecx = 1, + .reg = R_ECX, + }, + .tcg_features = TCG_7_1_ECX_FEATURES, + }, [FEAT_7_1_EDX] = { .type = CPUID_FEATURE_WORD, .feat_names = { NULL, NULL, NULL, NULL, "avx-vnni-int8", "avx-ne-convert", NULL, NULL, - "amx-complex", NULL, NULL, NULL, + "amx-complex", NULL, "avx-vnni-int16", NULL, NULL, NULL, "prefetchiti", NULL, - NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "avx10", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -997,8 +1192,8 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_7_2_EDX] = { .type = CPUID_FEATURE_WORD, .feat_names = { - NULL, NULL, NULL, NULL, - NULL, "mcdt-no", NULL, NULL, + "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, @@ -1013,6 +1208,20 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { }, .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, .feat_names = { @@ -1029,6 +1238,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 = { @@ -1048,16 +1273,38 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_8000_0021_EAX] = { .type = CPUID_FEATURE_WORD, .feat_names = { - "no-nested-data-bp", NULL, "lfence-always-serializing", NULL, + "no-nested-data-bp", "fs-gs-base-ns", "lfence-always-serializing", NULL, NULL, NULL, "null-sel-clr-base", NULL, "auto-ibrs", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "prefetchi", NULL, NULL, NULL, + "eraps", NULL, NULL, "sbpb", + "ibpb-brtype", "srso-no", "srso-user-kernel-no", NULL, + }, + .cpuid = { .eax = 0x80000021, .reg = R_EAX, }, + .tcg_features = TCG_8000_0021_EAX_FEATURES, + .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 = 0x80000021, .reg = R_EAX, }, + .cpuid = { .eax = 0x80000022, .reg = R_EAX, }, .tcg_features = 0, .unmigratable_flags = 0, }, @@ -1130,7 +1377,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 | @@ -1143,7 +1392,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] = { @@ -1154,9 +1403,17 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "taa-no", NULL, NULL, NULL, NULL, "sbdr-ssdp-no", "fbsdp-no", "psdp-no", NULL, "fb-clear", NULL, NULL, + "bhi-no", NULL, NULL, NULL, + "pbrsb-no", NULL, "gds-no", "rfds-no", + "rfds-clear", NULL, 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, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, "its-no", NULL, }, .msr = { .index = MSR_IA32_ARCH_CAPABILITIES, @@ -1268,7 +1525,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, @@ -1283,7 +1540,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, }, @@ -1340,6 +1597,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, @@ -1439,14 +1698,21 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { }, }; -typedef struct FeatureMask { - FeatureWord index; - uint64_t mask; -} FeatureMask; +bool is_feature_word_cpuid(uint32_t feature, uint32_t index, int reg) +{ + FeatureWordInfo *wi; + FeatureWord w; -typedef struct FeatureDep { - FeatureMask from, to; -} FeatureDep; + for (w = 0; w < FEATURE_WORDS; w++) { + wi = &feature_word_info[w]; + if (wi->type == CPUID_FEATURE_WORD && wi->cpuid.eax == feature && + (!wi->cpuid.needs_ecx || wi->cpuid.ecx == index) && + wi->cpuid.reg == reg) { + return true; + } + } + return false; +} static FeatureDep feature_dependencies[] = { { @@ -1546,8 +1812,48 @@ static FeatureDep feature_dependencies[] = { .to = { FEAT_SVM, ~0ull }, }, { - .from = { FEAT_VMX_SECONDARY_CTLS, VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE }, - .to = { FEAT_7_0_ECX, CPUID_7_0_ECX_WAITPKG }, + .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_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 }, }, }; @@ -1572,9 +1878,6 @@ static const X86RegisterInfo32 x86_reg_info_32[CPU_NB_REGS32] = { }; #undef REGISTER -/* CPUID feature bits available in XSS */ -#define CPUID_XSTATE_XSS_MASK (XSTATE_ARCH_LBR_MASK) - ExtSaveArea x86_ext_save_areas[XSAVE_STATE_AREA_COUNT] = { [XSTATE_FP_BIT] = { /* x87 FP state component is always enabled if XSAVE is supported */ @@ -1668,9 +1971,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; @@ -1684,6 +1988,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; } @@ -1741,8 +2051,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 { @@ -1763,6 +2072,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; @@ -1821,6 +2131,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, @@ -1833,6 +2144,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, @@ -1843,6 +2155,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, @@ -1856,6 +2169,7 @@ static const CPUCaches epyc_cache_info = { .self_init = true, .inclusive = true, .complex_indexing = true, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, }, }; @@ -1871,6 +2185,7 @@ static CPUCaches epyc_v4_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, @@ -1883,6 +2198,7 @@ static CPUCaches epyc_v4_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, @@ -1893,6 +2209,7 @@ static CPUCaches epyc_v4_cache_info = { .partitions = 1, .sets = 1024, .lines_per_tag = 1, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -1906,6 +2223,61 @@ static CPUCaches epyc_v4_cache_info = { .self_init = true, .inclusive = true, .complex_indexing = false, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, +}; + +static CPUCaches epyc_v5_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 = 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 = 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, + .self_init = true, + .inclusive = true, + .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, + .no_invd_sharing = true, + .complex_indexing = false, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, }, }; @@ -1921,6 +2293,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, @@ -1933,6 +2306,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, @@ -1943,6 +2317,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, @@ -1956,6 +2331,7 @@ static const CPUCaches epyc_rome_cache_info = { .self_init = true, .inclusive = true, .complex_indexing = true, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, }, }; @@ -1971,6 +2347,7 @@ static const CPUCaches epyc_rome_v3_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, @@ -1983,6 +2360,7 @@ static const CPUCaches epyc_rome_v3_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, @@ -1993,6 +2371,7 @@ static const CPUCaches epyc_rome_v3_cache_info = { .partitions = 1, .sets = 1024, .lines_per_tag = 1, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2006,10 +2385,11 @@ static const CPUCaches epyc_rome_v3_cache_info = { .self_init = true, .inclusive = true, .complex_indexing = false, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, }, }; -static const CPUCaches epyc_milan_cache_info = { +static const CPUCaches epyc_rome_v5_cache_info = { .l1d_cache = &(CPUCacheInfo) { .type = DATA_CACHE, .level = 1, @@ -2019,8 +2399,8 @@ static const CPUCaches epyc_milan_cache_info = { .partitions = 1, .sets = 64, .lines_per_tag = 1, - .self_init = 1, - .no_invd_sharing = true, + .self_init = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l1i_cache = &(CPUCacheInfo) { .type = INSTRUCTION_CACHE, @@ -2031,8 +2411,8 @@ static const CPUCaches epyc_milan_cache_info = { .partitions = 1, .sets = 64, .lines_per_tag = 1, - .self_init = 1, - .no_invd_sharing = true, + .self_init = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l2_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2043,23 +2423,27 @@ static const CPUCaches epyc_milan_cache_info = { .partitions = 1, .sets = 1024, .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, .level = 3, - .size = 32 * MiB, + .size = 16 * MiB, .line_size = 64, .associativity = 16, .partitions = 1, - .sets = 32768, + .sets = 16384, .lines_per_tag = 1, .self_init = true, - .inclusive = true, - .complex_indexing = true, + .no_invd_sharing = true, + .complex_indexing = false, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, }, }; -static const CPUCaches epyc_milan_v2_cache_info = { +static const CPUCaches epyc_milan_cache_info = { .l1d_cache = &(CPUCacheInfo) { .type = DATA_CACHE, .level = 1, @@ -2071,6 +2455,7 @@ static const CPUCaches epyc_milan_v2_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, @@ -2083,6 +2468,7 @@ static const CPUCaches epyc_milan_v2_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, @@ -2093,6 +2479,7 @@ static const CPUCaches epyc_milan_v2_cache_info = { .partitions = 1, .sets = 1024, .lines_per_tag = 1, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2105,11 +2492,12 @@ static const CPUCaches epyc_milan_v2_cache_info = { .lines_per_tag = 1, .self_init = true, .inclusive = true, - .complex_indexing = false, + .complex_indexing = true, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, }, }; -static const CPUCaches epyc_genoa_cache_info = { +static const CPUCaches epyc_milan_v2_cache_info = { .l1d_cache = &(CPUCacheInfo) { .type = DATA_CACHE, .level = 1, @@ -2121,6 +2509,7 @@ static const CPUCaches epyc_genoa_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, @@ -2133,16 +2522,18 @@ static const CPUCaches epyc_genoa_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, .level = 2, - .size = 1 * MiB, + .size = 512 * KiB, .line_size = 64, .associativity = 8, .partitions = 1, - .sets = 2048, + .sets = 1024, .lines_per_tag = 1, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -2156,27 +2547,244 @@ static const CPUCaches epyc_genoa_cache_info = { .self_init = true, .inclusive = true, .complex_indexing = false, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, }, }; -/* The following VMX features are not supported by KVM and are left out in the - * CPU definitions: - * - * Dual-monitor support (all processors) - * Entry to SMM - * Deactivate dual-monitor treatment - * Number of CR3-target values - * Shutdown activity state - * Wait-for-SIPI activity state - * PAUSE-loop exiting (Westmere and newer) - * EPT-violation #VE (Broadwell and newer) - * Inject event with insn length=0 (Skylake and newer) - * Conceal non-root operation from PT - * Conceal VM exits from PT - * Conceal VM entries from PT - * Enable ENCLS exiting +static const CPUCaches epyc_milan_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 = 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 = 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, + .self_init = true, + .inclusive = true, + .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, + .no_invd_sharing = 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, + }, +}; + +static const CPUCaches epyc_genoa_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 = 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 = 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, + .self_init = true, + .inclusive = true, + .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, + .no_invd_sharing = true, + .complex_indexing = false, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, +}; + +static const CPUCaches epyc_turin_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 48 * KiB, + .line_size = 64, + .associativity = 12, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = 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 = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 1 * MiB, + .line_size = 64, + .associativity = 16, + .partitions = 1, + .sets = 1024, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .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, + .no_invd_sharing = true, + .complex_indexing = false, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, +}; + +/* The following VMX features are not supported by KVM and are left out in the + * CPU definitions: + * + * Dual-monitor support (all processors) + * Entry to SMM + * Deactivate dual-monitor treatment + * Number of CR3-target values + * Shutdown activity state + * Wait-for-SIPI activity state + * PAUSE-loop exiting (Westmere and newer) + * EPT-violation #VE (Broadwell and newer) + * Inject event with insn length=0 (Skylake and newer) + * Conceal non-root operation from PT + * Conceal VM exits from PT + * 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 @@ -3399,6 +4007,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { }, { .version = 4, + .note = "IBRS, EPT switching, no TSX", .props = (PropValue[]) { { "vmx-eptp-switching", "on" }, { /* end of list */ } @@ -3533,7 +4142,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { }, }, { .version = 4, - .note = "ARCH_CAPABILITIES, no TSX", + .note = "ARCH_CAPABILITIES, EPT switching, no TSX", .props = (PropValue[]) { { "vmx-eptp-switching", "on" }, { /* end of list */ } @@ -3819,6 +4428,16 @@ 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 */ } } }, @@ -3957,6 +4576,17 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .version = 3, + .props = (PropValue[]) { + { "ss", "on" }, + { "tsc-adjust", "on" }, + { "cldemote", "on" }, + { "movdiri", "on" }, + { "movdir64b", "on" }, + { /* end of list */ } + } + }, { /* end of list */ } } }, @@ -4093,16 +4723,37 @@ static const X86CPUDefinition builtin_x86_defs[] = { .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 = "Denverton", - .level = 21, + .name = "SierraForest", + .level = 0x23, .vendor = CPUID_VENDOR_INTEL, .family = 6, - .model = 95, - .stepping = 1, + .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 | @@ -4110,19 +4761,295 @@ static const X86CPUDefinition builtin_x86_defs[] = { 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_MONITOR | - CPUID_EXT_SSSE3 | CPUID_EXT_CX16 | CPUID_EXT_SSE41 | + 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_RDRAND, + 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_3DNOWPREFETCH, + 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_SMEP | CPUID_7_0_EBX_ERMS | - CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_SMAP | + 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 }, + { + .version = 2, + .props = (PropValue[]) { + { "ss", "on" }, + { "tsc-adjust", "on" }, + { "cldemote", "on" }, + { "movdiri", "on" }, + { "movdir64b", "on" }, + { "gds-no", "on" }, + { "rfds-no", "on" }, + { "lam", "on" }, + { "intel-psfd", "on"}, + { "ipred-ctrl", "on"}, + { "rrsba-ctrl", "on"}, + { "bhi-ctrl", "on"}, + { "stepping", "3" }, + { /* end of list */ } + } + }, + { /* end of list */ }, + }, + }, + { + .name = "ClearwaterForest", + .level = 0x23, + .xlevel = 0x80000008, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 221, + .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 | CPUID_SS, + .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_TSC_ADJUST | + 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 | + CPUID_7_0_ECX_CLDEMOTE | CPUID_7_0_ECX_MOVDIRI | + CPUID_7_0_ECX_MOVDIR64B, + .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_BHI_NO | MSR_ARCH_CAP_PBRSB_NO | + MSR_ARCH_CAP_GDS_NO | MSR_ARCH_CAP_RFDS_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_SHA512 | CPUID_7_1_EAX_SM3 | CPUID_7_1_EAX_SM4 | + CPUID_7_1_EAX_AVX_VNNI | CPUID_7_1_EAX_CMPCCXADD | + CPUID_7_1_EAX_FSRS | CPUID_7_1_EAX_AVX_IFMA | + CPUID_7_1_EAX_LAM, + .features[FEAT_7_1_EDX] = + CPUID_7_1_EDX_AVX_VNNI_INT8 | CPUID_7_1_EDX_AVX_NE_CONVERT | + CPUID_7_1_EDX_AVX_VNNI_INT16 | CPUID_7_1_EDX_PREFETCHITI, + .features[FEAT_7_2_EDX] = + CPUID_7_2_EDX_PSFD | CPUID_7_2_EDX_IPRED_CTRL | + CPUID_7_2_EDX_RRSBA_CTRL | CPUID_7_2_EDX_DDPD_U | + CPUID_7_2_EDX_BHI_CTRL | 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, + .model_id = "Intel Xeon Processor (ClearwaterForest)", + .versions = (X86CPUVersionDefinition[]) { + { .version = 1 }, + { /* end of list */ }, + }, + }, + { + .name = "Denverton", + .level = 21, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 95, + .stepping = 1, + .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_MONITOR | + CPUID_EXT_SSSE3 | CPUID_EXT_CX16 | 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_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_3DNOWPREFETCH, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_ERMS | + CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT | CPUID_7_0_EBX_SHA_NI, .features[FEAT_7_0_EDX] = CPUID_7_0_EDX_SPEC_CTRL | CPUID_7_0_EDX_ARCH_CAPABILITIES | @@ -4597,6 +5524,25 @@ static const X86CPUDefinition builtin_x86_defs[] = { }, .cache_info = &epyc_v4_cache_info }, + { + .version = 5, + .props = (PropValue[]) { + { "overflow-recov", "on" }, + { "succor", "on" }, + { "lbrv", "on" }, + { "tsc-scale", "on" }, + { "vmcb-clean", "on" }, + { "flushbyasid", "on" }, + { "pause-filter", "on" }, + { "pfthreshold", "on" }, + { "v-vmsave-vmload", "on" }, + { "vgif", "on" }, + { "model-id", + "AMD EPYC-v5 Processor" }, + { /* end of list */ } + }, + .cache_info = &epyc_v5_cache_info + }, { /* end of list */ } } }, @@ -4735,6 +5681,25 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } }, }, + { + .version = 5, + .props = (PropValue[]) { + { "overflow-recov", "on" }, + { "succor", "on" }, + { "lbrv", "on" }, + { "tsc-scale", "on" }, + { "vmcb-clean", "on" }, + { "flushbyasid", "on" }, + { "pause-filter", "on" }, + { "pfthreshold", "on" }, + { "v-vmsave-vmload", "on" }, + { "vgif", "on" }, + { "model-id", + "AMD EPYC-Rome-v5 Processor" }, + { /* end of list */ } + }, + .cache_info = &epyc_rome_v5_cache_info + }, { /* end of list */ } } }, @@ -4810,6 +5775,25 @@ static const X86CPUDefinition builtin_x86_defs[] = { }, .cache_info = &epyc_milan_v2_cache_info }, + { + .version = 3, + .props = (PropValue[]) { + { "overflow-recov", "on" }, + { "succor", "on" }, + { "lbrv", "on" }, + { "tsc-scale", "on" }, + { "vmcb-clean", "on" }, + { "flushbyasid", "on" }, + { "pause-filter", "on" }, + { "pfthreshold", "on" }, + { "v-vmsave-vmload", "on" }, + { "vgif", "on" }, + { "model-id", + "AMD EPYC-Milan-v3 Processor" }, + { /* end of list */ } + }, + .cache_info = &epyc_milan_v3_cache_info + }, { /* end of list */ } } }, @@ -4849,7 +5833,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { 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_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, @@ -4876,14 +5860,258 @@ static const X86CPUDefinition builtin_x86_defs[] = { .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_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, + .versions = (X86CPUVersionDefinition[]) { + { .version = 1 }, + { + .version = 2, + .props = (PropValue[]) { + { "overflow-recov", "on" }, + { "succor", "on" }, + { "lbrv", "on" }, + { "tsc-scale", "on" }, + { "vmcb-clean", "on" }, + { "flushbyasid", "on" }, + { "pause-filter", "on" }, + { "pfthreshold", "on" }, + { "v-vmsave-vmload", "on" }, + { "vgif", "on" }, + { "fs-gs-base-ns", "on" }, + { "perfmon-v2", "on" }, + { "model-id", + "AMD EPYC-Genoa-v2 Processor" }, + { /* end of list */ } + }, + .cache_info = &epyc_genoa_v2_cache_info + }, + { /* end of list */ } + } + }, + { + .name = "YongFeng", + .level = 0x1F, + .vendor = CPUID_VENDOR_ZHAOXIN1, + .family = 7, + .model = 11, + .stepping = 3, + /* missing: CPUID_HT, CPUID_TM, CPUID_PBE */ + .features[FEAT_1_EDX] = + CPUID_SS | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_ACPI | 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, + /* + * missing: CPUID_EXT_OSXSAVE, CPUID_EXT_XTPR, CPUID_EXT_TM2, + * CPUID_EXT_EST, CPUID_EXT_SMX, CPUID_EXT_VMX + */ + .features[FEAT_1_ECX] = + CPUID_EXT_RDRAND | CPUID_EXT_F16C | CPUID_EXT_AVX | + CPUID_EXT_XSAVE | CPUID_EXT_AES | CPUID_EXT_TSC_DEADLINE_TIMER | + CPUID_EXT_POPCNT | CPUID_EXT_MOVBE | CPUID_EXT_X2APIC | + 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_7_0_EBX] = + CPUID_7_0_EBX_SHA_NI | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_ADX | + CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_BMI2 | + CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_BMI1 | + CPUID_7_0_EBX_FSGSBASE, + /* missing: CPUID_7_0_ECX_OSPKE */ + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_RDPID | CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_UMIP, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_ARCH_CAPABILITIES | CPUID_7_0_EDX_SPEC_CTRL, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_PDPE1GB | + CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM, + .features[FEAT_8000_0007_EDX] = CPUID_APM_INVTSC, + /* + * TODO: When the Linux kernel introduces other existing definitions + * for this leaf, remember to update the definitions here. + */ + .features[FEAT_C000_0001_EDX] = + CPUID_C000_0001_EDX_PMM_EN | CPUID_C000_0001_EDX_PMM | + CPUID_C000_0001_EDX_PHE_EN | CPUID_C000_0001_EDX_PHE | + CPUID_C000_0001_EDX_ACE2 | + CPUID_C000_0001_EDX_XCRYPT_EN | CPUID_C000_0001_EDX_XCRYPT | + CPUID_C000_0001_EDX_XSTORE_EN | CPUID_C000_0001_EDX_XSTORE, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT, + .features[FEAT_ARCH_CAPABILITIES] = + MSR_ARCH_CAP_RDCL_NO | MSR_ARCH_CAP_SKIP_L1DFL_VMENTRY | + MSR_ARCH_CAP_MDS_NO | MSR_ARCH_CAP_PSCHANGE_MC_NO | + MSR_ARCH_CAP_SSB_NO, + .features[FEAT_VMX_PROCBASED_CTLS] = + VMX_CPU_BASED_VIRTUAL_INTR_PENDING | VMX_CPU_BASED_HLT_EXITING | + VMX_CPU_BASED_USE_TSC_OFFSETING | 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, + /* + * missing: VMX_SECONDARY_EXEC_PAUSE_LOOP_EXITING, + * VMX_SECONDARY_EXEC_TSC_SCALING + */ + .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_ENABLE_VPID | + VMX_SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE | + 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_ENABLE_PML, + .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_EXIT_CTLS] = + VMX_VM_EXIT_SAVE_DEBUG_CONTROLS | VMX_VM_EXIT_HOST_ADDR_SPACE_SIZE | + 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, + /* missing: VMX_VM_ENTRY_SMM, VMX_VM_ENTRY_DEACT_DUAL_MONITOR */ + .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, + /* + * missing: MSR_VMX_MISC_ACTIVITY_SHUTDOWN, + * MSR_VMX_MISC_ACTIVITY_WAIT_SIPI + */ + .features[FEAT_VMX_MISC] = + MSR_VMX_MISC_STORE_LMA | MSR_VMX_MISC_ACTIVITY_HLT | + MSR_VMX_MISC_VMWRITE_VMEXIT, + /* missing: MSR_VMX_EPT_UC */ + .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_SINGLE_CONTEXT | MSR_VMX_EPT_INVVPID | + MSR_VMX_EPT_INVVPID_ALL_CONTEXT | MSR_VMX_EPT_INVVPID_SINGLE_ADDR | + MSR_VMX_EPT_INVVPID_SINGLE_CONTEXT_NOGLOBALS, + .features[FEAT_VMX_BASIC] = + MSR_VMX_BASIC_INS_OUTS | MSR_VMX_BASIC_TRUE_CTLS, + .features[FEAT_VMX_VMFUNC] = MSR_VMX_VMFUNC_EPT_SWITCHING, + .xlevel = 0x80000008, + .model_id = "Zhaoxin YongFeng Processor", + .versions = (X86CPUVersionDefinition[]) { + { .version = 1 }, + { + .version = 2, + .note = "with the correct model number", + .props = (PropValue[]) { + { "model", "0x5b" }, + { /* end of list */ } + } + }, + { /* end of list */ } + } + }, + { + .name = "EPYC-Turin", + .level = 0xd, + .vendor = CPUID_VENDOR_AMD, + .family = 26, + .model = 0, + .stepping = 0, + .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_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_6_EAX] = + CPUID_6_EAX_ARAT, + .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 | CPUID_7_0_ECX_MOVDIRI | + CPUID_7_0_ECX_MOVDIR64B, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_FSRM | CPUID_7_0_EDX_AVX512_VP2INTERSECT, + .features[FEAT_7_1_EAX] = + CPUID_7_1_EAX_AVX_VNNI | CPUID_7_1_EAX_AVX512_BF16, + .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_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_0007_EBX] = + CPUID_8000_0007_EBX_OVERFLOW_RECOV | CPUID_8000_0007_EBX_SUCCOR, + .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_FS_GS_BASE_NS | + CPUID_8000_0021_EAX_LFENCE_ALWAYS_SERIALIZING | + CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE | + CPUID_8000_0021_EAX_AUTO_IBRS | CPUID_8000_0021_EAX_PREFETCHI | + CPUID_8000_0021_EAX_SBPB | CPUID_8000_0021_EAX_IBPB_BRTYPE | + CPUID_8000_0021_EAX_SRSO_USER_KERNEL_NO, + .features[FEAT_8000_0022_EAX] = + CPUID_8000_0022_EAX_PERFMON_V2, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | + CPUID_XSAVE_XGETBV1 | CPUID_XSAVE_XSAVES, .features[FEAT_SVM] = - CPUID_SVM_NPT | CPUID_SVM_NRIPSAVE | CPUID_SVM_VNMI | - CPUID_SVM_SVME_ADDR_CHK, + CPUID_SVM_NPT | CPUID_SVM_LBRV | CPUID_SVM_NRIPSAVE | + CPUID_SVM_TSCSCALE | CPUID_SVM_VMCBCLEAN | CPUID_SVM_FLUSHASID | + CPUID_SVM_PAUSEFILTER | CPUID_SVM_PFTHRESHOLD | + CPUID_SVM_V_VMSAVE_VMLOAD | CPUID_SVM_VGIF | + CPUID_SVM_VNMI | CPUID_SVM_SVME_ADDR_CHK, .xlevel = 0x80000022, - .model_id = "AMD EPYC-Genoa Processor", - .cache_info = &epyc_genoa_cache_info, + .model_id = "AMD EPYC-Turin Processor", + .cache_info = &epyc_turin_cache_info, }, }; @@ -4928,10 +6156,9 @@ static X86CPUVersion x86_cpu_model_resolve_version(const X86CPUModel *model) return v; } -static Property max_x86_cpu_properties[] = { +static const Property max_x86_cpu_properties[] = { DEFINE_PROP_BOOL("migratable", X86CPU, migratable, true), DEFINE_PROP_BOOL("host-cache-info", X86CPU, cache_info_passthrough, false), - DEFINE_PROP_END_OF_LIST() }; static void max_x86_cpu_realize(DeviceState *dev, Error **errp) @@ -4953,7 +6180,7 @@ static void max_x86_cpu_realize(DeviceState *dev, Error **errp) x86_cpu_realizefn(dev, errp); } -static void max_x86_cpu_class_init(ObjectClass *oc, void *data) +static void max_x86_cpu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); X86CPUClass *xcc = X86_CPU_CLASS(oc); @@ -4995,7 +6222,7 @@ static const TypeInfo max_x86_cpu_type_info = { .class_init = max_x86_cpu_class_init, }; -static char *feature_word_description(FeatureWordInfo *f, uint32_t bit) +static char *feature_word_description(FeatureWordInfo *f) { assert(f->type == CPUID_FEATURE_WORD || f->type == MSR_FEATURE_WORD); @@ -5004,11 +6231,15 @@ static char *feature_word_description(FeatureWordInfo *f, uint32_t bit) { const char *reg = get_register_name_32(f->cpuid.reg); assert(reg); - return g_strdup_printf("CPUID.%02XH:%s", - f->cpuid.eax, reg); + if (!f->cpuid.needs_ecx) { + return g_strdup_printf("CPUID[eax=%02Xh].%s", f->cpuid.eax, reg); + } else { + return g_strdup_printf("CPUID[eax=%02Xh,ecx=%02Xh].%s", + f->cpuid.eax, f->cpuid.ecx, reg); + } } case MSR_FEATURE_WORD: - return g_strdup_printf("MSR(%02XH)", + return g_strdup_printf("MSR(%02Xh)", f->msr.index); } @@ -5028,12 +6259,13 @@ static bool x86_cpu_have_filtered_features(X86CPU *cpu) return false; } -static void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask, - const char *verbose_prefix) +void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask, + const char *verbose_prefix) { CPUX86State *env = &cpu->env; FeatureWordInfo *f = &feature_word_info[w]; int i; + g_autofree char *feat_word_str = feature_word_description(f); if (!cpu->force_features) { env->features[w] &= ~mask; @@ -5046,7 +6278,35 @@ static void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask, for (i = 0; i < 64; ++i) { if ((1ULL << i) & mask) { - g_autofree char *feat_word_str = feature_word_description(f, i); + warn_report("%s: %s%s%s [bit %d]", + verbose_prefix, + feat_word_str, + f->feat_names[i] ? "." : "", + f->feat_names[i] ? f->feat_names[i] : "", i); + } + } +} + +void mark_forced_on_features(X86CPU *cpu, FeatureWord w, uint64_t mask, + const char *verbose_prefix) +{ + CPUX86State *env = &cpu->env; + FeatureWordInfo *f = &feature_word_info[w]; + int i; + + if (!cpu->force_features) { + env->features[w] |= mask; + } + + cpu->forced_on_features[w] |= mask; + + if (!verbose_prefix) { + return; + } + + for (i = 0; i < 64; ++i) { + if ((1ULL << i) & mask) { + g_autofree char *feat_word_str = feature_word_description(f); warn_report("%s: %s%s%s [bit %d]", verbose_prefix, feat_word_str, @@ -5062,13 +6322,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, @@ -5077,16 +6337,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; } @@ -5104,11 +6363,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, @@ -5117,16 +6376,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; } @@ -5140,10 +6398,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, @@ -5152,16 +6410,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; } @@ -5189,7 +6446,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; } @@ -5254,16 +6512,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; } @@ -5340,7 +6597,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) @@ -5442,7 +6699,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, @@ -5493,7 +6750,7 @@ static void listflags(GList *features) } /* Sort alphabetically by type name, respecting X86CPUClass::ordering. */ -static gint x86_cpu_list_compare(gconstpointer a, gconstpointer b) +static gint x86_cpu_list_compare(gconstpointer a, gconstpointer b, gpointer d) { ObjectClass *class_a = (ObjectClass *)a; ObjectClass *class_b = (ObjectClass *)b; @@ -5514,7 +6771,7 @@ static gint x86_cpu_list_compare(gconstpointer a, gconstpointer b) static GSList *get_sorted_cpu_model_list(void) { GSList *list = object_class_get_list(TYPE_X86_CPU, false); - list = g_slist_sort(list, x86_cpu_list_compare); + list = g_slist_sort_with_data(list, x86_cpu_list_compare, NULL); return list; } @@ -5560,7 +6817,7 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data) desc = g_strdup_printf("%s [%s]", model_id, cc->model->note); } if (!desc) { - desc = g_strdup_printf("%s", model_id); + desc = g_strdup(model_id); } if (cc->model && cc->model->cpudef->deprecation_note) { @@ -5568,11 +6825,16 @@ 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); +} + +static gint strcmp_wrap(gconstpointer a, gconstpointer b, gpointer d) +{ + return strcmp(a, b); } /* list available CPU models and flags */ -void x86_cpu_list(void) +static void x86_cpu_list(void) { int i, j; GSList *list; @@ -5593,7 +6855,7 @@ void x86_cpu_list(void) } } - names = g_list_sort(names, (GCompareFunc)strcmp); + names = g_list_sort_with_data(names, strcmp_wrap, NULL); qemu_printf("\nRecognized CPUID flags:\n"); listflags(names); @@ -5679,11 +6941,11 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) #endif /* !CONFIG_USER_ONLY */ -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) { FeatureWordInfo *wi = &feature_word_info[w]; uint64_t r = 0; + uint64_t unavail = 0; if (kvm_enabled()) { switch (wi->type) { @@ -5709,21 +6971,50 @@ 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) { + 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. */ - uint32_t unavail = tcg_enabled() + unavail = tcg_enabled() ? CPUID_EXT2_LM & ~CPUID_EXT2_KERNEL_FEATURES : CPUID_EXT2_LM; - r &= ~unavail; - } + break; +#endif + + 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 - if (migratable_only) { - r &= x86_cpu_get_migratable_flags(w); + break; + + default: + break; + } + + r &= ~unavail; + if (cpu && cpu->migratable) { + r &= x86_cpu_get_migratable_flags(cpu, w); } return r; } @@ -5801,7 +7092,7 @@ void x86_cpu_apply_props(X86CPU *cpu, PropValue *props) * Only for builtin_x86_defs models initialized with x86_register_cpudef_types. */ -static void x86_cpu_apply_version_props(X86CPU *cpu, X86CPUModel *model) +static void x86_cpu_apply_version_props(X86CPU *cpu, const X86CPUModel *model) { const X86CPUVersionDefinition *vdef; X86CPUVersion version = x86_cpu_model_resolve_version(model); @@ -5830,7 +7121,7 @@ static void x86_cpu_apply_version_props(X86CPU *cpu, X86CPUModel *model) } static const CPUCaches *x86_cpu_get_versioned_cache_info(X86CPU *cpu, - X86CPUModel *model) + const X86CPUModel *model) { const X86CPUVersionDefinition *vdef; X86CPUVersion version = x86_cpu_model_resolve_version(model); @@ -5858,7 +7149,7 @@ static const CPUCaches *x86_cpu_get_versioned_cache_info(X86CPU *cpu, * Load data from X86CPUDefinition into a X86CPU object. * Only for builtin_x86_defs models initialized with x86_register_cpudef_types. */ -static void x86_cpu_load_model(X86CPU *cpu, X86CPUModel *model) +static void x86_cpu_load_model(X86CPU *cpu, const X86CPUModel *model) { const X86CPUDefinition *def = model->cpudef; CPUX86State *env = &cpu->env; @@ -5904,6 +7195,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); /* @@ -5914,18 +7208,18 @@ 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 } -static void x86_cpu_cpudef_class_init(ObjectClass *oc, void *data) +static void x86_cpu_cpudef_class_init(ObjectClass *oc, const void *data) { - X86CPUModel *model = data; + const X86CPUModel *model = data; X86CPUClass *xcc = X86_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); @@ -5944,7 +7238,7 @@ static void x86_register_cpu_model_type(const char *name, X86CPUModel *model) .class_data = model, }; - type_register(&ti); + type_register_static(&ti); } @@ -5975,9 +7269,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; @@ -6009,14 +7304,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, { X86CPU *cpu = env_archcpu(env); CPUState *cs = env_cpu(env); - uint32_t die_offset; uint32_t limit; uint32_t signature[3]; - X86CPUTopoInfo topo_info; + X86CPUTopoInfo *topo_info = &env->topo_info; + uint32_t threads_per_pkg; - topo_info.dies_per_pkg = env->nr_dies; - topo_info.cores_per_die = cs->nr_cores; - topo_info.threads_per_core = cs->nr_threads; + threads_per_pkg = x86_threads_per_pkg(topo_info); /* Calculate & apply limits for different index ranges */ if (index >= 0xC0000000) { @@ -6053,12 +7346,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; - *edx |= CPUID_HT; - } - if (!cpu->enable_pmu) { - *ecx &= ~CPUID_EXT_PDCM; + if (threads_per_pkg > 1) { + *ebx |= threads_per_pkg << 16; } break; case 2: @@ -6091,42 +7380,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; } @@ -6154,8 +7449,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, case 7: /* Structured Extended Feature Flags Enumeration Leaf */ if (count == 0) { - uint32_t eax_0_unused, ebx_0, ecx_0, edx_0_unused; - /* Maximum ECX value for sub-leaves */ *eax = env->cpuid_level_func7; *ebx = env->features[FEAT_7_0_EBX]; /* Feature flags */ @@ -6164,28 +7457,11 @@ 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. - */ - x86_cpu_get_supported_cpuid(0x7, 0, - &eax_0_unused, &ebx_0, - &ecx_0, &edx_0_unused); - if ((*ebx & CPUID_7_0_EBX_SGX) && !(ebx_0 & CPUID_7_0_EBX_SGX)) { - *ebx &= ~CPUID_7_0_EBX_SGX; - } - - if ((*ecx & CPUID_7_0_ECX_SGX_LC) - && (!(*ebx & CPUID_7_0_EBX_SGX) || !(ecx_0 & CPUID_7_0_ECX_SGX_LC))) { - *ecx &= ~CPUID_7_0_ECX_SGX_LC; - } } else if (count == 1) { *eax = env->features[FEAT_7_1_EAX]; + *ecx = env->features[FEAT_7_1_ECX]; *edx = env->features[FEAT_7_1_EDX]; *ebx = 0; - *ecx = 0; } else if (count == 2) { *edx = env->features[FEAT_7_2_EDX]; *eax = 0; @@ -6228,19 +7504,19 @@ 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; + *eax = apicid_core_offset(topo_info); + *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; + *eax = apicid_pkg_offset(topo_info); + *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)); @@ -6254,36 +7530,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0x1F: /* V2 Extended Topology Enumeration Leaf */ - if (env->nr_dies < 2) { + if (!x86_has_cpuid_0x1f(cpu)) { *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 */ @@ -6303,7 +7555,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) { @@ -6408,6 +7660,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; @@ -6458,6 +7714,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 @@ -6494,17 +7760,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx = env->features[FEAT_8000_0001_ECX]; *edx = env->features[FEAT_8000_0001_EDX]; - /* The Linux kernel checks for the CMPLegacy bit and - * 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 (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; @@ -6552,7 +7807,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; @@ -6562,17 +7817,18 @@ 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 * thread ID within a package". * 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); + *ecx = (apicid_pkg_offset(topo_info) << 12) | + (threads_per_pkg - 1); } else { *ecx = 0; } @@ -6600,28 +7856,31 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch (count) { case 0: /* L1 dcache info */ encode_cache_cpuid8000001d(env->cache_info_amd.l1d_cache, - &topo_info, eax, ebx, ecx, edx); + topo_info, eax, ebx, ecx, edx); break; case 1: /* L1 icache info */ encode_cache_cpuid8000001d(env->cache_info_amd.l1i_cache, - &topo_info, eax, ebx, ecx, edx); + topo_info, eax, ebx, ecx, edx); break; case 2: /* L2 cache info */ encode_cache_cpuid8000001d(env->cache_info_amd.l2_cache, - &topo_info, eax, ebx, ecx, edx); + topo_info, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ encode_cache_cpuid8000001d(env->cache_info_amd.l3_cache, - &topo_info, eax, ebx, ecx, edx); + topo_info, eax, ebx, ecx, edx); break; default: /* end of info */ *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) { - encode_topo_cpuid8000001e(cpu, &topo_info, eax, ebx, ecx, edx); + encode_topo_cpuid8000001e(cpu, topo_info, eax, ebx, ecx, edx); } else { *eax = 0; *ebx = 0; @@ -6629,6 +7888,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; @@ -6656,13 +7925,15 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (sev_enabled()) { *eax = 0x2; *eax |= sev_es_enabled() ? 0x8 : 0; + *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 = *ecx = *edx = 0; + *ebx = env->features[FEAT_8000_0021_EBX]; break; default: /* reserved values: zero */ @@ -6685,22 +7956,43 @@ static void x86_cpu_set_sgxlepubkeyhash(CPUX86State *env) #endif } -static void x86_cpu_reset_hold(Object *obj) +static bool cpuid_has_xsave_feature(CPUX86State *env, const ExtSaveArea *esa) +{ + 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 *s = CPU(obj); - X86CPU *cpu = X86_CPU(s); - X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); + CPUState *cs = CPU(obj); + X86CPU *cpu = X86_CPU(cs); + X86CPUClass *xcc = X86_CPU_GET_CLASS(obj); CPUX86State *env = &cpu->env; target_ulong cr4; uint64_t xcr0; int i; if (xcc->parent_phases.hold) { - xcc->parent_phases.hold(obj); + 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 */ @@ -6777,8 +8069,8 @@ static void x86_cpu_reset_hold(Object *obj) 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; @@ -6793,7 +8085,7 @@ static void x86_cpu_reset_hold(Object *obj) 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; } } @@ -6829,9 +8121,9 @@ static void x86_cpu_reset_hold(Object *obj) 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); @@ -6923,13 +8215,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); } } @@ -6941,9 +8235,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 @@ -7018,10 +8312,37 @@ 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; + } + } + + if (x86_threads_per_pkg(&env->topo_info) > 1) { + env->features[FEAT_1_EDX] |= CPUID_HT; + + /* + * The Linux kernel checks for the CMPLegacy bit and + * discards multiple thread information if it is set. + * So don't set it here for Intel (and other processors + * following Intel's behavior) to make Linux guests happy. + */ + if (!IS_INTEL_CPU(env) && !IS_ZHAOXIN_CPU(env)) { + env->features[FEAT_8000_0001_ECX] |= CPUID_EXT3_CMP_LEG; + } + } + + if (!cpu->enable_pmu) { + mark_unavailable_features(cpu, FEAT_1_ECX, + env->user_features[FEAT_1_ECX] & CPUID_EXT_PDCM, + "This feature is not available due to PMU being disabled"); + env->features[FEAT_1_ECX] &= ~CPUID_EXT_PDCM; } for (i = 0; i < ARRAY_SIZE(feature_dependencies); i++) { @@ -7052,6 +8373,7 @@ 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_ECX); 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); @@ -7080,11 +8402,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_cpuid_0x1f(cpu) && (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); @@ -7128,13 +8455,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() @@ -7144,20 +8475,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); } - if (env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT) { - uint32_t eax_0, ebx_0, ecx_0, edx_0_unused; - uint32_t eax_1, ebx_1, ecx_1_unused, edx_1_unused; - + /* + * 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()) { x86_cpu_get_supported_cpuid(0x14, 0, - &eax_0, &ebx_0, &ecx_0, &edx_0_unused); + &eax_0, &ebx_0, &ecx_0, &edx_0); x86_cpu_get_supported_cpuid(0x14, 1, - &eax_1, &ebx_1, &ecx_1_unused, &edx_1_unused); + &eax_1, &ebx_1, &ecx_1, &edx_1); if (!eax_0 || ((ebx_0 & INTEL_PT_MINIMAL_EBX) != INTEL_PT_MINIMAL_EBX) || @@ -7177,6 +8510,30 @@ 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) { + if (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) @@ -7208,6 +8565,64 @@ static void x86_cpu_hyperv_realize(X86CPU *cpu) cpu->hyperv_limits[2] = 0; } +#ifndef CONFIG_USER_ONLY +static bool x86_cpu_update_smp_cache_topo(MachineState *ms, X86CPU *cpu, + Error **errp) +{ + CPUX86State *env = &cpu->env; + CpuTopologyLevel level; + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l1d_cache->share_level = level; + env->cache_info_amd.l1d_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D, + env->cache_info_cpuid4.l1d_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D, + env->cache_info_amd.l1d_cache->share_level); + } + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l1i_cache->share_level = level; + env->cache_info_amd.l1i_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I, + env->cache_info_cpuid4.l1i_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I, + env->cache_info_amd.l1i_cache->share_level); + } + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l2_cache->share_level = level; + env->cache_info_amd.l2_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2, + env->cache_info_cpuid4.l2_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2, + env->cache_info_amd.l2_cache->share_level); + } + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l3_cache->share_level = level; + env->cache_info_amd.l3_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3, + env->cache_info_cpuid4.l3_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3, + env->cache_info_amd.l3_cache->share_level); + } + + if (!machine_check_smp_cache(ms, errp)) { + return false; + } + return true; +} +#endif + static void x86_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -7215,12 +8630,11 @@ 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 */ -#ifndef CONFIG_USER_ONLY - cs->tcg_cflags |= CF_PCREL; + tcg_cflags_set(cs, CF_PCREL); #endif if (cpu->apic_id == UNASSIGNED_APIC_ID) { @@ -7260,7 +8674,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) { @@ -7275,14 +8689,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 @@ -7314,6 +8728,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 @@ -7336,6 +8758,21 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) */ cpu->mwait.ecx |= CPUID_MWAIT_EMX | CPUID_MWAIT_IBE; + /* + * Most Intel and certain AMD CPUs support hyperthreading. Even though QEMU + * fixes this issue by adjusting CPUID_0000_0001_EBX and CPUID_8000_0008_ECX + * based on inputs (sockets,cores,threads), it is still better to give + * users a warning. + */ + if (IS_AMD_CPU(env) && + !(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) && + env->topo_info.threads_per_core > 1) { + warn_report_once("This family of AMD CPU doesn't support " + "hyperthreading(%d). Please configure -smp " + "options properly or try enabling topoext " + "feature.", env->topo_info.threads_per_core); + } + /* For 64bit systems think about the number of physical bits to present. * ideally this should be the same as the host; anything other than matching * the host can cause incorrect guest behaviour. @@ -7364,6 +8801,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. @@ -7372,8 +8817,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; @@ -7413,6 +8862,14 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) #ifndef CONFIG_USER_ONLY MachineState *ms = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(ms); + + if (mc->smp_props.has_caches) { + if (!x86_cpu_update_smp_cache_topo(ms, cpu, errp)) { + return; + } + } + qemu_register_reset(x86_cpu_machine_reset_cb, cpu); if (cpu->env.features[FEAT_1_EDX] & CPUID_APIC || ms->smp.cpus > 1) { @@ -7425,28 +8882,9 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) mce_init(cpu); + x86_cpu_gdb_init(cs); qemu_init_vcpu(cs); - /* - * Most Intel and certain AMD CPUs support hyperthreading. Even though QEMU - * fixes this issue by adjusting CPUID_0000_0001_EBX and CPUID_8000_0008_ECX - * based on inputs (sockets,cores,threads), it is still better to give - * users a warning. - * - * NOTE: the following code has to follow qemu_init_vcpu(). Otherwise - * cs->nr_threads hasn't be populated yet and the checking is incorrect. - */ - 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; - } - #ifndef CONFIG_USER_ONLY x86_cpu_apic_realize(cpu, &local_err); if (local_err != NULL) { @@ -7578,7 +9016,46 @@ 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)); + +#ifndef CONFIG_USER_ONLY + if (current_machine && current_machine->cgs) { + x86_confidential_guest_cpu_instance_init( + X86_CONFIDENTIAL_GUEST(current_machine->cgs), (CPU(obj))); + } +#endif +} + +static void x86_cpu_init_default_topo(X86CPU *cpu) +{ + CPUX86State *env = &cpu->env; + + env->topo_info = (X86CPUTopoInfo) {1, 1, 1, 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) @@ -7587,8 +9064,7 @@ static void x86_cpu_initfn(Object *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, @@ -7669,16 +9145,15 @@ static vaddr x86_cpu_get_pc(CPUState *cs) return cpu->env.eip + cpu->env.segs[R_CS].base; } +#if !defined(CONFIG_USER_ONLY) int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request) { X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; -#if !defined(CONFIG_USER_ONLY) if (interrupt_request & CPU_INTERRUPT_POLL) { return CPU_INTERRUPT_POLL; } -#endif if (interrupt_request & CPU_INTERRUPT_SIPI) { return CPU_INTERRUPT_SIPI; } @@ -7699,14 +9174,12 @@ int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request) (env->eflags & IF_MASK && !(env->hflags & HF_INHIBIT_IRQ_MASK))))) { return CPU_INTERRUPT_HARD; -#if !defined(CONFIG_USER_ONLY) } else if (env->hflags2 & HF2_VGIF_MASK) { if((interrupt_request & CPU_INTERRUPT_VIRQ) && (env->eflags & IF_MASK) && !(env->hflags & HF_INHIBIT_IRQ_MASK)) { return CPU_INTERRUPT_VIRQ; } -#endif } } @@ -7717,12 +9190,14 @@ static bool x86_cpu_has_work(CPUState *cs) { return x86_cpu_pending_interrupt(cs, cs->interrupt_request) != 0; } +#endif /* !CONFIG_USER_ONLY */ static void x86_disas_set_info(CPUState *cs, disassemble_info *info) { X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; + info->endian = BFD_ENDIAN_LITTLE; info->mach = (env->hflags & HF_CS64_MASK ? bfd_mach_x86_64 : env->hflags & HF_CS32_MASK ? bfd_mach_i386_i386 : bfd_mach_i386_i8086); @@ -7777,18 +9252,20 @@ void x86_update_hflags(CPUX86State *env) env->hflags = hflags; } -static Property x86_cpu_properties[] = { +static const Property x86_cpu_properties[] = { #ifdef CONFIG_USER_ONLY /* apic_id = 0 by default for *-user, see commit 9886e834 */ 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 @@ -7840,8 +9317,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), @@ -7861,6 +9340,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), @@ -7872,15 +9352,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), @@ -7892,6 +9372,7 @@ static Property x86_cpu_properties[] = { * own cache information (see x86_cpu_load_def()). */ DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, true), + DEFINE_PROP_BOOL("legacy-multi-node", X86CPU, legacy_multi_node, false), DEFINE_PROP_BOOL("xen-vapic", X86CPU, xen_vapic, false), /* @@ -7911,13 +9392,14 @@ static Property x86_cpu_properties[] = { false), DEFINE_PROP_BOOL("x-intel-pt-auto-level", X86CPU, intel_pt_auto_level, true), - DEFINE_PROP_END_OF_LIST() + DEFINE_PROP_BOOL("x-l1-cache-per-thread", X86CPU, l1_cache_per_core, true), }; #ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps i386_sysemu_ops = { + .has_work = x86_cpu_has_work, .get_memory_mapping = x86_cpu_get_memory_mapping, .get_paging_enabled = x86_cpu_get_paging_enabled, .get_phys_page_attrs_debug = x86_cpu_get_phys_page_attrs_debug, @@ -7931,7 +9413,7 @@ static const struct SysemuCPUOps i386_sysemu_ops = { }; #endif -static void x86_cpu_common_class_init(ObjectClass *oc, void *data) +static void x86_cpu_common_class_init(ObjectClass *oc, const void *data) { X86CPUClass *xcc = X86_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); @@ -7950,8 +9432,8 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) cc->reset_dump_flags = CPU_DUMP_FPU | CPU_DUMP_CCOP; cc->class_by_name = x86_cpu_class_by_name; + cc->list_cpus = x86_cpu_list; cc->parse_features = x86_cpu_parse_featurestr; - cc->has_work = x86_cpu_has_work; cc->dump_state = x86_cpu_dump_state; cc->set_pc = x86_cpu_set_pc; cc->get_pc = x86_cpu_get_pc; @@ -7962,14 +9444,15 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) #ifndef CONFIG_USER_ONLY cc->sysemu_ops = &i386_sysemu_ops; #endif /* !CONFIG_USER_ONLY */ +#ifdef CONFIG_TCG + cc->tcg_ops = &x86_tcg_ops; +#endif /* CONFIG_TCG */ 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; @@ -8020,6 +9503,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, @@ -8029,7 +9513,7 @@ static const TypeInfo x86_cpu_type_info = { }; /* "base" CPU model, used by query-cpu-model-expansion */ -static void x86_cpu_base_class_init(ObjectClass *oc, void *data) +static void x86_cpu_base_class_init(ObjectClass *oc, const void *data) { X86CPUClass *xcc = X86_CPU_CLASS(oc); diff --git a/target/i386/cpu.h b/target/i386/cpu.h index fbb05eace57..51e10139dfd 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -20,25 +20,21 @@ #ifndef I386_CPU_H #define I386_CPU_H -#include "sysemu/tcg.h" +#include "system/tcg.h" #include "cpu-qom.h" #include "kvm/hyperv-proto.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.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" +#include "standard-headers/asm-x86/kvm_para.h" #define XEN_NR_VIRQS 24 -/* The x86 has a strong memory model with some store-after-load re-ordering */ -#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) - -#define KVM_HAVE_MCE_INJECTION 1 - -/* support for self modifying code even if the modified instruction is - close to the modifying instruction */ -#define TARGET_HAS_PRECISE_SMC - #ifdef TARGET_X86_64 #define I386_ELF_MACHINE EM_X86_64 #define ELF_MACHINE_UNAME "x86_64" @@ -261,6 +257,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 \ @@ -269,7 +272,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) @@ -339,6 +343,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 */ @@ -365,6 +370,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 +386,10 @@ typedef enum X86Seg { #define MSR_IA32_APICBASE_ENABLE (1<<11) #define MSR_IA32_APICBASE_EXTD (1 << 10) #define MSR_IA32_APICBASE_BASE (0xfffffU<<12) +#define MSR_IA32_APICBASE_RESERVED \ + (~(uint64_t)(MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE \ + | MSR_IA32_APICBASE_EXTD | MSR_IA32_APICBASE_BASE)) + #define MSR_IA32_FEATURE_CONTROL 0x0000003a #define MSR_TSC_ADJUST 0x0000003b #define MSR_IA32_SPEC_CTRL 0x48 @@ -396,6 +407,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 +532,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 +573,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 @@ -553,6 +584,7 @@ typedef enum X86Seg { #define XSTATE_OPMASK_BIT 5 #define XSTATE_ZMM_Hi256_BIT 6 #define XSTATE_Hi16_ZMM_BIT 7 +#define XSTATE_PT_BIT 8 #define XSTATE_PKRU_BIT 9 #define XSTATE_ARCH_LBR_BIT 15 #define XSTATE_XTILE_CFG_BIT 17 @@ -566,6 +598,7 @@ typedef enum X86Seg { #define XSTATE_OPMASK_MASK (1ULL << XSTATE_OPMASK_BIT) #define XSTATE_ZMM_Hi256_MASK (1ULL << XSTATE_ZMM_Hi256_BIT) #define XSTATE_Hi16_ZMM_MASK (1ULL << XSTATE_Hi16_ZMM_BIT) +#define XSTATE_PT_MASK (1ULL << XSTATE_PT_BIT) #define XSTATE_PKRU_MASK (1ULL << XSTATE_PKRU_BIT) #define XSTATE_ARCH_LBR_MASK (1ULL << XSTATE_ARCH_LBR_BIT) #define XSTATE_XTILE_CFG_MASK (1ULL << XSTATE_XTILE_CFG_BIT) @@ -588,6 +621,11 @@ typedef enum X86Seg { XSTATE_Hi16_ZMM_MASK | XSTATE_PKRU_MASK | \ XSTATE_XTILE_CFG_MASK | XSTATE_XTILE_DATA_MASK) +/* CPUID feature bits available in XSS */ +#define CPUID_XSTATE_XSS_MASK (XSTATE_ARCH_LBR_MASK) + +#define CPUID_XSTATE_MASK (CPUID_XSTATE_XCR0_MASK | CPUID_XSTATE_XSS_MASK) + /* CPUID feature words */ typedef enum FeatureWord { FEAT_1_EDX, /* CPUID[1].EDX */ @@ -598,9 +636,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 */ @@ -627,14 +668,24 @@ 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_ECX, /* CPUID[EAX=7,ECX=1].ECX */ 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 struct FeatureMask { + FeatureWord index; + uint64_t mask; +} FeatureMask; + +typedef struct FeatureDep { + FeatureMask from, to; +} FeatureDep; + 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) @@ -728,7 +779,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 | \ @@ -780,6 +831,8 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, /* 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 */ @@ -788,6 +841,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 */ @@ -798,6 +853,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 */ @@ -812,8 +869,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 */ @@ -861,6 +916,8 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_7_0_ECX_LA57 (1U << 16) /* Read Processor ID */ #define CPUID_7_0_ECX_RDPID (1U << 22) +/* KeyLocker */ +#define CPUID_7_0_ECX_KeyLocker (1U << 23) /* Bus Lock Debug Exception */ #define CPUID_7_0_ECX_BUS_LOCK_DETECT (1U << 24) /* Cache Line Demote Instruction */ @@ -882,6 +939,8 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_7_0_EDX_FSRM (1U << 4) /* AVX512 Vector Pair Intersection to a Pair of Mask Registers */ #define CPUID_7_0_EDX_AVX512_VP2INTERSECT (1U << 8) + /* "md_clear" VERW clears CPU buffers */ +#define CPUID_7_0_EDX_MD_CLEAR (1U << 10) /* SERIALIZE instruction */ #define CPUID_7_0_EDX_SERIALIZE (1U << 14) /* TSX Suspend Load Address Tracking instruction */ @@ -909,10 +968,18 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, /* Speculative Store Bypass Disable */ #define CPUID_7_0_EDX_SPEC_CTRL_SSBD (1U << 31) +/* SHA512 Instruction */ +#define CPUID_7_1_EAX_SHA512 (1U << 0) +/* SM3 Instruction */ +#define CPUID_7_1_EAX_SM3 (1U << 1) +/* SM4 Instruction */ +#define CPUID_7_1_EAX_SM4 (1U << 2) /* AVX VNNI Instruction */ #define CPUID_7_1_EAX_AVX_VNNI (1U << 4) /* AVX512 BFloat16 Instruction */ #define CPUID_7_1_EAX_AVX512_BF16 (1U << 5) +/* Linear address space separation */ +#define CPUID_7_1_EAX_LASS (1U << 6) /* CMPCCXADD Instructions */ #define CPUID_7_1_EAX_CMPCCXADD (1U << 7) /* Fast Zero REP MOVS */ @@ -921,10 +988,21 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_7_1_EAX_FSRS (1U << 11) /* Fast Short REP CMPS/SCAS */ #define CPUID_7_1_EAX_FSRC (1U << 12) +/* 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) /* 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) + +/* The immediate form of MSR access instructions */ +#define CPUID_7_1_ECX_MSR_IMM (1U << 5) /* Support for VPDPB[SU,UU,SS]D[,S] */ #define CPUID_7_1_EDX_AVX_VNNI_INT8 (1U << 4) @@ -932,8 +1010,23 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_7_1_EDX_AVX_NE_CONVERT (1U << 5) /* AMX COMPLEX Instructions */ #define CPUID_7_1_EDX_AMX_COMPLEX (1U << 8) +/* AVX-VNNI-INT16 Instructions */ +#define CPUID_7_1_EDX_AVX_VNNI_INT16 (1U << 10) /* 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) + +/* Indicate bit 7 of the IA32_SPEC_CTRL MSR is supported */ +#define CPUID_7_2_EDX_PSFD (1U << 0) +/* Indicate bits 3 and 4 of the IA32_SPEC_CTRL MSR are supported */ +#define CPUID_7_2_EDX_IPRED_CTRL (1U << 1) +/* Indicate bits 5 and 6 of the IA32_SPEC_CTRL MSR are supported */ +#define CPUID_7_2_EDX_RRSBA_CTRL (1U << 2) +/* Indicate bit 8 of the IA32_SPEC_CTRL MSR is supported */ +#define CPUID_7_2_EDX_DDPD_U (1U << 3) +/* Indicate bit 10 of the IA32_SPEC_CTRL MSR is supported */ +#define CPUID_7_2_EDX_BHI_CTRL (1U << 4) /* Do not exhibit MXCSR Configuration Dependent Timing (MCDT) behavior */ #define CPUID_7_2_EDX_MCDT_NO (1U << 5) @@ -944,6 +1037,43 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, /* 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) + +/* (Old) KVM paravirtualized clocksource */ +#define CPUID_KVM_CLOCK (1U << KVM_FEATURE_CLOCKSOURCE) +/* (New) KVM specific paravirtualized clocksource */ +#define CPUID_KVM_CLOCK2 (1U << KVM_FEATURE_CLOCKSOURCE2) +/* KVM asynchronous page fault */ +#define CPUID_KVM_ASYNCPF (1U << KVM_FEATURE_ASYNC_PF) +/* KVM stolen (when guest vCPU is not running) time accounting */ +#define CPUID_KVM_STEAL_TIME (1U << KVM_FEATURE_STEAL_TIME) +/* KVM paravirtualized end-of-interrupt signaling */ +#define CPUID_KVM_PV_EOI (1U << KVM_FEATURE_PV_EOI) +/* KVM paravirtualized spinlocks support */ +#define CPUID_KVM_PV_UNHALT (1U << KVM_FEATURE_PV_UNHALT) +/* KVM host-side polling on HLT control from the guest */ +#define CPUID_KVM_POLL_CONTROL (1U << KVM_FEATURE_POLL_CONTROL) +/* KVM interrupt based asynchronous page fault*/ +#define CPUID_KVM_ASYNCPF_INT (1U << KVM_FEATURE_ASYNC_PF_INT) +/* KVM 'Extended Destination ID' support for external interrupts */ +#define CPUID_KVM_MSI_EXT_DEST_ID (1U << KVM_FEATURE_MSI_EXT_DEST_ID) + +/* Hint to KVM that vCPUs expect never preempted for an unlimited time */ +#define CPUID_KVM_HINTS_REALTIME (1U << KVM_HINTS_REALTIME) + /* CLZERO instruction */ #define CPUID_8000_0008_EBX_CLZERO (1U << 0) /* Always save/restore FP error pointers */ @@ -960,28 +1090,75 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #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) +#define CPUID_8000_0021_EAX_NO_NESTED_DATA_BP (1U << 0) +/* WRMSR to FS_BASE, GS_BASE, or KERNEL_GS_BASE is non-serializing */ +#define CPUID_8000_0021_EAX_FS_GS_BASE_NS (1U << 1) /* 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) +#define CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE (1U << 6) /* Automatic IBRS */ -#define CPUID_8000_0021_EAX_AUTO_IBRS (1U << 8) +#define CPUID_8000_0021_EAX_AUTO_IBRS (1U << 8) +/* Indicates support for IC prefetch */ +#define CPUID_8000_0021_EAX_PREFETCHI (1U << 20) +/* 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) #define CPUID_XSAVE_XGETBV1 (1U << 2) #define CPUID_XSAVE_XSAVES (1U << 3) +#define CPUID_XSAVE_XFD (1U << 4) #define CPUID_6_EAX_ARAT (1U << 2) /* CPUID[0x80000007].EDX flags: */ #define CPUID_APM_INVTSC (1U << 8) +/* "rng" RNG present (xstore) */ +#define CPUID_C000_0001_EDX_XSTORE (1U << 2) +/* "rng_en" RNG enabled */ +#define CPUID_C000_0001_EDX_XSTORE_EN (1U << 3) +/* "ace" on-CPU crypto (xcrypt) */ +#define CPUID_C000_0001_EDX_XCRYPT (1U << 6) +/* "ace_en" on-CPU crypto enabled */ +#define CPUID_C000_0001_EDX_XCRYPT_EN (1U << 7) +/* Advanced Cryptography Engine v2 */ +#define CPUID_C000_0001_EDX_ACE2 (1U << 8) +/* ACE v2 enabled */ +#define CPUID_C000_0001_EDX_ACE2_EN (1U << 9) +/* PadLock Hash Engine */ +#define CPUID_C000_0001_EDX_PHE (1U << 10) +/* PHE enabled */ +#define CPUID_C000_0001_EDX_PHE_EN (1U << 11) +/* PadLock Montgomery Multiplier */ +#define CPUID_C000_0001_EDX_PMM (1U << 12) +/* PMM enabled */ +#define CPUID_C000_0001_EDX_PMM_EN (1U << 13) + #define CPUID_VENDOR_SZ 12 #define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */ @@ -994,7 +1171,16 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_VENDOR_AMD_3 0x444d4163 /* "cAMD" */ #define CPUID_VENDOR_AMD "AuthenticAMD" -#define CPUID_VENDOR_VIA "CentaurHauls" +#define CPUID_VENDOR_ZHAOXIN1_1 0x746E6543 /* "Cent" */ +#define CPUID_VENDOR_ZHAOXIN1_2 0x48727561 /* "aurH" */ +#define CPUID_VENDOR_ZHAOXIN1_3 0x736C7561 /* "auls" */ + +#define CPUID_VENDOR_ZHAOXIN2_1 0x68532020 /* " Sh" */ +#define CPUID_VENDOR_ZHAOXIN2_2 0x68676E61 /* "angh" */ +#define CPUID_VENDOR_ZHAOXIN2_3 0x20206961 /* "ai " */ + +#define CPUID_VENDOR_ZHAOXIN1 "CentaurHauls" +#define CPUID_VENDOR_ZHAOXIN2 " Shanghai " #define CPUID_VENDOR_HYGON "HygonGenuine" @@ -1004,15 +1190,30 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define IS_AMD_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_AMD_1 && \ (env)->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && \ (env)->cpuid_vendor3 == CPUID_VENDOR_AMD_3) +#define IS_ZHAOXIN1_CPU(env) \ + ((env)->cpuid_vendor1 == CPUID_VENDOR_ZHAOXIN1_1 && \ + (env)->cpuid_vendor2 == CPUID_VENDOR_ZHAOXIN1_2 && \ + (env)->cpuid_vendor3 == CPUID_VENDOR_ZHAOXIN1_3) +#define IS_ZHAOXIN2_CPU(env) \ + ((env)->cpuid_vendor1 == CPUID_VENDOR_ZHAOXIN2_1 && \ + (env)->cpuid_vendor2 == CPUID_VENDOR_ZHAOXIN2_2 && \ + (env)->cpuid_vendor3 == CPUID_VENDOR_ZHAOXIN2_3) +#define IS_ZHAOXIN_CPU(env) (IS_ZHAOXIN1_CPU(env) || IS_ZHAOXIN2_CPU(env)) #define CPUID_MWAIT_IBE (1U << 1) /* Interrupts can exit capability */ #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) @@ -1028,7 +1229,10 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #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_BHI_NO (1U << 20) #define MSR_ARCH_CAP_PBRSB_NO (1U << 24) +#define MSR_ARCH_CAP_GDS_NO (1U << 26) +#define MSR_ARCH_CAP_RFDS_NO (1U << 27) #define MSR_CORE_CAP_SPLIT_LOCK_DETECT (1U << 5) @@ -1039,6 +1243,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) @@ -1134,6 +1340,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 @@ -1209,6 +1416,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 @@ -1217,10 +1426,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_MULB, /* modify all flags, C, O = (CC_SRC != 0) */ + 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. */ + + /* 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, @@ -1275,16 +1488,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; @@ -1401,8 +1638,6 @@ typedef struct { #define MAX_FIXED_COUNTERS 3 #define MAX_GP_COUNTERS (MSR_IA32_PERF_STATUS - MSR_P6_EVNTSEL0) -#define TARGET_INSN_START_EXTRA_WORDS 1 - #define NB_OPMASK_REGS 8 /* CPU can't have 0xFFFFFFFF APIC ID, use that value to distinguish @@ -1410,23 +1645,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; @@ -1527,12 +1773,6 @@ typedef enum TPRAccess { /* Cache information data structures: */ -enum CacheType { - DATA_CACHE, - INSTRUCTION_CACHE, - UNIFIED_CACHE -}; - typedef struct CPUCacheInfo { enum CacheType type; uint8_t level; @@ -1574,6 +1814,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; @@ -1584,11 +1831,6 @@ typedef struct CPUCaches { CPUCacheInfo *l3_cache; } CPUCaches; -typedef struct HVFX86LazyFlags { - target_ulong result; - target_ulong auxbits; -} HVFX86LazyFlags; - typedef struct CPUArchState { /* standard registers */ target_ulong regs[CPU_NB_REGS]; @@ -1671,6 +1913,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; @@ -1756,6 +2009,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; @@ -1786,6 +2042,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; @@ -1807,6 +2067,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]; @@ -1861,8 +2123,7 @@ typedef struct CPUArchState { QemuMutex xen_timers_lock; #endif #if defined(CONFIG_HVF) - HVFX86LazyFlags hvf_lflags; - void *hvf_mmio_buf; + void *emu_mmio_buf; #endif uint64_t mcg_cap; @@ -1881,7 +2142,10 @@ typedef struct CPUArchState { TPRAccess tpr_access_type; - unsigned nr_dies; + X86CPUTopoInfo topo_info; + + /* Bitmap of available CPU topology levels for this CPU. */ + DECLARE_BITMAP(avail_cpu_topo, CPU_TOPOLOGY_LEVEL__MAX); } CPUX86State; struct kvm_msrs; @@ -1896,11 +2160,8 @@ struct kvm_msrs; * An x86 CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUX86State env; VMChangeStateEntry *vmsentry; @@ -1957,6 +2218,9 @@ struct ArchCPU { /* Features that were filtered out because of missing host capabilities */ FeatureWordArray filtered_features; + /* Features that are forced enabled by underlying hypervisor, e.g., TDX */ + FeatureWordArray forced_on_features; + /* Enable PMU CPUID bits. This can't be enabled by default yet because * it doesn't have ABI stability guarantees, as it passes all PMU CPUID * bits returned by GET_SUPPORTED_CPUID (that depend on host CPU and kernel @@ -1985,20 +2249,37 @@ 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; + /* Force to enable cpuid 0x1f */ + bool force_cpuid_0x1f; + /* Enable auto level-increase for all CPUID leaves */ bool full_cpuid_auto_level; /* 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; @@ -2011,15 +2292,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; @@ -2031,6 +2317,7 @@ 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; @@ -2039,13 +2326,49 @@ struct ArchCPU { 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. + */ + const 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; #endif -int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request); - int x86_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, int cpuid, DumpState *s); int x86_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cpu, @@ -2055,23 +2378,25 @@ 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); 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 +int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request); + 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); @@ -2163,6 +2488,8 @@ static inline void cpu_x86_load_seg_cache_sipi(X86CPU *cpu, cs->halted = 0; } +uint64_t cpu_x86_get_msr_core_thread_count(X86CPU *cpu); + int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector, target_ulong *base, unsigned int *limit, unsigned int *flags); @@ -2171,15 +2498,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, @@ -2198,8 +2527,21 @@ 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); +bool is_feature_word_cpuid(uint32_t feature, uint32_t index, int reg); +void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask, + const char *verbose_prefix); +void mark_forced_on_features(X86CPU *cpu, FeatureWord w, uint64_t mask, + const char *verbose_prefix); + +static inline bool x86_has_cpuid_0x1f(X86CPU *cpu) +{ + return cpu->force_cpuid_0x1f || + x86_has_extended_topo(cpu->env.avail_cpu_topo); +} /* helper.c */ void x86_cpu_set_a20(X86CPU *cpu, int a20_state); @@ -2241,8 +2583,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 @@ -2251,27 +2591,38 @@ uint64_t cpu_get_tsc(CPUX86State *env); #define TARGET_DEFAULT_CPU_TYPE X86_CPU_TYPE_NAME("qemu32") #endif -#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 + +#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 int cpu_mmu_index(CPUX86State *env, bool ifetch) +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; } #define CC_DST (env->cc_dst) @@ -2279,22 +2630,12 @@ static inline int cpu_mmu_index_kernel(CPUX86State *env) #define CC_SRC2 (env->cc_src2) #define CC_OP (env->cc_op) -#include "exec/cpu-all.h" #include "svm.h" #if !defined(CONFIG_USER_ONLY) #include "hw/i386/apic.h" #endif -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)); -} - void do_cpu_init(X86CPU *cpu); #define MCE_INJECT_BROADCAST 1 @@ -2304,13 +2645,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; } @@ -2365,6 +2706,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); @@ -2479,6 +2823,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; } @@ -2503,4 +2853,29 @@ static inline bool ctl_has_irq(CPUX86State *env) # define TARGET_VSYSCALL_PAGE (UINT64_C(-10) << 20) #endif +/* majority(NOT a, b, c) = (a ^ b) ? b : c */ +#define MAJ_INV1(a, b, c) ((((a) ^ (b)) & ((b) ^ (c))) ^ (c)) + +/* + * ADD_COUT_VEC(x, y) = majority((x + y) ^ x ^ y, x, y) + * + * If two corresponding bits in x and y are the same, that's the carry + * independent of the value (x+y)^x^y. Hence x^y can be replaced with + * 1 in (x+y)^x^y, resulting in majority(NOT (x+y), x, y) + */ +#define ADD_COUT_VEC(op1, op2, result) \ + MAJ_INV1(result, op1, op2) + +/* + * SUB_COUT_VEC(x, y) = NOT majority(x, NOT y, (x - y) ^ x ^ NOT y) + * = majority(NOT x, y, (x - y) ^ x ^ y) + * + * Note that the carry out is actually a borrow, i.e. it is inverted. + * If two corresponding bits in x and y are different, the value of the + * bit in (x-y)^x^y likewise does not matter. Hence, x^y can be replaced + * with 0 in (x-y)^x^y, resulting in majority(NOT x, y, x-y) + */ +#define SUB_COUT_VEC(op1, op2, result) \ + MAJ_INV1(op1, op2, result) + #endif /* I386_CPU_H */ diff --git a/target/i386/emulate/meson.build b/target/i386/emulate/meson.build new file mode 100644 index 00000000000..4edd4f462fc --- /dev/null +++ b/target/i386/emulate/meson.build @@ -0,0 +1,5 @@ +i386_system_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: files( + 'x86_decode.c', + 'x86_emu.c', + 'x86_flags.c', +)) diff --git a/target/i386/emulate/panic.h b/target/i386/emulate/panic.h new file mode 100644 index 00000000000..71c24874ba0 --- /dev/null +++ b/target/i386/emulate/panic.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 Veertu Inc, + * Copyright (C) 2017 Google Inc, + * + * This program 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 program is distributed in the hope that 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 program; if not, see . + */ +#ifndef X86_EMU_PANIC_H +#define X86_EMU_PANIC_H + +#define VM_PANIC(x) {\ + printf("%s\n", x); \ + abort(); \ +} + +#define VM_PANIC_ON(x) {\ + if (x) { \ + printf("%s\n", #x); \ + abort(); \ + } \ +} + +#define VM_PANIC_EX(...) {\ + printf(__VA_ARGS__); \ + abort(); \ +} + +#define VM_PANIC_ON_EX(x, ...) {\ + if (x) { \ + printf(__VA_ARGS__); \ + abort(); \ + } \ +} + +#endif diff --git a/target/i386/hvf/x86.h b/target/i386/emulate/x86.h similarity index 88% rename from target/i386/hvf/x86.h rename to target/i386/emulate/x86.h index 947b98da414..73edccfba00 100644 --- a/target/i386/hvf/x86.h +++ b/target/i386/emulate/x86.h @@ -16,8 +16,8 @@ * License along with this program; if not, see . */ -#ifndef HVF_X86_H -#define HVF_X86_H +#ifndef X86_EMU_DEFS_H +#define X86_EMU_DEFS_H typedef struct x86_register { union { @@ -183,7 +183,7 @@ static inline uint32_t x86_call_gate_offset(x86_call_gate *gate) #define GDT_SEL 0 #define LDT_SEL 1 -typedef struct x68_segment_selector { +typedef struct x86_segment_selector { union { uint16_t sel; struct { @@ -192,7 +192,7 @@ typedef struct x68_segment_selector { uint16_t index:13; }; }; -} __attribute__ ((__packed__)) x68_segment_selector; +} __attribute__ ((__packed__)) x86_segment_selector; /* useful register access macros */ #define x86_reg(cpu, reg) ((x86_register *) &cpu->regs[reg]) @@ -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, + x86_segment_selector sel); +bool x86_write_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, - x68_segment_selector sel); + x86_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/emulate/x86_decode.c b/target/i386/emulate/x86_decode.c new file mode 100644 index 00000000000..2eca39802e3 --- /dev/null +++ b/target/i386/emulate/x86_decode.c @@ -0,0 +1,2173 @@ +/* + * Copyright (C) 2016 Veertu Inc, + * Copyright (C) 2017 Google Inc, + * + * This program 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 program is distributed in the hope that 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 program; if not, see . + */ + +#include "qemu/osdep.h" + +#include "panic.h" +#include "x86_decode.h" +#include "x86_emu.h" + +#define OPCODE_ESCAPE 0xf + +static void decode_invalid(CPUX86State *env, struct x86_decode *decode) +{ + printf(TARGET_FMT_lx ": failed to decode instruction ", env->eip); + for (int i = 0; i < decode->opcode_len; i++) { + printf("%x ", decode->opcode[i]); + } + printf("\n"); + VM_PANIC("decoder failed\n"); +} + +uint64_t sign(uint64_t val, int size) +{ + switch (size) { + case 1: + val = (int8_t)val; + break; + case 2: + val = (int16_t)val; + break; + case 4: + val = (int32_t)val; + break; + case 8: + val = (int64_t)val; + break; + default: + VM_PANIC_EX("%s invalid size %d\n", __func__, size); + break; + } + return val; +} + +static inline uint64_t decode_bytes(CPUX86State *env, struct x86_decode *decode, + int size) +{ + uint64_t val = 0; + + switch (size) { + case 1: + case 2: + case 4: + case 8: + break; + default: + VM_PANIC_EX("%s invalid size %d\n", __func__, size); + break; + } + target_ulong va = linear_rip(env_cpu(env), env->eip) + decode->len; + emul_ops->read_mem(env_cpu(env), &val, va, size); + decode->len += size; + + return val; +} + +static inline uint8_t decode_byte(CPUX86State *env, struct x86_decode *decode) +{ + return (uint8_t)decode_bytes(env, decode, 1); +} + +static inline uint16_t decode_word(CPUX86State *env, struct x86_decode *decode) +{ + return (uint16_t)decode_bytes(env, decode, 2); +} + +static inline uint32_t decode_dword(CPUX86State *env, struct x86_decode *decode) +{ + return (uint32_t)decode_bytes(env, decode, 4); +} + +static inline uint64_t decode_qword(CPUX86State *env, struct x86_decode *decode) +{ + return decode_bytes(env, decode, 8); +} + +static void decode_modrm_rm(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + op->type = X86_VAR_RM; +} + +static void decode_modrm_reg(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + op->type = X86_VAR_REG; + op->reg = decode->modrm.reg; + op->regptr = get_reg_ref(env, op->reg, decode->rex.rex, decode->rex.r, + decode->operand_size); +} + +static void decode_rax(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + op->type = X86_VAR_REG; + op->reg = R_EAX; + /* Since reg is always AX, REX prefix has no impact. */ + op->regptr = get_reg_ref(env, op->reg, false, 0, + decode->operand_size); +} + +static inline void decode_immediate(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *var, int size) +{ + var->type = X86_VAR_IMMEDIATE; + var->size = size; + switch (size) { + case 1: + var->val = decode_byte(env, decode); + break; + case 2: + var->val = decode_word(env, decode); + break; + case 4: + var->val = decode_dword(env, decode); + break; + case 8: + var->val = decode_qword(env, decode); + break; + default: + VM_PANIC_EX("bad size %d\n", size); + } +} + +static void decode_imm8(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + decode_immediate(env, decode, op, 1); + op->type = X86_VAR_IMMEDIATE; +} + +static void decode_imm8_signed(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + decode_immediate(env, decode, op, 1); + op->val = sign(op->val, 1); + op->type = X86_VAR_IMMEDIATE; +} + +static void decode_imm16(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + decode_immediate(env, decode, op, 2); + op->type = X86_VAR_IMMEDIATE; +} + + +static void decode_imm(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + if (8 == decode->operand_size) { + decode_immediate(env, decode, op, 4); + op->val = sign(op->val, decode->operand_size); + } else { + decode_immediate(env, decode, op, decode->operand_size); + } + op->type = X86_VAR_IMMEDIATE; +} + +static void decode_imm_signed(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + decode_immediate(env, decode, op, decode->operand_size); + op->val = sign(op->val, decode->operand_size); + op->type = X86_VAR_IMMEDIATE; +} + +static void decode_imm_1(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + op->type = X86_VAR_IMMEDIATE; + op->val = 1; +} + +static void decode_imm_0(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + op->type = X86_VAR_IMMEDIATE; + op->val = 0; +} + + +static void decode_pushseg(CPUX86State *env, struct x86_decode *decode) +{ + uint8_t op = (decode->opcode_len > 1) ? decode->opcode[1] : decode->opcode[0]; + + decode->op[0].type = X86_VAR_REG; + switch (op) { + case 0xe: + decode->op[0].reg = R_CS; + break; + case 0x16: + decode->op[0].reg = R_SS; + break; + case 0x1e: + decode->op[0].reg = R_DS; + break; + case 0x06: + decode->op[0].reg = R_ES; + break; + case 0xa0: + decode->op[0].reg = R_FS; + break; + case 0xa8: + decode->op[0].reg = R_GS; + break; + } +} + +static void decode_popseg(CPUX86State *env, struct x86_decode *decode) +{ + uint8_t op = (decode->opcode_len > 1) ? decode->opcode[1] : decode->opcode[0]; + + decode->op[0].type = X86_VAR_REG; + switch (op) { + case 0xf: + decode->op[0].reg = R_CS; + break; + case 0x17: + decode->op[0].reg = R_SS; + break; + case 0x1f: + decode->op[0].reg = R_DS; + break; + case 0x07: + decode->op[0].reg = R_ES; + break; + case 0xa1: + decode->op[0].reg = R_FS; + break; + case 0xa9: + decode->op[0].reg = R_GS; + break; + } +} + +static void decode_incgroup(CPUX86State *env, struct x86_decode *decode) +{ + decode->op[0].type = X86_VAR_REG; + decode->op[0].reg = decode->opcode[0] - 0x40; + decode->op[0].regptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); +} + +static void decode_decgroup(CPUX86State *env, struct x86_decode *decode) +{ + decode->op[0].type = X86_VAR_REG; + decode->op[0].reg = decode->opcode[0] - 0x48; + decode->op[0].regptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); +} + +static void decode_incgroup2(CPUX86State *env, struct x86_decode *decode) +{ + if (!decode->modrm.reg) { + decode->cmd = X86_DECODE_CMD_INC; + } else if (1 == decode->modrm.reg) { + decode->cmd = X86_DECODE_CMD_DEC; + } +} + +static void decode_pushgroup(CPUX86State *env, struct x86_decode *decode) +{ + decode->op[0].type = X86_VAR_REG; + decode->op[0].reg = decode->opcode[0] - 0x50; + decode->op[0].regptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); +} + +static void decode_popgroup(CPUX86State *env, struct x86_decode *decode) +{ + decode->op[0].type = X86_VAR_REG; + decode->op[0].reg = decode->opcode[0] - 0x58; + decode->op[0].regptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); +} + +static void decode_jxx(CPUX86State *env, struct x86_decode *decode) +{ + decode->displacement = decode_bytes(env, decode, decode->operand_size); + decode->displacement_size = decode->operand_size; +} + +static void decode_farjmp(CPUX86State *env, struct x86_decode *decode) +{ + decode->op[0].type = X86_VAR_IMMEDIATE; + decode->op[0].val = decode_bytes(env, decode, decode->operand_size); + decode->displacement = decode_word(env, decode); +} + +static void decode_addgroup(CPUX86State *env, struct x86_decode *decode) +{ + enum x86_decode_cmd group[] = { + X86_DECODE_CMD_ADD, + X86_DECODE_CMD_OR, + X86_DECODE_CMD_ADC, + X86_DECODE_CMD_SBB, + X86_DECODE_CMD_AND, + X86_DECODE_CMD_SUB, + X86_DECODE_CMD_XOR, + X86_DECODE_CMD_CMP + }; + decode->cmd = group[decode->modrm.reg]; +} + +static void decode_rotgroup(CPUX86State *env, struct x86_decode *decode) +{ + enum x86_decode_cmd group[] = { + X86_DECODE_CMD_ROL, + X86_DECODE_CMD_ROR, + X86_DECODE_CMD_RCL, + X86_DECODE_CMD_RCR, + X86_DECODE_CMD_SHL, + X86_DECODE_CMD_SHR, + X86_DECODE_CMD_SHL, + X86_DECODE_CMD_SAR + }; + decode->cmd = group[decode->modrm.reg]; +} + +static void decode_f7group(CPUX86State *env, struct x86_decode *decode) +{ + enum x86_decode_cmd group[] = { + X86_DECODE_CMD_TST, + X86_DECODE_CMD_TST, + X86_DECODE_CMD_NOT, + X86_DECODE_CMD_NEG, + X86_DECODE_CMD_MUL, + X86_DECODE_CMD_IMUL_1, + X86_DECODE_CMD_DIV, + X86_DECODE_CMD_IDIV + }; + decode->cmd = group[decode->modrm.reg]; + decode_modrm_rm(env, decode, &decode->op[0]); + + switch (decode->modrm.reg) { + case 0: + case 1: + decode_imm(env, decode, &decode->op[1]); + break; + case 2: + break; + case 3: + decode->op[1].type = X86_VAR_IMMEDIATE; + decode->op[1].val = 0; + break; + default: + break; + } +} + +static void decode_xchgroup(CPUX86State *env, struct x86_decode *decode) +{ + decode->op[0].type = X86_VAR_REG; + decode->op[0].reg = decode->opcode[0] - 0x90; + decode->op[0].regptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); +} + +static void decode_movgroup(CPUX86State *env, struct x86_decode *decode) +{ + decode->op[0].type = X86_VAR_REG; + decode->op[0].reg = decode->opcode[0] - 0xb8; + decode->op[0].regptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); + decode_immediate(env, decode, &decode->op[1], decode->operand_size); +} + +static void fetch_moffs(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + op->type = X86_VAR_OFFSET; + op->addr = decode_bytes(env, decode, decode->addressing_size); +} + +static void decode_movgroup8(CPUX86State *env, struct x86_decode *decode) +{ + decode->op[0].type = X86_VAR_REG; + decode->op[0].reg = decode->opcode[0] - 0xb0; + decode->op[0].regptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); + decode_immediate(env, decode, &decode->op[1], decode->operand_size); +} + +static void decode_rcx(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + op->type = X86_VAR_REG; + op->reg = R_ECX; + op->regptr = get_reg_ref(env, op->reg, decode->rex.rex, decode->rex.b, + decode->operand_size); +} + +struct decode_tbl { + uint8_t opcode; + enum x86_decode_cmd cmd; + uint8_t operand_size; + bool is_modrm; + void (*decode_op1)(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op1); + void (*decode_op2)(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op2); + void (*decode_op3)(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op3); + void (*decode_op4)(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op4); + void (*decode_postfix)(CPUX86State *env, struct x86_decode *decode); +}; + +struct decode_x87_tbl { + uint8_t opcode; + uint8_t modrm_reg; + uint8_t modrm_mod; + enum x86_decode_cmd cmd; + uint8_t operand_size; + bool rev; + bool pop; + void (*decode_op1)(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op1); + void (*decode_op2)(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op2); + void (*decode_postfix)(CPUX86State *env, struct x86_decode *decode); +}; + +struct decode_tbl invl_inst = {0x0, 0, 0, false, NULL, NULL, NULL, NULL, + decode_invalid}; + +struct decode_tbl _decode_tbl1[256]; +struct decode_tbl _decode_tbl2[256]; +struct decode_x87_tbl _decode_tbl3[256]; + +static void decode_x87_ins(CPUX86State *env, struct x86_decode *decode) +{ + struct decode_x87_tbl *decoder; + + decode->is_fpu = true; + int mode = decode->modrm.mod == 3 ? 1 : 0; + int index = ((decode->opcode[0] & 0xf) << 4) | (mode << 3) | + decode->modrm.reg; + + decoder = &_decode_tbl3[index]; + + decode->cmd = decoder->cmd; + if (decoder->operand_size) { + decode->operand_size = decoder->operand_size; + } + decode->fpop_stack = decoder->pop; + decode->frev = decoder->rev; + + if (decoder->decode_op1) { + decoder->decode_op1(env, decode, &decode->op[0]); + } + if (decoder->decode_op2) { + decoder->decode_op2(env, decode, &decode->op[1]); + } + if (decoder->decode_postfix) { + decoder->decode_postfix(env, decode); + } + + VM_PANIC_ON_EX(!decode->cmd, "x87 opcode %x %x (%x %x) not decoded\n", + decode->opcode[0], decode->modrm.modrm, decoder->modrm_reg, + decoder->modrm_mod); +} + +static void decode_ffgroup(CPUX86State *env, struct x86_decode *decode) +{ + enum x86_decode_cmd group[] = { + X86_DECODE_CMD_INC, + X86_DECODE_CMD_DEC, + X86_DECODE_CMD_CALL_NEAR_ABS_INDIRECT, + X86_DECODE_CMD_CALL_FAR_ABS_INDIRECT, + X86_DECODE_CMD_JMP_NEAR_ABS_INDIRECT, + X86_DECODE_CMD_JMP_FAR_ABS_INDIRECT, + X86_DECODE_CMD_PUSH, + X86_DECODE_CMD_INVL, + X86_DECODE_CMD_INVL + }; + decode->cmd = group[decode->modrm.reg]; +} + +static void decode_sldtgroup(CPUX86State *env, struct x86_decode *decode) +{ + + enum x86_decode_cmd group[] = { + X86_DECODE_CMD_SLDT, + X86_DECODE_CMD_STR, + X86_DECODE_CMD_LLDT, + X86_DECODE_CMD_LTR, + X86_DECODE_CMD_VERR, + X86_DECODE_CMD_VERW, + X86_DECODE_CMD_INVL, + X86_DECODE_CMD_INVL + }; + decode->cmd = group[decode->modrm.reg]; +} + +static void decode_lidtgroup(CPUX86State *env, struct x86_decode *decode) +{ + enum x86_decode_cmd group[] = { + X86_DECODE_CMD_SGDT, + X86_DECODE_CMD_SIDT, + X86_DECODE_CMD_LGDT, + X86_DECODE_CMD_LIDT, + X86_DECODE_CMD_SMSW, + X86_DECODE_CMD_LMSW, + X86_DECODE_CMD_LMSW, + X86_DECODE_CMD_INVLPG + }; + decode->cmd = group[decode->modrm.reg]; + if (0xf9 == decode->modrm.modrm) { + decode->opcode[decode->len++] = decode->modrm.modrm; + decode->cmd = X86_DECODE_CMD_RDTSCP; + } +} + +static void decode_btgroup(CPUX86State *env, struct x86_decode *decode) +{ + enum x86_decode_cmd group[] = { + X86_DECODE_CMD_INVL, + X86_DECODE_CMD_INVL, + X86_DECODE_CMD_INVL, + X86_DECODE_CMD_INVL, + X86_DECODE_CMD_BT, + X86_DECODE_CMD_BTS, + X86_DECODE_CMD_BTR, + X86_DECODE_CMD_BTC + }; + decode->cmd = group[decode->modrm.reg]; +} + +static void decode_x87_general(CPUX86State *env, struct x86_decode *decode) +{ + decode->is_fpu = true; +} + +static void decode_x87_modrm_floatp(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + op->type = X87_VAR_FLOATP; +} + +static void decode_x87_modrm_intp(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + op->type = X87_VAR_INTP; +} + +static void decode_x87_modrm_bytep(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + op->type = X87_VAR_BYTEP; +} + +static void decode_x87_modrm_st0(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + op->type = X87_VAR_REG; + op->reg = 0; +} + +static void decode_decode_x87_modrm_st0(CPUX86State *env, + struct x86_decode *decode, + struct x86_decode_op *op) +{ + op->type = X87_VAR_REG; + op->reg = decode->modrm.modrm & 7; +} + + +static void decode_aegroup(CPUX86State *env, struct x86_decode *decode) +{ + decode->is_fpu = true; + switch (decode->modrm.reg) { + case 0: + decode->cmd = X86_DECODE_CMD_FXSAVE; + decode_x87_modrm_bytep(env, decode, &decode->op[0]); + break; + case 1: + decode_x87_modrm_bytep(env, decode, &decode->op[0]); + decode->cmd = X86_DECODE_CMD_FXRSTOR; + break; + case 5: + if (decode->modrm.modrm == 0xe8) { + decode->cmd = X86_DECODE_CMD_LFENCE; + } else { + VM_PANIC("xrstor"); + } + break; + case 6: + VM_PANIC_ON(decode->modrm.modrm != 0xf0); + decode->cmd = X86_DECODE_CMD_MFENCE; + break; + case 7: + if (decode->modrm.modrm == 0xf8) { + decode->cmd = X86_DECODE_CMD_SFENCE; + } else { + decode->cmd = X86_DECODE_CMD_CLFLUSH; + } + break; + default: + VM_PANIC_EX("0xae: reg %d\n", decode->modrm.reg); + break; + } +} + +static void decode_bswap(CPUX86State *env, struct x86_decode *decode) +{ + decode->op[0].type = X86_VAR_REG; + decode->op[0].reg = decode->opcode[1] - 0xc8; + decode->op[0].regptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); +} + +static void decode_d9_4(CPUX86State *env, struct x86_decode *decode) +{ + switch (decode->modrm.modrm) { + case 0xe0: + /* FCHS */ + decode->cmd = X86_DECODE_CMD_FCHS; + break; + case 0xe1: + decode->cmd = X86_DECODE_CMD_FABS; + break; + case 0xe4: + VM_PANIC("FTST"); + break; + case 0xe5: + /* FXAM */ + decode->cmd = X86_DECODE_CMD_FXAM; + break; + default: + VM_PANIC("FLDENV"); + break; + } +} + +static void decode_db_4(CPUX86State *env, struct x86_decode *decode) +{ + switch (decode->modrm.modrm) { + case 0xe0: + VM_PANIC_EX("unhandled FNENI: %x %x\n", decode->opcode[0], + decode->modrm.modrm); + break; + case 0xe1: + VM_PANIC_EX("unhandled FNDISI: %x %x\n", decode->opcode[0], + decode->modrm.modrm); + break; + case 0xe2: + VM_PANIC_EX("unhandled FCLEX: %x %x\n", decode->opcode[0], + decode->modrm.modrm); + break; + case 0xe3: + decode->cmd = X86_DECODE_CMD_FNINIT; + break; + case 0xe4: + decode->cmd = X86_DECODE_CMD_FNSETPM; + break; + default: + VM_PANIC_EX("unhandled fpu opcode: %x %x\n", decode->opcode[0], + decode->modrm.modrm); + break; + } +} + + +struct decode_tbl _1op_inst[] = { + {0x0, X86_DECODE_CMD_ADD, 1, true, decode_modrm_rm, decode_modrm_reg, NULL, + NULL, NULL}, + {0x1, X86_DECODE_CMD_ADD, 0, true, decode_modrm_rm, decode_modrm_reg, NULL, + NULL, NULL}, + {0x2, X86_DECODE_CMD_ADD, 1, true, decode_modrm_reg, decode_modrm_rm, NULL, + NULL, NULL}, + {0x3, X86_DECODE_CMD_ADD, 0, true, decode_modrm_reg, decode_modrm_rm, NULL, + NULL, NULL}, + {0x4, X86_DECODE_CMD_ADD, 1, false, decode_rax, decode_imm8, NULL, NULL, + NULL}, + {0x5, X86_DECODE_CMD_ADD, 0, false, decode_rax, decode_imm, NULL, NULL, + NULL}, + {0x6, X86_DECODE_CMD_PUSH_SEG, 0, false, false, NULL, NULL, NULL, + decode_pushseg}, + {0x7, X86_DECODE_CMD_POP_SEG, 0, false, false, NULL, NULL, NULL, + decode_popseg}, + {0x8, X86_DECODE_CMD_OR, 1, true, decode_modrm_rm, decode_modrm_reg, NULL, + NULL, NULL}, + {0x9, X86_DECODE_CMD_OR, 0, true, decode_modrm_rm, decode_modrm_reg, NULL, + NULL, NULL}, + {0xa, X86_DECODE_CMD_OR, 1, true, decode_modrm_reg, decode_modrm_rm, NULL, + NULL, NULL}, + {0xb, X86_DECODE_CMD_OR, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0xc, X86_DECODE_CMD_OR, 1, false, decode_rax, decode_imm8, + NULL, NULL, NULL}, + {0xd, X86_DECODE_CMD_OR, 0, false, decode_rax, decode_imm, + NULL, NULL, NULL}, + + {0xe, X86_DECODE_CMD_PUSH_SEG, 0, false, false, + NULL, NULL, NULL, decode_pushseg}, + {0xf, X86_DECODE_CMD_POP_SEG, 0, false, false, + NULL, NULL, NULL, decode_popseg}, + + {0x10, X86_DECODE_CMD_ADC, 1, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x11, X86_DECODE_CMD_ADC, 0, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x12, X86_DECODE_CMD_ADC, 1, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x13, X86_DECODE_CMD_ADC, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x14, X86_DECODE_CMD_ADC, 1, false, decode_rax, decode_imm, + NULL, NULL, NULL}, + {0x15, X86_DECODE_CMD_ADC, 0, false, decode_rax, decode_imm, + NULL, NULL, NULL}, + + {0x16, X86_DECODE_CMD_PUSH_SEG, 0, false, false, + NULL, NULL, NULL, decode_pushseg}, + {0x17, X86_DECODE_CMD_POP_SEG, 0, false, false, + NULL, NULL, NULL, decode_popseg}, + + {0x18, X86_DECODE_CMD_SBB, 1, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x19, X86_DECODE_CMD_SBB, 0, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x1a, X86_DECODE_CMD_SBB, 1, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x1b, X86_DECODE_CMD_SBB, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x1c, X86_DECODE_CMD_SBB, 1, false, decode_rax, decode_imm8, + NULL, NULL, NULL}, + {0x1d, X86_DECODE_CMD_SBB, 0, false, decode_rax, decode_imm, + NULL, NULL, NULL}, + + {0x1e, X86_DECODE_CMD_PUSH_SEG, 0, false, false, + NULL, NULL, NULL, decode_pushseg}, + {0x1f, X86_DECODE_CMD_POP_SEG, 0, false, false, + NULL, NULL, NULL, decode_popseg}, + + {0x20, X86_DECODE_CMD_AND, 1, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x21, X86_DECODE_CMD_AND, 0, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x22, X86_DECODE_CMD_AND, 1, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x23, X86_DECODE_CMD_AND, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x24, X86_DECODE_CMD_AND, 1, false, decode_rax, decode_imm, + NULL, NULL, NULL}, + {0x25, X86_DECODE_CMD_AND, 0, false, decode_rax, decode_imm, + NULL, NULL, NULL}, + {0x28, X86_DECODE_CMD_SUB, 1, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x29, X86_DECODE_CMD_SUB, 0, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x2a, X86_DECODE_CMD_SUB, 1, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x2b, X86_DECODE_CMD_SUB, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x2c, X86_DECODE_CMD_SUB, 1, false, decode_rax, decode_imm, + NULL, NULL, NULL}, + {0x2d, X86_DECODE_CMD_SUB, 0, false, decode_rax, decode_imm, + NULL, NULL, NULL}, + {0x2f, X86_DECODE_CMD_DAS, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0x30, X86_DECODE_CMD_XOR, 1, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x31, X86_DECODE_CMD_XOR, 0, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x32, X86_DECODE_CMD_XOR, 1, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x33, X86_DECODE_CMD_XOR, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x34, X86_DECODE_CMD_XOR, 1, false, decode_rax, decode_imm, + NULL, NULL, NULL}, + {0x35, X86_DECODE_CMD_XOR, 0, false, decode_rax, decode_imm, + NULL, NULL, NULL}, + + {0x38, X86_DECODE_CMD_CMP, 1, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x39, X86_DECODE_CMD_CMP, 0, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x3a, X86_DECODE_CMD_CMP, 1, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x3b, X86_DECODE_CMD_CMP, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x3c, X86_DECODE_CMD_CMP, 1, false, decode_rax, decode_imm8, + NULL, NULL, NULL}, + {0x3d, X86_DECODE_CMD_CMP, 0, false, decode_rax, decode_imm, + NULL, NULL, NULL}, + + {0x3f, X86_DECODE_CMD_AAS, 0, false, + NULL, NULL, NULL, NULL, NULL}, + + {0x40, X86_DECODE_CMD_INC, 0, false, + NULL, NULL, NULL, NULL, decode_incgroup}, + {0x41, X86_DECODE_CMD_INC, 0, false, + NULL, NULL, NULL, NULL, decode_incgroup}, + {0x42, X86_DECODE_CMD_INC, 0, false, + NULL, NULL, NULL, NULL, decode_incgroup}, + {0x43, X86_DECODE_CMD_INC, 0, false, + NULL, NULL, NULL, NULL, decode_incgroup}, + {0x44, X86_DECODE_CMD_INC, 0, false, + NULL, NULL, NULL, NULL, decode_incgroup}, + {0x45, X86_DECODE_CMD_INC, 0, false, + NULL, NULL, NULL, NULL, decode_incgroup}, + {0x46, X86_DECODE_CMD_INC, 0, false, + NULL, NULL, NULL, NULL, decode_incgroup}, + {0x47, X86_DECODE_CMD_INC, 0, false, + NULL, NULL, NULL, NULL, decode_incgroup}, + + {0x48, X86_DECODE_CMD_DEC, 0, false, + NULL, NULL, NULL, NULL, decode_decgroup}, + {0x49, X86_DECODE_CMD_DEC, 0, false, + NULL, NULL, NULL, NULL, decode_decgroup}, + {0x4a, X86_DECODE_CMD_DEC, 0, false, + NULL, NULL, NULL, NULL, decode_decgroup}, + {0x4b, X86_DECODE_CMD_DEC, 0, false, + NULL, NULL, NULL, NULL, decode_decgroup}, + {0x4c, X86_DECODE_CMD_DEC, 0, false, + NULL, NULL, NULL, NULL, decode_decgroup}, + {0x4d, X86_DECODE_CMD_DEC, 0, false, + NULL, NULL, NULL, NULL, decode_decgroup}, + {0x4e, X86_DECODE_CMD_DEC, 0, false, + NULL, NULL, NULL, NULL, decode_decgroup}, + {0x4f, X86_DECODE_CMD_DEC, 0, false, + NULL, NULL, NULL, NULL, decode_decgroup}, + + {0x50, X86_DECODE_CMD_PUSH, 0, false, + NULL, NULL, NULL, NULL, decode_pushgroup}, + {0x51, X86_DECODE_CMD_PUSH, 0, false, + NULL, NULL, NULL, NULL, decode_pushgroup}, + {0x52, X86_DECODE_CMD_PUSH, 0, false, + NULL, NULL, NULL, NULL, decode_pushgroup}, + {0x53, X86_DECODE_CMD_PUSH, 0, false, + NULL, NULL, NULL, NULL, decode_pushgroup}, + {0x54, X86_DECODE_CMD_PUSH, 0, false, + NULL, NULL, NULL, NULL, decode_pushgroup}, + {0x55, X86_DECODE_CMD_PUSH, 0, false, + NULL, NULL, NULL, NULL, decode_pushgroup}, + {0x56, X86_DECODE_CMD_PUSH, 0, false, + NULL, NULL, NULL, NULL, decode_pushgroup}, + {0x57, X86_DECODE_CMD_PUSH, 0, false, + NULL, NULL, NULL, NULL, decode_pushgroup}, + + {0x58, X86_DECODE_CMD_POP, 0, false, + NULL, NULL, NULL, NULL, decode_popgroup}, + {0x59, X86_DECODE_CMD_POP, 0, false, + NULL, NULL, NULL, NULL, decode_popgroup}, + {0x5a, X86_DECODE_CMD_POP, 0, false, + NULL, NULL, NULL, NULL, decode_popgroup}, + {0x5b, X86_DECODE_CMD_POP, 0, false, + NULL, NULL, NULL, NULL, decode_popgroup}, + {0x5c, X86_DECODE_CMD_POP, 0, false, + NULL, NULL, NULL, NULL, decode_popgroup}, + {0x5d, X86_DECODE_CMD_POP, 0, false, + NULL, NULL, NULL, NULL, decode_popgroup}, + {0x5e, X86_DECODE_CMD_POP, 0, false, + NULL, NULL, NULL, NULL, decode_popgroup}, + {0x5f, X86_DECODE_CMD_POP, 0, false, + NULL, NULL, NULL, NULL, decode_popgroup}, + + {0x60, X86_DECODE_CMD_PUSHA, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0x61, X86_DECODE_CMD_POPA, 0, false, + NULL, NULL, NULL, NULL, NULL}, + + {0x68, X86_DECODE_CMD_PUSH, 0, false, decode_imm, + NULL, NULL, NULL, NULL}, + {0x6a, X86_DECODE_CMD_PUSH, 0, false, decode_imm8_signed, + NULL, NULL, NULL, NULL}, + {0x69, X86_DECODE_CMD_IMUL_3, 0, true, decode_modrm_reg, + decode_modrm_rm, decode_imm, NULL, NULL}, + {0x6b, X86_DECODE_CMD_IMUL_3, 0, true, decode_modrm_reg, decode_modrm_rm, + decode_imm8_signed, NULL, NULL}, + + {0x6c, X86_DECODE_CMD_INS, 1, false, + NULL, NULL, NULL, NULL, NULL}, + {0x6d, X86_DECODE_CMD_INS, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0x6e, X86_DECODE_CMD_OUTS, 1, false, + NULL, NULL, NULL, NULL, NULL}, + {0x6f, X86_DECODE_CMD_OUTS, 0, false, + NULL, NULL, NULL, NULL, NULL}, + + {0x70, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x71, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x72, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x73, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x74, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x75, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x76, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x77, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x78, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x79, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x7a, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x7b, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x7c, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x7d, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x7e, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x7f, X86_DECODE_CMD_JXX, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + + {0x80, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm8, + NULL, NULL, decode_addgroup}, + {0x81, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm, + NULL, NULL, decode_addgroup}, + {0x82, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm8, + NULL, NULL, decode_addgroup}, + {0x83, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm8_signed, + NULL, NULL, decode_addgroup}, + {0x84, X86_DECODE_CMD_TST, 1, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x85, X86_DECODE_CMD_TST, 0, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x86, X86_DECODE_CMD_XCHG, 1, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x87, X86_DECODE_CMD_XCHG, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x88, X86_DECODE_CMD_MOV, 1, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x89, X86_DECODE_CMD_MOV, 0, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0x8a, X86_DECODE_CMD_MOV, 1, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x8b, X86_DECODE_CMD_MOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x8c, X86_DECODE_CMD_MOV_FROM_SEG, 0, true, decode_modrm_rm, + decode_modrm_reg, NULL, NULL, NULL}, + {0x8d, X86_DECODE_CMD_LEA, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x8e, X86_DECODE_CMD_MOV_TO_SEG, 0, true, decode_modrm_reg, + decode_modrm_rm, NULL, NULL, NULL}, + {0x8f, X86_DECODE_CMD_POP, 0, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + + {0x90, X86_DECODE_CMD_NOP, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0x91, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, + NULL, NULL, decode_xchgroup}, + {0x92, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, + NULL, NULL, decode_xchgroup}, + {0x93, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, + NULL, NULL, decode_xchgroup}, + {0x94, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, + NULL, NULL, decode_xchgroup}, + {0x95, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, + NULL, NULL, decode_xchgroup}, + {0x96, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, + NULL, NULL, decode_xchgroup}, + {0x97, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, + NULL, NULL, decode_xchgroup}, + + {0x98, X86_DECODE_CMD_CBW, 0, false, NULL, NULL, + NULL, NULL, NULL}, + {0x99, X86_DECODE_CMD_CWD, 0, false, NULL, NULL, + NULL, NULL, NULL}, + + {0x9a, X86_DECODE_CMD_CALL_FAR, 0, false, NULL, + NULL, NULL, NULL, decode_farjmp}, + + {0x9c, X86_DECODE_CMD_PUSHF, 0, false, NULL, NULL, + NULL, NULL, NULL}, + /*{0x9d, X86_DECODE_CMD_POPF, 0, false, NULL, NULL, + NULL, NULL, NULL},*/ + {0x9e, X86_DECODE_CMD_SAHF, 0, false, NULL, NULL, + NULL, NULL, NULL}, + {0x9f, X86_DECODE_CMD_LAHF, 0, false, NULL, NULL, + NULL, NULL, NULL}, + + {0xa0, X86_DECODE_CMD_MOV, 1, false, decode_rax, fetch_moffs, + NULL, NULL, NULL}, + {0xa1, X86_DECODE_CMD_MOV, 0, false, decode_rax, fetch_moffs, + NULL, NULL, NULL}, + {0xa2, X86_DECODE_CMD_MOV, 1, false, fetch_moffs, decode_rax, + NULL, NULL, NULL}, + {0xa3, X86_DECODE_CMD_MOV, 0, false, fetch_moffs, decode_rax, + NULL, NULL, NULL}, + + {0xa4, X86_DECODE_CMD_MOVS, 1, false, NULL, NULL, + NULL, NULL, NULL}, + {0xa5, X86_DECODE_CMD_MOVS, 0, false, NULL, NULL, + NULL, NULL, NULL}, + {0xa6, X86_DECODE_CMD_CMPS, 1, false, NULL, NULL, + NULL, NULL, NULL}, + {0xa7, X86_DECODE_CMD_CMPS, 0, false, NULL, NULL, + NULL, NULL, NULL}, + {0xaa, X86_DECODE_CMD_STOS, 1, false, NULL, NULL, + NULL, NULL, NULL}, + {0xab, X86_DECODE_CMD_STOS, 0, false, NULL, NULL, + NULL, NULL, NULL}, + {0xac, X86_DECODE_CMD_LODS, 1, false, NULL, NULL, + NULL, NULL, NULL}, + {0xad, X86_DECODE_CMD_LODS, 0, false, NULL, NULL, + NULL, NULL, NULL}, + {0xae, X86_DECODE_CMD_SCAS, 1, false, NULL, NULL, + NULL, NULL, NULL}, + {0xaf, X86_DECODE_CMD_SCAS, 0, false, NULL, NULL, + NULL, NULL, NULL}, + + {0xa8, X86_DECODE_CMD_TST, 1, false, decode_rax, decode_imm, + NULL, NULL, NULL}, + {0xa9, X86_DECODE_CMD_TST, 0, false, decode_rax, decode_imm, + NULL, NULL, NULL}, + + {0xb0, X86_DECODE_CMD_MOV, 1, false, NULL, + NULL, NULL, NULL, decode_movgroup8}, + {0xb1, X86_DECODE_CMD_MOV, 1, false, NULL, + NULL, NULL, NULL, decode_movgroup8}, + {0xb2, X86_DECODE_CMD_MOV, 1, false, NULL, + NULL, NULL, NULL, decode_movgroup8}, + {0xb3, X86_DECODE_CMD_MOV, 1, false, NULL, + NULL, NULL, NULL, decode_movgroup8}, + {0xb4, X86_DECODE_CMD_MOV, 1, false, NULL, + NULL, NULL, NULL, decode_movgroup8}, + {0xb5, X86_DECODE_CMD_MOV, 1, false, NULL, + NULL, NULL, NULL, decode_movgroup8}, + {0xb6, X86_DECODE_CMD_MOV, 1, false, NULL, + NULL, NULL, NULL, decode_movgroup8}, + {0xb7, X86_DECODE_CMD_MOV, 1, false, NULL, + NULL, NULL, NULL, decode_movgroup8}, + + {0xb8, X86_DECODE_CMD_MOV, 0, false, NULL, + NULL, NULL, NULL, decode_movgroup}, + {0xb9, X86_DECODE_CMD_MOV, 0, false, NULL, + NULL, NULL, NULL, decode_movgroup}, + {0xba, X86_DECODE_CMD_MOV, 0, false, NULL, + NULL, NULL, NULL, decode_movgroup}, + {0xbb, X86_DECODE_CMD_MOV, 0, false, NULL, + NULL, NULL, NULL, decode_movgroup}, + {0xbc, X86_DECODE_CMD_MOV, 0, false, NULL, + NULL, NULL, NULL, decode_movgroup}, + {0xbd, X86_DECODE_CMD_MOV, 0, false, NULL, + NULL, NULL, NULL, decode_movgroup}, + {0xbe, X86_DECODE_CMD_MOV, 0, false, NULL, + NULL, NULL, NULL, decode_movgroup}, + {0xbf, X86_DECODE_CMD_MOV, 0, false, NULL, + NULL, NULL, NULL, decode_movgroup}, + + {0xc0, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm8, + NULL, NULL, decode_rotgroup}, + {0xc1, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm8, + NULL, NULL, decode_rotgroup}, + + {0xc2, X86_DECODE_RET_NEAR, 0, false, decode_imm16, + NULL, NULL, NULL, NULL}, + {0xc3, X86_DECODE_RET_NEAR, 0, false, NULL, + NULL, NULL, NULL, NULL}, + + {0xc4, X86_DECODE_CMD_LES, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0xc5, X86_DECODE_CMD_LDS, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + + {0xc6, X86_DECODE_CMD_MOV, 1, true, decode_modrm_rm, decode_imm8, + NULL, NULL, NULL}, + {0xc7, X86_DECODE_CMD_MOV, 0, true, decode_modrm_rm, decode_imm, + NULL, NULL, NULL}, + + {0xc8, X86_DECODE_CMD_ENTER, 0, false, decode_imm16, decode_imm8, + NULL, NULL, NULL}, + {0xc9, X86_DECODE_CMD_LEAVE, 0, false, NULL, NULL, + NULL, NULL, NULL}, + {0xca, X86_DECODE_RET_FAR, 0, false, decode_imm16, NULL, + NULL, NULL, NULL}, + {0xcb, X86_DECODE_RET_FAR, 0, false, decode_imm_0, NULL, + NULL, NULL, NULL}, + {0xcd, X86_DECODE_CMD_INT, 0, false, decode_imm8, NULL, + NULL, NULL, NULL}, + /*{0xcf, X86_DECODE_CMD_IRET, 0, false, NULL, NULL, + NULL, NULL, NULL},*/ + + {0xd0, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm_1, + NULL, NULL, decode_rotgroup}, + {0xd1, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm_1, + NULL, NULL, decode_rotgroup}, + {0xd2, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_rcx, + NULL, NULL, decode_rotgroup}, + {0xd3, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_rcx, + NULL, NULL, decode_rotgroup}, + + {0xd4, X86_DECODE_CMD_AAM, 0, false, decode_imm8, + NULL, NULL, NULL, NULL}, + {0xd5, X86_DECODE_CMD_AAD, 0, false, decode_imm8, + NULL, NULL, NULL, NULL}, + + {0xd7, X86_DECODE_CMD_XLAT, 0, false, + NULL, NULL, NULL, NULL, NULL}, + + {0xd8, X86_DECODE_CMD_INVL, 0, true, NULL, + NULL, NULL, NULL, decode_x87_ins}, + {0xd9, X86_DECODE_CMD_INVL, 0, true, NULL, + NULL, NULL, NULL, decode_x87_ins}, + {0xda, X86_DECODE_CMD_INVL, 0, true, NULL, + NULL, NULL, NULL, decode_x87_ins}, + {0xdb, X86_DECODE_CMD_INVL, 0, true, NULL, + NULL, NULL, NULL, decode_x87_ins}, + {0xdc, X86_DECODE_CMD_INVL, 0, true, NULL, + NULL, NULL, NULL, decode_x87_ins}, + {0xdd, X86_DECODE_CMD_INVL, 0, true, NULL, + NULL, NULL, NULL, decode_x87_ins}, + {0xde, X86_DECODE_CMD_INVL, 0, true, NULL, + NULL, NULL, NULL, decode_x87_ins}, + {0xdf, X86_DECODE_CMD_INVL, 0, true, NULL, + NULL, NULL, NULL, decode_x87_ins}, + + {0xe0, X86_DECODE_CMD_LOOP, 0, false, decode_imm8_signed, + NULL, NULL, NULL, NULL}, + {0xe1, X86_DECODE_CMD_LOOP, 0, false, decode_imm8_signed, + NULL, NULL, NULL, NULL}, + {0xe2, X86_DECODE_CMD_LOOP, 0, false, decode_imm8_signed, + NULL, NULL, NULL, NULL}, + + {0xe3, X86_DECODE_CMD_JCXZ, 1, false, + NULL, NULL, NULL, NULL, decode_jxx}, + + {0xe4, X86_DECODE_CMD_IN, 1, false, decode_imm8, + NULL, NULL, NULL, NULL}, + {0xe5, X86_DECODE_CMD_IN, 0, false, decode_imm8, + NULL, NULL, NULL, NULL}, + {0xe6, X86_DECODE_CMD_OUT, 1, false, decode_imm8, + NULL, NULL, NULL, NULL}, + {0xe7, X86_DECODE_CMD_OUT, 0, false, decode_imm8, + NULL, NULL, NULL, NULL}, + {0xe8, X86_DECODE_CMD_CALL_NEAR, 0, false, decode_imm_signed, + NULL, NULL, NULL, NULL}, + {0xe9, X86_DECODE_CMD_JMP_NEAR, 0, false, decode_imm_signed, + NULL, NULL, NULL, NULL}, + {0xea, X86_DECODE_CMD_JMP_FAR, 0, false, + NULL, NULL, NULL, NULL, decode_farjmp}, + {0xeb, X86_DECODE_CMD_JMP_NEAR, 1, false, decode_imm8_signed, + NULL, NULL, NULL, NULL}, + {0xec, X86_DECODE_CMD_IN, 1, false, + NULL, NULL, NULL, NULL, NULL}, + {0xed, X86_DECODE_CMD_IN, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0xee, X86_DECODE_CMD_OUT, 1, false, + NULL, NULL, NULL, NULL, NULL}, + {0xef, X86_DECODE_CMD_OUT, 0, false, + NULL, NULL, NULL, NULL, NULL}, + + {0xf4, X86_DECODE_CMD_HLT, 0, false, + NULL, NULL, NULL, NULL, NULL}, + + {0xf5, X86_DECODE_CMD_CMC, 0, false, + NULL, NULL, NULL, NULL, NULL}, + + {0xf6, X86_DECODE_CMD_INVL, 1, true, + NULL, NULL, NULL, NULL, decode_f7group}, + {0xf7, X86_DECODE_CMD_INVL, 0, true, + NULL, NULL, NULL, NULL, decode_f7group}, + + {0xf8, X86_DECODE_CMD_CLC, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0xf9, X86_DECODE_CMD_STC, 0, false, + NULL, NULL, NULL, NULL, NULL}, + + {0xfa, X86_DECODE_CMD_CLI, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0xfb, X86_DECODE_CMD_STI, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0xfc, X86_DECODE_CMD_CLD, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0xfd, X86_DECODE_CMD_STD, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0xfe, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, + NULL, NULL, NULL, decode_incgroup2}, + {0xff, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, + NULL, NULL, NULL, decode_ffgroup}, +}; + +struct decode_tbl _2op_inst[] = { + {0x0, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, + NULL, NULL, NULL, decode_sldtgroup}, + {0x1, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, + NULL, NULL, NULL, decode_lidtgroup}, + {0x6, X86_DECODE_CMD_CLTS, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0x9, X86_DECODE_CMD_WBINVD, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0x18, X86_DECODE_CMD_PREFETCH, 0, true, + NULL, NULL, NULL, NULL, decode_x87_general}, + {0x1f, X86_DECODE_CMD_NOP, 0, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x20, X86_DECODE_CMD_MOV_FROM_CR, 0, true, decode_modrm_rm, + decode_modrm_reg, NULL, NULL, NULL}, + {0x21, X86_DECODE_CMD_MOV_FROM_DR, 0, true, decode_modrm_rm, + decode_modrm_reg, NULL, NULL, NULL}, + {0x22, X86_DECODE_CMD_MOV_TO_CR, 0, true, decode_modrm_reg, + decode_modrm_rm, NULL, NULL, NULL}, + {0x23, X86_DECODE_CMD_MOV_TO_DR, 0, true, decode_modrm_reg, + decode_modrm_rm, NULL, NULL, NULL}, + {0x30, X86_DECODE_CMD_WRMSR, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0x31, X86_DECODE_CMD_RDTSC, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0x32, X86_DECODE_CMD_RDMSR, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0x40, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x41, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x42, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x43, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x44, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x45, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x46, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x47, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x48, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x49, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x4a, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x4b, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x4c, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x4d, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x4e, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x4f, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0x77, X86_DECODE_CMD_EMMS, 0, false, + NULL, NULL, NULL, NULL, decode_x87_general}, + {0x82, X86_DECODE_CMD_JXX, 0, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x83, X86_DECODE_CMD_JXX, 0, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x84, X86_DECODE_CMD_JXX, 0, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x85, X86_DECODE_CMD_JXX, 0, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x86, X86_DECODE_CMD_JXX, 0, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x87, X86_DECODE_CMD_JXX, 0, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x88, X86_DECODE_CMD_JXX, 0, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x89, X86_DECODE_CMD_JXX, 0, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x8a, X86_DECODE_CMD_JXX, 0, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x8b, X86_DECODE_CMD_JXX, 0, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x8c, X86_DECODE_CMD_JXX, 0, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x8d, X86_DECODE_CMD_JXX, 0, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x8e, X86_DECODE_CMD_JXX, 0, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x8f, X86_DECODE_CMD_JXX, 0, false, + NULL, NULL, NULL, NULL, decode_jxx}, + {0x90, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x91, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x92, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x93, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x94, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x95, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x96, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x97, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x98, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x99, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x9a, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x9b, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x9c, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x9d, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x9e, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + {0x9f, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + + {0xb0, X86_DECODE_CMD_CMPXCHG, 1, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0xb1, X86_DECODE_CMD_CMPXCHG, 0, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + + {0xb6, X86_DECODE_CMD_MOVZX, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0xb7, X86_DECODE_CMD_MOVZX, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0xb8, X86_DECODE_CMD_POPCNT, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0xbe, X86_DECODE_CMD_MOVSX, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0xbf, X86_DECODE_CMD_MOVSX, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0xa0, X86_DECODE_CMD_PUSH_SEG, 0, false, false, + NULL, NULL, NULL, decode_pushseg}, + {0xa1, X86_DECODE_CMD_POP_SEG, 0, false, false, + NULL, NULL, NULL, decode_popseg}, + {0xa2, X86_DECODE_CMD_CPUID, 0, false, + NULL, NULL, NULL, NULL, NULL}, + {0xa3, X86_DECODE_CMD_BT, 0, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0xa4, X86_DECODE_CMD_SHLD, 0, true, decode_modrm_rm, decode_modrm_reg, + decode_imm8, NULL, NULL}, + {0xa5, X86_DECODE_CMD_SHLD, 0, true, decode_modrm_rm, decode_modrm_reg, + decode_rcx, NULL, NULL}, + {0xa8, X86_DECODE_CMD_PUSH_SEG, 0, false, false, + NULL, NULL, NULL, decode_pushseg}, + {0xa9, X86_DECODE_CMD_POP_SEG, 0, false, false, + NULL, NULL, NULL, decode_popseg}, + {0xab, X86_DECODE_CMD_BTS, 0, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0xac, X86_DECODE_CMD_SHRD, 0, true, decode_modrm_rm, decode_modrm_reg, + decode_imm8, NULL, NULL}, + {0xad, X86_DECODE_CMD_SHRD, 0, true, decode_modrm_rm, decode_modrm_reg, + decode_rcx, NULL, NULL}, + + {0xae, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, + NULL, NULL, NULL, decode_aegroup}, + + {0xaf, X86_DECODE_CMD_IMUL_2, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0xb2, X86_DECODE_CMD_LSS, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0xb3, X86_DECODE_CMD_BTR, 0, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0xba, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm8, + NULL, NULL, decode_btgroup}, + {0xbb, X86_DECODE_CMD_BTC, 0, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + {0xbc, X86_DECODE_CMD_BSF, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + {0xbd, X86_DECODE_CMD_BSR, 0, true, decode_modrm_reg, decode_modrm_rm, + NULL, NULL, NULL}, + + {0xc1, X86_DECODE_CMD_XADD, 0, true, decode_modrm_rm, decode_modrm_reg, + NULL, NULL, NULL}, + + {0xc7, X86_DECODE_CMD_CMPXCHG8B, 0, true, decode_modrm_rm, + NULL, NULL, NULL, NULL}, + + {0xc8, X86_DECODE_CMD_BSWAP, 0, false, + NULL, NULL, NULL, NULL, decode_bswap}, + {0xc9, X86_DECODE_CMD_BSWAP, 0, false, + NULL, NULL, NULL, NULL, decode_bswap}, + {0xca, X86_DECODE_CMD_BSWAP, 0, false, + NULL, NULL, NULL, NULL, decode_bswap}, + {0xcb, X86_DECODE_CMD_BSWAP, 0, false, + NULL, NULL, NULL, NULL, decode_bswap}, + {0xcc, X86_DECODE_CMD_BSWAP, 0, false, + NULL, NULL, NULL, NULL, decode_bswap}, + {0xcd, X86_DECODE_CMD_BSWAP, 0, false, + NULL, NULL, NULL, NULL, decode_bswap}, + {0xce, X86_DECODE_CMD_BSWAP, 0, false, + NULL, NULL, NULL, NULL, decode_bswap}, + {0xcf, X86_DECODE_CMD_BSWAP, 0, false, + NULL, NULL, NULL, NULL, decode_bswap}, +}; + +struct decode_x87_tbl invl_inst_x87 = {0x0, 0, 0, 0, 0, false, false, NULL, + NULL, decode_invalid}; + +struct decode_x87_tbl _x87_inst[] = { + {0xd8, 0, 3, X86_DECODE_CMD_FADD, 10, false, false, + decode_x87_modrm_st0, decode_decode_x87_modrm_st0, NULL}, + {0xd8, 0, 0, X86_DECODE_CMD_FADD, 4, false, false, decode_x87_modrm_st0, + decode_x87_modrm_floatp, NULL}, + {0xd8, 1, 3, X86_DECODE_CMD_FMUL, 10, false, false, decode_x87_modrm_st0, + decode_decode_x87_modrm_st0, NULL}, + {0xd8, 1, 0, X86_DECODE_CMD_FMUL, 4, false, false, decode_x87_modrm_st0, + decode_x87_modrm_floatp, NULL}, + {0xd8, 4, 3, X86_DECODE_CMD_FSUB, 10, false, false, decode_x87_modrm_st0, + decode_x87_modrm_st0, NULL}, + {0xd8, 4, 0, X86_DECODE_CMD_FSUB, 4, false, false, decode_x87_modrm_st0, + decode_x87_modrm_floatp, NULL}, + {0xd8, 5, 3, X86_DECODE_CMD_FSUB, 10, true, false, decode_x87_modrm_st0, + decode_x87_modrm_st0, NULL}, + {0xd8, 5, 0, X86_DECODE_CMD_FSUB, 4, true, false, decode_x87_modrm_st0, + decode_x87_modrm_floatp, NULL}, + {0xd8, 6, 3, X86_DECODE_CMD_FDIV, 10, false, false, decode_x87_modrm_st0, + decode_x87_modrm_st0, NULL}, + {0xd8, 6, 0, X86_DECODE_CMD_FDIV, 4, false, false, decode_x87_modrm_st0, + decode_x87_modrm_floatp, NULL}, + {0xd8, 7, 3, X86_DECODE_CMD_FDIV, 10, true, false, decode_x87_modrm_st0, + decode_x87_modrm_st0, NULL}, + {0xd8, 7, 0, X86_DECODE_CMD_FDIV, 4, true, false, decode_x87_modrm_st0, + decode_x87_modrm_floatp, NULL}, + + {0xd9, 0, 3, X86_DECODE_CMD_FLD, 10, false, false, + decode_x87_modrm_st0, NULL, NULL}, + {0xd9, 0, 0, X86_DECODE_CMD_FLD, 4, false, false, + decode_x87_modrm_floatp, NULL, NULL}, + {0xd9, 1, 3, X86_DECODE_CMD_FXCH, 10, false, false, decode_x87_modrm_st0, + decode_x87_modrm_st0, NULL}, + {0xd9, 1, 0, X86_DECODE_CMD_INVL, 10, false, false, + decode_x87_modrm_st0, NULL, NULL}, + {0xd9, 2, 3, X86_DECODE_CMD_INVL, 10, false, false, + decode_x87_modrm_st0, NULL, NULL}, + {0xd9, 2, 0, X86_DECODE_CMD_FST, 4, false, false, + decode_x87_modrm_floatp, NULL, NULL}, + {0xd9, 3, 3, X86_DECODE_CMD_INVL, 10, false, false, + decode_x87_modrm_st0, NULL, NULL}, + {0xd9, 3, 0, X86_DECODE_CMD_FST, 4, false, true, + decode_x87_modrm_floatp, NULL, NULL}, + {0xd9, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, + decode_x87_modrm_st0, NULL, decode_d9_4}, + {0xd9, 4, 0, X86_DECODE_CMD_INVL, 4, false, false, + decode_x87_modrm_bytep, NULL, NULL}, + {0xd9, 5, 3, X86_DECODE_CMD_FLDxx, 10, false, false, NULL, NULL, NULL}, + {0xd9, 5, 0, X86_DECODE_CMD_FLDCW, 2, false, false, + decode_x87_modrm_bytep, NULL, NULL}, + + {0xd9, 7, 3, X86_DECODE_CMD_FNSTCW, 2, false, false, + decode_x87_modrm_bytep, NULL, NULL}, + {0xd9, 7, 0, X86_DECODE_CMD_FNSTCW, 2, false, false, + decode_x87_modrm_bytep, NULL, NULL}, + + {0xda, 0, 3, X86_DECODE_CMD_FCMOV, 10, false, false, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xda, 0, 0, X86_DECODE_CMD_FADD, 4, false, false, decode_x87_modrm_st0, + decode_x87_modrm_intp, NULL}, + {0xda, 1, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0, + decode_decode_x87_modrm_st0, NULL}, + {0xda, 1, 0, X86_DECODE_CMD_FMUL, 4, false, false, decode_x87_modrm_st0, + decode_x87_modrm_intp, NULL}, + {0xda, 2, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0, + decode_x87_modrm_st0, NULL}, + {0xda, 3, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0, + decode_x87_modrm_st0, NULL}, + {0xda, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL}, + {0xda, 4, 0, X86_DECODE_CMD_FSUB, 4, false, false, decode_x87_modrm_st0, + decode_x87_modrm_intp, NULL}, + {0xda, 5, 3, X86_DECODE_CMD_FUCOM, 10, false, true, decode_x87_modrm_st0, + decode_decode_x87_modrm_st0, NULL}, + {0xda, 5, 0, X86_DECODE_CMD_FSUB, 4, true, false, decode_x87_modrm_st0, + decode_x87_modrm_intp, NULL}, + {0xda, 6, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL}, + {0xda, 6, 0, X86_DECODE_CMD_FDIV, 4, false, false, decode_x87_modrm_st0, + decode_x87_modrm_intp, NULL}, + {0xda, 7, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL}, + {0xda, 7, 0, X86_DECODE_CMD_FDIV, 4, true, false, decode_x87_modrm_st0, + decode_x87_modrm_intp, NULL}, + + {0xdb, 0, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0, + decode_x87_modrm_st0, NULL}, + {0xdb, 0, 0, X86_DECODE_CMD_FLD, 4, false, false, + decode_x87_modrm_intp, NULL, NULL}, + {0xdb, 1, 3, X86_DECODE_CMD_FCMOV, 10, false, false, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdb, 2, 3, X86_DECODE_CMD_FCMOV, 10, false, false, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdb, 2, 0, X86_DECODE_CMD_FST, 4, false, false, + decode_x87_modrm_intp, NULL, NULL}, + {0xdb, 3, 3, X86_DECODE_CMD_FCMOV, 10, false, false, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdb, 3, 0, X86_DECODE_CMD_FST, 4, false, true, + decode_x87_modrm_intp, NULL, NULL}, + {0xdb, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, + decode_db_4}, + {0xdb, 4, 0, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL}, + {0xdb, 5, 3, X86_DECODE_CMD_FUCOMI, 10, false, false, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdb, 5, 0, X86_DECODE_CMD_FLD, 10, false, false, + decode_x87_modrm_floatp, NULL, NULL}, + {0xdb, 7, 0, X86_DECODE_CMD_FST, 10, false, true, + decode_x87_modrm_floatp, NULL, NULL}, + + {0xdc, 0, 3, X86_DECODE_CMD_FADD, 10, false, false, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdc, 0, 0, X86_DECODE_CMD_FADD, 8, false, false, + decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL}, + {0xdc, 1, 3, X86_DECODE_CMD_FMUL, 10, false, false, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdc, 1, 0, X86_DECODE_CMD_FMUL, 8, false, false, + decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL}, + {0xdc, 4, 3, X86_DECODE_CMD_FSUB, 10, true, false, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdc, 4, 0, X86_DECODE_CMD_FSUB, 8, false, false, + decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL}, + {0xdc, 5, 3, X86_DECODE_CMD_FSUB, 10, false, false, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdc, 5, 0, X86_DECODE_CMD_FSUB, 8, true, false, + decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL}, + {0xdc, 6, 3, X86_DECODE_CMD_FDIV, 10, true, false, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdc, 6, 0, X86_DECODE_CMD_FDIV, 8, false, false, + decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL}, + {0xdc, 7, 3, X86_DECODE_CMD_FDIV, 10, false, false, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdc, 7, 0, X86_DECODE_CMD_FDIV, 8, true, false, + decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL}, + + {0xdd, 0, 0, X86_DECODE_CMD_FLD, 8, false, false, + decode_x87_modrm_floatp, NULL, NULL}, + {0xdd, 1, 3, X86_DECODE_CMD_FXCH, 10, false, false, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdd, 2, 3, X86_DECODE_CMD_FST, 10, false, false, + decode_x87_modrm_st0, NULL, NULL}, + {0xdd, 2, 0, X86_DECODE_CMD_FST, 8, false, false, + decode_x87_modrm_floatp, NULL, NULL}, + {0xdd, 3, 3, X86_DECODE_CMD_FST, 10, false, true, + decode_x87_modrm_st0, NULL, NULL}, + {0xdd, 3, 0, X86_DECODE_CMD_FST, 8, false, true, + decode_x87_modrm_floatp, NULL, NULL}, + {0xdd, 4, 3, X86_DECODE_CMD_FUCOM, 10, false, false, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdd, 4, 0, X86_DECODE_CMD_FRSTOR, 8, false, false, + decode_x87_modrm_bytep, NULL, NULL}, + {0xdd, 5, 3, X86_DECODE_CMD_FUCOM, 10, false, true, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdd, 7, 0, X86_DECODE_CMD_FNSTSW, 0, false, false, + decode_x87_modrm_bytep, NULL, NULL}, + {0xdd, 7, 3, X86_DECODE_CMD_FNSTSW, 0, false, false, + decode_x87_modrm_bytep, NULL, NULL}, + + {0xde, 0, 3, X86_DECODE_CMD_FADD, 10, false, true, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xde, 0, 0, X86_DECODE_CMD_FADD, 2, false, false, + decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, + {0xde, 1, 3, X86_DECODE_CMD_FMUL, 10, false, true, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xde, 1, 0, X86_DECODE_CMD_FMUL, 2, false, false, + decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, + {0xde, 4, 3, X86_DECODE_CMD_FSUB, 10, true, true, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xde, 4, 0, X86_DECODE_CMD_FSUB, 2, false, false, + decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, + {0xde, 5, 3, X86_DECODE_CMD_FSUB, 10, false, true, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xde, 5, 0, X86_DECODE_CMD_FSUB, 2, true, false, + decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, + {0xde, 6, 3, X86_DECODE_CMD_FDIV, 10, true, true, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xde, 6, 0, X86_DECODE_CMD_FDIV, 2, false, false, + decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, + {0xde, 7, 3, X86_DECODE_CMD_FDIV, 10, false, true, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xde, 7, 0, X86_DECODE_CMD_FDIV, 2, true, false, + decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, + + {0xdf, 0, 0, X86_DECODE_CMD_FLD, 2, false, false, + decode_x87_modrm_intp, NULL, NULL}, + {0xdf, 1, 3, X86_DECODE_CMD_FXCH, 10, false, false, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdf, 2, 3, X86_DECODE_CMD_FST, 10, false, true, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdf, 2, 0, X86_DECODE_CMD_FST, 2, false, false, + decode_x87_modrm_intp, NULL, NULL}, + {0xdf, 3, 3, X86_DECODE_CMD_FST, 10, false, true, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdf, 3, 0, X86_DECODE_CMD_FST, 2, false, true, + decode_x87_modrm_intp, NULL, NULL}, + {0xdf, 4, 3, X86_DECODE_CMD_FNSTSW, 2, false, true, + decode_x87_modrm_bytep, NULL, NULL}, + {0xdf, 5, 3, X86_DECODE_CMD_FUCOMI, 10, false, true, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, + {0xdf, 5, 0, X86_DECODE_CMD_FLD, 8, false, false, + decode_x87_modrm_intp, NULL, NULL}, + {0xdf, 7, 0, X86_DECODE_CMD_FST, 8, false, true, + decode_x87_modrm_intp, NULL, NULL}, +}; + +void calc_modrm_operand16(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + target_ulong ptr = 0; + X86Seg seg = R_DS; + + if (!decode->modrm.mod && 6 == decode->modrm.rm) { + ptr = decode->displacement; + goto calc_addr; + } + + if (decode->displacement_size) { + ptr = sign(decode->displacement, decode->displacement_size); + } + + switch (decode->modrm.rm) { + case 0: + ptr += BX(env) + SI(env); + break; + case 1: + ptr += BX(env) + DI(env); + break; + case 2: + ptr += BP(env) + SI(env); + seg = R_SS; + break; + case 3: + ptr += BP(env) + DI(env); + seg = R_SS; + break; + case 4: + ptr += SI(env); + break; + case 5: + ptr += DI(env); + break; + case 6: + ptr += BP(env); + seg = R_SS; + break; + case 7: + ptr += BX(env); + break; + } +calc_addr: + if (X86_DECODE_CMD_LEA == decode->cmd) { + op->addr = (uint16_t)ptr; + } else { + op->addr = decode_linear_addr(env, decode, (uint16_t)ptr, seg); + } +} + +void *get_reg_ref(CPUX86State *env, int reg, int rex_present, + int is_extended, int size) +{ + void *ptr = NULL; + + if (is_extended) { + reg |= R_R8; + } + + switch (size) { + case 1: + if (is_extended || reg < 4 || rex_present) { + ptr = &RL(env, reg); + } else { + ptr = &RH(env, reg - 4); + } + break; + default: + ptr = &RRX(env, reg); + break; + } + return ptr; +} + +target_ulong get_reg_val(CPUX86State *env, int reg, int rex_present, + int is_extended, int size) +{ + target_ulong val = 0; + memcpy(&val, + get_reg_ref(env, reg, rex_present, is_extended, size), + size); + return val; +} + +static target_ulong get_sib_val(CPUX86State *env, struct x86_decode *decode, + X86Seg *sel) +{ + target_ulong base = 0; + target_ulong scaled_index = 0; + int addr_size = decode->addressing_size; + int base_reg = decode->sib.base; + int index_reg = decode->sib.index; + + *sel = R_DS; + + if (decode->modrm.mod || base_reg != R_EBP) { + if (decode->rex.b) { + base_reg |= R_R8; + } + if (base_reg == R_ESP || base_reg == R_EBP) { + *sel = R_SS; + } + base = get_reg_val(env, decode->sib.base, decode->rex.rex, + decode->rex.b, addr_size); + } + + if (decode->rex.x) { + index_reg |= R_R8; + } + + if (index_reg != R_ESP) { + scaled_index = get_reg_val(env, index_reg, decode->rex.rex, + decode->rex.x, addr_size) << + decode->sib.scale; + } + return base + scaled_index; +} + +void calc_modrm_operand32(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + X86Seg seg = R_DS; + target_ulong ptr = 0; + int addr_size = decode->addressing_size; + + if (decode->displacement_size) { + ptr = sign(decode->displacement, decode->displacement_size); + } + + if (4 == decode->modrm.rm) { + ptr += get_sib_val(env, decode, &seg); + } else if (!decode->modrm.mod && 5 == decode->modrm.rm) { + if (x86_is_long_mode(env_cpu(env))) { + ptr += env->eip + decode->len; + } else { + ptr = decode->displacement; + } + } else { + if (decode->modrm.rm == R_EBP || decode->modrm.rm == R_ESP) { + seg = R_SS; + } + ptr += get_reg_val(env, decode->modrm.rm, decode->rex.rex, + decode->rex.b, addr_size); + } + + if (X86_DECODE_CMD_LEA == decode->cmd) { + op->addr = (uint32_t)ptr; + } else { + op->addr = decode_linear_addr(env, decode, (uint32_t)ptr, seg); + } +} + +void calc_modrm_operand64(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + X86Seg seg = R_DS; + int32_t offset = 0; + int mod = decode->modrm.mod; + int rm = decode->modrm.rm; + target_ulong ptr; + int src = decode->modrm.rm; + + if (decode->displacement_size) { + offset = sign(decode->displacement, decode->displacement_size); + } + + if (4 == rm) { + ptr = get_sib_val(env, decode, &seg) + offset; + } else if (0 == mod && 5 == rm) { + ptr = env->eip + decode->len + (int32_t) offset; + } else { + ptr = get_reg_val(env, src, decode->rex.rex, decode->rex.b, 8) + + (int64_t) offset; + } + + if (X86_DECODE_CMD_LEA == decode->cmd) { + op->addr = ptr; + } else { + op->addr = decode_linear_addr(env, decode, ptr, seg); + } +} + + +void calc_modrm_operand(CPUX86State *env, struct x86_decode *decode, + struct x86_decode_op *op) +{ + if (3 == decode->modrm.mod) { + op->reg = decode->modrm.reg; + op->type = X86_VAR_REG; + op->regptr = get_reg_ref(env, decode->modrm.rm, decode->rex.rex, + decode->rex.b, decode->operand_size); + return; + } + + switch (decode->addressing_size) { + case 2: + calc_modrm_operand16(env, decode, op); + break; + case 4: + calc_modrm_operand32(env, decode, op); + break; + case 8: + calc_modrm_operand64(env, decode, op); + break; + default: + VM_PANIC_EX("unsupported address size %d\n", decode->addressing_size); + break; + } +} + +static void decode_prefix(CPUX86State *env, struct x86_decode *decode) +{ + while (1) { + /* + * REX prefix must come after legacy prefixes. + * REX before legacy is ignored. + * Clear rex to simulate this. + */ + uint8_t byte = decode_byte(env, decode); + switch (byte) { + case PREFIX_LOCK: + decode->lock = byte; + decode->rex.rex = 0; + break; + case PREFIX_REPN: + case PREFIX_REP: + decode->rep = byte; + decode->rex.rex = 0; + break; + case PREFIX_CS_SEG_OVERRIDE: + case PREFIX_SS_SEG_OVERRIDE: + case PREFIX_DS_SEG_OVERRIDE: + case PREFIX_ES_SEG_OVERRIDE: + case PREFIX_FS_SEG_OVERRIDE: + case PREFIX_GS_SEG_OVERRIDE: + decode->segment_override = byte; + decode->rex.rex = 0; + break; + case PREFIX_OP_SIZE_OVERRIDE: + decode->op_size_override = byte; + decode->rex.rex = 0; + break; + case PREFIX_ADDR_SIZE_OVERRIDE: + decode->addr_size_override = byte; + decode->rex.rex = 0; + break; + case PREFIX_REX ... (PREFIX_REX + 0xf): + if (x86_is_long_mode(env_cpu(env))) { + decode->rex.rex = byte; + break; + } + /* fall through when not in long mode */ + default: + decode->len--; + return; + } + } +} + +void set_addressing_size(CPUX86State *env, struct x86_decode *decode) +{ + decode->addressing_size = -1; + if (x86_is_real(env_cpu(env)) || x86_is_v8086(env_cpu(env))) { + if (decode->addr_size_override) { + decode->addressing_size = 4; + } else { + decode->addressing_size = 2; + } + } else if (!x86_is_long_mode(env_cpu(env))) { + /* protected */ + x86_segment_descriptor cs; + emul_ops->read_segment_descriptor(env_cpu(env), &cs, R_CS); + /* check db */ + if (cs.db) { + if (decode->addr_size_override) { + decode->addressing_size = 2; + } else { + decode->addressing_size = 4; + } + } else { + if (decode->addr_size_override) { + decode->addressing_size = 4; + } else { + decode->addressing_size = 2; + } + } + } else { + /* long */ + if (decode->addr_size_override) { + decode->addressing_size = 4; + } else { + decode->addressing_size = 8; + } + } +} + +void set_operand_size(CPUX86State *env, struct x86_decode *decode) +{ + decode->operand_size = -1; + if (x86_is_real(env_cpu(env)) || x86_is_v8086(env_cpu(env))) { + if (decode->op_size_override) { + decode->operand_size = 4; + } else { + decode->operand_size = 2; + } + } else if (!x86_is_long_mode(env_cpu(env))) { + /* protected */ + x86_segment_descriptor cs; + emul_ops->read_segment_descriptor(env_cpu(env), &cs, R_CS); + /* check db */ + if (cs.db) { + if (decode->op_size_override) { + decode->operand_size = 2; + } else{ + decode->operand_size = 4; + } + } else { + if (decode->op_size_override) { + decode->operand_size = 4; + } else { + decode->operand_size = 2; + } + } + } else { + /* long */ + if (decode->op_size_override) { + decode->operand_size = 2; + } else { + decode->operand_size = 4; + } + + if (decode->rex.w) { + decode->operand_size = 8; + } + } +} + +static void decode_sib(CPUX86State *env, struct x86_decode *decode) +{ + if ((decode->modrm.mod != 3) && (4 == decode->modrm.rm) && + (decode->addressing_size != 2)) { + decode->sib.sib = decode_byte(env, decode); + decode->sib_present = true; + } +} + +/* 16 bit modrm */ +int disp16_tbl[4][8] = { + {0, 0, 0, 0, 0, 0, 2, 0}, + {1, 1, 1, 1, 1, 1, 1, 1}, + {2, 2, 2, 2, 2, 2, 2, 2}, + {0, 0, 0, 0, 0, 0, 0, 0} +}; + +/* 32/64-bit modrm */ +int disp32_tbl[4][8] = { + {0, 0, 0, 0, -1, 4, 0, 0}, + {1, 1, 1, 1, 1, 1, 1, 1}, + {4, 4, 4, 4, 4, 4, 4, 4}, + {0, 0, 0, 0, 0, 0, 0, 0} +}; + +static inline void decode_displacement(CPUX86State *env, struct x86_decode *decode) +{ + int addressing_size = decode->addressing_size; + int mod = decode->modrm.mod; + int rm = decode->modrm.rm; + + decode->displacement_size = 0; + switch (addressing_size) { + case 2: + decode->displacement_size = disp16_tbl[mod][rm]; + if (decode->displacement_size) { + decode->displacement = (uint16_t)decode_bytes(env, decode, + decode->displacement_size); + } + break; + case 4: + case 8: + if (-1 == disp32_tbl[mod][rm]) { + if (5 == decode->sib.base) { + decode->displacement_size = 4; + } + } else { + decode->displacement_size = disp32_tbl[mod][rm]; + } + + if (decode->displacement_size) { + decode->displacement = (uint32_t)decode_bytes(env, decode, + decode->displacement_size); + } + break; + } +} + +static inline void decode_modrm(CPUX86State *env, struct x86_decode *decode) +{ + decode->modrm.modrm = decode_byte(env, decode); + decode->is_modrm = true; + + decode_sib(env, decode); + decode_displacement(env, decode); +} + +static inline void decode_opcode_general(CPUX86State *env, + struct x86_decode *decode, + uint8_t opcode, + struct decode_tbl *inst_decoder) +{ + decode->cmd = inst_decoder->cmd; + if (inst_decoder->operand_size) { + decode->operand_size = inst_decoder->operand_size; + } + + if (inst_decoder->is_modrm) { + decode_modrm(env, decode); + } + if (inst_decoder->decode_op1) { + inst_decoder->decode_op1(env, decode, &decode->op[0]); + } + if (inst_decoder->decode_op2) { + inst_decoder->decode_op2(env, decode, &decode->op[1]); + } + if (inst_decoder->decode_op3) { + inst_decoder->decode_op3(env, decode, &decode->op[2]); + } + if (inst_decoder->decode_op4) { + inst_decoder->decode_op4(env, decode, &decode->op[3]); + } + if (inst_decoder->decode_postfix) { + inst_decoder->decode_postfix(env, decode); + } +} + +static inline void decode_opcode_1(CPUX86State *env, struct x86_decode *decode, + uint8_t opcode) +{ + struct decode_tbl *inst_decoder = &_decode_tbl1[opcode]; + decode_opcode_general(env, decode, opcode, inst_decoder); +} + + +static inline void decode_opcode_2(CPUX86State *env, struct x86_decode *decode, + uint8_t opcode) +{ + struct decode_tbl *inst_decoder = &_decode_tbl2[opcode]; + decode_opcode_general(env, decode, opcode, inst_decoder); +} + +static void decode_opcodes(CPUX86State *env, struct x86_decode *decode) +{ + uint8_t opcode; + + opcode = decode_byte(env, decode); + decode->opcode[decode->opcode_len++] = opcode; + if (opcode != OPCODE_ESCAPE) { + decode_opcode_1(env, decode, opcode); + } else { + opcode = decode_byte(env, decode); + decode->opcode[decode->opcode_len++] = opcode; + decode_opcode_2(env, decode, opcode); + } +} + +uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode) +{ + memset(decode, 0, sizeof(*decode)); + decode_prefix(env, decode); + set_addressing_size(env, decode); + set_operand_size(env, decode); + + decode_opcodes(env, decode); + + return decode->len; +} + +void init_decoder(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(_decode_tbl1); i++) { + memcpy(&_decode_tbl1[i], &invl_inst, sizeof(invl_inst)); + } + for (i = 0; i < ARRAY_SIZE(_decode_tbl2); i++) { + memcpy(&_decode_tbl2[i], &invl_inst, sizeof(invl_inst)); + } + for (i = 0; i < ARRAY_SIZE(_decode_tbl3); i++) { + memcpy(&_decode_tbl3[i], &invl_inst_x87, sizeof(invl_inst_x87)); + + } + for (i = 0; i < ARRAY_SIZE(_1op_inst); i++) { + _decode_tbl1[_1op_inst[i].opcode] = _1op_inst[i]; + } + for (i = 0; i < ARRAY_SIZE(_2op_inst); i++) { + _decode_tbl2[_2op_inst[i].opcode] = _2op_inst[i]; + } + for (i = 0; i < ARRAY_SIZE(_x87_inst); i++) { + int index = ((_x87_inst[i].opcode & 0xf) << 4) | + ((_x87_inst[i].modrm_mod & 1) << 3) | + _x87_inst[i].modrm_reg; + _decode_tbl3[index] = _x87_inst[i]; + } +} + + +const char *decode_cmd_to_string(enum x86_decode_cmd cmd) +{ + static const char *cmds[] = {"INVL", "PUSH", "PUSH_SEG", "POP", "POP_SEG", + "MOV", "MOVSX", "MOVZX", "CALL_NEAR", "CALL_NEAR_ABS_INDIRECT", + "CALL_FAR_ABS_INDIRECT", "CMD_CALL_FAR", "RET_NEAR", "RET_FAR", "ADD", + "OR", "ADC", "SBB", "AND", "SUB", "XOR", "CMP", "INC", "DEC", "TST", + "NOT", "NEG", "JMP_NEAR", "JMP_NEAR_ABS_INDIRECT", "JMP_FAR", + "JMP_FAR_ABS_INDIRECT", "LEA", "JXX", "JCXZ", "SETXX", "MOV_TO_SEG", + "MOV_FROM_SEG", "CLI", "STI", "CLD", "STD", "STC", "CLC", "OUT", "IN", + "INS", "OUTS", "LIDT", "SIDT", "LGDT", "SGDT", "SMSW", "LMSW", + "RDTSCP", "INVLPG", "MOV_TO_CR", "MOV_FROM_CR", "MOV_TO_DR", + "MOV_FROM_DR", "PUSHF", "POPF", "CPUID", "ROL", "ROR", "RCL", "RCR", + "SHL", "SAL", "SHR", "SHRD", "SHLD", "SAR", "DIV", "IDIV", "MUL", + "IMUL_3", "IMUL_2", "IMUL_1", "MOVS", "CMPS", "SCAS", "LODS", "STOS", + "BSWAP", "XCHG", "RDTSC", "RDMSR", "WRMSR", "ENTER", "LEAVE", "BT", + "BTS", "BTC", "BTR", "BSF", "BSR", "IRET", "INT", "POPA", "PUSHA", + "CWD", "CBW", "DAS", "AAD", "AAM", "AAS", "LOOP", "SLDT", "STR", "LLDT", + "LTR", "VERR", "VERW", "SAHF", "LAHF", "WBINVD", "LDS", "LSS", "LES", + "LGS", "LFS", "CMC", "XLAT", "NOP", "CMOV", "CLTS", "XADD", "HLT", + "CMPXCHG8B", "CMPXCHG", "POPCNT", "FNINIT", "FLD", "FLDxx", "FNSTCW", + "FNSTSW", "FNSETPM", "FSAVE", "FRSTOR", "FXSAVE", "FXRSTOR", "FDIV", + "FMUL", "FSUB", "FADD", "EMMS", "MFENCE", "SFENCE", "LFENCE", + "PREFETCH", "FST", "FABS", "FUCOM", "FUCOMI", "FLDCW", + "FXCH", "FCHS", "FCMOV", "FRNDINT", "FXAM", "LAST"}; + return cmds[cmd]; +} + +target_ulong decode_linear_addr(CPUX86State *env, struct x86_decode *decode, + target_ulong addr, X86Seg seg) +{ + switch (decode->segment_override) { + case PREFIX_CS_SEG_OVERRIDE: + seg = R_CS; + break; + case PREFIX_SS_SEG_OVERRIDE: + seg = R_SS; + break; + case PREFIX_DS_SEG_OVERRIDE: + seg = R_DS; + break; + case PREFIX_ES_SEG_OVERRIDE: + seg = R_ES; + break; + case PREFIX_FS_SEG_OVERRIDE: + seg = R_FS; + break; + case PREFIX_GS_SEG_OVERRIDE: + seg = R_GS; + break; + default: + break; + } + return linear_addr_size(env_cpu(env), addr, decode->addressing_size, seg); +} diff --git a/target/i386/hvf/x86_decode.h b/target/i386/emulate/x86_decode.h similarity index 97% rename from target/i386/hvf/x86_decode.h rename to target/i386/emulate/x86_decode.h index a2d7a2a27b6..927645af1a3 100644 --- a/target/i386/hvf/x86_decode.h +++ b/target/i386/emulate/x86_decode.h @@ -15,8 +15,8 @@ * License along with this program; if not, see . */ -#ifndef HVF_X86_DECODE_H -#define HVF_X86_DECODE_H +#ifndef X86_EMU_DECODE_H +#define X86_EMU_DECODE_H #include "cpu.h" #include "x86.h" @@ -266,7 +266,10 @@ typedef struct x86_decode_op { int reg; target_ulong val; - target_ulong ptr; + union { + target_ulong addr; + void *regptr; + }; } x86_decode_op; typedef struct x86_decode { @@ -295,16 +298,14 @@ typedef struct x86_decode { struct x86_modrm modrm; struct x86_decode_op op[4]; bool is_fpu; - uint32_t flags_mask; - } x86_decode; uint64_t sign(uint64_t val, int size); uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode); -target_ulong get_reg_ref(CPUX86State *env, int reg, int rex_present, - int is_extended, int size); +void *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); void calc_modrm_operand(CPUX86State *env, struct x86_decode *decode, diff --git a/target/i386/emulate/x86_emu.c b/target/i386/emulate/x86_emu.c new file mode 100644 index 00000000000..4890e0a4e5e --- /dev/null +++ b/target/i386/emulate/x86_emu.c @@ -0,0 +1,1264 @@ +/* + * Copyright (C) 2016 Veertu Inc, + * Copyright (C) 2017 Google Inc, + * + * This program 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 program is distributed in the hope that 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 program; if not, see . + */ + +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2001-2012 The Bochs Project +// +// 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., 51 Franklin St, Fifth Floor, Boston, MA B 02110-1301 USA +///////////////////////////////////////////////////////////////////////// + +#include "qemu/osdep.h" +#include "panic.h" +#include "x86_decode.h" +#include "x86.h" +#include "x86_emu.h" +#include "x86_flags.h" + +#define EXEC_2OP_FLAGS_CMD(env, decode, cmd, FLAGS_FUNC, save_res) \ +{ \ + fetch_operands(env, decode, 2, true, true, false); \ + switch (decode->operand_size) { \ + case 1: \ + { \ + uint8_t v1 = (uint8_t)decode->op[0].val; \ + uint8_t v2 = (uint8_t)decode->op[1].val; \ + uint8_t diff = v1 cmd v2; \ + if (save_res) { \ + write_val_ext(env, &decode->op[0], diff, 1); \ + } \ + FLAGS_FUNC##8(env, v1, v2, diff); \ + break; \ + } \ + case 2: \ + { \ + uint16_t v1 = (uint16_t)decode->op[0].val; \ + uint16_t v2 = (uint16_t)decode->op[1].val; \ + uint16_t diff = v1 cmd v2; \ + if (save_res) { \ + write_val_ext(env, &decode->op[0], diff, 2); \ + } \ + FLAGS_FUNC##16(env, v1, v2, diff); \ + break; \ + } \ + case 4: \ + { \ + uint32_t v1 = (uint32_t)decode->op[0].val; \ + uint32_t v2 = (uint32_t)decode->op[1].val; \ + uint32_t diff = v1 cmd v2; \ + if (save_res) { \ + write_val_ext(env, &decode->op[0], diff, 4); \ + } \ + FLAGS_FUNC##32(env, v1, v2, diff); \ + break; \ + } \ + default: \ + VM_PANIC("bad size\n"); \ + } \ +} \ + +target_ulong read_reg(CPUX86State *env, int reg, int size) +{ + switch (size) { + case 1: + return x86_reg(env, reg)->lx; + case 2: + return x86_reg(env, reg)->rx; + case 4: + return x86_reg(env, reg)->erx; + case 8: + return x86_reg(env, reg)->rrx; + default: + abort(); + } + return 0; +} + +void write_reg(CPUX86State *env, int reg, target_ulong val, int size) +{ + switch (size) { + case 1: + x86_reg(env, reg)->lx = val; + break; + case 2: + x86_reg(env, reg)->rx = val; + break; + case 4: + x86_reg(env, reg)->rrx = (uint32_t)val; + break; + case 8: + x86_reg(env, reg)->rrx = val; + break; + default: + abort(); + } +} + +target_ulong read_val_from_reg(void *reg_ptr, int size) +{ + target_ulong val; + + switch (size) { + case 1: + val = *(uint8_t *)reg_ptr; + break; + case 2: + val = *(uint16_t *)reg_ptr; + break; + case 4: + val = *(uint32_t *)reg_ptr; + break; + case 8: + val = *(uint64_t *)reg_ptr; + break; + default: + abort(); + } + return val; +} + +void write_val_to_reg(void *reg_ptr, target_ulong val, int size) +{ + switch (size) { + case 1: + *(uint8_t *)reg_ptr = val; + break; + case 2: + *(uint16_t *)reg_ptr = val; + break; + case 4: + *(uint64_t *)reg_ptr = (uint32_t)val; + break; + case 8: + *(uint64_t *)reg_ptr = val; + break; + default: + abort(); + } +} + +static void write_val_to_mem(CPUX86State *env, target_ulong ptr, target_ulong val, int size) +{ + emul_ops->write_mem(env_cpu(env), &val, ptr, size); +} + +void write_val_ext(CPUX86State *env, struct x86_decode_op *decode, target_ulong val, int size) +{ + if (decode->type == X86_VAR_REG) { + write_val_to_reg(decode->regptr, val, size); + } else { + write_val_to_mem(env, decode->addr, val, size); + } +} + +uint8_t *read_mmio(CPUX86State *env, target_ulong ptr, int bytes) +{ + emul_ops->read_mem(env_cpu(env), env->emu_mmio_buf, ptr, bytes); + return env->emu_mmio_buf; +} + + +static target_ulong read_val_from_mem(CPUX86State *env, target_long ptr, int size) +{ + target_ulong val; + uint8_t *mmio_ptr; + + mmio_ptr = read_mmio(env, ptr, size); + switch (size) { + case 1: + val = *(uint8_t *)mmio_ptr; + break; + case 2: + val = *(uint16_t *)mmio_ptr; + break; + case 4: + val = *(uint32_t *)mmio_ptr; + break; + case 8: + val = *(uint64_t *)mmio_ptr; + break; + default: + VM_PANIC("bad size\n"); + break; + } + return val; +} + +target_ulong read_val_ext(CPUX86State *env, struct x86_decode_op *decode, int size) +{ + if (decode->type == X86_VAR_REG) { + return read_val_from_reg(decode->regptr, size); + } else { + return read_val_from_mem(env, decode->addr, size); + } +} + +static void fetch_operands(CPUX86State *env, struct x86_decode *decode, + int n, bool val_op0, bool val_op1, bool val_op2) +{ + int i; + bool calc_val[3] = {val_op0, val_op1, val_op2}; + + for (i = 0; i < n; i++) { + switch (decode->op[i].type) { + case X86_VAR_IMMEDIATE: + break; + case X86_VAR_REG: + VM_PANIC_ON(!decode->op[i].regptr); + if (calc_val[i]) { + decode->op[i].val = read_val_from_reg(decode->op[i].regptr, + decode->operand_size); + } + break; + case X86_VAR_RM: + calc_modrm_operand(env, decode, &decode->op[i]); + if (calc_val[i]) { + decode->op[i].val = read_val_ext(env, &decode->op[i], + decode->operand_size); + } + break; + case X86_VAR_OFFSET: + decode->op[i].addr = decode_linear_addr(env, decode, + decode->op[i].addr, + R_DS); + if (calc_val[i]) { + decode->op[i].val = read_val_ext(env, &decode->op[i], + decode->operand_size); + } + break; + default: + break; + } + } +} + +static void exec_mov(CPUX86State *env, struct x86_decode *decode) +{ + fetch_operands(env, decode, 2, false, true, false); + write_val_ext(env, &decode->op[0], decode->op[1].val, + decode->operand_size); + + env->eip += decode->len; +} + +static void exec_add(CPUX86State *env, struct x86_decode *decode) +{ + EXEC_2OP_FLAGS_CMD(env, decode, +, SET_FLAGS_OSZAPC_ADD, true); + env->eip += decode->len; +} + +static void exec_or(CPUX86State *env, struct x86_decode *decode) +{ + EXEC_2OP_FLAGS_CMD(env, decode, |, SET_FLAGS_OSZAPC_LOGIC, true); + env->eip += decode->len; +} + +static void exec_adc(CPUX86State *env, struct x86_decode *decode) +{ + EXEC_2OP_FLAGS_CMD(env, decode, +get_CF(env)+, SET_FLAGS_OSZAPC_ADD, true); + env->eip += decode->len; +} + +static void exec_sbb(CPUX86State *env, struct x86_decode *decode) +{ + EXEC_2OP_FLAGS_CMD(env, decode, -get_CF(env)-, SET_FLAGS_OSZAPC_SUB, true); + env->eip += decode->len; +} + +static void exec_and(CPUX86State *env, struct x86_decode *decode) +{ + EXEC_2OP_FLAGS_CMD(env, decode, &, SET_FLAGS_OSZAPC_LOGIC, true); + env->eip += decode->len; +} + +static void exec_sub(CPUX86State *env, struct x86_decode *decode) +{ + EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, true); + env->eip += decode->len; +} + +static void exec_xor(CPUX86State *env, struct x86_decode *decode) +{ + EXEC_2OP_FLAGS_CMD(env, decode, ^, SET_FLAGS_OSZAPC_LOGIC, true); + env->eip += decode->len; +} + +static void exec_neg(CPUX86State *env, struct x86_decode *decode) +{ + /*EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, false);*/ + int32_t val; + fetch_operands(env, decode, 2, true, true, false); + + val = 0 - sign(decode->op[1].val, decode->operand_size); + write_val_ext(env, &decode->op[1], val, decode->operand_size); + + if (4 == decode->operand_size) { + SET_FLAGS_OSZAPC_SUB32(env, 0, 0 - val, val); + } else if (2 == decode->operand_size) { + SET_FLAGS_OSZAPC_SUB16(env, 0, 0 - val, val); + } else if (1 == decode->operand_size) { + SET_FLAGS_OSZAPC_SUB8(env, 0, 0 - val, val); + } else { + VM_PANIC("bad op size\n"); + } + + /*lflags_to_rflags(env);*/ + env->eip += decode->len; +} + +static void exec_cmp(CPUX86State *env, struct x86_decode *decode) +{ + EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, false); + env->eip += decode->len; +} + +static void exec_inc(CPUX86State *env, struct x86_decode *decode) +{ + decode->op[1].type = X86_VAR_IMMEDIATE; + decode->op[1].val = 0; + + EXEC_2OP_FLAGS_CMD(env, decode, +1+, SET_FLAGS_OSZAP_ADD, true); + + env->eip += decode->len; +} + +static void exec_dec(CPUX86State *env, struct x86_decode *decode) +{ + decode->op[1].type = X86_VAR_IMMEDIATE; + decode->op[1].val = 0; + + EXEC_2OP_FLAGS_CMD(env, decode, -1-, SET_FLAGS_OSZAP_SUB, true); + env->eip += decode->len; +} + +static void exec_tst(CPUX86State *env, struct x86_decode *decode) +{ + EXEC_2OP_FLAGS_CMD(env, decode, &, SET_FLAGS_OSZAPC_LOGIC, false); + env->eip += decode->len; +} + +static void exec_not(CPUX86State *env, struct x86_decode *decode) +{ + fetch_operands(env, decode, 1, true, false, false); + + write_val_ext(env, &decode->op[0], ~decode->op[0].val, + decode->operand_size); + env->eip += decode->len; +} + +void exec_movzx(CPUX86State *env, struct x86_decode *decode) +{ + int src_op_size; + int op_size = decode->operand_size; + + fetch_operands(env, decode, 1, false, false, false); + + if (0xb6 == decode->opcode[1]) { + src_op_size = 1; + } else { + src_op_size = 2; + } + decode->operand_size = src_op_size; + calc_modrm_operand(env, decode, &decode->op[1]); + decode->op[1].val = read_val_ext(env, &decode->op[1], src_op_size); + write_val_ext(env, &decode->op[0], decode->op[1].val, op_size); + + env->eip += decode->len; +} + +static void exec_out(CPUX86State *env, struct x86_decode *decode) +{ + switch (decode->opcode[0]) { + case 0xe6: + emul_ops->handle_io(env_cpu(env), decode->op[0].val, &AL(env), 1, 1, 1); + break; + case 0xe7: + emul_ops->handle_io(env_cpu(env), decode->op[0].val, &RAX(env), 1, + decode->operand_size, 1); + break; + case 0xee: + emul_ops->handle_io(env_cpu(env), DX(env), &AL(env), 1, 1, 1); + break; + case 0xef: + emul_ops->handle_io(env_cpu(env), DX(env), &RAX(env), 1, + decode->operand_size, 1); + break; + default: + VM_PANIC("Bad out opcode\n"); + break; + } + env->eip += decode->len; +} + +static void exec_in(CPUX86State *env, struct x86_decode *decode) +{ + target_ulong val = 0; + switch (decode->opcode[0]) { + case 0xe4: + emul_ops->handle_io(env_cpu(env), decode->op[0].val, &AL(env), 0, 1, 1); + break; + case 0xe5: + emul_ops->handle_io(env_cpu(env), decode->op[0].val, &val, 0, + decode->operand_size, 1); + if (decode->operand_size == 2) { + AX(env) = val; + } else { + RAX(env) = (uint32_t)val; + } + break; + case 0xec: + emul_ops->handle_io(env_cpu(env), DX(env), &AL(env), 0, 1, 1); + break; + case 0xed: + emul_ops->handle_io(env_cpu(env), DX(env), &val, 0, + decode->operand_size, 1); + if (decode->operand_size == 2) { + AX(env) = val; + } else { + RAX(env) = (uint32_t)val; + } + + break; + default: + VM_PANIC("Bad in opcode\n"); + break; + } + + env->eip += decode->len; +} + +static inline void string_increment_reg(CPUX86State *env, int reg, + struct x86_decode *decode) +{ + target_ulong val = read_reg(env, reg, decode->addressing_size); + if (env->eflags & DF_MASK) { + val -= decode->operand_size; + } else { + val += decode->operand_size; + } + write_reg(env, reg, val, decode->addressing_size); +} + +static inline void string_rep(CPUX86State *env, struct x86_decode *decode, + void (*func)(CPUX86State *env, + struct x86_decode *ins), int rep) +{ + target_ulong rcx = read_reg(env, R_ECX, decode->addressing_size); + while (rcx--) { + func(env, decode); + write_reg(env, R_ECX, rcx, decode->addressing_size); + if ((PREFIX_REP == rep) && !env->cc_dst) { + break; + } + if ((PREFIX_REPN == rep) && env->cc_dst) { + break; + } + } +} + +static void exec_ins_single(CPUX86State *env, struct x86_decode *decode) +{ + target_ulong addr = linear_addr_size(env_cpu(env), RDI(env), + decode->addressing_size, R_ES); + + emul_ops->handle_io(env_cpu(env), DX(env), env->emu_mmio_buf, 0, + decode->operand_size, 1); + emul_ops->write_mem(env_cpu(env), env->emu_mmio_buf, addr, + decode->operand_size); + + string_increment_reg(env, R_EDI, decode); +} + +static void exec_ins(CPUX86State *env, struct x86_decode *decode) +{ + if (decode->rep) { + string_rep(env, decode, exec_ins_single, 0); + } else { + exec_ins_single(env, decode); + } + + env->eip += decode->len; +} + +static void exec_outs_single(CPUX86State *env, struct x86_decode *decode) +{ + target_ulong addr = decode_linear_addr(env, decode, RSI(env), R_DS); + + emul_ops->read_mem(env_cpu(env), env->emu_mmio_buf, addr, + decode->operand_size); + emul_ops->handle_io(env_cpu(env), DX(env), env->emu_mmio_buf, 1, + decode->operand_size, 1); + + string_increment_reg(env, R_ESI, decode); +} + +static void exec_outs(CPUX86State *env, struct x86_decode *decode) +{ + if (decode->rep) { + string_rep(env, decode, exec_outs_single, 0); + } else { + exec_outs_single(env, decode); + } + + env->eip += decode->len; +} + +static void exec_movs_single(CPUX86State *env, struct x86_decode *decode) +{ + target_ulong src_addr; + target_ulong dst_addr; + target_ulong val; + + src_addr = decode_linear_addr(env, decode, RSI(env), R_DS); + dst_addr = linear_addr_size(env_cpu(env), RDI(env), + decode->addressing_size, R_ES); + + val = read_val_from_mem(env, src_addr, decode->operand_size); + write_val_to_mem(env, dst_addr, val, decode->operand_size); + + string_increment_reg(env, R_ESI, decode); + string_increment_reg(env, R_EDI, decode); +} + +static void exec_movs(CPUX86State *env, struct x86_decode *decode) +{ + if (decode->rep) { + string_rep(env, decode, exec_movs_single, 0); + } else { + exec_movs_single(env, decode); + } + + env->eip += decode->len; +} + +static void exec_cmps_single(CPUX86State *env, struct x86_decode *decode) +{ + target_ulong src_addr; + target_ulong dst_addr; + + src_addr = decode_linear_addr(env, decode, RSI(env), R_DS); + dst_addr = linear_addr_size(env_cpu(env), RDI(env), + decode->addressing_size, R_ES); + + decode->op[0].type = X86_VAR_IMMEDIATE; + decode->op[0].val = read_val_from_mem(env, src_addr, decode->operand_size); + decode->op[1].type = X86_VAR_IMMEDIATE; + decode->op[1].val = read_val_from_mem(env, dst_addr, decode->operand_size); + + EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, false); + + string_increment_reg(env, R_ESI, decode); + string_increment_reg(env, R_EDI, decode); +} + +static void exec_cmps(CPUX86State *env, struct x86_decode *decode) +{ + if (decode->rep) { + string_rep(env, decode, exec_cmps_single, decode->rep); + } else { + exec_cmps_single(env, decode); + } + env->eip += decode->len; +} + + +static void exec_stos_single(CPUX86State *env, struct x86_decode *decode) +{ + target_ulong addr; + target_ulong val; + + addr = linear_addr_size(env_cpu(env), RDI(env), + decode->addressing_size, R_ES); + val = read_reg(env, R_EAX, decode->operand_size); + emul_ops->write_mem(env_cpu(env), &val, addr, decode->operand_size); + + string_increment_reg(env, R_EDI, decode); +} + + +static void exec_stos(CPUX86State *env, struct x86_decode *decode) +{ + if (decode->rep) { + string_rep(env, decode, exec_stos_single, 0); + } else { + exec_stos_single(env, decode); + } + + env->eip += decode->len; +} + +static void exec_scas_single(CPUX86State *env, struct x86_decode *decode) +{ + target_ulong addr; + + addr = linear_addr_size(env_cpu(env), RDI(env), + decode->addressing_size, R_ES); + decode->op[1].type = X86_VAR_IMMEDIATE; + emul_ops->read_mem(env_cpu(env), &decode->op[1].val, addr, decode->operand_size); + + EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, false); + string_increment_reg(env, R_EDI, decode); +} + +static void exec_scas(CPUX86State *env, struct x86_decode *decode) +{ + decode->op[0].type = X86_VAR_REG; + decode->op[0].reg = R_EAX; + if (decode->rep) { + string_rep(env, decode, exec_scas_single, decode->rep); + } else { + exec_scas_single(env, decode); + } + + env->eip += decode->len; +} + +static void exec_lods_single(CPUX86State *env, struct x86_decode *decode) +{ + target_ulong addr; + target_ulong val = 0; + + addr = decode_linear_addr(env, decode, RSI(env), R_DS); + emul_ops->read_mem(env_cpu(env), &val, addr, decode->operand_size); + write_reg(env, R_EAX, val, decode->operand_size); + + string_increment_reg(env, R_ESI, decode); +} + +static void exec_lods(CPUX86State *env, struct x86_decode *decode) +{ + if (decode->rep) { + string_rep(env, decode, exec_lods_single, 0); + } else { + exec_lods_single(env, decode); + } + + env->eip += decode->len; +} + +void x86_emul_raise_exception(CPUX86State *env, int exception_index, int error_code) +{ + env->exception_nr = exception_index; + env->error_code = error_code; + env->has_error_code = true; + env->exception_injected = 1; +} + +static void exec_rdmsr(CPUX86State *env, struct x86_decode *decode) +{ + emul_ops->simulate_rdmsr(env_cpu(env)); + env->eip += decode->len; +} + +static void exec_wrmsr(CPUX86State *env, struct x86_decode *decode) +{ + emul_ops->simulate_wrmsr(env_cpu(env)); + env->eip += decode->len; +} + +/* + * flag: + * 0 - bt, 1 - btc, 2 - bts, 3 - btr + */ +static void do_bt(CPUX86State *env, struct x86_decode *decode, int flag) +{ + int32_t displacement; + uint8_t index; + bool cf; + int mask = (4 == decode->operand_size) ? 0x1f : 0xf; + + VM_PANIC_ON(decode->rex.rex); + + fetch_operands(env, decode, 2, false, true, false); + index = decode->op[1].val & mask; + + if (decode->op[0].type != X86_VAR_REG) { + if (4 == decode->operand_size) { + displacement = ((int32_t) (decode->op[1].val & 0xffffffe0)) / 32; + decode->op[0].addr += 4 * displacement; + } else if (2 == decode->operand_size) { + displacement = ((int16_t) (decode->op[1].val & 0xfff0)) / 16; + decode->op[0].addr += 2 * displacement; + } else { + VM_PANIC("bt 64bit\n"); + } + } + decode->op[0].val = read_val_ext(env, &decode->op[0], + decode->operand_size); + cf = (decode->op[0].val >> index) & 0x01; + + switch (flag) { + case 0: + set_CF(env, cf); + return; + case 1: + decode->op[0].val ^= (1u << index); + break; + case 2: + decode->op[0].val |= (1u << index); + break; + case 3: + decode->op[0].val &= ~(1u << index); + break; + } + write_val_ext(env, &decode->op[0], decode->op[0].val, + decode->operand_size); + set_CF(env, cf); +} + +static void exec_bt(CPUX86State *env, struct x86_decode *decode) +{ + do_bt(env, decode, 0); + env->eip += decode->len; +} + +static void exec_btc(CPUX86State *env, struct x86_decode *decode) +{ + do_bt(env, decode, 1); + env->eip += decode->len; +} + +static void exec_btr(CPUX86State *env, struct x86_decode *decode) +{ + do_bt(env, decode, 3); + env->eip += decode->len; +} + +static void exec_bts(CPUX86State *env, struct x86_decode *decode) +{ + do_bt(env, decode, 2); + env->eip += decode->len; +} + +void exec_shl(CPUX86State *env, struct x86_decode *decode) +{ + uint8_t count; + int of = 0, cf = 0; + + fetch_operands(env, decode, 2, true, true, false); + + count = decode->op[1].val; + count &= 0x1f; /* count is masked to 5 bits*/ + if (!count) { + goto exit; + } + + switch (decode->operand_size) { + case 1: + { + uint8_t res = 0; + if (count <= 8) { + res = (decode->op[0].val << count); + cf = (decode->op[0].val >> (8 - count)) & 0x1; + of = cf ^ (res >> 7); + } + + write_val_ext(env, &decode->op[0], res, 1); + SET_FLAGS_OSZAPC_LOGIC8(env, 0, 0, res); + SET_FLAGS_OxxxxC(env, of, cf); + break; + } + case 2: + { + uint16_t res = 0; + + /* from bochs */ + if (count <= 16) { + res = (decode->op[0].val << count); + cf = (decode->op[0].val >> (16 - count)) & 0x1; + of = cf ^ (res >> 15); /* of = cf ^ result15 */ + } + + write_val_ext(env, &decode->op[0], res, 2); + SET_FLAGS_OSZAPC_LOGIC16(env, 0, 0, res); + SET_FLAGS_OxxxxC(env, of, cf); + break; + } + case 4: + { + uint32_t res = decode->op[0].val << count; + + write_val_ext(env, &decode->op[0], res, 4); + SET_FLAGS_OSZAPC_LOGIC32(env, 0, 0, res); + cf = (decode->op[0].val >> (32 - count)) & 0x1; + of = cf ^ (res >> 31); /* of = cf ^ result31 */ + SET_FLAGS_OxxxxC(env, of, cf); + break; + } + default: + abort(); + } + +exit: + /* lflags_to_rflags(env); */ + env->eip += decode->len; +} + +void exec_movsx(CPUX86State *env, struct x86_decode *decode) +{ + int src_op_size; + int op_size = decode->operand_size; + + fetch_operands(env, decode, 2, false, false, false); + + if (0xbe == decode->opcode[1]) { + src_op_size = 1; + } else { + src_op_size = 2; + } + + decode->operand_size = src_op_size; + calc_modrm_operand(env, decode, &decode->op[1]); + decode->op[1].val = sign(read_val_ext(env, &decode->op[1], src_op_size), + src_op_size); + + write_val_ext(env, &decode->op[0], decode->op[1].val, op_size); + + env->eip += decode->len; +} + +void exec_ror(CPUX86State *env, struct x86_decode *decode) +{ + uint8_t count; + + fetch_operands(env, decode, 2, true, true, false); + count = decode->op[1].val; + + switch (decode->operand_size) { + case 1: + { + uint32_t bit6, bit7; + uint8_t res; + + if ((count & 0x07) == 0) { + if (count & 0x18) { + bit6 = ((uint8_t)decode->op[0].val >> 6) & 1; + bit7 = ((uint8_t)decode->op[0].val >> 7) & 1; + SET_FLAGS_OxxxxC(env, bit6 ^ bit7, bit7); + } + } else { + count &= 0x7; /* use only bottom 3 bits */ + res = ((uint8_t)decode->op[0].val >> count) | + ((uint8_t)decode->op[0].val << (8 - count)); + write_val_ext(env, &decode->op[0], res, 1); + bit6 = (res >> 6) & 1; + bit7 = (res >> 7) & 1; + /* set eflags: ROR count affects the following flags: C, O */ + SET_FLAGS_OxxxxC(env, bit6 ^ bit7, bit7); + } + break; + } + case 2: + { + uint32_t bit14, bit15; + uint16_t res; + + if ((count & 0x0f) == 0) { + if (count & 0x10) { + bit14 = ((uint16_t)decode->op[0].val >> 14) & 1; + bit15 = ((uint16_t)decode->op[0].val >> 15) & 1; + /* of = result14 ^ result15 */ + SET_FLAGS_OxxxxC(env, bit14 ^ bit15, bit15); + } + } else { + count &= 0x0f; /* use only 4 LSB's */ + res = ((uint16_t)decode->op[0].val >> count) | + ((uint16_t)decode->op[0].val << (16 - count)); + write_val_ext(env, &decode->op[0], res, 2); + + bit14 = (res >> 14) & 1; + bit15 = (res >> 15) & 1; + /* of = result14 ^ result15 */ + SET_FLAGS_OxxxxC(env, bit14 ^ bit15, bit15); + } + break; + } + case 4: + { + uint32_t bit31, bit30; + uint32_t res; + + count &= 0x1f; + if (count) { + res = ((uint32_t)decode->op[0].val >> count) | + ((uint32_t)decode->op[0].val << (32 - count)); + write_val_ext(env, &decode->op[0], res, 4); + + bit31 = (res >> 31) & 1; + bit30 = (res >> 30) & 1; + /* of = result30 ^ result31 */ + SET_FLAGS_OxxxxC(env, bit30 ^ bit31, bit31); + } + break; + } + } + env->eip += decode->len; +} + +void exec_rol(CPUX86State *env, struct x86_decode *decode) +{ + uint8_t count; + + fetch_operands(env, decode, 2, true, true, false); + count = decode->op[1].val; + + switch (decode->operand_size) { + case 1: + { + uint32_t bit0, bit7; + uint8_t res; + + if ((count & 0x07) == 0) { + if (count & 0x18) { + bit0 = ((uint8_t)decode->op[0].val & 1); + bit7 = ((uint8_t)decode->op[0].val >> 7); + SET_FLAGS_OxxxxC(env, bit0 ^ bit7, bit0); + } + } else { + count &= 0x7; /* use only lowest 3 bits */ + res = ((uint8_t)decode->op[0].val << count) | + ((uint8_t)decode->op[0].val >> (8 - count)); + + write_val_ext(env, &decode->op[0], res, 1); + /* set eflags: + * ROL count affects the following flags: C, O + */ + bit0 = (res & 1); + bit7 = (res >> 7); + SET_FLAGS_OxxxxC(env, bit0 ^ bit7, bit0); + } + break; + } + case 2: + { + uint32_t bit0, bit15; + uint16_t res; + + if ((count & 0x0f) == 0) { + if (count & 0x10) { + bit0 = ((uint16_t)decode->op[0].val & 0x1); + bit15 = ((uint16_t)decode->op[0].val >> 15); + /* of = cf ^ result15 */ + SET_FLAGS_OxxxxC(env, bit0 ^ bit15, bit0); + } + } else { + count &= 0x0f; /* only use bottom 4 bits */ + res = ((uint16_t)decode->op[0].val << count) | + ((uint16_t)decode->op[0].val >> (16 - count)); + + write_val_ext(env, &decode->op[0], res, 2); + bit0 = (res & 0x1); + bit15 = (res >> 15); + /* of = cf ^ result15 */ + SET_FLAGS_OxxxxC(env, bit0 ^ bit15, bit0); + } + break; + } + case 4: + { + uint32_t bit0, bit31; + uint32_t res; + + count &= 0x1f; + if (count) { + res = ((uint32_t)decode->op[0].val << count) | + ((uint32_t)decode->op[0].val >> (32 - count)); + + write_val_ext(env, &decode->op[0], res, 4); + bit0 = (res & 0x1); + bit31 = (res >> 31); + /* of = cf ^ result31 */ + SET_FLAGS_OxxxxC(env, bit0 ^ bit31, bit0); + } + break; + } + } + env->eip += decode->len; +} + + +void exec_rcl(CPUX86State *env, struct x86_decode *decode) +{ + uint8_t count; + int of = 0, cf = 0; + + fetch_operands(env, decode, 2, true, true, false); + count = decode->op[1].val & 0x1f; + + switch (decode->operand_size) { + case 1: + { + uint8_t op1_8 = decode->op[0].val; + uint8_t res; + count %= 9; + if (!count) { + break; + } + + if (1 == count) { + res = (op1_8 << 1) | get_CF(env); + } else { + res = (op1_8 << count) | (get_CF(env) << (count - 1)) | + (op1_8 >> (9 - count)); + } + + write_val_ext(env, &decode->op[0], res, 1); + + cf = (op1_8 >> (8 - count)) & 0x01; + of = cf ^ (res >> 7); /* of = cf ^ result7 */ + SET_FLAGS_OxxxxC(env, of, cf); + break; + } + case 2: + { + uint16_t res; + uint16_t op1_16 = decode->op[0].val; + + count %= 17; + if (!count) { + break; + } + + if (1 == count) { + res = (op1_16 << 1) | get_CF(env); + } else if (count == 16) { + res = (get_CF(env) << 15) | (op1_16 >> 1); + } else { /* 2..15 */ + res = (op1_16 << count) | (get_CF(env) << (count - 1)) | + (op1_16 >> (17 - count)); + } + + write_val_ext(env, &decode->op[0], res, 2); + + cf = (op1_16 >> (16 - count)) & 0x1; + of = cf ^ (res >> 15); /* of = cf ^ result15 */ + SET_FLAGS_OxxxxC(env, of, cf); + break; + } + case 4: + { + uint32_t res; + uint32_t op1_32 = decode->op[0].val; + + if (!count) { + break; + } + + if (1 == count) { + res = (op1_32 << 1) | get_CF(env); + } else { + res = (op1_32 << count) | (get_CF(env) << (count - 1)) | + (op1_32 >> (33 - count)); + } + + write_val_ext(env, &decode->op[0], res, 4); + + cf = (op1_32 >> (32 - count)) & 0x1; + of = cf ^ (res >> 31); /* of = cf ^ result31 */ + SET_FLAGS_OxxxxC(env, of, cf); + break; + } + } + env->eip += decode->len; +} + +void exec_rcr(CPUX86State *env, struct x86_decode *decode) +{ + uint8_t count; + int of = 0, cf = 0; + + fetch_operands(env, decode, 2, true, true, false); + count = decode->op[1].val & 0x1f; + + switch (decode->operand_size) { + case 1: + { + uint8_t op1_8 = decode->op[0].val; + uint8_t res; + + count %= 9; + if (!count) { + break; + } + res = (op1_8 >> count) | (get_CF(env) << (8 - count)) | + (op1_8 << (9 - count)); + + write_val_ext(env, &decode->op[0], res, 1); + + cf = (op1_8 >> (count - 1)) & 0x1; + of = (((res << 1) ^ res) >> 7) & 0x1; /* of = result6 ^ result7 */ + SET_FLAGS_OxxxxC(env, of, cf); + break; + } + case 2: + { + uint16_t op1_16 = decode->op[0].val; + uint16_t res; + + count %= 17; + if (!count) { + break; + } + res = (op1_16 >> count) | (get_CF(env) << (16 - count)) | + (op1_16 << (17 - count)); + + write_val_ext(env, &decode->op[0], res, 2); + + cf = (op1_16 >> (count - 1)) & 0x1; + of = ((uint16_t)((res << 1) ^ res) >> 15) & 0x1; /* of = result15 ^ + result14 */ + SET_FLAGS_OxxxxC(env, of, cf); + break; + } + case 4: + { + uint32_t res; + uint32_t op1_32 = decode->op[0].val; + + if (!count) { + break; + } + + if (1 == count) { + res = (op1_32 >> 1) | (get_CF(env) << 31); + } else { + res = (op1_32 >> count) | (get_CF(env) << (32 - count)) | + (op1_32 << (33 - count)); + } + + write_val_ext(env, &decode->op[0], res, 4); + + cf = (op1_32 >> (count - 1)) & 0x1; + of = ((res << 1) ^ res) >> 31; /* of = result30 ^ result31 */ + SET_FLAGS_OxxxxC(env, of, cf); + break; + } + } + env->eip += decode->len; +} + +static void exec_xchg(CPUX86State *env, struct x86_decode *decode) +{ + fetch_operands(env, decode, 2, true, true, false); + + write_val_ext(env, &decode->op[0], decode->op[1].val, + decode->operand_size); + write_val_ext(env, &decode->op[1], decode->op[0].val, + decode->operand_size); + + env->eip += decode->len; +} + +static void exec_xadd(CPUX86State *env, struct x86_decode *decode) +{ + EXEC_2OP_FLAGS_CMD(env, decode, +, SET_FLAGS_OSZAPC_ADD, true); + write_val_ext(env, &decode->op[1], decode->op[0].val, + decode->operand_size); + + env->eip += decode->len; +} + +static struct cmd_handler { + enum x86_decode_cmd cmd; + void (*handler)(CPUX86State *env, struct x86_decode *ins); +} handlers[] = { + {X86_DECODE_CMD_INVL, NULL,}, + {X86_DECODE_CMD_MOV, exec_mov}, + {X86_DECODE_CMD_ADD, exec_add}, + {X86_DECODE_CMD_OR, exec_or}, + {X86_DECODE_CMD_ADC, exec_adc}, + {X86_DECODE_CMD_SBB, exec_sbb}, + {X86_DECODE_CMD_AND, exec_and}, + {X86_DECODE_CMD_SUB, exec_sub}, + {X86_DECODE_CMD_NEG, exec_neg}, + {X86_DECODE_CMD_XOR, exec_xor}, + {X86_DECODE_CMD_CMP, exec_cmp}, + {X86_DECODE_CMD_INC, exec_inc}, + {X86_DECODE_CMD_DEC, exec_dec}, + {X86_DECODE_CMD_TST, exec_tst}, + {X86_DECODE_CMD_NOT, exec_not}, + {X86_DECODE_CMD_MOVZX, exec_movzx}, + {X86_DECODE_CMD_OUT, exec_out}, + {X86_DECODE_CMD_IN, exec_in}, + {X86_DECODE_CMD_INS, exec_ins}, + {X86_DECODE_CMD_OUTS, exec_outs}, + {X86_DECODE_CMD_RDMSR, exec_rdmsr}, + {X86_DECODE_CMD_WRMSR, exec_wrmsr}, + {X86_DECODE_CMD_BT, exec_bt}, + {X86_DECODE_CMD_BTR, exec_btr}, + {X86_DECODE_CMD_BTC, exec_btc}, + {X86_DECODE_CMD_BTS, exec_bts}, + {X86_DECODE_CMD_SHL, exec_shl}, + {X86_DECODE_CMD_ROL, exec_rol}, + {X86_DECODE_CMD_ROR, exec_ror}, + {X86_DECODE_CMD_RCR, exec_rcr}, + {X86_DECODE_CMD_RCL, exec_rcl}, + /*{X86_DECODE_CMD_CPUID, exec_cpuid},*/ + {X86_DECODE_CMD_MOVS, exec_movs}, + {X86_DECODE_CMD_CMPS, exec_cmps}, + {X86_DECODE_CMD_STOS, exec_stos}, + {X86_DECODE_CMD_SCAS, exec_scas}, + {X86_DECODE_CMD_LODS, exec_lods}, + {X86_DECODE_CMD_MOVSX, exec_movsx}, + {X86_DECODE_CMD_XCHG, exec_xchg}, + {X86_DECODE_CMD_XADD, exec_xadd}, +}; + +static struct cmd_handler _cmd_handler[X86_DECODE_CMD_LAST]; + +const struct x86_emul_ops *emul_ops; + +static void init_cmd_handler(void) +{ + int i; + for (i = 0; i < ARRAY_SIZE(handlers); i++) { + _cmd_handler[handlers[i].cmd] = handlers[i]; + } +} + +bool exec_instruction(CPUX86State *env, struct x86_decode *ins) +{ + if (!_cmd_handler[ins->cmd].handler) { + printf("Unimplemented handler (" TARGET_FMT_lx ") for %d (%x %x) \n", env->eip, + ins->cmd, ins->opcode[0], + ins->opcode_len > 1 ? ins->opcode[1] : 0); + env->eip += ins->len; + return true; + } + + _cmd_handler[ins->cmd].handler(env, ins); + return true; +} + +void init_emu(const struct x86_emul_ops *o) +{ + emul_ops = o; + init_cmd_handler(); +} diff --git a/target/i386/emulate/x86_emu.h b/target/i386/emulate/x86_emu.h new file mode 100644 index 00000000000..a1a961284b2 --- /dev/null +++ b/target/i386/emulate/x86_emu.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 Veertu Inc, + * Copyright (C) 2017 Google Inc, + * + * This program 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 program is distributed in the hope that 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 program; if not, see . + */ + +#ifndef X86_EMU_H +#define X86_EMU_H + +#include "x86.h" +#include "x86_decode.h" +#include "cpu.h" + +struct x86_emul_ops { + void (*read_mem)(CPUState *cpu, void *data, target_ulong addr, int bytes); + void (*write_mem)(CPUState *cpu, void *data, target_ulong addr, int bytes); + void (*read_segment_descriptor)(CPUState *cpu, struct x86_segment_descriptor *desc, + enum X86Seg seg); + void (*handle_io)(CPUState *cpu, uint16_t port, void *data, int direction, + int size, int count); + void (*simulate_rdmsr)(CPUState *cs); + void (*simulate_wrmsr)(CPUState *cs); +}; + +extern const struct x86_emul_ops *emul_ops; + +void init_emu(const struct x86_emul_ops *ops); +bool exec_instruction(CPUX86State *env, struct x86_decode *ins); +void x86_emul_raise_exception(CPUX86State *env, int exception_index, int error_code); + +target_ulong read_reg(CPUX86State *env, int reg, int size); +void write_reg(CPUX86State *env, int reg, target_ulong val, int size); +target_ulong read_val_from_reg(void *reg_ptr, int size); +void write_val_to_reg(void *reg_ptr, target_ulong val, int size); +void write_val_ext(CPUX86State *env, struct x86_decode_op *decode, target_ulong val, int size); +uint8_t *read_mmio(CPUX86State *env, target_ulong ptr, int bytes); +target_ulong read_val_ext(CPUX86State *env, struct x86_decode_op *decode, int size); + +void exec_movzx(CPUX86State *env, struct x86_decode *decode); +void exec_shl(CPUX86State *env, struct x86_decode *decode); +void exec_movsx(CPUX86State *env, struct x86_decode *decode); +void exec_ror(CPUX86State *env, struct x86_decode *decode); +void exec_rol(CPUX86State *env, struct x86_decode *decode); +void exec_rcl(CPUX86State *env, struct x86_decode *decode); +void exec_rcr(CPUX86State *env, struct x86_decode *decode); +#endif diff --git a/target/i386/emulate/x86_flags.c b/target/i386/emulate/x86_flags.c new file mode 100644 index 00000000000..cc138c77494 --- /dev/null +++ b/target/i386/emulate/x86_flags.c @@ -0,0 +1,273 @@ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2001-2012 The Bochs Project +// Copyright (C) 2017 Google 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., 51 Franklin St, Fifth Floor, Boston, MA B 02110-1301 USA +///////////////////////////////////////////////////////////////////////// +/* + * flags functions + */ + +#include "qemu/osdep.h" + +#include "panic.h" +#include "cpu.h" +#include "x86_flags.h" +#include "x86.h" + + +/* + * The algorithms here are similar to those in Bochs. After an ALU + * operation, CC_DST can be used to compute ZF, SF and PF, whereas + * CC_SRC is used to compute AF, CF and OF. In reality, SF and PF are the + * XOR of the value computed from CC_DST and the value found in bits 7 and 2 + * of CC_SRC; this way the same logic can be used to compute the flags + * both before and after an ALU operation. + * + * Compared to the TCG CC_OP codes, this avoids conditionals when converting + * to and from the RFLAGS representation. + */ + +#define LF_SIGN_BIT (TARGET_LONG_BITS - 1) + +#define LF_BIT_PD (2) /* lazy Parity Delta, same bit as PF */ +#define LF_BIT_AF (3) /* lazy Adjust flag */ +#define LF_BIT_SD (7) /* lazy Sign Flag Delta, same bit as SF */ +#define LF_BIT_CF (TARGET_LONG_BITS - 1) /* lazy Carry Flag */ +#define LF_BIT_PO (TARGET_LONG_BITS - 2) /* lazy Partial Overflow = CF ^ OF */ + +#define LF_MASK_PD ((target_ulong)0x01 << LF_BIT_PD) +#define LF_MASK_AF ((target_ulong)0x01 << LF_BIT_AF) +#define LF_MASK_SD ((target_ulong)0x01 << LF_BIT_SD) +#define LF_MASK_CF ((target_ulong)0x01 << LF_BIT_CF) +#define LF_MASK_PO ((target_ulong)0x01 << LF_BIT_PO) + +/* ******************* */ +/* OSZAPC */ +/* ******************* */ + +/* use carries to fill in AF, PO and CF, while ensuring PD and SD are clear. + * for full-word operations just clear PD and SD; for smaller operand + * sizes only keep AF in the low byte and shift the carries left to + * place PO and CF in the top two bits. + */ +#define SET_FLAGS_OSZAPC_SIZE(size, lf_carries, lf_result) { \ + env->cc_dst = (target_ulong)(int##size##_t)(lf_result); \ + target_ulong temp = (lf_carries); \ + if ((size) == TARGET_LONG_BITS) { \ + temp = temp & ~(LF_MASK_PD | LF_MASK_SD); \ + } else { \ + temp = (temp & LF_MASK_AF) | (temp << (TARGET_LONG_BITS - (size))); \ + } \ + env->cc_src = temp; \ +} + +/* carries, result */ +#define SET_FLAGS_OSZAPC_8(carries, result) \ + SET_FLAGS_OSZAPC_SIZE(8, carries, result) +#define SET_FLAGS_OSZAPC_16(carries, result) \ + SET_FLAGS_OSZAPC_SIZE(16, carries, result) +#define SET_FLAGS_OSZAPC_32(carries, result) \ + SET_FLAGS_OSZAPC_SIZE(32, carries, result) + +/* ******************* */ +/* OSZAP */ +/* ******************* */ +/* same as setting OSZAPC, but preserve CF and flip PO if the old value of CF + * did not match the high bit of lf_carries. */ +#define SET_FLAGS_OSZAP_SIZE(size, lf_carries, lf_result) { \ + env->cc_dst = (target_ulong)(int##size##_t)(lf_result); \ + target_ulong temp = (lf_carries); \ + if ((size) == TARGET_LONG_BITS) { \ + temp = (temp & ~(LF_MASK_PD | LF_MASK_SD)); \ + } else { \ + temp = (temp & LF_MASK_AF) | (temp << (TARGET_LONG_BITS - (size))); \ + } \ + target_ulong cf_changed = ((target_long)(env->cc_src ^ temp)) < 0; \ + env->cc_src = temp ^ (cf_changed * (LF_MASK_PO | LF_MASK_CF)); \ +} + +/* carries, result */ +#define SET_FLAGS_OSZAP_8(carries, result) \ + SET_FLAGS_OSZAP_SIZE(8, carries, result) +#define SET_FLAGS_OSZAP_16(carries, result) \ + SET_FLAGS_OSZAP_SIZE(16, carries, result) +#define SET_FLAGS_OSZAP_32(carries, result) \ + SET_FLAGS_OSZAP_SIZE(32, carries, result) + +void SET_FLAGS_OxxxxC(CPUX86State *env, bool new_of, bool new_cf) +{ + env->cc_src &= ~(LF_MASK_PO | LF_MASK_CF); + env->cc_src |= (-(target_ulong)new_cf << LF_BIT_PO); + env->cc_src ^= ((target_ulong)new_of << LF_BIT_PO); +} + +void SET_FLAGS_OSZAPC_SUB32(CPUX86State *env, uint32_t v1, uint32_t v2, + uint32_t diff) +{ + SET_FLAGS_OSZAPC_32(SUB_COUT_VEC(v1, v2, diff), diff); +} + +void SET_FLAGS_OSZAPC_SUB16(CPUX86State *env, uint16_t v1, uint16_t v2, + uint16_t diff) +{ + SET_FLAGS_OSZAPC_16(SUB_COUT_VEC(v1, v2, diff), diff); +} + +void SET_FLAGS_OSZAPC_SUB8(CPUX86State *env, uint8_t v1, uint8_t v2, + uint8_t diff) +{ + SET_FLAGS_OSZAPC_8(SUB_COUT_VEC(v1, v2, diff), diff); +} + +void SET_FLAGS_OSZAPC_ADD32(CPUX86State *env, uint32_t v1, uint32_t v2, + uint32_t diff) +{ + SET_FLAGS_OSZAPC_32(ADD_COUT_VEC(v1, v2, diff), diff); +} + +void SET_FLAGS_OSZAPC_ADD16(CPUX86State *env, uint16_t v1, uint16_t v2, + uint16_t diff) +{ + SET_FLAGS_OSZAPC_16(ADD_COUT_VEC(v1, v2, diff), diff); +} + +void SET_FLAGS_OSZAPC_ADD8(CPUX86State *env, uint8_t v1, uint8_t v2, + uint8_t diff) +{ + SET_FLAGS_OSZAPC_8(ADD_COUT_VEC(v1, v2, diff), diff); +} + +void SET_FLAGS_OSZAP_SUB32(CPUX86State *env, uint32_t v1, uint32_t v2, + uint32_t diff) +{ + SET_FLAGS_OSZAP_32(SUB_COUT_VEC(v1, v2, diff), diff); +} + +void SET_FLAGS_OSZAP_SUB16(CPUX86State *env, uint16_t v1, uint16_t v2, + uint16_t diff) +{ + SET_FLAGS_OSZAP_16(SUB_COUT_VEC(v1, v2, diff), diff); +} + +void SET_FLAGS_OSZAP_SUB8(CPUX86State *env, uint8_t v1, uint8_t v2, + uint8_t diff) +{ + SET_FLAGS_OSZAP_8(SUB_COUT_VEC(v1, v2, diff), diff); +} + +void SET_FLAGS_OSZAP_ADD32(CPUX86State *env, uint32_t v1, uint32_t v2, + uint32_t diff) +{ + SET_FLAGS_OSZAP_32(ADD_COUT_VEC(v1, v2, diff), diff); +} + +void SET_FLAGS_OSZAP_ADD16(CPUX86State *env, uint16_t v1, uint16_t v2, + uint16_t diff) +{ + SET_FLAGS_OSZAP_16(ADD_COUT_VEC(v1, v2, diff), diff); +} + +void SET_FLAGS_OSZAP_ADD8(CPUX86State *env, uint8_t v1, uint8_t v2, + uint8_t diff) +{ + SET_FLAGS_OSZAP_8(ADD_COUT_VEC(v1, v2, diff), diff); +} + + +void SET_FLAGS_OSZAPC_LOGIC32(CPUX86State *env, uint32_t v1, uint32_t v2, + uint32_t diff) +{ + SET_FLAGS_OSZAPC_32(0, diff); +} + +void SET_FLAGS_OSZAPC_LOGIC16(CPUX86State *env, uint16_t v1, uint16_t v2, + uint16_t diff) +{ + SET_FLAGS_OSZAPC_16(0, diff); +} + +void SET_FLAGS_OSZAPC_LOGIC8(CPUX86State *env, uint8_t v1, uint8_t v2, + uint8_t diff) +{ + SET_FLAGS_OSZAPC_8(0, diff); +} + +static inline uint32_t get_PF(CPUX86State *env) +{ + return ((parity8(env->cc_dst) - 1) ^ env->cc_src) & CC_P; +} + +static inline uint32_t get_OF(CPUX86State *env) +{ + return ((env->cc_src >> (LF_BIT_CF - 11)) + CC_O / 2) & CC_O; +} + +bool get_CF(CPUX86State *env) +{ + return ((target_long)env->cc_src) < 0; +} + +void set_CF(CPUX86State *env, bool val) +{ + /* If CF changes, flip PO and CF */ + target_ulong temp = -(target_ulong)val; + target_ulong cf_changed = ((target_long)(env->cc_src ^ temp)) < 0; + env->cc_src ^= cf_changed * (LF_MASK_PO | LF_MASK_CF); +} + +static inline uint32_t get_ZF(CPUX86State *env) +{ + return env->cc_dst ? 0 : CC_Z; +} + +static inline uint32_t get_SF(CPUX86State *env) +{ + return ((env->cc_dst >> (LF_SIGN_BIT - LF_BIT_SD)) ^ + env->cc_src) & CC_S; +} + +void lflags_to_rflags(CPUX86State *env) +{ + env->eflags &= ~(CC_C|CC_P|CC_A|CC_Z|CC_S|CC_O); + /* rotate left by one to move carry-out bits into CF and AF */ + env->eflags |= ( + (env->cc_src << 1) | + (env->cc_src >> (TARGET_LONG_BITS - 1))) & (CC_C | CC_A); + env->eflags |= get_SF(env); + env->eflags |= get_PF(env); + env->eflags |= get_ZF(env); + env->eflags |= get_OF(env); +} + +void rflags_to_lflags(CPUX86State *env) +{ + target_ulong cf_af, cf_xor_of; + + /* Leave the low byte zero so that parity is always even... */ + env->cc_dst = !(env->eflags & CC_Z) << 8; + + /* ... and therefore cc_src always uses opposite polarity. */ + env->cc_src = CC_P; + env->cc_src ^= env->eflags & (CC_S | CC_P); + + /* rotate right by one to move CF and AF into the carry-out positions */ + cf_af = env->eflags & (CC_C | CC_A); + env->cc_src |= ((cf_af >> 1) | (cf_af << (TARGET_LONG_BITS - 1))); + + cf_xor_of = ((env->eflags & (CC_C | CC_O)) + (CC_O - CC_C)) & CC_O; + env->cc_src |= -cf_xor_of & LF_MASK_PO; +} diff --git a/target/i386/hvf/x86_flags.h b/target/i386/emulate/x86_flags.h similarity index 85% rename from target/i386/hvf/x86_flags.h rename to target/i386/emulate/x86_flags.h index 75c2a7feab5..28b008e5771 100644 --- a/target/i386/hvf/x86_flags.h +++ b/target/i386/emulate/x86_flags.h @@ -21,27 +21,17 @@ * x86 eflags functions */ -#ifndef X86_FLAGS_H -#define X86_FLAGS_H +#ifndef X86_EMU_FLAGS_H +#define X86_EMU_FLAGS_H #include "cpu.h" void lflags_to_rflags(CPUX86State *env); void rflags_to_lflags(CPUX86State *env); -bool get_PF(CPUX86State *env); -void set_PF(CPUX86State *env, bool val); bool get_CF(CPUX86State *env); void set_CF(CPUX86State *env, bool val); -bool get_AF(CPUX86State *env); -void set_AF(CPUX86State *env, bool val); -bool get_ZF(CPUX86State *env); -void set_ZF(CPUX86State *env, bool val); -bool get_SF(CPUX86State *env); -void set_SF(CPUX86State *env, bool val); -bool get_OF(CPUX86State *env); -void set_OF(CPUX86State *env, bool val); -void SET_FLAGS_OxxxxC(CPUX86State *env, uint32_t new_of, uint32_t new_cf); +void SET_FLAGS_OxxxxC(CPUX86State *env, bool new_of, bool new_cf); void SET_FLAGS_OSZAPC_SUB32(CPUX86State *env, uint32_t v1, uint32_t v2, uint32_t diff); @@ -78,4 +68,4 @@ void SET_FLAGS_OSZAPC_LOGIC16(CPUX86State *env, uint16_t v1, uint16_t v2, void SET_FLAGS_OSZAPC_LOGIC8(CPUX86State *env, uint8_t v1, uint8_t v2, uint8_t diff); -#endif /* X86_FLAGS_H */ +#endif /* X86_EMU_FLAGS_H */ diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c index ebb000df6a7..04c49e802d7 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 "include/gdbstub/helpers.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); @@ -137,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); @@ -248,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); @@ -288,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; @@ -397,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/helper.c b/target/i386/helper.c index 2070dd0dda1..e0aaed3c4c4 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -20,10 +20,13 @@ #include "qemu/osdep.h" #include "qapi/qapi-events-run-state.h" #include "cpu.h" -#include "exec/exec-all.h" -#include "sysemu/runstate.h" +#include "exec/cputlb.h" +#include "exec/translation-block.h" +#include "exec/target_page.h" +#include "system/runstate.h" #ifndef CONFIG_USER_ONLY -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" +#include "system/memory.h" #include "monitor/monitor.h" #include "kvm/kvm_i386.h" #endif @@ -91,6 +94,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; @@ -219,6 +226,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; @@ -430,7 +441,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; @@ -515,7 +526,7 @@ void cpu_x86_inject_mce(Monitor *mon, X86CPU *cpu, int bank, static inline target_ulong get_memio_eip(CPUX86State *env) { #ifdef CONFIG_TCG - uint64_t data[TARGET_INSN_START_WORDS]; + uint64_t data[INSN_START_WORDS]; CPUState *cs = env_cpu(env); if (!cpu_unwind_state_data(cs, cs->mem_io_pc, data)) { @@ -523,7 +534,7 @@ static inline target_ulong get_memio_eip(CPUX86State *env) } /* Per x86_restore_state_to_opc. */ - if (cs->tcg_cflags & CF_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; diff --git a/target/i386/helper.h b/target/i386/helper.h index ac2b04abd63..3f67098f11f 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) @@ -53,9 +54,10 @@ DEF_HELPER_1(sysenter, void, env) DEF_HELPER_2(sysexit, void, env, int) DEF_HELPER_2(syscall, void, env, int) DEF_HELPER_2(sysret, void, env, int) -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) @@ -89,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 */ @@ -207,15 +209,4 @@ DEF_HELPER_1(emms, void, env) #define SHIFT 2 #include "tcg/ops_sse_header.h.inc" -DEF_HELPER_3(rclb, tl, env, tl, tl) -DEF_HELPER_3(rclw, tl, env, tl, tl) -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 - DEF_HELPER_1(rdrand, tl, env) diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c index 92ecb7254b8..7512567298b 100644 --- a/target/i386/host-cpu.c +++ b/target/i386/host-cpu.c @@ -12,10 +12,10 @@ #include "host-cpu.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/sysemu.h" +#include "system/system.h" /* Note: Only safe for use on x86(-64) hosts */ -static uint32_t host_cpu_phys_bits(void) +uint32_t host_cpu_phys_bits(void) { uint32_t eax; uint32_t host_phys_bits; @@ -42,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) { @@ -78,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) @@ -86,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; } @@ -134,9 +109,13 @@ void host_cpu_vendor_fms(char *vendor, int *family, int *model, int *stepping) { uint32_t eax, ebx, ecx, edx; - host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx); + host_cpuid(0x0, 0, NULL, &ebx, &ecx, &edx); x86_cpu_vendor_words2str(vendor, ebx, edx, ecx); + if (!family && !model && !stepping) { + return; + } + host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx); if (family) { *family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF); @@ -154,11 +133,9 @@ void host_cpu_instance_init(X86CPU *cpu) X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); if (xcc->model) { - uint32_t ebx = 0, ecx = 0, edx = 0; char vendor[CPUID_VENDOR_SZ + 1]; - host_cpuid(0, 0, NULL, &ebx, &ecx, &edx); - x86_cpu_vendor_words2str(vendor, ebx, edx, ecx); + host_cpu_vendor_fms(vendor, NULL, NULL, NULL); object_property_set_str(OBJECT(cpu), "vendor", vendor, &error_abort); } } @@ -184,7 +161,7 @@ void host_cpu_max_instance_init(X86CPU *cpu) &error_abort); } -static void host_cpu_class_init(ObjectClass *oc, void *data) +static void host_cpu_class_init(ObjectClass *oc, const void *data) { X86CPUClass *xcc = X86_CPU_CLASS(oc); diff --git a/target/i386/host-cpu.h b/target/i386/host-cpu.h index 6a9bc918baa..b97ec01c9be 100644 --- a/target/i386/host-cpu.h +++ b/target/i386/host-cpu.h @@ -10,6 +10,7 @@ #ifndef HOST_CPU_H #define HOST_CPU_H +uint32_t host_cpu_phys_bits(void); void host_cpu_instance_init(X86CPU *cpu); void host_cpu_max_instance_init(X86CPU *cpu); bool host_cpu_realizefn(CPUState *cs, Error **errp); diff --git a/target/i386/hvf/README.md b/target/i386/hvf/README.md index 2d33477aca5..64a8935237c 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 333db59898a..dfdda701268 100644 --- a/target/i386/hvf/hvf-cpu.c +++ b/target/i386/hvf/hvf-cpu.c @@ -11,10 +11,11 @@ #include "cpu.h" #include "host-cpu.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" -#include "sysemu/hvf.h" -#include "hw/core/accel-cpu.h" +#include "system/hvf.h" +#include "accel/accel-cpu-target.h" +#include "hvf-i386.h" static void hvf_cpu_max_instance_init(X86CPU *cpu) { @@ -73,11 +74,11 @@ static void hvf_cpu_instance_init(CPUState *cs) hvf_cpu_xsave_init(); } -static void hvf_cpu_accel_class_init(ObjectClass *oc, void *data) +static void hvf_cpu_accel_class_init(ObjectClass *oc, const 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 95b47c1c2e7..8c42ae6b013 100644 --- a/target/i386/hvf/hvf-i386.h +++ b/target/i386/hvf/hvf-i386.h @@ -16,13 +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); +void hvf_handle_io(CPUState *, uint16_t, void *, int, int, int); +void hvf_simulate_rdmsr(CPUState *cpu); +void hvf_simulate_wrmsr(CPUState *cpu); /* Host specific functions */ int hvf_inject_interrupt(CPUArchState *env, int vector); diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index cb2cd0b02fa..99e37a33e50 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -49,19 +49,22 @@ #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" -#include "sysemu/runstate.h" -#include "sysemu/cpus.h" +#include "system/hvf.h" +#include "system/hvf_int.h" +#include "system/runstate.h" +#include "system/cpus.h" #include "hvf-i386.h" #include "vmcs.h" #include "vmx.h" -#include "x86.h" +#include "emulate/x86.h" #include "x86_descr.h" +#include "emulate/x86_flags.h" #include "x86_mmu.h" -#include "x86_decode.h" -#include "x86_emu.h" +#include "emulate/x86_decode.h" +#include "emulate/x86_emu.h" #include "x86_task.h" #include "x86hvf.h" @@ -73,6 +76,9 @@ #include "qemu/main-loop.h" #include "qemu/accel.h" #include "target/i386/cpu.h" +#include "exec/target_page.h" + +static Error *invtsc_mig_blocker; void vmx_update_tpr(CPUState *cpu) { @@ -99,7 +105,7 @@ static void update_apic_tpr(CPUState *cpu) #define VECTORING_INFO_VECTOR_MASK 0xff -void hvf_handle_io(CPUArchState *env, uint16_t port, void *buffer, +void hvf_handle_io(CPUState *env, uint16_t port, void *buffer, int direction, int size, int count) { int i; @@ -131,9 +137,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); } } @@ -162,7 +169,7 @@ void hvf_arch_vcpu_destroy(CPUState *cpu) X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; - g_free(env->hvf_mmio_buf); + g_free(env->emu_mmio_buf); } static void init_tsc_freq(CPUX86State *env) @@ -210,6 +217,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,17 +225,53 @@ 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); +} + +static void hvf_read_segment_descriptor(CPUState *s, struct x86_segment_descriptor *desc, + X86Seg seg) +{ + struct vmx_segment vmx_segment; + vmx_read_segment_descriptor(s, &vmx_segment, seg); + vmx_segment_to_x86_descriptor(s, &vmx_segment, desc); +} + +static void hvf_read_mem(CPUState *cpu, void *data, target_ulong gva, int bytes) +{ + vmx_read_mem(cpu, data, gva, bytes); +} + +static void hvf_write_mem(CPUState *cpu, void *data, target_ulong gva, int bytes) +{ + vmx_write_mem(cpu, gva, data, bytes); +} + +static const struct x86_emul_ops hvf_x86_emul_ops = { + .read_mem = hvf_read_mem, + .write_mem = hvf_write_mem, + .read_segment_descriptor = hvf_read_segment_descriptor, + .handle_io = hvf_handle_io, + .simulate_rdmsr = hvf_simulate_rdmsr, + .simulate_wrmsr = hvf_simulate_wrmsr, +}; + 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_emu(&hvf_x86_emul_ops); init_decoder(); - hvf_state->hvf_caps = g_new0(struct hvf_vcpu_caps, 1); - env->hvf_mmio_buf = g_new(char, 4096); + if (hvf_state->hvf_caps == NULL) { + hvf_state->hvf_caps = g_new0(struct hvf_vcpu_caps, 1); + } + env->emu_mmio_buf = g_new(char, 4096); if (x86cpu->vmware_cpuid_freq) { init_tsc_freq(env); @@ -238,6 +282,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(); @@ -407,6 +463,264 @@ static void hvf_cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } } +void hvf_load_regs(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + int i = 0; + 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(cs->accel->fd, HV_X86_RAX + i); + } + + env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); + rflags_to_lflags(env); + env->eip = rreg(cs->accel->fd, HV_X86_RIP); +} + +void hvf_store_regs(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + int i = 0; + 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(cs->accel->fd, HV_X86_RAX + i, RRX(env, i)); + } + + lflags_to_rflags(env); + wreg(cs->accel->fd, HV_X86_RFLAGS, env->eflags); + macvm_set_rip(cs, env->eip); +} + +void hvf_simulate_rdmsr(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + uint32_t msr = ECX(env); + uint64_t val = 0; + + switch (msr) { + case MSR_IA32_TSC: + val = rdtscp() + rvmcs(cs->accel->fd, VMCS_TSC_OFFSET); + break; + case MSR_IA32_APICBASE: + 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) { + x86_emul_raise_exception(env, EXCP0D_GPF, 0); + } + + break; + } + case MSR_IA32_UCODE_REV: + val = cpu->ucode_rev; + break; + case MSR_EFER: + val = rvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER); + break; + case MSR_FSBASE: + val = rvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE); + break; + case MSR_GSBASE: + val = rvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE); + break; + case MSR_KERNELGSBASE: + val = rvmcs(cs->accel->fd, VMCS_HOST_FS_BASE); + break; + case MSR_STAR: + abort(); + break; + case MSR_LSTAR: + abort(); + break; + case MSR_CSTAR: + abort(); + break; + case MSR_IA32_MISC_ENABLE: + val = env->msr_ia32_misc_enable; + break; + case MSR_MTRRphysBase(0): + case MSR_MTRRphysBase(1): + case MSR_MTRRphysBase(2): + case MSR_MTRRphysBase(3): + case MSR_MTRRphysBase(4): + case MSR_MTRRphysBase(5): + case MSR_MTRRphysBase(6): + case MSR_MTRRphysBase(7): + val = env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base; + break; + case MSR_MTRRphysMask(0): + case MSR_MTRRphysMask(1): + case MSR_MTRRphysMask(2): + case MSR_MTRRphysMask(3): + case MSR_MTRRphysMask(4): + case MSR_MTRRphysMask(5): + case MSR_MTRRphysMask(6): + case MSR_MTRRphysMask(7): + val = env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask; + break; + case MSR_MTRRfix64K_00000: + val = env->mtrr_fixed[0]; + break; + case MSR_MTRRfix16K_80000: + case MSR_MTRRfix16K_A0000: + val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1]; + break; + case MSR_MTRRfix4K_C0000: + case MSR_MTRRfix4K_C8000: + case MSR_MTRRfix4K_D0000: + case MSR_MTRRfix4K_D8000: + case MSR_MTRRfix4K_E0000: + case MSR_MTRRfix4K_E8000: + case MSR_MTRRfix4K_F0000: + case MSR_MTRRfix4K_F8000: + val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3]; + break; + case MSR_MTRRdefType: + val = env->mtrr_deftype; + break; + case MSR_CORE_THREAD_COUNT: + val = cpu_x86_get_msr_core_thread_count(cpu); + break; + default: + /* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */ + val = 0; + break; + } + + RAX(env) = (uint32_t)val; + RDX(env) = (uint32_t)(val >> 32); +} + +void hvf_simulate_wrmsr(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *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: { + int r; + + r = cpu_set_apic_base(cpu->apic_state, data); + if (r < 0) { + x86_emul_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) { + x86_emul_raise_exception(env, EXCP0D_GPF, 0); + } + + break; + } + case MSR_FSBASE: + wvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE, data); + break; + case MSR_GSBASE: + wvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE, data); + break; + case MSR_KERNELGSBASE: + wvmcs(cs->accel->fd, VMCS_HOST_FS_BASE, data); + break; + case MSR_STAR: + abort(); + break; + case MSR_LSTAR: + abort(); + break; + case MSR_CSTAR: + abort(); + break; + case MSR_EFER: + /*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(cs->accel->fd); + } + break; + case MSR_MTRRphysBase(0): + case MSR_MTRRphysBase(1): + case MSR_MTRRphysBase(2): + case MSR_MTRRphysBase(3): + case MSR_MTRRphysBase(4): + case MSR_MTRRphysBase(5): + case MSR_MTRRphysBase(6): + case MSR_MTRRphysBase(7): + env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base = data; + break; + case MSR_MTRRphysMask(0): + case MSR_MTRRphysMask(1): + case MSR_MTRRphysMask(2): + case MSR_MTRRphysMask(3): + case MSR_MTRRphysMask(4): + case MSR_MTRRphysMask(5): + case MSR_MTRRphysMask(6): + case MSR_MTRRphysMask(7): + env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask = data; + break; + case MSR_MTRRfix64K_00000: + env->mtrr_fixed[ECX(env) - MSR_MTRRfix64K_00000] = data; + break; + case MSR_MTRRfix16K_80000: + case MSR_MTRRfix16K_A0000: + env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1] = data; + break; + case MSR_MTRRfix4K_C0000: + case MSR_MTRRfix4K_C8000: + case MSR_MTRRfix4K_D0000: + case MSR_MTRRfix4K_D8000: + case MSR_MTRRfix4K_E0000: + case MSR_MTRRfix4K_E8000: + case MSR_MTRRfix4K_F0000: + case MSR_MTRRfix4K_F8000: + env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3] = data; + break; + case MSR_MTRRdefType: + env->mtrr_deftype = data; + break; + default: + break; + } + + /* Related to support known hypervisor interface */ + /* if (g_hypervisor_iface) + g_hypervisor_iface->wrmsr_handler(cs, msr, data); + + printf("write msr %llx\n", RCX(cs));*/ +} + int hvf_vcpu_exec(CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); @@ -419,9 +733,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,13 +743,13 @@ 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->accel->fd); + hv_return_t r = hv_vcpu_run_until(cpu->accel->fd, HV_DEADLINE_FOREVER); assert_hvf_ok(r); /* handle VMEXIT */ @@ -450,7 +764,7 @@ int hvf_vcpu_exec(CPUState *cpu) 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; @@ -490,10 +804,10 @@ int hvf_vcpu_exec(CPUState *cpu) if (ept_emulation_fault(slot, gpa, exit_qual)) { struct x86_decode decode; - load_regs(cpu); + hvf_load_regs(cpu); decode_instruction(env, &decode); exec_instruction(env, &decode); - store_regs(cpu); + hvf_store_regs(cpu); break; } break; @@ -508,8 +822,8 @@ int hvf_vcpu_exec(CPUState *cpu) if (!string && in) { uint64_t val = 0; - load_regs(cpu); - hvf_handle_io(env, port, &val, 0, size, 1); + hvf_load_regs(cpu); + hvf_handle_io(env_cpu(env), port, &val, 0, size, 1); if (size == 1) { AL(env) = val; } else if (size == 2) { @@ -520,21 +834,21 @@ int hvf_vcpu_exec(CPUState *cpu) RAX(env) = (uint64_t)val; } env->eip += ins_len; - store_regs(cpu); + hvf_store_regs(cpu); break; } else if (!string && !in) { RAX(env) = rreg(cpu->accel->fd, HV_X86_RAX); - hvf_handle_io(env, port, &RAX(env), 1, size, 1); + hvf_handle_io(env_cpu(env), port, &RAX(env), 1, size, 1); macvm_set_rip(cpu, rip + ins_len); break; } struct x86_decode decode; - load_regs(cpu); + hvf_load_regs(cpu); decode_instruction(env, &decode); assert(ins_len == decode.len); exec_instruction(env, &decode); - store_regs(cpu); + hvf_store_regs(cpu); break; } @@ -559,8 +873,6 @@ int hvf_vcpu_exec(CPUState *cpu) break; } case EXIT_REASON_XSETBV: { - X86CPU *x86_cpu = X86_CPU(cpu); - CPUX86State *env = &x86_cpu->env; 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); @@ -589,21 +901,21 @@ int hvf_vcpu_exec(CPUState *cpu) case EXIT_REASON_RDMSR: case EXIT_REASON_WRMSR: { - load_regs(cpu); + hvf_load_regs(cpu); if (exit_reason == EXIT_REASON_RDMSR) { - simulate_rdmsr(cpu); + hvf_simulate_rdmsr(cpu); } else { - simulate_wrmsr(cpu); + hvf_simulate_wrmsr(cpu); } env->eip += ins_len; - store_regs(cpu); + hvf_store_regs(cpu); break; } case EXIT_REASON_CR_ACCESS: { int cr; int reg; - load_regs(cpu); + hvf_load_regs(cpu); cr = exit_qual & 15; reg = (exit_qual >> 8) & 15; @@ -617,7 +929,6 @@ int hvf_vcpu_exec(CPUState *cpu) 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 { @@ -632,16 +943,16 @@ int hvf_vcpu_exec(CPUState *cpu) abort(); } env->eip += ins_len; - store_regs(cpu); + hvf_store_regs(cpu); break; } case EXIT_REASON_APIC_ACCESS: { /* TODO */ struct x86_decode decode; - load_regs(cpu); + hvf_load_regs(cpu); decode_instruction(env, &decode); exec_instruction(env, &decode); - store_regs(cpu); + hvf_store_regs(cpu); break; } case EXIT_REASON_TPR: { @@ -650,7 +961,7 @@ int hvf_vcpu_exec(CPUState *cpu) } case EXIT_REASON_TASK_SWITCH: { uint64_t vinfo = rvmcs(cpu->accel->fd, VMCS_IDT_VECTORING_INFO); - x68_segment_selector sel = {.sel = exit_qual & 0xffff}; + x86_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 & VMCS_INTR_T_MASK); @@ -708,7 +1019,7 @@ void hvf_arch_update_guest_debug(CPUState *cpu) { } -inline bool hvf_arch_supports_guest_debug(void) +bool hvf_arch_supports_guest_debug(void) { return false; } diff --git a/target/i386/hvf/meson.build b/target/i386/hvf/meson.build index 05c3c8cf18b..519d190f0e6 100644 --- a/target/i386/hvf/meson.build +++ b/target/i386/hvf/meson.build @@ -2,10 +2,7 @@ i386_system_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: files( 'hvf.c', 'x86.c', 'x86_cpuid.c', - 'x86_decode.c', 'x86_descr.c', - 'x86_emu.c', - 'x86_flags.c', 'x86_mmu.c', 'x86_task.c', 'x86hvf.c', diff --git a/target/i386/hvf/vmx.h b/target/i386/hvf/vmx.h index 0fffcfa46ce..26d6029fa55 100644 --- a/target/i386/hvf/vmx.h +++ b/target/i386/hvf/vmx.h @@ -29,11 +29,12 @@ #include #include "vmcs.h" #include "cpu.h" -#include "x86.h" -#include "sysemu/hvf.h" -#include "sysemu/hvf_int.h" +#include "emulate/x86.h" +#include "system/hvf.h" +#include "system/hvf_int.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" +#include "system/memory.h" static inline uint64_t rreg(hv_vcpuid_t vcpu, hv_x86_reg_t reg) { @@ -95,8 +96,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) && diff --git a/target/i386/hvf/x86.c b/target/i386/hvf/x86.c index 8ceea6398e7..5c75ec9a007 100644 --- a/target/i386/hvf/x86.c +++ b/target/i386/hvf/x86.c @@ -19,8 +19,8 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "x86_decode.h" -#include "x86_emu.h" +#include "emulate/x86_decode.h" +#include "emulate/x86_emu.h" #include "vmcs.h" #include "vmx.h" #include "x86_mmu.h" @@ -46,9 +46,9 @@ 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) + x86_segment_selector sel) { target_ulong base; uint32_t limit; @@ -76,9 +76,9 @@ 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) + x86_segment_selector sel) { target_ulong base; uint32_t limit; @@ -99,7 +99,7 @@ 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->accel->fd, VMCS_GUEST_IDTR_BASE); @@ -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->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->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->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->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_cpuid.c b/target/i386/hvf/x86_cpuid.c index 7323a7a94b1..0798a0cbafb 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 "emulate/x86.h" #include "vmx.h" -#include "sysemu/hvf.h" +#include "system/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 (cached_xcr0) { + return; + } - 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)); + if (cpuinfo & CPUINFO_OSXSAVE) { + uint64_t host_xcr0 = xgetbv_low(0); - *xcr = (((uint64_t)xcrh) << 32) | xcrl; - return true; + /* 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; + } } - return false; + 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) { @@ -61,11 +73,12 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, 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 | CPUID_SS; + CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS | CPUID_HT; 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 deleted file mode 100644 index 3728d7705e2..00000000000 --- a/target/i386/hvf/x86_decode.c +++ /dev/null @@ -1,2196 +0,0 @@ -/* - * Copyright (C) 2016 Veertu Inc, - * Copyright (C) 2017 Google Inc, - * - * This program 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 program is distributed in the hope that 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 program; if not, see . - */ - -#include "qemu/osdep.h" - -#include "panic.h" -#include "x86_decode.h" -#include "vmx.h" -#include "x86_mmu.h" -#include "x86_descr.h" - -#define OPCODE_ESCAPE 0xf - -static void decode_invalid(CPUX86State *env, struct x86_decode *decode) -{ - printf("%llx: failed to decode instruction ", env->eip); - for (int i = 0; i < decode->opcode_len; i++) { - printf("%x ", decode->opcode[i]); - } - printf("\n"); - VM_PANIC("decoder failed\n"); -} - -uint64_t sign(uint64_t val, int size) -{ - switch (size) { - case 1: - val = (int8_t)val; - break; - case 2: - val = (int16_t)val; - break; - case 4: - val = (int32_t)val; - break; - case 8: - val = (int64_t)val; - break; - default: - VM_PANIC_EX("%s invalid size %d\n", __func__, size); - break; - } - return val; -} - -static inline uint64_t decode_bytes(CPUX86State *env, struct x86_decode *decode, - int size) -{ - target_ulong val = 0; - - switch (size) { - case 1: - case 2: - case 4: - case 8: - break; - default: - VM_PANIC_EX("%s invalid size %d\n", __func__, size); - break; - } - target_ulong va = linear_rip(env_cpu(env), env->eip) + decode->len; - vmx_read_mem(env_cpu(env), &val, va, size); - decode->len += size; - - return val; -} - -static inline uint8_t decode_byte(CPUX86State *env, struct x86_decode *decode) -{ - return (uint8_t)decode_bytes(env, decode, 1); -} - -static inline uint16_t decode_word(CPUX86State *env, struct x86_decode *decode) -{ - return (uint16_t)decode_bytes(env, decode, 2); -} - -static inline uint32_t decode_dword(CPUX86State *env, struct x86_decode *decode) -{ - return (uint32_t)decode_bytes(env, decode, 4); -} - -static inline uint64_t decode_qword(CPUX86State *env, struct x86_decode *decode) -{ - return decode_bytes(env, decode, 8); -} - -static void decode_modrm_rm(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - op->type = X86_VAR_RM; -} - -static void decode_modrm_reg(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - op->type = X86_VAR_REG; - op->reg = decode->modrm.reg; - op->ptr = get_reg_ref(env, op->reg, decode->rex.rex, decode->rex.r, - decode->operand_size); -} - -static void decode_rax(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - op->type = X86_VAR_REG; - op->reg = R_EAX; - /* Since reg is always AX, REX prefix has no impact. */ - op->ptr = get_reg_ref(env, op->reg, false, 0, - decode->operand_size); -} - -static inline void decode_immediate(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *var, int size) -{ - var->type = X86_VAR_IMMEDIATE; - var->size = size; - switch (size) { - case 1: - var->val = decode_byte(env, decode); - break; - case 2: - var->val = decode_word(env, decode); - break; - case 4: - var->val = decode_dword(env, decode); - break; - case 8: - var->val = decode_qword(env, decode); - break; - default: - VM_PANIC_EX("bad size %d\n", size); - } -} - -static void decode_imm8(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - decode_immediate(env, decode, op, 1); - op->type = X86_VAR_IMMEDIATE; -} - -static void decode_imm8_signed(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - decode_immediate(env, decode, op, 1); - op->val = sign(op->val, 1); - op->type = X86_VAR_IMMEDIATE; -} - -static void decode_imm16(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - decode_immediate(env, decode, op, 2); - op->type = X86_VAR_IMMEDIATE; -} - - -static void decode_imm(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - if (8 == decode->operand_size) { - decode_immediate(env, decode, op, 4); - op->val = sign(op->val, decode->operand_size); - } else { - decode_immediate(env, decode, op, decode->operand_size); - } - op->type = X86_VAR_IMMEDIATE; -} - -static void decode_imm_signed(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - decode_immediate(env, decode, op, decode->operand_size); - op->val = sign(op->val, decode->operand_size); - op->type = X86_VAR_IMMEDIATE; -} - -static void decode_imm_1(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - op->type = X86_VAR_IMMEDIATE; - op->val = 1; -} - -static void decode_imm_0(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - op->type = X86_VAR_IMMEDIATE; - op->val = 0; -} - - -static void decode_pushseg(CPUX86State *env, struct x86_decode *decode) -{ - uint8_t op = (decode->opcode_len > 1) ? decode->opcode[1] : decode->opcode[0]; - - decode->op[0].type = X86_VAR_REG; - switch (op) { - case 0xe: - decode->op[0].reg = R_CS; - break; - case 0x16: - decode->op[0].reg = R_SS; - break; - case 0x1e: - decode->op[0].reg = R_DS; - break; - case 0x06: - decode->op[0].reg = R_ES; - break; - case 0xa0: - decode->op[0].reg = R_FS; - break; - case 0xa8: - decode->op[0].reg = R_GS; - break; - } -} - -static void decode_popseg(CPUX86State *env, struct x86_decode *decode) -{ - uint8_t op = (decode->opcode_len > 1) ? decode->opcode[1] : decode->opcode[0]; - - decode->op[0].type = X86_VAR_REG; - switch (op) { - case 0xf: - decode->op[0].reg = R_CS; - break; - case 0x17: - decode->op[0].reg = R_SS; - break; - case 0x1f: - decode->op[0].reg = R_DS; - break; - case 0x07: - decode->op[0].reg = R_ES; - break; - case 0xa1: - decode->op[0].reg = R_FS; - break; - case 0xa9: - decode->op[0].reg = R_GS; - break; - } -} - -static void decode_incgroup(CPUX86State *env, struct x86_decode *decode) -{ - decode->op[0].type = X86_VAR_REG; - decode->op[0].reg = decode->opcode[0] - 0x40; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, - decode->rex.b, decode->operand_size); -} - -static void decode_decgroup(CPUX86State *env, struct x86_decode *decode) -{ - decode->op[0].type = X86_VAR_REG; - decode->op[0].reg = decode->opcode[0] - 0x48; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, - decode->rex.b, decode->operand_size); -} - -static void decode_incgroup2(CPUX86State *env, struct x86_decode *decode) -{ - if (!decode->modrm.reg) { - decode->cmd = X86_DECODE_CMD_INC; - } else if (1 == decode->modrm.reg) { - decode->cmd = X86_DECODE_CMD_DEC; - } -} - -static void decode_pushgroup(CPUX86State *env, struct x86_decode *decode) -{ - decode->op[0].type = X86_VAR_REG; - decode->op[0].reg = decode->opcode[0] - 0x50; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, - decode->rex.b, decode->operand_size); -} - -static void decode_popgroup(CPUX86State *env, struct x86_decode *decode) -{ - decode->op[0].type = X86_VAR_REG; - decode->op[0].reg = decode->opcode[0] - 0x58; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, - decode->rex.b, decode->operand_size); -} - -static void decode_jxx(CPUX86State *env, struct x86_decode *decode) -{ - decode->displacement = decode_bytes(env, decode, decode->operand_size); - decode->displacement_size = decode->operand_size; -} - -static void decode_farjmp(CPUX86State *env, struct x86_decode *decode) -{ - decode->op[0].type = X86_VAR_IMMEDIATE; - decode->op[0].val = decode_bytes(env, decode, decode->operand_size); - decode->displacement = decode_word(env, decode); -} - -static void decode_addgroup(CPUX86State *env, struct x86_decode *decode) -{ - enum x86_decode_cmd group[] = { - X86_DECODE_CMD_ADD, - X86_DECODE_CMD_OR, - X86_DECODE_CMD_ADC, - X86_DECODE_CMD_SBB, - X86_DECODE_CMD_AND, - X86_DECODE_CMD_SUB, - X86_DECODE_CMD_XOR, - X86_DECODE_CMD_CMP - }; - decode->cmd = group[decode->modrm.reg]; -} - -static void decode_rotgroup(CPUX86State *env, struct x86_decode *decode) -{ - enum x86_decode_cmd group[] = { - X86_DECODE_CMD_ROL, - X86_DECODE_CMD_ROR, - X86_DECODE_CMD_RCL, - X86_DECODE_CMD_RCR, - X86_DECODE_CMD_SHL, - X86_DECODE_CMD_SHR, - X86_DECODE_CMD_SHL, - X86_DECODE_CMD_SAR - }; - decode->cmd = group[decode->modrm.reg]; -} - -static void decode_f7group(CPUX86State *env, struct x86_decode *decode) -{ - enum x86_decode_cmd group[] = { - X86_DECODE_CMD_TST, - X86_DECODE_CMD_TST, - X86_DECODE_CMD_NOT, - X86_DECODE_CMD_NEG, - X86_DECODE_CMD_MUL, - X86_DECODE_CMD_IMUL_1, - X86_DECODE_CMD_DIV, - X86_DECODE_CMD_IDIV - }; - decode->cmd = group[decode->modrm.reg]; - decode_modrm_rm(env, decode, &decode->op[0]); - - switch (decode->modrm.reg) { - case 0: - case 1: - decode_imm(env, decode, &decode->op[1]); - break; - case 2: - break; - case 3: - decode->op[1].type = X86_VAR_IMMEDIATE; - decode->op[1].val = 0; - break; - default: - break; - } -} - -static void decode_xchgroup(CPUX86State *env, struct x86_decode *decode) -{ - decode->op[0].type = X86_VAR_REG; - decode->op[0].reg = decode->opcode[0] - 0x90; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, - decode->rex.b, decode->operand_size); -} - -static void decode_movgroup(CPUX86State *env, struct x86_decode *decode) -{ - decode->op[0].type = X86_VAR_REG; - decode->op[0].reg = decode->opcode[0] - 0xb8; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, - decode->rex.b, decode->operand_size); - decode_immediate(env, decode, &decode->op[1], decode->operand_size); -} - -static void fetch_moffs(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - op->type = X86_VAR_OFFSET; - op->ptr = decode_bytes(env, decode, decode->addressing_size); -} - -static void decode_movgroup8(CPUX86State *env, struct x86_decode *decode) -{ - decode->op[0].type = X86_VAR_REG; - decode->op[0].reg = decode->opcode[0] - 0xb0; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, - decode->rex.b, decode->operand_size); - decode_immediate(env, decode, &decode->op[1], decode->operand_size); -} - -static void decode_rcx(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - op->type = X86_VAR_REG; - op->reg = R_ECX; - op->ptr = get_reg_ref(env, op->reg, decode->rex.rex, decode->rex.b, - decode->operand_size); -} - -struct decode_tbl { - uint8_t opcode; - enum x86_decode_cmd cmd; - uint8_t operand_size; - bool is_modrm; - void (*decode_op1)(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op1); - void (*decode_op2)(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op2); - void (*decode_op3)(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op3); - void (*decode_op4)(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op4); - void (*decode_postfix)(CPUX86State *env, struct x86_decode *decode); - uint32_t flags_mask; -}; - -struct decode_x87_tbl { - uint8_t opcode; - uint8_t modrm_reg; - uint8_t modrm_mod; - enum x86_decode_cmd cmd; - uint8_t operand_size; - bool rev; - bool pop; - void (*decode_op1)(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op1); - void (*decode_op2)(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op2); - void (*decode_postfix)(CPUX86State *env, struct x86_decode *decode); - uint32_t flags_mask; -}; - -struct decode_tbl invl_inst = {0x0, 0, 0, false, NULL, NULL, NULL, NULL, - decode_invalid}; - -struct decode_tbl _decode_tbl1[256]; -struct decode_tbl _decode_tbl2[256]; -struct decode_x87_tbl _decode_tbl3[256]; - -static void decode_x87_ins(CPUX86State *env, struct x86_decode *decode) -{ - struct decode_x87_tbl *decoder; - - decode->is_fpu = true; - int mode = decode->modrm.mod == 3 ? 1 : 0; - int index = ((decode->opcode[0] & 0xf) << 4) | (mode << 3) | - decode->modrm.reg; - - decoder = &_decode_tbl3[index]; - - decode->cmd = decoder->cmd; - if (decoder->operand_size) { - decode->operand_size = decoder->operand_size; - } - decode->flags_mask = decoder->flags_mask; - decode->fpop_stack = decoder->pop; - decode->frev = decoder->rev; - - if (decoder->decode_op1) { - decoder->decode_op1(env, decode, &decode->op[0]); - } - if (decoder->decode_op2) { - decoder->decode_op2(env, decode, &decode->op[1]); - } - if (decoder->decode_postfix) { - decoder->decode_postfix(env, decode); - } - - VM_PANIC_ON_EX(!decode->cmd, "x87 opcode %x %x (%x %x) not decoded\n", - decode->opcode[0], decode->modrm.modrm, decoder->modrm_reg, - decoder->modrm_mod); -} - -static void decode_ffgroup(CPUX86State *env, struct x86_decode *decode) -{ - enum x86_decode_cmd group[] = { - X86_DECODE_CMD_INC, - X86_DECODE_CMD_DEC, - X86_DECODE_CMD_CALL_NEAR_ABS_INDIRECT, - X86_DECODE_CMD_CALL_FAR_ABS_INDIRECT, - X86_DECODE_CMD_JMP_NEAR_ABS_INDIRECT, - X86_DECODE_CMD_JMP_FAR_ABS_INDIRECT, - X86_DECODE_CMD_PUSH, - X86_DECODE_CMD_INVL, - X86_DECODE_CMD_INVL - }; - decode->cmd = group[decode->modrm.reg]; - if (decode->modrm.reg > 2) { - decode->flags_mask = 0; - } -} - -static void decode_sldtgroup(CPUX86State *env, struct x86_decode *decode) -{ - - enum x86_decode_cmd group[] = { - X86_DECODE_CMD_SLDT, - X86_DECODE_CMD_STR, - X86_DECODE_CMD_LLDT, - X86_DECODE_CMD_LTR, - X86_DECODE_CMD_VERR, - X86_DECODE_CMD_VERW, - X86_DECODE_CMD_INVL, - X86_DECODE_CMD_INVL - }; - decode->cmd = group[decode->modrm.reg]; -} - -static void decode_lidtgroup(CPUX86State *env, struct x86_decode *decode) -{ - enum x86_decode_cmd group[] = { - X86_DECODE_CMD_SGDT, - X86_DECODE_CMD_SIDT, - X86_DECODE_CMD_LGDT, - X86_DECODE_CMD_LIDT, - X86_DECODE_CMD_SMSW, - X86_DECODE_CMD_LMSW, - X86_DECODE_CMD_LMSW, - X86_DECODE_CMD_INVLPG - }; - decode->cmd = group[decode->modrm.reg]; - if (0xf9 == decode->modrm.modrm) { - decode->opcode[decode->len++] = decode->modrm.modrm; - decode->cmd = X86_DECODE_CMD_RDTSCP; - } -} - -static void decode_btgroup(CPUX86State *env, struct x86_decode *decode) -{ - enum x86_decode_cmd group[] = { - X86_DECODE_CMD_INVL, - X86_DECODE_CMD_INVL, - X86_DECODE_CMD_INVL, - X86_DECODE_CMD_INVL, - X86_DECODE_CMD_BT, - X86_DECODE_CMD_BTS, - X86_DECODE_CMD_BTR, - X86_DECODE_CMD_BTC - }; - decode->cmd = group[decode->modrm.reg]; -} - -static void decode_x87_general(CPUX86State *env, struct x86_decode *decode) -{ - decode->is_fpu = true; -} - -static void decode_x87_modrm_floatp(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - op->type = X87_VAR_FLOATP; -} - -static void decode_x87_modrm_intp(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - op->type = X87_VAR_INTP; -} - -static void decode_x87_modrm_bytep(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - op->type = X87_VAR_BYTEP; -} - -static void decode_x87_modrm_st0(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - op->type = X87_VAR_REG; - op->reg = 0; -} - -static void decode_decode_x87_modrm_st0(CPUX86State *env, - struct x86_decode *decode, - struct x86_decode_op *op) -{ - op->type = X87_VAR_REG; - op->reg = decode->modrm.modrm & 7; -} - - -static void decode_aegroup(CPUX86State *env, struct x86_decode *decode) -{ - decode->is_fpu = true; - switch (decode->modrm.reg) { - case 0: - decode->cmd = X86_DECODE_CMD_FXSAVE; - decode_x87_modrm_bytep(env, decode, &decode->op[0]); - break; - case 1: - decode_x87_modrm_bytep(env, decode, &decode->op[0]); - decode->cmd = X86_DECODE_CMD_FXRSTOR; - break; - case 5: - if (decode->modrm.modrm == 0xe8) { - decode->cmd = X86_DECODE_CMD_LFENCE; - } else { - VM_PANIC("xrstor"); - } - break; - case 6: - VM_PANIC_ON(decode->modrm.modrm != 0xf0); - decode->cmd = X86_DECODE_CMD_MFENCE; - break; - case 7: - if (decode->modrm.modrm == 0xf8) { - decode->cmd = X86_DECODE_CMD_SFENCE; - } else { - decode->cmd = X86_DECODE_CMD_CLFLUSH; - } - break; - default: - VM_PANIC_EX("0xae: reg %d\n", decode->modrm.reg); - break; - } -} - -static void decode_bswap(CPUX86State *env, struct x86_decode *decode) -{ - decode->op[0].type = X86_VAR_REG; - decode->op[0].reg = decode->opcode[1] - 0xc8; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, - decode->rex.b, decode->operand_size); -} - -static void decode_d9_4(CPUX86State *env, struct x86_decode *decode) -{ - switch (decode->modrm.modrm) { - case 0xe0: - /* FCHS */ - decode->cmd = X86_DECODE_CMD_FCHS; - break; - case 0xe1: - decode->cmd = X86_DECODE_CMD_FABS; - break; - case 0xe4: - VM_PANIC("FTST"); - break; - case 0xe5: - /* FXAM */ - decode->cmd = X86_DECODE_CMD_FXAM; - break; - default: - VM_PANIC("FLDENV"); - break; - } -} - -static void decode_db_4(CPUX86State *env, struct x86_decode *decode) -{ - switch (decode->modrm.modrm) { - case 0xe0: - VM_PANIC_EX("unhandled FNENI: %x %x\n", decode->opcode[0], - decode->modrm.modrm); - break; - case 0xe1: - VM_PANIC_EX("unhandled FNDISI: %x %x\n", decode->opcode[0], - decode->modrm.modrm); - break; - case 0xe2: - VM_PANIC_EX("unhandled FCLEX: %x %x\n", decode->opcode[0], - decode->modrm.modrm); - break; - case 0xe3: - decode->cmd = X86_DECODE_CMD_FNINIT; - break; - case 0xe4: - decode->cmd = X86_DECODE_CMD_FNSETPM; - break; - default: - VM_PANIC_EX("unhandled fpu opcode: %x %x\n", decode->opcode[0], - decode->modrm.modrm); - break; - } -} - - -#define RFLAGS_MASK_NONE 0 -#define RFLAGS_MASK_OSZAPC (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C) -#define RFLAGS_MASK_LAHF (CC_S | CC_Z | CC_A | CC_P | CC_C) -#define RFLAGS_MASK_CF (CC_C) -#define RFLAGS_MASK_IF (IF_MASK) -#define RFLAGS_MASK_TF (TF_MASK) -#define RFLAGS_MASK_DF (DF_MASK) -#define RFLAGS_MASK_ZF (CC_Z) - -struct decode_tbl _1op_inst[] = { - {0x0, X86_DECODE_CMD_ADD, 1, true, decode_modrm_rm, decode_modrm_reg, NULL, - NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x1, X86_DECODE_CMD_ADD, 0, true, decode_modrm_rm, decode_modrm_reg, NULL, - NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x2, X86_DECODE_CMD_ADD, 1, true, decode_modrm_reg, decode_modrm_rm, NULL, - NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x3, X86_DECODE_CMD_ADD, 0, true, decode_modrm_reg, decode_modrm_rm, NULL, - NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x4, X86_DECODE_CMD_ADD, 1, false, decode_rax, decode_imm8, NULL, NULL, - NULL, RFLAGS_MASK_OSZAPC}, - {0x5, X86_DECODE_CMD_ADD, 0, false, decode_rax, decode_imm, NULL, NULL, - NULL, RFLAGS_MASK_OSZAPC}, - {0x6, X86_DECODE_CMD_PUSH_SEG, 0, false, false, NULL, NULL, NULL, - decode_pushseg, RFLAGS_MASK_NONE}, - {0x7, X86_DECODE_CMD_POP_SEG, 0, false, false, NULL, NULL, NULL, - decode_popseg, RFLAGS_MASK_NONE}, - {0x8, X86_DECODE_CMD_OR, 1, true, decode_modrm_rm, decode_modrm_reg, NULL, - NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x9, X86_DECODE_CMD_OR, 0, true, decode_modrm_rm, decode_modrm_reg, NULL, - NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xa, X86_DECODE_CMD_OR, 1, true, decode_modrm_reg, decode_modrm_rm, NULL, - NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xb, X86_DECODE_CMD_OR, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xc, X86_DECODE_CMD_OR, 1, false, decode_rax, decode_imm8, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xd, X86_DECODE_CMD_OR, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - - {0xe, X86_DECODE_CMD_PUSH_SEG, 0, false, false, - NULL, NULL, NULL, decode_pushseg, RFLAGS_MASK_NONE}, - {0xf, X86_DECODE_CMD_POP_SEG, 0, false, false, - NULL, NULL, NULL, decode_popseg, RFLAGS_MASK_NONE}, - - {0x10, X86_DECODE_CMD_ADC, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x11, X86_DECODE_CMD_ADC, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x12, X86_DECODE_CMD_ADC, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x13, X86_DECODE_CMD_ADC, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x14, X86_DECODE_CMD_ADC, 1, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x15, X86_DECODE_CMD_ADC, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - - {0x16, X86_DECODE_CMD_PUSH_SEG, 0, false, false, - NULL, NULL, NULL, decode_pushseg, RFLAGS_MASK_NONE}, - {0x17, X86_DECODE_CMD_POP_SEG, 0, false, false, - NULL, NULL, NULL, decode_popseg, RFLAGS_MASK_NONE}, - - {0x18, X86_DECODE_CMD_SBB, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x19, X86_DECODE_CMD_SBB, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x1a, X86_DECODE_CMD_SBB, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x1b, X86_DECODE_CMD_SBB, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x1c, X86_DECODE_CMD_SBB, 1, false, decode_rax, decode_imm8, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x1d, X86_DECODE_CMD_SBB, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - - {0x1e, X86_DECODE_CMD_PUSH_SEG, 0, false, false, - NULL, NULL, NULL, decode_pushseg, RFLAGS_MASK_NONE}, - {0x1f, X86_DECODE_CMD_POP_SEG, 0, false, false, - NULL, NULL, NULL, decode_popseg, RFLAGS_MASK_NONE}, - - {0x20, X86_DECODE_CMD_AND, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x21, X86_DECODE_CMD_AND, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x22, X86_DECODE_CMD_AND, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x23, X86_DECODE_CMD_AND, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x24, X86_DECODE_CMD_AND, 1, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x25, X86_DECODE_CMD_AND, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x28, X86_DECODE_CMD_SUB, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x29, X86_DECODE_CMD_SUB, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x2a, X86_DECODE_CMD_SUB, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x2b, X86_DECODE_CMD_SUB, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x2c, X86_DECODE_CMD_SUB, 1, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x2d, X86_DECODE_CMD_SUB, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x2f, X86_DECODE_CMD_DAS, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x30, X86_DECODE_CMD_XOR, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x31, X86_DECODE_CMD_XOR, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x32, X86_DECODE_CMD_XOR, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x33, X86_DECODE_CMD_XOR, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x34, X86_DECODE_CMD_XOR, 1, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x35, X86_DECODE_CMD_XOR, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - - {0x38, X86_DECODE_CMD_CMP, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x39, X86_DECODE_CMD_CMP, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x3a, X86_DECODE_CMD_CMP, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x3b, X86_DECODE_CMD_CMP, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x3c, X86_DECODE_CMD_CMP, 1, false, decode_rax, decode_imm8, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x3d, X86_DECODE_CMD_CMP, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - - {0x3f, X86_DECODE_CMD_AAS, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - - {0x40, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, - {0x41, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, - {0x42, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, - {0x43, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, - {0x44, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, - {0x45, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, - {0x46, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, - {0x47, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, - - {0x48, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, - {0x49, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, - {0x4a, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, - {0x4b, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, - {0x4c, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, - {0x4d, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, - {0x4e, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, - {0x4f, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, - - {0x50, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, - {0x51, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, - {0x52, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, - {0x53, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, - {0x54, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, - {0x55, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, - {0x56, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, - {0x57, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, - - {0x58, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, - {0x59, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, - {0x5a, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, - {0x5b, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, - {0x5c, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, - {0x5d, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, - {0x5e, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, - {0x5f, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, - - {0x60, X86_DECODE_CMD_PUSHA, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x61, X86_DECODE_CMD_POPA, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - - {0x68, X86_DECODE_CMD_PUSH, 0, false, decode_imm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x6a, X86_DECODE_CMD_PUSH, 0, false, decode_imm8_signed, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x69, X86_DECODE_CMD_IMUL_3, 0, true, decode_modrm_reg, - decode_modrm_rm, decode_imm, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x6b, X86_DECODE_CMD_IMUL_3, 0, true, decode_modrm_reg, decode_modrm_rm, - decode_imm8_signed, NULL, NULL, RFLAGS_MASK_OSZAPC}, - - {0x6c, X86_DECODE_CMD_INS, 1, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x6d, X86_DECODE_CMD_INS, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x6e, X86_DECODE_CMD_OUTS, 1, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x6f, X86_DECODE_CMD_OUTS, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - - {0x70, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x71, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x72, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x73, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x74, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x75, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x76, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x77, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x78, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x79, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x7a, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x7b, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x7c, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x7d, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x7e, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x7f, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - - {0x80, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm8, - NULL, NULL, decode_addgroup, RFLAGS_MASK_OSZAPC}, - {0x81, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm, - NULL, NULL, decode_addgroup, RFLAGS_MASK_OSZAPC}, - {0x82, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm8, - NULL, NULL, decode_addgroup, RFLAGS_MASK_OSZAPC}, - {0x83, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm8_signed, - NULL, NULL, decode_addgroup, RFLAGS_MASK_OSZAPC}, - {0x84, X86_DECODE_CMD_TST, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x85, X86_DECODE_CMD_TST, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0x86, X86_DECODE_CMD_XCHG, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x87, X86_DECODE_CMD_XCHG, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x88, X86_DECODE_CMD_MOV, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x89, X86_DECODE_CMD_MOV, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x8a, X86_DECODE_CMD_MOV, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x8b, X86_DECODE_CMD_MOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x8c, X86_DECODE_CMD_MOV_FROM_SEG, 0, true, decode_modrm_rm, - decode_modrm_reg, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x8d, X86_DECODE_CMD_LEA, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x8e, X86_DECODE_CMD_MOV_TO_SEG, 0, true, decode_modrm_reg, - decode_modrm_rm, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x8f, X86_DECODE_CMD_POP, 0, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - - {0x90, X86_DECODE_CMD_NOP, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x91, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, - NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE}, - {0x92, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, - NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE}, - {0x93, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, - NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE}, - {0x94, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, - NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE}, - {0x95, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, - NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE}, - {0x96, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, - NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE}, - {0x97, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, - NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE}, - - {0x98, X86_DECODE_CMD_CBW, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x99, X86_DECODE_CMD_CWD, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - - {0x9a, X86_DECODE_CMD_CALL_FAR, 0, false, NULL, - NULL, NULL, NULL, decode_farjmp, RFLAGS_MASK_NONE}, - - {0x9c, X86_DECODE_CMD_PUSHF, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - /*{0x9d, X86_DECODE_CMD_POPF, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_POPF},*/ - {0x9e, X86_DECODE_CMD_SAHF, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x9f, X86_DECODE_CMD_LAHF, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_LAHF}, - - {0xa0, X86_DECODE_CMD_MOV, 1, false, decode_rax, fetch_moffs, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xa1, X86_DECODE_CMD_MOV, 0, false, decode_rax, fetch_moffs, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xa2, X86_DECODE_CMD_MOV, 1, false, fetch_moffs, decode_rax, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xa3, X86_DECODE_CMD_MOV, 0, false, fetch_moffs, decode_rax, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - - {0xa4, X86_DECODE_CMD_MOVS, 1, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xa5, X86_DECODE_CMD_MOVS, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xa6, X86_DECODE_CMD_CMPS, 1, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xa7, X86_DECODE_CMD_CMPS, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xaa, X86_DECODE_CMD_STOS, 1, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xab, X86_DECODE_CMD_STOS, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xac, X86_DECODE_CMD_LODS, 1, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xad, X86_DECODE_CMD_LODS, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xae, X86_DECODE_CMD_SCAS, 1, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xaf, X86_DECODE_CMD_SCAS, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - - {0xa8, X86_DECODE_CMD_TST, 1, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xa9, X86_DECODE_CMD_TST, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - - {0xb0, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, - {0xb1, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, - {0xb2, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, - {0xb3, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, - {0xb4, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, - {0xb5, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, - {0xb6, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, - {0xb7, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, - - {0xb8, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, - {0xb9, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, - {0xba, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, - {0xbb, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, - {0xbc, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, - {0xbd, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, - {0xbe, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, - {0xbf, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, - - {0xc0, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm8, - NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC}, - {0xc1, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm8, - NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC}, - - {0xc2, X86_DECODE_RET_NEAR, 0, false, decode_imm16, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xc3, X86_DECODE_RET_NEAR, 0, false, NULL, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - - {0xc4, X86_DECODE_CMD_LES, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xc5, X86_DECODE_CMD_LDS, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - - {0xc6, X86_DECODE_CMD_MOV, 1, true, decode_modrm_rm, decode_imm8, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xc7, X86_DECODE_CMD_MOV, 0, true, decode_modrm_rm, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - - {0xc8, X86_DECODE_CMD_ENTER, 0, false, decode_imm16, decode_imm8, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xc9, X86_DECODE_CMD_LEAVE, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xca, X86_DECODE_RET_FAR, 0, false, decode_imm16, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xcb, X86_DECODE_RET_FAR, 0, false, decode_imm_0, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xcd, X86_DECODE_CMD_INT, 0, false, decode_imm8, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - /*{0xcf, X86_DECODE_CMD_IRET, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_IRET},*/ - - {0xd0, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm_1, - NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC}, - {0xd1, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm_1, - NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC}, - {0xd2, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_rcx, - NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC}, - {0xd3, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_rcx, - NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC}, - - {0xd4, X86_DECODE_CMD_AAM, 0, false, decode_imm8, - NULL, NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xd5, X86_DECODE_CMD_AAD, 0, false, decode_imm8, - NULL, NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - - {0xd7, X86_DECODE_CMD_XLAT, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - - {0xd8, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, - {0xd9, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, - {0xda, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, - {0xdb, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, - {0xdc, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, - {0xdd, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, - {0xde, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, - {0xdf, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, - - {0xe0, X86_DECODE_CMD_LOOP, 0, false, decode_imm8_signed, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xe1, X86_DECODE_CMD_LOOP, 0, false, decode_imm8_signed, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xe2, X86_DECODE_CMD_LOOP, 0, false, decode_imm8_signed, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - - {0xe3, X86_DECODE_CMD_JCXZ, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - - {0xe4, X86_DECODE_CMD_IN, 1, false, decode_imm8, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xe5, X86_DECODE_CMD_IN, 0, false, decode_imm8, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xe6, X86_DECODE_CMD_OUT, 1, false, decode_imm8, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xe7, X86_DECODE_CMD_OUT, 0, false, decode_imm8, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xe8, X86_DECODE_CMD_CALL_NEAR, 0, false, decode_imm_signed, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xe9, X86_DECODE_CMD_JMP_NEAR, 0, false, decode_imm_signed, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xea, X86_DECODE_CMD_JMP_FAR, 0, false, - NULL, NULL, NULL, NULL, decode_farjmp, RFLAGS_MASK_NONE}, - {0xeb, X86_DECODE_CMD_JMP_NEAR, 1, false, decode_imm8_signed, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xec, X86_DECODE_CMD_IN, 1, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xed, X86_DECODE_CMD_IN, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xee, X86_DECODE_CMD_OUT, 1, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xef, X86_DECODE_CMD_OUT, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - - {0xf4, X86_DECODE_CMD_HLT, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - - {0xf5, X86_DECODE_CMD_CMC, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_CF}, - - {0xf6, X86_DECODE_CMD_INVL, 1, true, - NULL, NULL, NULL, NULL, decode_f7group, RFLAGS_MASK_OSZAPC}, - {0xf7, X86_DECODE_CMD_INVL, 0, true, - NULL, NULL, NULL, NULL, decode_f7group, RFLAGS_MASK_OSZAPC}, - - {0xf8, X86_DECODE_CMD_CLC, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_CF}, - {0xf9, X86_DECODE_CMD_STC, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_CF}, - - {0xfa, X86_DECODE_CMD_CLI, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_IF}, - {0xfb, X86_DECODE_CMD_STI, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_IF}, - {0xfc, X86_DECODE_CMD_CLD, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_DF}, - {0xfd, X86_DECODE_CMD_STD, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_DF}, - {0xfe, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, - NULL, NULL, NULL, decode_incgroup2, RFLAGS_MASK_OSZAPC}, - {0xff, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, - NULL, NULL, NULL, decode_ffgroup, RFLAGS_MASK_OSZAPC}, -}; - -struct decode_tbl _2op_inst[] = { - {0x0, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, - NULL, NULL, NULL, decode_sldtgroup, RFLAGS_MASK_NONE}, - {0x1, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, - NULL, NULL, NULL, decode_lidtgroup, RFLAGS_MASK_NONE}, - {0x6, X86_DECODE_CMD_CLTS, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_TF}, - {0x9, X86_DECODE_CMD_WBINVD, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x18, X86_DECODE_CMD_PREFETCH, 0, true, - NULL, NULL, NULL, NULL, decode_x87_general, RFLAGS_MASK_NONE}, - {0x1f, X86_DECODE_CMD_NOP, 0, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x20, X86_DECODE_CMD_MOV_FROM_CR, 0, true, decode_modrm_rm, - decode_modrm_reg, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x21, X86_DECODE_CMD_MOV_FROM_DR, 0, true, decode_modrm_rm, - decode_modrm_reg, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x22, X86_DECODE_CMD_MOV_TO_CR, 0, true, decode_modrm_reg, - decode_modrm_rm, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x23, X86_DECODE_CMD_MOV_TO_DR, 0, true, decode_modrm_reg, - decode_modrm_rm, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x30, X86_DECODE_CMD_WRMSR, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x31, X86_DECODE_CMD_RDTSC, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x32, X86_DECODE_CMD_RDMSR, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x40, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x41, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x42, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x43, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x44, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x45, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x46, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x47, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x48, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x49, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x4a, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x4b, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x4c, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x4d, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x4e, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x4f, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x77, X86_DECODE_CMD_EMMS, 0, false, - NULL, NULL, NULL, NULL, decode_x87_general, RFLAGS_MASK_NONE}, - {0x82, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x83, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x84, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x85, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x86, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x87, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x88, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x89, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x8a, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x8b, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x8c, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x8d, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x8e, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x8f, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, - {0x90, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x91, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x92, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x93, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x94, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x95, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x96, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x97, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x98, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x99, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x9a, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x9b, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x9c, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x9d, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x9e, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0x9f, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - - {0xb0, X86_DECODE_CMD_CMPXCHG, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xb1, X86_DECODE_CMD_CMPXCHG, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - - {0xb6, X86_DECODE_CMD_MOVZX, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xb7, X86_DECODE_CMD_MOVZX, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xb8, X86_DECODE_CMD_POPCNT, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xbe, X86_DECODE_CMD_MOVSX, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xbf, X86_DECODE_CMD_MOVSX, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xa0, X86_DECODE_CMD_PUSH_SEG, 0, false, false, - NULL, NULL, NULL, decode_pushseg, RFLAGS_MASK_NONE}, - {0xa1, X86_DECODE_CMD_POP_SEG, 0, false, false, - NULL, NULL, NULL, decode_popseg, RFLAGS_MASK_NONE}, - {0xa2, X86_DECODE_CMD_CPUID, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xa3, X86_DECODE_CMD_BT, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_CF}, - {0xa4, X86_DECODE_CMD_SHLD, 0, true, decode_modrm_rm, decode_modrm_reg, - decode_imm8, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xa5, X86_DECODE_CMD_SHLD, 0, true, decode_modrm_rm, decode_modrm_reg, - decode_rcx, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xa8, X86_DECODE_CMD_PUSH_SEG, 0, false, false, - NULL, NULL, NULL, decode_pushseg, RFLAGS_MASK_NONE}, - {0xa9, X86_DECODE_CMD_POP_SEG, 0, false, false, - NULL, NULL, NULL, decode_popseg, RFLAGS_MASK_NONE}, - {0xab, X86_DECODE_CMD_BTS, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_CF}, - {0xac, X86_DECODE_CMD_SHRD, 0, true, decode_modrm_rm, decode_modrm_reg, - decode_imm8, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xad, X86_DECODE_CMD_SHRD, 0, true, decode_modrm_rm, decode_modrm_reg, - decode_rcx, NULL, NULL, RFLAGS_MASK_OSZAPC}, - - {0xae, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, - NULL, NULL, NULL, decode_aegroup, RFLAGS_MASK_NONE}, - - {0xaf, X86_DECODE_CMD_IMUL_2, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xb2, X86_DECODE_CMD_LSS, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, - {0xb3, X86_DECODE_CMD_BTR, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xba, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm8, - NULL, NULL, decode_btgroup, RFLAGS_MASK_OSZAPC}, - {0xbb, X86_DECODE_CMD_BTC, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xbc, X86_DECODE_CMD_BSF, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - {0xbd, X86_DECODE_CMD_BSR, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - - {0xc1, X86_DECODE_CMD_XADD, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, - - {0xc7, X86_DECODE_CMD_CMPXCHG8B, 0, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_ZF}, - - {0xc8, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, - {0xc9, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, - {0xca, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, - {0xcb, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, - {0xcc, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, - {0xcd, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, - {0xce, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, - {0xcf, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, -}; - -struct decode_x87_tbl invl_inst_x87 = {0x0, 0, 0, 0, 0, false, false, NULL, - NULL, decode_invalid, 0}; - -struct decode_x87_tbl _x87_inst[] = { - {0xd8, 0, 3, X86_DECODE_CMD_FADD, 10, false, false, - decode_x87_modrm_st0, decode_decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xd8, 0, 0, X86_DECODE_CMD_FADD, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, - {0xd8, 1, 3, X86_DECODE_CMD_FMUL, 10, false, false, decode_x87_modrm_st0, - decode_decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xd8, 1, 0, X86_DECODE_CMD_FMUL, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, - {0xd8, 4, 3, X86_DECODE_CMD_FSUB, 10, false, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xd8, 4, 0, X86_DECODE_CMD_FSUB, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, - {0xd8, 5, 3, X86_DECODE_CMD_FSUB, 10, true, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xd8, 5, 0, X86_DECODE_CMD_FSUB, 4, true, false, decode_x87_modrm_st0, - decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, - {0xd8, 6, 3, X86_DECODE_CMD_FDIV, 10, false, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xd8, 6, 0, X86_DECODE_CMD_FDIV, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, - {0xd8, 7, 3, X86_DECODE_CMD_FDIV, 10, true, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xd8, 7, 0, X86_DECODE_CMD_FDIV, 4, true, false, decode_x87_modrm_st0, - decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, - - {0xd9, 0, 3, X86_DECODE_CMD_FLD, 10, false, false, - decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE}, - {0xd9, 0, 0, X86_DECODE_CMD_FLD, 4, false, false, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, - {0xd9, 1, 3, X86_DECODE_CMD_FXCH, 10, false, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xd9, 1, 0, X86_DECODE_CMD_INVL, 10, false, false, - decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE}, - {0xd9, 2, 3, X86_DECODE_CMD_INVL, 10, false, false, - decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE}, - {0xd9, 2, 0, X86_DECODE_CMD_FST, 4, false, false, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, - {0xd9, 3, 3, X86_DECODE_CMD_INVL, 10, false, false, - decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE}, - {0xd9, 3, 0, X86_DECODE_CMD_FST, 4, false, true, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, - {0xd9, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, - decode_x87_modrm_st0, NULL, decode_d9_4, RFLAGS_MASK_NONE}, - {0xd9, 4, 0, X86_DECODE_CMD_INVL, 4, false, false, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, - {0xd9, 5, 3, X86_DECODE_CMD_FLDxx, 10, false, false, NULL, NULL, NULL, - RFLAGS_MASK_NONE}, - {0xd9, 5, 0, X86_DECODE_CMD_FLDCW, 2, false, false, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, - - {0xd9, 7, 3, X86_DECODE_CMD_FNSTCW, 2, false, false, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, - {0xd9, 7, 0, X86_DECODE_CMD_FNSTCW, 2, false, false, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, - - {0xda, 0, 3, X86_DECODE_CMD_FCMOV, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xda, 0, 0, X86_DECODE_CMD_FADD, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, - {0xda, 1, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0, - decode_decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xda, 1, 0, X86_DECODE_CMD_FMUL, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, - {0xda, 2, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xda, 3, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xda, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, - RFLAGS_MASK_NONE}, - {0xda, 4, 0, X86_DECODE_CMD_FSUB, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, - {0xda, 5, 3, X86_DECODE_CMD_FUCOM, 10, false, true, decode_x87_modrm_st0, - decode_decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xda, 5, 0, X86_DECODE_CMD_FSUB, 4, true, false, decode_x87_modrm_st0, - decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, - {0xda, 6, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, - RFLAGS_MASK_NONE}, - {0xda, 6, 0, X86_DECODE_CMD_FDIV, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, - {0xda, 7, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, - RFLAGS_MASK_NONE}, - {0xda, 7, 0, X86_DECODE_CMD_FDIV, 4, true, false, decode_x87_modrm_st0, - decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, - - {0xdb, 0, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdb, 0, 0, X86_DECODE_CMD_FLD, 4, false, false, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdb, 1, 3, X86_DECODE_CMD_FCMOV, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdb, 2, 3, X86_DECODE_CMD_FCMOV, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdb, 2, 0, X86_DECODE_CMD_FST, 4, false, false, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdb, 3, 3, X86_DECODE_CMD_FCMOV, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdb, 3, 0, X86_DECODE_CMD_FST, 4, false, true, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdb, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, - decode_db_4, RFLAGS_MASK_NONE}, - {0xdb, 4, 0, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, - RFLAGS_MASK_NONE}, - {0xdb, 5, 3, X86_DECODE_CMD_FUCOMI, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdb, 5, 0, X86_DECODE_CMD_FLD, 10, false, false, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdb, 7, 0, X86_DECODE_CMD_FST, 10, false, true, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, - - {0xdc, 0, 3, X86_DECODE_CMD_FADD, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdc, 0, 0, X86_DECODE_CMD_FADD, 8, false, false, - decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, - {0xdc, 1, 3, X86_DECODE_CMD_FMUL, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdc, 1, 0, X86_DECODE_CMD_FMUL, 8, false, false, - decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, - {0xdc, 4, 3, X86_DECODE_CMD_FSUB, 10, true, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdc, 4, 0, X86_DECODE_CMD_FSUB, 8, false, false, - decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, - {0xdc, 5, 3, X86_DECODE_CMD_FSUB, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdc, 5, 0, X86_DECODE_CMD_FSUB, 8, true, false, - decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, - {0xdc, 6, 3, X86_DECODE_CMD_FDIV, 10, true, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdc, 6, 0, X86_DECODE_CMD_FDIV, 8, false, false, - decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, - {0xdc, 7, 3, X86_DECODE_CMD_FDIV, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdc, 7, 0, X86_DECODE_CMD_FDIV, 8, true, false, - decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, - - {0xdd, 0, 0, X86_DECODE_CMD_FLD, 8, false, false, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdd, 1, 3, X86_DECODE_CMD_FXCH, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdd, 2, 3, X86_DECODE_CMD_FST, 10, false, false, - decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdd, 2, 0, X86_DECODE_CMD_FST, 8, false, false, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdd, 3, 3, X86_DECODE_CMD_FST, 10, false, true, - decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdd, 3, 0, X86_DECODE_CMD_FST, 8, false, true, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdd, 4, 3, X86_DECODE_CMD_FUCOM, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdd, 4, 0, X86_DECODE_CMD_FRSTOR, 8, false, false, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdd, 5, 3, X86_DECODE_CMD_FUCOM, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdd, 7, 0, X86_DECODE_CMD_FNSTSW, 0, false, false, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdd, 7, 3, X86_DECODE_CMD_FNSTSW, 0, false, false, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, - - {0xde, 0, 3, X86_DECODE_CMD_FADD, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xde, 0, 0, X86_DECODE_CMD_FADD, 2, false, false, - decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, - {0xde, 1, 3, X86_DECODE_CMD_FMUL, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xde, 1, 0, X86_DECODE_CMD_FMUL, 2, false, false, - decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, - {0xde, 4, 3, X86_DECODE_CMD_FSUB, 10, true, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xde, 4, 0, X86_DECODE_CMD_FSUB, 2, false, false, - decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, - {0xde, 5, 3, X86_DECODE_CMD_FSUB, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xde, 5, 0, X86_DECODE_CMD_FSUB, 2, true, false, - decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, - {0xde, 6, 3, X86_DECODE_CMD_FDIV, 10, true, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xde, 6, 0, X86_DECODE_CMD_FDIV, 2, false, false, - decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, - {0xde, 7, 3, X86_DECODE_CMD_FDIV, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xde, 7, 0, X86_DECODE_CMD_FDIV, 2, true, false, - decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, - - {0xdf, 0, 0, X86_DECODE_CMD_FLD, 2, false, false, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdf, 1, 3, X86_DECODE_CMD_FXCH, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdf, 2, 3, X86_DECODE_CMD_FST, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdf, 2, 0, X86_DECODE_CMD_FST, 2, false, false, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdf, 3, 3, X86_DECODE_CMD_FST, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdf, 3, 0, X86_DECODE_CMD_FST, 2, false, true, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdf, 4, 3, X86_DECODE_CMD_FNSTSW, 2, false, true, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdf, 5, 3, X86_DECODE_CMD_FUCOMI, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, - {0xdf, 5, 0, X86_DECODE_CMD_FLD, 8, false, false, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, - {0xdf, 7, 0, X86_DECODE_CMD_FST, 8, false, true, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, -}; - -void calc_modrm_operand16(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - target_ulong ptr = 0; - X86Seg seg = R_DS; - - if (!decode->modrm.mod && 6 == decode->modrm.rm) { - ptr = decode->displacement; - goto calc_addr; - } - - if (decode->displacement_size) { - ptr = sign(decode->displacement, decode->displacement_size); - } - - switch (decode->modrm.rm) { - case 0: - ptr += BX(env) + SI(env); - break; - case 1: - ptr += BX(env) + DI(env); - break; - case 2: - ptr += BP(env) + SI(env); - seg = R_SS; - break; - case 3: - ptr += BP(env) + DI(env); - seg = R_SS; - break; - case 4: - ptr += SI(env); - break; - case 5: - ptr += DI(env); - break; - case 6: - ptr += BP(env); - seg = R_SS; - break; - case 7: - ptr += BX(env); - break; - } -calc_addr: - if (X86_DECODE_CMD_LEA == decode->cmd) { - op->ptr = (uint16_t)ptr; - } else { - op->ptr = decode_linear_addr(env, decode, (uint16_t)ptr, seg); - } -} - -target_ulong get_reg_ref(CPUX86State *env, int reg, int rex_present, - int is_extended, int size) -{ - target_ulong ptr = 0; - - if (is_extended) { - reg |= R_R8; - } - - switch (size) { - case 1: - if (is_extended || reg < 4 || rex_present) { - ptr = (target_ulong)&RL(env, reg); - } else { - ptr = (target_ulong)&RH(env, reg - 4); - } - break; - default: - ptr = (target_ulong)&RRX(env, reg); - break; - } - return ptr; -} - -target_ulong get_reg_val(CPUX86State *env, int reg, int rex_present, - int is_extended, int size) -{ - target_ulong val = 0; - memcpy(&val, - (void *)get_reg_ref(env, reg, rex_present, is_extended, size), - size); - return val; -} - -static target_ulong get_sib_val(CPUX86State *env, struct x86_decode *decode, - X86Seg *sel) -{ - target_ulong base = 0; - target_ulong scaled_index = 0; - int addr_size = decode->addressing_size; - int base_reg = decode->sib.base; - int index_reg = decode->sib.index; - - *sel = R_DS; - - if (decode->modrm.mod || base_reg != R_EBP) { - if (decode->rex.b) { - base_reg |= R_R8; - } - if (base_reg == R_ESP || base_reg == R_EBP) { - *sel = R_SS; - } - base = get_reg_val(env, decode->sib.base, decode->rex.rex, - decode->rex.b, addr_size); - } - - if (decode->rex.x) { - index_reg |= R_R8; - } - - if (index_reg != R_ESP) { - scaled_index = get_reg_val(env, index_reg, decode->rex.rex, - decode->rex.x, addr_size) << - decode->sib.scale; - } - return base + scaled_index; -} - -void calc_modrm_operand32(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - X86Seg seg = R_DS; - target_ulong ptr = 0; - int addr_size = decode->addressing_size; - - if (decode->displacement_size) { - ptr = sign(decode->displacement, decode->displacement_size); - } - - if (4 == decode->modrm.rm) { - ptr += get_sib_val(env, decode, &seg); - } else if (!decode->modrm.mod && 5 == decode->modrm.rm) { - if (x86_is_long_mode(env_cpu(env))) { - ptr += env->eip + decode->len; - } else { - ptr = decode->displacement; - } - } else { - if (decode->modrm.rm == R_EBP || decode->modrm.rm == R_ESP) { - seg = R_SS; - } - ptr += get_reg_val(env, decode->modrm.rm, decode->rex.rex, - decode->rex.b, addr_size); - } - - if (X86_DECODE_CMD_LEA == decode->cmd) { - op->ptr = (uint32_t)ptr; - } else { - op->ptr = decode_linear_addr(env, decode, (uint32_t)ptr, seg); - } -} - -void calc_modrm_operand64(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - X86Seg seg = R_DS; - int32_t offset = 0; - int mod = decode->modrm.mod; - int rm = decode->modrm.rm; - target_ulong ptr; - int src = decode->modrm.rm; - - if (decode->displacement_size) { - offset = sign(decode->displacement, decode->displacement_size); - } - - if (4 == rm) { - ptr = get_sib_val(env, decode, &seg) + offset; - } else if (0 == mod && 5 == rm) { - ptr = env->eip + decode->len + (int32_t) offset; - } else { - ptr = get_reg_val(env, src, decode->rex.rex, decode->rex.b, 8) + - (int64_t) offset; - } - - if (X86_DECODE_CMD_LEA == decode->cmd) { - op->ptr = ptr; - } else { - op->ptr = decode_linear_addr(env, decode, ptr, seg); - } -} - - -void calc_modrm_operand(CPUX86State *env, struct x86_decode *decode, - struct x86_decode_op *op) -{ - if (3 == decode->modrm.mod) { - op->reg = decode->modrm.reg; - op->type = X86_VAR_REG; - op->ptr = get_reg_ref(env, decode->modrm.rm, decode->rex.rex, - decode->rex.b, decode->operand_size); - return; - } - - switch (decode->addressing_size) { - case 2: - calc_modrm_operand16(env, decode, op); - break; - case 4: - calc_modrm_operand32(env, decode, op); - break; - case 8: - calc_modrm_operand64(env, decode, op); - break; - default: - VM_PANIC_EX("unsupported address size %d\n", decode->addressing_size); - break; - } -} - -static void decode_prefix(CPUX86State *env, struct x86_decode *decode) -{ - while (1) { - /* - * REX prefix must come after legacy prefixes. - * REX before legacy is ignored. - * Clear rex to simulate this. - */ - uint8_t byte = decode_byte(env, decode); - switch (byte) { - case PREFIX_LOCK: - decode->lock = byte; - decode->rex.rex = 0; - break; - case PREFIX_REPN: - case PREFIX_REP: - decode->rep = byte; - decode->rex.rex = 0; - break; - case PREFIX_CS_SEG_OVERRIDE: - case PREFIX_SS_SEG_OVERRIDE: - case PREFIX_DS_SEG_OVERRIDE: - case PREFIX_ES_SEG_OVERRIDE: - case PREFIX_FS_SEG_OVERRIDE: - case PREFIX_GS_SEG_OVERRIDE: - decode->segment_override = byte; - decode->rex.rex = 0; - break; - case PREFIX_OP_SIZE_OVERRIDE: - decode->op_size_override = byte; - decode->rex.rex = 0; - break; - case PREFIX_ADDR_SIZE_OVERRIDE: - decode->addr_size_override = byte; - decode->rex.rex = 0; - break; - case PREFIX_REX ... (PREFIX_REX + 0xf): - if (x86_is_long_mode(env_cpu(env))) { - decode->rex.rex = byte; - break; - } - /* fall through when not in long mode */ - default: - decode->len--; - return; - } - } -} - -void set_addressing_size(CPUX86State *env, struct x86_decode *decode) -{ - decode->addressing_size = -1; - if (x86_is_real(env_cpu(env)) || x86_is_v8086(env_cpu(env))) { - if (decode->addr_size_override) { - decode->addressing_size = 4; - } else { - decode->addressing_size = 2; - } - } else if (!x86_is_long_mode(env_cpu(env))) { - /* protected */ - struct vmx_segment cs; - vmx_read_segment_descriptor(env_cpu(env), &cs, R_CS); - /* check db */ - if ((cs.ar >> 14) & 1) { - if (decode->addr_size_override) { - decode->addressing_size = 2; - } else { - decode->addressing_size = 4; - } - } else { - if (decode->addr_size_override) { - decode->addressing_size = 4; - } else { - decode->addressing_size = 2; - } - } - } else { - /* long */ - if (decode->addr_size_override) { - decode->addressing_size = 4; - } else { - decode->addressing_size = 8; - } - } -} - -void set_operand_size(CPUX86State *env, struct x86_decode *decode) -{ - decode->operand_size = -1; - if (x86_is_real(env_cpu(env)) || x86_is_v8086(env_cpu(env))) { - if (decode->op_size_override) { - decode->operand_size = 4; - } else { - decode->operand_size = 2; - } - } else if (!x86_is_long_mode(env_cpu(env))) { - /* protected */ - struct vmx_segment cs; - vmx_read_segment_descriptor(env_cpu(env), &cs, R_CS); - /* check db */ - if ((cs.ar >> 14) & 1) { - if (decode->op_size_override) { - decode->operand_size = 2; - } else{ - decode->operand_size = 4; - } - } else { - if (decode->op_size_override) { - decode->operand_size = 4; - } else { - decode->operand_size = 2; - } - } - } else { - /* long */ - if (decode->op_size_override) { - decode->operand_size = 2; - } else { - decode->operand_size = 4; - } - - if (decode->rex.w) { - decode->operand_size = 8; - } - } -} - -static void decode_sib(CPUX86State *env, struct x86_decode *decode) -{ - if ((decode->modrm.mod != 3) && (4 == decode->modrm.rm) && - (decode->addressing_size != 2)) { - decode->sib.sib = decode_byte(env, decode); - decode->sib_present = true; - } -} - -/* 16 bit modrm */ -int disp16_tbl[4][8] = { - {0, 0, 0, 0, 0, 0, 2, 0}, - {1, 1, 1, 1, 1, 1, 1, 1}, - {2, 2, 2, 2, 2, 2, 2, 2}, - {0, 0, 0, 0, 0, 0, 0, 0} -}; - -/* 32/64-bit modrm */ -int disp32_tbl[4][8] = { - {0, 0, 0, 0, -1, 4, 0, 0}, - {1, 1, 1, 1, 1, 1, 1, 1}, - {4, 4, 4, 4, 4, 4, 4, 4}, - {0, 0, 0, 0, 0, 0, 0, 0} -}; - -static inline void decode_displacement(CPUX86State *env, struct x86_decode *decode) -{ - int addressing_size = decode->addressing_size; - int mod = decode->modrm.mod; - int rm = decode->modrm.rm; - - decode->displacement_size = 0; - switch (addressing_size) { - case 2: - decode->displacement_size = disp16_tbl[mod][rm]; - if (decode->displacement_size) { - decode->displacement = (uint16_t)decode_bytes(env, decode, - decode->displacement_size); - } - break; - case 4: - case 8: - if (-1 == disp32_tbl[mod][rm]) { - if (5 == decode->sib.base) { - decode->displacement_size = 4; - } - } else { - decode->displacement_size = disp32_tbl[mod][rm]; - } - - if (decode->displacement_size) { - decode->displacement = (uint32_t)decode_bytes(env, decode, - decode->displacement_size); - } - break; - } -} - -static inline void decode_modrm(CPUX86State *env, struct x86_decode *decode) -{ - decode->modrm.modrm = decode_byte(env, decode); - decode->is_modrm = true; - - decode_sib(env, decode); - decode_displacement(env, decode); -} - -static inline void decode_opcode_general(CPUX86State *env, - struct x86_decode *decode, - uint8_t opcode, - struct decode_tbl *inst_decoder) -{ - decode->cmd = inst_decoder->cmd; - if (inst_decoder->operand_size) { - decode->operand_size = inst_decoder->operand_size; - } - decode->flags_mask = inst_decoder->flags_mask; - - if (inst_decoder->is_modrm) { - decode_modrm(env, decode); - } - if (inst_decoder->decode_op1) { - inst_decoder->decode_op1(env, decode, &decode->op[0]); - } - if (inst_decoder->decode_op2) { - inst_decoder->decode_op2(env, decode, &decode->op[1]); - } - if (inst_decoder->decode_op3) { - inst_decoder->decode_op3(env, decode, &decode->op[2]); - } - if (inst_decoder->decode_op4) { - inst_decoder->decode_op4(env, decode, &decode->op[3]); - } - if (inst_decoder->decode_postfix) { - inst_decoder->decode_postfix(env, decode); - } -} - -static inline void decode_opcode_1(CPUX86State *env, struct x86_decode *decode, - uint8_t opcode) -{ - struct decode_tbl *inst_decoder = &_decode_tbl1[opcode]; - decode_opcode_general(env, decode, opcode, inst_decoder); -} - - -static inline void decode_opcode_2(CPUX86State *env, struct x86_decode *decode, - uint8_t opcode) -{ - struct decode_tbl *inst_decoder = &_decode_tbl2[opcode]; - decode_opcode_general(env, decode, opcode, inst_decoder); -} - -static void decode_opcodes(CPUX86State *env, struct x86_decode *decode) -{ - uint8_t opcode; - - opcode = decode_byte(env, decode); - decode->opcode[decode->opcode_len++] = opcode; - if (opcode != OPCODE_ESCAPE) { - decode_opcode_1(env, decode, opcode); - } else { - opcode = decode_byte(env, decode); - decode->opcode[decode->opcode_len++] = opcode; - decode_opcode_2(env, decode, opcode); - } -} - -uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode) -{ - memset(decode, 0, sizeof(*decode)); - decode_prefix(env, decode); - set_addressing_size(env, decode); - set_operand_size(env, decode); - - decode_opcodes(env, decode); - - return decode->len; -} - -void init_decoder() -{ - int i; - - for (i = 0; i < ARRAY_SIZE(_decode_tbl1); i++) { - memcpy(&_decode_tbl1[i], &invl_inst, sizeof(invl_inst)); - } - for (i = 0; i < ARRAY_SIZE(_decode_tbl2); i++) { - memcpy(&_decode_tbl2[i], &invl_inst, sizeof(invl_inst)); - } - for (i = 0; i < ARRAY_SIZE(_decode_tbl3); i++) { - memcpy(&_decode_tbl3[i], &invl_inst_x87, sizeof(invl_inst_x87)); - - } - for (i = 0; i < ARRAY_SIZE(_1op_inst); i++) { - _decode_tbl1[_1op_inst[i].opcode] = _1op_inst[i]; - } - for (i = 0; i < ARRAY_SIZE(_2op_inst); i++) { - _decode_tbl2[_2op_inst[i].opcode] = _2op_inst[i]; - } - for (i = 0; i < ARRAY_SIZE(_x87_inst); i++) { - int index = ((_x87_inst[i].opcode & 0xf) << 4) | - ((_x87_inst[i].modrm_mod & 1) << 3) | - _x87_inst[i].modrm_reg; - _decode_tbl3[index] = _x87_inst[i]; - } -} - - -const char *decode_cmd_to_string(enum x86_decode_cmd cmd) -{ - static const char *cmds[] = {"INVL", "PUSH", "PUSH_SEG", "POP", "POP_SEG", - "MOV", "MOVSX", "MOVZX", "CALL_NEAR", "CALL_NEAR_ABS_INDIRECT", - "CALL_FAR_ABS_INDIRECT", "CMD_CALL_FAR", "RET_NEAR", "RET_FAR", "ADD", - "OR", "ADC", "SBB", "AND", "SUB", "XOR", "CMP", "INC", "DEC", "TST", - "NOT", "NEG", "JMP_NEAR", "JMP_NEAR_ABS_INDIRECT", "JMP_FAR", - "JMP_FAR_ABS_INDIRECT", "LEA", "JXX", "JCXZ", "SETXX", "MOV_TO_SEG", - "MOV_FROM_SEG", "CLI", "STI", "CLD", "STD", "STC", "CLC", "OUT", "IN", - "INS", "OUTS", "LIDT", "SIDT", "LGDT", "SGDT", "SMSW", "LMSW", - "RDTSCP", "INVLPG", "MOV_TO_CR", "MOV_FROM_CR", "MOV_TO_DR", - "MOV_FROM_DR", "PUSHF", "POPF", "CPUID", "ROL", "ROR", "RCL", "RCR", - "SHL", "SAL", "SHR", "SHRD", "SHLD", "SAR", "DIV", "IDIV", "MUL", - "IMUL_3", "IMUL_2", "IMUL_1", "MOVS", "CMPS", "SCAS", "LODS", "STOS", - "BSWAP", "XCHG", "RDTSC", "RDMSR", "WRMSR", "ENTER", "LEAVE", "BT", - "BTS", "BTC", "BTR", "BSF", "BSR", "IRET", "INT", "POPA", "PUSHA", - "CWD", "CBW", "DAS", "AAD", "AAM", "AAS", "LOOP", "SLDT", "STR", "LLDT", - "LTR", "VERR", "VERW", "SAHF", "LAHF", "WBINVD", "LDS", "LSS", "LES", - "LGS", "LFS", "CMC", "XLAT", "NOP", "CMOV", "CLTS", "XADD", "HLT", - "CMPXCHG8B", "CMPXCHG", "POPCNT", "FNINIT", "FLD", "FLDxx", "FNSTCW", - "FNSTSW", "FNSETPM", "FSAVE", "FRSTOR", "FXSAVE", "FXRSTOR", "FDIV", - "FMUL", "FSUB", "FADD", "EMMS", "MFENCE", "SFENCE", "LFENCE", - "PREFETCH", "FST", "FABS", "FUCOM", "FUCOMI", "FLDCW", - "FXCH", "FCHS", "FCMOV", "FRNDINT", "FXAM", "LAST"}; - return cmds[cmd]; -} - -target_ulong decode_linear_addr(CPUX86State *env, struct x86_decode *decode, - target_ulong addr, X86Seg seg) -{ - switch (decode->segment_override) { - case PREFIX_CS_SEG_OVERRIDE: - seg = R_CS; - break; - case PREFIX_SS_SEG_OVERRIDE: - seg = R_SS; - break; - case PREFIX_DS_SEG_OVERRIDE: - seg = R_DS; - break; - case PREFIX_ES_SEG_OVERRIDE: - seg = R_ES; - break; - case PREFIX_FS_SEG_OVERRIDE: - seg = R_FS; - break; - case PREFIX_GS_SEG_OVERRIDE: - seg = R_GS; - break; - default: - break; - } - return linear_addr_size(env_cpu(env), addr, decode->addressing_size, seg); -} diff --git a/target/i386/hvf/x86_descr.c b/target/i386/hvf/x86_descr.c index c2d2e9ee844..7b599c90377 100644 --- a/target/i386/hvf/x86_descr.c +++ b/target/i386/hvf/x86_descr.c @@ -60,19 +60,19 @@ uint64_t vmx_read_segment_base(CPUState *cpu, X86Seg seg) return rvmcs(cpu->accel->fd, vmx_segment_fields[seg].base); } -x68_segment_selector vmx_read_segment_selector(CPUState *cpu, X86Seg seg) +x86_segment_selector vmx_read_segment_selector(CPUState *cpu, X86Seg seg) { - x68_segment_selector sel; + x86_segment_selector sel; 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, x86_segment_selector selector, X86Seg seg) { 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->accel->fd, vmx_segment_fields[seg].selector); desc->base = rvmcs(cpu->accel->fd, vmx_segment_fields[seg].base); @@ -90,7 +90,9 @@ void vmx_write_segment_descriptor(CPUState *cpu, struct vmx_segment *desc, X86Se 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, x86_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 c356932fa4a..24af4946cd4 100644 --- a/target/i386/hvf/x86_descr.h +++ b/target/i386/hvf/x86_descr.h @@ -19,7 +19,7 @@ #ifndef HVF_X86_DESCR_H #define HVF_X86_DESCR_H -#include "x86.h" +#include "emulate/x86.h" typedef struct vmx_segment { uint16_t sel; @@ -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, +x86_segment_selector vmx_read_segment_selector(CPUState *cpu, enum X86Seg seg); -void vmx_write_segment_selector(struct CPUState *cpu, - x68_segment_selector selector, +void vmx_write_segment_selector(CPUState *cpu, + x86_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, - x68_segment_selector selector, +void x86_segment_descriptor_to_vmx(CPUState *cpu, + x86_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 deleted file mode 100644 index ccda5684786..00000000000 --- a/target/i386/hvf/x86_emu.c +++ /dev/null @@ -1,1488 +0,0 @@ -/* - * Copyright (C) 2016 Veertu Inc, - * Copyright (C) 2017 Google Inc, - * - * This program 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 program is distributed in the hope that 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 program; if not, see . - */ - -///////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2001-2012 The Bochs Project -// -// 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., 51 Franklin St, Fifth Floor, Boston, MA B 02110-1301 USA -///////////////////////////////////////////////////////////////////////// - -#include "qemu/osdep.h" -#include "panic.h" -#include "x86_decode.h" -#include "x86.h" -#include "x86_emu.h" -#include "x86_mmu.h" -#include "x86_flags.h" -#include "vmcs.h" -#include "vmx.h" - -void hvf_handle_io(struct CPUState *cpu, uint16_t port, void *data, - int direction, int size, uint32_t count); - -#define EXEC_2OP_FLAGS_CMD(env, decode, cmd, FLAGS_FUNC, save_res) \ -{ \ - fetch_operands(env, decode, 2, true, true, false); \ - switch (decode->operand_size) { \ - case 1: \ - { \ - uint8_t v1 = (uint8_t)decode->op[0].val; \ - uint8_t v2 = (uint8_t)decode->op[1].val; \ - uint8_t diff = v1 cmd v2; \ - if (save_res) { \ - write_val_ext(env, decode->op[0].ptr, diff, 1); \ - } \ - FLAGS_FUNC##8(env, v1, v2, diff); \ - break; \ - } \ - case 2: \ - { \ - uint16_t v1 = (uint16_t)decode->op[0].val; \ - uint16_t v2 = (uint16_t)decode->op[1].val; \ - uint16_t diff = v1 cmd v2; \ - if (save_res) { \ - write_val_ext(env, decode->op[0].ptr, diff, 2); \ - } \ - FLAGS_FUNC##16(env, v1, v2, diff); \ - break; \ - } \ - case 4: \ - { \ - uint32_t v1 = (uint32_t)decode->op[0].val; \ - uint32_t v2 = (uint32_t)decode->op[1].val; \ - uint32_t diff = v1 cmd v2; \ - if (save_res) { \ - write_val_ext(env, decode->op[0].ptr, diff, 4); \ - } \ - FLAGS_FUNC##32(env, v1, v2, diff); \ - break; \ - } \ - default: \ - VM_PANIC("bad size\n"); \ - } \ -} \ - -target_ulong read_reg(CPUX86State *env, int reg, int size) -{ - switch (size) { - case 1: - return x86_reg(env, reg)->lx; - case 2: - return x86_reg(env, reg)->rx; - case 4: - return x86_reg(env, reg)->erx; - case 8: - return x86_reg(env, reg)->rrx; - default: - abort(); - } - return 0; -} - -void write_reg(CPUX86State *env, int reg, target_ulong val, int size) -{ - switch (size) { - case 1: - x86_reg(env, reg)->lx = val; - break; - case 2: - x86_reg(env, reg)->rx = val; - break; - case 4: - x86_reg(env, reg)->rrx = (uint32_t)val; - break; - case 8: - x86_reg(env, reg)->rrx = val; - break; - default: - abort(); - } -} - -target_ulong read_val_from_reg(target_ulong reg_ptr, int size) -{ - target_ulong val; - - switch (size) { - case 1: - val = *(uint8_t *)reg_ptr; - break; - case 2: - val = *(uint16_t *)reg_ptr; - break; - case 4: - val = *(uint32_t *)reg_ptr; - break; - case 8: - val = *(uint64_t *)reg_ptr; - break; - default: - abort(); - } - return val; -} - -void write_val_to_reg(target_ulong reg_ptr, target_ulong val, int size) -{ - switch (size) { - case 1: - *(uint8_t *)reg_ptr = val; - break; - case 2: - *(uint16_t *)reg_ptr = val; - break; - case 4: - *(uint64_t *)reg_ptr = (uint32_t)val; - break; - case 8: - *(uint64_t *)reg_ptr = val; - break; - default: - abort(); - } -} - -static bool is_host_reg(CPUX86State *env, target_ulong ptr) -{ - return (ptr - (target_ulong)&env->regs[0]) < sizeof(env->regs); -} - -void write_val_ext(CPUX86State *env, target_ulong ptr, target_ulong val, int size) -{ - if (is_host_reg(env, ptr)) { - write_val_to_reg(ptr, val, size); - return; - } - vmx_write_mem(env_cpu(env), ptr, &val, size); -} - -uint8_t *read_mmio(CPUX86State *env, target_ulong ptr, int bytes) -{ - vmx_read_mem(env_cpu(env), env->hvf_mmio_buf, ptr, bytes); - return env->hvf_mmio_buf; -} - - -target_ulong read_val_ext(CPUX86State *env, target_ulong ptr, int size) -{ - target_ulong val; - uint8_t *mmio_ptr; - - if (is_host_reg(env, ptr)) { - return read_val_from_reg(ptr, size); - } - - mmio_ptr = read_mmio(env, ptr, size); - switch (size) { - case 1: - val = *(uint8_t *)mmio_ptr; - break; - case 2: - val = *(uint16_t *)mmio_ptr; - break; - case 4: - val = *(uint32_t *)mmio_ptr; - break; - case 8: - val = *(uint64_t *)mmio_ptr; - break; - default: - VM_PANIC("bad size\n"); - break; - } - return val; -} - -static void fetch_operands(CPUX86State *env, struct x86_decode *decode, - int n, bool val_op0, bool val_op1, bool val_op2) -{ - int i; - bool calc_val[3] = {val_op0, val_op1, val_op2}; - - for (i = 0; i < n; i++) { - switch (decode->op[i].type) { - case X86_VAR_IMMEDIATE: - break; - case X86_VAR_REG: - VM_PANIC_ON(!decode->op[i].ptr); - if (calc_val[i]) { - decode->op[i].val = read_val_from_reg(decode->op[i].ptr, - decode->operand_size); - } - break; - case X86_VAR_RM: - calc_modrm_operand(env, decode, &decode->op[i]); - if (calc_val[i]) { - decode->op[i].val = read_val_ext(env, decode->op[i].ptr, - decode->operand_size); - } - break; - case X86_VAR_OFFSET: - decode->op[i].ptr = decode_linear_addr(env, decode, - decode->op[i].ptr, - R_DS); - if (calc_val[i]) { - decode->op[i].val = read_val_ext(env, decode->op[i].ptr, - decode->operand_size); - } - break; - default: - break; - } - } -} - -static void exec_mov(CPUX86State *env, struct x86_decode *decode) -{ - fetch_operands(env, decode, 2, false, true, false); - write_val_ext(env, decode->op[0].ptr, decode->op[1].val, - decode->operand_size); - - env->eip += decode->len; -} - -static void exec_add(CPUX86State *env, struct x86_decode *decode) -{ - EXEC_2OP_FLAGS_CMD(env, decode, +, SET_FLAGS_OSZAPC_ADD, true); - env->eip += decode->len; -} - -static void exec_or(CPUX86State *env, struct x86_decode *decode) -{ - EXEC_2OP_FLAGS_CMD(env, decode, |, SET_FLAGS_OSZAPC_LOGIC, true); - env->eip += decode->len; -} - -static void exec_adc(CPUX86State *env, struct x86_decode *decode) -{ - EXEC_2OP_FLAGS_CMD(env, decode, +get_CF(env)+, SET_FLAGS_OSZAPC_ADD, true); - env->eip += decode->len; -} - -static void exec_sbb(CPUX86State *env, struct x86_decode *decode) -{ - EXEC_2OP_FLAGS_CMD(env, decode, -get_CF(env)-, SET_FLAGS_OSZAPC_SUB, true); - env->eip += decode->len; -} - -static void exec_and(CPUX86State *env, struct x86_decode *decode) -{ - EXEC_2OP_FLAGS_CMD(env, decode, &, SET_FLAGS_OSZAPC_LOGIC, true); - env->eip += decode->len; -} - -static void exec_sub(CPUX86State *env, struct x86_decode *decode) -{ - EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, true); - env->eip += decode->len; -} - -static void exec_xor(CPUX86State *env, struct x86_decode *decode) -{ - EXEC_2OP_FLAGS_CMD(env, decode, ^, SET_FLAGS_OSZAPC_LOGIC, true); - env->eip += decode->len; -} - -static void exec_neg(CPUX86State *env, struct x86_decode *decode) -{ - /*EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, false);*/ - int32_t val; - fetch_operands(env, decode, 2, true, true, false); - - val = 0 - sign(decode->op[1].val, decode->operand_size); - write_val_ext(env, decode->op[1].ptr, val, decode->operand_size); - - if (4 == decode->operand_size) { - SET_FLAGS_OSZAPC_SUB32(env, 0, 0 - val, val); - } else if (2 == decode->operand_size) { - SET_FLAGS_OSZAPC_SUB16(env, 0, 0 - val, val); - } else if (1 == decode->operand_size) { - SET_FLAGS_OSZAPC_SUB8(env, 0, 0 - val, val); - } else { - VM_PANIC("bad op size\n"); - } - - /*lflags_to_rflags(env);*/ - env->eip += decode->len; -} - -static void exec_cmp(CPUX86State *env, struct x86_decode *decode) -{ - EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, false); - env->eip += decode->len; -} - -static void exec_inc(CPUX86State *env, struct x86_decode *decode) -{ - decode->op[1].type = X86_VAR_IMMEDIATE; - decode->op[1].val = 0; - - EXEC_2OP_FLAGS_CMD(env, decode, +1+, SET_FLAGS_OSZAP_ADD, true); - - env->eip += decode->len; -} - -static void exec_dec(CPUX86State *env, struct x86_decode *decode) -{ - decode->op[1].type = X86_VAR_IMMEDIATE; - decode->op[1].val = 0; - - EXEC_2OP_FLAGS_CMD(env, decode, -1-, SET_FLAGS_OSZAP_SUB, true); - env->eip += decode->len; -} - -static void exec_tst(CPUX86State *env, struct x86_decode *decode) -{ - EXEC_2OP_FLAGS_CMD(env, decode, &, SET_FLAGS_OSZAPC_LOGIC, false); - env->eip += decode->len; -} - -static void exec_not(CPUX86State *env, struct x86_decode *decode) -{ - fetch_operands(env, decode, 1, true, false, false); - - write_val_ext(env, decode->op[0].ptr, ~decode->op[0].val, - decode->operand_size); - env->eip += decode->len; -} - -void exec_movzx(CPUX86State *env, struct x86_decode *decode) -{ - int src_op_size; - int op_size = decode->operand_size; - - fetch_operands(env, decode, 1, false, false, false); - - if (0xb6 == decode->opcode[1]) { - src_op_size = 1; - } else { - src_op_size = 2; - } - decode->operand_size = src_op_size; - calc_modrm_operand(env, decode, &decode->op[1]); - decode->op[1].val = read_val_ext(env, decode->op[1].ptr, src_op_size); - write_val_ext(env, decode->op[0].ptr, decode->op[1].val, op_size); - - env->eip += decode->len; -} - -static void exec_out(CPUX86State *env, struct x86_decode *decode) -{ - switch (decode->opcode[0]) { - case 0xe6: - hvf_handle_io(env_cpu(env), decode->op[0].val, &AL(env), 1, 1, 1); - break; - case 0xe7: - hvf_handle_io(env_cpu(env), decode->op[0].val, &RAX(env), 1, - decode->operand_size, 1); - break; - case 0xee: - hvf_handle_io(env_cpu(env), DX(env), &AL(env), 1, 1, 1); - break; - case 0xef: - hvf_handle_io(env_cpu(env), DX(env), &RAX(env), 1, - decode->operand_size, 1); - break; - default: - VM_PANIC("Bad out opcode\n"); - break; - } - env->eip += decode->len; -} - -static void exec_in(CPUX86State *env, struct x86_decode *decode) -{ - target_ulong val = 0; - switch (decode->opcode[0]) { - case 0xe4: - hvf_handle_io(env_cpu(env), decode->op[0].val, &AL(env), 0, 1, 1); - break; - case 0xe5: - hvf_handle_io(env_cpu(env), decode->op[0].val, &val, 0, - decode->operand_size, 1); - if (decode->operand_size == 2) { - AX(env) = val; - } else { - RAX(env) = (uint32_t)val; - } - break; - case 0xec: - hvf_handle_io(env_cpu(env), DX(env), &AL(env), 0, 1, 1); - break; - case 0xed: - hvf_handle_io(env_cpu(env), DX(env), &val, 0, decode->operand_size, 1); - if (decode->operand_size == 2) { - AX(env) = val; - } else { - RAX(env) = (uint32_t)val; - } - - break; - default: - VM_PANIC("Bad in opcode\n"); - break; - } - - env->eip += decode->len; -} - -static inline void string_increment_reg(CPUX86State *env, int reg, - struct x86_decode *decode) -{ - target_ulong val = read_reg(env, reg, decode->addressing_size); - if (env->eflags & DF_MASK) { - val -= decode->operand_size; - } else { - val += decode->operand_size; - } - write_reg(env, reg, val, decode->addressing_size); -} - -static inline void string_rep(CPUX86State *env, struct x86_decode *decode, - void (*func)(CPUX86State *env, - struct x86_decode *ins), int rep) -{ - target_ulong rcx = read_reg(env, R_ECX, decode->addressing_size); - while (rcx--) { - func(env, decode); - write_reg(env, R_ECX, rcx, decode->addressing_size); - if ((PREFIX_REP == rep) && !get_ZF(env)) { - break; - } - if ((PREFIX_REPN == rep) && get_ZF(env)) { - break; - } - } -} - -static void exec_ins_single(CPUX86State *env, struct x86_decode *decode) -{ - target_ulong addr = linear_addr_size(env_cpu(env), RDI(env), - decode->addressing_size, R_ES); - - hvf_handle_io(env_cpu(env), DX(env), env->hvf_mmio_buf, 0, - decode->operand_size, 1); - vmx_write_mem(env_cpu(env), addr, env->hvf_mmio_buf, - decode->operand_size); - - string_increment_reg(env, R_EDI, decode); -} - -static void exec_ins(CPUX86State *env, struct x86_decode *decode) -{ - if (decode->rep) { - string_rep(env, decode, exec_ins_single, 0); - } else { - exec_ins_single(env, decode); - } - - env->eip += decode->len; -} - -static void exec_outs_single(CPUX86State *env, struct x86_decode *decode) -{ - target_ulong addr = decode_linear_addr(env, decode, RSI(env), R_DS); - - vmx_read_mem(env_cpu(env), env->hvf_mmio_buf, addr, - decode->operand_size); - hvf_handle_io(env_cpu(env), DX(env), env->hvf_mmio_buf, 1, - decode->operand_size, 1); - - string_increment_reg(env, R_ESI, decode); -} - -static void exec_outs(CPUX86State *env, struct x86_decode *decode) -{ - if (decode->rep) { - string_rep(env, decode, exec_outs_single, 0); - } else { - exec_outs_single(env, decode); - } - - env->eip += decode->len; -} - -static void exec_movs_single(CPUX86State *env, struct x86_decode *decode) -{ - target_ulong src_addr; - target_ulong dst_addr; - target_ulong val; - - src_addr = decode_linear_addr(env, decode, RSI(env), R_DS); - dst_addr = linear_addr_size(env_cpu(env), RDI(env), - decode->addressing_size, R_ES); - - val = read_val_ext(env, src_addr, decode->operand_size); - write_val_ext(env, dst_addr, val, decode->operand_size); - - string_increment_reg(env, R_ESI, decode); - string_increment_reg(env, R_EDI, decode); -} - -static void exec_movs(CPUX86State *env, struct x86_decode *decode) -{ - if (decode->rep) { - string_rep(env, decode, exec_movs_single, 0); - } else { - exec_movs_single(env, decode); - } - - env->eip += decode->len; -} - -static void exec_cmps_single(CPUX86State *env, struct x86_decode *decode) -{ - target_ulong src_addr; - target_ulong dst_addr; - - src_addr = decode_linear_addr(env, decode, RSI(env), R_DS); - dst_addr = linear_addr_size(env_cpu(env), RDI(env), - decode->addressing_size, R_ES); - - decode->op[0].type = X86_VAR_IMMEDIATE; - decode->op[0].val = read_val_ext(env, src_addr, decode->operand_size); - decode->op[1].type = X86_VAR_IMMEDIATE; - decode->op[1].val = read_val_ext(env, dst_addr, decode->operand_size); - - EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, false); - - string_increment_reg(env, R_ESI, decode); - string_increment_reg(env, R_EDI, decode); -} - -static void exec_cmps(CPUX86State *env, struct x86_decode *decode) -{ - if (decode->rep) { - string_rep(env, decode, exec_cmps_single, decode->rep); - } else { - exec_cmps_single(env, decode); - } - env->eip += decode->len; -} - - -static void exec_stos_single(CPUX86State *env, struct x86_decode *decode) -{ - target_ulong addr; - target_ulong val; - - addr = linear_addr_size(env_cpu(env), RDI(env), - decode->addressing_size, R_ES); - val = read_reg(env, R_EAX, decode->operand_size); - vmx_write_mem(env_cpu(env), addr, &val, decode->operand_size); - - string_increment_reg(env, R_EDI, decode); -} - - -static void exec_stos(CPUX86State *env, struct x86_decode *decode) -{ - if (decode->rep) { - string_rep(env, decode, exec_stos_single, 0); - } else { - exec_stos_single(env, decode); - } - - env->eip += decode->len; -} - -static void exec_scas_single(CPUX86State *env, struct x86_decode *decode) -{ - target_ulong addr; - - addr = linear_addr_size(env_cpu(env), RDI(env), - decode->addressing_size, R_ES); - decode->op[1].type = X86_VAR_IMMEDIATE; - vmx_read_mem(env_cpu(env), &decode->op[1].val, addr, decode->operand_size); - - EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, false); - string_increment_reg(env, R_EDI, decode); -} - -static void exec_scas(CPUX86State *env, struct x86_decode *decode) -{ - decode->op[0].type = X86_VAR_REG; - decode->op[0].reg = R_EAX; - if (decode->rep) { - string_rep(env, decode, exec_scas_single, decode->rep); - } else { - exec_scas_single(env, decode); - } - - env->eip += decode->len; -} - -static void exec_lods_single(CPUX86State *env, struct x86_decode *decode) -{ - target_ulong addr; - target_ulong val = 0; - - addr = decode_linear_addr(env, decode, RSI(env), R_DS); - vmx_read_mem(env_cpu(env), &val, addr, decode->operand_size); - write_reg(env, R_EAX, val, decode->operand_size); - - string_increment_reg(env, R_ESI, decode); -} - -static void exec_lods(CPUX86State *env, struct x86_decode *decode) -{ - if (decode->rep) { - string_rep(env, decode, exec_lods_single, 0); - } else { - exec_lods_single(env, decode); - } - - env->eip += decode->len; -} - -void simulate_rdmsr(struct CPUState *cpu) -{ - X86CPU *x86_cpu = X86_CPU(cpu); - CPUX86State *env = &x86_cpu->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->accel->fd, VMCS_TSC_OFFSET); - break; - case MSR_IA32_APICBASE: - val = cpu_get_apic_base(X86_CPU(cpu)->apic_state); - break; - case MSR_IA32_UCODE_REV: - val = x86_cpu->ucode_rev; - break; - case MSR_EFER: - val = rvmcs(cpu->accel->fd, VMCS_GUEST_IA32_EFER); - break; - case MSR_FSBASE: - val = rvmcs(cpu->accel->fd, VMCS_GUEST_FS_BASE); - break; - case MSR_GSBASE: - val = rvmcs(cpu->accel->fd, VMCS_GUEST_GS_BASE); - break; - case MSR_KERNELGSBASE: - val = rvmcs(cpu->accel->fd, VMCS_HOST_FS_BASE); - break; - case MSR_STAR: - abort(); - break; - case MSR_LSTAR: - abort(); - break; - case MSR_CSTAR: - abort(); - break; - case MSR_IA32_MISC_ENABLE: - val = env->msr_ia32_misc_enable; - break; - case MSR_MTRRphysBase(0): - case MSR_MTRRphysBase(1): - case MSR_MTRRphysBase(2): - case MSR_MTRRphysBase(3): - case MSR_MTRRphysBase(4): - case MSR_MTRRphysBase(5): - case MSR_MTRRphysBase(6): - case MSR_MTRRphysBase(7): - val = env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base; - break; - case MSR_MTRRphysMask(0): - case MSR_MTRRphysMask(1): - case MSR_MTRRphysMask(2): - case MSR_MTRRphysMask(3): - case MSR_MTRRphysMask(4): - case MSR_MTRRphysMask(5): - case MSR_MTRRphysMask(6): - case MSR_MTRRphysMask(7): - val = env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask; - break; - case MSR_MTRRfix64K_00000: - val = env->mtrr_fixed[0]; - break; - case MSR_MTRRfix16K_80000: - case MSR_MTRRfix16K_A0000: - val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1]; - break; - case MSR_MTRRfix4K_C0000: - case MSR_MTRRfix4K_C8000: - case MSR_MTRRfix4K_D0000: - case MSR_MTRRfix4K_D8000: - case MSR_MTRRfix4K_E0000: - case MSR_MTRRfix4K_E8000: - case MSR_MTRRfix4K_F0000: - case MSR_MTRRfix4K_F8000: - val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3]; - break; - case MSR_MTRRdefType: - val = env->mtrr_deftype; - break; - case MSR_CORE_THREAD_COUNT: - 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: - /* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */ - val = 0; - break; - } - - RAX(env) = (uint32_t)val; - RDX(env) = (uint32_t)(val >> 32); -} - -static void exec_rdmsr(CPUX86State *env, struct x86_decode *decode) -{ - simulate_rdmsr(env_cpu(env)); - env->eip += decode->len; -} - -void simulate_wrmsr(struct CPUState *cpu) -{ - X86CPU *x86_cpu = X86_CPU(cpu); - CPUX86State *env = &x86_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); - break; - case MSR_FSBASE: - wvmcs(cpu->accel->fd, VMCS_GUEST_FS_BASE, data); - break; - case MSR_GSBASE: - wvmcs(cpu->accel->fd, VMCS_GUEST_GS_BASE, data); - break; - case MSR_KERNELGSBASE: - wvmcs(cpu->accel->fd, VMCS_HOST_FS_BASE, data); - break; - case MSR_STAR: - abort(); - break; - case MSR_LSTAR: - abort(); - break; - case MSR_CSTAR: - abort(); - break; - case MSR_EFER: - /*printf("new efer %llx\n", EFER(cpu));*/ - wvmcs(cpu->accel->fd, VMCS_GUEST_IA32_EFER, data); - if (data & MSR_EFER_NXE) { - hv_vcpu_invalidate_tlb(cpu->accel->fd); - } - break; - case MSR_MTRRphysBase(0): - case MSR_MTRRphysBase(1): - case MSR_MTRRphysBase(2): - case MSR_MTRRphysBase(3): - case MSR_MTRRphysBase(4): - case MSR_MTRRphysBase(5): - case MSR_MTRRphysBase(6): - case MSR_MTRRphysBase(7): - env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base = data; - break; - case MSR_MTRRphysMask(0): - case MSR_MTRRphysMask(1): - case MSR_MTRRphysMask(2): - case MSR_MTRRphysMask(3): - case MSR_MTRRphysMask(4): - case MSR_MTRRphysMask(5): - case MSR_MTRRphysMask(6): - case MSR_MTRRphysMask(7): - env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask = data; - break; - case MSR_MTRRfix64K_00000: - env->mtrr_fixed[ECX(env) - MSR_MTRRfix64K_00000] = data; - break; - case MSR_MTRRfix16K_80000: - case MSR_MTRRfix16K_A0000: - env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1] = data; - break; - case MSR_MTRRfix4K_C0000: - case MSR_MTRRfix4K_C8000: - case MSR_MTRRfix4K_D0000: - case MSR_MTRRfix4K_D8000: - case MSR_MTRRfix4K_E0000: - case MSR_MTRRfix4K_E8000: - case MSR_MTRRfix4K_F0000: - case MSR_MTRRfix4K_F8000: - env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3] = data; - break; - case MSR_MTRRdefType: - env->mtrr_deftype = data; - break; - default: - break; - } - - /* Related to support known hypervisor interface */ - /* if (g_hypervisor_iface) - g_hypervisor_iface->wrmsr_handler(cpu, msr, data); - - printf("write msr %llx\n", RCX(cpu));*/ -} - -static void exec_wrmsr(CPUX86State *env, struct x86_decode *decode) -{ - simulate_wrmsr(env_cpu(env)); - env->eip += decode->len; -} - -/* - * flag: - * 0 - bt, 1 - btc, 2 - bts, 3 - btr - */ -static void do_bt(CPUX86State *env, struct x86_decode *decode, int flag) -{ - int32_t displacement; - uint8_t index; - bool cf; - int mask = (4 == decode->operand_size) ? 0x1f : 0xf; - - VM_PANIC_ON(decode->rex.rex); - - fetch_operands(env, decode, 2, false, true, false); - index = decode->op[1].val & mask; - - if (decode->op[0].type != X86_VAR_REG) { - if (4 == decode->operand_size) { - displacement = ((int32_t) (decode->op[1].val & 0xffffffe0)) / 32; - decode->op[0].ptr += 4 * displacement; - } else if (2 == decode->operand_size) { - displacement = ((int16_t) (decode->op[1].val & 0xfff0)) / 16; - decode->op[0].ptr += 2 * displacement; - } else { - VM_PANIC("bt 64bit\n"); - } - } - decode->op[0].val = read_val_ext(env, decode->op[0].ptr, - decode->operand_size); - cf = (decode->op[0].val >> index) & 0x01; - - switch (flag) { - case 0: - set_CF(env, cf); - return; - case 1: - decode->op[0].val ^= (1u << index); - break; - case 2: - decode->op[0].val |= (1u << index); - break; - case 3: - decode->op[0].val &= ~(1u << index); - break; - } - write_val_ext(env, decode->op[0].ptr, decode->op[0].val, - decode->operand_size); - set_CF(env, cf); -} - -static void exec_bt(CPUX86State *env, struct x86_decode *decode) -{ - do_bt(env, decode, 0); - env->eip += decode->len; -} - -static void exec_btc(CPUX86State *env, struct x86_decode *decode) -{ - do_bt(env, decode, 1); - env->eip += decode->len; -} - -static void exec_btr(CPUX86State *env, struct x86_decode *decode) -{ - do_bt(env, decode, 3); - env->eip += decode->len; -} - -static void exec_bts(CPUX86State *env, struct x86_decode *decode) -{ - do_bt(env, decode, 2); - env->eip += decode->len; -} - -void exec_shl(CPUX86State *env, struct x86_decode *decode) -{ - uint8_t count; - int of = 0, cf = 0; - - fetch_operands(env, decode, 2, true, true, false); - - count = decode->op[1].val; - count &= 0x1f; /* count is masked to 5 bits*/ - if (!count) { - goto exit; - } - - switch (decode->operand_size) { - case 1: - { - uint8_t res = 0; - if (count <= 8) { - res = (decode->op[0].val << count); - cf = (decode->op[0].val >> (8 - count)) & 0x1; - of = cf ^ (res >> 7); - } - - write_val_ext(env, decode->op[0].ptr, res, 1); - SET_FLAGS_OSZAPC_LOGIC8(env, 0, 0, res); - SET_FLAGS_OxxxxC(env, of, cf); - break; - } - case 2: - { - uint16_t res = 0; - - /* from bochs */ - if (count <= 16) { - res = (decode->op[0].val << count); - cf = (decode->op[0].val >> (16 - count)) & 0x1; - of = cf ^ (res >> 15); /* of = cf ^ result15 */ - } - - write_val_ext(env, decode->op[0].ptr, res, 2); - SET_FLAGS_OSZAPC_LOGIC16(env, 0, 0, res); - SET_FLAGS_OxxxxC(env, of, cf); - break; - } - case 4: - { - uint32_t res = decode->op[0].val << count; - - write_val_ext(env, decode->op[0].ptr, res, 4); - SET_FLAGS_OSZAPC_LOGIC32(env, 0, 0, res); - cf = (decode->op[0].val >> (32 - count)) & 0x1; - of = cf ^ (res >> 31); /* of = cf ^ result31 */ - SET_FLAGS_OxxxxC(env, of, cf); - break; - } - default: - abort(); - } - -exit: - /* lflags_to_rflags(env); */ - env->eip += decode->len; -} - -void exec_movsx(CPUX86State *env, struct x86_decode *decode) -{ - int src_op_size; - int op_size = decode->operand_size; - - fetch_operands(env, decode, 2, false, false, false); - - if (0xbe == decode->opcode[1]) { - src_op_size = 1; - } else { - src_op_size = 2; - } - - decode->operand_size = src_op_size; - calc_modrm_operand(env, decode, &decode->op[1]); - decode->op[1].val = sign(read_val_ext(env, decode->op[1].ptr, src_op_size), - src_op_size); - - write_val_ext(env, decode->op[0].ptr, decode->op[1].val, op_size); - - env->eip += decode->len; -} - -void exec_ror(CPUX86State *env, struct x86_decode *decode) -{ - uint8_t count; - - fetch_operands(env, decode, 2, true, true, false); - count = decode->op[1].val; - - switch (decode->operand_size) { - case 1: - { - uint32_t bit6, bit7; - uint8_t res; - - if ((count & 0x07) == 0) { - if (count & 0x18) { - bit6 = ((uint8_t)decode->op[0].val >> 6) & 1; - bit7 = ((uint8_t)decode->op[0].val >> 7) & 1; - SET_FLAGS_OxxxxC(env, bit6 ^ bit7, bit7); - } - } else { - count &= 0x7; /* use only bottom 3 bits */ - res = ((uint8_t)decode->op[0].val >> count) | - ((uint8_t)decode->op[0].val << (8 - count)); - write_val_ext(env, decode->op[0].ptr, res, 1); - bit6 = (res >> 6) & 1; - bit7 = (res >> 7) & 1; - /* set eflags: ROR count affects the following flags: C, O */ - SET_FLAGS_OxxxxC(env, bit6 ^ bit7, bit7); - } - break; - } - case 2: - { - uint32_t bit14, bit15; - uint16_t res; - - if ((count & 0x0f) == 0) { - if (count & 0x10) { - bit14 = ((uint16_t)decode->op[0].val >> 14) & 1; - bit15 = ((uint16_t)decode->op[0].val >> 15) & 1; - /* of = result14 ^ result15 */ - SET_FLAGS_OxxxxC(env, bit14 ^ bit15, bit15); - } - } else { - count &= 0x0f; /* use only 4 LSB's */ - res = ((uint16_t)decode->op[0].val >> count) | - ((uint16_t)decode->op[0].val << (16 - count)); - write_val_ext(env, decode->op[0].ptr, res, 2); - - bit14 = (res >> 14) & 1; - bit15 = (res >> 15) & 1; - /* of = result14 ^ result15 */ - SET_FLAGS_OxxxxC(env, bit14 ^ bit15, bit15); - } - break; - } - case 4: - { - uint32_t bit31, bit30; - uint32_t res; - - count &= 0x1f; - if (count) { - res = ((uint32_t)decode->op[0].val >> count) | - ((uint32_t)decode->op[0].val << (32 - count)); - write_val_ext(env, decode->op[0].ptr, res, 4); - - bit31 = (res >> 31) & 1; - bit30 = (res >> 30) & 1; - /* of = result30 ^ result31 */ - SET_FLAGS_OxxxxC(env, bit30 ^ bit31, bit31); - } - break; - } - } - env->eip += decode->len; -} - -void exec_rol(CPUX86State *env, struct x86_decode *decode) -{ - uint8_t count; - - fetch_operands(env, decode, 2, true, true, false); - count = decode->op[1].val; - - switch (decode->operand_size) { - case 1: - { - uint32_t bit0, bit7; - uint8_t res; - - if ((count & 0x07) == 0) { - if (count & 0x18) { - bit0 = ((uint8_t)decode->op[0].val & 1); - bit7 = ((uint8_t)decode->op[0].val >> 7); - SET_FLAGS_OxxxxC(env, bit0 ^ bit7, bit0); - } - } else { - count &= 0x7; /* use only lowest 3 bits */ - res = ((uint8_t)decode->op[0].val << count) | - ((uint8_t)decode->op[0].val >> (8 - count)); - - write_val_ext(env, decode->op[0].ptr, res, 1); - /* set eflags: - * ROL count affects the following flags: C, O - */ - bit0 = (res & 1); - bit7 = (res >> 7); - SET_FLAGS_OxxxxC(env, bit0 ^ bit7, bit0); - } - break; - } - case 2: - { - uint32_t bit0, bit15; - uint16_t res; - - if ((count & 0x0f) == 0) { - if (count & 0x10) { - bit0 = ((uint16_t)decode->op[0].val & 0x1); - bit15 = ((uint16_t)decode->op[0].val >> 15); - /* of = cf ^ result15 */ - SET_FLAGS_OxxxxC(env, bit0 ^ bit15, bit0); - } - } else { - count &= 0x0f; /* only use bottom 4 bits */ - res = ((uint16_t)decode->op[0].val << count) | - ((uint16_t)decode->op[0].val >> (16 - count)); - - write_val_ext(env, decode->op[0].ptr, res, 2); - bit0 = (res & 0x1); - bit15 = (res >> 15); - /* of = cf ^ result15 */ - SET_FLAGS_OxxxxC(env, bit0 ^ bit15, bit0); - } - break; - } - case 4: - { - uint32_t bit0, bit31; - uint32_t res; - - count &= 0x1f; - if (count) { - res = ((uint32_t)decode->op[0].val << count) | - ((uint32_t)decode->op[0].val >> (32 - count)); - - write_val_ext(env, decode->op[0].ptr, res, 4); - bit0 = (res & 0x1); - bit31 = (res >> 31); - /* of = cf ^ result31 */ - SET_FLAGS_OxxxxC(env, bit0 ^ bit31, bit0); - } - break; - } - } - env->eip += decode->len; -} - - -void exec_rcl(CPUX86State *env, struct x86_decode *decode) -{ - uint8_t count; - int of = 0, cf = 0; - - fetch_operands(env, decode, 2, true, true, false); - count = decode->op[1].val & 0x1f; - - switch (decode->operand_size) { - case 1: - { - uint8_t op1_8 = decode->op[0].val; - uint8_t res; - count %= 9; - if (!count) { - break; - } - - if (1 == count) { - res = (op1_8 << 1) | get_CF(env); - } else { - res = (op1_8 << count) | (get_CF(env) << (count - 1)) | - (op1_8 >> (9 - count)); - } - - write_val_ext(env, decode->op[0].ptr, res, 1); - - cf = (op1_8 >> (8 - count)) & 0x01; - of = cf ^ (res >> 7); /* of = cf ^ result7 */ - SET_FLAGS_OxxxxC(env, of, cf); - break; - } - case 2: - { - uint16_t res; - uint16_t op1_16 = decode->op[0].val; - - count %= 17; - if (!count) { - break; - } - - if (1 == count) { - res = (op1_16 << 1) | get_CF(env); - } else if (count == 16) { - res = (get_CF(env) << 15) | (op1_16 >> 1); - } else { /* 2..15 */ - res = (op1_16 << count) | (get_CF(env) << (count - 1)) | - (op1_16 >> (17 - count)); - } - - write_val_ext(env, decode->op[0].ptr, res, 2); - - cf = (op1_16 >> (16 - count)) & 0x1; - of = cf ^ (res >> 15); /* of = cf ^ result15 */ - SET_FLAGS_OxxxxC(env, of, cf); - break; - } - case 4: - { - uint32_t res; - uint32_t op1_32 = decode->op[0].val; - - if (!count) { - break; - } - - if (1 == count) { - res = (op1_32 << 1) | get_CF(env); - } else { - res = (op1_32 << count) | (get_CF(env) << (count - 1)) | - (op1_32 >> (33 - count)); - } - - write_val_ext(env, decode->op[0].ptr, res, 4); - - cf = (op1_32 >> (32 - count)) & 0x1; - of = cf ^ (res >> 31); /* of = cf ^ result31 */ - SET_FLAGS_OxxxxC(env, of, cf); - break; - } - } - env->eip += decode->len; -} - -void exec_rcr(CPUX86State *env, struct x86_decode *decode) -{ - uint8_t count; - int of = 0, cf = 0; - - fetch_operands(env, decode, 2, true, true, false); - count = decode->op[1].val & 0x1f; - - switch (decode->operand_size) { - case 1: - { - uint8_t op1_8 = decode->op[0].val; - uint8_t res; - - count %= 9; - if (!count) { - break; - } - res = (op1_8 >> count) | (get_CF(env) << (8 - count)) | - (op1_8 << (9 - count)); - - write_val_ext(env, decode->op[0].ptr, res, 1); - - cf = (op1_8 >> (count - 1)) & 0x1; - of = (((res << 1) ^ res) >> 7) & 0x1; /* of = result6 ^ result7 */ - SET_FLAGS_OxxxxC(env, of, cf); - break; - } - case 2: - { - uint16_t op1_16 = decode->op[0].val; - uint16_t res; - - count %= 17; - if (!count) { - break; - } - res = (op1_16 >> count) | (get_CF(env) << (16 - count)) | - (op1_16 << (17 - count)); - - write_val_ext(env, decode->op[0].ptr, res, 2); - - cf = (op1_16 >> (count - 1)) & 0x1; - of = ((uint16_t)((res << 1) ^ res) >> 15) & 0x1; /* of = result15 ^ - result14 */ - SET_FLAGS_OxxxxC(env, of, cf); - break; - } - case 4: - { - uint32_t res; - uint32_t op1_32 = decode->op[0].val; - - if (!count) { - break; - } - - if (1 == count) { - res = (op1_32 >> 1) | (get_CF(env) << 31); - } else { - res = (op1_32 >> count) | (get_CF(env) << (32 - count)) | - (op1_32 << (33 - count)); - } - - write_val_ext(env, decode->op[0].ptr, res, 4); - - cf = (op1_32 >> (count - 1)) & 0x1; - of = ((res << 1) ^ res) >> 31; /* of = result30 ^ result31 */ - SET_FLAGS_OxxxxC(env, of, cf); - break; - } - } - env->eip += decode->len; -} - -static void exec_xchg(CPUX86State *env, struct x86_decode *decode) -{ - fetch_operands(env, decode, 2, true, true, false); - - write_val_ext(env, decode->op[0].ptr, decode->op[1].val, - decode->operand_size); - write_val_ext(env, decode->op[1].ptr, decode->op[0].val, - decode->operand_size); - - env->eip += decode->len; -} - -static void exec_xadd(CPUX86State *env, struct x86_decode *decode) -{ - EXEC_2OP_FLAGS_CMD(env, decode, +, SET_FLAGS_OSZAPC_ADD, true); - write_val_ext(env, decode->op[1].ptr, decode->op[0].val, - decode->operand_size); - - env->eip += decode->len; -} - -static struct cmd_handler { - enum x86_decode_cmd cmd; - void (*handler)(CPUX86State *env, struct x86_decode *ins); -} handlers[] = { - {X86_DECODE_CMD_INVL, NULL,}, - {X86_DECODE_CMD_MOV, exec_mov}, - {X86_DECODE_CMD_ADD, exec_add}, - {X86_DECODE_CMD_OR, exec_or}, - {X86_DECODE_CMD_ADC, exec_adc}, - {X86_DECODE_CMD_SBB, exec_sbb}, - {X86_DECODE_CMD_AND, exec_and}, - {X86_DECODE_CMD_SUB, exec_sub}, - {X86_DECODE_CMD_NEG, exec_neg}, - {X86_DECODE_CMD_XOR, exec_xor}, - {X86_DECODE_CMD_CMP, exec_cmp}, - {X86_DECODE_CMD_INC, exec_inc}, - {X86_DECODE_CMD_DEC, exec_dec}, - {X86_DECODE_CMD_TST, exec_tst}, - {X86_DECODE_CMD_NOT, exec_not}, - {X86_DECODE_CMD_MOVZX, exec_movzx}, - {X86_DECODE_CMD_OUT, exec_out}, - {X86_DECODE_CMD_IN, exec_in}, - {X86_DECODE_CMD_INS, exec_ins}, - {X86_DECODE_CMD_OUTS, exec_outs}, - {X86_DECODE_CMD_RDMSR, exec_rdmsr}, - {X86_DECODE_CMD_WRMSR, exec_wrmsr}, - {X86_DECODE_CMD_BT, exec_bt}, - {X86_DECODE_CMD_BTR, exec_btr}, - {X86_DECODE_CMD_BTC, exec_btc}, - {X86_DECODE_CMD_BTS, exec_bts}, - {X86_DECODE_CMD_SHL, exec_shl}, - {X86_DECODE_CMD_ROL, exec_rol}, - {X86_DECODE_CMD_ROR, exec_ror}, - {X86_DECODE_CMD_RCR, exec_rcr}, - {X86_DECODE_CMD_RCL, exec_rcl}, - /*{X86_DECODE_CMD_CPUID, exec_cpuid},*/ - {X86_DECODE_CMD_MOVS, exec_movs}, - {X86_DECODE_CMD_CMPS, exec_cmps}, - {X86_DECODE_CMD_STOS, exec_stos}, - {X86_DECODE_CMD_SCAS, exec_scas}, - {X86_DECODE_CMD_LODS, exec_lods}, - {X86_DECODE_CMD_MOVSX, exec_movsx}, - {X86_DECODE_CMD_XCHG, exec_xchg}, - {X86_DECODE_CMD_XADD, exec_xadd}, -}; - -static struct cmd_handler _cmd_handler[X86_DECODE_CMD_LAST]; - -static void init_cmd_handler() -{ - int i; - for (i = 0; i < ARRAY_SIZE(handlers); i++) { - _cmd_handler[handlers[i].cmd] = handlers[i]; - } -} - -void load_regs(struct CPUState *cpu) -{ - X86CPU *x86_cpu = X86_CPU(cpu); - CPUX86State *env = &x86_cpu->env; - - int i = 0; - RRX(env, R_EAX) = rreg(cpu->accel->fd, HV_X86_RAX); - RRX(env, R_EBX) = rreg(cpu->accel->fd, HV_X86_RBX); - RRX(env, R_ECX) = rreg(cpu->accel->fd, HV_X86_RCX); - RRX(env, R_EDX) = rreg(cpu->accel->fd, HV_X86_RDX); - RRX(env, R_ESI) = rreg(cpu->accel->fd, HV_X86_RSI); - RRX(env, R_EDI) = rreg(cpu->accel->fd, HV_X86_RDI); - RRX(env, R_ESP) = rreg(cpu->accel->fd, HV_X86_RSP); - RRX(env, R_EBP) = rreg(cpu->accel->fd, HV_X86_RBP); - for (i = 8; i < 16; i++) { - RRX(env, i) = rreg(cpu->accel->fd, HV_X86_RAX + i); - } - - env->eflags = rreg(cpu->accel->fd, HV_X86_RFLAGS); - rflags_to_lflags(env); - env->eip = rreg(cpu->accel->fd, HV_X86_RIP); -} - -void store_regs(struct CPUState *cpu) -{ - X86CPU *x86_cpu = X86_CPU(cpu); - CPUX86State *env = &x86_cpu->env; - - int i = 0; - wreg(cpu->accel->fd, HV_X86_RAX, RAX(env)); - wreg(cpu->accel->fd, HV_X86_RBX, RBX(env)); - wreg(cpu->accel->fd, HV_X86_RCX, RCX(env)); - wreg(cpu->accel->fd, HV_X86_RDX, RDX(env)); - wreg(cpu->accel->fd, HV_X86_RSI, RSI(env)); - wreg(cpu->accel->fd, HV_X86_RDI, RDI(env)); - wreg(cpu->accel->fd, HV_X86_RBP, RBP(env)); - wreg(cpu->accel->fd, HV_X86_RSP, RSP(env)); - for (i = 8; i < 16; i++) { - wreg(cpu->accel->fd, HV_X86_RAX + i, RRX(env, i)); - } - - lflags_to_rflags(env); - wreg(cpu->accel->fd, HV_X86_RFLAGS, env->eflags); - macvm_set_rip(cpu, 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, - decode_cmd_to_string(ins->cmd));*/ - - if (!_cmd_handler[ins->cmd].handler) { - printf("Unimplemented handler (%llx) for %d (%x %x) \n", env->eip, - ins->cmd, ins->opcode[0], - ins->opcode_len > 1 ? ins->opcode[1] : 0); - env->eip += ins->len; - return true; - } - - _cmd_handler[ins->cmd].handler(env, ins); - return true; -} - -void init_emu() -{ - init_cmd_handler(); -} diff --git a/target/i386/hvf/x86_emu.h b/target/i386/hvf/x86_emu.h deleted file mode 100644 index 640da90b30f..00000000000 --- a/target/i386/hvf/x86_emu.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2016 Veertu Inc, - * Copyright (C) 2017 Google Inc, - * - * This program 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 program is distributed in the hope that 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 program; if not, see . - */ - -#ifndef X86_EMU_H -#define X86_EMU_H - -#include "x86.h" -#include "x86_decode.h" -#include "cpu.h" - -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 simulate_rdmsr(struct CPUState *cpu); -void simulate_wrmsr(struct CPUState *cpu); - -target_ulong read_reg(CPUX86State *env, int reg, int size); -void write_reg(CPUX86State *env, int reg, target_ulong val, int size); -target_ulong read_val_from_reg(target_ulong reg_ptr, int size); -void write_val_to_reg(target_ulong reg_ptr, target_ulong val, int size); -void write_val_ext(CPUX86State *env, target_ulong ptr, target_ulong val, int size); -uint8_t *read_mmio(CPUX86State *env, target_ulong ptr, int bytes); -target_ulong read_val_ext(CPUX86State *env, target_ulong ptr, int size); - -void exec_movzx(CPUX86State *env, struct x86_decode *decode); -void exec_shl(CPUX86State *env, struct x86_decode *decode); -void exec_movsx(CPUX86State *env, struct x86_decode *decode); -void exec_ror(CPUX86State *env, struct x86_decode *decode); -void exec_rol(CPUX86State *env, struct x86_decode *decode); -void exec_rcl(CPUX86State *env, struct x86_decode *decode); -void exec_rcr(CPUX86State *env, struct x86_decode *decode); -#endif diff --git a/target/i386/hvf/x86_flags.c b/target/i386/hvf/x86_flags.c deleted file mode 100644 index 03d6de5efc3..00000000000 --- a/target/i386/hvf/x86_flags.c +++ /dev/null @@ -1,313 +0,0 @@ -///////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2001-2012 The Bochs Project -// Copyright (C) 2017 Google 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., 51 Franklin St, Fifth Floor, Boston, MA B 02110-1301 USA -///////////////////////////////////////////////////////////////////////// -/* - * flags functions - */ - -#include "qemu/osdep.h" - -#include "panic.h" -#include "cpu.h" -#include "x86_flags.h" -#include "x86.h" - - -/* this is basically bocsh code */ - -#define LF_SIGN_BIT 31 - -#define LF_BIT_SD (0) /* lazy Sign Flag Delta */ -#define LF_BIT_AF (3) /* lazy Adjust flag */ -#define LF_BIT_PDB (8) /* lazy Parity Delta Byte (8 bits) */ -#define LF_BIT_CF (31) /* lazy Carry Flag */ -#define LF_BIT_PO (30) /* lazy Partial Overflow = CF ^ OF */ - -#define LF_MASK_SD (0x01 << LF_BIT_SD) -#define LF_MASK_AF (0x01 << LF_BIT_AF) -#define LF_MASK_PDB (0xFF << LF_BIT_PDB) -#define LF_MASK_CF (0x01 << LF_BIT_CF) -#define LF_MASK_PO (0x01 << LF_BIT_PO) - -#define ADD_COUT_VEC(op1, op2, result) \ - (((op1) & (op2)) | (((op1) | (op2)) & (~(result)))) - -#define SUB_COUT_VEC(op1, op2, result) \ - (((~(op1)) & (op2)) | (((~(op1)) ^ (op2)) & (result))) - -#define GET_ADD_OVERFLOW(op1, op2, result, mask) \ - ((((op1) ^ (result)) & ((op2) ^ (result))) & (mask)) - -/* ******************* */ -/* OSZAPC */ -/* ******************* */ - -/* size, carries, result */ -#define SET_FLAGS_OSZAPC_SIZE(size, lf_carries, lf_result) { \ - target_ulong temp = ((lf_carries) & (LF_MASK_AF)) | \ - (((lf_carries) >> (size - 2)) << LF_BIT_PO); \ - env->hvf_lflags.result = (target_ulong)(int##size##_t)(lf_result); \ - if ((size) == 32) { \ - temp = ((lf_carries) & ~(LF_MASK_PDB | LF_MASK_SD)); \ - } else if ((size) == 16) { \ - temp = ((lf_carries) & (LF_MASK_AF)) | ((lf_carries) << 16); \ - } else if ((size) == 8) { \ - temp = ((lf_carries) & (LF_MASK_AF)) | ((lf_carries) << 24); \ - } else { \ - VM_PANIC("unimplemented"); \ - } \ - env->hvf_lflags.auxbits = (target_ulong)(uint32_t)temp; \ -} - -/* carries, result */ -#define SET_FLAGS_OSZAPC_8(carries, result) \ - SET_FLAGS_OSZAPC_SIZE(8, carries, result) -#define SET_FLAGS_OSZAPC_16(carries, result) \ - SET_FLAGS_OSZAPC_SIZE(16, carries, result) -#define SET_FLAGS_OSZAPC_32(carries, result) \ - SET_FLAGS_OSZAPC_SIZE(32, carries, result) - -/* ******************* */ -/* OSZAP */ -/* ******************* */ -/* size, carries, result */ -#define SET_FLAGS_OSZAP_SIZE(size, lf_carries, lf_result) { \ - target_ulong temp = ((lf_carries) & (LF_MASK_AF)) | \ - (((lf_carries) >> (size - 2)) << LF_BIT_PO); \ - if ((size) == 32) { \ - temp = ((lf_carries) & ~(LF_MASK_PDB | LF_MASK_SD)); \ - } else if ((size) == 16) { \ - temp = ((lf_carries) & (LF_MASK_AF)) | ((lf_carries) << 16); \ - } else if ((size) == 8) { \ - temp = ((lf_carries) & (LF_MASK_AF)) | ((lf_carries) << 24); \ - } else { \ - VM_PANIC("unimplemented"); \ - } \ - env->hvf_lflags.result = (target_ulong)(int##size##_t)(lf_result); \ - target_ulong delta_c = (env->hvf_lflags.auxbits ^ temp) & LF_MASK_CF; \ - delta_c ^= (delta_c >> 1); \ - env->hvf_lflags.auxbits = (target_ulong)(uint32_t)(temp ^ delta_c); \ -} - -/* carries, result */ -#define SET_FLAGS_OSZAP_8(carries, result) \ - SET_FLAGS_OSZAP_SIZE(8, carries, result) -#define SET_FLAGS_OSZAP_16(carries, result) \ - SET_FLAGS_OSZAP_SIZE(16, carries, result) -#define SET_FLAGS_OSZAP_32(carries, result) \ - SET_FLAGS_OSZAP_SIZE(32, carries, result) - -void SET_FLAGS_OxxxxC(CPUX86State *env, uint32_t new_of, uint32_t new_cf) -{ - uint32_t temp_po = new_of ^ new_cf; - env->hvf_lflags.auxbits &= ~(LF_MASK_PO | LF_MASK_CF); - env->hvf_lflags.auxbits |= (temp_po << LF_BIT_PO) | (new_cf << LF_BIT_CF); -} - -void SET_FLAGS_OSZAPC_SUB32(CPUX86State *env, uint32_t v1, uint32_t v2, - uint32_t diff) -{ - SET_FLAGS_OSZAPC_32(SUB_COUT_VEC(v1, v2, diff), diff); -} - -void SET_FLAGS_OSZAPC_SUB16(CPUX86State *env, uint16_t v1, uint16_t v2, - uint16_t diff) -{ - SET_FLAGS_OSZAPC_16(SUB_COUT_VEC(v1, v2, diff), diff); -} - -void SET_FLAGS_OSZAPC_SUB8(CPUX86State *env, uint8_t v1, uint8_t v2, - uint8_t diff) -{ - SET_FLAGS_OSZAPC_8(SUB_COUT_VEC(v1, v2, diff), diff); -} - -void SET_FLAGS_OSZAPC_ADD32(CPUX86State *env, uint32_t v1, uint32_t v2, - uint32_t diff) -{ - SET_FLAGS_OSZAPC_32(ADD_COUT_VEC(v1, v2, diff), diff); -} - -void SET_FLAGS_OSZAPC_ADD16(CPUX86State *env, uint16_t v1, uint16_t v2, - uint16_t diff) -{ - SET_FLAGS_OSZAPC_16(ADD_COUT_VEC(v1, v2, diff), diff); -} - -void SET_FLAGS_OSZAPC_ADD8(CPUX86State *env, uint8_t v1, uint8_t v2, - uint8_t diff) -{ - SET_FLAGS_OSZAPC_8(ADD_COUT_VEC(v1, v2, diff), diff); -} - -void SET_FLAGS_OSZAP_SUB32(CPUX86State *env, uint32_t v1, uint32_t v2, - uint32_t diff) -{ - SET_FLAGS_OSZAP_32(SUB_COUT_VEC(v1, v2, diff), diff); -} - -void SET_FLAGS_OSZAP_SUB16(CPUX86State *env, uint16_t v1, uint16_t v2, - uint16_t diff) -{ - SET_FLAGS_OSZAP_16(SUB_COUT_VEC(v1, v2, diff), diff); -} - -void SET_FLAGS_OSZAP_SUB8(CPUX86State *env, uint8_t v1, uint8_t v2, - uint8_t diff) -{ - SET_FLAGS_OSZAP_8(SUB_COUT_VEC(v1, v2, diff), diff); -} - -void SET_FLAGS_OSZAP_ADD32(CPUX86State *env, uint32_t v1, uint32_t v2, - uint32_t diff) -{ - SET_FLAGS_OSZAP_32(ADD_COUT_VEC(v1, v2, diff), diff); -} - -void SET_FLAGS_OSZAP_ADD16(CPUX86State *env, uint16_t v1, uint16_t v2, - uint16_t diff) -{ - SET_FLAGS_OSZAP_16(ADD_COUT_VEC(v1, v2, diff), diff); -} - -void SET_FLAGS_OSZAP_ADD8(CPUX86State *env, uint8_t v1, uint8_t v2, - uint8_t diff) -{ - SET_FLAGS_OSZAP_8(ADD_COUT_VEC(v1, v2, diff), diff); -} - - -void SET_FLAGS_OSZAPC_LOGIC32(CPUX86State *env, uint32_t v1, uint32_t v2, - uint32_t diff) -{ - SET_FLAGS_OSZAPC_32(0, diff); -} - -void SET_FLAGS_OSZAPC_LOGIC16(CPUX86State *env, uint16_t v1, uint16_t v2, - uint16_t diff) -{ - SET_FLAGS_OSZAPC_16(0, diff); -} - -void SET_FLAGS_OSZAPC_LOGIC8(CPUX86State *env, uint8_t v1, uint8_t v2, - uint8_t diff) -{ - SET_FLAGS_OSZAPC_8(0, diff); -} - -bool get_PF(CPUX86State *env) -{ - uint32_t temp = (255 & env->hvf_lflags.result); - temp = temp ^ (255 & (env->hvf_lflags.auxbits >> LF_BIT_PDB)); - temp = (temp ^ (temp >> 4)) & 0x0F; - return (0x9669U >> temp) & 1; -} - -void set_PF(CPUX86State *env, bool val) -{ - uint32_t temp = (255 & env->hvf_lflags.result) ^ (!val); - env->hvf_lflags.auxbits &= ~(LF_MASK_PDB); - env->hvf_lflags.auxbits |= (temp << LF_BIT_PDB); -} - -bool get_OF(CPUX86State *env) -{ - return ((env->hvf_lflags.auxbits + (1U << LF_BIT_PO)) >> LF_BIT_CF) & 1; -} - -bool get_CF(CPUX86State *env) -{ - return (env->hvf_lflags.auxbits >> LF_BIT_CF) & 1; -} - -void set_OF(CPUX86State *env, bool val) -{ - bool old_cf = get_CF(env); - SET_FLAGS_OxxxxC(env, val, old_cf); -} - -void set_CF(CPUX86State *env, bool val) -{ - bool old_of = get_OF(env); - SET_FLAGS_OxxxxC(env, old_of, val); -} - -bool get_AF(CPUX86State *env) -{ - return (env->hvf_lflags.auxbits >> LF_BIT_AF) & 1; -} - -void set_AF(CPUX86State *env, bool val) -{ - env->hvf_lflags.auxbits &= ~(LF_MASK_AF); - env->hvf_lflags.auxbits |= val << LF_BIT_AF; -} - -bool get_ZF(CPUX86State *env) -{ - return !env->hvf_lflags.result; -} - -void set_ZF(CPUX86State *env, bool val) -{ - if (val) { - env->hvf_lflags.auxbits ^= - (((env->hvf_lflags.result >> LF_SIGN_BIT) & 1) << LF_BIT_SD); - /* merge the parity bits into the Parity Delta Byte */ - uint32_t temp_pdb = (255 & env->hvf_lflags.result); - env->hvf_lflags.auxbits ^= (temp_pdb << LF_BIT_PDB); - /* now zero the .result value */ - env->hvf_lflags.result = 0; - } else { - env->hvf_lflags.result |= (1 << 8); - } -} - -bool get_SF(CPUX86State *env) -{ - return ((env->hvf_lflags.result >> LF_SIGN_BIT) ^ - (env->hvf_lflags.auxbits >> LF_BIT_SD)) & 1; -} - -void set_SF(CPUX86State *env, bool val) -{ - bool temp_sf = get_SF(env); - env->hvf_lflags.auxbits ^= (temp_sf ^ val) << LF_BIT_SD; -} - -void lflags_to_rflags(CPUX86State *env) -{ - env->eflags |= get_CF(env) ? CC_C : 0; - env->eflags |= get_PF(env) ? CC_P : 0; - env->eflags |= get_AF(env) ? CC_A : 0; - env->eflags |= get_ZF(env) ? CC_Z : 0; - env->eflags |= get_SF(env) ? CC_S : 0; - env->eflags |= get_OF(env) ? CC_O : 0; -} - -void rflags_to_lflags(CPUX86State *env) -{ - env->hvf_lflags.auxbits = env->hvf_lflags.result = 0; - set_OF(env, env->eflags & CC_O); - set_SF(env, env->eflags & CC_S); - set_ZF(env, env->eflags & CC_Z); - set_AF(env, env->eflags & CC_A); - set_PF(env, env->eflags & CC_P); - set_CF(env, env->eflags & CC_C); -} diff --git a/target/i386/hvf/x86_mmu.c b/target/i386/hvf/x86_mmu.c index 8cd08622a1e..afc5c17d5d5 100644 --- a/target/i386/hvf/x86_mmu.c +++ b/target/i386/hvf/x86_mmu.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "panic.h" #include "cpu.h" -#include "x86.h" +#include "emulate/x86.h" #include "x86_mmu.h" #include "vmcs.h" #include "vmx.h" @@ -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,9 +119,9 @@ 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; @@ -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,11 +176,11 @@ 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; + 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; @@ -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; @@ -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; diff --git a/target/i386/hvf/x86_mmu.h b/target/i386/hvf/x86_mmu.h index 9ae8a548de3..9447ae072cd 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 f09bfbdda5b..bdf8b51ae67 100644 --- a/target/i386/hvf/x86_task.c +++ b/target/i386/hvf/x86_task.c @@ -10,15 +10,15 @@ #include "panic.h" #include "qemu/error-report.h" -#include "sysemu/hvf.h" +#include "system/hvf.h" #include "hvf-i386.h" #include "vmcs.h" #include "vmx.h" -#include "x86.h" +#include "emulate/x86.h" #include "x86_descr.h" #include "x86_mmu.h" -#include "x86_decode.h" -#include "x86_emu.h" +#include "emulate/x86_decode.h" +#include "emulate/x86_emu.h" #include "x86_task.h" #include "x86hvf.h" @@ -76,16 +76,16 @@ static void load_state_from_tss32(CPUState *cpu, struct x86_tss_segment32 *tss) RSI(env) = tss->esi; RDI(env) = tss->edi; - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ldt}}, R_LDTR); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->es}}, R_ES); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->cs}}, R_CS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ss}}, R_SS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ds}}, R_DS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->fs}}, R_FS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->gs}}, R_GS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->ldt}}, R_LDTR); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->es}}, R_ES); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->cs}}, R_CS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->ss}}, R_SS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->ds}}, R_DS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->fs}}, R_FS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->gs}}, R_GS); } -static int task_switch_32(CPUState *cpu, x68_segment_selector tss_sel, x68_segment_selector old_tss_sel, +static int task_switch_32(CPUState *cpu, x86_segment_selector tss_sel, x86_segment_selector old_tss_sel, uint64_t old_tss_base, struct x86_segment_descriptor *new_desc) { struct x86_tss_segment32 tss_seg; @@ -108,7 +108,7 @@ static int task_switch_32(CPUState *cpu, x68_segment_selector tss_sel, x68_segme return 0; } -void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type) +void vmx_handle_task_switch(CPUState *cpu, x86_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type) { uint64_t rip = rreg(cpu->accel->fd, HV_X86_RIP); if (!gate_valid || (gate_type != VMCS_INTR_T_HWEXCEPTION && @@ -119,11 +119,10 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea return; } - load_regs(cpu); + hvf_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); + x86_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; struct x86_call_gate task_gate_desc; @@ -138,10 +137,10 @@ 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); + x86_segment_selector cs = vmx_read_segment_selector(cpu, R_CS); if (tss_sel.rpl > dpl || cs.rpl > dpl) ;//DPRINTF("emulate_gp"); } @@ -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->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); + hvf_store_regs(cpu); hv_vcpu_invalidate_tlb(cpu->accel->fd); } diff --git a/target/i386/hvf/x86_task.h b/target/i386/hvf/x86_task.h index 4eaa61a7dee..b9afac6a47b 100644 --- a/target/i386/hvf/x86_task.h +++ b/target/i386/hvf/x86_task.h @@ -15,6 +15,6 @@ #ifndef HVF_X86_TASK_H #define HVF_X86_TASK_H -void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, +void vmx_handle_task_switch(CPUState *cpu, x86_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type); #endif diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index 3b1ef5f49a8..2057314892a 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -24,8 +24,8 @@ #include "vmcs.h" #include "cpu.h" #include "x86_descr.h" -#include "x86_decode.h" -#include "sysemu/hw_accel.h" +#include "emulate/x86_decode.h" +#include "system/hw_accel.h" #include "hw/i386/apic_internal.h" @@ -408,7 +408,7 @@ bool hvf_inject_interrupts(CPUState *cs) if (!(env->hflags & HF_INHIBIT_IRQ_MASK) && (cs->interrupt_request & CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK) && !(info & VMCS_INTR_VALID)) { - int line = cpu_get_pic_interrupt(&x86cpu->env); + int line = cpu_get_pic_interrupt(env); cs->interrupt_request &= ~CPU_INTERRUPT_HARD; if (line >= 0) { wvmcs(cs->accel->fd, VMCS_ENTRY_INTR_INFO, line | @@ -427,7 +427,7 @@ int hvf_process_events(CPUState *cs) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - if (!cs->vcpu_dirty) { + if (!cs->accel->dirty) { /* light weight sync for CPU_INTERRUPT_HARD and IF_MASK */ env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); } diff --git a/target/i386/hvf/x86hvf.h b/target/i386/hvf/x86hvf.h index 423a89b6ad3..8c46ce8ad02 100644 --- a/target/i386/hvf/x86hvf.h +++ b/target/i386/hvf/x86hvf.h @@ -31,4 +31,7 @@ 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); + +void hvf_load_regs(CPUState *cpu); +void hvf_store_regs(CPUState *cpu); #endif diff --git a/target/i386/kvm/hyperv-proto.h b/target/i386/kvm/hyperv-proto.h index 464fbf09e35..a9f056f2f3e 100644 --- a/target/i386/kvm/hyperv-proto.h +++ b/target/i386/kvm/hyperv-proto.h @@ -151,18 +151,6 @@ #define HV_X64_MSR_STIMER3_CONFIG 0x400000B6 #define HV_X64_MSR_STIMER3_COUNT 0x400000B7 -/* - * Hyper-V Synthetic debug options MSR - */ -#define HV_X64_MSR_SYNDBG_CONTROL 0x400000F1 -#define HV_X64_MSR_SYNDBG_STATUS 0x400000F2 -#define HV_X64_MSR_SYNDBG_SEND_BUFFER 0x400000F3 -#define HV_X64_MSR_SYNDBG_RECV_BUFFER 0x400000F4 -#define HV_X64_MSR_SYNDBG_PENDING_BUFFER 0x400000F5 -#define HV_X64_MSR_SYNDBG_OPTIONS 0x400000FF - -#define HV_X64_SYNDBG_OPTION_USE_HCALLS BIT(2) - /* * Guest crash notification MSRs */ diff --git a/target/i386/kvm/hyperv-stub.c b/target/i386/kvm/hyperv-stub.c index 778ed782e6f..5836f53c23b 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 e3ac978648b..9865120cc43 100644 --- a/target/i386/kvm/hyperv.c +++ b/target/i386/kvm/hyperv.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" +#include "exec/target_page.h" #include "hyperv.h" #include "hw/hyperv/hyperv.h" #include "hyperv-proto.h" @@ -45,9 +46,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 +81,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 +151,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 67543296c3a..e45a4512fe9 100644 --- a/target/i386/kvm/hyperv.h +++ b/target/i386/kvm/hyperv.h @@ -15,7 +15,7 @@ #define TARGET_I386_HYPERV_H #include "cpu.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/hyperv/hyperv.h" #ifdef CONFIG_KVM @@ -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 7237378a7d4..16bde4de01e 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -10,18 +10,39 @@ #include "qemu/osdep.h" #include "cpu.h" #include "host-cpu.h" -#include "kvm-cpu.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "hw/boards.h" #include "kvm_i386.h" -#include "hw/core/accel-cpu.h" +#include "accel/accel-cpu-target.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++) { @@ -186,11 +223,11 @@ static void kvm_cpu_instance_init(CPUState *cs) kvm_cpu_xsave_init(); } -static void kvm_cpu_accel_class_init(ObjectClass *oc, void *data) +static void kvm_cpu_accel_class_init(ObjectClass *oc, const 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 e858ca21e5c..00000000000 --- 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.c b/target/i386/kvm/kvm.c index e5cd7cc8061..234878c613f 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -16,27 +16,34 @@ #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 "sysemu/sysemu.h" -#include "sysemu/hw_accel.h" -#include "sysemu/kvm_int.h" -#include "sysemu/runstate.h" +#include "vmsr_energy.h" +#include "system/system.h" +#include "system/hw_accel.h" +#include "system/kvm_int.h" +#include "system/runstate.h" #include "kvm_i386.h" +#include "../confidential-guest.h" #include "sev.h" +#include "tdx.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" @@ -50,6 +57,7 @@ #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" @@ -60,6 +68,7 @@ #include "hw/pci/msix.h" #include "migration/blocker.h" #include "exec/memattrs.h" +#include "exec/target_page.h" #include "trace.h" #include CONFIG_DEVICES @@ -74,23 +83,49 @@ 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) -#define MSR_KVM_WALL_CLOCK 0x11 -#define MSR_KVM_SYSTEM_TIME 0x12 - /* A 4096-byte buffer can hold the 8-byte kvm_msrs header, plus * 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 int 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 }; @@ -129,15 +164,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; @@ -154,9 +188,58 @@ 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); -bool 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", + [KVM_X86_TDX_VM] = "TDX", +}; + +bool kvm_is_vm_type_supported(int type) +{ + 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) { - return !!has_pit_state2; + 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) @@ -171,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; @@ -250,7 +328,7 @@ void kvm_synchronize_all_tsc(void) { CPUState *cpu; - if (kvm_enabled()) { + if (kvm_enabled() && !is_tdx_vm()) { CPU_FOREACH(cpu) { run_on_cpu(cpu, do_kvm_synchronize_tsc, RUN_ON_CPU_NULL); } @@ -316,7 +394,7 @@ static bool host_tsx_broken(void) /* Returns the value for a specific register on the cpuid entry */ -static uint32_t cpuid_entry_get_reg(struct kvm_cpuid_entry2 *entry, int reg) +uint32_t cpuid_entry_get_reg(struct kvm_cpuid_entry2 *entry, int reg) { uint32_t ret = 0; switch (reg) { @@ -338,9 +416,9 @@ static uint32_t cpuid_entry_get_reg(struct kvm_cpuid_entry2 *entry, int reg) /* Find matching entry for function/index on kvm_cpuid2 struct */ -static struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid, - uint32_t function, - uint32_t index) +struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid, + uint32_t function, + uint32_t index) { int i; for (i = 0; i < cpuid->nent; ++i) { @@ -373,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 @@ -477,20 +557,27 @@ 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 */ if (!kvm_irqchip_in_kernel()) { - ret &= ~(1U << KVM_FEATURE_PV_UNHALT); + ret &= ~CPUID_KVM_PV_UNHALT; } if (kvm_irqchip_is_split()) { - ret |= 1U << KVM_FEATURE_MSI_EXT_DEST_ID; + ret |= CPUID_KVM_MSI_EXT_DEST_ID; } } else if (function == KVM_CPUID_FEATURES && reg == R_EDX) { - ret |= 1U << KVM_HINTS_REALTIME; + ret |= CPUID_KVM_HINTS_REALTIME; } + if (current_machine->cgs) { + ret = x86_confidential_guest_adjust_cpuid_features( + X86_CONFIDENTIAL_GUEST(current_machine->cgs), + function, index, reg, ret); + } return ret; } @@ -575,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; @@ -685,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, @@ -726,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; @@ -807,6 +870,15 @@ static int kvm_arch_set_tsc_khz(CPUState *cs) int r, cur_freq; bool set_ioctl = false; + /* + * TSC of TD vcpu is immutable, it cannot be set/changed via vcpu scope + * VM_SET_TSC_KHZ, but only be initialized via VM scope VM_SET_TSC_KHZ + * before ioctl KVM_TDX_INIT_VM in tdx_pre_create_vcpu() + */ + if (is_tdx_vm()) { + return 0; + } + if (!env->tsc_khz) { return 0; } @@ -871,6 +943,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)", @@ -993,16 +1066,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 = { @@ -1254,6 +1326,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; @@ -1403,7 +1482,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); } } @@ -1601,7 +1681,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; @@ -1615,7 +1695,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; @@ -1698,21 +1778,24 @@ 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; } static Error *invtsc_mig_blocker; -#define KVM_MAX_CPUID_ENTRIES 100 - 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); @@ -1749,209 +1832,39 @@ static void kvm_init_nested_state(CPUX86State *env) } } -int kvm_arch_init_vcpu(CPUState *cs) +uint32_t kvm_x86_build_cpuid(CPUX86State *env, struct kvm_cpuid_entry2 *entries, + uint32_t cpuid_i) { - struct { - struct kvm_cpuid2 cpuid; - struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES]; - } cpuid_data; - /* - * The kernel defines these structs with padding fields so there - * should be no extra padding in our cpuid_data struct. - */ - QEMU_BUILD_BUG_ON(sizeof(cpuid_data) != - sizeof(struct kvm_cpuid2) + - sizeof(struct kvm_cpuid_entry2) * KVM_MAX_CPUID_ENTRIES); - - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - uint32_t limit, i, j, cpuid_i; + uint32_t limit, i, j; uint32_t unused; struct kvm_cpuid_entry2 *c; - uint32_t signature[3]; - int kvm_base = KVM_CPUID_SIGNATURE; - int max_nested_state_len; - int r; - Error *local_err = NULL; - - memset(&cpuid_data, 0, sizeof(cpuid_data)); - - cpuid_i = 0; - - has_xsave2 = kvm_check_extension(cs->kvm_state, KVM_CAP_XSAVE2); - - r = kvm_arch_set_tsc_khz(cs); - if (r < 0) { - return r; - } - - /* vcpu's TSC frequency is either specified by user, or following - * the value used by KVM if the former is not present. In the - * latter case, we query it from KVM and record in env->tsc_khz, - * so that vcpu's TSC frequency can be migrated later via this field. - */ - if (!env->tsc_khz) { - r = kvm_check_extension(cs->kvm_state, KVM_CAP_GET_TSC_KHZ) ? - kvm_vcpu_ioctl(cs, KVM_GET_TSC_KHZ) : - -ENOTSUP; - if (r > 0) { - env->tsc_khz = r; - } - } - - env->apic_bus_freq = KVM_APIC_BUS_FREQUENCY; - - /* - * kvm_hyperv_expand_features() is called here for the second time in case - * KVM_CAP_SYS_HYPERV_CPUID is not supported. While we can't possibly handle - * 'query-cpu-model-expansion' in this case as we don't have a KVM vCPU to - * check which Hyper-V enlightenments are supported and which are not, we - * can still proceed and check/expand Hyper-V enlightenments here so legacy - * behavior is preserved. - */ - if (!kvm_hyperv_expand_features(cpu, &local_err)) { - error_report_err(local_err); - return -ENOSYS; - } - - if (hyperv_enabled(cpu)) { - r = hyperv_init_vcpu(cpu); - if (r) { - return r; - } - - cpuid_i = hyperv_fill_cpuids(cs, cpuid_data.entries); - kvm_base = KVM_CPUID_SIGNATURE_NEXT; - has_msr_hv_hypercall = true; - } - - 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; - } - } - - 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; - c->eax = KVM_CPUID_FEATURES | kvm_base; - c->ebx = signature[0]; - c->ecx = signature[1]; - c->edx = signature[2]; - - c = &cpuid_data.entries[cpuid_i++]; - c->function = KVM_CPUID_FEATURES | kvm_base; - c->eax = env->features[FEAT_KVM]; - 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) { - fprintf(stderr, - "failed to enable KVM_CAP_ENFORCE_PV_FEATURE_CPUID: %s", - strerror(-r)); - abort(); - } - } - for (i = 0; i <= limit; i++) { + j = 0; if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "unsupported level value: 0x%x\n", limit); - abort(); + goto full; } - c = &cpuid_data.entries[cpuid_i++]; - + c = &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; + 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) { - fprintf(stderr, "cpuid_data is full, no space for " - "cpuid(eax:2):eax & 0xf = 0x%x\n", times); - abort(); + goto full; } - c = &cpuid_data.entries[cpuid_i++]; + 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); @@ -1959,7 +1872,8 @@ int kvm_arch_init_vcpu(CPUState *cs) break; } case 0x1f: - if (env->nr_dies < 2) { + if (!x86_has_cpuid_0x1f(env_archcpu(env))) { + cpuid_i--; break; } /* fallthrough */ @@ -1967,14 +1881,6 @@ int kvm_arch_init_vcpu(CPUState *cs) 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; @@ -1990,17 +1896,19 @@ int kvm_arch_init_vcpu(CPUState *cs) break; } if (i == 0xd && c->eax == 0) { - continue; + if (j < 63) { + continue; + } else { + cpuid_i--; + 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(); + goto full; } - c = &cpuid_data.entries[cpuid_i++]; + c = &entries[cpuid_i++]; } break; - case 0x7: case 0x12: for (j = 0; ; j++) { c->function = i; @@ -2013,16 +1921,16 @@ int kvm_arch_init_vcpu(CPUState *cs) } 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(); + goto full; } - c = &cpuid_data.entries[cpuid_i++]; + c = &entries[cpuid_i++]; } break; + case 0x7: case 0x14: case 0x1d: - case 0x1e: { + case 0x1e: + case 0x24: { uint32_t times; c->function = i; @@ -2033,11 +1941,9 @@ int kvm_arch_init_vcpu(CPUState *cs) 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(); + goto full; } - c = &cpuid_data.entries[cpuid_i++]; + c = &entries[cpuid_i++]; c->function = i; c->index = j; c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; @@ -2090,11 +1996,11 @@ int kvm_arch_init_vcpu(CPUState *cs) 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) { - fprintf(stderr, "unsupported xlevel value: 0x%x\n", limit); - abort(); + goto full; } - c = &cpuid_data.entries[cpuid_i++]; + c = &entries[cpuid_i++]; switch (i) { case 0x8000001d: @@ -2109,11 +2015,9 @@ int kvm_arch_init_vcpu(CPUState *cs) 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(); + goto full; } - c = &cpuid_data.entries[cpuid_i++]; + c = &entries[cpuid_i++]; } break; default: @@ -2136,11 +2040,11 @@ int kvm_arch_init_vcpu(CPUState *cs) 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) { - fprintf(stderr, "unsupported xlevel2 value: 0x%x\n", limit); - abort(); + goto full; } - c = &cpuid_data.entries[cpuid_i++]; + c = &entries[cpuid_i++]; c->function = i; c->flags = 0; @@ -2148,12 +2052,216 @@ int kvm_arch_init_vcpu(CPUState *cs) } } + 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_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + if (is_tdx_vm()) { + return tdx_pre_create_vcpu(cpu, errp); + } + + return 0; +} + +int kvm_arch_init_vcpu(CPUState *cs) +{ + struct { + struct kvm_cpuid2 cpuid; + struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES]; + } cpuid_data; + /* + * The kernel defines these structs with padding fields so there + * should be no extra padding in our cpuid_data struct. + */ + QEMU_BUILD_BUG_ON(sizeof(cpuid_data) != + sizeof(struct kvm_cpuid2) + + sizeof(struct kvm_cpuid_entry2) * KVM_MAX_CPUID_ENTRIES); + + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + uint32_t cpuid_i; + struct kvm_cpuid_entry2 *c; + uint32_t signature[3]; + int kvm_base = KVM_CPUID_SIGNATURE; + int max_nested_state_len; + int r; + Error *local_err = NULL; + + if (current_machine->cgs) { + r = x86_confidential_guest_check_features( + X86_CONFIDENTIAL_GUEST(current_machine->cgs), cs); + if (r < 0) { + return r; + } + } + + memset(&cpuid_data, 0, sizeof(cpuid_data)); + + cpuid_i = 0; + + has_xsave2 = kvm_check_extension(cs->kvm_state, KVM_CAP_XSAVE2); + + r = kvm_arch_set_tsc_khz(cs); + if (r < 0) { + return r; + } + + /* vcpu's TSC frequency is either specified by user, or following + * the value used by KVM if the former is not present. In the + * latter case, we query it from KVM and record in env->tsc_khz, + * so that vcpu's TSC frequency can be migrated later via this field. + */ + if (!env->tsc_khz) { + r = kvm_check_extension(cs->kvm_state, KVM_CAP_GET_TSC_KHZ) ? + kvm_vcpu_ioctl(cs, KVM_GET_TSC_KHZ) : + -ENOTSUP; + if (r > 0) { + env->tsc_khz = r; + } + } + + env->apic_bus_freq = KVM_APIC_BUS_FREQUENCY; + + /* + * kvm_hyperv_expand_features() is called here for the second time in case + * KVM_CAP_SYS_HYPERV_CPUID is not supported. While we can't possibly handle + * 'query-cpu-model-expansion' in this case as we don't have a KVM vCPU to + * check which Hyper-V enlightenments are supported and which are not, we + * can still proceed and check/expand Hyper-V enlightenments here so legacy + * behavior is preserved. + */ + if (!kvm_hyperv_expand_features(cpu, &local_err)) { + error_report_err(local_err); + return -ENOSYS; + } + + if (hyperv_enabled(cpu)) { + r = hyperv_init_vcpu(cpu); + if (r) { + return r; + } + + cpuid_i = hyperv_fill_cpuids(cs, cpuid_data.entries); + kvm_base = KVM_CPUID_SIGNATURE_NEXT; + has_msr_hv_hypercall = true; + } + + 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; + c->eax = KVM_CPUID_FEATURES | kvm_base; + c->ebx = signature[0]; + c->ecx = signature[1]; + c->edx = signature[2]; + + c = &cpuid_data.entries[cpuid_i++]; + c->function = KVM_CPUID_FEATURES | kvm_base; + c->eax = env->features[FEAT_KVM]; + c->edx = env->features[FEAT_KVM_HINTS]; + } + + if (cpu->kvm_pv_enforce_cpuid) { + r = kvm_vcpu_enable_cap(cs, KVM_CAP_ENFORCE_PV_FEATURE_CPUID, 0, 1); + if (r < 0) { + fprintf(stderr, + "failed to enable KVM_CAP_ENFORCE_PV_FEATURE_CPUID: %s", + strerror(-r)); + abort(); + } + } + + 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; @@ -2211,7 +2319,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; @@ -2269,7 +2377,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; } @@ -2330,6 +2438,21 @@ void kvm_arch_after_reset_vcpu(X86CPU *cpu) } } +void kvm_arch_reset_parked_vcpu(unsigned long vcpu_id, int kvm_fd) +{ + g_autofree struct kvm_msrs *msrs = NULL; + + msrs = g_malloc0(sizeof(*msrs) + sizeof(msrs->entries[0])); + msrs->entries[0].index = MSR_IA32_TSC; + msrs->entries[0].data = 1; /* match the value in x86_cpu_reset() */ + msrs->nmsrs++; + + if (ioctl(kvm_fd, KVM_SET_MSRS, msrs) != 1) { + warn_report("parked vCPU %lu TSC reset failed: %d", + vcpu_id, errno); + } +} + void kvm_arch_do_init_vcpu(X86CPU *cpu) { CPUX86State *env = &cpu->env; @@ -2502,6 +2625,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; } } } @@ -2511,13 +2636,58 @@ 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) { + *val = cpu_x86_get_msr_core_thread_count(cpu); + + 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->nr_threads * cs->nr_cores; /* thread count, bits 15..0 */ - *val |= ((uint32_t)cs->nr_cores << 16); /* core count, bits 31..16 */ + *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; } @@ -2556,67 +2726,534 @@ static void register_smram_listener(Notifier *n, void *unused) &smram_address_space, 1, "kvm-smram"); } -int kvm_arch_get_default_type(MachineState *ms) -{ +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; + + /* + * 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"); + return -1; + } + + if (!is_rapl_enabled()) { + return -1; + } + + /* 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"); + return -1; + } + + /* 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"); + return -1; + } + + /* 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); + return -1; + } + } + + /* 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"); + return -1; + } + + /* 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"); + return -1; + } + + qemu_thread_create(&r->msr_thr, "kvm-msr", + kvm_msr_energy_thread, + s, QEMU_THREAD_JOINABLE); + return 0; +} + +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); + + 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; + + 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); + } + + ret = kvm_filter_msr(s, MSR_CORE_THREAD_COUNT, + kvm_rdmsr_core_thread_count, NULL); + if (ret < 0) { + error_report("Could not install MSR_CORE_THREAD_COUNT handler: %s", + strerror(-ret)); + exit(1); + } + + return 0; +} + +static int kvm_vm_enable_energy_msrs(KVMState *s) +{ + int ret; + + if (s->msr_energy.enable == true) { + ret = kvm_filter_msr(s, MSR_RAPL_POWER_UNIT, + kvm_rdmsr_rapl_power_unit, NULL); + if (ret < 0) { + error_report("Could not install MSR_RAPL_POWER_UNIT handler: %s", + strerror(-ret)); + return ret; + } + + ret = kvm_filter_msr(s, MSR_PKG_POWER_LIMIT, + kvm_rdmsr_pkg_power_limit, NULL); + if (ret < 0) { + error_report("Could not install MSR_PKG_POWER_LIMIT handler: %s", + strerror(-ret)); + return ret; + } + + ret = kvm_filter_msr(s, MSR_PKG_POWER_INFO, + kvm_rdmsr_pkg_power_info, NULL); + if (ret < 0) { + error_report("Could not install MSR_PKG_POWER_INFO handler: %s", + strerror(-ret)); + return ret; + } + ret = kvm_filter_msr(s, MSR_PKG_ENERGY_STATUS, + kvm_rdmsr_pkg_energy_status, NULL); + if (ret < 0) { + error_report("Could not install MSR_PKG_ENERGY_STATUS handler: %s", + strerror(-ret)); + return ret; + } + } return 0; } 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; /* - * Initialize SEV context, if required - * - * If no memory encryption is requested (ms->cgs == NULL) this is - * a no-op. - * - * It's also a no-op if a non-SEV confidential guest support - * mechanism is selected. SEV is the only mechanism available to - * select on x86 at present, so this doesn't arise, but if new - * mechanisms are supported in future (e.g. TDX), they'll need - * their own initialization either here or elsewhere. + * Initialize confidential guest (SEV/TDX) context, if required */ - ret = sev_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; + if (ms->cgs) { + ret = confidential_guest_kvm_init(ms->cgs, &local_err); + if (ret < 0) { + error_report_err(local_err); + return ret; + } } - 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); - if (ret < 0) { - error_report("kvm: Failed to enable triple fault event cap: %s", - strerror(-ret)); - return ret; - } + ret = kvm_vm_enable_triple_fault_event(s); + if (ret < 0) { + return ret; } if (s->xen_version) { @@ -2642,54 +3279,33 @@ int kvm_arch_init(MachineState *ms, KVMState *s) return ret; } - kvm_get_supported_feature_msrs(s); + ret = kvm_get_supported_feature_msrs(s); + if (ret < 0) { + return ret; + } 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); + e820_add_entry(KVM_IDENTITY_BASE, 0x4000, E820_RESERVED); + + ret = kvm_vm_set_nr_mmu_pages(s); if (ret < 0) { - fprintf(stderr, "e820_add_entry() table is full\n"); return ret; } - 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; - } - } - if (kvm_check_extension(s, KVM_CAP_X86_SMM) && object_dynamic_cast(OBJECT(ms), TYPE_X86_MACHINE) && x86_machine_is_smm_enabled(X86_MACHINE(ms))) { @@ -2698,25 +3314,11 @@ 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)); + return ret; } } @@ -2724,16 +3326,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); @@ -2742,37 +3336,30 @@ 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) { + ret = kvm_vm_enable_energy_msrs(s); + if (ret < 0) { + return ret; + } + + ret = kvm_msr_energy_thread_init(s, ms); + if (ret < 0) { + error_report("kvm : error RAPL feature requirement not met"); + return ret; + } } } @@ -2879,40 +3466,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); @@ -3264,7 +3822,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 { @@ -3307,32 +3872,34 @@ static void kvm_init_msrs(X86CPU *cpu) CPUX86State *env = &cpu->env; kvm_msr_buf_reset(cpu); - if (has_msr_arch_capabs) { - kvm_msr_entry_add(cpu, MSR_IA32_ARCH_CAPABILITIES, - env->features[FEAT_ARCH_CAPABILITIES]); - } - if (has_msr_core_capabs) { - kvm_msr_entry_add(cpu, MSR_IA32_CORE_CAPABILITY, - env->features[FEAT_CORE_CAPABILITY]); - } + if (!is_tdx_vm()) { + if (has_msr_arch_capabs) { + kvm_msr_entry_add(cpu, MSR_IA32_ARCH_CAPABILITIES, + env->features[FEAT_ARCH_CAPABILITIES]); + } + + if (has_msr_core_capabs) { + kvm_msr_entry_add(cpu, MSR_IA32_CORE_CAPABILITY, + env->features[FEAT_CORE_CAPABILITY]); + } + + if (has_msr_perf_capabs && cpu->enable_pmu) { + kvm_msr_entry_add_perf(cpu, env->features); + } - if (has_msr_perf_capabs && cpu->enable_pmu) { - kvm_msr_entry_add_perf(cpu, env->features); + /* + * Older kernels do not include VMX MSRs in KVM_GET_MSR_INDEX_LIST, but + * all kernels with MSR features should have them. + */ + if (kvm_feature_msrs && cpu_has_vmx(env)) { + kvm_msr_entry_add_vmx(cpu, env->features); + } } if (has_msr_ucode_rev) { kvm_msr_entry_add(cpu, MSR_IA32_UCODE_REV, cpu->ucode_rev); } - - /* - * Older kernels do not include VMX MSRs in KVM_GET_MSR_INDEX_LIST, but - * all kernels with MSR features should have them. - */ - if (kvm_feature_msrs && cpu_has_vmx(env)) { - kvm_msr_entry_add_vmx(cpu, env->features); - } - assert(kvm_buf_set_msrs(cpu) == 0); } @@ -3394,6 +3961,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) { @@ -3401,6 +3971,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 @@ -3410,22 +3991,24 @@ static int kvm_put_msrs(X86CPU *cpu, int level) */ if (level >= KVM_PUT_RESET_STATE) { kvm_msr_entry_add(cpu, MSR_IA32_TSC, env->tsc); - kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, env->system_time_msr); - kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, env->wall_clock_msr); - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF_INT)) { + if (env->features[FEAT_KVM] & (CPUID_KVM_CLOCK | CPUID_KVM_CLOCK2)) { + kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, env->system_time_msr); + kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, env->wall_clock_msr); + } + if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF_INT) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_INT, env->async_pf_int_msr); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF)) { + if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_EN, env->async_pf_en_msr); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_PV_EOI)) { + if (env->features[FEAT_KVM] & CPUID_KVM_PV_EOI) { kvm_msr_entry_add(cpu, MSR_KVM_PV_EOI_EN, env->pv_eoi_en_msr); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_STEAL_TIME)) { + if (env->features[FEAT_KVM] & CPUID_KVM_STEAL_TIME) { kvm_msr_entry_add(cpu, MSR_KVM_STEAL_TIME, env->steal_time_msr); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_POLL_CONTROL)) { + if (env->features[FEAT_KVM] & CPUID_KVM_POLL_CONTROL) { kvm_msr_entry_add(cpu, MSR_KVM_POLL_CONTROL, env->poll_control_msr); } @@ -3483,13 +4066,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, @@ -3610,7 +4191,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 @@ -3643,8 +4224,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) { @@ -3659,45 +4238,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); @@ -3771,6 +4317,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); @@ -3810,6 +4360,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; @@ -3894,6 +4448,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) { @@ -3901,23 +4458,36 @@ 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); - kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, 0); - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF_INT)) { + if (env->features[FEAT_KVM] & (CPUID_KVM_CLOCK | CPUID_KVM_CLOCK2)) { + kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, 0); + kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, 0); + } + if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF_INT) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_INT, 0); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF)) { + if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_EN, 0); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_PV_EOI)) { + if (env->features[FEAT_KVM] & CPUID_KVM_PV_EOI) { kvm_msr_entry_add(cpu, MSR_KVM_PV_EOI_EN, 0); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_STEAL_TIME)) { + if (env->features[FEAT_KVM] & CPUID_KVM_STEAL_TIME) { kvm_msr_entry_add(cpu, MSR_KVM_STEAL_TIME, 0); } - if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_POLL_CONTROL)) { + if (env->features[FEAT_KVM] & CPUID_KVM_POLL_CONTROL) { kvm_msr_entry_add(cpu, MSR_KVM_POLL_CONTROL, 1); } if (has_architectural_pmu_version > 0) { @@ -4041,7 +4611,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) { @@ -4123,6 +4692,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; @@ -4376,6 +4972,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; } } @@ -4430,10 +5029,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) { @@ -4458,6 +5053,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()) { @@ -4472,12 +5068,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) { @@ -4501,10 +5091,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) { @@ -4570,47 +5156,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]; @@ -4628,10 +5179,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; @@ -4719,7 +5266,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; @@ -4729,11 +5276,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; } } @@ -4741,12 +5289,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; } } @@ -4764,6 +5314,7 @@ int kvm_arch_put_registers(CPUState *cpu, int level) 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; } } @@ -4771,53 +5322,51 @@ int kvm_arch_put_registers(CPUState *cpu, int level) 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; @@ -4826,6 +5375,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; } /* @@ -4834,44 +5384,54 @@ 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; } } @@ -4891,9 +5451,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) { @@ -4902,9 +5462,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) { @@ -4915,7 +5475,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 @@ -4968,7 +5528,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(); } } @@ -5016,12 +5576,12 @@ MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run) /* 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); } @@ -5302,15 +5862,16 @@ void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) } } -static bool kvm_install_msr_filters(KVMState *s) +static int kvm_install_msr_filters(KVMState *s) { uint64_t zero = 0; struct kvm_msr_filter filter = { .flags = KVM_MSR_FILTER_DEFAULT_ALLOW, }; - int r, i, j = 0; + int i, j = 0; - for (i = 0; i < KVM_MSR_FILTER_MAX_RANGES; i++) { + QEMU_BUILD_BUG_ON(ARRAY_SIZE(msr_handlers) != ARRAY_SIZE(filter.ranges)); + for (i = 0; i < ARRAY_SIZE(msr_handlers); i++) { KVMMSRHandlers *handler = &msr_handlers[i]; if (handler->msr) { struct kvm_msr_filter_range *range = &filter.ranges[j++]; @@ -5332,18 +5893,13 @@ static bool kvm_install_msr_filters(KVMState *s) } } - r = kvm_vm_ioctl(s, KVM_X86_SET_MSR_FILTER, &filter); - if (r) { - return false; - } - - return true; + return kvm_vm_ioctl(s, KVM_X86_SET_MSR_FILTER, &filter); } -bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, - QEMUWRMSRHandler *wrmsr) +static int kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, + QEMUWRMSRHandler *wrmsr) { - int i; + int i, ret; for (i = 0; i < ARRAY_SIZE(msr_handlers); i++) { if (!msr_handlers[i].msr) { @@ -5353,16 +5909,17 @@ bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, .wrmsr = wrmsr, }; - if (!kvm_install_msr_filters(s)) { + ret = kvm_install_msr_filters(s); + if (ret) { msr_handlers[i] = (KVMMSRHandlers) { }; - return false; + return ret; } - return true; + return 0; } } - return false; + return -EINVAL; } static int kvm_handle_rdmsr(X86CPU *cpu, struct kvm_run *run) @@ -5382,7 +5939,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) @@ -5401,7 +5958,7 @@ static int kvm_handle_wrmsr(X86CPU *cpu, struct kvm_run *run) } } - assert(false); + g_assert_not_reached(); } static bool has_sgx_provisioning; @@ -5441,6 +5998,71 @@ 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(X86CPU *cpu, struct kvm_run *run) +{ + struct kvm_pre_fault_memory mem; + uint64_t gpa, size, attributes; + int ret; + + 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); + + ret = kvm_convert_memory(gpa, size, attributes & KVM_MAP_GPA_RANGE_ENCRYPTED); + if (ret || !kvm_pre_fault_memory_supported) { + return ret; + } + + /* + * Opportunistically pre-fault memory in. Failures are ignored so that any + * errors in faulting in the memory will get captured in KVM page fault + * path when the guest first accesses the page. + */ + memset(&mem, 0, sizeof(mem)); + mem.gpa = gpa; + mem.size = size; + while (mem.size) { + if (kvm_vcpu_ioctl(CPU(cpu), KVM_PRE_FAULT_MEMORY, &mem)) { + break; + } + } + + return 0; +} + +static int kvm_handle_hypercall(X86CPU *cpu, struct kvm_run *run) +{ + if (run->hypercall.nr == KVM_HC_MAP_GPA_RANGE) + return kvm_handle_hc_map_gpa_range(cpu, run); + + return -EINVAL; +} + #define VMX_INVALID_GUEST_STATE 0x80000021 int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) @@ -5449,23 +6071,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; @@ -5491,9 +6112,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); @@ -5509,15 +6130,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; @@ -5536,6 +6157,34 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) ret = kvm_xen_handle_exit(cpu, &run->xen); break; #endif + case KVM_EXIT_HYPERCALL: + ret = kvm_handle_hypercall(cpu, run); + break; + case KVM_EXIT_SYSTEM_EVENT: + switch (run->system_event.type) { + case KVM_SYSTEM_EVENT_TDX_FATAL: + ret = tdx_handle_report_fatal_error(cpu, run); + break; + default: + ret = -1; + break; + } + break; + case KVM_EXIT_TDX: + /* + * run->tdx is already set up for the case where userspace + * does not handle the TDVMCALL. + */ + switch (run->tdx.nr) { + case TDVMCALL_GET_QUOTE: + tdx_handle_get_quote(cpu, run); + break; + case TDVMCALL_GET_TD_VM_CALL_INFO: + tdx_handle_get_tdvmcall_info(cpu, run); + break; + } + ret = 0; + break; default: fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); ret = -1; @@ -5608,7 +6257,7 @@ uint64_t kvm_swizzle_msi_ext_dest_id(uint64_t address) return address; } env = &X86_CPU(first_cpu)->env; - if (!(env->features[FEAT_KVM] & (1 << KVM_FEATURE_MSI_EXT_DEST_ID))) { + if (!(env->features[FEAT_KVM] & CPUID_KVM_MSI_EXT_DEST_ID)) { return address; } @@ -5653,7 +6302,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); @@ -5786,11 +6435,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) diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 55d4e68c342..5f83e8850a2 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -11,10 +11,11 @@ #ifndef QEMU_KVM_I386_H #define QEMU_KVM_I386_H -#include "sysemu/kvm.h" +#include "system/kvm.h" -#ifdef CONFIG_KVM +#define KVM_MAX_CPUID_ENTRIES 100 +/* always false if !CONFIG_KVM */ #define kvm_pit_in_kernel() \ (kvm_irqchip_in_kernel() && !kvm_irqchip_is_split()) #define kvm_pic_in_kernel() \ @@ -22,22 +23,15 @@ #define kvm_ioapic_in_kernel() \ (kvm_irqchip_in_kernel() && !kvm_irqchip_is_split()) -#else - -#define kvm_pit_in_kernel() 0 -#define kvm_pic_in_kernel() 0 -#define kvm_ioapic_in_kernel() 0 - -#endif /* CONFIG_KVM */ - bool kvm_has_smm(void); bool kvm_enable_x2apic(void); bool kvm_hv_vpindex_settable(void); -bool kvm_has_pit_state2(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); @@ -50,7 +44,14 @@ void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask); #ifdef CONFIG_KVM -bool kvm_has_adjust_clock(void); +#include + +typedef struct KvmCpuidInfo { + struct kvm_cpuid2 cpuid; + struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES]; +} KvmCpuidInfo; + +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); @@ -65,17 +66,12 @@ 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); -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); - +struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid, + uint32_t function, + uint32_t index); +uint32_t cpuid_entry_get_reg(struct kvm_cpuid_entry2 *entry, int reg); +uint32_t kvm_x86_build_cpuid(CPUX86State *env, struct kvm_cpuid_entry2 *entries, + uint32_t cpuid_i); #endif /* CONFIG_KVM */ void kvm_pc_setup_irq_routing(bool pci_enabled); diff --git a/target/i386/kvm/meson.build b/target/i386/kvm/meson.build index 5d9174bbb5d..2675bf89027 100644 --- a/target/i386/kvm/meson.build +++ b/target/i386/kvm/meson.build @@ -1,14 +1,15 @@ -i386_softmmu_kvm_ss = ss.source_set() +i386_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_XEN_EMU', if_true: files('xen-emu.c')) +i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files('xen-emu.c')) -i386_softmmu_kvm_ss.add(when: 'CONFIG_SEV', if_false: files('sev-stub.c')) +i386_kvm_ss.add(when: 'CONFIG_TDX', if_true: files('tdx.c', 'tdx-quote-generator.c'), if_false: files('tdx-stub.c')) i386_system_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c')) -i386_system_ss.add_all(when: 'CONFIG_KVM', if_true: i386_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 1be5341e8a6..00000000000 --- 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/tdx-quote-generator.c b/target/i386/kvm/tdx-quote-generator.c new file mode 100644 index 00000000000..f59715f6175 --- /dev/null +++ b/target/i386/kvm/tdx-quote-generator.c @@ -0,0 +1,300 @@ +/* + * QEMU TDX Quote Generation Support + * + * Copyright (c) 2025 Intel Corporation + * + * Author: + * Xiaoyao Li + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qapi/qapi-visit-sockets.h" + +#include "tdx-quote-generator.h" + +#define QGS_MSG_LIB_MAJOR_VER 1 +#define QGS_MSG_LIB_MINOR_VER 1 + +typedef enum _qgs_msg_type_t { + GET_QUOTE_REQ = 0, + GET_QUOTE_RESP = 1, + GET_COLLATERAL_REQ = 2, + GET_COLLATERAL_RESP = 3, + GET_PLATFORM_INFO_REQ = 4, + GET_PLATFORM_INFO_RESP = 5, + QGS_MSG_TYPE_MAX +} qgs_msg_type_t; + +typedef struct _qgs_msg_header_t { + uint16_t major_version; + uint16_t minor_version; + uint32_t type; + uint32_t size; // size of the whole message, include this header, in byte + uint32_t error_code; // used in response only +} qgs_msg_header_t; + +typedef struct _qgs_msg_get_quote_req_t { + qgs_msg_header_t header; // header.type = GET_QUOTE_REQ + uint32_t report_size; // cannot be 0 + uint32_t id_list_size; // length of id_list, in byte, can be 0 +} qgs_msg_get_quote_req_t; + +typedef struct _qgs_msg_get_quote_resp_s { + qgs_msg_header_t header; // header.type = GET_QUOTE_RESP + uint32_t selected_id_size; // can be 0 in case only one id is sent in request + uint32_t quote_size; // length of quote_data, in byte + uint8_t id_quote[]; // selected id followed by quote +} qgs_msg_get_quote_resp_t; + +#define HEADER_SIZE 4 + +static uint32_t decode_header(const char *buf, size_t len) { + if (len < HEADER_SIZE) { + return 0; + } + uint32_t msg_size = 0; + for (uint32_t i = 0; i < HEADER_SIZE; ++i) { + msg_size = msg_size * 256 + (buf[i] & 0xFF); + } + return msg_size; +} + +static void encode_header(char *buf, size_t len, uint32_t size) { + assert(len >= HEADER_SIZE); + buf[0] = ((size >> 24) & 0xFF); + buf[1] = ((size >> 16) & 0xFF); + buf[2] = ((size >> 8) & 0xFF); + buf[3] = (size & 0xFF); +} + +static void tdx_generate_quote_cleanup(TdxGenerateQuoteTask *task) +{ + timer_del(&task->timer); + + g_source_remove(task->watch); + qio_channel_close(QIO_CHANNEL(task->sioc), NULL); + object_unref(OBJECT(task->sioc)); + + task->completion(task); +} + +static gboolean tdx_get_quote_read(QIOChannel *ioc, GIOCondition condition, + gpointer opaque) +{ + TdxGenerateQuoteTask *task = opaque; + Error *err = NULL; + int ret; + + ret = qio_channel_read(ioc, task->receive_buf + task->receive_buf_received, + task->payload_len - task->receive_buf_received, &err); + if (ret < 0) { + if (ret == QIO_CHANNEL_ERR_BLOCK) { + return G_SOURCE_CONTINUE; + } else { + error_report_err(err); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + } + + if (ret == 0) { + error_report("End of file before reply received"); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + + task->receive_buf_received += ret; + if (task->receive_buf_received >= HEADER_SIZE) { + uint32_t len = decode_header(task->receive_buf, + task->receive_buf_received); + if (len == 0 || + len > (task->payload_len - HEADER_SIZE)) { + error_report("Message len %u must be non-zero & less than %zu", + len, (task->payload_len - HEADER_SIZE)); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + + /* Now we know the size, shrink to fit */ + task->payload_len = HEADER_SIZE + len; + task->receive_buf = g_renew(char, + task->receive_buf, + task->payload_len); + } + + if (task->receive_buf_received >= (sizeof(qgs_msg_header_t) + HEADER_SIZE)) { + qgs_msg_header_t *hdr = (qgs_msg_header_t *)(task->receive_buf + HEADER_SIZE); + if (hdr->major_version != QGS_MSG_LIB_MAJOR_VER || + hdr->minor_version != QGS_MSG_LIB_MINOR_VER) { + error_report("Invalid QGS message header version %d.%d", + hdr->major_version, + hdr->minor_version); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + if (hdr->type != GET_QUOTE_RESP) { + error_report("Invalid QGS message type %d", + hdr->type); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + if (hdr->size > (task->payload_len - HEADER_SIZE)) { + error_report("QGS message size %d exceeds payload capacity %zu", + hdr->size, task->payload_len); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + if (hdr->error_code != 0) { + error_report("QGS message error code %d", + hdr->error_code); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + } + if (task->receive_buf_received >= (sizeof(qgs_msg_get_quote_resp_t) + HEADER_SIZE)) { + qgs_msg_get_quote_resp_t *msg = (qgs_msg_get_quote_resp_t *)(task->receive_buf + HEADER_SIZE); + if (msg->selected_id_size != 0) { + error_report("QGS message selected ID was %d not 0", + msg->selected_id_size); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + + if ((task->payload_len - HEADER_SIZE - sizeof(qgs_msg_get_quote_resp_t)) != + msg->quote_size) { + error_report("QGS quote size %d should be %zu", + msg->quote_size, + (task->payload_len - sizeof(qgs_msg_get_quote_resp_t))); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + } + + if (task->receive_buf_received == task->payload_len) { + size_t strip = HEADER_SIZE + sizeof(qgs_msg_get_quote_resp_t); + memmove(task->receive_buf, + task->receive_buf + strip, + task->receive_buf_received - strip); + task->receive_buf_received -= strip; + task->status_code = TDX_VP_GET_QUOTE_SUCCESS; + goto end; + } + + return G_SOURCE_CONTINUE; + +end: + tdx_generate_quote_cleanup(task); + return G_SOURCE_REMOVE; +} + +static gboolean tdx_send_report(QIOChannel *ioc, GIOCondition condition, + gpointer opaque) +{ + TdxGenerateQuoteTask *task = opaque; + Error *err = NULL; + int ret; + + ret = qio_channel_write(ioc, task->send_data + task->send_data_sent, + task->send_data_size - task->send_data_sent, &err); + if (ret < 0) { + if (ret == QIO_CHANNEL_ERR_BLOCK) { + ret = 0; + } else { + error_report_err(err); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + tdx_generate_quote_cleanup(task); + goto end; + } + } + task->send_data_sent += ret; + + if (task->send_data_sent == task->send_data_size) { + task->watch = qio_channel_add_watch(QIO_CHANNEL(task->sioc), G_IO_IN, + tdx_get_quote_read, task, NULL); + goto end; + } + + return G_SOURCE_CONTINUE; + +end: + return G_SOURCE_REMOVE; +} + +static void tdx_quote_generator_connected(QIOTask *qio_task, gpointer opaque) +{ + TdxGenerateQuoteTask *task = opaque; + Error *err = NULL; + int ret; + + ret = qio_task_propagate_error(qio_task, &err); + if (ret) { + error_report_err(err); + task->status_code = TDX_VP_GET_QUOTE_QGS_UNAVAILABLE; + tdx_generate_quote_cleanup(task); + return; + } + + task->watch = qio_channel_add_watch(QIO_CHANNEL(task->sioc), G_IO_OUT, + tdx_send_report, task, NULL); +} + +#define TRANSACTION_TIMEOUT 30000 + +static void getquote_expired(void *opaque) +{ + TdxGenerateQuoteTask *task = opaque; + + task->status_code = TDX_VP_GET_QUOTE_ERROR; + tdx_generate_quote_cleanup(task); +} + +static void setup_get_quote_timer(TdxGenerateQuoteTask *task) +{ + int64_t time; + + timer_init_ms(&task->timer, QEMU_CLOCK_VIRTUAL, getquote_expired, task); + time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); + timer_mod(&task->timer, time + TRANSACTION_TIMEOUT); +} + +void tdx_generate_quote(TdxGenerateQuoteTask *task, + SocketAddress *qg_sock_addr) +{ + QIOChannelSocket *sioc; + qgs_msg_get_quote_req_t msg; + + /* Prepare a QGS message prelude */ + msg.header.major_version = QGS_MSG_LIB_MAJOR_VER; + msg.header.minor_version = QGS_MSG_LIB_MINOR_VER; + msg.header.type = GET_QUOTE_REQ; + msg.header.size = sizeof(msg) + task->send_data_size; + msg.header.error_code = 0; + msg.report_size = task->send_data_size; + msg.id_list_size = 0; + + /* Make room to add the QGS message prelude */ + task->send_data = g_renew(char, + task->send_data, + task->send_data_size + sizeof(msg) + HEADER_SIZE); + memmove(task->send_data + sizeof(msg) + HEADER_SIZE, + task->send_data, + task->send_data_size); + memcpy(task->send_data + HEADER_SIZE, + &msg, + sizeof(msg)); + encode_header(task->send_data, HEADER_SIZE, task->send_data_size + sizeof(msg)); + task->send_data_size += sizeof(msg) + HEADER_SIZE; + + sioc = qio_channel_socket_new(); + task->sioc = sioc; + + setup_get_quote_timer(task); + + qio_channel_socket_connect_async(sioc, qg_sock_addr, + tdx_quote_generator_connected, task, + NULL, NULL); +} diff --git a/target/i386/kvm/tdx-quote-generator.h b/target/i386/kvm/tdx-quote-generator.h new file mode 100644 index 00000000000..3bd9b8ef331 --- /dev/null +++ b/target/i386/kvm/tdx-quote-generator.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef QEMU_I386_TDX_QUOTE_GENERATOR_H +#define QEMU_I386_TDX_QUOTE_GENERATOR_H + +#include "qom/object_interfaces.h" +#include "io/channel-socket.h" +#include "exec/hwaddr.h" + +#define TDX_GET_QUOTE_STRUCTURE_VERSION 1ULL + +#define TDX_VP_GET_QUOTE_SUCCESS 0ULL +#define TDX_VP_GET_QUOTE_IN_FLIGHT (-1ULL) +#define TDX_VP_GET_QUOTE_ERROR 0x8000000000000000ULL +#define TDX_VP_GET_QUOTE_QGS_UNAVAILABLE 0x8000000000000001ULL + +/* Limit to avoid resource starvation. */ +#define TDX_GET_QUOTE_MAX_BUF_LEN (128 * 1024) +#define TDX_MAX_GET_QUOTE_REQUEST 16 + +#define TDX_GET_QUOTE_HDR_SIZE 24 + +/* Format of pages shared with guest. */ +struct tdx_get_quote_header { + /* Format version: must be 1 in little endian. */ + uint64_t structure_version; + + /* + * GetQuote status code in little endian: + * Guest must set error_code to 0 to avoid information leak. + * Qemu sets this before interrupting guest. + */ + uint64_t error_code; + + /* + * in-message size in little endian: The message will follow this header. + * The in-message will be send to QGS. + */ + uint32_t in_len; + + /* + * out-message size in little endian: + * On request, out_len must be zero to avoid information leak. + * On return, message size from QGS. Qemu overwrites this field. + * The message will follows this header. The in-message is overwritten. + */ + uint32_t out_len; + + /* + * Message buffer follows. + * Guest sets message that will be send to QGS. If out_len > in_len, guest + * should zero remaining buffer to avoid information leak. + * Qemu overwrites this buffer with a message returned from QGS. + */ +}; + +typedef struct TdxGenerateQuoteTask { + hwaddr buf_gpa; + hwaddr payload_gpa; + uint64_t payload_len; + + char *send_data; + uint64_t send_data_size; + uint64_t send_data_sent; + + char *receive_buf; + uint64_t receive_buf_received; + + uint64_t status_code; + struct tdx_get_quote_header hdr; + + QIOChannelSocket *sioc; + guint watch; + QEMUTimer timer; + + void (*completion)(struct TdxGenerateQuoteTask *task); + void *opaque; +} TdxGenerateQuoteTask; + +void tdx_generate_quote(TdxGenerateQuoteTask *task, SocketAddress *qg_sock_addr); + +#endif /* QEMU_I386_TDX_QUOTE_GENERATOR_H */ diff --git a/target/i386/kvm/tdx-stub.c b/target/i386/kvm/tdx-stub.c new file mode 100644 index 00000000000..76fee49eff0 --- /dev/null +++ b/target/i386/kvm/tdx-stub.c @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" + +#include "tdx.h" + +int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + return -EINVAL; +} + +int tdx_parse_tdvf(void *flash_ptr, int size) +{ + return -EINVAL; +} + +int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run) +{ + return -EINVAL; +} + +void tdx_handle_get_quote(X86CPU *cpu, struct kvm_run *run) +{ +} + +void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run) +{ +} diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c new file mode 100644 index 00000000000..e809e4b2dfa --- /dev/null +++ b/target/i386/kvm/tdx.c @@ -0,0 +1,1487 @@ +/* + * QEMU TDX support + * + * Copyright (c) 2025 Intel Corporation + * + * Author: + * Xiaoyao Li + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/base64.h" +#include "qemu/mmap-alloc.h" +#include "qapi/error.h" +#include "qapi/qapi-visit-sockets.h" +#include "qom/object_interfaces.h" +#include "crypto/hash.h" +#include "system/kvm_int.h" +#include "system/runstate.h" +#include "system/system.h" +#include "system/ramblock.h" +#include "system/address-spaces.h" + +#include + +#include "cpu.h" +#include "cpu-internal.h" +#include "host-cpu.h" +#include "hw/i386/e820_memory_layout.h" +#include "hw/i386/tdvf.h" +#include "hw/i386/x86.h" +#include "hw/i386/tdvf-hob.h" +#include "kvm_i386.h" +#include "tdx.h" +#include "tdx-quote-generator.h" + +#include "standard-headers/asm-x86/kvm_para.h" + +#define TDX_MIN_TSC_FREQUENCY_KHZ (100 * 1000) +#define TDX_MAX_TSC_FREQUENCY_KHZ (10 * 1000 * 1000) + +#define TDX_TD_ATTRIBUTES_DEBUG BIT_ULL(0) +#define TDX_TD_ATTRIBUTES_SEPT_VE_DISABLE BIT_ULL(28) +#define TDX_TD_ATTRIBUTES_PKS BIT_ULL(30) +#define TDX_TD_ATTRIBUTES_PERFMON BIT_ULL(63) + +#define TDX_SUPPORTED_TD_ATTRS (TDX_TD_ATTRIBUTES_SEPT_VE_DISABLE |\ + TDX_TD_ATTRIBUTES_PKS | \ + TDX_TD_ATTRIBUTES_PERFMON) + +#define TDX_SUPPORTED_KVM_FEATURES ((1U << KVM_FEATURE_NOP_IO_DELAY) | \ + (1U << KVM_FEATURE_PV_UNHALT) | \ + (1U << KVM_FEATURE_PV_TLB_FLUSH) | \ + (1U << KVM_FEATURE_PV_SEND_IPI) | \ + (1U << KVM_FEATURE_POLL_CONTROL) | \ + (1U << KVM_FEATURE_PV_SCHED_YIELD) | \ + (1U << KVM_FEATURE_MSI_EXT_DEST_ID)) + +static TdxGuest *tdx_guest; + +static struct kvm_tdx_capabilities *tdx_caps; +static struct kvm_cpuid2 *tdx_supported_cpuid; + +/* Valid after kvm_arch_init()->confidential_guest_kvm_init()->tdx_kvm_init() */ +bool is_tdx_vm(void) +{ + return !!tdx_guest; +} + +enum tdx_ioctl_level { + TDX_VM_IOCTL, + TDX_VCPU_IOCTL, +}; + +static int tdx_ioctl_internal(enum tdx_ioctl_level level, void *state, + int cmd_id, __u32 flags, void *data, + Error **errp) +{ + struct kvm_tdx_cmd tdx_cmd = {}; + int r; + + const char *tdx_ioctl_name[] = { + [KVM_TDX_CAPABILITIES] = "KVM_TDX_CAPABILITIES", + [KVM_TDX_INIT_VM] = "KVM_TDX_INIT_VM", + [KVM_TDX_INIT_VCPU] = "KVM_TDX_INIT_VCPU", + [KVM_TDX_INIT_MEM_REGION] = "KVM_TDX_INIT_MEM_REGION", + [KVM_TDX_FINALIZE_VM] = "KVM_TDX_FINALIZE_VM", + [KVM_TDX_GET_CPUID] = "KVM_TDX_GET_CPUID", + }; + + tdx_cmd.id = cmd_id; + tdx_cmd.flags = flags; + tdx_cmd.data = (__u64)(unsigned long)data; + + switch (level) { + case TDX_VM_IOCTL: + r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, &tdx_cmd); + break; + case TDX_VCPU_IOCTL: + r = kvm_vcpu_ioctl(state, KVM_MEMORY_ENCRYPT_OP, &tdx_cmd); + break; + default: + error_setg(errp, "Invalid tdx_ioctl_level %d", level); + return -EINVAL; + } + + if (r < 0) { + error_setg_errno(errp, -r, "TDX ioctl %s failed, hw_errors: 0x%llx", + tdx_ioctl_name[cmd_id], tdx_cmd.hw_error); + } + return r; +} + +static inline int tdx_vm_ioctl(int cmd_id, __u32 flags, void *data, + Error **errp) +{ + return tdx_ioctl_internal(TDX_VM_IOCTL, NULL, cmd_id, flags, data, errp); +} + +static inline int tdx_vcpu_ioctl(CPUState *cpu, int cmd_id, __u32 flags, + void *data, Error **errp) +{ + return tdx_ioctl_internal(TDX_VCPU_IOCTL, cpu, cmd_id, flags, data, errp); +} + +static int get_tdx_capabilities(Error **errp) +{ + struct kvm_tdx_capabilities *caps; + /* 1st generation of TDX reports 6 cpuid configs */ + int nr_cpuid_configs = 6; + size_t size; + int r; + + do { + Error *local_err = NULL; + size = sizeof(struct kvm_tdx_capabilities) + + nr_cpuid_configs * sizeof(struct kvm_cpuid_entry2); + caps = g_malloc0(size); + caps->cpuid.nent = nr_cpuid_configs; + + r = tdx_vm_ioctl(KVM_TDX_CAPABILITIES, 0, caps, &local_err); + if (r == -E2BIG) { + g_free(caps); + nr_cpuid_configs *= 2; + if (nr_cpuid_configs > KVM_MAX_CPUID_ENTRIES) { + error_report("KVM TDX seems broken that number of CPUID entries" + " in kvm_tdx_capabilities exceeds limit: %d", + KVM_MAX_CPUID_ENTRIES); + error_propagate(errp, local_err); + return r; + } + error_free(local_err); + } else if (r < 0) { + g_free(caps); + error_propagate(errp, local_err); + return r; + } + } while (r == -E2BIG); + + tdx_caps = caps; + + return 0; +} + +void tdx_set_tdvf_region(MemoryRegion *tdvf_mr) +{ + assert(!tdx_guest->tdvf_mr); + tdx_guest->tdvf_mr = tdvf_mr; +} + +static TdxFirmwareEntry *tdx_get_hob_entry(TdxGuest *tdx) +{ + TdxFirmwareEntry *entry; + + for_each_tdx_fw_entry(&tdx->tdvf, entry) { + if (entry->type == TDVF_SECTION_TYPE_TD_HOB) { + return entry; + } + } + error_report("TDVF metadata doesn't specify TD_HOB location."); + exit(1); +} + +static void tdx_add_ram_entry(uint64_t address, uint64_t length, + enum TdxRamType type) +{ + uint32_t nr_entries = tdx_guest->nr_ram_entries; + tdx_guest->ram_entries = g_renew(TdxRamEntry, tdx_guest->ram_entries, + nr_entries + 1); + + tdx_guest->ram_entries[nr_entries].address = address; + tdx_guest->ram_entries[nr_entries].length = length; + tdx_guest->ram_entries[nr_entries].type = type; + tdx_guest->nr_ram_entries++; +} + +static int tdx_accept_ram_range(uint64_t address, uint64_t length) +{ + uint64_t head_start, tail_start, head_length, tail_length; + uint64_t tmp_address, tmp_length; + TdxRamEntry *e; + int i = 0; + + do { + if (i == tdx_guest->nr_ram_entries) { + return -1; + } + + e = &tdx_guest->ram_entries[i++]; + } while (address + length <= e->address || address >= e->address + e->length); + + /* + * The to-be-accepted ram range must be fully contained by one + * RAM entry. + */ + if (e->address > address || + e->address + e->length < address + length) { + return -1; + } + + if (e->type == TDX_RAM_ADDED) { + return 0; + } + + tmp_address = e->address; + tmp_length = e->length; + + e->address = address; + e->length = length; + e->type = TDX_RAM_ADDED; + + head_length = address - tmp_address; + if (head_length > 0) { + head_start = tmp_address; + tdx_add_ram_entry(head_start, head_length, TDX_RAM_UNACCEPTED); + } + + tail_start = address + length; + if (tail_start < tmp_address + tmp_length) { + tail_length = tmp_address + tmp_length - tail_start; + tdx_add_ram_entry(tail_start, tail_length, TDX_RAM_UNACCEPTED); + } + + return 0; +} + +static int tdx_ram_entry_compare(const void *lhs_, const void* rhs_) +{ + const TdxRamEntry *lhs = lhs_; + const TdxRamEntry *rhs = rhs_; + + if (lhs->address == rhs->address) { + return 0; + } + if (le64_to_cpu(lhs->address) > le64_to_cpu(rhs->address)) { + return 1; + } + return -1; +} + +static void tdx_init_ram_entries(void) +{ + unsigned i, j, nr_e820_entries; + + nr_e820_entries = e820_get_table(NULL); + tdx_guest->ram_entries = g_new(TdxRamEntry, nr_e820_entries); + + for (i = 0, j = 0; i < nr_e820_entries; i++) { + uint64_t addr, len; + + if (e820_get_entry(i, E820_RAM, &addr, &len)) { + tdx_guest->ram_entries[j].address = addr; + tdx_guest->ram_entries[j].length = len; + tdx_guest->ram_entries[j].type = TDX_RAM_UNACCEPTED; + j++; + } + } + tdx_guest->nr_ram_entries = j; +} + +static void tdx_post_init_vcpus(void) +{ + TdxFirmwareEntry *hob; + CPUState *cpu; + + hob = tdx_get_hob_entry(tdx_guest); + CPU_FOREACH(cpu) { + tdx_vcpu_ioctl(cpu, KVM_TDX_INIT_VCPU, 0, (void *)(uintptr_t)hob->address, + &error_fatal); + } +} + +static void tdx_finalize_vm(Notifier *notifier, void *unused) +{ + TdxFirmware *tdvf = &tdx_guest->tdvf; + TdxFirmwareEntry *entry; + RAMBlock *ram_block; + Error *local_err = NULL; + int r; + + tdx_init_ram_entries(); + + for_each_tdx_fw_entry(tdvf, entry) { + switch (entry->type) { + case TDVF_SECTION_TYPE_BFV: + case TDVF_SECTION_TYPE_CFV: + entry->mem_ptr = tdvf->mem_ptr + entry->data_offset; + break; + case TDVF_SECTION_TYPE_TD_HOB: + case TDVF_SECTION_TYPE_TEMP_MEM: + entry->mem_ptr = qemu_ram_mmap(-1, entry->size, + qemu_real_host_page_size(), 0, 0); + if (entry->mem_ptr == MAP_FAILED) { + error_report("Failed to mmap memory for TDVF section %d", + entry->type); + exit(1); + } + if (tdx_accept_ram_range(entry->address, entry->size)) { + error_report("Failed to accept memory for TDVF section %d", + entry->type); + qemu_ram_munmap(-1, entry->mem_ptr, entry->size); + exit(1); + } + break; + default: + error_report("Unsupported TDVF section %d", entry->type); + exit(1); + } + } + + qsort(tdx_guest->ram_entries, tdx_guest->nr_ram_entries, + sizeof(TdxRamEntry), &tdx_ram_entry_compare); + + tdvf_hob_create(tdx_guest, tdx_get_hob_entry(tdx_guest)); + + tdx_post_init_vcpus(); + + for_each_tdx_fw_entry(tdvf, entry) { + struct kvm_tdx_init_mem_region region; + uint32_t flags; + + region = (struct kvm_tdx_init_mem_region) { + .source_addr = (uintptr_t)entry->mem_ptr, + .gpa = entry->address, + .nr_pages = entry->size >> 12, + }; + + flags = entry->attributes & TDVF_SECTION_ATTRIBUTES_MR_EXTEND ? + KVM_TDX_MEASURE_MEMORY_REGION : 0; + + do { + error_free(local_err); + local_err = NULL; + r = tdx_vcpu_ioctl(first_cpu, KVM_TDX_INIT_MEM_REGION, flags, + ®ion, &local_err); + } while (r == -EAGAIN || r == -EINTR); + if (r < 0) { + error_report_err(local_err); + exit(1); + } + + if (entry->type == TDVF_SECTION_TYPE_TD_HOB || + entry->type == TDVF_SECTION_TYPE_TEMP_MEM) { + qemu_ram_munmap(-1, entry->mem_ptr, entry->size); + entry->mem_ptr = NULL; + } + } + + /* + * TDVF image has been copied into private region above via + * KVM_MEMORY_MAPPING. It becomes useless. + */ + ram_block = tdx_guest->tdvf_mr->ram_block; + ram_block_discard_range(ram_block, 0, ram_block->max_length); + + tdx_vm_ioctl(KVM_TDX_FINALIZE_VM, 0, NULL, &error_fatal); + CONFIDENTIAL_GUEST_SUPPORT(tdx_guest)->ready = true; +} + +static Notifier tdx_machine_done_notify = { + .notify = tdx_finalize_vm, +}; + +/* + * Some CPUID bits change from fixed1 to configurable bits when TDX module + * supports TDX_FEATURES0.VE_REDUCTION. e.g., MCA/MCE/MTRR/CORE_CAPABILITY. + * + * To make QEMU work with all the versions of TDX module, keep the fixed1 bits + * here if they are ever fixed1 bits in any of the version though not fixed1 in + * the latest version. Otherwise, with the older version of TDX module, QEMU may + * treat the fixed1 bit as unsupported. + * + * For newer TDX module, it does no harm to keep them in tdx_fixed1_bits even + * though they changed to configurable bits. Because tdx_fixed1_bits is used to + * setup the supported bits. + */ +KvmCpuidInfo tdx_fixed1_bits = { + .cpuid.nent = 8, + .entries[0] = { + .function = 0x1, + .index = 0, + .ecx = CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_DTES64 | + CPUID_EXT_DSCPL | CPUID_EXT_SSSE3 | CPUID_EXT_CX16 | + CPUID_EXT_PDCM | CPUID_EXT_PCID | CPUID_EXT_SSE41 | + CPUID_EXT_SSE42 | CPUID_EXT_X2APIC | CPUID_EXT_MOVBE | + CPUID_EXT_POPCNT | CPUID_EXT_AES | CPUID_EXT_XSAVE | + CPUID_EXT_RDRAND | CPUID_EXT_HYPERVISOR, + .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_CLFLUSH | CPUID_DTS | CPUID_MMX | CPUID_FXSR | + CPUID_SSE | CPUID_SSE2, + }, + .entries[1] = { + .function = 0x6, + .index = 0, + .eax = CPUID_6_EAX_ARAT, + }, + .entries[2] = { + .function = 0x7, + .index = 0, + .flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX, + .ebx = CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_FDP_EXCPTN_ONLY | + CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_INVPCID | + CPUID_7_0_EBX_ZERO_FCS_FDS | CPUID_7_0_EBX_RDSEED | + CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT | + CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_SHA_NI, + .ecx = CPUID_7_0_ECX_BUS_LOCK_DETECT | CPUID_7_0_ECX_MOVDIRI | + CPUID_7_0_ECX_MOVDIR64B, + .edx = CPUID_7_0_EDX_MD_CLEAR | 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, + }, + .entries[3] = { + .function = 0x7, + .index = 2, + .flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX, + .edx = CPUID_7_2_EDX_PSFD | CPUID_7_2_EDX_IPRED_CTRL | + CPUID_7_2_EDX_RRSBA_CTRL | CPUID_7_2_EDX_BHI_CTRL, + }, + .entries[4] = { + .function = 0xD, + .index = 0, + .flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX, + .eax = XSTATE_FP_MASK | XSTATE_SSE_MASK, + }, + .entries[5] = { + .function = 0xD, + .index = 1, + .flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX, + .eax = CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC| + CPUID_XSAVE_XGETBV1 | CPUID_XSAVE_XSAVES, + }, + .entries[6] = { + .function = 0x80000001, + .index = 0, + .ecx = CPUID_EXT3_LAHF_LM | CPUID_EXT3_ABM | CPUID_EXT3_3DNOWPREFETCH, + /* + * Strictly speaking, SYSCALL is not fixed1 bit since it depends on + * the CPU to be in 64-bit mode. But here fixed1 is used to serve the + * purpose of supported bits for TDX. In this sense, SYACALL is always + * supported. + */ + .edx = CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | CPUID_EXT2_PDPE1GB | + CPUID_EXT2_RDTSCP | CPUID_EXT2_LM, + }, + .entries[7] = { + .function = 0x80000007, + .index = 0, + .edx = CPUID_APM_INVTSC, + }, +}; + +typedef struct TdxAttrsMap { + uint32_t attr_index; + uint32_t cpuid_leaf; + uint32_t cpuid_subleaf; + int cpuid_reg; + uint32_t feat_mask; +} TdxAttrsMap; + +static TdxAttrsMap tdx_attrs_maps[] = { + {.attr_index = 27, + .cpuid_leaf = 7, + .cpuid_subleaf = 1, + .cpuid_reg = R_EAX, + .feat_mask = CPUID_7_1_EAX_LASS,}, + + {.attr_index = 30, + .cpuid_leaf = 7, + .cpuid_subleaf = 0, + .cpuid_reg = R_ECX, + .feat_mask = CPUID_7_0_ECX_PKS,}, + + {.attr_index = 31, + .cpuid_leaf = 7, + .cpuid_subleaf = 0, + .cpuid_reg = R_ECX, + .feat_mask = CPUID_7_0_ECX_KeyLocker,}, +}; + +typedef struct TdxXFAMDep { + int xfam_bit; + FeatureMask feat_mask; +} TdxXFAMDep; + +/* + * Note, only the CPUID bits whose virtualization type are "XFAM & Native" are + * defiend here. + * + * For those whose virtualization type are "XFAM & Configured & Native", they + * are reported as configurable bits. And they are not supported if not in the + * configureable bits list from KVM even if the corresponding XFAM bit is + * supported. + */ +TdxXFAMDep tdx_xfam_deps[] = { + { XSTATE_YMM_BIT, { FEAT_1_ECX, CPUID_EXT_FMA }}, + { XSTATE_YMM_BIT, { FEAT_7_0_EBX, CPUID_7_0_EBX_AVX2 }}, + { XSTATE_OPMASK_BIT, { FEAT_7_0_ECX, CPUID_7_0_ECX_AVX512_VBMI}}, + { XSTATE_OPMASK_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AVX512_FP16}}, + { XSTATE_PT_BIT, { FEAT_7_0_EBX, CPUID_7_0_EBX_INTEL_PT}}, + { XSTATE_PKRU_BIT, { FEAT_7_0_ECX, CPUID_7_0_ECX_PKU}}, + { XSTATE_XTILE_CFG_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AMX_BF16 }}, + { XSTATE_XTILE_CFG_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AMX_TILE }}, + { XSTATE_XTILE_CFG_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AMX_INT8 }}, +}; + +static struct kvm_cpuid_entry2 *find_in_supported_entry(uint32_t function, + uint32_t index) +{ + struct kvm_cpuid_entry2 *e; + + e = cpuid_find_entry(tdx_supported_cpuid, function, index); + if (!e) { + if (tdx_supported_cpuid->nent >= KVM_MAX_CPUID_ENTRIES) { + error_report("tdx_supported_cpuid requries more space than %d entries", + KVM_MAX_CPUID_ENTRIES); + exit(1); + } + e = &tdx_supported_cpuid->entries[tdx_supported_cpuid->nent++]; + e->function = function; + e->index = index; + } + + return e; +} + +static void tdx_add_supported_cpuid_by_fixed1_bits(void) +{ + struct kvm_cpuid_entry2 *e, *e1; + int i; + + for (i = 0; i < tdx_fixed1_bits.cpuid.nent; i++) { + e = &tdx_fixed1_bits.entries[i]; + + e1 = find_in_supported_entry(e->function, e->index); + e1->eax |= e->eax; + e1->ebx |= e->ebx; + e1->ecx |= e->ecx; + e1->edx |= e->edx; + } +} + +static void tdx_add_supported_cpuid_by_attrs(void) +{ + struct kvm_cpuid_entry2 *e; + TdxAttrsMap *map; + int i; + + for (i = 0; i < ARRAY_SIZE(tdx_attrs_maps); i++) { + map = &tdx_attrs_maps[i]; + if (!((1ULL << map->attr_index) & tdx_caps->supported_attrs)) { + continue; + } + + e = find_in_supported_entry(map->cpuid_leaf, map->cpuid_subleaf); + + switch(map->cpuid_reg) { + case R_EAX: + e->eax |= map->feat_mask; + break; + case R_EBX: + e->ebx |= map->feat_mask; + break; + case R_ECX: + e->ecx |= map->feat_mask; + break; + case R_EDX: + e->edx |= map->feat_mask; + break; + } + } +} + +static void tdx_add_supported_cpuid_by_xfam(void) +{ + struct kvm_cpuid_entry2 *e; + int i; + + const TdxXFAMDep *xfam_dep; + const FeatureWordInfo *f; + for (i = 0; i < ARRAY_SIZE(tdx_xfam_deps); i++) { + xfam_dep = &tdx_xfam_deps[i]; + if (!((1ULL << xfam_dep->xfam_bit) & tdx_caps->supported_xfam)) { + continue; + } + + f = &feature_word_info[xfam_dep->feat_mask.index]; + if (f->type != CPUID_FEATURE_WORD) { + continue; + } + + e = find_in_supported_entry(f->cpuid.eax, f->cpuid.ecx); + switch(f->cpuid.reg) { + case R_EAX: + e->eax |= xfam_dep->feat_mask.mask; + break; + case R_EBX: + e->ebx |= xfam_dep->feat_mask.mask; + break; + case R_ECX: + e->ecx |= xfam_dep->feat_mask.mask; + break; + case R_EDX: + e->edx |= xfam_dep->feat_mask.mask; + break; + } + } + + e = find_in_supported_entry(0xd, 0); + e->eax |= (tdx_caps->supported_xfam & CPUID_XSTATE_XCR0_MASK); + e->edx |= (tdx_caps->supported_xfam & CPUID_XSTATE_XCR0_MASK) >> 32; + + e = find_in_supported_entry(0xd, 1); + /* + * Mark XFD always support for TDX, it will be cleared finally in + * tdx_adjust_cpuid_features() if XFD is unavailable on the hardware + * because in this case the original data has it as 0. + */ + e->eax |= CPUID_XSAVE_XFD; + e->ecx |= (tdx_caps->supported_xfam & CPUID_XSTATE_XSS_MASK); + e->edx |= (tdx_caps->supported_xfam & CPUID_XSTATE_XSS_MASK) >> 32; +} + +static void tdx_add_supported_kvm_features(void) +{ + struct kvm_cpuid_entry2 *e; + + e = find_in_supported_entry(0x40000001, 0); + e->eax = TDX_SUPPORTED_KVM_FEATURES; +} + +static void tdx_setup_supported_cpuid(void) +{ + if (tdx_supported_cpuid) { + return; + } + + tdx_supported_cpuid = g_malloc0(sizeof(*tdx_supported_cpuid) + + KVM_MAX_CPUID_ENTRIES * sizeof(struct kvm_cpuid_entry2)); + + memcpy(tdx_supported_cpuid->entries, tdx_caps->cpuid.entries, + tdx_caps->cpuid.nent * sizeof(struct kvm_cpuid_entry2)); + tdx_supported_cpuid->nent = tdx_caps->cpuid.nent; + + tdx_add_supported_cpuid_by_fixed1_bits(); + tdx_add_supported_cpuid_by_attrs(); + tdx_add_supported_cpuid_by_xfam(); + + tdx_add_supported_kvm_features(); +} + +static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + X86MachineState *x86ms = X86_MACHINE(ms); + TdxGuest *tdx = TDX_GUEST(cgs); + int r = 0; + + kvm_mark_guest_state_protected(); + + if (x86ms->smm == ON_OFF_AUTO_AUTO) { + x86ms->smm = ON_OFF_AUTO_OFF; + } else if (x86ms->smm == ON_OFF_AUTO_ON) { + error_setg(errp, "TDX VM doesn't support SMM"); + return -EINVAL; + } + + if (x86ms->pic == ON_OFF_AUTO_AUTO) { + x86ms->pic = ON_OFF_AUTO_OFF; + } else if (x86ms->pic == ON_OFF_AUTO_ON) { + error_setg(errp, "TDX VM doesn't support PIC"); + return -EINVAL; + } + + if (kvm_state->kernel_irqchip_split == ON_OFF_AUTO_AUTO) { + kvm_state->kernel_irqchip_split = ON_OFF_AUTO_ON; + } else if (kvm_state->kernel_irqchip_split != ON_OFF_AUTO_ON) { + error_setg(errp, "TDX VM requires kernel_irqchip to be split"); + return -EINVAL; + } + + if (!tdx_caps) { + r = get_tdx_capabilities(errp); + if (r) { + return r; + } + } + + tdx_setup_supported_cpuid(); + + /* TDX relies on KVM_HC_MAP_GPA_RANGE to handle TDG.VP.VMCALL */ + if (!kvm_enable_hypercall(BIT_ULL(KVM_HC_MAP_GPA_RANGE))) { + return -EOPNOTSUPP; + } + + /* + * Set kvm_readonly_mem_allowed to false, because TDX only supports readonly + * memory for shared memory but not for private memory. Besides, whether a + * memslot is private or shared is not determined by QEMU. + * + * Thus, just mark readonly memory not supported for simplicity. + */ + kvm_readonly_mem_allowed = false; + + qemu_add_machine_init_done_notifier(&tdx_machine_done_notify); + + tdx_guest = tdx; + return 0; +} + +static int tdx_kvm_type(X86ConfidentialGuest *cg) +{ + /* Do the object check */ + TDX_GUEST(cg); + + return KVM_X86_TDX_VM; +} + +static void tdx_cpu_instance_init(X86ConfidentialGuest *cg, CPUState *cpu) +{ + X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); + X86CPU *x86cpu = X86_CPU(cpu); + + if (xcc->model) { + error_report("Named cpu model is not supported for TDX yet!"); + exit(1); + } + + object_property_set_bool(OBJECT(cpu), "pmu", false, &error_abort); + + /* invtsc is fixed1 for TD guest */ + object_property_set_bool(OBJECT(cpu), "invtsc", true, &error_abort); + + x86cpu->force_cpuid_0x1f = true; +} + +static uint32_t tdx_adjust_cpuid_features(X86ConfidentialGuest *cg, + uint32_t feature, uint32_t index, + int reg, uint32_t value) +{ + struct kvm_cpuid_entry2 *e; + + e = cpuid_find_entry(&tdx_fixed1_bits.cpuid, feature, index); + if (e) { + value |= cpuid_entry_get_reg(e, reg); + } + + if (is_feature_word_cpuid(feature, index, reg)) { + e = cpuid_find_entry(tdx_supported_cpuid, feature, index); + if (e) { + value &= cpuid_entry_get_reg(e, reg); + } + } + + return value; +} + +static struct kvm_cpuid2 *tdx_fetch_cpuid(CPUState *cpu, int *ret) +{ + struct kvm_cpuid2 *fetch_cpuid; + int size = KVM_MAX_CPUID_ENTRIES; + Error *local_err = NULL; + int r; + + do { + error_free(local_err); + local_err = NULL; + + fetch_cpuid = g_malloc0(sizeof(*fetch_cpuid) + + sizeof(struct kvm_cpuid_entry2) * size); + fetch_cpuid->nent = size; + r = tdx_vcpu_ioctl(cpu, KVM_TDX_GET_CPUID, 0, fetch_cpuid, &local_err); + if (r == -E2BIG) { + g_free(fetch_cpuid); + size = fetch_cpuid->nent; + } + } while (r == -E2BIG); + + if (r < 0) { + error_report_err(local_err); + *ret = r; + return NULL; + } + + return fetch_cpuid; +} + +static int tdx_check_features(X86ConfidentialGuest *cg, CPUState *cs) +{ + uint64_t actual, requested, unavailable, forced_on; + g_autofree struct kvm_cpuid2 *fetch_cpuid; + const char *forced_on_prefix = NULL; + const char *unav_prefix = NULL; + struct kvm_cpuid_entry2 *entry; + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + FeatureWordInfo *wi; + FeatureWord w; + bool mismatch = false; + int r; + + fetch_cpuid = tdx_fetch_cpuid(cs, &r); + if (!fetch_cpuid) { + return r; + } + + if (cpu->check_cpuid || cpu->enforce_cpuid) { + unav_prefix = "TDX doesn't support requested feature"; + forced_on_prefix = "TDX forcibly sets the feature"; + } + + for (w = 0; w < FEATURE_WORDS; w++) { + wi = &feature_word_info[w]; + actual = 0; + + switch (wi->type) { + case CPUID_FEATURE_WORD: + entry = cpuid_find_entry(fetch_cpuid, wi->cpuid.eax, wi->cpuid.ecx); + if (!entry) { + /* + * If KVM doesn't report it means it's totally configurable + * by QEMU + */ + continue; + } + + actual = cpuid_entry_get_reg(entry, wi->cpuid.reg); + break; + case MSR_FEATURE_WORD: + /* + * TODO: + * validate MSR features when KVM has interface report them. + */ + continue; + } + + /* Fixup for special cases */ + switch (w) { + case FEAT_8000_0001_EDX: + /* + * Intel enumerates SYSCALL bit as 1 only when processor in 64-bit + * mode and before vcpu running it's not in 64-bit mode. + */ + actual |= CPUID_EXT2_SYSCALL; + break; + default: + break; + } + + requested = env->features[w]; + unavailable = requested & ~actual; + mark_unavailable_features(cpu, w, unavailable, unav_prefix); + if (unavailable) { + mismatch = true; + } + + forced_on = actual & ~requested; + mark_forced_on_features(cpu, w, forced_on, forced_on_prefix); + if (forced_on) { + mismatch = true; + } + } + + if (cpu->enforce_cpuid && mismatch) { + return -EINVAL; + } + + if (cpu->phys_bits != host_cpu_phys_bits()) { + error_report("TDX requires guest CPU physical bits (%u) " + "to match host CPU physical bits (%u)", + cpu->phys_bits, host_cpu_phys_bits()); + return -EINVAL; + } + + return 0; +} + +static int tdx_validate_attributes(TdxGuest *tdx, Error **errp) +{ + if ((tdx->attributes & ~tdx_caps->supported_attrs)) { + error_setg(errp, "Invalid attributes 0x%"PRIx64" for TDX VM " + "(KVM supported: 0x%"PRIx64")", tdx->attributes, + (uint64_t)tdx_caps->supported_attrs); + return -1; + } + + if (tdx->attributes & ~TDX_SUPPORTED_TD_ATTRS) { + error_setg(errp, "Some QEMU unsupported TD attribute bits being " + "requested: 0x%"PRIx64" (QEMU supported: 0x%"PRIx64")", + tdx->attributes, (uint64_t)TDX_SUPPORTED_TD_ATTRS); + return -1; + } + + return 0; +} + +static int setup_td_guest_attributes(X86CPU *x86cpu, Error **errp) +{ + CPUX86State *env = &x86cpu->env; + + tdx_guest->attributes |= (env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_PKS) ? + TDX_TD_ATTRIBUTES_PKS : 0; + tdx_guest->attributes |= x86cpu->enable_pmu ? TDX_TD_ATTRIBUTES_PERFMON : 0; + + return tdx_validate_attributes(tdx_guest, errp); +} + +static int setup_td_xfam(X86CPU *x86cpu, Error **errp) +{ + CPUX86State *env = &x86cpu->env; + uint64_t xfam; + + xfam = env->features[FEAT_XSAVE_XCR0_LO] | + env->features[FEAT_XSAVE_XCR0_HI] | + env->features[FEAT_XSAVE_XSS_LO] | + env->features[FEAT_XSAVE_XSS_HI]; + + if (xfam & ~tdx_caps->supported_xfam) { + error_setg(errp, "Invalid XFAM 0x%"PRIx64" for TDX VM (supported: 0x%"PRIx64"))", + xfam, (uint64_t)tdx_caps->supported_xfam); + return -1; + } + + tdx_guest->xfam = xfam; + return 0; +} + +static void tdx_filter_cpuid(struct kvm_cpuid2 *cpuids) +{ + int i, dest_cnt = 0; + struct kvm_cpuid_entry2 *src, *dest, *conf; + + for (i = 0; i < cpuids->nent; i++) { + src = cpuids->entries + i; + conf = cpuid_find_entry(&tdx_caps->cpuid, src->function, src->index); + if (!conf) { + continue; + } + dest = cpuids->entries + dest_cnt; + + dest->function = src->function; + dest->index = src->index; + dest->flags = src->flags; + dest->eax = src->eax & conf->eax; + dest->ebx = src->ebx & conf->ebx; + dest->ecx = src->ecx & conf->ecx; + dest->edx = src->edx & conf->edx; + + dest_cnt++; + } + cpuids->nent = dest_cnt++; +} + +int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + g_autofree struct kvm_tdx_init_vm *init_vm = NULL; + Error *local_err = NULL; + size_t data_len; + int retry = 10000; + int r = 0; + + QEMU_LOCK_GUARD(&tdx_guest->lock); + if (tdx_guest->initialized) { + return r; + } + + init_vm = g_malloc0(sizeof(struct kvm_tdx_init_vm) + + sizeof(struct kvm_cpuid_entry2) * KVM_MAX_CPUID_ENTRIES); + + if (!kvm_check_extension(kvm_state, KVM_CAP_X86_APIC_BUS_CYCLES_NS)) { + error_setg(errp, "KVM doesn't support KVM_CAP_X86_APIC_BUS_CYCLES_NS"); + return -EOPNOTSUPP; + } + + r = kvm_vm_enable_cap(kvm_state, KVM_CAP_X86_APIC_BUS_CYCLES_NS, + 0, TDX_APIC_BUS_CYCLES_NS); + if (r < 0) { + error_setg_errno(errp, -r, + "Unable to set core crystal clock frequency to 25MHz"); + return r; + } + + if (env->tsc_khz && (env->tsc_khz < TDX_MIN_TSC_FREQUENCY_KHZ || + env->tsc_khz > TDX_MAX_TSC_FREQUENCY_KHZ)) { + error_setg(errp, "Invalid TSC %"PRId64" KHz, must specify cpu_frequency " + "between [%d, %d] kHz", env->tsc_khz, + TDX_MIN_TSC_FREQUENCY_KHZ, TDX_MAX_TSC_FREQUENCY_KHZ); + return -EINVAL; + } + + if (env->tsc_khz % (25 * 1000)) { + error_setg(errp, "Invalid TSC %"PRId64" KHz, it must be multiple of 25MHz", + env->tsc_khz); + return -EINVAL; + } + + /* it's safe even env->tsc_khz is 0. KVM uses host's tsc_khz in this case */ + r = kvm_vm_ioctl(kvm_state, KVM_SET_TSC_KHZ, env->tsc_khz); + if (r < 0) { + error_setg_errno(errp, -r, "Unable to set TSC frequency to %"PRId64" kHz", + env->tsc_khz); + return r; + } + + if (tdx_guest->mrconfigid) { + g_autofree uint8_t *data = qbase64_decode(tdx_guest->mrconfigid, + strlen(tdx_guest->mrconfigid), &data_len, errp); + if (!data) { + return -1; + } + if (data_len != QCRYPTO_HASH_DIGEST_LEN_SHA384) { + error_setg(errp, "TDX 'mrconfigid' sha384 digest was %ld bytes, " + "expected %d bytes", data_len, + QCRYPTO_HASH_DIGEST_LEN_SHA384); + return -1; + } + memcpy(init_vm->mrconfigid, data, data_len); + } + + if (tdx_guest->mrowner) { + g_autofree uint8_t *data = qbase64_decode(tdx_guest->mrowner, + strlen(tdx_guest->mrowner), &data_len, errp); + if (!data) { + return -1; + } + if (data_len != QCRYPTO_HASH_DIGEST_LEN_SHA384) { + error_setg(errp, "TDX 'mrowner' sha384 digest was %ld bytes, " + "expected %d bytes", data_len, + QCRYPTO_HASH_DIGEST_LEN_SHA384); + return -1; + } + memcpy(init_vm->mrowner, data, data_len); + } + + if (tdx_guest->mrownerconfig) { + g_autofree uint8_t *data = qbase64_decode(tdx_guest->mrownerconfig, + strlen(tdx_guest->mrownerconfig), &data_len, errp); + if (!data) { + return -1; + } + if (data_len != QCRYPTO_HASH_DIGEST_LEN_SHA384) { + error_setg(errp, "TDX 'mrownerconfig' sha384 digest was %ld bytes, " + "expected %d bytes", data_len, + QCRYPTO_HASH_DIGEST_LEN_SHA384); + return -1; + } + memcpy(init_vm->mrownerconfig, data, data_len); + } + + r = setup_td_guest_attributes(x86cpu, errp); + if (r) { + return r; + } + + r = setup_td_xfam(x86cpu, errp); + if (r) { + return r; + } + + init_vm->cpuid.nent = kvm_x86_build_cpuid(env, init_vm->cpuid.entries, 0); + tdx_filter_cpuid(&init_vm->cpuid); + + init_vm->attributes = tdx_guest->attributes; + init_vm->xfam = tdx_guest->xfam; + + /* + * KVM_TDX_INIT_VM gets -EAGAIN when KVM side SEAMCALL(TDH_MNG_CREATE) + * gets TDX_RND_NO_ENTROPY due to Random number generation (e.g., RDRAND or + * RDSEED) is busy. + * + * Retry for the case. + */ + do { + error_free(local_err); + local_err = NULL; + r = tdx_vm_ioctl(KVM_TDX_INIT_VM, 0, init_vm, &local_err); + } while (r == -EAGAIN && --retry); + + if (r < 0) { + if (!retry) { + error_append_hint(&local_err, "Hardware RNG (Random Number " + "Generator) is busy occupied by someone (via RDRAND/RDSEED) " + "maliciously, which leads to KVM_TDX_INIT_VM keeping failure " + "due to lack of entropy.\n"); + } + error_propagate(errp, local_err); + return r; + } + + tdx_guest->initialized = true; + + return 0; +} + +int tdx_parse_tdvf(void *flash_ptr, int size) +{ + return tdvf_parse_metadata(&tdx_guest->tdvf, flash_ptr, size); +} + +static void tdx_get_quote_completion(TdxGenerateQuoteTask *task) +{ + TdxGuest *tdx = task->opaque; + int ret; + + /* Maintain the number of in-flight requests. */ + qemu_mutex_lock(&tdx->lock); + tdx->num--; + qemu_mutex_unlock(&tdx->lock); + + if (task->status_code == TDX_VP_GET_QUOTE_SUCCESS) { + ret = address_space_write(&address_space_memory, task->payload_gpa, + MEMTXATTRS_UNSPECIFIED, task->receive_buf, + task->receive_buf_received); + if (ret != MEMTX_OK) { + error_report("TDX: get-quote: failed to write quote data."); + } else { + task->hdr.out_len = cpu_to_le64(task->receive_buf_received); + } + } + task->hdr.error_code = cpu_to_le64(task->status_code); + + /* Publish the response contents before marking this request completed. */ + smp_wmb(); + ret = address_space_write(&address_space_memory, task->buf_gpa, + MEMTXATTRS_UNSPECIFIED, &task->hdr, + TDX_GET_QUOTE_HDR_SIZE); + if (ret != MEMTX_OK) { + error_report("TDX: get-quote: failed to update GetQuote header."); + } + + g_free(task->send_data); + g_free(task->receive_buf); + g_free(task); + object_unref(tdx); +} + +void tdx_handle_get_quote(X86CPU *cpu, struct kvm_run *run) +{ + TdxGenerateQuoteTask *task; + struct tdx_get_quote_header hdr; + hwaddr buf_gpa = run->tdx.get_quote.gpa; + uint64_t buf_len = run->tdx.get_quote.size; + + QEMU_BUILD_BUG_ON(sizeof(struct tdx_get_quote_header) != TDX_GET_QUOTE_HDR_SIZE); + + run->tdx.get_quote.ret = TDG_VP_VMCALL_INVALID_OPERAND; + + if (buf_len == 0) { + return; + } + + if (!QEMU_IS_ALIGNED(buf_gpa, 4096) || !QEMU_IS_ALIGNED(buf_len, 4096)) { + run->tdx.get_quote.ret = TDG_VP_VMCALL_ALIGN_ERROR; + return; + } + + if (address_space_read(&address_space_memory, buf_gpa, MEMTXATTRS_UNSPECIFIED, + &hdr, TDX_GET_QUOTE_HDR_SIZE) != MEMTX_OK) { + error_report("TDX: get-quote: failed to read GetQuote header."); + return; + } + + if (le64_to_cpu(hdr.structure_version) != TDX_GET_QUOTE_STRUCTURE_VERSION) { + return; + } + + /* Only safe-guard check to avoid too large buffer size. */ + if (buf_len > TDX_GET_QUOTE_MAX_BUF_LEN || + le32_to_cpu(hdr.in_len) > buf_len - TDX_GET_QUOTE_HDR_SIZE) { + return; + } + + if (!tdx_guest->qg_sock_addr) { + hdr.error_code = cpu_to_le64(TDX_VP_GET_QUOTE_QGS_UNAVAILABLE); + if (address_space_write(&address_space_memory, buf_gpa, + MEMTXATTRS_UNSPECIFIED, + &hdr, TDX_GET_QUOTE_HDR_SIZE) != MEMTX_OK) { + error_report("TDX: failed to update GetQuote header."); + return; + } + run->tdx.get_quote.ret = TDG_VP_VMCALL_SUCCESS; + return; + } + + qemu_mutex_lock(&tdx_guest->lock); + if (tdx_guest->num >= TDX_MAX_GET_QUOTE_REQUEST) { + qemu_mutex_unlock(&tdx_guest->lock); + run->tdx.get_quote.ret = TDG_VP_VMCALL_RETRY; + return; + } + tdx_guest->num++; + qemu_mutex_unlock(&tdx_guest->lock); + + task = g_new(TdxGenerateQuoteTask, 1); + task->buf_gpa = buf_gpa; + task->payload_gpa = buf_gpa + TDX_GET_QUOTE_HDR_SIZE; + task->payload_len = buf_len - TDX_GET_QUOTE_HDR_SIZE; + task->hdr = hdr; + task->completion = tdx_get_quote_completion; + + task->send_data_size = le32_to_cpu(hdr.in_len); + task->send_data = g_malloc(task->send_data_size); + task->send_data_sent = 0; + + if (address_space_read(&address_space_memory, task->payload_gpa, + MEMTXATTRS_UNSPECIFIED, task->send_data, + task->send_data_size) != MEMTX_OK) { + goto out_free; + } + + /* Mark the buffer in-flight. */ + hdr.error_code = cpu_to_le64(TDX_VP_GET_QUOTE_IN_FLIGHT); + if (address_space_write(&address_space_memory, buf_gpa, + MEMTXATTRS_UNSPECIFIED, + &hdr, TDX_GET_QUOTE_HDR_SIZE) != MEMTX_OK) { + goto out_free; + } + + task->receive_buf = g_malloc0(task->payload_len); + task->receive_buf_received = 0; + task->opaque = tdx_guest; + + object_ref(tdx_guest); + tdx_generate_quote(task, tdx_guest->qg_sock_addr); + run->tdx.get_quote.ret = TDG_VP_VMCALL_SUCCESS; + return; + +out_free: + g_free(task->send_data); + g_free(task); +} + +void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run) +{ + if (run->tdx.get_tdvmcall_info.leaf != 1) { + return; + } + + run->tdx.get_tdvmcall_info.r11 = TDG_VP_VMCALL_SUBFUNC_GET_QUOTE; + run->tdx.get_tdvmcall_info.r12 = 0; + run->tdx.get_tdvmcall_info.r13 = 0; + run->tdx.get_tdvmcall_info.r14 = 0; +} + +static void tdx_panicked_on_fatal_error(X86CPU *cpu, uint64_t error_code, + char *message, uint64_t gpa) +{ + GuestPanicInformation *panic_info; + + panic_info = g_new0(GuestPanicInformation, 1); + panic_info->type = GUEST_PANIC_INFORMATION_TYPE_TDX; + panic_info->u.tdx.error_code = (uint32_t) error_code; + panic_info->u.tdx.message = message; + panic_info->u.tdx.gpa = gpa; + + qemu_system_guest_panicked(panic_info); +} + +/* + * Only 8 registers can contain valid ASCII byte stream to form the fatal + * message, and their sequence is: R14, R15, RBX, RDI, RSI, R8, R9, RDX + */ +#define TDX_FATAL_MESSAGE_MAX 64 + +#define TDX_REPORT_FATAL_ERROR_GPA_VALID BIT_ULL(63) + +int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run) +{ + uint64_t error_code = run->system_event.data[R_R12]; + uint64_t reg_mask = run->system_event.data[R_ECX]; + char *message = NULL; + uint64_t *tmp; + uint64_t gpa = -1ull; + + if (error_code & 0xffff) { + error_report("TDX: REPORT_FATAL_ERROR: invalid error code: 0x%"PRIx64, + error_code); + return -1; + } + + if (reg_mask) { + message = g_malloc0(TDX_FATAL_MESSAGE_MAX + 1); + tmp = (uint64_t *)message; + +#define COPY_REG(REG) \ + do { \ + if (reg_mask & BIT_ULL(REG)) { \ + *(tmp++) = run->system_event.data[REG]; \ + } \ + } while (0) + + COPY_REG(R_R14); + COPY_REG(R_R15); + COPY_REG(R_EBX); + COPY_REG(R_EDI); + COPY_REG(R_ESI); + COPY_REG(R_R8); + COPY_REG(R_R9); + COPY_REG(R_EDX); + *((char *)tmp) = '\0'; + } +#undef COPY_REG + + if (error_code & TDX_REPORT_FATAL_ERROR_GPA_VALID) { + gpa = run->system_event.data[R_R13]; + } + + tdx_panicked_on_fatal_error(cpu, error_code, message, gpa); + + return -1; +} + +static bool tdx_guest_get_sept_ve_disable(Object *obj, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + return !!(tdx->attributes & TDX_TD_ATTRIBUTES_SEPT_VE_DISABLE); +} + +static void tdx_guest_set_sept_ve_disable(Object *obj, bool value, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + if (value) { + tdx->attributes |= TDX_TD_ATTRIBUTES_SEPT_VE_DISABLE; + } else { + tdx->attributes &= ~TDX_TD_ATTRIBUTES_SEPT_VE_DISABLE; + } +} + +static char *tdx_guest_get_mrconfigid(Object *obj, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + return g_strdup(tdx->mrconfigid); +} + +static void tdx_guest_set_mrconfigid(Object *obj, const char *value, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + g_free(tdx->mrconfigid); + tdx->mrconfigid = g_strdup(value); +} + +static char *tdx_guest_get_mrowner(Object *obj, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + return g_strdup(tdx->mrowner); +} + +static void tdx_guest_set_mrowner(Object *obj, const char *value, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + g_free(tdx->mrowner); + tdx->mrowner = g_strdup(value); +} + +static char *tdx_guest_get_mrownerconfig(Object *obj, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + return g_strdup(tdx->mrownerconfig); +} + +static void tdx_guest_set_mrownerconfig(Object *obj, const char *value, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + g_free(tdx->mrownerconfig); + tdx->mrownerconfig = g_strdup(value); +} + +static void tdx_guest_get_qgs(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + if (!tdx->qg_sock_addr) { + error_setg(errp, "quote-generation-socket is not set"); + return; + } + visit_type_SocketAddress(v, name, &tdx->qg_sock_addr, errp); +} + +static void tdx_guest_set_qgs(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + SocketAddress *sock = NULL; + + if (!visit_type_SocketAddress(v, name, &sock, errp)) { + return; + } + + if (tdx->qg_sock_addr) { + qapi_free_SocketAddress(tdx->qg_sock_addr); + } + + tdx->qg_sock_addr = sock; +} + +/* tdx guest */ +OBJECT_DEFINE_TYPE_WITH_INTERFACES(TdxGuest, + tdx_guest, + TDX_GUEST, + X86_CONFIDENTIAL_GUEST, + { TYPE_USER_CREATABLE }, + { NULL }) + +static void tdx_guest_init(Object *obj) +{ + ConfidentialGuestSupport *cgs = CONFIDENTIAL_GUEST_SUPPORT(obj); + TdxGuest *tdx = TDX_GUEST(obj); + + qemu_mutex_init(&tdx->lock); + + cgs->require_guest_memfd = true; + tdx->attributes = TDX_TD_ATTRIBUTES_SEPT_VE_DISABLE; + + object_property_add_uint64_ptr(obj, "attributes", &tdx->attributes, + OBJ_PROP_FLAG_READWRITE); + object_property_add_bool(obj, "sept-ve-disable", + tdx_guest_get_sept_ve_disable, + tdx_guest_set_sept_ve_disable); + object_property_add_str(obj, "mrconfigid", + tdx_guest_get_mrconfigid, + tdx_guest_set_mrconfigid); + object_property_add_str(obj, "mrowner", + tdx_guest_get_mrowner, tdx_guest_set_mrowner); + object_property_add_str(obj, "mrownerconfig", + tdx_guest_get_mrownerconfig, + tdx_guest_set_mrownerconfig); + + object_property_add(obj, "quote-generation-socket", "SocketAddress", + tdx_guest_get_qgs, + tdx_guest_set_qgs, + NULL, NULL); + + qemu_mutex_init(&tdx->lock); +} + +static void tdx_guest_finalize(Object *obj) +{ +} + +static void tdx_guest_class_init(ObjectClass *oc, const void *data) +{ + ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); + X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); + + klass->kvm_init = tdx_kvm_init; + x86_klass->kvm_type = tdx_kvm_type; + x86_klass->cpu_instance_init = tdx_cpu_instance_init; + x86_klass->adjust_cpuid_features = tdx_adjust_cpuid_features; + x86_klass->check_features = tdx_check_features; +} diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h new file mode 100644 index 00000000000..35a09c19c52 --- /dev/null +++ b/target/i386/kvm/tdx.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef QEMU_I386_TDX_H +#define QEMU_I386_TDX_H + +#ifndef CONFIG_USER_ONLY +#include CONFIG_DEVICES /* CONFIG_TDX */ +#endif + +#include "confidential-guest.h" +#include "cpu.h" +#include "hw/i386/tdvf.h" + +#include "tdx-quote-generator.h" + +#define TYPE_TDX_GUEST "tdx-guest" +#define TDX_GUEST(obj) OBJECT_CHECK(TdxGuest, (obj), TYPE_TDX_GUEST) + +typedef struct TdxGuestClass { + X86ConfidentialGuestClass parent_class; +} TdxGuestClass; + +/* TDX requires bus frequency 25MHz */ +#define TDX_APIC_BUS_CYCLES_NS 40 + +#define TDVMCALL_GET_TD_VM_CALL_INFO 0x10000 +#define TDVMCALL_GET_QUOTE 0x10002 + +#define TDG_VP_VMCALL_SUCCESS 0x0000000000000000ULL +#define TDG_VP_VMCALL_RETRY 0x0000000000000001ULL +#define TDG_VP_VMCALL_INVALID_OPERAND 0x8000000000000000ULL +#define TDG_VP_VMCALL_GPA_INUSE 0x8000000000000001ULL +#define TDG_VP_VMCALL_ALIGN_ERROR 0x8000000000000002ULL + +#define TDG_VP_VMCALL_SUBFUNC_GET_QUOTE 0x0000000000000001ULL + +enum TdxRamType { + TDX_RAM_UNACCEPTED, + TDX_RAM_ADDED, +}; + +typedef struct TdxRamEntry { + uint64_t address; + uint64_t length; + enum TdxRamType type; +} TdxRamEntry; + +typedef struct TdxGuest { + X86ConfidentialGuest parent_obj; + + QemuMutex lock; + + bool initialized; + uint64_t attributes; /* TD attributes */ + uint64_t xfam; + char *mrconfigid; /* base64 encoded sha384 digest */ + char *mrowner; /* base64 encoded sha384 digest */ + char *mrownerconfig; /* base64 encoded sha384 digest */ + + MemoryRegion *tdvf_mr; + TdxFirmware tdvf; + + uint32_t nr_ram_entries; + TdxRamEntry *ram_entries; + + /* GetQuote */ + SocketAddress *qg_sock_addr; + int num; +} TdxGuest; + +#ifdef CONFIG_TDX +bool is_tdx_vm(void); +#else +#define is_tdx_vm() 0 +#endif /* CONFIG_TDX */ + +int tdx_pre_create_vcpu(CPUState *cpu, Error **errp); +void tdx_set_tdvf_region(MemoryRegion *tdvf_mr); +int tdx_parse_tdvf(void *flash_ptr, int size); +int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run); +void tdx_handle_get_quote(X86CPU *cpu, struct kvm_run *run); +void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run); + +#endif /* QEMU_I386_TDX_H */ diff --git a/target/i386/kvm/trace-events b/target/i386/kvm/trace-events index b365a8e8e28..74a6234ff7f 100644 --- a/target/i386/kvm/trace-events +++ b/target/i386/kvm/trace-events @@ -5,6 +5,7 @@ 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 diff --git a/target/i386/kvm/vmsr_energy.c b/target/i386/kvm/vmsr_energy.c new file mode 100644 index 00000000000..d6aad5246b6 --- /dev/null +++ b/target/i386/kvm/vmsr_energy.c @@ -0,0 +1,344 @@ +/* + * 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) +{ + char vendor[CPUID_VENDOR_SZ + 1]; + + host_cpu_vendor_fms(vendor, NULL, NULL, NULL); + + 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); +} + +/* 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 00000000000..16cc1f4814f --- /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-emu.c b/target/i386/kvm/xen-emu.c index a8146115f0a..284c5ef6f68 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -13,14 +13,15 @@ #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/error-report.h" +#include "exec/target_page.h" #include "hw/xen/xen.h" -#include "sysemu/kvm_int.h" -#include "sysemu/kvm_xen.h" +#include "system/kvm_int.h" +#include "system/kvm_xen.h" #include "kvm/kvm_i386.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "xen-emu.h" #include "trace.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "hw/pci/msi.h" #include "hw/i386/apic-msidef.h" @@ -28,6 +29,7 @@ #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" @@ -175,14 +177,10 @@ int kvm_xen_init(KVMState *s, uint32_t hypercall_msr) s->xen_caps = xen_caps; /* Tell fw_cfg to notify the BIOS to reserve the range. */ - ret = e820_add_entry(XEN_SPECIAL_AREA_ADDR, XEN_SPECIAL_AREA_SIZE, - E820_RESERVED); - if (ret < 0) { - fprintf(stderr, "e820_add_entry() table is full\n"); - return ret; - } + e820_add_entry(XEN_SPECIAL_AREA_ADDR, XEN_SPECIAL_AREA_SIZE, E820_RESERVED); - /* The page couldn't be overlaid until KVM was initialized */ + /* The pages couldn't be overlaid until KVM was initialized */ + xen_primary_console_reset(); xen_xenstore_reset(); return 0; @@ -267,7 +265,6 @@ static bool kvm_xen_hcall_xen_version(struct kvm_xen_exit *exit, X86CPU *cpu, fi.submap |= 1 << XENFEAT_writable_page_tables | 1 << XENFEAT_writable_descriptor_tables | 1 << XENFEAT_auto_translated_physmap | - 1 << XENFEAT_supervisor_mode_kernel | 1 << XENFEAT_hvm_callback_vector | 1 << XENFEAT_hvm_safe_pvclock | 1 << XENFEAT_hvm_pirqs; @@ -307,7 +304,7 @@ static int kvm_xen_set_vcpu_callback_vector(CPUState *cs) trace_kvm_xen_set_vcpu_callback(cs->cpu_index, vector); - return kvm_vcpu_ioctl(cs, KVM_XEN_HVM_SET_ATTR, &xva); + 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) @@ -402,7 +399,7 @@ void kvm_xen_maybe_deassert_callback(CPUState *cs) /* If the evtchn_upcall_pending flag is cleared, turn the GSI off. */ if (!vi->evtchn_upcall_pending) { - qemu_mutex_lock_iothread(); + 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 @@ -412,7 +409,7 @@ void kvm_xen_maybe_deassert_callback(CPUState *cs) X86_CPU(cs)->env.xen_callback_asserted = false; xen_evtchn_set_callback_level(0); } - qemu_mutex_unlock_iothread(); + bql_unlock(); } } @@ -425,6 +422,13 @@ void kvm_xen_set_callback_asserted(void) } } +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); @@ -441,7 +445,8 @@ void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type) * deliver it as an MSI. */ MSIMessage msg = { - .address = APIC_DEFAULT_ADDRESS | X86_CPU(cs)->apic_id, + .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); @@ -572,7 +577,7 @@ static int xen_set_shared_info(uint64_t gfn) uint64_t gpa = gfn << TARGET_PAGE_BITS; int i, err; - QEMU_IOTHREAD_LOCK_GUARD(); + BQL_LOCK_GUARD(); /* * The xen_overlay device tells KVM about it too, since it had to @@ -764,9 +769,9 @@ static bool handle_set_param(struct kvm_xen_exit *exit, X86CPU *cpu, switch (hp.index) { case HVM_PARAM_CALLBACK_IRQ: - qemu_mutex_lock_iothread(); + bql_lock(); err = xen_evtchn_set_callback_param(hp.value); - qemu_mutex_unlock_iothread(); + bql_unlock(); xen_set_long_mode(exit->u.hcall.longmode); break; default: @@ -805,11 +810,23 @@ static bool handle_get_param(struct kvm_xen_exit *exit, X86CPU *cpu, 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 (kvm_copy_to_gva(cs, arg, &hp, sizeof(hp))) { + if (!err && kvm_copy_to_gva(cs, arg, &hp, sizeof(hp))) { err = -EFAULT; } out: @@ -850,8 +867,7 @@ static bool kvm_xen_hcall_hvm_op(struct kvm_xen_exit *exit, X86CPU *cpu, int ret = -ENOSYS; switch (cmd) { case HVMOP_set_evtchn_upcall_vector: - ret = kvm_xen_hcall_evtchn_upcall_vector(exit, cpu, - exit->u.hcall.params[0]); + ret = kvm_xen_hcall_evtchn_upcall_vector(exit, cpu, arg); break; case HVMOP_pagetable_dying: @@ -1033,7 +1049,7 @@ static int do_set_periodic_timer(CPUState *target, uint64_t period_ns) #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) wont overflow without an uptime of 200 years */ +/* 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, @@ -1071,17 +1087,13 @@ static int vcpuop_stop_periodic_timer(CPUState *target) * Must always be called with xen_timers_lock held. */ static int do_set_singleshot_timer(CPUState *cs, uint64_t timeout_abs, - bool future, bool linux_wa) + 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 (future && timeout_abs < now) { - return -ETIME; - } - if (linux_wa && unlikely((int64_t)timeout_abs < 0 || (delta > 0 && (uint32_t)(delta >> 50) != 0))) { /* @@ -1123,9 +1135,13 @@ static int vcpuop_set_singleshot_timer(CPUState *cs, uint64_t arg) } QEMU_LOCK_GUARD(&X86_CPU(cs)->env.xen_timers_lock); - return do_set_singleshot_timer(cs, sst.timeout_abs_ns, - !!(sst.flags & VCPU_SSHOTTMR_future), - false); + + /* + * 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) @@ -1150,7 +1166,7 @@ static bool kvm_xen_hcall_set_timer_op(struct kvm_xen_exit *exit, X86CPU *cpu, 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, false, true); + err = do_set_singleshot_timer(CPU(cpu), timeout, true); } exit->u.hcall.result = err; return true; @@ -1388,7 +1404,7 @@ int kvm_xen_soft_reset(void) CPUState *cpu; int err; - assert(qemu_mutex_iothread_locked()); + assert(bql_locked()); trace_kvm_xen_soft_reset(); @@ -1421,6 +1437,11 @@ int kvm_xen_soft_reset(void) return err; } + err = xen_primary_console_reset(); + if (err) { + return err; + } + err = xen_xenstore_reset(); if (err) { return err; @@ -1456,9 +1477,9 @@ static int schedop_shutdown(CPUState *cs, uint64_t arg) break; case SHUTDOWN_soft_reset: - qemu_mutex_lock_iothread(); + bql_lock(); ret = kvm_xen_soft_reset(); - qemu_mutex_unlock_iothread(); + bql_unlock(); break; default: @@ -1838,7 +1859,7 @@ int kvm_put_xen_state(CPUState *cs) 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, false); + false); if (ret < 0) { return ret; } diff --git a/target/i386/machine.c b/target/i386/machine.c index c7ac8084b22..dd2dac1d443 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -1,16 +1,16 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "hw/isa/isa.h" #include "migration/cpu.h" #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 "exec/watchpoint.h" +#include "system/kvm.h" +#include "system/kvm_xen.h" +#include "system/tcg.h" #include "qemu/error-report.h" @@ -18,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), @@ -43,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() @@ -59,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() @@ -74,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), @@ -92,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), @@ -114,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() @@ -128,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() @@ -142,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), @@ -201,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() @@ -210,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() } @@ -282,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 @@ -453,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), @@ -475,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() } @@ -486,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() } @@ -497,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() } @@ -508,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() } @@ -519,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() } @@ -538,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), @@ -559,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() } @@ -578,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() } @@ -597,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() } @@ -624,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() } @@ -635,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() } @@ -670,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), @@ -706,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), @@ -728,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() @@ -748,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() } @@ -767,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() } @@ -792,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() } @@ -815,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() } @@ -855,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), @@ -883,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), @@ -926,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), @@ -970,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 @@ -993,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() } @@ -1012,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() } @@ -1031,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() } @@ -1050,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() } @@ -1060,9 +1060,8 @@ static bool tsc_khz_needed(void *opaque) { X86CPU *cpu = opaque; CPUX86State *env = &cpu->env; - MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); - X86MachineClass *x86mc = X86_MACHINE_CLASS(mc); - return env->tsc_khz && x86mc->save_tsc_khz; + + return env->tsc_khz; } static const VMStateDescription vmstate_tsc_khz = { @@ -1070,7 +1069,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() } @@ -1090,7 +1089,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), @@ -1110,7 +1109,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), @@ -1131,13 +1130,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, @@ -1162,7 +1161,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, @@ -1232,13 +1231,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 @@ -1251,7 +1250,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), @@ -1269,7 +1268,7 @@ static const VMStateDescription vmstate_xen_vcpu = { .version_id = 1, .minimum_version_id = 1, .needed = xen_vcpu_needed, - .fields = (VMStateField[]) { + .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), @@ -1295,7 +1294,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() } @@ -1314,7 +1313,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() } @@ -1334,7 +1333,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() } @@ -1367,7 +1366,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), @@ -1391,7 +1390,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() } @@ -1410,7 +1409,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() @@ -1430,7 +1429,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() } @@ -1450,7 +1449,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() } @@ -1470,7 +1469,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() } @@ -1489,7 +1488,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() } @@ -1517,7 +1516,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() } @@ -1536,14 +1535,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; @@ -1557,7 +1602,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() @@ -1578,7 +1623,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), @@ -1599,7 +1644,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() } @@ -1611,7 +1656,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), @@ -1699,7 +1744,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, @@ -1746,7 +1791,9 @@ const VMStateDescription vmstate_x86_cpu = { &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 6f1036d4699..c1aacea6135 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -6,7 +6,7 @@ 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')) @@ -18,9 +18,11 @@ i386_system_ss.add(files( 'arch_memory_mapping.c', 'machine.c', 'monitor.c', - 'cpu-sysemu.c', + 'cpu-apic.c', + 'cpu-system.c', )) -i386_system_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-system-stub.c')) i386_user_ss = ss.source_set() @@ -29,7 +31,8 @@ subdir('whpx') subdir('nvmm') subdir('hvf') subdir('tcg') +subdir('emulate') target_arch += {'i386': i386_ss} -target_softmmu_arch += {'i386': i386_system_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 65128463270..3c9b6ca62f2 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -27,12 +27,9 @@ #include "monitor/monitor.h" #include "monitor/hmp-target.h" #include "monitor/hmp.h" -#include "qapi/qmp/qdict.h" -#include "sysemu/kvm.h" +#include "qobject/qdict.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) @@ -647,22 +644,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/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index 6c46101ac1a..21443078b72 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -8,12 +8,13 @@ */ #include "qemu/osdep.h" -#include "sysemu/kvm_int.h" +#include "system/kvm_int.h" #include "qemu/main-loop.h" -#include "sysemu/cpus.h" +#include "system/accel-ops.h" +#include "system/cpus.h" #include "qemu/guest-random.h" -#include "sysemu/nvmm.h" +#include "system/nvmm.h" #include "nvmm-accel-ops.h" static void *qemu_nvmm_cpu_thread_fn(void *arg) @@ -25,7 +26,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 +49,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 +65,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, @@ -83,7 +81,7 @@ static void nvmm_kick_vcpu_thread(CPUState *cpu) cpus_kick_thread(cpu); } -static void nvmm_accel_ops_class_init(ObjectClass *oc, void *data) +static void nvmm_accel_ops_class_init(ObjectClass *oc, const void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); diff --git a/target/i386/nvmm/nvmm-accel-ops.h b/target/i386/nvmm/nvmm-accel-ops.h index 7c5461bd759..931bb5ca243 100644 --- a/target/i386/nvmm/nvmm-accel-ops.h +++ b/target/i386/nvmm/nvmm-accel-ops.h @@ -10,7 +10,7 @@ #ifndef TARGET_I386_NVMM_ACCEL_OPS_H #define TARGET_I386_NVMM_ACCEL_OPS_H -#include "sysemu/cpus.h" +#include "system/cpus.h" int nvmm_init_vcpu(CPUState *cpu); int nvmm_vcpu_exec(CPUState *cpu); diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 066a173d268..f1c6120ccf1 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -9,12 +9,12 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/address-spaces.h" -#include "exec/ioport.h" +#include "system/address-spaces.h" +#include "system/ioport.h" #include "qemu/accel.h" -#include "sysemu/nvmm.h" -#include "sysemu/cpus.h" -#include "sysemu/runstate.h" +#include "system/nvmm.h" +#include "system/cpus.h" +#include "system/runstate.h" #include "qemu/main-loop.h" #include "qemu/error-report.h" #include "qapi/error.h" @@ -30,6 +30,7 @@ struct AccelCPUState { struct nvmm_vcpu vcpu; uint8_t tpr; bool stop; + bool dirty; /* Window-exiting for INTs/NMIs. */ bool int_window_exit; @@ -78,7 +79,7 @@ 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(); AccelCPUState *qcpu = cpu->accel; struct nvmm_vcpu *vcpu = &qcpu->vcpu; @@ -215,7 +216,7 @@ 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(); AccelCPUState *qcpu = cpu->accel; struct nvmm_vcpu *vcpu = &qcpu->vcpu; @@ -340,7 +341,6 @@ nvmm_get_registers(CPUState *cpu) static bool nvmm_can_take_int(CPUState *cpu) { - CPUX86State *env = cpu->env_ptr; AccelCPUState *qcpu = cpu->accel; struct nvmm_vcpu *vcpu = &qcpu->vcpu; struct nvmm_machine *mach = get_nvmm_mach(); @@ -349,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. */ @@ -387,7 +387,7 @@ 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(); AccelCPUState *qcpu = cpu->accel; struct nvmm_vcpu *vcpu = &qcpu->vcpu; @@ -399,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) { @@ -462,7 +462,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) } } - qemu_mutex_unlock_iothread(); + bql_unlock(); } /* @@ -473,8 +473,8 @@ static void nvmm_vcpu_post_run(CPUState *cpu, struct nvmm_vcpu_exit *exit) { AccelCPUState *qcpu = cpu->accel; - CPUX86State *env = cpu->env_ptr; X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; uint64_t tpr; env->eflags = exit->exitstate.rflags; @@ -485,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(); } } @@ -508,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 @@ -517,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 = { @@ -645,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; } @@ -678,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(); 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; @@ -721,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) { @@ -806,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); @@ -828,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); } } @@ -929,9 +928,8 @@ 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; } } @@ -984,7 +982,7 @@ nvmm_init_vcpu(CPUState *cpu) } } - cpu->vcpu_dirty = true; + qcpu->dirty = true; cpu->accel = qcpu; return 0; @@ -1202,7 +1200,7 @@ nvmm_enabled(void) } static void -nvmm_accel_class_init(ObjectClass *oc, void *data) +nvmm_accel_class_init(ObjectClass *oc, const void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "NVMM"; diff --git a/target/i386/ops_sse.h b/target/i386/ops_sse.h index a0e425733f1..a2e4d480399 100644 --- a/target/i386/ops_sse.h +++ b/target/i386/ops_sse.h @@ -20,6 +20,7 @@ #include "crypto/aes.h" #include "crypto/aes-round.h" +#include "crypto/clmul.h" #if SHIFT == 0 #define Reg MMXReg @@ -841,7 +842,7 @@ int64_t helper_cvttsd2sq(CPUX86State *env, ZMMReg *s) void glue(helper_rsqrtps, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); int i; for (i = 0; i < 2 << SHIFT; i++) { d->ZMM_S(i) = float32_div(float32_one, @@ -854,7 +855,7 @@ void glue(helper_rsqrtps, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) #if SHIFT == 1 void helper_rsqrtss(CPUX86State *env, ZMMReg *d, ZMMReg *v, ZMMReg *s) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); int i; d->ZMM_S(0) = float32_div(float32_one, float32_sqrt(s->ZMM_S(0), &env->sse_status), @@ -868,7 +869,7 @@ void helper_rsqrtss(CPUX86State *env, ZMMReg *d, ZMMReg *v, ZMMReg *s) void glue(helper_rcpps, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); int i; for (i = 0; i < 2 << SHIFT; i++) { d->ZMM_S(i) = float32_div(float32_one, s->ZMM_S(i), &env->sse_status); @@ -879,7 +880,7 @@ void glue(helper_rcpps, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) #if SHIFT == 1 void helper_rcpss(CPUX86State *env, ZMMReg *d, ZMMReg *v, ZMMReg *s) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); int i; d->ZMM_S(0) = float32_div(float32_one, s->ZMM_S(0), &env->sse_status); for (i = 1; i < 2 << SHIFT; i++) { @@ -1110,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) @@ -1121,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) @@ -1132,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) @@ -1143,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 @@ -1609,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) @@ -1708,7 +1714,7 @@ void glue(helper_phminposuw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) void glue(helper_roundps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t mode) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; int i; @@ -1732,7 +1738,7 @@ void glue(helper_roundps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, void glue(helper_roundpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t mode) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; int i; @@ -1757,7 +1763,7 @@ void glue(helper_roundpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, void glue(helper_roundss, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, uint32_t mode) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; int i; @@ -1782,7 +1788,7 @@ void glue(helper_roundss, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, void glue(helper_roundsd, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, uint32_t mode) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; int i; @@ -1965,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: @@ -2122,41 +2129,18 @@ 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); } } @@ -2319,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) @@ -2331,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, @@ -2549,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/sev-sysemu-stub.c b/target/i386/sev-sysemu-stub.c deleted file mode 100644 index 96e1c15cc3f..00000000000 --- a/target/i386/sev-sysemu-stub.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * QEMU SEV system 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 "monitor/monitor.h" -#include "monitor/hmp-target.h" -#include "qapi/qapi-commands-misc-target.h" -#include "qapi/error.h" -#include "sev.h" - -SevInfo *qmp_query_sev(Error **errp) -{ - error_setg(errp, "SEV is not available in this QEMU"); - return NULL; -} - -SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp) -{ - error_setg(errp, "SEV is not available in this QEMU"); - return NULL; -} - -SevCapability *qmp_query_sev_capabilities(Error **errp) -{ - error_setg(errp, "SEV is not available in this QEMU"); - return NULL; -} - -void qmp_sev_inject_launch_secret(const char *packet_header, const char *secret, - bool has_gpa, uint64_t gpa, Error **errp) -{ - error_setg(errp, "SEV is not available in this QEMU"); -} - -int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) -{ - g_assert_not_reached(); -} - -void sev_es_set_reset_vector(CPUState *cpu) -{ -} - -int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) -{ - g_assert_not_reached(); -} - -SevAttestationReport *qmp_query_sev_attestation_report(const char *mnonce, - Error **errp) -{ - error_setg(errp, "SEV is not available in this QEMU"); - return NULL; -} - -void hmp_info_sev(Monitor *mon, const QDict *qdict) -{ - monitor_printf(mon, "SEV is not available in this QEMU\n"); -} diff --git a/target/i386/sev-system-stub.c b/target/i386/sev-system-stub.c new file mode 100644 index 00000000000..7c5c02a5657 --- /dev/null +++ b/target/i386/sev-system-stub.c @@ -0,0 +1,41 @@ +/* + * QEMU SEV system 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 "monitor/monitor.h" +#include "monitor/hmp-target.h" +#include "qapi/error.h" +#include "sev.h" + +int sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp) +{ + g_assert_not_reached(); +} + +void sev_es_set_reset_vector(CPUState *cpu) +{ +} + +int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) +{ + g_assert_not_reached(); +} + +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 fe2144c0388..1a12f0671cc 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include +#include #include #include @@ -25,106 +26,160 @@ #include "qemu/uuid.h" #include "qemu/error-report.h" #include "crypto/hash.h" -#include "sysemu/kvm.h" +#include "exec/target_page.h" +#include "system/kvm.h" +#include "kvm/kvm_i386.h" #include "sev.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/runstate.h" #include "trace.h" #include "migration/blocker.h" #include "qom/object.h" #include "monitor/monitor.h" #include "monitor/hmp-target.h" -#include "qapi/qapi-commands-misc-target.h" -#include "exec/confidential-guest-support.h" +#include "qapi/qapi-commands-misc-i386.h" +#include "confidential-guest.h" #include "hw/i386/pc.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" +#include "qemu/queue.h" -#define TYPE_SEV_GUEST "sev-guest" -OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST) +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 -/** - * SevGuestState: - * - * The SevGuestState object is used for creating and managing a SEV - * guest. - * - * # $QEMU \ - * -object sev-guest,id=sev0 \ - * -machine ...,memory-encryption=sev0 +typedef struct QEMU_PACKED SevHashTableEntry { + QemuUUID guid; + uint16_t len; + uint8_t hash[HASH_SIZE]; +} SevHashTableEntry; + +typedef struct QEMU_PACKED SevHashTable { + QemuUUID guid; + uint16_t len; + SevHashTableEntry cmdline; + SevHashTableEntry initrd; + SevHashTableEntry kernel; +} SevHashTable; + +/* + * Data encrypted by sev_encrypt_flash() must be padded to a multiple of + * 16 bytes. */ -struct SevGuestState { - ConfidentialGuestSupport parent_obj; +typedef struct QEMU_PACKED PaddedSevHashTable { + SevHashTable ht; + uint8_t padding[ROUND_UP(sizeof(SevHashTable), 16) - sizeof(SevHashTable)]; +} PaddedSevHashTable; + +QEMU_BUILD_BUG_ON(sizeof(PaddedSevHashTable) % 16 != 0); + +#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 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" +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); +}; -#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; +/** + * 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; -#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; + /* configuration parameters */ + uint32_t handle; + uint32_t policy; + char *dh_cert_file; + char *session_file; + OnOffAuto legacy_vm_type; +}; -/* hard code sha256 digest size */ -#define HASH_SIZE 32 +struct SevSnpGuestState { + SevCommonState parent_obj; -typedef struct QEMU_PACKED SevHashTableEntry { - QemuUUID guid; - uint16_t len; - uint8_t hash[HASH_SIZE]; -} SevHashTableEntry; + /* 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; +}; -typedef struct QEMU_PACKED SevHashTable { - QemuUUID guid; - uint16_t len; - SevHashTableEntry cmdline; - SevHashTableEntry initrd; - SevHashTableEntry kernel; -} SevHashTable; +#define DEFAULT_GUEST_POLICY 0x1 /* disable debug */ +#define DEFAULT_SEV_DEVICE "/dev/sev" +#define DEFAULT_SEV_SNP_POLICY 0x30000 -/* - * Data encrypted by sev_encrypt_flash() must be padded to a multiple of - * 16 bytes. - */ -typedef struct QEMU_PACKED PaddedSevHashTable { - SevHashTable ht; - uint8_t padding[ROUND_UP(sizeof(SevHashTable), 16) - sizeof(SevHashTable)]; -} PaddedSevHashTable; +typedef struct SevLaunchUpdateData { + QTAILQ_ENTRY(SevLaunchUpdateData) next; + hwaddr gpa; + void *hva; + size_t len; + int type; +} SevLaunchUpdateData; -QEMU_BUILD_BUG_ON(sizeof(PaddedSevHashTable) % 16 != 0); +static QTAILQ_HEAD(, SevLaunchUpdateData) launch_update; -static SevGuestState *sev_guest; static Error *sev_mig_blocker; static const char *const sev_fw_errlist[] = { @@ -157,6 +212,28 @@ static const char *const sev_fw_errlist[] = { #define SEV_FW_MAX_ERROR ARRAY_SIZE(sev_fw_errlist) +#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 +244,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 +282,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 +317,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 +347,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 +363,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) +bool +sev_enabled(void) { - SevGuestState *sev = SEV_GUEST(obj); + ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs; - 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); + return !!object_dynamic_cast(OBJECT(cgs), TYPE_SEV_COMMON); } -/* 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) +sev_snp_enabled(void) { - return !!sev_guest; + 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 +451,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); + 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->policy & SEV_POLICY_NODBG ? "off" : "on"); - monitor_printf(mon, "key-sharing: %s\n", - info->policy & SEV_POLICY_NOKS ? "off" : "on"); + 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, "SEV is not enabled\n"); + 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 +567,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 +579,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 +632,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 +694,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 +713,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,42 +753,72 @@ 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) { - gsize sz; - int ret = 1; int fw_error, rc; - struct kvm_sev_launch_start start = { - .handle = sev->handle, .policy = sev->policy - }; - guchar *session = NULL, *dh_cert = NULL; + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(sev_common); + struct kvm_sev_snp_launch_start *start = &sev_snp_guest->kvm_start_conf; - if (sev->session_file) { - if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) { - goto out; - } - start.session_uaddr = (unsigned long)session; - start.session_len = sz; - } + trace_kvm_sev_snp_launch_start(start->policy, + sev_snp_guest->guest_visible_workarounds); - if (sev->dh_cert_file) { - if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) { - goto out; - } - start.dh_uaddr = (unsigned long)dh_cert; - start.dh_len = sz; + 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_guest->handle, .policy = sev_guest->policy + }; + guchar *session = NULL, *dh_cert = NULL; + + 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_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; + start.dh_len = sz; } 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 +827,152 @@ sev_launch_start(SevGuestState *sev) 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_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_adjust_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(SevGuestState *sev, uint8_t *addr, uint64_t len) +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 +981,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 +995,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 +1012,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 +1044,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 +1052,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,60 +1095,349 @@ 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 +snp_populate_metadata_pages(SevSnpGuestState *sev_snp, + OvmfSevMetadata *metadata) +{ + OvmfSevMetadataDesc *desc; + int type, ret, i; + void *hva; + MemoryRegion *mr = NULL; + + 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); + } + } +} + +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) { - SevGuestState *sev = opaque; + SevCommonState *sev_common = opaque; + SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(opaque); if (running) { - if (!sev_check_state(sev, SEV_STATE_RUNNING)) { - sev_launch_finish(sev); + if (!sev_check_state(sev_common, SEV_STATE_RUNNING)) { + klass->launch_finish(sev_common); } } } -int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +/* + * 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) { - 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 = {}; + /* Currently no KVM_SEV_INIT2-specific options are exposed via QEMU */ + return false; +} - if (!sev) { - return 0; +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; } - ret = ram_block_discard_disable(true); - if (ret) { - error_report("%s: cannot disable RAM discard", __func__); + /* 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_guest = sev; - sev->state = SEV_STATE_UNINIT; + 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) +{ + 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); + + sev_common->state = SEV_STATE_UNINIT; host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); host_cbitpos = ebx & 0x3f; @@ -937,10 +1447,10 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) * register of CPUID 0x8000001F. No need to verify the range as the * comparison against the host value accomplishes that. */ - if (host_cbitpos != sev->cbitpos) { + 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; } /* @@ -948,94 +1458,168 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) * the EBX register of CPUID 0x8000001F, so verify the supplied value * is in the range of 1 to 63. */ - if (sev->reduced_phys_bits < 1 || sev->reduced_phys_bits > 63) { + 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->reduced_phys_bits); - goto err; + __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; } -int -sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) +static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { - if (!sev_guest) { - return 0; - } + int ret; - /* 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 (ret < 0) { - error_setg(errp, "SEV: Failed to encrypt pflash rom"); - return 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(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp) +{ + 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_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; + } } return 0; @@ -1044,22 +1628,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; } @@ -1093,7 +1679,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'", @@ -1200,9 +1786,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; } @@ -1214,11 +1803,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) @@ -1226,6 +1815,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; @@ -1239,9 +1829,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); @@ -1269,44 +1859,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 @@ -1314,7 +1876,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; } @@ -1325,7 +1887,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; } @@ -1337,22 +1899,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; @@ -1373,7 +1925,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; } @@ -1383,10 +1980,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, const 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 = (const 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, const 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, const 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->adjust_cpuid_features = sev_snp_adjust_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 e7499c95b1e..373669eaace 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -18,7 +18,21 @@ #include CONFIG_DEVICES /* CONFIG_SEV */ #endif -#include "exec/confidential-guest-support.h" +#if !defined(CONFIG_SEV) || defined(CONFIG_USER_ONLY) +#define sev_enabled() 0 +#define sev_es_enabled() 0 +#define sev_snp_enabled() 0 +#else +bool sev_enabled(void); +bool sev_es_enabled(void); +bool sev_snp_enabled(void); +#endif + +#if !defined(CONFIG_USER_ONLY) + +#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 @@ -27,6 +41,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; @@ -38,25 +55,20 @@ typedef struct SevKernelLoaderContext { size_t cmdline_size; } SevKernelLoaderContext; -#ifdef CONFIG_SEV -bool sev_enabled(void); -bool sev_es_enabled(void); -#else -#define sev_enabled() 0 -#define sev_es_enabled() 0 -#endif - -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 /* !CONFIG_USER_ONLY */ + +uint32_t sev_get_cbit_position(void); +uint32_t sev_get_reduced_phys_bits(void); #endif diff --git a/target/i386/svm.h b/target/i386/svm.h index f9a785489d8..1bd78447306 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 00000000000..97e3f0e7568 --- /dev/null +++ b/target/i386/tcg/access.c @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Access guest memory in blocks. */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" +#include "exec/target_page.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 00000000000..d70808a3a39 --- /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 c310bd842f1..f1940b40927 100644 --- a/target/i386/tcg/cc_helper.c +++ b/target/i386/tcg/cc_helper.c @@ -22,41 +22,6 @@ #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.inc" #undef SHIFT @@ -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.inc b/target/i386/tcg/cc_helper_template.h.inc index bb611feb048..d8fd976ca15 100644 --- a/target/i386/tcg/cc_helper_template.h.inc +++ b/target/i386/tcg/cc_helper_template.h.inc @@ -22,12 +22,17 @@ #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 @@ -39,18 +44,32 @@ /* dynamic flags computation */ -static int glue(compute_all_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +static uint32_t glue(compute_all_cout, SUFFIX)(DATA_TYPE dst, DATA_TYPE carries) { - int cf, pf, af, zf, sf, of; - DATA_TYPE src2 = dst - src1; + uint32_t af_cf, pf, zf, sf, of; - cf = dst < src1; - pf = parity_table[(uint8_t)dst]; - af = (dst ^ src1 ^ src2) & CC_A; + /* PF, ZF, SF computed from result. */ + pf = compute_pf(dst); 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; + + /* + * AF, CF, OF computed from carry out vector. To compute AF and CF, rotate it + * left by one so cout(DATA_BITS - 1) is in bit 0 and cout(3) in bit 4. + * + * To compute OF, place the highest two carry bits into OF and the bit + * immediately to the right of it; then, adding CC_O / 2 XORs them. + */ + af_cf = ((carries << 1) | (carries >> (DATA_BITS - 1))) & (CC_A | CC_C); + of = (lshift(carries, 12 - DATA_BITS) + CC_O / 2) & CC_O; + return pf + zf + sf + af_cf + of; +} + +static uint32_t glue(compute_all_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + DATA_TYPE src2 = dst - src1; + DATA_TYPE carries = ADD_COUT_VEC(src1, src2, dst); + return glue(compute_all_cout, SUFFIX)(dst, carries); } static int glue(compute_c_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) @@ -58,39 +77,31 @@ 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, +static uint32_t 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; + DATA_TYPE carries = ADD_COUT_VEC(src1, src2, dst); + return glue(compute_all_cout, SUFFIX)(dst, carries); } 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 int glue(compute_all_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) +static uint32_t 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; + DATA_TYPE carries = SUB_COUT_VEC(src1, src2, dst); + return glue(compute_all_cout, SUFFIX)(dst, carries); } static int glue(compute_c_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) @@ -100,86 +111,80 @@ static int glue(compute_c_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) return src1 < src2; } -static int glue(compute_all_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, +static uint32_t 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; + DATA_TYPE carries = SUB_COUT_VEC(src1, src2, dst); + return glue(compute_all_cout, SUFFIX)(dst, carries); } 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 int glue(compute_all_logic, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +static uint32_t glue(compute_all_logic, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { - int cf, pf, af, zf, sf, of; + uint32_t cf, pf, af, zf, sf, of; cf = 0; - pf = parity_table[(uint8_t)dst]; + 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; + return cf + pf + af + zf + sf + of; } -static int glue(compute_all_inc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +static uint32_t glue(compute_all_inc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { - int cf, pf, af, zf, sf, of; - DATA_TYPE src2; + uint32_t cf, pf, af, zf, sf, of; cf = src1; - src1 = dst - 1; - src2 = 1; - pf = parity_table[(uint8_t)dst]; - af = (dst ^ src1 ^ src2) & CC_A; + pf = compute_pf(dst); + af = (dst ^ (dst - 1)) & CC_A; /* bits 0..3 are all clear */ 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; + return cf + pf + af + zf + sf + of; } -static int glue(compute_all_dec, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +static uint32_t glue(compute_all_dec, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { - int cf, pf, af, zf, sf, of; - DATA_TYPE src2; + uint32_t cf, pf, af, zf, sf, of; cf = src1; - src1 = dst + 1; - src2 = 1; - pf = parity_table[(uint8_t)dst]; - af = (dst ^ src1 ^ src2) & CC_A; + pf = compute_pf(dst); + af = (dst ^ (dst + 1)) & CC_A; /* bits 0..3 are all set */ 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; + return cf + pf + af + zf + sf + of; } -static int glue(compute_all_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +static uint32_t glue(compute_all_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { - int cf, pf, af, zf, sf, of; + uint32_t cf, pf, af, zf, sf, of; cf = (src1 >> (DATA_BITS - 1)) & CC_C; - pf = parity_table[(uint8_t)dst]; + 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; + return cf + pf + af + zf + sf + of; } static int glue(compute_c_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) @@ -187,39 +192,39 @@ 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) +static uint32_t glue(compute_all_sar, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { - int cf, pf, af, zf, sf, of; + uint32_t cf, pf, af, zf, sf, of; cf = src1 & 1; - pf = parity_table[(uint8_t)dst]; + 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; + 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) +static uint32_t glue(compute_all_mul, SUFFIX)(DATA_TYPE dst, target_long src1) { - int cf, pf, af, zf, sf, of; + uint32_t cf, pf, af, zf, sf, of; cf = (src1 != 0); - pf = parity_table[(uint8_t)dst]; + 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; + return cf + pf + af + zf + sf + of; } -static int glue(compute_all_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +static uint32_t glue(compute_all_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { - int cf, pf, af, zf, sf, of; + uint32_t cf, pf, af, zf, sf, of; cf = (src1 == 0); pf = 0; /* undefined */ @@ -227,7 +232,7 @@ static int glue(compute_all_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) zf = (dst == 0) * CC_Z; sf = lshift(dst, 8 - DATA_BITS) & CC_S; of = 0; - return cf | pf | af | zf | sf | of; + return cf + pf + af + zf + sf + of; } static int glue(compute_c_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) @@ -235,8 +240,26 @@ 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 0db19cda3b7..55216e0d249 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, @@ -114,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 @@ -132,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) { @@ -154,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]; } } @@ -314,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] = { @@ -337,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,xh, 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), /* @@ -362,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 */ @@ -391,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), - - [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), + /* Listed incorrectly as type 4 */ + [0x0c] = X86_OP_ENTRY3(VPERMILPS, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX) p_00_66), + [0x0d] = X86_OP_ENTRY3(VPERMILPD, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX) p_66), + [0x0e] = X86_OP_ENTRY3(VTESTPS, None,None, V,x, W,x, vex6 chk(W0) cpuid(AVX) p_66), + [0x0f] = X86_OP_ENTRY3(VTESTPD, None,None, V,x, W,x, vex6 chk(W0) cpuid(AVX) p_66), + + [0x18] = X86_OP_ENTRY3(VPBROADCASTD, V,x, None,None, W,d, vex6 chk(W0) cpuid(AVX) p_66), /* vbroadcastss */ + [0x19] = X86_OP_ENTRY3(VPBROADCASTQ, V,qq, None,None, W,q, vex6 chk(W0) cpuid(AVX) p_66), /* vbroadcastsd */ + [0x1a] = X86_OP_ENTRY3(VBROADCASTx128, V,qq, None,None, WM,dq,vex6 chk(W0) cpuid(AVX) p_66), [0x1c] = X86_OP_ENTRY3(PABSB, V,x, None,None, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), [0x1d] = X86_OP_ENTRY3(PABSW, V,x, None,None, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), [0x1e] = X86_OP_ENTRY3(PABSD, V,x, None,None, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), @@ -407,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), @@ -422,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), @@ -460,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)), @@ -491,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)), {}, {}, {}, @@ -500,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] = { @@ -512,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)), {}, }, }; @@ -554,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), - - [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), + [0x00] = X86_OP_ENTRY3(VPERMQ, V,qq, W,qq, I,b, vex6 chk(W1) cpuid(AVX2) p_66), + [0x01] = X86_OP_ENTRY3(VPERMQ, V,qq, W,qq, I,b, vex6 chk(W1) cpuid(AVX2) p_66), /* VPERMPD */ + [0x02] = X86_OP_ENTRY4(VBLENDPS, V,x, H,x, W,x, vex6 chk(W0) cpuid(AVX2) p_66), /* VPBLENDD */ + [0x04] = X86_OP_ENTRY3(VPERMILPS_i, V,x, W,x, I,b, vex6 chk(W0) cpuid(AVX) p_66), + [0x05] = X86_OP_ENTRY3(VPERMILPD_i, V,x, W,x, I,b, vex6 chk(W0) cpuid(AVX) p_66), + [0x06] = X86_OP_ENTRY4(VPERM2x128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX) p_66), + + [0x14] = X86_OP_ENTRY3(PEXTRB, E,b, V,dq, I,b, vex5 cpuid(SSE41) op0_Rd p_66), + [0x15] = X86_OP_ENTRY3(PEXTRW, E,w, V,dq, I,b, vex5 cpuid(SSE41) op0_Rd p_66), [0x16] = X86_OP_ENTRY3(PEXTR, E,y, V,dq, I,b, vex5 cpuid(SSE41) p_66), [0x17] = X86_OP_ENTRY3(VEXTRACTPS, E,d, V,dq, I,b, vex5 cpuid(SSE41) p_66), - [0x1d] = X86_OP_ENTRY3(VCVTPS2PH, W,xh, V,x, I,b, vex11 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), @@ -575,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), @@ -598,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), @@ -841,14 +1123,32 @@ static void decode_0FE6(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui *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), @@ -860,6 +1160,31 @@ static const X86OpEntry opcodes_0F[256] = { /* Incorrectly listed as Mq,Vq in the manual */ [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), /* sqrtps */ [0x52] = X86_OP_GROUP3(sse_unary, V,x, H,x, W,x, vex4_rep5 p_00_f3), /* rsqrtps */ @@ -887,6 +1212,97 @@ 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), + [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), + + [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), + + [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), + + [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), + [0xd2] = X86_OP_ENTRY3(PSRLD_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xd3] = X86_OP_ENTRY3(PSRLQ_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xd4] = X86_OP_ENTRY3(PADDQ, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xd5] = X86_OP_ENTRY3(PMULLW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xd6] = X86_OP_GROUP0(0FD6), + [0xd7] = X86_OP_ENTRY3(PMOVMSKB, G,d, None,None, U,x, vex7 mmx avx2_256 p_00_66), + + [0xe0] = X86_OP_ENTRY3(PAVGB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xe1] = X86_OP_ENTRY3(PSRAW_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), + [0xe2] = X86_OP_ENTRY3(PSRAD_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), + [0xe3] = X86_OP_ENTRY3(PAVGW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xe4] = X86_OP_ENTRY3(PMULHUW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xe5] = X86_OP_ENTRY3(PMULHW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xe6] = X86_OP_GROUP0(0FE6), + [0xe7] = X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex1 mmx p_00_66), /* MOVNTQ/MOVNTDQ */ + + [0xf0] = X86_OP_ENTRY3(MOVDQ, V,x, None,None, WM,x, vex4_unal cpuid(SSE3) p_f2), /* LDDQU */ + [0xf1] = X86_OP_ENTRY3(PSLLW_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), + [0xf2] = X86_OP_ENTRY3(PSLLD_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), + [0xf3] = X86_OP_ENTRY3(PSLLQ_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), + [0xf4] = X86_OP_ENTRY3(PMULUDQ, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xf5] = X86_OP_ENTRY3(PMADDWD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xf6] = X86_OP_ENTRY3(PSADBW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), + [0xf7] = X86_OP_ENTRY3(MASKMOV, None,None, V,dq, U,dq, vex4_unal avx2_256 mmx p_00_66), + + [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), @@ -899,6 +1315,15 @@ static const X86OpEntry opcodes_0F[256] = { [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), @@ -924,39 +1349,55 @@ static const X86OpEntry opcodes_0F[256] = { [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), - - [0xc2] = X86_OP_ENTRY4(VCMP, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), - [0xc4] = X86_OP_ENTRY4(PINSRW, V,dq,H,dq,E,w, vex5 mmx p_00_66), - [0xc5] = X86_OP_ENTRY3(PEXTRW, G,d, U,dq,I,b, vex5 mmx p_00_66), - [0xc6] = X86_OP_ENTRY4(VSHUF, V,x, H,x, W,x, vex4 p_00_66), - - [0xd0] = X86_OP_ENTRY3(VADDSUB, V,x, H,x, W,x, vex2 cpuid(SSE3) p_66_f2), - [0xd1] = X86_OP_ENTRY3(PSRLW_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xd2] = X86_OP_ENTRY3(PSRLD_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xd3] = X86_OP_ENTRY3(PSRLQ_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xd4] = X86_OP_ENTRY3(PADDQ, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xd5] = X86_OP_ENTRY3(PMULLW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xd6] = X86_OP_GROUP0(0FD6), - [0xd7] = X86_OP_ENTRY3(PMOVMSKB, G,d, None,None, U,x, vex7 mmx avx2_256 p_00_66), - - [0xe0] = X86_OP_ENTRY3(PAVGB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xe1] = X86_OP_ENTRY3(PSRAW_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), - [0xe2] = X86_OP_ENTRY3(PSRAD_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), - [0xe3] = X86_OP_ENTRY3(PAVGW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xe4] = X86_OP_ENTRY3(PMULHUW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xe5] = X86_OP_ENTRY3(PMULHW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xe6] = X86_OP_GROUP0(0FE6), - [0xe7] = X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex1 mmx p_00_66), /* MOVNTQ/MOVNTDQ */ - - [0xf0] = X86_OP_ENTRY3(MOVDQ, V,x, None,None, WM,x, vex4_unal cpuid(SSE3) p_f2), /* LDDQU */ - [0xf1] = X86_OP_ENTRY3(PSLLW_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), - [0xf2] = X86_OP_ENTRY3(PSLLD_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), - [0xf3] = X86_OP_ENTRY3(PSLLQ_r, V,x, H,x, W,x, vex7 mmx avx2_256 p_00_66), - [0xf4] = X86_OP_ENTRY3(PMULUDQ, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xf5] = X86_OP_ENTRY3(PMADDWD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xf6] = X86_OP_ENTRY3(PSADBW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66), - [0xf7] = X86_OP_ENTRY3(MASKMOV, None,None, V,dq, U,dq, vex4_unal avx2_256 mmx p_00_66), + /* + * 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), @@ -984,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) @@ -998,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 @@ -1026,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; } @@ -1079,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))) { @@ -1141,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 */ @@ -1150,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; @@ -1176,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 */ @@ -1220,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 */ @@ -1256,43 +2161,20 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, } case X86_TYPE_I: /* Immediate */ + case X86_TYPE_J: /* Relative offset for a jump */ op->unit = X86_OP_IMM; - decode->immediate = insn_get_signed(env, s, op->ot); + decode->immediate = op->imm = insn_get_signed(env, s, op->ot); break; - case X86_TYPE_J: /* Relative offset for a jump */ + case X86_TYPE_I_unsigned: /* Immediate */ 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(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; @@ -1409,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: @@ -1417,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: @@ -1442,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); @@ -1456,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(); } @@ -1493,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) { @@ -1579,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: @@ -1593,21 +2519,38 @@ illegal: * 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:; +#ifdef TARGET_X86_64 + /* clear any REX prefix followed by other prefixes. */ + int rex; + rex = -1; + next_byte_rex: +#endif + b = x86_ldub_code(env, s); - next_byte: - if (first) { - first = false; - } else { - b = x86_ldub_code(env, s); - } /* Collect prefixes. */ switch (b) { case 0xf3: @@ -1648,13 +2591,12 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) #ifdef TARGET_X86_64 case 0x40 ... 0x4f: if (CODE64(s)) { - /* REX prefix */ - s->prefix |= PREFIX_REX; - s->vex_w = (b >> 3) & 1; - s->rex_r = (b & 0x4) << 1; - s->rex_x = (b & 0x2) << 2; - s->rex_b = (b & 0x1) << 3; - goto next_byte; + /* + * REX prefix; ignored unless it is the last prefix, so + * for now just stash it + */ + rex = b; + goto next_byte_rex; } break; #endif @@ -1681,10 +2623,13 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) /* 4.1.1-4.1.3: No preceding lock, 66, f2, f3, or rex prefixes. */ if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ - | PREFIX_LOCK | PREFIX_DATA | PREFIX_REX)) { + | PREFIX_LOCK | PREFIX_DATA)) { goto illegal_op; } #ifdef TARGET_X86_64 + if (rex != -1) { + goto illegal_op; + } s->rex_r = (~vex2 >> 4) & 8; #endif if (b == 0xc5) { @@ -1719,15 +2664,21 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) } break; default: - if (b >= 0x100) { - b -= 0x100; - decode_func = do_decode_0F; - } break; } /* Post-process prefixes. */ if (CODE64(s)) { +#ifdef TARGET_X86_64 + if (rex != -1) { + s->prefix |= PREFIX_REX; + s->vex_w = (rex >> 3) & 1; + s->rex_r = (rex & 0x4) << 1; + s->rex_x = (rex & 0x2) << 2; + s->rex_b = (rex & 0x1) << 3; + } +#endif + /* * 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 @@ -1752,6 +2703,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; @@ -1764,6 +2716,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; @@ -1772,36 +2753,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; @@ -1813,27 +2781,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_helper_enter_mmx(cpu_env); + gen_helper_enter_mmx(tcg_env); } - if (decode.op[0].has_ea || decode.op[1].has_ea || decode.op[2].has_ea) { - gen_load_ea(s, &decode.mem, decode.e.vex_class == 12); + if (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]); @@ -1842,9 +2859,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); } + + /* + * 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 a542ec16813..7f23d373ea7 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,7 +90,9 @@ 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, @@ -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 45a3e55cbfb..1a7fab9333a 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -19,7 +19,13 @@ * License along with this library; if not, see . */ -#define ZMM_OFFSET(reg) offsetof(CPUX86State, xmm_regs[reg]) +#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 +51,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 +64,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 +176,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 +196,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 +247,54 @@ 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_TL && v == s->T0 && + (decode->e.special == X86_SPECIAL_SExtT0 || + decode->e.special == X86_SPECIAL_ZExtT0)) { + if (op->ot == MO_8 && byte_reg_is_xH(s, op->n)) { + if (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 (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); } 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 +318,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 +341,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, v, op->n == R_SS); break; case X86_OP_INT: if (op->has_ea) { @@ -307,11 +360,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 +389,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 +501,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; @@ -400,12 +519,12 @@ static void gen_3dnow(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) return; } - gen_helper_enter_mmx(cpu_env); + gen_helper_enter_mmx(tcg_env); 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 +534,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 +545,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 +555,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 +577,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 +591,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 +616,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 +662,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 +679,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 +690,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 +704,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 +718,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 +726,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,46 +739,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_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); \ } @@ -675,7 +794,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); \ \ @@ -693,7 +812,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); \ \ @@ -752,7 +871,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)); @@ -763,19 +882,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); \ @@ -822,9 +941,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); \ @@ -847,33 +966,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); \ } @@ -905,7 +1024,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); @@ -917,9 +1036,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); \ } @@ -932,21 +1051,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); \ } @@ -954,21 +1073,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); \ } @@ -978,7 +1097,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) { @@ -989,8 +1108,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 @@ -1002,32 +1121,114 @@ static inline void gen_vsib_avx(DisasContext *s, CPUX86State *env, X86DecodedIns } } #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) +{ + 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_ADD(DisasContext *s, X86DecodedInsn *decode); +static void gen_ADC(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + TCGv c_in; + + /* + * Try to avoid CC_OP_ADC by transforming as follows: + * CC_ADC: src1 = dst + c_in, src2 = 0, src3 = c_in + * CC_ADD: src1 = dst + c_in, src2 = c_in (no src3) + * + * In general src2 vs. src3 matters when computing AF and OF, but not here: + * - AF is bit 4 of dst^src1^src2, which is bit 4 of dst^src1 in both cases + * - OF is a function of the two MSBs, and in both cases they are zero for src2 + */ + if (decode->e.op2 == X86_TYPE_I && decode->immediate == 0) { + gen_compute_eflags_c(s, s->T1); + gen_ADD(s, decode); + return; + } + + 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) { - int opposite_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); } @@ -1039,45 +1240,83 @@ static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op) 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; } +} + +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; - 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); + 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_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, CPUX86State *env, X86DecodedInsn *decode) +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 +1328,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,745 +1341,2873 @@ 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_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); + } } -static void gen_BZHI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +/* 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; - TCGv bound = tcg_constant_tl(ot == MO_64 ? 63 : 31); - TCGv zero = tcg_constant_tl(0); - TCGv mone = tcg_constant_tl(-1); - tcg_gen_ext8u_tl(s->T1, s->T1); + /* 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); /* - * Note that since we're using BMILG (in order to get O - * cleared) we need to store the inverse into C. + * 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_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_ctz_tl(s->T0, s->T0, s->T1); } -static void gen_CRC32(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +/* 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[2].ot; + MemOp ot = decode->op[0].ot; - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_crc32(s->T0, s->tmp2_i32, s->T1, tcg_constant_i32(8 << ot)); -} + /* 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); -static void gen_CVTPI2Px(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) -{ - gen_helper_enter_mmx(cpu_env); - if (s->prefix & PREFIX_DATA) { - gen_helper_cvtpi2pd(cpu_env, OP_PTR0, OP_PTR2); - } else { - gen_helper_cvtpi2ps(cpu_env, OP_PTR0, OP_PTR2); - } + /* + * 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_CVTPx2PI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BSWAP(DisasContext *s, X86DecodedInsn *decode) { - gen_helper_enter_mmx(cpu_env); - if (s->prefix & PREFIX_DATA) { - gen_helper_cvtpd2pi(cpu_env, OP_PTR0, OP_PTR2); - } else { - gen_helper_cvtps2pi(cpu_env, OP_PTR0, OP_PTR2); +#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 void gen_CVTTPx2PI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static TCGv gen_bt_mask(DisasContext *s, X86DecodedInsn *decode) { - gen_helper_enter_mmx(cpu_env); - if (s->prefix & PREFIX_DATA) { - gen_helper_cvttpd2pi(cpu_env, OP_PTR0, OP_PTR2); - } else { - gen_helper_cvttps2pi(cpu_env, OP_PTR0, OP_PTR2); - } -} + MemOp ot = decode->op[1].ot; + TCGv mask = tcg_temp_new(); -static void gen_EMMS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) -{ - gen_helper_emms(cpu_env); + tcg_gen_andi_tl(s->T1, s->T1, (8 << ot) - 1); + tcg_gen_shl_tl(mask, tcg_constant_tl(1), s->T1); + return mask; } -static void gen_EXTRQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +/* Expects truncated bit index in COUNT, 1 << COUNT in MASK. */ +static void gen_bt_flags(DisasContext *s, X86DecodedInsn *decode, TCGv src, + TCGv count, TCGv mask) { - 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); -} + TCGv cf; -static void gen_EXTRQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) -{ - gen_helper_extrq_r(cpu_env, OP_PTR0, OP_PTR2); + /* + * 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, count); + } } -static void gen_INSERTQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BT(DisasContext *s, X86DecodedInsn *decode) { - TCGv_i32 length = tcg_constant_i32(decode->immediate & 63); - TCGv_i32 index = tcg_constant_i32((decode->immediate >> 8) & 63); + TCGv count = s->T1; + TCGv mask; - gen_helper_insertq_i(cpu_env, OP_PTR0, OP_PTR1, index, length); -} + /* + * Try to ensure that the rhs of the TSTNE condition is a constant (and a + * power of two), as that is more readily available on most TCG backends. + * + * For immediate bit number gen_bt_mask()'s output is already a constant; + * for register bit number, shift the source right and check bit 0. + */ + if (decode->e.op2 == X86_TYPE_I) { + mask = gen_bt_mask(s, decode); + } else { + MemOp ot = decode->op[1].ot; -static void gen_INSERTQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) -{ - gen_helper_insertq_r(cpu_env, OP_PTR0, OP_PTR2); -} + tcg_gen_andi_tl(s->T1, s->T1, (8 << ot) - 1); + tcg_gen_shr_tl(s->T0, s->T0, s->T1); -static void gen_LDMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) -{ - if (s->vex_l) { - gen_illegal_opcode(s); - return; + count = tcg_constant_tl(0); + mask = tcg_constant_tl(1); } - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T1); - gen_helper_ldmxcsr(cpu_env, s->tmp2_i32); + gen_bt_flags(s, decode, s->T0, count, mask); } -static void gen_MASKMOV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BTC(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[0].ot; + TCGv old = tcg_temp_new(); + TCGv mask = gen_bt_mask(s, decode); - if (s->prefix & PREFIX_DATA) { - gen_helper_maskmov_xmm(cpu_env, OP_PTR1, OP_PTR2, s->A0); + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_fetch_xor_tl(old, s->A0, mask, s->mem_index, ot | MO_LE); } else { - gen_helper_maskmov_mmx(cpu_env, OP_PTR1, OP_PTR2, s->A0); + tcg_gen_mov_tl(old, s->T0); + tcg_gen_xor_tl(s->T0, s->T0, mask); } + + gen_bt_flags(s, decode, old, s->T1, mask); } -static void gen_MOVBE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +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); - /* M operand type does not load/store */ - if (decode->e.op0 == X86_TYPE_M) { - tcg_gen_qemu_st_tl(s->T0, s->A0, s->mem_index, ot | MO_BE); + 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_qemu_ld_tl(s->T0, s->A0, s->mem_index, ot | MO_BE); + tcg_gen_mov_tl(old, s->T0); + tcg_gen_andc_tl(s->T0, s->T0, mask); } + + gen_bt_flags(s, decode, old, s->T1, mask); } -static void gen_MOVD_from(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BTS(DisasContext *s, X86DecodedInsn *decode) { - MemOp ot = decode->op[2].ot; + MemOp ot = decode->op[0].ot; + TCGv old = tcg_temp_new(); + TCGv mask = gen_bt_mask(s, decode); - switch (ot) { - case MO_32: -#ifdef TARGET_X86_64 - tcg_gen_ld32u_tl(s->T0, cpu_env, decode->op[2].offset); - break; - case MO_64: -#endif - tcg_gen_ld_tl(s->T0, cpu_env, decode->op[2].offset); - break; - default: - abort(); + 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, s->T1, mask); } -static void gen_MOVD_to(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BZHI(DisasContext *s, X86DecodedInsn *decode) { - MemOp ot = decode->op[2].ot; - int vec_len = vector_len(s, decode); - int lo_ofs = vector_elem_offset(&decode->op[0], ot, 0); + MemOp ot = decode->op[0].ot; + TCGv bound = tcg_constant_tl(ot == MO_64 ? 63 : 31); + TCGv zero = tcg_constant_tl(0); + TCGv mone = tcg_constant_tl(-1); - tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + tcg_gen_ext8u_tl(s->T1, s->T1); - switch (ot) { - case MO_32: -#ifdef TARGET_X86_64 - tcg_gen_st32_tl(s->T1, cpu_env, lo_ofs); - break; - case MO_64: -#endif - tcg_gen_st_tl(s->T1, cpu_env, lo_ofs); - break; - default: - g_assert_not_reached(); - } + tcg_gen_shl_tl(s->A0, mone, s->T1); + tcg_gen_movcond_tl(TCG_COND_LEU, s->A0, s->T1, bound, s->A0, zero); + tcg_gen_andc_tl(s->T0, s->T0, s->A0); + /* + * Note that since we're using BMILG (in order to get O + * cleared) we need to store the inverse into C. + */ + tcg_gen_setcond_tl(TCG_COND_LEU, s->T1, s->T1, bound); + prepare_update2_cc(decode, s, CC_OP_BMILGB + ot); } -static void gen_MOVDQ(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CALL(DisasContext *s, X86DecodedInsn *decode) { - gen_store_sse(s, decode, decode->op[2].offset); + gen_push_v(s, eip_next_tl(s)); + gen_JMP(s, decode); } -static void gen_MOVMSK(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CALL_m(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); - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); + gen_push_v(s, eip_next_tl(s)); + gen_JMP_m(s, decode); } -static void gen_MOVQ(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CALLF(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); - if (decode->op[0].has_ea) { - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); - } else { - /* - * tcg_gen_gvec_dup_i64(MO_64, op0.offset, 8, vec_len, s->tmp1_64) would - * seem to work, but it does not on big-endian platforms; the cleared parts - * are always at higher addresses, but cross-endian emulation inverts the - * byte order so that the cleared parts need to be at *lower* addresses. - * Because oprsz is 8, we see this here even for SSE; but more in general, - * it disqualifies using oprsz < maxsz to emulate VEX128. - */ - tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, lo_ofs); - } + gen_far_call(s); } -static void gen_MOVq_dq(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CALLF_m(DisasContext *s, X86DecodedInsn *decode) { - gen_helper_enter_mmx(cpu_env); - /* Otherwise the same as any other movq. */ - return gen_MOVQ(s, env, 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_MULX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CBW(DisasContext *s, X86DecodedInsn *decode) { - MemOp ot = decode->op[0].ot; + MemOp src_ot = decode->op[0].ot - 1; - /* low part of result in VEX.vvvv, high in MODRM */ - switch (ot) { - default: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - tcg_gen_mulu2_i32(s->tmp2_i32, s->tmp3_i32, - s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_regs[s->vex_v], s->tmp2_i32); - tcg_gen_extu_i32_tl(s->T0, s->tmp3_i32); - break; -#ifdef TARGET_X86_64 - case MO_64: - tcg_gen_mulu2_i64(cpu_regs[s->vex_v], s->T0, s->T0, s->T1); - break; -#endif - } + 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_PALIGNR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CLD(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); - } else if (!s->vex_l) { - gen_helper_palignr_xmm(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); - } else { - gen_helper_palignr_ymm(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); - } + tcg_gen_st_i32(tcg_constant_i32(1), tcg_env, offsetof(CPUX86State, df)); } -static void gen_PANDN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CLI(DisasContext *s, X86DecodedInsn *decode) { - int vec_len = vector_len(s, decode); + gen_reset_eflags(s, IF_MASK); +} - /* Careful, operand order is reversed! */ - tcg_gen_gvec_andc(MO_64, - decode->op[0].offset, decode->op[2].offset, - decode->op[1].offset, vec_len, vec_len); +static void gen_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_PCMPESTRI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CMC(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_compute_eflags(s); + tcg_gen_xori_tl(cpu_cc_src, cpu_cc_src, CC_C); } -static void gen_PCMPESTRM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CMOVcc(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); - 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); + gen_cmovcc(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 */ + cmp_lhs = tcg_temp_new(), cmp_rhs = tcg_constant_tl(0); + tcg_gen_xor_tl(newv, s->cc_srcT, s->T0); + tcg_gen_xor_tl(cmp_lhs, s->cc_srcT, cmpv); + tcg_gen_and_tl(cmp_lhs, cmp_lhs, newv); + tcg_gen_sextract_tl(cmp_lhs, cmp_lhs, 0, 8 << ot); + break; + + case JCC_P: + cmp_lhs = tcg_temp_new(), cmp_rhs = tcg_constant_tl(1); + tcg_gen_ext8u_tl(cmp_lhs, s->T0); + tcg_gen_ctpop_tl(cmp_lhs, cmp_lhs); + break; + + case JCC_S: + cmp_lhs = tcg_temp_new(), cmp_rhs = tcg_constant_tl(0); + tcg_gen_sextract_tl(cmp_lhs, s->T0, 0, 8 << ot); + 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_PCMPISTRI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CMPS(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); + MemOp ot = decode->op[2].ot; + gen_repz_nz(s, ot, gen_cmps); } -static void gen_PCMPISTRM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CMPXCHG(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); - 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); + 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_PDEP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CMPXCHG16B(DisasContext *s, X86DecodedInsn *decode) { - MemOp ot = decode->op[1].ot; - if (ot < MO_64) { - tcg_gen_ext32u_tl(s->T0, s->T0); +#ifdef TARGET_X86_64 + MemOp mop = MO_LE | 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_LEUQ); + } else { + tcg_gen_nonatomic_cmpxchg_i64(old, s->A0, cmp, val, + s->mem_index, MO_LEUQ); + } + + /* Compute 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; + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(tmp, s->T0); + gen_helper_crc32(s->T0, tmp, s->T1, tcg_constant_i32(8 << ot)); +} + +static void gen_CVTPI2Px(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_enter_mmx(tcg_env); + if (s->prefix & PREFIX_DATA) { + gen_helper_cvtpi2pd(tcg_env, OP_PTR0, OP_PTR2); + } else { + gen_helper_cvtpi2ps(tcg_env, OP_PTR0, OP_PTR2); } - gen_helper_pdep(s->T0, s->T0, s->T1); } -static void gen_PEXT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CVTPx2PI(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_enter_mmx(tcg_env); + if (s->prefix & PREFIX_DATA) { + gen_helper_cvtpd2pi(tcg_env, OP_PTR0, OP_PTR2); + } else { + gen_helper_cvtps2pi(tcg_env, OP_PTR0, OP_PTR2); + } +} + +static void gen_CVTTPx2PI(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_enter_mmx(tcg_env); + if (s->prefix & PREFIX_DATA) { + gen_helper_cvttpd2pi(tcg_env, OP_PTR0, OP_PTR2); + } else { + gen_helper_cvttps2pi(tcg_env, OP_PTR0, OP_PTR2); + } +} + +static void gen_CWD(DisasContext *s, X86DecodedInsn *decode) +{ + int shift = 8 << decode->op[0].ot; + + tcg_gen_sextract_tl(s->T0, s->T0, shift - 1, 1); +} + +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; - if (ot < MO_64) { - tcg_gen_ext32u_tl(s->T0, s->T0); + + 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); } - gen_helper_pext(s->T0, s->T0, s->T1); + prepare_update_cc_incdec(decode, s, CC_OP_DECB + ot); } -static inline void gen_pextr(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, MemOp ot) +static void gen_DIV(DisasContext *s, X86DecodedInsn *decode) { - int vec_len = vector_len(s, decode); - int mask = (vec_len >> ot) - 1; - int val = decode->immediate & mask; + MemOp ot = decode->op[1].ot; - switch (ot) { + switch(ot) { case MO_8: - tcg_gen_ld8u_tl(s->T0, cpu_env, vector_elem_offset(&decode->op[1], ot, val)); + gen_helper_divb_AL(tcg_env, s->T0); break; case MO_16: - tcg_gen_ld16u_tl(s->T0, cpu_env, vector_elem_offset(&decode->op[1], ot, val)); + gen_helper_divw_AX(tcg_env, s->T0); break; + default: case MO_32: -#ifdef TARGET_X86_64 - tcg_gen_ld32u_tl(s->T0, cpu_env, vector_elem_offset(&decode->op[1], ot, val)); + gen_helper_divl_EAX(tcg_env, s->T0); break; +#ifdef TARGET_X86_64 case MO_64: -#endif - tcg_gen_ld_tl(s->T0, cpu_env, vector_elem_offset(&decode->op[1], ot, val)); + gen_helper_divq_EAX(tcg_env, s->T0); break; - default: - abort(); +#endif } } -static void gen_PEXTRB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_EMMS(DisasContext *s, X86DecodedInsn *decode) { - gen_pextr(s, env, decode, MO_8); + gen_helper_emms(tcg_env); } -static void gen_PEXTRW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_ENTER(DisasContext *s, X86DecodedInsn *decode) { - gen_pextr(s, env, decode, MO_16); + gen_enter(s, decode->op[1].imm, decode->op[2].imm); } -static void gen_PEXTR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_EXTRQ_i(DisasContext *s, X86DecodedInsn *decode) { - MemOp ot = decode->op[0].ot; - gen_pextr(s, env, decode, ot); + TCGv_i32 length = tcg_constant_i32(decode->immediate & 63); + TCGv_i32 index = tcg_constant_i32((decode->immediate >> 8) & 63); + + gen_helper_extrq_i(tcg_env, OP_PTR0, index, length); } -static inline void gen_pinsr(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, MemOp ot) +static void gen_EXTRQ_r(DisasContext *s, X86DecodedInsn *decode) { - int vec_len = vector_len(s, decode); - int mask = (vec_len >> ot) - 1; - int val = decode->immediate & mask; + gen_helper_extrq_r(tcg_env, OP_PTR0, OP_PTR2); +} - if (decode->op[1].offset != decode->op[0].offset) { - assert(vec_len == 16); - gen_store_sse(s, decode, decode->op[1].offset); +static void gen_FXRSTOR(DisasContext *s, X86DecodedInsn *decode) +{ + if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { + gen_NM_exception(s); + } else { + gen_helper_fxrstor(tcg_env, s->A0); } +} - switch (ot) { +static void gen_FXSAVE(DisasContext *s, X86DecodedInsn *decode) +{ + 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_HLT(DisasContext *s, X86DecodedInsn *decode) +{ +#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: - tcg_gen_st8_tl(s->T1, cpu_env, vector_elem_offset(&decode->op[0], ot, val)); + 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: - tcg_gen_st16_tl(s->T1, cpu_env, vector_elem_offset(&decode->op[0], ot, val)); + /* 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 - tcg_gen_st32_tl(s->T1, cpu_env, vector_elem_offset(&decode->op[0], ot, val)); + 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 - tcg_gen_st_tl(s->T1, cpu_env, vector_elem_offset(&decode->op[0], ot, val)); + 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: - abort(); + 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_PINSRB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_IMUL(DisasContext *s, X86DecodedInsn *decode) { - gen_pinsr(s, env, decode, MO_8); + 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_PINSRW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_IN(DisasContext *s, X86DecodedInsn *decode) { - gen_pinsr(s, env, decode, MO_16); + 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; + } + 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_PINSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_INC(DisasContext *s, X86DecodedInsn *decode) { - gen_pinsr(s, env, decode, decode->op[2].ot); + 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_INCB + ot); } -static void gen_pmovmskb_i64(TCGv_i64 d, TCGv_i64 s) +static void gen_INS(DisasContext *s, X86DecodedInsn *decode) { - TCGv_i64 t = tcg_temp_new_i64(); + MemOp ot = decode->op[1].ot; + TCGv_i32 port = tcg_temp_new_i32(); - tcg_gen_andi_i64(d, s, 0x8080808080808080ull); + 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; + } - /* - * After each shift+or pair: - * 0: a.......b.......c.......d.......e.......f.......g.......h....... - * 7: ab......bc......cd......de......ef......fg......gh......h....... - * 14: abcd....bcde....cdef....defg....efgh....fgh.....gh......h....... - * 28: abcdefghbcdefgh.cdefgh..defgh...efgh....fgh.....gh......h....... - * The result is left in the high bits of the word. - */ - tcg_gen_shli_i64(t, d, 7); - tcg_gen_or_i64(d, d, t); - tcg_gen_shli_i64(t, d, 14); - tcg_gen_or_i64(d, d, t); - tcg_gen_shli_i64(t, d, 28); - tcg_gen_or_i64(d, d, t); + translator_io_start(&s->base); + gen_repz(s, ot, gen_ins); } -static void gen_pmovmskb_vec(unsigned vece, TCGv_vec d, TCGv_vec s) +static void gen_INSERTQ_i(DisasContext *s, X86DecodedInsn *decode) { - TCGv_vec t = tcg_temp_new_vec_matching(d); - TCGv_vec m = tcg_constant_vec_matching(d, MO_8, 0x80); + TCGv_i32 length = tcg_constant_i32(decode->immediate & 63); + TCGv_i32 index = tcg_constant_i32((decode->immediate >> 8) & 63); - /* See above */ - tcg_gen_and_vec(vece, d, s, m); - tcg_gen_shli_vec(vece, t, d, 7); - tcg_gen_or_vec(vece, d, d, t); - tcg_gen_shli_vec(vece, t, d, 14); - tcg_gen_or_vec(vece, d, d, t); - tcg_gen_shli_vec(vece, t, d, 28); - tcg_gen_or_vec(vece, d, d, t); + gen_helper_insertq_i(tcg_env, OP_PTR0, OP_PTR1, index, length); } -#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 +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) +{ + TCGLabel *taken = gen_new_label(); + + gen_bnd_jmp(s); + gen_jcc(s, decode->b & 0xf, taken); + gen_conditional_jump_labels(s, decode->immediate, NULL, taken); +} + +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) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(tmp, s->T0); + gen_helper_ldmxcsr(tcg_env, tmp); +} + +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, false); +} + +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; + gen_repz(s, ot, gen_lods); +} + +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_jcc(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_jcc(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; + + /* M operand type does not load/store */ + if (decode->e.op0 == X86_TYPE_M) { + tcg_gen_qemu_st_tl(s->T0, s->A0, s->mem_index, ot | MO_BE); + } else { + tcg_gen_qemu_ld_tl(s->T0, s->A0, s->mem_index, ot | MO_BE); + } +} + +static void gen_MOVD_from(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + + switch (ot) { + case MO_32: +#ifdef TARGET_X86_64 + tcg_gen_ld32u_tl(s->T0, tcg_env, decode->op[2].offset); + break; + case MO_64: #endif + tcg_gen_ld_tl(s->T0, tcg_env, decode->op[2].offset); + break; + default: + abort(); + } +} -static void gen_PMOVMSKB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MOVD_to(DisasContext *s, X86DecodedInsn *decode) { - static const TCGOpcode vecop_list[] = { INDEX_op_shli_vec, 0 }; - static const GVecGen2 g = { - .fni8 = gen_pmovmskb_i64, - .fniv = gen_pmovmskb_vec, - .opt_opc = vecop_list, - .vece = MO_64, - .prefer_i64 = TCG_TARGET_REG_BITS == 64 - }; MemOp ot = decode->op[2].ot; int vec_len = vector_len(s, decode); - TCGv t = tcg_temp_new(); + int lo_ofs = vector_elem_offset(&decode->op[0], ot, 0); + + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + + switch (ot) { + case MO_32: +#ifdef TARGET_X86_64 + tcg_gen_st32_tl(s->T1, tcg_env, lo_ofs); + break; + case MO_64: +#endif + tcg_gen_st_tl(s->T1, tcg_env, lo_ofs); + break; + default: + g_assert_not_reached(); + } +} + +static void gen_MOVDQ(DisasContext *s, X86DecodedInsn *decode) +{ + gen_store_sse(s, decode, decode->op[2].offset); +} + +static void gen_MOVMSK(DisasContext *s, X86DecodedInsn *decode) +{ + typeof(gen_helper_movmskps_ymm) *ps, *pd, *fn; + TCGv_i32 tmp = tcg_temp_new_i32(); + + 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(tmp, tcg_env, OP_PTR2); + tcg_gen_extu_i32_tl(s->T0, tmp); +} + +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, tcg_env, decode->op[2].offset); + if (decode->op[0].has_ea) { + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEUQ); + } else { + /* + * tcg_gen_gvec_dup_i64(MO_64, op0.offset, 8, vec_len, s->tmp1_64) would + * seem to work, but it does not on big-endian platforms; the cleared parts + * are always at higher addresses, but cross-endian emulation inverts the + * byte order so that the cleared parts need to be at *lower* addresses. + * Because oprsz is 8, we see this here even for SSE; but more in general, + * it disqualifies using oprsz < maxsz to emulate VEX128. + */ + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + tcg_gen_st_i64(s->tmp1_i64, tcg_env, lo_ofs); + } +} + +static void gen_MOVq_dq(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_enter_mmx(tcg_env); + /* Otherwise the same as any other movq. */ + return gen_MOVQ(s, decode); +} + +static void gen_MOVS(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + gen_repz(s, ot, gen_movs); +} + +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) { + case MO_32: +#ifdef TARGET_X86_64 + { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(t0, s->T0); + tcg_gen_trunc_tl_i32(t1, s->T1); + tcg_gen_mulu2_i32(t0, t1, t0, t1); + tcg_gen_extu_i32_tl(cpu_regs[s->vex_v], t0); + tcg_gen_extu_i32_tl(s->T0, t1); + break; + } + + case MO_64: +#endif + tcg_gen_mulu2_tl(cpu_regs[s->vex_v], s->T0, s->T0, s->T1); + break; + + default: + g_assert_not_reached(); + } +} + +static void gen_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); + gen_repz(s, ot, gen_outs); +} + +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(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); + } else if (!s->vex_l) { + gen_helper_palignr_xmm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); + } else { + gen_helper_palignr_ymm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); + } +} + +static void gen_PANDN(DisasContext *s, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + /* Careful, operand order is reversed! */ + tcg_gen_gvec_andc(MO_64, + decode->op[0].offset, decode->op[2].offset, + decode->op[1].offset, vec_len, vec_len); +} + +static void gen_PAUSE(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_next(s); + gen_helper_pause(tcg_env); + s->base.is_jmp = DISAS_NORETURN; +} + +static void gen_PCMPESTRI(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + 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, X86DecodedInsn *decode) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + gen_helper_pcmpistri_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); + assume_cc_op(s, CC_OP_EFLAGS); +} + +static void gen_PCMPISTRM(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + 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, X86DecodedInsn *decode) +{ + gen_helper_pdep(s->T0, s->T0, s->T1); +} + +static void gen_PEXT(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_pext(s->T0, s->T0, s->T1); +} + +static inline void gen_pextr(DisasContext *s, X86DecodedInsn *decode, MemOp ot) +{ + int vec_len = vector_len(s, decode); + int mask = (vec_len >> ot) - 1; + int val = decode->immediate & mask; + + switch (ot) { + case MO_8: + tcg_gen_ld8u_tl(s->T0, tcg_env, vector_elem_offset(&decode->op[1], ot, val)); + break; + case MO_16: + tcg_gen_ld16u_tl(s->T0, tcg_env, vector_elem_offset(&decode->op[1], ot, val)); + break; + case MO_32: +#ifdef TARGET_X86_64 + tcg_gen_ld32u_tl(s->T0, tcg_env, vector_elem_offset(&decode->op[1], ot, val)); + break; + case MO_64: +#endif + tcg_gen_ld_tl(s->T0, tcg_env, vector_elem_offset(&decode->op[1], ot, val)); + break; + default: + abort(); + } +} + +static void gen_PEXTRB(DisasContext *s, X86DecodedInsn *decode) +{ + gen_pextr(s, decode, MO_8); +} + +static void gen_PEXTRW(DisasContext *s, X86DecodedInsn *decode) +{ + gen_pextr(s, decode, MO_16); +} + +static void gen_PEXTR(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + gen_pextr(s, decode, 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; + int val = decode->immediate & mask; + + if (decode->op[1].offset != decode->op[0].offset) { + assert(vec_len == 16); + gen_store_sse(s, decode, decode->op[1].offset); + } + + switch (ot) { + case MO_8: + tcg_gen_st8_tl(s->T1, tcg_env, vector_elem_offset(&decode->op[0], ot, val)); + break; + case MO_16: + tcg_gen_st16_tl(s->T1, tcg_env, vector_elem_offset(&decode->op[0], ot, val)); + break; + case MO_32: +#ifdef TARGET_X86_64 + tcg_gen_st32_tl(s->T1, tcg_env, vector_elem_offset(&decode->op[0], ot, val)); + break; + case MO_64: +#endif + tcg_gen_st_tl(s->T1, tcg_env, vector_elem_offset(&decode->op[0], ot, val)); + break; + default: + abort(); + } +} + +static void gen_PINSRB(DisasContext *s, X86DecodedInsn *decode) +{ + gen_pinsr(s, decode, MO_8); +} + +static void gen_PINSRW(DisasContext *s, X86DecodedInsn *decode) +{ + gen_pinsr(s, decode, MO_16); +} + +static void gen_PINSR(DisasContext *s, X86DecodedInsn *decode) +{ + gen_pinsr(s, decode, decode->op[2].ot); +} + +static void gen_pmovmskb_i64(TCGv_i64 d, TCGv_i64 s) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_andi_i64(d, s, 0x8080808080808080ull); + + /* + * After each shift+or pair: + * 0: a.......b.......c.......d.......e.......f.......g.......h....... + * 7: ab......bc......cd......de......ef......fg......gh......h....... + * 14: abcd....bcde....cdef....defg....efgh....fgh.....gh......h....... + * 28: abcdefghbcdefgh.cdefgh..defgh...efgh....fgh.....gh......h....... + * The result is left in the high bits of the word. + */ + tcg_gen_shli_i64(t, d, 7); + tcg_gen_or_i64(d, d, t); + tcg_gen_shli_i64(t, d, 14); + tcg_gen_or_i64(d, d, t); + tcg_gen_shli_i64(t, d, 28); + tcg_gen_or_i64(d, d, t); +} + +static void gen_pmovmskb_vec(unsigned vece, TCGv_vec d, TCGv_vec s) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + TCGv_vec m = tcg_constant_vec_matching(d, MO_8, 0x80); + + /* See above */ + tcg_gen_and_vec(vece, d, s, m); + tcg_gen_shli_vec(vece, t, d, 7); + tcg_gen_or_vec(vece, d, d, t); + tcg_gen_shli_vec(vece, t, d, 14); + tcg_gen_or_vec(vece, d, d, t); + tcg_gen_shli_vec(vece, t, d, 28); + tcg_gen_or_vec(vece, d, d, t); +} + +static void gen_PMOVMSKB(DisasContext *s, X86DecodedInsn *decode) +{ + static const TCGOpcode vecop_list[] = { INDEX_op_shli_vec, 0 }; + static const GVecGen2 g = { + .fni8 = gen_pmovmskb_i64, + .fniv = gen_pmovmskb_vec, + .opt_opc = vecop_list, + .vece = MO_64, + .prefer_i64 = TCG_TARGET_REG_BITS == 64 + }; + MemOp ot = decode->op[2].ot; + int vec_len = vector_len(s, decode); + TCGv t = tcg_temp_new(); + + tcg_gen_gvec_2(offsetof(CPUX86State, xmm_t0) + xmm_offset(ot), decode->op[2].offset, + vec_len, vec_len, &g); + tcg_gen_ld8u_tl(s->T0, tcg_env, offsetof(CPUX86State, xmm_t0.ZMM_B(vec_len - 1))); + while (vec_len > 8) { + vec_len -= 8; + if (tcg_op_supported(INDEX_op_extract2, TCG_TYPE_TL, 0)) { + /* + * Load the next byte of the result into the high byte of T. + * TCG does a similar expansion of deposit to shl+extract2; by + * loading the whole word, the shift left is avoided. + */ +#ifdef TARGET_X86_64 + tcg_gen_ld_tl(t, tcg_env, offsetof(CPUX86State, xmm_t0.ZMM_Q((vec_len - 1) / 8))); +#else + tcg_gen_ld_tl(t, tcg_env, offsetof(CPUX86State, xmm_t0.ZMM_L((vec_len - 1) / 4))); +#endif + + tcg_gen_extract2_tl(s->T0, t, s->T0, TARGET_LONG_BITS - 8); + } else { + /* + * The _previous_ value is deposited into bits 8 and higher of t. Because + * those bits are known to be zero after ld8u, this becomes a shift+or + * if deposit is not available. + */ + tcg_gen_ld8u_tl(t, tcg_env, offsetof(CPUX86State, xmm_t0.ZMM_B(vec_len - 1))); + tcg_gen_deposit_tl(s->T0, t, s->T0, 8, TARGET_LONG_BITS - 8); + } + } +} + +static void gen_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, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 16) { + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + } else { + tcg_gen_gvec_shri(MO_16, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); + } +} + +static void gen_PSLLW_i(DisasContext *s, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 16) { + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + } else { + tcg_gen_gvec_shli(MO_16, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); + } +} + +static void gen_PSRAW_i(DisasContext *s, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 16) { + decode->immediate = 15; + } + tcg_gen_gvec_sari(MO_16, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); +} + +static void gen_PSRLD_i(DisasContext *s, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 32) { + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + } else { + tcg_gen_gvec_shri(MO_32, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); + } +} + +static void gen_PSLLD_i(DisasContext *s, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 32) { + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + } else { + tcg_gen_gvec_shli(MO_32, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); + } +} + +static void gen_PSRAD_i(DisasContext *s, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 32) { + decode->immediate = 31; + } + tcg_gen_gvec_sari(MO_32, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); +} + +static void gen_PSRLQ_i(DisasContext *s, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 64) { + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + } else { + tcg_gen_gvec_shri(MO_64, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); + } +} + +static void gen_PSLLQ_i(DisasContext *s, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + + if (decode->immediate >= 64) { + tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + } else { + tcg_gen_gvec_shli(MO_64, + decode->op[0].offset, decode->op[1].offset, + decode->immediate, vec_len, vec_len); + } +} + +static TCGv_ptr make_imm8u_xmm_vec(uint8_t imm, int vec_len) +{ + MemOp ot = vec_len == 16 ? MO_128 : MO_256; + TCGv_i32 imm_v = tcg_constant8u_i32(imm); + TCGv_ptr ptr = tcg_temp_new_ptr(); + + tcg_gen_gvec_dup_imm(MO_64, offsetof(CPUX86State, xmm_t0) + xmm_offset(ot), + vec_len, vec_len, 0); + + tcg_gen_addi_ptr(ptr, tcg_env, offsetof(CPUX86State, xmm_t0)); + tcg_gen_st_i32(imm_v, tcg_env, offsetof(CPUX86State, xmm_t0.ZMM_L(0))); + return ptr; +} + +static void gen_PSRLDQ_i(DisasContext *s, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + TCGv_ptr imm_vec = make_imm8u_xmm_vec(decode->immediate, vec_len); + + if (s->vex_l) { + gen_helper_psrldq_ymm(tcg_env, OP_PTR0, OP_PTR1, imm_vec); + } else { + gen_helper_psrldq_xmm(tcg_env, OP_PTR0, OP_PTR1, imm_vec); + } +} + +static void gen_PSLLDQ_i(DisasContext *s, X86DecodedInsn *decode) +{ + int vec_len = vector_len(s, decode); + TCGv_ptr imm_vec = make_imm8u_xmm_vec(decode->immediate, vec_len); + + if (s->vex_l) { + gen_helper_pslldq_ymm(tcg_env, OP_PTR0, OP_PTR1, imm_vec); + } else { + gen_helper_pslldq_xmm(tcg_env, OP_PTR0, OP_PTR1, imm_vec); + } +} + +static void gen_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 mask = (ot <= MO_32 ? 0x1f : 0x3f); + + *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); + } +} + +/* + * 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_op_deposit_valid(TCG_TYPE_TL, 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_op_deposit_valid(TCG_TYPE_TL, 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 + { + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(tmp, s->T0); + tcg_gen_rotri_i32(tmp, tmp, b); + tcg_gen_extu_i32_tl(s->T0, tmp); + 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); + tcg_gen_sar_tl(s->T0, s->T0, s->T1); +} + +static void gen_SUB(DisasContext *s, X86DecodedInsn *decode); +static void gen_SBB(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv c_in; + + /* + * Try to avoid CC_OP_SBB by transforming as follows: + * CC_SBB: src1 = dst + c_in, src2 = 0, src3 = c_in + * CC_SUB: src1 = dst + c_in, src2 = c_in (no src3) + * + * In general src2 vs. src3 matters when computing AF and OF, but not here: + * - AF is bit 4 of dst^src1^src2, which is bit 4 of dst^src1 in both cases + * - OF is a function of the two MSBs, and in both cases they are zero for src2 + */ + if (decode->e.op2 == X86_TYPE_I && decode->immediate == 0) { + gen_compute_eflags_c(s, s->T1); + gen_SUB(s, decode); + return; + } - 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))); - while (vec_len > 8) { - vec_len -= 8; - if (TCG_TARGET_HAS_extract2_tl) { - /* - * Load the next byte of the result into the high byte of T. - * TCG does a similar expansion of deposit to shl+extract2; by - * loading the whole word, the shift left is avoided. - */ -#ifdef TARGET_X86_64 - tcg_gen_ld_tl(t, cpu_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))); -#endif + c_in = tcg_temp_new(); + gen_compute_eflags_c(s, c_in); - tcg_gen_extract2_tl(s->T0, t, s->T0, TARGET_LONG_BITS - 8); + /* + * Here the change is as follows: + * CC_SBB: src1 = T0, src2 = T0, src3 = c_in + * CC_SUB: src1 = 0, src2 = c_in (no src3) + * + * The difference also does not matter: + * - AF is bit 4 of dst^src1^src2, but bit 4 of src1^src2 is zero in both cases + * therefore AF comes straight from dst (in fact it is c_in) + * - for OF, src1 and src2 have the same sign in both cases, meaning there + * can be no overflow + */ + if (decode->e.op2 != X86_TYPE_I && !decode->op[0].has_ea && decode->op[0].n == decode->op[2].n) { + if (s->cc_op == CC_OP_DYNAMIC) { + tcg_gen_neg_tl(s->T0, c_in); } else { /* - * The _previous_ value is deposited into bits 8 and higher of t. Because - * those bits are known to be zero after ld8u, this becomes a shift+or - * if deposit is not available. + * Do not negate c_in because it will often be dead and only the + * instruction generated by negsetcond will survive. */ - tcg_gen_ld8u_tl(t, cpu_env, offsetof(CPUX86State, xmm_t0.ZMM_B(vec_len - 1))); - tcg_gen_deposit_tl(s->T0, t, s->T0, 8, TARGET_LONG_BITS - 8); + gen_neg_setcc(s, JCC_B << 1, s->T0); } + tcg_gen_movi_tl(s->cc_srcT, 0); + decode->cc_src = c_in; + decode->cc_dst = s->T0; + decode->cc_op = CC_OP_SUBB + ot; + return; + } + + 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 { + 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_PSHUFW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SCAS(DisasContext *s, X86DecodedInsn *decode) { - TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); - gen_helper_pshufw_mmx(OP_PTR0, OP_PTR1, imm); + MemOp ot = decode->op[2].ot; + gen_repz_nz(s, ot, gen_scas); } -static void gen_PSRLW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SETcc(DisasContext *s, X86DecodedInsn *decode) { - int vec_len = vector_len(s, decode); + gen_setcc(s, decode->b & 0xf, s->T0); +} - if (decode->immediate >= 16) { - tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); - } else { - tcg_gen_gvec_shri(MO_16, - decode->op[0].offset, decode->op[1].offset, - decode->immediate, vec_len, vec_len); - } +static void gen_SFENCE(DisasContext *s, X86DecodedInsn *decode) +{ + tcg_gen_mb(TCG_MO_ST_ST | TCG_BAR_SC); } -static void gen_PSLLW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHA1NEXTE(DisasContext *s, X86DecodedInsn *decode) { - int vec_len = vector_len(s, decode); + gen_helper_sha1nexte(OP_PTR0, OP_PTR1, OP_PTR2); +} - if (decode->immediate >= 16) { - tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); - } else { - tcg_gen_gvec_shli(MO_16, - decode->op[0].offset, decode->op[1].offset, - decode->immediate, vec_len, vec_len); - } +static void gen_SHA1MSG1(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_sha1msg1(OP_PTR0, OP_PTR1, OP_PTR2); } -static void gen_PSRAW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHA1MSG2(DisasContext *s, X86DecodedInsn *decode) { - int vec_len = vector_len(s, decode); + gen_helper_sha1msg2(OP_PTR0, OP_PTR1, OP_PTR2); +} - if (decode->immediate >= 16) { - decode->immediate = 15; +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; } - tcg_gen_gvec_sari(MO_16, - decode->op[0].offset, decode->op[1].offset, - decode->immediate, vec_len, vec_len); } -static void gen_PSRLD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHA256MSG1(DisasContext *s, X86DecodedInsn *decode) { - int vec_len = vector_len(s, decode); + gen_helper_sha256msg1(OP_PTR0, OP_PTR1, OP_PTR2); +} - if (decode->immediate >= 32) { - tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); +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 { - tcg_gen_gvec_shri(MO_32, - decode->op[0].offset, decode->op[1].offset, - decode->immediate, vec_len, vec_len); + decode->cc_op = CC_OP_SHLB + ot; } } -static void gen_PSLLD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHLD(DisasContext *s, X86DecodedInsn *decode) { - int vec_len = vector_len(s, 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 (decode->immediate >= 32) { - tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + if (!count) { + return; + } + + decode->cc_dst = s->T0; + decode->cc_src = gen_shiftd_rm_T1(s, ot, false, count); + if (can_be_zero) { + gen_shift_dynamic_flags(s, decode, count, CC_OP_SHLB + ot); } else { - tcg_gen_gvec_shli(MO_32, - decode->op[0].offset, decode->op[1].offset, - decode->immediate, vec_len, vec_len); + decode->cc_op = CC_OP_SHLB + ot; } } -static void gen_PSRAD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHLX(DisasContext *s, X86DecodedInsn *decode) { - int vec_len = vector_len(s, decode); + MemOp ot = decode->op[0].ot; + int mask; - if (decode->immediate >= 32) { - decode->immediate = 31; - } - tcg_gen_gvec_sari(MO_32, - decode->op[0].offset, decode->op[1].offset, - decode->immediate, vec_len, vec_len); + mask = ot == MO_64 ? 63 : 31; + tcg_gen_andi_tl(s->T1, s->T1, mask); + tcg_gen_shl_tl(s->T0, s->T0, s->T1); } -static void gen_PSRLQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHR(DisasContext *s, X86DecodedInsn *decode) { - int vec_len = vector_len(s, decode); + bool can_be_zero; + TCGv count; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); - if (decode->immediate >= 64) { - tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + 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 { - tcg_gen_gvec_shri(MO_64, - decode->op[0].offset, decode->op[1].offset, - decode->immediate, vec_len, vec_len); + decode->cc_op = CC_OP_SARB + ot; } } -static void gen_PSLLQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHRD(DisasContext *s, X86DecodedInsn *decode) { - int vec_len = vector_len(s, 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 (decode->immediate >= 64) { - tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); + if (!count) { + return; + } + + decode->cc_dst = s->T0; + decode->cc_src = gen_shiftd_rm_T1(s, ot, true, count); + if (can_be_zero) { + gen_shift_dynamic_flags(s, decode, count, CC_OP_SARB + ot); } else { - tcg_gen_gvec_shli(MO_64, - decode->op[0].offset, decode->op[1].offset, - decode->immediate, vec_len, vec_len); + decode->cc_op = CC_OP_SARB + ot; } } -static TCGv_ptr make_imm8u_xmm_vec(uint8_t imm, int vec_len) +static void gen_SHRX(DisasContext *s, X86DecodedInsn *decode) { - MemOp ot = vec_len == 16 ? MO_128 : MO_256; - TCGv_i32 imm_v = tcg_constant8u_i32(imm); - TCGv_ptr ptr = tcg_temp_new_ptr(); + MemOp ot = decode->op[0].ot; + int mask; - tcg_gen_gvec_dup_imm(MO_64, offsetof(CPUX86State, xmm_t0) + xmm_offset(ot), - vec_len, vec_len, 0); + mask = ot == MO_64 ? 63 : 31; + tcg_gen_andi_tl(s->T1, s->T1, mask); + tcg_gen_shr_tl(s->T0, s->T0, s->T1); +} - 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))); - return ptr; +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_PSRLDQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_STD(DisasContext *s, X86DecodedInsn *decode) { - int vec_len = vector_len(s, decode); - TCGv_ptr imm_vec = make_imm8u_xmm_vec(decode->immediate, vec_len); + tcg_gen_st_i32(tcg_constant_i32(-1), tcg_env, offsetof(CPUX86State, df)); +} - if (s->vex_l) { - gen_helper_psrldq_ymm(cpu_env, OP_PTR0, OP_PTR1, imm_vec); - } else { - gen_helper_psrldq_xmm(cpu_env, OP_PTR0, OP_PTR1, imm_vec); - } +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_PSLLDQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VAESKEYGEN(DisasContext *s, X86DecodedInsn *decode) { - int vec_len = vector_len(s, decode); - TCGv_ptr imm_vec = make_imm8u_xmm_vec(decode->immediate, vec_len); + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + assert(!s->vex_l); + gen_helper_aeskeygenassist_xmm(tcg_env, OP_PTR0, OP_PTR1, imm); +} - if (s->vex_l) { - gen_helper_pslldq_ymm(cpu_env, OP_PTR0, OP_PTR1, imm_vec); - } else { - gen_helper_pslldq_xmm(cpu_env, OP_PTR0, OP_PTR1, imm_vec); - } +static void gen_STMXCSR(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_update_mxcsr(tcg_env); + tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, mxcsr)); } -static void gen_RORX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_STOS(DisasContext *s, X86DecodedInsn *decode) { - MemOp ot = decode->op[0].ot; - int b = decode->immediate; + MemOp ot = decode->op[1].ot; + gen_repz(s, ot, gen_stos); +} - if (ot == MO_64) { - tcg_gen_rotri_tl(s->T0, s->T0, b & 63); +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_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); + 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_SARX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SYSCALL(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); + 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); } - tcg_gen_sar_tl(s->T0, s->T0, s->T1); + + /* + * 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_SHLX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SYSENTER(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); - tcg_gen_shl_tl(s->T0, s->T0, s->T1); + gen_helper_sysenter(tcg_env); + s->base.is_jmp = DISAS_EOB_ONLY; } -static void gen_SHRX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SYSEXIT(DisasContext *s, X86DecodedInsn *decode) { - MemOp ot = decode->op[0].ot; - int mask; + gen_helper_sysexit(tcg_env, tcg_constant_i32(s->dflag - 1)); + s->base.is_jmp = DISAS_EOB_ONLY; +} - 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); +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); } - tcg_gen_shr_tl(s->T0, s->T0, s->T1); + + /* + * 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_VAESKEYGEN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_TZCNT(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); + 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_STMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_UD(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_illegal_opcode(s); } -static void gen_VAESIMC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +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); } /* @@ -1895,7 +4259,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 = @@ -1903,38 +4267,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_VCVTPD2PS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTPD2PS(DisasContext *s, X86DecodedInsn *decode) { if (s->vex_l) { - gen_helper_cvtpd2ps_ymm(cpu_env, OP_PTR0, OP_PTR2); + gen_helper_cvtpd2ps_ymm(tcg_env, OP_PTR0, OP_PTR2); } else { - gen_helper_cvtpd2ps_xmm(cpu_env, OP_PTR0, OP_PTR2); + gen_helper_cvtpd2ps_xmm(tcg_env, OP_PTR0, OP_PTR2); } } -static void gen_VCVTPS2PD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTPS2PD(DisasContext *s, X86DecodedInsn *decode) { if (s->vex_l) { - gen_helper_cvtps2pd_ymm(cpu_env, OP_PTR0, OP_PTR2); + gen_helper_cvtps2pd_ymm(tcg_env, OP_PTR0, OP_PTR2); } else { - gen_helper_cvtps2pd_xmm(cpu_env, OP_PTR0, OP_PTR2); + gen_helper_cvtps2pd_xmm(tcg_env, OP_PTR0, OP_PTR2); } } -static void gen_VCVTPS2PH(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTPS2PH(DisasContext *s, X86DecodedInsn *decode) { - gen_unary_imm_fp_sse(s, env, decode, + gen_unary_imm_fp_sse(s, decode, gen_helper_cvtps2ph_xmm, gen_helper_cvtps2ph_ymm); /* @@ -1946,17 +4310,17 @@ static void gen_VCVTPS2PH(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec } } -static void gen_VCVTSD2SS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTSD2SS(DisasContext *s, X86DecodedInsn *decode) { - gen_helper_cvtsd2ss(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2); + gen_helper_cvtsd2ss(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); } -static void gen_VCVTSS2SD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTSS2SD(DisasContext *s, X86DecodedInsn *decode) { - gen_helper_cvtss2sd(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2); + gen_helper_cvtss2sd(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); } -static void gen_VCVTSI2Sx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTSI2Sx(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); TCGv_i32 in; @@ -1967,26 +4331,26 @@ 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; } - in = s->tmp2_i32; + in = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(in, s->T1); #else in = s->T1; #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) { @@ -1996,21 +4360,21 @@ 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; } - out = s->tmp2_i32; + out = tcg_temp_new_i32(); #else 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); @@ -2024,21 +4388,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); @@ -2050,12 +4414,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, TCGv_i32 tmp) { int val = decode->immediate; int dest_word = (val >> 4) & 3; @@ -2072,7 +4436,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(tmp, tcg_env, vector_elem_offset(&decode->op[0], MO_32, dest_word)); } @@ -2081,28 +4445,32 @@ 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, + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_ld_i32(tmp, tcg_env, vector_elem_offset(&decode->op[2], MO_32, (val >> 6) & 3)); - gen_vinsertps(s, env, decode); + gen_vinsertps(s, decode, tmp); } -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); + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_qemu_ld_i32(tmp, s->A0, s->mem_index, MO_LEUL); + gen_vinsertps(s, decode, tmp); } -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, @@ -2113,69 +4481,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))); } } @@ -2184,16 +4552,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); @@ -2202,13 +4570,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); @@ -2217,73 +4585,77 @@ 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); + TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_ld_i32(s->tmp2_i32, OP_PTR2, offsetof(ZMMReg, ZMM_L(0))); + tcg_gen_ld_i32(tmp, OP_PTR2, offsetof(ZMMReg, ZMM_L(0))); tcg_gen_gvec_mov(MO_64, decode->op[0].offset, decode->op[1].offset, vec_len, vec_len); - tcg_gen_st_i32(s->tmp2_i32, OP_PTR0, offsetof(ZMMReg, ZMM_L(0))); + tcg_gen_st_i32(tmp, 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); + TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); + tcg_gen_qemu_ld_i32(tmp, s->A0, s->mem_index, MO_LEUL); tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); - tcg_gen_st_i32(s->tmp2_i32, OP_PTR0, offsetof(ZMMReg, ZMM_L(0))); + tcg_gen_st_i32(tmp, 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); + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_ld_i32(tmp, OP_PTR2, offsetof(ZMMReg, ZMM_L(0))); + tcg_gen_qemu_st_i32(tmp, 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; @@ -2293,24 +4665,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))); } -static void gen_VZEROUPPER(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VZEROUPPER(DisasContext *s, X86DecodedInsn *decode) { int i; @@ -2319,3 +4691,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 7c3c8dc7fe8..6fb8036d988 100644 --- a/target/i386/tcg/excp_helper.c +++ b/target/i386/tcg/excp_helper.c @@ -19,16 +19,15 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "qemu/log.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "exec/helper-proto.h" #include "helper-tcg.h" 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 +111,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 +139,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 4430d3d380c..b3b23823fda 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -21,11 +21,13 @@ #include #include "cpu.h" #include "tcg-cpu.h" -#include "exec/cpu_ldst.h" +#include "exec/cputlb.h" +#include "accel/tcg/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) @@ -83,23 +85,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 */ @@ -134,16 +135,79 @@ static void fpu_set_exception(CPUX86State *env, int mask) } } -static inline uint8_t save_exception_flags(CPUX86State *env) +void cpu_init_fp_statuses(CPUX86State *env) { - uint8_t old_flags = get_float_exception_flags(&env->fp_status); + /* + * 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); + /* + * Only SSE has multiply-add instructions. In the SDM Section 14.5.2 + * "Fused-Multiply-ADD (FMA) Numeric Behavior" the NaN handling is + * specified -- for 0 * inf + NaN the input NaN is selected, and if + * there are multiple input NaNs they are selected in the order a, b, c. + * We also do not raise Invalid for the 0 * inf + (Q)NaN case. + */ + set_float_infzeronan_rule(float_infzeronan_dnan_never | + float_infzeronan_suppress_invalid, + &env->sse_status); + set_float_3nan_prop_rule(float_3nan_prop_abc, &env->sse_status); + /* Default NaN: sign bit set, most significant frac bit set */ + set_float_default_nan_pattern(0b11000000, &env->fp_status); + set_float_default_nan_pattern(0b11000000, &env->mmx_status); + set_float_default_nan_pattern(0b11000000, &env->sse_status); + /* + * x86 does flush-to-zero detection after rounding (the SDM + * section 10.2.3.3 on the FTZ bit of MXCSR says that we flush + * when we detect underflow, which x86 does after rounding). + */ + set_float_ftz_detection(float_ftz_after_rounding, &env->fp_status); + set_float_ftz_detection(float_ftz_after_rounding, &env->mmx_status); + set_float_ftz_detection(float_ftz_after_rounding, &env->sse_status); +} + +static inline int save_exception_flags(CPUX86State *env) +{ + int old_flags = get_float_exception_flags(&env->fp_status); set_float_exception_flags(0, &env->fp_status); return old_flags; } -static void merge_exception_flags(CPUX86State *env, uint8_t old_flags) +static void merge_exception_flags(CPUX86State *env, int old_flags) { - uint8_t new_flags = get_float_exception_flags(&env->fp_status); + int new_flags = get_float_exception_flags(&env->fp_status); float_raise(old_flags, &env->fp_status); fpu_set_exception(env, ((new_flags & float_flag_invalid ? FPUS_IE : 0) | @@ -151,12 +215,12 @@ static void merge_exception_flags(CPUX86State *env, uint8_t old_flags) (new_flags & float_flag_overflow ? FPUS_OE : 0) | (new_flags & float_flag_underflow ? FPUS_UE : 0) | (new_flags & float_flag_inexact ? FPUS_PE : 0) | - (new_flags & float_flag_input_denormal ? FPUS_DE : 0))); + (new_flags & float_flag_input_denormal_used ? FPUS_DE : 0))); } static inline floatx80 helper_fdiv(CPUX86State *env, floatx80 a, floatx80 b) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); floatx80 ret = floatx80_div(a, b, &env->fp_status); merge_exception_flags(env, old_flags); return ret; @@ -176,7 +240,7 @@ static void fpu_raise_exception(CPUX86State *env, uintptr_t retaddr) void helper_flds_FT0(CPUX86State *env, uint32_t val) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); union { float32 f; uint32_t i; @@ -189,7 +253,7 @@ void helper_flds_FT0(CPUX86State *env, uint32_t val) void helper_fldl_FT0(CPUX86State *env, uint64_t val) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); union { float64 f; uint64_t i; @@ -207,7 +271,7 @@ void helper_fildl_FT0(CPUX86State *env, int32_t val) void helper_flds_ST0(CPUX86State *env, uint32_t val) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int new_fpstt; union { float32 f; @@ -224,7 +288,7 @@ void helper_flds_ST0(CPUX86State *env, uint32_t val) void helper_fldl_ST0(CPUX86State *env, uint64_t val) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int new_fpstt; union { float64 f; @@ -274,7 +338,7 @@ void helper_fildll_ST0(CPUX86State *env, int64_t val) uint32_t helper_fsts_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); union { float32 f; uint32_t i; @@ -287,7 +351,7 @@ uint32_t helper_fsts_ST0(CPUX86State *env) uint64_t helper_fstl_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); union { float64 f; uint64_t i; @@ -300,7 +364,7 @@ uint64_t helper_fstl_ST0(CPUX86State *env) int32_t helper_fist_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int32_t val; val = floatx80_to_int32(ST0, &env->fp_status); @@ -314,7 +378,7 @@ int32_t helper_fist_ST0(CPUX86State *env) int32_t helper_fistl_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int32_t val; val = floatx80_to_int32(ST0, &env->fp_status); @@ -327,7 +391,7 @@ int32_t helper_fistl_ST0(CPUX86State *env) int64_t helper_fistll_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int64_t val; val = floatx80_to_int64(ST0, &env->fp_status); @@ -340,7 +404,7 @@ int64_t helper_fistll_ST0(CPUX86State *env) int32_t helper_fistt_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int32_t val; val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status); @@ -354,7 +418,7 @@ int32_t helper_fistt_ST0(CPUX86State *env) int32_t helper_fisttl_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int32_t val; val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status); @@ -367,7 +431,7 @@ int32_t helper_fisttl_ST0(CPUX86State *env) int64_t helper_fisttll_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int64_t val; val = floatx80_to_int64_round_to_zero(ST0, &env->fp_status); @@ -381,16 +445,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) @@ -457,7 +527,7 @@ static const int fcom_ccval[4] = {0x0100, 0x4000, 0x0000, 0x4500}; void helper_fcom_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); FloatRelation ret; ret = floatx80_compare(ST0, FT0, &env->fp_status); @@ -467,7 +537,7 @@ void helper_fcom_ST0_FT0(CPUX86State *env) void helper_fucom_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); FloatRelation ret; ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); @@ -479,54 +549,54 @@ static const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C}; void helper_fcomi_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int eflags; 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); } void helper_fucomi_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int eflags; 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); } void helper_fadd_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST0 = floatx80_add(ST0, FT0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fmul_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST0 = floatx80_mul(ST0, FT0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fsub_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST0 = floatx80_sub(ST0, FT0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fsubr_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST0 = floatx80_sub(FT0, ST0, &env->fp_status); merge_exception_flags(env, old_flags); } @@ -545,28 +615,28 @@ void helper_fdivr_ST0_FT0(CPUX86State *env) void helper_fadd_STN_ST0(CPUX86State *env, int st_index) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST(st_index) = floatx80_add(ST(st_index), ST0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fmul_STN_ST0(CPUX86State *env, int st_index) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST(st_index) = floatx80_mul(ST(st_index), ST0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fsub_STN_ST0(CPUX86State *env, int st_index) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST(st_index) = floatx80_sub(ST(st_index), ST0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fsubr_STN_ST0(CPUX86State *env, int st_index) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST(st_index) = floatx80_sub(ST0, ST(st_index), &env->fp_status); merge_exception_flags(env, old_flags); } @@ -768,18 +838,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); @@ -788,12 +861,14 @@ void helper_fbld_ST0(CPUX86State *env, target_ulong ptr) void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int v; 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); @@ -801,20 +876,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) { @@ -823,10 +898,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); } @@ -1061,12 +1136,12 @@ static const struct f2xm1_data f2xm1_table[65] = { void helper_f2xm1(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); uint64_t sig = extractFloatx80Frac(ST0); int32_t exp = extractFloatx80Exp(ST0); bool sign = extractFloatx80Sign(ST0); - if (floatx80_invalid_encoding(ST0)) { + if (floatx80_invalid_encoding(ST0, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_default_nan(&env->fp_status); } else if (floatx80_is_any_nan(ST0)) { @@ -1294,7 +1369,7 @@ static const struct fpatan_data fpatan_table[9] = { void helper_fpatan(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); uint64_t arg0_sig = extractFloatx80Frac(ST0); int32_t arg0_exp = extractFloatx80Exp(ST0); bool arg0_sign = extractFloatx80Sign(ST0); @@ -1308,8 +1383,8 @@ void helper_fpatan(CPUX86State *env) } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_silence_nan(ST1, &env->fp_status); - } else if (floatx80_invalid_encoding(ST0) || - floatx80_invalid_encoding(ST1)) { + } else if (floatx80_invalid_encoding(ST0, &env->fp_status) || + floatx80_invalid_encoding(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_default_nan(&env->fp_status); } else if (floatx80_is_any_nan(ST0)) { @@ -1318,7 +1393,8 @@ void helper_fpatan(CPUX86State *env) /* Pass this NaN through. */ } else if (floatx80_is_zero(ST1) && !arg0_sign) { /* Pass this zero through. */ - } else if (((floatx80_is_infinity(ST0) && !floatx80_is_infinity(ST1)) || + } else if (((floatx80_is_infinity(ST0, &env->fp_status) && + !floatx80_is_infinity(ST1, &env->fp_status)) || arg0_exp - arg1_exp >= 80) && !arg0_sign) { /* @@ -1367,8 +1443,8 @@ void helper_fpatan(CPUX86State *env) rexp = pi_exp; rsig0 = pi_sig_high; rsig1 = pi_sig_low; - } else if (floatx80_is_infinity(ST1)) { - if (floatx80_is_infinity(ST0)) { + } else if (floatx80_is_infinity(ST1, &env->fp_status)) { + if (floatx80_is_infinity(ST0, &env->fp_status)) { if (arg0_sign) { rexp = pi_34_exp; rsig0 = pi_34_sig_high; @@ -1387,7 +1463,8 @@ void helper_fpatan(CPUX86State *env) rexp = pi_2_exp; rsig0 = pi_2_sig_high; rsig1 = pi_2_sig_low; - } else if (floatx80_is_infinity(ST0) || arg0_exp - arg1_exp >= 80) { + } else if (floatx80_is_infinity(ST0, &env->fp_status) || + arg0_exp - arg1_exp >= 80) { /* ST0 is negative. */ rexp = pi_exp; rsig0 = pi_sig_high; @@ -1731,7 +1808,7 @@ void helper_fpatan(CPUX86State *env) void helper_fxtract(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); CPU_LDoubleU temp; temp.d = ST0; @@ -1742,7 +1819,7 @@ void helper_fxtract(CPUX86State *env) &env->fp_status); fpush(env); ST0 = temp.d; - } else if (floatx80_invalid_encoding(ST0)) { + } else if (floatx80_invalid_encoding(ST0, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_default_nan(&env->fp_status); fpush(env); @@ -1754,10 +1831,10 @@ void helper_fxtract(CPUX86State *env) } fpush(env); ST0 = ST1; - } else if (floatx80_is_infinity(ST0)) { + } else if (floatx80_is_infinity(ST0, &env->fp_status)) { fpush(env); ST0 = ST1; - ST1 = floatx80_infinity; + ST1 = floatx80_default_inf(0, &env->fp_status); } else { int expdif; @@ -1765,7 +1842,7 @@ void helper_fxtract(CPUX86State *env) int shift = clz64(temp.l.lower); temp.l.lower <<= shift; expdif = 1 - EXPBIAS - shift; - float_raise(float_flag_input_denormal, &env->fp_status); + float_raise(float_flag_input_denormal_flushed, &env->fp_status); } else { expdif = EXPD(temp) - EXPBIAS; } @@ -1780,7 +1857,7 @@ void helper_fxtract(CPUX86State *env) static void helper_fprem_common(CPUX86State *env, bool mod) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); uint64_t quotient; CPU_LDoubleU temp0, temp1; int exp0, exp1, expdiff; @@ -1793,7 +1870,8 @@ static void helper_fprem_common(CPUX86State *env, bool mod) env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ if (floatx80_is_zero(ST0) || floatx80_is_zero(ST1) || exp0 == 0x7fff || exp1 == 0x7fff || - floatx80_invalid_encoding(ST0) || floatx80_invalid_encoding(ST1)) { + floatx80_invalid_encoding(ST0, &env->fp_status) || + floatx80_invalid_encoding(ST1, &env->fp_status)) { ST0 = floatx80_modrem(ST0, ST1, mod, "ient, &env->fp_status); } else { if (exp0 == 0) { @@ -1975,7 +2053,7 @@ static void helper_fyl2x_common(CPUX86State *env, floatx80 arg, int32_t *exp, void helper_fyl2xp1(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); uint64_t arg0_sig = extractFloatx80Frac(ST0); int32_t arg0_exp = extractFloatx80Exp(ST0); bool arg0_sign = extractFloatx80Sign(ST0); @@ -1989,8 +2067,8 @@ void helper_fyl2xp1(CPUX86State *env) } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_silence_nan(ST1, &env->fp_status); - } else if (floatx80_invalid_encoding(ST0) || - floatx80_invalid_encoding(ST1)) { + } else if (floatx80_invalid_encoding(ST0, &env->fp_status) || + floatx80_invalid_encoding(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_default_nan(&env->fp_status); } else if (floatx80_is_any_nan(ST0)) { @@ -2073,7 +2151,7 @@ void helper_fyl2xp1(CPUX86State *env) void helper_fyl2x(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); uint64_t arg0_sig = extractFloatx80Frac(ST0); int32_t arg0_exp = extractFloatx80Exp(ST0); bool arg0_sign = extractFloatx80Sign(ST0); @@ -2087,8 +2165,8 @@ void helper_fyl2x(CPUX86State *env) } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_silence_nan(ST1, &env->fp_status); - } else if (floatx80_invalid_encoding(ST0) || - floatx80_invalid_encoding(ST1)) { + } else if (floatx80_invalid_encoding(ST0, &env->fp_status) || + floatx80_invalid_encoding(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_default_nan(&env->fp_status); } else if (floatx80_is_any_nan(ST0)) { @@ -2098,7 +2176,7 @@ void helper_fyl2x(CPUX86State *env) } else if (arg0_sign && !floatx80_is_zero(ST0)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_default_nan(&env->fp_status); - } else if (floatx80_is_infinity(ST1)) { + } else if (floatx80_is_infinity(ST1, &env->fp_status)) { FloatRelation cmp = floatx80_compare(ST0, floatx80_one, &env->fp_status); switch (cmp) { @@ -2113,7 +2191,7 @@ void helper_fyl2x(CPUX86State *env) ST1 = floatx80_default_nan(&env->fp_status); break; } - } else if (floatx80_is_infinity(ST0)) { + } else if (floatx80_is_infinity(ST0, &env->fp_status)) { if (floatx80_is_zero(ST1)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_default_nan(&env->fp_status); @@ -2220,7 +2298,7 @@ void helper_fyl2x(CPUX86State *env) void helper_fsqrt(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); if (floatx80_is_neg(ST0)) { env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ env->fpus |= 0x400; @@ -2246,15 +2324,16 @@ void helper_fsincos(CPUX86State *env) void helper_frndint(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST0 = floatx80_round_to_int(ST0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fscale(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); - if (floatx80_invalid_encoding(ST1) || floatx80_invalid_encoding(ST0)) { + int old_flags = save_exception_flags(env); + if (floatx80_invalid_encoding(ST1, &env->fp_status) || + floatx80_invalid_encoding(ST0, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_default_nan(&env->fp_status); } else if (floatx80_is_any_nan(ST1)) { @@ -2266,11 +2345,11 @@ void helper_fscale(CPUX86State *env) float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_silence_nan(ST0, &env->fp_status); } - } else if (floatx80_is_infinity(ST1) && - !floatx80_invalid_encoding(ST0) && + } else if (floatx80_is_infinity(ST1, &env->fp_status) && + !floatx80_invalid_encoding(ST0, &env->fp_status) && !floatx80_is_any_nan(ST0)) { if (floatx80_is_neg(ST1)) { - if (floatx80_is_infinity(ST0)) { + if (floatx80_is_infinity(ST0, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_default_nan(&env->fp_status); } else { @@ -2283,15 +2362,14 @@ void helper_fscale(CPUX86State *env) float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_default_nan(&env->fp_status); } else { - ST0 = (floatx80_is_neg(ST0) ? - floatx80_chs(floatx80_infinity) : - floatx80_infinity); + ST0 = floatx80_default_inf(floatx80_is_neg(ST0), + &env->fp_status); } } } else { int n; FloatX80RoundPrec save = env->fp_status.floatx80_rounding_precision; - uint8_t save_flags = get_float_exception_flags(&env->fp_status); + int save_flags = get_float_exception_flags(&env->fp_status); set_float_exception_flags(0, &env->fp_status); n = floatx80_to_int32_round_to_zero(ST1, &env->fp_status); set_float_exception_flags(save_flags, &env->fp_status); @@ -2363,9 +2441,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; @@ -2392,28 +2470,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) @@ -2432,20 +2513,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); @@ -2455,21 +2531,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; } @@ -2478,20 +2555,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; } @@ -2499,13 +2578,18 @@ 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); } #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; @@ -2515,33 +2599,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; @@ -2553,14 +2641,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) { @@ -2570,58 +2659,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) @@ -2638,57 +2736,73 @@ 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; - /* The OS must have enabled XSAVE. */ - if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { - raise_exception_ra(env, EXCP06_ILLOP, ra); - } - - /* The operand must be 64 byte aligned. */ - if (ptr & 63) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } - - /* Never save anything not enabled by XCR0. */ - rfbm &= env->xcr0; - opt &= rfbm; - if (opt & XSTATE_FP_MASK) { - do_xsave_fpu(env, ptr, ra); + do_xsave_fpu(ac, ptr); } if (rfbm & XSTATE_SSE_MASK) { /* Note that saving MXCSR is not suppressed by XSAVEOPT. */ - do_xsave_mxcsr(env, ptr, ra); + do_xsave_mxcsr(ac, ptr); } if (opt & XSTATE_SSE_MASK) { - do_xsave_sse(env, ptr, ra); + do_xsave_sse(ac, ptr); } if (opt & XSTATE_YMM_MASK) { - do_xsave_ymmh(env, ptr + XO(avx_state), ra); + do_xsave_ymmh(ac, ptr + XO(avx_state)); } if (opt & XSTATE_BNDREGS_MASK) { - do_xsave_bndregs(env, ptr + XO(bndreg_state), ra); + do_xsave_bndregs(ac, ptr + XO(bndreg_state)); } if (opt & XSTATE_BNDCSR_MASK) { - do_xsave_bndcsr(env, ptr + XO(bndcsr_state), ra); + do_xsave_bndcsr(ac, ptr + XO(bndcsr_state)); } if (opt & XSTATE_PKRU_MASK) { - do_xsave_pkru(env, ptr + XO(pkru_state), ra); + do_xsave_pkru(ac, ptr + XO(pkru_state)); } /* Update the XSTATE_BV field. */ - old_bv = cpu_ldq_data_ra(env, ptr + XO(header.xstate_bv), ra); + old_bv = access_ldq(ac, ptr + XO(header.xstate_bv)); new_bv = (old_bv & ~rfbm) | (inuse & rfbm); - cpu_stq_data_ra(env, ptr + XO(header.xstate_bv), new_bv, ra); + 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); + } + + /* The operand must be 64 byte aligned. */ + 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); + + 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) @@ -2697,36 +2811,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; @@ -2738,8 +2857,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; } } @@ -2760,8 +2879,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) { @@ -2771,8 +2891,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); } } @@ -2792,100 +2912,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; - - /* The OS must have enabled XSAVE. */ - if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { - raise_exception_ra(env, EXCP06_ILLOP, ra); - } + 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 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); + /* + * 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; } - /* 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)); @@ -2894,23 +3011,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)); @@ -2919,7 +3036,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)); } @@ -2928,7 +3045,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; } @@ -2943,38 +3060,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 @@ -3012,6 +3208,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; @@ -3053,6 +3254,7 @@ void update_mxcsr_status(CPUX86State *env) /* Set exception flags. */ set_float_exception_flags((mxcsr & FPUS_IE ? float_flag_invalid : 0) | + (mxcsr & FPUS_DE ? float_flag_input_denormal_used : 0) | (mxcsr & FPUS_ZE ? float_flag_divbyzero : 0) | (mxcsr & FPUS_OE ? float_flag_overflow : 0) | (mxcsr & FPUS_UE ? float_flag_underflow : 0) | @@ -3068,20 +3270,14 @@ void update_mxcsr_status(CPUX86State *env) void update_mxcsr_from_sse_status(CPUX86State *env) { - uint8_t flags = get_float_exception_flags(&env->sse_status); - /* - * The MXCSR denormal flag has opposite semantics to - * float_flag_input_denormal (the softfloat code sets that flag - * only when flushing input denormals to zero, but SSE sets it - * only when not flushing them to zero), so is not converted - * here. - */ + int flags = get_float_exception_flags(&env->sse_status); env->mxcsr |= ((flags & float_flag_invalid ? FPUS_IE : 0) | + (flags & float_flag_input_denormal_used ? FPUS_DE : 0) | (flags & float_flag_divbyzero ? FPUS_ZE : 0) | (flags & float_flag_overflow ? FPUS_OE : 0) | (flags & float_flag_underflow ? FPUS_UE : 0) | (flags & float_flag_inexact ? FPUS_PE : 0) | - (flags & float_flag_output_denormal ? FPUS_UE | FPUS_PE : + (flags & float_flag_output_denormal_flushed ? FPUS_UE | FPUS_PE : 0)); } diff --git a/target/i386/tcg/helper-tcg.h b/target/i386/tcg/helper-tcg.h index cd1723389ad..be011b06b7c 100644 --- a/target/i386/tcg/helper-tcg.h +++ b/target/i386/tcg/helper-tcg.h @@ -20,7 +20,7 @@ #ifndef I386_HELPER_TCG_H #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 +39,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 @@ -56,6 +58,8 @@ static inline target_long lshift(target_long x, int n) /* translate.c */ void tcg_x86_init(void); +void x86_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); /* excp_helper.c */ G_NORETURN void raise_exception(CPUX86State *env, int exception_index); @@ -65,8 +69,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,13 +89,15 @@ 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 */ +/* system/svm_helper.c */ #ifndef CONFIG_USER_ONLY G_NORETURN void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1, uintptr_t retaddr); @@ -110,7 +115,17 @@ int exception_has_error_code(int intno); /* smm_helper.c */ void do_smm_enter(X86CPU *cpu); -/* bpt_helper.c */ +/* system/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 05418f181f1..46741d9f68a 100644 --- a/target/i386/tcg/int_helper.c +++ b/target/i386/tcg/int_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "qapi/error.h" @@ -29,22 +28,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 +144,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 +170,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 +186,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 +195,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 +211,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 +219,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 +236,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 +247,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 +268,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 +431,6 @@ target_ulong helper_pext(target_ulong src, target_ulong mask) return dest; } -#define SHIFT 0 -#include "shift_helper_template.h.inc" -#undef SHIFT - -#define SHIFT 1 -#include "shift_helper_template.h.inc" -#undef SHIFT - -#define SHIFT 2 -#include "shift_helper_template.h.inc" -#undef SHIFT - -#ifdef TARGET_X86_64 -#define SHIFT 3 -#include "shift_helper_template.h.inc" -#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 +452,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 3ef84e90d94..9e7c2d80293 100644 --- a/target/i386/tcg/mem_helper.c +++ b/target/i386/tcg/mem_helper.c @@ -20,8 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "qemu/int128.h" #include "qemu/atomic128.h" #include "tcg/tcg.h" diff --git a/target/i386/tcg/meson.build b/target/i386/tcg/meson.build index f9110e890c8..c57e661752e 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', @@ -11,5 +12,5 @@ i386_ss.add(when: 'CONFIG_TCG', if_true: files( 'tcg-cpu.c', 'translate.c'), if_false: files('tcg-stub.c')) -subdir('sysemu') +subdir('system') subdir('user') diff --git a/target/i386/tcg/misc_helper.c b/target/i386/tcg/misc_helper.c index 868f36ab7f5..2b5f092a23f 100644 --- a/target/i386/tcg/misc_helper.c +++ b/target/i386/tcg/misc_helper.c @@ -21,7 +21,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "helper-tcg.h" /* @@ -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); } } @@ -88,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) { @@ -134,7 +130,7 @@ void helper_wrpkru(CPUX86State *env, uint32_t ecx, uint64_t val) target_ulong HELPER(rdpid)(CPUX86State *env) { -#if defined CONFIG_SOFTMMU +#if !defined CONFIG_USER_ONLY return env->tsc_aux; #elif defined CONFIG_LINUX && defined CONFIG_GETCPU unsigned cpu, node; diff --git a/target/i386/tcg/mpx_helper.c b/target/i386/tcg/mpx_helper.c index 22423eedcd0..fa8abcc4820 100644 --- a/target/i386/tcg/mpx_helper.c +++ b/target/i386/tcg/mpx_helper.c @@ -20,8 +20,8 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" -#include "exec/exec-all.h" +#include "accel/tcg/cpu-ldst.h" +#include "exec/target_page.h" #include "helper-tcg.h" diff --git a/target/i386/tcg/ops_sse_header.h.inc b/target/i386/tcg/ops_sse_header.h.inc index 8a7b2f4e2f6..d92c6faf6d6 100644 --- a/target/i386/tcg/ops_sse_header.h.inc +++ b/target/i386/tcg/ops_sse_header.h.inc @@ -399,6 +399,20 @@ 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 diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index e8d19c65fdc..071f3fbd83d 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -22,15 +22,80 @@ #include "cpu.h" #include "qemu/log.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "exec/log.h" #include "helper-tcg.h" #include "seg_helper.h" +#include "access.h" +#include "tcg-cpu.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; } @@ -64,6 +129,22 @@ int get_pg_mode(CPUX86State *env) return pg_mode; } +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); +} + /* return non zero if error */ static inline int load_segment_ra(CPUX86State *env, uint32_t *e1_ptr, uint32_t *e2_ptr, int selector, @@ -226,23 +307,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) + uint32_t next_eip, bool has_error_code, + uint32_t error_code, 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,125 +388,124 @@ 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; } - /* XXX: avoid a compiler warning, see - http://support.amd.com/us/Processor_TechDocs/24593.pdf - 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 +595,43 @@ static void switch_tss_ra(CPUX86State *env, int tss_selector, cpu_x86_update_dr7(env, env->dr[7] & ~DR7_LOCAL_BP_MASK); } #endif + + if (has_error_code) { + int cpl = env->hflags & HF_CPL_MASK; + StackAccess sa; + + /* push the error code */ + sa.env = env; + sa.ra = retaddr; + sa.mmu_index = x86_mmu_index_pl(env, cpl); + sa.sp = env->regs[R_ESP]; + if (env->segs[R_SS].flags & DESC_B_MASK) { + sa.sp_mask = 0xffffffff; + } else { + sa.sp_mask = 0xffff; + } + sa.ss_base = env->segs[R_SS].base; + if (type & 8) { + pushl(&sa, error_code); + } else { + pushw(&sa, error_code); + } + SET_ESP(sa.sp, sa.sp_mask); + } + + if (new_trap) { + env->dr[6] |= DR6_BT; + raise_exception_ra(env, EXCP01_DB, retaddr); + } } static void switch_tss(CPUX86State *env, int tss_selector, uint32_t e1, uint32_t e2, int source, - uint32_t next_eip) + uint32_t next_eip, bool has_error_code, + int error_code) { - switch_tss_ra(env, tss_selector, e1, e2, source, next_eip, 0); + switch_tss_ra(env, tss_selector, e1, e2, source, next_eip, + has_error_code, error_code, 0); } static inline unsigned int get_sp_mask(unsigned int e2) @@ -522,6 +648,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 +681,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 +702,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,34 +735,17 @@ 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); - if (has_error_code) { - int type; - uint32_t mask; - - /* push the error code */ - type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; - shift = type >> 3; - if (env->segs[R_SS].flags & DESC_B_MASK) { - mask = 0xffffffff; - } else { - mask = 0xffff; - } - esp = (env->regs[R_ESP] - (2 << shift)) & mask; - ssp = env->segs[R_SS].base + esp; - if (shift) { - cpu_stl_kernel(env, ssp, error_code); - } else { - cpu_stw_kernel(env, ssp, error_code); - } - SET_ESP(esp, mask); - } + switch_tss(env, intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip, + has_error_code, error_code); return; } @@ -699,8 +776,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 +803,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 +827,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 +886,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 +901,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); \ - } - -#define POPQ_RA(sp, val, ra) \ - { \ - val = cpu_ldq_kernel_ra(env, sp, ra); \ - sp += 8; \ - } +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 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 +954,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,8 +965,10 @@ 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; @@ -934,28 +1025,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]; + } + sa.sp &= ~0xfLL; /* align stack */ + + /* See do_interrupt_protected. */ + eflags = cpu_compute_eflags(env); + if (set_rf) { + eflags |= RF_MASK; } - esp &= ~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); + 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 +1067,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, @@ -1040,10 +1142,11 @@ 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; @@ -1053,8 +1156,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 { @@ -1062,12 +1171,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); @@ -1432,7 +1541,8 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, if (dpl < cpl || dpl < rpl) { raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } - switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_JMP, next_eip, GETPC()); + switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_JMP, next_eip, + false, 0, GETPC()); break; case 4: /* 286 call gate */ case 12: /* 386 call gate */ @@ -1510,21 +1620,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); @@ -1536,9 +1649,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)); @@ -1550,7 +1664,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()); } @@ -1574,17 +1693,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); @@ -1592,15 +1712,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); @@ -1608,7 +1728,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; @@ -1634,7 +1754,8 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, if (dpl < cpl || dpl < rpl) { raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } - switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_CALL, next_eip, GETPC()); + switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_CALL, next_eip, + false, 0, GETPC()); return; case 4: /* 286 call gate */ case 12: /* 386 call gate */ @@ -1701,15 +1822,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 { @@ -1718,7 +1840,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()); } @@ -1741,63 +1862,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 */ @@ -1811,7 +1934,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); } @@ -1822,7 +1945,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; } } @@ -1830,26 +1953,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; @@ -1902,47 +2028,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); } } } @@ -1959,7 +2090,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); @@ -1978,7 +2108,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 */ @@ -1990,21 +2120,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", @@ -2054,14 +2182,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 */ @@ -2070,9 +2198,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 */ @@ -2092,12 +2220,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 | @@ -2138,7 +2266,8 @@ void helper_iret_protected(CPUX86State *env, int shift, int next_eip) if (type != 3) { raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC()); } - switch_tss_ra(env, tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip, GETPC()); + switch_tss_ra(env, tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip, + false, 0, GETPC()); } else { helper_ret_protected(env, shift, 1, 0, GETPC()); } @@ -2225,11 +2354,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; } @@ -2261,22 +2390,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; } @@ -2311,11 +2440,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; } @@ -2325,7 +2454,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; } @@ -2350,11 +2479,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) @@ -2363,7 +2492,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; } @@ -2384,9 +2513,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/seg_helper.h b/target/i386/tcg/seg_helper.h index ebf10352778..ea98e1a98ed 100644 --- a/target/i386/tcg/seg_helper.h +++ b/target/i386/tcg/seg_helper.h @@ -20,6 +20,8 @@ #ifndef SEG_HELPER_H #define SEG_HELPER_H +#include "cpu.h" + //#define DEBUG_PCALL #ifdef DEBUG_PCALL @@ -31,12 +33,12 @@ # define LOG_PCALL_STATE(cpu) do { } while (0) #endif +int cpu_mmu_index_kernel(CPUX86State *env); + /* * TODO: Convert callers to compute cpu_mmu_index_kernel once * and use *_mmuidx_ra directly. */ -#define cpu_ldub_kernel_ra(e, p, r) \ - cpu_ldub_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) #define cpu_lduw_kernel_ra(e, p, r) \ cpu_lduw_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) #define cpu_ldl_kernel_ra(e, p, r) \ @@ -44,8 +46,6 @@ #define cpu_ldq_kernel_ra(e, p, r) \ cpu_ldq_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) -#define cpu_stb_kernel_ra(e, p, v, r) \ - cpu_stb_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) #define cpu_stw_kernel_ra(e, p, v, r) \ cpu_stw_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) #define cpu_stl_kernel_ra(e, p, v, r) \ @@ -53,12 +53,10 @@ #define cpu_stq_kernel_ra(e, p, v, r) \ cpu_stq_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) -#define cpu_ldub_kernel(e, p) cpu_ldub_kernel_ra(e, p, 0) #define cpu_lduw_kernel(e, p) cpu_lduw_kernel_ra(e, p, 0) #define cpu_ldl_kernel(e, p) cpu_ldl_kernel_ra(e, p, 0) #define cpu_ldq_kernel(e, p) cpu_ldq_kernel_ra(e, p, 0) -#define cpu_stb_kernel(e, p, v) cpu_stb_kernel_ra(e, p, v, 0) #define cpu_stw_kernel(e, p, v) cpu_stw_kernel_ra(e, p, v, 0) #define cpu_stl_kernel(e, p, v) cpu_stl_kernel_ra(e, p, v, 0) #define cpu_stq_kernel(e, p, v) cpu_stq_kernel_ra(e, p, v, 0) diff --git a/target/i386/tcg/shift_helper_template.h.inc b/target/i386/tcg/shift_helper_template.h.inc deleted file mode 100644 index 54f15d6e05c..00000000000 --- a/target/i386/tcg/shift_helper_template.h.inc +++ /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/tcg/sysemu/fpu_helper.c b/target/i386/tcg/sysemu/fpu_helper.c deleted file mode 100644 index 93506cdd94e..00000000000 --- a/target/i386/tcg/sysemu/fpu_helper.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * x86 FPU, MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI helpers (sysemu code) - * - * 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 "cpu.h" -#include "hw/irq.h" - -static qemu_irq ferr_irq; - -void x86_register_ferr_irq(qemu_irq irq) -{ - ferr_irq = irq; -} - -void fpu_check_raise_ferr_irq(CPUX86State *env) -{ - if (ferr_irq && !(env->hflags2 & HF2_IGNNE_MASK)) { - qemu_mutex_lock_iothread(); - qemu_irq_raise(ferr_irq); - qemu_mutex_unlock_iothread(); - return; - } -} - -void cpu_clear_ignne(void) -{ - CPUX86State *env = &X86_CPU(first_cpu)->env; - env->hflags2 &= ~HF2_IGNNE_MASK; -} - -void cpu_set_ignne(void) -{ - CPUX86State *env = &X86_CPU(first_cpu)->env; - - assert(qemu_mutex_iothread_locked()); - - env->hflags2 |= HF2_IGNNE_MASK; - /* - * We get here in response to a write to port F0h. The chipset should - * deassert FP_IRQ and FERR# instead should stay signaled until FPSW_SE is - * cleared, because FERR# and FP_IRQ are two separate pins on real - * hardware. However, we don't model FERR# as a qemu_irq, so we just - * do directly what the chipset would do, i.e. deassert FP_IRQ. - */ - qemu_irq_lower(ferr_irq); -} diff --git a/target/i386/tcg/sysemu/tcg-cpu.c b/target/i386/tcg/sysemu/tcg-cpu.c deleted file mode 100644 index c223c0fe9bc..00000000000 --- a/target/i386/tcg/sysemu/tcg-cpu.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * i386 TCG cpu class initialization functions specific to sysemu - * - * 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 "cpu.h" -#include "tcg/helper-tcg.h" - -#include "sysemu/sysemu.h" -#include "qemu/units.h" -#include "exec/address-spaces.h" - -#include "tcg/tcg-cpu.h" - -static void tcg_cpu_machine_done(Notifier *n, void *unused) -{ - X86CPU *cpu = container_of(n, X86CPU, machine_done); - MemoryRegion *smram = - (MemoryRegion *) object_resolve_path("/machine/smram", NULL); - - if (smram) { - cpu->smram = g_new(MemoryRegion, 1); - memory_region_init_alias(cpu->smram, OBJECT(cpu), "smram", - smram, 0, 4 * GiB); - memory_region_set_enabled(cpu->smram, true); - memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, - cpu->smram, 1); - } -} - -bool tcg_cpu_realizefn(CPUState *cs, Error **errp) -{ - X86CPU *cpu = X86_CPU(cs); - - /* - * The realize order is important, since x86_cpu_realize() checks if - * nothing else has been set by the user (or by accelerators) in - * cpu->ucode_rev and cpu->phys_bits, and the memory regions - * initialized here are needed for the vcpu initialization. - * - * realize order: - * tcg_cpu -> host_cpu -> x86_cpu - */ - cpu->cpu_as_mem = g_new(MemoryRegion, 1); - cpu->cpu_as_root = g_new(MemoryRegion, 1); - - /* Outer container... */ - memory_region_init(cpu->cpu_as_root, OBJECT(cpu), "memory", ~0ull); - memory_region_set_enabled(cpu->cpu_as_root, true); - - /* - * ... with two regions inside: normal system memory with low - * priority, and... - */ - memory_region_init_alias(cpu->cpu_as_mem, OBJECT(cpu), "memory", - get_system_memory(), 0, ~0ull); - memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, cpu->cpu_as_mem, 0); - memory_region_set_enabled(cpu->cpu_as_mem, true); - - cs->num_ases = 2; - cpu_address_space_init(cs, 0, "cpu-memory", cs->memory); - cpu_address_space_init(cs, 1, "cpu-smm", cpu->cpu_as_root); - - /* ... SMRAM with higher priority, linked from /machine/smram. */ - cpu->machine_done.notify = tcg_cpu_machine_done; - qemu_add_machine_init_done_notifier(&cpu->machine_done); - return true; -} diff --git a/target/i386/tcg/sysemu/bpt_helper.c b/target/i386/tcg/system/bpt_helper.c similarity index 92% rename from target/i386/tcg/sysemu/bpt_helper.c rename to target/i386/tcg/system/bpt_helper.c index 4d96a48a3ca..aebb5caac37 100644 --- a/target/i386/tcg/sysemu/bpt_helper.c +++ b/target/i386/tcg/system/bpt_helper.c @@ -1,5 +1,5 @@ /* - * i386 breakpoint helpers - sysemu code + * i386 breakpoint helpers - system code * * Copyright (c) 2003 Fabrice Bellard * @@ -19,8 +19,8 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" +#include "exec/watchpoint.h" #include "tcg/helper-tcg.h" @@ -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/system/excp_helper.c similarity index 83% rename from target/i386/tcg/sysemu/excp_helper.c rename to target/i386/tcg/system/excp_helper.c index 226689a4f20..c1626215877 100644 --- a/target/i386/tcg/sysemu/excp_helper.c +++ b/target/i386/tcg/system/excp_helper.c @@ -1,5 +1,5 @@ /* - * x86 exception helpers - sysemu code + * x86 exception helpers - system code * * Copyright (c) 2003 Fabrice Bellard * @@ -19,8 +19,13 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/cpu_ldst.h" -#include "exec/exec-all.h" +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" +#include "exec/cputlb.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" +#include "exec/tlb-flags.h" +#include "exec/tswap.h" #include "tcg/helper-tcg.h" typedef struct TranslateParams { @@ -61,12 +66,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, 0, 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; @@ -82,20 +86,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); } /* @@ -107,6 +111,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); @@ -114,6 +122,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; } @@ -132,12 +141,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 = { @@ -149,6 +158,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, uint32_t pkr; int page_size; int error_code; + int prot; restart_all: rsvd_mask = ~MAKE_64BIT_MASK(0, env_archcpu(env)->phys_bits); @@ -164,13 +174,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; } @@ -189,13 +198,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; } @@ -210,13 +218,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; } @@ -238,13 +245,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; } @@ -260,13 +267,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; } @@ -287,12 +293,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; } @@ -302,16 +307,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; } @@ -335,11 +340,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; } @@ -347,6 +352,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: @@ -362,8 +376,8 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, 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; @@ -422,17 +436,20 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, } } - /* 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, 0, 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, @@ -464,7 +481,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, } } - out->paddr = paddr; + out->paddr = paddr & x86_get_a20_mask(env); out->prot = prot; out->page_size = page_size; return true; @@ -529,7 +546,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; @@ -545,10 +563,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; } @@ -557,7 +576,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; @@ -575,19 +598,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; @@ -597,11 +614,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/system/fpu_helper.c b/target/i386/tcg/system/fpu_helper.c new file mode 100644 index 00000000000..0b4fa187dfb --- /dev/null +++ b/target/i386/tcg/system/fpu_helper.c @@ -0,0 +1,63 @@ +/* + * x86 FPU, MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI helpers (system code) + * + * 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 "cpu.h" +#include "hw/irq.h" + +static qemu_irq ferr_irq; + +void x86_register_ferr_irq(qemu_irq irq) +{ + ferr_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; + } +} + +void cpu_clear_ignne(void) +{ + CPUX86State *env = &X86_CPU(first_cpu)->env; + env->hflags2 &= ~HF2_IGNNE_MASK; +} + +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 + * deassert FP_IRQ and FERR# instead should stay signaled until FPSW_SE is + * cleared, because FERR# and FP_IRQ are two separate pins on real + * hardware. However, we don't model FERR# as a qemu_irq, so we just + * do directly what the chipset would do, i.e. deassert FP_IRQ. + */ + qemu_irq_lower(ferr_irq); +} diff --git a/target/i386/tcg/sysemu/meson.build b/target/i386/tcg/system/meson.build similarity index 100% rename from target/i386/tcg/sysemu/meson.build rename to target/i386/tcg/system/meson.build diff --git a/target/i386/tcg/sysemu/misc_helper.c b/target/i386/tcg/system/misc_helper.c similarity index 89% rename from target/i386/tcg/sysemu/misc_helper.c rename to target/i386/tcg/system/misc_helper.c index e1528b7f80b..9c3f5cc99b3 100644 --- a/target/i386/tcg/sysemu/misc_helper.c +++ b/target/i386/tcg/system/misc_helper.c @@ -1,5 +1,5 @@ /* - * x86 misc helpers - sysemu code + * x86 misc helpers - system code * * Copyright (c) 2003 Fabrice Bellard * @@ -21,10 +21,12 @@ #include "qemu/main-loop.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" -#include "exec/address-spaces.h" -#include "exec/exec-all.h" +#include "accel/tcg/cpu-ldst.h" +#include "system/address-spaces.h" +#include "system/memory.h" +#include "exec/cputlb.h" #include "tcg/helper-tcg.h" +#include "hw/i386/apic.h" void helper_outb(CPUX86State *env, uint32_t port, uint32_t data) { @@ -62,23 +64,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 +110,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 +149,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 +203,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 +294,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 + @@ -451,8 +469,20 @@ void helper_rdmsr(CPUX86State *env) val = x86_cpu->ucode_rev; break; case MSR_CORE_THREAD_COUNT: { - CPUState *cs = CPU(x86_cpu); - val = (cs->nr_threads * cs->nr_cores) | (cs->nr_cores << 16); + val = cpu_x86_get_msr_core_thread_count(x86_cpu); + 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: @@ -476,25 +506,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) { @@ -516,8 +537,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/system/seg_helper.c similarity index 88% rename from target/i386/tcg/sysemu/seg_helper.c rename to target/i386/tcg/system/seg_helper.c index 1cb5a0db45c..d4ea890c124 100644 --- a/target/i386/tcg/sysemu/seg_helper.c +++ b/target/i386/tcg/system/seg_helper.c @@ -1,5 +1,5 @@ /* - * x86 segmentation related helpers: (sysemu-only code) + * x86 segmentation related helpers: (system-only code) * TSS, interrupts, system calls, jumps and call/task gates, descriptors * * Copyright (c) 2003 Fabrice Bellard @@ -20,9 +20,10 @@ #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 "accel/tcg/cpu-ldst.h" #include "tcg/helper-tcg.h" #include "../seg_helper.h" @@ -127,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/smm_helper.c b/target/i386/tcg/system/smm_helper.c similarity index 99% rename from target/i386/tcg/sysemu/smm_helper.c rename to target/i386/tcg/system/smm_helper.c index a45b5651c36..251eb7856ce 100644 --- a/target/i386/tcg/sysemu/smm_helper.c +++ b/target/i386/tcg/system/smm_helper.c @@ -1,5 +1,5 @@ /* - * x86 SMM helpers (sysemu-only) + * x86 SMM helpers (system-only) * * Copyright (c) 2003 Fabrice Bellard * diff --git a/target/i386/tcg/sysemu/svm_helper.c b/target/i386/tcg/system/svm_helper.c similarity index 93% rename from target/i386/tcg/sysemu/svm_helper.c rename to target/i386/tcg/system/svm_helper.c index 2d27731b608..b27049b9ed1 100644 --- a/target/i386/tcg/sysemu/svm_helper.c +++ b/target/i386/tcg/system/svm_helper.c @@ -1,5 +1,5 @@ /* - * x86 SVM helpers (sysemu only) + * x86 SVM helpers (system only) * * Copyright (c) 2003 Fabrice Bellard * @@ -21,8 +21,8 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "exec/cputlb.h" +#include "accel/tcg/cpu-ldst.h" #include "tcg/helper-tcg.h" /* Secure Virtual Machine helpers */ @@ -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. */ - /* 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. */ + /* FIXME: Checks the reloaded host state for consistency. */ - /* 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/system/tcg-cpu.c b/target/i386/tcg/system/tcg-cpu.c new file mode 100644 index 00000000000..0538a4fd51a --- /dev/null +++ b/target/i386/tcg/system/tcg-cpu.c @@ -0,0 +1,84 @@ +/* + * i386 TCG cpu class initialization functions specific to system emulation + * + * 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 "cpu.h" +#include "tcg/helper-tcg.h" + +#include "system/system.h" +#include "qemu/units.h" +#include "system/address-spaces.h" +#include "system/memory.h" + +#include "tcg/tcg-cpu.h" + +static void tcg_cpu_machine_done(Notifier *n, void *unused) +{ + X86CPU *cpu = container_of(n, X86CPU, machine_done); + MemoryRegion *smram = + (MemoryRegion *) object_resolve_path("/machine/smram", NULL); + + if (smram) { + cpu->smram = g_new(MemoryRegion, 1); + memory_region_init_alias(cpu->smram, OBJECT(cpu), "smram", + smram, 0, 4 * GiB); + memory_region_set_enabled(cpu->smram, true); + memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, + cpu->smram, 1); + } +} + +bool tcg_cpu_realizefn(CPUState *cs, Error **errp) +{ + X86CPU *cpu = X86_CPU(cs); + + /* + * The realize order is important, since x86_cpu_realize() checks if + * nothing else has been set by the user (or by accelerators) in + * cpu->ucode_rev and cpu->phys_bits, and the memory regions + * initialized here are needed for the vcpu initialization. + * + * realize order: + * tcg_cpu -> host_cpu -> x86_cpu + */ + cpu->cpu_as_mem = g_new(MemoryRegion, 1); + cpu->cpu_as_root = g_new(MemoryRegion, 1); + + /* Outer container... */ + memory_region_init(cpu->cpu_as_root, OBJECT(cpu), "memory", ~0ull); + memory_region_set_enabled(cpu->cpu_as_root, true); + + /* + * ... with two regions inside: normal system memory with low + * priority, and... + */ + memory_region_init_alias(cpu->cpu_as_mem, OBJECT(cpu), "memory", + get_system_memory(), 0, ~0ull); + memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, cpu->cpu_as_mem, 0); + memory_region_set_enabled(cpu->cpu_as_mem, true); + + cs->num_ases = 2; + cpu_address_space_init(cs, 0, "cpu-memory", cs->memory); + cpu_address_space_init(cs, 1, "cpu-smm", cpu->cpu_as_root); + + /* ... SMRAM with higher priority, linked from /machine/smram. */ + cpu->machine_done.notify = tcg_cpu_machine_done; + qemu_add_machine_init_done_notifier(&cpu->machine_done); + return true; +} diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index b942c306d60..6f5dc06b3b9 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -21,8 +21,10 @@ #include "cpu.h" #include "helper-tcg.h" #include "qemu/accel.h" -#include "hw/core/accel-cpu.h" - +#include "accel/accel-cpu-target.h" +#include "exec/translation-block.h" +#include "exec/target_page.h" +#include "accel/tcg/cpu-ops.h" #include "tcg-cpu.h" /* Frob eflags into and out of the CPU temporary format. */ @@ -46,13 +48,37 @@ static void x86_cpu_exec_exit(CPUState *cs) env->eflags = cpu_compute_eflags(env); } +static TCGTBCPUState x86_get_tb_cpu_state(CPUState *cs) +{ + CPUX86State *env = cpu_env(cs); + uint32_t flags, cs_base; + vaddr pc; + + 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); + } + + return (TCGTBCPUState){ .pc = pc, .flags = flags, .cs_base = cs_base }; +} + static void x86_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { /* The instruction pointer is always up to date with CF_PCREL. */ if (!(tb_cflags(tb) & CF_PCREL)) { - CPUX86State *env = cs->env_ptr; - env->eip = tb->pc - tb->cs_base; + 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,17 +89,48 @@ 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 (tb_cflags(tb) & CF_PCREL) { - env->eip = (env->eip & TARGET_PAGE_MASK) | data[0]; + /* + * 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; } } +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); +} + #ifndef CONFIG_USER_ONLY static bool x86_debug_check_breakpoint(CPUState *cs) { @@ -83,14 +140,36 @@ static bool x86_debug_check_breakpoint(CPUState *cs) /* RF disables all architectural breakpoints. */ return !(env->eflags & RF_MASK); } -#endif -#include "hw/core/tcg-cpu-ops.h" +static void x86_cpu_exec_reset(CPUState *cs) +{ + CPUArchState *env = cpu_env(cs); + + cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0); + do_cpu_init(env_archcpu(env)); + cs->exception_index = EXCP_HALTED; +} + +static vaddr x86_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + return cpu_env(cs)->hflags & HF_CS64_MASK ? result : (uint32_t)result; +} +#endif -static const struct TCGCPUOps x86_tcg_ops = { +const TCGCPUOps x86_tcg_ops = { + .mttcg_supported = true, + .precise_smc = true, + /* + * The x86 has a strong memory model with some store-after-load re-ordering + */ + .guest_default_memory_order = TCG_MO_ALL & ~TCG_MO_ST_LD, .initialize = tcg_x86_init, + .translate_code = x86_translate_code, + .get_tb_cpu_state = x86_get_tb_cpu_state, .synchronize_from_tb = x86_cpu_synchronize_from_tb, .restore_state_to_opc = x86_restore_state_to_opc, + .mmu_index = x86_cpu_mmu_index, .cpu_exec_enter = x86_cpu_exec_enter, .cpu_exec_exit = x86_cpu_exec_exit, #ifdef CONFIG_USER_ONLY @@ -99,26 +178,19 @@ static const struct TCGCPUOps x86_tcg_ops = { .record_sigbus = x86_cpu_record_sigbus, #else .tlb_fill = x86_cpu_tlb_fill, + .pointer_wrap = x86_pointer_wrap, .do_interrupt = x86_cpu_do_interrupt, + .cpu_exec_halt = x86_cpu_exec_halt, .cpu_exec_interrupt = x86_cpu_exec_interrupt, + .cpu_exec_reset = x86_cpu_exec_reset, .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) -{ - /* for x86, all cpus use the same set of operations */ - cc->tcg_ops = &x86_tcg_ops; -} - -static void tcg_cpu_class_init(CPUClass *cc) -{ - cc->init_accel_cpu = 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 +212,43 @@ 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, const 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_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/tcg-cpu.h b/target/i386/tcg/tcg-cpu.h index 53a84944551..85bcd61678f 100644 --- a/target/i386/tcg/tcg-cpu.h +++ b/target/i386/tcg/tcg-cpu.h @@ -19,6 +19,8 @@ #ifndef TCG_CPU_H #define TCG_CPU_H +#include "cpu.h" + #define XSAVE_FCW_FSW_OFFSET 0x000 #define XSAVE_FTW_FOP_OFFSET 0x004 #define XSAVE_CWD_RIP_OFFSET 0x008 @@ -76,6 +78,10 @@ QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, zmm_hi256_state) != XSAVE_ZMM_HI256_OFF QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, hi16_zmm_state) != XSAVE_HI16_ZMM_OFFSET); QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, pkru_state) != XSAVE_PKRU_OFFSET); +extern const TCGCPUOps x86_tcg_ops; + bool tcg_cpu_realizefn(CPUState *cs, Error **errp); +int x86_mmu_index_pl(CPUX86State *env, unsigned pl); + #endif /* TCG_CPU_H */ diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index e0a622941cb..0cb87d02012 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -20,17 +20,18 @@ #include "qemu/host-utils.h" #include "cpu.h" -#include "disas/disas.h" -#include "exec/exec-all.h" +#include "accel/tcg/cpu-mmu-index.h" +#include "exec/translation-block.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" -#include "exec/cpu_ldst.h" #include "exec/translator.h" +#include "exec/target_page.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" @@ -38,6 +39,9 @@ #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 @@ -110,7 +114,6 @@ typedef struct DisasContext { #endif bool vex_w; /* used by AVX even on 32-bit processors */ bool jmp_opt; /* use direct block chaining for direct jumps */ - bool repz_opt; /* optimize jumps within repz instructions */ bool cc_op_dirty; CCOp cc_op; /* current CC operation */ @@ -122,6 +125,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 */ @@ -131,21 +135,44 @@ typedef struct DisasContext { TCGv T1; /* TCG local register indexes (only used inside old micro ops) */ - TCGv tmp0; - TCGv tmp4; TCGv_i32 tmp2_i32; - TCGv_i32 tmp3_i32; TCGv_i64 tmp1_i64; sigjmp_buf jmpbuf; + TCGOp *prev_insn_start; TCGOp *prev_insn_end; } DisasContext; -#define DISAS_EOB_ONLY DISAS_TARGET_0 -#define DISAS_EOB_NEXT DISAS_TARGET_1 -#define DISAS_EOB_INHIBIT_IRQ DISAS_TARGET_2 +/* + * 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 @@ -178,10 +205,10 @@ typedef struct DisasContext { #else #define CODE64(S) (((S)->flags & HF_CS64_MASK) != 0) #endif -#if defined(CONFIG_SOFTMMU) && !defined(TARGET_X86_64) -#define LMA(S) false -#else +#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 @@ -199,7 +226,7 @@ typedef struct DisasContext { #endif /* - * Many sysemu-only helpers are not reachable for user-only. + * Many system-only helpers are not reachable for user-only. * Define stub generators here, so that we need not either sprinkle * ifdefs through the translator, nor provide the helper function. */ @@ -210,7 +237,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) @@ -219,10 +245,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) @@ -230,28 +252,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, @@ -275,22 +281,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, @@ -299,7 +289,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, @@ -313,14 +303,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; @@ -329,7 +334,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); } @@ -343,20 +348,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) { @@ -420,30 +432,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 @@ -495,7 +483,7 @@ static inline void gen_op_mov_v_reg(DisasContext *s, MemOp ot, TCGv t0, int reg) { if (ot == MO_8 && byte_reg_is_xH(s, reg)) { - tcg_gen_extract_tl(t0, cpu_regs[reg - 4], 8, 8); + tcg_gen_shri_tl(t0, cpu_regs[reg - 4], 8); } else { tcg_gen_mov_tl(t0, cpu_regs[reg]); } @@ -515,17 +503,24 @@ static inline void gen_op_jmp_v(DisasContext *s, TCGv dest) s->pc_save = -1; } -static inline -void gen_op_add_reg_im(DisasContext *s, MemOp size, int reg, int32_t val) +static inline void gen_op_add_reg(DisasContext *s, MemOp size, int reg, TCGv val) { - tcg_gen_addi_tl(s->tmp0, cpu_regs[reg], val); - gen_op_mov_reg_v(s, size, reg, s->tmp0); + /* Using cpu_regs[reg] does not work for xH registers. */ + assert(size >= MO_16); + if (size == MO_16) { + TCGv temp = tcg_temp_new(); + tcg_gen_add_tl(temp, cpu_regs[reg], val); + gen_op_mov_reg_v(s, size, reg, temp); + } else { + tcg_gen_add_tl(cpu_regs[reg], cpu_regs[reg], val); + tcg_gen_ext_tl(cpu_regs[reg], cpu_regs[reg], size); + } } -static inline void gen_op_add_reg_T0(DisasContext *s, MemOp size, int reg) +static inline +void gen_op_add_reg_im(DisasContext *s, MemOp size, int reg, int32_t val) { - tcg_gen_add_tl(s->tmp0, cpu_regs[reg], s->T0); - gen_op_mov_reg_v(s, size, reg, s->tmp0); + gen_op_add_reg(s, size, reg, tcg_constant_tl(val)); } static inline void gen_op_ld_v(DisasContext *s, int idx, TCGv t0, TCGv a0) @@ -538,13 +533,17 @@ 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) @@ -552,23 +551,14 @@ static void gen_update_eip_cur(DisasContext *s) assert(s->pc_save != -1); if (tb_cflags(s->base.tb) & CF_PCREL) { tcg_gen_addi_tl(cpu_eip, cpu_eip, s->base.pc_next - s->pc_save); + } else if (CODE64(s)) { + tcg_gen_movi_tl(cpu_eip, s->base.pc_next); } else { - tcg_gen_movi_tl(cpu_eip, 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 (tb_cflags(s->base.tb) & CF_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; @@ -610,8 +600,10 @@ static TCGv eip_next_tl(DisasContext *s) 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)); } } @@ -622,22 +614,24 @@ static TCGv eip_cur_tl(DisasContext *s) 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; @@ -648,14 +642,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; @@ -672,102 +666,113 @@ static void gen_lea_v_seg(DisasContext *s, MemOp aflag, TCGv a0, 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 inline void gen_string_movl_A0_ESI(DisasContext *s) +static void gen_lea_v_seg(DisasContext *s, TCGv a0, + int def_seg, int ovr_seg) { - gen_lea_v_seg(s, s->aflag, cpu_regs[R_ESI], R_DS, s->override); + gen_lea_v_seg_dest(s, s->aflag, s->A0, a0, def_seg, ovr_seg); } -static inline void gen_string_movl_A0_EDI(DisasContext *s) +static inline void gen_string_movl_A0_ESI(DisasContext *s) { - gen_lea_v_seg(s, s->aflag, cpu_regs[R_EDI], R_ES, -1); + gen_lea_v_seg(s, cpu_regs[R_ESI], R_DS, s->override); } -static inline void gen_op_movl_T0_Dshift(DisasContext *s, MemOp ot) +static inline void gen_string_movl_A0_EDI(DisasContext *s) { - tcg_gen_ld32s_tl(s->T0, cpu_env, offsetof(CPUX86State, df)); - tcg_gen_shli_tl(s->T0, s->T0, ot); -}; + gen_lea_v_seg(s, cpu_regs[R_EDI], R_ES, -1); +} static TCGv gen_ext_tl(TCGv dst, TCGv src, MemOp size, bool sign) { - switch (size) { - case MO_8: - if (sign) { - tcg_gen_ext8s_tl(dst, src); - } else { - tcg_gen_ext8u_tl(dst, src); - } - return dst; - case MO_16: - if (sign) { - tcg_gen_ext16s_tl(dst, src); - } else { - tcg_gen_ext16u_tl(dst, src); - } - return dst; -#ifdef TARGET_X86_64 - case MO_32: - if (sign) { - tcg_gen_ext32s_tl(dst, src); - } else { - tcg_gen_ext32u_tl(dst, src); - } - return dst; -#endif - default: + if (size == MO_TL) { return src; } + if (!dst) { + dst = tcg_temp_new(); + } + tcg_gen_ext_tl(dst, src, size | (sign ? MO_SIGN : 0)); + return dst; } -static void gen_extu(MemOp ot, TCGv reg) +static void gen_op_j_ecx(DisasContext *s, TCGCond cond, TCGLabel *label1) { - gen_ext_tl(reg, reg, ot, false); + TCGv tmp = gen_ext_tl(NULL, cpu_regs[R_ECX], s->aflag, false); + + tcg_gen_brcondi_tl(cond, tmp, 0, label1); } -static void gen_exts(MemOp ot, TCGv reg) +static inline void gen_op_jz_ecx(DisasContext *s, TCGLabel *label1) { - gen_ext_tl(reg, reg, ot, true); + gen_op_j_ecx(s, TCG_COND_EQ, label1); } -static void gen_op_j_ecx(DisasContext *s, TCGCond cond, TCGLabel *label1) +static inline void gen_op_jnz_ecx(DisasContext *s, 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); + gen_op_j_ecx(s, TCG_COND_NE, label1); } -static inline void gen_op_jz_ecx(DisasContext *s, TCGLabel *label1) +static void gen_set_hflag(DisasContext *s, uint32_t mask) { - gen_op_j_ecx(s, TCG_COND_EQ, label1); + if ((s->flags & mask) == 0) { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_ld_i32(t, tcg_env, offsetof(CPUX86State, hflags)); + tcg_gen_ori_i32(t, t, mask); + tcg_gen_st_i32(t, tcg_env, offsetof(CPUX86State, hflags)); + s->flags |= mask; + } } -static inline void gen_op_jnz_ecx(DisasContext *s, TCGLabel *label1) +static void gen_reset_hflag(DisasContext *s, uint32_t mask) { - gen_op_j_ecx(s, TCG_COND_NE, label1); + if (s->flags & mask) { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_ld_i32(t, tcg_env, offsetof(CPUX86State, hflags)); + tcg_gen_andi_i32(t, t, ~mask); + tcg_gen_st_i32(t, tcg_env, offsetof(CPUX86State, hflags)); + s->flags &= ~mask; + } +} + +static void gen_set_eflags(DisasContext *s, target_ulong mask) +{ + TCGv t = tcg_temp_new(); + + tcg_gen_ld_tl(t, tcg_env, offsetof(CPUX86State, eflags)); + tcg_gen_ori_tl(t, t, mask); + tcg_gen_st_tl(t, tcg_env, offsetof(CPUX86State, eflags)); +} + +static void gen_reset_eflags(DisasContext *s, target_ulong mask) +{ + TCGv t = tcg_temp_new(); + + tcg_gen_ld_tl(t, tcg_env, offsetof(CPUX86State, eflags)); + tcg_gen_andi_tl(t, t, ~mask); + tcg_gen_st_tl(t, tcg_env, offsetof(CPUX86State, eflags)); } 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: g_assert_not_reached(); @@ -778,13 +783,13 @@ 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: g_assert_not_reached(); @@ -807,7 +812,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); @@ -816,7 +821,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)); } @@ -824,72 +829,38 @@ static bool gen_check_io(DisasContext *s, MemOp ot, TCGv_i32 port, #endif } -static void gen_movs(DisasContext *s, MemOp ot) +static void gen_movs(DisasContext *s, MemOp ot, TCGv dshift) { gen_string_movl_A0_ESI(s); gen_op_ld_v(s, ot, s->T0, s->A0); gen_string_movl_A0_EDI(s); gen_op_st_v(s, ot, s->T0, s->A0); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_ESI); - gen_op_add_reg_T0(s, s->aflag, R_EDI); -} - -static void gen_op_update1_cc(DisasContext *s) -{ - 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); + gen_op_add_reg(s, s->aflag, R_ESI, dshift); + gen_op_add_reg(s, s->aflag, R_EDI, dshift); } -/* compute all eflags to cc_src */ -static void gen_compute_eflags(DisasContext *s) +/* compute all eflags to reg */ +static void gen_mov_eflags(DisasContext *s, TCGv reg) { - TCGv zero, dst, src1, src2; + TCGv dst, src1, src2; + TCGv_i32 cc_op; int live, dead; if (s->cc_op == CC_OP_EFLAGS) { - 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_constant_tl(0); + TCGv zero = tcg_constant_tl(0); if (dead & USES_CC_DST) { dst = zero; } @@ -901,8 +872,18 @@ 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); + 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); } @@ -911,94 +892,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) { @@ -1009,73 +1010,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; @@ -1083,27 +1081,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: @@ -1111,6 +1106,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. */ @@ -1126,8 +1143,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); @@ -1137,24 +1154,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; @@ -1166,29 +1181,39 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) return cc; } -static void gen_setcc1(DisasContext *s, int b, TCGv reg) +static void gen_neg_setcc(DisasContext *s, int b, TCGv reg) { CCPrepare cc = gen_prepare_cc(s, b, reg); if (cc.no_setcond) { if (cc.cond == TCG_COND_EQ) { - tcg_gen_xori_tl(reg, cc.reg, 1); + tcg_gen_addi_tl(reg, cc.reg, -1); } else { - tcg_gen_mov_tl(reg, cc.reg); + tcg_gen_neg_tl(reg, cc.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.use_reg2) { + tcg_gen_negsetcond_tl(cc.cond, reg, cc.reg, cc.reg2); + } else { + tcg_gen_negsetcondi_tl(cc.cond, reg, cc.reg, cc.imm); } - if (cc.mask != -1) { - tcg_gen_andi_tl(reg, cc.reg, cc.mask); - cc.reg = reg; +} + +static void gen_setcc(DisasContext *s, int b, TCGv reg) +{ + CCPrepare cc = gen_prepare_cc(s, b, reg); + + if (cc.no_setcond) { + if (cc.cond == TCG_COND_EQ) { + tcg_gen_xori_tl(reg, cc.reg, 1); + } else { + tcg_gen_mov_tl(reg, cc.reg); + } + return; } + if (cc.use_reg2) { tcg_gen_setcond_tl(cc.cond, reg, cc.reg, cc.reg2); } else { @@ -1198,19 +1223,15 @@ static void gen_setcc1(DisasContext *s, int b, TCGv reg) static inline void gen_compute_eflags_c(DisasContext *s, TCGv reg) { - gen_setcc1(s, JCC_B << 1, reg); + gen_setcc(s, JCC_B << 1, 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. */ -static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1) + value 'b'. In the fast case, T0 is guaranteed not to be used. */ +static inline void gen_jcc_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 { @@ -1219,18 +1240,19 @@ 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. */ -static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) + 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_jcc(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 to CC_OP_EFLAGS (because it's CC_OP_DYNAMIC or because + * it's cheaper to just compute the flags)! + */ 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 { @@ -1238,55 +1260,46 @@ static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) } } -/* XXX: does not work with gdbstub "ice" single step - not a - serious problem */ -static TCGLabel *gen_jz_ecx_string(DisasContext *s) -{ - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - gen_op_jnz_ecx(s, l1); - gen_set_label(l2); - gen_jmp_rel_csize(s, 0, 1); - gen_set_label(l1); - return l2; -} - -static void gen_stos(DisasContext *s, MemOp ot) +static void gen_stos(DisasContext *s, MemOp ot, TCGv dshift) { - 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, dshift); } -static void gen_lods(DisasContext *s, MemOp ot) +static void gen_lods(DisasContext *s, MemOp ot, TCGv dshift) { 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, dshift); } -static void gen_scas(DisasContext *s, MemOp ot) +static void gen_scas(DisasContext *s, MemOp ot, TCGv dshift) { 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, dshift); } -static void gen_cmps(DisasContext *s, MemOp ot) +static void gen_cmps(DisasContext *s, MemOp ot, TCGv dshift) { gen_string_movl_A0_EDI(s); gen_op_ld_v(s, ot, s->T1, s->A0); gen_string_movl_A0_ESI(s); - gen_op(s, OP_CMPL, ot, OR_TMP0); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_ESI); - gen_op_add_reg_T0(s, s->aflag, R_EDI); + 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); + + 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) @@ -1298,118 +1311,216 @@ 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 */ } } -static void gen_ins(DisasContext *s, MemOp ot) +static void gen_ins(DisasContext *s, MemOp ot, TCGv dshift) { + TCGv_i32 port = tcg_temp_new_i32(); + gen_string_movl_A0_EDI(s); /* Note: we must do this dummy write first to be restartable in case of page fault. */ tcg_gen_movi_tl(s->T0, 0); gen_op_st_v(s, ot, s->T0, s->A0); - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); - tcg_gen_andi_i32(s->tmp2_i32, s->tmp2_i32, 0xffff); - gen_helper_in_func(ot, s->T0, s->tmp2_i32); + tcg_gen_trunc_tl_i32(port, cpu_regs[R_EDX]); + tcg_gen_andi_i32(port, port, 0xffff); + gen_helper_in_func(ot, s->T0, port); 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_bpt_io(s, s->tmp2_i32, ot); + gen_op_add_reg(s, s->aflag, R_EDI, dshift); + gen_bpt_io(s, port, ot); } -static void gen_outs(DisasContext *s, MemOp ot) +static void gen_outs(DisasContext *s, MemOp ot, TCGv dshift) { + TCGv_i32 port = tcg_temp_new_i32(); + TCGv_i32 value = tcg_temp_new_i32(); + gen_string_movl_A0_ESI(s); gen_op_ld_v(s, ot, s->T0, s->A0); - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); - 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_bpt_io(s, s->tmp2_i32, ot); + tcg_gen_trunc_tl_i32(port, cpu_regs[R_EDX]); + tcg_gen_andi_i32(port, port, 0xffff); + tcg_gen_trunc_tl_i32(value, s->T0); + gen_helper_out_func(ot, port, value); + gen_op_add_reg(s, s->aflag, R_ESI, dshift); + gen_bpt_io(s, port, ot); } -/* Generate jumps to current or next instruction */ -static void gen_repz(DisasContext *s, MemOp ot, - void (*fn)(DisasContext *s, MemOp ot)) +#define REP_MAX 65535 + +static void do_gen_rep(DisasContext *s, MemOp ot, TCGv dshift, + void (*fn)(DisasContext *s, MemOp ot, TCGv dshift), + bool is_repz_nz) { - 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); + TCGLabel *last = gen_new_label(); + TCGLabel *loop = gen_new_label(); + TCGLabel *done = gen_new_label(); + + target_ulong cx_mask = MAKE_64BIT_MASK(0, 8 << s->aflag); + TCGv cx_next = tcg_temp_new(); + /* - * A loop would cause two single step exceptions if ECX = 1 - * before rep string_insn + * Check if we must translate a single iteration only. Normally, HF_RF_MASK + * would also limit translation blocks to one instruction, so that gen_eob + * can reset the flag; here however RF is set throughout the repetition, so + * we can plow through until CX/ECX/RCX is zero. */ - if (s->repz_opt) { - gen_op_jz_ecx(s, l2); - } - gen_jmp_rel_csize(s, -cur_insn_len(s), 0); -} + bool can_loop = + (!(tb_cflags(s->base.tb) & (CF_USE_ICOUNT | CF_SINGLE_STEP)) + && !(s->flags & (HF_TF_MASK | HF_INHIBIT_IRQ_MASK))); + bool had_rf = s->flags & HF_RF_MASK; -#define GEN_REPZ(op) \ - static inline void gen_repz_ ## op(DisasContext *s, MemOp ot) \ - { gen_repz(s, ot, gen_##op); } + /* + * Even if EFLAGS.RF was set on entry (such as if we're on the second or + * later iteration and an exception or interrupt happened), force gen_eob() + * not to clear the flag. We do that ourselves after the last iteration. + */ + s->flags &= ~HF_RF_MASK; -static void gen_repz2(DisasContext *s, MemOp ot, int nz, - void (*fn)(DisasContext *s, MemOp ot)) -{ - TCGLabel *l2; - gen_update_cc_op(s); - l2 = gen_jz_ecx_string(s); - fn(s, ot); - gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + /* + * For CMPS/SCAS, the CC_OP after a memory fault could come from either + * the previous instruction or the string instruction; but because we + * arrange to keep CC_OP up to date all the time, just mark the whole + * insn as CC_OP_DYNAMIC. + * + * It's not a problem to do this even for instructions that do not + * modify the flags, so do it unconditionally. + */ gen_update_cc_op(s); - gen_jcc1(s, (JCC_Z << 1) | (nz ^ 1), l2); - if (s->repz_opt) { - gen_op_jz_ecx(s, l2); - } - gen_jmp_rel_csize(s, -cur_insn_len(s), 0); + tcg_set_insn_start_param(s->base.insn_start, 1, CC_OP_DYNAMIC); + + /* Any iteration at all? */ + tcg_gen_brcondi_tl(TCG_COND_TSTEQ, cpu_regs[R_ECX], cx_mask, done); + + /* + * From now on we operate on the value of CX/ECX/RCX that will be written + * back, which is stored in cx_next. There can be no carry, so we can zero + * extend here if needed and not do any expensive deposit operations later. + */ + tcg_gen_subi_tl(cx_next, cpu_regs[R_ECX], 1); +#ifdef TARGET_X86_64 + if (s->aflag == MO_32) { + tcg_gen_ext32u_tl(cx_next, cx_next); + cx_mask = ~0; + } +#endif + + /* + * The last iteration is handled outside the loop, so that cx_next + * can never underflow. + */ + if (can_loop) { + tcg_gen_brcondi_tl(TCG_COND_TSTEQ, cx_next, cx_mask, last); + } + + gen_set_label(loop); + fn(s, ot, dshift); + tcg_gen_mov_tl(cpu_regs[R_ECX], cx_next); + gen_update_cc_op(s); + + /* Leave if REP condition fails. */ + if (is_repz_nz) { + int nz = (s->prefix & PREFIX_REPNZ) ? 1 : 0; + gen_jcc_noeob(s, (JCC_Z << 1) | (nz ^ 1), done); + /* gen_prepare_eflags_z never changes cc_op. */ + assert(!s->cc_op_dirty); + } + + if (can_loop) { + tcg_gen_subi_tl(cx_next, cx_next, 1); + tcg_gen_brcondi_tl(TCG_COND_TSTNE, cx_next, REP_MAX, loop); + tcg_gen_brcondi_tl(TCG_COND_TSTEQ, cx_next, cx_mask, last); + } + + /* + * Traps or interrupts set RF_MASK if they happen after any iteration + * but the last. Set it here before giving the main loop a chance to + * execute. (For faults, seg_helper.c sets the flag as usual). + */ + if (!had_rf) { + gen_set_eflags(s, RF_MASK); + } + + /* Go to the main loop but reenter the same instruction. */ + gen_jmp_rel_csize(s, -cur_insn_len(s), 0); + + if (can_loop) { + /* + * The last iteration needs no conditional jump, even if is_repz_nz, + * because the repeats are ending anyway. + */ + gen_set_label(last); + set_cc_op(s, CC_OP_DYNAMIC); + fn(s, ot, dshift); + tcg_gen_mov_tl(cpu_regs[R_ECX], cx_next); + gen_update_cc_op(s); + } + + /* CX/ECX/RCX is zero, or REPZ/REPNZ broke the repetition. */ + gen_set_label(done); + set_cc_op(s, CC_OP_DYNAMIC); + if (had_rf) { + gen_reset_eflags(s, RF_MASK); + } + gen_jmp_rel_csize(s, 0, 1); +} + +static void do_gen_string(DisasContext *s, MemOp ot, + void (*fn)(DisasContext *s, MemOp ot, TCGv dshift), + bool is_repz_nz) +{ + TCGv dshift = tcg_temp_new(); + tcg_gen_ld32s_tl(dshift, tcg_env, offsetof(CPUX86State, df)); + tcg_gen_shli_tl(dshift, dshift, ot); + + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + do_gen_rep(s, ot, dshift, fn, is_repz_nz); + } else { + fn(s, ot, dshift); + } } -#define GEN_REPZ2(op) \ - static inline void gen_repz_ ## op(DisasContext *s, MemOp ot, int nz) \ - { gen_repz2(s, ot, nz, gen_##op); } +static void gen_repz(DisasContext *s, MemOp ot, + void (*fn)(DisasContext *s, MemOp ot, TCGv dshift)) +{ + do_gen_string(s, ot, fn, false); +} -GEN_REPZ(movs) -GEN_REPZ(stos) -GEN_REPZ(lods) -GEN_REPZ(ins) -GEN_REPZ(outs) -GEN_REPZ2(scas) -GEN_REPZ2(cmps) +static void gen_repz_nz(DisasContext *s, MemOp ot, + void (*fn)(DisasContext *s, MemOp ot, TCGv dshift)) +{ + do_gen_string(s, ot, fn, true); +} static void gen_helper_fp_arith_ST0_FT0(int op) { switch (op) { case 0: - gen_helper_fadd_ST0_FT0(cpu_env); + gen_helper_fadd_ST0_FT0(tcg_env); break; case 1: - gen_helper_fmul_ST0_FT0(cpu_env); + gen_helper_fmul_ST0_FT0(tcg_env); break; case 2: - gen_helper_fcom_ST0_FT0(cpu_env); + gen_helper_fcom_ST0_FT0(tcg_env); break; case 3: - gen_helper_fcom_ST0_FT0(cpu_env); + gen_helper_fcom_ST0_FT0(tcg_env); break; case 4: - gen_helper_fsub_ST0_FT0(cpu_env); + gen_helper_fsub_ST0_FT0(tcg_env); break; case 5: - gen_helper_fsubr_ST0_FT0(cpu_env); + gen_helper_fsubr_ST0_FT0(tcg_env); break; case 6: - gen_helper_fdiv_ST0_FT0(cpu_env); + gen_helper_fdiv_ST0_FT0(tcg_env); break; case 7: - gen_helper_fdivr_ST0_FT0(cpu_env); + gen_helper_fdivr_ST0_FT0(tcg_env); break; } } @@ -1420,22 +1531,22 @@ static void gen_helper_fp_arith_STN_ST0(int op, int 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; } } @@ -1444,7 +1555,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_constant_i32(trapno)); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(trapno)); s->base.is_jmp = DISAS_NORETURN; } @@ -1471,514 +1582,14 @@ 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_constant_tl(0); - if (cc_op_live[s->cc_op] & USES_CC_DST) { - tcg_gen_movcond_tl(TCG_COND_NE, cpu_cc_dst, count, z_tl, - result, cpu_cc_dst); - } 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); - } - - /* 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_constant_i32(0); - s32 = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(s32, count); - tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, s32, z32, s->tmp2_i32, oldop); - - /* 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_constant_i32(0); - t1 = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(t1, s->T1); - tcg_gen_movi_i32(s->tmp2_i32, CC_OP_ADCOX); - tcg_gen_movi_i32(s->tmp3_i32, CC_OP_EFLAGS); - tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, t1, t0, - s->tmp2_i32, s->tmp3_i32); - - /* 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: - g_assert_not_reached(); - } - } 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: - g_assert_not_reached(); - } - } - /* 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 TCGv 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); + TCGv cc_src = tcg_temp_new(); + TCGv tmp = tcg_temp_new(); + TCGv hishift; switch (ot) { case MO_16: @@ -1986,9 +1597,9 @@ static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, int op1, This means "shrdw C, B, A" shifts A:B:A >> C. Build the B:A portion by constructing it as a 32-bit value. */ if (is_right) { - tcg_gen_deposit_tl(s->tmp0, s->T0, s->T1, 16, 16); + tcg_gen_deposit_tl(tmp, s->T0, s->T1, 16, 16); tcg_gen_mov_tl(s->T1, s->T0); - tcg_gen_mov_tl(s->T0, s->tmp0); + tcg_gen_mov_tl(s->T0, tmp); } else { tcg_gen_deposit_tl(s->T1, s->T0, s->T1, 16, 16); } @@ -1999,121 +1610,64 @@ static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, int op1, case MO_32: #ifdef TARGET_X86_64 /* Concatenate the two 32-bit values and use a 64-bit shift. */ - tcg_gen_subi_tl(s->tmp0, count, 1); + tcg_gen_subi_tl(tmp, count, 1); if (is_right) { tcg_gen_concat_tl_i64(s->T0, s->T0, s->T1); - tcg_gen_shr_i64(s->tmp0, s->T0, s->tmp0); + tcg_gen_shr_i64(cc_src, s->T0, tmp); tcg_gen_shr_i64(s->T0, s->T0, count); } else { tcg_gen_concat_tl_i64(s->T0, s->T1, s->T0); - tcg_gen_shl_i64(s->tmp0, s->T0, s->tmp0); + tcg_gen_shl_i64(cc_src, s->T0, tmp); tcg_gen_shl_i64(s->T0, s->T0, count); - tcg_gen_shri_i64(s->tmp0, s->tmp0, 32); + tcg_gen_shri_i64(cc_src, cc_src, 32); tcg_gen_shri_i64(s->T0, s->T0, 32); } break; #endif default: - tcg_gen_subi_tl(s->tmp0, count, 1); + hishift = tcg_temp_new(); + tcg_gen_subi_tl(tmp, count, 1); if (is_right) { - tcg_gen_shr_tl(s->tmp0, s->T0, s->tmp0); + tcg_gen_shr_tl(cc_src, s->T0, tmp); - tcg_gen_subfi_tl(s->tmp4, mask + 1, count); + /* mask + 1 - count = mask - tmp = mask ^ tmp */ + tcg_gen_xori_tl(hishift, tmp, mask); tcg_gen_shr_tl(s->T0, s->T0, count); - tcg_gen_shl_tl(s->T1, s->T1, s->tmp4); + tcg_gen_shl_tl(s->T1, s->T1, hishift); } else { - tcg_gen_shl_tl(s->tmp0, s->T0, s->tmp0); + tcg_gen_shl_tl(cc_src, s->T0, tmp); + + /* mask + 1 - count = mask - tmp = mask ^ tmp */ + tcg_gen_xori_tl(hishift, tmp, mask); + tcg_gen_shl_tl(s->T0, s->T0, count); + tcg_gen_shr_tl(s->T1, s->T1, hishift); + if (ot == MO_16) { /* Only needed if count > 16, for Intel behaviour. */ - tcg_gen_subfi_tl(s->tmp4, 33, count); - tcg_gen_shr_tl(s->tmp4, s->T1, s->tmp4); - tcg_gen_or_tl(s->tmp0, s->tmp0, s->tmp4); + tcg_gen_shri_tl(tmp, s->T1, 1); + tcg_gen_or_tl(cc_src, cc_src, tmp); } - - tcg_gen_subfi_tl(s->tmp4, mask + 1, count); - tcg_gen_shl_tl(s->T0, s->T0, count); - tcg_gen_shr_tl(s->T1, s->T1, s->tmp4); } - tcg_gen_movi_tl(s->tmp4, 0); - tcg_gen_movcond_tl(TCG_COND_EQ, s->T1, count, s->tmp4, - s->tmp4, s->T1); + tcg_gen_movcond_tl(TCG_COND_EQ, s->T1, + count, tcg_constant_tl(0), + tcg_constant_tl(0), s->T1); 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); + return cc_src; } -static void gen_shift(DisasContext *s1, int op, MemOp ot, int d, int s) +#define X86_MAX_INSN_LENGTH 15 + +static uint64_t advance_pc(CPUX86State *env, DisasContext *s, int num_bytes) { - 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 - -static uint64_t advance_pc(CPUX86State *env, DisasContext *s, int num_bytes) -{ - uint64_t pc = s->pc; - - /* This is a subsequent insn that crosses a page boundary. */ - if (s->base.num_insns > 1 && - !is_same_page(&s->base, s->pc + num_bytes - 1)) { - siglongjmp(s->jmpbuf, 2); + uint64_t pc = s->pc; + + /* This is a subsequent insn that crosses a page boundary. */ + if (s->base.num_insns > 1 && + !translator_is_same_page(&s->base, s->pc + num_bytes - 1)) { + siglongjmp(s->jmpbuf, 2); } s->pc += num_bytes; @@ -2124,9 +1678,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); } @@ -2139,11 +1692,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)); @@ -2163,16 +1711,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; @@ -2201,7 +1741,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); @@ -2329,70 +1869,52 @@ 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); + TCGv_i32 t32 = tcg_temp_new_i32(); + TCGv_i64 t64 = tcg_temp_new_i64(); - tcg_gen_extu_tl_i64(s->tmp1_i64, ea); + tcg_gen_extu_tl_i64(t64, ea); if (!CODE64(s)) { - tcg_gen_ext32u_i64(s->tmp1_i64, s->tmp1_i64); + tcg_gen_ext32u_i64(t64, t64); } - 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); + tcg_gen_setcond_i64(cond, t64, t64, bndv); + tcg_gen_extrl_i64_i32(t32, t64); + gen_helper_bndck(tcg_env, t32); } -/* used for LEA and MOV AX, mem */ -static void gen_add_A0_ds_seg(DisasContext *s) +/* generate modrm load of memory or register. */ +static void gen_ld_modrm(DisasContext *s, X86DecodedInsn *decode, MemOp ot) { - gen_lea_v_seg(s, s->aflag, s->A0, R_DS, s->override); + int modrm = s->modrm; + int mod, rm; + + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + if (mod == 3) { + gen_op_mov_v_reg(s, ot, s->T0, rm); + } else { + gen_lea_modrm(s, decode); + gen_op_ld_v(s, ot, s->T0, s->A0); + } } -/* 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 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) { - 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_reg_v(s, ot, rm, s->T0); } 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_st_v(s, ot, s->T0, s->A0); } } @@ -2469,83 +1991,106 @@ 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); } -} - -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_set_label(taken); gen_jmp_rel(s, s->dflag, diff, 0); } -static void gen_cmovcc1(CPUX86State *env, DisasContext *s, MemOp ot, int b, - int modrm, int reg) +static void gen_cmovcc(DisasContext *s, int b, TCGv dest, TCGv src) { - CCPrepare cc; - - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + CCPrepare cc = gen_prepare_cc(s, b, NULL); - 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_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); -} - -static inline void gen_op_movl_T0_seg(DisasContext *s, X86Seg seg_reg) -{ - tcg_gen_ld32u_tl(s->T0, cpu_env, - offsetof(CPUX86State,segs[seg_reg].selector)); + tcg_gen_movcond_tl(cc.cond, dest, cc.reg, cc.reg2, src, dest); } -static inline void gen_op_movl_seg_T0_vm(DisasContext *s, X86Seg seg_reg) +static void gen_op_movl_seg_real(DisasContext *s, X86Seg seg_reg, TCGv seg) { - 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, bool inhibit_irq) { if (PE(s) && !VM86(s)) { - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_load_seg(cpu_env, tcg_constant_i32(seg_reg), s->tmp2_i32); - /* abort translation because the addseg value may change or - because ss32 may change. For R_SS, translation must always - stop as a special handling must be done to disable hardware - interrupts for the next instruction */ - if (seg_reg == R_SS) { - s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ; - } else if (CODE32(s) && seg_reg < R_FS) { + TCGv_i32 sel = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(sel, src); + gen_helper_load_seg(tcg_env, tcg_constant_i32(seg_reg), sel); + + /* + * For moves to SS, the SS32 flag may change. For CODE32 only, changes + * to SS, DS and ES may change the ADDSEG flags. + */ + if (seg_reg == R_SS || (CODE32(s) && seg_reg < R_FS)) { s->base.is_jmp = DISAS_EOB_NEXT; } } else { - gen_op_movl_seg_T0_vm(s, seg_reg); - if (seg_reg == R_SS) { - s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ; - } + gen_op_movl_seg_real(s, seg_reg, src); + } + + /* + * For MOV or POP to SS (but not LSS) translation must always + * stop as a special handling must be done to disable hardware + * interrupts for the next instruction. + * + * This is the last instruction, so it's okay to overwrite + * HF_TF_MASK; the next TB will start with the flag set. + * + * DISAS_EOB_INHIBIT_IRQ is a superset of DISAS_EOB_NEXT which + * might have been set above. + */ + if (inhibit_irq) { + s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ; + s->flags &= ~HF_TF_MASK; + } +} + +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) @@ -2554,7 +2099,7 @@ static void gen_svm_check_intercept(DisasContext *s, uint32_t type) if (likely(!GUEST(s))) { return; } - gen_helper_svm_check_intercept(cpu_env, tcg_constant_i32(type)); + gen_helper_svm_check_intercept(tcg_env, tcg_constant_i32(type)); } static inline void gen_stack_update(DisasContext *s, int addend) @@ -2562,24 +2107,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); } @@ -2589,8 +2137,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; } @@ -2600,21 +2148,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); } @@ -2623,7 +2164,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; @@ -2633,8 +2173,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); } @@ -2645,37 +2184,37 @@ 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; if (level != 0) { int i; + if (level > 1) { + TCGv fp = tcg_temp_new(); - /* 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_op_ld_v(s, d_ot, s->tmp0, s->A0); + /* Copy level-1 pointers from the previous frame. */ + for (i = 1; i < level; ++i) { + gen_lea_ss_ofs(s, s->A0, cpu_regs[R_EBP], -size * i); + gen_op_ld_v(s, d_ot, fp, 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_op_st_v(s, d_ot, s->tmp0, s->A0); + gen_lea_ss_ofs(s, s->A0, s->T1, -size * i); + gen_op_st_v(s, d_ot, fp, 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); @@ -2687,7 +2226,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); @@ -2710,7 +2249,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); @@ -2720,55 +2259,15 @@ 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; } -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_ori_i32(t, t, mask); - tcg_gen_st_i32(t, cpu_env, offsetof(CPUX86State, hflags)); - s->flags |= mask; - } -} - -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_andi_i32(t, t, ~mask); - tcg_gen_st_i32(t, cpu_env, offsetof(CPUX86State, hflags)); - s->flags &= ~mask; - } -} - -static void gen_set_eflags(DisasContext *s, target_ulong mask) -{ - TCGv t = tcg_temp_new(); - - tcg_gen_ld_tl(t, cpu_env, offsetof(CPUX86State, eflags)); - tcg_gen_ori_tl(t, t, mask); - tcg_gen_st_tl(t, cpu_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_andi_tl(t, t, ~mask); - tcg_gen_st_tl(t, cpu_env, offsetof(CPUX86State, eflags)); -} - /* Clear BND registers during legacy branches. */ static void gen_bnd_jmp(DisasContext *s) { @@ -2778,65 +2277,48 @@ 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) { + if (s->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) { + 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); + s->base.is_jmp = DISAS_NORETURN; } /* Jump to eip+diff, truncating the result to OT. */ @@ -2847,6 +2329,8 @@ 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) { @@ -2860,9 +2344,6 @@ 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 (tb_cflags(s->base.tb) & CF_PCREL) { tcg_gen_addi_tl(cpu_eip, cpu_eip, new_pc - s->pc_save); /* @@ -2870,14 +2351,15 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) * no extra masking to apply (data16 branch in code32, see above), * then we have also proven that the addition does not wrap. */ - if (!use_goto_tb || !is_same_page(&s->base, new_pc)) { + if (!use_goto_tb || !translator_is_same_page(&s->base, new_pc)) { tcg_gen_andi_tl(cpu_eip, cpu_eip, mask); use_goto_tb = false; } + } else if (!CODE64(s)) { + new_pc = (uint32_t)(new_eip + s->cs_base); } - if (use_goto_tb && - translator_use_goto_tb(&s->base, new_eip + s->cs_base)) { + if (use_goto_tb && translator_use_goto_tb(&s->base, new_pc)) { /* jump to same page: we can use a direct jump */ tcg_gen_goto_tb(tb_num); if (!(tb_cflags(s->base.tb) & CF_PCREL)) { @@ -2890,9 +2372,9 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) 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 */ } } } @@ -2907,1035 +2389,657 @@ 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))); - - 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))); + TCGv_i128 t0 = tcg_temp_new_i128(); + TCGv_i128 t1 = tcg_temp_new_i128(); + TCGv a0_hi = tcg_temp_new(); + + tcg_gen_qemu_ld_i128(t0, s->A0, mem_index, mop | (align ? MO_ALIGN_32 : 0)); + tcg_gen_addi_tl(a0_hi, s->A0, 16); + tcg_gen_qemu_ld_i128(t1, a0_hi, 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); - 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); -} - -#include "decode-new.h" -#include "emit.c.inc" -#include "decode-new.c.inc" - -static void gen_cmpxchg8b(DisasContext *s, CPUX86State *env, int modrm) -{ - TCGv_i64 cmp, val, old; - TCGv Z; - - gen_lea_modrm(env, s, modrm); - - cmp = tcg_temp_new_i64(); - val = tcg_temp_new_i64(); - old = tcg_temp_new_i64(); + TCGv_i128 t = tcg_temp_new_i128(); + TCGv a0_hi = tcg_temp_new(); - /* 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); + 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(a0_hi, s->A0, 16); + tcg_gen_ld_i128(t, tcg_env, offset + offsetof(YMMReg, YMM_X(1))); + tcg_gen_qemu_st_i128(t, a0_hi, mem_index, mop); } -#ifdef TARGET_X86_64 -static void gen_cmpxchg16b(DisasContext *s, CPUX86State *env, int modrm) -{ - MemOp mop = MO_TE | MO_128 | MO_ALIGN; - TCGv_i64 t0, t1; - TCGv_i128 cmp, val; - - gen_lea_modrm(env, s, modrm); - - cmp = tcg_temp_new_i128(); - val = tcg_temp_new_i128(); - tcg_gen_concat_i64_i128(cmp, cpu_regs[R_EAX], cpu_regs[R_EDX]); - tcg_gen_concat_i64_i128(val, cpu_regs[R_EBX], cpu_regs[R_ECX]); - - /* Only require atomic with LOCK; non-parallel handled in generator. */ - if (s->prefix & PREFIX_LOCK) { - tcg_gen_atomic_cmpxchg_i128(val, s->A0, cmp, val, s->mem_index, mop); - } else { - tcg_gen_nonatomic_cmpxchg_i128(val, s->A0, cmp, val, s->mem_index, mop); - } - - tcg_gen_extr_i128_i64(s->T0, s->T1, val); - - /* Determine success after the fact. */ - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - tcg_gen_xor_i64(t0, s->T0, cpu_regs[R_EAX]); - tcg_gen_xor_i64(t1, s->T1, cpu_regs[R_EDX]); - tcg_gen_or_i64(t0, t0, t1); - - /* Update Z. */ - gen_compute_eflags(s); - tcg_gen_setcondi_i64(TCG_COND_EQ, t0, t0, 0); - tcg_gen_deposit_tl(cpu_cc_src, cpu_cc_src, t0, ctz32(CC_Z), 1); - - /* - * Extract the result values for the register pair. We may do this - * unconditionally, because on success (Z=1), the old value matches - * the previous value in RDX:RAX. - */ - tcg_gen_mov_i64(cpu_regs[R_EAX], s->T0); - tcg_gen_mov_i64(cpu_regs[R_EDX], s->T1); -} -#endif +#include "emit.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; - - 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(); - } - - prefixes = 0; - - next_byte: - s->prefix = prefixes; - b = x86_ldub_code(env, s); - /* Collect prefixes. */ - switch (b) { - default: - break; - case 0x0f: - b = x86_ldub_code(env, s) + 0x100; - break; - case 0xf3: - prefixes |= PREFIX_REPZ; - prefixes &= ~PREFIX_REPNZ; - goto next_byte; - case 0xf2: - prefixes |= PREFIX_REPNZ; - prefixes &= ~PREFIX_REPZ; - goto next_byte; - case 0xf0: - prefixes |= PREFIX_LOCK; - 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 */ - - 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. */ - break; - } - disas_insn_new(s, cpu, b); - return s->pc; - } - break; - } + bool update_fip = true; + int b = decode->b; + int modrm = s->modrm; + int mod, rm, op; - /* 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); - } 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; - } + 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; } - - s->prefix = prefixes; - s->aflag = aflag; - s->dflag = dflag; - - /* 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); + 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; + + tcg_gen_mov_tl(last_addr, ea); + gen_lea_v_seg(s, ea, decode->mem.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_helper_flds_FT0(tcg_env, s->tmp2_i32); + break; + case 1: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + gen_helper_fildl_FT0(tcg_env, s->tmp2_i32); + break; + case 2: + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEUQ); + gen_helper_fldl_FT0(tcg_env, s->tmp1_i64); + break; + case 3: + default: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LESW); + gen_helper_fildl_FT0(tcg_env, s->tmp2_i32); 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_helper_fp_arith_ST0_FT0(op1); + if (op1 == 3) { + /* fcomp needs pop */ + gen_helper_fpop(tcg_env); } - 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 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_helper_flds_ST0(tcg_env, s->tmp2_i32); + break; + case 1: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + gen_helper_fildl_ST0(tcg_env, s->tmp2_i32); + break; + case 2: + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEUQ); + gen_helper_fldl_ST0(tcg_env, s->tmp1_i64); + break; + case 3: + default: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LESW); + gen_helper_fildl_ST0(tcg_env, 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_helper_fpop(tcg_env); break; - case 0x83: - val = (int8_t)insn_get(env, s, MO_8); + default: + switch (op >> 4) { + case 0: + gen_helper_fsts_ST0(s->tmp2_i32, tcg_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + break; + case 1: + gen_helper_fistl_ST0(s->tmp2_i32, tcg_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + break; + case 2: + gen_helper_fstl_ST0(s->tmp1_i64, tcg_env); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEUQ); + break; + case 3: + default: + gen_helper_fist_ST0(s->tmp2_i32, tcg_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUW); + break; + } + if ((op & 7) == 3) { + gen_helper_fpop(tcg_env); + } break; } - tcg_gen_movi_tl(s->T1, val); - gen_op(s, op, ot, opreg); + break; + case 0x0c: /* fldenv mem */ + gen_helper_fldenv(tcg_env, s->A0, + tcg_constant_i32(s->dflag - 1)); + update_fip = update_fdp = false; + break; + case 0x0d: /* fldcw mem */ + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUW); + gen_helper_fldcw(tcg_env, s->tmp2_i32); + update_fip = update_fdp = false; + 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_helper_fpop(tcg_env); + 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_helper_fpop(tcg_env); + break; + case 0x3d: /* fildll */ + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEUQ); + gen_helper_fildll_ST0(tcg_env, s->tmp1_i64); + break; + case 0x3f: /* fistpll */ + gen_helper_fistll_ST0(s->tmp1_i64, tcg_env); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEUQ); + gen_helper_fpop(tcg_env); + break; + default: + goto illegal_op; } - 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); + if (update_fdp) { + int last_seg = s->override >= 0 ? s->override : decode->mem.def_seg; - 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); + 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)); } - - 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); + } else { + /* register float ops */ + int opreg = rm; + + switch (op) { + case 0x08: /* fld sti */ + gen_helper_fpush(tcg_env); + gen_helper_fmov_ST0_STN(tcg_env, + tcg_constant_i32((opreg + 1) & 7)); + break; + case 0x09: /* fxchg sti */ + case 0x29: /* fxchg4 sti, undocumented op */ + case 0x39: /* fxchg7 sti, undocumented op */ + gen_helper_fxchg_ST0_STN(tcg_env, tcg_constant_i32(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 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); - } + case 0x0c: /* grp d9/4 */ + switch (rm) { + case 0: /* fchs */ + gen_helper_fchs_ST0(tcg_env); + break; + case 1: /* fabs */ + gen_helper_fabs_ST0(tcg_env); + break; + case 4: /* ftst */ + gen_helper_fldz_FT0(tcg_env); + gen_helper_fcom_ST0_FT0(tcg_env); + break; + case 5: /* fxam */ + gen_helper_fxam_ST0(tcg_env); + break; + default: + goto illegal_op; } break; - case 3: /* neg */ - if (s->prefix & PREFIX_LOCK) { - TCGLabel *label1; - TCGv a0, t0, t1, t2; - - if (mod == 3) { + case 0x0d: /* grp d9/5 */ + { + switch (rm) { + case 0: + gen_helper_fpush(tcg_env); + gen_helper_fld1_ST0(tcg_env); + break; + case 1: + gen_helper_fpush(tcg_env); + gen_helper_fldl2t_ST0(tcg_env); + break; + case 2: + gen_helper_fpush(tcg_env); + gen_helper_fldl2e_ST0(tcg_env); + break; + case 3: + gen_helper_fpush(tcg_env); + gen_helper_fldpi_ST0(tcg_env); + break; + case 4: + gen_helper_fpush(tcg_env); + gen_helper_fldlg2_ST0(tcg_env); + break; + case 5: + gen_helper_fpush(tcg_env); + gen_helper_fldln2_ST0(tcg_env); + break; + case 6: + gen_helper_fpush(tcg_env); + gen_helper_fldz_ST0(tcg_env); + break; + default: goto illegal_op; } - a0 = s->A0; - t0 = s->T0; - label1 = gen_new_label(); - - 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_gen_brcond_tl(TCG_COND_NE, t0, t2, label1); - - tcg_gen_neg_tl(s->T0, 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); + case 0x0e: /* grp d9/6 */ + switch (rm) { + case 0: /* f2xm1 */ + gen_helper_f2xm1(tcg_env); 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); + case 1: /* fyl2x */ + gen_helper_fyl2x(tcg_env); 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); + case 2: /* fptan */ + gen_helper_fptan(tcg_env); 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); + case 3: /* fpatan */ + gen_helper_fpatan(tcg_env); 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); + case 4: /* fxtract */ + gen_helper_fxtract(tcg_env); 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); + case 5: /* fprem1 */ + gen_helper_fprem1(tcg_env); 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); + case 6: /* fdecstp */ + gen_helper_fdecstp(tcg_env); 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); + default: + case 7: /* fincstp */ + gen_helper_fincstp(tcg_env); break; -#endif } break; - case 6: /* div */ - switch(ot) { - case MO_8: - gen_helper_divb_AL(cpu_env, s->T0); + case 0x0f: /* grp d9/7 */ + switch (rm) { + case 0: /* fprem */ + gen_helper_fprem(tcg_env); break; - case MO_16: - gen_helper_divw_AX(cpu_env, s->T0); + case 1: /* fyl2xp1 */ + gen_helper_fyl2xp1(tcg_env); break; - default: - case MO_32: - gen_helper_divl_EAX(cpu_env, s->T0); + case 2: /* fsqrt */ + gen_helper_fsqrt(tcg_env); break; -#ifdef TARGET_X86_64 - case MO_64: - gen_helper_divq_EAX(cpu_env, s->T0); + case 3: /* fsincos */ + gen_helper_fsincos(tcg_env); break; -#endif - } - break; - case 7: /* idiv */ - switch(ot) { - case MO_8: - gen_helper_idivb_AL(cpu_env, s->T0); + case 5: /* fscale */ + gen_helper_fscale(tcg_env); break; - case MO_16: - gen_helper_idivw_AX(cpu_env, s->T0); + case 4: /* frndint */ + gen_helper_frndint(tcg_env); break; - default: - case MO_32: - gen_helper_idivl_EAX(cpu_env, s->T0); + case 6: /* fsin */ + gen_helper_fsin(tcg_env); break; -#ifdef TARGET_X86_64 - case MO_64: - gen_helper_idivq_EAX(cpu_env, s->T0); + default: + case 7: /* fcos */ + gen_helper_fcos(tcg_env); break; -#endif } break; - default: - goto unknown_op; - } - 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; - 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); + op1 = op & 7; + if (op >= 0x20) { + gen_helper_fp_arith_STN_ST0(op1, opreg); + if (op >= 0x30) { + gen_helper_fpop(tcg_env); + } + } else { + gen_helper_fmov_FT0_STN(tcg_env, + tcg_constant_i32(opreg)); + gen_helper_fp_arith_ST0_FT0(op1); + } } - } - 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); + case 0x02: /* fcom */ + case 0x22: /* fcom2, undocumented op */ + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fcom_ST0_FT0(tcg_env); + break; + case 0x03: /* fcomp */ + case 0x23: /* fcomp3, undocumented op */ + case 0x32: /* fcomp5, undocumented op */ + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fcom_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); + break; + case 0x15: /* da/5 */ + switch (rm) { + case 1: /* fucompp */ + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(1)); + gen_helper_fucom_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); + gen_helper_fpop(tcg_env); + break; + default: + goto illegal_op; } - 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) { + 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; + break; + case 4: /* fsetpm (287 only, just do nop here) */ + break; + default: 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); + case 0x1d: /* fucomi */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; } - gen_op_jmp_v(s, s->T0); - gen_bnd_jmp(s); - s->base.is_jmp = DISAS_JUMP; + gen_update_cc_op(s); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fucomi_ST0_FT0(tcg_env); + assume_cc_op(s, CC_OP_EFLAGS); break; - case 5: /* ljmp Ev */ - if (mod == 3) { + case 0x1e: /* fcomi */ + if (!(s->cpuid_features & CPUID_CMOV)) { 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); + gen_update_cc_op(s); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(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_helper_fmov_STN_ST0(tcg_env, tcg_constant_i32(opreg)); + break; + case 0x2b: /* fstp sti */ + case 0x0b: /* fstp1 sti, undocumented op */ + case 0x3a: /* fstp8 sti, undocumented op */ + case 0x3b: /* fstp9 sti, undocumented op */ + gen_helper_fmov_STN_ST0(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fpop(tcg_env); + break; + case 0x2c: /* fucom st(i) */ + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fucom_ST0_FT0(tcg_env); + break; + case 0x2d: /* fucomp st(i) */ + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fucom_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); + break; + case 0x33: /* de/3 */ + switch (rm) { + case 1: /* fcompp */ + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(1)); + gen_helper_fcom_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); + gen_helper_fpop(tcg_env); + break; + default: + goto illegal_op; } - 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: - g_assert_not_reached(); - } - 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: - g_assert_not_reached(); - } - 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); + case 0x38: /* ffreep sti, undocumented op */ + gen_helper_ffree_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fpop(tcg_env); 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); + 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; } - 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); - } + break; + case 0x3d: /* fucomip */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; } - /* - * 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); - } - break; - case 0x1c7: /* cmpxchg8b */ - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - switch ((modrm >> 3) & 7) { - case 1: /* CMPXCHG8, CMPXCHG16 */ - if (mod == 3) { + gen_update_cc_op(s); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fucomi_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); + assume_cc_op(s, CC_OP_EFLAGS); + break; + case 0x3e: /* fcomip */ + if (!(s->cpuid_features & CPUID_CMOV)) { goto illegal_op; } -#ifdef TARGET_X86_64 - if (dflag == MO_64) { - if (!(s->cpuid_ext_features & CPUID_EXT_CX16)) { + gen_update_cc_op(s); + gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg)); + gen_helper_fcomi_ST0_FT0(tcg_env); + gen_helper_fpop(tcg_env); + 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; } - gen_cmpxchg16b(s, env, modrm); - break; - } -#endif - if (!(s->cpuid_features & CPUID_CX8)) { - goto illegal_op; + op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1); + l1 = gen_new_label(); + gen_jcc_noeob(s, op1, l1); + gen_helper_fmov_ST0_STN(tcg_env, + tcg_constant_i32(opreg)); + gen_set_label(l1); } - gen_cmpxchg8b(s, env, modrm); break; + default: + goto illegal_op; + } + } + + 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; - case 7: /* RDSEED, RDPID with f3 prefix */ + /* now check op code */ + switch (b) { + case 0x1c7: /* RDSEED, RDPID with f3 prefix */ + mod = (modrm >> 6) & 3; + switch ((modrm >> 3) & 7) { + case 7: if (mod != 3 || - (s->prefix & (PREFIX_LOCK | PREFIX_REPNZ))) { + (s->prefix & PREFIX_REPNZ)) { goto illegal_op; } if (s->prefix & PREFIX_REPZ) { - if (!(s->cpuid_ext_features & CPUID_7_0_ECX_RDPID)) { + if (!(s->cpuid_7_0_ecx_features & CPUID_7_0_ECX_RDPID)) { goto illegal_op; } - gen_helper_rdpid(s->T0, cpu_env); + gen_helper_rdpid(s->T0, tcg_env); rm = (modrm & 7) | REX_B(s); gen_op_mov_reg_v(s, dflag, rm, s->T0); break; @@ -3948,1818 +3052,24 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) case 6: /* RDRAND */ if (mod != 3 || - (s->prefix & (PREFIX_LOCK | PREFIX_REPZ | PREFIX_REPNZ)) || - !(s->cpuid_ext_features & CPUID_EXT_RDRAND)) { - goto illegal_op; - } - do_rdrand: - translator_io_start(&s->base); - gen_helper_rdrand(s->T0, cpu_env); - rm = (modrm & 7) | REX_B(s); - gen_op_mov_reg_v(s, dflag, rm, s->T0); - set_cc_op(s, CC_OP_EFLAGS); - break; - - default: - goto illegal_op; - } - 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_constant_tl(x86_ldub_code(env, s)); - gen_shiftd_rm_T1(s, ot, opreg, op, 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_helper_flds_FT0(cpu_env, s->tmp2_i32); - break; - case 1: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - gen_helper_fildl_FT0(cpu_env, s->tmp2_i32); - break; - case 2: - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - gen_helper_fldl_FT0(cpu_env, s->tmp1_i64); - break; - case 3: - default: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LESW); - gen_helper_fildl_FT0(cpu_env, s->tmp2_i32); - break; - } - - gen_helper_fp_arith_ST0_FT0(op1); - if (op1 == 3) { - /* fcomp needs pop */ - gen_helper_fpop(cpu_env); - } - } - 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_helper_flds_ST0(cpu_env, s->tmp2_i32); - break; - case 1: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - gen_helper_fildl_ST0(cpu_env, s->tmp2_i32); - break; - case 2: - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - gen_helper_fldl_ST0(cpu_env, s->tmp1_i64); - break; - case 3: - default: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LESW); - gen_helper_fildl_ST0(cpu_env, s->tmp2_i32); - 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_helper_fpop(cpu_env); - break; - default: - switch (op >> 4) { - case 0: - gen_helper_fsts_ST0(s->tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - break; - case 1: - gen_helper_fistl_ST0(s->tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - break; - case 2: - gen_helper_fstl_ST0(s->tmp1_i64, cpu_env); - 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_helper_fpop(cpu_env); - } - break; - } - break; - case 0x0c: /* fldenv mem */ - gen_helper_fldenv(cpu_env, s->A0, - tcg_constant_i32(dflag - 1)); - update_fip = update_fdp = false; - break; - case 0x0d: /* fldcw mem */ - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUW); - gen_helper_fldcw(cpu_env, s->tmp2_i32); - update_fip = update_fdp = false; - break; - case 0x0e: /* fnstenv mem */ - gen_helper_fstenv(cpu_env, s->A0, - tcg_constant_i32(dflag - 1)); - update_fip = update_fdp = false; - break; - case 0x0f: /* fnstcw mem */ - gen_helper_fnstcw(s->tmp2_i32, cpu_env); - 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_helper_fpop(cpu_env); - break; - case 0x2c: /* frstor mem */ - gen_helper_frstor(cpu_env, s->A0, - tcg_constant_i32(dflag - 1)); - update_fip = update_fdp = false; - break; - case 0x2e: /* fnsave mem */ - gen_helper_fsave(cpu_env, s->A0, - tcg_constant_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_helper_fpop(cpu_env); - break; - case 0x3d: /* fildll */ - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - gen_helper_fildll_ST0(cpu_env, s->tmp1_i64); - break; - case 0x3f: /* fistpll */ - gen_helper_fistll_ST0(s->tmp1_i64, cpu_env); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - gen_helper_fpop(cpu_env); - 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)); - } - } else { - /* register float ops */ - opreg = rm; - - switch (op) { - case 0x08: /* fld sti */ - gen_helper_fpush(cpu_env); - gen_helper_fmov_ST0_STN(cpu_env, - tcg_constant_i32((opreg + 1) & 7)); - break; - case 0x09: /* fxchg sti */ - case 0x29: /* fxchg4 sti, undocumented op */ - case 0x39: /* fxchg7 sti, undocumented op */ - gen_helper_fxchg_ST0_STN(cpu_env, tcg_constant_i32(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(cpu_env); - update_fip = false; - break; - default: - goto unknown_op; - } - break; - case 0x0c: /* grp d9/4 */ - switch (rm) { - case 0: /* fchs */ - gen_helper_fchs_ST0(cpu_env); - break; - case 1: /* fabs */ - gen_helper_fabs_ST0(cpu_env); - break; - case 4: /* ftst */ - gen_helper_fldz_FT0(cpu_env); - gen_helper_fcom_ST0_FT0(cpu_env); - 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_helper_fpush(cpu_env); - gen_helper_fld1_ST0(cpu_env); - break; - case 1: - gen_helper_fpush(cpu_env); - gen_helper_fldl2t_ST0(cpu_env); - break; - case 2: - gen_helper_fpush(cpu_env); - gen_helper_fldl2e_ST0(cpu_env); - break; - case 3: - gen_helper_fpush(cpu_env); - gen_helper_fldpi_ST0(cpu_env); - break; - case 4: - gen_helper_fpush(cpu_env); - gen_helper_fldlg2_ST0(cpu_env); - break; - case 5: - gen_helper_fpush(cpu_env); - gen_helper_fldln2_ST0(cpu_env); - break; - case 6: - gen_helper_fpush(cpu_env); - gen_helper_fldz_ST0(cpu_env); - 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_helper_fsqrt(cpu_env); - 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_helper_fsin(cpu_env); - break; - default: - case 7: /* fcos */ - gen_helper_fcos(cpu_env); - 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(op1, opreg); - if (op >= 0x30) { - gen_helper_fpop(cpu_env); - } - } else { - gen_helper_fmov_FT0_STN(cpu_env, - tcg_constant_i32(opreg)); - gen_helper_fp_arith_ST0_FT0(op1); - } - } - break; - case 0x02: /* fcom */ - case 0x22: /* fcom2, undocumented op */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_constant_i32(opreg)); - gen_helper_fcom_ST0_FT0(cpu_env); - break; - case 0x03: /* fcomp */ - case 0x23: /* fcomp3, undocumented op */ - case 0x32: /* fcomp5, undocumented op */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_constant_i32(opreg)); - gen_helper_fcom_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); - break; - case 0x15: /* da/5 */ - switch (rm) { - case 1: /* fucompp */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_constant_i32(1)); - gen_helper_fucom_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); - gen_helper_fpop(cpu_env); - 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; - 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_helper_fmov_FT0_STN(cpu_env, tcg_constant_i32(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_helper_fmov_FT0_STN(cpu_env, tcg_constant_i32(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_constant_i32(opreg)); - break; - case 0x2a: /* fst sti */ - gen_helper_fmov_STN_ST0(cpu_env, tcg_constant_i32(opreg)); - break; - case 0x2b: /* fstp sti */ - case 0x0b: /* fstp1 sti, undocumented op */ - case 0x3a: /* fstp8 sti, undocumented op */ - case 0x3b: /* fstp9 sti, undocumented op */ - gen_helper_fmov_STN_ST0(cpu_env, tcg_constant_i32(opreg)); - gen_helper_fpop(cpu_env); - break; - case 0x2c: /* fucom st(i) */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_constant_i32(opreg)); - gen_helper_fucom_ST0_FT0(cpu_env); - break; - case 0x2d: /* fucomp st(i) */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_constant_i32(opreg)); - gen_helper_fucom_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); - break; - case 0x33: /* de/3 */ - switch (rm) { - case 1: /* fcompp */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_constant_i32(1)); - gen_helper_fcom_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); - gen_helper_fpop(cpu_env); - break; - default: - goto unknown_op; - } - break; - case 0x38: /* ffreep sti, undocumented op */ - gen_helper_ffree_STN(cpu_env, tcg_constant_i32(opreg)); - gen_helper_fpop(cpu_env); - 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_helper_fmov_FT0_STN(cpu_env, tcg_constant_i32(opreg)); - gen_helper_fucomi_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); - 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_helper_fmov_FT0_STN(cpu_env, tcg_constant_i32(opreg)); - gen_helper_fcomi_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); - 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_helper_fmov_ST0_STN(cpu_env, - tcg_constant_i32(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; - } - translator_io_start(&s->base); - 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; - } - translator_io_start(&s->base); - 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; - } - translator_io_start(&s->base); - gen_helper_in_func(ot, s->T1, s->tmp2_i32); - gen_op_mov_reg_v(s, ot, R_EAX, s->T1); - gen_bpt_io(s, s->tmp2_i32, ot); - 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; - } - translator_io_start(&s->base); - gen_op_mov_v_reg(s, ot, s->T1, R_EAX); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); - gen_bpt_io(s, s->tmp2_i32, ot); - 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; - } - translator_io_start(&s->base); - gen_helper_in_func(ot, s->T1, s->tmp2_i32); - gen_op_mov_reg_v(s, ot, R_EAX, s->T1); - gen_bpt_io(s, s->tmp2_i32, ot); - 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; - } - translator_io_start(&s->base); - gen_op_mov_v_reg(s, ot, s->T1, R_EAX); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); - gen_bpt_io(s, s->tmp2_i32, ot); - 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_constant_i32(dflag - 1), - tcg_constant_i32(val)); - } else { - gen_stack_A0(s); - /* pop offset */ - gen_op_ld_v(s, dflag, s->T0, s->A0); - /* NOTE: keeping EIP updated is not a problem in case of - exception */ - gen_op_jmp_v(s, 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_constant_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)) { - 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 (dflag == MO_16) { - mask &= 0xffff; - } - - ot = gen_pop_T0(s); - gen_helper_write_eflags(cpu_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; - } - 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_constant_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_constant_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 { - /* needs to be treated as I/O because of ferr_irq */ - translator_io_start(&s->base); - 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); - translator_io_start(&s->base); - 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 AMD SYSENTER is not valid in long mode */ - if (LMA(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) { - goto illegal_op; - } - if (!PE(s)) { - gen_exception_gpf(s); - } else { - gen_helper_sysenter(cpu_env); - s->base.is_jmp = DISAS_EOB_ONLY; - } - break; - case 0x135: /* sysexit */ - /* For AMD SYSEXIT is not valid in long mode */ - if (LMA(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) { - goto illegal_op; - } - if (!PE(s) || CPL(s) != 0) { - gen_exception_gpf(s); - } else { - gen_helper_sysexit(cpu_env, tcg_constant_i32(dflag - 1)); - s->base.is_jmp = DISAS_EOB_ONLY; - } - break; - case 0x105: /* syscall */ - /* For Intel SYSCALL is only valid in long mode */ - if (!LMA(s) && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_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 */ - /* For Intel SYSRET is only valid in long mode */ - if (!LMA(s) && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1) { - goto illegal_op; - } - if (!PE(s) || CPL(s) != 0) { - gen_exception_gpf(s); - } else { - gen_helper_sysret(cpu_env, tcg_constant_i32(dflag - 1)); - /* condition codes are modified only in long mode */ - if (LMA(s)) { - set_cc_op(s, CC_OP_EFLAGS); + (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) || + !(s->cpuid_ext_features & CPUID_EXT_RDRAND)) { + goto illegal_op; } - /* 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; - 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; + 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); + assume_cc_op(s, CC_OP_EFLAGS); + break; + + default: + goto illegal_op; } break; + case 0x100: - modrm = x86_ldub_code(env, s); mod = (modrm >> 6) & 3; op = (modrm >> 3) & 7; switch(op) { @@ -5770,19 +3080,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 */ @@ -5792,56 +3102,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; @@ -5851,10 +3161,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 */ @@ -5863,7 +3171,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; @@ -5890,41 +3198,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; @@ -5938,7 +3246,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_constant_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; @@ -5950,7 +3263,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 */ @@ -5962,7 +3275,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_constant_i32(s->aflag - 1)); + gen_helper_vmload(tcg_env, tcg_constant_i32(s->aflag - 1)); break; case 0xdb: /* VMSAVE */ @@ -5974,7 +3287,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_constant_i32(s->aflag - 1)); + gen_helper_vmsave(tcg_env, tcg_constant_i32(s->aflag - 1)); break; case 0xdc: /* STGI */ @@ -5986,7 +3299,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; @@ -5999,7 +3312,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 */ @@ -6024,7 +3337,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; @@ -6033,15 +3346,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 */ @@ -6049,15 +3362,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 */ @@ -6065,7 +3378,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 @@ -6073,24 +3386,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 */ @@ -6098,16 +3411,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; @@ -6116,8 +3429,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; @@ -6126,9 +3439,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; @@ -6143,157 +3456,36 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) gen_update_cc_op(s); gen_update_eip_cur(s); translator_io_start(&s->base); - gen_helper_rdtsc(cpu_env); - gen_helper_rdpid(s->T0, cpu_env); + 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; - } - break; - - case 0x108: /* invd */ - case 0x109: /* wbinvd; wbnoinvd with REPZ prefix */ - if (check_cpl0(s)) { - gen_svm_check_intercept(s, (b & 1) ? SVM_EXIT_WBINVD : SVM_EXIT_INVD); - /* nothing to do */ + goto illegal_op; } 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; - if (!PE(s) || VM86(s)) - goto illegal_op; - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); - t2 = tcg_temp_new(); - ot = MO_16; - modrm = x86_ldub_code(env, s); - reg = (modrm >> 3) & 7; - 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); - } else { - gen_op_mov_v_reg(s, ot, t0, rm); - } - 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, s->A0); - } else { - gen_op_mov_reg_v(s, ot, rm, t0); - } - gen_compute_eflags(s); - tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_Z); - tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t2); - } - 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_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); - } - 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); + gen_bndck(s, decode, TCG_COND_GTU, notu); } else if (prefixes & PREFIX_DATA) { /* bndmov -- from reg/mem */ if (reg >= 4 || s->aflag == MO_16) { @@ -6301,7 +3493,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) { @@ -6309,7 +3501,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); @@ -6328,9 +3520,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; @@ -6340,39 +3531,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)) { @@ -6385,7 +3573,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); } @@ -6396,11 +3584,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) { @@ -6408,7 +3595,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) { @@ -6416,7 +3603,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); @@ -6433,9 +3620,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; @@ -6445,389 +3631,32 @@ 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); - - translator_io_start(&s->base); - if (b & 2) { - gen_svm_check_intercept(s, SVM_EXIT_WRITE_CR0 + reg); - gen_op_mov_v_reg(s, ot, s->T0, rm); - gen_helper_write_crN(cpu_env, tcg_constant_i32(reg), s->T0); - 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); - 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; } +#include "decode-new.c.inc" + void tcg_x86_init(void) { static const char reg_names[CPU_NB_REGS][4] = { @@ -6882,36 +3711,36 @@ void tcg_x86_init(void) }; int i; - cpu_cc_op = tcg_global_mem_new_i32(cpu_env, + cpu_cc_op = tcg_global_mem_new_i32(tcg_env, offsetof(CPUX86State, cc_op), "cc_op"); - cpu_cc_dst = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, cc_dst), + cpu_cc_dst = tcg_global_mem_new(tcg_env, offsetof(CPUX86State, cc_dst), "cc_dst"); - cpu_cc_src = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, cc_src), + cpu_cc_src = tcg_global_mem_new(tcg_env, offsetof(CPUX86State, cc_src), "cc_src"); - cpu_cc_src2 = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, cc_src2), + cpu_cc_src2 = tcg_global_mem_new(tcg_env, offsetof(CPUX86State, cc_src2), "cc_src2"); - cpu_eip = tcg_global_mem_new(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]); } @@ -6920,7 +3749,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; @@ -6949,34 +3778,25 @@ 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 = cpu_mmu_index(env, false); + 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))); - /* - * If jmp_opt, we want to handle each string instruction individually. - * For icount also disable repz optimization so that each iteration - * is accounted separately. - */ - dc->repz_opt = !dc->jmp_opt && !(cflags & CF_USE_ICOUNT); + (flags & (HF_RF_MASK | HF_TF_MASK | HF_INHIBIT_IRQ_MASK))); dc->T0 = tcg_temp_new(); dc->T1 = tcg_temp_new(); dc->A0 = tcg_temp_new(); - dc->tmp0 = tcg_temp_new(); dc->tmp1_i64 = tcg_temp_new_i64(); dc->tmp2_i32 = tcg_temp_new_i32(); - dc->tmp3_i32 = tcg_temp_new_i32(); - dc->tmp4 = tcg_temp_new(); dc->cc_srcT = tcg_temp_new(); } @@ -6989,9 +3809,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 (tb_cflags(dcbase->tb) & CF_PCREL) { - pc_arg -= dc->cs_base; pc_arg &= ~TARGET_PAGE_MASK; } tcg_gen_insn_start(pc_arg, dc->cc_op); @@ -7000,6 +3820,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 /* @@ -7012,23 +3835,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 (!translator_is_same_page(&dc->base, dc->base.pc_next)) { + dc->base.is_jmp = DISAS_TOO_MANY; } } } @@ -7039,52 +3884,47 @@ 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 x86_translate_code(CPUState *cpu, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc; diff --git a/target/i386/tcg/user/excp_helper.c b/target/i386/tcg/user/excp_helper.c index b3bdb7831a7..98fab4cbc3f 100644 --- a/target/i386/tcg/user/excp_helper.c +++ b/target/i386/tcg/user/excp_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "tcg/helper-tcg.h" void x86_cpu_record_sigsegv(CPUState *cs, vaddr addr, diff --git a/target/i386/tcg/user/seg_helper.c b/target/i386/tcg/user/seg_helper.c index c45f2ac2ba6..263f59937fe 100644 --- a/target/i386/tcg/user/seg_helper.c +++ b/target/i386/tcg/user/seg_helper.c @@ -21,8 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "tcg/helper-tcg.h" #include "tcg/seg_helper.h" diff --git a/target/i386/trace-events b/target/i386/trace-events index 2cd8726eebb..51301673f0c 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/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c index 67cad867207..b8bebe403c9 100644 --- a/target/i386/whpx/whpx-accel-ops.c +++ b/target/i386/whpx/whpx-accel-ops.c @@ -9,12 +9,13 @@ */ #include "qemu/osdep.h" -#include "sysemu/kvm_int.h" +#include "system/kvm_int.h" #include "qemu/main-loop.h" -#include "sysemu/cpus.h" +#include "system/accel-ops.h" +#include "system/cpus.h" #include "qemu/guest-random.h" -#include "sysemu/whpx.h" +#include "system/whpx.h" #include "whpx-internal.h" #include "whpx-accel-ops.h" @@ -25,7 +26,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 +49,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,9 +65,6 @@ 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, @@ -85,7 +83,7 @@ static bool whpx_vcpu_thread_is_idle(CPUState *cpu) return !whpx_apic_in_platform(); } -static void whpx_accel_ops_class_init(ObjectClass *oc, void *data) +static void whpx_accel_ops_class_init(ObjectClass *oc, const void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); diff --git a/target/i386/whpx/whpx-accel-ops.h b/target/i386/whpx/whpx-accel-ops.h index 7a1bb1ab575..e6cf15511d4 100644 --- a/target/i386/whpx/whpx-accel-ops.h +++ b/target/i386/whpx/whpx-accel-ops.h @@ -10,7 +10,7 @@ #ifndef TARGET_I386_WHPX_ACCEL_OPS_H #define TARGET_I386_WHPX_ACCEL_OPS_H -#include "sysemu/cpus.h" +#include "system/cpus.h" int whpx_init_vcpu(CPUState *cpu); int whpx_vcpu_exec(CPUState *cpu); diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 3de0dc1d460..cf6d3e4cdd4 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -10,13 +10,13 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/address-spaces.h" -#include "exec/ioport.h" +#include "system/address-spaces.h" +#include "system/ioport.h" #include "gdbstub/helpers.h" #include "qemu/accel.h" -#include "sysemu/whpx.h" -#include "sysemu/cpus.h" -#include "sysemu/runstate.h" +#include "system/whpx.h" +#include "system/cpus.h" +#include "system/runstate.h" #include "qemu/main-loop.h" #include "hw/boards.h" #include "hw/intc/ioapic.h" @@ -237,6 +237,7 @@ struct AccelCPUState { 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; @@ -300,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; @@ -311,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)) { @@ -321,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; @@ -345,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)) { @@ -382,8 +381,8 @@ static void whpx_set_registers(CPUState *cpu, int level) { struct whpx_state *whpx = &whpx_global; AccelCPUState *vcpu = cpu->accel; - CPUX86State *env = cpu->env_ptr; X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; struct whpx_register_set vcxt; HRESULT hr; int idx; @@ -550,13 +549,10 @@ static void whpx_set_registers(CPUState *cpu, int level) error_report("WHPX: Failed to set virtual processor context, hr=%08lx", hr); } - - return; } 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; @@ -569,14 +565,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; @@ -594,15 +589,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; AccelCPUState *vcpu = cpu->accel; - CPUX86State *env = cpu->env_ptr; X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; struct whpx_register_set vcxt; uint64_t tpr, apic_base; HRESULT hr; @@ -774,8 +769,6 @@ static void whpx_get_registers(CPUState *cpu) } x86_update_hflags(env); - - return; } static HRESULT CALLBACK whpx_emu_ioport_callback( @@ -843,7 +836,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; } @@ -1324,7 +1317,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 && @@ -1398,10 +1391,9 @@ 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 @@ -1439,18 +1431,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; } @@ -1460,8 +1451,8 @@ static void whpx_vcpu_pre_run(CPUState *cpu) HRESULT hr; struct whpx_state *whpx = &whpx_global; AccelCPUState *vcpu = cpu->accel; - CPUX86State *env = cpu->env_ptr; X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; int irq; uint8_t tpr; WHV_X64_PENDING_INTERRUPTION_REGISTER new_int; @@ -1472,7 +1463,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 && @@ -1563,7 +1554,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) { @@ -1575,24 +1566,22 @@ static void whpx_vcpu_pre_run(CPUState *cpu) " hr=%08lx", hr); } } - - return; } static void whpx_vcpu_post_run(CPUState *cpu) { AccelCPUState *vcpu = cpu->accel; - CPUX86State *env = cpu->env_ptr; 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 = @@ -1600,14 +1589,12 @@ static void whpx_vcpu_post_run(CPUState *cpu) vcpu->interruptable = !vcpu->exit_ctx.VpContext.ExecutionState.InterruptShadow; - - return; } static void whpx_vcpu_process_async_events(CPUState *cpu) { - CPUX86State *env = cpu->env_ptr; X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; AccelCPUState *vcpu = cpu->accel; if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && @@ -1639,8 +1626,6 @@ static void whpx_vcpu_process_async_events(CPUState *cpu) apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, env->tpr_access_type); } - - return; } static int whpx_vcpu_run(CPUState *cpu) @@ -1652,7 +1637,7 @@ static int whpx_vcpu_run(CPUState *cpu) 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. */ @@ -1690,7 +1675,7 @@ static int whpx_vcpu_run(CPUState *cpu) } } - qemu_mutex_unlock_iothread(); + bql_unlock(); if (exclusive_step_mode != WHPX_STEP_NONE) { start_exclusive(); @@ -1719,9 +1704,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) { @@ -2028,9 +2013,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; } @@ -2055,7 +2040,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) { @@ -2069,9 +2054,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; } } @@ -2079,20 +2064,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; } /* @@ -2101,7 +2086,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); } } @@ -2147,8 +2132,8 @@ int whpx_init_vcpu(CPUState *cpu) struct whpx_state *whpx = &whpx_global; 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; @@ -2160,9 +2145,8 @@ 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; } @@ -2242,10 +2226,10 @@ int whpx_init_vcpu(CPUState *cpu) } vcpu->interruptable = true; - cpu->vcpu_dirty = true; + 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; @@ -2286,7 +2270,6 @@ void whpx_destroy_vcpu(CPUState *cpu) whp_dispatch.WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index); whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator); g_free(cpu->accel); - return; } void whpx_vcpu_kick(CPUState *cpu) @@ -2715,7 +2698,7 @@ bool whpx_apic_in_platform(void) { return whpx_global.apic_in_platform; } -static void whpx_accel_class_init(ObjectClass *oc, void *data) +static void whpx_accel_class_init(ObjectClass *oc, const void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "WHPX"; diff --git a/target/i386/whpx/whpx-apic.c b/target/i386/whpx/whpx-apic.c index 8710e37567d..e1ef6d4e6d4 100644 --- a/target/i386/whpx/whpx-apic.c +++ b/target/i386/whpx/whpx-apic.c @@ -16,8 +16,8 @@ #include "hw/i386/apic_internal.h" #include "hw/i386/apic-msidef.h" #include "hw/pci/msi.h" -#include "sysemu/hw_accel.h" -#include "sysemu/whpx.h" +#include "system/hw_accel.h" +#include "system/whpx.h" #include "whpx-internal.h" struct whpx_lapic_state { @@ -90,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) @@ -230,7 +231,7 @@ static void whpx_apic_mem_write(void *opaque, hwaddr addr, static const MemoryRegionOps whpx_apic_io_ops = { .read = whpx_apic_mem_read, .write = whpx_apic_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void whpx_apic_reset(APICCommonState *s) @@ -251,7 +252,7 @@ static void whpx_apic_realize(DeviceState *dev, Error **errp) msi_nonbroken = true; } -static void whpx_apic_class_init(ObjectClass *klass, void *data) +static void whpx_apic_class_init(ObjectClass *klass, const void *data) { APICCommonClass *k = APIC_COMMON_CLASS(klass); diff --git a/target/i386/xsave_helper.c b/target/i386/xsave_helper.c index 996e9f3bfef..24ab7be8e9a 100644 --- a/target/i386/xsave_helper.c +++ b/target/i386/xsave_helper.c @@ -5,6 +5,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/tswap.h" void x86_cpu_xsave_all_areas(X86CPU *cpu, void *buf, uint32_t buflen) { diff --git a/target/loongarch/arch_dump.c b/target/loongarch/arch_dump.c new file mode 100644 index 00000000000..2b0955a2091 --- /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 "system/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 c59d7a9fcbc..0834e91f30e 100644 --- a/target/loongarch/cpu-csr.h +++ b/target/loongarch/cpu-csr.h @@ -67,6 +67,9 @@ FIELD(TLBENTRY, D, 1, 1) FIELD(TLBENTRY, PLV, 2, 2) FIELD(TLBENTRY, MAT, 4, 2) FIELD(TLBENTRY, G, 6, 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) diff --git a/target/loongarch/cpu-param.h b/target/loongarch/cpu-param.h index 1265dc7cb55..58cc45a377e 100644 --- a/target/loongarch/cpu-param.h +++ b/target/loongarch/cpu-param.h @@ -8,10 +8,11 @@ #ifndef LOONGARCH_CPU_PARAM_H #define LOONGARCH_CPU_PARAM_H -#define TARGET_LONG_BITS 64 #define TARGET_PHYS_ADDR_SPACE_BITS 48 #define TARGET_VIRT_ADDR_SPACE_BITS 48 -#define TARGET_PAGE_BITS 14 +#define TARGET_PAGE_BITS 12 + +#define TARGET_INSN_START_EXTRA_WORDS 0 #endif diff --git a/target/loongarch/cpu-qom.h b/target/loongarch/cpu-qom.h new file mode 100644 index 00000000000..fa3fcf71861 --- /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 65f9320e347..abad84c0547 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -10,15 +10,29 @@ #include "qemu/qemu-print.h" #include "qapi/error.h" #include "qemu/module.h" -#include "sysemu/qtest.h" -#include "exec/cpu_ldst.h" -#include "exec/exec-all.h" +#include "system/qtest.h" +#include "system/tcg.h" +#include "system/kvm.h" +#include "kvm/kvm_loongarch.h" +#include "hw/qdev-properties.h" +#include "exec/translation-block.h" #include "cpu.h" #include "internals.h" #include "fpu/softfloat-helpers.h" -#include "cpu-csr.h" -#include "sysemu/reset.h" +#include "csr.h" +#ifndef CONFIG_USER_ONLY +#include "system/reset.h" +#endif +#include "vec.h" +#ifdef CONFIG_KVM +#include +#endif +#ifdef CONFIG_TCG +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/cpu-ops.h" #include "tcg/tcg.h" +#endif +#include "tcg/tcg_loongarch.h" const char * const regnames[32] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", @@ -34,32 +48,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", - [EXCCODE_SXD] = "128 bit vector instructions Disable 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, @@ -68,7 +95,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)); @@ -79,18 +106,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; - - set_pc(env, 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 @@ -106,12 +127,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); + } } } @@ -136,29 +160,25 @@ static inline bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env) 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) { @@ -189,6 +209,7 @@ static void loongarch_cpu_do_interrupt(CPUState *cs) case EXCCODE_FPD: case EXCCODE_FPE: case EXCCODE_SXD: + case EXCCODE_ASXD: env->CSR_BADV = env->pc; QEMU_FALLTHROUGH; case EXCCODE_BCE: @@ -289,8 +310,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); @@ -302,8 +322,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)) { @@ -315,45 +334,91 @@ static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } return false; } + +static vaddr loongarch_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + return is_va32(cpu_env(cs)) ? (uint32_t)result : result; +} #endif -#ifdef CONFIG_TCG +static TCGTBCPUState loongarch_get_tb_cpu_state(CPUState *cs) +{ + CPULoongArchState *env = cpu_env(cs); + uint32_t flags; + + 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; + + return (TCGTBCPUState){ .pc = env->pc, .flags = flags }; +} + static void loongarch_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; - - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); - set_pc(env, tb->pc); + 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; - - set_pc(env, data[0]); + set_pc(cpu_env(cs), data[0]); } #endif /* CONFIG_TCG */ +#ifndef CONFIG_USER_ONLY 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; } return has_work; +} +#endif /* !CONFIG_USER_ONLY */ + +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_init_csr(Object *obj) +{ +#ifndef CONFIG_USER_ONLY + static bool initialized; + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + CPULoongArchState *env = &cpu->env; + int i, num; + + if (!initialized) { + initialized = true; + num = FIELD_EX64(env->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM); + for (i = num; i < 16; i++) { + set_csr_flag(LOONGARCH_CSR_SAVE(i), CSRFL_UNUSED); + } + set_csr_flag(LOONGARCH_CSR_IMPCTL1, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_IMPCTL2, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_MERRCTL, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_MERRINFO1, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_MERRINFO2, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_MERRENTRY, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_MERRERA, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_MERRSAVE, CSRFL_UNUSED); + set_csr_flag(LOONGARCH_CSR_CTAG, CSRFL_UNUSED); + } #endif } @@ -361,6 +426,7 @@ static void loongarch_la464_initfn(Object *obj) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); CPULoongArchState *env = &cpu->env; + uint32_t data = 0, field; int i; for (i = 0; i < 21; i++) { @@ -370,18 +436,23 @@ static void loongarch_la464_initfn(Object *obj) cpu->dtb_compatible = "loongarch,Loongson-3A5000"; env->cpucfg[0] = 0x14c010; /* PRID */ - uint32_t data = 0; data = FIELD_DP32(data, CPUCFG1, ARCH, 2); data = FIELD_DP32(data, CPUCFG1, PGMMU, 1); data = FIELD_DP32(data, CPUCFG1, IOCSR, 1); - data = FIELD_DP32(data, CPUCFG1, PALEN, 0x2f); + if (kvm_enabled()) { + /* GPA address width of VM is 47, field value is 47 - 1 */ + field = 0x2e; + } else { + field = 0x2f; /* 48 bit - 1 */ + } + data = FIELD_DP32(data, CPUCFG1, PALEN, field); data = FIELD_DP32(data, CPUCFG1, VALEN, 0x2f); data = FIELD_DP32(data, CPUCFG1, UAL, 1); data = FIELD_DP32(data, CPUCFG1, RI, 1); data = FIELD_DP32(data, CPUCFG1, EP, 1); data = FIELD_DP32(data, CPUCFG1, RPLV, 1); data = FIELD_DP32(data, CPUCFG1, HP, 1); - data = FIELD_DP32(data, CPUCFG1, IOCSR_BRD, 1); + data = FIELD_DP32(data, CPUCFG1, CRC, 1); env->cpucfg[1] = data; data = 0; @@ -390,6 +461,7 @@ static void loongarch_la464_initfn(Object *obj) 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); @@ -439,13 +511,27 @@ 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_la464_init_csr(obj); + loongarch_cpu_post_init(obj); } static void loongarch_la132_initfn(Object *obj) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); CPULoongArchState *env = &cpu->env; - + uint32_t data = 0; int i; for (i = 0; i < 21; i++) { @@ -455,7 +541,6 @@ static void loongarch_la132_initfn(Object *obj) 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); @@ -466,47 +551,40 @@ static void loongarch_la132_initfn(Object *obj) 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); + data = FIELD_DP32(data, CPUCFG1, CRC, 1); env->cpucfg[1] = data; } -static void loongarch_cpu_list_entry(gpointer data, gpointer user_data) +static void loongarch_max_initfn(Object *obj) { - const char *typename = object_class_get_name(OBJECT_CLASS(data)); - - qemu_printf("%s\n", typename); + /* '-cpu max' for TCG: we use cpu la464. */ + loongarch_la464_initfn(obj); } -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_hold(Object *obj) +static void loongarch_cpu_reset_hold(Object *obj, ResetType type) { + uint8_t tlb_ps; CPUState *cs = CPU(obj); - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(cpu); - CPULoongArchState *env = &cpu->env; + LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(obj); + CPULoongArchState *env = cpu_env(cs); if (lacc->parent_phases.hold) { - lacc->parent_phases.hold(obj); + 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); @@ -520,16 +598,31 @@ static void loongarch_cpu_reset_hold(Object *obj) 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_PWCH = 0; + env->CSR_EENTRY = 0; + env->CSR_TLBRENTRY = 0; + env->CSR_MERRENTRY = 0; + /* set CSR_PWCL.PTBASE and CSR_STLBPS.PS bits from CSR_PRCFG2 */ + if (env->CSR_PRCFG2 == 0) { + env->CSR_PRCFG2 = 0x3fffff000; + } + tlb_ps = ctz32(env->CSR_PRCFG2); + env->CSR_STLBPS = FIELD_DP64(env->CSR_STLBPS, CSR_STLBPS, PS, tlb_ps); + env->CSR_PWCL = FIELD_DP64(env->CSR_PWCL, CSR_PWCL, PTBASE, tlb_ps); for (n = 0; n < 4; n++) { env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV0, 0); env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV1, 0); @@ -539,15 +632,23 @@ static void loongarch_cpu_reset_hold(Object *obj) #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; } static void loongarch_cpu_disas_set_info(CPUState *s, disassemble_info *info) { + info->endian = BFD_ENDIAN_LITTLE; info->print_insn = print_insn_loongarch; } @@ -565,70 +666,123 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) loongarch_cpu_register_gdb_regs_for_features(cs); - cpu_reset(cs); qemu_init_vcpu(cs); + cpu_reset(cs); lacc->parent_realize(dev, errp); } +static void loongarch_cpu_unrealizefn(DeviceState *dev) +{ + LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev); + #ifndef CONFIG_USER_ONLY -static void loongarch_qemu_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - qemu_log_mask(LOG_UNIMP, "[%s]: Unimplemented reg 0x%" HWADDR_PRIx "\n", - __func__, addr); -} - -static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size) -{ - switch (addr) { - case VERSION_REG: - return 0x11ULL; - case FEATURE_REG: - return 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI | - 1ULL << IOCSRF_CSRIPI; - case VENDOR_REG: - return 0x6e6f73676e6f6f4cULL; /* "Loongson" */ - case CPUNAME_REG: - return 0x303030354133ULL; /* "3A5000" */ - case MISC_FUNC_REG: - return 1ULL << IOCSRM_EXTIOI_EN; - } - return 0ULL; -} - -static const MemoryRegionOps loongarch_qemu_ops = { - .read = loongarch_qemu_read, - .write = loongarch_qemu_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 8, - }, - .impl = { - .min_access_size = 8, - .max_access_size = 8, - }, -}; + cpu_remove_sync(CPU(dev)); #endif -static void loongarch_cpu_init(Object *obj) + lacc->parent_unrealize(dev); +} + +static bool loongarch_get_lsx(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->lsx != ON_OFF_AUTO_OFF; +} + +static void loongarch_set_lsx(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + uint32_t val; + + cpu->lsx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + if (cpu->lsx == ON_OFF_AUTO_OFF) { + cpu->lasx = ON_OFF_AUTO_OFF; + if (cpu->lasx == ON_OFF_AUTO_ON) { + error_setg(errp, "Failed to disable LSX since LASX is enabled"); + return; + } + } + + if (kvm_enabled()) { + /* kvm feature detection in function kvm_arch_init_vcpu */ + return; + } + + /* LSX feature detection in TCG mode */ + val = cpu->env.cpucfg[2]; + if (cpu->lsx == ON_OFF_AUTO_ON) { + if (FIELD_EX32(val, CPUCFG2, LSX) == 0) { + error_setg(errp, "Failed to enable LSX in TCG mode"); + return; + } + } else { + cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, 0); + val = cpu->env.cpucfg[2]; + } + + cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LSX, value); +} + +static bool loongarch_get_lasx(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->lasx != ON_OFF_AUTO_OFF; +} + +static void loongarch_set_lasx(Object *obj, bool value, Error **errp) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); + uint32_t val; - cpu_set_cpustate_pointers(cpu); + cpu->lasx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + if ((cpu->lsx == ON_OFF_AUTO_OFF) && (cpu->lasx == ON_OFF_AUTO_ON)) { + error_setg(errp, "Failed to enable LASX since lSX is disabled"); + return; + } + if (kvm_enabled()) { + /* kvm feature detection in function kvm_arch_init_vcpu */ + return; + } + + /* LASX feature detection in TCG mode */ + val = cpu->env.cpucfg[2]; + if (cpu->lasx == ON_OFF_AUTO_ON) { + if (FIELD_EX32(val, CPUCFG2, LASX) == 0) { + error_setg(errp, "Failed to enable LASX in TCG mode"); + return; + } + } + + cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, value); +} + +void loongarch_cpu_post_init(Object *obj) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->lbt = ON_OFF_AUTO_OFF; + cpu->pmu = ON_OFF_AUTO_OFF; + cpu->lsx = ON_OFF_AUTO_AUTO; + cpu->lasx = ON_OFF_AUTO_AUTO; + 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()) { + kvm_loongarch_cpu_post_init(cpu); + } +} + +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 } @@ -641,27 +795,66 @@ static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model) 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 oc; +} + +static void loongarch_cpu_dump_csr(CPUState *cs, FILE *f) +{ +#ifndef CONFIG_USER_ONLY + CPULoongArchState *env = cpu_env(cs); + CSRInfo *csr_info; + int64_t *addr; + int i, j, len, col = 0; + + qemu_fprintf(f, "\n"); + + /* Dump all generic CSR register */ + for (i = 0; i < LOONGARCH_CSR_DBG; i++) { + csr_info = get_csr(i); + if (!csr_info || (csr_info->flags & CSRFL_UNUSED)) { + if (i == (col + 3)) { + qemu_fprintf(f, "\n"); + } + + continue; + } + + if ((i > (col + 3)) || (i == col)) { + col = i & ~3; + qemu_fprintf(f, " CSR%03d:", col); + } + + addr = (void *)env + csr_info->offset; + qemu_fprintf(f, " %s ", csr_info->name); + len = strlen(csr_info->name); + for (; len < 6; len++) { + qemu_fprintf(f, " "); + } + + qemu_fprintf(f, "%" PRIx64, *addr); + j = find_last_bit((void *)addr, BITS_PER_LONG) & (BITS_PER_LONG - 1); + len += j / 4 + 1; + for (; len < 22; len++) { + qemu_fprintf(f, " "); + } + + if (i == (col + 3)) { + qemu_fprintf(f, "\n"); + } } - return NULL; + qemu_fprintf(f, "\n"); +#endif } -void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) +static 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++) { @@ -674,20 +867,8 @@ void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } - qemu_fprintf(f, "CRMD=%016" PRIx64 "\n", env->CSR_CRMD); - qemu_fprintf(f, "PRMD=%016" PRIx64 "\n", env->CSR_PRMD); - qemu_fprintf(f, "EUEN=%016" PRIx64 "\n", env->CSR_EUEN); - qemu_fprintf(f, "ESTAT=%016" PRIx64 "\n", env->CSR_ESTAT); - qemu_fprintf(f, "ERA=%016" PRIx64 "\n", env->CSR_ERA); - qemu_fprintf(f, "BADV=%016" PRIx64 "\n", env->CSR_BADV); - qemu_fprintf(f, "BADI=%016" PRIx64 "\n", env->CSR_BADI); - 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); - 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); + /* csr */ + loongarch_cpu_dump_csr(cs, f); /* fpr */ if (flags & CPU_DUMP_FPU) { @@ -701,16 +882,23 @@ void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) } #ifdef CONFIG_TCG -#include "hw/core/tcg-cpu-ops.h" +static const TCGCPUOps loongarch_tcg_ops = { + .guest_default_memory_order = 0, + .mttcg_supported = true, -static struct TCGCPUOps loongarch_tcg_ops = { .initialize = loongarch_translate_init, + .translate_code = loongarch_translate_code, + .get_tb_cpu_state = loongarch_get_tb_cpu_state, .synchronize_from_tb = loongarch_cpu_synchronize_from_tb, .restore_state_to_opc = loongarch_restore_state_to_opc, + .mmu_index = loongarch_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill = loongarch_cpu_tlb_fill, + .pointer_wrap = loongarch_pointer_wrap, .cpu_exec_interrupt = loongarch_cpu_exec_interrupt, + .cpu_exec_halt = loongarch_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = loongarch_cpu_do_interrupt, .do_transaction_failed = loongarch_cpu_do_transaction_failed, #endif @@ -721,6 +909,8 @@ static struct TCGCPUOps loongarch_tcg_ops = { #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps loongarch_sysemu_ops = { + .has_work = loongarch_cpu_has_work, + .write_elf64_note = loongarch_cpu_write_elf64_note, .get_phys_page_debug = loongarch_cpu_get_phys_page_debug, }; @@ -732,20 +922,29 @@ static int64_t loongarch_cpu_get_arch_id(CPUState *cs) } #endif -static void loongarch_cpu_class_init(ObjectClass *c, void *data) +static const Property loongarch_cpu_properties[] = { + DEFINE_PROP_INT32("socket-id", LoongArchCPU, socket_id, 0), + DEFINE_PROP_INT32("core-id", LoongArchCPU, core_id, 0), + DEFINE_PROP_INT32("thread-id", LoongArchCPU, thread_id, 0), + DEFINE_PROP_INT32("node-id", LoongArchCPU, node_id, CPU_UNSET_NUMA_NODE_ID), +}; + +static void loongarch_cpu_class_init(ObjectClass *c, const 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_props(dc, loongarch_cpu_properties); device_class_set_parent_realize(dc, loongarch_cpu_realizefn, &lacc->parent_realize); + device_class_set_parent_unrealize(dc, loongarch_cpu_unrealizefn, + &lacc->parent_unrealize); 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->dump_state = loongarch_cpu_dump_state; cc->set_pc = loongarch_cpu_set_pc; cc->get_pc = loongarch_cpu_get_pc; @@ -762,32 +961,31 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data) #ifdef CONFIG_TCG cc->tcg_ops = &loongarch_tcg_ops; #endif + dc->user_creatable = true; } -static gchar *loongarch32_gdb_arch_name(CPUState *cs) +static const gchar *loongarch32_gdb_arch_name(CPUState *cs) { - return g_strdup("loongarch32"); + return "loongarch32"; } -static void loongarch32_cpu_class_init(ObjectClass *c, void *data) +static void loongarch32_cpu_class_init(ObjectClass *c, const void *data) { CPUClass *cc = CPU_CLASS(c); - cc->gdb_num_core_regs = 35; cc->gdb_core_xml_file = "loongarch-base32.xml"; cc->gdb_arch_name = loongarch32_gdb_arch_name; } -static gchar *loongarch64_gdb_arch_name(CPUState *cs) +static const gchar *loongarch64_gdb_arch_name(CPUState *cs) { - return g_strdup("loongarch64"); + return "loongarch64"; } -static void loongarch64_cpu_class_init(ObjectClass *c, void *data) +static void loongarch64_cpu_class_init(ObjectClass *c, const void *data) { CPUClass *cc = CPU_CLASS(c); - cc->gdb_num_core_regs = 35; cc->gdb_core_xml_file = "loongarch-base64.xml"; cc->gdb_arch_name = loongarch64_gdb_arch_name; } @@ -804,6 +1002,7 @@ 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, @@ -826,6 +1025,7 @@ static const TypeInfo loongarch_cpu_type_infos[] = { }, 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) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 4d7201995ae..9538e8d61d7 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -9,14 +9,17 @@ #define LOONGARCH_CPU_H #include "qemu/int128.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "fpu/softfloat-types.h" #include "hw/registerfields.h" #include "qemu/timer.h" #ifndef CONFIG_USER_ONLY -#include "exec/memory.h" +#include "system/memory.h" #endif #include "cpu-csr.h" +#include "cpu-qom.h" #define IOCSRF_TEMP 0 #define IOCSRF_NODECNT 1 @@ -35,11 +38,10 @@ #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 */ @@ -129,7 +131,7 @@ FIELD(CPUCFG1, RI, 21, 1) FIELD(CPUCFG1, EP, 22, 1) FIELD(CPUCFG1, RPLV, 23, 1) FIELD(CPUCFG1, HP, 24, 1) -FIELD(CPUCFG1, IOCSR_BRD, 25, 1) +FIELD(CPUCFG1, CRC, 25, 1) FIELD(CPUCFG1, MSG_INT, 26, 1) /* cpucfg[1].arch */ @@ -153,6 +155,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) @@ -251,24 +254,27 @@ FIELD(TLB_MISC, ASID, 1, 10) FIELD(TLB_MISC, VPPN, 13, 35) FIELD(TLB_MISC, PS, 48, 6) -#define LSX_LEN (128) +#define LSX_LEN (128) +#define LASX_LEN (256) + typedef union VReg { - int8_t B[LSX_LEN / 8]; - int16_t H[LSX_LEN / 16]; - int32_t W[LSX_LEN / 32]; - int64_t D[LSX_LEN / 64]; - uint8_t UB[LSX_LEN / 8]; - uint16_t UH[LSX_LEN / 16]; - uint32_t UW[LSX_LEN / 32]; - uint64_t UD[LSX_LEN / 64]; - Int128 Q[LSX_LEN / 128]; -}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 */ @@ -276,22 +282,39 @@ struct LoongArchTLB { uint64_t tlb_entry1; }; typedef struct LoongArchTLB LoongArchTLB; +#endif + +enum loongarch_features { + LOONGARCH_FEATURE_LSX, + LOONGARCH_FEATURE_LASX, + LOONGARCH_FEATURE_LBT, /* loongson binary translation extension */ + LOONGARCH_FEATURE_PMU, + LOONGARCH_FEATURE_PV_IPI, + LOONGARCH_FEATURE_STEALTIME, +}; + +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; fpr_t fpr[32]; - float_status fp_status; 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; + uint32_t pv_features; /* LoongArch CSRs */ uint64_t CSR_CRMD; @@ -316,6 +339,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; @@ -347,21 +371,36 @@ typedef struct CPUArchState { uint64_t CSR_DBG; uint64_t CSR_DERA; uint64_t CSR_DSAVE; - uint64_t CSR_CPUID; + 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; - /* Store ipistate to access from this struct */ - DeviceState *ipistate; + uint32_t mp_state; + + struct loongarch_boot_info *boot_info; #endif } CPULoongArchState; +typedef struct LoongArchCPUTopo { + int32_t socket_id; /* socket-id of this VCPU */ + int32_t core_id; /* core-id of this VCPU */ + int32_t thread_id; /* thread-id of this VCPU */ +} LoongArchCPUTopo; + /** * LoongArchCPU: * @env: #CPULoongArchState @@ -369,26 +408,29 @@ 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; + OnOffAuto lsx; + OnOffAuto lasx; + OnOffAuto kvm_pv_ipi; + OnOffAuto kvm_steal_time; + int32_t socket_id; /* socket-id of this CPU */ + int32_t core_id; /* core-id of this CPU */ + int32_t thread_id; /* thread-id of this CPU */ + int32_t node_id; /* NUMA node of this CPU */ /* '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; + VMChangeStateEntry *vmsentry; }; -#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) - /** * LoongArchCPUClass: * @parent_realize: The parent class' realize handler. @@ -397,11 +439,10 @@ OBJECT_DECLARE_CPU_TYPE(LoongArchCPU, LoongArchCPUClass, * A LoongArch CPU model. */ struct LoongArchCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; ResettablePhases parent_phases; }; @@ -412,21 +453,9 @@ 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 - -static inline int cpu_mmu_index(CPULoongArchState *env, bool ifetch) -{ -#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 MMU_IDX_DA; -#endif -} +#define MMU_KERNEL_IDX MMU_PLV_KERNEL +#define MMU_USER_IDX MMU_PLV_USER +#define MMU_DA_IDX 4 static inline bool is_la64(CPULoongArchState *env) { @@ -457,30 +486,23 @@ static inline void set_pc(CPULoongArchState *env, uint64_t value) * 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, 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 |= is_va32(env) * HW_FLAGS_VA32; -} - -void loongarch_cpu_list(void); - -#define cpu_list loongarch_cpu_list +#define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU -#include "exec/cpu-all.h" +void loongarch_cpu_post_init(Object *obj); -#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 +#ifdef CONFIG_KVM +void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu); +#else +static inline void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu) +{ +} +#endif +void kvm_loongarch_init_irq_routing(void); #endif /* LOONGARCH_CPU_H */ diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c new file mode 100644 index 00000000000..e172b11ce15 --- /dev/null +++ b/target/loongarch/cpu_helper.c @@ -0,0 +1,220 @@ +/* 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 "system/tcg.h" +#include "cpu.h" +#include "accel/tcg/cpu-mmu-index.h" +#include "exec/target_page.h" +#include "internals.h" +#include "cpu-csr.h" +#include "tcg/tcg_loongarch.h" + +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 int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address) +{ + CPUState *cs = env_cpu(env); + target_ulong index, phys; + uint64_t dir_base, dir_width; + uint64_t base; + int level; + + if ((address >> 63) & 0x1) { + base = env->CSR_PGDH; + } else { + base = env->CSR_PGDL; + } + base &= TARGET_PHYS_MASK; + + for (level = 4; level > 0; level--) { + get_dir_base_width(env, &dir_base, &dir_width, level); + + if (dir_width == 0) { + continue; + } + + /* get next level page directory */ + index = (address >> dir_base) & ((1 << dir_width) - 1); + phys = base | index << 3; + base = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; + if (FIELD_EX64(base, TLBENTRY, HUGE)) { + /* base is a huge pte */ + break; + } + } + + /* pte */ + if (FIELD_EX64(base, TLBENTRY, HUGE)) { + /* Huge Page. base is pte */ + 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); + } + } else { + /* Normal Page. base points to pte */ + get_dir_base_width(env, &dir_base, &dir_width, 0); + index = (address >> dir_base) & ((1 << dir_width) - 1); + phys = base | index << 3; + base = ldq_phys(cs->as, phys); + } + + /* TODO: check plv and other bits? */ + + /* base is pte, in normal pte format */ + if (!FIELD_EX64(base, TLBENTRY, V)) { + return TLBRET_NOMATCH; + } + + if (!FIELD_EX64(base, TLBENTRY, D)) { + *prot = PAGE_READ; + } else { + *prot = PAGE_READ | PAGE_WRITE; + } + + /* get TARGET_PAGE_SIZE aligned physical address */ + base += (address & TARGET_PHYS_MASK) & ((1 << dir_base) - 1); + /* mask RPLV, NX, NR bits */ + base = FIELD_DP64(base, TLBENTRY_64, RPLV, 0); + base = FIELD_DP64(base, TLBENTRY_64, NX, 0); + base = FIELD_DP64(base, TLBENTRY_64, NR, 0); + /* mask other attribute bits */ + *physical = base & TARGET_PAGE_MASK; + + return 0; +} + +static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx, + int is_debug) +{ + int ret; + + if (tcg_enabled()) { + ret = loongarch_get_addr_from_tlb(env, physical, prot, address, + access_type, mmu_idx); + if (ret != TLBRET_NOMATCH) { + return ret; + } + } + + if (is_debug) { + /* + * For debugger memory access, we want to do the map when there is a + * legal mapping, even if the mapping is not yet in TLB. return 0 if + * there is a valid map, else none zero. + */ + return loongarch_page_table_walker(env, physical, prot, address); + } + + return TLBRET_NOMATCH; +} + +static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, + target_ulong dmw) +{ + if (is_la64(env)) { + return va & TARGET_VIRT_MASK; + } else { + uint32_t pseg = FIELD_EX32(dmw, CSR_DMW_32, PSEG); + return (va & MAKE_64BIT_MASK(0, R_CSR_DMW_32_VSEG_SHIFT)) | \ + (pseg << R_CSR_DMW_32_VSEG_SHIFT); + } +} + +int get_physical_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx, int is_debug) +{ + 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, is_debug); +} + +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), 1) != 0) { + return -1; + } + return phys_addr; +} diff --git a/target/loongarch/csr.c b/target/loongarch/csr.c new file mode 100644 index 00000000000..7ea0a304506 --- /dev/null +++ b/target/loongarch/csr.c @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2025 Loongson Technology Corporation Limited + */ +#include +#include "qemu/osdep.h" +#include "cpu.h" +#include "csr.h" + +#define CSR_OFF_FUNCS(NAME, FL, RD, WR) \ + [LOONGARCH_CSR_##NAME] = { \ + .name = (stringify(NAME)), \ + .offset = offsetof(CPULoongArchState, CSR_##NAME), \ + .flags = FL, .readfn = RD, .writefn = WR \ + } + +#define CSR_OFF_ARRAY(NAME, N) \ + [LOONGARCH_CSR_##NAME(N)] = { \ + .name = (stringify(NAME##N)), \ + .offset = offsetof(CPULoongArchState, CSR_##NAME[N]), \ + .flags = 0, .readfn = NULL, .writefn = NULL \ + } + +#define CSR_OFF_FLAGS(NAME, FL) CSR_OFF_FUNCS(NAME, FL, NULL, NULL) +#define CSR_OFF(NAME) CSR_OFF_FLAGS(NAME, 0) + +static CSRInfo csr_info[] = { + CSR_OFF_FLAGS(CRMD, CSRFL_EXITTB), + CSR_OFF(PRMD), + CSR_OFF_FLAGS(EUEN, CSRFL_EXITTB), + CSR_OFF_FLAGS(MISC, CSRFL_READONLY), + CSR_OFF(ECFG), + CSR_OFF_FLAGS(ESTAT, CSRFL_EXITTB), + CSR_OFF(ERA), + CSR_OFF(BADV), + CSR_OFF_FLAGS(BADI, CSRFL_READONLY), + CSR_OFF(EENTRY), + CSR_OFF(TLBIDX), + CSR_OFF(TLBEHI), + CSR_OFF(TLBELO0), + CSR_OFF(TLBELO1), + CSR_OFF_FLAGS(ASID, CSRFL_EXITTB), + CSR_OFF(PGDL), + CSR_OFF(PGDH), + CSR_OFF_FLAGS(PGD, CSRFL_READONLY), + CSR_OFF(PWCL), + CSR_OFF(PWCH), + CSR_OFF(STLBPS), + CSR_OFF(RVACFG), + CSR_OFF_FLAGS(CPUID, CSRFL_READONLY), + CSR_OFF_FLAGS(PRCFG1, CSRFL_READONLY), + CSR_OFF_FLAGS(PRCFG2, CSRFL_READONLY), + CSR_OFF_FLAGS(PRCFG3, CSRFL_READONLY), + CSR_OFF_ARRAY(SAVE, 0), + CSR_OFF_ARRAY(SAVE, 1), + CSR_OFF_ARRAY(SAVE, 2), + CSR_OFF_ARRAY(SAVE, 3), + CSR_OFF_ARRAY(SAVE, 4), + CSR_OFF_ARRAY(SAVE, 5), + CSR_OFF_ARRAY(SAVE, 6), + CSR_OFF_ARRAY(SAVE, 7), + CSR_OFF_ARRAY(SAVE, 8), + CSR_OFF_ARRAY(SAVE, 9), + CSR_OFF_ARRAY(SAVE, 10), + CSR_OFF_ARRAY(SAVE, 11), + CSR_OFF_ARRAY(SAVE, 12), + CSR_OFF_ARRAY(SAVE, 13), + CSR_OFF_ARRAY(SAVE, 14), + CSR_OFF_ARRAY(SAVE, 15), + CSR_OFF(TID), + CSR_OFF_FLAGS(TCFG, CSRFL_IO), + CSR_OFF_FLAGS(TVAL, CSRFL_READONLY | CSRFL_IO), + CSR_OFF(CNTC), + CSR_OFF_FLAGS(TICLR, CSRFL_IO), + CSR_OFF(LLBCTL), + CSR_OFF(IMPCTL1), + CSR_OFF(IMPCTL2), + CSR_OFF(TLBRENTRY), + CSR_OFF(TLBRBADV), + CSR_OFF(TLBRERA), + CSR_OFF(TLBRSAVE), + CSR_OFF(TLBRELO0), + CSR_OFF(TLBRELO1), + CSR_OFF(TLBREHI), + CSR_OFF(TLBRPRMD), + CSR_OFF(MERRCTL), + CSR_OFF(MERRINFO1), + CSR_OFF(MERRINFO2), + CSR_OFF(MERRENTRY), + CSR_OFF(MERRERA), + CSR_OFF(MERRSAVE), + CSR_OFF(CTAG), + CSR_OFF_ARRAY(DMW, 0), + CSR_OFF_ARRAY(DMW, 1), + CSR_OFF_ARRAY(DMW, 2), + CSR_OFF_ARRAY(DMW, 3), + CSR_OFF(DBG), + CSR_OFF(DERA), + CSR_OFF(DSAVE), +}; + +CSRInfo *get_csr(unsigned int csr_num) +{ + CSRInfo *csr; + + if (csr_num >= ARRAY_SIZE(csr_info)) { + return NULL; + } + + csr = &csr_info[csr_num]; + if (csr->offset == 0) { + return NULL; + } + + return csr; +} + +bool set_csr_flag(unsigned int csr_num, int flag) +{ + CSRInfo *csr; + + csr = get_csr(csr_num); + if (!csr) { + return false; + } + + csr->flags |= flag; + return true; +} diff --git a/target/loongarch/csr.h b/target/loongarch/csr.h new file mode 100644 index 00000000000..81a656baaed --- /dev/null +++ b/target/loongarch/csr.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2025 Loongson Technology Corporation Limited + */ + +#ifndef TARGET_LOONGARCH_CSR_H +#define TARGET_LOONGARCH_CSR_H + +#include "cpu-csr.h" + +typedef void (*GenCSRFunc)(void); +enum { + CSRFL_READONLY = (1 << 0), + CSRFL_EXITTB = (1 << 1), + CSRFL_IO = (1 << 2), + CSRFL_UNUSED = (1 << 3), +}; + +typedef struct { + const char *name; + int offset; + int flags; + GenCSRFunc readfn; + GenCSRFunc writefn; +} CSRInfo; + +CSRInfo *get_csr(unsigned int csr_num); +bool set_csr_flag(unsigned int csr_num, int flag); +#endif /* TARGET_LOONGARCH_CSR_H */ diff --git a/target/loongarch/csr_helper.c b/target/loongarch/csr_helper.c deleted file mode 100644 index 55341551a5c..00000000000 --- a/target/loongarch/csr_helper.c +++ /dev/null @@ -1,97 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * LoongArch emulation helpers for CSRs - * - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -#include "qemu/osdep.h" -#include "qemu/main-loop.h" -#include "cpu.h" -#include "internals.h" -#include "qemu/host-utils.h" -#include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "hw/irq.h" -#include "cpu-csr.h" - -target_ulong helper_csrrd_pgd(CPULoongArchState *env) -{ - int64_t v; - - if (env->CSR_TLBRERA & 0x1) { - v = env->CSR_TLBRBADV; - } else { - v = env->CSR_BADV; - } - - if ((v >> 63) & 0x1) { - v = env->CSR_PGDH; - } else { - v = env->CSR_PGDL; - } - - 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); - - return cpu_loongarch_get_constant_timer_ticks(cpu); -} - -target_ulong helper_csrwr_estat(CPULoongArchState *env, target_ulong val) -{ - int64_t old_v = env->CSR_ESTAT; - - /* Only IS[1:0] can be written */ - env->CSR_ESTAT = deposit64(env->CSR_ESTAT, 0, 2, val); - - return old_v; -} - -target_ulong helper_csrwr_asid(CPULoongArchState *env, target_ulong val) -{ - int64_t old_v = env->CSR_ASID; - - /* Only ASID filed of CSR_ASID can be written */ - env->CSR_ASID = deposit64(env->CSR_ASID, 0, 10, val); - if (old_v != env->CSR_ASID) { - tlb_flush(env_cpu(env)); - } - return old_v; -} - -target_ulong helper_csrwr_tcfg(CPULoongArchState *env, target_ulong val) -{ - LoongArchCPU *cpu = env_archcpu(env); - int64_t old_v = env->CSR_TCFG; - - cpu_loongarch_store_constant_timer_config(cpu, val); - - return old_v; -} - -target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val) -{ - LoongArchCPU *cpu = env_archcpu(env); - int64_t old_v = 0; - - if (val & 0x1) { - qemu_mutex_lock_iothread(); - loongarch_cpu_set_irq(cpu, IRQ_TIMER, 0); - qemu_mutex_unlock_iothread(); - } - return old_v; -} diff --git a/target/loongarch/disas.c b/target/loongarch/disas.c index 5c402d944d7..63989a6282d 100644 --- a/target/loongarch/disas.c +++ b/target/loongarch/disas.c @@ -120,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" @@ -190,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); @@ -549,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) @@ -1695,3 +1707,927 @@ 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 f6753c5875b..00000000000 --- 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); -} - -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 b09804b62f6..471eda28c73 100644 --- a/target/loongarch/gdbstub.c +++ b/target/loongarch/gdbstub.c @@ -11,6 +11,7 @@ #include "internals.h" #include "exec/gdbstub.h" #include "gdbstub/helpers.h" +#include "vec.h" uint64_t read_fcc(CPULoongArchState *env) { @@ -32,60 +33,63 @@ 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; - uint64_t val; - - if (0 <= n && 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; - } + CPULoongArchState *env = cpu_env(cs); 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; + CPULoongArchState *env = cpu_env(cs); target_ulong tmp; - int read_length; int length = 0; + if (n < 0 || n > 34) { + return 0; + } + if (is_la64(env)) { - tmp = ldq_p(mem_buf); - read_length = 8; + tmp = ldq_le_p(mem_buf); + length = 8; } else { - tmp = ldl_p(mem_buf); - read_length = 4; + tmp = ldl_le_p(mem_buf); + length = 4; } if (0 <= n && n < 32) { env->gpr[n] = tmp; - length = read_length; } else if (n == 33) { set_pc(env, tmp); - length = read_length; } return length; } -static int loongarch_gdb_get_fpu(CPULoongArchState *env, - GByteArray *mem_buf, int n) +static int loongarch_gdb_get_fpu(CPUState *cs, GByteArray *mem_buf, int n) { + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = &cpu->env; + if (0 <= n && n < 32) { return gdb_get_reg64(mem_buf, env->fpr[n].vreg.D(0)); } else if (32 <= n && n < 40) { @@ -96,26 +100,96 @@ static int loongarch_gdb_get_fpu(CPULoongArchState *env, return 0; } -static int loongarch_gdb_set_fpu(CPULoongArchState *env, - uint8_t *mem_buf, int n) +static int loongarch_gdb_set_fpu(CPUState *cs, uint8_t *mem_buf, int n) { + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = &cpu->env; int length = 0; if (0 <= n && n < 32) { - env->fpr[n].vreg.D(0) = ldq_p(mem_buf); + env->fpr[n].vreg.D(0) = ldq_le_p(mem_buf); length = 8; } else if (32 <= n && n < 40) { env->cf[n - 32] = ldub_p(mem_buf); length = 1; } else if (n == 40) { - env->fcsr0 = ldl_p(mem_buf); + 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 ffb1e0b0bf6..99981abd01c 100644 --- a/target/loongarch/helper.h +++ b/target/loongarch/helper.h @@ -1,699 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (c) 2021 Loongson Technology Corporation Limited + * Copyright (c) 2025 Loongson Technology Corporation Limited */ -DEF_HELPER_2(raise_exception, noreturn, env, i32) - -DEF_HELPER_FLAGS_1(bitrev_w, TCG_CALL_NO_RWG_SE, tl, tl) -DEF_HELPER_FLAGS_1(bitrev_d, TCG_CALL_NO_RWG_SE, tl, tl) -DEF_HELPER_FLAGS_1(bitswap, TCG_CALL_NO_RWG_SE, tl, tl) - -DEF_HELPER_FLAGS_3(asrtle_d, TCG_CALL_NO_WG, void, env, tl, tl) -DEF_HELPER_FLAGS_3(asrtgt_d, TCG_CALL_NO_WG, void, env, tl, tl) - -DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) -DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) -DEF_HELPER_FLAGS_2(cpucfg, TCG_CALL_NO_RWG_SE, tl, env, tl) - -/* Floating-point helper */ -DEF_HELPER_FLAGS_3(fadd_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fsub_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fsub_d, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmul_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmul_d, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fdiv_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fdiv_d, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmax_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmax_d, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmin_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmin_d, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmaxa_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmaxa_d, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmina_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmina_d, TCG_CALL_NO_WG, i64, env, i64, i64) - -DEF_HELPER_FLAGS_5(fmuladd_s, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i32) -DEF_HELPER_FLAGS_5(fmuladd_d, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i32) - -DEF_HELPER_FLAGS_3(fscaleb_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fscaleb_d, TCG_CALL_NO_WG, i64, env, i64, i64) - -DEF_HELPER_FLAGS_2(flogb_s, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(flogb_d, TCG_CALL_NO_WG, i64, env, i64) - -DEF_HELPER_FLAGS_2(fsqrt_s, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(fsqrt_d, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(frsqrt_s, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(frsqrt_d, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(frecip_s, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(frecip_d, TCG_CALL_NO_WG, i64, env, i64) - -DEF_HELPER_FLAGS_2(fclass_s, TCG_CALL_NO_RWG_SE, i64, env, i64) -DEF_HELPER_FLAGS_2(fclass_d, TCG_CALL_NO_RWG_SE, i64, env, i64) - -/* fcmp.cXXX.s */ -DEF_HELPER_4(fcmp_c_s, i64, env, i64, i64, i32) -/* fcmp.sXXX.s */ -DEF_HELPER_4(fcmp_s_s, i64, env, i64, i64, i32) -/* fcmp.cXXX.d */ -DEF_HELPER_4(fcmp_c_d, i64, env, i64, i64, i32) -/* fcmp.sXXX.d */ -DEF_HELPER_4(fcmp_s_d, i64, env, i64, i64, i32) - -DEF_HELPER_2(fcvt_d_s, i64, env, i64) -DEF_HELPER_2(fcvt_s_d, i64, env, i64) -DEF_HELPER_2(ffint_d_w, i64, env, i64) -DEF_HELPER_2(ffint_d_l, i64, env, i64) -DEF_HELPER_2(ffint_s_w, i64, env, i64) -DEF_HELPER_2(ffint_s_l, i64, env, i64) -DEF_HELPER_2(ftintrm_l_s, i64, env, i64) -DEF_HELPER_2(ftintrm_l_d, i64, env, i64) -DEF_HELPER_2(ftintrm_w_s, i64, env, i64) -DEF_HELPER_2(ftintrm_w_d, i64, env, i64) -DEF_HELPER_2(ftintrp_l_s, i64, env, i64) -DEF_HELPER_2(ftintrp_l_d, i64, env, i64) -DEF_HELPER_2(ftintrp_w_s, i64, env, i64) -DEF_HELPER_2(ftintrp_w_d, i64, env, i64) -DEF_HELPER_2(ftintrz_l_s, i64, env, i64) -DEF_HELPER_2(ftintrz_l_d, i64, env, i64) -DEF_HELPER_2(ftintrz_w_s, i64, env, i64) -DEF_HELPER_2(ftintrz_w_d, i64, env, i64) -DEF_HELPER_2(ftintrne_l_s, i64, env, i64) -DEF_HELPER_2(ftintrne_l_d, i64, env, i64) -DEF_HELPER_2(ftintrne_w_s, i64, env, i64) -DEF_HELPER_2(ftintrne_w_d, i64, env, i64) -DEF_HELPER_2(ftint_l_s, i64, env, i64) -DEF_HELPER_2(ftint_l_d, i64, env, i64) -DEF_HELPER_2(ftint_w_s, i64, env, i64) -DEF_HELPER_2(ftint_w_d, i64, env, i64) -DEF_HELPER_2(frint_s, i64, env, i64) -DEF_HELPER_2(frint_d, i64, env, i64) - -DEF_HELPER_FLAGS_1(set_rounding_mode, TCG_CALL_NO_RWG, void, env) - -DEF_HELPER_1(rdtime_d, i64, env) - -#ifndef CONFIG_USER_ONLY -/* CSRs helper */ -DEF_HELPER_1(csrrd_pgd, i64, env) -DEF_HELPER_1(csrrd_cpuid, i64, env) -DEF_HELPER_1(csrrd_tval, i64, env) -DEF_HELPER_2(csrwr_estat, i64, env, tl) -DEF_HELPER_2(csrwr_asid, i64, env, tl) -DEF_HELPER_2(csrwr_tcfg, i64, env, tl) -DEF_HELPER_2(csrwr_ticlr, i64, env, tl) -DEF_HELPER_2(iocsrrd_b, i64, env, tl) -DEF_HELPER_2(iocsrrd_h, i64, env, tl) -DEF_HELPER_2(iocsrrd_w, i64, env, tl) -DEF_HELPER_2(iocsrrd_d, i64, env, tl) -DEF_HELPER_3(iocsrwr_b, void, env, tl, tl) -DEF_HELPER_3(iocsrwr_h, void, env, tl, tl) -DEF_HELPER_3(iocsrwr_w, void, env, tl, tl) -DEF_HELPER_3(iocsrwr_d, void, env, tl, tl) - -/* TLB helper */ -DEF_HELPER_1(tlbwr, void, env) -DEF_HELPER_1(tlbfill, void, env) -DEF_HELPER_1(tlbsrch, void, env) -DEF_HELPER_1(tlbrd, void, env) -DEF_HELPER_1(tlbclr, void, env) -DEF_HELPER_1(tlbflush, void, env) -DEF_HELPER_1(invtlb_all, void, env) -DEF_HELPER_2(invtlb_all_g, void, env, i32) -DEF_HELPER_2(invtlb_all_asid, void, env, tl) -DEF_HELPER_3(invtlb_page_asid, void, env, tl, tl) -DEF_HELPER_3(invtlb_page_asid_or_g, void, env, tl, tl) - -DEF_HELPER_4(lddir, tl, env, tl, tl, i32) -DEF_HELPER_4(ldpte, void, env, tl, tl, i32) -DEF_HELPER_1(ertn, void, env) -DEF_HELPER_1(idle, void, env) -#endif - -/* LoongArch LSX */ -DEF_HELPER_4(vhaddw_h_b, void, env, i32, i32, i32) -DEF_HELPER_4(vhaddw_w_h, void, env, i32, i32, i32) -DEF_HELPER_4(vhaddw_d_w, void, env, i32, i32, i32) -DEF_HELPER_4(vhaddw_q_d, void, env, i32, i32, i32) -DEF_HELPER_4(vhaddw_hu_bu, void, env, i32, i32, i32) -DEF_HELPER_4(vhaddw_wu_hu, void, env, i32, i32, i32) -DEF_HELPER_4(vhaddw_du_wu, void, env, i32, i32, i32) -DEF_HELPER_4(vhaddw_qu_du, void, env, i32, i32, i32) -DEF_HELPER_4(vhsubw_h_b, void, env, i32, i32, i32) -DEF_HELPER_4(vhsubw_w_h, void, env, i32, i32, i32) -DEF_HELPER_4(vhsubw_d_w, void, env, i32, i32, i32) -DEF_HELPER_4(vhsubw_q_d, void, env, i32, i32, i32) -DEF_HELPER_4(vhsubw_hu_bu, void, env, i32, i32, i32) -DEF_HELPER_4(vhsubw_wu_hu, void, env, i32, i32, i32) -DEF_HELPER_4(vhsubw_du_wu, void, env, i32, i32, i32) -DEF_HELPER_4(vhsubw_qu_du, void, env, i32, i32, 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_4(vdiv_b, void, env, i32, i32, i32) -DEF_HELPER_4(vdiv_h, void, env, i32, i32, i32) -DEF_HELPER_4(vdiv_w, void, env, i32, i32, i32) -DEF_HELPER_4(vdiv_d, void, env, i32, i32, i32) -DEF_HELPER_4(vdiv_bu, void, env, i32, i32, i32) -DEF_HELPER_4(vdiv_hu, void, env, i32, i32, i32) -DEF_HELPER_4(vdiv_wu, void, env, i32, i32, i32) -DEF_HELPER_4(vdiv_du, void, env, i32, i32, i32) -DEF_HELPER_4(vmod_b, void, env, i32, i32, i32) -DEF_HELPER_4(vmod_h, void, env, i32, i32, i32) -DEF_HELPER_4(vmod_w, void, env, i32, i32, i32) -DEF_HELPER_4(vmod_d, void, env, i32, i32, i32) -DEF_HELPER_4(vmod_bu, void, env, i32, i32, i32) -DEF_HELPER_4(vmod_hu, void, env, i32, i32, i32) -DEF_HELPER_4(vmod_wu, void, env, i32, i32, i32) -DEF_HELPER_4(vmod_du, void, env, i32, i32, 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_3(vexth_h_b, void, env, i32, i32) -DEF_HELPER_3(vexth_w_h, void, env, i32, i32) -DEF_HELPER_3(vexth_d_w, void, env, i32, i32) -DEF_HELPER_3(vexth_q_d, void, env, i32, i32) -DEF_HELPER_3(vexth_hu_bu, void, env, i32, i32) -DEF_HELPER_3(vexth_wu_hu, void, env, i32, i32) -DEF_HELPER_3(vexth_du_wu, void, env, i32, i32) -DEF_HELPER_3(vexth_qu_du, void, env, i32, 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_3(vmskltz_b, void, env, i32, i32) -DEF_HELPER_3(vmskltz_h, void, env, i32, i32) -DEF_HELPER_3(vmskltz_w, void, env, i32, i32) -DEF_HELPER_3(vmskltz_d, void, env, i32, i32) -DEF_HELPER_3(vmskgez_b, void, env, i32, i32) -DEF_HELPER_3(vmsknz_b, void, env, i32,i32) - -DEF_HELPER_FLAGS_4(vnori_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_4(vsllwil_h_b, void, env, i32, i32, i32) -DEF_HELPER_4(vsllwil_w_h, void, env, i32, i32, i32) -DEF_HELPER_4(vsllwil_d_w, void, env, i32, i32, i32) -DEF_HELPER_3(vextl_q_d, void, env, i32, i32) -DEF_HELPER_4(vsllwil_hu_bu, void, env, i32, i32, i32) -DEF_HELPER_4(vsllwil_wu_hu, void, env, i32, i32, i32) -DEF_HELPER_4(vsllwil_du_wu, void, env, i32, i32, i32) -DEF_HELPER_3(vextl_qu_du, void, env, i32, i32) - -DEF_HELPER_4(vsrlr_b, void, env, i32, i32, i32) -DEF_HELPER_4(vsrlr_h, void, env, i32, i32, i32) -DEF_HELPER_4(vsrlr_w, void, env, i32, i32, i32) -DEF_HELPER_4(vsrlr_d, void, env, i32, i32, i32) -DEF_HELPER_4(vsrlri_b, void, env, i32, i32, i32) -DEF_HELPER_4(vsrlri_h, void, env, i32, i32, i32) -DEF_HELPER_4(vsrlri_w, void, env, i32, i32, i32) -DEF_HELPER_4(vsrlri_d, void, env, i32, i32, i32) - -DEF_HELPER_4(vsrar_b, void, env, i32, i32, i32) -DEF_HELPER_4(vsrar_h, void, env, i32, i32, i32) -DEF_HELPER_4(vsrar_w, void, env, i32, i32, i32) -DEF_HELPER_4(vsrar_d, void, env, i32, i32, i32) -DEF_HELPER_4(vsrari_b, void, env, i32, i32, i32) -DEF_HELPER_4(vsrari_h, void, env, i32, i32, i32) -DEF_HELPER_4(vsrari_w, void, env, i32, i32, i32) -DEF_HELPER_4(vsrari_d, void, env, i32, i32, i32) - -DEF_HELPER_4(vsrln_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vsrln_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vsrln_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vsran_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vsran_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vsran_w_d, void, env, i32, i32, i32) - -DEF_HELPER_4(vsrlni_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vsrlni_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vsrlni_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vsrlni_d_q, void, env, i32, i32, i32) -DEF_HELPER_4(vsrani_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vsrani_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vsrani_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vsrani_d_q, void, env, i32, i32, i32) - -DEF_HELPER_4(vsrlrn_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vsrlrn_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vsrlrn_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vsrarn_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vsrarn_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vsrarn_w_d, void, env, i32, i32, i32) - -DEF_HELPER_4(vsrlrni_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vsrlrni_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vsrlrni_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vsrlrni_d_q, void, env, i32, i32, i32) -DEF_HELPER_4(vsrarni_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vsrarni_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vsrarni_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vsrarni_d_q, void, env, i32, i32, i32) - -DEF_HELPER_4(vssrln_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssrln_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssrln_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vssran_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssran_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssran_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vssrln_bu_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssrln_hu_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssrln_wu_d, void, env, i32, i32, i32) -DEF_HELPER_4(vssran_bu_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssran_hu_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssran_wu_d, void, env, i32, i32, i32) - -DEF_HELPER_4(vssrlni_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlni_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlni_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlni_d_q, void, env, i32, i32, i32) -DEF_HELPER_4(vssrani_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssrani_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssrani_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vssrani_d_q, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlni_bu_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlni_hu_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlni_wu_d, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlni_du_q, void, env, i32, i32, i32) -DEF_HELPER_4(vssrani_bu_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssrani_hu_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssrani_wu_d, void, env, i32, i32, i32) -DEF_HELPER_4(vssrani_du_q, void, env, i32, i32, i32) - -DEF_HELPER_4(vssrlrn_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlrn_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlrn_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vssrarn_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssrarn_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssrarn_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlrn_bu_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlrn_hu_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlrn_wu_d, void, env, i32, i32, i32) -DEF_HELPER_4(vssrarn_bu_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssrarn_hu_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssrarn_wu_d, void, env, i32, i32, i32) - -DEF_HELPER_4(vssrlrni_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlrni_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlrni_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlrni_d_q, void, env, i32, i32, i32) -DEF_HELPER_4(vssrarni_b_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssrarni_h_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssrarni_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vssrarni_d_q, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlrni_bu_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlrni_hu_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlrni_wu_d, void, env, i32, i32, i32) -DEF_HELPER_4(vssrlrni_du_q, void, env, i32, i32, i32) -DEF_HELPER_4(vssrarni_bu_h, void, env, i32, i32, i32) -DEF_HELPER_4(vssrarni_hu_w, void, env, i32, i32, i32) -DEF_HELPER_4(vssrarni_wu_d, void, env, i32, i32, i32) -DEF_HELPER_4(vssrarni_du_q, void, env, i32, i32, i32) - -DEF_HELPER_3(vclo_b, void, env, i32, i32) -DEF_HELPER_3(vclo_h, void, env, i32, i32) -DEF_HELPER_3(vclo_w, void, env, i32, i32) -DEF_HELPER_3(vclo_d, void, env, i32, i32) -DEF_HELPER_3(vclz_b, void, env, i32, i32) -DEF_HELPER_3(vclz_h, void, env, i32, i32) -DEF_HELPER_3(vclz_w, void, env, i32, i32) -DEF_HELPER_3(vclz_d, void, env, i32, i32) - -DEF_HELPER_3(vpcnt_b, void, env, i32, i32) -DEF_HELPER_3(vpcnt_h, void, env, i32, i32) -DEF_HELPER_3(vpcnt_w, void, env, i32, i32) -DEF_HELPER_3(vpcnt_d, void, env, i32, 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_4(vfrstp_b, void, env, i32, i32, i32) -DEF_HELPER_4(vfrstp_h, void, env, i32, i32, i32) -DEF_HELPER_4(vfrstpi_b, void, env, i32, i32, i32) -DEF_HELPER_4(vfrstpi_h, void, env, i32, i32, i32) - -DEF_HELPER_4(vfadd_s, void, env, i32, i32, i32) -DEF_HELPER_4(vfadd_d, void, env, i32, i32, i32) -DEF_HELPER_4(vfsub_s, void, env, i32, i32, i32) -DEF_HELPER_4(vfsub_d, void, env, i32, i32, i32) -DEF_HELPER_4(vfmul_s, void, env, i32, i32, i32) -DEF_HELPER_4(vfmul_d, void, env, i32, i32, i32) -DEF_HELPER_4(vfdiv_s, void, env, i32, i32, i32) -DEF_HELPER_4(vfdiv_d, void, env, i32, i32, i32) - -DEF_HELPER_5(vfmadd_s, void, env, i32, i32, i32, i32) -DEF_HELPER_5(vfmadd_d, void, env, i32, i32, i32, i32) -DEF_HELPER_5(vfmsub_s, void, env, i32, i32, i32, i32) -DEF_HELPER_5(vfmsub_d, void, env, i32, i32, i32, i32) -DEF_HELPER_5(vfnmadd_s, void, env, i32, i32, i32, i32) -DEF_HELPER_5(vfnmadd_d, void, env, i32, i32, i32, i32) -DEF_HELPER_5(vfnmsub_s, void, env, i32, i32, i32, i32) -DEF_HELPER_5(vfnmsub_d, void, env, i32, i32, i32, i32) - -DEF_HELPER_4(vfmax_s, void, env, i32, i32, i32) -DEF_HELPER_4(vfmax_d, void, env, i32, i32, i32) -DEF_HELPER_4(vfmin_s, void, env, i32, i32, i32) -DEF_HELPER_4(vfmin_d, void, env, i32, i32, i32) - -DEF_HELPER_4(vfmaxa_s, void, env, i32, i32, i32) -DEF_HELPER_4(vfmaxa_d, void, env, i32, i32, i32) -DEF_HELPER_4(vfmina_s, void, env, i32, i32, i32) -DEF_HELPER_4(vfmina_d, void, env, i32, i32, i32) - -DEF_HELPER_3(vflogb_s, void, env, i32, i32) -DEF_HELPER_3(vflogb_d, void, env, i32, i32) - -DEF_HELPER_3(vfclass_s, void, env, i32, i32) -DEF_HELPER_3(vfclass_d, void, env, i32, i32) - -DEF_HELPER_3(vfsqrt_s, void, env, i32, i32) -DEF_HELPER_3(vfsqrt_d, void, env, i32, i32) -DEF_HELPER_3(vfrecip_s, void, env, i32, i32) -DEF_HELPER_3(vfrecip_d, void, env, i32, i32) -DEF_HELPER_3(vfrsqrt_s, void, env, i32, i32) -DEF_HELPER_3(vfrsqrt_d, void, env, i32, i32) - -DEF_HELPER_3(vfcvtl_s_h, void, env, i32, i32) -DEF_HELPER_3(vfcvth_s_h, void, env, i32, i32) -DEF_HELPER_3(vfcvtl_d_s, void, env, i32, i32) -DEF_HELPER_3(vfcvth_d_s, void, env, i32, i32) -DEF_HELPER_4(vfcvt_h_s, void, env, i32, i32, i32) -DEF_HELPER_4(vfcvt_s_d, void, env, i32, i32, i32) - -DEF_HELPER_3(vfrintrne_s, void, env, i32, i32) -DEF_HELPER_3(vfrintrne_d, void, env, i32, i32) -DEF_HELPER_3(vfrintrz_s, void, env, i32, i32) -DEF_HELPER_3(vfrintrz_d, void, env, i32, i32) -DEF_HELPER_3(vfrintrp_s, void, env, i32, i32) -DEF_HELPER_3(vfrintrp_d, void, env, i32, i32) -DEF_HELPER_3(vfrintrm_s, void, env, i32, i32) -DEF_HELPER_3(vfrintrm_d, void, env, i32, i32) -DEF_HELPER_3(vfrint_s, void, env, i32, i32) -DEF_HELPER_3(vfrint_d, void, env, i32, i32) - -DEF_HELPER_3(vftintrne_w_s, void, env, i32, i32) -DEF_HELPER_3(vftintrne_l_d, void, env, i32, i32) -DEF_HELPER_3(vftintrz_w_s, void, env, i32, i32) -DEF_HELPER_3(vftintrz_l_d, void, env, i32, i32) -DEF_HELPER_3(vftintrp_w_s, void, env, i32, i32) -DEF_HELPER_3(vftintrp_l_d, void, env, i32, i32) -DEF_HELPER_3(vftintrm_w_s, void, env, i32, i32) -DEF_HELPER_3(vftintrm_l_d, void, env, i32, i32) -DEF_HELPER_3(vftint_w_s, void, env, i32, i32) -DEF_HELPER_3(vftint_l_d, void, env, i32, i32) -DEF_HELPER_3(vftintrz_wu_s, void, env, i32, i32) -DEF_HELPER_3(vftintrz_lu_d, void, env, i32, i32) -DEF_HELPER_3(vftint_wu_s, void, env, i32, i32) -DEF_HELPER_3(vftint_lu_d, void, env, i32, i32) -DEF_HELPER_4(vftintrne_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vftintrz_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vftintrp_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vftintrm_w_d, void, env, i32, i32, i32) -DEF_HELPER_4(vftint_w_d, void, env, i32, i32, i32) -DEF_HELPER_3(vftintrnel_l_s, void, env, i32, i32) -DEF_HELPER_3(vftintrneh_l_s, void, env, i32, i32) -DEF_HELPER_3(vftintrzl_l_s, void, env, i32, i32) -DEF_HELPER_3(vftintrzh_l_s, void, env, i32, i32) -DEF_HELPER_3(vftintrpl_l_s, void, env, i32, i32) -DEF_HELPER_3(vftintrph_l_s, void, env, i32, i32) -DEF_HELPER_3(vftintrml_l_s, void, env, i32, i32) -DEF_HELPER_3(vftintrmh_l_s, void, env, i32, i32) -DEF_HELPER_3(vftintl_l_s, void, env, i32, i32) -DEF_HELPER_3(vftinth_l_s, void, env, i32, i32) - -DEF_HELPER_3(vffint_s_w, void, env, i32, i32) -DEF_HELPER_3(vffint_d_l, void, env, i32, i32) -DEF_HELPER_3(vffint_s_wu, void, env, i32, i32) -DEF_HELPER_3(vffint_d_lu, void, env, i32, i32) -DEF_HELPER_3(vffintl_d_w, void, env, i32, i32) -DEF_HELPER_3(vffinth_d_w, void, env, i32, i32) -DEF_HELPER_4(vffint_s_l, void, env, i32, i32, 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_5(vfcmp_c_s, void, env, i32, i32, i32, i32) -DEF_HELPER_5(vfcmp_s_s, void, env, i32, i32, i32, i32) -DEF_HELPER_5(vfcmp_c_d, void, env, i32, i32, i32, i32) -DEF_HELPER_5(vfcmp_s_d, void, env, i32, i32, i32, i32) - -DEF_HELPER_FLAGS_4(vbitseli_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_3(vsetanyeqz_b, void, env, i32, i32) -DEF_HELPER_3(vsetanyeqz_h, void, env, i32, i32) -DEF_HELPER_3(vsetanyeqz_w, void, env, i32, i32) -DEF_HELPER_3(vsetanyeqz_d, void, env, i32, i32) -DEF_HELPER_3(vsetallnez_b, void, env, i32, i32) -DEF_HELPER_3(vsetallnez_h, void, env, i32, i32) -DEF_HELPER_3(vsetallnez_w, void, env, i32, i32) -DEF_HELPER_3(vsetallnez_d, void, env, i32, i32) - -DEF_HELPER_4(vpackev_b, void, env, i32, i32, i32) -DEF_HELPER_4(vpackev_h, void, env, i32, i32, i32) -DEF_HELPER_4(vpackev_w, void, env, i32, i32, i32) -DEF_HELPER_4(vpackev_d, void, env, i32, i32, i32) -DEF_HELPER_4(vpackod_b, void, env, i32, i32, i32) -DEF_HELPER_4(vpackod_h, void, env, i32, i32, i32) -DEF_HELPER_4(vpackod_w, void, env, i32, i32, i32) -DEF_HELPER_4(vpackod_d, void, env, i32, i32, i32) - -DEF_HELPER_4(vpickev_b, void, env, i32, i32, i32) -DEF_HELPER_4(vpickev_h, void, env, i32, i32, i32) -DEF_HELPER_4(vpickev_w, void, env, i32, i32, i32) -DEF_HELPER_4(vpickev_d, void, env, i32, i32, i32) -DEF_HELPER_4(vpickod_b, void, env, i32, i32, i32) -DEF_HELPER_4(vpickod_h, void, env, i32, i32, i32) -DEF_HELPER_4(vpickod_w, void, env, i32, i32, i32) -DEF_HELPER_4(vpickod_d, void, env, i32, i32, i32) - -DEF_HELPER_4(vilvl_b, void, env, i32, i32, i32) -DEF_HELPER_4(vilvl_h, void, env, i32, i32, i32) -DEF_HELPER_4(vilvl_w, void, env, i32, i32, i32) -DEF_HELPER_4(vilvl_d, void, env, i32, i32, i32) -DEF_HELPER_4(vilvh_b, void, env, i32, i32, i32) -DEF_HELPER_4(vilvh_h, void, env, i32, i32, i32) -DEF_HELPER_4(vilvh_w, void, env, i32, i32, i32) -DEF_HELPER_4(vilvh_d, void, env, i32, i32, i32) - -DEF_HELPER_5(vshuf_b, void, env, i32, i32, i32, i32) -DEF_HELPER_4(vshuf_h, void, env, i32, i32, i32) -DEF_HELPER_4(vshuf_w, void, env, i32, i32, i32) -DEF_HELPER_4(vshuf_d, void, env, i32, i32, i32) -DEF_HELPER_4(vshuf4i_b, void, env, i32, i32, i32) -DEF_HELPER_4(vshuf4i_h, void, env, i32, i32, i32) -DEF_HELPER_4(vshuf4i_w, void, env, i32, i32, i32) -DEF_HELPER_4(vshuf4i_d, void, env, i32, i32, i32) - -DEF_HELPER_4(vpermi_w, void, env, i32, i32, i32) - -DEF_HELPER_4(vextrins_b, void, env, i32, i32, i32) -DEF_HELPER_4(vextrins_h, void, env, i32, i32, i32) -DEF_HELPER_4(vextrins_w, void, env, i32, i32, i32) -DEF_HELPER_4(vextrins_d, void, env, i32, i32, i32) +#include "tcg/helper.h" 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 dd5d02e88c4..00000000000 --- a/target/loongarch/insn_trans/trans_extra.c.inc +++ /dev/null @@ -1,107 +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); - - if (!avail_64(ctx)) { - return false; - } - - 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); - - if (!avail_64(ctx)) { - return false; - } - - 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); - - translator_io_start(&ctx->base); - 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, 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/insn_trans/trans_fcmp.c.inc b/target/loongarch/insn_trans/trans_fcmp.c.inc deleted file mode 100644 index 43d5866a678..00000000000 --- a/target/loongarch/insn_trans/trans_fcmp.c.inc +++ /dev/null @@ -1,72 +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, src1, src2; - uint32_t flags; - void (*fn)(TCGv, TCGv_env, TCGv, TCGv, TCGv_i32); - - if (!avail_FP_SP(ctx)) { - return false; - } - - CHECK_FPE; - - var = tcg_temp_new(); - src1 = get_fpr(ctx, a->fj); - src2 = get_fpr(ctx, a->fk); - fn = (a->fcond & 1 ? gen_helper_fcmp_s_s : gen_helper_fcmp_c_s); - flags = get_fcmp_flags(a->fcond >> 1); - - fn(var, cpu_env, src1, src2, tcg_constant_i32(flags)); - - tcg_gen_st8_tl(var, cpu_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, cpu_env, src1, src2, tcg_constant_i32(flags)); - - tcg_gen_st8_tl(var, cpu_env, offsetof(CPULoongArchState, cf[a->cd])); - return true; -} diff --git a/target/loongarch/insn_trans/trans_lsx.c.inc b/target/loongarch/insn_trans/trans_lsx.c.inc deleted file mode 100644 index 5fbf2718f7e..00000000000 --- a/target/loongarch/insn_trans/trans_lsx.c.inc +++ /dev/null @@ -1,4555 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * LSX translate functions - * Copyright (c) 2022-2023 Loongson Technology Corporation Limited - */ - -#ifndef CONFIG_USER_ONLY -#define CHECK_SXE do { \ - if ((ctx->base.tb->flags & HW_FLAGS_EUEN_SXE) == 0) { \ - generate_exception(ctx, EXCCODE_SXD); \ - return true; \ - } \ -} while (0) -#else -#define CHECK_SXE -#endif - -static bool gen_vvvv(DisasContext *ctx, arg_vvvv *a, - void (*func)(TCGv_ptr, 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 va = tcg_constant_i32(a->va); - - CHECK_SXE; - func(cpu_env, vd, vj, vk, va); - return true; -} - -static bool gen_vvv(DisasContext *ctx, arg_vvv *a, - void (*func)(TCGv_ptr, 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); - - CHECK_SXE; - - func(cpu_env, vd, vj, vk); - return true; -} - -static bool gen_vv(DisasContext *ctx, arg_vv *a, - void (*func)(TCGv_ptr, TCGv_i32, TCGv_i32)) -{ - TCGv_i32 vd = tcg_constant_i32(a->vd); - TCGv_i32 vj = tcg_constant_i32(a->vj); - - CHECK_SXE; - func(cpu_env, vd, vj); - return true; -} - -static bool gen_vv_i(DisasContext *ctx, arg_vv_i *a, - void (*func)(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32)) -{ - TCGv_i32 vd = tcg_constant_i32(a->vd); - TCGv_i32 vj = tcg_constant_i32(a->vj); - TCGv_i32 imm = tcg_constant_i32(a->imm); - - CHECK_SXE; - func(cpu_env, vd, vj, imm); - return true; -} - -static bool gen_cv(DisasContext *ctx, arg_cv *a, - void (*func)(TCGv_ptr, TCGv_i32, TCGv_i32)) -{ - TCGv_i32 vj = tcg_constant_i32(a->vj); - TCGv_i32 cd = tcg_constant_i32(a->cd); - - CHECK_SXE; - func(cpu_env, cd, vj); - 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)) -{ - uint32_t vd_ofs, vj_ofs, vk_ofs; - - CHECK_SXE; - - vd_ofs = vec_full_offset(a->vd); - vj_ofs = vec_full_offset(a->vj); - vk_ofs = vec_full_offset(a->vk); - - func(mop, vd_ofs, vj_ofs, vk_ofs, 16, 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)) -{ - uint32_t vd_ofs, vj_ofs; - - CHECK_SXE; - - vd_ofs = vec_full_offset(a->vd); - vj_ofs = vec_full_offset(a->vj); - - func(mop, vd_ofs, vj_ofs, 16, 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)) -{ - uint32_t vd_ofs, vj_ofs; - - CHECK_SXE; - - vd_ofs = vec_full_offset(a->vd); - vj_ofs = vec_full_offset(a->vj); - - func(mop, vd_ofs, vj_ofs, a->imm , 16, ctx->vl/8); - return true; -} - -static bool gvec_subi(DisasContext *ctx, arg_vv_i *a, MemOp mop) -{ - uint32_t vd_ofs, vj_ofs; - - CHECK_SXE; - - vd_ofs = vec_full_offset(a->vd); - vj_ofs = vec_full_offset(a->vj); - - tcg_gen_gvec_addi(mop, vd_ofs, vj_ofs, -a->imm, 16, ctx->vl/8); - return true; -} - -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) - -#define VADDSUB_Q(NAME) \ -static bool trans_v## NAME ##_q(DisasContext *ctx, arg_vvv *a) \ -{ \ - TCGv_i64 rh, rl, ah, al, bh, bl; \ - \ - if (!avail_LSX(ctx)) { \ - return false; \ - } \ - \ - CHECK_SXE; \ - \ - 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(); \ - \ - get_vreg64(ah, a->vj, 1); \ - get_vreg64(al, a->vj, 0); \ - get_vreg64(bh, a->vk, 1); \ - get_vreg64(bl, a->vk, 0); \ - \ - tcg_gen_## NAME ##2_i64(rl, rh, al, ah, bl, bh); \ - \ - set_vreg64(rh, a->vd, 1); \ - set_vreg64(rl, a->vd, 0); \ - \ - return true; \ -} - -VADDSUB_Q(add) -VADDSUB_Q(sub) - -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(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(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(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(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) - -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) - -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) - -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) - -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) - -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) - -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) - -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) - -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) - -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) - -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) - -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) - -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) - -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) - -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(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(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) - -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) - -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(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) - -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) - -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) - -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) - -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); -} - -#define VMUL_Q(NAME, FN, idx1, idx2) \ -static bool trans_## NAME (DisasContext *ctx, arg_vvv *a) \ -{ \ - TCGv_i64 rh, rl, arg1, arg2; \ - \ - if (!avail_LSX(ctx)) { \ - return false; \ - } \ - \ - rh = tcg_temp_new_i64(); \ - rl = tcg_temp_new_i64(); \ - arg1 = tcg_temp_new_i64(); \ - arg2 = tcg_temp_new_i64(); \ - \ - get_vreg64(arg1, a->vj, idx1); \ - get_vreg64(arg2, a->vk, idx2); \ - \ - tcg_gen_## FN ##_i64(rl, rh, arg1, arg2); \ - \ - set_vreg64(rh, a->vd, 1); \ - set_vreg64(rl, a->vd, 0); \ - \ - return true; \ -} - -VMUL_Q(vmulwev_q_d, muls2, 0, 0) -VMUL_Q(vmulwod_q_d, muls2, 1, 1) -VMUL_Q(vmulwev_q_du, mulu2, 0, 0) -VMUL_Q(vmulwod_q_du, mulu2, 1, 1) -VMUL_Q(vmulwev_q_du_d, mulus2, 0, 0) -VMUL_Q(vmulwod_q_du_d, mulus2, 1, 1) - -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) - -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) - -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) - -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) - -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) - -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) - -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) - -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) - -#define VMADD_Q(NAME, FN, idx1, idx2) \ -static bool trans_## NAME (DisasContext *ctx, arg_vvv *a) \ -{ \ - TCGv_i64 rh, rl, arg1, arg2, th, tl; \ - \ - if (!avail_LSX(ctx)) { \ - return false; \ - } \ - \ - 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(); \ - \ - get_vreg64(arg1, a->vj, idx1); \ - get_vreg64(arg2, a->vk, idx2); \ - get_vreg64(rh, a->vd, 1); \ - get_vreg64(rl, a->vd, 0); \ - \ - tcg_gen_## FN ##_i64(tl, th, arg1, arg2); \ - tcg_gen_add2_i64(rl, rh, rl, rh, tl, th); \ - \ - set_vreg64(rh, a->vd, 1); \ - set_vreg64(rl, a->vd, 0); \ - \ - return true; \ -} - -VMADD_Q(vmaddwev_q_d, muls2, 0, 0) -VMADD_Q(vmaddwod_q_d, muls2, 1, 1) -VMADD_Q(vmaddwev_q_du, mulu2, 0, 0) -VMADD_Q(vmaddwod_q_du, mulu2, 1, 1) -VMADD_Q(vmaddwev_q_du_d, mulus2, 0, 0) -VMADD_Q(vmaddwod_q_du_d, mulus2, 1, 1) - -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) - -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) - -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) - -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) - -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(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) - -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) - -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(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) - -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(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) - -#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 trans_vldi(DisasContext *ctx, arg_vldi *a) -{ - int sel, vece; - uint64_t value; - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - - 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), 16, ctx->vl/8, - tcg_constant_i64(value)); - return true; -} - -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) - -static bool trans_vandn_v(DisasContext *ctx, arg_vvv *a) -{ - uint32_t vd_ofs, vj_ofs, vk_ofs; - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - - 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, 16, ctx->vl/8); - return true; -} -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) - -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(vnori_b, LSX, gvec_vv_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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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) - -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) - -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) - -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) - -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) - -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) - -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(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(vfadd_s, LSX, gen_vvv, gen_helper_vfadd_s) -TRANS(vfadd_d, LSX, gen_vvv, gen_helper_vfadd_d) -TRANS(vfsub_s, LSX, gen_vvv, gen_helper_vfsub_s) -TRANS(vfsub_d, LSX, gen_vvv, gen_helper_vfsub_d) -TRANS(vfmul_s, LSX, gen_vvv, gen_helper_vfmul_s) -TRANS(vfmul_d, LSX, gen_vvv, gen_helper_vfmul_d) -TRANS(vfdiv_s, LSX, gen_vvv, gen_helper_vfdiv_s) -TRANS(vfdiv_d, LSX, gen_vvv, gen_helper_vfdiv_d) - -TRANS(vfmadd_s, LSX, gen_vvvv, gen_helper_vfmadd_s) -TRANS(vfmadd_d, LSX, gen_vvvv, gen_helper_vfmadd_d) -TRANS(vfmsub_s, LSX, gen_vvvv, gen_helper_vfmsub_s) -TRANS(vfmsub_d, LSX, gen_vvvv, gen_helper_vfmsub_d) -TRANS(vfnmadd_s, LSX, gen_vvvv, gen_helper_vfnmadd_s) -TRANS(vfnmadd_d, LSX, gen_vvvv, gen_helper_vfnmadd_d) -TRANS(vfnmsub_s, LSX, gen_vvvv, gen_helper_vfnmsub_s) -TRANS(vfnmsub_d, LSX, gen_vvvv, gen_helper_vfnmsub_d) - -TRANS(vfmax_s, LSX, gen_vvv, gen_helper_vfmax_s) -TRANS(vfmax_d, LSX, gen_vvv, gen_helper_vfmax_d) -TRANS(vfmin_s, LSX, gen_vvv, gen_helper_vfmin_s) -TRANS(vfmin_d, LSX, gen_vvv, gen_helper_vfmin_d) - -TRANS(vfmaxa_s, LSX, gen_vvv, gen_helper_vfmaxa_s) -TRANS(vfmaxa_d, LSX, gen_vvv, gen_helper_vfmaxa_d) -TRANS(vfmina_s, LSX, gen_vvv, gen_helper_vfmina_s) -TRANS(vfmina_d, LSX, gen_vvv, gen_helper_vfmina_d) - -TRANS(vflogb_s, LSX, gen_vv, gen_helper_vflogb_s) -TRANS(vflogb_d, LSX, gen_vv, gen_helper_vflogb_d) - -TRANS(vfclass_s, LSX, gen_vv, gen_helper_vfclass_s) -TRANS(vfclass_d, LSX, gen_vv, gen_helper_vfclass_d) - -TRANS(vfsqrt_s, LSX, gen_vv, gen_helper_vfsqrt_s) -TRANS(vfsqrt_d, LSX, gen_vv, gen_helper_vfsqrt_d) -TRANS(vfrecip_s, LSX, gen_vv, gen_helper_vfrecip_s) -TRANS(vfrecip_d, LSX, gen_vv, gen_helper_vfrecip_d) -TRANS(vfrsqrt_s, LSX, gen_vv, gen_helper_vfrsqrt_s) -TRANS(vfrsqrt_d, LSX, gen_vv, gen_helper_vfrsqrt_d) - -TRANS(vfcvtl_s_h, LSX, gen_vv, gen_helper_vfcvtl_s_h) -TRANS(vfcvth_s_h, LSX, gen_vv, gen_helper_vfcvth_s_h) -TRANS(vfcvtl_d_s, LSX, gen_vv, gen_helper_vfcvtl_d_s) -TRANS(vfcvth_d_s, LSX, gen_vv, gen_helper_vfcvth_d_s) -TRANS(vfcvt_h_s, LSX, gen_vvv, gen_helper_vfcvt_h_s) -TRANS(vfcvt_s_d, LSX, gen_vvv, gen_helper_vfcvt_s_d) - -TRANS(vfrintrne_s, LSX, gen_vv, gen_helper_vfrintrne_s) -TRANS(vfrintrne_d, LSX, gen_vv, gen_helper_vfrintrne_d) -TRANS(vfrintrz_s, LSX, gen_vv, gen_helper_vfrintrz_s) -TRANS(vfrintrz_d, LSX, gen_vv, gen_helper_vfrintrz_d) -TRANS(vfrintrp_s, LSX, gen_vv, gen_helper_vfrintrp_s) -TRANS(vfrintrp_d, LSX, gen_vv, gen_helper_vfrintrp_d) -TRANS(vfrintrm_s, LSX, gen_vv, gen_helper_vfrintrm_s) -TRANS(vfrintrm_d, LSX, gen_vv, gen_helper_vfrintrm_d) -TRANS(vfrint_s, LSX, gen_vv, gen_helper_vfrint_s) -TRANS(vfrint_d, LSX, gen_vv, gen_helper_vfrint_d) - -TRANS(vftintrne_w_s, LSX, gen_vv, gen_helper_vftintrne_w_s) -TRANS(vftintrne_l_d, LSX, gen_vv, gen_helper_vftintrne_l_d) -TRANS(vftintrz_w_s, LSX, gen_vv, gen_helper_vftintrz_w_s) -TRANS(vftintrz_l_d, LSX, gen_vv, gen_helper_vftintrz_l_d) -TRANS(vftintrp_w_s, LSX, gen_vv, gen_helper_vftintrp_w_s) -TRANS(vftintrp_l_d, LSX, gen_vv, gen_helper_vftintrp_l_d) -TRANS(vftintrm_w_s, LSX, gen_vv, gen_helper_vftintrm_w_s) -TRANS(vftintrm_l_d, LSX, gen_vv, gen_helper_vftintrm_l_d) -TRANS(vftint_w_s, LSX, gen_vv, gen_helper_vftint_w_s) -TRANS(vftint_l_d, LSX, gen_vv, gen_helper_vftint_l_d) -TRANS(vftintrz_wu_s, LSX, gen_vv, gen_helper_vftintrz_wu_s) -TRANS(vftintrz_lu_d, LSX, gen_vv, gen_helper_vftintrz_lu_d) -TRANS(vftint_wu_s, LSX, gen_vv, gen_helper_vftint_wu_s) -TRANS(vftint_lu_d, LSX, gen_vv, gen_helper_vftint_lu_d) -TRANS(vftintrne_w_d, LSX, gen_vvv, gen_helper_vftintrne_w_d) -TRANS(vftintrz_w_d, LSX, gen_vvv, gen_helper_vftintrz_w_d) -TRANS(vftintrp_w_d, LSX, gen_vvv, gen_helper_vftintrp_w_d) -TRANS(vftintrm_w_d, LSX, gen_vvv, gen_helper_vftintrm_w_d) -TRANS(vftint_w_d, LSX, gen_vvv, gen_helper_vftint_w_d) -TRANS(vftintrnel_l_s, LSX, gen_vv, gen_helper_vftintrnel_l_s) -TRANS(vftintrneh_l_s, LSX, gen_vv, gen_helper_vftintrneh_l_s) -TRANS(vftintrzl_l_s, LSX, gen_vv, gen_helper_vftintrzl_l_s) -TRANS(vftintrzh_l_s, LSX, gen_vv, gen_helper_vftintrzh_l_s) -TRANS(vftintrpl_l_s, LSX, gen_vv, gen_helper_vftintrpl_l_s) -TRANS(vftintrph_l_s, LSX, gen_vv, gen_helper_vftintrph_l_s) -TRANS(vftintrml_l_s, LSX, gen_vv, gen_helper_vftintrml_l_s) -TRANS(vftintrmh_l_s, LSX, gen_vv, gen_helper_vftintrmh_l_s) -TRANS(vftintl_l_s, LSX, gen_vv, gen_helper_vftintl_l_s) -TRANS(vftinth_l_s, LSX, gen_vv, gen_helper_vftinth_l_s) - -TRANS(vffint_s_w, LSX, gen_vv, gen_helper_vffint_s_w) -TRANS(vffint_d_l, LSX, gen_vv, gen_helper_vffint_d_l) -TRANS(vffint_s_wu, LSX, gen_vv, gen_helper_vffint_s_wu) -TRANS(vffint_d_lu, LSX, gen_vv, gen_helper_vffint_d_lu) -TRANS(vffintl_d_w, LSX, gen_vv, gen_helper_vffintl_d_w) -TRANS(vffinth_d_w, LSX, gen_vv, gen_helper_vffinth_d_w) -TRANS(vffint_s_l, LSX, gen_vvv, gen_helper_vffint_s_l) - -static bool do_cmp(DisasContext *ctx, arg_vvv *a, MemOp mop, TCGCond cond) -{ - uint32_t vd_ofs, vj_ofs, vk_ofs; - - CHECK_SXE; - - 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, 16, ctx->vl/8); - return true; -} - -static void do_cmpi_vec(TCGCond cond, - unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) -{ - tcg_gen_cmp_vec(cond, vece, t, a, tcg_constant_vec_matching(t, vece, imm)); -} - -static void gen_vseqi_s_vec(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) -{ - do_cmpi_vec(TCG_COND_EQ, vece, t, a, imm); -} - -static void gen_vslei_s_vec(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) -{ - do_cmpi_vec(TCG_COND_LE, vece, t, a, imm); -} - -static void gen_vslti_s_vec(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) -{ - do_cmpi_vec(TCG_COND_LT, vece, t, a, imm); -} - -static void gen_vslei_u_vec(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) -{ - do_cmpi_vec(TCG_COND_LEU, vece, t, a, imm); -} - -static void gen_vslti_u_vec(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) -{ - do_cmpi_vec(TCG_COND_LTU, vece, t, a, imm); -} - -#define DO_CMPI_S(NAME) \ -static bool do_## NAME ##_s(DisasContext *ctx, arg_vv_i *a, MemOp mop) \ -{ \ - uint32_t vd_ofs, vj_ofs; \ - \ - CHECK_SXE; \ - \ - static const TCGOpcode vecop_list[] = { \ - INDEX_op_cmp_vec, 0 \ - }; \ - static const GVecGen2i op[4] = { \ - { \ - .fniv = gen_## NAME ##_s_vec, \ - .fnoi = gen_helper_## NAME ##_b, \ - .opt_opc = vecop_list, \ - .vece = MO_8 \ - }, \ - { \ - .fniv = gen_## NAME ##_s_vec, \ - .fnoi = gen_helper_## NAME ##_h, \ - .opt_opc = vecop_list, \ - .vece = MO_16 \ - }, \ - { \ - .fniv = gen_## NAME ##_s_vec, \ - .fnoi = gen_helper_## NAME ##_w, \ - .opt_opc = vecop_list, \ - .vece = MO_32 \ - }, \ - { \ - .fniv = gen_## NAME ##_s_vec, \ - .fnoi = gen_helper_## NAME ##_d, \ - .opt_opc = vecop_list, \ - .vece = MO_64 \ - } \ - }; \ - \ - vd_ofs = vec_full_offset(a->vd); \ - vj_ofs = vec_full_offset(a->vj); \ - \ - tcg_gen_gvec_2i(vd_ofs, vj_ofs, 16, ctx->vl/8, a->imm, &op[mop]); \ - \ - return true; \ -} - -DO_CMPI_S(vseqi) -DO_CMPI_S(vslei) -DO_CMPI_S(vslti) - -#define DO_CMPI_U(NAME) \ -static bool do_## NAME ##_u(DisasContext *ctx, arg_vv_i *a, MemOp mop) \ -{ \ - uint32_t vd_ofs, vj_ofs; \ - \ - CHECK_SXE; \ - \ - static const TCGOpcode vecop_list[] = { \ - INDEX_op_cmp_vec, 0 \ - }; \ - static const GVecGen2i op[4] = { \ - { \ - .fniv = gen_## NAME ##_u_vec, \ - .fnoi = gen_helper_## NAME ##_bu, \ - .opt_opc = vecop_list, \ - .vece = MO_8 \ - }, \ - { \ - .fniv = gen_## NAME ##_u_vec, \ - .fnoi = gen_helper_## NAME ##_hu, \ - .opt_opc = vecop_list, \ - .vece = MO_16 \ - }, \ - { \ - .fniv = gen_## NAME ##_u_vec, \ - .fnoi = gen_helper_## NAME ##_wu, \ - .opt_opc = vecop_list, \ - .vece = MO_32 \ - }, \ - { \ - .fniv = gen_## NAME ##_u_vec, \ - .fnoi = gen_helper_## NAME ##_du, \ - .opt_opc = vecop_list, \ - .vece = MO_64 \ - } \ - }; \ - \ - vd_ofs = vec_full_offset(a->vd); \ - vj_ofs = vec_full_offset(a->vj); \ - \ - tcg_gen_gvec_2i(vd_ofs, vj_ofs, 16, ctx->vl/8, a->imm, &op[mop]); \ - \ - return true; \ -} - -DO_CMPI_U(vslei) -DO_CMPI_U(vslti) - -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_vseqi_s, MO_8) -TRANS(vseqi_h, LSX, do_vseqi_s, MO_16) -TRANS(vseqi_w, LSX, do_vseqi_s, MO_32) -TRANS(vseqi_d, LSX, do_vseqi_s, MO_64) - -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_vslei_s, MO_8) -TRANS(vslei_h, LSX, do_vslei_s, MO_16) -TRANS(vslei_w, LSX, do_vslei_s, MO_32) -TRANS(vslei_d, LSX, do_vslei_s, MO_64) -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_vslei_u, MO_8) -TRANS(vslei_hu, LSX, do_vslei_u, MO_16) -TRANS(vslei_wu, LSX, do_vslei_u, MO_32) -TRANS(vslei_du, LSX, do_vslei_u, MO_64) - -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_vslti_s, MO_8) -TRANS(vslti_h, LSX, do_vslti_s, MO_16) -TRANS(vslti_w, LSX, do_vslti_s, MO_32) -TRANS(vslti_d, LSX, do_vslti_s, MO_64) -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_vslti_u, MO_8) -TRANS(vslti_hu, LSX, do_vslti_u, MO_16) -TRANS(vslti_wu, LSX, do_vslti_u, MO_32) -TRANS(vslti_du, LSX, do_vslti_u, MO_64) - -static bool trans_vfcmp_cond_s(DisasContext *ctx, arg_vvv_fcond *a) -{ - uint32_t flags; - void (*fn)(TCGv_env, 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); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - - fn = (a->fcond & 1 ? gen_helper_vfcmp_s_s : gen_helper_vfcmp_c_s); - flags = get_fcmp_flags(a->fcond >> 1); - fn(cpu_env, vd, vj, vk, tcg_constant_i32(flags)); - - return true; -} - -static bool trans_vfcmp_cond_d(DisasContext *ctx, arg_vvv_fcond *a) -{ - uint32_t flags; - void (*fn)(TCGv_env, 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); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - - fn = (a->fcond & 1 ? gen_helper_vfcmp_s_d : gen_helper_vfcmp_c_d); - flags = get_fcmp_flags(a->fcond >> 1); - fn(cpu_env, vd, vj, vk, tcg_constant_i32(flags)); - - return true; -} - -static bool trans_vbitsel_v(DisasContext *ctx, arg_vvvv *a) -{ - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - - 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), - 16, ctx->vl/8); - return true; -} - -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 trans_vbitseli_b(DisasContext *ctx, arg_vv_i *a) -{ - static const GVecGen2i op = { - .fniv = gen_vbitseli, - .fnoi = gen_helper_vbitseli_b, - .vece = MO_8, - .load_dest = true - }; - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - - tcg_gen_gvec_2i(vec_full_offset(a->vd), vec_full_offset(a->vj), - 16, ctx->vl/8, a->imm, &op); - return true; -} - -#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; \ - } \ - \ - CHECK_SXE; \ - tcg_gen_or_i64(t1, al, ah); \ - tcg_gen_setcondi_i64(COND, t1, t1, 0); \ - tcg_gen_st8_tl(t1, cpu_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) - -static bool trans_vinsgr2vr_b(DisasContext *ctx, arg_vr_i *a) -{ - TCGv src = gpr_src(ctx, a->rj, EXT_NONE); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - tcg_gen_st8_i64(src, cpu_env, - offsetof(CPULoongArchState, fpr[a->vd].vreg.B(a->imm))); - return true; -} - -static bool trans_vinsgr2vr_h(DisasContext *ctx, arg_vr_i *a) -{ - TCGv src = gpr_src(ctx, a->rj, EXT_NONE); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - tcg_gen_st16_i64(src, cpu_env, - offsetof(CPULoongArchState, fpr[a->vd].vreg.H(a->imm))); - return true; -} - -static bool trans_vinsgr2vr_w(DisasContext *ctx, arg_vr_i *a) -{ - TCGv src = gpr_src(ctx, a->rj, EXT_NONE); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - tcg_gen_st32_i64(src, cpu_env, - offsetof(CPULoongArchState, fpr[a->vd].vreg.W(a->imm))); - return true; -} - -static bool trans_vinsgr2vr_d(DisasContext *ctx, arg_vr_i *a) -{ - TCGv src = gpr_src(ctx, a->rj, EXT_NONE); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - tcg_gen_st_i64(src, cpu_env, - offsetof(CPULoongArchState, fpr[a->vd].vreg.D(a->imm))); - return true; -} - -static bool trans_vpickve2gr_b(DisasContext *ctx, arg_rv_i *a) -{ - TCGv dst = gpr_dst(ctx, a->rd, EXT_NONE); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - tcg_gen_ld8s_i64(dst, cpu_env, - offsetof(CPULoongArchState, fpr[a->vj].vreg.B(a->imm))); - return true; -} - -static bool trans_vpickve2gr_h(DisasContext *ctx, arg_rv_i *a) -{ - TCGv dst = gpr_dst(ctx, a->rd, EXT_NONE); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - tcg_gen_ld16s_i64(dst, cpu_env, - offsetof(CPULoongArchState, fpr[a->vj].vreg.H(a->imm))); - return true; -} - -static bool trans_vpickve2gr_w(DisasContext *ctx, arg_rv_i *a) -{ - TCGv dst = gpr_dst(ctx, a->rd, EXT_NONE); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - tcg_gen_ld32s_i64(dst, cpu_env, - offsetof(CPULoongArchState, fpr[a->vj].vreg.W(a->imm))); - return true; -} - -static bool trans_vpickve2gr_d(DisasContext *ctx, arg_rv_i *a) -{ - TCGv dst = gpr_dst(ctx, a->rd, EXT_NONE); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - tcg_gen_ld_i64(dst, cpu_env, - offsetof(CPULoongArchState, fpr[a->vj].vreg.D(a->imm))); - return true; -} - -static bool trans_vpickve2gr_bu(DisasContext *ctx, arg_rv_i *a) -{ - TCGv dst = gpr_dst(ctx, a->rd, EXT_NONE); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - tcg_gen_ld8u_i64(dst, cpu_env, - offsetof(CPULoongArchState, fpr[a->vj].vreg.B(a->imm))); - return true; -} - -static bool trans_vpickve2gr_hu(DisasContext *ctx, arg_rv_i *a) -{ - TCGv dst = gpr_dst(ctx, a->rd, EXT_NONE); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - tcg_gen_ld16u_i64(dst, cpu_env, - offsetof(CPULoongArchState, fpr[a->vj].vreg.H(a->imm))); - return true; -} - -static bool trans_vpickve2gr_wu(DisasContext *ctx, arg_rv_i *a) -{ - TCGv dst = gpr_dst(ctx, a->rd, EXT_NONE); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - tcg_gen_ld32u_i64(dst, cpu_env, - offsetof(CPULoongArchState, fpr[a->vj].vreg.W(a->imm))); - return true; -} - -static bool trans_vpickve2gr_du(DisasContext *ctx, arg_rv_i *a) -{ - TCGv dst = gpr_dst(ctx, a->rd, EXT_NONE); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - tcg_gen_ld_i64(dst, cpu_env, - offsetof(CPULoongArchState, fpr[a->vj].vreg.D(a->imm))); - return true; -} - -static bool gvec_dup(DisasContext *ctx, arg_vr *a, MemOp mop) -{ - TCGv src = gpr_src(ctx, a->rj, EXT_NONE); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - - tcg_gen_gvec_dup_i64(mop, vec_full_offset(a->vd), - 16, ctx->vl/8, src); - return true; -} - -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) - -static bool trans_vreplvei_b(DisasContext *ctx, arg_vv_i *a) -{ - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - 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; - } - - CHECK_SXE; - 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; - } - - CHECK_SXE; - 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; - } - - CHECK_SXE; - 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(DisasContext *ctx, arg_vvr *a, int vece, int bit, - void (*func)(TCGv_i64, TCGv_ptr, tcg_target_long)) -{ - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_ptr t1 = tcg_temp_new_ptr(); - TCGv_i64 t2 = tcg_temp_new_i64(); - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - - 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, cpu_env); - func(t2, t1, vec_full_offset(a->vj)); - tcg_gen_gvec_dup_i64(vece, vec_full_offset(a->vd), 16, ctx->vl/8, t2); - - return true; -} - -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) - -static bool trans_vbsll_v(DisasContext *ctx, arg_vv_i *a) -{ - int ofs; - TCGv_i64 desthigh, destlow, high, low; - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - - desthigh = tcg_temp_new_i64(); - destlow = tcg_temp_new_i64(); - high = tcg_temp_new_i64(); - low = tcg_temp_new_i64(); - - get_vreg64(low, a->vj, 0); - - ofs = ((a->imm) & 0xf) * 8; - if (ofs < 64) { - get_vreg64(high, a->vj, 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, 1); - set_vreg64(destlow, a->vd, 0); - - return true; -} - -static bool trans_vbsrl_v(DisasContext *ctx, arg_vv_i *a) -{ - TCGv_i64 desthigh, destlow, high, low; - int ofs; - - if (!avail_LSX(ctx)) { - return false; - } - - CHECK_SXE; - - desthigh = tcg_temp_new_i64(); - destlow = tcg_temp_new_i64(); - high = tcg_temp_new_i64(); - low = tcg_temp_new_i64(); - - get_vreg64(high, a->vj, 1); - - ofs = ((a->imm) & 0xf) * 8; - if (ofs < 64) { - get_vreg64(low, a->vj, 0); - 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, 1); - set_vreg64(destlow, a->vd, 0); - - return true; -} - -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(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(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(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(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(vpermi_w, LSX, gen_vv_i, gen_helper_vpermi_w) - -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) - -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; - } - - CHECK_SXE; - - 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; - } - - CHECK_SXE; - - 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; - } - - CHECK_SXE; - - 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; - } - - CHECK_SXE; - - 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; -} - -#define VLDREPL(NAME, MO) \ -static bool trans_## NAME (DisasContext *ctx, arg_vr_i *a) \ -{ \ - TCGv addr; \ - TCGv_i64 val; \ - \ - if (!avail_LSX(ctx)) { \ - return false; \ - } \ - \ - CHECK_SXE; \ - \ - 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, MO); \ - tcg_gen_gvec_dup_i64(MO, vec_full_offset(a->vd), 16, ctx->vl/8, val); \ - \ - return true; \ -} - -VLDREPL(vldrepl_b, MO_8) -VLDREPL(vldrepl_h, MO_16) -VLDREPL(vldrepl_w, MO_32) -VLDREPL(vldrepl_d, MO_64) - -#define VSTELM(NAME, MO, E) \ -static bool trans_## NAME (DisasContext *ctx, arg_vr_ii *a) \ -{ \ - TCGv addr; \ - TCGv_i64 val; \ - \ - if (!avail_LSX(ctx)) { \ - return false; \ - } \ - \ - CHECK_SXE; \ - \ - 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, cpu_env, \ - offsetof(CPULoongArchState, fpr[a->vd].vreg.E(a->imm2))); \ - tcg_gen_qemu_st_i64(val, addr, ctx->mem_idx, MO); \ - \ - return true; \ -} - -VSTELM(vstelm_b, MO_8, B) -VSTELM(vstelm_h, MO_16, H) -VSTELM(vstelm_w, MO_32, W) -VSTELM(vstelm_d, MO_64, D) diff --git a/target/loongarch/insn_trans/trans_privileged.c.inc b/target/loongarch/insn_trans/trans_privileged.c.inc deleted file mode 100644 index 4cb701b4b53..00000000000 --- a/target/loongarch/insn_trans/trans_privileged.c.inc +++ /dev/null @@ -1,498 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - * - * LoongArch translation routines for the privileged instructions. - */ - -#include "cpu-csr.h" - -#ifdef CONFIG_USER_ONLY - -#define GEN_FALSE_TRANS(name) \ -static bool trans_##name(DisasContext *ctx, arg_##name * a) \ -{ \ - return false; \ -} - -GEN_FALSE_TRANS(csrrd) -GEN_FALSE_TRANS(csrwr) -GEN_FALSE_TRANS(csrxchg) -GEN_FALSE_TRANS(iocsrrd_b) -GEN_FALSE_TRANS(iocsrrd_h) -GEN_FALSE_TRANS(iocsrrd_w) -GEN_FALSE_TRANS(iocsrrd_d) -GEN_FALSE_TRANS(iocsrwr_b) -GEN_FALSE_TRANS(iocsrwr_h) -GEN_FALSE_TRANS(iocsrwr_w) -GEN_FALSE_TRANS(iocsrwr_d) -GEN_FALSE_TRANS(tlbsrch) -GEN_FALSE_TRANS(tlbrd) -GEN_FALSE_TRANS(tlbwr) -GEN_FALSE_TRANS(tlbfill) -GEN_FALSE_TRANS(tlbclr) -GEN_FALSE_TRANS(tlbflush) -GEN_FALSE_TRANS(invtlb) -GEN_FALSE_TRANS(cacop) -GEN_FALSE_TRANS(ldpte) -GEN_FALSE_TRANS(lddir) -GEN_FALSE_TRANS(ertn) -GEN_FALSE_TRANS(dbcl) -GEN_FALSE_TRANS(idle) - -#else - -typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env); -typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src); - -typedef struct { - int offset; - int flags; - GenCSRRead readfn; - GenCSRWrite writefn; -} CSRInfo; - -enum { - CSRFL_READONLY = (1 << 0), - CSRFL_EXITTB = (1 << 1), - CSRFL_IO = (1 << 2), -}; - -#define CSR_OFF_FUNCS(NAME, FL, RD, WR) \ - [LOONGARCH_CSR_##NAME] = { \ - .offset = offsetof(CPULoongArchState, CSR_##NAME), \ - .flags = FL, .readfn = RD, .writefn = WR \ - } - -#define CSR_OFF_ARRAY(NAME, N) \ - [LOONGARCH_CSR_##NAME(N)] = { \ - .offset = offsetof(CPULoongArchState, CSR_##NAME[N]), \ - .flags = 0, .readfn = NULL, .writefn = NULL \ - } - -#define CSR_OFF_FLAGS(NAME, FL) \ - CSR_OFF_FUNCS(NAME, FL, NULL, NULL) - -#define CSR_OFF(NAME) \ - CSR_OFF_FLAGS(NAME, 0) - -static const CSRInfo csr_info[] = { - CSR_OFF_FLAGS(CRMD, CSRFL_EXITTB), - CSR_OFF(PRMD), - CSR_OFF_FLAGS(EUEN, CSRFL_EXITTB), - CSR_OFF_FLAGS(MISC, CSRFL_READONLY), - CSR_OFF(ECFG), - CSR_OFF_FUNCS(ESTAT, CSRFL_EXITTB, NULL, gen_helper_csrwr_estat), - CSR_OFF(ERA), - CSR_OFF(BADV), - CSR_OFF_FLAGS(BADI, CSRFL_READONLY), - CSR_OFF(EENTRY), - CSR_OFF(TLBIDX), - CSR_OFF(TLBEHI), - CSR_OFF(TLBELO0), - CSR_OFF(TLBELO1), - CSR_OFF_FUNCS(ASID, CSRFL_EXITTB, NULL, gen_helper_csrwr_asid), - CSR_OFF(PGDL), - CSR_OFF(PGDH), - CSR_OFF_FUNCS(PGD, CSRFL_READONLY, gen_helper_csrrd_pgd, NULL), - CSR_OFF(PWCL), - CSR_OFF(PWCH), - CSR_OFF(STLBPS), - CSR_OFF(RVACFG), - 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), - CSR_OFF_ARRAY(SAVE, 0), - CSR_OFF_ARRAY(SAVE, 1), - CSR_OFF_ARRAY(SAVE, 2), - CSR_OFF_ARRAY(SAVE, 3), - CSR_OFF_ARRAY(SAVE, 4), - CSR_OFF_ARRAY(SAVE, 5), - CSR_OFF_ARRAY(SAVE, 6), - CSR_OFF_ARRAY(SAVE, 7), - CSR_OFF_ARRAY(SAVE, 8), - CSR_OFF_ARRAY(SAVE, 9), - CSR_OFF_ARRAY(SAVE, 10), - CSR_OFF_ARRAY(SAVE, 11), - CSR_OFF_ARRAY(SAVE, 12), - CSR_OFF_ARRAY(SAVE, 13), - CSR_OFF_ARRAY(SAVE, 14), - CSR_OFF_ARRAY(SAVE, 15), - CSR_OFF(TID), - CSR_OFF_FUNCS(TCFG, CSRFL_IO, NULL, gen_helper_csrwr_tcfg), - CSR_OFF_FUNCS(TVAL, CSRFL_READONLY | CSRFL_IO, gen_helper_csrrd_tval, NULL), - CSR_OFF(CNTC), - CSR_OFF_FUNCS(TICLR, CSRFL_IO, NULL, gen_helper_csrwr_ticlr), - CSR_OFF(LLBCTL), - CSR_OFF(IMPCTL1), - CSR_OFF(IMPCTL2), - CSR_OFF(TLBRENTRY), - CSR_OFF(TLBRBADV), - CSR_OFF(TLBRERA), - CSR_OFF(TLBRSAVE), - CSR_OFF(TLBRELO0), - CSR_OFF(TLBRELO1), - CSR_OFF(TLBREHI), - CSR_OFF(TLBRPRMD), - CSR_OFF(MERRCTL), - CSR_OFF(MERRINFO1), - CSR_OFF(MERRINFO2), - CSR_OFF(MERRENTRY), - CSR_OFF(MERRERA), - CSR_OFF(MERRSAVE), - CSR_OFF(CTAG), - CSR_OFF_ARRAY(DMW, 0), - CSR_OFF_ARRAY(DMW, 1), - CSR_OFF_ARRAY(DMW, 2), - CSR_OFF_ARRAY(DMW, 3), - CSR_OFF(DBG), - CSR_OFF(DERA), - CSR_OFF(DSAVE), -}; - -static bool check_plv(DisasContext *ctx) -{ - if (ctx->plv == MMU_PLV_USER) { - generate_exception(ctx, EXCCODE_IPE); - return true; - } - return false; -} - -static const CSRInfo *get_csr(unsigned csr_num) -{ - const CSRInfo *csr; - - if (csr_num >= ARRAY_SIZE(csr_info)) { - return NULL; - } - csr = &csr_info[csr_num]; - if (csr->offset == 0) { - return NULL; - } - return csr; -} - -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) && 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; - } - return true; -} - -static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a) -{ - TCGv dest; - const CSRInfo *csr; - - if (check_plv(ctx)) { - return false; - } - csr = get_csr(a->csr); - if (csr == NULL) { - /* CSR is undefined: read as 0. */ - dest = tcg_constant_tl(0); - } else { - check_csr_flags(ctx, csr, false); - dest = gpr_dst(ctx, a->rd, EXT_NONE); - if (csr->readfn) { - csr->readfn(dest, cpu_env); - } else { - tcg_gen_ld_tl(dest, cpu_env, csr->offset); - } - } - gen_set_gpr(a->rd, dest, EXT_NONE); - return true; -} - -static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a) -{ - TCGv dest, src1; - const CSRInfo *csr; - - if (check_plv(ctx)) { - return false; - } - csr = get_csr(a->csr); - if (csr == NULL) { - /* CSR is undefined: write ignored, read old_value as 0. */ - gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE); - return true; - } - if (!check_csr_flags(ctx, csr, true)) { - /* CSR is readonly: trap. */ - return false; - } - 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); - } else { - dest = tcg_temp_new(); - tcg_gen_ld_tl(dest, cpu_env, csr->offset); - tcg_gen_st_tl(src1, cpu_env, csr->offset); - } - gen_set_gpr(a->rd, dest, EXT_NONE); - return true; -} - -static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a) -{ - TCGv src1, mask, oldv, newv, temp; - const CSRInfo *csr; - - if (check_plv(ctx)) { - return false; - } - csr = get_csr(a->csr); - if (csr == NULL) { - /* CSR is undefined: write ignored, read old_value as 0. */ - gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE); - return true; - } - - if (!check_csr_flags(ctx, csr, true)) { - /* CSR is readonly: trap. */ - return false; - } - - /* So far only readonly csrs have readfn. */ - assert(csr->readfn == NULL); - - src1 = gpr_src(ctx, a->rd, EXT_NONE); - mask = gpr_src(ctx, a->rj, EXT_NONE); - oldv = tcg_temp_new(); - newv = tcg_temp_new(); - temp = tcg_temp_new(); - - tcg_gen_ld_tl(oldv, cpu_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); - } else { - tcg_gen_st_tl(newv, cpu_env, csr->offset); - } - gen_set_gpr(a->rd, oldv, EXT_NONE); - return true; -} - -static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a, - void (*func)(TCGv, TCGv_ptr, TCGv)) -{ - TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); - TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - - if (check_plv(ctx)) { - return false; - } - func(dest, cpu_env, src1); - return true; -} - -static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a, - void (*func)(TCGv_ptr, TCGv, TCGv)) -{ - TCGv val = gpr_src(ctx, a->rd, EXT_NONE); - TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); - - if (check_plv(ctx)) { - return false; - } - func(cpu_env, addr, val); - return true; -} - -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) { - tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4); - ctx->base.is_jmp = DISAS_EXIT; - } -} - -static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a) -{ - if (check_plv(ctx)) { - return false; - } - gen_helper_tlbsrch(cpu_env); - return true; -} - -static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a) -{ - if (check_plv(ctx)) { - return false; - } - gen_helper_tlbrd(cpu_env); - return true; -} - -static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a) -{ - if (check_plv(ctx)) { - return false; - } - gen_helper_tlbwr(cpu_env); - check_mmu_idx(ctx); - return true; -} - -static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a) -{ - if (check_plv(ctx)) { - return false; - } - gen_helper_tlbfill(cpu_env); - check_mmu_idx(ctx); - return true; -} - -static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a) -{ - if (check_plv(ctx)) { - return false; - } - gen_helper_tlbclr(cpu_env); - check_mmu_idx(ctx); - return true; -} - -static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a) -{ - if (check_plv(ctx)) { - return false; - } - gen_helper_tlbflush(cpu_env); - check_mmu_idx(ctx); - return true; -} - -static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a) -{ - TCGv rj = gpr_src(ctx, a->rj, EXT_NONE); - TCGv rk = gpr_src(ctx, a->rk, EXT_NONE); - - if (check_plv(ctx)) { - return false; - } - - switch (a->imm) { - case 0: - case 1: - gen_helper_invtlb_all(cpu_env); - break; - case 2: - gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(1)); - break; - case 3: - gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(0)); - break; - case 4: - gen_helper_invtlb_all_asid(cpu_env, rj); - break; - case 5: - gen_helper_invtlb_page_asid(cpu_env, rj, rk); - break; - case 6: - gen_helper_invtlb_page_asid_or_g(cpu_env, rj, rk); - break; - default: - return false; - } - ctx->base.is_jmp = DISAS_STOP; - return true; -} - -static bool trans_cacop(DisasContext *ctx, arg_cacop *a) -{ - /* Treat the cacop as a nop */ - if (check_plv(ctx)) { - return false; - } - return true; -} - -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); - return true; -} - -static bool trans_lddir(DisasContext *ctx, arg_lddir *a) -{ - TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx); - 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); - return true; -} - -static bool trans_ertn(DisasContext *ctx, arg_ertn *a) -{ - if (check_plv(ctx)) { - return false; - } - gen_helper_ertn(cpu_env); - ctx->base.is_jmp = DISAS_EXIT; - return true; -} - -static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a) -{ - if (check_plv(ctx)) { - return false; - } - generate_exception(ctx, EXCCODE_DBP); - return true; -} - -static bool trans_idle(DisasContext *ctx, arg_idle *a) -{ - if (check_plv(ctx)) { - return false; - } - - tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4); - gen_helper_idle(cpu_env); - ctx->base.is_jmp = DISAS_NORETURN; - return true; -} -#endif diff --git a/target/loongarch/insns.decode b/target/loongarch/insns.decode index c9c3bc2c733..62f58cc541d 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 @@ -69,6 +70,7 @@ @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 @@ -228,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 @@ -550,6 +553,10 @@ dbcl 0000 00000010 10101 ............... @i15 @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 @@ -1296,3 +1303,781 @@ 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 7b0f29c9420..a7384b0d315 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -16,36 +16,9 @@ #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 - -#if HOST_BIG_ENDIAN -#define B(x) B[15 - (x)] -#define H(x) H[7 - (x)] -#define W(x) W[3 - (x)] -#define D(x) D[1 - (x)] -#define UB(x) UB[15 - (x)] -#define UH(x) UH[7 - (x)] -#define UW(x) UW[3 - (x)] -#define UD(x) UD[1 -(x)] -#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 - void loongarch_translate_init(void); - -void loongarch_cpu_dump_state(CPUState *cpu, FILE *f, int flags); +void loongarch_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); void G_NORETURN do_raise_exception(CPULoongArchState *env, uint32_t exception, @@ -53,10 +26,25 @@ 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, +}; + +bool check_ps(CPULoongArchState *ent, uint8_t ps); + extern const VMStateDescription vmstate_loongarch_cpu; void loongarch_cpu_set_irq(void *opaque, int irq, int level); @@ -66,12 +54,13 @@ 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_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); - +int get_physical_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx, int is_debug); +void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, + uint64_t *dir_width, target_ulong level); hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); + #endif /* !CONFIG_USER_ONLY */ uint64_t read_fcc(CPULoongArchState *env); @@ -80,5 +69,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 6cd01d5f094..00000000000 --- a/target/loongarch/iocsr_helper.c +++ /dev/null @@ -1,68 +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 "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/kvm/kvm.c b/target/loongarch/kvm/kvm.c new file mode 100644 index 00000000000..e5ea2dba9da --- /dev/null +++ b/target/loongarch/kvm/kvm.c @@ -0,0 +1,1410 @@ +/* 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 "asm-loongarch/kvm_para.h" +#include "qapi/error.h" +#include "qemu/timer.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "system/system.h" +#include "system/kvm.h" +#include "system/kvm_int.h" +#include "hw/pci/pci.h" +#include "exec/memattrs.h" +#include "system/address-spaces.h" +#include "hw/boards.h" +#include "hw/irq.h" +#include "hw/loongarch/virt.h" +#include "qemu/log.h" +#include "hw/loader.h" +#include "system/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_set_pv_features(CPUState *cs) +{ + CPULoongArchState *env = cpu_env(cs); + int err; + uint64_t val; + struct kvm_device_attr attr = { + .group = KVM_LOONGARCH_VCPU_CPUCFG, + .attr = CPUCFG_KVM_FEATURE, + .addr = (uint64_t)&val, + }; + + err = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, attr); + if (err) { + return 0; + } + + val = env->pv_features; + err = kvm_vcpu_ioctl(cs, KVM_SET_DEVICE_ATTR, attr); + if (err) { + error_report("Fail to set pv feature "TARGET_FMT_lx " with error %s", + val, strerror(errno)); + 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); + int ret = 0; + uint64_t unused = 0; + + env->mp_state = KVM_MP_STATE_RUNNABLE; + ret = kvm_set_one_reg(cs, KVM_REG_LOONGARCH_VCPU_RESET, &unused); + if (ret) { + error_report("Failed to set KVM_REG_LOONGARCH_VCPU_RESET: %s", + strerror(errno)); + exit(EXIT_FAILURE); + } +} + +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; + static int once; + + 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 (!once) { + ret = kvm_set_pv_features(cs); + if (ret) { + return ret; + } + once = 1; + } + + 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; + uint64_t val; + + switch (feature) { + case LOONGARCH_FEATURE_LSX: + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; + attr.attr = KVM_LOONGARCH_VM_FEAT_LSX; + ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + if (ret == 0) { + return true; + } + + /* Fallback to old kernel detect interface */ + val = 0; + attr.group = KVM_LOONGARCH_VCPU_CPUCFG; + /* Cpucfg2 */ + attr.attr = 2; + attr.addr = (uint64_t)&val; + ret = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, &attr); + if (!ret) { + ret = kvm_vcpu_ioctl(cs, KVM_GET_DEVICE_ATTR, &attr); + if (ret) { + return false; + } + + ret = FIELD_EX32((uint32_t)val, CPUCFG2, LSX); + return (ret != 0); + } + return false; + + case LOONGARCH_FEATURE_LASX: + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; + attr.attr = KVM_LOONGARCH_VM_FEAT_LASX; + ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + if (ret == 0) { + return true; + } + + /* Fallback to old kernel detect interface */ + val = 0; + attr.group = KVM_LOONGARCH_VCPU_CPUCFG; + /* Cpucfg2 */ + attr.attr = 2; + attr.addr = (uint64_t)&val; + ret = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, &attr); + if (!ret) { + ret = kvm_vcpu_ioctl(cs, KVM_GET_DEVICE_ATTR, &attr); + if (ret) { + return false; + } + + ret = FIELD_EX32((uint32_t)val, CPUCFG2, LASX); + return (ret != 0); + } + return false; + + 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); + + case LOONGARCH_FEATURE_PV_IPI: + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; + attr.attr = KVM_LOONGARCH_VM_FEAT_PV_IPI; + ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + return (ret == 0); + + case LOONGARCH_FEATURE_STEALTIME: + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; + attr.attr = KVM_LOONGARCH_VM_FEAT_PV_STEALTIME; + ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + return (ret == 0); + + default: + return false; + } + + return false; +} + +static int kvm_cpu_check_lsx(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_LSX); + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LSX, 0); + if (cpu->lsx == ON_OFF_AUTO_ON) { + if (kvm_supported) { + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LSX, 1); + } else { + error_setg(errp, "'lsx' feature not supported by KVM on this host"); + return -ENOTSUP; + } + } else if ((cpu->lsx == ON_OFF_AUTO_AUTO) && kvm_supported) { + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LSX, 1); + } + + return 0; +} + +static int kvm_cpu_check_lasx(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_LASX); + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LASX, 0); + if (cpu->lasx == ON_OFF_AUTO_ON) { + if (kvm_supported) { + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LASX, 1); + } else { + error_setg(errp, "'lasx' feature not supported by KVM on host"); + return -ENOTSUP; + } + } else if ((cpu->lasx == ON_OFF_AUTO_AUTO) && kvm_supported) { + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LASX, 1); + } + + return 0; +} + +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; +} + +static int kvm_cpu_check_pv_features(CPUState *cs, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = cpu_env(cs); + bool kvm_supported; + + kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_PV_IPI); + if (cpu->kvm_pv_ipi == ON_OFF_AUTO_ON) { + if (!kvm_supported) { + error_setg(errp, "'pv_ipi' feature not supported by KVM host"); + return -ENOTSUP; + } + } else if (cpu->kvm_pv_ipi != ON_OFF_AUTO_AUTO) { + kvm_supported = false; + } + + if (kvm_supported) { + env->pv_features |= BIT(KVM_FEATURE_IPI); + } + + kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_STEALTIME); + if (cpu->kvm_steal_time == ON_OFF_AUTO_ON) { + if (!kvm_supported) { + error_setg(errp, "'kvm stealtime' feature not supported by KVM host"); + return -ENOTSUP; + } + } else if (cpu->kvm_steal_time != ON_OFF_AUTO_AUTO) { + kvm_supported = false; + } + + if (kvm_supported) { + env->pv_features |= BIT(KVM_FEATURE_STEAL_TIME); + } + + if (object_dynamic_cast(OBJECT(ms), TYPE_LOONGARCH_VIRT_MACHINE)) { + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); + + if (virt_is_veiointc_enabled(lvms)) { + env->pv_features |= BIT(KVM_FEATURE_VIRT_EXTIOI); + } + } + return 0; +} + +int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + return 0; +} + +int kvm_arch_init_vcpu(CPUState *cs) +{ + uint64_t val; + int ret; + Error *local_err = NULL; + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + + cpu->vmsentry = 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_lsx(cs, &local_err); + if (ret < 0) { + error_report_err(local_err); + return ret; + } + + ret = kvm_cpu_check_lasx(cs, &local_err); + if (ret < 0) { + error_report_err(local_err); + return ret; + } + + ret = kvm_cpu_check_lbt(cs, &local_err); + if (ret < 0) { + error_report_err(local_err); + return ret; + } + + ret = kvm_cpu_check_pmu(cs, &local_err); + if (ret < 0) { + error_report_err(local_err); + return ret; + } + + ret = kvm_cpu_check_pv_features(cs, &local_err); + if (ret < 0) { + error_report_err(local_err); + return ret; + } + + return 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; +} + +static bool kvm_pv_ipi_get(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->kvm_pv_ipi != ON_OFF_AUTO_OFF; +} + +static void kvm_pv_ipi_set(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->kvm_pv_ipi = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; +} + +static bool kvm_steal_time_get(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->kvm_steal_time != ON_OFF_AUTO_OFF; +} + +static void kvm_steal_time_set(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->kvm_steal_time = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; +} + +void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu) +{ + cpu->lbt = ON_OFF_AUTO_AUTO; + object_property_add_bool(OBJECT(cpu), "lbt", loongarch_get_lbt, + loongarch_set_lbt); + object_property_set_description(OBJECT(cpu), "lbt", + "Set off to disable Binary Tranlation."); + + cpu->pmu = ON_OFF_AUTO_AUTO; + object_property_add_bool(OBJECT(cpu), "pmu", loongarch_get_pmu, + loongarch_set_pmu); + object_property_set_description(OBJECT(cpu), "pmu", + "Set off to disable performance monitor unit."); + + cpu->kvm_pv_ipi = ON_OFF_AUTO_AUTO; + object_property_add_bool(OBJECT(cpu), "kvm-pv-ipi", kvm_pv_ipi_get, + kvm_pv_ipi_set); + object_property_set_description(OBJECT(cpu), "kvm-pv-ipi", + "Set off to disable KVM paravirt IPI."); + + cpu->kvm_steal_time = ON_OFF_AUTO_AUTO; + object_property_add_bool(OBJECT(cpu), "kvm-steal-time", kvm_steal_time_get, + kvm_steal_time_set); + object_property_set_description(OBJECT(cpu), "kvm-steal-time", + "Set off to disable KVM steal time."); +} + +int kvm_arch_destroy_vcpu(CPUState *cs) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + + qemu_del_vm_change_state_handler(cpu->vmsentry); + 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) +{ +} + +void kvm_loongarch_init_irq_routing(void) +{ + int i; + + kvm_async_interrupts_allowed = true; + kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled(); + if (kvm_has_gsi_routing()) { + for (i = 0; i < KVM_IRQCHIP_NUM_PINS; ++i) { + kvm_irqchip_add_irq_route(kvm_state, i, 0, i); + } + + kvm_gsi_routing_allowed = true; + kvm_irqchip_commit_routes(kvm_state); + } +} + +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("kernel_irqchip=split is not supported on LoongArch"); + exit(1); + } + + return kvm_check_extension(s, KVM_CAP_DEVICE_CTRL); +} + +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 00000000000..1051a341ec2 --- /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 00000000000..2266de6ca97 --- /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 index 6c259578814..f5f1cd0009d 100644 --- a/target/loongarch/loongarch-qmp-cmds.c +++ b/target/loongarch/loongarch-qmp-cmds.c @@ -7,8 +7,12 @@ */ #include "qemu/osdep.h" -#include "qapi/qapi-commands-machine-target.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" #include "cpu.h" +#include "qobject/qdict.h" +#include "qapi/qobject-input-visitor.h" +#include "qom/qom-qobject.h" static void loongarch_cpu_add_definition(gpointer data, gpointer user_data) { @@ -17,8 +21,7 @@ static void loongarch_cpu_add_definition(gpointer data, gpointer 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->name = cpu_model_from_type(typename); info->q_typename = g_strdup(typename); QAPI_LIST_PREPEND(*cpu_list, info); @@ -35,3 +38,79 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) return cpu_list; } + +static const char *cpu_model_advertised_features[] = { + "lsx", "lasx", "lbt", "pmu", "kvm-pv-ipi", "kvm-steal-time", 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/lsx_helper.c b/target/loongarch/lsx_helper.c deleted file mode 100644 index 9571f0aef0d..00000000000 --- a/target/loongarch/lsx_helper.c +++ /dev/null @@ -1,3004 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * QEMU LoongArch LSX 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" - -#define DO_ADD(a, b) (a + b) -#define DO_SUB(a, b) (a - b) - -#define DO_ODD_EVEN(NAME, BIT, E1, E2, DO_OP) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - typedef __typeof(Vd->E1(0)) TD; \ - \ - for (i = 0; i < LSX_LEN/BIT; 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)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t vk) -{ - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - VReg *Vk = &(env->fpr[vk].vreg); - - Vd->Q(0) = int128_add(int128_makes64(Vj->D(1)), int128_makes64(Vk->D(0))); -} - -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)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t vk) -{ - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - VReg *Vk = &(env->fpr[vk].vreg); - - Vd->Q(0) = int128_sub(int128_makes64(Vj->D(1)), int128_makes64(Vk->D(0))); -} - -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)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t vk) -{ - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - VReg *Vk = &(env->fpr[vk].vreg); - - Vd->Q(0) = int128_add(int128_make64((uint64_t)Vj->D(1)), - int128_make64((uint64_t)Vk->D(0))); -} - -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)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t vk) -{ - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - VReg *Vk = &(env->fpr[vk].vreg); - - Vd->Q(0) = int128_sub(int128_make64((uint64_t)Vj->D(1)), - int128_make64((uint64_t)Vk->D(0))); -} - -#define DO_EVEN(NAME, BIT, E1, E2, DO_OP) \ -void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t v) \ -{ \ - int i; \ - VReg *Vd = (VReg *)vd; \ - VReg *Vj = (VReg *)vj; \ - VReg *Vk = (VReg *)vk; \ - typedef __typeof(Vd->E1(0)) TD; \ - for (i = 0; i < LSX_LEN/BIT; 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 v) \ -{ \ - int i; \ - VReg *Vd = (VReg *)vd; \ - VReg *Vj = (VReg *)vj; \ - VReg *Vk = (VReg *)vk; \ - typedef __typeof(Vd->E1(0)) TD; \ - for (i = 0; i < LSX_LEN/BIT; 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 v) -{ - VReg *Vd = (VReg *)vd; - VReg *Vj = (VReg *)vj; - VReg *Vk = (VReg *)vk; - - Vd->Q(0) = int128_add(int128_makes64(Vj->D(0)), int128_makes64(Vk->D(0))); -} - -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 v) -{ - VReg *Vd = (VReg *)vd; - VReg *Vj = (VReg *)vj; - VReg *Vk = (VReg *)vk; - - Vd->Q(0) = int128_add(int128_makes64(Vj->D(1)), int128_makes64(Vk->D(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 v) -{ - VReg *Vd = (VReg *)vd; - VReg *Vj = (VReg *)vj; - VReg *Vk = (VReg *)vk; - - Vd->Q(0) = int128_sub(int128_makes64(Vj->D(0)), int128_makes64(Vk->D(0))); -} - -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 v) -{ - VReg *Vd = (VReg *)vd; - VReg *Vj = (VReg *)vj; - VReg *Vk = (VReg *)vk; - - Vd->Q(0) = int128_sub(int128_makes64(Vj->D(1)), int128_makes64(Vk->D(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 v) -{ - VReg *Vd = (VReg *)vd; - VReg *Vj = (VReg *)vj; - VReg *Vk = (VReg *)vk; - - Vd->Q(0) = int128_add(int128_make64((uint64_t)Vj->D(0)), - int128_make64((uint64_t)Vk->D(0))); -} - -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 v) -{ - VReg *Vd = (VReg *)vd; - VReg *Vj = (VReg *)vj; - VReg *Vk = (VReg *)vk; - - Vd->Q(0) = int128_add(int128_make64((uint64_t)Vj->D(1)), - int128_make64((uint64_t)Vk->D(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 v) -{ - VReg *Vd = (VReg *)vd; - VReg *Vj = (VReg *)vj; - VReg *Vk = (VReg *)vk; - - Vd->Q(0) = int128_sub(int128_make64((uint64_t)Vj->D(0)), - int128_make64((uint64_t)Vk->D(0))); -} - -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 v) -{ - VReg *Vd = (VReg *)vd; - VReg *Vj = (VReg *)vj; - VReg *Vk = (VReg *)vk; - - Vd->Q(0) = int128_sub(int128_make64((uint64_t)Vj->D(1)), - int128_make64((uint64_t)Vk->D(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 v) \ -{ \ - 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; \ - for (i = 0; i < LSX_LEN/BIT; 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 v) \ -{ \ - 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; \ - for (i = 0; i < LSX_LEN/BIT; 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 v) -{ - VReg *Vd = (VReg *)vd; - VReg *Vj = (VReg *)vj; - VReg *Vk = (VReg *)vk; - - Vd->Q(0) = int128_add(int128_make64((uint64_t)Vj->D(0)), - int128_makes64(Vk->D(0))); -} - -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 v) -{ - VReg *Vd = (VReg *)vd; - VReg *Vj = (VReg *)vj; - VReg *Vk = (VReg *)vk; - - Vd->Q(0) = int128_add(int128_make64((uint64_t)Vj->D(1)), - int128_makes64(Vk->D(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_VAVG(a, b) ((a >> 1) + (b >> 1) + (a & b & 1)) -#define DO_VAVGR(a, b) ((a >> 1) + (b >> 1) + ((a | b) & 1)) - -#define DO_3OP(NAME, BIT, E, DO_OP) \ -void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t v) \ -{ \ - int i; \ - VReg *Vd = (VReg *)vd; \ - VReg *Vj = (VReg *)vj; \ - VReg *Vk = (VReg *)vk; \ - for (i = 0; i < LSX_LEN/BIT; 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) - -#define DO_VABSD(a, b) ((a > b) ? (a -b) : (b-a)) - -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_VABS(a) ((a < 0) ? (-a) : (a)) - -#define DO_VADDA(NAME, BIT, E, DO_OP) \ -void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t v) \ -{ \ - int i; \ - VReg *Vd = (VReg *)vd; \ - VReg *Vj = (VReg *)vj; \ - VReg *Vk = (VReg *)vk; \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - Vd->E(i) = DO_OP(Vj->E(i)) + DO_OP(Vk->E(i)); \ - } \ -} - -DO_VADDA(vadda_b, 8, B, DO_VABS) -DO_VADDA(vadda_h, 16, H, DO_VABS) -DO_VADDA(vadda_w, 32, W, DO_VABS) -DO_VADDA(vadda_d, 64, D, DO_VABS) - -#define DO_MIN(a, b) (a < b ? a : b) -#define DO_MAX(a, b) (a > b ? a : b) - -#define VMINMAXI(NAME, BIT, E, DO_OP) \ -void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t v) \ -{ \ - int i; \ - VReg *Vd = (VReg *)vd; \ - VReg *Vj = (VReg *)vj; \ - typedef __typeof(Vd->E(0)) TD; \ - \ - for (i = 0; i < LSX_LEN/BIT; 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 v) \ -{ \ - int i; \ - VReg *Vd = (VReg *)vd; \ - VReg *Vj = (VReg *)vj; \ - VReg *Vk = (VReg *)vk; \ - typedef __typeof(Vd->E1(0)) T; \ - \ - for (i = 0; i < LSX_LEN/BIT; 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 v) -{ - uint64_t l, h1, h2; - VReg *Vd = (VReg *)vd; - VReg *Vj = (VReg *)vj; - VReg *Vk = (VReg *)vk; - - muls64(&l, &h1, Vj->D(0), Vk->D(0)); - muls64(&l, &h2, Vj->D(1), Vk->D(1)); - - Vd->D(0) = h1; - Vd->D(1) = h2; -} - -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 v) -{ - uint64_t l, h1, h2; - VReg *Vd = (VReg *)vd; - VReg *Vj = (VReg *)vj; - VReg *Vk = (VReg *)vk; - - mulu64(&l, &h1, Vj->D(0), Vk->D(0)); - mulu64(&l, &h2, Vj->D(1), Vk->D(1)); - - Vd->D(0) = h1; - Vd->D(1) = h2; -} - -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) - -#define DO_MUL(a, b) (a * b) - -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 DO_MADD(a, b, c) (a + b * c) -#define DO_MSUB(a, b, c) (a - b * c) - -#define VMADDSUB(NAME, BIT, E, DO_OP) \ -void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t v) \ -{ \ - int i; \ - VReg *Vd = (VReg *)vd; \ - VReg *Vj = (VReg *)vj; \ - VReg *Vk = (VReg *)vk; \ - for (i = 0; i < LSX_LEN/BIT; 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 v) \ -{ \ - int i; \ - VReg *Vd = (VReg *)vd; \ - VReg *Vj = (VReg *)vj; \ - VReg *Vk = (VReg *)vk; \ - typedef __typeof(Vd->E1(0)) TD; \ - \ - for (i = 0; i < LSX_LEN/BIT; 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 v) \ -{ \ - int i; \ - VReg *Vd = (VReg *)vd; \ - VReg *Vj = (VReg *)vj; \ - VReg *Vk = (VReg *)vk; \ - typedef __typeof(Vd->E1(0)) TD; \ - \ - for (i = 0; i < LSX_LEN/BIT; 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 v) \ -{ \ - 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; \ - \ - for (i = 0; i < LSX_LEN/BIT; 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 v) \ -{ \ - 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; \ - \ - for (i = 0; i < LSX_LEN/BIT; 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 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 VDIV(NAME, BIT, E, DO_OP) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - for (i = 0; i < LSX_LEN/BIT; 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 v) \ -{ \ - int i; \ - VReg *Vd = (VReg *)vd; \ - VReg *Vj = (VReg *)vj; \ - typedef __typeof(Vd->E(0)) TD; \ - \ - for (i = 0; i < LSX_LEN/BIT; 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 v) \ -{ \ - int i; \ - VReg *Vd = (VReg *)vd; \ - VReg *Vj = (VReg *)vj; \ - typedef __typeof(Vd->E(0)) TD; \ - \ - for (i = 0; i < LSX_LEN/BIT; 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)(CPULoongArchState *env, uint32_t vd, uint32_t vj) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - Vd->E1(i) = Vj->E2(i + LSX_LEN/BIT); \ - } \ -} - -void HELPER(vexth_q_d)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - Vd->Q(0) = int128_makes64(Vj->D(1)); -} - -void HELPER(vexth_qu_du)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - Vd->Q(0) = int128_make64((uint64_t)Vj->D(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 DO_SIGNCOV(a, b) (a == 0 ? 0 : a < 0 ? -b : b) - -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)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - uint16_t temp = 0; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - temp = do_vmskltz_b(Vj->D(0)); - temp |= (do_vmskltz_b(Vj->D(1)) << 8); - Vd->D(0) = temp; - Vd->D(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)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - uint16_t temp = 0; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - temp = do_vmskltz_h(Vj->D(0)); - temp |= (do_vmskltz_h(Vj->D(1)) << 4); - Vd->D(0) = temp; - Vd->D(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)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - uint16_t temp = 0; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - temp = do_vmskltz_w(Vj->D(0)); - temp |= (do_vmskltz_w(Vj->D(1)) << 2); - Vd->D(0) = temp; - Vd->D(1) = 0; -} - -static uint64_t do_vmskltz_d(int64_t val) -{ - return (uint64_t)val >> 63; -} -void HELPER(vmskltz_d)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - uint16_t temp = 0; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - temp = do_vmskltz_d(Vj->D(0)); - temp |= (do_vmskltz_d(Vj->D(1)) << 1); - Vd->D(0) = temp; - Vd->D(1) = 0; -} - -void HELPER(vmskgez_b)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - uint16_t temp = 0; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - temp = do_vmskltz_b(Vj->D(0)); - temp |= (do_vmskltz_b(Vj->D(1)) << 8); - Vd->D(0) = (uint16_t)(~temp); - Vd->D(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)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - uint16_t temp = 0; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - temp = do_vmskez_b(Vj->D(0)); - temp |= (do_vmskez_b(Vj->D(1)) << 8); - Vd->D(0) = (uint16_t)(~temp); - Vd->D(1) = 0; -} - -void HELPER(vnori_b)(void *vd, void *vj, uint64_t imm, uint32_t v) -{ - int i; - VReg *Vd = (VReg *)vd; - VReg *Vj = (VReg *)vj; - - for (i = 0; i < LSX_LEN/8; i++) { - Vd->B(i) = ~(Vj->B(i) | (uint8_t)imm); - } -} - -#define VSLLWIL(NAME, BIT, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - typedef __typeof(temp.E1(0)) TD; \ - \ - temp.D(0) = 0; \ - temp.D(1) = 0; \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - temp.E1(i) = (TD)Vj->E2(i) << (imm % BIT); \ - } \ - *Vd = temp; \ -} - -void HELPER(vextl_q_d)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - Vd->Q(0) = int128_makes64(Vj->D(0)); -} - -void HELPER(vextl_qu_du)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - Vd->Q(0) = int128_make64(Vj->D(0)); -} - -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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; 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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; 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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; 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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; 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 R_SHIFT(a, b) (a >> b) - -#define VSRLN(NAME, BIT, T, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - Vd->E1(i) = R_SHIFT((T)Vj->E2(i),((T)Vk->E2(i)) % BIT); \ - } \ - Vd->D(1) = 0; \ -} - -VSRLN(vsrln_b_h, 16, uint16_t, B, H) -VSRLN(vsrln_h_w, 32, uint32_t, H, W) -VSRLN(vsrln_w_d, 64, uint64_t, W, D) - -#define VSRAN(NAME, BIT, T, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - Vd->E1(i) = R_SHIFT(Vj->E2(i), ((T)Vk->E2(i)) % BIT); \ - } \ - Vd->D(1) = 0; \ -} - -VSRAN(vsran_b_h, 16, uint16_t, B, H) -VSRAN(vsran_h_w, 32, uint32_t, H, W) -VSRAN(vsran_w_d, 64, uint64_t, W, D) - -#define VSRLNI(NAME, BIT, T, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i, max; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - temp.D(0) = 0; \ - temp.D(1) = 0; \ - max = LSX_LEN/BIT; \ - for (i = 0; i < max; i++) { \ - temp.E1(i) = R_SHIFT((T)Vj->E2(i), imm); \ - temp.E1(i + max) = R_SHIFT((T)Vd->E2(i), imm); \ - } \ - *Vd = temp; \ -} - -void HELPER(vsrlni_d_q)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t imm) -{ - VReg temp; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - temp.D(0) = 0; - temp.D(1) = 0; - temp.D(0) = int128_getlo(int128_urshift(Vj->Q(0), imm % 128)); - temp.D(1) = int128_getlo(int128_urshift(Vd->Q(0), imm % 128)); - *Vd = temp; -} - -VSRLNI(vsrlni_b_h, 16, uint16_t, B, H) -VSRLNI(vsrlni_h_w, 32, uint32_t, H, W) -VSRLNI(vsrlni_w_d, 64, uint64_t, W, D) - -#define VSRANI(NAME, BIT, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i, max; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - temp.D(0) = 0; \ - temp.D(1) = 0; \ - max = LSX_LEN/BIT; \ - for (i = 0; i < max; i++) { \ - temp.E1(i) = R_SHIFT(Vj->E2(i), imm); \ - temp.E1(i + max) = R_SHIFT(Vd->E2(i), imm); \ - } \ - *Vd = temp; \ -} - -void HELPER(vsrani_d_q)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t imm) -{ - VReg temp; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - temp.D(0) = 0; - temp.D(1) = 0; - temp.D(0) = int128_getlo(int128_rshift(Vj->Q(0), imm % 128)); - temp.D(1) = int128_getlo(int128_rshift(Vd->Q(0), 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, T, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - Vd->E1(i) = do_vsrlr_ ## E2(Vj->E2(i), ((T)Vk->E2(i))%BIT); \ - } \ - Vd->D(1) = 0; \ -} - -VSRLRN(vsrlrn_b_h, 16, uint16_t, B, H) -VSRLRN(vsrlrn_h_w, 32, uint32_t, H, W) -VSRLRN(vsrlrn_w_d, 64, uint64_t, W, D) - -#define VSRARN(NAME, BIT, T, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - Vd->E1(i) = do_vsrar_ ## E2(Vj->E2(i), ((T)Vk->E2(i))%BIT); \ - } \ - Vd->D(1) = 0; \ -} - -VSRARN(vsrarn_b_h, 16, uint8_t, B, H) -VSRARN(vsrarn_h_w, 32, uint16_t, H, W) -VSRARN(vsrarn_w_d, 64, uint32_t, W, D) - -#define VSRLRNI(NAME, BIT, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i, max; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - temp.D(0) = 0; \ - temp.D(1) = 0; \ - max = LSX_LEN/BIT; \ - for (i = 0; i < max; i++) { \ - temp.E1(i) = do_vsrlr_ ## E2(Vj->E2(i), imm); \ - temp.E1(i + max) = do_vsrlr_ ## E2(Vd->E2(i), imm); \ - } \ - *Vd = temp; \ -} - -void HELPER(vsrlrni_d_q)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t imm) -{ - VReg temp; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - Int128 r1, r2; - - if (imm == 0) { - temp.D(0) = int128_getlo(Vj->Q(0)); - temp.D(1) = int128_getlo(Vd->Q(0)); - } else { - r1 = int128_and(int128_urshift(Vj->Q(0), (imm -1)), int128_one()); - r2 = int128_and(int128_urshift(Vd->Q(0), (imm -1)), int128_one()); - - temp.D(0) = int128_getlo(int128_add(int128_urshift(Vj->Q(0), imm), r1)); - temp.D(1) = int128_getlo(int128_add(int128_urshift(Vd->Q(0), imm), r2)); - } - *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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i, max; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - temp.D(0) = 0; \ - temp.D(1) = 0; \ - max = LSX_LEN/BIT; \ - for (i = 0; i < max; i++) { \ - temp.E1(i) = do_vsrar_ ## E2(Vj->E2(i), imm); \ - temp.E1(i + max) = do_vsrar_ ## E2(Vd->E2(i), imm); \ - } \ - *Vd = temp; \ -} - -void HELPER(vsrarni_d_q)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t imm) -{ - VReg temp; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - Int128 r1, r2; - - if (imm == 0) { - temp.D(0) = int128_getlo(Vj->Q(0)); - temp.D(1) = int128_getlo(Vd->Q(0)); - } else { - r1 = int128_and(int128_rshift(Vj->Q(0), (imm -1)), int128_one()); - r2 = int128_and(int128_rshift(Vd->Q(0), (imm -1)), int128_one()); - - temp.D(0) = int128_getlo(int128_add(int128_rshift(Vj->Q(0), imm), r1)); - temp.D(1) = int128_getlo(int128_add(int128_rshift(Vd->Q(0), imm), r2)); - } - *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, T, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - Vd->E1(i) = do_ssrlns_ ## E1(Vj->E2(i), (T)Vk->E2(i)% BIT, BIT/2 -1); \ - } \ - Vd->D(1) = 0; \ -} - -VSSRLN(vssrln_b_h, 16, uint16_t, B, H) -VSSRLN(vssrln_h_w, 32, uint32_t, H, W) -VSSRLN(vssrln_w_d, 64, uint64_t, W, D) - -#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, T, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - Vd->E1(i) = do_ssrans_ ## E1(Vj->E2(i), (T)Vk->E2(i)%BIT, BIT/2 -1); \ - } \ - Vd->D(1) = 0; \ -} - -VSSRAN(vssran_b_h, 16, uint16_t, B, H) -VSSRAN(vssran_h_w, 32, uint32_t, H, W) -VSSRAN(vssran_w_d, 64, uint64_t, W, D) - -#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, T, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - Vd->E1(i) = do_ssrlnu_ ## E1(Vj->E2(i), (T)Vk->E2(i)%BIT, BIT/2); \ - } \ - Vd->D(1) = 0; \ -} - -VSSRLNU(vssrln_bu_h, 16, uint16_t, B, H) -VSSRLNU(vssrln_hu_w, 32, uint32_t, H, W) -VSSRLNU(vssrln_wu_d, 64, uint64_t, W, D) - -#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, T, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - Vd->E1(i) = do_ssranu_ ## E1(Vj->E2(i), (T)Vk->E2(i)%BIT, BIT/2); \ - } \ - Vd->D(1) = 0; \ -} - -VSSRANU(vssran_bu_h, 16, uint16_t, B, H) -VSSRANU(vssran_hu_w, 32, uint32_t, H, W) -VSSRANU(vssran_wu_d, 64, uint64_t, W, D) - -#define VSSRLNI(NAME, BIT, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - temp.E1(i) = do_ssrlns_ ## E1(Vj->E2(i), imm, BIT/2 -1); \ - temp.E1(i + LSX_LEN/BIT) = do_ssrlns_ ## E1(Vd->E2(i), imm, BIT/2 -1);\ - } \ - *Vd = temp; \ -} - -void HELPER(vssrlni_d_q)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t imm) -{ - Int128 shft_res1, shft_res2, mask; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - if (imm == 0) { - shft_res1 = Vj->Q(0); - shft_res2 = Vd->Q(0); - } else { - shft_res1 = int128_urshift(Vj->Q(0), imm); - shft_res2 = int128_urshift(Vd->Q(0), imm); - } - mask = int128_sub(int128_lshift(int128_one(), 63), int128_one()); - - if (int128_ult(mask, shft_res1)) { - Vd->D(0) = int128_getlo(mask); - }else { - Vd->D(0) = int128_getlo(shft_res1); - } - - if (int128_ult(mask, shft_res2)) { - Vd->D(1) = int128_getlo(mask); - }else { - Vd->D(1) = int128_getlo(shft_res2); - } -} - -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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - temp.E1(i) = do_ssrans_ ## E1(Vj->E2(i), imm, BIT/2 -1); \ - temp.E1(i + LSX_LEN/BIT) = do_ssrans_ ## E1(Vd->E2(i), imm, BIT/2 -1); \ - } \ - *Vd = temp; \ -} - -void HELPER(vssrani_d_q)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t imm) -{ - Int128 shft_res1, shft_res2, mask, min; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - if (imm == 0) { - shft_res1 = Vj->Q(0); - shft_res2 = Vd->Q(0); - } else { - shft_res1 = int128_rshift(Vj->Q(0), imm); - shft_res2 = int128_rshift(Vd->Q(0), imm); - } - mask = int128_sub(int128_lshift(int128_one(), 63), int128_one()); - min = int128_lshift(int128_one(), 63); - - if (int128_gt(shft_res1, mask)) { - Vd->D(0) = int128_getlo(mask); - } else if (int128_lt(shft_res1, int128_neg(min))) { - Vd->D(0) = int128_getlo(min); - } else { - Vd->D(0) = int128_getlo(shft_res1); - } - - if (int128_gt(shft_res2, mask)) { - Vd->D(1) = int128_getlo(mask); - } else if (int128_lt(shft_res2, int128_neg(min))) { - Vd->D(1) = int128_getlo(min); - } else { - Vd->D(1) = int128_getlo(shft_res2); - } -} - -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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - temp.E1(i) = do_ssrlnu_ ## E1(Vj->E2(i), imm, BIT/2); \ - temp.E1(i + LSX_LEN/BIT) = do_ssrlnu_ ## E1(Vd->E2(i), imm, BIT/2); \ - } \ - *Vd = temp; \ -} - -void HELPER(vssrlni_du_q)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t imm) -{ - Int128 shft_res1, shft_res2, mask; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - if (imm == 0) { - shft_res1 = Vj->Q(0); - shft_res2 = Vd->Q(0); - } else { - shft_res1 = int128_urshift(Vj->Q(0), imm); - shft_res2 = int128_urshift(Vd->Q(0), imm); - } - mask = int128_sub(int128_lshift(int128_one(), 64), int128_one()); - - if (int128_ult(mask, shft_res1)) { - Vd->D(0) = int128_getlo(mask); - }else { - Vd->D(0) = int128_getlo(shft_res1); - } - - if (int128_ult(mask, shft_res2)) { - Vd->D(1) = int128_getlo(mask); - }else { - Vd->D(1) = int128_getlo(shft_res2); - } -} - -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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - temp.E1(i) = do_ssranu_ ## E1(Vj->E2(i), imm, BIT/2); \ - temp.E1(i + LSX_LEN/BIT) = do_ssranu_ ## E1(Vd->E2(i), imm, BIT/2); \ - } \ - *Vd = temp; \ -} - -void HELPER(vssrani_du_q)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t imm) -{ - Int128 shft_res1, shft_res2, mask; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - if (imm == 0) { - shft_res1 = Vj->Q(0); - shft_res2 = Vd->Q(0); - } else { - shft_res1 = int128_rshift(Vj->Q(0), imm); - shft_res2 = int128_rshift(Vd->Q(0), imm); - } - - if (int128_lt(Vj->Q(0), int128_zero())) { - shft_res1 = int128_zero(); - } - - if (int128_lt(Vd->Q(0), int128_zero())) { - shft_res2 = int128_zero(); - } - - mask = int128_sub(int128_lshift(int128_one(), 64), int128_one()); - - if (int128_ult(mask, shft_res1)) { - Vd->D(0) = int128_getlo(mask); - }else { - Vd->D(0) = int128_getlo(shft_res1); - } - - if (int128_ult(mask, shft_res2)) { - Vd->D(1) = int128_getlo(mask); - }else { - Vd->D(1) = int128_getlo(shft_res2); - } -} - -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, T, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - Vd->E1(i) = do_ssrlrns_ ## E1(Vj->E2(i), (T)Vk->E2(i)%BIT, BIT/2 -1); \ - } \ - Vd->D(1) = 0; \ -} - -VSSRLRN(vssrlrn_b_h, 16, uint16_t, B, H) -VSSRLRN(vssrlrn_h_w, 32, uint32_t, H, W) -VSSRLRN(vssrlrn_w_d, 64, uint64_t, W, D) - -#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, T, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - Vd->E1(i) = do_ssrarns_ ## E1(Vj->E2(i), (T)Vk->E2(i)%BIT, BIT/2 -1); \ - } \ - Vd->D(1) = 0; \ -} - -VSSRARN(vssrarn_b_h, 16, uint16_t, B, H) -VSSRARN(vssrarn_h_w, 32, uint32_t, H, W) -VSSRARN(vssrarn_w_d, 64, uint64_t, W, D) - -#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, T, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - Vd->E1(i) = do_ssrlrnu_ ## E1(Vj->E2(i), (T)Vk->E2(i)%BIT, BIT/2); \ - } \ - Vd->D(1) = 0; \ -} - -VSSRLRNU(vssrlrn_bu_h, 16, uint16_t, B, H) -VSSRLRNU(vssrlrn_hu_w, 32, uint32_t, H, W) -VSSRLRNU(vssrlrn_wu_d, 64, uint64_t, W, D) - -#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, T, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - Vd->E1(i) = do_ssrarnu_ ## E1(Vj->E2(i), (T)Vk->E2(i)%BIT, BIT/2); \ - } \ - Vd->D(1) = 0; \ -} - -VSSRARNU(vssrarn_bu_h, 16, uint16_t, B, H) -VSSRARNU(vssrarn_hu_w, 32, uint32_t, H, W) -VSSRARNU(vssrarn_wu_d, 64, uint64_t, W, D) - -#define VSSRLRNI(NAME, BIT, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - temp.E1(i) = do_ssrlrns_ ## E1(Vj->E2(i), imm, BIT/2 -1); \ - temp.E1(i + LSX_LEN/BIT) = do_ssrlrns_ ## E1(Vd->E2(i), imm, BIT/2 -1);\ - } \ - *Vd = temp; \ -} - -#define VSSRLRNI_Q(NAME, sh) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - Int128 shft_res1, shft_res2, mask, r1, r2; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - if (imm == 0) { \ - shft_res1 = Vj->Q(0); \ - shft_res2 = Vd->Q(0); \ - } else { \ - r1 = int128_and(int128_urshift(Vj->Q(0), (imm -1)), int128_one()); \ - r2 = int128_and(int128_urshift(Vd->Q(0), (imm -1)), int128_one()); \ - \ - shft_res1 = (int128_add(int128_urshift(Vj->Q(0), imm), r1)); \ - shft_res2 = (int128_add(int128_urshift(Vd->Q(0), imm), r2)); \ - } \ - \ - mask = int128_sub(int128_lshift(int128_one(), sh), int128_one()); \ - \ - if (int128_ult(mask, shft_res1)) { \ - Vd->D(0) = int128_getlo(mask); \ - }else { \ - Vd->D(0) = int128_getlo(shft_res1); \ - } \ - \ - if (int128_ult(mask, shft_res2)) { \ - Vd->D(1) = int128_getlo(mask); \ - }else { \ - Vd->D(1) = int128_getlo(shft_res2); \ - } \ -} - -VSSRLRNI(vssrlrni_b_h, 16, B, H) -VSSRLRNI(vssrlrni_h_w, 32, H, W) -VSSRLRNI(vssrlrni_w_d, 64, W, D) -VSSRLRNI_Q(vssrlrni_d_q, 63) - -#define VSSRARNI(NAME, BIT, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - temp.E1(i) = do_ssrarns_ ## E1(Vj->E2(i), imm, BIT/2 -1); \ - temp.E1(i + LSX_LEN/BIT) = do_ssrarns_ ## E1(Vd->E2(i), imm, BIT/2 -1); \ - } \ - *Vd = temp; \ -} - -void HELPER(vssrarni_d_q)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t imm) -{ - Int128 shft_res1, shft_res2, mask1, mask2, r1, r2; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - if (imm == 0) { - shft_res1 = Vj->Q(0); - shft_res2 = Vd->Q(0); - } else { - r1 = int128_and(int128_rshift(Vj->Q(0), (imm -1)), int128_one()); - r2 = int128_and(int128_rshift(Vd->Q(0), (imm -1)), int128_one()); - - shft_res1 = int128_add(int128_rshift(Vj->Q(0), imm), r1); - shft_res2 = int128_add(int128_rshift(Vd->Q(0), imm), r2); - } - - mask1 = int128_sub(int128_lshift(int128_one(), 63), int128_one()); - mask2 = int128_lshift(int128_one(), 63); - - if (int128_gt(shft_res1, mask1)) { - Vd->D(0) = int128_getlo(mask1); - } else if (int128_lt(shft_res1, int128_neg(mask2))) { - Vd->D(0) = int128_getlo(mask2); - } else { - Vd->D(0) = int128_getlo(shft_res1); - } - - if (int128_gt(shft_res2, mask1)) { - Vd->D(1) = int128_getlo(mask1); - } else if (int128_lt(shft_res2, int128_neg(mask2))) { - Vd->D(1) = int128_getlo(mask2); - } else { - Vd->D(1) = int128_getlo(shft_res2); - } -} - -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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - temp.E1(i) = do_ssrlrnu_ ## E1(Vj->E2(i), imm, BIT/2); \ - temp.E1(i + LSX_LEN/BIT) = do_ssrlrnu_ ## E1(Vd->E2(i), imm, BIT/2); \ - } \ - *Vd = temp; \ -} - -VSSRLRNUI(vssrlrni_bu_h, 16, B, H) -VSSRLRNUI(vssrlrni_hu_w, 32, H, W) -VSSRLRNUI(vssrlrni_wu_d, 64, W, D) -VSSRLRNI_Q(vssrlrni_du_q, 64) - -#define VSSRARNUI(NAME, BIT, E1, E2) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - temp.E1(i) = do_ssrarnu_ ## E1(Vj->E2(i), imm, BIT/2); \ - temp.E1(i + LSX_LEN/BIT) = do_ssrarnu_ ## E1(Vd->E2(i), imm, BIT/2); \ - } \ - *Vd = temp; \ -} - -void HELPER(vssrarni_du_q)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t imm) -{ - Int128 shft_res1, shft_res2, mask1, mask2, r1, r2; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - if (imm == 0) { - shft_res1 = Vj->Q(0); - shft_res2 = Vd->Q(0); - } else { - r1 = int128_and(int128_rshift(Vj->Q(0), (imm -1)), int128_one()); - r2 = int128_and(int128_rshift(Vd->Q(0), (imm -1)), int128_one()); - - shft_res1 = int128_add(int128_rshift(Vj->Q(0), imm), r1); - shft_res2 = int128_add(int128_rshift(Vd->Q(0), imm), r2); - } - - if (int128_lt(Vj->Q(0), int128_zero())) { - shft_res1 = int128_zero(); - } - if (int128_lt(Vd->Q(0), int128_zero())) { - shft_res2 = int128_zero(); - } - - mask1 = int128_sub(int128_lshift(int128_one(), 64), int128_one()); - mask2 = int128_lshift(int128_one(), 64); - - if (int128_gt(shft_res1, mask1)) { - Vd->D(0) = int128_getlo(mask1); - } else if (int128_lt(shft_res1, int128_neg(mask2))) { - Vd->D(0) = int128_getlo(mask2); - } else { - Vd->D(0) = int128_getlo(shft_res1); - } - - if (int128_gt(shft_res2, mask1)) { - Vd->D(1) = int128_getlo(mask1); - } else if (int128_lt(shft_res2, int128_neg(mask2))) { - Vd->D(1) = int128_getlo(mask2); - } else { - Vd->D(1) = int128_getlo(shft_res2); - } -} - -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)(CPULoongArchState *env, uint32_t vd, uint32_t vj) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) \ - { \ - Vd->E(i) = DO_OP(Vj->E(i)); \ - } \ -} - -#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)) - -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)(CPULoongArchState *env, uint32_t vd, uint32_t vj) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; 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_BITCLR(a, bit) (a & ~(1ull << bit)) -#define DO_BITSET(a, bit) (a | 1ull << bit) -#define DO_BITREV(a, bit) (a ^ (1ull << bit)) - -#define DO_BIT(NAME, BIT, E, DO_OP) \ -void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t v) \ -{ \ - int i; \ - VReg *Vd = (VReg *)vd; \ - VReg *Vj = (VReg *)vj; \ - VReg *Vk = (VReg *)vk; \ - \ - for (i = 0; i < LSX_LEN/BIT; 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 v) \ -{ \ - int i; \ - VReg *Vd = (VReg *)vd; \ - VReg *Vj = (VReg *)vj; \ - \ - for (i = 0; i < LSX_LEN/BIT; 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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i, m; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - if (Vj->E(i) < 0) { \ - break; \ - } \ - } \ - m = Vk->E(0) & MASK; \ - Vd->E(m) = i; \ -} - -VFRSTP(vfrstp_b, 8, 0xf, B) -VFRSTP(vfrstp_h, 16, 0x7, H) - -#define VFRSTPI(NAME, BIT, E) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i, m; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - if (Vj->E(i) < 0) { \ - break; \ - } \ - } \ - m = imm % (LSX_LEN/BIT); \ - Vd->E(m) = i; \ -} - -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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - 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 < LSX_LEN/BIT; 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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk, uint32_t va) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - VReg *Va = &(env->fpr[va].vreg); \ - \ - vec_clear_cause(env); \ - for (i = 0; i < LSX_LEN/BIT; 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)(CPULoongArchState *env, uint32_t vd, uint32_t vj) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - vec_clear_cause(env); \ - for (i = 0; i < LSX_LEN/BIT; 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)(CPULoongArchState *env, uint32_t vd, uint32_t vj) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; 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)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - int i; - VReg temp; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - vec_clear_cause(env); - for (i = 0; i < LSX_LEN/32; i++) { - temp.UW(i) = float16_cvt_float32(Vj->UH(i), &env->fp_status); - vec_update_fcsr0(env, GETPC()); - } - *Vd = temp; -} - -void HELPER(vfcvtl_d_s)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - int i; - VReg temp; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - vec_clear_cause(env); - for (i = 0; i < LSX_LEN/64; i++) { - temp.UD(i) = float32_cvt_float64(Vj->UW(i), &env->fp_status); - vec_update_fcsr0(env, GETPC()); - } - *Vd = temp; -} - -void HELPER(vfcvth_s_h)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - int i; - VReg temp; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - vec_clear_cause(env); - for (i = 0; i < LSX_LEN/32; i++) { - temp.UW(i) = float16_cvt_float32(Vj->UH(i + 4), &env->fp_status); - vec_update_fcsr0(env, GETPC()); - } - *Vd = temp; -} - -void HELPER(vfcvth_d_s)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - int i; - VReg temp; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - vec_clear_cause(env); - for (i = 0; i < LSX_LEN/64; i++) { - temp.UD(i) = float32_cvt_float64(Vj->UW(i + 2), &env->fp_status); - vec_update_fcsr0(env, GETPC()); - } - *Vd = temp; -} - -void HELPER(vfcvt_h_s)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t vk) -{ - int i; - VReg temp; - 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 < LSX_LEN/32; i++) { - temp.UH(i + 4) = float32_cvt_float16(Vj->UW(i), &env->fp_status); - temp.UH(i) = float32_cvt_float16(Vk->UW(i), &env->fp_status); - vec_update_fcsr0(env, GETPC()); - } - *Vd = temp; -} - -void HELPER(vfcvt_s_d)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t vk) -{ - int i; - VReg temp; - 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 < LSX_LEN/64; i++) { - temp.UW(i + 2) = float64_cvt_float32(Vj->UD(i), &env->fp_status); - temp.UW(i) = float64_cvt_float32(Vk->UD(i), &env->fp_status); - vec_update_fcsr0(env, GETPC()); - } - *Vd = temp; -} - -void HELPER(vfrint_s)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - int i; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - vec_clear_cause(env); - for (i = 0; i < 4; i++) { - Vd->W(i) = float32_round_to_int(Vj->UW(i), &env->fp_status); - vec_update_fcsr0(env, GETPC()); - } -} - -void HELPER(vfrint_d)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - int i; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - vec_clear_cause(env); - for (i = 0; i < 2; 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)(CPULoongArchState *env, uint32_t vd, uint32_t vj) \ -{ \ - int i; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - vec_clear_cause(env); \ - for (i = 0; i < LSX_LEN/BIT; 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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg temp; \ - 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 < 2; i++) { \ - temp.W(i + 2) = FN(env, Vj->UD(i)); \ - temp.W(i) = FN(env, Vk->UD(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)(CPULoongArchState *env, uint32_t vd, uint32_t vj) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - vec_clear_cause(env); \ - for (i = 0; i < 2; i++) { \ - temp.D(i) = FN(env, Vj->UW(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)(CPULoongArchState *env, uint32_t vd, uint32_t vj) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - vec_clear_cause(env); \ - for (i = 0; i < 2; i++) { \ - temp.D(i) = FN(env, Vj->UW(i + 2)); \ - } \ - *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)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - int i; - VReg temp; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - vec_clear_cause(env); - for (i = 0; i < 2; i++) { - temp.D(i) = int32_to_float64(Vj->W(i), &env->fp_status); - vec_update_fcsr0(env, GETPC()); - } - *Vd = temp; -} - -void HELPER(vffinth_d_w)(CPULoongArchState *env, uint32_t vd, uint32_t vj) -{ - int i; - VReg temp; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - vec_clear_cause(env); - for (i = 0; i < 2; i++) { - temp.D(i) = int32_to_float64(Vj->W(i + 2), &env->fp_status); - vec_update_fcsr0(env, GETPC()); - } - *Vd = temp; -} - -void HELPER(vffint_s_l)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t vk) -{ - int i; - VReg temp; - 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 < 2; i++) { - temp.W(i + 2) = int64_to_float32(Vj->D(i), &env->fp_status); - temp.W(i) = int64_to_float32(Vk->D(i), &env->fp_status); - vec_update_fcsr0(env, GETPC()); - } - *Vd = temp; -} - -#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 VCMPI(NAME, BIT, E, DO_OP) \ -void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t v) \ -{ \ - int i; \ - VReg *Vd = (VReg *)vd; \ - VReg *Vj = (VReg *)vj; \ - typedef __typeof(Vd->E(0)) TD; \ - \ - for (i = 0; i < LSX_LEN/BIT; 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 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 < LSX_LEN/BIT ; 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 v) -{ - int i; - VReg *Vd = (VReg *)vd; - VReg *Vj = (VReg *)vj; - - for (i = 0; i < 16; 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) -{ - uint64_t 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 cd, uint32_t vj) \ -{ \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - env->cf[cd & 0x7] = do_match2(0, Vj->D(0), Vj->D(1), 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 cd, uint32_t vj) \ -{ \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - env->cf[cd & 0x7]= !do_match2(0, Vj->D(0), Vj->D(1), MO); \ -} -SETALLNEZ(vsetallnez_b, MO_8) -SETALLNEZ(vsetallnez_h, MO_16) -SETALLNEZ(vsetallnez_w, MO_32) -SETALLNEZ(vsetallnez_d, MO_64) - -#define VPACKEV(NAME, BIT, E) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; 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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; 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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - temp.E(i + LSX_LEN/BIT) = Vj->E(2 * i); \ - temp.E(i) = Vk->E(2 * 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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - temp.E(i + LSX_LEN/BIT) = Vj->E(2 * i + 1); \ - temp.E(i) = Vk->E(2 * 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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - temp.E(2 * i + 1) = Vj->E(i); \ - temp.E(2 * i) = Vk->E(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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - temp.E(2 * i + 1) = Vj->E(i + LSX_LEN/BIT); \ - temp.E(2 * i) = Vk->E(i + LSX_LEN/BIT); \ - } \ - *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)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t vk, uint32_t va) -{ - int i, m; - VReg temp; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - VReg *Vk = &(env->fpr[vk].vreg); - VReg *Va = &(env->fpr[va].vreg); - - m = LSX_LEN/8; - for (i = 0; i < m ; i++) { - uint64_t k = (uint8_t)Va->B(i) % (2 * m); - temp.B(i) = k < m ? Vk->B(k) : Vj->B(k - m); - } - *Vd = temp; -} - -#define VSHUF(NAME, BIT, E) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t vk) \ -{ \ - int i, m; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - VReg *Vk = &(env->fpr[vk].vreg); \ - \ - m = LSX_LEN/BIT; \ - for (i = 0; i < m; i++) { \ - uint64_t k = ((uint8_t) Vd->E(i)) % (2 * m); \ - temp.E(i) = k < m ? Vk->E(k) : Vj->E(k - 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)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int i; \ - VReg temp; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - for (i = 0; i < LSX_LEN/BIT; i++) { \ - temp.E(i) = Vj->E(((i) & 0xfc) + (((imm) >> \ - (2 * ((i) & 0x03))) & 0x03)); \ - } \ - *Vd = temp; \ -} - -VSHUF4I(vshuf4i_b, 8, B) -VSHUF4I(vshuf4i_h, 16, H) -VSHUF4I(vshuf4i_w, 32, W) - -void HELPER(vshuf4i_d)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t imm) -{ - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - VReg temp; - temp.D(0) = (imm & 2 ? Vj : Vd)->D(imm & 1); - temp.D(1) = (imm & 8 ? Vj : Vd)->D((imm >> 2) & 1); - *Vd = temp; -} - -void HELPER(vpermi_w)(CPULoongArchState *env, - uint32_t vd, uint32_t vj, uint32_t imm) -{ - VReg temp; - VReg *Vd = &(env->fpr[vd].vreg); - VReg *Vj = &(env->fpr[vj].vreg); - - temp.W(0) = Vj->W(imm & 0x3); - temp.W(1) = Vj->W((imm >> 2) & 0x3); - temp.W(2) = Vd->W((imm >> 4) & 0x3); - temp.W(3) = Vd->W((imm >> 6) & 0x3); - *Vd = temp; -} - -#define VEXTRINS(NAME, BIT, E, MASK) \ -void HELPER(NAME)(CPULoongArchState *env, \ - uint32_t vd, uint32_t vj, uint32_t imm) \ -{ \ - int ins, extr; \ - VReg *Vd = &(env->fpr[vd].vreg); \ - VReg *Vj = &(env->fpr[vj].vreg); \ - \ - ins = (imm >> 4) & MASK; \ - extr = imm & MASK; \ - Vd->E(ins) = Vj->E(extr); \ -} - -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/machine.c b/target/loongarch/machine.c index d8ac99c9a41..4e70f5c8798 100644 --- a/target/loongarch/machine.c +++ b/target/loongarch/machine.c @@ -8,13 +8,14 @@ #include "qemu/osdep.h" #include "cpu.h" #include "migration/cpu.h" -#include "internals.h" +#include "system/tcg.h" +#include "vec.h" static const VMStateDescription vmstate_fpu_reg = { .name = "fpu_reg", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(UD(0), VReg), VMSTATE_END_OF_LIST() } @@ -36,7 +37,7 @@ static const VMStateDescription vmstate_fpu = { .version_id = 1, .minimum_version_id = 1, .needed = fpu_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_FPU_REGS(env.fpr, LoongArchCPU, 0), VMSTATE_UINT32(env.fcsr0, LoongArchCPU), VMSTATE_BOOL_ARRAY(env.cf, LoongArchCPU, 8), @@ -48,7 +49,7 @@ static const VMStateDescription vmstate_lsxh_reg = { .name = "lsxh_reg", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(UD(1), VReg), VMSTATE_END_OF_LIST() } @@ -70,18 +71,80 @@ static const VMStateDescription vmstate_lsx = { .version_id = 1, .minimum_version_id = 1, .needed = lsx_needed, - .fields = (VMStateField[]) { + .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, + .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 */ -const VMStateDescription vmstate_tlb = { - .name = "cpu/tlb", +static const VMStateDescription vmstate_tlb_entry = { + .name = "cpu/tlb_entry", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(tlb_misc, LoongArchTLB), VMSTATE_UINT64(tlb_entry0, LoongArchTLB), VMSTATE_UINT64(tlb_entry1, LoongArchTLB), @@ -89,12 +152,25 @@ const VMStateDescription vmstate_tlb = { } }; +static const VMStateDescription vmstate_tlb = { + .name = "cpu/tlb", + .version_id = 0, + .minimum_version_id = 0, + .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 = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { + .version_id = 3, + .minimum_version_id = 3, + .fields = (const VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.gpr, LoongArchCPU, 32), VMSTATE_UINTTL(env.pc, LoongArchCPU), @@ -154,15 +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*[]) { + .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 b7a27df5a9f..20bd3e2f0a3 100644 --- a/target/loongarch/meson.build +++ b/target/loongarch/meson.build @@ -3,31 +3,22 @@ gen = decodetree.process('insns.decode') loongarch_ss = ss.source_set() loongarch_ss.add(files( 'cpu.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', - 'lsx_helper.c', )) -loongarch_tcg_ss.add(zlib) loongarch_system_ss = ss.source_set() loongarch_system_ss.add(files( + 'arch_dump.c', + 'cpu_helper.c', + 'csr.c', 'loongarch-qmp-cmds.c', 'machine.c', - 'tlb_helper.c', - 'constant_timer.c', - 'csr_helper.c', - 'iocsr_helper.c', )) common_ss.add(when: 'CONFIG_LOONGARCH_DIS', if_true: [files('disas.c'), gen]) -loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss]) +subdir('tcg') target_arch += {'loongarch': loongarch_ss} -target_softmmu_arch += {'loongarch': loongarch_system_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 fe79c62fa47..00000000000 --- a/target/loongarch/op_helper.c +++ /dev/null @@ -1,140 +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 "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) { - 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/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/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c new file mode 100644 index 00000000000..2942d7feb81 --- /dev/null +++ b/target/loongarch/tcg/csr_helper.c @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch emulation helpers for CSRs + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "cpu.h" +#include "internals.h" +#include "qemu/host-utils.h" +#include "exec/helper-proto.h" +#include "exec/cputlb.h" +#include "accel/tcg/cpu-ldst.h" +#include "hw/irq.h" +#include "cpu-csr.h" + +target_ulong helper_csrwr_stlbps(CPULoongArchState *env, target_ulong val) +{ + int64_t old_v = env->CSR_STLBPS; + + /* + * The real hardware only supports the min tlb_ps is 12 + * tlb_ps=0 may cause undefined-behavior. + */ + uint8_t tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + if (!check_ps(env, tlb_ps)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Attempted set ps %d\n", tlb_ps); + } + return old_v; +} + +target_ulong helper_csrrd_pgd(CPULoongArchState *env) +{ + int64_t v; + + if (env->CSR_TLBRERA & 0x1) { + v = env->CSR_TLBRBADV; + } else { + v = env->CSR_BADV; + } + + if ((v >> 63) & 0x1) { + v = env->CSR_PGDH; + } else { + v = env->CSR_PGDL; + } + + 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); + + return cpu_loongarch_get_constant_timer_ticks(cpu); +} + +target_ulong helper_csrwr_estat(CPULoongArchState *env, target_ulong val) +{ + int64_t old_v = env->CSR_ESTAT; + + /* Only IS[1:0] can be written */ + env->CSR_ESTAT = deposit64(env->CSR_ESTAT, 0, 2, val); + + return old_v; +} + +target_ulong helper_csrwr_asid(CPULoongArchState *env, target_ulong val) +{ + int64_t old_v = env->CSR_ASID; + + /* Only ASID filed of CSR_ASID can be written */ + env->CSR_ASID = deposit64(env->CSR_ASID, 0, 10, val); + if (old_v != env->CSR_ASID) { + tlb_flush(env_cpu(env)); + } + return old_v; +} + +target_ulong helper_csrwr_tcfg(CPULoongArchState *env, target_ulong val) +{ + LoongArchCPU *cpu = env_archcpu(env); + int64_t old_v = env->CSR_TCFG; + + cpu_loongarch_store_constant_timer_config(cpu, val); + + return old_v; +} + +target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val) +{ + LoongArchCPU *cpu = env_archcpu(env); + int64_t old_v = 0; + + if (val & 0x1) { + bql_lock(); + loongarch_cpu_set_irq(cpu, IRQ_TIMER, 0); + bql_unlock(); + } + return old_v; +} + +target_ulong helper_csrwr_pwcl(CPULoongArchState *env, target_ulong val) +{ + uint8_t shift, ptbase; + int64_t old_v = env->CSR_PWCL; + + /* + * The real hardware only supports 64bit PTE width now, 128bit or others + * treated as illegal. + */ + shift = FIELD_EX64(val, CSR_PWCL, PTEWIDTH); + ptbase = FIELD_EX64(val, CSR_PWCL, PTBASE); + if (shift) { + qemu_log_mask(LOG_GUEST_ERROR, + "Attempted set pte width with %d bit\n", 64 << shift); + val = FIELD_DP64(val, CSR_PWCL, PTEWIDTH, 0); + } + if (!check_ps(env, ptbase)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Attrmpted set ptbase 2^%d\n", ptbase); + } + env->CSR_PWCL =val; + return old_v; +} diff --git a/target/loongarch/tcg/fpu_helper.c b/target/loongarch/tcg/fpu_helper.c new file mode 100644 index 00000000000..fc9c64c20a8 --- /dev/null +++ b/target/loongarch/tcg/fpu_helper.c @@ -0,0 +1,885 @@ +/* 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 "accel/tcg/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); + /* + * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan) + * case sets InvalidOp and returns the input value 'c' + */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, &env->fp_status); + /* Default NaN: sign bit clear, msb frac bit set */ + set_float_default_nan_pattern(0b01000000, &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)) { + return float32_is_quiet_nan(f, &env->fp_status) ? 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)) { + return float64_is_quiet_nan(f, &env->fp_status) ? 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/helper.h b/target/loongarch/tcg/helper.h new file mode 100644 index 00000000000..1d5cb0198c9 --- /dev/null +++ b/target/loongarch/tcg/helper.h @@ -0,0 +1,722 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +DEF_HELPER_2(raise_exception, noreturn, env, i32) + +DEF_HELPER_FLAGS_1(bitrev_w, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(bitrev_d, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(bitswap, TCG_CALL_NO_RWG_SE, tl, tl) + +DEF_HELPER_FLAGS_3(asrtle_d, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(asrtgt_d, TCG_CALL_NO_WG, void, env, tl, tl) + +DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) +DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) +DEF_HELPER_FLAGS_2(cpucfg, TCG_CALL_NO_RWG_SE, tl, env, tl) + +/* Floating-point helper */ +DEF_HELPER_FLAGS_3(fadd_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fsub_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fsub_d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmul_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmul_d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fdiv_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fdiv_d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmax_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmax_d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmin_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmin_d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmaxa_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmaxa_d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmina_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmina_d, TCG_CALL_NO_WG, i64, env, i64, i64) + +DEF_HELPER_FLAGS_5(fmuladd_s, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i32) +DEF_HELPER_FLAGS_5(fmuladd_d, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i32) + +DEF_HELPER_FLAGS_3(fscaleb_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fscaleb_d, TCG_CALL_NO_WG, i64, env, i64, i64) + +DEF_HELPER_FLAGS_2(flogb_s, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(flogb_d, TCG_CALL_NO_WG, i64, env, i64) + +DEF_HELPER_FLAGS_2(fsqrt_s, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(fsqrt_d, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(frsqrt_s, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(frsqrt_d, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(frecip_s, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(frecip_d, TCG_CALL_NO_WG, i64, env, i64) + +DEF_HELPER_FLAGS_2(fclass_s, TCG_CALL_NO_RWG_SE, i64, env, i64) +DEF_HELPER_FLAGS_2(fclass_d, TCG_CALL_NO_RWG_SE, i64, env, i64) + +/* fcmp.cXXX.s */ +DEF_HELPER_4(fcmp_c_s, i64, env, i64, i64, i32) +/* fcmp.sXXX.s */ +DEF_HELPER_4(fcmp_s_s, i64, env, i64, i64, i32) +/* fcmp.cXXX.d */ +DEF_HELPER_4(fcmp_c_d, i64, env, i64, i64, i32) +/* fcmp.sXXX.d */ +DEF_HELPER_4(fcmp_s_d, i64, env, i64, i64, i32) + +DEF_HELPER_2(fcvt_d_s, i64, env, i64) +DEF_HELPER_2(fcvt_s_d, i64, env, i64) +DEF_HELPER_2(ffint_d_w, i64, env, i64) +DEF_HELPER_2(ffint_d_l, i64, env, i64) +DEF_HELPER_2(ffint_s_w, i64, env, i64) +DEF_HELPER_2(ffint_s_l, i64, env, i64) +DEF_HELPER_2(ftintrm_l_s, i64, env, i64) +DEF_HELPER_2(ftintrm_l_d, i64, env, i64) +DEF_HELPER_2(ftintrm_w_s, i64, env, i64) +DEF_HELPER_2(ftintrm_w_d, i64, env, i64) +DEF_HELPER_2(ftintrp_l_s, i64, env, i64) +DEF_HELPER_2(ftintrp_l_d, i64, env, i64) +DEF_HELPER_2(ftintrp_w_s, i64, env, i64) +DEF_HELPER_2(ftintrp_w_d, i64, env, i64) +DEF_HELPER_2(ftintrz_l_s, i64, env, i64) +DEF_HELPER_2(ftintrz_l_d, i64, env, i64) +DEF_HELPER_2(ftintrz_w_s, i64, env, i64) +DEF_HELPER_2(ftintrz_w_d, i64, env, i64) +DEF_HELPER_2(ftintrne_l_s, i64, env, i64) +DEF_HELPER_2(ftintrne_l_d, i64, env, i64) +DEF_HELPER_2(ftintrne_w_s, i64, env, i64) +DEF_HELPER_2(ftintrne_w_d, i64, env, i64) +DEF_HELPER_2(ftint_l_s, i64, env, i64) +DEF_HELPER_2(ftint_l_d, i64, env, i64) +DEF_HELPER_2(ftint_w_s, i64, env, i64) +DEF_HELPER_2(ftint_w_d, i64, env, i64) +DEF_HELPER_2(frint_s, i64, env, i64) +DEF_HELPER_2(frint_d, i64, env, i64) + +DEF_HELPER_FLAGS_1(set_rounding_mode, TCG_CALL_NO_RWG, void, env) + +DEF_HELPER_1(rdtime_d, i64, env) + +#ifndef CONFIG_USER_ONLY +/* CSRs helper */ +DEF_HELPER_1(csrrd_pgd, i64, env) +DEF_HELPER_1(csrrd_cpuid, i64, env) +DEF_HELPER_1(csrrd_tval, i64, env) +DEF_HELPER_2(csrwr_stlbps, i64, env, tl) +DEF_HELPER_2(csrwr_estat, i64, env, tl) +DEF_HELPER_2(csrwr_asid, i64, env, tl) +DEF_HELPER_2(csrwr_tcfg, i64, env, tl) +DEF_HELPER_2(csrwr_ticlr, i64, env, tl) +DEF_HELPER_2(csrwr_pwcl, i64, env, tl) +DEF_HELPER_2(iocsrrd_b, i64, env, tl) +DEF_HELPER_2(iocsrrd_h, i64, env, tl) +DEF_HELPER_2(iocsrrd_w, i64, env, tl) +DEF_HELPER_2(iocsrrd_d, i64, env, tl) +DEF_HELPER_3(iocsrwr_b, void, env, tl, tl) +DEF_HELPER_3(iocsrwr_h, void, env, tl, tl) +DEF_HELPER_3(iocsrwr_w, void, env, tl, tl) +DEF_HELPER_3(iocsrwr_d, void, env, tl, tl) + +/* TLB helper */ +DEF_HELPER_1(tlbwr, void, env) +DEF_HELPER_1(tlbfill, void, env) +DEF_HELPER_1(tlbsrch, void, env) +DEF_HELPER_1(tlbrd, void, env) +DEF_HELPER_1(tlbclr, void, env) +DEF_HELPER_1(tlbflush, void, env) +DEF_HELPER_1(invtlb_all, void, env) +DEF_HELPER_2(invtlb_all_g, void, env, i32) +DEF_HELPER_2(invtlb_all_asid, void, env, tl) +DEF_HELPER_3(invtlb_page_asid, void, env, tl, tl) +DEF_HELPER_3(invtlb_page_asid_or_g, void, env, tl, tl) + +DEF_HELPER_4(lddir, tl, env, tl, tl, i32) +DEF_HELPER_4(ldpte, void, env, tl, tl, i32) +DEF_HELPER_1(ertn, void, env) +DEF_HELPER_1(idle, void, env) +#endif + +/* LoongArch LSX */ +DEF_HELPER_FLAGS_4(vhaddw_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vaddwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsubwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vaddwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsubwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vaddwev_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_q_du_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_q_du_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vavg_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vavgr_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vabsd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vadda_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vadda_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vadda_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vadda_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmini_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vmaxi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vmuh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmulwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmulwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmulwev_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmadd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmadd_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmsub_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmsub_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmaddwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmaddwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmaddwev_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vdiv_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsat_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_3(vexth_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(vext2xv_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_w_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_d_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_d_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_wu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_du_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_du_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsigncov_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsigncov_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsigncov_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsigncov_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(vmskltz_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vmskltz_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vmskltz_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vmskltz_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vmskgez_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vmsknz_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vnori_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vsllwil_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsllwil_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsllwil_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_3(vextl_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsllwil_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsllwil_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsllwil_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_3(vextl_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsrlr_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlr_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlr_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlr_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlri_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlri_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vsrar_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrar_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrar_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrar_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrari_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrari_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrari_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrari_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vsrln_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrln_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrln_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsran_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsran_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsran_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsrlni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrani_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrani_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrani_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrani_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vsrlrn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlrn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlrn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrarn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrarn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrarn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsrlrni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlrni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlrni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlrni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrarni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrarni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrarni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrarni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vssrln_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrln_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrln_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrln_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrln_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrln_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vssrlni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vssrlrn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrlrn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrlrn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrlrn_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrlrn_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrlrn_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vssrlrni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_3(vclo_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclo_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclo_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclo_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclz_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclz_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclz_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclz_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(vpcnt_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vpcnt_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vpcnt_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vpcnt_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vbitclr_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitclr_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitclr_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitclr_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitclri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitclri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitclri_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitclri_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vbitset_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitset_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitset_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitset_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitseti_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitseti_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitseti_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitseti_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vbitrev_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitrev_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitrev_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitrev_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitrevi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitrevi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitrevi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitrevi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vfrstp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vfrstp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vfrstpi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vfrstpi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_5(vfadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmul_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfdiv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_6(vfmadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfmadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfmsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfmsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfnmadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfnmadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfnmsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfnmsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_5(vfmax_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmin_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_5(vfmaxa_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmaxa_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmina_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmina_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vflogb_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vflogb_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vfclass_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfclass_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vfsqrt_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfsqrt_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrecip_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrecip_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrsqrt_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrsqrt_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vfcvtl_s_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfcvth_s_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfcvtl_d_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfcvth_d_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfcvt_h_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfcvt_s_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vfrintrne_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrne_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrz_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrz_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrp_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrp_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrm_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrm_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrint_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrint_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vftintrne_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrne_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrz_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrz_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrp_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrp_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrm_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrm_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftint_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftint_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrz_wu_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrz_lu_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftint_wu_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftint_lu_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vftintrne_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vftintrz_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vftintrp_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vftintrm_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vftint_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrnel_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrneh_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrzl_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrzh_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrpl_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrph_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrml_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrmh_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintl_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftinth_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vffint_s_w, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vffint_d_l, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vffint_s_wu, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vffint_d_lu, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vffintl_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vffinth_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vffint_s_l, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vseqi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vseqi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vseqi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vseqi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vslei_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vslti_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_6(vfcmp_c_s, void, env, i32, i32, i32, i32, i32) +DEF_HELPER_6(vfcmp_s_s, void, env, i32, i32, i32, i32, i32) +DEF_HELPER_6(vfcmp_c_d, void, env, i32, i32, i32, i32, i32) +DEF_HELPER_6(vfcmp_s_d, void, env, i32, i32, i32, i32, i32) + +DEF_HELPER_FLAGS_4(vbitseli_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_4(vsetanyeqz_b, void, env, i32, i32, i32) +DEF_HELPER_4(vsetanyeqz_h, void, env, i32, i32, i32) +DEF_HELPER_4(vsetanyeqz_w, void, env, i32, i32, i32) +DEF_HELPER_4(vsetanyeqz_d, void, env, i32, i32, i32) +DEF_HELPER_4(vsetallnez_b, void, env, i32, i32, i32) +DEF_HELPER_4(vsetallnez_h, void, env, i32, i32, i32) +DEF_HELPER_4(vsetallnez_w, void, env, i32, i32, i32) +DEF_HELPER_4(vsetallnez_d, void, env, i32, i32, i32) + +DEF_HELPER_FLAGS_4(xvinsve0_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(xvinsve0_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(xvpickve_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(xvpickve_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vpackev_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackev_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackev_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackev_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackod_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackod_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackod_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackod_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vpickev_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickev_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickev_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickev_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickod_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickod_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickod_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickod_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vilvl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvl_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvh_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(vshuf_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vshuf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vshuf_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vshuf_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vshuf4i_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vshuf4i_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vshuf4i_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vshuf4i_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vperm_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpermi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vpermi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vpermi_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vextrins_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vextrins_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vextrins_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vextrins_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) diff --git a/target/loongarch/insn_trans/trans_arith.c.inc b/target/loongarch/tcg/insn_trans/trans_arith.c.inc similarity index 100% rename from target/loongarch/insn_trans/trans_arith.c.inc rename to target/loongarch/tcg/insn_trans/trans_arith.c.inc diff --git a/target/loongarch/insn_trans/trans_atomic.c.inc b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc similarity index 92% rename from target/loongarch/insn_trans/trans_atomic.c.inc rename to target/loongarch/tcg/insn_trans/trans_atomic.c.inc index 40085190f62..3d70d759417 100644 --- a/target/loongarch/insn_trans/trans_atomic.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc @@ -5,14 +5,14 @@ static bool gen_ll(DisasContext *ctx, arg_rr_i *a, MemOp mop) { - TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); + 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(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_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; } @@ -56,7 +56,7 @@ static bool gen_am(DisasContext *ctx, arg_rrr *a, 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", + "in atomic insn at pc=0x%" VADDR_PRIx "\n", ctx->base.pc_next - 4); return false; } diff --git a/target/loongarch/insn_trans/trans_bit.c.inc b/target/loongarch/tcg/insn_trans/trans_bit.c.inc similarity index 100% rename from target/loongarch/insn_trans/trans_bit.c.inc rename to target/loongarch/tcg/insn_trans/trans_bit.c.inc diff --git a/target/loongarch/insn_trans/trans_branch.c.inc b/target/loongarch/tcg/insn_trans/trans_branch.c.inc similarity index 95% rename from target/loongarch/insn_trans/trans_branch.c.inc rename to target/loongarch/tcg/insn_trans/trans_branch.c.inc index a4fd2092e5b..f94c1f37ab1 100644 --- a/target/loongarch/insn_trans/trans_branch.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_branch.c.inc @@ -66,7 +66,7 @@ static bool gen_cz_bc(DisasContext *ctx, arg_c_offs *a, TCGCond cond) TCGv src1 = tcg_temp_new(); TCGv src2 = tcg_constant_tl(0); - tcg_gen_ld8u_tl(src1, cpu_env, + tcg_gen_ld8u_tl(src1, tcg_env, offsetof(CPULoongArchState, cf[a->cj])); gen_bc(ctx, src1, src2, a->offs, cond); return true; @@ -80,5 +80,5 @@ 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) +TRANS(bceqz, FP, gen_cz_bc, TCG_COND_EQ) +TRANS(bcnez, FP, 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 00000000000..eda3d6e5611 --- /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, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(1)) +TRANS(crc_w_h_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(2)) +TRANS(crc_w_w_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(4)) +TRANS(crc_w_d_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(8)) +TRANS(crcc_w_b_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(1)) +TRANS(crcc_w_h_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(2)) +TRANS(crcc_w_w_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(4)) +TRANS(crcc_w_d_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(8)) diff --git a/target/loongarch/insn_trans/trans_farith.c.inc b/target/loongarch/tcg/insn_trans/trans_farith.c.inc similarity index 97% rename from target/loongarch/insn_trans/trans_farith.c.inc rename to target/loongarch/tcg/insn_trans/trans_farith.c.inc index a7ced99fd3b..f4a0dea7270 100644 --- a/target/loongarch/insn_trans/trans_farith.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_farith.c.inc @@ -23,7 +23,7 @@ static bool gen_fff(DisasContext *ctx, arg_fff *a, CHECK_FPE; - func(dest, cpu_env, src1, src2); + func(dest, tcg_env, src1, src2); set_fpr(a->fd, dest); return true; @@ -37,7 +37,7 @@ static bool gen_ff(DisasContext *ctx, arg_ff *a, CHECK_FPE; - func(dest, cpu_env, src); + func(dest, tcg_env, src); set_fpr(a->fd, dest); return true; @@ -55,7 +55,7 @@ static bool gen_muladd(DisasContext *ctx, arg_ffff *a, CHECK_FPE; - func(dest, cpu_env, src1, src2, src3, tflag); + func(dest, tcg_env, src1, src2, src3, tflag); set_fpr(a->fd, dest); return true; 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 00000000000..6a2c030a6be --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_fcmp.c.inc @@ -0,0 +1,83 @@ +/* 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(DisasContext *ctx, int cond) +{ + uint32_t flags = 0; + + /*check cond , cond =[0-8,10,12] */ + if ((cond > 8) &&(cond != 10) && (cond != 12)) { + return -1; + } + + 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 = get_fcmp_flags(ctx, a->fcond >>1); + void (*fn)(TCGv, TCGv_env, TCGv, TCGv, TCGv_i32); + + if (flags == -1) { + generate_exception(ctx, EXCCODE_INE); + return true; + } + + 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); + 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 = get_fcmp_flags(ctx, a->fcond >> 1); + void (*fn)(TCGv, TCGv_env, TCGv, TCGv, TCGv_i32); + + if (flags == -1) { + generate_exception(ctx, EXCCODE_INE); + return true; + } + + 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); + 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/insn_trans/trans_fcnv.c.inc b/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc similarity index 100% rename from target/loongarch/insn_trans/trans_fcnv.c.inc rename to target/loongarch/tcg/insn_trans/trans_fcnv.c.inc diff --git a/target/loongarch/insn_trans/trans_fmemory.c.inc b/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc similarity index 95% rename from target/loongarch/insn_trans/trans_fmemory.c.inc rename to target/loongarch/tcg/insn_trans/trans_fmemory.c.inc index 5ddb8a473b1..13452bc7e56 100644 --- a/target/loongarch/insn_trans/trans_fmemory.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc @@ -81,7 +81,7 @@ static bool gen_fload_gt(DisasContext *ctx, arg_frr *a, MemOp mop) CHECK_FPE; - gen_helper_asrtgt_d(cpu_env, src1, src2); + 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); @@ -99,7 +99,7 @@ static bool gen_fstore_gt(DisasContext *ctx, arg_frr *a, MemOp mop) CHECK_FPE; - gen_helper_asrtgt_d(cpu_env, src1, src2); + 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); @@ -115,7 +115,7 @@ static bool gen_fload_le(DisasContext *ctx, arg_frr *a, MemOp mop) CHECK_FPE; - gen_helper_asrtle_d(cpu_env, src1, src2); + 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); @@ -133,7 +133,7 @@ static bool gen_fstore_le(DisasContext *ctx, arg_frr *a, MemOp mop) CHECK_FPE; - gen_helper_asrtle_d(cpu_env, src1, src2); + 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); diff --git a/target/loongarch/insn_trans/trans_fmov.c.inc b/target/loongarch/tcg/insn_trans/trans_fmov.c.inc similarity index 88% rename from target/loongarch/insn_trans/trans_fmov.c.inc rename to target/loongarch/tcg/insn_trans/trans_fmov.c.inc index 928e127820a..5cbd9d3f347 100644 --- a/target/loongarch/insn_trans/trans_fmov.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_fmov.c.inc @@ -22,7 +22,7 @@ static bool trans_fsel(DisasContext *ctx, arg_fsel *a) CHECK_FPE; cond = tcg_temp_new(); - tcg_gen_ld8u_tl(cond, cpu_env, offsetof(CPULoongArchState, cf[a->ca])); + 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); @@ -94,17 +94,17 @@ static bool trans_movgr2fcsr(DisasContext *ctx, arg_movgr2fcsr *a) CHECK_FPE; if (mask == UINT32_MAX) { - tcg_gen_st32_i64(Rj, cpu_env, offsetof(CPULoongArchState, fcsr0)); + 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, cpu_env, offsetof(CPULoongArchState, fcsr0)); + 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, cpu_env, offsetof(CPULoongArchState, fcsr0)); + tcg_gen_st_i32(fcsr0, tcg_env, offsetof(CPULoongArchState, fcsr0)); } /* @@ -112,7 +112,7 @@ static bool trans_movgr2fcsr(DisasContext *ctx, arg_movgr2fcsr *a) * Note that FCSR3 is exactly the rounding mode field. */ if (mask & FCSR0_M3) { - gen_helper_set_rounding_mode(cpu_env); + gen_helper_set_rounding_mode(tcg_env); } return true; } @@ -127,7 +127,7 @@ static bool trans_movfcsr2gr(DisasContext *ctx, arg_movfcsr2gr *a) CHECK_FPE; - tcg_gen_ld32u_i64(dest, cpu_env, offsetof(CPULoongArchState, fcsr0)); + 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); @@ -162,7 +162,7 @@ static bool trans_movfr2cf(DisasContext *ctx, arg_movfr2cf *a) t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, src, 0x1); - tcg_gen_st8_tl(t0, cpu_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); + tcg_gen_st8_tl(t0, tcg_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); return true; } @@ -177,7 +177,7 @@ static bool trans_movcf2fr(DisasContext *ctx, arg_movcf2fr *a) CHECK_FPE; - tcg_gen_ld8u_tl(dest, cpu_env, + tcg_gen_ld8u_tl(dest, tcg_env, offsetof(CPULoongArchState, cf[a->cj & 0x7])); set_fpr(a->fd, dest); @@ -196,7 +196,7 @@ static bool trans_movgr2cf(DisasContext *ctx, arg_movgr2cf *a) 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_gen_st8_tl(t0, tcg_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); return true; } @@ -209,7 +209,7 @@ static bool trans_movcf2gr(DisasContext *ctx, arg_movcf2gr *a) CHECK_FPE; - tcg_gen_ld8u_tl(gpr_dst(ctx, a->rd, EXT_NONE), cpu_env, + tcg_gen_ld8u_tl(gpr_dst(ctx, a->rd, EXT_NONE), tcg_env, offsetof(CPULoongArchState, cf[a->cj & 0x7])); return true; } diff --git a/target/loongarch/insn_trans/trans_memory.c.inc b/target/loongarch/tcg/insn_trans/trans_memory.c.inc similarity index 95% rename from target/loongarch/insn_trans/trans_memory.c.inc rename to target/loongarch/tcg/insn_trans/trans_memory.c.inc index d9d062235a7..42f4e740126 100644 --- a/target/loongarch/insn_trans/trans_memory.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_memory.c.inc @@ -57,7 +57,7 @@ static bool gen_load_gt(DisasContext *ctx, arg_rrr *a, MemOp mop) TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - gen_helper_asrtgt_d(cpu_env, src1, src2); + gen_helper_asrtgt_d(tcg_env, src1, src2); src1 = make_address_i(ctx, src1, 0); tcg_gen_qemu_ld_tl(dest, src1, ctx->mem_idx, mop); gen_set_gpr(a->rd, dest, EXT_NONE); @@ -71,7 +71,7 @@ static bool gen_load_le(DisasContext *ctx, arg_rrr *a, MemOp mop) TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - gen_helper_asrtle_d(cpu_env, src1, src2); + gen_helper_asrtle_d(tcg_env, src1, src2); src1 = make_address_i(ctx, src1, 0); tcg_gen_qemu_ld_tl(dest, src1, ctx->mem_idx, mop); gen_set_gpr(a->rd, dest, EXT_NONE); @@ -85,7 +85,7 @@ static bool gen_store_gt(DisasContext *ctx, arg_rrr *a, MemOp mop) TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - gen_helper_asrtgt_d(cpu_env, src1, src2); + gen_helper_asrtgt_d(tcg_env, src1, src2); src1 = make_address_i(ctx, src1, 0); tcg_gen_qemu_st_tl(data, src1, ctx->mem_idx, mop); @@ -98,7 +98,7 @@ static bool gen_store_le(DisasContext *ctx, arg_rrr *a, MemOp mop) TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - gen_helper_asrtle_d(cpu_env, src1, src2); + gen_helper_asrtle_d(tcg_env, src1, src2); src1 = make_address_i(ctx, src1, 0); tcg_gen_qemu_st_tl(data, src1, ctx->mem_idx, mop); @@ -110,6 +110,11 @@ static bool trans_preld(DisasContext *ctx, arg_preld *a) return true; } +static bool trans_preldx(DisasContext *ctx, arg_preldx * a) +{ + return true; +} + static bool trans_dbar(DisasContext *ctx, arg_dbar * a) { tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc new file mode 100644 index 00000000000..ecbfe23b636 --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc @@ -0,0 +1,416 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + * + * LoongArch translation routines for the privileged instructions. + */ + +#include "csr.h" + +#ifdef CONFIG_USER_ONLY + +#define GEN_FALSE_TRANS(name) \ +static bool trans_##name(DisasContext *ctx, arg_##name * a) \ +{ \ + return false; \ +} + +GEN_FALSE_TRANS(csrrd) +GEN_FALSE_TRANS(csrwr) +GEN_FALSE_TRANS(csrxchg) +GEN_FALSE_TRANS(iocsrrd_b) +GEN_FALSE_TRANS(iocsrrd_h) +GEN_FALSE_TRANS(iocsrrd_w) +GEN_FALSE_TRANS(iocsrrd_d) +GEN_FALSE_TRANS(iocsrwr_b) +GEN_FALSE_TRANS(iocsrwr_h) +GEN_FALSE_TRANS(iocsrwr_w) +GEN_FALSE_TRANS(iocsrwr_d) +GEN_FALSE_TRANS(tlbsrch) +GEN_FALSE_TRANS(tlbrd) +GEN_FALSE_TRANS(tlbwr) +GEN_FALSE_TRANS(tlbfill) +GEN_FALSE_TRANS(tlbclr) +GEN_FALSE_TRANS(tlbflush) +GEN_FALSE_TRANS(invtlb) +GEN_FALSE_TRANS(cacop) +GEN_FALSE_TRANS(ldpte) +GEN_FALSE_TRANS(lddir) +GEN_FALSE_TRANS(ertn) +GEN_FALSE_TRANS(dbcl) +GEN_FALSE_TRANS(idle) + +#else + +typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env); +typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src); + +static bool check_plv(DisasContext *ctx) +{ + if (ctx->plv == MMU_PLV_USER) { + generate_exception(ctx, EXCCODE_IPE); + return true; + } + return false; +} + +static bool set_csr_trans_func(unsigned int csr_num, GenCSRRead readfn, + GenCSRWrite writefn) +{ + CSRInfo *csr; + + csr = get_csr(csr_num); + if (!csr) { + return false; + } + + csr->readfn = (GenCSRFunc)readfn; + csr->writefn = (GenCSRFunc)writefn; + return true; +} + +#define SET_CSR_FUNC(NAME, read, write) \ + set_csr_trans_func(LOONGARCH_CSR_##NAME, read, write) + +void loongarch_csr_translate_init(void) +{ + SET_CSR_FUNC(STLBPS, NULL, gen_helper_csrwr_stlbps); + SET_CSR_FUNC(ESTAT, NULL, gen_helper_csrwr_estat); + SET_CSR_FUNC(ASID, NULL, gen_helper_csrwr_asid); + SET_CSR_FUNC(PGD, gen_helper_csrrd_pgd, NULL); + SET_CSR_FUNC(PWCL, NULL, gen_helper_csrwr_pwcl); + SET_CSR_FUNC(CPUID, gen_helper_csrrd_cpuid, NULL); + SET_CSR_FUNC(TCFG, NULL, gen_helper_csrwr_tcfg); + SET_CSR_FUNC(TVAL, gen_helper_csrrd_tval, NULL); + SET_CSR_FUNC(TICLR, NULL, gen_helper_csrwr_ticlr); +} +#undef SET_CSR_FUNC + +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) && 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; + } + return true; +} + +static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a) +{ + TCGv dest; + const CSRInfo *csr; + GenCSRRead readfn; + + if (check_plv(ctx)) { + return false; + } + csr = get_csr(a->csr); + if (csr == NULL) { + /* CSR is undefined: read as 0. */ + dest = tcg_constant_tl(0); + } else { + check_csr_flags(ctx, csr, false); + dest = gpr_dst(ctx, a->rd, EXT_NONE); + readfn = (GenCSRRead)csr->readfn; + if (readfn) { + readfn(dest, tcg_env); + } else { + tcg_gen_ld_tl(dest, tcg_env, csr->offset); + } + } + gen_set_gpr(a->rd, dest, EXT_NONE); + return true; +} + +static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a) +{ + TCGv dest, src1; + const CSRInfo *csr; + GenCSRWrite writefn; + + if (check_plv(ctx)) { + return false; + } + csr = get_csr(a->csr); + if (csr == NULL) { + /* CSR is undefined: write ignored, read old_value as 0. */ + gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE); + return true; + } + if (!check_csr_flags(ctx, csr, true)) { + /* CSR is readonly: trap. */ + return false; + } + src1 = gpr_src(ctx, a->rd, EXT_NONE); + writefn = (GenCSRWrite)csr->writefn; + if (writefn) { + dest = gpr_dst(ctx, a->rd, EXT_NONE); + writefn(dest, tcg_env, src1); + } else { + 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; +} + +static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a) +{ + TCGv src1, mask, oldv, newv, temp; + const CSRInfo *csr; + GenCSRWrite writefn; + + if (check_plv(ctx)) { + return false; + } + csr = get_csr(a->csr); + if (csr == NULL) { + /* CSR is undefined: write ignored, read old_value as 0. */ + gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE); + return true; + } + + if (!check_csr_flags(ctx, csr, true)) { + /* CSR is readonly: trap. */ + return false; + } + + /* So far only readonly csrs have readfn. */ + assert(csr->readfn == NULL); + + src1 = gpr_src(ctx, a->rd, EXT_NONE); + mask = gpr_src(ctx, a->rj, EXT_NONE); + oldv = tcg_temp_new(); + newv = tcg_temp_new(); + temp = tcg_temp_new(); + + 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); + + writefn = (GenCSRWrite)csr->writefn; + if (writefn) { + writefn(oldv, tcg_env, newv); + } else { + tcg_gen_st_tl(newv, tcg_env, csr->offset); + } + gen_set_gpr(a->rd, oldv, EXT_NONE); + return true; +} + +static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a, + void (*func)(TCGv, TCGv_ptr, TCGv)) +{ + TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); + TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + + if (check_plv(ctx)) { + return false; + } + func(dest, tcg_env, src1); + return true; +} + +static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a, + void (*func)(TCGv_ptr, TCGv, TCGv)) +{ + TCGv val = gpr_src(ctx, a->rd, EXT_NONE); + TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); + + if (check_plv(ctx)) { + return false; + } + func(tcg_env, addr, val); + return true; +} + +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_DA_IDX) { + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; + } +} + +static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a) +{ + if (check_plv(ctx)) { + return false; + } + gen_helper_tlbsrch(tcg_env); + return true; +} + +static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a) +{ + if (check_plv(ctx)) { + return false; + } + gen_helper_tlbrd(tcg_env); + return true; +} + +static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a) +{ + if (check_plv(ctx)) { + return false; + } + gen_helper_tlbwr(tcg_env); + check_mmu_idx(ctx); + return true; +} + +static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a) +{ + if (check_plv(ctx)) { + return false; + } + gen_helper_tlbfill(tcg_env); + check_mmu_idx(ctx); + return true; +} + +static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a) +{ + if (check_plv(ctx)) { + return false; + } + gen_helper_tlbclr(tcg_env); + check_mmu_idx(ctx); + return true; +} + +static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a) +{ + if (check_plv(ctx)) { + return false; + } + gen_helper_tlbflush(tcg_env); + check_mmu_idx(ctx); + return true; +} + +static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a) +{ + TCGv rj = gpr_src(ctx, a->rj, EXT_NONE); + TCGv rk = gpr_src(ctx, a->rk, EXT_NONE); + + if (check_plv(ctx)) { + return false; + } + + switch (a->imm) { + case 0: + case 1: + gen_helper_invtlb_all(tcg_env); + break; + case 2: + gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(1)); + break; + case 3: + gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(0)); + break; + case 4: + gen_helper_invtlb_all_asid(tcg_env, rj); + break; + case 5: + gen_helper_invtlb_page_asid(tcg_env, rj, rk); + break; + case 6: + gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk); + break; + default: + return false; + } + ctx->base.is_jmp = DISAS_STOP; + return true; +} + +static bool trans_cacop(DisasContext *ctx, arg_cacop *a) +{ + /* Treat the cacop as a nop */ + if (check_plv(ctx)) { + return false; + } + return true; +} + +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(tcg_env, src1, tcg_constant_tl(a->imm), mem_idx); + return true; +} + +static bool trans_lddir(DisasContext *ctx, arg_lddir *a) +{ + TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx); + 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, tcg_env, src, tcg_constant_tl(a->imm), mem_idx); + return true; +} + +static bool trans_ertn(DisasContext *ctx, arg_ertn *a) +{ + if (check_plv(ctx)) { + return false; + } + gen_helper_ertn(tcg_env); + ctx->base.is_jmp = DISAS_EXIT; + return true; +} + +static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a) +{ + if (check_plv(ctx)) { + return false; + } + generate_exception(ctx, EXCCODE_DBP); + return true; +} + +static bool trans_idle(DisasContext *ctx, arg_idle *a) +{ + if (check_plv(ctx)) { + return false; + } + + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4); + gen_helper_idle(tcg_env); + ctx->base.is_jmp = DISAS_NORETURN; + return true; +} +#endif diff --git a/target/loongarch/insn_trans/trans_shift.c.inc b/target/loongarch/tcg/insn_trans/trans_shift.c.inc similarity index 88% rename from target/loongarch/insn_trans/trans_shift.c.inc rename to target/loongarch/tcg/insn_trans/trans_shift.c.inc index 2f4bd6ff288..377307785aa 100644 --- a/target/loongarch/insn_trans/trans_shift.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_shift.c.inc @@ -67,19 +67,9 @@ static void gen_rotr_d(TCGv dest, TCGv src1, TCGv src2) tcg_gen_rotr_tl(dest, src1, t0); } -static bool trans_srai_w(DisasContext *ctx, arg_srai_w *a) +static void gen_sari_w(TCGv dest, TCGv src1, target_long imm) { - TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); - TCGv src1 = gpr_src(ctx, a->rj, EXT_ZERO); - - if (!avail_64(ctx)) { - return false; - } - - tcg_gen_sextract_tl(dest, src1, a->imm, 32 - a->imm); - gen_set_gpr(a->rd, dest, EXT_NONE); - - return true; + tcg_gen_sextract_tl(dest, src1, imm, 32 - imm); } TRANS(sll_w, ALL, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_sll_w) @@ -94,6 +84,7 @@ 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 00000000000..78730029cba --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_vec.c.inc @@ -0,0 +1,5528 @@ +/* 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 = 0, 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 << 40) | (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: + g_assert_not_reached(); + } + return data; +} + +static bool check_valid_vldi_mode(arg_vldi *a) +{ + return extract32(a->imm, 8, 4) <= 12; +} + +static bool gen_vldi(DisasContext *ctx, arg_vldi *a, uint32_t oprsz) +{ + int sel, vece; + uint64_t value; + + if (!check_valid_vldi_mode(a)) { + generate_exception(ctx, EXCCODE_INE); + return true; + } + + 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 = get_fcmp_flags(ctx, a->fcond >> 1); + 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(flags == -1){ + generate_exception(ctx, EXCCODE_INE); + return true; + } + + if (!check_vec(ctx, sz)) { + return true; + } + + fn = (a->fcond & 1 ? gen_helper_vfcmp_s_s : gen_helper_vfcmp_c_s); + 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 = get_fcmp_flags(ctx, a->fcond >> 1); + 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 (flags == -1) { + generate_exception(ctx, EXCCODE_INE); + return true; + } + + if (!check_vec(ctx, sz)) { + return true; + } + + fn = (a->fcond & 1 ? gen_helper_vfcmp_s_d : gen_helper_vfcmp_c_d); + 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, 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(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 00000000000..c155f48564d --- /dev/null +++ b/target/loongarch/tcg/iocsr_helper.c @@ -0,0 +1,67 @@ +/* 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 "accel/tcg/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 00000000000..bdf34f9673b --- /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 00000000000..16ac0d43bc9 --- /dev/null +++ b/target/loongarch/tcg/op_helper.c @@ -0,0 +1,139 @@ +/* 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 "accel/tcg/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/tcg_loongarch.h b/target/loongarch/tcg/tcg_loongarch.h new file mode 100644 index 00000000000..fd4e1160229 --- /dev/null +++ b/target/loongarch/tcg/tcg_loongarch.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch TCG interface + * + * Copyright (c) 2025 Loongson Technology Corporation Limited + */ +#ifndef TARGET_LOONGARCH_TCG_LOONGARCH_H +#define TARGET_LOONGARCH_TCG_LOONGARCH_H +#include "cpu.h" + +void loongarch_csr_translate_init(void); + +bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr); + +int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx); + +#endif /* TARGET_LOONGARCH_TCG_LOONGARCH_H */ diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c new file mode 100644 index 00000000000..dc48b0f4d2f --- /dev/null +++ b/target/loongarch/tcg/tlb_helper.c @@ -0,0 +1,756 @@ +/* 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/cputlb.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" +#include "accel/tcg/cpu-ldst.h" +#include "exec/log.h" +#include "cpu-csr.h" +#include "tcg/tcg_loongarch.h" + +bool check_ps(CPULoongArchState *env, uint8_t tlb_ps) +{ + if (tlb_ps >= 64) { + return false; + } + return BIT_ULL(tlb_ps) & (env->CSR_PRCFG2); +} + +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); + uint8_t tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); + + if (!tlb_e) { + return; + } + 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; + } + + /*check csr_ps */ + if (!check_ps(env, csr_ps)) { + qemu_log_mask(LOG_GUEST_ERROR, "csr_ps %d is illegal\n", csr_ps); + return; + } + + /* 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; +} + +/* + * 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; +} + +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); + } + + if (!check_ps(env, pagesize)) { + qemu_log_mask(LOG_GUEST_ERROR, "pagesize %d is illegal\n", pagesize); + return; + } + + stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + if (!check_ps(env, stlb_ps)) { + qemu_log_mask(LOG_GUEST_ERROR, "stlb_ps %d is illegal\n", stlb_ps); + return; + } + + 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; + uint8_t tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); + + if (!tlb_e) { + continue; + } + 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; + uint8_t tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); + + if (!tlb_e) { + continue; + } + 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, 0); + + 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; + 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; + get_dir_base_width(env, &dir_base, &dir_width, level); + index = (badvaddr >> dir_base) & ((1 << dir_width) - 1); + phys = base | index << 3; + return ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; +} + +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; + 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 { + badv = env->CSR_TLBRBADV; + + ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1); + ptindex = ptindex & ~0x1; /* clear bit 0 */ + ptoffset0 = ptindex << 3; + ptoffset1 = (ptindex + 1) << 3; + 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); +} + +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; +} + +int loongarch_get_addr_from_tlb(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; +} diff --git a/target/loongarch/tcg/translate.c b/target/loongarch/tcg/translate.c new file mode 100644 index 00000000000..53a0b4c3ce9 --- /dev/null +++ b/target/loongarch/tcg/translate.c @@ -0,0 +1,367 @@ +/* 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/target_page.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 "tcg_loongarch.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. " + "0x%" VADDR_PRIx ": 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 loongarch_translate_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"); + +#ifndef CONFIG_USER_ONLY + loongarch_csr_translate_init(); +#endif +} diff --git a/target/loongarch/tcg/vec_helper.c b/target/loongarch/tcg/vec_helper.c new file mode 100644 index 00000000000..a270998e638 --- /dev/null +++ b/target/loongarch/tcg/vec_helper.c @@ -0,0 +1,3493 @@ +/* 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/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 c8b8b0497f5..00000000000 --- a/target/loongarch/tlb_helper.c +++ /dev/null @@ -1,804 +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); - 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; - } - - /* 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_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] - */ -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 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); - } -} - -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); - 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) -{ - 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; - 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, 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) -{ - 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 " 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; - 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 00000000000..dea11edc0f1 --- /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 00000000000..c2ecb78f084 --- /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 fd393ed76da..00000000000 --- a/target/loongarch/translate.c +++ /dev/null @@ -1,353 +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 "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" - -/* 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 void get_vreg64(TCGv_i64 dest, int regno, int index) -{ - tcg_gen_ld_i64(dest, cpu_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, cpu_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(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 (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 = cs->env_ptr; - 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); - - if (FIELD_EX64(env->cpucfg[2], CPUCFG2, LSX)) { - ctx->vl = LSX_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, cpu_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, cpu_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_lsx.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 = translator_ldl(env, &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 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]); - } - - 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 89b49a859ea..018dc5eb171 100644 --- a/target/loongarch/translate.h +++ b/target/loongarch/translate.h @@ -23,7 +23,9 @@ #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)) +#define avail_CRC(C) (FIELD_EX32((C)->cpucfg1, CPUCFG1, CRC)) /* * If an operation is being performed on less than TARGET_LONG_BITS, diff --git a/target/loongarch/vec.h b/target/loongarch/vec.h new file mode 100644 index 00000000000..3c9adf84274 --- /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 23debad519a..23aae24ebec 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 39dcbcece8f..256a2b5f8b2 100644 --- a/target/m68k/cpu-param.h +++ b/target/m68k/cpu-param.h @@ -2,13 +2,12 @@ * 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 #define M68K_CPU_PARAM_H -#define TARGET_LONG_BITS 32 /* * Coldfire Linux uses 8k pages * and m68k linux uses 4k pages @@ -18,4 +17,6 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 +#define TARGET_INSN_START_EXTRA_WORDS 1 + #endif diff --git a/target/m68k/cpu-qom.h b/target/m68k/cpu-qom.h index 0ec7750a926..273e8eae417 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_phases: The parent class' reset phase handlers. - * - * A Motorola 68k CPU model. - */ -struct M68kCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; - +#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 70d58471dc3..6a09db3a6f6 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -23,6 +23,8 @@ #include "cpu.h" #include "migration/vmstate.h" #include "fpu/softfloat.h" +#include "exec/translation-block.h" +#include "accel/tcg/cpu-ops.h" static void m68k_cpu_set_pc(CPUState *cs, vaddr value) { @@ -38,6 +40,24 @@ static vaddr m68k_cpu_get_pc(CPUState *cs) return cpu->env.pc; } +static TCGTBCPUState m68k_get_tb_cpu_state(CPUState *cs) +{ + CPUM68KState *env = cpu_env(cs); + uint32_t flags; + + flags = (env->macsr >> 4) & TB_FLAGS_MACSR; + if (env->sr & SR_S) { + flags |= TB_FLAGS_MSR_S; + flags |= (env->sfc << (TB_FLAGS_SFC_S_BIT - 2)) & TB_FLAGS_SFC_S; + flags |= (env->dfc << (TB_FLAGS_DFC_S_BIT - 2)) & TB_FLAGS_DFC_S; + } + if (M68K_SR_TRACE(env->sr) == M68K_SR_TRACE_ANY_INS) { + flags |= TB_FLAGS_TRACE; + } + + return (TCGTBCPUState){ .pc = env->pc, .flags = flags }; +} + static void m68k_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) @@ -51,10 +71,17 @@ static void m68k_restore_state_to_opc(CPUState *cs, } } +#ifndef CONFIG_USER_ONLY static bool m68k_cpu_has_work(CPUState *cs) { return cs->interrupt_request & CPU_INTERRUPT_HARD; } +#endif /* !CONFIG_USER_ONLY */ + +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) { @@ -66,17 +93,16 @@ static void m68k_unset_feature(CPUM68KState *env, int feature) env->features &= ~BIT_ULL(feature); } -static void m68k_cpu_reset_hold(Object *obj) +static void m68k_cpu_reset_hold(Object *obj, ResetType type) { - CPUState *s = CPU(obj); - M68kCPU *cpu = M68K_CPU(s); - M68kCPUClass *mcc = M68K_CPU_GET_CLASS(cpu); - CPUM68KState *env = &cpu->env; - floatx80 nan = floatx80_default_nan(NULL); + CPUState *cs = CPU(obj); + M68kCPUClass *mcc = M68K_CPU_GET_CLASS(obj); + CPUM68KState *env = cpu_env(cs); + floatx80 nan; int i; if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUM68KState, end_reset_fields)); @@ -85,6 +111,61 @@ static void m68k_cpu_reset_hold(Object *obj) #else cpu_m68k_set_sr(env, SR_S | SR_I); #endif + /* + * 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); + /* Default NaN: sign bit clear, all frac bits set */ + set_float_default_nan_pattern(0b01111111, &env->fp_status); + /* + * m68k-specific floatx80 behaviour: + * * default Infinity values have a zero Integer bit + * * input Infinities may have the Integer bit either 0 or 1 + * * pseudo-denormals supported for input and output + * * don't raise Invalid for pseudo-NaN/pseudo-Inf/Unnormal + * + * With m68k, the explicit integer bit can be zero in the case of: + * - zeros (exp == 0, mantissa == 0) + * - denormalized numbers (exp == 0, mantissa != 0) + * - unnormalized numbers (exp != 0, exp < 0x7FFF) + * - infinities (exp == 0x7FFF, mantissa == 0) + * - not-a-numbers (exp == 0x7FFF, mantissa != 0) + * + * For infinities and NaNs, the explicit integer bit can be either one or + * zero. + * + * The IEEE 754 standard does not define a zero integer bit. Such a number + * is an unnormalized number. Hardware does not directly support + * denormalized and unnormalized numbers, but implicitly supports them by + * trapping them as unimplemented data types, allowing efficient conversion + * in software. + * + * See "M68000 FAMILY PROGRAMMER’S REFERENCE MANUAL", + * "1.6 FLOATING-POINT DATA TYPES" + * + * Note though that QEMU's fp emulation does directly handle both + * denormal and unnormal values, and does not trap to guest software. + */ + set_floatx80_behaviour(floatx80_default_inf_int_bit_is_zero | + floatx80_pseudo_inf_valid | + floatx80_pseudo_nan_valid | + floatx80_unnormal_valid | + floatx80_pseudo_denormal_valid, + &env->fp_status); + + nan = floatx80_default_nan(&env->fp_status); for (i = 0; i < 8; i++) { env->fregs[i].d = nan; } @@ -98,6 +179,7 @@ static void m68k_cpu_reset_hold(Object *obj) static void m68k_cpu_disas_set_info(CPUState *s, disassemble_info *info) { info->print_insn = print_insn_m68k; + info->endian = BFD_ENDIAN_BIG; info->mach = 0; } @@ -111,17 +193,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); @@ -130,8 +208,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); @@ -140,12 +217,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); @@ -153,6 +230,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); } /* @@ -164,8 +242,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); @@ -195,8 +272,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); @@ -222,8 +298,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); @@ -243,8 +318,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); @@ -257,8 +331,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); @@ -270,8 +343,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); @@ -284,8 +356,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); @@ -327,13 +398,6 @@ 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_USER_ONLY) static bool fpu_needed(void *opaque) { @@ -389,7 +453,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() @@ -398,18 +462,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_post_load(void *opaque, int version) +static int fpu_pre_save(void *opaque) { M68kCPU *s = opaque; - cpu_m68k_restore_fp_status(&s->env); + 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_set_fpsr(&s->env, s->env.fpsr); return 0; } @@ -418,8 +489,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), @@ -440,7 +512,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), @@ -462,7 +534,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), @@ -487,7 +559,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), @@ -500,7 +572,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), @@ -517,7 +589,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, @@ -529,25 +601,34 @@ static const VMStateDescription vmstate_m68k_cpu = { #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps m68k_sysemu_ops = { + .has_work = m68k_cpu_has_work, .get_phys_page_debug = m68k_cpu_get_phys_page_debug, }; #endif /* !CONFIG_USER_ONLY */ -#include "hw/core/tcg-cpu-ops.h" +static const TCGCPUOps m68k_tcg_ops = { + /* MTTCG not yet supported: require strict ordering */ + .guest_default_memory_order = TCG_MO_ALL, + .mttcg_supported = false, -static const struct TCGCPUOps m68k_tcg_ops = { .initialize = m68k_tcg_init, + .translate_code = m68k_translate_code, + .get_tb_cpu_state = m68k_get_tb_cpu_state, .restore_state_to_opc = m68k_restore_state_to_opc, + .mmu_index = m68k_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill = m68k_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_uint32, .cpu_exec_interrupt = m68k_cpu_exec_interrupt, + .cpu_exec_halt = m68k_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = m68k_cpu_do_interrupt, .do_transaction_failed = m68k_cpu_transaction_failed, #endif /* !CONFIG_USER_ONLY */ }; -static void m68k_cpu_class_init(ObjectClass *c, void *data) +static void m68k_cpu_class_init(ObjectClass *c, const void *data) { M68kCPUClass *mcc = M68K_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); @@ -560,7 +641,6 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data) &mcc->parent_phases); cc->class_by_name = m68k_cpu_class_by_name; - cc->has_work = m68k_cpu_has_work; cc->dump_state = m68k_cpu_dump_state; cc->set_pc = m68k_cpu_set_pc; cc->get_pc = m68k_cpu_get_pc; @@ -572,11 +652,10 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data) #endif cc->disas_set_info = m68k_cpu_disas_set_info; - cc->gdb_num_core_regs = 18; cc->tcg_ops = &m68k_tcg_ops; } -static void m68k_cpu_class_init_cf_core(ObjectClass *c, void *data) +static void m68k_cpu_class_init_cf_core(ObjectClass *c, const void *data) { CPUClass *cc = CPU_CLASS(c); @@ -591,7 +670,7 @@ static void m68k_cpu_class_init_cf_core(ObjectClass *c, void *data) .class_init = m68k_cpu_class_init_cf_core \ } -static void m68k_cpu_class_init_m68k_core(ObjectClass *c, void *data) +static void m68k_cpu_class_init_m68k_core(ObjectClass *c, const void *data) { CPUClass *cc = CPU_CLASS(c); @@ -611,7 +690,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 cf702827171..d9db6a486a8 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -21,7 +21,9 @@ #ifndef M68K_CPU_H #define M68K_CPU_H +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" #include "cpu-qom.h" @@ -66,7 +68,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 @@ -76,8 +78,6 @@ #define M68K_MAX_TTR 2 #define TTR(type, index) ttr[((type & ACCESS_CODE) == ACCESS_CODE) * 2 + index] -#define TARGET_INSN_START_EXTRA_WORDS 1 - typedef CPU_LDoubleU FPReg; typedef struct CPUArchState { @@ -164,14 +164,24 @@ 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); @@ -183,13 +193,16 @@ int m68k_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int m68k_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void m68k_tcg_init(void); +void m68k_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); void m68k_cpu_init_gdb(M68kCPU *cpu); uint32_t cpu_m68k_get_ccr(CPUM68KState *env); 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 +481,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 +553,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 +562,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,19 +578,11 @@ 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, @@ -588,8 +594,6 @@ void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, MemTxResult response, uintptr_t retaddr); #endif -#include "exec/cpu-all.h" - /* TB flags */ #define TB_FLAGS_MACSR 0x0f #define TB_FLAGS_MSR_S_BIT 13 @@ -601,22 +605,6 @@ 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, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *pc = env->pc; - *cs_base = 0; - *flags = (env->macsr >> 4) & TB_FLAGS_MACSR; - if (env->sr & SR_S) { - *flags |= TB_FLAGS_MSR_S; - *flags |= (env->sfc << (TB_FLAGS_SFC_S_BIT - 2)) & TB_FLAGS_SFC_S; - *flags |= (env->dfc << (TB_FLAGS_DFC_S_BIT - 2)) & TB_FLAGS_DFC_S; - } - if (M68K_SR_TRACE(env->sr) == M68K_SR_TRACE_ANY_INS) { - *flags |= TB_FLAGS_TRACE; - } -} - void dump_mmu(CPUM68KState *env); #endif diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index ab120b5f59b..56012863c85 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -21,8 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "softfloat.h" /* @@ -164,6 +163,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_flushed)) { + 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 = \ @@ -383,7 +454,7 @@ void HELPER(ftst)(CPUM68KState *env, FPReg *val) if (floatx80_is_any_nan(val->d)) { cc |= FPSR_CC_A; - } else if (floatx80_is_infinity(val->d)) { + } else if (floatx80_is_infinity(val->d, &env->fp_status)) { cc |= FPSR_CC_I; } else if (floatx80_is_zero(val->d)) { cc |= FPSR_CC_Z; @@ -543,14 +614,13 @@ void HELPER(frem)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) fp_rem = floatx80_rem(val1->d, val0->d, &env->fp_status); if (!floatx80_is_any_nan(fp_rem)) { - float_status fp_status = { }; + /* Use local temporary fp_status to set different rounding mode */ + float_status fp_status = env->fp_status; uint32_t quotient; int sign; /* Calculate quotient directly using round to nearest mode */ set_float_rounding_mode(float_round_nearest_even, &fp_status); - set_floatx80_rounding_precision( - get_floatx80_rounding_precision(&env->fp_status), &fp_status); fp_quot.d = floatx80_div(val1->d, val0->d, &fp_status); sign = extractFloatx80Sign(fp_quot.d); diff --git a/target/m68k/gdbstub.c b/target/m68k/gdbstub.c index 1e5f033a12b..136159f98f2 100644 --- a/target/m68k/gdbstub.c +++ b/target/m68k/gdbstub.c @@ -23,8 +23,7 @@ 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 0a1544cd68d..15f110fa7a2 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -20,59 +20,26 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/gdbstub.h" #include "exec/helper-proto.h" +#include "system/memory.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; - - 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); -} + M68kCPU *cpu = M68K_CPU(cs); + CPUM68KState *env = &cpu->env; -static int cf_fpu_gdb_get_reg(CPUM68KState *env, GByteArray *mem_buf, int n) -{ if (n < 8) { - float_status s; + /* Use scratch float_status so any exceptions don't change CPU state */ + float_status s = env->fp_status; return gdb_get_reg64(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); } switch (n) { @@ -86,19 +53,23 @@ 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); + /* Use scratch float_status so any exceptions don't change CPU state */ + float_status s = env->fp_status; + 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; @@ -106,8 +77,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); @@ -118,15 +92,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); @@ -134,10 +111,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; @@ -152,10 +129,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. */ } @@ -314,7 +291,6 @@ void HELPER(m68k_movec_to)(CPUM68KState *env, uint32_t reg, uint32_t val) /* Invalid control registers will generate an exception. */ raise_exception_ra(env, EXCP_ILLEGAL, 0); - return; } uint32_t HELPER(m68k_movec_from)(CPUM68KState *env, uint32_t reg) @@ -506,7 +482,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; @@ -529,19 +504,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)) { @@ -934,8 +909,7 @@ static int get_physical_address(CPUM68KState *env, hwaddr *physical, 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; @@ -983,8 +957,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; @@ -1012,7 +985,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, diff --git a/target/m68k/helper.h b/target/m68k/helper.h index 2bbe0dc0325..95aa5e53bb9 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) diff --git a/target/m68k/m68k-semi.c b/target/m68k/m68k-semi.c index 239f6e44e90..6fbbd140f32 100644 --- a/target/m68k/m68k-semi.c +++ b/target/m68k/m68k-semi.c @@ -15,6 +15,10 @@ * * 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" @@ -23,7 +27,7 @@ #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" @@ -73,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) || @@ -91,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) || @@ -130,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); diff --git a/target/m68k/meson.build b/target/m68k/meson.build index 355db26c6f5..4d213daaf67 100644 --- a/target/m68k/meson.build +++ b/target/m68k/meson.build @@ -11,9 +11,12 @@ m68k_ss.add(files( m68k_system_ss = ss.source_set() m68k_system_ss.add(files( - 'm68k-semi.c', '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_system_ss} +target_system_arch += {'m68k': m68k_system_ss} diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c index 1ce850bbc59..f29ae12af84 100644 --- a/target/m68k/op_helper.c +++ b/target/m68k/op_helper.c @@ -20,8 +20,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "semihosting/semihost.h" #if !defined(CONFIG_USER_ONLY) @@ -52,7 +51,7 @@ static void m68k_rte(CPUM68KState *env) 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 +201,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 +245,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 +430,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 +443,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 +496,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 +795,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 00000000000..dbe669cc5f6 --- /dev/null +++ b/target/m68k/semihosting-stub.c @@ -0,0 +1,18 @@ +/* + * m68k/ColdFire semihosting stub + * + * Copyright (c) 2024 Linaro Ltd. + * + * Authors: + * Philippe Mathieu-Daudé + * + * 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/softfloat.c b/target/m68k/softfloat.c index 02dcc03d15d..d1f150e641f 100644 --- a/target/m68k/softfloat.c +++ b/target/m68k/softfloat.c @@ -142,8 +142,7 @@ floatx80 floatx80_scale(floatx80 a, floatx80 b, float_status *status) if ((uint64_t) (aSig << 1)) { return propagateFloatx80NaN(a, b, status); } - return packFloatx80(aSign, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(aSign, status); } if (aExp == 0) { if (aSig == 0) { @@ -245,7 +244,7 @@ floatx80 floatx80_lognp1(floatx80 a, float_status *status) float_raise(float_flag_invalid, status); return floatx80_default_nan(status); } - return packFloatx80(0, floatx80_infinity.high, floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { @@ -255,8 +254,7 @@ floatx80 floatx80_lognp1(floatx80 a, float_status *status) if (aSign && aExp >= one_exp) { if (aExp == one_exp && aSig == one_sig) { float_raise(float_flag_divbyzero, status); - return packFloatx80(aSign, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(aSign, status); } float_raise(float_flag_invalid, status); return floatx80_default_nan(status); @@ -442,8 +440,7 @@ floatx80 floatx80_logn(floatx80 a, float_status *status) propagateFloatx80NaNOneArg(a, status); } if (aSign == 0) { - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } } @@ -452,8 +449,7 @@ floatx80 floatx80_logn(floatx80 a, float_status *status) if (aExp == 0) { if (aSig == 0) { /* zero */ float_raise(float_flag_divbyzero, status); - return packFloatx80(1, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(1, status); } if ((aSig & one_sig) == 0) { /* denormal */ normalizeFloatx80Subnormal(aSig, &aExp, &aSig); @@ -610,15 +606,13 @@ floatx80 floatx80_log10(floatx80 a, float_status *status) propagateFloatx80NaNOneArg(a, status); } if (aSign == 0) { - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } } if (aExp == 0 && aSig == 0) { float_raise(float_flag_divbyzero, status); - return packFloatx80(1, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(1, status); } if (aSign) { @@ -668,16 +662,14 @@ floatx80 floatx80_log2(floatx80 a, float_status *status) propagateFloatx80NaNOneArg(a, status); } if (aSign == 0) { - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } } if (aExp == 0) { if (aSig == 0) { float_raise(float_flag_divbyzero, status); - return packFloatx80(1, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(1, status); } normalizeFloatx80Subnormal(aSig, &aExp, &aSig); } @@ -740,8 +732,7 @@ floatx80 floatx80_etox(floatx80 a, float_status *status) if (aSign) { return packFloatx80(0, 0, 0); } - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { @@ -924,8 +915,7 @@ floatx80 floatx80_twotox(floatx80 a, float_status *status) if (aSign) { return packFloatx80(0, 0, 0); } - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { @@ -1075,8 +1065,7 @@ floatx80 floatx80_tentox(floatx80 a, float_status *status) if (aSign) { return packFloatx80(0, 0, 0); } - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { @@ -2260,8 +2249,7 @@ floatx80 floatx80_atanh(floatx80 a, float_status *status) if (compact >= 0x3FFF8000) { /* |X| >= 1 */ if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */ float_raise(float_flag_divbyzero, status); - return packFloatx80(aSign, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(aSign, status); } else { /* |X| > 1 */ float_raise(float_flag_invalid, status); return floatx80_default_nan(status); @@ -2320,8 +2308,7 @@ floatx80 floatx80_etoxm1(floatx80 a, float_status *status) if (aSign) { return packFloatx80(aSign, one_exp, one_sig); } - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { @@ -2687,8 +2674,7 @@ floatx80 floatx80_sinh(floatx80 a, float_status *status) if ((uint64_t) (aSig << 1)) { return propagateFloatx80NaNOneArg(a, status); } - return packFloatx80(aSign, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(aSign, status); } if (aExp == 0 && aSig == 0) { @@ -2774,8 +2760,7 @@ floatx80 floatx80_cosh(floatx80 a, float_status *status) if ((uint64_t) (aSig << 1)) { return propagateFloatx80NaNOneArg(a, status); } - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 9e224fe7965..97afceb1297 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -20,18 +20,17 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "disas/disas.h" -#include "exec/exec-all.h" +#include "exec/translation-block.h" +#include "exec/target_page.h" #include "tcg/tcg-op.h" #include "qemu/log.h" #include "qemu/qemu-print.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" @@ -70,19 +69,19 @@ void m68k_tcg_init(void) 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 +89,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 */ @@ -264,7 +263,7 @@ static void gen_jmp(DisasContext *s, TCGv dest) static void gen_raise_exception(int nr) { - gen_helper_raise_exception(cpu_env, tcg_constant_i32(nr)); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(nr)); } static void gen_raise_exception_format2(DisasContext *s, int nr, @@ -276,7 +275,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; @@ -520,21 +519,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(); @@ -602,12 +589,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: - gen_helper_flush_flags(cpu_env, tcg_constant_i32(s->cc_op)); + gen_helper_flush_flags(tcg_env, tcg_constant_i32(s->cc_op)); s->cc_op_synced = 1; break; } @@ -734,7 +721,9 @@ 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; @@ -761,20 +750,23 @@ static TCGv gen_lea_mode(CPUM68KState *env, DisasContext *s, switch (reg0) { case 0: /* Absolute short. */ offset = (int16_t)read_im16(env, s); - return tcg_constant_i32(offset); + break; case 1: /* Absolute long. */ offset = read_im32(env, s); - return tcg_constant_i32(offset); + break; case 2: /* pc displacement */ offset = s->pc; offset += (int16_t)read_im16(env, s); - return tcg_constant_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; @@ -824,7 +816,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); @@ -916,14 +908,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; } @@ -954,15 +946,15 @@ static void gen_load_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, case OS_WORD: case OS_LONG: tcg_gen_qemu_ld_tl(tmp, addr, index, opsize | MO_SIGN | MO_TE); - gen_helper_exts32(cpu_env, fp, tmp); + gen_helper_exts32(tcg_env, fp, tmp); break; case OS_SINGLE: tcg_gen_qemu_ld_tl(tmp, addr, index, MO_TEUL); - gen_helper_extf32(cpu_env, fp, tmp); + gen_helper_extf32(tcg_env, fp, tmp); break; case OS_DOUBLE: tcg_gen_qemu_ld_i64(t64, addr, index, MO_TEUQ); - gen_helper_extf64(cpu_env, fp, t64); + gen_helper_extf64(tcg_env, fp, t64); break; case OS_EXTENDED: if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { @@ -1000,15 +992,15 @@ static void gen_store_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, case OS_BYTE: case OS_WORD: case OS_LONG: - gen_helper_reds32(tmp, cpu_env, fp); + 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); + 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); + gen_helper_redf64(t64, tcg_env, fp); tcg_gen_qemu_st_i64(t64, addr, index, MO_TEUQ); break; case OS_EXTENDED: @@ -1060,10 +1052,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(); @@ -1072,18 +1064,13 @@ 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(); @@ -1132,23 +1119,23 @@ static int gen_ea_mode_fp(CPUM68KState *env, DisasContext *s, int mode, switch (opsize) { case OS_BYTE: tmp = tcg_constant_i32((int8_t)read_im8(env, s)); - gen_helper_exts32(cpu_env, fp, tmp); + gen_helper_exts32(tcg_env, fp, tmp); break; case OS_WORD: tmp = tcg_constant_i32((int16_t)read_im16(env, s)); - gen_helper_exts32(cpu_env, fp, tmp); + gen_helper_exts32(tcg_env, fp, tmp); break; case OS_LONG: tmp = tcg_constant_i32(read_im32(env, s)); - gen_helper_exts32(cpu_env, fp, tmp); + gen_helper_exts32(tcg_env, fp, tmp); break; case OS_SINGLE: tmp = tcg_constant_i32(read_im32(env, s)); - gen_helper_extf32(cpu_env, fp, tmp); + gen_helper_extf32(tcg_env, fp, tmp); break; case OS_DOUBLE: t64 = tcg_constant_i64(read_im64(env, s)); - gen_helper_extf64(cpu_env, fp, t64); + gen_helper_extf64(tcg_env, fp, t64); break; case OS_EXTENDED: if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { @@ -1418,6 +1405,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; @@ -1474,7 +1495,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); } @@ -1516,9 +1537,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); @@ -1547,9 +1568,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; @@ -1563,9 +1584,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); @@ -2126,7 +2147,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; } @@ -2153,7 +2174,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); } @@ -2161,11 +2182,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); } @@ -2388,13 +2409,13 @@ DISAS_INSN(cas2w) */ if (tb_cflags(s->base.tb) & CF_PARALLEL) { - gen_helper_exit_atomic(cpu_env); + gen_helper_exit_atomic(tcg_env); } else { TCGv regs = tcg_constant_i32(REG(ext2, 6) | (REG(ext1, 6) << 3) | (REG(ext2, 0) << 6) | (REG(ext1, 0) << 9)); - gen_helper_cas2w(cpu_env, regs, addr1, addr2); + gen_helper_cas2w(tcg_env, regs, addr1, addr2); } /* Note that cas2w also assigned to env->cc_op. */ @@ -2442,9 +2463,9 @@ DISAS_INSN(cas2l) (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); } /* Note that cas2l also assigned to env->cc_op. */ @@ -2630,6 +2651,11 @@ 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 } @@ -2837,7 +2863,7 @@ DISAS_INSN(reset) return; } - gen_helper_reset(cpu_env); + gen_helper_reset(tcg_env); } #endif @@ -3971,11 +3997,11 @@ DISAS_INSN(bfext_mem) } 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); } set_cc_op(s, CC_OP_LOGIC); @@ -4093,21 +4119,21 @@ DISAS_INSN(bfop_mem) 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); 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(); @@ -4208,7 +4234,7 @@ DISAS_INSN(bfins_mem) 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); } @@ -4243,7 +4269,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) @@ -4288,7 +4314,7 @@ DISAS_INSN(chk2) } gen_flush_flags(s); - gen_helper_chk2(cpu_env, reg, bound1, bound2); + gen_helper_chk2(tcg_env, reg, bound1, bound2); } static void m68k_copy_line(TCGv dst, TCGv src, int index) @@ -4462,7 +4488,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])); } @@ -4472,7 +4498,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])); } @@ -4482,8 +4508,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) @@ -4528,7 +4558,7 @@ DISAS_INSN(cf_movec) } else { reg = DREG(ext, 12); } - gen_helper_cf_movec_to(cpu_env, tcg_constant_i32(ext & 0xfff), reg); + gen_helper_cf_movec_to(tcg_env, tcg_constant_i32(ext & 0xfff), reg); gen_exit_tb(s); } @@ -4551,9 +4581,9 @@ DISAS_INSN(m68k_movec) } creg = tcg_constant_i32(ext & 0xfff); if (insn & 1) { - gen_helper_m68k_movec_to(cpu_env, creg, reg); + gen_helper_m68k_movec_to(tcg_env, creg, reg); } else { - gen_helper_m68k_movec_from(reg, cpu_env, creg); + gen_helper_m68k_movec_from(reg, tcg_env, creg); } gen_exit_tb(s); } @@ -4605,7 +4635,7 @@ DISAS_INSN(pflush) } opmode = tcg_constant_i32((insn >> 3) & 3); - gen_helper_pflush(cpu_env, AREG(insn, 0), opmode); + gen_helper_pflush(tcg_env, AREG(insn, 0), opmode); } DISAS_INSN(ptest) @@ -4617,7 +4647,7 @@ DISAS_INSN(ptest) return; } is_read = tcg_constant_i32((insn >> 5) & 1); - gen_helper_ptest(cpu_env, AREG(insn, 0), is_read); + gen_helper_ptest(tcg_env, AREG(insn, 0), is_read); } #endif @@ -4703,10 +4733,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; } } @@ -4717,10 +4747,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; } } @@ -4877,23 +4907,23 @@ 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); } } } @@ -4925,7 +4955,7 @@ DISAS_INSN(fpu) /* fmovecr */ TCGv rom_offset = tcg_constant_i32(opmode); cpu_dest = gen_fp_ptr(REG(ext, 7)); - gen_helper_fconst(cpu_env, cpu_dest, rom_offset); + gen_helper_fconst(tcg_env, cpu_dest, rom_offset); return; } break; @@ -4936,7 +4966,7 @@ DISAS_INSN(fpu) EA_STORE, IS_USER(s)) == -1) { gen_addr_fault(s); } - gen_helper_ftst(cpu_env, cpu_src); + gen_helper_ftst(tcg_env, cpu_src); return; case 4: /* fmove to control register. */ case 5: /* fmove from control register. */ @@ -4970,172 +5000,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); + 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; } - gen_helper_ftst(cpu_env, cpu_dest); + gen_helper_ftst(tcg_env, cpu_dest); return; undef: /* FIXME: Is this right for offset addressing modes? */ @@ -5146,46 +5176,44 @@ DISAS_INSN(fpu) static void gen_fcc_cond(DisasCompare *c, DisasContext *s, int cond) { TCGv fpsr; + int imm = 0; - c->v2 = tcg_constant_i32(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(); - 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(); - 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(); 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(); 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) */ @@ -5193,49 +5221,45 @@ static void gen_fcc_cond(DisasCompare *c, DisasContext *s, int cond) 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(); - 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(); - 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(); - 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(); - 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(); 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(); - 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) */ @@ -5243,27 +5267,25 @@ static void gen_fcc_cond(DisasCompare *c, DisasContext *s, int cond) 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(); - 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(); - 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; } + c->v2 = tcg_constant_i32(imm); } static void gen_fjmpcc(DisasContext *s, int cond, TCGLabel *l1) @@ -5466,12 +5488,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); @@ -5507,11 +5529,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_constant_i32(acc)); + gen_helper_macsatf(tcg_env, tcg_constant_i32(acc)); else if (s->env->macsr & MACSR_SU) - gen_helper_macsats(cpu_env, tcg_constant_i32(acc)); + gen_helper_macsats(tcg_env, tcg_constant_i32(acc)); else - gen_helper_macsatu(cpu_env, tcg_constant_i32(acc)); + gen_helper_macsatu(tcg_env, tcg_constant_i32(acc)); #if 0 /* Disabled because conditional branches clobber temporary vars. */ @@ -5539,18 +5561,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_constant_i32(acc)); + gen_helper_macsatf(tcg_env, tcg_constant_i32(acc)); else if (s->env->macsr & MACSR_SU) - gen_helper_macsats(cpu_env, tcg_constant_i32(acc)); + gen_helper_macsats(tcg_env, tcg_constant_i32(acc)); else - gen_helper_macsatu(cpu_env, tcg_constant_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_constant_i32(acc)); + gen_helper_mac_set_flags(tcg_env, tcg_constant_i32(acc)); if (insn & 0x30) { TCGv rw; @@ -5580,7 +5602,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) { @@ -5601,9 +5623,9 @@ DISAS_INSN(move_mac) TCGv dest; src = insn & 3; dest = tcg_constant_i32((insn >> 9) & 3); - gen_helper_mac_move(cpu_env, dest, tcg_constant_i32(src)); + 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) @@ -5628,9 +5650,9 @@ DISAS_INSN(from_mext) reg = (insn & 8) ? AREG(insn, 0) : DREG(insn, 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) @@ -5639,7 +5661,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); + gen_helper_set_ccr(tcg_env, tmp); set_cc_op(s, CC_OP_FLAGS); } @@ -5661,14 +5683,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_constant_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); } @@ -5686,11 +5708,11 @@ DISAS_INSN(to_mext) SRC_EA(env, val, OS_LONG, 0, NULL); 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]; @@ -5990,7 +6012,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; @@ -6021,7 +6043,7 @@ 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); @@ -6088,24 +6110,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 m68k_translate_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); @@ -6125,8 +6139,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++) { diff --git a/target/meson.build b/target/meson.build index a53a60486fc..b29598e7c5e 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 a5410d9218d..e91d58d88f2 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 9770b0eb525..e0a37945136 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 @@ -17,11 +17,9 @@ * of address space. */ #ifdef CONFIG_USER_ONLY -#define TARGET_LONG_BITS 32 #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 #else -#define TARGET_LONG_BITS 64 #define TARGET_PHYS_ADDR_SPACE_BITS 64 #define TARGET_VIRT_ADDR_SPACE_BITS 64 #endif @@ -29,4 +27,6 @@ /* FIXME: MB uses variable pages down to 1K but linux only uses 4k. */ #define TARGET_PAGE_BITS 12 +#define TARGET_INSN_START_EXTRA_WORDS 1 + #endif diff --git a/target/microblaze/cpu-qom.h b/target/microblaze/cpu-qom.h index cda9220fa99..92e539fb2f9 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_phases: The parent class' reset phase handlers. - * - * A MicroBlaze CPU model. - */ -struct MicroBlazeCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; - - #endif diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 03c2c4db1f9..ee0a869a94a 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -27,9 +27,11 @@ #include "cpu.h" #include "qemu/module.h" #include "hw/qdev-properties.h" -#include "exec/exec-all.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/gdbstub.h" +#include "exec/translation-block.h" #include "fpu/softfloat-helpers.h" +#include "accel/tcg/cpu-ops.h" #include "tcg/tcg.h" static const struct { @@ -93,12 +95,23 @@ static vaddr mb_cpu_get_pc(CPUState *cs) return cpu->env.pc; } +static TCGTBCPUState mb_get_tb_cpu_state(CPUState *cs) +{ + CPUMBState *env = cpu_env(cs); + + return (TCGTBCPUState){ + .pc = env->pc, + .flags = (env->iflags & IFLAGS_TB_MASK) | (env->msr & MSR_TB_MASK), + .cs_base = (env->iflags & IMM_FLAG ? env->imm : 0), + }; +} + static void mb_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); cpu->env.pc = tb->pc; cpu->env.iflags = tb->flags & IFLAGS_TB_MASK; } @@ -113,10 +126,28 @@ static void mb_restore_state_to_opc(CPUState *cs, cpu->env.iflags = data[1]; } +#ifndef CONFIG_USER_ONLY static bool mb_cpu_has_work(CPUState *cs) { return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } +#endif /* !CONFIG_USER_ONLY */ + +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) @@ -165,15 +196,15 @@ static void microblaze_cpu_set_irq(void *opaque, int irq, int level) } #endif -static void mb_cpu_reset_hold(Object *obj) +static void mb_cpu_reset_hold(Object *obj, ResetType type) { - CPUState *s = CPU(obj); - 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; if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUMBState, end_reset_fields)); @@ -184,6 +215,15 @@ static void mb_cpu_reset_hold(Object *obj) 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); + /* Default NaN: sign bit set, most significant frac bit set */ + set_float_default_nan_pattern(0b11000000, &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); @@ -197,6 +237,8 @@ static void mb_disas_set_info(CPUState *cpu, disassemble_info *info) { info->mach = bfd_arch_microblaze; info->print_insn = print_insn_microblaze; + info->endian = TARGET_BIG_ENDIAN ? BFD_ENDIAN_BIG + : BFD_ENDIAN_LITTLE; } static void mb_cpu_realizefn(DeviceState *dev, Error **errp) @@ -221,6 +263,11 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) return; } + gdb_register_coprocessor(cs, mb_cpu_gdb_read_stack_protect, + mb_cpu_gdb_write_stack_protect, + gdb_find_static_feature("microblaze-stack-protect.xml"), + 0); + qemu_init_vcpu(cs); version = cpu->cfg.version ? cpu->cfg.version : DEFAULT_CPU_VERSION; @@ -293,27 +340,24 @@ 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); - gdb_register_coprocessor(CPU(cpu), mb_cpu_gdb_read_stack_protect, - mb_cpu_gdb_write_stack_protect, 2, - "microblaze-stack-protect.xml", 0); - - set_float_rounding_mode(float_round_nearest_even, &env->fp_status); - #ifndef CONFIG_USER_ONLY /* Inbound IRQ and FIR lines */ - qdev_init_gpio_in(DEVICE(cpu), microblaze_cpu_set_irq, 2); - qdev_init_gpio_in_named(DEVICE(cpu), mb_cpu_ns_axi_dp, "ns_axi_dp", 1); - qdev_init_gpio_in_named(DEVICE(cpu), mb_cpu_ns_axi_ip, "ns_axi_ip", 1); - 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); + qdev_init_gpio_in(DEVICE(obj), microblaze_cpu_set_irq, 2); + qdev_init_gpio_in_named(DEVICE(obj), mb_cpu_ns_axi_dp, "ns_axi_dp", 1); + qdev_init_gpio_in_named(DEVICE(obj), mb_cpu_ns_axi_ip, "ns_axi_ip", 1); + qdev_init_gpio_in_named(DEVICE(obj), mb_cpu_ns_axi_dc, "ns_axi_dc", 1); + qdev_init_gpio_in_named(DEVICE(obj), 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[] = { +static const 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), @@ -370,7 +414,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), - DEFINE_PROP_END_OF_LIST(), + /* + * End of properties reserved by Xilinx DTS conversion tool. + */ }; static ObjectClass *mb_cpu_class_by_name(const char *cpu_model) @@ -382,27 +428,36 @@ static ObjectClass *mb_cpu_class_by_name(const char *cpu_model) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps mb_sysemu_ops = { + .has_work = mb_cpu_has_work, .get_phys_page_attrs_debug = mb_cpu_get_phys_page_attrs_debug, }; #endif -#include "hw/core/tcg-cpu-ops.h" +static const TCGCPUOps mb_tcg_ops = { + /* MicroBlaze is always in-order. */ + .guest_default_memory_order = TCG_MO_ALL, + .mttcg_supported = true, -static const struct TCGCPUOps mb_tcg_ops = { .initialize = mb_tcg_init, + .translate_code = mb_translate_code, + .get_tb_cpu_state = mb_get_tb_cpu_state, .synchronize_from_tb = mb_cpu_synchronize_from_tb, .restore_state_to_opc = mb_restore_state_to_opc, + .mmu_index = mb_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill = mb_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_uint32, .cpu_exec_interrupt = mb_cpu_exec_interrupt, + .cpu_exec_halt = mb_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = mb_cpu_do_interrupt, .do_transaction_failed = mb_cpu_transaction_failed, .do_unaligned_access = mb_cpu_do_unaligned_access, #endif /* !CONFIG_USER_ONLY */ }; -static void mb_cpu_class_init(ObjectClass *oc, void *data) +static void mb_cpu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); @@ -415,8 +470,6 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data) &mcc->parent_phases); cc->class_by_name = mb_cpu_class_by_name; - cc->has_work = mb_cpu_has_work; - cc->dump_state = mb_cpu_dump_state; cc->set_pc = mb_cpu_set_pc; cc->get_pc = mb_cpu_get_pc; @@ -428,7 +481,6 @@ 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 + 25; cc->gdb_core_xml_file = "microblaze-core.xml"; cc->disas_set_info = mb_disas_set_info; @@ -439,6 +491,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 f6cab6ce191..3ce28b302fe 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -21,11 +21,10 @@ #define MICROBLAZE_CPU_H #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" - -/* MicroBlaze is always in-order. */ -#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL +#include "exec/cpu-interrupt.h" typedef struct CPUArchState CPUMBState; #if !defined(CONFIG_USER_ONLY) @@ -234,8 +233,6 @@ typedef struct CPUArchState CPUMBState; #define STREAM_CONTROL (1 << 3) #define STREAM_NONBLOCK (1 << 4) -#define TARGET_INSN_START_EXTRA_WORDS 1 - /* use-non-secure property masks */ #define USE_NON_SECURE_M_AXI_DP_MASK 0x1 #define USE_NON_SECURE_M_AXI_IP_MASK 0x2 @@ -251,7 +248,7 @@ struct CPUArchState { uint32_t pc; uint32_t msr; /* All bits of MSR except MSR[C] and MSR[CC] */ uint32_t msr_c; /* MSR[C], in low bit; other bits must be 0 */ - target_ulong ear; + uint64_t ear; uint32_t esr; uint32_t fsr; uint32_t btr; @@ -343,20 +340,31 @@ 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); @@ -370,8 +378,8 @@ G_NORETURN void mb_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, void mb_cpu_dump_state(CPUState *cpu, FILE *f, int flags); 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(CPUArchState *cpu, GByteArray *buf, int reg); -int mb_cpu_gdb_write_stack_protect(CPUArchState *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) { @@ -390,6 +398,8 @@ static inline void mb_cpu_write_msr(CPUMBState *env, uint32_t val) } void mb_tcg_init(void); +void mb_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); #define CPU_RESOLVING_TYPE TYPE_MICROBLAZE_CPU @@ -399,17 +409,14 @@ void mb_tcg_init(void); #define MMU_USER_IDX 2 /* 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, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) +static inline bool mb_cpu_is_big_endian(CPUState *cs) { - *pc = env->pc; - *flags = (env->iflags & IFLAGS_TB_MASK) | (env->msr & MSR_TB_MASK); - *cs_base = (*flags & IMM_FLAG ? env->imm : 0); + MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + + return !cpu->cfg.endi; } #if !defined(CONFIG_USER_ONLY) @@ -423,21 +430,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 29ac6e9c0f7..d493681d38d 100644 --- a/target/microblaze/gdbstub.c +++ b/target/microblaze/gdbstub.c @@ -49,14 +49,9 @@ enum { 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]; @@ -94,8 +89,10 @@ int mb_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) return gdb_get_reg32(mem_buf, val); } -int mb_cpu_gdb_read_stack_protect(CPUMBState *env, GByteArray *mem_buf, int n) +int mb_cpu_gdb_read_stack_protect(CPUState *cs, GByteArray *mem_buf, int n) { + MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + CPUMBState *env = &cpu->env; uint32_t val; switch (n) { @@ -113,15 +110,9 @@ int mb_cpu_gdb_read_stack_protect(CPUMBState *env, GByteArray *mem_buf, int n) int mb_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { - MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - CPUClass *cc = CPU_GET_CLASS(cs); - CPUMBState *env = &cpu->env; + CPUMBState *env = cpu_env(cs); uint32_t tmp; - if (n > cc->gdb_num_core_regs) { - return 0; - } - tmp = ldl_p(mem_buf); switch (n) { @@ -153,8 +144,11 @@ int mb_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 4; } -int mb_cpu_gdb_write_stack_protect(CPUMBState *env, uint8_t *mem_buf, int n) +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); diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index 98bdb82de87..ef0e2f973fa 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -20,11 +20,57 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "accel/tcg/cpu-mmu-index.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" #include "qemu/host-utils.h" #include "exec/log.h" +#include "exec/helper-proto.h" + + +G_NORETURN +static void mb_unaligned_access_internal(CPUState *cs, uint64_t addr, + uintptr_t retaddr) +{ + CPUMBState *env = cpu_env(cs); + uint32_t esr, iflags; + + /* Recover the pc and iflags from the corresponding insn_start. */ + cpu_restore_state(cs, retaddr); + iflags = env->iflags; + + qemu_log_mask(CPU_LOG_INT, + "Unaligned access addr=0x%" PRIx64 " pc=%x iflags=%x\n", + addr, env->pc, iflags); + + esr = ESR_EC_UNALIGNED_DATA; + if (likely(iflags & ESR_ESS_FLAG)) { + esr |= iflags & ESR_ESS_MASK; + } else { + qemu_log_mask(LOG_UNIMP, "Unaligned access without ESR_ESS_FLAG\n"); + } + + env->ear = addr; + env->esr = esr; + cs->exception_index = EXCP_HW_EXCP; + cpu_loop_exit(cs); +} + +void mb_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr) +{ + mb_unaligned_access_internal(cs, addr, retaddr); +} #ifndef CONFIG_USER_ONLY + +void HELPER(unaligned_access)(CPUMBState *env, uint64_t addr) +{ + mb_unaligned_access_internal(env_cpu(env), addr, GETPC()); +} + static bool mb_cpu_access_is_secure(MicroBlazeCPU *cpu, MMUAccessType access_type) { @@ -51,7 +97,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 +274,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 +298,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) @@ -268,31 +312,3 @@ bool mb_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } #endif /* !CONFIG_USER_ONLY */ - -void mb_cpu_do_unaligned_access(CPUState *cs, vaddr addr, - MMUAccessType access_type, - int mmu_idx, uintptr_t retaddr) -{ - MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - uint32_t esr, iflags; - - /* Recover the pc and iflags from the corresponding insn_start. */ - cpu_restore_state(cs, retaddr); - iflags = cpu->env.iflags; - - qemu_log_mask(CPU_LOG_INT, - "Unaligned access addr=" TARGET_FMT_lx " pc=%x iflags=%x\n", - (target_ulong)addr, cpu->env.pc, iflags); - - esr = ESR_EC_UNALIGNED_DATA; - if (likely(iflags & ESR_ESS_FLAG)) { - esr |= iflags & ESR_ESS_MASK; - } else { - qemu_log_mask(LOG_UNIMP, "Unaligned access without ESR_ESS_FLAG\n"); - } - - cpu->env.ear = addr; - cpu->env.esr = esr; - cs->exception_index = EXCP_HW_EXCP; - cpu_loop_exit(cs); -} diff --git a/target/microblaze/helper.h b/target/microblaze/helper.h index f740835fcb0..ef4fad9b91e 100644 --- a/target/microblaze/helper.h +++ b/target/microblaze/helper.h @@ -20,12 +20,22 @@ DEF_HELPER_FLAGS_3(fcmp_ne, TCG_CALL_NO_WG, i32, env, i32, i32) DEF_HELPER_FLAGS_3(fcmp_ge, TCG_CALL_NO_WG, i32, env, i32, i32) DEF_HELPER_FLAGS_2(pcmpbf, TCG_CALL_NO_RWG_SE, i32, i32, i32) -#if !defined(CONFIG_USER_ONLY) -DEF_HELPER_FLAGS_3(mmu_read, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_4(mmu_write, TCG_CALL_NO_RWG, void, env, i32, i32, i32) -#endif - DEF_HELPER_FLAGS_2(stackprot, TCG_CALL_NO_WG, void, env, tl) - DEF_HELPER_FLAGS_2(get, TCG_CALL_NO_RWG, i32, i32, i32) DEF_HELPER_FLAGS_3(put, TCG_CALL_NO_RWG, void, i32, i32, i32) + +#ifndef CONFIG_USER_ONLY +DEF_HELPER_FLAGS_3(mmu_read, TCG_CALL_NO_RWG, i32, env, i32, i32) +DEF_HELPER_FLAGS_4(mmu_write, TCG_CALL_NO_RWG, void, env, i32, i32, i32) +DEF_HELPER_FLAGS_2(unaligned_access, TCG_CALL_NO_WG, noreturn, env, i64) +DEF_HELPER_FLAGS_2(lbuea, TCG_CALL_NO_WG, i32, env, i64) +DEF_HELPER_FLAGS_2(lhuea_be, TCG_CALL_NO_WG, i32, env, i64) +DEF_HELPER_FLAGS_2(lhuea_le, TCG_CALL_NO_WG, i32, env, i64) +DEF_HELPER_FLAGS_2(lwea_be, TCG_CALL_NO_WG, i32, env, i64) +DEF_HELPER_FLAGS_2(lwea_le, TCG_CALL_NO_WG, i32, env, i64) +DEF_HELPER_FLAGS_3(sbea, TCG_CALL_NO_WG, void, env, i32, i64) +DEF_HELPER_FLAGS_3(shea_be, TCG_CALL_NO_WG, void, env, i32, i64) +DEF_HELPER_FLAGS_3(shea_le, TCG_CALL_NO_WG, void, env, i32, i64) +DEF_HELPER_FLAGS_3(swea_be, TCG_CALL_NO_WG, void, env, i32, i64) +DEF_HELPER_FLAGS_3(swea_le, TCG_CALL_NO_WG, void, env, i32, i64) +#endif diff --git a/target/microblaze/machine.c b/target/microblaze/machine.c index d24def3992e..a4cf38dc891 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,8 +92,8 @@ static const VMStateDescription vmstate_env = { .fields = vmstate_env_fields, }; -static VMStateField vmstate_cpu_fields[] = { - VMSTATE_CPU(), +static const VMStateField vmstate_cpu_fields[] = { + VMSTATE_STRUCT(parent_obj, MicroBlazeCPU, 0, vmstate_cpu_common, CPUState), 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 50fd9ff378d..3ed4fbb67a7 100644 --- a/target/microblaze/meson.build +++ b/target/microblaze/meson.build @@ -17,4 +17,4 @@ microblaze_system_ss.add(files( )) target_arch += {'microblaze': microblaze_ss} -target_softmmu_arch += {'microblaze': microblaze_system_ss} +target_system_arch += {'microblaze': microblaze_system_ss} diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 75651979a99..8703ff5c657 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -21,7 +21,10 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "accel/tcg/cpu-mmu-index.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" static unsigned int tlb_decode_size(unsigned int f) { @@ -169,7 +172,8 @@ unsigned int mmu_translate(MicroBlazeCPU *cpu, MicroBlazeMMULookup *lu, } done: qemu_log_mask(CPU_LOG_MMU, - "MMU vaddr=%" PRIx64 " rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", + "MMU vaddr=0x" TARGET_FMT_lx + " rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", vaddr, rw, tlb_wr, tlb_ex, hit); return hit; } @@ -305,7 +309,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 f6378030b7a..b8365b3b1d2 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -23,8 +23,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "fpu/softfloat.h" void helper_put(uint32_t id, uint32_t ctrl, uint32_t data) @@ -383,6 +382,8 @@ void helper_stackprot(CPUMBState *env, target_ulong addr) } #if !defined(CONFIG_USER_ONLY) +#include "system/memory.h" + /* Writes/reads to the MMU's special regs end up here. */ uint32_t helper_mmu_read(CPUMBState *env, uint32_t ext, uint32_t rn) { @@ -394,38 +395,90 @@ void helper_mmu_write(CPUMBState *env, uint32_t ext, uint32_t rn, uint32_t v) mmu_write(env, ext, rn, v); } +static void mb_transaction_failed_internal(CPUState *cs, hwaddr physaddr, + uint64_t addr, unsigned size, + MMUAccessType access_type, + uintptr_t retaddr) +{ + CPUMBState *env = cpu_env(cs); + MicroBlazeCPU *cpu = env_archcpu(env); + const char *access_name = "INVALID"; + bool take = env->msr & MSR_EE; + uint32_t esr = ESR_EC_DATA_BUS; + + switch (access_type) { + case MMU_INST_FETCH: + access_name = "INST_FETCH"; + esr = ESR_EC_INSN_BUS; + take &= cpu->cfg.iopb_bus_exception; + break; + case MMU_DATA_LOAD: + access_name = "DATA_LOAD"; + take &= cpu->cfg.dopb_bus_exception; + break; + case MMU_DATA_STORE: + access_name = "DATA_STORE"; + take &= cpu->cfg.dopb_bus_exception; + break; + } + + qemu_log_mask(CPU_LOG_INT, "Transaction failed: addr 0x%" PRIx64 + "physaddr 0x" HWADDR_FMT_plx " size %d access-type %s (%s)\n", + addr, physaddr, size, access_name, + take ? "TAKEN" : "DROPPED"); + + if (take) { + env->esr = esr; + env->ear = addr; + cs->exception_index = EXCP_HW_EXCP; + cpu_loop_exit_restore(cs, retaddr); + } +} + void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, unsigned size, MMUAccessType access_type, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr) { - MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - CPUMBState *env = &cpu->env; + mb_transaction_failed_internal(cs, physaddr, addr, size, + access_type, retaddr); +} - qemu_log_mask(CPU_LOG_INT, "Transaction failed: vaddr 0x%" VADDR_PRIx - " 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")); +#define LD_EA(NAME, TYPE, FUNC) \ +uint32_t HELPER(NAME)(CPUMBState *env, uint64_t ea) \ +{ \ + CPUState *cs = env_cpu(env); \ + MemTxResult txres; \ + TYPE ret = FUNC(cs->as, ea, MEMTXATTRS_UNSPECIFIED, &txres); \ + if (unlikely(txres != MEMTX_OK)) { \ + mb_transaction_failed_internal(cs, ea, ea, sizeof(TYPE), \ + MMU_DATA_LOAD, GETPC()); \ + } \ + return ret; \ +} - if (!(env->msr & MSR_EE)) { - return; - } +LD_EA(lbuea, uint8_t, address_space_ldub) +LD_EA(lhuea_be, uint16_t, address_space_lduw_be) +LD_EA(lhuea_le, uint16_t, address_space_lduw_le) +LD_EA(lwea_be, uint32_t, address_space_ldl_be) +LD_EA(lwea_le, uint32_t, address_space_ldl_le) + +#define ST_EA(NAME, TYPE, FUNC) \ +void HELPER(NAME)(CPUMBState *env, uint32_t data, uint64_t ea) \ +{ \ + CPUState *cs = env_cpu(env); \ + MemTxResult txres; \ + FUNC(cs->as, ea, data, MEMTXATTRS_UNSPECIFIED, &txres); \ + if (unlikely(txres != MEMTX_OK)) { \ + mb_transaction_failed_internal(cs, ea, ea, sizeof(TYPE), \ + MMU_DATA_STORE, GETPC()); \ + } \ +} - if (access_type == MMU_INST_FETCH) { - if (!cpu->cfg.iopb_bus_exception) { - return; - } - env->esr = ESR_EC_INSN_BUS; - } else { - if (!cpu->cfg.dopb_bus_exception) { - return; - } - env->esr = ESR_EC_DATA_BUS; - } +ST_EA(sbea, uint8_t, address_space_stb) +ST_EA(shea_be, uint16_t, address_space_stw_be) +ST_EA(shea_le, uint16_t, address_space_stw_le) +ST_EA(swea_be, uint32_t, address_space_stl_be) +ST_EA(swea_le, uint32_t, address_space_stl_le) - env->ear = addr; - cs->exception_index = EXCP_HW_EXCP; - cpu_loop_exit_restore(cs, retaddr); -} #endif diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index d02c16296a4..5098a1db4dc 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -20,12 +20,13 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "disas/disas.h" -#include "exec/exec-all.h" +#include "accel/tcg/cpu-ldst.h" #include "tcg/tcg-op.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" +#include "exec/translation-block.h" +#include "exec/target_page.h" #include "qemu/qemu-print.h" #include "exec/log.h" @@ -62,12 +63,6 @@ typedef struct DisasContext { DisasContextBase base; const MicroBlazeCPUConfig *cfg; - /* TCG op of the current insn_start. */ - TCGOp *insn_start; - - TCGv_i32 r0; - bool r0_set; - /* Decoder. */ uint32_t ext_imm; unsigned int tb_flags; @@ -102,7 +97,7 @@ static void t_sync_flags(DisasContext *dc) static void gen_raise_exception(DisasContext *dc, uint32_t index) { - gen_helper_raise_exception(cpu_env, tcg_constant_i32(index)); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(index)); dc->base.is_jmp = DISAS_NORETURN; } @@ -116,7 +111,7 @@ 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_constant_i32(esr_ec); - tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUMBState, esr)); + tcg_gen_st_i32(tmp, tcg_env, offsetof(CPUMBState, esr)); gen_raise_exception_sync(dc, EXCP_HW_EXCP); } @@ -181,14 +176,7 @@ static TCGv_i32 reg_for_read(DisasContext *dc, int reg) if (likely(reg != 0)) { return cpu_R[reg]; } - if (!dc->r0_set) { - if (dc->r0 == NULL) { - dc->r0 = tcg_temp_new_i32(); - } - tcg_gen_movi_i32(dc->r0, 0); - dc->r0_set = true; - } - return dc->r0; + return tcg_constant_i32(0); } static TCGv_i32 reg_for_write(DisasContext *dc, int reg) @@ -196,10 +184,7 @@ static TCGv_i32 reg_for_write(DisasContext *dc, int reg) if (likely(reg != 0)) { return cpu_R[reg]; } - if (dc->r0 == NULL) { - dc->r0 = tcg_temp_new_i32(); - } - return dc->r0; + return tcg_temp_new_i32(); } static bool do_typea(DisasContext *dc, arg_typea *arg, bool side_effects, @@ -295,11 +280,11 @@ 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) @@ -312,11 +297,7 @@ static void gen_add(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) /* Input and output carry. */ static void gen_addc(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - 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_gen_addcio_i32(out, cpu_msr_c, ina, inb, cpu_msr_c); } /* Input carry, but no output carry. */ @@ -472,12 +453,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) @@ -545,12 +526,10 @@ 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_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_gen_addcio_i32(out, cpu_msr_c, tmp, inb, cpu_msr_c); } /* No input or output carry. */ @@ -627,64 +606,64 @@ DO_TYPEBI(xori, false, tcg_gen_xori_i32) static TCGv compute_ldst_addr_typea(DisasContext *dc, int ra, int rb) { - TCGv ret = tcg_temp_new(); + TCGv ret; /* If any of the regs is r0, set t to the value of the other reg. */ if (ra && 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); + ret = tcg_temp_new_i32(); + tcg_gen_add_i32(ret, cpu_R[ra], cpu_R[rb]); } else if (ra) { - tcg_gen_extu_i32_tl(ret, cpu_R[ra]); + ret = cpu_R[ra]; } else if (rb) { - tcg_gen_extu_i32_tl(ret, cpu_R[rb]); + ret = cpu_R[rb]; } else { - tcg_gen_movi_tl(ret, 0); + ret = tcg_constant_i32(0); } if ((ra == 1 || rb == 1) && dc->cfg->stackprot) { - gen_helper_stackprot(cpu_env, ret); + gen_helper_stackprot(tcg_env, ret); } return ret; } static TCGv compute_ldst_addr_typeb(DisasContext *dc, int ra, int imm) { - TCGv ret = tcg_temp_new(); + TCGv ret; /* If any of the regs is r0, set t to the value of the other reg. */ - if (ra) { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_addi_i32(tmp, cpu_R[ra], imm); - tcg_gen_extu_i32_tl(ret, tmp); + if (ra && imm) { + ret = tcg_temp_new_i32(); + tcg_gen_addi_i32(ret, cpu_R[ra], imm); + } else if (ra) { + ret = cpu_R[ra]; } else { - tcg_gen_movi_tl(ret, (uint32_t)imm); + ret = tcg_constant_i32(imm); } if (ra == 1 && dc->cfg->stackprot) { - gen_helper_stackprot(cpu_env, ret); + gen_helper_stackprot(tcg_env, ret); } return ret; } #ifndef CONFIG_USER_ONLY -static TCGv compute_ldst_addr_ea(DisasContext *dc, int ra, int rb) +static TCGv_i64 compute_ldst_addr_ea(DisasContext *dc, int ra, int rb) { int addr_size = dc->cfg->addr_size; - TCGv ret = tcg_temp_new(); + TCGv_i64 ret = tcg_temp_new_i64(); if (addr_size == 32 || ra == 0) { if (rb) { - tcg_gen_extu_i32_tl(ret, cpu_R[rb]); + tcg_gen_extu_i32_i64(ret, cpu_R[rb]); } else { - tcg_gen_movi_tl(ret, 0); + return tcg_constant_i64(0); } } else { if (rb) { tcg_gen_concat_i32_i64(ret, cpu_R[rb], cpu_R[ra]); } else { - tcg_gen_extu_i32_tl(ret, cpu_R[ra]); - tcg_gen_shli_tl(ret, ret, 32); + tcg_gen_extu_i32_i64(ret, cpu_R[ra]); + tcg_gen_shli_i64(ret, ret, 32); } if (addr_size < 64) { /* Mask off out of range bits. */ @@ -699,22 +678,43 @@ 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); +} + +static void gen_alignment_check_ea(DisasContext *dc, TCGv_i64 ea, int rb, + int rd, MemOp size, bool store) +{ + if (rb && (dc->tb_flags & MSR_EE) && dc->cfg->unaligned_exceptions) { + TCGLabel *over = gen_new_label(); + + record_unaligned_ess(dc, rd, size, store); + + tcg_gen_brcondi_i64(TCG_COND_TSTEQ, ea, (1 << size) - 1, over); + gen_helper_unaligned_access(tcg_env, ea); + gen_set_label(over); + } } #endif +static inline MemOp mo_endian(DisasContext *dc) +{ + return dc->cfg->endi ? MO_LE : MO_BE; +} + static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop, int mem_index, bool rev) { MemOp size = mop & MO_SIZE; + mop |= mo_endian(dc); + /* * When doing reverse accesses we need to do two things. * @@ -766,10 +766,11 @@ static bool trans_lbuea(DisasContext *dc, arg_typea *arg) return true; } #ifdef CONFIG_USER_ONLY - return true; + g_assert_not_reached(); #else - TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_UB, MMU_NOMMU_IDX, false); + TCGv_i64 addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + gen_helper_lbuea(reg_for_write(dc, arg->rd), tcg_env, addr); + return true; #endif } @@ -782,13 +783,13 @@ static bool trans_lbui(DisasContext *dc, arg_typeb *arg) static bool trans_lhu(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); + return do_load(dc, arg->rd, addr, MO_UW, dc->mem_index, false); } static bool trans_lhur(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TEUW, dc->mem_index, true); + return do_load(dc, arg->rd, addr, MO_UW, dc->mem_index, true); } static bool trans_lhuea(DisasContext *dc, arg_typea *arg) @@ -797,29 +798,32 @@ static bool trans_lhuea(DisasContext *dc, arg_typea *arg) return true; } #ifdef CONFIG_USER_ONLY - return true; + g_assert_not_reached(); #else - TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TEUW, MMU_NOMMU_IDX, false); + TCGv_i64 addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + gen_alignment_check_ea(dc, addr, arg->rb, arg->rd, MO_16, false); + (mo_endian(dc) == MO_BE ? gen_helper_lhuea_be : gen_helper_lhuea_le) + (reg_for_write(dc, arg->rd), tcg_env, addr); + return true; #endif } static bool trans_lhui(DisasContext *dc, arg_typeb *arg) { TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); - return do_load(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); + return do_load(dc, arg->rd, addr, MO_UW, dc->mem_index, false); } static bool trans_lw(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); + return do_load(dc, arg->rd, addr, MO_UL, dc->mem_index, false); } static bool trans_lwr(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TEUL, dc->mem_index, true); + return do_load(dc, arg->rd, addr, MO_UL, dc->mem_index, true); } static bool trans_lwea(DisasContext *dc, arg_typea *arg) @@ -828,17 +832,20 @@ static bool trans_lwea(DisasContext *dc, arg_typea *arg) return true; } #ifdef CONFIG_USER_ONLY - return true; + g_assert_not_reached(); #else - TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_TEUL, MMU_NOMMU_IDX, false); + TCGv_i64 addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + gen_alignment_check_ea(dc, addr, arg->rb, arg->rd, MO_32, false); + (mo_endian(dc) == MO_BE ? gen_helper_lwea_be : gen_helper_lwea_le) + (reg_for_write(dc, arg->rd), tcg_env, addr); + return true; #endif } static bool trans_lwi(DisasContext *dc, arg_typeb *arg) { TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); - return do_load(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); + return do_load(dc, arg->rd, addr, MO_UL, dc->mem_index, false); } static bool trans_lwx(DisasContext *dc, arg_typea *arg) @@ -848,7 +855,8 @@ static bool trans_lwx(DisasContext *dc, arg_typea *arg) /* lwx does not throw unaligned access errors, so force alignment */ tcg_gen_andi_tl(addr, addr, ~3); - tcg_gen_qemu_ld_i32(cpu_res_val, addr, dc->mem_index, MO_TEUL); + tcg_gen_qemu_ld_i32(cpu_res_val, addr, dc->mem_index, + mo_endian(dc) | MO_UL); tcg_gen_mov_tl(cpu_res_addr, addr); if (arg->rd) { @@ -865,6 +873,8 @@ static bool do_store(DisasContext *dc, int rd, TCGv addr, MemOp mop, { MemOp size = mop & MO_SIZE; + mop |= mo_endian(dc); + /* * When doing reverse accesses we need to do two things. * @@ -916,10 +926,11 @@ static bool trans_sbea(DisasContext *dc, arg_typea *arg) return true; } #ifdef CONFIG_USER_ONLY - return true; + g_assert_not_reached(); #else - TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_UB, MMU_NOMMU_IDX, false); + TCGv_i64 addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + gen_helper_sbea(tcg_env, reg_for_read(dc, arg->rd), addr); + return true; #endif } @@ -932,13 +943,13 @@ static bool trans_sbi(DisasContext *dc, arg_typeb *arg) static bool trans_sh(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); + return do_store(dc, arg->rd, addr, MO_UW, dc->mem_index, false); } static bool trans_shr(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TEUW, dc->mem_index, true); + return do_store(dc, arg->rd, addr, MO_UW, dc->mem_index, true); } static bool trans_shea(DisasContext *dc, arg_typea *arg) @@ -947,29 +958,32 @@ static bool trans_shea(DisasContext *dc, arg_typea *arg) return true; } #ifdef CONFIG_USER_ONLY - return true; + g_assert_not_reached(); #else - TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TEUW, MMU_NOMMU_IDX, false); + TCGv_i64 addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + gen_alignment_check_ea(dc, addr, arg->rb, arg->rd, MO_16, true); + (mo_endian(dc) == MO_BE ? gen_helper_shea_be : gen_helper_shea_le) + (tcg_env, reg_for_read(dc, arg->rd), addr); + return true; #endif } static bool trans_shi(DisasContext *dc, arg_typeb *arg) { TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); - return do_store(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); + return do_store(dc, arg->rd, addr, MO_UW, dc->mem_index, false); } static bool trans_sw(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); + return do_store(dc, arg->rd, addr, MO_UL, dc->mem_index, false); } static bool trans_swr(DisasContext *dc, arg_typea *arg) { TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TEUL, dc->mem_index, true); + return do_store(dc, arg->rd, addr, MO_UL, dc->mem_index, true); } static bool trans_swea(DisasContext *dc, arg_typea *arg) @@ -978,17 +992,20 @@ static bool trans_swea(DisasContext *dc, arg_typea *arg) return true; } #ifdef CONFIG_USER_ONLY - return true; + g_assert_not_reached(); #else - TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_TEUL, MMU_NOMMU_IDX, false); + TCGv_i64 addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + gen_alignment_check_ea(dc, addr, arg->rb, arg->rd, MO_32, true); + (mo_endian(dc) == MO_BE ? gen_helper_swea_be : gen_helper_swea_le) + (tcg_env, reg_for_read(dc, arg->rd), addr); + return true; #endif } static bool trans_swi(DisasContext *dc, arg_typeb *arg) { TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); - return do_store(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); + return do_store(dc, arg->rd, addr, MO_UL, dc->mem_index, false); } static bool trans_swx(DisasContext *dc, arg_typea *arg) @@ -1017,7 +1034,7 @@ static bool trans_swx(DisasContext *dc, arg_typea *arg) tcg_gen_atomic_cmpxchg_i32(tval, cpu_res_addr, cpu_res_val, reg_for_write(dc, arg->rd), - dc->mem_index, MO_TEUL); + dc->mem_index, mo_endian(dc) | MO_UL); tcg_gen_brcond_i32(TCG_COND_NE, cpu_res_val, tval, swx_fail); @@ -1232,7 +1249,7 @@ static bool trans_mbar(DisasContext *dc, arg_mbar *arg) t_sync_flags(dc); - tcg_gen_st_i32(tcg_constant_i32(1), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(1), tcg_env, -offsetof(MicroBlazeCPU, env) +offsetof(CPUState, halted)); @@ -1381,13 +1398,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 */ @@ -1400,7 +1417,7 @@ static bool trans_mts(DisasContext *dc, arg_mts *arg) 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); + gen_helper_mmu_write(tcg_env, tmp_ext, tmp_reg, src); } break; @@ -1422,7 +1439,7 @@ 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); } return true; @@ -1452,27 +1469,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); } 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 @@ -1486,13 +1503,13 @@ static bool trans_mfs(DisasContext *dc, arg_mfs *arg) 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); + 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; @@ -1605,9 +1622,7 @@ static void mb_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs) dc->cfg = &cpu->cfg; dc->tb_flags = dc->base.tb->flags; 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; @@ -1624,13 +1639,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. */ @@ -1641,16 +1654,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_swap(cpu_env(cs), &dc->base, dc->base.pc_next, + mb_cpu_is_big_endian(cs) != TARGET_BIG_ENDIAN); if (!decode(dc, ir)) { trap_illegal(dc, true); } - if (dc->r0) { - dc->r0 = NULL; - dc->r0_set = false; - } - /* Discard the imm global when its contents cannot be used. */ if ((dc->tb_flags & ~dc->tb_flags_to_set) & IMM_FLAG) { tcg_gen_discard_i32(cpu_imm); @@ -1775,24 +1784,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 mb_translate_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); @@ -1800,8 +1801,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; @@ -1837,7 +1837,7 @@ void mb_cpu_dump_state(CPUState *cs, FILE *f, int flags) } qemu_fprintf(f, "\nesr=0x%04x fsr=0x%02x btr=0x%08x edr=0x%x\n" - "ear=0x" TARGET_FMT_lx " slr=0x%x shr=0x%x\n", + "ear=0x%" PRIx64 " slr=0x%x shr=0x%x\n", env->esr, env->fsr, env->btr, env->edr, env->ear, env->slr, env->shr); @@ -1882,9 +1882,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 6adf1453548..876048b150c 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 c0c389c59a1..922fc39138d 100644 --- a/target/mips/cpu-defs.c.inc +++ b/target/mips/cpu-defs.c.inc @@ -314,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, }, { @@ -478,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, @@ -499,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, @@ -541,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) @@ -661,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, }, { @@ -690,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, }, { @@ -1018,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; diff --git a/target/mips/cpu-param.h b/target/mips/cpu-param.h index 594c91a1562..58f450827f7 100644 --- a/target/mips/cpu-param.h +++ b/target/mips/cpu-param.h @@ -1,17 +1,12 @@ /* * MIPS cpu parameters for qemu. * - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef MIPS_CPU_PARAM_H #define MIPS_CPU_PARAM_H -#ifdef TARGET_MIPS64 -# define TARGET_LONG_BITS 64 -#else -# define TARGET_LONG_BITS 32 -#endif #ifdef TARGET_ABI_MIPSN64 #define TARGET_PHYS_ADDR_SPACE_BITS 48 #define TARGET_VIRT_ADDR_SPACE_BITS 48 @@ -23,11 +18,8 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif #endif -#ifdef CONFIG_USER_ONLY #define TARGET_PAGE_BITS 12 -#else -#define TARGET_PAGE_BITS_VARY -#define TARGET_PAGE_BITS_MIN 12 -#endif + +#define TARGET_INSN_START_EXTRA_WORDS 2 #endif diff --git a/target/mips/cpu-qom.h b/target/mips/cpu-qom.h index 0dffab453b2..0eea2a2598e 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_phases: The parent class' reset phase handlers. - * - * A MIPS CPU model. - */ -struct MIPSCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - ResettablePhases parent_phases; - 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 63da1948fd1..1f6c41fd340 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -27,13 +27,14 @@ #include "internal.h" #include "kvm_mips.h" #include "qemu/module.h" -#include "sysemu/kvm.h" -#include "sysemu/qtest.h" -#include "exec/exec-all.h" +#include "system/kvm.h" +#include "system/qtest.h" #include "hw/qdev-properties.h" #include "hw/qdev-clock.h" -#include "semihosting/semihost.h" #include "fpu_helper.h" +#ifndef CONFIG_USER_ONLY +#include "semihosting/semihost.h" +#endif const char regnames[32][3] = { "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3", @@ -80,8 +81,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 +123,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) @@ -135,10 +133,10 @@ static vaddr mips_cpu_get_pc(CPUState *cs) return cpu->env.active_tc.PC; } +#if !defined(CONFIG_USER_ONLY) 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; /* @@ -181,28 +179,27 @@ static bool mips_cpu_has_work(CPUState *cs) } return has_work; } +#endif /* !CONFIG_USER_ONLY */ #include "cpu-defs.c.inc" -static void mips_cpu_reset_hold(Object *obj) +static void mips_cpu_reset_hold(Object *obj, ResetType type) { 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; if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + 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; @@ -408,18 +405,17 @@ static void mips_cpu_reset_hold(Object *obj) } msa_reset(env); + fp_reset(env); compute_hflags(env); - restore_fp_status(env); restore_pamask(env); cs->exception_index = EXCP_NONE; +#ifndef CONFIG_USER_ONLY if (semihosting_get_argc()) { /* UHI interface can be used to obtain argc and argv */ env->active_tc.gpr[4] = -1; } - -#ifndef CONFIG_USER_ONLY if (kvm_enabled()) { kvm_mips_reset_vcpu(cpu); } @@ -428,17 +424,14 @@ static void mips_cpu_reset_hold(Object *obj) 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 TARGET_BIG_ENDIAN - info->print_insn = print_insn_big_mips; -#else - info->print_insn = print_insn_little_mips; -#endif + if (!(cpu_env(s)->insn_flags & ISA_NANOMIPS32)) { + info->endian = TARGET_BIG_ENDIAN ? BFD_ENDIAN_BIG + : BFD_ENDIAN_LITTLE; + info->print_insn = TARGET_BIG_ENDIAN ? print_insn_big_mips + : print_insn_little_mips; } else { info->print_insn = print_insn_nanomips; + info->endian = BFD_ENDIAN_LITTLE; } } @@ -504,7 +497,6 @@ 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"); @@ -539,25 +531,60 @@ static ObjectClass *mips_cpu_class_by_name(const char *cpu_model) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps mips_sysemu_ops = { + .has_work = mips_cpu_has_work, .get_phys_page_debug = mips_cpu_get_phys_page_debug, .legacy_vmsd = &vmstate_mips_cpu, }; #endif +static const Property mips_cpu_properties[] = { + DEFINE_PROP_BOOL("big-endian", MIPSCPU, is_big_endian, TARGET_BIG_ENDIAN), +}; + #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 = { +#include "accel/tcg/cpu-ops.h" + +static int mips_cpu_mmu_index(CPUState *cs, bool ifunc) +{ + return mips_env_mmu_index(cpu_env(cs)); +} + +static TCGTBCPUState mips_get_tb_cpu_state(CPUState *cs) +{ + CPUMIPSState *env = cpu_env(cs); + + return (TCGTBCPUState){ + .pc = env->active_tc.PC, + .flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK | + MIPS_HFLAG_HWRENA_ULR), + }; +} + +#ifndef CONFIG_USER_ONLY +static vaddr mips_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + return cpu_env(cs)->hflags & MIPS_HFLAG_AWRAP ? (int32_t)result : result; +} +#endif + +static const TCGCPUOps mips_tcg_ops = { + .mttcg_supported = TARGET_LONG_BITS == 32, + .guest_default_memory_order = 0, + .initialize = mips_tcg_init, + .translate_code = mips_translate_code, + .get_tb_cpu_state = mips_get_tb_cpu_state, .synchronize_from_tb = mips_cpu_synchronize_from_tb, .restore_state_to_opc = mips_restore_state_to_opc, + .mmu_index = mips_cpu_mmu_index, #if !defined(CONFIG_USER_ONLY) .tlb_fill = mips_cpu_tlb_fill, + .pointer_wrap = mips_pointer_wrap, .cpu_exec_interrupt = mips_cpu_exec_interrupt, + .cpu_exec_halt = mips_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = mips_cpu_do_interrupt, .do_transaction_failed = mips_cpu_do_transaction_failed, .do_unaligned_access = mips_cpu_do_unaligned_access, @@ -566,20 +593,20 @@ static const struct TCGCPUOps mips_tcg_ops = { }; #endif /* CONFIG_TCG */ -static void mips_cpu_class_init(ObjectClass *c, void *data) +static void mips_cpu_class_init(ObjectClass *c, const 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); 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->dump_state = mips_cpu_dump_state; cc->set_pc = mips_cpu_set_pc; cc->get_pc = mips_cpu_get_pc; @@ -600,13 +627,14 @@ 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), .class_init = mips_cpu_class_init, }; -static void mips_cpu_cpudef_class_init(ObjectClass *oc, void *data) +static void mips_cpu_cpudef_class_init(ObjectClass *oc, const void *data) { MIPSCPUClass *mcc = MIPS_CPU_CLASS(oc); mcc->cpu_def = data; @@ -619,10 +647,10 @@ static void mips_register_cpudef_type(const struct mips_def_t *def) .name = typename, .parent = TYPE_MIPS_CPU, .class_init = mips_cpu_cpudef_class_init, - .class_data = (void *)def, + .class_data = def, }; - type_register(&ti); + type_register_static(&ti); g_free(typename); } @@ -639,12 +667,15 @@ static void mips_cpu_register_types(void) type_init(mips_cpu_register_types) /* 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)); + cpu = qdev_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 6d6af1f2a86..5cd4c6c818d 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -2,16 +2,16 @@ #define MIPS_CPU_H #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #ifndef CONFIG_USER_ONLY -#include "exec/memory.h" +#include "system/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 */ @@ -100,8 +100,6 @@ struct CPUMIPSFPUContext { #define FP_UNIMPLEMENTED 32 }; -#define TARGET_INSN_START_EXTRA_WORDS 2 - typedef struct CPUMIPSMVPContext CPUMIPSMVPContext; struct CPUMIPSMVPContext { int32_t CP0_MVPControl; @@ -532,7 +530,6 @@ typedef struct CPUArchState { CPUMIPSFPUContext active_fpu; uint32_t current_tc; - uint32_t current_fpu; uint32_t SEGBITS; uint32_t PABITS; @@ -747,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 @@ -1174,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; @@ -1183,8 +1177,7 @@ 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 */ @@ -1209,20 +1202,34 @@ typedef struct CPUArchState { * A MIPS CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ + + CPUMIPSState env; Clock *clock; Clock *count_div; /* Divider for CP0_Count clock */ - CPUNegativeOffsetState neg; - CPUMIPSState env; + + /* 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; +}; void cpu_wrdsp(uint32_t rs, uint32_t mask_num, CPUMIPSState *env); uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env); @@ -1231,24 +1238,24 @@ 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); } -#include "exec/cpu-all.h" - /* Exceptions */ enum { EXCP_NONE = -1, @@ -1303,14 +1310,18 @@ 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) { @@ -1345,36 +1356,28 @@ uint64_t cpu_mips_phys_to_kseg1(void *opaque, uint64_t addr); #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, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *pc = env->active_tc.PC; - *cs_base = 0; - *flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK | - MIPS_HFLAG_HWRENA_ULR); -} - /** * 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 ad1116e8c10..08fb4093904 100644 --- a/target/mips/fpu_helper.h +++ b/target/mips/fpu_helper.h @@ -28,6 +28,8 @@ static inline void restore_flush_mode(CPUMIPSState *env) static inline void restore_snan_bit_mode(CPUMIPSState *env) { bool nan2008 = env->active_fpu.fcr31 & (1 << FCR31_NAN2008); + FloatInfZeroNaNRule izn_rule; + Float3NaNPropRule nan3_rule; /* * With nan2008, SNaNs are silenced in the usual way. @@ -35,6 +37,24 @@ static inline void restore_snan_bit_mode(CPUMIPSState *env) */ set_snan_bit_is_one(!nan2008, &env->active_fpu.fp_status); set_default_nan_mode(!nan2008, &env->active_fpu.fp_status); + /* + * For MIPS systems that conform to IEEE754-1985, the (inf,zero,nan) + * case sets InvalidOp and returns the default NaN. + * For MIPS systems that conform to IEEE754-2008, the (inf,zero,nan) + * case sets InvalidOp and returns the input value 'c'. + */ + izn_rule = nan2008 ? float_infzeronan_dnan_never : float_infzeronan_dnan_always; + set_float_infzeronan_rule(izn_rule, &env->active_fpu.fp_status); + nan3_rule = nan2008 ? float_3nan_prop_s_cab : float_3nan_prop_s_abc; + set_float_3nan_prop_rule(nan3_rule, &env->active_fpu.fp_status); + /* + * With nan2008, the default NaN value has the sign bit clear and the + * frac msb set; with the older mode, the sign bit is clear, and all + * frac bits except the msb are set. + */ + set_float_default_nan_pattern(nan2008 ? 0b01000000 : 0b00111111, + &env->active_fpu.fp_status); + } static inline void restore_fp_status(CPUMIPSState *env) @@ -44,6 +64,34 @@ 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); + /* + * TODO: the spec does't say clearly whether FTZ happens before + * or after rounding for normal FPU operations. + */ + set_float_ftz_detection(float_ftz_before_rounding, + &env->active_fpu.fp_status); +} + /* MSA */ enum CPUMIPSMSADataFormat { diff --git a/target/mips/gdbstub.c b/target/mips/gdbstub.c index 62d7b72407e..169d47416a6 100644 --- a/target/mips/gdbstub.c +++ b/target/mips/gdbstub.c @@ -25,8 +25,7 @@ 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 0f8462febb5..7e400418281 100644 --- a/target/mips/helper.h +++ b/target/mips/helper.h @@ -594,7 +594,7 @@ DEF_HELPER_FLAGS_3(wrdsp, 0, void, tl, tl, env) DEF_HELPER_FLAGS_2(rddsp, 0, tl, tl, env) #ifndef CONFIG_USER_ONLY -#include "tcg/sysemu_helper.h.inc" +#include "tcg/system_helper.h.inc" #endif /* !CONFIG_USER_ONLY */ #include "tcg/msa_helper.h.inc" diff --git a/target/mips/internal.h b/target/mips/internal.h index 1d0c026c7d0..28eb28936ba 100644 --- a/target/mips/internal.h +++ b/target/mips/internal.h @@ -83,7 +83,6 @@ struct mips_def_t { uint32_t lcsr_cpucfg2; uint64_t insn_flags; enum mips_mmu_types mmu_type; - int32_t SAARP; }; extern const char regnames[32][3]; @@ -163,8 +162,6 @@ void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val); extern const VMStateDescription vmstate_mips_cpu; -#endif /* !CONFIG_USER_ONLY */ - static inline bool cpu_mips_hw_interrupts_enabled(CPUMIPSState *env) { return (env->CP0_Status & (1 << CP0St_IE)) && @@ -207,6 +204,8 @@ static inline bool cpu_mips_hw_interrupts_pending(CPUMIPSState *env) return r; } +#endif /* !CONFIG_USER_ONLY */ + void msa_reset(CPUMIPSState *env); /* cp0_timer.c */ @@ -226,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 e22e24ed974..ec53acb51a1 100644 --- a/target/mips/kvm.c +++ b/target/mips/kvm.c @@ -18,9 +18,9 @@ #include "internal.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" -#include "sysemu/kvm.h" -#include "sysemu/kvm_int.h" -#include "sysemu/runstate.h" +#include "system/kvm.h" +#include "system/kvm_int.h" +#include "system/runstate.h" #include "kvm_mips.h" #include "hw/boards.h" #include "fpu_helper.h" @@ -61,10 +61,14 @@ int kvm_arch_irqchip_create(KVMState *s) return 0; } +int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + return 0; +} + 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 +142,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 +155,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 +464,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 +505,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 +592,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 +671,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 +751,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 +973,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 +1177,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 +1212,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; @@ -1282,11 +1278,6 @@ int kvm_arch_get_default_type(MachineState *machine) 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/meson.build b/target/mips/meson.build index f35e8f0ecad..247979a2cfc 100644 --- a/target/mips/meson.build +++ b/target/mips/meson.build @@ -9,15 +9,15 @@ mips_ss.add(files( )) if have_system - subdir('sysemu') + subdir('system') 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_system_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 a6cebe0265c..9d4d292586c 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 61f1a9a5936..32c6acbcc56 100644 --- a/target/mips/msa.c +++ b/target/mips/msa.c @@ -48,6 +48,35 @@ void msa_reset(CPUMIPSState *env) /* tininess detected after rounding.*/ set_float_detect_tininess(float_tininess_after_rounding, &env->active_tc.msa_fp_status); + /* + * MSACSR.FS detects tiny results to flush to zero before rounding + * (per "MIPS Architecture for Programmers Volume IV-j: The MIPS64 SIMD + * Architecture Module, Revision 1.1" section 3.5.4), even though it + * detects tininess after rounding for underflow purposes (section 3.4.2 + * table 3.3). + */ + set_float_ftz_detection(float_ftz_before_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); + + set_float_3nan_prop_rule(float_3nan_prop_s_cab, + &env->active_tc.msa_fp_status); /* clear float_status exception flags */ set_float_exception_flags(0, &env->active_tc.msa_fp_status); @@ -57,4 +86,11 @@ void msa_reset(CPUMIPSState *env) /* set proper signanling bit meaning ("1" means "quiet") */ set_snan_bit_is_one(0, &env->active_tc.msa_fp_status); + + /* Inf * 0 + NaN returns the input NaN */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, + &env->active_tc.msa_fp_status); + /* Default NaN: sign bit clear, frac msb set */ + set_float_default_nan_pattern(0b01000000, + &env->active_tc.msa_fp_status); } diff --git a/target/mips/sysemu/machine.c b/target/mips/sysemu/machine.c deleted file mode 100644 index 80d37f9c2fc..00000000000 --- a/target/mips/sysemu/machine.c +++ /dev/null @@ -1,333 +0,0 @@ -#include "qemu/osdep.h" -#include "cpu.h" -#include "internal.h" -#include "migration/cpu.h" -#include "fpu_helper.h" - -static int cpu_post_load(void *opaque, int version_id) -{ - MIPSCPU *cpu = opaque; - CPUMIPSState *env = &cpu->env; - - restore_fp_status(env); - restore_msa_fp_status(env); - compute_hflags(env); - restore_pamask(env); - - return 0; -} - -/* FPU state */ - -static int get_fpr(QEMUFile *f, void *pv, size_t size, - const VMStateField *field) -{ - int i; - fpr_t *v = pv; - /* Restore entire MSA vector register */ - for (i = 0; i < MSA_WRLEN / 64; i++) { - qemu_get_sbe64s(f, &v->wr.d[i]); - } - return 0; -} - -static int put_fpr(QEMUFile *f, void *pv, size_t size, - const VMStateField *field, JSONWriter *vmdesc) -{ - int i; - fpr_t *v = pv; - /* Save entire MSA vector register */ - for (i = 0; i < MSA_WRLEN / 64; i++) { - qemu_put_sbe64s(f, &v->wr.d[i]); - } - - return 0; -} - -const VMStateInfo vmstate_info_fpr = { - .name = "fpr", - .get = get_fpr, - .put = put_fpr, -}; - -#define VMSTATE_FPR_ARRAY_V(_f, _s, _n, _v) \ - VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_fpr, fpr_t) - -#define VMSTATE_FPR_ARRAY(_f, _s, _n) \ - VMSTATE_FPR_ARRAY_V(_f, _s, _n, 0) - -static 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 = { - .name = "cpu/fpu", - .version_id = 1, - .minimum_version_id = 1, - .fields = vmstate_fpu_fields -}; - -const VMStateDescription vmstate_inactive_fpu = { - .name = "cpu/inactive_fpu", - .version_id = 1, - .minimum_version_id = 1, - .fields = vmstate_fpu_fields -}; - -/* TC state */ - -static VMStateField vmstate_tc_fields[] = { - VMSTATE_UINTTL_ARRAY(gpr, TCState, 32), -#if defined(TARGET_MIPS64) - VMSTATE_UINT64_ARRAY(gpr_hi, TCState, 32), -#endif /* TARGET_MIPS64 */ - VMSTATE_UINTTL(PC, TCState), - VMSTATE_UINTTL_ARRAY(HI, TCState, MIPS_DSP_ACC), - VMSTATE_UINTTL_ARRAY(LO, TCState, MIPS_DSP_ACC), - VMSTATE_UINTTL_ARRAY(ACX, TCState, MIPS_DSP_ACC), - VMSTATE_UINTTL(DSPControl, TCState), - VMSTATE_INT32(CP0_TCStatus, TCState), - VMSTATE_INT32(CP0_TCBind, TCState), - VMSTATE_UINTTL(CP0_TCHalt, TCState), - VMSTATE_UINTTL(CP0_TCContext, TCState), - VMSTATE_UINTTL(CP0_TCSchedule, TCState), - VMSTATE_UINTTL(CP0_TCScheFBack, TCState), - VMSTATE_INT32(CP0_Debug_tcstatus, TCState), - VMSTATE_UINTTL(CP0_UserLocal, TCState), - VMSTATE_INT32(msacsr, TCState), - VMSTATE_UINTTL_ARRAY(mxu_gpr, TCState, NUMBER_OF_MXU_REGISTERS - 1), - VMSTATE_UINTTL(mxu_cr, TCState), - VMSTATE_END_OF_LIST() -}; - -const VMStateDescription vmstate_tc = { - .name = "cpu/tc", - .version_id = 2, - .minimum_version_id = 2, - .fields = vmstate_tc_fields -}; - -const VMStateDescription vmstate_inactive_tc = { - .name = "cpu/inactive_tc", - .version_id = 2, - .minimum_version_id = 2, - .fields = vmstate_tc_fields -}; - -/* MVP state */ - -const VMStateDescription vmstate_mvp = { - .name = "cpu/mvp", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(CP0_MVPControl, CPUMIPSMVPContext), - VMSTATE_INT32(CP0_MVPConf0, CPUMIPSMVPContext), - VMSTATE_INT32(CP0_MVPConf1, CPUMIPSMVPContext), - VMSTATE_END_OF_LIST() - } -}; - -/* TLB state */ - -static int get_tlb(QEMUFile *f, void *pv, size_t size, - const VMStateField *field) -{ - r4k_tlb_t *v = pv; - uint16_t flags; - - qemu_get_betls(f, &v->VPN); - qemu_get_be32s(f, &v->PageMask); - qemu_get_be16s(f, &v->ASID); - qemu_get_be16s(f, &flags); - v->G = (flags >> 10) & 1; - v->C0 = (flags >> 7) & 3; - v->C1 = (flags >> 4) & 3; - v->V0 = (flags >> 3) & 1; - v->V1 = (flags >> 2) & 1; - v->D0 = (flags >> 1) & 1; - v->D1 = (flags >> 0) & 1; - v->EHINV = (flags >> 15) & 1; - v->RI1 = (flags >> 14) & 1; - v->RI0 = (flags >> 13) & 1; - v->XI1 = (flags >> 12) & 1; - v->XI0 = (flags >> 11) & 1; - qemu_get_be64s(f, &v->PFN[0]); - qemu_get_be64s(f, &v->PFN[1]); - - return 0; -} - -static int put_tlb(QEMUFile *f, void *pv, size_t size, - const VMStateField *field, JSONWriter *vmdesc) -{ - r4k_tlb_t *v = pv; - - uint16_t asid = v->ASID; - uint16_t flags = ((v->EHINV << 15) | - (v->RI1 << 14) | - (v->RI0 << 13) | - (v->XI1 << 12) | - (v->XI0 << 11) | - (v->G << 10) | - (v->C0 << 7) | - (v->C1 << 4) | - (v->V0 << 3) | - (v->V1 << 2) | - (v->D0 << 1) | - (v->D1 << 0)); - - qemu_put_betls(f, &v->VPN); - qemu_put_be32s(f, &v->PageMask); - qemu_put_be16s(f, &asid); - qemu_put_be16s(f, &flags); - qemu_put_be64s(f, &v->PFN[0]); - qemu_put_be64s(f, &v->PFN[1]); - - return 0; -} - -const VMStateInfo vmstate_info_tlb = { - .name = "tlb_entry", - .get = get_tlb, - .put = put_tlb, -}; - -#define VMSTATE_TLB_ARRAY_V(_f, _s, _n, _v) \ - VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_tlb, r4k_tlb_t) - -#define VMSTATE_TLB_ARRAY(_f, _s, _n) \ - VMSTATE_TLB_ARRAY_V(_f, _s, _n, 0) - -const VMStateDescription vmstate_tlb = { - .name = "cpu/tlb", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32(nb_tlb, CPUMIPSTLBContext), - VMSTATE_UINT32(tlb_in_use, CPUMIPSTLBContext), - VMSTATE_TLB_ARRAY(mmu.r4k.tlb, CPUMIPSTLBContext, MIPS_TLB_MAX), - VMSTATE_END_OF_LIST() - } -}; - -/* MIPS CPU state */ - -const VMStateDescription vmstate_mips_cpu = { - .name = "cpu", - .version_id = 21, - .minimum_version_id = 21, - .post_load = cpu_post_load, - .fields = (VMStateField[]) { - /* Active TC */ - VMSTATE_STRUCT(env.active_tc, MIPSCPU, 1, vmstate_tc, TCState), - - /* Active FPU */ - VMSTATE_STRUCT(env.active_fpu, MIPSCPU, 1, vmstate_fpu, - CPUMIPSFPUContext), - - /* MVP */ - VMSTATE_STRUCT_POINTER(env.mvp, MIPSCPU, vmstate_mvp, - CPUMIPSMVPContext), - - /* TLB */ - VMSTATE_STRUCT_POINTER(env.tlb, MIPSCPU, vmstate_tlb, - CPUMIPSTLBContext), - - /* CPU metastate */ - VMSTATE_UINT32(env.current_tc, MIPSCPU), - VMSTATE_UINT32(env.current_fpu, MIPSCPU), - VMSTATE_INT32(env.error_code, MIPSCPU), - VMSTATE_UINTTL(env.btarget, MIPSCPU), - VMSTATE_UINTTL(env.bcond, MIPSCPU), - - /* Remaining CP0 registers */ - VMSTATE_INT32(env.CP0_Index, MIPSCPU), - VMSTATE_INT32(env.CP0_VPControl, MIPSCPU), - VMSTATE_INT32(env.CP0_Random, MIPSCPU), - VMSTATE_INT32(env.CP0_VPEControl, MIPSCPU), - VMSTATE_INT32(env.CP0_VPEConf0, MIPSCPU), - VMSTATE_INT32(env.CP0_VPEConf1, MIPSCPU), - VMSTATE_UINTTL(env.CP0_YQMask, MIPSCPU), - VMSTATE_UINTTL(env.CP0_VPESchedule, MIPSCPU), - VMSTATE_UINTTL(env.CP0_VPEScheFBack, MIPSCPU), - VMSTATE_INT32(env.CP0_VPEOpt, MIPSCPU), - VMSTATE_UINT64(env.CP0_EntryLo0, MIPSCPU), - VMSTATE_UINT64(env.CP0_EntryLo1, MIPSCPU), - VMSTATE_INT32(env.CP0_GlobalNumber, MIPSCPU), - VMSTATE_UINTTL(env.CP0_Context, MIPSCPU), - VMSTATE_INT32(env.CP0_MemoryMapID, MIPSCPU), - VMSTATE_INT32(env.CP0_PageMask, MIPSCPU), - VMSTATE_INT32(env.CP0_PageGrain, MIPSCPU), - VMSTATE_UINTTL(env.CP0_SegCtl0, MIPSCPU), - VMSTATE_UINTTL(env.CP0_SegCtl1, MIPSCPU), - VMSTATE_UINTTL(env.CP0_SegCtl2, MIPSCPU), - VMSTATE_UINTTL(env.CP0_PWBase, MIPSCPU), - VMSTATE_UINTTL(env.CP0_PWField, MIPSCPU), - VMSTATE_UINTTL(env.CP0_PWSize, MIPSCPU), - VMSTATE_INT32(env.CP0_Wired, MIPSCPU), - VMSTATE_INT32(env.CP0_PWCtl, MIPSCPU), - VMSTATE_INT32(env.CP0_SRSConf0, MIPSCPU), - VMSTATE_INT32(env.CP0_SRSConf1, MIPSCPU), - VMSTATE_INT32(env.CP0_SRSConf2, MIPSCPU), - VMSTATE_INT32(env.CP0_SRSConf3, MIPSCPU), - VMSTATE_INT32(env.CP0_SRSConf4, MIPSCPU), - VMSTATE_INT32(env.CP0_HWREna, MIPSCPU), - VMSTATE_UINTTL(env.CP0_BadVAddr, MIPSCPU), - VMSTATE_UINT32(env.CP0_BadInstr, MIPSCPU), - 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_UINTTL(env.CP0_EntryHi, MIPSCPU), - VMSTATE_INT32(env.CP0_Compare, MIPSCPU), - VMSTATE_INT32(env.CP0_Status, MIPSCPU), - VMSTATE_INT32(env.CP0_IntCtl, MIPSCPU), - VMSTATE_INT32(env.CP0_SRSCtl, MIPSCPU), - VMSTATE_INT32(env.CP0_SRSMap, MIPSCPU), - VMSTATE_INT32(env.CP0_Cause, MIPSCPU), - VMSTATE_UINTTL(env.CP0_EPC, MIPSCPU), - VMSTATE_INT32(env.CP0_PRid, MIPSCPU), - VMSTATE_UINTTL(env.CP0_EBase, MIPSCPU), - VMSTATE_UINTTL(env.CP0_CMGCRBase, MIPSCPU), - VMSTATE_INT32(env.CP0_Config0, MIPSCPU), - VMSTATE_INT32(env.CP0_Config1, MIPSCPU), - VMSTATE_INT32(env.CP0_Config2, MIPSCPU), - VMSTATE_INT32(env.CP0_Config3, MIPSCPU), - VMSTATE_INT32(env.CP0_Config4, MIPSCPU), - VMSTATE_INT32(env.CP0_Config5, MIPSCPU), - VMSTATE_INT32(env.CP0_Config6, MIPSCPU), - VMSTATE_INT32(env.CP0_Config7, MIPSCPU), - VMSTATE_UINT64(env.CP0_LLAddr, MIPSCPU), - VMSTATE_UINT64_ARRAY(env.CP0_MAAR, MIPSCPU, MIPS_MAAR_MAX), - VMSTATE_INT32(env.CP0_MAARI, MIPSCPU), - VMSTATE_UINTTL(env.lladdr, MIPSCPU), - VMSTATE_UINTTL_ARRAY(env.CP0_WatchLo, MIPSCPU, 8), - VMSTATE_UINT64_ARRAY(env.CP0_WatchHi, MIPSCPU, 8), - VMSTATE_UINTTL(env.CP0_XContext, MIPSCPU), - VMSTATE_INT32(env.CP0_Framemask, MIPSCPU), - VMSTATE_INT32(env.CP0_Debug, MIPSCPU), - VMSTATE_UINTTL(env.CP0_DEPC, MIPSCPU), - VMSTATE_INT32(env.CP0_Performance0, MIPSCPU), - VMSTATE_INT32(env.CP0_ErrCtl, MIPSCPU), - VMSTATE_UINT64(env.CP0_TagLo, MIPSCPU), - VMSTATE_INT32(env.CP0_DataLo, MIPSCPU), - VMSTATE_INT32(env.CP0_TagHi, MIPSCPU), - VMSTATE_INT32(env.CP0_DataHi, MIPSCPU), - VMSTATE_UINTTL(env.CP0_ErrorEPC, MIPSCPU), - VMSTATE_INT32(env.CP0_DESAVE, MIPSCPU), - VMSTATE_UINTTL_ARRAY(env.CP0_KScratch, MIPSCPU, MIPS_KSCRATCH_NUM), - - /* Inactive TC */ - VMSTATE_STRUCT_ARRAY(env.tcs, MIPSCPU, MIPS_SHADOW_SET_MAX, 1, - vmstate_inactive_tc, TCState), - VMSTATE_STRUCT_ARRAY(env.fpus, MIPSCPU, MIPS_FPU_MAX, 1, - vmstate_inactive_fpu, CPUMIPSFPUContext), - - VMSTATE_END_OF_LIST() - }, -}; diff --git a/target/mips/sysemu/mips-qmp-cmds.c b/target/mips/sysemu/mips-qmp-cmds.c deleted file mode 100644 index 6db4626412c..00000000000 --- a/target/mips/sysemu/mips-qmp-cmds.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 = 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; -} diff --git a/target/mips/sysemu/addr.c b/target/mips/system/addr.c similarity index 100% rename from target/mips/sysemu/addr.c rename to target/mips/system/addr.c diff --git a/target/mips/sysemu/cp0.c b/target/mips/system/cp0.c similarity index 99% rename from target/mips/sysemu/cp0.c rename to target/mips/system/cp0.c index bae37f515bf..ff7d3db00c7 100644 --- a/target/mips/sysemu/cp0.c +++ b/target/mips/system/cp0.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" /* Called for updates to CP0_Status. */ void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc) diff --git a/target/mips/sysemu/cp0_timer.c b/target/mips/system/cp0_timer.c similarity index 98% rename from target/mips/sysemu/cp0_timer.c rename to target/mips/system/cp0_timer.c index 9d2bcb0dea2..ca16945cee1 100644 --- a/target/mips/sysemu/cp0_timer.c +++ b/target/mips/system/cp0_timer.c @@ -22,9 +22,8 @@ #include "qemu/osdep.h" #include "hw/irq.h" -#include "hw/mips/cpudevs.h" #include "qemu/timer.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "internal.h" /* MIPS R4K timer */ diff --git a/target/mips/system/machine.c b/target/mips/system/machine.c new file mode 100644 index 00000000000..8af11fd896b --- /dev/null +++ b/target/mips/system/machine.c @@ -0,0 +1,336 @@ +#include "qemu/osdep.h" +#include "cpu.h" +#include "internal.h" +#include "migration/cpu.h" +#include "fpu_helper.h" + +static int cpu_post_load(void *opaque, int version_id) +{ + MIPSCPU *cpu = opaque; + CPUMIPSState *env = &cpu->env; + + restore_fp_status(env); + restore_msa_fp_status(env); + compute_hflags(env); + restore_pamask(env); + + return 0; +} + +/* FPU state */ + +static int get_fpr(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + int i; + fpr_t *v = pv; + /* Restore entire MSA vector register */ + for (i = 0; i < MSA_WRLEN / 64; i++) { + qemu_get_sbe64s(f, &v->wr.d[i]); + } + return 0; +} + +static int put_fpr(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + int i; + fpr_t *v = pv; + /* Save entire MSA vector register */ + for (i = 0; i < MSA_WRLEN / 64; i++) { + qemu_put_sbe64s(f, &v->wr.d[i]); + } + + return 0; +} + +static const VMStateInfo vmstate_info_fpr = { + .name = "fpr", + .get = get_fpr, + .put = put_fpr, +}; + +#define VMSTATE_FPR_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_fpr, fpr_t) + +#define VMSTATE_FPR_ARRAY(_f, _s, _n) \ + VMSTATE_FPR_ARRAY_V(_f, _s, _n, 0) + +static const VMStateField vmstate_fpu_fields[] = { + VMSTATE_FPR_ARRAY(fpr, CPUMIPSFPUContext, 32), + VMSTATE_UINT32(fcr0, CPUMIPSFPUContext), + VMSTATE_UINT32(fcr31, CPUMIPSFPUContext), + VMSTATE_END_OF_LIST() +}; + +static const VMStateDescription vmstate_fpu = { + .name = "cpu/fpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = vmstate_fpu_fields +}; + +static const VMStateDescription vmstate_inactive_fpu = { + .name = "cpu/inactive_fpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = vmstate_fpu_fields +}; + +/* TC state */ + +static const VMStateField vmstate_tc_fields[] = { + VMSTATE_UINTTL_ARRAY(gpr, TCState, 32), +#if defined(TARGET_MIPS64) + VMSTATE_UINT64_ARRAY(gpr_hi, TCState, 32), +#endif /* TARGET_MIPS64 */ + VMSTATE_UINTTL(PC, TCState), + VMSTATE_UINTTL_ARRAY(HI, TCState, MIPS_DSP_ACC), + VMSTATE_UINTTL_ARRAY(LO, TCState, MIPS_DSP_ACC), + VMSTATE_UINTTL_ARRAY(ACX, TCState, MIPS_DSP_ACC), + VMSTATE_UINTTL(DSPControl, TCState), + VMSTATE_INT32(CP0_TCStatus, TCState), + VMSTATE_INT32(CP0_TCBind, TCState), + VMSTATE_UINTTL(CP0_TCHalt, TCState), + VMSTATE_UINTTL(CP0_TCContext, TCState), + VMSTATE_UINTTL(CP0_TCSchedule, TCState), + VMSTATE_UINTTL(CP0_TCScheFBack, TCState), + VMSTATE_INT32(CP0_Debug_tcstatus, TCState), + VMSTATE_UINTTL(CP0_UserLocal, TCState), + VMSTATE_INT32(msacsr, TCState), + VMSTATE_UINTTL_ARRAY(mxu_gpr, TCState, NUMBER_OF_MXU_REGISTERS - 1), + VMSTATE_UINTTL(mxu_cr, TCState), + VMSTATE_END_OF_LIST() +}; + +static const VMStateDescription vmstate_tc = { + .name = "cpu/tc", + .version_id = 2, + .minimum_version_id = 2, + .fields = vmstate_tc_fields +}; + +static const VMStateDescription vmstate_inactive_tc = { + .name = "cpu/inactive_tc", + .version_id = 2, + .minimum_version_id = 2, + .fields = vmstate_tc_fields +}; + +/* MVP state */ + +static const VMStateDescription vmstate_mvp = { + .name = "cpu/mvp", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_INT32(CP0_MVPControl, CPUMIPSMVPContext), + VMSTATE_INT32(CP0_MVPConf0, CPUMIPSMVPContext), + VMSTATE_INT32(CP0_MVPConf1, CPUMIPSMVPContext), + VMSTATE_END_OF_LIST() + } +}; + +/* TLB state */ + +static int get_tlb(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + r4k_tlb_t *v = pv; + uint16_t flags; + + 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; + v->C1 = (flags >> 4) & 3; + v->V0 = (flags >> 3) & 1; + v->V1 = (flags >> 2) & 1; + v->D0 = (flags >> 1) & 1; + v->D1 = (flags >> 0) & 1; + v->EHINV = (flags >> 15) & 1; + v->RI1 = (flags >> 14) & 1; + v->RI0 = (flags >> 13) & 1; + v->XI1 = (flags >> 12) & 1; + v->XI0 = (flags >> 11) & 1; + qemu_get_be64s(f, &v->PFN[0]); + qemu_get_be64s(f, &v->PFN[1]); + + return 0; +} + +static int put_tlb(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + 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) | + (v->XI1 << 12) | + (v->XI0 << 11) | + (v->G << 10) | + (v->C0 << 7) | + (v->C1 << 4) | + (v->V0 << 3) | + (v->V1 << 2) | + (v->D0 << 1) | + (v->D1 << 0)); + + 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]); + + return 0; +} + +static const VMStateInfo vmstate_info_tlb = { + .name = "tlb_entry", + .get = get_tlb, + .put = put_tlb, +}; + +#define VMSTATE_TLB_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_tlb, r4k_tlb_t) + +#define VMSTATE_TLB_ARRAY(_f, _s, _n) \ + VMSTATE_TLB_ARRAY_V(_f, _s, _n, 0) + +static const VMStateDescription vmstate_tlb = { + .name = "cpu/tlb", + .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), + VMSTATE_END_OF_LIST() + } +}; + +/* MIPS CPU state */ + +const VMStateDescription vmstate_mips_cpu = { + .name = "cpu", + .version_id = 21, + .minimum_version_id = 21, + .post_load = cpu_post_load, + .fields = (const VMStateField[]) { + /* Active TC */ + VMSTATE_STRUCT(env.active_tc, MIPSCPU, 1, vmstate_tc, TCState), + + /* Active FPU */ + VMSTATE_STRUCT(env.active_fpu, MIPSCPU, 1, vmstate_fpu, + CPUMIPSFPUContext), + + /* MVP */ + VMSTATE_STRUCT_POINTER(env.mvp, MIPSCPU, vmstate_mvp, + CPUMIPSMVPContext), + + /* TLB */ + VMSTATE_STRUCT_POINTER(env.tlb, MIPSCPU, vmstate_tlb, + CPUMIPSTLBContext), + + /* CPU metastate */ + VMSTATE_UINT32(env.current_tc, 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), + + /* Remaining CP0 registers */ + VMSTATE_INT32(env.CP0_Index, MIPSCPU), + VMSTATE_INT32(env.CP0_VPControl, MIPSCPU), + VMSTATE_INT32(env.CP0_Random, MIPSCPU), + VMSTATE_INT32(env.CP0_VPEControl, MIPSCPU), + VMSTATE_INT32(env.CP0_VPEConf0, MIPSCPU), + VMSTATE_INT32(env.CP0_VPEConf1, MIPSCPU), + VMSTATE_UINTTL(env.CP0_YQMask, MIPSCPU), + VMSTATE_UINTTL(env.CP0_VPESchedule, MIPSCPU), + VMSTATE_UINTTL(env.CP0_VPEScheFBack, MIPSCPU), + VMSTATE_INT32(env.CP0_VPEOpt, MIPSCPU), + VMSTATE_UINT64(env.CP0_EntryLo0, MIPSCPU), + VMSTATE_UINT64(env.CP0_EntryLo1, MIPSCPU), + VMSTATE_INT32(env.CP0_GlobalNumber, MIPSCPU), + VMSTATE_UINTTL(env.CP0_Context, MIPSCPU), + VMSTATE_INT32(env.CP0_MemoryMapID, MIPSCPU), + VMSTATE_INT32(env.CP0_PageMask, MIPSCPU), + VMSTATE_INT32(env.CP0_PageGrain, MIPSCPU), + VMSTATE_UINTTL(env.CP0_SegCtl0, MIPSCPU), + VMSTATE_UINTTL(env.CP0_SegCtl1, MIPSCPU), + VMSTATE_UINTTL(env.CP0_SegCtl2, MIPSCPU), + VMSTATE_UINTTL(env.CP0_PWBase, MIPSCPU), + VMSTATE_UINTTL(env.CP0_PWField, MIPSCPU), + VMSTATE_UINTTL(env.CP0_PWSize, MIPSCPU), + VMSTATE_INT32(env.CP0_Wired, MIPSCPU), + VMSTATE_INT32(env.CP0_PWCtl, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSConf0, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSConf1, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSConf2, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSConf3, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSConf4, MIPSCPU), + VMSTATE_INT32(env.CP0_HWREna, MIPSCPU), + VMSTATE_UINTTL(env.CP0_BadVAddr, MIPSCPU), + VMSTATE_UINT32(env.CP0_BadInstr, MIPSCPU), + VMSTATE_UINT32(env.CP0_BadInstrP, MIPSCPU), + VMSTATE_UINT32(env.CP0_BadInstrX, MIPSCPU), + VMSTATE_INT32(env.CP0_Count, MIPSCPU), + 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), + VMSTATE_INT32(env.CP0_IntCtl, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSCtl, MIPSCPU), + VMSTATE_INT32(env.CP0_SRSMap, MIPSCPU), + VMSTATE_INT32(env.CP0_Cause, MIPSCPU), + VMSTATE_UINTTL(env.CP0_EPC, MIPSCPU), + VMSTATE_INT32(env.CP0_PRid, MIPSCPU), + VMSTATE_UINTTL(env.CP0_EBase, MIPSCPU), + VMSTATE_UINTTL(env.CP0_CMGCRBase, MIPSCPU), + VMSTATE_INT32(env.CP0_Config0, MIPSCPU), + VMSTATE_INT32(env.CP0_Config1, MIPSCPU), + VMSTATE_INT32(env.CP0_Config2, MIPSCPU), + VMSTATE_INT32(env.CP0_Config3, MIPSCPU), + VMSTATE_INT32(env.CP0_Config4, MIPSCPU), + VMSTATE_INT32(env.CP0_Config5, MIPSCPU), + VMSTATE_INT32(env.CP0_Config6, MIPSCPU), + VMSTATE_INT32(env.CP0_Config7, MIPSCPU), + VMSTATE_UINT64(env.CP0_LLAddr, MIPSCPU), + VMSTATE_UINT64_ARRAY(env.CP0_MAAR, MIPSCPU, MIPS_MAAR_MAX), + VMSTATE_INT32(env.CP0_MAARI, MIPSCPU), + VMSTATE_UINTTL(env.lladdr, MIPSCPU), + VMSTATE_UINTTL_ARRAY(env.CP0_WatchLo, MIPSCPU, 8), + VMSTATE_UINT64_ARRAY(env.CP0_WatchHi, MIPSCPU, 8), + VMSTATE_UINTTL(env.CP0_XContext, MIPSCPU), + VMSTATE_INT32(env.CP0_Framemask, MIPSCPU), + VMSTATE_INT32(env.CP0_Debug, MIPSCPU), + VMSTATE_UINTTL(env.CP0_DEPC, MIPSCPU), + VMSTATE_INT32(env.CP0_Performance0, MIPSCPU), + VMSTATE_INT32(env.CP0_ErrCtl, MIPSCPU), + VMSTATE_UINT64(env.CP0_TagLo, MIPSCPU), + VMSTATE_INT32(env.CP0_DataLo, MIPSCPU), + VMSTATE_INT32(env.CP0_TagHi, MIPSCPU), + VMSTATE_INT32(env.CP0_DataHi, MIPSCPU), + VMSTATE_UINTTL(env.CP0_ErrorEPC, MIPSCPU), + VMSTATE_INT32(env.CP0_DESAVE, MIPSCPU), + VMSTATE_UINTTL_ARRAY(env.CP0_KScratch, MIPSCPU, MIPS_KSCRATCH_NUM), + + /* Inactive TC */ + VMSTATE_STRUCT_ARRAY(env.tcs, MIPSCPU, MIPS_SHADOW_SET_MAX, 1, + vmstate_inactive_tc, TCState), + VMSTATE_STRUCT_ARRAY(env.fpus, MIPSCPU, MIPS_FPU_MAX, 1, + vmstate_inactive_fpu, CPUMIPSFPUContext), + + VMSTATE_END_OF_LIST() + }, +}; diff --git a/target/mips/sysemu/meson.build b/target/mips/system/meson.build similarity index 100% rename from target/mips/sysemu/meson.build rename to target/mips/system/meson.build diff --git a/target/mips/system/mips-qmp-cmds.c b/target/mips/system/mips-qmp-cmds.c new file mode 100644 index 00000000000..d98d6623f2f --- /dev/null +++ b/target/mips/system/mips-qmp-cmds.c @@ -0,0 +1,48 @@ +/* + * 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/error.h" +#include "qapi/qapi-commands-machine.h" +#include "cpu.h" + +CpuModelExpansionInfo * +qmp_query_cpu_model_expansion(CpuModelExpansionType type, + CpuModelInfo *model, + Error **errp) +{ + error_setg(errp, "CPU model expansion is not supported on this target"); + return NULL; +} + +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/system/physaddr.c similarity index 98% rename from target/mips/sysemu/physaddr.c rename to target/mips/system/physaddr.c index 05990aa5bb3..b8e1a5ac98e 100644 --- a/target/mips/sysemu/physaddr.c +++ b/target/mips/system/physaddr.c @@ -18,7 +18,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) @@ -230,13 +230,12 @@ int get_physical_address(CPUMIPSState *env, hwaddr *physical, hwaddr mips_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); hwaddr phys_addr; int prot; if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, - cpu_mmu_index(env, false)) != 0) { + mips_env_mmu_index(env)) != 0) { return -1; } return phys_addr; diff --git a/target/mips/tcg/exception.c b/target/mips/tcg/exception.c index da49a93912a..d32bcebf469 100644 --- a/target/mips/tcg/exception.c +++ b/target/mips/tcg/exception.c @@ -23,7 +23,7 @@ #include "cpu.h" #include "internal.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" +#include "exec/translation-block.h" target_ulong exception_resume_pc(CPUMIPSState *env) { @@ -79,10 +79,9 @@ void helper_wait(CPUMIPSState *env) void mips_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + 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 45d593de489..36af9808025 100644 --- a/target/mips/tcg/fpu_helper.c +++ b/target/mips/tcg/fpu_helper.c @@ -24,7 +24,6 @@ #include "cpu.h" #include "internal.h" #include "exec/helper-proto.h" -#include "exec/exec-all.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 00000000000..25b396b6822 --- /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_translate.c b/target/mips/tcg/lcsr_translate.c index 9f2a5f4a376..352b0f43282 100644 --- a/target/mips/tcg/lcsr_translate.c +++ b/target/mips/tcg/lcsr_translate.c @@ -22,7 +22,7 @@ static bool trans_CPUCFG(DisasContext *ctx, arg_CPUCFG *a) TCGv src1 = tcg_temp_new(); gen_load_gpr(src1, a->rs); - gen_helper_lcsr_cpucfg(dest, cpu_env, src1); + gen_helper_lcsr_cpucfg(dest, tcg_env, src1); gen_store_gpr(dest, a->rd); return true; @@ -37,7 +37,7 @@ static bool gen_rdcsr(DisasContext *ctx, arg_r *a, check_cp0_enabled(ctx); gen_load_gpr(src1, a->rs); - func(dest, cpu_env, src1); + func(dest, tcg_env, src1); gen_store_gpr(dest, a->rd); return true; @@ -52,7 +52,7 @@ static bool gen_wrcsr(DisasContext *ctx, arg_r *a, check_cp0_enabled(ctx); gen_load_gpr(addr, a->rs); gen_load_gpr(val, a->rd); - func(cpu_env, addr, val); + func(tcg_env, addr, val); return true; } diff --git a/target/mips/tcg/ldst_helper.c b/target/mips/tcg/ldst_helper.c index 97056d00a27..10319bf03a6 100644 --- a/target/mips/tcg/ldst_helper.c +++ b/target/mips/tcg/ldst_helper.c @@ -23,8 +23,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/memop.h" #include "internal.h" @@ -53,11 +52,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) { @@ -65,7 +59,7 @@ static inline target_ulong get_lmask(CPUMIPSState *env, value &= mask; - if (!cpu_is_bigendian(env)) { + if (!mips_env_is_bigendian(env)) { value ^= mask; } @@ -76,7 +70,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()); @@ -100,7 +94,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()); @@ -130,7 +124,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()); @@ -174,7 +168,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()); diff --git a/target/mips/tcg/loong-ext.decode b/target/mips/tcg/loong-ext.decode new file mode 100644 index 00000000000..b43979d0ef5 --- /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 00000000000..7d74cc34f8a --- /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 ea7fb582f2a..fff9cd6c7f5 100644 --- a/target/mips/tcg/meson.build +++ b/target/mips/tcg/meson.build @@ -5,6 +5,8 @@ gen = [ 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) @@ -28,10 +30,11 @@ 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', )) if have_system - subdir('sysemu') + subdir('system') endif diff --git a/target/mips/tcg/micromips_translate.c.inc b/target/mips/tcg/micromips_translate.c.inc index 211d102cf6c..c479bec1081 100644 --- a/target/mips/tcg/micromips_translate.c.inc +++ b/target/mips/tcg/micromips_translate.c.inc @@ -710,17 +710,17 @@ static void gen_ldst_multiple(DisasContext *ctx, uint32_t opc, int reglist, 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 } @@ -977,23 +977,21 @@ 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_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UL | ctx->default_tcg_memop_mask); - tcg_gen_movi_tl(t1, 4); - gen_op_addr_add(ctx, t0, t0, t1); + 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 @@ -1002,23 +1000,21 @@ 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_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); - tcg_gen_movi_tl(t1, 8); - gen_op_addr_add(ctx, t0, t0, t1); + 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 @@ -1271,7 +1267,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_di(t0, cpu_env); + gen_helper_di(t0, tcg_env); gen_store_gpr(t0, rs); /* * Stop translation as we may have switched the execution @@ -1286,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 @@ -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 5cffe0e412d..97da3456ea5 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,70 +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 | \ - ctx->default_tcg_memop_mask); \ - } 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) { @@ -270,21 +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); + 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, @@ -294,52 +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 | \ - ctx->default_tcg_memop_mask); \ - 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) { @@ -374,21 +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); + 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 95752891956..4410e2a02e1 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 c314a74397f..f554b3d10ee 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -21,10 +21,11 @@ #include "cpu.h" #include "internal.h" #include "tcg/tcg.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "exec/helper-proto.h" #include "exec/memop.h" +#include "exec/target_page.h" #include "fpu/softfloat.h" #include "fpu_helper.h" @@ -5577,7 +5578,7 @@ static inline int64_t msa_mulr_q_df(uint32_t df, int64_t arg1, int64_t arg2) { int64_t q_min = DF_MIN_INT(df); int64_t q_max = DF_MAX_INT(df); - int64_t r_bit = 1 << (DF_BITS(df) - 2); + int64_t r_bit = 1LL << (DF_BITS(df) - 2); if (arg1 == q_min && arg2 == q_min) { return q_max; @@ -5685,7 +5686,7 @@ static inline int64_t msa_maddr_q_df(uint32_t df, int64_t dest, int64_t arg1, int64_t q_max = DF_MAX_INT(df); int64_t q_min = DF_MIN_INT(df); - int64_t r_bit = 1 << (DF_BITS(df) - 2); + int64_t r_bit = 1LL << (DF_BITS(df) - 2); q_prod = arg1 * arg2; q_ret = ((dest << (DF_BITS(df) - 1)) + q_prod + r_bit) >> (DF_BITS(df) - 1); @@ -5700,7 +5701,7 @@ static inline int64_t msa_msubr_q_df(uint32_t df, int64_t dest, int64_t arg1, int64_t q_max = DF_MAX_INT(df); int64_t q_min = DF_MIN_INT(df); - int64_t r_bit = 1 << (DF_BITS(df) - 2); + int64_t r_bit = 1LL << (DF_BITS(df) - 2); q_prod = arg1 * arg2; q_ret = ((dest << (DF_BITS(df) - 1)) - q_prod + r_bit) >> (DF_BITS(df) - 1); @@ -6231,7 +6232,7 @@ static inline int update_msacsr(CPUMIPSState *env, int action, int denormal) enable = GET_FP_ENABLE(env->active_tc.msacsr) | FP_UNIMPLEMENTED; /* Set Inexact (I) when flushing inputs to zero */ - if ((ieee_exception_flags & float_flag_input_denormal) && + if ((ieee_exception_flags & float_flag_input_denormal_flushed) && (env->active_tc.msacsr & MSACSR_FS_MASK) != 0) { if (action & CLEAR_IS_INEXACT) { mips_exception_flags &= ~FP_INEXACT; @@ -6241,7 +6242,7 @@ static inline int update_msacsr(CPUMIPSState *env, int action, int denormal) } /* Set Inexact (I) and Underflow (U) when flushing outputs to zero */ - if ((ieee_exception_flags & float_flag_output_denormal) && + if ((ieee_exception_flags & float_flag_output_denormal_flushed) && (env->active_tc.msacsr & MSACSR_FS_MASK) != 0) { mips_exception_flags |= FP_INEXACT; if (action & CLEAR_FS_UNDERFLOW) { @@ -7432,15 +7433,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) @@ -8211,15 +8212,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) { uint64_t m = 0x00ff00ff00ff00ffull; @@ -8230,7 +8222,6 @@ static inline uint64_t bswap32x2(uint64_t x) { return ror64(bswap64(x), 32); } -#endif void helper_msa_ld_b(CPUMIPSState *env, uint32_t wd, target_ulong addr) @@ -8259,10 +8250,10 @@ void helper_msa_ld_h(CPUMIPSState *env, uint32_t wd, */ d0 = cpu_ldq_le_data_ra(env, addr + 0, ra); d1 = cpu_ldq_le_data_ra(env, addr + 8, ra); -#if TARGET_BIG_ENDIAN - d0 = bswap16x4(d0); - d1 = bswap16x4(d1); -#endif + if (mips_env_is_bigendian(env)) { + d0 = bswap16x4(d0); + d1 = bswap16x4(d1); + } pwd->d[0] = d0; pwd->d[1] = d1; } @@ -8280,10 +8271,10 @@ void helper_msa_ld_w(CPUMIPSState *env, uint32_t wd, */ d0 = cpu_ldq_le_data_ra(env, addr + 0, ra); d1 = cpu_ldq_le_data_ra(env, addr + 8, ra); -#if TARGET_BIG_ENDIAN - d0 = bswap32x2(d0); - d1 = bswap32x2(d1); -#endif + if (mips_env_is_bigendian(env)) { + d0 = bswap32x2(d0); + d1 = bswap32x2(d1); + } pwd->d[0] = d0; pwd->d[1] = d1; } @@ -8323,7 +8314,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); @@ -8337,7 +8328,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; @@ -8346,10 +8337,10 @@ void helper_msa_st_h(CPUMIPSState *env, uint32_t wd, /* Store 8 bytes at a time. See helper_msa_ld_h. */ d0 = pwd->d[0]; d1 = pwd->d[1]; -#if TARGET_BIG_ENDIAN - d0 = bswap16x4(d0); - d1 = bswap16x4(d1); -#endif + if (mips_env_is_bigendian(env)) { + d0 = bswap16x4(d0); + d1 = bswap16x4(d1); + } cpu_stq_le_data_ra(env, addr + 0, d0, ra); cpu_stq_le_data_ra(env, addr + 8, d1, ra); } @@ -8358,7 +8349,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; @@ -8367,10 +8358,10 @@ void helper_msa_st_w(CPUMIPSState *env, uint32_t wd, /* Store 8 bytes at a time. See helper_msa_ld_w. */ d0 = pwd->d[0]; d1 = pwd->d[1]; -#if TARGET_BIG_ENDIAN - d0 = bswap32x2(d0); - d1 = bswap32x2(d1); -#endif + if (mips_env_is_bigendian(env)) { + d0 = bswap32x2(d0); + d1 = bswap32x2(d1); + } cpu_stq_le_data_ra(env, addr + 0, d0, ra); cpu_stq_le_data_ra(env, addr + 8, d1, ra); } @@ -8379,7 +8370,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 b5b66fb38ab..75cf80a20ed 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -140,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]); } } @@ -288,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)); @@ -314,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), @@ -330,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), @@ -357,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)); @@ -376,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), @@ -405,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), @@ -425,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)); @@ -519,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)); @@ -537,7 +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)); + gen_helper_msa_ctcmsa(tcg_env, telm, tcg_constant_i32(a->wd)); return true; } @@ -552,7 +552,7 @@ 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); return true; @@ -569,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), @@ -593,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)); @@ -698,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; } @@ -718,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)); @@ -733,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)); @@ -770,7 +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); + 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 cfcd8ac9bce..35ebb0397da 100644 --- a/target/mips/tcg/mxu_translate.c +++ b/target/mips/tcg/mxu_translate.c @@ -617,12 +617,12 @@ static const char mxuregnames[NUMBER_OF_MXU_REGISTERS][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]); } @@ -1533,7 +1533,7 @@ static void gen_mxu_s32ldxx(DisasContext *ctx, bool reversed, bool postinc) tcg_gen_add_tl(t0, t0, t1); tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, - (MO_TESL ^ (reversed ? MO_BSWAP : 0)) | + MO_SL | mo_endian_rev(ctx, reversed) | ctx->default_tcg_memop_mask); gen_store_mxu_gpr(t1, XRa); @@ -1569,7 +1569,7 @@ static void gen_mxu_s32stxx(DisasContext *ctx, bool reversed, bool postinc) gen_load_mxu_gpr(t1, XRa); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, - (MO_TESL ^ (reversed ? MO_BSWAP : 0)) | + MO_SL | mo_endian_rev(ctx, reversed) | ctx->default_tcg_memop_mask); if (postinc) { @@ -1605,7 +1605,7 @@ static void gen_mxu_s32ldxvx(DisasContext *ctx, bool reversed, tcg_gen_add_tl(t0, t0, t1); tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, - (MO_TESL ^ (reversed ? MO_BSWAP : 0)) | + MO_SL | mo_endian_rev(ctx, reversed) | ctx->default_tcg_memop_mask); gen_store_mxu_gpr(t1, XRa); @@ -1675,7 +1675,7 @@ static void gen_mxu_s32stxvx(DisasContext *ctx, bool reversed, gen_load_mxu_gpr(t1, XRa); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, - (MO_TESL ^ (reversed ? MO_BSWAP : 0)) | + MO_SL | mo_endian_rev(ctx, reversed) | ctx->default_tcg_memop_mask); if (postinc) { @@ -4803,19 +4803,19 @@ static void decode_opc_mxu__pool17(DisasContext *ctx) switch (opcode) { case OPC_MXU_LXW: - gen_mxu_lxx(ctx, strd2, MO_TE | MO_UL); + gen_mxu_lxx(ctx, strd2, mo_endian(ctx) | MO_UL); break; case OPC_MXU_LXB: - gen_mxu_lxx(ctx, strd2, MO_TE | MO_SB); + gen_mxu_lxx(ctx, strd2, mo_endian(ctx) | MO_SB); break; case OPC_MXU_LXH: - gen_mxu_lxx(ctx, strd2, MO_TE | MO_SW); + gen_mxu_lxx(ctx, strd2, mo_endian(ctx) | MO_SW); break; case OPC_MXU_LXBU: - gen_mxu_lxx(ctx, strd2, MO_TE | MO_UB); + gen_mxu_lxx(ctx, strd2, mo_endian(ctx) | MO_UB); break; case OPC_MXU_LXHU: - gen_mxu_lxx(ctx, strd2, MO_TE | MO_UW); + gen_mxu_lxx(ctx, strd2, mo_endian(ctx) | MO_UW); break; default: MIPS_INVAL("decode_opc_mxu"); diff --git a/target/mips/tcg/nanomips_translate.c.inc b/target/mips/tcg/nanomips_translate.c.inc index a98dde0d2e1..1e274143bbd 100644 --- a/target/mips/tcg/nanomips_translate.c.inc +++ b/target/mips/tcg/nanomips_translate.c.inc @@ -998,16 +998,17 @@ 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_ld_i64(tval, taddr, ctx->mem_idx, MO_TEUQ | MO_ALIGN); - 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); gen_store_gpr(tmp2, reg2); - tcg_gen_st_i64(tval, cpu_env, offsetof(CPUMIPSState, llval_wp)); - tcg_gen_st_tl(taddr, cpu_env, offsetof(CPUMIPSState, lladdr)); + 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, @@ -1025,19 +1026,19 @@ 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 | MO_ALIGN); @@ -1052,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) @@ -1075,7 +1075,7 @@ 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++; } @@ -1095,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++; @@ -1335,14 +1335,14 @@ 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; @@ -1428,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); @@ -1443,7 +1443,7 @@ 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); @@ -1485,7 +1485,7 @@ 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); } break; @@ -1517,19 +1517,19 @@ 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); @@ -1543,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); @@ -1570,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); @@ -1587,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; } @@ -1630,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; } @@ -1671,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); @@ -1694,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); @@ -1717,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); @@ -1744,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); @@ -1848,8 +1842,7 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, 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; } @@ -1903,8 +1896,7 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, 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: @@ -1923,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: @@ -1947,8 +1938,7 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, 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; } @@ -1964,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: @@ -1990,8 +1979,7 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, 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; } @@ -2014,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: @@ -2109,7 +2097,7 @@ 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); } break; @@ -2149,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: @@ -2180,8 +2166,7 @@ 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: @@ -2243,7 +2228,7 @@ 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; @@ -2255,7 +2240,7 @@ 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; @@ -2302,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 */ @@ -2334,7 +2318,7 @@ static void gen_compute_imm_branch(DisasContext *ctx, uint32_t opc, } 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 { @@ -2389,7 +2373,7 @@ 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); @@ -2403,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); @@ -2415,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); @@ -2469,11 +2451,9 @@ 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); + gen_op_addr_addi(ctx, btarget, tbase, offset); } break; default: @@ -2647,13 +2627,13 @@ static void gen_p_lsx(DisasContext *ctx, int rd, int rs, int rt) case NM_LHX: /*case NM_LHXS:*/ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, - MO_TESW | ctx->default_tcg_memop_mask); + 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 | ctx->default_tcg_memop_mask); + mo_endian(ctx) | MO_SL | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rd); break; case NM_LBUX: @@ -2663,7 +2643,7 @@ static void gen_p_lsx(DisasContext *ctx, int rd, int rs, int rt) case NM_LHUX: /*case NM_LHUXS:*/ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, - MO_TEUW | ctx->default_tcg_memop_mask); + mo_endian(ctx) | MO_UW | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rd); break; case NM_SBX: @@ -2676,14 +2656,14 @@ static void gen_p_lsx(DisasContext *ctx, int rd, int rs, int rt) check_nms(ctx); gen_load_gpr(t1, rd); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, - MO_TEUW | ctx->default_tcg_memop_mask); + 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 | ctx->default_tcg_memop_mask); + mo_endian(ctx) | MO_UL | ctx->default_tcg_memop_mask); break; case NM_LWC1X: /*case NM_LWC1XS:*/ @@ -3036,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); @@ -3098,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: @@ -3131,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; } @@ -3176,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; } @@ -3191,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; } @@ -3251,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; } @@ -3296,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; } @@ -3311,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; } @@ -3341,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; } @@ -3376,32 +3356,32 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, 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: @@ -3434,34 +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); + 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: @@ -3469,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; } @@ -3496,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: @@ -3532,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: @@ -3547,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: @@ -3729,32 +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 | ctx->default_tcg_memop_mask); + 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 | ctx->default_tcg_memop_mask); + 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: @@ -4132,14 +4104,14 @@ 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; } } @@ -4161,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); @@ -4274,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); @@ -4317,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))) { @@ -4328,7 +4302,7 @@ 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++; @@ -4407,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); @@ -4503,7 +4477,7 @@ 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_constant_tl(ctx->base.pc_next); - tcg_gen_st_tl(tmp, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr)); + tcg_gen_st_tl(tmp, tcg_env, offsetof(CPUMIPSState, CP0_BadVAddr)); generate_exception_end(ctx, EXCP_AdEL); return 2; } @@ -4635,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); diff --git a/target/mips/tcg/octeon_translate.c b/target/mips/tcg/octeon_translate.c index e25c4cbaa06..d9eb43716e2 100644 --- a/target/mips/tcg/octeon_translate.c +++ b/target/mips/tcg/octeon_translate.c @@ -18,8 +18,8 @@ static bool trans_BBIT(DisasContext *ctx, arg_BBIT *a) TCGv p; if (ctx->hflags & MIPS_HFLAG_BMASK) { - LOG_DISAS("Branch in delay / forbidden slot at PC 0x" - TARGET_FMT_lx "\n", ctx->base.pc_next); + LOG_DISAS("Branch in delay / forbidden slot at PC 0x%" VADDR_PRIx "\n", + ctx->base.pc_next); generate_exception_end(ctx, EXCP_RI); return true; } diff --git a/target/mips/tcg/op_helper.c b/target/mips/tcg/op_helper.c index 98935b5e641..b906d10204b 100644 --- a/target/mips/tcg/op_helper.c +++ b/target/mips/tcg/op_helper.c @@ -22,7 +22,6 @@ #include "cpu.h" #include "internal.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "exec/memop.h" #include "fpu_helper.h" @@ -279,8 +278,7 @@ 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; @@ -306,9 +304,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/sysemu/meson.build b/target/mips/tcg/sysemu/meson.build deleted file mode 100644 index ec665a4b1ea..00000000000 --- a/target/mips/tcg/sysemu/meson.build +++ /dev/null @@ -1,10 +0,0 @@ -mips_system_ss.add(files( - 'cp0_helper.c', - 'mips-semi.c', - 'special_helper.c', - 'tlb_helper.c', -)) - -mips_system_ss.add(when: 'TARGET_MIPS64', if_true: files( - 'lcsr_helper.c', -)) diff --git a/target/mips/tcg/sysemu/tlb_helper.c b/target/mips/tcg/sysemu/tlb_helper.c deleted file mode 100644 index 7dbc2e24c4e..00000000000 --- a/target/mips/tcg/sysemu/tlb_helper.c +++ /dev/null @@ -1,1428 +0,0 @@ -/* - * MIPS TLB (Translation lookaside buffer) helpers. - * - * Copyright (c) 2004-2005 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 "qemu/bitops.h" - -#include "cpu.h" -#include "internal.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "exec/log.h" -#include "hw/mips/cpudevs.h" -#include "exec/helper-proto.h" - -/* TLB management */ -static void r4k_mips_tlb_flush_extra(CPUMIPSState *env, int first) -{ - /* Discard entries from env->tlb[first] onwards. */ - while (env->tlb->tlb_in_use > first) { - r4k_invalidate_tlb(env, --env->tlb->tlb_in_use, 0); - } -} - -static inline uint64_t get_tlb_pfn_from_entrylo(uint64_t entrylo) -{ -#if defined(TARGET_MIPS64) - return extract64(entrylo, 6, 54); -#else - return extract64(entrylo, 6, 24) | /* PFN */ - (extract64(entrylo, 32, 32) << 24); /* PFNX */ -#endif -} - -static void r4k_fill_tlb(CPUMIPSState *env, int idx) -{ - r4k_tlb_t *tlb; - uint64_t mask = env->CP0_PageMask >> (TARGET_PAGE_BITS + 1); - - /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */ - tlb = &env->tlb->mmu.r4k.tlb[idx]; - if (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) { - tlb->EHINV = 1; - return; - } - tlb->EHINV = 0; - tlb->VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1); -#if defined(TARGET_MIPS64) - tlb->VPN &= env->SEGMask; -#endif - tlb->ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; - tlb->MMID = env->CP0_MemoryMapID; - tlb->PageMask = env->CP0_PageMask; - tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; - tlb->V0 = (env->CP0_EntryLo0 & 2) != 0; - tlb->D0 = (env->CP0_EntryLo0 & 4) != 0; - tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7; - tlb->XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) & 1; - tlb->RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) & 1; - tlb->PFN[0] = (get_tlb_pfn_from_entrylo(env->CP0_EntryLo0) & ~mask) << 12; - tlb->V1 = (env->CP0_EntryLo1 & 2) != 0; - tlb->D1 = (env->CP0_EntryLo1 & 4) != 0; - tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7; - tlb->XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) & 1; - tlb->RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) & 1; - tlb->PFN[1] = (get_tlb_pfn_from_entrylo(env->CP0_EntryLo1) & ~mask) << 12; -} - -static void r4k_helper_tlbinv(CPUMIPSState *env) -{ - bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); - uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; - uint32_t MMID = env->CP0_MemoryMapID; - uint32_t tlb_mmid; - r4k_tlb_t *tlb; - int idx; - - MMID = mi ? MMID : (uint32_t) ASID; - for (idx = 0; idx < env->tlb->nb_tlb; idx++) { - tlb = &env->tlb->mmu.r4k.tlb[idx]; - tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; - if (!tlb->G && tlb_mmid == MMID) { - tlb->EHINV = 1; - } - } - cpu_mips_tlb_flush(env); -} - -static void r4k_helper_tlbinvf(CPUMIPSState *env) -{ - int idx; - - for (idx = 0; idx < env->tlb->nb_tlb; idx++) { - env->tlb->mmu.r4k.tlb[idx].EHINV = 1; - } - cpu_mips_tlb_flush(env); -} - -static void r4k_helper_tlbwi(CPUMIPSState *env) -{ - bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); - target_ulong VPN; - uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; - uint32_t MMID = env->CP0_MemoryMapID; - uint32_t tlb_mmid; - bool EHINV, G, V0, D0, V1, D1, XI0, XI1, RI0, RI1; - r4k_tlb_t *tlb; - int idx; - - MMID = mi ? MMID : (uint32_t) ASID; - - idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; - tlb = &env->tlb->mmu.r4k.tlb[idx]; - VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1); -#if defined(TARGET_MIPS64) - VPN &= env->SEGMask; -#endif - EHINV = (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) != 0; - G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; - V0 = (env->CP0_EntryLo0 & 2) != 0; - D0 = (env->CP0_EntryLo0 & 4) != 0; - XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) &1; - RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) &1; - V1 = (env->CP0_EntryLo1 & 2) != 0; - D1 = (env->CP0_EntryLo1 & 4) != 0; - XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) &1; - RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) &1; - - tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; - /* - * Discard cached TLB entries, unless tlbwi is just upgrading access - * permissions on the current entry. - */ - if (tlb->VPN != VPN || tlb_mmid != MMID || tlb->G != G || - (!tlb->EHINV && EHINV) || - (tlb->V0 && !V0) || (tlb->D0 && !D0) || - (!tlb->XI0 && XI0) || (!tlb->RI0 && RI0) || - (tlb->V1 && !V1) || (tlb->D1 && !D1) || - (!tlb->XI1 && XI1) || (!tlb->RI1 && RI1)) { - r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb); - } - - r4k_invalidate_tlb(env, idx, 0); - r4k_fill_tlb(env, idx); -} - -static void r4k_helper_tlbwr(CPUMIPSState *env) -{ - int r = cpu_mips_get_random(env); - - r4k_invalidate_tlb(env, r, 1); - r4k_fill_tlb(env, r); -} - -static void r4k_helper_tlbp(CPUMIPSState *env) -{ - bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); - r4k_tlb_t *tlb; - target_ulong mask; - target_ulong tag; - target_ulong VPN; - uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; - uint32_t MMID = env->CP0_MemoryMapID; - uint32_t tlb_mmid; - int i; - - MMID = mi ? MMID : (uint32_t) ASID; - for (i = 0; i < env->tlb->nb_tlb; i++) { - tlb = &env->tlb->mmu.r4k.tlb[i]; - /* 1k pages are not supported. */ - mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); - tag = env->CP0_EntryHi & ~mask; - VPN = tlb->VPN & ~mask; -#if defined(TARGET_MIPS64) - tag &= env->SEGMask; -#endif - tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; - /* Check ASID/MMID, virtual page number & size */ - if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag && !tlb->EHINV) { - /* TLB match */ - env->CP0_Index = i; - break; - } - } - if (i == env->tlb->nb_tlb) { - /* No match. Discard any shadow entries, if any of them match. */ - for (i = env->tlb->nb_tlb; i < env->tlb->tlb_in_use; i++) { - tlb = &env->tlb->mmu.r4k.tlb[i]; - /* 1k pages are not supported. */ - mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); - tag = env->CP0_EntryHi & ~mask; - VPN = tlb->VPN & ~mask; -#if defined(TARGET_MIPS64) - tag &= env->SEGMask; -#endif - tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; - /* Check ASID/MMID, virtual page number & size */ - if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag) { - r4k_mips_tlb_flush_extra(env, i); - break; - } - } - - env->CP0_Index |= 0x80000000; - } -} - -static inline uint64_t get_entrylo_pfn_from_tlb(uint64_t tlb_pfn) -{ -#if defined(TARGET_MIPS64) - return tlb_pfn << 6; -#else - return (extract64(tlb_pfn, 0, 24) << 6) | /* PFN */ - (extract64(tlb_pfn, 24, 32) << 32); /* PFNX */ -#endif -} - -static void r4k_helper_tlbr(CPUMIPSState *env) -{ - bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); - uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; - uint32_t MMID = env->CP0_MemoryMapID; - uint32_t tlb_mmid; - r4k_tlb_t *tlb; - int idx; - - MMID = mi ? MMID : (uint32_t) ASID; - idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; - tlb = &env->tlb->mmu.r4k.tlb[idx]; - - tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; - /* If this will change the current ASID/MMID, flush qemu's TLB. */ - if (MMID != tlb_mmid) { - cpu_mips_tlb_flush(env); - } - - r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb); - - if (tlb->EHINV) { - env->CP0_EntryHi = 1 << CP0EnHi_EHINV; - env->CP0_PageMask = 0; - env->CP0_EntryLo0 = 0; - env->CP0_EntryLo1 = 0; - } else { - env->CP0_EntryHi = mi ? tlb->VPN : tlb->VPN | tlb->ASID; - env->CP0_MemoryMapID = tlb->MMID; - env->CP0_PageMask = tlb->PageMask; - env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) | - ((uint64_t)tlb->RI0 << CP0EnLo_RI) | - ((uint64_t)tlb->XI0 << CP0EnLo_XI) | (tlb->C0 << 3) | - get_entrylo_pfn_from_tlb(tlb->PFN[0] >> 12); - env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) | - ((uint64_t)tlb->RI1 << CP0EnLo_RI) | - ((uint64_t)tlb->XI1 << CP0EnLo_XI) | (tlb->C1 << 3) | - get_entrylo_pfn_from_tlb(tlb->PFN[1] >> 12); - } -} - -void helper_tlbwi(CPUMIPSState *env) -{ - env->tlb->helper_tlbwi(env); -} - -void helper_tlbwr(CPUMIPSState *env) -{ - env->tlb->helper_tlbwr(env); -} - -void helper_tlbp(CPUMIPSState *env) -{ - env->tlb->helper_tlbp(env); -} - -void helper_tlbr(CPUMIPSState *env) -{ - env->tlb->helper_tlbr(env); -} - -void helper_tlbinv(CPUMIPSState *env) -{ - env->tlb->helper_tlbinv(env); -} - -void helper_tlbinvf(CPUMIPSState *env) -{ - env->tlb->helper_tlbinvf(env); -} - -static void global_invalidate_tlb(CPUMIPSState *env, - uint32_t invMsgVPN2, - uint8_t invMsgR, - uint32_t invMsgMMid, - bool invAll, - bool invVAMMid, - bool invMMid, - bool invVA) -{ - - int idx; - r4k_tlb_t *tlb; - bool VAMatch; - bool MMidMatch; - - for (idx = 0; idx < env->tlb->nb_tlb; idx++) { - tlb = &env->tlb->mmu.r4k.tlb[idx]; - VAMatch = - (((tlb->VPN & ~tlb->PageMask) == (invMsgVPN2 & ~tlb->PageMask)) -#ifdef TARGET_MIPS64 - && - (extract64(env->CP0_EntryHi, 62, 2) == invMsgR) -#endif - ); - MMidMatch = tlb->MMID == invMsgMMid; - if ((invAll && (idx > env->CP0_Wired)) || - (VAMatch && invVAMMid && (tlb->G || MMidMatch)) || - (VAMatch && invVA) || - (MMidMatch && !(tlb->G) && invMMid)) { - tlb->EHINV = 1; - } - } - cpu_mips_tlb_flush(env); -} - -void helper_ginvt(CPUMIPSState *env, target_ulong arg, uint32_t type) -{ - bool invAll = type == 0; - bool invVA = type == 1; - bool invMMid = type == 2; - bool invVAMMid = type == 3; - uint32_t invMsgVPN2 = arg & (TARGET_PAGE_MASK << 1); - uint8_t invMsgR = 0; - uint32_t invMsgMMid = env->CP0_MemoryMapID; - CPUState *other_cs = first_cpu; - -#ifdef TARGET_MIPS64 - invMsgR = extract64(arg, 62, 2); -#endif - - CPU_FOREACH(other_cs) { - MIPSCPU *other_cpu = MIPS_CPU(other_cs); - global_invalidate_tlb(&other_cpu->env, invMsgVPN2, invMsgR, invMsgMMid, - invAll, invVAMMid, invMMid, invVA); - } -} - -/* no MMU emulation */ -static int no_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot, - target_ulong address, MMUAccessType access_type) -{ - *physical = address; - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return TLBRET_MATCH; -} - -/* fixed mapping MMU emulation */ -static int fixed_mmu_map_address(CPUMIPSState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type) -{ - if (address <= (int32_t)0x7FFFFFFFUL) { - if (!(env->CP0_Status & (1 << CP0St_ERL))) { - *physical = address + 0x40000000UL; - } else { - *physical = address; - } - } else if (address <= (int32_t)0xBFFFFFFFUL) { - *physical = address & 0x1FFFFFFF; - } else { - *physical = address; - } - - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return TLBRET_MATCH; -} - -/* MIPS32/MIPS64 R4000-style MMU emulation */ -static int r4k_map_address(CPUMIPSState *env, hwaddr *physical, int *prot, - target_ulong address, MMUAccessType access_type) -{ - uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; - uint32_t MMID = env->CP0_MemoryMapID; - bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); - uint32_t tlb_mmid; - int i; - - MMID = mi ? MMID : (uint32_t) ASID; - - for (i = 0; i < env->tlb->tlb_in_use; i++) { - r4k_tlb_t *tlb = &env->tlb->mmu.r4k.tlb[i]; - /* 1k pages are not supported. */ - target_ulong mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); - target_ulong tag = address & ~mask; - target_ulong VPN = tlb->VPN & ~mask; -#if defined(TARGET_MIPS64) - tag &= env->SEGMask; -#endif - - /* Check ASID/MMID, virtual page number & size */ - tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; - if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag && !tlb->EHINV) { - /* TLB match */ - int n = !!(address & mask & ~(mask >> 1)); - /* Check access rights */ - if (!(n ? tlb->V1 : tlb->V0)) { - return TLBRET_INVALID; - } - if (access_type == MMU_INST_FETCH && (n ? tlb->XI1 : tlb->XI0)) { - return TLBRET_XI; - } - if (access_type == MMU_DATA_LOAD && (n ? tlb->RI1 : tlb->RI0)) { - return TLBRET_RI; - } - if (access_type != MMU_DATA_STORE || (n ? tlb->D1 : tlb->D0)) { - *physical = tlb->PFN[n] | (address & (mask >> 1)); - *prot = PAGE_READ; - if (n ? tlb->D1 : tlb->D0) { - *prot |= PAGE_WRITE; - } - if (!(n ? tlb->XI1 : tlb->XI0)) { - *prot |= PAGE_EXEC; - } - return TLBRET_MATCH; - } - return TLBRET_DIRTY; - } - } - return TLBRET_NOMATCH; -} - -static void no_mmu_init(CPUMIPSState *env, const mips_def_t *def) -{ - env->tlb->nb_tlb = 1; - env->tlb->map_address = &no_mmu_map_address; -} - -static void fixed_mmu_init(CPUMIPSState *env, const mips_def_t *def) -{ - env->tlb->nb_tlb = 1; - env->tlb->map_address = &fixed_mmu_map_address; -} - -static void r4k_mmu_init(CPUMIPSState *env, const mips_def_t *def) -{ - env->tlb->nb_tlb = 1 + ((def->CP0_Config1 >> CP0C1_MMU) & 63); - env->tlb->map_address = &r4k_map_address; - env->tlb->helper_tlbwi = r4k_helper_tlbwi; - env->tlb->helper_tlbwr = r4k_helper_tlbwr; - env->tlb->helper_tlbp = r4k_helper_tlbp; - env->tlb->helper_tlbr = r4k_helper_tlbr; - env->tlb->helper_tlbinv = r4k_helper_tlbinv; - env->tlb->helper_tlbinvf = r4k_helper_tlbinvf; -} - -void mmu_init(CPUMIPSState *env, const mips_def_t *def) -{ - env->tlb = g_malloc0(sizeof(CPUMIPSTLBContext)); - - switch (def->mmu_type) { - case MMU_TYPE_NONE: - no_mmu_init(env, def); - break; - case MMU_TYPE_R4000: - r4k_mmu_init(env, def); - break; - case MMU_TYPE_FMT: - fixed_mmu_init(env, def); - break; - case MMU_TYPE_R3000: - case MMU_TYPE_R6000: - case MMU_TYPE_R8000: - default: - cpu_abort(env_cpu(env), "MMU type not supported\n"); - } -} - -void cpu_mips_tlb_flush(CPUMIPSState *env) -{ - /* Flush qemu's TLB and discard all shadowed entries. */ - tlb_flush(env_cpu(env)); - env->tlb->tlb_in_use = env->tlb->nb_tlb; -} - -static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, - MMUAccessType access_type, int tlb_error) -{ - CPUState *cs = env_cpu(env); - int exception = 0, error_code = 0; - - if (access_type == MMU_INST_FETCH) { - error_code |= EXCP_INST_NOTAVAIL; - } - - switch (tlb_error) { - default: - case TLBRET_BADADDR: - /* Reference to kernel address from user mode or supervisor mode */ - /* Reference to supervisor address from user mode */ - if (access_type == MMU_DATA_STORE) { - exception = EXCP_AdES; - } else { - exception = EXCP_AdEL; - } - break; - case TLBRET_NOMATCH: - /* No TLB match for a mapped address */ - if (access_type == MMU_DATA_STORE) { - exception = EXCP_TLBS; - } else { - exception = EXCP_TLBL; - } - error_code |= EXCP_TLB_NOMATCH; - break; - case TLBRET_INVALID: - /* TLB match with no valid bit */ - if (access_type == MMU_DATA_STORE) { - exception = EXCP_TLBS; - } else { - exception = EXCP_TLBL; - } - break; - case TLBRET_DIRTY: - /* TLB match but 'D' bit is cleared */ - exception = EXCP_LTLBL; - break; - case TLBRET_XI: - /* Execute-Inhibit Exception */ - if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { - exception = EXCP_TLBXI; - } else { - exception = EXCP_TLBL; - } - break; - case TLBRET_RI: - /* Read-Inhibit Exception */ - if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { - exception = EXCP_TLBRI; - } else { - exception = EXCP_TLBL; - } - break; - } - /* Raise exception */ - if (!(env->hflags & MIPS_HFLAG_DM)) { - env->CP0_BadVAddr = address; - } - env->CP0_Context = (env->CP0_Context & ~0x007fffff) | - ((address >> 9) & 0x007ffff0); - env->CP0_EntryHi = (env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask) | - (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) | - (address & (TARGET_PAGE_MASK << 1)); -#if defined(TARGET_MIPS64) - env->CP0_EntryHi &= env->SEGMask; - env->CP0_XContext = - (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) | /* PTEBase */ - (extract64(address, 62, 2) << (env->SEGBITS - 9)) | /* R */ - (extract64(address, 13, env->SEGBITS - 13) << 4); /* BadVPN2 */ -#endif - cs->exception_index = exception; - env->error_code = error_code; -} - -#if !defined(TARGET_MIPS64) - -/* - * Perform hardware page table walk - * - * Memory accesses are performed using the KERNEL privilege level. - * Synchronous exceptions detected on memory accesses cause a silent exit - * from page table walking, resulting in a TLB or XTLB Refill exception. - * - * Implementations are not required to support page table walk memory - * accesses from mapped memory regions. When an unsupported access is - * attempted, a silent exit is taken, resulting in a TLB or XTLB Refill - * exception. - * - * Note that if an exception is caused by AddressTranslation or LoadMemory - * functions, the exception is not taken, a silent exit is taken, - * resulting in a TLB or XTLB Refill exception. - */ - -static bool get_pte(CPUMIPSState *env, uint64_t vaddr, int entry_size, - uint64_t *pte) -{ - if ((vaddr & ((entry_size >> 3) - 1)) != 0) { - return false; - } - if (entry_size == 64) { - *pte = cpu_ldq_code(env, vaddr); - } else { - *pte = cpu_ldl_code(env, vaddr); - } - return true; -} - -static uint64_t get_tlb_entry_layout(CPUMIPSState *env, uint64_t entry, - int entry_size, int ptei) -{ - uint64_t result = entry; - uint64_t rixi; - if (ptei > entry_size) { - ptei -= 32; - } - result >>= (ptei - 2); - rixi = result & 3; - result >>= 2; - result |= rixi << CP0EnLo_XI; - return result; -} - -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, - unsigned directory_shift, unsigned leaf_shift) -{ - 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; - uint32_t direntry_size = 1 << (directory_shift + 3); - uint32_t leafentry_size = 1 << (leaf_shift + 3); - uint64_t entry; - uint64_t paddr; - int prot; - uint64_t lsb = 0; - uint64_t w = 0; - - if (get_physical_address(env, &paddr, &prot, *vaddr, MMU_DATA_LOAD, - cpu_mmu_index(env, false)) != - TLBRET_MATCH) { - /* wrong base address */ - return 0; - } - if (!get_pte(env, *vaddr, direntry_size, &entry)) { - 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); - w = directory_index - 1; - if (directory_index & 0x1) { - /* Generate adjacent page from same PTE for odd TLB page */ - lsb = BIT_ULL(w) >> 6; - *pw_entrylo0 = entry & ~lsb; /* even page */ - *pw_entrylo1 = entry | lsb; /* odd page */ - } else if (dph) { - int oddpagebit = 1 << leaf_shift; - uint64_t vaddr2 = *vaddr ^ oddpagebit; - if (*vaddr & oddpagebit) { - *pw_entrylo1 = entry; - } else { - *pw_entrylo0 = entry; - } - if (get_physical_address(env, &paddr, &prot, vaddr2, MMU_DATA_LOAD, - cpu_mmu_index(env, false)) != - TLBRET_MATCH) { - return 0; - } - if (!get_pte(env, vaddr2, leafentry_size, &entry)) { - return 0; - } - entry = get_tlb_entry_layout(env, entry, leafentry_size, pf_ptew); - if (*vaddr & oddpagebit) { - *pw_entrylo0 = entry; - } else { - *pw_entrylo1 = entry; - } - } else { - return 0; - } - return 1; - } else { - *vaddr = entry; - return 2; - } -} - -static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, - int mmu_idx) -{ - int gdw = (env->CP0_PWSize >> CP0PS_GDW) & 0x3F; - int udw = (env->CP0_PWSize >> CP0PS_UDW) & 0x3F; - int mdw = (env->CP0_PWSize >> CP0PS_MDW) & 0x3F; - int ptw = (env->CP0_PWSize >> CP0PS_PTW) & 0x3F; - int ptew = (env->CP0_PWSize >> CP0PS_PTEW) & 0x3F; - - /* Initial values */ - bool huge_page = false; - bool hgpg_bdhit = false; - bool hgpg_gdhit = false; - bool hgpg_udhit = false; - bool hgpg_mdhit = false; - - int32_t pw_pagemask = 0; - target_ulong pw_entryhi = 0; - uint64_t pw_entrylo0 = 0; - uint64_t pw_entrylo1 = 0; - - /* 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; - - /* Indices from PWField */ - int pf_gdw = (env->CP0_PWField >> CP0PF_GDW) & 0x3F; - int pf_udw = (env->CP0_PWField >> CP0PF_UDW) & 0x3F; - int pf_mdw = (env->CP0_PWField >> CP0PF_MDW) & 0x3F; - int pf_ptw = (env->CP0_PWField >> CP0PF_PTW) & 0x3F; - int pf_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; - - /* Indices computed from faulting address */ - int gindex = (address >> pf_gdw) & ((1 << gdw) - 1); - int uindex = (address >> pf_udw) & ((1 << udw) - 1); - int mindex = (address >> pf_mdw) & ((1 << mdw) - 1); - int ptindex = (address >> pf_ptw) & ((1 << ptw) - 1); - - /* Other HTW configs */ - int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; - unsigned directory_shift, leaf_shift; - - /* Offsets into tables */ - unsigned goffset, uoffset, moffset, ptoffset0, ptoffset1; - uint32_t leafentry_size; - - /* Starting address - Page Table Base */ - uint64_t vaddr = env->CP0_PWBase; - - uint64_t dir_entry; - uint64_t paddr; - int prot; - int m; - - if (!(env->CP0_Config3 & (1 << CP0C3_PW))) { - /* walker is unimplemented */ - return false; - } - if (!(env->CP0_PWCtl & (1 << CP0PC_PWEN))) { - /* walker is disabled */ - return false; - } - if (!(gdw > 0 || udw > 0 || mdw > 0)) { - /* no structure to walk */ - return false; - } - if (ptew > 1) { - return false; - } - - /* HTW Shift values (depend on entry size) */ - directory_shift = (hugepg && (ptew == 1)) ? native_shift + 1 : native_shift; - leaf_shift = (ptew == 1) ? native_shift + 1 : native_shift; - - goffset = gindex << directory_shift; - uoffset = uindex << directory_shift; - moffset = mindex << directory_shift; - ptoffset0 = (ptindex >> 1) << (leaf_shift + 1); - ptoffset1 = ptoffset0 | (1 << (leaf_shift)); - - leafentry_size = 1 << (leaf_shift + 3); - - /* Global Directory */ - if (gdw > 0) { - vaddr |= goffset; - switch (walk_directory(env, &vaddr, pf_gdw, &huge_page, &hgpg_gdhit, - &pw_entrylo0, &pw_entrylo1, - directory_shift, leaf_shift)) - { - case 0: - return false; - case 1: - goto refill; - case 2: - default: - break; - } - } - - /* Upper directory */ - if (udw > 0) { - vaddr |= uoffset; - switch (walk_directory(env, &vaddr, pf_udw, &huge_page, &hgpg_udhit, - &pw_entrylo0, &pw_entrylo1, - directory_shift, leaf_shift)) - { - case 0: - return false; - case 1: - goto refill; - case 2: - default: - break; - } - } - - /* Middle directory */ - if (mdw > 0) { - vaddr |= moffset; - switch (walk_directory(env, &vaddr, pf_mdw, &huge_page, &hgpg_mdhit, - &pw_entrylo0, &pw_entrylo1, - directory_shift, leaf_shift)) - { - case 0: - return false; - case 1: - goto refill; - case 2: - default: - break; - } - } - - /* 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) { - return false; - } - if (!get_pte(env, vaddr, leafentry_size, &dir_entry)) { - return false; - } - dir_entry = get_tlb_entry_layout(env, dir_entry, leafentry_size, 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) { - return false; - } - if (!get_pte(env, vaddr, leafentry_size, &dir_entry)) { - return false; - } - dir_entry = get_tlb_entry_layout(env, dir_entry, leafentry_size, pf_ptew); - pw_entrylo1 = dir_entry; - -refill: - - m = (1 << pf_ptw) - 1; - - if (huge_page) { - switch (hgpg_bdhit << 3 | hgpg_gdhit << 2 | hgpg_udhit << 1 | - hgpg_mdhit) - { - case 4: - m = (1 << pf_gdw) - 1; - if (pf_gdw & 1) { - m >>= 1; - } - break; - case 2: - m = (1 << pf_udw) - 1; - if (pf_udw & 1) { - m >>= 1; - } - break; - case 1: - m = (1 << pf_mdw) - 1; - if (pf_mdw & 1) { - m >>= 1; - } - break; - } - } - pw_pagemask = m >> TARGET_PAGE_BITS_MIN; - update_pagemask(env, pw_pagemask << CP0PM_MASK, &pw_pagemask); - pw_entryhi = (address & ~0x1fff) | (env->CP0_EntryHi & 0xFF); - { - target_ulong tmp_entryhi = env->CP0_EntryHi; - int32_t tmp_pagemask = env->CP0_PageMask; - uint64_t tmp_entrylo0 = env->CP0_EntryLo0; - uint64_t tmp_entrylo1 = env->CP0_EntryLo1; - - env->CP0_EntryHi = pw_entryhi; - env->CP0_PageMask = pw_pagemask; - env->CP0_EntryLo0 = pw_entrylo0; - env->CP0_EntryLo1 = pw_entrylo1; - - /* - * The hardware page walker inserts a page into the TLB in a manner - * identical to a TLBWR instruction as executed by the software refill - * handler. - */ - r4k_helper_tlbwr(env); - - env->CP0_EntryHi = tmp_entryhi; - env->CP0_PageMask = tmp_pagemask; - env->CP0_EntryLo0 = tmp_entrylo0; - env->CP0_EntryLo1 = tmp_entrylo1; - } - return true; -} -#endif - -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; - hwaddr physical; - int prot; - int ret = TLBRET_BADADDR; - - /* data access */ - /* XXX: put correct access by using cpu_restore_state() correctly */ - ret = get_physical_address(env, &physical, &prot, address, - access_type, mmu_idx); - switch (ret) { - case TLBRET_MATCH: - qemu_log_mask(CPU_LOG_MMU, - "%s address=%" VADDR_PRIx " physical " HWADDR_FMT_plx - " prot %d\n", __func__, address, physical, prot); - break; - default: - qemu_log_mask(CPU_LOG_MMU, - "%s address=%" VADDR_PRIx " ret %d\n", __func__, address, - ret); - break; - } - if (ret == TLBRET_MATCH) { - tlb_set_page(cs, address & TARGET_PAGE_MASK, - physical & TARGET_PAGE_MASK, prot, - mmu_idx, TARGET_PAGE_SIZE); - return true; - } -#if !defined(TARGET_MIPS64) - if ((ret == TLBRET_NOMATCH) && (env->tlb->nb_tlb > 1)) { - /* - * 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) { - 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); - return true; - } - } - } -#endif - if (probe) { - return false; - } - - raise_mmu_exception(env, address, access_type, ret); - do_raise_exception_err(env, cs->exception_index, env->error_code, retaddr); -} - -hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address, - MMUAccessType access_type, uintptr_t retaddr) -{ - hwaddr physical; - int prot; - int ret = 0; - CPUState *cs = env_cpu(env); - - /* data access */ - ret = get_physical_address(env, &physical, &prot, address, access_type, - cpu_mmu_index(env, false)); - if (ret == TLBRET_MATCH) { - return physical; - } - - raise_mmu_exception(env, address, access_type, ret); - cpu_loop_exit_restore(cs, retaddr); -} - -static void set_hflags_for_handler(CPUMIPSState *env) -{ - /* Exception handlers are entered in 32-bit mode. */ - env->hflags &= ~(MIPS_HFLAG_M16); - /* ...except that microMIPS lets you choose. */ - if (env->insn_flags & ASE_MICROMIPS) { - env->hflags |= (!!(env->CP0_Config3 & - (1 << CP0C3_ISA_ON_EXC)) - << MIPS_HFLAG_M16_SHIFT); - } -} - -static inline void set_badinstr_registers(CPUMIPSState *env) -{ - if (env->insn_flags & ISA_NANOMIPS32) { - if (env->CP0_Config3 & (1 << CP0C3_BI)) { - uint32_t instr = (cpu_lduw_code(env, env->active_tc.PC)) << 16; - if ((instr & 0x10000000) == 0) { - instr |= cpu_lduw_code(env, env->active_tc.PC + 2); - } - env->CP0_BadInstr = instr; - - if ((instr & 0xFC000000) == 0x60000000) { - instr = cpu_lduw_code(env, env->active_tc.PC + 4) << 16; - env->CP0_BadInstrX = instr; - } - } - return; - } - - if (env->hflags & MIPS_HFLAG_M16) { - /* TODO: add BadInstr support for microMIPS */ - return; - } - if (env->CP0_Config3 & (1 << CP0C3_BI)) { - env->CP0_BadInstr = cpu_ldl_code(env, env->active_tc.PC); - } - if ((env->CP0_Config3 & (1 << CP0C3_BP)) && - (env->hflags & MIPS_HFLAG_BMASK)) { - env->CP0_BadInstrP = cpu_ldl_code(env, env->active_tc.PC - 4); - } -} - -void mips_cpu_do_interrupt(CPUState *cs) -{ - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; - bool update_badinstr = 0; - target_ulong offset; - int cause = -1; - - if (qemu_loglevel_mask(CPU_LOG_INT) - && cs->exception_index != EXCP_EXT_INTERRUPT) { - qemu_log("%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx - " %s exception\n", - __func__, env->active_tc.PC, env->CP0_EPC, - mips_exception_name(cs->exception_index)); - } - if (cs->exception_index == EXCP_EXT_INTERRUPT && - (env->hflags & MIPS_HFLAG_DM)) { - cs->exception_index = EXCP_DINT; - } - offset = 0x180; - switch (cs->exception_index) { - case EXCP_SEMIHOST: - cs->exception_index = EXCP_NONE; - mips_semihosting(env); - env->active_tc.PC += env->error_code; - return; - case EXCP_DSS: - env->CP0_Debug |= 1 << CP0DB_DSS; - /* - * Debug single step cannot be raised inside a delay slot and - * resume will always occur on the next instruction - * (but we assume the pc has always been updated during - * code translation). - */ - env->CP0_DEPC = env->active_tc.PC | !!(env->hflags & MIPS_HFLAG_M16); - goto enter_debug_mode; - case EXCP_DINT: - env->CP0_Debug |= 1 << CP0DB_DINT; - goto set_DEPC; - case EXCP_DIB: - env->CP0_Debug |= 1 << CP0DB_DIB; - goto set_DEPC; - case EXCP_DBp: - env->CP0_Debug |= 1 << CP0DB_DBp; - /* Setup DExcCode - SDBBP instruction */ - env->CP0_Debug = (env->CP0_Debug & ~(0x1fULL << CP0DB_DEC)) | - (9 << CP0DB_DEC); - goto set_DEPC; - case EXCP_DDBS: - env->CP0_Debug |= 1 << CP0DB_DDBS; - goto set_DEPC; - case EXCP_DDBL: - env->CP0_Debug |= 1 << CP0DB_DDBL; - set_DEPC: - env->CP0_DEPC = exception_resume_pc(env); - env->hflags &= ~MIPS_HFLAG_BMASK; - enter_debug_mode: - if (env->insn_flags & ISA_MIPS3) { - env->hflags |= MIPS_HFLAG_64; - if (!(env->insn_flags & ISA_MIPS_R6) || - env->CP0_Status & (1 << CP0St_KX)) { - env->hflags &= ~MIPS_HFLAG_AWRAP; - } - } - env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_CP0; - env->hflags &= ~(MIPS_HFLAG_KSU); - /* EJTAG probe trap enable is not implemented... */ - if (!(env->CP0_Status & (1 << CP0St_EXL))) { - env->CP0_Cause &= ~(1U << CP0Ca_BD); - } - env->active_tc.PC = env->exception_base + 0x480; - set_hflags_for_handler(env); - break; - case EXCP_RESET: - cpu_reset(CPU(cpu)); - break; - case EXCP_SRESET: - env->CP0_Status |= (1 << CP0St_SR); - memset(env->CP0_WatchLo, 0, sizeof(env->CP0_WatchLo)); - goto set_error_EPC; - case EXCP_NMI: - env->CP0_Status |= (1 << CP0St_NMI); - set_error_EPC: - env->CP0_ErrorEPC = exception_resume_pc(env); - env->hflags &= ~MIPS_HFLAG_BMASK; - env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV); - if (env->insn_flags & ISA_MIPS3) { - env->hflags |= MIPS_HFLAG_64; - if (!(env->insn_flags & ISA_MIPS_R6) || - env->CP0_Status & (1 << CP0St_KX)) { - env->hflags &= ~MIPS_HFLAG_AWRAP; - } - } - env->hflags |= MIPS_HFLAG_CP0; - env->hflags &= ~(MIPS_HFLAG_KSU); - if (!(env->CP0_Status & (1 << CP0St_EXL))) { - env->CP0_Cause &= ~(1U << CP0Ca_BD); - } - env->active_tc.PC = env->exception_base; - set_hflags_for_handler(env); - break; - case EXCP_EXT_INTERRUPT: - cause = 0; - if (env->CP0_Cause & (1 << CP0Ca_IV)) { - uint32_t spacing = (env->CP0_IntCtl >> CP0IntCtl_VS) & 0x1f; - - if ((env->CP0_Status & (1 << CP0St_BEV)) || spacing == 0) { - offset = 0x200; - } else { - uint32_t vector = 0; - uint32_t pending = (env->CP0_Cause & CP0Ca_IP_mask) >> CP0Ca_IP; - - if (env->CP0_Config3 & (1 << CP0C3_VEIC)) { - /* - * For VEIC mode, the external interrupt controller feeds - * the vector through the CP0Cause IP lines. - */ - vector = pending; - } else { - /* - * Vectored Interrupts - * Mask with Status.IM7-IM0 to get enabled interrupts. - */ - pending &= (env->CP0_Status >> CP0St_IM) & 0xff; - /* Find the highest-priority interrupt. */ - while (pending >>= 1) { - vector++; - } - } - offset = 0x200 + (vector * (spacing << 5)); - } - } - goto set_EPC; - case EXCP_LTLBL: - cause = 1; - update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); - goto set_EPC; - case EXCP_TLBL: - cause = 2; - update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); - if ((env->error_code & EXCP_TLB_NOMATCH) && - !(env->CP0_Status & (1 << CP0St_EXL))) { -#if defined(TARGET_MIPS64) - int R = env->CP0_BadVAddr >> 62; - int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; - int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; - - if ((R != 0 || UX) && (R != 3 || KX) && - (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) { - offset = 0x080; - } else { -#endif - offset = 0x000; -#if defined(TARGET_MIPS64) - } -#endif - } - goto set_EPC; - case EXCP_TLBS: - cause = 3; - update_badinstr = 1; - if ((env->error_code & EXCP_TLB_NOMATCH) && - !(env->CP0_Status & (1 << CP0St_EXL))) { -#if defined(TARGET_MIPS64) - int R = env->CP0_BadVAddr >> 62; - int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; - int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; - - if ((R != 0 || UX) && (R != 3 || KX) && - (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) { - offset = 0x080; - } else { -#endif - offset = 0x000; -#if defined(TARGET_MIPS64) - } -#endif - } - goto set_EPC; - case EXCP_AdEL: - cause = 4; - update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); - goto set_EPC; - case EXCP_AdES: - cause = 5; - update_badinstr = 1; - goto set_EPC; - case EXCP_IBE: - cause = 6; - goto set_EPC; - case EXCP_DBE: - cause = 7; - goto set_EPC; - case EXCP_SYSCALL: - cause = 8; - update_badinstr = 1; - goto set_EPC; - case EXCP_BREAK: - cause = 9; - update_badinstr = 1; - goto set_EPC; - case EXCP_RI: - cause = 10; - update_badinstr = 1; - goto set_EPC; - case EXCP_CpU: - cause = 11; - update_badinstr = 1; - env->CP0_Cause = (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) | - (env->error_code << CP0Ca_CE); - goto set_EPC; - case EXCP_OVERFLOW: - cause = 12; - update_badinstr = 1; - goto set_EPC; - case EXCP_TRAP: - cause = 13; - update_badinstr = 1; - goto set_EPC; - case EXCP_MSAFPE: - cause = 14; - update_badinstr = 1; - goto set_EPC; - case EXCP_FPE: - cause = 15; - update_badinstr = 1; - goto set_EPC; - case EXCP_C2E: - cause = 18; - goto set_EPC; - case EXCP_TLBRI: - cause = 19; - update_badinstr = 1; - goto set_EPC; - case EXCP_TLBXI: - cause = 20; - goto set_EPC; - case EXCP_MSADIS: - cause = 21; - update_badinstr = 1; - goto set_EPC; - case EXCP_MDMX: - cause = 22; - goto set_EPC; - case EXCP_DWATCH: - cause = 23; - /* XXX: TODO: manage deferred watch exceptions */ - goto set_EPC; - case EXCP_MCHECK: - cause = 24; - goto set_EPC; - case EXCP_THREAD: - cause = 25; - goto set_EPC; - case EXCP_DSPDIS: - cause = 26; - goto set_EPC; - case EXCP_CACHE: - cause = 30; - offset = 0x100; - set_EPC: - if (!(env->CP0_Status & (1 << CP0St_EXL))) { - env->CP0_EPC = exception_resume_pc(env); - if (update_badinstr) { - set_badinstr_registers(env); - } - if (env->hflags & MIPS_HFLAG_BMASK) { - env->CP0_Cause |= (1U << CP0Ca_BD); - } else { - env->CP0_Cause &= ~(1U << CP0Ca_BD); - } - env->CP0_Status |= (1 << CP0St_EXL); - if (env->insn_flags & ISA_MIPS3) { - env->hflags |= MIPS_HFLAG_64; - if (!(env->insn_flags & ISA_MIPS_R6) || - env->CP0_Status & (1 << CP0St_KX)) { - env->hflags &= ~MIPS_HFLAG_AWRAP; - } - } - env->hflags |= MIPS_HFLAG_CP0; - env->hflags &= ~(MIPS_HFLAG_KSU); - } - env->hflags &= ~MIPS_HFLAG_BMASK; - if (env->CP0_Status & (1 << CP0St_BEV)) { - env->active_tc.PC = env->exception_base + 0x200; - } else if (cause == 30 && !(env->CP0_Config3 & (1 << CP0C3_SC) && - env->CP0_Config5 & (1 << CP0C5_CV))) { - /* Force KSeg1 for cache errors */ - env->active_tc.PC = KSEG1_BASE | (env->CP0_EBase & 0x1FFFF000); - } else { - env->active_tc.PC = env->CP0_EBase & ~0xfff; - } - - env->active_tc.PC += offset; - set_hflags_for_handler(env); - env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | - (cause << CP0Ca_EC); - break; - default: - abort(); - } - if (qemu_loglevel_mask(CPU_LOG_INT) - && cs->exception_index != EXCP_EXT_INTERRUPT) { - qemu_log("%s: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " cause %d\n" - " S %08x C %08x A " TARGET_FMT_lx " D " TARGET_FMT_lx "\n", - __func__, env->active_tc.PC, env->CP0_EPC, cause, - env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr, - env->CP0_DEPC); - } - cs->exception_index = EXCP_NONE; -} - -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; - - if (cpu_mips_hw_interrupts_enabled(env) && - cpu_mips_hw_interrupts_pending(env)) { - /* Raise it */ - cs->exception_index = EXCP_EXT_INTERRUPT; - env->error_code = 0; - mips_cpu_do_interrupt(cs); - return true; - } - } - return false; -} - -void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra) -{ - CPUState *cs = env_cpu(env); - r4k_tlb_t *tlb; - target_ulong addr; - target_ulong end; - uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; - uint32_t MMID = env->CP0_MemoryMapID; - bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); - uint32_t tlb_mmid; - target_ulong mask; - - MMID = mi ? MMID : (uint32_t) ASID; - - tlb = &env->tlb->mmu.r4k.tlb[idx]; - /* - * The qemu TLB is flushed when the ASID/MMID changes, so no need to - * flush these entries again. - */ - tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; - if (tlb->G == 0 && tlb_mmid != MMID) { - return; - } - - if (use_extra && env->tlb->tlb_in_use < MIPS_TLB_MAX) { - /* - * For tlbwr, we can shadow the discarded entry into - * a new (fake) TLB entry, as long as the guest can not - * tell that it's there. - */ - env->tlb->mmu.r4k.tlb[env->tlb->tlb_in_use] = *tlb; - env->tlb->tlb_in_use++; - return; - } - - /* 1k pages are not supported. */ - mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); - if (tlb->V0) { - addr = tlb->VPN & ~mask; -#if defined(TARGET_MIPS64) - if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { - addr |= 0x3FFFFF0000000000ULL; - } -#endif - end = addr | (mask >> 1); - while (addr < end) { - tlb_flush_page(cs, addr); - addr += TARGET_PAGE_SIZE; - } - } - if (tlb->V1) { - addr = (tlb->VPN & ~mask) | ((mask >> 1) + 1); -#if defined(TARGET_MIPS64) - if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { - addr |= 0x3FFFFF0000000000ULL; - } -#endif - end = addr | mask; - while (addr - 1 < end) { - tlb_flush_page(cs, addr); - addr += TARGET_PAGE_SIZE; - } - } -} diff --git a/target/mips/tcg/sysemu_helper.h.inc b/target/mips/tcg/sysemu_helper.h.inc deleted file mode 100644 index f163af1eac7..00000000000 --- a/target/mips/tcg/sysemu_helper.h.inc +++ /dev/null @@ -1,191 +0,0 @@ -/* - * QEMU MIPS sysemu helpers - * - * Copyright (c) 2004-2005 Jocelyn Mayer - * Copyright (c) 2006 Marius Groeger (FPU operations) - * Copyright (c) 2006 Thiemo Seufer (MIPS32R2 support) - * Copyright (c) 2009 CodeSourcery (MIPS16 and microMIPS support) - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -/* CP0 helpers */ -DEF_HELPER_1(mfc0_mvpcontrol, tl, env) -DEF_HELPER_1(mfc0_mvpconf0, tl, env) -DEF_HELPER_1(mfc0_mvpconf1, tl, env) -DEF_HELPER_1(mftc0_vpecontrol, tl, env) -DEF_HELPER_1(mftc0_vpeconf0, tl, env) -DEF_HELPER_1(mfc0_random, tl, env) -DEF_HELPER_1(mfc0_tcstatus, tl, env) -DEF_HELPER_1(mftc0_tcstatus, tl, env) -DEF_HELPER_1(mfc0_tcbind, tl, env) -DEF_HELPER_1(mftc0_tcbind, tl, env) -DEF_HELPER_1(mfc0_tcrestart, tl, env) -DEF_HELPER_1(mftc0_tcrestart, tl, env) -DEF_HELPER_1(mfc0_tchalt, tl, env) -DEF_HELPER_1(mftc0_tchalt, tl, env) -DEF_HELPER_1(mfc0_tccontext, tl, env) -DEF_HELPER_1(mftc0_tccontext, tl, env) -DEF_HELPER_1(mfc0_tcschedule, tl, env) -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) -DEF_HELPER_1(mftc0_epc, tl, env) -DEF_HELPER_1(mftc0_ebase, tl, env) -DEF_HELPER_2(mftc0_configx, tl, env, tl) -DEF_HELPER_1(mfc0_lladdr, tl, env) -DEF_HELPER_1(mfc0_maar, tl, env) -DEF_HELPER_1(mfhc0_maar, tl, env) -DEF_HELPER_2(mfc0_watchlo, tl, env, i32) -DEF_HELPER_2(mfc0_watchhi, tl, env, i32) -DEF_HELPER_2(mfhc0_watchhi, tl, env, i32) -DEF_HELPER_1(mfc0_debug, tl, env) -DEF_HELPER_1(mftc0_debug, tl, env) -#ifdef TARGET_MIPS64 -DEF_HELPER_1(dmfc0_tcrestart, tl, env) -DEF_HELPER_1(dmfc0_tchalt, tl, env) -DEF_HELPER_1(dmfc0_tccontext, tl, env) -DEF_HELPER_1(dmfc0_tcschedule, tl, env) -DEF_HELPER_1(dmfc0_tcschefback, tl, env) -DEF_HELPER_1(dmfc0_lladdr, tl, env) -DEF_HELPER_1(dmfc0_maar, tl, env) -DEF_HELPER_2(dmfc0_watchlo, tl, env, i32) -DEF_HELPER_2(dmfc0_watchhi, tl, env, i32) -DEF_HELPER_1(dmfc0_saar, tl, env) -#endif /* TARGET_MIPS64 */ - -DEF_HELPER_2(mtc0_index, void, env, tl) -DEF_HELPER_2(mtc0_mvpcontrol, void, env, tl) -DEF_HELPER_2(mtc0_vpecontrol, void, env, tl) -DEF_HELPER_2(mttc0_vpecontrol, void, env, tl) -DEF_HELPER_2(mtc0_vpeconf0, void, env, tl) -DEF_HELPER_2(mttc0_vpeconf0, void, env, tl) -DEF_HELPER_2(mtc0_vpeconf1, void, env, tl) -DEF_HELPER_2(mtc0_yqmask, void, env, tl) -DEF_HELPER_2(mtc0_vpeopt, void, env, tl) -DEF_HELPER_2(mtc0_entrylo0, void, env, tl) -DEF_HELPER_2(mtc0_tcstatus, void, env, tl) -DEF_HELPER_2(mttc0_tcstatus, void, env, tl) -DEF_HELPER_2(mtc0_tcbind, void, env, tl) -DEF_HELPER_2(mttc0_tcbind, void, env, tl) -DEF_HELPER_2(mtc0_tcrestart, void, env, tl) -DEF_HELPER_2(mttc0_tcrestart, void, env, tl) -DEF_HELPER_2(mtc0_tchalt, void, env, tl) -DEF_HELPER_2(mttc0_tchalt, void, env, tl) -DEF_HELPER_2(mtc0_tccontext, void, env, tl) -DEF_HELPER_2(mttc0_tccontext, void, env, tl) -DEF_HELPER_2(mtc0_tcschedule, void, env, tl) -DEF_HELPER_2(mttc0_tcschedule, void, env, tl) -DEF_HELPER_2(mtc0_tcschefback, void, env, tl) -DEF_HELPER_2(mttc0_tcschefback, void, env, tl) -DEF_HELPER_2(mtc0_entrylo1, void, env, tl) -DEF_HELPER_2(mtc0_context, void, env, tl) -DEF_HELPER_2(mtc0_memorymapid, void, env, tl) -DEF_HELPER_2(mtc0_pagemask, void, env, tl) -DEF_HELPER_2(mtc0_pagegrain, void, env, tl) -DEF_HELPER_2(mtc0_segctl0, void, env, tl) -DEF_HELPER_2(mtc0_segctl1, void, env, tl) -DEF_HELPER_2(mtc0_segctl2, void, env, tl) -DEF_HELPER_2(mtc0_pwfield, void, env, tl) -DEF_HELPER_2(mtc0_pwsize, void, env, tl) -DEF_HELPER_2(mtc0_wired, void, env, tl) -DEF_HELPER_2(mtc0_srsconf0, void, env, tl) -DEF_HELPER_2(mtc0_srsconf1, void, env, tl) -DEF_HELPER_2(mtc0_srsconf2, void, env, tl) -DEF_HELPER_2(mtc0_srsconf3, void, env, tl) -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) -DEF_HELPER_2(mtc0_status, void, env, tl) -DEF_HELPER_2(mttc0_status, void, env, tl) -DEF_HELPER_2(mtc0_intctl, void, env, tl) -DEF_HELPER_2(mtc0_srsctl, void, env, tl) -DEF_HELPER_2(mtc0_cause, void, env, tl) -DEF_HELPER_2(mttc0_cause, void, env, tl) -DEF_HELPER_2(mtc0_ebase, void, env, tl) -DEF_HELPER_2(mttc0_ebase, void, env, tl) -DEF_HELPER_2(mtc0_config0, void, env, tl) -DEF_HELPER_2(mtc0_config2, void, env, tl) -DEF_HELPER_2(mtc0_config3, void, env, tl) -DEF_HELPER_2(mtc0_config4, void, env, tl) -DEF_HELPER_2(mtc0_config5, void, env, tl) -DEF_HELPER_2(mtc0_lladdr, void, env, tl) -DEF_HELPER_2(mtc0_maar, void, env, tl) -DEF_HELPER_2(mthc0_maar, void, env, tl) -DEF_HELPER_2(mtc0_maari, void, env, tl) -DEF_HELPER_3(mtc0_watchlo, void, env, tl, i32) -DEF_HELPER_3(mtc0_watchhi, void, env, tl, i32) -DEF_HELPER_3(mthc0_watchhi, void, env, tl, i32) -DEF_HELPER_2(mtc0_xcontext, void, env, tl) -DEF_HELPER_2(mtc0_framemask, void, env, tl) -DEF_HELPER_2(mtc0_debug, void, env, tl) -DEF_HELPER_2(mttc0_debug, void, env, tl) -DEF_HELPER_2(mtc0_performance0, void, env, tl) -DEF_HELPER_2(mtc0_errctl, void, env, tl) -DEF_HELPER_2(mtc0_taglo, void, env, tl) -DEF_HELPER_2(mtc0_datalo, void, env, tl) -DEF_HELPER_2(mtc0_taghi, void, env, tl) -DEF_HELPER_2(mtc0_datahi, void, env, tl) - -#if defined(TARGET_MIPS64) -DEF_HELPER_2(dmtc0_entrylo0, void, env, i64) -DEF_HELPER_2(dmtc0_entrylo1, void, env, i64) -#endif - -/* MIPS MT functions */ -DEF_HELPER_2(mftgpr, tl, env, i32) -DEF_HELPER_2(mftlo, tl, env, i32) -DEF_HELPER_2(mfthi, tl, env, i32) -DEF_HELPER_2(mftacx, tl, env, i32) -DEF_HELPER_1(mftdsp, tl, env) -DEF_HELPER_3(mttgpr, void, env, tl, i32) -DEF_HELPER_3(mttlo, void, env, tl, i32) -DEF_HELPER_3(mtthi, void, env, tl, i32) -DEF_HELPER_3(mttacx, void, env, tl, i32) -DEF_HELPER_2(mttdsp, void, env, tl) -DEF_HELPER_0(dmt, tl) -DEF_HELPER_0(emt, tl) -DEF_HELPER_1(dvpe, tl, env) -DEF_HELPER_1(evpe, tl, env) - -/* R6 Multi-threading */ -DEF_HELPER_1(dvp, tl, env) -DEF_HELPER_1(evp, tl, env) - -/* TLB */ -DEF_HELPER_1(tlbwi, void, env) -DEF_HELPER_1(tlbwr, void, env) -DEF_HELPER_1(tlbp, void, env) -DEF_HELPER_1(tlbr, void, env) -DEF_HELPER_1(tlbinv, void, env) -DEF_HELPER_1(tlbinvf, void, env) -DEF_HELPER_3(ginvt, void, env, tl, i32) - -/* Special */ -DEF_HELPER_1(di, tl, env) -DEF_HELPER_1(ei, tl, env) -DEF_HELPER_1(eret, void, env) -DEF_HELPER_1(eretnc, void, env) -DEF_HELPER_1(deret, void, env) -DEF_HELPER_3(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/sysemu/cp0_helper.c b/target/mips/tcg/system/cp0_helper.c similarity index 95% rename from target/mips/tcg/sysemu/cp0_helper.c rename to target/mips/tcg/system/cp0_helper.c index 5da11245892..101b1e65fdd 100644 --- a/target/mips/tcg/sysemu/cp0_helper.c +++ b/target/mips/tcg/system/cp0_helper.c @@ -27,7 +27,8 @@ #include "internal.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "exec/target_page.h" /* SMP helpers. */ @@ -58,9 +59,9 @@ static inline void mips_vpe_wake(MIPSCPU *c) * because there might be other conditions that state that c should * be sleeping. */ - qemu_mutex_lock_iothread(); + bql_lock(); cpu_interrupt(CPU(c), CPU_INTERRUPT_WAKE); - qemu_mutex_unlock_iothread(); + bql_unlock(); } static inline void mips_vpe_sleep(MIPSCPU *cpu) @@ -370,22 +371,6 @@ target_ulong helper_mfc0_count(CPUMIPSState *env) return (int32_t)cpu_mips_get_count(env); } -target_ulong helper_mfc0_saar(CPUMIPSState *env) -{ - if ((env->CP0_SAARI & 0x3f) < 2) { - return (int32_t) env->CP0_SAAR[env->CP0_SAARI & 0x3f]; - } - return 0; -} - -target_ulong helper_mfhc0_saar(CPUMIPSState *env) -{ - if ((env->CP0_SAARI & 0x3f) < 2) { - return env->CP0_SAAR[env->CP0_SAARI & 0x3f] >> 32; - } - return 0; -} - target_ulong helper_mftc0_entryhi(CPUMIPSState *env) { int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); @@ -513,13 +498,6 @@ target_ulong helper_dmfc0_watchhi(CPUMIPSState *env, uint32_t sel) return env->CP0_WatchHi[sel]; } -target_ulong helper_dmfc0_saar(CPUMIPSState *env) -{ - if ((env->CP0_SAARI & 0x3f) < 2) { - return env->CP0_SAAR[env->CP0_SAARI & 0x3f]; - } - return 0; -} #endif /* TARGET_MIPS64 */ void helper_mtc0_index(CPUMIPSState *env, target_ulong arg1) @@ -887,36 +865,24 @@ void helper_mtc0_memorymapid(CPUMIPSState *env, target_ulong arg1) } } -void update_pagemask(CPUMIPSState *env, target_ulong arg1, int32_t *pagemask) +uint32_t compute_pagemask(uint32_t val) { - uint32_t mask; - int maskbits; - /* Don't care MASKX as we don't support 1KB page */ - mask = extract32((uint32_t)arg1, CP0PM_MASK, 16); - maskbits = cto32(mask); + uint32_t mask = extract32(val, CP0PM_MASK, 16); + int maskbits = cto32(mask); - /* Ensure no more set bit after first zero */ - if ((mask >> maskbits) != 0) { - goto invalid; - } - /* We don't support VTLB entry smaller than target page */ - if ((maskbits + TARGET_PAGE_BITS_MIN) < TARGET_PAGE_BITS) { - goto invalid; + /* Ensure no more set bit after first zero, and maskbits even. */ + if ((mask >> maskbits) == 0 && maskbits % 2 == 0) { + return mask << CP0PM_MASK; + } else { + /* When invalid, set to default target page size. */ + return 0; } - env->CP0_PageMask = mask << CP0PM_MASK; - - return; - -invalid: - /* When invalid, set to default target page size. */ - mask = (~TARGET_PAGE_MASK >> TARGET_PAGE_BITS_MIN); - env->CP0_PageMask = mask << CP0PM_MASK; } void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1) { - update_pagemask(env, arg1, &env->CP0_PageMask); + env->CP0_PageMask = compute_pagemask(arg1); } void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1) @@ -1099,46 +1065,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 +1127,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/system/lcsr_helper.c similarity index 100% rename from target/mips/tcg/sysemu/lcsr_helper.c rename to target/mips/tcg/system/lcsr_helper.c diff --git a/target/mips/tcg/system/meson.build b/target/mips/tcg/system/meson.build new file mode 100644 index 00000000000..911341ac375 --- /dev/null +++ b/target/mips/tcg/system/meson.build @@ -0,0 +1,12 @@ +mips_system_ss.add(files( + 'cp0_helper.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/system/mips-semi.c similarity index 83% rename from target/mips/tcg/sysemu/mips-semi.c rename to target/mips/tcg/system/mips-semi.c index f3735df7b9e..e822a426145 100644 --- a/target/mips/tcg/sysemu/mips-semi.c +++ b/target/mips/tcg/system/mips-semi.c @@ -22,7 +22,7 @@ #include "qemu/log.h" #include "gdbstub/syscalls.h" #include "gdbstub/helpers.h" -#include "semihosting/softmmu-uaccess.h" +#include "semihosting/uaccess.h" #include "semihosting/semihost.h" #include "semihosting/console.h" #include "semihosting/syscalls.h" @@ -126,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 @@ -167,7 +167,8 @@ 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); + bool swap_needed = HOST_BIG_ENDIAN != mips_env_is_bigendian(env); target_ulong addr = env->active_tc.gpr[5]; UHIStat *dst = lock_user(VERIFY_WRITE, addr, sizeof(UHIStat), 1); struct gdb_stat s; @@ -179,19 +180,35 @@ static void uhi_fstat_cb(CPUState *cs, uint64_t ret, int err) memcpy(&s, dst, sizeof(struct gdb_stat)); memset(dst, 0, sizeof(UHIStat)); - dst->uhi_st_dev = tswap16(be32_to_cpu(s.gdb_st_dev)); - dst->uhi_st_ino = tswap16(be32_to_cpu(s.gdb_st_ino)); - dst->uhi_st_mode = tswap32(be32_to_cpu(s.gdb_st_mode)); - dst->uhi_st_nlink = tswap16(be32_to_cpu(s.gdb_st_nlink)); - dst->uhi_st_uid = tswap16(be32_to_cpu(s.gdb_st_uid)); - dst->uhi_st_gid = tswap16(be32_to_cpu(s.gdb_st_gid)); - dst->uhi_st_rdev = tswap16(be32_to_cpu(s.gdb_st_rdev)); - dst->uhi_st_size = tswap64(be64_to_cpu(s.gdb_st_size)); - dst->uhi_st_atime = tswap64(be32_to_cpu(s.gdb_st_atime)); - dst->uhi_st_mtime = tswap64(be32_to_cpu(s.gdb_st_mtime)); - dst->uhi_st_ctime = tswap64(be32_to_cpu(s.gdb_st_ctime)); - dst->uhi_st_blksize = tswap64(be64_to_cpu(s.gdb_st_blksize)); - dst->uhi_st_blocks = tswap64(be64_to_cpu(s.gdb_st_blocks)); + dst->uhi_st_dev = be32_to_cpu(s.gdb_st_dev); + dst->uhi_st_ino = be32_to_cpu(s.gdb_st_ino); + dst->uhi_st_mode = be32_to_cpu(s.gdb_st_mode); + dst->uhi_st_nlink = be32_to_cpu(s.gdb_st_nlink); + dst->uhi_st_uid = be32_to_cpu(s.gdb_st_uid); + dst->uhi_st_gid = be32_to_cpu(s.gdb_st_gid); + dst->uhi_st_rdev = be32_to_cpu(s.gdb_st_rdev); + dst->uhi_st_size = be64_to_cpu(s.gdb_st_size); + dst->uhi_st_atime = be32_to_cpu(s.gdb_st_atime); + dst->uhi_st_mtime = be32_to_cpu(s.gdb_st_mtime); + dst->uhi_st_ctime = be32_to_cpu(s.gdb_st_ctime); + dst->uhi_st_blksize = be64_to_cpu(s.gdb_st_blksize); + dst->uhi_st_blocks = be64_to_cpu(s.gdb_st_blocks); + + if (swap_needed) { + dst->uhi_st_dev = bswap16(dst->uhi_st_dev); + dst->uhi_st_ino = bswap16(dst->uhi_st_ino); + dst->uhi_st_mode = bswap32(dst->uhi_st_mode); + dst->uhi_st_nlink = bswap16(dst->uhi_st_nlink); + dst->uhi_st_uid = bswap16(dst->uhi_st_uid); + dst->uhi_st_gid = bswap16(dst->uhi_st_gid); + dst->uhi_st_rdev = bswap16(dst->uhi_st_rdev); + dst->uhi_st_size = bswap64(dst->uhi_st_size); + dst->uhi_st_atime = bswap64(dst->uhi_st_atime); + dst->uhi_st_mtime = bswap64(dst->uhi_st_mtime); + dst->uhi_st_ctime = bswap64(dst->uhi_st_ctime); + dst->uhi_st_blksize = bswap64(dst->uhi_st_blksize); + dst->uhi_st_blocks = bswap64(dst->uhi_st_blocks); + } unlock_user(dst, addr, sizeof(UHIStat)); } @@ -357,5 +374,4 @@ void mips_semihosting(CPUMIPSState *env) error_report("Unknown UHI operation %d", op); abort(); } - return; } diff --git a/target/mips/tcg/system/semihosting-stub.c b/target/mips/tcg/system/semihosting-stub.c new file mode 100644 index 00000000000..bb1f7aae624 --- /dev/null +++ b/target/mips/tcg/system/semihosting-stub.c @@ -0,0 +1,16 @@ +/* + * MIPS semihosting stub + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) 2024 Linaro Ltd. + * Authors: + * Philippe Mathieu-Daudé + */ + +#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/system/special_helper.c similarity index 95% rename from target/mips/tcg/sysemu/special_helper.c rename to target/mips/tcg/system/special_helper.c index 93276f789d7..b54cbe88a38 100644 --- a/target/mips/tcg/sysemu/special_helper.c +++ b/target/mips/tcg/system/special_helper.c @@ -22,7 +22,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" +#include "exec/translation-block.h" #include "internal.h" /* Specials */ @@ -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 - && !(cs->tcg_cflags & CF_PCREL) && env->active_tc.PC != tb->pc) { + && !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/system/tlb_helper.c b/target/mips/tcg/system/tlb_helper.c new file mode 100644 index 00000000000..eccaf3624cb --- /dev/null +++ b/target/mips/tcg/system/tlb_helper.c @@ -0,0 +1,1422 @@ +/* + * MIPS TLB (Translation lookaside buffer) helpers. + * + * Copyright (c) 2004-2005 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 "qemu/bitops.h" + +#include "cpu.h" +#include "internal.h" +#include "exec/cputlb.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" +#include "accel/tcg/cpu-ldst.h" +#include "exec/log.h" +#include "exec/helper-proto.h" + +/* TLB management */ +static void r4k_mips_tlb_flush_extra(CPUMIPSState *env, int first) +{ + /* Discard entries from env->tlb[first] onwards. */ + while (env->tlb->tlb_in_use > first) { + r4k_invalidate_tlb(env, --env->tlb->tlb_in_use, 0); + } +} + +static inline uint64_t get_tlb_pfn_from_entrylo(uint64_t entrylo) +{ +#if defined(TARGET_MIPS64) + return extract64(entrylo, 6, 54); +#else + return extract64(entrylo, 6, 24) | /* PFN */ + (extract64(entrylo, 32, 32) << 24); /* PFNX */ +#endif +} + +static void r4k_fill_tlb(CPUMIPSState *env, int idx) +{ + r4k_tlb_t *tlb; + uint64_t mask = env->CP0_PageMask >> (TARGET_PAGE_BITS + 1); + + /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */ + tlb = &env->tlb->mmu.r4k.tlb[idx]; + if (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) { + tlb->EHINV = 1; + return; + } + tlb->EHINV = 0; + tlb->VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1); +#if defined(TARGET_MIPS64) + tlb->VPN &= env->SEGMask; +#endif + tlb->ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + tlb->MMID = env->CP0_MemoryMapID; + tlb->PageMask = env->CP0_PageMask; + tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; + tlb->V0 = (env->CP0_EntryLo0 & 2) != 0; + tlb->D0 = (env->CP0_EntryLo0 & 4) != 0; + tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7; + tlb->XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) & 1; + tlb->RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) & 1; + tlb->PFN[0] = (get_tlb_pfn_from_entrylo(env->CP0_EntryLo0) & ~mask) << 12; + tlb->V1 = (env->CP0_EntryLo1 & 2) != 0; + tlb->D1 = (env->CP0_EntryLo1 & 4) != 0; + tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7; + tlb->XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) & 1; + tlb->RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) & 1; + tlb->PFN[1] = (get_tlb_pfn_from_entrylo(env->CP0_EntryLo1) & ~mask) << 12; +} + +static void r4k_helper_tlbinv(CPUMIPSState *env) +{ + bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); + uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + uint32_t MMID = env->CP0_MemoryMapID; + uint32_t tlb_mmid; + r4k_tlb_t *tlb; + int idx; + + MMID = mi ? MMID : (uint32_t) ASID; + for (idx = 0; idx < env->tlb->nb_tlb; idx++) { + tlb = &env->tlb->mmu.r4k.tlb[idx]; + tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; + if (!tlb->G && tlb_mmid == MMID) { + tlb->EHINV = 1; + } + } + cpu_mips_tlb_flush(env); +} + +static void r4k_helper_tlbinvf(CPUMIPSState *env) +{ + int idx; + + for (idx = 0; idx < env->tlb->nb_tlb; idx++) { + env->tlb->mmu.r4k.tlb[idx].EHINV = 1; + } + cpu_mips_tlb_flush(env); +} + +static void r4k_helper_tlbwi(CPUMIPSState *env) +{ + bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); + target_ulong VPN; + uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + uint32_t MMID = env->CP0_MemoryMapID; + uint32_t tlb_mmid; + bool EHINV, G, V0, D0, V1, D1, XI0, XI1, RI0, RI1; + r4k_tlb_t *tlb; + int idx; + + MMID = mi ? MMID : (uint32_t) ASID; + + idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; + tlb = &env->tlb->mmu.r4k.tlb[idx]; + VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1); +#if defined(TARGET_MIPS64) + VPN &= env->SEGMask; +#endif + EHINV = (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) != 0; + G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; + V0 = (env->CP0_EntryLo0 & 2) != 0; + D0 = (env->CP0_EntryLo0 & 4) != 0; + XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) &1; + RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) &1; + V1 = (env->CP0_EntryLo1 & 2) != 0; + D1 = (env->CP0_EntryLo1 & 4) != 0; + XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) &1; + RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) &1; + + tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; + /* + * Discard cached TLB entries, unless tlbwi is just upgrading access + * permissions on the current entry. + */ + if (tlb->VPN != VPN || tlb_mmid != MMID || tlb->G != G || + (!tlb->EHINV && EHINV) || + (tlb->V0 && !V0) || (tlb->D0 && !D0) || + (!tlb->XI0 && XI0) || (!tlb->RI0 && RI0) || + (tlb->V1 && !V1) || (tlb->D1 && !D1) || + (!tlb->XI1 && XI1) || (!tlb->RI1 && RI1)) { + r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb); + } + + r4k_invalidate_tlb(env, idx, 0); + r4k_fill_tlb(env, idx); +} + +static void r4k_helper_tlbwr(CPUMIPSState *env) +{ + int r = cpu_mips_get_random(env); + + r4k_invalidate_tlb(env, r, 1); + r4k_fill_tlb(env, r); +} + +static void r4k_helper_tlbp(CPUMIPSState *env) +{ + bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); + r4k_tlb_t *tlb; + target_ulong mask; + target_ulong tag; + target_ulong VPN; + uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + uint32_t MMID = env->CP0_MemoryMapID; + uint32_t tlb_mmid; + int i; + + MMID = mi ? MMID : (uint32_t) ASID; + for (i = 0; i < env->tlb->nb_tlb; i++) { + tlb = &env->tlb->mmu.r4k.tlb[i]; + /* 1k pages are not supported. */ + mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); + tag = env->CP0_EntryHi & ~mask; + VPN = tlb->VPN & ~mask; +#if defined(TARGET_MIPS64) + tag &= env->SEGMask; +#endif + tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; + /* Check ASID/MMID, virtual page number & size */ + if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag && !tlb->EHINV) { + /* TLB match */ + env->CP0_Index = i; + break; + } + } + if (i == env->tlb->nb_tlb) { + /* No match. Discard any shadow entries, if any of them match. */ + for (i = env->tlb->nb_tlb; i < env->tlb->tlb_in_use; i++) { + tlb = &env->tlb->mmu.r4k.tlb[i]; + /* 1k pages are not supported. */ + mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); + tag = env->CP0_EntryHi & ~mask; + VPN = tlb->VPN & ~mask; +#if defined(TARGET_MIPS64) + tag &= env->SEGMask; +#endif + tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; + /* Check ASID/MMID, virtual page number & size */ + if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag) { + r4k_mips_tlb_flush_extra(env, i); + break; + } + } + + env->CP0_Index |= 0x80000000; + } +} + +static inline uint64_t get_entrylo_pfn_from_tlb(uint64_t tlb_pfn) +{ +#if defined(TARGET_MIPS64) + return tlb_pfn << 6; +#else + return (extract64(tlb_pfn, 0, 24) << 6) | /* PFN */ + (extract64(tlb_pfn, 24, 32) << 32); /* PFNX */ +#endif +} + +static void r4k_helper_tlbr(CPUMIPSState *env) +{ + bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); + uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + uint32_t MMID = env->CP0_MemoryMapID; + uint32_t tlb_mmid; + r4k_tlb_t *tlb; + int idx; + + MMID = mi ? MMID : (uint32_t) ASID; + idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; + tlb = &env->tlb->mmu.r4k.tlb[idx]; + + tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; + /* If this will change the current ASID/MMID, flush qemu's TLB. */ + if (MMID != tlb_mmid) { + cpu_mips_tlb_flush(env); + } + + r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb); + + if (tlb->EHINV) { + env->CP0_EntryHi = 1 << CP0EnHi_EHINV; + env->CP0_PageMask = 0; + env->CP0_EntryLo0 = 0; + env->CP0_EntryLo1 = 0; + } else { + env->CP0_EntryHi = mi ? tlb->VPN : tlb->VPN | tlb->ASID; + env->CP0_MemoryMapID = tlb->MMID; + env->CP0_PageMask = tlb->PageMask; + env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) | + ((uint64_t)tlb->RI0 << CP0EnLo_RI) | + ((uint64_t)tlb->XI0 << CP0EnLo_XI) | (tlb->C0 << 3) | + get_entrylo_pfn_from_tlb(tlb->PFN[0] >> 12); + env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) | + ((uint64_t)tlb->RI1 << CP0EnLo_RI) | + ((uint64_t)tlb->XI1 << CP0EnLo_XI) | (tlb->C1 << 3) | + get_entrylo_pfn_from_tlb(tlb->PFN[1] >> 12); + } +} + +void helper_tlbwi(CPUMIPSState *env) +{ + env->tlb->helper_tlbwi(env); +} + +void helper_tlbwr(CPUMIPSState *env) +{ + env->tlb->helper_tlbwr(env); +} + +void helper_tlbp(CPUMIPSState *env) +{ + env->tlb->helper_tlbp(env); +} + +void helper_tlbr(CPUMIPSState *env) +{ + env->tlb->helper_tlbr(env); +} + +void helper_tlbinv(CPUMIPSState *env) +{ + env->tlb->helper_tlbinv(env); +} + +void helper_tlbinvf(CPUMIPSState *env) +{ + env->tlb->helper_tlbinvf(env); +} + +static void global_invalidate_tlb(CPUMIPSState *env, + uint32_t invMsgVPN2, + uint8_t invMsgR, + uint32_t invMsgMMid, + bool invAll, + bool invVAMMid, + bool invMMid, + bool invVA) +{ + + int idx; + r4k_tlb_t *tlb; + bool VAMatch; + bool MMidMatch; + + for (idx = 0; idx < env->tlb->nb_tlb; idx++) { + tlb = &env->tlb->mmu.r4k.tlb[idx]; + VAMatch = + (((tlb->VPN & ~tlb->PageMask) == (invMsgVPN2 & ~tlb->PageMask)) +#ifdef TARGET_MIPS64 + && + (extract64(env->CP0_EntryHi, 62, 2) == invMsgR) +#endif + ); + MMidMatch = tlb->MMID == invMsgMMid; + if ((invAll && (idx > env->CP0_Wired)) || + (VAMatch && invVAMMid && (tlb->G || MMidMatch)) || + (VAMatch && invVA) || + (MMidMatch && !(tlb->G) && invMMid)) { + tlb->EHINV = 1; + } + } + cpu_mips_tlb_flush(env); +} + +void helper_ginvt(CPUMIPSState *env, target_ulong arg, uint32_t type) +{ + bool invAll = type == 0; + bool invVA = type == 1; + bool invMMid = type == 2; + bool invVAMMid = type == 3; + uint32_t invMsgVPN2 = arg & (TARGET_PAGE_MASK << 1); + uint8_t invMsgR = 0; + uint32_t invMsgMMid = env->CP0_MemoryMapID; + CPUState *other_cs = first_cpu; + +#ifdef TARGET_MIPS64 + invMsgR = extract64(arg, 62, 2); +#endif + + CPU_FOREACH(other_cs) { + MIPSCPU *other_cpu = MIPS_CPU(other_cs); + global_invalidate_tlb(&other_cpu->env, invMsgVPN2, invMsgR, invMsgMMid, + invAll, invVAMMid, invMMid, invVA); + } +} + +/* no MMU emulation */ +static int no_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot, + target_ulong address, MMUAccessType access_type) +{ + *physical = address; + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return TLBRET_MATCH; +} + +/* fixed mapping MMU emulation */ +static int fixed_mmu_map_address(CPUMIPSState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type) +{ + if (address <= (int32_t)0x7FFFFFFFUL) { + if (!(env->CP0_Status & (1 << CP0St_ERL))) { + *physical = address + 0x40000000UL; + } else { + *physical = address; + } + } else if (address <= (int32_t)0xBFFFFFFFUL) { + *physical = address & 0x1FFFFFFF; + } else { + *physical = address; + } + + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return TLBRET_MATCH; +} + +/* MIPS32/MIPS64 R4000-style MMU emulation */ +static int r4k_map_address(CPUMIPSState *env, hwaddr *physical, int *prot, + target_ulong address, MMUAccessType access_type) +{ + uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + uint32_t MMID = env->CP0_MemoryMapID; + bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); + uint32_t tlb_mmid; + int i; + + MMID = mi ? MMID : (uint32_t) ASID; + + for (i = 0; i < env->tlb->tlb_in_use; i++) { + r4k_tlb_t *tlb = &env->tlb->mmu.r4k.tlb[i]; + /* 1k pages are not supported. */ + target_ulong mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); + target_ulong tag = address & ~mask; + target_ulong VPN = tlb->VPN & ~mask; +#if defined(TARGET_MIPS64) + tag &= env->SEGMask; +#endif + + /* Check ASID/MMID, virtual page number & size */ + tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; + if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag && !tlb->EHINV) { + /* TLB match */ + int n = !!(address & mask & ~(mask >> 1)); + /* Check access rights */ + if (!(n ? tlb->V1 : tlb->V0)) { + return TLBRET_INVALID; + } + if (access_type == MMU_INST_FETCH && (n ? tlb->XI1 : tlb->XI0)) { + return TLBRET_XI; + } + if (access_type == MMU_DATA_LOAD && (n ? tlb->RI1 : tlb->RI0)) { + return TLBRET_RI; + } + if (access_type != MMU_DATA_STORE || (n ? tlb->D1 : tlb->D0)) { + *physical = tlb->PFN[n] | (address & (mask >> 1)); + *prot = PAGE_READ; + if (n ? tlb->D1 : tlb->D0) { + *prot |= PAGE_WRITE; + } + if (!(n ? tlb->XI1 : tlb->XI0)) { + *prot |= PAGE_EXEC; + } + return TLBRET_MATCH; + } + return TLBRET_DIRTY; + } + } + return TLBRET_NOMATCH; +} + +static void no_mmu_init(CPUMIPSState *env, const mips_def_t *def) +{ + env->tlb->nb_tlb = 1; + env->tlb->map_address = &no_mmu_map_address; +} + +static void fixed_mmu_init(CPUMIPSState *env, const mips_def_t *def) +{ + env->tlb->nb_tlb = 1; + env->tlb->map_address = &fixed_mmu_map_address; +} + +static void r4k_mmu_init(CPUMIPSState *env, const mips_def_t *def) +{ + env->tlb->nb_tlb = 1 + ((def->CP0_Config1 >> CP0C1_MMU) & 63); + env->tlb->map_address = &r4k_map_address; + env->tlb->helper_tlbwi = r4k_helper_tlbwi; + env->tlb->helper_tlbwr = r4k_helper_tlbwr; + env->tlb->helper_tlbp = r4k_helper_tlbp; + env->tlb->helper_tlbr = r4k_helper_tlbr; + env->tlb->helper_tlbinv = r4k_helper_tlbinv; + env->tlb->helper_tlbinvf = r4k_helper_tlbinvf; +} + +void mmu_init(CPUMIPSState *env, const mips_def_t *def) +{ + env->tlb = g_malloc0(sizeof(CPUMIPSTLBContext)); + + switch (def->mmu_type) { + case MMU_TYPE_NONE: + no_mmu_init(env, def); + break; + case MMU_TYPE_R4000: + r4k_mmu_init(env, def); + break; + case MMU_TYPE_FMT: + fixed_mmu_init(env, def); + break; + case MMU_TYPE_R3000: + case MMU_TYPE_R6000: + case MMU_TYPE_R8000: + default: + cpu_abort(env_cpu(env), "MMU type not supported\n"); + } +} + +void cpu_mips_tlb_flush(CPUMIPSState *env) +{ + /* Flush qemu's TLB and discard all shadowed entries. */ + tlb_flush(env_cpu(env)); + env->tlb->tlb_in_use = env->tlb->nb_tlb; +} + +static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, + MMUAccessType access_type, int tlb_error) +{ + CPUState *cs = env_cpu(env); + int exception = 0, error_code = 0; + + if (access_type == MMU_INST_FETCH) { + error_code |= EXCP_INST_NOTAVAIL; + } + + switch (tlb_error) { + default: + case TLBRET_BADADDR: + /* Reference to kernel address from user mode or supervisor mode */ + /* Reference to supervisor address from user mode */ + if (access_type == MMU_DATA_STORE) { + exception = EXCP_AdES; + } else { + exception = EXCP_AdEL; + } + break; + case TLBRET_NOMATCH: + /* No TLB match for a mapped address */ + if (access_type == MMU_DATA_STORE) { + exception = EXCP_TLBS; + } else { + exception = EXCP_TLBL; + } + error_code |= EXCP_TLB_NOMATCH; + break; + case TLBRET_INVALID: + /* TLB match with no valid bit */ + if (access_type == MMU_DATA_STORE) { + exception = EXCP_TLBS; + } else { + exception = EXCP_TLBL; + } + break; + case TLBRET_DIRTY: + /* TLB match but 'D' bit is cleared */ + exception = EXCP_LTLBL; + break; + case TLBRET_XI: + /* Execute-Inhibit Exception */ + if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { + exception = EXCP_TLBXI; + } else { + exception = EXCP_TLBL; + } + break; + case TLBRET_RI: + /* Read-Inhibit Exception */ + if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { + exception = EXCP_TLBRI; + } else { + exception = EXCP_TLBL; + } + break; + } + /* Raise exception */ + if (!(env->hflags & MIPS_HFLAG_DM)) { + env->CP0_BadVAddr = address; + } + env->CP0_Context = (env->CP0_Context & ~0x007fffff) | + ((address >> 9) & 0x007ffff0); + env->CP0_EntryHi = (env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask) | + (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) | + (address & (TARGET_PAGE_MASK << 1)); +#if defined(TARGET_MIPS64) + env->CP0_EntryHi &= env->SEGMask; + env->CP0_XContext = + (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) | /* PTEBase */ + (extract64(address, 62, 2) << (env->SEGBITS - 9)) | /* R */ + (extract64(address, 13, env->SEGBITS - 13) << 4); /* BadVPN2 */ +#endif + cs->exception_index = exception; + env->error_code = error_code; +} + +#if !defined(TARGET_MIPS64) + +/* + * Perform hardware page table walk + * + * Memory accesses are performed using the KERNEL privilege level. + * Synchronous exceptions detected on memory accesses cause a silent exit + * from page table walking, resulting in a TLB or XTLB Refill exception. + * + * Implementations are not required to support page table walk memory + * accesses from mapped memory regions. When an unsupported access is + * attempted, a silent exit is taken, resulting in a TLB or XTLB Refill + * exception. + * + * Note that if an exception is caused by AddressTranslation or LoadMemory + * functions, the exception is not taken, a silent exit is taken, + * resulting in a TLB or XTLB Refill exception. + */ + +static bool get_pte(CPUMIPSState *env, uint64_t vaddr, MemOp op, + uint64_t *pte, unsigned ptw_mmu_idx) +{ + MemOpIdx oi; + + if ((vaddr & (memop_size(op) - 1)) != 0) { + return false; + } + + 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_mmu(env, vaddr, oi, 0); + } + + return true; +} + +static uint64_t get_tlb_entry_layout(CPUMIPSState *env, uint64_t entry, + MemOp op, int ptei) +{ + unsigned entry_size = memop_size(op) << 3; + uint64_t result = entry; + uint64_t rixi; + if (ptei > entry_size) { + ptei -= 32; + } + result >>= (ptei - 2); + rixi = result & 3; + result >>= 2; + result |= rixi << CP0EnLo_XI; + return result; +} + +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, + 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; + uint64_t entry; + uint64_t paddr; + int prot; + uint64_t lsb = 0; + uint64_t w = 0; + + if (get_physical_address(env, &paddr, &prot, *vaddr, MMU_DATA_LOAD, + ptw_mmu_idx) != TLBRET_MATCH) { + /* wrong base address */ + return 0; + } + 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, leaf_mop, pf_ptew); + w = directory_index - 1; + if (directory_index & 0x1) { + /* Generate adjacent page from same PTE for odd TLB page */ + lsb = BIT_ULL(w) >> 6; + *pw_entrylo0 = entry & ~lsb; /* even page */ + *pw_entrylo1 = entry | lsb; /* odd page */ + } else if (dph) { + int oddpagebit = 1 << leaf_mop; + uint64_t vaddr2 = *vaddr ^ oddpagebit; + if (*vaddr & oddpagebit) { + *pw_entrylo1 = entry; + } else { + *pw_entrylo0 = entry; + } + if (get_physical_address(env, &paddr, &prot, vaddr2, MMU_DATA_LOAD, + ptw_mmu_idx) != TLBRET_MATCH) { + return 0; + } + if (!get_pte(env, vaddr2, leaf_mop, &entry, ptw_mmu_idx)) { + return 0; + } + entry = get_tlb_entry_layout(env, entry, leaf_mop, pf_ptew); + if (*vaddr & oddpagebit) { + *pw_entrylo0 = entry; + } else { + *pw_entrylo1 = entry; + } + } else { + return 0; + } + return 1; + } else { + *vaddr = entry; + return 2; + } +} + +static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, + int ptw_mmu_idx) +{ + int gdw = (env->CP0_PWSize >> CP0PS_GDW) & 0x3F; + int udw = (env->CP0_PWSize >> CP0PS_UDW) & 0x3F; + int mdw = (env->CP0_PWSize >> CP0PS_MDW) & 0x3F; + int ptw = (env->CP0_PWSize >> CP0PS_PTW) & 0x3F; + int ptew = (env->CP0_PWSize >> CP0PS_PTEW) & 0x3F; + + /* Initial values */ + bool huge_page = false; + bool hgpg_bdhit = false; + bool hgpg_gdhit = false; + bool hgpg_udhit = false; + bool hgpg_mdhit = false; + + int32_t pw_pagemask = 0; + target_ulong pw_entryhi = 0; + uint64_t pw_entrylo0 = 0; + uint64_t pw_entrylo1 = 0; + + /* Native pointer size */ + /*For the 32-bit architectures, this bit is fixed to 0.*/ + 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; + int pf_udw = (env->CP0_PWField >> CP0PF_UDW) & 0x3F; + int pf_mdw = (env->CP0_PWField >> CP0PF_MDW) & 0x3F; + int pf_ptw = (env->CP0_PWField >> CP0PF_PTW) & 0x3F; + int pf_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; + + /* Indices computed from faulting address */ + int gindex = (address >> pf_gdw) & ((1 << gdw) - 1); + int uindex = (address >> pf_udw) & ((1 << udw) - 1); + int mindex = (address >> pf_mdw) & ((1 << mdw) - 1); + int ptindex = (address >> pf_ptw) & ((1 << ptw) - 1); + + /* Other HTW configs */ + int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; + MemOp directory_mop, leaf_mop; + + /* Offsets into tables */ + unsigned goffset, uoffset, moffset, ptoffset0, ptoffset1; + + /* Starting address - Page Table Base */ + uint64_t vaddr = env->CP0_PWBase; + + uint64_t dir_entry; + uint64_t paddr; + int prot; + int m; + + if (!(env->CP0_Config3 & (1 << CP0C3_PW))) { + /* walker is unimplemented */ + return false; + } + if (!(env->CP0_PWCtl & (1 << CP0PC_PWEN))) { + /* walker is disabled */ + return false; + } + if (!(gdw > 0 || udw > 0 || mdw > 0)) { + /* no structure to walk */ + return false; + } + 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, + directory_mop, leaf_mop, ptw_mmu_idx)) + { + case 0: + return false; + case 1: + goto refill; + case 2: + default: + break; + } + } + + /* Upper directory */ + if (udw > 0) { + vaddr |= uoffset; + switch (walk_directory(env, &vaddr, pf_udw, &huge_page, &hgpg_udhit, + &pw_entrylo0, &pw_entrylo1, + directory_mop, leaf_mop, ptw_mmu_idx)) + { + case 0: + return false; + case 1: + goto refill; + case 2: + default: + break; + } + } + + /* Middle directory */ + if (mdw > 0) { + vaddr |= moffset; + switch (walk_directory(env, &vaddr, pf_mdw, &huge_page, &hgpg_mdhit, + &pw_entrylo0, &pw_entrylo1, + directory_mop, leaf_mop, ptw_mmu_idx)) + { + case 0: + return false; + case 1: + goto refill; + case 2: + default: + break; + } + } + + /* Leaf Level Page Table - First half of PTE pair */ + vaddr |= ptoffset0; + if (get_physical_address(env, &paddr, &prot, vaddr, MMU_DATA_LOAD, + ptw_mmu_idx) != TLBRET_MATCH) { + return false; + } + if (!get_pte(env, vaddr, leaf_mop, &dir_entry, ptw_mmu_idx)) { + return false; + } + 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, + ptw_mmu_idx) != TLBRET_MATCH) { + return false; + } + if (!get_pte(env, vaddr, leaf_mop, &dir_entry, ptw_mmu_idx)) { + return false; + } + dir_entry = get_tlb_entry_layout(env, dir_entry, leaf_mop, pf_ptew); + pw_entrylo1 = dir_entry; + +refill: + + m = (1 << pf_ptw) - 1; + + if (huge_page) { + switch (hgpg_bdhit << 3 | hgpg_gdhit << 2 | hgpg_udhit << 1 | + hgpg_mdhit) + { + case 4: + m = (1 << pf_gdw) - 1; + if (pf_gdw & 1) { + m >>= 1; + } + break; + case 2: + m = (1 << pf_udw) - 1; + if (pf_udw & 1) { + m >>= 1; + } + break; + case 1: + m = (1 << pf_mdw) - 1; + if (pf_mdw & 1) { + m >>= 1; + } + break; + } + } + pw_pagemask = m >> TARGET_PAGE_BITS; + pw_pagemask = compute_pagemask(pw_pagemask << CP0PM_MASK); + pw_entryhi = (address & ~0x1fff) | (env->CP0_EntryHi & 0xFF); + { + target_ulong tmp_entryhi = env->CP0_EntryHi; + int32_t tmp_pagemask = env->CP0_PageMask; + uint64_t tmp_entrylo0 = env->CP0_EntryLo0; + uint64_t tmp_entrylo1 = env->CP0_EntryLo1; + + env->CP0_EntryHi = pw_entryhi; + env->CP0_PageMask = pw_pagemask; + env->CP0_EntryLo0 = pw_entrylo0; + env->CP0_EntryLo1 = pw_entrylo1; + + /* + * The hardware page walker inserts a page into the TLB in a manner + * identical to a TLBWR instruction as executed by the software refill + * handler. + */ + r4k_helper_tlbwr(env); + + env->CP0_EntryHi = tmp_entryhi; + env->CP0_PageMask = tmp_pagemask; + env->CP0_EntryLo0 = tmp_entrylo0; + env->CP0_EntryLo1 = tmp_entrylo1; + } + return true; +} +#endif + +bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr) +{ + CPUMIPSState *env = cpu_env(cs); + hwaddr physical; + int prot; + int ret = TLBRET_BADADDR; + + /* data access */ + /* XXX: put correct access by using cpu_restore_state() correctly */ + ret = get_physical_address(env, &physical, &prot, address, + access_type, mmu_idx); + switch (ret) { + case TLBRET_MATCH: + qemu_log_mask(CPU_LOG_MMU, + "%s address=%" VADDR_PRIx " physical " HWADDR_FMT_plx + " prot %d\n", __func__, address, physical, prot); + break; + default: + qemu_log_mask(CPU_LOG_MMU, + "%s address=%" VADDR_PRIx " ret %d\n", __func__, address, + ret); + break; + } + if (ret == TLBRET_MATCH) { + tlb_set_page(cs, address & TARGET_PAGE_MASK, + physical & TARGET_PAGE_MASK, prot, + mmu_idx, TARGET_PAGE_SIZE); + return true; + } +#if !defined(TARGET_MIPS64) + if ((ret == TLBRET_NOMATCH) && (env->tlb->nb_tlb > 1)) { + /* + * Memory reads during hardware page table walking are performed + * as if they were kernel-mode load instructions. + */ + 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) { + tlb_set_page(cs, address & TARGET_PAGE_MASK, + physical & TARGET_PAGE_MASK, prot, + mmu_idx, TARGET_PAGE_SIZE); + return true; + } + } + } +#endif + if (probe) { + return false; + } + + raise_mmu_exception(env, address, access_type, ret); + do_raise_exception_err(env, cs->exception_index, env->error_code, retaddr); +} + +hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address, + MMUAccessType access_type, uintptr_t retaddr) +{ + hwaddr physical; + int prot; + int ret = 0; + CPUState *cs = env_cpu(env); + + /* data access */ + ret = get_physical_address(env, &physical, &prot, address, access_type, + mips_env_mmu_index(env)); + if (ret == TLBRET_MATCH) { + return physical; + } + + raise_mmu_exception(env, address, access_type, ret); + cpu_loop_exit_restore(cs, retaddr); +} + +static void set_hflags_for_handler(CPUMIPSState *env) +{ + /* Exception handlers are entered in 32-bit mode. */ + env->hflags &= ~(MIPS_HFLAG_M16); + /* ...except that microMIPS lets you choose. */ + if (env->insn_flags & ASE_MICROMIPS) { + env->hflags |= (!!(env->CP0_Config3 & + (1 << CP0C3_ISA_ON_EXC)) + << MIPS_HFLAG_M16_SHIFT); + } +} + +static inline void set_badinstr_registers(CPUMIPSState *env) +{ + if (env->insn_flags & ISA_NANOMIPS32) { + if (env->CP0_Config3 & (1 << CP0C3_BI)) { + uint32_t instr = (cpu_lduw_code(env, env->active_tc.PC)) << 16; + if ((instr & 0x10000000) == 0) { + instr |= cpu_lduw_code(env, env->active_tc.PC + 2); + } + env->CP0_BadInstr = instr; + + if ((instr & 0xFC000000) == 0x60000000) { + instr = cpu_lduw_code(env, env->active_tc.PC + 4) << 16; + env->CP0_BadInstrX = instr; + } + } + return; + } + + if (env->hflags & MIPS_HFLAG_M16) { + /* TODO: add BadInstr support for microMIPS */ + return; + } + if (env->CP0_Config3 & (1 << CP0C3_BI)) { + env->CP0_BadInstr = cpu_ldl_code(env, env->active_tc.PC); + } + if ((env->CP0_Config3 & (1 << CP0C3_BP)) && + (env->hflags & MIPS_HFLAG_BMASK)) { + env->CP0_BadInstrP = cpu_ldl_code(env, env->active_tc.PC - 4); + } +} + +void mips_cpu_do_interrupt(CPUState *cs) +{ + MIPSCPU *cpu = MIPS_CPU(cs); + CPUMIPSState *env = &cpu->env; + bool update_badinstr = 0; + target_ulong offset; + int cause = -1; + + if (qemu_loglevel_mask(CPU_LOG_INT) + && cs->exception_index != EXCP_EXT_INTERRUPT) { + qemu_log("%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx + " %s exception\n", + __func__, env->active_tc.PC, env->CP0_EPC, + mips_exception_name(cs->exception_index)); + } + if (cs->exception_index == EXCP_EXT_INTERRUPT && + (env->hflags & MIPS_HFLAG_DM)) { + cs->exception_index = EXCP_DINT; + } + offset = 0x180; + switch (cs->exception_index) { + case EXCP_SEMIHOST: + cs->exception_index = EXCP_NONE; + mips_semihosting(env); + env->active_tc.PC += env->error_code; + return; + case EXCP_DSS: + env->CP0_Debug |= 1 << CP0DB_DSS; + /* + * Debug single step cannot be raised inside a delay slot and + * resume will always occur on the next instruction + * (but we assume the pc has always been updated during + * code translation). + */ + env->CP0_DEPC = env->active_tc.PC | !!(env->hflags & MIPS_HFLAG_M16); + goto enter_debug_mode; + case EXCP_DINT: + env->CP0_Debug |= 1 << CP0DB_DINT; + goto set_DEPC; + case EXCP_DIB: + env->CP0_Debug |= 1 << CP0DB_DIB; + goto set_DEPC; + case EXCP_DBp: + env->CP0_Debug |= 1 << CP0DB_DBp; + /* Setup DExcCode - SDBBP instruction */ + env->CP0_Debug = (env->CP0_Debug & ~(0x1fULL << CP0DB_DEC)) | + (9 << CP0DB_DEC); + goto set_DEPC; + case EXCP_DDBS: + env->CP0_Debug |= 1 << CP0DB_DDBS; + goto set_DEPC; + case EXCP_DDBL: + env->CP0_Debug |= 1 << CP0DB_DDBL; + set_DEPC: + env->CP0_DEPC = exception_resume_pc(env); + env->hflags &= ~MIPS_HFLAG_BMASK; + enter_debug_mode: + if (env->insn_flags & ISA_MIPS3) { + env->hflags |= MIPS_HFLAG_64; + if (!(env->insn_flags & ISA_MIPS_R6) || + env->CP0_Status & (1 << CP0St_KX)) { + env->hflags &= ~MIPS_HFLAG_AWRAP; + } + } + env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_CP0; + env->hflags &= ~(MIPS_HFLAG_KSU); + /* EJTAG probe trap enable is not implemented... */ + if (!(env->CP0_Status & (1 << CP0St_EXL))) { + env->CP0_Cause &= ~(1U << CP0Ca_BD); + } + env->active_tc.PC = env->exception_base + 0x480; + set_hflags_for_handler(env); + break; + case EXCP_RESET: + cpu_reset(CPU(cpu)); + break; + case EXCP_SRESET: + env->CP0_Status |= (1 << CP0St_SR); + memset(env->CP0_WatchLo, 0, sizeof(env->CP0_WatchLo)); + goto set_error_EPC; + case EXCP_NMI: + env->CP0_Status |= (1 << CP0St_NMI); + set_error_EPC: + env->CP0_ErrorEPC = exception_resume_pc(env); + env->hflags &= ~MIPS_HFLAG_BMASK; + env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV); + if (env->insn_flags & ISA_MIPS3) { + env->hflags |= MIPS_HFLAG_64; + if (!(env->insn_flags & ISA_MIPS_R6) || + env->CP0_Status & (1 << CP0St_KX)) { + env->hflags &= ~MIPS_HFLAG_AWRAP; + } + } + env->hflags |= MIPS_HFLAG_CP0; + env->hflags &= ~(MIPS_HFLAG_KSU); + if (!(env->CP0_Status & (1 << CP0St_EXL))) { + env->CP0_Cause &= ~(1U << CP0Ca_BD); + } + env->active_tc.PC = env->exception_base; + set_hflags_for_handler(env); + break; + case EXCP_EXT_INTERRUPT: + cause = 0; + if (env->CP0_Cause & (1 << CP0Ca_IV)) { + uint32_t spacing = (env->CP0_IntCtl >> CP0IntCtl_VS) & 0x1f; + + if ((env->CP0_Status & (1 << CP0St_BEV)) || spacing == 0) { + offset = 0x200; + } else { + uint32_t vector = 0; + uint32_t pending = (env->CP0_Cause & CP0Ca_IP_mask) >> CP0Ca_IP; + + if (env->CP0_Config3 & (1 << CP0C3_VEIC)) { + /* + * For VEIC mode, the external interrupt controller feeds + * the vector through the CP0Cause IP lines. + */ + vector = pending; + } else { + /* + * Vectored Interrupts + * Mask with Status.IM7-IM0 to get enabled interrupts. + */ + pending &= (env->CP0_Status >> CP0St_IM) & 0xff; + /* Find the highest-priority interrupt. */ + while (pending >>= 1) { + vector++; + } + } + offset = 0x200 + (vector * (spacing << 5)); + } + } + goto set_EPC; + case EXCP_LTLBL: + cause = 1; + update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); + goto set_EPC; + case EXCP_TLBL: + cause = 2; + update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); + if ((env->error_code & EXCP_TLB_NOMATCH) && + !(env->CP0_Status & (1 << CP0St_EXL))) { +#if defined(TARGET_MIPS64) + int R = env->CP0_BadVAddr >> 62; + int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; + int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; + + if ((R != 0 || UX) && (R != 3 || KX) && + (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) { + offset = 0x080; + } else { +#endif + offset = 0x000; +#if defined(TARGET_MIPS64) + } +#endif + } + goto set_EPC; + case EXCP_TLBS: + cause = 3; + update_badinstr = 1; + if ((env->error_code & EXCP_TLB_NOMATCH) && + !(env->CP0_Status & (1 << CP0St_EXL))) { +#if defined(TARGET_MIPS64) + int R = env->CP0_BadVAddr >> 62; + int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; + int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; + + if ((R != 0 || UX) && (R != 3 || KX) && + (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) { + offset = 0x080; + } else { +#endif + offset = 0x000; +#if defined(TARGET_MIPS64) + } +#endif + } + goto set_EPC; + case EXCP_AdEL: + cause = 4; + update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); + goto set_EPC; + case EXCP_AdES: + cause = 5; + update_badinstr = 1; + goto set_EPC; + case EXCP_IBE: + cause = 6; + goto set_EPC; + case EXCP_DBE: + cause = 7; + goto set_EPC; + case EXCP_SYSCALL: + cause = 8; + update_badinstr = 1; + goto set_EPC; + case EXCP_BREAK: + cause = 9; + update_badinstr = 1; + goto set_EPC; + case EXCP_RI: + cause = 10; + update_badinstr = 1; + goto set_EPC; + case EXCP_CpU: + cause = 11; + update_badinstr = 1; + env->CP0_Cause = (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) | + (env->error_code << CP0Ca_CE); + goto set_EPC; + case EXCP_OVERFLOW: + cause = 12; + update_badinstr = 1; + goto set_EPC; + case EXCP_TRAP: + cause = 13; + update_badinstr = 1; + goto set_EPC; + case EXCP_MSAFPE: + cause = 14; + update_badinstr = 1; + goto set_EPC; + case EXCP_FPE: + cause = 15; + update_badinstr = 1; + goto set_EPC; + case EXCP_C2E: + cause = 18; + goto set_EPC; + case EXCP_TLBRI: + cause = 19; + update_badinstr = 1; + goto set_EPC; + case EXCP_TLBXI: + cause = 20; + goto set_EPC; + case EXCP_MSADIS: + cause = 21; + update_badinstr = 1; + goto set_EPC; + case EXCP_MDMX: + cause = 22; + goto set_EPC; + case EXCP_DWATCH: + cause = 23; + /* XXX: TODO: manage deferred watch exceptions */ + goto set_EPC; + case EXCP_MCHECK: + cause = 24; + goto set_EPC; + case EXCP_THREAD: + cause = 25; + goto set_EPC; + case EXCP_DSPDIS: + cause = 26; + goto set_EPC; + case EXCP_CACHE: + cause = 30; + offset = 0x100; + set_EPC: + if (!(env->CP0_Status & (1 << CP0St_EXL))) { + env->CP0_EPC = exception_resume_pc(env); + if (update_badinstr) { + set_badinstr_registers(env); + } + if (env->hflags & MIPS_HFLAG_BMASK) { + env->CP0_Cause |= (1U << CP0Ca_BD); + } else { + env->CP0_Cause &= ~(1U << CP0Ca_BD); + } + env->CP0_Status |= (1 << CP0St_EXL); + if (env->insn_flags & ISA_MIPS3) { + env->hflags |= MIPS_HFLAG_64; + if (!(env->insn_flags & ISA_MIPS_R6) || + env->CP0_Status & (1 << CP0St_KX)) { + env->hflags &= ~MIPS_HFLAG_AWRAP; + } + } + env->hflags |= MIPS_HFLAG_CP0; + env->hflags &= ~(MIPS_HFLAG_KSU); + } + env->hflags &= ~MIPS_HFLAG_BMASK; + if (env->CP0_Status & (1 << CP0St_BEV)) { + env->active_tc.PC = env->exception_base + 0x200; + } else if (cause == 30 && !(env->CP0_Config3 & (1 << CP0C3_SC) && + env->CP0_Config5 & (1 << CP0C5_CV))) { + /* Force KSeg1 for cache errors */ + env->active_tc.PC = KSEG1_BASE | (env->CP0_EBase & 0x1FFFF000); + } else { + env->active_tc.PC = env->CP0_EBase & ~0xfff; + } + + env->active_tc.PC += offset; + set_hflags_for_handler(env); + env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | + (cause << CP0Ca_EC); + break; + default: + abort(); + } + if (qemu_loglevel_mask(CPU_LOG_INT) + && cs->exception_index != EXCP_EXT_INTERRUPT) { + qemu_log("%s: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " cause %d\n" + " S %08x C %08x A " TARGET_FMT_lx " D " TARGET_FMT_lx "\n", + __func__, env->active_tc.PC, env->CP0_EPC, cause, + env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr, + env->CP0_DEPC); + } + cs->exception_index = EXCP_NONE; +} + +bool mips_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + if (interrupt_request & CPU_INTERRUPT_HARD) { + CPUMIPSState *env = cpu_env(cs); + + if (cpu_mips_hw_interrupts_enabled(env) && + cpu_mips_hw_interrupts_pending(env)) { + /* Raise it */ + cs->exception_index = EXCP_EXT_INTERRUPT; + env->error_code = 0; + mips_cpu_do_interrupt(cs); + return true; + } + } + return false; +} + +void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra) +{ + CPUState *cs = env_cpu(env); + r4k_tlb_t *tlb; + target_ulong addr; + target_ulong end; + uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + uint32_t MMID = env->CP0_MemoryMapID; + bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); + uint32_t tlb_mmid; + target_ulong mask; + + MMID = mi ? MMID : (uint32_t) ASID; + + tlb = &env->tlb->mmu.r4k.tlb[idx]; + /* + * The qemu TLB is flushed when the ASID/MMID changes, so no need to + * flush these entries again. + */ + tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; + if (tlb->G == 0 && tlb_mmid != MMID) { + return; + } + + if (use_extra && env->tlb->tlb_in_use < MIPS_TLB_MAX) { + /* + * For tlbwr, we can shadow the discarded entry into + * a new (fake) TLB entry, as long as the guest can not + * tell that it's there. + */ + env->tlb->mmu.r4k.tlb[env->tlb->tlb_in_use] = *tlb; + env->tlb->tlb_in_use++; + return; + } + + /* 1k pages are not supported. */ + mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); + if (tlb->V0) { + addr = tlb->VPN & ~mask; +#if defined(TARGET_MIPS64) + if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { + addr |= 0x3FFFFF0000000000ULL; + } +#endif + end = addr | (mask >> 1); + while (addr < end) { + tlb_flush_page(cs, addr); + addr += TARGET_PAGE_SIZE; + } + } + if (tlb->V1) { + addr = (tlb->VPN & ~mask) | ((mask >> 1) + 1); +#if defined(TARGET_MIPS64) + if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { + addr |= 0x3FFFFF0000000000ULL; + } +#endif + end = addr | mask; + while (addr - 1 < end) { + tlb_flush_page(cs, addr); + addr += TARGET_PAGE_SIZE; + } + } +} diff --git a/target/mips/tcg/system_helper.h.inc b/target/mips/tcg/system_helper.h.inc new file mode 100644 index 00000000000..eaac5e2c6f0 --- /dev/null +++ b/target/mips/tcg/system_helper.h.inc @@ -0,0 +1,185 @@ +/* + * QEMU MIPS TCG system helpers + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * Copyright (c) 2006 Marius Groeger (FPU operations) + * Copyright (c) 2006 Thiemo Seufer (MIPS32R2 support) + * Copyright (c) 2009 CodeSourcery (MIPS16 and microMIPS support) + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +/* CP0 helpers */ +DEF_HELPER_1(mfc0_mvpcontrol, tl, env) +DEF_HELPER_1(mfc0_mvpconf0, tl, env) +DEF_HELPER_1(mfc0_mvpconf1, tl, env) +DEF_HELPER_1(mftc0_vpecontrol, tl, env) +DEF_HELPER_1(mftc0_vpeconf0, tl, env) +DEF_HELPER_1(mfc0_random, tl, env) +DEF_HELPER_1(mfc0_tcstatus, tl, env) +DEF_HELPER_1(mftc0_tcstatus, tl, env) +DEF_HELPER_1(mfc0_tcbind, tl, env) +DEF_HELPER_1(mftc0_tcbind, tl, env) +DEF_HELPER_1(mfc0_tcrestart, tl, env) +DEF_HELPER_1(mftc0_tcrestart, tl, env) +DEF_HELPER_1(mfc0_tchalt, tl, env) +DEF_HELPER_1(mftc0_tchalt, tl, env) +DEF_HELPER_1(mfc0_tccontext, tl, env) +DEF_HELPER_1(mftc0_tccontext, tl, env) +DEF_HELPER_1(mfc0_tcschedule, tl, env) +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(mftc0_entryhi, tl, env) +DEF_HELPER_1(mftc0_status, tl, env) +DEF_HELPER_1(mftc0_cause, tl, env) +DEF_HELPER_1(mftc0_epc, tl, env) +DEF_HELPER_1(mftc0_ebase, tl, env) +DEF_HELPER_2(mftc0_configx, tl, env, tl) +DEF_HELPER_1(mfc0_lladdr, tl, env) +DEF_HELPER_1(mfc0_maar, tl, env) +DEF_HELPER_1(mfhc0_maar, tl, env) +DEF_HELPER_2(mfc0_watchlo, tl, env, i32) +DEF_HELPER_2(mfc0_watchhi, tl, env, i32) +DEF_HELPER_2(mfhc0_watchhi, tl, env, i32) +DEF_HELPER_1(mfc0_debug, tl, env) +DEF_HELPER_1(mftc0_debug, tl, env) +#ifdef TARGET_MIPS64 +DEF_HELPER_1(dmfc0_tcrestart, tl, env) +DEF_HELPER_1(dmfc0_tchalt, tl, env) +DEF_HELPER_1(dmfc0_tccontext, tl, env) +DEF_HELPER_1(dmfc0_tcschedule, tl, env) +DEF_HELPER_1(dmfc0_tcschefback, tl, env) +DEF_HELPER_1(dmfc0_lladdr, tl, env) +DEF_HELPER_1(dmfc0_maar, tl, env) +DEF_HELPER_2(dmfc0_watchlo, tl, env, i32) +DEF_HELPER_2(dmfc0_watchhi, tl, env, i32) +#endif /* TARGET_MIPS64 */ + +DEF_HELPER_2(mtc0_index, void, env, tl) +DEF_HELPER_2(mtc0_mvpcontrol, void, env, tl) +DEF_HELPER_2(mtc0_vpecontrol, void, env, tl) +DEF_HELPER_2(mttc0_vpecontrol, void, env, tl) +DEF_HELPER_2(mtc0_vpeconf0, void, env, tl) +DEF_HELPER_2(mttc0_vpeconf0, void, env, tl) +DEF_HELPER_2(mtc0_vpeconf1, void, env, tl) +DEF_HELPER_2(mtc0_yqmask, void, env, tl) +DEF_HELPER_2(mtc0_vpeopt, void, env, tl) +DEF_HELPER_2(mtc0_entrylo0, void, env, tl) +DEF_HELPER_2(mtc0_tcstatus, void, env, tl) +DEF_HELPER_2(mttc0_tcstatus, void, env, tl) +DEF_HELPER_2(mtc0_tcbind, void, env, tl) +DEF_HELPER_2(mttc0_tcbind, void, env, tl) +DEF_HELPER_2(mtc0_tcrestart, void, env, tl) +DEF_HELPER_2(mttc0_tcrestart, void, env, tl) +DEF_HELPER_2(mtc0_tchalt, void, env, tl) +DEF_HELPER_2(mttc0_tchalt, void, env, tl) +DEF_HELPER_2(mtc0_tccontext, void, env, tl) +DEF_HELPER_2(mttc0_tccontext, void, env, tl) +DEF_HELPER_2(mtc0_tcschedule, void, env, tl) +DEF_HELPER_2(mttc0_tcschedule, void, env, tl) +DEF_HELPER_2(mtc0_tcschefback, void, env, tl) +DEF_HELPER_2(mttc0_tcschefback, void, env, tl) +DEF_HELPER_2(mtc0_entrylo1, void, env, tl) +DEF_HELPER_2(mtc0_context, void, env, tl) +DEF_HELPER_2(mtc0_memorymapid, void, env, tl) +DEF_HELPER_2(mtc0_pagemask, void, env, tl) +DEF_HELPER_2(mtc0_pagegrain, void, env, tl) +DEF_HELPER_2(mtc0_segctl0, void, env, tl) +DEF_HELPER_2(mtc0_segctl1, void, env, tl) +DEF_HELPER_2(mtc0_segctl2, void, env, tl) +DEF_HELPER_2(mtc0_pwfield, void, env, tl) +DEF_HELPER_2(mtc0_pwsize, void, env, tl) +DEF_HELPER_2(mtc0_wired, void, env, tl) +DEF_HELPER_2(mtc0_srsconf0, void, env, tl) +DEF_HELPER_2(mtc0_srsconf1, void, env, tl) +DEF_HELPER_2(mtc0_srsconf2, void, env, tl) +DEF_HELPER_2(mtc0_srsconf3, void, env, tl) +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_entryhi, void, env, tl) +DEF_HELPER_2(mttc0_entryhi, void, env, tl) +DEF_HELPER_2(mtc0_compare, void, env, tl) +DEF_HELPER_2(mtc0_status, void, env, tl) +DEF_HELPER_2(mttc0_status, void, env, tl) +DEF_HELPER_2(mtc0_intctl, void, env, tl) +DEF_HELPER_2(mtc0_srsctl, void, env, tl) +DEF_HELPER_2(mtc0_cause, void, env, tl) +DEF_HELPER_2(mttc0_cause, void, env, tl) +DEF_HELPER_2(mtc0_ebase, void, env, tl) +DEF_HELPER_2(mttc0_ebase, void, env, tl) +DEF_HELPER_2(mtc0_config0, void, env, tl) +DEF_HELPER_2(mtc0_config2, void, env, tl) +DEF_HELPER_2(mtc0_config3, void, env, tl) +DEF_HELPER_2(mtc0_config4, void, env, tl) +DEF_HELPER_2(mtc0_config5, void, env, tl) +DEF_HELPER_2(mtc0_lladdr, void, env, tl) +DEF_HELPER_2(mtc0_maar, void, env, tl) +DEF_HELPER_2(mthc0_maar, void, env, tl) +DEF_HELPER_2(mtc0_maari, void, env, tl) +DEF_HELPER_3(mtc0_watchlo, void, env, tl, i32) +DEF_HELPER_3(mtc0_watchhi, void, env, tl, i32) +DEF_HELPER_3(mthc0_watchhi, void, env, tl, i32) +DEF_HELPER_2(mtc0_xcontext, void, env, tl) +DEF_HELPER_2(mtc0_framemask, void, env, tl) +DEF_HELPER_2(mtc0_debug, void, env, tl) +DEF_HELPER_2(mttc0_debug, void, env, tl) +DEF_HELPER_2(mtc0_performance0, void, env, tl) +DEF_HELPER_2(mtc0_errctl, void, env, tl) +DEF_HELPER_2(mtc0_taglo, void, env, tl) +DEF_HELPER_2(mtc0_datalo, void, env, tl) +DEF_HELPER_2(mtc0_taghi, void, env, tl) +DEF_HELPER_2(mtc0_datahi, void, env, tl) + +#if defined(TARGET_MIPS64) +DEF_HELPER_2(dmtc0_entrylo0, void, env, i64) +DEF_HELPER_2(dmtc0_entrylo1, void, env, i64) +#endif + +/* MIPS MT functions */ +DEF_HELPER_2(mftgpr, tl, env, i32) +DEF_HELPER_2(mftlo, tl, env, i32) +DEF_HELPER_2(mfthi, tl, env, i32) +DEF_HELPER_2(mftacx, tl, env, i32) +DEF_HELPER_1(mftdsp, tl, env) +DEF_HELPER_3(mttgpr, void, env, tl, i32) +DEF_HELPER_3(mttlo, void, env, tl, i32) +DEF_HELPER_3(mtthi, void, env, tl, i32) +DEF_HELPER_3(mttacx, void, env, tl, i32) +DEF_HELPER_2(mttdsp, void, env, tl) +DEF_HELPER_0(dmt, tl) +DEF_HELPER_0(emt, tl) +DEF_HELPER_1(dvpe, tl, env) +DEF_HELPER_1(evpe, tl, env) + +/* R6 Multi-threading */ +DEF_HELPER_1(dvp, tl, env) +DEF_HELPER_1(evp, tl, env) + +/* TLB */ +DEF_HELPER_1(tlbwi, void, env) +DEF_HELPER_1(tlbwr, void, env) +DEF_HELPER_1(tlbp, void, env) +DEF_HELPER_1(tlbr, void, env) +DEF_HELPER_1(tlbinv, void, env) +DEF_HELPER_1(tlbinvf, void, env) +DEF_HELPER_3(ginvt, void, env, tl, i32) + +/* Special */ +DEF_HELPER_1(di, tl, env) +DEF_HELPER_1(ei, tl, env) +DEF_HELPER_1(eret, void, env) +DEF_HELPER_1(eretnc, void, env) +DEF_HELPER_1(deret, void, env) +DEF_HELPER_3(cache, void, env, tl, i32) + +#ifdef TARGET_MIPS64 +/* Longson CSR */ +DEF_HELPER_2(lcsr_rdcsr, i64, env, tl) +DEF_HELPER_2(lcsr_drdcsr, i64, env, tl) +DEF_HELPER_3(lcsr_wrcsr, void, env, tl, tl) +DEF_HELPER_3(lcsr_dwrcsr, void, env, tl, tl) +#endif diff --git a/target/mips/tcg/tcg-internal.h b/target/mips/tcg/tcg-internal.h index aef032c48dc..950e6afc3f2 100644 --- a/target/mips/tcg/tcg-internal.h +++ b/target/mips/tcg/tcg-internal.h @@ -16,6 +16,8 @@ #include "cpu.h" void mips_tcg_init(void); +void mips_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); void mips_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb); G_NORETURN void mips_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, @@ -45,7 +47,7 @@ bool mips_cpu_exec_interrupt(CPUState *cpu, int int_req); void mmu_init(CPUMIPSState *env, const mips_def_t *def); -void update_pagemask(CPUMIPSState *env, target_ulong arg1, int32_t *pagemask); +uint32_t compute_pagemask(uint32_t val); void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra); uint32_t cpu_mips_get_random(CPUMIPSState *env); diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 9bb40f1849d..8658315f938 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -27,9 +27,9 @@ #include "internal.h" #include "exec/helper-proto.h" #include "exec/translation-block.h" +#include "exec/target_page.h" #include "semihosting/semihost.h" #include "trace.h" -#include "disas/disas.h" #include "fpu_helper.h" #define HELPER_H "helper.h" @@ -38,7 +38,7 @@ /* - * Many sysemu-only helpers are not reachable for user-only. + * Many system-only helpers are not reachable for user-only. * Define stub generators here, so that we need not either sprinkle * ifdefs through the translator, nor provide the helper function. */ @@ -328,19 +328,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, @@ -369,20 +356,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 */ @@ -390,16 +363,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 */ @@ -557,7 +528,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 */ @@ -1268,12 +1238,12 @@ 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); } @@ -1288,12 +1258,12 @@ 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); } @@ -1344,14 +1314,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) @@ -1363,7 +1333,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); @@ -1457,8 +1427,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); @@ -1647,13 +1616,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); } } @@ -1868,70 +1842,70 @@ 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(); \ @@ -1954,20 +1928,20 @@ static inline void op_ld_##insn(TCGv ret, TCGv arg1, int mem_idx, \ TCGv t0 = tcg_temp_new(); \ tcg_gen_mov_tl(t0, arg1); \ tcg_gen_qemu_ld_tl(ret, arg1, ctx->mem_idx, memop); \ - tcg_gen_st_tl(t0, cpu_env, offsetof(CPUMIPSState, lladdr)); \ - tcg_gen_st_tl(ret, cpu_env, offsetof(CPUMIPSState, llval)); \ + 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, MO_TESL); +OP_LD_ATOMIC(ll, mo_endian(ctx) | MO_SL); #if defined(TARGET_MIPS64) -OP_LD_ATOMIC(lld, MO_TEUQ); +OP_LD_ATOMIC(lld, mo_endian(ctx) | MO_UQ); #endif #undef OP_LD_ATOMIC @@ -2011,7 +1985,7 @@ static void gen_lxl(DisasContext *ctx, TCGv reg, TCGv addr, */ tcg_gen_qemu_ld_tl(t1, addr, mem_idx, MO_UB); tcg_gen_andi_tl(t1, addr, sizem1); - if (!cpu_is_bigendian(ctx)) { + if (!disas_is_bigendian(ctx)) { tcg_gen_xori_tl(t1, t1, sizem1); } tcg_gen_shli_tl(t1, t1, 3); @@ -2038,7 +2012,7 @@ static void gen_lxr(DisasContext *ctx, TCGv reg, TCGv addr, */ tcg_gen_qemu_ld_tl(t1, addr, mem_idx, MO_UB); tcg_gen_andi_tl(t1, addr, sizem1); - if (cpu_is_bigendian(ctx)) { + if (disas_is_bigendian(ctx)) { tcg_gen_xori_tl(t1, t1, sizem1); } tcg_gen_shli_tl(t1, t1, 3); @@ -2074,12 +2048,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; @@ -2091,33 +2065,33 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, case OPC_LDL: t1 = tcg_temp_new(); gen_load_gpr(t1, rt); - gen_lxl(ctx, t1, t0, mem_idx, MO_TEUQ); + 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(); gen_load_gpr(t1, rt); - gen_lxr(ctx, t1, t0, mem_idx, MO_TEUQ); + gen_lxr(ctx, t1, t0, mem_idx, mo_endian(ctx) | MO_UQ); gen_store_gpr(t1, rt); break; case OPC_LDPC: t1 = tcg_constant_tl(pc_relative_pc(ctx)); gen_op_addr_add(ctx, t0, t0, 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_constant_tl(pc_relative_pc(ctx)); gen_op_addr_add(ctx, t0, t0, 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; @@ -2125,7 +2099,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; @@ -2133,7 +2107,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; @@ -2157,7 +2131,7 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, case OPC_LWL: t1 = tcg_temp_new(); gen_load_gpr(t1, rt); - gen_lxl(ctx, t1, t0, mem_idx, MO_TEUL); + gen_lxl(ctx, t1, t0, mem_idx, mo_endian(ctx) | MO_UL); tcg_gen_ext32s_tl(t1, t1); gen_store_gpr(t1, rt); break; @@ -2167,7 +2141,7 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, case OPC_LWR: t1 = tcg_temp_new(); gen_load_gpr(t1, rt); - gen_lxr(ctx, t1, t0, mem_idx, MO_TEUL); + gen_lxr(ctx, t1, t0, mem_idx, mo_endian(ctx) | MO_UL); tcg_gen_ext32s_tl(t1, t1); gen_store_gpr(t1, rt); break; @@ -2195,7 +2169,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: @@ -2209,14 +2183,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: @@ -2254,8 +2228,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_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); @@ -2282,7 +2255,7 @@ 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); } @@ -2291,14 +2264,14 @@ static void gen_flt_ldst(DisasContext *ctx, uint32_t opc, int ft, { 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); } 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); } @@ -2307,7 +2280,7 @@ static void gen_flt_ldst(DisasContext *ctx, uint32_t opc, int ft, { 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); } break; @@ -2988,14 +2961,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: @@ -3022,7 +2995,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: @@ -3061,8 +3034,7 @@ 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]); } @@ -3078,30 +3050,27 @@ 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]); } break; case R6_OPC_DIVU: { - TCGv t2 = tcg_constant_tl(0); - TCGv t3 = tcg_constant_tl(1); tcg_gen_ext32u_tl(t0, t0); tcg_gen_ext32u_tl(t1, t1); - tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); + tcg_gen_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]); } break; case R6_OPC_MODU: { - TCGv t2 = tcg_constant_tl(0); - TCGv t3 = tcg_constant_tl(1); tcg_gen_ext32u_tl(t0, t0); tcg_gen_ext32u_tl(t1, t1); - tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); + tcg_gen_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]); } @@ -3156,8 +3125,7 @@ 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); } break; @@ -3170,24 +3138,21 @@ 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); } break; case R6_OPC_DDIVU: { - TCGv t2 = tcg_constant_tl(0); - TCGv t3 = tcg_constant_tl(1); - tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); + tcg_gen_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); } break; case R6_OPC_DMODU: { - TCGv t2 = tcg_constant_tl(0); - TCGv t3 = tcg_constant_tl(1); - tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); + tcg_gen_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); } break; @@ -3240,8 +3205,7 @@ 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]); @@ -3296,8 +3260,7 @@ 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]); @@ -3349,17 +3312,15 @@ 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); } break; case OPC_DDIVU: { - TCGv t2 = tcg_constant_tl(0); - TCGv t3 = tcg_constant_tl(1); - tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); + tcg_gen_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); } @@ -3601,184 +3562,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; - } - - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); - 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 - } -} - /* Loongson multimedia instructions */ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) { @@ -4161,10 +3944,10 @@ 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); @@ -4173,10 +3956,10 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, 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); @@ -4185,11 +3968,11 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, 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); break; case OPC_GSSQC1: @@ -4197,11 +3980,11 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, 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); break; #endif @@ -4214,7 +3997,7 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, gen_load_fpr32(ctx, fp0, rt); t1 = tcg_temp_new(); tcg_gen_ext_i32_tl(t1, fp0); - gen_lxl(ctx, t1, t0, ctx->mem_idx, MO_TEUL); + 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); break; @@ -4225,7 +4008,7 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, gen_load_fpr32(ctx, fp0, rt); t1 = tcg_temp_new(); tcg_gen_ext_i32_tl(t1, fp0); - gen_lxr(ctx, t1, t0, ctx->mem_idx, MO_TEUL); + 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); break; @@ -4235,7 +4018,7 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, gen_base_offset_addr(ctx, t0, rs, shf_offset); t1 = tcg_temp_new(); gen_load_fpr64(ctx, t1, rt); - gen_lxl(ctx, t1, t0, ctx->mem_idx, MO_TEUQ); + gen_lxl(ctx, t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ); gen_store_fpr64(ctx, t1, rt); break; case OPC_GSLDRC1: @@ -4243,7 +4026,7 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, gen_base_offset_addr(ctx, t0, rs, shf_offset); t1 = tcg_temp_new(); gen_load_fpr64(ctx, t1, rt); - gen_lxr(ctx, t1, t0, ctx->mem_idx, MO_TEUQ); + gen_lxr(ctx, t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ); gen_store_fpr64(ctx, t1, rt); break; #endif @@ -4361,7 +4144,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; @@ -4370,7 +4153,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; @@ -4380,7 +4163,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; @@ -4391,7 +4174,7 @@ 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); break; @@ -4401,7 +4184,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; @@ -4414,34 +4197,34 @@ static void gen_loongson_lsdc2(DisasContext *ctx, int rt, 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); 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); 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); 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); 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); break; #endif @@ -4499,7 +4282,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); @@ -4544,7 +4327,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. */ @@ -4585,8 +4368,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; @@ -5053,13 +4836,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_gen_st_i64(t1, tcg_env, off); } static inline void gen_mthc0_store64(TCGv arg, target_ulong off) @@ -5068,16 +4851,16 @@ 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_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 @@ -5090,7 +4873,7 @@ 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); } @@ -5099,13 +4882,13 @@ 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); } 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); } @@ -5114,7 +4897,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_gen_st_i32(t0, tcg_env, off); } #define CP0_CHECK(c) \ @@ -5151,17 +4934,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: @@ -5171,7 +4943,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: @@ -5252,17 +5024,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: @@ -5276,7 +5037,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: @@ -5352,18 +5113,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: @@ -5379,41 +5140,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; @@ -5426,7 +5187,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) { @@ -5440,38 +5201,38 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) 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: @@ -5483,7 +5244,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) { @@ -5508,7 +5269,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; @@ -5519,14 +5280,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: @@ -5546,19 +5307,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; @@ -5635,7 +5396,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; @@ -5665,7 +5426,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* Mark as an IO operation because we read the time. */ translator_io_start(&ctx->base); - gen_helper_mfc0_count(arg, cpu_env); + 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 @@ -5675,16 +5436,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; } @@ -5692,7 +5443,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; @@ -5749,7 +5500,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; @@ -5765,14 +5516,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; @@ -5822,12 +5573,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: @@ -5880,7 +5631,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; @@ -5908,7 +5659,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: @@ -5944,7 +5695,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; @@ -6018,7 +5769,7 @@ 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); } register_name = "TagLo"; @@ -6057,7 +5808,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; @@ -6079,7 +5830,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"; @@ -6115,21 +5866,21 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) 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; @@ -6149,40 +5900,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: @@ -6192,42 +5943,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: @@ -6237,7 +5988,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: @@ -6252,7 +6003,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: @@ -6262,7 +6013,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; @@ -6278,28 +6029,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: @@ -6309,12 +6060,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: @@ -6324,37 +6075,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: @@ -6365,7 +6116,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; @@ -6398,19 +6149,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; } @@ -6418,7 +6159,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: @@ -6428,7 +6169,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 */ @@ -6440,7 +6181,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; @@ -6448,14 +6189,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"; @@ -6475,7 +6216,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 @@ -6492,7 +6233,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: @@ -6507,7 +6248,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: @@ -6517,7 +6258,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; @@ -6527,24 +6268,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; @@ -6566,17 +6307,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: @@ -6624,7 +6365,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 @@ -6637,7 +6378,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: @@ -6651,7 +6392,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; @@ -6659,14 +6400,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; @@ -6675,21 +6416,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"; @@ -6702,7 +6443,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: @@ -6712,7 +6453,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: @@ -6750,7 +6491,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; @@ -6774,14 +6515,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: @@ -6794,14 +6535,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: @@ -6812,7 +6553,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: @@ -6833,7 +6574,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; @@ -6879,18 +6620,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: @@ -6906,44 +6647,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; @@ -6954,43 +6695,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: @@ -7000,7 +6741,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: @@ -7015,7 +6756,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: @@ -7025,13 +6766,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: @@ -7051,32 +6792,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: @@ -7137,7 +6878,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: @@ -7165,7 +6906,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REG09__COUNT: /* Mark as an IO operation because we read the time. */ translator_io_start(&ctx->base); - gen_helper_mfc0_count(arg, cpu_env); + 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 @@ -7175,16 +6916,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; } @@ -7192,7 +6923,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: @@ -7248,7 +6979,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: @@ -7263,13 +6994,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: @@ -7318,12 +7049,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: @@ -7375,7 +7106,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: @@ -7401,32 +7132,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: @@ -7437,7 +7168,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: @@ -7546,7 +7277,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: @@ -7567,7 +7298,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; @@ -7602,21 +7333,21 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) 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; @@ -7636,40 +7367,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: @@ -7679,42 +7410,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: @@ -7724,7 +7455,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: @@ -7739,7 +7470,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: @@ -7749,7 +7480,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; @@ -7765,42 +7496,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: @@ -7810,37 +7541,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: @@ -7851,7 +7582,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; @@ -7884,19 +7615,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; } @@ -7906,7 +7627,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: @@ -7916,7 +7637,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 */ @@ -7930,7 +7651,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; @@ -7938,14 +7659,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"; @@ -7965,7 +7686,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 @@ -7982,7 +7703,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: @@ -7997,7 +7718,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: @@ -8007,7 +7728,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; @@ -8017,13 +7738,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; @@ -8033,7 +7754,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; @@ -8047,17 +7768,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: @@ -8104,7 +7825,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: @@ -8116,7 +7837,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: @@ -8130,7 +7851,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; @@ -8138,35 +7859,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"; @@ -8179,7 +7900,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: @@ -8189,35 +7910,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: @@ -8227,7 +7948,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; @@ -8251,14 +7972,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: @@ -8271,14 +7992,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: @@ -8289,7 +8010,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: @@ -8310,7 +8031,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; @@ -8358,10 +8079,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; @@ -8371,25 +8092,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); @@ -8399,7 +8120,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); @@ -8409,7 +8130,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); @@ -8419,7 +8140,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; @@ -8429,7 +8150,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; @@ -8439,7 +8160,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; @@ -8456,7 +8177,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_constant_tl(sel)); + gen_helper_mftc0_configx(t0, tcg_env, tcg_constant_tl(sel)); break; default: goto die; @@ -8466,7 +8187,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); @@ -8522,7 +8243,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; @@ -8585,10 +8306,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; @@ -8598,25 +8319,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); @@ -8626,7 +8347,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); @@ -8636,7 +8357,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); @@ -8646,7 +8367,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; @@ -8656,7 +8377,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; @@ -8666,7 +8387,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); @@ -8722,7 +8443,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; @@ -8849,7 +8570,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"; @@ -8857,7 +8578,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: @@ -8866,7 +8587,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: @@ -8874,21 +8595,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) && @@ -8900,12 +8621,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; } @@ -8921,7 +8642,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; @@ -8936,7 +8657,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: @@ -9061,8 +8782,8 @@ 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); return; @@ -9557,7 +9278,7 @@ 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); + gen_helper_float_add_s(fp0, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp0, fd); } break; @@ -9568,7 +9289,7 @@ 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); + gen_helper_float_sub_s(fp0, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp0, fd); } break; @@ -9579,7 +9300,7 @@ 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); + gen_helper_float_mul_s(fp0, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp0, fd); } break; @@ -9590,7 +9311,7 @@ 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); + gen_helper_float_div_s(fp0, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp0, fd); } break; @@ -9599,7 +9320,7 @@ 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); } break; @@ -9645,9 +9366,9 @@ 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); } gen_store_fpr64(ctx, fp64, fd); } @@ -9660,9 +9381,9 @@ 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); } gen_store_fpr64(ctx, fp64, fd); } @@ -9675,9 +9396,9 @@ 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); } gen_store_fpr64(ctx, fp64, fd); } @@ -9690,9 +9411,9 @@ 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); } gen_store_fpr64(ctx, fp64, fd); } @@ -9703,9 +9424,9 @@ 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); } @@ -9716,9 +9437,9 @@ 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); } @@ -9729,9 +9450,9 @@ 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); } @@ -9742,9 +9463,9 @@ 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); } @@ -9800,7 +9521,7 @@ 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); } break; @@ -9809,7 +9530,7 @@ 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); } break; @@ -9822,7 +9543,7 @@ 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); } break; @@ -9835,7 +9556,7 @@ 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); } break; @@ -9844,7 +9565,7 @@ 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); } break; @@ -9853,7 +9574,7 @@ 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); } break; @@ -9865,7 +9586,7 @@ 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); } else { /* OPC_RECIP2_S */ @@ -9876,7 +9597,7 @@ 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); + gen_helper_float_recip2_s(fp0, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp0, fd); } } @@ -9889,7 +9610,7 @@ 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); } else { /* OPC_RECIP1_S */ @@ -9898,7 +9619,7 @@ 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); } } @@ -9910,7 +9631,7 @@ 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); } else { /* OPC_RSQRT1_S */ @@ -9919,7 +9640,7 @@ 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); } } @@ -9931,7 +9652,7 @@ 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); } else { /* OPC_RSQRT2_S */ @@ -9942,7 +9663,7 @@ 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); + gen_helper_float_rsqrt2_s(fp0, tcg_env, fp0, fp1); gen_store_fpr32(ctx, fp0, fd); } } @@ -9954,7 +9675,7 @@ 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); + gen_helper_float_cvtd_s(fp64, tcg_env, fp32); gen_store_fpr64(ctx, fp64, fd); } break; @@ -9964,9 +9685,9 @@ 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); } @@ -9979,9 +9700,9 @@ 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); } gen_store_fpr64(ctx, fp64, fd); } @@ -10030,7 +9751,7 @@ 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); + gen_helper_float_add_d(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); } break; @@ -10042,7 +9763,7 @@ 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); + gen_helper_float_sub_d(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); } break; @@ -10054,7 +9775,7 @@ 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); + gen_helper_float_mul_d(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); } break; @@ -10066,7 +9787,7 @@ 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); + gen_helper_float_div_d(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); } break; @@ -10076,7 +9797,7 @@ 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); } break; @@ -10124,9 +9845,9 @@ 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); } @@ -10138,9 +9859,9 @@ 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); } @@ -10152,9 +9873,9 @@ 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); } @@ -10166,9 +9887,9 @@ 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); } @@ -10181,9 +9902,9 @@ 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); } gen_store_fpr32(ctx, fp32, fd); } @@ -10196,9 +9917,9 @@ 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); } gen_store_fpr32(ctx, fp32, fd); } @@ -10211,9 +9932,9 @@ 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); } gen_store_fpr32(ctx, fp32, fd); } @@ -10226,9 +9947,9 @@ 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); } gen_store_fpr32(ctx, fp32, fd); } @@ -10285,7 +10006,7 @@ 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); } break; @@ -10295,7 +10016,7 @@ 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); } break; @@ -10308,7 +10029,7 @@ 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); } break; @@ -10321,7 +10042,7 @@ 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); } break; @@ -10330,7 +10051,7 @@ 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); } break; @@ -10339,7 +10060,7 @@ 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); } break; @@ -10350,7 +10071,7 @@ 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); } else { /* OPC_RECIP2_D */ @@ -10361,7 +10082,7 @@ 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); + gen_helper_float_recip2_d(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); } } @@ -10373,7 +10094,7 @@ 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); } else { /* OPC_RECIP1_D */ @@ -10382,7 +10103,7 @@ 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); } } @@ -10394,7 +10115,7 @@ 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); } else { /* OPC_RSQRT1_D */ @@ -10403,7 +10124,7 @@ 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); } } @@ -10415,7 +10136,7 @@ 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); } else { /* OPC_RSQRT2_D */ @@ -10426,7 +10147,7 @@ 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); + gen_helper_float_rsqrt2_d(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); } } @@ -10461,7 +10182,7 @@ 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); + gen_helper_float_cvts_d(fp32, tcg_env, fp64); gen_store_fpr32(ctx, fp32, fd); } break; @@ -10473,9 +10194,9 @@ 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); } gen_store_fpr32(ctx, fp32, fd); } @@ -10487,9 +10208,9 @@ 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); } @@ -10499,7 +10220,7 @@ 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); } break; @@ -10510,7 +10231,7 @@ 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); + gen_helper_float_cvtd_w(fp64, tcg_env, fp32); gen_store_fpr64(ctx, fp64, fd); } break; @@ -10521,7 +10242,7 @@ 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); + gen_helper_float_cvts_l(fp32, tcg_env, fp64); gen_store_fpr32(ctx, fp32, fd); } break; @@ -10531,7 +10252,7 @@ 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); } break; @@ -10541,7 +10262,7 @@ 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); } break; @@ -10553,7 +10274,7 @@ 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); + gen_helper_float_add_ps(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); } break; @@ -10565,7 +10286,7 @@ 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); + gen_helper_float_sub_ps(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); } break; @@ -10577,7 +10298,7 @@ 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); + gen_helper_float_mul_ps(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); } break; @@ -10652,7 +10373,7 @@ 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); + gen_helper_float_addr_ps(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); } break; @@ -10664,7 +10385,7 @@ 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); + gen_helper_float_mulr_ps(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); } break; @@ -10676,7 +10397,7 @@ 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); + gen_helper_float_recip2_ps(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); } break; @@ -10686,7 +10407,7 @@ 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); } break; @@ -10696,7 +10417,7 @@ 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); } break; @@ -10708,7 +10429,7 @@ 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); + gen_helper_float_rsqrt2_ps(fp0, tcg_env, fp0, fp1); gen_store_fpr64(ctx, fp0, fd); } break; @@ -10718,7 +10439,7 @@ 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); } break; @@ -10728,7 +10449,7 @@ 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); } break; @@ -10738,7 +10459,7 @@ 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); } break; @@ -10842,7 +10563,7 @@ 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); } @@ -10852,7 +10573,7 @@ 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); } break; @@ -10862,7 +10583,7 @@ 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); } break; @@ -10871,7 +10592,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_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UL); } break; case OPC_SDXC1: @@ -10880,7 +10601,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_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ); } break; case OPC_SUXC1: @@ -10889,7 +10610,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_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ); } break; } @@ -10919,7 +10640,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); - 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); @@ -10943,7 +10664,7 @@ 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); + gen_helper_float_madd_s(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr32(ctx, fp2, fd); } break; @@ -10958,7 +10679,7 @@ 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); + gen_helper_float_madd_d(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); } break; @@ -10972,7 +10693,7 @@ 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); + gen_helper_float_madd_ps(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); } break; @@ -10986,7 +10707,7 @@ 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); + gen_helper_float_msub_s(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr32(ctx, fp2, fd); } break; @@ -11001,7 +10722,7 @@ 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); + gen_helper_float_msub_d(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); } break; @@ -11015,7 +10736,7 @@ 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); + gen_helper_float_msub_ps(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); } break; @@ -11029,7 +10750,7 @@ 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); + gen_helper_float_nmadd_s(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr32(ctx, fp2, fd); } break; @@ -11044,7 +10765,7 @@ 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); + gen_helper_float_nmadd_d(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); } break; @@ -11058,7 +10779,7 @@ 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); + gen_helper_float_nmadd_ps(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); } break; @@ -11072,7 +10793,7 @@ 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); + gen_helper_float_nmsub_s(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr32(ctx, fp2, fd); } break; @@ -11087,7 +10808,7 @@ 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); + gen_helper_float_nmsub_d(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); } break; @@ -11101,7 +10822,7 @@ 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); + gen_helper_float_nmsub_ps(fp2, tcg_env, fp0, fp1, fp2); gen_store_fpr64(ctx, fp2, fd); } break; @@ -11127,16 +10848,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: translator_io_start(&ctx->base); - gen_helper_rdhwr_cc(t0, cpu_env); + gen_helper_rdhwr_cc(t0, tcg_env); gen_store_gpr(t0, rt); /* * Break the TB to be able to take timer interrupts immediately @@ -11147,7 +10868,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: @@ -11159,24 +10880,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 { @@ -11212,7 +10933,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); @@ -11275,8 +10995,8 @@ 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); return; @@ -11329,10 +11049,9 @@ 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); + gen_op_addr_addi(ctx, btarget, tbase, offset); } break; default: @@ -11492,20 +11211,18 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, 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_gen_movi_tl(cpu_gpr[rx], npc); } static void gen_cache_operation(DisasContext *ctx, uint32_t op, int base, @@ -11514,7 +11231,7 @@ static void gen_cache_operation(DisasContext *ctx, uint32_t op, int base, 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); + gen_helper_cache(tcg_env, t1, t0); } static inline bool is_uhi(DisasContext *ctx, int sdbbp_code) @@ -11540,7 +11257,7 @@ 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); } @@ -11631,16 +11348,16 @@ 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 @@ -11665,8 +11382,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: @@ -11711,15 +11427,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); @@ -11770,67 +11486,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); @@ -11874,11 +11590,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; @@ -11943,15 +11659,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; @@ -11963,35 +11679,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); @@ -12003,35 +11719,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); @@ -12077,11 +11793,11 @@ 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; @@ -12117,35 +11833,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); @@ -12216,43 +11932,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); @@ -12349,24 +12065,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; @@ -12374,91 +12086,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; @@ -12471,107 +12183,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; } } @@ -12581,27 +12293,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; @@ -12610,23 +12322,23 @@ 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; @@ -12795,15 +12507,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); @@ -12843,23 +12555,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); @@ -12872,39 +12584,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); @@ -12920,15 +12632,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); @@ -12936,15 +12648,15 @@ 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; @@ -13066,80 +12778,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; @@ -13149,7 +12861,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: { @@ -13157,97 +12869,97 @@ 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; @@ -13579,7 +13291,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: @@ -13705,15 +13417,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); @@ -13738,15 +13441,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"); @@ -13783,7 +13477,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); @@ -13829,7 +13523,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); @@ -13879,17 +13573,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: @@ -13917,8 +13606,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); } @@ -14102,7 +13789,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); + gen_helper_insv(cpu_gpr[rt], tcg_env, t1, t0); break; } default: /* Invalid */ @@ -14147,15 +13834,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) { @@ -14371,7 +14049,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); + gen_helper_dinsv(cpu_gpr[rt], tcg_env, t1, t0); break; } default: /* Invalid */ @@ -14512,7 +14190,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); @@ -14606,7 +14284,7 @@ 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); } break; @@ -14797,32 +14475,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 @@ -14833,7 +14511,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 @@ -14976,7 +14654,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); @@ -15033,7 +14711,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); } @@ -15048,7 +14728,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); @@ -15255,7 +14937,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) { @@ -15348,6 +15030,9 @@ 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; @@ -15377,7 +15062,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; @@ -15426,7 +15111,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; } @@ -15448,7 +15134,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; @@ -15509,7 +15195,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; } } @@ -15538,24 +15224,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 mips_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext ctx; @@ -15564,11 +15242,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]); @@ -15578,48 +15254,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) { @@ -15631,8 +15307,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 db3dc932c77..1bf153d1838 100644 --- a/target/mips/tcg/translate.h +++ b/target/mips/tcg/translate.h @@ -49,7 +49,6 @@ typedef struct DisasContext { bool mrp; bool nan2008; bool abs2008; - bool saar; bool mi; int gi; } DisasContext; @@ -123,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); @@ -177,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); @@ -202,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)); \ @@ -216,10 +217,13 @@ 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); @@ -227,6 +231,11 @@ 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. @@ -235,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/tx79.decode b/target/mips/tcg/tx79.decode index 57d87a2076a..578b8c54c0f 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 dd6fb8a7bd7..ae3f5e19c43 100644 --- a/target/mips/tcg/tx79_translate.c +++ b/target/mips/tcg/tx79_translate.c @@ -340,12 +340,12 @@ 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); return true; } @@ -364,12 +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_gen_qemu_st_i64(t0, addr, ctx->mem_idx, mo_endian(ctx) | MO_UQ); return true; } diff --git a/target/mips/tcg/vr54xx_translate.c b/target/mips/tcg/vr54xx_translate.c index 2c1f6cc5278..c877ede76e9 100644 --- a/target/mips/tcg/vr54xx_translate.c +++ b/target/mips/tcg/vr54xx_translate.c @@ -43,7 +43,7 @@ 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); return true; diff --git a/target/nios2/Kconfig b/target/nios2/Kconfig deleted file mode 100644 index 1529ab8950d..00000000000 --- 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 767bba4b7b5..00000000000 --- a/target/nios2/cpu-param.h +++ /dev/null @@ -1,20 +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 - -#endif diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c deleted file mode 100644 index bc5cbf81c2e..00000000000 --- a/target/nios2/cpu.c +++ /dev/null @@ -1,413 +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 "gdbstub/helpers.h" -#include "hw/qdev-properties.h" - -static void nios2_cpu_set_pc(CPUState *cs, vaddr value) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - - 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_hold(Object *obj) -{ - CPUState *cs = CPU(obj); - Nios2CPU *cpu = NIOS2_CPU(cs); - Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(cpu); - CPUNios2State *env = &cpu->env; - - if (ncc->parent_phases.hold) { - ncc->parent_phases.hold(obj); - } - - 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); - ResettableClass *rc = RESETTABLE_CLASS(oc); - - device_class_set_parent_realize(dc, nios2_cpu_realizefn, - &ncc->parent_realize); - device_class_set_props(dc, nios2_properties); - resettable_class_set_parent_phases(rc, NULL, nios2_cpu_reset_hold, NULL, - &ncc->parent_phases); - - cc->class_by_name = nios2_cpu_class_by_name; - cc->has_work = nios2_cpu_has_work; - cc->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 477a3161fde..00000000000 --- 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_phases: The parent class' reset phase handlers. - * - * A Nios2 CPU model. - */ -struct Nios2CPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; - -#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); -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 -hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); -#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, vaddr *pc, - uint64_t *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 bb3b09e5a77..00000000000 --- 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 1648d76adea..00000000000 --- 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 8f0f9dc6289..00000000000 --- 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_system_ss = ss.source_set() -nios2_system_ss.add(files( - 'helper.c', - 'monitor.c', - 'mmu.c', - 'nios2-semi.c', -)) - -target_arch += {'nios2': nios2_ss} -target_softmmu_arch += {'nios2': nios2_system_ss} diff --git a/target/nios2/mmu.c b/target/nios2/mmu.c deleted file mode 100644 index d9b690b78e1..00000000000 --- 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 5b085900fbf..00000000000 --- 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 0152dec3fab..00000000000 --- 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 9d0241c758f..00000000000 --- a/target/nios2/nios2-semi.c +++ /dev/null @@ -1,232 +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 "gdbstub/syscalls.h" -#include "gdbstub/helpers.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_ARG1]); - exit(env->regs[R_ARG1]); - - 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, 32, 32, arg1), 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 5017457c5e3..00000000000 --- a/target/nios2/op_helper.c +++ /dev/null @@ -1,119 +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" - -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 07f1f0a5e7a..00000000000 --- 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 dfc546d3bb0..00000000000 --- a/target/nios2/translate.c +++ /dev/null @@ -1,1108 +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/translator.h" -#include "qemu/qemu-print.h" -#include "semihosting/semihost.h" - -#define HELPER_H "helper.h" -#include "exec/helper-info.c.inc" -#undef HELPER_H - - -/* is_jmp field values */ -#define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ - -#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_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); -#ifdef CONFIG_USER_ONLY - flags |= MO_UNALN; -#else - flags |= MO_ALIGN; -#endif - tcg_gen_qemu_ld_tl(data, addr, dc->mem_idx, flags); -} - -/* 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); -#ifdef CONFIG_USER_ONLY - flags |= MO_UNALN; -#else - flags |= MO_ALIGN; -#endif - tcg_gen_qemu_st_tl(val, addr, dc->mem_idx, flags); -} - -/* 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_TEUW), /* ldhu */ - INSTRUCTION(andi), /* andi */ - INSTRUCTION_FLG(gen_stx, MO_TEUW), /* sth */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_GE), /* bge */ - INSTRUCTION_FLG(gen_ldx, MO_TESW), /* ldh */ - INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_LT), /* cmplti */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_NOP(), /* initda */ - INSTRUCTION(ori), /* ori */ - INSTRUCTION_FLG(gen_stx, MO_TEUL), /* stw */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_LT), /* blt */ - INSTRUCTION_FLG(gen_ldx, MO_TEUL), /* 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_TEUW), /* ldhuio */ - INSTRUCTION(andhi), /* andhi */ - INSTRUCTION_FLG(gen_stx, MO_TEUW), /* sthio */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_GEU), /* bgeu */ - INSTRUCTION_FLG(gen_ldx, MO_TESW), /* ldhio */ - INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_LTU), /* cmpltui */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_UNIMPLEMENTED(), /* custom */ - INSTRUCTION_NOP(), /* initd */ - INSTRUCTION(orhi), /* orhi */ - INSTRUCTION_FLG(gen_stx, MO_TESL), /* stwio */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_LTU), /* bltu */ - INSTRUCTION_FLG(gen_ldx, MO_TEUL), /* 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)); - } 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)); - - 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); - 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_gen_st_tl(n, cpu_env, ofs); - } - 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)); -} - -#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); -} - -#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 - -/* 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); -} - -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 e0da4ac1dfc..cd66c2e3b6c 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 3f082074855..b4f57bbe692 100644 --- a/target/openrisc/cpu-param.h +++ b/target/openrisc/cpu-param.h @@ -2,15 +2,16 @@ * 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 #define OPENRISC_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 TARGET_INSN_START_EXTRA_WORDS 1 + #endif diff --git a/target/openrisc/cpu-qom.h b/target/openrisc/cpu-qom.h new file mode 100644 index 00000000000..14bac33312e --- /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 61d748cfdc1..dfbb2df643a 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -21,8 +21,9 @@ #include "qapi/error.h" #include "qemu/qemu-print.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/translation-block.h" #include "fpu/softfloat-helpers.h" +#include "accel/tcg/cpu-ops.h" #include "tcg/tcg.h" static void openrisc_cpu_set_pc(CPUState *cs, vaddr value) @@ -40,12 +41,24 @@ static vaddr openrisc_cpu_get_pc(CPUState *cs) return cpu->env.pc; } +static TCGTBCPUState openrisc_get_tb_cpu_state(CPUState *cs) +{ + CPUOpenRISCState *env = cpu_env(cs); + + return (TCGTBCPUState){ + .pc = env->pc, + .flags = ((env->dflag ? TB_FLAGS_DFLAG : 0) + | (cpu_get_gpr(env, 0) ? 0 : TB_FLAGS_R0_0) + | (env->sr & (SR_SM | SR_DME | SR_IME | SR_OVE))), + }; +} + static void openrisc_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { OpenRISCCPU *cpu = OPENRISC_CPU(cs); - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); cpu->env.pc = tb->pc; } @@ -62,25 +75,40 @@ static void openrisc_restore_state_to_opc(CPUState *cs, } } +#ifndef CONFIG_USER_ONLY static bool openrisc_cpu_has_work(CPUState *cs) { return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_TIMER); } +#endif /* !CONFIG_USER_ONLY */ + +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->endian = BFD_ENDIAN_BIG; info->print_insn = print_insn_or1k; } -static void openrisc_cpu_reset_hold(Object *obj) +static void openrisc_cpu_reset_hold(Object *obj, ResetType type) { - CPUState *s = CPU(obj); - 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); if (occ->parent_phases.hold) { - occ->parent_phases.hold(obj); + occ->parent_phases.hold(obj, type); } memset(&cpu->env, 0, offsetof(CPUOpenRISCState, end_reset_fields)); @@ -88,11 +116,19 @@ static void openrisc_cpu_reset_hold(Object *obj) 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); + + /* Default NaN: sign bit clear, frac msb set */ + set_float_default_nan_pattern(0b01000000, &cpu->env.fp_status); #ifndef CONFIG_USER_ONLY cpu->env.picmr = 0x00000000; @@ -144,17 +180,17 @@ static void openrisc_cpu_realizefn(DeviceState *dev, Error **errp) qemu_init_vcpu(cs); cpu_reset(cs); +#ifndef CONFIG_USER_ONLY + cpu_openrisc_clock_init(OPENRISC_CPU(dev)); +#endif + occ->parent_realize(dev, 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 } @@ -168,10 +204,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; } @@ -214,25 +247,33 @@ static void openrisc_any_initfn(Object *obj) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps openrisc_sysemu_ops = { + .has_work = openrisc_cpu_has_work, .get_phys_page_debug = openrisc_cpu_get_phys_page_debug, }; #endif -#include "hw/core/tcg-cpu-ops.h" +static const TCGCPUOps openrisc_tcg_ops = { + .guest_default_memory_order = 0, + .mttcg_supported = true, -static const struct TCGCPUOps openrisc_tcg_ops = { .initialize = openrisc_translate_init, + .translate_code = openrisc_translate_code, + .get_tb_cpu_state = openrisc_get_tb_cpu_state, .synchronize_from_tb = openrisc_cpu_synchronize_from_tb, .restore_state_to_opc = openrisc_restore_state_to_opc, + .mmu_index = openrisc_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill = openrisc_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_uint32, .cpu_exec_interrupt = openrisc_cpu_exec_interrupt, + .cpu_exec_halt = openrisc_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = openrisc_cpu_do_interrupt, #endif /* !CONFIG_USER_ONLY */ }; -static void openrisc_cpu_class_init(ObjectClass *oc, void *data) +static void openrisc_cpu_class_init(ObjectClass *oc, const void *data) { OpenRISCCPUClass *occ = OPENRISC_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(occ); @@ -245,7 +286,6 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) &occ->parent_phases); cc->class_by_name = openrisc_cpu_class_by_name; - cc->has_work = openrisc_cpu_has_work; cc->dump_state = openrisc_cpu_dump_state; cc->set_pc = openrisc_cpu_set_pc; cc->get_pc = openrisc_cpu_get_pc; @@ -260,48 +300,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, \ @@ -314,6 +312,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 ce4d605eb75..f4bcf00b073 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -20,16 +20,11 @@ #ifndef OPENRISC_CPU_H #define OPENRISC_CPU_H +#include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.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: @@ -39,16 +34,12 @@ OBJECT_DECLARE_CPU_TYPE(OpenRISCCPU, OpenRISCCPUClass, OPENRISC_CPU) * A OpenRISC CPU model. */ struct OpenRISCCPUClass { - /*< private >*/ CPUClass parent_class; - /*< public >*/ DeviceRealize parent_realize; ResettablePhases parent_phases; }; -#define TARGET_INSN_START_EXTRA_WORDS 1 - enum { MMU_NOMMU_IDX = 0, MMU_SUPERVISOR_IDX = 1, @@ -301,24 +292,19 @@ 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); 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); +void openrisc_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); 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); @@ -344,12 +330,8 @@ 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" - #define TB_FLAGS_SM SR_SM #define TB_FLAGS_DME SR_DME #define TB_FLAGS_IME SR_IME @@ -367,28 +349,6 @@ 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, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *pc = env->pc; - *cs_base = 0; - *flags = (env->dflag ? TB_FLAGS_DFLAG : 0) - | (cpu_get_gpr(env, 0) ? 0 : TB_FLAGS_R0_0) - | (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/exception.c b/target/openrisc/exception.c index 8699c3dcea4..e213be36b6b 100644 --- a/target/openrisc/exception.c +++ b/target/openrisc/exception.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exception.h" G_NORETURN void raise_exception(OpenRISCCPU *cpu, uint32_t excp) diff --git a/target/openrisc/exception_helper.c b/target/openrisc/exception_helper.c index 1f5be4bed90..c2c9d136528 100644 --- a/target/openrisc/exception_helper.c +++ b/target/openrisc/exception_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "exception.h" diff --git a/target/openrisc/fpu_helper.c b/target/openrisc/fpu_helper.c index 8b81d2f62f7..dba997255c6 100644 --- a/target/openrisc/fpu_helper.c +++ b/target/openrisc/fpu_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" diff --git a/target/openrisc/gdbstub.c b/target/openrisc/gdbstub.c index d1074a05811..45bba80d878 100644 --- a/target/openrisc/gdbstub.c +++ b/target/openrisc/gdbstub.c @@ -23,8 +23,7 @@ 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,15 +47,9 @@ 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) { - return 0; - } - tmp = ldl_p(mem_buf); if (n < 32) { diff --git a/target/openrisc/interrupt.c b/target/openrisc/interrupt.c index d4fdb8ce8e9..486823094c8 100644 --- a/target/openrisc/interrupt.c +++ b/target/openrisc/interrupt.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" #include "gdbstub/helpers.h" #include "qemu/host-utils.h" #ifndef CONFIG_USER_ONLY @@ -29,8 +28,7 @@ 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; @@ -105,8 +103,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/interrupt_helper.c b/target/openrisc/interrupt_helper.c index ab4ea88b692..1553ebc71b0 100644 --- a/target/openrisc/interrupt_helper.c +++ b/target/openrisc/interrupt_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" void HELPER(rfe)(CPUOpenRISCState *env) diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c index b7d73886401..081c706d02c 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,8 +135,8 @@ const VMStateDescription vmstate_openrisc_cpu = { .version_id = 1, .minimum_version_id = 1, .post_load = cpu_post_load, - .fields = (VMStateField[]) { - VMSTATE_CPU(), + .fields = (const VMStateField[]) { + VMSTATE_STRUCT(parent_obj, OpenRISCCPU, 0, vmstate_cpu_common, CPUState), 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 c1cd943f78f..31608b6dc7f 100644 --- a/target/openrisc/meson.build +++ b/target/openrisc/meson.build @@ -22,4 +22,4 @@ openrisc_system_ss.add(files( )) target_arch += {'openrisc': openrisc_ss} -target_softmmu_arch += {'openrisc': openrisc_system_ss} +target_system_arch += {'openrisc': openrisc_system_ss} diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index 603c26715ee..acea50c41eb 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -21,7 +21,9 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "exec/page-protection.h" +#include "exec/target_page.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 782a5751b75..d96b41a01c2 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -20,7 +20,8 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "exec/target_page.h" #include "exec/helper-proto.h" #include "exception.h" #ifndef CONFIG_USER_ONLY @@ -160,20 +161,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: @@ -198,15 +199,15 @@ 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(); + bql_unlock(); break; } #endif @@ -217,7 +218,7 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, { OpenRISCCPU *cpu = env_archcpu(env); #ifndef CONFIG_USER_ONLY - uint64_t data[TARGET_INSN_START_WORDS]; + uint64_t data[INSN_START_WORDS]; MachineState *ms = MACHINE(qdev_get_machine()); CPUState *cs = env_cpu(env); int idx; @@ -347,9 +348,9 @@ 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 diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index d65758449fc..5ab3bc7021d 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -20,14 +20,14 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" -#include "disas/disas.h" +#include "accel/tcg/cpu-mmu-index.h" #include "tcg/tcg-op.h" #include "qemu/log.h" #include "qemu/bitops.h" #include "qemu/qemu-print.h" #include "exec/translator.h" - +#include "exec/translation-block.h" +#include "exec/target_page.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" @@ -95,37 +95,37 @@ void openrisc_translate_init(void) }; int i; - cpu_sr = tcg_global_mem_new(cpu_env, + cpu_sr = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, sr), "sr"); - cpu_dflag = tcg_global_mem_new_i32(cpu_env, + cpu_dflag = tcg_global_mem_new_i32(tcg_env, offsetof(CPUOpenRISCState, dflag), "dflag"); - cpu_pc = tcg_global_mem_new(cpu_env, + cpu_pc = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, pc), "pc"); - cpu_ppc = tcg_global_mem_new(cpu_env, + cpu_ppc = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, ppc), "ppc"); - jmp_pc = tcg_global_mem_new(cpu_env, + jmp_pc = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, jmp_pc), "jmp_pc"); - cpu_sr_f = tcg_global_mem_new(cpu_env, + cpu_sr_f = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, sr_f), "sr_f"); - cpu_sr_cy = tcg_global_mem_new(cpu_env, + cpu_sr_cy = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, sr_cy), "sr_cy"); - cpu_sr_ov = tcg_global_mem_new(cpu_env, + cpu_sr_ov = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, sr_ov), "sr_ov"); - cpu_lock_addr = tcg_global_mem_new(cpu_env, + cpu_lock_addr = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, lock_addr), "lock_addr"); - cpu_lock_value = tcg_global_mem_new(cpu_env, + cpu_lock_value = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, lock_value), "lock_value"); - fpcsr = tcg_global_mem_new_i32(cpu_env, + fpcsr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUOpenRISCState, fpcsr), "fpcsr"); - cpu_mac = tcg_global_mem_new_i64(cpu_env, + cpu_mac = tcg_global_mem_new_i64(tcg_env, offsetof(CPUOpenRISCState, mac), "mac"); for (i = 0; i < 32; i++) { - cpu_regs[i] = tcg_global_mem_new(cpu_env, + cpu_regs[i] = tcg_global_mem_new(tcg_env, offsetof(CPUOpenRISCState, shadow_gpr[0][i]), regnames[i]); @@ -134,7 +134,7 @@ void openrisc_translate_init(void) static void gen_exception(DisasContext *dc, unsigned int excp) { - gen_helper_exception(cpu_env, tcg_constant_i32(excp)); + gen_helper_exception(tcg_env, tcg_constant_i32(excp)); } static void gen_illegal_exception(DisasContext *dc) @@ -182,21 +182,21 @@ static void check_r0_write(DisasContext *dc, int reg) static void gen_ove_cy(DisasContext *dc) { if (dc->tb_flags & SR_OVE) { - gen_helper_ove_cy(cpu_env); + gen_helper_ove_cy(tcg_env); } } static void gen_ove_ov(DisasContext *dc) { if (dc->tb_flags & SR_OVE) { - gen_helper_ove_ov(cpu_env); + gen_helper_ove_ov(tcg_env); } } static void gen_ove_cyov(DisasContext *dc) { if (dc->tb_flags & SR_OVE) { - gen_helper_ove_cyov(cpu_env); + gen_helper_ove_cyov(tcg_env); } } @@ -220,8 +220,7 @@ static void gen_addc(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb) TCGv t0 = tcg_temp_new(); TCGv res = tcg_temp_new(); - tcg_gen_add2_tl(res, cpu_sr_cy, srca, dc->zero, cpu_sr_cy, dc->zero); - tcg_gen_add2_tl(res, cpu_sr_cy, res, cpu_sr_cy, srcb, dc->zero); + tcg_gen_addcio_tl(res, cpu_sr_cy, srca, srcb, cpu_sr_cy); 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); @@ -835,7 +834,7 @@ static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a) } 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); + gen_helper_mfspr(cpu_R(dc, a->d), tcg_env, cpu_R(dc, a->d), spr); return true; } @@ -860,7 +859,7 @@ static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a) dc->base.is_jmp = DISAS_EXIT; tcg_gen_ori_tl(spr, cpu_R(dc, a->a), a->k); - gen_helper_mtspr(cpu_env, spr, cpu_R(dc, a->b)); + gen_helper_mtspr(tcg_env, spr, cpu_R(dc, a->b)); return true; } @@ -1102,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; @@ -1115,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; } @@ -1127,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; } @@ -1140,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; } @@ -1193,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; } @@ -1309,10 +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); - gen_helper_update_fpcsr(cpu_env); + gen_helper_update_fpcsr(tcg_env); return true; } @@ -1330,10 +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); - gen_helper_update_fpcsr(cpu_env); + gen_helper_update_fpcsr(tcg_env); return true; } @@ -1354,15 +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); } 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; } @@ -1412,10 +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); - gen_helper_update_fpcsr(cpu_env); + gen_helper_update_fpcsr(tcg_env); return true; } @@ -1431,9 +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); + 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; } @@ -1455,10 +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); - gen_helper_update_fpcsr(cpu_env); + gen_helper_update_fpcsr(tcg_env); return true; } @@ -1525,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; @@ -1564,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); @@ -1639,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 openrisc_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext ctx; @@ -1668,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 3ff152051a3..0283711673e 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 a8315659d96..80ac6c3e320 100644 --- a/target/ppc/arch_dump.c +++ b/target/ppc/arch_dump.c @@ -15,8 +15,8 @@ #include "qemu/osdep.h" #include "cpu.h" #include "elf.h" -#include "sysemu/dump.h" -#include "sysemu/kvm.h" +#include "system/dump.h" +#include "system/kvm.h" #ifdef TARGET_PPC64 #define ELFCLASS ELFCLASS64 @@ -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}, @@ -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 ebef2cccecf..55de3bd5d5d 100644 --- a/target/ppc/compat.c +++ b/target/ppc/compat.c @@ -18,10 +18,10 @@ */ #include "qemu/osdep.h" -#include "sysemu/hw_accel.h" -#include "sysemu/kvm.h" +#include "system/hw_accel.h" +#include "system/kvm.h" #include "kvm_ppc.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/visitor.h" @@ -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; diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c index 7dbb47de645..ea86ea202ab 100644 --- a/target/ppc/cpu-models.c +++ b/target/ppc/cpu-models.c @@ -35,7 +35,7 @@ #define POWERPC_DEF_SVR(_name, _desc, _pvr, _svr, _type) \ static void \ glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_class_init) \ - (ObjectClass *oc, void *data) \ + (ObjectClass *oc, const void *data) \ { \ DeviceClass *dc = DEVICE_CLASS(oc); \ PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); \ @@ -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,16 +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("power9_v2.2", CPU_POWERPC_POWER9_DD22, POWER9, "POWER9 v2.2") - POWERPC_DEF("power10_v1.0", CPU_POWERPC_POWER10_DD1, POWER10, - "POWER10 v1.0") 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) */ /***************************************************************************/ @@ -902,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.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 572b5e553a5..72ad31ba50d 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, @@ -354,6 +354,8 @@ enum { CPU_POWERPC_POWER10_BASE = 0x00800000, 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, @@ -391,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 0a0416e0a8c..e4ed9080ee9 100644 --- a/target/ppc/cpu-param.h +++ b/target/ppc/cpu-param.h @@ -2,14 +2,13 @@ * 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 #define PPC_CPU_PARAM_H #ifdef TARGET_PPC64 -# define TARGET_LONG_BITS 64 /* * Note that the official physical address space bits is 62-M where M * is implementation dependent. I've not looked up M for the set of @@ -27,10 +26,17 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 64 # endif #else -# define TARGET_LONG_BITS 32 # define TARGET_PHYS_ADDR_SPACE_BITS 36 # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif -#define TARGET_PAGE_BITS 12 + +#ifdef CONFIG_USER_ONLY +/* Allow user-only to vary page size from 4k */ +# define TARGET_PAGE_BITS_VARY +#else +# define TARGET_PAGE_BITS 12 +#endif + +#define TARGET_INSN_START_EXTRA_WORDS 0 #endif diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index be33786bd8d..8247fa23367 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" @@ -33,170 +33,9 @@ OBJECT_DECLARE_CPU_TYPE(PowerPCCPU, PowerPCCPUClass, POWERPC_CPU) #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 TYPE_HOST_POWERPC_CPU POWERPC_CPU_TYPE_NAME("host") -ObjectClass *ppc_cpu_class_by_name(const char *name); - -typedef struct CPUArchState CPUPPCState; -typedef struct ppc_tb_t ppc_tb_t; -typedef struct ppc_dcr_t ppc_dcr_t; - -/*****************************************************************************/ -/* 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_phases: The parent class' reset phase handlers. - * - * A PowerPC CPU model. - */ -struct PowerPCCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - DeviceUnrealize parent_unrealize; - ResettablePhases parent_phases; - void (*parent_parse_features)(const char *type, char *str, Error **errp); - - uint32_t pvr; - /* - * If @best is false, match if pcc is in the family of pvr - * Else match only if pcc is the best match for pvr in this family. - */ - bool (*pvr_match)(struct PowerPCCPUClass *pcc, uint32_t pvr, bool best); - uint64_t pcr_mask; /* Available bits in PCR register */ - uint64_t pcr_supported; /* Bits for supported PowerISA versions */ - uint32_t svr; - uint64_t insns_flags; - uint64_t insns_flags2; - uint64_t msr_mask; - uint64_t lpcr_mask; /* Available bits in the LPCR */ - uint64_t lpcr_pm; /* Power-saving mode Exit Cause Enable bits */ - powerpc_mmu_t mmu_model; - powerpc_excp_t excp_model; - powerpc_input_t bus_model; - uint32_t flags; - int bfd_mach; - uint32_t l1_dcache_size, l1_icache_size; -#ifndef CONFIG_USER_ONLY - 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); -}; - #ifndef CONFIG_USER_ONLY typedef struct PPCTimebase { uint64_t guest_timebase; diff --git a/target/ppc/cpu.c b/target/ppc/cpu.c index e3ad8e0c27f..4d8faaddee2 100644 --- a/target/ppc/cpu.c +++ b/target/ppc/cpu.c @@ -22,10 +22,11 @@ #include "cpu-models.h" #include "cpu-qom.h" #include "exec/log.h" +#include "exec/watchpoint.h" #include "fpu/softfloat-helpers.h" #include "mmu-hash64.h" #include "helper_regs.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" target_ulong cpu_read_xer(const CPUPPCState *env) { @@ -130,11 +131,13 @@ void ppc_store_ciabr(CPUPPCState *env, target_ulong val) ppc_update_ciabr(env); } -void ppc_update_daw0(CPUPPCState *env) +void ppc_update_daw(CPUPPCState *env, int rid) { CPUState *cs = env_cpu(env); - target_ulong deaw = env->spr[SPR_DAWR0] & PPC_BITMASK(0, 60); - uint32_t dawrx = env->spr[SPR_DAWRX0]; + int spr_dawr = rid ? SPR_DAWR1 : SPR_DAWR0; + int spr_dawrx = rid ? SPR_DAWRX1 : SPR_DAWRX0; + target_ulong deaw = env->spr[spr_dawr] & PPC_BITMASK(0, 60); + uint32_t dawrx = env->spr[spr_dawrx]; 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); @@ -144,9 +147,9 @@ void ppc_update_daw0(CPUPPCState *env) vaddr len; int flags; - if (env->dawr0_watchpoint) { - cpu_watchpoint_remove_by_ref(cs, env->dawr0_watchpoint); - env->dawr0_watchpoint = NULL; + if (env->dawr_watchpoint[rid]) { + cpu_watchpoint_remove_by_ref(cs, env->dawr_watchpoint[rid]); + env->dawr_watchpoint[rid] = NULL; } if (!dr && !dw) { @@ -166,28 +169,45 @@ void ppc_update_daw0(CPUPPCState *env) flags |= BP_MEM_WRITE; } - cpu_watchpoint_insert(cs, deaw, len, flags, &env->dawr0_watchpoint); + cpu_watchpoint_insert(cs, deaw, len, flags, &env->dawr_watchpoint[rid]); } void ppc_store_dawr0(CPUPPCState *env, target_ulong val) { env->spr[SPR_DAWR0] = val; - ppc_update_daw0(env); + ppc_update_daw(env, 0); } -void ppc_store_dawrx0(CPUPPCState *env, uint32_t val) +static void ppc_store_dawrx(CPUPPCState *env, uint32_t val, int rid) { 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__); + qemu_log_mask(LOG_UNIMP, "%s: DAWRX%d[HRAMMC] is unimplemented\n", + __func__, rid); } - env->spr[SPR_DAWRX0] = val; - ppc_update_daw0(env); + env->spr[rid ? SPR_DAWRX1 : SPR_DAWRX0] = val; + ppc_update_daw(env, rid); +} + +void ppc_store_dawrx0(CPUPPCState *env, uint32_t val) +{ + ppc_store_dawrx(env, val, 0); +} + +void ppc_store_dawr1(CPUPPCState *env, target_ulong val) +{ + env->spr[SPR_DAWR1] = val; + ppc_update_daw(env, 1); +} + +void ppc_store_dawrx1(CPUPPCState *env, uint32_t val) +{ + ppc_store_dawrx(env, val, 1); } + #endif #endif diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 173e4c351a4..6b90543811f 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -22,12 +22,14 @@ #include "qemu/int128.h" #include "qemu/cpu-float.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "cpu-qom.h" #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 +42,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 +193,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 +292,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 +527,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 +538,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 +553,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 +639,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) @@ -676,6 +783,8 @@ enum { 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, }; /* @@ -703,6 +812,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 */ @@ -1058,7 +1168,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 @@ -1101,6 +1215,9 @@ DEXCR_ASPECT(PHIE, 6) #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 */ @@ -1135,12 +1252,17 @@ struct CPUArchState { /* 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; + struct CPUWatchpoint *dawr_watchpoint[2]; #endif target_ulong sr[32]; /* segment registers */ uint32_t nb_BATs; /* number of BATs */ @@ -1151,7 +1273,6 @@ 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 */ @@ -1191,6 +1312,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; @@ -1227,6 +1358,18 @@ struct CPUArchState { * special way (such as routing some resume causes to 0x100, i.e. sreset). */ bool resume_as_sreset; + + /* + * On powernv, quiesced means the CPU has been stopped using PC direct + * control xscom registers. + * + * On spapr, quiesced means it is in the "RTAS stopped" state. + * + * The core halted/stopped variables aren't sufficient for this, because + * they can be changed with various side-band operations like qmp cont, + * powersave interrupts, etc. + */ + bool quiesced; #endif /* These resources are used only in TCG */ @@ -1236,6 +1379,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 @@ -1278,12 +1424,12 @@ struct CPUArchState { uint64_t pmu_base_time; }; -#define _CORE_ID(cs) \ - (POWERPC_CPU(cs)->env.spr_cb[SPR_PIR].default_value & ~(cs->nr_threads - 1)) - #define THREAD_SIBLING_FOREACH(cs, cs_sibling) \ CPU_FOREACH(cs_sibling) \ - if (_CORE_ID(cs) == _CORE_ID(cs_sibling)) + 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 { \ @@ -1313,16 +1459,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; @@ -1330,19 +1474,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); @@ -1374,7 +1567,7 @@ 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 */ @@ -1385,8 +1578,6 @@ 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 hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu); -const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name); #endif int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, DumpState *s); @@ -1403,20 +1594,22 @@ extern const VMStateDescription vmstate_ppc_cpu; /*****************************************************************************/ void ppc_translate_init(void); +void ppc_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); #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_update_daw(CPUPPCState *env, int rid); void ppc_store_dawr0(CPUPPCState *env, target_ulong value); void ppc_store_dawrx0(CPUPPCState *env, uint32_t value); +void ppc_store_dawr1(CPUPPCState *env, target_ulong value); +void ppc_store_dawrx1(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 */ uint64_t cpu_ppc_load_tbl(CPUPPCState *env); uint32_t cpu_ppc_load_tbu(CPUPPCState *env); @@ -1426,6 +1619,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); @@ -1449,10 +1644,6 @@ 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); void cpu_ppc_set_1lpar(PowerPCCPU *cpu); -int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, - target_ulong address, uint32_t pid); -int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid); -hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb); #endif void ppc_store_fpscr(CPUPPCState *env, target_ulong val); @@ -1480,11 +1671,9 @@ 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 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; @@ -1511,8 +1700,6 @@ void ppc_compat_add_property(Object *obj, const char *name, uint32_t *compat_pvr, const char *basedesc); #endif /* defined(TARGET_PPC64) */ -#include "exec/cpu-all.h" - /*****************************************************************************/ /* CRF definitions */ #define CRF_LT_BIT 3 @@ -1597,9 +1784,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) @@ -1610,22 +1799,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) @@ -1793,6 +1982,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) @@ -1903,7 +2098,9 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_DEXCR (0x33C) #define SPR_IC (0x350) #define SPR_VTB (0x351) +#define SPR_LDBAR (0x352) #define SPR_MMCRC (0x353) +#define SPR_PMSR (0x355) #define SPR_PSSCR (0x357) #define SPR_440_INV0 (0x370) #define SPR_440_INV1 (0x371) @@ -1911,8 +2108,10 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_440_INV2 (0x372) #define SPR_TRIG2 (0x372) #define SPR_440_INV3 (0x373) +#define SPR_PMCR (0x374) #define SPR_440_ITV0 (0x374) #define SPR_440_ITV1 (0x375) +#define SPR_RWMR (0x375) #define SPR_440_ITV2 (0x376) #define SPR_440_ITV3 (0x377) #define SPR_440_CCR1 (0x378) @@ -1925,6 +2124,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) @@ -1948,6 +2148,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) @@ -2129,6 +2330,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 */ @@ -2508,6 +2711,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)) @@ -2520,24 +2751,6 @@ 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, vaddr *pc, - uint64_t *cs_base, uint32_t *flags); -#else -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; - *flags = env->hflags; -} -#endif - -G_NORETURN void raise_exception(CPUPPCState *env, uint32_t exception); -G_NORETURN void raise_exception_ra(CPUPPCState *env, uint32_t exception, - uintptr_t raddr); -G_NORETURN void raise_exception_err(CPUPPCState *env, uint32_t exception, - uint32_t error_code); G_NORETURN void raise_exception_err_ra(CPUPPCState *env, uint32_t exception, uint32_t error_code, uintptr_t raddr); @@ -2662,6 +2875,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) @@ -2762,6 +2979,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); @@ -2805,12 +3023,19 @@ 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 */ #define POWERPC_FAMILY(_name) \ static void \ - glue(glue(ppc_, _name), _cpu_family_class_init)(ObjectClass *, void *); \ + glue(glue(ppc_, _name), _cpu_family_class_init)(ObjectClass *, \ + const void *); \ \ static const TypeInfo \ glue(glue(ppc_, _name), _cpu_family_type_info) = { \ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 7ab5ee92d90..a0e77f2673e 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -1,3 +1,4 @@ + /* * PowerPC CPU initialization for qemu. * @@ -21,9 +22,9 @@ #include "qemu/osdep.h" #include "disas/dis-asm.h" #include "gdbstub/helpers.h" -#include "sysemu/cpus.h" -#include "sysemu/hw_accel.h" -#include "sysemu/tcg.h" +#include "system/cpus.h" +#include "system/hw_accel.h" +#include "system/tcg.h" #include "cpu-models.h" #include "mmu-hash32.h" #include "mmu-hash64.h" @@ -31,7 +32,7 @@ #include "qemu/module.h" #include "qemu/qemu-print.h" #include "qapi/error.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "qapi/visitor.h" #include "hw/qdev-properties.h" #include "hw/ppc/ppc.h" @@ -39,18 +40,18 @@ #include "qemu/cutils.h" #include "disas/capstone.h" #include "fpu/softfloat.h" - +#include "exec/watchpoint.h" #include "helper_regs.h" #include "internal.h" #include "spr_common.h" #include "power8-pmu.h" - #ifndef CONFIG_USER_ONLY #include "hw/boards.h" #include "hw/intc/intc.h" #include "kvm_ppc.h" #endif +#include "cpu_init.h" /* #define PPC_DEBUG_SPR */ /* #define USE_APPLE_GDB */ @@ -246,7 +247,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 +793,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", @@ -920,6 +921,18 @@ static void register_BookE206_sprs(CPUPPCState *env, uint32_t mas_mask, #endif } +static void register_atb_sprs(CPUPPCState *env) +{ + spr_register(env, SPR_ATBL, "ATBL", + &spr_read_atbl, SPR_NOACCESS, + &spr_read_atbl, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_ATBU, "ATBU", + &spr_read_atbu, SPR_NOACCESS, + &spr_read_atbu, SPR_NOACCESS, + 0x00000000); +} + /* SPR specific to PowerPC 440 implementation */ static void register_440_sprs(CPUPPCState *env) { @@ -1642,7 +1655,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; @@ -2107,20 +2120,43 @@ static int check_pow_hid0_74xx(CPUPPCState *env) return 0; } -static void init_proc_405(CPUPPCState *env) +#if defined(TARGET_PPC64) +static int check_attn_hid0(CPUPPCState *env) { - register_40x_sprs(env); - register_405_sprs(env); - register_usprgh_sprs(env); + if (env->spr[SPR_HID0] & HID0_ENABLE_ATTN) { + return 1; + } - /* Memory management */ -#if !defined(CONFIG_USER_ONLY) + 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->id_tlbs = 0; env->tlb_type = TLB_EMB; #endif - init_excp_4xx_softmmu(env); +} + +static void init_proc_405(CPUPPCState *env) +{ + register_40x_sprs(env); + register_405_sprs(env); + register_usprgh_sprs(env); + + init_tlbs_emb(env); + init_excp_4xx(env); env->dcache_line_size = 32; env->icache_line_size = 32; /* Allocate hardware IRQ controller */ @@ -2130,7 +2166,7 @@ static void init_proc_405(CPUPPCState *env) SET_WDT_PERIOD(16, 20, 24, 28); } -POWERPC_FAMILY(405)(ObjectClass *oc, void *data) +POWERPC_FAMILY(405)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2138,6 +2174,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 | @@ -2186,13 +2223,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; @@ -2202,7 +2234,7 @@ static void init_proc_440EP(CPUPPCState *env) SET_WDT_PERIOD(20, 24, 28, 32); } -POWERPC_FAMILY(440EP)(ObjectClass *oc, void *data) +POWERPC_FAMILY(440EP)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2210,6 +2242,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 | @@ -2240,7 +2273,7 @@ POWERPC_FAMILY(440EP)(ObjectClass *oc, void *data) POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; } -POWERPC_FAMILY(460EX)(ObjectClass *oc, void *data) +POWERPC_FAMILY(460EX)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2248,6 +2281,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 | @@ -2284,13 +2318,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; @@ -2300,7 +2328,7 @@ static void init_proc_440GP(CPUPPCState *env) SET_WDT_PERIOD(20, 24, 28, 32); } -POWERPC_FAMILY(440GP)(ObjectClass *oc, void *data) +POWERPC_FAMILY(440GP)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2308,6 +2336,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 | @@ -2358,13 +2387,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; @@ -2374,7 +2398,7 @@ static void init_proc_440x5(CPUPPCState *env) SET_WDT_PERIOD(20, 24, 28, 32); } -POWERPC_FAMILY(440x5)(ObjectClass *oc, void *data) +POWERPC_FAMILY(440x5)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2382,6 +2406,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 | @@ -2409,7 +2434,7 @@ POWERPC_FAMILY(440x5)(ObjectClass *oc, void *data) POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; } -POWERPC_FAMILY(440x5wDFPU)(ObjectClass *oc, void *data) +POWERPC_FAMILY(440x5wDFPU)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2417,6 +2442,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 | @@ -2457,7 +2483,7 @@ static void init_proc_MPC5xx(CPUPPCState *env) /* XXX: TODO: allocate internal IRQ controller */ } -POWERPC_FAMILY(MPC5xx)(ObjectClass *oc, void *data) +POWERPC_FAMILY(MPC5xx)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2465,6 +2491,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 | @@ -2499,7 +2526,7 @@ static void init_proc_MPC8xx(CPUPPCState *env) /* XXX: TODO: allocate internal IRQ controller */ } -POWERPC_FAMILY(MPC8xx)(ObjectClass *oc, void *data) +POWERPC_FAMILY(MPC8xx)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2507,6 +2534,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; @@ -2549,7 +2577,7 @@ static void init_proc_G2(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(G2)(ObjectClass *oc, void *data) +POWERPC_FAMILY(G2)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2557,6 +2585,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 | @@ -2587,7 +2616,7 @@ POWERPC_FAMILY(G2)(ObjectClass *oc, void *data) POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; } -POWERPC_FAMILY(G2LE)(ObjectClass *oc, void *data) +POWERPC_FAMILY(G2LE)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2595,6 +2624,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 | @@ -2713,27 +2743,15 @@ static void init_proc_e200(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); /* TOFIX */ - spr_register(env, SPR_BOOKE_DSRR0, "DSRR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - spr_register(env, SPR_BOOKE_DSRR1, "DSRR1", - 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; /* XXX: TODO: allocate internal IRQ controller */ } -POWERPC_FAMILY(e200)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e200)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2741,6 +2759,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 @@ -2843,7 +2862,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); @@ -2896,6 +2914,11 @@ static void init_proc_e500(CPUPPCState *env, int version) register_BookE206_sprs(env, 0x000000DF, tlbncfg, mmucfg); register_usprgh_sprs(env); + if (version != fsl_e500v1) { + /* e500v1 has no support for alternate timebase */ + register_atb_sprs(env); + } + spr_register(env, SPR_HID0, "HID0", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, @@ -3021,7 +3044,7 @@ static void init_proc_e500v1(CPUPPCState *env) init_proc_e500(env, fsl_e500v1); } -POWERPC_FAMILY(e500v1)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e500v1)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3029,6 +3052,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 | @@ -3064,7 +3088,7 @@ static void init_proc_e500v2(CPUPPCState *env) init_proc_e500(env, fsl_e500v2); } -POWERPC_FAMILY(e500v2)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e500v2)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3072,6 +3096,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 | @@ -3107,7 +3132,7 @@ static void init_proc_e500mc(CPUPPCState *env) init_proc_e500(env, fsl_e500mc); } -POWERPC_FAMILY(e500mc)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e500mc)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3115,6 +3140,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 | @@ -3153,7 +3179,7 @@ static void init_proc_e5500(CPUPPCState *env) init_proc_e500(env, fsl_e5500); } -POWERPC_FAMILY(e5500)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e5500)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3161,6 +3187,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 | @@ -3201,7 +3228,7 @@ static void init_proc_e6500(CPUPPCState *env) init_proc_e500(env, fsl_e6500); } -POWERPC_FAMILY(e6500)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e6500)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3209,6 +3236,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 | @@ -3263,7 +3291,7 @@ static void init_proc_603(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(603)(ObjectClass *oc, void *data) +POWERPC_FAMILY(603)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3271,6 +3299,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 | @@ -3302,7 +3331,7 @@ POWERPC_FAMILY(603)(ObjectClass *oc, void *data) POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; } -POWERPC_FAMILY(603E)(ObjectClass *oc, void *data) +POWERPC_FAMILY(603E)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3310,6 +3339,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 | @@ -3347,7 +3377,7 @@ static void init_proc_e300(CPUPPCState *env) register_e300_sprs(env); } -POWERPC_FAMILY(e300)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e300)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3355,6 +3385,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 | @@ -3402,7 +3433,7 @@ static void init_proc_604(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(604)(ObjectClass *oc, void *data) +POWERPC_FAMILY(604)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3410,6 +3441,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 | @@ -3447,7 +3479,7 @@ static void init_proc_604E(CPUPPCState *env) register_604e_sprs(env); } -POWERPC_FAMILY(604E)(ObjectClass *oc, void *data) +POWERPC_FAMILY(604E)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3455,6 +3487,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 | @@ -3503,7 +3536,7 @@ static void init_proc_740(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(740)(ObjectClass *oc, void *data) +POWERPC_FAMILY(740)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3511,6 +3544,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 | @@ -3568,7 +3602,7 @@ static void init_proc_750(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(750)(ObjectClass *oc, void *data) +POWERPC_FAMILY(750)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3576,6 +3610,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 | @@ -3714,7 +3749,7 @@ static void init_proc_750cl(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(750cl)(ObjectClass *oc, void *data) +POWERPC_FAMILY(750cl)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3722,6 +3757,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: @@ -3821,7 +3857,7 @@ static void init_proc_750cx(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(750cx)(ObjectClass *oc, void *data) +POWERPC_FAMILY(750cx)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3829,6 +3865,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 | @@ -3893,7 +3930,7 @@ static void init_proc_750fx(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(750fx)(ObjectClass *oc, void *data) +POWERPC_FAMILY(750fx)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3901,6 +3938,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 | @@ -3965,7 +4003,7 @@ static void init_proc_750gx(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(750gx)(ObjectClass *oc, void *data) +POWERPC_FAMILY(750gx)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3973,6 +4011,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 | @@ -4024,7 +4063,7 @@ static void init_proc_745(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(745)(ObjectClass *oc, void *data) +POWERPC_FAMILY(745)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4032,6 +4071,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 | @@ -4069,7 +4109,7 @@ static void init_proc_755(CPUPPCState *env) register_755_sprs(env); } -POWERPC_FAMILY(755)(ObjectClass *oc, void *data) +POWERPC_FAMILY(755)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4077,6 +4117,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 | @@ -4135,7 +4176,7 @@ static void init_proc_7400(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(7400)(ObjectClass *oc, void *data) +POWERPC_FAMILY(7400)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4143,6 +4184,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 | @@ -4214,7 +4256,7 @@ static void init_proc_7410(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(7410)(ObjectClass *oc, void *data) +POWERPC_FAMILY(7410)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4222,6 +4264,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 | @@ -4314,7 +4357,7 @@ static void init_proc_7440(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(7440)(ObjectClass *oc, void *data) +POWERPC_FAMILY(7440)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4322,6 +4365,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 | @@ -4436,7 +4480,7 @@ static void init_proc_7450(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(7450)(ObjectClass *oc, void *data) +POWERPC_FAMILY(7450)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4444,6 +4488,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 | @@ -4565,7 +4610,7 @@ static void init_proc_7445(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(7445)(ObjectClass *oc, void *data) +POWERPC_FAMILY(7445)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4573,6 +4618,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 | @@ -4696,7 +4742,7 @@ static void init_proc_7455(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(7455)(ObjectClass *oc, void *data) +POWERPC_FAMILY(7455)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4704,6 +4750,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 | @@ -4847,7 +4894,7 @@ static void init_proc_7457(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(7457)(ObjectClass *oc, void *data) +POWERPC_FAMILY(7457)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4855,6 +4902,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 | @@ -4981,7 +5029,7 @@ static void init_proc_e600(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(e600)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e600)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4989,6 +5037,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 | @@ -5062,7 +5111,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); @@ -5070,11 +5119,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); @@ -5131,6 +5180,20 @@ static void register_book3s_207_dbg_sprs(CPUPPCState *env) KVM_REG_PPC_CIABR, 0x00000000); } +static void register_book3s_310_dbg_sprs(CPUPPCState *env) +{ + spr_register_kvm_hv(env, SPR_DAWR1, "DAWR1", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_dawr1, + KVM_REG_PPC_DAWR1, 0x00000000); + spr_register_kvm_hv(env, SPR_DAWRX1, "DAWRX1", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_dawrx1, + KVM_REG_PPC_DAWRX1, 0x00000000); +} + static void register_970_dbg_sprs(CPUPPCState *env) { /* Breakpoints */ @@ -5152,7 +5215,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, @@ -5308,6 +5371,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 */ @@ -5347,7 +5442,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, @@ -5383,7 +5478,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_generic32, + &spr_read_generic, &spr_core_write_generic32, 0x00000000); spr_register_hv(env, SPR_MMCRH, "MMCRH", SPR_NOACCESS, SPR_NOACCESS, @@ -5423,7 +5518,7 @@ static void register_book3s_ids_sprs(CPUPPCState *env) 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); } @@ -5465,7 +5560,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); @@ -5483,7 +5578,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); @@ -5517,6 +5612,14 @@ static void register_HEIR64_spr(CPUPPCState *env) 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", @@ -5643,7 +5746,7 @@ static void register_power_common_book4_sprs(CPUPPCState *env) spr_register_hv(env, SPR_TSCR, "TSCR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic32, + &spr_read_generic, &spr_core_write_generic32, 0x00000000); spr_register_hv(env, SPR_HMER, "HMER", SPR_NOACCESS, SPR_NOACCESS, @@ -5653,7 +5756,7 @@ static void register_power_common_book4_sprs(CPUPPCState *env) spr_register_hv(env, SPR_HMEER, "HMEER", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_core_write_generic, 0x00000000); spr_register_hv(env, SPR_TFMR, "TFMR", SPR_NOACCESS, SPR_NOACCESS, @@ -5670,6 +5773,16 @@ static void register_power_common_book4_sprs(CPUPPCState *env) &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 } @@ -5682,6 +5795,23 @@ static void register_power9_book4_sprs(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, KVM_REG_PPC_WORT, 0); + spr_register_hv(env, SPR_RWMR, "RWMR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + 0x00000000); + + /* 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 } @@ -5695,7 +5825,7 @@ static void register_power8_book4_sprs(CPUPPCState *env) &spr_read_generic, &spr_write_generic, KVM_REG_PPC_ACOP, 0); /* PID is only in BookE in ISA v2.07 */ - spr_register_kvm(env, SPR_BOOKS_PID, "PID", + spr_register_kvm(env, SPR_BOOKS_PID, "PIDR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_pidr, KVM_REG_PPC_PID, 0); @@ -5716,7 +5846,7 @@ static void register_power7_book4_sprs(CPUPPCState *env) &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, "PID", + spr_register_kvm(env, SPR_BOOKS_PID, "PIDR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic32, KVM_REG_PPC_PID, 0); @@ -5729,7 +5859,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 } @@ -5750,7 +5880,7 @@ static void register_power9_mmu_sprs(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x0000000000000000); /* PID is part of the BookS ISA from v3.0 */ - spr_register_kvm(env, SPR_BOOKS_PID, "PID", + spr_register_kvm(env, SPR_BOOKS_PID, "PIDR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_pidr, KVM_REG_PPC_PID, 0); @@ -5760,7 +5890,7 @@ static void register_power9_mmu_sprs(CPUPPCState *env) 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; @@ -5773,25 +5903,25 @@ 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(env, SPR_DEXCR, "DEXCR", + spr_register_kvm(env, SPR_DEXCR, "DEXCR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic32, + &spr_read_generic, &spr_write_generic, KVM_REG_PPC_DEXCR, 0); - spr_register(env, SPR_UDEXCR, "DEXCR", + spr_register(env, SPR_UDEXCR, "UDEXCR", &spr_read_dexcr_ureg, SPR_NOACCESS, &spr_read_dexcr_ureg, SPR_NOACCESS, 0); @@ -5799,10 +5929,10 @@ static void register_power10_dexcr_sprs(CPUPPCState *env) spr_register_hv(env, SPR_HDEXCR, "HDEXCR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic32, + &spr_read_generic, &spr_write_generic, 0); - spr_register(env, SPR_UHDEXCR, "HDEXCR", + spr_register(env, SPR_UHDEXCR, "UHDEXCR", &spr_read_dexcr_ureg, SPR_NOACCESS, &spr_read_dexcr_ureg, SPR_NOACCESS, 0); @@ -5864,7 +5994,7 @@ static void init_proc_970(CPUPPCState *env) ppc970_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(970)(ObjectClass *oc, void *data) +POWERPC_FAMILY(970)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -5872,6 +6002,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 | @@ -5938,7 +6069,7 @@ static void init_proc_power5plus(CPUPPCState *env) ppc970_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) +POWERPC_FAMILY(POWER5P)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -5947,6 +6078,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 | @@ -6010,6 +6142,7 @@ static void init_proc_POWER7(CPUPPCState *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 */ @@ -6042,18 +6175,20 @@ static bool ppc_pvr_match_power7(PowerPCCPUClass *pcc, uint32_t pvr, bool best) return true; } -POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) +POWERPC_FAMILY(POWER7)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); 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 | @@ -6110,6 +6245,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 */ @@ -6133,6 +6290,7 @@ static void init_proc_POWER8(CPUPPCState *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); @@ -6151,6 +6309,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)); @@ -6179,18 +6339,20 @@ static bool ppc_pvr_match_power8(PowerPCCPUClass *pcc, uint32_t pvr, bool best) return true; } -POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) +POWERPC_FAMILY(POWER8)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); 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 | @@ -6275,7 +6437,8 @@ static struct ppc_radix_page_info POWER9_radix_page_info = { }; #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); @@ -6294,8 +6457,8 @@ static void init_proc_POWER9(CPUPPCState *env) register_power5p_ear_sprs(env); register_power5p_tb_sprs(env); register_power6_common_sprs(env); - register_HEIR32_spr(env); register_power6_dbg_sprs(env); + register_power7_common_sprs(env); register_power8_tce_address_control_sprs(env); register_power8_ids_sprs(env); register_power8_ebb_sprs(env); @@ -6311,20 +6474,38 @@ static void init_proc_POWER9(CPUPPCState *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); + spr_register_hv(env, SPR_PMSR, "PMSR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_pmsr, SPR_NOACCESS, + 0); + spr_register_hv(env, SPR_PMCR, "PMCR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pmcr, + PPC_BIT(63)); /* Version 1 (POWER9/10) */ + +} + +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)); @@ -6350,10 +6531,7 @@ static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr, bool best) return false; } - if ((pvr & 0x0f00) == 0x100) { - /* DD1.x always matches power9_v1.0 */ - return true; - } else if ((pvr & 0x0f00) == 0x200) { + if ((pvr & 0x0f00) == 0x200) { if ((pvr & 0xf) < 2) { /* DD2.0, DD2.1 match power9_v2.0 */ if ((pcc->pvr & 0xf) == 0) { @@ -6370,65 +6548,24 @@ static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr, bool best) return false; } -POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) +POWERPC_FAMILY(POWER9)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); 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_USER_ONLY) @@ -6441,10 +6578,7 @@ 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; } @@ -6468,53 +6602,22 @@ static struct ppc_radix_page_info POWER10_radix_page_info = { }; #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_power9_common_sprs(env); register_HEIR64_spr(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_power9_book4_sprs(env); - register_power8_rpr_sprs(env); - register_power9_mmu_sprs(env); + register_book3s_310_dbg_sprs(env); register_power10_hash_sprs(env); register_power10_dexcr_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_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)); @@ -6536,75 +6639,31 @@ 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; } return false; } -POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) +POWERPC_FAMILY(POWER10)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); 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; @@ -6617,10 +6676,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, const 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; } @@ -6631,6 +6747,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 @@ -6645,7 +6762,8 @@ void cpu_ppc_set_1lpar(PowerPCCPU *cpu) /* * pseries SMT means "LPAR per core" mode, e.g., msgsndp is usable - * between threads. + * 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; @@ -6682,10 +6800,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)) { @@ -6774,20 +6888,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 */ @@ -6798,6 +6909,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 !"); + } } @@ -6837,7 +6953,7 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp) pcc->parent_realize(dev, errp); - if (env_cpu(env)->nr_threads > 1) { + if (!ppc_cpu_core_single_threaded(cs)) { env->flags |= POWERPC_FLAG_SMT; } @@ -6965,7 +7081,7 @@ ObjectClass *ppc_cpu_class_by_name(const char *name) if (strcmp(name, "max") == 0) { MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); if (mc) { - return object_class_by_name(mc->default_cpu_type); + return object_class_by_name(machine_class_default_cpu_type(mc)); } } #endif @@ -6998,7 +7114,7 @@ PowerPCCPUClass *ppc_cpu_get_family_class(PowerPCCPUClass *pcc) } /* Sort by PVR, ordering special case "host" last. */ -static gint ppc_cpu_list_compare(gconstpointer a, gconstpointer b) +static gint ppc_cpu_list_compare(gconstpointer a, gconstpointer b, gpointer d) { ObjectClass *oc_a = (ObjectClass *)a; ObjectClass *oc_b = (ObjectClass *)b; @@ -7036,9 +7152,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); @@ -7051,28 +7166,29 @@ 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); } } g_free(name); } -void ppc_cpu_list(void) +static 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); + list = g_slist_sort_with_data(list, ppc_cpu_list_compare, NULL); g_slist_foreach(list, ppc_cpu_list_entry, NULL); g_slist_free(list); #ifdef CONFIG_KVM qemu_printf("\n"); - qemu_printf("PowerPC %s\n", "host"); + qemu_printf(" %s\n", "host"); #endif } @@ -7099,24 +7215,31 @@ static void ppc_restore_state_to_opc(CPUState *cs, cpu->env.nip = data[0]; } + +static int ppc_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return ppc_env_mmu_index(cpu_env(cs), ifetch); +} #endif /* CONFIG_TCG */ +#ifndef CONFIG_USER_ONLY static bool ppc_cpu_has_work(CPUState *cs) { return cs->interrupt_request & CPU_INTERRUPT_HARD; } +#endif /* !CONFIG_USER_ONLY */ -static void ppc_cpu_reset_hold(Object *obj) +static void ppc_cpu_reset_hold(Object *obj, ResetType type) { - CPUState *s = CPU(obj); - PowerPCCPU *cpu = POWERPC_CPU(s); - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + 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; if (pcc->parent_phases.hold) { - pcc->parent_phases.hold(obj); + pcc->parent_phases.hold(obj, type); } msr = (target_ulong)0; @@ -7159,12 +7282,12 @@ static void ppc_cpu_reset_hold(Object *obj) env->nip = env->hreset_vector | env->excp_prefix; if (tcg_enabled()) { - 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); 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 */ @@ -7174,13 +7297,43 @@ static void ppc_cpu_reset_hold(Object *obj) 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); + /* Similarly for flush-to-zero */ + set_float_ftz_detection(float_ftz_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); + /* + * NaN propagation for fused multiply-add: + * if fRA is a NaN return it; otherwise if fRB is a NaN return it; + * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB + * whereas QEMU labels the operands as (a * b) + c. + */ + set_float_3nan_prop_rule(float_3nan_prop_acb, &env->fp_status); + set_float_3nan_prop_rule(float_3nan_prop_acb, &env->vec_status); + /* + * For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer + * to return an input NaN if we have one (ie c) rather than generating + * a default NaN + */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->vec_status); + + /* Default NaN: sign bit clear, set frac msb */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); + set_float_default_nan_pattern(0b01000000, &env->vec_status); for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) { ppc_spr_t *spr = &env->spr_cb[i]; @@ -7190,18 +7343,19 @@ static void ppc_cpu_reset_hold(Object *obj) } 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, @@ -7220,9 +7374,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); } } @@ -7231,11 +7383,15 @@ 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); } } + +static vaddr ppc_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + return (cpu_env(cs)->hflags >> HFLAGS_64) & 1 ? result : (uint32_t)result; +} #endif /* CONFIG_TCG */ #endif /* !CONFIG_USER_ONLY */ @@ -7246,7 +7402,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; @@ -7258,6 +7413,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 @@ -7289,11 +7445,12 @@ 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; + } else { + info->endian = BFD_ENDIAN_BIG; } info->mach = env->bfd_mach; if (!env->bfd_mach) { @@ -7310,19 +7467,11 @@ static void ppc_disas_set_info(CPUState *cs, disassemble_info *info) #endif } -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), - DEFINE_PROP_END_OF_LIST(), -}; - #ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps ppc_sysemu_ops = { + .has_work = ppc_cpu_has_work, .get_phys_page_debug = ppc_cpu_get_phys_page_debug, .write_elf32_note = ppc32_cpu_write_elf32_note, .write_elf64_note = ppc64_cpu_write_elf64_note, @@ -7332,17 +7481,25 @@ static const struct SysemuCPUOps ppc_sysemu_ops = { #endif #ifdef CONFIG_TCG -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" -static const struct TCGCPUOps ppc_tcg_ops = { +static const TCGCPUOps ppc_tcg_ops = { + .mttcg_supported = TARGET_LONG_BITS == 64, + .guest_default_memory_order = 0, .initialize = ppc_translate_init, + .translate_code = ppc_translate_code, + .get_tb_cpu_state = ppc_get_tb_cpu_state, .restore_state_to_opc = ppc_restore_state_to_opc, + .mmu_index = ppc_cpu_mmu_index, #ifdef CONFIG_USER_ONLY .record_sigsegv = ppc_cpu_record_sigsegv, #else .tlb_fill = ppc_cpu_tlb_fill, + .pointer_wrap = ppc_pointer_wrap, .cpu_exec_interrupt = ppc_cpu_exec_interrupt, + .cpu_exec_halt = ppc_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = ppc_cpu_do_interrupt, .cpu_exec_enter = ppc_cpu_exec_enter, .cpu_exec_exit = ppc_cpu_exec_exit, @@ -7355,7 +7512,7 @@ static const struct TCGCPUOps ppc_tcg_ops = { }; #endif /* CONFIG_TCG */ -static void ppc_cpu_class_init(ObjectClass *oc, void *data) +static void ppc_cpu_class_init(ObjectClass *oc, const void *data) { PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); @@ -7367,13 +7524,12 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_unrealize(dc, ppc_cpu_unrealize, &pcc->parent_unrealize); pcc->pvr_match = ppc_pvr_match_default; - device_class_set_props(dc, ppc_cpu_properties); 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->list_cpus = ppc_cpu_list; cc->dump_state = ppc_cpu_dump_state; cc->set_pc = ppc_cpu_set_pc; cc->get_pc = ppc_cpu_get_pc; @@ -7382,12 +7538,14 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) #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; @@ -7420,7 +7578,7 @@ static const TypeInfo ppc_cpu_type_info = { .class_size = sizeof(PowerPCCPUClass), .class_init = ppc_cpu_class_init, #ifndef CONFIG_USER_ONLY - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_INTERRUPT_STATS_PROVIDER }, { } }, @@ -7448,8 +7606,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 " @@ -7459,7 +7616,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 diff --git a/target/ppc/cpu_init.h b/target/ppc/cpu_init.h new file mode 100644 index 00000000000..f8fd6ff5cd5 --- /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 5967ea07a92..ecc3f793267 100644 --- a/target/ppc/dfp_helper.c +++ b/target/ppc/dfp_helper.c @@ -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 72ec2be92e4..1efdc4066eb 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -19,23 +19,20 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/log.h" +#include "system/memory.h" +#include "system/tcg.h" +#include "system/system.h" +#include "system/runstate.h" #include "cpu.h" -#include "exec/exec-all.h" #include "internal.h" #include "helper_regs.h" #include "hw/ppc/ppc.h" #include "trace.h" -#ifdef CONFIG_TCG -#include "sysemu/tcg.h" -#include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" -#endif - /*****************************************************************************/ /* Exception processing */ -#if !defined(CONFIG_USER_ONLY) +#ifndef CONFIG_USER_ONLY static const char *powerpc_excp_name(int excp) { @@ -134,26 +131,6 @@ static void dump_hcall(CPUPPCState *env) env->nip); } -#ifdef CONFIG_TCG -/* Return true iff byteswap is needed to load instruction */ -static inline bool insn_need_byteswap(CPUArchState *env) -{ - /* SYSTEM builds TARGET_BIG_ENDIAN. Need to swap when MSR[LE] is set */ - return !!(env->msr & ((target_ulong)1 << MSR_LE)); -} - -static uint32_t ppc_ldl_code(CPUArchState *env, abi_ptr addr) -{ - uint32_t insn = cpu_ldl_code(env, addr); - - if (insn_need_byteswap(env)) { - insn = bswap32(insn); - } - - return insn; -} -#endif - static void ppc_excp_debug_sw_tlb(CPUPPCState *env, int excp) { const char *es; @@ -186,7 +163,7 @@ static void ppc_excp_debug_sw_tlb(CPUPPCState *env, int excp) env->error_code); } -#if defined(TARGET_PPC64) +#ifdef TARGET_PPC64 static int powerpc_reset_wakeup(CPUPPCState *env, int excp, target_ulong *msr) { /* We no longer are in a PM state */ @@ -321,10 +298,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; } @@ -348,10 +322,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; } @@ -380,7 +351,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) { @@ -403,9 +374,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; @@ -426,57 +396,36 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector, static void powerpc_mcheck_checkstop(CPUPPCState *env) { - CPUState *cs = env_cpu(env); - + /* KVM guests always have MSR[ME] enabled */ if (FIELD_EX64(env->msr, MSR, ME)) { return; } - - /* Machine check exception is not enabled. Enter checkstop state. */ - fprintf(stderr, "Machine check while not allowed. " - "Entering checkstop state\n"); - if (qemu_log_separate()) { - qemu_log("Machine check while not allowed. " - "Entering checkstop state\n"); - } - cs->halted = 1; - cpu_interrupt_exittb(cs); + assert(tcg_enabled()); + powerpc_checkstop(env, "machine check with MSR[ME]=0"); } 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) { @@ -488,7 +437,6 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) 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; @@ -523,7 +471,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; } @@ -550,52 +498,41 @@ 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) { @@ -605,7 +542,6 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) 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]); @@ -633,11 +569,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; @@ -653,7 +587,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; } @@ -675,8 +609,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 */ @@ -703,60 +638,44 @@ 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) { @@ -764,7 +683,6 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) 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]); @@ -792,11 +710,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; @@ -812,7 +728,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; } @@ -840,9 +756,7 @@ 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; } @@ -854,8 +768,9 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) break; case POWERPC_EXCP_RESET: /* System reset exception */ if (FIELD_EX64(env->msr, MSR, POW)) { - cpu_abort(cs, "Trying to deliver power-saving system reset " - "exception %d with no HV support\n", excp); + cpu_abort(env_cpu(env), + "Trying to deliver power-saving system reset exception " + "%d with no HV support\n", excp); } break; case POWERPC_EXCP_TRACE: /* Trace exception */ @@ -864,71 +779,53 @@ 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) { @@ -936,7 +833,6 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) 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]); @@ -964,11 +860,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; @@ -984,7 +878,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; } @@ -1012,9 +906,7 @@ static void powerpc_excp_74xx(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; } @@ -1026,7 +918,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; @@ -1039,54 +932,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; - - msr = env->msr; + int srr0 = SPR_SRR0, srr1 = SPR_SRR1; /* - * new interrupt handler msr preserves existing ME unless - * explicitly overriden + * 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. */ - new_msr = env->msr & ((target_ulong)1 << MSR_ME); + msr = env->msr; - /* target registers */ - srr0 = SPR_SRR0; - srr1 = SPR_SRR1; + /* 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; } @@ -1103,10 +981,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) { @@ -1135,6 +1012,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); } @@ -1149,11 +1027,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; @@ -1173,7 +1049,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; } @@ -1214,7 +1090,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 */ @@ -1228,21 +1105,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; @@ -1251,12 +1130,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); } @@ -1288,7 +1163,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) @@ -1312,6 +1187,10 @@ 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 @@ -1322,6 +1201,15 @@ static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp) } 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) && @@ -1332,10 +1220,10 @@ static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp) * instruction at NIP would cause recursive faults with the same * translation). */ - break; + return false; } - /* fall through */ - case POWERPC_EXCP_MCHECK: + break; + case POWERPC_EXCP_DSI: case POWERPC_EXCP_DSEG: case POWERPC_EXCP_ALIGN: @@ -1346,17 +1234,13 @@ static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_VPU: case POWERPC_EXCP_VSXU: case POWERPC_EXCP_FU: - case POWERPC_EXCP_HV_FU: { - uint32_t insn = ppc_ldl_code(env, env->nip); - if (is_prefix_insn(env, insn)) { - return true; - } + case POWERPC_EXCP_HV_FU: break; - } default: - break; + return false; } - return false; + + return is_prefix_insn(env, ppc_ldl_code(env, env->nip)); } #else static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp) @@ -1367,24 +1251,19 @@ static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp) 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 @@ -1406,10 +1285,9 @@ 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)) { @@ -1425,10 +1303,10 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) * clear (e.g., see FWNMI in PAPR). */ new_msr |= (target_ulong)MSR_HVB; - } - /* machine check exceptions don't have ME set */ - new_msr &= ~((target_ulong)1 << MSR_ME); + /* HV machine check exceptions don't have ME set */ + new_msr &= ~((target_ulong)1 << MSR_ME); + } msr |= env->error_code; break; @@ -1444,23 +1322,17 @@ 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 */ @@ -1483,11 +1355,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; @@ -1503,7 +1373,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; } @@ -1525,9 +1395,7 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) /* "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); + cpu->vhyp_class->hypercall(cpu->vhyp, cpu); powerpc_reset_excp_state(cpu); return; } @@ -1569,7 +1437,8 @@ 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); } } @@ -1641,50 +1510,37 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_VPUA: /* Vector assist exception */ case POWERPC_EXCP_MAINT: /* Maintenance exception */ 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); } } @@ -1693,15 +1549,15 @@ 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) +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 @@ -1730,6 +1586,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: @@ -1744,58 +1601,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) { 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) { @@ -1805,9 +1665,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))) || @@ -1817,10 +1677,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; } } @@ -1833,39 +1693,42 @@ 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) { CPUState *cs = env_cpu(env); @@ -1876,18 +1739,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; @@ -1895,9 +1758,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))) || @@ -1907,20 +1770,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. @@ -1940,60 +1803,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) { 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) { @@ -2001,7 +1869,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 @@ -2012,14 +1880,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; @@ -2027,18 +1895,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))) || @@ -2048,20 +1916,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. @@ -2075,18 +1943,39 @@ 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; + if (unlikely(env->quiesced)) { + return 0; + } + +#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 */ @@ -2105,9 +1994,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; @@ -2115,18 +2004,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))) || @@ -2136,45 +2025,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. @@ -2189,23 +2078,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. @@ -2222,7 +2094,7 @@ static int ppc_next_unmasked_interrupt(CPUPPCState *env) void ppc_maybe_interrupt(CPUPPCState *env) { CPUState *cs = env_cpu(env); - QEMU_IOTHREAD_LOCK_GUARD(); + BQL_LOCK_GUARD(); if (ppc_next_unmasked_interrupt(env)) { cpu_interrupt(cs, CPU_INTERRUPT_HARD); @@ -2231,11 +2103,10 @@ void ppc_maybe_interrupt(CPUPPCState *env) } } -#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 */ @@ -2261,7 +2132,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: @@ -2279,14 +2149,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 */ @@ -2312,7 +2182,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 { @@ -2320,11 +2192,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 */ @@ -2350,7 +2223,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); } } @@ -2376,6 +2250,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; @@ -2395,15 +2270,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 */ @@ -2429,15 +2307,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 */ @@ -2504,7 +2396,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 */ @@ -2534,34 +2425,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); } @@ -2583,13 +2461,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) { @@ -2609,748 +2487,3 @@ bool ppc_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } #endif /* !CONFIG_USER_ONLY */ - -/*****************************************************************************/ -/* Exceptions processing helpers */ - -void raise_exception_err_ra(CPUPPCState *env, uint32_t exception, - uint32_t error_code, uintptr_t raddr) -{ - CPUState *cs = env_cpu(env); - - cs->exception_index = exception; - env->error_code = error_code; - cpu_loop_exit_restore(cs, raddr); -} - -void raise_exception_err(CPUPPCState *env, uint32_t exception, - uint32_t error_code) -{ - raise_exception_err_ra(env, exception, error_code, 0); -} - -void raise_exception(CPUPPCState *env, uint32_t exception) -{ - raise_exception_err_ra(env, exception, 0, 0); -} - -void raise_exception_ra(CPUPPCState *env, uint32_t exception, - uintptr_t raddr) -{ - raise_exception_err_ra(env, exception, 0, raddr); -} - -#ifdef CONFIG_TCG -void helper_raise_exception_err(CPUPPCState *env, uint32_t exception, - uint32_t error_code) -{ - raise_exception_err_ra(env, exception, error_code, 0); -} - -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 -void helper_store_msr(CPUPPCState *env, target_ulong val) -{ - uint32_t excp = hreg_store_msr(env, val, 0); - - if (excp != 0) { - cpu_interrupt_exittb(env_cpu(env)); - raise_exception(env, excp); - } -} - -void helper_ppc_maybe_interrupt(CPUPPCState *env) -{ - ppc_maybe_interrupt(env); -} - -#if defined(TARGET_PPC64) -void helper_scv(CPUPPCState *env, uint32_t lev) -{ - if (env->spr[SPR_FSCR] & (1ull << FSCR_SCV)) { - raise_exception_err(env, POWERPC_EXCP_SYSCALL_VECTORED, lev); - } else { - raise_exception_err(env, POWERPC_EXCP_FU, FSCR_IC_SCV); - } -} - -void helper_pminsn(CPUPPCState *env, uint32_t insn) -{ - CPUState *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) */ - -static void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr) -{ - /* MSR:POW cannot be set by any form of rfi */ - msr &= ~(1ULL << MSR_POW); - - /* MSR:TGPR cannot be set by any form of rfi */ - if (env->flags & POWERPC_FLAG_TGPR) - msr &= ~(1ULL << MSR_TGPR); - -#if defined(TARGET_PPC64) - /* Switching to 32-bit ? Crop the nip */ - if (!msr_is_64bit(env, msr)) { - nip = (uint32_t)nip; - } -#else - nip = (uint32_t)nip; -#endif - /* XXX: beware: this is false if VLE is supported */ - env->nip = nip & ~((target_ulong)0x00000003); - hreg_store_msr(env, msr, 1); - trace_ppc_excp_rfi(env->nip, env->msr); - /* - * No need to raise an exception here, as rfi is always the last - * insn of a TB - */ - cpu_interrupt_exittb(env_cpu(env)); - /* Reset the reservation */ - env->reserve_addr = -1; - - /* Context synchronizing: check if TCG TLB needs flush */ - check_tlb_flush(env, false); -} - -void helper_rfi(CPUPPCState *env) -{ - do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful); -} - -#if defined(TARGET_PPC64) -void helper_rfid(CPUPPCState *env) -{ - /* - * The architecture defines a number of rules for which bits can - * change but in practice, we handle this in hreg_store_msr() - * which will be called by do_rfi(), so there is no need to filter - * here - */ - do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1]); -} - -void helper_rfscv(CPUPPCState *env) -{ - do_rfi(env, env->lr, env->ctr); -} - -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; - - /* - * Handling of BESCR bits 32:33 according to PowerISA v3.1: - * - * "If BESCR 32:33 != 0b00 the instruction is treated as if - * the instruction form were invalid." - */ - if (env->spr[SPR_BESCR] & BESCR_INVALID) { - raise_exception_err(env, POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL); - } - - env->nip = env->spr[SPR_EBBRR]; - - /* Switching to 32-bit ? Crop the nip */ - if (!msr_is_64bit(env, msr)) { - env->nip = (uint32_t)env->spr[SPR_EBBRR]; - } - - if (s) { - env->spr[SPR_BESCR] |= BESCR_GE; - } else { - env->spr[SPR_BESCR] &= ~BESCR_GE; - } -} - -/* - * Triggers or queues an 'ebb_excp' EBB exception. All checks - * but FSCR, HFSCR and msr_pr must be done beforehand. - * - * PowerISA v3.1 isn't clear about whether an EBB should be - * postponed or cancelled if the EBB facility is unavailable. - * Our assumption here is that the EBB is cancelled if both - * FSCR and HFSCR EBB facilities aren't available. - */ -static void do_ebb(CPUPPCState *env, int ebb_excp) -{ - PowerPCCPU *cpu = env_archcpu(env); - - /* - * FSCR_EBB and FSCR_IC_EBB are the same bits used with - * HFSCR. - */ - helper_fscr_facility_check(env, FSCR_EBB, 0, FSCR_IC_EBB); - helper_hfscr_facility_check(env, FSCR_EBB, "EBB", FSCR_IC_EBB); - - if (ebb_excp == POWERPC_EXCP_PERFM_EBB) { - env->spr[SPR_BESCR] |= BESCR_PMEO; - } else if (ebb_excp == POWERPC_EXCP_EXTERNAL_EBB) { - env->spr[SPR_BESCR] |= BESCR_EEO; - } - - if (FIELD_EX64(env->msr, MSR, PR)) { - powerpc_excp(cpu, ebb_excp); - } else { - ppc_set_irq(cpu, PPC_INTERRUPT_EBB, 1); - } -} - -void raise_ebb_perfm_exception(CPUPPCState *env) -{ - bool perfm_ebb_enabled = env->spr[SPR_POWER_MMCR0] & MMCR0_EBE && - env->spr[SPR_BESCR] & BESCR_PME && - env->spr[SPR_BESCR] & BESCR_GE; - - if (!perfm_ebb_enabled) { - return; - } - - do_ebb(env, POWERPC_EXCP_PERFM_EBB); -} -#endif - -/*****************************************************************************/ -/* Embedded PowerPC specific helpers */ -void helper_40x_rfci(CPUPPCState *env) -{ - do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3]); -} - -void helper_rfci(CPUPPCState *env) -{ - do_rfi(env, env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1]); -} - -void helper_rfdi(CPUPPCState *env) -{ - /* FIXME: choose CSRR1 or DSRR1 based on cpu type */ - do_rfi(env, env->spr[SPR_BOOKE_DSRR0], env->spr[SPR_BOOKE_DSRR1]); -} - -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) */ - -#ifdef CONFIG_TCG -void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2, - uint32_t flags) -{ - if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) || - ((int32_t)arg1 > (int32_t)arg2 && (flags & 0x08)) || - ((int32_t)arg1 == (int32_t)arg2 && (flags & 0x04)) || - ((uint32_t)arg1 < (uint32_t)arg2 && (flags & 0x02)) || - ((uint32_t)arg1 > (uint32_t)arg2 && (flags & 0x01))))) { - raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_TRAP, GETPC()); - } -} - -#if defined(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)) || - ((int64_t)arg1 > (int64_t)arg2 && (flags & 0x08)) || - ((int64_t)arg1 == (int64_t)arg2 && (flags & 0x04)) || - ((uint64_t)arg1 < (uint64_t)arg2 && (flags & 0x02)) || - ((uint64_t)arg1 > (uint64_t)arg2 && (flags & 0x01))))) { - raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_TRAP, GETPC()); - } -} -#endif -#endif - -#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; - const uint64_t z0 = 0xfa2561cdf44ac398ULL; - uint16_t z = 0, temp; - uint16_t k[32], eff_k[32], xleft[33], xright[33], fxleft[32]; - - for (int i = 3; i >= 0; i--) { - k[i] = key & 0xffff; - key >>= 16; - } - xleft[0] = x & 0xffff; - xright[0] = (x >> 16) & 0xffff; - - for (int i = 0; i < 28; i++) { - z = (z0 >> (63 - i)) & 1; - temp = ror16(k[i + 3], 3) ^ k[i + 1]; - k[i + 4] = c ^ z ^ k[i] ^ temp ^ ror16(temp, 1); - } - - for (int i = 0; i < 8; i++) { - eff_k[4 * i + 0] = k[4 * i + ((0 + lane) % 4)]; - eff_k[4 * i + 1] = k[4 * i + ((1 + lane) % 4)]; - eff_k[4 * i + 2] = k[4 * i + ((2 + lane) % 4)]; - eff_k[4 * i + 3] = k[4 * i + ((3 + lane) % 4)]; - } - - for (int i = 0; i < 32; i++) { - fxleft[i] = (rol16(xleft[i], 1) & - rol16(xleft[i], 8)) ^ rol16(xleft[i], 2); - xleft[i + 1] = xright[i] ^ fxleft[i] ^ eff_k[i]; - xright[i + 1] = xleft[i]; - } - - return (((uint32_t)xright[32]) << 16) | xleft[32]; -} - -static uint64_t hash_digest(uint64_t ra, uint64_t rb, uint64_t key) -{ - uint64_t stage0_h = 0ULL, stage0_l = 0ULL; - uint64_t stage1_h, stage1_l; - - for (int i = 0; i < 4; i++) { - stage0_h |= ror64(rb & 0xff, 8 * (2 * i + 1)); - stage0_h |= ((ra >> 32) & 0xff) << (8 * 2 * i); - stage0_l |= ror64((rb >> 32) & 0xff, 8 * (2 * i + 1)); - stage0_l |= (ra & 0xff) << (8 * 2 * i); - rb >>= 8; - ra >>= 8; - } - - stage1_h = (uint64_t)helper_SIMON_LIKE_32_64(stage0_h >> 32, key, 0) << 32; - stage1_h |= helper_SIMON_LIKE_32_64(stage0_h, key, 1); - stage1_l = (uint64_t)helper_SIMON_LIKE_32_64(stage0_l >> 32, key, 2) << 32; - stage1_l |= helper_SIMON_LIKE_32_64(stage0_l, key, 3); - - return stage1_h ^ stage1_l; -} - -static void do_hash(CPUPPCState *env, target_ulong ea, target_ulong ra, - target_ulong rb, uint64_t key, bool store) -{ - uint64_t calculated_hash = hash_digest(ra, rb, key), loaded_hash; - - if (store) { - cpu_stq_data_ra(env, ea, calculated_hash, GETPC()); - } else { - loaded_hash = cpu_ldq_data_ra(env, ea, GETPC()); - if (loaded_hash != calculated_hash) { - raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_TRAP, GETPC()); - } - } -} - -#include "qemu/guest-random.h" - -#ifdef TARGET_PPC64 -#define HELPER_HASH(op, key, store, dexcr_aspect) \ -void helper_##op(CPUPPCState *env, target_ulong ea, target_ulong ra, \ - target_ulong rb) \ -{ \ - if (env->msr & R_MSR_PR_MASK) { \ - if (!(env->spr[SPR_DEXCR] & R_DEXCR_PRO_##dexcr_aspect##_MASK || \ - env->spr[SPR_HDEXCR] & R_HDEXCR_ENF_##dexcr_aspect##_MASK)) \ - return; \ - } else if (!(env->msr & R_MSR_HV_MASK)) { \ - if (!(env->spr[SPR_DEXCR] & R_DEXCR_PNH_##dexcr_aspect##_MASK || \ - env->spr[SPR_HDEXCR] & R_HDEXCR_ENF_##dexcr_aspect##_MASK)) \ - return; \ - } else if (!(env->msr & R_MSR_S_MASK)) { \ - if (!(env->spr[SPR_HDEXCR] & R_HDEXCR_HNU_##dexcr_aspect##_MASK)) \ - return; \ - } \ - \ - do_hash(env, ea, ra, rb, key, store); \ -} -#else -#define HELPER_HASH(op, key, store, dexcr_aspect) \ -void helper_##op(CPUPPCState *env, target_ulong ea, target_ulong ra, \ - target_ulong rb) \ -{ \ - do_hash(env, ea, ra, rb, key, store); \ -} -#endif /* TARGET_PPC64 */ - -HELPER_HASH(HASHST, env->spr[SPR_HASHKEYR], true, NPHIE) -HELPER_HASH(HASHCHK, env->spr[SPR_HASHKEYR], false, NPHIE) -HELPER_HASH(HASHSTP, env->spr[SPR_HASHPKEYR], true, PHIE) -HELPER_HASH(HASHCHKP, env->spr[SPR_HASHPKEYR], false, PHIE) -#endif /* CONFIG_TCG */ - -#if !defined(CONFIG_USER_ONLY) - -#ifdef CONFIG_TCG - -/* Embedded.Processor Control */ -static int dbell2irq(target_ulong rb) -{ - int msg = rb & DBELL_TYPE_MASK; - int irq = -1; - - switch (msg) { - case DBELL_TYPE_DBELL: - irq = PPC_INTERRUPT_DOORBELL; - break; - case DBELL_TYPE_DBELL_CRIT: - irq = PPC_INTERRUPT_CDOORBELL; - break; - case DBELL_TYPE_G_DBELL: - case DBELL_TYPE_G_DBELL_CRIT: - case DBELL_TYPE_G_DBELL_MC: - /* XXX implement */ - default: - break; - } - - return irq; -} - -void helper_msgclr(CPUPPCState *env, target_ulong rb) -{ - int irq = dbell2irq(rb); - - if (irq < 0) { - return; - } - - ppc_set_irq(env_archcpu(env), irq, 0); -} - -void helper_msgsnd(target_ulong rb) -{ - int irq = dbell2irq(rb); - int pir = rb & DBELL_PIRTAG_MASK; - CPUState *cs; - - if (irq < 0) { - return; - } - - qemu_mutex_lock_iothread(); - CPU_FOREACH(cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *cenv = &cpu->env; - - if ((rb & DBELL_BRDCAST) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { - ppc_set_irq(cpu, irq, 1); - } - } - qemu_mutex_unlock_iothread(); -} - -/* Server Processor Control */ - -static bool dbell_type_server(target_ulong rb) -{ - /* - * A Directed Hypervisor Doorbell message is sent only if the - * message type is 5. All other types are reserved and the - * instruction is a no-op - */ - return (rb & DBELL_TYPE_MASK) == DBELL_TYPE_DBELL_SERVER; -} - -void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) -{ - if (!dbell_type_server(rb)) { - return; - } - - 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) -{ - int pir = rb & DBELL_PROCIDTAG_MASK; - - if (!dbell_type_server(rb)) { - return; - } - - book3s_msgsnd_common(pir, PPC_INTERRUPT_HDOORBELL); -} - -#if defined(TARGET_PPC64) -void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb) -{ - helper_hfscr_facility_check(env, HFSCR_MSGP, "msgclrp", HFSCR_IC_MSGP); - - if (!dbell_type_server(rb)) { - return; - } - - ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_DOORBELL, 0); -} - -/* - * sends a message to another thread on the same - * multi-threaded processor - */ -void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb) -{ - CPUState *cs = env_cpu(env); - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - int ttir = rb & PPC_BITMASK(57, 63); - - helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP); - - if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) { - nr_threads = 1; /* msgsndp behaves as 1-thread in LPAR-per-thread mode*/ - } - - if (!dbell_type_server(rb) || ttir >= nr_threads) { - return; - } - - if (nr_threads == 1) { - ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, 1); - return; - } - - /* Does iothread need to be locked for walking CPU list? */ - qemu_mutex_lock_iothread(); - THREAD_SIBLING_FOREACH(cs, ccs) { - PowerPCCPU *ccpu = POWERPC_CPU(ccs); - uint32_t thread_id = ppc_cpu_tir(ccpu); - - if (ttir == thread_id) { - ppc_set_irq(ccpu, PPC_INTERRUPT_DOORBELL, 1); - qemu_mutex_unlock_iothread(); - return; - } - } - - g_assert_not_reached(); -} -#endif /* TARGET_PPC64 */ - -/* Single-step tracing */ -void helper_book3s_trace(CPUPPCState *env, target_ulong prev_ip) -{ - uint32_t error_code = 0; - if (env->insns_flags2 & PPC2_ISA207S) { - /* Load/store reporting, SRR1[35, 36] and SDAR, are not implemented. */ - env->spr[SPR_POWER_SIAR] = prev_ip; - error_code = PPC_BIT(33); - } - raise_exception_err(env, POWERPC_EXCP_TRACE, error_code); -} - -void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, - MMUAccessType access_type, - int mmu_idx, uintptr_t retaddr) -{ - CPUPPCState *env = cs->env_ptr; - uint32_t insn; - - /* Restore state and reload the insn we executed, for filling in DSISR. */ - cpu_restore_state(cs, retaddr); - insn = ppc_ldl_code(env, env->nip); - - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_4xx: - env->spr[SPR_40x_DEAR] = vaddr; - break; - case POWERPC_MMU_BOOKE: - case POWERPC_MMU_BOOKE206: - env->spr[SPR_BOOKE_DEAR] = vaddr; - break; - default: - env->spr[SPR_DAR] = vaddr; - break; - } - - cs->exception_index = POWERPC_EXCP_ALIGN; - env->error_code = insn & 0x03FF0000; - cpu_loop_exit(cs); -} - -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 = cs->env_ptr; - - switch (env->excp_model) { -#if defined(TARGET_PPC64) - case POWERPC_EXCP_POWER9: - case POWERPC_EXCP_POWER10: - /* - * Machine check codes can be found in processor User Manual or - * Linux or skiboot source. - */ - if (access_type == MMU_DATA_LOAD) { - env->spr[SPR_DAR] = vaddr; - env->spr[SPR_DSISR] = PPC_BIT(57); - env->error_code = PPC_BIT(42); - - } else if (access_type == MMU_DATA_STORE) { - /* - * MCE for stores in POWER is asynchronous so hardware does - * not set DAR, but QEMU can do better. - */ - env->spr[SPR_DAR] = vaddr; - env->error_code = PPC_BIT(36) | PPC_BIT(43) | PPC_BIT(45); - env->error_code |= PPC_BIT(42); - - } else { /* Fetch */ - 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 = cs->env_ptr; - - 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 = cs->env_ptr; - - 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 = cs->env_ptr; - - 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_TCG */ -#endif /* !CONFIG_USER_ONLY */ diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 03150a0f108..07b782f971b 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "internal.h" #include "fpu/softfloat.h" @@ -155,8 +154,7 @@ void helper_compute_fprf_##tp(CPUPPCState *env, tp arg) \ } 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)) { \ + if (tp##_is_signaling_nan(arg, &env->fp_status)) { \ fprf = 0x00 << FPSCR_FPRF; \ } else { \ fprf = 0x11 << FPSCR_FPRF; \ @@ -490,54 +488,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()); - } - - 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()); + float_invalid_op_addsub(env, flags, 1, ra); } - 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, @@ -550,29 +506,11 @@ static void float_invalid_op_mul(CPUPPCState *env, int flags, } } -/* fmul - fmul. */ -float64 helper_fmul(CPUPPCState *env, float64 arg1, float64 arg2) -{ - 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()); - } - - return ret; -} - -/* fmuls - fmuls. */ -float64 helper_fmuls(CPUPPCState *env, float64 arg1, float64 arg2) +static inline void mul_flags_handler(CPUPPCState *env, int flags, uintptr_t ra) { - 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()); + float_invalid_op_mul(env, flags, 1, ra); } - return ret; } static void float_invalid_op_div(CPUPPCState *env, int flags, @@ -587,36 +525,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, @@ -755,7 +671,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()); } @@ -764,10 +680,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) @@ -812,81 +728,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) @@ -903,7 +804,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; @@ -939,7 +840,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; @@ -1696,14 +1597,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) @@ -1773,10 +1674,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) @@ -1847,10 +1748,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) @@ -2480,12 +2381,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, \ @@ -2624,14 +2525,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 @@ -2880,20 +2781,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()); \ } @@ -2945,15 +2848,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()); \ @@ -2962,6 +2866,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 2ad11510bf1..3b28d4e21c7 100644 --- a/target/ppc/gdbstub.c +++ b/target/ppc/gdbstub.c @@ -54,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: @@ -74,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; } @@ -120,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); @@ -132,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: @@ -158,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; @@ -170,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); @@ -224,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) { @@ -271,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) { @@ -318,12 +296,12 @@ 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; @@ -346,13 +324,13 @@ void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu) num_regs++; } - if (pcc->gdb_spr_xml) { + if (pcc->gdb_spr.xml) { return; } - xml = g_string_new(""); - g_string_append(xml, ""); - g_string_append(xml, ""); + 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]; @@ -361,28 +339,12 @@ void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu) continue; } - spr_name = g_ascii_strdown(spr->name, -1); - g_string_append_printf(xml, ""); + gdb_feature_builder_append_reg(&builder, g_ascii_strdown(spr->name, -1), + TARGET_LONG_BITS, spr->gdb_id, + "int", "spr"); } - 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; - } - return NULL; + gdb_feature_builder_end(&builder); } #endif @@ -401,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; @@ -412,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; @@ -429,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)); @@ -453,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); @@ -468,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) { @@ -494,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); @@ -516,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); @@ -540,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]; @@ -569,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); @@ -579,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); @@ -589,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 } @@ -602,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 86f97ee1e78..ca414f2f43d 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) @@ -28,8 +28,11 @@ 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_dawr1, void, env, tl) +DEF_HELPER_2(store_dawrx1, 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) @@ -45,21 +48,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) @@ -67,12 +72,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) @@ -110,32 +115,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 * @@ -200,18 +205,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) @@ -267,17 +272,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) @@ -361,12 +366,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) @@ -389,8 +394,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) @@ -436,10 +441,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) @@ -458,10 +463,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) @@ -471,12 +476,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) @@ -492,10 +497,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) @@ -505,12 +510,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) @@ -694,14 +699,12 @@ 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 @@ -729,6 +732,11 @@ 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) +DEF_HELPER_FLAGS_1(load_pmsr, TCG_CALL_NO_RWG_SE, tl, env) +DEF_HELPER_FLAGS_2(store_pmcr, TCG_CALL_NO_RWG, void, env, tl) #endif DEF_HELPER_2(store_sdr1, void, env, tl) DEF_HELPER_2(store_pidr, void, env, tl) @@ -819,3 +827,11 @@ 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) + +#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 f380342d4dd..7e5726871e5 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -20,13 +20,15 @@ #include "qemu/osdep.h" #include "cpu.h" #include "qemu/main-loop.h" -#include "exec/exec-all.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "exec/cputlb.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "helper_regs.h" #include "power8-pmu.h" #include "cpu-models.h" #include "spr_common.h" +#include "accel/tcg/cpu-ops.h" +#include "internal.h" /* Swap temporary saved registers with GPRs */ void hreg_swap_gpr_tgpr(CPUPPCState *env) @@ -47,27 +49,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 @@ -85,6 +124,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; } @@ -106,10 +146,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,26 +257,23 @@ void hreg_update_pmu_hflags(CPUPPCState *env) env->hflags |= hreg_compute_pmu_hflags_value(env); } -#ifdef CONFIG_DEBUG_TCG -void cpu_get_tb_cpu_state(CPUPPCState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) +TCGTBCPUState ppc_get_tb_cpu_state(CPUState *cs) { + CPUPPCState *env = cpu_env(cs); uint32_t hflags_current = env->hflags; - uint32_t hflags_rebuilt; - - *pc = env->nip; - *cs_base = 0; - *flags = hflags_current; - hflags_rebuilt = hreg_compute_hflags_value(env); +#ifdef CONFIG_DEBUG_TCG + uint32_t hflags_rebuilt = hreg_compute_hflags_value(env); if (unlikely(hflags_current != hflags_rebuilt)) { cpu_abort(env_cpu(env), "TCG hflags mismatch (current:0x%08x rebuilt:0x%08x)\n", hflags_current, hflags_rebuilt); } -} #endif + return (TCGTBCPUState){ .pc = env->nip, .flags = hflags_current }; +} + void cpu_interrupt_exittb(CPUState *cs) { /* @@ -244,7 +281,7 @@ void cpu_interrupt_exittb(CPUState *cs) * unless running with TCG. */ if (tcg_enabled()) { - QEMU_IOTHREAD_LOCK_GUARD(); + BQL_LOCK_GUARD(); cpu_interrupt(cs, CPU_INTERRUPT_EXITTB); } } @@ -264,6 +301,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); } @@ -329,7 +371,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; } @@ -460,22 +502,41 @@ void register_generic_sprs(PowerPCCPU *cpu) } /* Time base */ - spr_register(env, SPR_VTBL, "TBL", - &spr_read_tbl, SPR_NOACCESS, +#if defined(TARGET_PPC64) + spr_register(env, SPR_TBL, "TB", +#else + spr_register(env, SPR_TBL, "TBL", +#endif &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_read_tbu, SPR_NOACCESS, + spr_register(env, SPR_TBU, "TBU", &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) @@ -490,7 +551,7 @@ void register_non_embedded_sprs(CPUPPCState *env) &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); @@ -669,7 +730,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/helper_regs.h b/target/ppc/helper_regs.h index 8196c1346dc..b928c2c452d 100644 --- a/target/ppc/helper_regs.h +++ b/target/ppc/helper_regs.h @@ -20,6 +20,8 @@ #ifndef HELPER_REGS_H #define HELPER_REGS_H +#include "target/ppc/cpu.h" + void hreg_swap_gpr_tgpr(CPUPPCState *env); void hreg_compute_hflags(CPUPPCState *env); void hreg_update_pmu_hflags(CPUPPCState *env); diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 4fcf3af8d0d..e53fd2840d4 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 @@ -500,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 @@ -531,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 @@ -643,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 @@ -650,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 @@ -663,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 @@ -766,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 @@ -777,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 @@ -846,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 @@ -881,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 @@ -972,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 96cdb3c7e37..ef4b2e75d60 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -25,6 +25,7 @@ #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]; \ - } \ -} - -PMSUM(vpmsumb, u8, u16, uint16_t) -PMSUM(vpmsumh, u16, u32, uint32_t) -PMSUM(vpmsumw, u32, u64, uint64_t) +/* + * 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); + } +} -void helper_VPMSUMD(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +void helper_vpmsumh(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); - } - } + 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); } +} - r->s128 = int128_xor(prod[0], prod[1]); +void helper_VPMSUMD(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + Int128 e = clmul_64(a->u64[0], b->u64[0]); + Int128 o = clmul_64(a->u64[1], b->u64[1]); + r->s128 = int128_xor(e, o); } #if HOST_BIG_ENDIAN @@ -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; \ } diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 15803bc3136..7723350227f 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -18,7 +18,19 @@ #ifndef PPC_INTERNAL_H #define PPC_INTERNAL_H +#include "exec/breakpoint.h" #include "hw/registerfields.h" +#include "exec/page-protection.h" +#include "accel/tcg/tb-cpu-state.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,53 +233,25 @@ 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); } -#ifndef CONFIG_USER_ONLY - /* PowerPC MMU emulation */ -typedef struct mmu_ctx_t mmu_ctx_t; - bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible); -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 */ @@ -285,6 +269,8 @@ static inline void pte_invalidate(target_ulong *pte0) #define PTE_PTEM_MASK 0x7FFFFFBF #define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) +uint32_t ppc_ldl_code(CPUArchState *env, target_ulong addr); + #ifdef CONFIG_USER_ONLY void ppc_cpu_record_sigsegv(CPUState *cs, vaddr addr, MMUAccessType access_type, @@ -304,7 +290,11 @@ void ppc_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, 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 + +G_NORETURN void powerpc_checkstop(CPUPPCState *env, const char *reason); +void powerpc_excp(PowerPCCPU *cpu, int excp); + +#endif /* !CONFIG_USER_ONLY */ FIELD(GER_MSK, XMSK, 0, 4) FIELD(GER_MSK, YMSK, 4, 4) @@ -319,4 +309,6 @@ static inline int ger_pack_masks(int pmsk, int ymsk, int xmsk) return msk; } +TCGTBCPUState ppc_get_tb_cpu_state(CPUState *cs); + #endif /* PPC_INTERNAL_H */ diff --git a/target/ppc/kvm-stub.c b/target/ppc/kvm-stub.c deleted file mode 100644 index b98e1d404f8..00000000000 --- 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 51112bd3670..015658049e6 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -26,10 +26,10 @@ #include "cpu.h" #include "cpu-models.h" #include "qemu/timer.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "kvm_ppc.h" -#include "sysemu/cpus.h" -#include "sysemu/device_tree.h" +#include "system/cpus.h" +#include "system/device_tree.h" #include "mmu-hash64.h" #include "hw/ppc/spapr.h" @@ -37,17 +37,21 @@ #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "migration/qemu-file-types.h" -#include "sysemu/watchdog.h" +#include "system/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" +#include "system/ram_addr.h" +#include "system/hostmem.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" #include "qemu/mmap-alloc.h" #include "elf.h" -#include "sysemu/kvm_int.h" +#include "system/kvm_int.h" +#include "system/kvm.h" +#include "accel/accel-cpu-target.h" + +#include CONFIG_DEVICES #define PROC_DEVTREE_CPU "/proc/device-tree/cpus/" @@ -71,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 +92,13 @@ static int cap_large_decr; static int cap_fwnmi; static int cap_rpt_invalidate; static int cap_ail_mode_3; +static int cap_dawr1; + +#ifdef CONFIG_PSERIES +static int cap_papr; +#else +#define cap_papr (0) +#endif static uint32_t debug_inst_opcode; @@ -143,6 +153,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_ppc_nested_kvm_hv = kvm_vm_check_extension(s, KVM_CAP_PPC_NESTED_HV); cap_large_decr = kvmppc_get_dec_bits(); cap_fwnmi = kvm_vm_check_extension(s, KVM_CAP_PPC_FWNMI); + cap_dawr1 = kvm_vm_check_extension(s, KVM_CAP_PPC_DAWR1); /* * Note: setting it to false because there is not such capability * in KVM at this moment. @@ -268,7 +279,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; @@ -468,6 +479,11 @@ static void kvmppc_hw_debug_points_init(CPUPPCState *cenv) } } +int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + return 0; +} + int kvm_arch_init_vcpu(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -546,8 +562,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; @@ -581,8 +596,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; @@ -615,8 +629,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; @@ -635,8 +648,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); @@ -682,8 +695,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; @@ -704,8 +716,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); @@ -862,9 +874,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]; } @@ -897,7 +907,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; @@ -960,8 +970,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 @@ -1204,7 +1212,7 @@ 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; @@ -1262,8 +1270,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 @@ -1331,7 +1337,6 @@ int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level) void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) { - return; } MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) @@ -1660,7 +1665,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: @@ -1676,7 +1681,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, @@ -1706,7 +1711,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); @@ -1719,7 +1724,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) break; } - qemu_mutex_unlock_iothread(); + bql_unlock(); return ret; } @@ -2062,6 +2067,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); @@ -2083,6 +2089,7 @@ void kvmppc_set_papr(PowerPCCPU *cpu) */ cap_papr = 1; } +#endif int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr) { @@ -2113,6 +2120,16 @@ int kvmppc_set_fwnmi(PowerPCCPU *cpu) return kvm_vcpu_enable_cap(cs, KVM_CAP_PPC_FWNMI, 0); } +bool kvmppc_has_cap_dawr1(void) +{ + return !!cap_dawr1; +} + +int kvmppc_set_cap_dawr1(int enable) +{ + return kvm_vm_enable_cap(kvm_state, KVM_CAP_PPC_DAWR1, 0, enable); +} + int kvmppc_smt_threads(void) { return cap_ppc_smt ? cap_ppc_smt : 1; @@ -2347,7 +2364,31 @@ static void alter_insns(uint64_t *word, uint64_t flags, bool on) } } -static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) +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, const void *data) { PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); uint32_t dcache_size = kvmppc_read_int_cpu_dt("d-cache-size"); @@ -2372,18 +2413,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) */ } @@ -2619,7 +2649,7 @@ static int kvm_ppc_register_host_cpu_type(void) return -1; } type_info.parent = object_class_get_name(OBJECT_CLASS(pvr_pcc)); - type_register(&type_info); + type_register_static(&type_info); /* override TCG default cpu type with 'host' cpu model */ object_class_foreach(pseries_machine_class_fixup, TYPE_SPAPR_MACHINE, false, NULL); @@ -2692,7 +2722,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 { @@ -2774,9 +2804,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"); } @@ -2856,7 +2886,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; @@ -2975,11 +3005,26 @@ void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset) } } -bool kvm_arch_cpu_check_are_resettable(void) +void kvm_arch_accel_class_init(ObjectClass *oc) { - return true; } -void kvm_arch_accel_class_init(ObjectClass *oc) +static void kvm_cpu_accel_class_init(ObjectClass *oc, const 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 6a4dd9c5608..a1d9ce9f9aa 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -9,10 +9,14 @@ #ifndef KVM_PPC_H #define KVM_PPC_H -#include "sysemu/kvm.h" +#include "system/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 uint32_t kvmppc_get_tbfreq(void); @@ -64,6 +68,8 @@ bool kvmppc_has_cap_htm(void); bool kvmppc_has_cap_mmu_radix(void); bool kvmppc_has_cap_mmu_hash_v3(void); bool kvmppc_has_cap_xive(void); +bool kvmppc_has_cap_dawr1(void); +int kvmppc_set_cap_dawr1(int enable); int kvmppc_get_cap_safe_cache(void); int kvmppc_get_cap_safe_bounds_check(void); int kvmppc_get_cap_safe_indirect_branch(void); @@ -215,7 +221,6 @@ static inline int kvmppc_smt_threads(void) static inline void kvmppc_error_append_smt_possible_hint(Error *const *errp) { - return; } static inline int kvmppc_set_smt_threads(int smt) @@ -253,7 +258,6 @@ static inline target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, static inline void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online) { - return; } static inline void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset) @@ -373,6 +377,16 @@ static inline bool kvmppc_has_cap_xive(void) return false; } +static inline bool kvmppc_has_cap_dawr1(void) +{ + return false; +} + +static inline int kvmppc_set_cap_dawr1(int enable) +{ + abort(); +} + static inline int kvmppc_get_cap_safe_cache(void) { return 0; @@ -440,7 +454,6 @@ static inline PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void) static inline void kvmppc_check_papr_resize_hpt(Error **errp) { - return; } static inline int kvmppc_resize_hpt_prepare(PowerPCCPU *cpu, diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 68cbdffecd4..d72e5ecb94b 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -1,15 +1,14 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "helper_regs.h" #include "mmu-hash64.h" #include "migration/cpu.h" #include "qapi/error.h" #include "kvm_ppc.h" #include "power8-pmu.h" -#include "sysemu/replay.h" +#include "system/replay.h" static void post_load_update_msr(CPUPPCState *env) { @@ -118,43 +117,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,35 +144,6 @@ 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; @@ -325,7 +263,8 @@ static int cpu_post_load(void *opaque, int version_id) /* Re-set breaks based on regs */ #if defined(TARGET_PPC64) ppc_update_ciabr(env); - ppc_update_daw0(env); + ppc_update_daw(env, 0); + ppc_update_daw(env, 1); #endif /* * TCG needs to re-start the decrementer timer and/or raise the @@ -333,7 +272,7 @@ static int cpu_post_load(void *opaque, int version_id) * triggered types (including HDEC) would need to carry more state. */ cpu_ppc_store_decr(env, env->spr[SPR_DECR]); - pmu_mmcr01_updated(env); + pmu_mmcr01a_updated(env); } return 0; @@ -351,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() @@ -392,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 @@ -425,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() }, @@ -445,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), @@ -479,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() }, @@ -549,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() } @@ -565,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), @@ -586,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, @@ -601,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), @@ -621,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, @@ -639,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), @@ -661,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, @@ -676,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 = { @@ -684,7 +622,7 @@ 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() } @@ -700,7 +638,7 @@ static const VMStateDescription vmstate_reservation = { .version_id = 1, .minimum_version_id = 1, .needed = reservation_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINTTL(env.reserve_addr, PowerPCCPU), VMSTATE_UINTTL(env.reserve_length, PowerPCCPU), VMSTATE_UINTTL(env.reserve_val, PowerPCCPU), @@ -711,13 +649,33 @@ static const VMStateDescription vmstate_reservation = { } }; +#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 */ @@ -740,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, @@ -756,6 +708,7 @@ const VMStateDescription vmstate_ppc_cpu = { #ifdef TARGET_PPC64 &vmstate_tm, &vmstate_slb, + &vmstate_bhrb, #endif /* TARGET_PPC64 */ &vmstate_tlb6xx, &vmstate_tlbemb, diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index c7535481d67..6ab71a6fcb4 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -19,11 +19,13 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/target_page.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "helper_regs.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/helper-retaddr.h" +#include "accel/tcg/probe.h" #include "internal.h" #include "qemu/atomic128.h" @@ -83,7 +85,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); @@ -105,7 +107,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); @@ -135,7 +137,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)) { @@ -224,7 +226,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)) { @@ -271,51 +273,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) { @@ -404,9 +414,9 @@ target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, } \ } #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 @@ -432,9 +442,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 @@ -467,8 +477,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) \ @@ -496,8 +506,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 4c2635039e3..8eed1fa40ca 100644 --- a/target/ppc/meson.build +++ b/target/ppc/meson.build @@ -14,6 +14,7 @@ ppc_ss.add(when: 'CONFIG_TCG', if_true: files( 'int_helper.c', 'mem_helper.c', 'misc_helper.c', + 'tcg-excp_helper.c', 'timebase_helper.c', 'translate.c', 'power8-pmu.c', @@ -30,7 +31,6 @@ 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_system_ss = ss.source_set() @@ -38,6 +38,7 @@ ppc_system_ss.add(files( 'arch_dump.c', 'machine.c', 'mmu-hash32.c', + 'mmu-booke.c', 'mmu_common.c', 'ppc-qmp-cmds.c', )) @@ -46,6 +47,7 @@ ppc_system_ss.add(when: 'CONFIG_TCG', if_true: files( ), if_false: files( 'tcg-stub.c', )) +ppc_system_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c')) ppc_system_ss.add(when: 'TARGET_PPC64', if_true: files( 'compat.c', @@ -55,4 +57,4 @@ ppc_system_ss.add(when: 'TARGET_PPC64', if_true: files( )) target_arch += {'ppc': ppc_ss} -target_softmmu_arch += {'ppc': ppc_system_ss} +target_system_arch += {'ppc': ppc_system_ss} diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index a05bdf78c98..e7d94625185 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/helper-proto.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" @@ -48,12 +48,8 @@ void helper_spr_core_write_generic(CPUPPCState *env, uint32_t sprn, { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; - uint32_t core_id = env->spr[SPR_PIR] & ~(nr_threads - 1); - assert(core_id == env->spr[SPR_PIR] - env->spr[SPR_TIR]); - - if (nr_threads == 1) { + if (ppc_cpu_core_single_threaded(cs)) { env->spr[sprn] = val; return; } @@ -153,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) { @@ -165,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; @@ -186,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); + } + } } } @@ -214,6 +233,16 @@ void helper_store_dawrx0(CPUPPCState *env, target_ulong value) ppc_store_dawrx0(env, value); } +void helper_store_dawr1(CPUPPCState *env, target_ulong value) +{ + ppc_store_dawr1(env, value); +} + +void helper_store_dawrx1(CPUPPCState *env, target_ulong value) +{ + ppc_store_dawrx1(env, value); +} + /* * DPDES register is shared. Each bit reflects the state of the * doorbell interrupt of a thread of the same core. @@ -222,23 +251,19 @@ target_ulong helper_load_dpdes(CPUPPCState *env) { CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; target_ulong dpdes = 0; helper_hfscr_facility_check(env, HFSCR_MSGP, "load DPDES", HFSCR_IC_MSGP); - if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) { - nr_threads = 1; /* DPDES behaves as 1-thread in LPAR-per-thread mode */ - } - - if (nr_threads == 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; } - qemu_mutex_lock_iothread(); + bql_lock(); THREAD_SIBLING_FOREACH(cs, ccs) { PowerPCCPU *ccpu = POWERPC_CPU(ccs); CPUPPCState *cenv = &ccpu->env; @@ -248,7 +273,7 @@ target_ulong helper_load_dpdes(CPUPPCState *env) dpdes |= (0x1 << thread_id); } } - qemu_mutex_unlock_iothread(); + bql_unlock(); return dpdes; } @@ -258,35 +283,169 @@ void helper_store_dpdes(CPUPPCState *env, target_ulong val) PowerPCCPU *cpu = env_archcpu(env); CPUState *cs = env_cpu(env); CPUState *ccs; - uint32_t nr_threads = cs->nr_threads; helper_hfscr_facility_check(env, HFSCR_MSGP, "store DPDES", HFSCR_IC_MSGP); - if (!(env->flags & POWERPC_FLAG_SMT_1LPAR)) { - nr_threads = 1; /* DPDES behaves as 1-thread in LPAR-per-thread mode */ + /* 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; } - if (val & ~(nr_threads - 1)) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid DPDES register value " + /* 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); - val &= (nr_threads - 1); /* Ignore the invalid bits */ + return; } + env->spr[SPR_POWER_SPRC] = val; +} - if (nr_threads == 1) { - ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & 0x1); +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]; + + if (pc->big_core) { + pc = pnv_chip_find_core(pc->chip, CPU_CORE(pc)->core_id & ~0x1); + } + + 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; + + if (pc->big_core) { + pc = pnv_chip_find_core(pc->chip, CPU_CORE(pc)->core_id & ~0x1); + } + + 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; + pc->scratch[nr] = val; + break; + default: + qemu_log_mask(LOG_UNIMP, "mtSPRD: Unimplemented SPRC:0x" + TARGET_FMT_lx"\n", sprc); + break; + } +} + +target_ulong helper_load_pmsr(CPUPPCState *env) +{ + target_ulong lowerps = extract64(env->spr[SPR_PMCR], PPC_BIT_NR(15), 8); + target_ulong val = 0; + + val |= PPC_BIT(63); /* verion 0x1 (POWER9/10) */ + /* Pmin = 0 */ + /* XXX: POWER9 should be 3 */ + val |= 4ULL << PPC_BIT_NR(31); /* Pmax */ + val |= lowerps << PPC_BIT_NR(15); /* Local actual Pstate */ + val |= lowerps << PPC_BIT_NR(7); /* Global actual Pstate */ + + return val; +} + +static void ppc_set_pmcr(PowerPCCPU *cpu, target_ulong val) +{ + cpu->env.spr[SPR_PMCR] = val; +} + +void helper_store_pmcr(CPUPPCState *env, target_ulong val) +{ + PowerPCCPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + CPUState *ccs; + + /* Leave version field unchanged (0x1) */ + val &= ~PPC_BITMASK(60, 63); + val |= PPC_BIT(63); + + val &= ~PPC_BITMASK(0, 7); /* UpperPS ignored */ + if (val & PPC_BITMASK(16, 59)) { + qemu_log_mask(LOG_GUEST_ERROR, "Non-zero PMCR reserved bits " + TARGET_FMT_lx"\n", val); + val &= ~PPC_BITMASK(16, 59); + } + + /* DPDES behaves as 1-thread in LPAR-per-thread mode */ + if (ppc_cpu_lpar_single_threaded(cs)) { + ppc_set_pmcr(cpu, val); return; } /* Does iothread need to be locked for walking CPU list? */ - qemu_mutex_lock_iothread(); + bql_lock(); THREAD_SIBLING_FOREACH(cs, ccs) { PowerPCCPU *ccpu = POWERPC_CPU(ccs); - uint32_t thread_id = ppc_cpu_tir(ccpu); - - ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & (0x1 << thread_id)); + ppc_set_pmcr(ccpu, val); } - qemu_mutex_unlock_iothread(); + bql_unlock(); } + #endif /* defined(TARGET_PPC64) */ void helper_store_pidr(CPUPPCState *env, target_ulong val) @@ -366,3 +525,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 c8f69b3df9b..38655563105 100644 --- a/target/ppc/mmu-book3s-v3.c +++ b/target/ppc/mmu-book3s-v3.c @@ -18,10 +18,10 @@ */ #include "qemu/osdep.h" +#include "system/memory.h" #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 674377a19e0..be66e26604e 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 00000000000..55e5dd7c6b0 --- /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 00000000000..f972843bbb7 --- /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 3976416840f..8b980a5aa90 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -20,8 +20,9 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" -#include "sysemu/kvm.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" +#include "system/kvm.h" #include "kvm_ppc.h" #include "internal.h" #include "mmu-hash32.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) @@ -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 7119a63d97a..04c23ea75ed 100644 --- a/target/ppc/mmu-hash32.h +++ b/target/ppc/mmu-hash32.h @@ -3,7 +3,8 @@ #ifndef CONFIG_USER_ONLY -hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash); +#include "system/memory.h" + 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 +103,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 d645c0bb94a..dd337558aa6 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -20,16 +20,18 @@ #include "qemu/osdep.h" #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" +#include "system/hw_accel.h" +#include "system/memory.h" #include "kvm_ppc.h" #include "mmu-hash64.h" #include "exec/log.h" #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) { @@ -820,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); @@ -837,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); @@ -950,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 @@ -979,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 */ @@ -1093,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"); @@ -1187,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 de653fcae52..b8fb12a9705 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -1,6 +1,8 @@ #ifndef MMU_HASH64_H #define MMU_HASH64_H +#include "exec/tswap.h" + #ifndef CONFIG_USER_ONLY #ifdef TARGET_PPC64 @@ -120,6 +122,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 5823e039e64..33ac3412901 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -19,14 +19,47 @@ #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 "system/kvm.h" +#include "system/memory.h" #include "kvm_ppc.h" #include "exec/log.h" #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, @@ -179,12 +212,29 @@ static void ppc_radix64_raise_hsi(PowerPCCPU *cpu, MMUAccessType access_type, } } +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) { @@ -209,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; @@ -300,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; } @@ -324,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; } @@ -521,6 +571,20 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, prtbe0 = ldq_phys(cs->as, h_raddr); } + /* + * Some Linux uses a zero process table entry in PID!=0 for kernel context + * without userspace in order to fault on NULL dereference, because using + * PIDR=0 for the kernel causes the Q0 page table to be used to translate + * Q3 as well. Check for that case here to avoid the invalid configuration + * message. + */ + if (unlikely(!prtbe0)) { + if (guest_visible) { + ppc_radix64_raise_si(cpu, access_type, eaddr, DSISR_R_BADCONFIG); + } + return 1; + } + /* Walk Radix Tree from Process Table Entry to Convert EA to RA */ *g_page_size = PRTBE_R_GET_RTS(prtbe0); base_addr = prtbe0 & PRTBE_R_RPDB; @@ -677,9 +741,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); diff --git a/target/ppc/mmu-radix64.h b/target/ppc/mmu-radix64.h index 4c768aa5cc7..6620b3d6481 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 6ca5d122070..52d48615ac2 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -20,11 +20,12 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "cpu.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_ppc.h" #include "mmu-hash64.h" #include "mmu-hash32.h" -#include "exec/exec-all.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/log.h" #include "helper_regs.h" #include "qemu/error-report.h" @@ -32,6 +33,7 @@ #include "internal.h" #include "mmu-book3s-v3.h" #include "mmu-radix64.h" +#include "mmu-booke.h" /* #define DUMP_PAGE_TABLES */ @@ -64,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) { @@ -116,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) { @@ -222,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 " 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); + 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; + } + } } - - 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; + 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); } } } - *blp = bl; - *validp = valid; - *protp = prot; +#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]; @@ -309,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) { + *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, ctx->raddr, - ctx->prot & PAGE_READ ? 'R' : '-', - ctx->prot & PAGE_WRITE ? 'W' : '-'); + " prot=%c%c\n", i, *raddr, + *prot & PAGE_READ ? 'R' : '-', + *prot & PAGE_WRITE ? 'W' : '-'); + ret = 0; + } else { + ret = -2; } break; } @@ -339,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); } } } @@ -359,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 @@ -390,531 +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 " HWADDR_FMT_plx - " htab_mask " HWADDR_FMT_plx - " hash " HWADDR_FMT_plx "\n", - ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash); - ctx->hash[0] = hash; - ctx->hash[1] = ~hash; - - /* 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: " HWADDR_FMT_plx " len " HWADDR_FMT_plx - "\n", ppc_hash32_hpt_base(cpu), - ppc_hash32_hpt_mask(cpu) + 0x80); - for (curaddr = ppc_hash32_hpt_base(cpu); - 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(HWADDR_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; - } - } else { - qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); - /* Direct-store segment : absolutely *BUGGY* for now */ - - 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; - } - } - - return ret; -} - -/* 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; -} - -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], 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 */ - 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 - " => " HWADDR_FMT_plx - " %d %d\n", __func__, address, ctx->raddr, ctx->prot, - ret); - return 0; - } - } - qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx - " => " HWADDR_FMT_plx - " %d %d\n", __func__, address, raddr, ctx->prot, ret); - - return ret; -} - -static bool mmubooke_check_pid(CPUPPCState *env, ppcemb_tlb_t *tlb, - hwaddr *raddr, target_ulong addr, int i) -{ - if (ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID], i)) { - if (!env->nb_pids) { - /* Extend the physical address to 36 bits */ - *raddr |= (uint64_t)(tlb->RPN & 0xF) << 32; - } - return true; - } else if (!env->nb_pids) { - return false; - } - if (env->spr[SPR_BOOKE_PID1] && - ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID1], i)) { - return true; - } - if (env->spr[SPR_BOOKE_PID2] && - ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID2], i)) { - return true; - } - return false; -} - -static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, - hwaddr *raddr, int *prot, target_ulong address, - MMUAccessType access_type, int i) -{ - int prot2; - - if (!mmubooke_check_pid(env, tlb, raddr, address, i)) { - qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__); - return -1; - } - - 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__); - 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 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 (ret >= 0) { - ctx->raddr = raddr; - qemu_log_mask(CPU_LOG_MMU, "%s: access granted " TARGET_FMT_lx - " => " HWADDR_FMT_plx " %d %d\n", __func__, - address, ctx->raddr, ctx->prot, ret); - } else { - qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx - " => " HWADDR_FMT_plx " %d %d\n", __func__, - address, raddr, ctx->prot, ret); - } - - return ret; -} - -hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb) -{ - 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; + return -3; } + /* 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; - 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; - } + /* Software TLB search */ + return ppc6xx_tlb_check(env, raddr, prot, eaddr, + access_type, ptem, key, nx); } - 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: - - 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__); + /* 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 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; - } - } + if ((access_type == MMU_DATA_STORE || !key) && + (access_type == MMU_DATA_LOAD || key)) { + *raddr = eaddr; + return 2; } - -found_tlb: - - if (ret >= 0) { - ctx->raddr = raddr; - qemu_log_mask(CPU_LOG_MMU, "%s: access granted " TARGET_FMT_lx - " => " HWADDR_FMT_plx " %d %d\n", __func__, address, - ctx->raddr, ctx->prot, ret); - } else { - qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx - " => " HWADDR_FMT_plx " %d %d\n", __func__, address, - raddr, ctx->prot, ret); - } - - return ret; + return -2; } static const char *book3e_tsize_to_str[32] = { @@ -1061,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", @@ -1103,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; @@ -1161,153 +580,107 @@ void dump_mmu(CPUPPCState *env) } } -static int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, - MMUAccessType access_type) -{ - ctx->raddr = eaddr; - ctx->prot = PAGE_READ | PAGE_EXEC; - 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(); - } - - return 0; -} - -int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, - MMUAccessType access_type, int type, - int mmu_idx) +static bool ppc_real_mode_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp) { - 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; + CPUPPCState *env = &cpu->env; - 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; + 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 ret; + return false; } -static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, - MMUAccessType access_type, int mmu_idx) +static bool ppc_40x_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + int mmu_idx, bool guest_visible) { - uint32_t epid; - bool as, pr; - uint32_t missed_tid = 0; - bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + int ret; - 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; + if (ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, protp)) { + return true; } - env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID; - env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK; + 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; + } - 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]; + 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 MAS4_TIDSELD_PID2: - missed_tid = env->spr[SPR_BOOKE_PID2]; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; break; + default: + g_assert_not_reached(); } - env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16; } else { - missed_tid = epid; - env->spr[SPR_BOOKE_MAS6] |= missed_tid << 16; + 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(); + } } - 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; + return false; } -/* 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_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; - mmu_ctx_t ctx; - int type; - int ret; + 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 */ @@ -1319,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; + 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; + } + 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 -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; + 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->error_code = 0x10000000; - } - break; - } - } 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_DSISR] = 0x04000000; } break; - case -2: - /* Access rights violation */ + case ACCESS_EXT: + /* eciwx or ecowx */ 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] = 0x06100000; } 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] = 0x04100000; } 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; - } + 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; @@ -1542,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"); } } @@ -1561,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 f87d35379a2..ac607054027 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -20,11 +20,13 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "cpu.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm_ppc.h" #include "mmu-hash64.h" #include "mmu-hash32.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/log.h" #include "helper_regs.h" #include "qemu/error-report.h" @@ -32,8 +34,9 @@ #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" +#include "accel/tcg/cpu-ldst.h" /* #define FLUSH_ALL_TLBS */ @@ -44,14 +47,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); @@ -307,9 +304,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: /* @@ -322,7 +317,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); @@ -533,7 +528,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; @@ -595,30 +590,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_wtlb(env, &ctx, addr, 0, ACCESS_INT, 0) == 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); @@ -749,12 +720,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, @@ -762,14 +750,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); @@ -803,27 +788,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; @@ -841,8 +824,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) @@ -850,54 +831,61 @@ target_ulong helper_4xx_tlbsx(CPUPPCState *env, target_ulong address) return ppcemb_tlb_search(env, address, env->spr[SPR_40x_PID]); } +static bool mmubooke_pid_match(CPUPPCState *env, ppcemb_tlb_t *tlb) +{ + if (tlb->PID == env->spr[SPR_BOOKE_PID]) { + return true; + } + if (!env->nb_pids) { + return false; + } + + if (env->spr[SPR_BOOKE_PID1] && tlb->PID == env->spr[SPR_BOOKE_PID1]) { + return true; + } + if (env->spr[SPR_BOOKE_PID2] && tlb->PID == env->spr[SPR_BOOKE_PID2]) { + return true; + } + + return false; +} + /* PowerPC 440 TLB management */ void helper_440_tlbwe(CPUPPCState *env, uint32_t word, target_ulong entry, target_ulong value) { ppcemb_tlb_t *tlb; - target_ulong EPN, RPN, size; - int do_flush_tlbs; qemu_log_mask(CPU_LOG_MMU, "%s word %d entry %d value " TARGET_FMT_lx "\n", __func__, word, (int)entry, value); - do_flush_tlbs = 0; entry &= 0x3F; tlb = &env->tlb.tlbe[entry]; + + /* Invalidate previous TLB (if it's valid) */ + if ((tlb->prot & PAGE_VALID) && mmubooke_pid_match(env, tlb)) { + qemu_log_mask(CPU_LOG_MMU, "%s: invalidate old TLB %d start " + TARGET_FMT_lx " end " TARGET_FMT_lx "\n", __func__, + (int)entry, tlb->EPN, tlb->EPN + tlb->size); + ppcemb_tlb_flush(env_cpu(env), tlb); + } + switch (word) { default: /* Just here to please gcc */ case 0: - EPN = value & 0xFFFFFC00; - if ((tlb->prot & PAGE_VALID) && EPN != tlb->EPN) { - do_flush_tlbs = 1; - } - tlb->EPN = EPN; - size = booke_tlb_to_page_size((value >> 4) & 0xF); - if ((tlb->prot & PAGE_VALID) && tlb->size < size) { - do_flush_tlbs = 1; - } - tlb->size = size; + tlb->EPN = value & 0xFFFFFC00; + tlb->size = booke_tlb_to_page_size((value >> 4) & 0xF); tlb->attr &= ~0x1; tlb->attr |= (value >> 8) & 1; if (value & 0x200) { tlb->prot |= PAGE_VALID; } else { - if (tlb->prot & PAGE_VALID) { - tlb->prot &= ~PAGE_VALID; - do_flush_tlbs = 1; - } + tlb->prot &= ~PAGE_VALID; } tlb->PID = env->spr[SPR_440_MMUCR] & 0x000000FF; - if (do_flush_tlbs) { - tlb_flush(env_cpu(env)); - } break; case 1: - RPN = value & 0xFFFFFC0F; - if ((tlb->prot & PAGE_VALID) && tlb->RPN != RPN) { - tlb_flush(env_cpu(env)); - } - tlb->RPN = RPN; + tlb->RPN = value & 0xFFFFFC0F; break; case 2: tlb->attr = (tlb->attr & 0x1) | (value & 0x0000FF00); diff --git a/target/ppc/power8-pmu-regs.c.inc b/target/ppc/power8-pmu-regs.c.inc index c82feedaff0..652cf20704b 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. @@ -106,7 +106,7 @@ static void write_MMCR0_common(DisasContext *ctx, TCGv val) * translator_io_start() beforehand. */ translator_io_start(&ctx->base); - gen_helper_store_mmcr0(cpu_env, val); + gen_helper_store_mmcr0(tcg_env, val); /* * End the translation block because MMCR0 writes can change @@ -175,12 +175,17 @@ void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn) gen_store_spr(SPR_POWER_MMCR2, 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_constant_i32(sprn); translator_io_start(&ctx->base); - gen_helper_read_pmc(cpu_gpr[gprn], cpu_env, t_sprn); + gen_helper_read_pmc(cpu_gpr[gprn], tcg_env, t_sprn); } void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn) @@ -213,7 +218,7 @@ void spr_write_PMC(DisasContext *ctx, int sprn, int gprn) TCGv_i32 t_sprn = tcg_constant_i32(sprn); translator_io_start(&ctx->base); - gen_helper_store_pmc(cpu_env, t_sprn, cpu_gpr[gprn]); + gen_helper_store_pmc(tcg_env, t_sprn, cpu_gpr[gprn]); } void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn) @@ -249,7 +254,7 @@ void spr_write_MMCR0(DisasContext *ctx, int sprn, int gprn) void spr_write_MMCR1(DisasContext *ctx, int sprn, int gprn) { translator_io_start(&ctx->base); - gen_helper_store_mmcr1(cpu_env, cpu_gpr[gprn]); + 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 cbc5889d91d..2a7a5b493af 100644 --- a/target/ppc/power8-pmu.c +++ b/target/ppc/power8-pmu.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "helper_regs.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qemu/error-report.h" #include "qemu/timer.h" @@ -82,7 +81,38 @@ 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); @@ -95,6 +125,8 @@ void pmu_mmcr01_updated(CPUPPCState *env) 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? @@ -260,7 +292,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); @@ -272,7 +304,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) @@ -301,7 +340,7 @@ static void perfm_alert(PowerPCCPU *cpu) 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 diff --git a/target/ppc/power8-pmu.h b/target/ppc/power8-pmu.h index 775e6400536..3f79cfc45b7 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 index f9acc210562..7022564604f 100644 --- a/target/ppc/ppc-qmp-cmds.c +++ b/target/ppc/ppc-qmp-cmds.c @@ -28,7 +28,8 @@ #include "qemu/ctype.h" #include "monitor/hmp-target.h" #include "monitor/hmp.h" -#include "qapi/qapi-commands-machine-target.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" #include "cpu-models.h" #include "cpu-qom.h" @@ -103,7 +104,11 @@ const MonitorDef monitor_defs[] = { { "xer", 0, &monitor_get_xer }, { "msr", offsetof(CPUPPCState, msr) }, { "tbu", 0, &monitor_get_tbu, }, +#if defined(TARGET_PPC64) + { "tb", 0, &monitor_get_tbl, }, +#else { "tbl", 0, &monitor_get_tbl, }, +#endif { NULL }, }; @@ -133,8 +138,7 @@ static int ppc_cpu_get_reg_num(const char *numstr, int maxnum, int *pregnum) int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval) { int i, regnum; - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + CPUPPCState *env = cpu_env(cs); /* General purpose registers */ if ((qemu_tolower(name[0]) == 'r') && @@ -172,6 +176,15 @@ int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval) return -EINVAL; } +CpuModelExpansionInfo * +qmp_query_cpu_model_expansion(CpuModelExpansionType type, + CpuModelInfo *model, + Error **errp) +{ + error_setg(errp, "CPU model expansion is not supported on this target"); + return NULL; +} + static void ppc_cpu_defs_entry(gpointer data, gpointer user_data) { ObjectClass *oc = data; @@ -181,8 +194,7 @@ static void ppc_cpu_defs_entry(gpointer data, gpointer user_data) typename = object_class_get_name(oc); info = g_malloc0(sizeof(*info)); - info->name = g_strndup(typename, - strlen(typename) - strlen(POWERPC_CPU_TYPE_SUFFIX)); + info->name = cpu_model_from_type(typename); QAPI_LIST_PREPEND(*first, info); } diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h index 8a9d6cd994c..84c910c440f 100644 --- a/target/ppc/spr_common.h +++ b/target/ppc/spr_common.h @@ -83,8 +83,11 @@ 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); @@ -162,6 +165,8 @@ 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_dawr1(DisasContext *ctx, int sprn, int gprn); +void spr_write_dawrx1(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); @@ -201,7 +206,14 @@ 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_pmsr(DisasContext *ctx, int gprn, int sprn); +void spr_write_pmcr(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-excp_helper.c b/target/ppc/tcg-excp_helper.c new file mode 100644 index 00000000000..f835be51563 --- /dev/null +++ b/target/ppc/tcg-excp_helper.c @@ -0,0 +1,851 @@ +/* + * PowerPC exception emulation helpers for QEMU (TCG specific) + * + * 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 "qemu/main-loop.h" +#include "qemu/log.h" +#include "target/ppc/cpu.h" +#include "accel/tcg/cpu-ldst.h" +#include "exec/helper-proto.h" +#include "system/runstate.h" + +#include "helper_regs.h" +#include "hw/ppc/ppc.h" +#include "internal.h" +#include "cpu.h" +#include "trace.h" + +/*****************************************************************************/ +/* Exceptions processing helpers */ + +void raise_exception_err_ra(CPUPPCState *env, uint32_t exception, + uint32_t error_code, uintptr_t raddr) +{ + CPUState *cs = env_cpu(env); + + cs->exception_index = exception; + env->error_code = error_code; + cpu_loop_exit_restore(cs, raddr); +} + +void helper_raise_exception_err(CPUPPCState *env, uint32_t exception, + uint32_t error_code) +{ + raise_exception_err_ra(env, exception, error_code, 0); +} + +void helper_raise_exception(CPUPPCState *env, uint32_t exception) +{ + raise_exception_err_ra(env, exception, 0, 0); +} + +#ifndef CONFIG_USER_ONLY + +static G_NORETURN void raise_exception_err(CPUPPCState *env, uint32_t exception, + uint32_t error_code) +{ + raise_exception_err_ra(env, exception, error_code, 0); +} + +static G_NORETURN void raise_exception(CPUPPCState *env, uint32_t exception) +{ + raise_exception_err_ra(env, exception, 0, 0); +} + +#endif /* !CONFIG_USER_ONLY */ + +void helper_TW(CPUPPCState *env, target_ulong arg1, target_ulong arg2, + uint32_t flags) +{ + if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) || + ((int32_t)arg1 > (int32_t)arg2 && (flags & 0x08)) || + ((int32_t)arg1 == (int32_t)arg2 && (flags & 0x04)) || + ((uint32_t)arg1 < (uint32_t)arg2 && (flags & 0x02)) || + ((uint32_t)arg1 > (uint32_t)arg2 && (flags & 0x01))))) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_TRAP, GETPC()); + } +} + +#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)) || + ((int64_t)arg1 > (int64_t)arg2 && (flags & 0x08)) || + ((int64_t)arg1 == (int64_t)arg2 && (flags & 0x04)) || + ((uint64_t)arg1 < (uint64_t)arg2 && (flags & 0x02)) || + ((uint64_t)arg1 > (uint64_t)arg2 && (flags & 0x01))))) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_TRAP, GETPC()); + } +} +#endif /* TARGET_PPC64 */ + +static uint32_t helper_SIMON_LIKE_32_64(uint32_t x, uint64_t key, uint32_t lane) +{ + const uint16_t c = 0xfffc; + const uint64_t z0 = 0xfa2561cdf44ac398ULL; + uint16_t z = 0, temp; + uint16_t k[32], eff_k[32], xleft[33], xright[33], fxleft[32]; + + for (int i = 3; i >= 0; i--) { + k[i] = key & 0xffff; + key >>= 16; + } + xleft[0] = x & 0xffff; + xright[0] = (x >> 16) & 0xffff; + + for (int i = 0; i < 28; i++) { + z = (z0 >> (63 - i)) & 1; + temp = ror16(k[i + 3], 3) ^ k[i + 1]; + k[i + 4] = c ^ z ^ k[i] ^ temp ^ ror16(temp, 1); + } + + for (int i = 0; i < 8; i++) { + eff_k[4 * i + 0] = k[4 * i + ((0 + lane) % 4)]; + eff_k[4 * i + 1] = k[4 * i + ((1 + lane) % 4)]; + eff_k[4 * i + 2] = k[4 * i + ((2 + lane) % 4)]; + eff_k[4 * i + 3] = k[4 * i + ((3 + lane) % 4)]; + } + + for (int i = 0; i < 32; i++) { + fxleft[i] = (rol16(xleft[i], 1) & + rol16(xleft[i], 8)) ^ rol16(xleft[i], 2); + xleft[i + 1] = xright[i] ^ fxleft[i] ^ eff_k[i]; + xright[i + 1] = xleft[i]; + } + + return (((uint32_t)xright[32]) << 16) | xleft[32]; +} + +static uint64_t hash_digest(uint64_t ra, uint64_t rb, uint64_t key) +{ + uint64_t stage0_h = 0ULL, stage0_l = 0ULL; + uint64_t stage1_h, stage1_l; + + for (int i = 0; i < 4; i++) { + stage0_h |= ror64(rb & 0xff, 8 * (2 * i + 1)); + stage0_h |= ((ra >> 32) & 0xff) << (8 * 2 * i); + stage0_l |= ror64((rb >> 32) & 0xff, 8 * (2 * i + 1)); + stage0_l |= (ra & 0xff) << (8 * 2 * i); + rb >>= 8; + ra >>= 8; + } + + stage1_h = (uint64_t)helper_SIMON_LIKE_32_64(stage0_h >> 32, key, 0) << 32; + stage1_h |= helper_SIMON_LIKE_32_64(stage0_h, key, 1); + stage1_l = (uint64_t)helper_SIMON_LIKE_32_64(stage0_l >> 32, key, 2) << 32; + stage1_l |= helper_SIMON_LIKE_32_64(stage0_l, key, 3); + + return stage1_h ^ stage1_l; +} + +static void do_hash(CPUPPCState *env, target_ulong ea, target_ulong ra, + target_ulong rb, uint64_t key, bool store) +{ + uint64_t calculated_hash = hash_digest(ra, rb, key), loaded_hash; + + if (store) { + cpu_stq_data_ra(env, ea, calculated_hash, GETPC()); + } else { + loaded_hash = cpu_ldq_data_ra(env, ea, GETPC()); + if (loaded_hash != calculated_hash) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_TRAP, GETPC()); + } + } +} + +#include "qemu/guest-random.h" + +#ifdef TARGET_PPC64 +#define HELPER_HASH(op, key, store, dexcr_aspect) \ +void helper_##op(CPUPPCState *env, target_ulong ea, target_ulong ra, \ + target_ulong rb) \ +{ \ + if (env->msr & R_MSR_PR_MASK) { \ + if (!(env->spr[SPR_DEXCR] & R_DEXCR_PRO_##dexcr_aspect##_MASK || \ + env->spr[SPR_HDEXCR] & R_HDEXCR_ENF_##dexcr_aspect##_MASK)) \ + return; \ + } else if (!(env->msr & R_MSR_HV_MASK)) { \ + if (!(env->spr[SPR_DEXCR] & R_DEXCR_PNH_##dexcr_aspect##_MASK || \ + env->spr[SPR_HDEXCR] & R_HDEXCR_ENF_##dexcr_aspect##_MASK)) \ + return; \ + } else if (!(env->msr & R_MSR_S_MASK)) { \ + if (!(env->spr[SPR_HDEXCR] & R_HDEXCR_HNU_##dexcr_aspect##_MASK)) \ + return; \ + } \ + \ + do_hash(env, ea, ra, rb, key, store); \ +} +#else +#define HELPER_HASH(op, key, store, dexcr_aspect) \ +void helper_##op(CPUPPCState *env, target_ulong ea, target_ulong ra, \ + target_ulong rb) \ +{ \ + do_hash(env, ea, ra, rb, key, store); \ +} +#endif /* TARGET_PPC64 */ + +HELPER_HASH(HASHST, env->spr[SPR_HASHKEYR], true, NPHIE) +HELPER_HASH(HASHCHK, env->spr[SPR_HASHKEYR], false, NPHIE) +HELPER_HASH(HASHSTP, env->spr[SPR_HASHPKEYR], true, PHIE) +HELPER_HASH(HASHCHKP, env->spr[SPR_HASHPKEYR], false, PHIE) + +#ifndef CONFIG_USER_ONLY + +void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr) +{ + 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 = ppc_ldl_code(env, env->nip); + + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_4xx: + env->spr[SPR_40x_DEAR] = vaddr; + break; + case POWERPC_MMU_BOOKE: + case POWERPC_MMU_BOOKE206: + env->spr[SPR_BOOKE_DEAR] = vaddr; + break; + default: + env->spr[SPR_DAR] = vaddr; + break; + } + + cs->exception_index = POWERPC_EXCP_ALIGN; + env->error_code = insn & 0x03FF0000; + cpu_loop_exit(cs); +} + +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); + bool wt, wti, hv, sv, pr; + uint32_t dawrx; + + if ((env->insns_flags2 & PPC2_ISA207S) && + (wp == env->dawr_watchpoint[0])) { + dawrx = env->spr[SPR_DAWRX0]; + } else if ((env->insns_flags2 & PPC2_ISA310) && + (wp == env->dawr_watchpoint[1])) { + dawrx = env->spr[SPR_DAWRX1]; + } else { + return false; + } + + wt = extract32(dawrx, PPC_BIT_NR(59), 1); + wti = extract32(dawrx, PPC_BIT_NR(60), 1); + hv = extract32(dawrx, PPC_BIT_NR(61), 1); + sv = extract32(dawrx, PPC_BIT_NR(62), 1); + 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)) { + return wt; + } else { + return !wt; + } + } + + return true; +#endif + + return false; +} + +/* + * 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. + */ +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); +} + +/* 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)); +} + +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; +} + +#if defined(TARGET_PPC64) +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); + } +} + +void helper_scv(CPUPPCState *env, uint32_t lev) +{ + if (env->spr[SPR_FSCR] & (1ull << FSCR_SCV)) { + raise_exception_err(env, POWERPC_EXCP_SYSCALL_VECTORED, lev); + } else { + raise_exception_err(env, POWERPC_EXCP_FU, FSCR_IC_SCV); + } +} + +void helper_pminsn(CPUPPCState *env, uint32_t insn) +{ + CPUState *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 /* TARGET_PPC64 */ +void helper_store_msr(CPUPPCState *env, target_ulong val) +{ + uint32_t excp = hreg_store_msr(env, val, 0); + + if (excp != 0) { + cpu_interrupt_exittb(env_cpu(env)); + raise_exception(env, excp); + } +} + +void helper_ppc_maybe_interrupt(CPUPPCState *env) +{ + ppc_maybe_interrupt(env); +} + +static void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr) +{ + /* MSR:POW cannot be set by any form of rfi */ + msr &= ~(1ULL << MSR_POW); + + /* MSR:TGPR cannot be set by any form of rfi */ + if (env->flags & POWERPC_FLAG_TGPR) { + msr &= ~(1ULL << MSR_TGPR); + } + +#ifdef TARGET_PPC64 + /* Switching to 32-bit ? Crop the nip */ + if (!msr_is_64bit(env, msr)) { + nip = (uint32_t)nip; + } +#else + nip = (uint32_t)nip; +#endif + /* XXX: beware: this is false if VLE is supported */ + env->nip = nip & ~((target_ulong)0x00000003); + hreg_store_msr(env, msr, 1); + trace_ppc_excp_rfi(env->nip, env->msr); + /* + * No need to raise an exception here, as rfi is always the last + * insn of a TB + */ + cpu_interrupt_exittb(env_cpu(env)); + /* Reset the reservation */ + env->reserve_addr = -1; + + /* Context synchronizing: check if TCG TLB needs flush */ + check_tlb_flush(env, false); +} + +void helper_rfi(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful); +} + +#ifdef TARGET_PPC64 +void helper_rfid(CPUPPCState *env) +{ + /* + * The architecture defines a number of rules for which bits can + * change but in practice, we handle this in hreg_store_msr() + * which will be called by do_rfi(), so there is no need to filter + * here + */ + do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1]); +} + +void helper_rfscv(CPUPPCState *env) +{ + do_rfi(env, env->lr, env->ctr); +} + +void helper_hrfid(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]); +} + +void helper_rfebb(CPUPPCState *env, target_ulong s) +{ + target_ulong msr = env->msr; + + /* + * Handling of BESCR bits 32:33 according to PowerISA v3.1: + * + * "If BESCR 32:33 != 0b00 the instruction is treated as if + * the instruction form were invalid." + */ + if (env->spr[SPR_BESCR] & BESCR_INVALID) { + raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL); + } + + env->nip = env->spr[SPR_EBBRR]; + + /* Switching to 32-bit ? Crop the nip */ + if (!msr_is_64bit(env, msr)) { + env->nip = (uint32_t)env->spr[SPR_EBBRR]; + } + + if (s) { + env->spr[SPR_BESCR] |= BESCR_GE; + } else { + env->spr[SPR_BESCR] &= ~BESCR_GE; + } +} + +/* + * Triggers or queues an 'ebb_excp' EBB exception. All checks + * but FSCR, HFSCR and msr_pr must be done beforehand. + * + * PowerISA v3.1 isn't clear about whether an EBB should be + * postponed or cancelled if the EBB facility is unavailable. + * Our assumption here is that the EBB is cancelled if both + * FSCR and HFSCR EBB facilities aren't available. + */ +static void do_ebb(CPUPPCState *env, int ebb_excp) +{ + PowerPCCPU *cpu = env_archcpu(env); + + /* + * FSCR_EBB and FSCR_IC_EBB are the same bits used with + * HFSCR. + */ + helper_fscr_facility_check(env, FSCR_EBB, 0, FSCR_IC_EBB); + helper_hfscr_facility_check(env, FSCR_EBB, "EBB", FSCR_IC_EBB); + + if (ebb_excp == POWERPC_EXCP_PERFM_EBB) { + env->spr[SPR_BESCR] |= BESCR_PMEO; + } else if (ebb_excp == POWERPC_EXCP_EXTERNAL_EBB) { + env->spr[SPR_BESCR] |= BESCR_EEO; + } + + if (FIELD_EX64(env->msr, MSR, PR)) { + powerpc_excp(cpu, ebb_excp); + } else { + ppc_set_irq(cpu, PPC_INTERRUPT_EBB, 1); + } +} + +void raise_ebb_perfm_exception(CPUPPCState *env) +{ + bool perfm_ebb_enabled = env->spr[SPR_POWER_MMCR0] & MMCR0_EBE && + env->spr[SPR_BESCR] & BESCR_PME && + env->spr[SPR_BESCR] & BESCR_GE; + + if (!perfm_ebb_enabled) { + return; + } + + do_ebb(env, POWERPC_EXCP_PERFM_EBB); +} +#endif /* TARGET_PPC64 */ + +/*****************************************************************************/ +/* Embedded PowerPC specific helpers */ +void helper_40x_rfci(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3]); +} + +void helper_rfci(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1]); +} + +void helper_rfdi(CPUPPCState *env) +{ + /* FIXME: choose CSRR1 or DSRR1 based on cpu type */ + do_rfi(env, env->spr[SPR_BOOKE_DSRR0], env->spr[SPR_BOOKE_DSRR1]); +} + +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]); +} + +/* Embedded.Processor Control */ +static int dbell2irq(target_ulong rb) +{ + int msg = rb & DBELL_TYPE_MASK; + int irq = -1; + + switch (msg) { + case DBELL_TYPE_DBELL: + irq = PPC_INTERRUPT_DOORBELL; + break; + case DBELL_TYPE_DBELL_CRIT: + irq = PPC_INTERRUPT_CDOORBELL; + break; + case DBELL_TYPE_G_DBELL: + case DBELL_TYPE_G_DBELL_CRIT: + case DBELL_TYPE_G_DBELL_MC: + /* XXX implement */ + default: + break; + } + + return irq; +} + +void helper_msgclr(CPUPPCState *env, target_ulong rb) +{ + int irq = dbell2irq(rb); + + if (irq < 0) { + return; + } + + ppc_set_irq(env_archcpu(env), irq, 0); +} + +void helper_msgsnd(target_ulong rb) +{ + int irq = dbell2irq(rb); + int pir = rb & DBELL_PIRTAG_MASK; + CPUState *cs; + + if (irq < 0) { + return; + } + + bql_lock(); + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *cenv = &cpu->env; + + if ((rb & DBELL_BRDCAST_MASK) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { + ppc_set_irq(cpu, irq, 1); + } + } + bql_unlock(); +} + +/* Server Processor Control */ + +static bool dbell_type_server(target_ulong rb) +{ + /* + * A Directed Hypervisor Doorbell message is sent only if the + * message type is 5. All other types are reserved and the + * instruction is a no-op + */ + return (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)) { + return; + } + + ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_HDOORBELL, 0); +} + +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; + } + + /* 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(); +} + +#ifdef TARGET_PPC64 +void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb) +{ + helper_hfscr_facility_check(env, HFSCR_MSGP, "msgclrp", HFSCR_IC_MSGP); + + if (!dbell_type_server(rb)) { + return; + } + + ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_DOORBELL, 0); +} + +/* + * sends a message to another thread on the same + * multi-threaded processor + */ +void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb) +{ + helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP); + + if (!dbell_type_server(rb)) { + return; + } + + 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); +} +#endif /* !CONFIG_USER_ONLY */ diff --git a/target/ppc/tcg-stub.c b/target/ppc/tcg-stub.c index aadcf59d261..740d796b98c 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 08a6b47ee08..7209b418fb6 100644 --- a/target/ppc/timebase_helper.c +++ b/target/ppc/timebase_helper.c @@ -18,8 +18,8 @@ */ #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" #include "qemu/main-loop.h" @@ -59,19 +59,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 +134,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,16 +210,253 @@ void helper_store_booke_tsr(CPUPPCState *env, target_ulong val) store_booke_tsr(env, val); } -#if defined(TARGET_PPC64) -/* POWER processor Timebase Facility */ +#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) { - return env->spr[SPR_TFMR]; + tb_state_machine_step(env); + + return env->spr[SPR_TFMR] | TFMR_TB_ECLIPZ; } void helper_store_tfmr(CPUPPCState *env, target_ulong val) { - env->spr[SPR_TFMR] = 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 @@ -173,9 +476,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); @@ -196,9 +499,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 5c28afbbb8f..27f90c3cc56 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -21,8 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" -#include "disas/disas.h" -#include "exec/exec-all.h" +#include "exec/target_page.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" #include "qemu/host-utils.h" @@ -31,6 +30,7 @@ #include "exec/helper-gen.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/log.h" #include "qemu/atomic128.h" #include "spr_common.h" @@ -92,7 +92,7 @@ void ppc_translate_init(void) for (i = 0; i < 8; i++) { snprintf(p, cpu_reg_names_size, "crf%d", i); - cpu_crf[i] = tcg_global_mem_new_i32(cpu_env, + cpu_crf[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUPPCState, crf[i]), p); p += 5; cpu_reg_names_size -= 5; @@ -100,67 +100,67 @@ void ppc_translate_init(void) for (i = 0; i < 32; i++) { snprintf(p, cpu_reg_names_size, "r%d", i); - cpu_gpr[i] = tcg_global_mem_new(cpu_env, + cpu_gpr[i] = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, gpr[i]), p); p += (i < 10) ? 3 : 4; cpu_reg_names_size -= (i < 10) ? 3 : 4; snprintf(p, cpu_reg_names_size, "r%dH", i); - cpu_gprh[i] = tcg_global_mem_new(cpu_env, + cpu_gprh[i] = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, gprh[i]), p); p += (i < 10) ? 4 : 5; cpu_reg_names_size -= (i < 10) ? 4 : 5; } - cpu_nip = tcg_global_mem_new(cpu_env, + cpu_nip = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, nip), "nip"); - cpu_msr = tcg_global_mem_new(cpu_env, + cpu_msr = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, msr), "msr"); - cpu_ctr = tcg_global_mem_new(cpu_env, + cpu_ctr = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, ctr), "ctr"); - cpu_lr = tcg_global_mem_new(cpu_env, + cpu_lr = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, lr), "lr"); #if defined(TARGET_PPC64) - cpu_cfar = tcg_global_mem_new(cpu_env, + cpu_cfar = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, cfar), "cfar"); #endif - cpu_xer = tcg_global_mem_new(cpu_env, + cpu_xer = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, xer), "xer"); - cpu_so = tcg_global_mem_new(cpu_env, + cpu_so = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, so), "SO"); - cpu_ov = tcg_global_mem_new(cpu_env, + cpu_ov = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, ov), "OV"); - cpu_ca = tcg_global_mem_new(cpu_env, + cpu_ca = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, ca), "CA"); - cpu_ov32 = tcg_global_mem_new(cpu_env, + cpu_ov32 = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, ov32), "OV32"); - cpu_ca32 = tcg_global_mem_new(cpu_env, + cpu_ca32 = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, ca32), "CA32"); - cpu_reserve = tcg_global_mem_new(cpu_env, + cpu_reserve = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, reserve_addr), "reserve_addr"); - cpu_reserve_length = tcg_global_mem_new(cpu_env, + cpu_reserve_length = tcg_global_mem_new(tcg_env, offsetof(CPUPPCState, reserve_length), "reserve_length"); - cpu_reserve_val = tcg_global_mem_new(cpu_env, + 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(cpu_env, + 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"); } @@ -179,8 +179,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; @@ -194,6 +196,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; @@ -240,20 +243,31 @@ static inline bool gen_serialize(DisasContext *ctx) { 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; return false; } return true; } -#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +#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 @@ -261,12 +275,12 @@ static inline bool gen_serialize_core_lpar(DisasContext *ctx) /* 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) @@ -285,33 +299,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); + gen_update_nip(ctx, nip); t0 = tcg_constant_i32(excp); t1 = tcg_constant_i32(error); - gen_helper_raise_exception_err(cpu_env, t0, t1); + 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_constant_i32(excp); - gen_helper_raise_exception(cpu_env, 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, @@ -321,15 +328,24 @@ static void gen_exception_nip(DisasContext *ctx, uint32_t excp, gen_update_nip(ctx, nip); t0 = tcg_constant_i32(excp); - gen_helper_raise_exception(cpu_env, t0); + gen_helper_raise_exception(tcg_env, t0); ctx->base.is_jmp = DISAS_NORETURN; } +static inline void gen_exception(DisasContext *ctx, uint32_t excp) +{ + /* + * These are all synchronous exceptions, we set the PC back to the + * faulting instruction + */ + gen_exception_nip(ctx, excp, ctx->cia); +} + #if !defined(CONFIG_USER_ONLY) static void gen_ppc_maybe_interrupt(DisasContext *ctx) { translator_io_start(&ctx->base); - gen_helper_ppc_maybe_interrupt(cpu_env); + gen_helper_ppc_maybe_interrupt(tcg_env); } #endif @@ -355,14 +371,14 @@ static void gen_debug_exception(DisasContext *ctx, bool rfi_type) gen_load_spr(t0, SPR_BOOKE_DBSR); tcg_gen_ori_tl(t0, t0, dbsr); gen_store_spr(SPR_BOOKE_DBSR, t0); - gen_helper_raise_exception(cpu_env, + gen_helper_raise_exception(tcg_env, tcg_constant_i32(POWERPC_EXCP_DEBUG)); ctx->base.is_jmp = DISAS_NORETURN; } else { 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(cpu_env, t0); + gen_helper_book3s_trace(tcg_env, t0); ctx->base.is_jmp = DISAS_NORETURN; } } @@ -407,7 +423,7 @@ static void spr_load_dump_spr(int sprn) { #ifdef PPC_DUMP_SPR_ACCESSES TCGv_i32 t0 = tcg_constant_i32(sprn); - gen_helper_load_dump_spr(cpu_env, t0); + gen_helper_load_dump_spr(tcg_env, t0); #endif } @@ -421,7 +437,7 @@ static void spr_store_dump_spr(int sprn) { #ifdef PPC_DUMP_SPR_ACCESSES TCGv_i32 t0 = tcg_constant_i32(sprn); - gen_helper_store_dump_spr(cpu_env, t0); + gen_helper_store_dump_spr(tcg_env, t0); #endif } @@ -454,11 +470,39 @@ void spr_core_write_generic(DisasContext *ctx, int sprn, int gprn) return; } - gen_helper_spr_core_write_generic(cpu_env, tcg_constant_i32(sprn), + 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 */ @@ -482,7 +526,7 @@ void spr_write_CTRL(DisasContext *ctx, int sprn, int gprn) return; } - gen_helper_spr_write_CTRL(cpu_env, tcg_constant_i32(sprn), + gen_helper_spr_write_CTRL(tcg_env, tcg_constant_i32(sprn), cpu_gpr[gprn]); out: spr_store_dump_spr(sprn); @@ -578,20 +622,32 @@ void spr_write_cfar(DisasContext *ctx, int sprn, int gprn) void spr_write_ciabr(DisasContext *ctx, int sprn, int gprn) { translator_io_start(&ctx->base); - gen_helper_store_ciabr(cpu_env, cpu_gpr[gprn]); + 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(cpu_env, cpu_gpr[gprn]); + 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(cpu_env, cpu_gpr[gprn]); + gen_helper_store_dawrx0(tcg_env, cpu_gpr[gprn]); +} + +void spr_write_dawr1(DisasContext *ctx, int sprn, int gprn) +{ + translator_io_start(&ctx->base); + gen_helper_store_dawr1(tcg_env, cpu_gpr[gprn]); +} + +void spr_write_dawrx1(DisasContext *ctx, int sprn, int gprn) +{ + translator_io_start(&ctx->base); + gen_helper_store_dawrx1(tcg_env, cpu_gpr[gprn]); } #endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */ @@ -630,13 +686,13 @@ void spr_write_ureg(DisasContext *ctx, int sprn, int gprn) void spr_read_decr(DisasContext *ctx, int gprn, int sprn) { translator_io_start(&ctx->base); - gen_helper_load_decr(cpu_gpr[gprn], cpu_env); + gen_helper_load_decr(cpu_gpr[gprn], tcg_env); } void spr_write_decr(DisasContext *ctx, int sprn, int gprn) { translator_io_start(&ctx->base); - gen_helper_store_decr(cpu_env, cpu_gpr[gprn]); + gen_helper_store_decr(tcg_env, cpu_gpr[gprn]); } #endif @@ -645,90 +701,110 @@ void spr_write_decr(DisasContext *ctx, int sprn, int gprn) void spr_read_tbl(DisasContext *ctx, int gprn, int sprn) { translator_io_start(&ctx->base); - gen_helper_load_tbl(cpu_gpr[gprn], cpu_env); + gen_helper_load_tbl(cpu_gpr[gprn], tcg_env); } void spr_read_tbu(DisasContext *ctx, int gprn, int sprn) { translator_io_start(&ctx->base); - gen_helper_load_tbu(cpu_gpr[gprn], cpu_env); + 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) { + if (!gen_serialize_core_lpar(ctx)) { + return; + } + translator_io_start(&ctx->base); - gen_helper_store_tbl(cpu_env, cpu_gpr[gprn]); + gen_helper_store_tbl(tcg_env, cpu_gpr[gprn]); } void spr_write_tbu(DisasContext *ctx, int sprn, int gprn) { + if (!gen_serialize_core_lpar(ctx)) { + return; + } + translator_io_start(&ctx->base); - gen_helper_store_tbu(cpu_env, cpu_gpr[gprn]); + 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) { translator_io_start(&ctx->base); - gen_helper_load_purr(cpu_gpr[gprn], cpu_env); + gen_helper_load_purr(cpu_gpr[gprn], tcg_env); } void spr_write_purr(DisasContext *ctx, int sprn, int gprn) { + if (!gen_serialize_core_lpar(ctx)) { + return; + } translator_io_start(&ctx->base); - gen_helper_store_purr(cpu_env, cpu_gpr[gprn]); + gen_helper_store_purr(tcg_env, cpu_gpr[gprn]); } /* HDECR */ void spr_read_hdecr(DisasContext *ctx, int gprn, int sprn) { translator_io_start(&ctx->base); - gen_helper_load_hdecr(cpu_gpr[gprn], cpu_env); + gen_helper_load_hdecr(cpu_gpr[gprn], tcg_env); } void spr_write_hdecr(DisasContext *ctx, int sprn, int gprn) { + if (!gen_serialize_core_lpar(ctx)) { + return; + } translator_io_start(&ctx->base); - gen_helper_store_hdecr(cpu_env, cpu_gpr[gprn]); + gen_helper_store_hdecr(tcg_env, cpu_gpr[gprn]); } void spr_read_vtb(DisasContext *ctx, int gprn, int sprn) { translator_io_start(&ctx->base); - gen_helper_load_vtb(cpu_gpr[gprn], cpu_env); + gen_helper_load_vtb(cpu_gpr[gprn], tcg_env); } void spr_write_vtb(DisasContext *ctx, int sprn, int gprn) { + if (!gen_serialize_core_lpar(ctx)) { + return; + } translator_io_start(&ctx->base); - gen_helper_store_vtb(cpu_env, cpu_gpr[gprn]); + gen_helper_store_vtb(tcg_env, cpu_gpr[gprn]); } void spr_write_tbu40(DisasContext *ctx, int sprn, int gprn) { + if (!gen_serialize_core_lpar(ctx)) { + return; + } translator_io_start(&ctx->base); - gen_helper_store_tbu40(cpu_env, cpu_gpr[gprn]); + gen_helper_store_tbu40(tcg_env, cpu_gpr[gprn]); } #endif @@ -739,14 +815,14 @@ 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])); } @@ -754,39 +830,39 @@ void spr_read_ibat_h(DisasContext *ctx, int gprn, int sprn) void spr_write_ibatu(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_constant_i32((sprn - SPR_IBAT0U) / 2); - gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); + 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_constant_i32(((sprn - SPR_IBAT4U) / 2) + 4); - gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); + gen_helper_store_ibatu(tcg_env, t0, cpu_gpr[gprn]); } void spr_write_ibatl(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_constant_i32((sprn - SPR_IBAT0L) / 2); - gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); + 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_constant_i32(((sprn - SPR_IBAT4L) / 2) + 4); - gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); + 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])); } @@ -794,31 +870,31 @@ void spr_read_dbat_h(DisasContext *ctx, int gprn, int sprn) void spr_write_dbatu(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_constant_i32((sprn - SPR_DBAT0U) / 2); - gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); + 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_constant_i32(((sprn - SPR_DBAT4U) / 2) + 4); - gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); + gen_helper_store_dbatu(tcg_env, t0, cpu_gpr[gprn]); } void spr_write_dbatl(DisasContext *ctx, int sprn, int gprn) { TCGv_i32 t0 = tcg_constant_i32((sprn - SPR_DBAT0L) / 2); - gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); + 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_constant_i32(((sprn - SPR_DBAT4L) / 2) + 4); - gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); + 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) @@ -826,33 +902,37 @@ 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_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 */ @@ -862,7 +942,7 @@ void spr_read_dpdes(DisasContext *ctx, int gprn, int sprn) return; } - gen_helper_load_dpdes(cpu_gpr[gprn], cpu_env); + gen_helper_load_dpdes(cpu_gpr[gprn], tcg_env); } void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn) @@ -871,7 +951,7 @@ void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn) return; } - gen_helper_store_dpdes(cpu_env, cpu_gpr[gprn]); + gen_helper_store_dpdes(tcg_env, cpu_gpr[gprn]); } #endif #endif @@ -881,20 +961,20 @@ void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn) void spr_read_40x_pit(DisasContext *ctx, int gprn, int sprn) { translator_io_start(&ctx->base); - gen_helper_load_40x_pit(cpu_gpr[gprn], cpu_env); + gen_helper_load_40x_pit(cpu_gpr[gprn], tcg_env); } void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn) { translator_io_start(&ctx->base); - gen_helper_store_40x_pit(cpu_env, cpu_gpr[gprn]); + gen_helper_store_40x_pit(tcg_env, cpu_gpr[gprn]); } void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn) { 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; } @@ -902,38 +982,38 @@ void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn) void spr_write_40x_sler(DisasContext *ctx, int sprn, int gprn) { translator_io_start(&ctx->base); - gen_helper_store_40x_sler(cpu_env, cpu_gpr[gprn]); + gen_helper_store_40x_sler(tcg_env, cpu_gpr[gprn]); } void spr_write_40x_tcr(DisasContext *ctx, int sprn, int gprn) { translator_io_start(&ctx->base); - gen_helper_store_40x_tcr(cpu_env, cpu_gpr[gprn]); + gen_helper_store_40x_tcr(tcg_env, cpu_gpr[gprn]); } void spr_write_40x_tsr(DisasContext *ctx, int sprn, int gprn) { translator_io_start(&ctx->base); - gen_helper_store_40x_tsr(cpu_env, cpu_gpr[gprn]); + 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); + gen_helper_store_40x_pid(tcg_env, t0); } void spr_write_booke_tcr(DisasContext *ctx, int sprn, int gprn) { translator_io_start(&ctx->base); - gen_helper_store_booke_tcr(cpu_env, cpu_gpr[gprn]); + gen_helper_store_booke_tcr(tcg_env, cpu_gpr[gprn]); } void spr_write_booke_tsr(DisasContext *ctx, int sprn, int gprn) { translator_io_start(&ctx->base); - gen_helper_store_booke_tsr(cpu_env, cpu_gpr[gprn]); + gen_helper_store_booke_tsr(tcg_env, cpu_gpr[gprn]); } #endif @@ -951,7 +1031,7 @@ 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); } @@ -959,7 +1039,7 @@ 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_gen_st_i32(t0, tcg_env, offsetof(CPUPPCState, spe_fscr)); } #if !defined(CONFIG_USER_ONLY) @@ -967,9 +1047,9 @@ 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); } @@ -991,9 +1071,9 @@ 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); } #endif @@ -1090,7 +1170,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); } @@ -1124,23 +1204,23 @@ void spr_write_e500_l2csr0(DisasContext *ctx, int sprn, int gprn) 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_constant_i32(sprn); - gen_helper_booke_setpid(cpu_env, t0, cpu_gpr[gprn]); + 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 @@ -1175,7 +1255,7 @@ static void gen_fscr_facility_check(DisasContext *ctx, int facility_sprn, TCGv_i32 t2 = tcg_constant_i32(sprn); TCGv_i32 t3 = tcg_constant_i32(cause); - gen_helper_fscr_facility_check(cpu_env, t1, t2, t3); + gen_helper_fscr_facility_check(tcg_env, t1, t2, t3); } static void gen_msr_facility_check(DisasContext *ctx, int facility_sprn, @@ -1185,7 +1265,7 @@ static void gen_msr_facility_check(DisasContext *ctx, int facility_sprn, TCGv_i32 t2 = tcg_constant_i32(sprn); TCGv_i32 t3 = tcg_constant_i32(cause); - gen_helper_msr_facility_check(cpu_env, t1, t2, t3); + gen_helper_msr_facility_check(tcg_env, t1, t2, t3); } void spr_read_prev_upper32(DisasContext *ctx, int gprn, int sprn) @@ -1220,19 +1300,60 @@ void spr_write_hmer(DisasContext *ctx, int sprn, int gprn) void spr_read_tfmr(DisasContext *ctx, int gprn, int sprn) { - gen_helper_load_tfmr(cpu_gpr[gprn], cpu_env); + /* 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) { - gen_helper_store_tfmr(cpu_env, cpu_gpr[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) { translator_io_start(&ctx->base); - gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]); + gen_helper_store_lpcr(tcg_env, cpu_gpr[gprn]); } + +void spr_read_pmsr(DisasContext *ctx, int gprn, int sprn) +{ + translator_io_start(&ctx->base); + gen_helper_load_pmsr(cpu_gpr[gprn], tcg_env); +} + +void spr_write_pmcr(DisasContext *ctx, int sprn, int gprn) +{ + if (!gen_serialize_core_lpar(ctx)) { + return; + } + translator_io_start(&ctx->base); + gen_helper_store_pmcr(tcg_env, cpu_gpr[gprn]); +} + #endif /* !defined(CONFIG_USER_ONLY) */ void spr_read_tar(DisasContext *ctx, int gprn, int sprn) @@ -1311,6 +1432,30 @@ void spr_read_dexcr_ureg(DisasContext *ctx, int gprn, int sprn) 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) \ @@ -1472,16 +1617,13 @@ 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); @@ -1524,73 +1666,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); -} - -#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_constant_tl(0); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr[rD(ctx->opcode)], t0, zr, - rA(ctx->opcode) ? cpu_gpr[rA(ctx->opcode)] : zr, - cpu_gpr[rB(ctx->opcode)]); -} - -/* 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, @@ -1670,11 +1745,10 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_mov_tl(ca32, ca); } } else { - 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); + tcg_gen_addcio_tl(t0, ca, arg1, arg2, ca); } else { + TCGv zero = tcg_constant_tl(0); tcg_gen_add2_tl(t0, ca, arg1, zero, arg2, zero); } gen_op_arith_compute_ca32(ctx, t0, arg1, arg2, ca32, 0); @@ -1697,64 +1771,10 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_mov_tl(ret, 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_constant_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)); \ -} - -/* 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_constant_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); -} -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(); @@ -1788,45 +1808,15 @@ static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); } - 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_constant_i32(compute_ov); \ - gen_helper_##hlpr(cpu_gpr[rD(ctx->opcode)], cpu_env, \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], 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(); @@ -1858,29 +1848,10 @@ static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); } - 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, @@ -1912,17 +1883,6 @@ static inline void gen_op_arith_modw(DisasContext *ctx, TCGv ret, TCGv arg1, } } -#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) @@ -1946,639 +1906,93 @@ static inline void gen_op_arith_modd(DisasContext *ctx, TCGv ret, TCGv arg1, } else { 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); - } -} - -#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); - 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); - 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); -#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); - - 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)]); - 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)]); - 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); - - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} -#endif - -/* Common subf function */ -static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, - TCGv arg2, bool add_ca, bool compute_ca, - bool compute_ov, bool compute_rc0) -{ - TCGv t0 = ret; - - if (compute_ca || compute_ov) { - t0 = tcg_temp_new(); - } - - if (compute_ca) { - /* dest = ~arg1 + arg2 [+ ca]. */ - if (NARROW_MODE(ctx)) { - /* - * Caution: a non-obvious corner case of the spec is that - * we must produce the *entire* 64-bit addition, but - * produce the carry into bit 32. - */ - TCGv inv1 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_not_tl(inv1, arg1); - if (add_ca) { - tcg_gen_add_tl(t0, arg2, cpu_ca); - } else { - tcg_gen_addi_tl(t0, arg2, 1); - } - tcg_gen_xor_tl(t1, arg2, inv1); /* add without carry */ - tcg_gen_add_tl(t0, t0, inv1); - tcg_gen_xor_tl(cpu_ca, t0, t1); /* bits changes w/ carry */ - tcg_gen_extract_tl(cpu_ca, cpu_ca, 32, 1); - if (is_isa300(ctx)) { - tcg_gen_mov_tl(cpu_ca32, cpu_ca); - } - } else if (add_ca) { - TCGv zero, inv1 = tcg_temp_new(); - tcg_gen_not_tl(inv1, arg1); - 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); - } else { - tcg_gen_setcond_tl(TCG_COND_GEU, cpu_ca, arg2, arg1); - tcg_gen_sub_tl(t0, arg2, arg1); - gen_op_arith_compute_ca32(ctx, t0, arg1, arg2, cpu_ca32, 1); - } - } else if (add_ca) { - /* - * Since we're ignoring carry-out, we can simplify the - * standard ~arg1 + arg2 + ca to arg2 - arg1 + ca - 1. - */ - tcg_gen_sub_tl(t0, arg2, arg1); - tcg_gen_add_tl(t0, t0, cpu_ca); - tcg_gen_subi_tl(t0, t0, 1); - } else { - tcg_gen_sub_tl(t0, arg2, arg1); - } - - if (compute_ov) { - gen_op_arith_compute_ov(ctx, t0, arg1, arg2, 1); - } - if (unlikely(compute_rc0)) { - gen_set_Rc0(ctx, t0); - } - - if (t0 != ret) { - tcg_gen_mov_tl(ret, 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_constant_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)); \ -} -/* 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_constant_tl(SIMM(ctx->opcode)); - gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - c, 0, 1, 0, 0); -} - -/* neg neg. nego nego. */ -static inline void gen_op_arith_neg(DisasContext *ctx, bool compute_ov) -{ - TCGv zero = tcg_constant_tl(0); - gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - zero, 0, 0, compute_ov, Rc(ctx->opcode)); -} - -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); - - 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); - - 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_constant_i32(0); - tcg_gen_st_i32(t0, cpu_env, - -offsetof(PowerPCCPU, env) + offsetof(CPUState, halted)); - - /* 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); - } -#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); -} - -#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_gen_movcond_i64(TCG_COND_EQ, t1, t1, t3, t2, t1); + tcg_gen_remu_i64(ret, t0, t1); + } } #endif -#if defined(TARGET_PPC64) -/* bpermd */ -static void gen_bpermd(DisasContext *ctx) +/* Common subf function */ +static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, + TCGv arg2, bool add_ca, bool compute_ca, + bool compute_ov, bool compute_rc0) { - gen_helper_bpermd(cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); -} -#endif + TCGv t0 = ret; -#if defined(TARGET_PPC64) -/* extsw & extsw. */ -GEN_LOGICAL1(extsw, tcg_gen_ext32s_tl, 0x1E, PPC_64B); + if (compute_ca || compute_ov) { + t0 = tcg_temp_new(); + } -/* 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)]); + if (compute_ca) { + /* dest = ~arg1 + arg2 [+ ca]. */ + if (NARROW_MODE(ctx)) { + /* + * Caution: a non-obvious corner case of the spec is that + * we must produce the *entire* 64-bit addition, but + * produce the carry into bit 32. + */ + TCGv inv1 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + tcg_gen_not_tl(inv1, arg1); + if (add_ca) { + tcg_gen_add_tl(t0, arg2, cpu_ca); + } else { + tcg_gen_addi_tl(t0, arg2, 1); + } + tcg_gen_xor_tl(t1, arg2, inv1); /* add without carry */ + tcg_gen_add_tl(t0, t0, inv1); + tcg_gen_xor_tl(cpu_ca, t0, t1); /* bits changes w/ carry */ + tcg_gen_extract_tl(cpu_ca, cpu_ca, 32, 1); + if (is_isa300(ctx)) { + tcg_gen_mov_tl(cpu_ca32, cpu_ca); + } + } else if (add_ca) { + TCGv inv1 = tcg_temp_new(); + tcg_gen_not_tl(inv1, arg1); + tcg_gen_addcio_tl(t0, cpu_ca, arg2, inv1, cpu_ca); + gen_op_arith_compute_ca32(ctx, t0, inv1, arg2, cpu_ca32, 0); + } else { + tcg_gen_setcond_tl(TCG_COND_GEU, cpu_ca, arg2, arg1); + tcg_gen_sub_tl(t0, arg2, arg1); + gen_op_arith_compute_ca32(ctx, t0, arg1, arg2, cpu_ca32, 1); + } + } else if (add_ca) { + /* + * Since we're ignoring carry-out, we can simplify the + * standard ~arg1 + arg2 + ca to arg2 - arg1 + ca - 1. + */ + tcg_gen_sub_tl(t0, arg2, arg1); + tcg_gen_add_tl(t0, t0, cpu_ca); + tcg_gen_subi_tl(t0, t0, 1); + } else { + tcg_gen_sub_tl(t0, arg2, arg1); } -} -/* 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)]); + if (compute_ov) { + gen_op_arith_compute_ov(ctx, t0, arg1, arg2, 1); + } + if (unlikely(compute_rc0)) { + gen_set_Rc0(ctx, t0); + } + + if (t0 != ret) { + tcg_gen_mov_tl(ret, t0); } } -/* darn */ -static void gen_darn(DisasContext *ctx) +/*** Integer logical ***/ + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +static void gen_pause(DisasContext *ctx) { - int l = L(ctx->opcode); + TCGv_i32 t0 = tcg_constant_i32(0); + tcg_gen_st_i32(t0, tcg_env, + -offsetof(PowerPCCPU, env) + offsetof(CPUState, halted)); - if (l > 2) { - tcg_gen_movi_i64(cpu_gpr[rD(ctx->opcode)], -1); - } else { - translator_io_start(&ctx->base); - 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)]); - } - } + /* Stop translation, this gives other CPUs a chance to run */ + gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); } -#endif +#endif /* defined(TARGET_PPC64) */ /*** Integer rotate ***/ @@ -2912,7 +2326,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)]); @@ -2995,7 +2409,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)]); @@ -3152,6 +2566,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(); @@ -3166,6 +2581,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)) @@ -3360,7 +2791,7 @@ static void gen_lmw(DisasContext *ctx) t0 = tcg_temp_new(); t1 = tcg_constant_i32(rD(ctx->opcode)); gen_addr_imm_index(ctx, t0, 0); - gen_helper_lmw(cpu_env, t0, t1); + gen_helper_lmw(tcg_env, t0, t1); } /* stmw */ @@ -3377,7 +2808,7 @@ static void gen_stmw(DisasContext *ctx) t0 = tcg_temp_new(); t1 = tcg_constant_i32(rS(ctx->opcode)); gen_addr_imm_index(ctx, t0, 0); - gen_helper_stmw(cpu_env, t0, t1); + gen_helper_stmw(tcg_env, t0, t1); } /*** Integer load and store strings ***/ @@ -3415,7 +2846,7 @@ static void gen_lswi(DisasContext *ctx) gen_addr_register(ctx, t0); t1 = tcg_constant_i32(nb); t2 = tcg_constant_i32(start); - gen_helper_lsw(cpu_env, t0, t1, t2); + gen_helper_lsw(tcg_env, t0, t1, t2); } /* lswx */ @@ -3434,7 +2865,7 @@ static void gen_lswx(DisasContext *ctx) t1 = tcg_constant_i32(rD(ctx->opcode)); t2 = tcg_constant_i32(rA(ctx->opcode)); t3 = tcg_constant_i32(rB(ctx->opcode)); - gen_helper_lswx(cpu_env, t0, t1, t2, t3); + gen_helper_lswx(tcg_env, t0, t1, t2, t3); } /* stswi */ @@ -3456,7 +2887,7 @@ static void gen_stswi(DisasContext *ctx) } t1 = tcg_constant_i32(nb); t2 = tcg_constant_i32(rS(ctx->opcode)); - gen_helper_stsw(cpu_env, t0, t1, t2); + gen_helper_stsw(tcg_env, t0, t1, t2); } /* stswx */ @@ -3476,60 +2907,7 @@ static void gen_stswx(DisasContext *ctx) tcg_gen_trunc_tl_i32(t1, cpu_xer); tcg_gen_andi_i32(t1, t1, 0x7F); t2 = tcg_constant_i32(rS(ctx->opcode)); - gen_helper_stsw(cpu_env, t0, t1, 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); + gen_helper_stsw(tcg_env, t0, t1, t2); } #if !defined(CONFIG_USER_ONLY) @@ -3543,14 +2921,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); + 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) { } @@ -3570,8 +2955,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)]; @@ -3579,7 +2962,7 @@ 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); @@ -3592,9 +2975,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) @@ -3604,7 +2987,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); @@ -3614,8 +2997,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_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) @@ -3710,7 +3093,7 @@ static void gen_ld_atomic(DisasContext *ctx, MemOp memop) 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; } } @@ -3766,7 +3149,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(); @@ -3776,7 +3159,7 @@ 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); @@ -3839,15 +3222,15 @@ 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) @@ -3876,8 +3259,8 @@ static void gen_lqarx(DisasContext *ctx) tcg_gen_mov_tl(cpu_reserve, EA); tcg_gen_movi_tl(cpu_reserve_length, 16); - tcg_gen_st_tl(hi, cpu_env, offsetof(CPUPPCState, reserve_val)); - tcg_gen_st_tl(lo, cpu_env, offsetof(CPUPPCState, reserve_val2)); + tcg_gen_st_tl(hi, tcg_env, offsetof(CPUPPCState, reserve_val)); + tcg_gen_st_tl(lo, tcg_env, offsetof(CPUPPCState, reserve_val2)); } /* stqcx. */ @@ -3933,31 +3316,6 @@ static void gen_stqcx_(DisasContext *ctx) } #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) { @@ -4011,7 +3369,7 @@ static void gen_wait(DisasContext *ctx) */ if (wc == 0) { TCGv_i32 t0 = tcg_constant_i32(1); - tcg_gen_st_i32(t0, cpu_env, + tcg_gen_st_i32(t0, tcg_env, -offsetof(PowerPCCPU, env) + offsetof(CPUState, halted)); /* Stop translation, as the CPU is supposed to sleep from now */ gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); @@ -4058,7 +3416,7 @@ static void gen_doze(DisasContext *ctx) CHK_HV(ctx); translator_io_start(&ctx->base); t = tcg_constant_i32(PPC_PM_DOZE); - gen_helper_pminsn(cpu_env, t); + 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) */ @@ -4074,7 +3432,7 @@ static void gen_nap(DisasContext *ctx) CHK_HV(ctx); translator_io_start(&ctx->base); t = tcg_constant_i32(PPC_PM_NAP); - gen_helper_pminsn(cpu_env, t); + 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) */ @@ -4090,7 +3448,7 @@ static void gen_stop(DisasContext *ctx) CHK_HV(ctx); translator_io_start(&ctx->base); t = tcg_constant_i32(PPC_PM_STOP); - gen_helper_pminsn(cpu_env, t); + 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) */ @@ -4106,7 +3464,7 @@ static void gen_sleep(DisasContext *ctx) CHK_HV(ctx); translator_io_start(&ctx->base); t = tcg_constant_i32(PPC_PM_SLEEP); - gen_helper_pminsn(cpu_env, t); + 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) */ @@ -4122,19 +3480,90 @@ static void gen_rvwinkle(DisasContext *ctx) CHK_HV(ctx); translator_io_start(&ctx->base); t = tcg_constant_i32(PPC_PM_RVWINKLE); - gen_helper_pminsn(cpu_env, t); + 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 } @@ -4172,12 +3601,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); } 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 /* @@ -4195,7 +3624,6 @@ static void pmu_count_insns(DisasContext *ctx) #else static void pmu_count_insns(DisasContext *ctx) { - return; } #endif /* #if defined(TARGET_PPC64) */ @@ -4264,8 +3692,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; } @@ -4280,6 +3710,7 @@ 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_new(); @@ -4290,11 +3721,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) { @@ -4345,6 +3781,7 @@ static void gen_bcond(DisasContext *ctx, int type) tcg_gen_brcondi_tl(TCG_COND_EQ, temp, 0, l1); } } + bhrb_type |= BHRB_TYPE_COND; } if ((bo & 0x10) == 0) { /* Test CR */ @@ -4359,8 +3796,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); } + 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)) { @@ -4476,8 +3916,8 @@ static void gen_rfi(DisasContext *ctx) /* Restore CPU state */ CHK_SV(ctx); translator_io_start(&ctx->base); - gen_update_cfar(ctx, ctx->cia); - gen_helper_rfi(cpu_env); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_NORECORD); + gen_helper_rfi(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif } @@ -4491,8 +3931,8 @@ static void gen_rfid(DisasContext *ctx) /* Restore CPU state */ CHK_SV(ctx); translator_io_start(&ctx->base); - gen_update_cfar(ctx, ctx->cia); - gen_helper_rfid(cpu_env); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_NORECORD); + gen_helper_rfid(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif } @@ -4506,8 +3946,8 @@ static void gen_rfscv(DisasContext *ctx) /* Restore CPU state */ CHK_SV(ctx); translator_io_start(&ctx->base); - gen_update_cfar(ctx, ctx->cia); - gen_helper_rfscv(cpu_env); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_NORECORD); + gen_helper_rfscv(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif } @@ -4521,7 +3961,7 @@ static void gen_hrfid(DisasContext *ctx) /* Restore CPU state */ CHK_HV(ctx); translator_io_start(&ctx->base); - gen_helper_hrfid(cpu_env); + gen_helper_hrfid(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif } @@ -4554,7 +3994,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; } @@ -4564,76 +4004,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_constant_i32(TO(ctx->opcode)); - gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], - t0); -} - -/* twi */ -static void gen_twi(DisasContext *ctx) -{ - TCGv t0; - TCGv_i32 t1; - - if (check_unconditional_trap(ctx)) { - return; - } - t0 = tcg_constant_tl(SIMM(ctx->opcode)); - t1 = tcg_constant_i32(TO(ctx->opcode)); - gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, t1); -} - -#if defined(TARGET_PPC64) -/* td */ -static void gen_td(DisasContext *ctx) -{ - TCGv_i32 t0; - - if (check_unconditional_trap(ctx)) { - return; - } - t0 = tcg_constant_i32(TO(ctx->opcode)); - gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], - t0); -} - -/* tdi */ -static void gen_tdi(DisasContext *ctx) -{ - TCGv t0; - TCGv_i32 t1; - - if (check_unconditional_trap(ctx)) { - return; - } - t0 = tcg_constant_tl(SIMM(ctx->opcode)); - t1 = tcg_constant_i32(TO(ctx->opcode)); - gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, t1); -} -#endif - /*** Processor control ***/ /* mcrxr */ @@ -4856,7 +4240,7 @@ 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; @@ -4895,7 +4279,7 @@ 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; @@ -5101,27 +4485,29 @@ static void gen_dcblc(DisasContext *ctx) /* 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_constant_i32(ctx->opcode & 0x03FF000); gen_addr_reg_index(ctx, tcgv_addr); - gen_helper_dcbz(cpu_env, tcgv_addr, 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_constant_i32(ctx->opcode & 0x03FF000); gen_addr_reg_index(ctx, tcgv_addr); - gen_helper_dcbzep(cpu_env, tcgv_addr, tcgv_op); + gen_helper_dcbz(tcg_env, tcgv_addr, tcg_constant_i32(PPC_TLB_EPID_STORE)); } /* dst / dstt */ @@ -5158,7 +4544,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); + gen_helper_icbi(tcg_env, t0); } /* icbiep */ @@ -5168,7 +4554,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); + gen_helper_icbiep(tcg_env, t0); } /* Optional: */ @@ -5195,7 +4581,7 @@ static void gen_mfsr(DisasContext *ctx) CHK_SV(ctx); t0 = tcg_constant_tl(SR(ctx->opcode)); - gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], tcg_env, t0); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5210,7 +4596,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); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], tcg_env, t0); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5224,7 +4610,7 @@ static void gen_mtsr(DisasContext *ctx) CHK_SV(ctx); t0 = tcg_constant_tl(SR(ctx->opcode)); - gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); + gen_helper_store_sr(tcg_env, t0, cpu_gpr[rS(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5239,7 +4625,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)]); + gen_helper_store_sr(tcg_env, t0, cpu_gpr[rD(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5256,7 +4642,7 @@ static void gen_mfsr_64b(DisasContext *ctx) CHK_SV(ctx); t0 = tcg_constant_tl(SR(ctx->opcode)); - gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], tcg_env, t0); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5271,7 +4657,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); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], tcg_env, t0); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5285,7 +4671,7 @@ static void gen_mtsr_64b(DisasContext *ctx) CHK_SV(ctx); t0 = tcg_constant_tl(SR(ctx->opcode)); - gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); + gen_helper_store_sr(tcg_env, t0, cpu_gpr[rS(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5300,7 +4686,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)]); + gen_helper_store_sr(tcg_env, t0, cpu_gpr[rS(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5317,7 +4703,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) */ } @@ -5377,7 +4763,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) */ } @@ -5388,7 +4774,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) */ } @@ -5412,7 +4798,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)]); + gen_helper_tlbiva(tcg_env, cpu_gpr[rB(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5639,7 +5025,7 @@ static void gen_mfdcr(DisasContext *ctx) CHK_SV(ctx); dcrn = tcg_constant_tl(SPR(ctx->opcode)); - gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, dcrn); + gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], tcg_env, dcrn); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5653,7 +5039,7 @@ static void gen_mtdcr(DisasContext *ctx) CHK_SV(ctx); dcrn = tcg_constant_tl(SPR(ctx->opcode)); - gen_helper_store_dcr(cpu_env, dcrn, cpu_gpr[rS(ctx->opcode)]); + gen_helper_store_dcr(tcg_env, dcrn, cpu_gpr[rS(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5665,7 +5051,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) */ @@ -5679,7 +5065,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) */ @@ -5742,7 +5128,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) */ } @@ -5754,7 +5140,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) */ } @@ -5769,7 +5155,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) */ } @@ -5782,7 +5168,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) */ } @@ -5798,11 +5184,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: @@ -5823,7 +5209,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); + 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); @@ -5844,11 +5230,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: @@ -5874,7 +5260,7 @@ static void gen_tlbre_440(DisasContext *ctx) case 2: { TCGv_i32 t0 = tcg_constant_i32(rB(ctx->opcode)); - gen_helper_440_tlbre(cpu_gpr[rD(ctx->opcode)], cpu_env, + gen_helper_440_tlbre(cpu_gpr[rD(ctx->opcode)], tcg_env, t0, cpu_gpr[rA(ctx->opcode)]); } break; @@ -5896,7 +5282,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); + 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); @@ -5920,7 +5306,7 @@ static void gen_tlbwe_440(DisasContext *ctx) case 2: { TCGv_i32 t0 = tcg_constant_i32(rB(ctx->opcode)); - gen_helper_440_tlbwe(cpu_env, t0, cpu_gpr[rA(ctx->opcode)], + gen_helper_440_tlbwe(tcg_env, t0, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); } break; @@ -5940,7 +5326,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) */ } @@ -5959,7 +5345,7 @@ static void gen_tlbsx_booke206(DisasContext *ctx) } else { t0 = cpu_gpr[rB(ctx->opcode)]; } - gen_helper_booke206_tlbsx(cpu_env, t0); + gen_helper_booke206_tlbsx(tcg_env, t0); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5970,7 +5356,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) */ } @@ -5984,7 +5370,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); + gen_helper_booke206_tlbivax(tcg_env, t0); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6001,13 +5387,13 @@ 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); @@ -6062,27 +5448,10 @@ static void gen_wrteei(DisasContext *ctx) static void gen_dlmzb(DisasContext *ctx) { TCGv_i32 t0 = tcg_constant_i32(Rc(ctx->opcode)); - gen_helper_dlmzb(cpu_gpr[rA(ctx->opcode)], cpu_env, + gen_helper_dlmzb(cpu_gpr[rA(ctx->opcode)], tcg_env, cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], 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 */ static void gen_icbt_440(DisasContext *ctx) { @@ -6093,43 +5462,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)]); -} - -/* 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); -} -#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) \ @@ -6225,12 +5564,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, @@ -6238,17 +5577,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)); } /* @@ -6420,6 +5749,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) { @@ -6480,49 +5813,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), @@ -6551,7 +5844,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), @@ -6569,7 +5861,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), @@ -6598,12 +5889,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), @@ -6692,115 +5977,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 @@ -7266,8 +6448,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"); @@ -7320,7 +6500,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; @@ -7336,8 +6516,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; @@ -7354,6 +6536,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) { @@ -7384,7 +6567,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; @@ -7398,6 +6581,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) { @@ -7501,24 +6685,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 ppc_translate_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 00000000000..3a19bc4555d --- /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 f9931b9d732..9ade0c659a8 100644 --- a/target/ppc/translate/branch-impl.c.inc +++ b/target/ppc/translate/branch-impl.c.inc @@ -17,8 +17,8 @@ static bool trans_RFEBB(DisasContext *ctx, arg_XL_s *arg) REQUIRE_INSNS_FLAGS2(ctx, ISA207S); translator_io_start(&ctx->base); - gen_update_cfar(ctx, ctx->cia); - gen_helper_rfebb(cpu_env, cpu_gpr[arg->s]); + 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 62911e04c7c..371076582b5 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,7 +16,7 @@ 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); \ } \ @@ -32,7 +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_env, ra, rb); \ return true; \ } @@ -44,7 +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_env, tcg_constant_i32(a->uim), rb);\ return true; \ } @@ -56,7 +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_env, ra, tcg_constant_i32(a->dm)); \ return true; \ } @@ -68,7 +68,7 @@ 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)) { \ @@ -86,7 +86,7 @@ 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); \ @@ -102,7 +102,7 @@ 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); \ } \ @@ -117,7 +117,7 @@ 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); \ @@ -188,7 +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); + gen_helper_DCFFIXQQ(tcg_env, rt, rb); return true; } @@ -203,7 +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); + 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 7ff7e1ec46a..fa0191e8664 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -289,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 */ @@ -325,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); @@ -359,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); @@ -447,6 +1223,27 @@ 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 target_ulong carry_bits = (target_ulong)-1 / 0xf; @@ -517,7 +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]); + 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 874774eadea..a66b83398b6 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) @@ -30,96 +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); \ - } \ +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); \ - } \ +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); \ - } \ +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) \ @@ -134,75 +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); \ } \ } -#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); \ - } \ -} - -/* 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); } + return true; } static bool trans_FSEL(DisasContext *ctx, arg_A *a) @@ -228,10 +163,6 @@ static bool trans_FSEL(DisasContext *ctx, arg_A *a) 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)) { @@ -245,7 +176,7 @@ 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)) { @@ -254,19 +185,33 @@ static bool do_helper_fsqrt(DisasContext *ctx, arg_A_tb *a, 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); @@ -304,35 +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); + 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); + get_fpr(t0, a->rb); + gen_helper_FTSQRT(cpu_crf[a->bf], t0); + return true; } - - /*** Floating-Point compare ***/ /* fcmpo */ @@ -351,8 +291,8 @@ static void gen_fcmpo(DisasContext *ctx) 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); - gen_helper_float_check_status(cpu_env); + gen_helper_fcmpo(tcg_env, t0, t1, crf); + gen_helper_float_check_status(tcg_env); } /* fcmpu */ @@ -371,8 +311,8 @@ static void gen_fcmpu(DisasContext *ctx) 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); - gen_helper_float_check_status(cpu_env); + gen_helper_fcmpu(tcg_env, t0, t1, crf); + gen_helper_float_check_status(tcg_env); } /*** Floating-point move ***/ @@ -542,7 +482,7 @@ static void gen_mcrfs(DisasContext *ctx) ~((0xF << shift) & FP_EX_CLEAR_BITS)); /* FEX and VX need to be updated, so don't set fpscr directly */ tmask = tcg_constant_i32(1 << nibble); - gen_helper_store_fpscr(cpu_env, tnew_fpscr, tmask); + gen_helper_store_fpscr(tcg_env, tnew_fpscr, tmask); } static TCGv_i64 place_from_fpscr(int rt, uint64_t mask) @@ -565,7 +505,7 @@ 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); } static bool trans_MFFS_ISA207(DisasContext *ctx, arg_X_t_rc *a) @@ -691,7 +631,7 @@ static void gen_mtfsb0(DisasContext *ctx) crb = 31 - crbD(ctx->opcode); gen_reset_fpstatus(); if (likely(crb != FPSCR_FEX && crb != FPSCR_VX)) { - gen_helper_fpscr_clrbit(cpu_env, tcg_constant_i32(crb)); + 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); @@ -711,14 +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)) { - gen_helper_fpscr_setbit(cpu_env, tcg_constant_i32(crb)); + 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 */ @@ -748,13 +688,13 @@ static void gen_mtfsf(DisasContext *ctx) } t1 = tcg_temp_new_i64(); get_fpr(t1, rB(ctx->opcode)); - gen_helper_store_fpscr(cpu_env, t1, 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); + gen_helper_fpscr_check_status(tcg_env); } /* mtfsfi */ @@ -777,13 +717,13 @@ static void gen_mtfsfi(DisasContext *ctx) sh = (8 * w) + 7 - bf; t0 = tcg_constant_i64(((uint64_t)FPIMM(ctx->opcode)) << (4 * sh)); t1 = tcg_constant_i32(1 << sh); - gen_helper_store_fpscr(cpu_env, t0, t1); + 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) @@ -1111,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 d4c6c4bed13..cef4b5dfcbe 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 00000000000..cbf82b1ea0a --- /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 cc7a50d579e..8abbb89630f 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 f4a858487d4..454dac823e2 100644 --- a/target/ppc/translate/spe-impl.c.inc +++ b/target/ppc/translate/spe-impl.c.inc @@ -22,7 +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_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)]); @@ -457,7 +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_gen_st_i64(tmp, tcg_env, offsetof(CPUPPCState, spe_acc)); } static inline void gen_evmwumiaa(DisasContext *ctx) @@ -479,13 +479,13 @@ 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); @@ -529,7 +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_gen_st_i64(tmp, tcg_env, offsetof(CPUPPCState, spe_acc)); } static inline void gen_evmwsmiaa(DisasContext *ctx) @@ -551,13 +551,13 @@ 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); @@ -878,7 +878,7 @@ 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); \ } #define GEN_SPEFPUOP_CONV_32_64(name) \ @@ -893,7 +893,7 @@ 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); \ } #define GEN_SPEFPUOP_CONV_64_32(name) \ @@ -908,7 +908,7 @@ 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); \ } #define GEN_SPEFPUOP_CONV_64_64(name) \ @@ -921,7 +921,7 @@ 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); \ } #define GEN_SPEFPUOP_ARITH2_32_32(name) \ @@ -931,7 +931,7 @@ 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); \ } #define GEN_SPEFPUOP_ARITH2_64_64(name) \ @@ -946,7 +946,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(t0, cpu_env, t0, t1); \ + gen_helper_##name(t0, tcg_env, t0, t1); \ gen_store_gpr64(rD(ctx->opcode), t0); \ } #define GEN_SPEFPUOP_COMP_32(name) \ @@ -957,7 +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); \ + 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) \ @@ -971,7 +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); \ + 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 faa7b04bbc1..b8b44546634 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,7 +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); + gen_helper_tlbie(tcg_env, t0); #if defined(TARGET_PPC64) /* @@ -219,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) { @@ -236,9 +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_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 6d7669aabd4..92d6e8c6032 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -10,129 +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); \ - } \ -} - -#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); \ - } \ -} - -#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); \ - } +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_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); \ - } +static inline void set_avr64(int regno, TCGv_i64 src, bool high) +{ + tcg_gen_st_i64(src, tcg_env, avr64_offset(regno, high)); +} + +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; +} -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); +/* 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); +} -GEN_VR_LVE(bx, 0x07, 0x00, 1); -GEN_VR_LVE(hx, 0x07, 0x01, 2); -GEN_VR_LVE(wx, 0x07, 0x02, 4); +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; +} -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); +/* 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; +} + +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_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) { @@ -146,7 +117,7 @@ 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); } @@ -167,8 +138,8 @@ 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_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) @@ -242,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) \ { \ @@ -287,7 +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); \ + gen_helper_##name(tcg_env, rd, ra, rb); \ } #define GEN_VXFORM3(name, opc2, opc3) \ @@ -389,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); @@ -460,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); @@ -478,13 +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); + set_avr64(a->rt, result, false); + return true; } /* @@ -494,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); @@ -513,13 +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); + set_avr64(a->rt, result, false); + return true; } /* @@ -689,10 +639,10 @@ 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); } } @@ -759,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), @@ -1013,8 +994,8 @@ static bool do_vector_rotl_quad(DisasContext *ctx, arg_VX *a, bool mask, { TCGv_i64 ah, al, vrb, n, t0, t1, zero = tcg_constant_i64(0); - REQUIRE_VECTOR(ctx); REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); ah = tcg_temp_new_i64(); al = tcg_temp_new_i64(); @@ -1074,58 +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); \ -} \ -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); @@ -1158,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) @@ -1174,7 +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); \ + gen_helper_##opname(tcg_env, rd, ra, rb); \ } #define GEN_VXRFORM(name, opc2, opc3) \ @@ -1183,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. */ @@ -1478,7 +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); \ + gen_helper_##name(tcg_env, rd, rb); \ } #define GEN_VXFORM_NOA_2(name, opc2, opc3, opc4) \ @@ -1526,7 +1453,7 @@ 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); @@ -1625,7 +1552,7 @@ static void glue(gen_, name)(DisasContext *ctx) \ 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); \ + gen_helper_##name(tcg_env, rd, rb, uimm); \ } #define GEN_VXFORM_UIMM_SPLAT(name, opc2, opc3, splat_max) \ @@ -1813,7 +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); + gen_helper(tcg_env, vrt, vra, vrb, rc); return true; } @@ -1841,7 +1768,7 @@ 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); + gen_helper(tcg_env, t, rb, idx); return true; } @@ -2349,9 +2276,9 @@ 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); \ } \ } @@ -2437,7 +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); + gen_helper(tcg_env, vrt, vra, vrb, vrc); return true; } @@ -2670,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) { @@ -2966,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)) { @@ -3365,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 33fec8aca4c..e28958a126a 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 0f5b0056f17..00ad57c6282 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -2,88 +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 */ \ +static bool do_lxs(DisasContext *ctx, arg_X *a, + void (*op)(DisasContext *, TCGv_i64, TCGv)) +{ + TCGv EA; + TCGv_i64 t0; + REQUIRE_VSX(ctx); + t0 = tcg_temp_new_i64(); + gen_set_access_type(ctx, ACCESS_INT); + 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; } -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) +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 void gen_lxvd2x(DisasContext *ctx) +static bool trans_LXVD2X(DisasContext *ctx, arg_LXVD2X *a) { TCGv EA; TCGv_i64 t0; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + + REQUIRE_INSNS_FLAGS2(ctx, VSX); + 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); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); gen_qemu_ld64_i64(ctx, t0, EA); - set_cpu_vsr(xT(ctx->opcode), t0, true); + set_cpu_vsr(a->rt, t0, true); tcg_gen_addi_tl(EA, EA, 8); gen_qemu_ld64_i64(ctx, t0, EA); - set_cpu_vsr(xT(ctx->opcode), t0, false); + set_cpu_vsr(a->rt, t0, false); + return true; } -static void gen_lxvw4x(DisasContext *ctx) +static bool trans_LXVW4X(DisasContext *ctx, arg_LXVW4X *a) { TCGv EA; - TCGv_i64 xth; - TCGv_i64 xtl; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + TCGv_i64 xth, xtl; + + REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); + 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(); @@ -100,55 +103,45 @@ static void gen_lxvw4x(DisasContext *ctx) 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); + 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; - } + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + if (a->rt < 32) { + REQUIRE_VSX(ctx); } else { - if (unlikely(!ctx->altivec_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VPU); - return; - } + REQUIRE_VECTOR(ctx); } 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_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_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); 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_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, @@ -187,145 +180,166 @@ static void gen_bswap32x4(TCGv_i64 outh, TCGv_i64 outl, tcg_gen_deposit_i64(outl, outl, lo, 32, 32); } -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_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); - 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); + 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; + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); + + data = tcg_temp_new_i128(); + gen_set_access_type(ctx, ACCESS_INT); + 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; +} + +#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); } - xth = tcg_temp_new_i64(); - xtl = tcg_temp_new_i64(); + xt = gen_vsr_ptr(a->rt); 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); + EA = do_ea_calc_ra(ctx, a->ra); + helper(tcg_env, EA, xt, cpu_gpr[a->rb]); + return true; } +#endif -#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)]); \ -} - -VSX_VECTOR_LOAD_STORE_LENGTH(lxvl) -VSX_VECTOR_LOAD_STORE_LENGTH(lxvll) -VSX_VECTOR_LOAD_STORE_LENGTH(stxvl) -VSX_VECTOR_LOAD_STORE_LENGTH(stxvll) +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; +} -#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); \ +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(stxsdx, st64_i64) +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; +} -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_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 void gen_stxvd2x(DisasContext *ctx) +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); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + get_cpu_vsr(t0, a->rt, true); + op(ctx, t0, EA); + return true; +} + +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 t0; + + REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); + + 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, xS(ctx->opcode), false); + get_cpu_vsr(t0, a->rt, false); gen_qemu_st64_i64(ctx, t0, EA); + return true; } -static void gen_stxvw4x(DisasContext *ctx) +static bool trans_STXVW4X(DisasContext *ctx, arg_STXVW4X *a) { TCGv EA; - TCGv_i64 xsh; - TCGv_i64 xsl; + TCGv_i64 xsh, xsl; + + REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); - 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(); @@ -342,25 +356,23 @@ static void gen_stxvw4x(DisasContext *ctx) tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEUQ); } + 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_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); - 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(); @@ -374,28 +386,24 @@ static void gen_stxvh8x(DisasContext *ctx) tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEUQ); } + 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_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); + + 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); + 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) @@ -788,34 +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); \ - } \ +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) { @@ -829,7 +831,7 @@ static bool trans_XSCVQPDP(DisasContext *ctx, arg_X_tb_rc *a) xt = gen_avr_ptr(a->rt); xb = gen_avr_ptr(a->rb); - gen_helper_XSCVQPDP(cpu_env, ro, xt, xb); + gen_helper_XSCVQPDP(tcg_env, ro, xt, xb); return true; } @@ -843,7 +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); + gen_helper(tcg_env, xt, xb); return true; } @@ -861,21 +863,7 @@ static void gen_##name(DisasContext *ctx) \ return; \ } \ opc = tcg_constant_i32(ctx->opcode); \ - gen_helper_##name(cpu_env, 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); \ + gen_helper_##name(tcg_env, opc); \ } #define GEN_VSX_HELPER_X2(name, op1, op2, inval, type) \ @@ -888,7 +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); \ + gen_helper_##name(tcg_env, xt, xb); \ } #define GEN_VSX_HELPER_X2_AB(name, op1, op2, inval, type) \ @@ -903,7 +891,7 @@ static void gen_##name(DisasContext *ctx) \ 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); \ + gen_helper_##name(tcg_env, opc, xa, xb); \ } #define GEN_VSX_HELPER_X1(name, op1, op2, inval, type) \ @@ -917,7 +905,7 @@ static void gen_##name(DisasContext *ctx) \ } \ opc = tcg_constant_i32(ctx->opcode); \ xb = gen_vsr_ptr(xB(ctx->opcode)); \ - gen_helper_##name(cpu_env, opc, xb); \ + gen_helper_##name(tcg_env, opc, xb); \ } #define GEN_VSX_HELPER_R3(name, op1, op2, inval, type) \ @@ -933,7 +921,7 @@ static void gen_##name(DisasContext *ctx) \ 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); \ + gen_helper_##name(tcg_env, opc, xt, xa, xb); \ } #define GEN_VSX_HELPER_R2(name, op1, op2, inval, type) \ @@ -948,7 +936,7 @@ static void gen_##name(DisasContext *ctx) \ 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); \ + gen_helper_##name(tcg_env, opc, xt, xb); \ } #define GEN_VSX_HELPER_R2_AB(name, op1, op2, inval, type) \ @@ -963,7 +951,7 @@ static void gen_##name(DisasContext *ctx) \ 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); \ + gen_helper_##name(tcg_env, opc, xa, xb); \ } #define GEN_VSX_HELPER_XT_XB_ENV(name, op1, op2, inval, type) \ @@ -978,17 +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); \ } -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) @@ -1001,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) @@ -1191,7 +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); + gen_helper(tcg_env, tcg_constant_i32(a->bf), tcg_constant_i32(a->uim), xb); return true; } @@ -1233,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) @@ -1269,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) @@ -1420,7 +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); + gen_helper(tcg_env, t, s1, s2, s3); return true; } @@ -1500,7 +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); \ + gen_helper_##name(tcg_env, xt, s1, s2, s3); \ } GEN_VSX_HELPER_VSX_MADD(xvmadddp, 0x04, 0x0C, 0x0D, 0, PPC2_VSX) @@ -1609,26 +1575,24 @@ static void gen_xxbrw(DisasContext *ctx) set_cpu_vsr(xT(ctx->opcode), xtl, false); } -#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) \ @@ -1728,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; @@ -2215,13 +2179,13 @@ 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); @@ -2235,32 +2199,20 @@ 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); } } return true; @@ -2268,7 +2220,7 @@ static bool do_lstxv(DisasContext *ctx, int ra, TCGv displ, 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); @@ -2292,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); @@ -2712,25 +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); + 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)) @@ -2741,7 +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); + helper(tcg_env, rt, ra, rb); return true; } @@ -2770,7 +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); + gen_helper_XVCVSPBF16(tcg_env, xt, xb); return true; } @@ -2833,7 +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)); + helper(tcg_env, xa, xb, xt, tcg_constant_i32(mask)); return true; } @@ -2910,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 a3ba094d628..e553b5b8faa 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 7ff76f7a061..ae210eb8474 100644 --- a/target/ppc/user_only_helper.c +++ b/target/ppc/user_only_helper.c @@ -20,15 +20,13 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "internal.h" 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 b9e5932f13f..11bc09b4146 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 434c8a3dbb7..12b68799070 100644 --- a/target/riscv/arch_dump.c +++ b/target/riscv/arch_dump.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "elf.h" -#include "sysemu/dump.h" +#include "system/dump.h" /* struct user_regs_struct from arch/riscv/include/uapi/asm/ptrace.h */ struct riscv64_user_regs { diff --git a/target/riscv/bitmanip_helper.c b/target/riscv/bitmanip_helper.c index b99c4a39a1f..e9c8d7f7780 100644 --- a/target/riscv/bitmanip_helper.c +++ b/target/riscv/bitmanip_helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" +#include "exec/target_long.h" #include "exec/helper-proto.h" #include "tcg/tcg.h" diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h index b2a9396dec9..cfdc67c258c 100644 --- a/target/riscv/cpu-param.h +++ b/target/riscv/cpu-param.h @@ -2,22 +2,28 @@ * 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 #define RISCV_CPU_PARAM_H #if defined(TARGET_RISCV64) -# define TARGET_LONG_BITS 64 # define TARGET_PHYS_ADDR_SPACE_BITS 56 /* 44-bit PPN */ # define TARGET_VIRT_ADDR_SPACE_BITS 48 /* sv48 */ #elif defined(TARGET_RISCV32) -# define TARGET_LONG_BITS 32 # define TARGET_PHYS_ADDR_SPACE_BITS 34 /* 22-bit PPN */ # define TARGET_VIRT_ADDR_SPACE_BITS 32 /* sv32 */ #endif #define TARGET_PAGE_BITS 12 /* 4 KiB Pages */ + +/* + * RISC-V-specific extra insn start words: + * 1: Original instruction opcode + * 2: more information about instruction + */ +#define TARGET_INSN_START_EXTRA_WORDS 2 + /* * The current MMU Modes are: * - U mode 0b000 diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index 04af50983e6..1ee05eb393d 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -1,5 +1,5 @@ /* - * QEMU RISC-V CPU QOM header + * QEMU RISC-V CPU QOM header (target agnostic) * * Copyright (c) 2023 Ventana Micro Systems Inc. * @@ -20,52 +20,43 @@ #define RISCV_CPU_QOM_H #include "hw/core/cpu.h" -#include "qom/object.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 CPU_RESOLVING_TYPE TYPE_RISCV_CPU -#define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any") +#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_RVA23U64 RISCV_CPU_TYPE_NAME("rva23u64") +#define TYPE_RISCV_CPU_RVA23S64 RISCV_CPU_TYPE_NAME("rva23s64") #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_E RISCV_CPU_TYPE_NAME("sifive-e") #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_U RISCV_CPU_TYPE_NAME("sifive-u") #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_TT_ASCALON RISCV_CPU_TYPE_NAME("tt-ascalon") +#define TYPE_RISCV_CPU_XIANGSHAN_NANHU RISCV_CPU_TYPE_NAME("xiangshan-nanhu") #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 - -typedef struct CPUArchState CPURISCVState; - OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU) -/** - * RISCVCPUClass: - * @parent_realize: The parent class' realize handler. - * @parent_phases: The parent class' reset phase handlers. - * - * A RISCV CPU model. - */ -struct RISCVCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; #endif /* RISCV_CPU_QOM_H */ diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index f227c7664e9..629ac37501e 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -23,32 +23,25 @@ #include "qemu/log.h" #include "cpu.h" #include "cpu_vendorid.h" -#include "pmu.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/kvm.h" -#include "sysemu/tcg.h" -#include "kvm_riscv.h" +#include "system/device_tree.h" +#include "system/kvm.h" +#include "system/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[] = "IEMAFDQCPVH"; - -struct isa_ext_data { - const char *name; - int min_version; - int ext_enable_offset; -}; - -#define ISA_EXT_DATA_ENTRY(_name, _min_ver, _prop) \ - {#_name, _min_ver, offsetof(struct RISCVCPUConfig, _prop)} +static const char riscv_single_letter_exts[] = "IEMAFDQCBPVH"; +const uint32_t misa_bits[] = {RVI, RVE, RVM, RVA, RVF, RVD, RVV, + RVC, RVS, RVU, RVH, RVG, RVB, 0}; /* * From vector_helper.c @@ -61,6 +54,35 @@ struct isa_ext_data { #define BYTE(x) (x) #endif +bool riscv_cpu_is_32bit(RISCVCPU *cpu) +{ + return riscv_cpu_mxl(&cpu->env) == MXL_RV32; +} + +/* Hash that stores general user set numeric options */ +static GHashTable *general_user_opts; + +static void cpu_option_add_user_setting(const char *optname, uint32_t value) +{ + g_hash_table_insert(general_user_opts, (gpointer)optname, + GUINT_TO_POINTER(value)); +} + +bool riscv_cpu_option_set(const char *optname) +{ + return g_hash_table_contains(general_user_opts, optname); +} + +static void riscv_cpu_cfg_merge(RISCVCPUConfig *dest, const RISCVCPUConfig *src) +{ +#define BOOL_FIELD(x) dest->x |= src->x; +#define TYPED_FIELD(type, x, default_) if (src->x != default_) dest->x = src->x; +#include "cpu_cfg_fields.h.inc" +} + +#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 : @@ -81,15 +103,32 @@ struct isa_ext_data { * 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(zicbom, PRIV_VERSION_1_12_0, ext_icbom), - ISA_EXT_DATA_ENTRY(zicboz, PRIV_VERSION_1_12_0, ext_icboz), +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, ext_ziccrse), + 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(zicsr, PRIV_VERSION_1_10_0, ext_icsr), - ISA_EXT_DATA_ENTRY(zifencei, PRIV_VERSION_1_10_0, ext_ifencei), + 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), @@ -102,6 +141,7 @@ static const struct isa_ext_data isa_edata_arr[] = { 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), @@ -121,33 +161,75 @@ static const struct isa_ext_data isa_edata_arr[] = { 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(shcounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(sha, PRIV_VERSION_1_12_0, ext_sha), + ISA_EXT_DATA_ENTRY(shgatpa, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(shtvala, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(shvsatpa, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(shvstvala, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(shvstvecd, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia), - ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, epmp), + ISA_EXT_DATA_ENTRY(smcdeleg, PRIV_VERSION_1_13_0, ext_smcdeleg), + ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf), + ISA_EXT_DATA_ENTRY(smcsrind, PRIV_VERSION_1_13_0, ext_smcsrind), + ISA_EXT_DATA_ENTRY(smdbltrp, PRIV_VERSION_1_13_0, ext_smdbltrp), + ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp), + ISA_EXT_DATA_ENTRY(smrnmi, PRIV_VERSION_1_12_0, ext_smrnmi), + ISA_EXT_DATA_ENTRY(smmpm, PRIV_VERSION_1_13_0, ext_smmpm), + ISA_EXT_DATA_ENTRY(smnpm, PRIV_VERSION_1_13_0, ext_smnpm), 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(ssccfg, PRIV_VERSION_1_13_0, ext_ssccfg), + 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(sscsrind, PRIV_VERSION_1_12_0, ext_sscsrind), + ISA_EXT_DATA_ENTRY(ssdbltrp, PRIV_VERSION_1_13_0, ext_ssdbltrp), + ISA_EXT_DATA_ENTRY(ssnpm, PRIV_VERSION_1_13_0, ext_ssnpm), + ISA_EXT_DATA_ENTRY(sspm, PRIV_VERSION_1_13_0, ext_sspm), + ISA_EXT_DATA_ENTRY(ssstateen, PRIV_VERSION_1_12_0, ext_ssstateen), 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(ssu64xl, PRIV_VERSION_1_12_0, has_priv_1_12), + ISA_EXT_DATA_ENTRY(supm, PRIV_VERSION_1_13_0, ext_supm), + ISA_EXT_DATA_ENTRY(svade, PRIV_VERSION_1_11_0, ext_svade), + ISA_EXT_DATA_ENTRY(smctr, PRIV_VERSION_1_12_0, ext_smctr), + ISA_EXT_DATA_ENTRY(ssctr, PRIV_VERSION_1_12_0, ext_ssctr), 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(svukte, PRIV_VERSION_1_13_0, ext_svukte), + 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), @@ -160,24 +242,29 @@ static const struct isa_ext_data isa_edata_arr[] = { 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), + + { }, }; -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", @@ -229,7 +316,7 @@ static const char * const riscv_excp_names[] = { "load_page_fault", "reserved", "store_page_fault", - "reserved", + "double_trap", "reserved", "reserved", "reserved", @@ -258,8 +345,6 @@ static const char * const riscv_intr_names[] = { "reserved" }; -static void riscv_cpu_add_user_properties(Object *obj); - const char *riscv_cpu_get_trap_name(target_ulong cause, bool async) { if (async) { @@ -271,12 +356,16 @@ 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; } +int riscv_cpu_max_xlen(RISCVCPUClass *mcc) +{ + return 16 << mcc->def->misa_mxl_max; +} + #ifndef CONFIG_USER_ONLY static uint8_t satp_mode_from_str(const char *satp_mode_str) { @@ -307,7 +396,7 @@ static uint8_t satp_mode_from_str(const char *satp_mode_str) g_assert_not_reached(); } -uint8_t satp_mode_max_from_map(uint32_t map) +static uint8_t satp_mode_max_from_map(uint32_t map) { /* * 'map = 0' will make us return (31 - 32), which C will @@ -351,285 +440,54 @@ const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit) g_assert_not_reached(); } -static void set_satp_mode_max_supported(RISCVCPU *cpu, - uint8_t satp_mode) +static bool get_satp_mode_supported(RISCVCPU *cpu, uint16_t *supported) { - bool rv32 = riscv_cpu_mxl(&cpu->env) == MXL_RV32; + bool rv32 = riscv_cpu_is_32bit(cpu); const bool *valid_vm = rv32 ? valid_vm_1_10_32 : valid_vm_1_10_64; + int satp_mode = cpu->cfg.max_satp_mode; + + if (satp_mode == -1) { + return false; + } + *supported = 0; for (int i = 0; i <= satp_mode; ++i) { if (valid_vm[i]) { - cpu->cfg.satp_mode.supported |= (1 << i); + *supported |= (1 << i); } } + return true; } /* Set the satp mode to the max supported */ static void set_satp_mode_default_map(RISCVCPU *cpu) { - cpu->cfg.satp_mode.map = cpu->cfg.satp_mode.supported; -} -#endif - -static void riscv_any_cpu_init(Object *obj) -{ - RISCVCPU *cpu = RISCV_CPU(obj); - CPURISCVState *env = &cpu->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); -#endif - -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(RISCV_CPU(obj), - riscv_cpu_mxl(&RISCV_CPU(obj)->env) == MXL_RV32 ? - VM_1_10_SV32 : VM_1_10_SV57); -#endif - - env->priv_ver = PRIV_VERSION_LATEST; - - /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_ifencei = true; - cpu->cfg.ext_icsr = true; - cpu->cfg.mmu = true; - cpu->cfg.pmp = true; -} - -#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); - riscv_cpu_add_user_properties(obj); - /* Set latest version of privileged specification */ - env->priv_ver = PRIV_VERSION_LATEST; -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57); -#endif -} - -static void rv64_sifive_u_cpu_init(Object *obj) -{ - RISCVCPU *cpu = RISCV_CPU(obj); - CPURISCVState *env = &cpu->env; - set_misa(env, MXL_RV64, 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_ifencei = true; - cpu->cfg.ext_icsr = true; - cpu->cfg.mmu = true; - cpu->cfg.pmp = true; -} - -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); - 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_ifencei = true; - cpu->cfg.ext_icsr = 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); - - set_misa(env, MXL_RV64, RVG | RVC | RVS | RVU); - env->priv_ver = PRIV_VERSION_1_11_0; - - cpu->cfg.ext_zfa = true; - cpu->cfg.ext_zfh = true; - cpu->cfg.mmu = true; - cpu->cfg.ext_xtheadba = true; - cpu->cfg.ext_xtheadbb = true; - cpu->cfg.ext_xtheadbs = true; - cpu->cfg.ext_xtheadcmo = true; - cpu->cfg.ext_xtheadcondmov = true; - cpu->cfg.ext_xtheadfmemidx = true; - cpu->cfg.ext_xtheadmac = true; - cpu->cfg.ext_xtheadmemidx = true; - cpu->cfg.ext_xtheadmempair = true; - cpu->cfg.ext_xtheadsync = true; - - cpu->cfg.mvendorid = THEAD_VENDOR_ID; -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(cpu, VM_1_10_SV39); -#endif - - /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.pmp = true; -} - -static void rv64_veyron_v1_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - RISCVCPU *cpu = RISCV_CPU(obj); - - set_misa(env, MXL_RV64, RVG | RVC | RVS | RVU | RVH); - env->priv_ver = PRIV_VERSION_1_12_0; - - /* Enable ISA extensions */ - cpu->cfg.mmu = true; - cpu->cfg.ext_ifencei = true; - cpu->cfg.ext_icsr = true; - cpu->cfg.pmp = true; - cpu->cfg.ext_icbom = true; - cpu->cfg.cbom_blocksize = 64; - cpu->cfg.cboz_blocksize = 64; - cpu->cfg.ext_icboz = true; - cpu->cfg.ext_smaia = true; - cpu->cfg.ext_ssaia = true; - cpu->cfg.ext_sscofpmf = true; - cpu->cfg.ext_sstc = true; - cpu->cfg.ext_svinval = true; - cpu->cfg.ext_svnapot = true; - cpu->cfg.ext_svpbmt = true; - cpu->cfg.ext_smstateen = true; - cpu->cfg.ext_zba = true; - cpu->cfg.ext_zbb = true; - cpu->cfg.ext_zbc = true; - cpu->cfg.ext_zbs = true; - cpu->cfg.ext_XVentanaCondOps = true; - - cpu->cfg.mvendorid = VEYRON_V1_MVENDORID; - cpu->cfg.marchid = VEYRON_V1_MARCHID; - cpu->cfg.mimpid = VEYRON_V1_MIMPID; - -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(cpu, VM_1_10_SV48); -#endif -} - -static void rv128_base_cpu_init(Object *obj) -{ - 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); + /* + * Bare CPUs do not default to the max available. + * Users must set a valid satp_mode in the command + * line. Otherwise, leave the existing max_satp_mode + * in place. + */ + if (object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_BARE_CPU) != NULL) { + warn_report("No satp mode set. Defaulting to 'bare'"); + cpu->cfg.max_satp_mode = VM_1_10_MBARE; } - CPURISCVState *env = &RISCV_CPU(obj)->env; - /* We set this in the realise function */ - set_misa(env, MXL_RV128, 0); - riscv_cpu_add_user_properties(obj); - /* Set latest version of privileged specification */ - env->priv_ver = PRIV_VERSION_LATEST; -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57); -#endif -} -#else -static void rv32_base_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - /* We set this in the realise function */ - set_misa(env, MXL_RV32, 0); - riscv_cpu_add_user_properties(obj); - /* Set latest version of privileged specification */ - env->priv_ver = PRIV_VERSION_LATEST; -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV32); -#endif -} - -static void rv32_sifive_u_cpu_init(Object *obj) -{ - RISCVCPU *cpu = RISCV_CPU(obj); - CPURISCVState *env = &cpu->env; - set_misa(env, MXL_RV32, 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_ifencei = true; - cpu->cfg.ext_icsr = true; - cpu->cfg.mmu = true; - cpu->cfg.pmp = true; } - -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); - 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_ifencei = true; - cpu->cfg.ext_icsr = true; - cpu->cfg.pmp = true; -} - -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); - env->priv_ver = PRIV_VERSION_1_11_0; #ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(cpu, VM_1_10_MBARE); -#endif - cpu->cfg.epmp = true; - - /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_ifencei = true; - cpu->cfg.ext_icsr = true; - cpu->cfg.pmp = true; -} - -static void rv32_imafcu_nommu_cpu_init(Object *obj) +static void riscv_register_custom_csrs(RISCVCPU *cpu, const RISCVCSR *csr_list) { - CPURISCVState *env = &RISCV_CPU(obj)->env; - RISCVCPU *cpu = RISCV_CPU(obj); - - set_misa(env, MXL_RV32, 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 - - /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_ifencei = true; - cpu->cfg.ext_icsr = true; - cpu->cfg.pmp = true; + for (size_t i = 0; csr_list[i].csr_ops.name; i++) { + int csrno = csr_list[i].csrno; + const riscv_csr_operations *csr_ops = &csr_list[i].csr_ops; + if (!csr_list[i].insertion_test || csr_list[i].insertion_test(cpu)) { + riscv_set_csr_ops(csrno, csr_ops); + } + } } #endif -#if defined(CONFIG_KVM) -static void riscv_host_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; -#if defined(TARGET_RISCV32) - set_misa(env, MXL_RV32, 0); -#elif defined(TARGET_RISCV64) - set_misa(env, MXL_RV64, 0); -#endif - riscv_cpu_add_user_properties(obj); -} -#endif /* CONFIG_KVM */ - static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model) { ObjectClass *oc; @@ -641,13 +499,20 @@ 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); @@ -695,16 +560,9 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) CSR_MSCRATCH, CSR_SSCRATCH, CSR_SATP, - CSR_MMTE, - CSR_UPMBASE, - CSR_UPMMASK, - CSR_SPMBASE, - CSR_SPMMASK, - CSR_MPMBASE, - 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); @@ -729,6 +587,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]); @@ -747,7 +611,7 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) CSR_VTYPE, CSR_VLENB, }; - for (int i = 0; i < ARRAY_SIZE(dump_rvv_csrs); ++i) { + 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); @@ -761,7 +625,7 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) csr_ops[csrno].name, val); } } - uint16_t vlenb = cpu->cfg.vlen >> 3; + uint16_t vlenb = cpu->cfg.vlenb; for (i = 0; i < 32; i++) { qemu_fprintf(f, " %-8s ", riscv_rvv_regnames[i]); @@ -798,63 +662,22 @@ static vaddr riscv_cpu_get_pc(CPUState *cs) return env->pc; } -static void riscv_cpu_synchronize_from_tb(CPUState *cs, - const TranslationBlock *tb) -{ - if (!(tb_cflags(tb) & CF_PCREL)) { - RISCVCPU *cpu = RISCV_CPU(cs); - CPURISCVState *env = &cpu->env; - RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL); - - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); - - if (xl == MXL_RV32) { - env->pc = (int32_t) tb->pc; - } else { - env->pc = tb->pc; - } - } -} - -static bool riscv_cpu_has_work(CPUState *cs) -{ #ifndef CONFIG_USER_ONLY +bool riscv_cpu_has_work(CPUState *cs) +{ RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; /* * 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; -#else - return true; -#endif -} - -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]; + return riscv_cpu_all_pending(env) != 0 || + riscv_cpu_sirq_pending(env) != RISCV_EXCP_NONE || + riscv_cpu_vsirq_pending(env) != RISCV_EXCP_NONE; } +#endif /* !CONFIG_USER_ONLY */ -static void riscv_cpu_reset_hold(Object *obj) +static void riscv_cpu_reset_hold(Object *obj, ResetType type) { #ifndef CONFIG_USER_ONLY uint8_t iprio; @@ -862,14 +685,14 @@ static void riscv_cpu_reset_hold(Object *obj) #endif 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; if (mcc->parent_phases.hold) { - mcc->parent_phases.hold(obj); + mcc->parent_phases.hold(obj, type); } #ifndef CONFIG_USER_ONLY - env->misa_mxl = env->misa_mxl_max; + env->misa_mxl = mcc->def->misa_mxl_max; env->priv = PRV_M; env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV); if (env->misa_mxl > MXL_RV32) { @@ -889,6 +712,9 @@ static void riscv_cpu_reset_hold(Object *obj) env->mstatus_hs = set_field(env->mstatus_hs, MSTATUS64_UXL, env->misa_mxl); } + if (riscv_cpu_cfg(env)->ext_smdbltrp) { + env->mstatus = set_field(env->mstatus, MSTATUS_MDT, 1); + } } env->mcause = 0; env->miclaim = MIP_SGEIP; @@ -897,9 +723,9 @@ static void riscv_cpu_reset_hold(Object *obj) env->two_stage_lookup = false; env->menvcfg = (cpu->cfg.ext_svpbmt ? MENVCFG_PBMTE : 0) | - (cpu->cfg.ext_svadu ? MENVCFG_ADUE : 0); - env->henvcfg = (cpu->cfg.ext_svpbmt ? HENVCFG_PBMTE : 0) | - (cpu->cfg.ext_svadu ? HENVCFG_ADUE : 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++) { @@ -915,20 +741,54 @@ static void riscv_cpu_reset_hold(Object *obj) } i++; } - /* mmte is supposed to have pm.current hardwired to 1 */ - 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); + /* Default NaN value: sign bit clear, frac msb set */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); + env->vill = true; #ifndef CONFIG_USER_ONLY if (cpu->cfg.debug) { riscv_trigger_reset_hold(env); } + if (cpu->cfg.ext_smrnmi) { + env->rnmip = 0; + env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, false); + } + if (kvm_enabled()) { kvm_riscv_reset_vcpu(cpu); } @@ -941,6 +801,15 @@ static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info) CPURISCVState *env = &cpu->env; info->target_info = &cpu->cfg; + /* + * A couple of bits in MSTATUS set the endianness: + * - MSTATUS_UBE (User-mode), + * - MSTATUS_SBE (Supervisor-mode), + * - MSTATUS_MBE (Machine-mode) + * but we don't implement that yet. + */ + info->endian = BFD_ENDIAN_LITTLE; + switch (env->xl) { case MXL_RV32: info->print_insn = print_insn_riscv32; @@ -956,1087 +825,1800 @@ static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info) } } -static void riscv_cpu_validate_v(CPURISCVState *env, RISCVCPUConfig *cfg, - Error **errp) +#ifndef CONFIG_USER_ONLY +static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) { - int vext_version = VEXT_VERSION_1_00_0; + bool rv32 = riscv_cpu_is_32bit(cpu); + uint16_t supported; + uint8_t satp_mode_map_max; - if (!is_power_of_2(cfg->vlen)) { - error_setg(errp, "Vector extension VLEN must be power of 2"); - return; - } - if (cfg->vlen > RV_VLEN_MAX || cfg->vlen < 128) { - error_setg(errp, - "Vector extension implementation only supports VLEN " - "in the range [128, %d]", RV_VLEN_MAX); + if (!get_satp_mode_supported(cpu, &supported)) { + /* The CPU wants the hypervisor to decide which satp mode to allow */ return; } - if (!is_power_of_2(cfg->elen)) { - error_setg(errp, "Vector extension ELEN must be power of 2"); + + if (cpu->satp_modes.map == 0) { + if (cpu->satp_modes.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->satp_modes.init & (1 << i)) && + supported & (1 << i)) { + for (int j = i - 1; j >= 0; --j) { + if (supported & (1 << j)) { + cpu->cfg.max_satp_mode = j; + return; + } + } + } + } + } return; } - if (cfg->elen > 64 || cfg->elen < 8) { - error_setg(errp, - "Vector extension implementation only supports ELEN " - "in the range [8, 64]"); + + satp_mode_map_max = satp_mode_max_from_map(cpu->satp_modes.map); + + /* Make sure the user asked for a supported configuration (HW and qemu) */ + if (satp_mode_map_max > cpu->cfg.max_satp_mode) { + error_setg(errp, "satp_mode %s is higher than hw max capability %s", + satp_mode_str(satp_mode_map_max, rv32), + satp_mode_str(cpu->cfg.max_satp_mode, rv32)); return; } - if (cfg->vext_spec) { - if (!g_strcmp0(cfg->vext_spec, "v1.0")) { - vext_version = VEXT_VERSION_1_00_0; - } else { - error_setg(errp, "Unsupported vector spec version '%s'", - cfg->vext_spec); - 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->satp_modes.map & (1 << i)) && + (cpu->satp_modes.init & (1 << i)) && + (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; + } } - } else { - qemu_log("vector version is not specified, " - "use the default value v1.0\n"); } - env->vext_ver = vext_version; + + cpu->cfg.max_satp_mode = satp_mode_map_max; } +#endif -static void riscv_cpu_validate_priv_spec(RISCVCPU *cpu, Error **errp) +void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp) { - CPURISCVState *env = &cpu->env; - int priv_version = -1; + Error *local_err = NULL; - 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); +#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; } - - env->priv_ver = priv_version; } } -static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu) +static void riscv_cpu_realize(DeviceState *dev, Error **errp) { - CPURISCVState *env = &cpu->env; - int i; - - /* 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" TARGET_FMT_lx - " because privilege spec version does not match", - isa_edata_arr[i].name, env->mhartid); -#else - warn_report("disabling %s extension because " - "privilege spec version does not match", - isa_edata_arr[i].name); -#endif - } - } -} - -static void riscv_cpu_validate_misa_mxl(RISCVCPU *cpu, Error **errp) -{ - RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); - CPUClass *cc = CPU_CLASS(mcc); - CPURISCVState *env = &cpu->env; - - /* 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(); - } - - if (env->misa_mxl_max != env->misa_mxl) { - error_setg(errp, "misa_mxl_max must be equal to misa_mxl"); - return; - } -} - -/* - * Check consistency between chosen extensions while setting - * cpu->cfg accordingly. - */ -void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) -{ - CPURISCVState *env = &cpu->env; + CPUState *cs = CPU(dev); + RISCVCPU *cpu = RISCV_CPU(dev); + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); Error *local_err = NULL; - /* Do some ISA extension error checking */ - if (riscv_has_ext(env, RVG) && - !(riscv_has_ext(env, RVI) && riscv_has_ext(env, RVM) && - riscv_has_ext(env, RVA) && riscv_has_ext(env, RVF) && - riscv_has_ext(env, RVD) && - cpu->cfg.ext_icsr && cpu->cfg.ext_ifencei)) { - warn_report("Setting G will also set IMAFD_Zicsr_Zifencei"); - cpu->cfg.ext_icsr = true; - cpu->cfg.ext_ifencei = true; - - env->misa_ext |= RVI | RVM | RVA | RVF | RVD; - env->misa_ext_mask |= RVI | RVM | RVA | RVF | RVD; - } - - 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_icsr) { - error_setg(errp, "F extension requires Zicsr"); - 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"); + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); return; } - if (cpu->cfg.ext_zfh) { - cpu->cfg.ext_zfhmin = true; - } - - if (cpu->cfg.ext_zfhmin && !riscv_has_ext(env, RVF)) { - error_setg(errp, "Zfh/Zfhmin extensions require F extension"); + riscv_cpu_finalize_features(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); return; } - if (cpu->cfg.ext_zfbfmin && !riscv_has_ext(env, RVF)) { - error_setg(errp, "Zfbfmin extension depends on F extension"); - return; - } + riscv_cpu_register_gdb_regs_for_features(cs); - if (riscv_has_ext(env, RVD) && !riscv_has_ext(env, RVF)) { - error_setg(errp, "D extension requires F extension"); - return; +#ifndef CONFIG_USER_ONLY + if (cpu->cfg.debug) { + riscv_trigger_realize(&cpu->env); } +#endif - if (riscv_has_ext(env, RVV)) { - riscv_cpu_validate_v(env, &cpu->cfg, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - return; - } - - /* The V vector extension depends on the Zve64d extension */ - cpu->cfg.ext_zve64d = true; - } + qemu_init_vcpu(cs); + cpu_reset(cs); - /* The Zve64d extension depends on the Zve64f extension */ - if (cpu->cfg.ext_zve64d) { - cpu->cfg.ext_zve64f = true; - } + mcc->parent_realize(dev, errp); +} - /* The Zve64f extension depends on the Zve32f extension */ - if (cpu->cfg.ext_zve64f) { - cpu->cfg.ext_zve32f = true; +bool riscv_cpu_accelerator_compatible(RISCVCPU *cpu) +{ + if (tcg_enabled()) { + return riscv_cpu_tcg_compatible(cpu); } - if (cpu->cfg.ext_zve64d && !riscv_has_ext(env, RVD)) { - error_setg(errp, "Zve64d/V extensions require D extension"); - return; - } + return true; +} - if (cpu->cfg.ext_zve32f && !riscv_has_ext(env, RVF)) { - error_setg(errp, "Zve32f/Zve64f extensions require F extension"); - return; - } +#ifndef CONFIG_USER_ONLY +static void cpu_riscv_get_satp(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVSATPModes *satp_modes = opaque; + uint8_t satp = satp_mode_from_str(name); + bool value; - if (cpu->cfg.ext_zvfh) { - cpu->cfg.ext_zvfhmin = true; - } + value = satp_modes->map & (1 << satp); - if (cpu->cfg.ext_zvfhmin && !cpu->cfg.ext_zve32f) { - error_setg(errp, "Zvfh/Zvfhmin extensions require Zve32f extension"); - return; - } + visit_type_bool(v, name, &value, errp); +} - if (cpu->cfg.ext_zvfh && !cpu->cfg.ext_zfhmin) { - error_setg(errp, "Zvfh extensions requires Zfhmin extension"); - return; - } +static void cpu_riscv_set_satp(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVSATPModes *satp_modes = opaque; + uint8_t satp = satp_mode_from_str(name); + bool value; - if (cpu->cfg.ext_zvfbfmin && !cpu->cfg.ext_zfbfmin) { - error_setg(errp, "Zvfbfmin extension depends on Zfbfmin extension"); + if (!visit_type_bool(v, name, &value, errp)) { return; } - if (cpu->cfg.ext_zvfbfmin && !cpu->cfg.ext_zve32f) { - error_setg(errp, "Zvfbfmin extension depends on Zve32f extension"); - return; - } + satp_modes->map = deposit32(satp_modes->map, satp, 1, value); + satp_modes->init |= 1 << satp; +} - if (cpu->cfg.ext_zvfbfwma && !cpu->cfg.ext_zvfbfmin) { - error_setg(errp, "Zvfbfwma extension depends on Zvfbfmin extension"); - return; - } +void riscv_add_satp_mode_properties(Object *obj) +{ + RISCVCPU *cpu = RISCV_CPU(obj); - /* Set the ISA extensions, checks should have happened above */ - if (cpu->cfg.ext_zhinx) { - cpu->cfg.ext_zhinxmin = true; + if (cpu->env.misa_mxl == MXL_RV32) { + object_property_add(obj, "sv32", "bool", cpu_riscv_get_satp, + cpu_riscv_set_satp, NULL, &cpu->satp_modes); + } else { + object_property_add(obj, "sv39", "bool", cpu_riscv_get_satp, + cpu_riscv_set_satp, NULL, &cpu->satp_modes); + object_property_add(obj, "sv48", "bool", cpu_riscv_get_satp, + cpu_riscv_set_satp, NULL, &cpu->satp_modes); + object_property_add(obj, "sv57", "bool", cpu_riscv_get_satp, + cpu_riscv_set_satp, NULL, &cpu->satp_modes); + object_property_add(obj, "sv64", "bool", cpu_riscv_get_satp, + cpu_riscv_set_satp, NULL, &cpu->satp_modes); } +} - if ((cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinxmin) && !cpu->cfg.ext_zfinx) { - error_setg(errp, "Zdinx/Zhinx/Zhinxmin extensions require Zfinx"); - return; - } +static void riscv_cpu_set_irq(void *opaque, int irq, int level) +{ + RISCVCPU *cpu = RISCV_CPU(opaque); + CPURISCVState *env = &cpu->env; - if (cpu->cfg.ext_zfinx) { - if (!cpu->cfg.ext_icsr) { - error_setg(errp, "Zfinx extension requires Zicsr"); - return; + if (irq < IRQ_LOCAL_MAX) { + switch (irq) { + case IRQ_U_SOFT: + case IRQ_S_SOFT: + case IRQ_VS_SOFT: + case IRQ_M_SOFT: + case IRQ_U_TIMER: + case IRQ_S_TIMER: + case IRQ_VS_TIMER: + case IRQ_M_TIMER: + case IRQ_U_EXT: + case IRQ_VS_EXT: + case IRQ_M_EXT: + if (kvm_enabled()) { + kvm_riscv_set_irq(cpu, irq, level); + } else { + riscv_cpu_update_mip(env, 1 << irq, BOOL_TO_MASK(level)); + } + break; + case IRQ_S_EXT: + if (kvm_enabled()) { + kvm_riscv_set_irq(cpu, irq, level); + } else { + env->external_seip = level; + riscv_cpu_update_mip(env, 1 << irq, + BOOL_TO_MASK(level | env->software_seip)); + } + break; + default: + g_assert_not_reached(); } - if (riscv_has_ext(env, RVF)) { - error_setg(errp, - "Zfinx cannot be supported together with F extension"); - return; + } else if (irq < (IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX)) { + /* Require H-extension for handling guest local interrupts */ + if (!riscv_has_ext(env, RVH)) { + g_assert_not_reached(); } - } - if (cpu->cfg.ext_zce) { - cpu->cfg.ext_zca = true; - cpu->cfg.ext_zcb = true; - cpu->cfg.ext_zcmp = true; - cpu->cfg.ext_zcmt = true; - if (riscv_has_ext(env, RVF) && env->misa_mxl_max == MXL_RV32) { - cpu->cfg.ext_zcf = true; + /* Compute bit position in HGEIP CSR */ + irq = irq - IRQ_LOCAL_MAX + 1; + if (env->geilen < irq) { + g_assert_not_reached(); } - } - /* 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_zca = true; - if (riscv_has_ext(env, RVF) && env->misa_mxl_max == MXL_RV32) { - cpu->cfg.ext_zcf = true; - } - if (riscv_has_ext(env, RVD)) { - cpu->cfg.ext_zcd = true; + /* Update HGEIP CSR */ + env->hgeip &= ~((target_ulong)1 << irq); + if (level) { + env->hgeip |= (target_ulong)1 << irq; } - } - if (env->misa_mxl_max != MXL_RV32 && cpu->cfg.ext_zcf) { - error_setg(errp, "Zcf extension is only relevant to RV32"); - return; + /* Update mip.SGEIP bit */ + riscv_cpu_update_mip(env, MIP_SGEIP, + BOOL_TO_MASK(!!(env->hgeie & env->hgeip))); + } else { + g_assert_not_reached(); } +} - if (!riscv_has_ext(env, RVF) && cpu->cfg.ext_zcf) { - error_setg(errp, "Zcf extension requires F extension"); - return; - } +static void riscv_cpu_set_nmi(void *opaque, int irq, int level) +{ + riscv_cpu_set_rnmi(RISCV_CPU(opaque), irq, level); +} +#endif /* CONFIG_USER_ONLY */ - if (!riscv_has_ext(env, RVD) && cpu->cfg.ext_zcd) { - error_setg(errp, "Zcd extension requires D extension"); - return; - } +static bool riscv_cpu_is_dynamic(Object *cpu_obj) +{ + return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL; +} - 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; - } +static void riscv_cpu_init(Object *obj) +{ + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(obj); + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; - 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; - } + env->misa_mxl = mcc->def->misa_mxl_max; - if (cpu->cfg.ext_zcmt && !cpu->cfg.ext_icsr) { - error_setg(errp, "Zcmt extension requires Zicsr extension"); - return; - } +#ifndef CONFIG_USER_ONLY + qdev_init_gpio_in(DEVICE(obj), riscv_cpu_set_irq, + IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX); + qdev_init_gpio_in_named(DEVICE(cpu), riscv_cpu_set_nmi, + "riscv.cpu.rnmi", RNMI_MAX); +#endif /* CONFIG_USER_ONLY */ + + general_user_opts = g_hash_table_new(g_str_hash, g_str_equal); /* - * In principle Zve*x would also suffice here, were they supported - * in qemu + * 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. */ - if ((cpu->cfg.ext_zvbb || cpu->cfg.ext_zvkg || cpu->cfg.ext_zvkned || - cpu->cfg.ext_zvknha || cpu->cfg.ext_zvksed || cpu->cfg.ext_zvksh) && - !cpu->cfg.ext_zve32f) { - error_setg(errp, - "Vector crypto extensions require V or Zve* extensions"); - return; - } + RISCV_CPU(obj)->cfg.ext_zicntr = !mcc->def->bare; + RISCV_CPU(obj)->cfg.ext_zihpm = !mcc->def->bare; - if ((cpu->cfg.ext_zvbc || cpu->cfg.ext_zvknhb) && !cpu->cfg.ext_zve64f) { - error_setg( - errp, - "Zvbc and Zvknhb extensions require V or Zve64{f,d} extensions"); - return; - } + /* 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; + cpu->cfg.max_satp_mode = -1; - if (cpu->cfg.ext_zk) { - cpu->cfg.ext_zkn = true; - cpu->cfg.ext_zkr = true; - cpu->cfg.ext_zkt = true; + if (mcc->def->profile) { + mcc->def->profile->enabled = 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; - } + env->misa_ext_mask = env->misa_ext = mcc->def->misa_ext; + riscv_cpu_cfg_merge(&cpu->cfg, &mcc->def->cfg); - 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 (mcc->def->priv_spec != RISCV_PROFILE_ATTR_UNUSED) { + cpu->env.priv_ver = mcc->def->priv_spec; + } + if (mcc->def->vext_spec != RISCV_PROFILE_ATTR_UNUSED) { + cpu->env.vext_ver = mcc->def->vext_spec; } - - /* - * 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 void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) -{ - bool rv32 = riscv_cpu_mxl(&cpu->env) == MXL_RV32; - 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; + if (mcc->def->custom_csrs) { + riscv_register_custom_csrs(cpu, mcc->def->custom_csrs); } +#endif - satp_mode_supported_max = - satp_mode_max_from_map(cpu->cfg.satp_mode.supported); + accel_cpu_instance_init(CPU(obj)); +} - 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; - } - } - } - } +typedef struct misa_ext_info { + const char *name; + const char *description; +} MISAExtInfo; - satp_mode_map_max = satp_mode_max_from_map(cpu->cfg.satp_mode.map); +#define MISA_INFO_IDX(_bit) \ + __builtin_ctz(_bit) - /* 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; +#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(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->def->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; /* - * Make sure the user did not ask for an invalid configuration as per - * the specification. + * Our lowest valid input (RVA) is 1 and + * __builtin_ctz() is UB with zero. */ - 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; - } - } - } + g_assert(bit != 0); + idx = MISA_INFO_IDX(bit); - /* 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); - } - } + g_assert(idx < ARRAY_SIZE(misa_ext_info_arr)); + return idx; } -#endif -static void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp) +const char *riscv_get_misa_ext_name(uint32_t bit) { -#ifndef CONFIG_USER_ONLY - Error *local_err = NULL; + int idx = riscv_validate_misa_info_idx(bit); + const char *val = misa_ext_info_arr[idx].name; - riscv_cpu_satp_mode_finalize(cpu, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - return; - } -#endif + g_assert(val != NULL); + return val; } -static void riscv_cpu_validate_misa_priv(CPURISCVState *env, Error **errp) +const char *riscv_get_misa_ext_description(uint32_t bit) { - 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; - } + 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 */ + MULTI_EXT_CFG_BOOL("sscofpmf", ext_sscofpmf, false), + MULTI_EXT_CFG_BOOL("smcntrpmf", ext_smcntrpmf, false), + MULTI_EXT_CFG_BOOL("smcsrind", ext_smcsrind, false), + MULTI_EXT_CFG_BOOL("smcdeleg", ext_smcdeleg, false), + MULTI_EXT_CFG_BOOL("sscsrind", ext_sscsrind, false), + MULTI_EXT_CFG_BOOL("ssccfg", ext_ssccfg, false), + MULTI_EXT_CFG_BOOL("smctr", ext_smctr, false), + MULTI_EXT_CFG_BOOL("ssctr", ext_ssctr, 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), + MULTI_EXT_CFG_BOOL("ssnpm", ext_ssnpm, false), + MULTI_EXT_CFG_BOOL("sspm", ext_sspm, false), + MULTI_EXT_CFG_BOOL("supm", ext_supm, false), + + MULTI_EXT_CFG_BOOL("smaia", ext_smaia, false), + MULTI_EXT_CFG_BOOL("smdbltrp", ext_smdbltrp, false), + MULTI_EXT_CFG_BOOL("smepmp", ext_smepmp, false), + MULTI_EXT_CFG_BOOL("smrnmi", ext_smrnmi, false), + MULTI_EXT_CFG_BOOL("smmpm", ext_smmpm, false), + MULTI_EXT_CFG_BOOL("smnpm", ext_smnpm, false), + MULTI_EXT_CFG_BOOL("smstateen", ext_smstateen, false), + MULTI_EXT_CFG_BOOL("ssaia", ext_ssaia, false), + MULTI_EXT_CFG_BOOL("ssdbltrp", ext_ssdbltrp, 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), + + MULTI_EXT_CFG_BOOL("zicntr", ext_zicntr, true), + MULTI_EXT_CFG_BOOL("zihpm", ext_zihpm, true), + + 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), + + 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), + + MULTI_EXT_CFG_BOOL("zicbom", ext_zicbom, true), + MULTI_EXT_CFG_BOOL("zicbop", ext_zicbop, true), + MULTI_EXT_CFG_BOOL("zicboz", ext_zicboz, true), + + MULTI_EXT_CFG_BOOL("zmmul", ext_zmmul, 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), + + { }, +}; + +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), + + { }, +}; + +/* These are experimental so mark with 'x-' */ +const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { + MULTI_EXT_CFG_BOOL("x-svukte", ext_svukte, false), + + { }, +}; + +/* + * '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), + MULTI_EXT_CFG_BOOL("ssstateen", ext_ssstateen, true), + MULTI_EXT_CFG_BOOL("sha", ext_sha, true), + MULTI_EXT_CFG_BOOL("ziccrse", ext_ziccrse, true), + + { }, +}; + +/* 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), + + { }, +}; + +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 riscv_cpu_realize_tcg(DeviceState *dev, Error **errp) +static void prop_pmu_num_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { - RISCVCPU *cpu = RISCV_CPU(dev); - CPURISCVState *env = &cpu->env; - Error *local_err = NULL; + RISCVCPU *cpu = RISCV_CPU(obj); + uint8_t pmu_num, curr_pmu_num; + uint32_t pmu_mask; + + visit_type_uint8(v, name, &pmu_num, errp); - if (object_dynamic_cast(OBJECT(dev), TYPE_RISCV_CPU_HOST)) { - error_setg(errp, "'host' CPU is not compatible with TCG acceleration"); + 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; } - riscv_cpu_validate_misa_mxl(cpu, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); + if (pmu_num > (RV_MAX_MHPMCOUNTERS - 3)) { + error_setg(errp, "Number of counters exceeds maximum available"); return; } - riscv_cpu_validate_priv_spec(cpu, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - return; + if (pmu_num == 0) { + pmu_mask = 0; + } else { + pmu_mask = MAKE_64BIT_MASK(3, pmu_num); } - riscv_cpu_validate_misa_priv(env, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); + 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 = { + .type = "int8", + .description = "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; } - if (cpu->cfg.epmp && !cpu->cfg.pmp) { - /* - * Enhanced PMP should only be available - * on harts with PMP support - */ - error_setg(errp, "Invalid configuration: EPMP requires PMP support"); + pmu_num = ctpop32(value); + + if (pmu_num > (RV_MAX_MHPMCOUNTERS - 3)) { + error_setg(errp, "Number of counters exceeds maximum available"); return; } - riscv_cpu_validate_set_extensions(cpu, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); + 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 = { + .type = "int8", + .description = "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; } -#ifndef CONFIG_USER_ONLY - CPU(dev)->tcg_cflags |= CF_PCREL; + cpu_option_add_user_setting(name, value); + cpu->cfg.mmu = value; +} - if (cpu->cfg.ext_sstc) { - riscv_timer_init(cpu); - } +static void prop_mmu_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + bool value = RISCV_CPU(obj)->cfg.mmu; - 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 + visit_type_bool(v, name, &value, errp); } -static void riscv_cpu_realize(DeviceState *dev, Error **errp) +static const PropertyInfo prop_mmu = { + .type = "bool", + .description = "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) { - CPUState *cs = CPU(dev); - RISCVCPU *cpu = RISCV_CPU(dev); - RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); - Error *local_err = NULL; + RISCVCPU *cpu = RISCV_CPU(obj); + bool value; - cpu_exec_realizefn(cs, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); + 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; } - if (tcg_enabled()) { - riscv_cpu_realize_tcg(dev, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - 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 = { + .type = "bool", + .description = "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; } - riscv_cpu_finalize_features(cpu, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - return; + 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; } +} - riscv_cpu_register_gdb_regs_for_features(cs); +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; -#ifndef CONFIG_USER_ONLY - if (cpu->cfg.debug) { - riscv_trigger_realize(&cpu->env); + 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; } -#endif - qemu_init_vcpu(cs); - cpu_reset(cs); + 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; + } - mcc->parent_realize(dev, errp); + cpu_option_add_user_setting(name, priv_version); + cpu->env.priv_ver = priv_version; } -#ifndef CONFIG_USER_ONLY -static void cpu_riscv_get_satp(Object *obj, Visitor *v, const char *name, +static void prop_priv_spec_get(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); + RISCVCPU *cpu = RISCV_CPU(obj); + const char *value = priv_spec_to_str(cpu->env.priv_ver); - visit_type_bool(v, name, &value, errp); + visit_type_str(v, name, (char **)&value, errp); } -static void cpu_riscv_set_satp(Object *obj, Visitor *v, const char *name, +static const PropertyInfo prop_priv_spec = { + .type = "str", + .description = "priv_spec", + /* FIXME enum? */ + .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) { - RISCVSATPMap *satp_map = opaque; - uint8_t satp = satp_mode_from_str(name); - bool value; + RISCVCPU *cpu = RISCV_CPU(obj); + g_autofree char *value = NULL; - if (!visit_type_bool(v, name, &value, errp)) { + 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; } - satp_map->map = deposit32(satp_map->map, satp, 1, value); - satp_map->init |= 1 << satp; + 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 void riscv_add_satp_mode_properties(Object *obj) +static const PropertyInfo prop_vext_spec = { + .type = "str", + .description = "vext_spec", + /* FIXME enum? */ + .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 cpu_vlen = cpu->cfg.vlenb << 3; + uint16_t value; - 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); + 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_vlen && riscv_cpu_is_vendor(obj)) { + cpu_set_prop_err(cpu, name, errp); + error_append_hint(errp, "Current '%s' val: %u\n", + name, cpu_vlen); + return; + } + + cpu_option_add_user_setting(name, value); + cpu->cfg.vlenb = value >> 3; } -static void riscv_cpu_set_irq(void *opaque, int irq, int level) +static void prop_vlen_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { - RISCVCPU *cpu = RISCV_CPU(opaque); - CPURISCVState *env = &cpu->env; + uint16_t value = RISCV_CPU(obj)->cfg.vlenb << 3; - if (irq < IRQ_LOCAL_MAX) { - switch (irq) { - case IRQ_U_SOFT: - case IRQ_S_SOFT: - case IRQ_VS_SOFT: - case IRQ_M_SOFT: - case IRQ_U_TIMER: - case IRQ_S_TIMER: - case IRQ_VS_TIMER: - case IRQ_M_TIMER: - case IRQ_U_EXT: - case IRQ_VS_EXT: - case IRQ_M_EXT: - if (kvm_enabled()) { - kvm_riscv_set_irq(cpu, irq, level); - } else { - riscv_cpu_update_mip(env, 1 << irq, BOOL_TO_MASK(level)); - } - break; - case IRQ_S_EXT: - if (kvm_enabled()) { - kvm_riscv_set_irq(cpu, irq, level); - } else { - env->external_seip = level; - riscv_cpu_update_mip(env, 1 << irq, - BOOL_TO_MASK(level | env->software_seip)); - } - break; - default: - g_assert_not_reached(); - } - } else if (irq < (IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX)) { - /* Require H-extension for handling guest local interrupts */ - if (!riscv_has_ext(env, RVH)) { - g_assert_not_reached(); - } + visit_type_uint16(v, name, &value, errp); +} - /* Compute bit position in HGEIP CSR */ - irq = irq - IRQ_LOCAL_MAX + 1; - if (env->geilen < irq) { - g_assert_not_reached(); - } +static const PropertyInfo prop_vlen = { + .type = "uint16", + .description = "vlen", + .get = prop_vlen_get, + .set = prop_vlen_set, +}; - /* Update HGEIP CSR */ - env->hgeip &= ~((target_ulong)1 << irq); - if (level) { - env->hgeip |= (target_ulong)1 << irq; - } +static void prop_elen_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + uint16_t value; - /* Update mip.SGEIP bit */ - riscv_cpu_update_mip(env, MIP_SGEIP, - BOOL_TO_MASK(!!(env->hgeie & env->hgeip))); - } else { - g_assert_not_reached(); + 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; } -#endif /* CONFIG_USER_ONLY */ -static void riscv_cpu_init(Object *obj) +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 = { + .type = "uint16", + .description = "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; - cpu_set_cpustate_pointers(cpu); + if (!visit_type_uint16(v, name, &value, errp)) { + return; + } -#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 */ + 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; } -typedef struct RISCVCPUMisaExtConfig { - const char *name; - const char *description; - target_ulong misa_bit; - bool enabled; -} RISCVCPUMisaExtConfig; +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; -static void cpu_set_misa_ext_cfg(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) + visit_type_uint16(v, name, &value, errp); +} + +static const PropertyInfo prop_cbom_blksize = { + .type = "uint16", + .description = "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) { - 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; + uint16_t value; - if (!visit_type_bool(v, name, &value, errp)) { + if (!visit_type_uint16(v, name, &value, errp)) { return; } - if (value) { - env->misa_ext |= misa_bit; - env->misa_ext_mask |= misa_bit; - } else { - env->misa_ext &= ~misa_bit; - env->misa_ext_mask &= ~misa_bit; + 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 void cpu_get_misa_ext_cfg(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static const PropertyInfo prop_cbop_blksize = { + .type = "uint16", + .description = "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) { - 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; + uint16_t value; - value = env->misa_ext & misa_bit; + if (!visit_type_uint16(v, name, &value, errp)) { + return; + } - visit_type_bool(v, name, &value, errp); + 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; } -typedef struct misa_ext_info { - const char *name; - const char *description; -} MISAExtInfo; +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; -#define MISA_INFO_IDX(_bit) \ - __builtin_ctz(_bit) + visit_type_uint16(v, name, &value, errp); +} -#define MISA_EXT_INFO(_bit, _propname, _descr) \ - [MISA_INFO_IDX(_bit)] = {.name = _propname, .description = _descr} +static const PropertyInfo prop_cboz_blksize = { + .type = "uint16", + .description = "cboz_blocksize", + .get = prop_cboz_blksize_get, + .set = prop_cboz_blksize_set, +}; -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)"), +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 = { + .type = "uint32", + .description = "mvendorid", + .get = prop_mvendorid_get, + .set = prop_mvendorid_set, }; -static int riscv_validate_misa_info_idx(uint32_t bit) +static void prop_mimpid_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { - int idx; + bool dynamic_cpu = riscv_cpu_is_dynamic(obj); + RISCVCPU *cpu = RISCV_CPU(obj); + uint64_t prev_val = cpu->cfg.mimpid; + uint64_t value; - /* - * Our lowest valid input (RVA) is 1 and - * __builtin_ctz() is UB with zero. - */ - g_assert(bit != 0); - idx = MISA_INFO_IDX(bit); + 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 = { + .type = "uint64", + .description = "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 = { + .type = "uint64", + .description = "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 = { + .u_parent = NULL, + .s_parent = NULL, + .name = "rva22u64", + .misa_ext = RVI | RVM | RVA | RVF | RVD | RVC | RVB | 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 = { + .u_parent = &RVA22U64, + .s_parent = NULL, + .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 + } +}; + +/* + * All mandatory extensions from RVA22U64 are present + * in RVA23U64 so set RVA22 as a parent. We need to + * declare just the newly added mandatory extensions. + */ +static RISCVCPUProfile RVA23U64 = { + .u_parent = &RVA22U64, + .s_parent = NULL, + .name = "rva23u64", + .misa_ext = RVV, + .priv_spec = RISCV_PROFILE_ATTR_UNUSED, + .satp_mode = RISCV_PROFILE_ATTR_UNUSED, + .ext_offsets = { + CPU_CFG_OFFSET(ext_zvfhmin), CPU_CFG_OFFSET(ext_zvbb), + CPU_CFG_OFFSET(ext_zvkt), CPU_CFG_OFFSET(ext_zihintntl), + CPU_CFG_OFFSET(ext_zicond), CPU_CFG_OFFSET(ext_zimop), + CPU_CFG_OFFSET(ext_zcmop), CPU_CFG_OFFSET(ext_zcb), + CPU_CFG_OFFSET(ext_zfa), CPU_CFG_OFFSET(ext_zawrs), + CPU_CFG_OFFSET(ext_supm), + + RISCV_PROFILE_EXT_LIST_END + } +}; + +/* + * As with RVA23U64, RVA23S64 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, Ssu64xl + * + * The remaining features/extensions comes from RVA23S64. + */ +static RISCVCPUProfile RVA23S64 = { + .u_parent = &RVA23U64, + .s_parent = &RVA22S64, + .name = "rva23s64", + .misa_ext = RVS, + .priv_spec = PRIV_VERSION_1_13_0, + .satp_mode = VM_1_10_SV39, + .ext_offsets = { + /* New in RVA23S64 */ + CPU_CFG_OFFSET(ext_svnapot), CPU_CFG_OFFSET(ext_sstc), + CPU_CFG_OFFSET(ext_sscofpmf), CPU_CFG_OFFSET(ext_ssnpm), + + /* Named features: Sha */ + CPU_CFG_OFFSET(ext_sha), + + RISCV_PROFILE_EXT_LIST_END + } +}; + +RISCVCPUProfile *riscv_profiles[] = { + &RVA22U64, + &RVA22S64, + &RVA23U64, + &RVA23S64, + 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 + }, +}; - g_assert(idx < ARRAY_SIZE(misa_ext_info_arr)); - return idx; -} +static RISCVCPUImpliedExtsRule ZVKNHB_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvknhb), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve64x), -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; + RISCV_IMPLIED_EXTS_RULE_END + }, +}; - g_assert(val != NULL); - return val; -} +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), -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; + RISCV_IMPLIED_EXTS_RULE_END + }, +}; - g_assert(val != NULL); - return val; -} +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 + }, +}; -#define MISA_CFG(_bit, _enabled) \ - {.misa_bit = _bit, .enabled = _enabled} +static RISCVCPUImpliedExtsRule ZVKSG_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvksg), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvks), CPU_CFG_OFFSET(ext_zvkg), -static 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), + RISCV_IMPLIED_EXTS_RULE_END + }, }; -static void riscv_cpu_add_misa_properties(Object *cpu_obj) -{ - int i; +static RISCVCPUImpliedExtsRule SSCFG_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_ssccfg), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_smcsrind), CPU_CFG_OFFSET(ext_sscsrind), + CPU_CFG_OFFSET(ext_smcdeleg), - for (i = 0; i < ARRAY_SIZE(misa_ext_cfgs); i++) { - RISCVCPUMisaExtConfig *misa_cfg = &misa_ext_cfgs[i]; - int bit = misa_cfg->misa_bit; + RISCV_IMPLIED_EXTS_RULE_END + }, +}; - misa_cfg->name = riscv_get_misa_ext_name(bit); - misa_cfg->description = riscv_get_misa_ext_description(bit); +static RISCVCPUImpliedExtsRule SUPM_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_supm), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_ssnpm), CPU_CFG_OFFSET(ext_smnpm), - /* Check if KVM already created the property */ - if (object_property_find(cpu_obj, misa_cfg->name)) { - continue; - } + RISCV_IMPLIED_EXTS_RULE_END + }, +}; - object_property_add(cpu_obj, misa_cfg->name, "bool", - cpu_get_misa_ext_cfg, - cpu_set_misa_ext_cfg, - NULL, (void *)misa_cfg); - object_property_set_description(cpu_obj, misa_cfg->name, - misa_cfg->description); - object_property_set_bool(cpu_obj, misa_cfg->name, - misa_cfg->enabled, NULL); - } -} +static RISCVCPUImpliedExtsRule SSPM_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_sspm), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_smnpm), -static Property riscv_cpu_extensions[] = { - /* Defaults for standard extensions */ - 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("Zihintntl", RISCVCPU, cfg.ext_zihintntl, true), - DEFINE_PROP_BOOL("Zihintpause", RISCVCPU, cfg.ext_zihintpause, true), - DEFINE_PROP_BOOL("Zawrs", RISCVCPU, cfg.ext_zawrs, true), - DEFINE_PROP_BOOL("Zfa", RISCVCPU, cfg.ext_zfa, 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("Zve64d", RISCVCPU, cfg.ext_zve64d, 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), - - 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), - - DEFINE_PROP_BOOL("smstateen", RISCVCPU, cfg.ext_smstateen, false), - DEFINE_PROP_BOOL("svadu", RISCVCPU, cfg.ext_svadu, 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), - - 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), - - 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), - - DEFINE_PROP_BOOL("zicbom", RISCVCPU, cfg.ext_icbom, true), - DEFINE_PROP_UINT16("cbom_blocksize", RISCVCPU, cfg.cbom_blocksize, 64), - DEFINE_PROP_BOOL("zicboz", RISCVCPU, cfg.ext_icboz, true), - DEFINE_PROP_UINT16("cboz_blocksize", RISCVCPU, cfg.cboz_blocksize, 64), - - DEFINE_PROP_BOOL("zmmul", RISCVCPU, cfg.ext_zmmul, false), - - DEFINE_PROP_BOOL("zca", RISCVCPU, cfg.ext_zca, false), - DEFINE_PROP_BOOL("zcb", RISCVCPU, cfg.ext_zcb, false), - DEFINE_PROP_BOOL("zcd", RISCVCPU, cfg.ext_zcd, false), - DEFINE_PROP_BOOL("zce", RISCVCPU, cfg.ext_zce, false), - DEFINE_PROP_BOOL("zcf", RISCVCPU, cfg.ext_zcf, false), - DEFINE_PROP_BOOL("zcmp", RISCVCPU, cfg.ext_zcmp, false), - DEFINE_PROP_BOOL("zcmt", RISCVCPU, cfg.ext_zcmt, false), - DEFINE_PROP_BOOL("zicond", RISCVCPU, cfg.ext_zicond, false), - - /* Vendor-specific custom extensions */ - DEFINE_PROP_BOOL("xtheadba", RISCVCPU, cfg.ext_xtheadba, false), - DEFINE_PROP_BOOL("xtheadbb", RISCVCPU, cfg.ext_xtheadbb, false), - DEFINE_PROP_BOOL("xtheadbs", RISCVCPU, cfg.ext_xtheadbs, false), - DEFINE_PROP_BOOL("xtheadcmo", RISCVCPU, cfg.ext_xtheadcmo, false), - DEFINE_PROP_BOOL("xtheadcondmov", RISCVCPU, cfg.ext_xtheadcondmov, false), - DEFINE_PROP_BOOL("xtheadfmemidx", RISCVCPU, cfg.ext_xtheadfmemidx, false), - DEFINE_PROP_BOOL("xtheadfmv", RISCVCPU, cfg.ext_xtheadfmv, false), - DEFINE_PROP_BOOL("xtheadmac", RISCVCPU, cfg.ext_xtheadmac, false), - DEFINE_PROP_BOOL("xtheadmemidx", RISCVCPU, cfg.ext_xtheadmemidx, false), - DEFINE_PROP_BOOL("xtheadmempair", RISCVCPU, cfg.ext_xtheadmempair, false), - DEFINE_PROP_BOOL("xtheadsync", RISCVCPU, cfg.ext_xtheadsync, false), - DEFINE_PROP_BOOL("xventanacondops", RISCVCPU, cfg.ext_XVentanaCondOps, false), - - /* These are experimental so mark with 'x-' */ - - /* 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), - - DEFINE_PROP_BOOL("x-zvfh", RISCVCPU, cfg.ext_zvfh, false), - DEFINE_PROP_BOOL("x-zvfhmin", RISCVCPU, cfg.ext_zvfhmin, false), - - DEFINE_PROP_BOOL("x-zfbfmin", RISCVCPU, cfg.ext_zfbfmin, false), - DEFINE_PROP_BOOL("x-zvfbfmin", RISCVCPU, cfg.ext_zvfbfmin, false), - DEFINE_PROP_BOOL("x-zvfbfwma", RISCVCPU, cfg.ext_zvfbfwma, false), + RISCV_IMPLIED_EXTS_RULE_END + }, +}; - /* Vector cryptography extensions */ - DEFINE_PROP_BOOL("x-zvbb", RISCVCPU, cfg.ext_zvbb, false), - DEFINE_PROP_BOOL("x-zvbc", RISCVCPU, cfg.ext_zvbc, false), - DEFINE_PROP_BOOL("x-zvkg", RISCVCPU, cfg.ext_zvkg, false), - DEFINE_PROP_BOOL("x-zvkned", RISCVCPU, cfg.ext_zvkned, false), - DEFINE_PROP_BOOL("x-zvknha", RISCVCPU, cfg.ext_zvknha, false), - DEFINE_PROP_BOOL("x-zvknhb", RISCVCPU, cfg.ext_zvknhb, false), - DEFINE_PROP_BOOL("x-zvksed", RISCVCPU, cfg.ext_zvksed, false), - DEFINE_PROP_BOOL("x-zvksh", RISCVCPU, cfg.ext_zvksh, false), +static RISCVCPUImpliedExtsRule SMCTR_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_smctr), + .implied_misa_exts = RVS, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_sscsrind), - DEFINE_PROP_END_OF_LIST(), + RISCV_IMPLIED_EXTS_RULE_END + }, }; +static RISCVCPUImpliedExtsRule SSCTR_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_ssctr), + .implied_misa_exts = RVS, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_sscsrind), -#ifndef CONFIG_USER_ONLY -static void cpu_set_cfg_unavailable(Object *obj, Visitor *v, - const char *name, - void *opaque, Error **errp) -{ - const char *propname = opaque; - bool value; + RISCV_IMPLIED_EXTS_RULE_END + }, +}; - if (!visit_type_bool(v, name, &value, errp)) { - return; - } +RISCVCPUImpliedExtsRule *riscv_misa_ext_implied_rules[] = { + &RVA_IMPLIED, &RVD_IMPLIED, &RVF_IMPLIED, + &RVM_IMPLIED, &RVV_IMPLIED, NULL +}; - if (value) { - error_setg(errp, "extension %s is not available with KVM", - propname); - } -} -#endif +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, &SSCFG_IMPLIED, + &SUPM_IMPLIED, &SSPM_IMPLIED, &SMCTR_IMPLIED, &SSCTR_IMPLIED, + NULL +}; -/* - * 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) -{ - Property *prop; - DeviceState *dev = DEVICE(obj); +static const Property riscv_cpu_properties[] = { + DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true), -#ifndef CONFIG_USER_ONLY - riscv_add_satp_mode_properties(obj); + {.name = "pmu-mask", .info = &prop_pmu_mask}, + {.name = "pmu-num", .info = &prop_pmu_num}, /* Deprecated */ - if (kvm_enabled()) { - kvm_riscv_init_user_properties(obj); - } -#endif + {.name = "mmu", .info = &prop_mmu}, + {.name = "pmp", .info = &prop_pmp}, - riscv_cpu_add_misa_properties(obj); + {.name = "priv_spec", .info = &prop_priv_spec}, + {.name = "vext_spec", .info = &prop_vext_spec}, - for (prop = riscv_cpu_extensions; prop && prop->name; prop++) { -#ifndef CONFIG_USER_ONLY - if (kvm_enabled()) { - /* Check if KVM created the property already */ - if (object_property_find(obj, prop->name)) { - continue; - } + {.name = "vlen", .info = &prop_vlen}, + {.name = "elen", .info = &prop_elen}, - /* - * Set the default to disabled for every extension - * unknown to KVM and error out if the user attempts - * to enable any of them. - * - * We're giving a pass for non-bool properties since they're - * not related to the availability of extensions and can be - * safely ignored as is. - */ - if (prop->info == &qdev_prop_bool) { - object_property_add(obj, prop->name, "bool", - NULL, cpu_set_cfg_unavailable, - NULL, (void *)prop->name); - continue; - } - } -#endif - qdev_property_add_static(dev, prop); - } -} + {.name = "cbom_blocksize", .info = &prop_cbom_blksize}, + {.name = "cbop_blocksize", .info = &prop_cbop_blksize}, + {.name = "cboz_blocksize", .info = &prop_cboz_blksize}, -static Property riscv_cpu_properties[] = { - DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true), + {.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), + DEFINE_PROP_UINT64("rnmi-interrupt-vector", RISCVCPU, env.rnmi_irqvec, + DEFAULT_RNMI_IRQVEC), + DEFINE_PROP_UINT64("rnmi-exception-vector", RISCVCPU, env.rnmi_excpvec, + DEFAULT_RNMI_EXCPVEC), #endif DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false), DEFINE_PROP_BOOL("rvv_ta_all_1s", RISCVCPU, cfg.rvv_ta_all_1s, false), DEFINE_PROP_BOOL("rvv_ma_all_1s", RISCVCPU, cfg.rvv_ma_all_1s, false), + 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) +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) -{ - 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; -} - #ifndef CONFIG_USER_ONLY static int64_t riscv_get_arch_id(CPUState *cs) { @@ -2048,6 +2630,7 @@ static int64_t riscv_get_arch_id(CPUState *cs) #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps riscv_sysemu_ops = { + .has_work = riscv_cpu_has_work, .get_phys_page_debug = riscv_cpu_get_phys_page_debug, .write_elf64_note = riscv_cpu_write_elf64_note, .write_elf32_note = riscv_cpu_write_elf32_note, @@ -2055,139 +2638,7 @@ 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 bool riscv_cpu_is_dynamic(Object *cpu_obj) -{ - return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL; -} - -static void cpu_set_mvendorid(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 cpu_get_mvendorid(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - bool value = RISCV_CPU(obj)->cfg.mvendorid; - - visit_type_bool(v, name, &value, errp); -} - -static void cpu_set_mimpid(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 cpu_get_mimpid(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - bool value = RISCV_CPU(obj)->cfg.mimpid; - - visit_type_bool(v, name, &value, errp); -} - -static void cpu_set_marchid(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 cpu_get_marchid(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - bool value = RISCV_CPU(obj)->cfg.marchid; - - visit_type_bool(v, name, &value, errp); -} - -static void riscv_cpu_class_init(ObjectClass *c, void *data) +static void riscv_cpu_common_class_init(ObjectClass *c, const void *data) { RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); @@ -2201,13 +2652,11 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) &mcc->parent_phases); cc->class_by_name = riscv_cpu_class_by_name; - cc->has_work = riscv_cpu_has_work; 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 @@ -2215,31 +2664,106 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) 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; +#ifdef CONFIG_TCG cc->tcg_ops = &riscv_tcg_ops; +#endif /* CONFIG_TCG */ - object_class_property_add(c, "mvendorid", "uint32", cpu_get_mvendorid, - cpu_set_mvendorid, NULL, NULL); + device_class_set_props(dc, riscv_cpu_properties); +} - object_class_property_add(c, "mimpid", "uint64", cpu_get_mimpid, - cpu_set_mimpid, NULL, NULL); +static bool profile_extends(RISCVCPUProfile *trial, RISCVCPUProfile *parent) +{ + RISCVCPUProfile *curr; + if (!parent) { + return true; + } - object_class_property_add(c, "marchid", "uint64", cpu_get_marchid, - cpu_set_marchid, NULL, NULL); + curr = trial; + while (curr) { + if (curr == parent) { + return true; + } + curr = curr->u_parent; + } - device_class_set_props(dc, riscv_cpu_properties); + curr = trial; + while (curr) { + if (curr == parent) { + return true; + } + curr = curr->s_parent; + } + + return false; +} + +static void riscv_cpu_class_base_init(ObjectClass *c, const void *data) +{ + RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); + RISCVCPUClass *pcc = RISCV_CPU_CLASS(object_class_get_parent(c)); + + if (pcc->def) { + mcc->def = g_memdup2(pcc->def, sizeof(*pcc->def)); + } else { + mcc->def = g_new0(RISCVCPUDef, 1); + } + + if (data) { + const RISCVCPUDef *def = data; + mcc->def->bare |= def->bare; + if (def->profile) { + assert(profile_extends(def->profile, mcc->def->profile)); + assert(mcc->def->bare); + mcc->def->profile = def->profile; + } + if (def->misa_mxl_max) { + assert(def->misa_mxl_max <= MXL_RV128); + mcc->def->misa_mxl_max = def->misa_mxl_max; + +#ifndef CONFIG_USER_ONLY + /* + * Hack to simplify CPU class hierarchies that include both 32- and + * 64-bit models: reduce SV39/48/57/64 to SV32 for 32-bit models. + */ + if (mcc->def->misa_mxl_max == MXL_RV32 && + !valid_vm_1_10_32[mcc->def->cfg.max_satp_mode]) { + mcc->def->cfg.max_satp_mode = VM_1_10_SV32; + } +#endif + } + if (def->priv_spec != RISCV_PROFILE_ATTR_UNUSED) { + assert(def->priv_spec <= PRIV_VERSION_LATEST); + mcc->def->priv_spec = def->priv_spec; + } + if (def->vext_spec != RISCV_PROFILE_ATTR_UNUSED) { + assert(def->vext_spec != 0); + mcc->def->vext_spec = def->vext_spec; + } + mcc->def->misa_ext |= def->misa_ext; + + riscv_cpu_cfg_merge(&mcc->def->cfg, &def->cfg); + + if (def->custom_csrs) { + assert(!mcc->def->custom_csrs); + mcc->def->custom_csrs = def->custom_csrs; + } + } + + if (!object_class_is_abstract(c)) { + 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_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; } @@ -2250,10 +2774,13 @@ static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str, 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]); @@ -2266,84 +2793,400 @@ 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)++; + } + } -static void riscv_cpu_list_entry(gpointer data, gpointer user_data) -{ - const char *typename = object_class_get_name(OBJECT_CLASS(data)); - int len = strlen(typename) - strlen(RISCV_CPU_TYPE_SUFFIX); + 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)++; + } + } - qemu_printf("%.*s\n", len, typename); + return extensions; } -void riscv_cpu_list(void) +void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) { - GSList *list; + 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); - 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); -} + riscv_isa = riscv_isa_string(cpu); + qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", riscv_isa); -#define DEFINE_CPU(type_name, initfn) \ - { \ - .name = type_name, \ - .parent = TYPE_RISCV_CPU, \ - .instance_init = initfn \ - } + 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); -#define DEFINE_DYNAMIC_CPU(type_name, initfn) \ - { \ - .name = type_name, \ - .parent = TYPE_RISCV_DYNAMIC_CPU, \ - .instance_init = initfn \ + for (int i = 0; i < count; i++) { + g_free(isa_extensions[i]); } + g_free(isa_extensions); +} +#endif + +#define DEFINE_ABSTRACT_RISCV_CPU(type_name, parent_type_name, ...) \ + { \ + .name = (type_name), \ + .parent = (parent_type_name), \ + .abstract = true, \ + .class_data = &(const RISCVCPUDef) { \ + .priv_spec = RISCV_PROFILE_ATTR_UNUSED, \ + .vext_spec = RISCV_PROFILE_ATTR_UNUSED, \ + .cfg.max_satp_mode = -1, \ + __VA_ARGS__ \ + }, \ + } + +#define DEFINE_RISCV_CPU(type_name, parent_type_name, ...) \ + { \ + .name = (type_name), \ + .parent = (parent_type_name), \ + .class_data = &(const RISCVCPUDef) { \ + .priv_spec = RISCV_PROFILE_ATTR_UNUSED, \ + .vext_spec = RISCV_PROFILE_ATTR_UNUSED, \ + .cfg.max_satp_mode = -1, \ + __VA_ARGS__ \ + }, \ + } + +#define DEFINE_PROFILE_CPU(type_name, parent_type_name, profile_) \ + DEFINE_RISCV_CPU(type_name, parent_type_name, \ + .profile = &(profile_)) + 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, .abstract = true, .class_size = sizeof(RISCVCPUClass), - .class_init = riscv_cpu_class_init, + .class_init = riscv_cpu_common_class_init, + .class_base_init = riscv_cpu_class_base_init, }, - { - .name = TYPE_RISCV_DYNAMIC_CPU, - .parent = TYPE_RISCV_CPU, - .abstract = true, - }, - DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_ANY, riscv_any_cpu_init), -#if defined(CONFIG_KVM) - DEFINE_CPU(TYPE_RISCV_CPU_HOST, riscv_host_cpu_init), + + DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_DYNAMIC_CPU, TYPE_RISCV_CPU, + .cfg.mmu = true, + .cfg.pmp = true, + .priv_spec = PRIV_VERSION_LATEST, + ), + + DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_VENDOR_CPU, TYPE_RISCV_CPU), + DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_BARE_CPU, TYPE_RISCV_CPU, + /* + * Bare CPUs do not inherit the timer and performance + * counters from the parent class (see riscv_cpu_init() + * for info on why the parent enables them). + * + * Users have to explicitly enable these counters for + * bare CPUs. + */ + .bare = true, + + /* Set to QEMU's first supported priv version */ + .priv_spec = PRIV_VERSION_1_10_0, + + /* + * Support all available satp_mode settings. By default + * only MBARE will be available if the user doesn't enable + * a mode manually (see riscv_cpu_satp_mode_finalize()). + */ +#ifdef TARGET_RISCV32 + .cfg.max_satp_mode = VM_1_10_SV32, +#else + .cfg.max_satp_mode = VM_1_10_SV57, #endif + ), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_MAX, TYPE_RISCV_DYNAMIC_CPU, #if defined(TARGET_RISCV32) - DEFINE_DYNAMIC_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), + .misa_mxl_max = MXL_RV32, + .cfg.max_satp_mode = VM_1_10_SV32, #elif defined(TARGET_RISCV64) - DEFINE_DYNAMIC_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_THEAD_C906, rv64_thead_c906_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_VEYRON_V1, rv64_veyron_v1_cpu_init), - DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE128, rv128_base_cpu_init), + .misa_mxl_max = MXL_RV64, + .cfg.max_satp_mode = VM_1_10_SV57, +#endif + ), + + DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_E, TYPE_RISCV_VENDOR_CPU, + .misa_ext = RVI | RVM | RVA | RVC | RVU, + .priv_spec = PRIV_VERSION_1_10_0, + .cfg.max_satp_mode = VM_1_10_MBARE, + .cfg.ext_zifencei = true, + .cfg.ext_zicsr = true, + .cfg.pmp = true + ), + + DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_U, TYPE_RISCV_VENDOR_CPU, + .misa_ext = RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU, + .priv_spec = PRIV_VERSION_1_10_0, + + .cfg.max_satp_mode = VM_1_10_SV39, + .cfg.ext_zifencei = true, + .cfg.ext_zicsr = true, + .cfg.mmu = true, + .cfg.pmp = true + ), + +#if defined(TARGET_RISCV32) || \ + (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY)) + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_BASE32, TYPE_RISCV_DYNAMIC_CPU, + .cfg.max_satp_mode = VM_1_10_SV32, + .misa_mxl_max = MXL_RV32, + ), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_IBEX, TYPE_RISCV_VENDOR_CPU, + .misa_mxl_max = MXL_RV32, + .misa_ext = RVI | RVM | RVC | RVU, + .priv_spec = PRIV_VERSION_1_12_0, + .cfg.max_satp_mode = VM_1_10_MBARE, + .cfg.ext_zifencei = true, + .cfg.ext_zicsr = true, + .cfg.pmp = true, + .cfg.ext_smepmp = true, + + .cfg.ext_zba = true, + .cfg.ext_zbb = true, + .cfg.ext_zbc = true, + .cfg.ext_zbs = true + ), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_E31, TYPE_RISCV_CPU_SIFIVE_E, + .misa_mxl_max = MXL_RV32 + ), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_E34, TYPE_RISCV_CPU_SIFIVE_E, + .misa_mxl_max = MXL_RV32, + .misa_ext = RVF, /* IMAFCU */ + ), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_U34, TYPE_RISCV_CPU_SIFIVE_U, + .misa_mxl_max = MXL_RV32, + ), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_RV32I, TYPE_RISCV_BARE_CPU, + .misa_mxl_max = MXL_RV32, + .misa_ext = RVI + ), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_RV32E, TYPE_RISCV_BARE_CPU, + .misa_mxl_max = MXL_RV32, + .misa_ext = RVE + ), +#endif + +#if (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY)) + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_MAX32, TYPE_RISCV_DYNAMIC_CPU, + .cfg.max_satp_mode = VM_1_10_SV32, + .misa_mxl_max = MXL_RV32, + ), +#endif + +#if defined(TARGET_RISCV64) + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_BASE64, TYPE_RISCV_DYNAMIC_CPU, + .cfg.max_satp_mode = VM_1_10_SV57, + .misa_mxl_max = MXL_RV64, + ), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_E51, TYPE_RISCV_CPU_SIFIVE_E, + .misa_mxl_max = MXL_RV64 + ), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_U54, TYPE_RISCV_CPU_SIFIVE_U, + .misa_mxl_max = MXL_RV64, + ), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SHAKTI_C, TYPE_RISCV_CPU_SIFIVE_U, + .misa_mxl_max = MXL_RV64, + ), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_THEAD_C906, TYPE_RISCV_VENDOR_CPU, + .misa_mxl_max = MXL_RV64, + .misa_ext = RVG | RVC | RVS | RVU, + .priv_spec = PRIV_VERSION_1_11_0, + + .cfg.ext_zfa = true, + .cfg.ext_zfh = true, + .cfg.mmu = true, + .cfg.ext_xtheadba = true, + .cfg.ext_xtheadbb = true, + .cfg.ext_xtheadbs = true, + .cfg.ext_xtheadcmo = true, + .cfg.ext_xtheadcondmov = true, + .cfg.ext_xtheadfmemidx = true, + .cfg.ext_xtheadmac = true, + .cfg.ext_xtheadmemidx = true, + .cfg.ext_xtheadmempair = true, + .cfg.ext_xtheadsync = true, + .cfg.pmp = true, + + .cfg.mvendorid = THEAD_VENDOR_ID, + + .cfg.max_satp_mode = VM_1_10_SV39, +#ifndef CONFIG_USER_ONLY + .custom_csrs = th_csr_list, #endif + ), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_TT_ASCALON, TYPE_RISCV_VENDOR_CPU, + .misa_mxl_max = MXL_RV64, + .misa_ext = RVG | RVC | RVS | RVU | RVH | RVV, + .priv_spec = PRIV_VERSION_1_13_0, + .vext_spec = VEXT_VERSION_1_00_0, + + /* ISA extensions */ + .cfg.mmu = true, + .cfg.vlenb = 256 >> 3, + .cfg.elen = 64, + .cfg.rvv_ma_all_1s = true, + .cfg.rvv_ta_all_1s = true, + .cfg.misa_w = true, + .cfg.pmp = true, + .cfg.cbom_blocksize = 64, + .cfg.cbop_blocksize = 64, + .cfg.cboz_blocksize = 64, + .cfg.ext_zic64b = true, + .cfg.ext_zicbom = true, + .cfg.ext_zicbop = true, + .cfg.ext_zicboz = true, + .cfg.ext_zicntr = true, + .cfg.ext_zicond = true, + .cfg.ext_zicsr = true, + .cfg.ext_zifencei = true, + .cfg.ext_zihintntl = true, + .cfg.ext_zihintpause = true, + .cfg.ext_zihpm = true, + .cfg.ext_zimop = true, + .cfg.ext_zawrs = true, + .cfg.ext_zfa = true, + .cfg.ext_zfbfmin = true, + .cfg.ext_zfh = true, + .cfg.ext_zfhmin = true, + .cfg.ext_zcb = true, + .cfg.ext_zcmop = true, + .cfg.ext_zba = true, + .cfg.ext_zbb = true, + .cfg.ext_zbs = true, + .cfg.ext_zkt = true, + .cfg.ext_zvbb = true, + .cfg.ext_zvbc = true, + .cfg.ext_zvfbfmin = true, + .cfg.ext_zvfbfwma = true, + .cfg.ext_zvfh = true, + .cfg.ext_zvfhmin = true, + .cfg.ext_zvkng = true, + .cfg.ext_smaia = true, + .cfg.ext_smstateen = true, + .cfg.ext_ssaia = true, + .cfg.ext_sscofpmf = true, + .cfg.ext_sstc = true, + .cfg.ext_svade = true, + .cfg.ext_svinval = true, + .cfg.ext_svnapot = true, + .cfg.ext_svpbmt = true, + + .cfg.max_satp_mode = VM_1_10_SV57, + ), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_VEYRON_V1, TYPE_RISCV_VENDOR_CPU, + .misa_mxl_max = MXL_RV64, + .misa_ext = RVG | RVC | RVS | RVU | RVH, + .priv_spec = PRIV_VERSION_1_12_0, + + /* ISA extensions */ + .cfg.mmu = true, + .cfg.ext_zifencei = true, + .cfg.ext_zicsr = true, + .cfg.pmp = true, + .cfg.ext_zicbom = true, + .cfg.cbom_blocksize = 64, + .cfg.cboz_blocksize = 64, + .cfg.ext_zicboz = true, + .cfg.ext_smaia = true, + .cfg.ext_ssaia = true, + .cfg.ext_sscofpmf = true, + .cfg.ext_sstc = true, + .cfg.ext_svinval = true, + .cfg.ext_svnapot = true, + .cfg.ext_svpbmt = true, + .cfg.ext_smstateen = true, + .cfg.ext_zba = true, + .cfg.ext_zbb = true, + .cfg.ext_zbc = true, + .cfg.ext_zbs = true, + .cfg.ext_XVentanaCondOps = true, + + .cfg.mvendorid = VEYRON_V1_MVENDORID, + .cfg.marchid = VEYRON_V1_MARCHID, + .cfg.mimpid = VEYRON_V1_MIMPID, + + .cfg.max_satp_mode = VM_1_10_SV48, + ), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_XIANGSHAN_NANHU, TYPE_RISCV_VENDOR_CPU, + .misa_mxl_max = MXL_RV64, + .misa_ext = RVG | RVC | RVB | RVS | RVU, + .priv_spec = PRIV_VERSION_1_12_0, + + /* ISA extensions */ + .cfg.ext_zbc = true, + .cfg.ext_zbkb = true, + .cfg.ext_zbkc = true, + .cfg.ext_zbkx = true, + .cfg.ext_zknd = true, + .cfg.ext_zkne = true, + .cfg.ext_zknh = true, + .cfg.ext_zksed = true, + .cfg.ext_zksh = true, + .cfg.ext_svinval = true, + + .cfg.mmu = true, + .cfg.pmp = true, + + .cfg.max_satp_mode = VM_1_10_SV39, + ), + +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_BASE128, TYPE_RISCV_DYNAMIC_CPU, + .cfg.max_satp_mode = VM_1_10_SV57, + .misa_mxl_max = MXL_RV128, + ), +#endif /* CONFIG_TCG */ + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_RV64I, TYPE_RISCV_BARE_CPU, + .misa_mxl_max = MXL_RV64, + .misa_ext = RVI + ), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_RV64E, TYPE_RISCV_BARE_CPU, + .misa_mxl_max = MXL_RV64, + .misa_ext = RVE + ), + + DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22U64, TYPE_RISCV_CPU_RV64I, RVA22U64), + DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22S64, TYPE_RISCV_CPU_RV64I, RVA22S64), + DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA23U64, TYPE_RISCV_CPU_RV64I, RVA23U64), + DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA23S64, TYPE_RISCV_CPU_RV64I, RVA23S64), +#endif /* TARGET_RISCV64 */ }; DEFINE_TYPES(riscv_cpu_type_infos) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 6316cbcc230..229ade9ed91 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -22,7 +22,11 @@ #include "hw/core/cpu.h" #include "hw/registerfields.h" +#include "hw/qdev-properties.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" +#include "exec/gdbstub.h" #include "qemu/cpu-float.h" #include "qom/object.h" #include "qemu/int128.h" @@ -31,18 +35,25 @@ #include "qapi/qapi-types-common.h" #include "cpu-qom.h" -#define TCG_GUEST_DEFAULT_MO 0 +typedef struct CPUArchState CPURISCVState; + +#define CPU_RESOLVING_TYPE TYPE_RISCV_CPU + +#if defined(TARGET_RISCV32) +# define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE32 +#elif defined(TARGET_RISCV64) +# define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE64 +#endif /* - * RISC-V-specific extra insn start words: - * 1: Original instruction opcode + * b0: Whether a instruction always raise a store AMO or not. */ -#define TARGET_INSN_START_EXTRA_WORDS 1 +#define RISCV_UW2_ALWAYS_STORE_AMO 1 #define RV(x) ((target_ulong)1 << (x - 'A')) /* - * Consider updating misa_ext_info_arr[] and misa_ext_cfgs[] + * Update misa_bits[], misa_ext_info_arr[] and misa_ext_cfgs[] * when adding new MISA bits here. */ #define RVI RV('I') @@ -56,22 +67,49 @@ #define RVS RV('S') #define RVU RV('U') #define RVH RV('H') -#define RVJ RV('J') #define RVG RV('G') +#define RVB RV('B') +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) +#define ENV_CSR_OFFSET(_csr) offsetof(CPURISCVState, _csr) + +typedef struct riscv_cpu_profile { + struct riscv_cpu_profile *u_parent; + struct riscv_cpu_profile *s_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_12_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, @@ -88,6 +126,37 @@ typedef enum { EXT_STATUS_DIRTY, } RISCVExtStatus; +/* Enum holds PMM field values for Zjpm v1.0 extension */ +typedef enum { + PMM_FIELD_DISABLED = 0, + PMM_FIELD_RESERVED = 1, + PMM_FIELD_PMLEN7 = 2, + PMM_FIELD_PMLEN16 = 3, +} RISCVPmPmm; + +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) @@ -117,11 +186,19 @@ typedef struct PMUCTRState { 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 */ @@ -150,12 +227,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 */ @@ -165,12 +240,24 @@ struct CPUArchState { 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. */ bool virt_enabled; target_ulong geilen; @@ -198,6 +285,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; @@ -211,6 +310,15 @@ struct CPUArchState { target_ulong mcause; target_ulong mtval; /* since: priv-1.10.0 */ + uint64_t mctrctl; + uint32_t sctrdepth; + uint32_t sctrstatus; + uint64_t vsctrctl; + + uint64_t ctr_src[16 << SCTRDEPTH_MAX]; + uint64_t ctr_dst[16 << SCTRDEPTH_MAX]; + uint64_t ctr_data[16 << SCTRDEPTH_MAX]; + /* Machine and Supervisor interrupt priorities */ uint8_t miprio[64]; uint8_t siprio[64]; @@ -218,18 +326,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; @@ -278,10 +396,17 @@ struct CPUArchState { */ bool two_stage_indirect_lookup; - target_ulong scounteren; - target_ulong mcounteren; + uint32_t scounteren; + uint32_t mcounteren; - target_ulong mcountinhibit; + uint32_t scountinhibit; + 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]; @@ -292,6 +417,8 @@ struct CPUArchState { /* 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; @@ -309,6 +436,7 @@ 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]; @@ -338,27 +466,11 @@ struct CPUArchState { /* True if in debugger mode. */ bool debugger; - /* - * CSRs for PointerMasking extension - */ - target_ulong mmte; - target_ulong mpmmask; - target_ulong mpmbase; - target_ulong spmmask; - target_ulong spmbase; - target_ulong upmmask; - target_ulong upmbase; - - /* CSRs for execution environment configuration */ - uint64_t menvcfg; uint64_t mstateen[SMSTATEEN_MAX_COUNT]; uint64_t hstateen[SMSTATEEN_MAX_COUNT]; uint64_t sstateen[SMSTATEEN_MAX_COUNT]; - target_ulong senvcfg; uint64_t henvcfg; #endif - target_ulong cur_pmmask; - target_ulong cur_pmbase; /* Fields from here on are preserved across CPU reset. */ QEMUTimer *stimer; /* Internal timer for S-mode interrupt */ @@ -376,8 +488,30 @@ struct CPUArchState { uint64_t kvm_timer_state; uint64_t kvm_timer_frequency; #endif /* CONFIG_KVM */ + + /* RNMI */ + target_ulong mnscratch; + target_ulong mnepc; + target_ulong mncause; /* mncause without bit XLEN-1 set to 1 */ + target_ulong mnstatus; + target_ulong rnmip; + uint64_t rnmi_irqvec; + uint64_t rnmi_excpvec; }; +/* + * 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. + */ +typedef struct { + uint16_t map, init; +} RISCVSATPModes; + /* * RISCVCPU: * @env: #CPURISCVState @@ -385,23 +519,51 @@ struct CPUArchState { * 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; + RISCVSATPModes satp_modes; QEMUTimer *pmu_timer; /* A bitmask of Available programmable counters */ uint32_t pmu_avail_ctrs; /* Mapping of events to counters */ GHashTable *pmu_event_ctr_map; + const GPtrArray *decoders; +}; + +typedef struct RISCVCSR RISCVCSR; + +typedef struct RISCVCPUDef { + RISCVMXL misa_mxl_max; /* max mxl for this cpu */ + RISCVCPUProfile *profile; + uint32_t misa_ext; + int priv_spec; + int32_t vext_spec; + RISCVCPUConfig cfg; + bool bare; + const RISCVCSR *custom_csrs; +} RISCVCPUDef; + +/** + * 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; + RISCVCPUDef *def; }; static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) @@ -416,7 +578,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, @@ -434,7 +595,10 @@ 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); void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable); -int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch); +int riscv_env_mmu_index(CPURISCVState *env, bool ifetch); +bool cpu_get_fcfien(CPURISCVState *env); +bool cpu_get_bcfien(CPURISCVState *env); +bool riscv_env_smode_dbltrp_enabled(CPURISCVState *env, bool virt); G_NORETURN void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); @@ -442,13 +606,12 @@ 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); -void riscv_cpu_list(void); -void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp); - -#define cpu_list riscv_cpu_list -#define cpu_mmu_index riscv_cpu_mmu_index +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, @@ -460,6 +623,8 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env); int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts); uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask, uint64_t value); +void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level); +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); @@ -472,18 +637,25 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, void *rmw_fn_arg); RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit); -#endif -void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv); +#endif /* !CONFIG_USER_ONLY */ + +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en); + +void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst, + enum CTRType type, target_ulong prev_priv, bool prev_virt); +void riscv_ctr_clear(CPURISCVState *env); void riscv_translate_init(void); +void riscv_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); + G_NORETURN void riscv_raise_exception(CPURISCVState *env, - uint32_t exception, uintptr_t pc); + RISCVException exception, + uintptr_t pc); target_ulong riscv_cpu_get_fflags(CPURISCVState *env); void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong); -#include "exec/cpu-all.h" - FIELD(TB_FLAGS, MEM_IDX, 0, 3) FIELD(TB_FLAGS, FS, 3, 2) /* Vector flags */ @@ -498,14 +670,22 @@ FIELD(TB_FLAGS, XL, 16, 2) /* If PointerMasking should be applied */ 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) +FIELD(TB_FLAGS, VTA, 18, 1) +FIELD(TB_FLAGS, VMA, 19, 1) /* Native debug itrigger */ -FIELD(TB_FLAGS, ITRIGGER, 22, 1) +FIELD(TB_FLAGS, ITRIGGER, 20, 1) /* Virtual mode enabled */ -FIELD(TB_FLAGS, VIRT_ENABLED, 23, 1) -FIELD(TB_FLAGS, PRIV, 24, 2) -FIELD(TB_FLAGS, AXL, 26, 2) +FIELD(TB_FLAGS, VIRT_ENABLED, 21, 1) +FIELD(TB_FLAGS, PRIV, 22, 2) +FIELD(TB_FLAGS, AXL, 24, 2) +/* zicfilp needs a TB flag to track indirect branches */ +FIELD(TB_FLAGS, FCFI_ENABLED, 26, 1) +FIELD(TB_FLAGS, FCFI_LP_EXPECTED, 27, 1) +/* zicfiss needs a TB flag so that correct TB is located based on tb flags */ +FIELD(TB_FLAGS, BCFI_ENABLED, 28, 1) +/* If pointer masking should be applied and address sign extended */ +FIELD(TB_FLAGS, PM_PMM, 29, 2) +FIELD(TB_FLAGS, PM_SIGNEXTEND, 31, 1) #ifdef TARGET_RISCV32 #define riscv_cpu_mxl(env) ((void)(env), MXL_RV32) @@ -599,11 +779,26 @@ 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 +static inline bool riscv_cpu_allow_16bit_insn(const RISCVCPUConfig *cfg, + target_long priv_ver, + uint32_t misa_ext) +{ + /* In priv spec version 1.12 or newer, C always implies Zca */ + if (priv_ver >= PRIV_VERSION_1_12_0) { + return cfg->ext_zca; + } else { + return misa_ext & RVC; + } +} + /* * Encode LMUL to lmul as follows: * LMUL vlmul lmul @@ -622,21 +817,32 @@ 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, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags); +bool riscv_cpu_is_32bit(RISCVCPU *cpu); -void riscv_cpu_update_mask(CPURISCVState *env); +bool riscv_cpu_virt_mem_enabled(CPURISCVState *env); +RISCVPmPmm riscv_pm_get_pmm(CPURISCVState *env); +RISCVPmPmm riscv_pm_get_virt_pmm(CPURISCVState *env); +uint32_t riscv_pm_get_pmlen(RISCVPmPmm pmm); + +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); + target_ulong *ret_value, target_ulong new_value, + target_ulong write_mask, uintptr_t ra); RISCVException riscv_csrrw_debug(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, @@ -645,13 +851,13 @@ RISCVException riscv_csrrw_debug(CPURISCVState *env, int csrno, static inline void riscv_csr_write(CPURISCVState *env, int csrno, target_ulong val) { - riscv_csrrw(env, csrno, NULL, val, MAKE_64BIT_MASK(0, TARGET_LONG_BITS)); + riscv_csrrw(env, csrno, NULL, val, MAKE_64BIT_MASK(0, TARGET_LONG_BITS), 0); } static inline target_ulong riscv_csr_read(CPURISCVState *env, int csrno) { target_ulong val = 0; - riscv_csrrw(env, csrno, &val, 0, 0); + riscv_csrrw(env, csrno, &val, 0, 0, 0); return val; } @@ -660,15 +866,18 @@ typedef RISCVException (*riscv_csr_predicate_fn)(CPURISCVState *env, typedef RISCVException (*riscv_csr_read_fn)(CPURISCVState *env, int csrno, target_ulong *ret_value); typedef RISCVException (*riscv_csr_write_fn)(CPURISCVState *env, int csrno, - target_ulong new_value); + target_ulong new_value, + uintptr_t ra); typedef RISCVException (*riscv_csr_op_fn)(CPURISCVState *env, int csrno, target_ulong *ret_value, 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); + Int128 *ret_value, Int128 new_value, + Int128 write_mask, uintptr_t ra); typedef RISCVException (*riscv_csr_read128_fn)(CPURISCVState *env, int csrno, Int128 *ret_value); @@ -687,6 +896,12 @@ typedef struct { uint32_t min_priv_ver; } riscv_csr_operations; +struct RISCVCSR { + int csrno; + bool (*insertion_test)(RISCVCPU *cpu); + riscv_csr_operations csr_ops; +}; + /* CSR function table constants */ enum { CSR_TABLE_SIZE = 0x1000 @@ -705,17 +920,53 @@ 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_set_csr_ops(int csrno, const riscv_csr_operations *ops); void riscv_cpu_register_gdb_regs_for_features(CPUState *cs); -uint8_t satp_mode_max_from_map(uint32_t map); +target_ulong riscv_new_csr_seed(target_ulong new_value, + target_ulong write_mask); + const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit); +/* In th_csr.c */ +extern const RISCVCSR th_csr_list[]; + +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 3d6ffaabc79..a30317c6178 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -32,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 @@ -164,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 @@ -176,6 +173,13 @@ #define CSR_MISELECT 0x350 #define CSR_MIREG 0x351 +/* Machine Indirect Register Alias */ +#define CSR_MIREG2 0x352 +#define CSR_MIREG3 0x353 +#define CSR_MIREG4 0x355 +#define CSR_MIREG5 0x356 +#define CSR_MIREG6 0x357 + /* Machine-Level Interrupts (AIA) */ #define CSR_MTOPEI 0x35c #define CSR_MTOPI 0xfb0 @@ -206,6 +210,9 @@ #define CSR_SSTATEEN2 0x10E #define CSR_SSTATEEN3 0x10F +/* Supervisor Counter Delegation */ +#define CSR_SCOUNTINHIBIT 0x120 + /* Supervisor Trap Handling */ #define CSR_SSCRATCH 0x140 #define CSR_SEPC 0x141 @@ -225,6 +232,13 @@ #define CSR_SISELECT 0x150 #define CSR_SIREG 0x151 +/* Supervisor Indirect Register Alias */ +#define CSR_SIREG2 0x152 +#define CSR_SIREG3 0x153 +#define CSR_SIREG4 0x155 +#define CSR_SIREG5 0x156 +#define CSR_SIREG6 0x157 + /* Supervisor-Level Interrupts (AIA) */ #define CSR_STOPEI 0x15c #define CSR_STOPI 0xdb0 @@ -233,6 +247,17 @@ #define CSR_SIEH 0x114 #define CSR_SIPH 0x154 +/* Machine-Level Control transfer records CSRs */ +#define CSR_MCTRCTL 0x34e + +/* Supervisor-Level Control transfer records CSRs */ +#define CSR_SCTRCTL 0x14e +#define CSR_SCTRSTATUS 0x14f +#define CSR_SCTRDEPTH 0x15f + +/* VS-Level Control transfer records CSRs */ +#define CSR_VSCTRCTL 0x24e + /* Hpervisor CSRs */ #define CSR_HSTATUS 0x600 #define CSR_HEDELEG 0x602 @@ -291,6 +316,13 @@ #define CSR_VSISELECT 0x250 #define CSR_VSIREG 0x251 +/* Virtual Supervisor Indirect Alias */ +#define CSR_VSIREG2 0x252 +#define CSR_VSIREG3 0x253 +#define CSR_VSIREG4 0x255 +#define CSR_VSIREG5 0x256 +#define CSR_VSIREG6 0x257 + /* VS-Level Interrupts (H-extension with AIA) */ #define CSR_VSTOPEI 0x25c #define CSR_VSTOPI 0xeb0 @@ -323,6 +355,8 @@ #define SMSTATEEN0_CS (1ULL << 0) #define SMSTATEEN0_FCSR (1ULL << 1) #define SMSTATEEN0_JVT (1ULL << 2) +#define SMSTATEEN0_CTR (1ULL << 54) +#define SMSTATEEN0_P1P13 (1ULL << 56) #define SMSTATEEN0_HSCONTXT (1ULL << 57) #define SMSTATEEN0_IMSIC (1ULL << 58) #define SMSTATEEN0_AIA (1ULL << 59) @@ -355,12 +389,19 @@ #define CSR_PMPADDR14 0x3be #define CSR_PMPADDR15 0x3bf +/* RNMI */ +#define CSR_MNSCRATCH 0x740 +#define CSR_MNEPC 0x741 +#define CSR_MNCAUSE 0x742 +#define CSR_MNSTATUS 0x744 + /* Debug/Trace Registers (shared with Debug Mode) */ #define CSR_TSELECT 0x7a0 #define CSR_TDATA1 0x7a1 #define CSR_TDATA2 0x7a2 #define CSR_TDATA3 0x7a3 #define CSR_TINFO 0x7a4 +#define CSR_MCONTEXT 0x7a8 /* Debug Mode Registers */ #define CSR_DCSR 0x7b0 @@ -401,6 +442,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 @@ -431,6 +476,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 @@ -491,37 +539,6 @@ #define CSR_MHPMCOUNTER30H 0xb9e #define CSR_MHPMCOUNTER31H 0xb9f -/* - * User PointerMasking registers - * NB: actual CSR numbers might be changed in future - */ -#define CSR_UMTE 0x4c0 -#define CSR_UPMMASK 0x4c1 -#define CSR_UPMBASE 0x4c2 - -/* - * Machine PointerMasking registers - * NB: actual CSR numbers might be changed in future - */ -#define CSR_MMTE 0x3c0 -#define CSR_MPMMASK 0x3c1 -#define CSR_MPMBASE 0x3c2 - -/* - * Supervisor PointerMaster registers - * NB: actual CSR numbers might be changed in future - */ -#define CSR_SMTE 0x1c0 -#define CSR_SPMMASK 0x1c1 -#define CSR_SPMBASE 0x1c2 - -/* - * Hypervisor PointerMaster registers - * NB: actual CSR numbers might be changed in future - */ -#define CSR_VSMTE 0x2c0 -#define CSR_VSPMMASK 0x2c1 -#define CSR_VSPMBASE 0x2c2 #define CSR_SCOUNTOVF 0xda0 /* Crypto Extension */ @@ -549,8 +566,12 @@ #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_SDT 0x01000000 +#define MSTATUS_MPELP 0x020000000000 /* zicfilp */ #define MSTATUS_GVA 0x4000000000ULL #define MSTATUS_MPV 0x8000000000ULL +#define MSTATUS_MDT 0x40000000000ULL /* Smdbltrp extension */ #define MSTATUS64_UXL 0x0000000300000000ULL #define MSTATUS64_SXL 0x0000000C00000000ULL @@ -579,6 +600,8 @@ 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 SSTATUS_SDT MSTATUS_SDT #define SSTATUS64_UXL 0x0000000300000000ULL @@ -595,7 +618,9 @@ typedef enum { #define HSTATUS_VTVM 0x00100000 #define HSTATUS_VTW 0x00200000 #define HSTATUS_VTSR 0x00400000 +#define HSTATUS_HUKTE 0x01000000 #define HSTATUS_VSXL 0x300000000 +#define HSTATUS_HUPMM 0x3000000000000 #define HSTATUS32_WPRI 0xFF8FF87E #define HSTATUS64_WPRI 0xFFFFFFFFFF8FF87EULL @@ -624,6 +649,12 @@ typedef enum { #define SATP64_ASID 0x0FFFF00000000000ULL #define SATP64_PPN 0x00000FFFFFFFFFFFULL +/* RNMI mnstatus CSR mask */ +#define MNSTATUS_NMIE 0x00000008 +#define MNSTATUS_MNPV 0x00000080 +#define MNSTATUS_MNPELP 0x00000200 +#define MNSTATUS_MNPP 0x00001800 + /* VM modes (satp.mode) privileged ISA 1.10 */ #define VM_1_10_MBARE 0 #define VM_1_10_SV32 1 @@ -659,6 +690,12 @@ typedef enum { /* Default Reset Vector address */ #define DEFAULT_RSTVEC 0x1000 +/* Default RNMI Interrupt Vector address */ +#define DEFAULT_RNMI_IRQVEC 0x0 + +/* Default RNMI Exception Vector address */ +#define DEFAULT_RNMI_EXCPVEC 0x0 + /* Exception causes */ typedef enum RISCVException { RISCV_EXCP_NONE = -1, /* sentinel value */ @@ -677,13 +714,21 @@ 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_DOUBLE_TRAP = 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 @@ -702,9 +747,13 @@ 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) +/* RNMI causes */ +#define RNMI_MAX 16 + /* mip masks */ #define MIP_USIP (1 << IRQ_U_SOFT) #define MIP_SSIP (1 << IRQ_S_SOFT) @@ -735,39 +784,55 @@ typedef enum RISCVException { #define MIE_SSIE (1 << IRQ_S_SOFT) #define MIE_USIE (1 << IRQ_U_SOFT) -/* General PointerMasking CSR bits */ -#define PM_ENABLE 0x00000001ULL -#define PM_CURRENT 0x00000002ULL -#define PM_INSN 0x00000004ULL +/* 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)) /* 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_PMM (3ULL << 32) +#define MENVCFG_DTE (1ULL << 59) +#define MENVCFG_CDE (1ULL << 60) #define MENVCFG_ADUE (1ULL << 61) #define MENVCFG_PBMTE (1ULL << 62) #define MENVCFG_STCE (1ULL << 63) /* For RV32 */ +#define MENVCFGH_DTE BIT(27) #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 SENVCFG_UKTE BIT(8) +#define SENVCFG_PMM MENVCFG_PMM #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_PMM MENVCFG_PMM +#define HENVCFG_DTE MENVCFG_DTE #define HENVCFG_ADUE MENVCFG_ADUE #define HENVCFG_PBMTE MENVCFG_PBMTE #define HENVCFG_STCE MENVCFG_STCE /* For RV32 */ +#define HENVCFGH_DTE MENVCFGH_DTE #define HENVCFGH_ADUE MENVCFGH_ADUE #define HENVCFGH_PBMTE MENVCFGH_PBMTE #define HENVCFGH_STCE MENVCFGH_STCE @@ -823,6 +888,88 @@ typedef enum RISCVException { #define UMTE_U_PM_INSN U_PM_INSN #define UMTE_MASK (UMTE_U_PM_ENABLE | MMTE_U_PM_CURRENT | UMTE_U_PM_INSN) +/* CTR control register commom fields */ +#define XCTRCTL_U BIT_ULL(0) +#define XCTRCTL_S BIT_ULL(1) +#define XCTRCTL_RASEMU BIT_ULL(7) +#define XCTRCTL_STE BIT_ULL(8) +#define XCTRCTL_BPFRZ BIT_ULL(11) +#define XCTRCTL_LCOFIFRZ BIT_ULL(12) +#define XCTRCTL_EXCINH BIT_ULL(33) +#define XCTRCTL_INTRINH BIT_ULL(34) +#define XCTRCTL_TRETINH BIT_ULL(35) +#define XCTRCTL_NTBREN BIT_ULL(36) +#define XCTRCTL_TKBRINH BIT_ULL(37) +#define XCTRCTL_INDCALLINH BIT_ULL(40) +#define XCTRCTL_DIRCALLINH BIT_ULL(41) +#define XCTRCTL_INDJMPINH BIT_ULL(42) +#define XCTRCTL_DIRJMPINH BIT_ULL(43) +#define XCTRCTL_CORSWAPINH BIT_ULL(44) +#define XCTRCTL_RETINH BIT_ULL(45) +#define XCTRCTL_INDLJMPINH BIT_ULL(46) +#define XCTRCTL_DIRLJMPINH BIT_ULL(47) + +#define XCTRCTL_MASK (XCTRCTL_U | XCTRCTL_S | XCTRCTL_RASEMU | \ + XCTRCTL_STE | XCTRCTL_BPFRZ | XCTRCTL_LCOFIFRZ | \ + XCTRCTL_EXCINH | XCTRCTL_INTRINH | XCTRCTL_TRETINH | \ + XCTRCTL_NTBREN | XCTRCTL_TKBRINH | XCTRCTL_INDCALLINH | \ + XCTRCTL_DIRCALLINH | XCTRCTL_INDJMPINH | \ + XCTRCTL_DIRJMPINH | XCTRCTL_CORSWAPINH | \ + XCTRCTL_RETINH | XCTRCTL_INDLJMPINH | XCTRCTL_DIRLJMPINH) + +#define XCTRCTL_INH_START 32U + +/* CTR mctrctl bits */ +#define MCTRCTL_M BIT_ULL(2) +#define MCTRCTL_MTE BIT_ULL(9) + +#define MCTRCTL_MASK (XCTRCTL_MASK | MCTRCTL_M | MCTRCTL_MTE) +#define SCTRCTL_MASK XCTRCTL_MASK +#define VSCTRCTL_MASK XCTRCTL_MASK + +/* sctrstatus CSR bits. */ +#define SCTRSTATUS_WRPTR_MASK 0xFF +#define SCTRSTATUS_FROZEN BIT(31) +#define SCTRSTATUS_MASK (SCTRSTATUS_WRPTR_MASK | SCTRSTATUS_FROZEN) + +/* sctrdepth CSR bits. */ +#define SCTRDEPTH_MASK 0x7 +#define SCTRDEPTH_MIN 0U /* 16 Entries. */ +#define SCTRDEPTH_MAX 4U /* 256 Entries. */ + +#define CTR_ENTRIES_FIRST 0x200 +#define CTR_ENTRIES_LAST 0x2ff + +#define CTRSOURCE_VALID BIT(0) +#define CTRTARGET_MISP BIT(0) + +#define CTRDATA_TYPE_MASK 0xF +#define CTRDATA_CCV BIT(15) +#define CTRDATA_CCM_MASK 0xFFF0000 +#define CTRDATA_CCE_MASK 0xF0000000 + +#define CTRDATA_MASK (CTRDATA_TYPE_MASK | CTRDATA_CCV | \ + CTRDATA_CCM_MASK | CTRDATA_CCE_MASK) + +typedef enum CTRType { + CTRDATA_TYPE_NONE = 0, + CTRDATA_TYPE_EXCEPTION = 1, + CTRDATA_TYPE_INTERRUPT = 2, + CTRDATA_TYPE_EXCEP_INT_RET = 3, + CTRDATA_TYPE_NONTAKEN_BRANCH = 4, + CTRDATA_TYPE_TAKEN_BRANCH = 5, + CTRDATA_TYPE_RESERVED_0 = 6, + CTRDATA_TYPE_RESERVED_1 = 7, + CTRDATA_TYPE_INDIRECT_CALL = 8, + CTRDATA_TYPE_DIRECT_CALL = 9, + CTRDATA_TYPE_INDIRECT_JUMP = 10, + CTRDATA_TYPE_DIRECT_JUMP = 11, + CTRDATA_TYPE_CO_ROUTINE_SWAP = 12, + CTRDATA_TYPE_RETURN = 13, + CTRDATA_TYPE_OTHER_INDIRECT_JUMP = 14, + CTRDATA_TYPE_OTHER_DIRECT_JUMP = 15, +} CTRType; + /* MISELECT, SISELECT, and VSISELECT bits (AIA) */ #define ISELECT_IPRIO0 0x30 #define ISELECT_IPRIO15 0x3f @@ -834,10 +981,15 @@ typedef enum RISCVException { #define ISELECT_IMSIC_EIE63 0xff #define ISELECT_IMSIC_FIRST ISELECT_IMSIC_EIDELIVERY #define ISELECT_IMSIC_LAST ISELECT_IMSIC_EIE63 -#define ISELECT_MASK 0x1ff +#define ISELECT_MASK_AIA 0x1ff + +/* [M|S|VS]SELCT value for Indirect CSR Access Extension */ +#define ISELECT_CD_FIRST 0x40 +#define ISELECT_CD_LAST 0x5f +#define ISELECT_MASK_SXCSRIND 0xfff /* Dummy [M|S|VS]ISELECT value for emulating [M|S|VS]TOPEI CSRs */ -#define ISELECT_IMSIC_TOPEI (ISELECT_MASK + 1) +#define ISELECT_IMSIC_TOPEI (ISELECT_MASK_AIA + 1) /* IMSIC bits (AIA) */ #define IMSIC_TOPEI_IID_SHIFT 16 @@ -879,6 +1031,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) @@ -892,11 +1066,41 @@ typedef enum RISCVException { #define MHPMEVENT_BIT_VUINH BIT_ULL(58) #define MHPMEVENTH_BIT_VUINH BIT(26) -#define MHPMEVENT_SSCOF_MASK _ULL(0xFFFF000000000000) -#define MHPMEVENT_IDX_MASK 0xFFFFF -#define MHPMEVENT_SSCOF_RESVD 16 +#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 MAKE_64BIT_MASK(63, 56) +#define MHPMEVENT_IDX_MASK (~MHPMEVENT_SSCOF_MASK) + +/* RISC-V-specific interrupt pending bits. */ +#define CPU_INTERRUPT_RNMI CPU_INTERRUPT_TGT_EXT_0 /* 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 index 0e6a0f245c3..aa28dc8d7e6 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -21,128 +21,10 @@ #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_ifencei; - bool ext_icsr; - bool ext_icbom; - bool ext_icboz; - bool ext_zicond; - bool ext_zihintntl; - bool ext_zihintpause; - bool ext_smstateen; - bool ext_sstc; - bool ext_svadu; - bool ext_svinval; - bool ext_svnapot; - bool ext_svpbmt; - bool ext_zdinx; - bool ext_zawrs; - bool ext_zfa; - bool ext_zfbfmin; - bool ext_zfh; - bool ext_zfhmin; - bool ext_zfinx; - bool ext_zhinx; - bool ext_zhinxmin; - bool ext_zve32f; - bool ext_zve64f; - bool ext_zve64d; - bool ext_zvbb; - bool ext_zvbc; - bool ext_zvkg; - bool ext_zvkned; - bool ext_zvknha; - bool ext_zvknhb; - bool ext_zvksed; - bool ext_zvksh; - 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 rvv_ta_all_1s; - bool rvv_ma_all_1s; - - uint32_t mvendorid; - uint64_t marchid; - uint64_t mimpid; - - /* 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; - - uint8_t pmu_num; - char *priv_spec; - char *user_spec; - char *bext_spec; - char *vext_spec; - uint16_t vlen; - uint16_t elen; - uint16_t cbom_blocksize; - uint16_t cboz_blocksize; - bool mmu; - bool pmp; - bool epmp; - bool debug; - bool misa_w; - - bool short_isa_string; - -#ifndef CONFIG_USER_ONLY - RISCVSATPMap satp_mode; -#endif +#define BOOL_FIELD(x) bool x; +#define TYPED_FIELD(type, x, default) type x; +#include "cpu_cfg_fields.h.inc" }; typedef struct RISCVCPUConfig RISCVCPUConfig; diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc new file mode 100644 index 00000000000..59f134a4192 --- /dev/null +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -0,0 +1,170 @@ +/* + * Required definitions before including this file: + * + * #define BOOL_FIELD(x) + * #define TYPED_FIELD(type, x, default) + */ + +BOOL_FIELD(ext_zba) +BOOL_FIELD(ext_zbb) +BOOL_FIELD(ext_zbc) +BOOL_FIELD(ext_zbkb) +BOOL_FIELD(ext_zbkc) +BOOL_FIELD(ext_zbkx) +BOOL_FIELD(ext_zbs) +BOOL_FIELD(ext_zca) +BOOL_FIELD(ext_zcb) +BOOL_FIELD(ext_zcd) +BOOL_FIELD(ext_zce) +BOOL_FIELD(ext_zcf) +BOOL_FIELD(ext_zcmp) +BOOL_FIELD(ext_zcmt) +BOOL_FIELD(ext_zk) +BOOL_FIELD(ext_zkn) +BOOL_FIELD(ext_zknd) +BOOL_FIELD(ext_zkne) +BOOL_FIELD(ext_zknh) +BOOL_FIELD(ext_zkr) +BOOL_FIELD(ext_zks) +BOOL_FIELD(ext_zksed) +BOOL_FIELD(ext_zksh) +BOOL_FIELD(ext_zkt) +BOOL_FIELD(ext_zifencei) +BOOL_FIELD(ext_zicntr) +BOOL_FIELD(ext_zicsr) +BOOL_FIELD(ext_zicbom) +BOOL_FIELD(ext_zicbop) +BOOL_FIELD(ext_zicboz) +BOOL_FIELD(ext_zicfilp) +BOOL_FIELD(ext_zicfiss) +BOOL_FIELD(ext_zicond) +BOOL_FIELD(ext_zihintntl) +BOOL_FIELD(ext_zihintpause) +BOOL_FIELD(ext_zihpm) +BOOL_FIELD(ext_zimop) +BOOL_FIELD(ext_zcmop) +BOOL_FIELD(ext_ztso) +BOOL_FIELD(ext_smstateen) +BOOL_FIELD(ext_sstc) +BOOL_FIELD(ext_smcdeleg) +BOOL_FIELD(ext_ssccfg) +BOOL_FIELD(ext_smcntrpmf) +BOOL_FIELD(ext_smcsrind) +BOOL_FIELD(ext_sscsrind) +BOOL_FIELD(ext_ssdbltrp) +BOOL_FIELD(ext_smdbltrp) +BOOL_FIELD(ext_svadu) +BOOL_FIELD(ext_svinval) +BOOL_FIELD(ext_svnapot) +BOOL_FIELD(ext_svpbmt) +BOOL_FIELD(ext_svvptc) +BOOL_FIELD(ext_svukte) +BOOL_FIELD(ext_zdinx) +BOOL_FIELD(ext_zaamo) +BOOL_FIELD(ext_zacas) +BOOL_FIELD(ext_zama16b) +BOOL_FIELD(ext_zabha) +BOOL_FIELD(ext_zalrsc) +BOOL_FIELD(ext_zawrs) +BOOL_FIELD(ext_zfa) +BOOL_FIELD(ext_zfbfmin) +BOOL_FIELD(ext_zfh) +BOOL_FIELD(ext_zfhmin) +BOOL_FIELD(ext_zfinx) +BOOL_FIELD(ext_zhinx) +BOOL_FIELD(ext_zhinxmin) +BOOL_FIELD(ext_zve32f) +BOOL_FIELD(ext_zve32x) +BOOL_FIELD(ext_zve64f) +BOOL_FIELD(ext_zve64d) +BOOL_FIELD(ext_zve64x) +BOOL_FIELD(ext_zvbb) +BOOL_FIELD(ext_zvbc) +BOOL_FIELD(ext_zvkb) +BOOL_FIELD(ext_zvkg) +BOOL_FIELD(ext_zvkned) +BOOL_FIELD(ext_zvknha) +BOOL_FIELD(ext_zvknhb) +BOOL_FIELD(ext_zvksed) +BOOL_FIELD(ext_zvksh) +BOOL_FIELD(ext_zvkt) +BOOL_FIELD(ext_zvkn) +BOOL_FIELD(ext_zvknc) +BOOL_FIELD(ext_zvkng) +BOOL_FIELD(ext_zvks) +BOOL_FIELD(ext_zvksc) +BOOL_FIELD(ext_zvksg) +BOOL_FIELD(ext_zmmul) +BOOL_FIELD(ext_zvfbfmin) +BOOL_FIELD(ext_zvfbfwma) +BOOL_FIELD(ext_zvfh) +BOOL_FIELD(ext_zvfhmin) +BOOL_FIELD(ext_smaia) +BOOL_FIELD(ext_ssaia) +BOOL_FIELD(ext_smctr) +BOOL_FIELD(ext_ssctr) +BOOL_FIELD(ext_sscofpmf) +BOOL_FIELD(ext_smepmp) +BOOL_FIELD(ext_smrnmi) +BOOL_FIELD(ext_ssnpm) +BOOL_FIELD(ext_smnpm) +BOOL_FIELD(ext_smmpm) +BOOL_FIELD(ext_sspm) +BOOL_FIELD(ext_supm) +BOOL_FIELD(rvv_ta_all_1s) +BOOL_FIELD(rvv_ma_all_1s) +BOOL_FIELD(rvv_vl_half_avl) +/* Named features */ +BOOL_FIELD(ext_svade) +BOOL_FIELD(ext_zic64b) +BOOL_FIELD(ext_ssstateen) +BOOL_FIELD(ext_sha) + +/* + * Always 'true' booleans for named features + * TCG always implement/can't be user disabled, + * based on spec version. + */ +BOOL_FIELD(has_priv_1_13) +BOOL_FIELD(has_priv_1_12) +BOOL_FIELD(has_priv_1_11) + +/* Always enabled for TCG if has_priv_1_11 */ +BOOL_FIELD(ext_ziccrse) + +/* Vendor-specific custom extensions */ +BOOL_FIELD(ext_xtheadba) +BOOL_FIELD(ext_xtheadbb) +BOOL_FIELD(ext_xtheadbs) +BOOL_FIELD(ext_xtheadcmo) +BOOL_FIELD(ext_xtheadcondmov) +BOOL_FIELD(ext_xtheadfmemidx) +BOOL_FIELD(ext_xtheadfmv) +BOOL_FIELD(ext_xtheadmac) +BOOL_FIELD(ext_xtheadmemidx) +BOOL_FIELD(ext_xtheadmempair) +BOOL_FIELD(ext_xtheadsync) +BOOL_FIELD(ext_XVentanaCondOps) + +BOOL_FIELD(mmu) +BOOL_FIELD(pmp) +BOOL_FIELD(debug) +BOOL_FIELD(misa_w) + +BOOL_FIELD(short_isa_string) + +TYPED_FIELD(uint32_t, mvendorid, 0) +TYPED_FIELD(uint64_t, marchid, 0) +TYPED_FIELD(uint64_t, mimpid, 0) + +TYPED_FIELD(uint32_t, pmu_mask, 0) +TYPED_FIELD(uint16_t, vlenb, 0) +TYPED_FIELD(uint16_t, elen, 0) +TYPED_FIELD(uint16_t, cbom_blocksize, 0) +TYPED_FIELD(uint16_t, cbop_blocksize, 0) +TYPED_FIELD(uint16_t, cboz_blocksize, 0) + +TYPED_FIELD(int8_t, max_satp_mode, -1) + +#undef BOOL_FIELD +#undef TYPED_FIELD diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 3a02079290f..2ed69d7c2d2 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -23,17 +23,21 @@ #include "cpu.h" #include "internals.h" #include "pmu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" +#include "system/memory.h" #include "instmap.h" #include "tcg/tcg-op.h" +#include "accel/tcg/cpu-ops.h" #include "trace.h" #include "semihosting/common-semi.h" -#include "sysemu/cpu-timers.h" +#include "exec/icount.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; @@ -62,132 +66,169 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) #endif } -void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) +bool cpu_get_fcfien(CPURISCVState *env) { - CPUState *cs = env_cpu(env); - RISCVCPU *cpu = RISCV_CPU(cs); - RISCVExtStatus fs, vs; - uint32_t flags = 0; + /* 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(); + } +} - *pc = env->xl == MXL_RV32 ? env->pc & UINT32_MAX : env->pc; - *cs_base = 0; +bool cpu_get_bcfien(CPURISCVState *env) +{ + /* no cfi extension, return false */ + if (!env_archcpu(env)->cfg.ext_zicfiss) { + return false; + } - if (cpu->cfg.ext_zve32f) { + switch (env->priv) { + case PRV_U: /* - * If env->vl equals to VLMAX, we can use generic vector operation - * expanders (GVEC) to accerlate the vector operations. - * However, as LMUL could be a fractional number. The maximum - * vector size can be operated might be less than 8 bytes, - * which is not supported by GVEC. So we set vl_eq_vlmax flag to true - * only when maxsz >= 8 bytes. + * 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 */ - uint32_t vlmax = vext_get_vlmax(cpu, env->vtype); - uint32_t sew = FIELD_EX64(env->vtype, VTYPE, VSEW); - uint32_t maxsz = vlmax << sew; - 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, LMUL, - FIELD_EX64(env->vtype, VTYPE, VLMUL)); - flags = FIELD_DP32(flags, TB_FLAGS, VL_EQ_VLMAX, vl_eq_vlmax); - flags = FIELD_DP32(flags, TB_FLAGS, VTA, - FIELD_EX64(env->vtype, VTYPE, VTA)); - flags = FIELD_DP32(flags, TB_FLAGS, VMA, - FIELD_EX64(env->vtype, VTYPE, VMA)); - flags = FIELD_DP32(flags, TB_FLAGS, VSTART_EQ_ZERO, env->vstart == 0); - } else { - flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1); + 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(); } +} +bool riscv_env_smode_dbltrp_enabled(CPURISCVState *env, bool virt) +{ #ifdef CONFIG_USER_ONLY - fs = EXT_STATUS_DIRTY; - vs = EXT_STATUS_DIRTY; + return false; #else - flags = FIELD_DP32(flags, TB_FLAGS, PRIV, env->priv); - - flags |= cpu_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 (virt) { + return (env->henvcfg & HENVCFG_DTE) != 0; + } else { + return (env->menvcfg & MENVCFG_DTE) != 0; } +#endif +} - /* 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; - } +RISCVPmPmm riscv_pm_get_pmm(CPURISCVState *env) +{ +#ifndef CONFIG_USER_ONLY + int priv_mode = cpu_address_mode(env); - if (cpu->cfg.debug && !icount_enabled()) { - flags = FIELD_DP32(flags, TB_FLAGS, ITRIGGER, env->itrigger_enabled); + if (get_field(env->mstatus, MSTATUS_MPRV) && + get_field(env->mstatus, MSTATUS_MXR)) { + return PMM_FIELD_DISABLED; } -#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); - 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) { - flags = FIELD_DP32(flags, TB_FLAGS, PM_BASE_ENABLED, 1); + /* Get current PMM field */ + switch (priv_mode) { + case PRV_M: + if (riscv_cpu_cfg(env)->ext_smmpm) { + return get_field(env->mseccfg, MSECCFG_PMM); + } + break; + case PRV_S: + if (riscv_cpu_cfg(env)->ext_smnpm) { + if (get_field(env->mstatus, MSTATUS_MPV)) { + return get_field(env->henvcfg, HENVCFG_PMM); + } else { + return get_field(env->menvcfg, MENVCFG_PMM); + } + } + break; + case PRV_U: + if (riscv_has_ext(env, RVS)) { + if (riscv_cpu_cfg(env)->ext_ssnpm) { + return get_field(env->senvcfg, SENVCFG_PMM); + } + } else { + if (riscv_cpu_cfg(env)->ext_smnpm) { + return get_field(env->menvcfg, MENVCFG_PMM); + } + } + break; + default: + g_assert_not_reached(); } - - *pflags = flags; + return PMM_FIELD_DISABLED; +#else + return PMM_FIELD_DISABLED; +#endif } -void riscv_cpu_update_mask(CPURISCVState *env) +RISCVPmPmm riscv_pm_get_virt_pmm(CPURISCVState *env) { - 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 (mode) { - case PRV_M: - if (env->mmte & M_PM_ENABLE) { - mask = env->mpmmask; - base = env->mpmbase; - } - break; - case PRV_S: - if (env->mmte & S_PM_ENABLE) { - mask = env->spmmask; - base = env->spmbase; - } - break; - case PRV_U: - if (env->mmte & U_PM_ENABLE) { - mask = env->upmmask; - base = env->upmbase; - } - break; - default: - g_assert_not_reached(); + int priv_mode = cpu_address_mode(env); + + if (priv_mode == PRV_U) { + return get_field(env->hstatus, HSTATUS_HUPMM); + } else { + if (get_field(env->hstatus, HSTATUS_SPVP)) { + return get_field(env->henvcfg, HENVCFG_PMM); + } else { + return get_field(env->senvcfg, SENVCFG_PMM); } } +#else + return PMM_FIELD_DISABLED; #endif - if (xl == MXL_RV32) { - env->cur_pmmask = mask & UINT32_MAX; - env->cur_pmbase = base & UINT32_MAX; +} + +bool riscv_cpu_virt_mem_enabled(CPURISCVState *env) +{ +#ifndef CONFIG_USER_ONLY + int satp_mode = 0; + int priv_mode = cpu_address_mode(env); + + if (riscv_cpu_mxl(env) == MXL_RV32) { + satp_mode = get_field(env->satp, SATP32_MODE); } else { - env->cur_pmmask = mask; - env->cur_pmbase = base; + satp_mode = get_field(env->satp, SATP64_MODE); + } + + return ((satp_mode != VM_1_10_MBARE) && (priv_mode != PRV_M)); +#else + return false; +#endif +} + +uint32_t riscv_pm_get_pmlen(RISCVPmPmm pmm) +{ + switch (pmm) { + case PMM_FIELD_DISABLED: + return 0; + case PMM_FIELD_PMLEN7: + return 7; + case PMM_FIELD_PMLEN16: + return 16; + default: + g_assert_not_reached(); } } @@ -377,6 +418,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); @@ -399,24 +445,44 @@ 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; + + /* Priority: RNMI > Other interrupt. */ + if (riscv_cpu_cfg(env)->ext_smrnmi) { + /* If mnstatus.NMIE == 0, all interrupts are disabled. */ + if (!get_field(env->mnstatus, MNSTATUS_NMIE)) { + return RISCV_EXCP_NONE; + } + + if (env->rnmip) { + return ctz64(env->rnmip); /* since non-zero */ + } + } /* Determine interrupt enable state of all privilege modes */ if (env->virt_enabled) { @@ -442,19 +508,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 */ @@ -463,7 +546,9 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env) bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { - if (interrupt_request & CPU_INTERRUPT_HARD) { + uint32_t mask = CPU_INTERRUPT_HARD | CPU_INTERRUPT_RNMI; + + if (interrupt_request & mask) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; int interruptno = riscv_cpu_local_irq_pending(env); @@ -513,8 +598,21 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *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)); + if (riscv_env_smode_dbltrp_enabled(env, current_virt)) { + mstatus_mask |= MSTATUS_SDT; + } + if (current_virt) { /* Current V=1 and we are about to change to V=0 */ env->vsstatus = env->mstatus & mstatus_mask; @@ -586,27 +684,27 @@ void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen) env->geilen = geilen; } -/* This function can only be called to set virt when RVH is enabled */ -void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable) +void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level) { - /* Flush the TLB on all virt mode changes. */ - if (env->virt_enabled != enable) { - tlb_flush(env_cpu(env)); + CPURISCVState *env = &cpu->env; + CPUState *cs = CPU(cpu); + bool release_lock = false; + + if (!bql_locked()) { + release_lock = true; + bql_lock(); } - env->virt_enabled = enable; + if (level) { + env->rnmip |= 1 << irq; + cpu_interrupt(cs, CPU_INTERRUPT_RNMI); + } else { + env->rnmip &= ~(1 << irq); + cpu_reset_interrupt(cs, CPU_INTERRUPT_RNMI); + } - 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, 0, 0); + if (release_lock) { + bql_unlock(); } } @@ -621,28 +719,42 @@ int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts) } } -uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask, - uint64_t value) +void riscv_cpu_interrupt(CPURISCVState *env) { + uint64_t gein, vsgein = 0, vstip = 0, irqf = 0; CPUState *cs = env_cpu(env); - uint64_t gein, vsgein = 0, vstip = 0, old = env->mip; + + 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; } vstip = env->vstime_irq ? MIP_VSTIP : 0; - QEMU_IOTHREAD_LOCK_GUARD(); - - 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); } +} + +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; } @@ -668,17 +780,269 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, } } -void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) +static void riscv_ctr_freeze(CPURISCVState *env, uint64_t freeze_mask, + bool virt) +{ + uint64_t ctl = virt ? env->vsctrctl : env->mctrctl; + + assert((freeze_mask & (~(XCTRCTL_BPFRZ | XCTRCTL_LCOFIFRZ))) == 0); + + if (ctl & freeze_mask) { + env->sctrstatus |= SCTRSTATUS_FROZEN; + } +} + +void riscv_ctr_clear(CPURISCVState *env) +{ + memset(env->ctr_src, 0x0, sizeof(env->ctr_src)); + memset(env->ctr_dst, 0x0, sizeof(env->ctr_dst)); + memset(env->ctr_data, 0x0, sizeof(env->ctr_data)); +} + +static uint64_t riscv_ctr_priv_to_mask(target_ulong priv, bool virt) +{ + switch (priv) { + case PRV_M: + return MCTRCTL_M; + case PRV_S: + if (virt) { + return XCTRCTL_S; + } + return XCTRCTL_S; + case PRV_U: + if (virt) { + return XCTRCTL_U; + } + return XCTRCTL_U; + } + + g_assert_not_reached(); +} + +static uint64_t riscv_ctr_get_control(CPURISCVState *env, target_long priv, + bool virt) +{ + switch (priv) { + case PRV_M: + return env->mctrctl; + case PRV_S: + case PRV_U: + if (virt) { + return env->vsctrctl; + } + return env->mctrctl; + } + + g_assert_not_reached(); +} + +/* + * This function assumes that src privilege and target privilege are not same + * and src privilege is less than target privilege. This includes the virtual + * state as well. + */ +static bool riscv_ctr_check_xte(CPURISCVState *env, target_long src_prv, + bool src_virt) +{ + target_long tgt_prv = env->priv; + bool res = true; + + /* + * VS and U mode are same in terms of xTE bits required to record an + * external trap. See 6.1.2. External Traps, table 8 External Trap Enable + * Requirements. This changes VS to U to simplify the logic a bit. + */ + if (src_virt && src_prv == PRV_S) { + src_prv = PRV_U; + } else if (env->virt_enabled && tgt_prv == PRV_S) { + tgt_prv = PRV_U; + } + + /* VU mode is an outlier here. */ + if (src_virt && src_prv == PRV_U) { + res &= !!(env->vsctrctl & XCTRCTL_STE); + } + + switch (src_prv) { + case PRV_U: + if (tgt_prv == PRV_U) { + break; + } + res &= !!(env->mctrctl & XCTRCTL_STE); + /* fall-through */ + case PRV_S: + if (tgt_prv == PRV_S) { + break; + } + res &= !!(env->mctrctl & MCTRCTL_MTE); + /* fall-through */ + case PRV_M: + break; + } + + return res; +} + +/* + * Special cases for traps and trap returns: + * + * 1- Traps, and trap returns, between enabled modes are recorded as normal. + * 2- Traps from an inhibited mode to an enabled mode, and trap returns from an + * enabled mode back to an inhibited mode, are partially recorded. In such + * cases, the PC from the inhibited mode (source PC for traps, and target PC + * for trap returns) is 0. + * + * 3- Trap returns from an inhibited mode to an enabled mode are not recorded. + * Traps from an enabled mode to an inhibited mode, known as external traps, + * receive special handling. + * By default external traps are not recorded, but a handshake mechanism exists + * to allow partial recording. Software running in the target mode of the trap + * can opt-in to allowing CTR to record traps into that mode even when the mode + * is inhibited. The MTE, STE, and VSTE bits allow M-mode, S-mode, and VS-mode, + * respectively, to opt-in. When an External Trap occurs, and xTE=1, such that + * x is the target privilege mode of the trap, will CTR record the trap. In such + * cases, the target PC is 0. + */ +/* + * CTR arrays are implemented as circular buffers and new entry is stored at + * sctrstatus.WRPTR, but they are presented to software as moving circular + * buffers. Which means, software get's the illusion that whenever a new entry + * is added the whole buffer is moved by one place and the new entry is added at + * the start keeping new entry at idx 0 and older ones follow. + * + * Depth = 16. + * + * buffer [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F] + * WRPTR W + * entry 7 6 5 4 3 2 1 0 F E D C B A 9 8 + * + * When a new entry is added: + * buffer [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F] + * WRPTR W + * entry 8 7 6 5 4 3 2 1 0 F E D C B A 9 + * + * entry here denotes the logical entry number that software can access + * using ctrsource, ctrtarget and ctrdata registers. So xiselect 0x200 + * will return entry 0 i-e buffer[8] and 0x201 will return entry 1 i-e + * buffer[7]. Here is how we convert entry to buffer idx. + * + * entry = isel - CTR_ENTRIES_FIRST; + * idx = (sctrstatus.WRPTR - entry - 1) & (depth - 1); + */ +void riscv_ctr_add_entry(CPURISCVState *env, target_long src, target_long dst, + enum CTRType type, target_ulong src_priv, bool src_virt) +{ + bool tgt_virt = env->virt_enabled; + uint64_t src_mask = riscv_ctr_priv_to_mask(src_priv, src_virt); + uint64_t tgt_mask = riscv_ctr_priv_to_mask(env->priv, tgt_virt); + uint64_t src_ctrl = riscv_ctr_get_control(env, src_priv, src_virt); + uint64_t tgt_ctrl = riscv_ctr_get_control(env, env->priv, tgt_virt); + uint64_t depth, head; + bool ext_trap = false; + + /* + * Return immediately if both target and src recording is disabled or if + * CTR is in frozen state. + */ + if ((!(src_ctrl & src_mask) && !(tgt_ctrl & tgt_mask)) || + env->sctrstatus & SCTRSTATUS_FROZEN) { + return; + } + + /* + * With RAS Emul enabled, only allow Indirect, direct calls, Function + * returns and Co-routine swap types. + */ + if (tgt_ctrl & XCTRCTL_RASEMU && + type != CTRDATA_TYPE_INDIRECT_CALL && + type != CTRDATA_TYPE_DIRECT_CALL && + type != CTRDATA_TYPE_RETURN && + type != CTRDATA_TYPE_CO_ROUTINE_SWAP) { + return; + } + + if (type == CTRDATA_TYPE_EXCEPTION || type == CTRDATA_TYPE_INTERRUPT) { + /* Case 2 for traps. */ + if (!(src_ctrl & src_mask)) { + src = 0; + } else if (!(tgt_ctrl & tgt_mask)) { + /* Check if target priv-mode has allowed external trap recording. */ + if (!riscv_ctr_check_xte(env, src_priv, src_virt)) { + return; + } + + ext_trap = true; + dst = 0; + } + } else if (type == CTRDATA_TYPE_EXCEP_INT_RET) { + /* + * Case 3 for trap returns. Trap returns from inhibited mode are not + * recorded. + */ + if (!(src_ctrl & src_mask)) { + return; + } + + /* Case 2 for trap returns. */ + if (!(tgt_ctrl & tgt_mask)) { + dst = 0; + } + } + + /* Ignore filters in case of RASEMU mode or External trap. */ + if (!(tgt_ctrl & XCTRCTL_RASEMU) && !ext_trap) { + /* + * Check if the specific type is inhibited. Not taken branch filter is + * an enable bit and needs to be checked separatly. + */ + bool check = tgt_ctrl & BIT_ULL(type + XCTRCTL_INH_START); + if ((type == CTRDATA_TYPE_NONTAKEN_BRANCH && !check) || + (type != CTRDATA_TYPE_NONTAKEN_BRANCH && check)) { + return; + } + } + + head = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK); + + depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK); + if (tgt_ctrl & XCTRCTL_RASEMU && type == CTRDATA_TYPE_RETURN) { + head = (head - 1) & (depth - 1); + + env->ctr_src[head] &= ~CTRSOURCE_VALID; + env->sctrstatus = + set_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK, head); + return; + } + + /* In case of Co-routine SWAP we overwrite latest entry. */ + if (tgt_ctrl & XCTRCTL_RASEMU && type == CTRDATA_TYPE_CO_ROUTINE_SWAP) { + head = (head - 1) & (depth - 1); + } + + env->ctr_src[head] = src | CTRSOURCE_VALID; + env->ctr_dst[head] = dst & ~CTRTARGET_MISP; + env->ctr_data[head] = set_field(0, CTRDATA_TYPE_MASK, type); + + head = (head + 1) & (depth - 1); + + env->sctrstatus = set_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK, head); +} + +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en) { g_assert(newpriv <= PRV_M && newpriv != PRV_RESERVED); - if (icount_enabled() && newpriv != env->priv) { - riscv_itrigger_update_priv(env); + 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); - riscv_cpu_update_mask(env); /* * Clear the load reservation - otherwise a reservation placed in one @@ -689,6 +1053,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); + } + } } /* @@ -727,6 +1113,55 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr, return TRANSLATE_SUCCESS; } +/* Returns 'true' if a svukte address check is needed */ +static bool do_svukte_check(CPURISCVState *env, bool first_stage, + int mode, bool virt) +{ + /* Svukte extension depends on Sv39. */ + if (!(env_archcpu(env)->cfg.ext_svukte || + !first_stage || + VM_1_10_SV39 != get_field(env->satp, SATP64_MODE))) { + return false; + } + + /* + * Check hstatus.HUKTE if the effective mode is switched to VU-mode by + * executing HLV/HLVX/HSV in U-mode. + * For other cases, check senvcfg.UKTE. + */ + if (env->priv == PRV_U && !env->virt_enabled && virt) { + if (!get_field(env->hstatus, HSTATUS_HUKTE)) { + return false; + } + } else if (!get_field(env->senvcfg, SENVCFG_UKTE)) { + return false; + } + + /* + * Svukte extension is qualified only in U or VU-mode. + * + * Effective mode can be switched to U or VU-mode by: + * - M-mode + mstatus.MPRV=1 + mstatus.MPP=U-mode. + * - Execute HLV/HLVX/HSV from HS-mode + hstatus.SPVP=0. + * - U-mode. + * - VU-mode. + * - Execute HLV/HLVX/HSV from U-mode + hstatus.HU=1. + */ + if (mode != PRV_U) { + return false; + } + + return true; +} + +static bool check_svukte_addr(CPURISCVState *env, vaddr addr) +{ + /* svukte extension excludes RV32 */ + uint32_t sxlen = 32 * riscv_cpu_sxl(env); + uint64_t high_bit = addr & (1UL << (sxlen - 1)); + return !high_bit; +} + /* * get_physical_address - get the physical address for this virtual address * @@ -754,7 +1189,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, 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 @@ -764,10 +1199,18 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, MemTxResult res; MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; int mode = mmuidx_priv(mmu_idx); + bool virt = mmuidx_2stage(mmu_idx); bool use_background = false; hwaddr ppn; 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; + + if (do_svukte_check(env, first_stage, mode, virt) && + !check_svukte_addr(env, addr)) { + return TRANSLATE_FAIL; + } /* * Check if we should use the background registers for the two @@ -840,12 +1283,14 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, CPUState *cs = env_cpu(env); int va_bits = PGSHIFT + levels * ptidxbits + widened; + int sxlen = 16 << riscv_cpu_sxl(env); + int sxlen_bytes = sxlen / 8; if (first_stage == true) { target_ulong mask, masked_msbs; - if (TARGET_LONG_BITS > (va_bits - 1)) { - mask = (1L << (TARGET_LONG_BITS - (va_bits - 1))) - 1; + if (sxlen > (va_bits - 1)) { + mask = (1L << (sxlen - (va_bits - 1))) - 1; } else { mask = 0; } @@ -861,7 +1306,9 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, } bool pbmte = env->menvcfg & MENVCFG_PBMTE; - bool adue = env->menvcfg & MENVCFG_ADUE; + 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); @@ -873,9 +1320,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, hwaddr pte_addr; int i; -#if !TCG_OVERSIZED_GUEST -restart: -#endif + restart: for (i = 0; i < levels; i++, ptshift -= ptidxbits) { target_ulong idx; if (i == 0) { @@ -896,7 +1341,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, int vbase_ret = get_physical_address(env, &vbase, &vbase_prot, base, NULL, MMU_DATA_LOAD, MMUIdx_U, false, true, - is_debug); + is_debug, false); if (vbase_ret != TRANSLATE_SUCCESS) { if (fault_pte_addr) { @@ -912,7 +1357,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, int pmp_prot; int pmp_ret = get_physical_address_pmp(env, &pmp_prot, pte_addr, - sizeof(target_ulong), + sxlen_bytes, MMU_DATA_LOAD, PRV_S); if (pmp_ret != TRANSLATE_SUCCESS) { return TRANSLATE_PMP_FAIL; @@ -932,14 +1377,27 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, ppn = pte >> PTE_PPN_SHIFT; } else { if (pte & PTE_RESERVED) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: reserved bits set in PTE: " + "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", + __func__, pte_addr, pte); return TRANSLATE_FAIL; } if (!pbmte && (pte & PTE_PBMT)) { + /* Reserved without Svpbmt. */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: PBMT bits set in PTE, " + "and Svpbmt extension is disabled: " + "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", + __func__, pte_addr, pte); return TRANSLATE_FAIL; } if (!riscv_cpu_cfg(env)->ext_svnapot && (pte & PTE_N)) { + /* Reserved without Svnapot extension */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: N bit set in PTE, " + "and Svnapot extension is disabled: " + "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", + __func__, pte_addr, pte); return TRANSLATE_FAIL; } @@ -950,14 +1408,19 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, /* Invalid PTE */ return TRANSLATE_FAIL; } + if (pte & (PTE_R | PTE_W | PTE_X)) { goto leaf; } - /* Inner PTE, continue walking */ if (pte & (PTE_D | PTE_A | PTE_U | PTE_ATTR)) { + /* D, A, and U bits are reserved in non-leaf/inner PTEs */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: D, A, or U bits set in non-leaf PTE: " + "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", + __func__, pte_addr, pte); return TRANSLATE_FAIL; } + /* Inner PTE, continue walking */ base = ppn << PGSHIFT; } @@ -967,35 +1430,80 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, leaf: if (ppn & ((1ULL << ptshift) - 1)) { /* Misaligned PPN */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: PPN bits in PTE is misaligned: " + "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", + __func__, pte_addr, pte); return TRANSLATE_FAIL; } if (!pbmte && (pte & PTE_PBMT)) { /* Reserved without Svpbmt. */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: PBMT bits set in PTE, " + "and Svpbmt extension is disabled: " + "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", + __func__, pte_addr, pte); return TRANSLATE_FAIL; } + target_ulong rwx = pte & (PTE_R | PTE_W | PTE_X); /* Check for reserved combinations of RWX flags. */ - switch (pte & (PTE_R | PTE_W | PTE_X)) { - case PTE_W: + 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 (pte & PTE_R) { + if (rwx & PTE_R) { prot |= PAGE_READ; } - if (pte & PTE_W) { + if (rwx & PTE_W) { prot |= PAGE_WRITE; } - if (pte & PTE_X) { - bool mxr; + if (rwx & PTE_X) { + bool mxr = false; - if (first_stage == true) { + /* + * 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); - } else { - mxr = get_field(env->vsstatus, 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; } @@ -1016,13 +1524,25 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, } if (!((prot >> access_type) & 1)) { - /* Access check failed */ - return TRANSLATE_FAIL; + /* + * Access check failed, access check failures for shadow stack are + * access faults. + */ + return sstack_page ? TRANSLATE_PMP_FAIL : TRANSLATE_FAIL; } - /* If necessary, set accessed and dirty bits. */ - target_ulong updated_pte = pte | PTE_A | - (access_type == MMU_DATA_STORE ? PTE_D : 0); + 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) { @@ -1039,24 +1559,23 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, * it is no longer valid and we must re-walk the page table. */ MemoryRegion *mr; - hwaddr l = sizeof(target_ulong), addr1; + 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; -#else - target_ulong old_pte = qatomic_cmpxchg(pte_pa, pte, updated_pte); + target_ulong old_pte; + if (riscv_cpu_sxl(env) == MXL_RV32) { + old_pte = qatomic_cmpxchg((uint32_t *)pte_pa, cpu_to_le32(pte), cpu_to_le32(updated_pte)); + old_pte = le32_to_cpu(old_pte); + } else { + old_pte = qatomic_cmpxchg(pte_pa, cpu_to_le64(pte), cpu_to_le64(updated_pte)); + old_pte = le64_to_cpu(old_pte); + } if (old_pte != pte) { goto restart; } pte = updated_pte; -#endif } else { /* * Misconfigured PTE in ROM (AD bits are not preset) or @@ -1100,47 +1619,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 (env->virt_enabled && !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: @@ -1157,16 +1662,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, env->virt_enabled, true)) { + true, env->virt_enabled, true, false)) { return -1; } 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; } } @@ -1209,9 +1714,17 @@ 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(); @@ -1258,9 +1771,9 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, 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; @@ -1272,7 +1785,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, /* 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. @@ -1295,7 +1808,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, ret = get_physical_address(env, &pa, &prot2, im_address, NULL, access_type, MMUIdx_U, false, true, - false); + false, probe); qemu_log_mask(CPU_LOG_MMU, "%s 2nd-stage address=%" VADDR_PRIx @@ -1312,27 +1825,28 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, qemu_log_mask(CPU_LOG_MMU, "%s PMP address=" HWADDR_FMT_plx " ret %d prot" - " %d tlb_size " TARGET_FMT_lu "\n", + " %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 { /* 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 " @@ -1346,7 +1860,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, qemu_log_mask(CPU_LOG_MMU, "%s PMP address=" HWADDR_FMT_plx " ret %d prot" - " %d tlb_size " TARGET_FMT_lu "\n", + " %d tlb_size %" HWADDR_PRIu "\n", __func__, pa, ret, prot_pmp, tlb_size); prot &= prot_pmp; @@ -1364,6 +1878,23 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } else if (probe) { return false; } else { + int wp_access = 0; + + if (access_type == MMU_DATA_LOAD) { + wp_access |= BP_MEM_READ; + } else if (access_type == MMU_DATA_STORE) { + wp_access |= BP_MEM_WRITE; + } + + /* + * If a watchpoint isn't found for 'addr' this will + * be a no-op and we'll resume the mmu_exception path. + * Otherwise we'll throw a debug exception and execution + * will continue elsewhere. + */ + cpu_check_watchpoint(cs, address, size, MEMTXATTRS_UNSPECIFIED, + wp_access, retaddr); + raise_mmu_exception(env, address, access_type, pmp_violation, first_stage_error, two_stage_lookup, two_stage_indirect_error); @@ -1577,7 +2108,40 @@ 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; +} + +static void riscv_do_nmi(CPURISCVState *env, target_ulong cause, bool virt) +{ + env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, false); + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPV, virt); + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPP, env->priv); + env->mncause = cause; + env->mnepc = env->pc; + env->pc = env->rnmi_irqvec; + + if (cpu_get_fcfien(env)) { + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPELP, env->elp); + } + + /* Trapping to M mode, virt is disabled */ + riscv_cpu_set_mode(env, PRV_M, false); +} /* * Handle Traps @@ -1587,12 +2151,14 @@ 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); + bool vsmode_exc; uint64_t s; + int mode; /* * cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide @@ -1601,20 +2167,38 @@ void riscv_cpu_do_interrupt(CPUState *cs) 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)); + bool smode_double_trap = false; + uint64_t hdeleg = async ? env->hideleg : env->hedeleg; + const bool prev_virt = env->virt_enabled; + const target_ulong prev_priv = env->priv; 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; + target_ulong src; + int sxlen = 0; + int mxlen = 16 << riscv_cpu_mxl(env); + bool nnmi_excep = false; + + if (cpu->cfg.ext_smrnmi && env->rnmip && async) { + riscv_do_nmi(env, cause | ((target_ulong)1U << (mxlen - 1)), + env->virt_enabled); return; } 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: @@ -1623,6 +2207,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) { @@ -1658,11 +2245,15 @@ void riscv_cpu_do_interrupt(CPUState *cs) 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; } @@ -1691,20 +2282,51 @@ 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)) { - /* handle the trap in S-mode */ + mode = env->priv <= PRV_S && cause < 64 && + (((deleg >> cause) & 1) || s_injected || vs_injected) ? PRV_S : PRV_M; + + vsmode_exc = env->virt_enabled && cause < 64 && + (((hdeleg >> cause) & 1) || vs_injected); + + /* + * Check double trap condition only if already in S-mode and targeting + * S-mode + */ + if (cpu->cfg.ext_ssdbltrp && env->priv == PRV_S && mode == PRV_S) { + bool dte = (env->menvcfg & MENVCFG_DTE) != 0; + bool sdt = (env->mstatus & MSTATUS_SDT) != 0; + /* In VS or HS */ if (riscv_has_ext(env, RVH)) { - uint64_t hdeleg = async ? env->hideleg : env->hedeleg; + if (vsmode_exc) { + /* VS -> VS, use henvcfg instead of menvcfg*/ + dte = (env->henvcfg & HENVCFG_DTE) != 0; + } else if (env->virt_enabled) { + /* VS -> HS, use mstatus_hs */ + sdt = (env->mstatus_hs & MSTATUS_SDT) != 0; + } + } + smode_double_trap = dte && sdt; + if (smode_double_trap) { + mode = PRV_M; + } + } + + if (mode == PRV_S) { + /* 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 (env->virt_enabled && ((hdeleg >> cause) & 1)) { + if (riscv_has_ext(env, RVH)) { + if (vsmode_exc) { /* 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; @@ -1717,7 +2339,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) 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); @@ -1730,17 +2352,41 @@ void riscv_cpu_do_interrupt(CPUState *cs) s = set_field(s, MSTATUS_SPIE, get_field(s, MSTATUS_SIE)); s = set_field(s, MSTATUS_SPP, env->priv); s = set_field(s, MSTATUS_SIE, 0); + if (riscv_env_smode_dbltrp_enabled(env, virt)) { + s = set_field(s, MSTATUS_SDT, 1); + } 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); + riscv_cpu_set_mode(env, PRV_S, virt); + + src = env->sepc; } else { + /* + * If the hart encounters an exception while executing in M-mode + * with the mnstatus.NMIE bit clear, the exception is an RNMI exception. + */ + nnmi_excep = cpu->cfg.ext_smrnmi && + !get_field(env->mnstatus, MNSTATUS_NMIE) && + !async; + /* handle the trap in M-mode */ + /* save elp status */ + if (cpu_get_fcfien(env)) { + if (nnmi_excep) { + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPELP, + env->elp); + } else { + env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, env->elp); + } + } + if (riscv_has_ext(env, RVH)) { if (env->virt_enabled) { riscv_cpu_swap_hypervisor_regs(env); @@ -1754,24 +2400,79 @@ void riscv_cpu_do_interrupt(CPUState *cs) mtval2 = env->guest_phys_fault_addr; /* Trapping to M mode, virt is disabled */ - riscv_cpu_set_virt_enabled(env, 0); + virt = false; } + /* + * If the hart encounters an exception while executing in M-mode, + * with the mnstatus.NMIE bit clear, the program counter is set to + * the RNMI exception trap handler address. + */ + nnmi_excep = cpu->cfg.ext_smrnmi && + !get_field(env->mnstatus, MNSTATUS_NMIE) && + !async; s = env->mstatus; s = set_field(s, MSTATUS_MPIE, get_field(s, MSTATUS_MIE)); s = set_field(s, MSTATUS_MPP, env->priv); s = set_field(s, MSTATUS_MIE, 0); + if (cpu->cfg.ext_smdbltrp) { + if (env->mstatus & MSTATUS_MDT) { + assert(env->priv == PRV_M); + if (!cpu->cfg.ext_smrnmi || nnmi_excep) { + cpu_abort(CPU(cpu), "M-mode double trap\n"); + } else { + riscv_do_nmi(env, cause, false); + return; + } + } + + s = set_field(s, MSTATUS_MDT, 1); + } env->mstatus = s; - env->mcause = cause | ~(((target_ulong)-1) >> async); + env->mcause = cause | ((target_ulong)async << (mxlen - 1)); + if (smode_double_trap) { + env->mtval2 = env->mcause; + env->mcause = RISCV_EXCP_DOUBLE_TRAP; + } else { + env->mtval2 = mtval2; + } 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); + + /* + * For RNMI exception, program counter is set to the RNMI exception + * trap handler address. + */ + if (nnmi_excep) { + env->pc = env->rnmi_excpvec; + } else { + env->pc = (env->mtvec >> 2 << 2) + + ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0); + } + riscv_cpu_set_mode(env, PRV_M, virt); + src = env->mepc; + } + + if (riscv_cpu_cfg(env)->ext_smctr || riscv_cpu_cfg(env)->ext_ssctr) { + if (async && cause == IRQ_PMU_OVF) { + riscv_ctr_freeze(env, XCTRCTL_LCOFIFRZ, virt); + } else if (!async && cause == RISCV_EXCP_BREAKPOINT) { + riscv_ctr_freeze(env, XCTRCTL_BPFRZ, virt); + } + + riscv_ctr_add_entry(env, src, env->pc, + async ? CTRDATA_TYPE_INTERRUPT : CTRDATA_TYPE_EXCEPTION, + prev_priv, prev_virt); } + /* + * 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 @@ -1781,6 +2482,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 02afad608b4..e6927ff847e 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/crypto_helper.c b/target/riscv/crypto_helper.c index bb084e00efe..a0fb54bc50c 100644 --- a/target/riscv/crypto_helper.c +++ b/target/riscv/crypto_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "crypto/aes.h" #include "crypto/aes-round.h" diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 85a31dc4206..fb149721691 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -21,13 +21,18 @@ #include "qemu/log.h" #include "qemu/timer.h" #include "cpu.h" +#include "tcg/tcg-cpu.h" #include "pmu.h" #include "time_helper.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/tb-flush.h" -#include "sysemu/cpu-timers.h" +#include "exec/icount.h" +#include "accel/tcg/getpc.h" #include "qemu/guest-random.h" #include "qapi/error.h" +#include "tcg/insn-start-words.h" +#include "internals.h" +#include /* CSR function table public API */ void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops) @@ -35,7 +40,7 @@ void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops) *ops = csr_ops[csrno & (CSR_TABLE_SIZE - 1)]; } -void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops) +void riscv_set_csr_ops(int csrno, const riscv_csr_operations *ops) { csr_ops[csrno & (CSR_TABLE_SIZE - 1)] = *ops; } @@ -91,7 +96,7 @@ static RISCVException fs(CPURISCVState *env, int csrno) static RISCVException vs(CPURISCVState *env, int csrno) { - if (riscv_cpu_cfg(env)->ext_zve32f) { + 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; @@ -120,6 +125,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; } @@ -179,19 +188,47 @@ static RISCVException zcmt(CPURISCVState *env, int csrno) 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 ext implemented, M-mode always have access to SSP CSR */ + if (env->priv == PRV_M) { + return RISCV_EXCP_NONE; + } + + /* 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) { - int pmu_num = riscv_cpu_cfg(env)->pmu_num; + 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 (!pmu_num || ctr_index >= pmu_num) { + 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; } @@ -217,6 +254,33 @@ static RISCVException sscofpmf(CPURISCVState *env, int csrno) 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; @@ -232,7 +296,7 @@ static RISCVException any32(CPURISCVState *env, int csrno) } -static int aia_any(CPURISCVState *env, int csrno) +static RISCVException aia_any(CPURISCVState *env, int csrno) { if (!riscv_cpu_cfg(env)->ext_smaia) { return RISCV_EXCP_ILLEGAL_INST; @@ -241,7 +305,7 @@ static int aia_any(CPURISCVState *env, int csrno) return any(env, csrno); } -static int aia_any32(CPURISCVState *env, int csrno) +static RISCVException aia_any32(CPURISCVState *env, int csrno) { if (!riscv_cpu_cfg(env)->ext_smaia) { return RISCV_EXCP_ILLEGAL_INST; @@ -250,6 +314,24 @@ static int aia_any32(CPURISCVState *env, int csrno) return any32(env, csrno); } +static RISCVException csrind_any(CPURISCVState *env, int csrno) +{ + if (!riscv_cpu_cfg(env)->ext_smcsrind) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException csrind_or_aia_any(CPURISCVState *env, int csrno) +{ + if (!riscv_cpu_cfg(env)->ext_smaia && !riscv_cpu_cfg(env)->ext_smcsrind) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return any(env, csrno); +} + static RISCVException smode(CPURISCVState *env, int csrno) { if (riscv_has_ext(env, RVS)) { @@ -259,7 +341,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; @@ -268,24 +350,91 @@ 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) { + int ret; + if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } + if (csrno == CSR_STOPEI) { + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_IMSIC); + } else { + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_AIA); + } + + if (ret != RISCV_EXCP_NONE) { + return ret; + } + return smode(env, csrno); } -static int aia_smode32(CPURISCVState *env, int csrno) +static RISCVException aia_smode32(CPURISCVState *env, int csrno) { + int ret; + if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_AIA); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + return smode32(env, csrno); } +static RISCVException scountinhibit_pred(CPURISCVState *env, int csrno) +{ + RISCVCPU *cpu = env_archcpu(env); + + if (!cpu->cfg.ext_ssccfg || !cpu->cfg.ext_smcdeleg) { + return RISCV_EXCP_ILLEGAL_INST; + } + + if (env->virt_enabled) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } + + return smode(env, csrno); +} + +static bool csrind_extensions_present(CPURISCVState *env) +{ + return riscv_cpu_cfg(env)->ext_smcsrind || riscv_cpu_cfg(env)->ext_sscsrind; +} + +static bool aia_extensions_present(CPURISCVState *env) +{ + return riscv_cpu_cfg(env)->ext_smaia || riscv_cpu_cfg(env)->ext_ssaia; +} + +static bool csrind_or_aia_extensions_present(CPURISCVState *env) +{ + return csrind_extensions_present(env) || aia_extensions_present(env); +} + +static RISCVException csrind_smode(CPURISCVState *env, int csrno) +{ + if (!csrind_extensions_present(env)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return smode(env, csrno); +} + +static RISCVException csrind_or_aia_smode(CPURISCVState *env, int csrno) +{ + if (!csrind_or_aia_extensions_present(env)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return smode(env, csrno); +} + static RISCVException hmode(CPURISCVState *env, int csrno) { if (riscv_has_ext(env, RVH)) { @@ -305,6 +454,24 @@ static RISCVException hmode32(CPURISCVState *env, int csrno) } +static RISCVException csrind_hmode(CPURISCVState *env, int csrno) +{ + if (!csrind_extensions_present(env)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return hmode(env, csrno); +} + +static RISCVException csrind_or_aia_hmode(CPURISCVState *env, int csrno) +{ + if (!csrind_or_aia_extensions_present(env)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return hmode(env, csrno); +} + static RISCVException umode(CPURISCVState *env, int csrno) { if (riscv_has_ext(env, RVU)) { @@ -476,27 +643,82 @@ static RISCVException hgatp(CPURISCVState *env, int csrno) return hmode(env, csrno); } -/* Checks if PointerMasking registers could be accessed */ -static RISCVException pointer_masking(CPURISCVState *env, int csrno) +/* + * M-mode: + * Without ext_smctr raise illegal inst excep. + * Otherwise everything is accessible to m-mode. + * + * S-mode: + * Without ext_ssctr or mstateen.ctr raise illegal inst excep. + * Otherwise everything other than mctrctl is accessible. + * + * VS-mode: + * Without ext_ssctr or mstateen.ctr raise illegal inst excep. + * Without hstateen.ctr raise virtual illegal inst excep. + * Otherwise allow sctrctl (vsctrctl), sctrstatus, 0x200-0x2ff entry range. + * Always raise illegal instruction exception for sctrdepth. + */ +static RISCVException ctr_mmode(CPURISCVState *env, int csrno) { - /* Check if j-ext is present */ - if (riscv_has_ext(env, RVJ)) { + /* Check if smctr-ext is present */ + if (riscv_cpu_cfg(env)->ext_smctr) { return RISCV_EXCP_NONE; } + return RISCV_EXCP_ILLEGAL_INST; } -static int aia_hmode(CPURISCVState *env, int csrno) +static RISCVException ctr_smode(CPURISCVState *env, int csrno) { + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); + + if (!cfg->ext_smctr && !cfg->ext_ssctr) { + return RISCV_EXCP_ILLEGAL_INST; + } + + RISCVException ret = smstateen_acc_ok(env, 0, SMSTATEEN0_CTR); + if (ret == RISCV_EXCP_NONE && csrno == CSR_SCTRDEPTH && + env->virt_enabled) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } + + return ret; +} + +static RISCVException aia_hmode(CPURISCVState *env, int csrno) +{ + int ret; + if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } - return hmode(env, csrno); + if (csrno == CSR_VSTOPEI) { + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_IMSIC); + } else { + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_AIA); + } + + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + return hmode(env, csrno); } -static int aia_hmode32(CPURISCVState *env, int csrno) +static RISCVException aia_hmode32(CPURISCVState *env, int csrno) { + int ret; + + if (!riscv_cpu_cfg(env)->ext_ssaia) { + return RISCV_EXCP_ILLEGAL_INST; + } + + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_AIA); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } @@ -504,6 +726,15 @@ static int aia_hmode32(CPURISCVState *env, int csrno) return hmode32(env, csrno); } +static RISCVException dbltrp_hmode(CPURISCVState *env, int csrno) +{ + if (riscv_cpu_cfg(env)->ext_ssdbltrp) { + return RISCV_EXCP_NONE; + } + + return hmode(env, csrno); +} + static RISCVException pmp(CPURISCVState *env, int csrno) { if (riscv_cpu_cfg(env)->pmp) { @@ -522,9 +753,15 @@ static RISCVException pmp(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } -static RISCVException epmp(CPURISCVState *env, int csrno) +static RISCVException have_mseccfg(CPURISCVState *env, int csrno) { - if (riscv_cpu_cfg(env)->epmp) { + if (riscv_cpu_cfg(env)->ext_smepmp) { + return RISCV_EXCP_NONE; + } + if (riscv_cpu_cfg(env)->ext_zkr) { + return RISCV_EXCP_NONE; + } + if (riscv_cpu_cfg(env)->ext_smmpm) { return RISCV_EXCP_NONE; } @@ -539,6 +776,17 @@ static RISCVException debug(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } + +static RISCVException rnmi(CPURISCVState *env, int csrno) +{ + RISCVCPU *cpu = env_archcpu(env); + + if (cpu->cfg.ext_smrnmi) { + return RISCV_EXCP_NONE; + } + + return RISCV_EXCP_ILLEGAL_INST; +} #endif static RISCVException seed(CPURISCVState *env, int csrno) @@ -583,6 +831,21 @@ static RISCVException seed(CPURISCVState *env, int csrno) #endif } +/* zicfiss CSR_SSP read and write */ +static RISCVException read_ssp(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->ssp; + return RISCV_EXCP_NONE; +} + +static RISCVException write_ssp(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) +{ + env->ssp = val; + return RISCV_EXCP_NONE; +} + /* User Floating-Point CSRs */ static RISCVException read_fflags(CPURISCVState *env, int csrno, target_ulong *val) @@ -592,7 +855,7 @@ static RISCVException read_fflags(CPURISCVState *env, int csrno, } static RISCVException write_fflags(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { #if !defined(CONFIG_USER_ONLY) if (riscv_has_ext(env, RVF)) { @@ -611,7 +874,7 @@ static RISCVException read_frm(CPURISCVState *env, int csrno, } static RISCVException write_frm(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { #if !defined(CONFIG_USER_ONLY) if (riscv_has_ext(env, RVF)) { @@ -631,7 +894,7 @@ static RISCVException read_fcsr(CPURISCVState *env, int csrno, } static RISCVException write_fcsr(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { #if !defined(CONFIG_USER_ONLY) if (riscv_has_ext(env, RVF)) { @@ -668,9 +931,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 = riscv_cpu_cfg(env)->vlen >> 3; + *val = riscv_cpu_cfg(env)->vlenb; return RISCV_EXCP_NONE; } @@ -682,7 +946,7 @@ static RISCVException read_vxrm(CPURISCVState *env, int csrno, } static RISCVException write_vxrm(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { #if !defined(CONFIG_USER_ONLY) env->mstatus |= MSTATUS_VS; @@ -694,17 +958,17 @@ 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; } static RISCVException write_vxsat(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { #if !defined(CONFIG_USER_ONLY) env->mstatus |= MSTATUS_VS; #endif - env->vxsat = val; + env->vxsat = val & BIT(0); return RISCV_EXCP_NONE; } @@ -716,7 +980,7 @@ static RISCVException read_vstart(CPURISCVState *env, int csrno, } static RISCVException write_vstart(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { #if !defined(CONFIG_USER_ONLY) env->mstatus |= MSTATUS_VS; @@ -725,17 +989,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(riscv_cpu_cfg(env)->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, uintptr_t ra) { #if !defined(CONFIG_USER_ONLY) env->mstatus |= MSTATUS_VS; @@ -745,32 +1011,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) { @@ -785,13 +1035,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; @@ -799,91 +1051,292 @@ 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) { - int evt_index = csrno - CSR_MCOUNTINHIBIT; - - *val = env->mhpmevent_val[evt_index]; - + *val = env->mcyclecfg; return RISCV_EXCP_NONE; } -static int write_mhpmevent(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_mcyclecfg(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { - 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) { - mhpmevt_val = mhpmevt_val | - ((uint64_t)env->mhpmeventh_val[evt_index] << 32); + 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; } - 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_mcyclecfgh(CPURISCVState *env, int csrno, + target_ulong *val) { - int evt_index = csrno - CSR_MHPMEVENT3H + 3; - - *val = env->mhpmeventh_val[evt_index]; - + *val = env->mcyclecfgh; return RISCV_EXCP_NONE; } -static int write_mhpmeventh(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_mcyclecfgh(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { - int evt_index = csrno - CSR_MHPMEVENT3H + 3; - uint64_t mhpmevth_val = val; - uint64_t mhpmevt_val = env->mhpmevent_val[evt_index]; + target_ulong inh_avail_mask = (target_ulong)(~MHPMEVENTH_FILTER_MASK | + MCYCLECFGH_BIT_MINH); - mhpmevt_val = mhpmevt_val | (mhpmevth_val << 32); - env->mhpmeventh_val[evt_index] = val; + /* 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; - riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); + 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 int write_mhpmcounter(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_minstretcfg(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { - int ctr_idx = csrno - CSR_MCYCLE; - PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; - uint64_t mhpmctr_val = val; + uint64_t inh_avail_mask; - counter->mhpmcounter_val = val; - if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || - riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { - counter->mhpmcounter_prev = get_ticks(false); - if (ctr_idx > 2) { - if (riscv_cpu_mxl(env) == MXL_RV32) { - mhpmctr_val = mhpmctr_val | - ((uint64_t)counter->mhpmcounterh_val << 32); - } - riscv_pmu_setup_timer(env, mhpmctr_val, ctr_idx); - } - } else { - /* Other counters can keep incrementing from the given value */ - counter->mhpmcounter_prev = val; + 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 int write_mhpmcounterh(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_minstretcfgh(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { - int ctr_idx = csrno - CSR_MCYCLEH; - PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; - uint64_t mhpmctr_val = counter->mhpmcounter_val; - uint64_t mhpmctrh_val = val; + target_ulong inh_avail_mask = (target_ulong)(~MHPMEVENTH_FILTER_MASK | + MINSTRETCFGH_BIT_MINH); - counter->mhpmcounterh_val = val; + 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; + + *val = env->mhpmevent_val[evt_index]; + + return RISCV_EXCP_NONE; +} + +static RISCVException write_mhpmevent(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) +{ + int evt_index = csrno - CSR_MCOUNTINHIBIT; + uint64_t mhpmevt_val = 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 RISCVException read_mhpmeventh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + int evt_index = csrno - CSR_MHPMEVENT3H + 3; + + *val = env->mhpmeventh_val[evt_index]; + + return RISCV_EXCP_NONE; +} + +static RISCVException write_mhpmeventh(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) +{ + int evt_index = csrno - CSR_MHPMEVENT3H + 3; + 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] = mhpmevth_val; + + riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); + + return RISCV_EXCP_NONE; +} + +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 riscv_pmu_write_ctr(CPURISCVState *env, target_ulong val, + uint32_t ctr_idx) +{ + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + uint64_t mhpmctr_val = val; + + counter->mhpmcounter_val = val; + 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 | + ((uint64_t)counter->mhpmcounterh_val << 32); + } + riscv_pmu_setup_timer(env, mhpmctr_val, ctr_idx); + } + } else { + /* Other counters can keep incrementing from the given value */ + counter->mhpmcounter_prev = val; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException riscv_pmu_write_ctrh(CPURISCVState *env, target_ulong val, + uint32_t ctr_idx) +{ + PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; + uint64_t mhpmctr_val = counter->mhpmcounter_val; + uint64_t mhpmctrh_val = val; + + counter->mhpmcounterh_val = val; mhpmctr_val = mhpmctr_val | (mhpmctrh_val << 32); - if (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || - riscv_pmu_ctr_monitor_instructions(env, ctr_idx)) { - counter->mhpmcounterh_prev = get_ticks(true); + if (!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); } @@ -894,28 +1347,38 @@ 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, +static RISCVException write_mhpmcounter(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) +{ + int ctr_idx = csrno - CSR_MCYCLE; + + return riscv_pmu_write_ctr(env, val, ctr_idx); +} + +static RISCVException write_mhpmcounterh(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) +{ + int ctr_idx = csrno - CSR_MCYCLEH; + + return riscv_pmu_write_ctrh(env, val, ctr_idx); +} + +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; } /* @@ -924,7 +1387,8 @@ static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *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; } @@ -932,7 +1396,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; @@ -947,7 +1412,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; @@ -962,7 +1428,169 @@ 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 int rmw_cd_mhpmcounter(CPURISCVState *env, int ctr_idx, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) +{ + if (wr_mask != 0 && wr_mask != -1) { + return -EINVAL; + } + + if (!wr_mask && val) { + riscv_pmu_read_ctr(env, val, false, ctr_idx); + } else if (wr_mask) { + riscv_pmu_write_ctr(env, new_val, ctr_idx); + } else { + return -EINVAL; + } + + return 0; +} + +static int rmw_cd_mhpmcounterh(CPURISCVState *env, int ctr_idx, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) +{ + if (wr_mask != 0 && wr_mask != -1) { + return -EINVAL; + } + + if (!wr_mask && val) { + riscv_pmu_read_ctr(env, val, true, ctr_idx); + } else if (wr_mask) { + riscv_pmu_write_ctrh(env, new_val, ctr_idx); + } else { + return -EINVAL; + } + + return 0; +} + +static int rmw_cd_mhpmevent(CPURISCVState *env, int evt_index, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) +{ + uint64_t mhpmevt_val = new_val; + + if (wr_mask != 0 && wr_mask != -1) { + return -EINVAL; + } + + if (!wr_mask && val) { + *val = env->mhpmevent_val[evt_index]; + if (riscv_cpu_cfg(env)->ext_sscofpmf) { + *val &= ~MHPMEVENT_BIT_MINH; + } + } else if (wr_mask) { + wr_mask &= ~MHPMEVENT_BIT_MINH; + mhpmevt_val = (new_val & wr_mask) | + (env->mhpmevent_val[evt_index] & ~wr_mask); + if (riscv_cpu_mxl(env) == MXL_RV32) { + mhpmevt_val = mhpmevt_val | + ((uint64_t)env->mhpmeventh_val[evt_index] << 32); + } + env->mhpmevent_val[evt_index] = mhpmevt_val; + riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); + } else { + return -EINVAL; + } + + return 0; +} + +static int rmw_cd_mhpmeventh(CPURISCVState *env, int evt_index, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) +{ + uint64_t mhpmevth_val; + uint64_t mhpmevt_val = env->mhpmevent_val[evt_index]; + + if (wr_mask != 0 && wr_mask != -1) { + return -EINVAL; + } + + if (!wr_mask && val) { + *val = env->mhpmeventh_val[evt_index]; + if (riscv_cpu_cfg(env)->ext_sscofpmf) { + *val &= ~MHPMEVENTH_BIT_MINH; + } + } else if (wr_mask) { + wr_mask &= ~MHPMEVENTH_BIT_MINH; + env->mhpmeventh_val[evt_index] = + (new_val & wr_mask) | (env->mhpmeventh_val[evt_index] & ~wr_mask); + mhpmevth_val = env->mhpmeventh_val[evt_index]; + mhpmevt_val = mhpmevt_val | (mhpmevth_val << 32); + riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); + } else { + return -EINVAL; + } + + return 0; +} + +static int rmw_cd_ctr_cfg(CPURISCVState *env, int cfg_index, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + switch (cfg_index) { + case 0: /* CYCLECFG */ + if (wr_mask) { + wr_mask &= ~MCYCLECFG_BIT_MINH; + env->mcyclecfg = (new_val & wr_mask) | (env->mcyclecfg & ~wr_mask); + } else { + *val = env->mcyclecfg &= ~MHPMEVENTH_BIT_MINH; + } + break; + case 2: /* INSTRETCFG */ + if (wr_mask) { + wr_mask &= ~MINSTRETCFG_BIT_MINH; + env->minstretcfg = (new_val & wr_mask) | + (env->minstretcfg & ~wr_mask); + } else { + *val = env->minstretcfg &= ~MHPMEVENTH_BIT_MINH; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int rmw_cd_ctr_cfgh(CPURISCVState *env, int cfg_index, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + + if (riscv_cpu_mxl(env) != MXL_RV32) { + return RISCV_EXCP_ILLEGAL_INST; + } + + switch (cfg_index) { + case 0: /* CYCLECFGH */ + if (wr_mask) { + wr_mask &= ~MCYCLECFGH_BIT_MINH; + env->mcyclecfgh = (new_val & wr_mask) | + (env->mcyclecfgh & ~wr_mask); + } else { + *val = env->mcyclecfgh; + } + break; + case 2: /* INSTRETCFGH */ + if (wr_mask) { + wr_mask &= ~MINSTRETCFGH_BIT_MINH; + env->minstretcfgh = (new_val & wr_mask) | + (env->minstretcfgh & ~wr_mask); + } else { + *val = env->minstretcfgh; + } + break; + default: + return -EINVAL; + } + return 0; +} + + +static RISCVException read_scountovf(CPURISCVState *env, int csrno, + target_ulong *val) { int mhpmevt_start = CSR_MHPMEVENT3 - CSR_MCOUNTINHIBIT; int i; @@ -970,6 +1598,14 @@ static int read_scountovf(CPURISCVState *env, int csrno, target_ulong *val) target_ulong *mhpm_evt_val; uint64_t of_bit_mask; + /* Virtualize scountovf for counter delegation */ + if (riscv_cpu_cfg(env)->ext_sscofpmf && + riscv_cpu_cfg(env)->ext_ssccfg && + get_field(env->menvcfg, MENVCFG_CDE) && + env->virt_enabled) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } + if (riscv_cpu_mxl(env) == MXL_RV32) { mhpm_evt_val = env->mhpmeventh_val; of_bit_mask = MHPMEVENTH_BIT_OF; @@ -1031,7 +1667,7 @@ static RISCVException read_vstimecmph(CPURISCVState *env, int csrno, } static RISCVException write_vstimecmp(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { if (riscv_cpu_mxl(env) == MXL_RV32) { env->vstimecmp = deposit64(env->vstimecmp, 0, 32, (uint64_t)val); @@ -1046,7 +1682,7 @@ static RISCVException write_vstimecmp(CPURISCVState *env, int csrno, } static RISCVException write_vstimecmph(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->vstimecmp = deposit64(env->vstimecmp, 32, 32, (uint64_t)val); riscv_timer_write_timecmp(env, env->vstimer, env->vstimecmp, @@ -1080,13 +1716,13 @@ static RISCVException read_stimecmph(CPURISCVState *env, int csrno, } static RISCVException write_stimecmp(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { if (env->virt_enabled) { if (env->hvictl & HVICTL_VTI) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } - return write_vstimecmp(env, csrno, val); + return write_vstimecmp(env, csrno, val, ra); } if (riscv_cpu_mxl(env) == MXL_RV32) { @@ -1101,13 +1737,13 @@ static RISCVException write_stimecmp(CPURISCVState *env, int csrno, } static RISCVException write_stimecmph(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { if (env->virt_enabled) { if (env->hvictl & HVICTL_VTI) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } - return write_vstimecmph(env, csrno, val); + return write_vstimecmph(env, csrno, val, ra); } env->stimecmp = deposit64(env->stimecmp, 32, 32, (uint64_t)val); @@ -1116,21 +1752,23 @@ static RISCVException write_stimecmph(CPURISCVState *env, int csrno, 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)) | \ @@ -1146,6 +1784,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)) | \ @@ -1161,12 +1800,32 @@ 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; + +/* + * 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, @@ -1189,7 +1848,7 @@ static RISCVException read_zero(CPURISCVState *env, int csrno, } static RISCVException write_ignore(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { return RISCV_EXCP_NONE; } @@ -1253,8 +1912,39 @@ static RISCVException read_mstatus(CPURISCVState *env, int csrno, static bool validate_vm(CPURISCVState *env, target_ulong vm) { - return (vm & 0xf) <= - satp_mode_max_from_map(riscv_cpu_cfg(env)->satp_mode.map); + bool rv32 = riscv_cpu_mxl(env) == MXL_RV32; + RISCVCPU *cpu = env_archcpu(env); + int satp_mode_supported_max = cpu->cfg.max_satp_mode; + const bool *valid_vm = rv32 ? valid_vm_1_10_32 : valid_vm_1_10_64; + + assert(satp_mode_supported_max >= 0); + return vm <= satp_mode_supported_max && valid_vm[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) { + vm = validate_vm(env, get_field(val, SATP32_MODE)); + mask = (val ^ old_xatp) & (SATP32_MODE | SATP32_ASID | SATP32_PPN); + } else { + 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, @@ -1284,7 +1974,7 @@ static target_ulong legalize_mpp(CPURISCVState *env, target_ulong old_mpp, } static RISCVException write_mstatus(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint64_t mstatus = env->mstatus; uint64_t mask = 0; @@ -1303,11 +1993,28 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, 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 (riscv_env_smode_dbltrp_enabled(env, env->virt_enabled)) { + mask |= MSTATUS_SDT; + if ((val & MSTATUS_SDT) != 0) { + val &= ~MSTATUS_SIE; + } + } + + if (riscv_cpu_cfg(env)->ext_smdbltrp) { + mask |= MSTATUS_MDT; + if ((val & MSTATUS_MDT) != 0) { + val &= ~MSTATUS_MIE; + } + } if (xl != MXL_RV32 || env->debugger) { if (riscv_has_ext(env, RVH)) { @@ -1318,6 +2025,11 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, } } + /* 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); env->mstatus = mstatus; @@ -1330,7 +2042,6 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, env->xl = cpu_recompute_xl(env); } - riscv_cpu_update_mask(env); return RISCV_EXCP_NONE; } @@ -1342,11 +2053,17 @@ static RISCVException read_mstatush(CPURISCVState *env, int csrno, } static RISCVException write_mstatush(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint64_t valh = (uint64_t)val << 32; uint64_t mask = riscv_has_ext(env, RVH) ? MSTATUS_MPV | MSTATUS_GVA : 0; + if (riscv_cpu_cfg(env)->ext_smdbltrp) { + mask |= MSTATUS_MDT; + if ((valh & MSTATUS_MDT) != 0) { + mask |= MSTATUS_MIE; + } + } env->mstatus = (env->mstatus & ~mask) | (valh & mask); return RISCV_EXCP_NONE; @@ -1389,8 +2106,21 @@ static RISCVException read_misa(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static target_ulong get_next_pc(CPURISCVState *env, uintptr_t ra) +{ + uint64_t data[INSN_START_WORDS]; + + /* Outside of a running cpu, env contains the next pc. */ + if (ra == 0 || !cpu_unwind_state_data(env_cpu(env), ra, data)) { + return env->pc; + } + + /* Within unwind data, [0] is pc and [1] is the opcode. */ + return data[0] + insn_len(data[1]); +} + static RISCVException write_misa(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { RISCVCPU *cpu = env_archcpu(env); uint32_t orig_misa_ext = env->misa_ext; @@ -1404,11 +2134,8 @@ static RISCVException write_misa(CPURISCVState *env, int csrno, /* Mask extensions that are not supported by this hart */ val &= env->misa_ext_mask; - /* - * Suppress 'C' if next instruction is not aligned - * TODO: this should check next_pc - */ - if ((val & RVC) && (GETPC() & ~3) != 0) { + /* Suppress 'C' if next instruction is not aligned. */ + if ((val & RVC) && (get_next_pc(env, ra) & 3) != 0) { val &= ~RVC; } @@ -1454,7 +2181,7 @@ static RISCVException read_medeleg(CPURISCVState *env, int csrno, } static RISCVException write_medeleg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->medeleg = (env->medeleg & ~DELEGABLE_EXCPS) | (val & DELEGABLE_EXCPS); return RISCV_EXCP_NONE; @@ -1524,7 +2251,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; @@ -1561,7 +2288,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; @@ -1601,13 +2375,41 @@ 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 int csrind_xlate_vs_csrno(CPURISCVState *env, int csrno) +{ + if (!env->virt_enabled) { + return csrno; + } + + switch (csrno) { + case CSR_SISELECT: + return CSR_VSISELECT; + case CSR_SIREG: + case CSR_SIREG2: + case CSR_SIREG3: + case CSR_SIREG4: + case CSR_SIREG5: + case CSR_SIREG6: + return CSR_VSIREG + (csrno - CSR_SIREG); + default: + return csrno; + }; +} + +static RISCVException rmw_xiselect(CPURISCVState *env, int csrno, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) { target_ulong *iselect; + int ret; + + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_SVSLCT); + if (ret != RISCV_EXCP_NONE) { + return ret; + } /* Translate CSR number for VS-mode */ - csrno = aia_xlate_vs_csrno(env, csrno); + csrno = csrind_xlate_vs_csrno(env, csrno); /* Find the iselect CSR based on CSR number */ switch (csrno) { @@ -1628,7 +2430,12 @@ static int rmw_xiselect(CPURISCVState *env, int csrno, target_ulong *val, *val = *iselect; } - wr_mask &= ISELECT_MASK; + if (riscv_cpu_cfg(env)->ext_smcsrind || riscv_cpu_cfg(env)->ext_sscsrind) { + wr_mask &= ISELECT_MASK_SXCSRIND; + } else { + wr_mask &= ISELECT_MASK_AIA; + } + if (wr_mask) { *iselect = (*iselect & ~wr_mask) | (new_val & wr_mask); } @@ -1636,6 +2443,24 @@ static int rmw_xiselect(CPURISCVState *env, int csrno, target_ulong *val, return RISCV_EXCP_NONE; } +static bool xiselect_aia_range(target_ulong isel) +{ + return (ISELECT_IPRIO0 <= isel && isel <= ISELECT_IPRIO15) || + (ISELECT_IMSIC_FIRST <= isel && isel <= ISELECT_IMSIC_LAST); +} + +static bool xiselect_cd_range(target_ulong isel) +{ + return (ISELECT_CD_FIRST <= isel && isel <= ISELECT_CD_LAST); +} + +static bool xiselect_ctr_range(int csrno, target_ulong isel) +{ + /* MIREG-MIREG6 for the range 0x200-0x2ff are not used by CTR. */ + return CTR_ENTRIES_FIRST <= isel && isel <= CTR_ENTRIES_LAST && + csrno < CSR_MIREG; +} + static int rmw_iprio(target_ulong xlen, target_ulong iselect, uint8_t *iprio, target_ulong *val, target_ulong new_val, @@ -1681,39 +2506,162 @@ 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 int rmw_ctrsource(CPURISCVState *env, int isel, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) { - bool virt, isel_reserved; - uint8_t *iprio; - int ret = -EINVAL; - target_ulong priv, isel, vgein; + /* + * CTR arrays are treated as circular buffers and TOS always points to next + * empty slot, keeping TOS - 1 always pointing to latest entry. Given entry + * 0 is always the latest one, traversal is a bit different here. See the + * below example. + * + * Depth = 16. + * + * idx [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F] + * TOS H + * entry 6 5 4 3 2 1 0 F E D C B A 9 8 7 + */ + const uint64_t entry = isel - CTR_ENTRIES_FIRST; + const uint64_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK); + uint64_t idx; + + /* Entry greater than depth-1 is read-only zero */ + if (entry >= depth) { + if (val) { + *val = 0; + } + return 0; + } - /* Translate CSR number for VS-mode */ - csrno = aia_xlate_vs_csrno(env, csrno); + idx = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK); + idx = (idx - entry - 1) & (depth - 1); - /* Decode register details from CSR number */ - virt = false; - isel_reserved = false; + if (val) { + *val = env->ctr_src[idx]; + } + + env->ctr_src[idx] = (env->ctr_src[idx] & ~wr_mask) | (new_val & wr_mask); + + return 0; +} + +static int rmw_ctrtarget(CPURISCVState *env, int isel, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + /* + * CTR arrays are treated as circular buffers and TOS always points to next + * empty slot, keeping TOS - 1 always pointing to latest entry. Given entry + * 0 is always the latest one, traversal is a bit different here. See the + * below example. + * + * Depth = 16. + * + * idx [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F] + * head H + * entry 6 5 4 3 2 1 0 F E D C B A 9 8 7 + */ + const uint64_t entry = isel - CTR_ENTRIES_FIRST; + const uint64_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK); + uint64_t idx; + + /* Entry greater than depth-1 is read-only zero */ + if (entry >= depth) { + if (val) { + *val = 0; + } + return 0; + } + + idx = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK); + idx = (idx - entry - 1) & (depth - 1); + + if (val) { + *val = env->ctr_dst[idx]; + } + + env->ctr_dst[idx] = (env->ctr_dst[idx] & ~wr_mask) | (new_val & wr_mask); + + return 0; +} + +static int rmw_ctrdata(CPURISCVState *env, int isel, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + /* + * CTR arrays are treated as circular buffers and TOS always points to next + * empty slot, keeping TOS - 1 always pointing to latest entry. Given entry + * 0 is always the latest one, traversal is a bit different here. See the + * below example. + * + * Depth = 16. + * + * idx [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F] + * head H + * entry 6 5 4 3 2 1 0 F E D C B A 9 8 7 + */ + const uint64_t entry = isel - CTR_ENTRIES_FIRST; + const uint64_t mask = wr_mask & CTRDATA_MASK; + const uint64_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK); + uint64_t idx; + + /* Entry greater than depth-1 is read-only zero */ + if (entry >= depth) { + if (val) { + *val = 0; + } + return 0; + } + + idx = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK); + idx = (idx - entry - 1) & (depth - 1); + + if (val) { + *val = env->ctr_data[idx]; + } + + env->ctr_data[idx] = (env->ctr_data[idx] & ~mask) | (new_val & mask); + + return 0; +} + +static RISCVException rmw_xireg_aia(CPURISCVState *env, int csrno, + target_ulong isel, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + bool virt = false, isel_reserved = false; + int ret = -EINVAL; + uint8_t *iprio; + target_ulong priv, vgein; + + /* VS-mode CSR number passed in has already been translated */ switch (csrno) { case CSR_MIREG: + if (!riscv_cpu_cfg(env)->ext_smaia) { + goto done; + } iprio = env->miprio; - isel = env->miselect; priv = PRV_M; break; case CSR_SIREG: + if (!riscv_cpu_cfg(env)->ext_ssaia || + (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; break; case CSR_VSIREG: + if (!riscv_cpu_cfg(env)->ext_ssaia) { + goto done; + } iprio = env->hviprio; - isel = env->vsiselect; priv = PRV_S; virt = true; break; default: - goto done; + goto done; }; /* Find the selected guest interrupt file */ @@ -1744,15 +2692,227 @@ static int rmw_xireg(CPURISCVState *env, int csrno, target_ulong *val, } done: + /* + * If AIA is not enabled, illegal instruction exception is always + * returned regardless of whether we are in VS-mode or not + */ if (ret) { 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, +static int rmw_xireg_cd(CPURISCVState *env, int csrno, + target_ulong isel, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + int ret = -EINVAL; + int ctr_index = isel - ISELECT_CD_FIRST; + int isel_hpm_start = ISELECT_CD_FIRST + 3; + + if (!riscv_cpu_cfg(env)->ext_smcdeleg || !riscv_cpu_cfg(env)->ext_ssccfg) { + ret = RISCV_EXCP_ILLEGAL_INST; + goto done; + } + + /* Invalid siselect value for reserved */ + if (ctr_index == 1) { + goto done; + } + + /* sireg4 and sireg5 provides access RV32 only CSRs */ + if (((csrno == CSR_SIREG5) || (csrno == CSR_SIREG4)) && + (riscv_cpu_mxl(env) != MXL_RV32)) { + ret = RISCV_EXCP_ILLEGAL_INST; + goto done; + } + + /* Check Sscofpmf dependancy */ + if (!riscv_cpu_cfg(env)->ext_sscofpmf && csrno == CSR_SIREG5 && + (isel_hpm_start <= isel && isel <= ISELECT_CD_LAST)) { + goto done; + } + + /* Check smcntrpmf dependancy */ + if (!riscv_cpu_cfg(env)->ext_smcntrpmf && + (csrno == CSR_SIREG2 || csrno == CSR_SIREG5) && + (ISELECT_CD_FIRST <= isel && isel < isel_hpm_start)) { + goto done; + } + + if (!get_field(env->mcounteren, BIT(ctr_index)) || + !get_field(env->menvcfg, MENVCFG_CDE)) { + goto done; + } + + switch (csrno) { + case CSR_SIREG: + ret = rmw_cd_mhpmcounter(env, ctr_index, val, new_val, wr_mask); + break; + case CSR_SIREG4: + ret = rmw_cd_mhpmcounterh(env, ctr_index, val, new_val, wr_mask); + break; + case CSR_SIREG2: + if (ctr_index <= 2) { + ret = rmw_cd_ctr_cfg(env, ctr_index, val, new_val, wr_mask); + } else { + ret = rmw_cd_mhpmevent(env, ctr_index, val, new_val, wr_mask); + } + break; + case CSR_SIREG5: + if (ctr_index <= 2) { + ret = rmw_cd_ctr_cfgh(env, ctr_index, val, new_val, wr_mask); + } else { + ret = rmw_cd_mhpmeventh(env, ctr_index, val, new_val, wr_mask); + } + break; + default: + goto done; + } + +done: + return ret; +} + +static int rmw_xireg_ctr(CPURISCVState *env, int csrno, + target_ulong isel, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + if (!riscv_cpu_cfg(env)->ext_smctr && !riscv_cpu_cfg(env)->ext_ssctr) { + return -EINVAL; + } + + if (csrno == CSR_SIREG || csrno == CSR_VSIREG) { + return rmw_ctrsource(env, isel, val, new_val, wr_mask); + } else if (csrno == CSR_SIREG2 || csrno == CSR_VSIREG2) { + return rmw_ctrtarget(env, isel, val, new_val, wr_mask); + } else if (csrno == CSR_SIREG3 || csrno == CSR_VSIREG3) { + return rmw_ctrdata(env, isel, val, new_val, wr_mask); + } else if (val) { + *val = 0; + } + + return 0; +} + +/* + * rmw_xireg_csrind: Perform indirect access to xireg and xireg2-xireg6 + * + * Perform indirect access to xireg and xireg2-xireg6. + * This is a generic interface for all xireg CSRs. Apart from AIA, all other + * extension using csrind should be implemented here. + */ +static int rmw_xireg_csrind(CPURISCVState *env, int csrno, + target_ulong isel, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + bool virt = csrno == CSR_VSIREG ? true : false; + int ret = -EINVAL; + + if (xiselect_cd_range(isel)) { + ret = rmw_xireg_cd(env, csrno, isel, val, new_val, wr_mask); + } else if (xiselect_ctr_range(csrno, isel)) { + ret = rmw_xireg_ctr(env, csrno, isel, val, new_val, wr_mask); + } else { + /* + * As per the specification, access to unimplented region is undefined + * but recommendation is to raise illegal instruction exception. + */ + return RISCV_EXCP_ILLEGAL_INST; + } + + if (ret) { + return (env->virt_enabled && virt) ? + RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; + } + + return RISCV_EXCP_NONE; +} + +static int rmw_xiregi(CPURISCVState *env, int csrno, target_ulong *val, target_ulong new_val, target_ulong wr_mask) +{ + int ret = -EINVAL; + target_ulong isel; + + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_SVSLCT); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + /* Translate CSR number for VS-mode */ + csrno = csrind_xlate_vs_csrno(env, csrno); + + if (CSR_MIREG <= csrno && csrno <= CSR_MIREG6 && + csrno != CSR_MIREG4 - 1) { + isel = env->miselect; + } else if (CSR_SIREG <= csrno && csrno <= CSR_SIREG6 && + csrno != CSR_SIREG4 - 1) { + isel = env->siselect; + } else if (CSR_VSIREG <= csrno && csrno <= CSR_VSIREG6 && + csrno != CSR_VSIREG4 - 1) { + isel = env->vsiselect; + } else { + return RISCV_EXCP_ILLEGAL_INST; + } + + return rmw_xireg_csrind(env, csrno, isel, val, new_val, wr_mask); +} + +static RISCVException rmw_xireg(CPURISCVState *env, int csrno, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) +{ + int ret = -EINVAL; + target_ulong isel; + + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_SVSLCT); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + /* Translate CSR number for VS-mode */ + csrno = csrind_xlate_vs_csrno(env, csrno); + + /* Decode register details from CSR number */ + switch (csrno) { + case CSR_MIREG: + isel = env->miselect; + break; + case CSR_SIREG: + isel = env->siselect; + break; + case CSR_VSIREG: + isel = env->vsiselect; + break; + default: + goto done; + }; + + /* + * Use the xiselect range to determine actual op on xireg. + * + * Since we only checked the existence of AIA or Indirect Access in the + * predicate, we should check the existence of the exact extension when + * we get to a specific range and return illegal instruction exception even + * in VS-mode. + */ + if (xiselect_aia_range(isel)) { + return rmw_xireg_aia(env, csrno, isel, val, new_val, wr_mask); + } else if (riscv_cpu_cfg(env)->ext_smcsrind || + riscv_cpu_cfg(env)->ext_sscsrind) { + return rmw_xireg_csrind(env, csrno, isel, val, new_val, wr_mask); + } + +done: + return RISCV_EXCP_ILLEGAL_INST; +} + +static RISCVException rmw_xtopei(CPURISCVState *env, int csrno, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask) { bool virt; int ret = -EINVAL; @@ -1768,6 +2928,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: @@ -1813,7 +2976,7 @@ static RISCVException read_mtvec(CPURISCVState *env, int csrno, } static RISCVException write_mtvec(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ if ((val & 3) < 2) { @@ -1832,27 +2995,86 @@ static RISCVException read_mcountinhibit(CPURISCVState *env, int csrno, } static RISCVException write_mcountinhibit(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { 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; /* WARL register - disable unavailable counters; TM bit is always 0 */ - env->mcountinhibit = - val & (cpu->pmu_avail_ctrs | COUNTEREN_CY | COUNTEREN_IR); + 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; + } } } return RISCV_EXCP_NONE; } +static RISCVException read_scountinhibit(CPURISCVState *env, int csrno, + target_ulong *val) +{ + /* S-mode can only access the bits delegated by M-mode */ + *val = env->mcountinhibit & env->mcounteren; + return RISCV_EXCP_NONE; +} + +static RISCVException write_scountinhibit(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) +{ + return write_mcountinhibit(env, csrno, val & env->mcounteren, ra); +} + static RISCVException read_mcounteren(CPURISCVState *env, int csrno, target_ulong *val) { @@ -1861,7 +3083,7 @@ static RISCVException read_mcounteren(CPURISCVState *env, int csrno, } static RISCVException write_mcounteren(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { RISCVCPU *cpu = env_archcpu(env); @@ -1895,7 +3117,7 @@ static RISCVException read_mscratch(CPURISCVState *env, int csrno, } static RISCVException write_mscratch(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->mscratch = val; return RISCV_EXCP_NONE; @@ -1909,7 +3131,7 @@ static RISCVException read_mepc(CPURISCVState *env, int csrno, } static RISCVException write_mepc(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->mepc = val; return RISCV_EXCP_NONE; @@ -1923,7 +3145,7 @@ static RISCVException read_mcause(CPURISCVState *env, int csrno, } static RISCVException write_mcause(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->mcause = val; return RISCV_EXCP_NONE; @@ -1937,7 +3159,7 @@ static RISCVException read_mtval(CPURISCVState *env, int csrno, } static RISCVException write_mtval(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->mtval = val; return RISCV_EXCP_NONE; @@ -1951,20 +3173,42 @@ static RISCVException read_menvcfg(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException write_henvcfg(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra); static RISCVException write_menvcfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); - uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | MENVCFG_CBZE; + uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | + MENVCFG_CBZE | MENVCFG_CDE; if (riscv_cpu_mxl(env) == MXL_RV64) { mask |= (cfg->ext_svpbmt ? MENVCFG_PBMTE : 0) | (cfg->ext_sstc ? MENVCFG_STCE : 0) | - (cfg->ext_svadu ? MENVCFG_ADUE : 0); + (cfg->ext_smcdeleg ? MENVCFG_CDE : 0) | + (cfg->ext_svadu ? MENVCFG_ADUE : 0) | + (cfg->ext_ssdbltrp ? MENVCFG_DTE : 0); + + if (env_archcpu(env)->cfg.ext_zicfilp) { + mask |= MENVCFG_LPE; + } + + if (env_archcpu(env)->cfg.ext_zicfiss) { + mask |= MENVCFG_SSE; + } + + /* Update PMM field only if the value is valid according to Zjpm v1.0 */ + if (env_archcpu(env)->cfg.ext_smnpm && + get_field(val, MENVCFG_PMM) != PMM_FIELD_RESERVED) { + mask |= MENVCFG_PMM; + } + + if ((val & MENVCFG_DTE) == 0) { + env->mstatus &= ~MSTATUS_SDT; + } } env->menvcfg = (env->menvcfg & ~mask) | (val & mask); - - return RISCV_EXCP_NONE; + return write_henvcfg(env, CSR_HENVCFG, env->henvcfg, ra); } static RISCVException read_menvcfgh(CPURISCVState *env, int csrno, @@ -1974,18 +3218,25 @@ static RISCVException read_menvcfgh(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException write_henvcfgh(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra); static RISCVException write_menvcfgh(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { 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); + (cfg->ext_svadu ? MENVCFG_ADUE : 0) | + (cfg->ext_smcdeleg ? MENVCFG_CDE : 0) | + (cfg->ext_ssdbltrp ? MENVCFG_DTE : 0); uint64_t valh = (uint64_t)val << 32; - env->menvcfg = (env->menvcfg & ~mask) | (valh & mask); + if ((valh & MENVCFG_DTE) == 0) { + env->mstatus &= ~MSTATUS_SDT; + } - return RISCV_EXCP_NONE; + env->menvcfg = (env->menvcfg & ~mask) | (valh & mask); + return write_henvcfgh(env, CSR_HENVCFGH, env->henvcfg >> 32, ra); } static RISCVException read_senvcfg(CPURISCVState *env, int csrno, @@ -2003,16 +3254,37 @@ static RISCVException read_senvcfg(CPURISCVState *env, int csrno, } static RISCVException write_senvcfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint64_t mask = SENVCFG_FIOM | SENVCFG_CBIE | SENVCFG_CBCFE | SENVCFG_CBZE; RISCVException ret; + /* Update PMM field only if the value is valid according to Zjpm v1.0 */ + if (env_archcpu(env)->cfg.ext_ssnpm && + riscv_cpu_mxl(env) == MXL_RV64 && + get_field(val, SENVCFG_PMM) != PMM_FIELD_RESERVED) { + mask |= SENVCFG_PMM; + } 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; + } + + if (env_archcpu(env)->cfg.ext_svukte) { + mask |= SENVCFG_UKTE; + } + env->senvcfg = (env->senvcfg & ~mask) | (val & mask); return RISCV_EXCP_NONE; } @@ -2030,15 +3302,16 @@ static RISCVException read_henvcfg(CPURISCVState *env, int csrno, /* * henvcfg.pbmte is read_only 0 when menvcfg.pbmte = 0 * henvcfg.stce is read_only 0 when menvcfg.stce = 0 - * henvcfg.hade is read_only 0 when menvcfg.hade = 0 + * henvcfg.adue is read_only 0 when menvcfg.adue = 0 + * henvcfg.dte is read_only 0 when menvcfg.dte = 0 */ - *val = env->henvcfg & (~(HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE) | - env->menvcfg); + *val = env->henvcfg & (~(HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE | + HENVCFG_DTE) | env->menvcfg); return RISCV_EXCP_NONE; } static RISCVException write_henvcfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | HENVCFG_CBZE; RISCVException ret; @@ -2049,10 +3322,30 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno, } if (riscv_cpu_mxl(env) == MXL_RV64) { - mask |= env->menvcfg & (HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE); + mask |= env->menvcfg & (HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE | + HENVCFG_DTE); + + 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; + } + + /* Update PMM field only if the value is valid according to Zjpm v1.0 */ + if (env_archcpu(env)->cfg.ext_ssnpm && + get_field(val, HENVCFG_PMM) != PMM_FIELD_RESERVED) { + mask |= HENVCFG_PMM; + } } - env->henvcfg = (env->henvcfg & ~mask) | (val & mask); + env->henvcfg = val & mask; + if ((env->henvcfg & HENVCFG_DTE) == 0) { + env->vsstatus &= ~MSTATUS_SDT; + } return RISCV_EXCP_NONE; } @@ -2067,16 +3360,16 @@ static RISCVException read_henvcfgh(CPURISCVState *env, int csrno, return ret; } - *val = (env->henvcfg & (~(HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE) | - env->menvcfg)) >> 32; + *val = (env->henvcfg & (~(HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE | + HENVCFG_DTE) | env->menvcfg)) >> 32; return RISCV_EXCP_NONE; } static RISCVException write_henvcfgh(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint64_t mask = env->menvcfg & (HENVCFG_PBMTE | HENVCFG_STCE | - HENVCFG_ADUE); + HENVCFG_ADUE | HENVCFG_DTE); uint64_t valh = (uint64_t)val << 32; RISCVException ret; @@ -2084,8 +3377,10 @@ static RISCVException write_henvcfgh(CPURISCVState *env, int csrno, if (ret != RISCV_EXCP_NONE) { return ret; } - - env->henvcfg = (env->henvcfg & ~mask) | (valh & mask); + env->henvcfg = (env->henvcfg & 0xFFFFFFFF) | (valh & mask); + if ((env->henvcfg & HENVCFG_DTE) == 0) { + env->vsstatus &= ~MSTATUS_SDT; + } return RISCV_EXCP_NONE; } @@ -2109,18 +3404,39 @@ static RISCVException write_mstateen(CPURISCVState *env, int csrno, } static RISCVException write_mstateen0(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { 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; + } + + if (riscv_cpu_cfg(env)->ext_smaia || riscv_cpu_cfg(env)->ext_smcsrind) { + wr_mask |= SMSTATEEN0_SVSLCT; + } + + /* + * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is + * implemented. However, that information is with MachineState and we can't + * figure that out in csr.c. Just enable if Smaia is available. + */ + if (riscv_cpu_cfg(env)->ext_smaia) { + wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC); + } + + if (riscv_cpu_cfg(env)->ext_ssctr) { + wr_mask |= SMSTATEEN0_CTR; + } + return write_mstateen(env, csrno, wr_mask, new_val); } static RISCVException write_mstateen_1_3(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { return write_mstateen(env, csrno, SMSTATEEN_STATEEN, new_val); } @@ -2147,15 +3463,23 @@ static RISCVException write_mstateenh(CPURISCVState *env, int csrno, } static RISCVException write_mstateen0h(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; + if (env->priv_ver >= PRIV_VERSION_1_13_0) { + wr_mask |= SMSTATEEN0_P1P13; + } + + if (riscv_cpu_cfg(env)->ext_ssctr) { + wr_mask |= SMSTATEEN0_CTR; + } + return write_mstateenh(env, csrno, wr_mask, new_val); } static RISCVException write_mstateenh_1_3(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { return write_mstateenh(env, csrno, SMSTATEEN_STATEEN, new_val); } @@ -2184,7 +3508,7 @@ static RISCVException write_hstateen(CPURISCVState *env, int csrno, } static RISCVException write_hstateen0(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; @@ -2192,11 +3516,28 @@ static RISCVException write_hstateen0(CPURISCVState *env, int csrno, wr_mask |= SMSTATEEN0_FCSR; } + if (riscv_cpu_cfg(env)->ext_ssaia || riscv_cpu_cfg(env)->ext_sscsrind) { + wr_mask |= SMSTATEEN0_SVSLCT; + } + + /* + * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is + * implemented. However, that information is with MachineState and we can't + * figure that out in csr.c. Just enable if Ssaia is available. + */ + if (riscv_cpu_cfg(env)->ext_ssaia) { + wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC); + } + + if (riscv_cpu_cfg(env)->ext_ssctr) { + wr_mask |= SMSTATEEN0_CTR; + } + return write_hstateen(env, csrno, wr_mask, new_val); } static RISCVException write_hstateen_1_3(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { return write_hstateen(env, csrno, SMSTATEEN_STATEEN, new_val); } @@ -2227,15 +3568,19 @@ static RISCVException write_hstateenh(CPURISCVState *env, int csrno, } static RISCVException write_hstateen0h(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; + if (riscv_cpu_cfg(env)->ext_ssctr) { + wr_mask |= SMSTATEEN0_CTR; + } + return write_hstateenh(env, csrno, wr_mask, new_val); } static RISCVException write_hstateenh_1_3(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { return write_hstateenh(env, csrno, SMSTATEEN_STATEEN, new_val); } @@ -2274,7 +3619,7 @@ static RISCVException write_sstateen(CPURISCVState *env, int csrno, } static RISCVException write_sstateen0(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; @@ -2286,7 +3631,7 @@ static RISCVException write_sstateen0(CPURISCVState *env, int csrno, } static RISCVException write_sstateen_1_3(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { return write_sstateen(env, csrno, SMSTATEEN_STATEEN, new_val); } @@ -2359,6 +3704,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) @@ -2368,6 +3850,13 @@ static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno, if (env->xl != MXL_RV32 || env->debugger) { mask |= SSTATUS64_UXL; } + if (riscv_cpu_cfg(env)->ext_ssdbltrp) { + mask |= SSTATUS_SDT; + } + + if (env_archcpu(env)->cfg.ext_zicfilp) { + mask |= SSTATUS_SPELP; + } *val = int128_make128(sstatus, add_status_sd(MXL_RV128, sstatus)); return RISCV_EXCP_NONE; @@ -2380,13 +3869,20 @@ 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; + } + if (riscv_cpu_cfg(env)->ext_ssdbltrp) { + mask |= SSTATUS_SDT; + } /* TODO: Use SXL not MXL. */ *val = add_status_sd(riscv_cpu_mxl(env), env->mstatus & mask); return RISCV_EXCP_NONE; } static RISCVException write_sstatus(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { target_ulong mask = (sstatus_v1_10_mask); @@ -2395,24 +3891,51 @@ static RISCVException write_sstatus(CPURISCVState *env, int csrno, mask |= SSTATUS64_UXL; } } + + if (env_archcpu(env)->cfg.ext_zicfilp) { + mask |= SSTATUS_SPELP; + } + if (riscv_cpu_cfg(env)->ext_ssdbltrp) { + mask |= SSTATUS_SDT; + } target_ulong newval = (env->mstatus & ~mask) | (val & mask); - return write_mstatus(env, CSR_MSTATUS, newval); + return write_mstatus(env, CSR_MSTATUS, newval, ra); } 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, mask = env->hideleg & VS_MODE_INTERRUPTS; /* Bring VS-level bits to correct position */ - new_val = (new_val & (VS_MODE_INTERRUPTS >> 1)) << 1; - wr_mask = (wr_mask & (VS_MODE_INTERRUPTS >> 1)) << 1; + 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; + + 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); - ret = rmw_mie64(env, csrno, &rval, new_val, wr_mask & mask); if (ret_val) { - *ret_val = (rval & mask) >> 1; + rval &= alias_mask; + vsbits = rval & VS_MODE_INTERRUPTS; + rval &= ~VS_MODE_INTERRUPTS; + *ret_val = rval | (vsbits >> 1) | rval_vs; } return ret; @@ -2453,20 +3976,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; + /* + * 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; @@ -2511,7 +4051,7 @@ static RISCVException read_stvec(CPURISCVState *env, int csrno, } static RISCVException write_stvec(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ if ((val & 3) < 2) { @@ -2530,9 +4070,13 @@ static RISCVException read_scounteren(CPURISCVState *env, int csrno, } static RISCVException write_scounteren(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { - 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; } @@ -2560,7 +4104,7 @@ static RISCVException read_sscratch(CPURISCVState *env, int csrno, } static RISCVException write_sscratch(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->sscratch = val; return RISCV_EXCP_NONE; @@ -2574,7 +4118,7 @@ static RISCVException read_sepc(CPURISCVState *env, int csrno, } static RISCVException write_sepc(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->sepc = val; return RISCV_EXCP_NONE; @@ -2588,7 +4132,7 @@ static RISCVException read_scause(CPURISCVState *env, int csrno, } static RISCVException write_scause(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->scause = val; return RISCV_EXCP_NONE; @@ -2602,27 +4146,42 @@ static RISCVException read_stval(CPURISCVState *env, int csrno, } static RISCVException write_stval(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->stval = val; 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, mask = env->hideleg & VS_MODE_INTERRUPTS; + uint64_t vsbits; - /* Bring VS-level bits to correct position */ - new_val = (new_val & (VS_MODE_INTERRUPTS >> 1)) << 1; - wr_mask = (wr_mask & (VS_MODE_INTERRUPTS >> 1)) << 1; + /* Add virtualized bits into vsip mask. */ + mask |= env->hvien & ~env->hideleg; - ret = rmw_mip64(env, csrno, &rval, new_val, - wr_mask & mask & vsip_writable_mask); + /* 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_hvip64(env, csrno, &rval, new_val, + wr_mask & mask & vsip_writable_mask); if (ret_val) { - *ret_val = (rval & mask) >> 1; + rval &= mask; + vsbits = rval & VS_MODE_INTERRUPTS; + rval &= ~VS_MODE_INTERRUPTS; + *ret_val = rval | (vsbits >> 1); } return ret; @@ -2664,7 +4223,7 @@ 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 (env->virt_enabled) { if (env->hvictl & HVICTL_VTI) { @@ -2672,11 +4231,12 @@ static RISCVException rmw_sip64(CPURISCVState *env, int csrno, } 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; @@ -2726,37 +4286,98 @@ static RISCVException read_satp(CPURISCVState *env, int csrno, } static RISCVException write_satp(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { - target_ulong mask; - bool vm; - 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); + env->satp = legalize_xatp(env, env->satp, val); + return RISCV_EXCP_NONE; +} + +static RISCVException rmw_sctrdepth(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t mask = wr_mask & SCTRDEPTH_MASK; + + if (ret_val) { + *ret_val = env->sctrdepth; } - if (vm && mask) { + env->sctrdepth = (env->sctrdepth & ~mask) | (new_val & mask); + + /* Correct depth. */ + if (mask) { + uint64_t depth = get_field(env->sctrdepth, SCTRDEPTH_MASK); + + if (depth > SCTRDEPTH_MAX) { + depth = SCTRDEPTH_MAX; + env->sctrdepth = set_field(env->sctrdepth, SCTRDEPTH_MASK, depth); + } + + /* Update sctrstatus.WRPTR with a legal value */ + depth = 16ULL << depth; + env->sctrstatus = + env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1)); + } + + return RISCV_EXCP_NONE; +} + +static RISCVException rmw_sctrstatus(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint32_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK); + uint32_t mask = wr_mask & SCTRSTATUS_MASK; + + if (ret_val) { + *ret_val = env->sctrstatus; + } + + env->sctrstatus = (env->sctrstatus & ~mask) | (new_val & mask); + + /* Update sctrstatus.WRPTR with a legal value */ + env->sctrstatus = env->sctrstatus & (~SCTRSTATUS_WRPTR_MASK | (depth - 1)); + + return RISCV_EXCP_NONE; +} + +static RISCVException rmw_xctrctl(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t csr_mask, mask = wr_mask; + uint64_t *ctl_ptr = &env->mctrctl; + + if (csrno == CSR_MCTRCTL) { + csr_mask = MCTRCTL_MASK; + } else if (csrno == CSR_SCTRCTL && !env->virt_enabled) { + csr_mask = SCTRCTL_MASK; + } 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. + * This is for csrno == CSR_SCTRCTL and env->virt_enabled == true + * or csrno == CSR_VSCTRCTL. */ - tlb_flush(env_cpu(env)); - env->satp = val; + csr_mask = VSCTRCTL_MASK; + ctl_ptr = &env->vsctrctl; + } + + mask &= csr_mask; + + if (ret_val) { + *ret_val = *ctl_ptr & csr_mask; } + + *ctl_ptr = (*ctl_ptr & ~mask) | (new_val & mask); + 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; @@ -2841,10 +4462,12 @@ 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; @@ -2885,9 +4508,20 @@ static RISCVException read_hstatus(CPURISCVState *env, int csrno, } static RISCVException write_hstatus(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { - env->hstatus = val; + uint64_t mask = (target_ulong)-1; + if (!env_archcpu(env)->cfg.ext_svukte) { + mask &= ~HSTATUS_HUKTE; + } + /* Update PMM field only if the value is valid according to Zjpm v1.0 */ + if (!env_archcpu(env)->cfg.ext_ssnpm || + riscv_cpu_mxl(env) != MXL_RV64 || + get_field(val, HSTATUS_HUPMM) == PMM_FIELD_RESERVED) { + mask &= ~HSTATUS_HUPMM; + } + env->hstatus = (env->hstatus & ~mask) | (val & mask); + 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."); @@ -2898,18 +4532,91 @@ static RISCVException write_hstatus(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static RISCVException read_hedeleg(CPURISCVState *env, int csrno, - target_ulong *val) +static RISCVException read_hedeleg(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->hedeleg; + return RISCV_EXCP_NONE; +} + +static RISCVException write_hedeleg(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) +{ + env->hedeleg = val & vs_delegable_excps; + 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, uintptr_t ra) +{ + 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) { - *val = env->hedeleg; - return RISCV_EXCP_NONE; + 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 write_hedeleg(CPURISCVState *env, int csrno, - target_ulong val) +static RISCVException rmw_hvienh(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) { - env->hedeleg = val & vs_delegable_excps; - return RISCV_EXCP_NONE; + 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, @@ -2957,16 +4664,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; @@ -3039,9 +4824,13 @@ static RISCVException read_hcounteren(CPURISCVState *env, int csrno, } static RISCVException write_hcounteren(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { - 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; } @@ -3055,7 +4844,7 @@ static RISCVException read_hgeie(CPURISCVState *env, int csrno, } static RISCVException write_hgeie(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { /* Only GEILEN:1 bits implemented and BIT0 is never implemented */ val &= ((((target_ulong)1) << env->geilen) - 1) << 1; @@ -3074,7 +4863,7 @@ static RISCVException read_htval(CPURISCVState *env, int csrno, } static RISCVException write_htval(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->htval = val; return RISCV_EXCP_NONE; @@ -3088,7 +4877,7 @@ static RISCVException read_htinst(CPURISCVState *env, int csrno, } static RISCVException write_htinst(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { return RISCV_EXCP_NONE; } @@ -3110,9 +4899,9 @@ static RISCVException read_hgatp(CPURISCVState *env, int csrno, } static RISCVException write_hgatp(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { - env->hgatp = val; + env->hgatp = legalize_xatp(env, env->hgatp, val); return RISCV_EXCP_NONE; } @@ -3128,7 +4917,7 @@ static RISCVException read_htimedelta(CPURISCVState *env, int csrno, } static RISCVException write_htimedelta(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { if (!env->rdtime_fn) { return RISCV_EXCP_ILLEGAL_INST; @@ -3160,7 +4949,7 @@ static RISCVException read_htimedeltah(CPURISCVState *env, int csrno, } static RISCVException write_htimedeltah(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { if (!env->rdtime_fn) { return RISCV_EXCP_ILLEGAL_INST; @@ -3176,19 +4965,21 @@ static RISCVException write_htimedeltah(CPURISCVState *env, int csrno, 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, uintptr_t ra) { 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); @@ -3214,7 +5005,7 @@ 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); @@ -3240,42 +5031,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, uintptr_t ra) { 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, uintptr_t ra) { 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, uintptr_t ra) { 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, uintptr_t ra) { return write_hvipriox(env, 12, env->hviprio, val); } @@ -3289,26 +5088,39 @@ static RISCVException read_vsstatus(CPURISCVState *env, int csrno, } static RISCVException write_vsstatus(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint64_t mask = (target_ulong)-1; if ((val & VSSTATUS64_UXL) == 0) { mask &= ~VSSTATUS64_UXL; } + if ((env->henvcfg & HENVCFG_DTE)) { + if ((val & SSTATUS_SDT) != 0) { + val &= ~SSTATUS_SIE; + } + } else { + val &= ~SSTATUS_SDT; + } env->vsstatus = (env->vsstatus & ~mask) | (uint64_t)val; 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; } static RISCVException write_vstvec(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { - 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; } @@ -3320,7 +5132,7 @@ static RISCVException read_vsscratch(CPURISCVState *env, int csrno, } static RISCVException write_vsscratch(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->vsscratch = val; return RISCV_EXCP_NONE; @@ -3334,7 +5146,7 @@ static RISCVException read_vsepc(CPURISCVState *env, int csrno, } static RISCVException write_vsepc(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->vsepc = val; return RISCV_EXCP_NONE; @@ -3348,7 +5160,7 @@ static RISCVException read_vscause(CPURISCVState *env, int csrno, } static RISCVException write_vscause(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->vscause = val; return RISCV_EXCP_NONE; @@ -3362,7 +5174,7 @@ static RISCVException read_vstval(CPURISCVState *env, int csrno, } static RISCVException write_vstval(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->vstval = val; return RISCV_EXCP_NONE; @@ -3376,9 +5188,9 @@ static RISCVException read_vsatp(CPURISCVState *env, int csrno, } static RISCVException write_vsatp(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { - env->vsatp = val; + env->vsatp = legalize_xatp(env, env->vsatp, val); return RISCV_EXCP_NONE; } @@ -3390,7 +5202,7 @@ static RISCVException read_mtval2(CPURISCVState *env, int csrno, } static RISCVException write_mtval2(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->mtval2 = val; return RISCV_EXCP_NONE; @@ -3404,7 +5216,7 @@ static RISCVException read_mtinst(CPURISCVState *env, int csrno, } static RISCVException write_mtinst(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->mtinst = val; return RISCV_EXCP_NONE; @@ -3419,7 +5231,7 @@ static RISCVException read_mseccfg(CPURISCVState *env, int csrno, } static RISCVException write_mseccfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { mseccfg_csr_write(env, val); return RISCV_EXCP_NONE; @@ -3435,7 +5247,7 @@ static RISCVException read_pmpcfg(CPURISCVState *env, int csrno, } static RISCVException write_pmpcfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint32_t reg_index = csrno - CSR_PMPCFG0; @@ -3451,7 +5263,7 @@ static RISCVException read_pmpaddr(CPURISCVState *env, int csrno, } static RISCVException write_pmpaddr(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { pmpaddr_csr_write(env, csrno - CSR_PMPADDR0, val); return RISCV_EXCP_NONE; @@ -3465,7 +5277,7 @@ static RISCVException read_tselect(CPURISCVState *env, int csrno, } static RISCVException write_tselect(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { tselect_csr_write(env, val); return RISCV_EXCP_NONE; @@ -3489,7 +5301,7 @@ static RISCVException read_tdata(CPURISCVState *env, int csrno, } static RISCVException write_tdata(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { if (!tdata_available(env, csrno - CSR_TDATA1)) { return RISCV_EXCP_ILLEGAL_INST; @@ -3506,309 +5318,104 @@ static RISCVException read_tinfo(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -/* - * Functions to access Pointer Masking feature registers - * We have to check if current priv lvl could modify - * csr in given mode - */ -static bool check_pm_current_disabled(CPURISCVState *env, int csrno) -{ - int csr_priv = get_field(csrno, 0x300); - int pm_current; - - if (env->debugger) { - return false; - } - /* - * If priv lvls differ that means we're accessing csr from higher priv lvl, - * so allow the access - */ - if (env->priv != csr_priv) { - return false; - } - switch (env->priv) { - case PRV_M: - pm_current = get_field(env->mmte, M_PM_CURRENT); - break; - case PRV_S: - pm_current = get_field(env->mmte, S_PM_CURRENT); - break; - case PRV_U: - pm_current = get_field(env->mmte, U_PM_CURRENT); - break; - default: - g_assert_not_reached(); - } - /* It's same priv lvl, so we allow to modify csr only if pm.current==1 */ - return !pm_current; -} - -static RISCVException read_mmte(CPURISCVState *env, int csrno, - target_ulong *val) -{ - *val = env->mmte & MMTE_MASK; - return RISCV_EXCP_NONE; -} - -static RISCVException write_mmte(CPURISCVState *env, int csrno, - target_ulong val) -{ - uint64_t mstatus; - 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); - } - /* 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 | EXT_STATUS_DIRTY; - riscv_cpu_update_mask(env); - - /* Set XS and SD bits, since PM CSRs are dirty */ - mstatus = env->mstatus | MSTATUS_XS; - write_mstatus(env, csrno, mstatus); - return RISCV_EXCP_NONE; -} - -static RISCVException read_smte(CPURISCVState *env, int csrno, - target_ulong *val) -{ - *val = env->mmte & SMTE_MASK; - return RISCV_EXCP_NONE; -} - -static RISCVException write_smte(CPURISCVState *env, int csrno, - target_ulong val) -{ - 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); - } - - /* if pm.current==0 we can't modify current PM CSRs */ - if (check_pm_current_disabled(env, csrno)) { - return RISCV_EXCP_NONE; - } - - wpri_val |= (env->mmte & ~SMTE_MASK); - write_mmte(env, csrno, wpri_val); - return RISCV_EXCP_NONE; -} - -static RISCVException read_umte(CPURISCVState *env, int csrno, - target_ulong *val) -{ - *val = env->mmte & UMTE_MASK; - return RISCV_EXCP_NONE; -} - -static RISCVException write_umte(CPURISCVState *env, int csrno, - target_ulong val) -{ - 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); - } - - if (check_pm_current_disabled(env, csrno)) { - return RISCV_EXCP_NONE; - } - - wpri_val |= (env->mmte & ~UMTE_MASK); - write_mmte(env, csrno, wpri_val); - return RISCV_EXCP_NONE; -} - -static RISCVException read_mpmmask(CPURISCVState *env, int csrno, - target_ulong *val) +static RISCVException read_mcontext(CPURISCVState *env, int csrno, + target_ulong *val) { - *val = env->mpmmask; + *val = env->mcontext; return RISCV_EXCP_NONE; } -static RISCVException write_mpmmask(CPURISCVState *env, int csrno, - target_ulong val) +static RISCVException write_mcontext(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { - uint64_t mstatus; + bool rv32 = riscv_cpu_mxl(env) == MXL_RV32 ? true : false; + int32_t mask; - env->mpmmask = val; - if ((cpu_address_mode(env) == PRV_M) && (env->mmte & M_PM_ENABLE)) { - env->cur_pmmask = val; + 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->mmte |= EXT_STATUS_DIRTY; - /* Set XS and SD bits, since PM CSRs are dirty */ - mstatus = env->mstatus | MSTATUS_XS; - write_mstatus(env, csrno, mstatus); + env->mcontext = val & mask; return RISCV_EXCP_NONE; } -static RISCVException read_spmmask(CPURISCVState *env, int csrno, - target_ulong *val) +static RISCVException read_mnscratch(CPURISCVState *env, int csrno, + target_ulong *val) { - *val = env->spmmask; + *val = env->mnscratch; return RISCV_EXCP_NONE; } -static RISCVException write_spmmask(CPURISCVState *env, int csrno, - target_ulong val) +static RISCVException write_mnscratch(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { - uint64_t mstatus; - - /* if pm.current==0 we can't modify current PM CSRs */ - if (check_pm_current_disabled(env, csrno)) { - return RISCV_EXCP_NONE; - } - env->spmmask = val; - 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 |= EXT_STATUS_DIRTY; - - /* Set XS and SD bits, since PM CSRs are dirty */ - mstatus = env->mstatus | MSTATUS_XS; - write_mstatus(env, csrno, mstatus); + env->mnscratch = val; return RISCV_EXCP_NONE; } -static RISCVException read_upmmask(CPURISCVState *env, int csrno, - target_ulong *val) +static RISCVException read_mnepc(CPURISCVState *env, int csrno, + target_ulong *val) { - *val = env->upmmask; + *val = env->mnepc; return RISCV_EXCP_NONE; } -static RISCVException write_upmmask(CPURISCVState *env, int csrno, - target_ulong val) +static RISCVException write_mnepc(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { - uint64_t mstatus; - - /* if pm.current==0 we can't modify current PM CSRs */ - if (check_pm_current_disabled(env, csrno)) { - return RISCV_EXCP_NONE; - } - env->upmmask = val; - 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 |= EXT_STATUS_DIRTY; - - /* Set XS and SD bits, since PM CSRs are dirty */ - mstatus = env->mstatus | MSTATUS_XS; - write_mstatus(env, csrno, mstatus); + env->mnepc = val; return RISCV_EXCP_NONE; } -static RISCVException read_mpmbase(CPURISCVState *env, int csrno, +static RISCVException read_mncause(CPURISCVState *env, int csrno, target_ulong *val) { - *val = env->mpmbase; + *val = env->mncause; return RISCV_EXCP_NONE; } -static RISCVException write_mpmbase(CPURISCVState *env, int csrno, - target_ulong val) +static RISCVException write_mncause(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { - uint64_t mstatus; - - env->mpmbase = val; - if ((cpu_address_mode(env) == PRV_M) && (env->mmte & M_PM_ENABLE)) { - env->cur_pmbase = val; - } - env->mmte |= EXT_STATUS_DIRTY; - - /* Set XS and SD bits, since PM CSRs are dirty */ - mstatus = env->mstatus | MSTATUS_XS; - write_mstatus(env, csrno, mstatus); + env->mncause = val; return RISCV_EXCP_NONE; } -static RISCVException read_spmbase(CPURISCVState *env, int csrno, - target_ulong *val) +static RISCVException read_mnstatus(CPURISCVState *env, int csrno, + target_ulong *val) { - *val = env->spmbase; + *val = env->mnstatus; return RISCV_EXCP_NONE; } -static RISCVException write_spmbase(CPURISCVState *env, int csrno, - target_ulong val) +static RISCVException write_mnstatus(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { - uint64_t mstatus; + target_ulong mask = (MNSTATUS_NMIE | MNSTATUS_MNPP); - /* if pm.current==0 we can't modify current PM CSRs */ - if (check_pm_current_disabled(env, csrno)) { - return RISCV_EXCP_NONE; - } - env->spmbase = val; - 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; + if (riscv_has_ext(env, RVH)) { + /* Flush tlb on mnstatus fields that affect VM. */ + if ((val ^ env->mnstatus) & MNSTATUS_MNPV) { + tlb_flush(env_cpu(env)); } - } - env->mmte |= EXT_STATUS_DIRTY; - - /* Set XS and SD bits, since PM CSRs are dirty */ - mstatus = env->mstatus | MSTATUS_XS; - write_mstatus(env, csrno, mstatus); - return RISCV_EXCP_NONE; -} - -static RISCVException read_upmbase(CPURISCVState *env, int csrno, - target_ulong *val) -{ - *val = env->upmbase; - return RISCV_EXCP_NONE; -} - -static RISCVException write_upmbase(CPURISCVState *env, int csrno, - target_ulong val) -{ - uint64_t mstatus; - /* if pm.current==0 we can't modify current PM CSRs */ - if (check_pm_current_disabled(env, csrno)) { - return RISCV_EXCP_NONE; + mask |= MNSTATUS_MNPV; } - env->upmbase = val; - 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 |= EXT_STATUS_DIRTY; - /* Set XS and SD bits, since PM CSRs are dirty */ - mstatus = env->mstatus | MSTATUS_XS; - write_mstatus(env, csrno, mstatus); + /* mnstatus.mnie can only be cleared by hardware. */ + env->mnstatus = (env->mnstatus & MNSTATUS_NMIE) | (val & mask); return RISCV_EXCP_NONE; } #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; @@ -3832,6 +5439,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; } @@ -3850,14 +5469,14 @@ static RISCVException rmw_seed(CPURISCVState *env, int csrno, static inline RISCVException riscv_csrrw_check(CPURISCVState *env, int csrno, - bool write_mask) + bool write) { /* check privileges and return RISCV_EXCP_ILLEGAL_INST if check fails */ bool read_only = get_field(csrno, 0xC00) == 3; int csr_min_priv = csr_ops[csrno].min_priv_ver; /* ensure the CSR extension is enabled */ - if (!riscv_cpu_cfg(env)->ext_icsr) { + if (!riscv_cpu_cfg(env)->ext_zicsr) { return RISCV_EXCP_ILLEGAL_INST; } @@ -3872,7 +5491,7 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, } /* read / write check */ - if (write_mask && read_only) { + if (write && read_only) { return RISCV_EXCP_ILLEGAL_INST; } @@ -3914,7 +5533,8 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, static RISCVException riscv_csrrw_do64(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, - target_ulong write_mask) + target_ulong write_mask, + uintptr_t ra) { RISCVException ret; target_ulong old_value = 0; @@ -3944,7 +5564,7 @@ static RISCVException riscv_csrrw_do64(CPURISCVState *env, int csrno, if (write_mask) { new_value = (old_value & ~write_mask) | (new_value & write_mask); if (csr_ops[csrno].write) { - ret = csr_ops[csrno].write(env, csrno, new_value); + ret = csr_ops[csrno].write(env, csrno, new_value, ra); if (ret != RISCV_EXCP_NONE) { return ret; } @@ -3959,22 +5579,33 @@ 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, 0); +} + RISCVException riscv_csrrw(CPURISCVState *env, int csrno, - target_ulong *ret_value, - target_ulong new_value, target_ulong write_mask) + target_ulong *ret_value, target_ulong new_value, + target_ulong write_mask, uintptr_t ra) { - RISCVException ret = riscv_csrrw_check(env, csrno, write_mask); + RISCVException ret = riscv_csrrw_check(env, csrno, true); if (ret != RISCV_EXCP_NONE) { return ret; } - return riscv_csrrw_do64(env, csrno, ret_value, new_value, write_mask); + return riscv_csrrw_do64(env, csrno, ret_value, new_value, write_mask, ra); } static RISCVException riscv_csrrw_do128(CPURISCVState *env, int csrno, Int128 *ret_value, Int128 new_value, - Int128 write_mask) + Int128 write_mask, uintptr_t ra) { RISCVException ret; Int128 old_value; @@ -3996,7 +5627,7 @@ static RISCVException riscv_csrrw_do128(CPURISCVState *env, int csrno, } } else if (csr_ops[csrno].write) { /* avoids having to write wrappers for all registers */ - ret = csr_ops[csrno].write(env, csrno, int128_getlo(new_value)); + ret = csr_ops[csrno].write(env, csrno, int128_getlo(new_value), ra); if (ret != RISCV_EXCP_NONE) { return ret; } @@ -4011,19 +5642,50 @@ 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(), 0); + } + + /* + * 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, 0, 0, 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) + Int128 *ret_value, Int128 new_value, + Int128 write_mask, uintptr_t ra) { RISCVException ret; - ret = riscv_csrrw_check(env, csrno, int128_nz(write_mask)); + ret = riscv_csrrw_check(env, csrno, true); if (ret != RISCV_EXCP_NONE) { return ret; } if (csr_ops[csrno].read128) { - return riscv_csrrw_do128(env, csrno, ret_value, new_value, write_mask); + return riscv_csrrw_do128(env, csrno, ret_value, + new_value, write_mask, ra); } /* @@ -4036,7 +5698,7 @@ RISCVException riscv_csrrw_i128(CPURISCVState *env, int csrno, target_ulong old_value; ret = riscv_csrrw_do64(env, csrno, &old_value, int128_getlo(new_value), - int128_getlo(write_mask)); + int128_getlo(write_mask), ra); if (ret == RISCV_EXCP_NONE && ret_value) { *ret_value = int128_make64(old_value); } @@ -4056,7 +5718,11 @@ 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, 0); + } #if !defined(CONFIG_USER_ONLY) env->debugger = false; #endif @@ -4071,7 +5737,7 @@ static RISCVException read_jvt(CPURISCVState *env, int csrno, } static RISCVException write_jvt(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->jvt = val; return RISCV_EXCP_NONE; @@ -4113,6 +5779,9 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* 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, @@ -4146,6 +5815,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, @@ -4156,22 +5829,36 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MIP] = { "mip", any, NULL, NULL, rmw_mip }, /* Machine-Level Window to Indirectly Accessed Registers (AIA) */ - [CSR_MISELECT] = { "miselect", aia_any, NULL, NULL, rmw_xiselect }, - [CSR_MIREG] = { "mireg", aia_any, NULL, NULL, rmw_xireg }, + [CSR_MISELECT] = { "miselect", csrind_or_aia_any, NULL, NULL, + rmw_xiselect }, + [CSR_MIREG] = { "mireg", csrind_or_aia_any, NULL, NULL, + rmw_xireg }, + + /* Machine Indirect Register Alias */ + [CSR_MIREG2] = { "mireg2", csrind_any, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MIREG3] = { "mireg3", csrind_any, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MIREG4] = { "mireg4", csrind_any, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MIREG5] = { "mireg5", csrind_any, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MIREG6] = { "mireg6", csrind_any, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Machine-Level Interrupts (AIA) */ [CSR_MTOPEI] = { "mtopei", aia_any, NULL, NULL, rmw_xtopei }, [CSR_MTOPI] = { "mtopi", aia_any, read_mtopi }, /* Virtual Interrupts for Supervisor Level (AIA) */ - [CSR_MVIEN] = { "mvien", aia_any, read_zero, write_ignore }, - [CSR_MVIP] = { "mvip", aia_any, read_zero, write_ignore }, + [CSR_MVIEN] = { "mvien", aia_any, NULL, NULL, rmw_mvien }, + [CSR_MVIP] = { "mvip", aia_any, NULL, NULL, rmw_mvip }, /* Machine-Level High-Half CSRs (AIA) */ [CSR_MIDELEGH] = { "midelegh", aia_any32, NULL, NULL, rmw_midelegh }, [CSR_MIEH] = { "mieh", aia_any32, NULL, NULL, rmw_mieh }, - [CSR_MVIENH] = { "mvienh", aia_any32, read_zero, write_ignore }, - [CSR_MVIPH] = { "mviph", aia_any32, read_zero, write_ignore }, + [CSR_MVIENH] = { "mvienh", aia_any32, NULL, NULL, rmw_mvienh }, + [CSR_MVIPH] = { "mviph", aia_any32, NULL, NULL, rmw_mviph }, [CSR_MIPH] = { "miph", aia_any32, NULL, NULL, rmw_miph }, /* Execution environment configuration */ @@ -4245,6 +5932,21 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { write_sstateen_1_3, .min_priv_ver = PRIV_VERSION_1_12_0 }, + /* RNMI */ + [CSR_MNSCRATCH] = { "mnscratch", rnmi, read_mnscratch, write_mnscratch, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MNEPC] = { "mnepc", rnmi, read_mnepc, write_mnepc, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MNCAUSE] = { "mncause", rnmi, read_mncause, write_mncause, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MNSTATUS] = { "mnstatus", rnmi, read_mnstatus, write_mnstatus, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + + /* Supervisor Counter Delegation */ + [CSR_SCOUNTINHIBIT] = {"scountinhibit", scountinhibit_pred, + read_scountinhibit, write_scountinhibit, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + /* Supervisor Trap Setup */ [CSR_SSTATUS] = { "sstatus", smode, read_sstatus, write_sstatus, NULL, read_sstatus_i128 }, @@ -4275,8 +5977,22 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_SATP] = { "satp", satp, read_satp, write_satp }, /* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */ - [CSR_SISELECT] = { "siselect", aia_smode, NULL, NULL, rmw_xiselect }, - [CSR_SIREG] = { "sireg", aia_smode, NULL, NULL, rmw_xireg }, + [CSR_SISELECT] = { "siselect", csrind_or_aia_smode, NULL, NULL, + rmw_xiselect }, + [CSR_SIREG] = { "sireg", csrind_or_aia_smode, NULL, NULL, + rmw_xireg }, + + /* Supervisor Indirect Register Alias */ + [CSR_SIREG2] = { "sireg2", csrind_smode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_SIREG3] = { "sireg3", csrind_smode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_SIREG4] = { "sireg4", csrind_smode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_SIREG5] = { "sireg5", csrind_smode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_SIREG6] = { "sireg6", csrind_smode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Supervisor-Level Interrupts (AIA) */ [CSR_STOPEI] = { "stopei", aia_smode, NULL, NULL, rmw_xtopei }, @@ -4339,26 +6055,38 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2, + [CSR_MTVAL2] = { "mtval2", dbltrp_hmode, read_mtval2, write_mtval2, .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst, .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */ - [CSR_HVIEN] = { "hvien", aia_hmode, read_zero, write_ignore }, + [CSR_HVIEN] = { "hvien", aia_hmode, NULL, NULL, rmw_hvien }, [CSR_HVICTL] = { "hvictl", aia_hmode, read_hvictl, write_hvictl }, [CSR_HVIPRIO1] = { "hviprio1", aia_hmode, read_hviprio1, write_hviprio1 }, [CSR_HVIPRIO2] = { "hviprio2", aia_hmode, read_hviprio2, write_hviprio2 }, - /* * VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) */ - [CSR_VSISELECT] = { "vsiselect", aia_hmode, NULL, NULL, - rmw_xiselect }, - [CSR_VSIREG] = { "vsireg", aia_hmode, NULL, NULL, rmw_xireg }, + [CSR_VSISELECT] = { "vsiselect", csrind_or_aia_hmode, NULL, NULL, + rmw_xiselect }, + [CSR_VSIREG] = { "vsireg", csrind_or_aia_hmode, NULL, NULL, + rmw_xireg }, + + /* Virtual Supervisor Indirect Alias */ + [CSR_VSIREG2] = { "vsireg2", csrind_hmode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIREG3] = { "vsireg3", csrind_hmode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIREG4] = { "vsireg4", csrind_hmode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIREG5] = { "vsireg5", csrind_hmode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_VSIREG6] = { "vsireg6", csrind_hmode, NULL, NULL, rmw_xiregi, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* VS-Level Interrupts (H-extension with AIA) */ [CSR_VSTOPEI] = { "vstopei", aia_hmode, NULL, NULL, rmw_xtopei }, @@ -4367,8 +6095,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 }, @@ -4378,7 +6105,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 }, @@ -4402,30 +6129,18 @@ 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 }, - - /* User Pointer Masking */ - [CSR_UMTE] = { "umte", pointer_masking, read_umte, write_umte }, - [CSR_UPMMASK] = { "upmmask", pointer_masking, read_upmmask, - write_upmmask }, - [CSR_UPMBASE] = { "upmbase", pointer_masking, read_upmbase, - write_upmbase }, - /* Machine Pointer Masking */ - [CSR_MMTE] = { "mmte", pointer_masking, read_mmte, write_mmte }, - [CSR_MPMMASK] = { "mpmmask", pointer_masking, read_mpmmask, - write_mpmmask }, - [CSR_MPMBASE] = { "mpmbase", pointer_masking, read_mpmbase, - write_mpmbase }, - /* Supervisor Pointer Masking */ - [CSR_SMTE] = { "smte", pointer_masking, read_smte, write_smte }, - [CSR_SPMMASK] = { "spmmask", pointer_masking, read_spmmask, - write_spmmask }, - [CSR_SPMBASE] = { "spmbase", pointer_masking, read_spmbase, - write_spmbase }, + [CSR_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 }, + + [CSR_MCTRCTL] = { "mctrctl", ctr_mmode, NULL, NULL, rmw_xctrctl }, + [CSR_SCTRCTL] = { "sctrctl", ctr_smode, NULL, NULL, rmw_xctrctl }, + [CSR_VSCTRCTL] = { "vsctrctl", ctr_smode, NULL, NULL, rmw_xctrctl }, + [CSR_SCTRDEPTH] = { "sctrdepth", ctr_smode, NULL, NULL, rmw_sctrdepth }, + [CSR_SCTRSTATUS] = { "sctrstatus", ctr_smode, NULL, NULL, rmw_sctrstatus }, /* Performance Counters */ [CSR_HPMCOUNTER3] = { "hpmcounter3", ctr, read_hpmcounter }, @@ -4521,6 +6236,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, @@ -4580,91 +6302,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 4945d1a1f25..56644667497 100644 --- a/target/riscv/debug.c +++ b/target/riscv/debug.c @@ -28,9 +28,10 @@ #include "qapi/error.h" #include "cpu.h" #include "trace.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" -#include "sysemu/cpu-timers.h" +#include "exec/watchpoint.h" +#include "system/cpu-timers.h" +#include "exec/icount.h" /* * The following M-mode trigger CSRs are implemented: @@ -217,6 +218,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); @@ -241,6 +302,119 @@ 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) @@ -305,7 +479,7 @@ static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index) bool enabled = type2_breakpoint_enabled(ctrl); CPUState *cs = env_cpu(env); int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; - uint32_t size; + uint32_t size, def_size; if (!enabled) { return; @@ -328,7 +502,9 @@ static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index) cpu_watchpoint_insert(cs, addr, size, flags, &env->cpu_watchpoint[index]); } else { - cpu_watchpoint_insert(cs, addr, 8, flags, + def_size = riscv_cpu_mxl(env) == MXL_RV64 ? 8 : 4; + + cpu_watchpoint_insert(cs, addr, def_size, flags, &env->cpu_watchpoint[index]); } } @@ -371,14 +547,11 @@ 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(); } - - return; } /* type 6 trigger */ @@ -488,14 +661,11 @@ 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(); } - - return; } /* icount trigger type */ @@ -554,7 +724,7 @@ void helper_itrigger_match(CPURISCVState *env) if (get_trigger_type(env, i) != TRIGGER_TYPE_INST_CNT) { continue; } - if (check_itrigger_priv(env, i)) { + if (!trigger_common_match(env, TRIGGER_TYPE_INST_CNT, i)) { continue; } count = itrigger_get_count(env, i); @@ -671,14 +841,11 @@ static void itrigger_reg_write(CPURISCVState *env, target_ulong index, "tdata2 is not supported for icount trigger\n"); break; case TDATA3: - qemu_log_mask(LOG_UNIMP, - "tdata3 is not supported for icount trigger\n"); + env->tdata3[index] = textra_validate(env, val); break; default: g_assert_not_reached(); } - - return; } static int itrigger_get_adjust_count(CPURISCVState *env) @@ -785,21 +952,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 (env->virt_enabled) { - return false; - } - ctrl = env->tdata1[i]; pc = env->tdata2[i]; if ((ctrl & TYPE2_EXEC) && (bp->pc == pc)) { - /* check U/S/M bit against current privilege level */ - if ((ctrl >> 3) & BIT(env->priv)) { - return true; - } + env->badaddr = pc; + return true; } break; case TRIGGER_TYPE_AD_MATCH6: @@ -807,17 +971,8 @@ bool riscv_cpu_debug_check_breakpoint(CPUState *cs) pc = env->tdata2[i]; if ((ctrl & TYPE6_EXEC) && (bp->pc == pc)) { - if (env->virt_enabled) { - /* check VU/VS bit against current privilege level */ - if ((ctrl >> 23) & BIT(env->priv)) { - return true; - } - } else { - /* check U/S/M bit against current privilege level */ - if ((ctrl >> 3) & BIT(env->priv)) { - return true; - } - } + env->badaddr = pc; + return true; } break; default: @@ -843,13 +998,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 (env->virt_enabled) { - return false; - } - ctrl = env->tdata1[i]; addr = env->tdata2[i]; flags = 0; @@ -862,10 +1016,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: @@ -881,17 +1032,7 @@ bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) } if ((wp->flags & flags) && (wp->vaddr == addr)) { - if (env->virt_enabled) { - /* check VU/VS bit against current privilege level */ - if ((ctrl >> 23) & BIT(env->priv)) { - return true; - } - } else { - /* check U/S/M bit against current privilege level */ - if ((ctrl >> 3) & BIT(env->priv)) { - return true; - } - } + return true; } break; default: @@ -940,4 +1081,6 @@ void riscv_trigger_reset_hold(CPURISCVState *env) 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 5794aa6ee53..f76b8f944a2 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 */ @@ -129,6 +131,9 @@ enum { #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); diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index 871a70a316e..706bdfa61d5 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" #include "internals.h" @@ -676,7 +675,7 @@ uint64_t helper_fround_h(CPURISCVState *env, uint64_t rs1) uint64_t helper_froundnx_h(CPURISCVState *env, uint64_t rs1) { - float16 frs1 = check_nanbox_s(env, rs1); + float16 frs1 = check_nanbox_h(env, rs1); frs1 = float16_round_to_int(frs1, &env->fp_status); return nanbox_h(env, frs1); } diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index 524bede8657..1934f919c01 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -49,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; @@ -61,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->def->misa_mxl_max) { case MXL_RV32: return gdb_get_reg32(mem_buf, tmp); case MXL_RV64: @@ -75,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->def->misa_mxl_max) { case MXL_RV32: tmp = (int32_t)ldl_p(mem_buf); length = 4; @@ -106,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]); @@ -119,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); @@ -128,9 +136,11 @@ static int riscv_gdb_set_fpu(CPURISCVState *env, uint8_t *mem_buf, int n) return 0; } -static int riscv_gdb_get_vector(CPURISCVState *env, GByteArray *buf, int n) +static int riscv_gdb_get_vector(CPUState *cs, GByteArray *buf, int n) { - uint16_t vlenb = riscv_cpu_cfg(env)->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; @@ -144,9 +154,11 @@ static int riscv_gdb_get_vector(CPURISCVState *env, GByteArray *buf, int n) 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 = riscv_cpu_cfg(env)->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) { @@ -158,8 +170,11 @@ static int riscv_gdb_set_vector(CPURISCVState *env, uint8_t *mem_buf, int n) 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; @@ -172,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; @@ -186,39 +204,62 @@ 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; + + /* Per RiscV debug spec v1.0.0 rc4 */ + target_ulong vbit = (env->virt_enabled) ? BIT(2) : 0; + + return gdb_get_regl(buf, env->priv | vbit); #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_RESERVED) { - cs->priv = PRV_S; + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + + target_ulong new_priv = ldtul_p(mem_buf) & 0x3; + bool new_virt = 0; + + if (new_priv == PRV_RESERVED) { + new_priv = PRV_S; } + + if (new_priv != PRV_M) { + new_virt = (ldtul_p(mem_buf) & BIT(2)) >> 2; + } + + if (riscv_has_ext(env, RVH) && new_virt != env->virt_enabled) { + riscv_cpu_swap_hypervisor_regs(env); + } + + riscv_cpu_set_mode(env, new_priv, new_virt); #endif return sizeof(target_ulong); } 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) @@ -230,9 +271,9 @@ static int riscv_gen_dynamic_csr_xml(CPUState *cs, int base_reg) 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) { @@ -240,112 +281,105 @@ static int riscv_gen_dynamic_csr_xml(CPUState *cs, int base_reg) } 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, ""); - - cpu->dyn_csr_xml = g_string_free(s, false); + gdb_feature_builder_end(&builder); #if !defined(CONFIG_USER_ONLY) env->debugger = false; #endif - return CSR_TABLE_SIZE; + 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"); } - g_string_append_printf(s, ""); + gdb_feature_builder_end(&builder); - 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) { - int base_reg = cs->gdb_num_regs; + if (cpu->cfg.ext_zve32x) { gdb_register_coprocessor(cs, riscv_gdb_get_vector, riscv_gdb_set_vector, - ricsv_gen_dynamic_vector_xml(cs, base_reg), - "riscv-vector.xml", 0); + ricsv_gen_dynamic_vector_feature(cs, cs->gdb_num_regs), + 0); } - switch (env->misa_mxl_max) { + switch (mcc->def->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(); } - if (cpu->cfg.ext_icsr) { - int base_reg = cs->gdb_num_regs; + if (cpu->cfg.ext_zicsr) { gdb_register_coprocessor(cs, riscv_gdb_get_csr, riscv_gdb_set_csr, - riscv_gen_dynamic_csr_xml(cs, base_reg), - "riscv-csr.xml", 0); + riscv_gen_dynamic_csr_feature(cs, cs->gdb_num_regs), + 0); } } diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 8a635238514..85d73e492d7 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -131,9 +131,13 @@ DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl) #ifndef CONFIG_USER_ONLY DEF_HELPER_1(sret, tl, env) DEF_HELPER_1(mret, tl, env) +DEF_HELPER_1(mnret, tl, env) +DEF_HELPER_1(ctr_clear, void, 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) +DEF_HELPER_4(ctr_add_entry, void, env, tl, tl, tl) /* Native Debug */ DEF_HELPER_1(itrigger_match, void, env) #endif diff --git a/target/riscv/insn16.decode b/target/riscv/insn16.decode index b96c534e737..bf893d1c2e9 100644 --- a/target/riscv/insn16.decode +++ b/target/riscv/insn16.decode @@ -140,6 +140,11 @@ sw 110 ... ... .. ... 00 @cs_w 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 diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 33597fe2bb1..cd23b1f3a9b 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -38,6 +38,8 @@ %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 @@ -56,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 @@ -98,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 @@ -107,16 +114,22 @@ # *** Privileged Instructions *** ecall 000000000000 00000 000 00000 1110011 ebreak 000000000001 00000 000 00000 1110011 +sctrclr 000100000100 00000 000 00000 1110011 uret 0000000 00010 00000 000 00000 1110011 sret 0001000 00010 00000 000 00000 1110011 mret 0011000 00010 00000 000 00000 1110011 wfi 0001000 00101 00000 000 00000 1110011 sfence_vma 0001001 ..... ..... 000 00000 1110011 @sfence_vma -sfence_vm 0001000 00100 ..... 000 00000 1110011 @sfence_vm + +# *** NMI *** +mnret 0111000 00010 00000 000 00000 1110011 # *** 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 @@ -236,6 +249,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 @@ -249,6 +263,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 @@ -688,14 +703,14 @@ vfredmax_vs 000111 . ..... ..... 001 ..... 1010111 @r_vm # Vector widening ordered and unordered float reduction sum vfwredusum_vs 110001 . ..... ..... 001 ..... 1010111 @r_vm vfwredosum_vs 110011 . ..... ..... 001 ..... 1010111 @r_vm -vmand_mm 011001 - ..... ..... 010 ..... 1010111 @r -vmnand_mm 011101 - ..... ..... 010 ..... 1010111 @r -vmandn_mm 011000 - ..... ..... 010 ..... 1010111 @r -vmxor_mm 011011 - ..... ..... 010 ..... 1010111 @r -vmor_mm 011010 - ..... ..... 010 ..... 1010111 @r -vmnor_mm 011110 - ..... ..... 010 ..... 1010111 @r -vmorn_mm 011100 - ..... ..... 010 ..... 1010111 @r -vmxnor_mm 011111 - ..... ..... 010 ..... 1010111 @r +vmand_mm 011001 1 ..... ..... 010 ..... 1010111 @r +vmnand_mm 011101 1 ..... ..... 010 ..... 1010111 @r +vmandn_mm 011000 1 ..... ..... 010 ..... 1010111 @r +vmxor_mm 011011 1 ..... ..... 010 ..... 1010111 @r +vmor_mm 011010 1 ..... ..... 010 ..... 1010111 @r +vmnor_mm 011110 1 ..... ..... 010 ..... 1010111 @r +vmorn_mm 011100 1 ..... ..... 010 ..... 1010111 @r +vmxnor_mm 011111 1 ..... ..... 010 ..... 1010111 @r vcpop_m 010000 . ..... 10000 010 ..... 1010111 @r2_vm vfirst_m 010000 . ..... 10001 010 ..... 1010111 @r2_vm vmsbf_m 010100 . ..... 00001 010 ..... 1010111 @r2_vm @@ -717,7 +732,7 @@ vrgather_vv 001100 . ..... ..... 000 ..... 1010111 @r_vm vrgatherei16_vv 001110 . ..... ..... 000 ..... 1010111 @r_vm vrgather_vx 001100 . ..... ..... 100 ..... 1010111 @r_vm vrgather_vi 001100 . ..... ..... 011 ..... 1010111 @r_vm -vcompress_vm 010111 - ..... ..... 010 ..... 1010111 @r +vcompress_vm 010111 1 ..... ..... 010 ..... 1010111 @r vmv1r_v 100111 1 ..... 00000 011 ..... 1010111 @r2rd vmv2r_v 100111 1 ..... 00001 011 ..... 1010111 @r2rd vmv4r_v 100111 1 ..... 00011 011 ..... 1010111 @r2rd @@ -1004,3 +1019,50 @@ vgmul_vv 101000 1 ..... 10001 010 ..... 1110111 @r2_vm_1 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 dc14d7fc7a2..8a62b4cfcd9 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -18,6 +18,12 @@ * this program. If not, see . */ +#define REQUIRE_SMRNMI(ctx) do { \ + if (!ctx->cfg_ptr->ext_smrnmi) { \ + return false; \ + } \ +} while (0) + static bool trans_ecall(DisasContext *ctx, arg_ecall *a) { /* always generates U-level ECALL, fixed in do_interrupt handler */ @@ -62,11 +68,24 @@ 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; } +static bool trans_sctrclr(DisasContext *ctx, arg_sctrclr *a) +{ +#ifndef CONFIG_USER_ONLY + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) { + gen_helper_ctr_clear(tcg_env); + return true; + } +#endif + return false; +} + static bool trans_uret(DisasContext *ctx, arg_uret *a) { return false; @@ -76,9 +95,10 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a) { #ifndef CONFIG_USER_ONLY if (has_ext(ctx, RVS)) { - decode_save_opc(ctx); + decode_save_opc(ctx, 0); translator_io_start(&ctx->base); - gen_helper_sret(cpu_pc, cpu_env); + gen_update_pc(ctx, 0); + gen_helper_sret(cpu_pc, tcg_env); exit_tb(ctx); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; } else { @@ -93,9 +113,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); + decode_save_opc(ctx, 0); translator_io_start(&ctx->base); - gen_helper_mret(cpu_pc, cpu_env); + gen_update_pc(ctx, 0); + gen_helper_mret(cpu_pc, tcg_env); exit_tb(ctx); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; return true; @@ -104,29 +125,38 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a) #endif } -static bool trans_wfi(DisasContext *ctx, arg_wfi *a) +static bool trans_mnret(DisasContext *ctx, arg_mnret *a) { #ifndef CONFIG_USER_ONLY - decode_save_opc(ctx); - gen_update_pc(ctx, ctx->cur_insn_len); - gen_helper_wfi(cpu_env); + REQUIRE_SMRNMI(ctx); + decode_save_opc(ctx, 0); + gen_helper_mnret(cpu_pc, tcg_env); + tcg_gen_exit_tb(NULL, 0); /* no chaining */ + ctx->base.is_jmp = DISAS_NORETURN; return true; #else return false; #endif } -static bool trans_sfence_vma(DisasContext *ctx, arg_sfence_vma *a) +static bool trans_wfi(DisasContext *ctx, arg_wfi *a) { #ifndef CONFIG_USER_ONLY - decode_save_opc(ctx); - gen_helper_tlb_flush(cpu_env); + decode_save_opc(ctx, 0); + gen_update_pc(ctx, ctx->cur_insn_len); + gen_helper_wfi(tcg_env); return true; -#endif +#else return false; +#endif } -static bool trans_sfence_vm(DisasContext *ctx, arg_sfence_vm *a) +static bool trans_sfence_vma(DisasContext *ctx, arg_sfence_vma *a) { +#ifndef CONFIG_USER_ONLY + 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 5f194a447bb..9cf3ae8019b 100644 --- a/target/riscv/insn_trans/trans_rva.c.inc +++ b/target/riscv/insn_trans/trans_rva.c.inc @@ -18,17 +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; - decode_save_opc(ctx); + 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); } @@ -45,7 +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); + decode_save_opc(ctx, 0); src1 = get_address(ctx, a->rs1, 0); tcg_gen_brcond_tl(TCG_COND_NE, load_res, src1, l1); @@ -64,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); @@ -79,149 +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, src2 = get_gpr(ctx, a->rs2, EXT_NONE); - - decode_save_opc(ctx); - src1 = get_address(ctx, a->rs1, 0); - func(dest, src1, src2, ctx->mem_idx, mop); - - gen_set_gpr(ctx, a->rd, dest); - 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_rvbf16.c.inc b/target/riscv/insn_trans/trans_rvbf16.c.inc index 911bc299084..066dc364c5b 100644 --- a/target/riscv/insn_trans/trans_rvbf16.c.inc +++ b/target/riscv/insn_trans/trans_rvbf16.c.inc @@ -43,7 +43,7 @@ static bool trans_fcvt_bf16_s(DisasContext *ctx, arg_fcvt_bf16_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_bf16_s(dest, cpu_env, src1); + gen_helper_fcvt_bf16_s(dest, tcg_env, src1); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -58,7 +58,7 @@ static bool trans_fcvt_s_bf16(DisasContext *ctx, arg_fcvt_s_bf16 *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fcvt_s_bf16(dest, cpu_env, src1); + gen_helper_fcvt_s_bf16(dest, tcg_env, src1); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); return true; @@ -71,23 +71,19 @@ static bool trans_vfncvtbf16_f_f_w(DisasContext *ctx, arg_vfncvtbf16_f_f_w *a) if (opfv_narrow_check(ctx, a) && (ctx->sew == MO_16)) { uint32_t data = 0; - TCGLabel *over = gen_new_label(); gen_set_rm_chkfrm(ctx, 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, 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), cpu_env, - ctx->cfg_ptr->vlen / 8, - ctx->cfg_ptr->vlen / 8, data, + vreg_ofs(ctx, a->rs2), tcg_env, + ctx->cfg_ptr->vlenb, + ctx->cfg_ptr->vlenb, data, gen_helper_vfncvtbf16_f_f_w); - mark_vs_dirty(ctx); - gen_set_label(over); + finalize_rvv_inst(ctx); return true; } return false; @@ -100,23 +96,19 @@ static bool trans_vfwcvtbf16_f_f_v(DisasContext *ctx, arg_vfwcvtbf16_f_f_v *a) if (opfv_widen_check(ctx, a) && (ctx->sew == MO_16)) { uint32_t data = 0; - TCGLabel *over = gen_new_label(); gen_set_rm_chkfrm(ctx, 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, 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), cpu_env, - ctx->cfg_ptr->vlen / 8, - ctx->cfg_ptr->vlen / 8, data, + vreg_ofs(ctx, a->rs2), tcg_env, + ctx->cfg_ptr->vlenb, + ctx->cfg_ptr->vlenb, data, gen_helper_vfwcvtbf16_f_f_v); - mark_vs_dirty(ctx); - gen_set_label(over); + finalize_rvv_inst(ctx); return true; } return false; @@ -127,14 +119,14 @@ static bool trans_vfwmaccbf16_vv(DisasContext *ctx, arg_vfwmaccbf16_vv *a) REQUIRE_FPU; REQUIRE_ZVFBFWMA(ctx); + uint8_t sew = ctx->sew; 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)) { + vext_check_dss(ctx, a->rd, a->rs1, a->rs2, a->vm) && + vext_check_input_eew(ctx, a->rd, sew + 1, a->rs1, sew, a->vm) && + vext_check_input_eew(ctx, a->rd, sew + 1, a->rs2, sew, a->vm)) { uint32_t data = 0; - TCGLabel *over = gen_new_label(); gen_set_rm_chkfrm(ctx, 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, ctx->lmul); @@ -142,12 +134,11 @@ static bool trans_vfwmaccbf16_vv(DisasContext *ctx, arg_vfwmaccbf16_vv *a) 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), cpu_env, - ctx->cfg_ptr->vlen / 8, - ctx->cfg_ptr->vlen / 8, data, + vreg_ofs(ctx, a->rs2), tcg_env, + ctx->cfg_ptr->vlenb, + ctx->cfg_ptr->vlenb, data, gen_helper_vfwmaccbf16_vv); - mark_vs_dirty(ctx); - gen_set_label(over); + finalize_rvv_inst(ctx); return true; } return false; @@ -158,8 +149,10 @@ static bool trans_vfwmaccbf16_vf(DisasContext *ctx, arg_vfwmaccbf16_vf *a) REQUIRE_FPU; REQUIRE_ZVFBFWMA(ctx); + uint8_t sew = ctx->sew; if (require_rvv(ctx) && (ctx->sew == MO_16) && vext_check_isa_ill(ctx) && - vext_check_ds(ctx, a->rd, a->rs2, a->vm)) { + vext_check_ds(ctx, a->rd, a->rs2, a->vm) && + vext_check_input_eew(ctx, a->rd, sew + 1, a->rs2, sew, a->vm)) { uint32_t data = 0; gen_set_rm(ctx, RISCV_FRM_DYN); diff --git a/target/riscv/insn_trans/trans_rvd.c.inc b/target/riscv/insn_trans/trans_rvd.c.inc index 6bdb55ef43d..30883ea37c8 100644 --- a/target/riscv/insn_trans/trans_rvd.c.inc +++ b/target/riscv/insn_trans/trans_rvd.c.inc @@ -42,13 +42,28 @@ static bool trans_fld(DisasContext *ctx, arg_fld *a) { TCGv addr; + MemOp memop = MO_TEUQ; REQUIRE_FPU; REQUIRE_EXT(ctx, RVD); - decode_save_opc(ctx); + /* + * 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; @@ -57,13 +72,22 @@ 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); - decode_save_opc(ctx); + 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; } @@ -91,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; @@ -109,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; @@ -127,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; @@ -145,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; @@ -162,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; @@ -179,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; @@ -196,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; @@ -213,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; @@ -229,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; @@ -308,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; @@ -324,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; @@ -340,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; @@ -356,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; @@ -372,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; } @@ -387,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; } @@ -402,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; } @@ -431,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; } @@ -446,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; } @@ -461,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); @@ -478,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); @@ -496,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; } @@ -512,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; } @@ -542,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); @@ -560,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 e7ab84cd9a5..ed73afe0894 100644 --- a/target/riscv/insn_trans/trans_rvf.c.inc +++ b/target/riscv/insn_trans/trans_rvf.c.inc @@ -43,14 +43,19 @@ static bool trans_flw(DisasContext *ctx, arg_flw *a) { TCGv_i64 dest; TCGv addr; + MemOp memop = MO_TEUL; REQUIRE_FPU; REQUIRE_EXT(ctx, RVF); - decode_save_opc(ctx); + 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); @@ -60,13 +65,18 @@ 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); - decode_save_opc(ctx); + 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; } @@ -93,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; @@ -110,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; @@ -127,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; @@ -144,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; @@ -160,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; @@ -176,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; @@ -192,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; @@ -208,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; @@ -223,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; @@ -363,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; @@ -378,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; @@ -393,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; } @@ -407,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; } @@ -439,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; } @@ -453,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; } @@ -467,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; } @@ -480,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; } @@ -494,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; @@ -509,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; @@ -541,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; } @@ -556,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; } @@ -571,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; @@ -587,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 3e9322130f4..03c66944308 100644 --- a/target/riscv/insn_trans/trans_rvh.c.inc +++ b/target/riscv/insn_trans/trans_rvh.c.inc @@ -44,8 +44,8 @@ static bool do_hlv(DisasContext *ctx, arg_r2 *a, TCGv dest = dest_gpr(ctx, a->rd); TCGv addr = get_gpr(ctx, a->rs1, EXT_NONE); - decode_save_opc(ctx); - func(dest, cpu_env, addr); + decode_save_opc(ctx, 0); + func(dest, tcg_env, addr); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -56,8 +56,8 @@ static bool do_hsv(DisasContext *ctx, arg_r2_s *a, TCGv addr = get_gpr(ctx, a->rs1, EXT_NONE); TCGv data = get_gpr(ctx, a->rs2, EXT_NONE); - decode_save_opc(ctx); - func(cpu_env, addr, data); + decode_save_opc(ctx, 0); + func(tcg_env, addr, data); return true; } #endif /* CONFIG_USER_ONLY */ @@ -147,8 +147,8 @@ 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; @@ -158,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 297142208ec..b9c71604687 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -36,6 +36,49 @@ 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) { TCGv target_pc = dest_gpr(ctx, a->rd); @@ -50,6 +93,51 @@ static bool trans_jal(DisasContext *ctx, arg_jal *a) return true; } +#ifndef CONFIG_USER_ONLY +/* + * Indirect calls + * - jalr x1, rs where rs != x5; + * - jalr x5, rs where rs != x1; + * - c.jalr rs1 where rs1 != x5; + * + * Indirect jumps + * - jalr x0, rs where rs != x1 and rs != x5; + * - c.jr rs1 where rs1 != x1 and rs1 != x5. + * + * Returns + * - jalr rd, rs where (rs == x1 or rs == x5) and rd != x1 and rd != x5; + * - c.jr rs1 where rs1 == x1 or rs1 == x5. + * + * Co-routine swap + * - jalr x1, x5; + * - jalr x5, x1; + * - c.jalr x5. + * + * Other indirect jumps + * - jalr rd, rs where rs != x1, rs != x5, rd != x0, rd != x1 and rd != x5. + */ +static void gen_ctr_jalr(DisasContext *ctx, arg_jalr *a, TCGv dest) +{ + TCGv src = tcg_temp_new(); + TCGv type; + + if ((a->rd == 1 && a->rs1 != 5) || (a->rd == 5 && a->rs1 != 1)) { + type = tcg_constant_tl(CTRDATA_TYPE_INDIRECT_CALL); + } else if (a->rd == 0 && a->rs1 != 1 && a->rs1 != 5) { + type = tcg_constant_tl(CTRDATA_TYPE_INDIRECT_JUMP); + } else if ((a->rs1 == 1 || a->rs1 == 5) && (a->rd != 1 && a->rd != 5)) { + type = tcg_constant_tl(CTRDATA_TYPE_RETURN); + } else if ((a->rs1 == 1 && a->rd == 5) || (a->rs1 == 5 && a->rd == 1)) { + type = tcg_constant_tl(CTRDATA_TYPE_CO_ROUTINE_SWAP); + } else { + type = tcg_constant_tl(CTRDATA_TYPE_OTHER_INDIRECT_JUMP); + } + + gen_pc_plus_diff(src, ctx, 0); + gen_helper_ctr_add_entry(tcg_env, src, dest, type); +} +#endif + static bool trans_jalr(DisasContext *ctx, arg_jalr *a) { TCGLabel *misaligned = NULL; @@ -63,7 +151,9 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a) tcg_gen_ext32s_tl(target_pc, target_pc); } - if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca) { + if (!riscv_cpu_allow_16bit_insn(ctx->cfg_ptr, + ctx->priv_ver, + ctx->misa_ext)) { TCGv t0 = tcg_temp_new(); misaligned = gen_new_label(); @@ -74,7 +164,25 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a) gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len); gen_set_gpr(ctx, a->rd, succ_pc); +#ifndef CONFIG_USER_ONLY + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) { + gen_ctr_jalr(ctx, a, target_pc); + } +#endif + 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) { @@ -176,18 +284,44 @@ static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) } else { tcg_gen_brcond_tl(cond, src1, src2, l); } + +#ifndef CONFIG_USER_ONLY + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) { + TCGv type = tcg_constant_tl(CTRDATA_TYPE_NONTAKEN_BRANCH); + TCGv dest = tcg_temp_new(); + TCGv src = tcg_temp_new(); + + gen_pc_plus_diff(src, ctx, 0); + gen_pc_plus_diff(dest, ctx, ctx->cur_insn_len); + gen_helper_ctr_add_entry(tcg_env, src, dest, type); + } +#endif + 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->cfg_ptr->ext_zca && + if (!riscv_cpu_allow_16bit_insn(ctx->cfg_ptr, + ctx->priv_ver, + ctx->misa_ext) && (a->imm & 0x3)) { /* misaligned */ TCGv target_pc = tcg_temp_new(); gen_pc_plus_diff(target_pc, ctx, a->imm); gen_exception_inst_addr_mis(ctx, target_pc); } else { +#ifndef CONFIG_USER_ONLY + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) { + TCGv type = tcg_constant_tl(CTRDATA_TYPE_TAKEN_BRANCH); + TCGv dest = tcg_temp_new(); + TCGv src = tcg_temp_new(); + + gen_pc_plus_diff(src, ctx, 0); + gen_pc_plus_diff(dest, ctx, a->imm); + gen_helper_ctr_add_entry(tcg_env, src, dest, type); + } +#endif gen_goto_tb(ctx, 0, a->imm); } ctx->pc_save = -1; @@ -266,12 +400,23 @@ static bool gen_load_i128(DisasContext *ctx, arg_lb *a, MemOp memop) static bool gen_load(DisasContext *ctx, arg_lb *a, MemOp memop) { - decode_save_opc(ctx); + bool out; + + if (ctx->cfg_ptr->ext_zama16b) { + memop |= MO_ATOM_WITHIN16; + } + decode_save_opc(ctx, 0); if (get_xl(ctx) == MXL_RV128) { - return gen_load_i128(ctx, a, memop); + out = gen_load_i128(ctx, a, memop); } else { - return gen_load_tl(ctx, a, memop); + out = gen_load_tl(ctx, a, memop); } + + if (ctx->ztso) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } + + return out; } static bool trans_lb(DisasContext *ctx, arg_lb *a) @@ -328,6 +473,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; } @@ -354,7 +503,10 @@ static bool gen_store_i128(DisasContext *ctx, arg_sb *a, MemOp memop) static bool gen_store(DisasContext *ctx, arg_sb *a, MemOp memop) { - decode_save_opc(ctx); + 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 { @@ -799,7 +951,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; } @@ -816,7 +968,7 @@ 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_update_pc(ctx, ctx->cur_insn_len); exit_tb(ctx); @@ -830,7 +982,7 @@ static bool do_csrr(DisasContext *ctx, int rd, int rc) TCGv_i32 csr = tcg_constant_i32(rc); translator_io_start(&ctx->base); - gen_helper_csrr(dest, cpu_env, csr); + gen_helper_csrr(dest, tcg_env, csr); gen_set_gpr(ctx, rd, dest); return do_csr_post(ctx); } @@ -840,7 +992,7 @@ static bool do_csrw(DisasContext *ctx, int rc, TCGv src) TCGv_i32 csr = tcg_constant_i32(rc); translator_io_start(&ctx->base); - gen_helper_csrw(cpu_env, csr, src); + gen_helper_csrw(tcg_env, csr, src); return do_csr_post(ctx); } @@ -850,7 +1002,7 @@ static bool do_csrrw(DisasContext *ctx, int rd, int rc, TCGv src, TCGv mask) TCGv_i32 csr = tcg_constant_i32(rc); translator_io_start(&ctx->base); - gen_helper_csrrw(dest, cpu_env, csr, src, mask); + gen_helper_csrrw(dest, tcg_env, csr, src, mask); gen_set_gpr(ctx, rd, dest); return do_csr_post(ctx); } @@ -862,8 +1014,8 @@ static bool do_csrr_i128(DisasContext *ctx, int rd, int rc) TCGv_i32 csr = tcg_constant_i32(rc); translator_io_start(&ctx->base); - gen_helper_csrr_i128(destl, cpu_env, csr); - tcg_gen_ld_tl(desth, cpu_env, offsetof(CPURISCVState, retxh)); + 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); } @@ -873,7 +1025,7 @@ static bool do_csrw_i128(DisasContext *ctx, int rc, TCGv srcl, TCGv srch) TCGv_i32 csr = tcg_constant_i32(rc); translator_io_start(&ctx->base); - gen_helper_csrw_i128(cpu_env, csr, srcl, srch); + gen_helper_csrw_i128(tcg_env, csr, srcl, srch); return do_csr_post(ctx); } @@ -885,8 +1037,8 @@ static bool do_csrrw_i128(DisasContext *ctx, int rd, int rc, TCGv_i32 csr = tcg_constant_i32(rc); translator_io_start(&ctx->base); - gen_helper_csrrw_i128(destl, cpu_env, csr, srcl, srch, maskl, maskh); - tcg_gen_ld_tl(desth, cpu_env, offsetof(CPURISCVState, retxh)); + 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_rvm.c.inc b/target/riscv/insn_trans/trans_rvm.c.inc index 2f0fd1f7001..795f0ccf140 100644 --- a/target/riscv/insn_trans/trans_rvm.c.inc +++ b/target/riscv/insn_trans/trans_rvm.c.inc @@ -169,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) @@ -212,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) @@ -244,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) @@ -289,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) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 63404f61fcb..2b6077ac067 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -50,6 +50,22 @@ static bool require_rvf(DisasContext *s) } } +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; + } +} + static bool require_scale_rvf(DisasContext *s) { if (s->mstatus_fs == EXT_STATUS_DISABLED) { @@ -75,8 +91,6 @@ static bool require_scale_rvfmin(DisasContext *s) } switch (s->sew) { - case MO_8: - return s->cfg_ptr->ext_zvfhmin; case MO_16: return s->cfg_ptr->ext_zve32f; case MO_32: @@ -86,10 +100,33 @@ static bool require_scale_rvfmin(DisasContext *s) } } -/* Destination vector register group cannot overlap source mask register. */ -static bool require_vm(int vm, int vd) +/* + * Source and destination vector register groups cannot overlap source mask + * register: + * + * A vector register cannot be used to provide source operands with more than + * one EEW for a single instruction. A mask register source is considered to + * have EEW=1 for this constraint. An encoding that would result in the same + * vector register being read with two or more different EEWs, including when + * the vector register appears at different positions within two or more vector + * register groups, is reserved. + * (Section 5.2) + * + * A destination vector register group can overlap a source vector + * register group only if one of the following holds: + * 1. The destination EEW equals the source EEW. + * 2. The destination EEW is smaller than the source EEW and the overlap + * is in the lowest-numbered part of the source register group. + * 3. The destination EEW is greater than the source EEW, the source EMUL + * is at least 1, and the overlap is in the highest-numbered part of + * the destination register group. + * For the purpose of determining register group overlap constraints, mask + * elements have EEW=1. + * (Section 5.2) + */ +static bool require_vm(int vm, int v) { - return (vm != 0 || vd != 0); + return (vm != 0 || v != 0); } static bool require_nf(int vd, int nf, int lmul) @@ -149,7 +186,7 @@ static bool do_vsetvl(DisasContext *s, int rd, int rs1, TCGv s2) { TCGv s1, dst; - if (!require_rvv(s) || !s->cfg_ptr->ext_zve32f) { + if (!require_rvv(s) || !s->cfg_ptr->ext_zve32x) { return false; } @@ -165,9 +202,9 @@ 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_update_pc(s, s->cur_insn_len); lookup_and_goto_ptr(s); @@ -179,15 +216,15 @@ static bool do_vsetivli(DisasContext *s, int rd, TCGv s1, TCGv s2) { TCGv dst; - if (!require_rvv(s) || !s->cfg_ptr->ext_zve32f) { + 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); + finalize_rvv_inst(s); gen_update_pc(s, s->cur_insn_len); lookup_and_goto_ptr(s); s->base.is_jmp = DISAS_NORETURN; @@ -217,7 +254,7 @@ static bool trans_vsetivli(DisasContext *s, arg_vsetivli *a) /* 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 */ @@ -342,11 +379,41 @@ static bool vext_check_ld_index(DisasContext *s, int vd, int vs2, return ret; } +/* + * Check whether a vector register is used to provide source operands with + * more than one EEW for the vector instruction. + * Returns true if the instruction has valid encoding + * Returns false if encoding violates the mismatched input EEWs constraint + */ +static bool vext_check_input_eew(DisasContext *s, int vs1, uint8_t eew_vs1, + int vs2, uint8_t eew_vs2, int vm) +{ + bool is_valid = true; + int8_t emul_vs1 = eew_vs1 - s->sew + s->lmul; + int8_t emul_vs2 = eew_vs2 - s->sew + s->lmul; + + /* When vm is 0, vs1 & vs2(EEW!=1) group can't overlap v0 (EEW=1) */ + if ((vs1 != -1 && !require_vm(vm, vs1)) || + (vs2 != -1 && !require_vm(vm, vs2))) { + is_valid = false; + } + + /* When eew_vs1 != eew_vs2, check whether vs1 and vs2 are overlapped */ + if ((vs1 != -1 && vs2 != -1) && (eew_vs1 != eew_vs2) && + is_overlapped(vs1, 1 << MAX(emul_vs1, 0), + vs2, 1 << MAX(emul_vs2, 0))) { + is_valid = false; + } + + return is_valid; +} + 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(vs, s->lmul) && + vext_check_input_eew(s, vs, s->sew, -1, s->sew, vm); } /* @@ -365,6 +432,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) && + vext_check_input_eew(s, vs1, s->sew, vs2, s->sew, vm) && require_align(vs1, s->lmul); } @@ -460,6 +528,7 @@ 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) && + vext_check_input_eew(s, vs, s->sew, -1, 0, vm) && require_align(vs, s->lmul) && require_noover(vd, s->lmul + 1, vs, s->lmul); } @@ -467,6 +536,7 @@ static bool vext_check_ds(DisasContext *s, int vd, int vs, int vm) static bool vext_check_dd(DisasContext *s, int vd, int vs, int vm) { return vext_wide_check_common(s, vd, vm) && + vext_check_input_eew(s, vs, s->sew + 1, -1, 0, vm) && require_align(vs, s->lmul + 1); } @@ -485,6 +555,7 @@ 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) && + vext_check_input_eew(s, vs1, s->sew, vs2, s->sew, vm) && require_align(vs1, s->lmul) && require_noover(vd, s->lmul + 1, vs1, s->lmul); } @@ -507,12 +578,14 @@ 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) && + vext_check_input_eew(s, vs1, s->sew, vs2, s->sew + 1, vm) && require_align(vs2, s->lmul + 1); } static bool vext_check_sd(DisasContext *s, int vd, int vs, int vm) { - bool ret = vext_narrow_check_common(s, vd, vs, vm); + bool ret = vext_narrow_check_common(s, vd, vs, vm) && + vext_check_input_eew(s, vs, s->sew + 1, -1, 0, vm); if (vd != vs) { ret &= require_noover(vd, s->lmul, vs, s->lmul + 1); } @@ -535,6 +608,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) && + vext_check_input_eew(s, vs1, s->sew, vs2, s->sew + 1, vm) && require_align(vs1, s->lmul); } @@ -570,7 +644,9 @@ static bool vext_check_slide(DisasContext *s, int vd, int vs2, { bool ret = require_align(vs2, s->lmul) && require_align(vd, s->lmul) && - require_vm(vm, vd); + require_vm(vm, vd) && + vext_check_input_eew(s, -1, 0, vs2, s->sew, vm); + if (is_over) { ret &= (vd != vs2); } @@ -616,9 +692,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_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); @@ -627,22 +700,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); + /* + * 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); + } - if (!is_store) { - mark_vs_dirty(s); + 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); } - gen_set_label(over); + finalize_rvv_inst(s); return true; } @@ -741,6 +832,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); } @@ -758,6 +850,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); } @@ -771,63 +864,302 @@ GEN_VEXT_TRANS(vlm_v, MO_8, vlm_v, ld_us_mask_op, ld_us_mask_check) GEN_VEXT_TRANS(vsm_v, MO_8, vsm_v, st_us_mask_op, st_us_mask_check) /* - *** stride load and store + * 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 accelerate vector operation. */ -typedef void gen_helper_ldst_stride(TCGv_ptr, TCGv_ptr, TCGv, - TCGv, TCGv_env, TCGv_i32); +static inline uint32_t MAXSZ(DisasContext *s) +{ + int max_sz = s->cfg_ptr->vlenb << 3; + return max_sz >> (3 - s->lmul); +} -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) +static inline uint32_t get_log2(uint32_t a) { - TCGv_ptr dest, mask; - TCGv base, stride; - TCGv_i32 desc; + uint32_t i = 0; + for (; a > 0;) { + a >>= 1; + i++; + } + return i; +} - TCGLabel *over = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); +typedef void gen_tl_ldst(TCGv, TCGv_ptr, tcg_target_long); - 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)); +/* + * Simulate the strided load/store main loop: + * + * for (i = env->vstart; i < env->vl; env->vstart = ++i) { + * k = 0; + * while (k < nf) { + * if (!vm && !vext_elem_mask(v0, i)) { + * vext_set_elems_1s(vd, vma, (i + k * max_elems) * esz, + * (i + k * max_elems + 1) * esz); + * k++; + * continue; + * } + * target_ulong addr = base + stride * i + (k << log2_esz); + * ldst(env, adjust_addr(env, addr), i + k * max_elems, vd, ra); + * k++; + * } + * } + */ +static void gen_ldst_stride_main_loop(DisasContext *s, TCGv dest, uint32_t rs1, + uint32_t rs2, uint32_t vm, uint32_t nf, + gen_tl_ldst *ld_fn, gen_tl_ldst *st_fn, + bool is_load) +{ + TCGv addr = tcg_temp_new(); + TCGv base = get_gpr(s, rs1, EXT_NONE); + TCGv stride = get_gpr(s, rs2, EXT_NONE); + + TCGv i = tcg_temp_new(); + TCGv i_esz = tcg_temp_new(); + TCGv k = tcg_temp_new(); + TCGv k_esz = tcg_temp_new(); + TCGv k_max = tcg_temp_new(); + TCGv mask = tcg_temp_new(); + TCGv mask_offs = tcg_temp_new(); + TCGv mask_offs_64 = tcg_temp_new(); + TCGv mask_elem = tcg_temp_new(); + TCGv mask_offs_rem = tcg_temp_new(); + TCGv vreg = tcg_temp_new(); + TCGv dest_offs = tcg_temp_new(); + TCGv stride_offs = tcg_temp_new(); + + uint32_t max_elems = MAXSZ(s) >> s->sew; + + TCGLabel *start = gen_new_label(); + TCGLabel *end = gen_new_label(); + TCGLabel *start_k = gen_new_label(); + TCGLabel *inc_k = gen_new_label(); + TCGLabel *end_k = gen_new_label(); + + MemOp atomicity = MO_ATOM_NONE; + if (s->sew == 0) { + atomicity = MO_ATOM_NONE; + } else { + atomicity = MO_ATOM_IFALIGN_PAIR; + } - tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); - tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); + mark_vs_dirty(s); - fn(dest, mask, base, stride, cpu_env, desc); + tcg_gen_addi_tl(mask, (TCGv)tcg_env, vreg_ofs(s, 0)); + + /* Start of outer loop. */ + tcg_gen_mov_tl(i, cpu_vstart); + gen_set_label(start); + tcg_gen_brcond_tl(TCG_COND_GE, i, cpu_vl, end); + tcg_gen_shli_tl(i_esz, i, s->sew); + /* Start of inner loop. */ + tcg_gen_movi_tl(k, 0); + gen_set_label(start_k); + tcg_gen_brcond_tl(TCG_COND_GE, k, tcg_constant_tl(nf), end_k); + /* + * If we are in mask agnostic regime and the operation is not unmasked we + * set the inactive elements to 1. + */ + if (!vm && s->vma) { + TCGLabel *active_element = gen_new_label(); + /* (i + k * max_elems) * esz */ + tcg_gen_shli_tl(mask_offs, k, get_log2(max_elems << s->sew)); + tcg_gen_add_tl(mask_offs, mask_offs, i_esz); - if (!is_store) { - mark_vs_dirty(s); + /* + * Check whether the i bit of the mask is 0 or 1. + * + * 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; + * } + */ + tcg_gen_shri_tl(mask_offs_64, mask_offs, 3); + tcg_gen_add_tl(mask_offs_64, mask_offs_64, mask); + tcg_gen_ld_i64((TCGv_i64)mask_elem, (TCGv_ptr)mask_offs_64, 0); + tcg_gen_rem_tl(mask_offs_rem, mask_offs, tcg_constant_tl(8)); + tcg_gen_shr_tl(mask_elem, mask_elem, mask_offs_rem); + tcg_gen_andi_tl(mask_elem, mask_elem, 1); + tcg_gen_brcond_tl(TCG_COND_NE, mask_elem, tcg_constant_tl(0), + active_element); + /* + * Set masked-off elements in the destination vector register to 1s. + * Store instructions simply skip this bit as memory ops access memory + * only for active elements. + */ + if (is_load) { + tcg_gen_shli_tl(mask_offs, mask_offs, s->sew); + tcg_gen_add_tl(mask_offs, mask_offs, dest); + st_fn(tcg_constant_tl(-1), (TCGv_ptr)mask_offs, 0); + } + tcg_gen_br(inc_k); + gen_set_label(active_element); } + /* + * The element is active, calculate the address with stride: + * target_ulong addr = base + stride * i + (k << log2_esz); + */ + tcg_gen_mul_tl(stride_offs, stride, i); + tcg_gen_shli_tl(k_esz, k, s->sew); + tcg_gen_add_tl(stride_offs, stride_offs, k_esz); + tcg_gen_add_tl(addr, base, stride_offs); + /* Calculate the offset in the dst/src vector register. */ + tcg_gen_shli_tl(k_max, k, get_log2(max_elems)); + tcg_gen_add_tl(dest_offs, i, k_max); + tcg_gen_shli_tl(dest_offs, dest_offs, s->sew); + tcg_gen_add_tl(dest_offs, dest_offs, dest); + if (is_load) { + tcg_gen_qemu_ld_tl(vreg, addr, s->mem_idx, MO_LE | s->sew | atomicity); + st_fn((TCGv)vreg, (TCGv_ptr)dest_offs, 0); + } else { + ld_fn((TCGv)vreg, (TCGv_ptr)dest_offs, 0); + tcg_gen_qemu_st_tl(vreg, addr, s->mem_idx, MO_LE | s->sew | atomicity); + } + /* + * We don't execute the load/store above if the element was inactive. + * We jump instead directly to incrementing k and continuing the loop. + */ + if (!vm && s->vma) { + gen_set_label(inc_k); + } + tcg_gen_addi_tl(k, k, 1); + tcg_gen_br(start_k); + /* End of the inner loop. */ + gen_set_label(end_k); - gen_set_label(over); - return true; + tcg_gen_addi_tl(i, i, 1); + tcg_gen_mov_tl(cpu_vstart, i); + tcg_gen_br(start); + + /* End of the outer loop. */ + gen_set_label(end); + + return; } -static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) + +/* + * Set the tail bytes of the strided loads/stores to 1: + * + * for (k = 0; k < nf; ++k) { + * cnt = (k * max_elems + vl) * esz; + * tot = (k * max_elems + max_elems) * esz; + * for (i = cnt; i < tot; i += esz) { + * store_1s(-1, vd[vl+i]); + * } + * } + */ +static void gen_ldst_stride_tail_loop(DisasContext *s, TCGv dest, uint32_t nf, + gen_tl_ldst *st_fn) +{ + TCGv i = tcg_temp_new(); + TCGv k = tcg_temp_new(); + TCGv tail_cnt = tcg_temp_new(); + TCGv tail_tot = tcg_temp_new(); + TCGv tail_addr = tcg_temp_new(); + + TCGLabel *start = gen_new_label(); + TCGLabel *end = gen_new_label(); + TCGLabel *start_i = gen_new_label(); + TCGLabel *end_i = gen_new_label(); + + uint32_t max_elems_b = MAXSZ(s); + uint32_t esz = 1 << s->sew; + + /* Start of the outer loop. */ + tcg_gen_movi_tl(k, 0); + tcg_gen_shli_tl(tail_cnt, cpu_vl, s->sew); + tcg_gen_movi_tl(tail_tot, max_elems_b); + tcg_gen_add_tl(tail_addr, dest, tail_cnt); + gen_set_label(start); + tcg_gen_brcond_tl(TCG_COND_GE, k, tcg_constant_tl(nf), end); + /* Start of the inner loop. */ + tcg_gen_mov_tl(i, tail_cnt); + gen_set_label(start_i); + tcg_gen_brcond_tl(TCG_COND_GE, i, tail_tot, end_i); + /* store_1s(-1, vd[vl+i]); */ + st_fn(tcg_constant_tl(-1), (TCGv_ptr)tail_addr, 0); + tcg_gen_addi_tl(tail_addr, tail_addr, esz); + tcg_gen_addi_tl(i, i, esz); + tcg_gen_br(start_i); + /* End of the inner loop. */ + gen_set_label(end_i); + /* Update the counts */ + tcg_gen_addi_tl(tail_cnt, tail_cnt, max_elems_b); + tcg_gen_addi_tl(tail_tot, tail_cnt, max_elems_b); + tcg_gen_addi_tl(k, k, 1); + tcg_gen_br(start); + /* End of the outer loop. */ + gen_set_label(end); + + return; +} + +static bool ldst_stride_trans(uint32_t vd, uint32_t rs1, uint32_t rs2, + uint32_t data, DisasContext *s, bool is_load) { - uint32_t data = 0; - gen_helper_ldst_stride *fn; - static gen_helper_ldst_stride * const fns[4] = { - gen_helper_vlse8_v, gen_helper_vlse16_v, - gen_helper_vlse32_v, gen_helper_vlse64_v + if (!s->vstart_eq_zero) { + return false; + } + + TCGv dest = tcg_temp_new(); + + uint32_t nf = FIELD_EX32(data, VDATA, NF); + uint32_t vm = FIELD_EX32(data, VDATA, VM); + + /* Destination register and mask register */ + tcg_gen_addi_tl(dest, (TCGv)tcg_env, vreg_ofs(s, vd)); + + /* + * Select the appropriate load/tore to retrieve data from the vector + * register given a specific sew. + */ + static gen_tl_ldst * const ld_fns[4] = { + tcg_gen_ld8u_tl, tcg_gen_ld16u_tl, + tcg_gen_ld32u_tl, tcg_gen_ld_tl }; - fn = fns[eew]; - if (fn == NULL) { + static gen_tl_ldst * const st_fns[4] = { + tcg_gen_st8_tl, tcg_gen_st16_tl, + tcg_gen_st32_tl, tcg_gen_st_tl + }; + + gen_tl_ldst *ld_fn = ld_fns[s->sew]; + gen_tl_ldst *st_fn = st_fns[s->sew]; + + if (ld_fn == NULL || st_fn == NULL) { return false; } + mark_vs_dirty(s); + + gen_ldst_stride_main_loop(s, dest, rs1, rs2, vm, nf, ld_fn, st_fn, is_load); + + tcg_gen_movi_tl(cpu_vstart, 0); + + /* + * Set the tail bytes to 1 if tail agnostic: + */ + if (s->vta != 0 && is_load) { + gen_ldst_stride_tail_loop(s, dest, nf, st_fn); + } + + finalize_rvv_inst(s); + return true; +} + +static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) +{ + uint32_t data = 0; + uint8_t emul = vext_get_emul(s, eew); data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, emul); data = FIELD_DP32(data, VDATA, NF, a->nf); data = FIELD_DP32(data, VDATA, VTA, s->vta); data = FIELD_DP32(data, VDATA, VMA, s->vma); - return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s, false); + return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, s, true); } static bool ld_stride_check(DisasContext *s, arg_rnfvm* a, uint8_t eew) @@ -845,23 +1177,13 @@ GEN_VEXT_TRANS(vlse64_v, MO_64, rnfvm, ld_stride_op, ld_stride_check) static bool st_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) { uint32_t data = 0; - gen_helper_ldst_stride *fn; - static gen_helper_ldst_stride * const fns[4] = { - /* masked stride store */ - gen_helper_vsse8_v, gen_helper_vsse16_v, - gen_helper_vsse32_v, gen_helper_vsse64_v - }; uint8_t emul = vext_get_emul(s, eew); data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, emul); data = FIELD_DP32(data, VDATA, NF, a->nf); - fn = fns[eew]; - if (fn == NULL) { - 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, s, false); } static bool st_stride_check(DisasContext *s, arg_rnfvm* a, uint8_t eew) @@ -884,33 +1206,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_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); - if (!is_store) { - mark_vs_dirty(s); - } + fn(dest, mask, base, index, tcg_env, desc); - gen_set_label(over); + finalize_rvv_inst(s); return true; } @@ -953,14 +1270,15 @@ 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) { return require_rvv(s) && vext_check_isa_ill(s) && - vext_check_ld_index(s, a->rd, a->rs2, a->nf, a->vm, eew); + vext_check_ld_index(s, a->rd, a->rs2, a->nf, a->vm, eew) && + vext_check_input_eew(s, -1, 0, a->rs2, eew, a->vm); } GEN_VEXT_TRANS(vlxei8_v, MO_8, rnfvm, ld_index_op, ld_index_check) @@ -1005,14 +1323,15 @@ 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) { return require_rvv(s) && vext_check_isa_ill(s) && - vext_check_st_index(s, a->rd, a->rs2, a->nf, eew); + vext_check_st_index(s, a->rd, a->rs2, a->nf, eew) && + vext_check_input_eew(s, a->rd, s->sew, a->rs2, eew, a->vm); } GEN_VEXT_TRANS(vsxei8_v, MO_8, rnfvm, st_index_op, st_index_check) @@ -1030,22 +1349,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_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); - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } @@ -1083,32 +1398,88 @@ 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) + uint32_t log2_esz, gen_helper_ldst_whole *fn, + DisasContext *s, bool is_load) { - uint32_t evl = (s->cfg_ptr->vlen / 8) * nf / width; - TCGLabel *over = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_GEU, cpu_vstart, evl, over); - - TCGv_ptr dest; - TCGv base; - TCGv_i32 desc; - - uint32_t data = FIELD_DP32(0, VDATA, NF, nf); - dest = tcg_temp_new_ptr(); - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, - s->cfg_ptr->vlen / 8, data)); - - base = get_gpr(s, rs1, EXT_NONE); - tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); + mark_vs_dirty(s); - fn(dest, base, cpu_env, desc); + /* + * Load/store multiple bytes per iteration. + * When possible do this atomically. + * Update vstart with the number of processed elements. + * Use the helper function if either: + * - vstart is not 0. + * - the target has 32 bit registers and we are loading/storing 64 bit long + * elements. This is to ensure that we process every element with a single + * memory instruction. + */ - if (!is_store) { - mark_vs_dirty(s); + bool use_helper_fn = !(s->vstart_eq_zero) || + (TCG_TARGET_REG_BITS == 32 && log2_esz == 3); + + if (!use_helper_fn) { + TCGv addr = tcg_temp_new(); + uint32_t size = s->cfg_ptr->vlenb * nf; + TCGv_i64 t8 = tcg_temp_new_i64(); + TCGv_i32 t4 = tcg_temp_new_i32(); + MemOp atomicity = MO_ATOM_NONE; + if (log2_esz == 0) { + atomicity = MO_ATOM_NONE; + } else { + atomicity = MO_ATOM_IFALIGN_PAIR; + } + if (TCG_TARGET_REG_BITS == 64) { + for (int i = 0; i < size; i += 8) { + addr = get_address(s, rs1, i); + if (is_load) { + tcg_gen_qemu_ld_i64(t8, addr, s->mem_idx, + MO_LE | MO_64 | atomicity); + tcg_gen_st_i64(t8, tcg_env, vreg_ofs(s, vd) + i); + } else { + tcg_gen_ld_i64(t8, tcg_env, vreg_ofs(s, vd) + i); + tcg_gen_qemu_st_i64(t8, addr, s->mem_idx, + MO_LE | MO_64 | atomicity); + } + if (i == size - 8) { + tcg_gen_movi_tl(cpu_vstart, 0); + } else { + tcg_gen_addi_tl(cpu_vstart, cpu_vstart, 8 >> log2_esz); + } + } + } else { + for (int i = 0; i < size; i += 4) { + addr = get_address(s, rs1, i); + if (is_load) { + tcg_gen_qemu_ld_i32(t4, addr, s->mem_idx, + MO_LE | MO_32 | atomicity); + tcg_gen_st_i32(t4, tcg_env, vreg_ofs(s, vd) + i); + } else { + tcg_gen_ld_i32(t4, tcg_env, vreg_ofs(s, vd) + i); + tcg_gen_qemu_st_i32(t4, addr, s->mem_idx, + MO_LE | MO_32 | atomicity); + } + if (i == size - 4) { + tcg_gen_movi_tl(cpu_vstart, 0); + } else { + tcg_gen_addi_tl(cpu_vstart, cpu_vstart, 4 >> log2_esz); + } + } + } + } else { + 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->vlenb, + s->cfg_ptr->vlenb, data)); + base = get_gpr(s, rs1, EXT_NONE); + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vd)); + fn(dest, base, tcg_env, desc); } - gen_set_label(over); + finalize_rvv_inst(s); return true; } @@ -1116,58 +1487,47 @@ 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) \ -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 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) +#define GEN_LDST_WHOLE_TRANS(NAME, ETYPE, ARG_NF, IS_LOAD) \ +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, ctzl(sizeof(ETYPE)), \ + gen_helper_##NAME, s, IS_LOAD); \ + } \ + return false; \ +} + +GEN_LDST_WHOLE_TRANS(vl1re8_v, int8_t, 1, true) +GEN_LDST_WHOLE_TRANS(vl1re16_v, int16_t, 1, true) +GEN_LDST_WHOLE_TRANS(vl1re32_v, int32_t, 1, true) +GEN_LDST_WHOLE_TRANS(vl1re64_v, int64_t, 1, true) +GEN_LDST_WHOLE_TRANS(vl2re8_v, int8_t, 2, true) +GEN_LDST_WHOLE_TRANS(vl2re16_v, int16_t, 2, true) +GEN_LDST_WHOLE_TRANS(vl2re32_v, int32_t, 2, true) +GEN_LDST_WHOLE_TRANS(vl2re64_v, int64_t, 2, true) +GEN_LDST_WHOLE_TRANS(vl4re8_v, int8_t, 4, true) +GEN_LDST_WHOLE_TRANS(vl4re16_v, int16_t, 4, true) +GEN_LDST_WHOLE_TRANS(vl4re32_v, int32_t, 4, true) +GEN_LDST_WHOLE_TRANS(vl4re64_v, int64_t, 4, true) +GEN_LDST_WHOLE_TRANS(vl8re8_v, int8_t, 8, true) +GEN_LDST_WHOLE_TRANS(vl8re16_v, int16_t, 8, true) +GEN_LDST_WHOLE_TRANS(vl8re32_v, int32_t, 8, true) +GEN_LDST_WHOLE_TRANS(vl8re64_v, int64_t, 8, true) /* * 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, int8_t, 1, false) +GEN_LDST_WHOLE_TRANS(vs2r_v, int8_t, 2, false) +GEN_LDST_WHOLE_TRANS(vs4r_v, int8_t, 4, false) +GEN_LDST_WHOLE_TRANS(vs8r_v, int8_t, 8, false) /* *** Vector Integer Arithmetic Instructions */ -/* - * 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. - */ -static inline uint32_t MAXSZ(DisasContext *s) -{ - int scale = s->lmul - 3; - return s->cfg_ptr->vlen >> -scale; -} - static bool opivv_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && @@ -1182,10 +1542,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(); - - 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), @@ -1199,11 +1555,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; } @@ -1235,9 +1590,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_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(); @@ -1248,17 +1600,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); - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } @@ -1283,7 +1634,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)); - mark_vs_dirty(s); + finalize_rvv_inst(s); return true; } return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s); @@ -1397,9 +1748,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_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(); @@ -1410,17 +1758,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); - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } @@ -1434,7 +1781,7 @@ do_opivi_gvec(DisasContext *s, arg_rmrr *a, GVecGen2iFn *gvec_fn, 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); @@ -1476,14 +1823,22 @@ static bool opivv_widen_check(DisasContext *s, arg_rmrr *a) vext_check_dss(s, a->rd, a->rs1, a->rs2, a->vm); } +/* OPIVV with overwrite and WIDEN */ +static bool opivv_overwrite_widen_check(DisasContext *s, arg_rmrr *a) +{ + return require_rvv(s) && + vext_check_isa_ill(s) && + vext_check_dss(s, a->rd, a->rs1, a->rs2, a->vm) && + vext_check_input_eew(s, a->rd, s->sew + 1, a->rs1, s->sew, a->vm) && + vext_check_input_eew(s, a->rd, s->sew + 1, a->rs2, s->sew, a->vm); +} + static bool do_opivv_widen(DisasContext *s, arg_rmrr *a, gen_helper_gvec_4_ptr *fn, bool (*checkfn)(DisasContext *, arg_rmrr *)) { if (checkfn(s, a)) { uint32_t data = 0; - TCGLabel *over = gen_new_label(); - 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); @@ -1492,11 +1847,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; @@ -1526,6 +1880,14 @@ static bool opivx_widen_check(DisasContext *s, arg_rmrr *a) vext_check_ds(s, a->rd, a->rs2, a->vm); } +static bool opivx_overwrite_widen_check(DisasContext *s, arg_rmrr *a) +{ + return require_rvv(s) && + vext_check_isa_ill(s) && + vext_check_ds(s, a->rd, a->rs2, a->vm) && + vext_check_input_eew(s, a->rd, s->sew + 1, a->rs2, s->sew, a->vm); +} + #define GEN_OPIVX_WIDEN_TRANS(NAME, CHECK) \ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ { \ @@ -1558,8 +1920,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_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); @@ -1568,10 +1928,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; @@ -1630,8 +1989,6 @@ 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; - TCGLabel *over = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); data = FIELD_DP32(data, VDATA, VM, vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); @@ -1639,10 +1996,9 @@ static bool opivv_trans(uint32_t vd, uint32_t vs1, uint32_t vs2, uint32_t vm, 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), 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, vs2), tcg_env, s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data, fn); + finalize_rvv_inst(s); return true; } @@ -1775,7 +2131,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)); - mark_vs_dirty(s); + finalize_rvv_inst(s); return true; } return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s); @@ -1821,8 +2177,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_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); \ @@ -1830,12 +2184,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; \ @@ -2006,13 +2359,13 @@ GEN_OPIVX_TRANS(vmadd_vx, opivx_check) GEN_OPIVX_TRANS(vnmsub_vx, opivx_check) /* Vector Widening Integer Multiply-Add Instructions */ -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, 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) +GEN_OPIVV_WIDEN_TRANS(vwmaccu_vv, opivv_overwrite_widen_check) +GEN_OPIVV_WIDEN_TRANS(vwmacc_vv, opivv_overwrite_widen_check) +GEN_OPIVV_WIDEN_TRANS(vwmaccsu_vv, opivv_overwrite_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwmaccu_vx, opivx_overwrite_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwmacc_vx, opivx_overwrite_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwmaccsu_vx, opivx_overwrite_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwmaccus_vx, opivx_overwrite_widen_check) /* Vector Integer Merge and Move Instructions */ static bool trans_vmv_v_v(DisasContext *s, arg_vmv_v_v *a) @@ -2032,16 +2385,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_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; @@ -2055,8 +2405,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_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); s1 = get_gpr(s, a->rs1, EXT_SIGN); @@ -2082,14 +2430,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); + 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; @@ -2105,7 +2452,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; @@ -2116,19 +2462,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_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); - - 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; @@ -2262,9 +2604,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_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); \ @@ -2274,12 +2614,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; \ @@ -2297,27 +2636,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_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); - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } @@ -2365,12 +2700,23 @@ 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); } +static bool opfvv_overwrite_widen_check(DisasContext *s, arg_rmrr *a) +{ + return require_rvv(s) && + require_rvf(s) && + require_scale_rvf(s) && + vext_check_isa_ill(s) && + vext_check_dss(s, a->rd, a->rs1, a->rs2, a->vm) && + vext_check_input_eew(s, a->rd, s->sew + 1, a->rs1, s->sew, a->vm) && + vext_check_input_eew(s, a->rd, s->sew + 1, a->rs2, s->sew, a->vm); +} + /* OPFVV with WIDEN */ #define GEN_OPFVV_WIDEN_TRANS(NAME, CHECK) \ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ @@ -2380,9 +2726,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_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); \ @@ -2390,12 +2734,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; \ @@ -2407,17 +2750,27 @@ 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); } +static bool opfvf_overwrite_widen_check(DisasContext *s, arg_rmrr *a) +{ + return require_rvv(s) && + require_rvf(s) && + require_scale_rvf(s) && + vext_check_isa_ill(s) && + vext_check_ds(s, a->rd, a->rs2, a->vm) && + vext_check_input_eew(s, a->rd, s->sew + 1, a->rs2, s->sew, a->vm); +} + /* OPFVF with WIDEN */ -#define GEN_OPFVF_WIDEN_TRANS(NAME) \ +#define GEN_OPFVF_WIDEN_TRANS(NAME, CHECK) \ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ { \ - if (opfvf_widen_check(s, a)) { \ + if (CHECK(s, a)) { \ uint32_t data = 0; \ static gen_helper_opfvf *const fns[2] = { \ gen_helper_##NAME##_h, gen_helper_##NAME##_w, \ @@ -2433,14 +2786,14 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ return false; \ } -GEN_OPFVF_WIDEN_TRANS(vfwadd_vf) -GEN_OPFVF_WIDEN_TRANS(vfwsub_vf) +GEN_OPFVF_WIDEN_TRANS(vfwadd_vf, opfvf_widen_check) +GEN_OPFVF_WIDEN_TRANS(vfwsub_vf, opfvf_widen_check) 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); } @@ -2454,9 +2807,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_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); \ @@ -2464,12 +2815,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; \ @@ -2481,8 +2831,8 @@ 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); } @@ -2519,7 +2869,7 @@ GEN_OPFVF_TRANS(vfrdiv_vf, opfvf_check) /* Vector Widening Floating-Point Multiply */ GEN_OPFVV_WIDEN_TRANS(vfwmul_vv, opfvv_widen_check) -GEN_OPFVF_WIDEN_TRANS(vfwmul_vf) +GEN_OPFVF_WIDEN_TRANS(vfwmul_vf, opfvf_widen_check) /* Vector Single-Width Floating-Point Fused Multiply-Add Instructions */ GEN_OPFVV_TRANS(vfmacc_vv, opfvv_check) @@ -2540,14 +2890,14 @@ GEN_OPFVF_TRANS(vfmsub_vf, opfvf_check) GEN_OPFVF_TRANS(vfnmsub_vf, opfvf_check) /* Vector Widening Floating-Point Fused Multiply-Add Instructions */ -GEN_OPFVV_WIDEN_TRANS(vfwmacc_vv, opfvv_widen_check) -GEN_OPFVV_WIDEN_TRANS(vfwnmacc_vv, opfvv_widen_check) -GEN_OPFVV_WIDEN_TRANS(vfwmsac_vv, opfvv_widen_check) -GEN_OPFVV_WIDEN_TRANS(vfwnmsac_vv, opfvv_widen_check) -GEN_OPFVF_WIDEN_TRANS(vfwmacc_vf) -GEN_OPFVF_WIDEN_TRANS(vfwnmacc_vf) -GEN_OPFVF_WIDEN_TRANS(vfwmsac_vf) -GEN_OPFVF_WIDEN_TRANS(vfwnmsac_vf) +GEN_OPFVV_WIDEN_TRANS(vfwmacc_vv, opfvv_overwrite_widen_check) +GEN_OPFVV_WIDEN_TRANS(vfwnmacc_vv, opfvv_overwrite_widen_check) +GEN_OPFVV_WIDEN_TRANS(vfwmsac_vv, opfvv_overwrite_widen_check) +GEN_OPFVV_WIDEN_TRANS(vfwnmsac_vv, opfvv_overwrite_widen_check) +GEN_OPFVF_WIDEN_TRANS(vfwmacc_vf, opfvf_overwrite_widen_check) +GEN_OPFVF_WIDEN_TRANS(vfwnmacc_vf, opfvf_overwrite_widen_check) +GEN_OPFVF_WIDEN_TRANS(vfwmsac_vf, opfvf_overwrite_widen_check) +GEN_OPFVF_WIDEN_TRANS(vfwnmsac_vf, opfvf_overwrite_widen_check) /* Vector Floating-Point Square-Root Instruction */ @@ -2571,20 +2921,17 @@ static bool do_opfv(DisasContext *s, arg_rmr *a, { if (checkfn(s, a)) { uint32_t data = 0; - TCGLabel *over = gen_new_label(); gen_set_rm_chkfrm(s, rm); - 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, 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; @@ -2671,7 +3018,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; @@ -2683,23 +3029,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_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); - - mark_vs_dirty(s); - gen_set_label(over); + fns[s->sew - 1](dest, t1, tcg_env, desc); } + finalize_rvv_inst(s); return true; } return false; @@ -2747,8 +3089,8 @@ static bool opxfv_widen_check(DisasContext *s, arg_rmr *a) static bool opffv_widen_check(DisasContext *s, arg_rmr *a) { return opfv_widen_check(s, a) && - require_scale_rvfmin(s) && - (s->sew != MO_8); + require_rvfmin(s) && + require_scale_rvfmin(s); } #define GEN_OPFV_WIDEN_TRANS(NAME, CHECK, HELPER, FRM) \ @@ -2760,21 +3102,18 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ gen_helper_##HELPER##_h, \ gen_helper_##HELPER##_w, \ }; \ - TCGLabel *over = gen_new_label(); \ gen_set_rm_chkfrm(s, FRM); \ - 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 - 1]); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + finalize_rvv_inst(s); \ return true; \ } \ return false; \ @@ -2811,21 +3150,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_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; \ @@ -2858,15 +3194,15 @@ static bool opfxv_narrow_check(DisasContext *s, arg_rmr *a) static bool opffv_narrow_check(DisasContext *s, arg_rmr *a) { return opfv_narrow_check(s, a) && - require_scale_rvfmin(s) && - (s->sew != MO_8); + 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_scale_rvf(s) && - (s->sew != MO_8); + require_rvf(s) && + require_scale_rvf(s); } #define GEN_OPFV_NARROW_TRANS(NAME, CHECK, HELPER, FRM) \ @@ -2878,21 +3214,18 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ gen_helper_##HELPER##_h, \ gen_helper_##HELPER##_w, \ }; \ - TCGLabel *over = gen_new_label(); \ gen_set_rm_chkfrm(s, FRM); \ - 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 - 1]); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + finalize_rvv_inst(s); \ return true; \ } \ return false; \ @@ -2927,21 +3260,18 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ gen_helper_##HELPER##_h, \ gen_helper_##HELPER##_w, \ }; \ - TCGLabel *over = gen_new_label(); \ gen_set_rm_chkfrm(s, FRM); \ - 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; \ @@ -2999,8 +3329,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) @@ -3018,19 +3348,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_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; \ @@ -3061,13 +3388,13 @@ 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); return true; } @@ -3090,13 +3417,13 @@ 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); return true; } @@ -3118,8 +3445,6 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ 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); \ @@ -3128,11 +3453,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; \ @@ -3158,8 +3482,6 @@ static bool trans_viota_m(DisasContext *s, arg_viota_m *a) require_align(a->rd, s->lmul) && 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); @@ -3170,11 +3492,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; @@ -3188,8 +3509,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_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); @@ -3200,11 +3519,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; @@ -3244,7 +3562,6 @@ static void load_element(TCGv_i64 dest, TCGv_ptr base, break; default: g_assert_not_reached(); - break; } } @@ -3288,7 +3605,7 @@ 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, @@ -3306,7 +3623,7 @@ static void vec_element_loadx(DisasContext *s, TCGv_i64 dest, 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 */ @@ -3329,7 +3646,6 @@ static void store_element(TCGv_i64 val, TCGv_ptr base, break; default: g_assert_not_reached(); - break; } } @@ -3340,7 +3656,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] */ @@ -3360,6 +3676,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_gen_movi_tl(cpu_vstart, 0); + finalize_rvv_inst(s); return true; } return false; @@ -3386,8 +3704,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); - mark_vs_dirty(s); gen_set_label(over); + tcg_gen_movi_tl(cpu_vstart, 0); + finalize_rvv_inst(s); return true; } return false; @@ -3414,6 +3733,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; @@ -3439,8 +3760,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); - mark_vs_dirty(s); + gen_set_label(over); + tcg_gen_movi_tl(cpu_vstart, 0); + finalize_rvv_inst(s); return true; } return false; @@ -3490,6 +3813,7 @@ static bool vrgather_vv_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && vext_check_isa_ill(s) && + vext_check_input_eew(s, a->rs1, s->sew, a->rs2, s->sew, a->vm) && require_align(a->rd, s->lmul) && require_align(a->rs1, s->lmul) && require_align(a->rs2, s->lmul) && @@ -3502,6 +3826,7 @@ static bool vrgatherei16_vv_check(DisasContext *s, arg_rmrr *a) int8_t emul = MO_16 - s->sew + s->lmul; return require_rvv(s) && vext_check_isa_ill(s) && + vext_check_input_eew(s, a->rs1, MO_16, a->rs2, s->sew, a->vm) && (emul >= -3 && emul <= 3) && require_align(a->rd, s->lmul) && require_align(a->rs1, emul) && @@ -3521,6 +3846,7 @@ static bool vrgather_vx_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && vext_check_isa_ill(s) && + vext_check_input_eew(s, -1, MO_64, a->rs2, s->sew, a->vm) && require_align(a->rd, s->lmul) && require_align(a->rs2, s->lmul) && (a->rd != a->rs2) && @@ -3535,8 +3861,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) { @@ -3547,7 +3872,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); - 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, @@ -3566,8 +3891,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); @@ -3576,7 +3900,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, @@ -3613,47 +3937,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; \ + uint32_t maxsz = s->cfg_ptr->vlenb * LEN; \ if (s->vstart_eq_zero) { \ - /* EEW = 8 */ \ - tcg_gen_gvec_mov(MO_8, vreg_ofs(s, a->rd), \ + 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; \ @@ -3673,7 +3990,9 @@ static bool int_ext_check(DisasContext *s, arg_rmr *a, uint8_t div) require_align(a->rd, s->lmul) && require_align(a->rs2, s->lmul - div) && require_vm(a->vm, a->rd) && - require_noover(a->rd, s->lmul, a->rs2, s->lmul - div); + require_noover(a->rd, s->lmul, a->rs2, s->lmul - div) && + vext_check_input_eew(s, -1, 0, a->rs2, s->sew, a->vm); + return ret; } @@ -3681,8 +4000,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_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); static gen_helper_gvec_3_ptr * const fns[6][4] = { { @@ -3722,12 +4039,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 index c00c70dfc62..27bf3f0b684 100644 --- a/target/riscv/insn_trans/trans_rvvk.c.inc +++ b/target/riscv/insn_trans/trans_rvvk.c.inc @@ -112,24 +112,27 @@ GEN_VX_MASKED_TRANS(vclmulh_vx, vclmul_vx_check) return false; \ } -static bool zvbb_vv_check(DisasContext *s, arg_rmrr *a) +static bool zvkb_vv_check(DisasContext *s, arg_rmrr *a) { - return opivv_check(s, a) && s->cfg_ptr->ext_zvbb == true; + return opivv_check(s, a) && + (s->cfg_ptr->ext_zvbb == true || s->cfg_ptr->ext_zvkb == true); } -static bool zvbb_vx_check(DisasContext *s, arg_rmrr *a) +static bool zvkb_vx_check(DisasContext *s, arg_rmrr *a) { - return opivx_check(s, a) && s->cfg_ptr->ext_zvbb == true; + 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, zvbb_vv_check) -GEN_OPIVX_GVEC_SHIFT_TRANS_CHECK(vrol_vx, rotls, zvbb_vx_check) +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, zvbb_vv_check) -GEN_OPIVX_GVEC_SHIFT_TRANS_CHECK(vror_vx, rotrs, zvbb_vx_check) -GEN_OPIVI_GVEC_TRANS_CHECK(vror_vi, IMM_TRUNC_SEW, vror_vx, rotri, zvbb_vx_check) +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) \ @@ -147,8 +150,8 @@ GEN_OPIVI_GVEC_TRANS_CHECK(vror_vi, IMM_TRUNC_SEW, vror_vx, rotri, zvbb_vx_check } /* vandn.v[vx] */ -GEN_OPIVV_GVEC_TRANS_CHECK(vandn_vv, andc, zvbb_vv_check) -GEN_OPIVX_GVEC_TRANS_CHECK(vandn_vx, andcs, zvbb_vx_check) +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) \ @@ -161,8 +164,6 @@ GEN_OPIVX_GVEC_TRANS_CHECK(vandn_vx, andcs, zvbb_vx_check) gen_helper_##NAME##_w, \ gen_helper_##NAME##_d, \ }; \ - TCGLabel *over = gen_new_label(); \ - 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); \ @@ -170,11 +171,10 @@ GEN_OPIVX_GVEC_TRANS_CHECK(vandn_vx, andcs, zvbb_vx_check) data = FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s); \ data = FIELD_DP32(data, VDATA, VMA, s->vma); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ - vreg_ofs(s, a->rs2), cpu_env, \ - s->cfg_ptr->vlen / 8, s->cfg_ptr->vlen / 8, \ + 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; \ @@ -188,8 +188,16 @@ static bool zvbb_opiv_check(DisasContext *s, arg_rmr *a) vext_check_ss(s, a->rd, a->rs2, a->vm); } -GEN_OPIV_TRANS(vbrev8_v, zvbb_opiv_check) -GEN_OPIV_TRANS(vrev8_v, zvbb_opiv_check) +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) @@ -238,14 +246,12 @@ GEN_OPIVI_WIDEN_TRANS(vwsll_vi, IMM_ZX, vwsll_vx, vwsll_vx_check) TCGv_ptr rd_v, rs2_v; \ TCGv_i32 desc, egs; \ uint32_t data = 0; \ - TCGLabel *over = gen_new_label(); \ \ if (!s->vstart_eq_zero || !s->vl_eq_vlmax) { \ /* save opcode for unwinding in case we throw an exception */ \ - decode_save_opc(s); \ + decode_save_opc(s, 0); \ egs = tcg_constant_i32(EGS); \ - gen_helper_egs_check(egs, cpu_env); \ - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); \ + gen_helper_egs_check(egs, tcg_env); \ } \ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ @@ -256,12 +262,11 @@ GEN_OPIVI_WIDEN_TRANS(vwsll_vi, IMM_ZX, vwsll_vx, vwsll_vx_check) rd_v = tcg_temp_new_ptr(); \ rs2_v = 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(rd_v, cpu_env, vreg_ofs(s, a->rd)); \ - tcg_gen_addi_ptr(rs2_v, cpu_env, vreg_ofs(s, a->rs2)); \ - gen_helper_##NAME(rd_v, rs2_v, cpu_env, desc); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + 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; \ @@ -314,14 +319,12 @@ GEN_V_UNMASKED_TRANS(vaesem_vs, vaes_check_vs, ZVKNED_EGS) TCGv_ptr rd_v, rs2_v; \ TCGv_i32 uimm_v, desc, egs; \ uint32_t data = 0; \ - TCGLabel *over = gen_new_label(); \ \ if (!s->vstart_eq_zero || !s->vl_eq_vlmax) { \ /* save opcode for unwinding in case we throw an exception */ \ - decode_save_opc(s); \ + decode_save_opc(s, 0); \ egs = tcg_constant_i32(EGS); \ - gen_helper_egs_check(egs, cpu_env); \ - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); \ + gen_helper_egs_check(egs, tcg_env); \ } \ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ @@ -334,12 +337,11 @@ GEN_V_UNMASKED_TRANS(vaesem_vs, vaes_check_vs, ZVKNED_EGS) rs2_v = tcg_temp_new_ptr(); \ uimm_v = tcg_constant_i32(a->rs1); \ desc = tcg_constant_i32( \ - simd_desc(s->cfg_ptr->vlen / 8, s->cfg_ptr->vlen / 8, data)); \ - tcg_gen_addi_ptr(rd_v, cpu_env, vreg_ofs(s, a->rd)); \ - tcg_gen_addi_ptr(rs2_v, cpu_env, vreg_ofs(s, a->rs2)); \ - gen_helper_##NAME(rd_v, rs2_v, uimm_v, cpu_env, desc); \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + 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; \ @@ -383,15 +385,13 @@ GEN_VI_UNMASKED_TRANS(vaeskf2_vi, vaeskf2_check, ZVKNED_EGS) { \ if (CHECK(s, a)) { \ uint32_t data = 0; \ - TCGLabel *over = gen_new_label(); \ 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); \ + decode_save_opc(s, 0); \ egs = tcg_constant_i32(EGS); \ - gen_helper_egs_check(egs, cpu_env); \ - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); \ + gen_helper_egs_check(egs, tcg_env); \ } \ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ @@ -401,12 +401,11 @@ GEN_VI_UNMASKED_TRANS(vaeskf2_vi, vaeskf2_check, ZVKNED_EGS) 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), cpu_env, \ - s->cfg_ptr->vlen / 8, s->cfg_ptr->vlen / 8, \ + vreg_ofs(s, a->rs2), tcg_env, \ + s->cfg_ptr->vlenb, s->cfg_ptr->vlenb, \ data, gen_helper_##NAME); \ \ - mark_vs_dirty(s); \ - gen_set_label(over); \ + finalize_rvv_inst(s); \ return true; \ } \ return false; \ @@ -437,15 +436,13 @@ static bool trans_vsha2cl_vv(DisasContext *s, arg_rmrr *a) { if (vsha_check(s, a)) { uint32_t data = 0; - TCGLabel *over = gen_new_label(); 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); + decode_save_opc(s, 0); egs = tcg_constant_i32(ZVKNH_EGS); - gen_helper_egs_check(egs, cpu_env); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); + gen_helper_egs_check(egs, tcg_env); } data = FIELD_DP32(data, VDATA, VM, a->vm); @@ -455,13 +452,12 @@ static bool trans_vsha2cl_vv(DisasContext *s, arg_rmrr *a) 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), 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, s->sew == MO_32 ? gen_helper_vsha2cl32_vv : gen_helper_vsha2cl64_vv); - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } return false; @@ -471,15 +467,13 @@ static bool trans_vsha2ch_vv(DisasContext *s, arg_rmrr *a) { if (vsha_check(s, a)) { uint32_t data = 0; - TCGLabel *over = gen_new_label(); 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); + decode_save_opc(s, 0); egs = tcg_constant_i32(ZVKNH_EGS); - gen_helper_egs_check(egs, cpu_env); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); + gen_helper_egs_check(egs, tcg_env); } data = FIELD_DP32(data, VDATA, VM, a->vm); @@ -489,13 +483,12 @@ static bool trans_vsha2ch_vv(DisasContext *s, arg_rmrr *a) 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), 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, s->sew == MO_32 ? gen_helper_vsha2ch32_vv : gen_helper_vsha2ch64_vv); - mark_vs_dirty(s); - gen_set_label(over); + finalize_rvv_inst(s); return true; } return false; 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 00000000000..ce8edcba62a --- /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 00000000000..15e688a0331 --- /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 index 32efbff4d5a..0eef0338388 100644 --- a/target/riscv/insn_trans/trans_rvzawrs.c.inc +++ b/target/riscv/insn_trans/trans_rvzawrs.c.inc @@ -16,7 +16,7 @@ * this program. If not, see . */ -static bool trans_wrs(DisasContext *ctx) +static bool trans_wrs_sto(DisasContext *ctx, arg_wrs_sto *a) { if (!ctx->cfg_ptr->ext_zawrs) { return false; @@ -40,12 +40,23 @@ static bool trans_wrs(DisasContext *ctx) return true; } -#define GEN_TRANS_WRS(insn) \ -static bool trans_ ## insn(DisasContext *ctx, arg_ ## insn *a) \ -{ \ - (void)a; \ - return trans_wrs(ctx); \ -} +static bool trans_wrs_nto(DisasContext *ctx, arg_wrs_nto *a) +{ + if (!ctx->cfg_ptr->ext_zawrs) { + return false; + } -GEN_TRANS_WRS(wrs_nto) -GEN_TRANS_WRS(wrs_sto) + /* + * 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 index 8d8a64f4932..c77c2b927b0 100644 --- a/target/riscv/insn_trans/trans_rvzce.c.inc +++ b/target/riscv/insn_trans/trans_rvzce.c.inc @@ -203,6 +203,14 @@ static bool gen_pop(DisasContext *ctx, arg_cmpp *a, bool ret, bool ret_val) if (ret) { TCGv ret_addr = get_gpr(ctx, xRA, EXT_SIGN); +#ifndef CONFIG_USER_ONLY + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) { + TCGv type = tcg_constant_tl(CTRDATA_TYPE_RETURN); + TCGv src = tcg_temp_new(); + gen_pc_plus_diff(src, ctx, 0); + gen_helper_ctr_add_entry(tcg_env, src, ret_addr, type); + } +#endif tcg_gen_mov_tl(cpu_pc, ret_addr); tcg_gen_lookup_and_goto_ptr(); ctx->base.is_jmp = DISAS_NORETURN; @@ -293,12 +301,14 @@ 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(cpu_pc, cpu_env, tcg_constant_i32(a->index)); + 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) { @@ -307,6 +317,21 @@ static bool trans_cm_jalt(DisasContext *ctx, arg_cm_jalt *a) gen_set_gpr(ctx, xRA, succ_pc); } +#ifndef CONFIG_USER_ONLY + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) { + if (a->index >= 32) { + TCGv type = tcg_constant_tl(CTRDATA_TYPE_DIRECT_CALL); + gen_helper_ctr_add_entry(tcg_env, cpu_pc, addr, type); + } else { + TCGv type = tcg_constant_tl(CTRDATA_TYPE_DIRECT_JUMP); + gen_helper_ctr_add_entry(tcg_env, cpu_pc, addr, type); + } + } +#endif + + + 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 00000000000..72055865087 --- /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 index 0fdd2698f6e..fd7e2daebf5 100644 --- a/target/riscv/insn_trans/trans_rvzfa.c.inc +++ b/target/riscv/insn_trans/trans_rvzfa.c.inc @@ -187,7 +187,7 @@ static bool trans_fminm_s(DisasContext *ctx, arg_fminm_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fminm_s(dest, cpu_env, src1, src2); + gen_helper_fminm_s(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -204,7 +204,7 @@ static bool trans_fmaxm_s(DisasContext *ctx, arg_fmaxm_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fmaxm_s(dest, cpu_env, src1, src2); + gen_helper_fmaxm_s(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -221,7 +221,7 @@ static bool trans_fminm_d(DisasContext *ctx, arg_fminm_d *a) TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); - gen_helper_fminm_d(dest, cpu_env, src1, src2); + gen_helper_fminm_d(dest, tcg_env, src1, src2); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -238,7 +238,7 @@ static bool trans_fmaxm_d(DisasContext *ctx, arg_fmaxm_d *a) TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); TCGv_i64 src2 = get_fpr_d(ctx, a->rs2); - gen_helper_fmaxm_d(dest, cpu_env, src1, src2); + gen_helper_fmaxm_d(dest, tcg_env, src1, src2); gen_set_fpr_d(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -255,7 +255,7 @@ static bool trans_fminm_h(DisasContext *ctx, arg_fminm_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fminm_h(dest, cpu_env, src1, src2); + gen_helper_fminm_h(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -272,7 +272,7 @@ static bool trans_fmaxm_h(DisasContext *ctx, arg_fmaxm_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fmaxm_h(dest, cpu_env, src1, src2); + gen_helper_fmaxm_h(dest, tcg_env, src1, src2); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -289,7 +289,7 @@ static bool trans_fround_s(DisasContext *ctx, arg_fround_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fround_s(dest, cpu_env, src1); + gen_helper_fround_s(dest, tcg_env, src1); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -306,7 +306,7 @@ static bool trans_froundnx_s(DisasContext *ctx, arg_froundnx_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_froundnx_s(dest, cpu_env, src1); + gen_helper_froundnx_s(dest, tcg_env, src1); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -323,7 +323,7 @@ static bool trans_fround_d(DisasContext *ctx, arg_fround_d *a) TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fround_d(dest, cpu_env, src1); + gen_helper_fround_d(dest, tcg_env, src1); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -340,7 +340,7 @@ static bool trans_froundnx_d(DisasContext *ctx, arg_froundnx_d *a) TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_froundnx_d(dest, cpu_env, src1); + gen_helper_froundnx_d(dest, tcg_env, src1); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -357,7 +357,7 @@ static bool trans_fround_h(DisasContext *ctx, arg_fround_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_fround_h(dest, cpu_env, src1); + gen_helper_fround_h(dest, tcg_env, src1); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -374,7 +374,7 @@ static bool trans_froundnx_h(DisasContext *ctx, arg_froundnx_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); gen_set_rm(ctx, a->rm); - gen_helper_froundnx_h(dest, cpu_env, src1); + gen_helper_froundnx_h(dest, tcg_env, src1); gen_set_fpr_hs(ctx, a->rd, dest); mark_fs_dirty(ctx); @@ -393,7 +393,7 @@ bool trans_fcvtmod_w_d(DisasContext *ctx, arg_fcvtmod_w_d *a) /* Rounding mode is RTZ. */ gen_set_rm(ctx, RISCV_FRM_RTZ); - gen_helper_fcvtmod_w_d(t1, cpu_env, src1); + gen_helper_fcvtmod_w_d(t1, tcg_env, src1); tcg_gen_trunc_i64_tl(dst, t1); gen_set_gpr(ctx, a->rd, dst); @@ -440,7 +440,7 @@ bool trans_fleq_s(DisasContext *ctx, arg_fleq_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fleq_s(dest, cpu_env, src1, src2); + gen_helper_fleq_s(dest, tcg_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -455,7 +455,7 @@ bool trans_fltq_s(DisasContext *ctx, arg_fltq_s *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fltq_s(dest, cpu_env, src1, src2); + gen_helper_fltq_s(dest, tcg_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -470,7 +470,7 @@ bool trans_fleq_d(DisasContext *ctx, arg_fleq_d *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fleq_d(dest, cpu_env, src1, src2); + gen_helper_fleq_d(dest, tcg_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -485,7 +485,7 @@ bool trans_fltq_d(DisasContext *ctx, arg_fltq_d *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fltq_d(dest, cpu_env, src1, src2); + gen_helper_fltq_d(dest, tcg_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -500,7 +500,7 @@ bool trans_fleq_h(DisasContext *ctx, arg_fleq_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fleq_h(dest, cpu_env, src1, src2); + gen_helper_fleq_h(dest, tcg_env, src1, src2); gen_set_gpr(ctx, a->rd, dest); return true; } @@ -515,7 +515,7 @@ bool trans_fltq_h(DisasContext *ctx, arg_fltq_h *a) TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2); - gen_helper_fltq_h(dest, cpu_env, src1, src2); + 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 4b01812fd82..bece48e6009 100644 --- a/target/riscv/insn_trans/trans_rvzfh.c.inc +++ b/target/riscv/insn_trans/trans_rvzfh.c.inc @@ -48,7 +48,7 @@ static bool trans_flh(DisasContext *ctx, arg_flh *a) REQUIRE_FPU; REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); - decode_save_opc(ctx); + decode_save_opc(ctx, 0); t0 = get_gpr(ctx, a->rs1, EXT_NONE); if (a->imm) { TCGv temp = tcg_temp_new(); @@ -71,7 +71,7 @@ static bool trans_fsh(DisasContext *ctx, arg_fsh *a) REQUIRE_FPU; REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); - decode_save_opc(ctx); + decode_save_opc(ctx, 0); t0 = get_gpr(ctx, a->rs1, EXT_NONE); if (a->imm) { TCGv temp = tcg_temp_new(); @@ -95,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; @@ -112,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; @@ -129,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; @@ -146,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; @@ -162,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; @@ -178,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; @@ -194,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; @@ -210,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; @@ -225,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; @@ -366,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; @@ -381,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; @@ -396,7 +396,7 @@ static bool trans_fcvt_s_h(DisasContext *ctx, arg_fcvt_s_h *a) 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); @@ -414,7 +414,7 @@ static bool trans_fcvt_d_h(DisasContext *ctx, arg_fcvt_d_h *a) 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); @@ -431,7 +431,7 @@ static bool trans_fcvt_h_s(DisasContext *ctx, arg_fcvt_h_s *a) 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); @@ -448,7 +448,7 @@ static bool trans_fcvt_h_d(DisasContext *ctx, arg_fcvt_h_d *a) 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); @@ -464,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; } @@ -478,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; @@ -493,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; } @@ -506,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; } @@ -520,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; } @@ -534,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; } @@ -548,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); @@ -564,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); @@ -615,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; } @@ -630,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; } @@ -645,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); @@ -662,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 index 7df9c30b58a..15711c3140c 100644 --- a/target/riscv/insn_trans/trans_rvzicbo.c.inc +++ b/target/riscv/insn_trans/trans_rvzicbo.c.inc @@ -16,42 +16,50 @@ * this program. If not, see . */ -#define REQUIRE_ZICBOM(ctx) do { \ - if (!ctx->cfg_ptr->ext_icbom) { \ - return false; \ - } \ +#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_icboz) { \ - return false; \ - } \ +#define REQUIRE_ZICBOZ(ctx) do { \ + if (!ctx->cfg_ptr->ext_zicboz) { \ + return false; \ + } \ } while (0) static bool trans_cbo_clean(DisasContext *ctx, arg_cbo_clean *a) { REQUIRE_ZICBOM(ctx); - gen_helper_cbo_clean_flush(cpu_env, cpu_gpr[a->rs1]); + 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); - gen_helper_cbo_clean_flush(cpu_env, cpu_gpr[a->rs1]); + 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); - gen_helper_cbo_inval(cpu_env, cpu_gpr[a->rs1]); + 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); - gen_helper_cbo_zero(cpu_env, cpu_gpr[a->rs1]); + 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 00000000000..b0096adcd0e --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzicfiss.c.inc @@ -0,0 +1,131 @@ +/* + * 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 . + */ + +#define REQUIRE_ZICFISS(ctx) do { \ + if (!ctx->cfg_ptr->ext_zicfiss) { \ + return false; \ + } \ +} while (0) + +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); + REQUIRE_ZICFISS(ctx); + if (ctx->priv == PRV_M) { + generate_exception(ctx, RISCV_EXCP_STORE_AMO_ACCESS_FAULT); + } + + 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); + REQUIRE_ZICFISS(ctx); + if (ctx->priv == PRV_M) { + generate_exception(ctx, RISCV_EXCP_STORE_AMO_ACCESS_FAULT); + } + + 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_rvzimop.c.inc b/target/riscv/insn_trans/trans_rvzimop.c.inc new file mode 100644 index 00000000000..165aacd2b6d --- /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 f3cd7d5c0b5..a06c3b214f1 100644 --- a/target/riscv/insn_trans/trans_svinval.c.inc +++ b/target/riscv/insn_trans/trans_svinval.c.inc @@ -28,8 +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 - 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; @@ -57,8 +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 - 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; @@ -70,8 +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 - 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; diff --git a/target/riscv/insn_trans/trans_xthead.c.inc b/target/riscv/insn_trans/trans_xthead.c.inc index da093a4cecb..22488412d4d 100644 --- a/target/riscv/insn_trans/trans_xthead.c.inc +++ b/target/riscv/insn_trans/trans_xthead.c.inc @@ -296,7 +296,7 @@ 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_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) @@ -985,14 +985,13 @@ static bool trans_th_sfence_vmas(DisasContext *ctx, arg_th_sfence_vmas *a) #ifndef CONFIG_USER_ONLY REQUIRE_PRIV_MS(ctx); - gen_helper_tlb_flush_all(cpu_env); + gen_helper_tlb_flush_all(tcg_env); return true; #else return false; #endif } -#ifndef CONFIG_USER_ONLY static void gen_th_sync_local(DisasContext *ctx) { /* @@ -1003,14 +1002,12 @@ static void gen_th_sync_local(DisasContext *ctx) tcg_gen_exit_tb(NULL, 0); ctx->base.is_jmp = DISAS_NORETURN; } -#endif static bool trans_th_sync(DisasContext *ctx, arg_th_sync *a) { (void) a; REQUIRE_XTHEADSYNC(ctx); -#ifndef CONFIG_USER_ONLY REQUIRE_PRIV_MSU(ctx); /* @@ -1019,9 +1016,6 @@ static bool trans_th_sync(DisasContext *ctx, arg_th_sync *a) gen_th_sync_local(ctx); return true; -#else - return false; -#endif } static bool trans_th_sync_i(DisasContext *ctx, arg_th_sync_i *a) @@ -1029,7 +1023,6 @@ static bool trans_th_sync_i(DisasContext *ctx, arg_th_sync_i *a) (void) a; REQUIRE_XTHEADSYNC(ctx); -#ifndef CONFIG_USER_ONLY REQUIRE_PRIV_MSU(ctx); /* @@ -1038,9 +1031,6 @@ static bool trans_th_sync_i(DisasContext *ctx, arg_th_sync_i *a) gen_th_sync_local(ctx); return true; -#else - return false; -#endif } static bool trans_th_sync_is(DisasContext *ctx, arg_th_sync_is *a) diff --git a/target/riscv/internals.h b/target/riscv/internals.h index b5f823c7ecb..4570bd50beb 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -19,7 +19,10 @@ #ifndef RISCV_CPU_INTERNALS_H #define RISCV_CPU_INTERNALS_H +#include "exec/cpu-common.h" #include "hw/registerfields.h" +#include "fpu/softfloat-types.h" +#include "target/riscv/cpu_bits.h" /* * The current MMU Modes are: @@ -30,12 +33,15 @@ * - 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) { @@ -87,7 +93,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); @@ -97,7 +103,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; } @@ -113,7 +119,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); @@ -123,7 +129,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; } @@ -136,4 +142,68 @@ static inline float16 check_nanbox_h(CPURISCVState *env, uint64_t f) } } +#ifndef CONFIG_USER_ONLY +/* Our implementation of SysemuCPUOps::has_work */ +bool riscv_cpu_has_work(CPUState *cs); +#endif + +/* Zjpm addr masking routine */ +static inline target_ulong adjust_addr_body(CPURISCVState *env, + target_ulong addr, + bool is_virt_addr) +{ + RISCVPmPmm pmm = PMM_FIELD_DISABLED; + uint32_t pmlen = 0; + bool signext = false; + + /* do nothing for rv32 mode */ + if (riscv_cpu_mxl(env) == MXL_RV32) { + return addr; + } + + /* get pmm field depending on whether addr is */ + if (is_virt_addr) { + pmm = riscv_pm_get_virt_pmm(env); + } else { + pmm = riscv_pm_get_pmm(env); + } + + /* if pointer masking is disabled, return original addr */ + if (pmm == PMM_FIELD_DISABLED) { + return addr; + } + + if (!is_virt_addr) { + signext = riscv_cpu_virt_mem_enabled(env); + } + addr = addr << pmlen; + pmlen = riscv_pm_get_pmlen(pmm); + + /* sign/zero extend masked address by N-1 bit */ + if (signext) { + addr = (target_long)addr >> pmlen; + } else { + addr = addr >> pmlen; + } + + return addr; +} + +static inline target_ulong adjust_addr(CPURISCVState *env, + target_ulong addr) +{ + return adjust_addr_body(env, addr, false); +} + +static inline target_ulong adjust_addr_virt(CPURISCVState *env, + target_ulong addr) +{ + return adjust_addr_body(env, addr, true); +} + +static inline int insn_len(uint16_t first_word) +{ + return (first_word & 3) == 3 ? 4 : 2; +} + #endif diff --git a/target/riscv/kvm-stub.c b/target/riscv/kvm-stub.c deleted file mode 100644 index 4e8fc31a213..00000000000 --- 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 c01cfb03f43..00000000000 --- a/target/riscv/kvm.c +++ /dev/null @@ -1,1231 +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 "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/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/migration.h" -#include "sysemu/runstate.h" -#include "hw/riscv/numa.h" - -void riscv_kvm_aplic_request(void *opaque, int irq, int level) -{ - kvm_set_irq(kvm_state, irq, !!level); -} - -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, name), ®); \ - if (ret) { \ - abort(); \ - } \ - } while (0) - -typedef struct KVMCPUConfig { - const char *name; - const char *description; - target_ulong offset; - int 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), -}; - -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(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 CPUCFG(_prop) offsetof(struct RISCVCPUConfig, _prop) - -#define KVM_EXT_CFG(_name, _prop, _reg_id) \ - {.name = _name, .offset = CPUCFG(_prop), \ - .kvm_reg_id = _reg_id} - -static KVMCPUConfig kvm_multi_ext_cfgs[] = { - KVM_EXT_CFG("zicbom", ext_icbom, KVM_RISCV_ISA_EXT_ZICBOM), - KVM_EXT_CFG("zicboz", ext_icboz, KVM_RISCV_ISA_EXT_ZICBOZ), - KVM_EXT_CFG("zihintpause", ext_zihintpause, KVM_RISCV_ISA_EXT_ZIHINTPAUSE), - KVM_EXT_CFG("zbb", ext_zbb, KVM_RISCV_ISA_EXT_ZBB), - 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("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_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 = CPUCFG(cbom_blocksize), - .kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicbom_block_size) -}; - -static KVMCPUConfig kvm_cboz_blocksize = { - .name = "cboz_blocksize", - .offset = CPUCFG(cboz_blocksize), - .kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicboz_block_size) -}; - -static void kvm_cpu_set_cbomz_blksize(Object *obj, Visitor *v, - const char *name, - void *opaque, Error **errp) -{ - KVMCPUConfig *cbomz_cfg = opaque; - RISCVCPU *cpu = RISCV_CPU(obj); - uint16_t value, *host_val; - - if (!visit_type_uint16(v, name, &value, errp)) { - return; - } - - host_val = kvmconfig_get_cfg_addr(cpu, cbomz_cfg); - - if (value != *host_val) { - error_report("Unable to set %s to a different value than " - "the host (%u)", - cbomz_cfg->name, *host_val); - exit(EXIT_FAILURE); - } - - cbomz_cfg->user_set = true; -} - -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(env, KVM_REG_RISCV_ISA_EXT, - multi_ext_cfg->kvm_reg_id); - reg = kvm_cpu_cfg_get(cpu, multi_ext_cfg); - ret = kvm_set_one_reg(cs, id, ®); - if (ret != 0) { - error_report("Unable to %s extension %s in KVM, error %d", - reg ? "enable" : "disable", - multi_ext_cfg->name, ret); - exit(EXIT_FAILURE); - } - } -} - -static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj) -{ - int i; - - 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", - NULL, - kvm_cpu_set_misa_ext_cfg, - NULL, misa_cfg); - object_property_set_description(cpu_obj, misa_cfg->name, - misa_cfg->description); - } - - 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", - NULL, - kvm_cpu_set_multi_ext_cfg, - NULL, multi_cfg); - } - - object_property_add(cpu_obj, "cbom_blocksize", "uint16", - NULL, kvm_cpu_set_cbomz_blksize, - NULL, &kvm_cbom_blocksize); - - object_property_add(cpu_obj, "cboz_blocksize", "uint16", - NULL, kvm_cpu_set_cbomz_blksize, - NULL, &kvm_cboz_blocksize); -} - -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; -} - -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 = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, - KVM_REG_RISCV_CONFIG_REG(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 = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, - KVM_REG_RISCV_CONFIG_REG(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 = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, - KVM_REG_RISCV_CONFIG_REG(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 = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, - KVM_REG_RISCV_CONFIG_REG(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(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_init_multiext_cfg(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(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, " - "error %d", multi_ext_cfg->name, ret); - kvm_riscv_destroy_scratch_vcpu(kvmcpu); - exit(EXIT_FAILURE); - } - } else { - multi_ext_cfg->supported = true; - } - - kvm_cpu_cfg_set(cpu, multi_ext_cfg, val); - } - - if (cpu->cfg.ext_icbom) { - kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cbom_blocksize); - } - - if (cpu->cfg.ext_icboz) { - kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cboz_blocksize); - } -} - -void kvm_riscv_init_user_properties(Object *cpu_obj) -{ - RISCVCPU *cpu = RISCV_CPU(cpu_obj); - KVMScratchCPU kvmcpu; - - if (!kvm_riscv_create_scratch_vcpu(&kvmcpu)) { - return; - } - - kvm_riscv_add_cpu_user_properties(cpu_obj); - kvm_riscv_init_machine_ids(cpu, &kvmcpu); - kvm_riscv_init_misa_ext_mask(cpu, &kvmcpu); - kvm_riscv_init_multiext_cfg(cpu, &kvmcpu); - - kvm_riscv_destroy_scratch_vcpu(&kvmcpu); -} - -const KVMCapabilityInfo kvm_arch_required_capabilities[] = { - KVM_CAP_LAST_INFO -}; - -int kvm_arch_get_registers(CPUState *cs) -{ - int ret = 0; - - ret = kvm_riscv_get_regs_core(cs); - if (ret) { - return ret; - } - - ret = kvm_riscv_get_regs_csr(cs); - if (ret) { - return ret; - } - - ret = kvm_riscv_get_regs_fp(cs); - if (ret) { - return ret; - } - - 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) -{ -} - -static int kvm_vcpu_set_machine_ids(RISCVCPU *cpu, CPUState *cs) -{ - CPURISCVState *env = &cpu->env; - target_ulong reg; - uint64_t id; - int ret; - - id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, - KVM_REG_RISCV_CONFIG_REG(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 = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, - KVM_REG_RISCV_CONFIG_REG(marchid)); - ret = kvm_set_one_reg(cs, id, &cpu->cfg.marchid); - if (ret != 0) { - return ret; - } - - id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG, - KVM_REG_RISCV_CONFIG_REG(mimpid)); - ret = kvm_set_one_reg(cs, id, &cpu->cfg.mimpid); - - return ret; -} - -int kvm_arch_init_vcpu(CPUState *cs) -{ - int ret = 0; - RISCVCPU *cpu = RISCV_CPU(cs); - - qemu_add_vm_change_state_handler(kvm_riscv_vm_state_change, cs); - - if (!object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST)) { - ret = kvm_vcpu_set_machine_ids(cpu, cs); - if (ret != 0) { - return ret; - } - } - - kvm_riscv_update_cpu_misa_ext(cpu, cs); - kvm_riscv_update_cpu_cfg_isa_ext(cpu, cs); - - return ret; -} - -int kvm_arch_msi_data_to_gsi(uint32_t data) -{ - abort(); -} - -int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route, - int vector, PCIDevice *dev) -{ - return 0; -} - -int kvm_arch_get_default_type(MachineState *ms) -{ - return 0; -} - -int kvm_arch_init(MachineState *ms, KVMState *s) -{ - return 0; -} - -int kvm_arch_irqchip_create(KVMState *s) -{ - if (kvm_kernel_irqchip_split()) { - error_report("-machine kernel_irqchip=split is not supported on RISC-V."); - exit(1); - } - - /* - * We can create the VAIA using the newer device control API. - */ - return kvm_check_extension(s, KVM_CAP_DEVICE_CTRL); -} - -int kvm_arch_process_async_events(CPUState *cs) -{ - return 0; -} - -void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) -{ -} - -MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) -{ - return MEMTXATTRS_UNSPECIFIED; -} - -bool kvm_arch_stop_on_emulation_error(CPUState *cs) -{ - return true; -} - -static int kvm_riscv_handle_sbi(CPUState *cs, struct kvm_run *run) -{ - int ret = 0; - unsigned char ch; - switch (run->riscv_sbi.extension_id) { - case SBI_EXT_0_1_CONSOLE_PUTCHAR: - ch = run->riscv_sbi.args[0]; - qemu_chr_fe_write(serial_hd(0)->be, &ch, sizeof(ch)); - break; - case SBI_EXT_0_1_CONSOLE_GETCHAR: - ret = qemu_chr_fe_read_all(serial_hd(0)->be, &ch, sizeof(ch)); - if (ret == sizeof(ch)) { - run->riscv_sbi.ret[0] = ch; - } else { - run->riscv_sbi.ret[0] = -1; - } - ret = 0; - break; - default: - qemu_log_mask(LOG_UNIMP, - "%s: un-handled SBI EXIT, specific reasons is %lu\n", - __func__, run->riscv_sbi.extension_id); - ret = -1; - break; - } - return ret; -} - -int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) -{ - int ret = 0; - switch (run->exit_reason) { - case KVM_EXIT_RISCV_SBI: - ret = kvm_riscv_handle_sbi(cs, run); - break; - default: - qemu_log_mask(LOG_UNIMP, "%s: un-handled exit reason %d\n", - __func__, run->exit_reason); - ret = -1; - break; - } - return ret; -} - -void kvm_riscv_reset_vcpu(RISCVCPU *cpu) -{ - CPURISCVState *env = &cpu->env; - - 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; -} - -static int aia_mode; - -static const char *kvm_aia_mode_str(uint64_t mode) -{ - switch (mode) { - case KVM_DEV_RISCV_AIA_MODE_EMUL: - return "emul"; - case KVM_DEV_RISCV_AIA_MODE_HWACCEL: - return "hwaccel"; - case KVM_DEV_RISCV_AIA_MODE_AUTO: - default: - return "auto"; - }; -} - -static char *riscv_get_kvm_aia(Object *obj, Error **errp) -{ - return g_strdup(kvm_aia_mode_str(aia_mode)); -} - -static void riscv_set_kvm_aia(Object *obj, const char *val, Error **errp) -{ - if (!strcmp(val, "emul")) { - aia_mode = KVM_DEV_RISCV_AIA_MODE_EMUL; - } else if (!strcmp(val, "hwaccel")) { - aia_mode = KVM_DEV_RISCV_AIA_MODE_HWACCEL; - } else if (!strcmp(val, "auto")) { - aia_mode = KVM_DEV_RISCV_AIA_MODE_AUTO; - } else { - error_setg(errp, "Invalid KVM AIA mode"); - error_append_hint(errp, "Valid values are emul, hwaccel, and auto.\n"); - } -} - -void kvm_arch_accel_class_init(ObjectClass *oc) -{ - object_class_property_add_str(oc, "riscv-aia", riscv_get_kvm_aia, - riscv_set_kvm_aia); - object_class_property_set_description(oc, "riscv-aia", - "Set KVM AIA mode. Valid values are " - "emul, hwaccel, and auto. Default " - "is auto."); - object_property_set_default_str(object_class_property_find(oc, "riscv-aia"), - "auto"); -} - -void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, - uint64_t aia_irq_num, uint64_t aia_msi_num, - uint64_t aplic_base, uint64_t imsic_base, - uint64_t guest_num) -{ - int ret, i; - int aia_fd = -1; - uint64_t default_aia_mode; - uint64_t socket_count = riscv_socket_count(machine); - uint64_t max_hart_per_socket = 0; - uint64_t socket, base_hart, hart_count, socket_imsic_base, imsic_addr; - uint64_t socket_bits, hart_bits, guest_bits; - - aia_fd = kvm_create_device(kvm_state, KVM_DEV_TYPE_RISCV_AIA, false); - - if (aia_fd < 0) { - error_report("Unable to create in-kernel irqchip"); - exit(1); - } - - ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, - KVM_DEV_RISCV_AIA_CONFIG_MODE, - &default_aia_mode, false, NULL); - if (ret < 0) { - error_report("KVM AIA: failed to get current KVM AIA mode"); - exit(1); - } - qemu_log("KVM AIA: default mode is %s\n", - kvm_aia_mode_str(default_aia_mode)); - - if (default_aia_mode != aia_mode) { - ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, - KVM_DEV_RISCV_AIA_CONFIG_MODE, - &aia_mode, true, NULL); - if (ret < 0) - warn_report("KVM AIA: failed to set KVM AIA mode"); - else - qemu_log("KVM AIA: set current mode to %s\n", - kvm_aia_mode_str(aia_mode)); - } - - ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, - KVM_DEV_RISCV_AIA_CONFIG_SRCS, - &aia_irq_num, true, NULL); - if (ret < 0) { - error_report("KVM AIA: failed to set number of input irq lines"); - exit(1); - } - - ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, - KVM_DEV_RISCV_AIA_CONFIG_IDS, - &aia_msi_num, true, NULL); - if (ret < 0) { - error_report("KVM AIA: failed to set number of msi"); - exit(1); - } - - socket_bits = find_last_bit(&socket_count, BITS_PER_LONG) + 1; - ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, - KVM_DEV_RISCV_AIA_CONFIG_GROUP_BITS, - &socket_bits, true, NULL); - if (ret < 0) { - error_report("KVM AIA: failed to set group_bits"); - exit(1); - } - - ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, - KVM_DEV_RISCV_AIA_CONFIG_GROUP_SHIFT, - &group_shift, true, NULL); - if (ret < 0) { - error_report("KVM AIA: failed to set group_shift"); - exit(1); - } - - guest_bits = guest_num == 0 ? 0 : - find_last_bit(&guest_num, BITS_PER_LONG) + 1; - ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, - KVM_DEV_RISCV_AIA_CONFIG_GUEST_BITS, - &guest_bits, true, NULL); - if (ret < 0) { - error_report("KVM AIA: failed to set guest_bits"); - exit(1); - } - - ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_ADDR, - KVM_DEV_RISCV_AIA_ADDR_APLIC, - &aplic_base, true, NULL); - if (ret < 0) { - error_report("KVM AIA: failed to set the base address of APLIC"); - exit(1); - } - - for (socket = 0; socket < socket_count; socket++) { - socket_imsic_base = imsic_base + socket * (1U << group_shift); - hart_count = riscv_socket_hart_count(machine, socket); - base_hart = riscv_socket_first_hartid(machine, socket); - - if (max_hart_per_socket < hart_count) { - max_hart_per_socket = hart_count; - } - - for (i = 0; i < hart_count; i++) { - imsic_addr = socket_imsic_base + i * IMSIC_HART_SIZE(guest_bits); - ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_ADDR, - KVM_DEV_RISCV_AIA_ADDR_IMSIC(i + base_hart), - &imsic_addr, true, NULL); - if (ret < 0) { - error_report("KVM AIA: failed to set the IMSIC address for hart %d", i); - exit(1); - } - } - } - - hart_bits = find_last_bit(&max_hart_per_socket, BITS_PER_LONG) + 1; - ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG, - KVM_DEV_RISCV_AIA_CONFIG_HART_BITS, - &hart_bits, true, NULL); - if (ret < 0) { - error_report("KVM AIA: failed to set hart_bits"); - exit(1); - } - - if (kvm_has_gsi_routing()) { - for (uint64_t idx = 0; idx < aia_irq_num + 1; ++idx) { - /* KVM AIA only has one APLIC instance */ - kvm_irqchip_add_irq_route(kvm_state, idx, 0, idx); - } - kvm_gsi_routing_allowed = true; - kvm_irqchip_commit_routes(kvm_state); - } - - ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CTRL, - KVM_DEV_RISCV_AIA_CTRL_INIT, - NULL, true, NULL); - if (ret < 0) { - error_report("KVM AIA: initialized fail"); - exit(1); - } - - kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled(); -} diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c new file mode 100644 index 00000000000..e1a04be20fe --- /dev/null +++ b/target/riscv/kvm/kvm-cpu.c @@ -0,0 +1,2185 @@ +/* + * 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 "system/system.h" +#include "system/kvm.h" +#include "system/kvm_int.h" +#include "cpu.h" +#include "trace.h" +#include "accel/accel-cpu-target.h" +#include "hw/pci/pci.h" +#include "exec/memattrs.h" +#include "system/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 "system/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; + +#define KVM_RISCV_REG_ID_U32(type, idx) (KVM_REG_RISCV | KVM_REG_SIZE_U32 | \ + type | idx) + +#define KVM_RISCV_REG_ID_U64(type, idx) (KVM_REG_RISCV | KVM_REG_SIZE_U64 | \ + type | idx) + +#if defined(TARGET_RISCV64) +#define KVM_RISCV_REG_ID_ULONG(type, idx) KVM_RISCV_REG_ID_U64(type, idx) +#else +#define KVM_RISCV_REG_ID_ULONG(type, idx) KVM_RISCV_REG_ID_U32(type, idx) +#endif + +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(name) \ + KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_CORE, \ + KVM_REG_RISCV_CORE_REG(name)) + +#define RISCV_CSR_REG(name) \ + KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_CSR, \ + KVM_REG_RISCV_CSR_REG(name)) + +#define RISCV_CONFIG_REG(name) \ + KVM_RISCV_REG_ID_ULONG(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(name) \ + KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_VECTOR, \ + KVM_REG_RISCV_VECTOR_CSR_REG(name)) + +#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; + uint32_t prop_size; + 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(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_CSR_CFG(_name, _env_prop, reg_id) \ + {.name = _name, .offset = ENV_CSR_OFFSET(_env_prop), \ + .prop_size = sizeof(((CPURISCVState *)0)->_env_prop), \ + .kvm_reg_id = reg_id} + +static KVMCPUConfig kvm_csr_cfgs[] = { + KVM_CSR_CFG("sstatus", mstatus, RISCV_CSR_REG(sstatus)), + KVM_CSR_CFG("sie", mie, RISCV_CSR_REG(sie)), + KVM_CSR_CFG("stvec", stvec, RISCV_CSR_REG(stvec)), + KVM_CSR_CFG("sscratch", sscratch, RISCV_CSR_REG(sscratch)), + KVM_CSR_CFG("sepc", sepc, RISCV_CSR_REG(sepc)), + KVM_CSR_CFG("scause", scause, RISCV_CSR_REG(scause)), + KVM_CSR_CFG("stval", stval, RISCV_CSR_REG(stval)), + KVM_CSR_CFG("sip", mip, RISCV_CSR_REG(sip)), + KVM_CSR_CFG("satp", satp, RISCV_CSR_REG(satp)), + KVM_CSR_CFG("scounteren", scounteren, RISCV_CSR_REG(scounteren)), + KVM_CSR_CFG("senvcfg", senvcfg, RISCV_CSR_REG(senvcfg)), +}; + +static void *kvmconfig_get_env_addr(RISCVCPU *cpu, KVMCPUConfig *csr_cfg) +{ + return (void *)&cpu->env + csr_cfg->offset; +} + +static uint32_t kvm_cpu_csr_get_u32(RISCVCPU *cpu, KVMCPUConfig *csr_cfg) +{ + uint32_t *val32 = kvmconfig_get_env_addr(cpu, csr_cfg); + return *val32; +} + +static uint64_t kvm_cpu_csr_get_u64(RISCVCPU *cpu, KVMCPUConfig *csr_cfg) +{ + uint64_t *val64 = kvmconfig_get_env_addr(cpu, csr_cfg); + return *val64; +} + +static void kvm_cpu_csr_set_u32(RISCVCPU *cpu, KVMCPUConfig *csr_cfg, + uint32_t val) +{ + uint32_t *val32 = kvmconfig_get_env_addr(cpu, csr_cfg); + *val32 = val; +} + +static void kvm_cpu_csr_set_u64(RISCVCPU *cpu, KVMCPUConfig *csr_cfg, + uint64_t val) +{ + uint64_t *val64 = kvmconfig_get_env_addr(cpu, csr_cfg); + *val64 = val; +} + +#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("ziccrse", ext_ziccrse, KVM_RISCV_ISA_EXT_ZICCRSE), + 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("zabha", ext_zabha, KVM_RISCV_ISA_EXT_ZABHA), + 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("smnpm", ext_smnpm, KVM_RISCV_ISA_EXT_SMNPM), + 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("sscofpmf", ext_sscofpmf, KVM_RISCV_ISA_EXT_SSCOFPMF), + KVM_EXT_CFG("ssnpm", ext_ssnpm, KVM_RISCV_ISA_EXT_SSNPM), + KVM_EXT_CFG("sstc", ext_sstc, KVM_RISCV_ISA_EXT_SSTC), + KVM_EXT_CFG("svade", ext_svade, KVM_RISCV_ISA_EXT_SVADE), + KVM_EXT_CFG("svadu", ext_svadu, KVM_RISCV_ISA_EXT_SVADU), + 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), + KVM_EXT_CFG("svvptc", ext_svvptc, KVM_RISCV_ISA_EXT_SVVPTC), +}; + +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) +{ + 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(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(regs.pc), ®); + if (ret) { + return ret; + } + env->pc = reg; + + for (i = 1; i < 32; i++) { + uint64_t id = KVM_RISCV_REG_ID_ULONG(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(regs.pc), ®); + if (ret) { + return ret; + } + + for (i = 1; i < 32; i++) { + uint64_t id = KVM_RISCV_REG_ID_ULONG(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) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + uint64_t reg; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(kvm_csr_cfgs); i++) { + KVMCPUConfig *csr_cfg = &kvm_csr_cfgs[i]; + + if (!csr_cfg->supported) { + continue; + } + + ret = kvm_get_one_reg(cs, csr_cfg->kvm_reg_id, ®); + if (ret) { + return ret; + } + + if (csr_cfg->prop_size == sizeof(uint32_t)) { + kvm_cpu_csr_set_u32(cpu, csr_cfg, (uint32_t)reg); + } else if (csr_cfg->prop_size == sizeof(uint64_t)) { + kvm_cpu_csr_set_u64(cpu, csr_cfg, reg); + } else { + g_assert_not_reached(); + } + } + + return 0; +} + +static int kvm_riscv_put_regs_csr(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + uint64_t reg; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(kvm_csr_cfgs); i++) { + KVMCPUConfig *csr_cfg = &kvm_csr_cfgs[i]; + + if (!csr_cfg->supported) { + continue; + } + + if (csr_cfg->prop_size == sizeof(uint32_t)) { + reg = kvm_cpu_csr_get_u32(cpu, csr_cfg); + } else if (csr_cfg->prop_size == sizeof(uint64_t)) { + reg = kvm_cpu_csr_get_u64(cpu, csr_cfg); + } else { + g_assert_not_reached(); + } + + ret = kvm_set_one_reg(cs, csr_cfg->kvm_reg_id, ®); + if (ret) { + return ret; + } + } + + return 0; +} + +static void kvm_riscv_reset_regs_csr(CPURISCVState *env) +{ + env->mstatus = 0; + env->mie = 0; + env->stvec = 0; + env->sscratch = 0; + env->sepc = 0; + env->scause = 0; + env->stval = 0; + env->mip = 0; + env->satp = 0; + env->scounteren = 0; + env->senvcfg = 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(RISCVCPU *cpu) +{ + uint64_t reg; + + KVM_RISCV_GET_TIMER(CPU(cpu), 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(vstart), ®); + if (ret) { + return ret; + } + env->vstart = reg; + + ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(vl), ®); + if (ret) { + return ret; + } + env->vl = reg; + + ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(vtype), ®); + if (ret) { + return ret; + } + env->vtype = reg; + + if (kvm_v_vlenb.supported) { + ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(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(vstart), ®); + if (ret) { + return ret; + } + + reg = env->vl; + ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(vl), ®); + if (ret) { + return ret; + } + + reg = env->vtype; + ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(vtype), ®); + if (ret) { + return ret; + } + + if (kvm_v_vlenb.supported) { + reg = cpu->cfg.vlenb; + ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(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) +{ + struct kvm_one_reg reg; + int ret; + + reg.id = RISCV_CONFIG_REG(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(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(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(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) +{ + struct kvm_one_reg reg; + int ret; + + reg.id = KVM_RISCV_REG_ID_ULONG(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) +{ + 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(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 void kvm_riscv_read_csr_cfg_legacy(KVMScratchCPU *kvmcpu) +{ + uint64_t val; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(kvm_csr_cfgs); i++) { + KVMCPUConfig *csr_cfg = &kvm_csr_cfgs[i]; + struct kvm_one_reg reg; + + reg.id = csr_cfg->kvm_reg_id; + reg.addr = (uint64_t)&val; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + if (errno == EINVAL) { + csr_cfg->supported = false; + } else { + error_report("Unable to read KVM CSR %s: %s", + csr_cfg->name, strerror(errno)); + exit(EXIT_FAILURE); + } + } else { + csr_cfg->supported = true; + } + } +} + +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, + 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_read_csr_cfg(struct kvm_reg_list *reglist) +{ + struct kvm_reg_list *reg_search; + uint64_t reg_id; + + for (int i = 0; i < ARRAY_SIZE(kvm_csr_cfgs); i++) { + KVMCPUConfig *csr_cfg = &kvm_csr_cfgs[i]; + + reg_id = csr_cfg->kvm_reg_id; + reg_search = bsearch(®_id, reglist->reg, reglist->n, + sizeof(uint64_t), uint64_cmp); + if (!reg_search) { + continue; + } + + csr_cfg->supported = true; + } +} + +static void kvm_riscv_init_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) +{ + g_autofree struct kvm_reg_list *reglist = NULL; + KVMCPUConfig *multi_ext_cfg; + struct kvm_one_reg reg; + struct kvm_reg_list rl_struct; + 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) { + kvm_riscv_read_multiext_legacy(cpu, kvmcpu); + kvm_riscv_read_csr_cfg_legacy(kvmcpu); + return; + } 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(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, reglist); + kvm_riscv_read_csr_cfg(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_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) +{ + target_ulong reg; + uint64_t id; + int ret; + + id = RISCV_CONFIG_REG(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(marchid); + ret = kvm_set_one_reg(cs, id, &cpu->cfg.marchid); + if (ret != 0) { + return ret; + } + + id = RISCV_CONFIG_REG(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_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + return 0; +} + +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) +{ + /* + * 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; + + 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 */ + + kvm_riscv_reset_regs_csr(env); +} + +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; + } + } + + /* + * Skip APLIC creation in KVM if we're running split mode. + * This is done by leaving KVM_DEV_RISCV_AIA_CONFIG_SRCS + * unset. We can also skip KVM_DEV_RISCV_AIA_ADDR_APLIC + * since KVM won't be using it. + */ + if (!kvm_kernel_irqchip_split()) { + 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_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); + } + } + + 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); + } + + 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(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(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, const 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 const TypeInfo riscv_kvm_cpu_type_infos[] = { + { + .name = TYPE_RISCV_CPU_HOST, + .parent = TYPE_RISCV_CPU, +#if defined(TARGET_RISCV32) + .class_data = &(const RISCVCPUDef) { + .misa_mxl_max = MXL_RV32, + .priv_spec = RISCV_PROFILE_ATTR_UNUSED, + .vext_spec = RISCV_PROFILE_ATTR_UNUSED, + .cfg.max_satp_mode = -1, + }, +#elif defined(TARGET_RISCV64) + .class_data = &(const RISCVCPUDef) { + .misa_mxl_max = MXL_RV64, + .priv_spec = RISCV_PROFILE_ATTR_UNUSED, + .vext_spec = RISCV_PROFILE_ATTR_UNUSED, + .cfg.max_satp_mode = -1, + }, +#endif + } +}; + +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_riscv.h b/target/riscv/kvm/kvm_riscv.h similarity index 84% rename from target/riscv/kvm_riscv.h rename to target/riscv/kvm/kvm_riscv.h index de8c209ebca..b2bcd1041f6 100644 --- a/target/riscv/kvm_riscv.h +++ b/target/riscv/kvm/kvm_riscv.h @@ -19,7 +19,8 @@ #ifndef QEMU_KVM_RISCV_H #define QEMU_KVM_RISCV_H -void kvm_riscv_init_user_properties(Object *cpu_obj); +#include "target/riscv/cpu-qom.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, @@ -27,5 +28,8 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, 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(RISCVCPU *cpu); #endif diff --git a/target/riscv/kvm/meson.build b/target/riscv/kvm/meson.build new file mode 100644 index 00000000000..7e924150912 --- /dev/null +++ b/target/riscv/kvm/meson.build @@ -0,0 +1 @@ +riscv_ss.add(when: 'CONFIG_KVM', if_true: files('kvm-cpu.c')) diff --git a/target/riscv/m128_helper.c b/target/riscv/m128_helper.c index ec14aaa901a..7d9b83b1b2c 100644 --- a/target/riscv/m128_helper.c +++ b/target/riscv/m128_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" target_ulong HELPER(divu_i128)(CPURISCVState *env, diff --git a/target/riscv/machine.c b/target/riscv/machine.c index c7c862cdd3c..c97e9ce9df1 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -19,9 +19,9 @@ #include "qemu/osdep.h" #include "cpu.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "migration/cpu.h" -#include "sysemu/cpu-timers.h" +#include "exec/icount.h" #include "debug.h" static bool pmp_needed(void *opaque) @@ -49,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() @@ -62,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() @@ -79,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), @@ -106,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), @@ -135,7 +138,7 @@ static const VMStateDescription vmstate_vector = { .version_id = 2, .minimum_version_id = 2, .needed = vector_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(env.vreg, RISCVCPU, 32 * RV_VLEN_MAX / 64), VMSTATE_UINTTL(env.vxrm, RISCVCPU), VMSTATE_UINTTL(env.vxsat, RISCVCPU), @@ -149,25 +152,15 @@ static const VMStateDescription vmstate_vector = { static bool pointermasking_needed(void *opaque) { - RISCVCPU *cpu = opaque; - CPURISCVState *env = &cpu->env; - - return riscv_has_ext(env, RVJ); + return false; } static const VMStateDescription vmstate_pointermasking = { .name = "cpu/pointer_masking", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .needed = pointermasking_needed, - .fields = (VMStateField[]) { - VMSTATE_UINTTL(env.mmte, RISCVCPU), - VMSTATE_UINTTL(env.mpmmask, RISCVCPU), - VMSTATE_UINTTL(env.mpmbase, RISCVCPU), - VMSTATE_UINTTL(env.spmmask, RISCVCPU), - VMSTATE_UINTTL(env.spmbase, RISCVCPU), - VMSTATE_UINTTL(env.upmmask, RISCVCPU), - VMSTATE_UINTTL(env.upmbase, RISCVCPU), + .fields = (const VMStateField[]) { VMSTATE_END_OF_LIST() } @@ -175,10 +168,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->def->misa_mxl_max == MXL_RV128; } static const VMStateDescription vmstate_rv128 = { @@ -186,7 +178,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), @@ -215,7 +207,7 @@ static const VMStateDescription vmstate_kvmtimer = { .minimum_version_id = 1, .needed = kvmtimer_needed, .post_load = cpu_kvmtimer_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64(env.kvm_timer_time, RISCVCPU), VMSTATE_UINT64(env.kvm_timer_compare, RISCVCPU), VMSTATE_UINT64(env.kvm_timer_state, RISCVCPU), @@ -249,7 +241,7 @@ static const VMStateDescription vmstate_debug = { .minimum_version_id = 2, .needed = debug_needed, .post_load = debug_post_load, - .fields = (VMStateField[]) { + .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), @@ -264,7 +256,6 @@ static int riscv_cpu_post_load(void *opaque, int version_id) CPURISCVState *env = &cpu->env; env->xl = cpu_recompute_xl(env); - riscv_cpu_update_mask(env); return 0; } @@ -280,7 +271,7 @@ static const VMStateDescription vmstate_smstateen = { .version_id = 1, .minimum_version_id = 1, .needed = smstateen_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(env.mstateen, RISCVCPU, 4), VMSTATE_UINT64_ARRAY(env.hstateen, RISCVCPU, 4), VMSTATE_UINT64_ARRAY(env.sstateen, RISCVCPU, 4), @@ -301,7 +292,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), @@ -309,24 +300,47 @@ static const VMStateDescription vmstate_envcfg = { } }; +static bool ctr_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + + return cpu->cfg.ext_smctr || cpu->cfg.ext_ssctr; +} + +static const VMStateDescription vmstate_ctr = { + .name = "cpu/ctr", + .version_id = 1, + .minimum_version_id = 1, + .needed = ctr_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(env.mctrctl, RISCVCPU), + VMSTATE_UINT32(env.sctrdepth, RISCVCPU), + VMSTATE_UINT32(env.sctrstatus, RISCVCPU), + VMSTATE_UINT64(env.vsctrctl, RISCVCPU), + VMSTATE_UINT64_ARRAY(env.ctr_src, RISCVCPU, 16 << SCTRDEPTH_MAX), + VMSTATE_UINT64_ARRAY(env.ctr_dst, RISCVCPU, 16 << SCTRDEPTH_MAX), + VMSTATE_UINT64_ARRAY(env.ctr_data, RISCVCPU, 16 << SCTRDEPTH_MAX), + VMSTATE_END_OF_LIST() + } +}; + 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() } }; @@ -343,18 +357,54 @@ static const VMStateDescription vmstate_jvt = { .version_id = 1, .minimum_version_id = 1, .needed = jvt_needed, - .fields = (VMStateField[]) { + .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 = 8, - .minimum_version_id = 8, + .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), @@ -369,7 +419,7 @@ 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_UINTTL(env.priv, RISCVCPU), VMSTATE_BOOL(env.virt_enabled, RISCVCPU), @@ -379,6 +429,9 @@ const VMStateDescription vmstate_riscv_cpu = { 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), @@ -392,9 +445,10 @@ 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.scountinhibit, 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), @@ -405,7 +459,7 @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_pmp, &vmstate_hyper, &vmstate_vector, @@ -418,6 +472,9 @@ const VMStateDescription vmstate_riscv_cpu = { &vmstate_debug, &vmstate_smstateen, &vmstate_jvt, + &vmstate_elp, + &vmstate_ssp, + &vmstate_ctr, NULL } }; diff --git a/target/riscv/meson.build b/target/riscv/meson.build index 660078bda1f..a4bd61e52a9 100644 --- a/target/riscv/meson.build +++ b/target/riscv/meson.build @@ -24,7 +24,6 @@ riscv_ss.add(files( 'zce_helper.c', 'vcrypto_helper.c' )) -riscv_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c')) riscv_system_ss = ss.source_set() riscv_system_ss.add(files( @@ -34,9 +33,13 @@ riscv_system_ss.add(files( 'monitor.c', 'machine.c', 'pmu.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_system_ss} +target_system_arch += {'riscv': riscv_system_ss} diff --git a/target/riscv/monitor.c b/target/riscv/monitor.c index f5b1ffe6c3e..100005ea4e9 100644 --- a/target/riscv/monitor.c +++ b/target/riscv/monitor.c @@ -184,7 +184,6 @@ static void mem_info_svxx(Monitor *mon, CPUArchState *env) break; default: g_assert_not_reached(); - break; } /* calculate virtual address bits */ diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 5355225d56c..557807ba4bd 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -21,15 +21,24 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "exec/cputlb.h" +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "exec/helper-proto.h" +#include "exec/tlb-flags.h" +#include "trace.h" /* Exceptions processing helpers */ G_NORETURN void riscv_raise_exception(CPURISCVState *env, - uint32_t exception, uintptr_t pc) + RISCVException exception, + uintptr_t pc) { CPUState *cs = env_cpu(env); + + trace_riscv_exception(exception, + riscv_cpu_get_trap_name(exception, false), + env->pc); + cs->exception_index = exception; cpu_loop_exit_restore(cs, pc); } @@ -51,7 +60,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()); @@ -62,7 +71,7 @@ target_ulong helper_csrr(CPURISCVState *env, int csr) void helper_csrw(CPURISCVState *env, int csr, target_ulong src) { target_ulong mask = env->xl == MXL_RV32 ? UINT32_MAX : (target_ulong)-1; - RISCVException ret = riscv_csrrw(env, csr, NULL, src, mask); + RISCVException ret = riscv_csrrw(env, csr, NULL, src, mask, GETPC()); if (ret != RISCV_EXCP_NONE) { riscv_raise_exception(env, ret, GETPC()); @@ -73,7 +82,7 @@ target_ulong helper_csrrw(CPURISCVState *env, int csr, target_ulong src, target_ulong write_mask) { target_ulong val = 0; - RISCVException ret = riscv_csrrw(env, csr, &val, src, write_mask); + RISCVException ret = riscv_csrrw(env, csr, &val, src, write_mask, GETPC()); if (ret != RISCV_EXCP_NONE) { riscv_raise_exception(env, ret, GETPC()); @@ -84,9 +93,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()); @@ -101,7 +108,7 @@ void helper_csrw_i128(CPURISCVState *env, int csr, { RISCVException ret = riscv_csrrw_i128(env, csr, NULL, int128_make128(srcl, srch), - UINT128_MAX); + UINT128_MAX, GETPC()); if (ret != RISCV_EXCP_NONE) { riscv_raise_exception(env, ret, GETPC()); @@ -109,13 +116,14 @@ void helper_csrw_i128(CPURISCVState *env, int csr, } target_ulong helper_csrrw_i128(CPURISCVState *env, int csr, - target_ulong srcl, target_ulong srch, - target_ulong maskl, target_ulong maskh) + target_ulong srcl, target_ulong srch, + target_ulong maskl, target_ulong maskh) { Int128 rv = int128_zero(); RISCVException ret = riscv_csrrw_i128(env, csr, &rv, int128_make128(srcl, srch), - int128_make128(maskl, maskh)); + int128_make128(maskl, maskh), + GETPC()); if (ret != RISCV_EXCP_NONE) { riscv_raise_exception(env, ret, GETPC()); @@ -157,7 +165,7 @@ void helper_cbo_zero(CPURISCVState *env, target_ulong address) { RISCVCPU *cpu = env_archcpu(env); uint16_t cbozlen = cpu->cfg.cboz_blocksize; - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = riscv_env_mmu_index(env, false); uintptr_t ra = GETPC(); void *mem; @@ -205,7 +213,7 @@ static void check_zicbom_access(CPURISCVState *env, uintptr_t ra) { RISCVCPU *cpu = env_archcpu(env); - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = riscv_env_mmu_index(env, false); uint16_t cbomlen = cpu->cfg.cbom_blocksize; void *phost; int ret; @@ -264,14 +272,18 @@ void helper_cbo_inval(CPURISCVState *env, target_ulong address) target_ulong helper_sret(CPURISCVState *env) { uint64_t mstatus; - target_ulong prev_priv, prev_virt; + target_ulong prev_priv, prev_virt = env->virt_enabled; + const target_ulong src_priv = env->priv; + const bool src_virt = env->virt_enabled; if (!(env->priv >= PRV_S)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } target_ulong retpc = env->sepc; - if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { + if (!riscv_cpu_allow_16bit_insn(&env_archcpu(env)->cfg, + env->priv_ver, + env->misa_ext) && (retpc & 0x3)) { riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); } @@ -289,6 +301,21 @@ target_ulong helper_sret(CPURISCVState *env) get_field(mstatus, MSTATUS_SPIE)); mstatus = set_field(mstatus, MSTATUS_SPIE, 1); mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); + + if (riscv_cpu_cfg(env)->ext_ssdbltrp) { + if (riscv_has_ext(env, RVH)) { + target_ulong prev_vu = get_field(env->hstatus, HSTATUS_SPV) && + prev_priv == PRV_U; + /* Returning to VU from HS, vsstatus.sdt = 0 */ + if (!env->virt_enabled && prev_vu) { + env->vsstatus = set_field(env->vsstatus, MSTATUS_SDT, 0); + } + } + mstatus = set_field(mstatus, MSTATUS_SDT, 0); + } + if (riscv_cpu_cfg(env)->ext_smdbltrp && env->priv >= PRV_M) { + mstatus = set_field(mstatus, MSTATUS_MDT, 0); + } if (env->priv_ver >= PRIV_VERSION_1_12_0) { mstatus = set_field(mstatus, MSTATUS_MPRV, 0); } @@ -299,7 +326,6 @@ target_ulong helper_sret(CPURISCVState *env) target_ulong hstatus = env->hstatus; prev_virt = get_field(hstatus, HSTATUS_SPV); - hstatus = set_field(hstatus, HSTATUS_SPV, 0); env->hstatus = hstatus; @@ -307,33 +333,69 @@ target_ulong helper_sret(CPURISCVState *env) if (prev_virt) { riscv_cpu_swap_hypervisor_regs(env); } + } - riscv_cpu_set_virt_enabled(env, prev_virt); + 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); - riscv_cpu_set_mode(env, prev_priv); + if (riscv_cpu_cfg(env)->ext_smctr || riscv_cpu_cfg(env)->ext_ssctr) { + riscv_ctr_add_entry(env, env->pc, retpc, CTRDATA_TYPE_EXCEP_INT_RET, + src_priv, src_virt); + } return retpc; } -target_ulong helper_mret(CPURISCVState *env) +static void check_ret_from_m_mode(CPURISCVState *env, target_ulong retpc, + target_ulong prev_priv) { if (!(env->priv >= PRV_M)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } - target_ulong retpc = env->mepc; - if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { + if (!riscv_cpu_allow_16bit_insn(&env_archcpu(env)->cfg, + env->priv_ver, + env->misa_ext) && (retpc & 0x3)) { riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); } - uint64_t mstatus = env->mstatus; - target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); - if (riscv_cpu_cfg(env)->pmp && !pmp_get_num_rules(env) && (prev_priv != PRV_M)) { riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, GETPC()); } +} +static target_ulong ssdbltrp_mxret(CPURISCVState *env, target_ulong mstatus, + target_ulong prev_priv, + target_ulong prev_virt) +{ + /* If returning to U, VS or VU, sstatus.sdt = 0 */ + if (prev_priv == PRV_U || (prev_virt && + (prev_priv == PRV_S || prev_priv == PRV_U))) { + mstatus = set_field(mstatus, MSTATUS_SDT, 0); + /* If returning to VU, vsstatus.sdt = 0 */ + if (prev_virt && prev_priv == PRV_U) { + env->vsstatus = set_field(env->vsstatus, MSTATUS_SDT, 0); + } + } + + return mstatus; +} + +target_ulong helper_mret(CPURISCVState *env) +{ + target_ulong retpc = env->mepc; + uint64_t mstatus = env->mstatus; + target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); + + check_ret_from_m_mode(env, retpc, prev_priv); target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV) && (prev_priv != PRV_M); @@ -343,23 +405,122 @@ target_ulong helper_mret(CPURISCVState *env) mstatus = set_field(mstatus, MSTATUS_MPP, riscv_has_ext(env, RVU) ? PRV_U : PRV_M); mstatus = set_field(mstatus, MSTATUS_MPV, 0); + if (riscv_cpu_cfg(env)->ext_ssdbltrp) { + mstatus = ssdbltrp_mxret(env, mstatus, prev_priv, prev_virt); + } + if (riscv_cpu_cfg(env)->ext_smdbltrp) { + mstatus = set_field(mstatus, MSTATUS_MDT, 0); + } if ((env->priv_ver >= PRIV_VERSION_1_12_0) && (prev_priv != PRV_M)) { mstatus = set_field(mstatus, MSTATUS_MPRV, 0); } env->mstatus = mstatus; - riscv_cpu_set_mode(env, prev_priv); - if (riscv_has_ext(env, RVH)) { - if (prev_virt) { - riscv_cpu_swap_hypervisor_regs(env); + 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); + + if (riscv_cpu_cfg(env)->ext_smctr || riscv_cpu_cfg(env)->ext_ssctr) { + riscv_ctr_add_entry(env, env->pc, retpc, CTRDATA_TYPE_EXCEP_INT_RET, + PRV_M, false); + } + + return retpc; +} + +target_ulong helper_mnret(CPURISCVState *env) +{ + target_ulong retpc = env->mnepc; + target_ulong prev_priv = get_field(env->mnstatus, MNSTATUS_MNPP); + target_ulong prev_virt; + + check_ret_from_m_mode(env, retpc, prev_priv); + + prev_virt = get_field(env->mnstatus, MNSTATUS_MNPV) && + (prev_priv != PRV_M); + env->mnstatus = set_field(env->mnstatus, MNSTATUS_NMIE, true); + + /* + * If MNRET changes the privilege mode to a mode + * less privileged than M, it also sets mstatus.MPRV to 0. + */ + if (prev_priv < PRV_M) { + env->mstatus = set_field(env->mstatus, MSTATUS_MPRV, false); + } + if (riscv_cpu_cfg(env)->ext_ssdbltrp) { + env->mstatus = ssdbltrp_mxret(env, env->mstatus, prev_priv, prev_virt); + } + + if (riscv_cpu_cfg(env)->ext_smdbltrp) { + if (prev_priv < PRV_M) { + env->mstatus = set_field(env->mstatus, MSTATUS_MDT, 0); } + } + + if (riscv_has_ext(env, RVH) && prev_virt) { + riscv_cpu_swap_hypervisor_regs(env); + } - riscv_cpu_set_virt_enabled(env, prev_virt); + riscv_cpu_set_mode(env, prev_priv, prev_virt); + + /* + * If forward cfi enabled for new priv, restore elp status + * and clear mnpelp in mnstatus + */ + if (cpu_get_fcfien(env)) { + env->elp = get_field(env->mnstatus, MNSTATUS_MNPELP); } + env->mnstatus = set_field(env->mnstatus, MNSTATUS_MNPELP, 0); return retpc; } +void helper_ctr_add_entry(CPURISCVState *env, target_ulong src, + target_ulong dest, target_ulong type) +{ + riscv_ctr_add_entry(env, src, dest, (enum CTRType)type, + env->priv, env->virt_enabled); +} + +void helper_ctr_clear(CPURISCVState *env) +{ + /* + * It's safe to call smstateen_acc_ok() for umode access regardless of the + * state of bit 54 (CTR bit in case of m/hstateen) of sstateen. If the bit + * is zero, smstateen_acc_ok() will return the correct exception code and + * if it's one, smstateen_acc_ok() will return RISCV_EXCP_NONE. In that + * scenario the U-mode check below will handle that case. + */ + RISCVException ret = smstateen_acc_ok(env, 0, SMSTATEEN0_CTR); + if (ret != RISCV_EXCP_NONE) { + riscv_raise_exception(env, ret, GETPC()); + } + + if (env->priv == PRV_U) { + /* + * One corner case is when sctrclr is executed from VU-mode and + * mstateen.CTR = 0, in which case we are supposed to raise + * RISCV_EXCP_ILLEGAL_INST. This case is already handled in + * smstateen_acc_ok(). + */ + uint32_t excep = env->virt_enabled ? RISCV_EXCP_VIRT_INSTRUCTION_FAULT : + RISCV_EXCP_ILLEGAL_INST; + riscv_raise_exception(env, excep, GETPC()); + } + + riscv_ctr_clear(env); +} + void helper_wfi(CPURISCVState *env) { CPUState *cs = env_cpu(env); @@ -380,6 +541,17 @@ 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); @@ -451,7 +623,7 @@ target_ulong helper_hyp_hlv_bu(CPURISCVState *env, target_ulong addr) int mmu_idx = check_access_hlsv(env, false, ra); MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); - return cpu_ldb_mmu(env, addr, oi, ra); + return cpu_ldb_mmu(env, adjust_addr_virt(env, addr), oi, ra); } target_ulong helper_hyp_hlv_hu(CPURISCVState *env, target_ulong addr) @@ -460,7 +632,7 @@ target_ulong helper_hyp_hlv_hu(CPURISCVState *env, target_ulong addr) 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); + return cpu_ldw_mmu(env, adjust_addr_virt(env, addr), oi, ra); } target_ulong helper_hyp_hlv_wu(CPURISCVState *env, target_ulong addr) @@ -469,7 +641,7 @@ target_ulong helper_hyp_hlv_wu(CPURISCVState *env, target_ulong addr) 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); + return cpu_ldl_mmu(env, adjust_addr_virt(env, addr), oi, ra); } target_ulong helper_hyp_hlv_d(CPURISCVState *env, target_ulong addr) @@ -478,7 +650,7 @@ target_ulong helper_hyp_hlv_d(CPURISCVState *env, target_ulong addr) 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); + return cpu_ldq_mmu(env, adjust_addr_virt(env, addr), oi, ra); } void helper_hyp_hsv_b(CPURISCVState *env, target_ulong addr, target_ulong val) @@ -487,7 +659,7 @@ void helper_hyp_hsv_b(CPURISCVState *env, target_ulong addr, target_ulong val) 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); + cpu_stb_mmu(env, adjust_addr_virt(env, addr), val, oi, ra); } void helper_hyp_hsv_h(CPURISCVState *env, target_ulong addr, target_ulong val) @@ -496,7 +668,7 @@ void helper_hyp_hsv_h(CPURISCVState *env, target_ulong addr, target_ulong val) 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); + cpu_stw_mmu(env, adjust_addr_virt(env, addr), val, oi, ra); } void helper_hyp_hsv_w(CPURISCVState *env, target_ulong addr, target_ulong val) @@ -505,7 +677,7 @@ void helper_hyp_hsv_w(CPURISCVState *env, target_ulong addr, target_ulong val) 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); + cpu_stl_mmu(env, adjust_addr_virt(env, addr), val, oi, ra); } void helper_hyp_hsv_d(CPURISCVState *env, target_ulong addr, target_ulong val) @@ -514,7 +686,7 @@ void helper_hyp_hsv_d(CPURISCVState *env, target_ulong addr, target_ulong val) 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); + cpu_stq_mmu(env, adjust_addr_virt(env, addr), val, oi, ra); } /* diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 5e60c26031b..5af295e410b 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -24,12 +24,23 @@ #include "qapi/error.h" #include "cpu.h" #include "trace.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" 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); +/* + * Convert the PMP permissions to match the truth table in the Smepmp spec. + */ +static inline uint8_t pmp_get_smepmp_operation(uint8_t cfg) +{ + return ((cfg & PMP_LOCK) >> 4) | ((cfg & PMP_READ) << 2) | + (cfg & PMP_WRITE) | ((cfg & PMP_EXEC) >> 2); +} + /* * Accessor method to extract address matching type 'a field' from cfg reg */ @@ -44,21 +55,58 @@ 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; } - /* Top PMP has no 'next' to check */ - if ((pmp_index + 1u) >= MAX_RISCV_PMPS) { + return 0; +} + +/* + * Check whether a PMP is locked for writing or not. + * (i.e. has LOCK flag and mseccfg.RLB is unset) + */ +static int pmp_is_readonly(CPURISCVState *env, uint32_t pmp_index) +{ + return pmp_is_locked(env, pmp_index) && !MSECCFG_RLB_ISSET(env); +} + +/* + * Check whether `val` is an invalid Smepmp config value + */ +static int pmp_is_invalid_smepmp_cfg(CPURISCVState *env, uint8_t val) +{ + /* No check if mseccfg.MML is not set or if mseccfg.RLB is set */ + if (!MSECCFG_MML_ISSET(env) || MSECCFG_RLB_ISSET(env)) { return 0; } - return 0; + /* + * Adding a rule with executable privileges that either is M-mode-only + * or a locked Shared-Region is not possible + */ + switch (pmp_get_smepmp_operation(val)) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 12: + case 14: + case 15: + return 0; + case 9: + case 10: + case 11: + case 13: + return 1; + default: + g_assert_not_reached(); + } } /* @@ -89,40 +137,18 @@ static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index) 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_cpu_cfg(env)->epmp) { - /* mseccfg.RLB is set */ - if (MSECCFG_RLB_ISSET(env)) { - locked = false; - } - - /* mseccfg.MML is not set */ - if (!MSECCFG_MML_ISSET(env) && !pmp_is_locked(env, pmp_index)) { - locked = false; - } - - /* mseccfg.MML is set */ - if (MSECCFG_MML_ISSET(env)) { - /* not adding execute bit */ - if ((val & PMP_LOCK) != 0 && (val & PMP_EXEC) != PMP_EXEC) { - locked = false; - } - /* shared region and not adding X bit */ - if ((val & PMP_LOCK) != PMP_LOCK && - (val & 0x7) != (PMP_WRITE | PMP_EXEC)) { - locked = false; - } - } - } else { - if (!pmp_is_locked(env, pmp_index)) { - locked = false; - } + if (env->pmp_state.pmp[pmp_index].cfg_reg == val) { + /* no change */ + return false; } - if (locked) { - qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - locked\n"); - } else if (env->pmp_state.pmp[pmp_index].cfg_reg != val) { + if (pmp_is_readonly(env, pmp_index)) { + qemu_log_mask(LOG_GUEST_ERROR, + "ignoring pmpcfg write - read only\n"); + } else if (pmp_is_invalid_smepmp_cfg(env, val)) { + qemu_log_mask(LOG_GUEST_ERROR, + "ignoring pmpcfg write - invalid\n"); + } else { env->pmp_state.pmp[pmp_index].cfg_reg = val; pmp_update_rule_addr(env, pmp_index); return true; @@ -135,8 +161,17 @@ static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) 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 @@ -158,8 +193,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; @@ -212,8 +247,7 @@ void pmp_update_rule_nums(CPURISCVState *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; @@ -290,14 +324,14 @@ static bool pmp_hart_has_privs_default(CPURISCVState *env, pmp_priv_t privs, * 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, +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 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)) { @@ -312,7 +346,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, */ pmp_size = -(addr | TARGET_PAGE_MASK); } else { - pmp_size = sizeof(target_ulong); + pmp_size = 2 << riscv_cpu_mxl(env); } } else { pmp_size = size; @@ -338,16 +372,6 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, const uint8_t a_field = pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg); - /* - * Convert the PMP permissions to match the truth table in the - * ePMP spec. - */ - const uint8_t epmp_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) | - ((env->pmp_state.pmp[i].cfg_reg & PMP_EXEC) >> 2); - if (((s + e) == 2) && (PMP_AMATCH_OFF != a_field)) { /* * If the PMP entry is not off and the address is in range, @@ -366,8 +390,11 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, /* * If mseccfg.MML Bit set, do the enhanced pmp priv check */ + const uint8_t smepmp_operation = + pmp_get_smepmp_operation(env->pmp_state.pmp[i].cfg_reg); + if (mode == PRV_M) { - switch (epmp_operation) { + switch (smepmp_operation) { case 0: case 1: case 4: @@ -398,7 +425,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: @@ -502,6 +529,11 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, bool is_next_cfg_tor = false; if (addr_index < MAX_RISCV_PMPS) { + if (env->pmp_state.pmp[addr_index].addr_reg == val) { + /* no change */ + return; + } + /* * In TOR mode, need to check the lock bit of the next pmp * (if there is a next). @@ -510,25 +542,23 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, 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 && is_next_cfg_tor) { + if (pmp_is_readonly(env, addr_index + 1) && is_next_cfg_tor) { qemu_log_mask(LOG_GUEST_ERROR, - "ignoring pmpaddr write - pmpcfg + 1 locked\n"); + "ignoring pmpaddr write - pmpcfg+1 read only\n"); return; } } - if (!pmp_is_locked(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)); + if (!pmp_is_readonly(env, addr_index)) { + 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"); + "ignoring pmpaddr write - read only\n"); } } else { qemu_log_mask(LOG_GUEST_ERROR, @@ -561,6 +591,13 @@ target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index) void mseccfg_csr_write(CPURISCVState *env, target_ulong val) { int i; + uint64_t mask = MSECCFG_MMWP | MSECCFG_MML; + /* Update PMM field only if the value is valid according to Zjpm v1.0 */ + if (riscv_cpu_cfg(env)->ext_smmpm && + riscv_cpu_mxl(env) == MXL_RV64 && + get_field(val, MSECCFG_PMM) != PMM_FIELD_RESERVED) { + mask |= MSECCFG_PMM; + } trace_mseccfg_csr_write(env->mhartid, val); @@ -574,14 +611,20 @@ void mseccfg_csr_write(CPURISCVState *env, target_ulong val) } } - if (riscv_cpu_cfg(env)->epmp) { + if (riscv_cpu_cfg(env)->ext_smepmp) { /* Sticky bits */ - val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML)); - if ((val ^ env->mseccfg) & (MSECCFG_MMWP | MSECCFG_MML)) { + val |= (env->mseccfg & mask); + if ((val ^ env->mseccfg) & mask) { tlb_flush(env_cpu(env)); } } else { - val &= ~(MSECCFG_MMWP | MSECCFG_MML | MSECCFG_RLB); + mask |= MSECCFG_RLB; + val &= ~(mask); + } + + /* 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; @@ -609,12 +652,12 @@ target_ulong mseccfg_csr_read(CPURISCVState *env) * To avoid this we return a size of 1 (which means no caching) if the PMP * region only covers partial of the TLB page. */ -target_ulong pmp_get_tlb_size(CPURISCVState *env, target_ulong addr) +target_ulong pmp_get_tlb_size(CPURISCVState *env, hwaddr addr) { - target_ulong pmp_sa; - target_ulong pmp_ea; - target_ulong tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1); - target_ulong tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1; + 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; /* diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h index cf5c99f8e68..271cf241699 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,9 @@ 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_PMM = 3ULL << 32, } mseccfg_field_t; typedef struct { @@ -52,8 +55,8 @@ typedef struct { } pmp_entry_t; typedef struct { - target_ulong sa; - target_ulong ea; + hwaddr sa; + hwaddr ea; } pmp_addr_t; typedef struct { @@ -72,15 +75,16 @@ target_ulong mseccfg_csr_read(CPURISCVState *env); void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, target_ulong val); target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index); -bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, +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, target_ulong addr); +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 36f6307d286..a68809eef32 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -18,14 +18,14 @@ #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" +#include "exec/icount.h" +#include "system/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 @@ -34,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[15] = {}; - uint32_t cmask; - - /* All the programmable counters can map to any event */ - cmask = MAKE_32BIT_MASK(3, num_ctrs); /* * The event encoding is specified in the SBI specification @@ -181,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; @@ -188,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, @@ -198,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; } @@ -300,7 +390,7 @@ int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, * Expected mhpmevent value is zero for reset case. Remove the current * mapping. */ - if (!value) { + if (!(value & MHPMEVENT_IDX_MASK)) { g_hash_table_foreach_remove(cpu->pmu_event_ctr_map, pmu_remove_event_map, GUINT_TO_POINTER(ctr_idx)); @@ -330,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) { @@ -351,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]; @@ -368,10 +492,28 @@ 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; + if (pmu_hpmevent_set_of_if_clear(env, ctr_idx)) { riscv_cpu_update_mip(env, MIP_LCOFIP, BOOL_TO_MASK(1)); } } @@ -389,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 = 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; } @@ -420,8 +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; @@ -434,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 2bfb71ba87f..3853d0e2629 100644 --- a/target/riscv/pmu.h +++ b/target/riscv/pmu.h @@ -16,17 +16,27 @@ * this program. If not, see . */ +#ifndef RISCV_PMU_H +#define RISCV_PMU_H + #include "cpu.h" +#include "qapi/error.h" bool riscv_pmu_ctr_monitor_instructions(CPURISCVState *env, uint32_t target_ctr); bool riscv_pmu_ctr_monitor_cycles(CPURISCVState *env, uint32_t target_ctr); void riscv_pmu_timer_cb(void *priv); -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 index 5ecff1afb37..8ba8aa0d5f8 100644 --- a/target/riscv/riscv-qmp-cmds.c +++ b/target/riscv/riscv-qmp-cmds.c @@ -24,8 +24,17 @@ #include "qemu/osdep.h" -#include "qapi/qapi-commands-machine-target.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/visitor.h" +#include "qom/qom-qobject.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "cpu-qom.h" +#include "cpu.h" static void riscv_cpu_add_definition(gpointer data, gpointer user_data) { @@ -35,8 +44,7 @@ static void riscv_cpu_add_definition(gpointer data, gpointer user_data) const char *typename = object_class_get_name(oc); ObjectClass *dyn_class; - info->name = g_strndup(typename, - strlen(typename) - strlen("-" TYPE_RISCV_CPU)); + info->name = cpu_model_from_type(typename); info->q_typename = g_strdup(typename); dyn_class = object_class_dynamic_cast(oc, TYPE_RISCV_DYNAMIC_CPU); @@ -55,3 +63,180 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) 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 43899d08f67..7dfe5f72c6e 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,6 +38,7 @@ #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 */ #define SBI_EXT_BASE_GET_SPEC_VERSION 0x0 @@ -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 00000000000..061df3d74a0 --- /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 00000000000..55fd9e55845 --- /dev/null +++ b/target/riscv/tcg/tcg-cpu.c @@ -0,0 +1,1677 @@ +/* + * 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/translation-block.h" +#include "tcg-cpu.h" +#include "cpu.h" +#include "exec/target_page.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 "accel/accel-cpu-target.h" +#include "accel/tcg/cpu-ops.h" +#include "tcg/tcg.h" +#ifndef CONFIG_USER_ONLY +#include "hw/boards.h" +#include "system/tcg.h" +#include "exec/icount.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 int riscv_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return riscv_env_mmu_index(cpu_env(cs), ifetch); +} + +static TCGTBCPUState riscv_get_tb_cpu_state(CPUState *cs) +{ + CPURISCVState *env = cpu_env(cs); + RISCVCPU *cpu = env_archcpu(env); + RISCVExtStatus fs, vs; + uint32_t flags = 0; + bool pm_signext = riscv_cpu_virt_mem_enabled(env); + + if (cpu->cfg.ext_zve32x) { + /* + * If env->vl equals to VLMAX, we can use generic vector operation + * expanders (GVEC) to accerlate the vector operations. + * However, as LMUL could be a fractional number. The maximum + * vector size can be operated might be less than 8 bytes, + * which is not supported by GVEC. So we set vl_eq_vlmax flag to true + * only when maxsz >= 8 bytes. + */ + + /* 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, vsew); + flags = FIELD_DP32(flags, TB_FLAGS, LMUL, + FIELD_EX64(env->vtype, VTYPE, VLMUL)); + flags = FIELD_DP32(flags, TB_FLAGS, VL_EQ_VLMAX, vl_eq_vlmax); + flags = FIELD_DP32(flags, TB_FLAGS, VTA, + FIELD_EX64(env->vtype, VTYPE, VTA)); + flags = FIELD_DP32(flags, TB_FLAGS, VMA, + FIELD_EX64(env->vtype, VTYPE, VMA)); + flags = FIELD_DP32(flags, TB_FLAGS, VSTART_EQ_ZERO, env->vstart == 0); + } else { + flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1); + } + + 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 + fs = EXT_STATUS_DIRTY; + vs = EXT_STATUS_DIRTY; +#else + 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)); + } + + /* 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 (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); + flags = FIELD_DP32(flags, TB_FLAGS, AXL, cpu_address_xl(env)); + flags = FIELD_DP32(flags, TB_FLAGS, PM_PMM, riscv_pm_get_pmm(env)); + flags = FIELD_DP32(flags, TB_FLAGS, PM_SIGNEXTEND, pm_signext); + + return (TCGTBCPUState){ + .pc = env->xl == MXL_RV32 ? env->pc & UINT32_MAX : env->pc, + .flags = flags + }; +} + +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]; +} + +#ifndef CONFIG_USER_ONLY +static vaddr riscv_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + CPURISCVState *env = cpu_env(cs); + uint32_t pm_len; + bool pm_signext; + + if (cpu_address_xl(env) == MXL_RV32) { + return (uint32_t)result; + } + + pm_len = riscv_pm_get_pmlen(riscv_pm_get_pmm(env)); + if (pm_len == 0) { + return result; + } + + pm_signext = riscv_cpu_virt_mem_enabled(env); + if (pm_signext) { + return sextract64(result, 0, 64 - pm_len); + } + return extract64(result, 0, 64 - pm_len); +} +#endif + +const TCGCPUOps riscv_tcg_ops = { + .mttcg_supported = true, + .guest_default_memory_order = 0, + + .initialize = riscv_translate_init, + .translate_code = riscv_translate_code, + .get_tb_cpu_state = riscv_get_tb_cpu_state, + .synchronize_from_tb = riscv_cpu_synchronize_from_tb, + .restore_state_to_opc = riscv_restore_state_to_opc, + .mmu_index = riscv_cpu_mmu_index, + +#ifndef CONFIG_USER_ONLY + .tlb_fill = riscv_cpu_tlb_fill, + .pointer_wrap = riscv_pointer_wrap, + .cpu_exec_interrupt = riscv_cpu_exec_interrupt, + .cpu_exec_halt = riscv_cpu_has_work, + .cpu_exec_reset = cpu_reset, + .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(). + */ + switch (feat_offset) { + case CPU_CFG_OFFSET(ext_zic64b): + cpu->cfg.cbom_blocksize = 64; + cpu->cfg.cbop_blocksize = 64; + cpu->cfg.cboz_blocksize = 64; + break; + case CPU_CFG_OFFSET(ext_sha): + if (!cpu_misa_ext_is_user_set(RVH)) { + riscv_cpu_write_misa_bit(cpu, RVH, true); + } + /* fallthrough */ + case CPU_CFG_OFFSET(ext_ssstateen): + cpu->cfg.ext_smstateen = true; + break; + } +} + +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); + + /* + * Do not show user warnings for named features that users + * can't enable/disable in the command line. See commit + * 68c9e54bea for more info. + */ + if (cpu_cfg_offset_is_named_feat(edata->ext_enable_offset)) { + continue; + } +#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; + } + + cpu->cfg.ext_zic64b = cpu->cfg.cbom_blocksize == 64 && + cpu->cfg.cbop_blocksize == 64 && + cpu->cfg.cboz_blocksize == 64; + + cpu->cfg.ext_ssstateen = cpu->cfg.ext_smstateen; + + cpu->cfg.ext_sha = riscv_has_ext(&cpu->env, RVH) && + cpu->cfg.ext_ssstateen; + + cpu->cfg.ext_ziccrse = cpu->cfg.has_priv_1_11; +} + +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->def->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; + } + + if (mcc->def->misa_mxl_max == MXL_RV32 && cpu->cfg.ext_svukte) { + error_setg(errp, "svukte is not supported for RV32"); + return; + } + + if ((cpu->cfg.ext_smctr || cpu->cfg.ext_ssctr) && + (!riscv_has_ext(env, RVS) || !cpu->cfg.ext_sscsrind)) { + if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_smctr)) || + cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_ssctr))) { + error_setg(errp, "Smctr and Ssctr require S-mode and Sscsrind"); + return; + } + cpu->cfg.ext_smctr = false; + cpu->cfg.ext_ssctr = false; + } + + /* + * 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 = cpu->cfg.max_satp_mode; + + assert(satp_max >= 0); + 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_check_parent_profile(RISCVCPU *cpu, + RISCVCPUProfile *profile, + RISCVCPUProfile *parent) +{ + const char *parent_name; + bool parent_enabled; + + if (!profile->enabled || !parent) { + return; + } + + parent_name = parent->name; + parent_enabled = object_property_get_bool(OBJECT(cpu), parent_name, NULL); + profile->enabled = parent_enabled; +} + +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 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; + + riscv_cpu_check_parent_profile(cpu, profile, profile->u_parent); + riscv_cpu_check_parent_profile(cpu, profile, profile->s_parent); +} + +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->def->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->def->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; + } +#ifndef CONFIG_USER_ONLY + if (cpu->cfg.pmu_mask) { + riscv_pmu_init(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + if (cpu->cfg.ext_sscofpmf) { + cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + riscv_pmu_timer_cb, cpu); + } + } +#endif +} + +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 + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); + + if (mcc->def->misa_mxl_max >= MXL_RV128 && qemu_tcg_mttcg_enabled()) { + /* Missing 128-bit aligned atomics */ + error_setg(errp, + "128-bit RISC-V currently does not work with Multi " + "Threaded TCG. Please use: -accel tcg,thread=single"); + return false; + } + + CPURISCVState *env = &cpu->env; + + tcg_cflags_set(CPU(cs), CF_PCREL); + + if (cpu->cfg.ext_sstc) { + riscv_timer_init(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(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->u_parent != NULL) { + object_property_set_bool(obj, profile->u_parent->name, + profile->enabled, NULL); + } + + if (profile->s_parent != NULL) { + object_property_set_bool(obj, profile->s_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 and RVV that are disabled by default */ + riscv_cpu_set_misa_ext(env, env->misa_ext | RVB | RVG | 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); + } + + /* + * TODO: ext_smrnmi requires OpenSBI changes that our current + * image does not have. Disable it for now. + */ + if (cpu->cfg.ext_smrnmi) { + isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_smrnmi), false); + } + + /* + * TODO: ext_smdbltrp requires the firmware to clear MSTATUS.MDT on startup + * to avoid generating a double trap. OpenSBI does not currently support it, + * disable it for now. + */ + if (cpu->cfg.ext_smdbltrp) { + isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_smdbltrp), 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_accel_class_init(ObjectClass *oc, const void *data) +{ + AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); + + 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 00000000000..a23716a5acf --- /dev/null +++ b/target/riscv/tcg/tcg-cpu.h @@ -0,0 +1,46 @@ +/* + * 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); + +extern const TCGCPUOps riscv_tcg_ops; + +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 00000000000..49eb7bbab5f --- /dev/null +++ b/target/riscv/th_csr.c @@ -0,0 +1,59 @@ +/* + * 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) + +static RISCVException smode(CPURISCVState *env, int csrno) +{ + if (riscv_has_ext(env, RVS)) { + return RISCV_EXCP_NONE; + } + + return RISCV_EXCP_ILLEGAL_INST; +} + +static bool test_thead_mvendorid(RISCVCPU *cpu) +{ + return cpu->cfg.mvendorid == THEAD_VENDOR_ID; +} + +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; +} + +const RISCVCSR th_csr_list[] = { + { + .csrno = CSR_TH_SXSTATUS, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.sxstatus", smode, read_th_sxstatus } + }, + { } +}; diff --git a/target/riscv/time_helper.c b/target/riscv/time_helper.c index 8d245bed3ae..bc0d9a0c4c3 100644 --- a/target/riscv/time_helper.c +++ b/target/riscv/time_helper.c @@ -92,6 +92,7 @@ void riscv_timer_write_timecmp(CPURISCVState *env, QEMUTimer *timer, * equals UINT64_MAX. */ if (timecmp == UINT64_MAX) { + timer_del(timer); return; } diff --git a/target/riscv/trace-events b/target/riscv/trace-events index 49ec4d3b7d7..93837f82a19 100644 --- a/target/riscv/trace-events +++ b/target/riscv/trace-events @@ -9,3 +9,6 @@ pmpaddr_csr_write(uint64_t mhartid, uint32_t addr_index, uint64_t val) "hart %" mseccfg_csr_read(uint64_t mhartid, uint64_t val) "hart %" PRIu64 ": read mseccfg, val: 0x%" PRIx64 mseccfg_csr_write(uint64_t mhartid, uint64_t val) "hart %" PRIu64 ": write mseccfg, val: 0x%" PRIx64 + +# op_helper.c +riscv_exception(uint32_t exception, const char *desc, uint64_t epc) "%u (%s) on epc 0x%"PRIx64"" diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 7dbf173adb5..d7a6de02df5 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -20,31 +20,27 @@ #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" - +#include "exec/target_page.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/log.h" #include "semihosting/semihost.h" -#include "instmap.h" #include "internals.h" #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" #undef HELPER_H +#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 */ static TCGv load_res; static TCGv load_val; -/* globals for PM CSRs */ -static TCGv pm_mask; -static TCGv pm_base; /* * If an operation is being performed on less than TARGET_LONG_BITS, @@ -106,15 +102,22 @@ typedef struct DisasContext { bool vl_eq_vlmax; CPUState *cs; TCGv zero; - /* PointerMasking extension */ - bool pm_mask_enabled; - bool pm_base_enabled; + /* actual address width */ + uint8_t addr_xl; + bool addr_signed; + /* Ztso */ + bool ztso; /* Use icount trigger for native debug */ bool itrigger; /* FRM is known to contain a valid value. */ bool frm_valid; - /* TCG of the current insn_start */ - TCGOp *insn_start; + 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) @@ -138,6 +141,8 @@ static inline bool has_ext(DisasContext *ctx, uint32_t ext) #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) { @@ -203,11 +208,12 @@ 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_pc_plus_diff(TCGv target, DisasContext *ctx, @@ -235,16 +241,16 @@ static void gen_update_pc(DisasContext *ctx, target_long diff) ctx->pc_save = ctx->base.pc_next + diff; } -static void generate_exception(DisasContext *ctx, int excp) +static void generate_exception(DisasContext *ctx, RISCVException excp) { gen_update_pc(ctx, 0); - gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp)); ctx->base.is_jmp = DISAS_NORETURN; } static 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)); if (ctx->virt_inst_excp) { generate_exception(ctx, RISCV_EXCP_VIRT_INSTRUCTION_FAULT); @@ -255,7 +261,7 @@ static void gen_exception_illegal(DisasContext *ctx) static void gen_exception_inst_addr_mis(DisasContext *ctx, TCGv target) { - tcg_gen_st_tl(target, cpu_env, offsetof(CPURISCVState, badaddr)); + tcg_gen_st_tl(target, tcg_env, offsetof(CPURISCVState, badaddr)); generate_exception(ctx, RISCV_EXCP_INST_ADDR_MIS); } @@ -263,7 +269,7 @@ static void lookup_and_goto_ptr(DisasContext *ctx) { #ifndef CONFIG_USER_ONLY if (ctx->itrigger) { - gen_helper_itrigger_match(cpu_env); + gen_helper_itrigger_match(tcg_env); } #endif tcg_gen_lookup_and_goto_ptr(); @@ -273,7 +279,7 @@ static void exit_tb(DisasContext *ctx) { #ifndef CONFIG_USER_ONLY if (ctx->itrigger) { - gen_helper_itrigger_match(cpu_env); + gen_helper_itrigger_match(tcg_env); } #endif tcg_gen_exit_tb(NULL, 0); @@ -554,12 +560,54 @@ static void gen_set_fpr_d(DisasContext *ctx, int reg_num, TCGv_i64 t) } } +#ifndef CONFIG_USER_ONLY +/* + * Direct calls + * - jal x1; + * - jal x5; + * - c.jal. + * - cm.jalt. + * + * Direct jumps + * - jal x0; + * - c.j; + * - cm.jt. + * + * Other direct jumps + * - jal rd where rd != x1 and rd != x5 and rd != x0; + */ +static void gen_ctr_jal(DisasContext *ctx, int rd, target_ulong imm) +{ + TCGv dest = tcg_temp_new(); + TCGv src = tcg_temp_new(); + TCGv type; + + /* + * If rd is x1 or x5 link registers, treat this as direct call otherwise + * its a direct jump. + */ + if (rd == 1 || rd == 5) { + type = tcg_constant_tl(CTRDATA_TYPE_DIRECT_CALL); + } else if (rd == 0) { + type = tcg_constant_tl(CTRDATA_TYPE_DIRECT_JUMP); + } else { + type = tcg_constant_tl(CTRDATA_TYPE_OTHER_DIRECT_JUMP); + } + + gen_pc_plus_diff(dest, ctx, imm); + gen_pc_plus_diff(src, ctx, 0); + gen_helper_ctr_add_entry(tcg_env, src, dest, type); +} +#endif + static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) { TCGv succ_pc = dest_gpr(ctx, rd); /* check misaligned: */ - if (!has_ext(ctx, RVC) && !ctx->cfg_ptr->ext_zca) { + if (!riscv_cpu_allow_16bit_insn(ctx->cfg_ptr, + ctx->priv_ver, + ctx->misa_ext)) { if ((imm & 0x3) != 0) { TCGv target_pc = tcg_temp_new(); gen_pc_plus_diff(target_pc, ctx, imm); @@ -568,6 +616,12 @@ static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) } } +#ifndef CONFIG_USER_ONLY + if (ctx->cfg_ptr->ext_smctr || ctx->cfg_ptr->ext_ssctr) { + gen_ctr_jal(ctx, rd, imm); + } +#endif + gen_pc_plus_diff(succ_pc, ctx, ctx->cur_insn_len); gen_set_gpr(ctx, rd, succ_pc); @@ -582,13 +636,10 @@ static TCGv get_address(DisasContext *ctx, int rs1, int imm) 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); + if (ctx->addr_signed) { + tcg_gen_sextract_tl(addr, addr, 0, ctx->addr_xl); + } else { + tcg_gen_extract_tl(addr, addr, 0, ctx->addr_xl); } return addr; @@ -601,14 +652,12 @@ static TCGv get_address_indexed(DisasContext *ctx, int rs1, TCGv offs) TCGv src1 = get_gpr(ctx, rs1, EXT_NONE); tcg_gen_add_tl(addr, src1, offs); - if (ctx->pm_mask_enabled) { - tcg_gen_andc_tl(addr, addr, pm_mask); - } else if (get_xl(ctx) == MXL_RV32) { - tcg_gen_ext32u_tl(addr, addr); - } - if (ctx->pm_base_enabled) { - tcg_gen_or_tl(addr, addr, pm_base); + if (ctx->addr_signed) { + tcg_gen_sextract_tl(addr, addr, 0, ctx->addr_xl); + } else { + tcg_gen_extract_tl(addr, addr, 0, ctx->addr_xl); } + return addr; } @@ -630,14 +679,14 @@ static void mark_fs_dirty(DisasContext *ctx) 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_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); if (ctx->virt_enabled) { - tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); + tcg_gen_ld_tl(tmp, tcg_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_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); } } } @@ -659,14 +708,14 @@ static void mark_vs_dirty(DisasContext *ctx) 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_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); if (ctx->virt_enabled) { - tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); + tcg_gen_ld_tl(tmp, tcg_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_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); } } } @@ -674,6 +723,12 @@ static void mark_vs_dirty(DisasContext *ctx) 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) { @@ -687,8 +742,8 @@ static void gen_set_rm(DisasContext *ctx, int rm) } /* 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) @@ -700,8 +755,8 @@ static void gen_set_rm_chkfrm(DisasContext *ctx, int rm) ctx->frm_valid = true; /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ - decode_save_opc(ctx); - gen_helper_set_rounding_mode_chkfrm(cpu_env, tcg_constant_i32(rm)); + 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) @@ -1070,15 +1125,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" @@ -1089,8 +1181,11 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) #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" @@ -1105,6 +1200,8 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) /* 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" @@ -1112,26 +1209,16 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) /* The specification allows for longer insns, but not supported by qemu. */ #define MAX_INSN_LEN 4 -static inline int insn_len(uint16_t first_word) -{ - 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)(const RISCVCPUConfig *); - bool (*decode_func)(DisasContext *, uint32_t); - } decoders[] = { - { always_true_p, decode_insn32 }, - { has_xthead_p, decode_xthead }, - { has_XVentanaCondOps_p, decode_XVentanaCodeOps }, - }; - ctx->virt_inst_excp = false; ctx->cur_insn_len = insn_len(opcode); /* Check for compressed insn */ @@ -1152,9 +1239,9 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) ctx->base.pc_next + 2)); ctx->opcode = opcode32; - for (size_t i = 0; i < ARRAY_SIZE(decoders); ++i) { - if (decoders[i].guard_func(ctx->cfg_ptr) && - 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; } } @@ -1166,7 +1253,8 @@ 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; @@ -1188,15 +1276,26 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->cfg_vta_all_1s = cpu->cfg.rvv_ta_all_1s; ctx->vstart_eq_zero = FIELD_EX32(tb_flags, TB_FLAGS, VSTART_EQ_ZERO); ctx->vl_eq_vlmax = FIELD_EX32(tb_flags, TB_FLAGS, VL_EQ_VLMAX); - ctx->misa_mxl_max = env->misa_mxl_max; + ctx->misa_mxl_max = mcc->def->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->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); + if (get_xl(ctx) == MXL_RV32) { + ctx->addr_xl = 32; + ctx->addr_signed = false; + } else { + int pm_pmm = FIELD_EX32(tb_flags, TB_FLAGS, PM_PMM); + ctx->addr_xl = 64 - riscv_pm_get_pmlen(pm_pmm); + ctx->addr_signed = FIELD_EX32(tb_flags, TB_FLAGS, PM_SIGNEXTEND); + } + 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) @@ -1212,32 +1311,51 @@ static void riscv_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) pc_next &= ~TARGET_PAGE_MASK; } - tcg_gen_insn_start(pc_next, 0); - ctx->insn_start = tcg_last_op(); + 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); ctx->ol = ctx->xl; decode_opc(env, ctx, opcode16); ctx->base.pc_next += ctx->cur_insn_len; + /* + * 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; + } + /* Only the first insn within a TB is allowed to cross a page boundary. */ if (ctx->base.is_jmp == DISAS_NEXT) { - if (ctx->itrigger || !is_same_page(&ctx->base, ctx->base.pc_next)) { + if (ctx->itrigger || !translator_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 - 1)) { + if (!translator_is_same_page(&ctx->base, ctx->base.pc_next + len - 1)) { ctx->base.is_jmp = DISAS_TOO_MANY; } } @@ -1260,33 +1378,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: %d\n", - env->priv, env->virt_enabled); -#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 riscv_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext ctx; @@ -1306,28 +1407,23 @@ 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), - "pmmask"); - pm_base = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, cur_pmbase), - "pmbase"); } diff --git a/target/riscv/vcrypto_helper.c b/target/riscv/vcrypto_helper.c index e2d719b13b6..9a0d9b4f536 100644 --- a/target/riscv/vcrypto_helper.c +++ b/target/riscv/vcrypto_helper.c @@ -26,7 +26,6 @@ #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" @@ -222,6 +221,8 @@ static inline void xor_round_key(AESState *round_state, AESState *round_key) uint32_t total_elems = vext_get_total_elems(env, desc, 4); \ uint32_t vta = vext_vta(desc); \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ 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)); \ @@ -246,6 +247,8 @@ static inline void xor_round_key(AESState *round_state, AESState *round_key) uint32_t total_elems = vext_get_total_elems(env, desc, 4); \ uint32_t vta = vext_vta(desc); \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (uint32_t i = env->vstart / 4; i < env->vl / 4; i++) { \ AESState round_key; \ round_key.d[0] = *((uint64_t *)vs2 + H8(0)); \ @@ -305,6 +308,8 @@ void HELPER(vaeskf1_vi)(void *vd_vptr, void *vs2_vptr, uint32_t uimm, uint32_t total_elems = vext_get_total_elems(env, desc, 4); uint32_t vta = vext_vta(desc); + VSTART_CHECK_EARLY_EXIT(env, vl); + uimm &= 0b1111; if (uimm > 10 || uimm == 0) { uimm ^= 0b1000; @@ -351,6 +356,8 @@ void HELPER(vaeskf2_vi)(void *vd_vptr, void *vs2_vptr, uint32_t uimm, uint32_t total_elems = vext_get_total_elems(env, desc, 4); uint32_t vta = vext_vta(desc); + VSTART_CHECK_EARLY_EXIT(env, vl); + uimm &= 0b1111; if (uimm > 14 || uimm < 2) { uimm ^= 0b1000; @@ -457,6 +464,8 @@ void HELPER(vsha2ms_vv)(void *vd, void *vs1, void *vs2, CPURISCVState *env, uint32_t total_elems; uint32_t vta = vext_vta(desc); + VSTART_CHECK_EARLY_EXIT(env, env->vl); + 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, @@ -572,6 +581,8 @@ void HELPER(vsha2ch32_vv)(void *vd, void *vs1, void *vs2, CPURISCVState *env, uint32_t total_elems; uint32_t vta = vext_vta(desc); + VSTART_CHECK_EARLY_EXIT(env, env->vl); + 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); @@ -590,6 +601,8 @@ void HELPER(vsha2ch64_vv)(void *vd, void *vs1, void *vs2, CPURISCVState *env, uint32_t total_elems; uint32_t vta = vext_vta(desc); + VSTART_CHECK_EARLY_EXIT(env, env->vl); + 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); @@ -608,6 +621,8 @@ void HELPER(vsha2cl32_vv)(void *vd, void *vs1, void *vs2, CPURISCVState *env, uint32_t total_elems; uint32_t vta = vext_vta(desc); + VSTART_CHECK_EARLY_EXIT(env, env->vl); + 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)); @@ -626,6 +641,8 @@ void HELPER(vsha2cl64_vv)(void *vd, void *vs1, void *vs2, CPURISCVState *env, uint32_t total_elems; uint32_t vta = vext_vta(desc); + VSTART_CHECK_EARLY_EXIT(env, env->vl); + 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)); @@ -658,6 +675,8 @@ void HELPER(vsm3me_vv)(void *vd_vptr, void *vs1_vptr, void *vs2_vptr, uint32_t *vs1 = vs1_vptr; uint32_t *vs2 = vs2_vptr; + VSTART_CHECK_EARLY_EXIT(env, env->vl); + for (int i = env->vstart / 8; i < env->vl / 8; i++) { uint32_t w[24]; for (int j = 0; j < 8; j++) { @@ -757,6 +776,8 @@ void HELPER(vsm3c_vi)(void *vd_vptr, void *vs2_vptr, uint32_t uimm, uint32_t *vs2 = vs2_vptr; uint32_t v1[8], v2[8], v3[8]; + VSTART_CHECK_EARLY_EXIT(env, env->vl); + 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)]); @@ -780,6 +801,8 @@ void HELPER(vghsh_vv)(void *vd_vptr, void *vs1_vptr, void *vs2_vptr, uint32_t vta = vext_vta(desc); uint32_t total_elems = vext_get_total_elems(env, desc, 4); + VSTART_CHECK_EARLY_EXIT(env, env->vl); + 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])}; @@ -817,6 +840,8 @@ void HELPER(vgmul_vv)(void *vd_vptr, void *vs2_vptr, CPURISCVState *env, uint32_t vta = vext_vta(desc); uint32_t total_elems = vext_get_total_elems(env, desc, 4); + VSTART_CHECK_EARLY_EXIT(env, env->vl); + 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])}; @@ -853,6 +878,8 @@ void HELPER(vsm4k_vi)(void *vd, void *vs2, uint32_t uimm5, CPURISCVState *env, uint32_t esz = sizeof(uint32_t); uint32_t total_elems = vext_get_total_elems(env, desc, esz); + VSTART_CHECK_EARLY_EXIT(env, env->vl); + for (uint32_t i = group_start; i < group_end; ++i) { uint32_t vstart = i * egs; uint32_t vend = (i + 1) * egs; @@ -909,6 +936,8 @@ void HELPER(vsm4r_vv)(void *vd, void *vs2, CPURISCVState *env, uint32_t desc) uint32_t esz = sizeof(uint32_t); uint32_t total_elems = vext_get_total_elems(env, desc, esz); + VSTART_CHECK_EARLY_EXIT(env, env->vl); + for (uint32_t i = group_start; i < group_end; ++i) { uint32_t vstart = i * egs; uint32_t vend = (i + 1) * egs; @@ -943,6 +972,8 @@ void HELPER(vsm4r_vs)(void *vd, void *vs2, CPURISCVState *env, uint32_t desc) uint32_t esz = sizeof(uint32_t); uint32_t total_elems = vext_get_total_elems(env, desc, esz); + VSTART_CHECK_EARLY_EXIT(env, env->vl); + for (uint32_t i = group_start; i < group_end; ++i) { uint32_t vstart = i * egs; uint32_t vend = (i + 1) * egs; diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 3fb05cc3d6e..5dc1c100125 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -21,9 +21,13 @@ #include "qemu/bitops.h" #include "cpu.h" #include "exec/memop.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" +#include "exec/page-protection.h" #include "exec/helper-proto.h" +#include "exec/tlb-flags.h" +#include "exec/target_page.h" +#include "exec/tswap.h" #include "fpu/softfloat.h" #include "tcg/tcg-gvec-desc.h" #include "internals.h" @@ -35,19 +39,27 @@ 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 - check LMUL * VLEN >= SEW */ - if (lmul == 4 || - cpu->cfg.vlen >> (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; } } @@ -61,9 +73,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; } @@ -92,38 +108,51 @@ static inline uint32_t vext_max_elems(uint32_t desc, uint32_t log2_esz) return scale < 0 ? vlenb >> -scale : vlenb << scale; } -static inline target_ulong adjust_addr(CPURISCVState *env, target_ulong addr) -{ - return (addr & ~env->cur_pmmask) | env->cur_pmbase; -} - /* * 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 * and page table walk can't fill the TLB entry. Then the guest * software can return here after process the exception or never return. + * + * This function can also be used when direct access to probe_access_flags is + * needed in order to access the flags. If a pointer to a flags operand is + * provided the function will call probe_access_flags instead, use nonfault + * and update host and flags. */ -static void probe_pages(CPURISCVState *env, target_ulong addr, - target_ulong len, uintptr_t ra, - MMUAccessType access_type) +static void probe_pages(CPURISCVState *env, target_ulong addr, target_ulong len, + uintptr_t ra, MMUAccessType access_type, int mmu_index, + void **host, int *flags, bool nonfault) { target_ulong pagelen = -(addr | TARGET_PAGE_MASK); target_ulong curlen = MIN(pagelen, len); - probe_access(env, adjust_addr(env, addr), curlen, access_type, - cpu_mmu_index(env, false), ra); + if (flags != NULL) { + *flags = probe_access_flags(env, adjust_addr(env, addr), curlen, + access_type, mmu_index, nonfault, host, ra); + } else { + probe_access(env, adjust_addr(env, addr), curlen, access_type, + 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); + if (flags != NULL) { + *flags = probe_access_flags(env, adjust_addr(env, addr), curlen, + access_type, mmu_index, nonfault, + host, ra); + } else { + probe_access(env, adjust_addr(env, addr), curlen, access_type, + mmu_index, ra); + } } } + static inline void vext_set_elem_mask(void *v0, int index, uint8_t value) { @@ -134,34 +163,90 @@ static inline void vext_set_elem_mask(void *v0, int index, } /* elements operations for load and store */ -typedef void vext_ldst_elem_fn(CPURISCVState *env, abi_ptr 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 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); \ +} -#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); \ +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, 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_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_continuous_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_continuous_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, @@ -184,11 +269,10 @@ static void vext_set_tail_elems_1s(target_ulong vl, void *vd, * 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); @@ -196,7 +280,9 @@ vext_ldst_stride(void *vd, void *v0, target_ulong base, uint32_t esz = 1 << log2_esz; uint32_t vma = vext_vma(desc); - for (i = env->vstart; i < env->vl; i++, env->vstart++) { + VSTART_CHECK_EARLY_EXIT(env, env->vl); + + for (i = env->vstart; i < env->vl; env->vstart = ++i) { k = 0; while (k < nf) { if (!vm && !vext_elem_mask(v0, i)) { @@ -226,10 +312,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, \ @@ -241,37 +327,137 @@ 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 */ /* unmasked unit-stride load and store operation */ -static void +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) +{ + 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. */ + probe_pages(env, addr, size, ra, access_type, mmu_index, &host, &flags, + true); + + if (flags == 0) { + if (nf == 1) { + vext_continuous_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_continuous_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 *ldst_elem, uint32_t log2_esz, uint32_t evl, - uintptr_t ra) + 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 i, k; + 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 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++; + VSTART_CHECK_EARLY_EXIT(env, evl); + +#if defined(CONFIG_USER_ONLY) + /* + * For data sizes <= 6 bytes we get better performance by simply calling + * vext_continuous_ldst_tlb + */ + if (nf == 1 && (evl << log2_esz) <= 6) { + addr = base + (env->vstart << log2_esz); + vext_continuous_ldst_tlb(env, ldst_tlb, vd, evl, addr, env->vstart, ra, + esz, is_load); + + env->vstart = 0; + vext_set_tail_elems_1s(evl, vd, desc, nf, esz, max_elems); + return; + } +#endif + + /* 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; + env->vstart = 0; vext_set_tail_elems_1s(evl, vd, desc, nf, esz, max_elems); } @@ -280,47 +466,47 @@ vext_ldst_us(void *vd, target_ulong base, CPURISCVState *env, uint32_t desc, * 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 @@ -330,8 +516,8 @@ void HELPER(vlm_v)(void *vd, void *v0, target_ulong base, { /* evl = ceil(vl/8) */ uint8_t evl = (env->vl + 7) >> 3; - vext_ldst_us(vd, base, env, desc, lde_b, - 0, evl, GETPC()); + 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, @@ -339,8 +525,8 @@ 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); } /* @@ -365,7 +551,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; @@ -375,8 +561,10 @@ vext_ldst_index(void *vd, void *v0, target_ulong base, uint32_t esz = 1 << log2_esz; uint32_t vma = vext_vma(desc); + VSTART_CHECK_EARLY_EXIT(env, env->vl); + /* 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)) { @@ -404,22 +592,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, \ @@ -430,74 +618,101 @@ 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 */ 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 msize = nf * esz; uint32_t vma = vext_vma(desc); - target_ulong addr, offset, remain; + target_ulong addr, addr_probe, addr_i, offset, remain, page_split, elems; + int mmu_index = riscv_env_mmu_index(env, false); + int flags, probe_flags; + void *host; - /* 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) { - 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) { - 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)) { + VSTART_CHECK_EARLY_EXIT(env, env->vl); + + 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; + } + + /* Check page permission/pmp/watchpoint/etc. */ + probe_pages(env, addr, elems * msize, ra, MMU_DATA_LOAD, mmu_index, &host, + &flags, true); + + /* If we are crossing a page check also the second page. */ + if (env->vl > elems) { + addr_probe = addr + (elems << log2_esz); + probe_pages(env, addr_probe, elems * msize, ra, MMU_DATA_LOAD, + mmu_index, &host, &probe_flags, true); + flags |= probe_flags; + } + + if (flags & ~TLB_WATCHPOINT) { + /* probe every access */ + for (i = env->vstart; i < env->vl; i++) { + if (!vm && !vext_elem_mask(v0, i)) { + continue; + } + addr_i = adjust_addr(env, base + i * (nf << log2_esz)); + if (i == 0) { + /* Allow fault on first element. */ + probe_pages(env, addr_i, nf << log2_esz, ra, MMU_DATA_LOAD, + mmu_index, &host, NULL, false); + } else { + remain = nf << log2_esz; + while (remain > 0) { + offset = -(addr_i | TARGET_PAGE_MASK); + + /* Probe nonfault on subsequent elements. */ + probe_pages(env, addr_i, offset, 0, MMU_DATA_LOAD, + mmu_index, &host, &flags, true); + + /* + * 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; } -#else - probe_pages(env, addr, offset, ra, MMU_DATA_LOAD); -#endif - } else { - vl = i; - goto ProbeSuccess; - } - if (remain <= offset) { - break; + if (remain <= offset) { + break; + } + remain -= offset; + addr_i = adjust_addr(env, addr_i + offset); } - remain -= offset; - addr = adjust_addr(env, addr + offset); } } } @@ -506,19 +721,54 @@ vext_ldff(void *vd, void *v0, target_ulong base, 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) { + /* 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; @@ -526,18 +776,18 @@ vext_ldff(void *vd, void *v0, target_ulong base, 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) @@ -552,76 +802,93 @@ GEN_VEXT_LDFF(vle64ff_v, int64_t, lde_d) /* * 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 = riscv_cpu_cfg(env)->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); + + /* 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; + } - 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++; + /* 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 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 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()); \ -} - -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) - -#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()); \ -} - -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) +#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_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_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_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 @@ -866,6 +1133,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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ @@ -898,6 +1167,8 @@ 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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ ETYPE carry = vext_elem_mask(v0, i); \ @@ -929,10 +1200,12 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ { \ uint32_t vl = env->vl; \ uint32_t vm = vext_vm(desc); \ - uint32_t total_elems = riscv_cpu_cfg(env)->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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ @@ -967,10 +1240,12 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ { \ uint32_t vl = env->vl; \ uint32_t vm = vext_vm(desc); \ - uint32_t total_elems = riscv_cpu_cfg(env)->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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ ETYPE carry = !vm && vext_elem_mask(v0, i); \ @@ -1067,6 +1342,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ /* set masked-off elements to 1s */ \ @@ -1114,6 +1391,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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ /* set masked-off elements to 1s */ \ @@ -1171,11 +1450,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 = riscv_cpu_cfg(env)->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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ @@ -1236,11 +1517,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 = riscv_cpu_cfg(env)->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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -1788,6 +2071,8 @@ void HELPER(NAME)(void *vd, void *vs1, CPURISCVState *env, \ uint32_t vta = vext_vta(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ *((ETYPE *)vd + H(i)) = s1; \ @@ -1812,6 +2097,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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ *((ETYPE *)vd + H(i)) = (ETYPE)s1; \ } \ @@ -1835,6 +2122,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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE *vt = (!vext_elem_mask(v0, i) ? vs2 : vs1); \ *((ETYPE *)vd + H(i)) = *(vt + H(i)); \ @@ -1859,6 +2148,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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ ETYPE d = (!vext_elem_mask(v0, i) ? s2 : \ @@ -1927,6 +2218,8 @@ vext_vv_rm_2(void *vd, void *v0, void *vs1, void *vs2, uint32_t vta = vext_vta(desc); uint32_t vma = vext_vma(desc); + VSTART_CHECK_EARLY_EXIT(env, vl); + switch (env->vxrm) { case 0: /* rnu */ vext_vv_rm_1(vd, v0, vs1, vs2, @@ -2052,6 +2345,8 @@ vext_vx_rm_2(void *vd, void *v0, target_long s1, void *vs2, uint32_t vta = vext_vta(desc); uint32_t vma = vext_vma(desc); + VSTART_CHECK_EARLY_EXIT(env, vl); + switch (env->vxrm) { case 0: /* rnu */ vext_vx_rm_1(vd, v0, s1, vs2, @@ -2826,6 +3121,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ /* set masked-off elements to 1s */ \ @@ -2869,6 +3166,8 @@ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ /* set masked-off elements to 1s */ \ @@ -3361,7 +3660,7 @@ static uint32_t fwmaccbf16(uint16_t a, uint16_t b, uint32_t d, float_status *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, fwmacc16) +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) @@ -3455,6 +3754,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ if (vl == 0) { \ return; \ } \ @@ -3971,11 +4272,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 = riscv_cpu_cfg(env)->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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s1 = *((ETYPE *)vs1 + H(i)); \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ @@ -4011,11 +4314,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 = riscv_cpu_cfg(env)->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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -4209,6 +4514,8 @@ 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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ ETYPE s2 = *((ETYPE *)vs2 + H(i)); \ *((ETYPE *)vd + H(i)) = \ @@ -4375,6 +4682,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ uint32_t i; \ TD s1 = *((TD *)vs1 + HD(0)); \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ TS2 s2 = *((TS2 *)vs2 + HS2(i)); \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -4382,7 +4691,9 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ } \ s1 = OP(s1, (TD)s2); \ } \ - *((TD *)vd + HD(0)) = s1; \ + if (vl > 0) { \ + *((TD *)vd + HD(0)) = s1; \ + } \ env->vstart = 0; \ /* set tail elements to 1s */ \ vext_set_elems_1s(vd, vta, esz, vlenb); \ @@ -4461,6 +4772,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ uint32_t i; \ TD s1 = *((TD *)vs1 + HD(0)); \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ TS2 s2 = *((TS2 *)vs2 + HS2(i)); \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -4468,7 +4781,9 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ } \ s1 = OP(s1, (TD)s2, &env->fp_status); \ } \ - *((TD *)vd + HD(0)) = s1; \ + if (vl > 0) { \ + *((TD *)vd + HD(0)) = s1; \ + } \ env->vstart = 0; \ /* set tail elements to 1s */ \ vext_set_elems_1s(vd, vta, esz, vlenb); \ @@ -4528,11 +4843,13 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ uint32_t desc) \ { \ uint32_t vl = env->vl; \ - uint32_t total_elems = riscv_cpu_cfg(env)->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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ a = vext_elem_mask(vs1, i); \ b = vext_elem_mask(vs2, i); \ @@ -4615,12 +4932,14 @@ static void vmsetm(void *vd, void *v0, void *vs2, CPURISCVState *env, { uint32_t vm = vext_vm(desc); uint32_t vl = env->vl; - uint32_t total_elems = riscv_cpu_cfg(env)->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; bool first_mask_bit = false; + VSTART_CHECK_EARLY_EXIT(env, vl); + for (i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { /* set masked-off elements to 1s */ @@ -4693,6 +5012,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, CPURISCVState *env, \ uint32_t sum = 0; \ int i; \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ /* set masked-off elements to 1s */ \ @@ -4726,6 +5047,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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ /* set masked-off elements to 1s */ \ @@ -4761,6 +5084,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, vl); \ + \ i_min = MAX(env->vstart, offset); \ for (i = i_min; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ @@ -4770,6 +5095,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); \ } @@ -4791,9 +5117,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, vl); \ + \ + 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 */ \ @@ -4804,9 +5133,11 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ } \ \ for (i = i_max; i < vl; ++i) { \ - if (vm || vext_elem_mask(v0, i)) { \ - *((ETYPE *)vd + H(i)) = 0; \ + if (!vm && !vext_elem_mask(v0, i)) { \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ + continue; \ } \ + *((ETYPE *)vd + H(i)) = 0; \ } \ \ env->vstart = 0; \ @@ -4834,6 +5165,8 @@ static void vslide1up_##BITWIDTH(void *vd, void *v0, uint64_t s1, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ /* set masked-off elements to 1s */ \ @@ -4883,6 +5216,8 @@ static void vslide1down_##BITWIDTH(void *vd, void *v0, uint64_t s1, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ /* set masked-off elements to 1s */ \ @@ -4958,6 +5293,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ uint64_t index; \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ /* set masked-off elements to 1s */ \ @@ -5001,6 +5338,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, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vm && !vext_elem_mask(v0, i)) { \ /* set masked-off elements to 1s */ \ @@ -5035,6 +5374,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ uint32_t vta = vext_vta(desc); \ uint32_t num = 0, i; \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ for (i = env->vstart; i < vl; i++) { \ if (!vext_elem_mask(vs1, i)) { \ continue; \ @@ -5044,7 +5385,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 */ @@ -5062,9 +5403,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; } @@ -5082,6 +5436,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ 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 index 9cf5c17cdea..b490b1d3989 100644 --- a/target/riscv/vector_internals.c +++ b/target/riscv/vector_internals.c @@ -16,6 +16,7 @@ * this program. If not, see . */ +#include "qemu/osdep.h" #include "vector_internals.h" /* set agnostic elements to 1s */ @@ -29,6 +30,28 @@ void vext_set_elems_1s(void *base, uint32_t is_agnostic, uint32_t cnt, 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); } @@ -43,6 +66,8 @@ void do_vext_vv(void *vd, void *v0, void *vs1, void *vs2, uint32_t vma = vext_vma(desc); uint32_t i; + VSTART_CHECK_EARLY_EXIT(env, vl); + for (i = env->vstart; i < vl; i++) { if (!vm && !vext_elem_mask(v0, i)) { /* set masked-off elements to 1s */ @@ -67,6 +92,8 @@ void do_vext_vx(void *vd, void *v0, target_long s1, void *vs2, uint32_t vma = vext_vma(desc); uint32_t i; + VSTART_CHECK_EARLY_EXIT(env, vl); + 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.h b/target/riscv/vector_internals.h index 8133111e5f6..8eee7e5c31c 100644 --- a/target/riscv/vector_internals.h +++ b/target/riscv/vector_internals.h @@ -19,12 +19,19 @@ #ifndef TARGET_RISCV_VECTOR_INTERNALS_H #define TARGET_RISCV_VECTOR_INTERNALS_H -#include "qemu/osdep.h" #include "qemu/bitops.h" +#include "hw/registerfields.h" #include "cpu.h" #include "tcg/tcg-gvec-desc.h" #include "internals.h" +#define VSTART_CHECK_EARLY_EXIT(env, vl) do { \ + if (env->vstart >= vl) { \ + env->vstart = 0; \ + return; \ + } \ +} while (0) + static inline uint32_t vext_nf(uint32_t desc) { return FIELD_EX32(simd_data(desc), VDATA, NF); @@ -152,6 +159,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env, vl); \ + \ 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/zce_helper.c b/target/riscv/zce_helper.c index b433bda16dc..55221f5f375 100644 --- a/target/riscv/zce_helper.c +++ b/target/riscv/zce_helper.c @@ -18,9 +18,8 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" target_ulong HELPER(cm_jalt)(CPURISCVState *env, uint32_t index) { diff --git a/target/rx/cpu-param.h b/target/rx/cpu-param.h index 521d669bdf7..84934f3bcaf 100644 --- a/target/rx/cpu-param.h +++ b/target/rx/cpu-param.h @@ -19,10 +19,11 @@ #ifndef RX_CPU_PARAM_H #define RX_CPU_PARAM_H -#define TARGET_LONG_BITS 32 #define TARGET_PAGE_BITS 12 #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 +#define TARGET_INSN_START_EXTRA_WORDS 0 + #endif diff --git a/target/rx/cpu-qom.h b/target/rx/cpu-qom.h index 1c8466a1870..ac2e5785efa 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_phases: The parent class' reset phase handlers. - * - * A RX CPU model. - */ -struct RXCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; +#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 157e57da0f5..c6dd5d6f832 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -21,10 +21,14 @@ #include "qapi/error.h" #include "cpu.h" #include "migration/vmstate.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "exec/page-protection.h" +#include "exec/translation-block.h" +#include "exec/target_page.h" #include "hw/loader.h" #include "fpu/softfloat.h" #include "tcg/debug-assert.h" +#include "accel/tcg/cpu-ops.h" static void rx_cpu_set_pc(CPUState *cs, vaddr value) { @@ -40,12 +44,23 @@ static vaddr rx_cpu_get_pc(CPUState *cs) return cpu->env.pc; } +static TCGTBCPUState rx_get_tb_cpu_state(CPUState *cs) +{ + CPURXState *env = cpu_env(cs); + uint32_t flags = 0; + + flags = FIELD_DP32(flags, PSW, PM, env->psw_pm); + flags = FIELD_DP32(flags, PSW, U, env->psw_u); + + return (TCGTBCPUState){ .pc = env->pc, .flags = flags }; +} + static void rx_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { RXCPU *cpu = RX_CPU(cs); - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); cpu->env.pc = tb->pc; } @@ -64,15 +79,20 @@ static bool rx_cpu_has_work(CPUState *cs) (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIR); } -static void rx_cpu_reset_hold(Object *obj) +static int rx_cpu_mmu_index(CPUState *cs, bool ifunc) { - RXCPU *cpu = RX_CPU(obj); - 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; if (rcc->parent_phases.hold) { - rcc->parent_phases.hold(obj); + rcc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPURXState, end_reset_fields)); @@ -87,22 +107,23 @@ static void rx_cpu_reset_hold(Object *obj) 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); + /* Default NaN value: sign bit clear, set frac msb */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); + /* + * TODO: "RX Family RXv1 Instruction Set Architecture" is not 100% clear + * on whether flush-to-zero should happen before or after rounding, but + * section 1.3.2 says that it happens when underflow is detected, and + * implies that underflow is detected after rounding. So this may not + * be the correct setting. + */ + set_float_ftz_detection(float_ftz_before_rounding, &env->fp_status); } static ObjectClass *rx_cpu_class_by_name(const char *cpu_model) @@ -111,16 +132,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; } @@ -164,6 +181,7 @@ static void rx_cpu_set_irq(void *opaque, int no, int request) static void rx_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) { + info->endian = BFD_ENDIAN_LITTLE; info->mach = bfd_mach_rx; info->print_insn = print_insn_rx; } @@ -183,38 +201,39 @@ 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); } -#ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps rx_sysemu_ops = { + .has_work = rx_cpu_has_work, .get_phys_page_debug = rx_cpu_get_phys_page_debug, }; -#endif -#include "hw/core/tcg-cpu-ops.h" +static const TCGCPUOps rx_tcg_ops = { + /* MTTCG not yet supported: require strict ordering */ + .guest_default_memory_order = TCG_MO_ALL, + .mttcg_supported = false, -static const struct TCGCPUOps rx_tcg_ops = { .initialize = rx_translate_init, + .translate_code = rx_translate_code, + .get_tb_cpu_state = rx_get_tb_cpu_state, .synchronize_from_tb = rx_cpu_synchronize_from_tb, .restore_state_to_opc = rx_restore_state_to_opc, + .mmu_index = rx_cpu_mmu_index, .tlb_fill = rx_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_uint32, -#ifndef CONFIG_USER_ONLY .cpu_exec_interrupt = rx_cpu_exec_interrupt, + .cpu_exec_halt = rx_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = rx_cpu_do_interrupt, -#endif /* !CONFIG_USER_ONLY */ }; -static void rx_cpu_class_init(ObjectClass *klass, void *data) +static void rx_cpu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); CPUClass *cc = CPU_CLASS(klass); @@ -227,19 +246,15 @@ static void rx_cpu_class_init(ObjectClass *klass, void *data) &rcc->parent_phases); cc->class_by_name = rx_cpu_class_by_name; - cc->has_work = rx_cpu_has_work; cc->dump_state = rx_cpu_dump_state; cc->set_pc = rx_cpu_set_pc; cc->get_pc = rx_cpu_get_pc; -#ifndef CONFIG_USER_ONLY cc->sysemu_ops = &rx_sysemu_ops; -#endif cc->gdb_read_register = rx_cpu_gdb_read_register; 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; } @@ -248,6 +263,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 7f03ffcfed2..ba5761b6472 100644 --- a/target/rx/cpu.h +++ b/target/rx/cpu.h @@ -23,9 +23,15 @@ #include "hw/registerfields.h" #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" +#ifdef CONFIG_USER_ONLY +#error "RX does not support user mode emulation" +#endif + /* PSW define */ REG32(PSW, 0) FIELD(PSW, C, 0, 1) @@ -107,56 +113,46 @@ 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); void rx_translate_init(void); -void rx_cpu_list(void); +void rx_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); 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 #define CPU_INTERRUPT_FIR CPU_INTERRUPT_TGT_INT_1 #define RX_CPU_IRQ 0 #define RX_CPU_FIR 1 -static inline void cpu_get_tb_cpu_state(CPURXState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *pc = env->pc; - *cs_base = 0; - *flags = FIELD_DP32(0, PSW, PM, env->psw_pm); - *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 d7e0e6689b6..f222bf003be 100644 --- a/target/rx/gdbstub.c +++ b/target/rx/gdbstub.c @@ -21,8 +21,7 @@ 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 dad5fb49768..0640ab322b5 100644 --- a/target/rx/helper.c +++ b/target/rx/helper.c @@ -20,7 +20,7 @@ #include "qemu/bitops.h" #include "cpu.h" #include "exec/log.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "hw/irq.h" void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte) @@ -40,13 +40,10 @@ void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte) env->psw_c = FIELD_EX32(psw, PSW, C); } -#ifndef CONFIG_USER_ONLY - #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; @@ -91,7 +88,7 @@ void rx_cpu_do_interrupt(CPUState *cs) cpu_stl_data(env, env->isp, env->pc); if (vec < 0x100) { - env->pc = cpu_ldl_data(env, 0xffffffc0 + vec * 4); + env->pc = cpu_ldl_data(env, 0xffffff80 + vec * 4); } else { env->pc = cpu_ldl_data(env, env->intb + (vec & 0xff) * 4); } @@ -122,8 +119,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) && @@ -148,5 +144,3 @@ hwaddr rx_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { return addr; } - -#endif /* !CONFIG_USER_ONLY */ diff --git a/target/rx/helper.h b/target/rx/helper.h index ebb47394744..8cc38b0cb71 100644 --- a/target/rx/helper.h +++ b/target/rx/helper.h @@ -4,27 +4,27 @@ DEF_HELPER_1(raise_privilege_violation, noreturn, env) DEF_HELPER_1(wait, noreturn, env) DEF_HELPER_2(rxint, noreturn, env, i32) DEF_HELPER_1(rxbrk, noreturn, env) -DEF_HELPER_FLAGS_3(fadd, TCG_CALL_NO_WG, f32, env, f32, f32) -DEF_HELPER_FLAGS_3(fsub, TCG_CALL_NO_WG, f32, env, f32, f32) -DEF_HELPER_FLAGS_3(fmul, TCG_CALL_NO_WG, f32, env, f32, f32) -DEF_HELPER_FLAGS_3(fdiv, TCG_CALL_NO_WG, f32, env, f32, f32) -DEF_HELPER_FLAGS_3(fcmp, TCG_CALL_NO_WG, void, env, f32, f32) -DEF_HELPER_FLAGS_2(ftoi, TCG_CALL_NO_WG, i32, env, f32) -DEF_HELPER_FLAGS_2(round, TCG_CALL_NO_WG, i32, env, f32) -DEF_HELPER_FLAGS_2(itof, TCG_CALL_NO_WG, f32, env, i32) +DEF_HELPER_3(fadd, f32, env, f32, f32) +DEF_HELPER_3(fsub, f32, env, f32, f32) +DEF_HELPER_3(fmul, f32, env, f32, f32) +DEF_HELPER_3(fdiv, f32, env, f32, f32) +DEF_HELPER_3(fcmp, void, env, f32, f32) +DEF_HELPER_2(ftoi, i32, env, f32) +DEF_HELPER_2(round, i32, env, f32) +DEF_HELPER_2(itof, f32, env, i32) DEF_HELPER_2(set_fpsw, void, env, i32) -DEF_HELPER_FLAGS_2(racw, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_FLAGS_2(set_psw_rte, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_FLAGS_2(set_psw, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_2(racw, void, env, i32) +DEF_HELPER_2(set_psw_rte, void, env, i32) +DEF_HELPER_2(set_psw, void, env, i32) DEF_HELPER_1(pack_psw, i32, env) -DEF_HELPER_FLAGS_3(div, TCG_CALL_NO_WG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32) -DEF_HELPER_FLAGS_1(scmpu, TCG_CALL_NO_WG, void, env) +DEF_HELPER_3(div, i32, env, i32, i32) +DEF_HELPER_3(divu, i32, env, i32, i32) +DEF_HELPER_1(scmpu, void, env) DEF_HELPER_1(smovu, void, env) DEF_HELPER_1(smovf, void, env) DEF_HELPER_1(smovb, void, env) DEF_HELPER_2(sstr, void, env, i32) -DEF_HELPER_FLAGS_2(swhile, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_FLAGS_2(suntil, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_FLAGS_2(rmpa, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_2(swhile, void, env, i32) +DEF_HELPER_2(suntil, void, env, i32) +DEF_HELPER_2(rmpa, void, env, i32) DEF_HELPER_1(satr, void, env) diff --git a/target/rx/meson.build b/target/rx/meson.build index 8de0ad49b9b..d196737ce33 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 691a12b2be1..2b190a4b2cf 100644 --- a/target/rx/op_helper.c +++ b/target/rx/op_helper.c @@ -19,9 +19,8 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "fpu/softfloat.h" #include "tcg/debug-assert.h" @@ -99,8 +98,8 @@ static void update_fpsw(CPURXState *env, float32 ret, uintptr_t retaddr) if (xcpt & float_flag_inexact) { SET_FPSW(X); } - if ((xcpt & (float_flag_input_denormal - | float_flag_output_denormal)) + if ((xcpt & (float_flag_input_denormal_flushed + | float_flag_output_denormal_flushed)) && !FIELD_EX32(env->fpsw, FPSW, DN)) { env->fpsw = FIELD_DP32(env->fpsw, FPSW, CE, 1); } diff --git a/target/rx/translate.c b/target/rx/translate.c index f552a0319ac..19a9584a829 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -20,12 +20,11 @@ #include "qemu/bswap.h" #include "qemu/qemu-print.h" #include "cpu.h" -#include "exec/exec-all.h" #include "tcg/tcg-op.h" -#include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/log.h" #define HELPER_H "helper.h" @@ -75,10 +74,10 @@ static TCGv_i64 cpu_acc; /* 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; @@ -86,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; } @@ -131,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; @@ -191,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) @@ -237,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 { @@ -318,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); @@ -370,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; @@ -385,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); @@ -492,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; } @@ -570,10 +569,7 @@ static bool trans_MOVU_mr(DisasContext *ctx, arg_MOVU_mr *a) /* 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; } @@ -1244,12 +1240,12 @@ static bool trans_EMULU_mr(DisasContext *ctx, arg_EMULU_mr *a) 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 */ @@ -1644,35 +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_constant_i32(a->sz); \ - gen_helper_##op(cpu_env, size); \ + gen_helper_##op(tcg_env, size); \ } while (0) /* suntile. */ @@ -1803,7 +1799,7 @@ static bool trans_MVTACLO(DisasContext *ctx, arg_MVTACLO *a) static bool trans_RACW(DisasContext *ctx, arg_RACW *a) { TCGv imm = tcg_constant_i32(a->imm + 1); - gen_helper_racw(cpu_env, imm); + gen_helper_racw(tcg_env, imm); return true; } @@ -1825,7 +1821,7 @@ static bool trans_SAT(DisasContext *ctx, arg_SAT *a) /* satr */ static bool trans_SATR(DisasContext *ctx, arg_SATR *a) { - gen_helper_satr(cpu_env); + gen_helper_satr(tcg_env); return true; } @@ -1835,7 +1831,7 @@ static bool trans_SATR(DisasContext *ctx, arg_SATR *a) cat3(arg_, name, _ir) * a) \ { \ TCGv imm = tcg_constant_i32(li(ctx, 0)); \ - gen_helper_##op(cpu_regs[a->rd], cpu_env, \ + gen_helper_##op(cpu_regs[a->rd], tcg_env, \ cpu_regs[a->rd], imm); \ return true; \ } \ @@ -1845,7 +1841,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, \ + gen_helper_##op(cpu_regs[a->rd], tcg_env, \ cpu_regs[a->rd], val); \ return true; \ } @@ -1856,7 +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); \ + gen_helper_##op(cpu_regs[a->rd], tcg_env, val); \ return true; \ } @@ -1869,7 +1865,7 @@ FOP(FDIV, fdiv) static bool trans_FCMP_ir(DisasContext *ctx, arg_FCMP_ir * a) { TCGv imm = tcg_constant_i32(li(ctx, 0)); - gen_helper_fcmp(cpu_env, cpu_regs[a->rd], imm); + gen_helper_fcmp(tcg_env, cpu_regs[a->rd], imm); return true; } @@ -1880,7 +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); + gen_helper_fcmp(tcg_env, cpu_regs[a->rd], val); return true; } @@ -1894,7 +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); + gen_helper_itof(cpu_regs[a->rd], tcg_env, val); return true; } @@ -2146,7 +2142,7 @@ 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; } return true; @@ -2160,7 +2156,7 @@ 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; } return true; @@ -2170,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; } @@ -2183,7 +2179,7 @@ static bool trans_INT(DisasContext *ctx, arg_INT *a) tcg_debug_assert(a->imm < 0x100); vec = tcg_constant_i32(a->imm); tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next); - gen_helper_rxint(cpu_env, vec); + gen_helper_rxint(tcg_env, vec); ctx->base.is_jmp = DISAS_NORETURN; return true; } @@ -2193,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; } @@ -2225,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); } } @@ -2254,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 rx_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc; @@ -2279,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) @@ -2291,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]); } @@ -2311,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 72da48136c6..8a95f2bc3ff 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 51a2116515c..2c26e992959 100644 --- a/target/s390x/arch_dump.c +++ b/target/s390x/arch_dump.c @@ -16,7 +16,7 @@ #include "cpu.h" #include "s390x-internal.h" #include "elf.h" -#include "sysemu/dump.h" +#include "system/dump.h" #include "kvm/kvm_s390x.h" #include "target/s390x/kvm/pv.h" @@ -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) @@ -433,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) { @@ -448,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 ffa9e94d848..869d3a4ad54 100644 --- a/target/s390x/cpu-dump.c +++ b/target/s390x/cpu-dump.c @@ -23,12 +23,11 @@ #include "cpu.h" #include "s390x-internal.h" #include "qemu/qemu-print.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" 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 84ca08626bb..abfae3bedfb 100644 --- a/target/s390x/cpu-param.h +++ b/target/s390x/cpu-param.h @@ -2,15 +2,16 @@ * 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 #define S390_CPU_PARAM_H -#define TARGET_LONG_BITS 64 #define TARGET_PAGE_BITS 12 #define TARGET_PHYS_ADDR_SPACE_BITS 64 #define TARGET_VIRT_ADDR_SPACE_BITS 64 +#define TARGET_INSN_START_EXTRA_WORDS 2 + #endif diff --git a/target/s390x/cpu-qom.h b/target/s390x/cpu-qom.h index 00cae2b1311..c59bb1eab15 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 deleted file mode 100644 index 8112561e5ea..00000000000 --- a/target/s390x/cpu-sysemu.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * QEMU S/390 CPU - System Emulation-only code - * - * Copyright (c) 2009 Ulrich Hecht - * Copyright (c) 2011 Alexander Graf - * Copyright (c) 2012 SUSE LINUX Products GmbH - * Copyright (c) 2012 IBM Corp. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qapi/error.h" -#include "cpu.h" -#include "s390x-internal.h" -#include "kvm/kvm_s390x.h" -#include "sysemu/kvm.h" -#include "sysemu/reset.h" -#include "qemu/timer.h" -#include "trace.h" -#include "qapi/qapi-visit-run-state.h" -#include "sysemu/hw_accel.h" - -#include "target/s390x/kvm/pv.h" -#include "hw/boards.h" -#include "sysemu/sysemu.h" -#include "sysemu/tcg.h" -#include "hw/core/sysemu-cpu-ops.h" - -/* S390CPUClass::load_normal() */ -static void s390_cpu_load_normal(CPUState *s) -{ - S390CPU *cpu = S390_CPU(s); - uint64_t spsw; - - if (!s390_is_pv()) { - spsw = ldq_phys(s->as, 0); - cpu->env.psw.mask = spsw & PSW_MASK_SHORT_CTRL; - /* - * Invert short psw indication, so SIE will report a specification - * exception if it was not set. - */ - cpu->env.psw.mask ^= PSW_MASK_SHORTPSW; - cpu->env.psw.addr = spsw & PSW_MASK_SHORT_ADDR; - } else { - /* - * Firmware requires us to set the load state before we set - * the cpu to operating on protected guests. - */ - s390_cpu_set_state(S390_CPU_STATE_LOAD, cpu); - } - s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu); -} - -void s390_cpu_machine_reset_cb(void *opaque) -{ - S390CPU *cpu = opaque; - - run_on_cpu(CPU(cpu), s390_do_cpu_full_reset, RUN_ON_CPU_NULL); -} - -static GuestPanicInformation *s390_cpu_get_crash_info(CPUState *cs) -{ - GuestPanicInformation *panic_info; - S390CPU *cpu = S390_CPU(cs); - - cpu_synchronize_state(cs); - panic_info = g_new0(GuestPanicInformation, 1); - - panic_info->type = GUEST_PANIC_INFORMATION_TYPE_S390; - panic_info->u.s390.core = cpu->env.core_id; - panic_info->u.s390.psw_mask = cpu->env.psw.mask; - panic_info->u.s390.psw_addr = cpu->env.psw.addr; - panic_info->u.s390.reason = cpu->env.crash_reason; - - return panic_info; -} - -static void s390_cpu_get_crash_info_qom(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - CPUState *cs = CPU(obj); - GuestPanicInformation *panic_info; - - if (!cs->crash_occurred) { - error_setg(errp, "No crash occurred"); - return; - } - - panic_info = s390_cpu_get_crash_info(cs); - - visit_type_GuestPanicInformation(v, "crash-information", &panic_info, - errp); - qapi_free_GuestPanicInformation(panic_info); -} - -void s390_cpu_init_sysemu(Object *obj) -{ - CPUState *cs = CPU(obj); - S390CPU *cpu = S390_CPU(obj); - - cs->start_powered_off = true; - object_property_add(obj, "crash-information", "GuestPanicInformation", - s390_cpu_get_crash_info_qom, NULL, NULL, NULL); - cpu->env.tod_timer = - timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_tod_timer, cpu); - cpu->env.cpu_timer = - timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu); - s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); -} - -bool s390_cpu_realize_sysemu(DeviceState *dev, Error **errp) -{ - S390CPU *cpu = S390_CPU(dev); - MachineState *ms = MACHINE(qdev_get_machine()); - unsigned int max_cpus = ms->smp.max_cpus; - - if (cpu->env.core_id >= max_cpus) { - error_setg(errp, "Unable to add CPU with core-id: %" PRIu32 - ", maximum core-id: %d", cpu->env.core_id, - max_cpus - 1); - return false; - } - - if (cpu_exists(cpu->env.core_id)) { - error_setg(errp, "Unable to add CPU with core-id: %" PRIu32 - ", it already exists", cpu->env.core_id); - return false; - } - - /* sync cs->cpu_index and env->core_id. The latter is needed for TCG. */ - CPU(cpu)->cpu_index = cpu->env.core_id; - return true; -} - -void s390_cpu_finalize(Object *obj) -{ - S390CPU *cpu = S390_CPU(obj); - - timer_free(cpu->env.tod_timer); - timer_free(cpu->env.cpu_timer); - - qemu_unregister_reset(s390_cpu_machine_reset_cb, cpu); - g_free(cpu->irqstate); -} - -static const struct SysemuCPUOps s390_sysemu_ops = { - .get_phys_page_debug = s390_cpu_get_phys_page_debug, - .get_crash_info = s390_cpu_get_crash_info, - .write_elf64_note = s390_cpu_write_elf64_note, - .legacy_vmsd = &vmstate_s390_cpu, -}; - -void s390_cpu_class_init_sysemu(CPUClass *cc) -{ - S390CPUClass *scc = S390_CPU_CLASS(cc); - - scc->load_normal = s390_cpu_load_normal; - cc->sysemu_ops = &s390_sysemu_ops; -} - -static bool disabled_wait(CPUState *cpu) -{ - return cpu->halted && !(S390_CPU(cpu)->env.psw.mask & - (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK)); -} - -static unsigned s390_count_running_cpus(void) -{ - CPUState *cpu; - int nr_running = 0; - - CPU_FOREACH(cpu) { - uint8_t state = S390_CPU(cpu)->env.cpu_state; - if (state == S390_CPU_STATE_OPERATING || - state == S390_CPU_STATE_LOAD) { - if (!disabled_wait(cpu)) { - nr_running++; - } - } - } - - return nr_running; -} - -unsigned int s390_cpu_halt(S390CPU *cpu) -{ - CPUState *cs = CPU(cpu); - trace_cpu_halt(cs->cpu_index); - - if (!cs->halted) { - cs->halted = 1; - cs->exception_index = EXCP_HLT; - } - - return s390_count_running_cpus(); -} - -void s390_cpu_unhalt(S390CPU *cpu) -{ - CPUState *cs = CPU(cpu); - trace_cpu_unhalt(cs->cpu_index); - - if (cs->halted) { - cs->halted = 0; - cs->exception_index = -1; - } -} - -unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu) - { - trace_cpu_set_state(CPU(cpu)->cpu_index, cpu_state); - - switch (cpu_state) { - case S390_CPU_STATE_STOPPED: - case S390_CPU_STATE_CHECK_STOP: - /* halt the cpu for common infrastructure */ - s390_cpu_halt(cpu); - break; - case S390_CPU_STATE_OPERATING: - case S390_CPU_STATE_LOAD: - /* - * Starting a CPU with a PSW WAIT bit set: - * KVM: handles this internally and triggers another WAIT exit. - * TCG: will actually try to continue to run. Don't unhalt, will - * be done when the CPU actually has work (an interrupt). - */ - if (!tcg_enabled() || !(cpu->env.psw.mask & PSW_MASK_WAIT)) { - s390_cpu_unhalt(cpu); - } - break; - default: - error_report("Requested CPU state is not a valid S390 CPU state: %u", - cpu_state); - exit(1); - } - if (kvm_enabled() && cpu->env.cpu_state != cpu_state) { - kvm_s390_set_cpu_state(cpu, cpu_state); - } - cpu->env.cpu_state = cpu_state; - - return s390_count_running_cpus(); -} - -int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit) -{ - if (kvm_enabled()) { - return kvm_s390_set_mem_limit(new_limit, hw_limit); - } - return 0; -} - -void s390_set_max_pagesize(uint64_t pagesize, Error **errp) -{ - if (kvm_enabled()) { - kvm_s390_set_max_pagesize(pagesize, errp); - } -} - -void s390_cmma_reset(void) -{ - if (kvm_enabled()) { - kvm_s390_cmma_reset(); - } -} - -int s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch_id, - int vq, bool assign) -{ - if (kvm_enabled()) { - return kvm_s390_assign_subch_ioeventfd(notifier, sch_id, vq, assign); - } else { - return 0; - } -} - -void s390_crypto_reset(void) -{ - if (kvm_enabled()) { - kvm_s390_crypto_reset(); - } -} - -void s390_enable_css_support(S390CPU *cpu) -{ - if (kvm_enabled()) { - kvm_s390_enable_css_support(cpu); - } -} - -void s390_do_cpu_set_diag318(CPUState *cs, run_on_cpu_data arg) -{ - if (kvm_enabled()) { - kvm_s390_set_diag318(cs, arg.host_ulong); - } -} diff --git a/target/s390x/cpu-system.c b/target/s390x/cpu-system.c new file mode 100644 index 00000000000..9b380e343c2 --- /dev/null +++ b/target/s390x/cpu-system.c @@ -0,0 +1,325 @@ +/* + * QEMU S/390 CPU - System-only code + * + * Copyright (c) 2009 Ulrich Hecht + * Copyright (c) 2011 Alexander Graf + * Copyright (c) 2012 SUSE LINUX Products GmbH + * Copyright (c) 2012 IBM Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "cpu.h" +#include "s390x-internal.h" +#include "kvm/kvm_s390x.h" +#include "system/kvm.h" +#include "system/reset.h" +#include "qemu/timer.h" +#include "trace.h" +#include "qapi/qapi-visit-run-state.h" +#include "system/hw_accel.h" + +#include "target/s390x/kvm/pv.h" +#include "hw/boards.h" +#include "system/system.h" +#include "system/tcg.h" +#include "hw/core/sysemu-cpu-ops.h" + +bool s390_cpu_has_work(CPUState *cs) +{ + S390CPU *cpu = S390_CPU(cs); + + /* STOPPED cpus can never wake up */ + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_LOAD && + s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) { + return false; + } + + if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { + return false; + } + + return s390_cpu_has_int(cpu); +} + +/* S390CPUClass::load_normal() */ +static void s390_cpu_load_normal(CPUState *s) +{ + S390CPU *cpu = S390_CPU(s); + uint64_t spsw; + + if (!s390_is_pv()) { + spsw = ldq_phys(s->as, 0); + cpu->env.psw.mask = spsw & PSW_MASK_SHORT_CTRL; + /* + * Invert short psw indication, so SIE will report a specification + * exception if it was not set. + */ + cpu->env.psw.mask ^= PSW_MASK_SHORTPSW; + cpu->env.psw.addr = spsw & PSW_MASK_SHORT_ADDR; + } else { + /* + * Firmware requires us to set the load state before we set + * the cpu to operating on protected guests. + */ + s390_cpu_set_state(S390_CPU_STATE_LOAD, cpu); + } + s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu); +} + +void s390_cpu_machine_reset_cb(void *opaque) +{ + S390CPU *cpu = opaque; + + run_on_cpu(CPU(cpu), s390_do_cpu_full_reset, RUN_ON_CPU_NULL); +} + +static GuestPanicInformation *s390_cpu_get_crash_info(CPUState *cs) +{ + GuestPanicInformation *panic_info; + S390CPU *cpu = S390_CPU(cs); + + cpu_synchronize_state(cs); + panic_info = g_new0(GuestPanicInformation, 1); + + panic_info->type = GUEST_PANIC_INFORMATION_TYPE_S390; + panic_info->u.s390.core = cpu->env.core_id; + panic_info->u.s390.psw_mask = cpu->env.psw.mask; + panic_info->u.s390.psw_addr = cpu->env.psw.addr; + panic_info->u.s390.reason = cpu->env.crash_reason; + + return panic_info; +} + +static void s390_cpu_get_crash_info_qom(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CPUState *cs = CPU(obj); + GuestPanicInformation *panic_info; + + if (!cs->crash_occurred) { + error_setg(errp, "No crash occurred"); + return; + } + + panic_info = s390_cpu_get_crash_info(cs); + + visit_type_GuestPanicInformation(v, "crash-information", &panic_info, + errp); + qapi_free_GuestPanicInformation(panic_info); +} + +void s390_cpu_system_init(Object *obj) +{ + CPUState *cs = CPU(obj); + S390CPU *cpu = S390_CPU(obj); + + cs->start_powered_off = true; + object_property_add(obj, "crash-information", "GuestPanicInformation", + s390_cpu_get_crash_info_qom, NULL, NULL, NULL); + cpu->env.tod_timer = + timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_tod_timer, cpu); + cpu->env.cpu_timer = + timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu); + s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); +} + +bool s390_cpu_system_realize(DeviceState *dev, Error **errp) +{ + S390CPU *cpu = S390_CPU(dev); + MachineState *ms = MACHINE(qdev_get_machine()); + unsigned int max_cpus = ms->smp.max_cpus; + + if (cpu->env.core_id >= max_cpus) { + error_setg(errp, "Unable to add CPU with core-id: %" PRIu32 + ", maximum core-id: %d", cpu->env.core_id, + max_cpus - 1); + return false; + } + + if (cpu_exists(cpu->env.core_id)) { + error_setg(errp, "Unable to add CPU with core-id: %" PRIu32 + ", it already exists", cpu->env.core_id); + return false; + } + + /* sync cs->cpu_index and env->core_id. The latter is needed for TCG. */ + CPU(cpu)->cpu_index = cpu->env.core_id; + return true; +} + +void s390_cpu_finalize(Object *obj) +{ + S390CPU *cpu = S390_CPU(obj); + + timer_free(cpu->env.tod_timer); + timer_free(cpu->env.cpu_timer); + + qemu_unregister_reset(s390_cpu_machine_reset_cb, cpu); + g_free(cpu->irqstate); +} + +static const struct SysemuCPUOps s390_sysemu_ops = { + .has_work = s390_cpu_has_work, + .get_phys_page_debug = s390_cpu_get_phys_page_debug, + .get_crash_info = s390_cpu_get_crash_info, + .write_elf64_note = s390_cpu_write_elf64_note, + .legacy_vmsd = &vmstate_s390_cpu, +}; + +void s390_cpu_system_class_init(CPUClass *cc) +{ + S390CPUClass *scc = S390_CPU_CLASS(cc); + + scc->load_normal = s390_cpu_load_normal; + cc->sysemu_ops = &s390_sysemu_ops; +} + +static bool disabled_wait(CPUState *cpu) +{ + return cpu->halted && !(S390_CPU(cpu)->env.psw.mask & + (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK)); +} + +static unsigned s390_count_running_cpus(void) +{ + CPUState *cpu; + int nr_running = 0; + + CPU_FOREACH(cpu) { + uint8_t state = S390_CPU(cpu)->env.cpu_state; + if (state == S390_CPU_STATE_OPERATING || + state == S390_CPU_STATE_LOAD) { + if (!disabled_wait(cpu)) { + nr_running++; + } + } + } + + return nr_running; +} + +unsigned int s390_cpu_halt(S390CPU *cpu) +{ + CPUState *cs = CPU(cpu); + trace_cpu_halt(cs->cpu_index); + + if (!cs->halted) { + cs->halted = 1; + cs->exception_index = EXCP_HLT; + } + + return s390_count_running_cpus(); +} + +void s390_cpu_unhalt(S390CPU *cpu) +{ + CPUState *cs = CPU(cpu); + trace_cpu_unhalt(cs->cpu_index); + + if (cs->halted) { + cs->halted = 0; + cs->exception_index = -1; + } +} + +unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu) + { + trace_cpu_set_state(CPU(cpu)->cpu_index, cpu_state); + + switch (cpu_state) { + case S390_CPU_STATE_STOPPED: + case S390_CPU_STATE_CHECK_STOP: + /* halt the cpu for common infrastructure */ + s390_cpu_halt(cpu); + break; + case S390_CPU_STATE_OPERATING: + case S390_CPU_STATE_LOAD: + /* + * Starting a CPU with a PSW WAIT bit set: + * KVM: handles this internally and triggers another WAIT exit. + * TCG: will actually try to continue to run. Don't unhalt, will + * be done when the CPU actually has work (an interrupt). + */ + if (!tcg_enabled() || !(cpu->env.psw.mask & PSW_MASK_WAIT)) { + s390_cpu_unhalt(cpu); + } + break; + default: + error_report("Requested CPU state is not a valid S390 CPU state: %u", + cpu_state); + exit(1); + } + if (kvm_enabled() && cpu->env.cpu_state != cpu_state) { + kvm_s390_set_cpu_state(cpu, cpu_state); + } + cpu->env.cpu_state = cpu_state; + + return s390_count_running_cpus(); +} + +void s390_cmma_reset(void) +{ + if (kvm_enabled()) { + kvm_s390_cmma_reset(); + } +} + +int s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch_id, + int vq, bool assign) +{ + if (kvm_enabled()) { + return kvm_s390_assign_subch_ioeventfd(notifier, sch_id, vq, assign); + } else { + return 0; + } +} + +void s390_crypto_reset(void) +{ + if (kvm_enabled()) { + kvm_s390_crypto_reset(); + } +} + +void s390_enable_css_support(S390CPU *cpu) +{ + if (kvm_enabled()) { + kvm_s390_enable_css_support(cpu); + } +} + +void s390_do_cpu_set_diag318(CPUState *cs, run_on_cpu_data arg) +{ + if (kvm_enabled()) { + 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 df167493c34..f05ce317da1 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -25,18 +25,21 @@ #include "cpu.h" #include "s390x-internal.h" #include "kvm/kvm_s390x.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "qemu/module.h" #include "trace.h" #include "qapi/qapi-types-machine.h" -#include "sysemu/hw_accel.h" +#include "system/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" +#include "system/tcg.h" #ifndef CONFIG_USER_ONLY -#include "sysemu/reset.h" +#include "system/reset.h" #endif +#include "hw/s390x/cpu-topology.h" #define CR0_RESET 0xE0UL #define CR14_RESET 0xC2000000UL; @@ -123,47 +126,40 @@ static vaddr s390_cpu_get_pc(CPUState *cs) return cpu->env.psw.addr; } -static bool s390_cpu_has_work(CPUState *cs) -{ - S390CPU *cpu = S390_CPU(cs); - - /* STOPPED cpus can never wake up */ - if (s390_cpu_get_state(cpu) != S390_CPU_STATE_LOAD && - s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) { - return false; - } - - if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { - return false; - } - - return s390_cpu_has_int(cpu); -} - static void s390_query_cpu_fast(CPUState *cpu, CpuInfoFast *value) { S390CPU *s390_cpu = S390_CPU(cpu); value->u.s390x.cpu_state = s390_cpu->env.cpu_state; +#if !defined(CONFIG_USER_ONLY) + if (s390_has_topology()) { + value->u.s390x.has_dedicated = true; + value->u.s390x.dedicated = s390_cpu->env.dedicated; + value->u.s390x.has_entitlement = true; + value->u.s390x.entitlement = s390_cpu->env.entitlement; + } +#endif } -/* S390CPUClass::reset() */ -static void s390_cpu_reset(CPUState *s, cpu_reset_type type) +/* S390CPUClass Resettable reset_hold phase method */ +static void s390_cpu_reset_hold(Object *obj, ResetType type) { - S390CPU *cpu = S390_CPU(s); + 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) - @@ -187,8 +183,14 @@ 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); + set_float_3nan_prop_rule(float_3nan_prop_s_abc, &env->fpu_status); + set_float_infzeronan_rule(float_infzeronan_dnan_always, + &env->fpu_status); + /* Default NaN value: sign bit clear, frac msb set */ + set_float_default_nan_pattern(0b01000000, &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) - @@ -197,20 +199,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; } @@ -221,6 +221,7 @@ static void s390_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) { info->mach = bfd_mach_s390_64; info->cap_arch = CS_ARCH_SYSZ; + info->endian = BFD_ENDIAN_BIG; info->cap_insn_unit = 2; info->cap_insn_split = 6; } @@ -238,7 +239,7 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp) } #if !defined(CONFIG_USER_ONLY) - if (!s390_cpu_realize_sysemu(dev, &err)) { + if (!s390_cpu_system_realize(dev, &err)) { goto out; } #endif @@ -274,47 +275,110 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp) 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) - s390_cpu_init_sysemu(obj); + s390_cpu_system_init(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) +#ifndef CONFIG_USER_ONLY +static const Property s390x_cpu_properties[] = { DEFINE_PROP_UINT32("core-id", S390CPU, env.core_id, 0), -#endif - DEFINE_PROP_END_OF_LIST() + 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 + +#ifdef CONFIG_TCG +#include "accel/tcg/cpu-ops.h" +#include "tcg/tcg_s390x.h" -static void s390_cpu_reset_full(DeviceState *dev) +static int s390x_cpu_mmu_index(CPUState *cs, bool ifetch) { - CPUState *s = CPU(dev); - return s390_cpu_reset(s, S390_CPU_RESET_CLEAR); + return s390x_env_mmu_index(cpu_env(cs), ifetch); } -#ifdef CONFIG_TCG -#include "hw/core/tcg-cpu-ops.h" +static TCGTBCPUState s390x_get_tb_cpu_state(CPUState *cs) +{ + CPUS390XState *env = cpu_env(cs); + 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); + } + + 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; + } + + return (TCGTBCPUState){ + .pc = env->psw.addr, + .flags = flags, + .cs_base = env->ex_value, + }; +} + +#ifndef CONFIG_USER_ONLY +static vaddr s390_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + return wrap_address(cpu_env(cs), result); +} +#endif + +static const TCGCPUOps s390_tcg_ops = { + .mttcg_supported = true, + .precise_smc = true, + /* + * The z/Architecture has a strong memory model with some + * store-after-load re-ordering. + */ + .guest_default_memory_order = TCG_MO_ALL & ~TCG_MO_ST_LD, -static const struct TCGCPUOps s390_tcg_ops = { .initialize = s390x_translate_init, + .translate_code = s390x_translate_code, + .get_tb_cpu_state = s390x_get_tb_cpu_state, .restore_state_to_opc = s390x_restore_state_to_opc, + .mmu_index = s390x_cpu_mmu_index, #ifdef CONFIG_USER_ONLY .record_sigsegv = s390_cpu_record_sigsegv, .record_sigbus = s390_cpu_record_sigbus, #else .tlb_fill = s390_cpu_tlb_fill, + .pointer_wrap = s390_pointer_wrap, .cpu_exec_interrupt = s390_cpu_exec_interrupt, + .cpu_exec_halt = s390_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = s390_cpu_do_interrupt, .debug_excp_handler = s390x_cpu_debug_excp_handler, .do_unaligned_access = s390x_cpu_do_unaligned_access, @@ -322,22 +386,22 @@ static const struct TCGCPUOps s390_tcg_ops = { }; #endif /* CONFIG_TCG */ -static void s390_cpu_class_init(ObjectClass *oc, void *data) +static void s390_cpu_class_init(ObjectClass *oc, const 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->class_by_name = s390_cpu_class_by_name; + cc->list_cpus = s390_cpu_list; cc->dump_state = s390_cpu_dump_state; cc->query_cpu_fast = s390_query_cpu_fast; cc->set_pc = s390_cpu_set_pc; @@ -345,10 +409,10 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_read_register = s390_cpu_gdb_read_register; cc->gdb_write_register = s390_cpu_gdb_write_register; #ifndef CONFIG_USER_ONLY - s390_cpu_class_init_sysemu(cc); + device_class_set_props(dc, s390x_cpu_properties); + s390_cpu_system_class_init(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 304029e57cf..aa931cb6748 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -27,19 +27,14 @@ #include "cpu-qom.h" #include "cpu_models.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" -#include "tcg/tcg_s390x.h" +#include "qapi/qapi-types-machine-common.h" #define ELF_MACHINE_UNAME "S390X" -/* The z/Architecture has a strong memory model with some store-after-load re-ordering */ -#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) - -#define TARGET_HAS_PRECISE_SMC - -#define TARGET_INSN_START_EXTRA_WORDS 2 - #define MMU_USER_IDX 0 #define S390_MAX_CPUS 248 @@ -55,7 +50,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. @@ -132,6 +127,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 +152,7 @@ struct CPUArchState { /* currently processed sigp order */ uint8_t sigp_order; -}; +} CPUS390XState; static inline uint64_t *get_freg(CPUS390XState *cs, int nr) { @@ -166,11 +166,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 +175,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; @@ -313,19 +331,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) - -/* 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) +#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 + +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 @@ -349,7 +380,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; @@ -380,36 +411,23 @@ static inline int cpu_mmu_index(CPUS390XState *env, bool ifetch) #endif } -static inline void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - if (env->psw.addr & 1) { - /* - * Instructions must be at even addresses. - * This needs to be checked before address translation. - */ - env->int_pgm_ilen = 2; /* see s390_cpu_tlb_fill() */ - tcg_s390_program_interrupt(env, PGM_SPECIFICATION, 0); - } - *pc = env->psw.addr; - *cs_base = env->ex_value; - *flags = (env->psw.mask >> FLAG_MASK_PSW_SHIFT) & FLAG_MASK_PSW; - if (env->cregs[0] & CR0_AFP) { - *flags |= FLAG_MASK_AFP; - } - if (env->cregs[0] & CR0_VECTOR) { - *flags |= FLAG_MASK_VECTOR; - } -} - /* 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 @@ -453,8 +471,6 @@ static inline void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *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) { @@ -565,6 +581,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; @@ -572,9 +611,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 */ @@ -760,16 +852,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) @@ -782,8 +870,6 @@ static inline void s390_do_cpu_load_normal(CPUState *cs, run_on_cpu_data arg) /* cpu.c */ void s390_crypto_reset(void); -int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit); -void s390_set_max_pagesize(uint64_t pagesize, Error **errp); void s390_cmma_reset(void); void s390_enable_css_support(S390CPU *cpu); void s390_do_cpu_set_diag318(CPUState *cs, run_on_cpu_data arg); @@ -803,16 +889,7 @@ static inline uint8_t s390_cpu_get_state(S390CPU *cpu) } -/* cpu_models.c */ -void s390_cpu_list(void); -#define cpu_list s390_cpu_list -void s390_set_qemu_cpu_model(uint16_t type, uint8_t gen, uint8_t ec_ga, - const S390FeatInit feat_init); - - /* 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 */ @@ -851,6 +928,4 @@ uint64_t s390_cpu_get_psw_mask(CPUS390XState *env); /* outside of target/s390x/ */ S390CPU *s390_cpu_addr2state(uint16_t cpu_addr); -#include "exec/cpu-all.h" - #endif diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index d28eb658456..4b5be6798ef 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -93,6 +93,7 @@ void s390_fill_feat_block(const S390FeatBitmap features, S390FeatType type, case S390_FEAT_TYPE_KDSA: case S390_FEAT_TYPE_SORTL: case S390_FEAT_TYPE_DFLTCC: + case S390_FEAT_TYPE_PFCR: set_be_bit(0, data); /* query is always available */ break; default: @@ -212,6 +213,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, \ @@ -222,8 +240,10 @@ void s390_feat_bitmap_to_ascii(const S390FeatBitmap features, void *opaque, /* indexed by feature group number for easy lookup */ static S390FeatGroupDef s390_feature_groups[] = { FEAT_GROUP_INIT("plo", PLO, "Perform-locked-operation facility"), + FEAT_GROUP_INIT("plo_ext", PLO_EXT, "PLO-extension facility"), FEAT_GROUP_INIT("tods", TOD_CLOCK_STEERING, "Tod-clock-steering facility"), FEAT_GROUP_INIT("gen13ptff", GEN13_PTFF, "PTFF enhancements introduced with z13"), + FEAT_GROUP_INIT("gen17ptff", GEN17_PTFF, "PTFF enhancements introduced with gen17"), FEAT_GROUP_INIT("msa", MSA, "Message-security-assist facility"), FEAT_GROUP_INIT("msa1", MSA_EXT_1, "Message-security-assist-extension 1 facility"), FEAT_GROUP_INIT("msa2", MSA_EXT_2, "Message-security-assist-extension 2 facility"), @@ -235,9 +255,17 @@ static S390FeatGroupDef s390_feature_groups[] = { FEAT_GROUP_INIT("msa8", MSA_EXT_8, "Message-security-assist-extension 8 facility"), FEAT_GROUP_INIT("msa9", MSA_EXT_9, "Message-security-assist-extension 9 facility"), FEAT_GROUP_INIT("msa9_pckmo", MSA_EXT_9_PCKMO, "Message-security-assist-extension 9 PCKMO subfunctions"), + FEAT_GROUP_INIT("msa10", MSA_EXT_10, "Message-security-assist-extension 10 facility"), + FEAT_GROUP_INIT("msa10_pckmo", MSA_EXT_10_PCKMO, "Message-security-assist-extension 10 PCKMO subfunctions"), + FEAT_GROUP_INIT("msa11", MSA_EXT_11, "Message-security-assist-extension 11 facility"), + FEAT_GROUP_INIT("msa11_pckmo", MSA_EXT_11_PCKMO, "Message-security-assist-extension 11 PCKMO subfunctions"), + FEAT_GROUP_INIT("msa12", MSA_EXT_12, "Message-security-assist-extension 12 facility"), + FEAT_GROUP_INIT("msa13", MSA_EXT_13, "Message-security-assist-extension 13 facility"), + FEAT_GROUP_INIT("msa13_pckmo", MSA_EXT_13_PCKMO, "Message-security-assist-extension 13 PCKMO subfunctions"), FEAT_GROUP_INIT("mepochptff", MULTIPLE_EPOCH_PTFF, "PTFF enhancements introduced with Multiple-epoch facility"), FEAT_GROUP_INIT("esort", ENH_SORT, "Enhanced-sort facility"), FEAT_GROUP_INIT("deflate", DEFLATE_CONVERSION, "Deflate-conversion facility"), + FEAT_GROUP_INIT("ccf", CONCURRENT_FUNCTIONS, "Concurrent-functions facility"), }; const S390FeatGroupDef *s390_feat_group_def(S390FeatGroup group) diff --git a/target/s390x/cpu_features.h b/target/s390x/cpu_features.h index a9bd68a2e11..5635839d032 100644 --- a/target/s390x/cpu_features.h +++ b/target/s390x/cpu_features.h @@ -44,6 +44,7 @@ typedef enum { S390_FEAT_TYPE_SORTL, S390_FEAT_TYPE_DFLTCC, S390_FEAT_TYPE_UV_FEAT_GUEST, + S390_FEAT_TYPE_PFCR, } S390FeatType; /* Definition of a CPU feature */ @@ -69,6 +70,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 e68da9b8ffd..e23e603a792 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -90,6 +90,10 @@ DEF_FEAT(EDAT_2, "edat2", STFL, 78, "Enhanced-DAT facility 2") DEF_FEAT(DFP_PACKED_CONVERSION, "dfppc", STFL, 80, "Decimal-floating-point packed-conversion facility") DEF_FEAT(PPA15, "ppa15", STFL, 81, "PPA15 is installed") DEF_FEAT(BPB, "bpb", STFL, 82, "Branch prediction blocking") +DEF_FEAT(MISC_INSTRUCTION_EXT4, "minste4", STFL, 84, "Miscellaneous-Instruction-Extensions Facility 4") +DEF_FEAT(SIF, "sif", STFL, 85, "Sequential-instruction-fetching facility") +DEF_FEAT(MSA_EXT_12, "msa12-base", STFL, 86, "Message-security-assist-extension-12 facility (excluding subfunctions)") +DEF_FEAT(PLO_EXT, "plo-ext", STFL, 87, "PLO-extension facility") DEF_FEAT(VECTOR, "vx", STFL, 129, "Vector facility") DEF_FEAT(INSTRUCTION_EXEC_PROT, "iep", STFL, 130, "Instruction-execution-protection facility") DEF_FEAT(SIDE_EFFECT_ACCESS_ESOP2, "sea_esop2", STFL, 131, "Side-effect-access facility and Enhanced-suppression-on-protection facility 2") @@ -110,11 +114,15 @@ DEF_FEAT(MSA_EXT_9, "msa9-base", STFL, 155, "Message-security-assist-extension-9 DEF_FEAT(ETOKEN, "etoken", STFL, 156, "Etoken facility") DEF_FEAT(UNPACK, "unpack", STFL, 161, "Unpack facility") DEF_FEAT(NNPA, "nnpa", STFL, 165, "NNPA facility") +DEF_FEAT(INEFF_NC_TX, "ineff_nc_tx", STFL, 170, "Ineffective-nonconstrained-transaction facility") DEF_FEAT(VECTOR_PACKED_DECIMAL_ENH2, "vxpdeh2", STFL, 192, "Vector-Packed-Decimal-Enhancement facility 2") DEF_FEAT(BEAR_ENH, "beareh", STFL, 193, "BEAR-enhancement facility") DEF_FEAT(RDP, "rdp", STFL, 194, "Reset-DAT-protection facility") DEF_FEAT(PAI, "pai", STFL, 196, "Processor-Activity-Instrumentation facility") DEF_FEAT(PAIE, "paie", STFL, 197, "Processor-Activity-Instrumentation extension-1") +DEF_FEAT(VECTOR_ENH3, "vxeh3", STFL, 198, "Vector Enhancements facility 3") +DEF_FEAT(VECTOR_PACKED_DECIMAL_ENH3, "vxpdeh3", STFL, 199, "Vector-Packed-Decimal-Enhancement facility 3") +DEF_FEAT(CCF_BASE, "ccf-base", STFL, 201, "Concurrent-Functions facility") /* Features exposed via SCLP SCCB Byte 80 - 98 (bit numbers relative to byte-80) */ DEF_FEAT(SIE_GSLS, "gsls", SCLP_CONF_CHAR, 40, "SIE: Guest-storage-limit-suppression facility") @@ -142,7 +150,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") @@ -151,28 +159,66 @@ DEF_FEAT(AP, "ap", MISC, 0, "AP instructions installed") /* Features exposed via the PLO instruction. */ DEF_FEAT(PLO_CL, "plo-cl", PLO, 0, "PLO Compare and load (32 bit in general registers)") DEF_FEAT(PLO_CLG, "plo-clg", PLO, 1, "PLO Compare and load (64 bit in parameter list)") -DEF_FEAT(PLO_CLGR, "plo-clgr", PLO, 2, "PLO Compare and load (32 bit in general registers)") +DEF_FEAT(PLO_CLGR, "plo-clgr", PLO, 2, "PLO Compare and load (64 bit in general registers)") DEF_FEAT(PLO_CLX, "plo-clx", PLO, 3, "PLO Compare and load (128 bit in parameter list)") DEF_FEAT(PLO_CS, "plo-cs", PLO, 4, "PLO Compare and swap (32 bit in general registers)") DEF_FEAT(PLO_CSG, "plo-csg", PLO, 5, "PLO Compare and swap (64 bit in parameter list)") -DEF_FEAT(PLO_CSGR, "plo-csgr", PLO, 6, "PLO Compare and swap (32 bit in general registers)") +DEF_FEAT(PLO_CSGR, "plo-csgr", PLO, 6, "PLO Compare and swap (64 bit in general registers)") DEF_FEAT(PLO_CSX, "plo-csx", PLO, 7, "PLO Compare and swap (128 bit in parameter list)") DEF_FEAT(PLO_DCS, "plo-dcs", PLO, 8, "PLO Double compare and swap (32 bit in general registers)") DEF_FEAT(PLO_DCSG, "plo-dcsg", PLO, 9, "PLO Double compare and swap (64 bit in parameter list)") -DEF_FEAT(PLO_DCSGR, "plo-dcsgr", PLO, 10, "PLO Double compare and swap (32 bit in general registers)") +DEF_FEAT(PLO_DCSGR, "plo-dcsgr", PLO, 10, "PLO Double compare and swap (64 bit in general registers)") DEF_FEAT(PLO_DCSX, "plo-dcsx", PLO, 11, "PLO Double compare and swap (128 bit in parameter list)") DEF_FEAT(PLO_CSST, "plo-csst", PLO, 12, "PLO Compare and swap and store (32 bit in general registers)") DEF_FEAT(PLO_CSSTG, "plo-csstg", PLO, 13, "PLO Compare and swap and store (64 bit in parameter list)") -DEF_FEAT(PLO_CSSTGR, "plo-csstgr", PLO, 14, "PLO Compare and swap and store (32 bit in general registers)") +DEF_FEAT(PLO_CSSTGR, "plo-csstgr", PLO, 14, "PLO Compare and swap and store (64 bit in general registers)") DEF_FEAT(PLO_CSSTX, "plo-csstx", PLO, 15, "PLO Compare and swap and store (128 bit in parameter list)") DEF_FEAT(PLO_CSDST, "plo-csdst", PLO, 16, "PLO Compare and swap and double store (32 bit in general registers)") DEF_FEAT(PLO_CSDSTG, "plo-csdstg", PLO, 17, "PLO Compare and swap and double store (64 bit in parameter list)") -DEF_FEAT(PLO_CSDSTGR, "plo-csdstgr", PLO, 18, "PLO Compare and swap and double store (32 bit in general registers)") +DEF_FEAT(PLO_CSDSTGR, "plo-csdstgr", PLO, 18, "PLO Compare and swap and double store (64 bit in general registers)") DEF_FEAT(PLO_CSDSTX, "plo-csdstx", PLO, 19, "PLO Compare and swap and double store (128 bit in parameter list)") DEF_FEAT(PLO_CSTST, "plo-cstst", PLO, 20, "PLO Compare and swap and triple store (32 bit in general registers)") DEF_FEAT(PLO_CSTSTG, "plo-cststg", PLO, 21, "PLO Compare and swap and triple store (64 bit in parameter list)") -DEF_FEAT(PLO_CSTSTGR, "plo-cststgr", PLO, 22, "PLO Compare and swap and triple store (32 bit in general registers)") +DEF_FEAT(PLO_CSTSTGR, "plo-cststgr", PLO, 22, "PLO Compare and swap and triple store (64 bit in general registers)") DEF_FEAT(PLO_CSTSTX, "plo-cststx", PLO, 23, "PLO Compare and swap and triple store (128 bit in parameter list)") +DEF_FEAT(PLO_CLO, "plo-clo", PLO, 24, "PLO Compare and load (256 bit in parameter list)") +DEF_FEAT(PLO_CSO, "plo-cso", PLO, 25, "PLO Compare and swap (256 bit in parameter list)") +DEF_FEAT(PLO_DCSO, "plo-dcso", PLO, 26, "PLO Double compare and swap (256 bit in parameter list)") +DEF_FEAT(PLO_CSSTO, "plo-cssto", PLO, 27, "PLO Compare and swap and store (256 bit in parameter list)") +DEF_FEAT(PLO_CSDSTO, "plo-csdsto", PLO, 28, "PLO Compare and swap and double store (256 bit in parameter list)") +DEF_FEAT(PLO_CSTSTO, "plo-cststo", PLO, 29, "PLO Compare and swap and trible store (256 bit in parameter list)") +DEF_FEAT(PLO_TCS, "plo-tcs", PLO, 30, "Triple compare and swap (32 bit in parameter list)") +DEF_FEAT(PLO_TCSG, "plo-tcsg", PLO, 31, "Triple compare and swap (64 bit in parameter list)") +DEF_FEAT(PLO_TCSX, "plo-tcsx", PLO, 32, "Triple compare and swap (128 bit in parameter list)") +DEF_FEAT(PLO_TCSO, "plo-tcso", PLO, 33, "Triple compare and swap (256 bit in parameter list)") +DEF_FEAT(PLO_QCS, "plo-qcs", PLO, 34, "Quadruple compare and swap (32 bit in parameter list)") +DEF_FEAT(PLO_QCSG, "plo-qcsg", PLO, 35, "Quadruple compare and swap (64 bit in parameter list)") +DEF_FEAT(PLO_QCSX, "plo-qcsx", PLO, 36, "Quadruple compare and swap (128 bit in parameter list)") +DEF_FEAT(PLO_QCSO, "plo-qcso", PLO, 37, "Quadruple compare and swap (256 bit in parameter list)") +DEF_FEAT(PLO_LO, "plo-lo", PLO, 38, "Load (256 bit in parameter list)") +DEF_FEAT(PLO_DLX, "plo-dlx", PLO, 39, "Double load (128 bit in parameter list)") +DEF_FEAT(PLO_DLO, "plo-dlo", PLO, 40, "Double load (256 bit in parameter list)") +DEF_FEAT(PLO_TL, "plo-tl", PLO, 41, "Triple load (32 bit in parameter list)") +DEF_FEAT(PLO_TLG, "plo-tlg", PLO, 42, "Triple load (64 bit in parameter list)") +DEF_FEAT(PLO_TLX, "plo-tlx", PLO, 43, "Triple load (128 bit in parameter list)") +DEF_FEAT(PLO_TLO, "plo-tlo", PLO, 44, "Triple load (256 bit in parameter list)") +DEF_FEAT(PLO_QL, "plo-ql", PLO, 45, "Quadruple load (32 bit in parameter list)") +DEF_FEAT(PLO_QLG, "plo-qlg", PLO, 46, "Quadruple load (64 bit in parameter list)") +DEF_FEAT(PLO_QLX, "plo-qlx", PLO, 47, "Quadruple load (128 bit in parameter list)") +DEF_FEAT(PLO_QLO, "plo-qlo", PLO, 48, "Quadruple load (256 bit in parameter list)") +DEF_FEAT(PLO_STO, "plo-sto", PLO, 49, "Store (256 bit in parameter list)") +DEF_FEAT(PLO_DST, "plo-dst", PLO, 50, "Double store (32 bit in parameter list)") +DEF_FEAT(PLO_DSTG, "plo-dstg", PLO, 51, "Double store (64 bit in parameter list)") +DEF_FEAT(PLO_DSTX, "plo-dstx", PLO, 52, "Double store (128 bit in parameter list)") +DEF_FEAT(PLO_DSTO, "plo-dsto", PLO, 53, "Double store (256 bit in parameter list)") +DEF_FEAT(PLO_TST, "plo-tst", PLO, 54, "Triple store (32 bit in parameter list)") +DEF_FEAT(PLO_TSTG, "plo-tstg", PLO, 55, "Triple store (64 bit in parameter list)") +DEF_FEAT(PLO_TSTX, "plo-tstx", PLO, 56, "Triple store (128 bit in parameter list)") +DEF_FEAT(PLO_TSTO, "plo-tsto", PLO, 57, "Triple store (256 bit in parameter list)") +DEF_FEAT(PLO_QST, "plo-qst", PLO, 58, "Quadruple store (32 bit in parameter list)") +DEF_FEAT(PLO_QSTG, "plo-qstg", PLO, 59, "Quadruple store (64 bit in parameter list)") +DEF_FEAT(PLO_QSTX, "plo-qstx", PLO, 60, "Quadruple store (128 bit in parameter list)") +DEF_FEAT(PLO_QSTO, "plo-qsto", PLO, 61, "Quadruple store (256 bit in parameter list)") /* Features exposed via the PTFF instruction. */ DEF_FEAT(PTFF_QTO, "ptff-qto", PTFF, 1, "PTFF Query TOD Offset") @@ -180,6 +226,7 @@ DEF_FEAT(PTFF_QSI, "ptff-qsi", PTFF, 2, "PTFF Query Steering Information") DEF_FEAT(PTFF_QPT, "ptff-qpc", PTFF, 3, "PTFF Query Physical Clock") DEF_FEAT(PTFF_QUI, "ptff-qui", PTFF, 4, "PTFF Query UTC Information") DEF_FEAT(PTFF_QTOU, "ptff-qtou", PTFF, 5, "PTFF Query TOD Offset User") +DEF_FEAT(PTFF_QTSE, "ptff-qtse", PTFF, 6, "PTFF Query Time-Stamp Event") DEF_FEAT(PTFF_QSIE, "ptff-qsie", PTFF, 10, "PTFF Query Steering Information Extended") DEF_FEAT(PTFF_QTOUE, "ptff-qtoue", PTFF, 13, "PTFF Query TOD Offset User Extended") DEF_FEAT(PTFF_STO, "ptff-sto", PTFF, 65, "PTFF Set TOD Offset") @@ -200,6 +247,15 @@ DEF_FEAT(KMAC_AES_256, "kmac-aes-256", KMAC, 20, "KMAC AES-256") DEF_FEAT(KMAC_EAES_128, "kmac-eaes-128", KMAC, 26, "KMAC Encrypted-AES-128") DEF_FEAT(KMAC_EAES_192, "kmac-eaes-192", KMAC, 27, "KMAC Encrypted-AES-192") DEF_FEAT(KMAC_EAES_256, "kmac-eaes-256", KMAC, 28, "KMAC Encrypted-AES-256") +DEF_FEAT(KMAC_HMAC_SHA_224, "kmac-hmac-sha-224", KMAC, 112, "KMAC HMAC-SHA-224") +DEF_FEAT(KMAC_HMAC_SHA_256, "kmac-hmac-sha-246", KMAC, 113, "KMAC HMAC-SHA-256") +DEF_FEAT(KMAC_HMAC_SHA_384, "kmac-hmac-sha-384", KMAC, 114, "KMAC HMAC-SHA-384") +DEF_FEAT(KMAC_HMAC_SHA_512, "kmac-hmac-sha-512", KMAC, 115, "KMAC HMAC-SHA-512") +DEF_FEAT(KMAC_HMAC_ESHA_224, "kmac-hmac-esha-224", KMAC, 120, "KMAC HMAC-Encrypted-SHA-224") +DEF_FEAT(KMAC_HMAC_ESHA_256, "kmac-hmac-esha-246", KMAC, 121, "KMAC HMAC-Encrypted-SHA-256") +DEF_FEAT(KMAC_HMAC_ESHA_384, "kmac-hmac-esha-384", KMAC, 122, "KMAC HMAC-Encrypted-SHA-384") +DEF_FEAT(KMAC_HMAC_ESHA_512, "kmac-hmac-esha-512", KMAC, 123, "KMAC HMAC-Encrypted-SHA-512") +DEF_FEAT(KMAC_QAI, "kmac-qai", KMAC, 127, "KMAC Query-Authentication-Information") /* Features exposed via the KMC instruction. */ DEF_FEAT(KMC_DEA, "kmc-dea", KMC, 1, "KMC DEA") @@ -233,6 +289,11 @@ DEF_FEAT(KM_XTS_AES_128, "km-xts-aes-128", KM, 50, "KM XTS-AES-128") DEF_FEAT(KM_XTS_AES_256, "km-xts-aes-256", KM, 52, "KM XTS-AES-256") DEF_FEAT(KM_XTS_EAES_128, "km-xts-eaes-128", KM, 58, "KM XTS-Encrypted-AES-128") DEF_FEAT(KM_XTS_EAES_256, "km-xts-eaes-256", KM, 60, "KM XTS-Encrypted-AES-256") +DEF_FEAT(KM_FULL_XTS_AES_128, "km-full-xts-aes-128", KM, 82, "KM Full-XTS-AES-128") +DEF_FEAT(KM_FULL_XTS_AES_256, "km-full-xts-aes-256", KM, 84, "KM Full-XTS-AES-256") +DEF_FEAT(KM_FULL_XTS_EAES_128, "km-full-xts-eaes-128", KM, 90, "KM Full-XTS-Encrypted-AES-128") +DEF_FEAT(KM_FULL_XTS_EAES_256, "km-full-xts-eaes-256", KM, 92, "KM Full-XTS-Encrypted-AES-256") +DEF_FEAT(KM_QAI, "km-qai", KM, 127, "KM Query-Authentication-Information") /* Features exposed via the KIMD instruction. */ DEF_FEAT(KIMD_SHA_1, "kimd-sha-1", KIMD, 1, "KIMD SHA-1") @@ -245,6 +306,7 @@ DEF_FEAT(KIMD_SHA3_512, "kimd-sha3-512", KIMD, 35, "KIMD SHA3-512") DEF_FEAT(KIMD_SHAKE_128, "kimd-shake-128", KIMD, 36, "KIMD SHAKE-128") DEF_FEAT(KIMD_SHAKE_256, "kimd-shake-256", KIMD, 37, "KIMD SHAKE-256") DEF_FEAT(KIMD_GHASH, "kimd-ghash", KIMD, 65, "KIMD GHASH") +DEF_FEAT(KIMD_QAI, "kimd-qai", KIMD, 127, "KIMD Query-Authentication-Information") /* Features exposed via the KLMD instruction. */ DEF_FEAT(KLMD_SHA_1, "klmd-sha-1", KLMD, 1, "KLMD SHA-1") @@ -256,6 +318,7 @@ DEF_FEAT(KLMD_SHA3_384, "klmd-sha3-384", KLMD, 34, "KLMD SHA3-384") DEF_FEAT(KLMD_SHA3_512, "klmd-sha3-512", KLMD, 35, "KLMD SHA3-512") DEF_FEAT(KLMD_SHAKE_128, "klmd-shake-128", KLMD, 36, "KLMD SHAKE-128") DEF_FEAT(KLMD_SHAKE_256, "klmd-shake-256", KLMD, 37, "KLMD SHAKE-256") +DEF_FEAT(KLMD_QAI, "klmd-qai", KLMD, 127, "KLMD Query-Authentication-Information") /* Features exposed via the PCKMO instruction. */ DEF_FEAT(PCKMO_EDEA, "pckmo-edea", PCKMO, 1, "PCKMO Encrypted-DEA-Key") @@ -264,11 +327,16 @@ DEF_FEAT(PCKMO_ETDEA_256, "pckmo-etdea-192", PCKMO, 3, "PCKMO Encrypted-TDEA-192 DEF_FEAT(PCKMO_AES_128, "pckmo-aes-128", PCKMO, 18, "PCKMO Encrypted-AES-128-Key") DEF_FEAT(PCKMO_AES_192, "pckmo-aes-192", PCKMO, 19, "PCKMO Encrypted-AES-192-Key") DEF_FEAT(PCKMO_AES_256, "pckmo-aes-256", PCKMO, 20, "PCKMO Encrypted-AES-256-Key") +DEF_FEAT(PCKMO_AES_XTS_128_DK, "pckmo-aes-xts-128-dk", PCKMO, 21, "PCKMO Encrypt-AES-XTS-128-Double-Key") +DEF_FEAT(PCKMO_AES_XTS_256_DK, "pckmo-aes-xts-256-dk", PCKMO, 22, "PCKMO Encrypt-AES-XTS-256-Double-Key") DEF_FEAT(PCKMO_ECC_P256, "pckmo-ecc-p256", PCKMO, 32, "PCKMO Encrypt-ECC-P256-Key") DEF_FEAT(PCKMO_ECC_P384, "pckmo-ecc-p384", PCKMO, 33, "PCKMO Encrypt-ECC-P384-Key") DEF_FEAT(PCKMO_ECC_P521, "pckmo-ecc-p521", PCKMO, 34, "PCKMO Encrypt-ECC-P521-Key") DEF_FEAT(PCKMO_ECC_ED25519, "pckmo-ecc-ed25519", PCKMO, 40 , "PCKMO Encrypt-ECC-Ed25519-Key") DEF_FEAT(PCKMO_ECC_ED448, "pckmo-ecc-ed448", PCKMO, 41 , "PCKMO Encrypt-ECC-Ed448-Key") +DEF_FEAT(PCKMO_HMAC_512, "pckmo-hmac-512", PCKMO, 118, "PCKMO Encrypt-HMAC-512-Key") +DEF_FEAT(PCKMO_HMAC_1024, "pckmo-hmac-1024", PCKMO, 122, "PCKMO Encrypt-HMAC-1024-Key") +DEF_FEAT(PCKMO_QAI, "pckmo-qai", PCKMO, 127, "PCKMO Query-Authentication-Information") /* Features exposed via the KMCTR instruction. */ DEF_FEAT(KMCTR_DEA, "kmctr-dea", KMCTR, 1, "KMCTR DEA") @@ -283,6 +351,7 @@ DEF_FEAT(KMCTR_AES_256, "kmctr-aes-256", KMCTR, 20, "KMCTR AES-256") DEF_FEAT(KMCTR_EAES_128, "kmctr-eaes-128", KMCTR, 26, "KMCTR Encrypted-AES-128") DEF_FEAT(KMCTR_EAES_192, "kmctr-eaes-192", KMCTR, 27, "KMCTR Encrypted-AES-192") DEF_FEAT(KMCTR_EAES_256, "kmctr-eaes-256", KMCTR, 28, "KMCTR Encrypted-AES-256") +DEF_FEAT(KMCTR_QAI, "kmctr-qai", KMCTR, 127, "KMCTR Query-Authentication-Information") /* Features exposed via the KMF instruction. */ DEF_FEAT(KMF_DEA, "kmf-dea", KMF, 1, "KMF DEA") @@ -297,6 +366,7 @@ DEF_FEAT(KMF_AES_256, "kmf-aes-256", KMF, 20, "KMF AES-256") DEF_FEAT(KMF_EAES_128, "kmf-eaes-128", KMF, 26, "KMF Encrypted-AES-128") DEF_FEAT(KMF_EAES_192, "kmf-eaes-192", KMF, 27, "KMF Encrypted-AES-192") DEF_FEAT(KMF_EAES_256, "kmf-eaes-256", KMF, 28, "KMF Encrypted-AES-256") +DEF_FEAT(KMF_QAI, "kmf-qai", KMF, 127, "KMF Query-Authentication-Information") /* Features exposed via the KMO instruction. */ DEF_FEAT(KMO_DEA, "kmo-dea", KMO, 1, "KMO DEA") @@ -311,6 +381,7 @@ DEF_FEAT(KMO_AES_256, "kmo-aes-256", KMO, 20, "KMO AES-256") DEF_FEAT(KMO_EAES_128, "kmo-eaes-128", KMO, 26, "KMO Encrypted-AES-128") DEF_FEAT(KMO_EAES_192, "kmo-eaes-192", KMO, 27, "KMO Encrypted-AES-192") DEF_FEAT(KMO_EAES_256, "kmo-eaes-256", KMO, 28, "KMO Encrypted-AES-256") +DEF_FEAT(KMO_QAI, "kmo-qai", KMO, 127, "KMO Query-Authentication-Information") /* Features exposed via the PCC instruction. */ DEF_FEAT(PCC_CMAC_DEA, "pcc-cmac-dea", PCC, 1, "PCC Compute-Last-Block-CMAC-Using-DEA") @@ -336,11 +407,13 @@ DEF_FEAT(PCC_SCALAR_MULT_ED25519, "pcc-scalar-mult-ed25519", PCC, 72, "PCC Scala DEF_FEAT(PCC_SCALAR_MULT_ED448, "pcc-scalar-mult-ed448", PCC, 73, "PCC Scalar-Multiply-Ed448") DEF_FEAT(PCC_SCALAR_MULT_X25519, "pcc-scalar-mult-x25519", PCC, 80, "PCC Scalar-Multiply-X25519") DEF_FEAT(PCC_SCALAR_MULT_X448, "pcc-scalar-mult-x448", PCC, 81, "PCC Scalar-Multiply-X448") +DEF_FEAT(PCC_QAI, "pcc-qai", PCC, 127, "PCC Query-Authentication-Information") /* Features exposed via the PPNO/PRNO instruction. */ DEF_FEAT(PPNO_SHA_512_DRNG, "ppno-sha-512-drng", PPNO, 3, "PPNO SHA-512-DRNG") DEF_FEAT(PRNO_TRNG_QRTCR, "prno-trng-qrtcr", PPNO, 112, "PRNO TRNG-Query-Raw-to-Conditioned-Ratio") DEF_FEAT(PRNO_TRNG, "prno-trng", PPNO, 114, "PRNO TRNG") +DEF_FEAT(PRNO_QAI, "prno-qai", PPNO, 127, "PRNO Query-Authentication-Information") /* Features exposed via the KMA instruction. */ DEF_FEAT(KMA_GCM_AES_128, "kma-gcm-aes-128", KMA, 18, "KMA GCM-AES-128") @@ -349,6 +422,7 @@ DEF_FEAT(KMA_GCM_AES_256, "kma-gcm-aes-256", KMA, 20, "KMA GCM-AES-256") DEF_FEAT(KMA_GCM_EAES_128, "kma-gcm-eaes-128", KMA, 26, "KMA GCM-Encrypted-AES-128") DEF_FEAT(KMA_GCM_EAES_192, "kma-gcm-eaes-192", KMA, 27, "KMA GCM-Encrypted-AES-192") DEF_FEAT(KMA_GCM_EAES_256, "kma-gcm-eaes-256", KMA, 28, "KMA GCM-Encrypted-AES-256") +DEF_FEAT(KMA_QAI, "kma-qai", KMA, 127, "KMA Query-Authentication-Information") /* Features exposed via the KDSA instruction. */ DEF_FEAT(KDSA_ECDSA_VERIFY_P256, "kdsa-ecdsa-verify-p256", KDSA, 1, "KDSA ECDSA-Verify-P256") @@ -366,6 +440,7 @@ DEF_FEAT(KDSA_EDDSA_SIGN_ED25519, "kdsa-eddsa-sign-ed25519", KDSA, 40, "KDSA EdD DEF_FEAT(KDSA_EDDSA_SIGN_ED448, "kdsa-eddsa-sign-ed448", KDSA, 44, "KDSA EdDSA-Sign-Ed448") DEF_FEAT(KDSA_EEDDSA_SIGN_ED25519, "kdsa-eeddsa-sign-ed25519", KDSA, 48, "KDSA Encrypted-EdDSA-Sign-Ed25519") DEF_FEAT(KDSA_EEDDSA_SIGN_ED448, "kdsa-eeddsa-sign-ed448", KDSA, 52, "KDSA Encrypted-EdDSA-Sign-Ed448") +DEF_FEAT(KDSA_QAI, "kdsa-qai", KDSA, 127, "KDSA Query-Authentication-Information") /* Features exposed via the SORTL instruction. */ DEF_FEAT(SORTL_SFLR, "sortl-sflr", SORTL, 1, "SORTL SFLR") @@ -383,3 +458,10 @@ 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") + +/* Features exposed via the PFCR instruction (concurrent-functions facility). */ +DEF_FEAT(PFCR_QAF, "pfcr-qaf", PFCR, 0, "PFCR Query-Available-Functions") +DEF_FEAT(PFCR_CSDST, "pfcr-csdst", PFCR, 1, "PFCR Compare-and-Swap-and-Double-Store (32)") +DEF_FEAT(PFCR_CSDSTG, "pfcr-csdstg", PFCR, 2, "PFCR Compare-and-Swap-and-Double-Store (64)") +DEF_FEAT(PFCR_CSTST, "pfcr-cstst", PFCR, 3, "PFCR Compare-and-Swap-and-Triple-Store (32)") +DEF_FEAT(PFCR_CSTSTG, "pfcr-cststg", PFCR, 4, "PFCR Compare-and-Swap-and-Triple-Store (64)") diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 98f14c09c21..954a7a99a9e 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -14,8 +14,8 @@ #include "cpu.h" #include "s390x-internal.h" #include "kvm/kvm_s390x.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qapi/visitor.h" @@ -23,8 +23,9 @@ #include "qemu/hw-version.h" #include "qemu/qemu-print.h" #ifndef CONFIG_USER_ONLY -#include "sysemu/sysemu.h" +#include "system/system.h" #include "target/s390x/kvm/pv.h" +#include CONFIG_DEVICES #endif #define CPUDEF_INIT(_type, _gen, _ec_ga, _mha_pow, _hmfai, _name, _desc) \ @@ -47,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"), @@ -64,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"), @@ -85,6 +94,8 @@ static S390CPUDef s390_cpu_defs[] = { CPUDEF_INIT(0x8562, 15, 1, 47, 0x08000000U, "gen15b", "IBM z15 T02 GA1"), CPUDEF_INIT(0x3931, 16, 1, 47, 0x08000000U, "gen16a", "IBM 3931 GA1"), CPUDEF_INIT(0x3932, 16, 1, 47, 0x08000000U, "gen16b", "IBM 3932 GA1"), + CPUDEF_INIT(0x9175, 17, 1, 47, 0x08000000U, "gen17a", "IBM 9175 GA1"), + CPUDEF_INIT(0x9176, 17, 1, 47, 0x08000000U, "gen17b", "IBM 9176 GA1"), }; #define QEMU_MAX_CPU_TYPE 0x8561 @@ -196,11 +207,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; @@ -255,6 +262,7 @@ 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: @@ -358,14 +366,14 @@ 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); } -static gint s390_cpu_list_compare(gconstpointer a, gconstpointer b) +static gint s390_cpu_list_compare(gconstpointer a, gconstpointer b, gpointer d) { const S390CPUClass *cc_a = S390_CPU_CLASS((ObjectClass *)a); const S390CPUClass *cc_b = S390_CPU_CLASS((ObjectClass *)b); @@ -405,8 +413,9 @@ 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); + list = g_slist_sort_with_data(list, s390_cpu_list_compare, NULL); g_slist_foreach(list, s390_print_cpu_model_list_entry, NULL); g_slist_free(list); @@ -414,14 +423,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); } } @@ -450,7 +459,10 @@ static void check_consistency(const S390CPUModel *model) { S390_FEAT_VECTOR_PACKED_DECIMAL, S390_FEAT_VECTOR }, { S390_FEAT_VECTOR_PACKED_DECIMAL_ENH, S390_FEAT_VECTOR_PACKED_DECIMAL }, { S390_FEAT_VECTOR_PACKED_DECIMAL_ENH2, S390_FEAT_VECTOR_PACKED_DECIMAL_ENH }, + { S390_FEAT_VECTOR_PACKED_DECIMAL_ENH3, S390_FEAT_VECTOR_PACKED_DECIMAL_ENH2 }, { S390_FEAT_VECTOR_ENH, S390_FEAT_VECTOR }, + { S390_FEAT_VECTOR_ENH2, S390_FEAT_VECTOR_ENH }, + { S390_FEAT_VECTOR_ENH3, S390_FEAT_VECTOR_ENH2 }, { S390_FEAT_INSTRUCTION_EXEC_PROT, S390_FEAT_SIDE_EFFECT_ACCESS_ESOP2 }, { S390_FEAT_SIDE_EFFECT_ACCESS_ESOP2, S390_FEAT_ESOP }, { S390_FEAT_CMM_NT, S390_FEAT_CMM }, @@ -470,6 +482,18 @@ static void check_consistency(const S390CPUModel *model) { S390_FEAT_KLMD_SHA3_512, S390_FEAT_MSA }, { S390_FEAT_KLMD_SHAKE_128, S390_FEAT_MSA }, { S390_FEAT_KLMD_SHAKE_256, S390_FEAT_MSA }, + { S390_FEAT_KMAC_HMAC_SHA_224, S390_FEAT_MSA_EXT_3 }, + { S390_FEAT_KMAC_HMAC_SHA_256, S390_FEAT_MSA_EXT_3 }, + { S390_FEAT_KMAC_HMAC_SHA_384, S390_FEAT_MSA_EXT_3 }, + { S390_FEAT_KMAC_HMAC_SHA_512, S390_FEAT_MSA_EXT_3 }, + { S390_FEAT_KMAC_HMAC_ESHA_224, S390_FEAT_MSA_EXT_3 }, + { S390_FEAT_KMAC_HMAC_ESHA_256, S390_FEAT_MSA_EXT_3 }, + { S390_FEAT_KMAC_HMAC_ESHA_384, S390_FEAT_MSA_EXT_3 }, + { S390_FEAT_KMAC_HMAC_ESHA_512, S390_FEAT_MSA_EXT_3 }, + { S390_FEAT_KM_FULL_XTS_AES_128, S390_FEAT_MSA_EXT_4 }, + { S390_FEAT_KM_FULL_XTS_AES_256, S390_FEAT_MSA_EXT_4 }, + { S390_FEAT_KM_FULL_XTS_EAES_128, S390_FEAT_MSA_EXT_4 }, + { S390_FEAT_KM_FULL_XTS_EAES_256, S390_FEAT_MSA_EXT_4 }, { S390_FEAT_PRNO_TRNG_QRTCR, S390_FEAT_MSA_EXT_5 }, { S390_FEAT_PRNO_TRNG, S390_FEAT_MSA_EXT_5 }, { S390_FEAT_SIE_KSS, S390_FEAT_SIE_F2 }, @@ -485,6 +509,50 @@ static void check_consistency(const S390CPUModel *model) { 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 }, + { S390_FEAT_PFCR_QAF, S390_FEAT_CCF_BASE }, + { S390_FEAT_PFCR_CSDST, S390_FEAT_CCF_BASE }, + { S390_FEAT_PFCR_CSDSTG, S390_FEAT_CCF_BASE }, + { S390_FEAT_PFCR_CSTST, S390_FEAT_CCF_BASE }, + { S390_FEAT_PFCR_CSTSTG, S390_FEAT_CCF_BASE }, + { S390_FEAT_INEFF_NC_TX, S390_FEAT_TRANSACTIONAL_EXE }, + { S390_FEAT_PLO_CLO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_CSO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_DCSO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_CSSTO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_CSDSTO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_CSTSTO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TCS, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TCSG, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TCSX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TCSO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QCS, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QCSG, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QCSX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QCSO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_LO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_DLX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_DLO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TL, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TLG, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TLX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TLO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QL, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QLG, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QLX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QLO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_STO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_DST, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_DSTG, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_DSTX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_DSTO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TST, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TSTG, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TSTX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_TSTO, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QST, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QSTG, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QSTX, S390_FEAT_PLO_EXT }, + { S390_FEAT_PLO_QSTO, S390_FEAT_PLO_EXT }, }; int i; @@ -503,22 +571,28 @@ 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"); +} + +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 @@ -526,25 +600,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; @@ -553,23 +629,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; @@ -598,9 +672,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; } @@ -756,7 +828,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; @@ -847,7 +919,7 @@ void s390_cpu_model_class_register_props(ObjectClass *oc) } #ifdef CONFIG_KVM -static void s390_host_cpu_model_class_init(ObjectClass *oc, void *data) +static void s390_host_cpu_model_class_init(ObjectClass *oc, const void *data) { S390CPUClass *xcc = S390_CPU_CLASS(oc); @@ -856,7 +928,7 @@ static void s390_host_cpu_model_class_init(ObjectClass *oc, void *data) } #endif -static void s390_base_cpu_model_class_init(ObjectClass *oc, void *data) +static void s390_base_cpu_model_class_init(ObjectClass *oc, const void *data) { S390CPUClass *xcc = S390_CPU_CLASS(oc); @@ -867,7 +939,7 @@ static void s390_base_cpu_model_class_init(ObjectClass *oc, void *data) xcc->desc = xcc->cpu_def->desc; } -static void s390_cpu_model_class_init(ObjectClass *oc, void *data) +static void s390_cpu_model_class_init(ObjectClass *oc, const void *data) { S390CPUClass *xcc = S390_CPU_CLASS(oc); @@ -877,7 +949,7 @@ static void s390_cpu_model_class_init(ObjectClass *oc, void *data) xcc->desc = xcc->cpu_def->desc; } -static void s390_qemu_cpu_model_class_init(ObjectClass *oc, void *data) +static void s390_qemu_cpu_model_class_init(ObjectClass *oc, const void *data) { S390CPUClass *xcc = S390_CPU_CLASS(oc); @@ -886,7 +958,7 @@ static void s390_qemu_cpu_model_class_init(ObjectClass *oc, void *data) qemu_hw_version()); } -static void s390_max_cpu_model_class_init(ObjectClass *oc, void *data) +static void s390_max_cpu_model_class_init(ObjectClass *oc, const void *data) { S390CPUClass *xcc = S390_CPU_CLASS(oc); @@ -1000,7 +1072,7 @@ static void register_types(void) .instance_init = s390_cpu_model_initfn, .instance_finalize = s390_cpu_model_finalize, .class_init = s390_base_cpu_model_class_init, - .class_data = (void *) &s390_cpu_defs[i], + .class_data = &s390_cpu_defs[i], }; char *name = s390_cpu_type_name(s390_cpu_defs[i].name); TypeInfo ti = { @@ -1009,7 +1081,7 @@ static void register_types(void) .instance_init = s390_cpu_model_initfn, .instance_finalize = s390_cpu_model_finalize, .class_init = s390_cpu_model_class_init, - .class_data = (void *) &s390_cpu_defs[i], + .class_data = &s390_cpu_defs[i], }; type_register_static(&ti_base); diff --git a/target/s390x/cpu_models.h b/target/s390x/cpu_models.h index cc7305ec213..f701bc0b532 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 @@ -113,24 +113,12 @@ 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); +void s390_set_qemu_cpu_model(uint16_t type, uint8_t gen, uint8_t ec_ga, + const S390FeatInit feat_init); +void s390_cpu_list(void); -#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 deleted file mode 100644 index 63981bf36b6..00000000000 --- a/target/s390x/cpu_models_sysemu.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * CPU models for s390x - System Emulation-only - * - * Copyright 2016 IBM Corp. - * - * Author(s): David Hildenbrand - * - * 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 "cpu.h" -#include "s390x-internal.h" -#include "kvm/kvm_s390x.h" -#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" - -static void list_add_feat(const char *name, void *opaque); - -static void check_unavailable_features(const S390CPUModel *max_model, - const S390CPUModel *model, - strList **unavailable) -{ - S390FeatBitmap missing; - - /* check general model compatibility */ - if (max_model->def->gen < model->def->gen || - (max_model->def->gen == model->def->gen && - max_model->def->ec_ga < model->def->ec_ga)) { - list_add_feat("type", unavailable); - } - - /* detect missing features if any to properly report them */ - bitmap_andnot(missing, model->features, max_model->features, - S390_FEAT_MAX); - if (!bitmap_empty(missing, S390_FEAT_MAX)) { - s390_feat_bitmap_to_ascii(missing, unavailable, list_add_feat); - } -} - -struct CpuDefinitionInfoListData { - CpuDefinitionInfoList *list; - S390CPUModel *model; -}; - -static void create_cpu_model_list(ObjectClass *klass, void *opaque) -{ - struct CpuDefinitionInfoListData *cpu_list_data = opaque; - CpuDefinitionInfoList **cpu_list = &cpu_list_data->list; - CpuDefinitionInfo *info; - char *name = g_strdup(object_class_get_name(klass)); - S390CPUClass *scc = S390_CPU_CLASS(klass); - - /* strip off the -s390x-cpu */ - g_strrstr(name, "-" TYPE_S390_CPU)[0] = 0; - info = g_new0(CpuDefinitionInfo, 1); - info->name = name; - info->has_migration_safe = true; - info->migration_safe = scc->is_migration_safe; - info->q_static = scc->is_static; - info->q_typename = g_strdup(object_class_get_name(klass)); - /* check for unavailable features */ - if (cpu_list_data->model) { - Object *obj; - S390CPU *sc; - obj = object_new_with_class(klass); - sc = S390_CPU(obj); - if (sc->model) { - info->has_unavailable_features = true; - check_unavailable_features(cpu_list_data->model, sc->model, - &info->unavailable_features); - } - object_unref(obj); - } - - QAPI_LIST_PREPEND(*cpu_list, info); -} - -CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) -{ - struct CpuDefinitionInfoListData list_data = { - .list = NULL, - }; - - list_data.model = get_max_cpu_model(NULL); - - object_class_foreach(create_cpu_model_list, TYPE_S390_CPU, false, - &list_data); - - return list_data.list; -} - -static void cpu_model_from_info(S390CPUModel *model, const CpuModelInfo *info, - Error **errp) -{ - Error *err = NULL; - const QDict *qdict = NULL; - const QDictEntry *e; - Visitor *visitor; - 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); - return; - } - if (S390_CPU_CLASS(oc)->kvm_required && !kvm_enabled()) { - error_setg(errp, "The CPU definition '%s' requires KVM", info->name); - return; - } - obj = object_new_with_class(oc); - cpu = S390_CPU(obj); - - if (!cpu->model) { - error_setg(errp, "Details about the host CPU model are not available, " - "it cannot be used."); - object_unref(obj); - return; - } - - if (qdict) { - visitor = qobject_input_visitor_new(info->props); - if (!visit_start_struct(visitor, NULL, NULL, 0, errp)) { - visit_free(visitor); - object_unref(obj); - return; - } - for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { - if (!object_property_set(obj, e->key, visitor, &err)) { - break; - } - } - if (!err) { - visit_check_struct(visitor, &err); - } - visit_end_struct(visitor, NULL); - visit_free(visitor); - if (err) { - error_propagate(errp, err); - object_unref(obj); - return; - } - } - - /* copy the model and throw the cpu away */ - memcpy(model, cpu->model, sizeof(*model)); - object_unref(obj); -} - -static void qdict_add_disabled_feat(const char *name, void *opaque) -{ - qdict_put_bool(opaque, name, false); -} - -static void qdict_add_enabled_feat(const char *name, void *opaque) -{ - qdict_put_bool(opaque, name, true); -} - -/* convert S390CPUDef into a static CpuModelInfo */ -static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, - bool delta_changes) -{ - QDict *qdict = qdict_new(); - S390FeatBitmap bitmap; - - /* always fallback to the static base model */ - info->name = g_strdup_printf("%s-base", model->def->name); - - if (delta_changes) { - /* features deleted from the base feature set */ - bitmap_andnot(bitmap, model->def->base_feat, model->features, - S390_FEAT_MAX); - if (!bitmap_empty(bitmap, S390_FEAT_MAX)) { - s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_disabled_feat); - } - - /* features added to the base feature set */ - bitmap_andnot(bitmap, model->features, model->def->base_feat, - S390_FEAT_MAX); - if (!bitmap_empty(bitmap, S390_FEAT_MAX)) { - s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_enabled_feat); - } - } else { - /* expand all features */ - s390_feat_bitmap_to_ascii(model->features, qdict, - qdict_add_enabled_feat); - bitmap_complement(bitmap, model->features, S390_FEAT_MAX); - s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_disabled_feat); - } - - if (!qdict_size(qdict)) { - qobject_unref(qdict); - } else { - info->props = QOBJECT(qdict); - } -} - -CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, - CpuModelInfo *model, - Error **errp) -{ - Error *err = NULL; - CpuModelExpansionInfo *expansion_info = NULL; - S390CPUModel s390_model; - bool delta_changes = false; - - /* convert it to our internal representation */ - cpu_model_from_info(&s390_model, model, &err); - if (err) { - error_propagate(errp, err); - return NULL; - } - - if (type == CPU_MODEL_EXPANSION_TYPE_STATIC) { - delta_changes = true; - } else if (type != CPU_MODEL_EXPANSION_TYPE_FULL) { - error_setg(errp, "The requested expansion type is not supported."); - return NULL; - } - - /* convert it back to a static representation */ - 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); - return expansion_info; -} - -static void list_add_feat(const char *name, void *opaque) -{ - strList **last = (strList **) opaque; - - QAPI_LIST_PREPEND(*last, g_strdup(name)); -} - -CpuModelCompareInfo *qmp_query_cpu_model_comparison(CpuModelInfo *infoa, - CpuModelInfo *infob, - Error **errp) -{ - Error *err = NULL; - CpuModelCompareResult feat_result, gen_result; - CpuModelCompareInfo *compare_info; - S390FeatBitmap missing, added; - S390CPUModel modela, modelb; - - /* convert both models to our internal representation */ - cpu_model_from_info(&modela, infoa, &err); - if (err) { - error_propagate(errp, err); - return NULL; - } - cpu_model_from_info(&modelb, infob, &err); - if (err) { - error_propagate(errp, err); - return NULL; - } - compare_info = g_new0(CpuModelCompareInfo, 1); - - /* check the cpu generation and ga level */ - if (modela.def->gen == modelb.def->gen) { - if (modela.def->ec_ga == modelb.def->ec_ga) { - /* ec and corresponding bc are identical */ - gen_result = CPU_MODEL_COMPARE_RESULT_IDENTICAL; - } else if (modela.def->ec_ga < modelb.def->ec_ga) { - gen_result = CPU_MODEL_COMPARE_RESULT_SUBSET; - } else { - gen_result = CPU_MODEL_COMPARE_RESULT_SUPERSET; - } - } else if (modela.def->gen < modelb.def->gen) { - gen_result = CPU_MODEL_COMPARE_RESULT_SUBSET; - } else { - gen_result = CPU_MODEL_COMPARE_RESULT_SUPERSET; - } - if (gen_result != CPU_MODEL_COMPARE_RESULT_IDENTICAL) { - /* both models cannot be made identical */ - list_add_feat("type", &compare_info->responsible_properties); - } - - /* check the feature set */ - if (bitmap_equal(modela.features, modelb.features, S390_FEAT_MAX)) { - feat_result = CPU_MODEL_COMPARE_RESULT_IDENTICAL; - } else { - bitmap_andnot(missing, modela.features, modelb.features, S390_FEAT_MAX); - s390_feat_bitmap_to_ascii(missing, - &compare_info->responsible_properties, - list_add_feat); - bitmap_andnot(added, modelb.features, modela.features, S390_FEAT_MAX); - s390_feat_bitmap_to_ascii(added, &compare_info->responsible_properties, - list_add_feat); - if (bitmap_empty(missing, S390_FEAT_MAX)) { - feat_result = CPU_MODEL_COMPARE_RESULT_SUBSET; - } else if (bitmap_empty(added, S390_FEAT_MAX)) { - feat_result = CPU_MODEL_COMPARE_RESULT_SUPERSET; - } else { - feat_result = CPU_MODEL_COMPARE_RESULT_INCOMPATIBLE; - } - } - - /* combine the results */ - if (gen_result == feat_result) { - compare_info->result = gen_result; - } else if (feat_result == CPU_MODEL_COMPARE_RESULT_IDENTICAL) { - compare_info->result = gen_result; - } else if (gen_result == CPU_MODEL_COMPARE_RESULT_IDENTICAL) { - compare_info->result = feat_result; - } else { - compare_info->result = CPU_MODEL_COMPARE_RESULT_INCOMPATIBLE; - } - return compare_info; -} - -CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *infoa, - CpuModelInfo *infob, - Error **errp) -{ - Error *err = NULL; - CpuModelBaselineInfo *baseline_info; - S390CPUModel modela, modelb, model; - uint16_t cpu_type; - uint8_t max_gen_ga; - uint8_t max_gen; - - /* convert both models to our internal representation */ - cpu_model_from_info(&modela, infoa, &err); - if (err) { - error_propagate(errp, err); - return NULL; - } - - cpu_model_from_info(&modelb, infob, &err); - if (err) { - error_propagate(errp, err); - return NULL; - } - - /* features both models support */ - bitmap_and(model.features, modela.features, modelb.features, S390_FEAT_MAX); - - /* detect the maximum model not regarding features */ - if (modela.def->gen == modelb.def->gen) { - if (modela.def->type == modelb.def->type) { - cpu_type = modela.def->type; - } else { - cpu_type = 0; - } - max_gen = modela.def->gen; - max_gen_ga = MIN(modela.def->ec_ga, modelb.def->ec_ga); - } else if (modela.def->gen > modelb.def->gen) { - cpu_type = modelb.def->type; - max_gen = modelb.def->gen; - max_gen_ga = modelb.def->ec_ga; - } else { - cpu_type = modela.def->type; - max_gen = modela.def->gen; - max_gen_ga = modela.def->ec_ga; - } - - model.def = s390_find_cpu_def(cpu_type, max_gen, max_gen_ga, - model.features); - - /* models without early base features (esan3) are bad */ - if (!model.def) { - error_setg(errp, "No compatible CPU model could be created as" - " important base features are disabled"); - return NULL; - } - - /* strip off features not part of the max model */ - bitmap_and(model.features, model.features, model.def->full_feat, - S390_FEAT_MAX); - - baseline_info = g_new0(CpuModelBaselineInfo, 1); - baseline_info->model = g_malloc0(sizeof(*baseline_info->model)); - cpu_info_from_model(baseline_info->model, &model, true); - return baseline_info; -} - -void apply_cpu_model(const S390CPUModel *model, Error **errp) -{ - Error *err = NULL; - static S390CPUModel applied_model; - static bool applied; - - /* - * We have the same model for all VCPUs. KVM can only be configured before - * any VCPUs are defined in KVM. - */ - if (applied) { - if (model && memcmp(&applied_model, model, sizeof(S390CPUModel))) { - error_setg(errp, "Mixed CPU models are not supported on s390x."); - } - return; - } - - if (kvm_enabled()) { - kvm_s390_apply_cpu_model(model, &err); - if (err) { - error_propagate(errp, err); - return; - } - } - - applied = true; - if (model) { - applied_model = *model; - } -} diff --git a/target/s390x/cpu_models_system.c b/target/s390x/cpu_models_system.c new file mode 100644 index 00000000000..9d84faa3c9e --- /dev/null +++ b/target/s390x/cpu_models_system.c @@ -0,0 +1,433 @@ +/* + * CPU models for s390x - System-only + * + * Copyright 2016 IBM Corp. + * + * Author(s): David Hildenbrand + * + * 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 "cpu.h" +#include "s390x-internal.h" +#include "kvm/kvm_s390x.h" +#include "system/kvm.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qapi/qobject-input-visitor.h" +#include "qobject/qdict.h" +#include "qapi/qapi-commands-machine.h" + +static void list_add_feat(const char *name, void *opaque); + +static void check_unavailable_features(const S390CPUModel *max_model, + const S390CPUModel *model, + strList **unavailable) +{ + S390FeatBitmap missing; + + /* check general model compatibility */ + if (max_model->def->gen < model->def->gen || + (max_model->def->gen == model->def->gen && + max_model->def->ec_ga < model->def->ec_ga)) { + list_add_feat("type", unavailable); + } + + /* detect missing features if any to properly report them */ + bitmap_andnot(missing, model->features, max_model->features, + S390_FEAT_MAX); + if (!bitmap_empty(missing, S390_FEAT_MAX)) { + s390_feat_bitmap_to_ascii(missing, unavailable, list_add_feat); + } +} + +struct CpuDefinitionInfoListData { + CpuDefinitionInfoList *list; + S390CPUModel *model; +}; + +static void create_cpu_model_list(ObjectClass *klass, void *opaque) +{ + struct CpuDefinitionInfoListData *cpu_list_data = opaque; + CpuDefinitionInfoList **cpu_list = &cpu_list_data->list; + CpuDefinitionInfo *info; + char *name = g_strdup(object_class_get_name(klass)); + S390CPUClass *scc = S390_CPU_CLASS(klass); + + /* strip off the -s390x-cpu */ + g_strrstr(name, "-" TYPE_S390_CPU)[0] = 0; + info = g_new0(CpuDefinitionInfo, 1); + info->name = name; + info->has_migration_safe = true; + info->migration_safe = scc->is_migration_safe; + info->q_static = scc->is_static; + info->q_typename = g_strdup(object_class_get_name(klass)); + /* check for unavailable features */ + if (cpu_list_data->model) { + Object *obj; + S390CPU *sc; + obj = object_new_with_class(klass); + sc = S390_CPU(obj); + if (sc->model) { + info->has_unavailable_features = true; + check_unavailable_features(cpu_list_data->model, sc->model, + &info->unavailable_features); + } + object_unref(obj); + } + + QAPI_LIST_PREPEND(*cpu_list, info); +} + +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) +{ + struct CpuDefinitionInfoListData list_data = { + .list = NULL, + }; + + list_data.model = get_max_cpu_model(NULL); + + object_class_foreach(create_cpu_model_list, TYPE_S390_CPU, false, + &list_data); + + return list_data.list; +} + +static void cpu_model_from_info(S390CPUModel *model, const CpuModelInfo *info, + const char *info_arg_name, Error **errp) +{ + Error *err = NULL; + const QDict *qdict; + const QDictEntry *e; + Visitor *visitor; + ObjectClass *oc; + S390CPU *cpu; + Object *obj; + + oc = cpu_class_by_name(TYPE_S390_CPU, info->name); + if (!oc) { + error_setg(errp, "The CPU definition \'%s\' is unknown.", info->name); + return; + } + if (S390_CPU_CLASS(oc)->kvm_required && !kvm_enabled()) { + error_setg(errp, "The CPU definition '%s' requires KVM", info->name); + return; + } + obj = object_new_with_class(oc); + cpu = S390_CPU(obj); + + if (!cpu->model) { + error_setg(errp, "Details about the host CPU model are not available, " + "it cannot be used."); + object_unref(obj); + return; + } + + 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, 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; + } + } + if (!err) { + visit_check_struct(visitor, &err); + } + visit_end_struct(visitor, NULL); + visit_free(visitor); + if (err) { + error_propagate(errp, err); + object_unref(obj); + return; + } + } + + /* copy the model and throw the cpu away */ + memcpy(model, cpu->model, sizeof(*model)); + object_unref(obj); +} + +static void qdict_add_disabled_feat(const char *name, void *opaque) +{ + qdict_put_bool(opaque, name, false); +} + +static void qdict_add_enabled_feat(const char *name, void *opaque) +{ + qdict_put_bool(opaque, name, true); +} + +/* convert S390CPUDef into a static CpuModelInfo */ +static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, + bool delta_changes) +{ + QDict *qdict = qdict_new(); + S390FeatBitmap bitmap; + + /* always fallback to the static base model */ + info->name = g_strdup_printf("%s-base", model->def->name); + + if (delta_changes) { + /* features deleted from the base feature set */ + bitmap_andnot(bitmap, model->def->base_feat, model->features, + S390_FEAT_MAX); + if (!bitmap_empty(bitmap, S390_FEAT_MAX)) { + s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_disabled_feat); + } + + /* features added to the base feature set */ + bitmap_andnot(bitmap, model->features, model->def->base_feat, + S390_FEAT_MAX); + if (!bitmap_empty(bitmap, S390_FEAT_MAX)) { + s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_enabled_feat); + } + } else { + /* expand all features */ + s390_feat_bitmap_to_ascii(model->features, qdict, + qdict_add_enabled_feat); + bitmap_complement(bitmap, model->features, S390_FEAT_MAX); + s390_feat_bitmap_to_ascii(bitmap, qdict, qdict_add_disabled_feat); + } + + if (!qdict_size(qdict)) { + qobject_unref(qdict); + } else { + info->props = QOBJECT(qdict); + } +} + +CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, + CpuModelInfo *model, + Error **errp) +{ + Error *err = NULL; + CpuModelExpansionInfo *expansion_info = NULL; + S390CPUModel s390_model; + bool delta_changes = false; + S390FeatBitmap deprecated_feats; + + /* convert it to our internal representation */ + cpu_model_from_info(&s390_model, model, "model", &err); + if (err) { + error_propagate(errp, err); + return NULL; + } + + if (type == CPU_MODEL_EXPANSION_TYPE_STATIC) { + delta_changes = true; + } else if (type != CPU_MODEL_EXPANSION_TYPE_FULL) { + error_setg(errp, "The requested expansion type is not supported."); + return NULL; + } + + /* convert it back to a static representation */ + 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; +} + +static void list_add_feat(const char *name, void *opaque) +{ + strList **last = (strList **) opaque; + + QAPI_LIST_PREPEND(*last, g_strdup(name)); +} + +CpuModelCompareInfo *qmp_query_cpu_model_comparison(CpuModelInfo *infoa, + CpuModelInfo *infob, + Error **errp) +{ + Error *err = NULL; + CpuModelCompareResult feat_result, gen_result; + CpuModelCompareInfo *compare_info; + S390FeatBitmap missing, added; + S390CPUModel modela, modelb; + + /* convert both models to our internal representation */ + cpu_model_from_info(&modela, infoa, "modela", &err); + if (err) { + error_propagate(errp, err); + return NULL; + } + cpu_model_from_info(&modelb, infob, "modelb", &err); + if (err) { + error_propagate(errp, err); + return NULL; + } + compare_info = g_new0(CpuModelCompareInfo, 1); + + /* check the cpu generation and ga level */ + if (modela.def->gen == modelb.def->gen) { + if (modela.def->ec_ga == modelb.def->ec_ga) { + /* ec and corresponding bc are identical */ + gen_result = CPU_MODEL_COMPARE_RESULT_IDENTICAL; + } else if (modela.def->ec_ga < modelb.def->ec_ga) { + gen_result = CPU_MODEL_COMPARE_RESULT_SUBSET; + } else { + gen_result = CPU_MODEL_COMPARE_RESULT_SUPERSET; + } + } else if (modela.def->gen < modelb.def->gen) { + gen_result = CPU_MODEL_COMPARE_RESULT_SUBSET; + } else { + gen_result = CPU_MODEL_COMPARE_RESULT_SUPERSET; + } + if (gen_result != CPU_MODEL_COMPARE_RESULT_IDENTICAL) { + /* both models cannot be made identical */ + list_add_feat("type", &compare_info->responsible_properties); + } + + /* check the feature set */ + if (bitmap_equal(modela.features, modelb.features, S390_FEAT_MAX)) { + feat_result = CPU_MODEL_COMPARE_RESULT_IDENTICAL; + } else { + bitmap_andnot(missing, modela.features, modelb.features, S390_FEAT_MAX); + s390_feat_bitmap_to_ascii(missing, + &compare_info->responsible_properties, + list_add_feat); + bitmap_andnot(added, modelb.features, modela.features, S390_FEAT_MAX); + s390_feat_bitmap_to_ascii(added, &compare_info->responsible_properties, + list_add_feat); + if (bitmap_empty(missing, S390_FEAT_MAX)) { + feat_result = CPU_MODEL_COMPARE_RESULT_SUBSET; + } else if (bitmap_empty(added, S390_FEAT_MAX)) { + feat_result = CPU_MODEL_COMPARE_RESULT_SUPERSET; + } else { + feat_result = CPU_MODEL_COMPARE_RESULT_INCOMPATIBLE; + } + } + + /* combine the results */ + if (gen_result == feat_result) { + compare_info->result = gen_result; + } else if (feat_result == CPU_MODEL_COMPARE_RESULT_IDENTICAL) { + compare_info->result = gen_result; + } else if (gen_result == CPU_MODEL_COMPARE_RESULT_IDENTICAL) { + compare_info->result = feat_result; + } else { + compare_info->result = CPU_MODEL_COMPARE_RESULT_INCOMPATIBLE; + } + return compare_info; +} + +CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *infoa, + CpuModelInfo *infob, + Error **errp) +{ + Error *err = NULL; + CpuModelBaselineInfo *baseline_info; + S390CPUModel modela, modelb, model; + uint16_t cpu_type; + uint8_t max_gen_ga; + uint8_t max_gen; + + /* convert both models to our internal representation */ + cpu_model_from_info(&modela, infoa, "modela", &err); + if (err) { + error_propagate(errp, err); + return NULL; + } + + cpu_model_from_info(&modelb, infob, "modelb", &err); + if (err) { + error_propagate(errp, err); + return NULL; + } + + /* features both models support */ + bitmap_and(model.features, modela.features, modelb.features, S390_FEAT_MAX); + + /* detect the maximum model not regarding features */ + if (modela.def->gen == modelb.def->gen) { + if (modela.def->type == modelb.def->type) { + cpu_type = modela.def->type; + } else { + cpu_type = 0; + } + max_gen = modela.def->gen; + max_gen_ga = MIN(modela.def->ec_ga, modelb.def->ec_ga); + } else if (modela.def->gen > modelb.def->gen) { + cpu_type = modelb.def->type; + max_gen = modelb.def->gen; + max_gen_ga = modelb.def->ec_ga; + } else { + cpu_type = modela.def->type; + max_gen = modela.def->gen; + max_gen_ga = modela.def->ec_ga; + } + + model.def = s390_find_cpu_def(cpu_type, max_gen, max_gen_ga, + model.features); + + /* models without early base features (esan3) are bad */ + if (!model.def) { + error_setg(errp, "No compatible CPU model could be created as" + " important base features are disabled"); + return NULL; + } + + /* strip off features not part of the max model */ + bitmap_and(model.features, model.features, model.def->full_feat, + S390_FEAT_MAX); + + baseline_info = g_new0(CpuModelBaselineInfo, 1); + baseline_info->model = g_malloc0(sizeof(*baseline_info->model)); + cpu_info_from_model(baseline_info->model, &model, true); + return baseline_info; +} + +void apply_cpu_model(const S390CPUModel *model, Error **errp) +{ + static S390CPUModel applied_model; + static bool applied; + + /* + * We have the same model for all VCPUs. KVM can only be configured before + * any VCPUs are defined in KVM. + */ + if (applied) { + if (model && memcmp(&applied_model, model, sizeof(S390CPUModel))) { + error_setg(errp, "Mixed CPU models are not supported on s390x."); + } + return; + } + + if (kvm_enabled()) { + if (!kvm_s390_apply_cpu_model(model, errp)) { + return; + } + } + + applied = true; + if (model) { + applied_model = *model; + } +} diff --git a/target/s390x/diag.c b/target/s390x/diag.c index 8ce18e08f34..da44b0133ed 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -16,10 +16,10 @@ #include "cpu.h" #include "s390x-internal.h" #include "hw/watchdog/wdt_diag288.h" -#include "sysemu/cpus.h" +#include "system/cpus.h" #include "hw/s390x/ipl.h" #include "hw/s390x/s390-virtio-ccw.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "kvm/kvm_s390x.h" #include "target/s390x/kvm/pv.h" #include "qemu/error-report.h" @@ -77,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; @@ -133,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 6fbfd41bc86..6bca376f2b6 100644 --- a/target/s390x/gdbstub.c +++ b/target/s390x/gdbstub.c @@ -21,17 +21,16 @@ #include "qemu/osdep.h" #include "cpu.h" #include "s390x-internal.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" #include "exec/gdbstub.h" #include "gdbstub/helpers.h" #include "qemu/bitops.h" -#include "sysemu/hw_accel.h" -#include "sysemu/tcg.h" +#include "system/hw_accel.h" +#include "system/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: @@ -46,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: @@ -69,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]); @@ -82,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: @@ -98,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); @@ -113,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; @@ -132,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) { @@ -154,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; @@ -172,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]); @@ -186,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)); } @@ -206,11 +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 -/* total number of registers in s390-virt.xml */ -#define S390_NUM_VIRT_REGS 4 -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); @@ -225,24 +239,27 @@ static int cpu_read_virt_reg(CPUS390XState *env, GByteArray *mem_buf, int n) } } -static int cpu_write_virt_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_write_virt_reg(CPUState *cs, uint8_t *mem_buf, int n) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + switch (n) { case S390_VIRT_CKC_REGNUM: - env->ckc = ldtul_p(mem_buf); - cpu_synchronize_post_init(env_cpu(env)); + env->ckc = ldq_be_p(mem_buf); + cpu_synchronize_post_init(cs); return 8; case S390_VIRT_CPUTM_REGNUM: - env->cputm = ldtul_p(mem_buf); - cpu_synchronize_post_init(env_cpu(env)); + env->cputm = ldq_be_p(mem_buf); + cpu_synchronize_post_init(cs); return 8; case S390_VIRT_BEA_REGNUM: - env->gbea = ldtul_p(mem_buf); - cpu_synchronize_post_init(env_cpu(env)); + env->gbea = ldq_be_p(mem_buf); + cpu_synchronize_post_init(cs); return 8; case S390_VIRT_PREFIX_REGNUM: - env->psa = ldtul_p(mem_buf); - cpu_synchronize_post_init(env_cpu(env)); + env->psa = ldq_be_p(mem_buf); + cpu_synchronize_post_init(cs); return 8; default: return 0; @@ -254,11 +271,12 @@ static int cpu_write_virt_reg(CPUS390XState *env, uint8_t *mem_buf, int n) #define S390_VIRT_KVM_PFT_REGNUM 1 #define S390_VIRT_KVM_PFS_REGNUM 2 #define S390_VIRT_KVM_PFC_REGNUM 3 -/* total number of registers in s390-virt-kvm.xml */ -#define S390_NUM_VIRT_KVM_REGS 4 -static int cpu_read_virt_kvm_reg(CPUS390XState *env, GByteArray *mem_buf, int n) +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); @@ -273,23 +291,26 @@ static int cpu_read_virt_kvm_reg(CPUS390XState *env, GByteArray *mem_buf, int n) } } -static int cpu_write_virt_kvm_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_KVM_PP_REGNUM: - env->pp = ldtul_p(mem_buf); + env->pp = ldq_be_p(mem_buf); cpu_synchronize_post_init(env_cpu(env)); return 8; case S390_VIRT_KVM_PFT_REGNUM: - env->pfault_token = ldtul_p(mem_buf); + env->pfault_token = ldq_be_p(mem_buf); cpu_synchronize_post_init(env_cpu(env)); return 8; case S390_VIRT_KVM_PFS_REGNUM: - env->pfault_select = ldtul_p(mem_buf); + env->pfault_select = ldq_be_p(mem_buf); cpu_synchronize_post_init(env_cpu(env)); return 8; case S390_VIRT_KVM_PFC_REGNUM: - env->pfault_compare = ldtul_p(mem_buf); + env->pfault_compare = ldq_be_p(mem_buf); cpu_synchronize_post_init(env_cpu(env)); return 8; default: @@ -303,17 +324,21 @@ static int cpu_write_virt_kvm_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; } @@ -322,33 +347,33 @@ 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, - S390_NUM_VIRT_REGS, "s390-virt.xml", 0); + gdb_find_static_feature("s390-virt.xml"), 0); if (kvm_enabled()) { gdb_register_coprocessor(cs, cpu_read_virt_kvm_reg, cpu_write_virt_kvm_reg, - S390_NUM_VIRT_KVM_REGS, "s390-virt-kvm.xml", + 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 2b2bfc3736b..8218e6470ec 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -46,6 +46,47 @@ S390_FEAT_PLO_CSTSTGR, \ S390_FEAT_PLO_CSTSTX +#define S390_FEAT_GROUP_PLO_EXT \ + S390_FEAT_PLO_EXT, \ + S390_FEAT_PLO_CLO, \ + S390_FEAT_PLO_CSO, \ + S390_FEAT_PLO_DCSO, \ + S390_FEAT_PLO_CSSTO, \ + S390_FEAT_PLO_CSDSTO, \ + S390_FEAT_PLO_CSTSTO, \ + S390_FEAT_PLO_TCS, \ + S390_FEAT_PLO_TCSG, \ + S390_FEAT_PLO_TCSX, \ + S390_FEAT_PLO_TCSO, \ + S390_FEAT_PLO_QCS, \ + S390_FEAT_PLO_QCSG, \ + S390_FEAT_PLO_QCSX, \ + S390_FEAT_PLO_QCSO, \ + S390_FEAT_PLO_LO, \ + S390_FEAT_PLO_DLX, \ + S390_FEAT_PLO_DLO, \ + S390_FEAT_PLO_TL, \ + S390_FEAT_PLO_TLG, \ + S390_FEAT_PLO_TLX, \ + S390_FEAT_PLO_TLO, \ + S390_FEAT_PLO_QL, \ + S390_FEAT_PLO_QLG, \ + S390_FEAT_PLO_QLX, \ + S390_FEAT_PLO_QLO, \ + S390_FEAT_PLO_STO, \ + S390_FEAT_PLO_DST, \ + S390_FEAT_PLO_DSTG, \ + S390_FEAT_PLO_DSTX, \ + S390_FEAT_PLO_DSTO, \ + S390_FEAT_PLO_TST, \ + S390_FEAT_PLO_TSTG, \ + S390_FEAT_PLO_TSTX, \ + S390_FEAT_PLO_TSTO, \ + S390_FEAT_PLO_QST, \ + S390_FEAT_PLO_QSTG, \ + S390_FEAT_PLO_QSTX, \ + S390_FEAT_PLO_QSTO + #define S390_FEAT_GROUP_TOD_CLOCK_STEERING \ S390_FEAT_TOD_CLOCK_STEERING, \ S390_FEAT_PTFF_QTO, \ @@ -64,6 +105,9 @@ S390_FEAT_PTFF_STOE, \ S390_FEAT_PTFF_STOUE +#define S390_FEAT_GROUP_GEN17_PTFF \ + S390_FEAT_PTFF_QTSE + #define S390_FEAT_GROUP_MSA \ S390_FEAT_MSA, \ S390_FEAT_KMAC_DEA, \ @@ -246,6 +290,49 @@ S390_FEAT_PCKMO_ECC_ED25519, \ S390_FEAT_PCKMO_ECC_ED448 +#define S390_FEAT_GROUP_MSA_EXT_10 \ + S390_FEAT_KM_FULL_XTS_AES_128, \ + S390_FEAT_KM_FULL_XTS_AES_256, \ + S390_FEAT_KM_FULL_XTS_EAES_128, \ + S390_FEAT_KM_FULL_XTS_EAES_256 + +#define S390_FEAT_GROUP_MSA_EXT_10_PCKMO \ + S390_FEAT_PCKMO_AES_XTS_128_DK, \ + S390_FEAT_PCKMO_AES_XTS_256_DK + +#define S390_FEAT_GROUP_MSA_EXT_11 \ + S390_FEAT_KMAC_HMAC_SHA_224, \ + S390_FEAT_KMAC_HMAC_SHA_256, \ + S390_FEAT_KMAC_HMAC_SHA_384, \ + S390_FEAT_KMAC_HMAC_SHA_512, \ + S390_FEAT_KMAC_HMAC_ESHA_224, \ + S390_FEAT_KMAC_HMAC_ESHA_256, \ + S390_FEAT_KMAC_HMAC_ESHA_384, \ + S390_FEAT_KMAC_HMAC_ESHA_512 + +#define S390_FEAT_GROUP_MSA_EXT_11_PCKMO \ + S390_FEAT_PCKMO_HMAC_512, \ + S390_FEAT_PCKMO_HMAC_1024 + +#define S390_FEAT_GROUP_MSA_EXT_12 \ + S390_FEAT_MSA_EXT_12 + +#define S390_FEAT_GROUP_MSA_EXT_13 \ + S390_FEAT_KDSA_QAI, \ + S390_FEAT_KIMD_QAI, \ + S390_FEAT_KLMD_QAI, \ + S390_FEAT_KMAC_QAI, \ + S390_FEAT_KMA_QAI, \ + S390_FEAT_KMCTR_QAI, \ + S390_FEAT_KMF_QAI, \ + S390_FEAT_KMO_QAI, \ + S390_FEAT_KM_QAI, \ + S390_FEAT_PCC_QAI, \ + S390_FEAT_PRNO_QAI + +#define S390_FEAT_GROUP_MSA_EXT_13_PCKMO \ + S390_FEAT_PCKMO_QAI + #define S390_FEAT_GROUP_ENH_SORT \ S390_FEAT_ESORT_BASE, \ S390_FEAT_SORTL_SFLR, \ @@ -262,10 +349,21 @@ S390_FEAT_DEFLATE_XPND, \ S390_FEAT_DEFLATE_F0 +#define S390_FEAT_GROUP_CONCURRENT_FUNCTIONS \ + S390_FEAT_CCF_BASE, \ + S390_FEAT_PFCR_QAF, \ + S390_FEAT_PFCR_CSDST, \ + S390_FEAT_PFCR_CSDSTG, \ + S390_FEAT_PFCR_CSTST, \ + S390_FEAT_PFCR_CSTSTG + /* cpu feature groups */ static uint16_t group_PLO[] = { S390_FEAT_GROUP_PLO, }; +static uint16_t group_PLO_EXT[] = { + S390_FEAT_GROUP_PLO_EXT, +}; static uint16_t group_TOD_CLOCK_STEERING[] = { S390_FEAT_GROUP_TOD_CLOCK_STEERING, }; @@ -275,6 +373,11 @@ static uint16_t group_GEN13_PTFF[] = { static uint16_t group_MULTIPLE_EPOCH_PTFF[] = { S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF, }; + +static uint16_t group_GEN17_PTFF[] = { + S390_FEAT_GROUP_GEN17_PTFF, +}; + static uint16_t group_MSA[] = { S390_FEAT_GROUP_MSA, }; @@ -307,10 +410,38 @@ static uint16_t group_MSA_EXT_9[] = { S390_FEAT_GROUP_MSA_EXT_9, }; +static uint16_t group_MSA_EXT_10[] = { + S390_FEAT_GROUP_MSA_EXT_10, +}; + +static uint16_t group_MSA_EXT_11[] = { + S390_FEAT_GROUP_MSA_EXT_11, +}; + +static uint16_t group_MSA_EXT_12[] = { + S390_FEAT_GROUP_MSA_EXT_12, +}; + +static uint16_t group_MSA_EXT_13[] = { + S390_FEAT_GROUP_MSA_EXT_13, +}; + static uint16_t group_MSA_EXT_9_PCKMO[] = { S390_FEAT_GROUP_MSA_EXT_9_PCKMO, }; +static uint16_t group_MSA_EXT_10_PCKMO[] = { + S390_FEAT_GROUP_MSA_EXT_10_PCKMO, +}; + +static uint16_t group_MSA_EXT_11_PCKMO[] = { + S390_FEAT_GROUP_MSA_EXT_11_PCKMO, +}; + +static uint16_t group_MSA_EXT_13_PCKMO[] = { + S390_FEAT_GROUP_MSA_EXT_13_PCKMO, +}; + static uint16_t group_ENH_SORT[] = { S390_FEAT_GROUP_ENH_SORT, }; @@ -319,6 +450,10 @@ static uint16_t group_DEFLATE_CONVERSION[] = { S390_FEAT_GROUP_DEFLATE_CONVERSION, }; +static uint16_t group_CONCURRENT_FUNCTIONS[] = { + S390_FEAT_GROUP_CONCURRENT_FUNCTIONS, +}; + /* Base features (in order of release) * Only non-hypervisor managed features belong here. * Base feature sets are static meaning they do not change in future QEMU @@ -426,6 +561,13 @@ static uint16_t base_GEN15_GA1[] = { #define base_GEN16_GA1 EmptyFeat +static uint16_t base_GEN17_GA1[] = { + S390_FEAT_MISC_INSTRUCTION_EXT4, + S390_FEAT_SIF, + S390_FEAT_GROUP_MSA_EXT_12, + S390_FEAT_GROUP_PLO_EXT, +}; + /* Full features (in order of release) * Automatically includes corresponding base features. * Full features are all features this hardware supports even if kvm/QEMU do not @@ -580,6 +722,20 @@ static uint16_t full_GEN16_GA1[] = { S390_FEAT_UV_FEAT_AP_INTR, }; +static uint16_t full_GEN17_GA1[] = { + S390_FEAT_VECTOR_ENH3, + S390_FEAT_VECTOR_PACKED_DECIMAL_ENH3, + S390_FEAT_INEFF_NC_TX, + S390_FEAT_GROUP_GEN17_PTFF, + S390_FEAT_GROUP_MSA_EXT_10, + S390_FEAT_GROUP_MSA_EXT_10_PCKMO, + S390_FEAT_GROUP_MSA_EXT_11, + S390_FEAT_GROUP_MSA_EXT_11_PCKMO, + S390_FEAT_GROUP_MSA_EXT_13, + S390_FEAT_GROUP_MSA_EXT_13_PCKMO, + S390_FEAT_GROUP_CONCURRENT_FUNCTIONS, +}; + /* Default features (in order of release) * Automatically includes corresponding base features. @@ -675,15 +831,24 @@ static uint16_t default_GEN16_GA1[] = { S390_FEAT_PAIE, }; +static uint16_t default_GEN17_GA1[] = { + S390_FEAT_VECTOR_ENH3, + S390_FEAT_VECTOR_PACKED_DECIMAL_ENH3, + S390_FEAT_GROUP_MSA_EXT_10, + S390_FEAT_GROUP_MSA_EXT_10_PCKMO, + S390_FEAT_GROUP_MSA_EXT_11, + S390_FEAT_GROUP_MSA_EXT_11_PCKMO, + S390_FEAT_GROUP_MSA_EXT_13, + S390_FEAT_GROUP_MSA_EXT_13_PCKMO, +}; + /* QEMU (CPU model) features */ -static uint16_t qemu_V2_11[] = { +static uint16_t qemu_MIN[] = { + /* Features supported by the default CPU of the oldest machine type */ S390_FEAT_GROUP_PLO, S390_FEAT_ESAN3, S390_FEAT_ZARCH, -}; - -static uint16_t qemu_V3_1[] = { S390_FEAT_DAT_ENH, S390_FEAT_IDTE_SEGMENT, S390_FEAT_STFLE, @@ -713,18 +878,12 @@ static uint16_t qemu_V3_1[] = { S390_FEAT_ADAPTER_INT_SUPPRESSION, S390_FEAT_MSA_EXT_3, S390_FEAT_MSA_EXT_4, -}; - -static uint16_t qemu_V4_0[] = { /* * Only BFP bits are implemented (HFP, DFP, PFPO and DIVIDE TO INTEGER not * implemented yet). */ S390_FEAT_FLOATING_POINT_EXT, S390_FEAT_ZPCI, -}; - -static uint16_t qemu_V4_1[] = { S390_FEAT_STFLE_53, S390_FEAT_VECTOR, }; @@ -823,6 +982,7 @@ static CpuFeatDefSpec CpuFeatDef[] = { CPU_FEAT_INITIALIZER(GEN14_GA2), CPU_FEAT_INITIALIZER(GEN15_GA1), CPU_FEAT_INITIALIZER(GEN16_GA1), + CPU_FEAT_INITIALIZER(GEN17_GA1), }; #define FEAT_GROUP_INITIALIZER(_name) \ @@ -845,8 +1005,10 @@ typedef struct { *******************************/ static FeatGroupDefSpec FeatGroupDef[] = { FEAT_GROUP_INITIALIZER(PLO), + FEAT_GROUP_INITIALIZER(PLO_EXT), FEAT_GROUP_INITIALIZER(TOD_CLOCK_STEERING), FEAT_GROUP_INITIALIZER(GEN13_PTFF), + FEAT_GROUP_INITIALIZER(GEN17_PTFF), FEAT_GROUP_INITIALIZER(MSA), FEAT_GROUP_INITIALIZER(MSA_EXT_1), FEAT_GROUP_INITIALIZER(MSA_EXT_2), @@ -858,9 +1020,17 @@ static FeatGroupDefSpec FeatGroupDef[] = { FEAT_GROUP_INITIALIZER(MSA_EXT_8), FEAT_GROUP_INITIALIZER(MSA_EXT_9), FEAT_GROUP_INITIALIZER(MSA_EXT_9_PCKMO), + FEAT_GROUP_INITIALIZER(MSA_EXT_10), + FEAT_GROUP_INITIALIZER(MSA_EXT_10_PCKMO), + FEAT_GROUP_INITIALIZER(MSA_EXT_11), + FEAT_GROUP_INITIALIZER(MSA_EXT_11_PCKMO), + FEAT_GROUP_INITIALIZER(MSA_EXT_12), + FEAT_GROUP_INITIALIZER(MSA_EXT_13), + FEAT_GROUP_INITIALIZER(MSA_EXT_13_PCKMO), FEAT_GROUP_INITIALIZER(MULTIPLE_EPOCH_PTFF), FEAT_GROUP_INITIALIZER(ENH_SORT), FEAT_GROUP_INITIALIZER(DEFLATE_CONVERSION), + FEAT_GROUP_INITIALIZER(CONCURRENT_FUNCTIONS), }; #define QEMU_FEAT_INITIALIZER(_name) \ @@ -875,10 +1045,7 @@ static FeatGroupDefSpec FeatGroupDef[] = { * QEMU (CPU model) features *******************************/ static FeatGroupDefSpec QemuFeatDef[] = { - QEMU_FEAT_INITIALIZER(V2_11), - QEMU_FEAT_INITIALIZER(V3_1), - QEMU_FEAT_INITIALIZER(V4_0), - QEMU_FEAT_INITIALIZER(V4_1), + QEMU_FEAT_INITIALIZER(MIN), QEMU_FEAT_INITIALIZER(V6_0), QEMU_FEAT_INITIALIZER(V6_2), QEMU_FEAT_INITIALIZER(V7_0), diff --git a/target/s390x/helper.c b/target/s390x/helper.c index d76c06381bb..3c57c32e479 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -1,5 +1,5 @@ /* - * S/390 helpers - sysemu only + * S/390 helpers - system only * * Copyright (c) 2009 Ulrich Hecht * Copyright (c) 2011 Alexander Graf @@ -25,8 +25,10 @@ #include "qemu/timer.h" #include "hw/s390x/ioinst.h" #include "target/s390x/kvm/pv.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" +#include "system/hw_accel.h" +#include "system/runstate.h" +#include "exec/target_page.h" +#include "exec/watchpoint.h" void s390x_tod_timer(void *opaque) { @@ -139,8 +141,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 05102578fc9..1a8a76abb98 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -88,7 +88,10 @@ 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_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) @@ -356,10 +359,10 @@ DEF_HELPER_FLAGS_4(ipte, TCG_CALL_NO_RWG, void, env, i64, i64, i32) DEF_HELPER_FLAGS_1(ptlb, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_1(purge, TCG_CALL_NO_RWG, void, env) DEF_HELPER_3(lra, i64, env, i64, i64) -DEF_HELPER_1(per_check_exception, void, env) -DEF_HELPER_FLAGS_3(per_branch, TCG_CALL_NO_RWG, void, env, i64, i64) -DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_RWG, void, env, i64) -DEF_HELPER_FLAGS_1(per_store_real, TCG_CALL_NO_RWG, void, env) +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/interrupt.c b/target/s390x/interrupt.c index 5195f060ecb..1dca835c5d8 100644 --- a/target/s390x/interrupt.c +++ b/target/s390x/interrupt.c @@ -11,9 +11,8 @@ #include "cpu.h" #include "kvm/kvm_s390x.h" #include "s390x-internal.h" -#include "exec/exec-all.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/tcg.h" #include "hw/s390x/ioinst.h" #include "tcg/tcg_s390x.h" #if !defined(CONFIG_USER_ONLY) @@ -30,6 +29,7 @@ void trigger_pgm_exception(CPUS390XState *env, uint32_t code) /* env->int_pgm_ilen is already set, or will be set during unwinding */ } +#if !defined(CONFIG_USER_ONLY) void s390_program_interrupt(CPUS390XState *env, uint32_t code, uintptr_t ra) { if (kvm_enabled()) { @@ -41,7 +41,6 @@ void s390_program_interrupt(CPUS390XState *env, uint32_t code, uintptr_t ra) } } -#if !defined(CONFIG_USER_ONLY) void cpu_inject_clock_comparator(S390CPU *cpu) { CPUS390XState *env = &cpu->env; @@ -225,11 +224,9 @@ bool s390_cpu_has_stop_int(S390CPU *cpu) return env->pending_int & INTERRUPT_STOP; } -#endif bool s390_cpu_has_int(S390CPU *cpu) { -#ifndef CONFIG_USER_ONLY if (!tcg_enabled()) { return false; } @@ -238,7 +235,5 @@ bool s390_cpu_has_int(S390CPU *cpu) s390_cpu_has_io_int(cpu) || s390_cpu_has_restart_int(cpu) || s390_cpu_has_stop_int(cpu); -#else - return false; -#endif } +#endif /* !CONFIG_USER_ONLY */ diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c index bbe45a497a8..2320dd4c122 100644 --- a/target/s390x/ioinst.c +++ b/target/s390x/ioinst.c @@ -12,11 +12,13 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/target_page.h" #include "s390x-internal.h" #include "hw/s390x/ioinst.h" #include "trace.h" #include "hw/s390x/s390-pci-bus.h" #include "target/s390x/kvm/pv.h" +#include "hw/s390x/ap-bridge.h" /* All I/O instructions but chsc use the s format */ static uint64_t get_address_from_regs(CPUS390XState *env, uint32_t ipb, @@ -573,13 +575,19 @@ static void ioinst_handle_chsc_sda(ChscReq *req, ChscResp *res) static int chsc_sei_nt0_get_event(void *res) { - /* no events yet */ + if (s390_has_feat(S390_FEAT_AP)) { + return ap_chsc_sei_nt0_get_event(res); + } + return 1; } static int chsc_sei_nt0_have_event(void) { - /* no events yet */ + if (s390_has_feat(S390_FEAT_AP)) { + return ap_chsc_sei_nt0_have_event(); + } + return 0; } @@ -603,7 +611,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 bc5c56a3055..67d9a1977c0 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -27,7 +27,7 @@ #include "cpu.h" #include "s390x-internal.h" #include "kvm_s390x.h" -#include "sysemu/kvm_int.h" +#include "system/kvm_int.h" #include "qemu/cutils.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -36,12 +36,12 @@ #include "qemu/main-loop.h" #include "qemu/mmap-alloc.h" #include "qemu/log.h" -#include "sysemu/sysemu.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" -#include "sysemu/device_tree.h" -#include "exec/gdbstub.h" -#include "exec/ram_addr.h" +#include "system/system.h" +#include "system/hw_accel.h" +#include "system/runstate.h" +#include "system/device_tree.h" +#include "gdbstub/enums.h" +#include "system/ram_addr.h" #include "trace.h" #include "hw/s390x/s390-pci-inst.h" #include "hw/s390x/s390-pci-bus.h" @@ -49,8 +49,9 @@ #include "hw/s390x/ebcdic.h" #include "exec/memattrs.h" #include "hw/s390x/s390-virtio-ccw.h" -#include "hw/s390x/s390-virtio-hcall.h" +#include "hw/s390x/s390-hypercall.h" #include "target/s390x/kvm/pv.h" +#include CONFIG_DEVICES #define kvm_vm_check_mem_attr(s, attr) \ kvm_vm_check_attr(s, KVM_S390_VM_MEM_CTRL, attr) @@ -86,6 +87,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 @@ -138,7 +140,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; @@ -297,12 +298,6 @@ void kvm_s390_set_max_pagesize(uint64_t pagesize, Error **errp) return; } - if (!hpage_1m_allowed()) { - error_setg(errp, "This QEMU machine does not support huge page " - "mappings"); - return; - } - if (pagesize != 1 * MiB) { error_setg(errp, "Memory backing with 2G pages was specified, " "but KVM does not support this memory backing"); @@ -337,21 +332,28 @@ int kvm_arch_get_default_type(MachineState *ms) 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); @@ -365,13 +367,10 @@ 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); - if (ri_allowed()) { - if (kvm_vm_enable_cap(s, KVM_CAP_S390_RI, 0) == 0) { - cap_ri = 1; - } - } - if (cpu_model_allowed()) { - kvm_vm_enable_cap(s, KVM_CAP_S390_GS, 0); + kvm_vm_enable_cap(s, KVM_CAP_S390_CPU_TOPOLOGY, 0); + kvm_vm_enable_cap(s, KVM_CAP_S390_GS, 0); + if (kvm_vm_enable_cap(s, KVM_CAP_S390_RI, 0) == 0) { + cap_ri = 1; } /* @@ -380,7 +379,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) * support is considered necessary, we only try to enable this for * newer machine types if KVM_CAP_S390_AIS_MIGRATION is available. */ - if (cpu_model_allowed() && kvm_kernel_irqchip_allowed() && + if (kvm_kernel_irqchip_allowed() && kvm_check_extension(s, KVM_CAP_S390_AIS_MIGRATION)) { kvm_vm_enable_cap(s, KVM_CAP_S390_AIS, 0); } @@ -399,6 +398,11 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu) return cpu->cpu_index; } +int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + return 0; +} + int kvm_arch_init_vcpu(CPUState *cs) { unsigned int max_cpus = MACHINE(qdev_get_machine())->smp.max_cpus; @@ -458,37 +462,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++) { @@ -521,6 +515,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; @@ -567,25 +570,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; @@ -607,22 +591,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; @@ -630,37 +604,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)) { @@ -685,11 +636,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; @@ -1222,12 +1168,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; @@ -1406,7 +1352,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); } @@ -1457,6 +1403,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; @@ -1474,6 +1427,9 @@ 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; @@ -1531,20 +1487,6 @@ static int handle_e3(S390CPU *cpu, struct kvm_run *run, uint8_t ipbl) return r; } -static int handle_hypercall(S390CPU *cpu, struct kvm_run *run) -{ - CPUS390XState *env = &cpu->env; - int ret; - - ret = s390_virtio_hypercall(env); - if (ret == -EINVAL) { - kvm_s390_program_interrupt(cpu, PGM_SPECIFICATION); - return 0; - } - - return ret; -} - static void kvm_handle_diag_288(S390CPU *cpu, struct kvm_run *run) { uint64_t r1, r3; @@ -1640,9 +1582,11 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb) case DIAG_SET_CONTROL_PROGRAM_CODES: handle_diag_318(cpu, run); break; +#ifdef CONFIG_S390_CCW_VIRTIO case DIAG_KVM_HYPERCALL: - r = handle_hypercall(cpu, run); + handle_diag_500(cpu, RA_IGNORED); break; +#endif /* CONFIG_S390_CCW_VIRTIO */ case DIAG_KVM_BREAKPOINT: r = handle_sw_breakpoint(cpu, run); break; @@ -1911,9 +1855,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; } @@ -1958,7 +1905,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); @@ -1982,7 +1929,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; @@ -2232,6 +2179,9 @@ static int query_cpu_subfunc(S390FeatBitmap features) if (test_bit(S390_FEAT_DEFLATE_BASE, features)) { s390_add_from_feat_block(features, S390_FEAT_TYPE_DFLTCC, prop.dfltcc); } + if (test_bit(S390_FEAT_CCF_BASE, features)) { + s390_add_from_feat_block(features, S390_FEAT_TYPE_PFCR, prop.pfcr); + } return 0; } @@ -2285,6 +2235,9 @@ static int configure_cpu_subfunc(const S390FeatBitmap features) if (test_bit(S390_FEAT_DEFLATE_BASE, features)) { s390_fill_feat_block(features, S390_FEAT_TYPE_DFLTCC, prop.dfltcc); } + if (test_bit(S390_FEAT_CCF_BASE, features)) { + s390_fill_feat_block(features, S390_FEAT_TYPE_PFCR, prop.pfcr); + } return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); } @@ -2396,10 +2349,6 @@ static int configure_cpu_feat(const S390FeatBitmap features) bool kvm_s390_cpu_models_supported(void) { - if (!cpu_model_allowed()) { - /* compatibility machines interfere with the cpu model */ - return false; - } return kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL, KVM_S390_VM_CPU_MACHINE) && kvm_vm_check_attr(kvm_state, KVM_S390_VM_CPU_MODEL, @@ -2412,7 +2361,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 = { @@ -2427,14 +2376,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); @@ -2457,13 +2406,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 */ @@ -2495,6 +2444,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); @@ -2511,7 +2468,7 @@ 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 (ap_available()) { @@ -2535,6 +2492,7 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) /* 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) @@ -2571,7 +2529,7 @@ static void kvm_s390_configure_apie(bool interpret) } } -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 }, @@ -2588,11 +2546,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); @@ -2602,19 +2560,19 @@ 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)) { @@ -2629,8 +2587,9 @@ void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp) rc = configure_uv_feat_guest(model->features); if (rc) { error_setg(errp, "KVM: Error configuring CPU UV features %d", rc); - return; + return false; } + return true; } void kvm_s390_restart_interrupt(S390CPU *cpu) @@ -2651,14 +2610,26 @@ void kvm_s390_stop_interrupt(S390CPU *cpu) kvm_s390_vcpu_interrupt(cpu, &irq); } -bool kvm_arch_cpu_check_are_resettable(void) +int kvm_s390_get_zpci_op(void) { - return true; + return cap_zpci_op; } -int kvm_s390_get_zpci_op(void) +int kvm_s390_topology_set_mtcr(uint64_t attr) { - return cap_zpci_op; + 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 f9785564d0b..649dae5948a 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 d6aca590ae4..588a9aa737e 100644 --- a/target/s390x/kvm/meson.build +++ b/target/s390x/kvm/meson.build @@ -1,7 +1,8 @@ s390x_ss.add(when: 'CONFIG_KVM', if_true: files( 'pv.c', - 'kvm.c' + 'kvm.c', + 'stsi-topology.c' ), if_false: files( 'stubs.c' )) diff --git a/target/s390x/kvm/pv.c b/target/s390x/kvm/pv.c index 6a69be7e5c5..2bc916a5455 100644 --- a/target/s390x/kvm/pv.c +++ b/target/s390x/kvm/pv.c @@ -16,10 +16,10 @@ #include "qemu/units.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/kvm.h" -#include "sysemu/cpus.h" +#include "system/kvm.h" +#include "system/cpus.h" #include "qom/object_interfaces.h" -#include "exec/confidential-guest-support.h" +#include "system/confidential-guest-support.h" #include "hw/s390x/ipl.h" #include "hw/s390x/sclp.h" #include "target/s390x/kvm/kvm_s390x.h" @@ -29,7 +29,8 @@ 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) +static int __s390_pv_cmd(uint32_t cmd, const char *cmdname, void *data, + struct S390PVResponse *pv_resp) { struct kvm_pv_cmd pv_cmd = { .cmd = cmd, @@ -46,6 +47,11 @@ static int __s390_pv_cmd(uint32_t cmd, const char *cmdname, void *data) "IOCTL rc: %d", cmd, cmdname, pv_cmd.rc, pv_cmd.rrc, rc); } + if (pv_resp) { + pv_resp->cmd = cmd; + pv_resp->rc = pv_cmd.rc; + pv_resp->rrc = pv_cmd.rrc; + } return rc; } @@ -53,15 +59,15 @@ static int __s390_pv_cmd(uint32_t cmd, const char *cmdname, void *data) * 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); \ - } \ +#define s390_pv_cmd(cmd, data) __s390_pv_cmd(cmd, #cmd, data, NULL) +#define s390_pv_cmd_pv_resp(cmd, data, pv_resp) \ + __s390_pv_cmd(cmd, #cmd, data, pv_resp) + +static void s390_pv_cmd_exit(uint32_t cmd, void *data) +{ + if (s390_pv_cmd(cmd, data)) { + exit(1); + } } int s390_pv_query_info(void) @@ -128,7 +134,7 @@ bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms) * 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) || + if (s390_get_memory_limit(ms) <= 2 * GiB || !kvm_check_extension(kvm_state, KVM_CAP_S390_PROTECTED_ASYNC_DISABLE)) { return false; } @@ -142,20 +148,33 @@ bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms) return true; } -int s390_pv_set_sec_parms(uint64_t origin, uint64_t length) +#define UV_RC_SSC_INVAL_HOSTKEY 0x0108 +int s390_pv_set_sec_parms(uint64_t origin, uint64_t length, + struct S390PVResponse *pv_resp, Error **errp) { + int ret; struct kvm_s390_pv_sec_parm args = { .origin = origin, .length = length, }; - return s390_pv_cmd(KVM_PV_SET_SEC_PARMS, &args); + ret = s390_pv_cmd_pv_resp(KVM_PV_SET_SEC_PARMS, &args, pv_resp); + if (ret) { + error_setg(errp, "Failed to set secure execution parameters"); + if (pv_resp->rc == UV_RC_SSC_INVAL_HOSTKEY) { + 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) +int s390_pv_unpack(uint64_t addr, uint64_t size, + uint64_t tweak, struct S390PVResponse *pv_resp) { struct kvm_s390_pv_unp args = { .addr = addr, @@ -163,7 +182,7 @@ int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak) .tweak = tweak, }; - return s390_pv_cmd(KVM_PV_UNPACK, &args); + return s390_pv_cmd_pv_resp(KVM_PV_UNPACK, &args, pv_resp); } void s390_pv_prep_reset(void) @@ -171,9 +190,9 @@ void s390_pv_prep_reset(void) s390_pv_cmd_exit(KVM_PV_PREP_RESET, NULL); } -int s390_pv_verify(void) +int s390_pv_verify(struct S390PVResponse *pv_resp) { - return s390_pv_cmd(KVM_PV_VERIFY, NULL); + return s390_pv_cmd_pv_resp(KVM_PV_VERIFY, NULL, pv_resp); } void s390_pv_unshare(void) @@ -181,13 +200,29 @@ void s390_pv_unshare(void) s390_pv_cmd_exit(KVM_PV_UNSHARE_ALL, NULL); } -void s390_pv_inject_reset_error(CPUState *cs) +void s390_pv_inject_reset_error(CPUState *cs, + struct S390PVResponse pv_resp) { int r1 = (cs->kvm_run->s390_sieic.ipa & 0x00f0) >> 4; CPUS390XState *env = &S390_CPU(cs)->env; + union { + struct { + uint16_t pv_cmd; + uint16_t pv_rrc; + uint16_t pv_rc; + uint16_t diag_rc; + }; + uint64_t regs; + } resp = { + .pv_cmd = pv_resp.cmd, + .pv_rrc = pv_resp.rrc, + .pv_rc = pv_resp.rc, + .diag_rc = DIAG_308_RC_INVAL_FOR_PV + }; + /* Report that we are unable to enter protected mode */ - env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV; + env->regs[r1 + 1] = resp.regs; } uint64_t kvm_s390_pv_dmp_get_size_cpu(void) @@ -319,12 +354,17 @@ static bool s390_pv_guest_check(ConfidentialGuestSupport *cgs, Error **errp) return s390_pv_check_cpus(errp); } -int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +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"); @@ -347,8 +387,11 @@ OBJECT_DEFINE_TYPE_WITH_INTERFACES(S390PVGuest, { TYPE_USER_CREATABLE }, { NULL }) -static void s390_pv_guest_class_init(ObjectClass *oc, void *data) +static void s390_pv_guest_class_init(ObjectClass *oc, const void *data) { + ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); + + klass->kvm_init = s390_pv_kvm_init; } static void s390_pv_guest_init(Object *obj) diff --git a/target/s390x/kvm/pv.h b/target/s390x/kvm/pv.h index 7b935e2246c..94e885e9335 100644 --- a/target/s390x/kvm/pv.h +++ b/target/s390x/kvm/pv.h @@ -13,9 +13,15 @@ #define HW_S390_PV_H #include "qapi/error.h" -#include "sysemu/kvm.h" +#include "system/kvm.h" #include "hw/s390x/s390-virtio-ccw.h" +struct S390PVResponse { + uint16_t cmd; + uint16_t rrc; + uint16_t rc; +}; + #ifdef CONFIG_KVM #include "cpu.h" @@ -42,12 +48,15 @@ int s390_pv_query_info(void); int s390_pv_vm_enable(void); void s390_pv_vm_disable(void); bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms); -int s390_pv_set_sec_parms(uint64_t origin, uint64_t length); -int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak); +int s390_pv_set_sec_parms(uint64_t origin, uint64_t length, + struct S390PVResponse *pv_resp, Error **errp); +int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak, + struct S390PVResponse *pv_resp); void s390_pv_prep_reset(void); -int s390_pv_verify(void); +int s390_pv_verify(struct S390PVResponse *pv_resp); void s390_pv_unshare(void); -void s390_pv_inject_reset_error(CPUState *cs); +void s390_pv_inject_reset_error(CPUState *cs, + struct S390PVResponse pv_resp); uint64_t kvm_s390_pv_dmp_get_size_cpu(void); uint64_t kvm_s390_pv_dmp_get_size_mem_state(void); uint64_t kvm_s390_pv_dmp_get_size_completion_data(void); @@ -62,12 +71,16 @@ 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 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) { return 0; } -static inline int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak) { return 0; } +static inline int s390_pv_set_sec_parms(uint64_t origin, uint64_t length, + struct S390PVResponse *pv_resp, + Error **errp) { return 0; } +static inline int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak, + struct S390PVResponse *pv_resp) { return 0; } static inline void s390_pv_prep_reset(void) {} -static inline int s390_pv_verify(void) { return 0; } +static inline int s390_pv_verify(struct S390PVResponse *pv_resp) { return 0; } static inline void s390_pv_unshare(void) {} -static inline void s390_pv_inject_reset_error(CPUState *cs) {}; +static inline void s390_pv_inject_reset_error(CPUState *cs, + struct S390PVResponse pv_resp) {}; static inline uint64_t kvm_s390_pv_dmp_get_size_cpu(void) { return 0; } static inline uint64_t kvm_s390_pv_dmp_get_size_mem_state(void) { return 0; } static inline uint64_t kvm_s390_pv_dmp_get_size_completion_data(void) { return 0; } @@ -79,18 +92,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 00000000000..c8d6389cd87 --- /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/machine.c b/target/s390x/machine.c index 37a076858c7..3bea6103ffb 100644 --- a/target/s390x/machine.c +++ b/target/s390x/machine.c @@ -20,8 +20,8 @@ #include "kvm/kvm_s390x.h" #include "migration/vmstate.h" #include "tcg/tcg_s390x.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" +#include "system/kvm.h" +#include "system/tcg.h" static int cpu_post_load(void *opaque, int version_id) { @@ -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 42ed38942a0..3b34ae034cb 100644 --- a/target/s390x/meson.build +++ b/target/s390x/meson.build @@ -27,8 +27,8 @@ s390x_system_ss.add(files( 'machine.c', 'mmu_helper.c', 'sigp.c', - 'cpu-sysemu.c', - 'cpu_models_sysemu.c', + 'cpu-system.c', + 'cpu_models_system.c', )) s390x_user_ss = ss.source_set() @@ -40,5 +40,5 @@ subdir('tcg') subdir('kvm') target_arch += {'s390x': s390x_ss} -target_softmmu_arch += {'s390x': s390x_system_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 fbb2f1b4d48..00946e9c0fe 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -17,14 +17,14 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "cpu.h" #include "s390x-internal.h" #include "kvm/kvm_s390x.h" -#include "sysemu/kvm.h" -#include "sysemu/tcg.h" -#include "exec/exec-all.h" -#include "trace.h" +#include "system/kvm.h" +#include "system/tcg.h" +#include "exec/page-protection.h" +#include "exec/target_page.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); } } diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index 825252d7284..a4ba6227ab4 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -240,11 +240,12 @@ uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst, #ifndef CONFIG_USER_ONLY unsigned int s390_cpu_halt(S390CPU *cpu); void s390_cpu_unhalt(S390CPU *cpu); -void s390_cpu_init_sysemu(Object *obj); -bool s390_cpu_realize_sysemu(DeviceState *dev, Error **errp); +void s390_cpu_system_init(Object *obj); +bool s390_cpu_system_realize(DeviceState *dev, Error **errp); void s390_cpu_finalize(Object *obj); -void s390_cpu_class_init_sysemu(CPUClass *cc); +void s390_cpu_system_class_init(CPUClass *cc); void s390_cpu_machine_reset_cb(void *opaque); +bool s390_cpu_has_work(CPUState *cs); #else static inline unsigned int s390_cpu_halt(S390CPU *cpu) @@ -341,6 +342,7 @@ void cpu_unmap_lowcore(LowCore *lowcore); /* interrupt.c */ void trigger_pgm_exception(CPUS390XState *env, uint32_t code); +#ifndef CONFIG_USER_ONLY void cpu_inject_clock_comparator(S390CPU *cpu); void cpu_inject_cpu_timer(S390CPU *cpu); void cpu_inject_emergency_signal(S390CPU *cpu, uint16_t src_cpu_addr); @@ -353,9 +355,11 @@ bool s390_cpu_has_restart_int(S390CPU *cpu); bool s390_cpu_has_stop_int(S390CPU *cpu); void cpu_inject_restart(S390CPU *cpu); void cpu_inject_stop(S390CPU *cpu); +#endif /* CONFIG_USER_ONLY */ /* ioinst.c */ +#ifndef CONFIG_USER_ONLY void ioinst_handle_xsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra); void ioinst_handle_csch(S390CPU *cpu, uint64_t reg1, uintptr_t ra); void ioinst_handle_hsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra); @@ -373,6 +377,7 @@ void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2, void ioinst_handle_rsch(S390CPU *cpu, uint64_t reg1, uintptr_t ra); void ioinst_handle_rchp(S390CPU *cpu, uint64_t reg1, uintptr_t ra); void ioinst_handle_sal(S390CPU *cpu, uint64_t reg1, uintptr_t ra); +#endif /* CONFIG_USER_ONLY */ /* mem_helper.c */ @@ -399,6 +404,8 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, /* translate.c */ void s390x_translate_init(void); +void s390x_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); void s390x_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data); diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index 9dd977349ab..5e95c4978f9 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -11,11 +11,12 @@ #include "qemu/osdep.h" #include "cpu.h" #include "s390x-internal.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" -#include "exec/address-spaces.h" -#include "exec/exec-all.h" -#include "sysemu/tcg.h" +#include "hw/boards.h" +#include "system/hw_accel.h" +#include "system/runstate.h" +#include "system/address-spaces.h" +#include "exec/cputlb.h" +#include "system/tcg.h" #include "trace.h" #include "qapi/qapi-types-machine.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/cc_helper.c b/target/s390x/tcg/cc_helper.c index b36f8cdc8b9..6595ac763c3 100644 --- a/target/s390x/tcg/cc_helper.c +++ b/target/s390x/tcg/cc_helper.c @@ -22,7 +22,6 @@ #include "cpu.h" #include "s390x-internal.h" #include "tcg_s390x.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" diff --git a/target/s390x/tcg/crypto_helper.c b/target/s390x/tcg/crypto_helper.c index 93aabd236f4..4447bb66eee 100644 --- a/target/s390x/tcg/crypto_helper.c +++ b/target/s390x/tcg/crypto_helper.c @@ -17,8 +17,7 @@ #include "s390x-internal.h" #include "tcg_s390x.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" static uint64_t R(uint64_t x, int c) { diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index b875bf14e56..e4c75d0ce01 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -22,12 +22,14 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "exec/target_page.h" +#include "exec/watchpoint.h" #include "s390x-internal.h" #include "tcg_s390x.h" #ifndef CONFIG_USER_ONLY #include "qemu/timer.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/s390x/ioinst.h" #include "hw/s390x/s390_flic.h" #include "hw/boards.h" @@ -90,10 +92,7 @@ void HELPER(data_exception)(CPUS390XState *env, uint32_t dxc) 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) @@ -146,8 +145,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; @@ -213,7 +211,7 @@ static void do_program_interrupt(CPUS390XState *env) switch (env->int_pgm_code) { case PGM_PER: - advance = !(env->per_perc_atmid & PER_CODE_EVENT_NULLIFICATION); + /* advance already handled */ break; case PGM_ASCE_TYPE: case PGM_REG_FIRST_TRANS: @@ -600,8 +598,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) { diff --git a/target/s390x/tcg/fpu_helper.c b/target/s390x/tcg/fpu_helper.c index d8bd5748faf..1ba43715ac1 100644 --- a/target/s390x/tcg/fpu_helper.c +++ b/target/s390x/tcg/fpu_helper.c @@ -22,7 +22,6 @@ #include "cpu.h" #include "s390x-internal.h" #include "tcg_s390x.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" @@ -780,7 +779,7 @@ uint32_t HELPER(kxb)(CPUS390XState *env, Int128 a, Int128 b) 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; } @@ -789,7 +788,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; } @@ -798,7 +797,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; @@ -808,7 +807,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; diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc index 0bfd88d3c3a..ec730ee0919 100644 --- a/target/s390x/tcg/insn-data.h.inc +++ b/target/s390x/tcg/insn-data.h.inc @@ -293,9 +293,14 @@ 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) @@ -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) @@ -1007,7 +1012,7 @@ D(0xb92e, KM, RRE, MSA, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_KM) D(0xb92f, KMC, RRE, MSA, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_KMC) D(0xb929, KMA, RRF_b, MSA8, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_KMA) - D(0xb93c, PPNO, RRE, MSA5, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_PPNO) + E(0xb93c, PPNO, RRE, MSA5, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_PPNO, IF_IO) D(0xb93e, KIMD, RRE, MSA, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_KIMD) D(0xb93f, KLMD, RRE, MSA, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_KLMD) diff --git a/target/s390x/tcg/int_helper.c b/target/s390x/tcg/int_helper.c index eb8e6dd1b57..fbda396f5b4 100644 --- a/target/s390x/tcg/int_helper.c +++ b/target/s390x/tcg/int_helper.c @@ -22,9 +22,9 @@ #include "cpu.h" #include "s390x-internal.h" #include "tcg_s390x.h" -#include "exec/exec-all.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" +#include "accel/tcg/cpu-ldst.h" /* #define DEBUG_HELPER */ #ifdef DEBUG_HELPER @@ -98,6 +98,81 @@ Int128 HELPER(divu64)(CPUS390XState *env, uint64_t ah, uint64_t al, uint64_t b) 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()); + } +} + +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()); + } + bin = tmp; + pow10 *= 10; + } + + 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) { /* positive 0 */ @@ -118,6 +193,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 84103251b97..a03609a1406 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -24,14 +24,21 @@ #include "s390x-internal.h" #include "tcg_s390x.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "hw/core/tcg-cpu-ops.h" +#include "exec/cpu-common.h" +#include "exec/cputlb.h" +#include "exec/page-protection.h" +#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" +#include "exec/target_page.h" +#include "exec/tlb-flags.h" +#include "accel/tcg/cpu-ops.h" +#include "accel/tcg/helper-retaddr.h" #include "qemu/int128.h" #include "qemu/atomic128.h" -#include "trace.h" -#if !defined(CONFIG_USER_ONLY) +#if defined(CONFIG_USER_ONLY) +#include "user/page-protection.h" +#else #include "hw/s390x/storage-keys.h" #include "hw/boards.h" #endif @@ -146,7 +153,7 @@ static inline int s390_probe_access(CPUArchState *env, target_ulong addr, int mmu_idx, bool nonfault, void **phost, uintptr_t ra) { - int flags = probe_access_flags(env, addr, 0, access_type, mmu_idx, + int flags = probe_access_flags(env, addr, size, access_type, mmu_idx, nonfault, phost, ra); if (unlikely(flags & TLB_INVALID_MASK)) { @@ -225,10 +232,7 @@ 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 - memset(haddr, byte, size); -#else - if (likely(haddr)) { + if (user_or_likely(haddr)) { memset(haddr, byte, size); } else { MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); @@ -236,20 +240,19 @@ static void do_access_memset(CPUS390XState *env, vaddr vaddr, char *haddr, 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); + clear_helper_retaddr(); } static uint8_t access_get_byte(CPUS390XState *env, S390Access *access, @@ -300,41 +303,39 @@ static void access_memmove(CPUS390XState *env, S390Access *desta, S390Access *srca, uintptr_t ra) { int len = desta->size1 + desta->size2; - int diff; assert(len == srca->size1 + srca->size2); /* Fallback to slow access in case we don't have access to all host pages */ - if (unlikely(!desta->haddr1 || (desta->size2 && !desta->haddr2) || - !srca->haddr1 || (srca->size2 && !srca->haddr2))) { - int i; - - for (i = 0; i < len; i++) { - uint8_t byte = access_get_byte(env, srca, i, ra); - - access_set_byte(env, desta, i, byte, ra); - } - return; - } - - diff = desta->size1 - srca->size1; - if (likely(diff == 0)) { - memmove(desta->haddr1, srca->haddr1, srca->size1); - if (unlikely(srca->size2)) { - memmove(desta->haddr2, srca->haddr2, srca->size2); - } - } else if (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); + if (user_or_likely(desta->haddr1 && + srca->haddr1 && + (!desta->size2 || desta->haddr2) && + (!srca->size2 || srca->haddr2))) { + int diff = desta->size1 - srca->size1; + + if (likely(diff == 0)) { + memmove(desta->haddr1, srca->haddr1, srca->size1); + if (unlikely(srca->size2)) { + memmove(desta->haddr2, srca->haddr2, srca->size2); + } + } else if (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 = -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); + 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); } } } @@ -358,7 +359,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; @@ -372,6 +373,8 @@ static uint32_t do_helper_nc(CPUS390XState *env, uint32_t l, uint64_t dest, 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); @@ -379,6 +382,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; } @@ -392,7 +397,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; @@ -413,6 +418,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); @@ -420,6 +426,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; } @@ -433,7 +440,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; @@ -447,6 +454,8 @@ static uint32_t do_helper_oc(CPUS390XState *env, uint32_t l, uint64_t dest, 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); @@ -454,6 +463,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; } @@ -467,7 +478,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; @@ -490,11 +501,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; @@ -508,7 +521,7 @@ 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; @@ -520,16 +533,18 @@ void HELPER(mvcrl)(CPUS390XState *env, uint64_t l, uint64_t dest, uint64_t src) 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; @@ -540,17 +555,19 @@ void HELPER(mvcin)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) src = wrap_address(env, src - l + 1); 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; @@ -561,18 +578,21 @@ void HELPER(mvn)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) 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; @@ -586,6 +606,8 @@ void HELPER(mvo)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) /* 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); @@ -601,12 +623,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; @@ -617,12 +640,15 @@ void HELPER(mvz)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) 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 */ @@ -669,7 +695,7 @@ uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask, if (!mask) { /* Recognize access exceptions for the first byte */ - probe_read(env, addr, 1, cpu_mmu_index(env, false), ra); + probe_read(env, addr, 1, s390x_env_mmu_index(env, false), ra); } while (mask) { @@ -893,7 +919,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); @@ -946,7 +972,7 @@ uint32_t HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint32_t r1, uint32_t r2) /* 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]; @@ -967,15 +993,19 @@ uint32_t HELPER(mvst)(CPUS390XState *env, uint32_t r1, uint32_t r2) */ 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; @@ -1027,7 +1057,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; @@ -1066,6 +1096,7 @@ static inline uint32_t do_mvcl(CPUS390XState *env, *dest = wrap_address(env, *dest + len); } else { 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++) { @@ -1075,6 +1106,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); } @@ -1084,7 +1116,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); @@ -1742,7 +1774,7 @@ uint32_t HELPER(trXX)(CPUS390XState *env, uint32_t r1, uint32_t r2, 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); @@ -2092,9 +2124,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; @@ -2107,7 +2138,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)) { @@ -2123,10 +2153,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. @@ -2156,18 +2183,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; } /* @@ -2867,12 +2892,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 e85658ce220..f7101be5745 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -20,28 +20,31 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/log.h" #include "cpu.h" #include "s390x-internal.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "qemu/timer.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "exec/cputlb.h" +#include "accel/tcg/cpu-ldst.h" +#include "exec/target_page.h" #include "qapi/error.h" #include "tcg_s390x.h" #include "s390-tod.h" #if !defined(CONFIG_USER_ONLY) -#include "sysemu/cpus.h" -#include "sysemu/sysemu.h" +#include "system/cpus.h" +#include "system/system.h" #include "hw/s390x/ebcdic.h" -#include "hw/s390x/s390-virtio-hcall.h" +#include "hw/s390x/s390-hypercall.h" #include "hw/s390x/sclp.h" #include "hw/s390x/s390_flic.h" #include "hw/s390x/ioinst.h" #include "hw/s390x/s390-pci-inst.h" #include "hw/boards.h" #include "hw/s390x/tod.h" +#include CONFIG_DEVICES #endif /* #define DEBUG_HELPER */ @@ -101,9 +104,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()); } @@ -115,21 +118,24 @@ void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num) uint64_t r; switch (num) { +#ifdef CONFIG_S390_CCW_VIRTIO case 0x500: - /* KVM hypercall */ - qemu_mutex_lock_iothread(); - r = s390_virtio_hypercall(env); - qemu_mutex_unlock_iothread(); + /* QEMU/KVM hypercall */ + bql_lock(); + handle_diag_500(env_archcpu(env), GETPC()); + bql_unlock(); + r = 0; break; +#endif /* CONFIG_S390_CCW_VIRTIO */ case 0x44: /* yield */ r = 0; 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: @@ -185,7 +191,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 */ @@ -207,16 +213,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 */ @@ -229,9 +233,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; } @@ -421,9 +425,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; } @@ -433,92 +437,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) @@ -533,10 +537,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; } @@ -554,7 +558,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; @@ -570,32 +574,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); } } @@ -610,46 +628,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 @@ -726,27 +743,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, @@ -754,18 +771,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()); @@ -776,9 +794,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, @@ -786,9 +804,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, @@ -796,8 +814,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 dc7041e1d87..c7e8574438c 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -31,17 +31,15 @@ #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/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/log.h" #include "qemu/atomic128.h" @@ -141,7 +139,6 @@ struct DisasFields { struct DisasContext { DisasContextBase base; const DisasInsn *insn; - TCGOp *insn_start; DisasFields fields; uint64_t ex_value; /* @@ -199,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]); } @@ -290,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; } @@ -298,7 +295,7 @@ 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; } @@ -319,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) @@ -335,7 +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)); + tcg_gen_st32_i64(v, tcg_env, freg32_offset(reg)); } static void update_psw_addr(DisasContext *s) @@ -344,33 +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_constant_i64(s->pc_tmp) : psw_addr; - gen_helper_per_branch(cpu_env, gbea, 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_constant_i64(s->base.pc_next); - tcg_gen_movcond_i64(cond, gbea, arg1, arg2, gbea, pc); + if (s->base.tb->flags & FLAG_MASK_PER_BRANCH) { + gen_helper_per_branch(tcg_env, dest, tcg_constant_i32(s->ilen)); } #endif } @@ -417,23 +392,22 @@ static int get_mem_index(DisasContext *s) return MMU_HOME_IDX; default: g_assert_not_reached(); - break; } #endif } static void gen_exception(int excp) { - gen_helper_exception(cpu_env, tcg_constant_i32(excp)); + gen_helper_exception(tcg_env, tcg_constant_i32(excp)); } static void gen_program_exception(DisasContext *s, int code) { /* Remember what pgm exception this was. */ - tcg_gen_st_i32(tcg_constant_i32(code), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(code), tcg_env, offsetof(CPUS390XState, int_pgm_code)); - tcg_gen_st_i32(tcg_constant_i32(s->ilen), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(s->ilen), tcg_env, offsetof(CPUS390XState, int_pgm_ilen)); /* update the psw */ @@ -453,7 +427,7 @@ static inline void gen_illegal_opcode(DisasContext *s) static inline void gen_data_exception(uint8_t dxc) { - gen_helper_data_exception(cpu_env, tcg_constant_i32(dxc)); + gen_helper_data_exception(tcg_env, tcg_constant_i32(dxc)); } static inline void gen_trap(DisasContext *s) @@ -620,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: @@ -636,18 +610,18 @@ 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: g_assert_not_reached(); @@ -659,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); } @@ -754,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; @@ -768,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; @@ -854,18 +825,14 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) c->u.s64.a = cc_dst; c->u.s64.b = tcg_constant_i64(0); break; + case CC_OP_LTGT_64: case CC_OP_LTUGTU_64: - c->u.s64.a = cc_src; - c->u.s64.b = cc_dst; - break; - 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_constant_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: @@ -889,67 +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; - switch (mask) { - case 0x8 | 0x4 | 0x2: /* cc != 3 */ - cond = TCG_COND_NE; + + /* 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->u.s32.b = tcg_constant_i32(3); break; - case 0x8 | 0x4 | 0x1: /* cc != 2 */ - cond = TCG_COND_NE; - c->u.s32.b = tcg_constant_i32(2); - break; - case 0x8 | 0x2 | 0x1: /* cc != 1 */ - cond = TCG_COND_NE; - c->u.s32.b = tcg_constant_i32(1); - break; - case 0x8 | 0x2: /* cc == 0 || cc == 2 => (cc & 1) == 0 */ + case 0x2: /* cc == 2 */ cond = TCG_COND_EQ; - c->u.s32.a = tcg_temp_new_i32(); - c->u.s32.b = tcg_constant_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_constant_i32(2); break; - case 0x8: /* cc == 0 */ - cond = TCG_COND_EQ; - c->u.s32.b = tcg_constant_i32(0); - break; - case 0x4 | 0x2 | 0x1: /* cc != 0 */ - cond = TCG_COND_NE; - c->u.s32.b = tcg_constant_i32(0); - break; - case 0x4 | 0x1: /* cc == 1 || cc == 3 => (cc & 1) != 0 */ - cond = TCG_COND_NE; - c->u.s32.a = tcg_temp_new_i32(); - c->u.s32.b = tcg_constant_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_constant_i32(1); break; - case 0x2 | 0x1: /* cc > 1 */ + case 0x2 | 0x1: /* cc == 2 || cc == 3 => cc > 1 */ cond = TCG_COND_GTU; c->u.s32.b = tcg_constant_i32(1); break; - case 0x2: /* cc == 2 */ - cond = TCG_COND_EQ; - c->u.s32.b = tcg_constant_i32(2); + case 0x4 | 0x1: /* cc == 1 || cc == 3 => (cc & 1) != 0 */ + cond = TCG_COND_TSTNE; + c->u.s32.b = tcg_constant_i32(1); break; - case 0x1: /* cc == 3 */ - cond = TCG_COND_EQ; - c->u.s32.b = tcg_constant_i32(3); + 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; - default: - /* CC is masked by something else: (8 >> cc) & mask. */ + case 0x4 | 0x2 | 0x1: /* cc != 0 */ cond = TCG_COND_NE; - c->u.s32.a = tcg_temp_new_i32(); c->u.s32.b = tcg_constant_i32(0); - tcg_gen_shr_i32(c->u.s32.a, tcg_constant_i32(8), cc_op); - tcg_gen_andi_i32(c->u.s32.a, c->u.s32.a, mask); break; + default: + /* case 0: never, handled above. */ + g_assert_not_reached(); + } + if (mask & 8) { + cond = tcg_invert_cond(cond); } break; @@ -1129,144 +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); - - 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); - } + update_cc_op(s); - /* 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); + /* + * 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 { + tcg_gen_brcond_i32(tcg_invert_cond(c->cond), + c->u.s32.a, c->u.s32.b, lab); + } - gen_set_label(lab); - if (is_imm) { - tcg_gen_movi_i64(psw_addr, dest); - } - per_breaking_event(s); - ret = DISAS_PC_UPDATED; - } + /* Branch taken. */ + per_breaking_event(s); + if (is_imm) { + tcg_gen_movi_i64(psw_addr, dest); } 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. */ + tcg_gen_mov_i64(psw_addr, cdest); + } + per_branch(s, psw_addr); - TCGv_i64 next = tcg_constant_i64(s->pc_tmp); - if (is_imm) { - cdest = tcg_constant_i64(dest); - } + 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(); + } - 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_constant_i64(0); - tcg_gen_setcond_i32(c->cond, t0, c->u.s32.a, c->u.s32.b); - tcg_gen_extu_i32_i64(t1, t0); - tcg_gen_movcond_i64(TCG_COND_NE, psw_addr, t1, z, cdest, next); - per_branch_cond(s, TCG_COND_NE, t1, z); - } + gen_set_label(lab); - ret = DISAS_PC_UPDATED; + /* 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; } - - egress: - return ret; + return DISAS_PC_CC_UPDATED; } /* ====================================================================== */ @@ -1343,11 +1249,7 @@ static DisasJumpType op_addc32(DisasContext *s, DisasOps *o) static DisasJumpType op_addc64(DisasContext *s, DisasOps *o) { compute_carry(s); - - 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_gen_addcio_i64(o->out, cc_src, o->in1, o->in2, cc_src); return DISAS_NEXT; } @@ -1398,19 +1300,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_128, cpu_env, o->in1_128, o->in2_128); + gen_helper_axb(o->out_128, tcg_env, o->in1_128, o->in2_128); return DISAS_NEXT; } @@ -1492,9 +1394,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; } @@ -1524,9 +1424,7 @@ 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; } @@ -1546,7 +1444,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 { \ @@ -1734,21 +1632,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->in1_128, o->in2_128); + gen_helper_cxb(cc_op, tcg_env, o->in1_128, o->in2_128); set_cc_static(s); return DISAS_NEXT; } @@ -1785,7 +1683,7 @@ static DisasJumpType op_cfeb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cfeb(o->out, cpu_env, o->in2, m34); + gen_helper_cfeb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1797,7 +1695,7 @@ static DisasJumpType op_cfdb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cfdb(o->out, cpu_env, o->in2, m34); + gen_helper_cfdb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1809,7 +1707,7 @@ static DisasJumpType op_cfxb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cfxb(o->out, cpu_env, o->in2_128, m34); + gen_helper_cfxb(o->out, tcg_env, o->in2_128, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1821,7 +1719,7 @@ static DisasJumpType op_cgeb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cgeb(o->out, cpu_env, o->in2, m34); + gen_helper_cgeb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1833,7 +1731,7 @@ static DisasJumpType op_cgdb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cgdb(o->out, cpu_env, o->in2, m34); + gen_helper_cgdb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1845,7 +1743,7 @@ static DisasJumpType op_cgxb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cgxb(o->out, cpu_env, o->in2_128, m34); + gen_helper_cgxb(o->out, tcg_env, o->in2_128, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1857,7 +1755,7 @@ static DisasJumpType op_clfeb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_clfeb(o->out, cpu_env, o->in2, m34); + gen_helper_clfeb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1869,7 +1767,7 @@ static DisasJumpType op_clfdb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_clfdb(o->out, cpu_env, o->in2, m34); + gen_helper_clfdb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1881,7 +1779,7 @@ static DisasJumpType op_clfxb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_clfxb(o->out, cpu_env, o->in2_128, m34); + gen_helper_clfxb(o->out, tcg_env, o->in2_128, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1893,7 +1791,7 @@ static DisasJumpType op_clgeb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_clgeb(o->out, cpu_env, o->in2, m34); + gen_helper_clgeb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1905,7 +1803,7 @@ static DisasJumpType op_clgdb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_clgdb(o->out, cpu_env, o->in2, m34); + gen_helper_clgdb(o->out, tcg_env, o->in2, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1917,7 +1815,7 @@ static DisasJumpType op_clgxb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_clgxb(o->out, cpu_env, o->in2_128, m34); + gen_helper_clgxb(o->out, tcg_env, o->in2_128, m34); set_cc_static(s); return DISAS_NEXT; } @@ -1929,7 +1827,7 @@ static DisasJumpType op_cegb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cegb(o->out, cpu_env, o->in2, m34); + gen_helper_cegb(o->out, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -1940,7 +1838,7 @@ static DisasJumpType op_cdgb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cdgb(o->out, cpu_env, o->in2, m34); + gen_helper_cdgb(o->out, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -1951,7 +1849,7 @@ static DisasJumpType op_cxgb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cxgb(o->out_128, cpu_env, o->in2, m34); + gen_helper_cxgb(o->out_128, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -1962,7 +1860,7 @@ static DisasJumpType op_celgb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_celgb(o->out, cpu_env, o->in2, m34); + gen_helper_celgb(o->out, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -1973,7 +1871,7 @@ static DisasJumpType op_cdlgb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cdlgb(o->out, cpu_env, o->in2, m34); + gen_helper_cdlgb(o->out, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -1984,7 +1882,7 @@ static DisasJumpType op_cxlgb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_cxlgb(o->out_128, cpu_env, o->in2, m34); + gen_helper_cxlgb(o->out_128, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -1994,7 +1892,7 @@ static DisasJumpType op_cksm(DisasContext *s, DisasOps *o) TCGv_i128 pair = tcg_temp_new_i128(); TCGv_i64 len = tcg_temp_new_i64(); - gen_helper_cksm(pair, 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); tcg_gen_extr_i128_i64(o->out, len, pair); @@ -2007,6 +1905,7 @@ 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; @@ -2016,13 +1915,15 @@ static DisasJumpType op_clc(DisasContext *s, DisasOps *o) case 4: case 8: mop = ctz32(l + 1) | MO_TE; - tcg_gen_qemu_ld_tl(cc_src, o->addr1, get_mem_index(s), mop); + /* 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, cc_src, cc_dst); + gen_op_update2_cc_i64(s, CC_OP_LTUGTU_64, src, cc_dst); return DISAS_NEXT; default: vl = tcg_constant_i32(l); - gen_helper_clc(cc_op, cpu_env, vl, o->addr1, o->in2); + gen_helper_clc(cc_op, tcg_env, vl, o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -2042,7 +1943,7 @@ static DisasJumpType op_clcl(DisasContext *s, DisasOps *o) t1 = tcg_constant_i32(r1); t2 = tcg_constant_i32(r2); - gen_helper_clcl(cc_op, cpu_env, t1, t2); + gen_helper_clcl(cc_op, tcg_env, t1, t2); set_cc_static(s); return DISAS_NEXT; } @@ -2061,7 +1962,7 @@ static DisasJumpType op_clcle(DisasContext *s, DisasOps *o) t1 = tcg_constant_i32(r1); t3 = tcg_constant_i32(r3); - gen_helper_clcle(cc_op, cpu_env, t1, o->in2, t3); + gen_helper_clcle(cc_op, tcg_env, t1, o->in2, t3); set_cc_static(s); return DISAS_NEXT; } @@ -2080,7 +1981,7 @@ static DisasJumpType op_clclu(DisasContext *s, DisasOps *o) t1 = tcg_constant_i32(r1); t3 = tcg_constant_i32(r3); - gen_helper_clclu(cc_op, cpu_env, t1, o->in2, t3); + gen_helper_clclu(cc_op, tcg_env, t1, o->in2, t3); set_cc_static(s); return DISAS_NEXT; } @@ -2091,7 +1992,7 @@ static DisasJumpType op_clm(DisasContext *s, DisasOps *o) 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); return DISAS_NEXT; } @@ -2100,7 +2001,7 @@ static DisasJumpType op_clst(DisasContext *s, DisasOps *o) { TCGv_i128 pair = tcg_temp_new_i128(); - gen_helper_clst(pair, cpu_env, regs[0], o->in1, o->in2); + 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); @@ -2169,9 +2070,9 @@ static DisasJumpType op_csst(DisasContext *s, DisasOps *o) 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); } set_cc_static(s); @@ -2213,13 +2114,29 @@ static DisasJumpType op_csp(DisasContext *s, DisasOps *o) tcg_gen_and_i64(cc, cc, o->in2); tcg_gen_brcondi_i64(TCG_COND_EQ, cc, 0, lab); - 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(); @@ -2230,6 +2147,14 @@ static DisasJumpType op_cvd(DisasContext *s, DisasOps *o) 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; +} + static DisasJumpType op_ct(DisasContext *s, DisasOps *o) { int m3 = get_field(s, m3); @@ -2271,22 +2196,22 @@ static DisasJumpType op_cuXX(DisasContext *s, DisasOps *o) 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(); @@ -2303,21 +2228,21 @@ static DisasJumpType op_diag(DisasContext *s, DisasOps *o) 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); + 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->out, cpu_env, o->in1, o->in2); + 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->out, cpu_env, o->in1, o->in2); + gen_helper_divu32(o->out, tcg_env, o->in1, o->in2); tcg_gen_extr32_i64(o->out2, o->out, o->out); return DISAS_NEXT; } @@ -2326,7 +2251,7 @@ static DisasJumpType op_divs64(DisasContext *s, DisasOps *o) { TCGv_i128 t = tcg_temp_new_i128(); - gen_helper_divs64(t, cpu_env, o->in1, o->in2); + gen_helper_divs64(t, tcg_env, o->in1, o->in2); tcg_gen_extr_i128_i64(o->out2, o->out, t); return DISAS_NEXT; } @@ -2335,33 +2260,33 @@ static DisasJumpType op_divu64(DisasContext *s, DisasOps *o) { TCGv_i128 t = tcg_temp_new_i128(); - gen_helper_divu64(t, cpu_env, o->out, o->out2, o->in2); + 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_128, cpu_env, o->in1_128, o->in2_128); + 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; } @@ -2374,7 +2299,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; } @@ -2420,7 +2345,7 @@ static DisasJumpType op_ex(DisasContext *s, DisasOps *o) } ilen = tcg_constant_i32(s->ilen); - gen_helper_ex(cpu_env, ilen, v1, o->in2); + gen_helper_ex(tcg_env, ilen, v1, o->in2); return DISAS_PC_CC_UPDATED; } @@ -2432,7 +2357,7 @@ static DisasJumpType op_fieb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_fieb(o->out, cpu_env, o->in2, m34); + gen_helper_fieb(o->out, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -2443,7 +2368,7 @@ static DisasJumpType op_fidb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_fidb(o->out, cpu_env, o->in2, m34); + gen_helper_fidb(o->out, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -2454,7 +2379,7 @@ static DisasJumpType op_fixb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_fixb(o->out_128, cpu_env, o->in2_128, m34); + gen_helper_fixb(o->out_128, tcg_env, o->in2_128, m34); return DISAS_NEXT; } @@ -2575,7 +2500,7 @@ static DisasJumpType op_idte(DisasContext *s, DisasOps *o) } else { m4 = tcg_constant_i32(0); } - gen_helper_idte(cpu_env, o->in1, o->in2, m4); + gen_helper_idte(tcg_env, o->in1, o->in2, m4); return DISAS_NEXT; } @@ -2588,13 +2513,13 @@ static DisasJumpType op_ipte(DisasContext *s, DisasOps *o) } else { m4 = tcg_constant_i32(0); } - gen_helper_ipte(cpu_env, o->in1, o->in2, 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 @@ -2648,43 +2573,58 @@ static DisasJumpType op_msa(DisasContext *s, DisasOps *o) t_r2 = tcg_constant_i32(r2); t_r3 = tcg_constant_i32(r3); type = tcg_constant_i32(s->insn->data); - gen_helper_msa(cc_op, cpu_env, t_r1, t_r2, t_r3, type); + gen_helper_msa(cc_op, tcg_env, t_r1, t_r2, t_r3, type); set_cc_static(s); 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->in1_128, o->in2_128); + 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; @@ -2720,7 +2660,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; } @@ -2731,7 +2671,7 @@ static DisasJumpType op_ledb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_ledb(o->out, cpu_env, o->in2, m34); + gen_helper_ledb(o->out, tcg_env, o->in2, m34); return DISAS_NEXT; } @@ -2742,7 +2682,7 @@ static DisasJumpType op_ldxb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_ldxb(o->out, cpu_env, o->in2_128, m34); + gen_helper_ldxb(o->out, tcg_env, o->in2_128, m34); return DISAS_NEXT; } @@ -2753,19 +2693,19 @@ static DisasJumpType op_lexb(DisasContext *s, DisasOps *o) if (!m34) { return DISAS_NORETURN; } - gen_helper_lexb(o->out, cpu_env, o->in2_128, 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_128, cpu_env, o->in2); + 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_128, cpu_env, o->in2); + gen_helper_lxeb(o->out_128, tcg_env, o->in2); return DISAS_NEXT; } @@ -2919,7 +2859,7 @@ static DisasJumpType op_lctl(DisasContext *s, DisasOps *o) TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); TCGv_i32 r3 = tcg_constant_i32(get_field(s, r3)); - gen_helper_lctl(cpu_env, r1, o->in2, 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; @@ -2930,7 +2870,7 @@ static DisasJumpType op_lctlg(DisasContext *s, DisasOps *o) TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); TCGv_i32 r3 = tcg_constant_i32(get_field(s, r3)); - gen_helper_lctlg(cpu_env, r1, o->in2, 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; @@ -2938,14 +2878,14 @@ static DisasJumpType op_lctlg(DisasContext *s, DisasOps *o) static DisasJumpType op_lra(DisasContext *s, DisasOps *o) { - gen_helper_lra(o->out, cpu_env, o->out, 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; } @@ -2965,7 +2905,7 @@ static DisasJumpType op_lpsw(DisasContext *s, DisasOps *o) 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(cpu_env, mask, addr); + gen_helper_load_psw(tcg_env, mask, addr); return DISAS_NORETURN; } @@ -2981,7 +2921,7 @@ static DisasJumpType op_lpswe(DisasContext *s, DisasOps *o) MO_TEUQ | MO_ALIGN_8); tcg_gen_addi_i64(o->in2, o->in2, 8); tcg_gen_qemu_ld_i64(t2, o->in2, get_mem_index(s), MO_TEUQ); - gen_helper_load_psw(cpu_env, t1, t2); + gen_helper_load_psw(tcg_env, t1, t2); return DISAS_NORETURN; } #endif @@ -2991,7 +2931,7 @@ static DisasJumpType op_lam(DisasContext *s, DisasOps *o) TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); TCGv_i32 r3 = tcg_constant_i32(get_field(s, r3)); - gen_helper_lam(cpu_env, r1, o->in2, r3); + gen_helper_lam(tcg_env, r1, o->in2, r3); return DISAS_NEXT; } @@ -3185,7 +3125,7 @@ static DisasJumpType op_mc(DisasContext *s, DisasOps *o) } #if !defined(CONFIG_USER_ONLY) - gen_helper_monitor_call(cpu_env, o->addr1, + gen_helper_monitor_call(tcg_env, o->addr1, tcg_constant_i32(monitor_class)); #endif /* Defaults to a NOP. */ @@ -3203,6 +3143,7 @@ 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->in2 = NULL; @@ -3216,7 +3157,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); } @@ -3226,7 +3167,7 @@ static DisasJumpType op_mov2e(DisasContext *s, DisasOps *o) break; } - tcg_gen_st32_i64(ar1, cpu_env, offsetof(CPUS390XState, aregs[1])); + tcg_gen_st32_i64(ar1, tcg_env, offsetof(CPUS390XState, aregs[r1])); return DISAS_NEXT; } @@ -3243,13 +3184,13 @@ static DisasJumpType op_mvc(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); - gen_helper_mvc(cpu_env, l, o->addr1, o->in2); + 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; } @@ -3257,7 +3198,7 @@ static DisasJumpType op_mvcin(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); - gen_helper_mvcin(cpu_env, l, o->addr1, o->in2); + gen_helper_mvcin(tcg_env, l, o->addr1, o->in2); return DISAS_NEXT; } @@ -3275,7 +3216,7 @@ static DisasJumpType op_mvcl(DisasContext *s, DisasOps *o) t1 = tcg_constant_i32(r1); t2 = tcg_constant_i32(r2); - gen_helper_mvcl(cc_op, cpu_env, t1, t2); + gen_helper_mvcl(cc_op, tcg_env, t1, t2); set_cc_static(s); return DISAS_NEXT; } @@ -3294,7 +3235,7 @@ static DisasJumpType op_mvcle(DisasContext *s, DisasOps *o) t1 = tcg_constant_i32(r1); t3 = tcg_constant_i32(r3); - gen_helper_mvcle(cc_op, cpu_env, t1, o->in2, t3); + gen_helper_mvcle(cc_op, tcg_env, t1, o->in2, t3); set_cc_static(s); return DISAS_NEXT; } @@ -3313,7 +3254,7 @@ static DisasJumpType op_mvclu(DisasContext *s, DisasOps *o) t1 = tcg_constant_i32(r1); t3 = tcg_constant_i32(r3); - gen_helper_mvclu(cc_op, cpu_env, t1, o->in2, t3); + gen_helper_mvclu(cc_op, tcg_env, t1, o->in2, t3); set_cc_static(s); return DISAS_NEXT; } @@ -3321,7 +3262,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; } @@ -3331,7 +3272,7 @@ static DisasJumpType op_mvcp(DisasContext *s, DisasOps *o) { int r1 = get_field(s, l1); int r3 = get_field(s, r3); - gen_helper_mvcp(cc_op, cpu_env, regs[r1], o->addr1, o->in2, regs[r3]); + gen_helper_mvcp(cc_op, tcg_env, regs[r1], o->addr1, o->in2, regs[r3]); set_cc_static(s); return DISAS_NEXT; } @@ -3340,7 +3281,7 @@ static DisasJumpType op_mvcs(DisasContext *s, DisasOps *o) { int r1 = get_field(s, l1); int r3 = get_field(s, r3); - gen_helper_mvcs(cc_op, cpu_env, regs[r1], o->addr1, o->in2, regs[r3]); + gen_helper_mvcs(cc_op, tcg_env, regs[r1], o->addr1, o->in2, regs[r3]); set_cc_static(s); return DISAS_NEXT; } @@ -3350,7 +3291,7 @@ static DisasJumpType op_mvn(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); - gen_helper_mvn(cpu_env, l, o->addr1, o->in2); + gen_helper_mvn(tcg_env, l, o->addr1, o->in2); return DISAS_NEXT; } @@ -3358,7 +3299,7 @@ static DisasJumpType op_mvo(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); - gen_helper_mvo(cpu_env, l, o->addr1, o->in2); + gen_helper_mvo(tcg_env, l, o->addr1, o->in2); return DISAS_NEXT; } @@ -3367,7 +3308,7 @@ static DisasJumpType op_mvpg(DisasContext *s, DisasOps *o) 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); + gen_helper_mvpg(cc_op, tcg_env, regs[0], t1, t2); set_cc_static(s); return DISAS_NEXT; } @@ -3377,7 +3318,7 @@ static DisasJumpType op_mvst(DisasContext *s, DisasOps *o) 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); + gen_helper_mvst(cc_op, tcg_env, t1, t2); set_cc_static(s); return DISAS_NEXT; } @@ -3386,7 +3327,7 @@ static DisasJumpType op_mvz(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); - gen_helper_mvz(cpu_env, l, o->addr1, o->in2); + gen_helper_mvz(tcg_env, l, o->addr1, o->in2); return DISAS_NEXT; } @@ -3410,59 +3351,59 @@ 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_128, cpu_env, o->in1_128, o->in2_128); + 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_128, cpu_env, o->in1, o->in2); + 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); + 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); + 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); + 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); + gen_helper_msdb(o->out, tcg_env, o->in1, o->in2, r3); return DISAS_NEXT; } @@ -3499,7 +3440,7 @@ static DisasJumpType op_nc(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); - gen_helper_nc(cc_op, cpu_env, l, o->addr1, o->in2); + gen_helper_nc(cc_op, tcg_env, l, o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -3533,7 +3474,7 @@ static DisasJumpType op_oc(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); - gen_helper_oc(cc_op, cpu_env, l, o->addr1, o->in2); + gen_helper_oc(cc_op, tcg_env, l, o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -3585,7 +3526,7 @@ static DisasJumpType op_pack(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); - gen_helper_pack(cpu_env, l, o->addr1, o->in2); + gen_helper_pack(tcg_env, l, o->addr1, o->in2); return DISAS_NEXT; } @@ -3600,7 +3541,7 @@ static DisasJumpType op_pka(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } l = tcg_constant_i32(l2); - gen_helper_pka(cpu_env, o->addr1, o->in2, l); + gen_helper_pka(tcg_env, o->addr1, o->in2, l); return DISAS_NEXT; } @@ -3615,7 +3556,7 @@ static DisasJumpType op_pku(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } l = tcg_constant_i32(l2); - gen_helper_pku(cpu_env, o->addr1, o->in2, l); + gen_helper_pku(tcg_env, o->addr1, o->in2, l); return DISAS_NEXT; } @@ -3634,7 +3575,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 @@ -3822,14 +3763,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; } @@ -3872,50 +3813,50 @@ 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_128, cpu_env, o->in1_128, o->in2_128); + 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_128, cpu_env, o->in2_128); + 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; } @@ -3925,7 +3866,7 @@ static DisasJumpType op_sigp(DisasContext *s, DisasOps *o) 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, cpu_env, o->in2, r1, r3); + gen_helper_sigp(cc_op, tcg_env, o->in2, r1, r3); set_cc_static(s); return DISAS_NEXT; } @@ -4013,13 +3954,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; } @@ -4027,7 +3968,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; } @@ -4035,7 +3976,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; } @@ -4047,9 +3988,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_gen_st32_i64(tmp, tcg_env, offsetof(CPUS390XState, fpc)); return DISAS_NEXT; } @@ -4085,7 +4026,7 @@ static DisasJumpType op_ectg(DisasContext *s, DisasOps *o) 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 */ @@ -4103,7 +4044,7 @@ 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; } @@ -4131,14 +4072,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; @@ -4149,9 +4090,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 @@ -4171,26 +4112,26 @@ 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; } @@ -4199,7 +4140,7 @@ static DisasJumpType op_stctg(DisasContext *s, DisasOps *o) TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); TCGv_i32 r3 = tcg_constant_i32(get_field(s, r3)); - gen_helper_stctg(cpu_env, r1, o->in2, r3); + gen_helper_stctg(tcg_env, r1, o->in2, r3); return DISAS_NEXT; } @@ -4208,98 +4149,98 @@ static DisasJumpType op_stctl(DisasContext *s, DisasOps *o) TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); TCGv_i32 r3 = tcg_constant_i32(get_field(s, r3)); - gen_helper_stctl(cpu_env, r1, o->in2, 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; } @@ -4318,49 +4259,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; } @@ -4395,9 +4336,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; } @@ -4405,7 +4348,7 @@ 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; } @@ -4441,7 +4384,7 @@ static DisasJumpType op_stam(DisasContext *s, DisasOps *o) TCGv_i32 r1 = tcg_constant_i32(get_field(s, r1)); TCGv_i32 r3 = tcg_constant_i32(get_field(s, r3)); - gen_helper_stam(cpu_env, r1, o->in2, r3); + gen_helper_stam(tcg_env, r1, o->in2, r3); return DISAS_NEXT; } @@ -4548,7 +4491,7 @@ static DisasJumpType op_srst(DisasContext *s, DisasOps *o) 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); + gen_helper_srst(tcg_env, r1, r2); set_cc_static(s); return DISAS_NEXT; } @@ -4558,7 +4501,7 @@ static DisasJumpType op_srstu(DisasContext *s, DisasOps *o) 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); + gen_helper_srstu(tcg_env, r1, r2); set_cc_static(s); return DISAS_NEXT; } @@ -4631,10 +4574,10 @@ static DisasJumpType op_svc(DisasContext *s, DisasOps *o) update_cc_op(s); t = tcg_constant_i32(get_field(s, i1) & 0xff); - tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, int_svc_code)); + tcg_gen_st_i32(t, tcg_env, offsetof(CPUS390XState, int_svc_code)); t = tcg_constant_i32(s->ilen); - tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, int_svc_ilen)); + tcg_gen_st_i32(t, tcg_env, offsetof(CPUS390XState, int_svc_ilen)); gen_exception(EXCP_SVC); return DISAS_NORETURN; @@ -4652,21 +4595,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->in1_128, o->in2); + gen_helper_tcxb(cc_op, tcg_env, o->in1_128, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -4675,14 +4618,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; } @@ -4693,7 +4636,7 @@ static DisasJumpType op_tp(DisasContext *s, DisasOps *o) { TCGv_i32 l1 = tcg_constant_i32(get_field(s, l1) + 1); - gen_helper_tp(cc_op, cpu_env, o->addr1, l1); + gen_helper_tp(cc_op, tcg_env, o->addr1, l1); set_cc_static(s); return DISAS_NEXT; } @@ -4702,7 +4645,7 @@ static DisasJumpType op_tr(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); - gen_helper_tr(cpu_env, l, o->addr1, o->in2); + gen_helper_tr(tcg_env, l, o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -4711,7 +4654,7 @@ static DisasJumpType op_tre(DisasContext *s, DisasOps *o) { TCGv_i128 pair = tcg_temp_new_i128(); - gen_helper_tre(pair, cpu_env, o->out, o->out2, o->in2); + 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; @@ -4721,7 +4664,7 @@ static DisasJumpType op_trt(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); - gen_helper_trt(cc_op, cpu_env, l, o->addr1, o->in2); + gen_helper_trt(cc_op, tcg_env, l, o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -4730,7 +4673,7 @@ static DisasJumpType op_trtr(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); - gen_helper_trtr(cc_op, cpu_env, l, o->addr1, o->in2); + gen_helper_trtr(cc_op, tcg_env, l, o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -4756,7 +4699,7 @@ 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); set_cc_static(s); return DISAS_NEXT; @@ -4764,9 +4707,10 @@ static DisasJumpType op_trXX(DisasContext *s, DisasOps *o) static DisasJumpType op_ts(DisasContext *s, DisasOps *o) { - TCGv_i32 t1 = tcg_constant_i32(0xff); + TCGv_i32 ff = tcg_constant_i32(0xff); + TCGv_i32 t1 = tcg_temp_new_i32(); - tcg_gen_atomic_xchg_i32(t1, o->in2, t1, get_mem_index(s), MO_UB); + tcg_gen_atomic_xchg_i32(t1, o->in2, ff, get_mem_index(s), MO_UB); tcg_gen_extract_i32(cc_op, t1, 7, 1); set_cc_static(s); return DISAS_NEXT; @@ -4776,7 +4720,7 @@ static DisasJumpType op_unpk(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_constant_i32(get_field(s, l1)); - gen_helper_unpk(cpu_env, l, o->addr1, o->in2); + gen_helper_unpk(tcg_env, l, o->addr1, o->in2); return DISAS_NEXT; } @@ -4791,7 +4735,7 @@ static DisasJumpType op_unpka(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } l = tcg_constant_i32(l1); - gen_helper_unpka(cc_op, cpu_env, o->addr1, l, o->in2); + gen_helper_unpka(cc_op, tcg_env, o->addr1, l, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -4807,7 +4751,7 @@ static DisasJumpType op_unpku(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } l = tcg_constant_i32(l1); - gen_helper_unpku(cc_op, cpu_env, o->addr1, l, o->in2); + gen_helper_unpku(cc_op, tcg_env, o->addr1, l, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -4860,7 +4804,7 @@ 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_constant_i32(l); - gen_helper_xc(cc_op, cpu_env, t32, o->addr1, o->in2); + gen_helper_xc(cc_op, tcg_env, t32, o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; } @@ -4926,7 +4870,7 @@ static DisasJumpType op_clp(DisasContext *s, DisasOps *o) { TCGv_i32 r2 = tcg_constant_i32(get_field(s, r2)); - gen_helper_clp(cpu_env, r2); + gen_helper_clp(tcg_env, r2); set_cc_static(s); return DISAS_NEXT; } @@ -4936,7 +4880,7 @@ static DisasJumpType op_pcilg(DisasContext *s, DisasOps *o) 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); + gen_helper_pcilg(tcg_env, r1, r2); set_cc_static(s); return DISAS_NEXT; } @@ -4946,7 +4890,7 @@ static DisasJumpType op_pcistg(DisasContext *s, DisasOps *o) 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); + gen_helper_pcistg(tcg_env, r1, r2); set_cc_static(s); return DISAS_NEXT; } @@ -4956,14 +4900,14 @@ static DisasJumpType op_stpcifc(DisasContext *s, DisasOps *o) 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); + 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; } @@ -4972,7 +4916,7 @@ static DisasJumpType op_rpcit(DisasContext *s, DisasOps *o) 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); + gen_helper_rpcit(tcg_env, r1, r2); set_cc_static(s); return DISAS_NEXT; } @@ -4983,7 +4927,7 @@ static DisasJumpType op_pcistb(DisasContext *s, DisasOps *o) 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); + gen_helper_pcistb(tcg_env, r1, r3, o->addr1, ar); set_cc_static(s); return DISAS_NEXT; } @@ -4993,7 +4937,7 @@ static DisasJumpType op_mpcifc(DisasContext *s, DisasOps *o) 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); + gen_helper_mpcifc(tcg_env, r1, o->addr1, ar); set_cc_static(s); return DISAS_NEXT; } @@ -6175,20 +6119,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. */ - tcg_gen_st_i64(tcg_constant_i64(0), cpu_env, + 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; + op = insn >> 56; /* Register insn bytes with translator so plugins work. */ - for (int i = 0; i < ilen; i++) { - uint8_t byte = extract64(insn, 56 - (i * 8), 8); - translator_fake_ldb(byte, pc + i); - } - op = insn >> 56; + 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; @@ -6296,7 +6240,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) { @@ -6308,9 +6252,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_constant_i64(s->base.pc_next); - gen_helper_per_ifetch(cpu_env, 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 @@ -6392,15 +6336,16 @@ 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; } } + if (insn->help_wout) { + insn->help_wout(s, &o); + } + if (insn->help_cout) { + insn->help_cout(s, &o); + } /* io should be the last instruction in tb when icount is enabled */ if (unlikely(icount && ret == DISAS_NEXT)) { @@ -6408,14 +6353,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 @@ -6437,7 +6387,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) @@ -6450,27 +6400,26 @@ static void s390x_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) /* Delay the set of ilen until we've read the insn. */ tcg_gen_insn_start(dc->base.pc_next, dc->cc_op, 0); - dc->insn_start = tcg_last_op(); } static target_ulong get_next_pc(CPUS390XState *env, DisasContext *s, uint64_t pc) { - uint64_t insn = cpu_lduw_code(env, pc); + 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); if (dc->base.is_jmp == DISAS_NEXT) { if (dc->ex_value || - !is_same_page(dcbase, dc->base.pc_next) || - !is_same_page(dcbase, get_next_pc(env, dc, dc->base.pc_next))) { + !translator_is_same_page(dcbase, dc->base.pc_next) || + !translator_is_same_page(dcbase, get_next_pc(env, dc, dc->base.pc_next))) { dc->base.is_jmp = DISAS_TOO_MANY; } } @@ -6504,18 +6453,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 = { @@ -6527,8 +6476,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 s390x_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc; @@ -6539,8 +6488,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 ec94d39df06..e073e5ad3aa 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). @@ -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,16 +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_gen_add_ptr(ptr, ptr, tcg_env); } #define gen_gvec_2(v1, v2, gen) \ @@ -754,8 +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_gen_addi_ptr(a0, tcg_env, v1_offs); + gen_helper_vll(tcg_env, a0, o->addr1, bytes); return DISAS_NEXT; } @@ -812,8 +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_gen_addi_ptr(a0, tcg_env, v1_offs); + gen_helper_vll(tcg_env, a0, o->addr1, o->in2); return DISAS_NEXT; } @@ -898,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]); @@ -906,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]); @@ -1058,7 +1058,7 @@ static DisasJumpType op_vst(DisasContext *s, DisasOps *o) TCGv_i64 tmp; /* Probe write access before actually modifying memory */ - gen_helper_probe_write_access(cpu_env, o->addr1, + gen_helper_probe_write_access(tcg_env, o->addr1, tcg_constant_i64(16)); tmp = tcg_temp_new_i64(); @@ -1098,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(); @@ -1169,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(); @@ -1211,7 +1211,7 @@ static DisasJumpType op_vstm(DisasContext *s, DisasOps *o) } /* Probe write access before actually modifying memory */ - gen_helper_probe_write_access(cpu_env, o->addr1, + gen_helper_probe_write_access(tcg_env, o->addr1, tcg_constant_i64((v3 - v1 + 1) * 16)); tmp = tcg_temp_new_i64(); @@ -1236,8 +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_gen_addi_ptr(a0, tcg_env, v1_offs); + gen_helper_vstl(tcg_env, a0, o->addr1, o->in2); return DISAS_NEXT; } @@ -2479,7 +2479,7 @@ static DisasJumpType op_vsum(DisasContext *s, DisasOps *o) 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; } @@ -2505,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), @@ -2536,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), @@ -2567,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), @@ -2598,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, @@ -2641,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 { @@ -2682,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; } @@ -2780,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; } @@ -2822,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; } @@ -2893,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); } @@ -3007,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; } @@ -3036,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; } @@ -3080,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; } @@ -3169,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; } @@ -3291,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; } @@ -3325,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 75cf605b9f4..744f800fb6c 100644 --- a/target/s390x/tcg/vec_fpu_helper.c +++ b/target/s390x/tcg/vec_fpu_helper.c @@ -15,7 +15,6 @@ #include "vec.h" #include "tcg_s390x.h" #include "tcg/tcg-gvec-desc.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" @@ -621,8 +620,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 +644,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 +663,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 dafc4c3582c..46ec4a947dd 100644 --- a/target/s390x/tcg/vec_helper.c +++ b/target/s390x/tcg/vec_helper.c @@ -16,8 +16,7 @@ #include "tcg/tcg.h" #include "tcg/tcg-gvec-desc.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" -#include "exec/exec-all.h" +#include "accel/tcg/cpu-ldst.h" void HELPER(gvec_vbperm)(void *v1, const void *v2, const void *v3, uint32_t desc) diff --git a/target/s390x/tcg/vec_int_helper.c b/target/s390x/tcg/vec_int_helper.c index 53ab5c5eb31..b18d8a6d16e 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; \ + +/* + * 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) +{ + return clmul_8x4_even(n, m) ^ clmul_8x4_odd(n, m) ^ a; } -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) +void HELPER(gvec_vgfm8)(void *v1, const void *v2, const void *v3, uint32_t d) { - 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; + 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); } -#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_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]); } -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) +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) { - S390Vector tmp1, tmp2; - uint64_t a, b; + uint64_t *q1 = v1; + const uint64_t *q2 = v2, *q3 = v3; - 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); + q1[0] = do_gfma16(q2[0], q3[0], 0); + q1[1] = do_gfma16(q2[1], q3[1], 0); } -#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); \ - } \ +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]); +} + +void HELPER(gvec_vgfm64)(void *v1, const void *v2, const void *v3, + uint32_t desc) +{ + uint64_t *q1 = v1; + const uint64_t *q2 = v2, *q3 = v3; + Int128 r; + + 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); } -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; - - 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); + uint64_t *q1 = v1; + const uint64_t *q2 = v2, *q3 = v3, *q4 = v4; + Int128 r; + + 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/trace-events b/target/s390x/trace-events index 729cb012b41..ef3120d3b16 100644 --- a/target/s390x/trace-events +++ b/target/s390x/trace-events @@ -1,16 +1,12 @@ # 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)" ioinst_chp_id(const char *insn, int cssid, int chpid) "IOINST: %s (%x.%02x)" ioinst_chsc_cmd(uint16_t cmd, uint16_t len) "IOINST: chsc command 0x%04x, len 0x%04x" -# cpu-sysemu.c +# cpu-system.c cpu_set_state(int cpu_index, uint8_t state) "setting cpu %d state to %" PRIu8 cpu_halt(int cpu_index) "halting cpu %d" cpu_unhalt(int cpu_index) "unhalting cpu %d" diff --git a/target/sh4/Kconfig b/target/sh4/Kconfig index 2397c860280..93b92f1e480 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 a7cdb7edb66..f328715ee86 100644 --- a/target/sh4/cpu-param.h +++ b/target/sh4/cpu-param.h @@ -2,13 +2,12 @@ * 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 #define SH4_CPU_PARAM_H -#define TARGET_LONG_BITS 32 #define TARGET_PAGE_BITS 12 /* 4k */ #define TARGET_PHYS_ADDR_SPACE_BITS 32 #ifdef CONFIG_USER_ONLY @@ -17,4 +16,6 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif +#define TARGET_INSN_START_EXTRA_WORDS 1 + #endif diff --git a/target/sh4/cpu-qom.h b/target/sh4/cpu-qom.h index 89785a90f02..6cf5fbb0745 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_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 { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - ResettablePhases parent_phases; - - 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 61769ffdfae..4f561e8c912 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -24,8 +24,9 @@ #include "qemu/qemu-print.h" #include "cpu.h" #include "migration/vmstate.h" -#include "exec/exec-all.h" +#include "exec/translation-block.h" #include "fpu/softfloat-helpers.h" +#include "accel/tcg/cpu-ops.h" #include "tcg/tcg.h" static void superh_cpu_set_pc(CPUState *cs, vaddr value) @@ -42,12 +43,35 @@ static vaddr superh_cpu_get_pc(CPUState *cs) return cpu->env.pc; } +static TCGTBCPUState superh_get_tb_cpu_state(CPUState *cs) +{ + CPUSH4State *env = cpu_env(cs); + uint32_t flags; + + flags = env->flags + | (env->fpscr & TB_FLAG_FPSCR_MASK) + | (env->sr & TB_FLAG_SR_MASK) + | (env->movcal_backup ? TB_FLAG_PENDING_MOVCA : 0); /* Bit 3 */ +#ifdef CONFIG_USER_ONLY + flags |= TB_FLAG_UNALIGN * !cs->prctl_unalign_sigbus; +#endif + + return (TCGTBCPUState){ + .pc = env->pc, + .flags = flags, +#ifdef CONFIG_USER_ONLY + /* For a gUSA region, notice the end of the region. */ + .cs_base = flags & TB_FLAG_GUSA_MASK ? env->gregs[0] : 0, +#endif + }; +} + static void superh_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { SuperHCPU *cpu = SUPERH_CPU(cs); - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); cpu->env.pc = tb->pc; cpu->env.flags = tb->flags & TB_FLAG_ENVFLAGS_MASK; } @@ -71,33 +95,46 @@ 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)) - && !(cs->tcg_cflags & CF_PCREL) && env->pc != tb->pc) { + && !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; } return false; } -#endif static bool superh_cpu_has_work(CPUState *cs) { return cs->interrupt_request & CPU_INTERRUPT_HARD; } +#endif /* !CONFIG_USER_ONLY */ + +static int sh4_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + CPUSH4State *env = cpu_env(cs); + + /* + * 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) +static void superh_cpu_reset_hold(Object *obj, ResetType type) { - CPUState *s = CPU(obj); - SuperHCPU *cpu = SUPERH_CPU(s); - SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(cpu); - CPUSH4State *env = &cpu->env; + 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); + scc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUSH4State, end_reset_fields)); @@ -114,31 +151,27 @@ static void superh_cpu_reset_hold(Object *obj) set_flush_to_zero(1, &env->fp_status); #endif set_default_nan_mode(1, &env->fp_status); + set_snan_bit_is_one(true, &env->fp_status); + /* sign bit clear, set all frac bits other than msb */ + set_float_default_nan_pattern(0b00111111, &env->fp_status); + /* + * TODO: "SH-4 CPU Core Architecture ADCS 7182230F" doesn't say whether + * it detects tininess before or after rounding. Section 6.4 is clear + * that flush-to-zero happens when the result underflows, though, so + * either this should be "detect ftz after rounding" or else we should + * be setting "detect tininess before rounding". + */ + set_float_ftz_detection(float_ftz_before_rounding, &env->fp_status); } static void superh_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) { + info->endian = TARGET_BIG_ENDIAN ? BFD_ENDIAN_BIG + : BFD_ENDIAN_LITTLE; info->mach = bfd_mach_sh4; 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; @@ -152,9 +185,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); @@ -164,14 +194,13 @@ static ObjectClass *superh_cpu_class_by_name(const char *cpu_model) 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; } -static void sh7750r_class_init(ObjectClass *oc, void *data) +static void sh7750r_class_init(ObjectClass *oc, const void *data) { SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); @@ -182,14 +211,13 @@ 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; } -static void sh7751r_class_init(ObjectClass *oc, void *data) +static void sh7751r_class_init(ObjectClass *oc, const void *data) { SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); @@ -200,14 +228,13 @@ 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; } -static void sh7785_class_init(ObjectClass *oc, void *data) +static void sh7785_class_init(ObjectClass *oc, const void *data) { SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); @@ -236,10 +263,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); } @@ -253,27 +277,36 @@ static const VMStateDescription vmstate_sh_cpu = { #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps sh4_sysemu_ops = { + .has_work = superh_cpu_has_work, .get_phys_page_debug = superh_cpu_get_phys_page_debug, }; #endif -#include "hw/core/tcg-cpu-ops.h" +static const TCGCPUOps superh_tcg_ops = { + /* MTTCG not yet supported: require strict ordering */ + .guest_default_memory_order = TCG_MO_ALL, + .mttcg_supported = false, -static const struct TCGCPUOps superh_tcg_ops = { .initialize = sh4_translate_init, + .translate_code = sh4_translate_code, + .get_tb_cpu_state = superh_get_tb_cpu_state, .synchronize_from_tb = superh_cpu_synchronize_from_tb, .restore_state_to_opc = superh_restore_state_to_opc, + .mmu_index = sh4_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill = superh_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_notreached, .cpu_exec_interrupt = superh_cpu_exec_interrupt, + .cpu_exec_halt = superh_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = superh_cpu_do_interrupt, .do_unaligned_access = superh_cpu_do_unaligned_access, .io_recompile_replay_branch = superh_io_recompile_replay_branch, #endif /* !CONFIG_USER_ONLY */ }; -static void superh_cpu_class_init(ObjectClass *oc, void *data) +static void superh_cpu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); @@ -287,7 +320,6 @@ static void superh_cpu_class_init(ObjectClass *oc, void *data) &scc->parent_phases); cc->class_by_name = superh_cpu_class_by_name; - cc->has_work = superh_cpu_has_work; cc->dump_state = superh_cpu_dump_state; cc->set_pc = superh_cpu_set_pc; cc->get_pc = superh_cpu_get_pc; @@ -315,6 +347,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 1399d3840fd..c41ab70dd7c 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -21,7 +21,9 @@ #define SH4_CPU_H #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" /* CPU Subtypes */ @@ -107,26 +109,24 @@ 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 #define ITLB_SIZE 4 -#define TARGET_INSN_START_EXTRA_WORDS 1 - enum sh_features { SH_FEATURE_SH4A = 1, SH_FEATURE_BCR3_AND_BCR4 = 2, @@ -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,14 +214,31 @@ 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); int superh_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); @@ -221,7 +248,8 @@ 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); +void sh4_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); #if !defined(CONFIG_USER_ONLY) hwaddr superh_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); @@ -253,26 +281,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" /* MMU control register */ #define MMUCR 0x1F000010 @@ -368,19 +380,4 @@ 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, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *pc = env->pc; - /* For a gUSA region, notice the end of the region. */ - *cs_base = env->flags & TB_FLAG_GUSA_MASK ? env->gregs[0] : 0; - *flags = env->flags - | (env->fpscr & TB_FLAG_FPSCR_MASK) - | (env->sr & TB_FLAG_SR_MASK) - | (env->movcal_backup ? TB_FLAG_PENDING_MOVCA : 0); /* Bit 3 */ -#ifdef CONFIG_USER_ONLY - *flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; -#endif -} - #endif /* SH4_CPU_H */ diff --git a/target/sh4/gdbstub.c b/target/sh4/gdbstub.c index d8e199fc060..75926d4e049 100644 --- a/target/sh4/gdbstub.c +++ b/target/sh4/gdbstub.c @@ -26,8 +26,7 @@ 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 e02e7af6079..fb7642bda1b 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -20,12 +20,14 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/log.h" #if !defined(CONFIG_USER_ONLY) #include "hw/sh4/sh_intc.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #endif #define MMU_OK 0 @@ -55,8 +57,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 +85,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 +150,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 +188,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 +214,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 +232,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 +242,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 +266,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 +298,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 +433,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 +518,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 +601,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 +612,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 */ - } - - /* search ITLB */ - for (i = 0; i < ITLB_SIZE; i++) { + 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++) { 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 +661,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 +782,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 +797,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 8d792f6b553..29011d3dbbb 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 a78e9ec7e4e..fe09f96684b 100644 --- a/target/sh4/meson.build +++ b/target/sh4/meson.build @@ -11,4 +11,4 @@ sh4_system_ss = ss.source_set() sh4_system_ss.add(files('monitor.c')) target_arch += {'sh4': sh4_ss} -target_softmmu_arch += {'sh4': sh4_system_ss} +target_system_arch += {'sh4': sh4_system_ss} diff --git a/target/sh4/op_helper.c b/target/sh4/op_helper.c index a663335c39a..557b1bf4972 100644 --- a/target/sh4/op_helper.c +++ b/target/sh4/op_helper.c @@ -19,8 +19,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "fpu/softfloat.h" #ifndef CONFIG_USER_ONLY @@ -29,9 +28,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 +111,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 +126,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 +139,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 +205,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 c1e590feb34..70fd13aa3f5 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -19,12 +19,12 @@ #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 "exec/helper-gen.h" +#include "exec/translation-block.h" #include "exec/translator.h" +#include "exec/target_page.h" #include "exec/log.h" #include "qemu/qemu-print.h" @@ -54,7 +54,7 @@ typedef struct DisasContext { #define UNALIGN(C) (ctx->tbflags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN) #else #define IS_USER(ctx) (!(ctx->tbflags & (1u << SR_MD))) -#define UNALIGN(C) 0 +#define UNALIGN(C) MO_ALIGN #endif /* Target-specific values for ctx->base.is_jmp. */ @@ -96,71 +96,70 @@ void sh4_translate_init(void) }; for (i = 0; i < 24; i++) { - cpu_gregs[i] = tcg_global_mem_new_i32(cpu_env, + cpu_gregs[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, gregs[i]), gregnames[i]); } memcpy(cpu_gregs + 24, cpu_gregs + 8, 8 * sizeof(TCGv)); - cpu_pc = tcg_global_mem_new_i32(cpu_env, + cpu_pc = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, pc), "PC"); - cpu_sr = tcg_global_mem_new_i32(cpu_env, + cpu_sr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, sr), "SR"); - cpu_sr_m = tcg_global_mem_new_i32(cpu_env, + cpu_sr_m = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, sr_m), "SR_M"); - cpu_sr_q = tcg_global_mem_new_i32(cpu_env, + cpu_sr_q = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, sr_q), "SR_Q"); - cpu_sr_t = tcg_global_mem_new_i32(cpu_env, + cpu_sr_t = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, sr_t), "SR_T"); - cpu_ssr = tcg_global_mem_new_i32(cpu_env, + cpu_ssr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, ssr), "SSR"); - cpu_spc = tcg_global_mem_new_i32(cpu_env, + cpu_spc = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, spc), "SPC"); - cpu_gbr = tcg_global_mem_new_i32(cpu_env, + cpu_gbr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, gbr), "GBR"); - cpu_vbr = tcg_global_mem_new_i32(cpu_env, + cpu_vbr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, vbr), "VBR"); - cpu_sgr = tcg_global_mem_new_i32(cpu_env, + cpu_sgr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, sgr), "SGR"); - cpu_dbr = tcg_global_mem_new_i32(cpu_env, + cpu_dbr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, dbr), "DBR"); - cpu_mach = tcg_global_mem_new_i32(cpu_env, + cpu_mach = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, mach), "MACH"); - cpu_macl = tcg_global_mem_new_i32(cpu_env, + cpu_macl = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, macl), "MACL"); - cpu_pr = tcg_global_mem_new_i32(cpu_env, + cpu_pr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, pr), "PR"); - cpu_fpscr = tcg_global_mem_new_i32(cpu_env, + cpu_fpscr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, fpscr), "FPSCR"); - cpu_fpul = tcg_global_mem_new_i32(cpu_env, + cpu_fpul = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, fpul), "FPUL"); - cpu_flags = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUSH4State, flags), "_flags_"); - cpu_delayed_pc = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUSH4State, delayed_pc), - "_delayed_pc_"); - cpu_delayed_cond = tcg_global_mem_new_i32(cpu_env, + cpu_flags = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUSH4State, flags), "_flags_"); + cpu_delayed_pc = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUSH4State, delayed_pc), + "_delayed_pc_"); + cpu_delayed_cond = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, delayed_cond), "_delayed_cond_"); - cpu_lock_addr = tcg_global_mem_new_i32(cpu_env, + cpu_lock_addr = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, lock_addr), "_lock_addr_"); - cpu_lock_value = tcg_global_mem_new_i32(cpu_env, + cpu_lock_value = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, lock_value), "_lock_value_"); for (i = 0; i < 32; i++) - cpu_fregs[i] = tcg_global_mem_new_i32(cpu_env, + cpu_fregs[i] = tcg_global_mem_new_i32(tcg_env, offsetof(CPUSH4State, fregs[i]), fregnames[i]); } void superh_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - SuperHCPU *cpu = SUPERH_CPU(cs); - CPUSH4State *env = &cpu->env; + CPUSH4State *env = cpu_env(cs); int i; qemu_fprintf(f, "pc=0x%08x sr=0x%08x pr=0x%08x fpscr=0x%08x\n", @@ -252,9 +251,9 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) static void gen_jump(DisasContext * ctx) { if (ctx->delayed_pc == -1) { - /* Target is not statically known, it comes necessarily from a - delayed jump as immediate jump are conditinal jumps */ - tcg_gen_mov_i32(cpu_pc, cpu_delayed_pc); + /* Target is not statically known, it comes necessarily from a + delayed jump as immediate jump are conditinal jumps */ + tcg_gen_mov_i32(cpu_pc, cpu_delayed_pc); tcg_gen_discard_i32(cpu_delayed_pc); if (use_exit_tb(ctx)) { tcg_gen_exit_tb(NULL, 0); @@ -263,7 +262,7 @@ static void gen_jump(DisasContext * ctx) } ctx->base.is_jmp = DISAS_NORETURN; } else { - gen_goto_tb(ctx, 0, ctx->delayed_pc); + gen_goto_tb(ctx, 0, ctx->delayed_pc); } } @@ -411,103 +410,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)); - } - 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)); - } - 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). @@ -521,236 +520,236 @@ 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 */ - { + 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 */ - { + } + 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 + } + 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 */ - } - 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); - } - 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); - } - 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); - } - 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)); - } - 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)); - } - 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); - } - 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)); - } - 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)); - } - 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); - } - 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); - } - 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 t0, t1; - 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); + 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 0x300f: /* addv Rm,Rn */ + 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 */ + tcg_gen_addcio_i32(REG(B11_8), cpu_sr_t, + REG(B11_8), REG(B7_4), cpu_sr_t); + 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_gen_shri_i32(cpu_sr_t, cpu_sr_t, 31); - tcg_gen_mov_i32(REG(B7_4), 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); - } - 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(); @@ -779,80 +778,80 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_xori_i32(cpu_sr_t, t1, 1); tcg_gen_xor_i32(cpu_sr_q, cpu_sr_m, t1); } - 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(); + 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 | MO_ALIGN); - arg1 = tcg_temp_new(); + arg1 = tcg_temp_new(); tcg_gen_qemu_ld_i32(arg1, REG(B11_8), ctx->memidx, MO_TESL | MO_ALIGN); - gen_helper_macl(cpu_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(); + 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_TESL | MO_ALIGN); - arg1 = tcg_temp_new(); + MO_TESW | MO_ALIGN); + arg1 = tcg_temp_new(); tcg_gen_qemu_ld_i32(arg1, REG(B11_8), ctx->memidx, - MO_TESL | MO_ALIGN); - gen_helper_macw(cpu_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 */ + 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, @@ -861,15 +860,15 @@ static void _decode_opc(DisasContext * ctx) t0, t0, REG(B11_8), cpu_sr_t); tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1); } - 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(); @@ -888,10 +887,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); - } - 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(); @@ -910,12 +909,12 @@ 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); - } - 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_constant_tl(0); @@ -925,82 +924,86 @@ static void _decode_opc(DisasContext * ctx) REG(B11_8), t0, t1, cpu_sr_t); tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1); } - 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_gen_shri_i32(cpu_sr_t, t1, 31); - tcg_gen_mov_i32(REG(B11_8), 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); - } - 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 | MO_ALIGN); - } else { + } else { tcg_gen_qemu_st_i32(FREG(B7_4), REG(B11_8), ctx->memidx, MO_TEUL | MO_ALIGN); - } - return; + } + 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 | MO_ALIGN); gen_store_fpr64(ctx, fp, XHACK(B11_8)); - } else { + } else { tcg_gen_qemu_ld_i32(FREG(B11_8), REG(B7_4), ctx->memidx, MO_TEUL | MO_ALIGN); - } - return; + } + 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 | MO_ALIGN); gen_store_fpr64(ctx, fp, XHACK(B11_8)); tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 8); - } else { + } 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; + 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) { @@ -1016,485 +1019,486 @@ static void _decode_opc(DisasContext * ctx) } tcg_gen_mov_i32(REG(B11_8), 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 | MO_ALIGN); gen_store_fpr64(ctx, fp, XHACK(B11_8)); - } else { + } else { tcg_gen_qemu_ld_i32(FREG(B11_8), addr, ctx->memidx, MO_TEUL | MO_ALIGN); - } - } - return; + } + } + 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 | MO_ALIGN); - } else { + } else { tcg_gen_qemu_st_i32(FREG(B7_4), addr, ctx->memidx, MO_TEUL | MO_ALIGN); - } - } - return; + } + } + 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); - } 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); - } - 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); - } - return; - case 0xc500: /* mov.w @(disp,GBR),R0 */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 2); + } + 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); + } + 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); + } + 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); - } - return; - case 0xc100: /* mov.w R0,@(disp,GBR) */ - { - TCGv addr = tcg_temp_new(); - tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 2); + } + 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); + } + 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); + } + 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); - } - 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)); - } - 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); - } - 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)); - } - 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); - } - 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_constant_i32(B7_0); - gen_helper_trapa(cpu_env, 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); - } - 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); - } - 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); - } - 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 + 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_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; + 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); ctx->base.is_jmp = DISAS_STOP; } - return; - case 0x4007: /* ldc.l @Rm+,SR */ - CHECK_PRIVILEGED - { - TCGv val = tcg_temp_new(); + 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 | MO_ALIGN); tcg_gen_andi_i32(val, val, 0x700083f3); gen_write_sr(val); - tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); + 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 + } + 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(); + 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); + 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); \ + 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); \ - } \ +#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) +#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)); + 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(); + 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(cpu_env, addr); + 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); + } + 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 */ + 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(cpu_env, REG(B11_8), val); + 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 @@ -1558,189 +1562,189 @@ static void _decode_opc(DisasContext * ctx) 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); - } - return; - case 0x4025: /* rotcr Rn */ - { - TCGv tmp = tcg_temp_new(); + } + 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_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); + } + 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 */ + 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_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_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_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_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); - } - 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); - } - 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); - } 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); - } - 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); - } - 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_constant_i32((ctx->opcode >> 8) & 3); TCGv n = tcg_constant_i32((ctx->opcode >> 10) & 3); - gen_helper_fipr(cpu_env, m, n); + gen_helper_fipr(tcg_env, m, n); return; } break; @@ -1752,7 +1756,7 @@ static void _decode_opc(DisasContext * ctx) goto do_illegal; } TCGv n = tcg_constant_i32((ctx->opcode >> 10) & 3); - gen_helper_ftrv(cpu_env, n); + gen_helper_ftrv(tcg_env, n); return; } break; @@ -1766,10 +1770,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; @@ -1777,12 +1781,11 @@ 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; } static void decode_opc(DisasContext * ctx) @@ -1808,14 +1811,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. @@ -1918,16 +1933,16 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) NEXT_INSN; switch (ctx->opcode & 0xf00f) { case 0x300c: /* add Rm,Rn */ - op_opc = INDEX_op_add_i32; + op_opc = INDEX_op_add; goto do_reg_op; case 0x2009: /* and Rm,Rn */ - op_opc = INDEX_op_and_i32; + op_opc = INDEX_op_and; goto do_reg_op; case 0x200a: /* xor Rm,Rn */ - op_opc = INDEX_op_xor_i32; + op_opc = INDEX_op_xor; goto do_reg_op; case 0x200b: /* or Rm,Rn */ - op_opc = INDEX_op_or_i32; + op_opc = INDEX_op_or; do_reg_op: /* The operation register should be as expected, and the other input cannot depend on the load. */ @@ -1954,7 +1969,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) goto fail; } op_dst = B11_8; - op_opc = INDEX_op_xor_i32; + op_opc = INDEX_op_xor; op_arg = tcg_constant_i32(-1); break; @@ -1962,7 +1977,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) if (op_dst != B11_8 || mv_src >= 0) { goto fail; } - op_opc = INDEX_op_add_i32; + op_opc = INDEX_op_add; op_arg = tcg_constant_i32(B7_0s); break; @@ -1973,7 +1988,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) if ((ld_dst == B11_8) + (ld_dst == B7_4) != 1 || mv_src >= 0) { goto fail; } - op_opc = INDEX_op_setcond_i32; /* placeholder */ + op_opc = INDEX_op_setcond; /* placeholder */ op_src = (ld_dst == B11_8 ? B7_4 : B11_8); op_arg = REG(op_src); @@ -2008,7 +2023,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) if (ld_dst != B11_8 || ld_dst != B7_4 || mv_src >= 0) { goto fail; } - op_opc = INDEX_op_setcond_i32; + op_opc = INDEX_op_setcond; op_arg = tcg_constant_i32(0); NEXT_INSN; @@ -2065,7 +2080,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) ctx->memidx, ld_mop); break; - case INDEX_op_add_i32: + case INDEX_op_add: if (op_dst != st_src) { goto fail; } @@ -2083,7 +2098,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) } break; - case INDEX_op_and_i32: + case INDEX_op_and: if (op_dst != st_src) { goto fail; } @@ -2097,7 +2112,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) } break; - case INDEX_op_or_i32: + case INDEX_op_or: if (op_dst != st_src) { goto fail; } @@ -2111,7 +2126,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) } break; - case INDEX_op_xor_i32: + case INDEX_op_xor: if (op_dst != st_src) { goto fail; } @@ -2125,7 +2140,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) } break; - case INDEX_op_setcond_i32: + case INDEX_op_setcond: if (st_src == ld_dst) { goto fail; } @@ -2149,12 +2164,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) 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 @@ -2172,6 +2182,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) */ for (i = 1; i < max_insns; ++i) { tcg_gen_insn_start(pc + i * 2, ctx->envflags); + ctx->base.insn_start = tcg_last_op(); } } #endif @@ -2179,7 +2190,6 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) 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; @@ -2189,7 +2199,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; @@ -2236,18 +2246,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 @@ -2283,24 +2303,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 sh4_translate_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 3270ed0c7fc..14ffaa3842d 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_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-cachable */ -#define ASI_REAL_IO 0x15 /* Real address, non-cachable */ +#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, cacheable, 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_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 */ @@ -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 7ad5b9b29e2..00000000000 --- 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 00000000000..be810052376 --- /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 cb11980404a..45eea9d6bac 100644 --- a/target/sparc/cpu-param.h +++ b/target/sparc/cpu-param.h @@ -1,14 +1,13 @@ /* * Sparc cpu parameters for qemu. * - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef SPARC_CPU_PARAM_H #define SPARC_CPU_PARAM_H #ifdef TARGET_SPARC64 -# define TARGET_LONG_BITS 64 # define TARGET_PAGE_BITS 13 /* 8k */ # define TARGET_PHYS_ADDR_SPACE_BITS 41 # ifdef TARGET_ABI32 @@ -17,10 +16,11 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 44 # endif #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 #endif +#define TARGET_INSN_START_EXTRA_WORDS 1 + #endif diff --git a/target/sparc/cpu-qom.h b/target/sparc/cpu-qom.h index 78bf00b9a23..a86331bd581 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_phases: The parent class' reset phase handlers. - * - * A SPARC CPU model. - */ -struct SPARCCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - ResettablePhases parent_phases; - 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 130ab8f5781..ed7701b02ff 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -22,22 +22,24 @@ #include "cpu.h" #include "qemu/module.h" #include "qemu/qemu-print.h" -#include "exec/exec-all.h" +#include "accel/tcg/cpu-mmu-index.h" +#include "exec/translation-block.h" #include "hw/qdev-properties.h" #include "qapi/visitor.h" #include "tcg/tcg.h" +#include "fpu/softfloat.h" +#include "target/sparc/translate.h" //#define DEBUG_FEATURES -static void sparc_cpu_reset_hold(Object *obj) +static void sparc_cpu_reset_hold(Object *obj, ResetType type) { - CPUState *s = CPU(obj); - 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); if (scc->parent_phases.hold) { - scc->parent_phases.hold(obj); + scc->parent_phases.hold(obj, type); } memset(env, 0, offsetof(CPUSPARCState, end_reset_fields)); @@ -46,7 +48,6 @@ static void sparc_cpu_reset_hold(Object *obj) 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; @@ -78,14 +79,14 @@ static void sparc_cpu_reset_hold(Object *obj) 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; @@ -105,6 +106,7 @@ static bool sparc_cpu_exec_interrupt(CPUState *cs, int interrupt_request) static void cpu_sparc_disas_set_info(CPUState *cpu, disassemble_info *info) { info->print_insn = print_insn_sparc; + info->endian = BFD_ENDIAN_BIG; #ifdef TARGET_SPARC64 info->mach = bfd_mach_sparc_v9b; #endif @@ -209,7 +211,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, @@ -218,7 +220,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, @@ -227,7 +229,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, @@ -236,7 +238,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, @@ -245,7 +247,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, @@ -254,7 +256,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, @@ -263,7 +265,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, @@ -272,7 +274,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, @@ -281,7 +283,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, @@ -290,7 +292,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, @@ -299,7 +301,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, @@ -308,7 +310,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, @@ -317,7 +319,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, @@ -326,7 +328,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, @@ -335,7 +337,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, @@ -346,7 +348,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, @@ -357,7 +359,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, @@ -367,9 +369,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, @@ -380,9 +382,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, @@ -393,9 +395,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, @@ -403,14 +405,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, @@ -421,9 +421,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, @@ -434,9 +434,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, @@ -447,9 +447,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, @@ -460,9 +460,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, @@ -474,9 +474,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, @@ -487,9 +487,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, @@ -501,9 +501,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, @@ -517,7 +517,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, @@ -530,7 +530,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, @@ -545,21 +545,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) @@ -576,13 +579,14 @@ static void print_features(uint32_t features, const char *prefix) } } -void sparc_cpu_list(void) +static 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, @@ -617,8 +621,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, @@ -674,7 +677,7 @@ static void sparc_cpu_dump_state(CPUState *cs, FILE *f, int flags) 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: %016x\n", - env->fsr, env->y, env->fprs); + cpu_get_fsr(env), env->y, env->fprs); #else qemu_fprintf(f, "psr: %08x (icc: ", cpu_get_psr(env)); @@ -683,7 +686,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"); } @@ -708,18 +711,109 @@ static void sparc_cpu_synchronize_from_tb(CPUState *cs, { SPARCCPU *cpu = SPARC_CPU(cs); - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); + 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) +static TCGTBCPUState sparc_get_tb_cpu_state(CPUState *cs) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; + CPUSPARCState *env = cpu_env(cs); + uint32_t flags = cpu_mmu_index(cs, false); + +#ifndef CONFIG_USER_ONLY + if (cpu_supervisor_mode(env)) { + flags |= TB_FLAG_SUPER; + } +#endif +#ifdef TARGET_SPARC64 +#ifndef CONFIG_USER_ONLY + if (cpu_hypervisor_mode(env)) { + flags |= TB_FLAG_HYPER; + } +#endif + if (env->pstate & PS_AM) { + flags |= TB_FLAG_AM_ENABLED; + } + if ((env->pstate & PS_PEF) && (env->fprs & FPRS_FEF)) { + flags |= TB_FLAG_FPU_ENABLED; + } + flags |= env->asi << TB_FLAG_ASI_SHIFT; +#else + if (env->psref) { + flags |= TB_FLAG_FPU_ENABLED; + } +#ifndef CONFIG_USER_ONLY + if (env->fsr_qne) { + flags |= TB_FLAG_FSR_QNE; + } +#endif /* !CONFIG_USER_ONLY */ +#endif /* TARGET_SPARC64 */ + + return (TCGTBCPUState){ + .pc = env->pc, + .flags = flags, + .cs_base = env->npc, + }; +} + +static void sparc_restore_state_to_opc(CPUState *cs, + const TranslationBlock *tb, + const uint64_t *data) +{ + CPUSPARCState *env = cpu_env(cs); + target_ulong pc = data[0]; + target_ulong npc = data[1]; + + env->pc = pc; + if (npc == DYNAMIC_PC) { + /* dynamic NPC: already stored */ + } else if (npc & JUMP_PC) { + /* jump PC: use 'cond' and the jump targets of the translation */ + if (env->cond) { + env->npc = npc & ~3; + } else { + env->npc = pc + 4; + } + } else { + env->npc = npc; + } +} +#ifndef CONFIG_USER_ONLY +static bool sparc_cpu_has_work(CPUState *cs) +{ return (cs->interrupt_request & CPU_INTERRUPT_HARD) && - cpu_interrupts_enabled(env); + cpu_interrupts_enabled(cpu_env(cs)); +} +#endif /* !CONFIG_USER_ONLY */ + +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) @@ -743,6 +837,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; @@ -753,17 +857,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; @@ -776,6 +877,19 @@ 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); + /* For fused-multiply add, prefer SNaN over QNaN, then C->B->A */ + set_float_3nan_prop_rule(float_3nan_prop_s_cba, &env->fp_status); + /* For inf * 0 + NaN, return the input NaN */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); + /* Default NaN value: sign bit clear, all frac bits set */ + set_float_default_nan_pattern(0b01111111, &env->fp_status); + cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -793,8 +907,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; } @@ -831,56 +943,115 @@ static void sparc_set_nwindows(Object *obj, Visitor *v, const char *name, cpu->env.def.nwindows = value; } -static PropertyInfo qdev_prop_nwindows = { - .name = "int", +static const PropertyInfo qdev_prop_nwindows = { + .type = "int", + .description = "Number of register windows", .get = sparc_get_nwindows, .set = sparc_set_nwindows, }; -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), +/* This must match feature_name[]. */ +static const Property sparc_cpu_properties[] = { + 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), DEFINE_PROP_UINT32("mmu-version", SPARCCPU, env.def.mmu_version, 0), DEFINE_PROP("nwindows", SPARCCPU, env.def.nwindows, qdev_prop_nwindows, uint32_t), - DEFINE_PROP_END_OF_LIST() }; #ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps sparc_sysemu_ops = { + .has_work = sparc_cpu_has_work, .get_phys_page_debug = sparc_cpu_get_phys_page_debug, .legacy_vmsd = &vmstate_sparc_cpu, }; #endif #ifdef CONFIG_TCG -#include "hw/core/tcg-cpu-ops.h" +#include "accel/tcg/cpu-ops.h" + +#ifndef CONFIG_USER_ONLY +static vaddr sparc_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ +#ifdef TARGET_SPARC64 + return cpu_env(cs)->pstate & PS_AM ? (uint32_t)result : result; +#else + return (uint32_t)result; +#endif +} +#endif + +static const TCGCPUOps sparc_tcg_ops = { + /* + * 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. + */ + .guest_default_memory_order = TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST, + .mttcg_supported = true, -static const struct TCGCPUOps sparc_tcg_ops = { .initialize = sparc_tcg_init, + .translate_code = sparc_translate_code, + .get_tb_cpu_state = sparc_get_tb_cpu_state, .synchronize_from_tb = sparc_cpu_synchronize_from_tb, .restore_state_to_opc = sparc_restore_state_to_opc, + .mmu_index = sparc_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill = sparc_cpu_tlb_fill, + .pointer_wrap = sparc_pointer_wrap, .cpu_exec_interrupt = sparc_cpu_exec_interrupt, + .cpu_exec_halt = sparc_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = sparc_cpu_do_interrupt, .do_transaction_failed = sparc_cpu_do_transaction_failed, .do_unaligned_access = sparc_cpu_do_unaligned_access, @@ -888,7 +1059,7 @@ static const struct TCGCPUOps sparc_tcg_ops = { }; #endif /* CONFIG_TCG */ -static void sparc_cpu_class_init(ObjectClass *oc, void *data) +static void sparc_cpu_class_init(ObjectClass *oc, const void *data) { SPARCCPUClass *scc = SPARC_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); @@ -903,8 +1074,8 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data) &scc->parent_phases); cc->class_by_name = sparc_cpu_class_by_name; + cc->list_cpus = sparc_cpu_list, cc->parse_features = sparc_cpu_parse_features; - cc->has_work = sparc_cpu_has_work; cc->dump_state = sparc_cpu_dump_state; #if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) cc->memory_rw_debug = sparc_cpu_memory_rw_debug; @@ -930,13 +1101,14 @@ 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), .class_init = sparc_cpu_class_init, }; -static void sparc_cpu_cpudef_class_init(ObjectClass *oc, void *data) +static void sparc_cpu_cpudef_class_init(ObjectClass *oc, const void *data) { SPARCCPUClass *scc = SPARC_CPU_CLASS(oc); scc->cpu_def = data; @@ -949,10 +1121,10 @@ static void sparc_register_cpudef_type(const struct sparc_def_t *def) .name = typename, .parent = TYPE_SPARC_CPU, .class_init = sparc_cpu_cpudef_class_init, - .class_data = (void *)def, + .class_data = def, }; - type_register(&ti); + type_register_static(&ti); g_free(typename); } diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 98044572f26..31cb3d97eb1 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -3,13 +3,17 @@ #include "qemu/bswap.h" #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" #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 +118,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 +157,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 +165,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,31 +174,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) #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_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) @@ -250,9 +223,8 @@ typedef struct trap_state { uint32_t tt; } 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; @@ -266,40 +238,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" +}; + +#undef FEATURE +#define FEATURE(X) CPU_FEATURE_##X = 1u << 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 #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) @@ -446,16 +407,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) */ @@ -503,14 +508,11 @@ struct CPUArchState { uint64_t mmubpregs[4]; uint64_t prom_addr; #endif - /* temporary float registers */ - float128 qt0, qt1; float_status fp_status; #if defined(TARGET_SPARC64) #define MAXTL_MAX 8 #define MAXTL_MASK (MAXTL_MAX - 1) trap_state ts[MAXTL_MAX]; - uint32_t xcc; /* Extended integer condition codes */ uint32_t asi; uint32_t pstate; uint32_t tl; @@ -543,10 +545,9 @@ struct CPUArchState { #endif sparc_def_t def; - void *irq_manager; - void (*qemu_irq_ack)(CPUSPARCState *env, void *irq_manager, int intno); - - /* Leon3 cache control */ + /* Leon3 */ + DeviceState *irq_manager; + void (*qemu_irq_ack)(CPUSPARCState *env, int intno); uint32_t cache_control; }; @@ -557,14 +558,25 @@ 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; + const sparc_def_t *cpu_def; +}; #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_sparc_cpu; @@ -583,7 +595,6 @@ G_NORETURN void cpu_raise_exception_ra(CPUSPARCState *, int, uintptr_t); /* cpu_init.c */ void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu); -void sparc_cpu_list(void); /* mmu_helper.c */ bool sparc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, @@ -593,21 +604,22 @@ void dump_mmu(CPUSPARCState *env); #if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) int sparc_cpu_memory_rw_debug(CPUState *cpu, vaddr addr, - uint8_t *buf, int len, bool is_write); + uint8_t *buf, size_t len, bool is_write); #endif - /* translate.c */ void sparc_tcg_init(void); -void sparc_restore_state_to_opc(CPUState *cs, - const TranslationBlock *tb, - const uint64_t *data); +void sparc_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); -/* 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); @@ -651,12 +663,8 @@ 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 - /* MMU modes definitions */ #if defined (TARGET_SPARC64) #define MMU_USER_IDX 0 @@ -693,34 +701,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) @@ -745,8 +725,6 @@ static inline int cpu_pil_allowed(CPUSPARCState *env1, int pil) #endif } -#include "exec/cpu-all.h" - #ifdef TARGET_SPARC64 /* sun4u.c */ void cpu_tick_set_count(CPUTimer *timer, uint64_t count); @@ -760,43 +738,9 @@ 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, 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); -#ifndef CONFIG_USER_ONLY - if (cpu_supervisor_mode(env)) { - flags |= TB_FLAG_SUPER; - } -#endif -#ifdef TARGET_SPARC64 -#ifndef CONFIG_USER_ONLY - if (cpu_hypervisor_mode(env)) { - flags |= TB_FLAG_HYPER; - } -#endif - 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)) { - flags |= TB_FLAG_FPU_ENABLED; - } - flags |= env->asi << TB_FLAG_ASI_SHIFT; -#else - if ((env->def.features & CPU_FEATURE_FLOAT) && env->psref) { - flags |= TB_FLAG_FPU_ENABLED; - } -#endif - *pflags = flags; -} - static inline bool tb_fpu_enabled(int tb_flags) { #if defined(CONFIG_USER_ONLY) diff --git a/target/sparc/fop_helper.c b/target/sparc/fop_helper.c index f54fa9b9595..29fd1664384 100644 --- a/target/sparc/fop_helper.c +++ b/target/sparc/fop_helper.c @@ -19,17 +19,35 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #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; + + 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 target_ulong do_check_ieee_exceptions(CPUSPARCState *env, uintptr_t ra) +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 +55,521 @@ 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_fsmuld(CPUSPARCState *env, float32 src1, float32 src2) +float64 helper_faddd(CPUSPARCState *env, float64 src1, float64 src2) { - return float64_mul(float32_to_float64(src1, &env->fp_status), - float32_to_float64(src2, &env->fp_status), - &env->fp_status); + float64 ret = float64_add(src1, src2, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -void helper_fdmulq(CPUSPARCState *env, float64 src1, float64 src2) +float64 helper_fsubd(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); + float64 ret = float64_sub(src1, src2, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -float32 helper_fnegs(float32 src) +float64 helper_fmuld(CPUSPARCState *env, float64 src1, float64 src2) { - return float32_chs(src); + float64 ret = float64_mul(src1, src2, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -#ifdef TARGET_SPARC64 -float64 helper_fnegd(float64 src) +float64 helper_fdivd(CPUSPARCState *env, float64 src1, float64 src2) { - return float64_chs(src); + float64 ret = float64_div(src1, src2, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -F_HELPER(neg, q) +Int128 helper_faddq(CPUSPARCState *env, Int128 src1, Int128 src2) { - QT0 = float128_chs(QT1); + 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) +{ + 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; +} + +Int128 helper_fdmulq(CPUSPARCState *env, float64 src1, float64 src2) +{ + 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); } -#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); + 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_fabss(float32 src) +float32 helper_fsqrts(CPUSPARCState *env, float32 src) { - return float32_abs(src); + float32 ret = float32_sqrt(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -#ifdef TARGET_SPARC64 -float64 helper_fabsd(float64 src) +float64 helper_fsqrtd(CPUSPARCState *env, float64 src) { - return float64_abs(src); + float64 ret = float64_sqrt(src, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -void helper_fabsq(CPUSPARCState *env) +Int128 helper_fsqrtq(CPUSPARCState *env, Int128 src) { - QT0 = float128_abs(QT1); + float128 ret = float128_sqrt(f128_in(src), &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return f128_ret(ret); } -#endif -float32 helper_fsqrts(CPUSPARCState *env, float32 src) +float32 helper_fmadds(CPUSPARCState *env, float32 s1, + float32 s2, float32 s3, int32_t sc, uint32_t op) { - return float32_sqrt(src, &env->fp_status); + float32 ret = float32_muladd_scalbn(s1, s2, s3, sc, op, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; } -float64 helper_fsqrtd(CPUSPARCState *env, float64 src) +float64 helper_fmaddd(CPUSPARCState *env, float64 s1, + float64 s2, float64 s3, int32_t sc, uint32_t op) +{ + float64 ret = float64_muladd_scalbn(s1, s2, s3, sc, op, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; +} + +float32 helper_fnadds(CPUSPARCState *env, float32 src1, float32 src2) { - return float64_sqrt(src, &env->fp_status); -} - -void helper_fsqrtq(CPUSPARCState *env) -{ - QT0 = float128_sqrt(QT1, &env->fp_status); -} - -#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 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); -#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); + /* 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: + 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()); +} -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); +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()); +} -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); +uint32_t helper_flcmps(CPUSPARCState *env, 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 = env->fp_status; + FloatRelation r; -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); + set_float_2nan_prop_rule(float_2nan_prop_s_ba, &discard); + r = float32_compare_quiet(src1, src2, &discard); -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); + 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(); +} -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); +uint32_t helper_flcmpd(CPUSPARCState *env, float64 src1, float64 src2) +{ + float_status discard = env->fp_status; + 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 + 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 +588,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 -target_ulong helper_ldxfsr(CPUSPARCState *env, target_ulong old_fsr, - uint64_t new_fsr) + 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); +} + +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 a1c8fdc4d55..134617fb232 100644 --- a/target/sparc/gdbstub.c +++ b/target/sparc/gdbstub.c @@ -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: @@ -80,8 +79,13 @@ int sparc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) } } if (n < 80) { - /* f32-f62 (double width, even numbers only) */ - return gdb_get_reg64(mem_buf, env->fpr[(n - 32) / 2].ll); + /* f32-f62 (16 double width registers, even register numbers only) + * n == 64: f32 : env->fpr[16] + * n == 65: f34 : env->fpr[17] + * etc... + * n == 79: f62 : env->fpr[31] + */ + return gdb_get_reg64(mem_buf, env->fpr[(n - 64) + 16].ll); } switch (n) { case 80: @@ -94,7 +98,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 +113,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 +160,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; @@ -174,8 +178,13 @@ int sparc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) } return 4; } else if (n < 80) { - /* f32-f62 (double width, even numbers only) */ - env->fpr[(n - 32) / 2].ll = tmp; + /* f32-f62 (16 double width registers, even register numbers only) + * n == 64: f32 : env->fpr[16] + * n == 65: f34 : env->fpr[17] + * etc... + * n == 79: f62 : env->fpr[31] + */ + env->fpr[(n - 64) + 16].ll = tmp; } else { switch (n) { case 80: @@ -191,7 +200,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 c4358bba84a..9163b9d46ad 100644 --- a/target/sparc/helper.c +++ b/target/sparc/helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "qemu/timer.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" @@ -81,113 +80,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 (b32 == 0) { + cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC()); } - if (cc) { - env->cc_dst = x0; - env->cc_src2 = overflow; - env->cc_op = CC_OP_DIV; + a64 /= b32; + r = a64; + if (unlikely(a64 > UINT32_MAX)) { + return -1; /* r = UINT32_MAX, v = 1 */ } - 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()); + return r; } -static target_ulong do_sdiv(CPUSPARCState *env, target_ulong a, - target_ulong b, int cc, uintptr_t ra) +uint64_t helper_sdiv(CPUSPARCState *env, target_ulong a, target_ulong b) { - 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; - } - } + int64_t a64 = (uint32_t)a | ((uint64_t)env->y << 32); + int32_t b32 = b; + int32_t r; - if (cc) { - env->cc_dst = x0; - env->cc_src2 = overflow; - env->cc_op = CC_OP_DIV; + if (b32 == 0) { + cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC()); } - 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; + 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); } -} -uint64_t helper_udivx(CPUSPARCState *env, uint64_t a, uint64_t b) -{ - if (b == 0) { - /* Raise divide by zero trap. */ - cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC()); + a64 /= b32; + r = a64; + if (unlikely(r != a64)) { + return (uint32_t)(a64 < 0 ? INT32_MIN : INT32_MAX) | (-1ull << 32); } - return a / b; + 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 +141,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 +167,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 +177,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 +211,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 b8f1e78c752..3a7f7dc1296 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_3(flcmps, TCG_CALL_NO_RWG_SE, i32, env, f32, f32) +DEF_HELPER_FLAGS_3(flcmpd, TCG_CALL_NO_RWG_SE, i32, env, 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_6(fmaddd, TCG_CALL_NO_WG, f64, env, f64, f64, f64, s32, 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_6(fmadds, TCG_CALL_NO_WG, f32, env, f32, f32, f32, s32, 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 00000000000..9e39d232735 --- /dev/null +++ b/target/sparc/insns.decode @@ -0,0 +1,713 @@ +# 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 + RDPIC 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 + WRPCR 10 10000 110000 01000 0 0000000000000 + WRPIC 10 10001 110000 01000 0 0000000000000 + { + 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 ..... @d_r2 +FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @d_d2 +FqTOx 10 ..... 110100 00000 0 1000 0011 ..... @d_q2 +FxTOs 10 ..... 110100 00000 0 1000 0100 ..... @r_d2 +FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @d_d2 +FxTOq 10 ..... 110100 00000 0 1000 1100 ..... @q_d2 +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 82e8418e465..39db4ffa70a 100644 --- a/target/sparc/int32_helper.c +++ b/target/sparc/int32_helper.c @@ -21,9 +21,9 @@ #include "qemu/main-loop.h" #include "cpu.h" #include "trace.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/log.h" -#include "sysemu/runstate.h" - +#include "system/runstate.h" static const char * const excp_names[0x80] = { [TT_TFAULT] = "Instruction Access Fault", @@ -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 793e57c536d..bd14c7a0db9 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 78b03308aef..2c63eb9e036 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -19,11 +19,18 @@ #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/cpu_ldst.h" +#include "exec/cputlb.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" +#include "accel/tcg/cpu-ldst.h" +#include "system/memory.h" +#ifdef CONFIG_USER_ONLY +#include "user/page-protection.h" +#endif #include "asi.h" //#define DEBUG_MMU @@ -66,9 +73,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 +246,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 +362,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 +370,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,8 +423,7 @@ 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 @@ -486,8 +484,7 @@ 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 " HWADDR_FMT_plx " from " TARGET_FMT_lx @@ -593,7 +590,6 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, #if defined(DEBUG_MXCC) || defined(DEBUG_ASI) uint32_t last_addr = addr; #endif - MemOpIdx oi; do_check_align(env, addr, size - 1, GETPC()); switch (asi) { @@ -605,6 +601,9 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, case 0x0C: /* Leon3 Date Cache config */ if (env->def.features & CPU_FEATURE_CACHE_CTRL) { ret = leon3_cache_control_ld(env, addr, size); + } else { + qemu_log_mask(LOG_UNIMP, "0x" TARGET_FMT_lx ": unimplemented" + " address, size: %d\n", addr, size); } break; case 0x01c00a00: /* MXCC control register */ @@ -692,24 +691,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 */ - oi = make_memop_idx(memop, cpu_mmu_index(env, true)); - switch (size) { - case 1: - ret = cpu_ldb_code_mmu(env, addr, oi, GETPC()); - break; - case 2: - ret = cpu_ldw_code_mmu(env, addr, oi, GETPC()); - break; - default: - case 4: - ret = cpu_ldl_code_mmu(env, addr, oi, GETPC()); - break; - case 8: - ret = cpu_ldq_code_mmu(env, addr, oi, GETPC()); - 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 */ @@ -787,7 +768,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; @@ -795,6 +775,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 */ @@ -838,6 +820,9 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, case 0x0C: /* Leon3 Date Cache config */ if (env->def.features & CPU_FEATURE_CACHE_CTRL) { leon3_cache_control_st(env, addr, val, size); + } else { + qemu_log_mask(LOG_UNIMP, "0x" TARGET_FMT_lx ": unimplemented" + " address, size: %d\n", addr, size); } break; @@ -1169,6 +1154,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 */ @@ -1377,6 +1405,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(); @@ -1653,7 +1685,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: @@ -1665,7 +1697,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: @@ -1682,7 +1714,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: @@ -1694,7 +1726,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 */ @@ -1923,6 +1955,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 274e1217dfb..4dd75aff74a 100644 --- a/target/sparc/machine.c +++ b/target/sparc/machine.c @@ -1,6 +1,5 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "qemu/timer.h" #include "migration/cpu.h" @@ -10,7 +9,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 +28,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 +41,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 +82,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 +190,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 +200,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 +207,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 +240,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), @@ -190,4 +282,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 d32e67b287e..46289c86693 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', @@ -20,4 +22,4 @@ sparc_system_ss.add(files( )) target_arch += {'sparc': sparc_ss} -target_softmmu_arch += {'sparc': sparc_system_ss} +target_system_arch += {'sparc': sparc_system_ss} diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index 453498c6704..217580a4d8c 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -20,7 +20,12 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "accel/tcg/cpu-mmu-index.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" +#include "exec/tlb-flags.h" +#include "system/memory.h" #include "qemu/qemu-print.h" #include "trace.h" @@ -206,8 +211,7 @@ 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; + CPUSPARCState *env = cpu_env(cs); CPUTLBEntryFull full = {}; target_ulong vaddr; int error_code = 0, access_index; @@ -389,10 +393,9 @@ void dump_mmu(CPUSPARCState *env) * that the sparc ABI is followed. */ int sparc_cpu_memory_rw_debug(CPUState *cs, vaddr address, - uint8_t *buf, int len, bool is_write) + uint8_t *buf, size_t 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; @@ -580,7 +583,7 @@ static int get_physical_address_data(CPUSPARCState *env, CPUTLBEntryFull *full, int do_fault = 0; if (TTE_IS_IE(env->dtlb[i].tte)) { - full->attrs.byte_swap = true; + full->tlb_fill_flags |= TLB_BSWAP; } /* access ok? */ @@ -759,8 +762,7 @@ 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; + CPUSPARCState *env = cpu_env(cs); CPUTLBEntryFull full = {}; int error_code = 0, access_index; @@ -898,10 +900,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) { @@ -916,8 +917,7 @@ G_NORETURN void sparc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, 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); diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 3bf0ab8135d..b922e53bf12 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -21,82 +21,178 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "disas/disas.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" +#include "exec/target_page.h" #include "tcg/tcg-op.h" - +#include "tcg/tcg-op-gvec.h" #include "exec/helper-gen.h" - #include "exec/translator.h" +#include "exec/translation-block.h" #include "exec/log.h" +#include "fpu/softfloat.h" #include "asi.h" +#include "target/sparc/translate.h" #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" #undef HELPER_H -/* 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 +#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 DISAS_EXIT DISAS_TARGET_0 /* global register indexes */ static TCGv_ptr cpu_regwptr; -static TCGv cpu_cc_src, cpu_cc_src2, cpu_cc_dst; -static TCGv_i32 cpu_cc_op; -static TCGv_i32 cpu_psr; -static TCGv cpu_fsr, cpu_pc, cpu_npc; +static TCGv cpu_pc, cpu_npc; static TCGv cpu_regs[32]; static TCGv cpu_y; -#ifndef CONFIG_USER_ONLY static TCGv cpu_tbr; -#endif static TCGv cpu_cond; +static TCGv cpu_cc_N; +static TCGv cpu_cc_V; +static TCGv cpu_icc_Z; +static TCGv cpu_icc_C; #ifdef TARGET_SPARC64 -static TCGv_i32 cpu_xcc, cpu_fprs; +static TCGv cpu_xcc_Z; +static TCGv cpu_xcc_C; +static TCGv_i32 cpu_fprs; static TCGv cpu_gsr; -static TCGv cpu_tick_cmpr, cpu_stick_cmpr, cpu_hstick_cmpr; -static TCGv cpu_hintp, cpu_htba, cpu_hver, cpu_ssr, cpu_ver; #else -static TCGv cpu_wim; +# define cpu_fprs ({ qemu_build_not_reached(); (TCGv)NULL; }) +# define cpu_gsr ({ qemu_build_not_reached(); (TCGv)NULL; }) +#endif + +#ifdef TARGET_SPARC64 +#define cpu_cc_Z cpu_xcc_Z +#define cpu_cc_C cpu_xcc_C +#else +#define cpu_cc_Z cpu_icc_Z +#define cpu_cc_C cpu_icc_C +#define cpu_xcc_Z ({ qemu_build_not_reached(); NULL; }) +#define cpu_xcc_C ({ qemu_build_not_reached(); NULL; }) +#endif + +/* Floating point 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 -/* Floating point registers */ -static TCGv_i64 cpu_fpr[TARGET_DPREGS]; + +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; #ifdef TARGET_SPARC64 int fprs_dirty; int asi; #endif + DisasDelayException *delay_excp_list; } DisasContext; -typedef struct { - TCGCond cond; - bool is_bool; - 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)) @@ -108,23 +204,9 @@ 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 void gen_update_fprs_dirty(DisasContext *dc, int rd) @@ -141,137 +223,108 @@ static 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) { - TCGv_i32 ret = tcg_temp_new_i32(); - if (src & 1) { - tcg_gen_extrl_i64_i32(ret, 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 { - tcg_gen_extrh_i64_i32(ret, cpu_fpr[src / 2]); + ret += offsetof(CPU_DoubleU, l.upper); } return ret; } -static void gen_store_fpr_F(DisasContext *dc, unsigned int dst, TCGv_i32 v) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_extu_i32_i64(t, v); - tcg_gen_deposit_i64(cpu_fpr[dst / 2], cpu_fpr[dst / 2], t, - (dst & 1 ? 0 : 32), 32); - gen_update_fprs_dirty(dc, dst); -} - -static TCGv_i32 gen_dest_fpr_F(DisasContext *dc) -{ - return tcg_temp_new_i32(); -} - -static TCGv_i64 gen_load_fpr_D(DisasContext *dc, unsigned int src) +static TCGv_i32 gen_load_fpr_F(DisasContext *dc, unsigned int src) { - src = DFPREG(src); - return cpu_fpr[src / 2]; + 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_D(DisasContext *dc, unsigned int dst, TCGv_i64 v) +static void gen_store_fpr_F(DisasContext *dc, unsigned int dst, TCGv_i32 v) { - dst = DFPREG(dst); - tcg_gen_mov_i64(cpu_fpr[dst / 2], v); + tcg_gen_st_i32(v, tcg_env, gen_offset_fpr_F(dst)); gen_update_fprs_dirty(dc, dst); } -static TCGv_i64 gen_dest_fpr_D(DisasContext *dc, unsigned int dst) -{ - return cpu_fpr[DFPREG(dst) / 2]; -} - -static void gen_op_load_fpr_QT0(unsigned int src) -{ - 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)); -} - -static void gen_op_load_fpr_QT1(unsigned int src) +static int gen_offset_fpr_D(unsigned int reg) { - 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)); + tcg_debug_assert(reg < 64); + tcg_debug_assert(reg % 2 == 0); + return offsetof(CPUSPARCState, fpr[reg / 2]); } -static void gen_op_store_QT0_fpr(unsigned int dst) +static TCGv_i64 gen_load_fpr_D(DisasContext *dc, unsigned int src) { - 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)); + 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_Q(DisasContext *dc, unsigned int dst, - TCGv_i64 v1, TCGv_i64 v2) +static void gen_store_fpr_D(DisasContext *dc, unsigned int dst, TCGv_i64 v) { - dst = QFPREG(dst); - - tcg_gen_mov_i64(cpu_fpr[dst / 2], v1); - tcg_gen_mov_i64(cpu_fpr[dst / 2 + 1], v2); + tcg_gen_st_i64(v, tcg_env, gen_offset_fpr_D(dst)); gen_update_fprs_dirty(dc, dst); } -#ifdef TARGET_SPARC64 -static TCGv_i64 gen_load_fpr_Q0(DisasContext *dc, unsigned int src) +static TCGv_i128 gen_load_fpr_Q(DisasContext *dc, unsigned int src) { - src = QFPREG(src); - return cpu_fpr[src / 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); -static TCGv_i64 gen_load_fpr_Q1(DisasContext *dc, unsigned int src) -{ - src = QFPREG(src); - return cpu_fpr[src / 2 + 1]; + tcg_gen_concat_i64_i128(ret, l, h); + return ret; } -static void gen_move_Q(DisasContext *dc, unsigned int rd, unsigned int rs) +static void gen_store_fpr_Q(DisasContext *dc, unsigned int dst, TCGv_i128 v) { - rd = QFPREG(rd); - rs = QFPREG(rs); + TCGv_i64 h = tcg_temp_new_i64(); + TCGv_i64 l = tcg_temp_new_i64(); - 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); + tcg_gen_extr_i128_i64(l, h, v); + gen_store_fpr_D(dc, dst, h); + gen_store_fpr_D(dc, dst + 2, l); } -#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 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 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) @@ -327,256 +380,183 @@ static void gen_goto_tb(DisasContext *s, int tb_num, } } -// XXX suboptimal -static void gen_mov_reg_N(TCGv reg, TCGv_i32 src) -{ - tcg_gen_extu_i32_tl(reg, src); - tcg_gen_extract_tl(reg, reg, PSR_NEG_SHIFT, 1); -} - -static void gen_mov_reg_Z(TCGv reg, TCGv_i32 src) +static TCGv gen_carry32(void) { - tcg_gen_extu_i32_tl(reg, src); - tcg_gen_extract_tl(reg, reg, PSR_ZERO_SHIFT, 1); + 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; } -static void gen_mov_reg_V(TCGv reg, TCGv_i32 src) +static void gen_op_addcc_int(TCGv dst, TCGv src1, TCGv src2, TCGv cin) { - tcg_gen_extu_i32_tl(reg, src); - tcg_gen_extract_tl(reg, reg, PSR_OVF_SHIFT, 1); -} + TCGv z = tcg_constant_tl(0); -static 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); + if (cin) { + tcg_gen_addcio_tl(cpu_cc_N, cpu_cc_C, src1, src2, cin); + } 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_add_cc(TCGv dst, TCGv src1, TCGv src2) +static void gen_op_addcc(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); + gen_op_addcc_int(dst, src1, src2, NULL); } -static TCGv_i32 gen_add32_carry32(void) +static void gen_op_taddcc(TCGv dst, TCGv src1, TCGv src2) { - TCGv_i32 carry_32, cc_src1_32, cc_src2_32; + TCGv t = tcg_temp_new(); - /* 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 + /* Save the tag bits around modification of dst. */ + tcg_gen_or_tl(t, src1, src2); - carry_32 = tcg_temp_new_i32(); - tcg_gen_setcond_i32(TCG_COND_LTU, carry_32, cc_src1_32, cc_src2_32); + gen_op_addcc(dst, src1, src2); - return carry_32; + /* 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 TCGv_i32 gen_sub32_carry32(void) +static void gen_op_addc(TCGv dst, TCGv src1, TCGv src2) { - 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); - - return carry_32; + tcg_gen_add_tl(dst, src1, src2); + tcg_gen_add_tl(dst, dst, gen_carry32()); } -static void gen_op_addx_int(DisasContext *dc, TCGv dst, TCGv src1, - TCGv src2, int update_cc) +static void gen_op_addccc(TCGv dst, TCGv src1, TCGv src2) { - TCGv_i32 carry_32; - TCGv carry; + gen_op_addcc_int(dst, src1, src2, gen_carry32()); +} - 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; +static void gen_op_addxc(TCGv dst, TCGv src1, TCGv src2) +{ + tcg_gen_add_tl(dst, src1, src2); + tcg_gen_add_tl(dst, dst, cpu_cc_C); +} - 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); - goto add_done; - } - carry_32 = gen_add32_carry32(); - break; +static void gen_op_addxccc(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_addcc_int(dst, src1, src2, cpu_cc_C); +} - case CC_OP_SUB: - case CC_OP_TSUB: - case CC_OP_TSUBTV: - carry_32 = gen_sub32_carry32(); - break; +static void gen_op_subcc_int(TCGv dst, TCGv src1, TCGv src2, TCGv cin) +{ + TCGv z = tcg_constant_tl(0); - 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_add_tl(dst, src1, src2); - tcg_gen_add_tl(dst, dst, carry); - - 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_mov_tl(cpu_cc_Z, cpu_cc_N); + tcg_gen_mov_tl(dst, cpu_cc_N); } -static void gen_op_sub_cc(TCGv dst, TCGv src1, TCGv src2) +static void gen_op_subcc(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_subcc_int(dst, src1, src2, NULL); } -static void gen_op_subx_int(DisasContext *dc, TCGv dst, TCGv src1, - TCGv src2, int update_cc) +static void gen_op_tsubcc(TCGv dst, TCGv src1, TCGv src2) { - TCGv_i32 carry_32; - TCGv carry; + TCGv t = tcg_temp_new(); - 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; + /* Save the tag bits around modification of dst. */ + tcg_gen_or_tl(t, src1, src2); - case CC_OP_ADD: - case CC_OP_TADD: - case CC_OP_TADDTV: - carry_32 = gen_add32_carry32(); - break; + gen_op_subcc(dst, src1, src2); - 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); - goto sub_done; - } - carry_32 = gen_sub32_carry32(); - break; + /* 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); +} - 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; - } +static void gen_op_subc(TCGv dst, TCGv src1, TCGv src2) +{ + tcg_gen_sub_tl(dst, src1, src2); + tcg_gen_sub_tl(dst, dst, gen_carry32()); +} -#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_subccc(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_subcc_int(dst, src1, src2, gen_carry32()); +} +static void gen_op_subxc(TCGv dst, TCGv src1, TCGv src2) +{ tcg_gen_sub_tl(dst, src1, src2); - tcg_gen_sub_tl(dst, dst, carry); + tcg_gen_sub_tl(dst, dst, cpu_cc_C); +} - 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; - } +static void gen_op_subxccc(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_subcc_int(dst, src1, src2, cpu_cc_C); } static void gen_op_mulscc(TCGv dst, TCGv src1, TCGv src2) { - TCGv r_temp, zero, t0; + 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(); - r_temp = tcg_temp_new(); - t0 = tcg_temp_new(); + tcg_gen_ext32u_tl(t_src1, src1); + tcg_gen_ext32u_tl(t_src2, src2); - /* old op: - if (!(env->y & 1)) - T1 = 0; - */ - zero = tcg_constant_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); + /* + * if (!(env->y & 1)) + * src2 = 0; + */ + tcg_gen_movcond_tl(TCG_COND_TSTEQ, t_src2, cpu_y, one, zero, t_src2); - // b2 = T0 & 1; - // env->y = (b2 << 31) | (env->y >> 1); + /* + * 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); - - // 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_gen_xor_tl(t0, cpu_cc_N, cpu_cc_V); - tcg_gen_add_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2); + /* + * 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_mov_tl(dst, cpu_cc_dst); + gen_op_addcc(dst, t_src1, t_src2); } static void gen_op_multiply(TCGv dst, TCGv src1, TCGv src2, int sign_ext) @@ -616,312 +596,407 @@ static void gen_op_smul(TCGv dst, TCGv src1, TCGv src2) gen_op_multiply(dst, src1, src2, 1); } -// 1 -static 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 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(); -// Z | (N ^ V) -static void gen_op_eval_ble(TCGv dst, TCGv_i32 src) -{ - 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_gen_mul_i64(t, src1, src2); + tcg_gen_add_i64(dst, src3, t); } -// N ^ V -static void gen_op_eval_bl(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_V(t0, src); - gen_mov_reg_N(dst, src); - tcg_gen_xor_tl(dst, dst, t0); -} + TCGv_i64 l = tcg_temp_new_i64(); + TCGv_i64 h = tcg_temp_new_i64(); + TCGv_i64 z = tcg_constant_i64(0); -// C | Z -static void gen_op_eval_bleu(TCGv dst, TCGv_i32 src) -{ - 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_gen_mulu2_i64(l, h, src1, src2); + tcg_gen_add2_i64(l, dst, l, h, src3, z); } -// C -static void gen_op_eval_bcs(TCGv dst, TCGv_i32 src) +static void gen_op_sdiv(TCGv dst, TCGv src1, TCGv src2) { - gen_mov_reg_C(dst, src); +#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 } -// V -static void gen_op_eval_bvs(TCGv dst, TCGv_i32 src) +static void gen_op_udivcc(TCGv dst, TCGv src1, TCGv src2) { - gen_mov_reg_V(dst, src); -} + TCGv_i64 t64; -// 0 -static void gen_op_eval_bn(TCGv dst) -{ - tcg_gen_movi_tl(dst, 0); -} +#ifdef TARGET_SPARC64 + t64 = cpu_cc_V; +#else + t64 = tcg_temp_new_i64(); +#endif -// N -static void gen_op_eval_bneg(TCGv dst, TCGv_i32 src) -{ - gen_mov_reg_N(dst, src); -} + gen_helper_udiv(t64, tcg_env, src1, src2); -// !Z -static void gen_op_eval_bne(TCGv dst, TCGv_i32 src) -{ - gen_mov_reg_Z(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); +#ifdef TARGET_SPARC64 + tcg_gen_ext32u_tl(cpu_cc_N, t64); + tcg_gen_shri_tl(cpu_cc_V, t64, 32); + tcg_gen_mov_tl(cpu_icc_Z, cpu_cc_N); + tcg_gen_movi_tl(cpu_icc_C, 0); +#else + tcg_gen_extr_i64_tl(cpu_cc_N, cpu_cc_V, t64); +#endif + tcg_gen_mov_tl(cpu_cc_Z, cpu_cc_N); + tcg_gen_movi_tl(cpu_cc_C, 0); + tcg_gen_mov_tl(dst, cpu_cc_N); } -// !(Z | (N ^ V)) -static void gen_op_eval_bg(TCGv dst, TCGv_i32 src) +static void gen_op_sdivcc(TCGv dst, TCGv src1, TCGv src2) { - gen_op_eval_ble(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); -} + TCGv_i64 t64; -// !(N ^ V) -static void gen_op_eval_bge(TCGv dst, TCGv_i32 src) -{ - gen_op_eval_bl(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); +#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); } -// !(C | Z) -static void gen_op_eval_bgu(TCGv dst, TCGv_i32 src) +static void gen_op_taddcctv(TCGv dst, TCGv src1, TCGv src2) { - gen_op_eval_bleu(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); + gen_helper_taddcctv(dst, tcg_env, src1, src2); } -// !C -static void gen_op_eval_bcc(TCGv dst, TCGv_i32 src) +static void gen_op_tsubcctv(TCGv dst, TCGv src1, TCGv src2) { - gen_mov_reg_C(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); + gen_helper_tsubcctv(dst, tcg_env, src1, src2); } -// !N -static void gen_op_eval_bpos(TCGv dst, TCGv_i32 src) +static void gen_op_popc(TCGv dst, TCGv src1, TCGv src2) { - gen_mov_reg_N(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); + tcg_gen_ctpop_tl(dst, src2); } -// !V -static void gen_op_eval_bvc(TCGv dst, TCGv_i32 src) +static void gen_op_lzcnt(TCGv dst, TCGv src) { - gen_mov_reg_V(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); + tcg_gen_clzi_tl(dst, src, TARGET_LONG_BITS); } -/* - FPSR bit field FCC1 | FCC0: - 0 = - 1 < - 2 > - 3 unordered -*/ -static void gen_mov_reg_FCC0(TCGv reg, TCGv src, - unsigned int fcc_offset) +#ifndef TARGET_SPARC64 +static void gen_helper_array8(TCGv dst, TCGv src1, TCGv src2) { - tcg_gen_shri_tl(reg, src, FSR_FCC0_SHIFT + fcc_offset); - tcg_gen_andi_tl(reg, reg, 0x1); + g_assert_not_reached(); } +#endif -static void gen_mov_reg_FCC1(TCGv reg, TCGv src, unsigned int fcc_offset) +static void gen_op_array16(TCGv dst, TCGv src1, TCGv src2) { - tcg_gen_shri_tl(reg, src, FSR_FCC1_SHIFT + fcc_offset); - tcg_gen_andi_tl(reg, reg, 0x1); + gen_helper_array8(dst, src1, src2); + tcg_gen_shli_tl(dst, dst, 1); } -// !0: FCC0 | FCC1 -static void gen_op_eval_fbne(TCGv dst, TCGv src, unsigned int fcc_offset) +static void gen_op_array32(TCGv dst, TCGv src1, TCGv 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); + gen_helper_array8(dst, src1, src2); + tcg_gen_shli_tl(dst, dst, 2); } -// 1 or 2: FCC0 ^ FCC1 -static void gen_op_eval_fblg(TCGv dst, TCGv src, unsigned int fcc_offset) +static void gen_op_fpack16(TCGv_i32 dst, TCGv_i64 src) { - 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); +#ifdef TARGET_SPARC64 + gen_helper_fpack16(dst, cpu_gsr, src); +#else + g_assert_not_reached(); +#endif } -// 1 or 3: FCC0 -static void gen_op_eval_fbul(TCGv dst, TCGv src, unsigned int fcc_offset) +static void gen_op_fpackfix(TCGv_i32 dst, TCGv_i64 src) { - gen_mov_reg_FCC0(dst, src, fcc_offset); +#ifdef TARGET_SPARC64 + gen_helper_fpackfix(dst, cpu_gsr, src); +#else + g_assert_not_reached(); +#endif } -// 1: FCC0 & !FCC1 -static void gen_op_eval_fbl(TCGv dst, TCGv src, unsigned int fcc_offset) +static void gen_op_fpack32(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); +#ifdef TARGET_SPARC64 + gen_helper_fpack32(dst, cpu_gsr, src1, src2); +#else + g_assert_not_reached(); +#endif } -// 2 or 3: FCC1 -static void gen_op_eval_fbug(TCGv dst, TCGv src, unsigned int fcc_offset) +static void gen_op_fpadds16s(TCGv_i32 d, TCGv_i32 src1, TCGv_i32 src2) { - gen_mov_reg_FCC1(dst, src, fcc_offset); + 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); } -// 2: !FCC0 & FCC1 -static void gen_op_eval_fbg(TCGv dst, TCGv src, unsigned int fcc_offset) +static void gen_op_fpsubs16s(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_andc_tl(dst, t0, dst); + 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); } -// 3: FCC0 & FCC1 -static void gen_op_eval_fbu(TCGv dst, TCGv src, unsigned int fcc_offset) +static void gen_op_fpadds32s(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_and_tl(dst, dst, 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_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 void gen_op_eval_fbe(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_gen_xori_tl(dst, dst, 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_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); } -// 0 or 3: !(FCC0 ^ FCC1) -static void gen_op_eval_fbue(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_gen_xori_tl(dst, dst, 0x1); +#ifdef TARGET_SPARC64 + TCGv t1, t2, shift; + + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + shift = tcg_temp_new(); + + tcg_gen_andi_tl(shift, 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 } -// 0 or 2: !FCC0 -static void gen_op_eval_fbge(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); - tcg_gen_xori_tl(dst, dst, 0x1); + gen_op_faligndata_i(dst, s1, s2, cpu_gsr); } -// !1: !(FCC0 & !FCC1) -static void gen_op_eval_fbuge(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_gen_xori_tl(dst, dst, 0x1); +#ifdef TARGET_SPARC64 + gen_helper_bshuffle(dst, cpu_gsr, src1, src2); +#else + g_assert_not_reached(); +#endif } -// 0 or 1: !FCC1 -static void gen_op_eval_fble(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); - tcg_gen_xori_tl(dst, dst, 0x1); +#ifdef TARGET_SPARC64 + gen_helper_pdist(dst, tcg_constant_i64(0), src1, src2); +#else + g_assert_not_reached(); +#endif } -// !2: !(!FCC0 & FCC1) -static void gen_op_eval_fbule(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_gen_xori_tl(dst, dst, 0x1); + tcg_gen_ext16s_i32(src2, src2); + gen_helper_fmul8x16a(dst, src1, src2); } -// !3: !(FCC0 & FCC1) -static void gen_op_eval_fbo(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_gen_xori_tl(dst, dst, 0x1); + tcg_gen_sari_i32(src2, src2, 16); + gen_helper_fmul8x16a(dst, src1, src2); } -static void gen_branch2(DisasContext *dc, target_ulong pc1, - target_ulong pc2, TCGv r_cond) +static void gen_op_fmuld8ulx16(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2) { - TCGLabel *l1 = gen_new_label(); + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); - tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, 0, l1); + tcg_gen_ext8u_i32(t0, src1); + tcg_gen_ext16s_i32(t1, src2); + tcg_gen_mul_i32(t0, t0, t1); - gen_goto_tb(dc, 0, pc1, pc1 + 4); + tcg_gen_extract_i32(t1, src1, 16, 8); + tcg_gen_sextract_i32(t2, src2, 16, 16); + tcg_gen_mul_i32(t1, t1, t2); - gen_set_label(l1); - gen_goto_tb(dc, 1, pc2, pc2 + 4); + tcg_gen_concat_i32_i64(dst, t0, t1); } -static void gen_branch_a(DisasContext *dc, target_ulong pc1) +static void gen_op_fmuld8sux16(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2) { - TCGLabel *l1 = gen_new_label(); - target_ulong npc = dc->npc; - - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_cond, 0, l1); + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); - gen_goto_tb(dc, 0, npc, pc1); + /* + * 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); - gen_set_label(l1); - gen_goto_tb(dc, 1, npc + 4, npc + 8); + 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); - dc->base.is_jmp = DISAS_NORETURN; + tcg_gen_concat_i32_i64(dst, t0, t1); } -static void gen_branch_n(DisasContext *dc, target_ulong pc1) +#ifdef TARGET_SPARC64 +static void gen_vec_fchksm16(unsigned vece, TCGv_vec dst, + TCGv_vec src1, TCGv_vec src2) { - target_ulong npc = dc->npc; + TCGv_vec a = tcg_temp_new_vec_matching(dst); + TCGv_vec c = tcg_temp_new_vec_matching(dst); - 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(TCG_COND_NE, cpu_npc, - cpu_cond, tcg_constant_tl(0), - tcg_constant_tl(pc1), cpu_npc); - dc->pc = npc; - break; - default: - g_assert_not_reached(); - } - } else { - dc->pc = npc; - dc->jump_pc[0] = pc1; - dc->jump_pc[1] = npc + 4; - dc->npc = JUMP_PC; + 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); +} + +static void gen_op_fchksm16(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + 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); +} + +static void gen_vec_fmean16(unsigned vece, TCGv_vec dst, + TCGv_vec src1, TCGv_vec src2) +{ + 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); +} + +static void gen_op_fmean16(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + 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 + +static void finishing_insn(DisasContext *dc) +{ + /* + * 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; } } @@ -929,9 +1004,9 @@ static void gen_generic_branch(DisasContext *dc) { TCGv npc0 = tcg_constant_tl(dc->jump_pc[0]); TCGv npc1 = tcg_constant_tl(dc->jump_pc[1]); - TCGv zero = tcg_constant_tl(0); + TCGv c2 = tcg_constant_tl(dc->jump.c2); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_npc, cpu_cond, zero, npc0, npc1); + 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 @@ -963,14 +1038,6 @@ static void save_npc(DisasContext *dc) } } -static 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 void save_state(DisasContext *dc) { tcg_gen_movi_tl(cpu_pc, dc->pc); @@ -979,18 +1046,50 @@ static void save_state(DisasContext *dc) static void gen_exception(DisasContext *dc, int which) { + finishing_insn(dc); save_state(dc); - gen_helper_raise_exception(cpu_env, tcg_constant_i32(which)); + 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) +{ + 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 TCGLabel *delay_exception(DisasContext *dc, int excp) +{ + return delay_exceptionv(dc, tcg_constant_i32(excp)); +} + +static void gen_check_align(DisasContext *dc, TCGv addr, int mask) { - gen_helper_check_align(cpu_env, addr, tcg_constant_i32(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: @@ -1011,917 +1110,422 @@ static void gen_mov_pc_npc(DisasContext *dc) } } -static 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 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 */ - }; - - 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 */ - }; + TCGv t1; - TCGv_i32 r_src; - TCGv r_dst; + cmp->c1 = t1 = tcg_temp_new(); + cmp->c2 = 0; -#ifdef TARGET_SPARC64 - if (xcc) { - r_src = cpu_xcc; - } else { - r_src = cpu_psr; - } -#else - r_src = cpu_psr; -#endif + switch (cond & 7) { + case 0x0: /* never */ + cmp->cond = TCG_COND_NEVER; + cmp->c1 = tcg_constant_tl(0); + break; - switch (dc->cc_op) { - case CC_OP_LOGIC: - cmp->cond = logic_cond[cond]; - do_compare_dst_0: - cmp->is_bool = false; - cmp->c2 = tcg_constant_tl(0); -#ifdef TARGET_SPARC64 - if (!xcc) { - cmp->c1 = tcg_temp_new(); - tcg_gen_ext32s_tl(cmp->c1, cpu_cc_dst); - 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); } -#endif - cmp->c1 = cpu_cc_dst; 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; + 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; - 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->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->c1 = cpu_cc_src; - cmp->c2 = cpu_cc_src2; - break; + 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; - default: - do_dynamic: - gen_helper_compute_psr(cpu_env); - dc->cc_op = CC_OP_FLAGS; - /* FALLTHRU */ + 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 CC_OP_FLAGS: - /* We're going to generate a boolean result. */ + case 0x5: /* ltu: C */ cmp->cond = TCG_COND_NE; - cmp->is_bool = true; - cmp->c1 = r_dst = tcg_temp_new(); - cmp->c2 = tcg_constant_tl(0); + 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; - 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; + 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; - - /* For now we still generate a straight boolean result. */ - cmp->cond = TCG_COND_NE; - cmp->is_bool = true; - cmp->c1 = r_dst = tcg_temp_new(); - cmp->c2 = tcg_constant_tl(0); + TCGv_i32 fcc = cpu_fcc[cc]; + TCGv_i32 c1 = fcc; + int c2 = 0; + TCGCond tcond; - switch (cc) { - default: - case 0x0: - offset = 0; - break; - case 0x1: - offset = 32 - 10; - break; - case 0x2: - offset = 34 - 10; - break; - case 0x3: - offset = 36 - 10; - 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); + /* + * FCC values: + * 0 = + * 1 < + * 2 > + * 3 unordered + */ + switch (cond & 7) { + case 0x0: /* fbn */ + tcond = TCG_COND_NEVER; break; - case 0x9: - gen_op_eval_fbe(r_dst, cpu_fsr, offset); + case 0x1: /* fbne : !0 */ + tcond = TCG_COND_NE; break; - case 0xa: - gen_op_eval_fbue(r_dst, cpu_fsr, offset); + 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 0xb: - gen_op_eval_fbge(r_dst, cpu_fsr, offset); + case 0x3: /* fbul : 1 or 3 */ + c1 = tcg_temp_new_i32(); + tcg_gen_andi_i32(c1, fcc, 1); + tcond = TCG_COND_NE; break; - case 0xc: - gen_op_eval_fbuge(r_dst, cpu_fsr, offset); + case 0x4: /* fbl : 1 */ + c2 = 1; + tcond = TCG_COND_EQ; break; - case 0xd: - gen_op_eval_fble(r_dst, cpu_fsr, offset); + case 0x5: /* fbug : 2 or 3 */ + c2 = 2; + tcond = TCG_COND_GEU; break; - case 0xe: - gen_op_eval_fbule(r_dst, cpu_fsr, offset); + case 0x6: /* fbg : 2 */ + c2 = 2; + tcond = TCG_COND_EQ; break; - case 0xf: - gen_op_eval_fbo(r_dst, cpu_fsr, offset); + case 0x7: /* fbu : 3 */ + c2 = 3; + tcond = TCG_COND_EQ; break; } + if (cond & 8) { + tcond = tcg_invert_cond(tcond); + } + + cmp->cond = tcond; + cmp->c2 = c2; + cmp->c1 = tcg_temp_new(); + tcg_gen_extu_i32_tl(cmp->c1, c1); } -static void gen_cond(TCGv r_dst, unsigned int cc, unsigned int cond, - DisasContext *dc) +static bool gen_compare_reg(DisasCompare *cmp, int cond, TCGv r_src) { - DisasCompare cmp; - gen_compare(&cmp, cc, cond, dc); + 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); + } + + cmp->cond = tcond; + cmp->c1 = tcg_temp_new(); + cmp->c2 = 0; + tcg_gen_mov_tl(cmp->c1, r_src); + return true; } -static void gen_fcond(TCGv r_dst, unsigned int cc, unsigned int cond) +static void gen_op_clear_ieee_excp_and_FTT(void) { - DisasCompare cmp; - gen_fcompare(&cmp, cc, cond); - - /* 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); - } + tcg_gen_st_i32(tcg_constant_i32(0), tcg_env, + offsetof(CPUSPARCState, fsr_cexc_ftt)); } -#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_op_fmovs(TCGv_i32 dst, TCGv_i32 src) +{ + gen_op_clear_ieee_excp_and_FTT(); + tcg_gen_mov_i32(dst, src); +} -static void gen_compare_reg(DisasCompare *cmp, int cond, TCGv r_src) +static void gen_op_fnegs(TCGv_i32 dst, TCGv_i32 src) { - cmp->cond = tcg_invert_cond(gen_tcg_cond_reg[cond]); - cmp->is_bool = false; - cmp->c1 = r_src; - cmp->c2 = tcg_constant_tl(0); + gen_op_clear_ieee_excp_and_FTT(); + tcg_gen_xori_i32(dst, src, 1u << 31); } -static void gen_cond_reg(TCGv r_dst, int cond, TCGv r_src) +static void gen_op_fabss(TCGv_i32 dst, TCGv_i32 src) { - DisasCompare cmp; - gen_compare_reg(&cmp, cond, r_src); + gen_op_clear_ieee_excp_and_FTT(); + tcg_gen_andi_i32(dst, src, ~(1u << 31)); +} - /* The interface is to return a boolean in r_dst. */ - tcg_gen_setcond_tl(cmp.cond, r_dst, cmp.c1, cmp.c2); +static void gen_op_fmovd(TCGv_i64 dst, TCGv_i64 src) +{ + gen_op_clear_ieee_excp_and_FTT(); + tcg_gen_mov_i64(dst, src); } -#endif -static void do_branch(DisasContext *dc, int32_t offset, uint32_t insn, int cc) +static void gen_op_fnegd(TCGv_i64 dst, TCGv_i64 src) { - unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29)); - target_ulong target = dc->pc + offset; + gen_op_clear_ieee_excp_and_FTT(); + tcg_gen_xori_i64(dst, src, 1ull << 63); +} -#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); - } - } +static void gen_op_fabsd(TCGv_i64 dst, TCGv_i64 src) +{ + gen_op_clear_ieee_excp_and_FTT(); + tcg_gen_andi_i64(dst, src, ~(1ull << 63)); } -static void do_fbranch(DisasContext *dc, int32_t offset, uint32_t insn, int cc) +static void gen_op_fnegq(TCGv_i128 dst, TCGv_i128 src) { - unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29)); - target_ulong target = dc->pc + offset; + TCGv_i64 l = tcg_temp_new_i64(); + TCGv_i64 h = tcg_temp_new_i64(); -#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); - } - } + tcg_gen_extr_i128_i64(l, h, src); + tcg_gen_xori_i64(h, h, 1ull << 63); + tcg_gen_concat_i64_i128(dst, l, h); } -#ifdef TARGET_SPARC64 -static void do_branch_reg(DisasContext *dc, int32_t offset, uint32_t insn, - TCGv r_reg) +static void gen_op_fabsq(TCGv_i128 dst, TCGv_i128 src) { - unsigned int cond = GET_FIELD_SP(insn, 25, 27), a = (insn & (1 << 29)); - target_ulong target = dc->pc + offset; + TCGv_i64 l = tcg_temp_new_i64(); + TCGv_i64 h = tcg_temp_new_i64(); - 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); - } + 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 void gen_op_fcmps(int fccno, TCGv_i32 r_rs1, TCGv_i32 r_rs2) +static void gen_op_fmadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) { - 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; - } + TCGv_i32 z = tcg_constant_i32(0); + gen_helper_fmadds(d, tcg_env, s1, s2, s3, z, z); } -static void gen_op_fcmpd(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2) +static void gen_op_fmaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) { - 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; - } + TCGv_i32 z = tcg_constant_i32(0); + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, z, z); } -static void gen_op_fcmpq(int fccno) +static void gen_op_fmsubs(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) { - 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; - } + TCGv_i32 z = tcg_constant_i32(0); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c); + gen_helper_fmadds(d, tcg_env, s1, s2, s3, z, op); } -static void gen_op_fcmpes(int fccno, TCGv_i32 r_rs1, TCGv_i32 r_rs2) +static void gen_op_fmsubd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) { - 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_i32 z = tcg_constant_i32(0); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c); + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, z, op); } -static void gen_op_fcmped(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2) +static void gen_op_fnmsubs(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) { - 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_i32 z = tcg_constant_i32(0); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c | + float_muladd_negate_result); + gen_helper_fmadds(d, tcg_env, s1, s2, s3, z, op); } -static void gen_op_fcmpeq(int fccno) +static void gen_op_fnmsubd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 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; - } + TCGv_i32 z = tcg_constant_i32(0); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c | + float_muladd_negate_result); + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, z, op); } -#else +static void gen_op_fnmadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) +{ + TCGv_i32 z = tcg_constant_i32(0); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_result); + gen_helper_fmadds(d, tcg_env, s1, s2, s3, z, op); +} -static void gen_op_fcmps(int fccno, TCGv r_rs1, TCGv r_rs2) +static void gen_op_fnmaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) { - gen_helper_fcmps(cpu_fsr, cpu_env, r_rs1, r_rs2); + TCGv_i32 z = tcg_constant_i32(0); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_result); + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, z, op); } -static void gen_op_fcmpd(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2) +/* 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) { - gen_helper_fcmpd(cpu_fsr, cpu_env, r_rs1, r_rs2); + TCGv_i32 fone = tcg_constant_i32(float32_one); + TCGv_i32 mone = tcg_constant_i32(-1); + TCGv_i32 op = tcg_constant_i32(0); + gen_helper_fmadds(d, tcg_env, fone, s1, s2, mone, op); } -static void gen_op_fcmpq(int fccno) +static void gen_op_fhaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2) { - gen_helper_fcmpq(cpu_fsr, cpu_env); + TCGv_i64 fone = tcg_constant_i64(float64_one); + TCGv_i32 mone = tcg_constant_i32(-1); + TCGv_i32 op = tcg_constant_i32(0); + gen_helper_fmaddd(d, tcg_env, fone, s1, s2, mone, op); } -static void gen_op_fcmpes(int fccno, TCGv r_rs1, TCGv r_rs2) +/* 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) { - gen_helper_fcmpes(cpu_fsr, cpu_env, r_rs1, r_rs2); + TCGv_i32 fone = tcg_constant_i32(float32_one); + TCGv_i32 mone = tcg_constant_i32(-1); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c); + gen_helper_fmadds(d, tcg_env, fone, s1, s2, mone, op); } -static void gen_op_fcmped(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2) +static void gen_op_fhsubd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2) { - gen_helper_fcmped(cpu_fsr, cpu_env, r_rs1, r_rs2); + TCGv_i64 fone = tcg_constant_i64(float64_one); + TCGv_i32 mone = tcg_constant_i32(-1); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c); + gen_helper_fmaddd(d, tcg_env, fone, s1, s2, mone, op); } -static void gen_op_fcmpeq(int fccno) +/* 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) { - gen_helper_fcmpeq(cpu_fsr, cpu_env); + TCGv_i32 fone = tcg_constant_i32(float32_one); + TCGv_i32 mone = tcg_constant_i32(-1); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_result); + gen_helper_fmadds(d, tcg_env, fone, s1, s2, mone, op); +} + +static void gen_op_fnhaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2) +{ + TCGv_i64 fone = tcg_constant_i64(float64_one); + TCGv_i32 mone = tcg_constant_i32(-1); + TCGv_i32 op = tcg_constant_i32(float_muladd_negate_result); + gen_helper_fmaddd(d, tcg_env, fone, s1, s2, mone, op); } -#endif -static void gen_op_fpexception_im(DisasContext *dc, int fsr_flags) +static void gen_op_fpexception_im(DisasContext *dc, int ftt) { - tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_NMASK); - tcg_gen_ori_tl(cpu_fsr, cpu_fsr, fsr_flags); + /* + * CEXC is only set when succesfully completing an FPop, + * or when raising FSR_FTT_IEEE_EXCP, i.e. check_ieee_exception. + * Thus we can simply store FTT into this field. + */ + tcg_gen_st_i32(tcg_constant_i32(ftt), tcg_env, + offsetof(CPUSPARCState, fsr_cexc_ftt)); gen_exception(dc, TT_FP_EXCP); } -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; -} - -static void gen_op_clear_ieee_excp_and_FTT(void) -{ - tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_CEXC_NMASK); + return false; } -static void gen_fop_FF(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i32)) +static bool gen_trap_iffpexception(DisasContext *dc) { - 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); +#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 + return false; } -static void gen_ne_fop_FF(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i32, TCGv_i32)) +static bool gen_trap_if_nofpu_fpexception(DisasContext *dc) { - 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 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 void gen_ne_fop_FFF(DisasContext *dc, int rd, int rs1, int rs2, - void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32)) -{ - TCGv_i32 dst, src1, src2; - - src1 = gen_load_fpr_F(dc, rs1); - src2 = gen_load_fpr_F(dc, rs2); - dst = gen_dest_fpr_F(dc); - - gen(dst, src1, src2); - - gen_store_fpr_F(dc, rd, dst); -} -#endif - -static void gen_fop_DD(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i64)) -{ - TCGv_i64 dst, src; - - src = gen_load_fpr_D(dc, rs); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, cpu_env, src); - gen_helper_check_ieee_exceptions(cpu_fsr, cpu_env); - - gen_store_fpr_D(dc, rd, dst); -} - -#ifdef TARGET_SPARC64 -static void gen_ne_fop_DD(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i64, TCGv_i64)) -{ - TCGv_i64 dst, src; - - src = gen_load_fpr_D(dc, rs); - dst = gen_dest_fpr_D(dc, rd); - - gen(dst, src); - - gen_store_fpr_D(dc, rd, dst); -} -#endif - -static 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 | MO_ALIGN); -} - -static void gen_ldstub(DisasContext *dc, TCGv dst, TCGv addr, int mmu_idx) -{ - TCGv m1 = tcg_constant_tl(0xff); - gen_address_mask(dc, addr); - tcg_gen_atomic_xchg_tl(dst, addr, m1, mmu_idx, MO_UB); + 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, @@ -1935,15 +1539,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) @@ -1954,14 +1568,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; @@ -1986,7 +1608,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. */ @@ -2030,6 +1652,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 */ @@ -2040,6 +1663,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 */ @@ -2053,6 +1677,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) { @@ -2070,6 +1695,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) { @@ -2087,6 +1713,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: @@ -2145,36 +1775,62 @@ 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) +{ + g_assert_not_reached(); +} + +static void gen_helper_st_asi(TCGv_env e, TCGv a, TCGv_i64 r, + TCGv_i32 asi, TCGv_i32 mop) { - DisasASI da = get_asi(dc, insn, memop); + g_assert_not_reached(); +} +#endif - switch (da.type) { +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 | MO_ALIGN); + 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_constant_i32(da.asi); - TCGv_i32 r_mop = tcg_constant_i32(memop | MO_ALIGN); + 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); } #endif @@ -2183,72 +1839,75 @@ static void gen_ld_asi(DisasContext *dc, TCGv dst, TCGv addr, } } -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 | MO_ALIGN); + 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_constant_tl(4); - TCGv_i32 tmp = tcg_temp_new_i32(); - int i; - - 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); - } + TCGv_i128 tmp = tcg_temp_new_i128(); + + 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_constant_i32(da.asi); - TCGv_i32 r_mop = tcg_constant_i32(memop | MO_ALIGN); + 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); + gen_helper_st_asi(tcg_env, addr, t64, r_asi, r_mop); } #endif @@ -2259,16 +1918,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. */ @@ -2277,20 +1935,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 | MO_ALIGN); - gen_store_gpr(dc, rd, oldv); + tcg_gen_atomic_cmpxchg_tl(oldv, addr, cmpv, newv, + da->mem_idx, da->memop | MO_ALIGN); break; default: /* ??? Should be DAE_invalid_asi. */ @@ -2299,32 +1952,31 @@ 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_constant_i32(da.asi); + 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_constant_i64(0xff); - gen_helper_st_asi(cpu_env, addr, s64, r_asi, r_mop); + gen_helper_st_asi(tcg_env, addr, s64, r_asi, r_mop); tcg_gen_trunc_i64_tl(dst, t64); @@ -2334,39 +1986,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; + + /* TODO: Use 128-bit load/store below. */ + if (size == MO_128) { + memop = (memop & ~MO_SIZE) | MO_64; + } - switch (da.type) { + 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 | MO_ALIGN); + case MO_32: + d32 = tcg_temp_new_i32(); + tcg_gen_qemu_ld_i32(d32, addr, da->mem_idx, memop); gen_store_fpr_F(dc, rd, d32); break; - case 8: - tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da.mem_idx, - da.memop | MO_ALIGN_4); + + case MO_64: + d64 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(d64, addr, da->mem_idx, memop); + gen_store_fpr_D(dc, rd, d64); break; - case 16: + + case MO_128: d64 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(d64, addr, da.mem_idx, da.memop | MO_ALIGN_4); - tcg_gen_addi_tl(addr, addr, 8); - tcg_gen_qemu_ld_i64(cpu_fpr[rd/2+1], addr, da.mem_idx, - da.memop | MO_ALIGN_4); - tcg_gen_mov_i64(cpu_fpr[rd / 2], d64); + 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(); @@ -2375,24 +2037,19 @@ 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_constant_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; } } else { gen_exception(dc, TT_ILL_INSN); @@ -2401,10 +2058,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 | MO_ALIGN); + 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); } @@ -2412,8 +2069,8 @@ static void gen_ldf_asi(DisasContext *dc, TCGv addr, default: { - TCGv_i32 r_asi = tcg_constant_i32(da.asi); - TCGv_i32 r_mop = tcg_constant_i32(da.memop | MO_ALIGN); + 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 @@ -2421,22 +2078,27 @@ 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); gen_store_fpr_F(dc, rd, d32); break; - case 8: - gen_helper_ld_asi(cpu_fpr[rd / 2], cpu_env, addr, r_asi, r_mop); + case MO_64: + d64 = tcg_temp_new_i64(); + gen_helper_ld_asi(d64, tcg_env, addr, r_asi, r_mop); + gen_store_fpr_D(dc, rd, d64); break; - case 16: + case MO_128: d64 = tcg_temp_new_i64(); - gen_helper_ld_asi(d64, cpu_env, addr, r_asi, r_mop); - tcg_gen_addi_tl(addr, addr, 8); - gen_helper_ld_asi(cpu_fpr[rd/2+1], cpu_env, addr, r_asi, r_mop); - tcg_gen_mov_i64(cpu_fpr[rd / 2], d64); + 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(); @@ -2446,37 +2108,47 @@ static void gen_ldf_asi(DisasContext *dc, TCGv addr, } } -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; + + /* TODO: Use 128-bit load/store below. */ + if (size == MO_128) { + memop = (memop & ~MO_SIZE) | MO_64; + } - switch (da.type) { + 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 | MO_ALIGN); + 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(); @@ -2485,24 +2157,18 @@ static void gen_stf_asi(DisasContext *dc, TCGv addr, case GET_ASI_BLOCK: /* Valid for stdfa on aligned registers only. */ - if (size == 8 && (rd & 7) == 0) { - MemOp memop; - TCGv eight; - int i; - - gen_address_mask(dc, addr); - + if (orig_size == MO_64 && (rd & 7) == 0) { /* The first operation checks required alignment. */ - memop = da.memop | MO_ALIGN_64; - eight = tcg_constant_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; } } else { gen_exception(dc, TT_ILL_INSN); @@ -2511,10 +2177,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 | MO_ALIGN); + 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); } @@ -2529,40 +2194,74 @@ 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 | MO_ALIGN); + 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_extr_i64_tl(hi, lo, 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_extr32_i64(hi, lo, tmp); + 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 @@ -2570,18 +2269,18 @@ static void gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd) real hardware allows others. This can be seen with e.g. FreeBSD 10.3 wrt ASI_IC_TAG. */ { - TCGv_i32 r_asi = tcg_constant_i32(da.asi); - TCGv_i32 r_mop = tcg_constant_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); + 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); } } break; @@ -2591,248 +2290,170 @@ 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); - break; - - case GET_ASI_DIRECT: +#ifdef TARGET_SPARC64 { - TCGv_i64 t64 = tcg_temp_new_i64(); - - /* 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); + 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_concat32_i64(t64, hi, lo); + tcg_gen_concat_i64_i128(t, hi, lo); } - gen_address_mask(dc, addr); - tcg_gen_qemu_st_i64(t64, addr, da.mem_idx, da.memop | MO_ALIGN); + tcg_gen_qemu_st_i128(t, addr, da->mem_idx, mop); } break; +#else + g_assert_not_reached(); +#endif - default: - /* ??? In theory we've handled all of the ASIs that are valid - for stda, and this should raise DAE_invalid_asi. */ + case GET_ASI_DIRECT: { - 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); + /* 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_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_gen_qemu_st_i64(t64, addr, da->mem_idx, da->memop | MO_ALIGN); } 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 | MO_ALIGN); - gen_store_gpr(dc, rd, 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: - return; - case GET_ASI_DIRECT: - gen_address_mask(dc, addr); - tcg_gen_qemu_ld_i64(t64, addr, da.mem_idx, da.memop | MO_ALIGN); - break; - default: + 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. + */ { - TCGv_i32 r_asi = tcg_constant_i32(da.asi); - TCGv_i32 r_mop = tcg_constant_i32(MO_UQ); + 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(); - save_state(dc); - gen_helper_ld_asi(t64, cpu_env, addr, r_asi, r_mop); + 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; - } - - tcg_gen_extr_i64_i32(lo, hi, 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 | MO_ALIGN); - 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_constant_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); - } - } - break; default: + /* ??? 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_constant_i32(da.asi); - TCGv_i32 r_mop = tcg_constant_i32(MO_UQ); + 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_concat_tl_i64(t64, lo, hi); + } else { + tcg_gen_concat_tl_i64(t64, hi, lo); + } save_state(dc); - gen_helper_st_asi(cpu_env, addr, t64, r_asi, r_mop); + gen_helper_st_asi(tcg_env, addr, t64, r_asi, r_mop); } break; } } -#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 = tcg_temp_new(); - 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_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); + dst = tcg_temp_new_i32(); zero = tcg_constant_i32(0); tcg_gen_movcond_i32(TCG_COND_NE, dst, c32, zero, s1, s2); 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); - - 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]); +#ifdef TARGET_SPARC64 + TCGv c2 = tcg_constant_tl(cmp->c2); + TCGv_i64 h = tcg_temp_new_i64(); + TCGv_i64 l = tcg_temp_new_i64(); - 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 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] */ { @@ -2843,2750 +2464,3234 @@ static void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr, TCGv_env cpu_env) } #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; - 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; - } - - /* Theory of operation: there are two tables, left and right (not to - be confused with the left and right versions of the opcode). These - are indexed by the low 3 bits of the inputs. To make things "easy", - these tables are loaded into two constants, TABL and TABR below. - The operation index = (input & imask) << shift calculates the index - into the constant, while val = (table >> index) & omask calculates - the value we're looking for. */ - switch (width) { - case 8: - imask = 0x7; - shift = 3; - omask = 0xff; - if (left) { - tabl = 0x80c0e0f0f8fcfeffULL; - tabr = 0xff7f3f1f0f070301ULL; - } else { - tabl = 0x0103070f1f3f7fffULL; - tabr = 0xfffefcf8f0e0c080ULL; +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(); } - break; - case 16: - imask = 0x6; - shift = 1; - omask = 0xf; - if (left) { - tabl = 0x8cef; - tabr = 0xf731; + } 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 { - tabl = 0x137f; - tabr = 0xfec8; + gen_mov_pc_npc(dc); + dc->npc = dest; } - break; - case 32: - imask = 0x4; - shift = 0; - omask = 0x3; - if (left) { - tabl = (2 << 2) | 3; - tabr = (3 << 2) | 1; + return true; + } + + if (cmp->cond == TCG_COND_NEVER) { + npc = dc->npc; + if (npc & 3) { + gen_mov_pc_npc(dc); + if (annul) { + tcg_gen_addi_tl(cpu_pc, cpu_pc, 4); + } + tcg_gen_addi_tl(cpu_npc, cpu_pc, 4); } else { - tabl = (1 << 2) | 3; - tabr = (3 << 2) | 2; + dc->pc = npc + (annul ? 4 : 0); + dc->npc = dc->pc + 4; } - break; - default: - abort(); + return true; } - 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); + flush_cond(dc); + npc = dc->npc; - tcg_gen_shr_tl(lo1, tcg_constant_tl(tabl), lo1); - tcg_gen_shr_tl(lo2, tcg_constant_tl(tabr), lo2); - tcg_gen_andi_tl(lo1, lo1, omask); - tcg_gen_andi_tl(lo2, lo2, omask); + if (annul) { + TCGLabel *l1 = gen_new_label(); - amask = -8; - if (AM_CHECK(dc)) { - amask &= 0xffffffffULL; + 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; + } } - tcg_gen_andi_tl(s1, s1, amask); - tcg_gen_andi_tl(s2, s2, amask); + return true; +} - /* Compute dst = (s1 == s2 ? lo1 : lo1 & lo2). */ - tcg_gen_and_tl(lo2, lo2, lo1); - tcg_gen_movcond_tl(TCG_COND_EQ, dst, s1, s2, lo1, lo2); +static bool raise_priv(DisasContext *dc) +{ + gen_exception(dc, TT_PRIV_INSN); + return true; } -static void gen_alignaddr(TCGv dst, TCGv s1, TCGv s2, bool left) +static bool raise_unimpfpop(DisasContext *dc) { - TCGv tmp = tcg_temp_new(); + gen_op_fpexception_im(dc, FSR_FTT_UNIMPFPOP); + return true; +} - tcg_gen_add_tl(tmp, s1, s2); - tcg_gen_andi_tl(dst, tmp, -8); - if (left) { - tcg_gen_neg_tl(tmp, tmp); +static bool gen_trap_float128(DisasContext *dc) +{ + if (dc->def->features & CPU_FEATURE_FLOAT128) { + return false; } - tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, tmp, 0, 3); + return raise_unimpfpop(dc); } -static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) +static bool do_bpcc(DisasContext *dc, arg_bcc *a) { - TCGv t1, t2, shift; + DisasCompare cmp; - t1 = tcg_temp_new(); - t2 = tcg_temp_new(); - shift = tcg_temp_new(); + gen_compare(&cmp, a->cc, a->cond, dc); + return advance_jump_cond(dc, &cmp, a->a, a->i); +} - tcg_gen_andi_tl(shift, gsr, 7); - tcg_gen_shli_tl(shift, shift, 3); - tcg_gen_shl_tl(t1, s1, shift); +TRANS(Bicc, ALL, do_bpcc, a) +TRANS(BPcc, 64, do_bpcc, a) - /* 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); +static bool do_fbpfcc(DisasContext *dc, arg_bcc *a) +{ + DisasCompare cmp; - tcg_gen_or_tl(dst, t1, t2); + 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); } -#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(FBPfcc, 64, do_fbpfcc, a) +TRANS(FBfcc, ALL, do_fbpfcc, a) -/* before an instruction, dc->pc must be static */ -static void disas_sparc_insn(DisasContext * dc, unsigned int insn) +static bool trans_BPr(DisasContext *dc, arg_BPr *a) { - 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); + DisasCompare cmp; - 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; - } -#else - case 0x7: /* CBN+x */ - { - goto ncp_insn; - } -#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); + 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); +} - 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 = tcg_temp_new(); - TCGv cpu_tmp0; - - if (xop == 0x3a) { /* generate trap */ - int cond = GET_FIELD(insn, 3, 6); - TCGv_i32 trap; - TCGLabel *l1 = NULL; - int mask; - - if (cond == 0) { - /* Trap never. */ - break; - } +static bool trans_CALL(DisasContext *dc, arg_CALL *a) +{ + target_long target = address_mask_i(dc, dc->pc + a->i * 4); - save_state(dc); + gen_store_gpr(dc, 15, tcg_constant_tl(dc->pc)); + gen_mov_pc_npc(dc); + dc->npc = target; + return true; +} - if (cond != 8) { - /* Conditional trap. */ - DisasCompare cmp; +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 - /* 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; - } + return false; #else - gen_compare(&cmp, 0, cond, dc); + gen_exception(dc, TT_NCP_INSN); + return true; #endif - l1 = gen_new_label(); - tcg_gen_brcond_tl(tcg_invert_cond(cmp.cond), - cmp.c1, cmp.c2, l1); - } +} - 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); - } +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); +} - gen_helper_raise_exception(cpu_env, trap); +/* + * Major Opcode 10 -- integer, floating-point, vis, and system insns. + */ - 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; +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_rdpic(DisasContext *dc, TCGv dst) +{ + return tcg_constant_tl(0); +} + +TRANS(RDPIC, HYPV, do_rd_special, supervisor(dc), a->rd, do_rdpic) + + +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 - 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_constant_i32(dc->mem_idx); - tcg_gen_ld_ptr(r_tickptr, cpu_env, - offsetof(CPUSPARCState, tick)); - if (translator_io_start(&dc->base)) { - dc->base.is_jmp = DISAS_EXIT; - } - gen_helper_tick_get_count(cpu_dst, cpu_env, r_tickptr, - r_const); - gen_store_gpr(dc, rd, cpu_dst); - } - 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_constant_i32(dc->mem_idx); - tcg_gen_ld_ptr(r_tickptr, cpu_env, - offsetof(CPUSPARCState, stick)); - if (translator_io_start(&dc->base)) { - dc->base.is_jmp = DISAS_EXIT; - } - gen_helper_tick_get_count(cpu_dst, cpu_env, r_tickptr, - r_const); - gen_store_gpr(dc, rd, cpu_dst); - } - 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); + return tcg_constant_tl(dc->asi); #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; - } + qemu_build_not_reached(); #endif - gen_store_gpr(dc, rd, cpu_dst); - break; - } else if (xop == 0x2a) { /* rdwim / V9 rdpr */ - if (!supervisor(dc)) { - goto priv_insn; - } - cpu_tmp0 = tcg_temp_new(); +} + +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 - 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)); - } - 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)); - } - 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)); - } - 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)); - } - break; - case 4: // tick - { - TCGv_ptr r_tickptr; - TCGv_i32 r_const; - - r_tickptr = tcg_temp_new_ptr(); - r_const = tcg_constant_i32(dc->mem_idx); - tcg_gen_ld_ptr(r_tickptr, cpu_env, - offsetof(CPUSPARCState, tick)); - if (translator_io_start(&dc->base)) { - dc->base.is_jmp = DISAS_EXIT; - } - gen_helper_tick_get_count(cpu_tmp0, cpu_env, - r_tickptr, r_const); - } - 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; - } + 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 - tcg_gen_ext_i32_tl(cpu_tmp0, cpu_wim); -#endif - gen_store_gpr(dc, rd, cpu_tmp0); - break; + qemu_build_not_reached(); #endif -#if defined(TARGET_SPARC64) || !defined(CONFIG_USER_ONLY) - } else if (xop == 0x2b) { /* rdtbr / V9 flushw */ +} + +TRANS(RDPR_tpc, 64, do_rd_special, supervisor(dc), a->rd, do_rdtpc) + +static TCGv do_rdtnpc(DisasContext *dc, TCGv dst) +{ #ifdef TARGET_SPARC64 - gen_helper_flushw(cpu_env); + 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 - if (!supervisor(dc)) - goto priv_insn; - gen_store_gpr(dc, rd, cpu_tbr); -#endif - break; + qemu_build_not_reached(); #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; +} + +TRANS(RDPR_tnpc, 64, do_rd_special, supervisor(dc), a->rd, do_rdtnpc) + +static TCGv do_rdtstate(DisasContext *dc, TCGv dst) +{ #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; + 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 - 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); +} +TRANS(RDPR_tstate, 64, do_rd_special, supervisor(dc), a->rd, do_rdtstate) + +static TCGv do_rdtt(DisasContext *dc, TCGv dst) +{ #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); \ - } 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); \ - } 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); \ - } 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 = tcg_temp_new(); - 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 = tcg_temp_new(); - 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 = tcg_temp_new(); - 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); + 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 - } 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; +} + +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 - case 0x9: /* V9 mulx */ - tcg_gen_mul_i64(cpu_dst, cpu_src1, cpu_src2); - break; + tcg_gen_trunc_tl_i32(cpu_fprs, src); + dc->fprs_dirty = 0; + dc->base.is_jmp = DISAS_EXIT; +#else + qemu_build_not_reached(); #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; +} + +TRANS(WRFPRS, 64, do_wr_special, a, true, do_wrfprs) + +static bool do_priv_nop(DisasContext *dc, bool priv) +{ + if (!priv) { + return raise_priv(dc); + } + return advance_pc(dc); +} + +TRANS(WRPCR, HYPV, do_priv_nop, supervisor(dc)) +TRANS(WRPIC, HYPV, do_priv_nop, supervisor(dc)) + +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 - 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 = tcg_temp_new(); - 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 = tcg_temp_new(); - 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 = tcg_temp_new(); - 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 = tcg_temp_new(); - 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; + 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 - 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. - * TODO: Could notice src1 = %g0 and IS_IMM, - * update DisasContext and not exit the TB. - */ - save_state(dc); - gen_op_next_insn(); - tcg_gen_lookup_and_goto_ptr(); - 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)); - translator_io_start(&dc->base); - gen_helper_tick_set_limit(r_tickptr, - cpu_tick_cmpr); - /* 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)); - translator_io_start(&dc->base); - gen_helper_tick_set_count(r_tickptr, - cpu_tmp0); - /* 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; + qemu_build_not_reached(); #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)); - translator_io_start(&dc->base); - gen_helper_tick_set_limit(r_tickptr, - cpu_stick_cmpr); - /* 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; +} + +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 - 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; - } + 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 - cpu_tmp0 = tcg_temp_new(); - 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; + qemu_build_not_reached(); #endif - } - break; - case 0x32: /* wrwim, V9 wrpr */ - { - if (!supervisor(dc)) - goto priv_insn; - cpu_tmp0 = tcg_temp_new(); - tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); +} + +TRANS(WRPR_tpc, 64, do_wr_special, a, supervisor(dc), do_wrtpc) + +static void do_wrtnpc(DisasContext *dc, TCGv src) +{ #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)); - } - 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)); - } - 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)); - } - 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)); - } - 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)); - translator_io_start(&dc->base); - gen_helper_tick_set_count(r_tickptr, - cpu_tmp0); - /* 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 (translator_io_start(&dc->base)) { - dc->base.is_jmp = DISAS_EXIT; - } - gen_helper_wrpstate(cpu_env, cpu_tmp0); - 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 (translator_io_start(&dc->base)) { - dc->base.is_jmp = DISAS_EXIT; - } - gen_helper_wrpil(cpu_env, cpu_tmp0); - 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; - } + 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 - 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); - } + qemu_build_not_reached(); #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); +} + +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 - CHECK_IU_FEATURE(dc, HYPV); - if (!hypervisor(dc)) - goto priv_insn; - cpu_tmp0 = tcg_temp_new(); - 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)); - translator_io_start(&dc->base); - gen_helper_tick_set_limit(r_tickptr, - cpu_hstick_cmpr); - /* End TB to handle timer interrupt */ - dc->base.is_jmp = DISAS_EXIT; - } - break; - case 6: // hver readonly - default: - goto illegal_insn; - } -#endif - } - break; + 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 - 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); - 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); - gen_store_gpr(dc, rd, dst); - break; - } + 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 - 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 */ +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 - goto illegal_insn; + tcg_gen_ext32u_i64(t2, cpu_regs[a->rs2_or_imm]); #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 = tcg_temp_new(); - 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_LOOKUP; - goto jmp_insn; -#endif - } else { - cpu_src1 = get_src1(dc, insn); - cpu_tmp0 = tcg_temp_new(); - 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_LOOKUP; - } - 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; - translator_io_start(&dc->base); - gen_helper_done(cpu_env); - goto jmp_insn; - case 1: - if (!supervisor(dc)) - goto priv_insn; - dc->npc = DYNAMIC_PC; - dc->pc = DYNAMIC_PC; - translator_io_start(&dc->base); - gen_helper_retry(cpu_env); - goto jmp_insn; - default: - goto illegal_insn; - } - } - break; + tcg_gen_extu_i32_i64(t2, cpu_regs[a->rs2_or_imm]); #endif - default: - goto illegal_insn; - } - } - break; + } + + 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: + 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 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 = tcg_temp_new(); - - 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_ld_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TEUL | MO_ALIGN); - break; - case 0x1: /* ldub, load unsigned byte */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_UB); - break; - case 0x2: /* lduh, load unsigned halfword */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TEUW | MO_ALIGN); - 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_ld_i64(t64, cpu_addr, - dc->mem_idx, MO_TEUQ | MO_ALIGN); - 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_gen_ext32u_tl(cpu_val, cpu_val); - } - break; - case 0x9: /* ldsb, load signed byte */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld_tl(cpu_val, cpu_addr, dc->mem_idx, MO_SB); - break; - case 0xa: /* ldsh, load signed halfword */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TESW | MO_ALIGN); - 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; + case 16: + 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: + 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(); + } + + /* 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); + } + + /* 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); + + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +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); + tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, tmp, 0, 3); +#else + g_assert_not_reached(); +#endif +} + +static void gen_op_alignaddrl(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); + tcg_gen_neg_tl(tmp, tmp); + tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, tmp, 0, 3); +#else + g_assert_not_reached(); +#endif +} + +TRANS(ALIGNADDR, VIS1, do_rrr, a, gen_op_alignaddr) +TRANS(ALIGNADDRL, VIS1, do_rrr, a, gen_op_alignaddrl) + +static void gen_op_bmask(TCGv dst, TCGv s1, TCGv s2) +{ +#ifdef TARGET_SPARC64 + tcg_gen_add_tl(dst, s1, s2); + tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, dst, 32, 32); +#else + g_assert_not_reached(); +#endif +} + +TRANS(BMASK, VIS2, do_rrr, a, gen_op_bmask) + +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; +} + +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) + +static bool do_shift_r(DisasContext *dc, arg_shiftr *a, bool l, bool u) +{ + TCGv dst, src1, src2; + + /* Reject 64-bit shifts for sparc32. */ + if (avail_32(dc) && a->x) { + return false; + } + + 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 { + if (!a->x) { + tcg_gen_ext32s_tl(dst, src1); + src1 = dst; + } + tcg_gen_sar_tl(dst, src1, src2); + } + 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); + } + } 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); + } -#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_ld_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TESL | MO_ALIGN); - break; - case 0x0b: /* V9 ldx */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_ld_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TEUQ | MO_ALIGN); - 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 | MO_ALIGN); - 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 | MO_ALIGN); - gen_helper_ldxfsr(cpu_fsr, cpu_env, cpu_fsr, t64); - break; - } -#endif - cpu_dst_32 = tcg_temp_new_i32(); - tcg_gen_qemu_ld_i32(cpu_dst_32, cpu_addr, - dc->mem_idx, MO_TEUL | MO_ALIGN); - 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); - 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_st_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TEUL | MO_ALIGN); - break; - case 0x5: /* stb, store byte */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_st_tl(cpu_val, cpu_addr, dc->mem_idx, MO_UB); - break; - case 0x6: /* sth, store halfword */ - gen_address_mask(dc, cpu_addr); - tcg_gen_qemu_st_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TEUW | MO_ALIGN); - 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_st_i64(t64, cpu_addr, - dc->mem_idx, MO_TEUQ | MO_ALIGN); - } - 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_st_tl(cpu_val, cpu_addr, - dc->mem_idx, MO_TEUQ | MO_ALIGN); - 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 | MO_ALIGN); - break; - case 0x25: /* stfsr, V9 stxfsr */ - { -#ifdef TARGET_SPARC64 - gen_address_mask(dc, cpu_addr); - if (rd == 1) { - tcg_gen_qemu_st_tl(cpu_fsr, cpu_addr, - dc->mem_idx, MO_TEUQ | MO_ALIGN); - break; - } -#endif - tcg_gen_qemu_st_tl(cpu_fsr, cpu_addr, - dc->mem_idx, MO_TEUL | MO_ALIGN); - } - 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; + 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; } - /* default case for non jump instructions */ - if (dc->npc & 3) { - switch (dc->npc) { - case DYNAMIC_PC: - case DYNAMIC_PC_LOOKUP: - dc->pc = dc->npc; - gen_op_next_insn(); - break; - case 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; - break; - default: - g_assert_not_reached(); - } + 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 { - dc->pc = dc->npc; - dc->npc = dc->npc + 4; + gen_helper_fcmps(cpu_fcc[a->cc], tcg_env, src1, src2); } - jmp_insn: - return; - illegal_insn: - gen_exception(dc, TT_ILL_INSN); - return; - unimp_flush: - gen_exception(dc, TT_UNIMP_FLUSH); - return; -#if !defined(CONFIG_USER_ONLY) - priv_insn: - gen_exception(dc, TT_PRIV_INSN); - return; -#endif - nfpu_insn: - gen_op_fpexception_im(dc, FSR_FTT_UNIMPFPOP); - return; -#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) - nfq_insn: - gen_op_fpexception_im(dc, FSR_FTT_SEQ_ERROR); - return; -#endif -#ifndef TARGET_SPARC64 - ncp_insn: - gen_exception(dc, TT_NCP_INSN); - return; -#endif + 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], tcg_env, 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], tcg_env, 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 @@ -5625,12 +5730,14 @@ static void sparc_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) 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; @@ -5643,8 +5750,11 @@ 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: @@ -5654,10 +5764,10 @@ static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) break; } + may_lookup = true; if (dc->pc & 3) { switch (dc->pc) { case DYNAMIC_PC_LOOKUP: - may_lookup = true; break; case DYNAMIC_PC: may_lookup = false; @@ -5667,10 +5777,24 @@ static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } else { tcg_gen_movi_tl(cpu_pc, dc->pc); - may_lookup = true; } - save_npc(dc); + 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 { @@ -5690,13 +5814,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 = { @@ -5705,11 +5835,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 sparc_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc = {}; @@ -5724,67 +5853,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]); } @@ -5794,34 +5909,4 @@ 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; - target_ulong pc = data[0]; - target_ulong npc = data[1]; - - env->pc = pc; - if (npc == DYNAMIC_PC) { - /* dynamic NPC: already stored */ - } else if (npc & JUMP_PC) { - /* jump PC: use 'cond' and the jump targets of the translation */ - if (env->cond) { - env->npc = npc & ~3; - } else { - env->npc = pc + 4; - } - } else { - env->npc = npc; - } } diff --git a/target/sparc/translate.h b/target/sparc/translate.h new file mode 100644 index 00000000000..a46fa4f124b --- /dev/null +++ b/target/sparc/translate.h @@ -0,0 +1,17 @@ +/* + * QEMU translation definitions for SPARC + * + * Copyright (c) 2024 Linaro, Ltd + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef SPARC_TRANSLATION_H +#define SPARC_TRANSLATION_H + +/* 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 + +#endif diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c index 3afdc6975cf..371f5445a1f 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,306 +98,274 @@ 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; - d.ll = src2; +uint64_t helper_fmul8x16(uint32_t src1, uint64_t src2) +{ + VIS64 d; + VIS32 s; -#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; + s.l = src1; + d.ll = src2; - 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; - - s.ll = src1; - d.ll = src2; + VIS32 s; + VIS64 d; -#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; + s.l = src1; + d.ll = 0; - 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; } -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(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_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; } -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) * ((int32_t)s.VIS_SW64(r) >> 8); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_W64(r) = tmp >> 8; + 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; +} - PMUL(0); - PMUL(1); - PMUL(2); - PMUL(3); -#undef PMUL +uint64_t helper_fexpand(uint32_t src2) +{ + VIS32 s; + VIS64 d; + + 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; + d.VIS_W64(3) = s.VIS_B32(3) << 4; return d.ll; } -uint64_t helper_fmul8ulx16(uint64_t src1, uint64_t src2) +uint64_t helper_fcmpeq8(uint64_t src1, uint64_t src2) { - VIS64 s, d; - uint32_t tmp; + 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; +} - s.ll = src1; - d.ll = src2; +uint64_t helper_fcmpne8(uint64_t src1, uint64_t src2) +{ + return helper_fcmpeq8(src1, src2) ^ 0xff; +} -#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; +uint64_t helper_fcmple8(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; - PMUL(0); - PMUL(1); - PMUL(2); - PMUL(3); -#undef PMUL + s1.ll = src1; + s2.ll = src2; - return d.ll; + for (int i = 0; i < 8; ++i) { + r |= (s1.VIS_SB64(i) <= s2.VIS_SB64(i)) << i; + } + return r; } -uint64_t helper_fmuld8sux16(uint64_t src1, uint64_t src2) +uint64_t helper_fcmpgt8(uint64_t src1, uint64_t src2) { - VIS64 s, d; - uint32_t tmp; + return helper_fcmple8(src1, src2) ^ 0xff; +} - s.ll = src1; - d.ll = src2; +uint64_t helper_fcmpule8(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; -#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; + s1.ll = src1; + s2.ll = src2; - /* Reverse calculation order to handle overlap */ - PMUL(1); - PMUL(0); -#undef PMUL + for (int i = 0; i < 8; ++i) { + r |= (s1.VIS_B64(i) <= s2.VIS_B64(i)) << i; + } + return r; +} - return d.ll; +uint64_t helper_fcmpugt8(uint64_t src1, uint64_t src2) +{ + return helper_fcmpule8(src1, src2) ^ 0xff; } -uint64_t helper_fmuld8ulx16(uint64_t src1, uint64_t src2) +uint64_t helper_fcmpeq16(uint64_t src1, uint64_t src2) { - VIS64 s, d; - uint32_t tmp; + 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; +} - s.ll = src1; - d.ll = src2; +uint64_t helper_fcmpne16(uint64_t src1, uint64_t src2) +{ + return helper_fcmpeq16(src1, src2) ^ 0xf; +} -#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; +uint64_t helper_fcmple16(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; - /* Reverse calculation order to handle overlap */ - PMUL(1); - PMUL(0); -#undef PMUL + s1.ll = src1; + s2.ll = src2; - return d.ll; + for (int i = 0; i < 4; ++i) { + r |= (s1.VIS_SW64(i) <= s2.VIS_SW64(i)) << i; + } + return r; } -uint64_t helper_fexpand(uint64_t src1, uint64_t src2) +uint64_t helper_fcmpgt16(uint64_t src1, uint64_t src2) { - VIS32 s; - VIS64 d; + return helper_fcmple16(src1, src2) ^ 0xf; +} - s.l = (uint32_t)src1; - d.ll = src2; - 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; - d.VIS_W64(3) = s.VIS_B32(3) << 4; +uint64_t helper_fcmpule16(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; - return d.ll; -} + s1.ll = src1; + s2.ll = src2; -#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; \ + for (int i = 0; i < 4; ++i) { + r |= (s1.VIS_W64(i) <= s2.VIS_W64(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) - -#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_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; +} -#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_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; +} -VIS_CMPHELPER(helper_fcmpgt, FCMPGT) -VIS_CMPHELPER(helper_fcmpeq, FCMPEQ) -VIS_CMPHELPER(helper_fcmple, FCMPLE) -VIS_CMPHELPER(helper_fcmpne, FCMPNE) +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 3a7c0ff943d..9ad9d01e8b9 100644 --- a/target/sparc/win_helper.c +++ b/target/sparc/win_helper.c @@ -20,56 +20,69 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "trace.h" -static inline void memcpy32(target_ulong *dst, const target_ulong *src) -{ - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - dst[3] = src[3]; - dst[4] = src[4]; - dst[5] = src[5]; - dst[6] = src[6]; - dst[7] = src[7]; -} - void cpu_set_cwp(CPUSPARCState *env, int new_cwp) { /* put the modified wrap registers at their proper location */ if (env->cwp == env->nwindows - 1) { - memcpy32(env->regbase, env->regbase + env->nwindows * 16); + memcpy(env->regbase, env->regbase + env->nwindows * 16, + sizeof(env->gregs)); } env->cwp = new_cwp; /* put the wrap registers at their temporary location */ if (new_cwp == env->nwindows - 1) { - memcpy32(env->regbase + env->nwindows * 16, env->regbase); + memcpy(env->regbase + env->nwindows * 16, env->regbase, + sizeof(env->gregs)); } env->regwptr = env->regbase + (new_cwp * 16); } 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 +90,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 +168,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 +256,29 @@ void helper_restored(CPUSPARCState *env) target_ulong cpu_get_ccr(CPUSPARCState *env) { - target_ulong psr; + target_ulong ccr = 0; + + 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; - psr = cpu_get_psr(env); + ccr |= env->xcc_C << 4; + ccr |= (env->cc_V < 0) << 5; + ccr |= (env->xcc_Z == 0) << 6; + ccr |= (env->cc_N < 0) << 7; - return ((env->xcc >> 20) << 4) | ((psr & PSR_ICC) >> 20); + 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) @@ -327,8 +350,8 @@ void cpu_gl_switch_gregs(CPUSPARCState *env, uint32_t new_gl) dst = get_gl_gregset(env, env->gl); if (src != dst) { - memcpy32(dst, env->gregs); - memcpy32(env->gregs, src); + memcpy(dst, env->gregs, sizeof(env->gregs)); + memcpy(env->gregs, src, sizeof(env->gregs)); } } @@ -359,8 +382,8 @@ void cpu_change_pstate(CPUSPARCState *env, uint32_t new_pstate) /* Switch global register bank */ src = get_gregset(env, new_pstate_regs); dst = get_gregset(env, pstate_regs); - memcpy32(dst, env->gregs); - memcpy32(env->gregs, src); + memcpy(dst, env->gregs, sizeof(env->gregs)); + memcpy(env->gregs, src, sizeof(env->gregs)); } else { trace_win_helper_no_switch_pstate(new_pstate_regs); } @@ -373,9 +396,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 +411,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 +440,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 +469,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 e29d551dd67..eb33a67c419 100644 --- a/target/tricore/cpu-param.h +++ b/target/tricore/cpu-param.h @@ -8,9 +8,10 @@ #ifndef TRICORE_CPU_PARAM_H #define TRICORE_CPU_PARAM_H -#define TARGET_LONG_BITS 32 #define TARGET_PAGE_BITS 14 #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 +#define TARGET_INSN_START_EXTRA_WORDS 0 + #endif diff --git a/target/tricore/cpu-qom.h b/target/tricore/cpu-qom.h index 612731daa09..e35dc1ad2d8 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; - ResettablePhases parent_phases; -}; - +#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 133a9ac70e8..4f035b6f768 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -20,68 +20,65 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/translation-block.h" #include "qemu/error-report.h" #include "tcg/debug-assert.h" +#include "accel/tcg/cpu-ops.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 cpu_env(cs)->PC; +} - return env->PC; +static TCGTBCPUState tricore_get_tb_cpu_state(CPUState *cs) +{ + CPUTriCoreState *env = cpu_env(cs); + + return (TCGTBCPUState){ + .pc = env->PC, + .flags = FIELD_DP32(0, TB_FLAGS, PRIV, extract32(env->PSW, 10, 2)), + }; } static void tricore_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - TriCoreCPU *cpu = TRICORE_CPU(cs); - CPUTriCoreState *env = &cpu->env; - - tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL)); - env->PC = tb->pc; + 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_hold(Object *obj) +static void tricore_cpu_reset_hold(Object *obj, ResetType type) { - CPUState *s = CPU(obj); - 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); if (tcc->parent_phases.hold) { - tcc->parent_phases.hold(obj); + tcc->parent_phases.hold(obj, type); } - cpu_state_reset(env); + cpu_state_reset(cpu_env(cs)); } static bool tricore_cpu_has_work(CPUState *cs) @@ -89,6 +86,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); @@ -124,14 +126,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; @@ -140,10 +134,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; } @@ -175,23 +166,37 @@ static void tc37x_initfn(Object *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 = { + .has_work = tricore_cpu_has_work, .get_phys_page_debug = tricore_cpu_get_phys_page_debug, }; -#include "hw/core/tcg-cpu-ops.h" - -static const struct TCGCPUOps tricore_tcg_ops = { +static const TCGCPUOps tricore_tcg_ops = { + /* MTTCG not yet supported: require strict ordering */ + .guest_default_memory_order = TCG_MO_ALL, + .mttcg_supported = false, .initialize = tricore_tcg_init, + .translate_code = tricore_translate_code, + .get_tb_cpu_state = tricore_get_tb_cpu_state, .synchronize_from_tb = tricore_cpu_synchronize_from_tb, .restore_state_to_opc = tricore_restore_state_to_opc, + .mmu_index = tricore_cpu_mmu_index, .tlb_fill = tricore_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_uint32, + .cpu_exec_interrupt = tricore_cpu_exec_interrupt, + .cpu_exec_halt = tricore_cpu_has_work, + .cpu_exec_reset = cpu_reset, }; -static void tricore_cpu_class_init(ObjectClass *c, void *data) +static void tricore_cpu_class_init(ObjectClass *c, const void *data) { TriCoreCPUClass *mcc = TRICORE_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); @@ -204,7 +209,6 @@ static void tricore_cpu_class_init(ObjectClass *c, void *data) 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->gdb_read_register = tricore_cpu_gdb_read_register; cc->gdb_write_register = tricore_cpu_gdb_write_register; @@ -230,7 +234,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, diff --git a/target/tricore/cpu.h b/target/tricore/cpu.h index 3708405be89..82085fbc32f 100644 --- a/target/tricore/cpu.h +++ b/target/tricore/cpu.h @@ -22,158 +22,38 @@ #include "cpu-qom.h" #include "hw/registerfields.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" #include "tricore-defs.h" +#ifdef CONFIG_USER_ONLY +#error "TriCore does not support user mode emulation" +#endif + 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; - - /* 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; +#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 + /* Floating Point Registers */ float_status fp_status; @@ -188,14 +68,17 @@ 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); @@ -368,36 +251,13 @@ 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); +void tricore_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); -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; - - 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/fpu_helper.c b/target/tricore/fpu_helper.c index cb7ee7dd35c..1b72dcc5f5c 100644 --- a/target/tricore/fpu_helper.c +++ b/target/tricore/fpu_helper.c @@ -43,7 +43,7 @@ static inline uint8_t f_get_excp_flags(CPUTriCoreState *env) & (float_flag_invalid | float_flag_overflow | float_flag_underflow - | float_flag_output_denormal + | float_flag_output_denormal_flushed | float_flag_divbyzero | float_flag_inexact); } @@ -99,7 +99,7 @@ static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags) some_excp = 1; } - if (flags & float_flag_underflow || flags & float_flag_output_denormal) { + if (flags & float_flag_underflow || flags & float_flag_output_denormal_flushed) { env->FPU_FU = 1 << 31; some_excp = 1; } @@ -109,7 +109,7 @@ static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags) some_excp = 1; } - if (flags & float_flag_inexact || flags & float_flag_output_denormal) { + if (flags & float_flag_inexact || flags & float_flag_output_denormal_flushed) { env->PSW |= 1 << 26; some_excp = 1; } @@ -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 e8f8e5e6ea0..0b73b1280e0 100644 --- a/target/tricore/gdbstub.c +++ b/target/tricore/gdbstub.c @@ -19,6 +19,7 @@ #include "qemu/osdep.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,11 +121,10 @@ 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; diff --git a/target/tricore/helper.c b/target/tricore/helper.c index 6d076ac36fe..e4c53d453dd 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -19,7 +19,10 @@ #include "qemu/log.h" #include "hw/registerfields.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "accel/tcg/cpu-mmu-index.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" #include "fpu/softfloat-helpers.h" #include "qemu/qemu-print.h" @@ -48,7 +51,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)) { @@ -67,8 +70,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; @@ -77,9 +79,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 " + qemu_log_mask(CPU_LOG_MMU, "%s address=0x%" VADDR_PRIx " ret %d physical " HWADDR_FMT_plx " prot %d\n", - __func__, (target_ulong)address, ret, physical, prot); + __func__, address, ret, physical, prot); if (ret == TLBRET_MATCH) { tlb_set_page(cs, address & TARGET_PAGE_MASK, @@ -96,40 +98,36 @@ 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_float_ftz_detection(float_ftz_before_rounding, &env->fp_status); set_default_nan_mode(1, &env->fp_status); + /* Default NaN pattern: sign bit clear, frac msb set */ + set_float_default_nan_pattern(0b01000000, &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); diff --git a/target/tricore/helper.h b/target/tricore/helper.h index 31d71eac7a4..1d97d078b00 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 */ @@ -134,6 +137,7 @@ DEF_HELPER_FLAGS_5(mulr_h, TCG_CALL_NO_RWG_SE, i32, i32, i32, 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) diff --git a/target/tricore/meson.build b/target/tricore/meson.build index 34825b60481..45f49f01288 100644 --- a/target/tricore/meson.build +++ b/target/tricore/meson.build @@ -12,4 +12,4 @@ tricore_ss.add(zlib) tricore_system_ss = ss.source_set() target_arch += {'tricore': tricore_ss} -target_softmmu_arch += {'tricore': tricore_system_ss} +target_system_arch += {'tricore': tricore_system_ss} diff --git a/target/tricore/op_helper.c b/target/tricore/op_helper.c index 89be1ed648f..9910c13f4b5 100644 --- a/target/tricore/op_helper.c +++ b/target/tricore/op_helper.c @@ -18,8 +18,7 @@ #include "cpu.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include /* for crc32 */ @@ -1505,8 +1504,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; @@ -2308,6 +2307,69 @@ uint32_t helper_crc32_le(uint32_t arg0, uint32_t arg1) 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; @@ -2395,7 +2457,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)); @@ -2415,7 +2477,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]); @@ -2435,7 +2497,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); @@ -2456,7 +2518,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); @@ -2700,26 +2762,26 @@ void helper_rfm(CPUTriCoreState *env) } } -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); } diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 6ae5ccbf726..3d0e7a10bd8 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -20,10 +20,8 @@ #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 "accel/tcg/cpu-ldst.h" #include "qemu/qemu-print.h" #include "exec/helper-proto.h" @@ -31,6 +29,8 @@ #include "tricore-opcodes.h" #include "exec/translator.h" +#include "exec/translation-block.h" +#include "exec/target_page.h" #include "exec/log.h" #define HELPER_H "helper.h" @@ -95,8 +95,7 @@ enum { void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) { - TriCoreCPU *cpu = TRICORE_CPU(cs); - CPUTriCoreState *env = &cpu->env; + CPUTriCoreState *env = cpu_env(cs); uint32_t psw; int i; @@ -132,7 +131,7 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) #define gen_helper_1arg(name, arg) do { \ TCGv_i32 helper_tmp = tcg_constant_i32(arg); \ - gen_helper_##name(cpu_env, helper_tmp); \ + gen_helper_##name(tcg_env, helper_tmp); \ } while (0) #define GEN_HELPER_LL(name, ret, arg0, arg1, n) do { \ @@ -191,7 +190,7 @@ void tricore_cpu_dump_state(CPUState *cs, FILE *f, int flags) #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); \ } while (0) @@ -341,7 +340,7 @@ static void gen_swapmsk(DisasContext *ctx, int reg, TCGv ea) #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) @@ -350,7 +349,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" @@ -366,7 +365,7 @@ static inline void gen_mfcr(DisasContext *ctx, TCGv ret, int32_t offset) #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 @@ -380,7 +379,7 @@ static inline void gen_mtcr(DisasContext *ctx, TCGv r1, 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) { @@ -788,7 +787,7 @@ 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); } @@ -843,7 +842,7 @@ gen_maddms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, 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); } @@ -867,7 +866,7 @@ gen_maddr64_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } - gen_helper_addr_h(ret, cpu_env, temp64, r1_low, r1_high); + gen_helper_addr_h(ret, tcg_env, temp64, r1_low, r1_high); } static inline void @@ -904,7 +903,7 @@ gen_maddsur32_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_helper_addsur_h(ret, cpu_env, temp64, temp, temp2); + gen_helper_addsur_h(ret, tcg_env, temp64, temp, temp2); } @@ -928,7 +927,7 @@ gen_maddr64s_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } - gen_helper_addr_h_ssov(ret, cpu_env, temp64, r1_low, r1_high); + gen_helper_addr_h_ssov(ret, tcg_env, temp64, r1_low, r1_high); } static inline void @@ -965,21 +964,21 @@ gen_maddsur32s_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_helper_addsur_h_ssov(ret, cpu_env, temp64, temp, temp2); + 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 t_n = tcg_constant_i32(n); - gen_helper_maddr_q(ret, cpu_env, r1, r2, r3, t_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 t_n = tcg_constant_i32(n); - gen_helper_maddr_q_ssov(ret, cpu_env, r1, r2, r3, t_n); + gen_helper_maddr_q_ssov(ret, tcg_env, r1, r2, r3, t_n); } static inline void @@ -1115,7 +1114,7 @@ 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); } @@ -1182,7 +1181,7 @@ gen_madds32_q(TCGv ret, TCGv arg1, TCGv arg2, TCGv arg3, uint32_t n, tcg_gen_mul_i64(t2, t2, t3); tcg_gen_sari_i64(t2, t2, up_shift - n); - gen_helper_madd32_q_add_ssov(ret, cpu_env, t1, t2); + gen_helper_madd32_q_add_ssov(ret, tcg_env, t1, t2); } static inline void @@ -1193,7 +1192,7 @@ gen_madds64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, 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, t_n); + gen_helper_madd64_q_ssov(r1, tcg_env, r1, arg2, arg3, t_n); tcg_gen_extr_i64_i32(rl, rh, r1); } @@ -1346,15 +1345,11 @@ static inline void gen_addi_CC(TCGv ret, TCGv r1, int32_t con) static inline void gen_addc_CC(TCGv ret, TCGv r1, TCGv r2) { - TCGv carry = tcg_temp_new_i32(); - TCGv t0 = tcg_temp_new_i32(); + TCGv t0 = tcg_temp_new_i32(); TCGv result = tcg_temp_new_i32(); - tcg_gen_movi_tl(t0, 0); - tcg_gen_setcondi_tl(TCG_COND_NE, carry, cpu_PSW_C, 0); /* Addition, carry and set C/V/SV bits */ - tcg_gen_add2_i32(result, cpu_PSW_C, r1, t0, carry, t0); - tcg_gen_add2_i32(result, cpu_PSW_C, result, cpu_PSW_C, r2, t0); + tcg_gen_addcio_i32(result, cpu_PSW_C, r1, r2, cpu_PSW_C); /* calc V bit */ tcg_gen_xor_tl(cpu_PSW_V, result, r1); tcg_gen_xor_tl(t0, r1, r2); @@ -1638,7 +1633,7 @@ gen_msubms_h(TCGv ret_low, TCGv ret_high, TCGv r1_low, TCGv r1_high, TCGv r2, 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); } @@ -1662,7 +1657,7 @@ gen_msubr64_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, uint32_t n, GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } - gen_helper_subr_h(ret, cpu_env, temp64, r1_low, r1_high); + gen_helper_subr_h(ret, tcg_env, temp64, r1_low, r1_high); } static inline void @@ -1696,7 +1691,7 @@ gen_msubr64s_h(TCGv ret, TCGv r1_low, TCGv r1_high, TCGv r2, TCGv r3, GEN_HELPER_UU(mul_h, temp64, r2, r3, t_n); break; } - gen_helper_subr_h_ssov(ret, cpu_env, temp64, r1_low, r1_high); + gen_helper_subr_h_ssov(ret, tcg_env, temp64, r1_low, r1_high); } static inline void @@ -1714,14 +1709,14 @@ static inline void gen_msubr_q(TCGv ret, TCGv r1, TCGv r2, TCGv r3, uint32_t n) { TCGv temp = tcg_constant_i32(n); - gen_helper_msubr_q(ret, cpu_env, r1, r2, r3, temp); + 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_constant_i32(n); - gen_helper_msubr_q_ssov(ret, cpu_env, r1, r2, r3, temp); + gen_helper_msubr_q_ssov(ret, tcg_env, r1, r2, r3, temp); } static inline void @@ -1848,7 +1843,7 @@ 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); } @@ -1920,7 +1915,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); + gen_helper_msub32_q_sub_ssov(ret, tcg_env, t1, t3); } static inline void @@ -1931,7 +1926,7 @@ gen_msubs64_q(TCGv rl, TCGv rh, TCGv arg1_low, TCGv arg1_high, TCGv arg2, 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, t_n); + gen_helper_msub64_q_ssov(r1, tcg_env, r1, arg2, arg3, t_n); tcg_gen_extr_i64_i32(rl, rh, r1); } @@ -2018,7 +2013,7 @@ gen_msubadr32_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_helper_subadr_h(ret, cpu_env, temp64, temp, temp2); + gen_helper_subadr_h(ret, tcg_env, temp64, temp, temp2); } static inline void @@ -2084,7 +2079,7 @@ 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); } @@ -2111,7 +2106,7 @@ gen_msubadr32s_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_helper_subadr_h_ssov(ret, cpu_env, temp64, temp, temp2); + gen_helper_subadr_h_ssov(ret, tcg_env, temp64, temp, temp2); } static inline void gen_abs(TCGv ret, TCGv r1) @@ -2164,7 +2159,7 @@ static inline void gen_absdifi(TCGv ret, TCGv r1, int32_t con) static inline void gen_absdifsi(TCGv ret, TCGv r1, int32_t con) { TCGv temp = tcg_constant_i32(con); - gen_helper_absdif_ssov(ret, cpu_env, r1, temp); + gen_helper_absdif_ssov(ret, tcg_env, r1, temp); } static inline void gen_mul_i32s(TCGv ret, TCGv r1, TCGv r2) @@ -2238,26 +2233,26 @@ static inline void gen_muli_i64u(TCGv ret_low, TCGv ret_high, TCGv r1, static inline void gen_mulsi_i32(TCGv ret, TCGv r1, int32_t con) { TCGv temp = tcg_constant_i32(con); - gen_helper_mul_ssov(ret, cpu_env, r1, temp); + 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_constant_i32(con); - gen_helper_mul_suov(ret, cpu_env, r1, temp); + 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_constant_i32(con); - gen_helper_madd32_ssov(ret, cpu_env, r1, r2, temp); + 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_constant_i32(con); - gen_helper_madd32_suov(ret, cpu_env, r1, r2, temp); + gen_helper_madd32_suov(ret, tcg_env, r1, r2, temp); } static void @@ -2371,7 +2366,7 @@ 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); } @@ -2389,7 +2384,7 @@ 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); } @@ -2404,13 +2399,13 @@ gen_maddsui_64(TCGv ret_low, TCGv ret_high, TCGv r1, TCGv r2_low, TCGv r2_high, static inline void gen_msubsi_32(TCGv ret, TCGv r1, TCGv r2, int32_t con) { TCGv temp = tcg_constant_i32(con); - gen_helper_msub32_ssov(ret, cpu_env, r1, r2, temp); + 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_constant_i32(con); - gen_helper_msub32_suov(ret, cpu_env, r1, r2, temp); + gen_helper_msub32_suov(ret, tcg_env, r1, r2, temp); } static inline void @@ -2419,7 +2414,7 @@ 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); } @@ -2437,7 +2432,7 @@ 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); } @@ -2542,7 +2537,7 @@ static void gen_shaci(TCGv ret, TCGv r1, int32_t shift_count) 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) @@ -2595,29 +2590,29 @@ static void gen_sh_condi(int cond, TCGv ret, TCGv r1, int32_t con) 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_constant_i32(con); - gen_helper_add_ssov(ret, cpu_env, r1, temp); + gen_helper_add_ssov(ret, tcg_env, r1, temp); } static inline void gen_addsui(TCGv ret, TCGv r1, int32_t con) { TCGv temp = tcg_constant_i32(con); - gen_helper_add_suov(ret, cpu_env, r1, temp); + 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, @@ -2734,8 +2729,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); @@ -2767,9 +2761,9 @@ 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); } @@ -2780,9 +2774,9 @@ 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); } @@ -2841,7 +2835,7 @@ static void generate_trap(DisasContext *ctx, int class, int tin) 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; } @@ -2996,7 +2990,7 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, break; case OPC2_32_SYS_RET: case OPC2_16_SR_RET: - gen_helper_ret(cpu_env); + gen_helper_ret(tcg_env); ctx->base.is_jmp = DISAS_EXIT; break; /* B-format */ @@ -3493,7 +3487,7 @@ 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); + gen_helper_rfe(tcg_env); ctx->base.is_jmp = DISAS_EXIT; break; case OPC2_16_SR_DEBUG: @@ -3982,7 +3976,7 @@ static void decode_bit_andacc(DisasContext *ctx) pos1, pos2, &tcg_gen_andc_tl, &tcg_gen_and_tl); break; case OPC2_32_BIT_AND_NOR_T: - if (TCG_TARGET_HAS_andc_i32) { + if (tcg_op_supported(INDEX_op_andc, TCG_TYPE_I32, 0)) { gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], pos1, pos2, &tcg_gen_or_tl, &tcg_gen_andc_tl); } else { @@ -4115,7 +4109,7 @@ static void decode_bit_orand(DisasContext *ctx) pos1, pos2, &tcg_gen_andc_tl, &tcg_gen_or_tl); break; case OPC2_32_BIT_OR_NOR_T: - if (TCG_TARGET_HAS_orc_i32) { + if (tcg_op_supported(INDEX_op_orc, TCG_TYPE_I32, 0)) { gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], pos1, pos2, &tcg_gen_or_tl, &tcg_gen_orc_tl); } else { @@ -4741,7 +4735,7 @@ static void decode_bo_addrmode_stctx_post_pre_base(DisasContext *ctx) 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); @@ -4757,18 +4751,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); @@ -4962,8 +4956,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); @@ -4972,10 +4964,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; @@ -5013,7 +5007,7 @@ static void decode_rc_logical_shift(DisasContext *ctx) break; case OPC2_32_RC_SHUFFLE: if (has_feature(ctx, TRICORE_FEATURE_162)) { - TCGv temp = tcg_constant_i32(const9); + 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); @@ -5310,8 +5304,11 @@ 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) { + } 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); } @@ -5590,44 +5587,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]); @@ -5636,15 +5633,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: @@ -5862,10 +5859,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]); @@ -5877,11 +5874,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: @@ -5971,7 +5968,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]); @@ -6255,34 +6252,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); @@ -6483,7 +6501,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: @@ -6492,7 +6510,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: @@ -6518,28 +6536,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: @@ -6554,7 +6560,10 @@ static void decode_rrpw_extract_insert(DisasContext *ctx) 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); } @@ -6669,18 +6678,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: @@ -6711,7 +6728,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: @@ -6727,7 +6744,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: @@ -6764,7 +6781,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: @@ -6780,7 +6797,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_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: @@ -7933,7 +7950,7 @@ static void decode_sys_interrupts(DisasContext *ctx) gen_fret(ctx); break; case OPC2_32_SYS_RFE: - gen_helper_rfe(cpu_env); + gen_helper_rfe(tcg_env); ctx->base.is_jmp = DISAS_EXIT; break; case OPC2_32_SYS_RFM: @@ -7941,10 +7958,10 @@ static void decode_sys_interrupts(DisasContext *ctx) 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); ctx->base.is_jmp = DISAS_EXIT; } else { @@ -7952,10 +7969,10 @@ static void decode_sys_interrupts(DisasContext *ctx) } 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)) { @@ -8192,12 +8209,12 @@ static void decode_32Bit_opc(DisasContext *ctx) 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); + gen_insert(cpu_gpr_d[r3], cpu_gpr_d[r1], temp, temp2, temp3); break; /* RCRW Format */ case OPCM_32_RCRW_MASK_INSERT: @@ -8331,8 +8348,8 @@ 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); + 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); @@ -8367,7 +8384,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); } @@ -8376,18 +8393,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); @@ -8430,25 +8448,16 @@ 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 tricore_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext ctx; translator_loop(cs, tb, max_insns, pc, host_pc, @@ -8470,13 +8479,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"); } @@ -8486,30 +8495,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 bc62b731730..60d2402b6e3 100644 --- a/target/tricore/tricore-opcodes.h +++ b/target/tricore/tricore-opcodes.h @@ -1152,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, @@ -1247,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 diff --git a/target/xtensa/Kconfig b/target/xtensa/Kconfig index a3c8dc7f6d7..e8c2598c4d9 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/cpu-param.h b/target/xtensa/cpu-param.h index b1da0555deb..7a0c22c9005 100644 --- a/target/xtensa/cpu-param.h +++ b/target/xtensa/cpu-param.h @@ -8,7 +8,6 @@ #ifndef XTENSA_CPU_PARAM_H #define XTENSA_CPU_PARAM_H -#define TARGET_LONG_BITS 32 #define TARGET_PAGE_BITS 12 #define TARGET_PHYS_ADDR_SPACE_BITS 32 #ifdef CONFIG_USER_ONLY @@ -17,4 +16,6 @@ #define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif +#define TARGET_INSN_START_EXTRA_WORDS 0 + #endif diff --git a/target/xtensa/cpu-qom.h b/target/xtensa/cpu-qom.h index 419c7d8e4a3..d932346b5fe 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_phases: The parent class' reset phase handlers. - * @config: The CPU core configuration. - * - * An Xtensa CPU model. - */ -struct XtensaCPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - ResettablePhases parent_phases; - - 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 acaf8c905f2..ea9b6df3aa2 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -35,8 +35,9 @@ #include "qemu/module.h" #include "migration/vmstate.h" #include "hw/qdev-clock.h" +#include "accel/tcg/cpu-ops.h" #ifndef CONFIG_USER_ONLY -#include "exec/memory.h" +#include "system/memory.h" #endif @@ -54,6 +55,80 @@ static vaddr xtensa_cpu_get_pc(CPUState *cs) return cpu->env.pc; } +static TCGTBCPUState xtensa_get_tb_cpu_state(CPUState *cs) +{ + CPUXtensaState *env = cpu_env(cs); + uint32_t flags = 0; + target_ulong cs_base = 0; + + flags |= xtensa_get_ring(env); + if (env->sregs[PS] & PS_EXCM) { + flags |= XTENSA_TBFLAG_EXCM; + } else if (xtensa_option_enabled(env->config, XTENSA_OPTION_LOOP)) { + target_ulong lend_dist = + env->sregs[LEND] - (env->pc & -(1u << TARGET_PAGE_BITS)); + + /* + * 0 in the csbase_lend field means that there may not be a loopback + * for any instruction that starts inside this page. Any other value + * means that an instruction that ends at this offset from the page + * start may loop back and will need loopback code to be generated. + * + * lend_dist is 0 when LEND points to the start of the page, but + * no instruction that starts inside this page may end at offset 0, + * so it's still correct. + * + * When an instruction ends at a page boundary it may only start in + * the previous page. lend_dist will be encoded as TARGET_PAGE_SIZE + * for the TB that contains this instruction. + */ + if (lend_dist < (1u << TARGET_PAGE_BITS) + env->config->max_insn_size) { + target_ulong lbeg_off = env->sregs[LEND] - env->sregs[LBEG]; + + cs_base = lend_dist; + if (lbeg_off < 256) { + cs_base |= lbeg_off << XTENSA_CSBASE_LBEG_OFF_SHIFT; + } + } + } + if (xtensa_option_enabled(env->config, XTENSA_OPTION_EXTENDED_L32R) && + (env->sregs[LITBASE] & 1)) { + flags |= XTENSA_TBFLAG_LITBASE; + } + if (xtensa_option_enabled(env->config, XTENSA_OPTION_DEBUG)) { + if (xtensa_get_cintlevel(env) < env->config->debug_level) { + flags |= XTENSA_TBFLAG_DEBUG; + } + if (xtensa_get_cintlevel(env) < env->sregs[ICOUNTLEVEL]) { + flags |= XTENSA_TBFLAG_ICOUNT; + } + } + if (xtensa_option_enabled(env->config, XTENSA_OPTION_COPROCESSOR)) { + flags |= env->sregs[CPENABLE] << XTENSA_TBFLAG_CPENABLE_SHIFT; + } + if (xtensa_option_enabled(env->config, XTENSA_OPTION_WINDOWED_REGISTER) && + (env->sregs[PS] & (PS_WOE | PS_EXCM)) == PS_WOE) { + uint32_t windowstart = xtensa_replicate_windowstart(env) >> + (env->sregs[WINDOW_BASE] + 1); + uint32_t w = ctz32(windowstart | 0x8); + + flags |= (w << XTENSA_TBFLAG_WINDOW_SHIFT) | XTENSA_TBFLAG_CWOE; + flags |= extract32(env->sregs[PS], PS_CALLINC_SHIFT, + PS_CALLINC_LEN) << XTENSA_TBFLAG_CALLINC_SHIFT; + } else { + flags |= 3 << XTENSA_TBFLAG_WINDOW_SHIFT; + } + if (env->yield_needed) { + flags |= XTENSA_TBFLAG_YIELD; + } + + return (TCGTBCPUState){ + .pc = env->pc, + .flags = flags, + .cs_base = cs_base, + }; +} + static void xtensa_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) @@ -63,15 +138,18 @@ static void xtensa_restore_state_to_opc(CPUState *cs, cpu->env.pc = data[0]; } +#ifndef CONFIG_USER_ONLY static bool xtensa_cpu_has_work(CPUState *cs) { -#ifndef CONFIG_USER_ONLY - XtensaCPU *cpu = XTENSA_CPU(cs); + CPUXtensaState *env = cpu_env(cs); - return !cpu->env.runstall && cpu->env.pending_irq_level; -#else - return true; -#endif + return !env->runstall && env->pending_irq_level; +} +#endif /* !CONFIG_USER_ONLY */ + +static int xtensa_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return xtensa_get_cring(cpu_env(cs)); } #ifdef CONFIG_USER_ONLY @@ -88,17 +166,16 @@ bool xtensa_abi_call0(void) } #endif -static void xtensa_cpu_reset_hold(Object *obj) +static void xtensa_cpu_reset_hold(Object *obj, ResetType type) { - CPUState *s = CPU(obj); - 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); if (xcc->parent_phases.hold) { - xcc->parent_phases.hold(obj); + xcc->parent_phases.hold(obj, type); } env->pc = env->config->exception_vector[EXC_RESET0 + env->static_vectors]; @@ -127,10 +204,14 @@ static void xtensa_cpu_reset_hold(Object *obj) #ifndef CONFIG_USER_ONLY reset_mmu(env); - s->halted = env->runstall; + cs->halted = env->runstall; #endif + /* For inf * 0 + NaN, return the input NaN */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); set_no_signaling_nans(!dfpu, &env->fp_status); - set_use_first_nan(!dfpu, &env->fp_status); + /* Default NaN value: sign bit clear, set frac msb */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); + xtensa_use_first_nan(env, !dfpu); } static ObjectClass *xtensa_cpu_class_by_name(const char *cpu_model) @@ -141,10 +222,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; } @@ -154,6 +232,8 @@ static void xtensa_cpu_disas_set_info(CPUState *cs, disassemble_info *info) info->private_data = cpu->env.config->isa; info->print_insn = print_insn_xtensa; + info->endian = TARGET_BIG_ENDIAN ? BFD_ENDIAN_BIG + : BFD_ENDIAN_LITTLE; } static void xtensa_cpu_realizefn(DeviceState *dev, Error **errp) @@ -185,7 +265,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 @@ -204,7 +283,7 @@ XtensaCPU *xtensa_cpu_create_with_clock(const char *cpu_type, Clock *cpu_refclk) { DeviceState *cpu; - cpu = DEVICE(object_new(cpu_type)); + cpu = qdev_new(cpu_type); qdev_connect_clock_in(cpu, "clk-in", cpu_refclk); qdev_realize(cpu, NULL, &error_abort); @@ -220,27 +299,37 @@ static const VMStateDescription vmstate_xtensa_cpu = { #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps xtensa_sysemu_ops = { + .has_work = xtensa_cpu_has_work, .get_phys_page_debug = xtensa_cpu_get_phys_page_debug, }; #endif -#include "hw/core/tcg-cpu-ops.h" +static const TCGCPUOps xtensa_tcg_ops = { + /* Xtensa processors have a weak memory model */ + .guest_default_memory_order = 0, + .mttcg_supported = true, -static const struct TCGCPUOps xtensa_tcg_ops = { .initialize = xtensa_translate_init, + .translate_code = xtensa_translate_code, .debug_excp_handler = xtensa_breakpoint_handler, + .get_tb_cpu_state = xtensa_get_tb_cpu_state, .restore_state_to_opc = xtensa_restore_state_to_opc, + .mmu_index = xtensa_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill = xtensa_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_uint32, .cpu_exec_interrupt = xtensa_cpu_exec_interrupt, + .cpu_exec_halt = xtensa_cpu_has_work, + .cpu_exec_reset = cpu_reset, .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 */ }; -static void xtensa_cpu_class_init(ObjectClass *oc, void *data) +static void xtensa_cpu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); @@ -254,7 +343,6 @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data) &xcc->parent_phases); cc->class_by_name = xtensa_cpu_class_by_name; - cc->has_work = xtensa_cpu_has_work; cc->dump_state = xtensa_cpu_dump_state; cc->set_pc = xtensa_cpu_set_pc; cc->get_pc = xtensa_cpu_get_pc; @@ -273,6 +361,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 87fe992ba68..74122ebe15c 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -30,13 +30,12 @@ #include "cpu-qom.h" #include "qemu/cpu-float.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #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 +228,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 +426,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,10 +489,10 @@ struct XtensaConfig { const xtensa_mpu_entry *mpu_bg; bool use_first_nan; -}; +} XtensaConfig; typedef struct XtensaConfigList { - const XtensaConfig *config; + XtensaConfig *config; struct XtensaConfigList *next; } XtensaConfigList; @@ -547,6 +547,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 +558,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, @@ -577,6 +592,7 @@ void xtensa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, 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); void xtensa_count_regs(const XtensaConfig *config, @@ -587,10 +603,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 @@ -607,6 +619,8 @@ G_NORETURN void xtensa_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, void xtensa_collect_sr_names(const XtensaConfig *config); void xtensa_translate_init(void); +void xtensa_translate_code(CPUState *cs, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc); void **xtensa_get_regfile_by_name(const char *name, int entries, int bits); void xtensa_breakpoint_handler(CPUState *cs); void xtensa_register_core(XtensaConfigList *node); @@ -615,7 +629,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 +714,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 @@ -725,77 +733,13 @@ static inline int cpu_mmu_index(CPUXtensaState *env, bool ifetch) #define XTENSA_CSBASE_LBEG_OFF_MASK 0x00ff0000 #define XTENSA_CSBASE_LBEG_OFF_SHIFT 16 -#include "exec/cpu-all.h" - -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; - *flags = 0; - *flags |= xtensa_get_ring(env); - if (env->sregs[PS] & PS_EXCM) { - *flags |= XTENSA_TBFLAG_EXCM; - } else if (xtensa_option_enabled(env->config, XTENSA_OPTION_LOOP)) { - target_ulong lend_dist = - env->sregs[LEND] - (env->pc & -(1u << TARGET_PAGE_BITS)); - - /* - * 0 in the csbase_lend field means that there may not be a loopback - * for any instruction that starts inside this page. Any other value - * means that an instruction that ends at this offset from the page - * start may loop back and will need loopback code to be generated. - * - * lend_dist is 0 when LEND points to the start of the page, but - * no instruction that starts inside this page may end at offset 0, - * so it's still correct. - * - * When an instruction ends at a page boundary it may only start in - * the previous page. lend_dist will be encoded as TARGET_PAGE_SIZE - * for the TB that contains this instruction. - */ - if (lend_dist < (1u << TARGET_PAGE_BITS) + env->config->max_insn_size) { - target_ulong lbeg_off = env->sregs[LEND] - env->sregs[LBEG]; - - *cs_base = lend_dist; - if (lbeg_off < 256) { - *cs_base |= lbeg_off << XTENSA_CSBASE_LBEG_OFF_SHIFT; - } - } - } - if (xtensa_option_enabled(env->config, XTENSA_OPTION_EXTENDED_L32R) && - (env->sregs[LITBASE] & 1)) { - *flags |= XTENSA_TBFLAG_LITBASE; - } - if (xtensa_option_enabled(env->config, XTENSA_OPTION_DEBUG)) { - if (xtensa_get_cintlevel(env) < env->config->debug_level) { - *flags |= XTENSA_TBFLAG_DEBUG; - } - if (xtensa_get_cintlevel(env) < env->sregs[ICOUNTLEVEL]) { - *flags |= XTENSA_TBFLAG_ICOUNT; - } - } - if (xtensa_option_enabled(env->config, XTENSA_OPTION_COPROCESSOR)) { - *flags |= env->sregs[CPENABLE] << XTENSA_TBFLAG_CPENABLE_SHIFT; - } - if (xtensa_option_enabled(env->config, XTENSA_OPTION_WINDOWED_REGISTER) && - (env->sregs[PS] & (PS_WOE | PS_EXCM)) == PS_WOE) { - uint32_t windowstart = xtensa_replicate_windowstart(env) >> - (env->sregs[WINDOW_BASE] + 1); - uint32_t w = ctz32(windowstart | 0x8); - - *flags |= (w << XTENSA_TBFLAG_WINDOW_SHIFT) | XTENSA_TBFLAG_CWOE; - *flags |= extract32(env->sregs[PS], PS_CALLINC_SHIFT, - PS_CALLINC_LEN) << XTENSA_TBFLAG_CALLINC_SHIFT; - } else { - *flags |= 3 << XTENSA_TBFLAG_WINDOW_SHIFT; - } - if (env->yield_needed) { - *flags |= XTENSA_TBFLAG_YIELD; - } -} - 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 3e0c9e8e8be..3b91f7c38ac 100644 --- a/target/xtensa/dbg_helper.c +++ b/target/xtensa/dbg_helper.c @@ -30,30 +30,24 @@ #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); - } -} +#include "exec/watchpoint.h" +#include "system/address-spaces.h" 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); @@ -62,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 91354884f7e..b611c9bf97c 100644 --- a/target/xtensa/exc_helper.c +++ b/target/xtensa/exc_helper.c @@ -32,7 +32,6 @@ #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) { @@ -105,9 +104,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); @@ -120,9 +119,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) @@ -171,7 +170,7 @@ static void handle_interrupt(CPUXtensaState *env) if (level > 1) { /* env->config->nlevel check should have ensured this */ - assert(level < sizeof(env->config->interrupt_vector)); + assert(level < ARRAY_SIZE(env->config->interrupt_vector)); env->sregs[EPC1 + level - 1] = env->pc; env->sregs[EPS2 + level - 2] = env->sregs[PS]; @@ -205,8 +204,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 381e83ded83..5358060c50a 100644 --- a/target/xtensa/fpu_helper.c +++ b/target/xtensa/fpu_helper.c @@ -30,7 +30,6 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" #include "fpu/softfloat.h" enum { @@ -57,6 +56,14 @@ static const struct { { XTENSA_FP_V, float_flag_invalid, }, }; +void xtensa_use_first_nan(CPUXtensaState *env, bool use_first) +{ + set_float_2nan_prop_rule(use_first ? float_2nan_prop_ab : float_2nan_prop_ba, + &env->fp_status); + set_float_3nan_prop_rule(use_first ? float_3nan_prop_abc : float_3nan_prop_cba, + &env->fp_status); +} + void HELPER(wur_fpu2k_fcr)(CPUXtensaState *env, uint32_t v) { static const int rounding_mode[] = { @@ -171,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 4b3bfb7e59c..4748fb65321 100644 --- a/target/xtensa/gdbstub.c +++ b/target/xtensa/gdbstub.c @@ -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 dbeb97a953c..2d93b45036d 100644 --- a/target/xtensa/helper.c +++ b/target/xtensa/helper.c @@ -28,7 +28,8 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "exec/target_page.h" #include "gdbstub/helpers.h" #include "exec/helper-proto.h" #include "qemu/error-report.h" @@ -169,13 +170,12 @@ static void xtensa_finalize_config(XtensaConfig *config) } } -static void xtensa_core_class_init(ObjectClass *oc, void *data) +static void xtensa_core_class_init(ObjectClass *oc, const void *data) { CPUClass *cc = CPU_CLASS(oc); XtensaCPUClass *xcc = XTENSA_CPU_CLASS(oc); - XtensaConfig *config = data; + const XtensaConfig *config = data; - xtensa_finalize_config(config); xcc->config = config; /* @@ -192,13 +192,15 @@ void xtensa_register_core(XtensaConfigList *node) TypeInfo type = { .parent = TYPE_XTENSA_CPU, .class_init = xtensa_core_class_init, - .class_data = (void *)node->config, + .class_data = node->config, }; + xtensa_finalize_config(node->config); + node->next = xtensa_cores; xtensa_cores = node; type.name = g_strdup_printf(XTENSA_CPU_TYPE_NAME("%s"), node->config->name); - type_register(&type); + type_register_static(&type); g_free((gpointer)type.name); } @@ -217,8 +219,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 +232,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 +267,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 +297,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/meson.build b/target/xtensa/meson.build index 95692bd75fd..f8d60101e3d 100644 --- a/target/xtensa/meson.build +++ b/target/xtensa/meson.build @@ -24,4 +24,4 @@ xtensa_system_ss.add(files( )) target_arch += {'xtensa': xtensa_ss} -target_softmmu_arch += {'xtensa': xtensa_system_ss} +target_system_arch += {'xtensa': xtensa_system_ss} diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c index 12552a33470..71330fc84b9 100644 --- a/target/xtensa/mmu_helper.c +++ b/target/xtensa/mmu_helper.c @@ -32,7 +32,12 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" +#include "exec/cputlb.h" +#include "accel/tcg/cpu-mmu-index.h" +#include "accel/tcg/probe.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" +#include "system/memory.h" #define XTENSA_MPU_SEGMENT_MASK 0x0000001f #define XTENSA_MPU_ACC_RIGHTS_MASK 0x00000f00 @@ -66,7 +71,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) @@ -224,22 +229,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; @@ -252,11 +266,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, @@ -482,7 +499,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; } @@ -491,7 +513,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) @@ -499,7 +526,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; } @@ -537,8 +564,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); + } } /*! @@ -967,7 +995,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, @@ -981,7 +1009,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 7bb8cd6726f..fc47ebaaf50 100644 --- a/target/xtensa/op_helper.c +++ b/target/xtensa/op_helper.c @@ -28,8 +28,9 @@ #include "qemu/osdep.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 "system/memory.h" #include "qemu/atomic.h" #include "qemu/timer.h" @@ -37,7 +38,7 @@ 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; @@ -58,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 b7386ff0f00..34ae2f4e162 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -31,19 +31,18 @@ #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/qemu-print.h" -#include "exec/cpu_ldst.h" -#include "semihosting/semihost.h" #include "exec/translator.h" - +#include "exec/translation-block.h" +#include "exec/target_page.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" - #include "exec/log.h" +#ifndef CONFIG_USER_ONLY +#include "semihosting/semihost.h" +#endif #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" @@ -154,49 +153,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]); @@ -205,7 +204,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]); @@ -214,7 +213,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]); @@ -222,15 +221,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"); } @@ -311,13 +310,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; @@ -327,7 +326,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; } @@ -523,7 +522,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; } @@ -536,7 +535,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; } @@ -576,11 +575,11 @@ static int gen_postprocess(DisasContext *dc, int slot) #ifndef CONFIG_USER_ONLY if (op_flags & XTENSA_OP_CHECK_INTERRUPTS) { translator_io_start(&dc->base); - gen_helper_check_interrupts(cpu_env); + 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; @@ -1042,13 +1041,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)) { @@ -1119,31 +1118,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; @@ -1180,7 +1165,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, @@ -1205,10 +1190,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) { @@ -1239,24 +1220,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 xtensa_translate_code(CPUState *cpu, TranslationBlock *tb, + int *max_insns, vaddr pc, void *host_pc) { DisasContext dc = {}; translator_loop(cpu, tb, max_insns, pc, host_pc, @@ -1265,8 +1238,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; @@ -1422,11 +1394,11 @@ static void translate_bbi(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { TCGv_i32 tmp = tcg_temp_new_i32(); -#if TARGET_BIG_ENDIAN - tcg_gen_andi_i32(tmp, arg[0].in, 0x80000000u >> arg[1].imm); -#else - tcg_gen_andi_i32(tmp, arg[0].in, 0x00000001u << arg[1].imm); -#endif + if (TARGET_BIG_ENDIAN) { + tcg_gen_andi_i32(tmp, arg[0].in, 0x80000000u >> arg[1].imm); + } else { + tcg_gen_andi_i32(tmp, arg[0].in, 0x00000001u << arg[1].imm); + } gen_brcondi(dc, par[0], tmp, 0, arg[2].imm); } @@ -1589,7 +1561,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[], @@ -1620,7 +1592,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); + gen_helper_itlb_hit_test(tcg_env, addr); #endif } @@ -1630,7 +1602,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 } @@ -1667,7 +1639,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)); } } @@ -1959,7 +1931,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 } @@ -1968,7 +1940,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 } @@ -2020,7 +1992,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[], @@ -2039,7 +2011,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; } } @@ -2053,7 +2025,7 @@ static void translate_retw(DisasContext *dc, const OpcodeArg arg[], 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); } @@ -2093,7 +2065,7 @@ static void translate_rfw(DisasContext *dc, const OpcodeArg arg[], cpu_SR[WINDOW_START], tmp); } - gen_helper_restore_owb(cpu_env); + gen_helper_restore_owb(tcg_env); gen_jump(dc, cpu_SR[EPC1]); } @@ -2126,7 +2098,7 @@ static void translate_rsr_ccount(DisasContext *dc, const OpcodeArg arg[], { #ifndef CONFIG_USER_ONLY translator_io_start(&dc->base); - gen_helper_update_ccount(cpu_env); + gen_helper_update_ccount(tcg_env); tcg_gen_mov_i32(arg[0].out, cpu_SR[par[0]]); #endif } @@ -2154,7 +2126,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 } @@ -2162,7 +2134,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 } @@ -2170,7 +2142,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 } @@ -2196,7 +2168,7 @@ 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 @@ -2262,34 +2234,22 @@ 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_gen_sextract_i32(arg[0].out, arg[1].in, 0, arg[2].imm + 1); } static uint32_t test_exceptions_simcall(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - bool is_semi = semihosting_enabled(dc->cring != 0); -#ifdef CONFIG_USER_ONLY - bool ill = true; -#else - /* Between RE.2 and RE.3 simcall opcode's become nop for the hardware. */ - bool ill = dc->config->hw_version <= 250002 && !is_semi; -#endif - if (ill || !is_semi) { - qemu_log_mask(LOG_GUEST_ERROR, "SIMCALL but semihosting is disabled\n"); +#ifndef CONFIG_USER_ONLY + if (semihosting_enabled(dc->cring != 0)) { + return 0; } - return ill ? XTENSA_OP_ILL : 0; +#endif + qemu_log_mask(LOG_GUEST_ERROR, "SIMCALL but semihosting is disabled\n"); + + /* Between RE.2 and RE.3 simcall opcode's become nop for the hardware. */ + return dc->config->hw_version <= 250002 ? XTENSA_OP_ILL : 0; } static void translate_simcall(DisasContext *dc, const OpcodeArg arg[], @@ -2297,7 +2257,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 } @@ -2442,7 +2402,7 @@ static void translate_waiti(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 pc = tcg_constant_i32(dc->base.pc_next); translator_io_start(&dc->base); - gen_helper_waiti(cpu_env, pc, tcg_constant_i32(arg[0].imm)); + gen_helper_waiti(tcg_env, pc, tcg_constant_i32(arg[0].imm)); #endif } @@ -2452,7 +2412,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 } @@ -2460,14 +2420,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[], @@ -2508,7 +2468,7 @@ static void translate_wsr_ccompare(DisasContext *dc, const OpcodeArg arg[], assert(id < dc->config->nccompare); 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 } @@ -2517,7 +2477,7 @@ static void translate_wsr_ccount(DisasContext *dc, const OpcodeArg arg[], { #ifndef CONFIG_USER_ONLY translator_io_start(&dc->base); - gen_helper_wsr_ccount(cpu_env, arg[0].in); + gen_helper_wsr_ccount(tcg_env, arg[0].in); #endif } @@ -2528,7 +2488,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 } @@ -2539,7 +2499,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 } @@ -2550,7 +2510,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 } @@ -2558,7 +2518,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 } @@ -2578,7 +2538,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 } @@ -2586,7 +2546,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 } @@ -2594,7 +2554,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 } @@ -2602,7 +2562,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 } @@ -2625,7 +2585,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 } @@ -2704,9 +2664,9 @@ static void translate_xsr_ccount(DisasContext *dc, const OpcodeArg arg[], TCGv_i32 tmp = tcg_temp_new_i32(); translator_io_start(&dc->base); - gen_helper_update_ccount(cpu_env); + 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); #endif @@ -6295,7 +6255,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); } @@ -6330,7 +6290,7 @@ 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); @@ -6359,7 +6319,7 @@ 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); @@ -6412,9 +6372,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); } } @@ -6426,9 +6386,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); } @@ -6440,10 +6400,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); } } @@ -6457,10 +6417,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); @@ -6505,7 +6465,7 @@ static void translate_ldstx(DisasContext *dc, const OpcodeArg arg[], 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); } @@ -6584,14 +6544,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); } @@ -6630,7 +6590,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); } @@ -6653,7 +6613,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[], @@ -6882,20 +6842,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); } } @@ -6906,7 +6866,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); + gen_helper_cvtd_s(arg[0].out, tcg_env, v); } static void translate_cvts_d(DisasContext *dc, const OpcodeArg arg[], @@ -6914,7 +6874,7 @@ 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); } @@ -7039,7 +6999,7 @@ static void translate_ldstx_s(DisasContext *dc, const OpcodeArg arg[], 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); } @@ -7047,13 +7007,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); } @@ -7062,20 +7022,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); } } @@ -7083,7 +7043,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); } @@ -7091,13 +7051,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); } @@ -7106,20 +7066,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); } } @@ -7127,7 +7087,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[], @@ -7136,14 +7096,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[], @@ -7152,26 +7112,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 ec9ff44db05..4b25f8f4de7 100644 --- a/target/xtensa/win_helper.c +++ b/target/xtensa/win_helper.c @@ -30,7 +30,6 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" static void copy_window_from_phys(CPUXtensaState *env, uint32_t window, uint32_t phys, uint32_t n) diff --git a/target/xtensa/xtensa-semi.c b/target/xtensa/xtensa-semi.c index fa21b7e11fc..636f421da2b 100644 --- a/target/xtensa/xtensa-semi.c +++ b/target/xtensa/xtensa-semi.c @@ -29,7 +29,9 @@ #include "cpu.h" #include "chardev/char-fe.h" #include "exec/helper-proto.h" +#include "exec/target_page.h" #include "semihosting/semihost.h" +#include "semihosting/uaccess.h" #include "qapi/error.h" #include "qemu/log.h" @@ -323,15 +325,12 @@ void HELPER(simcall)(CPUXtensaState *env) uint32_t fd = regs[3]; uint32_t rq = regs[4]; uint32_t target_tv = regs[5]; - uint32_t target_tvv[2]; struct timeval tv = {0}; if (target_tv) { - cpu_memory_rw_debug(cs, target_tv, - (uint8_t *)target_tvv, sizeof(target_tvv), 0); - tv.tv_sec = (int32_t)tswap32(target_tvv[0]); - tv.tv_usec = (int32_t)tswap32(target_tvv[1]); + get_user_u32(tv.tv_sec, target_tv); + get_user_u32(tv.tv_sec, target_tv + 4); } if (fd < 3 && sim_console) { if ((fd == 1 || fd == 2) && rq == SELECT_ONE_WRITE) { @@ -387,11 +386,8 @@ void HELPER(simcall)(CPUXtensaState *env) const char *str = semihosting_get_arg(i); int str_size = strlen(str) + 1; - argptr = tswap32(regs[3] + str_offset); - - cpu_memory_rw_debug(cs, - regs[3] + i * sizeof(uint32_t), - (uint8_t *)&argptr, sizeof(argptr), 1); + put_user_u32(regs[3] + str_offset, + regs[3] + i * sizeof(uint32_t)); cpu_memory_rw_debug(cs, regs[3] + str_offset, (uint8_t *)str, str_size, 1); diff --git a/tcg/aarch64/tcg-target-con-set.h b/tcg/aarch64/tcg-target-con-set.h index 3fdee26a3d6..d0622e65fb0 100644 --- a/tcg/aarch64/tcg-target-con-set.h +++ b/tcg/aarch64/tcg-target-con-set.h @@ -10,20 +10,23 @@ * tcg-target-con-str.h; the constraint combination is inclusive or. */ C_O0_I1(r) -C_O0_I2(r, rA) -C_O0_I2(rZ, r) +C_O0_I2(r, rC) +C_O0_I2(rz, r) C_O0_I2(w, r) -C_O0_I3(rZ, rZ, r) +C_O0_I3(rz, rz, r) 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, 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, rA) +C_O1_I2(r, rz, rMZ) +C_O1_I2(r, rz, rz) C_O1_I2(r, rZ, rZ) C_O1_I2(w, 0, w) C_O1_I2(w, w, w) @@ -31,6 +34,5 @@ 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 fb1a845b4f0..48e1722c683 100644 --- a/tcg/aarch64/tcg-target-con-str.h +++ b/tcg/aarch64/tcg-target-con-str.h @@ -16,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-has.h b/tcg/aarch64/tcg-target-has.h new file mode 100644 index 00000000000..69e83efb69d --- /dev/null +++ b/tcg/aarch64/tcg-target-has.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Define target-specific opcode support + * Copyright (c) 2013 Huawei Technologies Duesseldorf GmbH + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +#include "host/cpuinfo.h" + +#define have_lse (cpuinfo & CPUINFO_LSE) +#define have_lse2 (cpuinfo & CPUINFO_LSE2) + +/* optional instructions */ +#define TCG_TARGET_HAS_extr_i64_i32 0 + +/* + * 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 +#define TCG_TARGET_HAS_v256 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 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 1 +#define TCG_TARGET_HAS_roti_vec 0 +#define TCG_TARGET_HAS_rots_vec 0 +#define TCG_TARGET_HAS_rotv_vec 0 +#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_mul_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 1 + +#define TCG_TARGET_extract_valid(type, ofs, len) 1 +#define TCG_TARGET_sextract_valid(type, ofs, len) 1 +#define TCG_TARGET_deposit_valid(type, ofs, len) 1 + +#endif diff --git a/tcg/aarch64/tcg-target-mo.h b/tcg/aarch64/tcg-target-mo.h new file mode 100644 index 00000000000..e8e89230142 --- /dev/null +++ b/tcg/aarch64/tcg-target-mo.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Define target-specific memory model + * Copyright (c) 2013 Huawei Technologies Duesseldorf GmbH + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/aarch64/tcg-target-opc.h.inc b/tcg/aarch64/tcg-target-opc.h.inc new file mode 100644 index 00000000000..5382315c414 --- /dev/null +++ b/tcg/aarch64/tcg-target-opc.h.inc @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2019 Linaro + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + * + * See the COPYING file in the top-level directory 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. + */ + +DEF(aa64_sshl_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(aa64_sli_vec, 1, 2, 1, TCG_OPF_VECTOR) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 0931a69448c..3b088b7bd97 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -10,10 +10,21 @@ * See the COPYING file in the top-level directory for details. */ -#include "../tcg-ldst.c.inc" -#include "../tcg-pool.c.inc" #include "qemu/bitops.h" +/* Used for function call generation. */ +#define TCG_REG_CALL_STACK TCG_REG_SP +#define TCG_TARGET_STACK_ALIGN 16 +#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 + /* We're going to re-use TCGType in setting of the SF bit, which controls the size of the operation performed. If we know the values match, it makes things much cleaner. */ @@ -77,9 +88,7 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #define TCG_REG_TMP2 TCG_REG_X30 #define TCG_VEC_TMP0 TCG_REG_V31 -#ifndef CONFIG_SOFTMMU #define TCG_REG_GUEST_BASE TCG_REG_X28 -#endif static bool reloc_pc26(tcg_insn_unit *src_rw, const tcg_insn_unit *target) { @@ -107,6 +116,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) { @@ -117,6 +138,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(); } @@ -128,6 +151,7 @@ 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 @@ -272,7 +296,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; @@ -280,6 +305,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; } @@ -346,6 +380,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 { @@ -368,6 +405,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, @@ -467,7 +508,9 @@ typedef enum { /* Add/subtract with carry instructions. */ I3503_ADC = 0x1a000000, + I3503_ADCS = 0x3a000000, I3503_SBC = 0x5a000000, + I3503_SBCS = 0x7a000000, /* Conditional select instructions. */ I3506_CSEL = 0x1a800000, @@ -602,6 +645,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) @@ -644,6 +691,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)); @@ -843,6 +898,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) { @@ -1283,64 +1349,37 @@ static inline void tcg_out_extr(TCGContext *s, TCGType ext, TCGReg rd, tcg_out_insn(s, 3403, EXTR, ext, rd, rn, rm, a); } -static inline void tcg_out_shl(TCGContext *s, TCGType ext, - TCGReg rd, TCGReg rn, unsigned int m) -{ - int bits = ext ? 64 : 32; - int max = bits - 1; - tcg_out_ubfm(s, ext, rd, rn, (bits - m) & max, (max - m) & max); -} - -static inline void tcg_out_shr(TCGContext *s, TCGType ext, - TCGReg rd, TCGReg rn, unsigned int m) +static void tgen_cmp(TCGContext *s, TCGType ext, TCGCond cond, + TCGReg a, TCGReg b) { - int max = ext ? 63 : 31; - tcg_out_ubfm(s, ext, rd, rn, m & max, max); -} - -static inline void tcg_out_sar(TCGContext *s, TCGType ext, - TCGReg rd, TCGReg rn, unsigned int m) -{ - int max = ext ? 63 : 31; - tcg_out_sbfm(s, ext, rd, rn, m & max, max); -} - -static inline void tcg_out_rotr(TCGContext *s, TCGType ext, - TCGReg rd, TCGReg rn, unsigned int m) -{ - int max = ext ? 63 : 31; - tcg_out_extr(s, ext, rd, rn, rn, m & max); -} - -static inline void tcg_out_rotl(TCGContext *s, TCGType ext, - TCGReg rd, TCGReg rn, unsigned int m) -{ - int max = ext ? 63 : 31; - tcg_out_extr(s, ext, rd, rn, rn, -m & max); + if (is_tst_cond(cond)) { + tcg_out_insn(s, 3510, ANDS, ext, TCG_REG_XZR, a, b); + } else { + tcg_out_insn(s, 3502, SUBS, ext, TCG_REG_XZR, a, b); + } } -static inline void tcg_out_dep(TCGContext *s, TCGType ext, TCGReg rd, - TCGReg rn, unsigned lsb, unsigned width) +static void tgen_cmpi(TCGContext *s, TCGType ext, TCGCond cond, + TCGReg a, tcg_target_long b) { - unsigned size = ext ? 64 : 32; - unsigned a = (size - lsb) & (size - 1); - unsigned b = width - 1; - tcg_out_bfm(s, ext, rd, rn, a, b); + if (is_tst_cond(cond)) { + tcg_out_logicali(s, I3404_ANDSI, 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); + } } -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); - } else { - tcg_out_insn(s, 3401, ADDSI, ext, TCG_REG_XZR, a, -b); - } + tgen_cmpi(s, ext, cond, a, b); } else { - /* Using CMP alias SUBS wzr, Wn, Wm */ - tcg_out_insn(s, 3502, SUBS, ext, TCG_REG_XZR, a, b); + tgen_cmp(s, ext, cond, a, b); } } @@ -1351,18 +1390,6 @@ 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 void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *target) { ptrdiff_t offset = tcg_pcrel_diff(s, target) >> 2; @@ -1380,7 +1407,7 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target, tcg_out_call_int(s, target); } -static inline void tcg_out_goto_label(TCGContext *s, TCGLabel *l) +static void tcg_out_br(TCGContext *s, TCGLabel *l) { if (!l->has_value) { tcg_out_reloc(s, s->code_ptr, R_AARCH64_JUMP26, l, 0); @@ -1390,36 +1417,96 @@ 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) +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c, + TCGReg a, TCGReg b, TCGLabel *l) { - intptr_t offset; - bool need_cmp; + tgen_cmp(s, type, c, a, b); + tcg_out_reloc(s, s->code_ptr, R_AARCH64_CONDBR19, l, 0); + tcg_out_insn(s, 3202, B_C, c, 0); +} - 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); +static void tgen_brcondi(TCGContext *s, TCGType ext, TCGCond c, + TCGReg a, tcg_target_long b, TCGLabel *l) +{ + int tbit = -1; + bool need_cmp = true; + + switch (c) { + case TCG_COND_EQ: + case TCG_COND_NE: + /* cmp xN,0; b.ne L -> cbnz xN,L */ + if (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 == 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 == UINT32_MAX) { + c = tcg_tst_eqne_cond(c); + ext = TCG_TYPE_I32; + need_cmp = false; + break; + } + /* tst xN,1< tbnz xN,B,L */ + if (is_power_of_2(b)) { + tbit = ctz64(b); + need_cmp = false; + } + break; + default: + break; } - if (!l->has_value) { + if (need_cmp) { + tgen_cmpi(s, ext, c, a, b); 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)); + tcg_out_insn(s, 3202, B_C, c, 0); + return; } - 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); + 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(); + } } } +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rC), + .out_rr = tgen_brcond, + .out_ri = tgen_brcondi, +}; + static inline void tcg_out_rev(TCGContext *s, int ext, MemOp s_bits, TCGReg rd, TCGReg rn) { @@ -1488,67 +1575,7 @@ 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) -{ - if (aimm >= 0) { - tcg_out_insn(s, 3401, ADDI, ext, rd, rn, aimm); - } else { - tcg_out_insn(s, 3401, SUBI, ext, rd, rn, -aimm); - } -} - -static void tcg_out_addsub2(TCGContext *s, TCGType ext, TCGReg rl, - TCGReg rh, TCGReg al, TCGReg ah, - tcg_target_long bl, tcg_target_long bh, - bool const_bl, bool const_bh, bool sub) -{ - TCGReg orig_rl = rl; - AArch64Insn insn; - - if (rl == ah || (!const_bh && rl == bh)) { - rl = TCG_REG_TMP0; - } - - if (const_bl) { - if (bl < 0) { - bl = -bl; - insn = sub ? I3401_ADDSI : I3401_SUBSI; - } else { - insn = sub ? I3401_SUBSI : I3401_ADDSI; - } - - if (unlikely(al == TCG_REG_XZR)) { - /* ??? We want to allow al to be zero for the benefit of - negation via subtraction. However, that leaves open the - 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_TMP0; - tcg_out_movi(s, ext, al, 0); - } - tcg_out_insn_3401(s, insn, ext, rl, al, bl); - } else { - tcg_out_insn_3502(s, sub ? I3502_SUBS : I3502_ADDS, ext, rl, al, bl); - } - - insn = I3503_ADC; - if (const_bh) { - /* Note that the only two constants we support are 0 and -1, and - that SBC = rn + ~rm + c, so adc -1 is sbc 0, and vice-versa. */ - if ((bh != 0) ^ sub) { - insn = I3503_SBC; - } - bh = TCG_REG_XZR; - } else if (sub) { - insn = I3503_SBC; - } - tcg_out_insn_3503(s, insn, ext, rh, ah, bh); - - tcg_out_mov(s, ext, orig_rl, rl); -} - -static inline void tcg_out_mb(TCGContext *s, TCGArg a0) +static void tcg_out_mb(TCGContext *s, unsigned a0) { static const uint32_t sync[] = { [0 ... TCG_MO_ALL] = DMB_ISH | DMB_LD | DMB_ST, @@ -1560,37 +1587,6 @@ static inline void tcg_out_mb(TCGContext *s, TCGArg a0) tcg_out32(s, sync[a0 & TCG_MO_ALL]); } -static void tcg_out_cltz(TCGContext *s, TCGType ext, TCGReg d, - TCGReg a0, TCGArg b, bool const_b, bool is_ctz) -{ - TCGReg a1 = a0; - if (is_ctz) { - a1 = TCG_REG_TMP0; - tcg_out_insn(s, 3507, RBIT, ext, a1, a0); - } - if (const_b && b == (ext ? 64 : 32)) { - tcg_out_insn(s, 3507, CLZ, ext, d, a1); - } else { - AArch64Insn sel = I3506_CSEL; - - tcg_out_cmp(s, ext, a0, 0, 1); - tcg_out_insn(s, 3507, CLZ, ext, TCG_REG_TMP0, a1); - - if (const_b) { - if (b == -1) { - b = TCG_REG_XZR; - sel = I3506_CSINV; - } else if (b == 0) { - b = TCG_REG_XZR; - } else { - tcg_out_movi(s, ext, d, b); - b = d; - } - } - tcg_out_insn_3506(s, sel, ext, d, TCG_REG_TMP0, b, TCG_COND_NE); - } -} - typedef struct { TCGReg base; TCGReg index; @@ -1640,8 +1636,8 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) #define MIN_TLB_MASK_TABLE_OFS -512 /* - * For softmmu, perform the TLB load and compare. - * For useronly, perform any required alignment tests. + * 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. */ @@ -1661,97 +1657,94 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, s_bits == MO_128); a_mask = (1 << h->aa.align) - 1; -#ifdef CONFIG_SOFTMMU - unsigned s_mask = (1u << s_bits) - 1; - unsigned mem_index = get_mmuidx(oi); - TCGReg addr_adj; - TCGType mask_type; - uint64_t compare_mask; - - ldst = new_ldst_label(s); - ldst->is_ld = is_ld; - ldst->oi = oi; - ldst->addrlo_reg = addr_reg; - - mask_type = (s->page_bits + s->tlb_dyn_max_bits > 32 - ? TCG_TYPE_I64 : TCG_TYPE_I32); - - /* Load env_tlb(env)->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); - - /* 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 in TMP1. */ - 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 into TMP1. */ - 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_REG_TMP0, TCG_REG_TMP2, 0); + if (tcg_use_softmmu) { + unsigned s_mask = (1u << s_bits) - 1; + unsigned mem_index = get_mmuidx(oi); + TCGReg addr_adj; + uint64_t compare_mask; - /* 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 - if (a_mask) { ldst = new_ldst_label(s); - ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr_reg; + ldst->addr_reg = addr_reg; + + /* 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); + + /* Extract the TLB index from the address into X0. */ + tcg_out_insn(s, 3502S, AND_LSR, TCG_TYPE_I64, + TCG_REG_TMP0, TCG_REG_TMP0, addr_reg, + TARGET_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)TARGET_PAGE_MASK | a_mask; - /* tst addr, #mask */ - tcg_out_logicali(s, I3404_ANDSI, 0, TCG_REG_XZR, addr_reg, 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); - /* b.ne slow_path */ + /* 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); - } - if (guest_base || addr_type == TCG_TYPE_I32) { - h->base = TCG_REG_GUEST_BASE; + h->base = TCG_REG_TMP1; 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; + if (a_mask) { + ldst = new_ldst_label(s); + + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addr_reg = addr_reg; + + /* tst addr, #mask */ + tcg_out_logicali(s, I3404_ANDSI, 0, TCG_REG_XZR, addr_reg, a_mask); + + /* b.ne slow_path */ + ldst->label_ptr[0] = s->code_ptr; + tcg_out_insn(s, 3202, B_C, TCG_COND_NE, 0); + } + + 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; + } } -#endif return ldst; } @@ -1809,8 +1802,8 @@ static void tcg_out_qemu_st_direct(TCGContext *s, MemOp memop, } } -static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType data_type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; @@ -1825,8 +1818,13 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, } } -static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + +static void tgen_qemu_st(TCGContext *s, TCGType data_type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; @@ -1841,6 +1839,11 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, } } +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out = tgen_qemu_st, +}; + static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, TCGReg addr_reg, MemOpIdx oi, bool is_ld) { @@ -1943,16 +1946,54 @@ static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, } } +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr_reg, MemOpIdx oi) +{ + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr_reg, oi, true); +} + +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = C_O2_I1(r, r, r), + .out = tgen_qemu_ld2, +}; + +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr_reg, MemOpIdx oi) +{ + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr_reg, oi, false); +} + +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = C_O0_I3(rz, rz, r), + .out = tgen_qemu_st2, +}; + 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) { - tcg_out_goto_long(s, tcg_code_gen_epilogue); + target = tcg_code_gen_epilogue; } else { tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_X0, a0); - tcg_out_goto_long(s, tb_ret_addr); + 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); } } @@ -1970,6 +2011,12 @@ static void tcg_out_goto_tb(TCGContext *s, int 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); +} + +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_insn(s, 3207, BR, a0); } void tb_target_set_jmp_target(const TranslationBlock *tb, int n, @@ -1993,413 +2040,859 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, 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]) + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) { - /* 99% of the time, we can signal the use of extension registers - by looking to see if the opcode handles 64-bit data. */ - TCGType ext = (tcg_op_defs[opc].flags & TCG_OPF_64BIT) != 0; + tcg_out_insn(s, 3502, ADD, type, a0, a1, a2); +} - /* Hoist the loads of the most common arguments. */ - TCGArg a0 = args[0]; - TCGArg a1 = args[1]; - TCGArg a2 = args[2]; - int c2 = const_args[2]; +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a2 >= 0) { + tcg_out_insn(s, 3401, ADDI, type, a0, a1, a2); + } else { + tcg_out_insn(s, 3401, SUBI, type, a0, a1, -a2); + } +} - /* Some operands are defined with "rZ" constraint, a register or - the zero register. These need not actually test args[I] == 0. */ -#define REG0(I) (const_args[I] ? TCG_REG_XZR : (TCGReg)args[I]) +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, rA), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; - switch (opc) { - case INDEX_op_goto_ptr: - tcg_out_insn(s, 3207, BR, a0); - break; +static void tgen_addco(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3502, ADDS, type, a0, a1, a2); +} - case INDEX_op_br: - tcg_out_goto_label(s, arg_label(a0)); - break; +static void tgen_addco_imm(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a2 >= 0) { + tcg_out_insn(s, 3401, ADDSI, type, a0, a1, a2); + } else { + tcg_out_insn(s, 3401, SUBSI, type, a0, a1, -a2); + } +} - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - tcg_out_ldst(s, I3312_LDRB, a0, a1, a2, 0); - break; - case INDEX_op_ld8s_i32: - tcg_out_ldst(s, I3312_LDRSBW, a0, a1, a2, 0); - break; - case INDEX_op_ld8s_i64: - tcg_out_ldst(s, I3312_LDRSBX, a0, a1, a2, 0); - break; - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - tcg_out_ldst(s, I3312_LDRH, a0, a1, a2, 1); - break; - case INDEX_op_ld16s_i32: - tcg_out_ldst(s, I3312_LDRSHW, a0, a1, a2, 1); - break; - case INDEX_op_ld16s_i64: - tcg_out_ldst(s, I3312_LDRSHX, a0, a1, a2, 1); - break; - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - tcg_out_ldst(s, I3312_LDRW, a0, a1, a2, 2); - break; - case INDEX_op_ld32s_i64: - tcg_out_ldst(s, I3312_LDRSWX, a0, a1, a2, 2); - break; - case INDEX_op_ld_i64: - tcg_out_ldst(s, I3312_LDRX, a0, a1, a2, 3); - break; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_O1_I2(r, r, rA), + .out_rrr = tgen_addco, + .out_rri = tgen_addco_imm, +}; - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - tcg_out_ldst(s, I3312_STRB, REG0(0), a1, a2, 0); - break; - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - tcg_out_ldst(s, I3312_STRH, REG0(0), a1, a2, 1); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - tcg_out_ldst(s, I3312_STRW, REG0(0), a1, a2, 2); - break; - case INDEX_op_st_i64: - tcg_out_ldst(s, I3312_STRX, REG0(0), a1, a2, 3); - break; +static void tgen_addci_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3503, ADC, type, a0, a1, a2); +} - case INDEX_op_add_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_add_i64: - if (c2) { - tcg_out_addsubi(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3502, ADD, ext, a0, a1, a2); - } - break; +static void tgen_addci_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + /* + * Note that the only two constants we support are 0 and -1, and + * that SBC = rn + ~rm + c, so adc -1 is sbc 0, and vice-versa. + */ + if (a2) { + tcg_out_insn(s, 3503, SBC, type, a0, a1, TCG_REG_XZR); + } else { + tcg_out_insn(s, 3503, ADC, type, a0, a1, TCG_REG_XZR); + } +} - case INDEX_op_sub_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_sub_i64: - if (c2) { - tcg_out_addsubi(s, ext, a0, a1, -a2); - } else { - tcg_out_insn(s, 3502, SUB, ext, a0, a1, a2); - } - break; +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_O1_I2(r, rz, rMZ), + .out_rrr = tgen_addci_rrr, + .out_rri = tgen_addci_rri, +}; - case INDEX_op_neg_i64: - case INDEX_op_neg_i32: - tcg_out_insn(s, 3502, SUB, ext, a0, TCG_REG_XZR, a1); - break; +static void tgen_addcio(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3503, ADCS, type, a0, a1, a2); +} - case INDEX_op_and_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_and_i64: - if (c2) { - tcg_out_logicali(s, I3404_ANDI, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3510, AND, ext, a0, a1, a2); - } - break; +static void tgen_addcio_imm(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + /* Use SBCS w/0 for ADCS w/-1 -- see above. */ + if (a2) { + tcg_out_insn(s, 3503, SBCS, type, a0, a1, TCG_REG_XZR); + } else { + tcg_out_insn(s, 3503, ADCS, type, a0, a1, TCG_REG_XZR); + } +} - case INDEX_op_andc_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_andc_i64: - if (c2) { - tcg_out_logicali(s, I3404_ANDI, ext, a0, a1, ~a2); - } else { - tcg_out_insn(s, 3510, BIC, ext, a0, a1, a2); - } - break; +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_O1_I2(r, rz, rMZ), + .out_rrr = tgen_addcio, + .out_rri = tgen_addcio_imm, +}; - case INDEX_op_or_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_or_i64: - if (c2) { - tcg_out_logicali(s, I3404_ORRI, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3510, ORR, ext, a0, a1, a2); - } - break; +static void tcg_out_set_carry(TCGContext *s) +{ + tcg_out_insn(s, 3502, SUBS, TCG_TYPE_I32, + TCG_REG_XZR, TCG_REG_XZR, TCG_REG_XZR); +} - case INDEX_op_orc_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_orc_i64: - if (c2) { - tcg_out_logicali(s, I3404_ORRI, ext, a0, a1, ~a2); - } else { - tcg_out_insn(s, 3510, ORN, ext, a0, a1, a2); - } - break; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3510, AND, type, a0, a1, a2); +} - case INDEX_op_xor_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_xor_i64: - if (c2) { - tcg_out_logicali(s, I3404_EORI, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3510, EOR, ext, a0, a1, a2); - } - break; +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_logicali(s, I3404_ANDI, type, a0, a1, a2); +} - case INDEX_op_eqv_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_eqv_i64: - if (c2) { - tcg_out_logicali(s, I3404_EORI, ext, a0, a1, ~a2); - } else { - tcg_out_insn(s, 3510, EON, ext, a0, a1, a2); - } - break; +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, rL), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; - case INDEX_op_not_i64: - case INDEX_op_not_i32: - tcg_out_insn(s, 3510, ORN, ext, a0, TCG_REG_XZR, a1); - break; +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3510, BIC, type, a0, a1, a2); +} - case INDEX_op_mul_i64: - case INDEX_op_mul_i32: - tcg_out_insn(s, 3509, MADD, ext, a0, a1, a2, TCG_REG_XZR); - break; +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_andc, +}; - case INDEX_op_div_i64: - case INDEX_op_div_i32: - tcg_out_insn(s, 3508, SDIV, ext, a0, a1, a2); - break; - case INDEX_op_divu_i64: - case INDEX_op_divu_i32: - tcg_out_insn(s, 3508, UDIV, ext, a0, a1, a2); - break; +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_cmp(s, type, TCG_COND_NE, a1, 0, true); + tcg_out_insn(s, 3507, CLZ, type, TCG_REG_TMP0, a1); + tcg_out_insn(s, 3506, CSEL, type, a0, TCG_REG_TMP0, a2, TCG_COND_NE); +} - case INDEX_op_rem_i64: - case INDEX_op_rem_i32: - 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); +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a2 == (type == TCG_TYPE_I32 ? 32 : 64)) { + tcg_out_insn(s, 3507, CLZ, type, a0, a1); + return; + } + + tcg_out_cmp(s, type, TCG_COND_NE, a1, 0, true); + tcg_out_insn(s, 3507, CLZ, type, a0, a1); + + switch (a2) { + case -1: + tcg_out_insn(s, 3506, CSINV, type, a0, a0, TCG_REG_XZR, TCG_COND_NE); break; - case INDEX_op_remu_i64: - case INDEX_op_remu_i32: - 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); + case 0: + tcg_out_insn(s, 3506, CSEL, type, a0, a0, TCG_REG_XZR, TCG_COND_NE); break; - - case INDEX_op_shl_i64: - case INDEX_op_shl_i32: - if (c2) { - tcg_out_shl(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3508, LSLV, ext, a0, a1, a2); - } + default: + tcg_out_movi(s, type, TCG_REG_TMP0, a2); + tcg_out_insn(s, 3506, CSEL, type, a0, a0, TCG_REG_TMP0, TCG_COND_NE); break; + } +} - case INDEX_op_shr_i64: - case INDEX_op_shr_i32: - if (c2) { - tcg_out_shr(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3508, LSRV, ext, a0, a1, a2); - } - break; +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_O1_I2(r, r, rAL), + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; - case INDEX_op_sar_i64: - case INDEX_op_sar_i32: - if (c2) { - tcg_out_sar(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3508, ASRV, ext, a0, a1, a2); - } - break; +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_rotr_i64: - case INDEX_op_rotr_i32: - if (c2) { - tcg_out_rotr(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3508, RORV, ext, a0, a1, a2); - } - break; +static void tgen_ctz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3507, RBIT, type, TCG_REG_TMP0, a1); + tgen_clz(s, type, a0, TCG_REG_TMP0, a2); +} - case INDEX_op_rotl_i64: - case INDEX_op_rotl_i32: - if (c2) { - tcg_out_rotl(s, ext, a0, a1, a2); - } else { - 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; +static void tgen_ctzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_insn(s, 3507, RBIT, type, TCG_REG_TMP0, a1); + tgen_clzi(s, type, a0, TCG_REG_TMP0, a2); +} - case INDEX_op_clz_i64: - case INDEX_op_clz_i32: - tcg_out_cltz(s, ext, a0, a1, a2, c2, false); - break; - case INDEX_op_ctz_i64: - case INDEX_op_ctz_i32: - tcg_out_cltz(s, ext, a0, a1, a2, c2, true); - break; +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_O1_I2(r, r, rAL), + .out_rrr = tgen_ctz, + .out_rri = tgen_ctzi, +}; - case INDEX_op_brcond_i32: - a1 = (int32_t)a1; - /* FALLTHRU */ - case INDEX_op_brcond_i64: - tcg_out_brcond(s, ext, a2, a0, a1, const_args[1], arg_label(args[3])); - break; +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, SDIV, type, a0, a1, a2); +} - case INDEX_op_setcond_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_setcond_i64: - tcg_out_cmp(s, ext, 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; +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divs, +}; - case INDEX_op_negsetcond_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_negsetcond_i64: - tcg_out_cmp(s, ext, 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; +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; - 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_insn(s, 3506, CSEL, ext, a0, REG0(3), REG0(4), args[5]); - break; +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, UDIV, type, a0, a1, a2); +} - 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_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; +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divu, +}; - case INDEX_op_bswap64_i64: - tcg_out_rev(s, TCG_TYPE_I64, MO_64, a0, a1); - break; - case INDEX_op_bswap32_i64: - tcg_out_rev(s, TCG_TYPE_I32, MO_32, a0, a1); - if (a2 & TCG_BSWAP_OS) { - tcg_out_ext32s(s, a0, a0); - } - break; - case INDEX_op_bswap32_i32: - tcg_out_rev(s, TCG_TYPE_I32, MO_32, a0, a1); - break; - case INDEX_op_bswap16_i64: - case INDEX_op_bswap16_i32: - tcg_out_rev(s, TCG_TYPE_I32, MO_16, a0, a1); - if (a2 & TCG_BSWAP_OS) { - /* Output must be sign-extended. */ - 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_ext16u(s, a0, a0); - } - break; +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_deposit_i64: - case INDEX_op_deposit_i32: - tcg_out_dep(s, ext, a0, REG0(2), args[3], args[4]); - break; +static void tgen_eqv(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3510, EON, type, a0, a1, a2); +} - case INDEX_op_extract_i64: - case INDEX_op_extract_i32: - tcg_out_ubfm(s, ext, a0, a1, a2, a2 + args[3] - 1); - break; +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_eqv, +}; - case INDEX_op_sextract_i64: - case INDEX_op_sextract_i32: - tcg_out_sbfm(s, ext, a0, a1, a2, a2 + args[3] - 1); - break; +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_ubfm(s, TCG_TYPE_I64, a0, a1, 32, 63); +} - case INDEX_op_extract2_i64: - case INDEX_op_extract2_i32: - tcg_out_extr(s, ext, a0, REG0(2), REG0(1), args[3]); - break; +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; - case INDEX_op_add2_i32: - tcg_out_addsub2(s, TCG_TYPE_I32, a0, a1, REG0(2), REG0(3), - (int32_t)args[4], args[5], const_args[4], - const_args[5], false); - break; - case INDEX_op_add2_i64: - tcg_out_addsub2(s, TCG_TYPE_I64, a0, a1, REG0(2), REG0(3), args[4], - args[5], const_args[4], const_args[5], false); - break; - case INDEX_op_sub2_i32: - tcg_out_addsub2(s, TCG_TYPE_I32, a0, a1, REG0(2), REG0(3), - (int32_t)args[4], args[5], const_args[4], - const_args[5], true); - break; - case INDEX_op_sub2_i64: - tcg_out_addsub2(s, TCG_TYPE_I64, a0, a1, REG0(2), REG0(3), args[4], - args[5], const_args[4], const_args[5], true); - break; +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3509, MADD, type, a0, a1, a2, TCG_REG_XZR); +} - case INDEX_op_muluh_i64: - tcg_out_insn(s, 3508, UMULH, TCG_TYPE_I64, a0, a1, a2); - break; - case INDEX_op_mulsh_i64: - tcg_out_insn(s, 3508, SMULH, TCG_TYPE_I64, a0, a1, a2); - break; +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mul, +}; - case INDEX_op_mb: - tcg_out_mb(s, a0); - break; +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_NotImplemented, +}; - 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(); +static TCGConstraintSetIndex cset_mulh(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_I64 ? C_O1_I2(r, r, r) : C_NotImplemented; +} + +static void tgen_mulsh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, SMULH, TCG_TYPE_I64, a0, a1, a2); +} + +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mulh, + .out_rrr = tgen_mulsh, +}; + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_muluh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, UMULH, TCG_TYPE_I64, a0, a1, a2); +} + +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mulh, + .out_rrr = tgen_muluh, +}; + +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3510, ORR, type, a0, a1, a2); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_logicali(s, I3404_ORRI, type, a0, a1, a2); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rL), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + +static void tgen_orc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3510, ORN, type, a0, a1, a2); +} + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_orc, +}; + +static void tgen_rems(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, SDIV, type, TCG_REG_TMP0, a1, a2); + tcg_out_insn(s, 3509, MSUB, type, a0, TCG_REG_TMP0, a2, a1); +} + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_rems, +}; + +static void tgen_remu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, UDIV, type, TCG_REG_TMP0, a1, a2); + tcg_out_insn(s, 3509, MSUB, type, a0, TCG_REG_TMP0, a2, a1); +} + +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_remu, +}; + +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_rotr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, RORV, type, a0, a1, a2); +} + +static void tgen_rotri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int max = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_extr(s, type, a0, a1, a1, a2 & max); +} + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_rotr, + .out_rri = tgen_rotri, +}; + +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, ASRV, type, a0, a1, a2); +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int max = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_sbfm(s, type, a0, a1, a2 & max, max); +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, LSLV, type, a0, a1, a2); +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int max = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_ubfm(s, type, a0, a1, -a2 & max, ~a2 & max); +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, LSRV, type, a0, a1, a2); +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int max = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_ubfm(s, type, a0, a1, a2 & max, max); +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3502, SUB, type, a0, a1, a2); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sub, +}; + +static void tgen_subbo_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3502, SUBS, type, a0, a1, a2); +} + +static void tgen_subbo_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a2 >= 0) { + tcg_out_insn(s, 3401, SUBSI, type, a0, a1, a2); + } else { + tcg_out_insn(s, 3401, ADDSI, type, a0, a1, -a2); + } +} + +static void tgen_subbo_rir(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tgen_subbo_rrr(s, type, a0, TCG_REG_XZR, a2); +} + +static void tgen_subbo_rii(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, tcg_target_long a2) +{ + if (a2 == 0) { + tgen_subbo_rrr(s, type, a0, TCG_REG_XZR, TCG_REG_XZR); + return; } -#undef REG0 + /* + * We want to allow a1 to be zero for the benefit of negation via + * subtraction. However, that leaves open the possibility of + * adding 0 +/- const, and the immediate add/sub instructions + * encode XSP not XZR. Since we have 0 - non-zero, borrow is + * always set. + */ + tcg_out_movi(s, type, a0, -a2); + tcg_out_set_borrow(s); +} + +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_O1_I2(r, rZ, rA), + .out_rrr = tgen_subbo_rrr, + .out_rri = tgen_subbo_rri, + .out_rir = tgen_subbo_rir, + .out_rii = tgen_subbo_rii, +}; + +static void tgen_subbi_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3503, SBC, type, a0, a1, a2); +} + +static void tgen_subbi_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_addci_rri(s, type, a0, a1, ~a2); +} + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_O1_I2(r, rz, rMZ), + .out_rrr = tgen_subbi_rrr, + .out_rri = tgen_subbi_rri, +}; + +static void tgen_subbio_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3503, SBCS, type, a0, a1, a2); +} + +static void tgen_subbio_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_addcio_imm(s, type, a0, a1, ~a2); +} + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_O1_I2(r, rz, rMZ), + .out_rrr = tgen_subbio_rrr, + .out_rri = tgen_subbio_rri, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + tcg_out_insn(s, 3502, ADDS, TCG_TYPE_I32, + TCG_REG_XZR, TCG_REG_XZR, TCG_REG_XZR); +} + +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3510, EOR, type, a0, a1, a2); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_logicali(s, I3404_EORI, type, a0, a1, a2); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rL), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_rev(s, TCG_TYPE_I32, MO_16, a0, a1); + if (flags & TCG_BSWAP_OS) { + /* Output must be sign-extended. */ + tcg_out_ext16s(s, type, a0, a0); + } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + /* Output must be zero-extended, but input isn't. */ + tcg_out_ext16u(s, a0, a0); + } +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap16, +}; + +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_rev(s, TCG_TYPE_I32, MO_32, a0, a1); + if (flags & TCG_BSWAP_OS) { + tcg_out_ext32s(s, a0, a0); + } +} + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap32, +}; + +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_rev(s, TCG_TYPE_I64, MO_64, a0, a1); } +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap64, +}; + +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_sub(s, type, a0, TCG_REG_XZR, a1); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_orc(s, type, a0, TCG_REG_XZR, a1); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + +static void tgen_cset(TCGContext *s, TCGCond cond, TCGReg ret) +{ + /* Use CSET alias of CSINC Wd, WZR, WZR, invert(cond). */ + tcg_out_insn(s, 3506, CSINC, TCG_TYPE_I32, ret, TCG_REG_XZR, + TCG_REG_XZR, tcg_invert_cond(cond)); +} + +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_cmp(s, type, cond, a1, a2); + tgen_cset(s, cond, a0); +} + +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_cmpi(s, type, cond, a1, a2); + tgen_cset(s, cond, a0); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rC), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + +static void tgen_csetm(TCGContext *s, TCGType ext, TCGCond cond, TCGReg ret) +{ + /* Use CSETM alias of CSINV Wd, WZR, WZR, invert(cond). */ + tcg_out_insn(s, 3506, CSINV, ext, ret, TCG_REG_XZR, + TCG_REG_XZR, tcg_invert_cond(cond)); +} + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_cmp(s, type, cond, a1, a2); + tgen_csetm(s, type, cond, a0); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_cmpi(s, type, cond, a1, a2); + tgen_csetm(s, type, cond, a0); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rC), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; + +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg vt, bool const_vt, TCGArg vf, bool const_vf) +{ + tcg_out_cmp(s, type, cond, c1, c2, const_c2); + tcg_out_insn(s, 3506, CSEL, type, ret, vt, vf, cond); +} + +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, rC, rz, rz), + .out = tgen_movcond, +}; + +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) +{ + unsigned mask = type == TCG_TYPE_I32 ? 31 : 63; + + /* + * Since we can't support "0Z" as a constraint, we allow a1 in + * any register. Fix things up as if a matching constraint. + */ + if (a0 != a1) { + if (a0 == a2) { + tcg_out_mov(s, type, TCG_REG_TMP0, a2); + a2 = TCG_REG_TMP0; + } + tcg_out_mov(s, type, a0, a1); + } + tcg_out_bfm(s, type, a0, a2, -ofs & mask, len - 1); +} + +static void tgen_depositi(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + tcg_target_long a2, unsigned ofs, unsigned len) +{ + tgen_andi(s, type, a0, a1, ~MAKE_64BIT_MASK(ofs, len)); +} + +static void tgen_depositz(TCGContext *s, TCGType type, TCGReg a0, TCGReg a2, + unsigned ofs, unsigned len) +{ + int max = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_ubfm(s, type, a0, a2, -ofs & max, len - 1); +} + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(r, rZ, rZ), + .out_rrr = tgen_deposit, + .out_rri = tgen_depositi, + .out_rzr = tgen_depositz, +}; + +static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + uint64_t mask = MAKE_64BIT_MASK(0, len); + tcg_out_logicali(s, I3404_ANDI, type, a0, a1, mask); + } else { + tcg_out_ubfm(s, type, a0, a1, ofs, ofs + len - 1); + } +} + +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; + +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + tcg_out_sbfm(s, type, a0, a1, ofs, ofs + len - 1); +} + +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; + +static void tgen_extract2(TCGContext *s, TCGType type, TCGReg a0, + TCGReg a1, TCGReg a2, unsigned shr) +{ + tcg_out_extr(s, type, a0, a2, a1, shr); +} + +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_O1_I2(r, rz, rz), + .out_rrr = tgen_extract2, +}; + +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, I3312_LDRB, dest, base, offset, 0); +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + AArch64Insn insn = type == TCG_TYPE_I32 ? I3312_LDRSBW : I3312_LDRSBX; + tcg_out_ldst(s, insn, dest, base, offset, 0); +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, I3312_LDRH, dest, base, offset, 1); +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + AArch64Insn insn = type == TCG_TYPE_I32 ? I3312_LDRSHW : I3312_LDRSHX; + tcg_out_ldst(s, insn, dest, base, offset, 1); +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, I3312_LDRW, dest, base, offset, 2); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, I3312_LDRSWX, dest, base, offset, 2); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; + +static void tgen_st8_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, I3312_STRB, data, base, offset, 0); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st8_r, +}; + +static void tgen_st16_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, I3312_STRH, data, base, offset, 1); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st16_r, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tcg_out_st, +}; + static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, unsigned vecl, unsigned vece, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2626,7 +3119,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); @@ -2641,7 +3135,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]; @@ -2680,6 +3194,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; @@ -2818,155 +3333,10 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, } } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - - case INDEX_op_ld8u_i32: - case INDEX_op_ld8s_i32: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16s_i32: - case INDEX_op_ld_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: - case INDEX_op_not_i32: - case INDEX_op_not_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_ext8s_i32: - case INDEX_op_ext16s_i32: - case INDEX_op_ext8u_i32: - case INDEX_op_ext16u_i32: - case INDEX_op_ext8s_i64: - case INDEX_op_ext16s_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16u_i64: - case INDEX_op_ext32u_i64: - case INDEX_op_ext_i32_i64: - case INDEX_op_extu_i32_i64: - case INDEX_op_extract_i32: - case INDEX_op_extract_i64: - case INDEX_op_sextract_i32: - case INDEX_op_sextract_i64: - return C_O1_I1(r, r); - - case INDEX_op_st8_i32: - case INDEX_op_st16_i32: - case INDEX_op_st_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - return C_O0_I2(rZ, r); - - case INDEX_op_add_i32: - case INDEX_op_add_i64: - case INDEX_op_sub_i32: - case INDEX_op_sub_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, rA); - - case INDEX_op_mul_i32: - case INDEX_op_mul_i64: - case INDEX_op_div_i32: - case INDEX_op_div_i64: - case INDEX_op_divu_i32: - case INDEX_op_divu_i64: - case INDEX_op_rem_i32: - case INDEX_op_rem_i64: - case INDEX_op_remu_i32: - case INDEX_op_remu_i64: - case INDEX_op_muluh_i64: - case INDEX_op_mulsh_i64: - return C_O1_I2(r, r, r); - - 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: - case INDEX_op_xor_i64: - 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, rL); - - 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_ctz_i32: - case INDEX_op_clz_i64: - case INDEX_op_ctz_i64: - return C_O1_I2(r, r, rAL); - - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - return C_O0_I2(r, rA); - - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - return C_O1_I4(r, r, rA, rZ, rZ); - - 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: - return C_O1_I2(r, 0, rZ); - - case INDEX_op_extract2_i32: - case INDEX_op_extract2_i64: - return C_O1_I2(r, rZ, rZ); - - 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, rA, rMZ); - case INDEX_op_add_vec: case INDEX_op_sub_vec: case INDEX_op_mul_vec: @@ -3012,7 +3382,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) return C_O1_I2(w, 0, w); default: - g_assert_not_reached(); + return C_NotImplemented; } } @@ -3074,6 +3444,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); @@ -3095,16 +3467,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) - /* - * 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 + 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); + } 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]); @@ -3114,10 +3486,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, @@ -3135,6 +3509,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 98727ea53b9..3f3df5176d9 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -13,8 +13,6 @@ #ifndef AARCH64_TCG_TARGET_H #define AARCH64_TCG_TARGET_H -#include "host/cpuinfo.h" - #define TCG_TARGET_INSN_UNIT_SIZE 4 #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) @@ -47,127 +45,8 @@ typedef enum { TCG_AREG0 = TCG_REG_X19, } TCGReg; -#define TCG_TARGET_NB_REGS 64 - -/* used for function call generation */ -#define TCG_REG_CALL_STACK TCG_REG_SP -#define TCG_TARGET_STACK_ALIGN 16 -#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_EVEN -#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 -#define TCG_TARGET_HAS_rem_i32 1 -#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 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 -#define TCG_TARGET_HAS_eqv_i32 1 -#define TCG_TARGET_HAS_nand_i32 0 -#define TCG_TARGET_HAS_nor_i32 0 -#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_deposit_i32 1 -#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_extr_i64_i32 0 -#define TCG_TARGET_HAS_qemu_st8_i32 0 - -#define TCG_TARGET_HAS_div_i64 1 -#define TCG_TARGET_HAS_rem_i64 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 1 -#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 -#define TCG_TARGET_HAS_eqv_i64 1 -#define TCG_TARGET_HAS_nand_i64 0 -#define TCG_TARGET_HAS_nor_i64 0 -#define TCG_TARGET_HAS_clz_i64 1 -#define TCG_TARGET_HAS_ctz_i64 1 -#define TCG_TARGET_HAS_ctpop_i64 0 -#define TCG_TARGET_HAS_deposit_i64 1 -#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_REG_ZERO TCG_REG_XZR -/* - * 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_v64 1 -#define TCG_TARGET_HAS_v128 1 -#define TCG_TARGET_HAS_v256 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 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 1 -#define TCG_TARGET_HAS_roti_vec 0 -#define TCG_TARGET_HAS_rots_vec 0 -#define TCG_TARGET_HAS_rotv_vec 0 -#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_mul_vec 1 -#define TCG_TARGET_HAS_sat_vec 1 -#define TCG_TARGET_HAS_minmax_vec 1 -#define TCG_TARGET_HAS_bitsel_vec 1 -#define TCG_TARGET_HAS_cmpsel_vec 0 - -#define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS +#define TCG_TARGET_NB_REGS 64 #endif /* AARCH64_TCG_TARGET_H */ diff --git a/tcg/aarch64/tcg-target.opc.h b/tcg/aarch64/tcg-target.opc.h deleted file mode 100644 index bce30accd93..00000000000 --- a/tcg/aarch64/tcg-target.opc.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2019 Linaro - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * (at your option) any later version. - * - * See the COPYING file in the top-level directory 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. - */ - -DEF(aa64_sshl_vec, 1, 2, 0, IMPLVEC) -DEF(aa64_sli_vec, 1, 2, 1, IMPLVEC) diff --git a/tcg/arm/tcg-target-con-set.h b/tcg/arm/tcg-target-con-set.h index 229ae258ac1..16b1193228c 100644 --- a/tcg/arm/tcg-target-con-set.h +++ b/tcg/arm/tcg-target-con-set.h @@ -30,6 +30,9 @@ C_O1_I2(r, r, rI) C_O1_I2(r, r, rIK) C_O1_I2(r, r, rIN) C_O1_I2(r, r, ri) +C_O1_I2(r, rI, r) +C_O1_I2(r, rI, rIK) +C_O1_I2(r, rI, rIN) C_O1_I2(r, rZ, rZ) C_O1_I2(w, 0, w) C_O1_I2(w, w, w) @@ -42,5 +45,3 @@ C_O1_I4(r, r, rIN, rIK, 0) 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-has.h b/tcg/arm/tcg-target-has.h new file mode 100644 index 00000000000..3bbbde5d59e --- /dev/null +++ b/tcg/arm/tcg-target-has.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2008 Fabrice Bellard + * Copyright (c) 2008 Andrzej Zaborowski + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +extern int arm_arch; + +#define use_armv7_instructions (__ARM_ARCH >= 7 || arm_arch >= 7) + +#ifdef __ARM_ARCH_EXT_IDIV__ +#define use_idiv_instructions 1 +#else +extern bool use_idiv_instructions; +#endif +#ifdef __ARM_NEON__ +#define use_neon_instructions 1 +#else +extern bool use_neon_instructions; +#endif + +/* optional instructions */ +#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 + +#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 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 1 +#define TCG_TARGET_HAS_roti_vec 0 +#define TCG_TARGET_HAS_rots_vec 0 +#define TCG_TARGET_HAS_rotv_vec 0 +#define TCG_TARGET_HAS_shi_vec 1 +#define TCG_TARGET_HAS_shs_vec 0 +#define TCG_TARGET_HAS_shv_vec 0 +#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 1 +#define TCG_TARGET_HAS_cmpsel_vec 0 +#define TCG_TARGET_HAS_tst_vec 1 + +static inline bool +tcg_target_extract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (use_armv7_instructions) { + return true; /* SBFX or UBFX */ + } + switch (len) { + case 8: /* SXTB or UXTB */ + case 16: /* SXTH or UXTH */ + return (ofs % 8) == 0; + } + return false; +} + +#define TCG_TARGET_extract_valid tcg_target_extract_valid +#define TCG_TARGET_sextract_valid tcg_target_extract_valid +#define TCG_TARGET_deposit_valid(type, ofs, len) use_armv7_instructions + +#endif diff --git a/tcg/arm/tcg-target-mo.h b/tcg/arm/tcg-target-mo.h new file mode 100644 index 00000000000..12542dfd1cc --- /dev/null +++ b/tcg/arm/tcg-target-mo.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2008 Fabrice Bellard + * Copyright (c) 2008 Andrzej Zaborowski + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/arm/tcg-target-opc.h.inc b/tcg/arm/tcg-target-opc.h.inc new file mode 100644 index 00000000000..70394e0282a --- /dev/null +++ b/tcg/arm/tcg-target-opc.h.inc @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2019 Linaro + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + * + * See the COPYING file in the top-level directory 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. + */ + +DEF(arm_sli_vec, 1, 2, 1, TCG_OPF_VECTOR) +DEF(arm_sshl_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(arm_ushl_vec, 1, 2, 0, TCG_OPF_VECTOR) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index acb5f23b542..836894b16ad 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -23,8 +23,6 @@ */ #include "elf.h" -#include "../tcg-ldst.c.inc" -#include "../tcg-pool.c.inc" int arm_arch = __ARM_ARCH; @@ -35,6 +33,14 @@ bool use_idiv_instructions; bool use_neon_instructions; #endif +/* Used for function call generation. */ +#define TCG_TARGET_STACK_ALIGN 8 +#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 + #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", @@ -89,9 +95,7 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int 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, @@ -174,6 +178,8 @@ typedef enum { INSN_DMB_ISH = 0xf57ff05b, INSN_DMB_MCR = 0xee070fba, + INSN_MSRI_CPSR = 0x0360f000, + /* Architected nop introduced in v6k. */ /* ??? This is an MSR (imm) 0,0,0 insn. Anyone know if this also Just So Happened to do nothing on pre-v6k so that we @@ -353,17 +359,11 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, #define ALL_VECTOR_REGS 0xffff0000u /* - * r0-r3 will be overwritten when reading the tlb entry (softmmu only); + * 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_QLDST_REGS \ - (ALL_GENERAL_REGS & ~((1 << TCG_REG_R0) | (1 << TCG_REG_R1) | \ - (1 << TCG_REG_R2) | (1 << TCG_REG_R3) | \ - (1 << TCG_REG_R14))) -#else -#define ALL_QLDST_REGS (ALL_GENERAL_REGS & ~(1 << TCG_REG_R14)) -#endif + (ALL_GENERAL_REGS & ~((tcg_use_softmmu ? 0xf : 0) | (1 << TCG_REG_R14))) /* * ARM immediates for ALU instructions are made of an unsigned 8-bit @@ -509,7 +509,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; @@ -677,14 +678,8 @@ static void tcg_out_ldrd_r(TCGContext *s, ARMCond cond, TCGReg rt, tcg_out_memop_r(s, cond, INSN_LDRD_REG, rt, rn, rm, 1, 1, 0); } -static void __attribute__((unused)) -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 __attribute__((unused)) -tcg_out_strd_8(TCGContext *s, ARMCond cond, TCGReg rt, TCGReg rn, int imm8) +static void 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); } @@ -881,22 +876,39 @@ static void tcg_out_dat_rI(TCGContext *s, ARMCond cond, ARMInsn opc, * Emit either the reg,imm or reg,reg form of a data-processing insn. * rhs must satisfy the "rIK" constraint. */ +static void tcg_out_dat_IK(TCGContext *s, ARMCond cond, ARMInsn opc, + ARMInsn opinv, TCGReg dst, TCGReg lhs, TCGArg rhs) +{ + int imm12 = encode_imm(rhs); + if (imm12 < 0) { + imm12 = encode_imm_nofail(~rhs); + opc = opinv; + } + tcg_out_dat_imm(s, cond, opc, dst, lhs, imm12); +} + static void tcg_out_dat_rIK(TCGContext *s, ARMCond cond, ARMInsn opc, ARMInsn opinv, TCGReg dst, TCGReg lhs, TCGArg rhs, bool rhs_is_const) { if (rhs_is_const) { - int imm12 = encode_imm(rhs); - if (imm12 < 0) { - imm12 = encode_imm_nofail(~rhs); - opc = opinv; - } - tcg_out_dat_imm(s, cond, opc, dst, lhs, imm12); + tcg_out_dat_IK(s, cond, opc, opinv, dst, lhs, rhs); } else { tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0)); } } +static void tcg_out_dat_IN(TCGContext *s, ARMCond cond, ARMInsn opc, + ARMInsn opneg, TCGReg dst, TCGReg lhs, TCGArg rhs) +{ + int imm12 = encode_imm(rhs); + if (imm12 < 0) { + imm12 = encode_imm_nofail(-rhs); + opc = opneg; + } + tcg_out_dat_imm(s, cond, opc, dst, lhs, imm12); +} + static void tcg_out_dat_rIN(TCGContext *s, ARMCond cond, ARMInsn opc, ARMInsn opneg, TCGReg dst, TCGReg lhs, TCGArg rhs, bool rhs_is_const) @@ -905,52 +917,12 @@ static void tcg_out_dat_rIN(TCGContext *s, ARMCond cond, ARMInsn opc, * rhs must satisfy the "rIN" constraint. */ if (rhs_is_const) { - int imm12 = encode_imm(rhs); - if (imm12 < 0) { - imm12 = encode_imm_nofail(-rhs); - opc = opneg; - } - tcg_out_dat_imm(s, cond, opc, dst, lhs, imm12); + tcg_out_dat_IN(s, cond, opc, opneg, dst, lhs, rhs); } else { tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0)); } } -static void tcg_out_mul32(TCGContext *s, ARMCond cond, TCGReg rd, - TCGReg rn, TCGReg rm) -{ - /* mul */ - tcg_out32(s, (cond << 28) | 0x90 | (rd << 16) | (rm << 8) | rn); -} - -static void tcg_out_umull32(TCGContext *s, ARMCond cond, TCGReg rd0, - TCGReg rd1, TCGReg rn, TCGReg rm) -{ - /* umull */ - tcg_out32(s, (cond << 28) | 0x00800090 | - (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); -} - -static void tcg_out_smull32(TCGContext *s, ARMCond cond, TCGReg rd0, - TCGReg rd1, TCGReg rn, TCGReg rm) -{ - /* smull */ - tcg_out32(s, (cond << 28) | 0x00c00090 | - (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); -} - -static void tcg_out_sdiv(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, TCGReg rm) -{ - tcg_out32(s, 0x0710f010 | (cond << 28) | (rd << 16) | rn | (rm << 8)); -} - -static void tcg_out_udiv(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, TCGReg rm) -{ - tcg_out32(s, 0x0730f010 | (cond << 28) | (rd << 16) | rn | (rm << 8)); -} - static void tcg_out_ext8s(TCGContext *s, TCGType t, TCGReg rd, TCGReg rn) { /* sxtb */ @@ -999,56 +971,98 @@ 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, - TCGReg rd, TCGReg rn, int flags) +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) { - if (flags & TCG_BSWAP_OS) { - /* revsh */ - tcg_out32(s, 0x06ff0fb0 | (cond << 28) | (rd << 12) | rn); + /* bfi/bfc */ + tcg_out32(s, 0x07c00010 | (COND_AL << 28) | (a0 << 12) | a1 + | (ofs << 7) | ((ofs + len - 1) << 16)); +} + +static void tgen_depositi(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + tcg_target_long a2, unsigned ofs, unsigned len) +{ + /* bfi becomes bfc with rn == 15. */ + tgen_deposit(s, type, a0, a1, 15, ofs, len); +} + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(r, 0, rZ), + .out_rrr = tgen_deposit, + .out_rri = tgen_depositi, +}; + +static void tgen_extract(TCGContext *s, TCGType type, TCGReg rd, TCGReg rn, + unsigned ofs, unsigned len) +{ + /* According to gcc, AND can be faster. */ + if (ofs == 0 && len <= 8) { + tcg_out_dat_imm(s, COND_AL, ARITH_AND, rd, rn, + encode_imm_nofail((1 << len) - 1)); return; } - /* rev16 */ - tcg_out32(s, 0x06bf0fb0 | (cond << 28) | (rd << 12) | rn); - if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + if (use_armv7_instructions) { + /* ubfx */ + tcg_out32(s, 0x07e00050 | (COND_AL << 28) | (rd << 12) | rn + | (ofs << 7) | ((len - 1) << 16)); + return; + } + + assert(ofs % 8 == 0); + switch (len) { + case 8: + /* uxtb */ + tcg_out32(s, 0x06ef0070 | (COND_AL << 28) | + (rd << 12) | (ofs << 7) | rn); + break; + case 16: /* uxth */ - tcg_out32(s, 0x06ff0070 | (cond << 28) | (rd << 12) | rd); + tcg_out32(s, 0x06ff0070 | (COND_AL << 28) | + (rd << 12) | (ofs << 7) | rn); + break; + default: + g_assert_not_reached(); } } -static void tcg_out_bswap32(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn) -{ - /* rev */ - tcg_out32(s, 0x06bf0f30 | (cond << 28) | (rd << 12) | rn); -} +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; -static void tcg_out_deposit(TCGContext *s, ARMCond cond, TCGReg rd, - TCGArg a1, int ofs, int len, bool const_a1) +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg rd, TCGReg rn, + unsigned ofs, unsigned len) { - if (const_a1) { - /* bfi becomes bfc with rn == 15. */ - a1 = 15; + if (use_armv7_instructions) { + /* sbfx */ + tcg_out32(s, 0x07a00050 | (COND_AL << 28) | (rd << 12) | rn + | (ofs << 7) | ((len - 1) << 16)); + return; } - /* bfi/bfc */ - tcg_out32(s, 0x07c00010 | (cond << 28) | (rd << 12) | a1 - | (ofs << 7) | ((ofs + len - 1) << 16)); -} -static void tcg_out_extract(TCGContext *s, ARMCond cond, TCGReg rd, - TCGReg rn, int ofs, int len) -{ - /* ubfx */ - tcg_out32(s, 0x07e00050 | (cond << 28) | (rd << 12) | rn - | (ofs << 7) | ((len - 1) << 16)); + assert(ofs % 8 == 0); + switch (len) { + case 8: + /* sxtb */ + tcg_out32(s, 0x06af0070 | (COND_AL << 28) | + (rd << 12) | (ofs << 7) | rn); + break; + case 16: + /* sxth */ + tcg_out32(s, 0x06bf0070 | (COND_AL << 28) | + (rd << 12) | (ofs << 7) | rn); + break; + default: + g_assert_not_reached(); + } } -static void tcg_out_sextract(TCGContext *s, ARMCond cond, TCGReg rd, - TCGReg rn, int ofs, int len) -{ - /* sbfx */ - tcg_out32(s, 0x07a00050 | (cond << 28) | (rd << 12) | rn - | (ofs << 7) | ((len - 1) << 16)); -} +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; + static void tcg_out_ld32u(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn, int32_t offset) @@ -1070,66 +1084,6 @@ static void tcg_out_st32(TCGContext *s, ARMCond cond, tcg_out_st32_12(s, cond, rd, rn, offset); } -static void tcg_out_ld16u(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, int32_t offset) -{ - if (offset > 0xff || offset < -0xff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_ld16u_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_ld16u_8(s, cond, rd, rn, offset); -} - -static void tcg_out_ld16s(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, int32_t offset) -{ - if (offset > 0xff || offset < -0xff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_ld16s_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_ld16s_8(s, cond, rd, rn, offset); -} - -static void tcg_out_st16(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, int32_t offset) -{ - if (offset > 0xff || offset < -0xff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_st16_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_st16_8(s, cond, rd, rn, offset); -} - -static void tcg_out_ld8u(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, int32_t offset) -{ - if (offset > 0xfff || offset < -0xfff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_ld8_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_ld8_12(s, cond, rd, rn, offset); -} - -static void tcg_out_ld8s(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, int32_t offset) -{ - if (offset > 0xff || offset < -0xff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_ld8s_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_ld8s_8(s, cond, rd, rn, offset); -} - -static void tcg_out_st8(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, int32_t offset) -{ - if (offset > 0xfff || offset < -0xfff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_st8_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_st8_12(s, cond, rd, rn, offset); -} - /* * The _goto case is normally between TBs within the same code buffer, and * with the code buffer limited to 16MB we wouldn't need the long case. @@ -1189,7 +1143,12 @@ static void tcg_out_goto_label(TCGContext *s, ARMCond cond, TCGLabel *l) } } -static void tcg_out_mb(TCGContext *s, TCGArg a0) +static void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + tcg_out_goto_label(s, COND_AL, l); +} + +static void tcg_out_mb(TCGContext *s, unsigned a0) { if (use_armv7_instructions) { tcg_out32(s, INSN_DMB_ISH); @@ -1198,17 +1157,53 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) } } -static TCGCond tcg_out_cmp2(TCGContext *s, const TCGArg *args, - const int *const_args) +static TCGCond tgen_cmp(TCGContext *s, TCGCond cond, TCGReg a, TCGReg b) +{ + if (is_tst_cond(cond)) { + tcg_out_dat_reg(s, COND_AL, ARITH_TST, 0, a, b, SHIFT_IMM_LSL(0)); + return tcg_tst_eqne_cond(cond); + } + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, a, b, SHIFT_IMM_LSL(0)); + return cond; +} + +static TCGCond tgen_cmpi(TCGContext *s, TCGCond cond, TCGReg a, TCGArg b) +{ + int imm12; + + if (!is_tst_cond(cond)) { + tcg_out_dat_IN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, a, b); + return cond; + } + + /* + * The compare constraints allow rIN, but TST does not support N. + * Be prepared to load the constant into a scratch register. + */ + imm12 = encode_imm(b); + if (imm12 >= 0) { + tcg_out_dat_imm(s, COND_AL, ARITH_TST, 0, a, imm12); + } else { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, b); + tcg_out_dat_reg(s, COND_AL, ARITH_TST, 0, + a, TCG_REG_TMP, SHIFT_IMM_LSL(0)); + } + return tcg_tst_eqne_cond(cond); +} + +static TCGCond tcg_out_cmp(TCGContext *s, TCGCond cond, TCGReg a, + TCGArg b, int b_const) { - TCGReg al = args[0]; - TCGReg ah = args[1]; - TCGArg bl = args[2]; - TCGArg bh = args[3]; - TCGCond cond = args[4]; - int const_bl = const_args[2]; - int const_bh = const_args[3]; + if (b_const) { + return tgen_cmpi(s, cond, a, b); + } else { + return tgen_cmp(s, cond, a, b); + } +} +static TCGCond tcg_out_cmp2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, TCGArg bh, bool const_bh) +{ switch (cond) { case TCG_COND_EQ: case TCG_COND_NE: @@ -1225,6 +1220,13 @@ static TCGCond tcg_out_cmp2(TCGContext *s, const TCGArg *args, 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. @@ -1380,132 +1382,115 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) #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) + TCGReg addr, MemOpIdx oi, bool is_ld) { TCGLabelQemuLdst *ldst = NULL; MemOp opc = get_memop(oi); unsigned a_mask; -#ifdef CONFIG_SOFTMMU - *h = (HostAddress){ - .cond = COND_AL, - .base = addrlo, - .index = TCG_REG_R1, - .index_scratch = true, - }; -#else - *h = (HostAddress){ - .cond = COND_AL, - .base = addrlo, - .index = guest_base ? TCG_REG_GUEST_BASE : -1, - .index_scratch = false, - }; -#endif + if (tcg_use_softmmu) { + *h = (HostAddress){ + .cond = COND_AL, + .base = addr, + .index = TCG_REG_R1, + .index_scratch = true, + }; + } else { + *h = (HostAddress){ + .cond = COND_AL, + .base = addr, + .index = guest_base ? TCG_REG_GUEST_BASE : -1, + .index_scratch = false, + }; + } h->aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, false); a_mask = (1 << h->aa.align) - 1; -#ifdef CONFIG_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 env_tlb(env)->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)); + 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; - /* - * 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) { + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addr_reg = addr; + + /* 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, addr, + 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 and the fast path addend into R1. + */ + if (cmp_off == 0) { 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_dat_reg(s, COND_AL, ARITH_ADD, + TCG_REG_R1, TCG_REG_R1, TCG_REG_R0, 0); 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)); + /* 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); + /* + * 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 = addr; + if (a_mask < s_mask) { + t_addr = TCG_REG_R0; + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, t_addr, + addr, s_mask - 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) { + 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, addr, 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)); + } + } 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; + ldst->addr_reg = addr; /* 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); + tcg_out_dat_imm(s, COND_AL, ARITH_TST, 0, addr, a_mask); } -#endif return ldst; } @@ -1559,7 +1544,7 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, MemOp opc, TCGReg datalo, 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) { + if (memop_alignment_bits(opc) >= MO_64) { if (h.index < 0) { tcg_out_ldrd_8(s, h.cond, datalo, h.base, 0); break; @@ -1601,17 +1586,50 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, MemOp opc, TCGReg datalo, } } -static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) +{ + MemOp opc = get_memop(oi); + TCGLabelQemuLdst *ldst; + HostAddress h; + + ldst = prepare_host_addr(s, &h, addr, oi, true); + if (ldst) { + ldst->type = type; + ldst->datalo_reg = data; + ldst->datahi_reg = -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. + */ + ldst->label_ptr[0] = s->code_ptr; + tcg_out_bl_imm(s, COND_NE, 0); + } + + tcg_out_qemu_ld_direct(s, opc, data, -1, h); + + if (ldst) { + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } +} + +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, q), + .out = tgen_qemu_ld, +}; + +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, true); + ldst = prepare_host_addr(s, &h, addr, oi, true); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = datalo; ldst->datahi_reg = datahi; @@ -1622,14 +1640,20 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, */ ldst->label_ptr[0] = s->code_ptr; tcg_out_bl_imm(s, COND_NE, 0); + } - tcg_out_qemu_ld_direct(s, opc, datalo, datahi, h); + tcg_out_qemu_ld_direct(s, opc, datalo, datahi, h); + + if (ldst) { ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); - } else { - tcg_out_qemu_ld_direct(s, opc, datalo, datahi, h); } } +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = C_O2_I1(e, p, q), + .out = tgen_qemu_ld2, +}; + static void tcg_out_qemu_st_direct(TCGContext *s, MemOp opc, TCGReg datalo, TCGReg datahi, HostAddress h) { @@ -1663,12 +1687,15 @@ static void tcg_out_qemu_st_direct(TCGContext *s, MemOp opc, TCGReg datalo, 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) { + 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); @@ -1684,17 +1711,46 @@ static void tcg_out_qemu_st_direct(TCGContext *s, MemOp opc, TCGReg datalo, } } -static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) +{ + MemOp opc = get_memop(oi); + TCGLabelQemuLdst *ldst; + HostAddress h; + + ldst = prepare_host_addr(s, &h, addr, oi, false); + if (ldst) { + ldst->type = type; + ldst->datalo_reg = data; + ldst->datahi_reg = -1; + + h.cond = COND_EQ; + tcg_out_qemu_st_direct(s, opc, data, -1, h); + + /* 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, data, -1, h); + } +} + +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(q, q), + .out = tgen_qemu_st, +}; + +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, false); + ldst = prepare_host_addr(s, &h, addr, oi, false); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = datalo; ldst->datahi_reg = datahi; @@ -1710,6 +1766,11 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, } } +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = C_O0_I3(Q, p, q), + .out = tgen_qemu_st2, +}; + static void tcg_out_epilogue(TCGContext *s); static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg) @@ -1740,15 +1801,20 @@ static void tcg_out_goto_tb(TCGContext *s, int which) * shifted immediate from pc. */ int h = -i_disp; - int l = h & 0xfff; + int l = -(h & 0xfff); - h = encode_imm_nofail(h - l); + 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); } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_b_reg(s, COND_AL, a0); +} + void tb_target_set_jmp_target(const TranslationBlock *tb, int n, uintptr_t jmp_rx, uintptr_t jmp_rw) { @@ -1768,423 +1834,816 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, 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]) + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) { - TCGArg a0, a1, a2, a3, a4, a5; - int c; + tcg_out_dat_reg(s, COND_AL, ARITH_ADD, a0, a1, a2, SHIFT_IMM_LSL(0)); +} - switch (opc) { - case INDEX_op_goto_ptr: - tcg_out_b_reg(s, COND_AL, args[0]); - break; - case INDEX_op_br: - tcg_out_goto_label(s, COND_AL, arg_label(args[0])); - break; +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IN(s, COND_AL, ARITH_ADD, ARITH_SUB, a0, a1, a2); +} - case INDEX_op_ld8u_i32: - tcg_out_ld8u(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_ld8s_i32: - tcg_out_ld8s(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_ld16u_i32: - tcg_out_ld16u(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_ld16s_i32: - tcg_out_ld16s(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_ld_i32: - tcg_out_ld32u(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_st8_i32: - tcg_out_st8(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_st16_i32: - tcg_out_st16(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_st_i32: - tcg_out_st32(s, COND_AL, args[0], args[1], args[2]); - break; +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, rIN), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; - case INDEX_op_movcond_i32: - /* 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, - ARITH_MVN, args[0], 0, args[3], const_args[3]); - break; - case INDEX_op_add_i32: - tcg_out_dat_rIN(s, COND_AL, ARITH_ADD, ARITH_SUB, - args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_sub_i32: - if (const_args[1]) { - if (const_args[2]) { - tcg_out_movi32(s, COND_AL, args[0], args[1] - args[2]); - } else { - tcg_out_dat_rI(s, COND_AL, ARITH_RSB, - args[0], args[2], args[1], 1); - } - } else { - tcg_out_dat_rIN(s, COND_AL, ARITH_SUB, ARITH_ADD, - args[0], args[1], args[2], const_args[2]); - } - break; - case INDEX_op_and_i32: - tcg_out_dat_rIK(s, COND_AL, ARITH_AND, ARITH_BIC, - args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_andc_i32: - tcg_out_dat_rIK(s, COND_AL, ARITH_BIC, ARITH_AND, - args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_or_i32: - c = ARITH_ORR; - goto gen_arith; - case INDEX_op_xor_i32: - c = ARITH_EOR; - /* Fall through. */ - gen_arith: - tcg_out_dat_rI(s, COND_AL, c, args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_add2_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - a3 = args[3], a4 = args[4], a5 = args[5]; - if (a0 == a3 || (a0 == a5 && !const_args[5])) { - a0 = TCG_REG_TMP; - } - tcg_out_dat_rIN(s, COND_AL, ARITH_ADD | TO_CPSR, ARITH_SUB | TO_CPSR, - a0, a2, a4, const_args[4]); - tcg_out_dat_rIK(s, COND_AL, ARITH_ADC, ARITH_SBC, - a1, a3, a5, const_args[5]); - tcg_out_mov_reg(s, COND_AL, args[0], a0); - break; - case INDEX_op_sub2_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - a3 = args[3], a4 = args[4], a5 = args[5]; - if ((a0 == a3 && !const_args[3]) || (a0 == a5 && !const_args[5])) { - a0 = TCG_REG_TMP; - } - if (const_args[2]) { - if (const_args[4]) { - tcg_out_movi32(s, COND_AL, a0, a4); - a4 = a0; - } - tcg_out_dat_rI(s, COND_AL, ARITH_RSB | TO_CPSR, a0, a4, a2, 1); - } else { - tcg_out_dat_rIN(s, COND_AL, ARITH_SUB | TO_CPSR, - ARITH_ADD | TO_CPSR, a0, a2, a4, const_args[4]); - } - if (const_args[3]) { - if (const_args[5]) { - tcg_out_movi32(s, COND_AL, a1, a5); - a5 = a1; - } - tcg_out_dat_rI(s, COND_AL, ARITH_RSC, a1, a5, a3, 1); - } else { - tcg_out_dat_rIK(s, COND_AL, ARITH_SBC, ARITH_ADC, - a1, a3, a5, const_args[5]); - } - tcg_out_mov_reg(s, COND_AL, args[0], a0); - break; - case INDEX_op_neg_i32: - tcg_out_dat_imm(s, COND_AL, ARITH_RSB, args[0], args[1], 0); - break; - case INDEX_op_not_i32: - tcg_out_dat_reg(s, COND_AL, - ARITH_MVN, args[0], 0, args[1], SHIFT_IMM_LSL(0)); - break; - case INDEX_op_mul_i32: - tcg_out_mul32(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_mulu2_i32: - tcg_out_umull32(s, COND_AL, args[0], args[1], args[2], args[3]); - break; - case INDEX_op_muls2_i32: - tcg_out_smull32(s, COND_AL, args[0], args[1], args[2], args[3]); - break; - /* XXX: Perhaps args[2] & 0x1f is wrong */ - case INDEX_op_shl_i32: - c = const_args[2] ? - SHIFT_IMM_LSL(args[2] & 0x1f) : SHIFT_REG_LSL(args[2]); - goto gen_shift32; - case INDEX_op_shr_i32: - c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_LSR(args[2] & 0x1f) : - SHIFT_IMM_LSL(0) : SHIFT_REG_LSR(args[2]); - goto gen_shift32; - case INDEX_op_sar_i32: - c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ASR(args[2] & 0x1f) : - SHIFT_IMM_LSL(0) : SHIFT_REG_ASR(args[2]); - goto gen_shift32; - case INDEX_op_rotr_i32: - c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ROR(args[2] & 0x1f) : - SHIFT_IMM_LSL(0) : SHIFT_REG_ROR(args[2]); - /* Fall through. */ - gen_shift32: - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], c); - break; +static void tgen_addco(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_ADD | TO_CPSR, + a0, a1, a2, SHIFT_IMM_LSL(0)); +} - case INDEX_op_rotl_i32: - if (const_args[2]) { - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], - ((0x20 - args[2]) & 0x1f) ? - SHIFT_IMM_ROR((0x20 - args[2]) & 0x1f) : - SHIFT_IMM_LSL(0)); - } else { - tcg_out_dat_imm(s, COND_AL, ARITH_RSB, TCG_REG_TMP, args[2], 0x20); - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], - SHIFT_REG_ROR(TCG_REG_TMP)); - } - break; +static void tgen_addco_imm(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IN(s, COND_AL, ARITH_ADD | TO_CPSR, ARITH_SUB | TO_CPSR, + a0, a1, a2); +} - case INDEX_op_ctz_i32: - tcg_out_dat_reg(s, COND_AL, INSN_RBIT, TCG_REG_TMP, 0, args[1], 0); - a1 = TCG_REG_TMP; - goto do_clz; - - case INDEX_op_clz_i32: - a1 = args[1]; - do_clz: - a0 = args[0]; - a2 = args[2]; - c = const_args[2]; - if (c && a2 == 32) { - tcg_out_dat_reg(s, COND_AL, INSN_CLZ, a0, 0, a1, 0); - break; - } +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_O1_I2(r, r, rIN), + .out_rrr = tgen_addco, + .out_rri = tgen_addco_imm, +}; + +static void tgen_addci(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_ADC, a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_addci_imm(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IK(s, COND_AL, ARITH_ADC, ARITH_SBC, a0, a1, a2); +} + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_O1_I2(r, r, rIK), + .out_rrr = tgen_addci, + .out_rri = tgen_addci_imm, +}; + +static void tgen_addcio(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_ADC | TO_CPSR, + a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_addcio_imm(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IK(s, COND_AL, ARITH_ADC | TO_CPSR, ARITH_SBC | TO_CPSR, + a0, a1, a2); +} + +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_O1_I2(r, r, rIK), + .out_rrr = tgen_addcio, + .out_rri = tgen_addcio_imm, +}; + +/* Set C to @c; NZVQ all set to 0. */ +static void tcg_out_movi_apsr_c(TCGContext *s, bool c) +{ + int imm12 = encode_imm_nofail(c << 29); + tcg_out32(s, (COND_AL << 28) | INSN_MSRI_CPSR | 0x80000 | imm12); +} + +static void tcg_out_set_carry(TCGContext *s) +{ + tcg_out_movi_apsr_c(s, 1); +} + +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_AND, a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IK(s, COND_AL, ARITH_AND, ARITH_BIC, a0, a1, a2); +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, rIK), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; + +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_BIC, a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_andc, +}; + +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_imm(s, COND_AL, ARITH_CMP, 0, a1, 0); + tcg_out_dat_reg(s, COND_NE, INSN_CLZ, a0, 0, a1, 0); + tcg_out_mov_reg(s, COND_EQ, a0, a2); +} + +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a2 == 32) { + tcg_out_dat_reg(s, COND_AL, INSN_CLZ, a0, 0, a1, 0); + } else { tcg_out_dat_imm(s, COND_AL, ARITH_CMP, 0, a1, 0); tcg_out_dat_reg(s, COND_NE, INSN_CLZ, a0, 0, a1, 0); - if (c || a0 != a2) { - tcg_out_dat_rIK(s, COND_EQ, ARITH_MOV, ARITH_MVN, a0, 0, a2, c); - } - break; + tcg_out_movi32(s, COND_EQ, a0, a2); + } +} - 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])); - 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]], - ARITH_MOV, args[0], 0, 1); - tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[3])], - ARITH_MOV, args[0], 0, 0); - break; - case INDEX_op_negsetcond_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]], - ARITH_MVN, args[0], 0, 0); - tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[3])], - ARITH_MOV, args[0], 0, 0); - break; +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_O1_I2(r, r, rIK), + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; - case INDEX_op_brcond2_i32: - c = tcg_out_cmp2(s, args, const_args); - tcg_out_goto_label(s, tcg_cond_to_arm_cond[c], arg_label(args[5])); - break; - case INDEX_op_setcond2_i32: - c = tcg_out_cmp2(s, args + 1, const_args + 1); - 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(c)], - ARITH_MOV, args[0], 0, 0); - break; +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_NotImplemented, +}; - 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_a64_i32: - tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], - args[3], TCG_TYPE_I32); - break; - 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_ld_a64_i64: - tcg_out_qemu_ld(s, args[0], args[1], args[2], args[3], - args[4], TCG_TYPE_I64); - break; +static void tgen_ctz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, INSN_RBIT, TCG_REG_TMP, 0, a1, 0); + tgen_clz(s, TCG_TYPE_I32, a0, TCG_REG_TMP, a2); +} - 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; +static void tgen_ctzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_reg(s, COND_AL, INSN_RBIT, TCG_REG_TMP, 0, a1, 0); + tgen_clzi(s, TCG_TYPE_I32, a0, TCG_REG_TMP, a2); +} - case INDEX_op_bswap16_i32: - tcg_out_bswap16(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_bswap32_i32: - tcg_out_bswap32(s, COND_AL, args[0], args[1]); - break; +static TCGConstraintSetIndex cset_ctz(TCGType type, unsigned flags) +{ + return use_armv7_instructions ? C_O1_I2(r, r, rIK) : C_NotImplemented; +} - case INDEX_op_deposit_i32: - tcg_out_deposit(s, COND_AL, args[0], args[2], - args[3], args[4], const_args[2]); - break; - case INDEX_op_extract_i32: - tcg_out_extract(s, COND_AL, args[0], args[1], args[2], args[3]); - break; - case INDEX_op_sextract_i32: - tcg_out_sextract(s, COND_AL, args[0], args[1], args[2], args[3]); - break; - case INDEX_op_extract2_i32: - /* ??? These optimization vs zero should be generic. */ - /* ??? But we can't substitute 2 for 1 in the opcode stream yet. */ - if (const_args[1]) { - if (const_args[2]) { - tcg_out_movi(s, TCG_TYPE_REG, args[0], 0); - } else { - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, - args[2], SHIFT_IMM_LSL(32 - args[3])); - } - } else if (const_args[2]) { - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, - args[1], SHIFT_IMM_LSR(args[3])); - } else { - /* We can do extract2 in 2 insns, vs the 3 required otherwise. */ - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_TMP, 0, - args[2], SHIFT_IMM_LSL(32 - args[3])); - tcg_out_dat_reg(s, COND_AL, ARITH_ORR, args[0], TCG_REG_TMP, - args[1], SHIFT_IMM_LSR(args[3])); - } - break; +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctz, + .out_rrr = tgen_ctz, + .out_rri = tgen_ctzi, +}; - case INDEX_op_div_i32: - tcg_out_sdiv(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_divu_i32: - tcg_out_udiv(s, COND_AL, args[0], args[1], args[2]); - break; +static TCGConstraintSetIndex cset_idiv(TCGType type, unsigned flags) +{ + return use_idiv_instructions ? C_O1_I2(r, r, r) : C_NotImplemented; +} - case INDEX_op_mb: - tcg_out_mb(s, args[0]); - break; +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + /* sdiv */ + tcg_out32(s, 0x0710f010 | (COND_AL << 28) | (a0 << 16) | a1 | (a2 << 8)); +} - 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: - g_assert_not_reached(); +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_idiv, + .out_rrr = tgen_divs, +}; + +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + /* udiv */ + tcg_out32(s, 0x0730f010 | (COND_AL << 28) | (a0 << 16) | a1 | (a2 << 8)); +} + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_idiv, + .out_rrr = tgen_divu, +}; + +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + /* mul */ + tcg_out32(s, (COND_AL << 28) | 0x90 | (a0 << 16) | (a1 << 8) | a2); +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mul, +}; + +static void tgen_muls2(TCGContext *s, TCGType type, + TCGReg rd0, TCGReg rd1, TCGReg rn, TCGReg rm) +{ + /* smull */ + tcg_out32(s, (COND_AL << 28) | 0x00c00090 | + (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); +} + +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_O2_I2(r, r, r, r), + .out_rrrr = tgen_muls2, +}; + +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_mulu2(TCGContext *s, TCGType type, + TCGReg rd0, TCGReg rd1, TCGReg rn, TCGReg rm) +{ + /* umull */ + tcg_out32(s, (COND_AL << 28) | 0x00800090 | + (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); +} + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_O2_I2(r, r, r, r), + .out_rrrr = tgen_mulu2, +}; + +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_ORR, a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_imm(s, COND_AL, ARITH_ORR, a0, a1, encode_imm_nofail(a2)); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_rotr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, SHIFT_REG_ROR(a2)); +} + +static void tgen_rotri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, SHIFT_IMM_ROR(a2 & 0x1f)); +} + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_rotr, + .out_rri = tgen_rotri, +}; + +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, SHIFT_REG_ASR(a2)); +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, + SHIFT_IMM_ASR(a2 & 0x1f)); +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, SHIFT_REG_LSL(a2)); +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, + SHIFT_IMM_LSL(a2 & 0x1f)); +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, SHIFT_REG_LSR(a2)); +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, + SHIFT_IMM_LSR(a2 & 0x1f)); +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_SUB, a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_subfi(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tcg_out_dat_imm(s, COND_AL, ARITH_RSB, a0, a2, encode_imm_nofail(a1)); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, rI, r), + .out_rrr = tgen_sub, + .out_rir = tgen_subfi, +}; + +static void tgen_subbo_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_SUB | TO_CPSR, + a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_subbo_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IN(s, COND_AL, ARITH_SUB | TO_CPSR, ARITH_ADD | TO_CPSR, + a0, a1, a2); +} + +static void tgen_subbo_rir(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tcg_out_dat_imm(s, COND_AL, ARITH_RSB | TO_CPSR, + a0, a2, encode_imm_nofail(a1)); +} + +static void tgen_subbo_rii(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, tcg_target_long a2) +{ + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, a2); + tgen_subbo_rir(s, TCG_TYPE_I32, a0, a1, TCG_REG_TMP); +} + +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_O1_I2(r, rI, rIN), + .out_rrr = tgen_subbo_rrr, + .out_rri = tgen_subbo_rri, + .out_rir = tgen_subbo_rir, + .out_rii = tgen_subbo_rii, +}; + +static void tgen_subbi_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_SBC, + a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_subbi_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IK(s, COND_AL, ARITH_SBC, ARITH_ADC, a0, a1, a2); +} + +static void tgen_subbi_rir(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tcg_out_dat_imm(s, COND_AL, ARITH_RSC, a0, a2, encode_imm_nofail(a1)); +} + +static void tgen_subbi_rii(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, tcg_target_long a2) +{ + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, a2); + tgen_subbi_rir(s, TCG_TYPE_I32, a0, a1, TCG_REG_TMP); +} + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_O1_I2(r, rI, rIK), + .out_rrr = tgen_subbi_rrr, + .out_rri = tgen_subbi_rri, + .out_rir = tgen_subbi_rir, + .out_rii = tgen_subbi_rii, +}; + +static void tgen_subbio_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_SBC | TO_CPSR, + a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_subbio_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IK(s, COND_AL, ARITH_SBC | TO_CPSR, ARITH_ADC | TO_CPSR, + a0, a1, a2); +} + +static void tgen_subbio_rir(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tcg_out_dat_imm(s, COND_AL, ARITH_RSC | TO_CPSR, + a0, a2, encode_imm_nofail(a1)); +} + +static void tgen_subbio_rii(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, tcg_target_long a2) +{ + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, a2); + tgen_subbio_rir(s, TCG_TYPE_I32, a0, a1, TCG_REG_TMP); +} + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_O1_I2(r, rI, rIK), + .out_rrr = tgen_subbio_rrr, + .out_rri = tgen_subbio_rri, + .out_rir = tgen_subbio_rir, + .out_rii = tgen_subbio_rii, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + tcg_out_movi_apsr_c(s, 0); /* borrow = !carry */ +} + +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_EOR, a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_imm(s, COND_AL, ARITH_EOR, a0, a1, encode_imm_nofail(a2)); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg rd, TCGReg rn, unsigned flags) +{ + if (flags & TCG_BSWAP_OS) { + /* revsh */ + tcg_out32(s, 0x06ff0fb0 | (COND_AL << 28) | (rd << 12) | rn); + return; + } + + /* rev16 */ + tcg_out32(s, 0x06bf0fb0 | (COND_AL << 28) | (rd << 12) | rn); + if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + tcg_out_ext16u(s, rd, rd); } } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap16, +}; + +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg rd, TCGReg rn, unsigned flags) { - switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - - case INDEX_op_ld8u_i32: - case INDEX_op_ld8s_i32: - 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: - case INDEX_op_ext8s_i32: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16u_i32: - case INDEX_op_extract_i32: - case INDEX_op_sextract_i32: - return C_O1_I1(r, r); - - case INDEX_op_st8_i32: - case INDEX_op_st16_i32: - case INDEX_op_st_i32: - return C_O0_I2(r, r); - - 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: - case INDEX_op_andc_i32: - case INDEX_op_clz_i32: - case INDEX_op_ctz_i32: - return C_O1_I2(r, r, rIK); - - case INDEX_op_mul_i32: - case INDEX_op_div_i32: - case INDEX_op_divu_i32: - return C_O1_I2(r, r, r); - - case INDEX_op_mulu2_i32: - case INDEX_op_muls2_i32: - return C_O2_I2(r, r, r, r); - - case INDEX_op_or_i32: - case INDEX_op_xor_i32: - return C_O1_I2(r, r, rI); - - 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: - return C_O1_I2(r, r, ri); - - case INDEX_op_brcond_i32: - return C_O0_I2(r, rIN); - case INDEX_op_deposit_i32: - return C_O1_I2(r, 0, rZ); - case INDEX_op_extract2_i32: - return C_O1_I2(r, rZ, rZ); - case INDEX_op_movcond_i32: - return C_O1_I4(r, r, rIN, rIK, 0); - case INDEX_op_add2_i32: - return C_O2_I4(r, r, r, r, rIN, rIK); - case INDEX_op_sub2_i32: - return C_O2_I4(r, r, rI, rI, rIN, rIK); - case INDEX_op_brcond2_i32: - return C_O0_I4(r, r, rI, rI); - case INDEX_op_setcond2_i32: - return C_O1_I4(r, r, r, rI, rI); - - 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); + /* rev */ + tcg_out32(s, 0x06bf0f30 | (COND_AL << 28) | (rd << 12) | rn); +} +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap32, +}; + +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_subfi(s, type, a0, 0, a1); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MVN, a0, 0, a1, SHIFT_IMM_LSL(0)); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, TCGLabel *l) +{ + cond = tgen_cmp(s, cond, a0, a1); + tcg_out_goto_label(s, tcg_cond_to_arm_cond[cond], l); +} + +static void tgen_brcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, tcg_target_long a1, TCGLabel *l) +{ + cond = tgen_cmpi(s, cond, a0, a1); + tcg_out_goto_label(s, tcg_cond_to_arm_cond[cond], l); +} + +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rIN), + .out_rr = tgen_brcond, + .out_ri = tgen_brcondi, +}; + +static void finish_setcond(TCGContext *s, TCGCond cond, TCGReg ret, bool neg) +{ + tcg_out_movi32(s, tcg_cond_to_arm_cond[tcg_invert_cond(cond)], ret, 0); + tcg_out_movi32(s, tcg_cond_to_arm_cond[cond], ret, neg ? -1 : 1); +} + +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + cond = tgen_cmp(s, cond, a1, a2); + finish_setcond(s, cond, a0, false); +} + +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + cond = tgen_cmpi(s, cond, a1, a2); + finish_setcond(s, cond, a0, false); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rIN), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + cond = tgen_cmp(s, cond, a1, a2); + finish_setcond(s, cond, a0, true); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + cond = tgen_cmpi(s, cond, a1, a2); + finish_setcond(s, cond, a0, true); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rIN), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; + +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg vt, bool const_vt, TCGArg vf, bool consf_vf) +{ + cond = tcg_out_cmp(s, cond, c1, c2, const_c2); + tcg_out_dat_rIK(s, tcg_cond_to_arm_cond[cond], ARITH_MOV, ARITH_MVN, + ret, 0, vt, const_vt); +} + +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, rIN, rIK, 0), + .out = tgen_movcond, +}; + +static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, TCGArg bh, bool const_bh, + TCGLabel *l) +{ + cond = tcg_out_cmp2(s, cond, al, ah, bl, const_bl, bh, const_bh); + tcg_out_goto_label(s, tcg_cond_to_arm_cond[cond], l); +} + +static const TCGOutOpBrcond2 outop_brcond2 = { + .base.static_constraint = C_O0_I4(r, r, rI, rI), + .out = tgen_brcond2, +}; + +static void tgen_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh) +{ + cond = tcg_out_cmp2(s, cond, al, ah, bl, const_bl, bh, const_bh); + finish_setcond(s, cond, ret, false); +} + +static const TCGOutOpSetcond2 outop_setcond2 = { + .base.static_constraint = C_O1_I4(r, r, r, rI, rI), + .out = tgen_setcond2, +}; + +static void tgen_extract2(TCGContext *s, TCGType type, TCGReg a0, + TCGReg a1, TCGReg a2, unsigned shr) +{ + /* We can do extract2 in 2 insns, vs the 3 required otherwise. */ + tgen_shli(s, TCG_TYPE_I32, TCG_REG_TMP, a2, 32 - shr); + tcg_out_dat_reg(s, COND_AL, ARITH_ORR, a0, TCG_REG_TMP, + a1, SHIFT_IMM_LSR(shr)); +} + +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_extract2, +}; + +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rn, ptrdiff_t offset) +{ + if (offset > 0xfff || offset < -0xfff) { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, offset); + tcg_out_ld8_r(s, COND_AL, rd, rn, TCG_REG_TMP); + } else { + tcg_out_ld8_12(s, COND_AL, rd, rn, offset); + } +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rn, ptrdiff_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, offset); + tcg_out_ld8s_r(s, COND_AL, rd, rn, TCG_REG_TMP); + } else { + tcg_out_ld8s_8(s, COND_AL, rd, rn, offset); + } +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rn, ptrdiff_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, offset); + tcg_out_ld16u_r(s, COND_AL, rd, rn, TCG_REG_TMP); + } else { + tcg_out_ld16u_8(s, COND_AL, rd, rn, offset); + } +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rn, ptrdiff_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, offset); + tcg_out_ld16s_r(s, COND_AL, rd, rn, TCG_REG_TMP); + } else { + tcg_out_ld16s_8(s, COND_AL, rd, rn, offset); + } +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +static void tgen_st8(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rn, ptrdiff_t offset) +{ + if (offset > 0xfff || offset < -0xfff) { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, offset); + tcg_out_st8_r(s, COND_AL, rd, rn, TCG_REG_TMP); + } else { + tcg_out_st8_12(s, COND_AL, rd, rn, offset); + } +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st8, +}; + +static void tgen_st16(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rn, ptrdiff_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, offset); + tcg_out_st16_r(s, COND_AL, rd, rn, TCG_REG_TMP); + } else { + tcg_out_st16_8(s, COND_AL, rd, rn, offset); + } +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st16, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tcg_out_st, +}; + +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) +{ + switch (op) { case INDEX_op_st_vec: return C_O0_I2(w, r); case INDEX_op_ld_vec: @@ -2228,7 +2687,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_bitsel_vec: return C_O1_I3(w, w, w, w); default: - g_assert_not_reached(); + return C_NotImplemented; } } @@ -2714,17 +3173,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) { @@ -2743,6 +3218,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; @@ -2931,12 +3407,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]); @@ -2962,6 +3436,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 311a9852095..4f9f877121e 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -26,10 +26,6 @@ #ifndef ARM_TCG_TARGET_H #define ARM_TCG_TARGET_H -extern int arm_arch; - -#define use_armv7_instructions (__ARM_ARCH >= 7 || arm_arch >= 7) - #define TCG_TARGET_INSN_UNIT_SIZE 4 #define MAX_CODE_GEN_BUFFER_SIZE UINT32_MAX @@ -74,85 +70,4 @@ typedef enum { #define TCG_TARGET_NB_REGS 32 -#ifdef __ARM_ARCH_EXT_IDIV__ -#define use_idiv_instructions 1 -#else -extern bool use_idiv_instructions; -#endif -#ifdef __ARM_NEON__ -#define use_neon_instructions 1 -#else -extern bool use_neon_instructions; -#endif - -/* used for function call generation */ -#define TCG_TARGET_STACK_ALIGN 8 -#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 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 0 /* and r0, r1, #0xff */ -#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 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 -#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_clz_i32 1 -#define TCG_TARGET_HAS_ctz_i32 use_armv7_instructions -#define TCG_TARGET_HAS_ctpop_i32 0 -#define TCG_TARGET_HAS_deposit_i32 use_armv7_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_qemu_st8_i32 0 - -#define TCG_TARGET_HAS_qemu_ldst_i128 0 - -#define TCG_TARGET_HAS_v64 use_neon_instructions -#define TCG_TARGET_HAS_v128 use_neon_instructions -#define TCG_TARGET_HAS_v256 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 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 1 -#define TCG_TARGET_HAS_roti_vec 0 -#define TCG_TARGET_HAS_rots_vec 0 -#define TCG_TARGET_HAS_rotv_vec 0 -#define TCG_TARGET_HAS_shi_vec 1 -#define TCG_TARGET_HAS_shs_vec 0 -#define TCG_TARGET_HAS_shv_vec 0 -#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 1 -#define TCG_TARGET_HAS_cmpsel_vec 0 - -#define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS - #endif diff --git a/tcg/arm/tcg-target.opc.h b/tcg/arm/tcg-target.opc.h deleted file mode 100644 index d38af9a8088..00000000000 --- a/tcg/arm/tcg-target.opc.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2019 Linaro - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * (at your option) any later version. - * - * See the COPYING file in the top-level directory 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. - */ - -DEF(arm_sli_vec, 1, 2, 1, IMPLVEC) -DEF(arm_sshl_vec, 1, 2, 0, IMPLVEC) -DEF(arm_ushl_vec, 1, 2, 0, IMPLVEC) diff --git a/accel/tcg/debuginfo.c b/tcg/debuginfo.c similarity index 98% rename from accel/tcg/debuginfo.c rename to tcg/debuginfo.c index 71c66d04d12..3753f7ef67c 100644 --- a/accel/tcg/debuginfo.c +++ b/tcg/debuginfo.c @@ -6,11 +6,10 @@ #include "qemu/osdep.h" #include "qemu/lockable.h" +#include "tcg/debuginfo.h" #include -#include "debuginfo.h" - static QemuMutex lock; static Dwfl *dwfl; static const Dwfl_Callbacks dwfl_callbacks = { diff --git a/tcg/i386/tcg-target-con-set.h b/tcg/i386/tcg-target-con-set.h index 7d00a7dde80..458d69c3c07 100644 --- a/tcg/i386/tcg-target-con-set.h +++ b/tcg/i386/tcg-target-con-set.h @@ -20,7 +20,7 @@ C_O0_I2(L, L) C_O0_I2(qi, r) C_O0_I2(re, r) C_O0_I2(ri, r) -C_O0_I2(r, re) +C_O0_I2(r, reT) C_O0_I2(s, L) C_O0_I2(x, r) C_O0_I3(L, L, L) @@ -34,7 +34,7 @@ C_O1_I1(r, r) C_O1_I1(x, r) C_O1_I1(x, x) C_O1_I2(q, 0, qi) -C_O1_I2(q, r, re) +C_O1_I2(q, r, reT) C_O1_I2(r, 0, ci) C_O1_I2(r, 0, r) C_O1_I2(r, 0, re) @@ -42,18 +42,19 @@ C_O1_I2(r, 0, reZ) C_O1_I2(r, 0, ri) C_O1_I2(r, 0, rI) C_O1_I2(r, L, L) +C_O1_I2(r, r, r) C_O1_I2(r, r, re) C_O1_I2(r, r, ri) -C_O1_I2(r, r, rI) +C_O1_I2(r, rO, re) C_O1_I2(x, x, x) 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_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 95a30e58cd2..dbedff1f541 100644 --- a/tcg/i386/tcg-target-con-str.h +++ b/tcg/i386/tcg-target-con-str.h @@ -20,7 +20,7 @@ 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('L', ALL_GENERAL_REGS & ~SOFTMMU_RESERVE_REGS) /* qemu_ld/st */ -REGS('s', ALL_BYTEL_REGS & ~SOFTMMU_RESERVE_REGS) /* qemu_st8_i32 data */ +REGS('s', ALL_BYTEL_REGS & ~SOFTMMU_RESERVE_REGS) /* qemu_st MO_8 data */ /* * Define constraint letters for constants: @@ -28,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-has.h b/tcg/i386/tcg-target-has.h new file mode 100644 index 00000000000..42647fabbd7 --- /dev/null +++ b/tcg/i386/tcg-target-has.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +#include "host/cpuinfo.h" + +#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 */ +#if TCG_TARGET_REG_BITS == 64 +/* Keep 32-bit values zero-extended in a register. */ +#define TCG_TARGET_HAS_extr_i64_i32 1 +#endif + +#define TCG_TARGET_HAS_qemu_ldst_i128 \ + (TCG_TARGET_REG_BITS == 64 && (cpuinfo & CPUINFO_ATOMIC_VMOVDQA)) + +#define TCG_TARGET_HAS_tst 1 + +/* We do not support older SSE systems, only beginning with AVX1. */ +#define TCG_TARGET_HAS_v64 have_avx1 +#define TCG_TARGET_HAS_v128 have_avx1 +#define TCG_TARGET_HAS_v256 have_avx2 + +#define TCG_TARGET_HAS_andc_vec 1 +#define TCG_TARGET_HAS_orc_vec have_avx512vl +#define TCG_TARGET_HAS_nand_vec have_avx512vl +#define TCG_TARGET_HAS_nor_vec have_avx512vl +#define TCG_TARGET_HAS_eqv_vec have_avx512vl +#define TCG_TARGET_HAS_not_vec have_avx512vl +#define TCG_TARGET_HAS_neg_vec 0 +#define TCG_TARGET_HAS_abs_vec 1 +#define TCG_TARGET_HAS_roti_vec have_avx512vl +#define TCG_TARGET_HAS_rots_vec 0 +#define TCG_TARGET_HAS_rotv_vec have_avx512vl +#define TCG_TARGET_HAS_shi_vec 1 +#define TCG_TARGET_HAS_shs_vec 1 +#define TCG_TARGET_HAS_shv_vec have_avx2 +#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 have_avx512vl +#define TCG_TARGET_HAS_cmpsel_vec 1 +#define TCG_TARGET_HAS_tst_vec have_avx512bw + +#define TCG_TARGET_deposit_valid(type, ofs, len) \ + (((ofs) == 0 && ((len) == 8 || (len) == 16)) || \ + (TCG_TARGET_REG_BITS == 32 && (ofs) == 8 && (len) == 8)) + +/* + * Check for the possibility of low byte/word extraction, high-byte extraction + * and zero-extending 32-bit right-shift. + * + * We cannot sign-extend from high byte to 64-bits without using the + * REX prefix that explicitly excludes access to the high-byte registers. + */ +static inline bool +tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) +{ + switch (ofs) { + case 0: + switch (len) { + case 8: + case 16: + return true; + case 32: + return type == TCG_TYPE_I64; + } + return false; + case 8: + return len == 8 && type == TCG_TYPE_I32; + } + return false; +} +#define TCG_TARGET_sextract_valid tcg_target_sextract_valid + +static inline bool +tcg_target_extract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (type == TCG_TYPE_I64 && ofs + len == 32) { + return true; + } + switch (ofs) { + case 0: + return len == 8 || len == 16; + case 8: + return len == 8; + } + return false; +} +#define TCG_TARGET_extract_valid tcg_target_extract_valid + +#endif diff --git a/tcg/i386/tcg-target-mo.h b/tcg/i386/tcg-target-mo.h new file mode 100644 index 00000000000..7567dc7248b --- /dev/null +++ b/tcg/i386/tcg-target-mo.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +/* + * This defines the natural memory order supported by this architecture + * before guarantees made by various barrier instructions. + * + * The x86 has a pretty strong memory ordering which only really + * allows for some stores to be re-ordered after loads. + */ +#define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) + +#endif diff --git a/tcg/i386/tcg-target-opc.h.inc b/tcg/i386/tcg-target-opc.h.inc new file mode 100644 index 00000000000..8cc0dbaeafe --- /dev/null +++ b/tcg/i386/tcg-target-opc.h.inc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 Linaro + * + * 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. + * + * 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. + */ + +DEF(x86_shufps_vec, 1, 2, 1, TCG_OPF_VECTOR) +DEF(x86_blend_vec, 1, 2, 1, TCG_OPF_VECTOR) +DEF(x86_packss_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(x86_packus_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(x86_psrldq_vec, 1, 1, 1, TCG_OPF_VECTOR) +DEF(x86_vperm2i128_vec, 1, 2, 1, TCG_OPF_VECTOR) +DEF(x86_punpckl_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(x86_punpckh_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(x86_vpshldi_vec, 1, 2, 1, TCG_OPF_VECTOR) +DEF(x86_vpshldv_vec, 1, 3, 0, TCG_OPF_VECTOR) +DEF(x86_vpshrdv_vec, 1, 3, 0, TCG_OPF_VECTOR) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 0c3d1e4cefd..088c6c9264b 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -22,8 +22,25 @@ * THE SOFTWARE. */ -#include "../tcg-ldst.c.inc" -#include "../tcg-pool.c.inc" +/* Used for function call generation. */ +#define TCG_TARGET_STACK_ALIGN 16 +#if defined(_WIN64) +#define TCG_TARGET_CALL_STACK_OFFSET 32 +#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 #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { @@ -132,6 +149,8 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #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 @@ -153,18 +172,9 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) # define ALL_VECTOR_REGS 0x00ff0000u # define ALL_BYTEL_REGS 0x0000000fu #endif -#ifdef CONFIG_SOFTMMU -# define SOFTMMU_RESERVE_REGS ((1 << TCG_REG_L0) | (1 << TCG_REG_L1)) -#else -# define SOFTMMU_RESERVE_REGS 0 -#endif +#define SOFTMMU_RESERVE_REGS \ + (tcg_use_softmmu ? (1 << TCG_REG_L0) | (1 << TCG_REG_L1) : 0) -/* For 64-bit, we always know that CMOV is available. */ -#if TCG_TARGET_REG_BITS == 64 -# define have_cmov true -#else -# define have_cmov (cpuinfo & CPUINFO_CMOV) -#endif #define have_bmi2 (cpuinfo & CPUINFO_BMI2) #define have_lzcnt (cpuinfo & CPUINFO_LZCNT) @@ -198,13 +208,15 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, 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; } 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 { @@ -217,10 +229,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; } @@ -247,6 +273,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) */ @@ -397,11 +424,25 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define OPC_SHLX (0xf7 | P_EXT38 | P_DATA16) #define OPC_SHRX (0xf7 | P_EXT38 | P_SIMDF2) #define OPC_SHRD_Ib (0xac | P_EXT) +#define OPC_STC (0xf9) +#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) @@ -410,6 +451,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) @@ -435,6 +480,14 @@ 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) @@ -443,6 +496,12 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #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. */ @@ -507,6 +566,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 @@ -643,7 +704,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; @@ -680,7 +741,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); @@ -689,13 +752,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<carry_live) { tgen_arithr(s, ARITH_XOR, ret, ret); return; } @@ -1087,7 +1168,7 @@ static inline void tcg_out_pushi(TCGContext *s, tcg_target_long val) } } -static inline void tcg_out_mb(TCGContext *s, TCGArg a0) +static void tcg_out_mb(TCGContext *s, unsigned a0) { /* Given the strength of x86 memory ordering, we only need care for store-load ordering. Experimentally, "lock orl $0,0(%esp)" is @@ -1249,16 +1330,31 @@ static inline void tcg_out_rolw_8(TCGContext *s, int reg) static void tcg_out_ext8u(TCGContext *s, TCGReg dest, TCGReg src) { - /* movzbl */ - tcg_debug_assert(src < 4 || TCG_TARGET_REG_BITS == 64); + if (TCG_TARGET_REG_BITS == 32 && src >= 4) { + tcg_out_mov(s, TCG_TYPE_I32, dest, src); + if (dest >= 4) { + tcg_out_modrm(s, OPC_ARITH_EvIz, ARITH_AND, dest); + tcg_out32(s, 0xff); + return; + } + src = dest; + } tcg_out_modrm(s, OPC_MOVZBL + P_REXB_RM, dest, src); } 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); + + if (TCG_TARGET_REG_BITS == 32 && src >= 4) { + tcg_out_mov(s, TCG_TYPE_I32, dest, src); + if (dest >= 4) { + tcg_out_shifti(s, SHIFT_SHL, dest, 24); + tcg_out_shifti(s, SHIFT_SAR, dest, 24); + return; + } + src = dest; + } tcg_out_modrm(s, OPC_MOVSBL + P_REXB_RM + rexw, dest, src); } @@ -1319,23 +1415,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); @@ -1354,6 +1468,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) { @@ -1421,118 +1546,184 @@ static void tcg_out_jxx(TCGContext *s, int opc, TCGLabel *l, bool small) } } -static void tcg_out_cmp(TCGContext *s, TCGArg arg1, TCGArg arg2, - int const_arg2, int rexw) +static void tcg_out_br(TCGContext *s, TCGLabel *l) { - if (const_arg2) { - if (arg2 == 0) { - /* test r, r */ + tcg_out_jxx(s, JCC_JMP, l, 0); +} + +static int tcg_out_cmp(TCGContext *s, TCGCond cond, TCGArg arg1, + TCGArg arg2, int const_arg2, int rexw) +{ + 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_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, rexw); - 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 == 32 -static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, - const int *const_args, bool small) +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, TCGReg arg2, TCGLabel *label) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_brcond(s, rexw, cond, arg1, arg2, false, label, false); +} + +static void tgen_brcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, tcg_target_long arg2, TCGLabel *label) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_brcond(s, rexw, cond, arg1, arg2, true, label, false); +} + +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, reT), + .out_rr = tgen_brcond, + .out_ri = tgen_brcondi, +}; + +static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGReg al, + TCGReg ah, TCGArg bl, bool blconst, + TCGArg bh, bool bhconst, + TCGLabel *label_this, bool small) { TCGLabel *label_next = gen_new_label(); - TCGLabel *label_this = arg_label(args[5]); - switch(args[4]) { + switch (cond) { case TCG_COND_EQ: - tcg_out_brcond(s, 0, TCG_COND_NE, args[0], args[2], const_args[2], - label_next, 1); - tcg_out_brcond(s, 0, 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), + al, bl, blconst, label_next, true); + tcg_out_brcond(s, 0, cond, ah, bh, bhconst, label_this, small); break; + case TCG_COND_NE: - tcg_out_brcond(s, 0, TCG_COND_NE, args[0], args[2], const_args[2], - label_this, small); - tcg_out_brcond(s, 0, TCG_COND_NE, args[1], args[3], const_args[3], - label_this, small); - break; - case TCG_COND_LT: - 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_brcond(s, 0, TCG_COND_LTU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_LE: - 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_brcond(s, 0, TCG_COND_LEU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_GT: - 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_brcond(s, 0, TCG_COND_GTU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_GE: - 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_brcond(s, 0, TCG_COND_GEU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_LTU: - 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_brcond(s, 0, TCG_COND_LTU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_LEU: - 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_brcond(s, 0, TCG_COND_LEU, args[0], args[2], const_args[2], - label_this, small); - break; - case TCG_COND_GTU: - 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_brcond(s, 0, TCG_COND_GTU, args[0], args[2], const_args[2], - label_this, small); + case TCG_COND_TSTNE: + tcg_out_brcond(s, 0, cond, al, bl, blconst, label_this, small); + tcg_out_brcond(s, 0, cond, ah, bh, bhconst, label_this, small); break; - case TCG_COND_GEU: - tcg_out_brcond(s, 0, TCG_COND_GTU, args[1], args[3], const_args[3], - label_this, small); + + default: + tcg_out_brcond(s, 0, tcg_high_cond(cond), + ah, bh, bhconst, label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond(s, 0, TCG_COND_GEU, args[0], args[2], const_args[2], - label_this, small); + tcg_out_brcond(s, 0, tcg_unsigned_cond(cond), + al, bl, blconst, label_this, small); break; - default: - g_assert_not_reached(); } tcg_out_label(s, label_next); } + +static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, + TCGReg ah, TCGArg bl, bool blconst, + TCGArg bh, bool bhconst, TCGLabel *l) +{ + tcg_out_brcond2(s, cond, al, ah, bl, blconst, bh, bhconst, l, false); +} + +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) #endif +static const TCGOutOpBrcond2 outop_brcond2 = { + .base.static_constraint = C_O0_I4(r, r, ri, ri), + .out = tgen_brcond2, +}; -static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond, - TCGArg dest, TCGArg arg1, TCGArg arg2, - int const_arg2, bool neg) +static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGArg arg2, + bool const_arg2, bool neg) { + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + int cmp_rexw = rexw; bool inv = false; bool cleared; + int jcc; switch (cond) { case TCG_COND_NE: @@ -1546,6 +1737,18 @@ static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond, } 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 */ @@ -1569,7 +1772,7 @@ static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond, * We can then use NEG or INC to produce the desired result. * This is always smaller than the SETCC expansion. */ - tcg_out_cmp(s, arg1, arg2, const_arg2, rexw); + 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); @@ -1591,7 +1794,7 @@ static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond, 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); + tcg_out_mov(s, type, dest, arg1); if (inv) { tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, dest); } @@ -1616,8 +1819,8 @@ static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond, cleared = true; } - tcg_out_cmp(s, arg1, arg2, const_arg2, rexw); - tcg_out_modrm(s, OPC_SETCC | tcg_cond_to_jcc[cond], 0, dest); + 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); @@ -1627,114 +1830,110 @@ static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond, } } -#if TCG_TARGET_REG_BITS == 32 -static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, - const int *const_args) +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, false, false); +} + +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, true, false); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(q, r, reT), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, false, true); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) { - TCGArg new_args[6]; - TCGLabel *label_true, *label_over; + tcg_out_setcond(s, type, cond, dest, arg1, arg2, true, true); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(q, r, reT), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; - memcpy(new_args, args+1, 5*sizeof(TCGArg)); +static void tgen_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh) +{ + TCGLabel *label_over = gen_new_label(); - if (args[0] == args[1] || args[0] == args[2] - || (!const_args[3] && args[0] == args[3]) - || (!const_args[4] && args[0] == args[4])) { - /* When the destination overlaps with one of the argument - registers, don't do anything tricky. */ - label_true = gen_new_label(); - label_over = gen_new_label(); + if (ret == al || ret == ah + || (!const_bl && ret == bl) + || (!const_bh && ret == bh)) { + /* + * When the destination overlaps with one of the argument + * registers, don't do anything tricky. + */ + TCGLabel *label_true = gen_new_label(); - new_args[5] = label_arg(label_true); - tcg_out_brcond2(s, new_args, const_args+1, 1); + tcg_out_brcond2(s, cond, al, ah, bl, const_bl, + bh, const_bh, label_true, true); - tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); + tcg_out_movi(s, TCG_TYPE_I32, ret, 0); tcg_out_jxx(s, JCC_JMP, label_over, 1); tcg_out_label(s, label_true); - tcg_out_movi(s, TCG_TYPE_I32, args[0], 1); - tcg_out_label(s, label_over); + tcg_out_movi(s, TCG_TYPE_I32, ret, 1); } else { - /* When the destination does not overlap one of the arguments, - clear the destination first, jump if cond false, and emit an - increment in the true case. This results in smaller code. */ - - tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); + /* + * When the destination does not overlap one of the arguments, + * clear the destination first, jump if cond false, and emit an + * increment in the true case. This results in smaller code. + */ + tcg_out_movi(s, TCG_TYPE_I32, ret, 0); - label_over = gen_new_label(); - new_args[4] = tcg_invert_cond(new_args[4]); - new_args[5] = label_arg(label_over); - tcg_out_brcond2(s, new_args, const_args+1, 1); + tcg_out_brcond2(s, tcg_invert_cond(cond), al, ah, bl, const_bl, + bh, const_bh, label_over, true); - tgen_arithi(s, ARITH_ADD, args[0], 1, 0); - tcg_out_label(s, label_over); + tgen_arithi(s, ARITH_ADD, ret, 1, 0); } + tcg_out_label(s, label_over); } + +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) #endif +static const TCGOutOpSetcond2 outop_setcond2 = { + .base.static_constraint = C_O1_I4(r, r, r, ri, ri), + .out = tgen_setcond2, +}; -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); - } -} - -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, rexw); - tcg_out_cmov(s, cond, rexw, dest, v1); + tcg_out_modrm(s, OPC_CMOVCC | jcc | rexw, dest, v1); } -static void tcg_out_ctz(TCGContext *s, int rexw, TCGReg dest, TCGReg arg1, - TCGArg arg2, bool const_a2) +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg vt, bool const_vt, + TCGArg vf, bool consf_vf) { - if (have_bmi1) { - tcg_out_modrm(s, OPC_TZCNT + rexw, dest, arg1); - if (const_a2) { - tcg_debug_assert(arg2 == (rexw ? 64 : 32)); - } else { - tcg_debug_assert(dest != arg2); - tcg_out_cmov(s, TCG_COND_LTU, 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); - } + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + int jcc = tcg_out_cmp(s, cond, c1, c2, const_c2, rexw); + tcg_out_cmov(s, jcc, rexw, dest, vt); } -static void tcg_out_clz(TCGContext *s, int rexw, TCGReg dest, TCGReg arg1, - TCGArg arg2, bool const_a2) -{ - if (have_lzcnt) { - tcg_out_modrm(s, OPC_LZCNT + rexw, dest, arg1); - if (const_a2) { - tcg_debug_assert(arg2 == (rexw ? 64 : 32)); - } else { - tcg_debug_assert(dest != arg2); - tcg_out_cmov(s, TCG_COND_LTU, rexw, dest, arg2); - } - } else { - tcg_debug_assert(!const_a2); - tcg_debug_assert(dest != arg1); - tcg_debug_assert(dest != arg2); - - /* Recall that the output of BSR is the index not the count. */ - tcg_out_modrm(s, OPC_BSR + rexw, dest, 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); - } -} +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, reT, r, 0), + .out = tgen_movcond, +}; static void tcg_out_branch(TCGContext *s, int call, const tcg_insn_unit *dest) { @@ -1797,23 +1996,6 @@ static void tcg_out_nopn(TCGContext *s, int n) tcg_out8(s, 0x90); } -/* Test register R vs immediate bits I, setting Z flag for EQ/NE. */ -static void __attribute__((unused)) -tcg_out_testi(TCGContext *s, TCGReg r, uint32_t i) -{ - /* - * This is used for testing alignment, so we can usually use testb. - * For i686, we have to use testl for %esi/%edi. - */ - if (i <= 0xff && (TCG_TARGET_REG_BITS == 64 || r < 4)) { - tcg_out_modrm(s, OPC_GRP3_Eb | P_REXB_RM, EXT3_TESTi, r); - tcg_out8(s, i); - } else { - tcg_out_modrm(s, OPC_GRP3_Ev, EXT3_TESTi, r); - tcg_out32(s, i); - } -} - typedef struct { TCGReg base; int index; @@ -1933,7 +2115,7 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) return true; } -#ifndef CONFIG_SOFTMMU +#ifdef CONFIG_USER_ONLY static HostAddress x86_guest_base = { .index = -1 }; @@ -1949,6 +2131,7 @@ static inline int setup_guest_base_seg(void) } return 0; } +#define setup_guest_base_seg setup_guest_base_seg #elif defined(__x86_64__) && \ (defined (__FreeBSD__) || defined (__FreeBSD_kernel__)) # include @@ -1959,13 +2142,14 @@ static inline int setup_guest_base_seg(void) } return 0; } +#define setup_guest_base_seg setup_guest_base_seg +#endif #else -static inline int setup_guest_base_seg(void) -{ - return 0; -} -#endif /* setup_guest_base_seg */ -#endif /* !SOFTMMU */ +# 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 @@ -1976,116 +2160,100 @@ static inline int setup_guest_base_seg(void) * 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) + TCGReg addr, MemOpIdx oi, bool is_ld) { TCGLabelQemuLdst *ldst = NULL; MemOp opc = get_memop(oi); MemOp s_bits = opc & MO_SIZE; unsigned a_mask; -#ifdef CONFIG_SOFTMMU - h->index = TCG_REG_L0; - h->ofs = 0; - h->seg = 0; -#else - *h = x86_guest_base; -#endif - h->base = addrlo; + if (tcg_use_softmmu) { + h->index = TCG_REG_L0; + h->ofs = 0; + h->seg = 0; + } else { + *h = x86_guest_base; + } + h->base = addr; h->aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, s_bits == MO_128); a_mask = (1 << h->aa.align) - 1; -#ifdef CONFIG_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_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; - 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) { + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addr_reg = addr; + + 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; 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)); + tcg_out_mov(s, tlbtype, TCG_REG_L0, addr); + tcg_out_shifti(s, SHIFT_SHR + tlbrexw, TCG_REG_L0, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - /* - * 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); + tcg_out_modrm_offset(s, OPC_AND_GvEv + trexw, TCG_REG_L0, TCG_AREG0, + fast_ofs + offsetof(CPUTLBDescFast, mask)); - /* 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); + tcg_out_modrm_offset(s, OPC_ADD_GvEv + hrexw, TCG_REG_L0, TCG_AREG0, + fast_ofs + offsetof(CPUTLBDescFast, table)); - /* 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 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, addr); + } else { + tcg_out_modrm_offset(s, OPC_LEA + trexw, TCG_REG_L1, + addr, s_mask - a_mask); + } + tlb_mask = TARGET_PAGE_MASK | a_mask; + tgen_arithi(s, ARITH_AND + trexw, TCG_REG_L1, tlb_mask, 0); - 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); + /* 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[1] = s->code_ptr; + ldst->label_ptr[0] = 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) { - ldst = new_ldst_label(s); + /* 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; + ldst->addr_reg = addr; - tcg_out_testi(s, addrlo, a_mask); /* jne slow_path */ - tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); + jcc = tcg_out_cmp(s, TCG_COND_TSTNE, addr, 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; } -#endif return ldst; } @@ -2228,9 +2396,10 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, } else { TCGLabel *l1 = gen_new_label(); TCGLabel *l2 = gen_new_label(); + int jcc; - tcg_out_testi(s, h.base, 15); - tcg_out_jxx(s, JCC_JNE, l1, true); + 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, @@ -2251,24 +2420,50 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, } } -static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) +{ + TCGLabelQemuLdst *ldst; + HostAddress h; + + ldst = prepare_host_addr(s, &h, addr, oi, true); + tcg_out_qemu_ld_direct(s, data, -1, h, type, get_memop(oi)); + + if (ldst) { + ldst->type = type; + ldst->datalo_reg = data; + ldst->datahi_reg = -1; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } +} + +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, L), + .out = tgen_qemu_ld, +}; + +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, true); - tcg_out_qemu_ld_direct(s, datalo, datahi, h, data_type, get_memop(oi)); + ldst = prepare_host_addr(s, &h, addr, oi, true); + tcg_out_qemu_ld_direct(s, datalo, datahi, h, type, get_memop(oi)); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = datalo; ldst->datahi_reg = datahi; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = C_O2_I1(r, r, L), + .out = tgen_qemu_ld2, +}; + static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, HostAddress h, MemOp memop) { @@ -2276,7 +2471,7 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, 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) { @@ -2287,7 +2482,7 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, switch (memop & MO_SIZE) { case MO_8: - /* This is handled with constraints on INDEX_op_qemu_st8_i32. */ + /* This is handled with constraints in cset_qemu_st(). */ tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || datalo < 4); tcg_out_modrm_sib_offset(s, OPC_MOVB_EvGv + P_REXB_R + h.seg, datalo, h.base, h.index, 0, h.ofs); @@ -2356,9 +2551,10 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, } else { TCGLabel *l1 = gen_new_label(); TCGLabel *l2 = gen_new_label(); + int jcc; - tcg_out_testi(s, h.base, 15); - tcg_out_jxx(s, JCC_JNE, l1, true); + 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, @@ -2378,24 +2574,58 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, } } -static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) +{ + TCGLabelQemuLdst *ldst; + HostAddress h; + + ldst = prepare_host_addr(s, &h, addr, oi, false); + tcg_out_qemu_st_direct(s, data, -1, h, get_memop(oi)); + + if (ldst) { + ldst->type = type; + ldst->datalo_reg = data; + ldst->datahi_reg = -1; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } +} + +static TCGConstraintSetIndex cset_qemu_st(TCGType type, unsigned flags) +{ + return flags == MO_8 ? C_O0_I2(s, L) : C_O0_I2(L, L); +} + +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = + TCG_TARGET_REG_BITS == 32 ? C_Dynamic : C_O0_I2(L, L), + .base.dynamic_constraint = + TCG_TARGET_REG_BITS == 32 ? cset_qemu_st : NULL, + .out = tgen_qemu_st, +}; + +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, false); + ldst = prepare_host_addr(s, &h, addr, oi, false); tcg_out_qemu_st_direct(s, datalo, datahi, h, get_memop(oi)); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = datalo; ldst->datahi_reg = datahi; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = C_O0_I3(L, L, L), + .out = tgen_qemu_st2, +}; + static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) { /* Reuse the zeroing that exists for goto_ptr. */ @@ -2423,6 +2653,12 @@ static void tcg_out_goto_tb(TCGContext *s, int which) set_jmp_reset_offset(s, which); } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + /* Jump to the given host address (could be epilogue) */ + tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, a0); +} + void tb_target_set_jmp_target(const TranslationBlock *tb, int n, uintptr_t jmp_rx, uintptr_t jmp_rw) { @@ -2432,478 +2668,1144 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, /* no need to flush icache explicitly */ } -static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) { - TCGArg a0, a1, a2; - int c, const_a2, vexop, rexw = 0; + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; -#if TCG_TARGET_REG_BITS == 64 -# define OP_32_64(x) \ - case glue(glue(INDEX_op_, x), _i64): \ - rexw = P_REXW; /* FALLTHRU */ \ - case glue(glue(INDEX_op_, x), _i32) -#else -# define OP_32_64(x) \ - case glue(glue(INDEX_op_, x), _i32) -#endif + if (a0 == a1) { + tgen_arithr(s, ARITH_ADD + rexw, a0, a2); + } else if (a0 == a2) { + tgen_arithr(s, ARITH_ADD + rexw, a0, a1); + } else { + tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, a2, 0, 0); + } +} - /* Hoist the loads of the most common arguments. */ - a0 = args[0]; - a1 = args[1]; - a2 = args[2]; - const_a2 = const_args[2]; +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + if (a0 == a1) { + tgen_arithi(s, ARITH_ADD + rexw, a0, a2, false); + } else { + tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, -1, 0, a2); + } +} + +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, re), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; + +static void tgen_addco(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithr(s, ARITH_ADD + rexw, a0, a2); +} + +static void tgen_addco_imm(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithi(s, ARITH_ADD + rexw, a0, a2, true); +} + +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_O1_I2(r, 0, re), + .out_rrr = tgen_addco, + .out_rri = tgen_addco_imm, +}; + +static void tgen_addcio(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithr(s, ARITH_ADC + rexw, a0, a2); +} + +static void tgen_addcio_imm(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithi(s, ARITH_ADC + rexw, a0, a2, true); +} + +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_O1_I2(r, 0, re), + .out_rrr = tgen_addcio, + .out_rri = tgen_addcio_imm, +}; + +static void tgen_addci_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + /* Because "0O" is not a valid constraint, we must match ourselves. */ + if (a0 == a2) { + tgen_addcio(s, type, a0, a0, a1); + } else { + tcg_out_mov(s, type, a0, a1); + tgen_addcio(s, type, a0, a0, a2); + } +} + +static void tgen_addci_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_mov(s, type, a0, a1); + tgen_addcio_imm(s, type, a0, a0, a2); +} + +static void tgen_addci_rir(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tgen_addci_rri(s, type, a0, a2, a1); +} + +static void tgen_addci_rii(TCGContext *s, TCGType type, TCGReg a0, + tcg_target_long a1, tcg_target_long a2) +{ + if (a2 == 0) { + /* Implement 0 + 0 + C with -(x - x - c). */ + tgen_arithr(s, ARITH_SBB, a0, a0); + tcg_out_modrm(s, OPC_GRP3_Ev, EXT3_NEG, a0); + } else { + tcg_out_movi(s, type, a0, a2); + tgen_addcio_imm(s, type, a0, a0, a1); + } +} + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_O1_I2(r, rO, re), + .out_rrr = tgen_addci_rrr, + .out_rri = tgen_addci_rri, + .out_rir = tgen_addci_rir, + .out_rii = tgen_addci_rii, +}; + +static void tcg_out_set_carry(TCGContext *s) +{ + tcg_out8(s, OPC_STC); +} + +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithr(s, ARITH_AND + rexw, a0, a2); +} + +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithi(s, ARITH_AND + rexw, a0, a2, false); +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, 0, reZ), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; + +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_vex_modrm(s, OPC_ANDN + rexw, a0, a2, a1); +} + +static TCGConstraintSetIndex cset_andc(TCGType type, unsigned flags) +{ + return have_bmi1 ? C_O1_I2(r, r, r) : C_NotImplemented; +} + +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_andc, + .out_rrr = tgen_andc, +}; + +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + int jcc; + + if (have_lzcnt) { + tcg_out_modrm(s, OPC_LZCNT + rexw, a0, a1); + jcc = JCC_JB; + } else { + /* Recall that the output of BSR is the index not the count. */ + tcg_out_modrm(s, OPC_BSR + rexw, a0, a1); + tgen_arithi(s, ARITH_XOR + rexw, a0, rexw ? 63 : 31, 0); + + /* Since we have destroyed the flags from BSR, we have to re-test. */ + jcc = tcg_out_cmp(s, TCG_COND_EQ, a1, 0, 1, rexw); + } + tcg_out_cmov(s, jcc, rexw, a0, a2); +} + +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_LZCNT + rexw, a0, a1); +} + +static TCGConstraintSetIndex cset_clz(TCGType type, unsigned flags) +{ + return have_lzcnt ? C_N1_I2(r, r, rW) : C_N1_I2(r, r, r); +} + +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_clz, + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; + +static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_POPCNT + rexw, a0, a1); +} + +static TCGConstraintSetIndex cset_ctpop(TCGType type, unsigned flags) +{ + return have_popcnt ? C_O1_I1(r, r) : C_NotImplemented; +} + +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctpop, + .out_rr = tgen_ctpop, +}; + +static void tgen_ctz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + int jcc; + + if (have_bmi1) { + tcg_out_modrm(s, OPC_TZCNT + rexw, a0, a1); + jcc = JCC_JB; + } else { + tcg_out_modrm(s, OPC_BSF + rexw, a0, a1); + jcc = JCC_JE; + } + tcg_out_cmov(s, jcc, rexw, a0, a2); +} + +static void tgen_ctzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_TZCNT + rexw, a0, a1); +} + +static TCGConstraintSetIndex cset_ctz(TCGType type, unsigned flags) +{ + return have_bmi1 ? C_N1_I2(r, r, rW) : C_N1_I2(r, r, r); +} + +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctz, + .out_rrr = tgen_ctz, + .out_rri = tgen_ctzi, +}; + +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_divs2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a4) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IDIV, a4); +} + +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_O2_I3(a, d, 0, 1, r), + .out_rr01r = tgen_divs2, +}; + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_divu2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a4) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_DIV, a4); +} + +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_O2_I3(a, d, 0, 1, r), + .out_rr01r = tgen_divu2, +}; + +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_NotImplemented, +}; - switch (opc) { - case INDEX_op_goto_ptr: - /* jmp to the given host address (could be epilogue) */ - tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, a0); - break; - case INDEX_op_br: - tcg_out_jxx(s, JCC_JMP, arg_label(a0), 0); - break; - OP_32_64(ld8u): - /* Note that we can ignore REXW for the zero-extend to 64-bit. */ - tcg_out_modrm_offset(s, OPC_MOVZBL, a0, a1, a2); - break; - OP_32_64(ld8s): - tcg_out_modrm_offset(s, OPC_MOVSBL + rexw, a0, a1, a2); - break; - OP_32_64(ld16u): - /* Note that we can ignore REXW for the zero-extend to 64-bit. */ - tcg_out_modrm_offset(s, OPC_MOVZWL, a0, a1, a2); - break; - OP_32_64(ld16s): - tcg_out_modrm_offset(s, OPC_MOVSWL + rexw, a0, a1, a2); - break; #if TCG_TARGET_REG_BITS == 64 - case INDEX_op_ld32u_i64: -#endif - case INDEX_op_ld_i32: - tcg_out_ld(s, TCG_TYPE_I32, a0, a1, a2); - break; +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_shifti(s, SHIFT_SHR + P_REXW, a0, 32); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, 0), + .out_rr = tgen_extrh_i64_i32, +}; +#endif /* TCG_TARGET_REG_BITS == 64 */ + +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_IMUL_GvEv + rexw, a0, a2); +} - OP_32_64(st8): - if (const_args[0]) { - tcg_out_modrm_offset(s, OPC_MOVB_EvIz, 0, a1, a2); - tcg_out8(s, a0); +static void tgen_muli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + if (a2 == (int8_t)a2) { + tcg_out_modrm(s, OPC_IMUL_GvEvIb + rexw, a0, a0); + tcg_out8(s, a2); + } else { + tcg_out_modrm(s, OPC_IMUL_GvEvIz + rexw, a0, a0); + tcg_out32(s, a2); + } +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, 0, re), + .out_rrr = tgen_mul, + .out_rri = tgen_muli, +}; + +static void tgen_muls2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IMUL, a3); +} + +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_O2_I2(a, d, a, r), + .out_rrrr = tgen_muls2, +}; + +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_mulu2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_MUL, a3); +} + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_O2_I2(a, d, a, r), + .out_rrrr = tgen_mulu2, +}; + +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithr(s, ARITH_OR + rexw, a0, a2); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithi(s, ARITH_OR + rexw, a0, a2, false); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, 0, re), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_rotl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_SHIFT_cl + rexw, SHIFT_ROL, a0); +} + +static void tgen_rotli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_shifti(s, SHIFT_ROL + rexw, a0, a2); +} + +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_O1_I2(r, 0, ci), + .out_rrr = tgen_rotl, + .out_rri = tgen_rotli, +}; + +static void tgen_rotr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_SHIFT_cl + rexw, SHIFT_ROR, a0); +} + +static void tgen_rotri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_shifti(s, SHIFT_ROR + rexw, a0, a2); +} + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_O1_I2(r, 0, ci), + .out_rrr = tgen_rotr, + .out_rri = tgen_rotri, +}; + +static TCGConstraintSetIndex cset_shift(TCGType type, unsigned flags) +{ + return have_bmi2 ? C_O1_I2(r, r, ri) : C_O1_I2(r, 0, ci); +} + +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + if (have_bmi2) { + tcg_out_vex_modrm(s, OPC_SARX + rexw, a0, a2, a1); + } else { + tcg_out_modrm(s, OPC_SHIFT_cl + rexw, SHIFT_SAR, a0); + } +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + tcg_out_mov(s, type, a0, a1); + tcg_out_shifti(s, SHIFT_SAR + rexw, a0, a2); +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_shift, + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + if (have_bmi2) { + tcg_out_vex_modrm(s, OPC_SHLX + rexw, a0, a2, a1); + } else { + tcg_out_modrm(s, OPC_SHIFT_cl + rexw, SHIFT_SHL, a0); + } +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + /* For small constant 3-operand shift, use LEA. */ + if (a0 != a1 && a2 >= 1 && a2 <= 3) { + if (a2 == 1) { + /* shl $1,a1,a0 -> lea (a1,a1),a0 */ + tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, a1, 0, 0); } else { - tcg_out_modrm_offset(s, OPC_MOVB_EvGv | P_REXB_R, a0, a1, a2); + /* shl $n,a1,a0 -> lea 0(,a1,n),a0 */ + tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, -1, a1, a2, 0); } - break; - OP_32_64(st16): - if (const_args[0]) { - tcg_out_modrm_offset(s, OPC_MOVL_EvIz | P_DATA16, 0, a1, a2); - tcg_out16(s, a0); + return; + } + tcg_out_mov(s, type, a0, a1); + tcg_out_shifti(s, SHIFT_SHL + rexw, a0, a2); +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_shift, + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + if (have_bmi2) { + tcg_out_vex_modrm(s, OPC_SHRX + rexw, a0, a2, a1); + } else { + tcg_out_modrm(s, OPC_SHIFT_cl + rexw, SHIFT_SHR, a0); + } +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + tcg_out_mov(s, type, a0, a1); + tcg_out_shifti(s, SHIFT_SHR + rexw, a0, a2); +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_shift, + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithr(s, ARITH_SUB + rexw, a0, a2); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, 0, r), + .out_rrr = tgen_sub, +}; + +static void tgen_subbo_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithi(s, ARITH_SUB + rexw, a0, a2, 1); +} + +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_O1_I2(r, 0, re), + .out_rrr = tgen_sub, + .out_rri = tgen_subbo_rri, +}; + +static void tgen_subbio_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithr(s, ARITH_SBB + rexw, a0, a2); +} + +static void tgen_subbio_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithi(s, ARITH_SBB + rexw, a0, a2, 1); +} + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_O1_I2(r, 0, re), + .out_rrr = tgen_subbio_rrr, + .out_rri = tgen_subbio_rri, +}; + +#define outop_subbi outop_subbio + +static void tcg_out_set_borrow(TCGContext *s) +{ + tcg_out8(s, OPC_STC); +} + +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithr(s, ARITH_XOR + rexw, a0, a2); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithi(s, ARITH_XOR + rexw, a0, a2, false); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, 0, re), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + if (flags & TCG_BSWAP_OS) { + /* Output must be sign-extended. */ + if (rexw) { + tcg_out_bswap64(s, a0); + tcg_out_shifti(s, SHIFT_SAR + rexw, a0, 48); } else { - tcg_out_modrm_offset(s, OPC_MOVL_EvGv | P_DATA16, a0, a1, a2); + tcg_out_bswap32(s, a0); + tcg_out_shifti(s, SHIFT_SAR, a0, 16); } - break; + } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + /* Output must be zero-extended, but input isn't. */ + tcg_out_bswap32(s, a0); + tcg_out_shifti(s, SHIFT_SHR, a0, 16); + } else { + tcg_out_rolw_8(s, a0); + } +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, 0), + .out_rr = tgen_bswap16, +}; + +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_bswap32(s, a0); + if (flags & TCG_BSWAP_OS) { + tcg_out_ext32s(s, a0, a0); + } +} + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, 0), + .out_rr = tgen_bswap32, +}; + #if TCG_TARGET_REG_BITS == 64 - case INDEX_op_st32_i64: +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_bswap64(s, a0); +} + +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_O1_I1(r, 0), + .out_rr = tgen_bswap64, +}; #endif - case INDEX_op_st_i32: - if (const_args[0]) { - tcg_out_modrm_offset(s, OPC_MOVL_EvIz, 0, a1, a2); - tcg_out32(s, a0); - } else { - tcg_out_st(s, TCG_TYPE_I32, a0, a1, a2); - } - break; - OP_32_64(add): - /* For 3-operand addition, use LEA. */ - if (a0 != a1) { - TCGArg c3 = 0; - if (const_a2) { - c3 = a2, a2 = -1; - } else if (a0 == a2) { - /* Watch out for dest = src + dest, since we've removed - the matching constraint on the add. */ - tgen_arithr(s, ARITH_ADD + rexw, a0, a1); - break; - } +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NEG, a0); +} - tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, a2, 0, c3); - break; - } - c = ARITH_ADD; - goto gen_arith; - OP_32_64(sub): - c = ARITH_SUB; - goto gen_arith; - OP_32_64(and): - c = ARITH_AND; - goto gen_arith; - OP_32_64(or): - c = ARITH_OR; - goto gen_arith; - OP_32_64(xor): - c = ARITH_XOR; - goto gen_arith; - gen_arith: - if (const_a2) { - tgen_arithi(s, c + rexw, a0, a2, 0); - } else { - tgen_arithr(s, c + rexw, a0, a2); - } - break; +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, 0), + .out_rr = tgen_neg, +}; - OP_32_64(andc): - if (const_a2) { - tcg_out_mov(s, rexw ? TCG_TYPE_I64 : TCG_TYPE_I32, a0, a1); - tgen_arithi(s, ARITH_AND + rexw, a0, ~a2, 0); - } else { - tcg_out_vex_modrm(s, OPC_ANDN + rexw, a0, a2, a1); - } - break; +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, a0); +} - OP_32_64(mul): - if (const_a2) { - int32_t val; - val = a2; - if (val == (int8_t)val) { - tcg_out_modrm(s, OPC_IMUL_GvEvIb + rexw, a0, a0); - tcg_out8(s, val); - } else { - tcg_out_modrm(s, OPC_IMUL_GvEvIz + rexw, a0, a0); - tcg_out32(s, val); - } +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, 0), + .out_rr = tgen_not, +}; + +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) +{ + if (ofs == 0 && len == 8) { + tcg_out_modrm(s, OPC_MOVB_EvGv | P_REXB_R | P_REXB_RM, a2, a0); + } else if (ofs == 0 && len == 16) { + tcg_out_modrm(s, OPC_MOVL_EvGv | P_DATA16, a2, a0); + } else if (TCG_TARGET_REG_BITS == 32 && ofs == 8 && len == 8) { + tcg_out_modrm(s, OPC_MOVB_EvGv, a2, a0 + 4); + } else { + g_assert_not_reached(); + } +} + +static void tgen_depositi(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + tcg_target_long a2, unsigned ofs, unsigned len) +{ + if (ofs == 0 && len == 8) { + tcg_out_opc(s, OPC_MOVB_Ib | P_REXB_RM | LOWREGMASK(a0), 0, a0, 0); + tcg_out8(s, a2); + } else if (ofs == 0 && len == 16) { + tcg_out_opc(s, OPC_MOVL_Iv | P_DATA16 | LOWREGMASK(a0), 0, a0, 0); + tcg_out16(s, a2); + } else if (TCG_TARGET_REG_BITS == 32 && ofs == 8 && len == 8) { + tcg_out8(s, OPC_MOVB_Ib + a0 + 4); + tcg_out8(s, a2); + } else { + g_assert_not_reached(); + } +} + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(q, 0, qi), + .out_rrr = tgen_deposit, + .out_rri = tgen_depositi, +}; + +static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8u(s, a0, a1); + return; + case 16: + tcg_out_ext16u(s, a0, a1); + return; + case 32: + tcg_out_ext32u(s, a0, a1); + return; + } + } else if (TCG_TARGET_REG_BITS == 64 && ofs + len == 32) { + /* This is a 32-bit zero-extending right shift. */ + tcg_out_mov(s, TCG_TYPE_I32, a0, a1); + tcg_out_shifti(s, SHIFT_SHR, a0, ofs); + return; + } else if (ofs == 8 && len == 8) { + /* + * On the off-chance that we can use the high-byte registers. + * Otherwise we emit the same ext16 + shift pattern that we + * would have gotten from the normal tcg-op.c expansion. + */ + if (a1 < 4 && (TCG_TARGET_REG_BITS == 32 || a0 < 8)) { + tcg_out_modrm(s, OPC_MOVZBL, a0, a1 + 4); } else { - tcg_out_modrm(s, OPC_IMUL_GvEv + rexw, a0, a2); + tcg_out_ext16u(s, a0, a1); + tcg_out_shifti(s, SHIFT_SHR, a0, 8); } - break; + return; + } + g_assert_not_reached(); +} - OP_32_64(div2): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IDIV, args[4]); - break; - OP_32_64(divu2): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_DIV, args[4]); - break; +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; - OP_32_64(shl): - /* For small constant 3-operand shift, use LEA. */ - if (const_a2 && a0 != a1 && (a2 - 1) < 3) { - if (a2 - 1 == 0) { - /* shl $1,a1,a0 -> lea (a1,a1),a0 */ - tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, a1, 0, 0); - } else { - /* shl $n,a1,a0 -> lea 0(,a1,n),a0 */ - tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, -1, a1, a2, 0); - } - break; - } - c = SHIFT_SHL; - vexop = OPC_SHLX; - goto gen_shift_maybe_vex; - OP_32_64(shr): - c = SHIFT_SHR; - vexop = OPC_SHRX; - goto gen_shift_maybe_vex; - OP_32_64(sar): - c = SHIFT_SAR; - vexop = OPC_SARX; - goto gen_shift_maybe_vex; - OP_32_64(rotl): - c = SHIFT_ROL; - goto gen_shift; - OP_32_64(rotr): - c = SHIFT_ROR; - goto gen_shift; - gen_shift_maybe_vex: - if (have_bmi2) { - if (!const_a2) { - tcg_out_vex_modrm(s, vexop + rexw, a0, a2, a1); - break; - } - tcg_out_mov(s, rexw ? TCG_TYPE_I64 : TCG_TYPE_I32, a0, a1); +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8s(s, type, a0, a1); + return; + case 16: + tcg_out_ext16s(s, type, a0, a1); + return; + case 32: + tcg_out_ext32s(s, a0, a1); + return; } - /* FALLTHRU */ - gen_shift: - if (const_a2) { - tcg_out_shifti(s, c + rexw, a0, a2); + } else if (ofs == 8 && len == 8) { + if (type == TCG_TYPE_I32 && a1 < 4 && a0 < 8) { + tcg_out_modrm(s, OPC_MOVSBL, a0, a1 + 4); } else { - tcg_out_modrm(s, OPC_SHIFT_cl + rexw, c, a0); + tcg_out_ext16s(s, type, a0, a1); + tgen_sari(s, type, a0, a0, 8); } - break; + return; + } + g_assert_not_reached(); +} - OP_32_64(ctz): - tcg_out_ctz(s, rexw, args[0], args[1], args[2], const_args[2]); - break; - OP_32_64(clz): - tcg_out_clz(s, rexw, args[0], args[1], args[2], const_args[2]); - break; - OP_32_64(ctpop): - tcg_out_modrm(s, OPC_POPCNT + rexw, a0, a1); - break; +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; + +static void tgen_extract2(TCGContext *s, TCGType type, TCGReg a0, + TCGReg a1, TCGReg a2, unsigned shr) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + /* Note that SHRD outputs to the r/m operand. */ + tcg_out_modrm(s, OPC_SHRD_Ib + rexw, a2, a0); + tcg_out8(s, shr); +} + +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_O1_I2(r, 0, r), + .out_rrr = tgen_extract2, +}; + +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVZBL, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm_offset(s, OPC_MOVSBL + rexw, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVZWL, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm_offset(s, OPC_MOVSWL + rexw, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +#if TCG_TARGET_REG_BITS == 64 +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVL_GvEv, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVSLQ, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; +#endif + +static void tgen_st8_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVB_EvGv | P_REXB_R, data, base, offset); +} + +static void tgen_st8_i(TCGContext *s, TCGType type, tcg_target_long data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVB_EvIz, 0, base, offset); + tcg_out8(s, data); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(qi, r), + .out_r = tgen_st8_r, + .out_i = tgen_st8_i, +}; + +static void tgen_st16_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVL_EvGv | P_DATA16, data, base, offset); +} + +static void tgen_st16_i(TCGContext *s, TCGType type, tcg_target_long data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVL_EvIz | P_DATA16, 0, base, offset); + tcg_out16(s, data); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(ri, r), + .out_r = tgen_st16_r, + .out_i = tgen_st16_i, +}; + +static void tgen_st_i(TCGContext *s, TCGType type, tcg_target_long data, + TCGReg base, ptrdiff_t offset) +{ + bool ok = tcg_out_sti(s, type, data, base, offset); + tcg_debug_assert(ok); +} + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(re, r), + .out_r = tcg_out_st, + .out_i = tgen_st_i, +}; + +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); - OP_32_64(brcond): - tcg_out_brcond(s, rexw, a2, a0, a1, const_args[1], - arg_label(args[3]), 0); - break; - OP_32_64(setcond): - tcg_out_setcond(s, rexw, args[3], a0, a1, a2, const_a2, false); - break; - 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; + tcg_out_vex_modrm_type(s, op, TCG_TMP_VEC, v1, v2, type); + v2 = TCG_TMP_VEC; + cond = TCG_COND_EQ; + } - OP_32_64(bswap16): - if (a2 & TCG_BSWAP_OS) { - /* Output must be sign-extended. */ - if (rexw) { - tcg_out_bswap64(s, a0); - tcg_out_shifti(s, SHIFT_SAR + rexw, a0, 48); - } else { - tcg_out_bswap32(s, a0); - tcg_out_shifti(s, SHIFT_SAR, a0, 16); - } - } else if ((a2 & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - /* Output must be zero-extended, but input isn't. */ - tcg_out_bswap32(s, a0); - tcg_out_shifti(s, SHIFT_SHR, a0, 16); - } else { - tcg_out_rolw_8(s, a0); - } + switch (cond) { + case TCG_COND_EQ: + tcg_out_vex_modrm_type(s, cmpeq_insn[vece], v0, v1, v2, type); break; - OP_32_64(bswap32): - tcg_out_bswap32(s, a0); - if (rexw && (a2 & TCG_BSWAP_OS)) { - tcg_out_ext32s(s, a0, a0); - } + 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; +} - OP_32_64(neg): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NEG, a0); - break; - OP_32_64(not): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, a0); - break; +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 + }; - 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; - 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; - 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; - 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; + 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, + }; - 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_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); - } + switch (cond) { + case TCG_COND_TSTNE: + tcg_out_vex_modrm_type(s, testm_insn[vece], /* k1 */ 1, v1, v2, type); break; - 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); - } + case TCG_COND_TSTEQ: + tcg_out_vex_modrm_type(s, testnm_insn[vece], /* k1 */ 1, v1, v2, type); break; - case INDEX_op_qemu_st_a32_i128: - case INDEX_op_qemu_st_a64_i128: - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); - tcg_out_qemu_st(s, a0, a1, a2, -1, args[3], TCG_TYPE_I128); + 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; + } +} - OP_32_64(mulu2): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_MUL, args[3]); - break; - OP_32_64(muls2): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IMUL, args[3]); - break; - OP_32_64(add2): - if (const_args[4]) { - tgen_arithi(s, ARITH_ADD + rexw, a0, args[4], 1); - } else { - tgen_arithr(s, ARITH_ADD + rexw, a0, args[4]); - } - if (const_args[5]) { - tgen_arithi(s, ARITH_ADC + rexw, a1, args[5], 1); - } else { - tgen_arithr(s, ARITH_ADC + rexw, a1, args[5]); - } - break; - OP_32_64(sub2): - if (const_args[4]) { - tgen_arithi(s, ARITH_SUB + rexw, a0, args[4], 1); - } else { - tgen_arithr(s, ARITH_SUB + rexw, a0, args[4]); - } - if (const_args[5]) { - tgen_arithi(s, ARITH_SBB + rexw, a1, args[5], 1); - } else { - tgen_arithr(s, ARITH_SBB + rexw, a1, args[5]); - } - 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); +} -#if TCG_TARGET_REG_BITS == 32 - case INDEX_op_brcond2_i32: - tcg_out_brcond2(s, args, const_args, 0); - break; - case INDEX_op_setcond2_i32: - tcg_out_setcond2(s, args, const_args); - break; -#else /* TCG_TARGET_REG_BITS == 64 */ - case INDEX_op_ld32s_i64: - tcg_out_modrm_offset(s, OPC_MOVSLQ, a0, a1, a2); - break; - case INDEX_op_ld_i64: - tcg_out_ld(s, TCG_TYPE_I64, a0, a1, a2); - break; - case INDEX_op_st_i64: - if (const_args[0]) { - tcg_out_modrm_offset(s, OPC_MOVL_EvIz | P_REXW, 0, a1, a2); - tcg_out32(s, a0); - } else { - tcg_out_st(s, TCG_TYPE_I64, a0, a1, a2); - } - break; +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; + } - case INDEX_op_bswap64_i64: - tcg_out_bswap64(s, a0); - break; - case INDEX_op_extrh_i64_i32: - tcg_out_shifti(s, SHIFT_SHR + P_REXW, a0, 32); - break; -#endif + 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); + } +} - OP_32_64(deposit): - if (args[3] == 0 && args[4] == 8) { - /* load bits 0..7 */ - 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 */ - 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 */ - 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 { - g_assert_not_reached(); - } - break; +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; - case INDEX_op_extract_i64: - if (a2 + args[3] == 32) { - /* This is a 32-bit zero-extending right shift. */ - tcg_out_mov(s, TCG_TYPE_I32, a0, a1); - tcg_out_shifti(s, SHIFT_SHR, a0, a2); - break; - } - /* FALLTHRU */ - case INDEX_op_extract_i32: - /* On the off-chance that we can use the high-byte registers. - Otherwise we emit the same ext16 + shift pattern that we - would have gotten from the normal tcg-op.c expansion. */ - tcg_debug_assert(a2 == 8 && args[3] == 8); - if (a1 < 4 && a0 < 8) { - tcg_out_modrm(s, OPC_MOVZBL, a0, a1 + 4); - } else { - tcg_out_ext16u(s, a0, a1); - tcg_out_shifti(s, SHIFT_SHR, a0, 8); - } - break; + /* Swap to place constant in V4 to take advantage of zero-masking. */ + if (!v3) { + z = true; + v3 = v4; + cond = tcg_invert_cond(cond); + } - case INDEX_op_sextract_i32: - /* We don't implement sextract_i64, as we cannot sign-extend to - 64-bits without using the REX prefix that explicitly excludes - access to the high-byte registers. */ - tcg_debug_assert(a2 == 8 && args[3] == 8); - if (a1 < 4 && a0 < 8) { - tcg_out_modrm(s, OPC_MOVSBL, a0, a1 + 4); - } else { - tcg_out_ext16s(s, TCG_TYPE_I32, a0, a1); - tcg_out_shifti(s, SHIFT_SAR, a0, 8); - } - break; + 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); +} - OP_32_64(extract2): - /* Note that SHRD outputs to the r/m operand. */ - tcg_out_modrm(s, OPC_SHRD_Ib + rexw, a2, a0); - tcg_out8(s, args[3]); - break; +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; - case INDEX_op_mb: - tcg_out_mb(s, a0); - 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(); + if (vece <= MO_16 ? have_avx512bw : have_avx512vl) { + tcg_out_cmpsel_vec_k1(s, type, vece, v0, c1, c2, v3, v4, cond); + return; } -#undef OP_32_64 + 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, @@ -2935,12 +3837,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 }; @@ -2959,12 +3855,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 }; @@ -3116,29 +4006,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: @@ -3166,10 +4048,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; @@ -3246,22 +4125,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); @@ -3274,196 +4141,10 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, } } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: - return C_O1_I1(r, r); - - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - return C_O0_I2(qi, r); - - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - return C_O0_I2(ri, r); - - case INDEX_op_st_i64: - return C_O0_I2(re, r); - - case INDEX_op_add_i32: - case INDEX_op_add_i64: - return C_O1_I2(r, r, re); - - case INDEX_op_sub_i32: - case INDEX_op_sub_i64: - case INDEX_op_mul_i32: - case INDEX_op_mul_i64: - case INDEX_op_or_i32: - case INDEX_op_or_i64: - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: - return C_O1_I2(r, 0, re); - - case INDEX_op_and_i32: - case INDEX_op_and_i64: - return C_O1_I2(r, 0, reZ); - - case INDEX_op_andc_i32: - case INDEX_op_andc_i64: - return C_O1_I2(r, r, rI); - - case INDEX_op_shl_i32: - case INDEX_op_shl_i64: - case INDEX_op_shr_i32: - case INDEX_op_shr_i64: - case INDEX_op_sar_i32: - case INDEX_op_sar_i64: - return have_bmi2 ? C_O1_I2(r, r, ri) : C_O1_I2(r, 0, ci); - - case INDEX_op_rotl_i32: - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i32: - case INDEX_op_rotr_i64: - return C_O1_I2(r, 0, ci); - - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - return C_O0_I2(r, re); - - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: - 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_extrh_i64_i32: - return C_O1_I1(r, 0); - - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - return C_O1_I1(r, q); - - 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: - case INDEX_op_extract_i32: - case INDEX_op_extract_i64: - case INDEX_op_sextract_i32: - case INDEX_op_ctpop_i32: - case INDEX_op_ctpop_i64: - return C_O1_I1(r, r); - - case INDEX_op_extract2_i32: - case INDEX_op_extract2_i64: - return C_O1_I2(r, 0, r); - - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: - return C_O1_I2(q, 0, qi); - - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: - return C_O1_I2(q, r, re); - - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - return C_O1_I4(r, r, re, r, 0); - - case INDEX_op_div2_i32: - case INDEX_op_div2_i64: - case INDEX_op_divu2_i32: - case INDEX_op_divu2_i64: - return C_O2_I3(a, d, 0, 1, r); - - case INDEX_op_mulu2_i32: - case INDEX_op_mulu2_i64: - case INDEX_op_muls2_i32: - case INDEX_op_muls2_i64: - return C_O2_I2(a, d, a, r); - - case INDEX_op_add2_i32: - case INDEX_op_add2_i64: - case INDEX_op_sub2_i32: - case INDEX_op_sub2_i64: - return C_N1_O1_I4(r, r, 0, 1, re, re); - - case INDEX_op_ctz_i32: - case INDEX_op_ctz_i64: - return have_bmi1 ? C_N1_I2(r, r, rW) : C_N1_I2(r, r, r); - - case INDEX_op_clz_i32: - 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_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_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_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_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); - - case INDEX_op_setcond2_i32: - return C_O1_I4(r, r, r, ri, ri); - case INDEX_op_ld_vec: case INDEX_op_dupm_vec: return C_O1_I1(x, r); @@ -3527,11 +4208,12 @@ 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(); + return C_NotImplemented; } } @@ -3654,49 +4336,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, @@ -3706,7 +4359,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, @@ -3759,12 +4412,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; @@ -3898,152 +4546,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; - - 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); - } + /* + * 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)); - 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); } - - tcg_debug_assert(cond == TCG_COND_EQ || cond == TCG_COND_GT); - /* 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; + return 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_cmp(TCGType type, unsigned vece, TCGArg a0, + TCGArg a1, TCGArg a2, TCGCond cond) { - if (expand_vec_cmp_noinv(type, vece, v0, v1, v2, cond)) { - tcg_gen_not_vec(vece, v0, v0); - } + cond = expand_vec_cond(type, vece, &a1, &a2, cond); + /* Expand directly; do not recurse. */ + vec_gen_4(INDEX_op_cmp_vec, type, vece, a0, a1, a2, cond); } -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) +static void expand_vec_cmpsel(TCGType type, unsigned vece, TCGArg a0, + TCGArg a1, TCGArg a2, + TCGArg a3, TCGArg a4, 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: + expand_vec_shi(type, vece, false, v0, v1, a2); + break; case INDEX_op_shri_vec: - expand_vec_shi(type, vece, opc, v0, v1, a2); + expand_vec_shi(type, vece, true, v0, v1, a2); break; - case INDEX_op_sari_vec: expand_vec_sari(type, vece, v0, v1, a2); break; @@ -4071,15 +4634,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: @@ -4140,35 +4703,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) - if (guest_base) { + if (!tcg_use_softmmu && guest_base) { int seg = setup_guest_base_seg(); if (seg != 0) { x86_guest_base.seg = seg; } else if (guest_base == (int32_t)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); } } -# 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, @@ -4191,6 +4754,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); diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index 8417ea4899e..3cbdfbca52e 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -25,8 +25,6 @@ #ifndef I386_TCG_TARGET_H #define I386_TCG_TARGET_H -#include "host/cpuinfo.h" - #define TCG_TARGET_INSN_UNIT_SIZE 1 #ifdef __x86_64__ @@ -90,165 +88,4 @@ typedef enum { TCG_REG_CALL_STACK = TCG_REG_ESP } TCGReg; -/* used for function call generation */ -#define TCG_TARGET_STACK_ALIGN 16 -#if defined(_WIN64) -#define TCG_TARGET_CALL_STACK_OFFSET 32 -#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 - -#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 -#define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 1 -#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 -#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_clz_i32 1 -#define TCG_TARGET_HAS_ctz_i32 1 -#define TCG_TARGET_HAS_ctpop_i32 have_popcnt -#define TCG_TARGET_HAS_deposit_i32 1 -#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 - -#if TCG_TARGET_REG_BITS == 64 -/* 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 -#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 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 -#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 1 -#define TCG_TARGET_HAS_ctz_i64 1 -#define TCG_TARGET_HAS_ctpop_i64 have_popcnt -#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 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 -#define TCG_TARGET_HAS_muls2_i64 1 -#define TCG_TARGET_HAS_muluh_i64 0 -#define TCG_TARGET_HAS_mulsh_i64 0 -#define TCG_TARGET_HAS_qemu_st8_i32 0 -#else -#define TCG_TARGET_HAS_qemu_st8_i32 1 -#endif - -#define TCG_TARGET_HAS_qemu_ldst_i128 \ - (TCG_TARGET_REG_BITS == 64 && (cpuinfo & CPUINFO_ATOMIC_VMOVDQA)) - -/* 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 -#define TCG_TARGET_HAS_v256 have_avx2 - -#define TCG_TARGET_HAS_andc_vec 1 -#define TCG_TARGET_HAS_orc_vec have_avx512vl -#define TCG_TARGET_HAS_nand_vec have_avx512vl -#define TCG_TARGET_HAS_nor_vec have_avx512vl -#define TCG_TARGET_HAS_eqv_vec have_avx512vl -#define TCG_TARGET_HAS_not_vec have_avx512vl -#define TCG_TARGET_HAS_neg_vec 0 -#define TCG_TARGET_HAS_abs_vec 1 -#define TCG_TARGET_HAS_roti_vec have_avx512vl -#define TCG_TARGET_HAS_rots_vec 0 -#define TCG_TARGET_HAS_rotv_vec have_avx512vl -#define TCG_TARGET_HAS_shi_vec 1 -#define TCG_TARGET_HAS_shs_vec 1 -#define TCG_TARGET_HAS_shv_vec have_avx2 -#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 have_avx512vl -#define TCG_TARGET_HAS_cmpsel_vec -1 - -#define TCG_TARGET_deposit_i32_valid(ofs, len) \ - (((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, - zero-extending 32-bit right-shift. */ -#define TCG_TARGET_extract_i32_valid(ofs, len) ((ofs) == 8 && (len) == 8) -#define TCG_TARGET_extract_i64_valid(ofs, len) \ - (((ofs) == 8 && (len) == 8) || ((ofs) + (len)) == 32) - -/* This defines the natural memory order supported by this - * architecture before guarantees made by various barrier - * instructions. - * - * The x86 has a pretty strong memory ordering which only really - * allows for some stores to be re-ordered after loads. - */ -#include "tcg/tcg-mo.h" - -#define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS - #endif diff --git a/tcg/i386/tcg-target.opc.h b/tcg/i386/tcg-target.opc.h deleted file mode 100644 index b5f403e35e1..00000000000 --- a/tcg/i386/tcg-target.opc.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2019 Linaro - * - * 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. - * - * 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. - */ - -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) -DEF(x86_psrldq_vec, 1, 1, 1, IMPLVEC) -DEF(x86_vperm2i128_vec, 1, 2, 1, IMPLVEC) -DEF(x86_punpckl_vec, 1, 2, 0, IMPLVEC) -DEF(x86_punpckh_vec, 1, 2, 0, IMPLVEC) -DEF(x86_vpshldi_vec, 1, 2, 1, IMPLVEC) -DEF(x86_vpshldv_vec, 1, 3, 0, IMPLVEC) -DEF(x86_vpshrdv_vec, 1, 3, 0, IMPLVEC) diff --git a/tcg/loongarch64/tcg-insn-defs.c.inc b/tcg/loongarch64/tcg-insn-defs.c.inc index b5bb0c5e739..6bb8656fd89 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 25ca7effe9d88101c1cf96c4005423643386d81f. + * 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,10 @@ 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, @@ -92,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, @@ -103,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, @@ -113,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)) @@ -133,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) @@ -140,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) { @@ -190,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) { @@ -238,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) { @@ -245,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) { @@ -252,736 +833,4237 @@ encode_ud15_insn(LoongArchInsn opc, uint32_t ud15) return encode_d_slot(opc, ud15); } -/* Emits the `clz.w d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_clz_w(TCGContext *s, TCGReg d, TCGReg j) +static int32_t __attribute__((unused)) +encode_vdj_insn(LoongArchInsn opc, TCGReg vd, TCGReg j) { - tcg_out32(s, encode_dj_insn(OPC_CLZ_W, d, j)); + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + return encode_dj_slots(opc, vd & 0x1f, j); } -/* Emits the `ctz.w d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ctz_w(TCGContext *s, TCGReg d, TCGReg j) +static int32_t __attribute__((unused)) +encode_vdjk_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, TCGReg k) { - tcg_out32(s, encode_dj_insn(OPC_CTZ_W, d, j)); + 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); } -/* Emits the `clz.d d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_clz_d(TCGContext *s, TCGReg d, TCGReg j) +static int32_t __attribute__((unused)) +encode_vdjsk10_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk10) { - tcg_out32(s, encode_dj_insn(OPC_CLZ_D, d, j)); + 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); } -/* Emits the `ctz.d d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ctz_d(TCGContext *s, TCGReg d, TCGReg j) +static int32_t __attribute__((unused)) +encode_vdjsk11_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk11) { - tcg_out32(s, encode_dj_insn(OPC_CTZ_D, d, j)); + 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); } -/* Emits the `revb.2h d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_revb_2h(TCGContext *s, TCGReg d, TCGReg j) +static int32_t __attribute__((unused)) +encode_vdjsk12_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk12) { - tcg_out32(s, encode_dj_insn(OPC_REVB_2H, d, j)); + 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); } -/* Emits the `revb.2w d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_revb_2w(TCGContext *s, TCGReg d, TCGReg j) +static int32_t __attribute__((unused)) +encode_vdjsk8un1_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk8, + uint32_t un1) { - tcg_out32(s, encode_dj_insn(OPC_REVB_2W, d, j)); + 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); } -/* Emits the `revb.d d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_revb_d(TCGContext *s, TCGReg d, TCGReg j) +static int32_t __attribute__((unused)) +encode_vdjsk8un2_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk8, + uint32_t un2) { - tcg_out32(s, encode_dj_insn(OPC_REVB_D, d, j)); + 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); } -/* Emits the `sext.h d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sext_h(TCGContext *s, TCGReg d, TCGReg j) +static int32_t __attribute__((unused)) +encode_vdjsk8un3_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk8, + uint32_t un3) { - tcg_out32(s, encode_dj_insn(OPC_SEXT_H, d, j)); + 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); } -/* Emits the `sext.b d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sext_b(TCGContext *s, TCGReg d, TCGReg j) +static int32_t __attribute__((unused)) +encode_vdjsk8un4_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk8, + uint32_t un4) { - tcg_out32(s, encode_dj_insn(OPC_SEXT_B, d, j)); + 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); } -/* Emits the `add.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_add_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static int32_t __attribute__((unused)) +encode_vdjsk9_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, int32_t sk9) { - tcg_out32(s, encode_djk_insn(OPC_ADD_W, d, j, k)); + 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); } -/* Emits the `add.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_add_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static int32_t __attribute__((unused)) +encode_vdjuk1_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, uint32_t uk1) { - tcg_out32(s, encode_djk_insn(OPC_ADD_D, d, j, k)); + 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); } -/* Emits the `sub.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sub_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static int32_t __attribute__((unused)) +encode_vdjuk2_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, uint32_t uk2) { - tcg_out32(s, encode_djk_insn(OPC_SUB_W, d, j, k)); + 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); } -/* Emits the `sub.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sub_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static int32_t __attribute__((unused)) +encode_vdjuk3_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, uint32_t uk3) { - tcg_out32(s, encode_djk_insn(OPC_SUB_D, d, j, k)); + 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); } -/* Emits the `slt d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_slt(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static int32_t __attribute__((unused)) +encode_vdjuk4_insn(LoongArchInsn opc, TCGReg vd, TCGReg j, uint32_t uk4) { - tcg_out32(s, encode_djk_insn(OPC_SLT, d, j, k)); + 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); } -/* Emits the `sltu d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sltu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static int32_t __attribute__((unused)) +encode_vdsj13_insn(LoongArchInsn opc, TCGReg vd, int32_t sj13) { - tcg_out32(s, encode_djk_insn(OPC_SLTU, d, j, k)); + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(sj13 >= -0x1000 && sj13 <= 0xfff); + return encode_dj_slots(opc, vd & 0x1f, sj13 & 0x1fff); } -/* Emits the `maskeqz d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_maskeqz(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static int32_t __attribute__((unused)) +encode_vdvj_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj) { - tcg_out32(s, encode_djk_insn(OPC_MASKEQZ, d, j, k)); + tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); + tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); + return encode_dj_slots(opc, vd & 0x1f, vj & 0x1f); } -/* Emits the `masknez d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_masknez(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static int32_t __attribute__((unused)) +encode_vdvjk_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, TCGReg k) { - tcg_out32(s, encode_djk_insn(OPC_MASKNEZ, d, j, 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); } -/* Emits the `nor d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_nor(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static int32_t __attribute__((unused)) +encode_vdvjsk5_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, int32_t sk5) { - tcg_out32(s, encode_djk_insn(OPC_NOR, d, j, k)); + 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); } -/* Emits the `and d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_and(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static int32_t __attribute__((unused)) +encode_vdvjuk1_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, uint32_t uk1) { - tcg_out32(s, encode_djk_insn(OPC_AND, d, j, k)); + 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); } -/* Emits the `or d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_or(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static int32_t __attribute__((unused)) +encode_vdvjuk2_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, uint32_t uk2) { - tcg_out32(s, encode_djk_insn(OPC_OR, d, j, k)); + 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) +{ + tcg_out32(s, encode_dj_insn(OPC_CLZ_W, d, j)); +} + +/* Emits the `ctz.w d, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_ctz_w(TCGContext *s, TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_CTZ_W, d, j)); +} + +/* Emits the `clz.d d, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_clz_d(TCGContext *s, TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_CLZ_D, d, j)); +} + +/* Emits the `ctz.d d, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_ctz_d(TCGContext *s, TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_CTZ_D, d, j)); +} + +/* Emits the `revb.2h d, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_revb_2h(TCGContext *s, TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_REVB_2H, d, j)); +} + +/* Emits the `revb.2w d, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_revb_2w(TCGContext *s, TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_REVB_2W, d, j)); +} + +/* Emits the `revb.d d, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_revb_d(TCGContext *s, TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_REVB_D, d, j)); +} + +/* Emits the `sext.h d, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_sext_h(TCGContext *s, TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_SEXT_H, d, j)); +} + +/* Emits the `sext.b d, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_sext_b(TCGContext *s, TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_SEXT_B, d, j)); +} + +/* Emits the `add.w d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_add_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_ADD_W, d, j, k)); +} + +/* Emits the `add.d d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_add_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_ADD_D, d, j, k)); +} + +/* Emits the `sub.w d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_sub_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_SUB_W, d, j, k)); +} + +/* Emits the `sub.d d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_sub_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_SUB_D, d, j, k)); +} + +/* Emits the `slt d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_slt(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_SLT, d, j, k)); +} + +/* Emits the `sltu d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_sltu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_SLTU, d, j, k)); +} + +/* Emits the `maskeqz d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_maskeqz(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_MASKEQZ, d, j, k)); +} + +/* Emits the `masknez d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_masknez(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_MASKNEZ, d, j, k)); +} + +/* Emits the `nor d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_nor(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_NOR, d, j, k)); +} + +/* Emits the `and d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_and(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_AND, d, j, k)); +} + +/* Emits the `or d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_or(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_OR, d, j, k)); +} + +/* Emits the `xor d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xor(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_XOR, d, j, k)); +} + +/* Emits the `orn d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_orn(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_ORN, d, j, k)); +} + +/* Emits the `andn d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_andn(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_ANDN, d, j, k)); +} + +/* Emits the `sll.w d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_sll_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_SLL_W, d, j, k)); +} + +/* Emits the `srl.w d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_srl_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_SRL_W, d, j, k)); +} + +/* Emits the `sra.w d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_sra_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_SRA_W, d, j, k)); +} + +/* Emits the `sll.d d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_sll_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_SLL_D, d, j, k)); +} + +/* Emits the `srl.d d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_srl_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_SRL_D, d, j, k)); +} + +/* Emits the `sra.d d, j, k` instruction. */ +static void __attribute__((unused)) +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) +{ + tcg_out32(s, encode_djk_insn(OPC_ROTR_W, d, j, k)); +} + +/* Emits the `rotr.d d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_rotr_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_ROTR_D, d, j, k)); +} + +/* Emits the `mul.w d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_mul_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_MUL_W, d, j, k)); +} + +/* Emits the `mulh.w d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_mulh_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_MULH_W, d, j, k)); +} + +/* Emits the `mulh.wu d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_mulh_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_MULH_WU, d, j, k)); +} + +/* Emits the `mul.d d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_mul_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_MUL_D, d, j, k)); +} + +/* Emits the `mulh.d d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_mulh_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_MULH_D, d, j, k)); +} + +/* Emits the `mulh.du d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_mulh_du(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_MULH_DU, d, j, k)); +} + +/* Emits the `div.w d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_div_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_DIV_W, d, j, k)); +} + +/* Emits the `mod.w d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_mod_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_MOD_W, d, j, k)); +} + +/* Emits the `div.wu d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_div_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_DIV_WU, d, j, k)); +} + +/* Emits the `mod.wu d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_mod_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_MOD_WU, d, j, k)); +} + +/* Emits the `div.d d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_div_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_DIV_D, d, j, k)); +} + +/* Emits the `mod.d d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_mod_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_MOD_D, d, j, k)); +} + +/* Emits the `div.du d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_div_du(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_DIV_DU, d, j, k)); +} + +/* Emits the `mod.du d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_mod_du(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_MOD_DU, d, j, k)); +} + +/* Emits the `slli.w d, j, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_slli_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) +{ + tcg_out32(s, encode_djuk5_insn(OPC_SLLI_W, d, j, uk5)); +} + +/* Emits the `slli.d d, j, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_slli_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) +{ + tcg_out32(s, encode_djuk6_insn(OPC_SLLI_D, d, j, uk6)); +} + +/* Emits the `srli.w d, j, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_srli_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) +{ + tcg_out32(s, encode_djuk5_insn(OPC_SRLI_W, d, j, uk5)); +} + +/* Emits the `srli.d d, j, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_srli_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) +{ + tcg_out32(s, encode_djuk6_insn(OPC_SRLI_D, d, j, uk6)); +} + +/* Emits the `srai.w d, j, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_srai_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) +{ + tcg_out32(s, encode_djuk5_insn(OPC_SRAI_W, d, j, uk5)); +} + +/* Emits the `srai.d d, j, uk6` instruction. */ +static void __attribute__((unused)) +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) +{ + tcg_out32(s, encode_djuk5_insn(OPC_ROTRI_W, d, j, uk5)); +} + +/* Emits the `rotri.d d, j, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_rotri_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) +{ + tcg_out32(s, encode_djuk6_insn(OPC_ROTRI_D, d, j, uk6)); +} + +/* Emits the `bstrins.w d, j, uk5, um5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_bstrins_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5, + uint32_t um5) +{ + tcg_out32(s, encode_djuk5um5_insn(OPC_BSTRINS_W, d, j, uk5, um5)); +} + +/* Emits the `bstrpick.w d, j, uk5, um5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_bstrpick_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5, + uint32_t um5) +{ + tcg_out32(s, encode_djuk5um5_insn(OPC_BSTRPICK_W, d, j, uk5, um5)); +} + +/* Emits the `bstrins.d d, j, uk6, um6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_bstrins_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6, + uint32_t um6) +{ + tcg_out32(s, encode_djuk6um6_insn(OPC_BSTRINS_D, d, j, uk6, um6)); +} + +/* Emits the `bstrpick.d d, j, uk6, um6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_bstrpick_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6, + uint32_t um6) +{ + 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) +{ + tcg_out32(s, encode_djsk12_insn(OPC_SLTI, d, j, sk12)); +} + +/* Emits the `sltui d, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_sltui(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_djsk12_insn(OPC_SLTUI, d, j, sk12)); +} + +/* Emits the `addi.w d, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_addi_w(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_djsk12_insn(OPC_ADDI_W, d, j, sk12)); +} + +/* Emits the `addi.d d, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_addi_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_djsk12_insn(OPC_ADDI_D, d, j, sk12)); +} + +/* Emits the `cu52i.d d, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_cu52i_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_djsk12_insn(OPC_CU52I_D, d, j, sk12)); +} + +/* Emits the `andi d, j, uk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_andi(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk12) +{ + tcg_out32(s, encode_djuk12_insn(OPC_ANDI, d, j, uk12)); +} + +/* Emits the `ori d, j, uk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_ori(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk12) +{ + tcg_out32(s, encode_djuk12_insn(OPC_ORI, d, j, uk12)); +} + +/* Emits the `xori d, j, uk12` instruction. */ +static void __attribute__((unused)) +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) +{ + tcg_out32(s, encode_dsj20_insn(OPC_LU12I_W, d, sj20)); +} + +/* Emits the `cu32i.d d, sj20` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_cu32i_d(TCGContext *s, TCGReg d, int32_t sj20) +{ + tcg_out32(s, encode_dsj20_insn(OPC_CU32I_D, d, sj20)); +} + +/* Emits the `pcaddu2i d, sj20` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_pcaddu2i(TCGContext *s, TCGReg d, int32_t sj20) +{ + tcg_out32(s, encode_dsj20_insn(OPC_PCADDU2I, d, sj20)); +} + +/* Emits the `pcalau12i d, sj20` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_pcalau12i(TCGContext *s, TCGReg d, int32_t sj20) +{ + tcg_out32(s, encode_dsj20_insn(OPC_PCALAU12I, d, sj20)); +} + +/* Emits the `pcaddu12i d, sj20` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_pcaddu12i(TCGContext *s, TCGReg d, int32_t sj20) +{ + tcg_out32(s, encode_dsj20_insn(OPC_PCADDU12I, d, sj20)); +} + +/* Emits the `pcaddu18i d, sj20` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_pcaddu18i(TCGContext *s, TCGReg d, int32_t sj20) +{ + tcg_out32(s, encode_dsj20_insn(OPC_PCADDU18I, d, sj20)); +} + +/* Emits the `ld.b d, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_ld_b(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_djsk12_insn(OPC_LD_B, d, j, sk12)); +} + +/* Emits the `ld.h d, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_ld_h(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_djsk12_insn(OPC_LD_H, d, j, sk12)); +} + +/* Emits the `ld.w d, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_ld_w(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_djsk12_insn(OPC_LD_W, d, j, sk12)); +} + +/* Emits the `ld.d d, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_ld_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_djsk12_insn(OPC_LD_D, d, j, sk12)); +} + +/* Emits the `st.b d, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_st_b(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_djsk12_insn(OPC_ST_B, d, j, sk12)); +} + +/* Emits the `st.h d, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_st_h(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_djsk12_insn(OPC_ST_H, d, j, sk12)); +} + +/* Emits the `st.w d, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_st_w(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_djsk12_insn(OPC_ST_W, d, j, sk12)); +} + +/* Emits the `st.d d, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_st_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_djsk12_insn(OPC_ST_D, d, j, sk12)); +} + +/* Emits the `ld.bu d, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_ld_bu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_djsk12_insn(OPC_LD_BU, d, j, sk12)); +} + +/* Emits the `ld.hu d, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_ld_hu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_djsk12_insn(OPC_LD_HU, d, j, sk12)); +} + +/* Emits the `ld.wu d, j, sk12` instruction. */ +static void __attribute__((unused)) +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) +{ + tcg_out32(s, encode_djk_insn(OPC_LDX_B, d, j, k)); +} + +/* Emits the `ldx.h d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_ldx_h(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_LDX_H, d, j, k)); +} + +/* Emits the `ldx.w d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_ldx_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_LDX_W, d, j, k)); +} + +/* Emits the `ldx.d d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_ldx_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_LDX_D, d, j, k)); +} + +/* Emits the `stx.b d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_stx_b(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_STX_B, d, j, k)); +} + +/* Emits the `stx.h d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_stx_h(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_STX_H, d, j, k)); +} + +/* Emits the `stx.w d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_stx_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_STX_W, d, j, k)); +} + +/* Emits the `stx.d d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_stx_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_STX_D, d, j, k)); +} + +/* Emits the `ldx.bu d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_ldx_bu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_LDX_BU, d, j, k)); +} + +/* Emits the `ldx.hu d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_ldx_hu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_LDX_HU, d, j, k)); +} + +/* Emits the `ldx.wu d, j, k` instruction. */ +static void __attribute__((unused)) +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) +{ + 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) +{ + tcg_out32(s, encode_djsk16_insn(OPC_JIRL, d, j, sk16)); +} + +/* Emits the `b sd10k16` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_b(TCGContext *s, int32_t sd10k16) +{ + tcg_out32(s, encode_sd10k16_insn(OPC_B, sd10k16)); +} + +/* Emits the `bl sd10k16` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_bl(TCGContext *s, int32_t sd10k16) +{ + tcg_out32(s, encode_sd10k16_insn(OPC_BL, sd10k16)); +} + +/* Emits the `beq d, j, sk16` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_beq(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +{ + tcg_out32(s, encode_djsk16_insn(OPC_BEQ, d, j, sk16)); +} + +/* Emits the `bne d, j, sk16` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_bne(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +{ + tcg_out32(s, encode_djsk16_insn(OPC_BNE, d, j, sk16)); +} + +/* Emits the `bgt d, j, sk16` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_bgt(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +{ + tcg_out32(s, encode_djsk16_insn(OPC_BGT, d, j, sk16)); +} + +/* Emits the `ble d, j, sk16` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_ble(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +{ + tcg_out32(s, encode_djsk16_insn(OPC_BLE, d, j, sk16)); +} + +/* Emits the `bgtu d, j, sk16` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_bgtu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +{ + tcg_out32(s, encode_djsk16_insn(OPC_BGTU, d, j, sk16)); +} + +/* Emits the `bleu d, j, sk16` instruction. */ +static void __attribute__((unused)) +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 `xor d, j, k` instruction. */ +/* Emits the `xvslti.hu xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_xor(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvslti_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_XOR, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLTI_HU, xd, xj, uk5)); } -/* Emits the `orn d, j, k` instruction. */ +/* Emits the `xvslti.wu xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_orn(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvslti_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_ORN, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLTI_WU, xd, xj, uk5)); } -/* Emits the `andn d, j, k` instruction. */ +/* Emits the `xvslti.du xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_andn(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvslti_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_ANDN, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLTI_DU, xd, xj, uk5)); } -/* Emits the `sll.w d, j, k` instruction. */ +/* Emits the `xvaddi.bu xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_sll_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvaddi_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_SLL_W, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVADDI_BU, xd, xj, uk5)); } -/* Emits the `srl.w d, j, k` instruction. */ +/* Emits the `xvaddi.hu xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_srl_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvaddi_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_SRL_W, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVADDI_HU, xd, xj, uk5)); } -/* Emits the `sra.w d, j, k` instruction. */ +/* Emits the `xvaddi.wu xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_sra_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvaddi_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_SRA_W, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVADDI_WU, xd, xj, uk5)); } -/* Emits the `sll.d d, j, k` instruction. */ +/* Emits the `xvaddi.du xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_sll_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvaddi_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_SLL_D, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVADDI_DU, xd, xj, uk5)); } -/* Emits the `srl.d d, j, k` instruction. */ +/* Emits the `xvsubi.bu xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_srl_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvsubi_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_SRL_D, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSUBI_BU, xd, xj, uk5)); } -/* Emits the `sra.d d, j, k` instruction. */ +/* Emits the `xvsubi.hu xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_sra_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvsubi_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_SRA_D, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSUBI_HU, xd, xj, uk5)); } -/* Emits the `rotr.w d, j, k` instruction. */ +/* Emits the `xvsubi.wu xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_rotr_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvsubi_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_ROTR_W, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSUBI_WU, xd, xj, uk5)); } -/* Emits the `rotr.d d, j, k` instruction. */ +/* Emits the `xvsubi.du xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_rotr_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvsubi_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_ROTR_D, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSUBI_DU, xd, xj, uk5)); } -/* Emits the `mul.w d, j, k` instruction. */ +/* Emits the `xvmaxi.b xd, xj, sk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_mul_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvmaxi_b(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) { - tcg_out32(s, encode_djk_insn(OPC_MUL_W, d, j, k)); + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMAXI_B, xd, xj, sk5)); } -/* Emits the `mulh.w d, j, k` instruction. */ +/* Emits the `xvmaxi.h xd, xj, sk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_mulh_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvmaxi_h(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) { - tcg_out32(s, encode_djk_insn(OPC_MULH_W, d, j, k)); + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMAXI_H, xd, xj, sk5)); } -/* Emits the `mulh.wu d, j, k` instruction. */ +/* Emits the `xvmaxi.w xd, xj, sk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_mulh_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvmaxi_w(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) { - tcg_out32(s, encode_djk_insn(OPC_MULH_WU, d, j, k)); + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMAXI_W, xd, xj, sk5)); } -/* Emits the `mul.d d, j, k` instruction. */ +/* Emits the `xvmaxi.d xd, xj, sk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_mul_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvmaxi_d(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) { - tcg_out32(s, encode_djk_insn(OPC_MUL_D, d, j, k)); + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMAXI_D, xd, xj, sk5)); } -/* Emits the `mulh.d d, j, k` instruction. */ +/* Emits the `xvmini.b xd, xj, sk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_mulh_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvmini_b(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) { - tcg_out32(s, encode_djk_insn(OPC_MULH_D, d, j, k)); + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMINI_B, xd, xj, sk5)); } -/* Emits the `mulh.du d, j, k` instruction. */ +/* Emits the `xvmini.h xd, xj, sk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_mulh_du(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvmini_h(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) { - tcg_out32(s, encode_djk_insn(OPC_MULH_DU, d, j, k)); + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMINI_H, xd, xj, sk5)); } -/* Emits the `div.w d, j, k` instruction. */ +/* Emits the `xvmini.w xd, xj, sk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_div_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvmini_w(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) { - tcg_out32(s, encode_djk_insn(OPC_DIV_W, d, j, k)); + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMINI_W, xd, xj, sk5)); } -/* Emits the `mod.w d, j, k` instruction. */ +/* Emits the `xvmini.d xd, xj, sk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_mod_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvmini_d(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) { - tcg_out32(s, encode_djk_insn(OPC_MOD_W, d, j, k)); + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMINI_D, xd, xj, sk5)); } -/* Emits the `div.wu d, j, k` instruction. */ +/* Emits the `xvmaxi.bu xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_div_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvmaxi_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_DIV_WU, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMAXI_BU, xd, xj, uk5)); } -/* Emits the `mod.wu d, j, k` instruction. */ +/* Emits the `xvmaxi.hu xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_mod_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvmaxi_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_MOD_WU, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMAXI_HU, xd, xj, uk5)); } -/* Emits the `div.d d, j, k` instruction. */ +/* Emits the `xvmaxi.wu xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_div_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvmaxi_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_DIV_D, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMAXI_WU, xd, xj, uk5)); } -/* Emits the `mod.d d, j, k` instruction. */ +/* Emits the `xvmaxi.du xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_mod_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvmaxi_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_MOD_D, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMAXI_DU, xd, xj, uk5)); } -/* Emits the `div.du d, j, k` instruction. */ +/* Emits the `xvmini.bu xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_div_du(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvmini_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_DIV_DU, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMINI_BU, xd, xj, uk5)); } -/* Emits the `mod.du d, j, k` instruction. */ +/* Emits the `xvmini.hu xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_mod_du(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvmini_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_MOD_DU, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMINI_HU, xd, xj, uk5)); } -/* Emits the `slli.w d, j, uk5` instruction. */ +/* Emits the `xvmini.wu xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_slli_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) +tcg_out_opc_xvmini_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djuk5_insn(OPC_SLLI_W, d, j, uk5)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMINI_WU, xd, xj, uk5)); } -/* Emits the `slli.d d, j, uk6` instruction. */ +/* Emits the `xvmini.du xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_slli_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) +tcg_out_opc_xvmini_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djuk6_insn(OPC_SLLI_D, d, j, uk6)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMINI_DU, xd, xj, uk5)); } -/* Emits the `srli.w d, j, uk5` instruction. */ +/* Emits the `xvneg.b xd, xj` instruction. */ static void __attribute__((unused)) -tcg_out_opc_srli_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) +tcg_out_opc_xvneg_b(TCGContext *s, TCGReg xd, TCGReg xj) { - tcg_out32(s, encode_djuk5_insn(OPC_SRLI_W, d, j, uk5)); + tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_B, xd, xj)); } -/* Emits the `srli.d d, j, uk6` instruction. */ +/* Emits the `xvneg.h xd, xj` instruction. */ static void __attribute__((unused)) -tcg_out_opc_srli_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) +tcg_out_opc_xvneg_h(TCGContext *s, TCGReg xd, TCGReg xj) { - tcg_out32(s, encode_djuk6_insn(OPC_SRLI_D, d, j, uk6)); + tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_H, xd, xj)); } -/* Emits the `srai.w d, j, uk5` instruction. */ +/* Emits the `xvneg.w xd, xj` instruction. */ static void __attribute__((unused)) -tcg_out_opc_srai_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) +tcg_out_opc_xvneg_w(TCGContext *s, TCGReg xd, TCGReg xj) { - tcg_out32(s, encode_djuk5_insn(OPC_SRAI_W, d, j, uk5)); + tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_W, xd, xj)); } -/* Emits the `srai.d d, j, uk6` instruction. */ +/* Emits the `xvneg.d xd, xj` instruction. */ static void __attribute__((unused)) -tcg_out_opc_srai_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) +tcg_out_opc_xvneg_d(TCGContext *s, TCGReg xd, TCGReg xj) { - tcg_out32(s, encode_djuk6_insn(OPC_SRAI_D, d, j, uk6)); + tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_D, xd, xj)); } -/* Emits the `rotri.w d, j, uk5` instruction. */ +/* Emits the `xvreplgr2vr.b xd, j` instruction. */ static void __attribute__((unused)) -tcg_out_opc_rotri_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) +tcg_out_opc_xvreplgr2vr_b(TCGContext *s, TCGReg xd, TCGReg j) { - tcg_out32(s, encode_djuk5_insn(OPC_ROTRI_W, d, j, uk5)); + tcg_out32(s, encode_xdj_insn(OPC_XVREPLGR2VR_B, xd, j)); } -/* Emits the `rotri.d d, j, uk6` instruction. */ +/* Emits the `xvreplgr2vr.h xd, j` instruction. */ static void __attribute__((unused)) -tcg_out_opc_rotri_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) +tcg_out_opc_xvreplgr2vr_h(TCGContext *s, TCGReg xd, TCGReg j) { - tcg_out32(s, encode_djuk6_insn(OPC_ROTRI_D, d, j, uk6)); + tcg_out32(s, encode_xdj_insn(OPC_XVREPLGR2VR_H, xd, j)); } -/* Emits the `bstrins.w d, j, uk5, um5` instruction. */ +/* Emits the `xvreplgr2vr.w xd, j` instruction. */ static void __attribute__((unused)) -tcg_out_opc_bstrins_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5, - uint32_t um5) +tcg_out_opc_xvreplgr2vr_w(TCGContext *s, TCGReg xd, TCGReg j) { - tcg_out32(s, encode_djuk5um5_insn(OPC_BSTRINS_W, d, j, uk5, um5)); + tcg_out32(s, encode_xdj_insn(OPC_XVREPLGR2VR_W, xd, j)); } -/* Emits the `bstrpick.w d, j, uk5, um5` instruction. */ +/* Emits the `xvreplgr2vr.d xd, j` instruction. */ static void __attribute__((unused)) -tcg_out_opc_bstrpick_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5, - uint32_t um5) +tcg_out_opc_xvreplgr2vr_d(TCGContext *s, TCGReg xd, TCGReg j) { - tcg_out32(s, encode_djuk5um5_insn(OPC_BSTRPICK_W, d, j, uk5, um5)); + tcg_out32(s, encode_xdj_insn(OPC_XVREPLGR2VR_D, xd, j)); } -/* Emits the `bstrins.d d, j, uk6, um6` instruction. */ +/* Emits the `xvrotri.b xd, xj, uk3` instruction. */ static void __attribute__((unused)) -tcg_out_opc_bstrins_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6, - uint32_t um6) +tcg_out_opc_xvrotri_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) { - tcg_out32(s, encode_djuk6um6_insn(OPC_BSTRINS_D, d, j, uk6, um6)); + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVROTRI_B, xd, xj, uk3)); } -/* Emits the `bstrpick.d d, j, uk6, um6` instruction. */ +/* Emits the `xvrotri.h xd, xj, uk4` instruction. */ static void __attribute__((unused)) -tcg_out_opc_bstrpick_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6, - uint32_t um6) +tcg_out_opc_xvrotri_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) { - tcg_out32(s, encode_djuk6um6_insn(OPC_BSTRPICK_D, d, j, uk6, um6)); + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVROTRI_H, xd, xj, uk4)); } -/* Emits the `slti d, j, sk12` instruction. */ +/* Emits the `xvrotri.w xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_slti(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvrotri_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djsk12_insn(OPC_SLTI, d, j, sk12)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVROTRI_W, xd, xj, uk5)); } -/* Emits the `sltui d, j, sk12` instruction. */ +/* Emits the `xvrotri.d xd, xj, uk6` instruction. */ static void __attribute__((unused)) -tcg_out_opc_sltui(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvrotri_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) { - tcg_out32(s, encode_djsk12_insn(OPC_SLTUI, d, j, sk12)); + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVROTRI_D, xd, xj, uk6)); } -/* Emits the `addi.w d, j, sk12` instruction. */ +/* Emits the `xvinsgr2vr.w xd, j, uk3` instruction. */ static void __attribute__((unused)) -tcg_out_opc_addi_w(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvinsgr2vr_w(TCGContext *s, TCGReg xd, TCGReg j, uint32_t uk3) { - tcg_out32(s, encode_djsk12_insn(OPC_ADDI_W, d, j, sk12)); + tcg_out32(s, encode_xdjuk3_insn(OPC_XVINSGR2VR_W, xd, j, uk3)); } -/* Emits the `addi.d d, j, sk12` instruction. */ +/* Emits the `xvinsgr2vr.d xd, j, uk2` instruction. */ static void __attribute__((unused)) -tcg_out_opc_addi_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvinsgr2vr_d(TCGContext *s, TCGReg xd, TCGReg j, uint32_t uk2) { - tcg_out32(s, encode_djsk12_insn(OPC_ADDI_D, d, j, sk12)); + tcg_out32(s, encode_xdjuk2_insn(OPC_XVINSGR2VR_D, xd, j, uk2)); } -/* Emits the `cu52i.d d, j, sk12` instruction. */ +/* Emits the `xvpickve2gr.w d, xj, uk3` instruction. */ static void __attribute__((unused)) -tcg_out_opc_cu52i_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvpickve2gr_w(TCGContext *s, TCGReg d, TCGReg xj, uint32_t uk3) { - tcg_out32(s, encode_djsk12_insn(OPC_CU52I_D, d, j, sk12)); + tcg_out32(s, encode_dxjuk3_insn(OPC_XVPICKVE2GR_W, d, xj, uk3)); } -/* Emits the `andi d, j, uk12` instruction. */ +/* Emits the `xvpickve2gr.d d, xj, uk2` instruction. */ static void __attribute__((unused)) -tcg_out_opc_andi(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk12) +tcg_out_opc_xvpickve2gr_d(TCGContext *s, TCGReg d, TCGReg xj, uint32_t uk2) { - tcg_out32(s, encode_djuk12_insn(OPC_ANDI, d, j, uk12)); + tcg_out32(s, encode_dxjuk2_insn(OPC_XVPICKVE2GR_D, d, xj, uk2)); } -/* Emits the `ori d, j, uk12` instruction. */ +/* Emits the `xvpickve2gr.wu d, xj, uk3` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ori(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk12) +tcg_out_opc_xvpickve2gr_wu(TCGContext *s, TCGReg d, TCGReg xj, uint32_t uk3) { - tcg_out32(s, encode_djuk12_insn(OPC_ORI, d, j, uk12)); + tcg_out32(s, encode_dxjuk3_insn(OPC_XVPICKVE2GR_WU, d, xj, uk3)); } -/* Emits the `xori d, j, uk12` instruction. */ +/* Emits the `xvpickve2gr.du d, xj, uk2` instruction. */ static void __attribute__((unused)) -tcg_out_opc_xori(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk12) +tcg_out_opc_xvpickve2gr_du(TCGContext *s, TCGReg d, TCGReg xj, uint32_t uk2) { - tcg_out32(s, encode_djuk12_insn(OPC_XORI, d, j, uk12)); + tcg_out32(s, encode_dxjuk2_insn(OPC_XVPICKVE2GR_DU, d, xj, uk2)); } -/* Emits the `addu16i.d d, j, sk16` instruction. */ +/* Emits the `xvrepl128vei.b xd, xj, uk4` instruction. */ static void __attribute__((unused)) -tcg_out_opc_addu16i_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +tcg_out_opc_xvrepl128vei_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) { - tcg_out32(s, encode_djsk16_insn(OPC_ADDU16I_D, d, j, sk16)); + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVREPL128VEI_B, xd, xj, uk4)); } -/* Emits the `lu12i.w d, sj20` instruction. */ +/* Emits the `xvrepl128vei.h xd, xj, uk3` instruction. */ static void __attribute__((unused)) -tcg_out_opc_lu12i_w(TCGContext *s, TCGReg d, int32_t sj20) +tcg_out_opc_xvrepl128vei_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) { - tcg_out32(s, encode_dsj20_insn(OPC_LU12I_W, d, sj20)); + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVREPL128VEI_H, xd, xj, uk3)); } -/* Emits the `cu32i.d d, sj20` instruction. */ +/* Emits the `xvrepl128vei.w xd, xj, uk2` instruction. */ static void __attribute__((unused)) -tcg_out_opc_cu32i_d(TCGContext *s, TCGReg d, int32_t sj20) +tcg_out_opc_xvrepl128vei_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk2) { - tcg_out32(s, encode_dsj20_insn(OPC_CU32I_D, d, sj20)); + tcg_out32(s, encode_xdxjuk2_insn(OPC_XVREPL128VEI_W, xd, xj, uk2)); } -/* Emits the `pcaddu2i d, sj20` instruction. */ +/* Emits the `xvrepl128vei.d xd, xj, uk1` instruction. */ static void __attribute__((unused)) -tcg_out_opc_pcaddu2i(TCGContext *s, TCGReg d, int32_t sj20) +tcg_out_opc_xvrepl128vei_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk1) { - tcg_out32(s, encode_dsj20_insn(OPC_PCADDU2I, d, sj20)); + tcg_out32(s, encode_xdxjuk1_insn(OPC_XVREPL128VEI_D, xd, xj, uk1)); } -/* Emits the `pcalau12i d, sj20` instruction. */ +/* Emits the `xvreplve0.b xd, xj` instruction. */ static void __attribute__((unused)) -tcg_out_opc_pcalau12i(TCGContext *s, TCGReg d, int32_t sj20) +tcg_out_opc_xvreplve0_b(TCGContext *s, TCGReg xd, TCGReg xj) { - tcg_out32(s, encode_dsj20_insn(OPC_PCALAU12I, d, sj20)); + tcg_out32(s, encode_xdxj_insn(OPC_XVREPLVE0_B, xd, xj)); } -/* Emits the `pcaddu12i d, sj20` instruction. */ +/* Emits the `xvreplve0.h xd, xj` instruction. */ static void __attribute__((unused)) -tcg_out_opc_pcaddu12i(TCGContext *s, TCGReg d, int32_t sj20) +tcg_out_opc_xvreplve0_h(TCGContext *s, TCGReg xd, TCGReg xj) { - tcg_out32(s, encode_dsj20_insn(OPC_PCADDU12I, d, sj20)); + tcg_out32(s, encode_xdxj_insn(OPC_XVREPLVE0_H, xd, xj)); } -/* Emits the `pcaddu18i d, sj20` instruction. */ +/* Emits the `xvreplve0.w xd, xj` instruction. */ static void __attribute__((unused)) -tcg_out_opc_pcaddu18i(TCGContext *s, TCGReg d, int32_t sj20) +tcg_out_opc_xvreplve0_w(TCGContext *s, TCGReg xd, TCGReg xj) { - tcg_out32(s, encode_dsj20_insn(OPC_PCADDU18I, d, sj20)); + tcg_out32(s, encode_xdxj_insn(OPC_XVREPLVE0_W, xd, xj)); } -/* Emits the `ld.b d, j, sk12` instruction. */ +/* Emits the `xvreplve0.d xd, xj` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ld_b(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvreplve0_d(TCGContext *s, TCGReg xd, TCGReg xj) { - tcg_out32(s, encode_djsk12_insn(OPC_LD_B, d, j, sk12)); + tcg_out32(s, encode_xdxj_insn(OPC_XVREPLVE0_D, xd, xj)); } -/* Emits the `ld.h d, j, sk12` instruction. */ +/* Emits the `xvreplve0.q xd, xj` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ld_h(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvreplve0_q(TCGContext *s, TCGReg xd, TCGReg xj) { - tcg_out32(s, encode_djsk12_insn(OPC_LD_H, d, j, sk12)); + tcg_out32(s, encode_xdxj_insn(OPC_XVREPLVE0_Q, xd, xj)); } -/* Emits the `ld.w d, j, sk12` instruction. */ +/* Emits the `xvbitclri.b xd, xj, uk3` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ld_w(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvbitclri_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) { - tcg_out32(s, encode_djsk12_insn(OPC_LD_W, d, j, sk12)); + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVBITCLRI_B, xd, xj, uk3)); } -/* Emits the `ld.d d, j, sk12` instruction. */ +/* Emits the `xvbitclri.h xd, xj, uk4` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ld_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvbitclri_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) { - tcg_out32(s, encode_djsk12_insn(OPC_LD_D, d, j, sk12)); + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVBITCLRI_H, xd, xj, uk4)); } -/* Emits the `st.b d, j, sk12` instruction. */ +/* Emits the `xvbitclri.w xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_st_b(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvbitclri_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djsk12_insn(OPC_ST_B, d, j, sk12)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVBITCLRI_W, xd, xj, uk5)); } -/* Emits the `st.h d, j, sk12` instruction. */ +/* Emits the `xvbitclri.d xd, xj, uk6` instruction. */ static void __attribute__((unused)) -tcg_out_opc_st_h(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvbitclri_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) { - tcg_out32(s, encode_djsk12_insn(OPC_ST_H, d, j, sk12)); + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVBITCLRI_D, xd, xj, uk6)); } -/* Emits the `st.w d, j, sk12` instruction. */ +/* Emits the `xvbitseti.b xd, xj, uk3` instruction. */ static void __attribute__((unused)) -tcg_out_opc_st_w(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvbitseti_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) { - tcg_out32(s, encode_djsk12_insn(OPC_ST_W, d, j, sk12)); + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVBITSETI_B, xd, xj, uk3)); } -/* Emits the `st.d d, j, sk12` instruction. */ +/* Emits the `xvbitseti.h xd, xj, uk4` instruction. */ static void __attribute__((unused)) -tcg_out_opc_st_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvbitseti_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) { - tcg_out32(s, encode_djsk12_insn(OPC_ST_D, d, j, sk12)); + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVBITSETI_H, xd, xj, uk4)); } -/* Emits the `ld.bu d, j, sk12` instruction. */ +/* Emits the `xvbitseti.w xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ld_bu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvbitseti_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djsk12_insn(OPC_LD_BU, d, j, sk12)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVBITSETI_W, xd, xj, uk5)); } -/* Emits the `ld.hu d, j, sk12` instruction. */ +/* Emits the `xvbitseti.d xd, xj, uk6` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ld_hu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvbitseti_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) { - tcg_out32(s, encode_djsk12_insn(OPC_LD_HU, d, j, sk12)); + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVBITSETI_D, xd, xj, uk6)); } -/* Emits the `ld.wu d, j, sk12` instruction. */ +/* Emits the `xvbitrevi.b xd, xj, uk3` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ld_wu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +tcg_out_opc_xvbitrevi_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) { - tcg_out32(s, encode_djsk12_insn(OPC_LD_WU, d, j, sk12)); + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVBITREVI_B, xd, xj, uk3)); } -/* Emits the `ldx.b d, j, k` instruction. */ +/* Emits the `xvbitrevi.h xd, xj, uk4` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ldx_b(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvbitrevi_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) { - tcg_out32(s, encode_djk_insn(OPC_LDX_B, d, j, k)); + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVBITREVI_H, xd, xj, uk4)); } -/* Emits the `ldx.h d, j, k` instruction. */ +/* Emits the `xvbitrevi.w xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ldx_h(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvbitrevi_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_LDX_H, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVBITREVI_W, xd, xj, uk5)); } -/* Emits the `ldx.w d, j, k` instruction. */ +/* Emits the `xvbitrevi.d xd, xj, uk6` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ldx_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvbitrevi_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) { - tcg_out32(s, encode_djk_insn(OPC_LDX_W, d, j, k)); + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVBITREVI_D, xd, xj, uk6)); } -/* Emits the `ldx.d d, j, k` instruction. */ +/* Emits the `xvslli.b xd, xj, uk3` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ldx_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvslli_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) { - tcg_out32(s, encode_djk_insn(OPC_LDX_D, d, j, k)); + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVSLLI_B, xd, xj, uk3)); } -/* Emits the `stx.b d, j, k` instruction. */ +/* Emits the `xvslli.h xd, xj, uk4` instruction. */ static void __attribute__((unused)) -tcg_out_opc_stx_b(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvslli_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) { - tcg_out32(s, encode_djk_insn(OPC_STX_B, d, j, k)); + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVSLLI_H, xd, xj, uk4)); } -/* Emits the `stx.h d, j, k` instruction. */ +/* Emits the `xvslli.w xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_stx_h(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvslli_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_STX_H, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLLI_W, xd, xj, uk5)); } -/* Emits the `stx.w d, j, k` instruction. */ +/* Emits the `xvslli.d xd, xj, uk6` instruction. */ static void __attribute__((unused)) -tcg_out_opc_stx_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvslli_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) { - tcg_out32(s, encode_djk_insn(OPC_STX_W, d, j, k)); + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVSLLI_D, xd, xj, uk6)); } -/* Emits the `stx.d d, j, k` instruction. */ +/* Emits the `xvsrli.b xd, xj, uk3` instruction. */ static void __attribute__((unused)) -tcg_out_opc_stx_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvsrli_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) { - tcg_out32(s, encode_djk_insn(OPC_STX_D, d, j, k)); + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVSRLI_B, xd, xj, uk3)); } -/* Emits the `ldx.bu d, j, k` instruction. */ +/* Emits the `xvsrli.h xd, xj, uk4` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ldx_bu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvsrli_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) { - tcg_out32(s, encode_djk_insn(OPC_LDX_BU, d, j, k)); + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVSRLI_H, xd, xj, uk4)); } -/* Emits the `ldx.hu d, j, k` instruction. */ +/* Emits the `xvsrli.w xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ldx_hu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvsrli_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_djk_insn(OPC_LDX_HU, d, j, k)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSRLI_W, xd, xj, uk5)); } -/* Emits the `ldx.wu d, j, k` instruction. */ +/* Emits the `xvsrli.d xd, xj, uk6` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ldx_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +tcg_out_opc_xvsrli_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) { - tcg_out32(s, encode_djk_insn(OPC_LDX_WU, d, j, k)); + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVSRLI_D, xd, xj, uk6)); } -/* Emits the `dbar ud15` instruction. */ +/* Emits the `xvsrai.b xd, xj, uk3` instruction. */ static void __attribute__((unused)) -tcg_out_opc_dbar(TCGContext *s, uint32_t ud15) +tcg_out_opc_xvsrai_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) { - tcg_out32(s, encode_ud15_insn(OPC_DBAR, ud15)); + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVSRAI_B, xd, xj, uk3)); } -/* Emits the `jirl d, j, sk16` instruction. */ +/* Emits the `xvsrai.h xd, xj, uk4` instruction. */ static void __attribute__((unused)) -tcg_out_opc_jirl(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +tcg_out_opc_xvsrai_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) { - tcg_out32(s, encode_djsk16_insn(OPC_JIRL, d, j, sk16)); + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVSRAI_H, xd, xj, uk4)); } -/* Emits the `b sd10k16` instruction. */ +/* Emits the `xvsrai.w xd, xj, uk5` instruction. */ static void __attribute__((unused)) -tcg_out_opc_b(TCGContext *s, int32_t sd10k16) +tcg_out_opc_xvsrai_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) { - tcg_out32(s, encode_sd10k16_insn(OPC_B, sd10k16)); + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSRAI_W, xd, xj, uk5)); } -/* Emits the `bl sd10k16` instruction. */ +/* Emits the `xvsrai.d xd, xj, uk6` instruction. */ static void __attribute__((unused)) -tcg_out_opc_bl(TCGContext *s, int32_t sd10k16) +tcg_out_opc_xvsrai_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) { - tcg_out32(s, encode_sd10k16_insn(OPC_BL, sd10k16)); + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVSRAI_D, xd, xj, uk6)); } -/* Emits the `beq d, j, sk16` instruction. */ +/* Emits the `xvbitseli.b xd, xj, uk8` instruction. */ static void __attribute__((unused)) -tcg_out_opc_beq(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +tcg_out_opc_xvbitseli_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) { - tcg_out32(s, encode_djsk16_insn(OPC_BEQ, d, j, sk16)); + tcg_out32(s, encode_xdxjuk8_insn(OPC_XVBITSELI_B, xd, xj, uk8)); } -/* Emits the `bne d, j, sk16` instruction. */ +/* Emits the `xvandi.b xd, xj, uk8` instruction. */ static void __attribute__((unused)) -tcg_out_opc_bne(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +tcg_out_opc_xvandi_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) { - tcg_out32(s, encode_djsk16_insn(OPC_BNE, d, j, sk16)); + tcg_out32(s, encode_xdxjuk8_insn(OPC_XVANDI_B, xd, xj, uk8)); } -/* Emits the `bgt d, j, sk16` instruction. */ +/* Emits the `xvori.b xd, xj, uk8` instruction. */ static void __attribute__((unused)) -tcg_out_opc_bgt(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +tcg_out_opc_xvori_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) { - tcg_out32(s, encode_djsk16_insn(OPC_BGT, d, j, sk16)); + tcg_out32(s, encode_xdxjuk8_insn(OPC_XVORI_B, xd, xj, uk8)); } -/* Emits the `ble d, j, sk16` instruction. */ +/* Emits the `xvxori.b xd, xj, uk8` instruction. */ static void __attribute__((unused)) -tcg_out_opc_ble(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +tcg_out_opc_xvxori_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) { - tcg_out32(s, encode_djsk16_insn(OPC_BLE, d, j, sk16)); + tcg_out32(s, encode_xdxjuk8_insn(OPC_XVXORI_B, xd, xj, uk8)); } -/* Emits the `bgtu d, j, sk16` instruction. */ +/* Emits the `xvnori.b xd, xj, uk8` instruction. */ static void __attribute__((unused)) -tcg_out_opc_bgtu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +tcg_out_opc_xvnori_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) { - tcg_out32(s, encode_djsk16_insn(OPC_BGTU, d, j, sk16)); + tcg_out32(s, encode_xdxjuk8_insn(OPC_XVNORI_B, xd, xj, uk8)); } -/* Emits the `bleu d, j, sk16` instruction. */ +/* Emits the `xvldi xd, sj13` instruction. */ static void __attribute__((unused)) -tcg_out_opc_bleu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +tcg_out_opc_xvldi(TCGContext *s, TCGReg xd, int32_t sj13) { - tcg_out32(s, encode_djsk16_insn(OPC_BLEU, d, j, sk16)); + 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 c2bde44613a..fd731c0c0fc 100644 --- a/tcg/loongarch64/tcg-target-con-set.h +++ b/tcg/loongarch64/tcg-target-con-set.h @@ -15,18 +15,23 @@ * tcg-target-con-str.h; the constraint combination is inclusive or. */ C_O0_I1(r) -C_O0_I2(rZ, r) -C_O0_I2(rZ, rZ) +C_O0_I2(rz, r) +C_O0_I2(r, rz) +C_O0_I2(w, r) +C_O0_I3(r, r, r) C_O1_I1(r, r) -C_O1_I2(r, r, rC) +C_O1_I1(w, r) +C_O1_I1(w, w) +C_O1_I2(r, r, r) 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, ri) -C_O1_I2(r, rZ, rJ) -C_O1_I2(r, rZ, rZ) -C_O1_I4(r, rZ, rJ, rZ, rZ) +C_O1_I2(r, 0, 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, r, 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 6e9ccca3ad7..e5e57452d6d 100644 --- a/tcg/loongarch64/tcg-target-con-str.h +++ b/tcg/loongarch64/tcg-target-con-str.h @@ -14,6 +14,7 @@ * REGS(letter, register_mask) */ REGS('r', ALL_GENERAL_REGS) +REGS('w', ALL_VECTOR_REGS) /* * Define constraint letters for constants: @@ -22,6 +23,6 @@ REGS('r', ALL_GENERAL_REGS) CONST('I', TCG_CT_CONST_S12) 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-has.h b/tcg/loongarch64/tcg-target-has.h new file mode 100644 index 00000000000..32abc6f4574 --- /dev/null +++ b/tcg/loongarch64/tcg-target-has.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2021 WANG Xuerui + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +#include "host/cpuinfo.h" + +/* 64-bit operations */ +#define TCG_TARGET_HAS_extr_i64_i32 1 + +#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_extract_valid(type, ofs, len) 1 +#define TCG_TARGET_deposit_valid(type, ofs, len) 1 + +static inline bool +tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (type == TCG_TYPE_I64 && ofs + len == 32) { + return true; + } + return ofs == 0 && (len == 8 || len == 16); +} +#define TCG_TARGET_sextract_valid tcg_target_sextract_valid + +#endif diff --git a/tcg/loongarch64/tcg-target-mo.h b/tcg/loongarch64/tcg-target-mo.h new file mode 100644 index 00000000000..d35506957ff --- /dev/null +++ b/tcg/loongarch64/tcg-target-mo.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2021 WANG Xuerui + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/loongarch64/tcg-target-opc.h.inc b/tcg/loongarch64/tcg-target-opc.h.inc new file mode 100644 index 00000000000..fd1a40b7fdc --- /dev/null +++ b/tcg/loongarch64/tcg-target-opc.h.inc @@ -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/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index baf5fc38195..10c69211ac5 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -29,9 +29,17 @@ * THE SOFTWARE. */ -#include "../tcg-ldst.c.inc" #include +/* used for function call generation */ +#define TCG_REG_CALL_STACK TCG_REG_SP +#define TCG_TARGET_STACK_ALIGN 16 +#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 + #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "zero", @@ -65,7 +73,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 @@ -102,6 +142,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[] = { @@ -122,19 +171,17 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) 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_S32 0x400 -#define TCG_CT_CONST_U12 0x800 -#define TCG_CT_CONST_C12 0x1000 -#define TCG_CT_CONST_WSZ 0x2000 +#define TCG_CT_CONST_S12 0x100 +#define TCG_CT_CONST_S32 0x200 +#define TCG_CT_CONST_U12 0x400 +#define TCG_CT_CONST_WSZ 0x800 +#define TCG_CT_CONST_VCMP 0x1000 +#define TCG_CT_CONST_VADD 0x2000 #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) { @@ -142,14 +189,12 @@ 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; } - if ((ct & TCG_CT_CONST_ZERO) && val == 0) { - return true; - } if ((ct & TCG_CT_CONST_S12) && val == sextreg(val, 0, 12)) { return true; } @@ -159,12 +204,28 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) if ((ct & TCG_CT_CONST_U12) && val >= 0 && val <= 0xfff) { return true; } - if ((ct & TCG_CT_CONST_C12) && ~val >= 0 && ~val <= 0xfff) { - return true; - } if ((ct & TCG_CT_CONST_WSZ) && val == (type == TCG_TYPE_I32 ? 32 : 64)) { return true; } + if (ct & (TCG_CT_CONST_VCMP | TCG_CT_CONST_VADD)) { + int64_t vec_val = sextract64(val, 0, 8 << vece); + if (ct & TCG_CT_CONST_VCMP) { + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_LE: + case TCG_COND_LT: + return -0x10 <= vec_val && vec_val <= 0x0f; + case TCG_COND_LEU: + case TCG_COND_LTU: + return 0x00 <= vec_val && vec_val <= 0x1f; + default: + return false; + } + } + if ((ct & TCG_CT_CONST_VADD) && -0x1f <= vec_val && vec_val <= 0x1f) { + return true; + } + } return false; } @@ -240,7 +301,7 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, * TCG intrinsics */ -static void tcg_out_mb(TCGContext *s, TCGArg a0) +static void tcg_out_mb(TCGContext *s, unsigned a0) { /* Baseline LoongArch only has the full barrier, unfortunately. */ tcg_out_opc_dbar(s, 0); @@ -254,11 +315,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(); @@ -314,8 +394,7 @@ 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; + intptr_t src_rx, pc_offset; tcg_target_long hi12, hi32, hi52; /* Value fits in signed i32. */ @@ -325,24 +404,23 @@ 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; } @@ -468,28 +546,6 @@ 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) -{ - if (c2) { - /* - * Fast path: semantics already satisfied due to constraint and - * insn behavior, single instruction is enough. - */ - tcg_debug_assert(a2 == (is_32bit ? 32 : 64)); - /* all clz/ctz insns belong to DJ-format */ - tcg_out32(s, encode_dj_insn(opc, a0, a1)); - return; - } - - tcg_out32(s, encode_dj_insn(opc, TCG_REG_TMP0, a1)); - /* a0 = a1 ? REG_TMP0 : a2 */ - tcg_out_opc_maskeqz(s, TCG_REG_TMP0, TCG_REG_TMP0, a1); - tcg_out_opc_masknez(s, a0, a2, a1); - tcg_out_opc_or(s, a0, TCG_REG_TMP0, a0); -} - #define SETCOND_INV TCG_TARGET_NB_REGS #define SETCOND_NEZ (SETCOND_INV << 1) #define SETCOND_FLAGS (SETCOND_INV | SETCOND_NEZ) @@ -584,21 +640,35 @@ static int tcg_out_setcond_int(TCGContext *s, TCGCond cond, TCGReg ret, default: g_assert_not_reached(); - break; } return ret | flags; } static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg arg1, tcg_target_long arg2, bool c2) + TCGReg arg1, tcg_target_long arg2, + bool c2, bool neg) { int tmpflags = tcg_out_setcond_int(s, cond, ret, arg1, arg2, c2); + TCGReg tmp = tmpflags & ~SETCOND_FLAGS; - if (tmpflags != ret) { - TCGReg tmp = tmpflags & ~SETCOND_FLAGS; - + if (neg) { + /* If intermediate result is zero/non-zero: test != 0. */ + if (tmpflags & SETCOND_NEZ) { + tcg_out_opc_sltu(s, ret, TCG_REG_ZERO, tmp); + tmp = ret; + } + /* Produce the 0/-1 result. */ + if (tmpflags & SETCOND_INV) { + tcg_out_opc_addi_d(s, ret, tmp, -1); + } else { + tcg_out_opc_sub_d(s, ret, TCG_REG_ZERO, tmp); + } + } else { switch (tmpflags & SETCOND_FLAGS) { + case 0: + tcg_debug_assert(tmp == ret); + break; case SETCOND_INV: /* Intermediate result is boolean: simply invert. */ tcg_out_opc_xori(s, ret, tmp, 1); @@ -617,11 +687,47 @@ static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, } } -static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg c1, tcg_target_long c2, bool const2, - TCGReg v1, TCGReg v2) +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, cond, dest, arg1, arg2, false, false); +} + +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, cond, dest, arg1, arg2, true, false); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, cond, dest, arg1, arg2, false, true); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, cond, dest, arg1, arg2, true, true); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; + +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg v1, bool const_v1, TCGArg v2, bool const_v2) { - int tmpflags = tcg_out_setcond_int(s, cond, TCG_REG_TMP0, c1, c2, const2); + int tmpflags = tcg_out_setcond_int(s, cond, TCG_REG_TMP0, c1, c2, const_c2); TCGReg t; /* Standardize the test below to t != 0. */ @@ -641,10 +747,21 @@ static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, } } +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, rJ, rz, rz), + .out = tgen_movcond, +}; + /* * Branch helpers */ +static void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + tcg_out_reloc(s, s->code_ptr, R_LOONGARCH_BR_SD10K16, l, 0); + tcg_out_opc_b(s, 0); +} + static const struct { LoongArchInsn op; bool swap; @@ -661,8 +778,8 @@ static const struct { [TCG_COND_GTU] = { OPC_BGTU, false } }; -static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, - TCGReg arg2, TCGLabel *l) +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, TCGReg arg2, TCGLabel *l) { LoongArchInsn op = tcg_brcond_to_loongarch[cond].op; @@ -679,6 +796,11 @@ static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, tcg_out32(s, encode_djsk16_insn(op, arg1, arg2, 0)); } +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rz), + .out_rr = tgen_brcond, +}; + static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *arg, bool tail) { TCGReg link = tail ? TCG_REG_ZERO : TCG_REG_RA; @@ -751,23 +873,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, @@ -838,8 +1032,8 @@ bool tcg_target_has_memory_bswap(MemOp memop) #define MIN_TLB_MASK_TABLE_OFS -(1 << 11) /* - * For softmmu, perform the TLB load and compare. - * For useronly, perform any required alignment tests. + * 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. */ @@ -855,76 +1049,77 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, h->aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, false); a_bits = h->aa.align; -#ifdef CONFIG_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)); + 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); - /* - * 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); - } - 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; + ldst->addr_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, + 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. */ + 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)); /* - * 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. + * 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. */ - tcg_out_opc_bstrpick_d(s, TCG_REG_TMP1, addr_reg, 0, a_bits - 1); + 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); + } + tcg_out_opc_bstrins_d(s, TCG_REG_TMP1, TCG_REG_ZERO, + a_bits, TARGET_PAGE_BITS - 1); + /* Compare masked address with the TLB entry. */ ldst->label_ptr[0] = s->code_ptr; - tcg_out_opc_bne(s, TCG_REG_TMP1, TCG_REG_ZERO, 0); - } + tcg_out_opc_bne(s, TCG_REG_TMP0, TCG_REG_TMP1, 0); - h->index = USE_GUEST_BASE ? TCG_GUEST_BASE_REG : TCG_REG_ZERO; -#endif + h->index = TCG_REG_TMP2; + } else { + if (a_bits) { + ldst = new_ldst_label(s); + + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addr_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; + } if (addr_type == TCG_TYPE_I32) { h->base = TCG_REG_TMP0; @@ -972,22 +1167,27 @@ static void tcg_out_qemu_ld_indexed(TCGContext *s, MemOp opc, TCGType type, } } -static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; ldst = prepare_host_addr(s, &h, addr_reg, oi, true); - tcg_out_qemu_ld_indexed(s, get_memop(oi), data_type, data_reg, h); + tcg_out_qemu_ld_indexed(s, get_memop(oi), type, data_reg, h); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = data_reg; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + static void tcg_out_qemu_st_indexed(TCGContext *s, MemOp opc, TCGReg rd, HostAddress h) { @@ -1012,8 +1212,8 @@ static void tcg_out_qemu_st_indexed(TCGContext *s, MemOp opc, } } -static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; @@ -1022,12 +1222,86 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, tcg_out_qemu_st_indexed(s, get_memop(oi), data_reg, h); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = data_reg; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out = tgen_qemu_st, +}; + +static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg data_lo, TCGReg data_hi, + TCGReg addr_reg, MemOpIdx oi, bool is_ld) +{ + TCGLabelQemuLdst *ldst; + HostAddress h; + + ldst = prepare_host_addr(s, &h, addr_reg, oi, is_ld); + + if (h.aa.atom == MO_128) { + /* + * Use VLDX/VSTX when 128-bit atomicity is required. + * If address is aligned to 16-bytes, the 128-bit load/store is atomic. + */ + if (is_ld) { + tcg_out_opc_vldx(s, TCG_VEC_TMP0, h.base, h.index); + tcg_out_opc_vpickve2gr_d(s, data_lo, TCG_VEC_TMP0, 0); + tcg_out_opc_vpickve2gr_d(s, data_hi, TCG_VEC_TMP0, 1); + } else { + tcg_out_opc_vinsgr2vr_d(s, TCG_VEC_TMP0, data_lo, 0); + tcg_out_opc_vinsgr2vr_d(s, TCG_VEC_TMP0, data_hi, 1); + tcg_out_opc_vstx(s, TCG_VEC_TMP0, h.base, h.index); + } + } else { + /* Otherwise use a pair of LD/ST. */ + TCGReg base = h.base; + if (h.index != TCG_REG_ZERO) { + base = TCG_REG_TMP0; + tcg_out_opc_add_d(s, base, h.base, h.index); + } + if (is_ld) { + tcg_debug_assert(base != data_lo); + tcg_out_opc_ld_d(s, data_lo, base, 0); + tcg_out_opc_ld_d(s, data_hi, base, 8); + } else { + tcg_out_opc_st_d(s, data_lo, base, 0); + tcg_out_opc_st_d(s, data_hi, base, 8); + } + } + + if (ldst) { + ldst->type = TCG_TYPE_I128; + ldst->datalo_reg = data_lo; + ldst->datahi_reg = data_hi; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } +} + +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr_reg, MemOpIdx oi) +{ + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr_reg, oi, true); +} + +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = C_N2_I1(r, r, r), + .out = tgen_qemu_ld2, +}; + +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr_reg, MemOpIdx oi) +{ + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr_reg, oi, false); +} + +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = C_O0_I3(r, r, r), + .out = tgen_qemu_st2, +}; + /* * Entry-points */ @@ -1065,6 +1339,11 @@ static void tcg_out_goto_tb(TCGContext *s, int which) set_jmp_reset_offset(s, which); } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_opc_jirl(s, TCG_REG_ZERO, a0, 0); +} + void tb_target_set_jmp_target(const TranslationBlock *tb, int n, uintptr_t jmp_rx, uintptr_t jmp_rw) { @@ -1085,550 +1364,1228 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, 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]) + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) { - TCGArg a0 = args[0]; - TCGArg a1 = args[1]; - TCGArg a2 = args[2]; - int c2 = const_args[2]; + if (type == TCG_TYPE_I32) { + tcg_out_opc_add_w(s, a0, a1, a2); + } else { + tcg_out_opc_add_d(s, a0, a1, a2); + } +} - switch (opc) { - case INDEX_op_mb: - tcg_out_mb(s, a0); - break; +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_add, + .out_rri = tcg_out_addi, +}; - case INDEX_op_goto_ptr: - tcg_out_opc_jirl(s, TCG_REG_ZERO, a0, 0); - break; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_br: - tcg_out_reloc(s, s->code_ptr, R_LOONGARCH_BR_SD10K16, arg_label(a0), - 0); - tcg_out_opc_b(s, 0); - break; +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - tcg_out_brcond(s, a2, a0, a1, arg_label(args[3])); - break; +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_extrh_i64_i32: - tcg_out_opc_srai_d(s, a0, a1, 32); - break; +static void tcg_out_set_carry(TCGContext *s) +{ + g_assert_not_reached(); +} - case INDEX_op_not_i32: - case INDEX_op_not_i64: - tcg_out_opc_nor(s, a0, a1, TCG_REG_ZERO); - break; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_and(s, a0, a1, a2); +} - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: - if (c2) { - tcg_out_opc_ori(s, a0, a1, a2); - tcg_out_opc_nor(s, a0, a0, TCG_REG_ZERO); - } else { - tcg_out_opc_nor(s, a0, a1, a2); - } - break; +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_andi(s, a0, a1, a2); +} - case INDEX_op_andc_i32: - case INDEX_op_andc_i64: - if (c2) { - /* guaranteed to fit due to constraint */ - tcg_out_opc_andi(s, a0, a1, ~a2); - } else { - tcg_out_opc_andn(s, a0, a1, a2); - } - break; +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, rU), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; - case INDEX_op_orc_i32: - case INDEX_op_orc_i64: - if (c2) { - /* guaranteed to fit due to constraint */ - tcg_out_opc_ori(s, a0, a1, ~a2); - } else { - tcg_out_opc_orn(s, a0, a1, a2); - } - break; +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_andn(s, a0, a1, a2); +} - case INDEX_op_and_i32: - case INDEX_op_and_i64: - if (c2) { - tcg_out_opc_andi(s, a0, a1, a2); - } else { - tcg_out_opc_and(s, a0, a1, a2); - } - break; +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_andc, +}; - case INDEX_op_or_i32: - case INDEX_op_or_i64: - if (c2) { - tcg_out_opc_ori(s, a0, a1, a2); - } else { - tcg_out_opc_or(s, a0, a1, a2); - } - break; +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + /* a2 is constrained to exactly the type width. */ + if (type == TCG_TYPE_I32) { + tcg_out_opc_clz_w(s, a0, a1); + } else { + tcg_out_opc_clz_d(s, a0, a1); + } +} - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: - if (c2) { - tcg_out_opc_xori(s, a0, a1, a2); - } else { - tcg_out_opc_xor(s, a0, a1, a2); - } - break; +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_clzi(s, type, TCG_REG_TMP0, a1, /* ignored */ 0); + /* a0 = a1 ? REG_TMP0 : a2 */ + tcg_out_opc_maskeqz(s, TCG_REG_TMP0, TCG_REG_TMP0, a1); + tcg_out_opc_masknez(s, a0, a2, a1); + tcg_out_opc_or(s, a0, a0, TCG_REG_TMP0); +} - case INDEX_op_extract_i32: - tcg_out_opc_bstrpick_w(s, a0, a1, a2, a2 + args[3] - 1); - break; - case INDEX_op_extract_i64: - tcg_out_opc_bstrpick_d(s, a0, a1, a2, a2 + args[3] - 1); - break; +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_O1_I2(r, r, rW), + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; - case INDEX_op_deposit_i32: - tcg_out_opc_bstrins_w(s, a0, a2, args[3], args[3] + args[4] - 1); - break; - case INDEX_op_deposit_i64: - tcg_out_opc_bstrins_d(s, a0, a2, args[3], args[3] + args[4] - 1); - break; +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: - tcg_out_opc_revb_2h(s, a0, a1); - if (a2 & TCG_BSWAP_OS) { - 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); - } - break; +static void tgen_ctzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + /* a2 is constrained to exactly the type width. */ + if (type == TCG_TYPE_I32) { + tcg_out_opc_ctz_w(s, a0, a1); + } else { + tcg_out_opc_ctz_d(s, a0, a1); + } +} - case INDEX_op_bswap32_i32: - /* All 32-bit values are computed sign-extended in the register. */ - a2 = TCG_BSWAP_OS; - /* fallthrough */ - case INDEX_op_bswap32_i64: - tcg_out_opc_revb_2w(s, a0, a1); - if (a2 & TCG_BSWAP_OS) { - tcg_out_ext32s(s, a0, a0); - } else if ((a2 & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - tcg_out_ext32u(s, a0, a0); - } - break; +static void tgen_ctz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_ctzi(s, type, TCG_REG_TMP0, a1, /* ignored */ 0); + /* a0 = a1 ? REG_TMP0 : a2 */ + tcg_out_opc_maskeqz(s, TCG_REG_TMP0, TCG_REG_TMP0, a1); + tcg_out_opc_masknez(s, a0, a2, a1); + tcg_out_opc_or(s, a0, a0, TCG_REG_TMP0); +} - case INDEX_op_bswap64_i64: - tcg_out_opc_revb_d(s, a0, a1); - break; +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_O1_I2(r, r, rW), + .out_rrr = tgen_ctz, + .out_rri = tgen_ctzi, +}; - case INDEX_op_clz_i32: - tcg_out_clzctz(s, OPC_CLZ_W, a0, a1, a2, c2, true); - break; - case INDEX_op_clz_i64: - tcg_out_clzctz(s, OPC_CLZ_D, a0, a1, a2, c2, false); - break; +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_div_w(s, a0, a1, a2); + } else { + tcg_out_opc_div_d(s, a0, a1, a2); + } +} - case INDEX_op_ctz_i32: - tcg_out_clzctz(s, OPC_CTZ_W, a0, a1, a2, c2, true); - break; - case INDEX_op_ctz_i64: - tcg_out_clzctz(s, OPC_CTZ_D, a0, a1, a2, c2, false); - break; +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divs, +}; - case INDEX_op_shl_i32: - if (c2) { - tcg_out_opc_slli_w(s, a0, a1, a2 & 0x1f); - } else { - tcg_out_opc_sll_w(s, a0, a1, a2); - } - break; - case INDEX_op_shl_i64: - if (c2) { - tcg_out_opc_slli_d(s, a0, a1, a2 & 0x3f); - } else { - tcg_out_opc_sll_d(s, a0, a1, a2); - } - break; +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_shr_i32: - if (c2) { - tcg_out_opc_srli_w(s, a0, a1, a2 & 0x1f); - } else { - tcg_out_opc_srl_w(s, a0, a1, a2); - } - break; - case INDEX_op_shr_i64: - if (c2) { - tcg_out_opc_srli_d(s, a0, a1, a2 & 0x3f); - } else { - tcg_out_opc_srl_d(s, a0, a1, a2); - } - break; +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_div_wu(s, a0, a1, a2); + } else { + tcg_out_opc_div_du(s, a0, a1, a2); + } +} - case INDEX_op_sar_i32: - if (c2) { - tcg_out_opc_srai_w(s, a0, a1, a2 & 0x1f); - } else { - tcg_out_opc_sra_w(s, a0, a1, a2); - } - break; - case INDEX_op_sar_i64: - if (c2) { - tcg_out_opc_srai_d(s, a0, a1, a2 & 0x3f); - } else { - tcg_out_opc_sra_d(s, a0, a1, a2); - } - break; +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divu, +}; - case INDEX_op_rotl_i32: - /* transform into equivalent rotr/rotri */ - if (c2) { - tcg_out_opc_rotri_w(s, a0, a1, (32 - a2) & 0x1f); - } else { - tcg_out_opc_sub_w(s, TCG_REG_TMP0, TCG_REG_ZERO, a2); - tcg_out_opc_rotr_w(s, a0, a1, TCG_REG_TMP0); - } - break; - case INDEX_op_rotl_i64: - /* transform into equivalent rotr/rotri */ - if (c2) { - tcg_out_opc_rotri_d(s, a0, a1, (64 - a2) & 0x3f); - } else { - tcg_out_opc_sub_w(s, TCG_REG_TMP0, TCG_REG_ZERO, a2); - tcg_out_opc_rotr_d(s, a0, a1, TCG_REG_TMP0); - } - break; +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_rotr_i32: - if (c2) { - tcg_out_opc_rotri_w(s, a0, a1, a2 & 0x1f); - } else { - tcg_out_opc_rotr_w(s, a0, a1, a2); - } - break; - case INDEX_op_rotr_i64: - if (c2) { - tcg_out_opc_rotri_d(s, a0, a1, a2 & 0x3f); - } else { - tcg_out_opc_rotr_d(s, a0, a1, a2); - } - break; +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_add_i32: - if (c2) { - 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_addi(s, TCG_TYPE_I64, a0, a1, a2); - } else { - tcg_out_opc_add_d(s, a0, a1, a2); - } - break; +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_opc_srai_d(s, a0, a1, 32); +} - case INDEX_op_sub_i32: - if (c2) { - 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_addi(s, TCG_TYPE_I64, a0, a1, -a2); - } else { - tcg_out_opc_sub_d(s, a0, a1, a2); - } - break; +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; - case INDEX_op_mul_i32: +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { tcg_out_opc_mul_w(s, a0, a1, a2); - break; - case INDEX_op_mul_i64: + } else { tcg_out_opc_mul_d(s, a0, a1, a2); - break; + } +} - case INDEX_op_mulsh_i32: +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mul, +}; + +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_mulsh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { tcg_out_opc_mulh_w(s, a0, a1, a2); - break; - case INDEX_op_mulsh_i64: + } else { tcg_out_opc_mulh_d(s, a0, a1, a2); - break; + } +} - case INDEX_op_muluh_i32: +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mulsh, +}; + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_muluh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { tcg_out_opc_mulh_wu(s, a0, a1, a2); - break; - case INDEX_op_muluh_i64: + } else { tcg_out_opc_mulh_du(s, a0, a1, a2); - break; + } +} - case INDEX_op_div_i32: - tcg_out_opc_div_w(s, a0, a1, a2); - break; - case INDEX_op_div_i64: - tcg_out_opc_div_d(s, a0, a1, a2); - break; +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_muluh, +}; - case INDEX_op_divu_i32: - tcg_out_opc_div_wu(s, a0, a1, a2); - break; - case INDEX_op_divu_i64: - tcg_out_opc_div_du(s, a0, a1, a2); - break; +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_rem_i32: +static void tgen_nor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_nor(s, a0, a1, a2); +} + +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_nor, +}; + +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_or(s, a0, a1, a2); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_ori(s, a0, a1, a2); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rU), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + +static void tgen_orc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_orn(s, a0, a1, a2); +} + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_orc, +}; + +static void tgen_rems(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { tcg_out_opc_mod_w(s, a0, a1, a2); - break; - case INDEX_op_rem_i64: + } else { tcg_out_opc_mod_d(s, a0, a1, a2); - break; + } +} + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_rems, +}; - case INDEX_op_remu_i32: +static void tgen_remu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { tcg_out_opc_mod_wu(s, a0, a1, a2); - break; - case INDEX_op_remu_i64: + } else { tcg_out_opc_mod_du(s, a0, a1, a2); - break; + } +} - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - tcg_out_setcond(s, args[3], a0, a1, a2, c2); - break; +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_remu, +}; - 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; +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - tcg_out_ldst(s, OPC_LD_B, a0, a1, a2); - break; - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - tcg_out_ldst(s, OPC_LD_BU, a0, a1, a2); - break; - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - tcg_out_ldst(s, OPC_LD_H, a0, a1, a2); - break; - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - tcg_out_ldst(s, OPC_LD_HU, a0, a1, a2); +static void tgen_rotr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_rotr_w(s, a0, a1, a2); + } else { + tcg_out_opc_rotr_d(s, a0, a1, a2); + } +} + +static void tgen_rotri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_rotri_w(s, a0, a1, a2 & 0x1f); + } else { + tcg_out_opc_rotri_d(s, a0, a1, a2 & 0x3f); + } +} + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_rotr, + .out_rri = tgen_rotri, +}; + +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_sra_w(s, a0, a1, a2); + } else { + tcg_out_opc_sra_d(s, a0, a1, a2); + } +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_srai_w(s, a0, a1, a2 & 0x1f); + } else { + tcg_out_opc_srai_d(s, a0, a1, a2 & 0x3f); + } +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_sll_w(s, a0, a1, a2); + } else { + tcg_out_opc_sll_d(s, a0, a1, a2); + } +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_slli_w(s, a0, a1, a2 & 0x1f); + } else { + tcg_out_opc_slli_d(s, a0, a1, a2 & 0x3f); + } +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_srl_w(s, a0, a1, a2); + } else { + tcg_out_opc_srl_d(s, a0, a1, a2); + } +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_srli_w(s, a0, a1, a2 & 0x1f); + } else { + tcg_out_opc_srli_d(s, a0, a1, a2 & 0x3f); + } +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_sub_w(s, a0, a1, a2); + } else { + tcg_out_opc_sub_d(s, a0, a1, a2); + } +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sub, +}; + +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + g_assert_not_reached(); +} + +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_xor(s, a0, a1, a2); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_xori(s, a0, a1, a2); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rU), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_opc_revb_2h(s, a0, a1); + if (flags & TCG_BSWAP_OS) { + tcg_out_ext16s(s, TCG_TYPE_REG, a0, a0); + } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + tcg_out_ext16u(s, a0, a0); + } +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap16, +}; + +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_opc_revb_2w(s, a0, a1); + + /* All 32-bit values are computed sign-extended in the register. */ + if (type == TCG_TYPE_I32 || (flags & TCG_BSWAP_OS)) { + tcg_out_ext32s(s, a0, a0); + } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + tcg_out_ext32u(s, a0, a0); + } +} + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap32, +}; + +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_opc_revb_d(s, a0, a1); +} + +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap64, +}; + +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_sub(s, type, a0, TCG_REG_ZERO, a1); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_nor(s, type, a0, a1, TCG_REG_ZERO); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_bstrins_w(s, a0, a2, ofs, ofs + len - 1); + } else { + tcg_out_opc_bstrins_d(s, a0, a2, ofs, ofs + len - 1); + } +} + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(r, 0, rz), + .out_rrr = tgen_deposit, +}; + +static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0 && len <= 12) { + tcg_out_opc_andi(s, a0, a1, (1 << len) - 1); + } else if (type == TCG_TYPE_I32) { + tcg_out_opc_bstrpick_w(s, a0, a1, ofs, ofs + len - 1); + } else { + tcg_out_opc_bstrpick_d(s, a0, a1, ofs, ofs + len - 1); + } +} + +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; + +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8s(s, type, a0, a1); + return; + case 16: + tcg_out_ext16s(s, type, a0, a1); + return; + case 32: + tcg_out_ext32s(s, a0, a1); + return; + } + } else if (ofs + len == 32) { + tcg_out_opc_srai_w(s, a0, a1, ofs); + return; + } + g_assert_not_reached(); +} + +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; + +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LD_BU, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LD_B, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LD_HU, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LD_H, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LD_WU, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LD_W, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; + +static void tgen_st8_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_ST_B, data, base, offset); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st8_r, +}; + +static void tgen_st16_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_ST_H, data, base, offset); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st16_r, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tcg_out_st, +}; + +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 INDEX_op_ld_i32: - case INDEX_op_ld32s_i64: - tcg_out_ldst(s, OPC_LD_W, a0, a1, a2); + 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 INDEX_op_ld32u_i64: - tcg_out_ldst(s, OPC_LD_WU, a0, a1, a2); + 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 INDEX_op_ld_i64: - tcg_out_ldst(s, OPC_LD_D, a0, a1, a2); + 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; +} - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - tcg_out_ldst(s, OPC_ST_B, a0, a1, a2); - break; - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - tcg_out_ldst(s, OPC_ST_H, a0, a1, a2); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - tcg_out_ldst(s, OPC_ST_W, a0, a1, a2); +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_st_i64: - tcg_out_ldst(s, OPC_ST_D, a0, a1, a2); + 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); + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_LE: + case TCG_COND_LT: + insn = cmp_vec_imm_insn[cond][lasx][vece]; + tcg_out32(s, encode_vdvjsk5_insn(insn, a0, a1, value)); + break; + case TCG_COND_LEU: + case TCG_COND_LTU: + insn = cmp_vec_imm_insn[cond][lasx][vece]; + tcg_out32(s, encode_vdvjuk5_insn(insn, a0, a1, value)); + break; + default: + g_assert_not_reached(); + } + break; + } - 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); + 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_qemu_ld_a32_i64: - case INDEX_op_qemu_ld_a64_i64: - tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I64); + case INDEX_op_dupm_vec: + tcg_out_dupm_vec(s, type, vece, a0, a1, a2); break; - 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); + default: + g_assert_not_reached(); + vdvjvk: + tcg_out32(s, encode_vdvjvk_insn(insn, a0, a1, a2)); 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); + 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; + } +} - 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: +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: - g_assert_not_reached(); + return 0; } } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +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, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - 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_brcond_i32: - case INDEX_op_brcond_i64: - return C_O0_I2(rZ, rZ); - - case INDEX_op_ext8s_i32: - 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_extu_i32_i64: - case INDEX_op_extrl_i64_i32: - case INDEX_op_extrh_i64_i32: - case INDEX_op_ext_i32_i64: - case INDEX_op_not_i32: - case INDEX_op_not_i64: - case INDEX_op_extract_i32: - case INDEX_op_extract_i64: - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: - case INDEX_op_bswap64_i64: - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - case INDEX_op_ld32s_i64: - 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_andc_i32: - case INDEX_op_andc_i64: - case INDEX_op_orc_i32: - case INDEX_op_orc_i64: - /* - * LoongArch insns for these ops don't have reg-imm forms, but we - * can express using andi/ori if ~constant satisfies - * TCG_CT_CONST_U12. - */ - return C_O1_I2(r, r, rC); - - case INDEX_op_shl_i32: - case INDEX_op_shl_i64: - case INDEX_op_shr_i32: - case INDEX_op_shr_i64: - case INDEX_op_sar_i32: - case INDEX_op_sar_i64: - case INDEX_op_rotl_i32: - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i32: - case INDEX_op_rotr_i64: - 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, rJ); - - case INDEX_op_and_i32: - case INDEX_op_and_i64: - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: - case INDEX_op_or_i32: - case INDEX_op_or_i64: - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: - /* LoongArch reg-imm bitops have their imms ZERO-extended */ - return C_O1_I2(r, r, rU); - - case INDEX_op_clz_i32: - case INDEX_op_clz_i64: - case INDEX_op_ctz_i32: - case INDEX_op_ctz_i64: - return C_O1_I2(r, r, rW); - - 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: - case INDEX_op_setcond_i64: - return C_O1_I2(r, rZ, rJ); - - case INDEX_op_mul_i32: - case INDEX_op_mul_i64: - case INDEX_op_mulsh_i32: - case INDEX_op_mulsh_i64: - case INDEX_op_muluh_i32: - case INDEX_op_muluh_i64: - case INDEX_op_div_i32: - case INDEX_op_div_i64: - case INDEX_op_divu_i32: - case INDEX_op_divu_i64: - case INDEX_op_rem_i32: - case INDEX_op_rem_i64: - case INDEX_op_remu_i32: - 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(); + return C_NotImplemented; } } @@ -1672,12 +2629,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]); @@ -1698,6 +2653,19 @@ 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_out_nop_fill(tcg_insn_unit *p, int count) +{ + for (int i = 0; i < count; ++i) { + /* Canonical nop is andi r0,r0,0 */ + p[i] = OPC_ANDI; + } +} + static void tcg_target_init(TCGContext *s) { unsigned long hwcap = qemu_getauxval(AT_HWCAP); @@ -1711,7 +2679,7 @@ static void tcg_target_init(TCGContext *s) 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); @@ -1723,6 +2691,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); @@ -1731,6 +2715,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 559be671866..6a206fb97e6 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -30,7 +30,7 @@ #define LOONGARCH_TCG_TARGET_H #define TCG_TARGET_INSN_UNIT_SIZE 4 -#define TCG_TARGET_NB_REGS 32 +#define TCG_TARGET_NB_REGS 64 #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) @@ -68,101 +68,23 @@ 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_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 1 -#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 -#define TCG_TARGET_HAS_rot_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_add2_i32 0 -#define TCG_TARGET_HAS_sub2_i32 0 -#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_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 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 -#define TCG_TARGET_HAS_nand_i32 0 -#define TCG_TARGET_HAS_nor_i32 1 -#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_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 1 -#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 -#define TCG_TARGET_HAS_rot_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_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 1 -#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 -#define TCG_TARGET_HAS_nand_i64 0 -#define TCG_TARGET_HAS_nor_i64 1 -#define TCG_TARGET_HAS_clz_i64 1 -#define TCG_TARGET_HAS_ctz_i64 1 -#define TCG_TARGET_HAS_ctpop_i64 0 -#define TCG_TARGET_HAS_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 -#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_qemu_ldst_i128 0 - -#define TCG_TARGET_DEFAULT_MO (0) - -#define TCG_TARGET_NEED_LDST_LABELS +#define TCG_REG_ZERO TCG_REG_ZERO #endif /* LOONGARCH_TCG_TARGET_H */ diff --git a/tcg/meson.build b/tcg/meson.build index 0014dca7d4f..706a6eb260e 100644 --- a/tcg/meson.build +++ b/tcg/meson.build @@ -1,4 +1,4 @@ -if not get_option('tcg').allowed() +if not have_tcg subdir_done() endif @@ -22,24 +22,10 @@ if get_option('tcg_interpreter') tcg_ss.add(files('tci.c')) endif -tcg_ss = tcg_ss.apply(config_targetos, strict: false) - -libtcg_user = static_library('tcg_user', - tcg_ss.sources() + genh, - name_suffix: 'fa', - c_args: '-DCONFIG_USER_ONLY', - build_by_default: have_user) - -tcg_user = declare_dependency(link_with: libtcg_user, - dependencies: tcg_ss.dependencies()) -user_ss.add(tcg_user) - -libtcg_softmmu = static_library('tcg_softmmu', - tcg_ss.sources() + genh, - name_suffix: 'fa', - c_args: '-DCONFIG_SOFTMMU', - build_by_default: have_system) +tcg_ss.add(when: libdw, if_true: files('debuginfo.c')) +if host_os == 'linux' + tcg_ss.add(files('perf.c')) +endif -tcg_softmmu = declare_dependency(link_with: libtcg_softmmu, - dependencies: tcg_ss.dependencies()) -system_ss.add(tcg_softmmu) +user_ss.add_all(tcg_ss) +system_ss.add_all(tcg_ss) diff --git a/tcg/mips/tcg-target-con-set.h b/tcg/mips/tcg-target-con-set.h index 864034f4687..5304691dc18 100644 --- a/tcg/mips/tcg-target-con-set.h +++ b/tcg/mips/tcg-target-con-set.h @@ -10,24 +10,21 @@ * tcg-target-con-str.h; the constraint combination is inclusive or. */ C_O0_I1(r) -C_O0_I2(rZ, r) -C_O0_I2(rZ, rZ) -C_O0_I3(rZ, r, r) -C_O0_I3(rZ, rZ, r) -C_O0_I4(rZ, rZ, rZ, rZ) -C_O0_I4(rZ, rZ, r, r) +C_O0_I2(r, rz) +C_O0_I2(rz, r) +C_O0_I3(rz, rz, r) +C_O0_I4(r, r, rz, rz) C_O1_I1(r, r) -C_O1_I2(r, 0, rZ) +C_O1_I2(r, 0, rz) C_O1_I2(r, r, r) C_O1_I2(r, r, ri) C_O1_I2(r, r, rI) C_O1_I2(r, r, rIK) C_O1_I2(r, r, rJ) -C_O1_I2(r, r, rWZ) -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_O1_I2(r, r, rz) +C_O1_I2(r, r, rzW) +C_O1_I4(r, r, rz, rz, 0) +C_O1_I4(r, r, rz, rz, rz) +C_O1_I4(r, r, r, rz, rz) 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 413c280a7a9..db2b225e4a2 100644 --- a/tcg/mips/tcg-target-con-str.h +++ b/tcg/mips/tcg-target-con-str.h @@ -17,6 +17,4 @@ REGS('r', ALL_GENERAL_REGS) CONST('I', TCG_CT_CONST_U16) CONST('J', TCG_CT_CONST_S16) CONST('K', TCG_CT_CONST_P2M1) -CONST('N', TCG_CT_CONST_N16) CONST('W', TCG_CT_CONST_WSZ) -CONST('Z', TCG_CT_CONST_ZERO) diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h new file mode 100644 index 00000000000..b9eb3385288 --- /dev/null +++ b/tcg/mips/tcg-target-has.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2008-2009 Arnaud Patard + * Copyright (c) 2009 Aurelien Jarno + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +/* MOVN/MOVZ instructions detection */ +#if (defined(__mips_isa_rev) && (__mips_isa_rev >= 1)) || \ + defined(_MIPS_ARCH_LOONGSON2E) || defined(_MIPS_ARCH_LOONGSON2F) || \ + defined(_MIPS_ARCH_MIPS4) +#define use_movnz_instructions 1 +#else +extern bool use_movnz_instructions; +#endif + +/* MIPS32 instruction set detection */ +#if defined(__mips_isa_rev) && (__mips_isa_rev >= 1) +#define use_mips32_instructions 1 +#else +extern bool use_mips32_instructions; +#endif + +/* MIPS32R2 instruction set detection */ +#if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) +#define use_mips32r2_instructions 1 +#else +extern bool use_mips32r2_instructions; +#endif + +/* MIPS32R6 instruction set detection */ +#if defined(__mips_isa_rev) && (__mips_isa_rev >= 6) +#define use_mips32r6_instructions 1 +#else +#define use_mips32r6_instructions 0 +#endif + +/* optional instructions */ +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_extr_i64_i32 1 +#define TCG_TARGET_HAS_ext32s_i64 1 +#define TCG_TARGET_HAS_ext32u_i64 1 +#endif + +/* optional instructions detected at runtime */ +#define TCG_TARGET_HAS_qemu_ldst_i128 0 +#define TCG_TARGET_HAS_tst 0 + +#define TCG_TARGET_extract_valid(type, ofs, len) use_mips32r2_instructions +#define TCG_TARGET_deposit_valid(type, ofs, len) use_mips32r2_instructions + +static inline bool +tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + case 16: + return use_mips32r2_instructions; + case 32: + return type == TCG_TYPE_I64; + } + } + return false; +} +#define TCG_TARGET_sextract_valid tcg_target_sextract_valid + +#endif diff --git a/tcg/mips/tcg-target-mo.h b/tcg/mips/tcg-target-mo.h new file mode 100644 index 00000000000..50cefc222d9 --- /dev/null +++ b/tcg/mips/tcg-target-mo.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2008-2009 Arnaud Patard + * Copyright (c) 2009 Aurelien Jarno + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/mips/tcg-target-opc.h.inc b/tcg/mips/tcg-target-opc.h.inc new file mode 100644 index 00000000000..84e777bfe53 --- /dev/null +++ b/tcg/mips/tcg-target-opc.h.inc @@ -0,0 +1 @@ +/* No target specific opcodes. */ diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 9faa8bdf0bf..400eafbab4b 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -24,8 +24,19 @@ * THE SOFTWARE. */ -#include "../tcg-ldst.c.inc" -#include "../tcg-pool.c.inc" +/* 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_ARG_I32 TCG_CALL_ARG_NORMAL +#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_EVEN #if TCG_TARGET_REG_BITS == 32 # define LO_OFF (HOST_BIG_ENDIAN * 4) @@ -78,13 +89,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_S7 -#endif #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) +#define TCG_REG_TB ({ qemu_build_not_reached(); TCG_REG_ZERO; }) #endif /* check if we really need so many registers :P */ @@ -175,12 +184,10 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, g_assert_not_reached(); } -#define TCG_CT_CONST_ZERO 0x100 -#define TCG_CT_CONST_U16 0x200 /* Unsigned 16-bit: 0 - 0xffff. */ -#define TCG_CT_CONST_S16 0x400 /* Signed 16-bit: -32768 - 32767 */ -#define TCG_CT_CONST_P2M1 0x800 /* Power of 2 minus 1. */ -#define TCG_CT_CONST_N16 0x1000 /* "Negatable" 16-bit: -32767 - 32767 */ -#define TCG_CT_CONST_WSZ 0x2000 /* word size */ +#define TCG_CT_CONST_U16 0x100 /* Unsigned 16-bit: 0 - 0xffff. */ +#define TCG_CT_CONST_S16 0x200 /* Signed 16-bit: -32768 - 32767 */ +#define TCG_CT_CONST_P2M1 0x400 /* Power of 2 minus 1. */ +#define TCG_CT_CONST_WSZ 0x800 /* word size */ #define ALL_GENERAL_REGS 0xffffffffu @@ -190,18 +197,15 @@ 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; - } else if ((ct & TCG_CT_CONST_ZERO) && val == 0) { - return 1; } else if ((ct & TCG_CT_CONST_U16) && val == (uint16_t)val) { return 1; } else if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val) { return 1; - } else if ((ct & TCG_CT_CONST_N16) && val >= -32767 && val <= 32767) { - return 1; } else if ((ct & TCG_CT_CONST_P2M1) && use_mips32r2_instructions && is_p2m1(val)) { return 1; @@ -640,7 +644,7 @@ static void tcg_out_movi(TCGContext *s, TCGType type, static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) { - tcg_debug_assert(TCG_TARGET_HAS_ext8s_i32); + tcg_debug_assert(use_mips32r2_instructions); tcg_out_opc_reg(s, OPC_SEB, rd, TCG_REG_ZERO, rs); } @@ -651,7 +655,7 @@ static void tcg_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rs) static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) { - tcg_debug_assert(TCG_TARGET_HAS_ext16s_i32); + tcg_debug_assert(use_mips32r2_instructions); tcg_out_opc_reg(s, OPC_SEH, rd, TCG_REG_ZERO, rs); } @@ -695,39 +699,6 @@ static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs, g_assert_not_reached(); } -static void tcg_out_bswap16(TCGContext *s, TCGReg ret, TCGReg arg, int flags) -{ - /* ret and arg can't be register tmp0 */ - tcg_debug_assert(ret != TCG_TMP0); - tcg_debug_assert(arg != TCG_TMP0); - - /* With arg = abcd: */ - if (use_mips32r2_instructions) { - tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); /* badc */ - if (flags & TCG_BSWAP_OS) { - tcg_out_opc_reg(s, OPC_SEH, ret, 0, ret); /* ssdc */ - } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - tcg_out_opc_imm(s, OPC_ANDI, ret, ret, 0xffff); /* 00dc */ - } - return; - } - - tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 8); /* 0abc */ - if (!(flags & TCG_BSWAP_IZ)) { - tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP0, TCG_TMP0, 0x00ff); /* 000c */ - } - if (flags & TCG_BSWAP_OS) { - tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); /* d000 */ - tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16); /* ssd0 */ - } else { - tcg_out_opc_sa(s, OPC_SLL, ret, arg, 8); /* bcd0 */ - if (flags & TCG_BSWAP_OZ) { - tcg_out_opc_imm(s, OPC_ANDI, ret, ret, 0xff00); /* 00d0 */ - } - } - tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0); /* ssdc */ -} - static void tcg_out_bswap_subr(TCGContext *s, const tcg_insn_unit *sub) { if (!tcg_out_opc_jmp(s, OPC_JAL, sub)) { @@ -736,39 +707,6 @@ static void tcg_out_bswap_subr(TCGContext *s, const tcg_insn_unit *sub) } } -static void tcg_out_bswap32(TCGContext *s, TCGReg ret, TCGReg arg, int flags) -{ - if (use_mips32r2_instructions) { - tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); - tcg_out_opc_sa(s, OPC_ROTR, ret, ret, 16); - if (flags & TCG_BSWAP_OZ) { - tcg_out_opc_bf(s, OPC_DEXT, ret, ret, 31, 0); - } - } else { - if (flags & TCG_BSWAP_OZ) { - tcg_out_bswap_subr(s, bswap32u_addr); - } else { - tcg_out_bswap_subr(s, bswap32_addr); - } - /* delay slot -- never omit the insn, like tcg_out_mov might. */ - tcg_out_opc_reg(s, OPC_OR, TCG_TMP0, arg, TCG_REG_ZERO); - tcg_out_mov(s, TCG_TYPE_I32, ret, TCG_TMP3); - } -} - -static void tcg_out_bswap64(TCGContext *s, TCGReg ret, TCGReg arg) -{ - if (use_mips32r2_instructions) { - tcg_out_opc_reg(s, OPC_DSBH, ret, 0, arg); - tcg_out_opc_reg(s, OPC_DSHD, ret, 0, ret); - } else { - tcg_out_bswap_subr(s, bswap64_addr); - /* delay slot -- never omit the insn, like tcg_out_mov might. */ - tcg_out_opc_reg(s, OPC_OR, TCG_TMP0, arg, TCG_REG_ZERO); - tcg_out_mov(s, TCG_TYPE_I32, ret, TCG_TMP3); - } -} - static void tcg_out_ext32u(TCGContext *s, TCGReg ret, TCGReg arg) { tcg_debug_assert(TCG_TARGET_REG_BITS == 64); @@ -824,124 +762,116 @@ static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, return false; } -static void tcg_out_addsub2(TCGContext *s, TCGReg rl, TCGReg rh, TCGReg al, - TCGReg ah, TCGArg bl, TCGArg bh, bool cbl, - bool cbh, bool is_sub) -{ - TCGReg th = TCG_TMP1; +#define SETCOND_INV TCG_TARGET_NB_REGS +#define SETCOND_NEZ (SETCOND_INV << 1) +#define SETCOND_FLAGS (SETCOND_INV | SETCOND_NEZ) - /* If we have a negative constant such that negating it would - make the high part zero, we can (usually) eliminate one insn. */ - if (cbl && cbh && bh == -1 && bl != 0) { - bl = -bl; - bh = 0; - is_sub = !is_sub; - } +static int tcg_out_setcond_int(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg arg1, TCGReg arg2) +{ + int flags = 0; - /* By operating on the high part first, we get to use the final - carry operation to move back from the temporary. */ - if (!cbh) { - tcg_out_opc_reg(s, (is_sub ? OPC_SUBU : OPC_ADDU), th, ah, bh); - } else if (bh != 0 || ah == rl) { - tcg_out_opc_imm(s, OPC_ADDIU, th, ah, (is_sub ? -bh : bh)); - } else { - th = ah; + 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; } - /* Note that tcg optimization should eliminate the bl == 0 case. */ - if (is_sub) { - if (cbl) { - tcg_out_opc_imm(s, OPC_SLTIU, TCG_TMP0, al, bl); - tcg_out_opc_imm(s, OPC_ADDIU, rl, al, -bl); - } else { - tcg_out_opc_reg(s, OPC_SLTU, TCG_TMP0, al, bl); - tcg_out_opc_reg(s, OPC_SUBU, rl, al, bl); - } - tcg_out_opc_reg(s, OPC_SUBU, rh, th, TCG_TMP0); - } else { - if (cbl) { - tcg_out_opc_imm(s, OPC_ADDIU, rl, al, bl); - tcg_out_opc_imm(s, OPC_SLTIU, TCG_TMP0, rl, bl); - } else if (rl == al && rl == bl) { - tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, al, TCG_TARGET_REG_BITS - 1); - tcg_out_opc_reg(s, OPC_ADDU, rl, al, bl); - } else { - tcg_out_opc_reg(s, OPC_ADDU, rl, al, bl); - tcg_out_opc_reg(s, OPC_SLTU, TCG_TMP0, rl, (rl == bl ? al : bl)); + switch (cond) { + case TCG_COND_NE: + flags |= SETCOND_NEZ; + if (arg2 == 0) { + return arg1 | flags; } - tcg_out_opc_reg(s, OPC_ADDU, rh, th, TCG_TMP0); + 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; } -/* Bit 0 set if inversion required; bit 1 set if swapping required. */ -#define MIPS_CMP_INV 1 -#define MIPS_CMP_SWAP 2 - -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 void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg arg1, TCGReg arg2) +static void tcg_out_setcond_end(TCGContext *s, TCGReg ret, int tmpflags) { - MIPSInsn s_opc = OPC_SLTU; - int cmp_map; + if (tmpflags != ret) { + TCGReg tmp = tmpflags & ~SETCOND_FLAGS; - switch (cond) { - case TCG_COND_EQ: - if (arg2 != 0) { - tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); - arg1 = ret; + 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(); } - 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; +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg arg1, TCGReg arg2) +{ + int tmpflags = tcg_out_setcond_int(s, cond, ret, arg1, arg2); + tcg_out_setcond_end(s, ret, tmpflags); +} - case TCG_COND_LT: - case TCG_COND_GE: - case TCG_COND_LE: - case TCG_COND_GT: - s_opc = OPC_SLT; - /* FALLTHRU */ +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rz), + .out_rrr = tgen_setcond, +}; - 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; +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg arg1, TCGReg arg2) +{ + int tmpflags = tcg_out_setcond_int(s, cond, ret, arg1, arg2); + TCGReg tmp = tmpflags & ~SETCOND_FLAGS; - default: - g_assert_not_reached(); - break; - } + /* 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_ADDIU, ret, tmp, -1); + } else { + tcg_out_opc_reg(s, OPC_SUBU, ret, TCG_REG_ZERO, tmp); + } } -static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, - TCGReg arg2, TCGLabel *l) +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rz), + .out_rrr = tgen_negsetcond, +}; + +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, TCGReg arg2, TCGLabel *l) { static const MIPSInsn b_zero[16] = { [TCG_COND_LT] = OPC_BLTZ, @@ -950,9 +880,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: @@ -961,7 +889,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: @@ -970,133 +897,107 @@ 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: - g_assert_not_reached(); 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 const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rz), + .out_rr = tgen_brcond, +}; + +void tcg_out_br(TCGContext *s, TCGLabel *l) { - /* 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; - } - } - /* 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; + tgen_brcond(s, TCG_TYPE_I32, TCG_COND_EQ, TCG_REG_ZERO, TCG_REG_ZERO, l); } -static void tcg_out_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, - 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) { - 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; - } + int flags = 0; switch (cond) { case TCG_COND_EQ: + flags |= SETCOND_INV; + /* fall through */ 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); + 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, 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); + tgen_setcond(s, TCG_TYPE_I32, TCG_COND_EQ, TCG_TMP0, ah, bh); + tgen_setcond(s, TCG_TYPE_I32, tcg_unsigned_cond(cond), + TCG_TMP1, al, bl); + tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, TCG_TMP0); + tgen_setcond(s, TCG_TYPE_I32, tcg_high_cond(cond), TCG_TMP0, ah, bh); + tcg_out_opc_reg(s, OPC_OR, ret, TCG_TMP0, TCG_TMP1); break; } + return ret | flags; } -static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, - TCGReg bl, TCGReg bh, TCGLabel *l) +static void tgen_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh) { - TCGCond b_cond = TCG_COND_NE; - TCGReg tmp = TCG_TMP1; + int tmpflags = tcg_out_setcond2_int(s, cond, ret, al, ah, bl, bh); + tcg_out_setcond_end(s, ret, tmpflags); +} - /* 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; +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) +#endif +static const TCGOutOpSetcond2 outop_setcond2 = { + .base.static_constraint = C_O1_I4(r, r, r, rz, rz), + .out = tgen_setcond2, +}; - 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; - } +static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh, TCGLabel *l) +{ + 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; - 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) +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) +#endif +static const TCGOutOpBrcond2 outop_brcond2 = { + .base.static_constraint = C_O0_I4(r, r, rz, rz), + .out = tgen_brcond2, +}; + +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg v1, bool const_v1, TCGArg v2, bool const_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) { @@ -1105,27 +1006,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; @@ -1138,16 +1021,32 @@ 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 { - MIPSInsn m_opc = eqz ? OPC_MOVZ : OPC_MOVN; + return; + } - tcg_out_opc_reg(s, m_opc, ret, v1, c1); + /* This should be guaranteed via constraints */ + tcg_debug_assert(v2 == ret); - /* 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); + } 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 const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = (use_mips32r6_instructions + ? C_O1_I4(r, r, rz, rz, rz) + : C_O1_I4(r, r, rz, rz, 0)), + .out = tgen_movcond, +}; + static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *arg, bool tail) { /* @@ -1258,14 +1157,13 @@ bool tcg_target_has_memory_bswap(MemOp memop) #define MIN_TLB_MASK_TABLE_OFS -32768 /* - * For softmmu, perform the TLB load and compare. - * For useronly, perform any required alignment tests. + * 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) + TCGReg addr, MemOpIdx oi, bool is_ld) { TCGType addr_type = s->addr_type; TCGLabelQemuLdst *ldst = NULL; @@ -1279,130 +1177,112 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, a_bits = h->aa.align; a_mask = (1 << a_bits) - 1; -#ifdef CONFIG_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 in TMP3. */ - tcg_out_opc_reg(s, ALIAS_PADD, TCG_TMP3, TCG_TMP3, TCG_TMP1); + 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); - 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); - } + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addr_reg = addr; - 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); - } + /* 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); - /* - * 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) { + /* Extract the TLB index from the address into TMP3. */ if (TCG_TARGET_REG_BITS == 32 || addr_type == TCG_TYPE_I32) { - tcg_out_opc_imm(s, OPC_ADDIU, TCG_TMP2, addrlo, s_mask - a_mask); + tcg_out_opc_sa(s, OPC_SRL, TCG_TMP3, addr, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); } else { - tcg_out_opc_imm(s, OPC_DADDIU, TCG_TMP2, addrlo, s_mask - a_mask); + tcg_out_dsrl(s, TCG_TMP3, addr, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); } - 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; - } + tcg_out_opc_reg(s, OPC_AND, TCG_TMP3, TCG_TMP3, TCG_TMP0); - ldst->label_ptr[0] = s->code_ptr; - tcg_out_opc_br(s, OPC_BNE, TCG_TMP1, TCG_TMP0); + /* Add the tlb_table pointer, creating the CPUTLBEntry address. */ + tcg_out_opc_reg(s, ALIAS_PADD, TCG_TMP3, TCG_TMP3, TCG_TMP1); - /* 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 comparator. */ + if (TCG_TARGET_REG_BITS == 64 && addr_type == TCG_TYPE_I32) { + tcg_out_ld(s, TCG_TYPE_I32, TCG_TMP0, TCG_TMP3, + cmp_off + HOST_BIG_ENDIAN * 4); + } else { + tcg_out_ld(s, TCG_TYPE_REG, TCG_TMP0, TCG_TMP3, cmp_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); - } + /* + * 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, TARGET_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, addr, 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, addr); + } - /* 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); + /* 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, addr); + addr = TCG_TMP2; + } - ldst->is_ld = is_ld; - ldst->oi = oi; - ldst->addrlo_reg = addrlo; - ldst->addrhi_reg = addrhi; + ldst->label_ptr[0] = s->code_ptr; + tcg_out_opc_br(s, OPC_BNE, TCG_TMP1, TCG_TMP0); - /* 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); + /* delay slot */ + base = TCG_TMP3; + tcg_out_opc_reg(s, ALIAS_PADD, base, TCG_TMP3, addr); + } else { + if (a_mask && (use_mips32r6_instructions || a_bits != s_bits)) { + ldst = new_ldst_label(s); - 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); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addr_reg = addr; + + /* 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, addr, 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 = addr; + 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; } - base = TCG_REG_A0; } -#endif h->base = base; return ldst; @@ -1507,30 +1387,66 @@ static void tcg_out_qemu_ld_unalign(TCGContext *s, TCGReg lo, TCGReg hi, } } -static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) +{ + MemOp opc = get_memop(oi); + TCGLabelQemuLdst *ldst; + HostAddress h; + + ldst = prepare_host_addr(s, &h, addr, oi, true); + + if (use_mips32r6_instructions || h.aa.align >= (opc & MO_SIZE)) { + tcg_out_qemu_ld_direct(s, data, 0, h.base, opc, type); + } else { + tcg_out_qemu_ld_unalign(s, data, 0, h.base, opc, type); + } + + if (ldst) { + ldst->type = type; + ldst->datalo_reg = data; + ldst->datahi_reg = 0; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } +} + +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, true); + tcg_debug_assert(TCG_TARGET_REG_BITS == 32); + ldst = prepare_host_addr(s, &h, addr, oi, true); if (use_mips32r6_instructions || h.aa.align >= (opc & MO_SIZE)) { - tcg_out_qemu_ld_direct(s, datalo, datahi, h.base, opc, data_type); + tcg_out_qemu_ld_direct(s, datalo, datahi, h.base, opc, type); } else { - tcg_out_qemu_ld_unalign(s, datalo, datahi, h.base, opc, data_type); + tcg_out_qemu_ld_unalign(s, datalo, datahi, h.base, opc, type); } if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = datalo; ldst->datahi_reg = datahi; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + /* Ensure that the mips32 code is compiled but discarded for mips64. */ + .base.static_constraint = + TCG_TARGET_REG_BITS == 32 ? C_O2_I1(r, r, r) : C_NotImplemented, + .out = + TCG_TARGET_REG_BITS == 32 ? tgen_qemu_ld2 : NULL, +}; + static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg lo, TCGReg hi, TCGReg base, MemOp opc) { @@ -1594,31 +1510,67 @@ static void tcg_out_qemu_st_unalign(TCGContext *s, TCGReg lo, TCGReg hi, } } -static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, false); + ldst = prepare_host_addr(s, &h, addr, oi, false); if (use_mips32r6_instructions || h.aa.align >= (opc & MO_SIZE)) { - tcg_out_qemu_st_direct(s, datalo, datahi, h.base, opc); + tcg_out_qemu_st_direct(s, data, 0, h.base, opc); } else { - tcg_out_qemu_st_unalign(s, datalo, datahi, h.base, opc); + tcg_out_qemu_st_unalign(s, data, 0, h.base, opc); } if (ldst) { - ldst->type = data_type; + ldst->type = type; + ldst->datalo_reg = data; + ldst->datahi_reg = 0; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } +} + +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out = tgen_qemu_st, +}; + +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) +{ + MemOp opc = get_memop(oi); + TCGLabelQemuLdst *ldst; + HostAddress h; + + tcg_debug_assert(TCG_TARGET_REG_BITS == 32); + ldst = prepare_host_addr(s, &h, addr, oi, false); + + 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, datalo, datahi, h.base, opc); + } + + if (ldst) { + ldst->type = type; ldst->datalo_reg = datalo; ldst->datahi_reg = datahi; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } -static void tcg_out_mb(TCGContext *s, TCGArg a0) +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + /* Ensure that the mips32 code is compiled but discarded for mips64. */ + .base.static_constraint = + TCG_TARGET_REG_BITS == 32 ? C_O0_I3(rz, rz, r) : C_NotImplemented, + .out = + TCG_TARGET_REG_BITS == 32 ? tgen_qemu_st2 : NULL, +}; + +static void tcg_out_mb(TCGContext *s, unsigned a0) { static const MIPSInsn sync[] = { /* Note that SYNC_MB is a slightly weaker than SYNC 0, @@ -1634,20 +1586,166 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) tcg_out32(s, sync[a0 & TCG_MO_ALL]); } -static void tcg_out_clz(TCGContext *s, MIPSInsn opcv2, MIPSInsn opcv6, - int width, TCGReg a0, TCGReg a1, TCGArg a2) +static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) { - if (use_mips32r6_instructions) { - if (a2 == width) { - tcg_out_opc_reg(s, opcv6, a0, a1, 0); + 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 { - tcg_out_opc_reg(s, opcv6, TCG_TMP0, a1, 0); - tcg_out_movcond(s, TCG_COND_EQ, a0, a1, 0, a2, TCG_TMP0); + 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 { - if (a2 == width) { - tcg_out_opc_reg(s, opcv2, a0, a1, a1); - } else if (a0 == a2) { + 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)); + } +} + +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_opc_reg(s, OPC_JR, 0, a0, 0); + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_TB, a0); + } else { + tcg_out_nop(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 tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_ADDU : OPC_DADDU; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_ADDIU : OPC_DADDIU; + tcg_out_opc_imm(s, insn, a0, a1, a2); +} + +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; + +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_carry(TCGContext *s) +{ + g_assert_not_reached(); +} + +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_AND, a0, a1, a2); +} + +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int msb; + + if (a2 == (uint16_t)a2) { + tcg_out_opc_imm(s, OPC_ANDI, a0, a1, a2); + return; + } + + tcg_debug_assert(use_mips32r2_instructions); + tcg_debug_assert(is_p2m1(a2)); + msb = ctz64(~a2) - 1; + if (type == TCG_TYPE_I32) { + tcg_out_opc_bf(s, OPC_EXT, a0, a1, msb, 0); + } else { + tcg_out_opc_bf64(s, OPC_DEXT, OPC_DEXTM, OPC_DEXTU, a0, a1, msb, 0); + } +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, rIK), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; + +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (use_mips32r6_instructions) { + MIPSInsn opcv6 = type == TCG_TYPE_I32 ? OPC_CLZ_R6 : OPC_DCLZ_R6; + tcg_out_opc_reg(s, opcv6, TCG_TMP0, a1, 0); + tgen_movcond(s, TCG_TYPE_REG, TCG_COND_EQ, a0, a1, a2, false, + TCG_TMP0, false, TCG_REG_ZERO, false); + } else { + MIPSInsn opcv2 = type == TCG_TYPE_I32 ? OPC_CLZ : OPC_DCLZ; + if (a0 == a2) { tcg_out_opc_reg(s, opcv2, TCG_TMP0, a1, a1); tcg_out_opc_reg(s, OPC_MOVN, a0, TCG_TMP0, a1); } else if (a0 != a1) { @@ -1656,684 +1754,700 @@ static void tcg_out_clz(TCGContext *s, MIPSInsn opcv2, MIPSInsn opcv6, } else { tcg_out_opc_reg(s, opcv2, TCG_TMP0, a1, a1); tcg_out_opc_reg(s, OPC_MOVZ, TCG_TMP0, a2, a1); - tcg_out_mov(s, TCG_TYPE_REG, a0, TCG_TMP0); + tcg_out_mov(s, type, a0, TCG_TMP0); } } } -static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) { - TCGReg base = TCG_REG_ZERO; - int16_t lo = 0; + if (a2 == 0) { + tgen_clz(s, type, a0, a1, TCG_REG_ZERO); + } else if (use_mips32r6_instructions) { + MIPSInsn opcv6 = type == TCG_TYPE_I32 ? OPC_CLZ_R6 : OPC_DCLZ_R6; + tcg_out_opc_reg(s, opcv6, a0, a1, 0); + } else { + MIPSInsn opcv2 = type == TCG_TYPE_I32 ? OPC_CLZ : OPC_DCLZ; + tcg_out_opc_reg(s, opcv2, a0, a1, a1); + } +} - 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); - } +static TCGConstraintSetIndex cset_clz(TCGType type, unsigned flags) +{ + return use_mips32r2_instructions ? C_O1_I2(r, r, rzW) : C_NotImplemented; +} + +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_clz, + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; + +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (use_mips32r6_instructions) { + if (type == TCG_TYPE_I32) { + tcg_out_opc_reg(s, OPC_DIV_R6, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_DDIV_R6, a0, a1, a2); + } + } else { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_DIV : OPC_DDIV; + tcg_out_opc_reg(s, insn, 0, a1, a2); + tcg_out_opc_reg(s, OPC_MFLO, a0, 0, 0); + } +} + +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divs, +}; + +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (use_mips32r6_instructions) { + if (type == TCG_TYPE_I32) { + tcg_out_opc_reg(s, OPC_DIVU_R6, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_DDIVU_R6, a0, a1, a2); + } + } else { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_DIVU : OPC_DDIVU; + tcg_out_opc_reg(s, insn, 0, a1, a2); + tcg_out_opc_reg(s, OPC_MFLO, a0, 0, 0); + } +} + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divu, +}; + +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_NotImplemented, +}; + +#if TCG_TARGET_REG_BITS == 64 +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_dsra(s, a0, a1, 32); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; +#endif + +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + MIPSInsn insn; + + if (type == TCG_TYPE_I32) { + if (use_mips32_instructions) { + tcg_out_opc_reg(s, OPC_MUL, a0, a1, a2); + return; + } + insn = OPC_MULT; + } else { + if (use_mips32r6_instructions) { + tcg_out_opc_reg(s, OPC_DMUL, a0, a1, a2); + return; + } + insn = OPC_DMULT; + } + tcg_out_opc_reg(s, insn, 0, a1, a2); + tcg_out_opc_reg(s, OPC_MFLO, a0, 0, 0); +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mul, +}; + +static void tgen_muls2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_MULT : OPC_DMULT; + tcg_out_opc_reg(s, insn, 0, a2, a3); + tcg_out_opc_reg(s, OPC_MFLO, a0, 0, 0); + tcg_out_opc_reg(s, OPC_MFHI, a1, 0, 0); +} + +static TCGConstraintSetIndex cset_mul2(TCGType type, unsigned flags) +{ + return use_mips32r6_instructions ? C_NotImplemented : C_O2_I2(r, r, r, r); +} + +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mul2, + .out_rrrr = tgen_muls2, +}; + +static void tgen_mulsh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (use_mips32r6_instructions) { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_MUH : OPC_DMUH; + tcg_out_opc_reg(s, insn, a0, a1, a2); + } else { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_MULT : OPC_DMULT; + tcg_out_opc_reg(s, insn, 0, a1, a2); + tcg_out_opc_reg(s, OPC_MFHI, a0, 0, 0); + } +} + +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mulsh, +}; + +static void tgen_mulu2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_MULTU : OPC_DMULTU; + tcg_out_opc_reg(s, insn, 0, a2, a3); + tcg_out_opc_reg(s, OPC_MFLO, a0, 0, 0); + tcg_out_opc_reg(s, OPC_MFHI, a1, 0, 0); +} + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mul2, + .out_rrrr = tgen_mulu2, +}; + +static void tgen_muluh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (use_mips32r6_instructions) { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_MUHU : OPC_DMUHU; + tcg_out_opc_reg(s, insn, a0, a1, a2); + } else { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_MULTU : OPC_DMULTU; + tcg_out_opc_reg(s, insn, 0, a1, a2); + tcg_out_opc_reg(s, OPC_MFHI, a0, 0, 0); + } +} + +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_muluh, +}; + +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_nor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_NOR, a0, a1, a2); +} + +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_nor, +}; + +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_OR, a0, a1, a2); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_imm(s, OPC_ORI, a0, a1, a2); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_rems(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (use_mips32r6_instructions) { + if (type == TCG_TYPE_I32) { + tcg_out_opc_reg(s, OPC_MOD, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_DMOD, a0, a1, a2); + } + } else { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_DIV : OPC_DDIV; + tcg_out_opc_reg(s, insn, 0, a1, a2); + tcg_out_opc_reg(s, OPC_MFHI, a0, 0, 0); + } +} + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_rems, +}; + +static void tgen_remu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (use_mips32r6_instructions) { + if (type == TCG_TYPE_I32) { + tcg_out_opc_reg(s, OPC_MODU, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_DMODU, a0, a1, a2); + } + } else { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_DIVU : OPC_DDIVU; + tcg_out_opc_reg(s, insn, 0, a1, a2); + tcg_out_opc_reg(s, OPC_MFHI, a0, 0, 0); + } +} + +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_remu, +}; + +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_NotImplemented, +}; + +static TCGConstraintSetIndex cset_rotr(TCGType type, unsigned flags) +{ + return use_mips32r2_instructions ? C_O1_I2(r, r, ri) : C_NotImplemented; +} + +static void tgen_rotr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_ROTRV : OPC_DROTRV; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_rotri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_sa(s, OPC_ROTR, a0, a1, a2); + } else { + tcg_out_opc_sa64(s, OPC_DROTR, OPC_DROTR32, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_rotr, + .out_rrr = tgen_rotr, + .out_rri = tgen_rotri, +}; + +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_SRAV : OPC_DSRAV; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_sa(s, OPC_SRA, a0, a1, a2); + } else { + tcg_out_dsra(s, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_SLLV : OPC_DSLLV; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_sa(s, OPC_SLL, a0, a1, a2); + } else { + tcg_out_dsll(s, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_SRLV : OPC_DSRLV; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_sa(s, OPC_SRL, a0, a1, a2); + } else { + tcg_out_dsrl(s, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_SUBU : OPC_DSUBU; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sub, +}; + +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + g_assert_not_reached(); +} + +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_XOR, a0, a1, a2); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_imm(s, OPC_XORI, a0, a1, a2); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg ret, TCGReg arg, unsigned flags) +{ + /* With arg = abcd: */ + if (use_mips32r2_instructions) { + tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); /* badc */ + if (flags & TCG_BSWAP_OS) { + tcg_out_opc_reg(s, OPC_SEH, ret, 0, ret); /* ssdc */ + } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + tcg_out_opc_imm(s, OPC_ANDI, ret, ret, 0xffff); /* 00dc */ + } + return; + } + + tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 8); /* 0abc */ + if (!(flags & TCG_BSWAP_IZ)) { + tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP0, TCG_TMP0, 0x00ff); /* 000c */ + } + if (flags & TCG_BSWAP_OS) { + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); /* d000 */ + tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16); /* ssd0 */ + } else { + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 8); /* bcd0 */ + if (flags & TCG_BSWAP_OZ) { + tcg_out_opc_imm(s, OPC_ANDI, ret, ret, 0xff00); /* 00d0 */ + } + } + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0); /* ssdc */ +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap16, +}; + +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg ret, TCGReg arg, unsigned flags) +{ + if (use_mips32r2_instructions) { + tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); + tcg_out_opc_sa(s, OPC_ROTR, ret, ret, 16); + if (flags & TCG_BSWAP_OZ) { + tcg_out_opc_bf(s, OPC_DEXT, ret, ret, 31, 0); + } + } else { + if (flags & TCG_BSWAP_OZ) { + tcg_out_bswap_subr(s, bswap32u_addr); } else { - ofs = a0; - lo = ofs; - base = TCG_REG_V0; - tcg_out_movi(s, TCG_TYPE_PTR, base, ofs - lo); + tcg_out_bswap_subr(s, bswap32_addr); } + /* delay slot -- never omit the insn, like tcg_out_mov might. */ + tcg_out_opc_reg(s, OPC_OR, TCG_TMP0, arg, TCG_REG_ZERO); + tcg_out_mov(s, TCG_TYPE_I32, ret, TCG_TMP3); } - 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; +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap32, +}; - /* indirect jump method */ - if (TCG_TARGET_REG_BITS == 64) { - dest = TCG_REG_TB; - base = TCG_REG_TB; - ofs = tcg_tbrel_diff(s, (void *)ofs); +#if TCG_TARGET_REG_BITS == 64 +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) +{ + if (use_mips32r2_instructions) { + tcg_out_opc_reg(s, OPC_DSBH, ret, 0, arg); + tcg_out_opc_reg(s, OPC_DSHD, ret, 0, ret); } else { - dest = TCG_TMP0; - base = TCG_REG_ZERO; + tcg_out_bswap_subr(s, bswap64_addr); + /* delay slot -- never omit the insn, like tcg_out_mov might. */ + tcg_out_opc_reg(s, OPC_OR, TCG_TMP0, arg, TCG_REG_ZERO); + tcg_out_mov(s, TCG_TYPE_I32, ret, TCG_TMP3); } - 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)); - } +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap64, +}; +#endif /* TCG_TARGET_REG_BITS == 64 */ + +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_sub(s, type, a0, TCG_REG_ZERO, a1); } -void tb_target_set_jmp_target(const TranslationBlock *tb, int n, - uintptr_t jmp_rx, uintptr_t jmp_rw) +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { - /* Always indirect, nothing to do */ + tgen_nor(s, type, a0, TCG_REG_ZERO, a1); } -static void tcg_out_op(TCGContext *s, TCGOpcode opc, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) { - MIPSInsn i1, i2; - TCGArg a0, a1, a2; - int c2; + if (type == TCG_TYPE_I32) { + tcg_out_opc_bf(s, OPC_INS, a0, a2, ofs + len - 1, ofs); + } else { + tcg_out_opc_bf64(s, OPC_DINS, OPC_DINSM, OPC_DINSU, a0, a2, + ofs + len - 1, ofs); + } +} - /* - * Note that many operands use the constraint set "rZ". - * We make use of the fact that 0 is the ZERO register, - * and hence such cases need not check for const_args. - */ - a0 = args[0]; - a1 = args[1]; - a2 = args[2]; - c2 = const_args[2]; - - switch (opc) { - case INDEX_op_goto_ptr: - /* jmp to the given host address (could be epilogue) */ - tcg_out_opc_reg(s, OPC_JR, 0, a0, 0); - 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, - arg_label(a0)); - break; +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(r, 0, rz), + .out_rrr = tgen_deposit, +}; - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - i1 = OPC_LBU; - goto do_ldst; - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - i1 = OPC_LB; - goto do_ldst; - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - i1 = OPC_LHU; - goto do_ldst; - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - i1 = OPC_LH; - goto do_ldst; - case INDEX_op_ld_i32: - case INDEX_op_ld32s_i64: - i1 = OPC_LW; - goto do_ldst; - case INDEX_op_ld32u_i64: - i1 = OPC_LWU; - goto do_ldst; - case INDEX_op_ld_i64: - i1 = OPC_LD; - goto do_ldst; - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - i1 = OPC_SB; - goto do_ldst; - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - i1 = OPC_SH; - goto do_ldst; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - i1 = OPC_SW; - goto do_ldst; - case INDEX_op_st_i64: - i1 = OPC_SD; - do_ldst: - tcg_out_ldst(s, i1, a0, a1, a2); - break; +static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0 && len <= 16) { + tcg_out_opc_imm(s, OPC_ANDI, a0, a1, (1 << len) - 1); + } else if (type == TCG_TYPE_I32) { + tcg_out_opc_bf(s, OPC_EXT, a0, a1, len - 1, ofs); + } else { + tcg_out_opc_bf64(s, OPC_DEXT, OPC_DEXTM, OPC_DEXTU, + a0, a1, len - 1, ofs); + } +} - case INDEX_op_add_i32: - i1 = OPC_ADDU, i2 = OPC_ADDIU; - goto do_binary; - case INDEX_op_add_i64: - i1 = OPC_DADDU, i2 = OPC_DADDIU; - goto do_binary; - case INDEX_op_or_i32: - case INDEX_op_or_i64: - i1 = OPC_OR, i2 = OPC_ORI; - goto do_binary; - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: - i1 = OPC_XOR, i2 = OPC_XORI; - do_binary: - if (c2) { - tcg_out_opc_imm(s, i2, a0, a1, a2); - break; - } - do_binaryv: - tcg_out_opc_reg(s, i1, a0, a1, a2); - break; +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; - case INDEX_op_sub_i32: - i1 = OPC_SUBU, i2 = OPC_ADDIU; - goto do_subtract; - case INDEX_op_sub_i64: - i1 = OPC_DSUBU, i2 = OPC_DADDIU; - do_subtract: - if (c2) { - tcg_out_opc_imm(s, i2, a0, a1, -a2); - break; - } - goto do_binaryv; - case INDEX_op_and_i32: - if (c2 && a2 != (uint16_t)a2) { - int msb = ctz32(~a2) - 1; - tcg_debug_assert(use_mips32r2_instructions); - tcg_debug_assert(is_p2m1(a2)); - tcg_out_opc_bf(s, OPC_EXT, a0, a1, msb, 0); - break; - } - i1 = OPC_AND, i2 = OPC_ANDI; - goto do_binary; - case INDEX_op_and_i64: - if (c2 && a2 != (uint16_t)a2) { - int msb = ctz64(~a2) - 1; - tcg_debug_assert(use_mips32r2_instructions); - tcg_debug_assert(is_p2m1(a2)); - tcg_out_opc_bf64(s, OPC_DEXT, OPC_DEXTM, OPC_DEXTU, a0, a1, msb, 0); - break; - } - i1 = OPC_AND, i2 = OPC_ANDI; - goto do_binary; - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: - i1 = OPC_NOR; - goto do_binaryv; - - case INDEX_op_mul_i32: - if (use_mips32_instructions) { - tcg_out_opc_reg(s, OPC_MUL, a0, a1, a2); - break; - } - i1 = OPC_MULT, i2 = OPC_MFLO; - goto do_hilo1; - case INDEX_op_mulsh_i32: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_MUH, a0, a1, a2); - break; - } - i1 = OPC_MULT, i2 = OPC_MFHI; - goto do_hilo1; - case INDEX_op_muluh_i32: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_MUHU, a0, a1, a2); - break; - } - i1 = OPC_MULTU, i2 = OPC_MFHI; - goto do_hilo1; - case INDEX_op_div_i32: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DIV_R6, a0, a1, a2); - break; - } - i1 = OPC_DIV, i2 = OPC_MFLO; - goto do_hilo1; - case INDEX_op_divu_i32: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DIVU_R6, a0, a1, a2); - break; - } - i1 = OPC_DIVU, i2 = OPC_MFLO; - goto do_hilo1; - case INDEX_op_rem_i32: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_MOD, a0, a1, a2); - break; - } - i1 = OPC_DIV, i2 = OPC_MFHI; - goto do_hilo1; - case INDEX_op_remu_i32: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_MODU, a0, a1, a2); - break; - } - i1 = OPC_DIVU, i2 = OPC_MFHI; - goto do_hilo1; - case INDEX_op_mul_i64: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DMUL, a0, a1, a2); - break; - } - i1 = OPC_DMULT, i2 = OPC_MFLO; - goto do_hilo1; - case INDEX_op_mulsh_i64: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DMUH, a0, a1, a2); - break; - } - i1 = OPC_DMULT, i2 = OPC_MFHI; - goto do_hilo1; - case INDEX_op_muluh_i64: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DMUHU, a0, a1, a2); - break; - } - i1 = OPC_DMULTU, i2 = OPC_MFHI; - goto do_hilo1; - case INDEX_op_div_i64: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DDIV_R6, a0, a1, a2); - break; - } - i1 = OPC_DDIV, i2 = OPC_MFLO; - goto do_hilo1; - case INDEX_op_divu_i64: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DDIVU_R6, a0, a1, a2); - break; - } - i1 = OPC_DDIVU, i2 = OPC_MFLO; - goto do_hilo1; - case INDEX_op_rem_i64: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DMOD, a0, a1, a2); - break; - } - i1 = OPC_DDIV, i2 = OPC_MFHI; - goto do_hilo1; - case INDEX_op_remu_i64: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DMODU, a0, a1, a2); - break; +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8s(s, type, a0, a1); + return; + case 16: + tcg_out_ext16s(s, type, a0, a1); + return; + case 32: + tcg_out_ext32s(s, a0, a1); + return; } - i1 = OPC_DDIVU, i2 = OPC_MFHI; - do_hilo1: - tcg_out_opc_reg(s, i1, 0, a1, a2); - tcg_out_opc_reg(s, i2, a0, 0, 0); - break; + } + g_assert_not_reached(); +} - case INDEX_op_muls2_i32: - i1 = OPC_MULT; - goto do_hilo2; - case INDEX_op_mulu2_i32: - i1 = OPC_MULTU; - goto do_hilo2; - case INDEX_op_muls2_i64: - i1 = OPC_DMULT; - goto do_hilo2; - case INDEX_op_mulu2_i64: - i1 = OPC_DMULTU; - do_hilo2: - tcg_out_opc_reg(s, i1, 0, a2, args[3]); - tcg_out_opc_reg(s, OPC_MFLO, a0, 0, 0); - tcg_out_opc_reg(s, OPC_MFHI, a1, 0, 0); - break; +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; - case INDEX_op_not_i32: - case INDEX_op_not_i64: - i1 = OPC_NOR; - goto do_unary; - do_unary: - tcg_out_opc_reg(s, i1, a0, TCG_REG_ZERO, a1); - break; +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: - tcg_out_bswap16(s, a0, a1, a2); - break; - case INDEX_op_bswap32_i32: - tcg_out_bswap32(s, a0, a1, 0); - break; - case INDEX_op_bswap32_i64: - tcg_out_bswap32(s, a0, a1, a2); - break; - case INDEX_op_bswap64_i64: - tcg_out_bswap64(s, a0, a1); - break; - case INDEX_op_extrh_i64_i32: - tcg_out_dsra(s, a0, a1, 32); - break; +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LBU, dest, base, offset); +} - case INDEX_op_sar_i32: - i1 = OPC_SRAV, i2 = OPC_SRA; - goto do_shift; - case INDEX_op_shl_i32: - i1 = OPC_SLLV, i2 = OPC_SLL; - goto do_shift; - case INDEX_op_shr_i32: - i1 = OPC_SRLV, i2 = OPC_SRL; - goto do_shift; - case INDEX_op_rotr_i32: - i1 = OPC_ROTRV, i2 = OPC_ROTR; - do_shift: - if (c2) { - tcg_out_opc_sa(s, i2, a0, a1, a2); - break; - } - do_shiftv: - tcg_out_opc_reg(s, i1, a0, a2, a1); - break; - case INDEX_op_rotl_i32: - if (c2) { - tcg_out_opc_sa(s, OPC_ROTR, a0, a1, 32 - a2); - } else { - tcg_out_opc_reg(s, OPC_SUBU, TCG_TMP0, TCG_REG_ZERO, a2); - tcg_out_opc_reg(s, OPC_ROTRV, a0, TCG_TMP0, a1); - } - break; - case INDEX_op_sar_i64: - if (c2) { - tcg_out_dsra(s, a0, a1, a2); - break; - } - i1 = OPC_DSRAV; - goto do_shiftv; - case INDEX_op_shl_i64: - if (c2) { - tcg_out_dsll(s, a0, a1, a2); - break; - } - i1 = OPC_DSLLV; - goto do_shiftv; - case INDEX_op_shr_i64: - if (c2) { - tcg_out_dsrl(s, a0, a1, a2); - break; - } - i1 = OPC_DSRLV; - goto do_shiftv; - case INDEX_op_rotr_i64: - if (c2) { - tcg_out_opc_sa64(s, OPC_DROTR, OPC_DROTR32, a0, a1, a2); - break; - } - i1 = OPC_DROTRV; - goto do_shiftv; - case INDEX_op_rotl_i64: - if (c2) { - tcg_out_opc_sa64(s, OPC_DROTR, OPC_DROTR32, a0, a1, 64 - a2); - } else { - tcg_out_opc_reg(s, OPC_DSUBU, TCG_TMP0, TCG_REG_ZERO, a2); - tcg_out_opc_reg(s, OPC_DROTRV, a0, TCG_TMP0, a1); - } - break; +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; - case INDEX_op_clz_i32: - tcg_out_clz(s, OPC_CLZ, OPC_CLZ_R6, 32, a0, a1, a2); - break; - case INDEX_op_clz_i64: - tcg_out_clz(s, OPC_DCLZ, OPC_DCLZ_R6, 64, a0, a1, a2); - break; +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LB, dest, base, offset); +} - case INDEX_op_deposit_i32: - tcg_out_opc_bf(s, OPC_INS, a0, a2, args[3] + args[4] - 1, args[3]); - break; - case INDEX_op_deposit_i64: - tcg_out_opc_bf64(s, OPC_DINS, OPC_DINSM, OPC_DINSU, a0, a2, - args[3] + args[4] - 1, args[3]); - break; - case INDEX_op_extract_i32: - tcg_out_opc_bf(s, OPC_EXT, a0, a1, args[3] - 1, a2); - break; - case INDEX_op_extract_i64: - tcg_out_opc_bf64(s, OPC_DEXT, OPC_DEXTM, OPC_DEXTU, a0, a1, - args[3] - 1, a2); - break; +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; - case INDEX_op_brcond_i32: - 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; +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LHU, dest, base, offset); +} - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - tcg_out_movcond(s, args[5], a0, a1, a2, args[3], args[4]); - break; +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; - 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]); - break; +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LH, dest, base, offset); +} - 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_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_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; +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; - 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; +#if TCG_TARGET_REG_BITS == 64 +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LWU, dest, base, offset); +} - 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); - break; - case INDEX_op_sub2_i32: - tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], - const_args[4], const_args[5], true); - break; +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; - case INDEX_op_mb: - tcg_out_mb(s, a0); - 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_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 tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LW, dest, base, offset); } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) -{ - switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - - case INDEX_op_ld8u_i32: - case INDEX_op_ld8s_i32: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16s_i32: - case INDEX_op_ld_i32: - case INDEX_op_not_i32: - case INDEX_op_bswap16_i32: - case INDEX_op_bswap32_i32: - case INDEX_op_ext8s_i32: - case INDEX_op_ext16s_i32: - case INDEX_op_extract_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld_i64: - case INDEX_op_not_i64: - case INDEX_op_bswap16_i64: - case INDEX_op_bswap32_i64: - case INDEX_op_bswap64_i64: - case INDEX_op_ext8s_i64: - 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: - case INDEX_op_extrh_i64_i32: - case INDEX_op_extract_i64: - return C_O1_I1(r, r); - - case INDEX_op_st8_i32: - case INDEX_op_st16_i32: - case INDEX_op_st_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - return C_O0_I2(rZ, r); - - case INDEX_op_add_i32: - case INDEX_op_add_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); - case INDEX_op_mul_i32: - case INDEX_op_mulsh_i32: - case INDEX_op_muluh_i32: - case INDEX_op_div_i32: - case INDEX_op_divu_i32: - case INDEX_op_rem_i32: - case INDEX_op_remu_i32: - case INDEX_op_nor_i32: - case INDEX_op_setcond_i32: - case INDEX_op_mul_i64: - case INDEX_op_mulsh_i64: - case INDEX_op_muluh_i64: - case INDEX_op_div_i64: - case INDEX_op_divu_i64: - case INDEX_op_rem_i64: - case INDEX_op_remu_i64: - case INDEX_op_nor_i64: - case INDEX_op_setcond_i64: - return C_O1_I2(r, rZ, rZ); - case INDEX_op_muls2_i32: - case INDEX_op_mulu2_i32: - case INDEX_op_muls2_i64: - case INDEX_op_mulu2_i64: - return C_O2_I2(r, r, r, r); - case INDEX_op_and_i32: - case INDEX_op_and_i64: - return C_O1_I2(r, r, rIK); - case INDEX_op_or_i32: - case INDEX_op_xor_i32: - case INDEX_op_or_i64: - case INDEX_op_xor_i64: - return C_O1_I2(r, r, rI); - case INDEX_op_shl_i32: - case INDEX_op_shr_i32: - case INDEX_op_sar_i32: - case INDEX_op_rotr_i32: - case INDEX_op_rotl_i32: - case INDEX_op_shl_i64: - case INDEX_op_shr_i64: - case INDEX_op_sar_i64: - case INDEX_op_rotr_i64: - case INDEX_op_rotl_i64: - return C_O1_I2(r, r, ri); - case INDEX_op_clz_i32: - case INDEX_op_clz_i64: - return C_O1_I2(r, r, rWZ); - - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: - return C_O1_I2(r, 0, rZ); - 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 (use_mips32r6_instructions - ? C_O1_I4(r, rZ, rZ, rZ, rZ) - : C_O1_I4(r, rZ, rZ, rZ, 0)); - case INDEX_op_add2_i32: - case INDEX_op_sub2_i32: - return C_O2_I4(r, r, rZ, rZ, rN, rN); - case INDEX_op_setcond2_i32: - return C_O1_I4(r, rZ, rZ, rZ, rZ); - case INDEX_op_brcond2_i32: - return C_O0_I4(rZ, rZ, rZ, rZ); - - 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)); +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; +#endif - default: - g_assert_not_reached(); - } +static void tgen_st8_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_SB, data, base, offset); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st8_r, +}; + +static void tgen_st16_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_SH, data, base, offset); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st16_r, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tcg_out_st, +}; + + +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) +{ + return C_NotImplemented; } static const int tcg_target_callee_save_regs[] = { @@ -2465,8 +2579,7 @@ static void tcg_target_qemu_prologue(TCGContext *s) TCG_REG_SP, SAVE_OFS + i * REG_SIZE); } -#ifndef CONFIG_SOFTMMU - if (guest_base != (int16_t)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 @@ -2479,7 +2592,6 @@ static void tcg_target_qemu_prologue(TCGContext *s) 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]); @@ -2628,6 +2740,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(); diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index c0576f66d70..bd4ca5f852d 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -70,136 +70,6 @@ typedef enum { 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_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)) || \ - defined(_MIPS_ARCH_LOONGSON2E) || defined(_MIPS_ARCH_LOONGSON2F) || \ - defined(_MIPS_ARCH_MIPS4) -#define use_movnz_instructions 1 -#else -extern bool use_movnz_instructions; -#endif - -/* MIPS32 instruction set detection */ -#if defined(__mips_isa_rev) && (__mips_isa_rev >= 1) -#define use_mips32_instructions 1 -#else -extern bool use_mips32_instructions; -#endif - -/* MIPS32R2 instruction set detection */ -#if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) -#define use_mips32r2_instructions 1 -#else -extern bool use_mips32r2_instructions; -#endif - -/* MIPS32R6 instruction set detection */ -#if defined(__mips_isa_rev) && (__mips_isa_rev >= 6) -#define use_mips32r6_instructions 1 -#else -#define use_mips32r6_instructions 0 -#endif - -/* optional instructions */ -#define TCG_TARGET_HAS_div_i32 1 -#define TCG_TARGET_HAS_rem_i32 1 -#define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_nor_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_mulu2_i32 (!use_mips32r6_instructions) -#define TCG_TARGET_HAS_muls2_i32 (!use_mips32r6_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_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_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 -#define TCG_TARGET_HAS_nor_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_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 -#define TCG_TARGET_HAS_mulu2_i64 (!use_mips32r6_instructions) -#define TCG_TARGET_HAS_muls2_i64 (!use_mips32r6_instructions) -#define TCG_TARGET_HAS_muluh_i64 1 -#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 -#define TCG_TARGET_HAS_sextract_i32 0 -#define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_ext8s_i32 use_mips32r2_instructions -#define TCG_TARGET_HAS_ext16s_i32 use_mips32r2_instructions -#define TCG_TARGET_HAS_rot_i32 use_mips32r2_instructions -#define TCG_TARGET_HAS_clz_i32 use_mips32r2_instructions -#define TCG_TARGET_HAS_ctz_i32 0 -#define TCG_TARGET_HAS_ctpop_i32 0 -#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 -#define TCG_TARGET_HAS_deposit_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_extract_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_sextract_i64 0 -#define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_ext8s_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_ext16s_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_rot_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_clz_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_ctz_i64 0 -#define TCG_TARGET_HAS_ctpop_i64 0 -#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_HAS_qemu_ldst_i128 0 - -#define TCG_TARGET_DEFAULT_MO 0 -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS +#define TCG_REG_ZERO TCG_REG_ZERO #endif diff --git a/tcg/optimize.c b/tcg/optimize.c index 3013eb04e60..10a76c54616 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -25,25 +25,27 @@ #include "qemu/osdep.h" #include "qemu/int128.h" +#include "qemu/interval-tree.h" #include "tcg/tcg-op-common.h" #include "tcg-internal.h" +#include "tcg-has.h" -#define CASE_OP_32_64(x) \ - glue(glue(case INDEX_op_, x), _i32): \ - glue(glue(case INDEX_op_, x), _i64) -#define CASE_OP_32_64_VEC(x) \ - glue(glue(case INDEX_op_, x), _i32): \ - 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. */ + uint64_t s_mask; /* mask bit is 1 if value bit matches msb */ } TempOptInfo; typedef struct OptContext { @@ -51,65 +53,47 @@ 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 */ - uint64_t s_mask; /* mask of clrsb(value) bits */ TCGType type; + int carry_state; /* -1 = non-constant, {0,1} = constant carry-in */ } OptContext; -/* Calculate the smask for a specific value. */ -static uint64_t smask_from_value(uint64_t value) +static inline TempOptInfo *ts_info(TCGTemp *ts) { - int rep = clrsb64(value); - return ~(~0ull >> rep); + return ts->state_ptr; } -/* - * Calculate the smask for a given set of known-zeros. - * If there are lots of zeros on the left, we can consider the remainder - * an unsigned field, and thus the corresponding signed field is one bit - * larger. - */ -static uint64_t smask_from_zmask(uint64_t zmask) +static inline TempOptInfo *arg_info(TCGArg arg) { - /* - * Only the 0 bits are significant for zmask, thus the msb itself - * must be zero, else we have no sign information. - */ - int rep = clz64(zmask); - if (rep == 0) { - return 0; - } - rep -= 1; - return ~(~0ull >> rep); + return ts_info(arg_temp(arg)); } -/* - * Recreate a properly left-aligned smask after manipulation. - * Some bit-shuffling, particularly shifts and rotates, may - * retain sign bits on the left, but may scatter disconnected - * sign bits on the right. Retain only what remains to the left. - */ -static uint64_t smask_from_smask(int64_t smask) +static inline bool ti_is_const(TempOptInfo *ti) { - /* Only the 1 bits are significant for smask */ - return smask_from_zmask(~smask); + return ti->is_const; } -static inline TempOptInfo *ts_info(TCGTemp *ts) +static inline uint64_t ti_const_val(TempOptInfo *ti) { - return ts->state_ptr; + return ti->val; } -static inline TempOptInfo *arg_info(TCGArg arg) +static inline bool ti_is_const_val(TempOptInfo *ti, uint64_t val) { - return ts_info(arg_temp(arg)); + return ti_is_const(ti) && ti_const_val(ti) == val; } static inline bool ts_is_const(TCGTemp *ts) { - return ts_info(ts)->is_const; + return ti_is_const(ts_info(ts)); +} + +static inline bool ts_is_const_val(TCGTemp *ts, uint64_t val) +{ + return ti_is_const_val(ts_info(ts), val); } static inline bool arg_is_const(TCGArg arg) @@ -117,30 +101,19 @@ static inline bool arg_is_const(TCGArg arg) return ts_is_const(arg_temp(arg)); } -static inline bool ts_is_copy(TCGTemp *ts) +static inline bool arg_is_const_val(TCGArg arg, uint64_t val) { - return ts_info(ts)->next_copy != ts; + return ts_is_const_val(arg_temp(arg), val); } -/* Reset TEMP's state, possibly removing the temp for the list of copies. */ -static void reset_ts(TCGTemp *ts) +static inline bool ts_is_copy(TCGTemp *ts) { - 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; + return ts_info(ts)->next_copy != ts; } -static void reset_temp(TCGArg arg) +static TCGTemp *cmp_better_copy(TCGTemp *a, TCGTemp *b) { - reset_ts(arg_temp(arg)); + return a->kind < b->kind ? b : a; } /* Initialize and activate a temporary. */ @@ -162,11 +135,12 @@ 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; ti->z_mask = ts->val; - ti->s_mask = smask_from_value(ts->val); + ti->s_mask = INT64_MIN >> clrsb64(ts->val); } else { ti->is_const = false; ti->z_mask = -1; @@ -174,30 +148,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) +{ + 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) { - TCGTemp *i, *g, *l; + 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_TB) { - 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); } } +} + +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)); + } - /* If we didn't find a better representation, return the same temp. */ - return g ? g : l ? l : ts; + 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 +303,52 @@ 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 TCGOp *opt_insert_after(OptContext *ctx, TCGOp *op, + TCGOpcode opc, unsigned narg) +{ + return tcg_op_insert_after(ctx->tcg, op, opc, ctx->type, narg); +} + +static TCGOp *opt_insert_before(OptContext *ctx, TCGOp *op, + TCGOpcode opc, unsigned narg) +{ + return tcg_op_insert_before(ctx->tcg, op, opc, ctx->type, narg); +} + static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) { TCGTemp *dst_ts = arg_temp(dst); @@ -239,21 +362,19 @@ 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); switch (ctx->type) { case TCG_TYPE_I32: - new_op = INDEX_op_mov_i32; - break; case TCG_TYPE_I64: - new_op = INDEX_op_mov_i64; + new_op = INDEX_op_mov; break; case TCG_TYPE_V64: case TCG_TYPE_V128: case TCG_TYPE_V256: - /* TCGOP_VECL and TCGOP_VECE remain unchanged. */ + /* TCGOP_TYPE and TCGOP_VECE remain unchanged. */ new_op = INDEX_op_mov_vec; break; default: @@ -275,6 +396,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,174 +408,167 @@ 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) +static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, + uint64_t x, uint64_t y) { uint64_t l64, h64; switch (op) { - CASE_OP_32_64(add): + case INDEX_op_add: return x + y; - CASE_OP_32_64(sub): + case INDEX_op_sub: return x - y; - CASE_OP_32_64(mul): + case INDEX_op_mul: return x * y; - CASE_OP_32_64_VEC(and): + case INDEX_op_and: + case INDEX_op_and_vec: return x & y; - CASE_OP_32_64_VEC(or): + case INDEX_op_or: + case INDEX_op_or_vec: return x | y; - CASE_OP_32_64_VEC(xor): + case INDEX_op_xor: + case INDEX_op_xor_vec: return x ^ y; - case INDEX_op_shl_i32: - return (uint32_t)x << (y & 31); - - case INDEX_op_shl_i64: + case INDEX_op_shl: + if (type == TCG_TYPE_I32) { + return (uint32_t)x << (y & 31); + } return (uint64_t)x << (y & 63); - case INDEX_op_shr_i32: - return (uint32_t)x >> (y & 31); - - case INDEX_op_shr_i64: + case INDEX_op_shr: + if (type == TCG_TYPE_I32) { + return (uint32_t)x >> (y & 31); + } return (uint64_t)x >> (y & 63); - case INDEX_op_sar_i32: - return (int32_t)x >> (y & 31); - - case INDEX_op_sar_i64: + case INDEX_op_sar: + if (type == TCG_TYPE_I32) { + return (int32_t)x >> (y & 31); + } return (int64_t)x >> (y & 63); - case INDEX_op_rotr_i32: - return ror32(x, y & 31); - - case INDEX_op_rotr_i64: + case INDEX_op_rotr: + if (type == TCG_TYPE_I32) { + return ror32(x, y & 31); + } return ror64(x, y & 63); - case INDEX_op_rotl_i32: - return rol32(x, y & 31); - - case INDEX_op_rotl_i64: + case INDEX_op_rotl: + if (type == TCG_TYPE_I32) { + return rol32(x, y & 31); + } return rol64(x, y & 63); - CASE_OP_32_64_VEC(not): + case INDEX_op_not: + case INDEX_op_not_vec: return ~x; - CASE_OP_32_64(neg): + case INDEX_op_neg: return -x; - CASE_OP_32_64_VEC(andc): + case INDEX_op_andc: + case INDEX_op_andc_vec: return x & ~y; - CASE_OP_32_64_VEC(orc): + case INDEX_op_orc: + case INDEX_op_orc_vec: return x | ~y; - CASE_OP_32_64_VEC(eqv): + case INDEX_op_eqv: + case INDEX_op_eqv_vec: return ~(x ^ y); - CASE_OP_32_64_VEC(nand): + case INDEX_op_nand: + case INDEX_op_nand_vec: return ~(x & y); - CASE_OP_32_64_VEC(nor): + case INDEX_op_nor: + case INDEX_op_nor_vec: return ~(x | y); - case INDEX_op_clz_i32: - return (uint32_t)x ? clz32(x) : y; - - case INDEX_op_clz_i64: + case INDEX_op_clz: + if (type == TCG_TYPE_I32) { + return (uint32_t)x ? clz32(x) : y; + } return x ? clz64(x) : y; - case INDEX_op_ctz_i32: - return (uint32_t)x ? ctz32(x) : y; - - case INDEX_op_ctz_i64: + case INDEX_op_ctz: + if (type == TCG_TYPE_I32) { + return (uint32_t)x ? ctz32(x) : y; + } return x ? ctz64(x) : y; - case INDEX_op_ctpop_i32: - return ctpop32(x); - - case INDEX_op_ctpop_i64: - return ctpop64(x); - - CASE_OP_32_64(ext8s): - return (int8_t)x; - - CASE_OP_32_64(ext16s): - return (int16_t)x; + case INDEX_op_ctpop: + return type == TCG_TYPE_I32 ? ctpop32(x) : ctpop64(x); - CASE_OP_32_64(ext8u): - return (uint8_t)x; - - CASE_OP_32_64(ext16u): - return (uint16_t)x; - - CASE_OP_32_64(bswap16): + case INDEX_op_bswap16: x = bswap16(x); return y & TCG_BSWAP_OS ? (int16_t)x : x; - CASE_OP_32_64(bswap32): + case INDEX_op_bswap32: x = bswap32(x); return y & TCG_BSWAP_OS ? (int32_t)x : x; - case INDEX_op_bswap64_i64: + case INDEX_op_bswap64: return bswap64(x); case INDEX_op_ext_i32_i64: - case INDEX_op_ext32s_i64: return (int32_t)x; case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: - case INDEX_op_ext32u_i64: return (uint32_t)x; case INDEX_op_extrh_i64_i32: return (uint64_t)x >> 32; - case INDEX_op_muluh_i32: - return ((uint64_t)(uint32_t)x * (uint32_t)y) >> 32; - case INDEX_op_mulsh_i32: - return ((int64_t)(int32_t)x * (int32_t)y) >> 32; - - case INDEX_op_muluh_i64: + case INDEX_op_muluh: + if (type == TCG_TYPE_I32) { + return ((uint64_t)(uint32_t)x * (uint32_t)y) >> 32; + } mulu64(&l64, &h64, x, y); return h64; - case INDEX_op_mulsh_i64: + + case INDEX_op_mulsh: + if (type == TCG_TYPE_I32) { + return ((int64_t)(int32_t)x * (int32_t)y) >> 32; + } muls64(&l64, &h64, x, y); return h64; - case INDEX_op_div_i32: + case INDEX_op_divs: /* Avoid crashing on divide by zero, otherwise undefined. */ - return (int32_t)x / ((int32_t)y ? : 1); - case INDEX_op_divu_i32: - return (uint32_t)x / ((uint32_t)y ? : 1); - case INDEX_op_div_i64: + if (type == TCG_TYPE_I32) { + return (int32_t)x / ((int32_t)y ? : 1); + } return (int64_t)x / ((int64_t)y ? : 1); - case INDEX_op_divu_i64: + + case INDEX_op_divu: + if (type == TCG_TYPE_I32) { + return (uint32_t)x / ((uint32_t)y ? : 1); + } return (uint64_t)x / ((uint64_t)y ? : 1); - case INDEX_op_rem_i32: - return (int32_t)x % ((int32_t)y ? : 1); - case INDEX_op_remu_i32: - return (uint32_t)x % ((uint32_t)y ? : 1); - case INDEX_op_rem_i64: + case INDEX_op_rems: + if (type == TCG_TYPE_I32) { + return (int32_t)x % ((int32_t)y ? : 1); + } return (int64_t)x % ((int64_t)y ? : 1); - case INDEX_op_remu_i64: + + case INDEX_op_remu: + if (type == TCG_TYPE_I32) { + return (uint32_t)x % ((uint32_t)y ? : 1); + } return (uint64_t)x % ((uint64_t)y ? : 1); default: @@ -460,7 +579,7 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) static uint64_t do_constant_folding(TCGOpcode op, TCGType type, uint64_t x, uint64_t y) { - uint64_t res = do_constant_folding_2(op, x, y); + uint64_t res = do_constant_folding_2(op, type, x, y); if (type == TCG_TYPE_I32) { res = (int32_t)res; } @@ -490,9 +609,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: - g_assert_not_reached(); + 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) @@ -518,12 +643,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: - g_assert_not_reached(); + 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: @@ -538,9 +669,14 @@ static bool do_constant_folding_cond_eq(TCGCond c) case TCG_COND_LEU: case TCG_COND_EQ: return 1; - default: - g_assert_not_reached(); + case TCG_COND_TSTEQ: + case TCG_COND_TSTNE: + return -1; + case TCG_COND_ALWAYS: + case TCG_COND_NEVER: + break; } + g_assert_not_reached(); } /* @@ -565,11 +701,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; @@ -578,43 +716,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. @@ -628,12 +729,18 @@ static int do_constant_folding_cond2(TCGArg *p1, TCGArg *p2, TCGCond c) #define NO_DEST temp_arg(NULL) +static int pref_commutative(TempOptInfo *ti) +{ + /* Slight preference for non-zero constants second. */ + return !ti_is_const(ti) ? 0 : ti_const_val(ti) ? 3 : 2; +} + static bool swap_commutative(TCGArg dest, TCGArg *p1, TCGArg *p2) { TCGArg a1 = *p1, a2 = *p2; int sum = 0; - sum += arg_is_const(a1); - sum -= arg_is_const(a2); + sum += pref_commutative(arg_info(a1)); + sum -= pref_commutative(arg_info(a2)); /* Prefer the constant in second argument, and then the form op a, a, b, which is better handled on non-RISC hosts. */ @@ -648,10 +755,10 @@ static bool swap_commutative(TCGArg dest, TCGArg *p1, TCGArg *p2) static bool swap_commutative2(TCGArg *p1, TCGArg *p2) { int sum = 0; - sum += arg_is_const(p1[0]); - sum += arg_is_const(p1[1]); - sum -= arg_is_const(p2[0]); - sum -= arg_is_const(p2[1]); + sum += pref_commutative(arg_info(p1[0])); + sum += pref_commutative(arg_info(p1[1])); + sum -= pref_commutative(arg_info(p2[0])); + sum -= pref_commutative(arg_info(p2[1])); if (sum > 0) { TCGArg t; t = p1[0], p1[0] = p2[0], p2[0] = t; @@ -661,95 +768,251 @@ static bool swap_commutative2(TCGArg *p1, TCGArg *p2) return false; } -static void init_arguments(OptContext *ctx, TCGOp *op, int nb_args) +/* + * 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) { - for (int i = 0; i < nb_args; i++) { - TCGTemp *ts = arg_temp(op->args[i]); - init_ts_info(ctx, ts); - } -} + TCGCond cond; + TempOptInfo *i1; + bool swap; + int r; -static void copy_propagate(OptContext *ctx, TCGOp *op, - int nb_oargs, int nb_iargs) -{ - TCGContext *s = ctx->tcg; + swap = swap_commutative(dest, p1, p2); + cond = *pcond; + if (swap) { + *pcond = cond = tcg_swap_cond(cond); + } - for (int i = nb_oargs; i < nb_oargs + nb_iargs; i++) { - TCGTemp *ts = arg_temp(op->args[i]); - if (ts_is_copy(ts)) { - op->args[i] = temp_arg(find_better_copy(s, ts)); - } + r = do_constant_folding_cond(ctx->type, *p1, *p2, cond); + if (r >= 0) { + return r; + } + if (!is_tst_cond(cond)) { + return -1; } -} -static void finish_folding(OptContext *ctx, TCGOp *op) -{ - const TCGOpDef *def = &tcg_op_defs[op->opc]; - int i, nb_oargs; + i1 = arg_info(*p1); /* - * For an opcode that ends a BB, reset all temp data. - * We do no cross-BB optimization. + * TSTNE x,x -> NE x,0 + * TSTNE x,i -> NE x,0 if i includes all nonzero bits of x */ - if (def->flags & TCG_OPF_BB_END) { - memset(&ctx->temps_used, 0, sizeof(ctx->temps_used)); - ctx->prev_mb = NULL; - return; + if (args_are_copies(*p1, *p2) || + (arg_is_const(*p2) && (i1->z_mask & ~arg_info(*p2)->val) == 0)) { + *p2 = arg_new_constant(ctx, 0); + *pcond = tcg_tst_eqne_cond(cond); + return -1; } - nb_oargs = def->nb_oargs; - for (i = 0; i < nb_oargs; i++) { - TCGTemp *ts = arg_temp(op->args[i]); - reset_ts(ts); - /* - * Save the corresponding known-zero/sign bits mask for the - * first output argument (only one supported so far). - */ - if (i == 0) { - ts_info(ts)->z_mask = ctx->z_mask; - ts_info(ts)->s_mask = ctx->s_mask; - } + /* TSTNE x,i -> LT x,0 if i only includes sign bit copies */ + if (arg_is_const(*p2) && (arg_info(*p2)->val & ~i1->s_mask) == 0) { + *p2 = arg_new_constant(ctx, 0); + *pcond = tcg_tst_ltge_cond(cond); + return -1; } -} -/* - * The fold_* functions return true when processing is complete, - * usually by folding the operation to a constant or to a copy, - * and calling tcg_opt_gen_{mov,movi}. They may do other things, - * like collect information about the value produced, for use in - * optimizing a subsequent operation. - * - * These first fold_* functions are all helpers, used by other - * folders for more specific operations. - */ + /* Expand to AND with a temporary if no backend support. */ + if (!TCG_TARGET_HAS_tst) { + TCGOp *op2 = opt_insert_before(ctx, op, INDEX_op_and, 3); + TCGArg tmp = arg_new_temp(ctx); -static bool fold_const1(OptContext *ctx, TCGOp *op) -{ - if (arg_is_const(op->args[1])) { - uint64_t t; + op2->args[0] = tmp; + op2->args[1] = *p1; + op2->args[2] = *p2; - t = arg_info(op->args[1])->val; - t = do_constant_folding(op->opc, ctx->type, t, 0); - return tcg_opt_gen_movi(ctx, op, op->args[0], t); + *p1 = tmp; + *p2 = arg_new_constant(ctx, 0); + *pcond = tcg_tst_eqne_cond(cond); } - return false; + return -1; } -static bool fold_const2(OptContext *ctx, TCGOp *op) +static int do_constant_folding_cond2(OptContext *ctx, TCGOp *op, TCGArg *args) { - 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; + TCGArg al, ah, bl, bh; + TCGCond c; + bool swap; + int r; - t1 = do_constant_folding(op->opc, ctx->type, t1, t2); - return tcg_opt_gen_movi(ctx, op, op->args[0], t1); + swap = swap_commutative2(args, args + 2); + c = args[4]; + if (swap) { + args[4] = c = tcg_swap_cond(c); } - return false; -} -static bool fold_commutative(OptContext *ctx, TCGOp *op) -{ - swap_commutative(op->args[0], &op->args[1], &op->args[2]); + 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 = opt_insert_before(ctx, op, INDEX_op_and, 3); + TCGOp *op2 = opt_insert_before(ctx, op, INDEX_op_and, 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]); + init_ts_info(ctx, ts); + } +} + +static void copy_propagate(OptContext *ctx, TCGOp *op, + int nb_oargs, int nb_iargs) +{ + for (int i = nb_oargs; i < nb_oargs + nb_iargs; i++) { + TCGTemp *ts = arg_temp(op->args[i]); + if (ts_is_copy(ts)) { + op->args[i] = temp_arg(find_better_copy(ts)); + } + } +} + +static void finish_bb(OptContext *ctx) +{ + /* We only optimize memory barriers across basic blocks. */ + ctx->prev_mb = NULL; +} + +static void finish_ebb(OptContext *ctx) +{ + finish_bb(ctx); + /* We only optimize across extended basic blocks. */ + memset(&ctx->temps_used, 0, sizeof(ctx->temps_used)); + remove_mem_copy_all(ctx); +} + +static bool finish_folding(OptContext *ctx, TCGOp *op) +{ + const TCGOpDef *def = &tcg_op_defs[op->opc]; + int i, nb_oargs; + + nb_oargs = def->nb_oargs; + for (i = 0; i < nb_oargs; i++) { + TCGTemp *ts = arg_temp(op->args[i]); + reset_ts(ctx, ts); + } + return true; +} + +/* + * The fold_* functions return true when processing is complete, + * usually by folding the operation to a constant or to a copy, + * and calling tcg_opt_gen_{mov,movi}. They may do other things, + * like collect information about the value produced, for use in + * optimizing a subsequent operation. + * + * These first fold_* functions are all helpers, used by other + * folders for more specific operations. + */ + +static bool fold_const1(OptContext *ctx, TCGOp *op) +{ + if (arg_is_const(op->args[1])) { + uint64_t t; + + t = arg_info(op->args[1])->val; + t = do_constant_folding(op->opc, ctx->type, t, 0); + return tcg_opt_gen_movi(ctx, op, op->args[0], t); + } + return false; +} + +static bool fold_const2(OptContext *ctx, TCGOp *op) +{ + 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; + + t1 = do_constant_folding(op->opc, ctx->type, t1, t2); + return tcg_opt_gen_movi(ctx, op, op->args[0], t1); + } + return false; +} + +static bool fold_commutative(OptContext *ctx, TCGOp *op) +{ + swap_commutative(op->args[0], &op->args[1], &op->args[2]); return false; } @@ -759,11 +1022,22 @@ static bool fold_const2_commutative(OptContext *ctx, TCGOp *op) return fold_const2(ctx, op); } -static bool fold_masks(OptContext *ctx, TCGOp *op) +/* + * Record "zero" and "sign" masks for the single output of @op. + * See TempOptInfo definition of z_mask and s_mask. + * If z_mask allows, fold the output to constant zero. + * The passed s_mask may be augmented by z_mask. + */ +static bool fold_masks_zs(OptContext *ctx, TCGOp *op, + uint64_t z_mask, int64_t s_mask) { - uint64_t a_mask = ctx->a_mask; - uint64_t z_mask = ctx->z_mask; - uint64_t s_mask = ctx->s_mask; + const TCGOpDef *def = &tcg_op_defs[op->opc]; + TCGTemp *ts; + TempOptInfo *ti; + int rep; + + /* Only single-output opcodes are supported here. */ + tcg_debug_assert(def->nb_oargs == 1); /* * 32-bit ops generate 32-bit results, which for the purpose of @@ -773,16 +1047,49 @@ static bool fold_masks(OptContext *ctx, TCGOp *op) * type changing opcodes. */ if (ctx->type == TCG_TYPE_I32) { - a_mask = (int32_t)a_mask; z_mask = (int32_t)z_mask; - s_mask |= MAKE_64BIT_MASK(32, 32); - ctx->z_mask = z_mask; - ctx->s_mask = s_mask; + s_mask |= INT32_MIN; } if (z_mask == 0) { return tcg_opt_gen_movi(ctx, op, op->args[0], 0); } + + ts = arg_temp(op->args[0]); + reset_ts(ctx, ts); + + ti = ts_info(ts); + ti->z_mask = z_mask; + + /* Canonicalize s_mask and incorporate data from z_mask. */ + rep = clz64(~s_mask); + rep = MAX(rep, clz64(z_mask)); + rep = MAX(rep - 1, 0); + ti->s_mask = INT64_MIN >> rep; + + return true; +} + +static bool fold_masks_z(OptContext *ctx, TCGOp *op, uint64_t z_mask) +{ + return fold_masks_zs(ctx, op, z_mask, 0); +} + +static bool fold_masks_s(OptContext *ctx, TCGOp *op, uint64_t s_mask) +{ + return fold_masks_zs(ctx, op, -1, s_mask); +} + +/* + * An "affected" mask bit is 0 if and only if the result is identical + * to the first input. Thus if the entire mask is 0, the operation + * is equivalent to a copy. + */ +static bool fold_affected_mask(OptContext *ctx, TCGOp *op, uint64_t a_mask) +{ + if (ctx->type == TCG_TYPE_I32) { + a_mask = (uint32_t)a_mask; + } if (a_mask == 0) { return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); } @@ -802,12 +1109,9 @@ static bool fold_to_not(OptContext *ctx, TCGOp *op, int idx) switch (ctx->type) { case TCG_TYPE_I32: - not_op = INDEX_op_not_i32; - have_not = TCG_TARGET_HAS_not_i32; - break; case TCG_TYPE_I64: - not_op = INDEX_op_not_i64; - have_not = TCG_TARGET_HAS_not_i64; + not_op = INDEX_op_not; + have_not = tcg_op_supported(INDEX_op_not, ctx->type, 0); break; case TCG_TYPE_V64: case TCG_TYPE_V128: @@ -829,7 +1133,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; @@ -838,7 +1142,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; @@ -847,7 +1151,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; @@ -856,7 +1160,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; @@ -865,7 +1169,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; @@ -898,13 +1202,19 @@ static bool fold_xx_to_x(OptContext *ctx, TCGOp *op) * 3) those that produce information about the result value. */ +static bool fold_addco(OptContext *ctx, TCGOp *op); +static bool fold_or(OptContext *ctx, TCGOp *op); +static bool fold_orc(OptContext *ctx, TCGOp *op); +static bool fold_subbo(OptContext *ctx, TCGOp *op); +static bool fold_xor(OptContext *ctx, TCGOp *op); + static bool fold_add(OptContext *ctx, TCGOp *op) { if (fold_const2_commutative(ctx, op) || fold_xi_to_x(ctx, op, 0)) { return true; } - return false; + return finish_folding(ctx, op); } /* We cannot as yet do_constant_folding with vectors. */ @@ -914,71 +1224,177 @@ static bool fold_add_vec(OptContext *ctx, TCGOp *op) fold_xi_to_x(ctx, op, 0)) { return true; } - return false; + return finish_folding(ctx, op); } -static bool fold_addsub2(OptContext *ctx, TCGOp *op, bool add) +static void squash_prev_carryout(OptContext *ctx, TCGOp *op) { - 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])) { - 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; - uint64_t bh = arg_info(op->args[5])->val; - TCGArg rl, rh; - TCGOp *op2; + TempOptInfo *t2; - if (ctx->type == TCG_TYPE_I32) { - uint64_t a = deposit64(al, 32, 32, ah); - uint64_t b = deposit64(bl, 32, 32, bh); + op = QTAILQ_PREV(op, link); + switch (op->opc) { + case INDEX_op_addco: + op->opc = INDEX_op_add; + fold_add(ctx, op); + break; + case INDEX_op_addcio: + op->opc = INDEX_op_addci; + break; + case INDEX_op_addc1o: + op->opc = INDEX_op_add; + t2 = arg_info(op->args[2]); + if (ti_is_const(t2)) { + op->args[2] = arg_new_constant(ctx, ti_const_val(t2) + 1); + /* Perform other constant folding, if needed. */ + fold_add(ctx, op); + } else { + TCGArg ret = op->args[0]; + op = opt_insert_after(ctx, op, INDEX_op_add, 3); + op->args[0] = ret; + op->args[1] = ret; + op->args[2] = arg_new_constant(ctx, 1); + } + break; + default: + g_assert_not_reached(); + } +} - if (add) { - a += b; - } else { - a -= b; - } +static bool fold_addci(OptContext *ctx, TCGOp *op) +{ + fold_commutative(ctx, op); - al = sextract64(a, 0, 32); - ah = sextract64(a, 32, 32); + if (ctx->carry_state < 0) { + return finish_folding(ctx, op); + } + + squash_prev_carryout(ctx, op); + op->opc = INDEX_op_add; + + if (ctx->carry_state > 0) { + TempOptInfo *t2 = arg_info(op->args[2]); + + /* + * Propagate the known carry-in into a constant, if possible. + * Otherwise emit a second add +1. + */ + if (ti_is_const(t2)) { + op->args[2] = arg_new_constant(ctx, ti_const_val(t2) + 1); } else { - Int128 a = int128_make128(al, ah); - Int128 b = int128_make128(bl, bh); + TCGOp *op2 = opt_insert_before(ctx, op, INDEX_op_add, 3); - if (add) { - a = int128_add(a, b); - } else { - a = int128_sub(a, b); - } + op2->args[0] = op->args[0]; + op2->args[1] = op->args[1]; + op2->args[2] = op->args[2]; + fold_add(ctx, op2); - al = int128_getlo(a); - ah = int128_gethi(a); + op->args[1] = op->args[0]; + op->args[2] = arg_new_constant(ctx, 1); } + } - rl = op->args[0]; - rh = op->args[1]; + ctx->carry_state = -1; + return fold_add(ctx, op); +} - /* The proper opcode is supplied by tcg_opt_gen_mov. */ - op2 = tcg_op_insert_before(ctx->tcg, op, 0, 2); +static bool fold_addcio(OptContext *ctx, TCGOp *op) +{ + TempOptInfo *t1, *t2; + int carry_out = -1; + uint64_t sum, max; - tcg_opt_gen_movi(ctx, op, rl, al); - tcg_opt_gen_movi(ctx, op2, rh, ah); - return true; + fold_commutative(ctx, op); + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + + /* + * The z_mask value is >= the maximum value that can be represented + * with the known zero bits. So adding the z_mask values will not + * overflow if and only if the true values cannot overflow. + */ + if (!uadd64_overflow(t1->z_mask, t2->z_mask, &sum) && + !uadd64_overflow(sum, ctx->carry_state != 0, &sum)) { + carry_out = 0; } - return false; + + if (ctx->carry_state < 0) { + ctx->carry_state = carry_out; + return finish_folding(ctx, op); + } + + squash_prev_carryout(ctx, op); + if (ctx->carry_state == 0) { + goto do_addco; + } + + /* Propagate the known carry-in into a constant, if possible. */ + max = ctx->type == TCG_TYPE_I32 ? UINT32_MAX : UINT64_MAX; + if (ti_is_const(t2)) { + uint64_t v = ti_const_val(t2) & max; + if (v < max) { + op->args[2] = arg_new_constant(ctx, v + 1); + goto do_addco; + } + /* max + known carry in produces known carry out. */ + carry_out = 1; + } + if (ti_is_const(t1)) { + uint64_t v = ti_const_val(t1) & max; + if (v < max) { + op->args[1] = arg_new_constant(ctx, v + 1); + goto do_addco; + } + carry_out = 1; + } + + /* Adjust the opcode to remember the known carry-in. */ + op->opc = INDEX_op_addc1o; + ctx->carry_state = carry_out; + return finish_folding(ctx, op); + + do_addco: + op->opc = INDEX_op_addco; + return fold_addco(ctx, op); } -static bool fold_add2(OptContext *ctx, TCGOp *op) +static bool fold_addco(OptContext *ctx, TCGOp *op) { - /* Note that the high and low parts may be independently swapped. */ - swap_commutative(op->args[0], &op->args[2], &op->args[4]); - swap_commutative(op->args[1], &op->args[3], &op->args[5]); + TempOptInfo *t1, *t2; + int carry_out = -1; + uint64_t ign; - return fold_addsub2(ctx, op, true); + fold_commutative(ctx, op); + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + + if (ti_is_const(t2)) { + uint64_t v2 = ti_const_val(t2); + + if (ti_is_const(t1)) { + uint64_t v1 = ti_const_val(t1); + /* Given sign-extension of z_mask for I32, we need not truncate. */ + carry_out = uadd64_overflow(v1, v2, &ign); + } else if (v2 == 0) { + carry_out = 0; + } + } else { + /* + * The z_mask value is >= the maximum value that can be represented + * with the known zero bits. So adding the z_mask values will not + * overflow if and only if the true values cannot overflow. + */ + if (!uadd64_overflow(t1->z_mask, t2->z_mask, &ign)) { + carry_out = 0; + } + } + ctx->carry_state = carry_out; + return finish_folding(ctx, op); } static bool fold_and(OptContext *ctx, TCGOp *op) { - uint64_t z1, z2; + uint64_t z1, z2, z_mask, s_mask; + TempOptInfo *t1, *t2; if (fold_const2_commutative(ctx, op) || fold_xi_to_i(ctx, op, 0) || @@ -987,31 +1403,34 @@ static bool fold_and(OptContext *ctx, TCGOp *op) return true; } - z1 = arg_info(op->args[1])->z_mask; - z2 = arg_info(op->args[2])->z_mask; - ctx->z_mask = z1 & z2; - - /* - * Sign repetitions are perforce all identical, whether they are 1 or 0. - * Bitwise operations preserve the relative quantity of the repetitions. - */ - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + z1 = t1->z_mask; + z2 = t2->z_mask; /* * Known-zeros does not imply known-ones. Therefore unless * arg2 is constant, we can't infer affected bits from it. */ - if (arg_is_const(op->args[2])) { - ctx->a_mask = z1 & ~z2; + if (ti_is_const(t2) && fold_affected_mask(ctx, op, z1 & ~z2)) { + return true; } - return fold_masks(ctx, op); + z_mask = z1 & z2; + + /* + * Sign repetitions are perforce all identical, whether they are 1 or 0. + * Bitwise operations preserve the relative quantity of the repetitions. + */ + s_mask = t1->s_mask & t2->s_mask; + + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_andc(OptContext *ctx, TCGOp *op) { - uint64_t z1; + uint64_t z_mask, s_mask; + TempOptInfo *t1, *t2; if (fold_const2(ctx, op) || fold_xx_to_i(ctx, op, 0) || @@ -1020,34 +1439,104 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) return true; } - z1 = arg_info(op->args[1])->z_mask; + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + z_mask = t1->z_mask; + + if (ti_is_const(t2)) { + /* Fold andc r,x,i to and r,x,~i. */ + switch (ctx->type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + op->opc = INDEX_op_and; + break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + op->opc = INDEX_op_and_vec; + break; + default: + g_assert_not_reached(); + } + op->args[2] = arg_new_constant(ctx, ~ti_const_val(t2)); + return fold_and(ctx, op); + } /* * Known-zeros does not imply known-ones. Therefore unless * arg2 is constant, we can't infer anything from it. */ - if (arg_is_const(op->args[2])) { - uint64_t z2 = ~arg_info(op->args[2])->z_mask; - ctx->a_mask = z1 & ~z2; - z1 &= z2; + if (ti_is_const(t2)) { + uint64_t v2 = ti_const_val(t2); + if (fold_affected_mask(ctx, op, z_mask & v2)) { + return true; + } + z_mask &= ~v2; } - ctx->z_mask = z1; - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return fold_masks(ctx, op); + s_mask = t1->s_mask & t2->s_mask; + return fold_masks_zs(ctx, op, z_mask, s_mask); } -static bool fold_brcond(OptContext *ctx, TCGOp *op) +static bool fold_bitsel_vec(OptContext *ctx, TCGOp *op) { - TCGCond cond = op->args[2]; - int i; + /* 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 (swap_commutative(NO_DEST, &op->args[0], &op->args[1])) { - op->args[2] = cond = tcg_swap_cond(cond); + 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 finish_folding(ctx, op); +} - i = do_constant_folding_cond(ctx->type, op->args[0], op->args[1], cond); +static bool fold_brcond(OptContext *ctx, TCGOp *op) +{ + 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; @@ -1055,21 +1544,22 @@ static bool fold_brcond(OptContext *ctx, TCGOp *op) if (i > 0) { op->opc = INDEX_op_br; op->args[0] = op->args[3]; + finish_ebb(ctx); + } else { + finish_bb(ctx); } - return false; + return true; } 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; } @@ -1081,8 +1571,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; @@ -1110,24 +1600,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; + 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->opc = INDEX_op_brcond; 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) { @@ -1136,44 +1639,45 @@ static bool fold_brcond2(OptContext *ctx, TCGOp *op) } op->opc = INDEX_op_br; op->args[0] = label; - break; + finish_ebb(ctx); + return true; } - return false; + + finish_bb(ctx); + return true; } static bool fold_bswap(OptContext *ctx, TCGOp *op) { uint64_t z_mask, s_mask, sign; + TempOptInfo *t1 = arg_info(op->args[1]); - if (arg_is_const(op->args[1])) { - uint64_t t = arg_info(op->args[1])->val; - - t = do_constant_folding(op->opc, ctx->type, t, op->args[2]); - return tcg_opt_gen_movi(ctx, op, op->args[0], t); + if (ti_is_const(t1)) { + return tcg_opt_gen_movi(ctx, op, op->args[0], + do_constant_folding(op->opc, ctx->type, + ti_const_val(t1), + op->args[2])); } - z_mask = arg_info(op->args[1])->z_mask; - + z_mask = t1->z_mask; switch (op->opc) { - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: + case INDEX_op_bswap16: z_mask = bswap16(z_mask); sign = INT16_MIN; break; - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: + case INDEX_op_bswap32: z_mask = bswap32(z_mask); sign = INT32_MIN; break; - case INDEX_op_bswap64_i64: + case INDEX_op_bswap64: z_mask = bswap64(z_mask); sign = INT64_MIN; break; default: g_assert_not_reached(); } - s_mask = smask_from_zmask(z_mask); + s_mask = 0; switch (op->args[2] & (TCG_BSWAP_OZ | TCG_BSWAP_OS)) { case TCG_BSWAP_OZ: break; @@ -1181,19 +1685,17 @@ static bool fold_bswap(OptContext *ctx, TCGOp *op) /* If the sign bit may be 1, force all the bits above to 1. */ if (z_mask & sign) { z_mask |= sign; - s_mask = sign << 1; } + /* The value and therefore s_mask is explicitly sign-extended. */ + s_mask = sign; break; default: /* The high bits are undefined: force all bits above the sign to 1. */ z_mask |= sign << 1; - s_mask = 0; break; } - ctx->z_mask = z_mask; - ctx->s_mask = s_mask; - return fold_masks(ctx, op); + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_call(OptContext *ctx, TCGOp *op) @@ -1213,14 +1715,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. */ @@ -1228,12 +1735,44 @@ static bool fold_call(OptContext *ctx, TCGOp *op) return true; } +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 finish_folding(ctx, op); +} + +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 finish_folding(ctx, op); +} + static bool fold_count_zeros(OptContext *ctx, TCGOp *op) { - uint64_t z_mask; + uint64_t z_mask, s_mask; + TempOptInfo *t1 = arg_info(op->args[1]); + TempOptInfo *t2 = arg_info(op->args[2]); - if (arg_is_const(op->args[1])) { - uint64_t t = arg_info(op->args[1])->val; + if (ti_is_const(t1)) { + uint64_t t = ti_const_val(t1); if (t != 0) { t = do_constant_folding(op->opc, ctx->type, t, 0); @@ -1252,82 +1791,77 @@ static bool fold_count_zeros(OptContext *ctx, TCGOp *op) default: g_assert_not_reached(); } - ctx->z_mask = arg_info(op->args[2])->z_mask | z_mask; - ctx->s_mask = smask_from_zmask(ctx->z_mask); - return false; + s_mask = ~z_mask; + z_mask |= t2->z_mask; + s_mask &= t2->s_mask; + + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_ctpop(OptContext *ctx, TCGOp *op) { + uint64_t z_mask; + if (fold_const1(ctx, op)) { return true; } switch (ctx->type) { case TCG_TYPE_I32: - ctx->z_mask = 32 | 31; + z_mask = 32 | 31; break; case TCG_TYPE_I64: - ctx->z_mask = 64 | 63; + z_mask = 64 | 63; break; default: g_assert_not_reached(); } - ctx->s_mask = smask_from_zmask(ctx->z_mask); - return false; + return fold_masks_z(ctx, op, z_mask); } 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; + TempOptInfo *t1 = arg_info(op->args[1]); + TempOptInfo *t2 = arg_info(op->args[2]); + int ofs = op->args[3]; + int len = op->args[4]; + int width = 8 * tcg_type_size(ctx->type); + uint64_t z_mask, s_mask; - t1 = deposit64(t1, op->args[3], op->args[4], t2); - 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(); + if (ti_is_const(t1) && ti_is_const(t2)) { + return tcg_opt_gen_movi(ctx, op, op->args[0], + deposit64(ti_const_val(t1), ofs, len, + ti_const_val(t2))); } /* Inserting a value into zero at offset 0. */ - if (arg_is_const(op->args[1]) - && arg_info(op->args[1])->val == 0 - && op->args[3] == 0) { - uint64_t mask = MAKE_64BIT_MASK(0, op->args[4]); + if (ti_is_const_val(t1, 0) && ofs == 0) { + uint64_t mask = MAKE_64BIT_MASK(0, len); - op->opc = and_opc; + op->opc = INDEX_op_and; op->args[1] = op->args[2]; - op->args[2] = temp_arg(tcg_constant_internal(ctx->type, mask)); - ctx->z_mask = mask & arg_info(op->args[1])->z_mask; - return false; + op->args[2] = arg_new_constant(ctx, mask); + return fold_and(ctx, op); } /* Inserting zero into a value. */ - if (arg_is_const(op->args[2]) - && arg_info(op->args[2])->val == 0) { - uint64_t mask = deposit64(-1, op->args[3], op->args[4], 0); + if (ti_is_const_val(t2, 0)) { + uint64_t mask = deposit64(-1, ofs, len, 0); - op->opc = and_opc; - op->args[2] = temp_arg(tcg_constant_internal(ctx->type, mask)); - ctx->z_mask = mask & arg_info(op->args[1])->z_mask; - return false; + op->opc = INDEX_op_and; + op->args[2] = arg_new_constant(ctx, mask); + return fold_and(ctx, op); } - ctx->z_mask = deposit64(arg_info(op->args[1])->z_mask, - op->args[3], op->args[4], - arg_info(op->args[2])->z_mask); - return false; + /* The s_mask from the top portion of the deposit is still valid. */ + if (ofs + len == width) { + s_mask = t2->s_mask << ofs; + } else { + s_mask = t1->s_mask & ~MAKE_64BIT_MASK(0, ofs + len); + } + + z_mask = deposit64(t1->z_mask, ofs, len, t2->z_mask); + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_divide(OptContext *ctx, TCGOp *op) @@ -1336,7 +1870,7 @@ static bool fold_divide(OptContext *ctx, TCGOp *op) fold_xi_to_x(ctx, op, 1)) { return true; } - return false; + return finish_folding(ctx, op); } static bool fold_dup(OptContext *ctx, TCGOp *op) @@ -1346,7 +1880,7 @@ static bool fold_dup(OptContext *ctx, TCGOp *op) t = dup_const(TCGOP_VECE(op), t); return tcg_opt_gen_movi(ctx, op, op->args[0], t); } - return false; + return finish_folding(ctx, op); } static bool fold_dup2(OptContext *ctx, TCGOp *op) @@ -1361,45 +1895,64 @@ static bool fold_dup2(OptContext *ctx, TCGOp *op) op->opc = INDEX_op_dup_vec; TCGOP_VECE(op) = MO_32; } - return false; + return finish_folding(ctx, op); } static bool fold_eqv(OptContext *ctx, TCGOp *op) { + uint64_t s_mask; + TempOptInfo *t1, *t2; + if (fold_const2_commutative(ctx, op) || fold_xi_to_x(ctx, op, -1) || fold_xi_to_not(ctx, op, 0)) { return true; } - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return false; + t2 = arg_info(op->args[2]); + if (ti_is_const(t2)) { + /* Fold eqv r,x,i to xor r,x,~i. */ + switch (ctx->type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + op->opc = INDEX_op_xor; + break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + op->opc = INDEX_op_xor_vec; + break; + default: + g_assert_not_reached(); + } + op->args[2] = arg_new_constant(ctx, ~ti_const_val(t2)); + return fold_xor(ctx, op); + } + + t1 = arg_info(op->args[1]); + s_mask = t1->s_mask & t2->s_mask; + return fold_masks_s(ctx, op, s_mask); } static bool fold_extract(OptContext *ctx, TCGOp *op) { uint64_t z_mask_old, z_mask; + TempOptInfo *t1 = arg_info(op->args[1]); int pos = op->args[2]; int len = op->args[3]; - if (arg_is_const(op->args[1])) { - uint64_t t; - - t = arg_info(op->args[1])->val; - t = extract64(t, pos, len); - return tcg_opt_gen_movi(ctx, op, op->args[0], t); + if (ti_is_const(t1)) { + return tcg_opt_gen_movi(ctx, op, op->args[0], + extract64(ti_const_val(t1), pos, len)); } - z_mask_old = arg_info(op->args[1])->z_mask; + z_mask_old = t1->z_mask; z_mask = extract64(z_mask_old, pos, len); - if (pos == 0) { - ctx->a_mask = z_mask_old ^ z_mask; + if (pos == 0 && fold_affected_mask(ctx, op, z_mask_old ^ z_mask)) { + return true; } - ctx->z_mask = z_mask; - ctx->s_mask = smask_from_zmask(z_mask); - return fold_masks(ctx, op); + return fold_masks_z(ctx, op, z_mask); } static bool fold_extract2(OptContext *ctx, TCGOp *op) @@ -1409,104 +1962,63 @@ static bool fold_extract2(OptContext *ctx, TCGOp *op) uint64_t v2 = arg_info(op->args[2])->val; int shr = op->args[3]; - if (op->opc == INDEX_op_extract2_i64) { - v1 >>= shr; - v2 <<= 64 - shr; - } else { + if (ctx->type == TCG_TYPE_I32) { v1 = (uint32_t)v1 >> shr; v2 = (uint64_t)((int32_t)v2 << (32 - shr)); + } else { + v1 >>= shr; + v2 <<= 64 - shr; } return tcg_opt_gen_movi(ctx, op, op->args[0], v1 | v2); } - return false; + return finish_folding(ctx, op); } static bool fold_exts(OptContext *ctx, TCGOp *op) { - uint64_t s_mask_old, s_mask, z_mask, sign; - bool type_change = false; + uint64_t s_mask, z_mask; + TempOptInfo *t1; if (fold_const1(ctx, op)) { return true; } - z_mask = arg_info(op->args[1])->z_mask; - s_mask = arg_info(op->args[1])->s_mask; - s_mask_old = s_mask; + t1 = arg_info(op->args[1]); + z_mask = t1->z_mask; + s_mask = t1->s_mask; switch (op->opc) { - CASE_OP_32_64(ext8s): - sign = INT8_MIN; - z_mask = (uint8_t)z_mask; - break; - CASE_OP_32_64(ext16s): - sign = INT16_MIN; - z_mask = (uint16_t)z_mask; - break; case INDEX_op_ext_i32_i64: - type_change = true; - QEMU_FALLTHROUGH; - case INDEX_op_ext32s_i64: - sign = INT32_MIN; - z_mask = (uint32_t)z_mask; + s_mask |= INT32_MIN; + z_mask = (int32_t)z_mask; break; default: g_assert_not_reached(); } - - if (z_mask & sign) { - z_mask |= sign; - } - s_mask |= sign << 1; - - ctx->z_mask = z_mask; - ctx->s_mask = s_mask; - if (!type_change) { - ctx->a_mask = s_mask & ~s_mask_old; - } - - return fold_masks(ctx, op); + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_extu(OptContext *ctx, TCGOp *op) { - uint64_t z_mask_old, z_mask; - bool type_change = false; + uint64_t z_mask; if (fold_const1(ctx, op)) { return true; } - z_mask_old = z_mask = arg_info(op->args[1])->z_mask; - + z_mask = arg_info(op->args[1])->z_mask; switch (op->opc) { - CASE_OP_32_64(ext8u): - z_mask = (uint8_t)z_mask; - break; - CASE_OP_32_64(ext16u): - z_mask = (uint16_t)z_mask; - break; case INDEX_op_extrl_i64_i32: case INDEX_op_extu_i32_i64: - type_change = true; - QEMU_FALLTHROUGH; - case INDEX_op_ext32u_i64: z_mask = (uint32_t)z_mask; break; case INDEX_op_extrh_i64_i32: - type_change = true; z_mask >>= 32; break; default: g_assert_not_reached(); } - - ctx->z_mask = z_mask; - ctx->s_mask = smask_from_zmask(z_mask); - if (!type_change) { - ctx->a_mask = z_mask_old ^ z_mask; - } - return fold_masks(ctx, op); + return fold_masks_z(ctx, op, z_mask); } static bool fold_mb(OptContext *ctx, TCGOp *op) @@ -1540,71 +2052,55 @@ static bool fold_mov(OptContext *ctx, TCGOp *op) static bool fold_movcond(OptContext *ctx, TCGOp *op) { - TCGCond cond = op->args[5]; + uint64_t z_mask, s_mask; + TempOptInfo *tt, *ft; 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]); } - ctx->z_mask = arg_info(op->args[3])->z_mask - | arg_info(op->args[4])->z_mask; - ctx->s_mask = arg_info(op->args[3])->s_mask - & arg_info(op->args[4])->s_mask; - - 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, negopc = 0; + tt = arg_info(op->args[3]); + ft = arg_info(op->args[4]); + z_mask = tt->z_mask | ft->z_mask; + s_mask = tt->s_mask & ft->s_mask; - 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(); - } + if (ti_is_const(tt) && ti_is_const(ft)) { + uint64_t tv = ti_const_val(tt); + uint64_t fv = ti_const_val(ft); + TCGCond cond = op->args[5]; if (tv == 1 && fv == 0) { - op->opc = opc; + op->opc = INDEX_op_setcond; op->args[3] = cond; } else if (fv == 1 && tv == 0) { - op->opc = opc; + op->opc = INDEX_op_setcond; + op->args[3] = tcg_invert_cond(cond); + } else if (tv == -1 && fv == 0) { + op->opc = INDEX_op_negsetcond; + op->args[3] = cond; + } else if (fv == -1 && tv == 0) { + op->opc = INDEX_op_negsetcond; 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; + + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_mul(OptContext *ctx, TCGOp *op) @@ -1614,7 +2110,7 @@ static bool fold_mul(OptContext *ctx, TCGOp *op) fold_xi_to_x(ctx, op, 1)) { return true; } - return false; + return finish_folding(ctx, op); } static bool fold_mul_highpart(OptContext *ctx, TCGOp *op) @@ -1623,7 +2119,7 @@ static bool fold_mul_highpart(OptContext *ctx, TCGOp *op) fold_xi_to_i(ctx, op, 0)) { return true; } - return false; + return finish_folding(ctx, op); } static bool fold_multiply2(OptContext *ctx, TCGOp *op) @@ -1638,21 +2134,23 @@ static bool fold_multiply2(OptContext *ctx, TCGOp *op) TCGOp *op2; switch (op->opc) { - case INDEX_op_mulu2_i32: - l = (uint64_t)(uint32_t)a * (uint32_t)b; - h = (int32_t)(l >> 32); - l = (int32_t)l; - break; - case INDEX_op_muls2_i32: - l = (int64_t)(int32_t)a * (int32_t)b; - h = l >> 32; - l = (int32_t)l; - break; - case INDEX_op_mulu2_i64: - mulu64(&l, &h, a, b); + case INDEX_op_mulu2: + if (ctx->type == TCG_TYPE_I32) { + l = (uint64_t)(uint32_t)a * (uint32_t)b; + h = (int32_t)(l >> 32); + l = (int32_t)l; + } else { + mulu64(&l, &h, a, b); + } break; - case INDEX_op_muls2_i64: - muls64(&l, &h, a, b); + case INDEX_op_muls2: + if (ctx->type == TCG_TYPE_I32) { + l = (int64_t)(int32_t)a * (int32_t)b; + h = l >> 32; + l = (int32_t)l; + } else { + muls64(&l, &h, a, b); + } break; default: g_assert_not_reached(); @@ -1662,57 +2160,55 @@ 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, 2); + op2 = opt_insert_before(ctx, op, 0, 2); tcg_opt_gen_movi(ctx, op, rl, l); tcg_opt_gen_movi(ctx, op2, rh, h); return true; } - return false; + return finish_folding(ctx, op); } static bool fold_nand(OptContext *ctx, TCGOp *op) { + uint64_t s_mask; + if (fold_const2_commutative(ctx, op) || fold_xi_to_not(ctx, op, -1)) { return true; } - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return false; + s_mask = arg_info(op->args[1])->s_mask + & arg_info(op->args[2])->s_mask; + return fold_masks_s(ctx, op, s_mask); } -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; - ctx->z_mask = -(z_mask & -z_mask); + uint64_t z_mask = arg_info(op->args[1])->z_mask; + z_mask = -(z_mask & -z_mask); - /* - * Because of fold_sub_to_neg, we want to always return true, - * via finish_folding. - */ - finish_folding(ctx, op); - return true; + return fold_masks_z(ctx, op, z_mask); +} + +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) { + uint64_t s_mask; + if (fold_const2_commutative(ctx, op) || fold_xi_to_not(ctx, op, 0)) { return true; } - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return false; + s_mask = arg_info(op->args[1])->s_mask + & arg_info(op->args[2])->s_mask; + return fold_masks_s(ctx, op, s_mask); } static bool fold_not(OptContext *ctx, TCGOp *op) @@ -1720,31 +2216,32 @@ static bool fold_not(OptContext *ctx, TCGOp *op) if (fold_const1(ctx, op)) { return true; } - - ctx->s_mask = arg_info(op->args[1])->s_mask; - - /* Because of fold_to_not, we want to always return true, via finish. */ - finish_folding(ctx, op); - return true; + return fold_masks_s(ctx, op, arg_info(op->args[1])->s_mask); } static bool fold_or(OptContext *ctx, TCGOp *op) { + uint64_t z_mask, s_mask; + TempOptInfo *t1, *t2; + if (fold_const2_commutative(ctx, op) || fold_xi_to_x(ctx, op, 0) || fold_xx_to_x(ctx, op)) { return true; } - ctx->z_mask = arg_info(op->args[1])->z_mask - | arg_info(op->args[2])->z_mask; - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return fold_masks(ctx, op); + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + z_mask = t1->z_mask | t2->z_mask; + s_mask = t1->s_mask & t2->s_mask; + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_orc(OptContext *ctx, TCGOp *op) { + uint64_t s_mask; + TempOptInfo *t1, *t2; + if (fold_const2(ctx, op) || fold_xx_to_i(ctx, op, -1) || fold_xi_to_x(ctx, op, -1) || @@ -1752,36 +2249,65 @@ static bool fold_orc(OptContext *ctx, TCGOp *op) return true; } - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return false; + t2 = arg_info(op->args[2]); + if (ti_is_const(t2)) { + /* Fold orc r,x,i to or r,x,~i. */ + switch (ctx->type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + op->opc = INDEX_op_or; + break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + op->opc = INDEX_op_or_vec; + break; + default: + g_assert_not_reached(); + } + op->args[2] = arg_new_constant(ctx, ~ti_const_val(t2)); + return fold_or(ctx, op); + } + + t1 = arg_info(op->args[1]); + s_mask = t1->s_mask & t2->s_mask; + return fold_masks_s(ctx, op, s_mask); } -static bool fold_qemu_ld(OptContext *ctx, TCGOp *op) +static bool fold_qemu_ld_1reg(OptContext *ctx, TCGOp *op) { const TCGOpDef *def = &tcg_op_defs[op->opc]; MemOpIdx oi = op->args[def->nb_oargs + def->nb_iargs]; MemOp mop = get_memop(oi); int width = 8 * memop_size(mop); + uint64_t z_mask = -1, s_mask = 0; if (width < 64) { - ctx->s_mask = MAKE_64BIT_MASK(width, 64 - width); - if (!(mop & MO_SIGN)) { - ctx->z_mask = MAKE_64BIT_MASK(0, width); - ctx->s_mask <<= 1; + if (mop & MO_SIGN) { + s_mask = MAKE_64BIT_MASK(width - 1, 64 - (width - 1)); + } else { + z_mask = MAKE_64BIT_MASK(0, width); } } /* Opcodes that touch guest memory stop the mb optimization. */ ctx->prev_mb = NULL; - return false; + + return fold_masks_zs(ctx, op, z_mask, s_mask); +} + +static bool fold_qemu_ld_2reg(OptContext *ctx, TCGOp *op) +{ + /* Opcodes that touch guest memory stop the mb optimization. */ + ctx->prev_mb = NULL; + return finish_folding(ctx, op); } static bool fold_qemu_st(OptContext *ctx, TCGOp *op) { /* Opcodes that touch guest memory stop the mb optimization. */ ctx->prev_mb = NULL; - return false; + return true; } static bool fold_remainder(OptContext *ctx, TCGOp *op) @@ -1790,58 +2316,205 @@ static bool fold_remainder(OptContext *ctx, TCGOp *op) fold_xx_to_i(ctx, op, 0)) { return true; } - return false; + return finish_folding(ctx, op); } -static bool fold_setcond(OptContext *ctx, TCGOp *op) +/* Return 1 if finished, -1 if simplified, 0 if unchanged. */ +static int 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); - if (i >= 0) { - return tcg_opt_gen_movi(ctx, op, op->args[0], i); + 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; } - ctx->z_mask = 1; - ctx->s_mask = smask_from_zmask(1); - return false; + /* + * 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) { + if (!inv && !neg) { + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); + } + + if (!inv) { + op->opc = INDEX_op_neg; + } else if (neg) { + op->opc = INDEX_op_add; + op->args[2] = arg_new_constant(ctx, -1); + } else { + op->opc = INDEX_op_xor; + op->args[2] = arg_new_constant(ctx, 1); + } + return -1; + } + } + return 0; } -static bool fold_negsetcond(OptContext *ctx, TCGOp *op) +static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) { TCGCond cond = op->args[3]; - int i; + 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); + + ret = op->args[0]; + src1 = op->args[1]; + inv = cond == TCG_COND_TSTEQ; + + if (sh && neg && !inv && TCG_TARGET_sextract_valid(ctx->type, sh, 1)) { + op->opc = INDEX_op_sextract; + op->args[1] = src1; + op->args[2] = sh; + op->args[3] = 1; + return; + } else if (sh && TCG_TARGET_extract_valid(ctx->type, sh, 1)) { + op->opc = INDEX_op_extract; + op->args[1] = src1; + op->args[2] = sh; + op->args[3] = 1; + } else { + if (sh) { + op2 = opt_insert_before(ctx, op, INDEX_op_shr, 3); + op2->args[0] = ret; + op2->args[1] = src1; + op2->args[2] = arg_new_constant(ctx, sh); + src1 = ret; + } + op->opc = INDEX_op_and; + op->args[1] = src1; + op->args[2] = arg_new_constant(ctx, 1); + } + + if (neg && inv) { + op2 = opt_insert_after(ctx, op, INDEX_op_add, 3); + op2->args[0] = ret; + op2->args[1] = ret; + op2->args[2] = arg_new_constant(ctx, -1); + } else if (inv) { + op2 = opt_insert_after(ctx, op, INDEX_op_xor, 3); + op2->args[0] = ret; + op2->args[1] = ret; + op2->args[2] = arg_new_constant(ctx, 1); + } else if (neg) { + op2 = opt_insert_after(ctx, op, INDEX_op_neg, 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 (swap_commutative(op->args[0], &op->args[1], &op->args[2])) { - op->args[3] = cond = tcg_swap_cond(cond); + i = fold_setcond_zmask(ctx, op, false); + if (i > 0) { + return true; } + if (i == 0) { + fold_setcond_tst_pow2(ctx, op, false); + } + + return fold_masks_z(ctx, op, 1); +} - i = do_constant_folding_cond(ctx->type, op->args[1], op->args[2], cond); +static bool fold_negsetcond(OptContext *ctx, TCGOp *op) +{ + int i = do_constant_folding_cond1(ctx, op, op->args[0], &op->args[1], + &op->args[2], &op->args[3]); if (i >= 0) { return tcg_opt_gen_movi(ctx, op, op->args[0], -i); } + i = fold_setcond_zmask(ctx, op, true); + if (i > 0) { + return true; + } + if (i == 0) { + 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; + return fold_masks_s(ctx, op, -1); } - static bool fold_setcond2(OptContext *ctx, TCGOp *op) { - TCGCond cond = op->args[5]; + TCGCond cond; int i, inv = 0; - if (swap_commutative2(&op->args[1], &op->args[3])) { - op->args[5] = cond = tcg_swap_cond(cond); - } - - i = do_constant_folding_cond2(&op->args[1], &op->args[3], cond); + i = do_constant_folding_cond2(ctx, op, &op->args[1]); + cond = op->args[5]; if (i >= 0) { goto do_setcond_const; } @@ -1853,8 +2526,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; @@ -1882,27 +2555,38 @@ 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; + 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; + op->opc = INDEX_op_setcond; + return fold_setcond(ctx, op); } - ctx->z_mask = 1; - ctx->s_mask = smask_from_zmask(1); - return false; + return fold_masks_z(ctx, op, 1); do_setcond_const: return tcg_opt_gen_movi(ctx, op, op->args[0], i); @@ -1911,36 +2595,31 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) static bool fold_sextract(OptContext *ctx, TCGOp *op) { uint64_t z_mask, s_mask, s_mask_old; + TempOptInfo *t1 = arg_info(op->args[1]); int pos = op->args[2]; int len = op->args[3]; - if (arg_is_const(op->args[1])) { - uint64_t t; - - t = arg_info(op->args[1])->val; - t = sextract64(t, pos, len); - return tcg_opt_gen_movi(ctx, op, op->args[0], t); + if (ti_is_const(t1)) { + return tcg_opt_gen_movi(ctx, op, op->args[0], + sextract64(ti_const_val(t1), pos, len)); } - z_mask = arg_info(op->args[1])->z_mask; - z_mask = sextract64(z_mask, pos, len); - ctx->z_mask = z_mask; - - s_mask_old = arg_info(op->args[1])->s_mask; - s_mask = sextract64(s_mask_old, pos, len); - s_mask |= MAKE_64BIT_MASK(len, 64 - len); - ctx->s_mask = s_mask; + s_mask_old = t1->s_mask; + s_mask = s_mask_old >> pos; + s_mask |= -1ull << (len - 1); - if (pos == 0) { - ctx->a_mask = s_mask & ~s_mask_old; + if (pos == 0 && fold_affected_mask(ctx, op, s_mask & ~s_mask_old)) { + return true; } - return fold_masks(ctx, op); + z_mask = sextract64(t1->z_mask, pos, len); + return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_shift(OptContext *ctx, TCGOp *op) { - uint64_t s_mask, z_mask, sign; + uint64_t s_mask, z_mask; + TempOptInfo *t1, *t2; if (fold_const2(ctx, op) || fold_ix_to_i(ctx, op, 0) || @@ -1948,43 +2627,41 @@ static bool fold_shift(OptContext *ctx, TCGOp *op) return true; } - s_mask = arg_info(op->args[1])->s_mask; - z_mask = arg_info(op->args[1])->z_mask; - - if (arg_is_const(op->args[2])) { - int sh = arg_info(op->args[2])->val; + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + s_mask = t1->s_mask; + z_mask = t1->z_mask; - ctx->z_mask = do_constant_folding(op->opc, ctx->type, z_mask, sh); + if (ti_is_const(t2)) { + int sh = ti_const_val(t2); + z_mask = do_constant_folding(op->opc, ctx->type, z_mask, sh); s_mask = do_constant_folding(op->opc, ctx->type, s_mask, sh); - ctx->s_mask = smask_from_smask(s_mask); - return fold_masks(ctx, op); + return fold_masks_zs(ctx, op, z_mask, s_mask); } switch (op->opc) { - CASE_OP_32_64(sar): + case INDEX_op_sar: /* * Arithmetic right shift will not reduce the number of * input sign repetitions. */ - ctx->s_mask = s_mask; - break; - CASE_OP_32_64(shr): + return fold_masks_s(ctx, op, s_mask); + case INDEX_op_shr: /* * If the sign bit is known zero, then logical right shift - * will not reduced the number of input sign repetitions. + * will not reduce the number of input sign repetitions. */ - sign = (s_mask & -s_mask) >> 1; - if (!(z_mask & sign)) { - ctx->s_mask = s_mask; + if (~z_mask & -s_mask) { + return fold_masks_s(ctx, op, s_mask); } break; default: break; } - return false; + return finish_folding(ctx, op); } static bool fold_sub_to_neg(OptContext *ctx, TCGOp *op) @@ -1998,12 +2675,9 @@ 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; - break; case TCG_TYPE_I64: - neg_op = INDEX_op_neg_i64; - have_neg = TCG_TARGET_HAS_neg_i64; + neg_op = INDEX_op_neg; + have_neg = true; break; case TCG_TYPE_V64: case TCG_TYPE_V128: @@ -2018,7 +2692,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; } @@ -2031,52 +2705,288 @@ static bool fold_sub_vec(OptContext *ctx, TCGOp *op) fold_sub_to_neg(ctx, op)) { return true; } - return false; + return finish_folding(ctx, 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_xx_to_i(ctx, op, 0) || + fold_xi_to_x(ctx, op, 0) || + fold_sub_to_neg(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 = INDEX_op_add; + op->args[2] = arg_new_constant(ctx, -val); + } + return finish_folding(ctx, op); +} + +static void squash_prev_borrowout(OptContext *ctx, TCGOp *op) +{ + TempOptInfo *t2; + + op = QTAILQ_PREV(op, link); + switch (op->opc) { + case INDEX_op_subbo: + op->opc = INDEX_op_sub; + fold_sub(ctx, op); + break; + case INDEX_op_subbio: + op->opc = INDEX_op_subbi; + break; + case INDEX_op_subb1o: + t2 = arg_info(op->args[2]); + if (ti_is_const(t2)) { + op->opc = INDEX_op_add; + op->args[2] = arg_new_constant(ctx, -(ti_const_val(t2) + 1)); + /* Perform other constant folding, if needed. */ + fold_add(ctx, op); + } else { + TCGArg ret = op->args[0]; + op->opc = INDEX_op_sub; + op = opt_insert_after(ctx, op, INDEX_op_add, 3); + op->args[0] = ret; + op->args[1] = ret; + op->args[2] = arg_new_constant(ctx, -1); + } + break; + default: + g_assert_not_reached(); + } +} + +static bool fold_subbi(OptContext *ctx, TCGOp *op) +{ + TempOptInfo *t2; + int borrow_in = ctx->carry_state; + + if (borrow_in < 0) { + return finish_folding(ctx, op); + } + ctx->carry_state = -1; + + squash_prev_borrowout(ctx, op); + if (borrow_in == 0) { + op->opc = INDEX_op_sub; + return fold_sub(ctx, op); + } + + /* + * Propagate the known carry-in into any constant, then negate to + * transform from sub to add. If there is no constant, emit a + * separate add -1. + */ + t2 = arg_info(op->args[2]); + if (ti_is_const(t2)) { + op->args[2] = arg_new_constant(ctx, -(ti_const_val(t2) + 1)); + } else { + TCGOp *op2 = opt_insert_before(ctx, op, INDEX_op_sub, 3); + + op2->args[0] = op->args[0]; + op2->args[1] = op->args[1]; + op2->args[2] = op->args[2]; + fold_sub(ctx, op2); + + op->args[1] = op->args[0]; + op->args[2] = arg_new_constant(ctx, -1); + } + op->opc = INDEX_op_add; + return fold_add(ctx, op); +} + +static bool fold_subbio(OptContext *ctx, TCGOp *op) +{ + TempOptInfo *t1, *t2; + int borrow_out = -1; + + if (ctx->carry_state < 0) { + return finish_folding(ctx, op); + } + + squash_prev_borrowout(ctx, op); + if (ctx->carry_state == 0) { + goto do_subbo; + } + + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + + /* Propagate the known borrow-in into a constant, if possible. */ + if (ti_is_const(t2)) { + uint64_t max = ctx->type == TCG_TYPE_I32 ? UINT32_MAX : UINT64_MAX; + uint64_t v = ti_const_val(t2) & max; + + if (v < max) { + op->args[2] = arg_new_constant(ctx, v + 1); + goto do_subbo; + } + /* subtracting max + 1 produces known borrow out. */ + borrow_out = 1; + } + if (ti_is_const(t1)) { + uint64_t v = ti_const_val(t1); + if (v != 0) { + op->args[2] = arg_new_constant(ctx, v - 1); + goto do_subbo; + } + } + + /* Adjust the opcode to remember the known carry-in. */ + op->opc = INDEX_op_subb1o; + ctx->carry_state = borrow_out; + return finish_folding(ctx, op); + + do_subbo: + op->opc = INDEX_op_subbo; + return fold_subbo(ctx, op); } -static bool fold_sub2(OptContext *ctx, TCGOp *op) +static bool fold_subbo(OptContext *ctx, TCGOp *op) { - return fold_addsub2(ctx, op, false); + TempOptInfo *t1 = arg_info(op->args[1]); + TempOptInfo *t2 = arg_info(op->args[2]); + int borrow_out = -1; + + if (ti_is_const(t2)) { + uint64_t v2 = ti_const_val(t2); + if (v2 == 0) { + borrow_out = 0; + } else if (ti_is_const(t1)) { + uint64_t v1 = ti_const_val(t1); + borrow_out = v1 < v2; + } + } + ctx->carry_state = borrow_out; + return finish_folding(ctx, op); } static bool fold_tcg_ld(OptContext *ctx, TCGOp *op) { + uint64_t z_mask = -1, s_mask = 0; + /* We can't do any folding with a load, but we can record bits. */ switch (op->opc) { - CASE_OP_32_64(ld8s): - ctx->s_mask = MAKE_64BIT_MASK(8, 56); + case INDEX_op_ld8s: + s_mask = INT8_MIN; break; - CASE_OP_32_64(ld8u): - ctx->z_mask = MAKE_64BIT_MASK(0, 8); - ctx->s_mask = MAKE_64BIT_MASK(9, 55); + case INDEX_op_ld8u: + z_mask = MAKE_64BIT_MASK(0, 8); break; - CASE_OP_32_64(ld16s): - ctx->s_mask = MAKE_64BIT_MASK(16, 48); + case INDEX_op_ld16s: + s_mask = INT16_MIN; break; - CASE_OP_32_64(ld16u): - ctx->z_mask = MAKE_64BIT_MASK(0, 16); - ctx->s_mask = MAKE_64BIT_MASK(17, 47); + case INDEX_op_ld16u: + z_mask = MAKE_64BIT_MASK(0, 16); break; - case INDEX_op_ld32s_i64: - ctx->s_mask = MAKE_64BIT_MASK(32, 32); + case INDEX_op_ld32s: + s_mask = INT32_MIN; break; - case INDEX_op_ld32u_i64: - ctx->z_mask = MAKE_64BIT_MASK(0, 32); - ctx->s_mask = MAKE_64BIT_MASK(33, 31); + case INDEX_op_ld32u: + z_mask = MAKE_64BIT_MASK(0, 32); break; default: g_assert_not_reached(); } - return false; + return fold_masks_zs(ctx, op, z_mask, s_mask); +} + +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 finish_folding(ctx, op); + } + + 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 true; + } + + switch (op->opc) { + case INDEX_op_st8: + lm1 = 0; + break; + case INDEX_op_st16: + lm1 = 1; + break; + case INDEX_op_st32: + lm1 = 3; + break; + case INDEX_op_st: + 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 true; +} + +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)) { + return fold_tcg_st(ctx, op); + } + + 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 true; } static bool fold_xor(OptContext *ctx, TCGOp *op) { + uint64_t z_mask, s_mask; + TempOptInfo *t1, *t2; + if (fold_const2_commutative(ctx, op) || fold_xx_to_i(ctx, op, 0) || fold_xi_to_x(ctx, op, 0) || @@ -2084,11 +2994,11 @@ static bool fold_xor(OptContext *ctx, TCGOp *op) return true; } - ctx->z_mask = arg_info(op->args[1])->z_mask - | arg_info(op->args[2])->z_mask; - ctx->s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return fold_masks(ctx, op); + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + z_mask = t1->z_mask | t2->z_mask; + s_mask = t1->s_mask & t2->s_mask; + return fold_masks_zs(ctx, op, z_mask, s_mask); } /* Propagate constants and copies, fold constant expressions. */ @@ -2098,6 +3008,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 @@ -2124,62 +3036,59 @@ void tcg_optimize(TCGContext *s) copy_propagate(&ctx, op, def->nb_oargs, def->nb_iargs); /* Pre-compute the type of the operation. */ - if (def->flags & TCG_OPF_VECTOR) { - ctx.type = TCG_TYPE_V64 + TCGOP_VECL(op); - } else if (def->flags & TCG_OPF_64BIT) { - ctx.type = TCG_TYPE_I64; - } else { - ctx.type = TCG_TYPE_I32; - } - - /* Assume all bits affected, no bits known zero, no sign reps. */ - ctx.a_mask = -1; - ctx.z_mask = -1; - ctx.s_mask = 0; + ctx.type = TCGOP_TYPE(op); /* * Process each opcode. * Sorted alphabetically by opcode as much as possible. */ switch (opc) { - CASE_OP_32_64(add): + case INDEX_op_add: done = fold_add(&ctx, op); break; case INDEX_op_add_vec: done = fold_add_vec(&ctx, op); break; - CASE_OP_32_64(add2): - done = fold_add2(&ctx, op); + case INDEX_op_addci: + done = fold_addci(&ctx, op); + break; + case INDEX_op_addcio: + done = fold_addcio(&ctx, op); break; - CASE_OP_32_64_VEC(and): + case INDEX_op_addco: + done = fold_addco(&ctx, op); + break; + case INDEX_op_and: + case INDEX_op_and_vec: done = fold_and(&ctx, op); break; - CASE_OP_32_64_VEC(andc): + case INDEX_op_andc: + case INDEX_op_andc_vec: done = fold_andc(&ctx, op); break; - CASE_OP_32_64(brcond): + case INDEX_op_brcond: done = fold_brcond(&ctx, op); break; case INDEX_op_brcond2_i32: done = fold_brcond2(&ctx, op); break; - CASE_OP_32_64(bswap16): - CASE_OP_32_64(bswap32): - case INDEX_op_bswap64_i64: + case INDEX_op_bswap16: + case INDEX_op_bswap32: + case INDEX_op_bswap64: done = fold_bswap(&ctx, op); break; - CASE_OP_32_64(clz): - CASE_OP_32_64(ctz): + case INDEX_op_clz: + case INDEX_op_ctz: done = fold_count_zeros(&ctx, op); break; - CASE_OP_32_64(ctpop): + case INDEX_op_ctpop: done = fold_ctpop(&ctx, op); break; - CASE_OP_32_64(deposit): + case INDEX_op_deposit: done = fold_deposit(&ctx, op); break; - CASE_OP_32_64(div): - CASE_OP_32_64(divu): + case INDEX_op_divs: + case INDEX_op_divu: done = fold_divide(&ctx, op); break; case INDEX_op_dup_vec: @@ -2188,134 +3097,162 @@ void tcg_optimize(TCGContext *s) case INDEX_op_dup2_vec: done = fold_dup2(&ctx, op); break; - CASE_OP_32_64_VEC(eqv): + case INDEX_op_eqv: + case INDEX_op_eqv_vec: done = fold_eqv(&ctx, op); break; - CASE_OP_32_64(extract): + case INDEX_op_extract: done = fold_extract(&ctx, op); break; - CASE_OP_32_64(extract2): + case INDEX_op_extract2: done = fold_extract2(&ctx, op); break; - CASE_OP_32_64(ext8s): - CASE_OP_32_64(ext16s): - case INDEX_op_ext32s_i64: case INDEX_op_ext_i32_i64: done = fold_exts(&ctx, op); break; - CASE_OP_32_64(ext8u): - CASE_OP_32_64(ext16u): - case INDEX_op_ext32u_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: done = fold_extu(&ctx, op); break; - CASE_OP_32_64(ld8s): - CASE_OP_32_64(ld8u): - CASE_OP_32_64(ld16s): - CASE_OP_32_64(ld16u): - case INDEX_op_ld32s_i64: - case INDEX_op_ld32u_i64: + case INDEX_op_ld8s: + case INDEX_op_ld8u: + case INDEX_op_ld16s: + case INDEX_op_ld16u: + case INDEX_op_ld32s: + case INDEX_op_ld32u: done = fold_tcg_ld(&ctx, op); break; + case INDEX_op_ld: + case INDEX_op_ld_vec: + done = fold_tcg_ld_memcopy(&ctx, op); + break; + case INDEX_op_st8: + case INDEX_op_st16: + case INDEX_op_st32: + done = fold_tcg_st(&ctx, op); + break; + case INDEX_op_st: + case INDEX_op_st_vec: + done = fold_tcg_st_memcopy(&ctx, op); + break; case INDEX_op_mb: done = fold_mb(&ctx, op); break; - CASE_OP_32_64_VEC(mov): + case INDEX_op_mov: + case INDEX_op_mov_vec: done = fold_mov(&ctx, op); break; - CASE_OP_32_64(movcond): + case INDEX_op_movcond: done = fold_movcond(&ctx, op); break; - CASE_OP_32_64(mul): + case INDEX_op_mul: done = fold_mul(&ctx, op); break; - CASE_OP_32_64(mulsh): - CASE_OP_32_64(muluh): + case INDEX_op_mulsh: + case INDEX_op_muluh: done = fold_mul_highpart(&ctx, op); break; - CASE_OP_32_64(muls2): - CASE_OP_32_64(mulu2): + case INDEX_op_muls2: + case INDEX_op_mulu2: done = fold_multiply2(&ctx, op); break; - CASE_OP_32_64_VEC(nand): + case INDEX_op_nand: + case INDEX_op_nand_vec: done = fold_nand(&ctx, op); break; - CASE_OP_32_64(neg): + case INDEX_op_neg: done = fold_neg(&ctx, op); break; - CASE_OP_32_64_VEC(nor): + case INDEX_op_nor: + case INDEX_op_nor_vec: done = fold_nor(&ctx, op); break; - CASE_OP_32_64_VEC(not): + case INDEX_op_not: + case INDEX_op_not_vec: done = fold_not(&ctx, op); break; - CASE_OP_32_64_VEC(or): + case INDEX_op_or: + case INDEX_op_or_vec: done = fold_or(&ctx, op); break; - CASE_OP_32_64_VEC(orc): + case INDEX_op_orc: + case INDEX_op_orc_vec: done = fold_orc(&ctx, op); break; - 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_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: + case INDEX_op_qemu_ld: + done = fold_qemu_ld_1reg(&ctx, op); + break; + case INDEX_op_qemu_ld2: + done = fold_qemu_ld_2reg(&ctx, op); + break; + case INDEX_op_qemu_st: + case INDEX_op_qemu_st2: done = fold_qemu_st(&ctx, op); break; - CASE_OP_32_64(rem): - CASE_OP_32_64(remu): + case INDEX_op_rems: + case INDEX_op_remu: done = fold_remainder(&ctx, op); break; - CASE_OP_32_64(rotl): - CASE_OP_32_64(rotr): - CASE_OP_32_64(sar): - CASE_OP_32_64(shl): - CASE_OP_32_64(shr): + case INDEX_op_rotl: + case INDEX_op_rotr: + case INDEX_op_sar: + case INDEX_op_shl: + case INDEX_op_shr: done = fold_shift(&ctx, op); break; - CASE_OP_32_64(setcond): + case INDEX_op_setcond: done = fold_setcond(&ctx, op); break; - CASE_OP_32_64(negsetcond): + case INDEX_op_negsetcond: done = fold_negsetcond(&ctx, op); break; case INDEX_op_setcond2_i32: done = fold_setcond2(&ctx, op); break; - CASE_OP_32_64(sextract): + 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 INDEX_op_sextract: done = fold_sextract(&ctx, op); break; - CASE_OP_32_64(sub): + case INDEX_op_sub: done = fold_sub(&ctx, op); break; + case INDEX_op_subbi: + done = fold_subbi(&ctx, op); + break; + case INDEX_op_subbio: + done = fold_subbio(&ctx, op); + break; + case INDEX_op_subbo: + done = fold_subbo(&ctx, op); + break; case INDEX_op_sub_vec: done = fold_sub_vec(&ctx, op); break; - CASE_OP_32_64(sub2): - done = fold_sub2(&ctx, op); - break; - CASE_OP_32_64_VEC(xor): + case INDEX_op_xor: + case INDEX_op_xor_vec: done = fold_xor(&ctx, op); break; + case INDEX_op_set_label: + case INDEX_op_br: + case INDEX_op_exit_tb: + case INDEX_op_goto_tb: + case INDEX_op_goto_ptr: + finish_ebb(&ctx); + done = true; + break; default: + done = finish_folding(&ctx, op); break; } - - if (!done) { - finish_folding(&ctx, op); - } + tcg_debug_assert(done); } } diff --git a/accel/tcg/perf.c b/tcg/perf.c similarity index 96% rename from accel/tcg/perf.c rename to tcg/perf.c index cd1aa99a7ee..8fa5fa99917 100644 --- a/accel/tcg/perf.c +++ b/tcg/perf.c @@ -10,13 +10,13 @@ #include "qemu/osdep.h" #include "elf.h" -#include "exec/exec-all.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" -#include "debuginfo.h" -#include "perf.h" - static FILE *safe_fopen_w(const char *path) { int saved_errno; @@ -313,7 +313,7 @@ void perf_report_code(uint64_t guest_pc, TranslationBlock *tb, const void *start) { struct debuginfo_query *q; - size_t insn, start_words; + size_t insn; uint64_t *gen_insn_data; if (!perfmap && !jitdump) { @@ -329,17 +329,12 @@ void perf_report_code(uint64_t guest_pc, TranslationBlock *tb, /* 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]; + q[insn].address = gen_insn_data[insn * INSN_START_WORDS + 0]; if (tb_cflags(tb) & CF_PCREL) { - q[insn].address |= (guest_pc & TARGET_PAGE_MASK); - } else { -#if defined(TARGET_I386) - q[insn].address -= tb->cs_base; -#endif + q[insn].address |= guest_pc & TARGET_PAGE_MASK; } q[insn].flags = DEBUGINFO_SYMBOL | (jitdump ? DEBUGINFO_LINE : 0); } diff --git a/tcg/ppc/tcg-target-con-set.h b/tcg/ppc/tcg-target-con-set.h index bbd7b212474..da7a383bff5 100644 --- a/tcg/ppc/tcg-target-con-set.h +++ b/tcg/ppc/tcg-target-con-set.h @@ -11,31 +11,33 @@ */ C_O0_I1(r) C_O0_I2(r, r) -C_O0_I2(r, ri) +C_O0_I2(r, rC) C_O0_I2(v, r) C_O0_I3(r, r, r) C_O0_I3(o, m, r) -C_O0_I4(r, r, ri, ri) +C_O0_I4(r, r, rU, rC) 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, rI, ri) -C_O1_I2(r, rI, rT) +C_O1_I2(r, rI, r) 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, rZM) C_O1_I2(r, r, rZW) +C_O1_I2(r, rI, rN) +C_O1_I2(r, rZM, rZM) C_O1_I2(v, v, v) C_O1_I3(v, v, v, v) -C_O1_I4(r, r, ri, rZ, rZ) -C_O1_I4(r, r, r, ri, ri) +C_O1_I4(v, v, v, vZM, v) +C_O1_I4(r, r, rC, rZ, rZ) +C_O1_I4(r, r, r, rU, rC) C_O2_I1(r, r, r) -C_O2_I1(o, m, 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 20846901de9..faf92da47f4 100644 --- a/tcg/ppc/tcg-target-con-str.h +++ b/tcg/ppc/tcg-target-con-str.h @@ -16,8 +16,10 @@ REGS('v', ALL_VECTOR_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('M', TCG_CT_CONST_MONE) +CONST('N', TCG_CT_CONST_N16) CONST('T', TCG_CT_CONST_S32) CONST('U', TCG_CT_CONST_U32) CONST('W', TCG_CT_CONST_WSZ) diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h new file mode 100644 index 00000000000..81ec5aece7a --- /dev/null +++ b/tcg/ppc/tcg-target-has.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +#include "host/cpuinfo.h" + +#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 */ +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_extr_i64_i32 0 +#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 + * code quite large. + */ +#define TCG_TARGET_HAS_v64 have_vsx +#define TCG_TARGET_HAS_v128 have_altivec +#define TCG_TARGET_HAS_v256 0 + +#define TCG_TARGET_HAS_andc_vec 1 +#define TCG_TARGET_HAS_orc_vec have_isa_2_07 +#define TCG_TARGET_HAS_nand_vec have_isa_2_07 +#define TCG_TARGET_HAS_nor_vec 1 +#define TCG_TARGET_HAS_eqv_vec have_isa_2_07 +#define TCG_TARGET_HAS_not_vec 1 +#define TCG_TARGET_HAS_neg_vec have_isa_3_00 +#define TCG_TARGET_HAS_abs_vec 0 +#define TCG_TARGET_HAS_roti_vec 0 +#define TCG_TARGET_HAS_rots_vec 0 +#define TCG_TARGET_HAS_rotv_vec 1 +#define TCG_TARGET_HAS_shi_vec 0 +#define TCG_TARGET_HAS_shs_vec 0 +#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 have_vsx +#define TCG_TARGET_HAS_cmpsel_vec 1 +#define TCG_TARGET_HAS_tst_vec 0 + +#define TCG_TARGET_extract_valid(type, ofs, len) 1 +#define TCG_TARGET_deposit_valid(type, ofs, len) 1 + +static inline bool +tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (type == TCG_TYPE_I64 && ofs + len == 32) { + return true; + } + return ofs == 0 && (len == 8 || len == 16); +} +#define TCG_TARGET_sextract_valid tcg_target_sextract_valid + +#endif diff --git a/tcg/ppc/tcg-target-mo.h b/tcg/ppc/tcg-target-mo.h new file mode 100644 index 00000000000..98bfe03b7ac --- /dev/null +++ b/tcg/ppc/tcg-target-mo.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/ppc/tcg-target-opc.h.inc b/tcg/ppc/tcg-target-opc.h.inc new file mode 100644 index 00000000000..c3635831b57 --- /dev/null +++ b/tcg/ppc/tcg-target-opc.h.inc @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 Linaro Limited + * + * 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. + * + * 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. + */ + +DEF(ppc_mrgh_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(ppc_mrgl_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(ppc_msum_vec, 1, 3, 0, TCG_OPF_VECTOR) +DEF(ppc_muleu_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(ppc_mulou_vec, 1, 2, 0, TCG_OPF_VECTOR) +DEF(ppc_pkum_vec, 1, 2, 0, TCG_OPF_VECTOR) diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 090f11e71cd..b8b23d44d5e 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -23,8 +23,6 @@ */ #include "elf.h" -#include "../tcg-pool.c.inc" -#include "../tcg-ldst.c.inc" /* * Standardize on the _CALL_FOO symbols used by GCC: @@ -83,7 +81,7 @@ #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 *)) @@ -91,21 +89,26 @@ /* Shorthand for size of a register. */ #define SZR (TCG_TARGET_REG_BITS / 8) -#define TCG_CT_CONST_S16 0x100 -#define TCG_CT_CONST_S32 0x400 -#define TCG_CT_CONST_U32 0x800 -#define TCG_CT_CONST_ZERO 0x1000 -#define TCG_CT_CONST_MONE 0x2000 -#define TCG_CT_CONST_WSZ 0x4000 +#define TCG_CT_CONST_S16 0x00100 +#define TCG_CT_CONST_U16 0x00200 +#define TCG_CT_CONST_N16 0x00400 +#define TCG_CT_CONST_S32 0x00800 +#define TCG_CT_CONST_U32 0x01000 +#define TCG_CT_CONST_ZERO 0x02000 +#define TCG_CT_CONST_MONE 0x04000 +#define TCG_CT_CONST_WSZ 0x08000 +#define TCG_CT_CONST_CMP 0x10000 #define ALL_GENERAL_REGS 0xffffffffu #define ALL_VECTOR_REGS 0xffffffff00000000ull +#ifndef R_PPC64_PCREL34 +#define R_PPC64_PCREL34 132 +#endif + #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] = { @@ -215,13 +218,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)); @@ -241,7 +250,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); @@ -260,31 +269,96 @@ static bool reloc_pc14(tcg_insn_unit *src_rw, const tcg_insn_unit *target) return false; } +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 val, TCGType type, int ct) +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_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) && val == (int16_t)val) { + if ((ct & TCG_CT_CONST_S16) && sval == (int16_t)sval) { + return 1; + } + if ((ct & TCG_CT_CONST_U16) && uval == (uint16_t)uval) { + return 1; + } + if ((ct & TCG_CT_CONST_N16) && -sval == (int16_t)-sval) { 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; @@ -323,6 +397,15 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #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) #define ADDIS OPCD( 15) @@ -356,6 +439,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) @@ -638,31 +722,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, @@ -680,6 +768,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, @@ -712,6 +802,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); @@ -759,19 +895,33 @@ 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 void tcg_out_rld(TCGContext *s, int op, TCGReg ra, TCGReg rs, + int sh, int mb) +{ + tcg_out_rld_rc(s, op, ra, rs, sh, mb, false); +} + +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 inline void tcg_out_rlw(TCGContext *s, int op, TCGReg ra, TCGReg rs, - int sh, int mb, int me) +static void tcg_out_rlw(TCGContext *s, int op, TCGReg ra, TCGReg rs, + int sh, int mb, int me) { - tcg_out32(s, op | RA(ra) | RS(rs) | SH(sh) | MB(mb) | ME(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) @@ -853,109 +1003,17 @@ 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_bswap16(TCGContext *s, TCGReg dst, TCGReg src, int flags) -{ - TCGReg tmp = dst == src ? TCG_REG_R0 : dst; - - if (have_isa_3_10) { - tcg_out32(s, BRH | RA(dst) | RS(src)); - if (flags & TCG_BSWAP_OS) { - 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); - } - return; - } - - /* - * In the following, - * dep(a, b, m) -> (a & ~m) | (b & m) - * - * Begin with: src = xxxxabcd - */ - /* tmp = rol32(src, 24) & 0x000000ff = 0000000c */ - tcg_out_rlw(s, RLWINM, tmp, src, 24, 24, 31); - /* tmp = dep(tmp, rol32(src, 8), 0x0000ff00) = 000000dc */ - tcg_out_rlw(s, RLWIMI, tmp, src, 8, 16, 23); - - if (flags & TCG_BSWAP_OS) { - tcg_out_ext16s(s, TCG_TYPE_REG, dst, tmp); - } else { - tcg_out_mov(s, TCG_TYPE_REG, dst, tmp); - } -} - -static void tcg_out_bswap32(TCGContext *s, TCGReg dst, TCGReg src, int flags) -{ - TCGReg tmp = dst == src ? TCG_REG_R0 : dst; - - if (have_isa_3_10) { - tcg_out32(s, BRW | RA(dst) | RS(src)); - if (flags & TCG_BSWAP_OS) { - tcg_out_ext32s(s, dst, dst); - } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - tcg_out_ext32u(s, dst, dst); - } - return; - } - - /* - * Stolen from gcc's builtin_bswap32. - * In the following, - * dep(a, b, m) -> (a & ~m) | (b & m) - * - * Begin with: src = xxxxabcd - */ - /* tmp = rol32(src, 8) & 0xffffffff = 0000bcda */ - tcg_out_rlw(s, RLWINM, tmp, src, 8, 0, 31); - /* tmp = dep(tmp, rol32(src, 24), 0xff000000) = 0000dcda */ - tcg_out_rlw(s, RLWIMI, tmp, src, 24, 0, 7); - /* tmp = dep(tmp, rol32(src, 24), 0x0000ff00) = 0000dcba */ - tcg_out_rlw(s, RLWIMI, tmp, src, 24, 16, 23); - - if (flags & TCG_BSWAP_OS) { - tcg_out_ext32s(s, dst, tmp); - } else { - tcg_out_mov(s, TCG_TYPE_REG, dst, tmp); - } -} - -static void tcg_out_bswap64(TCGContext *s, TCGReg dst, TCGReg src) +static void tcg_out_addpcis(TCGContext *s, TCGReg dst, intptr_t imm) { - TCGReg t0 = dst == src ? TCG_REG_R0 : dst; - TCGReg t1 = dst == src ? dst : TCG_REG_R0; - - if (have_isa_3_10) { - tcg_out32(s, BRD | RA(dst) | RS(src)); - return; - } - - /* - * In the following, - * dep(a, b, m) -> (a & ~m) | (b & m) - * - * Begin with: src = abcdefgh - */ - /* t0 = rol32(src, 8) & 0xffffffff = 0000fghe */ - tcg_out_rlw(s, RLWINM, t0, src, 8, 0, 31); - /* t0 = dep(t0, rol32(src, 24), 0xff000000) = 0000hghe */ - tcg_out_rlw(s, RLWIMI, t0, src, 24, 0, 7); - /* t0 = dep(t0, rol32(src, 24), 0x0000ff00) = 0000hgfe */ - tcg_out_rlw(s, RLWIMI, t0, src, 24, 16, 23); - - /* t0 = rol64(t0, 32) = hgfe0000 */ - tcg_out_rld(s, RLDICL, t0, t0, 32, 0); - /* t1 = rol64(src, 32) = efghabcd */ - tcg_out_rld(s, RLDICL, t1, src, 32, 0); + uint32_t d0, d1, d2; - /* t0 = dep(t0, rol32(t1, 24), 0xffffffff) = hgfebcda */ - tcg_out_rlw(s, RLWIMI, t0, t1, 8, 0, 31); - /* t0 = dep(t0, rol32(t1, 24), 0xff000000) = hgfedcda */ - tcg_out_rlw(s, RLWIMI, t0, t1, 24, 0, 7); - /* t0 = dep(t0, rol32(t1, 24), 0x0000ff00) = hgfedcba */ - tcg_out_rlw(s, RLWIMI, t0, t1, 24, 16, 23); + tcg_debug_assert((imm & 0xffff) == 0); + tcg_debug_assert(imm == (int32_t)imm); - tcg_out_mov(s, TCG_TYPE_REG, dst, t0); + 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); } /* Emit a move into ret of arg, if it can be done in one insn. */ @@ -991,12 +1049,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) { @@ -1035,6 +1112,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); @@ -1044,10 +1134,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); @@ -1104,7 +1205,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; @@ -1131,6 +1245,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)); @@ -1322,6 +1438,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) { @@ -1475,15 +1634,58 @@ 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) + bool const_arg2, int cr, TCGType type) { - int imm; uint32_t op; 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; } @@ -1492,33 +1694,33 @@ static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, case TCG_COND_EQ: case TCG_COND_NE: if (const_arg2) { - if ((int16_t) arg2 == arg2) { + if ((int16_t)arg2 == arg2) { op = CMPI; - imm = 1; - break; - } else if ((uint16_t) arg2 == arg2) { - op = CMPLI; - imm = 1; break; } + tcg_debug_assert((uint16_t)arg2 == arg2); + op = CMPLI; + break; } op = CMPL; - 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: case TCG_COND_GT: if (const_arg2) { - if ((int16_t) arg2 == arg2) { - op = CMPI; - imm = 1; - break; - } + tcg_debug_assert((int16_t)arg2 == arg2); + op = CMPI; + break; } op = CMP; - imm = 0; break; case TCG_COND_LTU: @@ -1526,30 +1728,20 @@ static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, case TCG_COND_LEU: case TCG_COND_GTU: if (const_arg2) { - if ((uint16_t) arg2 == arg2) { - op = CMPLI; - imm = 1; - break; - } + tcg_debug_assert((uint16_t)arg2 == arg2); + op = CMPLI; + break; } op = CMPL; - imm = 0; break; default: g_assert_not_reached(); } op |= BF(cr) | ((type == TCG_TYPE_I64) << 21); - - if (imm) { - tcg_out32(s, op | RA(arg1) | (arg2 & 0xffff)); - } else { - if (const_arg2) { - tcg_out_movi(s, type, TCG_REG_R0, arg2); - arg2 = TCG_REG_R0; - } - tcg_out32(s, op | RA(arg1) | RB(arg2)); - } + op |= RA(arg1); + op |= const_arg2 ? arg2 & 0xffff : RB(arg2); + tcg_out32(s, op); } static void tcg_out_setcond_eq0(TCGContext *s, TCGType type, @@ -1616,8 +1808,8 @@ 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, bool neg) + TCGReg arg0, TCGReg arg1, TCGArg arg2, + bool const_arg2, bool neg) { int sh; bool inv; @@ -1633,7 +1825,7 @@ static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, if (have_isa_3_10) { tcg_insn_unit bi, opc; - tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); + 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); @@ -1686,7 +1878,7 @@ 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]; @@ -1716,8 +1908,18 @@ static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, tcg_out_setcond_ne0(s, type, arg0, arg1, neg); break; - case TCG_COND_LE: - case TCG_COND_LEU: + 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: @@ -1752,27 +1954,93 @@ static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, } } -static void tcg_out_bc(TCGContext *s, int bc, TCGLabel *l) +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, false, false); +} + +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, true, false); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rC), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, false, true); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, true, true); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rC), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; + +void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + uint32_t insn = B; + + if (l->has_value) { + insn |= reloc_pc24_val(tcg_splitwx_to_rx(s->code_ptr), l->u.value_ptr); + } else { + tcg_out_reloc(s, s->code_ptr, R_PPC_REL24, l, 0); + } + tcg_out32(s, insn); +} + +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 tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, TCGReg arg2, TCGLabel *l) +{ + tcg_out_cmp(s, cond, arg1, arg2, false, 0, type); + tcg_out_bc_lab(s, cond, l); } -static void tcg_out_brcond(TCGContext *s, TCGCond cond, - TCGArg arg1, TCGArg arg2, int const_arg2, - TCGLabel *l, TCGType type) +static void tgen_brcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, tcg_target_long arg2, TCGLabel *l) { - 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, true, 0, type); + tcg_out_bc_lab(s, cond, l); } -static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, - TCGArg dest, TCGArg c1, TCGArg c2, TCGArg v1, - TCGArg v2, bool const_c2) +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rC), + .out_rr = tgen_brcond, + .out_ri = tgen_brcondi, +}; + +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg v1, bool const_v1, TCGArg v2, bool const_v2) { /* If for some reason both inputs are zero, don't produce bad code. */ if (v1 == 0 && v2 == 0) { @@ -1780,7 +2048,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]; @@ -1809,7 +2077,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 { @@ -1818,23 +2086,28 @@ static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, } } +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, rC, rZ, rZ), + .out = tgen_movcond, +}; + static void tcg_out_cntxz(TCGContext *s, TCGType type, uint32_t opc, TCGArg a0, TCGArg a1, TCGArg a2, bool const_a2) { 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 { @@ -1844,8 +2117,8 @@ static void tcg_out_cntxz(TCGContext *s, TCGType type, uint32_t opc, } } -static void tcg_out_cmp2(TCGContext *s, const TCGArg *args, - const int *const_args) +static void tcg_out_cmp2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGArg bl, bool blconst, TCGArg bh, bool bhconst) { static const struct { uint8_t bit1, bit2; } bits[] = { [TCG_COND_LT ] = { CR_LT, CR_LT }, @@ -1858,18 +2131,9 @@ static void tcg_out_cmp2(TCGContext *s, const TCGArg *args, [TCG_COND_GEU] = { CR_GT, CR_LT }, }; - TCGCond cond = args[4], cond2; - TCGArg al, ah, bl, bh; - int blconst, bhconst; + TCGCond cond2; int op, bit1, bit2; - al = args[0]; - ah = args[1]; - bl = args[2]; - bh = args[3]; - blconst = const_args[2]; - bhconst = const_args[3]; - switch (cond) { case TCG_COND_EQ: op = CRAND; @@ -1879,7 +2143,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: @@ -1897,8 +2176,8 @@ 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: @@ -1906,22 +2185,42 @@ static void tcg_out_cmp2(TCGContext *s, const TCGArg *args, } } -static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, - const int *const_args) +static void tgen_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh) { - 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_out_cmp2(s, cond, al, ah, bl, const_bl, bh, const_bh); + tcg_out32(s, MFOCRF | RT(TCG_REG_R0) | FXM(0)); + tcg_out_rlw(s, RLWINM, ret, TCG_REG_R0, CR_EQ + 0*4 + 1, 31, 31); } -static void tcg_out_brcond2 (TCGContext *s, const TCGArg *args, - const int *const_args) +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) +#endif +static const TCGOutOpSetcond2 outop_setcond2 = { + .base.static_constraint = C_O1_I4(r, r, r, rU, rC), + .out = tgen_setcond2, +}; + +static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh, TCGLabel *l) { - tcg_out_cmp2(s, args, const_args); - tcg_out_bc(s, BC | BI(7, CR_EQ) | BO_COND_TRUE, arg_label(args[5])); + assert(TCG_TARGET_REG_BITS == 32); + tcg_out_cmp2(s, cond, al, ah, bl, const_bl, bh, const_bh); + tcg_out_bc_lab(s, TCG_COND_EQ, l); } -static void tcg_out_mb(TCGContext *s, TCGArg a0) +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) +#endif +static const TCGOutOpBrcond2 outop_brcond2 = { + .base.static_constraint = C_O0_I4(r, r, rU, rC), + .out = tgen_brcond2, +}; + +static void tcg_out_mb(TCGContext *s, unsigned a0) { uint32_t insn; @@ -2091,14 +2390,13 @@ bool tcg_target_has_memory_bswap(MemOp memop) #define MIN_TLB_MASK_TABLE_OFS -32768 /* - * For softmmu, perform the TLB load and compare. - * For useronly, perform any required alignment tests. + * 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) + TCGReg addr, MemOpIdx oi, bool is_ld) { TCGType addr_type = s->addr_type; TCGLabelQemuLdst *ldst = NULL; @@ -2122,172 +2420,142 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, s_bits == MO_128); a_bits = h->aa.align; -#ifdef CONFIG_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); - - 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_REG_TMP1, TCG_AREG0, mask_off); - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP2, 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_R0, addrlo, - s->page_bits - CPU_TLB_ENTRY_BITS); - } else { - 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)); + 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 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)); + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addr_reg = addr; + + /* 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); + + /* Extract the page index, shifted into place for tlb index. */ + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_shri32(s, TCG_REG_R0, addr, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); } 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); + tcg_out_shri64(s, TCG_REG_R0, addr, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); } - } 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)); - } + tcg_out32(s, AND | SAB(TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_R0)); - /* 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. + * Load the 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 (a_bits < s_bits) { - a_bits = s_bits; + if (cmp_off == 0) { + tcg_out32(s, (TCG_TARGET_REG_BITS == 64 ? LDUX : 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_PTR, TCG_REG_TMP2, TCG_REG_TMP1, cmp_off); } - 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. + * Load the TLB addend for use on the fast path. + * Do this asap to minimize any load use delay. */ - 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 == 64 || addr_type == TCG_TYPE_I32) { + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_REG_TMP1, + offsetof(CPUTLBEntry, addend)); } - } - 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); + /* 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, addr, 0, + (32 - a_bits) & 31, 31 - TARGET_PAGE_BITS); + } else { + TCGReg t = addr; + + /* + * 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; + } - /* 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); + /* 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 - TARGET_PAGE_BITS); + } 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); + } + } - /* Load addend, deferred for this case. */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_REG_TMP1, - offsetof(CPUTLBEntry, addend)); + /* Full comparison into cr0. */ + tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP2, 0, 0, addr_type); - /* High part comparison into cr6. */ - tcg_out_cmp(s, TCG_COND_EQ, addrhi, TCG_REG_TMP2, 0, 6, TCG_TYPE_I32); + /* 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); - /* Combine comparisons into cr7. */ - tcg_out32(s, CRAND | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); + h->base = TCG_REG_TMP1; } else { - /* Full comparison into cr7. */ - tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP2, 0, 7, addr_type); - } - - /* Load a pointer into the current opcode w/conditional branch-link. */ - ldst->label_ptr[0] = s->code_ptr; - tcg_out32(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | 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)); + if (a_bits) { + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addr_reg = addr; + + /* 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(addr, 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); + } - 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; } - h->base = guest_base ? TCG_GUEST_BASE_REG : 0; -#endif - if (TCG_TARGET_REG_BITS == 64 && addr_type == TCG_TYPE_I32) { /* Zero-extend the guest address for use in the host address. */ - tcg_out_ext32u(s, TCG_REG_R0, addrlo); - h->index = TCG_REG_R0; + tcg_out_ext32u(s, TCG_REG_TMP2, addr); + h->index = TCG_REG_TMP2; } else { - h->index = addrlo; + h->index = addr; } return ldst; } static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) + TCGReg addr, MemOpIdx oi, TCGType data_type) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, true); + ldst = prepare_host_addr(s, &h, addr, oi, true); if (TCG_TARGET_REG_BITS == 32 && (opc & MO_SIZE) == MO_64) { if (opc & MO_BSWAP) { @@ -2331,14 +2599,13 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, } static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addrhi, - MemOpIdx oi, TCGType data_type) + TCGReg addr, MemOpIdx oi, TCGType data_type) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; HostAddress h; - ldst = prepare_host_addr(s, &h, addrlo, addrhi, oi, false); + ldst = prepare_host_addr(s, &h, addr, oi, false); if (TCG_TARGET_REG_BITS == 32 && (opc & MO_SIZE) == MO_64) { if (opc & MO_BSWAP) { @@ -2357,9 +2624,9 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, uint32_t insn = qemu_stx_opc[opc & (MO_BSWAP | MO_SIZE)]; if (!have_isa_2_06 && insn == STDBRX) { tcg_out32(s, STWBRX | SAB(datalo, h.base, h.index)); - tcg_out32(s, ADDI | TAI(TCG_REG_TMP1, h.index, 4)); + 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, h.base, TCG_REG_TMP1)); + tcg_out32(s, STWBRX | SAB(TCG_REG_R0, h.base, TCG_REG_TMP2)); } else { tcg_out32(s, insn | SAB(datalo, h.base, h.index)); } @@ -2382,7 +2649,7 @@ static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, uint32_t insn; TCGReg index; - ldst = prepare_host_addr(s, &h, addr_reg, -1, oi, is_ld); + ldst = prepare_host_addr(s, &h, addr_reg, oi, is_ld); /* Compose the final address, as LQ/STQ have no indexing. */ index = h.index; @@ -2396,6 +2663,7 @@ static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, 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 { @@ -2427,6 +2695,60 @@ static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, } } +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) +{ + tcg_out_qemu_ld(s, data, -1, addr, oi, type); +} + +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_qemu_ld(s, datalo, datahi, addr, oi, type); + } else { + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr, oi, true); + } +} + +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = + TCG_TARGET_REG_BITS == 64 ? C_N1O1_I1(o, m, r) : C_O2_I1(r, r, r), + .out = tgen_qemu_ld2, +}; + +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) +{ + tcg_out_qemu_st(s, data, -1, addr, oi, type); +} + +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(r, r), + .out = tgen_qemu_st, +}; + +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_qemu_st(s, datalo, datahi, addr, oi, type); + } else { + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr, oi, false); + } +} + +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = + TCG_TARGET_REG_BITS == 64 ? C_O0_I3(o, m, r) : C_O0_I3(r, r, r), + .out = tgen_qemu_st2, +}; + static void tcg_out_nop_fill(tcg_insn_unit *p, int count) { int i; @@ -2500,18 +2822,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 */ @@ -2527,6 +2844,21 @@ 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); @@ -2536,33 +2868,40 @@ static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg) 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 = tcg_tbrel_diff(s, (void *)ptr); - tcg_out_mem_long(s, LD, LDX, TCG_REG_TB, TCG_REG_TB, offset); - - /* TODO: Use direct branches when possible. */ - set_jmp_insn_offset(s, which); - tcg_out32(s, MTSPR | RS(TCG_REG_TB) | CTR); - - tcg_out32(s, BCCTR | BO_ALWAYS); - - /* For the unlinked case, need to reset TCG_REG_TB. */ - set_jmp_reset_offset(s, which); - tcg_out_mem_long(s, ADDI, ADD, TCG_REG_TB, TCG_REG_TB, - -tcg_current_code_size(s)); + 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 { - /* 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. */ - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP1, ptr - (int16_t)ptr); - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_REG_TMP1, (int16_t)ptr); - tcg_out32(s, MTSPR | RS(TCG_REG_TMP1) | CTR); - tcg_out32(s, BCCTR | BO_ALWAYS); - set_jmp_reset_offset(s, which); + 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); +} + +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out32(s, MTSPR | RS(a0) | CTR); + tcg_out32(s, ADDI | TAI(TCG_REG_R3, 0, 0)); + tcg_out32(s, BCCTR | BO_ALWAYS); } void tb_target_set_jmp_target(const TranslationBlock *tb, int n, @@ -2572,10 +2911,6 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, intptr_t diff = addr - jmp_rx; tcg_insn_unit insn; - if (USE_REG_TB) { - return; - } - if (in_range_b(diff)) { insn = B | (diff & 0x3fffffc); } else { @@ -2586,606 +2921,913 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, 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]) + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) { - TCGArg a0, a1, a2; + tcg_out32(s, ADD | TAB(a0, a1, a2)); +} - switch (opc) { - 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; - case INDEX_op_br: - { - TCGLabel *l = arg_label(args[0]); - uint32_t insn = B; - - if (l->has_value) { - insn |= reloc_pc24_val(tcg_splitwx_to_rx(s->code_ptr), - l->u.value_ptr); - } else { - tcg_out_reloc(s, s->code_ptr, R_PPC_REL24, l, 0); - } - tcg_out32(s, insn); - } - break; - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); - break; - 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, TCG_TYPE_REG, args[0], args[0]); - break; - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - tcg_out_mem_long(s, LHZ, LHZX, args[0], args[1], args[2]); - break; - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - tcg_out_mem_long(s, LHA, LHAX, args[0], args[1], args[2]); - break; - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - tcg_out_mem_long(s, LWZ, LWZX, args[0], args[1], args[2]); - break; - case INDEX_op_ld32s_i64: - tcg_out_mem_long(s, LWA, LWAX, args[0], args[1], args[2]); - break; - case INDEX_op_ld_i64: - tcg_out_mem_long(s, LD, LDX, args[0], args[1], args[2]); - break; - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - tcg_out_mem_long(s, STB, STBX, args[0], args[1], args[2]); - break; - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - tcg_out_mem_long(s, STH, STHX, args[0], args[1], args[2]); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - tcg_out_mem_long(s, STW, STWX, args[0], args[1], args[2]); - break; - case INDEX_op_st_i64: - tcg_out_mem_long(s, STD, STDX, args[0], args[1], args[2]); - break; +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_mem_long(s, ADDI, ADD, a0, a1, a2); +} - case INDEX_op_add_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - do_addi_32: - tcg_out_mem_long(s, ADDI, ADD, a0, a1, (int32_t)a2); - } else { - tcg_out32(s, ADD | TAB(a0, a1, a2)); - } - break; - case INDEX_op_sub_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[1]) { - if (const_args[2]) { - tcg_out_movi(s, TCG_TYPE_I32, a0, a1 - a2); - } else { - tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); - } - } else if (const_args[2]) { - a2 = -a2; - goto do_addi_32; - } else { - tcg_out32(s, SUBF | TAB(a0, a2, a1)); - } - break; +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, rT), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; - case INDEX_op_and_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi32(s, a0, a1, a2); - } else { - tcg_out32(s, AND | SAB(a1, a0, a2)); - } - break; - case INDEX_op_and_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi64(s, a0, a1, a2); - } else { - tcg_out32(s, AND | SAB(a1, a0, a2)); - } - break; - case INDEX_op_or_i64: - case INDEX_op_or_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_ori32(s, a0, a1, a2); - } else { - tcg_out32(s, OR | SAB(a1, a0, a2)); - } - break; - case INDEX_op_xor_i64: - case INDEX_op_xor_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_xori32(s, a0, a1, a2); - } else { - tcg_out32(s, XOR | SAB(a1, a0, a2)); - } - break; - case INDEX_op_andc_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi32(s, a0, a1, ~a2); - } else { - tcg_out32(s, ANDC | SAB(a1, a0, a2)); - } - break; - case INDEX_op_andc_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi64(s, a0, a1, ~a2); - } else { - tcg_out32(s, ANDC | SAB(a1, a0, a2)); - } - break; - case INDEX_op_orc_i32: - if (const_args[2]) { - tcg_out_ori32(s, args[0], args[1], ~args[2]); - break; - } - /* FALLTHRU */ - case INDEX_op_orc_i64: - tcg_out32(s, ORC | SAB(args[1], args[0], args[2])); - break; - case INDEX_op_eqv_i32: - if (const_args[2]) { - tcg_out_xori32(s, args[0], args[1], ~args[2]); - break; - } - /* FALLTHRU */ - case INDEX_op_eqv_i64: - tcg_out32(s, EQV | SAB(args[1], args[0], args[2])); - break; - case INDEX_op_nand_i32: - case INDEX_op_nand_i64: - tcg_out32(s, NAND | SAB(args[1], args[0], args[2])); - break; - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: - tcg_out32(s, NOR | SAB(args[1], args[0], args[2])); - break; +static void tgen_addco_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, ADDC | TAB(a0, a1, a2)); +} - case INDEX_op_clz_i32: - tcg_out_cntxz(s, TCG_TYPE_I32, CNTLZW, args[0], args[1], - args[2], const_args[2]); - break; - case INDEX_op_ctz_i32: - tcg_out_cntxz(s, TCG_TYPE_I32, CNTTZW, args[0], args[1], - args[2], const_args[2]); - break; - case INDEX_op_ctpop_i32: - tcg_out32(s, CNTPOPW | SAB(args[1], args[0], 0)); - break; +static void tgen_addco_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out32(s, ADDIC | TAI(a0, a1, a2)); +} - case INDEX_op_clz_i64: - tcg_out_cntxz(s, TCG_TYPE_I64, CNTLZD, args[0], args[1], - args[2], const_args[2]); - break; - case INDEX_op_ctz_i64: - tcg_out_cntxz(s, TCG_TYPE_I64, CNTTZD, args[0], args[1], - args[2], const_args[2]); - break; - case INDEX_op_ctpop_i64: - tcg_out32(s, CNTPOPD | SAB(args[1], args[0], 0)); - break; +static TCGConstraintSetIndex cset_addco(TCGType type, unsigned flags) +{ + /* + * Note that the CA bit is defined based on the word size of the + * environment. So in 64-bit mode it's always carry-out of bit 63. + * The fallback code using deposit works just as well for TCG_TYPE_I32. + */ + return type == TCG_TYPE_REG ? C_O1_I2(r, r, rI) : C_NotImplemented; +} - case INDEX_op_mul_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out32(s, MULLI | TAI(a0, a1, a2)); - } else { - tcg_out32(s, MULLW | TAB(a0, a1, a2)); - } - break; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addco, + .out_rrr = tgen_addco_rrr, + .out_rri = tgen_addco_rri, +}; - case INDEX_op_div_i32: - tcg_out32(s, DIVW | TAB(args[0], args[1], args[2])); - break; +static void tgen_addcio_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, ADDE | TAB(a0, a1, a2)); +} - case INDEX_op_divu_i32: - tcg_out32(s, DIVWU | TAB(args[0], args[1], args[2])); - break; +static void tgen_addcio_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out32(s, (a2 ? ADDME : ADDZE) | RT(a0) | RA(a1)); +} - case INDEX_op_rem_i32: - tcg_out32(s, MODSW | TAB(args[0], args[1], args[2])); - break; +static TCGConstraintSetIndex cset_addcio(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_REG ? C_O1_I2(r, r, rZM) : C_NotImplemented; +} - case INDEX_op_remu_i32: - tcg_out32(s, MODUW | TAB(args[0], args[1], args[2])); - break; +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addcio, + .out_rrr = tgen_addcio_rrr, + .out_rri = tgen_addcio_rri, +}; - case INDEX_op_shl_i32: - if (const_args[2]) { - /* Limit immediate shift count lest we create an illegal insn. */ - tcg_out_shli32(s, args[0], args[1], args[2] & 31); - } else { - tcg_out32(s, SLW | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_shr_i32: - if (const_args[2]) { - /* Limit immediate shift count lest we create an illegal insn. */ - tcg_out_shri32(s, args[0], args[1], args[2] & 31); - } else { - tcg_out32(s, SRW | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_sar_i32: - if (const_args[2]) { - tcg_out_sari32(s, args[0], args[1], args[2]); - } else { - tcg_out32(s, SRAW | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_rotl_i32: - if (const_args[2]) { - tcg_out_rlw(s, RLWINM, args[0], args[1], args[2], 0, 31); - } else { - tcg_out32(s, RLWNM | SAB(args[1], args[0], args[2]) - | MB(0) | ME(31)); - } - break; - case INDEX_op_rotr_i32: - if (const_args[2]) { - tcg_out_rlw(s, RLWINM, args[0], args[1], 32 - args[2], 0, 31); - } else { - tcg_out32(s, SUBFIC | TAI(TCG_REG_R0, args[2], 32)); - tcg_out32(s, RLWNM | SAB(args[1], args[0], TCG_REG_R0) - | MB(0) | ME(31)); - } - break; +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addcio, + .out_rrr = tgen_addcio_rrr, + .out_rri = tgen_addcio_rri, +}; - case INDEX_op_brcond_i32: - tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], - arg_label(args[3]), TCG_TYPE_I32); - break; - case INDEX_op_brcond_i64: - tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], - arg_label(args[3]), TCG_TYPE_I64); - break; - case INDEX_op_brcond2_i32: - tcg_out_brcond2(s, args, const_args); - break; +static void tcg_out_set_carry(TCGContext *s) +{ + tcg_out32(s, SUBFC | TAB(TCG_REG_R0, TCG_REG_R0, TCG_REG_R0)); +} - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: - tcg_out32(s, NEG | RT(args[0]) | RA(args[1])); - break; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, AND | SAB(a1, a0, a2)); +} - case INDEX_op_not_i32: - case INDEX_op_not_i64: - tcg_out32(s, NOR | SAB(args[1], args[0], args[1])); - break; +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_andi32(s, a0, a1, a2); + } else { + tcg_out_andi64(s, a0, a1, a2); + } +} - case INDEX_op_add_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - do_addi_64: - tcg_out_mem_long(s, ADDI, ADD, a0, a1, a2); - } else { - tcg_out32(s, ADD | TAB(a0, a1, a2)); - } - break; - case INDEX_op_sub_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[1]) { - if (const_args[2]) { - tcg_out_movi(s, TCG_TYPE_I64, a0, a1 - a2); - } else { - tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); - } - } else if (const_args[2]) { - a2 = -a2; - goto do_addi_64; - } else { - tcg_out32(s, SUBF | TAB(a0, a2, a1)); - } - break; +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; - case INDEX_op_shl_i64: - if (const_args[2]) { - /* Limit immediate shift count lest we create an illegal insn. */ - tcg_out_shli64(s, args[0], args[1], args[2] & 63); - } else { - tcg_out32(s, SLD | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_shr_i64: - if (const_args[2]) { - /* Limit immediate shift count lest we create an illegal insn. */ - tcg_out_shri64(s, args[0], args[1], args[2] & 63); - } else { - tcg_out32(s, SRD | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_sar_i64: - if (const_args[2]) { - tcg_out_sari64(s, args[0], args[1], args[2]); - } else { - tcg_out32(s, SRAD | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_rotl_i64: - if (const_args[2]) { - tcg_out_rld(s, RLDICL, args[0], args[1], args[2], 0); - } else { - tcg_out32(s, RLDCL | SAB(args[1], args[0], args[2]) | MB64(0)); - } - break; - case INDEX_op_rotr_i64: - if (const_args[2]) { - tcg_out_rld(s, RLDICL, args[0], args[1], 64 - args[2], 0); - } else { - tcg_out32(s, SUBFIC | TAI(TCG_REG_R0, args[2], 64)); - tcg_out32(s, RLDCL | SAB(args[1], args[0], TCG_REG_R0) | MB64(0)); - } - break; +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, ANDC | SAB(a1, a0, a2)); +} - case INDEX_op_mul_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out32(s, MULLI | TAI(a0, a1, a2)); - } else { - tcg_out32(s, MULLD | TAB(a0, a1, a2)); - } - break; - case INDEX_op_div_i64: - tcg_out32(s, DIVD | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_divu_i64: - tcg_out32(s, DIVDU | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_rem_i64: - tcg_out32(s, MODSD | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_remu_i64: - tcg_out32(s, MODUD | TAB(args[0], args[1], args[2])); - break; +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_andc, +}; - 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_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_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_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; +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? CNTLZW : CNTLZD; + tcg_out_cntxz(s, type, insn, a0, a1, a2, false); +} - 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; +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? CNTLZW : CNTLZD; + tcg_out_cntxz(s, type, insn, a0, a1, a2, true); +} + +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_O1_I2(r, r, rZW), + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; + +static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + uint32_t insn = type == TCG_TYPE_I32 ? CNTPOPW : CNTPOPD; + tcg_out32(s, insn | SAB(a1, a0, 0)); +} + +static TCGConstraintSetIndex cset_ctpop(TCGType type, unsigned flags) +{ + return have_isa_2_06 ? C_O1_I1(r, r) : C_NotImplemented; +} + +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctpop, + .out_rr = tgen_ctpop, +}; + +static void tgen_ctz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? CNTTZW : CNTTZD; + tcg_out_cntxz(s, type, insn, a0, a1, a2, false); +} + +static void tgen_ctzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? CNTTZW : CNTTZD; + tcg_out_cntxz(s, type, insn, a0, a1, a2, true); +} + +static TCGConstraintSetIndex cset_ctz(TCGType type, unsigned flags) +{ + return have_isa_3_00 ? C_O1_I2(r, r, rZW) : C_NotImplemented; +} + +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctz, + .out_rrr = tgen_ctz, + .out_rri = tgen_ctzi, +}; + +static void tgen_eqv(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, EQV | SAB(a1, a0, a2)); +} + +#if TCG_TARGET_REG_BITS == 64 +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_shri64(s, a0, a1, 32); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; +#endif + +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? DIVW : DIVD; + tcg_out32(s, insn | TAB(a0, a1, a2)); +} + +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divs, +}; + +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? DIVWU : DIVDU; + tcg_out32(s, insn | TAB(a0, a1, a2)); +} + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divu, +}; + +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_eqv, +}; + +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? MULLW : MULLD; + tcg_out32(s, insn | TAB(a0, a1, a2)); +} + +static void tgen_muli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out32(s, MULLI | TAI(a0, a1, a2)); +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_mul, + .out_rri = tgen_muli, +}; + +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_mulsh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? MULHW : MULHD; + tcg_out32(s, insn | TAB(a0, a1, a2)); +} + +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mulsh, +}; + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_muluh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? MULHWU : MULHDU; + tcg_out32(s, insn | TAB(a0, a1, a2)); +} + +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_muluh, +}; + +static void tgen_nand(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, NAND | SAB(a1, a0, a2)); +} + +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_nand, +}; + +static void tgen_nor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, NOR | SAB(a1, a0, a2)); +} + +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_nor, +}; + +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, OR | SAB(a1, a0, a2)); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_ori32(s, a0, a1, a2); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rU), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + +static void tgen_orc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, ORC | SAB(a1, a0, a2)); +} + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_orc, +}; + +static TCGConstraintSetIndex cset_mod(TCGType type, unsigned flags) +{ + return have_isa_3_00 ? C_O1_I2(r, r, r) : C_NotImplemented; +} + +static void tgen_rems(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? MODSW : MODSD; + tcg_out32(s, insn | TAB(a0, a1, a2)); +} + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mod, + .out_rrr = tgen_rems, +}; + +static void tgen_remu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? MODUW : MODUD; + tcg_out32(s, insn | TAB(a0, a1, a2)); +} + +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mod, + .out_rrr = tgen_remu, +}; + +static void tgen_rotl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out32(s, RLWNM | SAB(a1, a0, a2) | MB(0) | ME(31)); + } else { + tcg_out32(s, RLDCL | SAB(a1, a0, a2) | MB64(0)); + } +} + +static void tgen_rotli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_rlw(s, RLWINM, a0, a1, a2, 0, 31); + } else { + tcg_out_rld(s, RLDICL, a0, a1, a2, 0); + } +} + +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_rotl, + .out_rri = tgen_rotli, +}; + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SRAW : SRAD; + tcg_out32(s, insn | SAB(a1, a0, a2)); +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + /* Limit immediate shift count lest we create an illegal insn. */ + if (type == TCG_TYPE_I32) { + tcg_out_sari32(s, a0, a1, a2 & 31); + } else { + tcg_out_sari64(s, a0, a1, a2 & 63); + } +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SLW : SLD; + tcg_out32(s, insn | SAB(a1, a0, a2)); +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + /* Limit immediate shift count lest we create an illegal insn. */ + if (type == TCG_TYPE_I32) { + tcg_out_shli32(s, a0, a1, a2 & 31); + } else { + tcg_out_shli64(s, a0, a1, a2 & 63); + } +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SRW : SRD; + tcg_out32(s, insn | SAB(a1, a0, a2)); +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + /* Limit immediate shift count lest we create an illegal insn. */ + if (type == TCG_TYPE_I32) { + tcg_out_shri32(s, a0, a1, a2 & 31); + } else { + tcg_out_shri64(s, a0, a1, a2 & 63); + } +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, SUBF | TAB(a0, a2, a1)); +} + +static void tgen_subfi(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, rI, r), + .out_rrr = tgen_sub, + .out_rir = tgen_subfi, +}; + +static void tgen_subbo_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, SUBFC | TAB(a0, a2, a1)); +} + +static void tgen_subbo_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a2 == 0) { + tcg_out_movi(s, type, TCG_REG_R0, 0); + tgen_subbo_rrr(s, type, a0, a1, TCG_REG_R0); + } else { + tgen_addco_rri(s, type, a0, a1, -a2); + } +} + +/* The underlying insn for subfi is subfic. */ +#define tgen_subbo_rir tgen_subfi + +static void tgen_subbo_rii(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, tcg_target_long a2) +{ + tcg_out_movi(s, type, TCG_REG_R0, a2); + tgen_subbo_rir(s, type, a0, a1, TCG_REG_R0); +} + +static TCGConstraintSetIndex cset_subbo(TCGType type, unsigned flags) +{ + /* Recall that the CA bit is defined based on the host word size. */ + return type == TCG_TYPE_REG ? C_O1_I2(r, rI, rN) : C_NotImplemented; +} + +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_subbo, + .out_rrr = tgen_subbo_rrr, + .out_rri = tgen_subbo_rri, + .out_rir = tgen_subbo_rir, + .out_rii = tgen_subbo_rii, +}; + +static void tgen_subbio_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, SUBFE | TAB(a0, a2, a1)); +} + +static void tgen_subbio_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_addcio_rri(s, type, a0, a1, ~a2); +} + +static void tgen_subbio_rir(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tcg_debug_assert(a1 == 0 || a1 == -1); + tcg_out32(s, (a1 ? SUBFME : SUBFZE) | RT(a0) | RA(a2)); +} + +static void tgen_subbio_rii(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, tcg_target_long a2) +{ + tcg_out_movi(s, type, TCG_REG_R0, a2); + tgen_subbio_rir(s, type, a0, a1, TCG_REG_R0); +} + +static TCGConstraintSetIndex cset_subbio(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_REG ? C_O1_I2(r, rZM, rZM) : C_NotImplemented; +} + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_subbio, + .out_rrr = tgen_subbio_rrr, + .out_rri = tgen_subbio_rri, + .out_rir = tgen_subbio_rir, + .out_rii = tgen_subbio_rii, +}; + +#define outop_subbi outop_subbio + +static void tcg_out_set_borrow(TCGContext *s) +{ + /* borrow = !carry */ + tcg_out32(s, ADDIC | TAI(TCG_REG_R0, TCG_REG_R0, 0)); +} + +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, XOR | SAB(a1, a0, a2)); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_xori32(s, a0, a1, a2); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rU), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg dst, TCGReg src, unsigned flags) +{ + TCGReg tmp = dst == src ? TCG_REG_R0 : dst; + + if (have_isa_3_10) { + tcg_out32(s, BRH | RA(dst) | RS(src)); + if (flags & TCG_BSWAP_OS) { + 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); } - /* fall through */ - case INDEX_op_qemu_st_a32_i32: - tcg_out_qemu_st(s, args[0], -1, args[1], -1, args[2], TCG_TYPE_I32); - break; - case INDEX_op_qemu_st_a32_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_st(s, args[0], -1, args[1], -1, - args[2], TCG_TYPE_I64); - } else { - tcg_out_qemu_st(s, args[0], args[1], args[2], -1, - args[3], TCG_TYPE_I64); + return; + } + + /* + * In the following, + * dep(a, b, m) -> (a & ~m) | (b & m) + * + * Begin with: src = xxxxabcd + */ + /* tmp = rol32(src, 24) & 0x000000ff = 0000000c */ + tcg_out_rlw(s, RLWINM, tmp, src, 24, 24, 31); + /* tmp = dep(tmp, rol32(src, 8), 0x0000ff00) = 000000dc */ + tcg_out_rlw(s, RLWIMI, tmp, src, 8, 16, 23); + + if (flags & TCG_BSWAP_OS) { + tcg_out_ext16s(s, TCG_TYPE_REG, dst, tmp); + } else { + tcg_out_mov(s, TCG_TYPE_REG, dst, tmp); + } +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap16, +}; + +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg dst, TCGReg src, unsigned flags) +{ + TCGReg tmp = dst == src ? TCG_REG_R0 : dst; + + if (have_isa_3_10) { + tcg_out32(s, BRW | RA(dst) | RS(src)); + if (flags & TCG_BSWAP_OS) { + tcg_out_ext32s(s, dst, dst); + } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + tcg_out_ext32u(s, dst, dst); } - break; - case INDEX_op_qemu_st_a64_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_st(s, args[0], -1, args[1], -1, - args[2], TCG_TYPE_I64); - } else { - tcg_out_qemu_st(s, args[0], args[1], args[2], args[3], - args[4], TCG_TYPE_I64); + return; + } + + /* + * Stolen from gcc's builtin_bswap32. + * In the following, + * dep(a, b, m) -> (a & ~m) | (b & m) + * + * Begin with: src = xxxxabcd + */ + /* tmp = rol32(src, 8) & 0xffffffff = 0000bcda */ + tcg_out_rlw(s, RLWINM, tmp, src, 8, 0, 31); + /* tmp = dep(tmp, rol32(src, 24), 0xff000000) = 0000dcda */ + tcg_out_rlw(s, RLWIMI, tmp, src, 24, 0, 7); + /* tmp = dep(tmp, rol32(src, 24), 0x0000ff00) = 0000dcba */ + tcg_out_rlw(s, RLWIMI, tmp, src, 24, 16, 23); + + if (flags & TCG_BSWAP_OS) { + tcg_out_ext32s(s, dst, tmp); + } else { + tcg_out_mov(s, TCG_TYPE_REG, dst, tmp); + } +} + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap32, +}; + +#if TCG_TARGET_REG_BITS == 64 +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg dst, TCGReg src) +{ + TCGReg t0 = dst == src ? TCG_REG_R0 : dst; + TCGReg t1 = dst == src ? dst : TCG_REG_R0; + + if (have_isa_3_10) { + tcg_out32(s, BRD | RA(dst) | RS(src)); + return; + } + + /* + * In the following, + * dep(a, b, m) -> (a & ~m) | (b & m) + * + * Begin with: src = abcdefgh + */ + /* t0 = rol32(src, 8) & 0xffffffff = 0000fghe */ + tcg_out_rlw(s, RLWINM, t0, src, 8, 0, 31); + /* t0 = dep(t0, rol32(src, 24), 0xff000000) = 0000hghe */ + tcg_out_rlw(s, RLWIMI, t0, src, 24, 0, 7); + /* t0 = dep(t0, rol32(src, 24), 0x0000ff00) = 0000hgfe */ + tcg_out_rlw(s, RLWIMI, t0, src, 24, 16, 23); + + /* t0 = rol64(t0, 32) = hgfe0000 */ + tcg_out_rld(s, RLDICL, t0, t0, 32, 0); + /* t1 = rol64(src, 32) = efghabcd */ + tcg_out_rld(s, RLDICL, t1, src, 32, 0); + + /* t0 = dep(t0, rol32(t1, 24), 0xffffffff) = hgfebcda */ + tcg_out_rlw(s, RLWIMI, t0, t1, 8, 0, 31); + /* t0 = dep(t0, rol32(t1, 24), 0xff000000) = hgfedcda */ + tcg_out_rlw(s, RLWIMI, t0, t1, 24, 0, 7); + /* t0 = dep(t0, rol32(t1, 24), 0x0000ff00) = hgfedcba */ + tcg_out_rlw(s, RLWIMI, t0, t1, 24, 16, 23); + + tcg_out_mov(s, TCG_TYPE_REG, dst, t0); +} + +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap64, +}; +#endif /* TCG_TARGET_REG_BITS == 64 */ + +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out32(s, NEG | RT(a0) | RA(a1)); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_nor(s, type, a0, a1, a1); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) +{ + if (type == TCG_TYPE_I32) { + tcg_out_rlw(s, RLWIMI, a0, a2, ofs, 32 - ofs - len, 31 - ofs); + } else { + tcg_out_rld(s, RLDIMI, a0, a2, ofs, 64 - ofs - len); + } +} + +static void tgen_depositi(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + tcg_target_long a2, unsigned ofs, unsigned len) +{ + tgen_andi(s, type, a0, a1, ~MAKE_64BIT_MASK(ofs, len)); +} + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(r, 0, rZ), + .out_rrr = tgen_deposit, + .out_rri = tgen_depositi, +}; + +static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0 && len <= 16) { + tgen_andi(s, TCG_TYPE_I32, a0, a1, (1 << len) - 1); + } else if (type == TCG_TYPE_I32) { + tcg_out_rlw(s, RLWINM, a0, a1, 32 - ofs, 32 - len, 31); + } else { + tcg_out_rld(s, RLDICL, a0, a1, 64 - ofs, 64 - len); + } +} + +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; + +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8s(s, type, a0, a1); + return; + case 16: + tcg_out_ext16s(s, type, a0, a1); + return; + case 32: + tcg_out_ext32s(s, a0, a1); + return; } - break; - case INDEX_op_qemu_st_a32_i128: - case INDEX_op_qemu_st_a64_i128: - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); - tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); - break; + } else if (ofs + len == 32) { + tcg_out_sari32(s, a0, a1, ofs); + return; + } + g_assert_not_reached(); +} + +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; - case INDEX_op_setcond_i32: - tcg_out_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], 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], 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); - break; +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: - tcg_out_bswap16(s, args[0], args[1], args[2]); - break; - case INDEX_op_bswap32_i32: - tcg_out_bswap32(s, args[0], args[1], 0); - break; - case INDEX_op_bswap32_i64: - tcg_out_bswap32(s, args[0], args[1], args[2]); - break; - case INDEX_op_bswap64_i64: - tcg_out_bswap64(s, args[0], args[1]); - break; +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem_long(s, LBZ, LBZX, dest, base, offset); +} - case INDEX_op_deposit_i32: - if (const_args[2]) { - uint32_t mask = ((2u << (args[4] - 1)) - 1) << args[3]; - tcg_out_andi32(s, args[0], args[0], ~mask); - } else { - tcg_out_rlw(s, RLWIMI, args[0], args[2], args[3], - 32 - args[3] - args[4], 31 - args[3]); - } - break; - case INDEX_op_deposit_i64: - if (const_args[2]) { - uint64_t mask = ((2ull << (args[4] - 1)) - 1) << args[3]; - tcg_out_andi64(s, args[0], args[0], ~mask); - } else { - tcg_out_rld(s, RLDIMI, args[0], args[2], args[3], - 64 - args[3] - args[4]); - } - break; +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; - case INDEX_op_extract_i32: - tcg_out_rlw(s, RLWINM, args[0], args[1], - 32 - args[2], 32 - args[3], 31); - break; - case INDEX_op_extract_i64: - tcg_out_rld(s, RLDICL, args[0], args[1], 64 - args[2], 64 - args[3]); - break; +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tgen_ld8u(s, type, dest, base, offset); + tcg_out_ext8s(s, type, dest, dest); +} - case INDEX_op_movcond_i32: - tcg_out_movcond(s, TCG_TYPE_I32, args[5], args[0], args[1], args[2], - args[3], args[4], const_args[2]); - break; - case INDEX_op_movcond_i64: - tcg_out_movcond(s, TCG_TYPE_I64, args[5], args[0], args[1], args[2], - args[3], args[4], const_args[2]); - break; +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; -#if TCG_TARGET_REG_BITS == 64 - case INDEX_op_add2_i64: -#else - case INDEX_op_add2_i32: -#endif - /* Note that the CA bit is defined based on the word size of the - environment. So in 64-bit mode it's always carry-out of bit 63. - The fallback code using deposit works just as well for 32-bit. */ - a0 = args[0], a1 = args[1]; - if (a0 == args[3] || (!const_args[5] && a0 == args[5])) { - a0 = TCG_REG_R0; - } - if (const_args[4]) { - tcg_out32(s, ADDIC | TAI(a0, args[2], args[4])); - } else { - tcg_out32(s, ADDC | TAB(a0, args[2], args[4])); - } - if (const_args[5]) { - tcg_out32(s, (args[5] ? ADDME : ADDZE) | RT(a1) | RA(args[3])); - } else { - tcg_out32(s, ADDE | TAB(a1, args[3], args[5])); - } - if (a0 != args[0]) { - tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); - } - break; +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem_long(s, LHZ, LHZX, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem_long(s, LHA, LHAX, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; #if TCG_TARGET_REG_BITS == 64 - case INDEX_op_sub2_i64: -#else - case INDEX_op_sub2_i32: +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem_long(s, LWZ, LWZX, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem_long(s, LWA, LWAX, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; #endif - a0 = args[0], a1 = args[1]; - if (a0 == args[5] || (!const_args[3] && a0 == args[3])) { - a0 = TCG_REG_R0; - } - if (const_args[2]) { - tcg_out32(s, SUBFIC | TAI(a0, args[4], args[2])); - } else { - tcg_out32(s, SUBFC | TAB(a0, args[4], args[2])); - } - if (const_args[3]) { - tcg_out32(s, (args[3] ? SUBFME : SUBFZE) | RT(a1) | RA(args[5])); - } else { - tcg_out32(s, SUBFE | TAB(a1, args[5], args[3])); - } - if (a0 != args[0]) { - tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); - } - break; - case INDEX_op_muluh_i32: - tcg_out32(s, MULHWU | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_mulsh_i32: - tcg_out32(s, MULHW | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_muluh_i64: - tcg_out32(s, MULHDU | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_mulsh_i64: - tcg_out32(s, MULHD | TAB(args[0], args[1], args[2])); - break; +static void tgen_st8(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem_long(s, STB, STBX, data, base, offset); +} - case INDEX_op_mb: - tcg_out_mb(s, args[0]); - break; +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st8, +}; - 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 tgen_st16(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem_long(s, STH, STHX, data, base, offset); } +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st16, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tcg_out_st, +}; + + int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) { switch (opc) { @@ -3216,12 +3858,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: @@ -3362,6 +4006,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], @@ -3372,10 +4159,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 }, @@ -3457,24 +4240,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; @@ -3486,26 +4268,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: @@ -3570,56 +4340,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) { @@ -3640,7 +4360,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); @@ -3694,10 +4414,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); @@ -3715,164 +4431,15 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, va_end(va); } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - - case INDEX_op_ld8u_i32: - case INDEX_op_ld8s_i32: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16s_i32: - case INDEX_op_ld_i32: - case INDEX_op_ctpop_i32: - case INDEX_op_neg_i32: - case INDEX_op_not_i32: - case INDEX_op_ext8s_i32: - case INDEX_op_ext16s_i32: - case INDEX_op_bswap16_i32: - case INDEX_op_bswap32_i32: - case INDEX_op_extract_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: - case INDEX_op_ctpop_i64: - case INDEX_op_neg_i64: - case INDEX_op_not_i64: - case INDEX_op_ext8s_i64: - case INDEX_op_ext16s_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext_i32_i64: - case INDEX_op_extu_i32_i64: - case INDEX_op_bswap16_i64: - case INDEX_op_bswap32_i64: - case INDEX_op_bswap64_i64: - case INDEX_op_extract_i64: - return C_O1_I1(r, r); - - case INDEX_op_st8_i32: - case INDEX_op_st16_i32: - case INDEX_op_st_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - return C_O0_I2(r, r); - - case INDEX_op_add_i32: - case INDEX_op_and_i32: - case INDEX_op_or_i32: - case INDEX_op_xor_i32: - case INDEX_op_andc_i32: - case INDEX_op_orc_i32: - case INDEX_op_eqv_i32: - 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_setcond_i32: - case INDEX_op_negsetcond_i32: - case INDEX_op_and_i64: - case INDEX_op_andc_i64: - 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: - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i64: - return C_O1_I2(r, r, ri); - - case INDEX_op_mul_i32: - case INDEX_op_mul_i64: - return C_O1_I2(r, r, rI); - - case INDEX_op_div_i32: - case INDEX_op_divu_i32: - case INDEX_op_rem_i32: - case INDEX_op_remu_i32: - case INDEX_op_nand_i32: - case INDEX_op_nor_i32: - case INDEX_op_muluh_i32: - case INDEX_op_mulsh_i32: - case INDEX_op_orc_i64: - case INDEX_op_eqv_i64: - case INDEX_op_nand_i64: - case INDEX_op_nor_i64: - case INDEX_op_div_i64: - case INDEX_op_divu_i64: - case INDEX_op_rem_i64: - case INDEX_op_remu_i64: - case INDEX_op_mulsh_i64: - case INDEX_op_muluh_i64: - return C_O1_I2(r, r, r); - - case INDEX_op_sub_i32: - return C_O1_I2(r, rI, ri); - case INDEX_op_add_i64: - return C_O1_I2(r, r, rT); - case INDEX_op_or_i64: - case INDEX_op_xor_i64: - return C_O1_I2(r, r, rU); - case INDEX_op_sub_i64: - return C_O1_I2(r, rI, rT); - case INDEX_op_clz_i32: - case INDEX_op_ctz_i32: - case INDEX_op_clz_i64: - case INDEX_op_ctz_i64: - return C_O1_I2(r, r, rZW); - - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - return C_O0_I2(r, ri); - - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - return C_O1_I4(r, r, ri, rZ, rZ); - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: - return C_O1_I2(r, 0, rZ); - case INDEX_op_brcond2_i32: - return C_O0_I4(r, r, ri, ri); - case INDEX_op_setcond2_i32: - return C_O1_I4(r, r, r, ri, ri); - case INDEX_op_add2_i64: - case INDEX_op_add2_i32: - return C_O2_I4(r, r, r, r, rI, rZM); - case INDEX_op_sub2_i64: - case INDEX_op_sub2_i32: - return C_O2_I4(r, r, rI, rZM, 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: + case INDEX_op_qemu_st: 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_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_qemu_st2: + return TCG_TARGET_REG_BITS == 64 + ? C_O0_I3(o, m, r) : C_O0_I3(r, r, r); case INDEX_op_add_vec: case INDEX_op_sub_vec: @@ -3924,9 +4491,11 @@ 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(); + return C_NotImplemented; } } diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index 8bfb14998ef..5607634e995 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -25,8 +25,6 @@ #ifndef PPC_TCG_TARGET_H #define PPC_TCG_TARGET_H -#include "host/cpuinfo.h" - #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) #define TCG_TARGET_NB_REGS 64 @@ -55,129 +53,4 @@ typedef enum { TCG_AREG0 = TCG_REG_R27 } TCGReg; -typedef enum { - tcg_isa_base, - tcg_isa_2_06, - tcg_isa_2_07, - tcg_isa_3_00, - tcg_isa_3_10, -} TCGPowerISA; - -#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 */ -#define TCG_TARGET_HAS_ext16u_i32 0 - -/* optional instructions */ -#define TCG_TARGET_HAS_div_i32 1 -#define TCG_TARGET_HAS_rem_i32 have_isa_3_00 -#define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_bswap16_i32 1 -#define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 -#define TCG_TARGET_HAS_andc_i32 1 -#define TCG_TARGET_HAS_orc_i32 1 -#define TCG_TARGET_HAS_eqv_i32 1 -#define TCG_TARGET_HAS_nand_i32 1 -#define TCG_TARGET_HAS_nor_i32 1 -#define TCG_TARGET_HAS_clz_i32 1 -#define TCG_TARGET_HAS_ctz_i32 have_isa_3_00 -#define TCG_TARGET_HAS_ctpop_i32 have_isa_2_06 -#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_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_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_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 -#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 0 -#define TCG_TARGET_HAS_ext16u_i64 0 -#define TCG_TARGET_HAS_ext32u_i64 0 -#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 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 -#define TCG_TARGET_HAS_nand_i64 1 -#define TCG_TARGET_HAS_nor_i64 1 -#define TCG_TARGET_HAS_clz_i64 1 -#define TCG_TARGET_HAS_ctz_i64 have_isa_3_00 -#define TCG_TARGET_HAS_ctpop_i64 have_isa_2_06 -#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 0 -#define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 1 -#define TCG_TARGET_HAS_mulsh_i64 1 -#endif - -#define TCG_TARGET_HAS_qemu_ldst_i128 \ - (TCG_TARGET_REG_BITS == 64 && have_isa_2_07) - -/* - * While technically Altivec could support V64, it has no 64-bit store - * instruction and substituting two 32-bit stores makes the generated - * code quite large. - */ -#define TCG_TARGET_HAS_v64 have_vsx -#define TCG_TARGET_HAS_v128 have_altivec -#define TCG_TARGET_HAS_v256 0 - -#define TCG_TARGET_HAS_andc_vec 1 -#define TCG_TARGET_HAS_orc_vec have_isa_2_07 -#define TCG_TARGET_HAS_nand_vec have_isa_2_07 -#define TCG_TARGET_HAS_nor_vec 1 -#define TCG_TARGET_HAS_eqv_vec have_isa_2_07 -#define TCG_TARGET_HAS_not_vec 1 -#define TCG_TARGET_HAS_neg_vec have_isa_3_00 -#define TCG_TARGET_HAS_abs_vec 0 -#define TCG_TARGET_HAS_roti_vec 0 -#define TCG_TARGET_HAS_rots_vec 0 -#define TCG_TARGET_HAS_rotv_vec 1 -#define TCG_TARGET_HAS_shi_vec 0 -#define TCG_TARGET_HAS_shs_vec 0 -#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 have_vsx -#define TCG_TARGET_HAS_cmpsel_vec 0 - -#define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS - #endif diff --git a/tcg/ppc/tcg-target.opc.h b/tcg/ppc/tcg-target.opc.h deleted file mode 100644 index db514403c3f..00000000000 --- a/tcg/ppc/tcg-target.opc.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2019 Linaro Limited - * - * 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. - * - * 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. - */ - -DEF(ppc_mrgh_vec, 1, 2, 0, IMPLVEC) -DEF(ppc_mrgl_vec, 1, 2, 0, IMPLVEC) -DEF(ppc_msum_vec, 1, 3, 0, IMPLVEC) -DEF(ppc_muleu_vec, 1, 2, 0, IMPLVEC) -DEF(ppc_mulou_vec, 1, 2, 0, IMPLVEC) -DEF(ppc_pkum_vec, 1, 2, 0, IMPLVEC) diff --git a/tcg/region.c b/tcg/region.c index 2b28ed35560..7ea0b37a84c 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -33,8 +33,19 @@ #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; QTree *tree; @@ -83,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) { @@ -399,7 +422,7 @@ void tcg_region_reset_all(void) tcg_region_tree_reset_all(); } -static size_t tcg_n_regions(size_t tb_size, unsigned max_cpus) +static size_t tcg_n_regions(size_t tb_size, unsigned max_threads) { #ifdef CONFIG_USER_ONLY return 1; @@ -408,24 +431,25 @@ static size_t tcg_n_regions(size_t tb_size, unsigned max_cpus) /* * It is likely that some vCPUs will translate more code than others, - * so we first try to set more regions than max_cpus, with those regions + * so we first try to set more regions than threads, with those regions * being of reasonable size. If that's not possible we make do by evenly * dividing the code_gen_buffer among the vCPUs. + * + * Use a single region if all we have is one vCPU thread. */ - /* Use a single region if all we have is one vCPU thread */ - if (max_cpus == 1 || !qemu_tcg_mttcg_enabled()) { + if (max_threads == 1) { return 1; } /* - * Try to have more regions than max_cpus, with each region being >= 2 MB. + * Try to have more regions than threads, with each region being >= 2 MB. * If we can't, then just allocate one region per vCPU thread. */ n_regions = tb_size / (2 * MiB); - if (n_regions <= max_cpus) { - return max_cpus; + if (n_regions <= max_threads) { + return max_threads; } - return MIN(n_regions, max_cpus * 8); + return MIN(n_regions, max_threads * 8); #endif } @@ -505,14 +529,6 @@ static int alloc_code_gen_buffer(size_t tb_size, int splitwx, Error **errp) return PROT_READ | PROT_WRITE; } #elif defined(_WIN32) -/* - * Local source-level compatibility with Unix. - * Used by tcg_region_init below. - */ -#define PROT_READ 1 -#define PROT_WRITE 2 -#define PROT_EXEC 4 - static int alloc_code_gen_buffer(size_t size, int splitwx, Error **errp) { void *buf; @@ -567,9 +583,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); @@ -579,12 +597,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); } @@ -642,7 +656,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); @@ -718,11 +732,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 - * 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 - * qemu_tcg_mttcg_enabled(). + * In system-mode the number of TCG threads is bounded by max_threads, * * In user-mode we use a single region. Having multiple regions in user-mode * is not supported, because the number of vCPU threads (recall that each thread @@ -734,9 +744,9 @@ 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) +void tcg_region_init(size_t tb_size, int splitwx, unsigned max_threads) { const size_t page_size = qemu_real_host_page_size(); size_t region_size; @@ -774,7 +784,7 @@ void tcg_region_init(size_t tb_size, int splitwx, unsigned max_cpus) * As a result of this we might end up with a few extra pages at the end of * the buffer; we will assign those to the last region. */ - region.n = tcg_n_regions(tb_size, max_cpus); + region.n = tcg_n_regions(tb_size, max_threads); region_size = tb_size / region.n; region_size = QEMU_ALIGN_DOWN(region_size, page_size); @@ -805,7 +815,7 @@ void tcg_region_init(size_t tb_size, int splitwx, unsigned max_cpus) need_prot = PROT_READ | PROT_WRITE; #ifndef CONFIG_TCG_INTERPRETER if (tcg_splitwx_diff == 0) { - need_prot |= PROT_EXEC; + need_prot |= host_prot_read_exec(); } #endif for (size_t i = 0, n = region.n; i < n; i++) { @@ -820,7 +830,11 @@ void tcg_region_init(size_t tb_size, int splitwx, unsigned max_cpus) } 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 aac5ceee2b6..0fc26d3f98b 100644 --- a/tcg/riscv/tcg-target-con-set.h +++ b/tcg/riscv/tcg-target-con-set.h @@ -10,14 +10,20 @@ * tcg-target-con-str.h; the constraint combination is inclusive or. */ C_O0_I1(r) -C_O0_I2(rZ, r) -C_O0_I2(rZ, rZ) +C_O0_I2(rz, r) +C_O0_I2(r, rz) C_O1_I1(r, r) +C_O1_I2(r, r, r) 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_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 d5c419dff19..c04e15ddfa3 100644 --- a/tcg/riscv/tcg-target-con-str.h +++ b/tcg/riscv/tcg-target-con-str.h @@ -9,13 +9,13 @@ * REGS(letter, register_mask) */ REGS('r', ALL_GENERAL_REGS) +REGS('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('N', TCG_CT_CONST_N12) +CONST('K', TCG_CT_CONST_S5) +CONST('L', TCG_CT_CONST_CMP_VI) CONST('M', TCG_CT_CONST_M12) -CONST('Z', TCG_CT_CONST_ZERO) diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h new file mode 100644 index 00000000000..aef10c2d9d6 --- /dev/null +++ b/tcg/riscv/tcg-target-has.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2018 SiFive, Inc + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +#include "host/cpuinfo.h" + +/* optional instructions */ +#define TCG_TARGET_HAS_extr_i64_i32 1 +#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 + +static inline bool +tcg_target_extract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (type == TCG_TYPE_I64 && ofs + len == 32) { + /* ofs > 0 uses SRLIW; ofs == 0 uses add.uw. */ + return ofs || (cpuinfo & CPUINFO_ZBA); + } + switch (len) { + case 1: + return (cpuinfo & CPUINFO_ZBS) && ofs != 0; + case 16: + return (cpuinfo & CPUINFO_ZBB) && ofs == 0; + } + return false; +} +#define TCG_TARGET_extract_valid tcg_target_extract_valid + +static inline bool +tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (type == TCG_TYPE_I64 && ofs + len == 32) { + return true; + } + return (cpuinfo & CPUINFO_ZBB) && ofs == 0 && (len == 8 || len == 16); +} +#define TCG_TARGET_sextract_valid tcg_target_sextract_valid + +#define TCG_TARGET_deposit_valid(type, ofs, len) 0 + +#endif diff --git a/tcg/riscv/tcg-target-mo.h b/tcg/riscv/tcg-target-mo.h new file mode 100644 index 00000000000..691b5d0da80 --- /dev/null +++ b/tcg/riscv/tcg-target-mo.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2018 SiFive, Inc + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/riscv/tcg-target-opc.h.inc b/tcg/riscv/tcg-target-opc.h.inc new file mode 100644 index 00000000000..b80b39e1e5d --- /dev/null +++ b/tcg/riscv/tcg-target-opc.h.inc @@ -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/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 9be81c1b7b8..1800fd5077e 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -27,43 +27,25 @@ * THE SOFTWARE. */ -#include "../tcg-ldst.c.inc" -#include "../tcg-pool.c.inc" +/* Used for function call generation. */ +#define TCG_REG_CALL_STACK TCG_REG_SP +#define TCG_TARGET_STACK_ALIGN 16 +#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 #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 @@ -100,6 +82,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,20 +105,6 @@ static const int tcg_target_call_iarg_regs[] = { TCG_REG_A7, }; -#ifndef have_zbb -bool have_zbb; -#endif -#if defined(__riscv_arch_test) && defined(__riscv_zba) -# define have_zba true -#else -static bool have_zba; -#endif -#if defined(__riscv_arch_test) && defined(__riscv_zicond) -# define have_zicond true -#else -static bool have_zicond; -#endif - static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) { tcg_debug_assert(kind == TCG_CALL_RET_NORMAL); @@ -134,61 +112,47 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) return TCG_REG_A0 + slot; } -#define TCG_CT_CONST_ZERO 0x100 -#define TCG_CT_CONST_S12 0x200 -#define TCG_CT_CONST_N12 0x400 -#define TCG_CT_CONST_M12 0x800 -#define TCG_CT_CONST_J12 0x1000 +#define TCG_CT_CONST_S12 0x100 +#define TCG_CT_CONST_M12 0x200 +#define TCG_CT_CONST_S5 0x400 +#define TCG_CT_CONST_CMP_VI 0x800 #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 -/* 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; - } - /* - * 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; - } - return 0; -} - /* * 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, @@ -196,6 +160,7 @@ typedef enum { OPC_ANDI = 0x7013, OPC_AUIPC = 0x17, OPC_BEQ = 0x63, + OPC_BEXTI = 0x48005013, OPC_BGE = 0x5063, OPC_BGEU = 0x7063, OPC_BLT = 0x4063, @@ -284,8 +249,182 @@ typedef enum { /* 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 (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, +/- matching: [-0x7ff, 0x7ff]. + * Used by 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; + } + /* + * 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) */ @@ -376,6 +515,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 */ @@ -488,6 +666,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 */ @@ -502,6 +765,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(); } @@ -593,7 +865,7 @@ static void tcg_out_ext8u(TCGContext *s, TCGReg ret, TCGReg arg) static void tcg_out_ext16u(TCGContext *s, TCGReg ret, TCGReg arg) { - if (have_zbb) { + 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); @@ -603,7 +875,7 @@ static void tcg_out_ext16u(TCGContext *s, TCGReg ret, TCGReg arg) static void tcg_out_ext32u(TCGContext *s, TCGReg ret, TCGReg arg) { - if (have_zba) { + 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); @@ -613,7 +885,7 @@ static void tcg_out_ext32u(TCGContext *s, TCGReg ret, TCGReg arg) static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { - if (have_zbb) { + 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); @@ -623,7 +895,7 @@ 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) { - if (have_zbb) { + 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); @@ -694,18 +966,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) { - RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_LW : OPC_LD; - tcg_out_ldst(s, insn, 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) { - RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SW : OPC_SD; - tcg_out_ldst(s, insn, 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, @@ -718,65 +1073,44 @@ static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, return false; } -static void tcg_out_addsub2(TCGContext *s, - TCGReg rl, TCGReg rh, - TCGReg al, TCGReg ah, - TCGArg bl, TCGArg bh, - bool cbl, bool cbh, bool is_sub, bool is32bit) +static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg dst, TCGReg src) { - const RISCVInsn opc_add = is32bit ? OPC_ADDW : OPC_ADD; - const RISCVInsn opc_addi = is32bit ? OPC_ADDIW : OPC_ADDI; - const RISCVInsn opc_sub = is32bit ? OPC_SUBW : OPC_SUB; - TCGReg th = TCG_REG_TMP1; + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vx(s, OPC_VMV_V_X, dst, 0, src); + return true; +} - /* If we have a negative constant such that negating it would - make the high part zero, we can (usually) eliminate one insn. */ - if (cbl && cbh && bh == -1 && bl != 0) { - bl = -bl; - bh = 0; - is_sub = !is_sub; - } +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); +} - /* By operating on the high part first, we get to use the final - carry operation to move back from the temporary. */ - if (!cbh) { - tcg_out_opc_reg(s, (is_sub ? opc_sub : opc_add), th, ah, bh); - } else if (bh != 0 || ah == rl) { - tcg_out_opc_imm(s, opc_addi, th, ah, (is_sub ? -bh : bh)); - } else { - th = ah; - } +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; - /* Note that tcg optimization should eliminate the bl == 0 case. */ - if (is_sub) { - if (cbl) { - tcg_out_opc_imm(s, OPC_SLTIU, TCG_REG_TMP0, al, bl); - tcg_out_opc_imm(s, opc_addi, rl, al, -bl); - } else { - tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_TMP0, al, bl); - tcg_out_opc_reg(s, opc_sub, rl, al, bl); - } - tcg_out_opc_reg(s, opc_sub, rh, th, TCG_REG_TMP0); - } else { - 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 (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_add, rl, al, al); + if (arg >= -16 && arg < 16) { + if (arg == 0 || arg == -1) { + set_vtype_len(s, type); } else { - tcg_out_opc_reg(s, opc_add, rl, al, bl); - tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_TMP0, - rl, (rl == bl ? al : bl)); + set_vtype_len_sew(s, type, vece); } - tcg_out_opc_reg(s, opc_add, rh, th, TCG_REG_TMP0); + 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 void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + tcg_out_reloc(s, s->code_ptr, R_RISCV_JAL, l, 0); + tcg_out_opc_jump(s, OPC_JAL, TCG_REG_ZERO, 0); } static const struct { @@ -795,8 +1129,8 @@ static const struct { [TCG_COND_GTU] = { OPC_BLTU, true } }; -static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, - TCGReg arg2, TCGLabel *l) +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, TCGReg arg2, TCGLabel *l) { RISCVInsn op = tcg_brcond_to_riscv[cond].op; @@ -812,6 +1146,11 @@ static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, tcg_out_opc_branch(s, op, arg1, arg2, 0); } +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rz), + .out_rr = tgen_brcond, +}; + #define SETCOND_INV TCG_TARGET_NB_REGS #define SETCOND_NEZ (SETCOND_INV << 1) #define SETCOND_FLAGS (SETCOND_INV | SETCOND_NEZ) @@ -936,6 +1275,24 @@ static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, } } +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, cond, dest, arg1, arg2, false); +} + +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, cond, dest, arg1, arg2, true); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + static void tcg_out_negsetcond(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg arg1, tcg_target_long arg2, bool c2) { @@ -974,6 +1331,24 @@ static void tcg_out_negsetcond(TCGContext *s, TCGCond cond, TCGReg ret, } } +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_negsetcond(s, cond, dest, arg1, arg2, false); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_negsetcond(s, cond, dest, arg1, arg2, true); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; + static void tcg_out_movcond_zicond(TCGContext *s, TCGReg ret, TCGReg test_ne, int val1, bool c_val1, int val2, bool c_val2) @@ -1071,15 +1446,15 @@ static void tcg_out_movcond_br2(TCGContext *s, TCGCond cond, TCGReg ret, 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) +static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg cmp1, TCGArg cmp2, bool c_cmp2, + TCGArg val1, bool c_val1, + TCGArg val2, bool c_val2) { int tmpflags; TCGReg t; - if (!have_zicond && (!c_cmp2 || cmp2 == 0)) { + if (!(cpuinfo & CPUINFO_ZICOND) && (!c_cmp2 || cmp2 == 0)) { tcg_out_movcond_br2(s, cond, ret, cmp1, cmp2, val1, c_val1, val2, c_val2); return; @@ -1088,7 +1463,7 @@ static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, tmpflags = tcg_out_setcond_int(s, cond, TCG_REG_TMP0, cmp1, cmp2, c_cmp2); t = tmpflags & ~SETCOND_FLAGS; - if (have_zicond) { + if (cpuinfo & CPUINFO_ZICOND) { if (tmpflags & SETCOND_INV) { tcg_out_movcond_zicond(s, ret, t, val2, c_val2, val1, c_val1); } else { @@ -1101,6 +1476,11 @@ static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, } } +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, rI, rM, rM), + .out = tcg_out_movcond, +}; + static void tcg_out_cltz(TCGContext *s, TCGType type, RISCVInsn insn, TCGReg ret, TCGReg src1, int src2, bool c_src2) { @@ -1112,17 +1492,77 @@ static void tcg_out_cltz(TCGContext *s, TCGType type, RISCVInsn insn, * 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, + tcg_out_movcond(s, type, 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) { TCGReg link = tail ? TCG_REG_ZERO : TCG_REG_RA; 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 */ @@ -1148,7 +1588,7 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *arg, tcg_out_call_int(s, arg, false); } -static void tcg_out_mb(TCGContext *s, TCGArg a0) +static void tcg_out_mb(TCGContext *s, unsigned a0) { tcg_insn_unit insn = OPC_FENCE; @@ -1162,7 +1602,7 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) insn |= 0x02100000; } if (a0 & TCG_MO_ST_ST) { - insn |= 0x02200000; + insn |= 0x01100000; } tcg_out32(s, insn); } @@ -1227,8 +1667,8 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) #define MIN_TLB_MASK_TABLE_OFS -(1 << 11) /* - * For softmmu, perform the TLB load and compare. - * For useronly, perform any required alignment tests. + * 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. */ @@ -1245,111 +1685,120 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, false); a_mask = (1u << aa.align) - 1; -#ifdef CONFIG_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; - - 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_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); + 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; - /* - * For aligned accesses, we check the first byte and include the alignment - * bits within the address. For unaligned access, we check that we don't - * cross pages using the address of the last byte of the access. - */ - addr_adj = addr_reg; - if (a_mask < s_mask) { - addr_adj = TCG_REG_TMP0; - tcg_out_opc_imm(s, addr_type == TCG_TYPE_I32 ? OPC_ADDIW : OPC_ADDI, - addr_adj, addr_reg, s_mask - a_mask); - } - compare_mask = s->page_mask | a_mask; - if (compare_mask == sextreg(compare_mask, 0, 12)) { - tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_TMP1, addr_adj, compare_mask); - } else { - tcg_out_movi(s, addr_type, TCG_REG_TMP1, compare_mask); - tcg_out_opc_reg(s, OPC_AND, TCG_REG_TMP1, TCG_REG_TMP1, addr_adj); - } - - /* Load the tlb comparator and the addend. */ - QEMU_BUILD_BUG_ON(HOST_BIG_ENDIAN); - tcg_out_ld(s, addr_type, TCG_REG_TMP0, TCG_REG_TMP2, - is_ld ? offsetof(CPUTLBEntry, addr_read) - : offsetof(CPUTLBEntry, addr_write)); - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP2, TCG_REG_TMP2, - offsetof(CPUTLBEntry, addend)); - - /* Compare masked address with the TLB entry. */ - ldst->label_ptr[0] = s->code_ptr; - tcg_out_opc_branch(s, OPC_BNE, TCG_REG_TMP0, TCG_REG_TMP1, 0); - - /* TLB Hit - translate address using addend. */ - if (addr_type != TCG_TYPE_I32) { - tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP0, addr_reg, TCG_REG_TMP2); - } else if (have_zba) { - tcg_out_opc_reg(s, OPC_ADD_UW, TCG_REG_TMP0, addr_reg, TCG_REG_TMP2); - } else { - tcg_out_ext32u(s, TCG_REG_TMP0, addr_reg); - tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP0, TCG_REG_TMP0, TCG_REG_TMP2); - } - *pbase = TCG_REG_TMP0; -#else - TCGReg base; - - if (a_mask) { ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr_reg; + ldst->addr_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); + 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, + 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); + + /* + * 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 = TARGET_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_TMP1, TCG_REG_ZERO, 0); - } + tcg_out_opc_branch(s, OPC_BNE, TCG_REG_TMP0, TCG_REG_TMP1, 0); - if (guest_base != 0) { - base = TCG_REG_TMP0; + /* TLB Hit - translate address using addend. */ if (addr_type != TCG_TYPE_I32) { - tcg_out_opc_reg(s, OPC_ADD, base, addr_reg, TCG_GUEST_BASE_REG); - } else if (have_zba) { - tcg_out_opc_reg(s, OPC_ADD_UW, base, addr_reg, TCG_GUEST_BASE_REG); + 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, base, addr_reg); - tcg_out_opc_reg(s, OPC_ADD, base, base, TCG_GUEST_BASE_REG); + 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); } - } else if (addr_type != TCG_TYPE_I32) { - base = addr_reg; + *pbase = TCG_REG_TMP0; } else { - base = TCG_REG_TMP0; - tcg_out_ext32u(s, base, addr_reg); - } - *pbase = base; -#endif + TCGReg base; - return ldst; -} + if (a_mask) { + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addr_reg = addr_reg; -static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg val, - TCGReg base, MemOp opc, TCGType type) + 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; + } + + return ldst; +} + +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); @@ -1384,22 +1833,31 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg val, } } -static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; TCGReg base; ldst = prepare_host_addr(s, &base, addr_reg, oi, true); - tcg_out_qemu_ld_direct(s, data_reg, base, get_memop(oi), data_type); + tcg_out_qemu_ld_direct(s, data_reg, base, get_memop(oi), type); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = data_reg; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg val, TCGReg base, MemOp opc) { @@ -1424,8 +1882,8 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg val, } } -static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; TCGReg base; @@ -1434,12 +1892,21 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, tcg_out_qemu_st_direct(s, data_reg, base, get_memop(oi)); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = data_reg; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out = tgen_qemu_st, +}; + +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = C_NotImplemented, +}; + static const tcg_insn_unit *tb_ret_addr; static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) @@ -1466,6 +1933,11 @@ static void tcg_out_goto_tb(TCGContext *s, int which) set_jmp_reset_offset(s, which); } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_opc_imm(s, OPC_JALR, TCG_REG_ZERO, a0, 0); +} + void tb_target_set_jmp_target(const TranslationBlock *tb, int n, uintptr_t jmp_rx, uintptr_t jmp_rw) { @@ -1483,553 +1955,960 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, 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]) + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) { - TCGArg a0 = args[0]; - TCGArg a1 = args[1]; - TCGArg a2 = args[2]; - int c2 = const_args[2]; + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_ADDW : OPC_ADD; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} - switch (opc) { - case INDEX_op_goto_ptr: - tcg_out_opc_imm(s, OPC_JALR, TCG_REG_ZERO, a0, 0); - break; +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_ADDIW : OPC_ADDI; + tcg_out_opc_imm(s, insn, a0, a1, a2); +} - case INDEX_op_br: - tcg_out_reloc(s, s->code_ptr, R_RISCV_JAL, arg_label(a0), 0); - tcg_out_opc_jump(s, OPC_JAL, TCG_REG_ZERO, 0); - break; +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - tcg_out_ldst(s, OPC_LBU, a0, a1, a2); - break; - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - tcg_out_ldst(s, OPC_LB, a0, a1, a2); - break; - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - tcg_out_ldst(s, OPC_LHU, a0, a1, a2); - break; - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - tcg_out_ldst(s, OPC_LH, a0, a1, a2); - break; - case INDEX_op_ld32u_i64: - tcg_out_ldst(s, OPC_LWU, a0, a1, a2); - break; - case INDEX_op_ld_i32: - case INDEX_op_ld32s_i64: - tcg_out_ldst(s, OPC_LW, a0, a1, a2); - break; - case INDEX_op_ld_i64: - tcg_out_ldst(s, OPC_LD, a0, a1, a2); - break; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - tcg_out_ldst(s, OPC_SB, a0, a1, a2); - break; - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - tcg_out_ldst(s, OPC_SH, a0, a1, a2); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - tcg_out_ldst(s, OPC_SW, a0, a1, a2); - break; - case INDEX_op_st_i64: - tcg_out_ldst(s, OPC_SD, a0, a1, a2); - break; +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_add_i32: - if (c2) { - tcg_out_opc_imm(s, OPC_ADDIW, a0, a1, a2); - } else { - tcg_out_opc_reg(s, OPC_ADDW, a0, a1, a2); - } - break; - case INDEX_op_add_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_ADDI, a0, a1, a2); - } else { - tcg_out_opc_reg(s, OPC_ADD, a0, a1, a2); - } - break; +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_NotImplemented, +}; - case INDEX_op_sub_i32: - if (c2) { - tcg_out_opc_imm(s, OPC_ADDIW, a0, a1, -a2); - } else { - tcg_out_opc_reg(s, OPC_SUBW, a0, a1, a2); - } - break; - case INDEX_op_sub_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_ADDI, a0, a1, -a2); - } else { - tcg_out_opc_reg(s, OPC_SUB, a0, a1, a2); - } - break; +static void tcg_out_set_carry(TCGContext *s) +{ + g_assert_not_reached(); +} - case INDEX_op_and_i32: - case INDEX_op_and_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_ANDI, a0, a1, a2); - } else { - tcg_out_opc_reg(s, OPC_AND, a0, a1, a2); - } - break; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_AND, a0, a1, a2); +} - case INDEX_op_or_i32: - case INDEX_op_or_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_ORI, a0, a1, a2); - } else { - tcg_out_opc_reg(s, OPC_OR, a0, a1, a2); - } - break; +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_imm(s, OPC_ANDI, a0, a1, a2); +} - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_XORI, a0, a1, a2); - } else { - tcg_out_opc_reg(s, OPC_XOR, a0, a1, a2); - } - break; +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; - 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; +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_ANDN, a0, a1, a2); +} - case INDEX_op_not_i32: - case INDEX_op_not_i64: - tcg_out_opc_imm(s, OPC_XORI, a0, a1, -1); - break; +static TCGConstraintSetIndex cset_zbb_rrr(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_ZBB ? C_O1_I2(r, r, r) : C_NotImplemented; +} - case INDEX_op_neg_i32: - tcg_out_opc_reg(s, OPC_SUBW, a0, TCG_REG_ZERO, a1); - break; - case INDEX_op_neg_i64: - tcg_out_opc_reg(s, OPC_SUB, a0, TCG_REG_ZERO, a1); - break; +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_zbb_rrr, + .out_rrr = tgen_andc, +}; - case INDEX_op_mul_i32: - tcg_out_opc_reg(s, OPC_MULW, a0, a1, a2); - break; - case INDEX_op_mul_i64: - tcg_out_opc_reg(s, OPC_MUL, a0, a1, a2); - break; +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_CLZW : OPC_CLZ; + tcg_out_cltz(s, type, insn, a0, a1, a2, false); +} - case INDEX_op_div_i32: - tcg_out_opc_reg(s, OPC_DIVW, a0, a1, a2); - break; - case INDEX_op_div_i64: - tcg_out_opc_reg(s, OPC_DIV, a0, a1, a2); - break; +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_CLZW : OPC_CLZ; + tcg_out_cltz(s, type, insn, a0, a1, a2, true); +} - case INDEX_op_divu_i32: - tcg_out_opc_reg(s, OPC_DIVUW, a0, a1, a2); - break; - case INDEX_op_divu_i64: - tcg_out_opc_reg(s, OPC_DIVU, a0, a1, a2); - break; +static TCGConstraintSetIndex cset_clzctz(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_ZBB ? C_N1_I2(r, r, rM) : C_NotImplemented; +} - case INDEX_op_rem_i32: - tcg_out_opc_reg(s, OPC_REMW, a0, a1, a2); - break; - case INDEX_op_rem_i64: - tcg_out_opc_reg(s, OPC_REM, a0, a1, a2); - break; +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_clzctz, + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; - case INDEX_op_remu_i32: - tcg_out_opc_reg(s, OPC_REMUW, a0, a1, a2); - break; - case INDEX_op_remu_i64: - tcg_out_opc_reg(s, OPC_REMU, a0, a1, a2); - break; +static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_CPOPW : OPC_CPOP; + tcg_out_opc_imm(s, insn, a0, a1, 0); +} - case INDEX_op_shl_i32: - if (c2) { - tcg_out_opc_imm(s, OPC_SLLIW, a0, a1, a2 & 0x1f); - } else { - tcg_out_opc_reg(s, OPC_SLLW, a0, a1, a2); - } - break; - case INDEX_op_shl_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_SLLI, a0, a1, a2 & 0x3f); - } else { - tcg_out_opc_reg(s, OPC_SLL, a0, a1, a2); - } - break; +static TCGConstraintSetIndex cset_ctpop(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_ZBB ? C_O1_I1(r, r) : C_NotImplemented; +} - case INDEX_op_shr_i32: - if (c2) { - tcg_out_opc_imm(s, OPC_SRLIW, a0, a1, a2 & 0x1f); - } else { - tcg_out_opc_reg(s, OPC_SRLW, a0, a1, a2); - } - break; - case INDEX_op_shr_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_SRLI, a0, a1, a2 & 0x3f); - } else { - tcg_out_opc_reg(s, OPC_SRL, a0, a1, a2); - } - break; +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctpop, + .out_rr = tgen_ctpop, +}; - case INDEX_op_sar_i32: - if (c2) { - tcg_out_opc_imm(s, OPC_SRAIW, a0, a1, a2 & 0x1f); - } else { - tcg_out_opc_reg(s, OPC_SRAW, a0, a1, a2); - } - break; - case INDEX_op_sar_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_SRAI, a0, a1, a2 & 0x3f); - } else { - tcg_out_opc_reg(s, OPC_SRA, a0, a1, a2); - } - break; +static void tgen_ctz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_CTZW : OPC_CTZ; + tcg_out_cltz(s, type, insn, a0, a1, a2, false); +} - 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; +static void tgen_ctzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_CTZW : OPC_CTZ; + tcg_out_cltz(s, type, insn, a0, a1, a2, true); +} - 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; +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_clzctz, + .out_rrr = tgen_ctz, + .out_rri = tgen_ctzi, +}; - 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); +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_DIVW : OPC_DIV; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divs, +}; + +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_DIVUW : OPC_DIVU; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divu, +}; + +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_eqv(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_XNOR, a0, a1, a2); +} + +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_zbb_rrr, + .out_rrr = tgen_eqv, +}; + +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_opc_imm(s, OPC_SRAI, a0, a1, 32); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; + +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_MULW : OPC_MUL; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mul, +}; + +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_NotImplemented, +}; + +static TCGConstraintSetIndex cset_mulh(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_I32 ? C_NotImplemented : C_O1_I2(r, r, r); +} + +static void tgen_mulsh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_MULH, a0, a1, a2); +} + +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mulh, + .out_rrr = tgen_mulsh, +}; + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_muluh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_MULHU, a0, a1, a2); +} + +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mulh, + .out_rrr = tgen_muluh, +}; + +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_OR, a0, a1, a2); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_imm(s, OPC_ORI, a0, a1, a2); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + +static void tgen_orc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_ORN, a0, a1, a2); +} + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_zbb_rrr, + .out_rrr = tgen_orc, +}; + +static void tgen_rems(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_REMW : OPC_REM; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_rems, +}; + +static void tgen_remu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_REMUW : OPC_REMU; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_remu, +}; + +static TCGConstraintSetIndex cset_rot(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_ZBB ? C_O1_I2(r, r, ri) : C_NotImplemented; +} + +static void tgen_rotr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_RORW : OPC_ROR; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_rotri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_RORIW : OPC_RORI; + unsigned mask = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_opc_imm(s, insn, a0, a1, a2 & mask); +} + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_rot, + .out_rrr = tgen_rotr, + .out_rri = tgen_rotri, +}; + +static void tgen_rotl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_ROLW : OPC_ROL; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_rotli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_rotri(s, type, a0, a1, -a2); +} + +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_rot, + .out_rrr = tgen_rotl, + .out_rri = tgen_rotli, +}; + +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SRAW : OPC_SRA; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SRAIW : OPC_SRAI; + unsigned mask = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_opc_imm(s, insn, a0, a1, a2 & mask); +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SLLW : OPC_SLL; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SLLIW : OPC_SLLI; + unsigned mask = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_opc_imm(s, insn, a0, a1, a2 & mask); +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SRLW : OPC_SRL; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SRLIW : OPC_SRLI; + unsigned mask = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_opc_imm(s, insn, a0, a1, a2 & mask); +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SUBW : OPC_SUB; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sub, +}; + +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + g_assert_not_reached(); +} + +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_XOR, a0, a1, a2); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_imm(s, OPC_XORI, a0, a1, a2); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + +static TCGConstraintSetIndex cset_bswap(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_ZBB ? C_O1_I1(r, r) : C_NotImplemented; +} + +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_opc_imm(s, OPC_REV8, a0, a1, 0); + if (flags & TCG_BSWAP_OZ) { + tcg_out_opc_imm(s, OPC_SRLI, a0, a0, 48); + } else { + tcg_out_opc_imm(s, OPC_SRAI, a0, a0, 48); + } +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_bswap, + .out_rr = tgen_bswap16, +}; + +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_opc_imm(s, OPC_REV8, a0, a1, 0); + if (flags & TCG_BSWAP_OZ) { + tcg_out_opc_imm(s, OPC_SRLI, a0, a0, 32); + } else { + tcg_out_opc_imm(s, OPC_SRAI, a0, a0, 32); + } +} + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_bswap, + .out_rr = tgen_bswap32, +}; + +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_opc_imm(s, OPC_REV8, a0, a1, 0); +} + +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_bswap, + .out_rr = tgen_bswap64, +}; + +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_sub(s, type, a0, TCG_REG_ZERO, a1); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_xori(s, type, a0, a1, -1); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 16: + tcg_out_ext16u(s, a0, a1); + return; + case 32: + tcg_out_ext32u(s, a0, a1); + return; } - 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); + } + if (ofs + len == 32) { + tgen_shli(s, TCG_TYPE_I32, a0, a1, ofs); + return; + } + if (len == 1) { + tcg_out_opc_imm(s, OPC_BEXTI, a0, a1, ofs); + return; + } + g_assert_not_reached(); +} + +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; + +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8s(s, type, a0, a1); + return; + case 16: + tcg_out_ext16s(s, type, a0, a1); + return; + case 32: + tcg_out_ext32s(s, a0, a1); + return; } - break; + } else if (ofs + len == 32) { + tgen_sari(s, TCG_TYPE_I32, a0, a1, ofs); + return; + } + g_assert_not_reached(); +} - 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; +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; - 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; +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_NotImplemented, +}; - 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); - break; - case INDEX_op_add2_i64: - tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], - const_args[4], const_args[5], false, false); - break; - case INDEX_op_sub2_i32: - tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], - const_args[4], const_args[5], true, true); - break; - case INDEX_op_sub2_i64: - tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], - const_args[4], const_args[5], true, false); - break; +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LBU, dest, base, offset); +} - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - tcg_out_brcond(s, a2, a0, a1, arg_label(args[3])); - break; +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - tcg_out_setcond(s, args[3], a0, a1, a2, c2); - break; +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LB, dest, base, offset); +} - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: - tcg_out_negsetcond(s, args[3], a0, a1, a2, c2); - break; +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; - 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; +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LHU, dest, base, offset); +} - 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_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_a32_i32: - case INDEX_op_qemu_st_a64_i32: - tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I32); +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LH, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LWU, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LW, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; + +static void tgen_st8_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_SB, data, base, offset); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st8_r, +}; + +static void tgen_st16_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_SH, data, base, offset); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st16_r, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tcg_out_st, +}; + + +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_qemu_st_a32_i64: - case INDEX_op_qemu_st_a64_i64: - tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I64); + case INDEX_op_ld_vec: + tcg_out_ld(s, type, a0, a1, a2); break; - - case INDEX_op_extrh_i64_i32: - tcg_out_opc_imm(s, OPC_SRAI, a0, a1, 32); + case INDEX_op_st_vec: + tcg_out_st(s, type, a0, a1, a2); break; - - case INDEX_op_mulsh_i32: - case INDEX_op_mulsh_i64: - tcg_out_opc_reg(s, OPC_MULH, a0, a1, a2); + 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_muluh_i32: - case INDEX_op_muluh_i64: - tcg_out_opc_reg(s, OPC_MULHU, a0, a1, a2); + 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(); + } +} - case INDEX_op_mb: - tcg_out_mb(s, a0); - break; +void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, + TCGArg a0, ...) +{ + g_assert_not_reached(); +} - 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: +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: - g_assert_not_reached(); + return 0; } } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - - case INDEX_op_ld8u_i32: - case INDEX_op_ld8s_i32: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16s_i32: - case INDEX_op_ld_i32: - case INDEX_op_not_i32: - case INDEX_op_neg_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld_i64: - case INDEX_op_not_i64: - case INDEX_op_neg_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - case INDEX_op_ext32u_i64: - case INDEX_op_extu_i32_i64: - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext32s_i64: - 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: - case INDEX_op_st16_i32: - case INDEX_op_st_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - return C_O0_I2(rZ, r); - - case INDEX_op_add_i32: - case INDEX_op_and_i32: - case INDEX_op_or_i32: - case INDEX_op_xor_i32: - case INDEX_op_add_i64: - 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); - - case INDEX_op_mul_i32: - case INDEX_op_mulsh_i32: - case INDEX_op_muluh_i32: - case INDEX_op_div_i32: - case INDEX_op_divu_i32: - case INDEX_op_rem_i32: - case INDEX_op_remu_i32: - case INDEX_op_mul_i64: - case INDEX_op_mulsh_i64: - case INDEX_op_muluh_i64: - case INDEX_op_div_i64: - case INDEX_op_divu_i64: - case INDEX_op_rem_i64: - case INDEX_op_remu_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_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(); + return C_NotImplemented; } } @@ -2075,10 +2954,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]); @@ -2099,66 +2978,75 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out_opc_imm(s, OPC_JALR, TCG_REG_ZERO, TCG_REG_RA, 0); } -static volatile sig_atomic_t got_sigill; +static void tcg_out_tb_start(TCGContext *s) +{ + init_setting_vtype(s); +} -static void sigill_handler(int signo, siginfo_t *si, void *data) +static bool vtype_check(unsigned vtype) { - /* Skip the faulty instruction */ - ucontext_t *uc = (ucontext_t *)data; - uc->uc_mcontext.__gregs[REG_PC] += 4; + unsigned long tmp; - got_sigill = 1; + /* vsetvl tmp, zero, vtype */ + asm(".insn r 0x57, 7, 0x40, %0, zero, %1" : "=r"(tmp) : "r"(vtype)); + return tmp != 0; } -static void tcg_target_detect_isa(void) +static void probe_frac_lmul_1(TCGType type, MemOp vsew) { -#if !defined(have_zba) || !defined(have_zbb) || !defined(have_zicond) - /* - * TODO: It is expected that this will be determinable via - * linux riscv_hwprobe syscall, not yet merged. - * In the meantime, test via sigill. - */ - - struct sigaction sa_old, sa_new; + 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; - 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); + /* Guaranteed by Zve64x. */ + assert(lmul < 3); -#ifndef have_zba - /* Probe for Zba: add.uw zero,zero,zero. */ - got_sigill = 0; - asm volatile(".insn r 0x3b, 0, 0x04, zero, zero, zero" : : : "memory"); - have_zba = !got_sigill; -#endif + /* + * 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; + } -#ifndef have_zbb - /* Probe for Zba: andn zero,zero,zero. */ - got_sigill = 0; - asm volatile(".insn r 0x33, 7, 0x20, zero, zero, zero" : : : "memory"); - have_zbb = !got_sigill; -#endif + 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); + } +} -#ifndef have_zicond - /* Probe for Zicond: czero.eqz zero,zero,zero. */ - got_sigill = 0; - asm volatile(".insn r 0x33, 5, 0x07, zero, zero, zero" : : : "memory"); - have_zicond = !got_sigill; -#endif +static void probe_frac_lmul(void) +{ + /* Match riscv_lg2_vlenb to TCG_TYPE_V64. */ + QEMU_BUILD_BUG_ON(TCG_TYPE_V64 != 3); - sigaction(SIGILL, &sa_old, NULL); -#endif + 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_detect_isa(); - tcg_target_available_regs[TCG_TYPE_I32] = 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); @@ -2180,6 +3068,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 c1132d178f2..6dc77d944ba 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -26,42 +26,28 @@ #define RISCV_TCG_TARGET_H #define TCG_TARGET_INSN_UNIT_SIZE 4 -#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, @@ -71,100 +57,6 @@ typedef enum { TCG_REG_TMP2 = TCG_REG_T4, } TCGReg; -/* used for function call generation */ -#define TCG_REG_CALL_STACK TCG_REG_SP -#define TCG_TARGET_STACK_ALIGN 16 -#define TCG_TARGET_CALL_STACK_OFFSET 0 -#define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_ARG_I128 TCG_CALL_ARG_NORMAL -#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL - -#if defined(__riscv_arch_test) && defined(__riscv_zbb) -# define have_zbb true -#else -extern bool have_zbb; -#endif - -/* optional instructions */ -#define TCG_TARGET_HAS_movcond_i32 1 -#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 have_zbb -#define TCG_TARGET_HAS_deposit_i32 0 -#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_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_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 have_zbb -#define TCG_TARGET_HAS_bswap32_i32 have_zbb -#define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 -#define TCG_TARGET_HAS_andc_i32 have_zbb -#define TCG_TARGET_HAS_orc_i32 have_zbb -#define TCG_TARGET_HAS_eqv_i32 have_zbb -#define TCG_TARGET_HAS_nand_i32 0 -#define TCG_TARGET_HAS_nor_i32 0 -#define TCG_TARGET_HAS_clz_i32 have_zbb -#define TCG_TARGET_HAS_ctz_i32 have_zbb -#define TCG_TARGET_HAS_ctpop_i32 have_zbb -#define TCG_TARGET_HAS_brcond2 1 -#define TCG_TARGET_HAS_setcond2 1 -#define TCG_TARGET_HAS_qemu_st8_i32 0 - -#define TCG_TARGET_HAS_movcond_i64 1 -#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 have_zbb -#define TCG_TARGET_HAS_deposit_i64 0 -#define TCG_TARGET_HAS_extract_i64 0 -#define TCG_TARGET_HAS_sextract_i64 0 -#define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_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 have_zbb -#define TCG_TARGET_HAS_bswap32_i64 have_zbb -#define TCG_TARGET_HAS_bswap64_i64 have_zbb -#define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_neg_i64 1 -#define TCG_TARGET_HAS_andc_i64 have_zbb -#define TCG_TARGET_HAS_orc_i64 have_zbb -#define TCG_TARGET_HAS_eqv_i64 have_zbb -#define TCG_TARGET_HAS_nand_i64 0 -#define TCG_TARGET_HAS_nor_i64 0 -#define TCG_TARGET_HAS_clz_i64 have_zbb -#define TCG_TARGET_HAS_ctz_i64 have_zbb -#define TCG_TARGET_HAS_ctpop_i64 have_zbb -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 0 -#define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 1 -#define TCG_TARGET_HAS_mulsh_i64 1 - -#define TCG_TARGET_HAS_qemu_ldst_i128 0 - -#define TCG_TARGET_DEFAULT_MO (0) - -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS +#define TCG_REG_ZERO TCG_REG_ZERO #endif diff --git a/tcg/s390x/tcg-target-con-set.h b/tcg/s390x/tcg-target-con-set.h index 9a420374999..f67fd7898ed 100644 --- a/tcg/s390x/tcg-target-con-set.h +++ b/tcg/s390x/tcg-target-con-set.h @@ -15,34 +15,33 @@ C_O0_I1(r) C_O0_I2(r, r) C_O0_I2(r, ri) -C_O0_I2(r, rA) +C_O0_I2(r, rC) C_O0_I2(v, r) C_O0_I3(o, m, 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, r) 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, rA) +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, r, rUV) 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, rI, r) -C_O1_I4(r, r, rA, rI, r) +C_O1_I4(v, v, v, vZ, v) +C_O1_I4(v, v, v, vZM, v) +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, rA, r) diff --git a/tcg/s390x/tcg-target-con-str.h b/tcg/s390x/tcg-target-con-str.h index 25675b449e3..636a38a168a 100644 --- a/tcg/s390x/tcg-target-con-str.h +++ b/tcg/s390x/tcg-target-con-str.h @@ -16,10 +16,13 @@ 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('V', TCG_CT_CONST_N32) CONST('Z', TCG_CT_CONST_ZERO) diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h new file mode 100644 index 00000000000..0aeb5ba01a9 --- /dev/null +++ b/tcg/s390x/tcg-target-has.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2009 Ulrich Hecht + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +/* 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_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 + +extern uint64_t s390_facilities[3]; + +#define HAVE_FACILITY(X) \ + ((s390_facilities[FACILITY_##X / 64] >> (63 - FACILITY_##X % 64)) & 1) + +/* optional instructions */ +#define TCG_TARGET_HAS_extr_i64_i32 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 + +#define TCG_TARGET_HAS_andc_vec 1 +#define TCG_TARGET_HAS_orc_vec HAVE_FACILITY(VECTOR_ENH1) +#define TCG_TARGET_HAS_nand_vec HAVE_FACILITY(VECTOR_ENH1) +#define TCG_TARGET_HAS_nor_vec 1 +#define TCG_TARGET_HAS_eqv_vec HAVE_FACILITY(VECTOR_ENH1) +#define TCG_TARGET_HAS_not_vec 1 +#define TCG_TARGET_HAS_neg_vec 1 +#define TCG_TARGET_HAS_abs_vec 1 +#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 0 +#define TCG_TARGET_HAS_minmax_vec 1 +#define TCG_TARGET_HAS_bitsel_vec 1 +#define TCG_TARGET_HAS_cmpsel_vec 1 +#define TCG_TARGET_HAS_tst_vec 0 + +#define TCG_TARGET_extract_valid(type, ofs, len) 1 +#define TCG_TARGET_deposit_valid(type, ofs, len) 1 + +static inline bool +tcg_target_sextract_valid(TCGType type, unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + case 16: + return true; + case 32: + return type == TCG_TYPE_I64; + } + } + return false; +} +#define TCG_TARGET_sextract_valid tcg_target_sextract_valid + +#endif diff --git a/tcg/s390x/tcg-target-mo.h b/tcg/s390x/tcg-target-mo.h new file mode 100644 index 00000000000..962295ed513 --- /dev/null +++ b/tcg/s390x/tcg-target-mo.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2009 Ulrich Hecht + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) + +#endif diff --git a/tcg/s390x/tcg-target-opc.h.inc b/tcg/s390x/tcg-target-opc.h.inc new file mode 100644 index 00000000000..61237b39cd2 --- /dev/null +++ b/tcg/s390x/tcg-target-opc.h.inc @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2021 Linaro + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. + * + * See the COPYING file in the top-level directory 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. + */ +DEF(s390_vuph_vec, 1, 1, 0, TCG_OPF_VECTOR) +DEF(s390_vupl_vec, 1, 1, 0, TCG_OPF_VECTOR) +DEF(s390_vpks_vec, 1, 2, 0, TCG_OPF_VECTOR) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index ecd8aaf2a13..84a9e73a46e 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -24,17 +24,26 @@ * THE SOFTWARE. */ -#include "../tcg-ldst.c.inc" -#include "../tcg-pool.c.inc" #include "elf.h" +/* Used for function call generation. */ +#define TCG_TARGET_STACK_ALIGN 8 +#define TCG_TARGET_CALL_STACK_OFFSET 160 +#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_CT_CONST_S16 (1 << 8) #define TCG_CT_CONST_S32 (1 << 9) -#define TCG_CT_CONST_S33 (1 << 10) +#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 TCG_CT_CONST_N32 (1 << 17) #define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 16) #define ALL_VECTOR_REGS MAKE_64BIT_MASK(32, 32) @@ -45,10 +54,9 @@ /* A scratch register that may be be used throughout the backend. */ #define TCG_TMP0 TCG_REG_R1 +#define TCG_VEC_TMP0 TCG_REG_V31 -#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 @@ -113,6 +121,9 @@ typedef enum S390Opcode { RI_OILH = 0xa50a, RI_OILL = 0xa50b, RI_TMLL = 0xa701, + RI_TMLH = 0xa700, + RI_TMHL = 0xa703, + RI_TMHH = 0xa702, RIEb_CGRJ = 0xec64, RIEb_CLGRJ = 0xec65, @@ -124,6 +135,9 @@ typedef enum S390Opcode { RIEc_CLGIJ = 0xec7d, RIEc_CLIJ = 0xec7f, + RIEd_ALHSIK = 0xecda, + RIEd_ALGHSIK = 0xecdb, + RIEf_RISBG = 0xec55, RIEg_LOCGHI = 0xec46, @@ -162,6 +176,8 @@ typedef enum S390Opcode { RRE_SLBGR = 0xb989, RRE_XGR = 0xb982, + RRFa_ALRK = 0xb9fa, + RRFa_ALGRK = 0xb9ea, RRFa_MGRK = 0xb9ec, RRFa_MSRKC = 0xb9fd, RRFa_MSGRKC = 0xb9ed, @@ -405,10 +421,15 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #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, @@ -422,9 +443,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, @@ -540,42 +563,88 @@ static bool risbg_mask(uint64_t c) } /* 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) { + uint64_t uval = val; + if (ct & TCG_CT_CONST) { - return 1; + 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(); + } + } + + if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { + return true; + } + if ((ct & TCG_CT_CONST_U32) && uval <= UINT32_MAX) { + return true; + } + if ((ct & TCG_CT_CONST_N32) && -uval <= UINT32_MAX) { + 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; } - /* - * Note that is_const_p16 is a subset of is_const_p32, - * so we don't need both constraints. - */ 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 0; + return false; } /* Emit instructions according to the given instruction format. */ @@ -616,8 +685,16 @@ 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_RIEd(TCGContext *s, S390Opcode op, + TCGReg r1, TCGReg r3, int i2) +{ + tcg_out16(s, (op & 0xff00) | (r1 << 4) | r3); + tcg_out16(s, i2); + tcg_out16(s, op & 0xff); +} + static void tcg_out_insn_RIEg(TCGContext *s, S390Opcode op, TCGReg r1, - int i2, int m3) + int i2, int m3) { tcg_out16(s, (op & 0xff00) | (r1 << 4) | m3); tcg_out32(s, (i2 << 16) | (op & 0xff)); @@ -685,7 +762,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, @@ -740,7 +817,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, @@ -764,7 +841,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, @@ -845,6 +922,9 @@ static const S390Opcode oi_insns[4] = { static const S390Opcode lif_insns[2] = { RIL_LLILF, RIL_LLIHF, }; +static const S390Opcode tm_insns[4] = { + RI_TMLL, RI_TMLH, RI_TMHL, RI_TMHH +}; /* load a register with an immediate value */ static void tcg_out_movi(TCGContext *s, TCGType type, @@ -888,25 +968,32 @@ static void tcg_out_movi(TCGContext *s, TCGType type, 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); + tcg_out_insn(s, RX, LA, ret, ret, TCG_REG_NONE, 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); + if (!s->carry_live) { + /* Load by parts, at most 2 instructions. */ + 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); + } + return; } + + /* Otherwise, stuff it in the constant pool. */ + tcg_out_insn(s, RIL, LGRL, ret, 0); + new_pool_label(s, sval, R_390_PC32DBL, s->code_ptr - 2, 2); } /* Emit a load/store type instruction. Inputs are: @@ -1205,6 +1292,36 @@ static int tgen_cmp2(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, 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)) { @@ -1230,22 +1347,34 @@ static int tgen_cmp2(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, goto exit; } - /* - * Constraints are for a signed 33-bit operand, which is a - * convenient superset of this signed/unsigned test. - */ - 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; + /* 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(); } - - /* Load everything else into a register. */ - tcg_out_movi(s, TCG_TYPE_I64, TCG_TMP0, c2); - c2 = TCG_TMP0; - } - - if (type == TCG_TYPE_I32) { + 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 { @@ -1265,9 +1394,9 @@ static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, 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, - bool c2const, bool neg) +static void tgen_setcond_int(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg c1, TCGArg c2, + bool c2const, bool neg) { int cc; @@ -1359,6 +1488,42 @@ static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, tcg_out_insn(s, RRFc, LOCGR, dest, TCG_TMP0, cc); } +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tgen_setcond_int(s, type, cond, dest, arg1, arg2, false, false); +} + +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tgen_setcond_int(s, type, cond, dest, arg1, arg2, true, false); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rC), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tgen_setcond_int(s, type, cond, dest, arg1, arg2, false, true); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tgen_setcond_int(s, type, cond, dest, arg1, arg2, true, true); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rC), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; + static void tgen_movcond_int(TCGContext *s, TCGType type, TCGReg dest, TCGArg v3, int v3const, TCGReg v4, int cc, int inv_cc) @@ -1399,9 +1564,9 @@ static void tgen_movcond_int(TCGContext *s, TCGType type, TCGReg dest, 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, TCGReg v4) +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond c, + TCGReg dest, TCGReg c1, TCGArg c2, bool c2const, + TCGArg v3, bool v3const, TCGArg v4, bool v4const) { int cc, inv_cc; @@ -1409,66 +1574,96 @@ static void tgen_movcond(TCGContext *s, TCGType type, TCGCond c, TCGReg dest, tgen_movcond_int(s, type, dest, v3, v3const, v4, cc, inv_cc); } -static void tgen_clz(TCGContext *s, TCGReg dest, TCGReg a1, - TCGArg a2, int a2const) -{ - /* Since this sets both R and R+1, we have no choice but to store the - result into R0, allowing R1 == TCG_TMP0 to be clobbered as well. */ - QEMU_BUILD_BUG_ON(TCG_TMP0 != TCG_REG_R1); - tcg_out_insn(s, RRE, FLOGR, TCG_REG_R0, a1); +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, rC, rI, r), + .out = tgen_movcond, +}; - if (a2const && a2 == 64) { - tcg_out_mov(s, TCG_TYPE_I64, dest, TCG_REG_R0); - return; - } +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) +{ + unsigned lsb = (63 - ofs); + unsigned msb = lsb - (len - 1); /* - * Conditions from FLOGR are: - * 2 -> one bit found - * 8 -> no one bit found + * Since we can't support "0Z" as a constraint, we allow a1 in + * any register. Fix things up as if a matching constraint. */ - tgen_movcond_int(s, TCG_TYPE_I64, dest, a2, a2const, TCG_REG_R0, 8, 2); + if (a0 != a1) { + if (a0 == a2) { + tcg_out_mov(s, type, TCG_TMP0, a2); + a2 = TCG_TMP0; + } + tcg_out_mov(s, type, a0, a1); + } + tcg_out_risbg(s, a0, a2, msb, lsb, ofs, false); } -static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +static void tgen_depositz(TCGContext *s, TCGType type, TCGReg a0, TCGReg a2, + unsigned ofs, unsigned len) { - /* With MIE3, and bit 0 of m4 set, we get the complete result. */ - if (HAVE_FACILITY(MISC_INSN_EXT3)) { - if (type == TCG_TYPE_I32) { + unsigned lsb = (63 - ofs); + unsigned msb = lsb - (len - 1); + tcg_out_risbg(s, a0, a2, msb, lsb, ofs, true); +} + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(r, rZ, r), + .out_rrr = tgen_deposit, + .out_rzr = tgen_depositz, +}; + +static void tgen_extract(TCGContext *s, TCGType type, TCGReg dest, + TCGReg src, unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8u(s, dest, src); + return; + case 16: + tcg_out_ext16u(s, dest, src); + return; + case 32: tcg_out_ext32u(s, dest, src); - src = dest; + return; } - tcg_out_insn(s, RRFc, POPCNT, dest, src, 8); - return; } + tcg_out_risbg(s, dest, src, 64 - len, 63, 64 - ofs, 1); +} - /* Without MIE3, each byte gets the count of bits for the byte. */ - tcg_out_insn(s, RRFc, POPCNT, dest, src, 0); +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; - /* 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 { - 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); +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg dest, + TCGReg src, unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8s(s, TCG_TYPE_REG, dest, src); + return; + case 16: + tcg_out_ext16s(s, TCG_TYPE_REG, dest, src); + return; + case 32: + tcg_out_ext32s(s, dest, src); + return; + } } + g_assert_not_reached(); } -static void tgen_deposit(TCGContext *s, TCGReg dest, TCGReg src, - int ofs, int len, int z) -{ - int lsb = (63 - ofs); - int msb = lsb - (len - 1); - tcg_out_risbg(s, dest, src, msb, lsb, ofs, z); -} +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; -static void tgen_extract(TCGContext *s, TCGReg dest, TCGReg src, - int ofs, int len) -{ - tcg_out_risbg(s, dest, src, 64 - len, 63, 64 - ofs, 1); -} +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_NotImplemented, +}; static void tgen_gotoi(TCGContext *s, int cc, const tcg_insn_unit *dest) { @@ -1494,6 +1689,11 @@ static void tgen_branch(TCGContext *s, int cc, TCGLabel *l) } } +static void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + tgen_branch(s, S390_CC_ALWAYS, l); +} + static void tgen_compare_branch(TCGContext *s, S390Opcode opc, int cc, TCGReg r1, TCGReg r2, TCGLabel *l) { @@ -1518,52 +1718,73 @@ static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, TCGArg c2, int c2const, TCGLabel *l) { int cc; - bool is_unsigned = is_unsigned_cond(c); - bool in_range; - S390Opcode opc; - cc = tcg_cond_to_s390_cond[c]; + if (!is_tst_cond(c)) { + bool is_unsigned = is_unsigned_cond(c); + bool in_range; + S390Opcode opc; - if (!c2const) { - opc = (type == TCG_TYPE_I32 - ? (is_unsigned ? RIEb_CLRJ : RIEb_CRJ) - : (is_unsigned ? RIEb_CLGRJ : RIEb_CGRJ)); - tgen_compare_branch(s, opc, cc, r1, c2, l); - return; - } + cc = tcg_cond_to_s390_cond[c]; - /* - * 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 = RIEc_CLIJ; - in_range = (uint32_t)c2 == (uint8_t)c2; - } else { - opc = RIEc_CIJ; - in_range = (int32_t)c2 == (int8_t)c2; + if (!c2const) { + opc = (type == TCG_TYPE_I32 + ? (is_unsigned ? RIEb_CLRJ : RIEb_CRJ) + : (is_unsigned ? RIEb_CLGRJ : RIEb_CGRJ)); + tgen_compare_branch(s, opc, cc, r1, c2, l); + return; } - } else { - if (is_unsigned) { - opc = RIEc_CLGIJ; - in_range = (uint64_t)c2 == (uint8_t)c2; + + /* + * 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 = RIEc_CLIJ; + in_range = (uint32_t)c2 == (uint8_t)c2; + } else { + opc = RIEc_CIJ; + in_range = (int32_t)c2 == (int8_t)c2; + } } else { - opc = RIEc_CGIJ; - in_range = (int64_t)c2 == (int8_t)c2; + if (is_unsigned) { + opc = RIEc_CLGIJ; + in_range = (uint64_t)c2 == (uint8_t)c2; + } else { + opc = RIEc_CGIJ; + in_range = (int64_t)c2 == (int8_t)c2; + } + } + if (in_range) { + tgen_compare_imm_branch(s, opc, cc, r1, c2, l); + return; } - } - if (in_range) { - tgen_compare_imm_branch(s, opc, cc, r1, c2, l); - return; } cc = tgen_cmp(s, type, c, r1, c2, c2const, false); tgen_branch(s, cc, l); } +static void tgen_brcondr(TCGContext *s, TCGType type, TCGCond c, + TCGReg a0, TCGReg a1, TCGLabel *l) +{ + tgen_brcond(s, type, c, a0, a1, false, l); +} + +static void tgen_brcondi(TCGContext *s, TCGType type, TCGCond c, + TCGReg a0, tcg_target_long a1, TCGLabel *l) +{ + tgen_brcond(s, type, c, a0, a1, true, l); +} + +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rC), + .out_rr = tgen_brcondr, + .out_ri = tgen_brcondi, +}; + static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *dest) { ptrdiff_t off = tcg_pcrel_diff(s, dest) >> 1; @@ -1750,8 +1971,8 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) #define MIN_TLB_MASK_TABLE_OFS -(1 << 19) /* - * For softmmu, perform the TLB load and compare. - * For useronly, perform any required alignment tests. + * 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. */ @@ -1768,100 +1989,100 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, h->aa = atom_and_align_for_opc(s, opc, MO_ATOM_IFALIGN, s_bits == MO_128); a_mask = (1 << h->aa.align) - 1; -#ifdef CONFIG_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; + 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; - ldst = new_ldst_label(s); - ldst->is_ld = is_ld; - ldst->oi = oi; - ldst->addrlo_reg = addr_reg; + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addr_reg = addr_reg; - tcg_out_sh64(s, RSY_SRLG, TCG_TMP0, addr_reg, TCG_REG_NONE, - s->page_bits - CPU_TLB_ENTRY_BITS); + tcg_out_sh64(s, RSY_SRLG, TCG_TMP0, addr_reg, TCG_REG_NONE, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - 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_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); - /* - * 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); - } + /* + * 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)TARGET_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); + } - 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); - } + 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); + } - tcg_out16(s, RI_BRC | (S390_CC_NE << 4)); - ldst->label_ptr[0] = s->code_ptr++; + tcg_out16(s, RI_BRC | (S390_CC_NE << 4)); + ldst->label_ptr[0] = s->code_ptr++; - h->index = TCG_TMP0; - tcg_out_insn(s, RXY, LG, h->index, TCG_TMP0, TCG_REG_NONE, - offsetof(CPUTLBEntry, addend)); + h->index = TCG_TMP0; + tcg_out_insn(s, RXY, LG, h->index, TCG_TMP0, TCG_REG_NONE, + offsetof(CPUTLBEntry, addend)); - if (addr_type == TCG_TYPE_I32) { - tcg_out_insn(s, RRE, ALGFR, h->index, addr_reg); - h->base = TCG_REG_NONE; + 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 { - h->base = addr_reg; - } - h->disp = 0; -#else - if (a_mask) { - ldst = new_ldst_label(s); - ldst->is_ld = is_ld; - ldst->oi = oi; - ldst->addrlo_reg = addr_reg; + if (a_mask) { + ldst = new_ldst_label(s); + ldst->is_ld = is_ld; + ldst->oi = oi; + ldst->addr_reg = addr_reg; - /* We are expecting a_bits to max out at 7, much lower than TMLL. */ - tcg_debug_assert(a_mask <= 0xffff); - tcg_out_insn(s, RI, TMLL, addr_reg, a_mask); + tcg_debug_assert(a_mask <= 0xffff); + tcg_out_insn(s, RI, TMLL, addr_reg, a_mask); - tcg_out16(s, RI_BRC | (7 << 4)); /* CC in {1,2,3} */ - ldst->label_ptr[0] = s->code_ptr++; - } + 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; + 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; + } } -#endif return ldst; } -static void tcg_out_qemu_ld(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; @@ -1870,14 +2091,19 @@ static void tcg_out_qemu_ld(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, tcg_out_qemu_ld_direct(s, get_memop(oi), data_reg, h); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = data_reg; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } -static void tcg_out_qemu_st(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; @@ -1886,12 +2112,17 @@ static void tcg_out_qemu_st(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, tcg_out_qemu_st_direct(s, get_memop(oi), data_reg, h); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = data_reg; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(r, r), + .out = tgen_qemu_st, +}; + static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, TCGReg addr_reg, MemOpIdx oi, bool is_ld) { @@ -1920,7 +2151,7 @@ static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, l2 = gen_new_label(); tcg_out_insn(s, RI, TMLL, addr_reg, 15); - tgen_branch(s, 7, l1); /* CC in {1,2,3} */ + tgen_branch(s, S390_TM_NE, l1); } tcg_debug_assert(!need_bswap); @@ -1966,18 +2197,40 @@ static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, } } -static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr_reg, MemOpIdx oi) { - /* 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); - } + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr_reg, oi, true); } -static void tcg_out_goto_tb(TCGContext *s, int which) +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = C_O2_I1(o, m, r), + .out = tgen_qemu_ld2, +}; + +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr_reg, MemOpIdx oi) +{ + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr_reg, oi, false); +} + +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = C_O0_I3(o, m, r), + .out = tgen_qemu_st2, +}; + +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; @@ -1992,6 +2245,11 @@ static void tcg_out_goto_tb(TCGContext *s, int which) set_jmp_reset_offset(s, which); } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, a0); +} + void tb_target_set_jmp_target(const TranslationBlock *tb, int n, uintptr_t jmp_rx, uintptr_t jmp_rw) { @@ -2005,664 +2263,903 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, /* no need to flush icache explicitly */ } -# define OP_32_64(x) \ - case glue(glue(INDEX_op_,x),_i32): \ - case glue(glue(INDEX_op_,x),_i64) -static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) { - S390Opcode op, op2; - TCGArg a0, a1, a2; + if (a0 != a1) { + tcg_out_insn(s, RX, LA, a0, a1, a2, 0); + } else if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, AR, a0, a2); + } else { + tcg_out_insn(s, RRE, AGR, a0, a2); + } +} - switch (opc) { - case INDEX_op_goto_ptr: - a0 = args[0]; - tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, a0); - break; +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a0 == a1) { + if (type == TCG_TYPE_I32) { + if (a2 == (int16_t)a2) { + tcg_out_insn(s, RI, AHI, a0, a2); + } else { + tcg_out_insn(s, RIL, AFI, a0, a2); + } + return; + } + if (a2 == (int16_t)a2) { + tcg_out_insn(s, RI, AGHI, a0, a2); + return; + } + if (a2 == (int32_t)a2) { + tcg_out_insn(s, RIL, AGFI, a0, a2); + return; + } + if (a2 == (uint32_t)a2) { + tcg_out_insn(s, RIL, ALGFI, a0, a2); + return; + } + if (-a2 == (uint32_t)-a2) { + tcg_out_insn(s, RIL, SLGFI, a0, -a2); + return; + } + } + tcg_out_mem(s, RX_LA, RXY_LAY, a0, a1, TCG_REG_NONE, a2); +} - OP_32_64(ld8u): - /* ??? LLC (RXY format) is only present with the extended-immediate - facility, whereas LLGC is always present. */ - tcg_out_mem(s, 0, RXY_LLGC, args[0], args[1], TCG_REG_NONE, args[2]); - break; +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; - OP_32_64(ld8s): - /* ??? LB is no smaller than LGB, so no point to using it. */ - tcg_out_mem(s, 0, RXY_LGB, args[0], args[1], TCG_REG_NONE, args[2]); - break; +static void tgen_addco_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, ALGRK, a0, a1, a2); + } else if (a0 == a1) { + tcg_out_insn(s, RR, ALR, a0, a2); + } else { + tcg_out_insn(s, RRFa, ALRK, a0, a1, a2); + } +} - OP_32_64(ld16u): - /* ??? LLH (RXY format) is only present with the extended-immediate - facility, whereas LLGH is always present. */ - tcg_out_mem(s, 0, RXY_LLGH, args[0], args[1], TCG_REG_NONE, args[2]); - break; +static void tgen_addco_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a2 == (int16_t)a2) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIEd, ALHSIK, a0, a1, a2); + } else { + tcg_out_insn(s, RIEd, ALGHSIK, a0, a1, a2); + } + return; + } - case INDEX_op_ld16s_i32: - tcg_out_mem(s, RX_LH, RXY_LHY, args[0], args[1], TCG_REG_NONE, args[2]); - break; + tcg_out_mov(s, type, a0, a1); + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, ALFI, a0, a2); + } else if (a2 >= 0) { + tcg_out_insn(s, RIL, ALGFI, a0, a2); + } else { + tcg_out_insn(s, RIL, SLGFI, a0, -a2); + } +} - case INDEX_op_ld_i32: - tcg_out_ld(s, TCG_TYPE_I32, args[0], args[1], args[2]); - break; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_O1_I2(r, r, rUV), + .out_rrr = tgen_addco_rrr, + .out_rri = tgen_addco_rri, +}; - OP_32_64(st8): - tcg_out_mem(s, RX_STC, RXY_STCY, args[0], args[1], - TCG_REG_NONE, args[2]); - break; +static void tgen_addcio(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRE, ALCR, a0, a2); + } else { + tcg_out_insn(s, RRE, ALCGR, a0, a2); + } +} - OP_32_64(st16): - tcg_out_mem(s, RX_STH, RXY_STHY, args[0], args[1], - TCG_REG_NONE, args[2]); - break; +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_O1_I2(r, 0, r), + .out_rrr = tgen_addcio, +}; - case INDEX_op_st_i32: - tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); - break; +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_O1_I2(r, 0, r), + .out_rrr = tgen_addcio, +}; - case INDEX_op_add_i32: - a0 = args[0], a1 = args[1], a2 = (int32_t)args[2]; - if (const_args[2]) { - do_addi_32: - if (a0 == a1) { - if (a2 == (int16_t)a2) { - tcg_out_insn(s, RI, AHI, 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) { - tcg_out_insn(s, RR, AR, a0, a2); - } else { - tcg_out_insn(s, RX, LA, a0, a1, a2, 0); - } - break; - case INDEX_op_sub_i32: - a0 = args[0], a1 = args[1], a2 = (int32_t)args[2]; - if (const_args[2]) { - a2 = -a2; - goto do_addi_32; - } else if (a0 == a1) { - tcg_out_insn(s, RR, SR, a0, a2); - } else { - tcg_out_insn(s, RRFa, SRK, a0, a1, a2); - } - break; +static void tcg_out_set_carry(TCGContext *s) +{ + tcg_out_insn(s, RR, SLR, TCG_REG_R0, TCG_REG_R0); /* cc = 2 */ +} - case INDEX_op_and_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, a2); - } else if (a0 == a1) { - tcg_out_insn(s, RR, NR, a0, a2); - } else { - 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, a0, a2); - } else if (a0 == a1) { - tcg_out_insn(s, RR, OR, a0, a2); - } else { - 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); - 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, RRFa, XRK, a0, a1, a2); - } - break; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, NGRK, a0, a1, a2); + } else if (a0 == a1) { + tcg_out_insn(s, RR, NR, a0, a2); + } else { + tcg_out_insn(s, RRFa, NRK, a0, a1, a2); + } +} - 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); +static void tgen_andi_3(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_mov(s, type, a0, a1); + tgen_andi(s, type, a0, a2); +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, rNKR), + .out_rrr = tgen_and, + .out_rri = tgen_andi_3, +}; + +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, NCRK, a0, a1, a2); + } else { + tcg_out_insn(s, RRFa, NCGRK, a0, a1, a2); + } +} + +static TCGConstraintSetIndex cset_misc3_rrr(TCGType type, unsigned flags) +{ + return HAVE_FACILITY(MISC_INSN_EXT3) ? C_O1_I2(r, r, r) : C_NotImplemented; +} + +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_misc3_rrr, + .out_rrr = tgen_andc, +}; + +static void tgen_clz_int(TCGContext *s, TCGReg dest, TCGReg a1, + TCGArg a2, int a2const) +{ + /* + * Since this sets both R and R+1, we have no choice but to store the + * result into R0, allowing R1 == TCG_TMP0 to be clobbered as well. + */ + QEMU_BUILD_BUG_ON(TCG_TMP0 != TCG_REG_R1); + tcg_out_insn(s, RRE, FLOGR, TCG_REG_R0, 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_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_clz_int(s, a0, a1, a2, false); +} + +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_clz_int(s, a0, a1, a2, true); +} + +static TCGConstraintSetIndex cset_clz(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_I64 ? C_O1_I2(r, r, rI) : C_NotImplemented; +} + +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_clz, + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; + +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; } - 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; + tcg_out_insn(s, RRFc, POPCNT, dest, src, 8); + return; + } - 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; + /* Without MIE3, each byte gets the count of bits for the byte. */ + tcg_out_insn(s, RRFc, POPCNT, dest, src, 0); - case INDEX_op_mul_i32: - a0 = args[0], a1 = args[1], a2 = (int32_t)args[2]; - if (const_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, a0, a2); - } - } else if (a0 == a1) { + /* 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 { + 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); + } +} + +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_ctpop, +}; + +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_divs2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a4) +{ + tcg_debug_assert((a1 & 1) == 0); + tcg_debug_assert(a0 == a1 + 1); + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, DR, a1, a4); + } else { + /* + * TODO: Move the sign-extend of the numerator from a2 into a3 + * into the tcg backend, instead of in early expansion. It is + * required for 32-bit DR, but not 64-bit DSGR. + */ + tcg_out_insn(s, RRE, DSGR, a1, a4); + } +} + +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_O2_I3(o, m, 0, 1, r), + .out_rr01r = tgen_divs2, +}; + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_divu2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a4) +{ + tcg_debug_assert((a1 & 1) == 0); + tcg_debug_assert(a0 == a1 + 1); + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRE, DLR, a1, a4); + } else { + tcg_out_insn(s, RRE, DLGR, a1, a4); + } +} + +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_O2_I3(o, m, 0, 1, r), + .out_rr01r = tgen_divu2, +}; + +static void tgen_eqv(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, NXRK, a0, a1, a2); + } else { + tcg_out_insn(s, RRFa, NXGRK, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_misc3_rrr, + .out_rrr = tgen_eqv, +}; + +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_sh64(s, RSY_SRLG, a0, a1, TCG_REG_NONE, 32); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; + +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + if (a0 == a1) { tcg_out_insn(s, RRE, MSR, a0, a2); } else { tcg_out_insn(s, RRFa, MSRKC, a0, a1, a2); } - break; - - case INDEX_op_div2_i32: - 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_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: - op = RS_SLL; - op2 = RSY_SLLK; - do_shift32: - a0 = args[0], a1 = args[1], a2 = (int32_t)args[2]; + } else { if (a0 == a1) { - if (const_args[2]) { - tcg_out_sh32(s, op, a0, TCG_REG_NONE, a2); - } else { - tcg_out_sh32(s, op, a0, a2, 0); - } + tcg_out_insn(s, RRE, MSGR, a0, a2); } else { - /* Using tcg_out_sh64 here for the format; it is a 32-bit shift. */ - if (const_args[2]) { - tcg_out_sh64(s, op2, a0, a1, TCG_REG_NONE, a2); - } else { - tcg_out_sh64(s, op2, a0, a1, a2, 0); - } + tcg_out_insn(s, RRFa, MSGRKC, a0, a1, a2); } - break; - case INDEX_op_shr_i32: - op = RS_SRL; - op2 = RSY_SRLK; - goto do_shift32; - case INDEX_op_sar_i32: - op = RS_SRA; - op2 = RSY_SRAK; - goto do_shift32; + } +} - case INDEX_op_rotl_i32: - /* ??? Using tcg_out_sh64 here for the format; it is a 32-bit rol. */ - if (const_args[2]) { - tcg_out_sh64(s, RSY_RLL, args[0], args[1], TCG_REG_NONE, args[2]); +static void tgen_muli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_mov(s, type, a0, a1); + if (type == TCG_TYPE_I32) { + if (a2 == (int16_t)a2) { + tcg_out_insn(s, RI, MHI, a0, a2); } else { - tcg_out_sh64(s, RSY_RLL, args[0], args[1], args[2], 0); + tcg_out_insn(s, RIL, MSFI, a0, a2); } - break; - case INDEX_op_rotr_i32: - if (const_args[2]) { - tcg_out_sh64(s, RSY_RLL, args[0], args[1], - TCG_REG_NONE, (32 - args[2]) & 31); + } else { + if (a2 == (int16_t)a2) { + tcg_out_insn(s, RI, MGHI, a0, a2); } else { - tcg_out_insn(s, RR, LCR, TCG_TMP0, args[2]); - tcg_out_sh64(s, RSY_RLL, args[0], args[1], TCG_TMP0, 0); + tcg_out_insn(s, RIL, MSGFI, a0, a2); } - break; + } +} + +static TCGConstraintSetIndex cset_mul(TCGType type, unsigned flags) +{ + return (HAVE_FACILITY(MISC_INSN_EXT2) + ? C_O1_I2(r, r, rJ) + : C_O1_I2(r, 0, rJ)); +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mul, + .out_rrr = tgen_mul, + .out_rri = tgen_muli, +}; + +static void tgen_muls2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + tcg_debug_assert((a1 & 1) == 0); + tcg_debug_assert(a0 == a1 + 1); + tcg_out_insn(s, RRFa, MGRK, a1, a2, a3); +} + +static TCGConstraintSetIndex cset_muls2(TCGType type, unsigned flags) +{ + return (type == TCG_TYPE_I64 && HAVE_FACILITY(MISC_INSN_EXT2) + ? C_O2_I2(o, m, r, r) : C_NotImplemented); +} + +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_muls2, + .out_rrrr = tgen_muls2, +}; + +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_mulu2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + tcg_debug_assert(a0 == a2); + tcg_debug_assert((a1 & 1) == 0); + tcg_debug_assert(a0 == a1 + 1); + tcg_out_insn(s, RRE, MLGR, a1, a3); +} + +static TCGConstraintSetIndex cset_mulu2(TCGType type, unsigned flags) +{ + return (type == TCG_TYPE_I64 && HAVE_FACILITY(MISC_INSN_EXT2) + ? C_O2_I2(o, m, 0, r) : C_NotImplemented); +} + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mulu2, + .out_rrrr = tgen_mulu2, +}; + +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_nand(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, NNRK, a0, a1, a2); + } else { + tcg_out_insn(s, RRFa, NNGRK, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_misc3_rrr, + .out_rrr = tgen_nand, +}; + +static void tgen_nor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, NORK, a0, a1, a2); + } else { + tcg_out_insn(s, RRFa, NOGRK, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_misc3_rrr, + .out_rrr = tgen_nor, +}; + +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, OGRK, a0, a1, a2); + } else if (a0 == a1) { + tcg_out_insn(s, RR, OR, a0, a2); + } else { + tcg_out_insn(s, RRFa, ORK, a0, a1, a2); + } +} + +static void tgen_ori_3(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_mov(s, type, a0, a1); + tgen_ori(s, a0, type == TCG_TYPE_I32 ? (uint32_t)a2 : a2); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rK), + .out_rrr = tgen_or, + .out_rri = tgen_ori_3, +}; + +static void tgen_orc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, OCRK, a0, a1, a2); + } else { + tcg_out_insn(s, RRFa, OCGRK, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_misc3_rrr, + .out_rrr = tgen_orc, +}; + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_rotl_int(TCGContext *s, TCGType type, TCGReg dst, + TCGReg src, TCGReg v, tcg_target_long i) +{ + S390Opcode insn = type == TCG_TYPE_I32 ? RSY_RLL : RSY_RLLG; + tcg_out_sh64(s, insn, dst, src, v, i); +} + +static void tgen_rotl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_rotl_int(s, type, a0, a1, a2, 0); +} + +static void tgen_rotli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_rotl_int(s, type, a0, a1, TCG_REG_NONE, a2); +} + +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_rotl, + .out_rri = tgen_rotli, +}; + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_sar_int(TCGContext *s, TCGType type, TCGReg dst, + TCGReg src, TCGReg v, tcg_target_long i) +{ + if (type != TCG_TYPE_I32) { + tcg_out_sh64(s, RSY_SRAG, dst, src, v, i); + } else if (dst == src) { + tcg_out_sh32(s, RS_SRA, dst, v, i); + } else { + tcg_out_sh64(s, RSY_SRAK, dst, src, v, i); + } +} + +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_sar_int(s, type, a0, a1, a2, 0); +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_sar_int(s, type, a0, a1, TCG_REG_NONE, a2); +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + +static void tgen_shl_int(TCGContext *s, TCGType type, TCGReg dst, + TCGReg src, TCGReg v, tcg_target_long i) +{ + if (type != TCG_TYPE_I32) { + tcg_out_sh64(s, RSY_SLLG, dst, src, v, i); + } else if (dst == src) { + tcg_out_sh32(s, RS_SLL, dst, v, i); + } else { + tcg_out_sh64(s, RSY_SLLK, dst, src, v, i); + } +} + +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_shl_int(s, type, a0, a1, a2, 0); +} - case INDEX_op_bswap16_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_shl_int(s, type, a0, a1, TCG_REG_NONE, a2); +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + +static void tgen_shr_int(TCGContext *s, TCGType type, TCGReg dst, + TCGReg src, TCGReg v, tcg_target_long i) +{ + if (type != TCG_TYPE_I32) { + tcg_out_sh64(s, RSY_SRLG, dst, src, v, i); + } else if (dst == src) { + tcg_out_sh32(s, RS_SRL, dst, v, i); + } else { + tcg_out_sh64(s, RSY_SRLK, dst, src, v, i); + } +} + +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_shr_int(s, type, a0, a1, a2, 0); +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_shr_int(s, type, a0, a1, TCG_REG_NONE, a2); +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, SGRK, a0, a1, a2); + } else if (a0 == a1) { + tcg_out_insn(s, RR, SR, a0, a2); + } else { + tcg_out_insn(s, RRFa, SRK, a0, a1, a2); + } +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sub, +}; + +static void tgen_subbo_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, SLGRK, a0, a1, a2); + } else if (a0 == a1) { + tcg_out_insn(s, RR, SLR, a0, a2); + } else { + tcg_out_insn(s, RRFa, SLRK, a0, a1, a2); + } +} + +static void tgen_subbo_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_mov(s, type, a0, a1); + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, SLFI, a0, a2); + } else if (a2 >= 0) { + tcg_out_insn(s, RIL, SLGFI, a0, a2); + } else { + tcg_out_insn(s, RIL, ALGFI, a0, -a2); + } +} + +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_O1_I2(r, r, rUV), + .out_rrr = tgen_subbo_rrr, + .out_rri = tgen_subbo_rri, +}; + +static void tgen_subbio(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRE, SLBR, a0, a2); + } else { + tcg_out_insn(s, RRE, SLBGR, a0, a2); + } +} + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_O1_I2(r, 0, r), + .out_rrr = tgen_subbio, +}; + +#define outop_subbi outop_subbio + +static void tcg_out_set_borrow(TCGContext *s) +{ + tcg_out_insn(s, RR, CLR, TCG_REG_R0, TCG_REG_R0); /* cc = 0 */ +} + +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, XGRK, a0, a1, a2); + } else if (a0 == a1) { + tcg_out_insn(s, RR, XR, a0, a2); + } else { + tcg_out_insn(s, RRFa, XRK, a0, a1, a2); + } +} + +static void tgen_xori_3(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_mov(s, type, a0, a1); + tgen_xori(s, a0, type == TCG_TYPE_I32 ? (uint32_t)a2 : a2); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rK), + .out_rrr = tgen_xor, + .out_rri = tgen_xori_3, +}; + +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + if (type == TCG_TYPE_I32) { tcg_out_insn(s, RRE, LRVR, a0, a1); - if (a2 & TCG_BSWAP_OS) { - tcg_out_sh32(s, RS_SRA, a0, TCG_REG_NONE, 16); - } else { - tcg_out_sh32(s, RS_SRL, a0, TCG_REG_NONE, 16); - } - break; - case INDEX_op_bswap16_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; + tcg_out_sh32(s, (flags & TCG_BSWAP_OS ? RS_SRA : RS_SRL), + a0, TCG_REG_NONE, 16); + } else { tcg_out_insn(s, RRE, LRVGR, a0, a1); - if (a2 & TCG_BSWAP_OS) { - tcg_out_sh64(s, RSY_SRAG, a0, a0, TCG_REG_NONE, 48); - } else { - tcg_out_sh64(s, RSY_SRLG, a0, a0, TCG_REG_NONE, 48); - } - break; + tcg_out_sh64(s, (flags & TCG_BSWAP_OS ? RSY_SRAG : RSY_SRLG), + a0, a0, TCG_REG_NONE, 48); + } +} - case INDEX_op_bswap32_i32: - tcg_out_insn(s, RRE, LRVR, args[0], args[1]); - break; - case INDEX_op_bswap32_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - tcg_out_insn(s, RRE, LRVR, a0, a1); - if (a2 & TCG_BSWAP_OS) { - tcg_out_ext32s(s, a0, a0); - } else if ((a2 & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - tcg_out_ext32u(s, a0, a0); - } - break; +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap16, +}; - case INDEX_op_add2_i32: - if (const_args[4]) { - tcg_out_insn(s, RIL, ALFI, args[0], args[4]); - } else { - tcg_out_insn(s, RR, ALR, args[0], args[4]); - } - tcg_out_insn(s, RRE, ALCR, args[1], args[5]); - break; - case INDEX_op_sub2_i32: - if (const_args[4]) { - tcg_out_insn(s, RIL, SLFI, args[0], args[4]); - } else { - tcg_out_insn(s, RR, SLR, args[0], args[4]); - } - tcg_out_insn(s, RRE, SLBR, args[1], args[5]); - break; +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_insn(s, RRE, LRVR, a0, a1); + if (flags & TCG_BSWAP_OS) { + tcg_out_ext32s(s, a0, a0); + } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + tcg_out_ext32u(s, a0, a0); + } +} - case INDEX_op_br: - tgen_branch(s, S390_CC_ALWAYS, arg_label(args[0])); - break; +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap32, +}; - case INDEX_op_brcond_i32: - tgen_brcond(s, TCG_TYPE_I32, args[2], args[0], - args[1], const_args[1], arg_label(args[3])); - break; - case INDEX_op_setcond_i32: - tgen_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], - 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[4]); - break; +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_insn(s, RRE, LRVGR, a0, a1); +} - 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_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; +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap64, +}; + +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, LCR, a0, a1); + } else { + tcg_out_insn(s, RRE, LCGR, a0, a1); + } +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_nor(s, type, a0, a1, a1); +} + +static TCGConstraintSetIndex cset_not(TCGType type, unsigned flags) +{ + return HAVE_FACILITY(MISC_INSN_EXT3) ? C_O1_I1(r, r) : C_NotImplemented; +} - case INDEX_op_ld16s_i64: - tcg_out_mem(s, 0, RXY_LGH, args[0], args[1], TCG_REG_NONE, args[2]); - break; - case INDEX_op_ld32u_i64: - tcg_out_mem(s, 0, RXY_LLGF, args[0], args[1], TCG_REG_NONE, args[2]); - break; - case INDEX_op_ld32s_i64: - tcg_out_mem(s, 0, RXY_LGF, args[0], args[1], TCG_REG_NONE, args[2]); - break; - case INDEX_op_ld_i64: - tcg_out_ld(s, TCG_TYPE_I64, args[0], args[1], args[2]); - break; +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_not, + .out_rr = tgen_not, +}; - case INDEX_op_st32_i64: - tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); - break; - case INDEX_op_st_i64: - tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); - break; +static void tcg_out_mb(TCGContext *s, unsigned a0) +{ + /* + * The host memory model is quite strong, we simply need to + * serialize the instruction stream. + */ + if (a0 & TCG_MO_ST_LD) { + /* fast-bcr-serialization facility (45) is present */ + tcg_out_insn(s, RR, BCR, 14, 0); + } +} - case INDEX_op_add_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - do_addi_64: - if (a0 == a1) { - if (a2 == (int16_t)a2) { - tcg_out_insn(s, RI, AGHI, 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); - } else if (a0 == a1) { - tcg_out_insn(s, RRE, AGR, a0, a2); - } else { - tcg_out_insn(s, RX, LA, a0, a1, a2, 0); - } - break; - case INDEX_op_sub_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - a2 = -a2; - goto do_addi_64; - } else { - tcg_out_insn(s, RRFa, SGRK, a0, a1, a2); - } - break; +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem(s, 0, RXY_LLGC, dest, base, TCG_REG_NONE, offset); +} - case INDEX_op_and_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, args[0], args[2]); - } else { - 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, a0, a2); - } else { - 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, a0, a2); - } else { - tcg_out_insn(s, RRFa, XGRK, a0, a1, a2); - } - break; +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; - 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; +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem(s, 0, RXY_LGB, dest, base, TCG_REG_NONE, offset); +} - 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; +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; - case INDEX_op_mul_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_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, a0, a2); - } - } else if (a0 == a1) { - tcg_out_insn(s, RRE, MSGR, a0, a2); - } else { - tcg_out_insn(s, RRFa, MSGRKC, a0, a1, a2); - } - break; +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem(s, 0, RXY_LLGH, dest, base, TCG_REG_NONE, offset); +} - case INDEX_op_div2_i64: - /* - * ??? 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_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_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: - op = RSY_SLLG; - do_shift64: - if (const_args[2]) { - tcg_out_sh64(s, op, args[0], args[1], TCG_REG_NONE, args[2]); - } else { - tcg_out_sh64(s, op, args[0], args[1], args[2], 0); - } - break; - case INDEX_op_shr_i64: - op = RSY_SRLG; - goto do_shift64; - case INDEX_op_sar_i64: - op = RSY_SRAG; - goto do_shift64; +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; - case INDEX_op_rotl_i64: - if (const_args[2]) { - tcg_out_sh64(s, RSY_RLLG, args[0], args[1], - TCG_REG_NONE, args[2]); - } else { - tcg_out_sh64(s, RSY_RLLG, args[0], args[1], args[2], 0); - } - break; - case INDEX_op_rotr_i64: - if (const_args[2]) { - tcg_out_sh64(s, RSY_RLLG, args[0], args[1], - TCG_REG_NONE, (64 - args[2]) & 63); - } else { - /* We can use the smaller 32-bit negate because only the - low 6 bits are examined for the rotate. */ - tcg_out_insn(s, RR, LCR, TCG_TMP0, args[2]); - tcg_out_sh64(s, RSY_RLLG, args[0], args[1], TCG_TMP0, 0); - } - break; +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + if (type == TCG_TYPE_I32) { + tcg_out_mem(s, RX_LH, RXY_LHY, dest, base, TCG_REG_NONE, offset); + } else { + tcg_out_mem(s, 0, RXY_LGH, dest, base, TCG_REG_NONE, offset); + } +} - case INDEX_op_add2_i64: - if (const_args[4]) { - if ((int64_t)args[4] >= 0) { - tcg_out_insn(s, RIL, ALGFI, args[0], args[4]); - } else { - tcg_out_insn(s, RIL, SLGFI, args[0], -args[4]); - } - } else { - tcg_out_insn(s, RRE, ALGR, args[0], args[4]); - } - tcg_out_insn(s, RRE, ALCGR, args[1], args[5]); - break; - case INDEX_op_sub2_i64: - if (const_args[4]) { - if ((int64_t)args[4] >= 0) { - tcg_out_insn(s, RIL, SLGFI, args[0], args[4]); - } else { - tcg_out_insn(s, RIL, ALGFI, args[0], -args[4]); - } - } else { - tcg_out_insn(s, RRE, SLGR, args[0], args[4]); - } - tcg_out_insn(s, RRE, SLBGR, args[1], args[5]); - break; +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; - case INDEX_op_brcond_i64: - tgen_brcond(s, TCG_TYPE_I64, args[2], args[0], - args[1], const_args[1], arg_label(args[3])); - break; - case INDEX_op_setcond_i64: - tgen_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], - 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[4]); - break; +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem(s, 0, RXY_LLGF, dest, base, TCG_REG_NONE, offset); +} - OP_32_64(deposit): - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[1]) { - tgen_deposit(s, a0, a2, args[3], args[4], 1); - } else { - /* Since we can't support "0Z" as a constraint, we allow a1 in - any register. Fix things up as if a matching constraint. */ - if (a0 != a1) { - TCGType type = (opc == INDEX_op_deposit_i64); - if (a0 == a2) { - tcg_out_mov(s, type, TCG_TMP0, a2); - a2 = TCG_TMP0; - } - tcg_out_mov(s, type, a0, a1); - } - tgen_deposit(s, a0, a2, args[3], args[4], 0); - } - break; +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; - OP_32_64(extract): - tgen_extract(s, args[0], args[1], args[2], args[3]); - break; +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem(s, 0, RXY_LGF, dest, base, TCG_REG_NONE, offset); +} - case INDEX_op_clz_i64: - tgen_clz(s, args[0], args[1], args[2], const_args[2]); - break; +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; - 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; +static void tgen_st8(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem(s, RX_STC, RXY_STCY, data, base, TCG_REG_NONE, offset); +} - 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) { - /* fast-bcr-serialization facility (45) is present */ - tcg_out_insn(s, RR, BCR, 14, 0); - } - break; +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st8, +}; - 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 tgen_st16(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem(s, RX_STH, RXY_STHY, data, base, TCG_REG_NONE, offset); } +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st16, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tcg_out_st, +}; + + static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, TCGReg dst, TCGReg src) { @@ -2752,6 +3249,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], @@ -2870,19 +3455,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: @@ -2935,9 +3512,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: @@ -2950,71 +3527,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) { @@ -3056,7 +3568,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)); @@ -3064,16 +3576,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); @@ -3094,173 +3596,10 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, va_end(va); } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: - return C_O1_I1(r, r); - - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - return C_O0_I2(r, r); - - case INDEX_op_add_i32: - case INDEX_op_add_i64: - case INDEX_op_shl_i64: - case INDEX_op_shr_i64: - case INDEX_op_sar_i64: - case INDEX_op_rotl_i32: - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i32: - case INDEX_op_rotr_i64: - case INDEX_op_setcond_i32: - case INDEX_op_negsetcond_i32: - return C_O1_I2(r, r, ri); - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i64: - return C_O1_I2(r, r, rA); - - 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_or_i32: - 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 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: - 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(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 C_O1_I2(r, r, ri); - - case INDEX_op_brcond_i32: - return C_O0_I2(r, ri); - case INDEX_op_brcond_i64: - return C_O0_I2(r, rA); - - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: - 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: - 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_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_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 C_O1_I4(r, r, rA, 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(o, m, 0, 1, r); - - case INDEX_op_mulu2_i64: - 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 C_N1_O1_I4(r, r, 0, 1, ri, r); - - case INDEX_op_add2_i64: - case INDEX_op_sub2_i64: - return C_N1_O1_I4(r, r, 0, 1, rA, r); - case INDEX_op_st_vec: return C_O0_I2(v, r); case INDEX_op_ld_vec: @@ -3308,9 +3647,13 @@ 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(); + return C_NotImplemented; } } @@ -3432,6 +3775,7 @@ 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); @@ -3453,12 +3797,10 @@ 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) { + 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]); @@ -3483,6 +3825,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 50e12ef9d6f..0ef5a6d3ddc 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -51,131 +51,4 @@ typedef enum TCGReg { #define TCG_TARGET_NB_REGS 64 -/* 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_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 - -extern uint64_t s390_facilities[3]; - -#define HAVE_FACILITY(X) \ - ((s390_facilities[FACILITY_##X / 64] >> (63 - FACILITY_##X % 64)) & 1) - -/* optional instructions */ -#define TCG_TARGET_HAS_div2_i32 1 -#define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 1 -#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 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_neg_i32 1 -#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 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_extr_i64_i32 0 -#define TCG_TARGET_HAS_qemu_st8_i32 0 - -#define TCG_TARGET_HAS_div2_i64 1 -#define TCG_TARGET_HAS_rot_i64 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 1 -#define TCG_TARGET_HAS_bswap32_i64 1 -#define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_not_i64 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_neg_i64 1 -#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 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 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_v64 HAVE_FACILITY(VECTOR) -#define TCG_TARGET_HAS_v128 HAVE_FACILITY(VECTOR) -#define TCG_TARGET_HAS_v256 0 - -#define TCG_TARGET_HAS_andc_vec 1 -#define TCG_TARGET_HAS_orc_vec HAVE_FACILITY(VECTOR_ENH1) -#define TCG_TARGET_HAS_nand_vec HAVE_FACILITY(VECTOR_ENH1) -#define TCG_TARGET_HAS_nor_vec 1 -#define TCG_TARGET_HAS_eqv_vec HAVE_FACILITY(VECTOR_ENH1) -#define TCG_TARGET_HAS_not_vec 1 -#define TCG_TARGET_HAS_neg_vec 1 -#define TCG_TARGET_HAS_abs_vec 1 -#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 0 -#define TCG_TARGET_HAS_minmax_vec 1 -#define TCG_TARGET_HAS_bitsel_vec 1 -#define TCG_TARGET_HAS_cmpsel_vec 0 - -/* used for function call generation */ -#define TCG_TARGET_STACK_ALIGN 8 -#define TCG_TARGET_CALL_STACK_OFFSET 160 -#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) -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS - #endif diff --git a/tcg/s390x/tcg-target.opc.h b/tcg/s390x/tcg-target.opc.h deleted file mode 100644 index 0eb2350fb39..00000000000 --- a/tcg/s390x/tcg-target.opc.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2021 Linaro - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * (at your option) any later version. - * - * See the COPYING file in the top-level directory 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. - */ -DEF(s390_vuph_vec, 1, 1, 0, IMPLVEC) -DEF(s390_vupl_vec, 1, 1, 0, IMPLVEC) -DEF(s390_vpks_vec, 1, 2, 0, IMPLVEC) diff --git a/tcg/sparc64/tcg-target-con-set.h b/tcg/sparc64/tcg-target-con-set.h index 434bf25072d..1a57adc0e8c 100644 --- a/tcg/sparc64/tcg-target-con-set.h +++ b/tcg/sparc64/tcg-target-con-set.h @@ -10,11 +10,12 @@ * tcg-target-con-str.h; the constraint combination is inclusive or. */ C_O0_I1(r) -C_O0_I2(rZ, r) -C_O0_I2(rZ, rJ) +C_O0_I2(rz, r) +C_O0_I2(r, rJ) C_O1_I1(r, r) C_O1_I2(r, r, r) -C_O1_I2(r, rZ, rJ) -C_O1_I4(r, rZ, rJ, rI, 0) -C_O2_I2(r, r, rZ, rJ) -C_O2_I4(r, r, rZ, rZ, rJ, rJ) +C_O1_I2(r, r, rJ) +C_O1_I2(r, rz, rJ) +C_O1_I2(r, rz, rz) +C_O1_I4(r, r, rJ, rI, 0) +C_O2_I2(r, r, r, r) diff --git a/tcg/sparc64/tcg-target-con-str.h b/tcg/sparc64/tcg-target-con-str.h index 0577ec49420..2f033b3ac24 100644 --- a/tcg/sparc64/tcg-target-con-str.h +++ b/tcg/sparc64/tcg-target-con-str.h @@ -16,4 +16,3 @@ REGS('r', ALL_GENERAL_REGS) */ CONST('I', TCG_CT_CONST_S11) CONST('J', TCG_CT_CONST_S13) -CONST('Z', TCG_CT_CONST_ZERO) diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h new file mode 100644 index 00000000000..b29fd177f69 --- /dev/null +++ b/tcg/sparc64/tcg-target-has.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +/* optional instructions */ +#define TCG_TARGET_HAS_extr_i64_i32 0 +#define TCG_TARGET_HAS_qemu_ldst_i128 0 +#define TCG_TARGET_HAS_tst 1 + +#define TCG_TARGET_extract_valid(type, ofs, len) \ + ((type) == TCG_TYPE_I64 && (ofs) + (len) == 32) + +#define TCG_TARGET_sextract_valid TCG_TARGET_extract_valid + +#define TCG_TARGET_deposit_valid(type, ofs, len) 0 + +#endif diff --git a/tcg/sparc64/tcg-target-mo.h b/tcg/sparc64/tcg-target-mo.h new file mode 100644 index 00000000000..98bfe03b7ac --- /dev/null +++ b/tcg/sparc64/tcg-target-mo.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2008 Fabrice Bellard + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/sparc64/tcg-target-opc.h.inc b/tcg/sparc64/tcg-target-opc.h.inc new file mode 100644 index 00000000000..84e777bfe53 --- /dev/null +++ b/tcg/sparc64/tcg-target-opc.h.inc @@ -0,0 +1 @@ +/* No target specific opcodes. */ diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 81a08bb6c52..5e5c3f1cda5 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -27,8 +27,15 @@ #error "unsupported code generation mode" #endif -#include "../tcg-ldst.c.inc" -#include "../tcg-pool.c.inc" +/* Used for function call generation. */ +#define TCG_REG_CALL_STACK TCG_REG_O6 +#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_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 #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { @@ -69,7 +76,6 @@ static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { #define TCG_CT_CONST_S11 0x100 #define TCG_CT_CONST_S13 0x200 -#define TCG_CT_CONST_ZERO 0x400 #define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) @@ -193,7 +199,9 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #define ARITH_SUB (INSN_OP(2) | INSN_OP3(0x04)) #define ARITH_SUBCC (INSN_OP(2) | INSN_OP3(0x14)) #define ARITH_ADDC (INSN_OP(2) | INSN_OP3(0x08)) +#define ARITH_ADDCCC (INSN_OP(2) | INSN_OP3(0x18)) #define ARITH_SUBC (INSN_OP(2) | INSN_OP3(0x0c)) +#define ARITH_SUBCCC (INSN_OP(2) | INSN_OP3(0x1c)) #define ARITH_UMUL (INSN_OP(2) | INSN_OP3(0x0a)) #define ARITH_SMUL (INSN_OP(2) | INSN_OP3(0x0b)) #define ARITH_UDIV (INSN_OP(2) | INSN_OP3(0x0e)) @@ -202,9 +210,11 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #define ARITH_UDIVX (INSN_OP(2) | INSN_OP3(0x0d)) #define ARITH_SDIVX (INSN_OP(2) | INSN_OP3(0x2d)) #define ARITH_MOVCC (INSN_OP(2) | INSN_OP3(0x2c)) +#define ARITH_POPC (INSN_OP(2) | INSN_OP3(0x2e)) #define ARITH_MOVR (INSN_OP(2) | INSN_OP3(0x2f)) #define ARITH_ADDXC (INSN_OP(2) | INSN_OP3(0x36) | INSN_OPF(0x11)) +#define ARITH_ADDXCCC (INSN_OP(2) | INSN_OP3(0x36) | INSN_OPF(0x13)) #define ARITH_UMULXHI (INSN_OP(2) | INSN_OP3(0x36) | INSN_OPF(0x16)) #define SHIFT_SLL (INSN_OP(2) | INSN_OP3(0x25)) @@ -217,6 +227,7 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #define RDY (INSN_OP(2) | INSN_OP3(0x28) | INSN_RS1(0)) #define WRY (INSN_OP(2) | INSN_OP3(0x30) | INSN_RD(0)) +#define WRCCR (INSN_OP(2) | INSN_OP3(0x30) | INSN_RD(2)) #define JMPL (INSN_OP(2) | INSN_OP3(0x38)) #define RETURN (INSN_OP(2) | INSN_OP3(0x39)) #define SAVE (INSN_OP(2) | INSN_OP3(0x3c)) @@ -264,8 +275,11 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #define STW_LE (STWA | INSN_ASI(ASI_PRIMARY_LITTLE)) #define STX_LE (STXA | INSN_ASI(ASI_PRIMARY_LITTLE)) -#ifndef use_vis3_instructions -bool use_vis3_instructions; +static bool use_popc_instructions; +#if defined(__VIS__) && __VIS__ >= 0x300 +#define use_vis3_instructions 1 +#else +static bool use_vis3_instructions; #endif static bool check_fit_i64(int64_t val, unsigned int bits) @@ -322,7 +336,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; @@ -332,9 +347,7 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) val = (int32_t)val; } - if ((ct & TCG_CT_CONST_ZERO) && val == 0) { - return 1; - } else if ((ct & TCG_CT_CONST_S11) && check_fit_tl(val, 11)) { + if ((ct & TCG_CT_CONST_S11) && check_fit_tl(val, 11)) { return 1; } else if ((ct & TCG_CT_CONST_S13) && check_fit_tl(val, 13)) { return 1; @@ -361,7 +374,7 @@ static void tcg_out_arithi(TCGContext *s, TCGReg rd, TCGReg rs1, } static void tcg_out_arithc(TCGContext *s, TCGReg rd, TCGReg rs1, - int32_t val2, int val2const, int op) + int32_t val2, int val2const, int op) { tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) | (val2const ? INSN_IMM13(val2) : INSN_RS2(val2))); @@ -529,6 +542,11 @@ 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; @@ -586,24 +604,11 @@ static void tcg_out_sety(TCGContext *s, TCGReg rs) tcg_out32(s, WRY | INSN_RS1(TCG_REG_G0) | INSN_RS2(rs)); } -static void tcg_out_div32(TCGContext *s, TCGReg rd, TCGReg rs1, - int32_t val2, int val2const, int uns) -{ - /* Load Y with the sign/zero extension of RS1 to 64-bits. */ - if (uns) { - tcg_out_sety(s, TCG_REG_G0); - } else { - tcg_out_arithi(s, TCG_REG_T1, rs1, 31, SHIFT_SRA); - tcg_out_sety(s, TCG_REG_T1); - } - - tcg_out_arithc(s, rd, rs1, val2, val2const, - 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, @@ -614,7 +619,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, @@ -640,24 +645,31 @@ 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_br(TCGContext *s, TCGLabel *l) { - tcg_out_arithc(s, TCG_REG_G0, c1, c2, c2const, ARITH_SUBCC); + tcg_out_bpcc(s, COND_A, BPCC_PT, l); + tcg_out_nop(s); +} + +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, + 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); } -static void tcg_out_movcc(TCGContext *s, TCGCond cond, int cc, TCGReg ret, +static void tcg_out_movcc(TCGContext *s, int scond, int cc, TCGReg ret, int32_t v1, int v1const) { - tcg_out32(s, ARITH_MOVCC | cc | INSN_RD(ret) - | INSN_RS1(tcg_cond_to_bcond[cond]) + tcg_out32(s, ARITH_MOVCC | cc | INSN_RD(ret) | INSN_RS1(scond) | (v1const ? INSN_IMM11(v1) : INSN_RS2(v1))); } @@ -665,15 +677,16 @@ 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_movcc(s, cond, MOVCC_ICC, ret, v1, v1const); + tcg_out_cmp(s, cond, c1, c2, c2const); + tcg_out_movcc(s, tcg_cond_to_bcond[cond], MOVCC_ICC, ret, v1, v1const); } 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) { @@ -682,19 +695,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))); } @@ -705,17 +717,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_movcc(s, cond, MOVCC_XCC, ret, v1, v1const); + tcg_out_cmp(s, cond, c1, c2, c2const); + tcg_out_movcc(s, tcg_cond_to_bcond[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, bool neg) + TCGReg c1, int32_t c2, bool c2const, bool neg) { /* For 32-bit comparisons, we can play games with ADDC/SUBC. */ switch (cond) { @@ -735,7 +747,16 @@ static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, } c1 = TCG_REG_G0, c2const = 0; cond = (cond == TCG_COND_EQ ? TCG_COND_GEU : TCG_COND_LTU); - break; + 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: @@ -753,13 +774,14 @@ static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, /* FALLTHRU */ default: - tcg_out_cmp(s, c1, c2, c2const); + 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); + tcg_out_movcc(s, tcg_cond_to_bcond[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) { if (neg) { /* 0 - 0 - C = -C = (C ? -1 : 0) */ @@ -780,8 +802,10 @@ static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, } static void tcg_out_setcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg c1, int32_t c2, int c2const, bool neg) + TCGReg c1, int32_t c2, bool c2const, bool neg) { + int rcond; + if (use_vis3_instructions && !neg) { switch (cond) { case TCG_COND_NE: @@ -791,7 +815,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: @@ -801,84 +825,110 @@ 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) { + rcond = tcg_cond_to_rcond[cond]; + if (c2 == 0 && rcond && c1 != ret) { tcg_out_movi_s13(s, ret, 0); - tcg_out_movr(s, cond, ret, c1, neg ? -1 : 1, 1); + tcg_out_movr(s, rcond, ret, c1, neg ? -1 : 1, 1); } else { - tcg_out_cmp(s, c1, c2, c2const); + 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); + tcg_out_movcc(s, tcg_cond_to_bcond[cond], + MOVCC_XCC, ret, neg ? -1 : 1, 1); } } -static void tcg_out_addsub2_i32(TCGContext *s, TCGReg rl, TCGReg rh, - TCGReg al, TCGReg ah, int32_t bl, int blconst, - int32_t bh, int bhconst, int opl, int oph) +static void tcg_out_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, TCGArg arg2, bool const_arg2, + TCGLabel *l) { - TCGReg tmp = TCG_REG_T1; - - /* Note that the low parts are fully consumed before tmp is set. */ - if (rl != ah && (bhconst || rl != bh)) { - tmp = rl; + if (type == TCG_TYPE_I32) { + tcg_out_brcond_i32(s, cond, arg1, arg2, const_arg2, l); + } else { + tcg_out_brcond_i64(s, cond, arg1, arg2, const_arg2, l); } +} - tcg_out_arithc(s, tmp, al, bl, blconst, opl); - tcg_out_arithc(s, rh, ah, bh, bhconst, oph); - tcg_out_mov(s, TCG_TYPE_I32, rl, tmp); +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, TCGReg arg2, TCGLabel *l) +{ + tcg_out_brcond(s, type, cond, arg1, arg2, false, l); } -static void tcg_out_addsub2_i64(TCGContext *s, TCGReg rl, TCGReg rh, - TCGReg al, TCGReg ah, int32_t bl, int blconst, - int32_t bh, int bhconst, bool is_sub) +static void tgen_brcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, tcg_target_long arg2, TCGLabel *l) { - TCGReg tmp = TCG_REG_T1; + tcg_out_brcond(s, type, cond, arg1, arg2, true, l); +} + +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rJ), + .out_rr = tgen_brcond, + .out_ri = tgen_brcondi, +}; - /* Note that the low parts are fully consumed before tmp is set. */ - if (rl != ah && (bhconst || rl != bh)) { - tmp = rl; +static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, + TCGArg c2, bool c2const, bool neg) +{ + if (type == TCG_TYPE_I32) { + tcg_out_setcond_i32(s, cond, ret, c1, c2, c2const, neg); + } else { + tcg_out_setcond_i64(s, cond, ret, c1, c2, c2const, neg); } +} - tcg_out_arithc(s, tmp, al, bl, blconst, is_sub ? ARITH_SUBCC : ARITH_ADDCC); +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, false, false); +} - if (use_vis3_instructions && !is_sub) { - /* Note that ADDXC doesn't accept immediates. */ - if (bhconst && bh != 0) { - tcg_out_movi_s13(s, TCG_REG_T2, bh); - bh = TCG_REG_T2; - } - tcg_out_arith(s, rh, ah, bh, ARITH_ADDXC); - } else if (bh == TCG_REG_G0) { - /* If we have a zero, we can perform the operation in two insns, - with the arithmetic first, and a conditional move into place. */ - if (rh == ah) { - tcg_out_arithi(s, TCG_REG_T2, ah, 1, - is_sub ? ARITH_SUB : ARITH_ADD); - tcg_out_movcc(s, TCG_COND_LTU, MOVCC_XCC, rh, TCG_REG_T2, 0); - } else { - tcg_out_arithi(s, rh, ah, 1, is_sub ? ARITH_SUB : ARITH_ADD); - tcg_out_movcc(s, TCG_COND_GEU, MOVCC_XCC, rh, ah, 0); - } +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, true, false); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, false, true); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, true, true); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; + +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, TCGArg c2, bool c2const, + TCGArg v1, bool v1const, TCGArg v2, bool v2consf) +{ + if (type == TCG_TYPE_I32) { + tcg_out_movcond_i32(s, cond, ret, c1, c2, c2const, v1, v1const); } else { - /* - * Otherwise adjust BH as if there is carry into T2. - * Note that constant BH is constrained to 11 bits for the MOVCC, - * so the adjustment fits 12 bits. - */ - if (bhconst) { - 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); - } - /* ... smoosh T2 back to original BH if carry is clear ... */ - tcg_out_movcc(s, TCG_COND_GEU, MOVCC_XCC, TCG_REG_T2, bh, bhconst); - /* ... and finally perform the arithmetic with the new operand. */ - tcg_out_arith(s, rh, ah, TCG_REG_T2, is_sub ? ARITH_SUB : ARITH_ADD); + tcg_out_movcond_i64(s, cond, ret, c1, c2, c2const, v1, v1const); } - - tcg_out_mov(s, TCG_TYPE_I64, rl, tmp); } +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, rJ, rI, 0), + .out = tgen_movcond, +}; + static void tcg_out_jmpl_const(TCGContext *s, const tcg_insn_unit *dest, bool in_prologue, bool tail_call) { @@ -909,7 +959,7 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *dest, tcg_out_nop(s); } -static void tcg_out_mb(TCGContext *s, TCGArg a0) +static void tcg_out_mb(TCGContext *s, unsigned a0) { /* Note that the TCG memory order constants mirror the Sparc MEMBAR. */ tcg_out32(s, MEMBAR | (a0 & TCG_MO_ALL)); @@ -962,6 +1012,11 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out_movi_s13(s, TCG_REG_O0, 0); } +static void tcg_out_tb_start(TCGContext *s) +{ + /* nothing to do */ +} + static void tcg_out_nop_fill(tcg_insn_unit *p, int count) { int i; @@ -1028,8 +1083,8 @@ bool tcg_target_has_memory_bswap(MemOp memop) #define MIN_TLB_MASK_TABLE_OFS -(1 << 12) /* - * For softmmu, perform the TLB load and compare. - * For useronly, perform any required alignment tests. + * 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. */ @@ -1065,7 +1120,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, /* 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); + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS, SHIFT_SRL); tcg_out_arith(s, TCG_REG_T1, TCG_REG_T1, TCG_REG_T2, ARITH_AND); /* Add the tlb_table pointer, creating the CPUTLBEntry address into R2. */ @@ -1081,19 +1136,19 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, h->base = TCG_REG_T1; /* Mask out the page offset, except for the required alignment. */ - compare_mask = s->page_mask | a_mask; + compare_mask = TARGET_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 { 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_out_cmp(s, TCG_REG_T2, TCG_REG_T3, 0); + tcg_out_cmp(s, TCG_COND_NE, TCG_REG_T2, TCG_REG_T3, 0); ldst = new_ldst_label(s); ldst->is_ld = is_ld; ldst->oi = oi; - ldst->addrlo_reg = addr_reg; + ldst->addr_reg = addr_reg; ldst->label_ptr[0] = s->code_ptr; /* bne,pn %[xi]cc, label0 */ @@ -1106,14 +1161,14 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, * Otherwise, test for at least natural alignment and defer * everything else to the helper functions. */ - if (s_bits != get_alignment_bits(opc)) { + 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->addr_reg = addr_reg; ldst->label_ptr[0] = s->code_ptr; /* bne,pn %icc, label0 */ @@ -1135,8 +1190,8 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, return ldst; } -static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) { static const int ld_opc[(MO_SSIZE | MO_BSWAP) + 1] = { [MO_UB] = LDUB, @@ -1168,14 +1223,23 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr, ld_opc[get_memop(oi) & (MO_BSWAP | MO_SSIZE)]); if (ldst) { - ldst->type = data_type; + ldst->type = 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, TCGType data_type) +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) { static const int st_opc[(MO_SIZE | MO_BSWAP) + 1] = { [MO_UB] = STB, @@ -1198,12 +1262,21 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data, TCGReg addr, st_opc[get_memop(oi) & (MO_BSWAP | MO_SIZE)]); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = data; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out = tgen_qemu_st, +}; + +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) { if (check_fit_ptr(a0, 13)) { @@ -1249,365 +1322,794 @@ static void tcg_out_goto_tb(TCGContext *s, int which) } } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_arithi(s, TCG_REG_G0, a0, 0, JMPL); + tcg_out_mov_delay(s, TCG_REG_TB, a0); +} + 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, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) { - TCGArg a0, a1, a2; - int c, c2; + tcg_out_arith(s, a0, a1, a2, ARITH_ADD); +} - /* Hoist the loads of the most common arguments. */ - a0 = args[0]; - a1 = args[1]; - a2 = args[2]; - c2 = const_args[2]; +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_arithi(s, a0, a1, a2, ARITH_ADD); +} - switch (opc) { - case INDEX_op_goto_ptr: - tcg_out_arithi(s, TCG_REG_G0, a0, 0, JMPL); - 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)); - tcg_out_nop(s); - break; +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; -#define OP_32_64(x) \ - glue(glue(case INDEX_op_, x), _i32): \ - glue(glue(case INDEX_op_, x), _i64) +static void tgen_addco_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_ADDCC); +} - OP_32_64(ld8u): - tcg_out_ldst(s, a0, a1, a2, LDUB); - break; - OP_32_64(ld8s): - tcg_out_ldst(s, a0, a1, a2, LDSB); - break; - OP_32_64(ld16u): - tcg_out_ldst(s, a0, a1, a2, LDUH); - break; - OP_32_64(ld16s): - tcg_out_ldst(s, a0, a1, a2, LDSH); - break; - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - tcg_out_ldst(s, a0, a1, a2, LDUW); - break; - OP_32_64(st8): - tcg_out_ldst(s, a0, a1, a2, STB); - break; - OP_32_64(st16): - tcg_out_ldst(s, a0, a1, a2, STH); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - tcg_out_ldst(s, a0, a1, a2, STW); - break; - OP_32_64(add): - c = ARITH_ADD; - goto gen_arith; - OP_32_64(sub): - c = ARITH_SUB; - goto gen_arith; - OP_32_64(and): - c = ARITH_AND; - goto gen_arith; - OP_32_64(andc): - c = ARITH_ANDN; - goto gen_arith; - OP_32_64(or): - c = ARITH_OR; - goto gen_arith; - OP_32_64(orc): - c = ARITH_ORN; - goto gen_arith; - OP_32_64(xor): - c = ARITH_XOR; - goto gen_arith; - case INDEX_op_shl_i32: - c = SHIFT_SLL; - do_shift32: - /* Limit immediate shift count lest we create an illegal insn. */ - tcg_out_arithc(s, a0, a1, a2 & 31, c2, c); - break; - case INDEX_op_shr_i32: - c = SHIFT_SRL; - goto do_shift32; - case INDEX_op_sar_i32: - c = SHIFT_SRA; - goto do_shift32; - case INDEX_op_mul_i32: - c = ARITH_UMUL; - goto gen_arith; - - OP_32_64(neg): - c = ARITH_SUB; - goto gen_arith1; - OP_32_64(not): - c = ARITH_ORN; - goto gen_arith1; - - case INDEX_op_div_i32: - tcg_out_div32(s, a0, a1, a2, c2, 0); - break; - case INDEX_op_divu_i32: - tcg_out_div32(s, a0, a1, a2, c2, 1); - break; +static void tgen_addco_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_arithi(s, a0, a1, a2, ARITH_ADDCC); +} - case INDEX_op_brcond_i32: - 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, 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]); - break; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_addco_rrr, + .out_rri = tgen_addco_rri, +}; - case INDEX_op_add2_i32: - tcg_out_addsub2_i32(s, args[0], args[1], args[2], args[3], - args[4], const_args[4], args[5], const_args[5], - ARITH_ADDCC, ARITH_ADDC); - break; - case INDEX_op_sub2_i32: - tcg_out_addsub2_i32(s, args[0], args[1], args[2], args[3], - args[4], const_args[4], args[5], const_args[5], - ARITH_SUBCC, ARITH_SUBC); - break; - case INDEX_op_mulu2_i32: - c = ARITH_UMUL; - goto do_mul2; - case INDEX_op_muls2_i32: - c = ARITH_SMUL; - do_mul2: - /* The 32-bit multiply insns produce a full 64-bit result. */ - tcg_out_arithc(s, a0, a2, args[3], const_args[3], c); - tcg_out_arithi(s, a1, a0, 32, SHIFT_SRLX); - break; +static void tgen_addci_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_arith(s, a0, a1, a2, ARITH_ADDC); + } else if (use_vis3_instructions) { + tcg_out_arith(s, a0, a1, a2, ARITH_ADDXC); + } else { + tcg_out_arith(s, TCG_REG_T1, a1, a2, ARITH_ADD); /* for CC */ + tcg_out_arithi(s, a0, TCG_REG_T1, 1, ARITH_ADD); /* for CS */ + /* Select the correct result based on actual carry value. */ + tcg_out_movcc(s, COND_CC, MOVCC_XCC, a0, TCG_REG_T1, 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_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_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; +static void tgen_addci_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_arithi(s, a0, a1, a2, ARITH_ADDC); + return; + } + /* !use_vis3_instructions */ + if (a2 != 0) { + tcg_out_arithi(s, TCG_REG_T1, a1, a2, ARITH_ADD); /* for CC */ + tcg_out_arithi(s, a0, TCG_REG_T1, 1, ARITH_ADD); /* for CS */ + tcg_out_movcc(s, COND_CC, MOVCC_XCC, a0, TCG_REG_T1, false); + } else if (a0 == a1) { + tcg_out_arithi(s, TCG_REG_T1, a1, 1, ARITH_ADD); + tcg_out_movcc(s, COND_CS, MOVCC_XCC, a0, TCG_REG_T1, false); + } else { + tcg_out_arithi(s, a0, a1, 1, ARITH_ADD); + tcg_out_movcc(s, COND_CC, MOVCC_XCC, a0, a1, false); + } +} - case INDEX_op_ld32s_i64: - tcg_out_ldst(s, a0, a1, a2, LDSW); - break; - case INDEX_op_ld_i64: - tcg_out_ldst(s, a0, a1, a2, LDX); - break; - case INDEX_op_st_i64: - tcg_out_ldst(s, a0, a1, a2, STX); - break; - case INDEX_op_shl_i64: - c = SHIFT_SLLX; - do_shift64: - /* Limit immediate shift count lest we create an illegal insn. */ - tcg_out_arithc(s, a0, a1, a2 & 63, c2, c); - break; - case INDEX_op_shr_i64: - c = SHIFT_SRLX; - goto do_shift64; - case INDEX_op_sar_i64: - c = SHIFT_SRAX; - goto do_shift64; - case INDEX_op_mul_i64: - c = ARITH_MULX; - goto gen_arith; - case INDEX_op_div_i64: - c = ARITH_SDIVX; - goto gen_arith; - case INDEX_op_divu_i64: - c = ARITH_UDIVX; - goto gen_arith; - - 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, 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]); - break; - case INDEX_op_add2_i64: - tcg_out_addsub2_i64(s, args[0], args[1], args[2], args[3], args[4], - const_args[4], args[5], const_args[5], false); - break; - case INDEX_op_sub2_i64: - tcg_out_addsub2_i64(s, args[0], args[1], args[2], args[3], args[4], - const_args[4], args[5], const_args[5], true); - break; - case INDEX_op_muluh_i64: - tcg_out_arith(s, args[0], args[1], args[2], ARITH_UMULXHI); - break; +static TCGConstraintSetIndex cset_addci(TCGType type, unsigned flags) +{ + if (use_vis3_instructions && type == TCG_TYPE_I64) { + /* Note that ADDXC doesn't accept immediates. */ + return C_O1_I2(r, rz, rz); + } + return C_O1_I2(r, rz, rJ); +} - gen_arith: - tcg_out_arithc(s, a0, a1, a2, c2, c); - break; +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addci, + .out_rrr = tgen_addci_rrr, + .out_rri = tgen_addci_rri, +}; - gen_arith1: - tcg_out_arithc(s, a0, TCG_REG_G0, a1, const_args[1], c); - break; +/* Copy %xcc.c to %icc.c */ +static void tcg_out_dup_xcc_c(TCGContext *s) +{ + if (use_vis3_instructions) { + tcg_out_arith(s, TCG_REG_T1, TCG_REG_G0, TCG_REG_G0, ARITH_ADDXC); + } else { + tcg_out_movi_s13(s, TCG_REG_T1, 0); + tcg_out_movcc(s, COND_CS, MOVCC_XCC, TCG_REG_T1, 1, true); + } + /* Write carry-in into %icc via {0,1} + -1. */ + tcg_out_arithi(s, TCG_REG_G0, TCG_REG_T1, -1, ARITH_ADDCC); +} - case INDEX_op_mb: - tcg_out_mb(s, a0); - break; +static void tgen_addcio_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + if (use_vis3_instructions) { + tcg_out_arith(s, a0, a1, a2, ARITH_ADDXCCC); + return; + } + tcg_out_dup_xcc_c(s); + } + tcg_out_arith(s, a0, a1, a2, ARITH_ADDCCC); +} - 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: - g_assert_not_reached(); +static void tgen_addcio_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type != TCG_TYPE_I32) { + /* !use_vis3_instructions */ + tcg_out_dup_xcc_c(s); } + tcg_out_arithi(s, a0, a1, a2, ARITH_ADDCCC); +} + +static TCGConstraintSetIndex cset_addcio(TCGType type, unsigned flags) +{ + if (use_vis3_instructions && type == TCG_TYPE_I64) { + /* Note that ADDXCCC doesn't accept immediates. */ + return C_O1_I2(r, rz, rz); + } + return C_O1_I2(r, rz, rJ); +} + +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addcio, + .out_rrr = tgen_addcio_rrr, + .out_rri = tgen_addcio_rri, +}; + +static void tcg_out_set_carry(TCGContext *s) +{ + /* 0x11 -> xcc = nzvC, icc = nzvC */ + tcg_out_arithi(s, 0, TCG_REG_G0, 0x11, WRCCR); +} + +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_AND); +} + +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_arithi(s, a0, a1, a2, ARITH_AND); +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; + +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_ANDN); +} + +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_andc, +}; + +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_arith(s, a0, TCG_REG_G0, a1, ARITH_POPC); } -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) -{ - switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: - case INDEX_op_not_i32: - case INDEX_op_not_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: - case INDEX_op_ext_i32_i64: - case INDEX_op_extu_i32_i64: - case INDEX_op_qemu_ld_a32_i32: - case INDEX_op_qemu_ld_a64_i32: - case INDEX_op_qemu_ld_a32_i64: - case INDEX_op_qemu_ld_a64_i64: +static TCGConstraintSetIndex cset_ctpop(TCGType type, unsigned flags) +{ + if (use_popc_instructions && type == TCG_TYPE_I64) { return C_O1_I1(r, r); + } + return C_NotImplemented; +} - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - case INDEX_op_qemu_st_a32_i32: - case INDEX_op_qemu_st_a64_i32: - case INDEX_op_qemu_st_a32_i64: - case INDEX_op_qemu_st_a64_i64: - return C_O0_I2(rZ, r); - - case INDEX_op_add_i32: - case INDEX_op_add_i64: - case INDEX_op_mul_i32: - case INDEX_op_mul_i64: - case INDEX_op_div_i32: - case INDEX_op_div_i64: - case INDEX_op_divu_i32: - case INDEX_op_divu_i64: - case INDEX_op_sub_i32: - case INDEX_op_sub_i64: - case INDEX_op_and_i32: - case INDEX_op_and_i64: - case INDEX_op_andc_i32: - case INDEX_op_andc_i64: - case INDEX_op_or_i32: - case INDEX_op_or_i64: - case INDEX_op_orc_i32: - case INDEX_op_orc_i64: - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: - case INDEX_op_shl_i32: - case INDEX_op_shl_i64: - case INDEX_op_shr_i32: - case INDEX_op_shr_i64: - case INDEX_op_sar_i32: - case INDEX_op_sar_i64: - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: - return C_O1_I2(r, rZ, rJ); - - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - return C_O0_I2(rZ, rJ); - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - return C_O1_I4(r, rZ, rJ, rI, 0); - case INDEX_op_add2_i32: - case INDEX_op_add2_i64: - case INDEX_op_sub2_i32: - case INDEX_op_sub2_i64: - return C_O2_I4(r, r, rZ, rZ, rJ, rJ); - case INDEX_op_mulu2_i32: - case INDEX_op_muls2_i32: - return C_O2_I2(r, r, rZ, rJ); - case INDEX_op_muluh_i64: - return C_O1_I2(r, r, r); +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctpop, + .out_rr = tgen_ctpop, +}; - default: - g_assert_not_reached(); +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_divs_rJ(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGArg a2, bool c2) +{ + uint32_t insn; + + if (type == TCG_TYPE_I32) { + /* Load Y with the sign extension of a1 to 64-bits. */ + tcg_out_arithi(s, TCG_REG_T1, a1, 31, SHIFT_SRA); + tcg_out_sety(s, TCG_REG_T1); + insn = ARITH_SDIV; + } else { + insn = ARITH_SDIVX; } + tcg_out_arithc(s, a0, a1, a2, c2, insn); +} + +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_divs_rJ(s, type, a0, a1, a2, false); +} + +static void tgen_divsi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_divs_rJ(s, type, a0, a1, a2, true); +} + +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_divs, + .out_rri = tgen_divsi, +}; + +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_divu_rJ(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGArg a2, bool c2) +{ + uint32_t insn; + + if (type == TCG_TYPE_I32) { + /* Load Y with the zero extension to 64-bits. */ + tcg_out_sety(s, TCG_REG_G0); + insn = ARITH_UDIV; + } else { + insn = ARITH_UDIVX; + } + tcg_out_arithc(s, a0, a1, a2, c2, insn); +} + +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_divu_rJ(s, type, a0, a1, a2, false); +} + +static void tgen_divui(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_divu_rJ(s, type, a0, a1, a2, true); +} + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_divu, + .out_rri = tgen_divui, +}; + +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_arithi(s, a0, a1, 32, SHIFT_SRLX); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; + +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? ARITH_UMUL : ARITH_MULX; + tcg_out_arith(s, a0, a1, a2, insn); +} + +static void tgen_muli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? ARITH_UMUL : ARITH_MULX; + tcg_out_arithi(s, a0, a1, a2, insn); +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_mul, + .out_rri = tgen_muli, +}; + +/* + * The 32-bit multiply insns produce a full 64-bit result. + * Supporting 32-bit mul[us]2 opcodes avoids sign/zero-extensions + * before the actual multiply; we only need extract the high part + * into the separate operand. + */ +static TCGConstraintSetIndex cset_mul2(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_I32 ? C_O2_I2(r, r, r, r) : C_NotImplemented; +} + +static void tgen_muls2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + tcg_out_arith(s, a0, a2, a3, ARITH_SMUL); + tcg_out_arithi(s, a1, a0, 32, SHIFT_SRLX); +} + +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mul2, + .out_rrrr = tgen_muls2, +}; + +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_mulu2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + tcg_out_arith(s, a0, a2, a3, ARITH_UMUL); + tcg_out_arithi(s, a1, a0, 32, SHIFT_SRLX); +} + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mul2, + .out_rrrr = tgen_mulu2, +}; + +static void tgen_muluh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_UMULXHI); +} + +static TCGConstraintSetIndex cset_muluh(TCGType type, unsigned flags) +{ + return (type == TCG_TYPE_I64 && use_vis3_instructions + ? C_O1_I2(r, r, r) : C_NotImplemented); +} + +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_muluh, + .out_rrr = tgen_muluh, +}; + +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_OR); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_arithi(s, a0, a1, a2, ARITH_OR); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + +static void tgen_orc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_ORN); +} + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_orc, +}; + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SHIFT_SRA : SHIFT_SRAX; + tcg_out_arith(s, a0, a1, a2, insn); +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SHIFT_SRA : SHIFT_SRAX; + uint32_t mask = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_arithi(s, a0, a1, a2 & mask, insn); +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SHIFT_SLL : SHIFT_SLLX; + tcg_out_arith(s, a0, a1, a2, insn); +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SHIFT_SLL : SHIFT_SLLX; + uint32_t mask = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_arithi(s, a0, a1, a2 & mask, insn); +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SHIFT_SRL : SHIFT_SRLX; + tcg_out_arith(s, a0, a1, a2, insn); +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SHIFT_SRL : SHIFT_SRLX; + uint32_t mask = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_arithi(s, a0, a1, a2 & mask, insn); +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_SUB); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sub, +}; + +static void tgen_subbo_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_SUBCC); +} + +static void tgen_subbo_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_arithi(s, a0, a1, a2, ARITH_SUBCC); +} + +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_O1_I2(r, rz, rJ), + .out_rrr = tgen_subbo_rrr, + .out_rri = tgen_subbo_rri, +}; + +static void tgen_subbi_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + /* TODO: OSA 2015 added SUBXC */ + if (type == TCG_TYPE_I32) { + tcg_out_arith(s, a0, a1, a2, ARITH_SUBC); + } else { + tcg_out_arith(s, TCG_REG_T1, a1, a2, ARITH_SUB); /* for CC */ + tcg_out_arithi(s, a0, TCG_REG_T1, 1, ARITH_SUB); /* for CS */ + /* Select the correct result based on actual borrow value. */ + tcg_out_movcc(s, COND_CC, MOVCC_XCC, a0, TCG_REG_T1, false); + } +} + +static void tgen_subbi_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_arithi(s, a0, a1, a2, ARITH_SUBC); + } else if (a2 != 0) { + tcg_out_arithi(s, TCG_REG_T1, a1, a2, ARITH_SUB); /* for CC */ + tcg_out_arithi(s, a0, TCG_REG_T1, 1, ARITH_SUB); /* for CS */ + tcg_out_movcc(s, COND_CC, MOVCC_XCC, a0, TCG_REG_T1, false); + } else if (a0 == a1) { + tcg_out_arithi(s, TCG_REG_T1, a1, 1, ARITH_SUB); + tcg_out_movcc(s, COND_CS, MOVCC_XCC, a0, TCG_REG_T1, false); + } else { + tcg_out_arithi(s, a0, a1, 1, ARITH_SUB); + tcg_out_movcc(s, COND_CC, MOVCC_XCC, a0, a1, false); + } +} + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_O1_I2(r, rz, rJ), + .out_rrr = tgen_subbi_rrr, + .out_rri = tgen_subbi_rri, +}; + +static void tgen_subbio_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + /* TODO: OSA 2015 added SUBXCCC */ + tcg_out_dup_xcc_c(s); + } + tcg_out_arith(s, a0, a1, a2, ARITH_SUBCCC); +} + +static void tgen_subbio_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type != TCG_TYPE_I32) { + tcg_out_dup_xcc_c(s); + } + tcg_out_arithi(s, a0, a1, a2, ARITH_SUBCCC); +} + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_O1_I2(r, rz, rJ), + .out_rrr = tgen_subbio_rrr, + .out_rri = tgen_subbio_rri, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + tcg_out_set_carry(s); /* borrow == carry */ +} + +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_XOR); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_arithi(s, a0, a1, a2, ARITH_XOR); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_sub(s, type, a0, TCG_REG_G0, a1); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_orc(s, type, a0, TCG_REG_G0, a1); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + tcg_debug_assert(ofs + len == 32); + tcg_out_arithi(s, a0, a1, ofs, SHIFT_SRL); +} + +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; + +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + tcg_debug_assert(ofs + len == 32); + tcg_out_arithi(s, a0, a1, ofs, SHIFT_SRA); +} + +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; + +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, dest, base, offset, LDUB); +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, dest, base, offset, LDSB); +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, dest, base, offset, LDUH); +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, dest, base, offset, LDSH); +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, dest, base, offset, LDUW); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, dest, base, offset, LDSW); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; + +static void tgen_st8_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, data, base, offset, STB); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st8_r, +}; + +static void tgen_st16_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, data, base, offset, STH); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st16_r, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tcg_out_st, +}; + + +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) +{ + return C_NotImplemented; } static void tcg_target_init(TCGContext *s) { + unsigned long hwcap = qemu_getauxval(AT_HWCAP); + /* * Only probe for the platform and capabilities if we haven't already * determined maximum values at compile time. */ + use_popc_instructions = (hwcap & HWCAP_SPARC_POPC) != 0; #ifndef use_vis3_instructions - { - unsigned long hwcap = qemu_getauxval(AT_HWCAP); - use_vis3_instructions = (hwcap & HWCAP_SPARC_VIS3) != 0; - } + use_vis3_instructions = (hwcap & HWCAP_SPARC_VIS3) != 0; #endif tcg_target_available_regs[TCG_TYPE_I32] = ALL_GENERAL_REGS; diff --git a/tcg/sparc64/tcg-target.h b/tcg/sparc64/tcg-target.h index 5cfc4b46799..1b9adccd85f 100644 --- a/tcg/sparc64/tcg-target.h +++ b/tcg/sparc64/tcg-target.h @@ -64,99 +64,7 @@ typedef enum { TCG_REG_I7, } TCGReg; -/* used for function call generation */ -#define TCG_REG_CALL_STACK TCG_REG_O6 - -#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_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 -#else -extern bool use_vis3_instructions; -#endif - -/* optional instructions */ -#define TCG_TARGET_HAS_div_i32 1 -#define TCG_TARGET_HAS_rem_i32 0 -#define TCG_TARGET_HAS_rot_i32 0 -#define TCG_TARGET_HAS_ext8s_i32 0 -#define TCG_TARGET_HAS_ext16s_i32 0 -#define TCG_TARGET_HAS_ext8u_i32 0 -#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 -#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_clz_i32 0 -#define TCG_TARGET_HAS_ctz_i32 0 -#define TCG_TARGET_HAS_ctpop_i32 0 -#define TCG_TARGET_HAS_deposit_i32 0 -#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_qemu_st8_i32 0 - -#define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_div_i64 1 -#define TCG_TARGET_HAS_rem_i64 0 -#define TCG_TARGET_HAS_rot_i64 0 -#define TCG_TARGET_HAS_ext8s_i64 0 -#define TCG_TARGET_HAS_ext16s_i64 0 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 0 -#define TCG_TARGET_HAS_ext16u_i64 0 -#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_neg_i64 1 -#define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_andc_i64 1 -#define TCG_TARGET_HAS_orc_i64 1 -#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 0 -#define TCG_TARGET_HAS_ctz_i64 0 -#define TCG_TARGET_HAS_ctpop_i64 0 -#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_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 use_vis3_instructions -#define TCG_TARGET_HAS_mulsh_i64 0 - -#define TCG_TARGET_HAS_qemu_ldst_i128 0 - -#define TCG_AREG0 TCG_REG_I0 - -#define TCG_TARGET_DEFAULT_MO (0) -#define TCG_TARGET_NEED_LDST_LABELS -#define TCG_TARGET_NEED_POOL_LABELS +#define TCG_AREG0 TCG_REG_I0 +#define TCG_REG_ZERO TCG_REG_G0 #endif diff --git a/tcg/tcg-common.c b/tcg/tcg-common.c index 35e7616ae95..e98b3e5fddf 100644 --- a/tcg/tcg-common.c +++ b/tcg/tcg-common.c @@ -24,10 +24,11 @@ #include "qemu/osdep.h" #include "tcg/tcg.h" +#include "tcg-has.h" -TCGOpDef tcg_op_defs[] = { +const TCGOpDef tcg_op_defs[] = { #define DEF(s, oargs, iargs, cargs, flags) \ - { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags, NULL }, + { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags }, #include "tcg/tcg-opc.h" #undef DEF }; diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h new file mode 100644 index 00000000000..2fc0e50d203 --- /dev/null +++ b/tcg/tcg-has.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2024 Linaro, Ltd. + */ + +#ifndef TCG_HAS_H +#define TCG_HAS_H + +#include "tcg-target-has.h" + +#if TCG_TARGET_REG_BITS == 32 +/* Turn some undef macros into false macros. */ +#define TCG_TARGET_HAS_extr_i64_i32 0 +#endif + +#if !defined(TCG_TARGET_HAS_v64) \ + && !defined(TCG_TARGET_HAS_v128) \ + && !defined(TCG_TARGET_HAS_v256) +#define TCG_TARGET_MAYBE_vec 0 +#define TCG_TARGET_HAS_abs_vec 0 +#define TCG_TARGET_HAS_neg_vec 0 +#define TCG_TARGET_HAS_not_vec 0 +#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_roti_vec 0 +#define TCG_TARGET_HAS_rots_vec 0 +#define TCG_TARGET_HAS_rotv_vec 0 +#define TCG_TARGET_HAS_shi_vec 0 +#define TCG_TARGET_HAS_shs_vec 0 +#define TCG_TARGET_HAS_shv_vec 0 +#define TCG_TARGET_HAS_mul_vec 0 +#define TCG_TARGET_HAS_sat_vec 0 +#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 +#ifndef TCG_TARGET_HAS_v64 +#define TCG_TARGET_HAS_v64 0 +#endif +#ifndef TCG_TARGET_HAS_v128 +#define TCG_TARGET_HAS_v128 0 +#endif +#ifndef TCG_TARGET_HAS_v256 +#define TCG_TARGET_HAS_v256 0 +#endif + +#endif diff --git a/tcg/tcg-internal.h b/tcg/tcg-internal.h index 40a69e6e6e2..d6a12afe064 100644 --- a/tcg/tcg-internal.h +++ b/tcg/tcg-internal.h @@ -34,7 +34,7 @@ extern TCGContext **tcg_ctxs; extern unsigned int tcg_cur_ctxs; extern unsigned int tcg_max_ctxs; -void tcg_region_init(size_t tb_size, int splitwx, unsigned max_cpus); +void tcg_region_init(size_t tb_size, int splitwx, unsigned max_threads); bool tcg_region_alloc(TCGContext *s); void tcg_region_initial_alloc(TCGContext *s); void tcg_region_prologue_set(TCGContext *s); @@ -83,4 +83,32 @@ static inline TCGv_i64 TCGV128_HIGH(TCGv_i128 t) 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, TCGType, TCGArg); +TCGOp *tcg_gen_op2(TCGOpcode, TCGType, TCGArg, TCGArg); +TCGOp *tcg_gen_op3(TCGOpcode, TCGType, TCGArg, TCGArg, TCGArg); +TCGOp *tcg_gen_op4(TCGOpcode, TCGType, TCGArg, TCGArg, TCGArg, TCGArg); +TCGOp *tcg_gen_op5(TCGOpcode, TCGType, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); +TCGOp *tcg_gen_op6(TCGOpcode, TCGType, 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); + +TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op, + TCGOpcode, TCGType, unsigned nargs); +TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, + TCGOpcode, TCGType, unsigned nargs); + #endif /* TCG_INTERNAL_H */ diff --git a/tcg/tcg-ldst.c.inc b/tcg/tcg-ldst.c.inc deleted file mode 100644 index ffada04af09..00000000000 --- a/tcg/tcg-ldst.c.inc +++ /dev/null @@ -1,65 +0,0 @@ -/* - * TCG Backend Data: load-store optimization only. - * - * 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. - */ - -/* - * Generate TB finalization at the end of block - */ - -static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l); -static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l); - -static int tcg_out_ldst_finalize(TCGContext *s) -{ - TCGLabelQemuLdst *lb; - - /* qemu_ld/st slow paths */ - QSIMPLEQ_FOREACH(lb, &s->ldst_labels, next) { - if (lb->is_ld - ? !tcg_out_qemu_ld_slow_path(s, lb) - : !tcg_out_qemu_st_slow_path(s, lb)) { - return -2; - } - - /* 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 - generating code without having to check during generation. */ - if (unlikely((void *)s->code_ptr > s->code_gen_highwater)) { - return -1; - } - } - return 0; -} - -/* - * Allocate a new TCGLabelQemuLdst entry. - */ - -static inline TCGLabelQemuLdst *new_ldst_label(TCGContext *s) -{ - TCGLabelQemuLdst *l = tcg_malloc(sizeof(*l)); - - memset(l, 0, sizeof(*l)); - QSIMPLEQ_INSERT_TAIL(&s->ldst_labels, l, next); - - return l; -} diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index e260a07c613..2d184547ba0 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -23,6 +23,7 @@ #include "tcg/tcg-op-common.h" #include "tcg/tcg-op-gvec-common.h" #include "tcg/tcg-gvec-desc.h" +#include "tcg-has.h" #define MAX_UNROLL 4 @@ -56,30 +57,39 @@ static void check_size_align(uint32_t oprsz, uint32_t maxsz, uint32_t ofs) tcg_debug_assert((ofs & max_align) == 0); } -/* Verify vector overlap rules for two operands. */ -static void check_overlap_2(uint32_t d, uint32_t a, uint32_t s) +/* + * Verify vector overlap rules for two operands. + * When dbase and abase are not the same pointer, we cannot check for + * overlap at compile-time, but the runtime restrictions remain. + */ +static void check_overlap_2(TCGv_ptr dbase, uint32_t d, + TCGv_ptr abase, uint32_t a, uint32_t s) { - tcg_debug_assert(d == a || d + s <= a || a + s <= d); + tcg_debug_assert(dbase != abase || d == a || d + s <= a || a + s <= d); } /* Verify vector overlap rules for three operands. */ -static void check_overlap_3(uint32_t d, uint32_t a, uint32_t b, uint32_t s) +static void check_overlap_3(TCGv_ptr dbase, uint32_t d, + TCGv_ptr abase, uint32_t a, + TCGv_ptr bbase, uint32_t b, uint32_t s) { - check_overlap_2(d, a, s); - check_overlap_2(d, b, s); - check_overlap_2(a, b, s); + check_overlap_2(dbase, d, abase, a, s); + check_overlap_2(dbase, d, bbase, b, s); + check_overlap_2(abase, a, bbase, b, s); } /* Verify vector overlap rules for four operands. */ -static void check_overlap_4(uint32_t d, uint32_t a, uint32_t b, - uint32_t c, uint32_t s) +static void check_overlap_4(TCGv_ptr dbase, uint32_t d, + TCGv_ptr abase, uint32_t a, + TCGv_ptr bbase, uint32_t b, + TCGv_ptr cbase, uint32_t c, uint32_t s) { - check_overlap_2(d, a, s); - check_overlap_2(d, b, s); - check_overlap_2(d, c, s); - check_overlap_2(a, b, s); - check_overlap_2(a, c, s); - check_overlap_2(b, c, s); + check_overlap_2(dbase, d, abase, a, s); + check_overlap_2(dbase, d, bbase, b, s); + check_overlap_2(dbase, d, cbase, c, s); + check_overlap_2(abase, a, bbase, b, s); + check_overlap_2(abase, a, cbase, c, s); + check_overlap_2(bbase, b, cbase, c, s); } /* Create a descriptor from components. */ @@ -88,7 +98,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; @@ -110,9 +133,10 @@ uint32_t simd_desc(uint32_t oprsz, uint32_t maxsz, int32_t data) } /* Generate a call to a gvec-style helper with two vector operands. */ -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) +static void expand_2_ool(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz, + int32_t data, gen_helper_gvec_2 *fn) { TCGv_ptr a0, a1; TCGv_i32 desc = tcg_constant_i32(simd_desc(oprsz, maxsz, data)); @@ -120,8 +144,8 @@ void tcg_gen_gvec_2_ool(uint32_t dofs, uint32_t aofs, 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, dbase, dofs); + tcg_gen_addi_ptr(a1, abase, aofs); fn(a0, a1, desc); @@ -129,6 +153,13 @@ void tcg_gen_gvec_2_ool(uint32_t dofs, uint32_t aofs, tcg_temp_free_ptr(a1); } +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) +{ + expand_2_ool(tcg_env, dofs, tcg_env, aofs, oprsz, maxsz, data, fn); +} + /* Generate a call to a gvec-style helper with two vector operands and one scalar operand. */ void tcg_gen_gvec_2i_ool(uint32_t dofs, uint32_t aofs, TCGv_i64 c, @@ -141,8 +172,8 @@ void tcg_gen_gvec_2i_ool(uint32_t dofs, uint32_t aofs, TCGv_i64 c, 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); @@ -151,9 +182,11 @@ void tcg_gen_gvec_2i_ool(uint32_t dofs, uint32_t aofs, TCGv_i64 c, } /* Generate a call to a gvec-style helper with three vector operands. */ -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) +static void expand_3_ool(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz, + int32_t data, gen_helper_gvec_3 *fn) { TCGv_ptr a0, a1, a2; TCGv_i32 desc = tcg_constant_i32(simd_desc(oprsz, maxsz, data)); @@ -162,9 +195,9 @@ void tcg_gen_gvec_3_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, 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, dbase, dofs); + tcg_gen_addi_ptr(a1, abase, aofs); + tcg_gen_addi_ptr(a2, bbase, bofs); fn(a0, a1, a2, desc); @@ -173,6 +206,14 @@ void tcg_gen_gvec_3_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, tcg_temp_free_ptr(a2); } +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) +{ + expand_3_ool(tcg_env, dofs, tcg_env, aofs, tcg_env, bofs, + oprsz, maxsz, data, fn); +} + /* Generate a call to a gvec-style helper with four vector operands. */ void tcg_gen_gvec_4_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, uint32_t oprsz, uint32_t maxsz, @@ -186,10 +227,10 @@ void tcg_gen_gvec_4_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, 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); @@ -213,11 +254,11 @@ void tcg_gen_gvec_5_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, 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); @@ -240,8 +281,8 @@ void tcg_gen_gvec_2_ptr(uint32_t dofs, uint32_t aofs, 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); @@ -262,9 +303,9 @@ void tcg_gen_gvec_3_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, 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); @@ -288,10 +329,10 @@ void tcg_gen_gvec_4_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, 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); @@ -317,11 +358,11 @@ void tcg_gen_gvec_5_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, 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); @@ -366,7 +407,7 @@ static inline bool check_size_impl(uint32_t oprsz, uint32_t lnsz) return q <= MAX_UNROLL; } -static void expand_clr(uint32_t dofs, uint32_t maxsz); +static void expand_clr(TCGv_ptr dbase, uint32_t dofs, uint32_t maxsz); /* Duplicate C as per VECE. */ uint64_t (dup_const)(unsigned vece, uint64_t c) @@ -469,8 +510,8 @@ static TCGType choose_vector_type(const TCGOpcode *list, unsigned vece, return 0; } -static void do_dup_store(TCGType type, uint32_t dofs, uint32_t oprsz, - uint32_t maxsz, TCGv_vec t_vec) +static void do_dup_store(TCGType type, TCGv_ptr dbase, uint32_t dofs, + uint32_t oprsz, uint32_t maxsz, TCGv_vec t_vec) { uint32_t i = 0; @@ -482,7 +523,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, dbase, dofs + i, TCG_TYPE_V64); i += 8; } @@ -494,17 +535,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, dbase, 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, dbase, 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, dbase, dofs + i, TCG_TYPE_V64); } break; default: @@ -512,17 +553,18 @@ static void do_dup_store(TCGType type, uint32_t dofs, uint32_t oprsz, } if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(dbase, dofs + oprsz, maxsz - oprsz); } } -/* Set OPRSZ bytes at DOFS to replications of IN_32, IN_64 or IN_C. +/* + * Set OPRSZ bytes at DBASE + DOFS to replications of IN_32, IN_64 or IN_C. * Only one of IN_32 or IN_64 may be set; * IN_C is used if IN_32 and IN_64 are unset. */ -static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, - uint32_t maxsz, TCGv_i32 in_32, TCGv_i64 in_64, - uint64_t in_c) +static void do_dup(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + uint32_t oprsz, uint32_t maxsz, + TCGv_i32 in_32, TCGv_i64 in_64, uint64_t in_c) { TCGType type; TCGv_i64 t_64; @@ -560,8 +602,7 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, } else { tcg_gen_dupi_vec(vece, t_vec, in_c); } - do_dup_store(type, dofs, oprsz, maxsz, t_vec); - tcg_temp_free_vec(t_vec); + do_dup_store(type, dbase, dofs, oprsz, maxsz, t_vec); return; } @@ -605,14 +646,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, dbase, 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, dbase, dofs + i); } tcg_temp_free_i64(t_64); goto done; @@ -621,7 +662,7 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, /* Otherwise implement out of line. */ t_ptr = tcg_temp_ebb_new_ptr(); - tcg_gen_addi_ptr(t_ptr, cpu_env, dofs); + tcg_gen_addi_ptr(t_ptr, dbase, dofs); /* * This may be expand_clr for the tail of an operation, e.g. @@ -690,31 +731,32 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, done: if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(dbase, dofs + oprsz, maxsz - oprsz); } } /* Likewise, but with zero. */ -static void expand_clr(uint32_t dofs, uint32_t maxsz) +static void expand_clr(TCGv_ptr dbase, uint32_t dofs, uint32_t maxsz) { - do_dup(MO_8, dofs, maxsz, maxsz, NULL, NULL, 0); + do_dup(MO_8, dbase, dofs, maxsz, maxsz, NULL, NULL, 0); } /* Expand OPSZ bytes worth of two-operand operations using i32 elements. */ -static void expand_2_i32(uint32_t dofs, uint32_t aofs, uint32_t oprsz, - bool load_dest, void (*fni)(TCGv_i32, TCGv_i32)) +static void expand_2_i32(TCGv_ptr dbase, uint32_t dofs, TCGv_ptr abase, + uint32_t aofs, uint32_t oprsz, bool load_dest, + void (*fni)(TCGv_i32, TCGv_i32)) { TCGv_i32 t0 = tcg_temp_new_i32(); TCGv_i32 t1 = tcg_temp_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(t0, abase, aofs + i); if (load_dest) { - tcg_gen_ld_i32(t1, cpu_env, dofs + i); + tcg_gen_ld_i32(t1, dbase, dofs + i); } fni(t1, t0); - tcg_gen_st_i32(t1, cpu_env, dofs + i); + tcg_gen_st_i32(t1, dbase, dofs + i); } tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); @@ -729,12 +771,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); @@ -749,21 +791,23 @@ 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); } /* Expand OPSZ bytes worth of three-operand operations using i32 elements. */ -static void expand_3_i32(uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, bool load_dest, +static void expand_3_i32(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, bool load_dest, void (*fni)(TCGv_i32, TCGv_i32, TCGv_i32)) { TCGv_i32 t0 = tcg_temp_new_i32(); @@ -772,13 +816,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, abase, aofs + i); + tcg_gen_ld_i32(t1, bbase, bofs + i); if (load_dest) { - tcg_gen_ld_i32(t2, cpu_env, dofs + i); + tcg_gen_ld_i32(t2, dbase, dofs + i); } fni(t2, t0, t1); - tcg_gen_st_i32(t2, cpu_env, dofs + i); + tcg_gen_st_i32(t2, dbase, dofs + i); } tcg_temp_free_i32(t2); tcg_temp_free_i32(t1); @@ -786,7 +830,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(); @@ -795,13 +840,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); @@ -820,13 +868,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); @@ -847,11 +895,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); @@ -860,20 +908,21 @@ static void expand_4i_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs, } /* Expand OPSZ bytes worth of two-operand operations using i64 elements. */ -static void expand_2_i64(uint32_t dofs, uint32_t aofs, uint32_t oprsz, - bool load_dest, void (*fni)(TCGv_i64, TCGv_i64)) +static void expand_2_i64(TCGv_ptr dbase, uint32_t dofs, TCGv_ptr abase, + uint32_t aofs, uint32_t oprsz, bool load_dest, + void (*fni)(TCGv_i64, TCGv_i64)) { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_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(t0, abase, aofs + i); if (load_dest) { - tcg_gen_ld_i64(t1, cpu_env, dofs + i); + tcg_gen_ld_i64(t1, dbase, dofs + i); } fni(t1, t0); - tcg_gen_st_i64(t1, cpu_env, dofs + i); + tcg_gen_st_i64(t1, dbase, dofs + i); } tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); @@ -888,12 +937,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); @@ -908,21 +957,23 @@ 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); } /* Expand OPSZ bytes worth of three-operand operations using i64 elements. */ -static void expand_3_i64(uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, bool load_dest, +static void expand_3_i64(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, bool load_dest, void (*fni)(TCGv_i64, TCGv_i64, TCGv_i64)) { TCGv_i64 t0 = tcg_temp_new_i64(); @@ -931,13 +982,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, abase, aofs + i); + tcg_gen_ld_i64(t1, bbase, bofs + i); if (load_dest) { - tcg_gen_ld_i64(t2, cpu_env, dofs + i); + tcg_gen_ld_i64(t2, dbase, dofs + i); } fni(t2, t0, t1); - tcg_gen_st_i64(t2, cpu_env, dofs + i); + tcg_gen_st_i64(t2, dbase, dofs + i); } tcg_temp_free_i64(t2); tcg_temp_free_i64(t1); @@ -945,7 +996,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(); @@ -954,13 +1006,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); @@ -979,13 +1034,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); @@ -1006,11 +1061,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); @@ -1019,25 +1074,23 @@ static void expand_4i_i64(uint32_t dofs, uint32_t aofs, uint32_t bofs, } /* Expand OPSZ bytes worth of two-operand operations using host vectors. */ -static void expand_2_vec(unsigned vece, uint32_t dofs, uint32_t aofs, +static void expand_2_vec(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, uint32_t oprsz, uint32_t tysz, TCGType type, 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, abase, aofs + i); if (load_dest) { - tcg_gen_ld_vec(t1, cpu_env, dofs + i); + tcg_gen_ld_vec(t1, dbase, dofs + i); } fni(vece, t1, t0); - tcg_gen_st_vec(t1, cpu_env, dofs + i); + tcg_gen_st_vec(t1, dbase, dofs + i); } - tcg_temp_free_vec(t0); - tcg_temp_free_vec(t1); } /* Expand OPSZ bytes worth of two-vector operands and an immediate operand @@ -1047,20 +1100,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, @@ -1068,46 +1118,40 @@ 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. */ -static void expand_3_vec(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, +static void expand_3_vec(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, uint32_t oprsz, 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, abase, aofs + i); + tcg_gen_ld_vec(t1, bbase, bofs + i); if (load_dest) { - tcg_gen_ld_vec(t2, cpu_env, dofs + i); + tcg_gen_ld_vec(t2, dbase, dofs + i); } fni(vece, t2, t0, t1); - tcg_gen_st_vec(t2, cpu_env, dofs + i); + tcg_gen_st_vec(t2, dbase, dofs + i); } - tcg_temp_free_vec(t2); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t0); } /* @@ -1116,27 +1160,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. */ @@ -1146,26 +1190,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); } /* @@ -1178,28 +1217,24 @@ 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. */ -void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz, const GVecGen2 *g) +void tcg_gen_gvec_2_var(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen2 *g) { const TCGOpcode *this_list = g->opt_opc ? : vecop_list_empty; const TCGOpcode *hold_list = tcg_swap_vecop_list(this_list); @@ -1207,7 +1242,7 @@ void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs); - check_overlap_2(dofs, aofs, maxsz); + check_overlap_2(dbase, dofs, abase, aofs, maxsz); type = 0; if (g->fniv) { @@ -1220,8 +1255,8 @@ void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, * that e.g. size == 80 would be expanded with 2x32 + 1x16. */ some = QEMU_ALIGN_DOWN(oprsz, 32); - expand_2_vec(g->vece, dofs, aofs, some, 32, TCG_TYPE_V256, - g->load_dest, g->fniv); + expand_2_vec(g->vece, dbase, dofs, abase, aofs, some, 32, + TCG_TYPE_V256, g->load_dest, g->fniv); if (some == oprsz) { break; } @@ -1231,22 +1266,25 @@ void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, maxsz -= some; /* fallthru */ case TCG_TYPE_V128: - expand_2_vec(g->vece, dofs, aofs, oprsz, 16, TCG_TYPE_V128, - g->load_dest, g->fniv); + expand_2_vec(g->vece, dbase, dofs, abase, aofs, oprsz, 16, + TCG_TYPE_V128, g->load_dest, g->fniv); break; case TCG_TYPE_V64: - expand_2_vec(g->vece, dofs, aofs, oprsz, 8, TCG_TYPE_V64, - g->load_dest, g->fniv); + expand_2_vec(g->vece, dbase, dofs, abase, aofs, oprsz, 8, + TCG_TYPE_V64, g->load_dest, g->fniv); break; case 0: if (g->fni8 && check_size_impl(oprsz, 8)) { - expand_2_i64(dofs, aofs, oprsz, g->load_dest, g->fni8); + expand_2_i64(dbase, dofs, abase, aofs, + oprsz, g->load_dest, g->fni8); } else if (g->fni4 && check_size_impl(oprsz, 4)) { - expand_2_i32(dofs, aofs, oprsz, g->load_dest, g->fni4); + expand_2_i32(dbase, dofs, abase, aofs, + oprsz, g->load_dest, g->fni4); } else { assert(g->fno != NULL); - tcg_gen_gvec_2_ool(dofs, aofs, oprsz, maxsz, g->data, g->fno); + expand_2_ool(dbase, dofs, abase, aofs, + oprsz, maxsz, g->data, g->fno); oprsz = maxsz; } break; @@ -1257,10 +1295,16 @@ void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(dbase, dofs + oprsz, maxsz - oprsz); } } +void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen2 *g) +{ + tcg_gen_gvec_2_var(tcg_env, dofs, tcg_env, aofs, oprsz, maxsz, g); +} + /* Expand a vector operation with two vectors and an immediate. */ void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t maxsz, int64_t c, const GVecGen2i *g) @@ -1271,7 +1315,7 @@ void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs); - check_overlap_2(dofs, aofs, maxsz); + check_overlap_2(tcg_env, dofs, tcg_env, aofs, maxsz); type = 0; if (g->fniv) { @@ -1326,7 +1370,7 @@ void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } @@ -1337,7 +1381,7 @@ void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz, TCGType type; check_size_align(oprsz, maxsz, dofs | aofs); - check_overlap_2(dofs, aofs, maxsz); + check_overlap_2(tcg_env, dofs, tcg_env, aofs, maxsz); type = 0; if (g->fniv) { @@ -1403,13 +1447,15 @@ void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz, } if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } /* Expand a vector three-operand operation. */ -void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t oprsz, uint32_t maxsz, const GVecGen3 *g) +void tcg_gen_gvec_3_var(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen3 *g) { const TCGOpcode *this_list = g->opt_opc ? : vecop_list_empty; const TCGOpcode *hold_list = tcg_swap_vecop_list(this_list); @@ -1417,7 +1463,7 @@ void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs | bofs); - check_overlap_3(dofs, aofs, bofs, maxsz); + check_overlap_3(dbase, dofs, abase, aofs, bbase, bofs, maxsz); type = 0; if (g->fniv) { @@ -1430,8 +1476,8 @@ void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, * that e.g. size == 80 would be expanded with 2x32 + 1x16. */ some = QEMU_ALIGN_DOWN(oprsz, 32); - expand_3_vec(g->vece, dofs, aofs, bofs, some, 32, TCG_TYPE_V256, - g->load_dest, g->fniv); + expand_3_vec(g->vece, dbase, dofs, abase, aofs, bbase, bofs, + some, 32, TCG_TYPE_V256, g->load_dest, g->fniv); if (some == oprsz) { break; } @@ -1442,23 +1488,25 @@ void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, maxsz -= some; /* fallthru */ case TCG_TYPE_V128: - expand_3_vec(g->vece, dofs, aofs, bofs, oprsz, 16, TCG_TYPE_V128, - g->load_dest, g->fniv); + expand_3_vec(g->vece, dbase, dofs, abase, aofs, bbase, bofs, + oprsz, 16, TCG_TYPE_V128, g->load_dest, g->fniv); break; case TCG_TYPE_V64: - expand_3_vec(g->vece, dofs, aofs, bofs, oprsz, 8, TCG_TYPE_V64, - g->load_dest, g->fniv); + expand_3_vec(g->vece, dbase, dofs, abase, aofs, bbase, bofs, + oprsz, 8, TCG_TYPE_V64, g->load_dest, g->fniv); break; case 0: if (g->fni8 && check_size_impl(oprsz, 8)) { - expand_3_i64(dofs, aofs, bofs, oprsz, g->load_dest, g->fni8); + expand_3_i64(dbase, dofs, abase, aofs, bbase, bofs, + oprsz, g->load_dest, g->fni8); } else if (g->fni4 && check_size_impl(oprsz, 4)) { - expand_3_i32(dofs, aofs, bofs, oprsz, g->load_dest, g->fni4); + expand_3_i32(dbase, dofs, abase, aofs, bbase, bofs, + oprsz, g->load_dest, g->fni4); } else { assert(g->fno != NULL); - tcg_gen_gvec_3_ool(dofs, aofs, bofs, oprsz, - maxsz, g->data, g->fno); + expand_3_ool(dbase, dofs, abase, aofs, bbase, bofs, + oprsz, maxsz, g->data, g->fno); oprsz = maxsz; } break; @@ -1469,10 +1517,17 @@ void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(dbase, dofs + oprsz, maxsz - oprsz); } } +void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen3 *g) +{ + tcg_gen_gvec_3_var(tcg_env, dofs, tcg_env, aofs, tcg_env, bofs, + oprsz, maxsz, g); +} + /* Expand a vector operation with three vectors and an immediate. */ void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz, int64_t c, @@ -1484,7 +1539,7 @@ void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs | bofs); - check_overlap_3(dofs, aofs, bofs, maxsz); + check_overlap_3(tcg_env, dofs, tcg_env, aofs, tcg_env, bofs, maxsz); type = 0; if (g->fniv) { @@ -1499,7 +1554,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; } @@ -1511,18 +1566,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); @@ -1536,7 +1593,7 @@ void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } @@ -1550,7 +1607,8 @@ void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs | bofs | cofs); - check_overlap_4(dofs, aofs, bofs, cofs, maxsz); + check_overlap_4(tcg_env, dofs, tcg_env, aofs, + tcg_env, bofs, tcg_env, cofs, maxsz); type = 0; if (g->fniv) { @@ -1605,7 +1663,7 @@ void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } @@ -1620,7 +1678,8 @@ void tcg_gen_gvec_4i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs | bofs | cofs); - check_overlap_4(dofs, aofs, bofs, cofs, maxsz); + check_overlap_4(tcg_env, dofs, tcg_env, aofs, + tcg_env, bofs, tcg_env, cofs, maxsz); type = 0; if (g->fniv) { @@ -1674,7 +1733,7 @@ void tcg_gen_gvec_4i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } @@ -1687,8 +1746,9 @@ static void vec_mov2(unsigned vece, TCGv_vec a, TCGv_vec b) tcg_gen_mov_vec(a, b); } -void tcg_gen_gvec_mov(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz) +void tcg_gen_gvec_mov_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz) { static const GVecGen2 g = { .fni8 = tcg_gen_mov_i64, @@ -1696,14 +1756,22 @@ void tcg_gen_gvec_mov(unsigned vece, uint32_t dofs, uint32_t aofs, .fno = gen_helper_gvec_mov, .prefer_i64 = TCG_TARGET_REG_BITS == 64, }; - if (dofs != aofs) { - tcg_gen_gvec_2(dofs, aofs, oprsz, maxsz, &g); - } else { + + if (dofs == aofs && dbase == abase) { check_size_align(oprsz, maxsz, dofs); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(dbase, dofs + oprsz, maxsz - oprsz); } + return; } + + tcg_gen_gvec_2_var(dbase, dofs, abase, aofs, oprsz, maxsz, &g); +} + +void tcg_gen_gvec_mov(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz) +{ + tcg_gen_gvec_mov_var(vece, tcg_env, dofs, tcg_env, aofs, oprsz, maxsz); } void tcg_gen_gvec_dup_i32(unsigned vece, uint32_t dofs, uint32_t oprsz, @@ -1711,7 +1779,7 @@ void tcg_gen_gvec_dup_i32(unsigned vece, uint32_t dofs, uint32_t oprsz, { check_size_align(oprsz, maxsz, dofs); tcg_debug_assert(vece <= MO_32); - do_dup(vece, dofs, oprsz, maxsz, in, NULL, 0); + do_dup(vece, tcg_env, dofs, oprsz, maxsz, in, NULL, 0); } void tcg_gen_gvec_dup_i64(unsigned vece, uint32_t dofs, uint32_t oprsz, @@ -1719,7 +1787,7 @@ void tcg_gen_gvec_dup_i64(unsigned vece, uint32_t dofs, uint32_t oprsz, { check_size_align(oprsz, maxsz, dofs); tcg_debug_assert(vece <= MO_64); - do_dup(vece, dofs, oprsz, maxsz, NULL, in, 0); + do_dup(vece, tcg_env, dofs, oprsz, maxsz, NULL, in, 0); } void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, @@ -1730,28 +1798,27 @@ 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); - do_dup_store(type, dofs, oprsz, maxsz, t_vec); - tcg_temp_free_vec(t_vec); + tcg_gen_dup_mem_vec(vece, t_vec, tcg_env, aofs); + do_dup_store(type, tcg_env, dofs, oprsz, maxsz, t_vec); } else if (vece <= MO_32) { 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); + do_dup(vece, tcg_env, dofs, oprsz, maxsz, in, NULL, 0); tcg_temp_free_i32(in); } else { TCGv_i64 in = tcg_temp_ebb_new_i64(); - tcg_gen_ld_i64(in, cpu_env, aofs); - do_dup(vece, dofs, oprsz, maxsz, NULL, in, 0); + tcg_gen_ld_i64(in, tcg_env, aofs); + do_dup(vece, tcg_env, dofs, oprsz, maxsz, NULL, in, 0); tcg_temp_free_i64(in); } } else if (vece == 4) { @@ -1762,26 +1829,25 @@ 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_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); } if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } else if (vece == 5) { /* 256-bit duplicate. */ @@ -1792,34 +1858,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_ebb_new_i64(); - tcg_gen_ld_i64(in[j], cpu_env, aofs + j * 8); + 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) { @@ -1827,18 +1890,24 @@ void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, } } if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } else { g_assert_not_reached(); } } +void tcg_gen_gvec_dup_imm_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + uint32_t oprsz, uint32_t maxsz, uint64_t x) +{ + check_size_align(oprsz, maxsz, dofs); + do_dup(vece, dbase, dofs, oprsz, maxsz, NULL, NULL, x); +} + void tcg_gen_gvec_dup_imm(unsigned vece, uint32_t dofs, uint32_t oprsz, uint32_t maxsz, uint64_t x) { - check_size_align(oprsz, maxsz, dofs); - do_dup(vece, dofs, oprsz, maxsz, NULL, NULL, x); + tcg_gen_gvec_dup_imm_var(vece, tcg_env, dofs, oprsz, maxsz, x); } void tcg_gen_gvec_not(unsigned vece, uint32_t dofs, uint32_t aofs, @@ -1936,8 +2005,10 @@ void tcg_gen_vec_add32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) static const TCGOpcode vecop_list_add[] = { INDEX_op_add_vec, 0 }; -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_add_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz) { static const GVecGen3 g[4] = { { .fni8 = tcg_gen_vec_add8_i64, @@ -1964,7 +2035,15 @@ void tcg_gen_gvec_add(unsigned vece, uint32_t dofs, uint32_t aofs, }; tcg_debug_assert(vece <= MO_64); - tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g[vece]); + tcg_gen_gvec_3_var(dbase, dofs, abase, aofs, bbase, bofs, + oprsz, maxsz, &g[vece]); +} + +void tcg_gen_gvec_add(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + tcg_gen_gvec_add_var(vece, tcg_env, dofs, tcg_env, aofs, tcg_env, bofs, + oprsz, maxsz); } void tcg_gen_gvec_adds(unsigned vece, uint32_t dofs, uint32_t aofs, @@ -2117,8 +2196,10 @@ void tcg_gen_vec_sub32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) tcg_temp_free_i64(t2); } -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_sub_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz) { static const GVecGen3 g[4] = { { .fni8 = tcg_gen_vec_sub8_i64, @@ -2145,7 +2226,15 @@ void tcg_gen_gvec_sub(unsigned vece, uint32_t dofs, uint32_t aofs, }; tcg_debug_assert(vece <= MO_64); - tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g[vece]); + tcg_gen_gvec_3_var(dbase, dofs, abase, aofs, bbase, bofs, + oprsz, maxsz, &g[vece]); +} + +void tcg_gen_gvec_sub(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + tcg_gen_gvec_sub_var(vece, tcg_env, dofs, tcg_env, aofs, tcg_env, bofs, + oprsz, maxsz); } static const TCGOpcode vecop_list_mul[] = { INDEX_op_mul_vec, 0 }; @@ -3136,15 +3225,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 @@ -3155,7 +3243,7 @@ do_gvec_shifts(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_i32 shift, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs); - check_overlap_2(dofs, aofs, maxsz); + check_overlap_2(tcg_env, dofs, tcg_env, aofs, maxsz); /* If the backend has a scalar expansion, great. */ type = choose_vector_type(g->s_list, vece, oprsz, vece == MO_64); @@ -3248,8 +3336,8 @@ do_gvec_shifts(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_i32 shift, 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); @@ -3261,7 +3349,7 @@ do_gvec_shifts(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_i32 shift, clear_tail: if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } @@ -3690,10 +3778,10 @@ static void expand_cmp_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); tcg_gen_negsetcond_i32(cond, t0, t0, t1); - tcg_gen_st_i32(t0, cpu_env, dofs + i); + tcg_gen_st_i32(t0, tcg_env, dofs + i); } tcg_temp_free_i32(t1); tcg_temp_free_i32(t0); @@ -3707,10 +3795,10 @@ static void expand_cmp_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); tcg_gen_negsetcond_i64(cond, t0, t0, t1); - tcg_gen_st_i64(t0, cpu_env, dofs + i); + tcg_gen_st_i64(t0, tcg_env, dofs + i); } tcg_temp_free_i64(t1); tcg_temp_free_i64(t0); @@ -3720,18 +3808,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, @@ -3777,10 +3863,10 @@ void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs | bofs); - check_overlap_3(dofs, aofs, bofs, maxsz); + check_overlap_3(tcg_env, dofs, tcg_env, aofs, tcg_env, bofs, maxsz); if (cond == TCG_COND_NEVER || cond == TCG_COND_ALWAYS) { - do_dup(MO_8, dofs, oprsz, maxsz, + do_dup(MO_8, tcg_env, dofs, oprsz, maxsz, NULL, NULL, -(cond == TCG_COND_ALWAYS)); return; } @@ -3842,10 +3928,159 @@ void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); + } +} + +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(tcg_env, dofs, tcg_env, aofs, maxsz); + + if (cond == TCG_COND_NEVER || cond == TCG_COND_ALWAYS) { + do_dup(MO_8, tcg_env, 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(tcg_env, 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_ebb_new_i64(); diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index d54c3055988..548496002d7 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -27,25 +27,27 @@ #include "tcg/tcg-temp-internal.h" #include "tcg/tcg-op-common.h" #include "tcg/tcg-mo.h" +#include "exec/target_page.h" #include "exec/translation-block.h" #include "exec/plugin-gen.h" #include "tcg-internal.h" - +#include "tcg-has.h" +#include "tcg-target-mo.h" static void check_max_alignment(unsigned a_bits) { -#if defined(CONFIG_SOFTMMU) /* * The requested alignment cannot overlap the TLB flags. - * FIXME: Must keep the count up-to-date with "exec/cpu-all.h". + * FIXME: Must keep the count up-to-date with "exec/tlb-flags.h". */ - tcg_debug_assert(a_bits + 5 <= tcg_ctx->page_bits); -#endif + if (tcg_use_softmmu) { + tcg_debug_assert(a_bits + 5 <= TARGET_PAGE_BITS); + } } static MemOp tcg_canonicalize_memop(MemOp op, bool is64, bool st) { - unsigned a_bits = get_alignment_bits(op); + unsigned a_bits = memop_alignment_bits(op); check_max_alignment(a_bits); @@ -77,40 +79,50 @@ static MemOp tcg_canonicalize_memop(MemOp op, bool is64, bool st) 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) +static void gen_ldst1(TCGOpcode opc, TCGType type, TCGTemp *v, + 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; + TCGOp *op = tcg_gen_op3(opc, type, temp_arg(v), temp_arg(addr), oi); + TCGOP_FLAGS(op) = get_memop(oi) & MO_SIZE; +} - 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_ldst2(TCGOpcode opc, TCGType type, TCGTemp *vl, TCGTemp *vh, + TCGTemp *addr, MemOpIdx oi) +{ + TCGOp *op = tcg_gen_op4(opc, type, temp_arg(vl), temp_arg(vh), + temp_arg(addr), oi); + TCGOP_FLAGS(op) = get_memop(oi) & MO_SIZE; +} + +static void gen_ld_i64(TCGv_i64 v, TCGTemp *addr, MemOpIdx oi) +{ + if (TCG_TARGET_REG_BITS == 32) { + gen_ldst2(INDEX_op_qemu_ld2, TCG_TYPE_I64, + tcgv_i32_temp(TCGV_LOW(v)), tcgv_i32_temp(TCGV_HIGH(v)), + addr, oi); + } else { + gen_ldst1(INDEX_op_qemu_ld, TCG_TYPE_I64, tcgv_i64_temp(v), addr, oi); } } -static void gen_ldst_i64(TCGOpcode opc, TCGv_i64 v, TCGTemp *addr, MemOpIdx oi) +static void gen_st_i64(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); + gen_ldst2(INDEX_op_qemu_st2, TCG_TYPE_I64, + tcgv_i32_temp(TCGV_LOW(v)), tcgv_i32_temp(TCGV_HIGH(v)), + addr, oi); } else { - gen_ldst(opc, tcgv_i64_temp(v), NULL, addr, oi); + gen_ldst1(INDEX_op_qemu_st, TCG_TYPE_I64, tcgv_i64_temp(v), addr, oi); } } @@ -141,11 +153,11 @@ static TCGv_i64 plugin_maybe_preserve_addr(TCGTemp *addr) 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) { -#ifdef CONFIG_PLUGIN if (tcg_ctx->plugin_insn != NULL) { qemu_plugin_meminfo_t info = make_plugin_meminfo(oi, rw); @@ -154,17 +166,65 @@ plugin_gen_mem_callbacks(TCGv_i64 copy_addr, TCGTemp *orig_addr, MemOpIdx oi, copy_addr = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(copy_addr, temp_tcgv_i32(orig_addr)); } - plugin_gen_empty_mem_callback(copy_addr, info); + tcg_gen_plugin_mem_cb(copy_addr, info); tcg_temp_free_i64(copy_addr); } else { if (copy_addr) { - plugin_gen_empty_mem_callback(copy_addr, info); + tcg_gen_plugin_mem_cb(copy_addr, info); tcg_temp_free_i64(copy_addr); } else { - plugin_gen_empty_mem_callback(temp_tcgv_i64(orig_addr), info); + 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 } @@ -174,7 +234,6 @@ static void tcg_gen_qemu_ld_i32_int(TCGv_i32 val, TCGTemp *addr, 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); @@ -190,13 +249,9 @@ static void tcg_gen_qemu_ld_i32_int(TCGv_i32 val, TCGTemp *addr, } copy_addr = plugin_maybe_preserve_addr(addr); - if (tcg_ctx->addr_type == TCG_TYPE_I32) { - opc = INDEX_op_qemu_ld_a32_i32; - } else { - opc = INDEX_op_qemu_ld_a64_i32; - } - gen_ldst(opc, tcgv_i32_temp(val), NULL, addr, oi); - plugin_gen_mem_callbacks(copy_addr, addr, orig_oi, QEMU_PLUGIN_MEM_R); + gen_ldst1(INDEX_op_qemu_ld, TCG_TYPE_I32, tcgv_i32_temp(val), 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) { @@ -227,7 +282,6 @@ static void tcg_gen_qemu_st_i32_int(TCGv_i32 val, TCGTemp *addr, { 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); @@ -250,21 +304,8 @@ static void tcg_gen_qemu_st_i32_int(TCGv_i32 val, TCGTemp *addr, oi = make_memop_idx(memop, idx); } - if (TCG_TARGET_HAS_qemu_st8_i32 && (memop & MO_SIZE) == MO_8) { - if (tcg_ctx->addr_type == TCG_TYPE_I32) { - opc = INDEX_op_qemu_st8_a32_i32; - } else { - opc = INDEX_op_qemu_st8_a64_i32; - } - } else { - if (tcg_ctx->addr_type == TCG_TYPE_I32) { - opc = INDEX_op_qemu_st_a32_i32; - } else { - opc = INDEX_op_qemu_st_a64_i32; - } - } - gen_ldst(opc, tcgv_i32_temp(val), NULL, addr, oi); - plugin_gen_mem_callbacks(NULL, addr, orig_oi, QEMU_PLUGIN_MEM_W); + gen_ldst1(INDEX_op_qemu_st, TCG_TYPE_I32, tcgv_i32_temp(val), addr, oi); + plugin_gen_mem_callbacks_i32(val, NULL, addr, orig_oi, QEMU_PLUGIN_MEM_W); if (swap) { tcg_temp_free_i32(swap); @@ -285,7 +326,6 @@ static void tcg_gen_qemu_ld_i64_int(TCGv_i64 val, TCGTemp *addr, 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); @@ -311,13 +351,9 @@ static void tcg_gen_qemu_ld_i64_int(TCGv_i64 val, TCGTemp *addr, } copy_addr = plugin_maybe_preserve_addr(addr); - if (tcg_ctx->addr_type == TCG_TYPE_I32) { - opc = INDEX_op_qemu_ld_a32_i64; - } else { - opc = INDEX_op_qemu_ld_a64_i64; - } - gen_ldst_i64(opc, val, addr, oi); - plugin_gen_mem_callbacks(copy_addr, addr, orig_oi, QEMU_PLUGIN_MEM_R); + gen_ld_i64(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 @@ -352,7 +388,6 @@ static void tcg_gen_qemu_st_i64_int(TCGv_i64 val, TCGTemp *addr, { 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); @@ -383,13 +418,8 @@ static void tcg_gen_qemu_st_i64_int(TCGv_i64 val, TCGTemp *addr, oi = make_memop_idx(memop, idx); } - if (tcg_ctx->addr_type == TCG_TYPE_I32) { - opc = INDEX_op_qemu_st_a32_i64; - } else { - opc = INDEX_op_qemu_st_a64_i64; - } - gen_ldst_i64(opc, val, addr, oi); - plugin_gen_mem_callbacks(NULL, addr, orig_oi, QEMU_PLUGIN_MEM_W); + gen_st_i64(val, addr, oi); + plugin_gen_mem_callbacks_i64(val, NULL, addr, orig_oi, QEMU_PLUGIN_MEM_W); if (swap) { tcg_temp_free_i64(swap); @@ -411,10 +441,11 @@ void tcg_gen_qemu_st_i64_chk(TCGv_i64 val, TCGTemp *addr, TCGArg idx, */ static bool use_two_i64_for_i128(MemOp mop) { -#ifdef CONFIG_SOFTMMU /* Two softmmu tlb lookups is larger than one function call. */ - return false; -#else + 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. @@ -427,12 +458,10 @@ static bool use_two_i64_for_i128(MemOp mop) case MO_ATOM_SUBALIGN: case MO_ATOM_WITHIN16: case MO_ATOM_WITHIN16_PAIR: - /* In a serialized context, no atomicity is required. */ - return !(tcg_ctx->gen_tb->cflags & CF_PARALLEL); + return false; default: g_assert_not_reached(); } -#endif } static void canonicalize_memop_i128_as_i64(MemOp ret[2], MemOp orig) @@ -499,13 +528,19 @@ static void maybe_free_addr64(TCGv_i64 a64) static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, TCGArg idx, MemOp memop) { - const MemOpIdx orig_oi = make_memop_idx(memop, idx); + MemOpIdx orig_oi; TCGv_i64 ext_addr = NULL; - TCGOpcode opc; - check_max_alignment(get_alignment_bits(memop)); + 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; @@ -522,12 +557,8 @@ static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, 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); + gen_ldst2(INDEX_op_qemu_ld2, TCG_TYPE_I128, tcgv_i64_temp(lo), + tcgv_i64_temp(hi), addr, oi); if (need_bswap) { tcg_gen_bswap64_i64(lo, lo); @@ -542,12 +573,6 @@ static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, 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 @@ -561,7 +586,7 @@ static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, y = TCGV128_LOW(val); } - gen_ldst_i64(opc, x, addr, make_memop_idx(mop[0], idx)); + gen_ld_i64(x, addr, make_memop_idx(mop[0], idx)); if (need_bswap) { tcg_gen_bswap64_i64(x, x); @@ -577,7 +602,7 @@ static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, addr_p8 = tcgv_i64_temp(t); } - gen_ldst_i64(opc, y, addr_p8, make_memop_idx(mop[1], idx)); + gen_ld_i64(y, addr_p8, make_memop_idx(mop[1], idx)); tcg_temp_free_internal(addr_p8); if (need_bswap) { @@ -589,11 +614,12 @@ static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, tcg_gen_extu_i32_i64(ext_addr, temp_tcgv_i32(addr)); addr = tcgv_i64_temp(ext_addr); } - gen_helper_ld_i128(val, cpu_env, temp_tcgv_i64(addr), + gen_helper_ld_i128(val, tcg_env, temp_tcgv_i64(addr), tcg_constant_i32(orig_oi)); } - plugin_gen_mem_callbacks(ext_addr, addr, orig_oi, QEMU_PLUGIN_MEM_R); + 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, @@ -608,13 +634,19 @@ void tcg_gen_qemu_ld_i128_chk(TCGv_i128 val, TCGTemp *addr, TCGArg idx, static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, TCGArg idx, MemOp memop) { - const MemOpIdx orig_oi = make_memop_idx(memop, idx); + MemOpIdx orig_oi; TCGv_i64 ext_addr = NULL; - TCGOpcode opc; - check_max_alignment(get_alignment_bits(memop)); + 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) { @@ -634,12 +666,8 @@ static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, 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); + gen_ldst2(INDEX_op_qemu_st2, TCG_TYPE_I128, + tcgv_i64_temp(lo), tcgv_i64_temp(hi), addr, oi); if (need_bswap) { tcg_temp_free_i64(lo); @@ -652,12 +680,6 @@ static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, 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); @@ -672,7 +694,7 @@ static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, x = b; } - gen_ldst_i64(opc, x, addr, make_memop_idx(mop[0], idx)); + gen_st_i64(x, addr, make_memop_idx(mop[0], idx)); if (tcg_ctx->addr_type == TCG_TYPE_I32) { TCGv_i32 t = tcg_temp_ebb_new_i32(); @@ -686,10 +708,10 @@ static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, if (b) { tcg_gen_bswap64_i64(b, y); - gen_ldst_i64(opc, b, addr_p8, make_memop_idx(mop[1], idx)); + gen_st_i64(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)); + gen_st_i64(y, addr_p8, make_memop_idx(mop[1], idx)); } tcg_temp_free_internal(addr_p8); } else { @@ -698,11 +720,12 @@ static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, tcg_gen_extu_i32_i64(ext_addr, temp_tcgv_i32(addr)); addr = tcgv_i64_temp(ext_addr); } - gen_helper_st_i128(cpu_env, temp_tcgv_i64(addr), val, + gen_helper_st_i128(tcg_env, temp_tcgv_i64(addr), val, tcg_constant_i32(orig_oi)); } - plugin_gen_mem_callbacks(ext_addr, addr, orig_oi, QEMU_PLUGIN_MEM_W); + 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, @@ -714,7 +737,7 @@ void tcg_gen_qemu_st_i128_chk(TCGv_i128 val, TCGTemp *addr, TCGArg idx, tcg_gen_qemu_st_i128_int(val, addr, idx, memop); } -static void tcg_gen_ext_i32(TCGv_i32 ret, TCGv_i32 val, MemOp opc) +void tcg_gen_ext_i32(TCGv_i32 ret, TCGv_i32 val, MemOp opc) { switch (opc & MO_SSIZE) { case MO_SB: @@ -729,13 +752,16 @@ static void tcg_gen_ext_i32(TCGv_i32 ret, TCGv_i32 val, MemOp opc) case MO_UW: tcg_gen_ext16u_i32(ret, val); break; - default: + case MO_UL: + case MO_SL: tcg_gen_mov_i32(ret, val); break; + default: + g_assert_not_reached(); } } -static void tcg_gen_ext_i64(TCGv_i64 ret, TCGv_i64 val, MemOp opc) +void tcg_gen_ext_i64(TCGv_i64 ret, TCGv_i64 val, MemOp opc) { switch (opc & MO_SSIZE) { case MO_SB: @@ -756,9 +782,12 @@ static void tcg_gen_ext_i64(TCGv_i64 ret, TCGv_i64 val, MemOp opc) case MO_UL: tcg_gen_ext32u_i64(ret, val); break; - default: + case MO_UQ: + case MO_SQ: tcg_gen_mov_i64(ret, val); break; + default: + g_assert_not_reached(); } } @@ -847,7 +876,7 @@ static void tcg_gen_atomic_cmpxchg_i32_int(TCGv_i32 retv, TCGTemp *addr, oi = make_memop_idx(memop & ~MO_SIGN, idx); a64 = maybe_extend_addr64(addr); - gen(retv, cpu_env, a64, cmpv, newv, tcg_constant_i32(oi)); + gen(retv, tcg_env, a64, cmpv, newv, tcg_constant_i32(oi)); maybe_free_addr64(a64); if (memop & MO_SIGN) { @@ -927,12 +956,12 @@ static void tcg_gen_atomic_cmpxchg_i64_int(TCGv_i64 retv, TCGTemp *addr, if (gen) { MemOpIdx oi = make_memop_idx(memop, idx); TCGv_i64 a64 = maybe_extend_addr64(addr); - gen(retv, cpu_env, a64, cmpv, newv, tcg_constant_i32(oi)); + gen(retv, tcg_env, a64, cmpv, newv, tcg_constant_i32(oi)); maybe_free_addr64(a64); return; } - gen_helper_exit_atomic(cpu_env); + gen_helper_exit_atomic(tcg_env); /* * Produce a result for a well-formed opcode stream. This satisfies @@ -990,7 +1019,7 @@ static void tcg_gen_nonatomic_cmpxchg_i128_int(TCGv_i128 retv, TCGTemp *addr, MemOpIdx oi = make_memop_idx(memop, idx); TCGv_i64 a64 = maybe_extend_addr64(addr); - gen_helper_nonatomic_cmpxchgo(retv, cpu_env, a64, cmpv, newv, + gen_helper_nonatomic_cmpxchgo(retv, tcg_env, a64, cmpv, newv, tcg_constant_i32(oi)); maybe_free_addr64(a64); } else { @@ -1049,12 +1078,12 @@ static void tcg_gen_atomic_cmpxchg_i128_int(TCGv_i128 retv, TCGTemp *addr, if (gen) { MemOpIdx oi = make_memop_idx(memop, idx); TCGv_i64 a64 = maybe_extend_addr64(addr); - gen(retv, cpu_env, a64, cmpv, newv, tcg_constant_i32(oi)); + gen(retv, tcg_env, a64, cmpv, newv, tcg_constant_i32(oi)); maybe_free_addr64(a64); return; } - gen_helper_exit_atomic(cpu_env); + gen_helper_exit_atomic(tcg_env); /* * Produce a result for a well-formed opcode stream. This satisfies @@ -1108,7 +1137,7 @@ static void do_atomic_op_i32(TCGv_i32 ret, TCGTemp *addr, TCGv_i32 val, oi = make_memop_idx(memop & ~MO_SIGN, idx); a64 = maybe_extend_addr64(addr); - gen(ret, cpu_env, a64, val, tcg_constant_i32(oi)); + gen(ret, tcg_env, a64, val, tcg_constant_i32(oi)); maybe_free_addr64(a64); if (memop & MO_SIGN) { @@ -1146,12 +1175,12 @@ static void do_atomic_op_i64(TCGv_i64 ret, TCGTemp *addr, TCGv_i64 val, if (gen) { MemOpIdx oi = make_memop_idx(memop & ~MO_SIGN, idx); TCGv_i64 a64 = maybe_extend_addr64(addr); - gen(ret, cpu_env, a64, val, tcg_constant_i32(oi)); + gen(ret, tcg_env, a64, val, tcg_constant_i32(oi)); maybe_free_addr64(a64); return; } - gen_helper_exit_atomic(cpu_env); + 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); diff --git a/tcg/tcg-op-vec.c b/tcg/tcg-op-vec.c index 094298bb273..893d68e7d80 100644 --- a/tcg/tcg-op-vec.c +++ b/tcg/tcg-op-vec.c @@ -23,6 +23,7 @@ #include "tcg/tcg-op-common.h" #include "tcg/tcg-mo.h" #include "tcg-internal.h" +#include "tcg-has.h" /* * Vector optional opcode tracking. @@ -143,7 +144,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, 2); - TCGOP_VECL(op) = type - TCG_TYPE_V64; + TCGOP_TYPE(op) = type; TCGOP_VECE(op) = vece; op->args[0] = r; op->args[1] = a; @@ -153,7 +154,7 @@ void vec_gen_3(TCGOpcode opc, TCGType type, unsigned vece, TCGArg r, TCGArg a, TCGArg b) { TCGOp *op = tcg_emit_op(opc, 3); - TCGOP_VECL(op) = type - TCG_TYPE_V64; + TCGOP_TYPE(op) = type; TCGOP_VECE(op) = vece; op->args[0] = r; op->args[1] = a; @@ -164,7 +165,7 @@ void vec_gen_4(TCGOpcode opc, TCGType type, unsigned vece, TCGArg r, TCGArg a, TCGArg b, TCGArg c) { TCGOp *op = tcg_emit_op(opc, 4); - TCGOP_VECL(op) = type - TCG_TYPE_V64; + TCGOP_TYPE(op) = type; TCGOP_VECE(op) = vece; op->args[0] = r; op->args[1] = a; @@ -172,11 +173,11 @@ 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, 6); - TCGOP_VECL(op) = type - TCG_TYPE_V64; + TCGOP_TYPE(op) = type; TCGOP_VECE(op) = vece; op->args[0] = r; op->args[1] = a; @@ -508,9 +509,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; @@ -518,6 +521,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 { @@ -526,6 +541,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 02a8cadcc00..dfa5c387284 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -29,75 +29,269 @@ #include "exec/translation-block.h" #include "exec/plugin-gen.h" #include "tcg-internal.h" +#include "tcg-has.h" +/* + * Encourage the compiler to tail-call to a function, rather than inlining. + * Minimizes code size across 99 bottles of beer on the wall. + */ +#define NI __attribute__((noinline)) -void tcg_gen_op1(TCGOpcode opc, TCGArg a1) +TCGOp * NI tcg_gen_op1(TCGOpcode opc, TCGType type, TCGArg a1) { TCGOp *op = tcg_emit_op(opc, 1); + TCGOP_TYPE(op) = type; op->args[0] = a1; + return op; } -void tcg_gen_op2(TCGOpcode opc, TCGArg a1, TCGArg a2) +TCGOp * NI tcg_gen_op2(TCGOpcode opc, TCGType type, TCGArg a1, TCGArg a2) { TCGOp *op = tcg_emit_op(opc, 2); + TCGOP_TYPE(op) = type; 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, TCGType type, TCGArg a1, + TCGArg a2, TCGArg a3) { TCGOp *op = tcg_emit_op(opc, 3); + TCGOP_TYPE(op) = type; 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, TCGType type, TCGArg a1, TCGArg a2, + TCGArg a3, TCGArg a4) { TCGOp *op = tcg_emit_op(opc, 4); + TCGOP_TYPE(op) = type; 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, TCGType type, TCGArg a1, TCGArg a2, + TCGArg a3, TCGArg a4, TCGArg a5) { TCGOp *op = tcg_emit_op(opc, 5); + TCGOP_TYPE(op) = type; 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, TCGType type, TCGArg a1, TCGArg a2, + TCGArg a3, TCGArg a4, TCGArg a5, TCGArg a6) { TCGOp *op = tcg_emit_op(opc, 6); + TCGOP_TYPE(op) = type; 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, TCGType type, TCGv_i32 a1) +{ + tcg_gen_op1(opc, type, tcgv_i32_arg(a1)); +} + +static void DNI tcg_gen_op1_i64(TCGOpcode opc, TCGType type, TCGv_i64 a1) +{ + tcg_gen_op1(opc, type, tcgv_i64_arg(a1)); +} + +static TCGOp * DNI tcg_gen_op1i(TCGOpcode opc, TCGType type, TCGArg a1) +{ + return tcg_gen_op1(opc, type, a1); +} + +static void DNI tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2) +{ + tcg_gen_op2(opc, TCG_TYPE_I32, 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, TCG_TYPE_I64, 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, TCG_TYPE_I32, 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, TCG_TYPE_I64, 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, TCG_TYPE_I32, 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, TCG_TYPE_I64, 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, TCG_TYPE_I32, 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, TCG_TYPE_I64, 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, TCG_TYPE_I32, 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, TCG_TYPE_I64, 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, TCG_TYPE_I32, 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, TCG_TYPE_I64, 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, TCG_TYPE_I32, + 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, TCG_TYPE_I64, + 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, TCG_TYPE_I32, 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, TCG_TYPE_I64, 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, TCG_TYPE_I32, 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, TCG_TYPE_I64, tcgv_i64_arg(a1), tcgv_i64_arg(a2), + tcgv_i64_arg(a3), a4, a5); +} + +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, TCG_TYPE_I32, 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, TCG_TYPE_I64, 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, TCG_TYPE_I32, tcgv_i32_arg(a1), tcgv_i32_arg(a2), + tcgv_i32_arg(a3), tcgv_i32_arg(a4), a5, a6); } /* Generic ops. */ -static void add_last_as_label_use(TCGLabel *l) +void gen_set_label(TCGLabel *l) +{ + l->present = 1; + tcg_gen_op1(INDEX_op_set_label, 0, label_arg(l)); +} + +static void add_as_label_use(TCGLabel *l, TCGOp *op) { TCGLabelUse *u = tcg_malloc(sizeof(TCGLabelUse)); - u->op = tcg_last_op(); + u->op = op; QSIMPLEQ_INSERT_TAIL(&l->branches, u, next); } void tcg_gen_br(TCGLabel *l) { - tcg_gen_op1(INDEX_op_br, label_arg(l)); - add_last_as_label_use(l); + add_as_label_use(l, tcg_gen_op1(INDEX_op_br, 0, label_arg(l))); } void tcg_gen_mb(TCGBar mb_type) @@ -115,17 +309,44 @@ void tcg_gen_mb(TCGBar mb_type) #endif if (parallel) { - tcg_gen_op1(INDEX_op_mb, mb_type); + tcg_gen_op1(INDEX_op_mb, 0, mb_type); } } +void tcg_gen_plugin_cb(unsigned from) +{ + tcg_gen_op1(INDEX_op_plugin_cb, 0, from); +} + +void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo) +{ + tcg_gen_op2(INDEX_op_plugin_mem_cb, 0, tcgv_i64_arg(addr), meminfo); +} + /* 32 bit ops */ +void tcg_gen_discard_i32(TCGv_i32 arg) +{ + tcg_gen_op1_i32(INDEX_op_discard, TCG_TYPE_I32, arg); +} + +void tcg_gen_mov_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (ret != arg) { + tcg_gen_op2_i32(INDEX_op_mov, 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, ret, arg1, arg2); +} + void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { /* some cases can be optimized here */ @@ -136,11 +357,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, 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); } @@ -148,12 +373,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, ret, arg); +} + +void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op3_i32(INDEX_op_and, ret, arg1, arg2); } void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) @@ -166,17 +396,19 @@ void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) case -1: tcg_gen_mov_i32(ret, arg1); return; - case 0xff: - /* Don't recurse with tcg_gen_ext8u_i32. */ - if (TCG_TARGET_HAS_ext8u_i32) { - tcg_gen_op2_i32(INDEX_op_ext8u_i32, ret, arg1); - return; - } - break; - case 0xffff: - if (TCG_TARGET_HAS_ext16u_i32) { - tcg_gen_op2_i32(INDEX_op_ext16u_i32, ret, arg1); - return; + default: + /* + * Canonicalize on extract, if valid. This aids x86 with its + * 2 operand MOVZBL and 2 operand AND, selecting the TCGOpcode + * which does not require matching operands. Other backends can + * trivially expand the extract to AND during code generation. + */ + if (!(arg2 & (arg2 + 1))) { + unsigned len = ctz32(~arg2); + if (TCG_TARGET_extract_valid(TCG_TYPE_I32, 0, len)) { + tcg_gen_extract_i32(ret, arg1, 0, len); + return; + } } break; } @@ -184,6 +416,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, ret, arg1, arg2); +} + void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { /* Some cases can be optimized here. */ @@ -196,19 +433,39 @@ 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, ret, arg1, arg2); +} + void tcg_gen_xori_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 if (arg2 == -1 && TCG_TARGET_HAS_not_i32) { + } else if (arg2 == -1 && + tcg_op_supported(INDEX_op_not, TCG_TYPE_I32, 0)) { /* Don't recurse with tcg_gen_not_i32. */ - tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg1); + tcg_gen_op2_i32(INDEX_op_not, ret, arg1); } else { tcg_gen_xor_i32(ret, arg1, tcg_constant_i32(arg2)); } } +void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (tcg_op_supported(INDEX_op_not, TCG_TYPE_I32, 0)) { + tcg_gen_op2_i32(INDEX_op_not, 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, ret, arg1, arg2); +} + void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { tcg_debug_assert(arg2 >= 0 && arg2 < 32); @@ -219,6 +476,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, ret, arg1, arg2); +} + void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { tcg_debug_assert(arg2 >= 0 && arg2 < 32); @@ -229,6 +491,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, ret, arg1, arg2); +} + void tcg_gen_sari_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { tcg_debug_assert(arg2 >= 0 && arg2 < 32); @@ -244,8 +511,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) { - tcg_gen_op4ii_i32(INDEX_op_brcond_i32, arg1, arg2, cond, label_arg(l)); - add_last_as_label_use(l); + TCGOp *op = tcg_gen_op4ii_i32(INDEX_op_brcond, + arg1, arg2, cond, label_arg(l)); + add_as_label_use(l, op); } } @@ -266,7 +534,7 @@ void tcg_gen_setcond_i32(TCGCond cond, TCGv_i32 ret, } else if (cond == TCG_COND_NEVER) { tcg_gen_movi_i32(ret, 0); } else { - tcg_gen_op4i_i32(INDEX_op_setcond_i32, ret, arg1, arg2, cond); + tcg_gen_op4i_i32(INDEX_op_setcond, ret, arg1, arg2, cond); } } @@ -283,14 +551,22 @@ void tcg_gen_negsetcond_i32(TCGCond cond, TCGv_i32 ret, 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); + tcg_gen_op4i_i32(INDEX_op_negsetcond, ret, arg1, arg2, cond); } } +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, ret, arg1, arg2); +} + void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { if (arg2 == 0) { @@ -304,12 +580,12 @@ 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) { - if (TCG_TARGET_HAS_div_i32) { - tcg_gen_op3_i32(INDEX_op_div_i32, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div2_i32) { + if (tcg_op_supported(INDEX_op_divs, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_divs, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_divs2, TCG_TYPE_I32, 0)) { 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_gen_op5_i32(INDEX_op_divs2, ret, t0, arg1, t0, arg2); tcg_temp_free_i32(t0); } else { gen_helper_div_i32(ret, arg1, arg2); @@ -318,18 +594,18 @@ 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) { - if (TCG_TARGET_HAS_rem_i32) { - tcg_gen_op3_i32(INDEX_op_rem_i32, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div_i32) { + if (tcg_op_supported(INDEX_op_rems, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_rems, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_divs, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); - tcg_gen_op3_i32(INDEX_op_div_i32, t0, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_divs, 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) { + } else if (tcg_op_supported(INDEX_op_divs2, TCG_TYPE_I32, 0)) { 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_gen_op5_i32(INDEX_op_divs2, t0, ret, arg1, t0, arg2); tcg_temp_free_i32(t0); } else { gen_helper_rem_i32(ret, arg1, arg2); @@ -338,12 +614,12 @@ 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) { - if (TCG_TARGET_HAS_div_i32) { - tcg_gen_op3_i32(INDEX_op_divu_i32, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div2_i32) { + if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_divu, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_divu2, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); - tcg_gen_movi_i32(t0, 0); - tcg_gen_op5_i32(INDEX_op_divu2_i32, ret, t0, arg1, t0, arg2); + TCGv_i32 zero = tcg_constant_i32(0); + tcg_gen_op5_i32(INDEX_op_divu2, ret, t0, arg1, zero, arg2); tcg_temp_free_i32(t0); } else { gen_helper_divu_i32(ret, arg1, arg2); @@ -352,18 +628,18 @@ 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) { - if (TCG_TARGET_HAS_rem_i32) { - tcg_gen_op3_i32(INDEX_op_remu_i32, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div_i32) { + if (tcg_op_supported(INDEX_op_remu, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_remu, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); - tcg_gen_op3_i32(INDEX_op_divu_i32, t0, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_divu, 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) { + } else if (tcg_op_supported(INDEX_op_divu2, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); - tcg_gen_movi_i32(t0, 0); - tcg_gen_op5_i32(INDEX_op_divu2_i32, t0, ret, arg1, t0, arg2); + TCGv_i32 zero = tcg_constant_i32(0); + tcg_gen_op5_i32(INDEX_op_divu2, t0, ret, arg1, zero, arg2); tcg_temp_free_i32(t0); } else { gen_helper_remu_i32(ret, arg1, arg2); @@ -372,8 +648,8 @@ 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) { - if (TCG_TARGET_HAS_andc_i32) { - tcg_gen_op3_i32(INDEX_op_andc_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_andc, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_andc, ret, arg1, arg2); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_not_i32(t0, arg2); @@ -384,8 +660,8 @@ 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) { - if (TCG_TARGET_HAS_eqv_i32) { - tcg_gen_op3_i32(INDEX_op_eqv_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_eqv, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_eqv, ret, arg1, arg2); } else { tcg_gen_xor_i32(ret, arg1, arg2); tcg_gen_not_i32(ret, ret); @@ -394,8 +670,8 @@ 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) { - if (TCG_TARGET_HAS_nand_i32) { - tcg_gen_op3_i32(INDEX_op_nand_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_nand, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_nand, ret, arg1, arg2); } else { tcg_gen_and_i32(ret, arg1, arg2); tcg_gen_not_i32(ret, ret); @@ -404,8 +680,8 @@ 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) { - if (TCG_TARGET_HAS_nor_i32) { - tcg_gen_op3_i32(INDEX_op_nor_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_nor, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_nor, ret, arg1, arg2); } else { tcg_gen_or_i32(ret, arg1, arg2); tcg_gen_not_i32(ret, ret); @@ -414,8 +690,8 @@ 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) { - if (TCG_TARGET_HAS_orc_i32) { - tcg_gen_op3_i32(INDEX_op_orc_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_orc, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_orc, ret, arg1, arg2); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_not_i32(t0, arg2); @@ -426,9 +702,9 @@ 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) { - if (TCG_TARGET_HAS_clz_i32) { - tcg_gen_op3_i32(INDEX_op_clz_i32, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_clz_i64) { + if (tcg_op_supported(INDEX_op_clz, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_clz, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_clz, TCG_TYPE_I64, 0)) { TCGv_i64 t1 = tcg_temp_ebb_new_i64(); TCGv_i64 t2 = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(t1, arg1); @@ -451,9 +727,13 @@ void tcg_gen_clzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2) 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_i32 z, t; + + if (tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_ctz, ret, arg1, arg2); + return; + } + if (tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I64, 0)) { TCGv_i64 t1 = tcg_temp_ebb_new_i64(); TCGv_i64 t2 = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(t1, arg1); @@ -462,34 +742,34 @@ void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) tcg_gen_extrl_i64_i32(ret, t1); tcg_temp_free_i64(t1); tcg_temp_free_i64(t2); - } else if (TCG_TARGET_HAS_ctpop_i32 - || TCG_TARGET_HAS_ctpop_i64 - || TCG_TARGET_HAS_clz_i32 - || TCG_TARGET_HAS_clz_i64) { - 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); - tcg_gen_andc_i32(t, t, arg1); - tcg_gen_ctpop_i32(t, t); - } else { - /* Since all non-x86 hosts have clz(0) == 32, don't fight it. */ - tcg_gen_neg_i32(t, arg1); - tcg_gen_and_i32(t, t, arg1); - tcg_gen_clzi_i32(t, t, 32); - tcg_gen_xori_i32(t, t, 31); - } - z = tcg_constant_i32(0); - tcg_gen_movcond_i32(TCG_COND_EQ, ret, arg1, z, arg2, t); - tcg_temp_free_i32(t); + return; + } + if (tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_REG, 0)) { + t = tcg_temp_ebb_new_i32(); + tcg_gen_subi_i32(t, arg1, 1); + tcg_gen_andc_i32(t, t, arg1); + tcg_gen_ctpop_i32(t, t); + } else if (tcg_op_supported(INDEX_op_clz, TCG_TYPE_REG, 0)) { + t = tcg_temp_ebb_new_i32(); + tcg_gen_neg_i32(t, arg1); + tcg_gen_and_i32(t, t, arg1); + tcg_gen_clzi_i32(t, t, 32); + tcg_gen_xori_i32(t, t, 31); } else { gen_helper_ctz_i32(ret, arg1, arg2); + return; } + + z = tcg_constant_i32(0); + tcg_gen_movcond_i32(TCG_COND_EQ, ret, arg1, z, arg2, t); + tcg_temp_free_i32(t); } 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) { + if (arg2 == 32 + && !tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I32, 0) + && tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_REG, 0)) { /* This equivalence has the advantage of not requiring a fixup. */ TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_subi_i32(t, arg1, 1); @@ -503,7 +783,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) { + if (tcg_op_supported(INDEX_op_clz, TCG_TYPE_REG, 0)) { TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_sari_i32(t, arg, 31); tcg_gen_xor_i32(t, t, arg); @@ -517,9 +797,9 @@ void tcg_gen_clrsb_i32(TCGv_i32 ret, TCGv_i32 arg) 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) { + if (tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_I32, 0)) { + tcg_gen_op2_i32(INDEX_op_ctpop, ret, arg1); + } else if (tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_I64, 0)) { TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(t, arg1); tcg_gen_ctpop_i64(t, t); @@ -532,15 +812,18 @@ void tcg_gen_ctpop_i32(TCGv_i32 ret, TCGv_i32 arg1) void tcg_gen_rotl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCG_TARGET_HAS_rot_i32) { - tcg_gen_op3_i32(INDEX_op_rotl_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_rotl, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_rotl, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_rotr, TCG_TYPE_I32, 0)) { + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + tcg_gen_neg_i32(t0, arg2); + tcg_gen_op3_i32(INDEX_op_rotr, ret, arg1, t0); + tcg_temp_free_i32(t0); } else { - TCGv_i32 t0, t1; - - t0 = tcg_temp_ebb_new_i32(); - t1 = tcg_temp_ebb_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); tcg_gen_shl_i32(t0, arg1, arg2); - tcg_gen_subfi_i32(t1, 32, arg2); + tcg_gen_neg_i32(t1, arg2); tcg_gen_shr_i32(t1, arg1, t1); tcg_gen_or_i32(ret, t0, t1); tcg_temp_free_i32(t0); @@ -554,12 +837,15 @@ void tcg_gen_rotli_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 if (TCG_TARGET_HAS_rot_i32) { - tcg_gen_rotl_i32(ret, arg1, tcg_constant_i32(arg2)); + } else if (tcg_op_supported(INDEX_op_rotl, TCG_TYPE_I32, 0)) { + TCGv_i32 t0 = tcg_constant_i32(arg2); + tcg_gen_op3_i32(INDEX_op_rotl, ret, arg1, t0); + } else if (tcg_op_supported(INDEX_op_rotr, TCG_TYPE_I32, 0)) { + TCGv_i32 t0 = tcg_constant_i32(32 - arg2); + tcg_gen_op3_i32(INDEX_op_rotr, ret, arg1, t0); } else { - TCGv_i32 t0, t1; - t0 = tcg_temp_ebb_new_i32(); - t1 = tcg_temp_ebb_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_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); @@ -570,15 +856,18 @@ 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) { - if (TCG_TARGET_HAS_rot_i32) { - tcg_gen_op3_i32(INDEX_op_rotr_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_rotr, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_rotr, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_rotl, TCG_TYPE_I32, 0)) { + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + tcg_gen_neg_i32(t0, arg2); + tcg_gen_op3_i32(INDEX_op_rotl, ret, arg1, t0); + tcg_temp_free_i32(t0); } else { - TCGv_i32 t0, t1; - - t0 = tcg_temp_ebb_new_i32(); - t1 = tcg_temp_ebb_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); tcg_gen_shr_i32(t0, arg1, arg2); - tcg_gen_subfi_i32(t1, 32, arg2); + tcg_gen_neg_i32(t1, arg2); tcg_gen_shl_i32(t1, arg1, t1); tcg_gen_or_i32(ret, t0, t1); tcg_temp_free_i32(t0); @@ -589,12 +878,7 @@ 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) { tcg_debug_assert(arg2 >= 0 && arg2 < 32); - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i32(ret, arg1); - } else { - tcg_gen_rotli_i32(ret, arg1, 32 - arg2); - } + tcg_gen_rotli_i32(ret, arg1, -arg2 & 31); } void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, @@ -612,14 +896,14 @@ void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, tcg_gen_mov_i32(ret, arg2); return; } - if (TCG_TARGET_HAS_deposit_i32 && TCG_TARGET_deposit_i32_valid(ofs, len)) { - tcg_gen_op5ii_i32(INDEX_op_deposit_i32, ret, arg1, arg2, ofs, len); + if (TCG_TARGET_deposit_valid(TCG_TYPE_I32, ofs, len)) { + tcg_gen_op5ii_i32(INDEX_op_deposit, ret, arg1, arg2, ofs, len); return; } t1 = tcg_temp_ebb_new_i32(); - if (TCG_TARGET_HAS_extract2_i32) { + if (tcg_op_supported(INDEX_op_extract2, TCG_TYPE_I32, 0)) { if (ofs + len == 32) { tcg_gen_shli_i32(t1, arg1, len); tcg_gen_extract2_i32(ret, t1, arg2, len); @@ -657,45 +941,24 @@ void tcg_gen_deposit_z_i32(TCGv_i32 ret, TCGv_i32 arg, tcg_gen_shli_i32(ret, arg, ofs); } else if (ofs == 0) { tcg_gen_andi_i32(ret, arg, (1u << len) - 1); - } else if (TCG_TARGET_HAS_deposit_i32 - && TCG_TARGET_deposit_i32_valid(ofs, len)) { + } else if (TCG_TARGET_deposit_valid(TCG_TYPE_I32, ofs, len)) { TCGv_i32 zero = tcg_constant_i32(0); - tcg_gen_op5ii_i32(INDEX_op_deposit_i32, ret, zero, arg, ofs, len); - } else { - /* To help two-operand hosts we prefer to zero-extend first, - which allows ARG to stay live. */ - switch (len) { - case 16: - if (TCG_TARGET_HAS_ext16u_i32) { - tcg_gen_ext16u_i32(ret, arg); - tcg_gen_shli_i32(ret, ret, ofs); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8u_i32) { - tcg_gen_ext8u_i32(ret, arg); - tcg_gen_shli_i32(ret, ret, ofs); - return; - } - break; + tcg_gen_op5ii_i32(INDEX_op_deposit, ret, zero, arg, ofs, len); + } else { + /* + * To help two-operand hosts we prefer to zero-extend first, + * which allows ARG to stay live. + */ + if (TCG_TARGET_extract_valid(TCG_TYPE_I32, 0, len)) { + tcg_gen_extract_i32(ret, arg, 0, len); + tcg_gen_shli_i32(ret, ret, ofs); + return; } /* Otherwise prefer zero-extension over AND for code size. */ - switch (ofs + len) { - case 16: - if (TCG_TARGET_HAS_ext16u_i32) { - tcg_gen_shli_i32(ret, arg, ofs); - tcg_gen_ext16u_i32(ret, ret); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8u_i32) { - tcg_gen_shli_i32(ret, arg, ofs); - tcg_gen_ext8u_i32(ret, ret); - return; - } - break; + if (TCG_TARGET_extract_valid(TCG_TYPE_I32, 0, ofs + len)) { + tcg_gen_shli_i32(ret, arg, ofs); + tcg_gen_extract_i32(ret, ret, 0, ofs + len); + return; } tcg_gen_andi_i32(ret, arg, (1u << len) - 1); tcg_gen_shli_i32(ret, ret, ofs); @@ -715,33 +978,21 @@ void tcg_gen_extract_i32(TCGv_i32 ret, TCGv_i32 arg, tcg_gen_shri_i32(ret, arg, 32 - len); return; } - if (ofs == 0) { - tcg_gen_andi_i32(ret, arg, (1u << len) - 1); + + if (TCG_TARGET_extract_valid(TCG_TYPE_I32, ofs, len)) { + tcg_gen_op4ii_i32(INDEX_op_extract, ret, arg, ofs, len); return; } - - if (TCG_TARGET_HAS_extract_i32 - && TCG_TARGET_extract_i32_valid(ofs, len)) { - tcg_gen_op4ii_i32(INDEX_op_extract_i32, ret, arg, ofs, len); + if (ofs == 0) { + tcg_gen_andi_i32(ret, arg, (1u << len) - 1); return; } /* Assume that zero-extension, if available, is cheaper than a shift. */ - switch (ofs + len) { - case 16: - if (TCG_TARGET_HAS_ext16u_i32) { - tcg_gen_ext16u_i32(ret, arg); - tcg_gen_shri_i32(ret, ret, ofs); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8u_i32) { - tcg_gen_ext8u_i32(ret, arg); - tcg_gen_shri_i32(ret, ret, ofs); - return; - } - break; + if (TCG_TARGET_extract_valid(TCG_TYPE_I32, 0, ofs + len)) { + tcg_gen_op4ii_i32(INDEX_op_extract, ret, arg, 0, ofs + len); + tcg_gen_shri_i32(ret, ret, ofs); + return; } /* ??? Ideally we'd know what values are available for immediate AND. @@ -772,55 +1023,22 @@ void tcg_gen_sextract_i32(TCGv_i32 ret, TCGv_i32 arg, tcg_gen_sari_i32(ret, arg, 32 - len); return; } - if (ofs == 0) { - switch (len) { - case 16: - tcg_gen_ext16s_i32(ret, arg); - return; - case 8: - tcg_gen_ext8s_i32(ret, arg); - return; - } - } - if (TCG_TARGET_HAS_sextract_i32 - && TCG_TARGET_extract_i32_valid(ofs, len)) { - tcg_gen_op4ii_i32(INDEX_op_sextract_i32, ret, arg, ofs, len); + if (TCG_TARGET_sextract_valid(TCG_TYPE_I32, ofs, len)) { + tcg_gen_op4ii_i32(INDEX_op_sextract, ret, arg, ofs, len); return; } /* Assume that sign-extension, if available, is cheaper than a shift. */ - switch (ofs + len) { - case 16: - if (TCG_TARGET_HAS_ext16s_i32) { - tcg_gen_ext16s_i32(ret, arg); - tcg_gen_sari_i32(ret, ret, ofs); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8s_i32) { - tcg_gen_ext8s_i32(ret, arg); - tcg_gen_sari_i32(ret, ret, ofs); - return; - } - break; + if (TCG_TARGET_sextract_valid(TCG_TYPE_I32, 0, ofs + len)) { + tcg_gen_op4ii_i32(INDEX_op_sextract, ret, arg, 0, ofs + len); + tcg_gen_sari_i32(ret, ret, ofs); + return; } - switch (len) { - case 16: - if (TCG_TARGET_HAS_ext16s_i32) { - tcg_gen_shri_i32(ret, arg, ofs); - tcg_gen_ext16s_i32(ret, ret); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8s_i32) { - tcg_gen_shri_i32(ret, arg, ofs); - tcg_gen_ext8s_i32(ret, ret); - return; - } - break; + if (TCG_TARGET_sextract_valid(TCG_TYPE_I32, 0, len)) { + tcg_gen_shri_i32(ret, arg, ofs); + tcg_gen_op4ii_i32(INDEX_op_sextract, ret, ret, 0, len); + return; } tcg_gen_shli_i32(ret, arg, 32 - len - ofs); @@ -841,8 +1059,8 @@ void tcg_gen_extract2_i32(TCGv_i32 ret, TCGv_i32 al, TCGv_i32 ah, tcg_gen_mov_i32(ret, ah); } else if (al == ah) { tcg_gen_rotri_i32(ret, al, ofs); - } else if (TCG_TARGET_HAS_extract2_i32) { - tcg_gen_op4i_i32(INDEX_op_extract2_i32, ret, al, ah, ofs); + } else if (tcg_op_supported(INDEX_op_extract2, TCG_TYPE_I32, 0)) { + tcg_gen_op4i_i32(INDEX_op_extract2, ret, al, ah, ofs); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_shri_i32(t0, al, ofs); @@ -858,62 +1076,90 @@ 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 { + tcg_gen_op6i_i32(INDEX_op_movcond, ret, c1, c2, v1, v2, cond); + } +} + +void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, + TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh) +{ + if (tcg_op_supported(INDEX_op_addci, TCG_TYPE_I32, 0)) { + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + tcg_gen_op3_i32(INDEX_op_addco, t0, al, bl); + tcg_gen_op3_i32(INDEX_op_addci, rh, ah, bh); + tcg_gen_mov_i32(rl, t0); + tcg_temp_free_i32(t0); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); TCGv_i32 t1 = tcg_temp_ebb_new_i32(); - tcg_gen_negsetcond_i32(cond, t0, c1, c2); - tcg_gen_and_i32(t1, v1, t0); - tcg_gen_andc_i32(ret, v2, t0); - tcg_gen_or_i32(ret, ret, t1); + tcg_gen_add_i32(t0, al, bl); + tcg_gen_setcond_i32(TCG_COND_LTU, t1, t0, al); + tcg_gen_add_i32(rh, ah, bh); + tcg_gen_add_i32(rh, rh, t1); + tcg_gen_mov_i32(rl, t0); tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); } } -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_addcio_i32(TCGv_i32 r, TCGv_i32 co, + TCGv_i32 a, TCGv_i32 b, TCGv_i32 ci) { - if (TCG_TARGET_HAS_add2_i32) { - tcg_gen_op6_i32(INDEX_op_add2_i32, rl, rh, al, ah, bl, bh); + if (tcg_op_supported(INDEX_op_addci, TCG_TYPE_I32, 0)) { + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 zero = tcg_constant_i32(0); + TCGv_i32 mone = tcg_constant_i32(-1); + + tcg_gen_op3_i32(INDEX_op_addco, t0, ci, mone); + tcg_gen_op3_i32(INDEX_op_addcio, r, a, b); + tcg_gen_op3_i32(INDEX_op_addci, co, zero, zero); + tcg_temp_free_i32(t0); } else { - 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); - tcg_gen_extr_i64_i32(rl, rh, t0); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + + tcg_gen_add_i32(t0, a, b); + tcg_gen_setcond_i32(TCG_COND_LTU, t1, t0, a); + tcg_gen_add_i32(r, t0, ci); + tcg_gen_setcond_i32(TCG_COND_LTU, t0, r, t0); + tcg_gen_or_i32(co, t0, t1); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); } } void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh) { - if (TCG_TARGET_HAS_sub2_i32) { - tcg_gen_op6_i32(INDEX_op_sub2_i32, rl, rh, al, ah, bl, bh); + if (tcg_op_supported(INDEX_op_subbi, TCG_TYPE_I32, 0)) { + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + tcg_gen_op3_i32(INDEX_op_subbo, t0, al, bl); + tcg_gen_op3_i32(INDEX_op_subbi, rh, ah, bh); + tcg_gen_mov_i32(rl, t0); + tcg_temp_free_i32(t0); } else { - 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); - tcg_gen_extr_i64_i32(rl, rh, t0); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + tcg_gen_sub_i32(t0, al, bl); + tcg_gen_setcond_i32(TCG_COND_LTU, t1, al, bl); + tcg_gen_sub_i32(rh, ah, bh); + tcg_gen_sub_i32(rh, rh, t1); + tcg_gen_mov_i32(rl, t0); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); } } 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) { + if (tcg_op_supported(INDEX_op_mulu2, TCG_TYPE_I32, 0)) { + tcg_gen_op4_i32(INDEX_op_mulu2, rl, rh, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_muluh, TCG_TYPE_I32, 0)) { 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_op3_i32(INDEX_op_mul, t, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_muluh, rh, arg1, arg2); tcg_gen_mov_i32(rl, t); tcg_temp_free_i32(t); } else if (TCG_TARGET_REG_BITS == 64) { @@ -926,18 +1172,18 @@ void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); } else { - qemu_build_not_reached(); + g_assert_not_reached(); } } 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) { + if (tcg_op_supported(INDEX_op_muls2, TCG_TYPE_I32, 0)) { + tcg_gen_op4_i32(INDEX_op_muls2, rl, rh, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_mulsh, TCG_TYPE_I32, 0)) { 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_op3_i32(INDEX_op_mul, t, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_mulsh, rh, arg1, arg2); tcg_gen_mov_i32(rl, t); tcg_temp_free_i32(t); } else if (TCG_TARGET_REG_BITS == 32) { @@ -999,40 +1245,22 @@ 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) { - if (TCG_TARGET_HAS_ext8s_i32) { - tcg_gen_op2_i32(INDEX_op_ext8s_i32, ret, arg); - } else { - tcg_gen_shli_i32(ret, arg, 24); - tcg_gen_sari_i32(ret, ret, 24); - } + tcg_gen_sextract_i32(ret, arg, 0, 8); } void tcg_gen_ext16s_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (TCG_TARGET_HAS_ext16s_i32) { - tcg_gen_op2_i32(INDEX_op_ext16s_i32, ret, arg); - } else { - tcg_gen_shli_i32(ret, arg, 16); - tcg_gen_sari_i32(ret, ret, 16); - } + tcg_gen_sextract_i32(ret, arg, 0, 16); } void tcg_gen_ext8u_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (TCG_TARGET_HAS_ext8u_i32) { - tcg_gen_op2_i32(INDEX_op_ext8u_i32, ret, arg); - } else { - tcg_gen_andi_i32(ret, arg, 0xffu); - } + tcg_gen_extract_i32(ret, arg, 0, 8); } void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (TCG_TARGET_HAS_ext16u_i32) { - tcg_gen_op2_i32(INDEX_op_ext16u_i32, ret, arg); - } else { - tcg_gen_andi_i32(ret, arg, 0xffffu); - } + tcg_gen_extract_i32(ret, arg, 0, 16); } /* @@ -1048,8 +1276,8 @@ void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg, int flags) /* Only one extension flag may be present. */ tcg_debug_assert(!(flags & TCG_BSWAP_OS) || !(flags & TCG_BSWAP_OZ)); - if (TCG_TARGET_HAS_bswap16_i32) { - tcg_gen_op3i_i32(INDEX_op_bswap16_i32, ret, arg, flags); + if (tcg_op_supported(INDEX_op_bswap16, TCG_TYPE_I32, 0)) { + tcg_gen_op3i_i32(INDEX_op_bswap16, ret, arg, flags); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); TCGv_i32 t1 = tcg_temp_ebb_new_i32(); @@ -1085,8 +1313,8 @@ void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg, int flags) */ 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); + if (tcg_op_supported(INDEX_op_bswap32, TCG_TYPE_I32, 0)) { + tcg_gen_op3i_i32(INDEX_op_bswap32, ret, arg, 0); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); TCGv_i32 t1 = tcg_temp_ebb_new_i32(); @@ -1149,154 +1377,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, ret, arg2, offset); +} + +void tcg_gen_ld8s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) +{ + tcg_gen_ldst_op_i32(INDEX_op_ld8s, 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, 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, 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, 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, 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, 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, arg1, arg2, offset); +} -#if TCG_TARGET_REG_BITS == 32 -/* These are all inline for TCG_TARGET_REG_BITS == 64. */ + +/* 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, TCG_TYPE_I64, 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, 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, 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, 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, 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, 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, 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, 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, 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) { - tcg_gen_st8_i32(TCGV_LOW(arg1), arg2, offset); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_st8, 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) { - tcg_gen_st16_i32(TCGV_LOW(arg1), arg2, offset); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_st16, 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) { - tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_ldst_op_i64(INDEX_op_st32, 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, 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) { - tcg_gen_add2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1), - TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2)); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_add, 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) { - tcg_gen_sub2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1), - TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2)); + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_sub, 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, 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, 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, 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, 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, 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, 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) @@ -1304,6 +1659,12 @@ void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) TCGv_i64 t0; TCGv_i32 t1; + if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op3_i64(INDEX_op_mul, ret, arg1, arg2); + return; + } + + t0 = tcg_temp_ebb_new_i64(); t1 = tcg_temp_ebb_new_i32(); @@ -1320,15 +1681,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 */ @@ -1345,9 +1697,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 { @@ -1359,15 +1710,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, 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)); } } @@ -1387,23 +1740,19 @@ void tcg_gen_andi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) case -1: tcg_gen_mov_i64(ret, arg1); return; - case 0xff: - /* Don't recurse with tcg_gen_ext8u_i64. */ - if (TCG_TARGET_HAS_ext8u_i64) { - tcg_gen_op2_i64(INDEX_op_ext8u_i64, ret, arg1); - return; - } - break; - case 0xffff: - if (TCG_TARGET_HAS_ext16u_i64) { - tcg_gen_op2_i64(INDEX_op_ext16u_i64, ret, arg1); - return; - } - break; - case 0xffffffffu: - if (TCG_TARGET_HAS_ext32u_i64) { - tcg_gen_op2_i64(INDEX_op_ext32u_i64, ret, arg1); - return; + default: + /* + * Canonicalize on extract, if valid. This aids x86 with its + * 2 operand MOVZBL and 2 operand AND, selecting the TCGOpcode + * which does not require matching operands. Other backends can + * trivially expand the extract to AND during code generation. + */ + if (!(arg2 & (arg2 + 1))) { + unsigned len = ctz64(~arg2); + if (TCG_TARGET_extract_valid(TCG_TYPE_I64, 0, len)) { + tcg_gen_extract_i64(ret, arg1, 0, len); + return; + } } break; } @@ -1438,9 +1787,10 @@ void tcg_gen_xori_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 (arg2 == -1 && TCG_TARGET_HAS_not_i64) { + } else if (arg2 == -1 && + tcg_op_supported(INDEX_op_not, TCG_TYPE_I64, 0)) { /* Don't recurse with tcg_gen_not_i64. */ - tcg_gen_op2_i64(INDEX_op_not_i64, ret, arg1); + tcg_gen_op2_i64(INDEX_op_not, ret, arg1); } else { tcg_gen_xor_i64(ret, arg1, tcg_constant_i64(arg2)); } @@ -1468,7 +1818,7 @@ static inline void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1, tcg_gen_movi_i32(TCGV_LOW(ret), 0); } } else if (right) { - if (TCG_TARGET_HAS_extract2_i32) { + if (tcg_op_supported(INDEX_op_extract2, TCG_TYPE_I32, 0)) { tcg_gen_extract2_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_HIGH(arg1), c); } else { @@ -1482,7 +1832,7 @@ static inline void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1, tcg_gen_shri_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), c); } } else { - if (TCG_TARGET_HAS_extract2_i32) { + if (tcg_op_supported(INDEX_op_extract2, TCG_TYPE_I32, 0)) { tcg_gen_extract2_i32(TCGV_HIGH(ret), TCGV_LOW(arg1), TCGV_HIGH(arg1), 32 - c); } else { @@ -1537,15 +1887,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) { + 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, arg1, arg2, cond, + label_arg(l)); } - add_last_as_label_use(l); + add_as_label_use(l, op); } } @@ -1556,12 +1907,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) { - tcg_gen_op6ii_i32(INDEX_op_brcond2_i32, - TCGV_LOW(arg1), TCGV_HIGH(arg1), - tcg_constant_i32(arg2), - tcg_constant_i32(arg2 >> 32), - cond, label_arg(l)); - add_last_as_label_use(l); + 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); } } @@ -1579,7 +1930,7 @@ void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, TCGV_LOW(arg2), TCGV_HIGH(arg2), cond); tcg_gen_movi_i32(TCGV_HIGH(ret), 0); } else { - tcg_gen_op4i_i64(INDEX_op_setcond_i64, ret, arg1, arg2, cond); + tcg_gen_op4i_i64(INDEX_op_setcond, ret, arg1, arg2, cond); } } } @@ -1602,6 +1953,12 @@ 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) { @@ -1609,17 +1966,14 @@ void tcg_gen_negsetcond_i64(TCGCond cond, TCGv_i64 ret, 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) { + } else if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op4i_i64(INDEX_op_negsetcond, ret, arg1, arg2, cond); + } else { 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); } } @@ -1636,12 +1990,12 @@ 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) { - if (TCG_TARGET_HAS_div_i64) { - tcg_gen_op3_i64(INDEX_op_div_i64, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div2_i64) { + if (tcg_op_supported(INDEX_op_divs, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_divs, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_divs2, TCG_TYPE_I64, 0)) { 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_gen_op5_i64(INDEX_op_divs2, ret, t0, arg1, t0, arg2); tcg_temp_free_i64(t0); } else { gen_helper_div_i64(ret, arg1, arg2); @@ -1650,18 +2004,18 @@ 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) { - if (TCG_TARGET_HAS_rem_i64) { - tcg_gen_op3_i64(INDEX_op_rem_i64, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div_i64) { + if (tcg_op_supported(INDEX_op_rems, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_rems, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_divs, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); - tcg_gen_op3_i64(INDEX_op_div_i64, t0, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_divs, 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) { + } else if (tcg_op_supported(INDEX_op_divs2, TCG_TYPE_I64, 0)) { 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_gen_op5_i64(INDEX_op_divs2, t0, ret, arg1, t0, arg2); tcg_temp_free_i64(t0); } else { gen_helper_rem_i64(ret, arg1, arg2); @@ -1670,12 +2024,12 @@ 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) { - if (TCG_TARGET_HAS_div_i64) { - tcg_gen_op3_i64(INDEX_op_divu_i64, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div2_i64) { + if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_divu, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_divu2, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); - tcg_gen_movi_i64(t0, 0); - tcg_gen_op5_i64(INDEX_op_divu2_i64, ret, t0, arg1, t0, arg2); + TCGv_i64 zero = tcg_constant_i64(0); + tcg_gen_op5_i64(INDEX_op_divu2, ret, t0, arg1, zero, arg2); tcg_temp_free_i64(t0); } else { gen_helper_divu_i64(ret, arg1, arg2); @@ -1684,18 +2038,18 @@ 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) { - if (TCG_TARGET_HAS_rem_i64) { - tcg_gen_op3_i64(INDEX_op_remu_i64, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div_i64) { + if (tcg_op_supported(INDEX_op_remu, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_remu, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); - tcg_gen_op3_i64(INDEX_op_divu_i64, t0, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_divu, 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) { + } else if (tcg_op_supported(INDEX_op_divu2, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); - tcg_gen_movi_i64(t0, 0); - tcg_gen_op5_i64(INDEX_op_divu2_i64, t0, ret, arg1, t0, arg2); + TCGv_i64 zero = tcg_constant_i64(0); + tcg_gen_op5_i64(INDEX_op_divu2, t0, ret, arg1, zero, arg2); tcg_temp_free_i64(t0); } else { gen_helper_remu_i64(ret, arg1, arg2); @@ -1704,77 +2058,32 @@ void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_ext8s_i64(TCGv_i64 ret, TCGv_i64 arg) { - if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_ext8s_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); - } else if (TCG_TARGET_HAS_ext8s_i64) { - tcg_gen_op2_i64(INDEX_op_ext8s_i64, ret, arg); - } else { - tcg_gen_shli_i64(ret, arg, 56); - tcg_gen_sari_i64(ret, ret, 56); - } + tcg_gen_sextract_i64(ret, arg, 0, 8); } void tcg_gen_ext16s_i64(TCGv_i64 ret, TCGv_i64 arg) { - if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_ext16s_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); - } else if (TCG_TARGET_HAS_ext16s_i64) { - tcg_gen_op2_i64(INDEX_op_ext16s_i64, ret, arg); - } else { - tcg_gen_shli_i64(ret, arg, 48); - tcg_gen_sari_i64(ret, ret, 48); - } + tcg_gen_sextract_i64(ret, arg, 0, 16); } void tcg_gen_ext32s_i64(TCGv_i64 ret, TCGv_i64 arg) { - if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); - } else if (TCG_TARGET_HAS_ext32s_i64) { - tcg_gen_op2_i64(INDEX_op_ext32s_i64, ret, arg); - } else { - tcg_gen_shli_i64(ret, arg, 32); - tcg_gen_sari_i64(ret, ret, 32); - } + tcg_gen_sextract_i64(ret, arg, 0, 32); } void tcg_gen_ext8u_i64(TCGv_i64 ret, TCGv_i64 arg) { - if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_ext8u_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); - } else if (TCG_TARGET_HAS_ext8u_i64) { - tcg_gen_op2_i64(INDEX_op_ext8u_i64, ret, arg); - } else { - tcg_gen_andi_i64(ret, arg, 0xffu); - } + tcg_gen_extract_i64(ret, arg, 0, 8); } void tcg_gen_ext16u_i64(TCGv_i64 ret, TCGv_i64 arg) { - if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_ext16u_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); - } else if (TCG_TARGET_HAS_ext16u_i64) { - tcg_gen_op2_i64(INDEX_op_ext16u_i64, ret, arg); - } else { - tcg_gen_andi_i64(ret, arg, 0xffffu); - } + tcg_gen_extract_i64(ret, arg, 0, 16); } void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg) { - if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); - } else if (TCG_TARGET_HAS_ext32u_i64) { - tcg_gen_op2_i64(INDEX_op_ext32u_i64, ret, arg); - } else { - tcg_gen_andi_i64(ret, arg, 0xffffffffu); - } + tcg_gen_extract_i64(ret, arg, 0, 32); } /* @@ -1797,8 +2106,8 @@ void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) } else { tcg_gen_movi_i32(TCGV_HIGH(ret), 0); } - } else if (TCG_TARGET_HAS_bswap16_i64) { - tcg_gen_op3i_i64(INDEX_op_bswap16_i64, ret, arg, flags); + } else if (tcg_op_supported(INDEX_op_bswap16, TCG_TYPE_I64, 0)) { + tcg_gen_op3i_i64(INDEX_op_bswap16, ret, arg, flags); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); @@ -1847,8 +2156,8 @@ void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) } else { tcg_gen_movi_i32(TCGV_HIGH(ret), 0); } - } else if (TCG_TARGET_HAS_bswap32_i64) { - tcg_gen_op3i_i64(INDEX_op_bswap32_i64, ret, arg, flags); + } else if (tcg_op_supported(INDEX_op_bswap32, TCG_TYPE_I64, 0)) { + tcg_gen_op3i_i64(INDEX_op_bswap32, ret, arg, flags); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); @@ -1894,8 +2203,8 @@ void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) tcg_gen_mov_i32(TCGV_HIGH(ret), t0); tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); - } else if (TCG_TARGET_HAS_bswap64_i64) { - tcg_gen_op3i_i64(INDEX_op_bswap64_i64, ret, arg, 0); + } else if (tcg_op_supported(INDEX_op_bswap64, TCG_TYPE_I64, 0)) { + tcg_gen_op3i_i64(INDEX_op_bswap64, ret, arg, 0); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); @@ -1966,8 +2275,8 @@ void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_not_i32(TCGV_LOW(ret), TCGV_LOW(arg)); tcg_gen_not_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); - } else if (TCG_TARGET_HAS_not_i64) { - tcg_gen_op2_i64(INDEX_op_not_i64, ret, arg); + } else if (tcg_op_supported(INDEX_op_not, TCG_TYPE_I64, 0)) { + tcg_gen_op2_i64(INDEX_op_not, ret, arg); } else { tcg_gen_xori_i64(ret, arg, -1); } @@ -1978,8 +2287,8 @@ void tcg_gen_andc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_andc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_andc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); - } else if (TCG_TARGET_HAS_andc_i64) { - tcg_gen_op3_i64(INDEX_op_andc_i64, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_andc, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_andc, ret, arg1, arg2); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_not_i64(t0, arg2); @@ -1993,8 +2302,8 @@ void tcg_gen_eqv_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_eqv_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_eqv_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); - } else if (TCG_TARGET_HAS_eqv_i64) { - tcg_gen_op3_i64(INDEX_op_eqv_i64, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_eqv, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_eqv, ret, arg1, arg2); } else { tcg_gen_xor_i64(ret, arg1, arg2); tcg_gen_not_i64(ret, ret); @@ -2006,8 +2315,8 @@ void tcg_gen_nand_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_nand_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_nand_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); - } else if (TCG_TARGET_HAS_nand_i64) { - tcg_gen_op3_i64(INDEX_op_nand_i64, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_nand, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_nand, ret, arg1, arg2); } else { tcg_gen_and_i64(ret, arg1, arg2); tcg_gen_not_i64(ret, ret); @@ -2019,8 +2328,8 @@ void tcg_gen_nor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_nor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_nor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); - } else if (TCG_TARGET_HAS_nor_i64) { - tcg_gen_op3_i64(INDEX_op_nor_i64, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_nor, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_nor, ret, arg1, arg2); } else { tcg_gen_or_i64(ret, arg1, arg2); tcg_gen_not_i64(ret, ret); @@ -2032,8 +2341,8 @@ void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_orc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_orc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); - } else if (TCG_TARGET_HAS_orc_i64) { - tcg_gen_op3_i64(INDEX_op_orc_i64, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_orc, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_orc, ret, arg1, arg2); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_not_i64(t0, arg2); @@ -2044,8 +2353,8 @@ 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) { - if (TCG_TARGET_HAS_clz_i64) { - tcg_gen_op3_i64(INDEX_op_clz_i64, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_clz, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_clz, ret, arg1, arg2); } else { gen_helper_clz_i64(ret, arg1, arg2); } @@ -2054,8 +2363,8 @@ void tcg_gen_clz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) 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) { + && arg2 <= 0xffffffffu + && tcg_op_supported(INDEX_op_clz, TCG_TYPE_I32, 0)) { 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); @@ -2069,45 +2378,47 @@ void tcg_gen_clzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) 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_ebb_new_i64(); + TCGv_i64 z, t; - if (TCG_TARGET_HAS_ctpop_i64) { - tcg_gen_subi_i64(t, arg1, 1); - tcg_gen_andc_i64(t, t, arg1); - tcg_gen_ctpop_i64(t, t); - } else { - /* Since all non-x86 hosts have clz(0) == 64, don't fight it. */ - tcg_gen_neg_i64(t, arg1); - tcg_gen_and_i64(t, t, arg1); - tcg_gen_clzi_i64(t, t, 64); - tcg_gen_xori_i64(t, t, 63); - } - z = tcg_constant_i64(0); - tcg_gen_movcond_i64(TCG_COND_EQ, ret, arg1, z, arg2, t); - tcg_temp_free_i64(t); - tcg_temp_free_i64(z); + if (tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_ctz, ret, arg1, arg2); + return; + } + if (tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_I64, 0)) { + t = tcg_temp_ebb_new_i64(); + tcg_gen_subi_i64(t, arg1, 1); + tcg_gen_andc_i64(t, t, arg1); + tcg_gen_ctpop_i64(t, t); + } else if (tcg_op_supported(INDEX_op_clz, TCG_TYPE_I64, 0)) { + t = tcg_temp_ebb_new_i64(); + tcg_gen_neg_i64(t, arg1); + tcg_gen_and_i64(t, t, arg1); + tcg_gen_clzi_i64(t, t, 64); + tcg_gen_xori_i64(t, t, 63); } else { gen_helper_ctz_i64(ret, arg1, arg2); + return; } + + z = tcg_constant_i64(0); + tcg_gen_movcond_i64(TCG_COND_EQ, ret, arg1, z, arg2, t); + tcg_temp_free_i64(t); } 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) { + && arg2 <= 0xffffffffu + && tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I32, 0)) { 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); tcg_gen_movi_i32(TCGV_HIGH(ret), 0); tcg_temp_free_i32(t32); - } else if (!TCG_TARGET_HAS_ctz_i64 - && TCG_TARGET_HAS_ctpop_i64 - && arg2 == 64) { + } else if (arg2 == 64 + && !tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I64, 0) + && tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_I64, 0)) { /* This equivalence has the advantage of not requiring a fixup. */ TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_subi_i64(t, arg1, 1); @@ -2121,7 +2432,7 @@ 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) { - if (TCG_TARGET_HAS_clz_i64 || TCG_TARGET_HAS_clz_i32) { + if (tcg_op_supported(INDEX_op_clz, TCG_TYPE_I64, 0)) { TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_sari_i64(t, arg, 63); tcg_gen_xor_i64(t, t, arg); @@ -2135,28 +2446,37 @@ void tcg_gen_clrsb_i64(TCGv_i64 ret, TCGv_i64 arg) void tcg_gen_ctpop_i64(TCGv_i64 ret, TCGv_i64 arg1) { - if (TCG_TARGET_HAS_ctpop_i64) { - tcg_gen_op2_i64(INDEX_op_ctpop_i64, ret, arg1); - } else if (TCG_TARGET_REG_BITS == 32 && TCG_TARGET_HAS_ctpop_i32) { - tcg_gen_ctpop_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1)); - tcg_gen_ctpop_i32(TCGV_LOW(ret), TCGV_LOW(arg1)); - tcg_gen_add_i32(TCGV_LOW(ret), TCGV_LOW(ret), TCGV_HIGH(ret)); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + if (TCG_TARGET_REG_BITS == 64) { + if (tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_I64, 0)) { + tcg_gen_op2_i64(INDEX_op_ctpop, ret, arg1); + return; + } } else { - gen_helper_ctpop_i64(ret, arg1); + if (tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_I32, 0)) { + tcg_gen_ctpop_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1)); + tcg_gen_ctpop_i32(TCGV_LOW(ret), TCGV_LOW(arg1)); + tcg_gen_add_i32(TCGV_LOW(ret), TCGV_LOW(ret), TCGV_HIGH(ret)); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + return; + } } + gen_helper_ctpop_i64(ret, arg1); } void tcg_gen_rotl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - if (TCG_TARGET_HAS_rot_i64) { - tcg_gen_op3_i64(INDEX_op_rotl_i64, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_rotl, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_rotl, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_rotl, TCG_TYPE_I64, 0)) { + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + tcg_gen_neg_i64(t0, arg2); + tcg_gen_op3_i64(INDEX_op_rotr, ret, arg1, t0); + tcg_temp_free_i64(t0); } else { - TCGv_i64 t0, t1; - t0 = tcg_temp_ebb_new_i64(); - t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); tcg_gen_shl_i64(t0, arg1, arg2); - tcg_gen_subfi_i64(t1, 64, arg2); + tcg_gen_neg_i64(t1, arg2); tcg_gen_shr_i64(t1, arg1, t1); tcg_gen_or_i64(ret, t0, t1); tcg_temp_free_i64(t0); @@ -2170,12 +2490,15 @@ void tcg_gen_rotli_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_HAS_rot_i64) { - tcg_gen_rotl_i64(ret, arg1, tcg_constant_i64(arg2)); + } else if (tcg_op_supported(INDEX_op_rotl, TCG_TYPE_I64, 0)) { + TCGv_i64 t0 = tcg_constant_i64(arg2); + tcg_gen_op3_i64(INDEX_op_rotl, ret, arg1, t0); + } else if (tcg_op_supported(INDEX_op_rotr, TCG_TYPE_I64, 0)) { + TCGv_i64 t0 = tcg_constant_i64(64 - arg2); + tcg_gen_op3_i64(INDEX_op_rotr, ret, arg1, t0); } else { - TCGv_i64 t0, t1; - t0 = tcg_temp_ebb_new_i64(); - t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_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); @@ -2186,14 +2509,18 @@ 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) { - if (TCG_TARGET_HAS_rot_i64) { - tcg_gen_op3_i64(INDEX_op_rotr_i64, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_rotr, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_rotr, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_rotl, TCG_TYPE_I64, 0)) { + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + tcg_gen_neg_i64(t0, arg2); + tcg_gen_op3_i64(INDEX_op_rotl, ret, arg1, t0); + tcg_temp_free_i64(t0); } else { - TCGv_i64 t0, t1; - t0 = tcg_temp_ebb_new_i64(); - t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); tcg_gen_shr_i64(t0, arg1, arg2); - tcg_gen_subfi_i64(t1, 64, arg2); + tcg_gen_neg_i64(t1, arg2); tcg_gen_shl_i64(t1, arg1, t1); tcg_gen_or_i64(ret, t0, t1); tcg_temp_free_i64(t0); @@ -2204,12 +2531,7 @@ 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) { tcg_debug_assert(arg2 >= 0 && arg2 < 64); - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i64(ret, arg1); - } else { - tcg_gen_rotli_i64(ret, arg1, 64 - arg2); - } + tcg_gen_rotli_i64(ret, arg1, -arg2 & 63); } void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2, @@ -2227,12 +2549,13 @@ void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2, tcg_gen_mov_i64(ret, arg2); return; } - if (TCG_TARGET_HAS_deposit_i64 && TCG_TARGET_deposit_i64_valid(ofs, len)) { - tcg_gen_op5ii_i64(INDEX_op_deposit_i64, ret, arg1, arg2, ofs, len); - return; - } - if (TCG_TARGET_REG_BITS == 32) { + if (TCG_TARGET_REG_BITS == 64) { + if (TCG_TARGET_deposit_valid(TCG_TYPE_I64, ofs, len)) { + tcg_gen_op5ii_i64(INDEX_op_deposit, ret, arg1, arg2, ofs, len); + return; + } + } else { if (ofs >= 32) { tcg_gen_deposit_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_LOW(arg2), ofs - 32, len); @@ -2249,7 +2572,7 @@ void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2, t1 = tcg_temp_ebb_new_i64(); - if (TCG_TARGET_HAS_extract2_i64) { + if (tcg_op_supported(INDEX_op_extract2, TCG_TYPE_I64, 0)) { if (ofs + len == 64) { tcg_gen_shli_i64(t1, arg1, len); tcg_gen_extract2_i64(ret, t1, arg2, len); @@ -2287,10 +2610,10 @@ void tcg_gen_deposit_z_i64(TCGv_i64 ret, TCGv_i64 arg, tcg_gen_shli_i64(ret, arg, ofs); } else if (ofs == 0) { tcg_gen_andi_i64(ret, arg, (1ull << len) - 1); - } else if (TCG_TARGET_HAS_deposit_i64 - && TCG_TARGET_deposit_i64_valid(ofs, len)) { + } else if (TCG_TARGET_REG_BITS == 64 && + TCG_TARGET_deposit_valid(TCG_TYPE_I64, ofs, len)) { TCGv_i64 zero = tcg_constant_i64(0); - tcg_gen_op5ii_i64(INDEX_op_deposit_i64, ret, zero, arg, ofs, len); + tcg_gen_op5ii_i64(INDEX_op_deposit, ret, zero, arg, ofs, len); } else { if (TCG_TARGET_REG_BITS == 32) { if (ofs >= 32) { @@ -2305,54 +2628,20 @@ void tcg_gen_deposit_z_i64(TCGv_i64 ret, TCGv_i64 arg, return; } } - /* To help two-operand hosts we prefer to zero-extend first, - which allows ARG to stay live. */ - switch (len) { - case 32: - if (TCG_TARGET_HAS_ext32u_i64) { - tcg_gen_ext32u_i64(ret, arg); - tcg_gen_shli_i64(ret, ret, ofs); - return; - } - break; - case 16: - if (TCG_TARGET_HAS_ext16u_i64) { - tcg_gen_ext16u_i64(ret, arg); - tcg_gen_shli_i64(ret, ret, ofs); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8u_i64) { - tcg_gen_ext8u_i64(ret, arg); - tcg_gen_shli_i64(ret, ret, ofs); - return; - } - break; + /* + * To help two-operand hosts we prefer to zero-extend first, + * which allows ARG to stay live. + */ + if (TCG_TARGET_extract_valid(TCG_TYPE_I64, 0, len)) { + tcg_gen_extract_i64(ret, arg, 0, len); + tcg_gen_shli_i64(ret, ret, ofs); + return; } /* Otherwise prefer zero-extension over AND for code size. */ - switch (ofs + len) { - case 32: - if (TCG_TARGET_HAS_ext32u_i64) { - tcg_gen_shli_i64(ret, arg, ofs); - tcg_gen_ext32u_i64(ret, ret); - return; - } - break; - case 16: - if (TCG_TARGET_HAS_ext16u_i64) { - tcg_gen_shli_i64(ret, arg, ofs); - tcg_gen_ext16u_i64(ret, ret); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8u_i64) { - tcg_gen_shli_i64(ret, arg, ofs); - tcg_gen_ext8u_i64(ret, ret); - return; - } - break; + if (TCG_TARGET_extract_valid(TCG_TYPE_I64, 0, ofs + len)) { + tcg_gen_shli_i64(ret, arg, ofs); + tcg_gen_extract_i64(ret, ret, 0, ofs + len); + return; } tcg_gen_andi_i64(ret, arg, (1ull << len) - 1); tcg_gen_shli_i64(ret, ret, ofs); @@ -2372,10 +2661,6 @@ void tcg_gen_extract_i64(TCGv_i64 ret, TCGv_i64 arg, tcg_gen_shri_i64(ret, arg, 64 - len); return; } - if (ofs == 0) { - tcg_gen_andi_i64(ret, arg, (1ull << len) - 1); - return; - } if (TCG_TARGET_REG_BITS == 32) { /* Look for a 32-bit extract within one of the two words. */ @@ -2389,40 +2674,34 @@ void tcg_gen_extract_i64(TCGv_i64 ret, TCGv_i64 arg, tcg_gen_movi_i32(TCGV_HIGH(ret), 0); return; } - /* The field is split across two words. One double-word - shift is better than two double-word shifts. */ - goto do_shift_and; + + /* The field is split across two words. */ + tcg_gen_extract2_i32(TCGV_LOW(ret), TCGV_LOW(arg), + TCGV_HIGH(arg), ofs); + if (len <= 32) { + tcg_gen_extract_i32(TCGV_LOW(ret), TCGV_LOW(ret), 0, len); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } else { + tcg_gen_extract_i32(TCGV_HIGH(ret), TCGV_HIGH(arg), + ofs, len - 32); + } + return; } - if (TCG_TARGET_HAS_extract_i64 - && TCG_TARGET_extract_i64_valid(ofs, len)) { - tcg_gen_op4ii_i64(INDEX_op_extract_i64, ret, arg, ofs, len); + if (TCG_TARGET_extract_valid(TCG_TYPE_I64, ofs, len)) { + tcg_gen_op4ii_i64(INDEX_op_extract, ret, arg, ofs, len); + return; + } + if (ofs == 0) { + tcg_gen_andi_i64(ret, arg, (1ull << len) - 1); return; } /* Assume that zero-extension, if available, is cheaper than a shift. */ - switch (ofs + len) { - case 32: - if (TCG_TARGET_HAS_ext32u_i64) { - tcg_gen_ext32u_i64(ret, arg); - tcg_gen_shri_i64(ret, ret, ofs); - return; - } - break; - case 16: - if (TCG_TARGET_HAS_ext16u_i64) { - tcg_gen_ext16u_i64(ret, arg); - tcg_gen_shri_i64(ret, ret, ofs); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8u_i64) { - tcg_gen_ext8u_i64(ret, arg); - tcg_gen_shri_i64(ret, ret, ofs); - return; - } - break; + if (TCG_TARGET_extract_valid(TCG_TYPE_I64, 0, ofs + len)) { + tcg_gen_op4ii_i64(INDEX_op_extract, ret, arg, 0, ofs + len); + tcg_gen_shri_i64(ret, ret, ofs); + return; } /* ??? Ideally we'd know what values are available for immediate AND. @@ -2430,7 +2709,6 @@ void tcg_gen_extract_i64(TCGv_i64 ret, TCGv_i64 arg, so that we get ext8u, ext16u, and ext32u. */ switch (len) { case 1 ... 8: case 16: case 32: - do_shift_and: tcg_gen_shri_i64(ret, arg, ofs); tcg_gen_andi_i64(ret, ret, (1ull << len) - 1); break; @@ -2454,19 +2732,6 @@ void tcg_gen_sextract_i64(TCGv_i64 ret, TCGv_i64 arg, tcg_gen_sari_i64(ret, arg, 64 - len); return; } - if (ofs == 0) { - switch (len) { - case 32: - tcg_gen_ext32s_i64(ret, arg); - return; - case 16: - tcg_gen_ext16s_i64(ret, arg); - return; - case 8: - tcg_gen_ext8s_i64(ret, arg); - return; - } - } if (TCG_TARGET_REG_BITS == 32) { /* Look for a 32-bit extract within one of the two words. */ @@ -2500,59 +2765,23 @@ void tcg_gen_sextract_i64(TCGv_i64 ret, TCGv_i64 arg, return; } - if (TCG_TARGET_HAS_sextract_i64 - && TCG_TARGET_extract_i64_valid(ofs, len)) { - tcg_gen_op4ii_i64(INDEX_op_sextract_i64, ret, arg, ofs, len); + if (TCG_TARGET_sextract_valid(TCG_TYPE_I64, ofs, len)) { + tcg_gen_op4ii_i64(INDEX_op_sextract, ret, arg, ofs, len); return; } /* Assume that sign-extension, if available, is cheaper than a shift. */ - switch (ofs + len) { - case 32: - if (TCG_TARGET_HAS_ext32s_i64) { - tcg_gen_ext32s_i64(ret, arg); - tcg_gen_sari_i64(ret, ret, ofs); - return; - } - break; - case 16: - if (TCG_TARGET_HAS_ext16s_i64) { - tcg_gen_ext16s_i64(ret, arg); - tcg_gen_sari_i64(ret, ret, ofs); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8s_i64) { - tcg_gen_ext8s_i64(ret, arg); - tcg_gen_sari_i64(ret, ret, ofs); - return; - } - break; + if (TCG_TARGET_sextract_valid(TCG_TYPE_I64, 0, ofs + len)) { + tcg_gen_op4ii_i64(INDEX_op_sextract, ret, arg, 0, ofs + len); + tcg_gen_sari_i64(ret, ret, ofs); + return; } - switch (len) { - case 32: - if (TCG_TARGET_HAS_ext32s_i64) { - tcg_gen_shri_i64(ret, arg, ofs); - tcg_gen_ext32s_i64(ret, ret); - return; - } - break; - case 16: - if (TCG_TARGET_HAS_ext16s_i64) { - tcg_gen_shri_i64(ret, arg, ofs); - tcg_gen_ext16s_i64(ret, ret); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8s_i64) { - tcg_gen_shri_i64(ret, arg, ofs); - tcg_gen_ext8s_i64(ret, ret); - return; - } - break; + if (TCG_TARGET_sextract_valid(TCG_TYPE_I64, 0, len)) { + tcg_gen_shri_i64(ret, arg, ofs); + tcg_gen_op4ii_i64(INDEX_op_sextract, ret, ret, 0, len); + return; } + tcg_gen_shli_i64(ret, arg, 64 - len - ofs); tcg_gen_sari_i64(ret, ret, 64 - len); } @@ -2571,8 +2800,8 @@ void tcg_gen_extract2_i64(TCGv_i64 ret, TCGv_i64 al, TCGv_i64 ah, tcg_gen_mov_i64(ret, ah); } else if (al == ah) { tcg_gen_rotri_i64(ret, al, ofs); - } else if (TCG_TARGET_HAS_extract2_i64) { - tcg_gen_op4i_i64(INDEX_op_extract2_i64, ret, al, ah, ofs); + } else if (tcg_op_supported(INDEX_op_extract2, TCG_TYPE_I64, 0)) { + tcg_gen_op4i_i64(INDEX_op_extract2, ret, al, ah, ofs); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_shri_i64(t0, al, ofs); @@ -2588,51 +2817,47 @@ 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) { + } else if (TCG_TARGET_REG_BITS == 64) { + tcg_gen_op6i_i64(INDEX_op_movcond, ret, c1, c2, v1, v2, cond); + } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); - TCGv_i32 t1 = 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_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_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_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_ebb_new_i64(); - TCGv_i64 t1 = tcg_temp_ebb_new_i64(); - tcg_gen_negsetcond_i64(cond, t0, c1, c2); - 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); } } void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh) { - if (TCG_TARGET_HAS_add2_i64) { - tcg_gen_op6_i64(INDEX_op_add2_i64, rl, rh, al, ah, bl, bh); + if (tcg_op_supported(INDEX_op_addci, TCG_TYPE_REG, 0)) { + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_op3_i32(INDEX_op_addco, TCGV_LOW(t0), + TCGV_LOW(al), TCGV_LOW(bl)); + tcg_gen_op3_i32(INDEX_op_addcio, TCGV_HIGH(t0), + TCGV_HIGH(al), TCGV_HIGH(bl)); + tcg_gen_op3_i32(INDEX_op_addcio, TCGV_LOW(rh), + TCGV_LOW(ah), TCGV_LOW(bh)); + tcg_gen_op3_i32(INDEX_op_addci, TCGV_HIGH(rh), + TCGV_HIGH(ah), TCGV_HIGH(bh)); + } else { + tcg_gen_op3_i64(INDEX_op_addco, t0, al, bl); + tcg_gen_op3_i64(INDEX_op_addci, rh, ah, bh); + } + + tcg_gen_mov_i64(rl, t0); + tcg_temp_free_i64(t0); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); @@ -2646,11 +2871,96 @@ void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, } } +void tcg_gen_addcio_i64(TCGv_i64 r, TCGv_i64 co, + TCGv_i64 a, TCGv_i64 b, TCGv_i64 ci) +{ + if (TCG_TARGET_REG_BITS == 64) { + if (tcg_op_supported(INDEX_op_addci, TCG_TYPE_I64, 0)) { + TCGv_i64 discard = tcg_temp_ebb_new_i64(); + TCGv_i64 zero = tcg_constant_i64(0); + TCGv_i64 mone = tcg_constant_i64(-1); + + tcg_gen_op3_i64(INDEX_op_addco, discard, ci, mone); + tcg_gen_op3_i64(INDEX_op_addcio, r, a, b); + tcg_gen_op3_i64(INDEX_op_addci, co, zero, zero); + tcg_temp_free_i64(discard); + } else { + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + + tcg_gen_add_i64(t0, a, b); + tcg_gen_setcond_i64(TCG_COND_LTU, t1, t0, a); + tcg_gen_add_i64(r, t0, ci); + tcg_gen_setcond_i64(TCG_COND_LTU, t0, r, t0); + tcg_gen_or_i64(co, t0, t1); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } + } else { + if (tcg_op_supported(INDEX_op_addci, TCG_TYPE_I32, 0)) { + TCGv_i32 discard = tcg_temp_ebb_new_i32(); + TCGv_i32 zero = tcg_constant_i32(0); + TCGv_i32 mone = tcg_constant_i32(-1); + + tcg_gen_op3_i32(INDEX_op_addco, discard, TCGV_LOW(ci), mone); + tcg_gen_op3_i32(INDEX_op_addcio, discard, TCGV_HIGH(ci), mone); + tcg_gen_op3_i32(INDEX_op_addcio, TCGV_LOW(r), + TCGV_LOW(a), TCGV_LOW(b)); + tcg_gen_op3_i32(INDEX_op_addcio, TCGV_HIGH(r), + TCGV_HIGH(a), TCGV_HIGH(b)); + tcg_gen_op3_i32(INDEX_op_addci, TCGV_LOW(co), zero, zero); + tcg_temp_free_i32(discard); + } else { + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 c0 = tcg_temp_ebb_new_i32(); + TCGv_i32 c1 = tcg_temp_ebb_new_i32(); + + tcg_gen_or_i32(c1, TCGV_LOW(ci), TCGV_HIGH(ci)); + tcg_gen_setcondi_i32(TCG_COND_NE, c1, c1, 0); + + tcg_gen_add_i32(t0, TCGV_LOW(a), TCGV_LOW(b)); + tcg_gen_setcond_i32(TCG_COND_LTU, c0, t0, TCGV_LOW(a)); + tcg_gen_add_i32(TCGV_LOW(r), t0, c1); + tcg_gen_setcond_i32(TCG_COND_LTU, c1, TCGV_LOW(r), c1); + tcg_gen_or_i32(c1, c1, c0); + + tcg_gen_add_i32(t0, TCGV_HIGH(a), TCGV_HIGH(b)); + tcg_gen_setcond_i32(TCG_COND_LTU, c0, t0, TCGV_HIGH(a)); + tcg_gen_add_i32(TCGV_HIGH(r), t0, c1); + tcg_gen_setcond_i32(TCG_COND_LTU, c1, TCGV_HIGH(r), c1); + tcg_gen_or_i32(TCGV_LOW(co), c0, c1); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(c0); + tcg_temp_free_i32(c1); + } + tcg_gen_movi_i32(TCGV_HIGH(co), 0); + } +} + void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh) { - if (TCG_TARGET_HAS_sub2_i64) { - tcg_gen_op6_i64(INDEX_op_sub2_i64, rl, rh, al, ah, bl, bh); + if (tcg_op_supported(INDEX_op_subbi, TCG_TYPE_REG, 0)) { + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_op3_i32(INDEX_op_subbo, TCGV_LOW(t0), + TCGV_LOW(al), TCGV_LOW(bl)); + tcg_gen_op3_i32(INDEX_op_subbio, TCGV_HIGH(t0), + TCGV_HIGH(al), TCGV_HIGH(bl)); + tcg_gen_op3_i32(INDEX_op_subbio, TCGV_LOW(rh), + TCGV_LOW(ah), TCGV_LOW(bh)); + tcg_gen_op3_i32(INDEX_op_subbi, TCGV_HIGH(rh), + TCGV_HIGH(ah), TCGV_HIGH(bh)); + } else { + tcg_gen_op3_i64(INDEX_op_subbo, t0, al, bl); + tcg_gen_op3_i64(INDEX_op_subbi, rh, ah, bh); + } + + tcg_gen_mov_i64(rl, t0); + tcg_temp_free_i64(t0); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); @@ -2666,12 +2976,12 @@ void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, 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) { + if (tcg_op_supported(INDEX_op_mulu2, TCG_TYPE_I64, 0)) { + tcg_gen_op4_i64(INDEX_op_mulu2, rl, rh, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_muluh, TCG_TYPE_I64, 0)) { 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_op3_i64(INDEX_op_mul, t, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_muluh, rh, arg1, arg2); tcg_gen_mov_i64(rl, t); tcg_temp_free_i64(t); } else { @@ -2685,15 +2995,16 @@ 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) { - 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) { + if (tcg_op_supported(INDEX_op_muls2, TCG_TYPE_I64, 0)) { + tcg_gen_op4_i64(INDEX_op_muls2, rl, rh, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_mulsh, TCG_TYPE_I64, 0)) { 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_op3_i64(INDEX_op_mul, t, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_mulsh, 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) { + } else if (tcg_op_supported(INDEX_op_mulu2, TCG_TYPE_I64, 0) || + tcg_op_supported(INDEX_op_muluh, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); TCGv_i64 t2 = tcg_temp_ebb_new_i64(); @@ -2772,11 +3083,9 @@ 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_extr_i64_i32) { - tcg_gen_op2(INDEX_op_extrl_i64_i32, - tcgv_i32_arg(ret), tcgv_i64_arg(arg)); } else { - tcg_gen_mov_i32(ret, (TCGv_i32)arg); + tcg_gen_op2(INDEX_op_extrl_i64_i32, TCG_TYPE_I32, + tcgv_i32_arg(ret), tcgv_i64_arg(arg)); } } @@ -2784,14 +3093,9 @@ 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_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_ebb_new_i64(); - tcg_gen_shri_i64(t, arg, 32); - tcg_gen_mov_i32(ret, (TCGv_i32)t); - tcg_temp_free_i64(t); + tcg_gen_op2(INDEX_op_extrh_i64_i32, TCG_TYPE_I32, + tcgv_i32_arg(ret), tcgv_i64_arg(arg)); } } @@ -2801,7 +3105,7 @@ void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg) tcg_gen_mov_i32(TCGV_LOW(ret), arg); tcg_gen_movi_i32(TCGV_HIGH(ret), 0); } else { - tcg_gen_op2(INDEX_op_extu_i32_i64, + tcg_gen_op2(INDEX_op_extu_i32_i64, TCG_TYPE_I64, tcgv_i64_arg(ret), tcgv_i32_arg(arg)); } } @@ -2812,7 +3116,7 @@ void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg) tcg_gen_mov_i32(TCGV_LOW(ret), arg); tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); } else { - tcg_gen_op2(INDEX_op_ext_i32_i64, + tcg_gen_op2(INDEX_op_ext_i32_i64, TCG_TYPE_I64, tcgv_i64_arg(ret), tcgv_i32_arg(arg)); } } @@ -2834,7 +3138,7 @@ void tcg_gen_concat_i32_i64(TCGv_i64 dest, TCGv_i32 low, TCGv_i32 high) tcg_gen_extu_i32_i64(dest, low); /* If deposit is available, use it. Otherwise use the extra knowledge that we have of the zero-extensions above. */ - if (TCG_TARGET_HAS_deposit_i64 && TCG_TARGET_deposit_i64_valid(32, 32)) { + if (TCG_TARGET_deposit_valid(TCG_TYPE_I64, 32, 32)) { tcg_gen_deposit_i64(dest, dest, tmp, 32, 32); } else { tcg_gen_shli_i64(tmp, tmp, 32); @@ -2860,6 +3164,11 @@ 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)); @@ -2880,6 +3189,28 @@ 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) +{ + 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) @@ -2910,7 +3241,7 @@ void tcg_gen_exit_tb(const TranslationBlock *tb, unsigned idx) tcg_debug_assert(idx == TB_EXIT_REQUESTED); } - tcg_gen_op1i(INDEX_op_exit_tb, val); + tcg_gen_op1i(INDEX_op_exit_tb, 0, val); } void tcg_gen_goto_tb(unsigned idx) @@ -2925,7 +3256,7 @@ void tcg_gen_goto_tb(unsigned idx) tcg_ctx->goto_tb_issue_mask |= 1 << idx; #endif plugin_gen_disable_mem_helpers(); - tcg_gen_op1i(INDEX_op_goto_tb, idx); + tcg_gen_op1i(INDEX_op_goto_tb, 0, idx); } void tcg_gen_lookup_and_goto_ptr(void) @@ -2939,7 +3270,7 @@ void tcg_gen_lookup_and_goto_ptr(void) plugin_gen_disable_mem_helpers(); ptr = tcg_temp_ebb_new_ptr(); - gen_helper_lookup_tb_ptr(ptr, cpu_env); - tcg_gen_op1i(INDEX_op_goto_ptr, tcgv_ptr_arg(ptr)); + gen_helper_lookup_tb_ptr(ptr, tcg_env); + tcg_gen_op1i(INDEX_op_goto_ptr, TCG_TYPE_PTR, tcgv_ptr_arg(ptr)); tcg_temp_free_ptr(ptr); } diff --git a/tcg/tcg-pool.c.inc b/tcg/tcg-pool.c.inc deleted file mode 100644 index 90c2e63b7f3..00000000000 --- a/tcg/tcg-pool.c.inc +++ /dev/null @@ -1,162 +0,0 @@ -/* - * TCG Backend Data: constant pool. - * - * 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. - */ - -typedef struct TCGLabelPoolData { - struct TCGLabelPoolData *next; - tcg_insn_unit *label; - intptr_t addend; - int rtype; - unsigned nlong; - tcg_target_ulong data[]; -} TCGLabelPoolData; - - -static TCGLabelPoolData *new_pool_alloc(TCGContext *s, int nlong, int rtype, - tcg_insn_unit *label, intptr_t addend) -{ - TCGLabelPoolData *n = tcg_malloc(sizeof(TCGLabelPoolData) - + sizeof(tcg_target_ulong) * nlong); - - n->label = label; - n->addend = addend; - n->rtype = rtype; - n->nlong = nlong; - return n; -} - -static void new_pool_insert(TCGContext *s, TCGLabelPoolData *n) -{ - TCGLabelPoolData *i, **pp; - int nlong = n->nlong; - - /* Insertion sort on the pool. */ - for (pp = &s->pool_labels; (i = *pp) != NULL; pp = &i->next) { - if (nlong > i->nlong) { - break; - } - if (nlong < i->nlong) { - continue; - } - if (memcmp(n->data, i->data, sizeof(tcg_target_ulong) * nlong) >= 0) { - break; - } - } - n->next = *pp; - *pp = n; -} - -/* The "usual" for generic integer code. */ -static inline void new_pool_label(TCGContext *s, tcg_target_ulong d, int rtype, - tcg_insn_unit *label, intptr_t addend) -{ - TCGLabelPoolData *n = new_pool_alloc(s, 1, rtype, label, addend); - n->data[0] = d; - new_pool_insert(s, n); -} - -/* For v64 or v128, depending on the host. */ -static inline void new_pool_l2(TCGContext *s, int rtype, tcg_insn_unit *label, - intptr_t addend, tcg_target_ulong d0, - tcg_target_ulong d1) -{ - TCGLabelPoolData *n = new_pool_alloc(s, 2, rtype, label, addend); - n->data[0] = d0; - n->data[1] = d1; - new_pool_insert(s, n); -} - -/* For v128 or v256, depending on the host. */ -static inline void new_pool_l4(TCGContext *s, int rtype, tcg_insn_unit *label, - intptr_t addend, tcg_target_ulong d0, - tcg_target_ulong d1, tcg_target_ulong d2, - tcg_target_ulong d3) -{ - TCGLabelPoolData *n = new_pool_alloc(s, 4, rtype, label, addend); - n->data[0] = d0; - n->data[1] = d1; - n->data[2] = d2; - n->data[3] = d3; - new_pool_insert(s, n); -} - -/* For v256, for 32-bit host. */ -static inline void new_pool_l8(TCGContext *s, int rtype, tcg_insn_unit *label, - intptr_t addend, tcg_target_ulong d0, - tcg_target_ulong d1, tcg_target_ulong d2, - tcg_target_ulong d3, tcg_target_ulong d4, - tcg_target_ulong d5, tcg_target_ulong d6, - tcg_target_ulong d7) -{ - TCGLabelPoolData *n = new_pool_alloc(s, 8, rtype, label, addend); - n->data[0] = d0; - n->data[1] = d1; - n->data[2] = d2; - n->data[3] = d3; - n->data[4] = d4; - n->data[5] = d5; - n->data[6] = d6; - n->data[7] = d7; - new_pool_insert(s, n); -} - -/* To be provided by cpu/tcg-target.c.inc. */ -static void tcg_out_nop_fill(tcg_insn_unit *p, int count); - -static int tcg_out_pool_finalize(TCGContext *s) -{ - TCGLabelPoolData *p = s->pool_labels; - TCGLabelPoolData *l = NULL; - void *a; - - if (p == NULL) { - return 0; - } - - /* ??? Round up to qemu_icache_linesize, but then do not round - again when allocating the next TranslationBlock structure. */ - a = (void *)ROUND_UP((uintptr_t)s->code_ptr, - sizeof(tcg_target_ulong) * p->nlong); - tcg_out_nop_fill(s->code_ptr, (tcg_insn_unit *)a - s->code_ptr); - s->data_gen_ptr = a; - - for (; p != NULL; p = p->next) { - size_t size = sizeof(tcg_target_ulong) * p->nlong; - uintptr_t value; - - if (!l || l->nlong != p->nlong || memcmp(l->data, p->data, size)) { - if (unlikely(a > s->code_gen_highwater)) { - return -1; - } - memcpy(a, p->data, size); - a += size; - l = p; - } - - value = (uintptr_t)tcg_splitwx_to_rx(a) - size; - if (!patch_reloc(p->label, p->rtype, value, p->addend)) { - return -2; - } - } - - s->code_ptr = a; - return 0; -} diff --git a/tcg/tcg.c b/tcg/tcg.c index 620dbe08da1..d714ae2889c 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -34,8 +34,10 @@ #include "qemu/cacheflush.h" #include "qemu/cacheinfo.h" #include "qemu/timer.h" +#include "exec/target_page.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 @@ -54,9 +56,10 @@ #include "tcg/tcg-ldst.h" #include "tcg/tcg-temp-internal.h" #include "tcg-internal.h" -#include "accel/tcg/perf.h" +#include "tcg/perf.h" +#include "tcg-has.h" #ifdef CONFIG_USER_ONLY -#include "exec/user/guest-base.h" +#include "user/guest-base.h" #endif /* Forward declarations for functions declared in tcg-target.c.inc and @@ -65,6 +68,11 @@ static void tcg_target_init(TCGContext *s); static void tcg_target_qemu_prologue(TCGContext *s); static bool patch_reloc(tcg_insn_unit *code_ptr, int type, intptr_t value, intptr_t addend); +static void tcg_out_nop_fill(tcg_insn_unit *p, int count); + +typedef struct TCGLabelQemuLdst TCGLabelQemuLdst; +static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l); +static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l); /* The CIE and FDE header definitions will be common to all hosts. */ typedef struct { @@ -89,18 +97,17 @@ typedef struct QEMU_PACKED { DebugFrameFDEHeader fde; } DebugFrameHeader; -typedef struct TCGLabelQemuLdst { +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 addr_reg; /* reg index for 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, @@ -108,6 +115,7 @@ static void tcg_register_jit_int(const void *buf, size_t 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); @@ -126,9 +134,11 @@ 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]); +static void tcg_out_goto_ptr(TCGContext *s, TCGReg dest); +static void tcg_out_mb(TCGContext *s, unsigned bar); +static void tcg_out_br(TCGContext *s, TCGLabel *l); +static void tcg_out_set_carry(TCGContext *s); +static void tcg_out_set_borrow(TCGContext *s); #if TCG_TARGET_MAYBE_vec static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, TCGReg dst, TCGReg src); @@ -163,6 +173,10 @@ static inline void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, { g_assert_not_reached(); } +int tcg_can_emit_vec_op(TCGOpcode o, TCGType t, unsigned ve) +{ + return 0; +} #endif static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, intptr_t arg2); @@ -171,9 +185,11 @@ static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target, const TCGHelperInfo *info); static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot); -static bool tcg_target_const_match(int64_t val, TCGType type, int ct); -#ifdef TCG_TARGET_NEED_LDST_LABELS -static int tcg_out_ldst_finalize(TCGContext *s); +static bool tcg_target_const_match(int64_t val, int ct, + TCGType type, TCGCond cond, int vece); + +#ifndef CONFIG_USER_ONLY +#define guest_base ({ qemu_build_not_reached(); (uintptr_t)0; }) #endif typedef struct TCGLdstHelperParam { @@ -224,13 +240,17 @@ 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; @@ -402,12 +422,12 @@ static uintptr_t G_GNUC_UNUSED get_jmp_target_addr(TCGContext *s, int which) return (uintptr_t)tcg_splitwx_to_rx(&s->gen_tb->jmp_target_addr[which]); } -#if defined(CONFIG_SOFTMMU) && !defined(CONFIG_TCG_INTERPRETER) -static int tlb_mask_table_ofs(TCGContext *s, int which) +static int __attribute__((unused)) +tlb_mask_table_ofs(TCGContext *s, int which) { - return s->tlb_fast_offset + which * sizeof(CPUTLBDescFast); + return (offsetof(CPUNegativeOffsetState, tlb.f[which]) - + sizeof(CPUNegativeOffsetState)); } -#endif /* Signal overflow, starting over with fewer guest insns. */ static G_NORETURN @@ -623,6 +643,197 @@ static void tcg_out_movext3(TCGContext *s, const TCGMovExtend *i1, } } +/* + * Allocate a new TCGLabelQemuLdst entry. + */ + +__attribute__((unused)) +static 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; +} + +/* + * Allocate new constant pool entries. + */ + +typedef struct TCGLabelPoolData { + struct TCGLabelPoolData *next; + tcg_insn_unit *label; + intptr_t addend; + int rtype; + unsigned nlong; + tcg_target_ulong data[]; +} TCGLabelPoolData; + +static TCGLabelPoolData *new_pool_alloc(TCGContext *s, int nlong, int rtype, + tcg_insn_unit *label, intptr_t addend) +{ + TCGLabelPoolData *n = tcg_malloc(sizeof(TCGLabelPoolData) + + sizeof(tcg_target_ulong) * nlong); + + n->label = label; + n->addend = addend; + n->rtype = rtype; + n->nlong = nlong; + return n; +} + +static void new_pool_insert(TCGContext *s, TCGLabelPoolData *n) +{ + TCGLabelPoolData *i, **pp; + int nlong = n->nlong; + + /* Insertion sort on the pool. */ + for (pp = &s->pool_labels; (i = *pp) != NULL; pp = &i->next) { + if (nlong > i->nlong) { + break; + } + if (nlong < i->nlong) { + continue; + } + if (memcmp(n->data, i->data, sizeof(tcg_target_ulong) * nlong) >= 0) { + break; + } + } + n->next = *pp; + *pp = n; +} + +/* The "usual" for generic integer code. */ +__attribute__((unused)) +static void new_pool_label(TCGContext *s, tcg_target_ulong d, int rtype, + tcg_insn_unit *label, intptr_t addend) +{ + TCGLabelPoolData *n = new_pool_alloc(s, 1, rtype, label, addend); + n->data[0] = d; + new_pool_insert(s, n); +} + +/* For v64 or v128, depending on the host. */ +__attribute__((unused)) +static void new_pool_l2(TCGContext *s, int rtype, tcg_insn_unit *label, + intptr_t addend, tcg_target_ulong d0, + tcg_target_ulong d1) +{ + TCGLabelPoolData *n = new_pool_alloc(s, 2, rtype, label, addend); + n->data[0] = d0; + n->data[1] = d1; + new_pool_insert(s, n); +} + +/* For v128 or v256, depending on the host. */ +__attribute__((unused)) +static void new_pool_l4(TCGContext *s, int rtype, tcg_insn_unit *label, + intptr_t addend, tcg_target_ulong d0, + tcg_target_ulong d1, tcg_target_ulong d2, + tcg_target_ulong d3) +{ + TCGLabelPoolData *n = new_pool_alloc(s, 4, rtype, label, addend); + n->data[0] = d0; + n->data[1] = d1; + n->data[2] = d2; + n->data[3] = d3; + new_pool_insert(s, n); +} + +/* For v256, for 32-bit host. */ +__attribute__((unused)) +static void new_pool_l8(TCGContext *s, int rtype, tcg_insn_unit *label, + intptr_t addend, tcg_target_ulong d0, + tcg_target_ulong d1, tcg_target_ulong d2, + tcg_target_ulong d3, tcg_target_ulong d4, + tcg_target_ulong d5, tcg_target_ulong d6, + tcg_target_ulong d7) +{ + TCGLabelPoolData *n = new_pool_alloc(s, 8, rtype, label, addend); + n->data[0] = d0; + n->data[1] = d1; + n->data[2] = d2; + n->data[3] = d3; + n->data[4] = d4; + n->data[5] = d5; + n->data[6] = d6; + n->data[7] = d7; + new_pool_insert(s, n); +} + +/* + * Generate TB finalization at the end of block + */ + +static int tcg_out_ldst_finalize(TCGContext *s) +{ + TCGLabelQemuLdst *lb; + + /* qemu_ld/st slow paths */ + QSIMPLEQ_FOREACH(lb, &s->ldst_labels, next) { + if (lb->is_ld + ? !tcg_out_qemu_ld_slow_path(s, lb) + : !tcg_out_qemu_st_slow_path(s, lb)) { + return -2; + } + + /* + * 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 + * generating code without having to check during generation. + */ + if (unlikely((void *)s->code_ptr > s->code_gen_highwater)) { + return -1; + } + } + return 0; +} + +static int tcg_out_pool_finalize(TCGContext *s) +{ + TCGLabelPoolData *p = s->pool_labels; + TCGLabelPoolData *l = NULL; + void *a; + + if (p == NULL) { + return 0; + } + + /* + * ??? Round up to qemu_icache_linesize, but then do not round + * again when allocating the next TranslationBlock structure. + */ + a = (void *)ROUND_UP((uintptr_t)s->code_ptr, + sizeof(tcg_target_ulong) * p->nlong); + tcg_out_nop_fill(s->code_ptr, (tcg_insn_unit *)a - s->code_ptr); + s->data_gen_ptr = a; + + for (; p != NULL; p = p->next) { + size_t size = sizeof(tcg_target_ulong) * p->nlong; + uintptr_t value; + + if (!l || l->nlong != p->nlong || memcmp(l->data, p->data, size)) { + if (unlikely(a > s->code_gen_highwater)) { + return -1; + } + memcpy(a, p->data, size); + a += size; + l = p; + } + + value = (uintptr_t)tcg_splitwx_to_rx(a) - size; + if (!patch_reloc(p->label, p->rtype, value, p->addend)) { + return -2; + } + } + + s->code_ptr = a; + return 0; +} + #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 @@ -643,6 +854,8 @@ static void tcg_out_movext3(TCGContext *s, const TCGMovExtend *i1, #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), @@ -651,10 +864,12 @@ static void tcg_out_movext3(TCGContext *s, const TCGMovExtend *i1, #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 { + C_Dynamic = -2, + C_NotImplemented = -1, #include "tcg-target-con-set.h" } TCGConstraintSetIndex; -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode); +static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode, TCGType, unsigned); #undef C_O0_I1 #undef C_O0_I2 @@ -665,6 +880,8 @@ 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 @@ -673,29 +890,35 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode); /* Put all of the constraint sets into an array, indexed by the enum. */ -#define C_O0_I1(I1) { .args_ct_str = { #I1 } }, -#define C_O0_I2(I1, I2) { .args_ct_str = { #I1, #I2 } }, -#define C_O0_I3(I1, I2, I3) { .args_ct_str = { #I1, #I2, #I3 } }, -#define C_O0_I4(I1, I2, I3, I4) { .args_ct_str = { #I1, #I2, #I3, #I4 } }, +typedef struct TCGConstraintSet { + uint8_t nb_oargs, nb_iargs; + const char *args_ct_str[TCG_MAX_OP_ARGS]; +} TCGConstraintSet; + +#define C_O0_I1(I1) { 0, 1, { #I1 } }, +#define C_O0_I2(I1, I2) { 0, 2, { #I1, #I2 } }, +#define C_O0_I3(I1, I2, I3) { 0, 3, { #I1, #I2, #I3 } }, +#define C_O0_I4(I1, I2, I3, I4) { 0, 4, { #I1, #I2, #I3, #I4 } }, -#define C_O1_I1(O1, I1) { .args_ct_str = { #O1, #I1 } }, -#define C_O1_I2(O1, I1, I2) { .args_ct_str = { #O1, #I1, #I2 } }, -#define C_O1_I3(O1, I1, I2, I3) { .args_ct_str = { #O1, #I1, #I2, #I3 } }, -#define C_O1_I4(O1, I1, I2, I3, I4) { .args_ct_str = { #O1, #I1, #I2, #I3, #I4 } }, +#define C_O1_I1(O1, I1) { 1, 1, { #O1, #I1 } }, +#define C_O1_I2(O1, I1, I2) { 1, 2, { #O1, #I1, #I2 } }, +#define C_O1_I3(O1, I1, I2, I3) { 1, 3, { #O1, #I1, #I2, #I3 } }, +#define C_O1_I4(O1, I1, I2, I3, I4) { 1, 4, { #O1, #I1, #I2, #I3, #I4 } }, -#define C_N1_I2(O1, I1, I2) { .args_ct_str = { "&" #O1, #I1, #I2 } }, +#define C_N1_I2(O1, I1, I2) { 1, 2, { "&" #O1, #I1, #I2 } }, +#define C_N1O1_I1(O1, O2, I1) { 2, 1, { "&" #O1, #O2, #I1 } }, +#define C_N2_I1(O1, O2, I1) { 2, 1, { "&" #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 } }, +#define C_O2_I1(O1, O2, I1) { 2, 1, { #O1, #O2, #I1 } }, +#define C_O2_I2(O1, O2, I1, I2) { 2, 2, { #O1, #O2, #I1, #I2 } }, +#define C_O2_I3(O1, O2, I1, I2, I3) { 2, 3, { #O1, #O2, #I1, #I2, #I3 } }, +#define C_O2_I4(O1, O2, I1, I2, I3, I4) { 2, 4, { #O1, #O2, #I1, #I2, #I3, #I4 } }, +#define C_N1_O1_I4(O1, O2, I1, I2, I3, I4) { 2, 4, { "&" #O1, #O2, #I1, #I2, #I3, #I4 } }, -static const TCGTargetOpDef constraint_sets[] = { +static const TCGConstraintSet constraint_sets[] = { #include "tcg-target-con-set.h" }; - #undef C_O0_I1 #undef C_O0_I2 #undef C_O0_I3 @@ -705,6 +928,8 @@ 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 @@ -724,6 +949,8 @@ 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) @@ -731,17 +958,311 @@ static const TCGTargetOpDef constraint_sets[] = { #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) +/* + * TCGOutOp is the base class for a set of structures that describe how + * to generate code for a given TCGOpcode. + * + * @static_constraint: + * C_NotImplemented: The TCGOpcode is not supported by the backend. + * C_Dynamic: Use @dynamic_constraint to select a constraint set + * based on any of @type, @flags, or host isa. + * Otherwise: The register allocation constrains for the TCGOpcode. + * + * Subclasses of TCGOutOp will define a set of output routines that may + * be used. Such routines will often be selected by the set of registers + * and constants that come out of register allocation. The set of + * routines that are provided will guide the set of constraints that are + * legal. In particular, assume that tcg_optimize() has done its job in + * swapping commutative operands and folding operations for which all + * operands are constant. + */ +typedef struct TCGOutOp { + TCGConstraintSetIndex static_constraint; + TCGConstraintSetIndex (*dynamic_constraint)(TCGType type, unsigned flags); +} TCGOutOp; + +typedef struct TCGOutOpAddSubCarry { + TCGOutOp base; + void (*out_rrr)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2); + void (*out_rri)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2); + void (*out_rir)(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2); + void (*out_rii)(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, tcg_target_long a2); +} TCGOutOpAddSubCarry; + +typedef struct TCGOutOpBinary { + TCGOutOp base; + void (*out_rrr)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2); + void (*out_rri)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2); +} TCGOutOpBinary; + +typedef struct TCGOutOpBrcond { + TCGOutOp base; + void (*out_rr)(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a1, TCGReg a2, TCGLabel *label); + void (*out_ri)(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a1, tcg_target_long a2, TCGLabel *label); +} TCGOutOpBrcond; + +typedef struct TCGOutOpBrcond2 { + TCGOutOp base; + void (*out)(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh, TCGLabel *l); +} TCGOutOpBrcond2; + +typedef struct TCGOutOpBswap { + TCGOutOp base; + void (*out_rr)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags); +} TCGOutOpBswap; + +typedef struct TCGOutOpDeposit { + TCGOutOp base; + void (*out_rrr)(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len); + void (*out_rri)(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + tcg_target_long a2, unsigned ofs, unsigned len); + void (*out_rzr)(TCGContext *s, TCGType type, TCGReg a0, + TCGReg a2, unsigned ofs, unsigned len); +} TCGOutOpDeposit; + +typedef struct TCGOutOpDivRem { + TCGOutOp base; + void (*out_rr01r)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a4); +} TCGOutOpDivRem; + +typedef struct TCGOutOpExtract { + TCGOutOp base; + void (*out_rr)(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len); +} TCGOutOpExtract; + +typedef struct TCGOutOpExtract2 { + TCGOutOp base; + void (*out_rrr)(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned shr); +} TCGOutOpExtract2; + +typedef struct TCGOutOpLoad { + TCGOutOp base; + void (*out)(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, intptr_t offset); +} TCGOutOpLoad; + +typedef struct TCGOutOpMovcond { + TCGOutOp base; + void (*out)(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg vt, bool const_vt, TCGArg vf, bool consf_vf); +} TCGOutOpMovcond; + +typedef struct TCGOutOpMul2 { + TCGOutOp base; + void (*out_rrrr)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3); +} TCGOutOpMul2; + +typedef struct TCGOutOpQemuLdSt { + TCGOutOp base; + void (*out)(TCGContext *s, TCGType type, TCGReg dest, + TCGReg addr, MemOpIdx oi); +} TCGOutOpQemuLdSt; + +typedef struct TCGOutOpQemuLdSt2 { + TCGOutOp base; + void (*out)(TCGContext *s, TCGType type, TCGReg dlo, TCGReg dhi, + TCGReg addr, MemOpIdx oi); +} TCGOutOpQemuLdSt2; + +typedef struct TCGOutOpUnary { + TCGOutOp base; + void (*out_rr)(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1); +} TCGOutOpUnary; + +typedef struct TCGOutOpSetcond { + TCGOutOp base; + void (*out_rrr)(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg a1, TCGReg a2); + void (*out_rri)(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg a1, tcg_target_long a2); +} TCGOutOpSetcond; + +typedef struct TCGOutOpSetcond2 { + TCGOutOp base; + void (*out)(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, TCGArg bh, bool const_bh); +} TCGOutOpSetcond2; + +typedef struct TCGOutOpStore { + TCGOutOp base; + void (*out_r)(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, intptr_t offset); + void (*out_i)(TCGContext *s, TCGType type, tcg_target_long data, + TCGReg base, intptr_t offset); +} TCGOutOpStore; + +typedef struct TCGOutOpSubtract { + TCGOutOp base; + void (*out_rrr)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2); + void (*out_rir)(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2); +} TCGOutOpSubtract; + #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 + +#if TCG_TARGET_REG_BITS == 64 +/* + * We require these functions for slow-path function calls. + * Adapt them generically for opcode output. + */ + +static void tgen_exts_i32_i64(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_exts_i32_i64(s, a0, a1); } +static const TCGOutOpUnary outop_exts_i32_i64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_exts_i32_i64, +}; + +static void tgen_extu_i32_i64(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_extu_i32_i64(s, a0, a1); +} + +static const TCGOutOpUnary outop_extu_i32_i64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extu_i32_i64, +}; + +static void tgen_extrl_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_extrl_i64_i32(s, a0, a1); +} + +static const TCGOutOpUnary outop_extrl_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = TCG_TARGET_HAS_extr_i64_i32 ? tgen_extrl_i64_i32 : NULL, +}; +#endif + +static const TCGOutOp outop_goto_ptr = { + .static_constraint = C_O0_I1(r), +}; + +static const TCGOutOpLoad outop_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tcg_out_ld, +}; + +/* + * Register V as the TCGOutOp for O. + * This verifies that V is of type T, otherwise give a nice compiler error. + * This prevents trivial mistakes within each arch/tcg-target.c.inc. + */ +#define OUTOP(O, T, V) [O] = _Generic(V, T: &V.base) + +/* Register allocation descriptions for every TCGOpcode. */ +static const TCGOutOp * const all_outop[NB_OPS] = { + OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), + OUTOP(INDEX_op_addci, TCGOutOpAddSubCarry, outop_addci), + OUTOP(INDEX_op_addcio, TCGOutOpBinary, outop_addcio), + OUTOP(INDEX_op_addco, TCGOutOpBinary, outop_addco), + /* addc1o is implemented with set_carry + addcio */ + OUTOP(INDEX_op_addc1o, TCGOutOpBinary, outop_addcio), + OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), + OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), + OUTOP(INDEX_op_brcond, TCGOutOpBrcond, outop_brcond), + OUTOP(INDEX_op_bswap16, TCGOutOpBswap, outop_bswap16), + OUTOP(INDEX_op_bswap32, TCGOutOpBswap, outop_bswap32), + OUTOP(INDEX_op_clz, TCGOutOpBinary, outop_clz), + OUTOP(INDEX_op_ctpop, TCGOutOpUnary, outop_ctpop), + OUTOP(INDEX_op_ctz, TCGOutOpBinary, outop_ctz), + OUTOP(INDEX_op_deposit, TCGOutOpDeposit, outop_deposit), + OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), + OUTOP(INDEX_op_divu, TCGOutOpBinary, outop_divu), + OUTOP(INDEX_op_divs2, TCGOutOpDivRem, outop_divs2), + OUTOP(INDEX_op_divu2, TCGOutOpDivRem, outop_divu2), + OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), + OUTOP(INDEX_op_extract, TCGOutOpExtract, outop_extract), + OUTOP(INDEX_op_extract2, TCGOutOpExtract2, outop_extract2), + OUTOP(INDEX_op_ld8u, TCGOutOpLoad, outop_ld8u), + OUTOP(INDEX_op_ld8s, TCGOutOpLoad, outop_ld8s), + OUTOP(INDEX_op_ld16u, TCGOutOpLoad, outop_ld16u), + OUTOP(INDEX_op_ld16s, TCGOutOpLoad, outop_ld16s), + OUTOP(INDEX_op_ld, TCGOutOpLoad, outop_ld), + OUTOP(INDEX_op_movcond, TCGOutOpMovcond, outop_movcond), + OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), + OUTOP(INDEX_op_muls2, TCGOutOpMul2, outop_muls2), + OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), + OUTOP(INDEX_op_mulu2, TCGOutOpMul2, outop_mulu2), + OUTOP(INDEX_op_muluh, TCGOutOpBinary, outop_muluh), + OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), + OUTOP(INDEX_op_neg, TCGOutOpUnary, outop_neg), + OUTOP(INDEX_op_negsetcond, TCGOutOpSetcond, outop_negsetcond), + OUTOP(INDEX_op_nor, TCGOutOpBinary, outop_nor), + OUTOP(INDEX_op_not, TCGOutOpUnary, outop_not), + OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), + OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), + OUTOP(INDEX_op_qemu_ld, TCGOutOpQemuLdSt, outop_qemu_ld), + OUTOP(INDEX_op_qemu_ld2, TCGOutOpQemuLdSt2, outop_qemu_ld2), + OUTOP(INDEX_op_qemu_st, TCGOutOpQemuLdSt, outop_qemu_st), + OUTOP(INDEX_op_qemu_st2, TCGOutOpQemuLdSt2, outop_qemu_st2), + OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), + OUTOP(INDEX_op_remu, TCGOutOpBinary, outop_remu), + OUTOP(INDEX_op_rotl, TCGOutOpBinary, outop_rotl), + OUTOP(INDEX_op_rotr, TCGOutOpBinary, outop_rotr), + OUTOP(INDEX_op_sar, TCGOutOpBinary, outop_sar), + OUTOP(INDEX_op_setcond, TCGOutOpSetcond, outop_setcond), + OUTOP(INDEX_op_sextract, TCGOutOpExtract, outop_sextract), + OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), + OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), + OUTOP(INDEX_op_st, TCGOutOpStore, outop_st), + OUTOP(INDEX_op_st8, TCGOutOpStore, outop_st8), + OUTOP(INDEX_op_st16, TCGOutOpStore, outop_st16), + OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), + OUTOP(INDEX_op_subbi, TCGOutOpAddSubCarry, outop_subbi), + OUTOP(INDEX_op_subbio, TCGOutOpAddSubCarry, outop_subbio), + OUTOP(INDEX_op_subbo, TCGOutOpAddSubCarry, outop_subbo), + /* subb1o is implemented with set_borrow + subbio */ + OUTOP(INDEX_op_subb1o, TCGOutOpAddSubCarry, outop_subbio), + OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), + + [INDEX_op_goto_ptr] = &outop_goto_ptr, + +#if TCG_TARGET_REG_BITS == 32 + OUTOP(INDEX_op_brcond2_i32, TCGOutOpBrcond2, outop_brcond2), + OUTOP(INDEX_op_setcond2_i32, TCGOutOpSetcond2, outop_setcond2), +#else + OUTOP(INDEX_op_bswap64, TCGOutOpUnary, outop_bswap64), + OUTOP(INDEX_op_ext_i32_i64, TCGOutOpUnary, outop_exts_i32_i64), + OUTOP(INDEX_op_extu_i32_i64, TCGOutOpUnary, outop_extu_i32_i64), + OUTOP(INDEX_op_extrl_i64_i32, TCGOutOpUnary, outop_extrl_i64_i32), + OUTOP(INDEX_op_extrh_i64_i32, TCGOutOpUnary, outop_extrh_i64_i32), + OUTOP(INDEX_op_ld32u, TCGOutOpLoad, outop_ld32u), + OUTOP(INDEX_op_ld32s, TCGOutOpLoad, outop_ld32s), + OUTOP(INDEX_op_st32, TCGOutOpStore, outop_st), +#endif +}; + +#undef OUTOP + /* * All TCG threads except the parent (i.e. the one that called tcg_context_init * and registered the target's TCG globals) must register with this function @@ -750,12 +1271,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) @@ -785,7 +1307,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); } @@ -1274,39 +1795,19 @@ static void init_call_layout(TCGHelperInfo *info) } static int indirect_reg_alloc_order[ARRAY_SIZE(tcg_target_reg_alloc_order)]; -static void process_op_defs(TCGContext *s); +static void process_constraint_sets(void); static TCGTemp *tcg_global_reg_new_internal(TCGContext *s, TCGType type, TCGReg reg, const char *name); -static void tcg_context_init(unsigned max_cpus) +static void tcg_context_init(unsigned max_threads) { TCGContext *s = &tcg_init_ctx; - int op, total_args, n, i; - TCGOpDef *def; - TCGArgConstraint *args_ct; + int n, i; TCGTemp *ts; memset(s, 0, sizeof(*s)); s->nb_globals = 0; - /* Count total number of arguments and allocate the corresponding - space */ - total_args = 0; - for(op = 0; op < NB_OPS; op++) { - def = &tcg_op_defs[op]; - n = def->nb_iargs + def->nb_oargs; - total_args += n; - } - - args_ct = g_new0(TCGArgConstraint, total_args); - - for(op = 0; op < NB_OPS; op++) { - def = &tcg_op_defs[op]; - def->args_ct = args_ct; - n = def->nb_iargs + def->nb_oargs; - args_ct += n; - } - init_call_layout(&info_helper_ld32_mmu); init_call_layout(&info_helper_ld64_mmu); init_call_layout(&info_helper_ld128_mmu); @@ -1315,7 +1816,7 @@ static void tcg_context_init(unsigned max_cpus) init_call_layout(&info_helper_st128_mmu); tcg_target_init(s); - process_op_defs(s); + process_constraint_sets(); /* Reverse the order of the saved registers, assuming they're all at the start of tcg_target_reg_alloc_order. */ @@ -1332,33 +1833,31 @@ 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_threads TCG threads. */ #ifdef CONFIG_USER_ONLY tcg_ctxs = &tcg_ctx; tcg_cur_ctxs = 1; tcg_max_ctxs = 1; #else - tcg_max_ctxs = max_cpus; - tcg_ctxs = g_new0(TCGContext *, max_cpus); + tcg_max_ctxs = max_threads; + tcg_ctxs = g_new0(TCGContext *, max_threads); #endif 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) +void tcg_init(size_t tb_size, int splitwx, unsigned max_threads) { - tcg_context_init(max_cpus); - tcg_region_init(tb_size, splitwx, max_cpus); + tcg_context_init(max_threads); + tcg_region_init(tb_size, splitwx, max_threads); } /* @@ -1382,12 +1881,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; @@ -1398,21 +1897,17 @@ void tcg_prologue_init(TCGContext *s) tcg_qemu_tb_exec = (tcg_prologue_fn *)tcg_splitwx_to_rx(s->code_ptr); #endif -#ifdef TCG_TARGET_NEED_POOL_LABELS s->pool_labels = NULL; -#endif qemu_thread_jit_write(); /* Generate the prologue. */ tcg_target_qemu_prologue(s); -#ifdef TCG_TARGET_NEED_POOL_LABELS /* Allow the prologue to put e.g. guest_base into a pool entry. */ { int result = tcg_out_pool_finalize(s); tcg_debug_assert(result == 0); } -#endif prologue_size = tcg_current_code_size(s); perf_report_prologue(s->code_gen_ptr, prologue_size); @@ -1472,7 +1967,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) { @@ -1491,17 +1986,10 @@ 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); - -#if defined(CONFIG_SOFTMMU) && !defined(CONFIG_TCG_INTERPRETER) - tcg_debug_assert(s->tlb_fast_offset < 0); - tcg_debug_assert(s->tlb_fast_offset >= MIN_TLB_MASK_TABLE_OFS); -#endif - - tcg_debug_assert(s->insn_start_words > 0); + tcg_debug_assert(s->addr_type <= TCG_TYPE_REG); } static TCGTemp *tcg_temp_alloc(TCGContext *s) @@ -1553,8 +2041,8 @@ 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); @@ -1613,6 +2101,24 @@ TCGTemp *tcg_global_mem_new_internal(TCGType type, TCGv_ptr base, return ts; } +TCGv_i32 tcg_global_mem_new_i32(TCGv_ptr reg, intptr_t off, const char *name) +{ + TCGTemp *ts = tcg_global_mem_new_internal(reg, off, name, TCG_TYPE_I32); + return temp_tcgv_i32(ts); +} + +TCGv_i64 tcg_global_mem_new_i64(TCGv_ptr reg, intptr_t off, const char *name) +{ + TCGTemp *ts = tcg_global_mem_new_internal(reg, off, name, TCG_TYPE_I64); + return temp_tcgv_i64(ts); +} + +TCGv_ptr tcg_global_mem_new_ptr(TCGv_ptr reg, intptr_t off, const char *name) +{ + TCGTemp *ts = tcg_global_mem_new_internal(reg, off, name, TCG_TYPE_PTR); + return temp_tcgv_ptr(ts); +} + TCGTemp *tcg_temp_new_internal(TCGType type, TCGTempKind kind) { TCGContext *s = tcg_ctx; @@ -1677,6 +2183,46 @@ TCGTemp *tcg_temp_new_internal(TCGType type, TCGTempKind kind) return ts; } +TCGv_i32 tcg_temp_new_i32(void) +{ + return temp_tcgv_i32(tcg_temp_new_internal(TCG_TYPE_I32, TEMP_TB)); +} + +TCGv_i32 tcg_temp_ebb_new_i32(void) +{ + return temp_tcgv_i32(tcg_temp_new_internal(TCG_TYPE_I32, TEMP_EBB)); +} + +TCGv_i64 tcg_temp_new_i64(void) +{ + return temp_tcgv_i64(tcg_temp_new_internal(TCG_TYPE_I64, TEMP_TB)); +} + +TCGv_i64 tcg_temp_ebb_new_i64(void) +{ + return temp_tcgv_i64(tcg_temp_new_internal(TCG_TYPE_I64, TEMP_EBB)); +} + +TCGv_ptr tcg_temp_new_ptr(void) +{ + return temp_tcgv_ptr(tcg_temp_new_internal(TCG_TYPE_PTR, TEMP_TB)); +} + +TCGv_ptr tcg_temp_ebb_new_ptr(void) +{ + return temp_tcgv_ptr(tcg_temp_new_internal(TCG_TYPE_PTR, TEMP_EBB)); +} + +TCGv_i128 tcg_temp_new_i128(void) +{ + return temp_tcgv_i128(tcg_temp_new_internal(TCG_TYPE_I128, TEMP_TB)); +} + +TCGv_i128 tcg_temp_ebb_new_i128(void) +{ + return temp_tcgv_i128(tcg_temp_new_internal(TCG_TYPE_I128, TEMP_EBB)); +} + TCGv_vec tcg_temp_new_vec(TCGType type) { TCGTemp *t; @@ -1732,6 +2278,31 @@ void tcg_temp_free_internal(TCGTemp *ts) } } +void tcg_temp_free_i32(TCGv_i32 arg) +{ + tcg_temp_free_internal(tcgv_i32_temp(arg)); +} + +void tcg_temp_free_i64(TCGv_i64 arg) +{ + tcg_temp_free_internal(tcgv_i64_temp(arg)); +} + +void tcg_temp_free_i128(TCGv_i128 arg) +{ + tcg_temp_free_internal(tcgv_i128_temp(arg)); +} + +void tcg_temp_free_ptr(TCGv_ptr arg) +{ + tcg_temp_free_internal(tcgv_ptr_temp(arg)); +} + +void tcg_temp_free_vec(TCGv_vec arg) +{ + tcg_temp_free_internal(tcgv_vec_temp(arg)); +} + TCGTemp *tcg_constant_internal(TCGType type, int64_t val) { TCGContext *s = tcg_ctx; @@ -1787,6 +2358,26 @@ TCGTemp *tcg_constant_internal(TCGType type, int64_t val) 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_vaddr tcg_constant_vaddr(uintptr_t val) +{ + return temp_tcgv_vaddr(tcg_constant_internal(TCG_TYPE_PTR, 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); @@ -1820,12 +2411,34 @@ TCGTemp *tcgv_i32_temp(TCGv_i32 v) } #endif /* CONFIG_DEBUG_TCG */ -/* Return true if OP may appear in the opcode stream. - Test the runtime variable that controls each opcode. */ -bool tcg_op_supported(TCGOpcode op) +/* + * Return true if OP may appear in the opcode stream with TYPE. + * Test the runtime variable that controls each opcode. + */ +bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) { - const bool have_vec - = TCG_TARGET_HAS_v64 | TCG_TARGET_HAS_v128 | TCG_TARGET_HAS_v256; + bool has_type; + + switch (type) { + case TCG_TYPE_I32: + has_type = true; + break; + case TCG_TYPE_I64: + has_type = TCG_TARGET_REG_BITS == 64; + break; + case TCG_TYPE_V64: + has_type = TCG_TARGET_HAS_v64; + break; + case TCG_TYPE_V128: + has_type = TCG_TARGET_HAS_v128; + break; + case TCG_TYPE_V256: + has_type = TCG_TARGET_HAS_v256; + break; + default: + has_type = false; + break; + } switch (op) { case INDEX_op_discard: @@ -1837,225 +2450,56 @@ 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_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_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_ld8u_i32: - case INDEX_op_ld8s_i32: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16s_i32: - case INDEX_op_ld_i32: - case INDEX_op_st8_i32: - case INDEX_op_st16_i32: - case INDEX_op_st_i32: - case INDEX_op_add_i32: - case INDEX_op_sub_i32: - case INDEX_op_mul_i32: - case INDEX_op_and_i32: - case INDEX_op_or_i32: - case INDEX_op_xor_i32: - case INDEX_op_shl_i32: - case INDEX_op_shr_i32: - case INDEX_op_sar_i32: + case INDEX_op_qemu_ld: + case INDEX_op_qemu_st: + tcg_debug_assert(type <= TCG_TYPE_REG); return true; - case INDEX_op_negsetcond_i32: - return TCG_TARGET_HAS_negsetcond_i32; - case INDEX_op_movcond_i32: - return TCG_TARGET_HAS_movcond_i32; - case INDEX_op_div_i32: - case INDEX_op_divu_i32: - return TCG_TARGET_HAS_div_i32; - case INDEX_op_rem_i32: - case INDEX_op_remu_i32: - return TCG_TARGET_HAS_rem_i32; - case INDEX_op_div2_i32: - case INDEX_op_divu2_i32: - return TCG_TARGET_HAS_div2_i32; - case INDEX_op_rotl_i32: - case INDEX_op_rotr_i32: - return TCG_TARGET_HAS_rot_i32; - case INDEX_op_deposit_i32: - return TCG_TARGET_HAS_deposit_i32; - case INDEX_op_extract_i32: - return TCG_TARGET_HAS_extract_i32; - case INDEX_op_sextract_i32: - return TCG_TARGET_HAS_sextract_i32; - case INDEX_op_extract2_i32: - return TCG_TARGET_HAS_extract2_i32; - case INDEX_op_add2_i32: - return TCG_TARGET_HAS_add2_i32; - case INDEX_op_sub2_i32: - return TCG_TARGET_HAS_sub2_i32; - case INDEX_op_mulu2_i32: - return TCG_TARGET_HAS_mulu2_i32; - case INDEX_op_muls2_i32: - return TCG_TARGET_HAS_muls2_i32; - case INDEX_op_muluh_i32: - return TCG_TARGET_HAS_muluh_i32; - case INDEX_op_mulsh_i32: - return TCG_TARGET_HAS_mulsh_i32; - case INDEX_op_ext8s_i32: - return TCG_TARGET_HAS_ext8s_i32; - case INDEX_op_ext16s_i32: - return TCG_TARGET_HAS_ext16s_i32; - case INDEX_op_ext8u_i32: - return TCG_TARGET_HAS_ext8u_i32; - case INDEX_op_ext16u_i32: - return TCG_TARGET_HAS_ext16u_i32; - case INDEX_op_bswap16_i32: - return TCG_TARGET_HAS_bswap16_i32; - case INDEX_op_bswap32_i32: - 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: - return TCG_TARGET_HAS_orc_i32; - case INDEX_op_eqv_i32: - return TCG_TARGET_HAS_eqv_i32; - case INDEX_op_nand_i32: - return TCG_TARGET_HAS_nand_i32; - case INDEX_op_nor_i32: - return TCG_TARGET_HAS_nor_i32; - case INDEX_op_clz_i32: - return TCG_TARGET_HAS_clz_i32; - case INDEX_op_ctz_i32: - return TCG_TARGET_HAS_ctz_i32; - case INDEX_op_ctpop_i32: - return TCG_TARGET_HAS_ctpop_i32; + case INDEX_op_qemu_ld2: + case INDEX_op_qemu_st2: + if (TCG_TARGET_REG_BITS == 32) { + tcg_debug_assert(type == TCG_TYPE_I64); + return true; + } + tcg_debug_assert(type == TCG_TYPE_I128); + goto do_lookup; + + case INDEX_op_add: + case INDEX_op_and: + case INDEX_op_brcond: + case INDEX_op_deposit: + case INDEX_op_extract: + case INDEX_op_ld8u: + case INDEX_op_ld8s: + case INDEX_op_ld16u: + case INDEX_op_ld16s: + case INDEX_op_ld: + case INDEX_op_mov: + case INDEX_op_movcond: + case INDEX_op_negsetcond: + case INDEX_op_or: + case INDEX_op_setcond: + case INDEX_op_sextract: + case INDEX_op_st8: + case INDEX_op_st16: + case INDEX_op_st: + case INDEX_op_xor: + return has_type; case INDEX_op_brcond2_i32: case INDEX_op_setcond2_i32: return TCG_TARGET_REG_BITS == 32; - case INDEX_op_mov_i64: - case INDEX_op_setcond_i64: - case INDEX_op_brcond_i64: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: - case INDEX_op_st8_i64: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - case INDEX_op_add_i64: - case INDEX_op_sub_i64: - case INDEX_op_mul_i64: - case INDEX_op_and_i64: - case INDEX_op_or_i64: - case INDEX_op_xor_i64: - case INDEX_op_shl_i64: - case INDEX_op_shr_i64: - case INDEX_op_sar_i64: + case INDEX_op_ld32u: + case INDEX_op_ld32s: + case INDEX_op_st32: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - return TCG_TARGET_REG_BITS == 64; - - case INDEX_op_negsetcond_i64: - return TCG_TARGET_HAS_negsetcond_i64; - case INDEX_op_movcond_i64: - return TCG_TARGET_HAS_movcond_i64; - case INDEX_op_div_i64: - case INDEX_op_divu_i64: - return TCG_TARGET_HAS_div_i64; - case INDEX_op_rem_i64: - case INDEX_op_remu_i64: - return TCG_TARGET_HAS_rem_i64; - case INDEX_op_div2_i64: - case INDEX_op_divu2_i64: - return TCG_TARGET_HAS_div2_i64; - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i64: - return TCG_TARGET_HAS_rot_i64; - case INDEX_op_deposit_i64: - return TCG_TARGET_HAS_deposit_i64; - case INDEX_op_extract_i64: - return TCG_TARGET_HAS_extract_i64; - case INDEX_op_sextract_i64: - return TCG_TARGET_HAS_sextract_i64; - case INDEX_op_extract2_i64: - return TCG_TARGET_HAS_extract2_i64; case INDEX_op_extrl_i64_i32: case INDEX_op_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: - return TCG_TARGET_HAS_ext16s_i64; - case INDEX_op_ext32s_i64: - return TCG_TARGET_HAS_ext32s_i64; - case INDEX_op_ext8u_i64: - return TCG_TARGET_HAS_ext8u_i64; - case INDEX_op_ext16u_i64: - return TCG_TARGET_HAS_ext16u_i64; - case INDEX_op_ext32u_i64: - return TCG_TARGET_HAS_ext32u_i64; - case INDEX_op_bswap16_i64: - return TCG_TARGET_HAS_bswap16_i64; - case INDEX_op_bswap32_i64: - return TCG_TARGET_HAS_bswap32_i64; - case INDEX_op_bswap64_i64: - 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: - return TCG_TARGET_HAS_orc_i64; - case INDEX_op_eqv_i64: - return TCG_TARGET_HAS_eqv_i64; - case INDEX_op_nand_i64: - return TCG_TARGET_HAS_nand_i64; - case INDEX_op_nor_i64: - return TCG_TARGET_HAS_nor_i64; - case INDEX_op_clz_i64: - return TCG_TARGET_HAS_clz_i64; - case INDEX_op_ctz_i64: - return TCG_TARGET_HAS_ctz_i64; - case INDEX_op_ctpop_i64: - return TCG_TARGET_HAS_ctpop_i64; - case INDEX_op_add2_i64: - return TCG_TARGET_HAS_add2_i64; - case INDEX_op_sub2_i64: - return TCG_TARGET_HAS_sub2_i64; - case INDEX_op_mulu2_i64: - return TCG_TARGET_HAS_mulu2_i64; - case INDEX_op_muls2_i64: - return TCG_TARGET_HAS_muls2_i64; - case INDEX_op_muluh_i64: - return TCG_TARGET_HAS_muluh_i64; - case INDEX_op_mulsh_i64: - return TCG_TARGET_HAS_mulsh_i64; + return TCG_TARGET_REG_BITS == 64; case INDEX_op_mov_vec: case INDEX_op_dup_vec: @@ -2068,70 +2512,110 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_or_vec: case INDEX_op_xor_vec: case INDEX_op_cmp_vec: - return have_vec; + return has_type; case INDEX_op_dup2_vec: - return have_vec && TCG_TARGET_REG_BITS == 32; + return has_type && TCG_TARGET_REG_BITS == 32; case INDEX_op_not_vec: - return have_vec && TCG_TARGET_HAS_not_vec; + return has_type && TCG_TARGET_HAS_not_vec; case INDEX_op_neg_vec: - return have_vec && TCG_TARGET_HAS_neg_vec; + return has_type && TCG_TARGET_HAS_neg_vec; case INDEX_op_abs_vec: - return have_vec && TCG_TARGET_HAS_abs_vec; + return has_type && TCG_TARGET_HAS_abs_vec; case INDEX_op_andc_vec: - return have_vec && TCG_TARGET_HAS_andc_vec; + return has_type && TCG_TARGET_HAS_andc_vec; case INDEX_op_orc_vec: - return have_vec && TCG_TARGET_HAS_orc_vec; + return has_type && TCG_TARGET_HAS_orc_vec; case INDEX_op_nand_vec: - return have_vec && TCG_TARGET_HAS_nand_vec; + return has_type && TCG_TARGET_HAS_nand_vec; case INDEX_op_nor_vec: - return have_vec && TCG_TARGET_HAS_nor_vec; + return has_type && TCG_TARGET_HAS_nor_vec; case INDEX_op_eqv_vec: - return have_vec && TCG_TARGET_HAS_eqv_vec; + return has_type && TCG_TARGET_HAS_eqv_vec; case INDEX_op_mul_vec: - return have_vec && TCG_TARGET_HAS_mul_vec; + return has_type && TCG_TARGET_HAS_mul_vec; case INDEX_op_shli_vec: case INDEX_op_shri_vec: case INDEX_op_sari_vec: - return have_vec && TCG_TARGET_HAS_shi_vec; + return has_type && TCG_TARGET_HAS_shi_vec; case INDEX_op_shls_vec: case INDEX_op_shrs_vec: case INDEX_op_sars_vec: - return have_vec && TCG_TARGET_HAS_shs_vec; + return has_type && TCG_TARGET_HAS_shs_vec; case INDEX_op_shlv_vec: case INDEX_op_shrv_vec: case INDEX_op_sarv_vec: - return have_vec && TCG_TARGET_HAS_shv_vec; + return has_type && TCG_TARGET_HAS_shv_vec; case INDEX_op_rotli_vec: - return have_vec && TCG_TARGET_HAS_roti_vec; + return has_type && TCG_TARGET_HAS_roti_vec; case INDEX_op_rotls_vec: - return have_vec && TCG_TARGET_HAS_rots_vec; + return has_type && TCG_TARGET_HAS_rots_vec; case INDEX_op_rotlv_vec: case INDEX_op_rotrv_vec: - return have_vec && TCG_TARGET_HAS_rotv_vec; + return has_type && TCG_TARGET_HAS_rotv_vec; case INDEX_op_ssadd_vec: case INDEX_op_usadd_vec: case INDEX_op_sssub_vec: case INDEX_op_ussub_vec: - return have_vec && TCG_TARGET_HAS_sat_vec; + return has_type && TCG_TARGET_HAS_sat_vec; case INDEX_op_smin_vec: case INDEX_op_umin_vec: case INDEX_op_smax_vec: case INDEX_op_umax_vec: - return have_vec && TCG_TARGET_HAS_minmax_vec; + return has_type && TCG_TARGET_HAS_minmax_vec; case INDEX_op_bitsel_vec: - return have_vec && TCG_TARGET_HAS_bitsel_vec; + return has_type && TCG_TARGET_HAS_bitsel_vec; case INDEX_op_cmpsel_vec: - return have_vec && TCG_TARGET_HAS_cmpsel_vec; + return has_type && TCG_TARGET_HAS_cmpsel_vec; default: - tcg_debug_assert(op > INDEX_op_last_generic && op < NB_OPS); + if (op < INDEX_op_last_generic) { + const TCGOutOp *outop; + TCGConstraintSetIndex con_set; + + if (!has_type) { + return false; + } + + do_lookup: + outop = all_outop[op]; + tcg_debug_assert(outop != NULL); + + con_set = outop->static_constraint; + if (con_set == C_Dynamic) { + con_set = outop->dynamic_constraint(type, flags); + } + if (con_set >= 0) { + return true; + } + tcg_debug_assert(con_set == C_NotImplemented); + return false; + } + tcg_debug_assert(op < NB_OPS); return true; + + case INDEX_op_last_generic: + g_assert_not_reached(); } } +bool tcg_op_deposit_valid(TCGType type, unsigned ofs, unsigned len) +{ + unsigned width; + + tcg_debug_assert(type == TCG_TYPE_I32 || type == TCG_TYPE_I64); + width = (type == TCG_TYPE_I32 ? 32 : 64); + + tcg_debug_assert(ofs < width); + tcg_debug_assert(len > 0); + tcg_debug_assert(len <= width - ofs); + + return TCG_TARGET_deposit_valid(type, ofs, len); +} + static TCGOp *tcg_op_alloc(TCGOpcode opc, unsigned nargs); -static void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args) +static void tcg_gen_callN(void *func, TCGHelperInfo *info, + TCGTemp *ret, TCGTemp **args) { TCGv_i64 extend_free[MAX_CALL_IARGS]; int n_extend = 0; @@ -2148,9 +2632,7 @@ static void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args) #ifdef CONFIG_PLUGIN /* Flag helpers that may affect guest state */ - if (tcg_ctx->plugin_insn && - !(info->flags & TCG_CALL_PLUGIN) && - !(info->flags & TCG_CALL_NO_SIDE_EFFECTS)) { + if (tcg_ctx->plugin_insn && !(info->flags & TCG_CALL_NO_SIDE_EFFECTS)) { tcg_ctx->plugin_insn->calls_helpers = true; } #endif @@ -2209,11 +2691,15 @@ static void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args) g_assert_not_reached(); } } - op->args[pi++] = (uintptr_t)info->func; + op->args[pi++] = (uintptr_t)func; op->args[pi++] = (uintptr_t)info; tcg_debug_assert(pi == total_args); - QTAILQ_INSERT_TAIL(&tcg_ctx->ops, op, link); + 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); + } tcg_debug_assert(n_extend < ARRAY_SIZE(extend_free)); for (i = 0; i < n_extend; ++i) { @@ -2221,56 +2707,58 @@ static void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args) } } -void tcg_gen_call0(TCGHelperInfo *info, TCGTemp *ret) +void tcg_gen_call0(void *func, TCGHelperInfo *info, TCGTemp *ret) { - tcg_gen_callN(info, ret, NULL); + tcg_gen_callN(func, info, ret, NULL); } -void tcg_gen_call1(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1) +void tcg_gen_call1(void *func, TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1) { - tcg_gen_callN(info, ret, &t1); + tcg_gen_callN(func, info, ret, &t1); } -void tcg_gen_call2(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, TCGTemp *t2) +void tcg_gen_call2(void *func, TCGHelperInfo *info, TCGTemp *ret, + TCGTemp *t1, TCGTemp *t2) { TCGTemp *args[2] = { t1, t2 }; - tcg_gen_callN(info, ret, args); + tcg_gen_callN(func, info, ret, args); } -void tcg_gen_call3(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, - TCGTemp *t2, TCGTemp *t3) +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(info, ret, args); + tcg_gen_callN(func, info, ret, args); } -void tcg_gen_call4(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, - TCGTemp *t2, TCGTemp *t3, TCGTemp *t4) +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(info, ret, args); + tcg_gen_callN(func, info, ret, args); } -void tcg_gen_call5(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, +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(info, ret, args); + tcg_gen_callN(func, info, ret, args); } -void tcg_gen_call6(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, TCGTemp *t2, - TCGTemp *t3, TCGTemp *t4, TCGTemp *t5, TCGTemp *t6) +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(info, ret, args); + tcg_gen_callN(func, info, ret, args); } -void tcg_gen_call7(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, +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(info, ret, args); + tcg_gen_callN(func, info, ret, args); } static void tcg_reg_alloc_start(TCGContext *s) @@ -2364,7 +2852,9 @@ 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[(MO_BSWAP | MO_SSIZE) + 1] = @@ -2413,6 +2903,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; @@ -2431,7 +2930,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; @@ -2449,7 +2948,7 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) nb_oargs = 0; col += ne_fprintf(f, "\n ----"); - for (i = 0, k = s->insn_start_words; i < k; ++i) { + for (i = 0, k = INSN_START_WORDS; i < k; ++i) { col += ne_fprintf(f, " %016" PRIx64, tcg_get_insn_start_param(op, i)); } @@ -2486,17 +2985,23 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) col += ne_fprintf(f, ",%s", t); } } else { - col += ne_fprintf(f, " %s ", def->name); + if (def->flags & TCG_OPF_INT) { + col += ne_fprintf(f, " %s_i%d ", + def->name, + 8 * tcg_type_size(TCGOP_TYPE(op))); + } else if (def->flags & TCG_OPF_VECTOR) { + col += ne_fprintf(f, "%s v%d,e%d,", + def->name, + 8 * tcg_type_size(TCGOP_TYPE(op)), + 8 << TCGOP_VECE(op)); + } else { + col += ne_fprintf(f, " %s ", def->name); + } nb_oargs = def->nb_oargs; nb_iargs = def->nb_iargs; nb_cargs = def->nb_cargs; - if (def->flags & TCG_OPF_VECTOR) { - col += ne_fprintf(f, "v%d,e%d,", 64 << TCGOP_VECL(op), - 8 << TCGOP_VECE(op)); - } - k = 0; for (i = 0; i < nb_oargs; i++) { const char *sep = k ? "," : ""; @@ -2511,16 +3016,12 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) op->args[k++])); } 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_brcond: + case INDEX_op_setcond: + case INDEX_op_negsetcond: + case INDEX_op_movcond: 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: if (op->args[k] < ARRAY_SIZE(cond_name) @@ -2531,47 +3032,35 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) } i = 1; break; - 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: + case INDEX_op_qemu_ld: + case INDEX_op_qemu_st: + case INDEX_op_qemu_ld2: + case INDEX_op_qemu_st2: { 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); - s_al = alignment_name[(op & MO_AMASK) >> MO_ASHIFT]; - s_op = ldst_name[op & (MO_BSWAP | MO_SSIZE)]; - s_at = atom_name[(op & MO_ATOM_MASK) >> MO_ATOM_SHIFT]; - op &= ~(MO_AMASK | MO_BSWAP | MO_SSIZE | MO_ATOM_MASK); + 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 (!op && s_al && s_op && s_at) { + if (!mop && s_al && s_op && s_at) { col += ne_fprintf(f, ",%s%s%s,%u", s_at, s_al, s_op, ix); } else { - op = get_memop(oi); - col += ne_fprintf(f, ",$0x%x,%u", op, ix); + mop = get_memop(oi); + col += ne_fprintf(f, ",$0x%x,%u", mop, ix); } i = 1; } break; - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: - case INDEX_op_bswap64_i64: + case INDEX_op_bswap16: + case INDEX_op_bswap32: + case INDEX_op_bswap64: { TCGArg flags = op->args[k]; const char *name = NULL; @@ -2587,6 +3076,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; @@ -2594,8 +3101,7 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) switch (c) { case INDEX_op_set_label: case INDEX_op_br: - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: + case INDEX_op_brcond: case INDEX_op_brcond2_i32: col += ne_fprintf(f, "%s$L%d", k ? "," : "", arg_label(op->args[k])->id); @@ -2748,10 +3254,12 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) } /* we give more priority to constraints with less registers */ -static int get_constraint_priority(const TCGOpDef *def, int k) +static int get_constraint_priority(const TCGArgConstraint *arg_ct, int k) { - const TCGArgConstraint *arg_ct = &def->args_ct[k]; - int n = ctpop64(arg_ct->regs); + int n; + + arg_ct += k; + n = ctpop64(arg_ct->regs); /* * Sort constraints of a single register first, which includes output @@ -2780,10 +3288,9 @@ static int get_constraint_priority(const TCGOpDef *def, int k) } /* sort from highest priority to lowest */ -static void sort_constraints(TCGOpDef *def, int start, int n) +static void sort_constraints(TCGArgConstraint *a, int start, int n) { int i, j; - TCGArgConstraint *a = def->args_ct; for (i = 0; i < n; i++) { a[start + i].sort_index = start + i; @@ -2793,8 +3300,8 @@ static void sort_constraints(TCGOpDef *def, int start, int n) } for (i = 0; i < n - 1; i++) { for (j = i + 1; j < n; j++) { - int p1 = get_constraint_priority(def, a[start + i].sort_index); - int p2 = get_constraint_priority(def, a[start + j].sort_index); + int p1 = get_constraint_priority(a, a[start + i].sort_index); + int p2 = get_constraint_priority(a, a[start + j].sort_index); if (p1 < p2) { int tmp = a[start + i].sort_index; a[start + i].sort_index = a[start + j].sort_index; @@ -2804,56 +3311,39 @@ static void sort_constraints(TCGOpDef *def, int start, int n) } } -static void process_op_defs(TCGContext *s) -{ - TCGOpcode op; +static const TCGArgConstraint empty_cts[TCG_MAX_OP_ARGS]; +static TCGArgConstraint all_cts[ARRAY_SIZE(constraint_sets)][TCG_MAX_OP_ARGS]; - for (op = 0; op < NB_OPS; op++) { - TCGOpDef *def = &tcg_op_defs[op]; - const TCGTargetOpDef *tdefs; +static void process_constraint_sets(void) +{ + for (size_t c = 0; c < ARRAY_SIZE(constraint_sets); ++c) { + const TCGConstraintSet *tdefs = &constraint_sets[c]; + TCGArgConstraint *args_ct = all_cts[c]; + int nb_oargs = tdefs->nb_oargs; + int nb_iargs = tdefs->nb_iargs; + int nb_args = nb_oargs + nb_iargs; bool saw_alias_pair = false; - int i, o, i2, o2, nb_args; - - if (def->flags & TCG_OPF_NOT_PRESENT) { - continue; - } - - nb_args = def->nb_iargs + def->nb_oargs; - if (nb_args == 0) { - continue; - } - /* - * Macro magic should make it impossible, but double-check that - * the array index is in range. Since the signness of an enum - * is implementation defined, force the result to unsigned. - */ - unsigned con_set = tcg_target_op_def(op); - tcg_debug_assert(con_set < ARRAY_SIZE(constraint_sets)); - tdefs = &constraint_sets[con_set]; - - for (i = 0; i < nb_args; i++) { + for (int 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); + bool input_p = i >= nb_oargs; + int o; 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]; + tcg_debug_assert(o < nb_oargs); + tcg_debug_assert(args_ct[o].regs != 0); + tcg_debug_assert(!args_ct[o].oalias); + args_ct[i] = args_ct[o]; /* The output sets oalias. */ - def->args_ct[o].oalias = 1; - def->args_ct[o].alias_index = i; + args_ct[o].oalias = 1; + 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) { + args_ct[i].ialias = 1; + args_ct[i].alias_index = o; + if (args_ct[i].pair) { saw_alias_pair = true; } tcg_debug_assert(ct_str[1] == '\0'); @@ -2861,39 +3351,41 @@ static void process_op_defs(TCGContext *s) case '&': tcg_debug_assert(!input_p); - def->args_ct[i].newreg = true; + 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)); + tcg_debug_assert(i > (input_p ? 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){ + tcg_debug_assert(!args_ct[o].pair); + tcg_debug_assert(!args_ct[o].ct); + args_ct[i] = (TCGArgConstraint){ .pair = 2, .pair_index = o, - .regs = def->args_ct[o].regs << 1, + .regs = args_ct[o].regs << 1, + .newreg = args_ct[o].newreg, }; - def->args_ct[o].pair = 1; - def->args_ct[o].pair_index = i; + args_ct[o].pair = 1; + 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)); + tcg_debug_assert(i > (input_p ? 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){ + tcg_debug_assert(!args_ct[o].pair); + tcg_debug_assert(!args_ct[o].ct); + args_ct[i] = (TCGArgConstraint){ .pair = 1, .pair_index = o, - .regs = def->args_ct[o].regs >> 1, + .regs = args_ct[o].regs >> 1, + .newreg = args_ct[o].newreg, }; - def->args_ct[o].pair = 2; - def->args_ct[o].pair_index = i; + args_ct[o].pair = 2; + args_ct[o].pair_index = i; tcg_debug_assert(ct_str[1] == '\0'); continue; } @@ -2901,16 +3393,21 @@ static void process_op_defs(TCGContext *s) do { switch (*ct_str) { case 'i': - def->args_ct[i].ct |= TCG_CT_CONST; + args_ct[i].ct |= TCG_CT_CONST; break; +#ifdef TCG_REG_ZERO + case 'z': + args_ct[i].ct |= TCG_CT_REG_ZERO; + break; +#endif /* Include all of the target-specific constraints. */ #undef CONST #define CONST(CASE, MASK) \ - case CASE: def->args_ct[i].ct |= MASK; break; + case CASE: args_ct[i].ct |= MASK; break; #define REGS(CASE, MASK) \ - case CASE: def->args_ct[i].regs |= MASK; break; + case CASE: args_ct[i].regs |= MASK; break; #include "tcg-target-con-str.h" @@ -2921,15 +3418,12 @@ static void process_op_defs(TCGContext *s) case '&': case 'p': case 'm': - /* Typo in TCGTargetOpDef constraint. */ + /* Typo in TCGConstraintSet 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. @@ -2950,51 +3444,53 @@ static void process_op_defs(TCGContext *s) * 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++) { + for (int i = nb_oargs; i < nb_args; i++) { + int o, o2, i2; + /* * 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) { + if (!args_ct[i].ialias) { continue; } - switch (def->args_ct[i].pair) { + switch (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) { + o = args_ct[i].alias_index; + o2 = args_ct[o].pair_index; + tcg_debug_assert(args_ct[o].pair == 1); + tcg_debug_assert(args_ct[o2].pair == 2); + if (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; + i2 = args_ct[o2].alias_index; + tcg_debug_assert(args_ct[i2].pair == 2); + args_ct[i2].pair_index = i; + args_ct[i].pair_index = i2; } else { /* Case 1b */ - def->args_ct[i].pair_index = i; + 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) { + o = args_ct[i].alias_index; + o2 = args_ct[o].pair_index; + tcg_debug_assert(args_ct[o].pair == 2); + tcg_debug_assert(args_ct[o2].pair == 1); + if (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; + i2 = args_ct[o2].alias_index; + tcg_debug_assert(args_ct[i2].pair == 1); + args_ct[i2].pair_index = i; + 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; + args_ct[i].pair = 3; + args_ct[o2].pair = 3; + args_ct[i].pair_index = o2; + args_ct[o2].pair_index = i; } break; default: @@ -3003,10 +3499,41 @@ static void process_op_defs(TCGContext *s) } } - /* 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); + /* sort the constraints (XXX: this is just an heuristic) */ + sort_constraints(args_ct, 0, nb_oargs); + sort_constraints(args_ct, nb_oargs, nb_iargs); + } +} + +static const TCGArgConstraint *opcode_args_ct(const TCGOp *op) +{ + TCGOpcode opc = op->opc; + TCGType type = TCGOP_TYPE(op); + unsigned flags = TCGOP_FLAGS(op); + const TCGOpDef *def = &tcg_op_defs[opc]; + const TCGOutOp *outop = all_outop[opc]; + TCGConstraintSetIndex con_set; + + if (def->flags & TCG_OPF_NOT_PRESENT) { + return empty_cts; + } + + if (outop) { + con_set = outop->static_constraint; + if (con_set == C_Dynamic) { + con_set = outop->dynamic_constraint(type, flags); + } + } else { + con_set = tcg_target_op_def(opc, type, flags); } + tcg_debug_assert(con_set >= 0); + tcg_debug_assert(con_set < ARRAY_SIZE(constraint_sets)); + + /* The constraint arguments must match TCGOpcode arguments. */ + tcg_debug_assert(constraint_sets[con_set].nb_oargs == def->nb_oargs); + tcg_debug_assert(constraint_sets[con_set].nb_iargs == def->nb_iargs); + + return all_cts[con_set]; } static void remove_label_use(TCGOp *op, int idx) @@ -3029,8 +3556,7 @@ void tcg_op_remove(TCGContext *s, TCGOp *op) case INDEX_op_br: remove_label_use(op, 0); break; - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: + case INDEX_op_brcond: remove_label_use(op, 3); break; case INDEX_op_brcond2_i32: @@ -3092,22 +3618,31 @@ static TCGOp *tcg_op_alloc(TCGOpcode opc, unsigned nargs) TCGOp *tcg_emit_op(TCGOpcode opc, unsigned nargs) { TCGOp *op = tcg_op_alloc(opc, nargs); - QTAILQ_INSERT_TAIL(&tcg_ctx->ops, op, link); + + 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, unsigned nargs) + TCGOpcode opc, TCGType type, unsigned nargs) { TCGOp *new_op = tcg_op_alloc(opc, nargs); + + TCGOP_TYPE(new_op) = type; QTAILQ_INSERT_BEFORE(old_op, new_op, link); return new_op; } TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *old_op, - TCGOpcode opc, unsigned nargs) + TCGOpcode opc, TCGType type, unsigned nargs) { TCGOp *new_op = tcg_op_alloc(opc, nargs); + + TCGOP_TYPE(new_op) = type; QTAILQ_INSERT_AFTER(&s->ops, old_op, new_op, link); return new_op; } @@ -3122,8 +3657,7 @@ static void move_label_uses(TCGLabel *to, TCGLabel *from) case INDEX_op_br: op->args[0] = label_arg(to); break; - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: + case INDEX_op_brcond: op->args[3] = label_arg(to); break; case INDEX_op_brcond2_i32: @@ -3442,6 +3976,17 @@ liveness_pass_0(TCGContext *s) } } +static void assert_carry_dead(TCGContext *s) +{ + /* + * Carry operations can be separated by a few insns like mov, + * load or store, but they should always be "close", and + * carry-out operations should always be paired with carry-in. + * At various boundaries, carry must have been consumed. + */ + tcg_debug_assert(!s->carry_live); +} + /* Liveness analysis : update the opc_arg_life array to tell if a given input arguments is dead. Instructions updating dead temporaries are removed. */ @@ -3452,27 +3997,28 @@ liveness_pass_1(TCGContext *s) int nb_temps = s->nb_temps; TCGOp *op, *op_prev; TCGRegSet *prefs; - int i; prefs = tcg_malloc(sizeof(TCGRegSet) * nb_temps); - for (i = 0; i < nb_temps; ++i) { + for (int i = 0; i < nb_temps; ++i) { s->temps[i].state_ptr = prefs + i; } /* ??? Should be redundant with the exit_tb that ends the TB. */ la_func_end(s, nb_globals, nb_temps); + s->carry_live = false; QTAILQ_FOREACH_REVERSE_SAFE(op, &s->ops, link, op_prev) { int nb_iargs, nb_oargs; TCGOpcode opc_new, opc_new2; - bool have_opc_new2; TCGLifeData arg_life = 0; TCGTemp *ts; TCGOpcode opc = op->opc; - const TCGOpDef *def = &tcg_op_defs[opc]; + const TCGOpDef *def; + const TCGArgConstraint *args_ct; switch (opc) { case INDEX_op_call: + assert_carry_dead(s); { const TCGHelperInfo *info = tcg_call_info(op); int call_flags = tcg_call_flags(op); @@ -3482,7 +4028,7 @@ liveness_pass_1(TCGContext *s) /* pure functions can be removed if their result is unused */ if (call_flags & TCG_CALL_NO_SIDE_EFFECTS) { - for (i = 0; i < nb_oargs; i++) { + for (int i = 0; i < nb_oargs; i++) { ts = arg_temp(op->args[i]); if (ts->state != TS_DEAD) { goto do_not_remove_call; @@ -3493,7 +4039,7 @@ liveness_pass_1(TCGContext *s) do_not_remove_call: /* Output args are dead. */ - for (i = 0; i < nb_oargs; i++) { + for (int i = 0; i < nb_oargs; i++) { ts = arg_temp(op->args[i]); if (ts->state & TS_DEAD) { arg_life |= DEAD_ARG << i; @@ -3516,7 +4062,7 @@ liveness_pass_1(TCGContext *s) } /* Record arguments that die in this helper. */ - for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) { + for (int i = nb_oargs; i < nb_iargs + nb_oargs; i++) { ts = arg_temp(op->args[i]); if (ts->state & TS_DEAD) { arg_life |= DEAD_ARG << i; @@ -3536,7 +4082,7 @@ liveness_pass_1(TCGContext *s) * 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--) { + for (int i = nb_iargs - 1; i >= 0; i--) { const TCGCallArgumentLoc *loc = &info->in[i]; ts = arg_temp(op->args[nb_oargs + i]); @@ -3564,7 +4110,7 @@ liveness_pass_1(TCGContext *s) * 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++) { + for (int i = 0; i < nb_iargs; i++) { const TCGCallArgumentLoc *loc = &info->in[i]; ts = arg_temp(op->args[nb_oargs + i]); @@ -3584,6 +4130,7 @@ liveness_pass_1(TCGContext *s) } break; case INDEX_op_insn_start: + assert_carry_dead(s); break; case INDEX_op_discard: /* mark the temporary as dead */ @@ -3592,62 +4139,15 @@ liveness_pass_1(TCGContext *s) la_reset_pref(ts); break; - case INDEX_op_add2_i32: - opc_new = INDEX_op_add_i32; - goto do_addsub2; - case INDEX_op_sub2_i32: - opc_new = INDEX_op_sub_i32; - goto do_addsub2; - case INDEX_op_add2_i64: - opc_new = INDEX_op_add_i64; - goto do_addsub2; - case INDEX_op_sub2_i64: - opc_new = INDEX_op_sub_i64; - do_addsub2: - nb_iargs = 4; - nb_oargs = 2; - /* Test if the high part of the operation is dead, but not - the low part. The result can be optimized to a simple - add or sub. This happens often for x86_64 guest when the - cpu mode is set to 32 bit. */ - if (arg_temp(op->args[1])->state == TS_DEAD) { - if (arg_temp(op->args[0])->state == TS_DEAD) { - goto do_remove; - } - /* Replace the opcode and adjust the args in place, - leaving 3 unused args at the end. */ - op->opc = opc = opc_new; - op->args[1] = op->args[2]; - op->args[2] = op->args[4]; - /* Fall through and mark the single-word operation live. */ - nb_iargs = 2; - nb_oargs = 1; - } - goto do_not_remove; - - case INDEX_op_mulu2_i32: - opc_new = INDEX_op_mul_i32; - opc_new2 = INDEX_op_muluh_i32; - have_opc_new2 = TCG_TARGET_HAS_muluh_i32; - goto do_mul2; - case INDEX_op_muls2_i32: - opc_new = INDEX_op_mul_i32; - opc_new2 = INDEX_op_mulsh_i32; - have_opc_new2 = TCG_TARGET_HAS_mulsh_i32; - goto do_mul2; - case INDEX_op_mulu2_i64: - opc_new = INDEX_op_mul_i64; - opc_new2 = INDEX_op_muluh_i64; - have_opc_new2 = TCG_TARGET_HAS_muluh_i64; - goto do_mul2; - case INDEX_op_muls2_i64: - opc_new = INDEX_op_mul_i64; - opc_new2 = INDEX_op_mulsh_i64; - have_opc_new2 = TCG_TARGET_HAS_mulsh_i64; + case INDEX_op_muls2: + opc_new = INDEX_op_mul; + opc_new2 = INDEX_op_mulsh; goto do_mul2; + case INDEX_op_mulu2: + opc_new = INDEX_op_mul; + opc_new2 = INDEX_op_muluh; do_mul2: - nb_iargs = 2; - nb_oargs = 2; + assert_carry_dead(s); if (arg_temp(op->args[1])->state == TS_DEAD) { if (arg_temp(op->args[0])->state == TS_DEAD) { /* Both parts of the operation are dead. */ @@ -3657,7 +4157,8 @@ liveness_pass_1(TCGContext *s) op->opc = opc = opc_new; op->args[1] = op->args[2]; op->args[2] = op->args[3]; - } else if (arg_temp(op->args[0])->state == TS_DEAD && have_opc_new2) { + } else if (arg_temp(op->args[0])->state == TS_DEAD && + tcg_op_supported(opc_new2, TCGOP_TYPE(op), 0)) { /* The low part of the operation is dead; generate the high. */ op->opc = opc = opc_new2; op->args[0] = op->args[1]; @@ -3667,19 +4168,94 @@ liveness_pass_1(TCGContext *s) goto do_not_remove; } /* Mark the single-word operation live. */ - nb_oargs = 1; goto do_not_remove; - default: - /* XXX: optimize by hardcoding common cases (e.g. triadic ops) */ - nb_iargs = def->nb_iargs; - nb_oargs = def->nb_oargs; + case INDEX_op_addco: + if (s->carry_live) { + goto do_not_remove; + } + op->opc = opc = INDEX_op_add; + goto do_default; + + case INDEX_op_addcio: + if (s->carry_live) { + goto do_not_remove; + } + op->opc = opc = INDEX_op_addci; + goto do_default; + + case INDEX_op_subbo: + if (s->carry_live) { + goto do_not_remove; + } + /* Lower to sub, but this may also require canonicalization. */ + op->opc = opc = INDEX_op_sub; + ts = arg_temp(op->args[2]); + if (ts->kind == TEMP_CONST) { + ts = tcg_constant_internal(ts->type, -ts->val); + if (ts->state_ptr == NULL) { + tcg_debug_assert(temp_idx(ts) == nb_temps); + nb_temps++; + ts->state_ptr = tcg_malloc(sizeof(TCGRegSet)); + ts->state = TS_DEAD; + la_reset_pref(ts); + } + op->args[2] = temp_arg(ts); + op->opc = opc = INDEX_op_add; + } + goto do_default; + + case INDEX_op_subbio: + if (s->carry_live) { + goto do_not_remove; + } + op->opc = opc = INDEX_op_subbi; + goto do_default; + + case INDEX_op_addc1o: + if (s->carry_live) { + goto do_not_remove; + } + /* Lower to add, add +1. */ + op_prev = tcg_op_insert_before(s, op, INDEX_op_add, + TCGOP_TYPE(op), 3); + op_prev->args[0] = op->args[0]; + op_prev->args[1] = op->args[1]; + op_prev->args[2] = op->args[2]; + op->opc = opc = INDEX_op_add; + op->args[1] = op->args[0]; + ts = arg_temp(op->args[0]); + ts = tcg_constant_internal(ts->type, 1); + op->args[2] = temp_arg(ts); + goto do_default; - /* Test if the operation can be removed because all - its outputs are dead. We assume that nb_oargs == 0 - implies side effects */ - if (!(def->flags & TCG_OPF_SIDE_EFFECTS) && nb_oargs != 0) { - for (i = 0; i < nb_oargs; i++) { + case INDEX_op_subb1o: + if (s->carry_live) { + goto do_not_remove; + } + /* Lower to sub, add -1. */ + op_prev = tcg_op_insert_before(s, op, INDEX_op_sub, + TCGOP_TYPE(op), 3); + op_prev->args[0] = op->args[0]; + op_prev->args[1] = op->args[1]; + op_prev->args[2] = op->args[2]; + op->opc = opc = INDEX_op_add; + op->args[1] = op->args[0]; + ts = arg_temp(op->args[0]); + ts = tcg_constant_internal(ts->type, -1); + op->args[2] = temp_arg(ts); + goto do_default; + + default: + do_default: + /* + * Test if the operation can be removed because all + * its outputs are dead. We assume that nb_oargs == 0 + * implies side effects. + */ + def = &tcg_op_defs[opc]; + if (!(def->flags & TCG_OPF_SIDE_EFFECTS) && def->nb_oargs != 0) { + for (int i = def->nb_oargs - 1; i >= 0; i--) { if (arg_temp(op->args[i])->state != TS_DEAD) { goto do_not_remove; } @@ -3693,7 +4269,11 @@ liveness_pass_1(TCGContext *s) break; do_not_remove: - for (i = 0; i < nb_oargs; i++) { + def = &tcg_op_defs[opc]; + nb_iargs = def->nb_iargs; + nb_oargs = def->nb_oargs; + + for (int i = 0; i < nb_oargs; i++) { ts = arg_temp(op->args[i]); /* Remember the preference of the uses that followed. */ @@ -3714,12 +4294,16 @@ liveness_pass_1(TCGContext *s) /* If end of basic block, update. */ if (def->flags & TCG_OPF_BB_EXIT) { + assert_carry_dead(s); la_func_end(s, nb_globals, nb_temps); } else if (def->flags & TCG_OPF_COND_BRANCH) { + assert_carry_dead(s); la_bb_sync(s, nb_globals, nb_temps); } else if (def->flags & TCG_OPF_BB_END) { + assert_carry_dead(s); la_bb_end(s, nb_globals, nb_temps); } else if (def->flags & TCG_OPF_SIDE_EFFECTS) { + assert_carry_dead(s); la_global_sync(s, nb_globals); if (def->flags & TCG_OPF_CALL_CLOBBER) { la_cross_call(s, nb_temps); @@ -3727,15 +4311,18 @@ liveness_pass_1(TCGContext *s) } /* Record arguments that die in this opcode. */ - for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { + for (int i = nb_oargs; i < nb_oargs + nb_iargs; i++) { ts = arg_temp(op->args[i]); if (ts->state & TS_DEAD) { arg_life |= DEAD_ARG << i; } } + if (def->flags & TCG_OPF_CARRY_OUT) { + s->carry_live = false; + } /* Input arguments are live for preceding opcodes. */ - for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { + for (int i = nb_oargs; i < nb_oargs + nb_iargs; i++) { ts = arg_temp(op->args[i]); if (ts->state & TS_DEAD) { /* For operands that were dead, initially allow @@ -3744,11 +4331,13 @@ liveness_pass_1(TCGContext *s) ts->state &= ~TS_DEAD; } } + if (def->flags & TCG_OPF_CARRY_IN) { + s->carry_live = true; + } /* Incorporate constraints for this operand. */ switch (opc) { - case INDEX_op_mov_i32: - case INDEX_op_mov_i64: + case INDEX_op_mov: /* Note that these are TCG_OPF_NOT_PRESENT and do not have proper constraints. That said, special case moves to propagate preferences backward. */ @@ -3759,8 +4348,9 @@ liveness_pass_1(TCGContext *s) break; default: - for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { - const TCGArgConstraint *ct = &def->args_ct[i]; + args_ct = opcode_args_ct(op); + for (int i = nb_oargs; i < nb_oargs + nb_iargs; i++) { + const TCGArgConstraint *ct = &args_ct[i]; TCGRegSet set, *pset; ts = arg_temp(op->args[i]); @@ -3783,6 +4373,7 @@ liveness_pass_1(TCGContext *s) } op->life = arg_life; } + assert_carry_dead(s); } /* Liveness analysis: Convert indirect regs to direct temporaries. */ @@ -3853,10 +4444,8 @@ liveness_pass_2(TCGContext *s) arg_ts = arg_temp(op->args[i]); 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); + TCGOp *lop = tcg_op_insert_before(s, op, INDEX_op_ld, + arg_ts->type, 3); lop->args[0] = temp_arg(dir_ts); lop->args[1] = temp_arg(arg_ts->mem_base); @@ -3905,7 +4494,7 @@ liveness_pass_2(TCGContext *s) } /* Outputs become available. */ - if (opc == INDEX_op_mov_i32 || opc == INDEX_op_mov_i64) { + if (opc == INDEX_op_mov) { arg_ts = arg_temp(op->args[0]); dir_ts = arg_ts->state_ptr; if (dir_ts) { @@ -3916,10 +4505,8 @@ liveness_pass_2(TCGContext *s) arg_ts->state = 0; if (NEED_SYNC_ARG(0)) { - 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, 3); + TCGOp *sop = tcg_op_insert_after(s, op, INDEX_op_st, + arg_ts->type, 3); TCGTemp *out_ts = dir_ts; if (IS_DEAD_ARG(0)) { @@ -3952,10 +4539,8 @@ liveness_pass_2(TCGContext *s) /* Sync outputs upon their last write. */ if (NEED_SYNC_ARG(i)) { - 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, 3); + TCGOp *sop = tcg_op_insert_after(s, op, INDEX_op_st, + arg_ts->type, 3); sop->args[0] = temp_arg(dir_ts); sop->args[1] = temp_arg(arg_ts->mem_base); @@ -4313,6 +4898,9 @@ static void temp_load(TCGContext *s, TCGTemp *ts, TCGRegSet desired_regs, ts->mem_coherent = 0; break; case TEMP_VAL_MEM: + if (!ts->mem_allocated) { + temp_allocate_frame(s, ts); + } reg = tcg_reg_alloc(s, desired_regs, allocated_regs, preferred_regs, ts->indirect_base); tcg_out_ld(s, ts->type, reg, ts->mem_base->reg, ts->mem_offset); @@ -4365,9 +4953,8 @@ static void sync_globals(TCGContext *s, TCGRegSet allocated_regs) all globals are stored at their canonical location. */ static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs) { - int i; - - for (i = s->nb_globals; i < s->nb_temps; i++) { + assert_carry_dead(s); + for (int i = s->nb_globals; i < s->nb_temps; i++) { TCGTemp *ts = &s->temps[i]; switch (ts->kind) { @@ -4398,6 +4985,7 @@ static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs) */ static void tcg_reg_alloc_cbranch(TCGContext *s, TCGRegSet allocated_regs) { + assert_carry_dead(s); sync_globals(s, allocated_regs); for (int i = s->nb_globals; i < s->nb_temps; i++) { @@ -4547,6 +5135,7 @@ static void tcg_reg_alloc_dup(TCGContext *s, const TCGOp *op) { const TCGLifeData arg_life = op->life; TCGRegSet dup_out_regs, dup_in_regs; + const TCGArgConstraint *dup_args_ct; TCGTemp *its, *ots; TCGType itype, vtype; unsigned vece; @@ -4561,7 +5150,7 @@ static void tcg_reg_alloc_dup(TCGContext *s, const TCGOp *op) itype = its->type; vece = TCGOP_VECE(op); - vtype = TCGOP_VECL(op) + TCG_TYPE_V64; + vtype = TCGOP_TYPE(op); if (its->val_type == TEMP_VAL_CONST) { /* Propagate constant via movi -> dupi. */ @@ -4573,8 +5162,9 @@ static void tcg_reg_alloc_dup(TCGContext *s, const TCGOp *op) return; } - dup_out_regs = tcg_op_defs[INDEX_op_dup_vec].args_ct[0].regs; - dup_in_regs = tcg_op_defs[INDEX_op_dup_vec].args_ct[1].regs; + dup_args_ct = opcode_args_ct(op); + dup_out_regs = dup_args_ct[0].regs; + dup_in_regs = dup_args_ct[1].regs; /* Allocate the output register now. */ if (ots->val_type != TEMP_VAL_REG) { @@ -4660,10 +5250,16 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) int i, k, nb_iargs, nb_oargs; TCGReg reg; TCGArg arg; + const TCGArgConstraint *args_ct; const TCGArgConstraint *arg_ct; TCGTemp *ts; TCGArg new_args[TCG_MAX_OP_ARGS]; int const_args[TCG_MAX_OP_ARGS]; + TCGCond op_cond; + + if (def->flags & TCG_OPF_CARRY_IN) { + tcg_debug_assert(s->carry_live); + } nb_oargs = def->nb_oargs; nb_iargs = def->nb_iargs; @@ -4676,6 +5272,31 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) i_allocated_regs = s->reserved_regs; o_allocated_regs = s->reserved_regs; + switch (op->opc) { + case INDEX_op_brcond: + op_cond = op->args[2]; + break; + case INDEX_op_setcond: + case INDEX_op_negsetcond: + 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: + 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; + } + + args_ct = opcode_args_ct(op); + /* satisfy input constraints */ for (k = 0; k < nb_iargs; k++) { TCGRegSet i_preferred_regs, i_required_regs; @@ -4683,17 +5304,28 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) TCGTemp *ts2; int i1, i2; - i = def->args_ct[nb_oargs + k].sort_index; + i = args_ct[nb_oargs + k].sort_index; arg = op->args[i]; - arg_ct = &def->args_ct[i]; + arg_ct = &args_ct[i]; ts = arg_temp(arg); - if (ts->val_type == TEMP_VAL_CONST - && tcg_target_const_match(ts->val, ts->type, arg_ct->ct)) { - /* constant is OK for instruction */ - const_args[i] = 1; - new_args[i] = ts->val; - continue; + if (ts->val_type == TEMP_VAL_CONST) { +#ifdef TCG_REG_ZERO + if (ts->val == 0 && (arg_ct->ct & TCG_CT_REG_ZERO)) { + /* Hardware zero register: indicate register via non-const. */ + const_args[i] = 0; + new_args[i] = TCG_REG_ZERO; + continue; + } +#endif + + if (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; + } } reg = ts->reg; @@ -4714,7 +5346,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) * register and move it. */ if (temp_readonly(ts) || !IS_DEAD_ARG(i) - || def->args_ct[arg_ct->alias_index].newreg) { + || args_ct[arg_ct->alias_index].newreg) { allocate_new_reg = true; } else if (ts->val_type == TEMP_VAL_REG) { /* @@ -4885,6 +5517,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) { + assert_carry_dead(s); /* 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)) { @@ -4899,10 +5532,10 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } /* satisfy the output constraints */ - for(k = 0; k < nb_oargs; k++) { - i = def->args_ct[k].sort_index; + for (k = 0; k < nb_oargs; k++) { + i = args_ct[k].sort_index; arg = op->args[i]; - arg_ct = &def->args_ct[i]; + arg_ct = &args_ct[i]; ts = arg_temp(arg); /* ENV should not be modified. */ @@ -4923,17 +5556,21 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; case 1: /* first of pair */ - tcg_debug_assert(!arg_ct->newreg); if (arg_ct->oalias) { reg = new_args[arg_ct->alias_index]; - break; + } 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); } - 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 */ - tcg_debug_assert(!arg_ct->newreg); if (arg_ct->oalias) { reg = new_args[arg_ct->alias_index]; } else { @@ -4957,50 +5594,345 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } /* emit instruction */ + TCGType type = TCGOP_TYPE(op); switch (op->opc) { - case INDEX_op_ext8s_i32: - tcg_out_ext8s(s, TCG_TYPE_I32, new_args[0], new_args[1]); + case INDEX_op_addc1o: + tcg_out_set_carry(s); + /* fall through */ + case INDEX_op_add: + case INDEX_op_addcio: + case INDEX_op_addco: + case INDEX_op_and: + case INDEX_op_andc: + case INDEX_op_clz: + case INDEX_op_ctz: + case INDEX_op_divs: + case INDEX_op_divu: + case INDEX_op_eqv: + case INDEX_op_mul: + case INDEX_op_mulsh: + case INDEX_op_muluh: + case INDEX_op_nand: + case INDEX_op_nor: + case INDEX_op_or: + case INDEX_op_orc: + case INDEX_op_rems: + case INDEX_op_remu: + case INDEX_op_rotl: + case INDEX_op_rotr: + case INDEX_op_sar: + case INDEX_op_shl: + case INDEX_op_shr: + case INDEX_op_xor: + { + const TCGOutOpBinary *out = + container_of(all_outop[op->opc], TCGOutOpBinary, base); + + /* Constants should never appear in the first source operand. */ + tcg_debug_assert(!const_args[1]); + if (const_args[2]) { + out->out_rri(s, type, new_args[0], new_args[1], new_args[2]); + } else { + out->out_rrr(s, type, new_args[0], new_args[1], new_args[2]); + } + } + break; + + case INDEX_op_sub: + { + const TCGOutOpSubtract *out = &outop_sub; + + /* + * Constants should never appear in the second source operand. + * These are folded to add with negative constant. + */ + tcg_debug_assert(!const_args[2]); + if (const_args[1]) { + out->out_rir(s, type, new_args[0], new_args[1], new_args[2]); + } else { + out->out_rrr(s, type, new_args[0], new_args[1], new_args[2]); + } + } + break; + + case INDEX_op_subb1o: + tcg_out_set_borrow(s); + /* fall through */ + case INDEX_op_addci: + case INDEX_op_subbi: + case INDEX_op_subbio: + case INDEX_op_subbo: + { + const TCGOutOpAddSubCarry *out = + container_of(all_outop[op->opc], TCGOutOpAddSubCarry, base); + + if (const_args[2]) { + if (const_args[1]) { + out->out_rii(s, type, new_args[0], + new_args[1], new_args[2]); + } else { + out->out_rri(s, type, new_args[0], + new_args[1], new_args[2]); + } + } else if (const_args[1]) { + out->out_rir(s, type, new_args[0], new_args[1], new_args[2]); + } else { + out->out_rrr(s, type, new_args[0], new_args[1], new_args[2]); + } + } break; - case INDEX_op_ext8s_i64: - tcg_out_ext8s(s, TCG_TYPE_I64, new_args[0], new_args[1]); + + case INDEX_op_bswap64: + case INDEX_op_ext_i32_i64: + case INDEX_op_extu_i32_i64: + case INDEX_op_extrl_i64_i32: + case INDEX_op_extrh_i64_i32: + assert(TCG_TARGET_REG_BITS == 64); + /* fall through */ + case INDEX_op_ctpop: + case INDEX_op_neg: + case INDEX_op_not: + { + const TCGOutOpUnary *out = + container_of(all_outop[op->opc], TCGOutOpUnary, base); + + /* Constants should have been folded. */ + tcg_debug_assert(!const_args[1]); + out->out_rr(s, type, new_args[0], new_args[1]); + } + break; + + case INDEX_op_bswap16: + case INDEX_op_bswap32: + { + const TCGOutOpBswap *out = + container_of(all_outop[op->opc], TCGOutOpBswap, base); + + tcg_debug_assert(!const_args[1]); + out->out_rr(s, type, new_args[0], new_args[1], new_args[2]); + } break; - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - tcg_out_ext8u(s, new_args[0], new_args[1]); + + case INDEX_op_deposit: + { + const TCGOutOpDeposit *out = &outop_deposit; + + if (const_args[2]) { + tcg_debug_assert(!const_args[1]); + out->out_rri(s, type, new_args[0], new_args[1], + new_args[2], new_args[3], new_args[4]); + } else if (const_args[1]) { + tcg_debug_assert(new_args[1] == 0); + tcg_debug_assert(!const_args[2]); + out->out_rzr(s, type, new_args[0], new_args[2], + new_args[3], new_args[4]); + } else { + out->out_rrr(s, type, new_args[0], new_args[1], + new_args[2], new_args[3], new_args[4]); + } + } break; - case INDEX_op_ext16s_i32: - tcg_out_ext16s(s, TCG_TYPE_I32, new_args[0], new_args[1]); + + case INDEX_op_divs2: + case INDEX_op_divu2: + { + const TCGOutOpDivRem *out = + container_of(all_outop[op->opc], TCGOutOpDivRem, base); + + /* Only used by x86 and s390x, which use matching constraints. */ + tcg_debug_assert(new_args[0] == new_args[2]); + tcg_debug_assert(new_args[1] == new_args[3]); + tcg_debug_assert(!const_args[4]); + out->out_rr01r(s, type, new_args[0], new_args[1], new_args[4]); + } break; - case INDEX_op_ext16s_i64: - tcg_out_ext16s(s, TCG_TYPE_I64, new_args[0], new_args[1]); + + case INDEX_op_extract: + case INDEX_op_sextract: + { + const TCGOutOpExtract *out = + container_of(all_outop[op->opc], TCGOutOpExtract, base); + + tcg_debug_assert(!const_args[1]); + out->out_rr(s, type, new_args[0], new_args[1], + new_args[2], new_args[3]); + } break; - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - tcg_out_ext16u(s, new_args[0], new_args[1]); + + case INDEX_op_extract2: + { + const TCGOutOpExtract2 *out = &outop_extract2; + + tcg_debug_assert(!const_args[1]); + tcg_debug_assert(!const_args[2]); + out->out_rrr(s, type, new_args[0], new_args[1], + new_args[2], new_args[3]); + } break; - case INDEX_op_ext32s_i64: - tcg_out_ext32s(s, new_args[0], new_args[1]); + + case INDEX_op_ld8u: + case INDEX_op_ld8s: + case INDEX_op_ld16u: + case INDEX_op_ld16s: + case INDEX_op_ld32u: + case INDEX_op_ld32s: + case INDEX_op_ld: + { + const TCGOutOpLoad *out = + container_of(all_outop[op->opc], TCGOutOpLoad, base); + + tcg_debug_assert(!const_args[1]); + out->out(s, type, new_args[0], new_args[1], new_args[2]); + } break; - case INDEX_op_ext32u_i64: - tcg_out_ext32u(s, new_args[0], new_args[1]); + + case INDEX_op_muls2: + case INDEX_op_mulu2: + { + const TCGOutOpMul2 *out = + container_of(all_outop[op->opc], TCGOutOpMul2, base); + + tcg_debug_assert(!const_args[2]); + tcg_debug_assert(!const_args[3]); + out->out_rrrr(s, type, new_args[0], new_args[1], + new_args[2], new_args[3]); + } break; - case INDEX_op_ext_i32_i64: - tcg_out_exts_i32_i64(s, new_args[0], new_args[1]); + + case INDEX_op_st32: + /* Use tcg_op_st w/ I32. */ + type = TCG_TYPE_I32; + /* fall through */ + case INDEX_op_st: + case INDEX_op_st8: + case INDEX_op_st16: + { + const TCGOutOpStore *out = + container_of(all_outop[op->opc], TCGOutOpStore, base); + + if (const_args[0]) { + out->out_i(s, type, new_args[0], new_args[1], new_args[2]); + } else { + out->out_r(s, type, new_args[0], new_args[1], new_args[2]); + } + } break; - case INDEX_op_extu_i32_i64: - tcg_out_extu_i32_i64(s, new_args[0], new_args[1]); + + case INDEX_op_qemu_ld: + case INDEX_op_qemu_st: + { + const TCGOutOpQemuLdSt *out = + container_of(all_outop[op->opc], TCGOutOpQemuLdSt, base); + + out->out(s, type, new_args[0], new_args[1], new_args[2]); + } break; - case INDEX_op_extrl_i64_i32: - tcg_out_extrl_i64_i32(s, new_args[0], new_args[1]); + + case INDEX_op_qemu_ld2: + case INDEX_op_qemu_st2: + { + const TCGOutOpQemuLdSt2 *out = + container_of(all_outop[op->opc], TCGOutOpQemuLdSt2, base); + + out->out(s, type, new_args[0], new_args[1], + new_args[2], new_args[3]); + } 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); + + case INDEX_op_brcond: + { + const TCGOutOpBrcond *out = &outop_brcond; + TCGCond cond = new_args[2]; + TCGLabel *label = arg_label(new_args[3]); + + tcg_debug_assert(!const_args[0]); + if (const_args[1]) { + out->out_ri(s, type, cond, new_args[0], new_args[1], label); + } else { + out->out_rr(s, type, cond, new_args[0], new_args[1], label); + } + } + break; + + case INDEX_op_movcond: + { + const TCGOutOpMovcond *out = &outop_movcond; + TCGCond cond = new_args[5]; + + tcg_debug_assert(!const_args[1]); + out->out(s, type, cond, new_args[0], + new_args[1], new_args[2], const_args[2], + new_args[3], const_args[3], + new_args[4], const_args[4]); + } + break; + + case INDEX_op_setcond: + case INDEX_op_negsetcond: + { + const TCGOutOpSetcond *out = + container_of(all_outop[op->opc], TCGOutOpSetcond, base); + TCGCond cond = new_args[3]; + + tcg_debug_assert(!const_args[1]); + if (const_args[2]) { + out->out_rri(s, type, cond, + new_args[0], new_args[1], new_args[2]); + } else { + out->out_rrr(s, type, cond, + new_args[0], new_args[1], new_args[2]); + } + } + break; + +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_brcond2_i32: + { + const TCGOutOpBrcond2 *out = &outop_brcond2; + TCGCond cond = new_args[4]; + TCGLabel *label = arg_label(new_args[5]); + + tcg_debug_assert(!const_args[0]); + tcg_debug_assert(!const_args[1]); + out->out(s, cond, new_args[0], new_args[1], + new_args[2], const_args[2], + new_args[3], const_args[3], label); + } + break; + case INDEX_op_setcond2_i32: + { + const TCGOutOpSetcond2 *out = &outop_setcond2; + TCGCond cond = new_args[5]; + + tcg_debug_assert(!const_args[1]); + tcg_debug_assert(!const_args[2]); + out->out(s, cond, new_args[0], new_args[1], new_args[2], + new_args[3], const_args[3], new_args[4], const_args[4]); } break; +#else + case INDEX_op_brcond2_i32: + case INDEX_op_setcond2_i32: + g_assert_not_reached(); +#endif + + case INDEX_op_goto_ptr: + tcg_debug_assert(!const_args[0]); + tcg_out_goto_ptr(s, new_args[0]); + break; + + default: + tcg_debug_assert(def->flags & TCG_OPF_VECTOR); + tcg_out_vec_op(s, op->opc, type - TCG_TYPE_V64, + TCGOP_VECE(op), new_args, const_args); + break; + } + + if (def->flags & TCG_OPF_CARRY_IN) { + s->carry_live = false; + } + if (def->flags & TCG_OPF_CARRY_OUT) { + s->carry_live = true; } /* move the outputs in the correct register if needed */ @@ -5022,7 +5954,7 @@ static bool tcg_reg_alloc_dup2(TCGContext *s, const TCGOp *op) { const TCGLifeData arg_life = op->life; TCGTemp *ots, *itsl, *itsh; - TCGType vtype = TCGOP_VECL(op) + TCG_TYPE_V64; + TCGType vtype = TCGOP_TYPE(op); /* This opcode is only valid for 32-bit hosts, for 64-bit elements. */ tcg_debug_assert(TCG_TARGET_REG_BITS == 32); @@ -5038,8 +5970,7 @@ static bool tcg_reg_alloc_dup2(TCGContext *s, const TCGOp *op) /* Allocate the output register now. */ if (ots->val_type != TEMP_VAL_REG) { TCGRegSet allocated_regs = s->reserved_regs; - TCGRegSet dup_out_regs = - tcg_op_defs[INDEX_op_dup_vec].args_ct[0].regs; + TCGRegSet dup_out_regs = opcode_args_ct(op)[0].regs; TCGReg oreg; /* Make sure to not spill the input registers. */ @@ -5324,18 +6255,11 @@ static void tcg_reg_alloc_call(TCGContext *s, TCGOp *op) static TCGAtomAlign atom_and_align_for_opc(TCGContext *s, MemOp opc, MemOp host_atom, bool allow_two_ops) { - MemOp align = get_alignment_bits(opc); + 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; - MemOp atom; - - /* When serialized, no further atomicity required. */ - if (s->gen_tb->cflags & CF_PARALLEL) { - atom = opc & MO_ATOM_MASK; - } else { - atom = MO_ATOM_NONE; - } switch (atom) { case MO_ATOM_NONE: @@ -5677,7 +6601,7 @@ static void tcg_out_ld_helper_args(TCGContext *s, const TCGLabelQemuLdst *ldst, */ tcg_out_helper_add_mov(mov, loc + HOST_BIG_ENDIAN, TCG_TYPE_I32, TCG_TYPE_I32, - ldst->addrlo_reg, -1); + ldst->addr_reg, -1); tcg_out_helper_load_slots(s, 1, mov, parm); tcg_out_helper_load_imm(s, loc[!HOST_BIG_ENDIAN].arg_slot, @@ -5685,7 +6609,7 @@ static void tcg_out_ld_helper_args(TCGContext *s, const TCGLabelQemuLdst *ldst, next_arg += 2; } else { nmov = tcg_out_helper_add_mov(mov, loc, TCG_TYPE_I64, s->addr_type, - ldst->addrlo_reg, ldst->addrhi_reg); + ldst->addr_reg, -1); tcg_out_helper_load_slots(s, nmov, mov, parm); next_arg += nmov; } @@ -5842,21 +6766,22 @@ static void tcg_out_st_helper_args(TCGContext *s, const TCGLabelQemuLdst *ldst, /* Handle addr argument. */ loc = &info->in[next_arg]; - if (TCG_TARGET_REG_BITS == 32 && s->addr_type == TCG_TYPE_I32) { + tcg_debug_assert(s->addr_type <= TCG_TYPE_REG); + if (TCG_TARGET_REG_BITS == 32) { /* - * 32-bit host with 32-bit guest: zero-extend the guest address + * 32-bit host (and thus 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); + ldst->addr_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); + ldst->addr_reg, -1); next_arg += n; nmov += n; } @@ -5904,7 +6829,7 @@ static void tcg_out_st_helper_args(TCGContext *s, const TCGLabelQemuLdst *ldst, g_assert_not_reached(); } - if (TCG_TARGET_REG_BITS == 32 && s->addr_type == TCG_TYPE_I32) { + if (TCG_TARGET_REG_BITS == 32) { /* 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); @@ -5913,14 +6838,9 @@ static void tcg_out_st_helper_args(TCGContext *s, const TCGLabelQemuLdst *ldst, tcg_out_helper_load_common_args(s, ldst, parm, info, next_arg); } -void tcg_dump_op_count(GString *buf) -{ - g_string_append_printf(buf, "[TCG profiler not compiled]\n"); -} - int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) { - int i, start_words, num_insns; + int i, num_insns; TCGOp *op; if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP) @@ -5951,6 +6871,9 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) } #endif + /* Do not reuse any EBB that may be allocated within the TB. */ + tcg_temp_ebb_reset_freed(s); + tcg_optimize(s); reachable_code_pass(s); @@ -6002,25 +6925,39 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t 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); -#endif -#ifdef TCG_TARGET_NEED_POOL_LABELS 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_malloc(sizeof(uint64_t) * s->gen_tb->icount * INSN_START_WORDS); + + tcg_out_tb_start(s); num_insns = -1; + s->carry_live = false; QTAILQ_FOREACH(op, &s->ops, link) { TCGOpcode opc = op->opc; switch (opc) { - case INDEX_op_mov_i32: - case INDEX_op_mov_i64: + case INDEX_op_extrl_i64_i32: + assert(TCG_TARGET_REG_BITS == 64); + /* + * If TCG_TYPE_I32 is represented in some canonical form, + * e.g. zero or sign-extended, then emit as a unary op. + * Otherwise we can treat this as a plain move. + * If the output dies, treat this as a plain move, because + * this will be implemented with a store. + */ + if (TCG_TARGET_HAS_extr_i64_i32) { + TCGLifeData arg_life = op->life; + if (!IS_DEAD_ARG(0)) { + goto do_default; + } + } + /* fall through */ + case INDEX_op_mov: case INDEX_op_mov_vec: tcg_reg_alloc_mov(s, op); break; @@ -6028,6 +6965,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) tcg_reg_alloc_dup(s, op); break; case INDEX_op_insn_start: + assert_carry_dead(s); if (num_insns >= 0) { size_t off = tcg_current_code_size(s); s->gen_insn_end_off[num_insns] = off; @@ -6035,8 +6973,8 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) assert(s->gen_insn_end_off[num_insns] == off); } num_insns++; - for (i = 0; i < start_words; ++i) { - s->gen_insn_data[num_insns * start_words + i] = + for (i = 0; i < INSN_START_WORDS; ++i) { + s->gen_insn_data[num_insns * INSN_START_WORDS + i] = tcg_get_insn_start_param(op, i); } break; @@ -6048,6 +6986,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) tcg_out_label(s, arg_label(op->args[0])); break; case INDEX_op_call: + assert_carry_dead(s); tcg_reg_alloc_call(s, op); break; case INDEX_op_exit_tb: @@ -6056,14 +6995,22 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) case INDEX_op_goto_tb: tcg_out_goto_tb(s, op->args[0]); break; + case INDEX_op_br: + tcg_out_br(s, arg_label(op->args[0])); + break; + case INDEX_op_mb: + tcg_out_mb(s, op->args[0]); + break; case INDEX_op_dup2_vec: if (tcg_reg_alloc_dup2(s, op)) { break; } /* fall through */ default: + do_default: /* Sanity check that we've not introduced any unhandled opcodes. */ - tcg_debug_assert(tcg_op_supported(opc)); + tcg_debug_assert(tcg_op_supported(opc, TCGOP_TYPE(op), + TCGOP_FLAGS(op))); /* Note: in order to speed up the code, it would be much faster to have specialized register allocator functions for some common argument patterns */ @@ -6082,22 +7029,20 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) return -2; } } + assert_carry_dead(s); + 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 */ -#ifdef TCG_TARGET_NEED_LDST_LABELS i = tcg_out_ldst_finalize(s); if (i < 0) { return i; } -#endif -#ifdef TCG_TARGET_NEED_POOL_LABELS i = tcg_out_pool_finalize(s); if (i < 0) { return i; } -#endif if (!tcg_resolve_relocs(s)) { return -2; } @@ -6112,11 +7057,6 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) return tcg_current_code_size(s); } -void tcg_dump_info(GString *buf) -{ - g_string_append_printf(buf, "[TCG profiler not compiled]\n"); -} - #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 4640902c881..700e6726169 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -19,10 +19,18 @@ #include "qemu/osdep.h" #include "tcg/tcg.h" +#include "tcg/helper-info.h" #include "tcg/tcg-ldst.h" +#include "disas/dis-asm.h" +#include "tcg-has.h" #include +#define ctpop_tr glue(ctpop, TCG_TARGET_REG_BITS) +#define deposit_tr glue(deposit, TCG_TARGET_REG_BITS) +#define extract_tr glue(extract, TCG_TARGET_REG_BITS) +#define sextract_tr glue(sextract, TCG_TARGET_REG_BITS) + /* * Enable TCI assertions only when debugging TCG (and without NDEBUG defined). * Without assertions, the interpreter runs much faster. @@ -151,16 +159,6 @@ static void tci_args_rrrbb(uint32_t insn, TCGReg *r0, TCGReg *r1, *i4 = extract32(insn, 26, 6); } -static void tci_args_rrrrr(uint32_t insn, TCGReg *r0, TCGReg *r1, - TCGReg *r2, TCGReg *r3, TCGReg *r4) -{ - *r0 = extract32(insn, 8, 4); - *r1 = extract32(insn, 12, 4); - *r2 = extract32(insn, 16, 4); - *r3 = extract32(insn, 20, 4); - *r4 = extract32(insn, 24, 4); -} - static void tci_args_rrrr(uint32_t insn, TCGReg *r0, TCGReg *r1, TCGReg *r2, TCGReg *r3) { @@ -181,17 +179,6 @@ static void tci_args_rrrrrc(uint32_t insn, TCGReg *r0, TCGReg *r1, *c5 = extract32(insn, 28, 4); } -static void tci_args_rrrrrr(uint32_t insn, TCGReg *r0, TCGReg *r1, - TCGReg *r2, TCGReg *r3, TCGReg *r4, TCGReg *r5) -{ - *r0 = extract32(insn, 8, 4); - *r1 = extract32(insn, 12, 4); - *r2 = extract32(insn, 16, 4); - *r3 = extract32(insn, 20, 4); - *r4 = extract32(insn, 24, 4); - *r5 = extract32(insn, 28, 4); -} - static bool tci_compare32(uint32_t u0, uint32_t u1, TCGCond condition) { bool result = false; @@ -228,6 +215,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(); } @@ -270,6 +263,12 @@ 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(); } @@ -326,18 +325,6 @@ static void tci_qemu_st(CPUArchState *env, uint64_t taddr, uint64_t val, } } -#if TCG_TARGET_REG_BITS == 64 -# define CASE_32_64(x) \ - case glue(glue(INDEX_op_, x), _i64): \ - case glue(glue(INDEX_op_, x), _i32): -# define CASE_64(x) \ - case glue(glue(INDEX_op_, x), _i64): -#else -# define CASE_32_64(x) \ - case glue(glue(INDEX_op_, x), _i32): -# define CASE_64(x) -#endif - /* Interpret pseudo code in tb. */ /* * Disable CFI checks. @@ -351,6 +338,7 @@ 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)]; + bool carry = false; regs[TCG_AREG0] = (tcg_target_ulong)env; regs[TCG_REG_CALL_STACK] = (uintptr_t)stack; @@ -359,13 +347,12 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, for (;;) { uint32_t insn; TCGOpcode opc; - TCGReg r0, r1, r2, r3, r4, r5; + TCGReg r0, r1, r2, r3, r4; tcg_target_ulong t1; TCGCond condition; uint8_t pos, len; uint32_t tmp32; uint64_t tmp64, taddr; - uint64_t T1, T2; MemOpIdx oi; int32_t ofs; void *ptr; @@ -431,34 +418,25 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_l(insn, tb_ptr, &ptr); tb_ptr = ptr; continue; - case INDEX_op_setcond_i32: - tci_args_rrrc(insn, &r0, &r1, &r2, &condition); - regs[r0] = tci_compare32(regs[r1], regs[r2], condition); - break; - case INDEX_op_movcond_i32: - tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition); - tmp32 = tci_compare32(regs[r1], regs[r2], condition); - regs[r0] = regs[tmp32 ? r3 : r4]; - break; #if TCG_TARGET_REG_BITS == 32 case INDEX_op_setcond2_i32: tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition); - T1 = tci_uint64(regs[r2], regs[r1]); - T2 = tci_uint64(regs[r4], regs[r3]); - regs[r0] = tci_compare64(T1, T2, condition); + regs[r0] = tci_compare64(tci_uint64(regs[r2], regs[r1]), + tci_uint64(regs[r4], regs[r3]), + condition); break; #elif TCG_TARGET_REG_BITS == 64 - case INDEX_op_setcond_i64: + case INDEX_op_setcond: tci_args_rrrc(insn, &r0, &r1, &r2, &condition); regs[r0] = tci_compare64(regs[r1], regs[r2], condition); break; - case INDEX_op_movcond_i64: + case INDEX_op_movcond: tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition); tmp32 = tci_compare64(regs[r1], regs[r2], condition); regs[r0] = regs[tmp32 ? r3 : r4]; break; #endif - CASE_32_64(mov) + case INDEX_op_mov: tci_args_rr(insn, &r0, &r1); regs[r0] = regs[r1]; break; @@ -470,425 +448,325 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rl(insn, tb_ptr, &r0, &ptr); regs[r0] = *(tcg_target_ulong *)ptr; break; + case INDEX_op_tci_setcarry: + carry = true; + break; /* Load/store operations (32 bit). */ - CASE_32_64(ld8u) + case INDEX_op_ld8u: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); regs[r0] = *(uint8_t *)ptr; break; - CASE_32_64(ld8s) + case INDEX_op_ld8s: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); regs[r0] = *(int8_t *)ptr; break; - CASE_32_64(ld16u) + case INDEX_op_ld16u: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); regs[r0] = *(uint16_t *)ptr; break; - CASE_32_64(ld16s) + case INDEX_op_ld16s: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); regs[r0] = *(int16_t *)ptr; break; - case INDEX_op_ld_i32: - CASE_64(ld32u) + case INDEX_op_ld: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); - regs[r0] = *(uint32_t *)ptr; + regs[r0] = *(tcg_target_ulong *)ptr; break; - CASE_32_64(st8) + case INDEX_op_st8: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); *(uint8_t *)ptr = regs[r0]; break; - CASE_32_64(st16) + case INDEX_op_st16: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); *(uint16_t *)ptr = regs[r0]; break; - case INDEX_op_st_i32: - CASE_64(st32) + case INDEX_op_st: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); - *(uint32_t *)ptr = regs[r0]; + *(tcg_target_ulong *)ptr = regs[r0]; break; /* Arithmetic operations (mixed 32/64 bit). */ - CASE_32_64(add) + case INDEX_op_add: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] + regs[r2]; break; - CASE_32_64(sub) + case INDEX_op_sub: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] - regs[r2]; break; - CASE_32_64(mul) + case INDEX_op_mul: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] * regs[r2]; break; - CASE_32_64(and) + case INDEX_op_and: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] & regs[r2]; break; - CASE_32_64(or) + case INDEX_op_or: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] | regs[r2]; break; - CASE_32_64(xor) + case INDEX_op_xor: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] ^ regs[r2]; break; -#if TCG_TARGET_HAS_andc_i32 || TCG_TARGET_HAS_andc_i64 - CASE_32_64(andc) + case INDEX_op_andc: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] & ~regs[r2]; break; -#endif -#if TCG_TARGET_HAS_orc_i32 || TCG_TARGET_HAS_orc_i64 - CASE_32_64(orc) + case INDEX_op_orc: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] | ~regs[r2]; break; -#endif -#if TCG_TARGET_HAS_eqv_i32 || TCG_TARGET_HAS_eqv_i64 - CASE_32_64(eqv) + case INDEX_op_eqv: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ~(regs[r1] ^ regs[r2]); break; -#endif -#if TCG_TARGET_HAS_nand_i32 || TCG_TARGET_HAS_nand_i64 - CASE_32_64(nand) + case INDEX_op_nand: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ~(regs[r1] & regs[r2]); break; -#endif -#if TCG_TARGET_HAS_nor_i32 || TCG_TARGET_HAS_nor_i64 - CASE_32_64(nor) + case INDEX_op_nor: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ~(regs[r1] | regs[r2]); break; + case INDEX_op_neg: + tci_args_rr(insn, &r0, &r1); + regs[r0] = -regs[r1]; + break; + case INDEX_op_not: + tci_args_rr(insn, &r0, &r1); + regs[r0] = ~regs[r1]; + break; + case INDEX_op_ctpop: + tci_args_rr(insn, &r0, &r1); + regs[r0] = ctpop_tr(regs[r1]); + break; + case INDEX_op_addco: + tci_args_rrr(insn, &r0, &r1, &r2); + t1 = regs[r1] + regs[r2]; + carry = t1 < regs[r1]; + regs[r0] = t1; + break; + case INDEX_op_addci: + tci_args_rrr(insn, &r0, &r1, &r2); + regs[r0] = regs[r1] + regs[r2] + carry; + break; + case INDEX_op_addcio: + tci_args_rrr(insn, &r0, &r1, &r2); + if (carry) { + t1 = regs[r1] + regs[r2] + 1; + carry = t1 <= regs[r1]; + } else { + t1 = regs[r1] + regs[r2]; + carry = t1 < regs[r1]; + } + regs[r0] = t1; + break; + case INDEX_op_subbo: + tci_args_rrr(insn, &r0, &r1, &r2); + carry = regs[r1] < regs[r2]; + regs[r0] = regs[r1] - regs[r2]; + break; + case INDEX_op_subbi: + tci_args_rrr(insn, &r0, &r1, &r2); + regs[r0] = regs[r1] - regs[r2] - carry; + break; + case INDEX_op_subbio: + tci_args_rrr(insn, &r0, &r1, &r2); + if (carry) { + carry = regs[r1] <= regs[r2]; + regs[r0] = regs[r1] - regs[r2] - 1; + } else { + carry = regs[r1] < regs[r2]; + regs[r0] = regs[r1] - regs[r2]; + } + break; + case INDEX_op_muls2: + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); +#if TCG_TARGET_REG_BITS == 32 + tmp64 = (int64_t)(int32_t)regs[r2] * (int32_t)regs[r3]; + tci_write_reg64(regs, r1, r0, tmp64); +#else + muls64(®s[r0], ®s[r1], regs[r2], regs[r3]); +#endif + break; + case INDEX_op_mulu2: + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); +#if TCG_TARGET_REG_BITS == 32 + tmp64 = (uint64_t)(uint32_t)regs[r2] * (uint32_t)regs[r3]; + tci_write_reg64(regs, r1, r0, tmp64); +#else + mulu64(®s[r0], ®s[r1], regs[r2], regs[r3]); #endif + break; /* Arithmetic operations (32 bit). */ - case INDEX_op_div_i32: + case INDEX_op_tci_divs32: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (int32_t)regs[r1] / (int32_t)regs[r2]; break; - case INDEX_op_divu_i32: + case INDEX_op_tci_divu32: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (uint32_t)regs[r1] / (uint32_t)regs[r2]; break; - case INDEX_op_rem_i32: + case INDEX_op_tci_rems32: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (int32_t)regs[r1] % (int32_t)regs[r2]; break; - case INDEX_op_remu_i32: + case INDEX_op_tci_remu32: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (uint32_t)regs[r1] % (uint32_t)regs[r2]; break; -#if TCG_TARGET_HAS_clz_i32 - case INDEX_op_clz_i32: + case INDEX_op_tci_clz32: tci_args_rrr(insn, &r0, &r1, &r2); tmp32 = regs[r1]; regs[r0] = tmp32 ? clz32(tmp32) : regs[r2]; break; -#endif -#if TCG_TARGET_HAS_ctz_i32 - case INDEX_op_ctz_i32: + case INDEX_op_tci_ctz32: tci_args_rrr(insn, &r0, &r1, &r2); tmp32 = regs[r1]; regs[r0] = tmp32 ? ctz32(tmp32) : regs[r2]; break; -#endif -#if TCG_TARGET_HAS_ctpop_i32 - case INDEX_op_ctpop_i32: - tci_args_rr(insn, &r0, &r1); - regs[r0] = ctpop32(regs[r1]); + case INDEX_op_tci_setcond32: + tci_args_rrrc(insn, &r0, &r1, &r2, &condition); + regs[r0] = tci_compare32(regs[r1], regs[r2], condition); + break; + case INDEX_op_tci_movcond32: + tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition); + tmp32 = tci_compare32(regs[r1], regs[r2], condition); + regs[r0] = regs[tmp32 ? r3 : r4]; break; -#endif - /* Shift/rotate operations (32 bit). */ + /* Shift/rotate operations. */ - case INDEX_op_shl_i32: + case INDEX_op_shl: tci_args_rrr(insn, &r0, &r1, &r2); - regs[r0] = (uint32_t)regs[r1] << (regs[r2] & 31); + regs[r0] = regs[r1] << (regs[r2] % TCG_TARGET_REG_BITS); break; - case INDEX_op_shr_i32: + case INDEX_op_shr: tci_args_rrr(insn, &r0, &r1, &r2); - regs[r0] = (uint32_t)regs[r1] >> (regs[r2] & 31); + regs[r0] = regs[r1] >> (regs[r2] % TCG_TARGET_REG_BITS); break; - case INDEX_op_sar_i32: + case INDEX_op_sar: tci_args_rrr(insn, &r0, &r1, &r2); - regs[r0] = (int32_t)regs[r1] >> (regs[r2] & 31); + regs[r0] = ((tcg_target_long)regs[r1] + >> (regs[r2] % TCG_TARGET_REG_BITS)); break; -#if TCG_TARGET_HAS_rot_i32 - case INDEX_op_rotl_i32: + case INDEX_op_tci_rotl32: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = rol32(regs[r1], regs[r2] & 31); break; - case INDEX_op_rotr_i32: + case INDEX_op_tci_rotr32: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ror32(regs[r1], regs[r2] & 31); break; -#endif -#if TCG_TARGET_HAS_deposit_i32 - case INDEX_op_deposit_i32: + case INDEX_op_deposit: tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); - regs[r0] = deposit32(regs[r1], pos, len, regs[r2]); + regs[r0] = deposit_tr(regs[r1], pos, len, regs[r2]); break; -#endif -#if TCG_TARGET_HAS_extract_i32 - case INDEX_op_extract_i32: + case INDEX_op_extract: tci_args_rrbb(insn, &r0, &r1, &pos, &len); - regs[r0] = extract32(regs[r1], pos, len); + regs[r0] = extract_tr(regs[r1], pos, len); break; -#endif -#if TCG_TARGET_HAS_sextract_i32 - case INDEX_op_sextract_i32: + case INDEX_op_sextract: tci_args_rrbb(insn, &r0, &r1, &pos, &len); - regs[r0] = sextract32(regs[r1], pos, len); + regs[r0] = sextract_tr(regs[r1], pos, len); break; -#endif - case INDEX_op_brcond_i32: + case INDEX_op_brcond: tci_args_rl(insn, tb_ptr, &r0, &ptr); - if ((uint32_t)regs[r0]) { + if (regs[r0]) { tb_ptr = ptr; } break; -#if TCG_TARGET_REG_BITS == 32 || TCG_TARGET_HAS_add2_i32 - case INDEX_op_add2_i32: - tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); - T1 = tci_uint64(regs[r3], regs[r2]); - T2 = tci_uint64(regs[r5], regs[r4]); - tci_write_reg64(regs, r1, r0, T1 + T2); - break; -#endif -#if TCG_TARGET_REG_BITS == 32 || TCG_TARGET_HAS_sub2_i32 - case INDEX_op_sub2_i32: - tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); - T1 = tci_uint64(regs[r3], regs[r2]); - T2 = tci_uint64(regs[r5], regs[r4]); - tci_write_reg64(regs, r1, r0, T1 - T2); - break; -#endif -#if TCG_TARGET_HAS_mulu2_i32 - case INDEX_op_mulu2_i32: - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - tmp64 = (uint64_t)(uint32_t)regs[r2] * (uint32_t)regs[r3]; - tci_write_reg64(regs, r1, r0, tmp64); - break; -#endif -#if TCG_TARGET_HAS_muls2_i32 - case INDEX_op_muls2_i32: - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - tmp64 = (int64_t)(int32_t)regs[r2] * (int32_t)regs[r3]; - tci_write_reg64(regs, r1, r0, tmp64); - break; -#endif -#if TCG_TARGET_HAS_ext8s_i32 || TCG_TARGET_HAS_ext8s_i64 - CASE_32_64(ext8s) - tci_args_rr(insn, &r0, &r1); - regs[r0] = (int8_t)regs[r1]; - break; -#endif -#if TCG_TARGET_HAS_ext16s_i32 || TCG_TARGET_HAS_ext16s_i64 || \ - TCG_TARGET_HAS_bswap16_i32 || TCG_TARGET_HAS_bswap16_i64 - CASE_32_64(ext16s) - tci_args_rr(insn, &r0, &r1); - regs[r0] = (int16_t)regs[r1]; - break; -#endif -#if TCG_TARGET_HAS_ext8u_i32 || TCG_TARGET_HAS_ext8u_i64 - CASE_32_64(ext8u) - tci_args_rr(insn, &r0, &r1); - regs[r0] = (uint8_t)regs[r1]; - break; -#endif -#if TCG_TARGET_HAS_ext16u_i32 || TCG_TARGET_HAS_ext16u_i64 - CASE_32_64(ext16u) - tci_args_rr(insn, &r0, &r1); - regs[r0] = (uint16_t)regs[r1]; - break; -#endif -#if TCG_TARGET_HAS_bswap16_i32 || TCG_TARGET_HAS_bswap16_i64 - CASE_32_64(bswap16) + case INDEX_op_bswap16: tci_args_rr(insn, &r0, &r1); regs[r0] = bswap16(regs[r1]); break; -#endif -#if TCG_TARGET_HAS_bswap32_i32 || TCG_TARGET_HAS_bswap32_i64 - CASE_32_64(bswap32) + case INDEX_op_bswap32: tci_args_rr(insn, &r0, &r1); regs[r0] = bswap32(regs[r1]); break; -#endif -#if TCG_TARGET_HAS_not_i32 || TCG_TARGET_HAS_not_i64 - CASE_32_64(not) - tci_args_rr(insn, &r0, &r1); - 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). */ - case INDEX_op_ld32s_i64: + case INDEX_op_ld32u: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); - regs[r0] = *(int32_t *)ptr; + regs[r0] = *(uint32_t *)ptr; break; - case INDEX_op_ld_i64: + case INDEX_op_ld32s: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); - regs[r0] = *(uint64_t *)ptr; + regs[r0] = *(int32_t *)ptr; break; - case INDEX_op_st_i64: + case INDEX_op_st32: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); - *(uint64_t *)ptr = regs[r0]; + *(uint32_t *)ptr = regs[r0]; break; /* Arithmetic operations (64 bit). */ - case INDEX_op_div_i64: + case INDEX_op_divs: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (int64_t)regs[r1] / (int64_t)regs[r2]; break; - case INDEX_op_divu_i64: + case INDEX_op_divu: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (uint64_t)regs[r1] / (uint64_t)regs[r2]; break; - case INDEX_op_rem_i64: + case INDEX_op_rems: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (int64_t)regs[r1] % (int64_t)regs[r2]; break; - case INDEX_op_remu_i64: + case INDEX_op_remu: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (uint64_t)regs[r1] % (uint64_t)regs[r2]; break; -#if TCG_TARGET_HAS_clz_i64 - case INDEX_op_clz_i64: + case INDEX_op_clz: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] ? clz64(regs[r1]) : regs[r2]; break; -#endif -#if TCG_TARGET_HAS_ctz_i64 - case INDEX_op_ctz_i64: + case INDEX_op_ctz: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] ? ctz64(regs[r1]) : regs[r2]; break; -#endif -#if TCG_TARGET_HAS_ctpop_i64 - case INDEX_op_ctpop_i64: - tci_args_rr(insn, &r0, &r1); - regs[r0] = ctpop64(regs[r1]); - break; -#endif -#if TCG_TARGET_HAS_mulu2_i64 - case INDEX_op_mulu2_i64: - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - mulu64(®s[r0], ®s[r1], regs[r2], regs[r3]); - break; -#endif -#if TCG_TARGET_HAS_muls2_i64 - case INDEX_op_muls2_i64: - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - muls64(®s[r0], ®s[r1], regs[r2], regs[r3]); - break; -#endif -#if TCG_TARGET_HAS_add2_i64 - case INDEX_op_add2_i64: - tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); - T1 = regs[r2] + regs[r4]; - T2 = regs[r3] + regs[r5] + (T1 < regs[r2]); - regs[r0] = T1; - regs[r1] = T2; - break; -#endif -#if TCG_TARGET_HAS_add2_i64 - case INDEX_op_sub2_i64: - tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); - T1 = regs[r2] - regs[r4]; - T2 = regs[r3] - regs[r5] - (regs[r2] < regs[r4]); - regs[r0] = T1; - regs[r1] = T2; - break; -#endif /* Shift/rotate operations (64 bit). */ - case INDEX_op_shl_i64: - tci_args_rrr(insn, &r0, &r1, &r2); - regs[r0] = regs[r1] << (regs[r2] & 63); - break; - case INDEX_op_shr_i64: - tci_args_rrr(insn, &r0, &r1, &r2); - regs[r0] = regs[r1] >> (regs[r2] & 63); - break; - case INDEX_op_sar_i64: - tci_args_rrr(insn, &r0, &r1, &r2); - regs[r0] = (int64_t)regs[r1] >> (regs[r2] & 63); - break; -#if TCG_TARGET_HAS_rot_i64 - case INDEX_op_rotl_i64: + case INDEX_op_rotl: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = rol64(regs[r1], regs[r2] & 63); break; - case INDEX_op_rotr_i64: + case INDEX_op_rotr: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ror64(regs[r1], regs[r2] & 63); break; -#endif -#if TCG_TARGET_HAS_deposit_i64 - case INDEX_op_deposit_i64: - tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); - regs[r0] = deposit64(regs[r1], pos, len, regs[r2]); - break; -#endif -#if TCG_TARGET_HAS_extract_i64 - case INDEX_op_extract_i64: - tci_args_rrbb(insn, &r0, &r1, &pos, &len); - regs[r0] = extract64(regs[r1], pos, len); - break; -#endif -#if TCG_TARGET_HAS_sextract_i64 - case INDEX_op_sextract_i64: - tci_args_rrbb(insn, &r0, &r1, &pos, &len); - regs[r0] = sextract64(regs[r1], pos, len); - break; -#endif - case INDEX_op_brcond_i64: - tci_args_rl(insn, tb_ptr, &r0, &ptr); - if (regs[r0]) { - tb_ptr = ptr; - } - break; - case INDEX_op_ext32s_i64: case INDEX_op_ext_i32_i64: tci_args_rr(insn, &r0, &r1); regs[r0] = (int32_t)regs[r1]; break; - case INDEX_op_ext32u_i64: case INDEX_op_extu_i32_i64: tci_args_rr(insn, &r0, &r1); regs[r0] = (uint32_t)regs[r1]; break; -#if TCG_TARGET_HAS_bswap64_i64 - case INDEX_op_bswap64_i64: + case INDEX_op_bswap64: tci_args_rr(insn, &r0, &r1); regs[r0] = bswap64(regs[r1]); break; -#endif #endif /* TCG_TARGET_REG_BITS == 64 */ /* QEMU specific operations. */ @@ -911,92 +789,33 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tb_ptr = ptr; break; - case INDEX_op_qemu_ld_a32_i32: + case INDEX_op_qemu_ld: 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: + taddr = regs[r1]; 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 { - 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); - } else { - regs[r0] = tmp64; - } - break; - - case INDEX_op_qemu_st_a32_i32: + case INDEX_op_qemu_st: 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]; - } else { - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - taddr = tci_uint64(regs[r2], regs[r1]); - oi = regs[r3]; - } - do_st_i32: + taddr = regs[r1]; 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: + case INDEX_op_qemu_ld2: + tcg_debug_assert(TCG_TARGET_REG_BITS == 32); + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); + taddr = regs[r2]; + oi = regs[r3]; + tmp64 = tci_qemu_ld(env, taddr, oi, tb_ptr); + tci_write_reg64(regs, r1, r0, tmp64); + break; + + case INDEX_op_qemu_st2: + tcg_debug_assert(TCG_TARGET_REG_BITS == 32); + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); + tmp64 = tci_uint64(regs[r1], regs[r0]); + taddr = regs[r2]; + oi = regs[r3]; tci_qemu_st(env, taddr, tmp64, oi, tb_ptr); break; @@ -1043,6 +862,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)); @@ -1058,7 +879,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) const char *op_name; uint32_t insn; TCGOpcode op; - TCGReg r0, r1, r2, r3, r4, r5; + TCGReg r0, r1, r2, r3, r4; tcg_target_ulong i1; int32_t s2; TCGCond c; @@ -1093,15 +914,14 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) info->fprintf_func(info->stream, "%-12s %d, %p", op_name, len, ptr); break; - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: + case INDEX_op_brcond: tci_args_rl(insn, tb_ptr, &r0, &ptr); info->fprintf_func(info->stream, "%-12s %s, 0, ne, %p", op_name, str_r(r0), ptr); break; - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: + case INDEX_op_setcond: + case INDEX_op_tci_setcond32: tci_args_rrrc(insn, &r0, &r1, &r2, &c); info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s", op_name, str_r(r0), str_r(r1), str_r(r2), str_c(c)); @@ -1119,126 +939,95 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) op_name, str_r(r0), ptr); break; - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i32: - case INDEX_op_ld_i64: - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i32: - case INDEX_op_st_i64: + case INDEX_op_tci_setcarry: + info->fprintf_func(info->stream, "%-12s", op_name); + break; + + case INDEX_op_ld8u: + case INDEX_op_ld8s: + case INDEX_op_ld16u: + case INDEX_op_ld16s: + case INDEX_op_ld32u: + case INDEX_op_ld: + case INDEX_op_st8: + case INDEX_op_st16: + case INDEX_op_st32: + case INDEX_op_st: tci_args_rrs(insn, &r0, &r1, &s2); info->fprintf_func(info->stream, "%-12s %s, %s, %d", op_name, str_r(r0), str_r(r1), s2); break; - case INDEX_op_mov_i32: - case INDEX_op_mov_i64: - case INDEX_op_ext8s_i32: - 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_ext32s_i64: - case INDEX_op_ext32u_i64: + case INDEX_op_bswap16: + case INDEX_op_bswap32: + case INDEX_op_ctpop: + case INDEX_op_mov: + case INDEX_op_neg: + case INDEX_op_not: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: - case INDEX_op_bswap64_i64: - case INDEX_op_not_i32: - case INDEX_op_not_i64: - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: - case INDEX_op_ctpop_i32: - case INDEX_op_ctpop_i64: + case INDEX_op_bswap64: tci_args_rr(insn, &r0, &r1); info->fprintf_func(info->stream, "%-12s %s, %s", op_name, str_r(r0), str_r(r1)); break; - case INDEX_op_add_i32: - case INDEX_op_add_i64: - case INDEX_op_sub_i32: - case INDEX_op_sub_i64: - case INDEX_op_mul_i32: - case INDEX_op_mul_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: - case INDEX_op_xor_i64: - 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: - case INDEX_op_nand_i32: - case INDEX_op_nand_i64: - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: - case INDEX_op_div_i32: - case INDEX_op_div_i64: - case INDEX_op_rem_i32: - case INDEX_op_rem_i64: - case INDEX_op_divu_i32: - case INDEX_op_divu_i64: - case INDEX_op_remu_i32: - case INDEX_op_remu_i64: - case INDEX_op_shl_i32: - case INDEX_op_shl_i64: - case INDEX_op_shr_i32: - case INDEX_op_shr_i64: - case INDEX_op_sar_i32: - case INDEX_op_sar_i64: - case INDEX_op_rotl_i32: - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i32: - case INDEX_op_rotr_i64: - case INDEX_op_clz_i32: - case INDEX_op_clz_i64: - case INDEX_op_ctz_i32: - case INDEX_op_ctz_i64: + case INDEX_op_add: + case INDEX_op_addci: + case INDEX_op_addcio: + case INDEX_op_addco: + case INDEX_op_and: + case INDEX_op_andc: + case INDEX_op_clz: + case INDEX_op_ctz: + case INDEX_op_divs: + case INDEX_op_divu: + case INDEX_op_eqv: + case INDEX_op_mul: + case INDEX_op_nand: + case INDEX_op_nor: + case INDEX_op_or: + case INDEX_op_orc: + case INDEX_op_rems: + case INDEX_op_remu: + case INDEX_op_rotl: + case INDEX_op_rotr: + case INDEX_op_sar: + case INDEX_op_shl: + case INDEX_op_shr: + case INDEX_op_sub: + case INDEX_op_subbi: + case INDEX_op_subbio: + case INDEX_op_subbo: + case INDEX_op_xor: + case INDEX_op_tci_ctz32: + case INDEX_op_tci_clz32: + case INDEX_op_tci_divs32: + case INDEX_op_tci_divu32: + case INDEX_op_tci_rems32: + case INDEX_op_tci_remu32: + case INDEX_op_tci_rotl32: + case INDEX_op_tci_rotr32: tci_args_rrr(insn, &r0, &r1, &r2); info->fprintf_func(info->stream, "%-12s %s, %s, %s", op_name, str_r(r0), str_r(r1), str_r(r2)); break; - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: + case INDEX_op_deposit: tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); info->fprintf_func(info->stream, "%-12s %s, %s, %s, %d, %d", op_name, str_r(r0), str_r(r1), str_r(r2), pos, len); break; - case INDEX_op_extract_i32: - case INDEX_op_extract_i64: - case INDEX_op_sextract_i32: - case INDEX_op_sextract_i64: + case INDEX_op_extract: + case INDEX_op_sextract: tci_args_rrbb(insn, &r0, &r1, &pos, &len); info->fprintf_func(info->stream, "%-12s %s,%s,%d,%d", op_name, str_r(r0), str_r(r1), pos, len); break; - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: + case INDEX_op_tci_movcond32: + case INDEX_op_movcond: case INDEX_op_setcond2_i32: tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &c); info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s, %s, %s", @@ -1246,62 +1035,27 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) str_r(r3), str_r(r4), str_c(c)); break; - case INDEX_op_mulu2_i32: - case INDEX_op_mulu2_i64: - case INDEX_op_muls2_i32: - case INDEX_op_muls2_i64: + case INDEX_op_muls2: + case INDEX_op_mulu2: 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 INDEX_op_add2_i32: - case INDEX_op_add2_i64: - case INDEX_op_sub2_i32: - case INDEX_op_sub2_i64: - tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); - info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s, %s, %s", - op_name, str_r(r0), str_r(r1), str_r(r2), - str_r(r3), str_r(r4), str_r(r5)); + case INDEX_op_qemu_ld: + case INDEX_op_qemu_st: + tci_args_rrm(insn, &r0, &r1, &oi); + info->fprintf_func(info->stream, "%-12s %s, %s, %x", + op_name, str_r(r0), str_r(r1), oi); break; - 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; - do_qemu_ldst: - switch (len) { - case 2: - tci_args_rrm(insn, &r0, &r1, &oi); - info->fprintf_func(info->stream, "%-12s %s, %s, %x", - op_name, str_r(r0), str_r(r1), oi); - break; - case 3: - 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); - info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s, %s", - op_name, str_r(r0), str_r(r1), - str_r(r2), str_r(r3), str_r(r4)); - break; - default: - g_assert_not_reached(); - } + case INDEX_op_qemu_ld2: + case INDEX_op_qemu_st2: + 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 0: diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h new file mode 100644 index 00000000000..ab07ce1fcbc --- /dev/null +++ b/tcg/tci/tcg-target-has.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific opcode support + * Copyright (c) 2009, 2011 Stefan Weil + */ + +#ifndef TCG_TARGET_HAS_H +#define TCG_TARGET_HAS_H + +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_extr_i64_i32 0 +#endif /* TCG_TARGET_REG_BITS == 64 */ + +#define TCG_TARGET_HAS_qemu_ldst_i128 0 + +#define TCG_TARGET_HAS_tst 1 + +#define TCG_TARGET_extract_valid(type, ofs, len) 1 +#define TCG_TARGET_sextract_valid(type, ofs, len) 1 +#define TCG_TARGET_deposit_valid(type, ofs, len) 1 + +#endif diff --git a/tcg/tci/tcg-target-mo.h b/tcg/tci/tcg-target-mo.h new file mode 100644 index 00000000000..779872e39a0 --- /dev/null +++ b/tcg/tci/tcg-target-mo.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Define target-specific memory model + * Copyright (c) 2009, 2011 Stefan Weil + */ + +#ifndef TCG_TARGET_MO_H +#define TCG_TARGET_MO_H + +/* + * We could notice __i386__ or __s390x__ and reduce the barriers depending + * on the host. But if you want performance, you use the normal backend. + * We prefer consistency across hosts on this. + */ +#define TCG_TARGET_DEFAULT_MO 0 + +#endif diff --git a/tcg/tci/tcg-target-opc.h.inc b/tcg/tci/tcg-target-opc.h.inc new file mode 100644 index 00000000000..4eb32ed7361 --- /dev/null +++ b/tcg/tci/tcg-target-opc.h.inc @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: MIT */ +/* These opcodes for use between the tci generator and interpreter. */ +DEF(tci_movi, 1, 0, 1, TCG_OPF_NOT_PRESENT) +DEF(tci_movl, 1, 0, 1, TCG_OPF_NOT_PRESENT) +DEF(tci_setcarry, 0, 0, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_clz32, 1, 2, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_ctz32, 1, 2, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_divs32, 1, 2, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_divu32, 1, 2, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_rems32, 1, 2, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_remu32, 1, 2, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_rotl32, 1, 2, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_rotr32, 1, 2, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_setcond32, 1, 2, 1, TCG_OPF_NOT_PRESENT) +DEF(tci_movcond32, 1, 2, 1, TCG_OPF_NOT_PRESENT) diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 253f27f1740..35c66a48369 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -22,160 +22,24 @@ * THE SOFTWARE. */ -#include "../tcg-pool.c.inc" - -static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) -{ - switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - - case INDEX_op_ld8u_i32: - case INDEX_op_ld8s_i32: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16s_i32: - case INDEX_op_ld_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: - case INDEX_op_not_i32: - case INDEX_op_not_i64: - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_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_bswap16_i32: - case INDEX_op_bswap16_i64: - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: - case INDEX_op_bswap64_i64: - case INDEX_op_extract_i32: - case INDEX_op_extract_i64: - case INDEX_op_sextract_i32: - case INDEX_op_sextract_i64: - case INDEX_op_ctpop_i32: - case INDEX_op_ctpop_i64: - return C_O1_I1(r, r); - - case INDEX_op_st8_i32: - case INDEX_op_st16_i32: - case INDEX_op_st_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - return C_O0_I2(r, r); - - case INDEX_op_div_i32: - case INDEX_op_div_i64: - case INDEX_op_divu_i32: - case INDEX_op_divu_i64: - case INDEX_op_rem_i32: - case INDEX_op_rem_i64: - case INDEX_op_remu_i32: - case INDEX_op_remu_i64: - case INDEX_op_add_i32: - case INDEX_op_add_i64: - case INDEX_op_sub_i32: - case INDEX_op_sub_i64: - case INDEX_op_mul_i32: - case INDEX_op_mul_i64: - case INDEX_op_and_i32: - case INDEX_op_and_i64: - case INDEX_op_andc_i32: - case INDEX_op_andc_i64: - case INDEX_op_eqv_i32: - case INDEX_op_eqv_i64: - case INDEX_op_nand_i32: - case INDEX_op_nand_i64: - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: - case INDEX_op_or_i32: - case INDEX_op_or_i64: - case INDEX_op_orc_i32: - case INDEX_op_orc_i64: - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: - case INDEX_op_shl_i32: - case INDEX_op_shl_i64: - case INDEX_op_shr_i32: - case INDEX_op_shr_i64: - case INDEX_op_sar_i32: - case INDEX_op_sar_i64: - case INDEX_op_rotl_i32: - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i32: - case INDEX_op_rotr_i64: - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: - case INDEX_op_clz_i32: - case INDEX_op_clz_i64: - case INDEX_op_ctz_i32: - case INDEX_op_ctz_i64: - return C_O1_I2(r, r, r); - - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - return C_O0_I2(r, r); - - 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, r, r, r, r); - +/* Used for function call generation. */ +#define TCG_TARGET_CALL_STACK_OFFSET 0 +#define TCG_TARGET_STACK_ALIGN 8 #if TCG_TARGET_REG_BITS == 32 - case INDEX_op_brcond2_i32: - return C_O0_I4(r, r, r, r); +# 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 - case INDEX_op_mulu2_i32: - case INDEX_op_mulu2_i64: - case INDEX_op_muls2_i32: - case INDEX_op_muls2_i64: - return C_O2_I2(r, r, r, r); - - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - case INDEX_op_setcond2_i32: - return C_O1_I4(r, 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(); - } +static TCGConstraintSetIndex +tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) +{ + return C_NotImplemented; } static const int tcg_target_reg_alloc_order[] = { @@ -409,20 +273,6 @@ static void tcg_out_op_rrrbb(TCGContext *s, TCGOpcode op, TCGReg r0, tcg_out32(s, insn); } -static void tcg_out_op_rrrrr(TCGContext *s, TCGOpcode op, TCGReg r0, - TCGReg r1, TCGReg r2, TCGReg r3, TCGReg r4) -{ - tcg_insn_unit insn = 0; - - 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, 4, r3); - insn = deposit32(insn, 24, 4, r4); - tcg_out32(s, insn); -} - static void tcg_out_op_rrrr(TCGContext *s, TCGOpcode op, TCGReg r0, TCGReg r1, TCGReg r2, TCGReg r3) { @@ -452,31 +302,13 @@ static void tcg_out_op_rrrrrc(TCGContext *s, TCGOpcode op, tcg_out32(s, insn); } -static void tcg_out_op_rrrrrr(TCGContext *s, TCGOpcode op, - TCGReg r0, TCGReg r1, TCGReg r2, - TCGReg r3, TCGReg r4, TCGReg r5) -{ - tcg_insn_unit insn = 0; - - 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, 4, r3); - insn = deposit32(insn, 24, 4, r4); - insn = deposit32(insn, 28, 4, r5); - tcg_out32(s, insn); -} - static void tcg_out_ldst(TCGContext *s, TCGOpcode op, TCGReg val, TCGReg base, intptr_t offset) { stack_bounds_check(base, offset); if (offset != sextract32(offset, 0, 16)) { tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP, offset); - tcg_out_op_rrr(s, (TCG_TARGET_REG_BITS == 32 - ? INDEX_op_add_i32 : INDEX_op_add_i64), - TCG_REG_TMP, TCG_REG_TMP, base); + tcg_out_op_rrr(s, INDEX_op_add, TCG_REG_TMP, TCG_REG_TMP, base); base = TCG_REG_TMP; offset = 0; } @@ -486,34 +318,17 @@ static void tcg_out_ldst(TCGContext *s, TCGOpcode op, TCGReg val, static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg val, TCGReg base, intptr_t offset) { - switch (type) { - case TCG_TYPE_I32: - tcg_out_ldst(s, INDEX_op_ld_i32, val, base, offset); - break; -#if TCG_TARGET_REG_BITS == 64 - case TCG_TYPE_I64: - tcg_out_ldst(s, INDEX_op_ld_i64, val, base, offset); - break; -#endif - default: - g_assert_not_reached(); + TCGOpcode op = INDEX_op_ld; + + if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { + op = INDEX_op_ld32u; } + tcg_out_ldst(s, op, val, base, offset); } static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { - switch (type) { - case TCG_TYPE_I32: - tcg_out_op_rr(s, INDEX_op_mov_i32, ret, arg); - break; -#if TCG_TARGET_REG_BITS == 64 - case TCG_TYPE_I64: - tcg_out_op_rr(s, INDEX_op_mov_i64, ret, arg); - break; -#endif - default: - g_assert_not_reached(); - } + tcg_out_op_rr(s, INDEX_op_mov, ret, arg); return true; } @@ -544,76 +359,62 @@ static void tcg_out_movi(TCGContext *s, TCGType type, } } +static void tcg_out_extract(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rs, unsigned pos, unsigned len) +{ + tcg_out_op_rrbb(s, INDEX_op_extract, rd, rs, pos, len); +} + +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tcg_out_extract, +}; + +static void tcg_out_sextract(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rs, unsigned pos, unsigned len) +{ + tcg_out_op_rrbb(s, INDEX_op_sextract, rd, rs, pos, len); +} + +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tcg_out_sextract, +}; + +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_NotImplemented, +}; + 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(); - } + tcg_out_sextract(s, type, rd, rs, 0, 8); } 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); - } + tcg_out_extract(s, TCG_TYPE_REG, rd, rs, 0, 8); } 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(); - } + tcg_out_sextract(s, type, rd, rs, 0, 16); } 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); - } + tcg_out_extract(s, TCG_TYPE_REG, rd, rs, 0, 16); } 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); + tcg_out_sextract(s, TCG_TYPE_I64, rd, rs, 0, 32); } 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); + tcg_out_extract(s, TCG_TYPE_I64, rd, rs, 0, 32); } static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs) @@ -665,18 +466,6 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *func, tcg_out32(s, insn); } -#if TCG_TARGET_REG_BITS == 64 -# define CASE_32_64(x) \ - case glue(glue(INDEX_op_, x), _i64): \ - case glue(glue(INDEX_op_, x), _i32): -# define CASE_64(x) \ - case glue(glue(INDEX_op_, x), _i64): -#else -# define CASE_32_64(x) \ - case glue(glue(INDEX_op_, x), _i32): -# 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); @@ -689,221 +478,772 @@ static void tcg_out_goto_tb(TCGContext *s, int which) set_jmp_reset_offset(s, which); } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_op_r(s, INDEX_op_goto_ptr, a0); +} + 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]) +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) { - TCGOpcode exts; + tcg_out_op_rrr(s, INDEX_op_add, a0, a1, a2); +} - switch (opc) { - case INDEX_op_goto_ptr: - tcg_out_op_r(s, opc, args[0]); - break; +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_add, +}; - case INDEX_op_br: - tcg_out_op_l(s, opc, arg_label(args[0])); - break; +static TCGConstraintSetIndex cset_addsubcarry(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_REG ? C_O1_I2(r, r, r) : C_NotImplemented; +} - CASE_32_64(setcond) - tcg_out_op_rrrc(s, opc, args[0], args[1], args[2], args[3]); - break; +static void tgen_addco(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_addco, a0, a1, a2); +} - CASE_32_64(movcond) - case INDEX_op_setcond2_i32: - tcg_out_op_rrrrrc(s, opc, args[0], args[1], args[2], - args[3], args[4], args[5]); - break; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addsubcarry, + .out_rrr = tgen_addco, +}; - CASE_32_64(ld8u) - CASE_32_64(ld8s) - CASE_32_64(ld16u) - CASE_32_64(ld16s) - case INDEX_op_ld_i32: - CASE_64(ld32u) - CASE_64(ld32s) - CASE_64(ld) - CASE_32_64(st8) - CASE_32_64(st16) - case INDEX_op_st_i32: - CASE_64(st32) - CASE_64(st) - tcg_out_ldst(s, opc, args[0], args[1], args[2]); - break; +static void tgen_addci(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_addci, a0, a1, a2); +} - CASE_32_64(add) - CASE_32_64(sub) - CASE_32_64(mul) - CASE_32_64(and) - CASE_32_64(or) - CASE_32_64(xor) - CASE_32_64(andc) /* Optional (TCG_TARGET_HAS_andc_*). */ - CASE_32_64(orc) /* Optional (TCG_TARGET_HAS_orc_*). */ - CASE_32_64(eqv) /* Optional (TCG_TARGET_HAS_eqv_*). */ - CASE_32_64(nand) /* Optional (TCG_TARGET_HAS_nand_*). */ - CASE_32_64(nor) /* Optional (TCG_TARGET_HAS_nor_*). */ - CASE_32_64(shl) - CASE_32_64(shr) - CASE_32_64(sar) - CASE_32_64(rotl) /* Optional (TCG_TARGET_HAS_rot_*). */ - CASE_32_64(rotr) /* Optional (TCG_TARGET_HAS_rot_*). */ - CASE_32_64(div) /* Optional (TCG_TARGET_HAS_div_*). */ - CASE_32_64(divu) /* Optional (TCG_TARGET_HAS_div_*). */ - CASE_32_64(rem) /* Optional (TCG_TARGET_HAS_div_*). */ - CASE_32_64(remu) /* Optional (TCG_TARGET_HAS_div_*). */ - CASE_32_64(clz) /* Optional (TCG_TARGET_HAS_clz_*). */ - CASE_32_64(ctz) /* Optional (TCG_TARGET_HAS_ctz_*). */ - tcg_out_op_rrr(s, opc, args[0], args[1], args[2]); - break; +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addsubcarry, + .out_rrr = tgen_addci, +}; - CASE_32_64(deposit) /* Optional (TCG_TARGET_HAS_deposit_*). */ - { - TCGArg pos = args[3], len = args[4]; - TCGArg max = opc == INDEX_op_deposit_i32 ? 32 : 64; +static void tgen_addcio(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_addcio, a0, a1, a2); +} - tcg_debug_assert(pos < max); - tcg_debug_assert(pos + len <= max); +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addsubcarry, + .out_rrr = tgen_addcio, +}; - tcg_out_op_rrrbb(s, opc, args[0], args[1], args[2], pos, len); - } - break; +static void tcg_out_set_carry(TCGContext *s) +{ + tcg_out_op_v(s, INDEX_op_tci_setcarry); +} - CASE_32_64(extract) /* Optional (TCG_TARGET_HAS_extract_*). */ - CASE_32_64(sextract) /* Optional (TCG_TARGET_HAS_sextract_*). */ - { - TCGArg pos = args[2], len = args[3]; - TCGArg max = tcg_op_defs[opc].flags & TCG_OPF_64BIT ? 64 : 32; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_and, a0, a1, a2); +} - tcg_debug_assert(pos < max); - tcg_debug_assert(pos + len <= max); +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_and, +}; - tcg_out_op_rrbb(s, opc, args[0], args[1], pos, len); - } - break; +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_andc, a0, a1, a2); +} - CASE_32_64(brcond) - tcg_out_op_rrrc(s, (opc == INDEX_op_brcond_i32 - ? INDEX_op_setcond_i32 : INDEX_op_setcond_i64), - TCG_REG_TMP, args[0], args[1], args[2]); - tcg_out_op_rl(s, opc, TCG_REG_TMP, arg_label(args[3])); - break; +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_andc, +}; - CASE_32_64(neg) /* Optional (TCG_TARGET_HAS_neg_*). */ - CASE_32_64(not) /* Optional (TCG_TARGET_HAS_not_*). */ - 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). */ - tcg_out_op_rr(s, opc, args[0], args[1]); - break; +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_clz32 + : INDEX_op_clz); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} - case INDEX_op_bswap16_i32: /* Optional (TCG_TARGET_HAS_bswap16_i32). */ - exts = INDEX_op_ext16s_i32; - goto do_bswap; - case INDEX_op_bswap16_i64: /* Optional (TCG_TARGET_HAS_bswap16_i64). */ - exts = INDEX_op_ext16s_i64; - goto do_bswap; - case INDEX_op_bswap32_i64: /* Optional (TCG_TARGET_HAS_bswap32_i64). */ - exts = INDEX_op_ext32s_i64; - do_bswap: - /* The base tci bswaps zero-extend, and ignore high bits. */ - tcg_out_op_rr(s, opc, args[0], args[1]); - if (args[2] & TCG_BSWAP_OS) { - tcg_out_op_rr(s, exts, args[0], args[0]); - } - break; +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_clz, +}; - CASE_32_64(add2) - CASE_32_64(sub2) - tcg_out_op_rrrrrr(s, opc, args[0], args[1], args[2], - args[3], args[4], args[5]); - break; +static void tgen_ctz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_ctz32 + : INDEX_op_ctz); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} -#if TCG_TARGET_REG_BITS == 32 - case INDEX_op_brcond2_i32: - tcg_out_op_rrrrrc(s, INDEX_op_setcond2_i32, TCG_REG_TMP, - args[0], args[1], args[2], args[3], args[4]); - tcg_out_op_rl(s, INDEX_op_brcond_i32, TCG_REG_TMP, arg_label(args[5])); - break; +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_ctz, +}; + +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) +{ + tcg_out_op_rrrbb(s, INDEX_op_deposit, a0, a1, a2, ofs, len); +} + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_deposit, +}; + +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_divs32 + : INDEX_op_divs); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} + +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divs, +}; + +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_divu32 + : INDEX_op_divu); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divu, +}; + +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_eqv(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_eqv, a0, a1, a2); +} + +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_eqv, +}; + +#if TCG_TARGET_REG_BITS == 64 +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_extract(s, TCG_TYPE_I64, a0, a1, 32, 32); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; #endif - CASE_32_64(mulu2) - CASE_32_64(muls2) - tcg_out_op_rrrr(s, opc, args[0], args[1], args[2], args[3]); - break; +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_mul, a0, a1, a2); +} - 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_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 { - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, args[4]); - tcg_out_op_rrrrr(s, opc, args[0], args[1], - args[2], args[3], TCG_REG_TMP); - } - break; +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mul, +}; - case INDEX_op_mb: - tcg_out_op_v(s, opc); - break; +static TCGConstraintSetIndex cset_mul2(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_REG ? C_O2_I2(r, r, r, r) : C_NotImplemented; +} - 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 tgen_muls2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + tcg_out_op_rrrr(s, INDEX_op_muls2, a0, a1, a2, a3); +} + +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mul2, + .out_rrrr = tgen_muls2, +}; + +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_mulu2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + tcg_out_op_rrrr(s, INDEX_op_mulu2, a0, a1, a2, a3); +} + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mul2, + .out_rrrr = tgen_mulu2, +}; + +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_nand(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_nand, a0, a1, a2); +} + +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_nand, +}; + +static void tgen_nor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_nor, a0, a1, a2); +} + +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_nor, +}; + +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_or, a0, a1, a2); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_or, +}; + +static void tgen_orc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_orc, a0, a1, a2); +} + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_orc, +}; + +static void tgen_rems(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_rems32 + : INDEX_op_rems); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_rems, +}; + +static void tgen_remu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_remu32 + : INDEX_op_remu); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} + +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_remu, +}; + +static void tgen_rotl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_rotl32 + : INDEX_op_rotl); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} + +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_rotl, +}; + +static void tgen_rotr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_rotr32 + : INDEX_op_rotr); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_rotr, +}; + +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type < TCG_TYPE_REG) { + tcg_out_ext32s(s, TCG_REG_TMP, a1); + a1 = TCG_REG_TMP; } + tcg_out_op_rrr(s, INDEX_op_sar, a0, a1, a2); } -static void tcg_out_st(TCGContext *s, TCGType type, TCGReg val, TCGReg base, - intptr_t offset) +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sar, +}; + +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) { - switch (type) { - case TCG_TYPE_I32: - tcg_out_ldst(s, INDEX_op_st_i32, val, base, offset); - break; + tcg_out_op_rrr(s, INDEX_op_shl, a0, a1, a2); +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_shl, +}; + +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type < TCG_TYPE_REG) { + tcg_out_ext32u(s, TCG_REG_TMP, a1); + a1 = TCG_REG_TMP; + } + tcg_out_op_rrr(s, INDEX_op_shr, a0, a1, a2); +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_shr, +}; + +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_sub, a0, a1, a2); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sub, +}; + +static void tgen_subbo(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_subbo, a0, a1, a2); +} + +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addsubcarry, + .out_rrr = tgen_subbo, +}; + +static void tgen_subbi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_subbi, a0, a1, a2); +} + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addsubcarry, + .out_rrr = tgen_subbi, +}; + +static void tgen_subbio(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_subbio, a0, a1, a2); +} + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addsubcarry, + .out_rrr = tgen_subbio, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + tcg_out_op_v(s, INDEX_op_tci_setcarry); /* borrow == carry */ +} + +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_xor, a0, a1, a2); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_xor, +}; + +static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_op_rr(s, INDEX_op_ctpop, a0, a1); +} + +static TCGConstraintSetIndex cset_ctpop(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_REG ? C_O1_I1(r, r) : C_NotImplemented; +} + +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctpop, + .out_rr = tgen_ctpop, +}; + +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_op_rr(s, INDEX_op_bswap16, a0, a1); + if (flags & TCG_BSWAP_OS) { + tcg_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 16); + } +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap16, +}; + +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_op_rr(s, INDEX_op_bswap32, a0, a1); + if (flags & TCG_BSWAP_OS) { + tcg_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 32); + } +} + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap32, +}; + #if TCG_TARGET_REG_BITS == 64 - case TCG_TYPE_I64: - tcg_out_ldst(s, INDEX_op_st_i64, val, base, offset); - break; +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_op_rr(s, INDEX_op_bswap64, a0, a1); +} + +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap64, +}; #endif - default: - g_assert_not_reached(); + +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_op_rr(s, INDEX_op_neg, a0, a1); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_op_rr(s, INDEX_op_not, a0, a1); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_setcond32 + : INDEX_op_setcond); + tcg_out_op_rrrc(s, opc, dest, arg1, arg2, cond); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_setcond, +}; + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tgen_setcond(s, type, cond, dest, arg1, arg2); + tgen_neg(s, type, dest, dest); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_negsetcond, +}; + +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg0, TCGReg arg1, TCGLabel *l) +{ + tgen_setcond(s, type, cond, TCG_REG_TMP, arg0, arg1); + tcg_out_op_rl(s, INDEX_op_brcond, TCG_REG_TMP, l); +} + +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, r), + .out_rr = tgen_brcond, +}; + +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg vt, bool const_vt, TCGArg vf, bool consf_vf) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_movcond32 + : INDEX_op_movcond); + tcg_out_op_rrrrrc(s, opc, ret, c1, c2, vt, vf, cond); +} + +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, r, r, r), + .out = tgen_movcond, +}; + +static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh, TCGLabel *l) +{ + tcg_out_op_rrrrrc(s, INDEX_op_setcond2_i32, TCG_REG_TMP, + al, ah, bl, bh, cond); + tcg_out_op_rl(s, INDEX_op_brcond, TCG_REG_TMP, l); +} + +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) +#endif +static const TCGOutOpBrcond2 outop_brcond2 = { + .base.static_constraint = C_O0_I4(r, r, r, r), + .out = tgen_brcond2, +}; + +static void tgen_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh) +{ + tcg_out_op_rrrrrc(s, INDEX_op_setcond2_i32, ret, al, ah, bl, bh, cond); +} + +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) +#endif +static const TCGOutOpSetcond2 outop_setcond2 = { + .base.static_constraint = C_O1_I4(r, r, r, r, r), + .out = tgen_setcond2, +}; + +static void tcg_out_mb(TCGContext *s, unsigned a0) +{ + tcg_out_op_v(s, INDEX_op_mb); +} + +static void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + tcg_out_op_l(s, INDEX_op_br, l); +} + +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_ld8u, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_ld8s, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_ld16u, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_ld16s, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +#if TCG_TARGET_REG_BITS == 64 +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_ld32u, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_ld32s, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; +#endif + +static void tgen_st8(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_st8, data, base, offset); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st8, +}; + +static void tgen_st16(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_st16, data, base, offset); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st16, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tcg_out_st, +}; + +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) +{ + tcg_out_op_rrm(s, INDEX_op_qemu_ld, data, addr, oi); +} + +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) +{ + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, oi); + tcg_out_op_rrrr(s, INDEX_op_qemu_ld2, datalo, datahi, addr, TCG_REG_TMP); +} + +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = + TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O2_I1(r, r, r), + .out = + TCG_TARGET_REG_BITS == 64 ? NULL : tgen_qemu_ld2, +}; + +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) +{ + tcg_out_op_rrm(s, INDEX_op_qemu_st, data, addr, oi); +} + +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(r, r), + .out = tgen_qemu_st, +}; + +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) +{ + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, oi); + tcg_out_op_rrrr(s, INDEX_op_qemu_st2, datalo, datahi, addr, TCG_REG_TMP); +} + +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = + TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O0_I3(r, r, r), + .out = + TCG_TARGET_REG_BITS == 64 ? NULL : tgen_qemu_st2, +}; + +static void tcg_out_st(TCGContext *s, TCGType type, TCGReg val, TCGReg base, + intptr_t offset) +{ + TCGOpcode op = INDEX_op_st; + + if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { + op = INDEX_op_st32; } + tcg_out_ldst(s, op, val, base, offset); } static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, @@ -913,7 +1253,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; } @@ -955,7 +1296,22 @@ 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; } + +static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + g_assert_not_reached(); +} + +static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + g_assert_not_reached(); +} diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h index 91ca33b6166..bd03aa1bc4a 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -44,83 +44,6 @@ #define TCG_TARGET_INSN_UNIT_SIZE 4 #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) -/* Optional instructions. */ - -#define TCG_TARGET_HAS_bswap16_i32 1 -#define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_div_i32 1 -#define TCG_TARGET_HAS_rem_i32 1 -#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_andc_i32 1 -#define TCG_TARGET_HAS_deposit_i32 1 -#define TCG_TARGET_HAS_extract_i32 1 -#define TCG_TARGET_HAS_sextract_i32 1 -#define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_eqv_i32 1 -#define TCG_TARGET_HAS_nand_i32 1 -#define TCG_TARGET_HAS_nor_i32 1 -#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_qemu_st8_i32 0 - -#if TCG_TARGET_REG_BITS == 64 -#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 -#define TCG_TARGET_HAS_deposit_i64 1 -#define TCG_TARGET_HAS_extract_i64 1 -#define TCG_TARGET_HAS_sextract_i64 1 -#define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_div_i64 1 -#define TCG_TARGET_HAS_rem_i64 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_andc_i64 1 -#define TCG_TARGET_HAS_eqv_i64 1 -#define TCG_TARGET_HAS_nand_i64 1 -#define TCG_TARGET_HAS_nor_i64 1 -#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 -#define TCG_TARGET_HAS_mulu2_i32 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_muluh_i64 0 -#define TCG_TARGET_HAS_mulsh_i64 0 -#else -#define TCG_TARGET_HAS_mulu2_i32 1 -#endif /* TCG_TARGET_REG_BITS == 64 */ - -#define TCG_TARGET_HAS_qemu_ldst_i128 0 - /* Number of registers available. */ #define TCG_TARGET_NB_REGS 16 @@ -148,26 +71,6 @@ typedef enum { TCG_REG_CALL_STACK = TCG_REG_R15, } TCGReg; -/* 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 - -/* We could notice __i386__ or __s390x__ and reduce the barriers depending - on the host. But if you want performance, you use the normal backend. - We prefer consistency across hosts on this. */ -#define TCG_TARGET_DEFAULT_MO (0) #endif /* TCG_TARGET_H */ diff --git a/tests/Makefile.include b/tests/Makefile.include index 38987426594..23fb722d426 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -3,28 +3,28 @@ .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 - @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 @echo @echo "The variable SPEED can be set to control the gtester speed setting." @@ -73,7 +73,7 @@ $(TCG_TESTS_TARGETS:%=distclean-tcg-tests-%): distclean-tcg-tests-%: build-tcg: $(BUILD_TCG_TARGET_RULES) .PHONY: check-tcg -.ninja-goals.check-tcg = all +.ninja-goals.check-tcg = all test-plugins check-tcg: $(RUN_TCG_TARGET_RULES) .PHONY: clean-tcg @@ -84,26 +84,12 @@ distclean-tcg: $(DISTCLEAN_TCG_TARGET_RULES) # Python venv for running tests -.PHONY: check-venv check-avocado check-acceptance check-acceptance-deprecated-warning +.PHONY: check-venv # 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_TOKEN=$(BUILD_DIR)/pyvenv/tests.group -TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results -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 -ifndef AVOCADO_TAGS - AVOCADO_CMDLINE_TAGS=$(patsubst %-softmmu,-t arch:%, \ - $(filter %-softmmu,$(TARGETS))) -else - AVOCADO_CMDLINE_TAGS=$(addprefix -t , $(AVOCADO_TAGS)) -endif quiet-venv-pip = $(quiet-@)$(call quiet-command-run, \ $(PYTHON) -m pip -q --disable-pip-version-check $1, \ @@ -111,56 +97,30 @@ quiet-venv-pip = $(quiet-@)$(call quiet-command-run, \ $(TESTS_VENV_TOKEN): $(SRC_PATH)/pythondeps.toml $(call quiet-venv-pip,install -e "$(SRC_PATH)/python/") - $(PYTHON) python/scripts/mkvenv.py ensuregroup --online $< avocado + $(MKVENV_ENSUREGROUP) $< testdeps $(call quiet-command, touch $@) -$(TESTS_RESULTS_DIR): - $(call quiet-command, mkdir -p $@, \ - MKDIR, $@) - 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)) -FEDORA_31_ARCHES := x86_64 aarch64 ppc64le s390x -FEDORA_31_DOWNLOAD=$(filter $(FEDORA_31_ARCHES),$(FEDORA_31_ARCHES_CANDIDATES)) - -# download one specific Fedora 31 image -get-vm-image-fedora-31-%: check-venv - $(call quiet-command, \ - $(PYTHON) -m avocado vmimage get \ - --distro=fedora --distro-version=31 --arch=$*, \ - "AVOCADO", "Downloading avocado tests VM image for $*") - -# download all vm images, according to defined targets -get-vm-images: check-venv $(patsubst %,get-vm-image-fedora-31-%, $(FEDORA_31_DOWNLOAD)) - -check-avocado: check-venv $(TESTS_RESULTS_DIR) get-vm-images - $(call quiet-command, \ - $(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) \ - $(if $(GITLAB_CI),,--failfast) $(AVOCADO_TESTS), \ - "AVOCADO", "tests/avocado") - -check-acceptance-deprecated-warning: - @echo - @echo "Note '$(MAKE) check-acceptance' is deprecated, use '$(MAKE) check-avocado' instead." - @echo +FUNCTIONAL_TARGETS=$(patsubst %-softmmu,check-functional-%, $(filter %-softmmu,$(TARGETS))) +.PHONY: $(FUNCTIONAL_TARGETS) +$(FUNCTIONAL_TARGETS): + @$(MAKE) SPEED=thorough $(subst -functional,-func,$@) -check-acceptance: check-acceptance-deprecated-warning | check-avocado +.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 +.PHONY: check check-clean check: check-build: run-ninja check-clean: - rm -rf $(TESTS_RESULTS_DIR) + rm -rf $(BUILD_DIR)/tests/functional clean: check-clean clean-tcg distclean: distclean-tcg diff --git a/tests/avocado/README.rst b/tests/avocado/README.rst deleted file mode 100644 index 94488371bbe..00000000000 --- a/tests/avocado/README.rst +++ /dev/null @@ -1,10 +0,0 @@ -============================================= -Integration tests using the Avocado Framework -============================================= - -This directory contains integration tests. They're usually higher -level, and may interact with external resources and with various -guest operating systems. - -For more information, please refer to ``docs/devel/testing.rst``, -section "Integration tests using the Avocado Framework". diff --git a/tests/avocado/acpi-bits.py b/tests/avocado/acpi-bits.py deleted file mode 100644 index bb3f8186899..00000000000 --- a/tests/avocado/acpi-bits.py +++ /dev/null @@ -1,398 +0,0 @@ -#!/usr/bin/env python3 -# group: rw quick -# Exercise QEMU generated ACPI/SMBIOS tables using biosbits, -# https://biosbits.org/ -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# -# Author: -# Ani Sinha - -# pylint: disable=invalid-name -# pylint: disable=consider-using-f-string - -""" -This is QEMU ACPI/SMBIOS avocado tests using biosbits. -Biosbits is available originally at https://biosbits.org/. -This test uses a fork of the upstream bits and has numerous fixes -including an upgraded acpica. The fork is located here: -https://gitlab.com/qemu-project/biosbits-bits . -""" - -import logging -import os -import platform -import re -import shutil -import subprocess -import tarfile -import tempfile -import time -import zipfile -from typing import ( - List, - Optional, - Sequence, -) -from qemu.machine import QEMUMachine -from avocado import skipIf -from avocado_qemu import QemuBaseTest - -deps = ["xorriso", "mformat"] # 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(), - 'unsupported platform or dependencies (%s) not installed' \ - % ','.join(deps)) -class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes - """ - ACPI and SMBIOS tests using biosbits. - - :avocado: tags=arch:x86_64 - :avocado: tags=acpi - - """ - # in slower systems the test can take as long as 3 minutes to complete. - timeout = 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') and self._workDir: - self.logger.info('removing the work directory %s', self._workDir) - shutil.rmtree(self._workDir) - else: - self.logger.info('not removing the work directory %s ' \ - 'as BITS_DEBUG is ' \ - 'passed in the environment', self._workDir) - super().tearDown() - - def test_acpi_smbios_bits(self): - """The main test case implementation.""" - - iso_file = os.path.join(self._workDir, - 'bits-%d.iso' %self._bitsInternalVer) - - self.assertTrue(os.access(iso_file, os.R_OK)) - - self._vm = QEMUBitsMachine(binary=self.qemu_bin, - base_temp_dir=self._workDir, - debugcon_log=self._debugcon_log, - debugcon_addr=self._debugcon_addr) - - self._vm.add_args('-cdrom', '%s' %iso_file) - # the vm needs to be run under icount so that TCG emulation is - # consistent in terms of timing. smilatency tests have consistent - # timing requirements. - self._vm.add_args('-icount', 'auto') - - 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 deleted file mode 100644 index 33090903f1e..00000000000 --- a/tests/avocado/avocado_qemu/__init__.py +++ /dev/null @@ -1,681 +0,0 @@ -# Test class and utilities for functional 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 logging -import os -import shutil -import subprocess -import sys -import tempfile -import time -import uuid - -import avocado -from avocado.utils import cloudinit, datadrainer, process, ssh, vmimage -from avocado.utils.path import find_command - -from qemu.machine import QEMUMachine -from qemu.utils import (get_info_usernet_hostfwd_port, kvm_available, - tcg_available) - - -#: The QEMU build root directory. It may also be the source directory -#: if building from the source dir, but it's safer to use BUILD_DIR for -#: that purpose. Be aware that if this code is moved outside of a source -#: and build tree, it will not be accurate. -BUILD_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) - -if os.path.islink(os.path.dirname(os.path.dirname(__file__))): - # The link to the 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): - """ - This function is for use in a @avocado.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 @avocado.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) - - -def pick_default_qemu_bin(bin_prefix='qemu-system-', arch=None): - """ - Picks the path of a QEMU binary, starting either in the current working - directory or in the source tree root directory. - - :param arch: the arch to use when looking for a QEMU binary (the target - will match the arch given). If None (the default), arch - will be the current host system arch (as given by - :func:`os.uname`). - :type arch: str - :returns: the path to the default QEMU binary or None if one could not - be found - :rtype: str or None - """ - if arch is None: - arch = os.uname()[4] - # qemu binary path does not match arch for powerpc, handle it - if 'ppc64le' in arch: - arch = 'ppc64' - qemu_bin_name = bin_prefix + arch - qemu_bin_paths = [ - os.path.join(".", qemu_bin_name), - os.path.join(BUILD_DIR, qemu_bin_name), - os.path.join(BUILD_DIR, "build", qemu_bin_name), - ] - for path in qemu_bin_paths: - if is_readable_executable_file(path): - return path - return None - - -def _console_interaction(test, success_message, failure_message, - send_string, keep_sending=False, vm=None): - 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_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 - try: - msg = console.readline().decode().strip() - except UnicodeDecodeError: - msg = None - if not msg: - continue - console_logger.debug(msg) - if success_message is None or success_message in msg: - break - if failure_message and failure_message in msg: - console.close() - fail = 'Failure message found in console: "%s". Expected: "%s"' % \ - (failure_message, success_message) - test.fail(fail) - -def interrupt_interactive_console_until_pattern(test, success_message, - failure_message=None, - interrupt_string='\r'): - """ - Keep sending a string to interrupt a console prompt, while logging the - console output. Typical use case is to break a boot loader prompt, such: - - Press a key within 5 seconds to interrupt boot process. - 5 - 4 - 3 - 2 - 1 - Booting default image... - - :param test: an Avocado test containing a VM that will have its console - read and probed for a success or failure message - :type test: :class:`avocado_qemu.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 - """ - _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: an Avocado test containing a VM that will have its console - read and probed for a success or failure message - :type test: :class:`avocado_qemu.QemuSystemTest` - :param success_message: if this message appears, test succeeds - :param failure_message: if this message appears, test fails - """ - _console_interaction(test, success_message, failure_message, None, vm=vm) - -def exec_command(test, command): - """ - Send a command to a console (appending CRLF characters), while logging - the content. - - :param test: an Avocado test containing a VM. - :type test: :class:`avocado_qemu.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: an Avocado test containing a VM that will have its console - read and probed for a success or failure message - :type test: :class:`avocado_qemu.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 - """ - _console_interaction(test, success_message, failure_message, command + '\r') - -class QemuBaseTest(avocado.Test): - - # default timeout for all tests, can be overridden - timeout = 120 - - def _get_unique_tag_val(self, tag_name): - """ - Gets a tag value, if unique for a key - """ - vals = self.tags.get(tag_name, []) - if len(vals) == 1: - return vals.pop() - return None - - def setUp(self, bin_prefix): - self.arch = self.params.get('arch', - default=self._get_unique_tag_val('arch')) - - self.cpu = self.params.get('cpu', - default=self._get_unique_tag_val('cpu')) - - default_qemu_bin = pick_default_qemu_bin(bin_prefix, arch=self.arch) - self.qemu_bin = self.params.get('qemu_bin', - default=default_qemu_bin) - if self.qemu_bin is None: - self.cancel("No QEMU binary defined or found in the build tree") - - def fetch_asset(self, name, - asset_hash=None, algorithm=None, - locations=None, expire=None, - find_only=False, cancel_on_missing=True): - return super().fetch_asset(name, - asset_hash=asset_hash, - algorithm=algorithm, - locations=locations, - expire=expire, - find_only=find_only, - cancel_on_missing=cancel_on_missing) - - -class QemuSystemTest(QemuBaseTest): - """Facilitates system emulation tests.""" - - def setUp(self): - self._vms = {} - - 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')) - - 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.cancel("Don't know how to check for the presence " - "of accelerator %s" % accelerator) - if not checker(qemu_bin=self.qemu_bin): - self.cancel("%s accelerator does not seem to be " - "available" % accelerator) - - def require_netdev(self, netdevname): - netdevhelp = run_cmd([self.qemu_bin, - '-M', 'none', '-netdev', 'help'])[0]; - if netdevhelp.find('\n' + netdevname + '\n') < 0: - self.cancel('no support for user networking') - - def require_multiprocess(self): - """ - Test for the presence of the x-pci-proxy-dev which is required - to support multiprocess. - """ - devhelp = run_cmd([self.qemu_bin, - '-M', 'none', '-device', 'help'])[0]; - if devhelp.find('x-pci-proxy-dev') < 0: - self.cancel('no support for multiprocess device emulation') - - def _new_vm(self, name, *args): - self._sd = tempfile.TemporaryDirectory(prefix="qemu_") - vm = QEMUMachine(self.qemu_bin, base_temp_dir=self.workdir, - sock_dir=self._sd.name, log_dir=self.logdir) - self.log.debug('QEMUMachine "%s" created', name) - self.log.debug('QEMUMachine "%s" temp_dir: %s', name, vm.temp_dir) - self.log.debug('QEMUMachine "%s" log_dir: %s', name, vm.log_dir) - if args: - 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') - - 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() - self._sd = None - 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') - port = get_info_usernet_hostfwd_port(res) - self.assertIsNotNone(port) - self.assertGreater(port, 0) - self.log.debug('sshd listening on port: %d', port) - if credential_is_key: - self.ssh_session = ssh.Session('127.0.0.1', port=port, - user=username, key=credential) - else: - self.ssh_session = ssh.Session('127.0.0.1', port=port, - user=username, password=credential) - for i in range(10): - try: - self.ssh_session.connect() - return - except: - time.sleep(i) - self.fail('ssh connection timeout') - - def ssh_command(self, command): - self.ssh_logger.info(command) - result = self.ssh_session.cmd(command) - stdout_lines = [line.rstrip() for line - in result.stdout_text.splitlines()] - for line in stdout_lines: - self.ssh_logger.info(line) - stderr_lines = [line.rstrip() for line - in result.stderr_text.splitlines()] - for line in stderr_lines: - self.ssh_logger.warning(line) - - self.assertEqual(result.exit_status, 0, - f'Guest command failed: {command}') - return stdout_lines, stderr_lines - - def ssh_command_output_contains(self, cmd, exp): - stdout, _ = self.ssh_command(cmd) - for line in stdout: - if exp in line: - break - else: - self.fail('"%s" output does not contain "%s"' % (cmd, exp)) - -class LinuxDistro: - """Represents a Linux distribution - - 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 deleted file mode 100644 index be30dcbd581..00000000000 --- a/tests/avocado/boot_linux.py +++ /dev/null @@ -1,122 +0,0 @@ -# Functional test that boots a complete Linux system via a cloud image -# -# Copyright (c) 2018-2020 Red Hat, Inc. -# -# Author: -# Cleber Rosa -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import os - -from avocado_qemu import LinuxTest, BUILD_DIR - -from avocado import skipIf - - -class BootLinuxX8664(LinuxTest): - """ - :avocado: tags=arch:x86_64 - """ - timeout = 480 - - def test_pc_i440fx_tcg(self): - """ - :avocado: tags=machine:pc - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - self.vm.add_args("-accel", "tcg") - self.launch_and_wait(set_up_ssh_connection=False) - - def test_pc_i440fx_kvm(self): - """ - :avocado: tags=machine:pc - :avocado: tags=accel:kvm - """ - self.require_accelerator("kvm") - self.vm.add_args("-accel", "kvm") - self.launch_and_wait(set_up_ssh_connection=False) - - def test_pc_q35_tcg(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - self.vm.add_args("-accel", "tcg") - self.launch_and_wait(set_up_ssh_connection=False) - - def test_pc_q35_kvm(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=accel:kvm - """ - self.require_accelerator("kvm") - self.vm.add_args("-accel", "kvm") - self.launch_and_wait(set_up_ssh_connection=False) - - -# For Aarch64 we only boot KVM tests in CI as booting the current -# Fedora OS in TCG tests is very heavyweight. There are lighter weight -# distros which we use in the machine_aarch64_virt.py tests. -class BootLinuxAarch64(LinuxTest): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - """ - timeout = 720 - - def test_virt_kvm(self): - """ - :avocado: tags=accel:kvm - :avocado: tags=cpu:host - """ - self.require_accelerator("kvm") - self.vm.add_args("-accel", "kvm") - self.vm.add_args("-machine", "virt,gic-version=host") - self.vm.add_args('-bios', - os.path.join(BUILD_DIR, 'pc-bios', - 'edk2-aarch64-code.fd')) - self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') - self.vm.add_args('-object', 'rng-random,id=rng0,filename=/dev/urandom') - self.launch_and_wait(set_up_ssh_connection=False) - - -# See the tux_baseline.py tests for almost the same coverage in a lot -# less time. -class BootLinuxPPC64(LinuxTest): - """ - :avocado: tags=arch:ppc64 - """ - - timeout = 360 - - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') - def test_pseries_tcg(self): - """ - :avocado: tags=machine:pseries - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - self.vm.add_args("-accel", "tcg") - self.launch_and_wait(set_up_ssh_connection=False) - - -class BootLinuxS390X(LinuxTest): - """ - :avocado: tags=arch:s390x - """ - - timeout = 240 - - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') - def test_s390_ccw_virtio_tcg(self): - """ - :avocado: tags=machine:s390-ccw-virtio - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - self.vm.add_args("-accel", "tcg") - self.launch_and_wait(set_up_ssh_connection=False) diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py deleted file mode 100644 index 6eab515718a..00000000000 --- a/tests/avocado/boot_linux_console.py +++ /dev/null @@ -1,1447 +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 -import lzma -import gzip -import shutil - -from avocado import skip -from avocado import skipUnless -from avocado import skipIf -from avocado_qemu import QemuSystemTest -from avocado_qemu import exec_command -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import interrupt_interactive_console_until_pattern -from avocado_qemu import wait_for_console_pattern -from avocado.utils import process -from avocado.utils import archive - -""" -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) - -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 extract_from_deb(self, deb, path): - """ - Extracts a file from a deb package into the test workdir - - :param deb: 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) - file_path = process.run("ar t %s" % deb).stdout_text.split()[2] - process.run("ar x %s %s" % (deb, 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, '/'))) - - def extract_from_rpm(self, rpm, path): - """ - Extracts a file from an RPM package into the test workdir. - - :param rpm: path to the rpm archive - :param path: path within the rpm archive of the file to be extracted - needs to be a relative path (starting with './') because - cpio(1), which is used to extract the file, expects that. - :returns: path of the extracted file - """ - cwd = os.getcwd() - os.chdir(self.workdir) - process.run("rpm2cpio %s | cpio -id %s" % (rpm, path), shell=True) - os.chdir(cwd) - return os.path.normpath(os.path.join(self.workdir, path)) - -class BootLinuxConsole(LinuxKernelTest): - """ - Boots a Linux kernel and checks that the console is operational and the - kernel command line is properly passed from QEMU to the kernel - """ - timeout = 90 - - def test_x86_64_pc(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:pc - """ - 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) - - 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(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 - :avocado: tags=machine:xlnx-versal-virt - :avocado: tags=device:pl011 - :avocado: tags=device:arm_gicv3 - :avocado: tags=accel:tcg - """ - images_url = ('http://ports.ubuntu.com/ubuntu-ports/dists/' - 'bionic-updates/main/installer-arm64/' - '20101020ubuntu543.19/images/') - kernel_url = images_url + 'netboot/ubuntu-installer/arm64/linux' - kernel_hash = 'e167757620640eb26de0972f578741924abb3a82' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - initrd_url = images_url + 'netboot/ubuntu-installer/arm64/initrd.gz' - initrd_hash = 'cab5cb3fcefca8408aa5aae57f24574bfce8bdb9' - initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - - self.vm.set_console() - self.vm.add_args('-m', '2G', - '-accel', 'tcg', - '-kernel', kernel_path, - '-initrd', initrd_path) - self.vm.launch() - self.wait_for_console_pattern('Checked W+X mappings: passed') - - def test_arm_virt(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:virt - :avocado: tags=accel:tcg - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/armhfp/os/images/pxeboot' - '/vmlinuz') - kernel_hash = 'e9826d741b4fb04cadba8d4824d1ed3b7fb8b4d4' - 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.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_arm_emcraft_sf2(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:emcraft-sf2 - :avocado: tags=endian:little - :avocado: tags=u-boot - :avocado: tags=accel:tcg - """ - self.require_netdev('user') - - uboot_url = ('https://raw.githubusercontent.com/' - 'Subbaraya-Sundeep/qemu-test-binaries/' - 'fe371d32e50ca682391e1e70ab98c2942aeffb01/u-boot') - uboot_hash = 'cbb8cbab970f594bf6523b9855be209c08374ae2' - uboot_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash) - spi_url = ('https://raw.githubusercontent.com/' - 'Subbaraya-Sundeep/qemu-test-binaries/' - 'fe371d32e50ca682391e1e70ab98c2942aeffb01/spi.bin') - spi_hash = '65523a1835949b6f4553be96dec1b6a38fb05501' - spi_path = self.fetch_asset(spi_url, asset_hash=spi_hash) - - file_truncate(spi_path, 16 << 20) # Spansion S25FL128SDPBHICO is 16 MiB - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE - self.vm.add_args('-kernel', uboot_path, - '-append', kernel_command_line, - '-drive', 'file=' + spi_path + ',if=mtd,format=raw', - '-no-reboot') - self.vm.launch() - self.wait_for_console_pattern('Enter \'help\' for a list') - - exec_command_and_wait_for_pattern(self, 'ifconfig eth0 10.0.2.15', - 'eth0: link becomes ready') - 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 - :avocado: tags=machine:smdkc210 - :avocado: tags=accel:tcg - """ - deb_url = ('https://snapshot.debian.org/archive/debian/' - '20190928T224601Z/pool/main/l/linux/' - 'linux-image-4.19.0-6-armmp_4.19.67-2+deb10u1_armhf.deb') - deb_hash = 'fa9df4a0d38936cb50084838f2cb933f570d7d82' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-4.19.0-6-armmp') - dtb_path = '/usr/lib/linux-image-4.19.0-6-armmp/exynos4210-smdkv310.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv5.cpio.gz') - initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'earlycon=exynos4210,0x13800000 earlyprintk ' + - 'console=ttySAC0,115200n8 ' + - 'random.trust_cpu=off cryptomgr.notests ' + - 'cpuidle.off=1 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.') - # TODO user command, for now the uart is stuck - - def test_arm_cubieboard_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:cubieboard - :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/sun4i-a10-cubieboard.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv5.cpio.gz') - initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'usbcore.nousb ' - 'panic=-1 noreboot') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-initrd', initrd_path, - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - self.wait_for_console_pattern('Boot successful.') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun4i/sun5i') - exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', - 'system-control@1c00000') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - def test_arm_cubieboard_sata(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:cubieboard - :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/sun4i-a10-cubieboard.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - rootfs_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv5.ext2.gz') - rootfs_hash = '093e89d2b4d982234bf528bc9fb2f2f17a9d1f93' - rootfs_path_gz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash) - rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(rootfs_path_gz, rootfs_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'usbcore.nousb ' - 'root=/dev/sda ro ' - 'panic=-1 noreboot') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-drive', 'if=none,format=raw,id=disk0,file=' - + rootfs_path, - '-device', 'ide-hd,bus=ide.0,drive=disk0', - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - self.wait_for_console_pattern('Boot successful.') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun4i/sun5i') - exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', - 'sda') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # 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): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:quanta-gsj - :avocado: tags=accel:tcg - """ - # 25 MiB compressed, 32 MiB uncompressed. - image_url = ( - 'https://github.com/hskinnemoen/openbmc/releases/download/' - '20200711-gsj-qemu-0/obmc-phosphor-image-gsj.static.mtd.gz') - image_hash = '14895e634923345cb5c8776037ff7876df96f6b1' - image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash) - image_name = 'obmc.mtd' - image_path = os.path.join(self.workdir, image_name) - archive.gzip_uncompress(image_path_gz, image_path) - - self.vm.set_console() - drive_args = 'file=' + image_path + ',if=mtd,bus=0,unit=0' - self.vm.add_args('-drive', drive_args) - self.vm.launch() - - # Disable drivers and services that stall for a long time during boot, - # to avoid running past the 90-second timeout. These may be removed - # as the corresponding device support is added. - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + ( - 'console=${console} ' - 'mem=${mem} ' - 'initcall_blacklist=npcm_i2c_bus_driver_init ' - 'systemd.mask=systemd-random-seed.service ' - 'systemd.mask=dropbearkey.service ' - ) - - self.wait_for_console_pattern('> BootBlock by Nuvoton') - self.wait_for_console_pattern('>Device: Poleg BMC NPCM730') - self.wait_for_console_pattern('>Skip DDR init.') - self.wait_for_console_pattern('U-Boot ') - interrupt_interactive_console_until_pattern( - self, 'Hit any key to stop autoboot:', 'U-Boot>') - exec_command_and_wait_for_pattern( - self, "setenv bootargs ${bootargs} " + kernel_command_line, - 'U-Boot>') - exec_command_and_wait_for_pattern( - self, 'run romboot', 'Booting Kernel from flash') - self.wait_for_console_pattern('Booting Linux on physical CPU 0x0') - self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0') - self.wait_for_console_pattern('OpenBMC Project Reference Distro') - self.wait_for_console_pattern('gsj login:') - - def test_arm_quanta_gsj_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:quanta-gsj - :avocado: tags=accel:tcg - """ - initrd_url = ( - 'https://github.com/hskinnemoen/openbmc/releases/download/' - '20200711-gsj-qemu-0/obmc-phosphor-initramfs-gsj.cpio.xz') - initrd_hash = '98fefe5d7e56727b1eb17d5c00311b1b5c945300' - initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - kernel_url = ( - 'https://github.com/hskinnemoen/openbmc/releases/download/' - '20200711-gsj-qemu-0/uImage-gsj.bin') - kernel_hash = 'fa67b2f141d56d39b3c54305c0e8a899c99eb2c7' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - dtb_url = ( - 'https://github.com/hskinnemoen/openbmc/releases/download/' - '20200711-gsj-qemu-0/nuvoton-npcm730-gsj.dtb') - dtb_hash = '18315f7006d7b688d8312d5c727eecd819aa36a4' - dtb_path = self.fetch_asset(dtb_url, asset_hash=dtb_hash) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200n8 ' - 'earlycon=uart8250,mmio32,0xf0001000') - self.vm.add_args('-kernel', kernel_path, - '-initrd', initrd_path, - '-dtb', dtb_path, - '-append', kernel_command_line) - self.vm.launch() - - self.wait_for_console_pattern('Booting Linux on physical CPU 0x0') - self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0') - self.wait_for_console_pattern( - 'Give root password for system maintenance') - - def test_arm_bpim2u(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:bpim2u - :avocado: tags=accel:tcg - """ - deb_url = ('https://apt.armbian.com/pool/main/l/linux-5.10.16-sunxi/' - 'linux-image-current-sunxi_21.02.2_armhf.deb') - deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-5.10.16-sunxi') - dtb_path = ('/usr/lib/linux-image-current-sunxi/' - 'sun8i-r40-bananapi-m2-ultra.dtb') - dtb_path = self.extract_from_deb(deb_path, dtb_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200n8 ' - 'earlycon=uart,mmio32,0x1c28000') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - def test_arm_bpim2u_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=accel:tcg - :avocado: tags=machine:bpim2u - """ - deb_url = ('https://apt.armbian.com/pool/main/l/linux-5.10.16-sunxi/' - 'linux-image-current-sunxi_21.02.2_armhf.deb') - deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-5.10.16-sunxi') - dtb_path = ('/usr/lib/linux-image-current-sunxi/' - 'sun8i-r40-bananapi-m2-ultra.dtb') - dtb_path = self.extract_from_deb(deb_path, dtb_path) - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv7a.cpio.gz') - initrd_hash = '604b2e45cdf35045846b8bbfbf2129b1891bdc9c' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'panic=-1 noreboot') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-initrd', initrd_path, - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - self.wait_for_console_pattern('Boot successful.') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun8i Family') - exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', - 'system-control@1c00000') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - def test_arm_bpim2u_gmac(self): - """ - :avocado: tags=arch:arm - :avocado: tags=accel:tcg - :avocado: tags=machine:bpim2u - :avocado: tags=device:sd - """ - self.require_netdev('user') - - deb_url = ('https://apt.armbian.com/pool/main/l/linux-5.10.16-sunxi/' - 'linux-image-current-sunxi_21.02.2_armhf.deb') - deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-5.10.16-sunxi') - dtb_path = ('/usr/lib/linux-image-current-sunxi/' - 'sun8i-r40-bananapi-m2-ultra.dtb') - dtb_path = self.extract_from_deb(deb_path, dtb_path) - rootfs_url = ('http://storage.kernelci.org/images/rootfs/buildroot/' - 'buildroot-baseline/20221116.0/armel/rootfs.ext2.xz') - rootfs_hash = 'fae32f337c7b87547b10f42599acf109da8b6d9a' - rootfs_path_xz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash) - rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.lzma_uncompress(rootfs_path_xz, rootfs_path) - image_pow2ceil_expand(rootfs_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'root=b300 rootwait rw ' - 'panic=-1 noreboot') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-drive', 'file=' + rootfs_path + ',if=sd,format=raw', - '-net', 'nic,model=gmac,netdev=host_gmac', - '-netdev', 'user,id=host_gmac', - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - shell_ready = "/bin/sh: can't access tty; job control turned off" - self.wait_for_console_pattern(shell_ready) - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun8i Family') - exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', - 'mmcblk') - exec_command_and_wait_for_pattern(self, 'ifconfig eth0 up', - 'eth0: Link is Up') - exec_command_and_wait_for_pattern(self, 'udhcpc eth0', - 'udhcpc: lease of 10.0.2.15 obtained') - exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', - '3 packets transmitted, 3 packets received, 0% packet loss') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') - def test_arm_bpim2u_openwrt_22_03_3(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:bpim2u - :avocado: tags=device:sd - """ - - # This test download a 8.9 MiB compressed image and expand it - # to 127 MiB. - image_url = ('https://downloads.openwrt.org/releases/22.03.3/targets/' - 'sunxi/cortexa7/openwrt-22.03.3-sunxi-cortexa7-' - 'sinovoip_bananapi-m2-ultra-ext4-sdcard.img.gz') - image_hash = ('5b41b4e11423e562c6011640f9a7cd3b' - 'dd0a3d42b83430f7caa70a432e6cd82c') - image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - image_path = archive.extract(image_path_gz, self.workdir) - image_pow2ceil_expand(image_path) - - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw', - '-nic', 'user', - '-no-reboot') - self.vm.launch() - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'usbcore.nousb ' - 'noreboot') - - self.wait_for_console_pattern('U-Boot SPL') - - interrupt_interactive_console_until_pattern( - self, 'Hit any key to stop autoboot:', '=>') - exec_command_and_wait_for_pattern(self, "setenv extraargs '" + - kernel_command_line + "'", '=>') - exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...'); - - self.wait_for_console_pattern( - 'Please press Enter to activate this console.') - - exec_command_and_wait_for_pattern(self, ' ', 'root@') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun8i Family') - exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', - 'system-control@1c00000') - - def test_arm_orangepi(self): - """ - :avocado: tags=arch:arm - :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=accel:tcg - :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:rainier-bmc - """ - deb_url = ('http://snapshot.debian.org/archive/debian/' - '20220606T211338Z/' - 'pool/main/l/linux/' - '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.17.0-2-armmp') - dtb_path = self.extract_from_deb(deb_path, - '/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, - '-dtb', dtb_path, - '-net', 'nic') - self.vm.launch() - self.wait_for_console_pattern("Booting Linux on physical CPU 0xf00") - 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 deleted file mode 100644 index fc2faeedb55..00000000000 --- a/tests/avocado/boot_xen.py +++ /dev/null @@ -1,116 +0,0 @@ -# Functional test that boots a Xen hypervisor with a domU kernel and -# checks the console output is vaguely sane . -# -# Copyright (c) 2020 Linaro -# -# Author: -# Alex Bennée -# -# 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. - -import os - -from avocado_qemu import wait_for_console_pattern -from boot_linux_console import LinuxKernelTest - - -class BootXenBase(LinuxKernelTest): - """ - Boots a Xen hypervisor with a Linux DomU kernel. - """ - - timeout = 90 - XEN_COMMON_COMMAND_LINE = 'dom0_mem=128M loglvl=all guest_loglvl=all' - - def fetch_guest_kernel(self): - # 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 - - 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, - '-device', - 'guest-loader,addr=0x47000000,kernel=%s,bootargs=console=hvc0' - % (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=' - 'xen-hypervisor-4.11-arm64_4.11.4%2B37-g3263f257ca-1_arm64.deb') - xen_sha1 = '034e634d4416adbad1212d59b62bccdcda63e62a' - xen_deb = self.fetch_asset(xen_url, asset_hash=xen_sha1) - xen_path = self.extract_from_deb(xen_deb, "/boot/xen-4.11-arm64") - - 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=' - 'xen-hypervisor-4.14-arm64_4.14.0%2B80-gd101b417b7-1_arm64.deb') - xen_sha1 = 'b9d209dd689ed2b393e625303a225badefec1160' - xen_deb = self.fetch_asset(xen_url, asset_hash=xen_sha1) - xen_path = self.extract_from_deb(xen_deb, "/boot/xen-4.14-arm64") - - 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') - xen_sha1 = 'fc191172b85cf355abb95d275a24cc0f6d6579d8' - xen_deb = self.fetch_asset(xen_url, asset_hash=xen_sha1) - xen_path = self.extract_from_deb(xen_deb, "/boot/xen-4.15-unstable") - - self.launch_xen(xen_path) diff --git a/tests/avocado/cpu_queries.py b/tests/avocado/cpu_queries.py deleted file mode 100644 index cf69f69b116..00000000000 --- 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 22f504418d2..00000000000 --- 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_cpu.py b/tests/avocado/hotplug_cpu.py deleted file mode 100644 index 6374bf1b546..00000000000 --- a/tests/avocado/hotplug_cpu.py +++ /dev/null @@ -1,37 +0,0 @@ -# Functional test that hotplugs a CPU and checks it on a Linux guest -# -# 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 LinuxTest - - -class HotPlugCPU(LinuxTest): - - def test(self): - """ - :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('-cpu', 'Haswell') - self.vm.add_args('-smp', '1,sockets=1,cores=2,threads=1,maxcpus=2') - self.launch_and_wait() - - self.ssh_command('test -e /sys/devices/system/cpu/cpu0') - 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.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 fdc4d90c420..00000000000 --- 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 deleted file mode 100644 index 474d62f6bf1..00000000000 --- a/tests/avocado/intel_iommu.py +++ /dev/null @@ -1,119 +0,0 @@ -# INTEL_IOMMU Functional tests -# -# Copyright (c) 2021 Red Hat, Inc. -# -# Author: -# Eric Auger -# -# 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 LinuxTest - -@skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') -class IntelIOMMU(LinuxTest): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=distro:fedora - :avocado: tags=distro_version:31 - :avocado: tags=machine:q35 - :avocado: tags=accel:kvm - :avocado: tags=intel_iommu - """ - - IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on' - kernel_path = None - initrd_path = None - kernel_params = None - - def set_up_boot(self): - path = self.download_boot() - self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,scsi=off,' + - '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) - self.vm.add_args('-drive', - 'file=%s,if=none,cache=writethrough,id=drv0' % path) - - def setUp(self): - super(IntelIOMMU, self).setUp(None, 'virtio-net-pci' + self.IOMMU_ADDON) - - def add_common_args(self): - self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') - self.vm.add_args('-object', - 'rng-random,id=rng0,filename=/dev/urandom') - - def common_vm_setup(self, custom_kernel=None): - self.require_accelerator("kvm") - self.add_common_args() - self.vm.add_args("-accel", "kvm") - - if custom_kernel is None: - return - - kernel_url = self.distro.pxeboot_url + 'vmlinuz' - initrd_url = self.distro.pxeboot_url + 'initrd.img' - self.kernel_path = self.fetch_asset(kernel_url) - self.initrd_path = self.fetch_asset(initrd_url) - - def run_and_check(self): - if self.kernel_path: - self.vm.add_args('-kernel', self.kernel_path, - '-append', self.kernel_params, - '-initrd', self.initrd_path) - self.launch_and_wait() - self.ssh_command('cat /proc/cmdline') - self.ssh_command('dmesg | grep -e DMAR -e IOMMU') - self.ssh_command('find /sys/kernel/iommu_groups/ -type l') - self.ssh_command('dnf -y install numactl-devel') - - def test_intel_iommu(self): - """ - :avocado: tags=intel_iommu_intremap - """ - - self.common_vm_setup(True) - self.vm.add_args('-device', 'intel-iommu,intremap=on') - self.vm.add_args('-machine', 'kernel_irqchip=split') - - self.kernel_params = (self.distro.default_kernel_params + - ' quiet intel_iommu=on') - self.run_and_check() - - def test_intel_iommu_strict(self): - """ - :avocado: tags=intel_iommu_strict - """ - - self.common_vm_setup(True) - self.vm.add_args('-device', 'intel-iommu,intremap=on') - self.vm.add_args('-machine', 'kernel_irqchip=split') - self.kernel_params = (self.distro.default_kernel_params + - ' quiet intel_iommu=on,strict') - self.run_and_check() - - def test_intel_iommu_strict_cm(self): - """ - :avocado: tags=intel_iommu_strict_cm - """ - - self.common_vm_setup(True) - self.vm.add_args('-device', 'intel-iommu,intremap=on,caching-mode=on') - self.vm.add_args('-machine', 'kernel_irqchip=split') - self.kernel_params = (self.distro.default_kernel_params + - ' quiet intel_iommu=on,strict') - self.run_and_check() - - def test_intel_iommu_pt(self): - """ - :avocado: tags=intel_iommu_pt - """ - - self.common_vm_setup(True) - self.vm.add_args('-device', 'intel-iommu,intremap=on') - self.vm.add_args('-machine', 'kernel_irqchip=split') - self.kernel_params = (self.distro.default_kernel_params + - ' quiet intel_iommu=on iommu=pt') - self.run_and_check() diff --git a/tests/avocado/kvm_xen_guest.py b/tests/avocado/kvm_xen_guest.py deleted file mode 100644 index 5391283113e..00000000000 --- a/tests/avocado/kvm_xen_guest.py +++ /dev/null @@ -1,171 +0,0 @@ -# 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,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 ba02e5a563d..00000000000 --- 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 deleted file mode 100644 index d9bb525ad9c..00000000000 --- a/tests/avocado/linux_ssh_mips_malta.py +++ /dev/null @@ -1,205 +0,0 @@ -# Functional test that boots a VM and run commands via a SSH session -# -# 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 -import re -import base64 -import logging -import time - -from avocado import skipUnless -from avocado_qemu import LinuxSSHMixIn -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado.utils import process -from avocado.utils import archive -from avocado.utils import ssh - - -@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') -@skipUnless(ssh.SSH_CLIENT_BINARY, 'No SSH client available') -class LinuxSSH(QemuSystemTest, LinuxSSHMixIn): - """ - :avocado: tags=accel:tcg - """ - - timeout = 150 # Not for 'configure --enable-debug --enable-debug-tcg' - - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - VM_IP = '127.0.0.1' - - BASE_URL = 'https://people.debian.org/~aurel32/qemu/' - IMAGE_INFO = { - 'be': {'base_url': 'mips', - 'image_name': 'debian_wheezy_mips_standard.qcow2', - 'image_hash': '8987a63270df67345b2135a6b7a4885a35e392d5', - 'kernel_hash': { - 32: '592e384a4edc16dade52a6cd5c785c637bcbc9ad', - 64: 'db6eea7de35d36c77d8c165b6bcb222e16eb91db'} - }, - 'le': {'base_url': 'mipsel', - 'image_name': 'debian_wheezy_mipsel_standard.qcow2', - 'image_hash': '7866764d9de3ef536ffca24c9fb9f04ffdb45802', - 'kernel_hash': { - 32: 'a66bea5a8adaa2cb3d36a1d4e0ccdb01be8f6c2a', - 64: '6a7f77245acf231415a0e8b725d91ed2f3487794'} - } - } - CPU_INFO = { - 32: {'cpu': 'MIPS 24Kc', 'kernel_release': '3.2.0-4-4kc-malta'}, - 64: {'cpu': 'MIPS 20Kc', 'kernel_release': '3.2.0-4-5kc-malta'} - } - - def get_url(self, endianess, path=''): - qkey = {'le': 'el', 'be': ''} - return '%s/mips%s/%s' % (self.BASE_URL, qkey[endianess], path) - - def get_image_info(self, endianess): - dinfo = self.IMAGE_INFO[endianess] - image_url = self.get_url(endianess, dinfo['image_name']) - image_hash = dinfo['image_hash'] - return (image_url, image_hash) - - def get_kernel_info(self, endianess, wordsize): - minfo = self.CPU_INFO[wordsize] - kernel_url = self.get_url(endianess, - 'vmlinux-%s' % minfo['kernel_release']) - kernel_hash = self.IMAGE_INFO[endianess]['kernel_hash'][wordsize] - return kernel_url, kernel_hash - - def ssh_disconnect_vm(self): - self.ssh_session.quit() - - def boot_debian_wheezy_image_and_ssh_login(self, endianess, kernel_path): - image_url, image_hash = self.get_image_info(endianess) - image_path = self.fetch_asset(image_url, asset_hash=image_hash) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE - + 'console=ttyS0 root=/dev/sda1') - self.vm.add_args('-no-reboot', - '-kernel', kernel_path, - '-append', kernel_command_line, - '-drive', 'file=%s,snapshot=on' % image_path, - '-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', - '-device', 'pcnet,netdev=vnet') - self.vm.launch() - - self.log.info('VM launched, waiting for sshd') - console_pattern = 'Starting OpenBSD Secure Shell server: sshd' - wait_for_console_pattern(self, console_pattern, 'Oops') - self.log.info('sshd ready') - - self.ssh_connect('root', 'root', False) - - def shutdown_via_ssh(self): - self.ssh_command('poweroff') - self.ssh_disconnect_vm() - wait_for_console_pattern(self, 'Power down', 'Oops') - - def run_common_commands(self, wordsize): - self.ssh_command_output_contains( - 'cat /proc/cpuinfo', - self.CPU_INFO[wordsize]['cpu']) - self.ssh_command_output_contains( - 'uname -m', - 'mips') - self.ssh_command_output_contains( - 'uname -r', - self.CPU_INFO[wordsize]['kernel_release']) - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'XT-PIC timer') - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'XT-PIC i8042') - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'XT-PIC serial') - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'XT-PIC ata_piix') - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'XT-PIC eth0') - self.ssh_command_output_contains( - 'cat /proc/devices', - 'input') - self.ssh_command_output_contains( - 'cat /proc/devices', - 'usb') - self.ssh_command_output_contains( - 'cat /proc/devices', - 'fb') - self.ssh_command_output_contains( - 'cat /proc/ioports', - ' : serial') - self.ssh_command_output_contains( - 'cat /proc/ioports', - ' : ata_piix') - self.ssh_command_output_contains( - 'cat /proc/ioports', - ' : piix4_smbus') - self.ssh_command_output_contains( - 'lspci -d 11ab:4620', - 'GT-64120') - self.ssh_command_output_contains( - 'cat /sys/bus/i2c/devices/i2c-0/name', - 'SMBus PIIX4 adapter') - self.ssh_command_output_contains( - 'cat /proc/mtd', - 'YAMON') - # Empty 'Board Config' (64KB) - self.ssh_command_output_contains( - 'md5sum /dev/mtd2ro', - '0dfbe8aa4c20b52e1b8bf3cb6cbdf193') - - def check_mips_malta(self, uname_m, endianess): - wordsize = 64 if '64' in uname_m else 32 - kernel_url, kernel_hash = self.get_kernel_info(endianess, wordsize) - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.boot_debian_wheezy_image_and_ssh_login(endianess, kernel_path) - - stdout, _ = self.ssh_command('uname -a') - self.assertIn(True, [uname_m + " GNU/Linux" in line for line in stdout]) - - self.run_common_commands(wordsize) - self.shutdown_via_ssh() - # Wait for VM to shut down gracefully - self.vm.wait() - - def test_mips_malta32eb_kernel3_2_0(self): - """ - :avocado: tags=arch:mips - :avocado: tags=endian:big - :avocado: tags=device:pcnet32 - """ - self.check_mips_malta('mips', 'be') - - def test_mips_malta32el_kernel3_2_0(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=endian:little - :avocado: tags=device:pcnet32 - """ - self.check_mips_malta('mips', 'le') - - def test_mips_malta64eb_kernel3_2_0(self): - """ - :avocado: tags=arch:mips64 - :avocado: tags=endian:big - :avocado: tags=device:pcnet32 - """ - self.check_mips_malta('mips64', 'be') - - def test_mips_malta64el_kernel3_2_0(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=endian:little - :avocado: tags=device:pcnet32 - """ - self.check_mips_malta('mips64', 'le') diff --git a/tests/avocado/load_bflt.py b/tests/avocado/load_bflt.py deleted file mode 100644 index bb50cec1ee8..00000000000 --- 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_sbsaref.py b/tests/avocado/machine_aarch64_sbsaref.py deleted file mode 100644 index a794245e7e2..00000000000 --- a/tests/avocado/machine_aarch64_sbsaref.py +++ /dev/null @@ -1,155 +0,0 @@ -# Functional test that boots a Linux kernel and checks the console -# -# SPDX-FileCopyrightText: 2023 Linaro Ltd. -# SPDX-FileContributor: Philippe Mathieu-Daudé -# SPDX-FileContributor: Marcin Juszkiewicz -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import os - -from avocado import skipUnless -from avocado.utils import archive - -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado_qemu import interrupt_interactive_console_until_pattern - - -class Aarch64SbsarefMachine(QemuSystemTest): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:sbsa-ref - """ - - timeout = 180 - - def fetch_firmware(self): - """ - Flash volumes generated using: - - - Fedora GNU Toolchain version 13.1.1 20230511 (Red Hat 13.1.1-2) - - - Trusted Firmware-A - https://github.com/ARM-software/arm-trusted-firmware/tree/c0d8ee38 - - - Tianocore EDK II - https://github.com/tianocore/edk2/tree/0f9283429dd4 - https://github.com/tianocore/edk2-non-osi/tree/f0bb00937ad6 - https://github.com/tianocore/edk2-platforms/tree/7880b92e2a04 - """ - - # Secure BootRom (TF-A code) - fs0_xz_url = ( - "https://fileserver.linaro.org/s/HrYMCjP7MEccjRP/" - "download/SBSA_FLASH0.fd.xz" - ) - fs0_xz_hash = "447eff64a90b84ce47703c6ec41fbfc25befaaea" - tar_xz_path = self.fetch_asset(fs0_xz_url, asset_hash=fs0_xz_hash) - archive.extract(tar_xz_path, self.workdir) - fs0_path = os.path.join(self.workdir, "SBSA_FLASH0.fd") - - # Non-secure rom (UEFI and EFI variables) - fs1_xz_url = ( - "https://fileserver.linaro.org/s/t8foNnMPz74DZZy/" - "download/SBSA_FLASH1.fd.xz" - ) - fs1_xz_hash = "13a9a262953787c7fc5a9155dfaa26e703631e02" - tar_xz_path = self.fetch_asset(fs1_xz_url, asset_hash=fs1_xz_hash) - archive.extract(tar_xz_path, self.workdir) - fs1_path = os.path.join(self.workdir, "SBSA_FLASH1.fd") - - for path in [fs0_path, fs1_path]: - with open(path, "ab+") as fd: - fd.truncate(256 << 20) # Expand volumes to 256MiB - - self.vm.set_console() - self.vm.add_args( - "-drive", - f"if=pflash,file={fs0_path},format=raw", - "-drive", - f"if=pflash,file={fs1_path},format=raw", - "-smp", - "1", - "-machine", - "sbsa-ref", - ) - - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is not reliable') - def test_sbsaref_edk2_firmware(self): - """ - :avocado: tags=cpu:cortex-a57 - """ - - self.fetch_firmware() - self.vm.launch() - - # TF-A boot sequence: - # - # https://github.com/ARM-software/arm-trusted-firmware/blob/v2.8.0/\ - # docs/design/trusted-board-boot.rst#trusted-board-boot-sequence - # https://trustedfirmware-a.readthedocs.io/en/v2.8/\ - # design/firmware-design.html#cold-boot - - # AP Trusted ROM - wait_for_console_pattern(self, "Booting Trusted Firmware") - wait_for_console_pattern(self, "BL1: v2.9(release):v2.9") - wait_for_console_pattern(self, "BL1: Booting BL2") - - # Trusted Boot Firmware - wait_for_console_pattern(self, "BL2: v2.9(release)") - wait_for_console_pattern(self, "Booting BL31") - - # EL3 Runtime Software - wait_for_console_pattern(self, "BL31: v2.9(release)") - - # Non-trusted Firmware - wait_for_console_pattern(self, "UEFI firmware (version 1.0") - interrupt_interactive_console_until_pattern(self, "QEMU SBSA-REF Machine") - - # This tests the whole boot chain from EFI to Userspace - # We only boot a whole OS for the current top level CPU and GIC - # Other test profiles should use more minimal boots - def boot_alpine_linux(self, cpu): - self.fetch_firmware() - - iso_url = ( - "https://dl-cdn.alpinelinux.org/" - "alpine/v3.17/releases/aarch64/alpine-standard-3.17.2-aarch64.iso" - ) - - iso_hash = "5a36304ecf039292082d92b48152a9ec21009d3a62f459de623e19c4bd9dc027" - iso_path = self.fetch_asset(iso_url, algorithm="sha256", asset_hash=iso_hash) - - self.vm.set_console() - self.vm.add_args( - "-cpu", - cpu, - "-drive", - f"file={iso_path},format=raw", - "-device", - "virtio-rng-pci,rng=rng0", - "-object", - "rng-random,id=rng0,filename=/dev/urandom", - ) - - self.vm.launch() - wait_for_console_pattern(self, "Welcome to Alpine Linux 3.17") - - def test_sbsaref_alpine_linux_cortex_a57(self): - """ - :avocado: tags=cpu:cortex-a57 - """ - self.boot_alpine_linux("cortex-a57") - - def test_sbsaref_alpine_linux_neoverse_n1(self): - """ - :avocado: tags=cpu:max - """ - self.boot_alpine_linux("neoverse-n1") - - def test_sbsaref_alpine_linux_max(self): - """ - :avocado: tags=cpu:max - """ - self.boot_alpine_linux("max,pauth-impdef=on") diff --git a/tests/avocado/machine_aarch64_virt.py b/tests/avocado/machine_aarch64_virt.py deleted file mode 100644 index a90dc6ff4b4..00000000000 --- a/tests/avocado/machine_aarch64_virt.py +++ /dev/null @@ -1,146 +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 -import logging - -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado_qemu import exec_command -from avocado_qemu import BUILD_DIR -from avocado.utils import process -from avocado.utils.path import find_command - -class Aarch64VirtMachine(QemuSystemTest): - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - timeout = 360 - - def wait_for_console_pattern(self, success_message, vm=None): - wait_for_console_pattern(self, success_message, - failure_message='Kernel panic - not syncing', - vm=vm) - - # 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.17/releases/aarch64/' - 'alpine-standard-3.17.2-aarch64.iso') - - # Alpine use sha256 so I recalculated this myself - iso_sha1 = '76284fcd7b41fe899b0c2375ceb8470803eea839' - iso_path = self.fetch_asset(iso_url, asset_hash=iso_sha1) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - self.require_accelerator("tcg") - - self.vm.add_args("-accel", "tcg") - self.vm.add_args("-cpu", "max,pauth-impdef=on") - self.vm.add_args("-machine", - "virt,acpi=on," - "virtualization=on," - "mte=on," - "gic-version=max,iommu=smmuv3") - self.vm.add_args("-smp", "2", "-m", "1024") - self.vm.add_args('-bios', os.path.join(BUILD_DIR, 'pc-bios', - 'edk2-aarch64-code.fd')) - self.vm.add_args("-drive", f"file={iso_path},format=raw") - self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') - self.vm.add_args('-object', 'rng-random,id=rng0,filename=/dev/urandom') - - self.vm.launch() - self.wait_for_console_pattern('Welcome to Alpine Linux 3.17') - - - def common_aarch64_virt(self, machine): - """ - Common code to launch basic virt machine with kernel+initrd - and a scratch disk. - """ - logger = logging.getLogger('aarch64_virt') - - kernel_url = ('https://fileserver.linaro.org/s/' - 'z6B2ARM7DQT3HWN/download') - kernel_hash = 'ed11daab50c151dde0e1e9c9cb8b2d9bd3215347' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - 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 = os.path.join(BUILD_DIR, 'qemu-img') - if not os.path.exists(qemu_img): - qemu_img = find_command('qemu-img', False) - if qemu_img is False: - self.cancel('Could not find "qemu-img", which is required to ' - 'create the temporary qcow2 image') - cmd = '%s create -f qcow2 %s 8M' % (qemu_img, image_path) - process.run(cmd) - - # Add the device - self.vm.add_args('-blockdev', - f"driver=qcow2,file.driver=file,file.filename={image_path},node-name=scratch") - self.vm.add_args('-device', - 'virtio-blk-device,drive=scratch') - - self.vm.launch() - self.wait_for_console_pattern('Welcome to Buildroot') - time.sleep(0.1) - exec_command(self, 'root') - time.sleep(0.1) - exec_command(self, 'dd if=/dev/hwrng of=/dev/vda bs=512 count=4') - time.sleep(0.1) - exec_command(self, 'md5sum /dev/vda') - time.sleep(0.1) - exec_command(self, 'cat /proc/interrupts') - time.sleep(0.1) - exec_command(self, 'cat /proc/self/maps') - time.sleep(0.1) - - def test_aarch64_virt_gicv3(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=accel:tcg - :avocado: tags=cpu:max - """ - self.common_aarch64_virt("virt,gic_version=3") - - def test_aarch64_virt_gicv2(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=accel:tcg - :avocado: tags=cpu:max - """ - self.common_aarch64_virt("virt,gic-version=2") diff --git a/tests/avocado/machine_arm_canona1100.py b/tests/avocado/machine_arm_canona1100.py deleted file mode 100644 index a42d8b0f2b0..00000000000 --- 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 1ffe1073ef8..00000000000 --- 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 12e9a6803ba..00000000000 --- 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 90f1b7cb77a..00000000000 --- a/tests/avocado/machine_aspeed.py +++ /dev/null @@ -1,370 +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 -import tempfile -import subprocess - -from avocado_qemu import LinuxSSHMixIn -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado_qemu import exec_command -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import interrupt_interactive_console_until_pattern -from avocado_qemu import has_cmd -from avocado.utils import archive -from avocado import skipIf -from avocado import skipUnless - - -class AST1030Machine(QemuSystemTest): - """Boots the zephyr os and checks that the console is operational""" - - timeout = 10 - - def test_ast1030_zephyros_1_04(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:ast1030-evb - :avocado: tags=os:zephyr - """ - tar_url = ('https://github.com/AspeedTech-BMC' - '/zephyr/releases/download/v00.01.04/ast1030-evb-demo.zip') - 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") - - def test_ast1030_zephyros_1_07(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:ast1030-evb - :avocado: tags=os:zephyr - """ - tar_url = ('https://github.com/AspeedTech-BMC' - '/zephyr/releases/download/v00.01.07/ast1030-evb-demo.zip') - tar_hash = '40ac87eabdcd3b3454ce5aad11fedc72a33ecda2' - tar_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - archive.extract(tar_path, self.workdir) - kernel_file = self.workdir + "/ast1030-evb-demo/zephyr.bin" - self.vm.set_console() - self.vm.add_args('-kernel', kernel_file, - '-nographic') - self.vm.launch() - wait_for_console_pattern(self, "Booting Zephyr OS") - for shell_cmd in [ - 'kernel stacks', - 'otp info conf', - 'otp info scu', - 'hwinfo devid', - 'crypto aes256_cbc_vault', - 'random get', - 'jtag JTAG1 sw_xfer high TMS', - 'adc ADC0 resolution 12', - 'adc ADC0 read 42', - 'adc ADC1 read 69', - 'i2c scan I2C_0', - 'i3c attach I3C_0', - 'hash test', - 'kernel uptime', - 'kernel reboot warm', - 'kernel uptime', - 'kernel reboot cold', - 'kernel uptime', - ]: exec_command_and_wait_for_pattern(self, shell_cmd, "uart:~$") - -class AST2x00Machine(QemuSystemTest): - - timeout = 90 - - def wait_for_console_pattern(self, success_message, vm=None): - wait_for_console_pattern(self, success_message, - failure_message='Kernel panic - not syncing', - vm=vm) - - def do_test_arm_aspeed(self, image): - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', - '-net', 'nic') - self.vm.launch() - - self.wait_for_console_pattern("U-Boot 2016.07") - self.wait_for_console_pattern("## Loading kernel from FIT Image at 20080000") - self.wait_for_console_pattern("Starting kernel ...") - self.wait_for_console_pattern("Booting Linux on physical CPU 0x0") - wait_for_console_pattern(self, - "aspeed-smc 1e620000.spi: read control register: 203b0641") - self.wait_for_console_pattern("ftgmac100 1e660000.ethernet eth0: irq ") - self.wait_for_console_pattern("systemd[1]: Set hostname to") - - def test_arm_ast2400_palmetto_openbmc_v2_9_0(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:palmetto-bmc - """ - - image_url = ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' - 'obmc-phosphor-image-palmetto.static.mtd') - image_hash = ('3e13bbbc28e424865dc42f35ad672b10f2e82cdb11846bb28fa625b48beafd0d') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - - self.do_test_arm_aspeed(image_path) - - def test_arm_ast2500_romulus_openbmc_v2_9_0(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:romulus-bmc - """ - - image_url = ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' - 'obmc-phosphor-image-romulus.static.mtd') - image_hash = ('820341076803f1955bc31e647a512c79f9add4f5233d0697678bab4604c7bb25') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - - self.do_test_arm_aspeed(image_path) - - def do_test_arm_aspeed_buildroot_start(self, image, cpu_id, pattern='Aspeed EVB'): - self.require_netdev('user') - - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', - '-net', 'nic', '-net', 'user') - self.vm.launch() - - self.wait_for_console_pattern('U-Boot 2019.04') - self.wait_for_console_pattern('## Loading kernel from FIT Image') - self.wait_for_console_pattern('Starting kernel ...') - self.wait_for_console_pattern('Booting Linux on physical CPU ' + cpu_id) - self.wait_for_console_pattern('lease of 10.0.2.15') - # the line before login: - self.wait_for_console_pattern(pattern) - time.sleep(0.1) - exec_command(self, 'root') - time.sleep(0.1) - - 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.11-2-g15d3648df9/flash.img') - image_hash = ('f96d11db521fe7a2787745e9e391225deeeec3318ee0fc07c8b799b8833dd474') - 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.11-2-g15d3648df9/flash.img') - image_hash = ('e598d86e5ea79671ca8b59212a326c911bc8bea728dec1a1f5390d717a28bb8b') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - - self.vm.add_args('-device', - 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test'); - self.vm.add_args('-device', - 'ds1338,bus=aspeed.i2c.bus.3,address=0x32'); - self.vm.add_args('-device', - 'i2c-echo,bus=aspeed.i2c.bus.3,address=0x42'); - self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00') - - 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); - - exec_command_and_wait_for_pattern(self, - 'echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-3/new_device', - 'i2c i2c-3: new_device: Instantiated device slave-24c02 at 0x64'); - exec_command(self, 'i2cset -y 3 0x42 0x64 0x00 0xaa i'); - time.sleep(0.1) - exec_command_and_wait_for_pattern(self, - 'hexdump /sys/bus/i2c/devices/3-1064/slave-eeprom', - '0000000 ffaa ffff ffff ffff ffff ffff ffff ffff'); - self.do_test_arm_aspeed_buildroot_poweroff() - - @skipUnless(*has_cmd('swtpm')) - def test_arm_ast2600_evb_buildroot_tpm(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:ast2600-evb - """ - - image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' - 'images/ast2600-evb/buildroot-2023.02-tpm/flash.img') - image_hash = ('a46009ae8a5403a0826d607215e731a8c68d27c14c41e55331706b8f9c7bd997') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - - socket = os.path.join(self.vm.sock_dir, 'swtpm-socket') - - subprocess.run(['swtpm', 'socket', '-d', '--tpm2', - '--tpmstate', f'dir={self.vm.temp_dir}', - '--ctrl', f'type=unixio,path={socket}']) - - self.vm.add_args('-chardev', f'socket,id=chrtpm,path={socket}') - self.vm.add_args('-tpmdev', 'emulator,id=tpm0,chardev=chrtpm') - self.vm.add_args('-device', - 'tpm-tis-i2c,tpmdev=tpm0,bus=aspeed.i2c.bus.12,address=0x2e') - self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB') - exec_command(self, "passw0rd") - - exec_command_and_wait_for_pattern(self, - 'echo tpm_tis_i2c 0x2e > /sys/bus/i2c/devices/i2c-12/new_device', - 'tpm_tis_i2c 12-002e: 2.0 TPM (device-id 0x1, rev-id 1)'); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/tpm/tpm0/pcr-sha256/0', - 'B804724EA13F52A9072BA87FE8FDCC497DFC9DF9AA15B9088694639C431688E0'); - - self.do_test_arm_aspeed_buildroot_poweroff() - -class AST2x00MachineSDK(QemuSystemTest, LinuxSSHMixIn): - - EXTRA_BOOTARGS = ( - 'quiet ' - 'systemd.mask=org.openbmc.HostIpmi.service ' - 'systemd.mask=xyz.openbmc_project.Chassis.Control.Power@0.service ' - 'systemd.mask=modprobe@fuse.service ' - 'systemd.mask=rngd.service ' - 'systemd.mask=obmc-console@ttyS2.service ' - ) - - # FIXME: Although these tests boot a whole distro they are still - # slower than comparable machine models. There may be some - # optimisations which bring down the runtime. In the meantime they - # have generous timeouts and are disable for CI which aims for all - # tests to run in less than 60 seconds. - timeout = 240 - - def wait_for_console_pattern(self, success_message, vm=None): - wait_for_console_pattern(self, success_message, - failure_message='Kernel panic - not syncing', - vm=vm) - - def do_test_arm_aspeed_sdk_start(self, image): - self.require_netdev('user') - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', - '-net', 'nic', '-net', 'user,hostfwd=:127.0.0.1:0-:22') - self.vm.launch() - - self.wait_for_console_pattern('U-Boot 2019.04') - interrupt_interactive_console_until_pattern( - self, 'Hit any key to stop autoboot:', 'ast#') - exec_command_and_wait_for_pattern( - self, 'setenv bootargs ${bootargs} ' + self.EXTRA_BOOTARGS, 'ast#') - exec_command_and_wait_for_pattern( - self, 'boot', '## Loading kernel from FIT Image') - self.wait_for_console_pattern('Starting kernel ...') - - @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.06/ast2500-default-obmc.tar.gz') - image_hash = ('e1755f3cadff69190438c688d52dd0f0d399b70a1e14b1d3d5540fc4851d38ca') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - archive.extract(image_path, self.workdir) - - self.do_test_arm_aspeed_sdk_start( - self.workdir + '/ast2500-default/image-bmc') - self.wait_for_console_pattern('nodistro.0 ast2500-default ttyS4') - - @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.06/ast2600-a2-obmc.tar.gz') - image_hash = ('9083506135f622d5e7351fcf7d4e1c7125cee5ba16141220c0ba88931f3681a4') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - archive.extract(image_path, self.workdir) - - self.vm.add_args('-device', - 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test'); - self.vm.add_args('-device', - 'ds1338,bus=aspeed.i2c.bus.5,address=0x32'); - self.do_test_arm_aspeed_sdk_start( - self.workdir + '/ast2600-a2/image-bmc') - self.wait_for_console_pattern('nodistro.0 ast2600-a2 ttyS4') - - self.ssh_connect('root', '0penBmc', False) - self.ssh_command('dmesg -c > /dev/null') - - self.ssh_command_output_contains( - 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-5/device/new_device ; ' - 'dmesg -c', - 'i2c i2c-5: new_device: Instantiated device lm75 at 0x4d'); - self.ssh_command_output_contains( - 'cat /sys/class/hwmon/hwmon19/temp1_input', '0') - self.vm.command('qom-set', path='/machine/peripheral/tmp-test', - property='temperature', value=18000); - self.ssh_command_output_contains( - 'cat /sys/class/hwmon/hwmon19/temp1_input', '18000') - - self.ssh_command_output_contains( - 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-5/device/new_device ; ' - 'dmesg -c', - 'i2c i2c-5: new_device: Instantiated device ds1307 at 0x32'); - year = time.strftime("%Y") - self.ssh_command_output_contains('/sbin/hwclock -f /dev/rtc1', year); diff --git a/tests/avocado/machine_avr6.py b/tests/avocado/machine_avr6.py deleted file mode 100644 index 5485db79c68..00000000000 --- 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_loongarch.py b/tests/avocado/machine_loongarch.py deleted file mode 100644 index 7d8a3c1fa5c..00000000000 --- a/tests/avocado/machine_loongarch.py +++ /dev/null @@ -1,58 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -# -# LoongArch virt test. -# -# Copyright (c) 2023 Loongson Technology Corporation Limited -# - -from avocado_qemu import QemuSystemTest -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import wait_for_console_pattern - -class LoongArchMachine(QemuSystemTest): - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - - timeout = 120 - - def wait_for_console_pattern(self, success_message, vm=None): - wait_for_console_pattern(self, success_message, - failure_message='Kernel panic - not syncing', - vm=vm) - - def test_loongarch64_devices(self): - - """ - :avocado: tags=arch:loongarch64 - :avocado: tags=machine:virt - """ - - kernel_url = ('https://github.com/yangxiaojuan-loongson/qemu-binary/' - 'releases/download/binary-files/vmlinuz.efi') - kernel_hash = '951b485b16e3788b6db03a3e1793c067009e31a2' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - initrd_url = ('https://github.com/yangxiaojuan-loongson/qemu-binary/' - 'releases/download/binary-files/ramdisk') - initrd_hash = 'c67658d9b2a447ce7db2f73ba3d373c9b2b90ab2' - initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - - bios_url = ('https://github.com/yangxiaojuan-loongson/qemu-binary/' - 'releases/download/binary-files/QEMU_EFI.fd') - bios_hash = ('dfc1bfba4853cd763b9d392d0031827e8addbca8') - bios_path = self.fetch_asset(bios_url, asset_hash=bios_hash) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'root=/dev/ram rdinit=/sbin/init console=ttyS0,115200') - self.vm.add_args('-nographic', - '-smp', '4', - '-m', '1024', - '-cpu', 'la464', - '-kernel', kernel_path, - '-initrd', initrd_path, - '-bios', bios_path, - '-append', kernel_command_line) - self.vm.launch() - self.wait_for_console_pattern('Run /sbin/init as init process') - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'processor : 3') diff --git a/tests/avocado/machine_m68k_nextcube.py b/tests/avocado/machine_m68k_nextcube.py deleted file mode 100644 index 6790e7d9cd1..00000000000 --- 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 8d0efff30d2..00000000000 --- 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 89291f47b24..00000000000 --- 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 5194cf18c9c..00000000000 --- 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 92233451c59..00000000000 --- a/tests/avocado/machine_mips_malta.py +++ /dev/null @@ -1,159 +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 skipIf -from avocado import skipUnless -from avocado.utils import archive -from avocado_qemu import QemuSystemTest -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import interrupt_interactive_console_until_pattern -from avocado_qemu import wait_for_console_pattern - - -NUMPY_AVAILABLE = True -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) - -class MaltaMachine(QemuSystemTest): - - def do_test_yamon(self): - rom_url = ('http://www.imgtec.com/tools/mips-tools/downloads/' - 'yamon/yamon-bin-02.22.zip') - rom_hash = '8da7ecddbc5312704b8b324341ee238189bde480' - zip_path = self.fetch_asset(rom_url, asset_hash=rom_hash) - - archive.extract(zip_path, self.workdir) - yamon_path = os.path.join(self.workdir, 'yamon-02.22.bin') - - self.vm.set_console() - self.vm.add_args('-bios', yamon_path) - self.vm.launch() - - prompt = 'YAMON>' - pattern = 'YAMON ROM Monitor' - interrupt_interactive_console_until_pattern(self, pattern, prompt) - wait_for_console_pattern(self, prompt) - self.vm.shutdown() - - def test_mipsel_malta_yamon(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=machine:malta - :avocado: tags=endian:little - """ - self.do_test_yamon() - - def test_mips64el_malta_yamon(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=machine:malta - :avocado: tags=endian:little - """ - self.do_test_yamon() diff --git a/tests/avocado/machine_rx_gdbsim.py b/tests/avocado/machine_rx_gdbsim.py deleted file mode 100644 index 6cd8704b016..00000000000 --- 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 e7a2a20ba67..00000000000 --- a/tests/avocado/machine_s390_ccw_virtio.py +++ /dev/null @@ -1,276 +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') - - - 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. - # Unfortunately, this test is flaky, so we don't run it by default - if os.getenv('QEMU_TEST_FLAKY_TESTS'): - self.log.info("Test screendump of virtio-gpu device") - exec_command_and_wait_for_pattern(self, - 'while ! (dmesg | grep gpudrmfb) ; do sleep 1 ; done', - 'virtio_gpudrmfb frame buffer device') - exec_command_and_wait_for_pattern(self, - 'echo -e "\e[?25l" > /dev/tty0', ':/#') - exec_command_and_wait_for_pattern(self, 'for ((i=0;i<250;i++)); do ' - 'echo " The qu ick fo x j ump s o ver a laz y d og" >> fox.txt;' - 'done', - ':/#') - exec_command_and_wait_for_pattern(self, - 'dd if=fox.txt of=/dev/fb0 bs=1000 oflag=sync,nocache ; rm fox.txt', - '12+0 records out') - with tempfile.NamedTemporaryFile(suffix='.ppm', - prefix='qemu-scrdump-') as ppmfile: - self.vm.command('screendump', filename=ppmfile.name) - ppmfile.seek(0) - line = ppmfile.readline() - self.assertEqual(line, b"P6\n") - line = ppmfile.readline() - self.assertEqual(line, b"1280 800\n") - line = ppmfile.readline() - self.assertEqual(line, b"255\n") - line = ppmfile.readline(256) - self.assertEqual(line, b"The quick fox jumps over a lazy dog\n") - else: - self.log.info("Skipped flaky screendump of virtio-gpu device test") - - # Hot-plug a virtio-crypto device and see whether it gets accepted - self.log.info("Test hot-plug virtio-crypto device") - self.clear_guest_dmesg() - self.vm.command('object-add', qom_type='cryptodev-backend-builtin', - id='cbe0') - self.vm.command('device_add', driver='virtio-crypto-ccw', id='crypdev0', - cryptodev='cbe0', devno='fe.0.2342') - 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 d333c0ae91c..00000000000 --- 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 e61b223185a..00000000000 --- 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 deleted file mode 100644 index fdc1d234fb7..00000000000 --- a/tests/avocado/migration.py +++ /dev/null @@ -1,136 +0,0 @@ -# Migration test -# -# Copyright (c) 2019 Red Hat, Inc. -# -# Authors: -# Cleber Rosa -# Caio Carrara -# -# 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 tempfile -import os - -from avocado_qemu import QemuSystemTest -from avocado import skipUnless - -from avocado.utils.network import ports -from avocado.utils import wait -from avocado.utils.path import find_command - - -class MigrationTest(QemuSystemTest): - """ - :avocado: tags=migration - """ - - timeout = 10 - - @staticmethod - def migration_finished(vm): - return vm.command('query-migrate')['status'] in ('completed', 'failed') - - def assert_migration(self, src_vm, dst_vm): - wait.wait_for(self.migration_finished, - timeout=self.timeout, - step=0.1, - args=(src_vm,)) - wait.wait_for(self.migration_finished, - 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') - - def do_migrate(self, dest_uri, src_uri=None): - dest_vm = self.get_vm('-incoming', dest_uri) - dest_vm.add_args('-nodefaults') - dest_vm.launch() - if src_uri is None: - src_uri = dest_uri - source_vm = self.get_vm() - source_vm.add_args('-nodefaults') - source_vm.launch() - source_vm.qmp('migrate', uri=src_uri) - self.assert_migration(source_vm, dest_vm) - - def _get_free_port(self): - port = ports.find_free_port() - if port is None: - self.cancel('Failed to find a free port') - return port - - def migration_with_tcp_localhost(self): - dest_uri = 'tcp:localhost:%u' % self._get_free_port() - self.do_migrate(dest_uri) - - 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 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 - :avocado: tags=cpu:power9_v2.0 - """ - - 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 9112a4cacc6..00000000000 --- a/tests/avocado/multiprocess.py +++ /dev/null @@ -1,96 +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') - self.require_multiprocess() - - # 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/netdev-ethtool.py b/tests/avocado/netdev-ethtool.py deleted file mode 100644 index 5f33288f811..00000000000 --- a/tests/avocado/netdev-ethtool.py +++ /dev/null @@ -1,101 +0,0 @@ -# ethtool tests for emulated network devices -# -# This test leverages ethtool's --test sequence to validate network -# device behaviour. -# -# SPDX-License-Identifier: GPL-2.0-or-late - -from avocado import skip -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern - -class NetDevEthtool(QemuSystemTest): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:q35 - """ - - # Runs in about 17s under KVM, 19s under TCG, 25s under GCOV - timeout = 45 - - # Fetch assets from the netdev-ethtool subdir of my shared test - # images directory on fileserver.linaro.org. - def get_asset(self, name, sha1): - base_url = ('https://fileserver.linaro.org/s/' - 'kE4nCFLdQcoBF9t/download?' - 'path=%2Fnetdev-ethtool&files=' ) - url = base_url + name - # use explicit name rather than failing to neatly parse the - # URL into a unique one - return self.fetch_asset(name=name, locations=(url), asset_hash=sha1) - - def common_test_code(self, netdev, extra_args=None): - - # This custom kernel has drivers for all the supported network - # devices we can emulate in QEMU - kernel = self.get_asset("bzImage", - "33469d7802732d5815226166581442395cb289e2") - - rootfs = self.get_asset("rootfs.squashfs", - "9793cea7021414ae844bda51f558bd6565b50cdc") - - append = 'printk.time=0 console=ttyS0 ' - append += 'root=/dev/sr0 rootfstype=squashfs ' - - # any additional kernel tweaks for the test - if extra_args: - append += extra_args - - # finally invoke ethtool directly - append += ' init=/usr/sbin/ethtool -- -t eth1 offline' - - # add the rootfs via a readonly cdrom image - drive = f"file={rootfs},if=ide,index=0,media=cdrom" - - self.vm.add_args('-kernel', kernel, - '-append', append, - '-drive', drive, - '-device', netdev) - - self.vm.set_console(console_index=0) - self.vm.launch() - - wait_for_console_pattern(self, - "The test result is PASS", - "The test result is FAIL", - vm=None) - # no need to gracefully shutdown, just finish - self.vm.kill() - - def test_igb(self): - """ - :avocado: tags=device:igb - """ - self.common_test_code("igb") - - def test_igb_nomsi(self): - """ - :avocado: tags=device:igb - """ - self.common_test_code("igb", "pci=nomsi") - - # It seems the other popular cards we model in QEMU currently fail - # the pattern test with: - # - # pattern test failed (reg 0x00178): got 0x00000000 expected 0x00005A5A - # - # So for now we skip them. - - @skip("Incomplete reg 0x00178 support") - def test_e1000(self): - """ - :avocado: tags=device:e1000 - """ - self.common_test_code("e1000") - - @skip("Incomplete reg 0x00178 support") - def test_i82550(self): - """ - :avocado: tags=device:i82550 - """ - self.common_test_code("i82550") diff --git a/tests/avocado/pc_cpu_hotplug_props.py b/tests/avocado/pc_cpu_hotplug_props.py deleted file mode 100644 index 52b878188ed..00000000000 --- 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 4e7e01aa762..00000000000 --- 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 f54757c243f..00000000000 --- 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 a81be3d6088..00000000000 --- 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 b599fb1cc9b..00000000000 --- 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_powernv.py b/tests/avocado/ppc_powernv.py deleted file mode 100644 index d0e5c07bde1..00000000000 --- a/tests/avocado/ppc_powernv.py +++ /dev/null @@ -1,87 +0,0 @@ -# Test that Linux kernel boots on ppc powernv machines and check the console -# -# Copyright (c) 2018, 2020 Red Hat, Inc. -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -from avocado.utils import archive -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern - -class powernvMachine(QemuSystemTest): - - timeout = 90 - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - panic_message = 'Kernel panic - not syncing' - good_message = 'VFS: Cannot open root device' - - def do_test_linux_boot(self): - self.require_accelerator("tcg") - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/29/Everything/ppc64le/os' - '/ppc/ppc64/vmlinuz') - kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0' - self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - - def test_linux_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - :avocado: tags=accel:tcg - """ - - self.do_test_linux_boot() - console_pattern = 'VFS: Cannot open root device' - wait_for_console_pattern(self, console_pattern, self.panic_message) - - def test_linux_smp_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - :avocado: tags=accel:tcg - """ - - self.vm.add_args('-smp', '4') - self.do_test_linux_boot() - console_pattern = 'smp: Brought up 1 node, 4 CPUs' - wait_for_console_pattern(self, console_pattern, self.panic_message) - wait_for_console_pattern(self, self.good_message, self.panic_message) - - def test_linux_smt_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - :avocado: tags=accel:tcg - """ - - self.vm.add_args('-smp', '4,threads=4') - self.do_test_linux_boot() - console_pattern = 'CPU maps initialized for 4 threads per core' - wait_for_console_pattern(self, console_pattern, self.panic_message) - console_pattern = 'smp: Brought up 1 node, 4 CPUs' - wait_for_console_pattern(self, console_pattern, self.panic_message) - wait_for_console_pattern(self, self.good_message, self.panic_message) - - def test_linux_big_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - :avocado: tags=accel:tcg - """ - - self.vm.add_args('-smp', '16,threads=4,cores=2,sockets=2') - - # powernv does not support NUMA - self.do_test_linux_boot() - console_pattern = 'CPU maps initialized for 4 threads per core' - wait_for_console_pattern(self, console_pattern, self.panic_message) - console_pattern = 'smp: Brought up 2 nodes, 16 CPUs' - wait_for_console_pattern(self, console_pattern, self.panic_message) - wait_for_console_pattern(self, self.good_message, self.panic_message) diff --git a/tests/avocado/ppc_prep_40p.py b/tests/avocado/ppc_prep_40p.py deleted file mode 100644 index d4f1eb7e1d8..00000000000 --- 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 a8311e65554..00000000000 --- a/tests/avocado/ppc_pseries.py +++ /dev/null @@ -1,96 +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' - good_message = 'VFS: Cannot open root device' - - def do_test_ppc64_linux_boot(self): - 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() - - def test_ppc64_vof_linux_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ - - self.vm.add_args('-machine', 'x-vof=on') - self.do_test_ppc64_linux_boot() - console_pattern = 'VFS: Cannot open root device' - wait_for_console_pattern(self, console_pattern, self.panic_message) - - def test_ppc64_linux_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ - - self.do_test_ppc64_linux_boot() - console_pattern = 'VFS: Cannot open root device' - wait_for_console_pattern(self, console_pattern, self.panic_message) - - def test_ppc64_linux_smp_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ - - self.vm.add_args('-smp', '4') - self.do_test_ppc64_linux_boot() - console_pattern = 'smp: Brought up 1 node, 4 CPUs' - wait_for_console_pattern(self, console_pattern, self.panic_message) - wait_for_console_pattern(self, self.good_message, self.panic_message) - - def test_ppc64_linux_smt_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ - - self.vm.add_args('-smp', '4,threads=4') - self.do_test_ppc64_linux_boot() - console_pattern = 'CPU maps initialized for 4 threads per core' - wait_for_console_pattern(self, console_pattern, self.panic_message) - console_pattern = 'smp: Brought up 1 node, 4 CPUs' - wait_for_console_pattern(self, console_pattern, self.panic_message) - wait_for_console_pattern(self, self.good_message, self.panic_message) - - def test_ppc64_linux_big_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ - - self.vm.add_args('-smp', '16,threads=4,cores=2,sockets=2') - self.vm.add_args('-m', '512M', - '-object', 'memory-backend-ram,size=256M,id=m0', - '-object', 'memory-backend-ram,size=256M,id=m1') - self.vm.add_args('-numa', 'node,nodeid=0,memdev=m0') - self.vm.add_args('-numa', 'node,nodeid=1,memdev=m1') - self.do_test_ppc64_linux_boot() - console_pattern = 'CPU maps initialized for 4 threads per core' - wait_for_console_pattern(self, console_pattern, self.panic_message) - console_pattern = 'smp: Brought up 2 nodes, 16 CPUs' - wait_for_console_pattern(self, console_pattern, self.panic_message) - wait_for_console_pattern(self, self.good_message, self.panic_message) diff --git a/tests/avocado/ppc_virtex_ml507.py b/tests/avocado/ppc_virtex_ml507.py deleted file mode 100644 index a73f8ae396a..00000000000 --- 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 deleted file mode 100644 index a18610542ea..00000000000 --- a/tests/avocado/replay_kernel.py +++ /dev/null @@ -1,545 +0,0 @@ -# Record/replay test that boots a Linux kernel -# -# Copyright (c) 2020 ISP RAS -# -# Author: -# Pavel Dovgalyuk -# -# 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 lzma -import shutil -import logging -import time - -from avocado import skip -from avocado import skipIf -from avocado import skipUnless -from avocado_qemu import wait_for_console_pattern -from avocado.utils import archive -from avocado.utils import process -from boot_linux_console import LinuxKernelTest - -class ReplayKernelBase(LinuxKernelTest): - """ - Boots a Linux kernel in record mode and checks that the console - is operational and the kernel command line is properly passed - from QEMU to the kernel. - Then replays the same scenario and verifies, that QEMU correctly - terminates. - """ - - timeout = 120 - KERNEL_COMMON_COMMAND_LINE = 'printk.time=1 panic=-1 ' - - def run_vm(self, kernel_path, kernel_command_line, console_pattern, - record, shift, args, replay_path): - # icount requires TCG to be available - self.require_accelerator('tcg') - - logger = logging.getLogger('replay') - start_time = time.time() - vm = self.get_vm() - vm.set_console() - if record: - logger.info('recording the execution...') - mode = 'record' - else: - logger.info('replaying the execution...') - mode = 'replay' - vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s' % - (shift, mode, replay_path), - '-kernel', kernel_path, - '-append', kernel_command_line, - '-net', 'none', - '-no-reboot') - if args: - vm.add_args(*args) - vm.launch() - self.wait_for_console_pattern(console_pattern, vm) - if record: - vm.shutdown() - logger.info('finished the recording with log size %s bytes' - % os.path.getsize(replay_path)) - else: - vm.wait() - logger.info('successfully finished the replay') - elapsed = time.time() - start_time - logger.info('elapsed time %.2f sec' % elapsed) - return elapsed - - def run_rr(self, kernel_path, kernel_command_line, console_pattern, - shift=7, args=None): - replay_path = os.path.join(self.workdir, 'replay.bin') - t1 = self.run_vm(kernel_path, kernel_command_line, console_pattern, - True, shift, args, replay_path) - t2 = self.run_vm(kernel_path, kernel_command_line, console_pattern, - False, shift, args, replay_path) - logger = logging.getLogger('replay') - logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) - -class ReplayKernelNormal(ReplayKernelBase): - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') - def test_x86_64_pc(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:pc - """ - 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_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') - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'Kernel command line: %s' % kernel_command_line - - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - - 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') - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - - def test_aarch64_virt(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:cortex-a53 - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/aarch64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '8c73e469fc6ea06a58dc83a628fc695b693b8493' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - console_pattern = 'VFS: Cannot open root device' - - self.run_rr(kernel_path, kernel_command_line, console_pattern) - - def test_arm_virt(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:virt - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/armhfp/os/images/pxeboot' - '/vmlinuz') - kernel_hash = 'e9826d741b4fb04cadba8d4824d1ed3b7fb8b4d4' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - console_pattern = 'VFS: Cannot open root device' - - 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' - 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' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv5.cpio.gz') - initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'usbcore.nousb ' - 'panic=-1 noreboot') - console_pattern = 'Boot successful.' - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1, - args=('-dtb', dtb_path, - '-initrd', initrd_path, - '-no-reboot')) - - 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) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=sclp0' - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=9) - - 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) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.run_rr(uncompressed_kernel, kernel_command_line, console_pattern, shift=9, - args=('-nodefaults', )) - - def test_ppc64_pseries(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - :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=hvc0' - 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): - """ - :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') - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0 vga=off') - console_pattern = 'No filesystem could mount root' - self.run_rr(kernel_path, kernel_command_line, console_pattern) - - def do_test_advcal_2018(self, file_path, kernel_name, args=None): - archive.extract(file_path, self.workdir) - - for entry in os.scandir(self.workdir): - if entry.name.startswith('day') and entry.is_dir(): - kernel_path = os.path.join(entry.path, kernel_name) - break - - kernel_command_line = '' - console_pattern = 'QEMU advent calendar' - self.run_rr(kernel_path, kernel_command_line, console_pattern, - args=args) - - def test_arm_vexpressa9(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:vexpress-a9 - """ - tar_hash = '32b7677ce8b6f1471fb0059865f451169934245b' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day16.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - dtb_path = self.workdir + '/day16/vexpress-v2p-ca9.dtb' - self.do_test_advcal_2018(file_path, 'winter.zImage', - args=('-dtb', dtb_path)) - - def test_m68k_mcf5208evb(self): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:mcf5208evb - """ - tar_hash = 'ac688fd00561a2b6ce1359f9ff6aa2b98c9a570c' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day07.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'sanity-clause.elf') - - @skip("Test currently broken") # Console stuck as of 5.2-rc1 - def test_microblaze_s3adsp1800(self): - """ - :avocado: tags=arch:microblaze - :avocado: tags=machine:petalogix-s3adsp1800 - """ - tar_hash = '08bf3e3bfb6b6c7ce1e54ab65d54e189f2caf13f' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day17.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'ballerina.bin') - - def test_ppc64_e500(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:ppce500 - :avocado: tags=cpu:e5500 - """ - tar_hash = '6951d86d644b302898da2fd701739c9406527fe1' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day19.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'uImage') - - def test_or1k_sim(self): - """ - :avocado: tags=arch:or1k - :avocado: tags=machine:or1k-sim - """ - tar_hash = '20334cdaf386108c530ff0badaecc955693027dd' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day20.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'vmlinux') - - @skip("nios2 emulation is buggy under record/replay") - 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 - :avocado: tags=machine:g3beige - """ - tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day15.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'invaders.elf', - args=('-M', 'graphics=off')) - - def test_ppc_mac99(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:mac99 - """ - tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day15.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'invaders.elf', - args=('-M', 'graphics=off')) - - def test_sparc_ss20(self): - """ - :avocado: tags=arch:sparc - :avocado: tags=machine:SS-20 - """ - tar_hash = 'b18550d5d61c7615d989a06edace051017726a9f' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day11.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'zImage.elf') - - def test_xtensa_lx60(self): - """ - :avocado: tags=arch:xtensa - :avocado: tags=machine:lx60 - :avocado: tags=cpu:dc233c - """ - tar_hash = '49e88d9933742f0164b60839886c9739cb7a0d34' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day02.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'santas-sleigh-ride.elf') - -@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') -class ReplayKernelSlow(ReplayKernelBase): - # Override the timeout, because this kernel includes an inner - # loop which is executed with TB recompilings during replay, - # making it very slow. - timeout = 180 - - def test_mips_malta_cpio(self): - """ - :avocado: tags=arch:mips - :avocado: tags=machine:malta - :avocado: tags=endian:big - :avocado: tags=slowness:high - """ - 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) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0 console=tty ' - 'rdinit=/sbin/init noreboot') - console_pattern = 'Boot successful.' - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, - args=('-initrd', initrd_path)) - - @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=slowness:high - :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) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0 console=tty ' - 'rdinit=/sbin/init noreboot') - console_pattern = 'Boot successful.' - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, - args=('-initrd', initrd_path)) - - def do_test_mips_malta32el_nanomips(self, kernel_path_xz): - 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) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'mem=256m@@0x0 ' - 'console=ttyS0') - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - - 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 = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' - 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' - 'generic_nano32r6el_page4k.xz') - kernel_hash = '477456aafd2a0f1ddc9482727f20fe9575565dd6' - kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.do_test_mips_malta32el_nanomips(kernel_path_xz) - - 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 = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' - 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' - 'generic_nano32r6el_page16k_up.xz') - kernel_hash = 'e882868f944c71c816e832e2303b7874d044a7bc' - kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.do_test_mips_malta32el_nanomips(kernel_path_xz) - - 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 = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' - 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' - 'generic_nano32r6el_page64k_dbg.xz') - kernel_hash = '18d1c68f2e23429e266ca39ba5349ccd0aeb7180' - kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.do_test_mips_malta32el_nanomips(kernel_path_xz) diff --git a/tests/avocado/replay_linux.py b/tests/avocado/replay_linux.py deleted file mode 100644 index a76dd507fc1..00000000000 --- a/tests/avocado/replay_linux.py +++ /dev/null @@ -1,193 +0,0 @@ -# Record/replay test that boots a complete Linux system via a cloud image -# -# Copyright (c) 2020 ISP RAS -# -# Author: -# Pavel Dovgalyuk -# -# 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 time - -from avocado import skipUnless -from avocado_qemu import BUILD_DIR -from avocado.utils import cloudinit -from avocado.utils import network -from avocado.utils import vmimage -from avocado.utils import datadrainer -from avocado.utils.path import find_command -from avocado_qemu import LinuxTest - -class ReplayLinux(LinuxTest): - """ - Boots a Linux system, checking for a successful initialization - """ - - timeout = 1800 - chksum = None - hdd = 'ide-hd' - cd = 'ide-cd' - bus = 'ide' - - def setUp(self): - # LinuxTest does many replay-incompatible things, but includes - # useful methods. Do not setup LinuxTest here and just - # call some functions. - super(LinuxTest, self).setUp() - self._set_distro() - self.boot_path = self.download_boot() - self.phone_server = cloudinit.PhoneHomeServer(('0.0.0.0', 0), - self.name) - ssh_pubkey, self.ssh_key = self.set_up_existing_ssh_keys() - self.cloudinit_path = self.prepare_cloudinit(ssh_pubkey) - - def vm_add_disk(self, vm, path, id, device): - 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', - '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 launch_and_wait(self, record, args, shift): - self.require_netdev('user') - vm = self.get_vm() - vm.add_args('-smp', '1') - vm.add_args('-m', '1024') - vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', - '-device', 'virtio-net,netdev=vnet') - vm.add_args('-object', 'filter-replay,id=replay,netdev=vnet') - 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) - logger = logging.getLogger('replay') - if record: - logger.info('recording the execution...') - mode = 'record' - else: - logger.info('replaying the execution...') - mode = 'replay' - replay_path = os.path.join(self.workdir, 'replay.bin') - vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s' % - (shift, mode, replay_path)) - - start_time = time.time() - - vm.set_console() - 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() - if record: - while not self.phone_server.instance_phoned_back: - self.phone_server.handle_request() - vm.shutdown() - logger.info('finished the recording with log size %s bytes' - % os.path.getsize(replay_path)) - else: - vm.event_wait('SHUTDOWN', self.timeout) - vm.shutdown(True) - logger.info('successfully fihished the replay') - elapsed = time.time() - start_time - logger.info('elapsed time %.2f sec' % elapsed) - return elapsed - - def run_rr(self, args=None, shift=7): - t1 = self.launch_and_wait(True, args, shift) - t2 = self.launch_and_wait(False, args, shift) - logger = logging.getLogger('replay') - logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) - -@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') -class ReplayLinuxX8664(ReplayLinux): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=accel:tcg - """ - - chksum = 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0' - - def test_pc_i440fx(self): - """ - :avocado: tags=machine:pc - """ - self.run_rr(shift=1) - - def test_pc_q35(self): - """ - :avocado: tags=machine:q35 - """ - self.run_rr(shift=3) - -@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') -class ReplayLinuxX8664Virtio(ReplayLinux): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=virtio - :avocado: tags=accel:tcg - """ - - hdd = 'virtio-blk-pci' - cd = 'virtio-blk-pci' - bus = None - - chksum = 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0' - - def test_pc_i440fx(self): - """ - :avocado: tags=machine:pc - """ - self.run_rr(shift=1) - - def test_pc_q35(self): - """ - :avocado: tags=machine:q35 - """ - self.run_rr(shift=3) - -@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') -class ReplayLinuxAarch64(ReplayLinux): - """ - :avocado: tags=accel:tcg - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:max - """ - - chksum = '1e18d9c0cf734940c4b5d5ec592facaed2af0ad0329383d5639c997fdf16fe49' - - hdd = 'virtio-blk-device' - cd = 'virtio-blk-device' - bus = None - - def get_common_args(self): - return ('-bios', - os.path.join(BUILD_DIR, 'pc-bios', 'edk2-aarch64-code.fd'), - "-cpu", "max,lpa2=off", - '-device', 'virtio-rng-pci,rng=rng0', - '-object', 'rng-builtin,id=rng0') - - def test_virt_gicv2(self): - """ - :avocado: tags=machine:gic-version=2 - """ - - self.run_rr(shift=3, - args=(*self.get_common_args(), - "-machine", "virt,gic-version=2")) - - def test_virt_gicv3(self): - """ - :avocado: tags=machine:gic-version=3 - """ - - self.run_rr(shift=3, - args=(*self.get_common_args(), - "-machine", "virt,gic-version=3")) diff --git a/tests/avocado/reverse_debugging.py b/tests/avocado/reverse_debugging.py deleted file mode 100644 index fc47874edaa..00000000000 --- a/tests/avocado/reverse_debugging.py +++ /dev/null @@ -1,264 +0,0 @@ -# Reverse debugging test -# -# Copyright (c) 2020 ISP RAS -# -# Author: -# Pavel Dovgalyuk -# -# 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 skipIf -from avocado_qemu import BUILD_DIR -from avocado.utils import gdb -from avocado.utils import process -from avocado.utils.network.ports import find_free_port -from avocado.utils.path import find_command -from boot_linux_console import LinuxKernelTest - -class ReverseDebugging(LinuxKernelTest): - """ - Test GDB reverse debugging commands: reverse step and reverse continue. - Recording saves the execution of some instructions and makes an initial - VM snapshot to allow reverse execution. - Replay saves the order of the first instructions and then checks that they - are executed backwards in the correct order. - After that the execution is replayed to the end, and reverse continue - command is checked by setting several breakpoints, and asserting - that the execution is stopped at the last of them. - """ - - timeout = 10 - STEPS = 10 - endian_is_le = True - - def run_vm(self, record, shift, args, replay_path, image_path, port): - logger = logging.getLogger('replay') - vm = self.get_vm() - vm.set_console() - if record: - logger.info('recording the execution...') - mode = 'record' - else: - logger.info('replaying the execution...') - mode = 'replay' - vm.add_args('-gdb', 'tcp::%d' % port, '-S') - vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s,rrsnapshot=init' % - (shift, mode, replay_path), - '-net', 'none') - vm.add_args('-drive', 'file=%s,if=none' % image_path) - if args: - vm.add_args(*args) - vm.launch() - return vm - - @staticmethod - def get_reg_le(g, reg): - res = g.cmd(b'p%x' % reg) - num = 0 - for i in range(len(res))[-2::-2]: - num = 0x100 * num + int(res[i:i + 2], 16) - return num - - @staticmethod - def get_reg_be(g, reg): - res = g.cmd(b'p%x' % reg) - return int(res, 16) - - def get_reg(self, g, reg): - # value may be encoded in BE or LE order - if self.endian_is_le: - return self.get_reg_le(g, reg) - else: - return self.get_reg_be(g, reg) - - def get_pc(self, g): - return self.get_reg(g, self.REG_PC) - - def check_pc(self, g, addr): - pc = self.get_pc(g) - if pc != addr: - self.fail('Invalid PC (read %x instead of %x)' % (pc, addr)) - - @staticmethod - def gdb_step(g): - g.cmd(b's', b'T05thread:01;') - - @staticmethod - def gdb_bstep(g): - g.cmd(b'bs', b'T05thread:01;') - - @staticmethod - def vm_get_icount(vm): - return vm.qmp('query-replay')['return']['icount'] - - def reverse_debugging(self, shift=7, args=None): - logger = logging.getLogger('replay') - - # create qcow2 for snapshots - logger.info('creating qcow2 image for VM snapshots') - image_path = os.path.join(self.workdir, 'disk.qcow2') - qemu_img = os.path.join(BUILD_DIR, 'qemu-img') - if not os.path.exists(qemu_img): - qemu_img = find_command('qemu-img', False) - if qemu_img is False: - self.cancel('Could not find "qemu-img", which is required to ' - 'create the temporary qcow2 image') - cmd = '%s create -f qcow2 %s 128M' % (qemu_img, image_path) - process.run(cmd) - - replay_path = os.path.join(self.workdir, 'replay.bin') - port = find_free_port() - - # record the log - vm = self.run_vm(True, shift, args, replay_path, image_path, port) - while self.vm_get_icount(vm) <= self.STEPS: - pass - last_icount = self.vm_get_icount(vm) - vm.shutdown() - - logger.info("recorded log with %s+ steps" % last_icount) - - # replay and run debug commands - vm = self.run_vm(False, shift, args, replay_path, image_path, port) - logger.info('connecting to gdbstub') - g = gdb.GDBRemote('127.0.0.1', port, False, False) - g.connect() - r = g.cmd(b'qSupported') - if b'qXfer:features:read+' in r: - g.cmd(b'qXfer:features:read:target.xml:0,ffb') - if b'ReverseStep+' not in r: - self.fail('Reverse step is not supported by QEMU') - if b'ReverseContinue+' not in r: - self.fail('Reverse continue is not supported by QEMU') - - logger.info('stepping forward') - steps = [] - # record first instruction addresses - for _ in range(self.STEPS): - pc = self.get_pc(g) - logger.info('saving position %x' % pc) - steps.append(pc) - self.gdb_step(g) - - # visit the recorded instruction in reverse order - logger.info('stepping backward') - for addr in steps[::-1]: - self.gdb_bstep(g) - self.check_pc(g, addr) - logger.info('found position %x' % addr) - - # 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;') - - # assume that none of the first instructions is executed again - # breaking the order of the breakpoints - self.check_pc(g, steps[-1]) - logger.info('successfully reached %x' % steps[-1]) - - logger.info('exitting 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): - return self.get_reg_le(g, self.REG_PC) \ - + self.get_reg_le(g, self.REG_CS) * 0x10 - - # unidentified gitlab timeout problem - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') - def test_x86_64_pc(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:pc - """ - # start with BIOS only - 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') - def test_aarch64_virt(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:cortex-a53 - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/aarch64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '8c73e469fc6ea06a58dc83a628fc695b693b8493' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.reverse_debugging( - args=('-kernel', kernel_path)) - -class ReverseDebugging_ppc64(ReverseDebugging): - """ - :avocado: tags=accel:tcg - """ - - REG_PC = 0x40 - - # unidentified gitlab timeout problem - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') - def test_ppc64_pseries(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ - # 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() - - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') - def test_ppc64_powernv(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - """ - self.endian_is_le = False - self.reverse_debugging() diff --git a/tests/avocado/riscv_opensbi.py b/tests/avocado/riscv_opensbi.py deleted file mode 100644 index bfff9cc3c33..00000000000 --- a/tests/avocado/riscv_opensbi.py +++ /dev/null @@ -1,63 +0,0 @@ -# OpenSBI boot test for RISC-V machines -# -# Copyright (c) 2022, Ventana Micro -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern - -class RiscvOpenSBI(QemuSystemTest): - """ - :avocado: tags=accel:tcg - """ - timeout = 5 - - def boot_opensbi(self): - self.vm.set_console() - self.vm.launch() - wait_for_console_pattern(self, 'Platform Name') - wait_for_console_pattern(self, 'Boot HART MEDELEG') - - def test_riscv32_spike(self): - """ - :avocado: tags=arch:riscv32 - :avocado: tags=machine:spike - """ - self.boot_opensbi() - - def test_riscv64_spike(self): - """ - :avocado: tags=arch:riscv64 - :avocado: tags=machine:spike - """ - self.boot_opensbi() - - def test_riscv32_sifive_u(self): - """ - :avocado: tags=arch:riscv32 - :avocado: tags=machine:sifive_u - """ - self.boot_opensbi() - - def test_riscv64_sifive_u(self): - """ - :avocado: tags=arch:riscv64 - :avocado: tags=machine:sifive_u - """ - self.boot_opensbi() - - def test_riscv32_virt(self): - """ - :avocado: tags=arch:riscv32 - :avocado: tags=machine:virt - """ - self.boot_opensbi() - - def test_riscv64_virt(self): - """ - :avocado: tags=arch:riscv64 - :avocado: tags=machine:virt - """ - self.boot_opensbi() diff --git a/tests/avocado/smmu.py b/tests/avocado/smmu.py deleted file mode 100644 index b3c4de6bf4a..00000000000 --- a/tests/avocado/smmu.py +++ /dev/null @@ -1,137 +0,0 @@ -# SMMUv3 Functional tests -# -# Copyright (c) 2021 Red Hat, Inc. -# -# Author: -# Eric Auger -# -# 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 LinuxTest, BUILD_DIR - -@skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') -class SMMU(LinuxTest): - """ - :avocado: tags=accel:kvm - :avocado: tags=cpu:host - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=distro:fedora - :avocado: tags=smmu - """ - - IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on' - kernel_path = None - initrd_path = None - kernel_params = None - - def set_up_boot(self): - path = self.download_boot() - self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,scsi=off,' + - 'drive=drv0,id=virtio-disk0,bootindex=1,' - 'werror=stop,rerror=stop' + self.IOMMU_ADDON) - self.vm.add_args('-drive', - 'file=%s,if=none,cache=writethrough,id=drv0' % path) - - def setUp(self): - super(SMMU, self).setUp(None, 'virtio-net-pci' + self.IOMMU_ADDON) - - def common_vm_setup(self, custom_kernel=False): - self.require_accelerator("kvm") - self.vm.add_args("-accel", "kvm") - self.vm.add_args("-cpu", "host") - self.vm.add_args("-machine", "iommu=smmuv3") - self.vm.add_args("-d", "guest_errors") - 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') - - if custom_kernel is False: - return - - kernel_url = self.distro.pxeboot_url + 'vmlinuz' - initrd_url = self.distro.pxeboot_url + 'initrd.img' - self.kernel_path = self.fetch_asset(kernel_url) - self.initrd_path = self.fetch_asset(initrd_url) - - def run_and_check(self): - if self.kernel_path: - self.vm.add_args('-kernel', self.kernel_path, - '-append', self.kernel_params, - '-initrd', self.initrd_path) - self.launch_and_wait() - self.ssh_command('cat /proc/cmdline') - self.ssh_command('dnf -y install numactl-devel') - - - # 5.3 kernel without RIL # - - def test_smmu_noril(self): - """ - :avocado: tags=smmu_noril - :avocado: tags=smmu_noril_tests - :avocado: tags=distro_version:31 - """ - self.common_vm_setup() - self.run_and_check() - - def test_smmu_noril_passthrough(self): - """ - :avocado: tags=smmu_noril_passthrough - :avocado: tags=smmu_noril_tests - :avocado: tags=distro_version:31 - """ - self.common_vm_setup(True) - self.kernel_params = (self.distro.default_kernel_params + - ' iommu.passthrough=on') - self.run_and_check() - - def test_smmu_noril_nostrict(self): - """ - :avocado: tags=smmu_noril_nostrict - :avocado: tags=smmu_noril_tests - :avocado: tags=distro_version:31 - """ - self.common_vm_setup(True) - self.kernel_params = (self.distro.default_kernel_params + - ' iommu.strict=0') - self.run_and_check() - - # 5.8 kernel featuring range invalidation - # >= v5.7 kernel - - def test_smmu_ril(self): - """ - :avocado: tags=smmu_ril - :avocado: tags=smmu_ril_tests - :avocado: tags=distro_version:33 - """ - self.common_vm_setup() - self.run_and_check() - - def test_smmu_ril_passthrough(self): - """ - :avocado: tags=smmu_ril_passthrough - :avocado: tags=smmu_ril_tests - :avocado: tags=distro_version:33 - """ - self.common_vm_setup(True) - self.kernel_params = (self.distro.default_kernel_params + - ' iommu.passthrough=on') - self.run_and_check() - - def test_smmu_ril_nostrict(self): - """ - :avocado: tags=smmu_ril_nostrict - :avocado: tags=smmu_ril_tests - :avocado: tags=distro_version:33 - """ - self.common_vm_setup(True) - self.kernel_params = (self.distro.default_kernel_params + - ' iommu.strict=0') - self.run_and_check() diff --git a/tests/avocado/tcg_plugins.py b/tests/avocado/tcg_plugins.py deleted file mode 100644 index 642d2e49e30..00000000000 --- 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 72cd9ab7989..00000000000 --- 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/tuxrun_baselines.py b/tests/avocado/tuxrun_baselines.py deleted file mode 100644 index e12250eabb8..00000000000 --- a/tests/avocado/tuxrun_baselines.py +++ /dev/null @@ -1,587 +0,0 @@ -# Functional test that boots known good tuxboot images the same way -# that tuxrun (www.tuxrun.org) does. This tool is used by things like -# the LKFT project to run regression tests on kernels. -# -# Copyright (c) 2023 Linaro Ltd. -# -# Author: -# Alex Bennée -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import os -import time -import tempfile - -from avocado import skip, skipIf -from avocado_qemu import QemuSystemTest -from avocado_qemu import exec_command, exec_command_and_wait_for_pattern -from avocado_qemu import wait_for_console_pattern -from avocado.utils import process -from avocado.utils.path import find_command - -class TuxRunBaselineTest(QemuSystemTest): - """ - :avocado: tags=accel:tcg - """ - - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0' - # Tests are ~10-40s, allow for --debug/--enable-gcov overhead - timeout = 100 - - def get_tag(self, tagname, default=None): - """ - Get the metadata tag or return the default. - """ - utag = self._get_unique_tag_val(tagname) - print(f"{tagname}/{default} -> {utag}") - if utag: - return utag - - return default - - def setUp(self): - super().setUp() - - # We need zstd for all the tuxrun tests - # See https://github.com/avocado-framework/avocado/issues/5609 - zstd = find_command('zstd', False) - if zstd is False: - self.cancel('Could not find "zstd", which is required to ' - 'decompress rootfs') - self.zstd = zstd - - # Process the TuxRun specific tags, most machines work with - # reasonable defaults but we sometimes need to tweak the - # config. To avoid open coding everything we store all these - # details in the metadata for each test. - - # The tuxboot tag matches the root directory - self.tuxboot = self.get_tag('tuxboot') - - # Most Linux's use ttyS0 for their serial port - self.console = self.get_tag('console', "ttyS0") - - # Does the machine shutdown QEMU nicely on "halt" - self.shutdown = self.get_tag('shutdown') - - # The name of the kernel Image file - self.image = self.get_tag('image', "Image") - - self.root = self.get_tag('root', "vda") - - # Occasionally we need extra devices to hook things up - self.extradev = self.get_tag('extradev') - - self.qemu_img = super().get_qemu_img() - - def wait_for_console_pattern(self, success_message, vm=None): - wait_for_console_pattern(self, success_message, - failure_message='Kernel panic - not syncing', - vm=vm) - - def fetch_tuxrun_assets(self, csums=None, dt=None): - """ - Fetch the TuxBoot assets. They are stored in a standard way so we - use the per-test tags to fetch details. - """ - base_url = f"https://storage.tuxboot.com/20230331/{self.tuxboot}/" - - # empty hash if we weren't passed one - csums = {} if csums is None else csums - ksum = csums.get(self.image, None) - isum = csums.get("rootfs.ext4.zst", None) - - kernel_image = self.fetch_asset(base_url + self.image, - asset_hash = ksum, - algorithm = "sha256") - disk_image_zst = self.fetch_asset(base_url + "rootfs.ext4.zst", - asset_hash = isum, - algorithm = "sha256") - - cmd = f"{self.zstd} -d {disk_image_zst} -o {self.workdir}/rootfs.ext4" - process.run(cmd) - - if dt: - dsum = csums.get(dt, None) - dtb = self.fetch_asset(base_url + dt, - asset_hash = dsum, - algorithm = "sha256") - else: - dtb = None - - return (kernel_image, self.workdir + "/rootfs.ext4", dtb) - - def prepare_run(self, kernel, disk, drive, dtb=None, console_index=0): - """ - Setup to run and add the common parameters to the system - """ - self.vm.set_console(console_index=console_index) - - # all block devices are raw ext4's - blockdev = "driver=raw,file.driver=file," \ - + f"file.filename={disk},node-name=hd0" - - kcmd_line = self.KERNEL_COMMON_COMMAND_LINE - kcmd_line += f" root=/dev/{self.root}" - kcmd_line += f" console={self.console}" - - self.vm.add_args('-kernel', kernel, - '-append', kcmd_line, - '-blockdev', blockdev) - - # Sometimes we need extra devices attached - if self.extradev: - self.vm.add_args('-device', self.extradev) - - self.vm.add_args('-device', - f"{drive},drive=hd0") - - # Some machines need an explicit DTB - if dtb: - self.vm.add_args('-dtb', dtb) - - def run_tuxtest_tests(self, haltmsg): - """ - Wait for the system to boot up, wait for the login prompt and - then do a few things on the console. Trigger a shutdown and - wait to exit cleanly. - """ - self.wait_for_console_pattern("Welcome to TuxTest") - time.sleep(0.2) - exec_command(self, 'root') - time.sleep(0.2) - exec_command(self, 'cat /proc/interrupts') - time.sleep(0.1) - exec_command(self, 'cat /proc/self/maps') - time.sleep(0.1) - exec_command(self, 'uname -a') - time.sleep(0.1) - exec_command_and_wait_for_pattern(self, 'halt', haltmsg) - - # Wait for VM to shut down gracefully if it can - if self.shutdown == "nowait": - self.vm.shutdown() - else: - self.vm.wait() - - def common_tuxrun(self, - csums=None, - dt=None, - drive="virtio-blk-device", - haltmsg="reboot: System halted", - console_index=0): - """ - Common path for LKFT tests. Unless we need to do something - special with the command line we can process most things using - the tag metadata. - """ - (kernel, disk, dtb) = self.fetch_tuxrun_assets(csums, dt) - - self.prepare_run(kernel, disk, drive, dtb, console_index) - self.vm.launch() - self.run_tuxtest_tests(haltmsg) - - def ppc64_common_tuxrun(self, sums, prefix): - # add device args to command line. - self.require_netdev('user') - self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', - '-device', 'virtio-net,netdev=vnet') - self.vm.add_args('-netdev', '{"type":"user","id":"hostnet0"}', - '-device', '{"driver":"virtio-net-pci","netdev":' - '"hostnet0","id":"net0","mac":"52:54:00:4c:e3:86",' - '"bus":"pci.0","addr":"0x9"}') - self.vm.add_args('-device', '{"driver":"qemu-xhci","p2":15,"p3":15,' - '"id":"usb","bus":"pci.0","addr":"0x2"}') - self.vm.add_args('-device', '{"driver":"virtio-scsi-pci","id":"scsi0"' - ',"bus":"pci.0","addr":"0x3"}') - self.vm.add_args('-device', '{"driver":"virtio-serial-pci","id":' - '"virtio-serial0","bus":"pci.0","addr":"0x4"}') - self.vm.add_args('-device', '{"driver":"scsi-cd","bus":"scsi0.0"' - ',"channel":0,"scsi-id":0,"lun":0,"device_id":' - '"drive-scsi0-0-0-0","id":"scsi0-0-0-0"}') - self.vm.add_args('-device', '{"driver":"virtio-balloon-pci",' - '"id":"balloon0","bus":"pci.0","addr":"0x6"}') - self.vm.add_args('-audiodev', '{"id":"audio1","driver":"none"}') - self.vm.add_args('-device', '{"driver":"usb-tablet","id":"input0"' - ',"bus":"usb.0","port":"1"}') - self.vm.add_args('-device', '{"driver":"usb-kbd","id":"input1"' - ',"bus":"usb.0","port":"2"}') - self.vm.add_args('-device', '{"driver":"VGA","id":"video0",' - '"vgamem_mb":16,"bus":"pci.0","addr":"0x7"}') - self.vm.add_args('-object', '{"qom-type":"rng-random","id":"objrng0"' - ',"filename":"/dev/urandom"}', - '-device', '{"driver":"virtio-rng-pci","rng":"objrng0"' - ',"id":"rng0","bus":"pci.0","addr":"0x8"}') - self.vm.add_args('-object', '{"qom-type":"cryptodev-backend-builtin",' - '"id":"objcrypto0","queues":1}', - '-device', '{"driver":"virtio-crypto-pci",' - '"cryptodev":"objcrypto0","id":"crypto0","bus"' - ':"pci.0","addr":"0xa"}') - self.vm.add_args('-device', '{"driver":"spapr-pci-host-bridge"' - ',"index":1,"id":"pci.1"}') - self.vm.add_args('-device', '{"driver":"spapr-vscsi","id":"scsi1"' - ',"reg":12288}') - self.vm.add_args('-m', '2G,slots=32,maxmem=4G', - '-object', 'memory-backend-ram,id=ram1,size=1G', - '-device', 'pc-dimm,id=dimm1,memdev=ram1') - - # Create a temporary qcow2 and launch the test-case - with tempfile.NamedTemporaryFile(prefix=prefix, - suffix='.qcow2') as qcow2: - process.run(self.qemu_img + ' create -f qcow2 ' + - qcow2.name + ' 1G') - - self.vm.add_args('-drive', 'file=' + qcow2.name + - ',format=qcow2,if=none,id=' - 'drive-virtio-disk1', - '-device', 'virtio-blk-pci,scsi=off,bus=pci.0,' - 'addr=0xb,drive=drive-virtio-disk1,id=virtio-disk1' - ',bootindex=2') - self.common_tuxrun(csums=sums, drive="scsi-hd") - - # - # The tests themselves. The configuration is derived from how - # tuxrun invokes qemu (with minor tweaks like using -blockdev - # consistently). The tuxrun equivalent is something like: - # - # tuxrun --device qemu-{ARCH} \ - # --kernel https://storage.tuxboot.com/{TUXBOOT}/{IMAGE} - # - - def test_arm64(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=cpu:cortex-a57 - :avocado: tags=machine:virt - :avocado: tags=tuxboot:arm64 - :avocado: tags=console:ttyAMA0 - :avocado: tags=shutdown:nowait - """ - sums = {"Image" : - "ce95a7101a5fecebe0fe630deee6bd97b32ba41bc8754090e9ad8961ea8674c7", - "rootfs.ext4.zst" : - "bbd5ed4b9c7d3f4ca19ba71a323a843c6b585e880115df3b7765769dbd9dd061"} - self.common_tuxrun(csums=sums) - - def test_arm64be(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=cpu:cortex-a57 - :avocado: tags=endian:big - :avocado: tags=machine:virt - :avocado: tags=tuxboot:arm64be - :avocado: tags=console:ttyAMA0 - :avocado: tags=shutdown:nowait - """ - sums = { "Image" : - "e0df4425eb2cd9ea9a283e808037f805641c65d8fcecc8f6407d8f4f339561b4", - "rootfs.ext4.zst" : - "e6ffd8813c8a335bc15728f2835f90539c84be7f8f5f691a8b01451b47fb4bd7"} - self.common_tuxrun(csums=sums) - - def test_armv5(self): - """ - :avocado: tags=arch:arm - :avocado: tags=cpu:arm926 - :avocado: tags=machine:versatilepb - :avocado: tags=tuxboot:armv5 - :avocado: tags=image:zImage - :avocado: tags=console:ttyAMA0 - :avocado: tags=shutdown:nowait - """ - sums = { "rootfs.ext4.zst" : - "17177afa74e7294da0642861f08c88ca3c836764299a54bf6d1ce276cb9712a5", - "versatile-pb.dtb" : - "0bc0c0b0858cefd3c32b385c0d66d97142ded29472a496f4f490e42fc7615b25", - "zImage" : - "c95af2f27647c12265d75e9df44c22ff5228c59855f54aaa70f41ec2842e3a4d" } - - self.common_tuxrun(csums=sums, - drive="virtio-blk-pci", - dt="versatile-pb.dtb") - - def test_armv7(self): - """ - :avocado: tags=arch:arm - :avocado: tags=cpu:cortex-a15 - :avocado: tags=machine:virt - :avocado: tags=tuxboot:armv7 - :avocado: tags=image:zImage - :avocado: tags=console:ttyAMA0 - :avocado: tags=shutdown:nowait - """ - sums = { "rootfs.ext4.zst" : - "ab1fbbeaddda1ffdd45c9405a28cd5370c20f23a7cbc809cc90dc9f243a8eb5a", - "zImage" : - "4c7a22e9f15875bec06bd2a29d822496571eb297d4f22694099ffcdb19077572" } - - self.common_tuxrun(csums=sums) - - def test_armv7be(self): - """ - :avocado: tags=arch:arm - :avocado: tags=cpu:cortex-a15 - :avocado: tags=endian:big - :avocado: tags=machine:virt - :avocado: tags=tuxboot:armv7be - :avocado: tags=image:zImage - :avocado: tags=console:ttyAMA0 - :avocado: tags=shutdown:nowait - """ - sums = {"rootfs.ext4.zst" : - "42ed46dd2d59986206c5b1f6cf35eab58fe3fd20c96b41aaa16b32f3f90a9835", - "zImage" : - "7facc62082b57af12015b08f7fdbaf2f123ba07a478367853ae12b219afc9f2f" } - - self.common_tuxrun(csums=sums) - - def test_i386(self): - """ - :avocado: tags=arch:i386 - :avocado: tags=cpu:coreduo - :avocado: tags=machine:q35 - :avocado: tags=tuxboot:i386 - :avocado: tags=image:bzImage - :avocado: tags=shutdown:nowait - """ - sums = {"bzImage" : - "a3e5b32a354729e65910f5a1ffcda7c14a6c12a55e8213fb86e277f1b76ed956", - "rootfs.ext4.zst" : - "f15e66b2bf673a210ec2a4b2e744a80530b36289e04f5388aab812b97f69754a" } - - self.common_tuxrun(csums=sums, drive="virtio-blk-pci") - - def test_mips32(self): - """ - :avocado: tags=arch:mips - :avocado: tags=machine:malta - :avocado: tags=cpu:mips32r6-generic - :avocado: tags=endian:big - :avocado: tags=tuxboot:mips32 - :avocado: tags=image:vmlinux - :avocado: tags=root:sda - :avocado: tags=shutdown:nowait - """ - sums = { "rootfs.ext4.zst" : - "fc3da0b4c2f38d74c6d705123bb0f633c76ed953128f9d0859378c328a6d11a0", - "vmlinux" : - "bfd2172f8b17fb32970ca0c8c58f59c5a4ca38aa5855d920be3a69b5d16e52f0" } - - self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0") - - def test_mips32el(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=machine:malta - :avocado: tags=cpu:mips32r6-generic - :avocado: tags=tuxboot:mips32el - :avocado: tags=image:vmlinux - :avocado: tags=root:sda - :avocado: tags=shutdown:nowait - """ - sums = { "rootfs.ext4.zst" : - "e799768e289fd69209c21f4dacffa11baea7543d5db101e8ce27e3bc2c41d90e", - "vmlinux" : - "8573867c68a8443db8de6d08bb33fb291c189ca2ca671471d3973a3e712096a3" } - - self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0") - - def test_mips64(self): - """ - :avocado: tags=arch:mips64 - :avocado: tags=machine:malta - :avocado: tags=tuxboot:mips64 - :avocado: tags=endian:big - :avocado: tags=image:vmlinux - :avocado: tags=root:sda - :avocado: tags=shutdown:nowait - """ - sums = { "rootfs.ext4.zst" : - "69d91eeb04df3d8d172922c6993bb37d4deeb6496def75d8580f6f9de3e431da", - "vmlinux" : - "09010e51e4b8bcbbd2494786ffb48eca78f228e96e5c5438344b0eac4029dc61" } - - self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0") - - def test_mips64el(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=machine:malta - :avocado: tags=tuxboot:mips64el - :avocado: tags=image:vmlinux - :avocado: tags=root:sda - :avocado: tags=shutdown:nowait - """ - sums = { "rootfs.ext4.zst" : - "fba585368f5915b1498ed081863474b2d7ec4e97cdd46d21bdcb2f9698f83de4", - "vmlinux" : - "d4e08965e2155c4cccce7c5f34d18fe34c636cda2f2c9844387d614950155266" } - - self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0") - - def test_ppc32(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:ppce500 - :avocado: tags=cpu:e500mc - :avocado: tags=tuxboot:ppc32 - :avocado: tags=image:uImage - :avocado: tags=shutdown:nowait - """ - sums = { "rootfs.ext4.zst" : - "8885b9d999cc24d679542a02e9b6aaf48f718f2050ece6b8347074b6ee41dd09", - "uImage" : - "1a68f74b860fda022fb12e03c5efece8c2b8b590d96cca37a8481a3ae0b3f81f" } - - self.common_tuxrun(csums=sums, drive="virtio-blk-pci") - - def test_ppc64(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - :avocado: tags=cpu:POWER10 - :avocado: tags=endian:big - :avocado: tags=console:hvc0 - :avocado: tags=tuxboot:ppc64 - :avocado: tags=image:vmlinux - :avocado: tags=extradev:driver=spapr-vscsi - :avocado: tags=root:sda - """ - sums = { "rootfs.ext4.zst" : - "1d953e81a4379e537fc8e41e05a0a59d9b453eef97aa03d47866c6c45b00bdff", - "vmlinux" : - "f22a9b9e924174a4c199f4c7e5d91a2339fcfe51c6eafd0907dc3e09b64ab728" } - self.ppc64_common_tuxrun(sums, prefix='tuxrun_ppc64_') - - def test_ppc64le(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - :avocado: tags=cpu:POWER10 - :avocado: tags=console:hvc0 - :avocado: tags=tuxboot:ppc64le - :avocado: tags=image:vmlinux - :avocado: tags=extradev:driver=spapr-vscsi - :avocado: tags=root:sda - """ - sums = { "rootfs.ext4.zst" : - "b442678c93fb8abe1f7d3bfa20556488de6b475c22c8fed363f42cf81a0a3906", - "vmlinux" : - "979eb61b445a010fb13e2b927126991f8ceef9c590fa2be0996c00e293e80cf2" } - self.ppc64_common_tuxrun(sums, prefix='tuxrun_ppc64le_') - - def test_riscv32(self): - """ - :avocado: tags=arch:riscv32 - :avocado: tags=machine:virt - :avocado: tags=tuxboot:riscv32 - """ - sums = { "Image" : - "89599407d7334de629a40e7ad6503c73670359eb5f5ae9d686353a3d6deccbd5", - "fw_jump.elf" : - "f2ef28a0b77826f79d085d3e4aa686f1159b315eff9099a37046b18936676985", - "rootfs.ext4.zst" : - "7168d296d0283238ea73cd5a775b3dd608e55e04c7b92b76ecce31bb13108cba" } - - self.common_tuxrun(csums=sums) - - def test_riscv64(self): - """ - :avocado: tags=arch:riscv64 - :avocado: tags=machine:virt - :avocado: tags=tuxboot:riscv64 - """ - sums = { "Image" : - "cd634badc65e52fb63465ec99e309c0de0369f0841b7d9486f9729e119bac25e", - "fw_jump.elf" : - "6e3373abcab4305fe151b564a4c71110d833c21f2c0a1753b7935459e36aedcf", - "rootfs.ext4.zst" : - "b18e3a3bdf27be03da0b285e84cb71bf09eca071c3a087b42884b6982ed679eb" } - - self.common_tuxrun(csums=sums) - - def test_s390(self): - """ - :avocado: tags=arch:s390x - :avocado: tags=endian:big - :avocado: tags=tuxboot:s390 - :avocado: tags=image:bzImage - :avocado: tags=shutdown:nowait - """ - sums = { "bzImage" : - "0414e98dd1c3dafff8496c9cd9c28a5f8d04553bb5ba37e906a812b48d442ef0", - "rootfs.ext4.zst" : - "88c37c32276677f873a25ab9ec6247895b8e3e6f8259134de2a616080b8ab3fc" } - - self.common_tuxrun(csums=sums, - drive="virtio-blk-ccw", - haltmsg="Requesting system halt") - - # Note: some segfaults caused by unaligned userspace access - @skipIf(os.getenv('GITLAB_CI'), 'Skipping unstable test on GitLab') - def test_sh4(self): - """ - :avocado: tags=arch:sh4 - :avocado: tags=machine:r2d - :avocado: tags=cpu:sh7785 - :avocado: tags=tuxboot:sh4 - :avocado: tags=image:zImage - :avocado: tags=root:sda - :avocado: tags=console:ttySC1 - """ - sums = { "rootfs.ext4.zst" : - "3592a7a3d5a641e8b9821449e77bc43c9904a56c30d45da0694349cfd86743fd", - "zImage" : - "29d9b2aba604a0f53a5dc3b5d0f2b8e35d497de1129f8ee5139eb6fdf0db692f" } - - # The test is currently too unstable to do much in userspace - # so we skip common_tuxrun and do a minimal boot and shutdown. - (kernel, disk, dtb) = self.fetch_tuxrun_assets(csums=sums) - - # the console comes on the second serial port - self.prepare_run(kernel, disk, - "driver=ide-hd,bus=ide.0,unit=0", - console_index=1) - self.vm.launch() - - self.wait_for_console_pattern("Welcome to TuxTest") - time.sleep(0.1) - exec_command(self, 'root') - time.sleep(0.1) - exec_command_and_wait_for_pattern(self, 'halt', - "reboot: System halted") - - def test_sparc64(self): - """ - :avocado: tags=arch:sparc64 - :avocado: tags=tuxboot:sparc64 - :avocado: tags=image:vmlinux - :avocado: tags=root:sda - :avocado: tags=shutdown:nowait - """ - - sums = { "rootfs.ext4.zst" : - "ad2f1dc436ab51583543d25d2c210cab478645d47078d30d129a66ab0e281d76", - "vmlinux" : - "e34313e4325ff21deaa3d38a502aa09a373ef62b9bd4d7f8f29388b688225c55" } - - self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0") - - def test_x86_64(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:q35 - :avocado: tags=cpu:Nehalem - :avocado: tags=tuxboot:x86_64 - :avocado: tags=image:bzImage - :avocado: tags=root:sda - :avocado: tags=shutdown:nowait - """ - sums = { "bzImage" : - "2bc7480a669ee9b6b82500a236aba0c54233debe98cb968268fa230f52f03461", - "rootfs.ext4.zst" : - "b72ac729769b8f51c6dffb221113c9a063c774dbe1d66af30eb593c4e9999b4b" } - - self.common_tuxrun(csums=sums, - drive="driver=ide-hd,bus=ide.0,unit=0") diff --git a/tests/avocado/version.py b/tests/avocado/version.py deleted file mode 100644 index dd775955eb8..00000000000 --- a/tests/avocado/version.py +++ /dev/null @@ -1,25 +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 - :avocado: tags=machine:none - """ - def test_qmp_human_info_version(self): - self.vm.add_args('-nodefaults') - self.vm.launch() - res = self.vm.command('human-monitor-command', - command_line='info version') - self.assertRegexpMatches(res, r'^(\d+\.\d+\.\d)') diff --git a/tests/avocado/virtio-gpu.py b/tests/avocado/virtio-gpu.py deleted file mode 100644 index 89bfecc7157..00000000000 --- a/tests/avocado/virtio-gpu.py +++ /dev/null @@ -1,159 +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/archive/fedora" - "/linux/releases/33/Everything/x86_64/os/images" - "/pxeboot/vmlinuz" - ) - KERNEL_HASH = '1433cfe3f2ffaa44de4ecfb57ec25dc2399cdecf' - INITRD_URL = ( - "https://archives.fedoraproject.org/pub/archive/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, - ) - 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") - 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 4093da8a674..00000000000 --- 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 c84e48813a1..00000000000 --- 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.data/cleanup.sh b/tests/avocado/virtiofs_submounts.py.data/cleanup.sh deleted file mode 100644 index 2a6579a0fea..00000000000 --- 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 729cb2d1a5e..00000000000 --- 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 59ba40fde17..00000000000 --- 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 d8a9afebdb5..00000000000 --- 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 deleted file mode 100644 index aeeefc70be2..00000000000 --- a/tests/avocado/vnc.py +++ /dev/null @@ -1,117 +0,0 @@ -# Simple functional tests for VNC functionality -# -# 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 socket -from typing import List - -from avocado_qemu import QemuSystemTest - - -VNC_ADDR = '127.0.0.1' -VNC_PORT_START = 32768 -VNC_PORT_END = VNC_PORT_START + 1024 - - -def check_bind(port: int) -> bool: - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: - try: - sock.bind((VNC_ADDR, port)) - except OSError: - return False - - return True - - -def check_connect(port: int) -> bool: - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: - try: - sock.connect((VNC_ADDR, port)) - except ConnectionRefusedError: - return False - - return True - - -def find_free_ports(count: int) -> List[int]: - result = [] - for port in range(VNC_PORT_START, VNC_PORT_END): - if check_bind(port): - result.append(port) - if len(result) >= count: - break - assert len(result) == count - return result - - -class Vnc(QemuSystemTest): - """ - :avocado: tags=vnc,quick - :avocado: tags=machine:none - """ - def test_no_vnc(self): - self.vm.add_args('-nodefaults', '-S') - self.vm.launch() - self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled']) - - def test_no_vnc_change_password(self): - self.vm.add_args('-nodefaults', '-S') - self.vm.launch() - self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled']) - set_password_response = self.vm.qmp('change-vnc-password', - password='new_password') - self.assertIn('error', set_password_response) - self.assertEqual(set_password_response['error']['class'], - 'GenericError') - self.assertEqual(set_password_response['error']['desc'], - 'Could not set password') - - def test_change_password_requires_a_password(self): - self.vm.add_args('-nodefaults', '-S', '-vnc', ':0') - 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.assertIn('error', set_password_response) - self.assertEqual(set_password_response['error']['class'], - 'GenericError') - self.assertEqual(set_password_response['error']['desc'], - 'Could not set password') - - def test_change_password(self): - 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'], {}) - - def test_change_listen(self): - a, b, c = find_free_ports(3) - self.assertFalse(check_connect(a)) - self.assertFalse(check_connect(b)) - self.assertFalse(check_connect(c)) - - self.vm.add_args('-nodefaults', '-S', '-vnc', f'{VNC_ADDR}:{a - 5900}') - self.vm.launch() - self.assertEqual(self.vm.qmp('query-vnc')['return']['service'], str(a)) - self.assertTrue(check_connect(a)) - 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.assertEqual(self.vm.qmp('query-vnc')['return']['service'], str(b)) - self.assertFalse(check_connect(a)) - self.assertTrue(check_connect(b)) - self.assertTrue(check_connect(c)) diff --git a/tests/avocado/x86_cpu_model_versions.py b/tests/avocado/x86_cpu_model_versions.py deleted file mode 100644 index a6edf74c1cc..00000000000 --- 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 5e68cb0a1c4..0a6e5db1d6c 100644 --- a/tests/bench/benchmark-crypto-akcipher.c +++ b/tests/bench/benchmark-crypto-akcipher.c @@ -16,19 +16,19 @@ #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; - 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; - return qcrypto_akcipher_new(&opt, QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, + return qcrypto_akcipher_new(&opt, QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, priv_key, keylen, &error_abort); } @@ -39,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); @@ -53,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, @@ -64,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, @@ -81,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 c04f0a0fba3..889a29ba5c2 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 927b00bb4d6..252098a69d2 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 5cca6367897..d51de98f472 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 00000000000..222695c1faa --- /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 3c799dbd983..4cd7a2f6b52 100644 --- a/tests/bench/meson.build +++ b/tests/bench/meson.build @@ -3,9 +3,9 @@ qht_bench = executable('qht-bench', sources: 'qht-bench.c', dependencies: [qemuutil]) -qtree_bench = executable('qtree-bench', - sources: 'qtree-bench.c', - dependencies: [qemuutil]) +executable('qtree-bench', + sources: 'qtree-bench.c', + dependencies: [qemuutil]) executable('atomic_add-bench', sources: files('atomic_add-bench.c'), @@ -21,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/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/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/virt/APIC.topology b/tests/data/acpi/aarch64/virt/APIC.topology similarity index 100% rename from tests/data/acpi/virt/APIC.topology rename to tests/data/acpi/aarch64/virt/APIC.topology diff --git a/tests/data/acpi/aarch64/virt/DBG2 b/tests/data/acpi/aarch64/virt/DBG2 new file mode 100644 index 00000000000..0a05e1a47f9 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 00000000000..36d3e5d5a5e 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 00000000000..e6154d0355f 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 00000000000..33f011d6b63 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 00000000000..c0fdc6e9c13 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 00000000000..029d03eecc4 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 00000000000..da0c3644cc4 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 00000000000..7f330e04d14 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/virt/PPTT b/tests/data/acpi/aarch64/virt/PPTT similarity index 100% rename from tests/data/acpi/virt/PPTT rename to tests/data/acpi/aarch64/virt/PPTT diff --git a/tests/data/acpi/virt/PPTT.acpihmatvirt b/tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt similarity index 100% rename from tests/data/acpi/virt/PPTT.acpihmatvirt rename to tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt diff --git a/tests/data/acpi/virt/PPTT.topology b/tests/data/acpi/aarch64/virt/PPTT.topology similarity index 100% rename from tests/data/acpi/virt/PPTT.topology rename to tests/data/acpi/aarch64/virt/PPTT.topology 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 00000000000..cf0f2b75226 Binary files /dev/null and b/tests/data/acpi/aarch64/virt/SPCR differ diff --git a/tests/data/acpi/virt/SRAT.acpihmatvirt b/tests/data/acpi/aarch64/virt/SRAT.acpihmatvirt similarity index 100% rename from tests/data/acpi/virt/SRAT.acpihmatvirt rename to tests/data/acpi/aarch64/virt/SRAT.acpihmatvirt 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 ef93c44464f..1deb1d28322 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 253b7620a07..89561d233d2 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/DSDT.pcie b/tests/data/acpi/microvm/DSDT.pcie deleted file mode 100644 index 765f14ef3d1..00000000000 Binary files a/tests/data/acpi/microvm/DSDT.pcie and /dev/null differ diff --git a/tests/data/acpi/pc/DSDT b/tests/data/acpi/pc/DSDT deleted file mode 100644 index c93ad6b7f83..00000000000 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 f643fa2d034..00000000000 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 9d3695ff289..00000000000 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 840b45f354a..00000000000 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 dbc0141b2bb..00000000000 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 1294f655d41..00000000000 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 8012b5eb315..00000000000 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 4fa0c6fe720..00000000000 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 0a891baf458..00000000000 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 9b442a64cf7..00000000000 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 1754c687883..00000000000 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 9fc731d3d2b..00000000000 Binary files a/tests/data/acpi/pc/DSDT.numamem 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 f5da2eb1e8a..00000000000 Binary files a/tests/data/acpi/q35/APIC.core-count2 and /dev/null differ diff --git a/tests/data/acpi/q35/DMAR.dmar b/tests/data/acpi/q35/DMAR.dmar deleted file mode 100644 index 0dca6e68ad8..00000000000 Binary files a/tests/data/acpi/q35/DMAR.dmar and /dev/null differ diff --git a/tests/data/acpi/q35/DSDT b/tests/data/acpi/q35/DSDT deleted file mode 100644 index fb89ae0ac6d..00000000000 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 46fd25400b7..00000000000 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 61c5bd52a42..00000000000 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 d9938dba8fa..00000000000 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 b47891ec10b..00000000000 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 20955d0aa30..00000000000 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 ee16a861c2d..00000000000 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 228374b55bd..00000000000 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 5ce081187a5..00000000000 Binary files a/tests/data/acpi/q35/DSDT.memhp 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 c13e45e3612..00000000000 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 ba6669437e6..00000000000 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 64e81f57112..00000000000 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 17611202e53..00000000000 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 dcf2e2f221b..c1092fb8ba6 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 00000000000..66a25dfd2d6 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 00000000000..6a33f5647dd 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 00000000000..a5276b65ea8 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 00000000000..37eb923a932 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 00000000000..13c8025b868 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 00000000000..09617f8793a 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 00000000000..2b6467364b7 Binary files /dev/null and b/tests/data/acpi/riscv64/virt/SRAT.numamem differ diff --git a/tests/data/acpi/virt/DBG2 b/tests/data/acpi/virt/DBG2 deleted file mode 100644 index 86e6314f7b0..00000000000 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 c4750399071..00000000000 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 aee6ba017cd..00000000000 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 bae36cdd397..00000000000 Binary files a/tests/data/acpi/virt/DSDT.memhp 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 fbd78f44c47..00000000000 Binary files a/tests/data/acpi/virt/DSDT.pxb and /dev/null differ diff --git a/tests/data/acpi/virt/DSDT.topology b/tests/data/acpi/virt/DSDT.topology deleted file mode 100644 index 501314c91be..00000000000 Binary files a/tests/data/acpi/virt/DSDT.topology and /dev/null differ diff --git a/tests/data/acpi/virt/FACP b/tests/data/acpi/virt/FACP deleted file mode 100644 index ac05c35a694..00000000000 Binary files a/tests/data/acpi/virt/FACP and /dev/null differ diff --git a/tests/data/acpi/virt/GTDT b/tests/data/acpi/virt/GTDT deleted file mode 100644 index 6f8cb9b8f30..00000000000 Binary files a/tests/data/acpi/virt/GTDT and /dev/null differ diff --git a/tests/data/acpi/virt/SPCR b/tests/data/acpi/virt/SPCR deleted file mode 100644 index 24e0a579e7d..00000000000 Binary files a/tests/data/acpi/virt/SPCR and /dev/null differ diff --git a/tests/data/acpi/microvm/APIC b/tests/data/acpi/x86/microvm/APIC similarity index 100% rename from tests/data/acpi/microvm/APIC rename to tests/data/acpi/x86/microvm/APIC diff --git a/tests/data/acpi/microvm/APIC.ioapic2 b/tests/data/acpi/x86/microvm/APIC.ioapic2 similarity index 100% rename from tests/data/acpi/microvm/APIC.ioapic2 rename to tests/data/acpi/x86/microvm/APIC.ioapic2 diff --git a/tests/data/acpi/microvm/APIC.pcie b/tests/data/acpi/x86/microvm/APIC.pcie similarity index 100% rename from tests/data/acpi/microvm/APIC.pcie rename to tests/data/acpi/x86/microvm/APIC.pcie 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 00000000000..8eacd21d6ec 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/pc/APIC b/tests/data/acpi/x86/pc/APIC similarity index 100% rename from tests/data/acpi/pc/APIC rename to tests/data/acpi/x86/pc/APIC diff --git a/tests/data/acpi/pc/APIC.acpihmat b/tests/data/acpi/x86/pc/APIC.acpihmat similarity index 100% rename from tests/data/acpi/pc/APIC.acpihmat rename to tests/data/acpi/x86/pc/APIC.acpihmat diff --git a/tests/data/acpi/pc/APIC.cphp b/tests/data/acpi/x86/pc/APIC.cphp similarity index 100% rename from tests/data/acpi/pc/APIC.cphp rename to tests/data/acpi/x86/pc/APIC.cphp diff --git a/tests/data/acpi/pc/APIC.dimmpxm b/tests/data/acpi/x86/pc/APIC.dimmpxm similarity index 100% rename from tests/data/acpi/pc/APIC.dimmpxm rename to tests/data/acpi/x86/pc/APIC.dimmpxm diff --git a/tests/data/acpi/x86/pc/DSDT b/tests/data/acpi/x86/pc/DSDT new file mode 100644 index 00000000000..4beb5194b84 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 00000000000..abda6863b64 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 00000000000..d081db26d7b 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 00000000000..e16897dc5f0 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 00000000000..e95711cd9cd 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 00000000000..90ba66b9164 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 00000000000..0eafe5fbf3d 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 00000000000..077a4cc988d 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 00000000000..8d465f02777 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 00000000000..e3b49757cb7 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 00000000000..9e772c1316d 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 00000000000..9bfbfc28213 Binary files /dev/null and b/tests/data/acpi/x86/pc/DSDT.numamem differ diff --git a/tests/data/acpi/pc/DSDT.roothp b/tests/data/acpi/x86/pc/DSDT.roothp similarity index 77% rename from tests/data/acpi/pc/DSDT.roothp rename to tests/data/acpi/x86/pc/DSDT.roothp index e654c83ebe4..efbee6d8aa5 100644 Binary files a/tests/data/acpi/pc/DSDT.roothp 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/q35/APIC b/tests/data/acpi/x86/q35/APIC similarity index 100% rename from tests/data/acpi/q35/APIC rename to tests/data/acpi/x86/q35/APIC diff --git a/tests/data/acpi/q35/APIC.acpihmat b/tests/data/acpi/x86/q35/APIC.acpihmat similarity index 100% rename from tests/data/acpi/q35/APIC.acpihmat rename to tests/data/acpi/x86/q35/APIC.acpihmat 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 00000000000..317ddb3fbed Binary files /dev/null and b/tests/data/acpi/x86/q35/APIC.acpihmat-generic-x differ diff --git a/tests/data/acpi/q35/APIC.acpihmat-noinitiator b/tests/data/acpi/x86/q35/APIC.acpihmat-noinitiator similarity index 100% rename from tests/data/acpi/q35/APIC.acpihmat-noinitiator rename to tests/data/acpi/x86/q35/APIC.acpihmat-noinitiator 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 00000000000..d9d7ca9a896 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 00000000000..4f242844343 Binary files /dev/null and b/tests/data/acpi/x86/q35/APIC.core-count2 differ diff --git a/tests/data/acpi/q35/APIC.cphp b/tests/data/acpi/x86/q35/APIC.cphp similarity index 100% rename from tests/data/acpi/q35/APIC.cphp rename to tests/data/acpi/x86/q35/APIC.cphp diff --git a/tests/data/acpi/q35/APIC.dimmpxm b/tests/data/acpi/x86/q35/APIC.dimmpxm similarity index 100% rename from tests/data/acpi/q35/APIC.dimmpxm rename to tests/data/acpi/x86/q35/APIC.dimmpxm 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 00000000000..c27e87fcf1c 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 00000000000..ac200ab7aa6 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 00000000000..ab60a6ef065 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 100% rename from tests/data/acpi/q35/APIC.xapic rename to tests/data/acpi/x86/q35/APIC.xapic 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 00000000000..31c90116636 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/x86/q35/DMAR.dmar b/tests/data/acpi/x86/q35/DMAR.dmar new file mode 100644 index 00000000000..0c05976715c Binary files /dev/null and b/tests/data/acpi/x86/q35/DMAR.dmar differ diff --git a/tests/data/acpi/x86/q35/DSDT b/tests/data/acpi/x86/q35/DSDT new file mode 100644 index 00000000000..e5e8d1e041e 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 00000000000..072a3fe2cd1 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 00000000000..2a4f2fc1d5c 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 00000000000..7911c058bba 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 89% rename from tests/data/acpi/q35/DSDT.acpihmat-noinitiator rename to tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator index 3aaa2bbdf54..580b4a456a2 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 88% rename from tests/data/acpi/q35/DSDT.applesmc rename to tests/data/acpi/x86/q35/DSDT.applesmc index 944209adeaa..5e8220e38d6 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 00000000000..ee039453af1 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 00000000000..7ebfceeb664 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 00000000000..d0394558a1f 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 00000000000..a055c2e7d3c 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 00000000000..20843549f54 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 00000000000..664e926e907 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 88% rename from tests/data/acpi/q35/DSDT.ipmibt rename to tests/data/acpi/x86/q35/DSDT.ipmibt index 45f911ada56..4066a76d26a 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 88% rename from tests/data/acpi/q35/DSDT.ipmismbus rename to tests/data/acpi/x86/q35/DSDT.ipmismbus index e5d6811bee1..6d0b6b95c2a 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 88% rename from tests/data/acpi/q35/DSDT.ivrs rename to tests/data/acpi/x86/q35/DSDT.ivrs index 46fd25400b7..072a3fe2cd1 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 00000000000..4f2f9bcfcef 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 88% rename from tests/data/acpi/q35/DSDT.mmio64 rename to tests/data/acpi/x86/q35/DSDT.mmio64 index 8459b82c958..0fb6aab16f1 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/q35/DSDT.multi-bridge b/tests/data/acpi/x86/q35/DSDT.multi-bridge similarity index 92% rename from tests/data/acpi/q35/DSDT.multi-bridge rename to tests/data/acpi/x86/q35/DSDT.multi-bridge index 1db43a69e4c..f6afa6d96d2 100644 Binary files a/tests/data/acpi/q35/DSDT.multi-bridge and b/tests/data/acpi/x86/q35/DSDT.multi-bridge differ diff --git a/tests/data/acpi/q35/DSDT.noacpihp b/tests/data/acpi/x86/q35/DSDT.noacpihp similarity index 91% rename from tests/data/acpi/q35/DSDT.noacpihp rename to tests/data/acpi/x86/q35/DSDT.noacpihp index 8bc16887e1c..9f7261d1b06 100644 Binary files a/tests/data/acpi/q35/DSDT.noacpihp 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 00000000000..99ad629c917 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 00000000000..fd1d8a79d3d 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 88% rename from tests/data/acpi/q35/DSDT.pvpanic-isa rename to tests/data/acpi/x86/q35/DSDT.pvpanic-isa index 6ad42873e91..89032fa0290 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 00000000000..7ebfceeb664 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 00000000000..d0394558a1f 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 88% rename from tests/data/acpi/q35/DSDT.tis.tpm12 rename to tests/data/acpi/x86/q35/DSDT.tis.tpm12 index e381ce4cbf2..f2ed40ca70c 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 88% rename from tests/data/acpi/q35/DSDT.tis.tpm2 rename to tests/data/acpi/x86/q35/DSDT.tis.tpm2 index a09253042ce..5c975d2162d 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 00000000000..3194a82b8b4 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 00000000000..129d43e1e56 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 97% rename from tests/data/acpi/q35/DSDT.xapic rename to tests/data/acpi/x86/q35/DSDT.xapic index d4acd851c62..b37ab591110 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 00000000000..31fa5dd19c2 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 00000000000..31fa5dd19c2 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 00000000000..31fa5dd19c2 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 00000000000..31fa5dd19c2 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 00000000000..0e5765f6ee4 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 00000000000..7f9e91aabc0 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 00000000000..b45838adb7d 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 95% rename from tests/data/acpi/q35/SSDT.dimmpxm rename to tests/data/acpi/x86/q35/SSDT.dimmpxm index 70f133412f5..9ea4e0d0cea 100644 Binary files a/tests/data/acpi/q35/SSDT.dimmpxm and b/tests/data/acpi/x86/q35/SSDT.dimmpxm differ 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 122fda45243..888f3431b53 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 00000000000..1d8fea4b0c6 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 00000000000..aef463aab90 Binary files /dev/null and b/tests/data/smbios/type11_blob.legacy differ diff --git a/tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2 b/tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2 new file mode 100644 index 00000000000..18daee0c52f Binary files /dev/null and b/tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2 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 00000000000..c720bf99a45 Binary files /dev/null and b/tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2 differ diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index dfabafab925..3959d8a028a 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -16,9 +16,8 @@ DOCKER_DEFAULT_REGISTRY := registry.gitlab.com/qemu-project/qemu endif DOCKER_REGISTRY := $(if $(REGISTRY),$(REGISTRY),$(DOCKER_DEFAULT_REGISTRY)) -RUNC ?= docker -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) @@ -89,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), \ @@ -118,23 +114,9 @@ docker-image-debian-microblaze-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docke $(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-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,17 +177,18 @@ 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 +# Where QEMU caches build artefacts +DOCKER_QEMU_CACHE_DIR := $$HOME/.cache/qemu # Use a global constant ccache directory to speed up repetitive builds -DOCKER_CCACHE_DIR := $$HOME/.cache/qemu-docker-ccache +DOCKER_QEMU_CCACHE_DIR := DOCKER_QEMU_CACHE_DIR/docker-ccache # This rule if for directly running against an arbitrary docker target. # It is called by the expanded docker targets (e.g. make @@ -214,7 +197,7 @@ DOCKER_CCACHE_DIR := $$HOME/.cache/qemu-docker-ccache # For example: make docker-run TEST="test-quick" IMAGE="debian:arm64" EXECUTABLE=./aarch64-linux-user/qemu-aarch64 # docker-run: docker-qemu-src - @mkdir -p "$(DOCKER_CCACHE_DIR)" + @mkdir -p "$(DOCKER_QEMU_CCACHE_DIR)" @if test -z "$(IMAGE)" || test -z "$(TEST)"; \ then echo "Invalid target $(IMAGE)/$(TEST)"; exit 1; \ fi @@ -226,7 +209,12 @@ docker-run: docker-qemu-src $(call quiet-command, \ $(RUNC) run \ --rm \ - $(if $(NOUSER),,-u $(UID)) \ + $(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) \ @@ -236,8 +224,8 @@ docker-run: docker-qemu-src -e V=$V -e J=$J -e DEBUG=$(DEBUG) \ -e SHOW_ENV=$(SHOW_ENV) \ $(if $(NOUSER),, \ - -e CCACHE_DIR=/var/tmp/ccache \ - -v $(DOCKER_CCACHE_DIR):/var/tmp/ccache:z \ + -v $(DOCKER_QEMU_CACHE_DIR):$(DOCKER_QEMU_CACHE_DIR) \ + -e CCACHE_DIR=$(DOCKER_QEMU_CCACHE_DIR) \ ) \ -v $$(readlink -e $(DOCKER_SRC_COPY)):/var/tmp/qemu:z$(COMMA)ro \ $(IMAGE) \ @@ -250,3 +238,6 @@ docker-image: ${DOCKER_IMAGES:%=docker-image-%} docker-clean: $(call quiet-command, $(DOCKER_SCRIPT) clean) + +# Overrides +docker-test-rust%: NETWORK=1 diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 688ef62989c..3b8a26704df 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -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" % diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index fa455f1474d..bf3bd5a30dd 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-318 qemu +# $ lcitool dockerfile --layers all alpine-321 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/alpine:3.18 +FROM docker.io/library/alpine:3.21 RUN apk update && \ apk upgrade && \ @@ -32,7 +32,6 @@ RUN apk update && \ findutils \ flex \ fuse3-dev \ - g++ \ gcc \ gcovr \ gettext \ @@ -41,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 \ @@ -59,6 +60,7 @@ RUN apk update && \ libtasn1-dev \ liburing-dev \ libusb-dev \ + libxdp-dev \ linux-pam-dev \ llvm \ lttng-ust-dev \ @@ -89,6 +91,8 @@ RUN apk update && \ py3-yaml \ python3 \ rpm2cpio \ + rust \ + rust-bindgen \ samurai \ sdl2-dev \ sdl2_image-dev \ @@ -99,6 +103,7 @@ RUN apk update && \ sparse \ spice-dev \ spice-protocol \ + swtpm \ tar \ tesseract-ocr \ usbredir-dev \ @@ -106,20 +111,19 @@ RUN apk update && \ vde2-dev \ virglrenderer-dev \ vte3-dev \ + vulkan-tools \ 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" diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker deleted file mode 100644 index fc1830966f4..00000000000 --- a/tests/docker/dockerfiles/centos8.docker +++ /dev/null @@ -1,148 +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++ \ - 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 \ - 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 \ - python38 \ - python38-PyYAML \ - python38-numpy \ - python38-pip \ - python38-setuptools \ - python38-wheel \ - rdma-core-devel \ - sed \ - snappy-devel \ - socat \ - spice-protocol \ - spice-server-devel \ - systemd-devel \ - systemtap-sdt-devel \ - tar \ - usbredir-devel \ - util-linux \ - virglrenderer-devel \ - vte291-devel \ - which \ - xfsprogs-devel \ - xorriso \ - zlib-devel \ - zlib-static \ - zstd && \ - 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 - -RUN /usr/bin/pip3.8 install \ - meson==0.63.2 \ - pillow \ - sphinx \ - sphinx-rtd-theme \ - tomli - -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" -ENV LANG "en_US.UTF-8" -ENV MAKE "/usr/bin/make" -ENV NINJA "/usr/bin/ninja" -ENV PYTHON "/usr/bin/python3.8" -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/centos9.docker b/tests/docker/dockerfiles/centos9.docker new file mode 100644 index 00000000000..a942835a1d2 --- /dev/null +++ b/tests/docker/dockerfiles/centos9.docker @@ -0,0 +1,142 @@ +# 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 \ + vulkan-tools \ + 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 54e957d5e74..8ab244e018a 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,20 +61,15 @@ 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 \ - python3-pip \ - python3-setuptools \ - python3-venv \ - python3-wheel + libc6-dev-sparc64-cross && \ + 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 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 \ diff --git a/tests/docker/dockerfiles/debian-alpha-cross.docker b/tests/docker/dockerfiles/debian-alpha-cross.docker deleted file mode 100644 index 7fa7bf1bdec..00000000000 --- a/tests/docker/dockerfiles/debian-alpha-cross.docker +++ /dev/null @@ -1,19 +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 -# 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-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index b66b9cc191b..081f3e00f7b 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-arch 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 \ @@ -30,9 +31,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ @@ -47,27 +45,27 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ - python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ - python3-wheel \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales - -RUN /usr/bin/pip3 install tomli + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" @@ -81,10 +79,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 \ @@ -94,6 +91,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 \ @@ -108,7 +106,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 \ @@ -120,6 +118,7 @@ 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 \ @@ -134,8 +133,10 @@ 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 \ + libssh-dev:amd64 \ libsystemd-dev:amd64 \ libtasn1-6-dev:amd64 \ libubsan1:amd64 \ @@ -146,11 +147,11 @@ 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 && \ @@ -168,13 +169,12 @@ cpu = 'x86_64'\n\ endian = 'little'\n" > /usr/local/share/meson/cross/x86_64-linux-gnu && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-gcc ENV ABI "x86_64-linux-gnu" ENV MESON_OPTS "--cross-file=x86_64-linux-gnu" +ENV 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) diff --git a/tests/docker/dockerfiles/debian-amd64.docker b/tests/docker/dockerfiles/debian-amd64.docker deleted file mode 100644 index 02262bc70ec..00000000000 --- a/tests/docker/dockerfiles/debian-amd64.docker +++ /dev/null @@ -1,169 +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 \ - 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 \ - 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 \ - 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-setuptools \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-wheel \ - python3-yaml \ - rpm2cpio \ - sed \ - socat \ - sparse \ - systemtap-sdt-dev \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - xfslibs-dev \ - xorriso \ - zlib1g-dev \ - zstd && \ - eatmydata apt-get autoremove -y && \ - eatmydata apt-get autoclean -y && \ - sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - 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 tomli - -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" -ENV LANG "en_US.UTF-8" -ENV MAKE "/usr/bin/make" -ENV NINJA "/usr/bin/ninja" -ENV PYTHON "/usr/bin/python3" -# 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 -# 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-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index a0a968b8c67..91c555a36e9 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-arch 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 \ @@ -30,9 +31,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ @@ -47,27 +45,27 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ - python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ - python3-wheel \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales - -RUN /usr/bin/pip3 install tomli + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" @@ -81,10 +79,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 \ @@ -94,6 +91,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 \ @@ -108,7 +106,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 \ @@ -120,6 +118,7 @@ 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 \ @@ -133,8 +132,10 @@ 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 \ + libssh-dev:arm64 \ libsystemd-dev:arm64 \ libtasn1-6-dev:arm64 \ libubsan1:arm64 \ @@ -145,11 +146,11 @@ 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 && \ @@ -167,13 +168,12 @@ cpu = 'aarch64'\n\ 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) diff --git a/tests/docker/dockerfiles/debian-armel-cross.docker b/tests/docker/dockerfiles/debian-armel-cross.docker deleted file mode 100644 index f1fc34a28ac..00000000000 --- a/tests/docker/dockerfiles/debian-armel-cross.docker +++ /dev/null @@ -1,182 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool dockerfile --layers all --cross-arch 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 \ - gcc \ - gcovr \ - gettext \ - git \ - hostname \ - libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ - llvm \ - locales \ - make \ - meson \ - mtools \ - ncat \ - ninja-build \ - openssh-client \ - pkgconf \ - python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-setuptools \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-wheel \ - python3-yaml \ - rpm2cpio \ - sed \ - socat \ - sparse \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - xorriso \ - zstd && \ - eatmydata apt-get autoremove -y && \ - eatmydata apt-get autoclean -y && \ - sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales - -RUN /usr/bin/pip3 install tomli - -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" -ENV LANG "en_US.UTF-8" -ENV MAKE "/usr/bin/make" -ENV NINJA "/usr/bin/ninja" -ENV PYTHON "/usr/bin/python3" - -RUN export DEBIAN_FRONTEND=noninteractive && \ - dpkg --add-architecture 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 \ - libpipewire-0.3-dev:armel \ - libpixman-1-dev:armel \ - libpng-dev:armel \ - libpulse-dev:armel \ - librbd-dev:armel \ - librdmacm-dev:armel \ - libsasl2-dev:armel \ - libsdl2-dev:armel \ - libsdl2-image-dev:armel \ - libseccomp-dev:armel \ - libselinux1-dev:armel \ - libslirp-dev:armel \ - libsnappy-dev:armel \ - libspice-server-dev:armel \ - libssh-gcrypt-dev:armel \ - libsystemd-dev:armel \ - libtasn1-6-dev:armel \ - libubsan1:armel \ - libudev-dev:armel \ - liburing-dev:armel \ - libusb-1.0-0-dev:armel \ - libusbredirhost-dev:armel \ - libvdeplug-dev:armel \ - libvirglrenderer-dev:armel \ - libvte-2.91-dev:armel \ - libzstd-dev:armel \ - nettle-dev:armel \ - systemtap-sdt-dev:armel \ - xfslibs-dev:armel \ - zlib1g-dev:armel && \ - eatmydata apt-get autoremove -y && \ - eatmydata apt-get autoclean -y && \ - mkdir -p /usr/local/share/meson/cross && \ - printf "[binaries]\n\ -c = '/usr/bin/arm-linux-gnueabi-gcc'\n\ -ar = '/usr/bin/arm-linux-gnueabi-gcc-ar'\n\ -strip = '/usr/bin/arm-linux-gnueabi-strip'\n\ -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'\n" > /usr/local/share/meson/cross/arm-linux-gnueabi && \ - dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ - mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-c++ && \ - 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 -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index a2785782119..f0e2efcda09 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-arch 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 \ @@ -30,9 +31,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ @@ -47,27 +45,27 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ - python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ - python3-wheel \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales - -RUN /usr/bin/pip3 install tomli + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" @@ -81,10 +79,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 \ @@ -94,6 +91,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 \ @@ -108,7 +106,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 \ @@ -120,6 +118,7 @@ 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 \ @@ -133,8 +132,10 @@ 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 \ + libssh-dev:armhf \ libsystemd-dev:armhf \ libtasn1-6-dev:armhf \ libubsan1:armhf \ @@ -145,11 +146,11 @@ 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 && \ @@ -167,13 +168,12 @@ cpu = 'armhf'\n\ 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) diff --git a/tests/docker/dockerfiles/debian-hexagon-cross.docker b/tests/docker/dockerfiles/debian-hexagon-cross.docker index 7c38d7c9e46..23152b4918b 100644 --- a/tests/docker/dockerfiles/debian-hexagon-cross.docker +++ b/tests/docker/dockerfiles/debian-hexagon-cross.docker @@ -33,18 +33,20 @@ RUN apt-get update && \ 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 + 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 16.0.0 +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/v${TOOLCHAIN_RELEASE}/${TOOLCHAIN_BASENAME}.tar.xz +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 diff --git a/tests/docker/dockerfiles/debian-hppa-cross.docker b/tests/docker/dockerfiles/debian-hppa-cross.docker deleted file mode 100644 index dd47ffdfa4f..00000000000 --- a/tests/docker/dockerfiles/debian-hppa-cross.docker +++ /dev/null @@ -1,19 +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 -# 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-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker new file mode 100644 index 00000000000..025beb1ce25 --- /dev/null +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -0,0 +1,182 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool dockerfile --layers all --cross-arch i686 debian-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-web \ + sed \ + socat \ + sparse \ + swtpm \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + vulkan-tools \ + 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-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 00000000000..5a6616b7d39 --- /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 9d957547b5c..538ab534902 100644 --- a/tests/docker/dockerfiles/debian-loongarch-cross.docker +++ b/tests/docker/dockerfiles/debian-loongarch-cross.docker @@ -9,22 +9,44 @@ 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 /usr/bin/pip3 install tomli -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 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 diff --git a/tests/docker/dockerfiles/debian-m68k-cross.docker b/tests/docker/dockerfiles/debian-m68k-cross.docker deleted file mode 100644 index 25dd1c1e688..00000000000 --- a/tests/docker/dockerfiles/debian-m68k-cross.docker +++ /dev/null @@ -1,19 +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 -# 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-microblaze-cross.d/build-toolchain.sh b/tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh index 23ec0aa9a72..c5cd0aa931c 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 2cbc568ed1d..00000000000 --- a/tests/docker/dockerfiles/debian-mips-cross.docker +++ /dev/null @@ -1,19 +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 -# 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-mips64-cross.docker b/tests/docker/dockerfiles/debian-mips64-cross.docker deleted file mode 100644 index ba965cf5644..00000000000 --- a/tests/docker/dockerfiles/debian-mips64-cross.docker +++ /dev/null @@ -1,19 +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 -# 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-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index 17d3e01ecc8..4a941dd870e 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-arch 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 \ @@ -30,9 +31,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ @@ -47,27 +45,27 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ - python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ - python3-wheel \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales - -RUN /usr/bin/pip3 install tomli + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" @@ -81,7 +79,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 \ @@ -93,6 +90,7 @@ 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 \ @@ -107,7 +105,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:mips64el \ libgnutls28-dev:mips64el \ libgtk-3-dev:mips64el \ - libibumad-dev:mips64el \ + libgtk-vnc-2.0-dev:mips64el \ libibverbs-dev:mips64el \ libiscsi-dev:mips64el \ libjemalloc-dev:mips64el \ @@ -119,6 +117,7 @@ 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 \ @@ -132,8 +131,10 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:mips64el \ libslirp-dev:mips64el \ libsnappy-dev:mips64el \ + libsndio-dev:mips64el \ + libspice-protocol-dev:mips64el \ libspice-server-dev:mips64el \ - libssh-gcrypt-dev:mips64el \ + libssh-dev:mips64el \ libsystemd-dev:mips64el \ libtasn1-6-dev:mips64el \ libudev-dev:mips64el \ @@ -143,10 +144,10 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ 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 && \ @@ -164,13 +165,12 @@ cpu = 'mips64el'\n\ 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) diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index 5fcd641f155..4d3e5d711bd 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-arch 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 \ @@ -30,9 +31,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ @@ -47,27 +45,27 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ - python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ - python3-wheel \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales - -RUN /usr/bin/pip3 install tomli + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" @@ -81,7 +79,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 \ @@ -93,6 +90,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 \ @@ -107,7 +105,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 \ @@ -119,6 +117,7 @@ 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 \ @@ -132,8 +131,10 @@ 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 \ + libssh-dev:mipsel \ libsystemd-dev:mipsel \ libtasn1-6-dev:mipsel \ libudev-dev:mipsel \ @@ -143,10 +144,10 @@ 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 && \ @@ -164,13 +165,12 @@ cpu = 'mipsel'\n\ 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) diff --git a/tests/docker/dockerfiles/debian-native.docker b/tests/docker/dockerfiles/debian-native.docker deleted file mode 100644 index abac7d7cd74..00000000000 --- a/tests/docker/dockerfiles/debian-native.docker +++ /dev/null @@ -1,54 +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" -# 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-nios2-cross.d/build-toolchain.sh b/tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh deleted file mode 100755 index ba3c9d8affa..00000000000 --- 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 23779413d3b..00000000000 --- a/tests/docker/dockerfiles/debian-powerpc-test-cross.docker +++ /dev/null @@ -1,23 +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 -# 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-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 30e5efa986e..22b4457ba99 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-arch 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 \ @@ -30,9 +31,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ @@ -47,27 +45,27 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ - python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ - python3-wheel \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales - -RUN /usr/bin/pip3 install tomli + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" @@ -81,10 +79,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 \ @@ -94,6 +91,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 \ @@ -108,7 +106,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 \ @@ -120,6 +118,7 @@ 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 \ @@ -133,8 +132,10 @@ 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 \ + libssh-dev:ppc64el \ libsystemd-dev:ppc64el \ libtasn1-6-dev:ppc64el \ libubsan1:ppc64el \ @@ -145,10 +146,10 @@ 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 && \ @@ -166,13 +167,12 @@ cpu = 'powerpc64le'\n\ 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) diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker index a26637ec4fb..b0386cd3a1f 100644 --- a/tests/docker/dockerfiles/debian-riscv64-cross.docker +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch riscv64 debian-sid qemu-minimal +# $ lcitool dockerfile --layers all --cross-arch riscv64 debian-13 qemu-minimal # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:sid-slim +FROM docker.io/library/debian:trixie-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -33,7 +33,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ 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" @@ -50,7 +51,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++-riscv64-linux-gnu \ gcc-riscv64-linux-gnu \ libc6-dev:riscv64 \ libfdt-dev:riscv64 \ @@ -73,9 +73,7 @@ cpu = 'riscv64'\n\ endian = 'little'\n" > /usr/local/share/meson/cross/riscv64-linux-gnu && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-c++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-gcc ENV ABI "riscv64-linux-gnu" 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 6e631295bce..00000000000 --- a/tests/docker/dockerfiles/debian-riscv64-test-cross.docker +++ /dev/null @@ -1,19 +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 -# 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-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index ee6db7b526b..13ec52c8ad0 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-arch 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 \ @@ -30,9 +31,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ @@ -47,27 +45,27 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ - python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ - python3-wheel \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ eatmydata apt-get autoclean -y && \ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales - -RUN /usr/bin/pip3 install tomli + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" @@ -81,10 +79,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 \ @@ -94,6 +91,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 \ @@ -108,7 +106,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 \ @@ -120,6 +118,7 @@ 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 \ @@ -133,7 +132,9 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:s390x \ libslirp-dev:s390x \ libsnappy-dev:s390x \ - libssh-gcrypt-dev:s390x \ + libsndio-dev:s390x \ + libspice-protocol-dev:s390x \ + libssh-dev:s390x \ libsystemd-dev:s390x \ libtasn1-6-dev:s390x \ libubsan1:s390x \ @@ -144,10 +145,10 @@ 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 && \ @@ -165,13 +166,12 @@ cpu = 's390x'\n\ 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) diff --git a/tests/docker/dockerfiles/debian-sh4-cross.docker b/tests/docker/dockerfiles/debian-sh4-cross.docker deleted file mode 100644 index 6bd8171d333..00000000000 --- a/tests/docker/dockerfiles/debian-sh4-cross.docker +++ /dev/null @@ -1,19 +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 -# 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-sparc64-cross.docker b/tests/docker/dockerfiles/debian-sparc64-cross.docker deleted file mode 100644 index 1ef735f2230..00000000000 --- a/tests/docker/dockerfiles/debian-sparc64-cross.docker +++ /dev/null @@ -1,19 +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 -# 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-toolchain.docker b/tests/docker/dockerfiles/debian-toolchain.docker index 687a97fec4a..ab4ce29533d 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 \ @@ -33,6 +35,11 @@ RUN cd /root && ./build-toolchain.sh # and the build trees by restoring the original image, # then copying the built toolchain from stage 0. 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 diff --git a/tests/docker/dockerfiles/debian-tricore-cross.docker b/tests/docker/dockerfiles/debian-tricore-cross.docker index c597f8e16b1..7e00e870ceb 100644 --- a/tests/docker/dockerfiles/debian-tricore-cross.docker +++ b/tests/docker/dockerfiles/debian-tricore-cross.docker @@ -11,8 +11,6 @@ # FROM docker.io/library/debian:11-slim -MAINTAINER Philippe Mathieu-Daudé - RUN apt update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ DEBIAN_FRONTEND=noninteractive eatmydata apt install -yy \ @@ -34,7 +32,8 @@ RUN apt update && \ python3-pip \ python3-setuptools \ python3-wheel \ - python3-venv + python3-venv && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt RUN /usr/bin/pip3 install tomli @@ -44,6 +43,7 @@ RUN curl -#SL https://github.com/bkoppelmann/package_940/releases/download/trico # 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 diff --git a/tests/docker/dockerfiles/debian-xtensa-cross.docker b/tests/docker/dockerfiles/debian-xtensa-cross.docker index 72c25d63d90..d011eee2ad3 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,6 +28,7 @@ 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 diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker new file mode 100644 index 00000000000..0a57c1a1d37 --- /dev/null +++ b/tests/docker/dockerfiles/debian.docker @@ -0,0 +1,171 @@ +# 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-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-web \ + sed \ + socat \ + sparse \ + swtpm \ + systemtap-sdt-dev \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + vulkan-tools \ + 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/emsdk-wasm32-cross.docker b/tests/docker/dockerfiles/emsdk-wasm32-cross.docker new file mode 100644 index 00000000000..60a7d02f561 --- /dev/null +++ b/tests/docker/dockerfiles/emsdk-wasm32-cross.docker @@ -0,0 +1,145 @@ +# syntax = docker/dockerfile:1.5 + +ARG EMSDK_VERSION_QEMU=3.1.50 +ARG ZLIB_VERSION=1.3.1 +ARG GLIB_MINOR_VERSION=2.84 +ARG GLIB_VERSION=${GLIB_MINOR_VERSION}.0 +ARG PIXMAN_VERSION=0.44.2 +ARG FFI_VERSION=v3.4.7 +ARG MESON_VERSION=1.5.0 + +FROM emscripten/emsdk:$EMSDK_VERSION_QEMU AS build-base +ARG MESON_VERSION +ENV TARGET=/builddeps/target +ENV CPATH="$TARGET/include" +ENV PKG_CONFIG_PATH="$TARGET/lib/pkgconfig" +ENV EM_PKG_CONFIG_PATH="$PKG_CONFIG_PATH" +ENV CFLAGS="-O3 -pthread -DWASM_BIGINT" +ENV CXXFLAGS="$CFLAGS" +ENV LDFLAGS="-sWASM_BIGINT -sASYNCIFY=1 -L$TARGET/lib" +RUN apt-get update && apt-get install -y \ + autoconf \ + build-essential \ + libglib2.0-dev \ + libtool \ + pkgconf \ + ninja-build \ + python3-pip +RUN pip3 install meson==${MESON_VERSION} tomli +RUN mkdir /build +WORKDIR /build +RUN mkdir -p $TARGET +RUN < /cross.meson +[host_machine] +system = 'emscripten' +cpu_family = 'wasm32' +cpu = 'wasm32' +endian = 'little' + +[binaries] +c = 'emcc' +cpp = 'em++' +ar = 'emar' +ranlib = 'emranlib' +pkgconfig = ['pkg-config', '--static'] +EOT +EOF + +FROM build-base AS zlib-dev +ARG ZLIB_VERSION +RUN mkdir -p /zlib +RUN curl -Ls https://zlib.net/zlib-$ZLIB_VERSION.tar.xz | \ + tar xJC /zlib --strip-components=1 +WORKDIR /zlib +RUN emconfigure ./configure --prefix=$TARGET --static +RUN emmake make install -j$(nproc) + +FROM build-base AS libffi-dev +ARG FFI_VERSION +RUN mkdir -p /libffi +RUN git clone https://github.com/libffi/libffi /libffi +WORKDIR /libffi +RUN git checkout $FFI_VERSION +RUN autoreconf -fiv +RUN emconfigure ./configure --host=wasm32-unknown-linux \ + --prefix=$TARGET --enable-static \ + --disable-shared --disable-dependency-tracking \ + --disable-builddir --disable-multi-os-directory \ + --disable-raw-api --disable-docs +RUN emmake make install SUBDIRS='include' -j$(nproc) + +FROM build-base AS pixman-dev +ARG PIXMAN_VERSION +RUN mkdir /pixman/ +RUN git clone https://gitlab.freedesktop.org/pixman/pixman /pixman/ +WORKDIR /pixman +RUN git checkout pixman-$PIXMAN_VERSION +RUN <> /cross.meson +[built-in options] +c_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')] +cpp_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')] +objc_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')] +c_link_args = [$(printf "'%s', " $LDFLAGS | sed 's/, $//')] +cpp_link_args = [$(printf "'%s', " $LDFLAGS | sed 's/, $//')] +EOT +EOF +RUN meson setup _build --prefix=$TARGET --cross-file=/cross.meson \ + --default-library=static \ + --buildtype=release -Dtests=disabled -Ddemos=disabled +RUN meson install -C _build + +FROM build-base AS glib-dev +ARG GLIB_VERSION +ARG GLIB_MINOR_VERSION +RUN mkdir -p /stub +WORKDIR /stub +RUN < res_query.c +#include +int res_query(const char *name, int class, + int type, unsigned char *dest, int len) +{ + h_errno = HOST_NOT_FOUND; + return -1; +} +EOT +EOF +RUN emcc ${CFLAGS} -c res_query.c -fPIC -o libresolv.o +RUN ar rcs libresolv.a libresolv.o +RUN mkdir -p $TARGET/lib/ +RUN cp libresolv.a $TARGET/lib/ + +RUN mkdir -p /glib +RUN curl -Lks https://download.gnome.org/sources/glib/${GLIB_MINOR_VERSION}/glib-$GLIB_VERSION.tar.xz | \ + tar xJC /glib --strip-components=1 + +COPY --link --from=zlib-dev /builddeps/ /builddeps/ +COPY --link --from=libffi-dev /builddeps/ /builddeps/ + +WORKDIR /glib +RUN <> /cross.meson +[built-in options] +c_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')] +cpp_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')] +objc_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')] +c_link_args = [$(printf "'%s', " $LDFLAGS | sed 's/, $//')] +cpp_link_args = [$(printf "'%s', " $LDFLAGS | sed 's/, $//')] +EOT +EOF +RUN meson setup _build --prefix=$TARGET --cross-file=/cross.meson \ + --default-library=static --buildtype=release --force-fallback-for=pcre2 \ + -Dselinux=disabled -Dxattr=false -Dlibmount=disabled -Dnls=disabled \ + -Dtests=false -Dglib_debug=disabled -Dglib_assert=false -Dglib_checks=false +# FIXME: emscripten doesn't provide some pthread functions in the final link, +# which isn't detected during meson setup. +RUN sed -i -E "/#define HAVE_POSIX_SPAWN 1/d" ./_build/config.h +RUN sed -i -E "/#define HAVE_PTHREAD_GETNAME_NP 1/d" ./_build/config.h +RUN meson install -C _build + +FROM build-base +COPY --link --from=glib-dev /builddeps/ /builddeps/ +COPY --link --from=pixman-dev /builddeps/ /builddeps/ diff --git a/tests/docker/dockerfiles/fedora-cris-cross.docker b/tests/docker/dockerfiles/fedora-cris-cross.docker deleted file mode 100644 index f2899af410b..00000000000 --- a/tests/docker/dockerfiles/fedora-cris-cross.docker +++ /dev/null @@ -1,13 +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 -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/fedora-i386-cross.docker b/tests/docker/dockerfiles/fedora-i386-cross.docker deleted file mode 100644 index b59a9115c4c..00000000000 --- a/tests/docker/dockerfiles/fedora-i386-cross.docker +++ /dev/null @@ -1,40 +0,0 @@ -FROM registry.fedoraproject.org/fedora:34 - -ENV PACKAGES \ - bison \ - bzip2 \ - ccache \ - diffutils \ - flex \ - findutils \ - gcc \ - git \ - libfdt-devel.i686 \ - 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 \ - python3-tomli \ - 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 -# 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-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker new file mode 100644 index 00000000000..4a033309b38 --- /dev/null +++ b/tests/docker/dockerfiles/fedora-rust-nightly.docker @@ -0,0 +1,183 @@ +# 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 \ + vulkan-tools \ + 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 +ENV RUSTDOC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustdoc +ENV CARGO=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo +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 cargo --version && \ + /usr/local/cargo/bin/rustup run nightly rustc --version && \ + test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \ + test "$RUSTDOC" = "$(/usr/local/cargo/bin/rustup +nightly which rustdoc)" && \ + 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 +RUN $CARGO --list +# 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 afa988574f9..00000000000 --- a/tests/docker/dockerfiles/fedora-win32-cross.docker +++ /dev/null @@ -1,110 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool dockerfile --layers all --cross-arch mingw32 fedora-38 qemu -# -# https://gitlab.com/libvirt/libvirt-ci - -FROM registry.fedoraproject.org/fedora:38 - -RUN dnf install -y nosync && \ - printf '#!/bin/sh\n\ -if test -d /usr/lib64\n\ -then\n\ - export LD_PRELOAD=/usr/lib64/nosync/nosync.so\n\ -else\n\ - export LD_PRELOAD=/usr/lib/nosync/nosync.so\n\ -fi\n\ -exec "$@"\n' > /usr/bin/nosync && \ - chmod +x /usr/bin/nosync && \ - nosync dnf update -y && \ - nosync dnf install -y \ - bash \ - bc \ - bison \ - bzip2 \ - ca-certificates \ - ccache \ - ctags \ - dbus-daemon \ - diffutils \ - findutils \ - flex \ - gcc \ - gcovr \ - git \ - glib2-devel \ - glibc-langpack-en \ - hostname \ - llvm \ - make \ - meson \ - mtools \ - ninja-build \ - nmap-ncat \ - openssh-clients \ - pcre-static \ - python3 \ - python3-PyYAML \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-sphinx \ - python3-sphinx_rtd_theme \ - sed \ - socat \ - sparse \ - spice-protocol \ - tar \ - tesseract \ - tesseract-langpack-eng \ - util-linux \ - which \ - xorriso \ - zstd && \ - 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-libepoxy \ - 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 -# 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-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index cf93a0ca600..a9503444027 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch mingw64 fedora-38 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:38 +FROM registry.fedoraproject.org/fedora:40 RUN dnf install -y nosync && \ printf '#!/bin/sh\n\ @@ -20,6 +20,7 @@ exec "$@"\n' > /usr/bin/nosync && \ nosync dnf install -y \ bash \ bc \ + bindgen-cli \ bison \ bzip2 \ ca-certificates \ @@ -42,7 +43,6 @@ exec "$@"\n' > /usr/bin/nosync && \ ninja-build \ nmap-ncat \ openssh-clients \ - pcre-static \ python3 \ python3-PyYAML \ python3-numpy \ @@ -51,19 +51,23 @@ exec "$@"\n' > /usr/bin/nosync && \ python3-pip \ python3-sphinx \ python3-sphinx_rtd_theme \ + python3-zombie-imp \ + rust \ sed \ socat \ sparse \ - spice-protocol \ + swtpm \ tar \ tesseract \ tesseract-langpack-eng \ util-linux \ + vulkan-tools \ 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" @@ -72,6 +76,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 \ @@ -82,6 +87,7 @@ RUN nosync dnf install -y \ mingw64-gettext \ mingw64-glib2 \ mingw64-gnutls \ + mingw64-gtk-vnc2 \ mingw64-gtk3 \ mingw64-libepoxy \ mingw64-libgcrypt \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index c5b6c969430..014e3ccf17d 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all fedora-38 qemu +# $ lcitool dockerfile --layers all fedora-40 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.fedoraproject.org/fedora:38 +FROM registry.fedoraproject.org/fedora:40 RUN dnf install -y nosync && \ printf '#!/bin/sh\n\ @@ -23,6 +23,7 @@ exec "$@"\n' > /usr/bin/nosync && \ alsa-lib-devel \ bash \ bc \ + bindgen-cli \ bison \ brlapi-devel \ bzip2 \ @@ -41,7 +42,6 @@ exec "$@"\n' > /usr/bin/nosync && \ flex \ fuse3-devel \ gcc \ - gcc-c++ \ gcovr \ gettext \ git \ @@ -51,6 +51,7 @@ exec "$@"\n' > /usr/bin/nosync && \ glibc-static \ glusterfs-api-devel \ gnutls-devel \ + gtk-vnc2-devel \ gtk3-devel \ hostname \ jemalloc-devel \ @@ -61,6 +62,7 @@ exec "$@"\n' > /usr/bin/nosync && \ libbpf-devel \ libcacard-devel \ libcap-ng-devel \ + libcbor-devel \ libcmocka-devel \ libcurl-devel \ libdrm-devel \ @@ -82,6 +84,7 @@ exec "$@"\n' > /usr/bin/nosync && \ libubsan \ liburing-devel \ libusbx-devel \ + libxdp-devel \ libzstd-devel \ llvm \ lttng-ust-devel \ @@ -110,13 +113,16 @@ exec "$@"\n' > /usr/bin/nosync && \ 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 \ @@ -126,21 +132,20 @@ exec "$@"\n' > /usr/bin/nosync && \ util-linux \ virglrenderer-devel \ vte291-devel \ + vulkan-tools \ which \ xen-devel \ - xfsprogs-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/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" diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index fef8d5a2e45..e90225dc235 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -4,9 +4,10 @@ # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.opensuse.org/opensuse/leap:15.5 +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 \ @@ -90,16 +92,20 @@ RUN zypper update -y && \ pcre-devel-static \ pipewire-devel \ pkgconfig \ + python311 \ python311-base \ python311-pip \ python311-setuptools \ rdma-core-devel \ + rust \ + rust-bindgen \ sed \ snappy-devel \ sndio-devel \ socat \ sparse \ spice-protocol-devel \ + swtpm \ systemd-devel \ systemtap-sdt-devel \ tar \ @@ -109,25 +115,24 @@ RUN zypper update -y && \ util-linux \ virglrenderer-devel \ vte-devel \ + vulkan-tools \ which \ xen-devel \ - xfsprogs-devel \ xorriso \ zlib-devel \ 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.11 install \ PyYAML \ - meson==0.63.2 \ + meson==1.5.0 \ pillow \ sphinx \ sphinx-rtd-theme diff --git a/tests/docker/dockerfiles/python.docker b/tests/docker/dockerfiles/python.docker index 383ccbdc3a5..59e70a02484 100644 --- a/tests/docker/dockerfiles/python.docker +++ b/tests/docker/dockerfiles/python.docker @@ -11,7 +11,11 @@ ENV PACKAGES \ python3-pip \ python3-tox \ python3-virtualenv \ - python3.10 + python3.10 \ + python3.11 \ + python3.12 \ + python3.13 \ + python3.9 RUN dnf install -y $PACKAGES RUN rpm -q $PACKAGES | sort > /packages.txt diff --git a/tests/docker/dockerfiles/ubuntu2004.docker b/tests/docker/dockerfiles/ubuntu2004.docker deleted file mode 100644 index 4180cd86740..00000000000 --- a/tests/docker/dockerfiles/ubuntu2004.docker +++ /dev/null @@ -1,157 +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 \ - 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 \ - mtools \ - multipath-tools \ - ncat \ - nettle-dev \ - ninja-build \ - openssh-client \ - pkgconf \ - python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-setuptools \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-wheel \ - python3-yaml \ - rpm2cpio \ - sed \ - socat \ - sparse \ - systemtap-sdt-dev \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - xfslibs-dev \ - xorriso \ - zlib1g-dev \ - zstd && \ - eatmydata apt-get autoremove -y && \ - eatmydata apt-get autoclean -y && \ - sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - 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.63.2 \ - tomli - -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" -ENV LANG "en_US.UTF-8" -ENV MAKE "/usr/bin/make" -ENV NINJA "/usr/bin/ninja" -ENV PYTHON "/usr/bin/python3" -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker index 88493f00f6f..28a6f932430 100644 --- a/tests/docker/dockerfiles/ubuntu2204.docker +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -25,14 +25,13 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ exuberant-ctags \ findutils \ flex \ - g++ \ gcc \ gcovr \ gettext \ git \ hostname \ libaio-dev \ - libasan5 \ + libasan6 \ libasound2-dev \ libattr1-dev \ libbpf-dev \ @@ -42,6 +41,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev \ libcap-ng-dev \ libcapstone-dev \ + libcbor-dev \ libcmocka-dev \ libcurl4-gnutls-dev \ libdaxctl-dev \ @@ -56,7 +56,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev \ libgnutls28-dev \ libgtk-3-dev \ - libibumad-dev \ + libgtk-vnc-2.0-dev \ libibverbs-dev \ libiscsi-dev \ libjemalloc-dev \ @@ -121,14 +121,16 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-venv \ python3-yaml \ rpm2cpio \ + rustc-1.77 \ sed \ socat \ sparse \ + swtpm \ systemtap-sdt-dev \ tar \ tesseract-ocr \ tesseract-ocr-eng \ - xfslibs-dev \ + vulkan-tools \ xorriso \ zlib1g-dev \ zstd && \ @@ -136,12 +138,11 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ 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/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" @@ -149,6 +150,13 @@ ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" +ENV RUSTC=/usr/bin/rustc-1.77 +ENV RUSTDOC=/usr/bin/rustdoc-1.77 +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 diff --git a/tests/docker/test-debug b/tests/docker/test-debug index f52f16328ca..678ceccc274 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-rust b/tests/docker/test-rust new file mode 100755 index 00000000000..e7e3e94a553 --- /dev/null +++ b/tests/docker/test-rust @@ -0,0 +1,21 @@ +#!/bin/bash -e +# +# Run the rust code checks (a.k.a. check-rust-tools-nightly) +# +# Copyright (c) 2025 Linaro Ltd +# +# Authors: +# Alex Bennée +# +# 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. + +. common.rc + +cd "$BUILD_DIR" + +configure_qemu --disable-user --disable-docs --enable-rust +pyvenv/bin/meson devenv -w $QEMU_SRC/rust ${CARGO-cargo} fmt --check +make clippy +make rustdoc diff --git a/tests/fp/fp-bench.c b/tests/fp/fp-bench.c index 8ce0ca1545d..d90f542ea25 100644 --- a/tests/fp/fp-bench.c +++ b/tests/fp/fp-bench.c @@ -488,6 +488,16 @@ static void run_bench(void) { bench_func_t f; + /* + * These implementation-defined choices for various things IEEE + * doesn't specify match those used by the Arm architecture. + */ + set_float_2nan_prop_rule(float_2nan_prop_s_ab, &soft_status); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, &soft_status); + set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, &soft_status); + set_float_default_nan_pattern(0b01000000, &soft_status); + set_float_ftz_detection(float_ftz_before_rounding, &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 4eae93eb7cc..79f619cdea9 100644 --- a/tests/fp/fp-test-log2.c +++ b/tests/fp/fp-test-log2.c @@ -70,6 +70,8 @@ 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_default_nan_pattern(0b01000000, &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 36b5712cda0..c619e5dbf72 100644 --- a/tests/fp/fp-test.c +++ b/tests/fp/fp-test.c @@ -935,6 +935,15 @@ void run_test(void) { unsigned int i; + /* + * These implementation-defined choices for various things IEEE + * doesn't specify match those used by the Arm architecture. + */ + set_float_2nan_prop_rule(float_2nan_prop_s_ab, &qsf); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, &qsf); + set_float_default_nan_pattern(0b01000000, &qsf); + set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, &qsf); + genCases_setLevel(test_level); verCases_maxErrorCount = n_max_errors; diff --git a/tests/fp/meson.build b/tests/fp/meson.build index cbc17392d67..9059a247521 100644 --- a/tests/fp/meson.build +++ b/tests/fp/meson.build @@ -1,12 +1,22 @@ -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 +# 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', @@ -109,6 +119,7 @@ fptest_rounding_args = ['-r', 'all'] 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 @@ -116,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 @@ -124,7 +136,8 @@ 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', @@ -140,4 +153,5 @@ fptestlog2 = executable( 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 fc623de072a..5868a7137a7 100644 --- a/tests/avocado/acpi-bits/bits-tests/smbios.py2 +++ b/tests/functional/acpi-bits/bits-tests/smbios.py2 @@ -1060,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 00000000000..405af67e190 --- /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 100% rename from tests/avocado/acpi-bits/bits-tests/testacpi.py2 rename to tests/functional/acpi-bits/bits-tests/testacpi.py2 diff --git a/tests/avocado/acpi-bits/bits-tests/testcpuid.py2 b/tests/functional/acpi-bits/bits-tests/testcpuid.py2 similarity index 100% rename from tests/avocado/acpi-bits/bits-tests/testcpuid.py2 rename to tests/functional/acpi-bits/bits-tests/testcpuid.py2 diff --git a/tests/functional/aspeed.py b/tests/functional/aspeed.py new file mode 100644 index 00000000000..7a40d5dda73 --- /dev/null +++ b/tests/functional/aspeed.py @@ -0,0 +1,58 @@ +# Test class to boot aspeed machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import LinuxKernelTest + +class AspeedTest(LinuxKernelTest): + + def do_test_arm_aspeed_openbmc(self, machine, image, uboot='2019.04', + cpu_id='0x0', soc='AST2500 rev A1'): + hostname = machine.removesuffix('-bmc') + + self.set_machine(machine) + self.vm.set_console() + self.vm.add_args('-drive', f'file={image},if=mtd,format=raw', + '-snapshot') + self.vm.launch() + + self.wait_for_console_pattern(f'U-Boot {uboot}') + self.wait_for_console_pattern('## Loading kernel from FIT Image') + self.wait_for_console_pattern('Starting kernel ...') + self.wait_for_console_pattern(f'Booting Linux on physical CPU {cpu_id}') + self.wait_for_console_pattern(f'ASPEED {soc}') + self.wait_for_console_pattern('/init as init process') + self.wait_for_console_pattern(f'systemd[1]: Hostname set to <{hostname}>.') + + 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', + 'System halted') + + 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 ...') diff --git a/tests/functional/meson.build b/tests/functional/meson.build new file mode 100644 index 00000000000..e9f19d54a27 --- /dev/null +++ b/tests/functional/meson.build @@ -0,0 +1,420 @@ +# 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_ast2700' : 600, + 'aarch64_aspeed_ast2700fc' : 600, + 'aarch64_imx8mp_evk' : 240, + 'aarch64_raspi4' : 480, + 'aarch64_reverse_debug' : 180, + 'aarch64_rme_virt' : 1200, + 'aarch64_rme_sbsaref' : 1200, + 'aarch64_sbsaref_alpine' : 1200, + 'aarch64_sbsaref_freebsd' : 720, + 'aarch64_smmu' : 720, + 'aarch64_tuxrun' : 240, + 'aarch64_virt' : 360, + 'aarch64_virt_gpu' : 480, + 'acpi_bits' : 420, + 'arm_aspeed_palmetto' : 120, + 'arm_aspeed_romulus' : 120, + 'arm_aspeed_witherspoon' : 120, + 'arm_aspeed_ast2500' : 720, + 'arm_aspeed_ast2600' : 1200, + 'arm_aspeed_bletchley' : 480, + 'arm_aspeed_rainier' : 480, + 'arm_bpim2u' : 500, + 'arm_collie' : 180, + 'arm_cubieboard' : 360, + 'arm_orangepi' : 540, + 'arm_quanta_gsj' : 240, + 'arm_raspi2' : 120, + 'arm_replay' : 240, + 'arm_tuxrun' : 240, + 'arm_sx1' : 360, + 'intel_iommu': 300, + 'mips_malta' : 480, + 'mipsel_malta' : 420, + 'mipsel_replay' : 480, + 'mips64_malta' : 240, + 'mips64el_malta' : 420, + 'mips64el_replay' : 180, + 'netdev_ethtool' : 180, + 'ppc_40p' : 240, + 'ppc64_hv' : 1000, + 'ppc64_powernv' : 480, + 'ppc64_pseries' : 480, + 'ppc64_replay' : 210, + 'ppc64_tuxrun' : 420, + 'ppc64_mac99' : 120, + 'riscv64_tuxrun' : 120, + 's390x_ccw_virtio' : 420, + 'sh4_tuxrun' : 240, + 'virtio_balloon': 120, + 'x86_64_kvm_xen' : 180, + 'x86_64_replay' : 480, +} + +tests_generic_system = [ + 'empty_cpu_model', + 'info_usernet', + 'version', +] + +tests_generic_linuxuser = [ +] + +tests_generic_bsduser = [ +] + +tests_aarch64_system_quick = [ + 'migration', +] + +tests_aarch64_system_thorough = [ + 'aarch64_aspeed_ast2700', + 'aarch64_aspeed_ast2700fc', + 'aarch64_imx8mp_evk', + 'aarch64_raspi3', + 'aarch64_raspi4', + 'aarch64_replay', + 'aarch64_reverse_debug', + 'aarch64_rme_virt', + 'aarch64_rme_sbsaref', + 'aarch64_sbsaref', + 'aarch64_sbsaref_alpine', + 'aarch64_sbsaref_freebsd', + 'aarch64_smmu', + 'aarch64_tcg_plugins', + 'aarch64_tuxrun', + 'aarch64_virt', + 'aarch64_virt_gpu', + 'aarch64_xen', + 'aarch64_xlnx_versal', + 'multiprocess', +] + +tests_alpha_system_quick = [ + 'migration', +] + +tests_alpha_system_thorough = [ + 'alpha_clipper', + 'alpha_replay', +] + +tests_arm_system_quick = [ + 'migration', +] + +tests_arm_system_thorough = [ + 'arm_aspeed_ast1030', + 'arm_aspeed_palmetto', + 'arm_aspeed_romulus', + 'arm_aspeed_witherspoon', + 'arm_aspeed_ast2500', + 'arm_aspeed_ast2600', + 'arm_aspeed_bletchley', + 'arm_aspeed_rainier', + 'arm_bpim2u', + 'arm_canona1100', + 'arm_collie', + 'arm_cubieboard', + 'arm_emcraft_sf2', + 'arm_integratorcp', + 'arm_microbit', + 'arm_orangepi', + 'arm_quanta_gsj', + 'arm_raspi2', + 'arm_realview', + 'arm_replay', + 'arm_smdkc210', + 'arm_stellaris', + 'arm_sx1', + 'arm_vexpress', + 'arm_virt', + 'arm_tuxrun', +] + +tests_arm_linuxuser_thorough = [ + 'arm_bflt', +] + +tests_avr_system_thorough = [ + 'avr_mega2560', + 'avr_uno', +] + +tests_hppa_system_quick = [ + 'hppa_seabios', +] + +tests_i386_system_quick = [ + 'migration', +] + +tests_i386_system_thorough = [ + 'i386_replay', + 'i386_tuxrun', +] + +tests_loongarch64_system_thorough = [ + 'loongarch64_virt', +] + +tests_m68k_system_thorough = [ + 'm68k_mcf5208evb', + 'm68k_nextcube', + 'm68k_replay', + 'm68k_q800', + 'm68k_tuxrun', +] + +tests_microblaze_system_thorough = [ + 'microblaze_replay', + 'microblaze_s3adsp1800' +] + +tests_microblazeel_system_thorough = [ + 'microblazeel_s3adsp1800' +] + +tests_mips_system_thorough = [ + 'mips_malta', + 'mips_replay', + 'mips_tuxrun', +] + +tests_mipsel_system_thorough = [ + 'mipsel_malta', + 'mipsel_replay', + 'mipsel_tuxrun', +] + +tests_mips64_system_thorough = [ + 'mips64_malta', + 'mips64_tuxrun', +] + +tests_mips64el_system_thorough = [ + 'mips64el_fuloong2e', + 'mips64el_loongson3v', + 'mips64el_malta', + 'mips64el_replay', + 'mips64el_tuxrun', +] + +tests_or1k_system_thorough = [ + 'or1k_replay', + 'or1k_sim', +] + +tests_ppc_system_quick = [ + 'migration', + 'ppc_74xx', +] + +tests_ppc_system_thorough = [ + 'ppc_40p', + 'ppc_amiga', + 'ppc_bamboo', + 'ppc_mac', + 'ppc_mpc8544ds', + 'ppc_replay', + 'ppc_sam460ex', + 'ppc_tuxrun', + 'ppc_virtex_ml507', +] + +tests_ppc64_system_quick = [ + 'migration', +] + +tests_ppc64_system_thorough = [ + 'ppc64_e500', + 'ppc64_hv', + 'ppc64_powernv', + 'ppc64_pseries', + 'ppc64_replay', + 'ppc64_reverse_debug', + 'ppc64_tuxrun', + 'ppc64_mac99', +] + +tests_riscv32_system_quick = [ + 'migration', + 'riscv_opensbi', +] + +tests_riscv32_system_thorough = [ + 'riscv32_tuxrun', +] + +tests_riscv64_system_quick = [ + 'migration', + 'riscv_opensbi', +] + +tests_riscv64_system_thorough = [ + 'riscv64_tuxrun', +] + +tests_rx_system_thorough = [ + 'rx_gdbsim', +] + +tests_s390x_system_thorough = [ + 's390x_ccw_virtio', + 's390x_replay', + 's390x_topology', + 's390x_tuxrun', +] + +tests_sh4_system_thorough = [ + 'sh4_r2d', + 'sh4_tuxrun', +] + +tests_sh4eb_system_thorough = [ + 'sh4eb_r2d', +] + +tests_sparc_system_quick = [ + 'migration', +] + +tests_sparc_system_thorough = [ + 'sparc_replay', + 'sparc_sun4m', +] + +tests_sparc64_system_quick = [ + 'migration', +] + +tests_sparc64_system_thorough = [ + 'sparc64_sun4u', + 'sparc64_tuxrun', +] + +tests_x86_64_system_quick = [ + 'cpu_queries', + 'mem_addr_space', + 'migration', + 'pc_cpu_hotplug_props', + 'virtio_version', + 'x86_cpu_model_versions', + 'vnc', + 'memlock', +] + +tests_x86_64_system_thorough = [ + 'acpi_bits', + 'intel_iommu', + 'linux_initrd', + 'multiprocess', + 'netdev_ethtool', + 'virtio_balloon', + 'virtio_gpu', + 'x86_64_hotplug_blk', + 'x86_64_hotplug_cpu', + 'x86_64_kvm_xen', + 'x86_64_replay', + 'x86_64_reverse_debug', + 'x86_64_tuxrun', +] + +tests_xtensa_system_thorough = [ + 'xtensa_lx60', + 'xtensa_replay', +] + +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, plugin_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: [python, '-c', '']) diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py new file mode 100644 index 00000000000..6e666a059fc --- /dev/null +++ b/tests/functional/qemu_test/__init__.py @@ -0,0 +1,20 @@ +# 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, dso_suffix +from .cmd import is_readable_executable_file, \ + interrupt_interactive_console_until_pattern, wait_for_console_pattern, \ + exec_command, exec_command_and_wait_for_pattern, get_qemu_img, which +from .testcase import QemuBaseTest, QemuUserTest, QemuSystemTest +from .linuxkernel import LinuxKernelTest +from .decorators import skipIfMissingCommands, skipIfNotMachine, \ + skipFlakyTest, skipUntrustedTest, skipBigDataTest, skipSlowTest, \ + skipIfMissingImports, skipIfOperatingSystem, skipLockedMemoryTest +from .archive import archive_extract +from .uncompress import uncompress diff --git a/tests/functional/qemu_test/archive.py b/tests/functional/qemu_test/archive.py new file mode 100644 index 00000000000..c803fdaf6dc --- /dev/null +++ b/tests/functional/qemu_test/archive.py @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Utilities for python-based QEMU tests +# +# Copyright 2024 Red Hat, Inc. +# +# Authors: +# Thomas Huth + +import os +from subprocess import check_call, run, DEVNULL +import tarfile +from urllib.parse import urlparse +import zipfile + +from .asset import Asset + + +def tar_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 cpio_extract(archive, output_path): + cwd = os.getcwd() + os.chdir(output_path) + # Not passing 'check=True' as cpio exits with non-zero + # status if the archive contains any device nodes :-( + if type(archive) == str: + run(['cpio', '-i', '-F', archive], + stdout=DEVNULL, stderr=DEVNULL) + else: + run(['cpio', '-i'], + input=archive.read(), + stdout=DEVNULL, stderr=DEVNULL) + os.chdir(cwd) + +def zip_extract(archive, dest_dir, member=None): + with zipfile.ZipFile(archive, 'r') as zf: + if member: + zf.extract(member=member, path=dest_dir) + else: + zf.extractall(path=dest_dir) + +def deb_extract(archive, dest_dir, member=None): + cwd = os.getcwd() + os.chdir(dest_dir) + try: + proc = run(['ar', 't', archive], + check=True, capture_output=True, encoding='utf8') + file_path = proc.stdout.split()[2] + check_call(['ar', 'x', archive, file_path], + stdout=DEVNULL, stderr=DEVNULL) + tar_extract(file_path, dest_dir, member) + finally: + os.chdir(cwd) + +''' +@params archive: filename, Asset, or file-like object to extract +@params dest_dir: target directory to extract into +@params member: optional member file to limit extraction to + +Extracts @archive into @dest_dir. All files are extracted +unless @member specifies a limit. + +If @format is None, heuristics will be applied to guess the format +from the filename or Asset URL. @format must be non-None if @archive +is a file-like object. +''' +def archive_extract(archive, dest_dir, format=None, member=None): + if format is None: + format = guess_archive_format(archive) + if type(archive) == Asset: + archive = str(archive) + + if format == "tar": + tar_extract(archive, dest_dir, member) + elif format == "zip": + zip_extract(archive, dest_dir, member) + elif format == "cpio": + if member is not None: + raise Exception("Unable to filter cpio extraction") + cpio_extract(archive, dest_dir) + elif format == "deb": + if type(archive) != str: + raise Exception("Unable to use file-like object with deb archives") + deb_extract(archive, dest_dir, "./" + member) + else: + raise Exception(f"Unknown archive format {format}") + +''' +@params archive: filename, or Asset to guess + +Guess the format of @compressed, raising an exception if +no format can be determined +''' +def guess_archive_format(archive): + if type(archive) == Asset: + archive = urlparse(archive.url).path + elif type(archive) != str: + raise Exception(f"Unable to guess archive format for {archive}") + + if ".tar." in archive or archive.endswith("tgz"): + return "tar" + elif archive.endswith(".zip"): + return "zip" + elif archive.endswith(".cpio"): + return "cpio" + elif archive.endswith(".deb") or archive.endswith(".udeb"): + return "deb" + else: + raise Exception(f"Unknown archive format for {archive}") diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py new file mode 100644 index 00000000000..704b84d0ea6 --- /dev/null +++ b/tests/functional/qemu_test/asset.py @@ -0,0 +1,230 @@ +# 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 sys +import unittest +import urllib.request +from time import sleep +from pathlib import Path +from shutil import copyfileobj +from urllib.error import HTTPError + +class AssetError(Exception): + def __init__(self, asset, msg, transient=False): + self.url = asset.url + self.msg = msg + self.transient = transient + + def __str__(self): + return "%s: %s" % (self.url, self.msg) + +# 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 __str__(self): + return str(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 AssetError(self, "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 fetchable(self): + return not os.environ.get("QEMU_TEST_NO_DOWNLOAD", False) + + def available(self): + return self.valid() or self.fetchable() + + 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 not self.fetchable(): + raise AssetError(self, + "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) + length_hdr = resp.getheader("Content-Length") + + # Verify downloaded file size against length metadata, if + # available. + if length_hdr is not None: + length = int(length_hdr) + fsize = tmp_cache_file.stat().st_size + if fsize != length: + self.log.error("Unable to download %s: " + "connection closed before " + "transfer complete (%d/%d)", + self.url, fsize, length) + tmp_cache_file.unlink() + continue + 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 HTTPError as e: + tmp_cache_file.unlink() + self.log.error("Unable to download %s: HTTP error %d", + self.url, e.code) + # Treat 404 as fatal, since it is highly likely to + # indicate a broken test rather than a transient + # server or networking problem + if e.code == 404: + raise AssetError(self, "Unable to download: " + "HTTP error %d" % e.code) + continue + except Exception as e: + tmp_cache_file.unlink() + raise AssetError(self, "Unable to download: " % e) + + if not os.path.exists(tmp_cache_file): + raise AssetError(self, "Download retries exceeded", transient=True) + + 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 AssetError(self, "Hash does not match %s" % 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) + try: + asset.fetch() + except AssetError as e: + if not e.transient: + raise + log.error("%s: skipping asset precache" % e) + + 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 00000000000..dc5f422b77d --- /dev/null +++ b/tests/functional/qemu_test/cmd.py @@ -0,0 +1,202 @@ +# 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 + + +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.access(p, os.X_OK): + return p + return None + +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 = test.build_file('qemu-img') + if os.path.exists(qemu_img): + return qemu_img + qemu_img = which('qemu-img') + if qemu_img is not None: + return qemu_img + test.skipTest(f"qemu-img not found in build dir or '$PATH'") diff --git a/tests/functional/qemu_test/config.py b/tests/functional/qemu_test/config.py new file mode 100644 index 00000000000..6d4c9c3ce1d --- /dev/null +++ b/tests/functional/qemu_test/config.py @@ -0,0 +1,48 @@ +# 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 +import platform + + +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() + +def dso_suffix(): + '''Return the dynamic libraries suffix for the current platform''' + + if platform.system() == "Darwin": + return "dylib" + + if platform.system() == "Windows": + return "dll" + + return "so" diff --git a/tests/functional/qemu_test/decorators.py b/tests/functional/qemu_test/decorators.py new file mode 100644 index 00000000000..c0d1567b142 --- /dev/null +++ b/tests/functional/qemu_test/decorators.py @@ -0,0 +1,151 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Decorators useful in functional tests + +import importlib +import os +import platform +import resource +from unittest import skipIf, skipUnless + +from .cmd import which + +''' +Decorator to skip execution of a test if the list +of command binaries is not available in $PATH. +Example: + + @skipIfMissingCommands("mkisofs", "losetup") +''' +def skipIfMissingCommands(*args): + has_cmds = True + for cmd in args: + if not which(cmd): + has_cmds = False + break + + return skipUnless(has_cmds, 'required command(s) "%s" not installed' % + ", ".join(args)) + +''' +Decorator to skip execution of a test if the current +host operating system does match one of the prohibited +ones. +Example + + @skipIfOperatingSystem("Linux", "Darwin") +''' +def skipIfOperatingSystem(*args): + return skipIf(platform.system() in args, + 'running on an OS (%s) that is not able to run this test' % + ", ".join(args)) + +''' +Decorator to skip execution of a test if the current +host machine does not match one of the permitted +machines. +Example + + @skipIfNotMachine("x86_64", "aarch64") +''' +def skipIfNotMachine(*args): + return skipUnless(platform.machine() in args, + 'not running on one of the required machine(s) "%s"' % + ", ".join(args)) + +''' +Decorator to skip execution of flaky tests, unless +the $QEMU_TEST_FLAKY_TESTS environment variable is set. +A bug URL must be provided that documents the observed +failure behaviour, so it can be tracked & re-evaluated +in future. + +Historical tests may be providing "None" as the bug_url +but this should not be done for new test. + +Example: + + @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/NNN") +''' +def skipFlakyTest(bug_url): + if bug_url is None: + bug_url = "FIXME: reproduce flaky test and file bug report or remove" + return skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), + f'Test is unstable: {bug_url}') + +''' +Decorator to skip execution of tests which are likely +to execute untrusted commands on the host, or commands +which process untrusted code, unless the +$QEMU_TEST_ALLOW_UNTRUSTED_CODE env var is set. +Example: + + @skipUntrustedTest() +''' +def skipUntrustedTest(): + return skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), + 'Test runs untrusted code / processes untrusted data') + +''' +Decorator to skip execution of tests which need large +data storage (over around 500MB-1GB mark) on the host, +unless the $QEMU_TEST_ALLOW_LARGE_STORAGE environment +variable is set + +Example: + + @skipBigDataTest() +''' +def skipBigDataTest(): + return skipUnless(os.getenv('QEMU_TEST_ALLOW_LARGE_STORAGE'), + 'Test requires large host storage space') + +''' +Decorator to skip execution of tests which have a really long +runtime (and might e.g. time out if QEMU has been compiled with +debugging enabled) unless the $QEMU_TEST_ALLOW_SLOW +environment variable is set + +Example: + + @skipSlowTest() +''' +def skipSlowTest(): + return skipUnless(os.getenv('QEMU_TEST_ALLOW_SLOW'), + 'Test has a very long runtime and might time out') + +''' +Decorator to skip execution of a test if the list +of python imports is not available. +Example: + + @skipIfMissingImports("numpy", "cv2") +''' +def skipIfMissingImports(*args): + has_imports = True + for impname in args: + try: + importlib.import_module(impname) + except ImportError: + has_imports = False + break + + return skipUnless(has_imports, 'required import(s) "%s" not installed' % + ", ".join(args)) + +''' +Decorator to skip execution of a test if the system's +locked memory limit is below the required threshold. +Takes required locked memory threshold in kB. +Example: + + @skipLockedMemoryTest(2_097_152) +''' +def skipLockedMemoryTest(locked_memory): + # get memlock hard limit in bytes + _, ulimit_memory = resource.getrlimit(resource.RLIMIT_MEMLOCK) + + return skipUnless( + ulimit_memory == resource.RLIM_INFINITY or ulimit_memory >= locked_memory * 1024, + f'Test required {locked_memory} kB of available locked memory', + ) diff --git a/tests/functional/qemu_test/linuxkernel.py b/tests/functional/qemu_test/linuxkernel.py new file mode 100644 index 00000000000..2aca0ee3cd0 --- /dev/null +++ b/tests/functional/qemu_test/linuxkernel.py @@ -0,0 +1,52 @@ +# 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 hashlib +import urllib.request + +from .cmd import wait_for_console_pattern, exec_command_and_wait_for_pattern +from .testcase import QemuSystemTest +from .utils import get_usernet_hostfwd_port + + +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 check_http_download(self, filename, hashsum, guestport=8080, + pythoncmd='python3 -m http.server'): + exec_command_and_wait_for_pattern(self, + f'{pythoncmd} {guestport} & sleep 1', + f'Serving HTTP on 0.0.0.0 port {guestport}') + hl = hashlib.sha256() + hostport = get_usernet_hostfwd_port(self.vm) + url = f'http://localhost:{hostport}{filename}' + self.log.info(f'Downloading {url} ...') + with urllib.request.urlopen(url) as response: + while True: + chunk = response.read(1 << 20) + if not chunk: + break + hl.update(chunk) + + digest = hl.hexdigest() + self.log.info(f'sha256sum of download is {digest}.') + self.assertEqual(digest, hashsum) diff --git a/tests/functional/qemu_test/ports.py b/tests/functional/qemu_test/ports.py new file mode 100644 index 00000000000..631b77abf6b --- /dev/null +++ b/tests/functional/qemu_test/ports.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# +# Simple functional tests for VNC functionality +# +# Copyright 2018, 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 fcntl +import os +import socket + +from .config import BUILD_DIR +from typing import List + + +class Ports(): + + PORTS_ADDR = '127.0.0.1' + PORTS_RANGE_SIZE = 1024 + PORTS_START = 49152 + ((os.getpid() * PORTS_RANGE_SIZE) % 16384) + PORTS_END = PORTS_START + PORTS_RANGE_SIZE + + def __enter__(self): + lock_file = os.path.join(BUILD_DIR, "tests", "functional", "port_lock") + self.lock_fh = os.open(lock_file, os.O_CREAT) + fcntl.flock(self.lock_fh, fcntl.LOCK_EX) + return self + + def __exit__(self, exc_type, exc_value, traceback): + fcntl.flock(self.lock_fh, fcntl.LOCK_UN) + os.close(self.lock_fh) + + def check_bind(self, port: int) -> bool: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + try: + sock.bind((self.PORTS_ADDR, port)) + except OSError: + return False + + return True + + def find_free_ports(self, count: int) -> List[int]: + result = [] + for port in range(self.PORTS_START, self.PORTS_END): + if self.check_bind(port): + result.append(port) + if len(result) >= count: + break + assert len(result) == count + return result + + def find_free_port(self) -> int: + return self.find_free_ports(1)[0] diff --git a/tests/functional/qemu_test/tesseract.py b/tests/functional/qemu_test/tesseract.py new file mode 100644 index 00000000000..ede6c6501e2 --- /dev/null +++ b/tests/functional/qemu_test/tesseract.py @@ -0,0 +1,25 @@ +# ... +# +# 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 logging +from subprocess import run + + +def tesseract_ocr(image_path, tesseract_args=''): + console_logger = logging.getLogger('console') + console_logger.debug(image_path) + proc = run(['tesseract', image_path, 'stdout'], + capture_output=True, encoding='utf8') + if proc.returncode: + return None + lines = [] + for line in proc.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 00000000000..50c401b8c3c --- /dev/null +++ b/tests/functional/qemu_test/testcase.py @@ -0,0 +1,400 @@ +# 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 +from pathlib import Path +import pycotap +import shutil +from subprocess import run +import sys +import tempfile +import unittest +import uuid + +from qemu.machine import QEMUMachine +from qemu.utils import kvm_available, tcg_available + +from .archive import archive_extract +from .asset import Asset +from .config import BUILD_DIR, dso_suffix +from .uncompress import uncompress + + +class QemuBaseTest(unittest.TestCase): + + ''' + @params compressed: filename, Asset, or file-like object to uncompress + @params format: optional compression format (gzip, lzma) + + Uncompresses @compressed into the scratch directory. + + If @format is None, heuristics will be applied to guess the format + from the filename or Asset URL. @format must be non-None if @uncompressed + is a file-like object. + + Returns the fully qualified path to the uncompressed file + ''' + def uncompress(self, compressed, format=None): + self.log.debug(f"Uncompress {compressed} format={format}") + if type(compressed) == Asset: + compressed.fetch() + + (name, ext) = os.path.splitext(str(compressed)) + uncompressed = self.scratch_file(os.path.basename(name)) + + uncompress(compressed, uncompressed, format) + + return uncompressed + + ''' + @params archive: filename, Asset, or file-like object to extract + @params format: optional archive format (tar, zip, deb, cpio) + @params sub_dir: optional sub-directory to extract into + @params member: optional member file to limit extraction to + + Extracts @archive into the scratch directory, or a directory beneath + named by @sub_dir. All files are extracted unless @member specifies + a limit. + + If @format is None, heuristics will be applied to guess the format + from the filename or Asset URL. @format must be non-None if @archive + is a file-like object. + + If @member is non-None, returns the fully qualified path to @member + ''' + def archive_extract(self, archive, format=None, sub_dir=None, member=None): + self.log.debug(f"Extract {archive} format={format}" + + f"sub_dir={sub_dir} member={member}") + if type(archive) == Asset: + archive.fetch() + if sub_dir is None: + archive_extract(archive, self.scratch_file(), format, member) + else: + archive_extract(archive, self.scratch_file(sub_dir), + format, member) + + if member is not None: + return self.scratch_file(member) + return None + + ''' + Create a temporary directory suitable for storing UNIX + socket paths. + + Returns: a tempfile.TemporaryDirectory instance + ''' + def socket_dir(self): + if self.socketdir is None: + self.socketdir = tempfile.TemporaryDirectory( + prefix="qemu_func_test_sock_") + return self.socketdir + + ''' + @params args list of zero or more subdirectories or file + + Construct a path for accessing a data file located + relative to the source directory that is the root for + functional tests. + + @args may be an empty list to reference the root dir + itself, may be a single element to reference a file in + the root directory, or may be multiple elements to + reference a file nested below. The path components + will be joined using the platform appropriate path + separator. + + Returns: string representing a file path + ''' + def data_file(self, *args): + return str(Path(Path(__file__).parent.parent, *args)) + + ''' + @params args list of zero or more subdirectories or file + + Construct a path for accessing a data file located + relative to the build directory root. + + @args may be an empty list to reference the build dir + itself, may be a single element to reference a file in + the build directory, or may be multiple elements to + reference a file nested below. The path components + will be joined using the platform appropriate path + separator. + + Returns: string representing a file path + ''' + def build_file(self, *args): + return str(Path(BUILD_DIR, *args)) + + ''' + @params args list of zero or more subdirectories or file + + Construct a path for accessing/creating a scratch file + located relative to a temporary directory dedicated to + this test case. The directory and its contents will be + purged upon completion of the test. + + @args may be an empty list to reference the scratch dir + itself, may be a single element to reference a file in + the scratch directory, or may be multiple elements to + reference a file nested below. The path components + will be joined using the platform appropriate path + separator. + + Returns: string representing a file path + ''' + def scratch_file(self, *args): + return str(Path(self.workdir, *args)) + + ''' + @params args list of zero or more subdirectories or file + + Construct a path for accessing/creating a log file + located relative to a temporary directory dedicated to + this test case. The directory and its log files will be + preserved upon completion of the test. + + @args may be an empty list to reference the log dir + itself, may be a single element to reference a file in + the log directory, or may be multiple elements to + reference a file nested below. The path components + will be joined using the platform appropriate path + separator. + + Returns: string representing a file path + ''' + def log_file(self, *args): + return str(Path(self.outputdir, *args)) + + ''' + @params plugin name + + Return the full path to the plugin taking into account any host OS + specific suffixes. + ''' + def plugin_file(self, plugin_name): + sfx = dso_suffix() + return os.path.join('tests', 'tcg', 'plugins', f'{plugin_name}.{sfx}') + + def assets_available(self): + for name, asset in vars(self.__class__).items(): + if name.startswith("ASSET_") and type(asset) == Asset: + if not asset.available(): + self.log.debug(f"Asset {asset.url} not available") + return False + return True + + def setUp(self): + self.qemu_bin = os.getenv('QEMU_TEST_QEMU_BINARY') + self.assertIsNotNone(self.qemu_bin, 'QEMU_TEST_QEMU_BINARY must be set') + self.arch = self.qemu_bin.split('-')[-1] + self.socketdir = None + + self.outputdir = self.build_file('tests', 'functional', + self.arch, self.id()) + self.workdir = os.path.join(self.outputdir, 'scratch') + os.makedirs(self.workdir, exist_ok=True) + + self.log_filename = self.log_file('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) + + if not self.assets_available(): + self.skipTest('One or more assets is not available') + + def tearDown(self): + if "QEMU_TEST_KEEP_SCRATCH" not in os.environ: + shutil.rmtree(self.workdir) + if self.socketdir is not None: + shutil.rmtree(self.socketdir.name) + self.socketdir = None + 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() + self._ldpath = [] + + def add_ldpath(self, ldpath): + self._ldpath.append(os.path.abspath(ldpath)) + + def run_cmd(self, bin_path, args=[]): + return 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() + + console_log = logging.getLogger('console') + console_log.setLevel(logging.DEBUG) + self.console_log_name = self.log_file('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( + [self.qemu_bin, '-M', 'help'], + capture_output=True, check=True, encoding='utf8').stdout + 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): + help = run([self.qemu_bin, + '-M', 'none', '-netdev', 'help'], + capture_output=True, check=True, encoding='utf8').stdout; + if help.find('\n' + netdevname + '\n') < 0: + self.skipTest('no support for " + netdevname + " networking') + + def require_device(self, devicename): + help = run([self.qemu_bin, + '-M', 'none', '-device', 'help'], + capture_output=True, check=True, encoding='utf8').stdout; + if help.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.log_file()) + 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 00000000000..6c442ff0dc4 --- /dev/null +++ b/tests/functional/qemu_test/tuxruntest.py @@ -0,0 +1,136 @@ +# 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 + +from qemu_test import QemuSystemTest +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern +from qemu_test import which, 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 setUp(self): + super().setUp() + + # We need zstd for all the tuxrun tests + if which('zstd') is None: + self.skipTest("zstd not found in $PATH") + + # 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 = self.uncompress(rootfs_asset) + 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" + + self.kcmd_line = self.KERNEL_COMMON_COMMAND_LINE + self.kcmd_line += f" root=/dev/{self.root}" + self.kcmd_line += f" console={self.console}" + + self.vm.add_args('-kernel', kernel, + '-append', self.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(self.kcmd_line) + 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/uncompress.py b/tests/functional/qemu_test/uncompress.py new file mode 100644 index 00000000000..b7ef8f759b7 --- /dev/null +++ b/tests/functional/qemu_test/uncompress.py @@ -0,0 +1,107 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Utilities for python-based QEMU tests +# +# Copyright 2024 Red Hat, Inc. +# +# Authors: +# Thomas Huth + +import gzip +import lzma +import os +import stat +import shutil +from urllib.parse import urlparse +from subprocess import run, CalledProcessError + +from .asset import Asset + + +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 zstd_uncompress(zstd_path, output_path): + if os.path.exists(output_path): + return + + try: + run(['zstd', "-f", "-d", zstd_path, + "-o", output_path], capture_output=True, check=True) + except CalledProcessError as e: + os.remove(output_path) + raise Exception( + f"Unable to decompress zstd file {zstd_path} with {e}") from e + + # zstd copies source archive permissions for the output + # file, so must make this writable for QEMU + os.chmod(output_path, stat.S_IRUSR | stat.S_IWUSR) + + +''' +@params compressed: filename, Asset, or file-like object to uncompress +@params uncompressed: filename to uncompress into +@params format: optional compression format (gzip, lzma) + +Uncompresses @compressed into @uncompressed + +If @format is None, heuristics will be applied to guess the format +from the filename or Asset URL. @format must be non-None if @uncompressed +is a file-like object. + +Returns the fully qualified path to the uncompessed file +''' +def uncompress(compressed, uncompressed, format=None): + if format is None: + format = guess_uncompress_format(compressed) + + if format == "xz": + lzma_uncompress(str(compressed), uncompressed) + elif format == "gz": + gzip_uncompress(str(compressed), uncompressed) + elif format == "zstd": + zstd_uncompress(str(compressed), uncompressed) + else: + raise Exception(f"Unknown compression format {format}") + +''' +@params compressed: filename, Asset, or file-like object to guess + +Guess the format of @compressed, raising an exception if +no format can be determined +''' +def guess_uncompress_format(compressed): + if type(compressed) == Asset: + compressed = urlparse(compressed.url).path + elif type(compressed) != str: + raise Exception(f"Unable to guess compression cformat for {compressed}") + + (name, ext) = os.path.splitext(compressed) + if ext == ".xz": + return "xz" + elif ext == ".gz": + return "gz" + elif ext in [".zstd", ".zst"]: + return 'zstd' + else: + raise Exception(f"Unknown compression format for {compressed}") diff --git a/tests/functional/qemu_test/utils.py b/tests/functional/qemu_test/utils.py new file mode 100644 index 00000000000..e7c8de81654 --- /dev/null +++ b/tests/functional/qemu_test/utils.py @@ -0,0 +1,39 @@ +# 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 os + +from qemu.utils import get_info_usernet_hostfwd_port + + +def get_usernet_hostfwd_port(vm): + res = vm.cmd('human-monitor-command', command_line='info usernet') + return get_info_usernet_hostfwd_port(res) + +""" +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) diff --git a/tests/functional/replay_kernel.py b/tests/functional/replay_kernel.py new file mode 100644 index 00000000000..80795eb0520 --- /dev/null +++ b/tests/functional/replay_kernel.py @@ -0,0 +1,84 @@ +# Record/replay test that boots a Linux kernel +# +# Copyright (c) 2020 ISP RAS +# +# Author: +# Pavel Dovgalyuk +# +# 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 time +import subprocess + +from qemu_test.linuxkernel import LinuxKernelTest + +class ReplayKernelBase(LinuxKernelTest): + """ + Boots a Linux kernel in record mode and checks that the console + is operational and the kernel command line is properly passed + from QEMU to the kernel. + Then replays the same scenario and verifies, that QEMU correctly + terminates. + """ + + timeout = 180 + REPLAY_KERNEL_COMMAND_LINE = 'printk.time=1 panic=-1 ' + + def run_vm(self, kernel_path, kernel_command_line, console_pattern, + record, shift, args, replay_path): + # icount requires TCG to be available + self.require_accelerator('tcg') + + logger = logging.getLogger('replay') + start_time = time.time() + vm = self.get_vm(name='recording' if record else 'replay') + vm.set_console() + if record: + logger.info('recording the execution...') + mode = 'record' + else: + logger.info('replaying the execution...') + mode = 'replay' + vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s' % + (shift, mode, replay_path), + '-kernel', kernel_path, + '-append', kernel_command_line, + '-net', 'none', + '-no-reboot') + if args: + vm.add_args(*args) + vm.launch() + self.wait_for_console_pattern(console_pattern, vm) + if record: + 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') + elapsed = time.time() - start_time + 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') + t1 = self.run_vm(kernel_path, kernel_command_line, console_pattern, + True, shift, args, replay_path) + t2 = self.run_vm(kernel_path, kernel_command_line, console_pattern, + False, shift, args, replay_path) + logger = logging.getLogger('replay') + logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) diff --git a/tests/functional/reverse_debugging.py b/tests/functional/reverse_debugging.py new file mode 100644 index 00000000000..f9a1d395f1d --- /dev/null +++ b/tests/functional/reverse_debugging.py @@ -0,0 +1,196 @@ +# Reverse debugging test +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2020 ISP RAS +# +# Author: +# Pavel Dovgalyuk +# +# 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 qemu_test import LinuxKernelTest, get_qemu_img +from qemu_test.ports import Ports + + +class ReverseDebugging(LinuxKernelTest): + """ + Test GDB reverse debugging commands: reverse step and reverse continue. + Recording saves the execution of some instructions and makes an initial + VM snapshot to allow reverse execution. + Replay saves the order of the first instructions and then checks that they + are executed backwards in the correct order. + After that the execution is replayed to the end, and reverse continue + command is checked by setting several breakpoints, and asserting + that the execution is stopped at the last of them. + """ + + timeout = 10 + STEPS = 10 + endian_is_le = True + + def run_vm(self, record, shift, args, replay_path, image_path, port): + from avocado.utils import datadrainer + + logger = logging.getLogger('replay') + vm = self.get_vm(name='record' if record else 'replay') + vm.set_console() + if record: + logger.info('recording the execution...') + mode = 'record' + else: + logger.info('replaying the execution...') + mode = 'replay' + vm.add_args('-gdb', 'tcp::%d' % port, '-S') + vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s,rrsnapshot=init' % + (shift, mode, replay_path), + '-net', 'none') + vm.add_args('-drive', 'file=%s,if=none' % image_path) + 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 + def get_reg_le(g, reg): + res = g.cmd(b'p%x' % reg) + num = 0 + for i in range(len(res))[-2::-2]: + num = 0x100 * num + int(res[i:i + 2], 16) + return num + + @staticmethod + def get_reg_be(g, reg): + res = g.cmd(b'p%x' % reg) + return int(res, 16) + + def get_reg(self, g, reg): + # value may be encoded in BE or LE order + if self.endian_is_le: + return self.get_reg_le(g, reg) + else: + return self.get_reg_be(g, reg) + + def get_pc(self, g): + return self.get_reg(g, self.REG_PC) + + def check_pc(self, g, addr): + pc = self.get_pc(g) + if pc != addr: + self.fail('Invalid PC (read %x instead of %x)' % (pc, addr)) + + @staticmethod + def gdb_step(g): + g.cmd(b's', b'T05thread:01;') + + @staticmethod + def gdb_bstep(g): + g.cmd(b'bs', b'T05thread:01;') + + @staticmethod + def vm_get_icount(vm): + return vm.qmp('query-replay')['return']['icount'] + + def reverse_debugging(self, shift=7, args=None): + from avocado.utils import gdb + from avocado.utils import process + + logger = logging.getLogger('replay') + + # create qcow2 for snapshots + logger.info('creating qcow2 image for VM snapshots') + image_path = os.path.join(self.workdir, 'disk.qcow2') + qemu_img = get_qemu_img(self) + if qemu_img is None: + self.skipTest('Could not find "qemu-img", which is required to ' + 'create the temporary qcow2 image') + cmd = '%s create -f qcow2 %s 128M' % (qemu_img, image_path) + process.run(cmd) + + replay_path = os.path.join(self.workdir, 'replay.bin') + + # record the log + vm = self.run_vm(True, shift, args, replay_path, image_path, -1) + while self.vm_get_icount(vm) <= self.STEPS: + pass + last_icount = self.vm_get_icount(vm) + vm.shutdown() + + logger.info("recorded log with %s+ steps" % last_icount) + + # replay and run debug commands + with Ports() as ports: + port = ports.find_free_port() + vm = self.run_vm(False, shift, args, replay_path, image_path, port) + logger.info('connecting to gdbstub') + g = gdb.GDBRemote('127.0.0.1', port, False, False) + g.connect() + r = g.cmd(b'qSupported') + if b'qXfer:features:read+' in r: + g.cmd(b'qXfer:features:read:target.xml:0,ffb') + if b'ReverseStep+' not in r: + self.fail('Reverse step is not supported by QEMU') + if b'ReverseContinue+' not in r: + self.fail('Reverse continue is not supported by QEMU') + + logger.info('stepping forward') + steps = [] + # record first instruction addresses + for _ in range(self.STEPS): + pc = self.get_pc(g) + logger.info('saving position %x' % pc) + steps.append(pc) + self.gdb_step(g) + + # visit the recorded instruction in reverse order + logger.info('stepping backward') + for addr in steps[::-1]: + self.gdb_bstep(g) + self.check_pc(g, addr) + logger.info('found position %x' % addr) + + # 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;') + + # assume that none of the first instructions is executed again + # breaking the order of the breakpoints + self.check_pc(g, steps[-1]) + logger.info('successfully reached %x' % steps[-1]) + + logger.info('exiting gdb and qemu') + vm.shutdown() diff --git a/tests/functional/test_aarch64_aspeed_ast2700.py b/tests/functional/test_aarch64_aspeed_ast2700.py new file mode 100755 index 00000000000..d02dc7991c1 --- /dev/null +++ b/tests/functional/test_aarch64_aspeed_ast2700.py @@ -0,0 +1,140 @@ +#!/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 + +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 AST2x00MachineSDK(QemuSystemTest): + + def do_test_aarch64_aspeed_sdk_start(self, image): + self.require_netdev('user') + self.vm.set_console() + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.1,address=0x4d,id=tmp-test') + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', + '-net', 'nic', '-net', 'user', '-snapshot') + + self.vm.launch() + + def verify_vbootrom_firmware_flow(self): + wait_for_console_pattern(self, 'Found valid FIT image') + wait_for_console_pattern(self, '[uboot] loading') + wait_for_console_pattern(self, 'done') + wait_for_console_pattern(self, '[fdt] loading') + wait_for_console_pattern(self, 'done') + wait_for_console_pattern(self, '[tee] loading') + wait_for_console_pattern(self, 'done') + wait_for_console_pattern(self, '[atf] loading') + wait_for_console_pattern(self, 'done') + wait_for_console_pattern(self, 'Jumping to BL31 (Trusted Firmware-A)') + + def verify_openbmc_boot_and_login(self, name): + 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 ...') + + wait_for_console_pattern(self, f'{name} login:') + exec_command_and_wait_for_pattern(self, 'root', 'Password:') + exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') + + ASSET_SDK_V906_AST2700 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-a0-default-obmc.tar.gz', + '7247b6f19dbfb700686f8d9f723ac23f3eb229226c0589cb9b06b80d1b61f3cb') + + ASSET_SDK_V906_AST2700A1 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-default-obmc.tar.gz', + 'f1d53e0be8a404ecce3e105f72bc50fa4e090ad13160ffa91b10a6e0233a9dc6') + + def do_ast2700_i2c_test(self): + 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/bus/i2c/devices/1-004d/hwmon/hwmon*/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/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '18000') + + def start_ast2700_test(self, name): + num_cpu = 4 + uboot_size = os.path.getsize(self.scratch_file(name, + 'u-boot-nodtb.bin')) + uboot_dtb_load_addr = hex(0x400000000 + uboot_size) + + load_images_list = [ + { + 'addr': '0x400000000', + 'file': self.scratch_file(name, + 'u-boot-nodtb.bin') + }, + { + 'addr': str(uboot_dtb_load_addr), + 'file': self.scratch_file(name, 'u-boot.dtb') + }, + { + 'addr': '0x430000000', + 'file': self.scratch_file(name, 'bl31.bin') + }, + { + 'addr': '0x430080000', + 'file': self.scratch_file(name, '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.do_test_aarch64_aspeed_sdk_start( + self.scratch_file(name, 'image-bmc')) + + def start_ast2700_test_vbootrom(self, name): + self.vm.add_args('-bios', 'ast27x0_bootrom.bin') + self.do_test_aarch64_aspeed_sdk_start( + self.scratch_file(name, 'image-bmc')) + + def test_aarch64_ast2700_evb_sdk_v09_06(self): + self.set_machine('ast2700-evb') + + self.archive_extract(self.ASSET_SDK_V906_AST2700) + self.start_ast2700_test('ast2700-a0-default') + self.verify_openbmc_boot_and_login('ast2700-a0-default') + self.do_ast2700_i2c_test() + + def test_aarch64_ast2700a1_evb_sdk_v09_06(self): + self.set_machine('ast2700a1-evb') + + self.archive_extract(self.ASSET_SDK_V906_AST2700A1) + self.start_ast2700_test('ast2700-default') + self.verify_openbmc_boot_and_login('ast2700-default') + self.do_ast2700_i2c_test() + + def test_aarch64_ast2700a1_evb_sdk_vbootrom_v09_06(self): + self.set_machine('ast2700a1-evb') + + self.archive_extract(self.ASSET_SDK_V906_AST2700A1) + self.start_ast2700_test_vbootrom('ast2700-default') + self.verify_vbootrom_firmware_flow() + self.verify_openbmc_boot_and_login('ast2700-default') + self.do_ast2700_i2c_test() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_aspeed_ast2700fc.py b/tests/functional/test_aarch64_aspeed_ast2700fc.py new file mode 100755 index 00000000000..b85370e182e --- /dev/null +++ b/tests/functional/test_aarch64_aspeed_ast2700fc.py @@ -0,0 +1,135 @@ +#!/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 + +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 AST2x00MachineSDK(QemuSystemTest): + + def do_test_aarch64_aspeed_sdk_start(self, image): + self.require_netdev('user') + self.vm.set_console() + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.1,address=0x4d,id=tmp-test') + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', + '-net', 'nic', '-net', 'user', '-snapshot') + + self.vm.launch() + + def verify_openbmc_boot_and_login(self, name): + 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 ...') + + wait_for_console_pattern(self, f'{name} login:') + exec_command_and_wait_for_pattern(self, 'root', 'Password:') + exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') + + ASSET_SDK_V906_AST2700 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-default-obmc.tar.gz', + 'f1d53e0be8a404ecce3e105f72bc50fa4e090ad13160ffa91b10a6e0233a9dc6') + + def do_ast2700_i2c_test(self): + 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/bus/i2c/devices/1-004d/hwmon/hwmon*/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/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '18000') + + def do_ast2700fc_ssp_test(self): + self.vm.shutdown() + self.vm.set_console(console_index=1) + self.vm.launch() + + exec_command_and_wait_for_pattern(self, '\012', 'ssp:~$') + exec_command_and_wait_for_pattern(self, 'version', + 'Zephyr version 3.7.1') + exec_command_and_wait_for_pattern(self, 'md 72c02000 1', + '[72c02000] 06010103') + + def do_ast2700fc_tsp_test(self): + self.vm.shutdown() + self.vm.set_console(console_index=2) + self.vm.launch() + + exec_command_and_wait_for_pattern(self, '\012', 'tsp:~$') + exec_command_and_wait_for_pattern(self, 'version', + 'Zephyr version 3.7.1') + exec_command_and_wait_for_pattern(self, 'md 72c02000 1', + '[72c02000] 06010103') + + def start_ast2700fc_test(self, name): + ca35_core = 4 + uboot_size = os.path.getsize(self.scratch_file(name, + 'u-boot-nodtb.bin')) + uboot_dtb_load_addr = hex(0x400000000 + uboot_size) + + load_images_list = [ + { + 'addr': '0x400000000', + 'file': self.scratch_file(name, + 'u-boot-nodtb.bin') + }, + { + 'addr': str(uboot_dtb_load_addr), + 'file': self.scratch_file(name, 'u-boot.dtb') + }, + { + 'addr': '0x430000000', + 'file': self.scratch_file(name, 'bl31.bin') + }, + { + 'addr': '0x430080000', + 'file': self.scratch_file(name, '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(ca35_core): + self.vm.add_args('-device', + f'loader,addr=0x430000000,cpu-num={i}') + + load_elf_list = { + 'ssp': self.scratch_file(name, 'zephyr-aspeed-ssp.elf'), + 'tsp': self.scratch_file(name, 'zephyr-aspeed-tsp.elf') + } + + for cpu_num, key in enumerate(load_elf_list, start=4): + file = load_elf_list[key] + self.vm.add_args('-device', + f'loader,file={file},cpu-num={cpu_num}') + + self.do_test_aarch64_aspeed_sdk_start( + self.scratch_file(name, 'image-bmc')) + + def test_aarch64_ast2700fc_sdk_v09_06(self): + self.set_machine('ast2700fc') + + self.archive_extract(self.ASSET_SDK_V906_AST2700) + self.start_ast2700fc_test('ast2700-default') + self.verify_openbmc_boot_and_login('ast2700-default') + self.do_ast2700_i2c_test() + self.do_ast2700fc_ssp_test() + self.do_ast2700fc_tsp_test() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_imx8mp_evk.py b/tests/functional/test_aarch64_imx8mp_evk.py new file mode 100755 index 00000000000..638bf9e1310 --- /dev/null +++ b/tests/functional/test_aarch64_imx8mp_evk.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class Imx8mpEvkMachine(LinuxKernelTest): + + ASSET_IMAGE = Asset( + ('https://cloud.debian.org/images/cloud/bookworm/20231210-1590/' + 'debian-12-generic-arm64-20231210-1590.tar.xz'), + '7ebf1577b32d5af6204df74b54ca2e4675de9b5a9fa14f3ff70b88eeb7b3b359') + + KERNEL_OFFSET = 0x51000000 + KERNEL_SIZE = 32622528 + INITRD_OFFSET = 0x76000000 + INITRD_SIZE = 30987766 + DTB_OFFSET = 0x64F51000 + DTB_SIZE = 45 * 1024 + + def extract(self, in_path, out_path, offset, size): + try: + with open(in_path, "rb") as source: + source.seek(offset) + data = source.read(size) + with open(out_path, "wb") as target: + target.write(data) + except (IOError, ValueError) as e: + self.log.error(f"Failed to extract {out_path}: {e}") + raise + + def setUp(self): + super().setUp() + + self.image_path = self.scratch_file("disk.raw") + self.kernel_path = self.scratch_file("linux") + self.initrd_path = self.scratch_file("initrd.zstd") + self.dtb_path = self.scratch_file("imx8mp-evk.dtb") + + self.archive_extract(self.ASSET_IMAGE) + self.extract(self.image_path, self.kernel_path, + self.KERNEL_OFFSET, self.KERNEL_SIZE) + self.extract(self.image_path, self.initrd_path, + self.INITRD_OFFSET, self.INITRD_SIZE) + self.extract(self.image_path, self.dtb_path, + self.DTB_OFFSET, self.DTB_SIZE) + + def test_aarch64_imx8mp_evk_usdhc(self): + self.set_machine('imx8mp-evk') + self.vm.set_console(console_index=1) + self.vm.add_args('-m', '2G', + '-smp', '4', + '-kernel', self.kernel_path, + '-initrd', self.initrd_path, + '-dtb', self.dtb_path, + '-append', 'root=/dev/mmcblk2p1', + '-drive', f'file={self.image_path},if=sd,bus=2,' + 'format=raw,id=mmcblk2,snapshot=on') + + self.vm.launch() + self.wait_for_console_pattern('Welcome to ') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_aarch64_raspi3.py b/tests/functional/test_aarch64_raspi3.py new file mode 100755 index 00000000000..74f6630ed26 --- /dev/null +++ b/tests/functional/test_aarch64_raspi3.py @@ -0,0 +1,34 @@ +#!/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 + +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' + efi_fd = self.archive_extract(self.ASSET_RPI3_UEFI, member=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 00000000000..7a4302b0c5a --- /dev/null +++ b/tests/functional/test_aarch64_raspi4.py @@ -0,0 +1,96 @@ +#!/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 + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern + + +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): + kernel_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/kernel8.img') + dtb_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='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): + kernel_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/kernel8.img') + dtb_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/bcm2711-rpi-4-b.dtb') + initrd_path = self.uncompress(self.ASSET_INITRD) + + 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_replay.py b/tests/functional/test_aarch64_replay.py new file mode 100755 index 00000000000..db12e76603f --- /dev/null +++ b/tests/functional/test_aarch64_replay.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an aarch64 machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from subprocess import check_call, DEVNULL + +from qemu_test import Asset, skipIfOperatingSystem, get_qemu_img +from replay_kernel import ReplayKernelBase + + +class Aarch64Replay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/arm64/Image', + 'b74743c5e89e1cea0f73368d24ae0ae85c5204ff84be3b5e9610417417d2f235') + + ASSET_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/arm64/rootfs.ext4.zst', + 'a1acaaae2068df4648d04ff75f532aaa8c5edcd6b936122b6f0db4848a07b465') + + def test_aarch64_virt(self): + self.require_netdev('user') + self.set_machine('virt') + self.cpu = 'cortex-a57' + kernel_path = self.ASSET_KERNEL.fetch() + + raw_disk = self.uncompress(self.ASSET_ROOTFS) + disk = self.scratch_file('scratch.qcow2') + qemu_img = get_qemu_img(self) + check_call([qemu_img, 'create', '-f', 'qcow2', '-b', raw_disk, + '-F', 'raw', disk], stdout=DEVNULL, stderr=DEVNULL) + + args = ('-drive', 'file=%s,snapshot=on,id=hd0,if=none' % disk, + '-drive', 'driver=blkreplay,id=hd0-rr,if=none,image=hd0', + '-device', 'virtio-blk-device,drive=hd0-rr', + '-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', + '-device', 'virtio-net,netdev=vnet', + '-object', 'filter-replay,id=replay,netdev=vnet') + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0 root=/dev/vda') + console_pattern = 'Welcome to TuxTest' + self.run_rr(kernel_path, kernel_command_line, console_pattern, + args=args) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_aarch64_reverse_debug.py b/tests/functional/test_aarch64_reverse_debug.py new file mode 100755 index 00000000000..58d45328350 --- /dev/null +++ b/tests/functional/test_aarch64_reverse_debug.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Reverse debugging test +# +# Copyright (c) 2020 ISP RAS +# +# Author: +# Pavel Dovgalyuk +# +# 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 Asset, skipIfMissingImports, skipFlakyTest +from reverse_debugging import ReverseDebugging + + +@skipIfMissingImports('avocado.utils') +class ReverseDebugging_AArch64(ReverseDebugging): + + REG_PC = 32 + + KERNEL_ASSET = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/29/Everything/aarch64/os/images/pxeboot/vmlinuz'), + '7e1430b81c26bdd0da025eeb8fbd77b5dc961da4364af26e771bd39f379cbbf7') + + @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/2921") + def test_aarch64_virt(self): + self.set_machine('virt') + self.cpu = 'cortex-a53' + kernel_path = self.KERNEL_ASSET.fetch() + self.reverse_debugging(args=('-kernel', kernel_path)) + + +if __name__ == '__main__': + ReverseDebugging.main() diff --git a/tests/functional/test_aarch64_rme_sbsaref.py b/tests/functional/test_aarch64_rme_sbsaref.py new file mode 100755 index 00000000000..746770e776d --- /dev/null +++ b/tests/functional/test_aarch64_rme_sbsaref.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Realms environment on sbsa-ref machine and a +# nested guest VM using it. +# +# Copyright (c) 2024 Linaro Ltd. +# +# Author: Pierrick Bouvier +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern +from test_aarch64_rme_virt import test_realms_guest + + +class Aarch64RMESbsaRefMachine(QemuSystemTest): + + # Stack is built with OP-TEE build environment from those instructions: + # https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/ + # https://github.com/pbo-linaro/qemu-rme-stack + ASSET_RME_STACK_SBSA = Asset( + ('https://fileserver.linaro.org/s/KJyeBxL82mz2r7F/' + 'download/rme-stack-op-tee-4.2.0-cca-v4-sbsa.tar.gz'), + 'dd9ab28ec869bdf3b5376116cb3689103b43433fd5c4bca0f4a8d8b3c104999e') + + # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, + # and launching a nested VM using it. + def test_aarch64_rme_sbsaref(self): + self.set_machine('sbsa-ref') + self.require_accelerator('tcg') + self.require_netdev('user') + + self.vm.set_console() + + stack_path_tar_gz = self.ASSET_RME_STACK_SBSA.fetch() + self.archive_extract(stack_path_tar_gz, format="tar") + + rme_stack = self.scratch_file('rme-stack-op-tee-4.2.0-cca-v4-sbsa') + pflash0 = os.path.join(rme_stack, 'images', 'SBSA_FLASH0.fd') + pflash1 = os.path.join(rme_stack, 'images', 'SBSA_FLASH1.fd') + virtual = os.path.join(rme_stack, 'images', 'disks', 'virtual') + drive = os.path.join(rme_stack, 'out-br', 'images', 'rootfs.ext4') + + self.vm.add_args('-cpu', 'max,x-rme=on,pauth-impdef=on') + self.vm.add_args('-m', '2G') + self.vm.add_args('-M', 'sbsa-ref') + self.vm.add_args('-drive', f'file={pflash0},format=raw,if=pflash') + self.vm.add_args('-drive', f'file={pflash1},format=raw,if=pflash') + self.vm.add_args('-drive', f'file=fat:rw:{virtual},format=raw') + self.vm.add_args('-drive', f'format=raw,if=none,file={drive},id=hd0') + self.vm.add_args('-device', 'virtio-blk-pci,drive=hd0') + self.vm.add_args('-device', 'virtio-9p-pci,fsdev=shr0,mount_tag=shr0') + self.vm.add_args('-fsdev', f'local,security_model=none,path={rme_stack},id=shr0') + self.vm.add_args('-device', 'virtio-net-pci,netdev=net0') + self.vm.add_args('-netdev', 'user,id=net0') + + self.vm.launch() + # Wait for host VM boot to complete. + wait_for_console_pattern(self, 'Welcome to Buildroot', + failure_message='Synchronous Exception at') + exec_command_and_wait_for_pattern(self, 'root', '#') + + test_realms_guest(self) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_rme_virt.py b/tests/functional/test_aarch64_rme_virt.py new file mode 100755 index 00000000000..8452d27928f --- /dev/null +++ b/tests/functional/test_aarch64_rme_virt.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Realms environment on virt machine and a nested +# guest VM using it. +# +# Copyright (c) 2024 Linaro Ltd. +# +# Author: Pierrick Bouvier +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command, wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern + +def test_realms_guest(test_rme_instance): + + # Boot the (nested) guest VM + exec_command(test_rme_instance, + 'qemu-system-aarch64 -M virt,gic-version=3 ' + '-cpu host -enable-kvm -m 512M ' + '-M confidential-guest-support=rme0 ' + '-object rme-guest,id=rme0 ' + '-device virtio-net-pci,netdev=net0,romfile= ' + '-netdev user,id=net0 ' + '-kernel /mnt/out/bin/Image ' + '-initrd /mnt/out-br/images/rootfs.cpio ' + '-serial stdio') + # Detect Realm activation during (nested) guest boot. + wait_for_console_pattern(test_rme_instance, + 'SMC_RMI_REALM_ACTIVATE') + # Wait for (nested) guest boot to complete. + wait_for_console_pattern(test_rme_instance, + 'Welcome to Buildroot') + exec_command_and_wait_for_pattern(test_rme_instance, 'root', '#') + # query (nested) guest cca report + exec_command(test_rme_instance, 'cca-workload-attestation report') + wait_for_console_pattern(test_rme_instance, + '"cca-platform-hash-algo-id": "sha-256"') + wait_for_console_pattern(test_rme_instance, + '"cca-realm-hash-algo-id": "sha-512"') + wait_for_console_pattern(test_rme_instance, + '"cca-realm-public-key-hash-algo-id": "sha-256"') + +class Aarch64RMEVirtMachine(QemuSystemTest): + + # Stack is built with OP-TEE build environment from those instructions: + # https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/ + # https://github.com/pbo-linaro/qemu-rme-stack + ASSET_RME_STACK_VIRT = Asset( + ('https://fileserver.linaro.org/s/iaRsNDJp2CXHMSJ/' + 'download/rme-stack-op-tee-4.2.0-cca-v4-qemu_v8.tar.gz'), + '1851adc232b094384d8b879b9a2cfff07ef3d6205032b85e9b3a4a9ae6b0b7ad') + + # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, + # and launching a nested VM using it. + def test_aarch64_rme_virt(self): + self.set_machine('virt') + self.require_accelerator('tcg') + self.require_netdev('user') + + self.vm.set_console() + + stack_path_tar_gz = self.ASSET_RME_STACK_VIRT.fetch() + self.archive_extract(stack_path_tar_gz, format="tar") + + rme_stack = self.scratch_file('rme-stack-op-tee-4.2.0-cca-v4-qemu_v8') + kernel = os.path.join(rme_stack, 'out', 'bin', 'Image') + bios = os.path.join(rme_stack, 'out', 'bin', 'flash.bin') + drive = os.path.join(rme_stack, 'out-br', 'images', 'rootfs.ext4') + + self.vm.add_args('-cpu', 'max,x-rme=on,pauth-impdef=on') + self.vm.add_args('-m', '2G') + self.vm.add_args('-M', 'virt,acpi=off,' + 'virtualization=on,' + 'secure=on,' + 'gic-version=3') + self.vm.add_args('-bios', bios) + self.vm.add_args('-kernel', kernel) + self.vm.add_args('-drive', f'format=raw,if=none,file={drive},id=hd0') + self.vm.add_args('-device', 'virtio-blk-pci,drive=hd0') + self.vm.add_args('-device', 'virtio-9p-device,fsdev=shr0,mount_tag=shr0') + self.vm.add_args('-fsdev', f'local,security_model=none,path={rme_stack},id=shr0') + self.vm.add_args('-device', 'virtio-net-pci,netdev=net0') + self.vm.add_args('-netdev', 'user,id=net0') + # We need to add nokaslr to avoid triggering this sporadic bug: + # https://gitlab.com/qemu-project/qemu/-/issues/2823 + self.vm.add_args('-append', 'root=/dev/vda nokaslr') + + self.vm.launch() + # Wait for host VM boot to complete. + wait_for_console_pattern(self, 'Welcome to Buildroot', + failure_message='Synchronous Exception at') + exec_command_and_wait_for_pattern(self, 'root', '#') + + test_realms_guest(self) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_sbsaref.py b/tests/functional/test_aarch64_sbsaref.py new file mode 100755 index 00000000000..e6a55aecfac --- /dev/null +++ b/tests/functional/test_aarch64_sbsaref.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a kernel and checks the console +# +# Copyright (c) 2023-2024 Linaro Ltd. +# +# Authors: +# Philippe Mathieu-Daudé +# Marcin Juszkiewicz +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import interrupt_interactive_console_until_pattern + + +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.12.0 + - Tianocore EDK2 edk2-stable202411 + - Tianocore EDK2-platforms 4b3530d + + """ + + # Secure BootRom (TF-A code) + fs0_path = test.uncompress(Aarch64SbsarefMachine.ASSET_FLASH0) + + # Non-secure rom (UEFI and EFI variables) + fs1_path = test.uncompress(Aarch64SbsarefMachine.ASSET_FLASH1) + + 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/' + '20241122-189881/edk2/SBSA_FLASH0.fd.xz'), + '76eb89d42eebe324e4395329f47447cda9ac920aabcf99aca85424609c3384a5') + + ASSET_FLASH1 = Asset( + ('https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/' + '20241122-189881/edk2/SBSA_FLASH1.fd.xz'), + 'f850f243bd8dbd49c51e061e0f79f1697546938f454aeb59ab7d93e5f0d412fc') + + 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.12.0(release):") + wait_for_console_pattern(self, "BL1: Booting BL2") + + # Trusted Boot Firmware + wait_for_console_pattern(self, "BL2: v2.12.0(release)") + wait_for_console_pattern(self, "Booting BL31") + + # EL3 Runtime Software + wait_for_console_pattern(self, "BL31: v2.12.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 00000000000..6108ec65a54 --- /dev/null +++ b/tests/functional/test_aarch64_sbsaref_alpine.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a kernel and checks the console +# +# Copyright (c) 2023-2024 Linaro Ltd. +# +# Authors: +# Philippe Mathieu-Daudé +# Marcin Juszkiewicz +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset, skipSlowTest +from qemu_test import wait_for_console_pattern +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") + + @skipSlowTest() # 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 00000000000..26dfc5878bb --- /dev/null +++ b/tests/functional/test_aarch64_sbsaref_freebsd.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a kernel and checks the console +# +# Copyright (c) 2023-2024 Linaro Ltd. +# +# Authors: +# Philippe Mathieu-Daudé +# Marcin Juszkiewicz +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset, skipSlowTest +from qemu_test import wait_for_console_pattern +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") + + @skipSlowTest() # Test might timeout due to PAuth emulation + def test_sbsaref_freebsd14_max_pauth_impdef(self): + self.boot_freebsd14("max,pauth-impdef=on") + + @skipSlowTest() # 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_smmu.py b/tests/functional/test_aarch64_smmu.py new file mode 100755 index 00000000000..c65d0f28178 --- /dev/null +++ b/tests/functional/test_aarch64_smmu.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# SMMUv3 Functional tests +# +# Copyright (c) 2021 Red Hat, Inc. +# +# Author: +# Eric Auger +# +# 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 LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import BUILD_DIR +from qemu.utils import kvm_available + + +class SMMU(LinuxKernelTest): + + default_kernel_params = ('earlyprintk=pl011,0x9000000 no_timer_check ' + 'printk.time=1 rd_NO_PLYMOUTH net.ifnames=0 ' + 'console=ttyAMA0 rd.rescue') + IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on' + kernel_path = None + initrd_path = None + kernel_params = None + + GUEST_PORT = 8080 + + def set_up_boot(self, path): + 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', + f'file={path},if=none,cache=writethrough,id=drv0,snapshot=on') + + self.vm.add_args('-netdev', + 'user,id=n1,hostfwd=tcp:127.0.0.1:0-:%d' % + self.GUEST_PORT) + self.vm.add_args('-device', 'virtio-net,netdev=n1' + self.IOMMU_ADDON) + + def common_vm_setup(self, kernel, initrd, disk): + self.require_accelerator("kvm") + self.require_netdev('user') + self.set_machine("virt") + self.vm.add_args('-m', '1G') + self.vm.add_args("-accel", "kvm") + self.vm.add_args("-cpu", "host") + self.vm.add_args("-machine", "iommu=smmuv3") + self.vm.add_args("-d", "guest_errors") + 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.kernel_path = kernel.fetch() + self.initrd_path = initrd.fetch() + self.set_up_boot(disk.fetch()) + + def run_and_check(self, filename, hashsum): + self.vm.add_args('-initrd', self.initrd_path) + self.vm.add_args('-append', self.kernel_params) + self.launch_kernel(self.kernel_path, initrd=self.initrd_path, + wait_for='attach it to a bug report.') + prompt = '# ' + # Fedora 33 requires 'return' to be pressed to enter the shell. + # There seems to be a small race between detecting the previous ':' + # and sending the newline, so we need to add a small delay here. + self.wait_for_console_pattern(':') + time.sleep(0.2) + exec_command_and_wait_for_pattern(self, '\n', prompt) + exec_command_and_wait_for_pattern(self, 'cat /proc/cmdline', + self.kernel_params) + + # Checking for SMMU enablement: + self.log.info("Checking whether SMMU has been enabled...") + exec_command_and_wait_for_pattern(self, 'dmesg | grep smmu', + 'arm-smmu-v3') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, + 'find /sys/kernel/iommu_groups/ -type l', + 'devices/0000:00:') + self.wait_for_console_pattern(prompt) + + # Copy a file (checked later), umount afterwards to drop disk cache: + self.log.info("Checking hard disk...") + exec_command_and_wait_for_pattern(self, + "while ! (dmesg -c | grep vda:) ; do sleep 1 ; done", + "vda2") + exec_command_and_wait_for_pattern(self, 'mount /dev/vda2 /sysroot', + 'mounted filesystem') + exec_command_and_wait_for_pattern(self, 'cp /bin/vi /sysroot/root/vi', + prompt) + exec_command_and_wait_for_pattern(self, 'umount /sysroot', prompt) + # Switch from initrd to the cloud image filesystem: + exec_command_and_wait_for_pattern(self, 'mount /dev/vda2 /sysroot', + prompt) + exec_command_and_wait_for_pattern(self, + ('for d in dev proc sys run ; do ' + 'mount -o bind /$d /sysroot/$d ; done'), prompt) + exec_command_and_wait_for_pattern(self, 'chroot /sysroot', prompt) + # Check files on the hard disk: + exec_command_and_wait_for_pattern(self, + ('if diff -q /root/vi /usr/bin/vi ; then echo "file" "ok" ; ' + 'else echo "files differ"; fi'), 'file ok') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, f'sha256sum {filename}', + hashsum) + + # Check virtio-net via HTTP: + exec_command_and_wait_for_pattern(self, 'dhclient eth0', prompt) + self.check_http_download(filename, hashsum, self.GUEST_PORT) + + + # 5.3 kernel without RIL # + + ASSET_KERNEL_F31 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/31/Server/aarch64/os/images/pxeboot/vmlinuz'), + '3ae07fcafbfc8e4abeb693035a74fe10698faae15e9ccd48882a9167800c1527') + + ASSET_INITRD_F31 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/31/Server/aarch64/os/images/pxeboot/initrd.img'), + '9f3146b28bc531c689f3c5f114cb74e4bd7bd548e0ba19fa77921d8bd256755a') + + ASSET_DISK_F31 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Cloud/aarch64/images/Fedora-Cloud-Base-31-1.9.aarch64.qcow2'), + '1e18d9c0cf734940c4b5d5ec592facaed2af0ad0329383d5639c997fdf16fe49') + + F31_FILENAME = '/boot/initramfs-5.3.7-301.fc31.aarch64.img' + F31_HSUM = '1a4beec6607d94df73d9dd1b4985c9c23dd0fdcf4e6ca1351d477f190df7bef9' + + def test_smmu_noril(self): + self.common_vm_setup(self.ASSET_KERNEL_F31, self.ASSET_INITRD_F31, + self.ASSET_DISK_F31) + self.kernel_params = self.default_kernel_params + self.run_and_check(self.F31_FILENAME, self.F31_HSUM) + + def test_smmu_noril_passthrough(self): + self.common_vm_setup(self.ASSET_KERNEL_F31, self.ASSET_INITRD_F31, + self.ASSET_DISK_F31) + self.kernel_params = (self.default_kernel_params + + ' iommu.passthrough=on') + self.run_and_check(self.F31_FILENAME, self.F31_HSUM) + + def test_smmu_noril_nostrict(self): + self.common_vm_setup(self.ASSET_KERNEL_F31, self.ASSET_INITRD_F31, + self.ASSET_DISK_F31) + self.kernel_params = (self.default_kernel_params + + ' iommu.strict=0') + self.run_and_check(self.F31_FILENAME, self.F31_HSUM) + + + # 5.8 kernel featuring range invalidation + # >= v5.7 kernel + + ASSET_KERNEL_F33 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/33/Server/aarch64/os/images/pxeboot/vmlinuz'), + 'd8b1e6f7241f339d8e7609c456cf0461ffa4583ed07e0b55c7d1d8a0c154aa89') + + ASSET_INITRD_F33 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/33/Server/aarch64/os/images/pxeboot/initrd.img'), + '92513f55295c2c16a777f7b6c35ccd70a438e9e1e40b6ba39e0e60900615b3df') + + ASSET_DISK_F33 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/33/Cloud/aarch64/images/Fedora-Cloud-Base-33-1.2.aarch64.qcow2'), + 'e7f75cdfd523fe5ac2ca9eeece68edc1a81f386a17f969c1d1c7c87031008a6b') + + F33_FILENAME = '/boot/initramfs-5.8.15-301.fc33.aarch64.img' + F33_HSUM = '079cfad0caa82e84c8ca1fb0897a4999dd769f262216099f518619e807a550d9' + + def test_smmu_ril(self): + self.common_vm_setup(self.ASSET_KERNEL_F33, self.ASSET_INITRD_F33, + self.ASSET_DISK_F33) + self.kernel_params = self.default_kernel_params + self.run_and_check(self.F33_FILENAME, self.F33_HSUM) + + def test_smmu_ril_passthrough(self): + self.common_vm_setup(self.ASSET_KERNEL_F33, self.ASSET_INITRD_F33, + self.ASSET_DISK_F33) + self.kernel_params = (self.default_kernel_params + + ' iommu.passthrough=on') + self.run_and_check(self.F33_FILENAME, self.F33_HSUM) + + def test_smmu_ril_nostrict(self): + self.common_vm_setup(self.ASSET_KERNEL_F33, self.ASSET_INITRD_F33, + self.ASSET_DISK_F33) + self.kernel_params = (self.default_kernel_params + + ' iommu.strict=0') + self.run_and_check(self.F33_FILENAME, self.F33_HSUM) + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_aarch64_tcg_plugins.py b/tests/functional/test_aarch64_tcg_plugins.py new file mode 100755 index 00000000000..cb7e9298fb8 --- /dev/null +++ b/tests/functional/test_aarch64_tcg_plugins.py @@ -0,0 +1,118 @@ +#!/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.machine.machine import VMLaunchFailure +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 VMLaunchFailure as excp: + if "plugin interface not enabled in this build" in excp.output: + self.skipTest("TCG plugins not enabled") + else: + self.log.info(f"unhandled launch failure: {excp.output}") + raise excp + + 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 = 'Please append a correct "root=" boot option' + + plugin_log = tempfile.NamedTemporaryFile(mode="r+t", prefix="plugin", + suffix=".log") + + self.run_vm(kernel_path, kernel_command_line, + self.plugin_file('libinsn'), 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 = 'Please append a correct "root=" boot option' + + plugin_log = tempfile.NamedTemporaryFile(mode="r+t", prefix="plugin", + suffix=".log") + + self.run_vm(kernel_path, kernel_command_line, + self.plugin_file('libinsn'), 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 00000000000..75adc8acb83 --- /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 00000000000..4d0ad90ff89 --- /dev/null +++ b/tests/functional/test_aarch64_virt.py @@ -0,0 +1,135 @@ +#!/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 logging +from subprocess import check_call, DEVNULL + +from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern, get_qemu_img + + +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.require_accelerator("tcg") + + self.vm.set_console() + 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', self.build_file('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. + """ + self.set_machine('virt') + self.require_accelerator("tcg") + + logger = logging.getLogger('aarch64_virt') + + kernel_path = self.ASSET_KERNEL.fetch() + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + 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 = self.scratch_file('scratch.qcow2') + qemu_img = get_qemu_img(self) + check_call([qemu_img, 'create', '-f', 'qcow2', image_path, '8M'], + stdout=DEVNULL, stderr=DEVNULL) + + # Add the device + self.vm.add_args('-blockdev', + "driver=qcow2," + "file.driver=file," + f"file.filename={image_path},node-name=scratch") + self.vm.add_args('-device', + 'virtio-blk-device,drive=scratch') + + self.vm.launch() + + ps1='#' + self.wait_for_console_pattern('login:') + + commands = [ + ('root', ps1), + ('cat /proc/interrupts', ps1), + ('cat /proc/self/maps', ps1), + ('uname -a', ps1), + ('dd if=/dev/hwrng of=/dev/vda bs=512 count=4', ps1), + ('md5sum /dev/vda', ps1), + ('halt -n', 'reboot: System halted') + ] + + for cmd, pattern in commands: + exec_command_and_wait_for_pattern(self, cmd, pattern) + + 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_aarch64_virt_gpu.py b/tests/functional/test_aarch64_virt_gpu.py new file mode 100755 index 00000000000..38447278579 --- /dev/null +++ b/tests/functional/test_aarch64_virt_gpu.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# +# Functional tests for the various graphics modes we can support. +# +# Copyright (c) 2024, 2025 Linaro Ltd. +# +# Author: +# Alex Bennée +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu.machine.machine import VMLaunchFailure + +from qemu_test import Asset +from qemu_test import exec_command_and_wait_for_pattern as ec_and_wait +from qemu_test import skipIfMissingCommands + +from qemu_test.linuxkernel import LinuxKernelTest + +from re import search +from subprocess import check_output, CalledProcessError + +class Aarch64VirtGPUMachine(LinuxKernelTest): + + ASSET_VIRT_GPU_KERNEL = Asset( + 'https://fileserver.linaro.org/s/ce5jXBFinPxtEdx/' + 'download?path=%2F&files=' + 'Image.6.12.16.aarch64', + '7888c51c55d37e86bbbdeb5acea9f08c34e6b0f03c1f5b2463285f6a6f6eec8b') + + ASSET_VIRT_GPU_ROOTFS = Asset( + 'https://fileserver.linaro.org/s/ce5jXBFinPxtEdx/' + 'download?path=%2F&files=' + 'rootfs.aarch64.ext2.zstd', + 'd45118c899420b7e673f1539a37a35480134b3e36e3a59e2cb69b1781cbb14ef') + + def _launch_virt_gpu(self, gpu_device): + + self.set_machine('virt') + self.require_accelerator("tcg") + + kernel_path = self.ASSET_VIRT_GPU_KERNEL.fetch() + image_path = self.uncompress(self.ASSET_VIRT_GPU_ROOTFS, format="zstd") + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0 root=/dev/vda') + + self.vm.add_args("-accel", "tcg") + self.vm.add_args("-cpu", "cortex-a72") + self.vm.add_args("-machine", "virt,gic-version=max", + '-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.add_args("-smp", "2", "-m", "2048") + self.vm.add_args("-device", gpu_device) + self.vm.add_args("-display", "egl-headless") + self.vm.add_args("-display", "dbus,gl=on") + + self.vm.add_args("-device", "virtio-blk-device,drive=hd0") + self.vm.add_args("-blockdev", + "driver=raw,file.driver=file," + "node-name=hd0,read-only=on," + f"file.filename={image_path}") + self.vm.add_args("-snapshot") + + try: + self.vm.launch() + except VMLaunchFailure as excp: + if "old virglrenderer, blob resources unsupported" in excp.output: + self.skipTest("No blob support for virtio-gpu") + elif "old virglrenderer, venus unsupported" in excp.output: + self.skipTest("No venus support for virtio-gpu") + elif "egl: no drm render node available" in excp.output: + self.skipTest("Can't access host DRM render node") + elif "'type' does not accept value 'egl-headless'" in excp.output: + self.skipTest("egl-headless support is not available") + elif "'type' does not accept value 'dbus'" in excp.output: + self.skipTest("dbus display support is not available") + else: + self.log.info("unhandled launch failure: %s", excp.output) + raise excp + + self.wait_for_console_pattern('buildroot login:') + ec_and_wait(self, 'root', '#') + + def _run_virt_weston_test(self, cmd, fail = None): + + # make it easier to detect successful return to shell + PS1 = 'RES=[$?] # ' + OK_CMD = 'RES=[0] # ' + + ec_and_wait(self, 'export XDG_RUNTIME_DIR=/tmp', '#') + ec_and_wait(self, f"export PS1='{PS1}'", OK_CMD) + full_cmd = f"weston -B headless --renderer gl --shell kiosk -- {cmd}" + ec_and_wait(self, full_cmd, OK_CMD, fail) + + @skipIfMissingCommands('zstd') + def test_aarch64_virt_with_virgl_gpu(self): + + self.require_device('virtio-gpu-gl-pci') + + self._launch_virt_gpu("virtio-gpu-gl-pci") + + # subset of the glmark tests + tests = " ".join([f"-b {test}" for test in + ["build", "texture", "shading", + "bump", "desktop", "buffer"]]) + + self._run_virt_weston_test("glmark2-wayland --validate " + tests) + + @skipIfMissingCommands('zstd') + def test_aarch64_virt_with_virgl_blobs_gpu(self): + + self.require_device('virtio-gpu-gl-pci') + + self._launch_virt_gpu("virtio-gpu-gl-pci,hostmem=4G,blob=on") + self._run_virt_weston_test("glmark2-wayland -b:duration=1.0") + + @skipIfMissingCommands('zstd') + @skipIfMissingCommands('vulkaninfo') + def test_aarch64_virt_with_vulkan_gpu(self): + + self.require_device('virtio-gpu-gl-pci') + + try: + vk_info = check_output(["vulkaninfo", "--summary"], + encoding="utf-8") + except CalledProcessError as excp: + self.skipTest(f"Miss-configured host Vulkan: {excp.output}") + + if search(r"driverID\s+=\s+DRIVER_ID_NVIDIA_PROPRIETARY", vk_info): + self.skipTest("Test skipped on NVIDIA proprietary driver") + + self._launch_virt_gpu("virtio-gpu-gl-pci,hostmem=4G,blob=on,venus=on") + self._run_virt_weston_test("vkmark -b:duration=1.0", + "debug: stuck in fence wait with iter at") + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_aarch64_xen.py b/tests/functional/test_aarch64_xen.py new file mode 100755 index 00000000000..339904221b0 --- /dev/null +++ b/tests/functional/test_aarch64_xen.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Xen hypervisor with a domU kernel and +# checks the console output is vaguely sane . +# +# Copyright (c) 2020 Linaro +# +# Author: +# Alex Bennée +# +# 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. + +from qemu_test import Asset, LinuxKernelTest, wait_for_console_pattern + + +class BootXen(LinuxKernelTest): + """ + Boots a Xen hypervisor with a Linux DomU kernel. + """ + + timeout = 90 + XEN_COMMON_COMMAND_LINE = 'dom0_mem=128M loglvl=all guest_loglvl=all' + + ASSET_KERNEL = Asset( + ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/' + 'download?path=%2F&files=linux-5.9.9-arm64-ajb'), + '00366fa51ea957c19462d2e2aefd480bef80ce727120e714ae48e0c88f261edb') + + def launch_xen(self, xen_path): + """ + Launch Xen with a dom0 guest kernel + """ + self.set_machine('virt') + self.cpu = "cortex-a57" + self.kernel_path = self.ASSET_KERNEL.fetch() + self.log.info("launch with xen_path: %s", xen_path) + + self.vm.set_console() + + self.vm.add_args('-machine', 'virtualization=on', + '-m', '768', + '-kernel', xen_path, + '-append', self.XEN_COMMON_COMMAND_LINE, + '-device', + 'guest-loader,addr=0x47000000,kernel=%s,bootargs=console=hvc0' + % (self.kernel_path)) + + self.vm.launch() + + console_pattern = 'VFS: Cannot open root device' + wait_for_console_pattern(self, console_pattern, "Panic on CPU 0:") + + ASSET_XEN_4_11 = Asset( + ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/download?path=%2F&' + 'files=xen-hypervisor-4.11-arm64_4.11.4%2B37-g3263f257ca-1_arm64.deb'), + 'b745c2631342f9fcc0147ddc364edb62c20ecfebd430e5a3546e7d7c6891c0bc') + + def test_arm64_xen_411_and_dom0(self): + # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ + xen_path = self.archive_extract(self.ASSET_XEN_4_11, format='deb', + member="boot/xen-4.11-arm64") + self.launch_xen(xen_path) + + ASSET_XEN_4_14 = Asset( + ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/download?path=%2F&' + 'files=xen-hypervisor-4.14-arm64_4.14.0%2B80-gd101b417b7-1_arm64.deb'), + 'e930a3293248edabd367d5b4b3b6448b9c99c057096ea8b47228a7870661d5cb') + + def test_arm64_xen_414_and_dom0(self): + # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ + xen_path = self.archive_extract(self.ASSET_XEN_4_14, format='deb', + member="boot/xen-4.14-arm64") + self.launch_xen(xen_path) + + ASSET_XEN_4_15 = Asset( + ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/download?path=%2F&' + 'files=xen-upstream-4.15-unstable.deb'), + '2a9a8af8acf0231844657cc28baab95bd918b0ee2d493ee4ee6f8846e1358bc9') + + def test_arm64_xen_415_and_dom0(self): + xen_path = self.archive_extract(self.ASSET_XEN_4_15, format='deb', + member="boot/xen-4.15-unstable") + self.launch_xen(xen_path) + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_aarch64_xlnx_versal.py b/tests/functional/test_aarch64_xlnx_versal.py new file mode 100755 index 00000000000..4b9c49e5d64 --- /dev/null +++ b/tests/functional/test_aarch64_xlnx_versal.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + +class XlnxVersalVirtMachine(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('http://ports.ubuntu.com/ubuntu-ports/dists/bionic-updates/main/' + 'installer-arm64/20101020ubuntu543.19/images/netboot/' + 'ubuntu-installer/arm64/linux'), + 'ce54f74ab0b15cfd13d1a293f2d27ffd79d8a85b7bb9bf21093ae9513864ac79') + + ASSET_INITRD = Asset( + ('http://ports.ubuntu.com/ubuntu-ports/dists/bionic-updates/main/' + 'installer-arm64/20101020ubuntu543.19/images/netboot/' + '/ubuntu-installer/arm64/initrd.gz'), + 'e7a5e716b6f516d8be315c06e7331aaf16994fe4222e0e7cfb34bc015698929e') + + def test_aarch64_xlnx_versal_virt(self): + self.set_machine('xlnx-versal-virt') + kernel_path = self.ASSET_KERNEL.fetch() + initrd_path = self.ASSET_INITRD.fetch() + + self.vm.set_console() + self.vm.add_args('-m', '2G', + '-accel', 'tcg', + '-kernel', kernel_path, + '-initrd', initrd_path) + self.vm.launch() + self.wait_for_console_pattern('Checked W+X mappings: passed') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_acpi_bits.py b/tests/functional/test_acpi_bits.py new file mode 100755 index 00000000000..8e0563a97b1 --- /dev/null +++ b/tests/functional/test_acpi_bits.py @@ -0,0 +1,340 @@ +#!/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 os +import re +import shutil +import subprocess + +from typing import ( + List, + Optional, + Sequence, +) +from qemu.machine import QEMUMachine +from qemu_test import (QemuSystemTest, Asset, skipIfMissingCommands, + skipIfNotMachine) + + +# default timeout of 120 secs is sometimes not enough for bits test. +BITS_TIMEOUT = 200 + +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 + +@skipIfMissingCommands("xorriso", "mformat") +@skipIfNotMachine("x86_64") +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._debugcon_addr = '0x403' + self._debugcon_log = 'debugcon-log.txt' + + 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. + """ + bits_config_file = self.data_file('acpi-bits', + 'bits-config', + 'bits-cfg.txt') + target_config_dir = self.scratch_file('bits-%d' % + self.BITS_INTERNAL_VER, + 'boot') + self.assertTrue(os.path.exists(bits_config_file)) + self.assertTrue(os.path.exists(target_config_dir)) + shutil.copy2(bits_config_file, target_config_dir) + self.logger.info('copied config file %s to %s', + bits_config_file, target_config_dir) + + def copy_test_scripts(self): + """copies the python test scripts into bits. """ + + bits_test_dir = self.data_file('acpi-bits', 'bits-tests') + target_test_dir = self.scratch_file('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 = self.scratch_file('grub-inst-x86_64-efi') + grub_i386_mods = self.scratch_file('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 = self.scratch_file('bits-%d' % self.BITS_INTERNAL_VER) + iso_file = self.scratch_file('bits-%d.iso' % self.BITS_INTERNAL_VER) + mkrescue_script = self.scratch_file('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 + + prebuiltDir = self.scratch_file('prebuilt') + if not os.path.isdir(prebuiltDir): + os.mkdir(prebuiltDir, mode=0o775) + + bits_zip_file = self.scratch_file('prebuilt', + 'bits-%d-%s.zip' + %(self.BITS_INTERNAL_VER, + self.BITS_COMMIT_HASH)) + grub_tar_file = self.scratch_file('prebuilt', + 'bits-%d-%s-grub.tar.gz' + %(self.BITS_INTERNAL_VER, + self.BITS_COMMIT_HASH)) + + # extract the bits artifact in the temp working directory + self.archive_extract(self.ASSET_BITS, sub_dir='prebuilt', format='zip') + + # extract the bits software in the temp working directory + self.archive_extract(bits_zip_file) + self.archive_extract(grub_tar_file) + + 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 = self.scratch_file(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 = self.scratch_file('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 00000000000..c5d71819531 --- /dev/null +++ b/tests/functional/test_alpha_clipper.py @@ -0,0 +1,34 @@ +#!/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 + +from qemu_test import LinuxKernelTest, Asset + + +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 = self.uncompress(self.ASSET_KERNEL, format="gz") + + 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_alpha_replay.py b/tests/functional/test_alpha_replay.py new file mode 100755 index 00000000000..24a17ef5904 --- /dev/null +++ b/tests/functional/test_alpha_replay.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an Alpha machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class AlphaReplay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + ('http://archive.debian.org/debian/dists/lenny/main/installer-alpha/' + '20090123lenny10/images/cdrom/vmlinuz'), + '34f53da3fa32212e4f00b03cb944b2ad81c06bc8faaf9b7193b2e544ceeca576') + + def test_clipper(self): + self.set_machine('clipper') + kernel_path = self.uncompress(self.ASSET_KERNEL, format='gz') + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=9, + args=('-nodefaults', )) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_arm_aspeed_ast1030.py b/tests/functional/test_arm_aspeed_ast1030.py new file mode 100755 index 00000000000..77037f01793 --- /dev/null +++ b/tests/functional/test_arm_aspeed_ast1030.py @@ -0,0 +1,73 @@ +#!/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 + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern + + +class AST1030Machine(LinuxKernelTest): + + ASSET_ZEPHYR_3_00 = Asset( + ('https://github.com/AspeedTech-BMC' + '/zephyr/releases/download/v00.03.00/ast1030-evb-demo.zip'), + '37fe3ecd4a1b9d620971a15b96492a81093435396eeac69b6f3e384262ff555f') + + def test_ast1030_zephyros_3_00(self): + self.set_machine('ast1030-evb') + + kernel_name = "ast1030-evb-demo/zephyr.elf" + kernel_file = self.archive_extract( + self.ASSET_ZEPHYR_3_00, member=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') + + kernel_name = "ast1030-evb-demo/zephyr.bin" + kernel_file = self.archive_extract( + self.ASSET_ZEPHYR_1_07, member=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:~$") + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_aspeed_ast2500.py b/tests/functional/test_arm_aspeed_ast2500.py new file mode 100755 index 00000000000..6923fe87017 --- /dev/null +++ b/tests/functional/test_arm_aspeed_ast2500.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, exec_command_and_wait_for_pattern +from aspeed import AspeedTest + + +class AST2500Machine(AspeedTest): + + ASSET_BR2_202411_AST2500_FLASH = Asset( + ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2500-evb/buildroot-2024.11/flash.img'), + '641e6906c18c0f19a2aeb48099d66d4771929c361001d554d0d45c667413e13a') + + def test_arm_ast2500_evb_buildroot(self): + self.set_machine('ast2500-evb') + + image_path = self.ASSET_BR2_202411_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_SDK_V906_AST2500 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2500-default-obmc.tar.gz', + '542db84645b4efd8aed50385d7f4dd1caff379a987032311cfa7b563a3addb2a') + + def test_arm_ast2500_evb_sdk(self): + self.set_machine('ast2500-evb') + + self.archive_extract(self.ASSET_SDK_V906_AST2500) + + self.do_test_arm_aspeed_sdk_start( + self.scratch_file("ast2500-default", "image-bmc")) + + self.wait_for_console_pattern('ast2500-default login:') + + +if __name__ == '__main__': + AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_ast2600.py b/tests/functional/test_arm_aspeed_ast2600.py new file mode 100755 index 00000000000..fdae4c939d8 --- /dev/null +++ b/tests/functional/test_arm_aspeed_ast2600.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import time +import tempfile +import subprocess + +from qemu_test import Asset +from aspeed import AspeedTest +from qemu_test import exec_command_and_wait_for_pattern, skipIfMissingCommands + + +class AST2600Machine(AspeedTest): + + ASSET_BR2_202411_AST2600_FLASH = Asset( + ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2600-evb/buildroot-2024.11/flash.img'), + '4bb2f3dfdea31199b51d66b42f686dc5374c144a7346fdc650194a5578b73609') + + def test_arm_ast2600_evb_buildroot(self): + self.set_machine('ast2600-evb') + + image_path = self.ASSET_BR2_202411_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') + + @skipIfMissingCommands('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() + + ASSET_SDK_V906_AST2600 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2600-default-obmc.tar.gz', + '768d76e247896ad78c154b9cff4f766da2ce65f217d620b286a4a03a8a4f68f5') + + def test_arm_ast2600_evb_sdk(self): + self.set_machine('ast2600-evb') + + self.archive_extract(self.ASSET_SDK_V906_AST2600) + + 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.scratch_file("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.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) + +if __name__ == '__main__': + AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_bletchley.py b/tests/functional/test_arm_aspeed_bletchley.py new file mode 100644 index 00000000000..5a60b24b3d2 --- /dev/null +++ b/tests/functional/test_arm_aspeed_bletchley.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + + +class BletchleyMachine(AspeedTest): + + ASSET_BLETCHLEY_FLASH = Asset( + 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/bletchley-bmc/openbmc-20250128071329/obmc-phosphor-image-bletchley-20250128071329.static.mtd.xz', + 'db21d04d47d7bb2a276f59d308614b4dfb70b9c7c81facbbca40a3977a2d8844') + + def test_arm_ast2600_bletchley_openbmc(self): + image_path = self.uncompress(self.ASSET_BLETCHLEY_FLASH) + + self.do_test_arm_aspeed_openbmc('bletchley-bmc', image=image_path, + uboot='2019.04', cpu_id='0xf00', + soc='AST2600 rev A3') + +if __name__ == '__main__': + AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_palmetto.py b/tests/functional/test_arm_aspeed_palmetto.py new file mode 100755 index 00000000000..ff0b821be65 --- /dev/null +++ b/tests/functional/test_arm_aspeed_palmetto.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + + +class PalmettoMachine(AspeedTest): + + ASSET_PALMETTO_FLASH = Asset( + 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/palmetto-bmc/openbmc-20250128071432/obmc-phosphor-image-palmetto-20250128071432.static.mtd', + 'bce7c392eec75c707a91cfc8fad7ca9a69d7e4f10df936930d65c1cb9897ac81') + + def test_arm_ast2400_palmetto_openbmc(self): + image_path = self.ASSET_PALMETTO_FLASH.fetch() + + self.do_test_arm_aspeed_openbmc('palmetto-bmc', image=image_path, + uboot='2019.04', cpu_id='0x0', + soc='AST2400 rev A1') + +if __name__ == '__main__': + AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_rainier.py b/tests/functional/test_arm_aspeed_rainier.py new file mode 100755 index 00000000000..602d6194ac8 --- /dev/null +++ b/tests/functional/test_arm_aspeed_rainier.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + +class RainierMachine(AspeedTest): + + 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') + + ASSET_DEBIAN_LINUX_ARMHF_DEB = Asset( + ('http://snapshot.debian.org/archive/debian/20220606T211338Z/pool/main/l/linux/linux-image-5.17.0-2-armmp_5.17.6-1%2Bb1_armhf.deb'), + '8acb2b4439faedc2f3ed4bdb2847ad4f6e0491f73debaeb7f660c8abe4dcdc0e') + + def test_arm_debian_kernel_boot(self): + self.set_machine('rainier-bmc') + + kernel_path = self.archive_extract( + self.ASSET_DEBIAN_LINUX_ARMHF_DEB, + member='boot/vmlinuz-5.17.0-2-armmp') + dtb_path = self.archive_extract( + self.ASSET_DEBIAN_LINUX_ARMHF_DEB, + member='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, + '-dtb', dtb_path, + '-net', 'nic') + self.vm.launch() + + self.wait_for_console_pattern("Booting Linux on physical CPU 0xf00") + self.wait_for_console_pattern("SMP: Total of 2 processors activated") + self.wait_for_console_pattern("No filesystem could mount root") + + +if __name__ == '__main__': + AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_romulus.py b/tests/functional/test_arm_aspeed_romulus.py new file mode 100755 index 00000000000..0447212bbf0 --- /dev/null +++ b/tests/functional/test_arm_aspeed_romulus.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + + +class RomulusMachine(AspeedTest): + + ASSET_ROMULUS_FLASH = Asset( + 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/romulus-bmc/openbmc-20250128071340/obmc-phosphor-image-romulus-20250128071340.static.mtd', + '6d031376440c82ed9d087d25e9fa76aea75b42f80daa252ec402c0bc3cf6cf5b') + + def test_arm_ast2500_romulus_openbmc(self): + image_path = self.ASSET_ROMULUS_FLASH.fetch() + + self.do_test_arm_aspeed_openbmc('romulus-bmc', image=image_path, + uboot='2019.04', cpu_id='0x0', + soc='AST2500 rev A1') + +if __name__ == '__main__': + AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_witherspoon.py b/tests/functional/test_arm_aspeed_witherspoon.py new file mode 100644 index 00000000000..51a2d47af28 --- /dev/null +++ b/tests/functional/test_arm_aspeed_witherspoon.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + + +class WitherspoonMachine(AspeedTest): + + ASSET_WITHERSPOON_FLASH = Asset( + 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/witherspoon-bmc/openbmc-20240618035022/obmc-phosphor-image-witherspoon-20240618035022.ubi.mtd', + '937d9ed449ea6c6cbed983519088a42d0cafe276bcfe4fce07772ca6673f9213') + + def test_arm_ast2500_witherspoon_openbmc(self): + image_path = self.ASSET_WITHERSPOON_FLASH.fetch() + + self.do_test_arm_aspeed_openbmc('witherspoon-bmc', image=image_path, + uboot='2016.07', cpu_id='0x0', + soc='AST2500 rev A1') + +if __name__ == '__main__': + AspeedTest.main() diff --git a/tests/functional/test_arm_bflt.py b/tests/functional/test_arm_bflt.py new file mode 100755 index 00000000000..f273fc83546 --- /dev/null +++ b/tests/functional/test_arm_bflt.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# +# Test the bFLT loader format +# +# Copyright (C) 2019 Philippe Mathieu-Daudé +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import bz2 + +from qemu_test import QemuUserTest, Asset +from qemu_test import skipIfMissingCommands, skipUntrustedTest + + +class LoadBFLT(QemuUserTest): + + ASSET_ROOTFS = Asset( + ('https://elinux.org/images/5/51/Stm32_mini_rootfs.cpio.bz2'), + 'eefb788e4980c9e8d6c9d60ce7d15d4da6bf4fbc6a80f487673824600d5ba9cc') + + @skipIfMissingCommands('cpio') + @skipUntrustedTest() + def test_stm32(self): + # See https://elinux.org/STM32#User_Space + rootfs_path_bz2 = self.ASSET_ROOTFS.fetch() + busybox_path = self.scratch_file("bin", "busybox") + + with bz2.open(rootfs_path_bz2, 'rb') as cpio_handle: + self.archive_extract(cpio_handle, format="cpio") + + 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 00000000000..8bed64b702f --- /dev/null +++ b/tests/functional/test_arm_bpim2u.py @@ -0,0 +1,180 @@ +#!/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 import skipBigDataTest +from qemu_test.utils import image_pow2ceil_expand + + +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') + kernel_path = self.archive_extract( + self.ASSET_DEB, member='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.archive_extract(self.ASSET_DEB, member=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') + kernel_path = self.archive_extract( + self.ASSET_DEB, member='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.archive_extract(self.ASSET_DEB, member=dtb_path) + initrd_path = self.uncompress(self.ASSET_INITRD) + + 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.archive_extract( + self.ASSET_DEB, member='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.archive_extract(self.ASSET_DEB, member=dtb_path) + rootfs_path = self.uncompress(self.ASSET_ROOTFS) + 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) + + @skipBigDataTest() + def test_arm_bpim2u_openwrt_22_03_3(self): + self.set_machine('bpim2u') + self.require_netdev('user') + + # This test download a 8.9 MiB compressed image and expand it + # to 127 MiB. + image_path = self.uncompress(self.ASSET_SD_IMAGE) + 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 00000000000..21a1a596a0c --- /dev/null +++ b/tests/functional/test_arm_canona1100.py @@ -0,0 +1,37 @@ +#!/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 + + +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') + + bios = self.archive_extract(self.ASSET_BIOS, + member="day18/barebox.canon-a1100.bin") + self.vm.set_console() + self.vm.add_args('-bios', bios) + 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 00000000000..fe1be3d0792 --- /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 + + +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_cubieboard.py b/tests/functional/test_arm_cubieboard.py new file mode 100755 index 00000000000..b536c2f77a0 --- /dev/null +++ b/tests/functional/test_arm_cubieboard.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import interrupt_interactive_console_until_pattern +from qemu_test import skipBigDataTest +from qemu_test.utils import image_pow2ceil_expand + + +class CubieboardMachine(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-armv5.cpio.gz'), + '334b8d256db67a3f2b3ad070aa08b5ade39624e0e7e35b02f4359a577bc8f39b') + + ASSET_SATA_ROOTFS = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv5.ext2.gz'), + '17fc750da568580b39372133051ef2f0a963c0c0b369b845614442d025701745') + + ASSET_OPENWRT = Asset( + ('https://downloads.openwrt.org/releases/22.03.2/targets/sunxi/cortexa8/' + 'openwrt-22.03.2-sunxi-cortexa8-cubietech_a10-cubieboard-ext4-sdcard.img.gz'), + '94b5ecbfbc0b3b56276e5146b899eafa2ac5dc2d08733d6705af9f144f39f554') + + def test_arm_cubieboard_initrd(self): + self.set_machine('cubieboard') + kernel_path = self.archive_extract( + self.ASSET_DEB, member='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.archive_extract(self.ASSET_DEB, member=dtb_path) + initrd_path = self.uncompress(self.ASSET_INITRD) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'usbcore.nousb ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun4i/sun5i') + exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', + 'system-control@1c00000') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + + def test_arm_cubieboard_sata(self): + self.set_machine('cubieboard') + kernel_path = self.archive_extract( + self.ASSET_DEB, member='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.archive_extract(self.ASSET_DEB, member=dtb_path) + + rootfs_path = self.uncompress(self.ASSET_SATA_ROOTFS) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'usbcore.nousb ' + 'root=/dev/sda ro ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-drive', 'if=none,format=raw,id=disk0,file=' + + rootfs_path, + '-device', 'ide-hd,bus=ide.0,drive=disk0', + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun4i/sun5i') + exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', + 'sda') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + + @skipBigDataTest() + def test_arm_cubieboard_openwrt_22_03_2(self): + # This test download a 7.5 MiB compressed image and expand it + # to 126 MiB. + self.set_machine('cubieboard') + self.require_netdev('user') + + image_path = self.uncompress(self.ASSET_OPENWRT) + 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() + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_emcraft_sf2.py b/tests/functional/test_arm_emcraft_sf2.py new file mode 100755 index 00000000000..f9f3f069e2c --- /dev/null +++ b/tests/functional/test_arm_emcraft_sf2.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import shutil + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test.utils import file_truncate + +class EmcraftSf2Machine(LinuxKernelTest): + + ASSET_UBOOT = Asset( + ('https://raw.githubusercontent.com/Subbaraya-Sundeep/qemu-test-binaries/' + 'fe371d32e50ca682391e1e70ab98c2942aeffb01/u-boot'), + '5c6a15103375db11b21f2236473679a9dbbed6d89652bfcdd501c263d68ab725') + + ASSET_SPI = Asset( + ('https://raw.githubusercontent.com/Subbaraya-Sundeep/qemu-test-binaries/' + 'fe371d32e50ca682391e1e70ab98c2942aeffb01/spi.bin'), + 'cd9bdd2c4cb55a59c3adb6bcf74881667c4500dde0570a43aa3be2b17eecfdb6') + + def test_arm_emcraft_sf2(self): + self.set_machine('emcraft-sf2') + self.require_netdev('user') + + uboot_path = self.ASSET_UBOOT.fetch() + spi_path = self.ASSET_SPI.fetch() + spi_path_rw = self.scratch_file('spi.bin') + shutil.copy(spi_path, spi_path_rw) + os.chmod(spi_path_rw, 0o600) + + 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_rw + ',if=mtd,format=raw', + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Enter \'help\' for a list') + + exec_command_and_wait_for_pattern(self, 'ifconfig eth0 10.0.2.15', + 'eth0: link becomes ready') + exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', + '3 packets transmitted, 3 packets received, 0% packet loss') + +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 00000000000..4f00924aa03 --- /dev/null +++ b/tests/functional/test_arm_integratorcp.py @@ -0,0 +1,95 @@ +#!/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 logging + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import skipIfMissingImports, skipUntrustedTest + + +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() + + @skipUntrustedTest() + 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') + + @skipIfMissingImports("numpy", "cv2") + @skipUntrustedTest() + def test_framebuffer_tux_logo(self): + """ + Boot Linux and verify the Tux logo is displayed on the framebuffer. + """ + import numpy as np + import cv2 + + screendump_path = self.scratch_file("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') + res = self.vm.cmd('human-monitor-command', + command_line='screendump %s' % screendump_path) + if 'unknown command' in res: + self.skipTest('screendump not available') + 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_microbit.py b/tests/functional/test_arm_microbit.py new file mode 100755 index 00000000000..68ea4e73d62 --- /dev/null +++ b/tests/functional/test_arm_microbit.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright 2025, The QEMU Project Developers. +# +# A functional test that runs MicroPython on the arm microbit machine. + +from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern + + +class MicrobitMachine(QemuSystemTest): + + ASSET_MICRO = Asset('https://ozlabs.org/~joel/microbit-micropython.hex', + '021641f93dfb11767d4978dbb3ca7f475d1b13c69e7f4aec3382f212636bffd6') + + def test_arm_microbit(self): + self.set_machine('microbit') + + micropython = self.ASSET_MICRO.fetch() + self.vm.set_console() + self.vm.add_args('-device', f'loader,file={micropython}') + self.vm.launch() + wait_for_console_pattern(self, 'Type "help()" for more information.') + exec_command_and_wait_for_pattern(self, 'import machine as mch', '>>>') + exec_command_and_wait_for_pattern(self, 'mch.reset()', 'MicroPython') + wait_for_console_pattern(self, '>>>') + +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 00000000000..f9bfa8c78d9 --- /dev/null +++ b/tests/functional/test_arm_orangepi.py @@ -0,0 +1,237 @@ +#!/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, skipBigDataTest +from qemu_test.utils import image_pow2ceil_expand + + +class OrangePiMachine(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') + kernel_path = self.archive_extract( + self.ASSET_DEB, member='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.archive_extract(self.ASSET_DEB, member=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') + kernel_path = self.archive_extract( + self.ASSET_DEB, member='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.archive_extract(self.ASSET_DEB, member=dtb_path) + initrd_path = self.uncompress(self.ASSET_INITRD) + + 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') + kernel_path = self.archive_extract( + self.ASSET_DEB, member='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.archive_extract(self.ASSET_DEB, member=dtb_path) + rootfs_path = self.uncompress(self.ASSET_ROOTFS) + 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) + + @skipBigDataTest() + def test_arm_orangepi_armbian(self): + self.set_machine('orangepi-pc') + self.require_netdev('user') + + # 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 = self.uncompress(self.ASSET_ARMBIAN) + 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...') + + @skipBigDataTest() + def test_arm_orangepi_uboot_netbsd9(self): + self.set_machine('orangepi-pc') + self.require_netdev('user') + + # This test download a 304MB compressed image and expand it to 2GB + # 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.archive_extract(self.ASSET_UBOOT, member=uboot_path) + image_path = self.uncompress(self.ASSET_NETBSD) + 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_quanta_gsj.py b/tests/functional/test_arm_quanta_gsj.py new file mode 100755 index 00000000000..cb0545f7bfa --- /dev/null +++ b/tests/functional/test_arm_quanta_gsj.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import interrupt_interactive_console_until_pattern, skipSlowTest + + +class EmcraftSf2Machine(LinuxKernelTest): + + ASSET_IMAGE = Asset( + ('https://github.com/hskinnemoen/openbmc/releases/download/' + '20200711-gsj-qemu-0/obmc-phosphor-image-gsj.static.mtd.gz'), + 'eccd4e375cde53034c84aece5c511932cacf838d9fd3f63da368a511757da72b') + + ASSET_INITRD = Asset( + ('https://github.com/hskinnemoen/openbmc/releases/download/' + '20200711-gsj-qemu-0/obmc-phosphor-initramfs-gsj.cpio.xz'), + '37b05009fc54db1434beac12bd7ff99a2e751a2f032ee18d9042f991dd0cdeaa') + + ASSET_KERNEL = Asset( + ('https://github.com/hskinnemoen/openbmc/releases/download/' + '20200711-gsj-qemu-0/uImage-gsj.bin'), + 'ce6d6b37bff46c74fc7b1e90da10a431cc37a62cdb35ec199fa73473d0790110') + + ASSET_DTB = Asset( + ('https://github.com/hskinnemoen/openbmc/releases/download/' + '20200711-gsj-qemu-0/nuvoton-npcm730-gsj.dtb'), + '3249b2da787d4b9ad4e61f315b160abfceb87b5e1895a7ce898ce7f40c8d4045') + + @skipSlowTest() + def test_arm_quanta_gsj(self): + self.set_machine('quanta-gsj') + image_path = self.uncompress(self.ASSET_IMAGE, format='gz') + + self.vm.set_console() + drive_args = 'file=' + image_path + ',if=mtd,bus=0,unit=0' + self.vm.add_args('-drive', drive_args) + self.vm.launch() + + # Disable drivers and services that stall for a long time during boot, + # to avoid running past the 90-second timeout. These may be removed + # as the corresponding device support is added. + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + ( + 'console=${console} ' + 'mem=${mem} ' + 'initcall_blacklist=npcm_i2c_bus_driver_init ' + 'systemd.mask=systemd-random-seed.service ' + 'systemd.mask=dropbearkey.service ' + ) + + self.wait_for_console_pattern('> BootBlock by Nuvoton') + self.wait_for_console_pattern('>Device: Poleg BMC NPCM730') + self.wait_for_console_pattern('>Skip DDR init.') + self.wait_for_console_pattern('U-Boot ') + interrupt_interactive_console_until_pattern( + self, 'Hit any key to stop autoboot:', 'U-Boot>') + exec_command_and_wait_for_pattern( + self, "setenv bootargs ${bootargs} " + kernel_command_line, + 'U-Boot>') + exec_command_and_wait_for_pattern( + self, 'run romboot', 'Booting Kernel from flash') + self.wait_for_console_pattern('Booting Linux on physical CPU 0x0') + self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0') + self.wait_for_console_pattern('OpenBMC Project Reference Distro') + self.wait_for_console_pattern('gsj login:') + + def test_arm_quanta_gsj_initrd(self): + self.set_machine('quanta-gsj') + initrd_path = self.ASSET_INITRD.fetch() + kernel_path = self.ASSET_KERNEL.fetch() + dtb_path = self.ASSET_DTB.fetch() + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200n8 ' + 'earlycon=uart8250,mmio32,0xf0001000') + self.vm.add_args('-kernel', kernel_path, + '-initrd', initrd_path, + '-dtb', dtb_path, + '-append', kernel_command_line) + self.vm.launch() + + self.wait_for_console_pattern('Booting Linux on physical CPU 0x0') + self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0') + self.wait_for_console_pattern( + 'Give root password for system maintenance') + +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 00000000000..d3c7aaa39b0 --- /dev/null +++ b/tests/functional/test_arm_raspi2.py @@ -0,0 +1,92 @@ +#!/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 + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern + + +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', + } + kernel_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/kernel7.img') + dtb_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='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): + kernel_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/kernel7.img') + dtb_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/bcm2709-rpi-2-b.dtb') + initrd_path = self.uncompress(self.ASSET_INITRD) + + 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_realview.py b/tests/functional/test_arm_realview.py new file mode 100755 index 00000000000..82cc964333e --- /dev/null +++ b/tests/functional/test_arm_realview.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a realview arm machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, exec_command_and_wait_for_pattern +from qemu_test import Asset + + +class RealviewMachine(LinuxKernelTest): + + ASSET_REALVIEW_MPCORE = Asset( + ('https://archive.openwrt.org/chaos_calmer/15.05.1/realview/generic/' + 'openwrt-15.05.1-realview-vmlinux-initramfs.elf'), + 'd3a01037f33e7512d46d50975588d5c3a0e0cbf25f37afab44775c2a2be523e6') + + def test_realview_ep_mpcore(self): + self.require_netdev('user') + self.set_machine('realview-eb-mpcore') + kernel_path = self.ASSET_REALVIEW_MPCORE.fetch() + self.vm.set_console() + kernel_param = 'console=ttyAMA0 mem=128M quiet' + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_param) + self.vm.launch() + self.wait_for_console_pattern('Please press Enter to activate') + prompt = ':/#' + exec_command_and_wait_for_pattern(self, '', prompt) + exec_command_and_wait_for_pattern(self, 'dmesg', kernel_param) + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, + ('while ! dmesg | grep "br-lan: port 1(eth0) entered" ;' + ' do sleep 1 ; done'), + 'entered forwarding state') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, + 'while ! ifconfig | grep "10.0.2.15" ; do sleep 1 ; done', + 'addr:10.0.2.15') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, 'ping -c 1 10.0.2.2', + '1 packets received, 0% packet loss') + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_replay.py b/tests/functional/test_arm_replay.py new file mode 100755 index 00000000000..e002e6a2647 --- /dev/null +++ b/tests/functional/test_arm_replay.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on arm machines and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class ArmReplay(ReplayKernelBase): + + ASSET_VIRT = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/29/Everything/armhfp/os/images/pxeboot/vmlinuz'), + '18dd5f1a9a28bd539f9d047f7c0677211bae528e8712b40ca5a229a4ad8e2591') + + def test_virt(self): + self.set_machine('virt') + kernel_path = self.ASSET_VIRT.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1) + + ASSET_CUBIE_KERNEL = 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_CUBIE_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/arm/rootfs-armv5.cpio.gz'), + '334b8d256db67a3f2b3ad070aa08b5ade39624e0e7e35b02f4359a577bc8f39b') + + def test_cubieboard(self): + self.set_machine('cubieboard') + kernel_path = self.archive_extract(self.ASSET_CUBIE_KERNEL, + member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = self.archive_extract(self.ASSET_CUBIE_KERNEL, + member='usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb') + initrd_path = self.uncompress(self.ASSET_CUBIE_INITRD) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'usbcore.nousb ' + 'panic=-1 noreboot') + console_pattern = 'Boot successful.' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1, + args=('-dtb', dtb_path, + '-initrd', initrd_path, + '-no-reboot')) + + ASSET_DAY16 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day16.tar.xz', + '63311adb2d4c4e7a73214a86d29988add87266a909719c56acfadd026b4110a7') + + def test_vexpressa9(self): + self.set_machine('vexpress-a9') + self.archive_extract(self.ASSET_DAY16) + kernel_path = self.scratch_file('day16', 'winter.zImage') + dtb_path = self.scratch_file('day16', 'vexpress-v2p-ca9.dtb') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar', args=('-dtb', dtb_path)) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_arm_smdkc210.py b/tests/functional/test_arm_smdkc210.py new file mode 100755 index 00000000000..3154e7f7322 --- /dev/null +++ b/tests/functional/test_arm_smdkc210.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class Smdkc210Machine(LinuxKernelTest): + + ASSET_DEB = Asset( + ('https://snapshot.debian.org/archive/debian/20190928T224601Z/pool/' + 'main/l/linux/linux-image-4.19.0-6-armmp_4.19.67-2+deb10u1_armhf.deb'), + '421804e7579ef40d554c962850dbdf1bfc79f7fa7faec9d391397170dc806c3e') + + ASSET_ROOTFS = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/arm/' + 'rootfs-armv5.cpio.gz'), + '334b8d256db67a3f2b3ad070aa08b5ade39624e0e7e35b02f4359a577bc8f39b') + + def test_arm_exynos4210_initrd(self): + self.set_machine('smdkc210') + + kernel_path = self.archive_extract(self.ASSET_DEB, + member='boot/vmlinuz-4.19.0-6-armmp') + dtb_path = 'usr/lib/linux-image-4.19.0-6-armmp/exynos4210-smdkv310.dtb' + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) + + initrd_path = self.uncompress(self.ASSET_ROOTFS) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'earlycon=exynos4210,0x13800000 earlyprintk ' + + 'console=ttySAC0,115200n8 ' + + 'random.trust_cpu=off cryptomgr.notests ' + + 'cpuidle.off=1 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.') + # TODO user command, for now the uart is stuck + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_stellaris.py b/tests/functional/test_arm_stellaris.py new file mode 100755 index 00000000000..cbd21cb1a0b --- /dev/null +++ b/tests/functional/test_arm_stellaris.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# +# Functional test that checks the serial console of the stellaris machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern + + +class StellarisMachine(QemuSystemTest): + + ASSET_DAY22 = Asset( + 'https://www.qemu-advent-calendar.org/2023/download/day22.tar.gz', + 'ae3a63ef4b7a22c21bfc7fc0d85e402fe95e223308ed23ac854405016431ff51') + + def test_lm3s6965evb(self): + self.set_machine('lm3s6965evb') + kernel_path = self.archive_extract(self.ASSET_DAY22, + member='day22/day22.bin') + self.vm.set_console() + self.vm.add_args('-kernel', kernel_path) + self.vm.launch() + + wait_for_console_pattern(self, 'In a one horse open') + + ASSET_NOTMAIN = Asset( + 'https://github.com/Ahelion/QemuArmM4FDemoSw/raw/master/build/notmain.bin', + '6ceda031aa081a420fca2fca9e137fa681d6e3820d820ad1917736cb265e611a') + + def test_lm3s811evb(self): + self.set_machine('lm3s811evb') + kernel_path = self.ASSET_NOTMAIN.fetch() + + self.vm.set_console() + self.vm.add_args('-cpu', 'cortex-m4') + self.vm.add_args('-kernel', kernel_path) + self.vm.launch() + + # The test kernel emits an initial '!' and then waits for input. + # For each character that we send it responds with a certain + # other ASCII character. + wait_for_console_pattern(self, '!') + exec_command_and_wait_for_pattern(self, '789', 'cdf') + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_arm_sx1.py b/tests/functional/test_arm_sx1.py new file mode 100755 index 00000000000..25800b388c9 --- /dev/null +++ b/tests/functional/test_arm_sx1.py @@ -0,0 +1,73 @@ +#!/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 + + +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, + wait_for='Boot successful') + self.vm.wait(timeout=120) + + 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, wait_for='Boot successful') + self.vm.wait(timeout=120) + + 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, wait_for='Boot successful') + self.vm.wait(timeout=120) + +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 00000000000..4ac85f48ac1 --- /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/buildroot/20241119/armv5/zImage', + '3931a3908dbcf0ec0fe292d035ffc4dfed95f797dedd4a59ccfcf7a46e6f92d4') + ASSET_ARMV5_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/armv5/rootfs.ext4.zst', + '60ff78b68c7021df378e4fc2d66d3b016484d1acc7e07fb8920c1d8e30f4571f') + ASSET_ARMV5_DTB = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/armv5/versatile-pb.dtb', + '50988e69ef3f3b08bfb9146e8fe414129990029e8dfbed444953b7e14809530a') + + 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/buildroot/20241119/armv7/zImage', + '1377bc3d90de5ce57ab17cd67429fe8b15c2e9964248c775c682b67e6299b991') + ASSET_ARMV7_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/armv7/rootfs.ext4.zst', + 'ed2cbc69bd6b3fbd5cafb5ee961393c7cfbe726446f14301c67d6b1f28bfdb51') + + 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/buildroot/20241119/armv7be/zImage', + 'a244e6da99f1bbd254827ec7681bd4aac9eb1aa05aaebc6b15e5d289ebb683f3') + ASSET_ARMV7BE_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/armv7be/rootfs.ext4.zst', + 'd4f9c57860a512163f30ecc69b2174d1a1bdeb853a43dc49a09cfcfe84e428ea') + + 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 00000000000..6b115528947 --- /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 + + +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') + self.archive_extract(self.ASSET_DAY16) + self.launch_kernel(self.scratch_file('day16', 'winter.zImage'), + dtb=self.scratch_file('day16', + 'vexpress-v2p-ca9.dtb'), + wait_for='QEMU advent calendar') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_virt.py b/tests/functional/test_arm_virt.py new file mode 100755 index 00000000000..7b6549176f5 --- /dev/null +++ b/tests/functional/test_arm_virt.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + +class ArmVirtMachine(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/29/Everything/armhfp/os/images/pxeboot/vmlinuz'), + '18dd5f1a9a28bd539f9d047f7c0677211bae528e8712b40ca5a229a4ad8e2591') + + def test_arm_virt(self): + self.set_machine('virt') + kernel_path = self.ASSET_KERNEL.fetch() + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + 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) + +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 00000000000..6359b72af39 --- /dev/null +++ b/tests/functional/test_avr_mega2560.py @@ -0,0 +1,49 @@ +#!/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 . +# + +from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern + + +class AVR6Machine(QemuSystemTest): + + 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.set_console() + self.vm.launch() + + wait_for_console_pattern(self, + 'XABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWXA') + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_avr_uno.py b/tests/functional/test_avr_uno.py new file mode 100755 index 00000000000..adb3b73da4f --- /dev/null +++ b/tests/functional/test_avr_uno.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# +# QEMU AVR Arduino UNO functional test +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern + + +class UnoMachine(QemuSystemTest): + + ASSET_UNO = Asset( + ('https://github.com/RahulRNandan/LED_Blink_AVR/raw/' + 'c6d602cbb974a193/build/main.elf'), + '3009a4e2cf5c5b65142f538abdf66d4dc6bc6beab7e552fff9ae314583761b72') + + def test_uno(self): + """ + The binary constantly prints out 'LED Blink' + """ + self.set_machine('arduino-uno') + rom_path = self.ASSET_UNO.fetch() + + self.vm.add_args('-bios', rom_path) + self.vm.set_console() + self.vm.launch() + + wait_for_console_pattern(self, 'LED Blink') + + +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 00000000000..b1122a0e8f7 --- /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 00000000000..0081b06d85a --- /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_hppa_seabios.py b/tests/functional/test_hppa_seabios.py new file mode 100755 index 00000000000..661b2464e13 --- /dev/null +++ b/tests/functional/test_hppa_seabios.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# SeaBIOS boot test for HPPA machines +# +# Copyright (c) 2024 Linaro, Ltd +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest +from qemu_test import wait_for_console_pattern + +class HppaSeabios(QemuSystemTest): + + timeout = 5 + MACH_BITS = {'B160L': 32, 'C3700': 64} + + def boot_seabios(self): + mach = self.machine + bits = self.MACH_BITS[mach] + self.vm.add_args('-no-shutdown') + self.vm.set_console() + self.vm.launch() + wait_for_console_pattern(self, f'SeaBIOS PA-RISC {bits}-bit Firmware') + wait_for_console_pattern(self, f'Emulated machine: HP {mach} ({bits}-bit') + + def test_hppa_32(self): + self.set_machine('B160L') + self.boot_seabios() + + def test_hppa_64(self): + self.set_machine('C3700') + self.boot_seabios() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_i386_replay.py b/tests/functional/test_i386_replay.py new file mode 100755 index 00000000000..7c4c2602da9 --- /dev/null +++ b/tests/functional/test_i386_replay.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on a i386 machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class I386Replay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/i386/bzImage', + 'a3e5b32a354729e65910f5a1ffcda7c14a6c12a55e8213fb86e277f1b76ed956') + + def test_pc(self): + self.set_machine('pc') + kernel_url = () + kernel_path = self.ASSET_KERNEL.fetch() + 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) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_i386_tuxrun.py b/tests/functional/test_i386_tuxrun.py new file mode 100755 index 00000000000..f3ccf11ae8b --- /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/buildroot/20241119/i386/bzImage', + '47fb44e38e34101eb0f71a2a01742b959d40ed5fd67cefb5608a39be11d3b74e') + ASSET_I386_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/i386/rootfs.ext4.zst', + 'a1a3b3b4c9dccd6475b58db95c107b468b736b700f6620985a8ed050a73d51c8') + + 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 00000000000..e8cbc37eed6 --- /dev/null +++ b/tests/functional/test_info_usernet.py @@ -0,0 +1,34 @@ +#!/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_test.utils import get_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() + + port = get_usernet_hostfwd_port(self.vm) + 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_intel_iommu.py b/tests/functional/test_intel_iommu.py new file mode 100755 index 00000000000..62268d6f278 --- /dev/null +++ b/tests/functional/test_intel_iommu.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# +# INTEL_IOMMU Functional tests +# +# Copyright (c) 2021 Red Hat, Inc. +# +# Author: +# Eric Auger +# +# 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, exec_command_and_wait_for_pattern + + +class IntelIOMMU(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/initrd.img'), + '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b') + + ASSET_DISKIMAGE = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Cloud/x86_64/images/Fedora-Cloud-Base-31-1.9.x86_64.qcow2'), + 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0') + + DEFAULT_KERNEL_PARAMS = ('root=/dev/vda1 console=ttyS0 net.ifnames=0 ' + 'quiet rd.rescue ') + GUEST_PORT = 8080 + IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on' + kernel_path = None + initrd_path = None + kernel_params = None + + def add_common_args(self, path): + self.vm.add_args('-drive', f'file={path},if=none,id=drv0,snapshot=on') + 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) + + self.vm.add_args('-netdev', + 'user,id=n1,hostfwd=tcp:127.0.0.1:0-:%d' % + self.GUEST_PORT) + self.vm.add_args('-device', + 'virtio-net-pci,netdev=n1' + self.IOMMU_ADDON) + + self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') + self.vm.add_args('-object', + 'rng-random,id=rng0,filename=/dev/urandom') + self.vm.add_args("-m", "1G") + self.vm.add_args("-accel", "kvm") + + def common_vm_setup(self): + self.set_machine('q35') + self.require_accelerator("kvm") + self.require_netdev('user') + + self.kernel_path = self.ASSET_KERNEL.fetch() + self.initrd_path = self.ASSET_INITRD.fetch() + image_path = self.ASSET_DISKIMAGE.fetch() + self.add_common_args(image_path) + self.kernel_params = self.DEFAULT_KERNEL_PARAMS + + def run_and_check(self): + if self.kernel_path: + self.vm.add_args('-kernel', self.kernel_path, + '-append', self.kernel_params, + '-initrd', self.initrd_path) + self.vm.set_console() + self.vm.launch() + self.wait_for_console_pattern('Entering emergency mode.') + prompt = '# ' + self.wait_for_console_pattern(prompt) + + # Copy a file (checked later), umount afterwards to drop disk cache: + exec_command_and_wait_for_pattern(self, 'mount /dev/vda1 /sysroot', + prompt) + filename = '/boot/initramfs-5.3.7-301.fc31.x86_64.img' + exec_command_and_wait_for_pattern(self, (f'cp /sysroot{filename}' + ' /sysroot/root/data'), + prompt) + exec_command_and_wait_for_pattern(self, 'umount /sysroot', prompt) + + # Switch from initrd to the cloud image filesystem: + exec_command_and_wait_for_pattern(self, 'mount /dev/vda1 /sysroot', + prompt) + exec_command_and_wait_for_pattern(self, + ('for d in dev proc sys run ; do ' + 'mount -o bind /$d /sysroot/$d ; done'), prompt) + exec_command_and_wait_for_pattern(self, 'chroot /sysroot', prompt) + + # Checking for IOMMU enablement: + self.log.info("Checking whether IOMMU has been enabled...") + exec_command_and_wait_for_pattern(self, 'cat /proc/cmdline', + 'intel_iommu=on') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, 'dmesg | grep DMAR:', + 'IOMMU enabled') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, + 'find /sys/kernel/iommu_groups/ -type l', + 'devices/0000:00:') + self.wait_for_console_pattern(prompt) + + # Check hard disk device via sha256sum: + self.log.info("Checking hard disk...") + hashsum = '0dc7472f879be70b2f3daae279e3ae47175ffe249691e7d97f47222b65b8a720' + exec_command_and_wait_for_pattern(self, 'sha256sum ' + filename, + hashsum) + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, 'sha256sum /root/data', + hashsum) + self.wait_for_console_pattern(prompt) + + # Check virtio-net via HTTP: + exec_command_and_wait_for_pattern(self, 'dhclient eth0', prompt) + self.check_http_download(filename, hashsum, self.GUEST_PORT) + + def test_intel_iommu(self): + self.common_vm_setup() + self.vm.add_args('-device', 'intel-iommu,intremap=on') + self.vm.add_args('-machine', 'kernel_irqchip=split') + self.kernel_params += 'intel_iommu=on' + self.run_and_check() + + def test_intel_iommu_strict(self): + self.common_vm_setup() + self.vm.add_args('-device', 'intel-iommu,intremap=on') + self.vm.add_args('-machine', 'kernel_irqchip=split') + self.kernel_params += 'intel_iommu=on,strict' + self.run_and_check() + + def test_intel_iommu_strict_cm(self): + self.common_vm_setup() + self.vm.add_args('-device', 'intel-iommu,intremap=on,caching-mode=on') + self.vm.add_args('-machine', 'kernel_irqchip=split') + self.kernel_params += 'intel_iommu=on,strict' + self.run_and_check() + + def test_intel_iommu_pt(self): + self.common_vm_setup() + self.vm.add_args('-device', 'intel-iommu,intremap=on') + self.vm.add_args('-machine', 'kernel_irqchip=split') + self.kernel_params += 'intel_iommu=on iommu=pt' + self.run_and_check() + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_linux_initrd.py b/tests/functional/test_linux_initrd.py new file mode 100755 index 00000000000..2207f83fbfd --- /dev/null +++ b/tests/functional/test_linux_initrd.py @@ -0,0 +1,95 @@ +#!/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 logging +import tempfile + +from qemu_test import QemuSystemTest, Asset, skipFlakyTest + + +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) + + # XXX file tracking bug + @skipFlakyTest(bug_url=None) + 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 00000000000..b7d9abf933f --- /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 00000000000..c7d19989334 --- /dev/null +++ b/tests/functional/test_m68k_mcf5208evb.py @@ -0,0 +1,27 @@ +#!/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 + +from qemu_test import LinuxKernelTest, Asset + + +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') + self.archive_extract(self.ASSET_DAY07) + self.vm.set_console() + self.vm.add_args('-kernel', + self.scratch_file('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 00000000000..13c72bd136a --- /dev/null +++ b/tests/functional/test_m68k_nextcube.py @@ -0,0 +1,64 @@ +#!/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 time + +from qemu_test import QemuSystemTest, Asset +from qemu_test import skipIfMissingImports, skipIfMissingCommands +from qemu_test.tesseract import tesseract_ocr + + +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) + + res = self.vm.cmd('human-monitor-command', + command_line='screendump %s' % screenshot_path) + if 'unknown command' in res: + self.skipTest('screendump not available') + + @skipIfMissingImports("PIL") + def test_bootrom_framebuffer_size(self): + self.set_machine('next-cube') + screenshot_path = self.scratch_file("dump.ppm") + self.check_bootrom_framebuffer(screenshot_path) + + from PIL import Image + width, height = Image.open(screenshot_path).size + self.assertEqual(width, 1120) + self.assertEqual(height, 832) + + @skipIfMissingCommands('tesseract') + def test_bootrom_framebuffer_ocr_with_tesseract(self): + self.set_machine('next-cube') + screenshot_path = self.scratch_file("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 00000000000..b3e655346c4 --- /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') + + kernel_path = self.archive_extract(self.ASSET_KERNEL, + member='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, + '-audio', 'none') + 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_m68k_replay.py b/tests/functional/test_m68k_replay.py new file mode 100755 index 00000000000..213d6ae07e0 --- /dev/null +++ b/tests/functional/test_m68k_replay.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an m68k machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class M68kReplay(ReplayKernelBase): + + ASSET_Q800 = 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_q800(self): + self.set_machine('q800') + kernel_path = self.archive_extract(self.ASSET_Q800, + member='boot/vmlinux-5.3.0-1-m68k') + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 vga=off') + console_pattern = 'No filesystem could mount root' + self.run_rr(kernel_path, kernel_command_line, console_pattern, + args=('-audio', 'none')) + + ASSET_MCF5208 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day07.tar.xz', + '753c2f3837126b7c6ba92d0b1e0b156e8a2c5131d2d576bb0b9a763fae73c08a') + + def test_mcf5208evb(self): + self.set_machine('mcf5208evb') + kernel_path = self.archive_extract(self.ASSET_MCF5208, + member='day07/sanity-clause.elf') + self.run_rr(kernel_path, self.KERNEL_COMMON_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_m68k_tuxrun.py b/tests/functional/test_m68k_tuxrun.py new file mode 100755 index 00000000000..7eacba135f6 --- /dev/null +++ b/tests/functional/test_m68k_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) 2024 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 TuxRunM68KTest(TuxRunBaselineTest): + + ASSET_M68K_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/m68k/vmlinux', + '7754e1d5cec753ccf1dc6894729a7f54c1a4965631ebf56df8e4ce1163ad19d8') + ASSET_M68K_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/m68k/rootfs.ext4.zst', + '557962ffff265607912e82232cf21adbe0e4e5a88e1e1d411ce848c37f0213e9') + + def test_m68k(self): + self.set_machine('virt') + self.cpu="m68040" + self.common_tuxrun(kernel_asset=self.ASSET_M68K_KERNEL, + rootfs_asset=self.ASSET_M68K_ROOTFS, + drive="virtio-blk-device") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_mem_addr_space.py b/tests/functional/test_mem_addr_space.py new file mode 100755 index 00000000000..61b4a190b41 --- /dev/null +++ b/tests/functional/test_mem_addr_space.py @@ -0,0 +1,349 @@ +#!/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 + + # This helper can go away when the 32-bit host deprecation + # turns into full & final removal of support. + def ensure_64bit_binary(self): + with open(self.qemu_bin, "rb") as fh: + ident = fh.read(4) + + # "\x7fELF" + if ident != bytes([0x7f, 0x45, 0x4C, 0x46]): + # Non-ELF file implies macOS or Windows which + # we already assume to be 64-bit only + return + + # bits == 1 -> 32-bit; bits == 2 -> 64-bit + bits = int.from_bytes(fh.read(1), byteorder='little') + if bits != 2: + # 32-bit ELF builds won't be able to address sufficient + # RAM to run the tests + self.skipTest("64-bit build host is required") + + # 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.ensure_64bit_binary() + self.set_machine('q35') + self.vm.add_args('-S', '-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.ensure_64bit_binary() + self.set_machine('q35') + self.vm.add_args('-S', '-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.ensure_64bit_binary() + self.set_machine('q35') + self.vm.add_args('-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.ensure_64bit_binary() + self.set_machine('q35') + self.vm.add_args('-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.ensure_64bit_binary() + self.set_machine('q35') + self.vm.add_args('-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.ensure_64bit_binary() + self.set_machine('q35') + self.vm.add_args('-S', '-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.ensure_64bit_binary() + self.set_machine('pc-q35-7.0') + self.vm.add_args('-S', '-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.ensure_64bit_binary() + self.set_machine('pc-q35-7.1') + self.vm.add_args('-S', '-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.ensure_64bit_binary() + self.set_machine('pc-q35-7.0') + self.vm.add_args('-S', '-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.ensure_64bit_binary() + self.set_machine('pc-q35-7.1') + self.vm.add_args('-S', '-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.ensure_64bit_binary() + self.set_machine('pc-q35-7.1') + self.vm.add_args('-S', '-cpu', 'Skylake-Server', + '-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.ensure_64bit_binary() + self.set_machine('pc-q35-7.1') + self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', + '-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.ensure_64bit_binary() + self.set_machine('pc-q35-7.1') + self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', + '-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.ensure_64bit_binary() + self.set_machine('q35') + self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', + '-m', '512,slots=1,maxmem=987G', + '-display', 'none', + '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1', + '-M', 'cxl=on,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.ensure_64bit_binary() + self.set_machine('q35') + self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', + '-machine', '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_memlock.py b/tests/functional/test_memlock.py new file mode 100755 index 00000000000..2b515ff979f --- /dev/null +++ b/tests/functional/test_memlock.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# +# Functional test that check overcommit memlock options +# +# Copyright (c) Yandex Technologies LLC, 2025 +# +# Author: +# Alexandr Moshkov +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import re + +from typing import Dict + +from qemu_test import QemuSystemTest +from qemu_test import skipLockedMemoryTest + + +STATUS_VALUE_PATTERN = re.compile(r'^(\w+):\s+(\d+) kB', re.MULTILINE) + + +@skipLockedMemoryTest(2_097_152) # 2GB +class MemlockTest(QemuSystemTest): + """ + Runs a guest with memlock options. + Then verify, that this options is working correctly + by checking the status file of the QEMU process. + """ + + def common_vm_setup_with_memlock(self, memlock): + self.vm.add_args('-overcommit', f'mem-lock={memlock}') + self.vm.launch() + + def test_memlock_off(self): + self.common_vm_setup_with_memlock('off') + + status = self.get_process_status_values(self.vm.get_pid()) + + self.assertTrue(status['VmLck'] == 0) + + def test_memlock_on(self): + self.common_vm_setup_with_memlock('on') + + status = self.get_process_status_values(self.vm.get_pid()) + + # VmLck > 0 kB and almost all memory is resident + self.assertTrue(status['VmLck'] > 0) + self.assertTrue(status['VmRSS'] >= status['VmSize'] * 0.70) + + def test_memlock_onfault(self): + self.common_vm_setup_with_memlock('on-fault') + + status = self.get_process_status_values(self.vm.get_pid()) + + # VmLck > 0 kB and only few memory is resident + self.assertTrue(status['VmLck'] > 0) + self.assertTrue(status['VmRSS'] <= status['VmSize'] * 0.30) + + def get_process_status_values(self, pid: int) -> Dict[str, int]: + result = {} + raw_status = self._get_raw_process_status(pid) + + for line in raw_status.split('\n'): + if m := STATUS_VALUE_PATTERN.match(line): + result[m.group(1)] = int(m.group(2)) + + return result + + def _get_raw_process_status(self, pid: int) -> str: + try: + with open(f'/proc/{pid}/status', 'r') as f: + return f.read() + except FileNotFoundError: + self.skipTest("Can't open status file of the process") + + +if __name__ == '__main__': + MemlockTest.main() diff --git a/tests/functional/test_microblaze_replay.py b/tests/functional/test_microblaze_replay.py new file mode 100755 index 00000000000..7484c4186f3 --- /dev/null +++ b/tests/functional/test_microblaze_replay.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an microblaze machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class MicroblazeReplay(ReplayKernelBase): + + ASSET_DAY17 = 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') + kernel_path = self.archive_extract(self.ASSET_DAY17, + member='day17/ballerina.bin') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/test_microblaze_s3adsp1800.py new file mode 100755 index 00000000000..f093b162c0a --- /dev/null +++ b/tests/functional/test_microblaze_s3adsp1800.py @@ -0,0 +1,76 @@ +#!/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. + +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern + + +class MicroblazeMachine(QemuSystemTest): + + timeout = 90 + + ASSET_IMAGE_BE = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day17.tar.xz'), + '3ba7439dfbea7af4876662c97f8e1f0cdad9231fc166e4861d17042489270057') + + ASSET_IMAGE_LE = Asset( + ('http://www.qemu-advent-calendar.org/2023/download/day13.tar.gz'), + 'b9b3d43c5dd79db88ada495cc6e0d1f591153fe41355e925d791fbf44de50c22') + + def do_ballerina_be_test(self, force_endianness=False): + self.set_machine('petalogix-s3adsp1800') + self.archive_extract(self.ASSET_IMAGE_BE) + self.vm.set_console() + self.vm.add_args('-kernel', + self.scratch_file('day17', 'ballerina.bin')) + if force_endianness: + self.vm.add_args('-M', 'endianness=big') + 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... + + def do_xmaton_le_test(self, force_endianness=False): + self.require_netdev('user') + self.set_machine('petalogix-s3adsp1800') + self.archive_extract(self.ASSET_IMAGE_LE) + self.vm.set_console() + self.vm.add_args('-kernel', self.scratch_file('day13', 'xmaton.bin')) + if force_endianness: + self.vm.add_args('-M', 'endianness=little') + tftproot = self.scratch_file('day13') + self.vm.add_args('-nic', f'user,tftp={tftproot}') + self.vm.launch() + wait_for_console_pattern(self, 'QEMU Advent Calendar 2023') + wait_for_console_pattern(self, 'buildroot login:') + exec_command_and_wait_for_pattern(self, 'root', '#') + exec_command_and_wait_for_pattern(self, + 'tftp -g -r xmaton.png 10.0.2.2 ; md5sum xmaton.png', + '821cd3cab8efd16ad6ee5acc3642a8ea') + + +class MicroblazeBigEndianMachine(MicroblazeMachine): + + ASSET_IMAGE_BE = MicroblazeMachine.ASSET_IMAGE_BE + ASSET_IMAGE_LE = MicroblazeMachine.ASSET_IMAGE_LE + + def test_microblaze_s3adsp1800_legacy_be(self): + self.do_ballerina_be_test() + + def test_microblaze_s3adsp1800_legacy_le(self): + self.do_xmaton_le_test(force_endianness=True) + + +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 00000000000..915902d48bd --- /dev/null +++ b/tests/functional/test_microblazeel_s3adsp1800.py @@ -0,0 +1,26 @@ +#!/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. + +from test_microblaze_s3adsp1800 import MicroblazeMachine + + +class MicroblazeLittleEndianMachine(MicroblazeMachine): + + ASSET_IMAGE_LE = MicroblazeMachine.ASSET_IMAGE_LE + ASSET_IMAGE_BE = MicroblazeMachine.ASSET_IMAGE_BE + + def test_microblaze_s3adsp1800_legacy_le(self): + self.do_xmaton_le_test() + + def test_microblaze_s3adsp1800_legacy_be(self): + self.do_ballerina_be_test(force_endianness=True) + + +if __name__ == '__main__': + MicroblazeMachine.main() diff --git a/tests/functional/test_migration.py b/tests/functional/test_migration.py new file mode 100755 index 00000000000..c4393c35434 --- /dev/null +++ b/tests/functional/test_migration.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# +# Migration test +# +# Copyright (c) 2019 Red Hat, Inc. +# +# Authors: +# Cleber Rosa +# Caio Carrara +# +# 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 tempfile +import time + +from qemu_test import QemuSystemTest, skipIfMissingCommands +from qemu_test.ports import Ports + + +class MigrationTest(QemuSystemTest): + + timeout = 10 + + @staticmethod + def migration_finished(vm): + return vm.cmd('query-migrate')['status'] in ('completed', 'failed') + + def assert_migration(self, src_vm, dst_vm): + + end = time.monotonic() + self.timeout + while time.monotonic() < end and not self.migration_finished(src_vm): + time.sleep(0.1) + + end = time.monotonic() + self.timeout + while time.monotonic() < end and not self.migration_finished(dst_vm): + time.sleep(0.1) + + 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 select_machine(self): + target_machine = { + 'aarch64': 'quanta-gsj', + 'alpha': 'clipper', + 'arm': 'npcm750-evb', + 'i386': 'isapc', + 'ppc': 'sam460ex', + 'ppc64': 'mac99', + 'riscv32': 'spike', + 'riscv64': 'virt', + 'sparc': 'SS-4', + 'sparc64': 'sun4u', + 'x86_64': 'microvm', + } + self.set_machine(target_machine[self.arch]) + + def do_migrate(self, dest_uri, src_uri=None): + self.select_machine() + dest_vm = self.get_vm('-incoming', dest_uri, name="dest-qemu") + dest_vm.add_args('-nodefaults') + dest_vm.launch() + if src_uri is None: + src_uri = dest_uri + source_vm = self.get_vm(name="source-qemu") + source_vm.add_args('-nodefaults') + source_vm.launch() + source_vm.qmp('migrate', uri=src_uri) + self.assert_migration(source_vm, dest_vm) + + def _get_free_port(self, ports): + port = ports.find_free_port() + if port is None: + self.skipTest('Failed to find a free port') + return port + + def test_migration_with_tcp_localhost(self): + with Ports() as ports: + dest_uri = 'tcp:localhost:%u' % self._get_free_port(ports) + self.do_migrate(dest_uri) + + def test_migration_with_unix(self): + with tempfile.TemporaryDirectory(prefix='socket_') as socket_path: + dest_uri = 'unix:%s/qemu-test.sock' % socket_path + self.do_migrate(dest_uri) + + @skipIfMissingCommands('ncat') + def test_migration_with_exec(self): + with Ports() as ports: + free_port = self._get_free_port(ports) + dest_uri = 'exec:ncat -l localhost %u' % free_port + src_uri = 'exec:ncat localhost %u' % free_port + self.do_migrate(dest_uri, src_uri) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_mips64_malta.py b/tests/functional/test_mips64_malta.py new file mode 100755 index 00000000000..53c3e0c1221 --- /dev/null +++ b/tests/functional/test_mips64_malta.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# Functional tests for the big-endian 64-bit MIPS Malta board +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset +from test_mips_malta import mips_check_wheezy + + +class MaltaMachineConsole(LinuxKernelTest): + + ASSET_WHEEZY_KERNEL = Asset( + ('https://people.debian.org/~aurel32/qemu/mips/' + 'vmlinux-3.2.0-4-5kc-malta'), + '3e4ec154db080b3f1839f04dde83120654a33e5e1716863de576c47cb94f68f6') + + ASSET_WHEEZY_DISK = Asset( + ('https://people.debian.org/~aurel32/qemu/mips/' + 'debian_wheezy_mips_standard.qcow2'), + 'de03599285b8382ad309309a6c4869f6c6c42a5cfc983342bab9ec0dfa7849a2') + + def test_wheezy(self): + kernel_path = self.ASSET_WHEEZY_KERNEL.fetch() + image_path = self.ASSET_WHEEZY_DISK.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 root=/dev/sda1') + mips_check_wheezy(self, + kernel_path, image_path, kernel_command_line, cpuinfo='MIPS 20Kc', + dl_file='/boot/initrd.img-3.2.0-4-5kc-malta', + hsum='d98b953bb4a41c0fc0fd8d19bbc691c08989ac52568c1d3054d92dfd890d3f06') + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_mips64_tuxrun.py b/tests/functional/test_mips64_tuxrun.py new file mode 100755 index 00000000000..0e4c65961d2 --- /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/buildroot/20241119/mips64/vmlinux', + 'fe2882d216898ba2c56b49ba59f46ad392f36871f7fe325373cd926848b9dbdc') + ASSET_MIPS64_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/mips64/rootfs.ext4.zst', + 'b8c98400216b6d4fb3b3ff05e9929aa015948b596cf0b82234813c84a4f7f4d5') + + 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 00000000000..35e500b0221 --- /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, skipUntrustedTest +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): + kernel_path = self.archive_extract( + self.ASSET_KERNEL, + member='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) + + @skipUntrustedTest() + @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 00000000000..f85371e50cd --- /dev/null +++ b/tests/functional/test_mips64el_loongson3v.py @@ -0,0 +1,36 @@ +#!/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 + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern, skipUntrustedTest + + +class MipsLoongson3v(QemuSystemTest): + timeout = 60 + + ASSET_PMON = Asset( + ('https://github.com/loongson-community/pmon/' + 'releases/download/20210112/pmon-3avirt.bin'), + 'fcdf6bb2cb7885a4a62f31fcb0d5e368bac7b6cea28f40c6dfa678af22fea20a') + + @skipUntrustedTest() + 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 00000000000..3cc79b74c18 --- /dev/null +++ b/tests/functional/test_mips64el_malta.py @@ -0,0 +1,197 @@ +#!/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 import skipIfMissingImports, skipFlakyTest, skipUntrustedTest + +from test_mips_malta import mips_check_wheezy + + +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 + """ + kernel_path = self.archive_extract( + self.ASSET_KERNEL_2_63_2, + member='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') + + @skipUntrustedTest() + def test_mips64el_malta_5KEc_cpio(self): + kernel_path = self.ASSET_KERNEL_3_19_3.fetch() + initrd_path = self.uncompress(self.ASSET_CPIO_R1) + + 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() + + ASSET_WHEEZY_KERNEL = Asset( + ('https://people.debian.org/~aurel32/qemu/mipsel/' + 'vmlinux-3.2.0-4-5kc-malta'), + '5e8b725244c59745bb8b64f5d8f49f25fecfa549f3395fb6d19a3b9e5065b85b') + + ASSET_WHEEZY_DISK = Asset( + ('https://people.debian.org/~aurel32/qemu/mipsel/' + 'debian_wheezy_mipsel_standard.qcow2'), + '454f09ae39f7e6461c84727b927100d2c7813841f2a0a5dce328114887ecf914') + + def test_wheezy(self): + kernel_path = self.ASSET_WHEEZY_KERNEL.fetch() + image_path = self.ASSET_WHEEZY_DISK.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 root=/dev/sda1') + mips_check_wheezy(self, + kernel_path, image_path, kernel_command_line, cpuinfo='MIPS 20Kc', + dl_file='/boot/initrd.img-3.2.0-4-5kc-malta', + hsum='7579f8b56c1187c7c04d0dc3c0c56c7a6314c5ddd3a9bf8803ecc7cf8a3be9f8') + + +@skipIfMissingImports('numpy', 'cv2') +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. + """ + + import numpy as np + import cv2 + + screendump_path = self.scratch_file('screendump.pbm') + + kernel_path = self.uncompress(self.ASSET_KERNEL_4_7_0) + + 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') + res = self.vm.cmd('human-monitor-command', + command_line='screendump %s' % screendump_path) + if 'unknown command' in res: + self.skipTest('screendump not available') + 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) + + # XXX file tracking bug + @skipFlakyTest(bug_url=None) + def test_mips_malta_i6400_framebuffer_logo_7cores(self): + self.do_test_i6400_framebuffer_logo(7) + + @skipFlakyTest(bug_url=None) + 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_replay.py b/tests/functional/test_mips64el_replay.py new file mode 100755 index 00000000000..26a6ccff3f7 --- /dev/null +++ b/tests/functional/test_mips64el_replay.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# +# Replay tests for the little-endian 64-bit MIPS Malta board +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, skipUntrustedTest +from replay_kernel import ReplayKernelBase + + +class Mips64elReplay(ReplayKernelBase): + + 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_replay_mips64el_malta(self): + self.set_machine('malta') + kernel_path = self.archive_extract(self.ASSET_KERNEL_2_63_2, + member='boot/vmlinux-2.6.32-5-5kc-malta') + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + + 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') + + @skipUntrustedTest() + def test_replay_mips64el_malta_5KEc_cpio(self): + self.set_machine('malta') + self.cpu = '5KEc' + kernel_path = self.ASSET_KERNEL_3_19_3.fetch() + initrd_path = self.uncompress(self.ASSET_CPIO_R1) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 console=tty ' + 'rdinit=/sbin/init noreboot') + console_pattern = 'Boot successful.' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, + args=('-initrd', initrd_path)) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_mips64el_tuxrun.py b/tests/functional/test_mips64el_tuxrun.py new file mode 100755 index 00000000000..0a24757c518 --- /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/buildroot/20241119/mips64el/vmlinux', + '0d2829a96f005229839c4cd586d4d8a136ea4b488d29821611c8e97f2266bfa9') + ASSET_MIPS64EL_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/mips64el/rootfs.ext4.zst', + '69c8b69a4f1582ce4c6f01a994968f5d73bffb2fc99cbeeeb26c8b5a28eaeb84') + + 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 00000000000..30279f0ff21 --- /dev/null +++ b/tests/functional/test_mips_malta.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +# +# Functional tests for the big-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, wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern + + +def mips_run_common_commands(test, prompt='#'): + exec_command_and_wait_for_pattern(test, + 'uname -m', + 'mips') + exec_command_and_wait_for_pattern(test, + 'grep XT-PIC /proc/interrupts', + 'timer') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'grep XT-PIC /proc/interrupts', + 'serial') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'grep XT-PIC /proc/interrupts', + 'ata_piix') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'grep XT-PIC /proc/interrupts', + 'rtc') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'cat /proc/devices', + 'input') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'cat /proc/devices', + 'fb') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'cat /proc/ioports', + ' : serial') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'cat /proc/ioports', + ' : ata_piix') + wait_for_console_pattern(test, prompt) + +def mips_check_wheezy(test, kernel_path, image_path, kernel_command_line, + dl_file, hsum, nic='pcnet', cpuinfo='MIPS 24Kc'): + test.require_netdev('user') + test.require_device(nic) + test.set_machine('malta') + + port=8080 + test.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line, + '-drive', 'file=%s,snapshot=on' % image_path, + '-netdev', 'user,id=n1' + + ',tftp=' + os.path.basename(kernel_path) + + ',hostfwd=tcp:127.0.0.1:0-:%d' % port, + '-device', f'{nic},netdev=n1', + '-no-reboot') + test.vm.set_console() + test.vm.launch() + + wait_for_console_pattern(test, 'login: ', 'Oops') + exec_command_and_wait_for_pattern(test, 'root', 'Password:') + exec_command_and_wait_for_pattern(test, 'root', ':~# ') + mips_run_common_commands(test) + + exec_command_and_wait_for_pattern(test, 'cd /', '# ') + test.check_http_download(dl_file, hsum, port, + pythoncmd='python -m SimpleHTTPServer') + + exec_command_and_wait_for_pattern(test, 'cat /proc/cpuinfo', cpuinfo) + exec_command_and_wait_for_pattern(test, 'cat /proc/devices', 'usb') + exec_command_and_wait_for_pattern(test, 'cat /proc/ioports', + ' : piix4_smbus') + exec_command_and_wait_for_pattern(test, 'lspci -d 11ab:4620', + 'GT-64120') + exec_command_and_wait_for_pattern(test, + 'cat /sys/bus/i2c/devices/i2c-0/name', + 'SMBus PIIX4 adapter') + exec_command_and_wait_for_pattern(test, 'cat /proc/mtd', 'YAMON') + # Empty 'Board Config' (64KB) + exec_command_and_wait_for_pattern(test, 'md5sum /dev/mtd2ro', + '0dfbe8aa4c20b52e1b8bf3cb6cbdf193') + + +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): + kernel_path = self.archive_extract( + self.ASSET_KERNEL_2_63_2, + member='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): + self.require_netdev('user') + self.set_machine('malta') + self.require_device('pcnet') + + kernel_path = self.archive_extract( + self.ASSET_KERNEL_4_5_0, + member='boot/vmlinux-4.5.0-2-4kc-malta') + initrd_path = self.uncompress(self.ASSET_INITRD) + + 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, + '-netdev', 'user,id=n1,tftp=' + self.scratch_file('boot'), + '-device', 'pcnet,netdev=n1', + '-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', + '4.5.0-2-4kc-malta #1 Debian') + mips_run_common_commands(self) + + exec_command_and_wait_for_pattern(self, 'ip link set eth0 up', + 'eth0: link up') + exec_command_and_wait_for_pattern(self, + 'ip addr add 10.0.2.15 dev eth0', + '#') + exec_command_and_wait_for_pattern(self, 'route add default eth0', '#') + exec_command_and_wait_for_pattern(self, + 'tftp -g -r vmlinux-4.5.0-2-4kc-malta 10.0.2.2', '#') + exec_command_and_wait_for_pattern(self, + 'md5sum vmlinux-4.5.0-2-4kc-malta', + 'a98218a7efbdefb2dfdf9ecd08c98318') + + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + + ASSET_WHEEZY_KERNEL = Asset( + ('https://people.debian.org/~aurel32/qemu/mips/' + 'vmlinux-3.2.0-4-4kc-malta'), + '0377fcda31299213c10b8e5babe7260ef99188b3ae1aca6f56594abb71e7f67e') + + ASSET_WHEEZY_DISK = Asset( + ('https://people.debian.org/~aurel32/qemu/mips/' + 'debian_wheezy_mips_standard.qcow2'), + 'de03599285b8382ad309309a6c4869f6c6c42a5cfc983342bab9ec0dfa7849a2') + + def test_wheezy(self): + kernel_path = self.ASSET_WHEEZY_KERNEL.fetch() + image_path = self.ASSET_WHEEZY_DISK.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 root=/dev/sda1') + mips_check_wheezy(self, + kernel_path, image_path, kernel_command_line, nic='e1000', + dl_file='/boot/initrd.img-3.2.0-4-4kc-malta', + hsum='ff0c0369143d9bbb9a6e6bc79322a2be535619df639e84103237f406e87493dc') + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_mips_replay.py b/tests/functional/test_mips_replay.py new file mode 100755 index 00000000000..4327481e35b --- /dev/null +++ b/tests/functional/test_mips_replay.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# +# Replay tests for the big-endian 32-bit MIPS Malta board +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, skipSlowTest +from replay_kernel import ReplayKernelBase + + +class MipsReplay(ReplayKernelBase): + + 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_replay_mips_malta(self): + self.set_machine('malta') + kernel_path = self.archive_extract(self.ASSET_KERNEL_2_63_2, + member='boot/vmlinux-2.6.32-5-4kc-malta') + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + 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') + + @skipSlowTest() + def test_replay_mips_malta_cpio(self): + self.set_machine('malta') + kernel_path = self.archive_extract(self.ASSET_KERNEL_4_5_0, + member='boot/vmlinux-4.5.0-2-4kc-malta') + initrd_path = self.uncompress(self.ASSET_INITRD) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 console=tty ' + 'rdinit=/sbin/init noreboot') + console_pattern = 'Boot successful.' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, + args=('-initrd', initrd_path)) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_mips_tuxrun.py b/tests/functional/test_mips_tuxrun.py new file mode 100755 index 00000000000..6771dbd57ea --- /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/buildroot/20241119/mips32/vmlinux', + 'b6f97fc698ae8c96456ad8c996c7454228074df0d7520dedd0a15e2913700a19') + ASSET_MIPS_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/mips32/rootfs.ext4.zst', + '87055cf3cbde3fd134e5039e7b87feb03231d8c4b21ee712b8ba3308dfa72f50') + + 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 00000000000..9ee2884da8e --- /dev/null +++ b/tests/functional/test_mipsel_malta.py @@ -0,0 +1,108 @@ +#!/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 + +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 test_mips_malta import mips_check_wheezy + + +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): + kernel_path = self.uncompress(kernel) + + 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): + self.do_test_mips_malta32el_nanomips(self.ASSET_KERNEL_4K) + + def test_mips_malta32el_nanomips_16k_up(self): + self.do_test_mips_malta32el_nanomips(self.ASSET_KERNEL_16K) + + def test_mips_malta32el_nanomips_64k_dbg(self): + self.do_test_mips_malta32el_nanomips(self.ASSET_KERNEL_64K) + + ASSET_WHEEZY_KERNEL = Asset( + ('https://people.debian.org/~aurel32/qemu/mipsel/' + 'vmlinux-3.2.0-4-4kc-malta'), + 'dc8a3648305b0201ca7a5cd135fe2890067a65d93c38728022bb0e656ad2bf9a') + + ASSET_WHEEZY_DISK = Asset( + ('https://people.debian.org/~aurel32/qemu/mipsel/' + 'debian_wheezy_mipsel_standard.qcow2'), + '454f09ae39f7e6461c84727b927100d2c7813841f2a0a5dce328114887ecf914') + + def test_wheezy(self): + kernel_path = self.ASSET_WHEEZY_KERNEL.fetch() + image_path = self.ASSET_WHEEZY_DISK.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 root=/dev/sda1') + mips_check_wheezy(self, + kernel_path, image_path, kernel_command_line, + dl_file='/boot/initrd.img-3.2.0-4-4kc-malta', + hsum='9fc9f250ed56a74e35e704ddfd5a1c5a5625adefc5c9da91f649288d3ca000f0') + + +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' + self.archive_extract(self.ASSET_YAMON_ROM) + yamon_path = self.scratch_file(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_replay.py b/tests/functional/test_mipsel_replay.py new file mode 100644 index 00000000000..5f4796cf899 --- /dev/null +++ b/tests/functional/test_mipsel_replay.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# +# Replay tests for the little-endian 32-bit MIPS Malta board +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, skipSlowTest +from replay_kernel import ReplayKernelBase + + +class MipselReplay(ReplayKernelBase): + + 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_replay_mips_malta32el_nanomips(self, kernel_asset): + self.set_machine('malta') + self.cpu = 'I7200' + kernel_path = self.uncompress(kernel_asset) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'mem=256m@@0x0 ' + 'console=ttyS0') + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + @skipSlowTest() + def test_replay_mips_malta32el_nanomips_4k(self): + self.do_test_replay_mips_malta32el_nanomips(self.ASSET_KERNEL_4K) + + @skipSlowTest() + def test_replay_mips_malta32el_nanomips_16k_up(self): + self.do_test_replay_mips_malta32el_nanomips(self.ASSET_KERNEL_16K) + + @skipSlowTest() + def test_replay_mips_malta32el_nanomips_64k_dbg(self): + self.do_test_replay_mips_malta32el_nanomips(self.ASSET_KERNEL_64K) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_mipsel_tuxrun.py b/tests/functional/test_mipsel_tuxrun.py new file mode 100755 index 00000000000..d4b39baab59 --- /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/buildroot/20241119/mips32el/vmlinux', + '660dd8c7a6ca7a32d37b4e6348865532ab0edb66802e8cc07869338444cf4929') + ASSET_MIPSEL_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/mips32el/rootfs.ext4.zst', + 'c5d69542bcaed54a4f34671671eb4be5c608ee02671d4d0436544367816a73b1') + + 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 00000000000..751cf10e635 --- /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 00000000000..ee1a397bd24 --- /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_replay.py b/tests/functional/test_or1k_replay.py new file mode 100755 index 00000000000..2b60a9372c5 --- /dev/null +++ b/tests/functional/test_or1k_replay.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an OpenRISC-1000 SIM machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class Or1kReplay(ReplayKernelBase): + + ASSET_DAY20 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day20.tar.xz', + 'ff9d7dd7c6bdba325bd85ee85c02db61ff653e129558aeffe6aff55bffb6763a') + + def test_sim(self): + self.set_machine('or1k-sim') + kernel_path = self.archive_extract(self.ASSET_DAY20, + member='day20/vmlinux') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_or1k_sim.py b/tests/functional/test_or1k_sim.py new file mode 100755 index 00000000000..f9f0b690a0a --- /dev/null +++ b/tests/functional/test_or1k_sim.py @@ -0,0 +1,26 @@ +#!/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 + +from qemu_test import LinuxKernelTest, Asset + + +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') + self.archive_extract(self.ASSET_DAY20) + self.vm.set_console() + self.vm.add_args('-kernel', self.scratch_file('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 00000000000..9d5a37cb170 --- /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 00000000000..f5fcad9f6b6 --- /dev/null +++ b/tests/functional/test_ppc64_e500.py @@ -0,0 +1,44 @@ +#!/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 import exec_command_and_wait_for_pattern + + +class E500Test(LinuxKernelTest): + + ASSET_BR2_E5500_UIMAGE = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/buildroot/qemu_ppc64_e5500-2023.11-8-gdcd9f0f6eb-20240104/uImage', + '2478187c455d6cca3984e9dfde9c635d824ea16236b85fd6b4809f744706deda') + + ASSET_BR2_E5500_ROOTFS = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main//buildroot/qemu_ppc64_e5500-2023.11-8-gdcd9f0f6eb-20240104/rootfs.ext2', + '9035ef97237c84c7522baaff17d25cdfca4bb7a053d5e296e902919473423d76') + + def test_ppc64_e500_buildroot(self): + self.set_machine('ppce500') + self.require_netdev('user') + self.cpu = 'e5500' + + uimage_path = self.ASSET_BR2_E5500_UIMAGE.fetch() + rootfs_path = self.ASSET_BR2_E5500_ROOTFS.fetch() + + self.vm.set_console() + self.vm.add_args('-kernel', uimage_path, + '-append', 'root=/dev/vda', + '-drive', f'file={rootfs_path},if=virtio,format=raw', + '-snapshot', '-no-shutdown') + self.vm.launch() + + self.wait_for_console_pattern('Linux version') + self.wait_for_console_pattern('/init as init process') + self.wait_for_console_pattern('lease of 10.0.2.15') + self.wait_for_console_pattern('buildroot login:') + exec_command_and_wait_for_pattern(self, 'root', '#') + exec_command_and_wait_for_pattern(self, 'poweroff', 'Power down') + +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 00000000000..d87f440fa79 --- /dev/null +++ b/tests/functional/test_ppc64_hv.py @@ -0,0 +1,165 @@ +#!/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. + +import os +import subprocess + +from datetime import datetime +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern, exec_command +from qemu_test import skipIfMissingCommands, skipBigDataTest +from qemu_test import exec_command_and_wait_for_pattern + +# 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. +@skipIfMissingCommands("xorriso") +@skipBigDataTest() +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.21/' + 'releases/ppc64le/alpine-standard-3.21.0-ppc64le.iso'), + '7651ab4e3027604535c0b36e86c901b4695bf8fe97b908f5b48590f6baae8f30') + + 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 = self.scratch_file(os.path.basename(path)) + + 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) + + return 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() + ps1='localhost:~#' + wait_for_console_pattern(self, 'localhost login:') + exec_command_and_wait_for_pattern(self, 'root', ps1) + # If the time is wrong, SSL certificates can fail. + exec_command_and_wait_for_pattern(self, 'date -s "' + datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S' + '"'), ps1) + ps1='alpine:~#' + exec_command_and_wait_for_pattern(self, 'setup-alpine -qe', ps1) + exec_command_and_wait_for_pattern(self, 'setup-apkrepos -c1', ps1) + exec_command_and_wait_for_pattern(self, 'apk update', ps1) + # Could upgrade here but it usually should not be necessary + # exec_command_and_wait_for_pattern(self, 'apk upgrade --available', ps1) + + def do_stop_alpine(self): + exec_command(self, 'echo "TEST ME"') + wait_for_console_pattern(self, 'alpine:~#') + exec_command(self, 'poweroff') + wait_for_console_pattern(self, 'reboot: Power down') + self.vm.wait() + + def do_setup_kvm(self): + ps1='alpine:~#' + exec_command_and_wait_for_pattern(self, 'apk add qemu-system-ppc64', ps1) + exec_command_and_wait_for_pattern(self, 'modprobe kvm-hv', ps1) + + # 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.21 kernel seems to crash in XHCI USB driver. + ps1='localhost:~#' + wait_for_console_pattern(self, 'localhost login:') + exec_command_and_wait_for_pattern(self, 'root', ps1) + exec_command(self, 'poweroff') + wait_for_console_pattern(self, 'reboot: Power down') + # Now wait for the host's prompt to come back + wait_for_console_pattern(self, 'alpine:~#') + + def test_hv_pseries(self): + self.require_accelerator("tcg") + self.require_netdev('user') + 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.require_netdev('user') + 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.require_netdev('user') + 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_mac99.py b/tests/functional/test_ppc64_mac99.py new file mode 100755 index 00000000000..dfd9c01371d --- /dev/null +++ b/tests/functional/test_ppc64_mac99.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a mac99 machine with a PPC970 CPU +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern + +class mac99Test(LinuxKernelTest): + + ASSET_BR2_MAC99_LINUX = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/buildroot/qemu_ppc64_mac99-2023.11-8-gdcd9f0f6eb-20240105/vmlinux', + 'd59307437e4365f2cced0bbd1b04949f7397b282ef349b7cafd894d74aadfbff') + + ASSET_BR2_MAC99_ROOTFS = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main//buildroot/qemu_ppc64_mac99-2023.11-8-gdcd9f0f6eb-20240105/rootfs.ext2', + 'bbd5fd8af62f580bc4e585f326fe584e22856572633a8333178ea6d4ed4955a4') + + def test_ppc64_mac99_buildroot(self): + self.set_machine('mac99') + + linux_path = self.ASSET_BR2_MAC99_LINUX.fetch() + rootfs_path = self.ASSET_BR2_MAC99_ROOTFS.fetch() + + self.vm.set_console() + + # Note: We need '-nographic' to get a serial console + self.vm.add_args('-kernel', linux_path, + '-append', 'root=/dev/sda', + '-drive', f'file={rootfs_path},format=raw', + '-snapshot', '-nographic') + self.vm.launch() + + self.wait_for_console_pattern('>> OpenBIOS') + self.wait_for_console_pattern('Linux version') + self.wait_for_console_pattern('/init as init process') + self.wait_for_console_pattern('gem 0000:f0:0e.0 eth0: Link is up at 100 Mbps') + self.wait_for_console_pattern('buildroot login:') + exec_command_and_wait_for_pattern(self, 'root', '#') + exec_command_and_wait_for_pattern(self, 'poweroff', 'Power down') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_ppc64_powernv.py b/tests/functional/test_ppc64_powernv.py new file mode 100755 index 00000000000..685e2178ed7 --- /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 00000000000..67057934e8d --- /dev/null +++ b/tests/functional/test_ppc64_pseries.py @@ -0,0 +1,91 @@ +#!/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.set_machine('pseries') + self.vm.add_args('-smp', '4,threads=4') + self.do_test_ppc64_linux_boot() + console_pattern = 'CPU maps initialized for 4 threads per core' + wait_for_console_pattern(self, console_pattern, self.panic_message) + console_pattern = 'smp: Brought up 1 node, 4 CPUs' + wait_for_console_pattern(self, console_pattern, self.panic_message) + wait_for_console_pattern(self, self.good_message, self.panic_message) + + def test_ppc64_linux_big_boot(self): + 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_replay.py b/tests/functional/test_ppc64_replay.py new file mode 100755 index 00000000000..e8c9c4bcbf8 --- /dev/null +++ b/tests/functional/test_ppc64_replay.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on ppc64 machines +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, skipFlakyTest +from replay_kernel import ReplayKernelBase + + +class Ppc64Replay(ReplayKernelBase): + + ASSET_DAY19 = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day19.tar.xz'), + '20b1bb5a8488c664defbb5d283addc91a05335a936c63b3f5ff7eee74b725755') + + @skipFlakyTest('https://gitlab.com/qemu-project/qemu/-/issues/2523') + def test_ppc64_e500(self): + self.set_machine('ppce500') + self.cpu = 'e5500' + kernel_path = self.archive_extract(self.ASSET_DAY19, + member='day19/uImage') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora-secondary/' + 'releases/29/Everything/ppc64le/os/ppc/ppc64/vmlinuz'), + '383c2f5c23bc0d9d32680c3924d3fd7ee25cc5ef97091ac1aa5e1d853422fc5f') + + def test_ppc64_pseries(self): + self.set_machine('pseries') + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0' + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern) + + def test_ppc64_powernv(self): + self.set_machine('powernv') + kernel_path = self.ASSET_KERNEL.fetch() + 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) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_ppc64_reverse_debug.py b/tests/functional/test_ppc64_reverse_debug.py new file mode 100755 index 00000000000..5931adef5a9 --- /dev/null +++ b/tests/functional/test_ppc64_reverse_debug.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Reverse debugging test +# +# Copyright (c) 2020 ISP RAS +# +# Author: +# Pavel Dovgalyuk +# +# 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 skipIfMissingImports, skipFlakyTest +from reverse_debugging import ReverseDebugging + + +@skipIfMissingImports('avocado.utils') +class ReverseDebugging_ppc64(ReverseDebugging): + + REG_PC = 0x40 + + @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/1992") + def test_ppc64_pseries(self): + self.set_machine('pseries') + # 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() + + @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/1992") + def test_ppc64_powernv(self): + self.set_machine('powernv') + self.endian_is_le = False + self.reverse_debugging() + + +if __name__ == '__main__': + ReverseDebugging.main() diff --git a/tests/functional/test_ppc64_tuxrun.py b/tests/functional/test_ppc64_tuxrun.py new file mode 100755 index 00000000000..e8f79c676e5 --- /dev/null +++ b/tests/functional/test_ppc64_tuxrun.py @@ -0,0 +1,113 @@ +#!/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 subprocess import check_call, DEVNULL +import tempfile + +from qemu_test import 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', '1G,slots=32,maxmem=2G', + '-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: + check_call([self.qemu_img, 'create', '-f', 'qcow2', + qcow2.name, ' 1G'], + stdout=DEVNULL, stderr=DEVNULL) + + 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/buildroot/20241119/ppc64/vmlinux', + '8219d5cb26e7654ad7826fe8aee6290f7c01eef44f2cd6d26c15fe8f99e1c17c') + ASSET_PPC64_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/ppc64/rootfs.ext4.zst', + 'b68e12314303c5dd0fef37ae98021299a206085ae591893e73557af99a02d373') + + 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/buildroot/20241119/ppc64le/vmlinux', + '21aea1fbc18bf6fa7d8ca4ea48d4940b2c8363c077acd564eb47d769b7495279') + ASSET_PPC64LE_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/ppc64le/rootfs.ext4.zst', + '67d36a3f9597b738e8b7359bdf04500f4d9bb82fc35eaa65aa439d888b2392f4') + + 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_40p.py b/tests/functional/test_ppc_40p.py new file mode 100755 index 00000000000..614972a7eb3 --- /dev/null +++ b/tests/functional/test_ppc_40p.py @@ -0,0 +1,94 @@ +#!/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. + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern, skipUntrustedTest +from qemu_test import exec_command_and_wait_for_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. + @skipUntrustedTest() + 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') + + ASSET_40P_SANDALFOOT = Asset( + 'http://www.juneau-lug.org/zImage.initrd.sandalfoot', + '749ab02f576c6dc8f33b9fb022ecb44bf6a35a0472f2ea6a5e9956bc15933901') + + def test_openbios_and_linux(self): + self.set_machine('40p') + self.require_accelerator("tcg") + drive_path = self.ASSET_40P_SANDALFOOT.fetch() + self.vm.set_console() + self.vm.add_args('-cdrom', drive_path, + '-boot', 'd') + + self.vm.launch() + wait_for_console_pattern(self, 'Please press Enter to activate this console.') + exec_command_and_wait_for_pattern(self, '\012', '#') + exec_command_and_wait_for_pattern(self, 'uname -a', 'Linux ppc 2.4.18') + +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 00000000000..5386016f261 --- /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 00000000000..8600e2e9633 --- /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 + + +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' + self.archive_extract(self.ASSET_IMAGE, format="zip") + bios = self.scratch_file("u-boot-amigaone.bin") + with open(bios, "wb") as bios_fh: + subprocess.run(['tail', '-c', '524288', + self.scratch_file("floppy_edition", + "updater.image")], + stdout=bios_fh) + + self.vm.set_console() + self.vm.add_args('-bios', bios) + 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 00000000000..fddcc24d0da --- /dev/null +++ b/tests/functional/test_ppc_bamboo.py @@ -0,0 +1,44 @@ +#!/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 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') + self.archive_extract(self.ASSET_IMAGE) + self.vm.set_console() + self.vm.add_args('-kernel', + self.scratch_file('system-image-powerpc-440fp', + 'linux'), + '-initrd', + self.scratch_file('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 00000000000..9e4bc1a52c7 --- /dev/null +++ b/tests/functional/test_ppc_mac.py @@ -0,0 +1,36 @@ +#!/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 + + +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") + self.archive_extract(self.ASSET_DAY15) + self.vm.add_args('-M', 'graphics=off') + self.launch_kernel(self.scratch_file('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 00000000000..0715410d7a2 --- /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 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') + kernel_file = self.archive_extract(self.ASSET_IMAGE, + member='creek/creek.bin') + self.vm.set_console() + self.vm.add_args('-kernel', kernel_file) + 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_replay.py b/tests/functional/test_ppc_replay.py new file mode 100755 index 00000000000..8382070abd1 --- /dev/null +++ b/tests/functional/test_ppc_replay.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# +# Replay tests for ppc machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class PpcReplay(ReplayKernelBase): + + ASSET_DAY15 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day15.tar.xz', + '03e0757c131d2959decf293a3572d3b96c5a53587165bf05ce41b2818a2bccd5') + + def do_day15_test(self): + self.require_accelerator("tcg") + kernel_path = self.archive_extract(self.ASSET_DAY15, + member='day15/invaders.elf') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar', args=('-M', 'graphics=off')) + + def test_g3beige(self): + self.set_machine('g3beige') + self.do_day15_test() + + def test_mac99(self): + self.set_machine('mac99') + self.do_day15_test() + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_ppc_sam460ex.py b/tests/functional/test_ppc_sam460ex.py new file mode 100644 index 00000000000..31cf9dd6de8 --- /dev/null +++ b/tests/functional/test_ppc_sam460ex.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a sam460ex machine with a PPC 460EX CPU +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern + + +class sam460exTest(LinuxKernelTest): + + ASSET_BR2_SAM460EX_LINUX = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/buildroot/qemu_ppc_sam460ex-2023.11-8-gdcd9f0f6eb-20240105/vmlinux', + '6f46346f3e20e8b5fc050ff363f350f8b9d76a051b9e0bd7ea470cc680c14df2') + + def test_ppc_sam460ex_buildroot(self): + self.set_machine('sam460ex') + self.require_netdev('user') + + linux_path = self.ASSET_BR2_SAM460EX_LINUX.fetch() + + self.vm.set_console() + self.vm.add_args('-kernel', linux_path, + '-device', 'virtio-net-pci,netdev=net0', + '-netdev', 'user,id=net0') + self.vm.launch() + + self.wait_for_console_pattern('Linux version') + self.wait_for_console_pattern('Hardware name: amcc,canyonlands 460EX') + self.wait_for_console_pattern('/init as init process') + self.wait_for_console_pattern('lease of 10.0.2.15 obtained') + self.wait_for_console_pattern('buildroot login:') + exec_command_and_wait_for_pattern(self, 'root', '#') + exec_command_and_wait_for_pattern(self, 'poweroff', 'System Halted') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_ppc_tuxrun.py b/tests/functional/test_ppc_tuxrun.py new file mode 100755 index 00000000000..5458a7fb716 --- /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/buildroot/20241119/ppc32/uImage', + 'aa5d81deabdb255a318c4bc5ffd6fdd2b5da1ef39f1955dcc35b671d258b68e9') + ASSET_PPC32_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/ppc32/rootfs.ext4.zst', + '67554f830269d6bf53b67c7dd206bcc821e463993d526b1644066fea8117019b') + + 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 00000000000..8fe43549b78 --- /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 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') + self.archive_extract(self.ASSET_IMAGE) + self.vm.set_console() + self.vm.add_args('-kernel', self.scratch_file('hippo', 'hippo.linux'), + '-dtb', self.scratch_file('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 00000000000..3c570208d03 --- /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/buildroot/20241119/riscv32/Image', + '872bc8f8e0d4661825d5f47f7bec64988e9d0a8bd5db8917d57e16f66d83b329') + ASSET_RISCV32_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/riscv32/rootfs.ext4.zst', + '511ad34e63222db08d6c1da16fad224970de36517a784110956ba6a24a0ee5f6') + + 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 00000000000..0d8de36204a --- /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/buildroot/20241119/riscv64/Image', + '2bd8132a3bf21570290042324fff48c987f42f2a00c08de979f43f0662ebadba') + ASSET_RISCV64_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/riscv64/rootfs.ext4.zst', + 'aa4736a9872651dfc0d95e709465eedf1134fd19d42b8cb305bfd776f9801004') + + ASSET_RISCV32_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/riscv32/Image', + '872bc8f8e0d4661825d5f47f7bec64988e9d0a8bd5db8917d57e16f66d83b329') + ASSET_RISCV32_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/riscv32/rootfs.ext4.zst', + '511ad34e63222db08d6c1da16fad224970de36517a784110956ba6a24a0ee5f6') + + 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 00000000000..d077e40f427 --- /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 00000000000..49245793e17 --- /dev/null +++ b/tests/functional/test_rx_gdbsim.py @@ -0,0 +1,76 @@ +#!/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. + +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern, skipFlakyTest + + +class RxGdbSimMachine(QemuSystemTest): + + timeout = 30 + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + + ASSET_UBOOT = Asset( + ('https://github.com/philmd/qemu-testing-blob/raw/rx-gdbsim/rx/gdbsim/' + 'u-boot.bin'), + 'dd7dd4220cccf7aeb32227b26233bf39600db05c3f8e26005bcc2bf6c927207d') + ASSET_DTB = Asset( + ('https://github.com/philmd/qemu-testing-blob/raw/rx-gdbsim/rx/gdbsim/' + 'rx-gdbsim.dtb'), + 'aa278d9c1907a4501741d7ee57e7f65c02dd1b3e0323b33c6d4247f1b32cf29a') + ASSET_KERNEL = Asset( + ('https://github.com/philmd/qemu-testing-blob/raw/rx-gdbsim/rx/gdbsim/' + 'zImage'), + 'baa43205e74a7220ed8482188c5e9ce497226712abb7f4e7e4f825ce19ff9656') + + def test_uboot(self): + """ + U-Boot and checks that the console is operational. + """ + self.set_machine('gdbsim-r5f562n8') + + uboot_path = self.ASSET_UBOOT.fetch() + + 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 + # https://gitlab.com/qemu-project/qemu/-/issues/2691 + #exec_command_and_wait_for_pattern(self, 'version', gcc_version) + + @skipFlakyTest(bug_url="https://gitlab.com/qemu-project/qemu/-/issues/2691") + 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 00000000000..453711aa0f5 --- /dev/null +++ b/tests/functional/test_s390x_ccw_virtio.py @@ -0,0 +1,274 @@ +#!/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 + + +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 = self.uncompress(self.ASSET_F31_INITRD, format="xz") + + 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_replay.py b/tests/functional/test_s390x_replay.py new file mode 100755 index 00000000000..33b5843adae --- /dev/null +++ b/tests/functional/test_s390x_replay.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an s390x machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class S390xReplay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora-secondary/' + 'releases/29/Everything/s390x/os/images/kernel.img'), + 'dace03b8ae0c9f670ebb9b8d6ce5eb24b62987f346de8f1300a439bb00bb99e7') + + def test_s390_ccw_virtio(self): + self.set_machine('s390-ccw-virtio') + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=sclp0' + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=9) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_s390x_topology.py b/tests/functional/test_s390x_topology.py new file mode 100755 index 00000000000..1b5dc651353 --- /dev/null +++ b/tests/functional/test_s390x_topology.py @@ -0,0 +1,415 @@ +#!/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. + +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 + + +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 = self.uncompress(self.ASSET_F35_INITRD, format="xz") + + 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 00000000000..8df3c6893b7 --- /dev/null +++ b/tests/functional/test_s390x_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 TuxRunS390xTest(TuxRunBaselineTest): + + ASSET_S390X_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/s390/bzImage', + 'ee67e91db52a2aed104a7c72b2a08987c678f8179c029626789c35d6dd0fedf1') + ASSET_S390X_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/s390/rootfs.ext4.zst', + 'bff7971fc2fef56372d98afe4557b82fd0a785a241e44c29b058e577ad1bbb44') + + def test_s390(self): + self.set_machine('s390-ccw-virtio') + 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 00000000000..03a648374ce --- /dev/null +++ b/tests/functional/test_sh4_r2d.py @@ -0,0 +1,29 @@ +#!/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 + +from qemu_test import LinuxKernelTest, Asset, skipFlakyTest + + +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. + # XXX file tracking bug + @skipFlakyTest(bug_url=None) + def test_r2d(self): + self.set_machine('r2d') + self.archive_extract(self.ASSET_DAY09) + self.vm.add_args('-append', 'console=ttySC1') + self.launch_kernel(self.scratch_file('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 00000000000..1748f8c7eff --- /dev/null +++ b/tests/functional/test_sh4_tuxrun.py @@ -0,0 +1,49 @@ +#!/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, 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 00000000000..473093bbe13 --- /dev/null +++ b/tests/functional/test_sh4eb_r2d.py @@ -0,0 +1,28 @@ +#!/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 + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern + + +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') + self.archive_extract(self.ASSET_TGZ) + self.vm.add_args('-append', 'console=ttySC1 noiotrap') + self.launch_kernel(self.scratch_file('sh4eb', 'linux-kernel'), + initrd=self.scratch_file('sh4eb', + 'initramfs.cpio.gz'), + console_index=1, wait_for='Type exit when done') + exec_command_and_wait_for_pattern(self, 'exit', 'Restarting system') + +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 00000000000..27ac2896590 --- /dev/null +++ b/tests/functional/test_sparc64_sun4u.py @@ -0,0 +1,38 @@ +#!/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. + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern + + +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') + kernel_file = self.archive_extract(self.ASSET_IMAGE, + member='day23/vmlinux') + self.vm.set_console() + self.vm.add_args('-kernel', kernel_file, + '-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 00000000000..0d7b43dd74c --- /dev/null +++ b/tests/functional/test_sparc64_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 TuxRunSparc64Test(TuxRunBaselineTest): + + ASSET_SPARC64_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/sparc64/vmlinux', + 'a04cfb2e70a264051d161fdd93aabf4b2a9472f2e435c14ed18c5848c5fed261') + ASSET_SPARC64_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/sparc64/rootfs.ext4.zst', + '479c3dc104c82b68be55e2c0c5c38cd473d0b37ad4badccde4775bb88ce34611') + + def test_sparc64(self): + self.set_machine('sun4u') + 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_replay.py b/tests/functional/test_sparc_replay.py new file mode 100755 index 00000000000..865d6486f99 --- /dev/null +++ b/tests/functional/test_sparc_replay.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Replay 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 Asset +from replay_kernel import ReplayKernelBase + + +class SparcReplay(ReplayKernelBase): + + ASSET_DAY11 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day11.tar.xz', + 'c776533ba756bf4dd3f1fc4c024fb50ef0d853e05c5f5ddf0900a32d1eaa49e0') + + def test_replay(self): + self.set_machine('SS-10') + kernel_path = self.archive_extract(self.ASSET_DAY11, + member="day11/zImage.elf") + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_sparc_sun4m.py b/tests/functional/test_sparc_sun4m.py new file mode 100755 index 00000000000..7cd28ebdd1f --- /dev/null +++ b/tests/functional/test_sparc_sun4m.py @@ -0,0 +1,24 @@ +#!/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 + + +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') + self.archive_extract(self.ASSET_DAY11) + self.launch_kernel(self.scratch_file('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 00000000000..3ab3b67f7e3 --- /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_balloon.py b/tests/functional/test_virtio_balloon.py new file mode 100755 index 00000000000..5877b6c408c --- /dev/null +++ b/tests/functional/test_virtio_balloon.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +# +# virtio-balloon 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. + +import time + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern + +UNSET_STATS_VALUE = 18446744073709551615 + + +class VirtioBalloonx86(QemuSystemTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/initrd.img'), + '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b') + + ASSET_DISKIMAGE = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Cloud/x86_64/images/Fedora-Cloud-Base-31-1.9.x86_64.qcow2'), + 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0') + + DEFAULT_KERNEL_PARAMS = ('root=/dev/vda1 console=ttyS0 net.ifnames=0 ' + 'rd.rescue quiet') + + 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 mount_root(self): + self.wait_for_console_pattern('Entering emergency mode.') + prompt = '# ' + self.wait_for_console_pattern(prompt) + + # Synchronize on virtio-block driver creating the root device + exec_command_and_wait_for_pattern(self, + "while ! (dmesg -c | grep vda:) ; do sleep 1 ; done", + "vda1") + + exec_command_and_wait_for_pattern(self, 'mount /dev/vda1 /sysroot', + prompt) + exec_command_and_wait_for_pattern(self, 'chroot /sysroot', + prompt) + exec_command_and_wait_for_pattern(self, "modprobe virtio-balloon", + prompt) + + def assert_initial_stats(self): + ret = self.vm.qmp('qom-get', + {'path': '/machine/peripheral/balloon', + 'property': 'guest-stats'})['return'] + when = ret.get('last-update') + assert when == 0 + stats = ret.get('stats') + for name, val in stats.items(): + assert val == UNSET_STATS_VALUE + + def assert_running_stats(self, then): + # We told the QEMU to refresh stats every 100ms, but + # there can be a delay between virtio-ballon driver + # being modprobed and seeing the first stats refresh + # Retry a few times for robustness under heavy load + retries = 10 + when = 0 + while when == 0 and retries: + ret = self.vm.qmp('qom-get', + {'path': '/machine/peripheral/balloon', + 'property': 'guest-stats'})['return'] + when = ret.get('last-update') + if when == 0: + retries = retries - 1 + time.sleep(0.5) + + now = time.time() + + assert when > then and when < now + stats = ret.get('stats') + # Stat we expect this particular Kernel to have set + expectData = [ + "stat-available-memory", + "stat-disk-caches", + "stat-free-memory", + "stat-htlb-pgalloc", + "stat-htlb-pgfail", + "stat-major-faults", + "stat-minor-faults", + "stat-swap-in", + "stat-swap-out", + "stat-total-memory", + ] + for name, val in stats.items(): + if name in expectData: + assert val != UNSET_STATS_VALUE + else: + assert val == UNSET_STATS_VALUE + + def test_virtio_balloon_stats(self): + self.set_machine('q35') + self.require_accelerator("kvm") + kernel_path = self.ASSET_KERNEL.fetch() + initrd_path = self.ASSET_INITRD.fetch() + diskimage_path = self.ASSET_DISKIMAGE.fetch() + + self.vm.set_console() + self.vm.add_args("-S") + self.vm.add_args("-cpu", "max") + self.vm.add_args("-m", "2G") + # Slow down BIOS phase with boot menu, so that after a system + # reset, we can reliably catch the clean stats again in BIOS + # phase before the guest OS launches + self.vm.add_args("-boot", "menu=on") + self.vm.add_args("-accel", "kvm") + self.vm.add_args("-device", "virtio-balloon,id=balloon") + self.vm.add_args('-drive', + f'file={diskimage_path},if=none,id=drv0,snapshot=on') + self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + + 'drive=drv0,id=virtio-disk0,bootindex=1') + + self.vm.add_args( + "-kernel", + kernel_path, + "-initrd", + initrd_path, + "-append", + self.DEFAULT_KERNEL_PARAMS + ) + self.vm.launch() + + # Poll stats at 100ms + self.vm.qmp('qom-set', + {'path': '/machine/peripheral/balloon', + 'property': 'guest-stats-polling-interval', + 'value': 100 }) + + # We've not run any guest code yet, neither BIOS or guest, + # so stats should be all default values + self.assert_initial_stats() + + self.vm.qmp('cont') + + then = time.time() + self.mount_root() + self.assert_running_stats(then) + + # Race window between these two commands, where we + # rely on '-boot menu=on' to (hopefully) ensure we're + # still executing the BIOS when QEMU processes the + # 'stop', and thus have not loaded the virtio-balloon + # driver in the guest + self.vm.qmp('system_reset') + self.vm.qmp('stop') + + # If the above assumption held, we're in BIOS now and + # stats should be all back at their default values + self.assert_initial_stats() + self.vm.qmp('cont') + + then = time.time() + self.mount_root() + self.assert_running_stats(then) + + +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 00000000000..81c9156d638 --- /dev/null +++ b/tests/functional/test_virtio_gpu.py @@ -0,0 +1,142 @@ +#!/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 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 + + +import os +import socket +import subprocess + + +def pick_default_vug_bin(test): + bld_dir_path = test.build_file("contrib", "vhost-user-gpu", "vhost-user-gpu") + 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(self) + 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 = self.log_file("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 00000000000..a5ea73237f5 --- /dev/null +++ b/tests/functional/test_virtio_version.py @@ -0,0 +1,177 @@ +#!/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. + +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_vnc.py b/tests/functional/test_vnc.py new file mode 100755 index 00000000000..f1dd1597cf1 --- /dev/null +++ b/tests/functional/test_vnc.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# +# Simple functional tests for VNC functionality +# +# 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 socket + +from qemu.machine.machine import VMLaunchFailure +from qemu_test import QemuSystemTest +from qemu_test.ports import Ports + + +VNC_ADDR = '127.0.0.1' + +def check_connect(port: int) -> bool: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + try: + sock.connect((VNC_ADDR, port)) + except ConnectionRefusedError: + return False + + return True + +class Vnc(QemuSystemTest): + + def test_no_vnc_change_password(self): + self.set_machine('none') + self.vm.add_args('-nodefaults', '-S') + self.vm.launch() + + query_vnc_response = self.vm.qmp('query-vnc') + if 'error' in query_vnc_response: + self.assertEqual(query_vnc_response['error']['class'], + 'CommandNotFound') + self.skipTest('VNC support not available') + self.assertFalse(query_vnc_response['return']['enabled']) + + set_password_response = self.vm.qmp('change-vnc-password', + password='new_password') + self.assertIn('error', set_password_response) + self.assertEqual(set_password_response['error']['class'], + 'GenericError') + self.assertEqual(set_password_response['error']['desc'], + 'Could not set password') + + def launch_guarded(self): + try: + self.vm.launch() + except VMLaunchFailure as excp: + if "-vnc: invalid option" in excp.output: + self.skipTest("VNC support not available") + elif "Cipher backend does not support DES algorithm" in excp.output: + self.skipTest("No cryptographic backend available") + else: + self.log.info("unhandled launch failure: %s", excp.output) + raise excp + + def test_change_password_requires_a_password(self): + self.set_machine('none') + self.vm.add_args('-nodefaults', '-S', '-vnc', ':1,to=999') + self.launch_guarded() + self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) + set_password_response = self.vm.qmp('change-vnc-password', + password='new_password') + self.assertIn('error', set_password_response) + self.assertEqual(set_password_response['error']['class'], + 'GenericError') + self.assertEqual(set_password_response['error']['desc'], + 'Could not set password') + + def test_change_password(self): + self.set_machine('none') + self.vm.add_args('-nodefaults', '-S', '-vnc', ':1,to=999,password=on') + self.launch_guarded() + self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) + self.vm.cmd('change-vnc-password', + password='new_password') + + def do_test_change_listen(self, a, b, c): + self.assertFalse(check_connect(a)) + self.assertFalse(check_connect(b)) + self.assertFalse(check_connect(c)) + + self.vm.add_args('-nodefaults', '-S', '-vnc', f'{VNC_ADDR}:{a - 5900}') + self.launch_guarded() + self.assertEqual(self.vm.qmp('query-vnc')['return']['service'], str(a)) + self.assertTrue(check_connect(a)) + self.assertFalse(check_connect(b)) + self.assertFalse(check_connect(c)) + + 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)) + self.assertTrue(check_connect(c)) + + def test_change_listen(self): + self.set_machine('none') + with Ports() as ports: + a, b, c = ports.find_free_ports(3) + self.do_test_change_listen(a, b, c) + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_x86_64_hotplug_blk.py b/tests/functional/test_x86_64_hotplug_blk.py new file mode 100755 index 00000000000..7ddbfefc210 --- /dev/null +++ b/tests/functional/test_x86_64_hotplug_blk.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# +# 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. + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern + + +class HotPlugBlk(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/initrd.img'), + '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b') + + def blockdev_add(self) -> None: + self.vm.cmd('blockdev-add', **{ + 'driver': 'null-co', + 'size': 1073741824, + 'node-name': 'disk' + }) + + def assert_vda(self) -> None: + exec_command_and_wait_for_pattern(self, 'while ! test -e /sys/block/vda ;' + ' do sleep 0.2 ; done', '# ') + + def assert_no_vda(self) -> None: + exec_command_and_wait_for_pattern(self, 'while test -e /sys/block/vda ;' + ' do sleep 0.2 ; done', '# ') + + 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) + self.wait_for_console_pattern('virtio_blk virtio0: [vda]') + 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: + self.require_accelerator('kvm') + self.set_machine('q35') + + self.vm.add_args('-accel', 'kvm') + self.vm.add_args('-device', 'pcie-pci-bridge,id=pci.1,bus=pcie.0') + self.vm.add_args('-m', '1G') + self.vm.add_args('-append', 'console=ttyS0 rd.rescue') + + self.launch_kernel(self.ASSET_KERNEL.fetch(), + self.ASSET_INITRD.fetch(), + wait_for='Entering emergency mode.') + self.wait_for_console_pattern('# ') + + self.blockdev_add() + + self.plug() + self.unplug() + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_x86_64_hotplug_cpu.py b/tests/functional/test_x86_64_hotplug_cpu.py new file mode 100755 index 00000000000..7b9200ac2e8 --- /dev/null +++ b/tests/functional/test_x86_64_hotplug_cpu.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# +# Functional test that hotplugs a CPU and checks it on a Linux guest +# +# 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 LinuxKernelTest, Asset, exec_command_and_wait_for_pattern + + +class HotPlugCPU(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/initrd.img'), + '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b') + + def test_hotplug(self): + + self.require_accelerator('kvm') + self.vm.add_args('-accel', 'kvm') + self.vm.add_args('-cpu', 'Haswell') + self.vm.add_args('-smp', '1,sockets=1,cores=2,threads=1,maxcpus=2') + self.vm.add_args('-m', '1G') + self.vm.add_args('-append', 'console=ttyS0 rd.rescue') + + self.launch_kernel(self.ASSET_KERNEL.fetch(), + self.ASSET_INITRD.fetch(), + wait_for='Entering emergency mode.') + prompt = '# ' + self.wait_for_console_pattern(prompt) + + exec_command_and_wait_for_pattern(self, + 'cd /sys/devices/system/cpu/cpu0', + 'cpu0#') + exec_command_and_wait_for_pattern(self, + 'cd /sys/devices/system/cpu/cpu1', + 'No such file or directory') + + self.vm.cmd('device_add', + driver='Haswell-x86_64-cpu', + id='c1', + socket_id=0, + core_id=1, + thread_id=0) + self.wait_for_console_pattern('CPU1 has been hot-added') + + exec_command_and_wait_for_pattern(self, + 'cd /sys/devices/system/cpu/cpu1', + 'cpu1#') + + exec_command_and_wait_for_pattern(self, 'cd ..', prompt) + self.vm.cmd('device_del', id='c1') + + exec_command_and_wait_for_pattern(self, + 'while cd /sys/devices/system/cpu/cpu1 ;' + ' do sleep 0.2 ; done', + 'No such file or directory') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_x86_64_kvm_xen.py b/tests/functional/test_x86_64_kvm_xen.py new file mode 100755 index 00000000000..a5d445023c9 --- /dev/null +++ b/tests/functional/test_x86_64_kvm_xen.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +# +# 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 + +from qemu.machine import machine + +from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern + +class KVMXenGuest(QemuSystemTest): + + KERNEL_DEFAULT = 'printk.time=0 root=/dev/xvda console=ttyS0 quiet' + + 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. + ASSET_KERNEL = Asset( + ('https://fileserver.linaro.org/s/kE4nCFLdQcoBF9t/download?' + 'path=%2Fkvm-xen-guest&files=bzImage'), + 'ec0ad7bb8c33c5982baee0a75505fe7dbf29d3ff5d44258204d6307c6fe0132a') + + ASSET_ROOTFS = Asset( + ('https://fileserver.linaro.org/s/kE4nCFLdQcoBF9t/download?' + 'path=%2Fkvm-xen-guest&files=rootfs.ext4'), + 'b11045d649006c649c184e93339aaa41a8fe20a1a86620af70323252eb29e40b') + + def common_vm_setup(self): + # We also catch lack of KVM_XEN support if we fail to launch + self.require_accelerator("kvm") + self.require_netdev('user') + + 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.ASSET_KERNEL.fetch() + self.rootfs = self.ASSET_ROOTFS.fetch() + + 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.skipTest("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.skipTest("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') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cmdline', 'xen') + exec_command_and_wait_for_pattern(self, 'dmesg | grep "Grant table"', + 'Grant table initialized') + wait_for_console_pattern(self, '#', 'Oops') + + def test_kvm_xen_guest(self): + self.common_vm_setup() + + self.kernel_params = (self.KERNEL_DEFAULT + + ' xen_emul_unplug=ide-disks') + self.run_and_check() + exec_command_and_wait_for_pattern(self, + 'grep xen-pirq.*msi /proc/interrupts', + 'virtio0-output') + + def test_kvm_xen_guest_nomsi(self): + self.common_vm_setup() + + self.kernel_params = (self.KERNEL_DEFAULT + + ' xen_emul_unplug=ide-disks pci=nomsi') + self.run_and_check() + exec_command_and_wait_for_pattern(self, + 'grep xen-pirq.* /proc/interrupts', + 'virtio0') + + def test_kvm_xen_guest_noapic_nomsi(self): + self.common_vm_setup() + + self.kernel_params = (self.KERNEL_DEFAULT + + ' xen_emul_unplug=ide-disks noapic pci=nomsi') + self.run_and_check() + exec_command_and_wait_for_pattern(self, + 'grep xen-pirq /proc/interrupts', + 'virtio0') + + def test_kvm_xen_guest_vapic(self): + 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() + exec_command_and_wait_for_pattern(self, + 'grep xen-pirq /proc/interrupts', + 'acpi') + wait_for_console_pattern(self, '#') + exec_command_and_wait_for_pattern(self, + 'grep PCI-MSI /proc/interrupts', + 'virtio0-output') + + def test_kvm_xen_guest_novector(self): + self.common_vm_setup() + self.kernel_params = (self.KERNEL_DEFAULT + + ' xen_emul_unplug=ide-disks' + + ' xen_no_vector_callback') + self.run_and_check() + exec_command_and_wait_for_pattern(self, + 'grep xen-platform-pci /proc/interrupts', + 'fasteoi') + + def test_kvm_xen_guest_novector_nomsi(self): + 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() + exec_command_and_wait_for_pattern(self, + 'grep xen-platform-pci /proc/interrupts', + 'IO-APIC') + + def test_kvm_xen_guest_novector_noapic(self): + self.common_vm_setup() + self.kernel_params = (self.KERNEL_DEFAULT + + ' xen_emul_unplug=ide-disks' + + ' xen_no_vector_callback noapic') + self.run_and_check() + exec_command_and_wait_for_pattern(self, + 'grep xen-platform-pci /proc/interrupts', + 'XT-PIC') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_x86_64_replay.py b/tests/functional/test_x86_64_replay.py new file mode 100755 index 00000000000..27287d452dc --- /dev/null +++ b/tests/functional/test_x86_64_replay.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on x86_64 machines +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from subprocess import check_call, DEVNULL + +from qemu_test import Asset, skipFlakyTest, get_qemu_img +from replay_kernel import ReplayKernelBase + + +class X86Replay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/x86_64/bzImage', + 'f57bfc6553bcd6e0a54aab86095bf642b33b5571d14e3af1731b18c87ed5aef8') + + ASSET_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/x86_64/rootfs.ext4.zst', + '4b8b2a99117519c5290e1202cb36eb6c7aaba92b357b5160f5970cf5fb78a751') + + def do_test_x86(self, machine, blkdevice, devroot): + self.require_netdev('user') + self.set_machine(machine) + self.cpu="Nehalem" + kernel_path = self.ASSET_KERNEL.fetch() + + raw_disk = self.uncompress(self.ASSET_ROOTFS) + disk = self.scratch_file('scratch.qcow2') + qemu_img = get_qemu_img(self) + check_call([qemu_img, 'create', '-f', 'qcow2', '-b', raw_disk, + '-F', 'raw', disk], stdout=DEVNULL, stderr=DEVNULL) + + args = ('-drive', 'file=%s,snapshot=on,id=hd0,if=none' % disk, + '-drive', 'driver=blkreplay,id=hd0-rr,if=none,image=hd0', + '-device', '%s,drive=hd0-rr' % blkdevice, + '-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', + '-device', 'virtio-net,netdev=vnet', + '-object', 'filter-replay,id=replay,netdev=vnet') + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + f"console=ttyS0 root=/dev/{devroot}") + console_pattern = 'Welcome to TuxTest' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, + args=args) + + @skipFlakyTest('https://gitlab.com/qemu-project/qemu/-/issues/2094') + def test_pc(self): + self.do_test_x86('pc', 'virtio-blk', 'vda') + + def test_q35(self): + self.do_test_x86('q35', 'ide-hd', 'sda') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_x86_64_reverse_debug.py b/tests/functional/test_x86_64_reverse_debug.py new file mode 100755 index 00000000000..d713e91e144 --- /dev/null +++ b/tests/functional/test_x86_64_reverse_debug.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Reverse debugging test +# +# Copyright (c) 2020 ISP RAS +# +# Author: +# Pavel Dovgalyuk +# +# 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 skipIfMissingImports, skipFlakyTest +from reverse_debugging import ReverseDebugging + + +@skipIfMissingImports('avocado.utils') +class ReverseDebugging_X86_64(ReverseDebugging): + + REG_PC = 0x10 + REG_CS = 0x12 + def get_pc(self, g): + return self.get_reg_le(g, self.REG_PC) \ + + self.get_reg_le(g, self.REG_CS) * 0x10 + + @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/2922") + def test_x86_64_pc(self): + self.set_machine('pc') + # start with BIOS only + self.reverse_debugging() + + +if __name__ == '__main__': + ReverseDebugging.main() diff --git a/tests/functional/test_x86_64_tuxrun.py b/tests/functional/test_x86_64_tuxrun.py new file mode 100755 index 00000000000..fcbc62b1b0f --- /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/buildroot/20241119/x86_64/bzImage', + 'f57bfc6553bcd6e0a54aab86095bf642b33b5571d14e3af1731b18c87ed5aef8') + ASSET_X86_64_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/x86_64/rootfs.ext4.zst', + '4b8b2a99117519c5290e1202cb36eb6c7aaba92b357b5160f5970cf5fb78a751') + + 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 00000000000..bd18acd44fa --- /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 00000000000..147c9208991 --- /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 + + +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' + self.archive_extract(self.ASSET_DAY02) + self.launch_kernel(self.scratch_file('day02', + 'santas-sleigh-ride.elf'), + wait_for='QEMU advent calendar') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_xtensa_replay.py b/tests/functional/test_xtensa_replay.py new file mode 100755 index 00000000000..eb00a3b0044 --- /dev/null +++ b/tests/functional/test_xtensa_replay.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Replay 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 Asset +from replay_kernel import ReplayKernelBase + + +class XTensaReplay(ReplayKernelBase): + + ASSET_DAY02 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day02.tar.xz', + '68ff07f9b3fd3df36d015eb46299ba44748e94bfbb2d5295fddc1a8d4a9fd324') + + def test_replay(self): + self.set_machine('lx60') + self.cpu = 'dc233c' + kernel_path = self.archive_extract(self.ASSET_DAY02, + member='day02/santas-sleigh-ride.elf') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/guest-debug/run-test.py b/tests/guest-debug/run-test.py index b13b27d4b19..75e9c92e036 100755 --- a/tests/guest-debug/run-test.py +++ b/tests/guest-debug/run-test.py @@ -27,11 +27,17 @@ def get_args(): parser.add_argument("--binary", help="Binary to debug", 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") + parser.add_argument("--no-suspend", action="store_true", + help="Ask the binary to not wait for GDB connection") return parser.parse_args() @@ -69,10 +75,19 @@ def log(output, msg): # Launch QEMU with binary if "system" in args.qemu: + if args.no_suspend: + suspend = '' + else: + suspend = ' -S' cmd = f'{args.qemu} {args.qargs} {args.binary}' \ - f' -S -gdb unix:path={socket_name},server=on' + f'{suspend} -gdb unix:path={socket_name},server=on' else: - cmd = f'{args.qemu} {args.qargs} -g {socket_name} {args.binary}' + if args.no_suspend: + suspend = ',suspend=n' + else: + suspend = '' + cmd = f'{args.qemu} {args.qargs} -g {socket_name}{suspend}' \ + f' {args.binary}' log(output, "QEMU CMD: %s" % (cmd)) inferior = subprocess.Popen(shlex.split(cmd)) @@ -91,13 +106,20 @@ def log(output, msg): gdb_cmd += " -ex 'target remote %s'" % (socket_name) # finally the test script itself 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, stderr=stderr) + 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 new file mode 100644 index 00000000000..4f08089e6a9 --- /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/include/meson.build b/tests/include/meson.build index 9abba308fa0..8e8d1ec4e69 100644 --- a/tests/include/meson.build +++ b/tests/include/meson.build @@ -13,4 +13,4 @@ test_qapi_outputs_extra = [ test_qapi_files_extra = custom_target('QAPI test (include)', output: test_qapi_outputs_extra, input: test_qapi_files, - command: 'true') + command: [python, '-c', '']) diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci index bbd55b4d18c..18c4bfe02c4 160000 --- a/tests/lcitool/libvirt-ci +++ b/tests/lcitool/libvirt-ci @@ -1 +1 @@ -Subproject commit bbd55b4d18cce8f89b5167675e434a6941315634 +Subproject commit 18c4bfe02c467e5639bf9a687139735ccd7a3fff diff --git a/tests/lcitool/mappings.yml b/tests/lcitool/mappings.yml index 0b908882f1d..8f0e95e1c56 100644 --- a/tests/lcitool/mappings.yml +++ b/tests/lcitool/mappings.yml @@ -1,85 +1,82 @@ mappings: + # Too old on Ubuntu 22.04; we install it from cargo instead + bindgen: + Ubuntu2204: + flake8: - CentOSStream8: OpenSUSELeap15: meson: - CentOSStream8: OpenSUSELeap15: + # Use Meson from PyPI wherever Rust is enabled + Debian: + Fedora: + Ubuntu: python3: - CentOSStream8: python38 OpenSUSELeap15: python311-base python3-PyYAML: - CentOSStream8: python38-PyYAML OpenSUSELeap15: python3-devel: - CentOSStream8: python38-devel OpenSUSELeap15: python311-devel python3-docutils: - CentOSStream8: OpenSUSELeap15: python3-numpy: - CentOSStream8: python38-numpy OpenSUSELeap15: python3-opencv: - CentOSStream8: OpenSUSELeap15: python3-pillow: - CentOSStream8: OpenSUSELeap15: python3-pip: - CentOSStream8: python38-pip OpenSUSELeap15: python311-pip python3-pillow: - CentOSStream8: OpenSUSELeap15: python3-selinux: - CentOSStream8: OpenSUSELeap15: python3-setuptools: - CentOSStream8: python38-setuptools OpenSUSELeap15: python311-setuptools python3-sphinx: - CentOSStream8: OpenSUSELeap15: python3-sphinx-rtd-theme: - CentOSStream8: OpenSUSELeap15: + python3-sqlite3: + OpenSUSELeap15: python311 + python3-tomli: # test using tomllib apk: Fedora: Debian12: OpenSUSELeap15: - # Not available for Python 3.8 - CentOSStream8: python3-venv: - CentOSStream8: python38 OpenSUSELeap15: python311-base python3-wheel: - CentOSStream8: python38-wheel OpenSUSELeap15: python311-pip + rust: + Debian12: rustc-web + Ubuntu2204: rustc-1.77 + Ubuntu2404: rustc-1.77 + pypi_mappings: # Request more recent version meson: - default: meson==0.63.2 + default: meson==1.8.1 # Drop packages that need devel headers python3-numpy: diff --git a/tests/lcitool/projects/qemu-minimal.yml b/tests/lcitool/projects/qemu-minimal.yml index d44737dc1df..6bc232a1c3e 100644 --- a/tests/lcitool/projects/qemu-minimal.yml +++ b/tests/lcitool/projects/qemu-minimal.yml @@ -7,7 +7,6 @@ packages: - ccache - findutils - flex - - g++ - gcc - gcc-native - glib2 diff --git a/tests/lcitool/projects/qemu-win-installer.yml b/tests/lcitool/projects/qemu-win-installer.yml new file mode 100644 index 00000000000..f3663ba030c --- /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 584f78cb7f9..c07242f2728 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,7 +23,6 @@ packages: - findutils - flex - fuse3 - - g++ - gcc - gcc-native - gcovr @@ -33,28 +33,30 @@ packages: - 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,36 +71,39 @@ 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 - pixman - - libpipewire-dev - 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 @@ -109,6 +114,7 @@ packages: - spice-protocol - spice-server - ssh-client + - swtpm - systemd - tar - tesseract @@ -116,10 +122,10 @@ packages: - usbredir - virglrenderer - vte + - vulkan-tools - which - xen - - xfsprogs - xorriso - - zstdtools - zlib - zlib-static + - zstdtools diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 4584870ea16..d3488b2679e 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -43,12 +43,12 @@ 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) @@ -80,7 +80,7 @@ def generate_dockerfile(host, target, project="qemu", cross=None, trailer=None): 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) @@ -90,22 +90,68 @@ def generate_pkglist(vm, target): 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", + "ENV RUSTDOC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustdoc\n", + "ENV CARGO=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo\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 cargo --version && \\\n", + " /usr/local/cargo/bin/rustup run nightly rustc --version && \\\n", + ' test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \\\n', + ' test "$RUSTDOC" = "$(/usr/local/cargo/bin/rustup +nightly which rustdoc)" && \\\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', + 'RUN $CARGO --list\n', +] + +ubuntu2204_rust_extras = [ + "ENV RUSTC=/usr/bin/rustc-1.77\n", + "ENV RUSTDOC=/usr/bin/rustdoc-1.77\n", + "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) @@ -121,72 +167,78 @@ try: # # Standard native builds # - generate_dockerfile("alpine", "alpine-318") - generate_dockerfile("centos8", "centos-stream-8") - generate_dockerfile("debian-amd64", "debian-11", - trailer="".join(debian11_extras)) - generate_dockerfile("fedora", "fedora-38") + generate_dockerfile("alpine", "alpine-321") + 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("ubuntu2004", "ubuntu-2004") - generate_dockerfile("ubuntu2204", "ubuntu-2204") + generate_dockerfile("ubuntu2204", "ubuntu-2204", + trailer="".join(ubuntu2204_rust_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-riscv64-cross", "debian-sid", + # while not yet a release architecture the packages are still + # build while part of testing + generate_dockerfile("debian-riscv64-cross", "debian-13", project="qemu-minimal", cross="riscv64", trailer=cross_build("riscv64-linux-gnu-", "riscv64-softmmu,riscv64-linux-user")) - generate_dockerfile("debian-s390x-cross", "debian-11", + 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-38", - cross="mingw32", - trailer=cross_build("i686-w64-mingw32-", - "i386-softmmu")) - - generate_dockerfile("fedora-win64-cross", "fedora-38", + generate_dockerfile("fedora-win64-cross", "fedora-40", + project='qemu,qemu-win-installer', cross="mingw64", trailer=cross_build("x86_64-w64-mingw32-", "x86_64-softmmu")) @@ -194,13 +246,20 @@ try: # # Cirrus packages lists for GitLab # - generate_cirrus("freebsd-13") - generate_cirrus("macos-12") + generate_cirrus("freebsd-14") + generate_cirrus("macos-14") # # VM packages lists # - generate_pkglist("freebsd", "freebsd-13") + 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/centos-stream-8.yml b/tests/lcitool/targets/centos-stream-8.yml deleted file mode 100644 index 6b11160fd1d..00000000000 --- a/tests/lcitool/targets/centos-stream-8.yml +++ /dev/null @@ -1,3 +0,0 @@ -paths: - pip3: /usr/bin/pip3.8 - python: /usr/bin/python3.8 diff --git a/tests/meson.build b/tests/meson.build index debaa4505eb..c59619220f7 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -16,6 +16,8 @@ test_qapi_outputs = [ 'test-qapi-events-sub-sub-module.h', 'test-qapi-events.c', 'test-qapi-events.h', + 'test-qapi-features.c', + 'test-qapi-features.h', 'test-qapi-init-commands.c', 'test-qapi-init-commands.h', 'test-qapi-introspect.c', @@ -68,7 +70,7 @@ test_deps = { 'test-qht-par': qht_bench, } -if have_tools and have_vhost_user and targetos == 'linux' +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]) @@ -76,15 +78,13 @@ endif subdir('decode') -if 'CONFIG_TCG' in config_all +if 'CONFIG_TCG' in config_all_accel subdir('fp') -endif - -if get_option('plugins') - subdir('plugin') + subdir('tcg/plugins') endif subdir('unit') subdir('qapi-schema') subdir('qtest') -subdir('migration') +subdir('migration-stress') +subdir('functional') diff --git a/tests/migration/guestperf-batch.py b/tests/migration-stress/guestperf-batch.py similarity index 100% rename from tests/migration/guestperf-batch.py rename to tests/migration-stress/guestperf-batch.py diff --git a/tests/migration/guestperf-plot.py b/tests/migration-stress/guestperf-plot.py similarity index 100% rename from tests/migration/guestperf-plot.py rename to tests/migration-stress/guestperf-plot.py diff --git a/tests/migration/guestperf.py b/tests/migration-stress/guestperf.py similarity index 100% rename from tests/migration/guestperf.py rename to tests/migration-stress/guestperf.py diff --git a/tests/migration/guestperf/__init__.py b/tests/migration-stress/guestperf/__init__.py similarity index 100% rename from tests/migration/guestperf/__init__.py rename to tests/migration-stress/guestperf/__init__.py diff --git a/tests/migration/guestperf/comparison.py b/tests/migration-stress/guestperf/comparison.py similarity index 75% rename from tests/migration/guestperf/comparison.py rename to tests/migration-stress/guestperf/comparison.py index c03b3f6d7e9..dee3ac25e46 100644 --- a/tests/migration/guestperf/comparison.py +++ b/tests/migration-stress/guestperf/comparison.py @@ -127,7 +127,7 @@ def __init__(self, name, scenarios): # varying numbers of channels Comparison("compr-multifd", scenarios = [ Scenario("compr-multifd-channels-4", - multifd=True, multifd_channels=2), + multifd=True, multifd_channels=4), Scenario("compr-multifd-channels-8", multifd=True, multifd_channels=8), Scenario("compr-multifd-channels-32", @@ -135,4 +135,40 @@ def __init__(self, name, scenarios): 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), + ]), + + # Looking at effect of multifd with + # different compression algorithms + Comparison("compr-multifd-compression", scenarios = [ + Scenario("compr-multifd-compression-zlib", + multifd=True, multifd_channels=2, multifd_compression="zlib"), + Scenario("compr-multifd-compression-zstd", + multifd=True, multifd_channels=2, multifd_compression="zstd"), + Scenario("compr-multifd-compression-qpl", + multifd=True, multifd_channels=2, multifd_compression="qpl"), + Scenario("compr-multifd-compression-uadk", + multifd=True, multifd_channels=2, multifd_compression="uadk"), + ]), ] diff --git a/tests/migration-stress/guestperf/engine.py b/tests/migration-stress/guestperf/engine.py new file mode 100644 index 00000000000..d8462db7653 --- /dev/null +++ b/tests/migration-stress/guestperf/engine.py @@ -0,0 +1,536 @@ +# +# Migration test main engine +# +# Copyright (c) 2016 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + + +import os +import re +import sys +import time + +from guestperf.progress import Progress, ProgressStats +from guestperf.report import Report, ReportResult +from guestperf.timings import TimingRecord, Timings + +sys.path.append(os.path.join(os.path.dirname(__file__), + '..', '..', '..', 'python')) +from qemu.machine import QEMUMachine + +# multifd supported compression algorithms +MULTIFD_CMP_ALGS = ("zlib", "zstd", "qpl", "uadk") + +class Engine(object): + + def __init__(self, binary, dst_host, kernel, initrd, transport="tcp", + sleep=15, verbose=False, debug=False): + + self._binary = binary # Path to QEMU binary + self._dst_host = dst_host # Hostname of target host + self._kernel = kernel # Path to kernel image + self._initrd = initrd # Path to stress initrd + self._transport = transport # 'unix' or 'tcp' or 'rdma' + self._sleep = sleep + self._verbose = verbose + self._debug = debug + + if debug: + self._verbose = debug + + def _vcpu_timing(self, pid, tid_list): + records = [] + now = time.time() + + jiffies_per_sec = os.sysconf(os.sysconf_names['SC_CLK_TCK']) + for tid in tid_list: + statfile = "/proc/%d/task/%d/stat" % (pid, tid) + with open(statfile, "r") as fh: + stat = fh.readline() + fields = stat.split(" ") + stime = int(fields[13]) + utime = int(fields[14]) + records.append(TimingRecord(tid, now, 1000 * (stime + utime) / jiffies_per_sec)) + return records + + def _cpu_timing(self, pid): + now = time.time() + + jiffies_per_sec = os.sysconf(os.sysconf_names['SC_CLK_TCK']) + statfile = "/proc/%d/stat" % pid + with open(statfile, "r") as fh: + stat = fh.readline() + fields = stat.split(" ") + stime = int(fields[13]) + utime = int(fields[14]) + return TimingRecord(pid, now, 1000 * (stime + utime) / jiffies_per_sec) + + def _migrate_progress(self, vm): + info = vm.cmd("query-migrate") + + if "ram" not in info: + info["ram"] = {} + + return Progress( + info.get("status", "active"), + ProgressStats( + info["ram"].get("transferred", 0), + info["ram"].get("remaining", 0), + info["ram"].get("total", 0), + info["ram"].get("duplicate", 0), + info["ram"].get("skipped", 0), + info["ram"].get("normal", 0), + info["ram"].get("normal-bytes", 0), + info["ram"].get("dirty-pages-rate", 0), + info["ram"].get("mbps", 0), + info["ram"].get("dirty-sync-count", 0) + ), + time.time(), + info.get("total-time", 0), + info.get("downtime", 0), + 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, defer_migrate): + src_qemu_time = [] + src_vcpu_time = [] + src_pid = src.get_pid() + + vcpus = src.cmd("query-cpus-fast") + src_threads = [] + for vcpu in vcpus: + src_threads.append(vcpu["thread-id"]) + + # XXX how to get dst timings on remote host ? + + if self._verbose: + print("Sleeping %d seconds for initial guest workload run" % self._sleep) + sleep_secs = self._sleep + while sleep_secs > 1: + src_qemu_time.append(self._cpu_timing(src_pid)) + src_vcpu_time.extend(self._vcpu_timing(src_pid, src_threads)) + time.sleep(1) + sleep_secs -= 1 + + if self._verbose: + print("Starting migration") + if scenario._auto_converge: + 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.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "postcopy-ram", + "state": True } + ]) + resp = dst.cmd("migrate-set-capabilities", + capabilities = [ + { "capability": "postcopy-ram", + "state": True } + ]) + + resp = src.cmd("migrate-set-parameters", + max_bandwidth=scenario._bandwidth * 1024 * 1024) + + resp = src.cmd("migrate-set-parameters", + downtime_limit=scenario._downtime) + + if scenario._compression_mt: + 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.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: + if (scenario._multifd_compression and + (scenario._multifd_compression not in MULTIFD_CMP_ALGS)): + raise Exception("unsupported multifd compression " + "algorithm: %s" % + scenario._multifd_compression) + + 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) + + if scenario._multifd_compression: + resp = src.cmd("migrate-set-parameters", + multifd_compression=scenario._multifd_compression) + resp = dst.cmd("migrate-set-parameters", + multifd_compression=scenario._multifd_compression) + + 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) + + if defer_migrate: + resp = dst.cmd("migrate-incoming", uri=connect_uri) + resp = src.cmd("migrate", uri=connect_uri) + + post_copy = False + paused = False + + progress_history = [] + + start = time.time() + loop = 0 + while True: + loop = loop + 1 + time.sleep(0.05) + + progress = self._migrate_progress(src) + if (loop % 20) == 0: + src_qemu_time.append(self._cpu_timing(src_pid)) + src_vcpu_time.extend(self._vcpu_timing(src_pid, src_threads)) + + if (len(progress_history) == 0 or + (progress_history[-1]._ram._iterations < + progress._ram._iterations)): + progress_history.append(progress) + + if progress._status in ("completed", "failed", "cancelled"): + if progress._status == "completed" and paused: + dst.cmd("cont") + if progress_history[-1] != progress: + progress_history.append(progress) + + if progress._status == "completed": + if self._verbose: + print("Sleeping %d seconds for final guest workload run" % self._sleep) + sleep_secs = self._sleep + while sleep_secs > 1: + time.sleep(1) + src_qemu_time.append(self._cpu_timing(src_pid)) + src_vcpu_time.extend(self._vcpu_timing(src_pid, src_threads)) + sleep_secs -= 1 + + result = ReportResult() + if progress._status == "completed" and not paused: + result = ReportResult(True) + + return [progress_history, src_qemu_time, src_vcpu_time, result] + + if self._verbose and (loop % 20) == 0: + print("Iter %d: remain %5dMB of %5dMB (total %5dMB @ %5dMb/sec)" % ( + progress._ram._iterations, + progress._ram._remaining_bytes / (1024 * 1024), + progress._ram._total_bytes / (1024 * 1024), + progress._ram._transferred_bytes / (1024 * 1024), + progress._ram._transfer_rate_mbs, + )) + + if progress._ram._iterations > scenario._max_iters: + if self._verbose: + print("No completion after %d iterations over RAM" % scenario._max_iters) + 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.cmd("migrate_cancel") + continue + + if (scenario._post_copy and + progress._ram._iterations >= scenario._post_copy_iters and + not post_copy): + if self._verbose: + print("Switching to post-copy after %d iterations" % scenario._post_copy_iters) + resp = src.cmd("migrate-start-postcopy") + post_copy = True + + if (scenario._pause and + progress._ram._iterations >= scenario._pause_iters and + not paused): + if self._verbose: + print("Pausing VM after %d iterations" % scenario._pause_iters) + 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", + "edd=off", + "printk.time=1", + "noreplace-smp", + "cgroup_disable=memory", + "pci=noearly", + ] + + args.append(self._get_guest_console_args()) + + if self._debug: + args.append("debug") + else: + args.append("quiet") + + args.append("ramsize=%s" % hardware._mem) + + cmdline = " ".join(args) + if tunnelled: + cmdline = "'" + cmdline + "'" + + argv = [ + "-cpu", "host", + "-kernel", self._kernel, + "-initrd", self._initrd, + "-append", cmdline, + "-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(["-machine", "graphics=off"]) + + if hardware._prealloc_pages: + argv_source += ["-mem-path", "/dev/shm", + "-mem-prealloc"] + if hardware._locked_pages: + argv_source += ["-overcommit", "mem-lock=on"] + if hardware._huge_pages: + pass + + return argv + + def _get_src_args(self, hardware): + return self._get_common_args(hardware) + + def _get_dst_args(self, hardware, uri, defer_migrate): + tunnelled = False + if self._dst_host != "localhost": + tunnelled = True + argv = self._get_common_args(hardware, tunnelled) + + if defer_migrate: + return argv + ["-incoming", "defer"] + return argv + ["-incoming", uri] + + @staticmethod + def _get_common_wrapper(cpu_bind, mem_bind): + wrapper = [] + if len(cpu_bind) > 0 or len(mem_bind) > 0: + wrapper.append("numactl") + if cpu_bind: + wrapper.append("--physcpubind=%s" % ",".join(cpu_bind)) + if mem_bind: + wrapper.append("--membind=%s" % ",".join(mem_bind)) + + return wrapper + + def _get_src_wrapper(self, hardware): + return self._get_common_wrapper(hardware._src_cpu_bind, hardware._src_mem_bind) + + def _get_dst_wrapper(self, hardware): + wrapper = self._get_common_wrapper(hardware._dst_cpu_bind, hardware._dst_mem_bind) + if self._dst_host != "localhost": + return ["ssh", + "-R", "9001:localhost:9001", + self._dst_host] + wrapper + else: + return wrapper + + def _get_timings(self, vm): + log = vm.get_log() + if not log: + return [] + if self._debug: + print(log) + + regex = r"[^\s]+\s\((\d+)\):\sINFO:\s(\d+)ms\scopied\s\d+\sGB\sin\s(\d+)ms" + matcher = re.compile(regex) + records = [] + for line in log.split("\n"): + match = matcher.match(line) + if match: + records.append(TimingRecord(int(match.group(1)), + int(match.group(2)) / 1000.0, + int(match.group(3)))) + return records + + def run(self, hardware, scenario, result_dir=os.getcwd()): + abs_result_dir = os.path.join(result_dir, scenario._name) + defer_migrate = False + + if self._transport == "tcp": + uri = "tcp:%s:9000" % self._dst_host + elif self._transport == "rdma": + uri = "rdma:%s:9000" % self._dst_host + elif self._transport == "unix": + if self._dst_host != "localhost": + raise Exception("Running use unix migration transport for non-local host") + uri = "unix:/var/tmp/qemu-migrate-%d.migrate" % os.getpid() + try: + os.remove(uri[5:]) + os.remove(monaddr) + except: + pass + + if scenario._multifd: + defer_migrate = True + + if self._dst_host != "localhost": + dstmonaddr = ("localhost", 9001) + else: + dstmonaddr = "/var/tmp/qemu-dst-%d-monitor.sock" % os.getpid() + srcmonaddr = "/var/tmp/qemu-src-%d-monitor.sock" % os.getpid() + + src = QEMUMachine(self._binary, + args=self._get_src_args(hardware), + wrapper=self._get_src_wrapper(hardware), + name="qemu-src-%d" % os.getpid(), + monitor_address=srcmonaddr) + + dst = QEMUMachine(self._binary, + args=self._get_dst_args(hardware, uri, defer_migrate), + wrapper=self._get_dst_wrapper(hardware), + name="qemu-dst-%d" % os.getpid(), + monitor_address=dstmonaddr) + + try: + src.launch() + dst.launch() + + ret = self._migrate(hardware, scenario, src, + dst, uri, defer_migrate) + progress_history = ret[0] + qemu_timings = ret[1] + vcpu_timings = ret[2] + result = ret[3] + if uri[0:5] == "unix:" and os.path.exists(uri[5:]): + os.remove(uri[5:]) + + if os.path.exists(srcmonaddr): + os.remove(srcmonaddr) + + if self._dst_host == "localhost" and os.path.exists(dstmonaddr): + os.remove(dstmonaddr) + + if self._verbose: + print("Finished migration") + + src.shutdown() + dst.shutdown() + + return Report(hardware, scenario, progress_history, + Timings(self._get_timings(src) + self._get_timings(dst)), + Timings(qemu_timings), + Timings(vcpu_timings), + result, + self._binary, self._dst_host, self._kernel, + self._initrd, self._transport, self._sleep) + except Exception as e: + if self._debug: + print("Failed: %s" % str(e)) + try: + src.shutdown() + except: + pass + try: + dst.shutdown() + except: + pass + + if self._debug: + print(src.get_log()) + print(dst.get_log()) + raise + diff --git a/tests/migration/guestperf/hardware.py b/tests/migration-stress/guestperf/hardware.py similarity index 89% rename from tests/migration/guestperf/hardware.py rename to tests/migration-stress/guestperf/hardware.py index 3145785ffd7..f779cc050be 100644 --- a/tests/migration/guestperf/hardware.py +++ b/tests/migration-stress/guestperf/hardware.py @@ -23,7 +23,8 @@ def __init__(self, cpus=1, mem=1, 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 @@ def __init__(self, cpus=1, mem=1, 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 @@ def serialize(self): "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 @@ def deserialize(cls, data): 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/plot.py b/tests/migration-stress/guestperf/plot.py similarity index 100% rename from tests/migration/guestperf/plot.py rename to tests/migration-stress/guestperf/plot.py diff --git a/tests/migration/guestperf/progress.py b/tests/migration-stress/guestperf/progress.py similarity index 85% rename from tests/migration/guestperf/progress.py rename to tests/migration-stress/guestperf/progress.py index ab1ee572739..d4905842172 100644 --- a/tests/migration/guestperf/progress.py +++ b/tests/migration-stress/guestperf/progress.py @@ -81,7 +81,9 @@ def __init__(self, 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 @@ def __init__(self, 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 @@ def serialize(self): "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 @@ def deserialize(cls, data): 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/report.py b/tests/migration-stress/guestperf/report.py similarity index 88% rename from tests/migration/guestperf/report.py rename to tests/migration-stress/guestperf/report.py index 1efd40c8680..e135e01be6e 100644 --- a/tests/migration/guestperf/report.py +++ b/tests/migration-stress/guestperf/report.py @@ -24,6 +24,22 @@ from guestperf.progress import Progress from guestperf.timings import Timings +class ReportResult(object): + + def __init__(self, success=False): + self._success = success + + def serialize(self): + return { + "success": self._success, + } + + @classmethod + def deserialize(cls, data): + return cls( + data["success"]) + + class Report(object): def __init__(self, @@ -33,6 +49,7 @@ def __init__(self, guest_timings, qemu_timings, vcpu_timings, + result, binary, dst_host, kernel, @@ -46,6 +63,7 @@ def __init__(self, self._guest_timings = guest_timings self._qemu_timings = qemu_timings self._vcpu_timings = vcpu_timings + self._result = result self._binary = binary self._dst_host = dst_host self._kernel = kernel @@ -61,6 +79,7 @@ def serialize(self): "guest_timings": self._guest_timings.serialize(), "qemu_timings": self._qemu_timings.serialize(), "vcpu_timings": self._vcpu_timings.serialize(), + "result": self._result.serialize(), "binary": self._binary, "dst_host": self._dst_host, "kernel": self._kernel, @@ -78,6 +97,7 @@ def deserialize(cls, data): Timings.deserialize(data["guest_timings"]), Timings.deserialize(data["qemu_timings"]), Timings.deserialize(data["vcpu_timings"]), + ReportResult.deserialize(data["result"]), data["binary"], data["dst_host"], data["kernel"], diff --git a/tests/migration/guestperf/scenario.py b/tests/migration-stress/guestperf/scenario.py similarity index 83% rename from tests/migration/guestperf/scenario.py rename to tests/migration-stress/guestperf/scenario.py index de70d9b2f59..4be7fafebf9 100644 --- a/tests/migration/guestperf/scenario.py +++ b/tests/migration-stress/guestperf/scenario.py @@ -30,7 +30,9 @@ def __init__(self, name, 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, multifd_compression="", + dirty_limit=False, x_vcpu_dirty_limit_period=500, + vcpu_dirty_limit=1): self._name = name @@ -59,6 +61,11 @@ def __init__(self, name, self._multifd = multifd self._multifd_channels = multifd_channels + self._multifd_compression = multifd_compression + + 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 { @@ -79,6 +86,10 @@ def serialize(self): "compression_xbzrle_cache": self._compression_xbzrle_cache, "multifd": self._multifd, "multifd_channels": self._multifd_channels, + "multifd_compression": self._multifd_compression, + "dirty_limit": self._dirty_limit, + "x_vcpu_dirty_limit_period": self._x_vcpu_dirty_limit_period, + "vcpu_dirty_limit": self._vcpu_dirty_limit, } @classmethod @@ -100,4 +111,5 @@ def deserialize(cls, data): data["compression_xbzrle"], data["compression_xbzrle_cache"], data["multifd"], - data["multifd_channels"]) + data["multifd_channels"], + data["multifd_compression"]) diff --git a/tests/migration/guestperf/shell.py b/tests/migration-stress/guestperf/shell.py similarity index 89% rename from tests/migration/guestperf/shell.py rename to tests/migration-stress/guestperf/shell.py index 8a809e3dda0..63bbe3226c6 100644 --- a/tests/migration/guestperf/shell.py +++ b/tests/migration-stress/guestperf/shell.py @@ -46,7 +46,8 @@ def __init__(self): parser.add_argument("--binary", dest="binary", default="/usr/bin/qemu-system-x86_64") parser.add_argument("--dst-host", dest="dst_host", default="localhost") parser.add_argument("--kernel", dest="kernel", default="/boot/vmlinuz-%s" % platform.release()) - parser.add_argument("--initrd", dest="initrd", default="tests/migration/initrd-stress.img") + parser.add_argument("--initrd", dest="initrd", + default="tests/migration-stress/initrd-stress.img") parser.add_argument("--transport", dest="transport", default="unix") @@ -60,6 +61,8 @@ def __init__(self): 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 +92,9 @@ def split_map(value): 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): @@ -126,6 +131,19 @@ def __init__(self): action="store_true") parser.add_argument("--multifd-channels", dest="multifd_channels", default=2, type=int) + parser.add_argument("--multifd-compression", dest="multifd_compression", + default="") + + 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", @@ -150,7 +168,13 @@ def get_scenario(self, args): compression_xbzrle_cache=args.compression_xbzrle_cache, multifd=args.multifd, - multifd_channels=args.multifd_channels) + multifd_channels=args.multifd_channels, + multifd_compression=args.multifd_compression, + + 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/guestperf/timings.py b/tests/migration-stress/guestperf/timings.py similarity index 100% rename from tests/migration/guestperf/timings.py rename to tests/migration-stress/guestperf/timings.py diff --git a/tests/migration/initrd-stress.sh b/tests/migration-stress/initrd-stress.sh similarity index 100% rename from tests/migration/initrd-stress.sh rename to tests/migration-stress/initrd-stress.sh diff --git a/tests/migration/meson.build b/tests/migration-stress/meson.build similarity index 100% rename from tests/migration/meson.build rename to tests/migration-stress/meson.build diff --git a/tests/migration/stress.c b/tests/migration-stress/stress.c similarity index 100% rename from tests/migration/stress.c rename to tests/migration-stress/stress.c diff --git a/tests/migration/Makefile b/tests/migration/Makefile deleted file mode 100644 index 13e99b1692f..00000000000 --- a/tests/migration/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -# -# Copyright (c) 2018 Red Hat, Inc. 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. -# - -TARGET_LIST = i386 aarch64 s390x - -SRC_PATH = ../.. - -.PHONY: help $(TARGET_LIST) -help: - @echo "Create migration guest includes. We generate a binary." - @echo "And then convert that binary to an include file that can be" - @echo "run in a guest." - @echo "Possible operations are:" - @echo - @echo " $(MAKE) clean Remove all intermediate files" - @echo " $(MAKE) target Generate for that target" - @echo " $(MAKE) CROSS_PREFIX=... target" - @echo " Cross-compile than target" - @echo " Possible targets are: $(TARGET_LIST)" - -override define __note -/* This file is automatically generated from the assembly file in - * tests/migration/$@. 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. - */ -endef -export __note - -$(TARGET_LIST): - $(MAKE) CROSS_PREFIX=$(CROSS_PREFIX) -C $@ - -clean: - for target in $(TARGET_LIST); do \ - $(MAKE) -C $$target clean; \ - done diff --git a/tests/migration/guestperf/engine.py b/tests/migration/guestperf/engine.py deleted file mode 100644 index e69d16a62c8..00000000000 --- a/tests/migration/guestperf/engine.py +++ /dev/null @@ -1,484 +0,0 @@ -# -# Migration test main engine -# -# Copyright (c) 2016 Red Hat, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, see . -# - - -import os -import re -import sys -import time - -from guestperf.progress import Progress, ProgressStats -from guestperf.report import Report -from guestperf.timings import TimingRecord, Timings - -sys.path.append(os.path.join(os.path.dirname(__file__), - '..', '..', '..', 'python')) -from qemu.machine import QEMUMachine - - -class Engine(object): - - def __init__(self, binary, dst_host, kernel, initrd, transport="tcp", - sleep=15, verbose=False, debug=False): - - self._binary = binary # Path to QEMU binary - self._dst_host = dst_host # Hostname of target host - self._kernel = kernel # Path to kernel image - self._initrd = initrd # Path to stress initrd - self._transport = transport # 'unix' or 'tcp' or 'rdma' - self._sleep = sleep - self._verbose = verbose - self._debug = debug - - if debug: - self._verbose = debug - - def _vcpu_timing(self, pid, tid_list): - records = [] - now = time.time() - - jiffies_per_sec = os.sysconf(os.sysconf_names['SC_CLK_TCK']) - for tid in tid_list: - statfile = "/proc/%d/task/%d/stat" % (pid, tid) - with open(statfile, "r") as fh: - stat = fh.readline() - fields = stat.split(" ") - stime = int(fields[13]) - utime = int(fields[14]) - records.append(TimingRecord(tid, now, 1000 * (stime + utime) / jiffies_per_sec)) - return records - - def _cpu_timing(self, pid): - now = time.time() - - jiffies_per_sec = os.sysconf(os.sysconf_names['SC_CLK_TCK']) - statfile = "/proc/%d/stat" % pid - with open(statfile, "r") as fh: - stat = fh.readline() - fields = stat.split(" ") - stime = int(fields[13]) - utime = int(fields[14]) - return TimingRecord(pid, now, 1000 * (stime + utime) / jiffies_per_sec) - - def _migrate_progress(self, vm): - info = vm.command("query-migrate") - - if "ram" not in info: - info["ram"] = {} - - return Progress( - info.get("status", "active"), - ProgressStats( - info["ram"].get("transferred", 0), - info["ram"].get("remaining", 0), - info["ram"].get("total", 0), - info["ram"].get("duplicate", 0), - info["ram"].get("skipped", 0), - info["ram"].get("normal", 0), - info["ram"].get("normal-bytes", 0), - info["ram"].get("dirty-pages-rate", 0), - info["ram"].get("mbps", 0), - info["ram"].get("dirty-sync-count", 0) - ), - time.time(), - info.get("total-time", 0), - info.get("downtime", 0), - info.get("expected-downtime", 0), - info.get("setup-time", 0), - info.get("cpu-throttle-percentage", 0), - ) - - def _migrate(self, hardware, scenario, src, dst, connect_uri): - src_qemu_time = [] - src_vcpu_time = [] - src_pid = src.get_pid() - - vcpus = src.command("query-cpus-fast") - src_threads = [] - for vcpu in vcpus: - src_threads.append(vcpu["thread-id"]) - - # XXX how to get dst timings on remote host ? - - if self._verbose: - print("Sleeping %d seconds for initial guest workload run" % self._sleep) - sleep_secs = self._sleep - while sleep_secs > 1: - src_qemu_time.append(self._cpu_timing(src_pid)) - src_vcpu_time.extend(self._vcpu_timing(src_pid, src_threads)) - time.sleep(1) - sleep_secs -= 1 - - 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) - - 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.command("migrate-set-parameters", - max_bandwidth=scenario._bandwidth * 1024 * 1024) - - resp = src.command("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) - - 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)) - - 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.command("migrate", uri=connect_uri) - - post_copy = False - paused = False - - progress_history = [] - - start = time.time() - loop = 0 - while True: - loop = loop + 1 - time.sleep(0.05) - - progress = self._migrate_progress(src) - if (loop % 20) == 0: - src_qemu_time.append(self._cpu_timing(src_pid)) - src_vcpu_time.extend(self._vcpu_timing(src_pid, src_threads)) - - if (len(progress_history) == 0 or - (progress_history[-1]._ram._iterations < - progress._ram._iterations)): - progress_history.append(progress) - - if progress._status in ("completed", "failed", "cancelled"): - if progress._status == "completed" and paused: - dst.command("cont") - if progress_history[-1] != progress: - progress_history.append(progress) - - if progress._status == "completed": - if self._verbose: - print("Sleeping %d seconds for final guest workload run" % self._sleep) - sleep_secs = self._sleep - while sleep_secs > 1: - time.sleep(1) - src_qemu_time.append(self._cpu_timing(src_pid)) - src_vcpu_time.extend(self._vcpu_timing(src_pid, src_threads)) - sleep_secs -= 1 - - return [progress_history, src_qemu_time, src_vcpu_time] - - if self._verbose and (loop % 20) == 0: - print("Iter %d: remain %5dMB of %5dMB (total %5dMB @ %5dMb/sec)" % ( - progress._ram._iterations, - progress._ram._remaining_bytes / (1024 * 1024), - progress._ram._total_bytes / (1024 * 1024), - progress._ram._transferred_bytes / (1024 * 1024), - progress._ram._transfer_rate_mbs, - )) - - 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") - continue - - if time.time() > (start + scenario._max_time): - if self._verbose: - print("No completion after %d seconds" % scenario._max_time) - src.command("migrate_cancel") - continue - - if (scenario._post_copy and - progress._ram._iterations >= scenario._post_copy_iters and - not post_copy): - if self._verbose: - print("Switching to post-copy after %d iterations" % scenario._post_copy_iters) - resp = src.command("migrate-start-postcopy") - post_copy = True - - if (scenario._pause and - progress._ram._iterations >= scenario._pause_iters and - not paused): - if self._verbose: - print("Pausing VM after %d iterations" % scenario._pause_iters) - resp = src.command("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", - "edd=off", - "printk.time=1", - "noreplace-smp", - "cgroup_disable=memory", - "pci=noearly", - ] - - args.append(self._get_guest_console_args()) - - if self._debug: - args.append("debug") - else: - args.append("quiet") - - args.append("ramsize=%s" % hardware._mem) - - cmdline = " ".join(args) - if tunnelled: - cmdline = "'" + cmdline + "'" - - argv = [ - "-accel", "kvm", - "-cpu", "host", - "-kernel", self._kernel, - "-initrd", self._initrd, - "-append", cmdline, - "-m", str((hardware._mem * 1024) + 512), - "-smp", str(hardware._cpus), - ] - - argv.extend(self._get_qemu_serial_args()) - - if self._debug: - argv.extend(["-machine", "graphics=off"]) - - if hardware._prealloc_pages: - argv_source += ["-mem-path", "/dev/shm", - "-mem-prealloc"] - if hardware._locked_pages: - argv_source += ["-overcommit", "mem-lock=on"] - if hardware._huge_pages: - pass - - return argv - - def _get_src_args(self, hardware): - return self._get_common_args(hardware) - - def _get_dst_args(self, hardware, uri): - tunnelled = False - if self._dst_host != "localhost": - tunnelled = True - argv = self._get_common_args(hardware, tunnelled) - return argv + ["-incoming", uri] - - @staticmethod - def _get_common_wrapper(cpu_bind, mem_bind): - wrapper = [] - if len(cpu_bind) > 0 or len(mem_bind) > 0: - wrapper.append("numactl") - if cpu_bind: - wrapper.append("--physcpubind=%s" % ",".join(cpu_bind)) - if mem_bind: - wrapper.append("--membind=%s" % ",".join(mem_bind)) - - return wrapper - - def _get_src_wrapper(self, hardware): - return self._get_common_wrapper(hardware._src_cpu_bind, hardware._src_mem_bind) - - def _get_dst_wrapper(self, hardware): - wrapper = self._get_common_wrapper(hardware._dst_cpu_bind, hardware._dst_mem_bind) - if self._dst_host != "localhost": - return ["ssh", - "-R", "9001:localhost:9001", - self._dst_host] + wrapper - else: - return wrapper - - def _get_timings(self, vm): - log = vm.get_log() - if not log: - return [] - if self._debug: - print(log) - - regex = r"[^\s]+\s\((\d+)\):\sINFO:\s(\d+)ms\scopied\s\d+\sGB\sin\s(\d+)ms" - matcher = re.compile(regex) - records = [] - for line in log.split("\n"): - match = matcher.match(line) - if match: - records.append(TimingRecord(int(match.group(1)), - int(match.group(2)) / 1000.0, - int(match.group(3)))) - return records - - def run(self, hardware, scenario, result_dir=os.getcwd()): - abs_result_dir = os.path.join(result_dir, scenario._name) - - if self._transport == "tcp": - uri = "tcp:%s:9000" % self._dst_host - elif self._transport == "rdma": - uri = "rdma:%s:9000" % self._dst_host - elif self._transport == "unix": - if self._dst_host != "localhost": - raise Exception("Running use unix migration transport for non-local host") - uri = "unix:/var/tmp/qemu-migrate-%d.migrate" % os.getpid() - try: - os.remove(uri[5:]) - os.remove(monaddr) - except: - pass - - if self._dst_host != "localhost": - dstmonaddr = ("localhost", 9001) - else: - dstmonaddr = "/var/tmp/qemu-dst-%d-monitor.sock" % os.getpid() - srcmonaddr = "/var/tmp/qemu-src-%d-monitor.sock" % os.getpid() - - src = QEMUMachine(self._binary, - args=self._get_src_args(hardware), - wrapper=self._get_src_wrapper(hardware), - name="qemu-src-%d" % os.getpid(), - monitor_address=srcmonaddr) - - dst = QEMUMachine(self._binary, - args=self._get_dst_args(hardware, uri), - wrapper=self._get_dst_wrapper(hardware), - name="qemu-dst-%d" % os.getpid(), - monitor_address=dstmonaddr) - - try: - src.launch() - dst.launch() - - ret = self._migrate(hardware, scenario, src, dst, uri) - progress_history = ret[0] - qemu_timings = ret[1] - vcpu_timings = ret[2] - if uri[0:5] == "unix:" and os.path.exists(uri[5:]): - os.remove(uri[5:]) - - if os.path.exists(srcmonaddr): - os.remove(srcmonaddr) - - if self._dst_host == "localhost" and os.path.exists(dstmonaddr): - os.remove(dstmonaddr) - - if self._verbose: - print("Finished migration") - - src.shutdown() - dst.shutdown() - - return Report(hardware, scenario, progress_history, - Timings(self._get_timings(src) + self._get_timings(dst)), - Timings(qemu_timings), - Timings(vcpu_timings), - self._binary, self._dst_host, self._kernel, - self._initrd, self._transport, self._sleep) - except Exception as e: - if self._debug: - print("Failed: %s" % str(e)) - try: - src.shutdown() - except: - pass - try: - dst.shutdown() - except: - pass - - if self._debug: - print(src.get_log()) - print(dst.get_log()) - raise - diff --git a/tests/migration/i386/Makefile b/tests/migration/i386/Makefile deleted file mode 100644 index 5c0324134a7..00000000000 --- a/tests/migration/i386/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# To specify cross compiler prefix, use CROSS_PREFIX= -# $ make CROSS_PREFIX=x86_64-linux-gnu- - -.PHONY: all clean -all: a-b-bootblock.h - -a-b-bootblock.h: x86.bootsect - echo "$$__note" > header.tmp - xxd -i $< | sed -e 's/.*int.*//' >> header.tmp - mv header.tmp $@ - -x86.bootsect: x86.boot - dd if=$< of=$@ bs=256 count=2 skip=124 - -x86.boot: x86.o - $(CROSS_PREFIX)objcopy -O binary $< $@ - -x86.o: a-b-bootblock.S - $(CROSS_PREFIX)gcc -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 deleted file mode 100644 index 3d464c7568e..00000000000 --- a/tests/migration/i386/a-b-bootblock.S +++ /dev/null @@ -1,89 +0,0 @@ -# x86 bootblock used in migration test -# repeatedly increments the first byte of each page in a 100MB -# range. -# Outputs an initial 'A' on serial followed by repeated 'B's -# -# Copyright (c) 2016 Red Hat, Inc. 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. -# -# Author: dgilbert@redhat.com - - -.code16 -.org 0x7c00 - .file "fill.s" - .text - .globl start - .type start, @function -start: # at 0x7c00 ? - cli - lgdt gdtdesc - mov $1,%eax - mov %eax,%cr0 # Protected mode enable - data32 ljmp $8,$0x7c20 - -.org 0x7c20 -.code32 - # A20 enable - not sure I actually need this - inb $0x92,%al - or $2,%al - outb %al, $0x92 - - # set up DS for the whole of RAM (needed on KVM) - mov $16,%eax - mov %eax,%ds - - mov $65,%ax - mov $0x3f8,%dx - outb %al,%dx - - # bl keeps a counter so we limit the output speed - mov $0, %bl -mainloop: - # Start from 1MB - mov $(1024*1024),%eax -innerloop: - incb (%eax) - add $4096,%eax - cmp $(100*1024*1024),%eax - jl innerloop - - inc %bl - andb $0x3f,%bl - jnz mainloop - - mov $66,%ax - mov $0x3f8,%dx - outb %al,%dx - - jmp mainloop - - # GDT magic from old (GPLv2) Grub startup.S - .p2align 2 /* force 4-byte alignment */ -gdt: - .word 0, 0 - .byte 0, 0, 0, 0 - - /* -- code segment -- - * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity), present - * type = 32bit code execute/read, DPL = 0 - */ - .word 0xFFFF, 0 - .byte 0, 0x9A, 0xCF, 0 - - /* -- data segment -- - * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present - * type = 32 bit data read/write, DPL = 0 - */ - .word 0xFFFF, 0 - .byte 0, 0x92, 0xCF, 0 - -gdtdesc: - .word 0x27 /* limit */ - .long gdt /* addr */ - -/* I'm a bootable disk */ -.org 0x7dfe - .byte 0x55 - .byte 0xAA diff --git a/tests/migration/i386/a-b-bootblock.h b/tests/migration/i386/a-b-bootblock.h deleted file mode 100644 index b7b0fce2ee1..00000000000 --- a/tests/migration/i386/a-b-bootblock.h +++ /dev/null @@ -1,51 +0,0 @@ -/* This file is automatically generated from the assembly file in - * tests/migration/i386. 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 x86_bootsect[] = { - 0xfa, 0x0f, 0x01, 0x16, 0x78, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa -}; - diff --git a/tests/migration/migration-test.h b/tests/migration/migration-test.h deleted file mode 100644 index 68512c0b1b9..00000000000 --- a/tests/migration/migration-test.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2018 Red Hat, Inc. 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 MIGRATION_TEST_H -#define MIGRATION_TEST_H - -/* Common */ -#define TEST_MEM_PAGE_SIZE 4096 - -/* x86 */ -#define X86_TEST_MEM_START (1 * 1024 * 1024) -#define X86_TEST_MEM_END (100 * 1024 * 1024) - -/* S390 */ -#define S390_TEST_MEM_START (1 * 1024 * 1024) -#define S390_TEST_MEM_END (100 * 1024 * 1024) - -/* PPC */ -#define PPC_TEST_MEM_START (1 * 1024 * 1024) -#define PPC_TEST_MEM_END (100 * 1024 * 1024) - -/* ARM */ -#define ARM_TEST_MEM_START (0x40000000 + 1 * 1024 * 1024) -#define ARM_TEST_MEM_END (0x40000000 + 100 * 1024 * 1024) -#define ARM_MACH_VIRT_UART 0x09000000 -/* AArch64 kernel load address is 0x40080000, and the test memory starts at - * 0x40100000. So the maximum allowable kernel size is 512KB. - */ -#define ARM_TEST_MAX_KERNEL_SIZE (512 * 1024) - -#endif /* MIGRATION_TEST_H */ diff --git a/tests/migration/s390x/a-b-bios.h b/tests/migration/s390x/a-b-bios.h deleted file mode 100644 index e722dc7c40f..00000000000 --- a/tests/migration/s390x/a-b-bios.h +++ /dev/null @@ -1,253 +0,0 @@ -/* This file is automatically generated from the a-b-bios.c file in - * tests/migration/s390x. 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 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, 0x40, 0x00, 0x38, 0x00, 0x07, 0x00, 0x40, - 0x00, 0x0c, 0x00, 0x0b, 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, - 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 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, 0x0f, - 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, 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, 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, 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, - 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, - 0xf0, 0x00, 0x00, 0x25, 0x94, 0xfd, 0xf0, 0x06, 0xeb, 0x00, 0xf0, 0x00, - 0x00, 0x2f, 0x07, 0xfe, 0x07, 0x07, 0x07, 0x07, 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, - 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, 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, - 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, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 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, 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 df50d1fd3bc..00000000000 --- 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_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; - } - } - - 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 5fd3017c2b3..00000000000 --- a/tests/plugin/insn.c +++ /dev/null @@ -1,218 +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 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]; - - 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_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 { - 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 44e91065ba7..00000000000 --- 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_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 { - 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 2bbfc4b19e1..00000000000 --- 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 72e1a5bf901..00000000000 --- 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_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 { - 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 a657d85738f..2f30973ac3f 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/comments.out b/tests/qapi-schema/comments.out index ce4f6a4f0f5..937070c2c42 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 d7286bb57c4..1f6b7d2fe40 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 fa4143da4c4..37593b6698c 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 7137af3ec92..d6793e7fd84 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 18ed076cef6..b76167ec60b 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 7efeb473635..0aa8d8e8e20 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 d13cacf21f0..90527d5f827 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 49d1746c3d1..3166c6a305a 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-union-member.err b/tests/qapi-schema/doc-bad-union-member.err index 6dd2726a656..cdf1225cab3 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 00000000000..cadb2957a6d --- /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 00000000000..a4d559e7408 --- /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 00000000000..e69de29bb2d diff --git a/tests/qapi-schema/doc-duplicated-arg.err b/tests/qapi-schema/doc-duplicated-arg.err index 0d0d777a1f9..d876312734d 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 fe97e3db8dc..503b916b256 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 b44b5ae979b..4e1ec2ef42f 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 abca141a2c6..a9b60c0c3de 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 343cd872cb5..2755f95719b 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 2d0f35f310f..83f4fc66d54 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 00000000000..2709a18d8f6 --- /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 00000000000..06f814e45d7 --- /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 00000000000..e69de29bb2d diff --git a/tests/qapi-schema/doc-empty-section.err b/tests/qapi-schema/doc-empty-section.err index ba7ba701258..711a0d629c2 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 f3384e9a3bb..f179d3eff6d 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 354dfdf4616..14b808f9090 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -3,11 +3,19 @@ # # Positive QAPI doc comment tests -{ 'pragma': { 'doc-required': true } } +{ 'pragma': { + 'doc-required': true, + 'documentation-exceptions': [ 'Enum', 'Variant1', 'Alternate', 'cmd' ] } } ## # = Section -# +## + +## +# Just text, no heading. +## + +## # == Subsection # # *with emphasis* @@ -51,6 +59,8 @@ # - {braces} ## +# Not a doc comment + ## # @Enum: # @@ -73,8 +83,8 @@ # @Base: # # @base1: -# description starts on a new line, -# not indented +# description starts on a new line, +# minimally indented ## { 'struct': 'Base', 'data': { 'base1': 'Enum' }, 'if': { 'all': ['IFALL1', 'IFALL2'] } } @@ -150,22 +160,39 @@ # 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', @@ -176,14 +203,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 24d9ea954db..dc8352eed4f 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 @@ -57,6 +56,9 @@ event EVT_BOXED Object doc freeform body= = Section +doc freeform + body= +Just text, no heading. doc freeform body= == Subsection @@ -111,14 +113,14 @@ The _one_ {and only}, description on the same line Also _one_ {and only} feature=enum-member-feat a member feature - section=None + section=Plain @two is undocumented doc symbol=Base body= arg=base1 -description starts on a new line, -not indented + description starts on a new line, + minimally indented doc symbol=Variant1 body= A paragraph @@ -145,8 +147,8 @@ doc symbol=Alternate arg=i description starts on the same line -remainder indented the same -@b is undocumented + remainder indented the same + @b is undocumented arg=b feature=alt-feat @@ -158,34 +160,49 @@ doc symbol=cmd body= arg=arg1 -description starts on a new line, -indented + description starts on a new line, + indented arg=arg2 description starts on the same line -remainder indented differently + remainder indented differently arg=arg3 feature=cmd-feat1 a feature feature=cmd-feat2 another feature - section=Note -@arg3 is undocumented + section=Plain +.. note:: @arg3 is undocumented section=Returns @Object - section=TODO + section=Errors +some + section=Todo frobnicate - section=Notes -- Lorem ipsum dolor sit amet -- Ut enim ad minim veniam + section=Plain +.. 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 @@ -195,10 +212,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=Plain +.. 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 b3b76bd43fd..17a1d56ef1a 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 -~~~~ - -frobnicate +Errors +~~~~~~ +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 -~~~~~~~ - - -> in + -> "this example" - <- out + <- ... has no title ... "EVT_BOXED" (Event) diff --git a/tests/qapi-schema/doc-interleaved-section.json b/tests/qapi-schema/doc-interleaved-section.json index adb29e98daa..eec01ed5650 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 2ad89c59411..aafd57b135a 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 95e75839307..1aabef3482b 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 00000000000..c3d0c7a4527 --- /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 00000000000..37883d4fea4 --- /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 00000000000..e69de29bb2d 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 00000000000..eeced2bca71 --- /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 00000000000..1590876061d --- /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 00000000000..e69de29bb2d diff --git a/tests/qapi-schema/empty.out b/tests/qapi-schema/empty.out index 3feb3f69d3d..d1981f85861 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/features-too-many.err b/tests/qapi-schema/features-too-many.err new file mode 100644 index 00000000000..bbbd6e52026 --- /dev/null +++ b/tests/qapi-schema/features-too-many.err @@ -0,0 +1,2 @@ +features-too-many.json: In command 'go-fish': +features-too-many.json:2: Maximum of 64 schema features is permitted diff --git a/tests/qapi-schema/features-too-many.json b/tests/qapi-schema/features-too-many.json new file mode 100644 index 00000000000..aab0a0b5f1b --- /dev/null +++ b/tests/qapi-schema/features-too-many.json @@ -0,0 +1,13 @@ +# Max 64 features, with 2 specials, so 63rd custom is invalid +{ 'command': 'go-fish', + 'features': [ + 'f00', 'f01', 'f02', 'f03', 'f04', 'f05', 'f06', 'f07', + 'f08', 'f09', 'f0a', 'f0b', 'f0c', 'f0d', 'f0e', 'f0f', + 'f10', 'f11', 'f12', 'f13', 'f14', 'f15', 'f16', 'f17', + 'f18', 'f19', 'f1a', 'f1b', 'f1c', 'f1d', 'f1e', 'f1f', + 'f20', 'f21', 'f22', 'f23', 'f24', 'f25', 'f26', 'f27', + 'f28', 'f29', 'f2a', 'f2b', 'f2c', 'f2d', 'f2e', 'f2f', + 'f30', 'f31', 'f32', 'f33', 'f34', 'f35', 'f36', 'f37', + 'f38', 'f39', 'f3a', 'f3b', 'f3c', 'f3d', 'f3e' + ] +} diff --git a/tests/qapi-schema/features-too-many.out b/tests/qapi-schema/features-too-many.out new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/qapi-schema/include-repetition.out b/tests/qapi-schema/include-repetition.out index 16dbd9b8194..c564d278626 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 48e923bfbc8..ec8200ab18b 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 6a30ded3fa6..a7c22c3eefc 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 af085f745d6..9577178b6ff 100644 --- a/tests/qapi-schema/meson.build +++ b/tests/qapi-schema/meson.build @@ -66,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', @@ -77,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', @@ -102,6 +105,7 @@ schemas = [ 'event-case.json', 'event-member-invalid-dict.json', 'event-nest-struct.json', + 'features-too-many.json', 'features-bad-type.json', 'features-deprecated-type.json', 'features-duplicate-name.json', @@ -273,15 +277,17 @@ if build_docs output: ['doc-good.txt.nocr'], input: qapi_doc_out[0], build_by_default: true, - command: [remove_cr, '@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: [remove_cr, '@INPUT@'], - capture: true) + command: [remove_cr], + capture: true, + feed: true) test('QAPI rST doc', diff, args: ['-u', qapi_doc_ref_nocr[0], qapi_doc_out_nocr[0]], suite: ['qapi-schema', 'qapi-doc']) diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index e2f0981348b..4617eb4e98a 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 diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index d58c31f5393..4be930228cc 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -48,7 +48,7 @@ def visit_array_type(self, name, info, ifcond, element_type): 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 @@ def visit_object_type(self, name, info, ifcond, features, % (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) @@ -95,17 +96,8 @@ def _print_variants(variants): @staticmethod def _print_if(ifcond, indent=4): - # TODO Drop this hack after replacing OrderedDict by plain - # dict (requires Python 3.7) - def _massage(subcond): - if isinstance(subcond, str): - return subcond - if isinstance(subcond, list): - return [_massage(val) for val in subcond] - return {key: _massage(val) for key, val in subcond.items()} - if ifcond.is_present(): - print('%sif %s' % (' ' * indent, _massage(ifcond.ifcond))) + print('%sif %s' % (' ' * indent, ifcond.ifcond)) @classmethod def _print_features(cls, features, indent=4): @@ -130,18 +122,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.kind, 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): @@ -218,9 +209,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/qemu-iotests/024 b/tests/qemu-iotests/024 index 25a564a1505..b29c76e1618 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 973a5a37111..3d1e31927a2 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/030 b/tests/qemu-iotests/030 index 98595d47fec..0e6a39d103c 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 da9133c44bc..4bc7a071bdf 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 5601a4873c0..5c18e413ec9 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,13 +811,12 @@ 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')) @@ -849,13 +827,12 @@ class TestCommitWithFilters(iotests.QMPTestCase): 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')) @@ -870,9 +847,8 @@ class TestCommitWithFilters(iotests.QMPTestCase): # 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')) @@ -904,12 +878,11 @@ class TestCommitWithFilters(iotests.QMPTestCase): 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')) @@ -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 8429958bf0a..8452845f448 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', @@ -1154,10 +1100,8 @@ class TestRepairQuorum(iotests.QMPTestCase): # Check the full error message now self.vm.shutdown() - log = self.vm.get_log() - log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) + log = iotests.filter_qtest(self.vm.get_log()) log = re.sub(r'^Formatting.*\n', '', log) - log = re.sub(r'\n\[I \+\d+\.\d+\] CLOSED\n?$', '', log) log = re.sub(r'^%s: ' % os.path.basename(iotests.qemu_prog), '', log) self.assertEqual(log, @@ -1199,9 +1143,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 +1160,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', {}) - - 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-add', + driver='blkdebug', + node_name='dest-perm', + image='dest', + unshare_child_perms=['consistent-read']) + + 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 +1217,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', {}) - - result = self.vm.qmp('blockdev-add', - node_name='target', driver='null-co') - self.assert_qmp(result, 'return', {}) - - result = self.vm.qmp('blockdev-mirror', job_id='mirror', device='filter0', - target='target', sync='full', replaces='filter1') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', { + 'driver': 'copy-on-read', + 'node-name': 'filter0', + 'file': { + 'driver': 'copy-on-read', + 'node-name': 'filter1', + 'file': { + 'driver': 'null-co' + } + } + }) + + self.vm.cmd('blockdev-add', + node_name='target', driver='null-co') + + self.vm.cmd('blockdev-mirror', job_id='mirror', device='filter0', + target='target', sync='full', replaces='filter1') self.complete_and_wait('mirror') @@ -1318,16 +1255,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 +1290,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', {}) - - result = self.vm.qmp('blockdev-mirror', - job_id='mirror', - device='filter', - target='target', - sync='top') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-add', { + 'node-name': 'filter', + 'driver': 'copy-on-read', + 'file': self.filterless_chain + }) + + self.vm.cmd('blockdev-mirror', + job_id='mirror', + device='filter', + target='target', + sync='top') self.complete_and_wait('mirror') @@ -1383,23 +1317,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', {}) - - result = self.vm.qmp('blockdev-mirror', - job_id='mirror', - device='source', - target='target', - sync='top') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('device_add', + driver='scsi-hd', + id='virtio', + bus='vio-scsi.0', + drive='source') + + 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 +1348,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', {}) - - 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('device_add', + driver='scsi-hd', + id='virtio', + bus='vio-scsi.0', + drive='source') + + 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 45eb239baaf..a341f21cd70 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/051.pc.out b/tests/qemu-iotests/051.pc.out index 4d4af5a486d..f19b532eb9b 100644 --- a/tests/qemu-iotests/051.pc.out +++ b/tests/qemu-iotests/051.pc.out @@ -169,11 +169,11 @@ QEMU_PROG: -device scsi-hd,drive=disk: Device needs media, but drive is empty Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device ide-hd,drive=disk,share-rw=on QEMU X.Y.Z monitor - type 'help' for more information -QEMU_PROG: -device ide-hd,drive=disk,share-rw=on: Cannot change iothread of active block backend +(qemu) QEMU_PROG: -device ide-hd,drive=disk,share-rw=on: Cannot change iothread of active block backend Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-blk-pci,drive=disk,share-rw=on QEMU X.Y.Z monitor - type 'help' for more information -QEMU_PROG: -device virtio-blk-pci,drive=disk,share-rw=on: Cannot change iothread of active block backend +(qemu) QEMU_PROG: -device virtio-blk-pci,drive=disk,share-rw=on: Cannot change iothread of active block backend Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device lsi53c895a,id=lsi0 -device scsi-hd,bus=lsi0.0,drive=disk,share-rw=on QEMU X.Y.Z monitor - type 'help' for more information @@ -181,11 +181,11 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-scsi,id=virtio-scsi1 -device scsi-hd,bus=virtio-scsi1.0,drive=disk,share-rw=on QEMU X.Y.Z monitor - type 'help' for more information -(qemu) QEMU_PROG: -device scsi-hd,bus=virtio-scsi1.0,drive=disk,share-rw=on: Cannot change iothread of active block backend +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-blk-pci,drive=disk,iothread=thread0,share-rw=on QEMU X.Y.Z monitor - type 'help' for more information -QEMU_PROG: -device virtio-blk-pci,drive=disk,iothread=thread0,share-rw=on: Cannot change iothread of active block backend +(qemu) QEMU_PROG: -device virtio-blk-pci,drive=disk,iothread=thread0,share-rw=on: Cannot change iothread of active block backend Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-scsi,id=virtio-scsi1,iothread=thread0 -device scsi-hd,bus=virtio-scsi1.0,drive=disk,share-rw=on QEMU X.Y.Z monitor - type 'help' for more information diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055 index 5d6b607051d..d8372b55988 100755 --- a/tests/qemu-iotests/055 +++ b/tests/qemu-iotests/055 @@ -69,8 +69,7 @@ class TestSingleDrive(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp(cmd, device='drive0', target=target, sync='full') - self.assert_qmp(result, 'return', {}) + self.vm.cmd(cmd, device='drive0', target=target, sync='full') event = self.cancel_and_wait(resume=True) self.assert_qmp(event, 'data/type', 'backup') @@ -85,9 +84,8 @@ class TestSingleDrive(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp(cmd, device='drive0', - target=target, sync='full') - self.assert_qmp(result, 'return', {}) + self.vm.cmd(cmd, device='drive0', + target=target, sync='full') self.pause_job('drive0', wait=False) self.vm.resume_drive('drive0') @@ -100,8 +98,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() @@ -123,10 +120,9 @@ class TestSingleDrive(iotests.QMPTestCase): result = self.vm.qmp('block_resize', node_name=node, size=65536) self.assert_qmp(result, 'error/class', 'GenericError') - result = self.vm.qmp('blockdev-backup', job_id='job0', device='drive0', - target='drive1', sync='full', auto_finalize=False, - auto_dismiss=False) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('blockdev-backup', job_id='job0', device='drive0', + target='drive1', sync='full', auto_finalize=False, + auto_dismiss=False) self.vm.run_job('job0', auto_finalize=False, pre_finalize=pre_finalize) @@ -137,8 +133,7 @@ class TestSingleDrive(iotests.QMPTestCase): self.do_test_resize_blockdev_backup('drive1', 'target') def do_test_target_size(self, size): - result = self.vm.qmp('block_resize', device='drive1', size=size) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block_resize', device='drive1', size=size) result = self.vm.qmp('blockdev-backup', job_id='job0', device='drive0', target='drive1', sync='full') @@ -219,16 +214,14 @@ class TestSetSpeed(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp(cmd, device='drive0', target=target, sync='full') - self.assert_qmp(result, 'return', {}) + self.vm.cmd(cmd, device='drive0', target=target, sync='full') # 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') @@ -240,9 +233,8 @@ class TestSetSpeed(iotests.QMPTestCase): # Check setting speed option works self.vm.pause_drive('drive0') - result = self.vm.qmp(cmd, device='drive0', - target=target, sync='full', speed=4*1024*1024) - self.assert_qmp(result, 'return', {}) + self.vm.cmd(cmd, device='drive0', + target=target, sync='full', speed=4*1024*1024) result = self.vm.qmp('query-block-jobs') self.assert_qmp(result, 'return[0]/device', 'drive0') @@ -267,9 +259,8 @@ class TestSetSpeed(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp(cmd, device='drive0', - target=target, sync='full') - self.assert_qmp(result, 'return', {}) + self.vm.cmd(cmd, device='drive0', + target=target, sync='full') result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) self.assert_qmp(result, 'error/class', 'GenericError') @@ -306,7 +297,7 @@ class TestSingleTransaction(iotests.QMPTestCase): def do_test_cancel(self, cmd, target): self.assert_no_active_block_jobs() - result = self.vm.qmp('transaction', actions=[{ + self.vm.cmd('transaction', actions=[{ 'type': cmd, 'data': { 'device': 'drive0', 'target': target, @@ -315,8 +306,6 @@ class TestSingleTransaction(iotests.QMPTestCase): } ]) - self.assert_qmp(result, 'return', {}) - event = self.cancel_and_wait() self.assert_qmp(event, 'data/type', 'backup') @@ -329,7 +318,7 @@ class TestSingleTransaction(iotests.QMPTestCase): def do_test_pause(self, cmd, target, image): self.assert_no_active_block_jobs() - result = self.vm.qmp('transaction', actions=[{ + self.vm.cmd('transaction', actions=[{ 'type': cmd, 'data': { 'device': 'drive0', 'target': target, @@ -337,12 +326,10 @@ class TestSingleTransaction(iotests.QMPTestCase): 'speed': 64 * 1024 }, } ]) - self.assert_qmp(result, 'return', {}) self.pause_job('drive0', wait=False) - 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.pause_wait('drive0') @@ -353,8 +340,7 @@ class TestSingleTransaction(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() @@ -519,8 +505,7 @@ class TestCompressedToQcow2(iotests.QMPTestCase): self.assert_no_active_block_jobs() - result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args) - self.assert_qmp(result, 'return', {}) + self.vm.cmd(cmd, device='drive0', sync='full', compress=True, **args) self.wait_until_completed() @@ -545,8 +530,7 @@ class TestCompressedToQcow2(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args) - self.assert_qmp(result, 'return', {}) + self.vm.cmd(cmd, device='drive0', sync='full', compress=True, **args) event = self.cancel_and_wait(resume=True) self.assert_qmp(event, 'data/type', 'backup') @@ -568,8 +552,7 @@ class TestCompressedToQcow2(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args) - self.assert_qmp(result, 'return', {}) + self.vm.cmd(cmd, device='drive0', sync='full', compress=True, **args) self.pause_job('drive0', wait=False) self.vm.resume_drive('drive0') @@ -582,8 +565,7 @@ class TestCompressedToQcow2(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() diff --git a/tests/qemu-iotests/056 b/tests/qemu-iotests/056 index bef865eec48..808ea6b48ab 100755 --- a/tests/qemu-iotests/056 +++ b/tests/qemu-iotests/056 @@ -75,9 +75,8 @@ class TestSyncModesNoneAndTop(iotests.QMPTestCase): def test_complete_top(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-backup', device='drive0', sync='top', - format=iotests.imgfmt, target=target_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-backup', device='drive0', sync='top', + format=iotests.imgfmt, target=target_img) self.wait_until_completed(check_offset=False) @@ -89,9 +88,8 @@ class TestSyncModesNoneAndTop(iotests.QMPTestCase): def test_cancel_sync_none(self): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-backup', device='drive0', - sync='none', target=target_img) - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-backup', device='drive0', + sync='none', target=target_img) time.sleep(1) self.vm.hmp_qemu_io('drive0', 'write -P0x5e 0 512') self.vm.hmp_qemu_io('drive0', 'aio_flush') @@ -115,18 +113,15 @@ class TestBeforeWriteNotifier(iotests.QMPTestCase): def test_before_write_notifier(self): self.vm.pause_drive("drive0") - result = self.vm.qmp('drive-backup', device='drive0', - sync='full', target=target_img, - format="file", speed=1) - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('block-job-pause', device="drive0") - self.assert_qmp(result, 'return', {}) + self.vm.cmd('drive-backup', device='drive0', + sync='full', target=target_img, + format="file", speed=1) + self.vm.cmd('block-job-pause', device="drive0") # Speed is low enough that this must be an uncopied range, which will # trigger the before write notifier self.vm.hmp_qemu_io('drive0', 'aio_write -P 1 512512 512') self.vm.resume_drive("drive0") - result = self.vm.qmp('block-job-resume', device="drive0") - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-job-resume', device="drive0") event = self.cancel_and_wait() self.assert_qmp(event, 'data/type', 'backup') @@ -191,8 +186,7 @@ class BackupTest(iotests.QMPTestCase): self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt, sync='full', target=self.ref_img, auto_dismiss=False) - res = self.vm.qmp('block-job-dismiss', id='drive0') - self.assert_qmp(res, 'return', {}) + self.vm.cmd('block-job-dismiss', id='drive0') # Now to the test backup: We simulate the following guest # writes: @@ -211,11 +205,9 @@ class BackupTest(iotests.QMPTestCase): ('66', '1M', '1M')]) # Let the job complete - res = self.vm.qmp('block-job-set-speed', device='drive0', speed=0) - self.assert_qmp(res, 'return', {}) + self.vm.cmd('block-job-set-speed', device='drive0', speed=0) self.qmp_backup_wait('drive0') - res = self.vm.qmp('block-job-dismiss', id='drive0') - self.assert_qmp(res, 'return', {}) + self.vm.cmd('block-job-dismiss', id='drive0') self.assertTrue(iotests.compare_images(self.ref_img, self.dest_img), 'target image does not match reference image') @@ -237,8 +229,7 @@ class BackupTest(iotests.QMPTestCase): auto_dismiss=False) res = self.vm.qmp('query-block-jobs') self.assert_qmp(res, 'return[0]/status', 'concluded') - res = self.vm.qmp('block-job-dismiss', id='drive0') - self.assert_qmp(res, 'return', {}) + self.vm.cmd('block-job-dismiss', id='drive0') res = self.vm.qmp('query-block-jobs') self.assert_qmp(res, 'return', []) @@ -263,8 +254,7 @@ class BackupTest(iotests.QMPTestCase): auto_dismiss=False) self.assertEqual(res, False) # OK, dismiss the zombie. - res = self.vm.qmp('block-job-dismiss', id='drive0') - self.assert_qmp(res, 'return', {}) + self.vm.cmd('block-job-dismiss', id='drive0') res = self.vm.qmp('query-block-jobs') self.assert_qmp(res, 'return', []) # Ensure it's really gone. @@ -281,23 +271,22 @@ class BackupTest(iotests.QMPTestCase): ('0x55', '8M', '352k'), ('0x78', '15872k', '1M'))) # Add destination node via blkdebug - res = self.vm.qmp('blockdev-add', - node_name='target0', - driver=iotests.imgfmt, - file={ - 'driver': 'blkdebug', - 'image': { - 'driver': 'file', - 'filename': self.dest_img - }, - 'inject-error': [{ - 'event': 'write_aio', - 'errno': 5, - 'immediately': False, - 'once': True - }], - }) - self.assert_qmp(res, 'return', {}) + self.vm.cmd('blockdev-add', + node_name='target0', + driver=iotests.imgfmt, + file={ + 'driver': 'blkdebug', + 'image': { + 'driver': 'file', + 'filename': self.dest_img + }, + 'inject-error': [{ + 'event': 'write_aio', + 'errno': 5, + 'immediately': False, + 'once': True + }], + }) res = self.qmp_backup(cmd='blockdev-backup', device='drive0', target='target0', @@ -323,8 +312,7 @@ class BackupTest(iotests.QMPTestCase): res = self.vm.qmp('query-block-jobs') self.assert_qmp(res, 'return[0]/status', 'paused') # OK, unstick job and move forward. - res = self.vm.qmp('block-job-resume', device='drive0') - self.assert_qmp(res, 'return', {}) + self.vm.cmd('block-job-resume', device='drive0') # And now we need to wait for it to conclude; res = self.qmp_backup_wait(device='drive0') self.assertTrue(res) @@ -332,8 +320,7 @@ class BackupTest(iotests.QMPTestCase): # Job should now be languishing: res = self.vm.qmp('query-block-jobs') self.assert_qmp(res, 'return[0]/status', 'concluded') - res = self.vm.qmp('block-job-dismiss', id='drive0') - self.assert_qmp(res, 'return', {}) + self.vm.cmd('block-job-dismiss', id='drive0') res = self.vm.qmp('query-block-jobs') self.assert_qmp(res, 'return', []) diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059 index 2bcb1f7f9ca..0634c7bd928 100755 --- a/tests/qemu-iotests/059 +++ b/tests/qemu-iotests/059 @@ -84,6 +84,8 @@ echo echo "=== Testing big twoGbMaxExtentFlat ===" _make_test_img -o "subformat=twoGbMaxExtentFlat" 1000G _img_info --format-specific | _filter_img_info --format-specific +$QEMU_IO -c "write 990G 512 -P 89" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read 990G 512 -P 89" "$TEST_IMG" | _filter_qemu_io _cleanup_test_img echo diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out index 2b83c0c8b66..275ee7c7781 100644 --- a/tests/qemu-iotests/059.out +++ b/tests/qemu-iotests/059.out @@ -2032,6 +2032,10 @@ Format specific information: virtual size: 2147483648 filename: TEST_DIR/t-f500.IMGFMT format: FLAT +wrote 512/512 bytes at offset 1063004405760 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 1063004405760 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Testing malformed VMFS extent description line === qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Invalid extent line: RW 12582912 VMFS "dummy.IMGFMT" 1 diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out index 329977d9b9b..a37bf446e94 100644 --- a/tests/qemu-iotests/060.out +++ b/tests/qemu-iotests/060.out @@ -421,8 +421,8 @@ QMP_VERSION {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "none0", "msg": "Preventing invalid write on metadata (overlaps with refcount table)", "offset": 65536, "node-name": "drive", "fatal": true, "size": 65536}} write failed: Input/output error {"return": ""} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} === Testing incoming inactive corrupted image === @@ -432,8 +432,8 @@ QMP_VERSION qcow2: Image is corrupt: L2 table offset 0x2a2a2a00 unaligned (L1 index: 0); further non-fatal corruption events will be suppressed {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}} {"return": ""} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} corrupt: false *** done diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 index 53c7d428e38..b71ac097d14 100755 --- a/tests/qemu-iotests/061 +++ b/tests/qemu-iotests/061 @@ -326,12 +326,14 @@ $QEMU_IMG amend -o "data_file=foo" "$TEST_IMG" echo _make_test_img -o "compat=1.1,data_file=$TEST_IMG.data" 64M $QEMU_IMG amend -o "data_file=foo" "$TEST_IMG" -_img_info --format-specific +$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 $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 139fc68177d..24c33add7ce 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/066 b/tests/qemu-iotests/066 index cf63144cb94..336d8565ddf 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.out b/tests/qemu-iotests/071.out index bca0c02f5c0..a2923b05c29 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/081.out b/tests/qemu-iotests/081.out index 615c0835498..aba85ea5644 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 e1c23a69836..97b6d8036dc 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 93274dc8cbd..4f9e224e8a8 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 ae0fc466910..55548439aad 100755 --- a/tests/qemu-iotests/106 +++ b/tests/qemu-iotests/106 @@ -40,6 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt raw _supported_proto file fuse _supported_os Linux +_require_disk_usage # in kB CREATION_SIZE=128 diff --git a/tests/qemu-iotests/108.out b/tests/qemu-iotests/108.out index b5401d788dc..b9c876b394a 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 e207a555f37..0fb580f9a59 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 2611d6a40f9..3ae8552ff73 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,12 +35,10 @@ 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"}} @@ -50,6 +48,7 @@ read 512/512 bytes at offset 0 {"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 === @@ -59,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. @@ -75,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' } @@ -87,12 +86,10 @@ 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"}} @@ -102,6 +99,7 @@ read 512/512 bytes at offset 0 {"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 === @@ -111,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. @@ -127,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' } @@ -139,12 +137,10 @@ 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"}} @@ -154,6 +150,7 @@ read 512/512 bytes at offset 0 {"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 === @@ -163,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. @@ -179,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' } @@ -191,12 +188,10 @@ 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"}} @@ -206,6 +201,7 @@ read 512/512 bytes at offset 0 {"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 === @@ -215,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. @@ -231,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' } @@ -243,12 +239,10 @@ 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"}} @@ -258,6 +252,7 @@ read 512/512 bytes at offset 0 {"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 === @@ -267,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. @@ -283,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' } @@ -295,12 +290,10 @@ 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"}} @@ -310,6 +303,7 @@ read 512/512 bytes at offset 0 {"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 === @@ -318,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. @@ -334,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' } @@ -346,12 +340,10 @@ 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"}} @@ -361,6 +353,7 @@ read 512/512 bytes at offset 0 {"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 === @@ -369,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. @@ -385,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' } @@ -397,12 +390,10 @@ 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"}} @@ -412,6 +403,7 @@ read 512/512 bytes at offset 0 {"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 === @@ -420,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. @@ -436,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' } @@ -448,12 +440,10 @@ 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"}} @@ -463,6 +453,7 @@ read 512/512 bytes at offset 0 {"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 === @@ -471,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. @@ -487,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' } @@ -499,12 +490,10 @@ 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"}} @@ -514,6 +503,7 @@ read 512/512 bytes at offset 0 {"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 === @@ -522,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. @@ -530,12 +520,10 @@ 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"}} @@ -545,6 +533,7 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"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": {}} @@ -554,12 +543,10 @@ 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"}} @@ -569,5 +556,6 @@ Images are identical. {"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/114 b/tests/qemu-iotests/114 index de6fd327eea..dccc71008b5 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 735ffd25c65..1cea9e02173 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 cae52ffa5ea..6a4210c2199 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', {}) - - result = self.vm.qmp('blockdev-open-tray', - id=self.device_name, force=True) - 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'}) + + 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 0744c1f1360..35d84a5bc5a 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 e18766e1671..6a1aa3fe2b9 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 845ab5303c5..b2f4328e345 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/125 b/tests/qemu-iotests/125 index 46279d6b382..708e7c5ba21 100755 --- a/tests/qemu-iotests/125 +++ b/tests/qemu-iotests/125 @@ -35,7 +35,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 get_image_size_on_host() { - echo $(($(stat -c '%b * %B' "$TEST_IMG_FILE"))) + disk_usage "$TEST_IMG_FILE" } # get standard environment and filters diff --git a/tests/qemu-iotests/127.out b/tests/qemu-iotests/127.out index 1685c4850a5..dd8c4a8aa94 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 c75ec62ecf1..97773cd96da 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 7257f096774..7af85d20a89 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 304bbb3f61e..3119100e78a 100755 --- a/tests/qemu-iotests/131 +++ b/tests/qemu-iotests/131 @@ -74,6 +74,58 @@ poke_file "$TEST_IMG" "$inuse_offset" "\x59\x6e\x6f\x74" echo "== read corrupted image with repairing ==" { $QEMU_IO -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +echo "== check discard ==" + +# Clear image +_make_test_img $size + +{ $QEMU_IO -c "write -P 0x11 0 $CLUSTER_DBL_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG map "$TEST_IMG"; } 2>&1 | _filter_qemu_img_map +{ $QEMU_IO -c "discard 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG map "$TEST_IMG"; } 2>&1 | _filter_qemu_img_map +{ $QEMU_IO -c "read -P 0 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check simple allocation over the discarded hole ==" + +{ $QEMU_IO -c "write -P 0x11 $CLUSTER_DBL_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG map "$TEST_IMG"; } 2>&1 | _filter_qemu_img_map +{ $QEMU_IO -c "read -P 0x11 $CLUSTER_DBL_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check more complex allocation over the discard hole ==" + +# Clear image +_make_test_img $size + +{ $QEMU_IO -c "write -P 0x11 $CLUSTER_DBL_SIZE $CLUSTER_DBL_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "discard $CLUSTER_DBL_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +# There is 1 cluster hole. Fill it fully and allocate 1 cluster at the end +{ $QEMU_IO -c "write -P 0x12 $CLUSTER_HALF_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG map "$TEST_IMG"; } 2>&1 | _filter_qemu_img_map +{ $QEMU_IO -c "read -P 0x12 $CLUSTER_HALF_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "read -P 0 0 $CLUSTER_HALF_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "read -P 0 $((CLUSTER_SIZE + CLUSTER_HALF_SIZE)) $CLUSTER_HALF_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check write-zeroes ==" + +# Clear image +_make_test_img $size + +{ $QEMU_IO -c "write -P 0x11 0 $CLUSTER_DBL_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "write -z 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG map "$TEST_IMG"; } 2>&1 | _filter_qemu_img_map +{ $QEMU_IO -c "read -P 0 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== check cluster-partial write-zeroes ==" + +# Clear image +_make_test_img $size + +{ $QEMU_IO -c "write -P 0x11 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "write -z 0 $CLUSTER_HALF_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "read -P 0 0 $CLUSTER_HALF_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "read -P 0x11 $CLUSTER_HALF_SIZE $CLUSTER_HALF_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + echo "== allocate with backing ==" # Verify that allocating clusters works fine even when there is a backing image. # Regression test for a bug where we would pass a buffer read from the backing diff --git a/tests/qemu-iotests/131.out b/tests/qemu-iotests/131.out index d2904578dfd..86a2d2a49bf 100644 --- a/tests/qemu-iotests/131.out +++ b/tests/qemu-iotests/131.out @@ -26,6 +26,66 @@ read 524288/524288 bytes at offset 0 Repairing image was not closed correctly 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 diff --git a/tests/qemu-iotests/132 b/tests/qemu-iotests/132 index 367ea080365..12a64b3d95a 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 ded153c0b9f..b2c3c03f08b 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 178b1ee230c..ebb4cd62b6f 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 312f76d5daf..32866440aed 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 a37030ee170..a7d3985a02e 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 - -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 - - -test_blockjob() -{ - _send_qemu_cmd $QEMU_HANDLE \ - "{'execute': 'blockdev-add', - 'arguments': { - 'node-name': 'drv0', - 'driver': '$IMGFMT', - 'file': { - 'driver': 'file', - 'filename': '$TEST_IMG' - }}}" \ - 'return' - - # 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 - - # 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 - - _send_qemu_cmd $QEMU_HANDLE \ - "{'execute': 'block-job-cancel', - 'arguments': {'device': 'job0'}}" \ - "$3" - - _send_qemu_cmd $QEMU_HANDLE \ - "{'execute': 'blockdev-del', - 'arguments': {'node-name': 'drv0'}}" \ - 'return' -} - - -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 - -_launch_qemu -nodefaults - -_send_qemu_cmd $QEMU_HANDLE \ - "{'execute': 'qmp_capabilities'}" \ - 'return' - -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. - -test_blockjob \ - "{'execute': 'drive-backup', - 'arguments': {'job-id': 'job0', - 'device': 'drv0', - 'target': '$TEST_DIR/o.$IMGFMT', - 'format': '$IMGFMT', - 'sync': 'none'}}" \ - 'return' \ - '"status": "null"' - -echo -echo '=== Testing drive-mirror ===' -echo - -# drive-mirror will send BLOCK_JOB_READY basically immediately, and cancelling -# the job will consequently result in BLOCK_JOB_COMPLETED being emitted. - -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 +import iotests + +# Common filters to mask values that vary in the test output +QMP_FILTERS = [iotests.filter_qmp_testfiles, \ + iotests.filter_qmp_imgfmt] + + +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 + + 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) + + 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]) + + 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]) + + # This time it succeeds + self.vm.qmp_log('blockdev-del', node_name='drv0') + + # Separate test cases in output + iotests.log('') + + +def main() -> None: + with iotests.FilePath('bottom', 'middle', 'top', 'target') as \ + (bottom_path, middle_path, top_path, target_path), \ + iotests.VM() as vm: + + 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) + + iotests.log('Starting VM...') + vm.add_args('-nodefaults') + vm.launch() + + # 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-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]) + + # 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]) + + # 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]) + + # 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) + +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 63203d9944b..91b7ba50af7 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' - }}} -{"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": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}} +{"return": {}} +{"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' - }}} -{"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": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}} +{"return": {}} +{"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' - }}} -{"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": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}} +{"return": {}} +{"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' - }}} -{"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": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}} +{"return": {}} +{"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' - }}} -{"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": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}} +{"return": {}} +{"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 9ec5888e0ee..d6afa32abc5 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 bdcc498fa24..d284a0e442d 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 b3b48120150..2245ddfa106 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 dfd6c771407..a48804154e2 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 47dfa62e6b4..6d6f077a14d 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 2ae318f16f0..c13343d7ef9 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 2cc5b82f7cc..72ca8471593 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 b4d1bc2553f..f2ff9c5dac2 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 4e179c340ff..197bea9e778 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 1fa7ffc4757..0199269add2 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 eadda526154..38eacb41272 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 a9540bd80d2..97c2d86ce57 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 4a22f0c41af..07e5e83f5d4 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 0dc9ba68d20..aa2ebbfb4b4 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 a95878e4cee..3a9ad7eed03 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 e3ef28e2ee8..b3b1709d71f 100755 --- a/tests/qemu-iotests/165 +++ b/tests/qemu-iotests/165 @@ -82,9 +82,7 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase): self.vm.shutdown() #catch 'Persistent bitmaps are lost' possible error - log = self.vm.get_log() - log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) - log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) + log = iotests.filter_qtest(self.vm.get_log()) if log: print(log) @@ -116,9 +114,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 +134,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 +143,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.out b/tests/qemu-iotests/172.out index 07eebf35836..146fc723883 100644 --- a/tests/qemu-iotests/172.out +++ b/tests/qemu-iotests/172.out @@ -68,9 +68,6 @@ floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -125,9 +122,6 @@ ide1-cd0: [not inserted] floppy0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -183,9 +177,6 @@ floppy1 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -265,9 +256,6 @@ floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -322,9 +310,6 @@ ide1-cd0: [not inserted] floppy0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -380,9 +365,6 @@ floppy1 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -422,9 +404,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -461,9 +440,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -519,9 +495,6 @@ none1 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -586,9 +559,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -644,9 +614,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -702,9 +669,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -760,9 +724,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -827,9 +788,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -885,9 +843,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -930,9 +885,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -1106,9 +1058,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -1145,9 +1094,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -1187,9 +1133,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -1226,9 +1169,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit diff --git a/tests/qemu-iotests/175 b/tests/qemu-iotests/175 index f74f053b719..bbbf550a5af 100755 --- a/tests/qemu-iotests/175 +++ b/tests/qemu-iotests/175 @@ -77,6 +77,7 @@ _supported_os Linux _default_cache_mode none _supported_cache_modes none directsync +_require_disk_usage size=$((1 * 1024 * 1024)) diff --git a/tests/qemu-iotests/176.out b/tests/qemu-iotests/176.out index 9d09b604520..9c73ef2eea7 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/179.out b/tests/qemu-iotests/179.out index 7cf22cd75fd..65b909ebc2b 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/182.out b/tests/qemu-iotests/182.out index 57f7265458f..83fc1a4797a 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 ee62939e72f..00000000000 --- 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 fd9c2e52a58..00000000000 --- 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 77e5489d653..52692b6b3b9 100644 --- a/tests/qemu-iotests/184.out +++ b/tests/qemu-iotests/184.out @@ -26,6 +26,7 @@ Testing: { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "virtual-size": 1073741824, @@ -59,6 +60,7 @@ Testing: { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 1073741824, "filename": "null-co://", @@ -89,10 +91,6 @@ Testing: "return": [ ] } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -104,6 +102,10 @@ Testing: "reason": "host-qmp-quit" } } +{ + "return": { + } +} == property changes in ThrottleGroup == @@ -169,10 +171,6 @@ Testing: "iops-total-max": 0 } } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -184,6 +182,10 @@ Testing: "reason": "host-qmp-quit" } } +{ + "return": { + } +} == object creation/set errors == @@ -211,10 +213,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 +224,10 @@ Testing: "reason": "host-qmp-quit" } } +{ + "return": { + } +} == don't specify group == @@ -247,10 +249,6 @@ Testing: "desc": "Parameter 'throttle-group' is missing" } } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -262,6 +260,10 @@ Testing: "reason": "host-qmp-quit" } } +{ + "return": { + } +} *** done diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 index 2ae0a85bbf3..17489fb91c1 100755 --- a/tests/qemu-iotests/185 +++ b/tests/qemu-iotests/185 @@ -344,14 +344,14 @@ 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 diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out index 7292c26bae0..6af0953c4d6 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 === diff --git a/tests/qemu-iotests/188 b/tests/qemu-iotests/188 index ce087d18739..2950b1dc31f 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 801494c6b97..008f73b07d0 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 ea887773743..2a72ca7106e 100644 --- a/tests/qemu-iotests/191.out +++ b/tests/qemu-iotests/191.out @@ -114,6 +114,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "virtual-size": 67108864, @@ -155,6 +156,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT.ovl2", @@ -183,6 +185,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "virtual-size": 67108864, @@ -224,6 +227,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT", @@ -252,6 +256,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "virtual-size": 67108864, @@ -293,6 +298,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 393216, "filename": "TEST_DIR/t.IMGFMT.mid", @@ -321,6 +327,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 67108864, "filename": "TEST_DIR/t.IMGFMT.base", @@ -350,6 +357,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 393216, "filename": "TEST_DIR/t.IMGFMT.base", @@ -378,10 +386,6 @@ wrote 65536/65536 bytes at offset 1048576 ] } { 'execute': 'quit' } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -393,6 +397,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) @@ -521,6 +529,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "virtual-size": 67108864, @@ -562,6 +571,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT.ovl2", @@ -590,6 +600,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "backing-image": { @@ -642,6 +653,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT.ovl3", @@ -670,6 +682,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 67108864, "filename": "TEST_DIR/t.IMGFMT.base", @@ -699,6 +712,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 393216, "filename": "TEST_DIR/t.IMGFMT.base", @@ -727,6 +741,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "virtual-size": 67108864, @@ -768,6 +783,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT", @@ -796,10 +812,6 @@ wrote 65536/65536 bytes at offset 1048576 ] } { 'execute': 'quit' } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -811,6 +823,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 c0ce82dd257..e114c0b2695 100755 --- a/tests/qemu-iotests/194 +++ b/tests/qemu-iotests/194 @@ -34,6 +34,7 @@ with iotests.FilePath('source.img') as source_img_path, \ img_size = '1G' iotests.qemu_img_create('-f', iotests.imgfmt, source_img_path, img_size) + iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write 512M 1M', source_img_path) iotests.qemu_img_create('-f', iotests.imgfmt, dest_img_path, img_size) iotests.log('Launching VMs...') @@ -61,7 +62,8 @@ with iotests.FilePath('source.img') as source_img_path, \ iotests.log('Waiting for `drive-mirror` to complete...') iotests.log(source_vm.event_wait('BLOCK_JOB_READY'), - filters=[iotests.filter_qmp_event]) + filters=[iotests.filter_qmp_event, + iotests.filter_block_job]) iotests.log('Starting migration...') capabilities = [{'capability': 'events', 'state': True}, @@ -87,7 +89,8 @@ with iotests.FilePath('source.img') as source_img_path, \ while True: event2 = source_vm.event_wait('BLOCK_JOB_COMPLETED') - iotests.log(event2, filters=[iotests.filter_qmp_event]) + iotests.log(event2, filters=[iotests.filter_qmp_event, + iotests.filter_block_job]) if event2['event'] == 'BLOCK_JOB_COMPLETED': iotests.log('Stopping the NBD server on destination...') iotests.log(dest_vm.qmp('nbd-server-stop')) diff --git a/tests/qemu-iotests/194.out b/tests/qemu-iotests/194.out index 376ed1d2e63..d02655a5147 100644 --- a/tests/qemu-iotests/194.out +++ b/tests/qemu-iotests/194.out @@ -7,17 +7,18 @@ Launching NBD server on destination... Starting `drive-mirror` on source... {"return": {}} Waiting for `drive-mirror` to complete... -{"data": {"device": "mirror-job0", "len": 1073741824, "offset": 1073741824, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"device": "mirror-job0", "len": "LEN", "offset": "OFFSET", "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Starting migration... {"return": {}} {"execute": "migrate-start-postcopy", "arguments": {}} {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "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": {}} -{"data": {"device": "mirror-job0", "len": 1073741824, "offset": 1073741824, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"device": "mirror-job0", "len": "LEN", "offset": "OFFSET", "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Stopping the NBD server on destination... {"return": {}} Wait for migration completion on target... diff --git a/tests/qemu-iotests/195.out b/tests/qemu-iotests/195.out index ec84df5012a..91717d302e1 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 76509a5ad1c..e5105b13543 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/198 b/tests/qemu-iotests/198 index 1c93dea1f73..6ddeffddd2c 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 805494916f1..62fb73fa3ea 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 b784dcd791a..13304242e5c 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 ab80fd0e44a..1ba878522b0 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/203.out b/tests/qemu-iotests/203.out index 9d4abba8c5d..8e58705e515 100644 --- a/tests/qemu-iotests/203.out +++ b/tests/qemu-iotests/203.out @@ -8,4 +8,5 @@ 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": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} diff --git a/tests/qemu-iotests/205 b/tests/qemu-iotests/205 index 15f798288a4..2370e1a138b 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 7e956947772..979f00f9bf2 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 515906ac7a1..79c4103a226 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 96d9f749dda..94b29b2120a 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 f02c75409ca..ff9f9a6913a 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/218 b/tests/qemu-iotests/218 index 6320c4cb56e..81aa68806f5 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'] == {} - - ret = vm.qmp('blockdev-add', - node_name='target', - driver='null-co', - size=1048576) - assert ret['return'] == {} + vm.cmd('blockdev-add', + node_name='source', + driver='null-co', + size=1048576) + + 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'] == {} - - ret = vm.qmp('blockdev-add', - node_name='source', - driver=iotests.imgfmt, - file={ - 'driver': 'file', - 'filename': src_img_path - }) - assert ret['return'] == {} - - ret = vm.qmp('blockdev-add', - node_name='throttled-source', - driver='throttle', - throttle_group='tg', - file='source') - assert ret['return'] == {} - - ret = vm.qmp('blockdev-add', - node_name='target', - driver='null-co', - size=(64 * 1048576)) - assert ret['return'] == {} - - ret = vm.qmp('blockdev-mirror', - job_id='mirror', - device='throttled-source', - target='target', - sync='full') - assert ret['return'] == {} + vm.cmd('object-add', qom_type='throttle-group', id='tg', + limits={'bps-read': 4096}) + + vm.cmd('blockdev-add', + node_name='source', + driver=iotests.imgfmt, + file={ + 'driver': 'file', + 'filename': src_img_path + }) + + vm.cmd('blockdev-add', + node_name='throttled-source', + driver='throttle', + throttle_group='tg', + file='source') + + vm.cmd('blockdev-add', + node_name='target', + driver='null-co', + size=(64 * 1048576)) + + 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 b/tests/qemu-iotests/221 index c463fd4b113..eba00b80adb 100755 --- a/tests/qemu-iotests/221 +++ b/tests/qemu-iotests/221 @@ -41,6 +41,7 @@ _supported_os Linux _default_cache_mode writeback _supported_cache_modes writeback writethrough unsafe +_require_disk_usage echo echo "=== Check mapping of unaligned raw image ===" diff --git a/tests/qemu-iotests/221.out b/tests/qemu-iotests/221.out index 9cdd171a2db..df231c4e3d5 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 26fb347c5da..5f5b42e2dc2 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"}} +{"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": "n"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}} {"return": {}} {"execute":"nbd-server-remove", "arguments":{"name":"n2"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "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"}} +{"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": "n"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}} {"return": {}} {"execute":"nbd-server-remove", "arguments":{"name":"n2"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "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 7e45a47ac61..eddaad679e2 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 a947b1a87df..d6a1d4ecb65 100644 --- a/tests/qemu-iotests/227.out +++ b/tests/qemu-iotests/227.out @@ -54,10 +54,6 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio } ] } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -69,6 +65,10 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio "reason": "host-qmp-quit" } } +{ + "return": { + } +} === blockstats with -drive if=none === @@ -124,10 +124,6 @@ Testing: -drive driver=null-co,if=none } ] } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -139,6 +135,10 @@ Testing: -drive driver=null-co,if=none "reason": "host-qmp-quit" } } +{ + "return": { + } +} === blockstats with -blockdev === @@ -155,10 +155,6 @@ Testing: -blockdev driver=null-co,node-name=null "return": [ ] } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -170,6 +166,10 @@ Testing: -blockdev driver=null-co,node-name=null "reason": "host-qmp-quit" } } +{ + "return": { + } +} === blockstats with -blockdev and -device === @@ -226,10 +226,6 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b } ] } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -241,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 237c82767ea..d498d55e0e7 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 692976d1c6a..be3e138b585 100644 --- a/tests/qemu-iotests/234.out +++ b/tests/qemu-iotests/234.out @@ -10,13 +10,14 @@ Starting migration to B... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} 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... @@ -27,10 +28,11 @@ Starting migration back to A... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} 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/240 b/tests/qemu-iotests/240 index 9b281e1dc03..f8af9ff648e 100755 --- a/tests/qemu-iotests/240 +++ b/tests/qemu-iotests/240 @@ -81,8 +81,6 @@ class TestCase(iotests.QMPTestCase): self.vm.qmp_log('device_del', id='scsi-hd0') self.vm.event_wait('DEVICE_DELETED') - self.vm.qmp_log('device_add', id='scsi-hd1', driver='scsi-hd', drive='hd0', bus="scsi1.0") - self.vm.qmp_log('device_del', id='scsi-hd1') self.vm.event_wait('DEVICE_DELETED') self.vm.qmp_log('blockdev-del', node_name='hd0') diff --git a/tests/qemu-iotests/240.out b/tests/qemu-iotests/240.out index 89ed25e506c..10dcc42e06d 100644 --- a/tests/qemu-iotests/240.out +++ b/tests/qemu-iotests/240.out @@ -46,10 +46,8 @@ {"execute": "device_add", "arguments": {"bus": "scsi0.0", "drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd0"}} {"return": {}} {"execute": "device_add", "arguments": {"bus": "scsi1.0", "drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd1"}} -{"error": {"class": "GenericError", "desc": "Cannot change iothread of active block backend"}} -{"execute": "device_del", "arguments": {"id": "scsi-hd0"}} {"return": {}} -{"execute": "device_add", "arguments": {"bus": "scsi1.0", "drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd1"}} +{"execute": "device_del", "arguments": {"id": "scsi-hd0"}} {"return": {}} {"execute": "device_del", "arguments": {"id": "scsi-hd1"}} {"return": {}} diff --git a/tests/qemu-iotests/241.out b/tests/qemu-iotests/241.out index 88e8cfcd7e2..7267cd2997e 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 3e61fa25bb6..bb9cc6512f3 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 4815a489b09..f46cfe93f11 100644 --- a/tests/qemu-iotests/244.out +++ b/tests/qemu-iotests/244.out @@ -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 92b28c79beb..f96610f5109 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'}) @@ -614,16 +592,14 @@ class TestBlockdevReopen(iotests.QMPTestCase): @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) @@ -659,12 +635,10 @@ class TestBlockdevReopen(iotests.QMPTestCase): 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") @@ -712,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'" + @@ -732,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 ###### @@ -748,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) @@ -762,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'") @@ -780,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'] @@ -803,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) @@ -851,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 @@ -868,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) @@ -897,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 @@ -911,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'") @@ -923,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 @@ -937,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) @@ -961,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 @@ -982,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): @@ -991,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 @@ -1019,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) @@ -1029,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 @@ -1054,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. @@ -1090,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 @@ -1110,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') @@ -1121,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 @@ -1147,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) @@ -1205,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/247.out b/tests/qemu-iotests/247.out index e909e839949..7d252e7fe4d 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/250 b/tests/qemu-iotests/250 index af48f83abac..c0a0dbc0ff1 100755 --- a/tests/qemu-iotests/250 +++ b/tests/qemu-iotests/250 @@ -52,11 +52,6 @@ _unsupported_imgopts data_file # bdrv_co_truncate(bs->file) call in qcow2_co_truncate(), which might succeed # anyway. -disk_usage() -{ - du --block-size=1 $1 | awk '{print $1}' -} - size=2100M _make_test_img -o "cluster_size=1M,preallocation=metadata" $size diff --git a/tests/qemu-iotests/252.out b/tests/qemu-iotests/252.out index c578129c25b..b1aa94cb053 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 b/tests/qemu-iotests/253 index 35039d20a89..6da85e6a113 100755 --- a/tests/qemu-iotests/253 +++ b/tests/qemu-iotests/253 @@ -41,6 +41,7 @@ _supported_os Linux _default_cache_mode none _supported_cache_modes none directsync +_require_disk_usage echo echo "=== Check mapping of unaligned raw image ===" diff --git a/tests/qemu-iotests/253.out b/tests/qemu-iotests/253.out index b3dca75a899..b458085adb8 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 d7e67f4a052..f34af6cef7a 100755 --- a/tests/qemu-iotests/256 +++ b/tests/qemu-iotests/256 @@ -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 e7e7a2317e3..7d3720b8e59 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.command('blockdev-create', job_id='bdc-fmt-job', - options={ - 'driver': fmt, - 'file': file_node_name, - 'size': size, - })) + log(vm.cmd('blockdev-add', driver='file', + node_name=file_node_name, filename=self.path)) + + 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 aa76131ca91..c33dd7f3a90 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 b73da565da4..22b969d3108 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.out b/tests/qemu-iotests/262.out index 8e04c496c40..bd7706b84b1 100644 --- a/tests/qemu-iotests/262.out +++ b/tests/qemu-iotests/262.out @@ -8,10 +8,11 @@ Starting migration to B... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} 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 ec09b41405a..44fdada0d6f 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 289381e315f..c6ba2754e27 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 7176e376e1f..f6f5d8715ad 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 74352342db5..c37b674aa2e 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 c7c2cadda03..59a6fafa2fa 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 5be780de768..0b24d501595 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 6a74a8138ba..c19753c685a 100644 --- a/tests/qemu-iotests/273.out +++ b/tests/qemu-iotests/273.out @@ -23,6 +23,7 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "backing-image": { @@ -74,6 +75,7 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT", @@ -102,6 +104,7 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "virtual-size": 197120, @@ -142,6 +145,7 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT.mid", @@ -170,6 +174,7 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT.base", @@ -282,10 +287,6 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev ] } } -{ - "return": { - } -} { "timestamp": { "seconds": TIMESTAMP, @@ -297,5 +298,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 acd8b166a65..c2967335ca0 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 24833e7eb68..4224202ac2c 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 c75f437c00e..37411144caf 100644 --- a/tests/qemu-iotests/280.out +++ b/tests/qemu-iotests/280.out @@ -7,12 +7,13 @@ Enabling migration QMP events on VM... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} VM is now stopped: completed {"execute": "query-status", "arguments": {}} -{"return": {"running": false, "singlestep": false, "status": "postmigrate"}} +{"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 5e1339bd756..f6746a12e88 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 5a82639e7f8..722267486d0 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 120a8375b76..38216c2a0e2 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 39ff07e12cd..bb04748e080 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 270ad3999f2..04818af2649 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 0d21b740a72..2b63cefff0e 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 9e75ac69755..09c9290711a 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) diff --git a/tests/qemu-iotests/300 b/tests/qemu-iotests/300 index dbd28384ec3..e46616d7b19 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 b/tests/qemu-iotests/302 index a6d79e727b5..e980ec513f2 100755 --- a/tests/qemu-iotests/302 +++ b/tests/qemu-iotests/302 @@ -115,13 +115,22 @@ with tarfile.open(tar_file, "w") as tar: disk = tarfile.TarInfo("disk") disk.size = actual_size - tar.addfile(disk) - # 6. Shrink the tar to the actual size, aligned to 512 bytes. + # Since python 3.13 we cannot use addfile() to create the member header. + # Add the tarinfo directly using public but undocumented attributes. - tar_size = offset + (disk.size + 511) & ~511 - tar.fileobj.seek(tar_size) - tar.fileobj.truncate(tar_size) + buf = disk.tobuf(tar.format, tar.encoding, tar.errors) + tar.fileobj.write(buf) + tar.members.append(disk) + + # Update the offset and position to the location of the next member. + + tar.offset = offset + (disk.size + 511) & ~511 + tar.fileobj.seek(tar.offset) + + # 6. Shrink the tar to the actual size. + + tar.fileobj.truncate(tar.offset) with tarfile.open(tar_file) as tar: members = [{"name": m.name, "size": m.size, "offset": m.offset_data} diff --git a/tests/qemu-iotests/307.out b/tests/qemu-iotests/307.out index 390f05d1b78..f645f3315f8 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 de12b2b1b93..6eced3aefb9 100755 --- a/tests/qemu-iotests/308 +++ b/tests/qemu-iotests/308 @@ -51,6 +51,7 @@ _unsupported_fmt vpc _supported_proto file # We create the FUSE export manually _supported_os Linux # We need /dev/urandom +_require_disk_usage # $1: Export ID # $2: Options (beyond the node-name and ID) @@ -77,6 +78,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 +86,7 @@ fuse_export_del() } }" \ 'return' - _send_qemu_cmd $QEMU_HANDLE \ - '' \ + _wait_event $QEMU_HANDLE \ 'BLOCK_EXPORT_DELETED' } @@ -290,7 +291,7 @@ echo '--- Try growing non-growable export ---' # Get the current size so we can write beyond the EOF orig_len=$(get_proto_len "$EXT_MP" "$TEST_IMG") -orig_disk_usage=$(stat -c '%b' "$TEST_IMG") +orig_disk_usage=$(disk_usage "$TEST_IMG") # Should fail (exports are non-growable by default) # (Note that qemu-io can never write beyond the EOF, so we have to use @@ -312,7 +313,7 @@ else echo 'OK: Post-truncate image size is as expected' fi -new_disk_usage=$(stat -c '%b' "$TEST_IMG") +new_disk_usage=$(disk_usage "$TEST_IMG") if [ "$new_disk_usage" -gt "$orig_disk_usage" ]; then echo 'OK: Disk usage grew with fallocate' else diff --git a/tests/qemu-iotests/308.out b/tests/qemu-iotests/308.out index d5767133b1b..e5e233691d6 100644 --- a/tests/qemu-iotests/308.out +++ b/tests/qemu-iotests/308.out @@ -165,9 +165,9 @@ 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. @@ -201,9 +201,9 @@ wrote 67108864/67108864 bytes at offset 0 read 67108864/67108864 bytes at offset 0 64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {'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"}} +{"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/314 b/tests/qemu-iotests/314 new file mode 100755 index 00000000000..96d7b4d2581 --- /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 00000000000..ac9337a5435 --- /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 f2e9d27dcf5..545f9ec7bdd 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -84,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. ' @@ -184,7 +184,8 @@ if __name__ == '__main__': sys.exit(str(e)) if args.dry_run: - print('\n'.join([os.path.basename(t) for t in 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.rc b/tests/qemu-iotests/common.rc index d145f08201c..e977cb4eb61 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -140,6 +140,12 @@ _optstr_add() fi } +# report real disk usage for sparse files +disk_usage() +{ + du --block-size=1 "$1" | awk '{print $1}' +} + # Set the variables to the empty string to turn Valgrind off # for specific processes, e.g. # $ VALGRIND_QEMU_IO= ./check -qcow2 -valgrind 015 @@ -979,10 +985,45 @@ _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 whether disk_usage can be reliably used. +_require_disk_usage() +{ + local unusable=false + # ZFS triggers known failures on this front; it does not immediately + # allocate files, and then aggressively compresses writes even when full + # allocation was requested. + if [ -z "$TEST_IMG_FILE" ]; then + FILENAME="$TEST_IMG" + else + FILENAME="$TEST_IMG_FILE" + fi + if [ -e "FILENAME" ]; then + echo "unwilling to overwrite existing file" + exit 1 + fi + $QEMU_IMG create -f raw "$FILENAME" 5M > /dev/null + if [ $(disk_usage "$FILENAME") -gt $((1024*1024)) ]; then + unusable=true + fi + $QEMU_IMG create -f raw -o preallocation=full "$FILENAME" 5M > /dev/null + if [ $(disk_usage "$FILENAME") -lt $((4*1024*1024)) ]; then + unusable=true + fi + rm -f "$FILENAME" + if $unusable; then + _notrun "file system on $TEST_DIR does not handle sparse files nicely" + fi } # 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 00000000000..7d2d0524133 --- /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 ef66fbd62b0..05274772ce4 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -38,7 +38,7 @@ 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 @@ -460,12 +460,17 @@ def __init__(self, *args: str, instance_id: str = 'a', qmp: bool = False): 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) self._p.wait() @@ -596,13 +601,23 @@ def filter_chown(msg): return chown_re.sub("chown UID:GID", msg) def filter_qmp_event(event): - '''Filter a QMP event dict''' + '''Filter the timestamp of a QMP event dict''' event = dict(event) if 'timestamp' in event: event['timestamp']['seconds'] = 'SECS' event['timestamp']['microseconds'] = 'USECS' return event +def filter_block_job(event): + '''Filter the offset and length of a QMP block job event dict''' + event = dict(event) + if 'data' in event: + if 'offset' in event['data']: + event['data']['offset'] = 'OFFSET' + if 'len' in event['data']: + event['data']['len'] = 'LEN' + return event + def filter_qmp(qmsg, filter_fn): '''Given a string filter, filter a QMP object's values. filter_fn takes a (key, value) pair.''' @@ -646,6 +661,13 @@ def _filter(_key, value): def filter_generated_node_ids(msg): return re.sub("#block[0-9]+", "NODE_NAME", msg) +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 = [] @@ -689,6 +711,10 @@ def _filter(_key, value): def filter_nbd_exports(output: str) -> str: return re.sub(r'((min|opt|max) block): [0-9]+', r'\1: XXX', output) +def filter_qtest(output: str) -> str: + output = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', output) + output = re.sub(r'\n?\[I \+\d+\.\d+\] CLOSED\n?$', '', output) + return output Msg = TypeVar('Msg', Dict[str, Any], List[Any], str) @@ -823,7 +849,7 @@ def __init__(self, path_suffix=''): 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: @@ -897,6 +923,10 @@ def add_incoming(self, addr): self._args.append(addr) return self + def add_paused(self): + self._args.append('-S') + return self + def hmp(self, command_line: str, use_log: bool = False) -> QMPMessage: cmd = 'human-monitor-command' kwargs: Dict[str, Any] = {'command-line': command_line} @@ -1247,8 +1277,7 @@ def assert_json_filename_equal(self, json_filename, reference): 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) @@ -1310,8 +1339,7 @@ def complete_and_wait(self, drive='drive0', wait_ready=True, 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']) @@ -1330,11 +1358,9 @@ def pause_wait(self, job_id='job0'): 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''' @@ -1606,10 +1632,13 @@ def write(self, arg=None): 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 65c4c4e8272..9fb3fd14497 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 44761e1e4d3..fad340ad595 100644 --- a/tests/qemu-iotests/meson.build +++ b/tests/qemu-iotests/meson.build @@ -1,4 +1,4 @@ -if not have_tools or targetos == 'windows' or get_option('gprof') +if not have_tools or host_os == 'windows' subdir_done() endif diff --git a/tests/qemu-iotests/pylintrc b/tests/qemu-iotests/pylintrc index de2e0c2781e..c5f4833e458 100644 --- a/tests/qemu-iotests/pylintrc +++ b/tests/qemu-iotests/pylintrc @@ -13,6 +13,7 @@ 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, @@ -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 9a37ad91529..6326e46b7b1 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py @@ -25,7 +25,12 @@ 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 @@ def init_directories(self) -> None: 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', @@ -216,7 +221,7 @@ def __init__(self, source_dir: str, build_dir: str, 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() @@ -235,9 +240,12 @@ def __init__(self, source_dir: str, build_dir: str, ('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 @@ def __init__(self, source_dir: str, build_dir: str, 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 7b322272e92..2e236c8fa39 100644 --- a/tests/qemu-iotests/testrunner.py +++ b/tests/qemu-iotests/testrunner.py @@ -27,11 +27,14 @@ import shutil import sys from multiprocessing import Pool -from typing import List, Optional, 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: diff --git a/tests/qemu-iotests/tests/backing-file-invalidation b/tests/qemu-iotests/tests/backing-file-invalidation index 4eccc801532..b0e19839db9 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 00000000000..17fef9c6d34 --- /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 00000000000..fbc63e62f88 --- /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/commit-zero-blocks b/tests/qemu-iotests/tests/commit-zero-blocks new file mode 100755 index 00000000000..de00273e727 --- /dev/null +++ b/tests/qemu-iotests/tests/commit-zero-blocks @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# group: rw quick +# +# Test for commit of discarded blocks +# +# This tests committing a live snapshot where some of the blocks that +# are present in the base image are discarded in the intermediate image. +# This intends to check that these blocks are also discarded in the base +# image after the commit. +# +# Copyright (C) 2024 Vincent Vanlaer. +# +# This program is free software; you can redistribute it and/or modify +# it under the 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=libvirt-e6954efa@volkihar.be + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_qemu + _rm_test_img "${TEST_IMG}.base" + _rm_test_img "${TEST_IMG}.mid" + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 +_supported_proto file + +size="1M" + +TEST_IMG="$TEST_IMG.base" _make_test_img $size +TEST_IMG="$TEST_IMG.mid" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT $size +_make_test_img -b "${TEST_IMG}.mid" -F $IMGFMT $size + +$QEMU_IO -c "write -P 0x01 64k 128k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "discard 64k 64k" "$TEST_IMG.mid" | _filter_qemu_io + +echo +echo "=== Base image info before commit ===" +TEST_IMG="${TEST_IMG}.base" _img_info | _filter_img_info +$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map + +echo +echo "=== Middle image info before commit ===" +TEST_IMG="${TEST_IMG}.mid" _img_info | _filter_img_info +$QEMU_IMG map --output=json "$TEST_IMG.mid" | _filter_qemu_img_map + +echo +echo === Running QEMU Live Commit Test === +echo + +qemu_comm_method="qmp" +_launch_qemu -drive file="${TEST_IMG}",if=virtio,id=test +h=$QEMU_HANDLE + +_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return" + +_send_qemu_cmd $h "{ 'execute': 'block-commit', + 'arguments': { 'device': 'test', + 'top': '"${TEST_IMG}.mid"', + 'base': '"${TEST_IMG}.base"'} }" '"status": "null"' + +_cleanup_qemu + +echo +echo "=== Base image info after commit ===" +TEST_IMG="${TEST_IMG}.base" _img_info | _filter_img_info +$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/commit-zero-blocks.out b/tests/qemu-iotests/tests/commit-zero-blocks.out new file mode 100644 index 00000000000..85bdc46aafd --- /dev/null +++ b/tests/qemu-iotests/tests/commit-zero-blocks.out @@ -0,0 +1,54 @@ +QA output created by commit-zero-blocks +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', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.mid backing_fmt=IMGFMT +wrote 131072/131072 bytes at offset 65536 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Base image info before commit === +image: TEST_DIR/t.IMGFMT.base +file format: IMGFMT +virtual size: 1 MiB (1048576 bytes) +[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 131072, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 196608, "length": 851968, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] + +=== Middle image info before commit === +image: TEST_DIR/t.IMGFMT.mid +file format: IMGFMT +virtual size: 1 MiB (1048576 bytes) +backing file: TEST_DIR/t.IMGFMT.base +backing file format: IMGFMT +[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 131072, "length": 65536, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 196608, "length": 851968, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] + +=== Running QEMU Live Commit Test === + +{ 'execute': 'qmp_capabilities' } +{"return": {}} +{ 'execute': 'block-commit', + 'arguments': { 'device': 'test', + 'top': 'TEST_DIR/t.IMGFMT.mid', + 'base': 'TEST_DIR/t.IMGFMT.base'} } +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "test"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "test", "len": 1048576, "offset": 1048576, "speed": 0, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "test"}} + +=== Base image info after commit === +image: TEST_DIR/t.IMGFMT.base +file format: IMGFMT +virtual size: 1 MiB (1048576 bytes) +[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 131072, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 196608, "length": 851968, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] +*** done diff --git a/tests/qemu-iotests/tests/copy-before-write b/tests/qemu-iotests/tests/copy-before-write index 2ffe092b318..236cb8ac374 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,75 @@ 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' + }) + + result = self.vm.qmp('human-monitor-command', + command_line='qemu-io cbw "write 0 1M"') + self.assert_qmp(result, 'return', '') + + result = self.vm.qmp('human-monitor-command', + command_line='qemu-io access "read 0 1M"') + self.assert_qmp(result, 'return', '') + + self.vm.shutdown() + log = self.vm.get_log() + log = iotests.filter_qtest(log) + log = iotests.filter_qemu_io(log) + return log + + def do_cbw_error_via_blockdev_backup(self, on_cbw_error=None): + self.vm.cmd('blockdev-add', { + 'node-name': 'source', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': source_img + } + }) + + self.vm.cmd('blockdev-add', { + 'node-name': 'target', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'blkdebug', + 'image': { + 'driver': 'file', + 'filename': temp_img + }, + 'inject-error': [ + { + 'event': 'write_aio', + 'errno': 5, + 'immediately': False, + 'once': True + } + ] + } + }) + + blockdev_backup_options = { + 'device': 'source', + 'target': 'target', + 'sync': 'none', + 'job-id': 'job-id', + 'filter-node-name': 'cbw' + } + + if on_cbw_error: + blockdev_backup_options['on-cbw-error'] = on_cbw_error + + self.vm.cmd('blockdev-backup', blockdev_backup_options) + + 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"') @@ -98,8 +158,6 @@ class TestCbwError(iotests.QMPTestCase): self.vm.shutdown() log = self.vm.get_log() - log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) - log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) log = iotests.filter_qemu_io(log) return log @@ -127,17 +185,49 @@ read failed: Permission denied write failed: Input/output error read 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +""") + + def test_break_snapshot_policy_forwarding(self): + """Ensure CBW filter accepts break-snapshot policy + specified in blockdev-backup QMP command. + """ + log = self.do_cbw_error_via_blockdev_backup('break-snapshot') + self.assertEqual(log, """\ +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read failed: Permission denied +""") + + def test_break_guest_write_policy_forwarding(self): + """Ensure CBW filter accepts break-guest-write policy + specified in blockdev-backup QMP command. + """ + log = self.do_cbw_error_via_blockdev_backup('break-guest-write') + self.assertEqual(log, """\ +write failed: Input/output error +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +""") + + def test_default_on_cbw_error_policy_forwarding(self): + """Ensure break-guest-write policy is used by default when + on-cbw-error is not explicitly specified. + """ + log = self.do_cbw_error_via_blockdev_backup() + self.assertEqual(log, """\ +write failed: Input/output error +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) """) def do_cbw_timeout(self, on_cbw_error): - 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 +251,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/copy-before-write.out b/tests/qemu-iotests/tests/copy-before-write.out index 89968f35d78..2f7d3902f23 100644 --- a/tests/qemu-iotests/tests/copy-before-write.out +++ b/tests/qemu-iotests/tests/copy-before-write.out @@ -1,5 +1,5 @@ -.... +....... ---------------------------------------------------------------------- -Ran 4 tests +Ran 7 tests OK diff --git a/tests/qemu-iotests/tests/detect-zeroes-registered-buf b/tests/qemu-iotests/tests/detect-zeroes-registered-buf index edb5f2cee52..5eaf34e5a6b 100755 --- a/tests/qemu-iotests/tests/detect-zeroes-registered-buf +++ b/tests/qemu-iotests/tests/detect-zeroes-registered-buf @@ -36,6 +36,8 @@ _cleanup() } trap "_cleanup; exit \$status" 0 1 2 3 15 +IMGOPTSSYNTAX=true + # get standard environment, filters and checks cd .. . ./common.rc @@ -46,7 +48,7 @@ _supported_proto generic size=128M _make_test_img $size -IMGSPEC="driver=$IMGFMT,file.filename=$TEST_IMG,discard=unmap,detect-zeroes=unmap" +IMGSPEC="$TEST_IMG,discard=unmap,detect-zeroes=unmap" echo echo "== writing zero buffer to image ==" diff --git a/tests/qemu-iotests/tests/export-incoming-iothread b/tests/qemu-iotests/tests/export-incoming-iothread index 7679e491035..d36d6194e0a 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 00000000000..fb8db73b31b --- /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 00000000000..0f46455a94a --- /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 750e7d4d387..dca1167b6d1 100755 --- a/tests/qemu-iotests/tests/graph-changes-while-io +++ b/tests/qemu-iotests/tests/graph-changes-while-io @@ -27,6 +27,7 @@ from iotests import imgfmt, qemu_img, qemu_img_create, qemu_io, \ top = os.path.join(iotests.test_dir, 'top.img') +mid = os.path.join(iotests.test_dir, 'mid.img') nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock') @@ -57,6 +58,16 @@ class TestGraphChangesWhileIO(QMPTestCase): def tearDown(self) -> None: self.qsd.stop() + os.remove(top) + + def _wait_for_blockjob(self, status: str) -> None: + done = False + while not done: + for event in self.qsd.get_qmp().get_events(wait=10.0): + if event['event'] != 'JOB_STATUS_CHANGE': + continue + if event['data']['status'] == status: + done = True def test_blockdev_add_while_io(self) -> None: # Run qemu-img bench in the background @@ -66,7 +77,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 +86,10 @@ 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() @@ -92,7 +101,7 @@ class TestGraphChangesWhileIO(QMPTestCase): qemu_io('-c', 'write 0 64k', top) qemu_io('-c', 'write 128k 64k', top) - result = self.qsd.qmp('blockdev-add', { + self.qsd.cmd('blockdev-add', { 'driver': imgfmt, 'node-name': 'overlay', 'backing': None, @@ -101,36 +110,109 @@ class TestGraphChangesWhileIO(QMPTestCase): 'filename': top } }) - self.assert_qmp(result, 'return', {}) - result = self.qsd.qmp('blockdev-snapshot', { + self.qsd.cmd('blockdev-snapshot', { 'node': 'node0', 'overlay': 'overlay', }) - self.assert_qmp(result, 'return', {}) # While qemu-img bench is running, repeatedly commit overlay to node0 while bench_thr.is_alive(): - result = self.qsd.qmp('block-commit', { + self.qsd.cmd('block-commit', { 'job-id': 'job0', 'device': 'overlay', }) - self.assert_qmp(result, 'return', {}) - result = self.qsd.qmp('block-job-cancel', { + self.qsd.cmd('block-job-cancel', { 'device': 'job0', }) - self.assert_qmp(result, 'return', {}) - 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 + self._wait_for_blockjob('null') + + bench_thr.join() + + def test_remove_lower_snapshot_while_io(self) -> None: + # Run qemu-img bench in the background + bench_thr = Thread(target=do_qemu_img_bench, args=(100000, )) + bench_thr.start() + + # While I/O is performed on 'node0' node, consequently add 2 snapshots + # on top of it, then remove (commit) them starting from lower one. + while bench_thr.is_alive(): + # Recreate snapshot images on every iteration + qemu_img_create('-f', imgfmt, mid, '1G') + qemu_img_create('-f', imgfmt, top, '1G') + + self.qsd.cmd('blockdev-add', { + 'driver': imgfmt, + 'node-name': 'mid', + 'file': { + 'driver': 'file', + 'filename': mid + } + }) + + self.qsd.cmd('blockdev-snapshot', { + 'node': 'node0', + 'overlay': 'mid', + }) + + self.qsd.cmd('blockdev-add', { + 'driver': imgfmt, + 'node-name': 'top', + 'file': { + 'driver': 'file', + 'filename': top + } + }) + + self.qsd.cmd('blockdev-snapshot', { + 'node': 'mid', + 'overlay': 'top', + }) + + self.qsd.cmd('block-commit', { + 'job-id': 'commit-mid', + 'device': 'top', + 'top-node': 'mid', + 'base-node': 'node0', + 'auto-finalize': True, + 'auto-dismiss': False, + }) + + self._wait_for_blockjob('concluded') + self.qsd.cmd('job-dismiss', { + 'id': 'commit-mid', + }) + + self.qsd.cmd('block-commit', { + 'job-id': 'commit-top', + 'device': 'top', + 'top-node': 'top', + 'base-node': 'node0', + 'auto-finalize': True, + 'auto-dismiss': False, + }) + + self._wait_for_blockjob('ready') + self.qsd.cmd('job-complete', { + 'id': 'commit-top', + }) + + self._wait_for_blockjob('concluded') + self.qsd.cmd('job-dismiss', { + 'id': 'commit-top', + }) + + self.qsd.cmd('blockdev-del', { + 'node-name': 'mid' + }) + self.qsd.cmd('blockdev-del', { + 'node-name': 'top' + }) bench_thr.join() + os.remove(mid) if __name__ == '__main__': # Format must support raw backing files diff --git a/tests/qemu-iotests/tests/graph-changes-while-io.out b/tests/qemu-iotests/tests/graph-changes-while-io.out index fbc63e62f88..8d7e9967009 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 2 tests +Ran 3 tests OK diff --git a/tests/qemu-iotests/tests/image-fleecing b/tests/qemu-iotests/tests/image-fleecing index f6e449d0711..5e3b2c7e46a 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/inactive-node-nbd b/tests/qemu-iotests/tests/inactive-node-nbd new file mode 100755 index 00000000000..a95b37e7962 --- /dev/null +++ b/tests/qemu-iotests/tests/inactive-node-nbd @@ -0,0 +1,303 @@ +#!/usr/bin/env python3 +# group: rw quick +# +# 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 . +# +# Creator/Owner: Kevin Wolf + +import iotests + +from iotests import QemuIoInteractive +from iotests import filter_qemu_io, filter_qtest, filter_qmp_testfiles + +iotests.script_initialize(supported_fmts=['generic'], + supported_protocols=['file'], + supported_platforms=['linux']) + +def get_export(node_name='disk-fmt', allow_inactive=None): + exp = { + 'id': 'exp0', + 'type': 'nbd', + 'node-name': node_name, + 'writable': True, + } + + if allow_inactive is not None: + exp['allow-inactive'] = allow_inactive + + return exp + +def node_is_active(_vm, node_name): + nodes = _vm.cmd('query-named-block-nodes', flat=True) + node = next(n for n in nodes if n['node-name'] == node_name) + return node['active'] + +with iotests.FilePath('disk.img') as path, \ + iotests.FilePath('snap.qcow2') as snap_path, \ + iotests.FilePath('snap2.qcow2') as snap2_path, \ + iotests.FilePath('target.img') as target_path, \ + iotests.FilePath('nbd.sock', base_dir=iotests.sock_dir) as nbd_sock, \ + iotests.VM() as vm: + + img_size = '10M' + + iotests.log('Preparing disk...') + iotests.qemu_img_create('-f', iotests.imgfmt, path, img_size) + iotests.qemu_img_create('-f', iotests.imgfmt, target_path, img_size) + + iotests.qemu_img_create('-f', 'qcow2', '-b', path, '-F', iotests.imgfmt, + snap_path) + iotests.qemu_img_create('-f', 'qcow2', '-b', snap_path, '-F', 'qcow2', + snap2_path) + + iotests.log('Launching VM...') + vm.add_blockdev(f'file,node-name=disk-file,filename={path}') + vm.add_blockdev(f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt,' + 'active=off') + vm.add_blockdev(f'file,node-name=target-file,filename={target_path}') + vm.add_blockdev(f'{iotests.imgfmt},file=target-file,node-name=target-fmt') + vm.add_blockdev(f'file,node-name=snap-file,filename={snap_path}') + vm.add_blockdev(f'file,node-name=snap2-file,filename={snap2_path}') + + # Actually running the VM activates all images + vm.add_paused() + + vm.launch() + vm.qmp_log('nbd-server-start', + addr={'type': 'unix', 'data':{'path': nbd_sock}}, + filters=[filter_qmp_testfiles]) + + iotests.log('\n=== Creating export of inactive node ===') + + iotests.log('\nExports activate nodes without allow-inactive') + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('block-export-add', **get_export()) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('query-block-exports') + vm.qmp_log('block-export-del', id='exp0') + vm.event_wait('BLOCK_EXPORT_DELETED') + vm.qmp_log('query-block-exports') + + iotests.log('\nExports activate nodes with allow-inactive=false') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('block-export-add', **get_export(allow_inactive=False)) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('query-block-exports') + vm.qmp_log('block-export-del', id='exp0') + vm.event_wait('BLOCK_EXPORT_DELETED') + vm.qmp_log('query-block-exports') + + iotests.log('\nExport leaves nodes inactive with allow-inactive=true') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('block-export-add', **get_export(allow_inactive=True)) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('query-block-exports') + vm.qmp_log('block-export-del', id='exp0') + vm.event_wait('BLOCK_EXPORT_DELETED') + vm.qmp_log('query-block-exports') + + iotests.log('\n=== Inactivating node with existing export ===') + + iotests.log('\nInactivating nodes with an export fails without ' + 'allow-inactive') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True) + vm.qmp_log('block-export-add', **get_export(node_name='disk-fmt')) + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('query-block-exports') + vm.qmp_log('block-export-del', id='exp0') + vm.event_wait('BLOCK_EXPORT_DELETED') + vm.qmp_log('query-block-exports') + + iotests.log('\nInactivating nodes with an export fails with ' + 'allow-inactive=false') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True) + vm.qmp_log('block-export-add', + **get_export(node_name='disk-fmt', allow_inactive=False)) + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('query-block-exports') + vm.qmp_log('block-export-del', id='exp0') + vm.event_wait('BLOCK_EXPORT_DELETED') + vm.qmp_log('query-block-exports') + + iotests.log('\nInactivating nodes with an export works with ' + 'allow-inactive=true') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True) + vm.qmp_log('block-export-add', + **get_export(node_name='disk-fmt', allow_inactive=True)) + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('query-block-exports') + vm.qmp_log('block-export-del', id='exp0') + vm.event_wait('BLOCK_EXPORT_DELETED') + vm.qmp_log('query-block-exports') + + iotests.log('\n=== Inactive nodes with parent ===') + + iotests.log('\nInactivating nodes with an active parent fails') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True) + vm.qmp_log('blockdev-set-active', node_name='disk-file', active=False) + iotests.log('disk-file active: %s' % node_is_active(vm, 'disk-file')) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + + iotests.log('\nInactivating nodes with an inactive parent works') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) + vm.qmp_log('blockdev-set-active', node_name='disk-file', active=False) + iotests.log('disk-file active: %s' % node_is_active(vm, 'disk-file')) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + + iotests.log('\nCreating active parent node with an inactive child fails') + vm.qmp_log('blockdev-add', driver='raw', file='disk-fmt', + node_name='disk-filter') + vm.qmp_log('blockdev-add', driver='raw', file='disk-fmt', + node_name='disk-filter', active=True) + + iotests.log('\nCreating inactive parent node with an inactive child works') + vm.qmp_log('blockdev-add', driver='raw', file='disk-fmt', + node_name='disk-filter', active=False) + vm.qmp_log('blockdev-del', node_name='disk-filter') + + iotests.log('\n=== Resizing an inactive node ===') + vm.qmp_log('block_resize', node_name='disk-fmt', size=16*1024*1024) + + iotests.log('\n=== Taking a snapshot of an inactive node ===') + + iotests.log('\nActive overlay over inactive backing file automatically ' + 'makes both inactive for compatibility') + vm.qmp_log('blockdev-add', driver='qcow2', node_name='snap-fmt', + file='snap-file', backing=None) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) + vm.qmp_log('blockdev-snapshot', node='disk-fmt', overlay='snap-fmt') + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) + vm.qmp_log('blockdev-del', node_name='snap-fmt') + + iotests.log('\nInactive overlay over inactive backing file just works') + vm.qmp_log('blockdev-add', driver='qcow2', node_name='snap-fmt', + file='snap-file', backing=None, active=False) + vm.qmp_log('blockdev-snapshot', node='disk-fmt', overlay='snap-fmt') + + iotests.log('\n=== Block jobs with inactive nodes ===') + + iotests.log('\nStreaming into an inactive node') + vm.qmp_log('block-stream', device='snap-fmt', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\nCommitting an inactive root node (active commit)') + vm.qmp_log('block-commit', job_id='job0', device='snap-fmt', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\nCommitting an inactive intermediate node to inactive base') + vm.qmp_log('blockdev-add', driver='qcow2', node_name='snap2-fmt', + file='snap2-file', backing='snap-fmt', active=False) + + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) + iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt')) + + vm.qmp_log('block-commit', job_id='job0', device='snap2-fmt', + top_node='snap-fmt', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\nCommitting an inactive intermediate node to active base') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True) + vm.qmp_log('block-commit', job_id='job0', device='snap2-fmt', + top_node='snap-fmt', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\nMirror from inactive source to active target') + vm.qmp_log('blockdev-mirror', job_id='job0', device='snap2-fmt', + target='target-fmt', sync='full', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\nMirror from active source to inactive target') + + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) + iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt')) + iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt')) + + # Activating snap2-fmt recursively activates the whole backing chain + vm.qmp_log('blockdev-set-active', node_name='snap2-fmt', active=True) + vm.qmp_log('blockdev-set-active', node_name='target-fmt', active=False) + + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) + iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt')) + iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt')) + + vm.qmp_log('blockdev-mirror', job_id='job0', device='snap2-fmt', + target='target-fmt', sync='full', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\nBackup from active source to inactive target') + + vm.qmp_log('blockdev-backup', job_id='job0', device='snap2-fmt', + target='target-fmt', sync='full', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\nBackup from inactive source to active target') + + # Inactivating snap2-fmt recursively inactivates the whole backing chain + vm.qmp_log('blockdev-set-active', node_name='snap2-fmt', active=False) + vm.qmp_log('blockdev-set-active', node_name='target-fmt', active=True) + + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) + iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt')) + iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt')) + + vm.qmp_log('blockdev-backup', job_id='job0', device='snap2-fmt', + target='target-fmt', sync='full', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\n=== Accessing export on inactive node ===') + + # Use the target node because it has the right image format and isn't the + # (read-only) backing file of a qcow2 node + vm.qmp_log('blockdev-set-active', node_name='target-fmt', active=False) + vm.qmp_log('block-export-add', + **get_export(node_name='target-fmt', allow_inactive=True)) + + # The read should succeed, everything else should fail gracefully + qemu_io = QemuIoInteractive('-f', 'raw', + f'nbd+unix:///target-fmt?socket={nbd_sock}') + iotests.log(qemu_io.cmd('read 0 64k'), filters=[filter_qemu_io]) + iotests.log(qemu_io.cmd('write 0 64k'), filters=[filter_qemu_io]) + iotests.log(qemu_io.cmd('write -z 0 64k'), filters=[filter_qemu_io]) + iotests.log(qemu_io.cmd('write -zu 0 64k'), filters=[filter_qemu_io]) + iotests.log(qemu_io.cmd('discard 0 64k'), filters=[filter_qemu_io]) + iotests.log(qemu_io.cmd('flush'), filters=[filter_qemu_io]) + iotests.log(qemu_io.cmd('map'), filters=[filter_qemu_io]) + qemu_io.close() + + iotests.log('\n=== Resuming VM activates all images ===') + vm.qmp_log('cont') + + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) + iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt')) + iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt')) + + iotests.log('\nShutting down...') + vm.shutdown() + log = vm.get_log() + if log: + iotests.log(log, [filter_qtest, filter_qemu_io]) diff --git a/tests/qemu-iotests/tests/inactive-node-nbd.out b/tests/qemu-iotests/tests/inactive-node-nbd.out new file mode 100644 index 00000000000..a458b4fc055 --- /dev/null +++ b/tests/qemu-iotests/tests/inactive-node-nbd.out @@ -0,0 +1,239 @@ +Preparing disk... +Launching VM... +{"execute": "nbd-server-start", "arguments": {"addr": {"data": {"path": "SOCK_DIR/PID-nbd.sock"}, "type": "unix"}}} +{"return": {}} + +=== Creating export of inactive node === + +Exports activate nodes without allow-inactive +disk-fmt active: False +{"execute": "block-export-add", "arguments": {"id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} +{"return": {}} +disk-fmt active: True +{"execute": "query-block-exports", "arguments": {}} +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} +{"execute": "block-export-del", "arguments": {"id": "exp0"}} +{"return": {}} +{"execute": "query-block-exports", "arguments": {}} +{"return": []} + +Exports activate nodes with allow-inactive=false +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} +{"return": {}} +disk-fmt active: False +{"execute": "block-export-add", "arguments": {"allow-inactive": false, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} +{"return": {}} +disk-fmt active: True +{"execute": "query-block-exports", "arguments": {}} +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} +{"execute": "block-export-del", "arguments": {"id": "exp0"}} +{"return": {}} +{"execute": "query-block-exports", "arguments": {}} +{"return": []} + +Export leaves nodes inactive with allow-inactive=true +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} +{"return": {}} +disk-fmt active: False +{"execute": "block-export-add", "arguments": {"allow-inactive": true, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} +{"return": {}} +disk-fmt active: False +{"execute": "query-block-exports", "arguments": {}} +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} +{"execute": "block-export-del", "arguments": {"id": "exp0"}} +{"return": {}} +{"execute": "query-block-exports", "arguments": {}} +{"return": []} + +=== Inactivating node with existing export === + +Inactivating nodes with an export fails without allow-inactive +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}} +{"return": {}} +{"execute": "block-export-add", "arguments": {"id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} +{"return": {}} +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} +{"error": {"class": "GenericError", "desc": "Failed to inactivate node: Operation not permitted"}} +disk-fmt active: True +{"execute": "query-block-exports", "arguments": {}} +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} +{"execute": "block-export-del", "arguments": {"id": "exp0"}} +{"return": {}} +{"execute": "query-block-exports", "arguments": {}} +{"return": []} + +Inactivating nodes with an export fails with allow-inactive=false +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}} +{"return": {}} +{"execute": "block-export-add", "arguments": {"allow-inactive": false, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} +{"return": {}} +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} +{"error": {"class": "GenericError", "desc": "Failed to inactivate node: Operation not permitted"}} +disk-fmt active: True +{"execute": "query-block-exports", "arguments": {}} +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} +{"execute": "block-export-del", "arguments": {"id": "exp0"}} +{"return": {}} +{"execute": "query-block-exports", "arguments": {}} +{"return": []} + +Inactivating nodes with an export works with allow-inactive=true +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}} +{"return": {}} +{"execute": "block-export-add", "arguments": {"allow-inactive": true, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} +{"return": {}} +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} +{"return": {}} +disk-fmt active: False +{"execute": "query-block-exports", "arguments": {}} +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} +{"execute": "block-export-del", "arguments": {"id": "exp0"}} +{"return": {}} +{"execute": "query-block-exports", "arguments": {}} +{"return": []} + +=== Inactive nodes with parent === + +Inactivating nodes with an active parent fails +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}} +{"return": {}} +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-file"}} +{"error": {"class": "GenericError", "desc": "Node has active parent node"}} +disk-file active: True +disk-fmt active: True + +Inactivating nodes with an inactive parent works +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} +{"return": {}} +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-file"}} +{"return": {}} +disk-file active: False +disk-fmt active: False + +Creating active parent node with an inactive child fails +{"execute": "blockdev-add", "arguments": {"driver": "raw", "file": "disk-fmt", "node-name": "disk-filter"}} +{"error": {"class": "GenericError", "desc": "Inactive 'disk-fmt' can't be a file child of active 'disk-filter'"}} +{"execute": "blockdev-add", "arguments": {"active": true, "driver": "raw", "file": "disk-fmt", "node-name": "disk-filter"}} +{"error": {"class": "GenericError", "desc": "Inactive 'disk-fmt' can't be a file child of active 'disk-filter'"}} + +Creating inactive parent node with an inactive child works +{"execute": "blockdev-add", "arguments": {"active": false, "driver": "raw", "file": "disk-fmt", "node-name": "disk-filter"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": {"node-name": "disk-filter"}} +{"return": {}} + +=== Resizing an inactive node === +{"execute": "block_resize", "arguments": {"node-name": "disk-fmt", "size": 16777216}} +{"error": {"class": "GenericError", "desc": "Permission 'resize' unavailable on inactive node"}} + +=== Taking a snapshot of an inactive node === + +Active overlay over inactive backing file automatically makes both inactive for compatibility +{"execute": "blockdev-add", "arguments": {"backing": null, "driver": "qcow2", "file": "snap-file", "node-name": "snap-fmt"}} +{"return": {}} +disk-fmt active: False +snap-fmt active: True +{"execute": "blockdev-snapshot", "arguments": {"node": "disk-fmt", "overlay": "snap-fmt"}} +{"return": {}} +disk-fmt active: False +snap-fmt active: False +{"execute": "blockdev-del", "arguments": {"node-name": "snap-fmt"}} +{"return": {}} + +Inactive overlay over inactive backing file just works +{"execute": "blockdev-add", "arguments": {"active": false, "backing": null, "driver": "qcow2", "file": "snap-file", "node-name": "snap-fmt"}} +{"return": {}} +{"execute": "blockdev-snapshot", "arguments": {"node": "disk-fmt", "overlay": "snap-fmt"}} +{"return": {}} + +=== Block jobs with inactive nodes === + +Streaming into an inactive node +{"execute": "block-stream", "arguments": {"device": "snap-fmt"}} +{"error": {"class": "GenericError", "desc": "Could not create node: Inactive 'snap-fmt' can't be a file child of active 'NODE_NAME'"}} + +Committing an inactive root node (active commit) +{"execute": "block-commit", "arguments": {"device": "snap-fmt", "job-id": "job0"}} +{"error": {"class": "GenericError", "desc": "Inactive 'snap-fmt' can't be a backing child of active 'NODE_NAME'"}} + +Committing an inactive intermediate node to inactive base +{"execute": "blockdev-add", "arguments": {"active": false, "backing": "snap-fmt", "driver": "qcow2", "file": "snap2-file", "node-name": "snap2-fmt"}} +{"return": {}} +disk-fmt active: False +snap-fmt active: False +snap2-fmt active: False +{"execute": "block-commit", "arguments": {"device": "snap2-fmt", "job-id": "job0", "top-node": "snap-fmt"}} +{"error": {"class": "GenericError", "desc": "Inactive 'snap-fmt' can't be a backing child of active 'NODE_NAME'"}} + +Committing an inactive intermediate node to active base +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}} +{"return": {}} +{"execute": "block-commit", "arguments": {"device": "snap2-fmt", "job-id": "job0", "top-node": "snap-fmt"}} +{"error": {"class": "GenericError", "desc": "Inactive 'snap-fmt' can't be a backing child of active 'NODE_NAME'"}} + +Mirror from inactive source to active target +{"execute": "blockdev-mirror", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}} +{"error": {"class": "GenericError", "desc": "Inactive 'snap2-fmt' can't be a backing child of active 'NODE_NAME'"}} + +Mirror from active source to inactive target +disk-fmt active: True +snap-fmt active: False +snap2-fmt active: False +target-fmt active: True +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "snap2-fmt"}} +{"return": {}} +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "target-fmt"}} +{"return": {}} +disk-fmt active: True +snap-fmt active: True +snap2-fmt active: True +target-fmt active: False +{"execute": "blockdev-mirror", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}} +{"error": {"class": "GenericError", "desc": "Permission 'write' unavailable on inactive node"}} + +Backup from active source to inactive target +{"execute": "blockdev-backup", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}} +{"error": {"class": "GenericError", "desc": "Could not create node: Inactive 'target-fmt' can't be a target child of active 'NODE_NAME'"}} + +Backup from inactive source to active target +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "snap2-fmt"}} +{"return": {}} +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "target-fmt"}} +{"return": {}} +disk-fmt active: False +snap-fmt active: False +snap2-fmt active: False +target-fmt active: True +{"execute": "blockdev-backup", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}} +{"error": {"class": "GenericError", "desc": "Could not create node: Inactive 'snap2-fmt' can't be a file child of active 'NODE_NAME'"}} + +=== Accessing export on inactive node === +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "target-fmt"}} +{"return": {}} +{"execute": "block-export-add", "arguments": {"allow-inactive": true, "id": "exp0", "node-name": "target-fmt", "type": "nbd", "writable": true}} +{"return": {}} +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +write failed: Operation not permitted + +write failed: Operation not permitted + +write failed: Operation not permitted + +discard failed: Operation not permitted + + +qemu-io: Failed to get allocation status: Operation not permitted + + +=== Resuming VM activates all images === +{"execute": "cont", "arguments": {}} +{"return": {}} +disk-fmt active: True +snap-fmt active: True +snap2-fmt active: True +target-fmt active: True + +Shutting down... + diff --git a/tests/qemu-iotests/tests/iothreads-nbd-export b/tests/qemu-iotests/tests/iothreads-nbd-export new file mode 100755 index 00000000000..037260729c7 --- /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 00000000000..bc514e35e59 --- /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.out b/tests/qemu-iotests/tests/iothreads-resize.out index 2ca5a9d9644..2967ac8f0d1 100644 --- a/tests/qemu-iotests/tests/iothreads-resize.out +++ b/tests/qemu-iotests/tests/iothreads-resize.out @@ -3,8 +3,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 QMP_VERSION {"return": {}} {"return": {}} -{"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"return": {}} image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 128 MiB (134217728 bytes) diff --git a/tests/qemu-iotests/tests/iothreads-stream b/tests/qemu-iotests/tests/iothreads-stream new file mode 100755 index 00000000000..231195b5e87 --- /dev/null +++ b/tests/qemu-iotests/tests/iothreads-stream @@ -0,0 +1,75 @@ +#!/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('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 00000000000..ef134165e5c --- /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/luks-detached-header b/tests/qemu-iotests/tests/luks-detached-header new file mode 100755 index 00000000000..3455fd8de1e --- /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 00000000000..fbc63e62f88 --- /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 dda55fad284..c519e6db8c7 100755 --- a/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test +++ b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test @@ -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 59f33575805..8fb4099201d 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) @@ -125,11 +122,10 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): # catch 'Could not reopen qcow2 layer: Bitmap already exists' # possible error - log = self.vm_a.get_log() - log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) - log = re.sub(r'^(wrote .* bytes at offset .*\n.*KiB.*ops.*sec.*\n){3}', + log = iotests.filter_qtest(self.vm_a.get_log()) + log = re.sub(r'^(wrote .* bytes at offset .*\n' + r'.*KiB.*ops.*sec.*\n?){3}', '', log) - log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) self.assertEqual(log, '') # test that bitmap is still persistent @@ -158,9 +154,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 +166,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 +178,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 +246,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 +255,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 +266,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 34103229eef..afb2277896a 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', {}) - - result = self.vm.qmp('job-pause', id='drive0') - self.assert_qmp(result, 'return', {}) - - 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('blockdev-backup', device='drive0', + target='target', sync='full', + speed=1, x_perf={ + 'max-workers': 1, + 'max-chunk': 64 * 1024 + }) + + self.vm.cmd('job-pause', id='drive0') + + 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 4e1da369c94..0deaad2d3a6 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 00000000000..51788b85c7a --- /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 00000000000..ae1213e6f86 --- /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 01217459b98..ed2e46447e3 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-sparse b/tests/qemu-iotests/tests/mirror-sparse new file mode 100755 index 00000000000..cfcaa600ab4 --- /dev/null +++ b/tests/qemu-iotests/tests/mirror-sparse @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +# group: rw auto quick +# +# Test blockdev-mirror with raw sparse destination +# +# Copyright (C) 2025 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +seq="$(basename $0)" +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + _cleanup_qemu +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 raw # Format of the source. dst is always raw file +_supported_proto file +_supported_os Linux +_require_disk_usage + +echo +echo "=== Initial image setup ===" +echo + +TEST_IMG="$TEST_IMG.base" _make_test_img 20M +$QEMU_IO -c 'w 8M 2M' -f $IMGFMT "$TEST_IMG.base" | _filter_qemu_io + +_launch_qemu \ + -blockdev '{"driver":"file", "cache":{"direct":true, "no-flush":false}, + "filename":"'"$TEST_IMG.base"'", "node-name":"src-file"}' \ + -blockdev '{"driver":"'$IMGFMT'", "node-name":"src", "file":"src-file"}' +h1=$QEMU_HANDLE +_send_qemu_cmd $h1 '{"execute": "qmp_capabilities"}' 'return' + +# Check several combinations; most should result in a sparse destination; +# the destination should only be fully allocated if pre-allocated +# and not punching holes due to detect-zeroes +# do_test creation discard zeroes result +do_test() { + creation=$1 + discard=$2 + zeroes=$3 + expected=$4 + +echo +echo "=== Testing creation=$creation discard=$discard zeroes=$zeroes ===" +echo + +rm -f $TEST_IMG +if test $creation = external; then + truncate --size=20M $TEST_IMG +else + _send_qemu_cmd $h1 '{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"'$TEST_IMG'", + "size":'$((20*1024*1024))', "preallocation":"'$creation'"}, + "job-id":"job1"}}' 'concluded' + _send_qemu_cmd $h1 '{"execute": "job-dismiss", "arguments": + {"id": "job1"}}' 'return' +fi +_send_qemu_cmd $h1 '{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"'$TEST_IMG'", "aio":"threads", + "auto-read-only":true, "discard":"'$discard'", + "detect-zeroes":"'$zeroes'"}}' 'return' +_send_qemu_cmd $h1 '{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}}' 'return' +_timed_wait_for $h1 '"ready"' +_send_qemu_cmd $h1 '{"execute": "job-complete", "arguments": + {"id":"job2"}}' 'return' \ + | _filter_block_job_offset | _filter_block_job_len +_send_qemu_cmd $h1 '{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}}' 'return' \ + | _filter_block_job_offset | _filter_block_job_len +$QEMU_IMG compare -U -f $IMGFMT -F raw $TEST_IMG.base $TEST_IMG +# Some filesystems can fudge allocations for various reasons; rather +# than expecting precise 2M and 20M images, it is better to allow for slop. +result=$(disk_usage $TEST_IMG) +if test $result -lt $((4*1024*1024)); then + actual=sparse +elif test $result -gt $((19*1024*1024)); then + actual=full +else + actual="unexpected size ($result)" +fi +echo "Destination is $actual; expected $expected" +} + +do_test external ignore off sparse +do_test external unmap off sparse +do_test external unmap unmap sparse +do_test off ignore off sparse +do_test off unmap off sparse +do_test off unmap unmap sparse +do_test full ignore off full +do_test full unmap off sparse +do_test full unmap unmap sparse + +_send_qemu_cmd $h1 '{"execute":"quit"}' '' + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/mirror-sparse.out b/tests/qemu-iotests/tests/mirror-sparse.out new file mode 100644 index 00000000000..2103b891c3f --- /dev/null +++ b/tests/qemu-iotests/tests/mirror-sparse.out @@ -0,0 +1,365 @@ +QA output created by mirror-sparse + +=== Initial image setup === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=20971520 +wrote 2097152/2097152 bytes at offset 8388608 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"execute": "qmp_capabilities"} +{"return": {}} + +=== Testing creation=external discard=ignore zeroes=off === + +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"ignore", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=external discard=unmap zeroes=off === + +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=external discard=unmap zeroes=unmap === + +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"unmap"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=off discard=ignore zeroes=off === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"off"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"ignore", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=off discard=unmap zeroes=off === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"off"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=off discard=unmap zeroes=unmap === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"off"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"unmap"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=full discard=ignore zeroes=off === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"full"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"ignore", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is full; expected full + +=== Testing creation=full discard=unmap zeroes=off === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"full"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=full discard=unmap zeroes=unmap === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"full"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"unmap"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse +{"execute":"quit"} +*** done diff --git a/tests/qemu-iotests/tests/mirror-top-perms b/tests/qemu-iotests/tests/mirror-top-perms index 8bca5927089..fab9907e928 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 b121f2e363d..479e872f2a8 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 9d938db24e6..56b57c69ed8 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-tls-iothread b/tests/qemu-iotests/tests/nbd-tls-iothread new file mode 100755 index 00000000000..a2fb07206e5 --- /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 00000000000..1d83d4f903b --- /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 index a7a1b357b5a..b281246a423 100755 --- a/tests/qemu-iotests/tests/parallels-checks +++ b/tests/qemu-iotests/tests/parallels-checks @@ -91,7 +91,7 @@ file_size=`stat --printf="%s" "$TEST_IMG"` echo "file size: $file_size" echo "== check last cluster ==" -{ $QEMU_IO -c "read -P 0x11 $LAST_CLUSTER_OFF $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $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 @@ -105,25 +105,68 @@ 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 -c "read -P 0x55 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $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 -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $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 -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $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 first cluster on host ==" -printf "content: 0x%02x\n" `peek_file_le $TEST_IMG $(($CLUSTER_SIZE)) 1` +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 second cluster on host ==" -printf "content: 0x%02x\n" `peek_file_le $TEST_IMG $(($CLUSTER_SIZE)) 1` +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 @@ -139,6 +182,23 @@ 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 diff --git a/tests/qemu-iotests/tests/parallels-checks.out b/tests/qemu-iotests/tests/parallels-checks.out index 98a3a7f55ef..97934231119 100644 --- a/tests/qemu-iotests/tests/parallels-checks.out +++ b/tests/qemu-iotests/tests/parallels-checks.out @@ -55,13 +55,52 @@ The following inconsistencies were found and repaired: 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) -== check first cluster on host == -content: 0x11 -== check second cluster on host == -content: 0x11 +== 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 == @@ -72,4 +111,22 @@ wrote 1048576/1048576 bytes at offset 0 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-encryption b/tests/qemu-iotests/tests/qcow2-encryption new file mode 100755 index 00000000000..95f6195ab86 --- /dev/null +++ b/tests/qemu-iotests/tests/qcow2-encryption @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# group: rw quick +# +# Test case for encryption support in qcow2 +# +# Copyright (C) 2025 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 +_require_working_luks + +IMG_SIZE=64M + +echo +echo "=== Create an encrypted image ===" +echo + +_make_test_img --object secret,id=sec0,data=123456 -o encrypt.format=luks,encrypt.key-secret=sec0 $IMG_SIZE +$PYTHON ../qcow2.py "$TEST_IMG" dump-header-exts +_img_info +$QEMU_IMG check \ + --object secret,id=sec0,data=123456 \ + --image-opts file.filename="$TEST_IMG",encrypt.key-secret=sec0 \ + | _filter_qemu_img_check + +echo +echo "=== Remove the header extension ===" +echo + +$PYTHON ../qcow2.py "$TEST_IMG" del-header-ext 0x0537be77 +$PYTHON ../qcow2.py "$TEST_IMG" dump-header-exts +_img_info +$QEMU_IMG check \ + --object secret,id=sec0,data=123456 \ + --image-opts file.filename="$TEST_IMG",encrypt.key-secret=sec0 2>&1 \ + | _filter_qemu_img_check \ + | _filter_testdir + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/qcow2-encryption.out b/tests/qemu-iotests/tests/qcow2-encryption.out new file mode 100644 index 00000000000..9b549dc2abb --- /dev/null +++ b/tests/qemu-iotests/tests/qcow2-encryption.out @@ -0,0 +1,32 @@ +QA output created by qcow2-encryption + +=== Create an encrypted image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Header extension: +magic 0x537be77 (Crypto header) +length 16 +data + +Header extension: +magic 0x6803f857 (Feature table) +length 384 +data + +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64 MiB (67108864 bytes) +encrypted: yes +cluster_size: 65536 +No errors were found on the image. + +=== Remove the header extension === + +Header extension: +magic 0x6803f857 (Feature table) +length 384 +data + +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Missing CRYPTO header for crypt method 2 +qemu-img: Could not open 'file.filename=TEST_DIR/t.qcow2,encrypt.key-secret=sec0': Missing CRYPTO header for crypt method 2 +*** done diff --git a/tests/qemu-iotests/tests/qcow2-internal-snapshots b/tests/qemu-iotests/tests/qcow2-internal-snapshots new file mode 100755 index 00000000000..9f83aa89039 --- /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 00000000000..fedb09224ea --- /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 e851f0320ec..74b81f703b0 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/qsd-jobs b/tests/qemu-iotests/tests/qsd-jobs index 510bf0a9dcb..9b843af6312 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 c1bc9b83562..aa6b6d1aefe 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/qsd-migrate b/tests/qemu-iotests/tests/qsd-migrate new file mode 100755 index 00000000000..a4c6592420c --- /dev/null +++ b/tests/qemu-iotests/tests/qsd-migrate @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# group: rw quick +# +# 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 . +# +# Creator/Owner: Kevin Wolf + +import iotests + +from iotests import filter_qemu_io, filter_qtest + +iotests.script_initialize(supported_fmts=['qcow2', 'qed', 'raw'], + supported_protocols=['file'], + supported_platforms=['linux']) + +with iotests.FilePath('disk.img') as path, \ + iotests.FilePath('nbd-src.sock', base_dir=iotests.sock_dir) as nbd_src, \ + iotests.FilePath('nbd-dst.sock', base_dir=iotests.sock_dir) as nbd_dst, \ + iotests.FilePath('migrate.sock', base_dir=iotests.sock_dir) as mig_sock, \ + iotests.VM(path_suffix="-src") as vm_src, \ + iotests.VM(path_suffix="-dst") as vm_dst: + + img_size = '10M' + + iotests.log('Preparing disk...') + iotests.qemu_img_create('-f', iotests.imgfmt, path, img_size) + + iotests.log('Launching source QSD...') + qsd_src = iotests.QemuStorageDaemon( + '--blockdev', f'file,node-name=disk-file,filename={path}', + '--blockdev', f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt', + '--nbd-server', f'addr.type=unix,addr.path={nbd_src}', + '--export', 'nbd,id=exp0,node-name=disk-fmt,writable=true,' + 'allow-inactive=true', + qmp=True, + ) + + iotests.log('Launching source VM...') + vm_src.add_args('-blockdev', f'nbd,node-name=disk,server.type=unix,' + f'server.path={nbd_src},export=disk-fmt') + vm_src.add_args('-device', 'virtio-blk,drive=disk,id=virtio0') + vm_src.launch() + + iotests.log('Launching destination QSD...') + qsd_dst = iotests.QemuStorageDaemon( + '--blockdev', f'file,node-name=disk-file,filename={path},active=off', + '--blockdev', f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt,' + f'active=off', + '--nbd-server', f'addr.type=unix,addr.path={nbd_dst}', + '--export', 'nbd,id=exp0,node-name=disk-fmt,writable=true,' + 'allow-inactive=true', + qmp=True, + instance_id='b', + ) + + iotests.log('Launching destination VM...') + vm_dst.add_args('-blockdev', f'nbd,node-name=disk,server.type=unix,' + f'server.path={nbd_dst},export=disk-fmt') + vm_dst.add_args('-device', 'virtio-blk,drive=disk,id=virtio0') + vm_dst.add_args('-incoming', f'unix:{mig_sock}') + vm_dst.launch() + + iotests.log('\nTest I/O on the source') + vm_src.hmp_qemu_io('virtio0/virtio-backend', 'write -P 0x11 0 4k', + use_log=True, qdev=True) + vm_src.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x11 0 4k', + use_log=True, qdev=True) + + iotests.log('\nStarting migration...') + + mig_caps = [ + {'capability': 'events', 'state': True}, + {'capability': 'pause-before-switchover', 'state': True}, + ] + vm_src.qmp_log('migrate-set-capabilities', capabilities=mig_caps) + vm_dst.qmp_log('migrate-set-capabilities', capabilities=mig_caps) + vm_src.qmp_log('migrate', uri=f'unix:{mig_sock}', + filters=[iotests.filter_qmp_testfiles]) + + vm_src.event_wait('MIGRATION', + match={'data': {'status': 'pre-switchover'}}) + + iotests.log('\nPre-switchover: Reconfigure QSD instances') + + iotests.log(qsd_src.qmp('blockdev-set-active', {'active': False})) + + # Reading is okay from both sides while the image is inactive. Note that + # the destination may have stale data until it activates the image, though. + vm_src.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x11 0 4k', + use_log=True, qdev=True) + vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'read 0 4k', + use_log=True, qdev=True) + + iotests.log(qsd_dst.qmp('blockdev-set-active', {'active': True})) + + iotests.log('\nCompleting migration...') + + vm_src.qmp_log('migrate-continue', state='pre-switchover') + vm_dst.event_wait('MIGRATION', match={'data': {'status': 'completed'}}) + + iotests.log('\nTest I/O on the destination') + + # Now the destination must see what the source wrote + vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x11 0 4k', + use_log=True, qdev=True) + + # And be able to overwrite it + vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'write -P 0x22 0 4k', + use_log=True, qdev=True) + vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x22 0 4k', + use_log=True, qdev=True) + + iotests.log('\nDone') + + vm_src.shutdown() + iotests.log('\n--- vm_src log ---') + log = vm_src.get_log() + if log: + iotests.log(log, [filter_qtest, filter_qemu_io]) + qsd_src.stop() + + vm_dst.shutdown() + iotests.log('\n--- vm_dst log ---') + log = vm_dst.get_log() + if log: + iotests.log(log, [filter_qtest, filter_qemu_io]) + qsd_dst.stop() diff --git a/tests/qemu-iotests/tests/qsd-migrate.out b/tests/qemu-iotests/tests/qsd-migrate.out new file mode 100644 index 00000000000..4a5241e5d40 --- /dev/null +++ b/tests/qemu-iotests/tests/qsd-migrate.out @@ -0,0 +1,59 @@ +Preparing disk... +Launching source QSD... +Launching source VM... +Launching destination QSD... +Launching destination VM... + +Test I/O on the source +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"write -P 0x11 0 4k\""}} +{"return": ""} +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x11 0 4k\""}} +{"return": ""} + +Starting migration... +{"execute": "migrate-set-capabilities", "arguments": {"capabilities": [{"capability": "events", "state": true}, {"capability": "pause-before-switchover", "state": true}]}} +{"return": {}} +{"execute": "migrate-set-capabilities", "arguments": {"capabilities": [{"capability": "events", "state": true}, {"capability": "pause-before-switchover", "state": true}]}} +{"return": {}} +{"execute": "migrate", "arguments": {"uri": "unix:SOCK_DIR/PID-migrate.sock"}} +{"return": {}} + +Pre-switchover: Reconfigure QSD instances +{"return": {}} +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x11 0 4k\""}} +{"return": ""} +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read 0 4k\""}} +{"return": ""} +{"return": {}} + +Completing migration... +{"execute": "migrate-continue", "arguments": {"state": "pre-switchover"}} +{"return": {}} + +Test I/O on the destination +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x11 0 4k\""}} +{"return": ""} +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"write -P 0x22 0 4k\""}} +{"return": ""} +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x22 0 4k\""}} +{"return": ""} + +Done + +--- vm_src log --- +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 0 +4 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) + +--- vm_dst log --- +read 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 0 +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 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/tests/reopen-file b/tests/qemu-iotests/tests/reopen-file index 8590a94d536..5a50794ffc4 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 5a8c3a9e8d2..b60aabb68ea 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 00000000000..546db1d3698 --- /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 00000000000..ae1213e6f86 --- /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 c24dfbcaa2f..1a50b682fc4 100755 --- a/tests/qemu-iotests/tests/stream-under-throttle +++ b/tests/qemu-iotests/tests/stream-under-throttle @@ -102,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 00000000000..acdc6ce8fff --- /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 00000000000..b6f257674e9 --- /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 00000000000..f90fb8e8d27 --- /dev/null +++ b/tests/qemu-iotests/tests/write-zeroes-unmap @@ -0,0 +1,128 @@ +#!/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 +_require_disk_usage + +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 00000000000..c9319948976 --- /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/qtest/acpi-utils.c b/tests/qtest/acpi-utils.c index 673fc975862..9dc24fbe5a0 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 00000000000..5ae82062342 --- /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 "qobject/qdict.h" +#include "qobject/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/adm1272-test.c b/tests/qtest/adm1272-test.c index 63f8514801b..2abda8d5be4 100644 --- a/tests/qtest/adm1272-test.c +++ b/tests/qtest/adm1272-test.c @@ -12,8 +12,8 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/bitops.h" #define TEST_ID "adm1272-test" diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c index eea8b5f77b5..e8aabfc13f5 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -30,7 +30,7 @@ #include "libqos/ahci.h" #include "libqos/pci-pc.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/host-utils.h" #include "hw/pci/pci_ids.h" @@ -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; @@ -1798,7 +1881,6 @@ static void test_io_interface(gconstpointer opaque) sector = offset_sector(opts->offset, opts->address_type, bufsize); test_io_rw_interface(opts->address_type, opts->io_type, bufsize, sector); g_free(opts); - return; } static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, @@ -1945,7 +2027,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 a8a4c668adb..eb8ddebffbf 100644 --- a/tests/qtest/arm-cpu-features.c +++ b/tests/qtest/arm-cpu-features.c @@ -11,8 +11,8 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" /* * We expect the SVE max-vq to be 16. Also it must be <= 64 @@ -79,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); \ }) @@ -194,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); } @@ -212,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); } @@ -418,21 +419,28 @@ 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_has_feature_disabled(qts, cpu_type, "pauth-qarma5"); 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_set_feature(qts, cpu_type, "pauth-qarma3", true); assert_set_feature(qts, cpu_type, "pauth-qarma3", false); + assert_set_feature(qts, cpu_type, "pauth-qarma5", true); + assert_set_feature(qts, cpu_type, "pauth-qarma5", false); assert_error(qts, cpu_type, - "cannot enable pauth-impdef or pauth-qarma3 without pauth", + "cannot enable pauth-impdef, pauth-qarma3 or pauth-qarma5 without pauth", "{ 'pauth': false, 'pauth-impdef': true }"); assert_error(qts, cpu_type, - "cannot enable pauth-impdef or pauth-qarma3 without pauth", + "cannot enable pauth-impdef, pauth-qarma3 or pauth-qarma5 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 }"); + "cannot enable pauth-impdef, pauth-qarma3 or pauth-qarma5 without pauth", + "{ 'pauth': false, 'pauth-qarma5': true }"); + assert_error(qts, cpu_type, + "cannot enable pauth-impdef, pauth-qarma3 and pauth-qarma5 at the same time", + "{ 'pauth': true, 'pauth-impdef': true, 'pauth-qarma3': true," + " 'pauth-qarma5': true }"); } static void test_query_cpu_model_expansion(const void *data) @@ -446,7 +454,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); @@ -508,6 +516,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]; @@ -536,11 +545,6 @@ static void test_query_cpu_model_expansion_kvm(const void *data) 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 @@ -550,11 +554,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); @@ -631,6 +642,10 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); + 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); diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c new file mode 100644 index 00000000000..0f7f911e5eb --- /dev/null +++ b/tests/qtest/aspeed-hace-utils.c @@ -0,0 +1,646 @@ +/* + * QTest testcase for the ASPEED Hash and Crypto Engine + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 IBM Corp. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" +#include "aspeed-hace-utils.h" + +/* + * Test vector is the ascii "abc" + * + * Expected results were generated using command line utitiles: + * + * echo -n -e 'abc' | dd of=/tmp/test + * for hash in sha512sum sha384sum sha256sum md5sum; do $hash /tmp/test; done + * + */ +static const uint8_t test_vector[3] = {0x61, 0x62, 0x63}; + +static const uint8_t test_result_sha512[64] = { + 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, + 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, + 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, + 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, + 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, + 0xa5, 0x4c, 0xa4, 0x9f}; + +static const uint8_t test_result_sha384[48] = { + 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, 0xb5, 0xa0, 0x3d, 0x69, + 0x9a, 0xc6, 0x50, 0x07, 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63, + 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed, 0x80, 0x86, 0x07, 0x2b, + 0xa1, 0xe7, 0xcc, 0x23, 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7}; + +static const uint8_t test_result_sha256[32] = { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, + 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; + +static const uint8_t test_result_md5[16] = { + 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, + 0x28, 0xe1, 0x7f, 0x72}; + +/* + * The Scatter-Gather Test vector is the ascii "abc" "def" "ghi", broken + * into blocks of 3 characters as shown + * + * Expected results were generated using command line utitiles: + * + * echo -n -e 'abcdefghijkl' | dd of=/tmp/test + * for hash in sha512sum sha384sum sha256sum; do $hash /tmp/test; done + * + */ +static const uint8_t test_vector_sg1[6] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; +static const uint8_t test_vector_sg2[3] = {0x67, 0x68, 0x69}; +static const uint8_t test_vector_sg3[3] = {0x6a, 0x6b, 0x6c}; + +static const uint8_t test_result_sg_sha512[64] = { + 0x17, 0x80, 0x7c, 0x72, 0x8e, 0xe3, 0xba, 0x35, 0xe7, 0xcf, 0x7a, 0xf8, + 0x23, 0x11, 0x6d, 0x26, 0xe4, 0x1e, 0x5d, 0x4d, 0x6c, 0x2f, 0xf1, 0xf3, + 0x72, 0x0d, 0x3d, 0x96, 0xaa, 0xcb, 0x6f, 0x69, 0xde, 0x64, 0x2e, 0x63, + 0xd5, 0xb7, 0x3f, 0xc3, 0x96, 0xc1, 0x2b, 0xe3, 0x8b, 0x2b, 0xd5, 0xd8, + 0x84, 0x25, 0x7c, 0x32, 0xc8, 0xf6, 0xd0, 0x85, 0x4a, 0xe6, 0xb5, 0x40, + 0xf8, 0x6d, 0xda, 0x2e}; + +static const uint8_t test_result_sg_sha384[48] = { + 0x10, 0x3c, 0xa9, 0x6c, 0x06, 0xa1, 0xce, 0x79, 0x8f, 0x08, 0xf8, 0xef, + 0xf0, 0xdf, 0xb0, 0xcc, 0xdb, 0x56, 0x7d, 0x48, 0xb2, 0x85, 0xb2, 0x3d, + 0x0c, 0xd7, 0x73, 0x45, 0x46, 0x67, 0xa3, 0xc2, 0xfa, 0x5f, 0x1b, 0x58, + 0xd9, 0xcd, 0xf2, 0x32, 0x9b, 0xd9, 0x97, 0x97, 0x30, 0xbf, 0xaa, 0xff}; + +static const uint8_t test_result_sg_sha256[32] = { + 0xd6, 0x82, 0xed, 0x4c, 0xa4, 0xd9, 0x89, 0xc1, 0x34, 0xec, 0x94, 0xf1, + 0x55, 0x1e, 0x1e, 0xc5, 0x80, 0xdd, 0x6d, 0x5a, 0x6e, 0xcd, 0xe9, 0xf3, + 0xd3, 0x5e, 0x6e, 0x4a, 0x71, 0x7f, 0xbd, 0xe4}; + +/* + * The accumulative mode requires firmware to provide internal initial state + * and message padding (including length L at the end of padding). + * + * This test vector is a ascii text "abc" with padding message. + * + * Expected results were generated using command line utitiles: + * + * echo -n -e 'abc' | dd of=/tmp/test + * for hash in sha512sum sha384sum sha256sum; do $hash /tmp/test; done + */ +static const uint8_t test_vector_accum_512[128] = { + 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; + +static const uint8_t test_vector_accum_384[128] = { + 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; + +static const uint8_t test_vector_accum_256[64] = { + 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; + +static const uint8_t test_result_accum_sha512[64] = { + 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, + 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, + 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, + 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, + 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, + 0xa5, 0x4c, 0xa4, 0x9f}; + +static const uint8_t test_result_accum_sha384[48] = { + 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, 0xb5, 0xa0, 0x3d, 0x69, + 0x9a, 0xc6, 0x50, 0x07, 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63, + 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed, 0x80, 0x86, 0x07, 0x2b, + 0xa1, 0xe7, 0xcc, 0x23, 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7}; + +static const uint8_t test_result_accum_sha256[32] = { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, + 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; + +static void write_regs(QTestState *s, uint32_t base, uint64_t src, + uint32_t length, uint64_t out, uint32_t method) +{ + qtest_writel(s, base + HACE_HASH_SRC, extract64(src, 0, 32)); + qtest_writel(s, base + HACE_HASH_SRC_HI, extract64(src, 32, 32)); + qtest_writel(s, base + HACE_HASH_DIGEST, extract64(out, 0, 32)); + qtest_writel(s, base + HACE_HASH_DIGEST_HI, extract64(out, 32, 32)); + qtest_writel(s, base + HACE_HASH_DATA_LEN, length); + qtest_writel(s, base + HACE_HASH_CMD, HACE_SHA_BE_EN | method); +} + +void aspeed_test_md5(const char *machine, const uint32_t base, + const uint64_t src_addr) + +{ + QTestState *s = qtest_init(machine); + + uint64_t digest_addr = src_addr + 0x010000; + uint8_t digest[16] = {0}; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); + + write_regs(s, base, src_addr, sizeof(test_vector), + digest_addr, HACE_ALGO_MD5); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_md5, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha256(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t digest_addr = src_addr + 0x10000; + uint8_t digest[32] = {0}; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); + + write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, + HACE_ALGO_SHA256); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sha256, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha384(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t digest_addr = src_addr + 0x10000; + uint8_t digest[48] = {0}; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); + + write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, + HACE_ALGO_SHA384); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sha384, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha512(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t digest_addr = src_addr + 0x10000; + uint8_t digest[64] = {0}; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); + + write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, + HACE_ALGO_SHA512); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sha512, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha256_sg(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t src_addr_1 = src_addr + 0x10000; + const uint64_t src_addr_2 = src_addr + 0x20000; + const uint64_t src_addr_3 = src_addr + 0x30000; + const uint64_t digest_addr = src_addr + 0x40000; + uint8_t digest[32] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_sg1)), + cpu_to_le32(src_addr_1) }, + { cpu_to_le32(sizeof(test_vector_sg2)), + cpu_to_le32(src_addr_2) }, + { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST), + cpu_to_le32(src_addr_3) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1)); + qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2)); + qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, + (sizeof(test_vector_sg1) + + sizeof(test_vector_sg2) + + sizeof(test_vector_sg3)), + digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sg_sha256, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha384_sg(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t src_addr_1 = src_addr + 0x10000; + const uint64_t src_addr_2 = src_addr + 0x20000; + const uint64_t src_addr_3 = src_addr + 0x30000; + const uint64_t digest_addr = src_addr + 0x40000; + uint8_t digest[48] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_sg1)), + cpu_to_le32(src_addr_1) }, + { cpu_to_le32(sizeof(test_vector_sg2)), + cpu_to_le32(src_addr_2) }, + { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST), + cpu_to_le32(src_addr_3) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1)); + qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2)); + qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, + (sizeof(test_vector_sg1) + + sizeof(test_vector_sg2) + + sizeof(test_vector_sg3)), + digest_addr, HACE_ALGO_SHA384 | HACE_SG_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sg_sha384, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha512_sg(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t src_addr_1 = src_addr + 0x10000; + const uint64_t src_addr_2 = src_addr + 0x20000; + const uint64_t src_addr_3 = src_addr + 0x30000; + const uint64_t digest_addr = src_addr + 0x40000; + uint8_t digest[64] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_sg1)), + cpu_to_le32(src_addr_1) }, + { cpu_to_le32(sizeof(test_vector_sg2)), + cpu_to_le32(src_addr_2) }, + { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST), + cpu_to_le32(src_addr_3) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1)); + qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2)); + qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, + (sizeof(test_vector_sg1) + + sizeof(test_vector_sg2) + + sizeof(test_vector_sg3)), + digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sg_sha512, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha256_accum(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t buffer_addr = src_addr + 0x10000; + const uint64_t digest_addr = src_addr + 0x40000; + uint8_t digest[32] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_accum_256) | SG_LIST_LEN_LAST), + cpu_to_le32(buffer_addr) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, buffer_addr, test_vector_accum_256, + sizeof(test_vector_accum_256)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, sizeof(test_vector_accum_256), + digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN | HACE_ACCUM_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_accum_sha256, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha384_accum(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t buffer_addr = src_addr + 0x10000; + const uint64_t digest_addr = src_addr + 0x40000; + uint8_t digest[48] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_accum_384) | SG_LIST_LEN_LAST), + cpu_to_le32(buffer_addr) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, buffer_addr, test_vector_accum_384, + sizeof(test_vector_accum_384)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, sizeof(test_vector_accum_384), + digest_addr, HACE_ALGO_SHA384 | HACE_SG_EN | HACE_ACCUM_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_accum_sha384, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha512_accum(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t buffer_addr = src_addr + 0x10000; + const uint64_t digest_addr = src_addr + 0x40000; + uint8_t digest[64] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_accum_512) | SG_LIST_LEN_LAST), + cpu_to_le32(buffer_addr) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, buffer_addr, test_vector_accum_512, + sizeof(test_vector_accum_512)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, sizeof(test_vector_accum_512), + digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN | HACE_ACCUM_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_accum_sha512, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_addresses(const char *machine, const uint32_t base, + const struct AspeedMasks *expected) +{ + QTestState *s = qtest_init(machine); + + /* + * Check command mode is zero, meaning engine is in direct access mode, + * as this affects the masking behavior of the HASH_SRC register. + */ + g_assert_cmphex(qtest_readl(s, base + HACE_CMD), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC_HI), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST_HI), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF_HI), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); + + /* Check that the address masking is correct */ + qtest_writel(s, base + HACE_HASH_SRC, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, expected->src); + + qtest_writel(s, base + HACE_HASH_SRC_HI, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC_HI), + ==, expected->src_hi); + + qtest_writel(s, base + HACE_HASH_DIGEST, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, + expected->dest); + + qtest_writel(s, base + HACE_HASH_DIGEST_HI, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST_HI), ==, + expected->dest_hi); + + qtest_writel(s, base + HACE_HASH_KEY_BUFF, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF), ==, + expected->key); + + qtest_writel(s, base + HACE_HASH_KEY_BUFF_HI, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF_HI), ==, + expected->key_hi); + + qtest_writel(s, base + HACE_HASH_DATA_LEN, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, + expected->len); + + /* Reset to zero */ + qtest_writel(s, base + HACE_HASH_SRC, 0); + qtest_writel(s, base + HACE_HASH_SRC_HI, 0); + qtest_writel(s, base + HACE_HASH_DIGEST, 0); + qtest_writel(s, base + HACE_HASH_DIGEST_HI, 0); + qtest_writel(s, base + HACE_HASH_KEY_BUFF, 0); + qtest_writel(s, base + HACE_HASH_KEY_BUFF_HI, 0); + qtest_writel(s, base + HACE_HASH_DATA_LEN, 0); + + /* Check that all bits are now zero */ + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC_HI), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST_HI), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF_HI), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); + + qtest_quit(s); +} + diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h new file mode 100644 index 00000000000..c8b2ec45af2 --- /dev/null +++ b/tests/qtest/aspeed-hace-utils.h @@ -0,0 +1,84 @@ +/* + * QTest testcase for the ASPEED Hash and Crypto Engine + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 IBM Corp. + */ + +#ifndef TESTS_ASPEED_HACE_UTILS_H +#define TESTS_ASPEED_HACE_UTILS_H + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" + +#define HACE_CMD 0x10 +#define HACE_SHA_BE_EN BIT(3) +#define HACE_MD5_LE_EN BIT(2) +#define HACE_ALGO_MD5 0 +#define HACE_ALGO_SHA1 BIT(5) +#define HACE_ALGO_SHA224 BIT(6) +#define HACE_ALGO_SHA256 (BIT(4) | BIT(6)) +#define HACE_ALGO_SHA512 (BIT(5) | BIT(6)) +#define HACE_ALGO_SHA384 (BIT(5) | BIT(6) | BIT(10)) +#define HACE_SG_EN BIT(18) +#define HACE_ACCUM_EN BIT(8) + +#define HACE_STS 0x1c +#define HACE_RSA_ISR BIT(13) +#define HACE_CRYPTO_ISR BIT(12) +#define HACE_HASH_ISR BIT(9) +#define HACE_RSA_BUSY BIT(2) +#define HACE_CRYPTO_BUSY BIT(1) +#define HACE_HASH_BUSY BIT(0) +#define HACE_HASH_SRC 0x20 +#define HACE_HASH_DIGEST 0x24 +#define HACE_HASH_KEY_BUFF 0x28 +#define HACE_HASH_DATA_LEN 0x2c +#define HACE_HASH_CMD 0x30 +#define HACE_HASH_SRC_HI 0x90 +#define HACE_HASH_DIGEST_HI 0x94 +#define HACE_HASH_KEY_BUFF_HI 0x98 + +/* Scatter-Gather Hash */ +#define SG_LIST_LEN_LAST BIT(31) +struct AspeedSgList { + uint32_t len; + uint32_t addr; +} __attribute__ ((__packed__)); + +struct AspeedMasks { + uint32_t src; + uint32_t dest; + uint32_t key; + uint32_t len; + uint32_t src_hi; + uint32_t dest_hi; + uint32_t key_hi; +}; + +void aspeed_test_md5(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha256(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha384(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha512(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha256_sg(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha384_sg(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha512_sg(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha256_accum(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha384_accum(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha512_accum(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_addresses(const char *machine, const uint32_t base, + const struct AspeedMasks *expected); + +#endif /* TESTS_ASPEED_HACE_UTILS_H */ + diff --git a/tests/qtest/aspeed-smc-utils.c b/tests/qtest/aspeed-smc-utils.c new file mode 100644 index 00000000000..c27d09e767e --- /dev/null +++ b/tests/qtest/aspeed-smc-utils.c @@ -0,0 +1,686 @@ +/* + * QTest testcase for the M25P80 Flash (Using the Aspeed SPI + * Controller) + * + * Copyright (C) 2016 IBM Corp. + * + * 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/bswap.h" +#include "libqtest-single.h" +#include "qemu/bitops.h" +#include "aspeed-smc-utils.h" + +/* + * Use an explicit bswap for the values read/wrote to the flash region + * as they are BE and the Aspeed CPU is LE. + */ +static inline uint32_t make_be32(uint32_t data) +{ + return bswap32(data); +} + +static inline void spi_writel(const AspeedSMCTestData *data, uint64_t offset, + uint32_t value) +{ + qtest_writel(data->s, data->spi_base + offset, value); +} + +static inline uint32_t spi_readl(const AspeedSMCTestData *data, uint64_t offset) +{ + return qtest_readl(data->s, data->spi_base + offset); +} + +static inline void flash_writeb(const AspeedSMCTestData *data, uint64_t offset, + uint8_t value) +{ + qtest_writeb(data->s, data->flash_base + offset, value); +} + +static inline void flash_writel(const AspeedSMCTestData *data, uint64_t offset, + uint32_t value) +{ + qtest_writel(data->s, data->flash_base + offset, value); +} + +static inline uint8_t flash_readb(const AspeedSMCTestData *data, + uint64_t offset) +{ + return qtest_readb(data->s, data->flash_base + offset); +} + +static inline uint32_t flash_readl(const AspeedSMCTestData *data, + uint64_t offset) +{ + return qtest_readl(data->s, data->flash_base + offset); +} + +static void spi_conf(const AspeedSMCTestData *data, uint32_t value) +{ + uint32_t conf = spi_readl(data, R_CONF); + + conf |= value; + spi_writel(data, R_CONF, conf); +} + +static void spi_conf_remove(const AspeedSMCTestData *data, uint32_t value) +{ + uint32_t conf = spi_readl(data, R_CONF); + + conf &= ~value; + spi_writel(data, R_CONF, conf); +} + +static void spi_ce_ctrl(const AspeedSMCTestData *data, uint32_t value) +{ + uint32_t conf = spi_readl(data, R_CE_CTRL); + + conf |= value; + spi_writel(data, R_CE_CTRL, conf); +} + +static void spi_ctrl_setmode(const AspeedSMCTestData *data, uint8_t mode, + uint8_t cmd) +{ + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); + ctrl &= ~(CTRL_USERMODE | 0xff << 16); + ctrl |= mode | (cmd << 16); + spi_writel(data, ctrl_reg, ctrl); +} + +static void spi_ctrl_start_user(const AspeedSMCTestData *data) +{ + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); + + ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; + spi_writel(data, ctrl_reg, ctrl); + + ctrl &= ~CTRL_CE_STOP_ACTIVE; + spi_writel(data, ctrl_reg, ctrl); +} + +static void spi_ctrl_stop_user(const AspeedSMCTestData *data) +{ + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); + + ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; + spi_writel(data, ctrl_reg, ctrl); +} + +static void spi_ctrl_set_io_mode(const AspeedSMCTestData *data, uint32_t value) +{ + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); + uint32_t mode; + + mode = value & CTRL_IO_MODE_MASK; + ctrl &= ~CTRL_IO_MODE_MASK; + ctrl |= mode; + spi_writel(data, ctrl_reg, ctrl); +} + +static void flash_reset(const AspeedSMCTestData *data) +{ + spi_conf(data, 1 << (CONF_ENABLE_W0 + data->cs)); + + spi_ctrl_start_user(data); + flash_writeb(data, 0, RESET_ENABLE); + flash_writeb(data, 0, RESET_MEMORY); + flash_writeb(data, 0, WREN); + flash_writeb(data, 0, BULK_ERASE); + flash_writeb(data, 0, WRDI); + spi_ctrl_stop_user(data); + + spi_conf_remove(data, 1 << (CONF_ENABLE_W0 + data->cs)); +} + +static void read_page(const AspeedSMCTestData *data, uint32_t addr, + uint32_t *page) +{ + int i; + + spi_ctrl_start_user(data); + + flash_writeb(data, 0, EN_4BYTE_ADDR); + flash_writeb(data, 0, READ); + flash_writel(data, 0, make_be32(addr)); + + /* Continuous read are supported */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, 0)); + } + spi_ctrl_stop_user(data); +} + +static void read_page_mem(const AspeedSMCTestData *data, uint32_t addr, + uint32_t *page) +{ + int i; + + /* move out USER mode to use direct reads from the AHB bus */ + spi_ctrl_setmode(data, CTRL_READMODE, READ); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, addr + i * 4)); + } +} + +static void write_page_mem(const AspeedSMCTestData *data, uint32_t addr, + uint32_t write_value) +{ + spi_ctrl_setmode(data, CTRL_WRITEMODE, PP); + + for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(data, addr + i * 4, write_value); + } +} + +static void assert_page_mem(const AspeedSMCTestData *data, uint32_t addr, + uint32_t expected_value) +{ + uint32_t page[FLASH_PAGE_SIZE / 4]; + read_page_mem(data, addr, page); + for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, expected_value); + } +} + +void aspeed_smc_test_read_jedec(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t jedec = 0x0; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, JEDEC_READ); + jedec |= flash_readb(test_data, 0) << 16; + jedec |= flash_readb(test_data, 0) << 8; + jedec |= flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + + flash_reset(test_data); + + g_assert_cmphex(jedec, ==, test_data->jedec_id); +} + +void aspeed_smc_test_erase_sector(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t some_page_addr = test_data->page_addr; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* + * Previous page should be full of 0xffs after backend is + * initialized + */ + read_page(test_data, some_page_addr - FLASH_PAGE_SIZE, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(some_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(some_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + + /* Check the page is correctly written */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, some_page_addr + i * 4); + } + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, ERASE_SECTOR); + flash_writel(test_data, 0, make_be32(some_page_addr)); + spi_ctrl_stop_user(test_data); + + /* Check the page is erased */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_erase_all(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t some_page_addr = test_data->page_addr; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* + * Previous page should be full of 0xffs after backend is + * initialized + */ + read_page(test_data, some_page_addr - FLASH_PAGE_SIZE, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(some_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(some_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + + /* Check the page is correctly written */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, some_page_addr + i * 4); + } + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, BULK_ERASE); + spi_ctrl_stop_user(test_data); + + /* Check the page is erased */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_write_page(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(my_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + + /* Check what was written */ + read_page(test_data, my_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + /* Check some other page. It should be full of 0xff */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_read_page_mem(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + /* + * Enable 4BYTE mode for controller. + */ + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); + + /* Enable 4BYTE mode for flash. */ + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(my_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + spi_conf_remove(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* Check what was written */ + read_page_mem(test_data, my_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + /* Check some other page. It should be full of 0xff */ + read_page_mem(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_write_page_mem(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + /* + * Enable 4BYTE mode for controller. + */ + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); + + /* Enable 4BYTE mode for flash. */ + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + spi_ctrl_stop_user(test_data); + + /* move out USER mode to use direct writes to the AHB bus */ + spi_ctrl_setmode(test_data, CTRL_WRITEMODE, PP); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, my_page_addr + i * 4, + make_be32(my_page_addr + i * 4)); + } + + /* Check what was written */ + read_page_mem(test_data, my_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_read_status_reg(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint8_t r; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + + g_assert_cmphex(r & SR_WEL, ==, 0); + g_assert(!qtest_qom_get_bool + (test_data->s, test_data->node, "write-enable")); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + + g_assert_cmphex(r & SR_WEL, ==, SR_WEL); + g_assert(qtest_qom_get_bool + (test_data->s, test_data->node, "write-enable")); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WRDI); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + + g_assert_cmphex(r & SR_WEL, ==, 0); + g_assert(!qtest_qom_get_bool + (test_data->s, test_data->node, "write-enable")); + + flash_reset(test_data); +} + +void aspeed_smc_test_status_reg_write_protection(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint8_t r; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* default case: WP# is high and SRWD is low -> status register writable */ + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + /* test ability to write SRWD */ + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, SRWD); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + g_assert_cmphex(r & SRWD, ==, SRWD); + + /* WP# high and SRWD high -> status register writable */ + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + /* test ability to write SRWD */ + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, 0); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + g_assert_cmphex(r & SRWD, ==, 0); + + /* WP# low and SRWD low -> status register writable */ + qtest_set_irq_in(test_data->s, test_data->node, "WP#", 0, 0); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + /* test ability to write SRWD */ + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, SRWD); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + g_assert_cmphex(r & SRWD, ==, SRWD); + + /* WP# low and SRWD high -> status register NOT writable */ + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0 , WREN); + /* test ability to write SRWD */ + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, 0); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + /* write is not successful */ + g_assert_cmphex(r & SRWD, ==, SRWD); + + qtest_set_irq_in(test_data->s, test_data->node, "WP#", 0, 1); + flash_reset(test_data); +} + +void aspeed_smc_test_write_block_protect(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t sector_size = 65536; + uint32_t n_sectors = 512; + + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + uint32_t bp_bits = 0b0; + + for (int i = 0; i < 16; i++) { + bp_bits = ((i & 0b1000) << 3) | ((i & 0b0111) << 2); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, BULK_ERASE); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, bp_bits); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + spi_ctrl_stop_user(test_data); + + uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; + uint32_t protection_start = n_sectors - num_protected_sectors; + uint32_t protection_end = n_sectors; + + for (int sector = 0; sector < n_sectors; sector++) { + uint32_t addr = sector * sector_size; + + assert_page_mem(test_data, addr, 0xffffffff); + write_page_mem(test_data, addr, make_be32(0xabcdef12)); + + uint32_t expected_value = protection_start <= sector + && sector < protection_end + ? 0xffffffff : 0xabcdef12; + + assert_page_mem(test_data, addr, expected_value); + } + } + + flash_reset(test_data); +} + +void aspeed_smc_test_write_block_protect_bottom_bit(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t sector_size = 65536; + uint32_t n_sectors = 512; + + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* top bottom bit is enabled */ + uint32_t bp_bits = 0b00100 << 3; + + for (int i = 0; i < 16; i++) { + bp_bits = (((i & 0b1000) | 0b0100) << 3) | ((i & 0b0111) << 2); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, BULK_ERASE); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, bp_bits); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + spi_ctrl_stop_user(test_data); + + uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; + uint32_t protection_start = 0; + uint32_t protection_end = num_protected_sectors; + + for (int sector = 0; sector < n_sectors; sector++) { + uint32_t addr = sector * sector_size; + + assert_page_mem(test_data, addr, 0xffffffff); + write_page_mem(test_data, addr, make_be32(0xabcdef12)); + + uint32_t expected_value = protection_start <= sector + && sector < protection_end + ? 0xffffffff : 0xabcdef12; + + assert_page_mem(test_data, addr, expected_value); + } + } + + flash_reset(test_data); +} + +void aspeed_smc_test_write_page_qpi(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; + uint32_t page[FLASH_PAGE_SIZE / 4]; + uint32_t page_pattern[] = { + 0xebd8c134, 0x5da196bc, 0xae15e729, 0x5085ccdf + }; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(my_page_addr)); + + /* Set QPI mode */ + spi_ctrl_set_io_mode(test_data, CTRL_IO_QUAD_IO); + + /* Fill the page pattern */ + for (i = 0; i < ARRAY_SIZE(page_pattern); i++) { + flash_writel(test_data, 0, make_be32(page_pattern[i])); + } + + /* Fill the page with its own addresses */ + for (; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); + } + + /* Restore io mode */ + spi_ctrl_set_io_mode(test_data, 0); + spi_ctrl_stop_user(test_data); + + /* Check what was written */ + read_page(test_data, my_page_addr, page); + for (i = 0; i < ARRAY_SIZE(page_pattern); i++) { + g_assert_cmphex(page[i], ==, page_pattern[i]); + } + for (; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + /* Check some other page. It should be full of 0xff */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + diff --git a/tests/qtest/aspeed-smc-utils.h b/tests/qtest/aspeed-smc-utils.h new file mode 100644 index 00000000000..b07870f3b8f --- /dev/null +++ b/tests/qtest/aspeed-smc-utils.h @@ -0,0 +1,95 @@ +/* + * QTest testcase for the M25P80 Flash (Using the Aspeed SPI + * Controller) + * + * Copyright (C) 2016 IBM Corp. + * + * 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 TESTS_ASPEED_SMC_UTILS_H +#define TESTS_ASPEED_SMC_UTILS_H + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "libqtest-single.h" +#include "qemu/bitops.h" + +/* + * ASPEED SPI Controller registers + */ +#define R_CONF 0x00 +#define CONF_ENABLE_W0 16 +#define R_CE_CTRL 0x04 +#define CRTL_EXTENDED0 0 /* 32 bit addressing for SPI */ +#define R_CTRL0 0x10 +#define CTRL_IO_QUAD_IO BIT(31) +#define CTRL_CE_STOP_ACTIVE BIT(2) +#define CTRL_READMODE 0x0 +#define CTRL_FREADMODE 0x1 +#define CTRL_WRITEMODE 0x2 +#define CTRL_USERMODE 0x3 +#define SR_WEL BIT(1) + +/* + * Flash commands + */ +enum { + JEDEC_READ = 0x9f, + RDSR = 0x5, + WRDI = 0x4, + BULK_ERASE = 0xc7, + READ = 0x03, + PP = 0x02, + WRSR = 0x1, + WREN = 0x6, + SRWD = 0x80, + RESET_ENABLE = 0x66, + RESET_MEMORY = 0x99, + EN_4BYTE_ADDR = 0xB7, + ERASE_SECTOR = 0xd8, +}; + +#define CTRL_IO_MODE_MASK (BIT(31) | BIT(30) | BIT(29) | BIT(28)) +#define FLASH_PAGE_SIZE 256 + +typedef struct AspeedSMCTestData { + QTestState *s; + uint64_t spi_base; + uint64_t flash_base; + uint32_t jedec_id; + char *tmp_path; + uint8_t cs; + const char *node; + uint32_t page_addr; +} AspeedSMCTestData; + +void aspeed_smc_test_read_jedec(const void *data); +void aspeed_smc_test_erase_sector(const void *data); +void aspeed_smc_test_erase_all(const void *data); +void aspeed_smc_test_write_page(const void *data); +void aspeed_smc_test_read_page_mem(const void *data); +void aspeed_smc_test_write_page_mem(const void *data); +void aspeed_smc_test_read_status_reg(const void *data); +void aspeed_smc_test_status_reg_write_protection(const void *data); +void aspeed_smc_test_write_block_protect(const void *data); +void aspeed_smc_test_write_block_protect_bottom_bit(const void *data); +void aspeed_smc_test_write_page_qpi(const void *data); + +#endif /* TESTS_ASPEED_SMC_UTILS_H */ diff --git a/tests/qtest/aspeed_fsi-test.c b/tests/qtest/aspeed_fsi-test.c new file mode 100644 index 00000000000..f5ab269972e --- /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_gpio-test.c b/tests/qtest/aspeed_gpio-test.c index d38f51d7190..12675d4cbba 100644 --- a/tests/qtest/aspeed_gpio-test.c +++ b/tests/qtest/aspeed_gpio-test.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "qemu/timer.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "libqtest-single.h" #define AST2600_GPIO_BASE 0x1E780000 diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c index ce86a44672e..38777020ca3 100644 --- a/tests/qtest/aspeed_hace-test.c +++ b/tests/qtest/aspeed_hace-test.c @@ -6,599 +6,222 @@ */ #include "qemu/osdep.h" - #include "libqtest.h" #include "qemu/bitops.h" +#include "aspeed-hace-utils.h" -#define HACE_CMD 0x10 -#define HACE_SHA_BE_EN BIT(3) -#define HACE_MD5_LE_EN BIT(2) -#define HACE_ALGO_MD5 0 -#define HACE_ALGO_SHA1 BIT(5) -#define HACE_ALGO_SHA224 BIT(6) -#define HACE_ALGO_SHA256 (BIT(4) | BIT(6)) -#define HACE_ALGO_SHA512 (BIT(5) | BIT(6)) -#define HACE_ALGO_SHA384 (BIT(5) | BIT(6) | BIT(10)) -#define HACE_SG_EN BIT(18) -#define HACE_ACCUM_EN BIT(8) - -#define HACE_STS 0x1c -#define HACE_RSA_ISR BIT(13) -#define HACE_CRYPTO_ISR BIT(12) -#define HACE_HASH_ISR BIT(9) -#define HACE_RSA_BUSY BIT(2) -#define HACE_CRYPTO_BUSY BIT(1) -#define HACE_HASH_BUSY BIT(0) -#define HACE_HASH_SRC 0x20 -#define HACE_HASH_DIGEST 0x24 -#define HACE_HASH_KEY_BUFF 0x28 -#define HACE_HASH_DATA_LEN 0x2c -#define HACE_HASH_CMD 0x30 -/* Scatter-Gather Hash */ -#define SG_LIST_LEN_LAST BIT(31) -struct AspeedSgList { - uint32_t len; - uint32_t addr; -} __attribute__ ((__packed__)); - -/* - * Test vector is the ascii "abc" - * - * Expected results were generated using command line utitiles: - * - * echo -n -e 'abc' | dd of=/tmp/test - * for hash in sha512sum sha256sum md5sum; do $hash /tmp/test; done - * - */ -static const uint8_t test_vector[] = {0x61, 0x62, 0x63}; - -static const uint8_t test_result_sha512[] = { - 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, - 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, - 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, - 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, - 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, - 0xa5, 0x4c, 0xa4, 0x9f}; +static const struct AspeedMasks ast1030_masks = { + .src = 0x7fffffff, + .dest = 0x7ffffff8, + .key = 0x7ffffff8, + .len = 0x0fffffff, +}; -static const uint8_t test_result_sha256[] = { - 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, - 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, - 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; +static const struct AspeedMasks ast2600_masks = { + .src = 0x7fffffff, + .dest = 0x7ffffff8, + .key = 0x7ffffff8, + .len = 0x0fffffff, +}; -static const uint8_t test_result_md5[] = { - 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, - 0x28, 0xe1, 0x7f, 0x72}; +static const struct AspeedMasks ast2500_masks = { + .src = 0x3fffffff, + .dest = 0x3ffffff8, + .key = 0x3fffffc0, + .len = 0x0fffffff, +}; -/* - * The Scatter-Gather Test vector is the ascii "abc" "def" "ghi", broken - * into blocks of 3 characters as shown - * - * Expected results were generated using command line utitiles: - * - * echo -n -e 'abcdefghijkl' | dd of=/tmp/test - * for hash in sha512sum sha256sum; do $hash /tmp/test; done - * - */ -static const uint8_t test_vector_sg1[] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; -static const uint8_t test_vector_sg2[] = {0x67, 0x68, 0x69}; -static const uint8_t test_vector_sg3[] = {0x6a, 0x6b, 0x6c}; - -static const uint8_t test_result_sg_sha512[] = { - 0x17, 0x80, 0x7c, 0x72, 0x8e, 0xe3, 0xba, 0x35, 0xe7, 0xcf, 0x7a, 0xf8, - 0x23, 0x11, 0x6d, 0x26, 0xe4, 0x1e, 0x5d, 0x4d, 0x6c, 0x2f, 0xf1, 0xf3, - 0x72, 0x0d, 0x3d, 0x96, 0xaa, 0xcb, 0x6f, 0x69, 0xde, 0x64, 0x2e, 0x63, - 0xd5, 0xb7, 0x3f, 0xc3, 0x96, 0xc1, 0x2b, 0xe3, 0x8b, 0x2b, 0xd5, 0xd8, - 0x84, 0x25, 0x7c, 0x32, 0xc8, 0xf6, 0xd0, 0x85, 0x4a, 0xe6, 0xb5, 0x40, - 0xf8, 0x6d, 0xda, 0x2e}; - -static const uint8_t test_result_sg_sha256[] = { - 0xd6, 0x82, 0xed, 0x4c, 0xa4, 0xd9, 0x89, 0xc1, 0x34, 0xec, 0x94, 0xf1, - 0x55, 0x1e, 0x1e, 0xc5, 0x80, 0xdd, 0x6d, 0x5a, 0x6e, 0xcd, 0xe9, 0xf3, - 0xd3, 0x5e, 0x6e, 0x4a, 0x71, 0x7f, 0xbd, 0xe4}; +static const struct AspeedMasks ast2400_masks = { + .src = 0x0fffffff, + .dest = 0x0ffffff8, + .key = 0x0fffffc0, + .len = 0x0fffffff, +}; -/* - * The accumulative mode requires firmware to provide internal initial state - * and message padding (including length L at the end of padding). - * - * This test vector is a ascii text "abc" with padding message. - * - * Expected results were generated using command line utitiles: - * - * echo -n -e 'abc' | dd of=/tmp/test - * for hash in sha512sum sha256sum; do $hash /tmp/test; done - */ -static const uint8_t test_vector_accum_512[] = { - 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; - -static const uint8_t test_vector_accum_256[] = { - 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; - -static const uint8_t test_result_accum_sha512[] = { - 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, - 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, - 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, - 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, - 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, - 0xa5, 0x4c, 0xa4, 0x9f}; - -static const uint8_t test_result_accum_sha256[] = { - 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, - 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, - 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; - -static void write_regs(QTestState *s, uint32_t base, uint32_t src, - uint32_t length, uint32_t out, uint32_t method) +/* ast1030 */ +static void test_md5_ast1030(void) { - qtest_writel(s, base + HACE_HASH_SRC, src); - qtest_writel(s, base + HACE_HASH_DIGEST, out); - qtest_writel(s, base + HACE_HASH_DATA_LEN, length); - qtest_writel(s, base + HACE_HASH_CMD, HACE_SHA_BE_EN | method); + aspeed_test_md5("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -static void test_md5(const char *machine, const uint32_t base, - const uint32_t src_addr) - +static void test_sha256_ast1030(void) { - QTestState *s = qtest_init(machine); - - uint32_t digest_addr = src_addr + 0x01000000; - uint8_t digest[16] = {0}; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); - - write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, HACE_ALGO_MD5); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_md5, sizeof(digest)); - - qtest_quit(s); + aspeed_test_sha256("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -static void test_sha256(const char *machine, const uint32_t base, - const uint32_t src_addr) +static void test_sha256_sg_ast1030(void) { - QTestState *s = qtest_init(machine); - - const uint32_t digest_addr = src_addr + 0x1000000; - uint8_t digest[32] = {0}; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); - - write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, HACE_ALGO_SHA256); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_sha256, sizeof(digest)); - - qtest_quit(s); + aspeed_test_sha256_sg("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -static void test_sha512(const char *machine, const uint32_t base, - const uint32_t src_addr) +static void test_sha384_ast1030(void) { - QTestState *s = qtest_init(machine); - - const uint32_t digest_addr = src_addr + 0x1000000; - uint8_t digest[64] = {0}; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); - - write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, HACE_ALGO_SHA512); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_sha512, sizeof(digest)); - - qtest_quit(s); + aspeed_test_sha384("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -static void test_sha256_sg(const char *machine, const uint32_t base, - const uint32_t src_addr) +static void test_sha384_sg_ast1030(void) { - QTestState *s = qtest_init(machine); - - const uint32_t src_addr_1 = src_addr + 0x1000000; - const uint32_t src_addr_2 = src_addr + 0x2000000; - const uint32_t src_addr_3 = src_addr + 0x3000000; - const uint32_t digest_addr = src_addr + 0x4000000; - uint8_t digest[32] = {0}; - struct AspeedSgList array[] = { - { cpu_to_le32(sizeof(test_vector_sg1)), - cpu_to_le32(src_addr_1) }, - { cpu_to_le32(sizeof(test_vector_sg2)), - cpu_to_le32(src_addr_2) }, - { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST), - cpu_to_le32(src_addr_3) }, - }; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1)); - qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2)); - qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3)); - qtest_memwrite(s, src_addr, array, sizeof(array)); - - write_regs(s, base, src_addr, - (sizeof(test_vector_sg1) - + sizeof(test_vector_sg2) - + sizeof(test_vector_sg3)), - digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_sg_sha256, sizeof(digest)); - - qtest_quit(s); + aspeed_test_sha384_sg("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -static void test_sha512_sg(const char *machine, const uint32_t base, - const uint32_t src_addr) +static void test_sha512_ast1030(void) { - QTestState *s = qtest_init(machine); - - const uint32_t src_addr_1 = src_addr + 0x1000000; - const uint32_t src_addr_2 = src_addr + 0x2000000; - const uint32_t src_addr_3 = src_addr + 0x3000000; - const uint32_t digest_addr = src_addr + 0x4000000; - uint8_t digest[64] = {0}; - struct AspeedSgList array[] = { - { cpu_to_le32(sizeof(test_vector_sg1)), - cpu_to_le32(src_addr_1) }, - { cpu_to_le32(sizeof(test_vector_sg2)), - cpu_to_le32(src_addr_2) }, - { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST), - cpu_to_le32(src_addr_3) }, - }; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1)); - qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2)); - qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3)); - qtest_memwrite(s, src_addr, array, sizeof(array)); - - write_regs(s, base, src_addr, - (sizeof(test_vector_sg1) - + sizeof(test_vector_sg2) - + sizeof(test_vector_sg3)), - digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_sg_sha512, sizeof(digest)); - - qtest_quit(s); + aspeed_test_sha512("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -static void test_sha256_accum(const char *machine, const uint32_t base, - const uint32_t src_addr) +static void test_sha512_sg_ast1030(void) { - QTestState *s = qtest_init(machine); - - const uint32_t buffer_addr = src_addr + 0x1000000; - const uint32_t digest_addr = src_addr + 0x4000000; - uint8_t digest[32] = {0}; - struct AspeedSgList array[] = { - { cpu_to_le32(sizeof(test_vector_accum_256) | SG_LIST_LEN_LAST), - cpu_to_le32(buffer_addr) }, - }; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, buffer_addr, test_vector_accum_256, - sizeof(test_vector_accum_256)); - qtest_memwrite(s, src_addr, array, sizeof(array)); - - write_regs(s, base, src_addr, sizeof(test_vector_accum_256), - digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN | HACE_ACCUM_EN); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_accum_sha256, sizeof(digest)); - - qtest_quit(s); + aspeed_test_sha512_sg("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -static void test_sha512_accum(const char *machine, const uint32_t base, - const uint32_t src_addr) +static void test_sha256_accum_ast1030(void) { - QTestState *s = qtest_init(machine); - - const uint32_t buffer_addr = src_addr + 0x1000000; - const uint32_t digest_addr = src_addr + 0x4000000; - uint8_t digest[64] = {0}; - struct AspeedSgList array[] = { - { cpu_to_le32(sizeof(test_vector_accum_512) | SG_LIST_LEN_LAST), - cpu_to_le32(buffer_addr) }, - }; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, buffer_addr, test_vector_accum_512, - sizeof(test_vector_accum_512)); - qtest_memwrite(s, src_addr, array, sizeof(array)); - - write_regs(s, base, src_addr, sizeof(test_vector_accum_512), - digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN | HACE_ACCUM_EN); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_accum_sha512, sizeof(digest)); - - qtest_quit(s); + aspeed_test_sha256_accum("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -struct masks { - uint32_t src; - uint32_t dest; - uint32_t len; -}; - -static const struct masks ast2600_masks = { - .src = 0x7fffffff, - .dest = 0x7ffffff8, - .len = 0x0fffffff, -}; - -static const struct masks ast2500_masks = { - .src = 0x3fffffff, - .dest = 0x3ffffff8, - .len = 0x0fffffff, -}; - -static const struct masks ast2400_masks = { - .src = 0x0fffffff, - .dest = 0x0ffffff8, - .len = 0x0fffffff, -}; - -static void test_addresses(const char *machine, const uint32_t base, - const struct masks *expected) +static void test_sha384_accum_ast1030(void) { - QTestState *s = qtest_init(machine); - - /* - * Check command mode is zero, meaning engine is in direct access mode, - * as this affects the masking behavior of the HASH_SRC register. - */ - g_assert_cmphex(qtest_readl(s, base + HACE_CMD), ==, 0); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); - - - /* Check that the address masking is correct */ - qtest_writel(s, base + HACE_HASH_SRC, 0xffffffff); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, expected->src); - - qtest_writel(s, base + HACE_HASH_DIGEST, 0xffffffff); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, expected->dest); - - qtest_writel(s, base + HACE_HASH_DATA_LEN, 0xffffffff); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, expected->len); - - /* Reset to zero */ - qtest_writel(s, base + HACE_HASH_SRC, 0); - qtest_writel(s, base + HACE_HASH_DIGEST, 0); - qtest_writel(s, base + HACE_HASH_DATA_LEN, 0); + aspeed_test_sha384_accum("-machine ast1030-evb", 0x7e6d0000, 0x00000000); +} - /* Check that all bits are now zero */ - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); +static void test_sha512_accum_ast1030(void) +{ + aspeed_test_sha512_accum("-machine ast1030-evb", 0x7e6d0000, 0x00000000); +} - qtest_quit(s); +static void test_addresses_ast1030(void) +{ + aspeed_test_addresses("-machine ast1030-evb", 0x7e6d0000, &ast1030_masks); } /* ast2600 */ static void test_md5_ast2600(void) { - test_md5("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_md5("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha256_ast2600(void) { - test_sha256("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha256("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha256_sg_ast2600(void) { - test_sha256_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha256_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); +} + +static void test_sha384_ast2600(void) +{ + aspeed_test_sha384("-machine ast2600-evb", 0x1e6d0000, 0x80000000); +} + +static void test_sha384_sg_ast2600(void) +{ + aspeed_test_sha384_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha512_ast2600(void) { - test_sha512("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha512("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha512_sg_ast2600(void) { - test_sha512_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha512_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha256_accum_ast2600(void) { - test_sha256_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha256_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); +} + +static void test_sha384_accum_ast2600(void) +{ + aspeed_test_sha384_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha512_accum_ast2600(void) { - test_sha512_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha512_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_addresses_ast2600(void) { - test_addresses("-machine ast2600-evb", 0x1e6d0000, &ast2600_masks); + aspeed_test_addresses("-machine ast2600-evb", 0x1e6d0000, &ast2600_masks); } /* ast2500 */ static void test_md5_ast2500(void) { - test_md5("-machine ast2500-evb", 0x1e6e3000, 0x80000000); + aspeed_test_md5("-machine ast2500-evb", 0x1e6e3000, 0x80000000); } static void test_sha256_ast2500(void) { - test_sha256("-machine ast2500-evb", 0x1e6e3000, 0x80000000); + aspeed_test_sha256("-machine ast2500-evb", 0x1e6e3000, 0x80000000); } static void test_sha512_ast2500(void) { - test_sha512("-machine ast2500-evb", 0x1e6e3000, 0x80000000); + aspeed_test_sha512("-machine ast2500-evb", 0x1e6e3000, 0x80000000); } static void test_addresses_ast2500(void) { - test_addresses("-machine ast2500-evb", 0x1e6e3000, &ast2500_masks); + aspeed_test_addresses("-machine ast2500-evb", 0x1e6e3000, &ast2500_masks); } /* ast2400 */ static void test_md5_ast2400(void) { - test_md5("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); + aspeed_test_md5("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); } static void test_sha256_ast2400(void) { - test_sha256("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); + aspeed_test_sha256("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); } static void test_sha512_ast2400(void) { - test_sha512("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); + aspeed_test_sha512("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); } static void test_addresses_ast2400(void) { - test_addresses("-machine palmetto-bmc", 0x1e6e3000, &ast2400_masks); + aspeed_test_addresses("-machine palmetto-bmc", 0x1e6e3000, &ast2400_masks); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); + qtest_add_func("ast1030/hace/addresses", test_addresses_ast1030); + qtest_add_func("ast1030/hace/sha512", test_sha512_ast1030); + qtest_add_func("ast1030/hace/sha384", test_sha384_ast1030); + qtest_add_func("ast1030/hace/sha256", test_sha256_ast1030); + qtest_add_func("ast1030/hace/md5", test_md5_ast1030); + + qtest_add_func("ast1030/hace/sha512_sg", test_sha512_sg_ast1030); + qtest_add_func("ast1030/hace/sha384_sg", test_sha384_sg_ast1030); + qtest_add_func("ast1030/hace/sha256_sg", test_sha256_sg_ast1030); + + qtest_add_func("ast1030/hace/sha512_accum", test_sha512_accum_ast1030); + qtest_add_func("ast1030/hace/sha384_accum", test_sha384_accum_ast1030); + qtest_add_func("ast1030/hace/sha256_accum", test_sha256_accum_ast1030); + qtest_add_func("ast2600/hace/addresses", test_addresses_ast2600); qtest_add_func("ast2600/hace/sha512", test_sha512_ast2600); + qtest_add_func("ast2600/hace/sha384", test_sha384_ast2600); qtest_add_func("ast2600/hace/sha256", test_sha256_ast2600); qtest_add_func("ast2600/hace/md5", test_md5_ast2600); qtest_add_func("ast2600/hace/sha512_sg", test_sha512_sg_ast2600); + qtest_add_func("ast2600/hace/sha384_sg", test_sha384_sg_ast2600); qtest_add_func("ast2600/hace/sha256_sg", test_sha256_sg_ast2600); qtest_add_func("ast2600/hace/sha512_accum", test_sha512_accum_ast2600); + qtest_add_func("ast2600/hace/sha384_accum", test_sha384_accum_ast2600); qtest_add_func("ast2600/hace/sha256_accum", test_sha256_accum_ast2600); qtest_add_func("ast2500/hace/addresses", test_addresses_ast2500); diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index c713a3700b6..52a00e6f0a7 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -27,623 +27,211 @@ #include "qemu/bswap.h" #include "libqtest-single.h" #include "qemu/bitops.h" +#include "aspeed-smc-utils.h" -/* - * ASPEED SPI Controller registers - */ -#define R_CONF 0x00 -#define CONF_ENABLE_W0 (1 << 16) -#define R_CE_CTRL 0x04 -#define CRTL_EXTENDED0 0 /* 32 bit addressing for SPI */ -#define R_CTRL0 0x10 -#define CTRL_CE_STOP_ACTIVE (1 << 2) -#define CTRL_READMODE 0x0 -#define CTRL_FREADMODE 0x1 -#define CTRL_WRITEMODE 0x2 -#define CTRL_USERMODE 0x3 -#define SR_WEL BIT(1) - -#define ASPEED_FMC_BASE 0x1E620000 -#define ASPEED_FLASH_BASE 0x20000000 - -/* - * Flash commands - */ -enum { - JEDEC_READ = 0x9f, - RDSR = 0x5, - WRDI = 0x4, - BULK_ERASE = 0xc7, - READ = 0x03, - PP = 0x02, - WRSR = 0x1, - WREN = 0x6, - SRWD = 0x80, - RESET_ENABLE = 0x66, - RESET_MEMORY = 0x99, - EN_4BYTE_ADDR = 0xB7, - ERASE_SECTOR = 0xd8, -}; - -#define FLASH_JEDEC 0x20ba19 /* n25q256a */ -#define FLASH_SIZE (32 * 1024 * 1024) - -#define FLASH_PAGE_SIZE 256 - -/* - * Use an explicit bswap for the values read/wrote to the flash region - * as they are BE and the Aspeed CPU is LE. - */ -static inline uint32_t make_be32(uint32_t data) +static void test_palmetto_bmc(AspeedSMCTestData *data) { - return bswap32(data); -} - -static void spi_conf(uint32_t value) -{ - uint32_t conf = readl(ASPEED_FMC_BASE + R_CONF); - - conf |= value; - writel(ASPEED_FMC_BASE + R_CONF, conf); -} - -static void spi_conf_remove(uint32_t value) -{ - uint32_t conf = readl(ASPEED_FMC_BASE + R_CONF); - - conf &= ~value; - writel(ASPEED_FMC_BASE + R_CONF, conf); -} - -static void spi_ce_ctrl(uint32_t value) -{ - uint32_t conf = readl(ASPEED_FMC_BASE + R_CE_CTRL); - - conf |= value; - writel(ASPEED_FMC_BASE + R_CE_CTRL, conf); -} - -static void spi_ctrl_setmode(uint8_t mode, uint8_t cmd) -{ - uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); - ctrl &= ~(CTRL_USERMODE | 0xff << 16); - ctrl |= mode | (cmd << 16); - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); -} - -static void spi_ctrl_start_user(void) -{ - uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); - - ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); - - ctrl &= ~CTRL_CE_STOP_ACTIVE; - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); -} - -static void spi_ctrl_stop_user(void) -{ - uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); - - ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); -} - -static void flash_reset(void) -{ - spi_conf(CONF_ENABLE_W0); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, RESET_ENABLE); - writeb(ASPEED_FLASH_BASE, RESET_MEMORY); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, BULK_ERASE); - writeb(ASPEED_FLASH_BASE, WRDI); - spi_ctrl_stop_user(); - - spi_conf_remove(CONF_ENABLE_W0); -} - -static void test_read_jedec(void) -{ - uint32_t jedec = 0x0; - - spi_conf(CONF_ENABLE_W0); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, JEDEC_READ); - jedec |= readb(ASPEED_FLASH_BASE) << 16; - jedec |= readb(ASPEED_FLASH_BASE) << 8; - jedec |= readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - - flash_reset(); - - g_assert_cmphex(jedec, ==, FLASH_JEDEC); -} - -static void read_page(uint32_t addr, uint32_t *page) -{ - int i; - - spi_ctrl_start_user(); - - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, READ); - writel(ASPEED_FLASH_BASE, make_be32(addr)); - - /* Continuous read are supported */ - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - page[i] = make_be32(readl(ASPEED_FLASH_BASE)); - } - spi_ctrl_stop_user(); -} - -static void read_page_mem(uint32_t addr, uint32_t *page) -{ - int i; - - /* move out USER mode to use direct reads from the AHB bus */ - spi_ctrl_setmode(CTRL_READMODE, READ); - - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - page[i] = make_be32(readl(ASPEED_FLASH_BASE + addr + i * 4)); - } -} - -static void write_page_mem(uint32_t addr, uint32_t write_value) -{ - spi_ctrl_setmode(CTRL_WRITEMODE, PP); - - for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE + addr + i * 4, write_value); - } -} - -static void assert_page_mem(uint32_t addr, uint32_t expected_value) -{ - uint32_t page[FLASH_PAGE_SIZE / 4]; - read_page_mem(addr, page); - for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, expected_value); - } -} - -static void test_erase_sector(void) -{ - uint32_t some_page_addr = 0x600 * FLASH_PAGE_SIZE; - uint32_t page[FLASH_PAGE_SIZE / 4]; - int i; - - spi_conf(CONF_ENABLE_W0); - - /* - * Previous page should be full of 0xffs after backend is - * initialized - */ - read_page(some_page_addr - FLASH_PAGE_SIZE, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, PP); - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); - - /* Fill the page with its own addresses */ - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr + i * 4)); - } - spi_ctrl_stop_user(); - - /* Check the page is correctly written */ - read_page(some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, some_page_addr + i * 4); - } - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, ERASE_SECTOR); - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); - spi_ctrl_stop_user(); - - /* Check the page is erased */ - read_page(some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(); -} - -static void test_erase_all(void) -{ - uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE; - uint32_t page[FLASH_PAGE_SIZE / 4]; - int i; - - spi_conf(CONF_ENABLE_W0); - - /* - * Previous page should be full of 0xffs after backend is - * initialized - */ - read_page(some_page_addr - FLASH_PAGE_SIZE, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, PP); - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); - - /* Fill the page with its own addresses */ - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr + i * 4)); - } - spi_ctrl_stop_user(); - - /* Check the page is correctly written */ - read_page(some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, some_page_addr + i * 4); - } - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, BULK_ERASE); - spi_ctrl_stop_user(); - - /* Check the page is erased */ - read_page(some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(); -} - -static void test_write_page(void) -{ - uint32_t my_page_addr = 0x14000 * FLASH_PAGE_SIZE; /* beyond 16MB */ - uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE; - uint32_t page[FLASH_PAGE_SIZE / 4]; - int i; - - spi_conf(CONF_ENABLE_W0); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, PP); - writel(ASPEED_FLASH_BASE, make_be32(my_page_addr)); - - /* Fill the page with its own addresses */ - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4)); - } - spi_ctrl_stop_user(); - - /* Check what was written */ - read_page(my_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, my_page_addr + i * 4); - } - - /* Check some other page. It should be full of 0xff */ - read_page(some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(); -} - -static void test_read_page_mem(void) -{ - uint32_t my_page_addr = 0x14000 * FLASH_PAGE_SIZE; /* beyond 16MB */ - uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE; - uint32_t page[FLASH_PAGE_SIZE / 4]; - int i; - - /* Enable 4BYTE mode for controller. This is should be strapped by - * HW for CE0 anyhow. - */ - spi_ce_ctrl(1 << CRTL_EXTENDED0); - - /* Enable 4BYTE mode for flash. */ - spi_conf(CONF_ENABLE_W0); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, PP); - writel(ASPEED_FLASH_BASE, make_be32(my_page_addr)); - - /* Fill the page with its own addresses */ - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4)); - } - spi_ctrl_stop_user(); - spi_conf_remove(CONF_ENABLE_W0); - - /* Check what was written */ - read_page_mem(my_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, my_page_addr + i * 4); - } - - /* Check some other page. It should be full of 0xff */ - read_page_mem(some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(); -} - -static void test_write_page_mem(void) -{ - uint32_t my_page_addr = 0x15000 * FLASH_PAGE_SIZE; - uint32_t page[FLASH_PAGE_SIZE / 4]; - int i; - - /* Enable 4BYTE mode for controller. This is should be strapped by - * HW for CE0 anyhow. - */ - spi_ce_ctrl(1 << CRTL_EXTENDED0); - - /* Enable 4BYTE mode for flash. */ - spi_conf(CONF_ENABLE_W0); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - spi_ctrl_stop_user(); - - /* move out USER mode to use direct writes to the AHB bus */ - spi_ctrl_setmode(CTRL_WRITEMODE, PP); - - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE + my_page_addr + i * 4, - make_be32(my_page_addr + i * 4)); - } - - /* Check what was written */ - read_page_mem(my_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, my_page_addr + i * 4); - } - - flash_reset(); -} - -static void test_read_status_reg(void) -{ - uint8_t r; - - spi_conf(CONF_ENABLE_W0); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - - g_assert_cmphex(r & SR_WEL, ==, 0); - g_assert(!qtest_qom_get_bool - (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - - g_assert_cmphex(r & SR_WEL, ==, SR_WEL); - g_assert(qtest_qom_get_bool - (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WRDI); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - - g_assert_cmphex(r & SR_WEL, ==, 0); - g_assert(!qtest_qom_get_bool - (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); + int ret; + int fd; - flash_reset(); -} + fd = g_file_open_tmp("qtest.m25p80.n25q256a.XXXXXX", &data->tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, 32 * 1024 * 1024); + g_assert(ret == 0); + close(fd); -static void test_status_reg_write_protection(void) + data->s = qtest_initf("-m 256 -machine palmetto-bmc " + "-drive file=%s,format=raw,if=mtd", + data->tmp_path); + + /* fmc cs0 with n25q256a flash */ + data->flash_base = 0x20000000; + data->spi_base = 0x1E620000; + data->jedec_id = 0x20ba19; + data->cs = 0; + data->node = "/machine/soc/fmc/ssi.0/child[0]"; + /* beyond 16MB */ + data->page_addr = 0x14000 * FLASH_PAGE_SIZE; + + qtest_add_data_func("/ast2400/smc/read_jedec", + data, aspeed_smc_test_read_jedec); + qtest_add_data_func("/ast2400/smc/erase_sector", + data, aspeed_smc_test_erase_sector); + qtest_add_data_func("/ast2400/smc/erase_all", + data, aspeed_smc_test_erase_all); + qtest_add_data_func("/ast2400/smc/write_page", + data, aspeed_smc_test_write_page); + qtest_add_data_func("/ast2400/smc/read_page_mem", + data, aspeed_smc_test_read_page_mem); + qtest_add_data_func("/ast2400/smc/write_page_mem", + data, aspeed_smc_test_write_page_mem); + qtest_add_data_func("/ast2400/smc/read_status_reg", + data, aspeed_smc_test_read_status_reg); + qtest_add_data_func("/ast2400/smc/status_reg_write_protection", + data, aspeed_smc_test_status_reg_write_protection); + qtest_add_data_func("/ast2400/smc/write_block_protect", + data, aspeed_smc_test_write_block_protect); + qtest_add_data_func("/ast2400/smc/write_block_protect_bottom_bit", + data, aspeed_smc_test_write_block_protect_bottom_bit); +} + +static void test_ast2500_evb(AspeedSMCTestData *data) { - uint8_t r; - - spi_conf(CONF_ENABLE_W0); - - /* default case: WP# is high and SRWD is low -> status register writable */ - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - /* test ability to write SRWD */ - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, SRWD); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - g_assert_cmphex(r & SRWD, ==, SRWD); - - /* WP# high and SRWD high -> status register writable */ - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - /* test ability to write SRWD */ - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, 0); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - g_assert_cmphex(r & SRWD, ==, 0); - - /* WP# low and SRWD low -> status register writable */ - qtest_set_irq_in(global_qtest, - "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 0); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - /* test ability to write SRWD */ - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, SRWD); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - g_assert_cmphex(r & SRWD, ==, SRWD); - - /* WP# low and SRWD high -> status register NOT writable */ - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - /* test ability to write SRWD */ - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, 0); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - /* write is not successful */ - g_assert_cmphex(r & SRWD, ==, SRWD); + int ret; + int fd; - qtest_set_irq_in(global_qtest, - "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 1); - flash_reset(); -} + fd = g_file_open_tmp("qtest.m25p80.mx25l25635e.XXXXXX", + &data->tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, 32 * 1024 * 1024); + g_assert(ret == 0); + close(fd); -static void test_write_block_protect(void) + data->s = qtest_initf("-machine ast2500-evb " + "-drive file=%s,format=raw,if=mtd", + data->tmp_path); + + /* fmc cs0 with mx25l25635e flash */ + data->flash_base = 0x20000000; + data->spi_base = 0x1E620000; + data->jedec_id = 0xc22019; + data->cs = 0; + data->node = "/machine/soc/fmc/ssi.0/child[0]"; + /* beyond 16MB */ + data->page_addr = 0x14000 * FLASH_PAGE_SIZE; + + qtest_add_data_func("/ast2500/smc/read_jedec", + data, aspeed_smc_test_read_jedec); + qtest_add_data_func("/ast2500/smc/erase_sector", + data, aspeed_smc_test_erase_sector); + qtest_add_data_func("/ast2500/smc/erase_all", + data, aspeed_smc_test_erase_all); + qtest_add_data_func("/ast2500/smc/write_page", + data, aspeed_smc_test_write_page); + qtest_add_data_func("/ast2500/smc/read_page_mem", + data, aspeed_smc_test_read_page_mem); + qtest_add_data_func("/ast2500/smc/write_page_mem", + data, aspeed_smc_test_write_page_mem); + qtest_add_data_func("/ast2500/smc/read_status_reg", + data, aspeed_smc_test_read_status_reg); + qtest_add_data_func("/ast2500/smc/write_page_qpi", + data, aspeed_smc_test_write_page_qpi); +} + +static void test_ast2600_evb(AspeedSMCTestData *data) { - uint32_t sector_size = 65536; - uint32_t n_sectors = 512; - - spi_ce_ctrl(1 << CRTL_EXTENDED0); - spi_conf(CONF_ENABLE_W0); - - uint32_t bp_bits = 0b0; - - for (int i = 0; i < 16; i++) { - bp_bits = ((i & 0b1000) << 3) | ((i & 0b0111) << 2); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, BULK_ERASE); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, bp_bits); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - spi_ctrl_stop_user(); - - uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; - uint32_t protection_start = n_sectors - num_protected_sectors; - uint32_t protection_end = n_sectors; - - for (int sector = 0; sector < n_sectors; sector++) { - uint32_t addr = sector * sector_size; - - assert_page_mem(addr, 0xffffffff); - write_page_mem(addr, make_be32(0xabcdef12)); - - uint32_t expected_value = protection_start <= sector - && sector < protection_end - ? 0xffffffff : 0xabcdef12; - - assert_page_mem(addr, expected_value); - } - } + int ret; + int fd; - flash_reset(); -} + fd = g_file_open_tmp("qtest.m25p80.mx66u51235f.XXXXXX", + &data->tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, 64 * 1024 * 1024); + g_assert(ret == 0); + close(fd); -static void test_write_block_protect_bottom_bit(void) + data->s = qtest_initf("-machine ast2600-evb " + "-drive file=%s,format=raw,if=mtd", + data->tmp_path); + + /* fmc cs0 with mx66u51235f flash */ + data->flash_base = 0x20000000; + data->spi_base = 0x1E620000; + data->jedec_id = 0xc2253a; + data->cs = 0; + data->node = "/machine/soc/fmc/ssi.0/child[0]"; + /* beyond 16MB */ + data->page_addr = 0x14000 * FLASH_PAGE_SIZE; + + qtest_add_data_func("/ast2600/smc/read_jedec", + data, aspeed_smc_test_read_jedec); + qtest_add_data_func("/ast2600/smc/erase_sector", + data, aspeed_smc_test_erase_sector); + qtest_add_data_func("/ast2600/smc/erase_all", + data, aspeed_smc_test_erase_all); + qtest_add_data_func("/ast2600/smc/write_page", + data, aspeed_smc_test_write_page); + qtest_add_data_func("/ast2600/smc/read_page_mem", + data, aspeed_smc_test_read_page_mem); + qtest_add_data_func("/ast2600/smc/write_page_mem", + data, aspeed_smc_test_write_page_mem); + qtest_add_data_func("/ast2600/smc/read_status_reg", + data, aspeed_smc_test_read_status_reg); + qtest_add_data_func("/ast2600/smc/write_page_qpi", + data, aspeed_smc_test_write_page_qpi); +} + +static void test_ast1030_evb(AspeedSMCTestData *data) { - uint32_t sector_size = 65536; - uint32_t n_sectors = 512; - - spi_ce_ctrl(1 << CRTL_EXTENDED0); - spi_conf(CONF_ENABLE_W0); - - /* top bottom bit is enabled */ - uint32_t bp_bits = 0b00100 << 3; - - for (int i = 0; i < 16; i++) { - bp_bits = (((i & 0b1000) | 0b0100) << 3) | ((i & 0b0111) << 2); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, BULK_ERASE); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, bp_bits); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - spi_ctrl_stop_user(); - - uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; - uint32_t protection_start = 0; - uint32_t protection_end = num_protected_sectors; - - for (int sector = 0; sector < n_sectors; sector++) { - uint32_t addr = sector * sector_size; - - assert_page_mem(addr, 0xffffffff); - write_page_mem(addr, make_be32(0xabcdef12)); - - uint32_t expected_value = protection_start <= sector - && sector < protection_end - ? 0xffffffff : 0xabcdef12; + int ret; + int fd; - assert_page_mem(addr, expected_value); - } - } + fd = g_file_open_tmp("qtest.m25p80.w25q80bl.XXXXXX", + &data->tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, 1 * 1024 * 1024); + g_assert(ret == 0); + close(fd); - flash_reset(); + data->s = qtest_initf("-machine ast1030-evb " + "-drive file=%s,format=raw,if=mtd", + data->tmp_path); + + /* fmc cs0 with w25q80bl flash */ + data->flash_base = 0x80000000; + data->spi_base = 0x7E620000; + data->jedec_id = 0xef4014; + data->cs = 0; + data->node = "/machine/soc/fmc/ssi.0/child[0]"; + /* beyond 512KB */ + data->page_addr = 0x800 * FLASH_PAGE_SIZE; + + qtest_add_data_func("/ast1030/smc/read_jedec", + data, aspeed_smc_test_read_jedec); + qtest_add_data_func("/ast1030/smc/erase_sector", + data, aspeed_smc_test_erase_sector); + qtest_add_data_func("/ast1030/smc/erase_all", + data, aspeed_smc_test_erase_all); + qtest_add_data_func("/ast1030/smc/write_page", + data, aspeed_smc_test_write_page); + qtest_add_data_func("/ast1030/smc/read_page_mem", + data, aspeed_smc_test_read_page_mem); + qtest_add_data_func("/ast1030/smc/write_page_mem", + data, aspeed_smc_test_write_page_mem); + qtest_add_data_func("/ast1030/smc/read_status_reg", + data, aspeed_smc_test_read_status_reg); + qtest_add_data_func("/ast1030/smc/write_page_qpi", + data, aspeed_smc_test_write_page_qpi); } int main(int argc, char **argv) { - g_autofree char *tmp_path = NULL; + AspeedSMCTestData palmetto_data; + AspeedSMCTestData ast2500_evb_data; + AspeedSMCTestData ast2600_evb_data; + AspeedSMCTestData ast1030_evb_data; int ret; - int fd; g_test_init(&argc, &argv, NULL); - fd = g_file_open_tmp("qtest.m25p80.XXXXXX", &tmp_path, NULL); - g_assert(fd >= 0); - ret = ftruncate(fd, FLASH_SIZE); - g_assert(ret == 0); - close(fd); - - global_qtest = qtest_initf("-m 256 -machine palmetto-bmc " - "-drive file=%s,format=raw,if=mtd", - tmp_path); - - qtest_add_func("/ast2400/smc/read_jedec", test_read_jedec); - qtest_add_func("/ast2400/smc/erase_sector", test_erase_sector); - qtest_add_func("/ast2400/smc/erase_all", test_erase_all); - qtest_add_func("/ast2400/smc/write_page", test_write_page); - qtest_add_func("/ast2400/smc/read_page_mem", test_read_page_mem); - qtest_add_func("/ast2400/smc/write_page_mem", test_write_page_mem); - qtest_add_func("/ast2400/smc/read_status_reg", test_read_status_reg); - qtest_add_func("/ast2400/smc/status_reg_write_protection", - test_status_reg_write_protection); - qtest_add_func("/ast2400/smc/write_block_protect", - test_write_block_protect); - qtest_add_func("/ast2400/smc/write_block_protect_bottom_bit", - test_write_block_protect_bottom_bit); - - flash_reset(); + test_palmetto_bmc(&palmetto_data); + test_ast2500_evb(&ast2500_evb_data); + test_ast2600_evb(&ast2600_evb_data); + test_ast1030_evb(&ast1030_evb_data); ret = g_test_run(); - qtest_quit(global_qtest); - unlink(tmp_path); + qtest_quit(palmetto_data.s); + qtest_quit(ast2500_evb_data.s); + qtest_quit(ast2600_evb_data.s); + qtest_quit(ast1030_evb_data.s); + unlink(palmetto_data.tmp_path); + unlink(ast2500_evb_data.tmp_path); + unlink(ast2600_evb_data.tmp_path); + unlink(ast1030_evb_data.tmp_path); + g_free(palmetto_data.tmp_path); + g_free(ast2500_evb_data.tmp_path); + g_free(ast2600_evb_data.tmp_path); + g_free(ast1030_evb_data.tmp_path); + return ret; } diff --git a/tests/qtest/ast2700-gpio-test.c b/tests/qtest/ast2700-gpio-test.c new file mode 100644 index 00000000000..eeae9bf11fc --- /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 "qobject/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/ast2700-hace-test.c b/tests/qtest/ast2700-hace-test.c new file mode 100644 index 00000000000..a400e2962b0 --- /dev/null +++ b/tests/qtest/ast2700-hace-test.c @@ -0,0 +1,98 @@ +/* + * QTest testcase for the ASPEED Hash and Crypto Engine + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2025 ASPEED Technology Inc. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" +#include "aspeed-hace-utils.h" + +static const struct AspeedMasks as2700_masks = { + .src = 0x7fffffff, + .dest = 0x7ffffff8, + .key = 0x7ffffff8, + .len = 0x0fffffff, + .src_hi = 0x00000003, + .dest_hi = 0x00000003, + .key_hi = 0x00000003, +}; + +/* ast2700 */ +static void test_md5_ast2700(void) +{ + aspeed_test_md5("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha256_ast2700(void) +{ + aspeed_test_sha256("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha256_sg_ast2700(void) +{ + aspeed_test_sha256_sg("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha384_ast2700(void) +{ + aspeed_test_sha384("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha384_sg_ast2700(void) +{ + aspeed_test_sha384_sg("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha512_ast2700(void) +{ + aspeed_test_sha512("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha512_sg_ast2700(void) +{ + aspeed_test_sha512_sg("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha256_accum_ast2700(void) +{ + aspeed_test_sha256_accum("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha384_accum_ast2700(void) +{ + aspeed_test_sha384_accum("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha512_accum_ast2700(void) +{ + aspeed_test_sha512_accum("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_addresses_ast2700(void) +{ + aspeed_test_addresses("-machine ast2700a1-evb", 0x12070000, &as2700_masks); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("ast2700/hace/addresses", test_addresses_ast2700); + qtest_add_func("ast2700/hace/sha512", test_sha512_ast2700); + qtest_add_func("ast2700/hace/sha384", test_sha384_ast2700); + qtest_add_func("ast2700/hace/sha256", test_sha256_ast2700); + qtest_add_func("ast2700/hace/md5", test_md5_ast2700); + + qtest_add_func("ast2700/hace/sha512_sg", test_sha512_sg_ast2700); + qtest_add_func("ast2700/hace/sha384_sg", test_sha384_sg_ast2700); + qtest_add_func("ast2700/hace/sha256_sg", test_sha256_sg_ast2700); + + qtest_add_func("ast2700/hace/sha512_accum", test_sha512_accum_ast2700); + qtest_add_func("ast2700/hace/sha384_accum", test_sha384_accum_ast2700); + qtest_add_func("ast2700/hace/sha256_accum", test_sha256_accum_ast2700); + + return g_test_run(); +} diff --git a/tests/qtest/ast2700-smc-test.c b/tests/qtest/ast2700-smc-test.c new file mode 100644 index 00000000000..62d538d8a3a --- /dev/null +++ b/tests/qtest/ast2700-smc-test.c @@ -0,0 +1,72 @@ +/* + * QTest testcase for the M25P80 Flash using the ASPEED SPI Controller since + * AST2700. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2024 ASPEED Technology Inc. + */ + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "libqtest-single.h" +#include "qemu/bitops.h" +#include "aspeed-smc-utils.h" + +static void test_ast2700_evb(AspeedSMCTestData *data) +{ + int ret; + int fd; + + fd = g_file_open_tmp("qtest.m25p80.w25q01jvq.XXXXXX", + &data->tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, 128 * 1024 * 1024); + g_assert(ret == 0); + close(fd); + + data->s = qtest_initf("-machine ast2700-evb " + "-drive file=%s,format=raw,if=mtd", + data->tmp_path); + + /* fmc cs0 with w25q01jvq flash */ + data->flash_base = 0x100000000; + data->spi_base = 0x14000000; + data->jedec_id = 0xef4021; + data->cs = 0; + data->node = "/machine/soc/fmc/ssi.0/child[0]"; + /* beyond 64MB */ + data->page_addr = 0x40000 * FLASH_PAGE_SIZE; + + qtest_add_data_func("/ast2700/smc/read_jedec", + data, aspeed_smc_test_read_jedec); + qtest_add_data_func("/ast2700/smc/erase_sector", + data, aspeed_smc_test_erase_sector); + qtest_add_data_func("/ast2700/smc/erase_all", + data, aspeed_smc_test_erase_all); + qtest_add_data_func("/ast2700/smc/write_page", + data, aspeed_smc_test_write_page); + qtest_add_data_func("/ast2700/smc/read_page_mem", + data, aspeed_smc_test_read_page_mem); + qtest_add_data_func("/ast2700/smc/write_page_mem", + data, aspeed_smc_test_write_page_mem); + qtest_add_data_func("/ast2700/smc/read_status_reg", + data, aspeed_smc_test_read_status_reg); + qtest_add_data_func("/ast2700/smc/write_page_qpi", + data, aspeed_smc_test_write_page_qpi); +} + +int main(int argc, char **argv) +{ + AspeedSMCTestData ast2700_evb_data; + int ret; + + g_test_init(&argc, &argv, NULL); + + test_ast2700_evb(&ast2700_evb_data); + ret = g_test_run(); + + qtest_quit(ast2700_evb_data.s); + unlink(ast2700_evb_data.tmp_path); + g_free(ast2700_evb_data.tmp_path); + return ret; +} diff --git a/tests/qtest/bcm2835-i2c-test.c b/tests/qtest/bcm2835-i2c-test.c new file mode 100644 index 00000000000..15991949260 --- /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 d1b80149f29..0b2bdf9d0d4 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -78,6 +78,7 @@ typedef struct { bool tcg_only; const char *machine; + const char *arch; const char *machine_param; const char *variant; const char *uefi_fl1; @@ -95,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; @@ -109,6 +113,7 @@ 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) { @@ -241,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) { @@ -274,6 +292,7 @@ static void dump_aml_files(test_data *data, bool rebuild) g_free(aml_file); } + free_test_data(&exp_data); } static bool create_tmp_asl(AcpiSdtTable *sdt) @@ -383,8 +402,8 @@ 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); + 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); } @@ -639,8 +658,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; @@ -662,6 +683,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)); @@ -670,6 +698,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); } } @@ -678,7 +724,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; @@ -704,6 +750,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") */ @@ -747,6 +794,8 @@ 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) @@ -813,16 +862,8 @@ static void test_vm_prepare(const char *params, test_data *data) g_free(args); } -static void process_acpi_tables_noexit(test_data *data) +static void process_smbios_tables_noexit(test_data *data) { - test_acpi_load_tables(data); - - if (getenv(ACPI_REBUILD_EXPECTED_AML)) { - dump_aml_files(data, true); - } else { - test_acpi_asl(data); - } - /* * TODO: make SMBIOS tests work with UEFI firmware, * Bug on uefi-test-tools to provide entry point: @@ -834,6 +875,27 @@ static void process_acpi_tables_noexit(test_data *data) } } +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); + } else { + test_acpi_asl(data); + } + + process_smbios_tables_noexit(data); +} + static void process_acpi_tables(test_data *data) { process_acpi_tables_noexit(data); @@ -858,6 +920,7 @@ static void test_acpi_piix4_tcg(void) * This is to make guest actually run. */ 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); @@ -869,6 +932,7 @@ static void test_acpi_piix4_tcg_bridge(void) test_data 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); @@ -896,7 +960,7 @@ static void test_acpi_piix4_tcg_bridge(void) free_test_data(&data); /* check that reboot/reset doesn't change any ACPI tables */ - qtest_qmp_send(data.qts, "{'execute':'system_reset' }"); + qtest_system_reset(data.qts); process_acpi_tables(&data); free_test_data(&data); } @@ -906,6 +970,7 @@ static void test_acpi_piix4_no_root_hotplug(void) test_data 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); @@ -922,6 +987,7 @@ static void test_acpi_piix4_no_bridge_hotplug(void) test_data 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); @@ -938,6 +1004,7 @@ static void test_acpi_piix4_no_acpi_pci_hotplug(void) test_data 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); @@ -959,6 +1026,7 @@ static void test_acpi_q35_tcg(void) test_data 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); @@ -970,18 +1038,92 @@ 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 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 275", &data); + 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); } @@ -990,6 +1132,7 @@ static void test_acpi_q35_tcg_bridge(void) test_data 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); @@ -1004,6 +1147,7 @@ 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); @@ -1032,6 +1176,7 @@ static void test_acpi_q35_multif_bridge(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".multi-bridge", }; test_vm_prepare("-S" @@ -1072,7 +1217,7 @@ static void test_acpi_q35_multif_bridge(void) free_test_data(&data); /* check that reboot/reset doesn't change any ACPI tables */ - qtest_qmp_send(data.qts, "{'execute':'system_reset' }"); + qtest_system_reset(data.qts); process_acpi_tables(&data); free_test_data(&data); } @@ -1081,12 +1226,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", @@ -1099,6 +1247,7 @@ static void test_acpi_piix4_tcg_cphp(void) test_data 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" @@ -1114,6 +1263,7 @@ static void test_acpi_q35_tcg_cphp(void) test_data 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" @@ -1133,6 +1283,7 @@ static void test_acpi_q35_tcg_ipmi(void) test_data 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); @@ -1147,6 +1298,7 @@ static void test_acpi_q35_tcg_smbus_ipmi(void) test_data 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); @@ -1164,6 +1316,7 @@ static void test_acpi_piix4_tcg_ipmi(void) * This is to make guest actually run. */ 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); @@ -1178,6 +1331,7 @@ static void test_acpi_q35_tcg_memhp(void) test_data 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" @@ -1193,6 +1347,7 @@ static void test_acpi_piix4_tcg_memhp(void) test_data 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" @@ -1208,6 +1363,7 @@ static void test_acpi_piix4_tcg_nosmm(void) test_data data = {}; data.machine = MACHINE_PC; + data.arch = "x86"; data.variant = ".nosmm"; test_acpi_one("-machine smm=off", &data); free_test_data(&data); @@ -1218,6 +1374,7 @@ static void test_acpi_piix4_tcg_smm_compat(void) test_data 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); @@ -1228,6 +1385,7 @@ static void test_acpi_piix4_tcg_smm_compat_nosmm(void) test_data 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); @@ -1238,6 +1396,7 @@ static void test_acpi_piix4_tcg_nohpet(void) test_data data = {}; data.machine = MACHINE_PC; + data.arch = "x86"; data.machine_param = ",hpet=off"; data.variant = ".nohpet"; test_acpi_one(NULL, &data); @@ -1249,6 +1408,7 @@ static void test_acpi_q35_tcg_numamem(void) test_data 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); @@ -1260,6 +1420,7 @@ static void test_acpi_q35_kvm_xapic(void) test_data 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" @@ -1272,6 +1433,7 @@ static void test_acpi_q35_tcg_nosmm(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".nosmm"; test_acpi_one("-machine smm=off", &data); free_test_data(&data); @@ -1282,6 +1444,7 @@ static void test_acpi_q35_tcg_smm_compat(void) test_data 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); @@ -1292,6 +1455,7 @@ static void test_acpi_q35_tcg_smm_compat_nosmm(void) test_data 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); @@ -1302,6 +1466,7 @@ static void test_acpi_q35_tcg_nohpet(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.machine_param = ",hpet=off"; data.variant = ".nohpet"; test_acpi_one(NULL, &data); @@ -1313,6 +1478,7 @@ static void test_acpi_q35_kvm_dmar(void) test_data 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); @@ -1324,6 +1490,7 @@ static void test_acpi_q35_tcg_ivrs(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".ivrs"; data.tcg_only = true, test_acpi_one(" -device amd-iommu", &data); @@ -1335,6 +1502,7 @@ static void test_acpi_piix4_tcg_numamem(void) test_data 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); @@ -1343,8 +1511,9 @@ 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); @@ -1371,6 +1540,7 @@ static void test_acpi_tcg_tpm(const char *machine, const char *tpm_if, tpm_emu_test_wait_cond(&test); data.machine = machine; + data.arch = arch; data.variant = variant; args = g_strdup_printf( @@ -1394,19 +1564,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 = {}; data.machine = machine; + data.arch = arch; data.variant = ".dimmpxm"; test_acpi_one(" -machine nvdimm=on,nvdimm-persistence=cpu" " -smp 4,sockets=4" @@ -1433,24 +1604,25 @@ 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", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 256ULL * 1024 * 1024, + .scan_len = 256ULL * MiB, }; data.variant = ".memhp"; @@ -1474,6 +1646,7 @@ static void test_acpi_virt_tcg_memhp(void) static void test_acpi_microvm_prepare(test_data *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"; @@ -1534,16 +1707,43 @@ 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 * MiB, + }; + + 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", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; data.variant = ".numamem"; @@ -1556,15 +1756,16 @@ 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", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; /* * While using -cdrom, the cdrom would auto plugged into pxb-pcie, @@ -1588,11 +1789,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 = {}; data.machine = machine; + data.arch = arch; data.variant = ".acpihmat"; test_acpi_one(" -machine hmat=on" " -smp 2,sockets=2" @@ -1621,24 +1823,25 @@ 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", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; data.variant = ".acpihmatvirt"; @@ -1691,6 +1894,7 @@ static void test_acpi_q35_tcg_acpi_hmat_noinitiator(void) test_data data = {}; data.machine = MACHINE_Q35; + data.arch = "x86"; data.variant = ".acpihmat-noinitiator"; test_acpi_one(" -machine hmat=on" " -smp 4,sockets=2" @@ -1733,14 +1937,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 = {}; data.machine = machine; + data.arch = arch; data.variant = ".acpierst"; params = g_strdup_printf( " -object memory-backend-file,id=erstnvram," @@ -1755,12 +2055,12 @@ 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) @@ -1785,16 +2085,39 @@ 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 * MiB, + }; + + /* + * 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", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; data.smbios_cpu_max_speed = 2900; @@ -1804,17 +2127,18 @@ static void test_acpi_virt_tcg(void) free_test_data(&data); } -static void test_acpi_virt_tcg_topology(void) +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, + .scan_len = 128ULL * MiB, }; test_acpi_one("-cpu cortex-a57 " @@ -1826,6 +2150,7 @@ static void test_acpi_q35_viot(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".viot", }; @@ -1850,6 +2175,7 @@ static void test_acpi_q35_cxl(void) test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".cxl", }; /* @@ -1887,16 +2213,17 @@ 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", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; test_acpi_one("-cpu cortex-a57 " @@ -1914,6 +2241,7 @@ static void test_acpi_q35_slic(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".slic", }; @@ -1928,6 +2256,7 @@ static void test_acpi_q35_applesmc(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".applesmc", }; @@ -1941,6 +2270,7 @@ static void test_acpi_q35_pvpanic_isa(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".pvpanic-isa", }; @@ -1948,6 +2278,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; @@ -1972,6 +2348,7 @@ static void test_acpi_piix4_oem_fields(void) test_data 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); @@ -1990,6 +2367,7 @@ static void test_acpi_q35_oem_fields(void) test_data 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); @@ -2019,16 +2397,17 @@ static void test_acpi_microvm_oem_fields(void) 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", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; char *args; @@ -2080,7 +2459,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", @@ -2088,12 +2466,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); @@ -2108,11 +2498,9 @@ int main(int argc, char *argv[]) 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", @@ -2120,10 +2508,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 @@ -2135,8 +2532,16 @@ 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); @@ -2167,18 +2572,27 @@ int main(int argc, char *argv[]) } } else if (strcmp(arch, "aarch64") == 0) { if (has_tcg && qtest_has_device("virtio-blk-pci")) { - qtest_add_func("acpi/virt", test_acpi_virt_tcg); + 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/topology", test_acpi_virt_tcg_topology); - qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem); - qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp); - qtest_add_func("acpi/virt/pxb", test_acpi_virt_tcg_pxb); - qtest_add_func("acpi/virt/oem-fields", test_acpi_virt_oem_fields); + 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_virt_viot); + 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(); boot_sector_cleanup(disk); diff --git a/tests/qtest/boot-order-test.c b/tests/qtest/boot-order-test.c index 8f2b6ef05a5..74d6b82dd20 100644 --- a/tests/qtest/boot-order-test.c +++ b/tests/qtest/boot-order-test.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "libqos/fw_cfg.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "standard-headers/linux/qemu_fw_cfg.h" typedef struct { @@ -31,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; } @@ -40,12 +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); - 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. - */ - qtest_qmp_eventwait(qts, "RESET"); + qtest_system_reset(qts); actual = read_boot_order(qts); g_assert_cmphex(actual, ==, expected_reboot); qtest_quit(qts); @@ -107,7 +102,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-serial-test.c b/tests/qtest/boot-serial-test.c index 6dd06aeaf47..a05d26ee996 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 */ @@ -62,18 +70,23 @@ static const uint8_t kernel_plml605[] = { }; static const uint8_t bios_raspi2[] = { - 0x08, 0x30, 0x9f, 0xe5, /* ldr r3,[pc,#8] Get base */ - 0x54, 0x20, 0xa0, 0xe3, /* mov r2,#'T' */ - 0x00, 0x20, 0xc3, 0xe5, /* strb r2,[r3] */ - 0xfb, 0xff, 0xff, 0xea, /* b loop */ - 0x00, 0x10, 0x20, 0x3f, /* 0x3f201000 = UART0 base addr */ + 0x10, 0x30, 0x9f, 0xe5, /* ldr r3, [pc, #16] Get &UART0 */ + 0x10, 0x20, 0x9f, 0xe5, /* ldr r2, [pc, #16] Get &CR */ + 0xb0, 0x23, 0xc3, 0xe1, /* strh r2, [r3, #48] Set CR */ + 0x54, 0x20, 0xa0, 0xe3, /* mov r2, #'T' */ + 0x00, 0x20, 0xc3, 0xe5, /* loop: strb r2, [r3] *TXDAT = 'T' */ + 0xff, 0xff, 0xff, 0xea, /* b -4 (loop) */ + 0x00, 0x10, 0x20, 0x3f, /* UART0: 0x3f201000 */ + 0x01, 0x01, 0x00, 0x00, /* CR: 0x101 = UARTEN|TXE */ }; static const uint8_t kernel_aarch64[] = { - 0x81, 0x0a, 0x80, 0x52, /* mov w1, #0x54 */ - 0x02, 0x20, 0xa1, 0xd2, /* mov x2, #0x9000000 */ - 0x41, 0x00, 0x00, 0x39, /* strb w1, [x2] */ - 0xfd, 0xff, 0xff, 0x17, /* b -12 (loop) */ + 0x02, 0x20, 0xa1, 0xd2, /* mov x2, #0x9000000 Load UART0 */ + 0x21, 0x20, 0x80, 0x52, /* mov w1, 0x101 CR = UARTEN|TXE */ + 0x41, 0x60, 0x00, 0x79, /* strh w1, [x2, #48] Set CR */ + 0x81, 0x0a, 0x80, 0x52, /* mov w1, #'T' */ + 0x41, 0x00, 0x00, 0x39, /* loop: strb w1, [x2] *TXDAT = 'T' */ + 0xff, 0xff, 0xff, 0x17, /* b -4 (loop) */ }; static const uint8_t kernel_nrf51[] = { @@ -156,7 +169,7 @@ static const 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 +180,8 @@ static const 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", @@ -174,8 +189,6 @@ static const testdef_t tests[] = { { "microblazeel", "petalogix-ml605", "", "TT", sizeof(kernel_plml605), kernel_plml605 }, { "arm", "raspi2b", "", "TT", sizeof(bios_raspi2), 0, bios_raspi2 }, - /* For hppa, force bios to output to serial by disabling graphics. */ - { "hppa", "hppa", "-vga none", "SeaBIOS wants SYSTEM HALT" }, { "aarch64", "virt", "-cpu max", "TT", sizeof(kernel_aarch64), kernel_aarch64 }, { "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 }, diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c index f2a8d919292..56e2d283a9d 100644 --- a/tests/qtest/cdrom-test.c +++ b/tests/qtest/cdrom-test.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "boot-sector.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" static char isoimage[] = "cdrom-boot-iso-XXXXXX"; @@ -37,17 +37,17 @@ static int exec_xorrisofs(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[] = { "xorrisofs", "-quiet", "-l", "-no-emul-boot", - "-b", NULL, "-o", isoimage, srcdir, NULL + "-b", NULL, "-o", isoimagepath, srcdir, NULL }; - ifh = mkstemp(isoimage); + ifh = mkstemp(isoimagepath); if (ifh < 0) { perror("Error creating temporary iso image file"); return -1; @@ -135,13 +135,35 @@ static void add_x86_tests(void) return; } - 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_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 @@ -150,35 +172,20 @@ 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); + } } } @@ -206,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 " @@ -271,6 +302,11 @@ int main(int argc, char **argv) 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 ad6a758289c..3b89bed97d9 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 2710cb17b86..cd0c6023617 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); + resp = qtest_qmp(qtest, "{'execute': 'system_reset'}"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + qtest_qmp_eventwait(qtest, "RESET"); +} + +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 * 80 + 1); - g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0); - g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 500); + 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 * 80); - g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 1); - g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 0); + 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(80); - g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000); + clock_step(tick); + 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); + 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(); } -static void test_clock_change(void) +/* + * 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,23 +165,231 @@ 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(); } -int main(int argc, char **argv) +/* Tests the counter is not running after reset. */ +static void test_watchdog_reset(const void *ptr) { - int r; + 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_test_init(&argc, &argv, NULL); + 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); - qtest_start("-machine lm3s811evb"); + /* Check defaults after reset */ + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); - qtest_add_func("/cmsdk-apb-watchdog/watchdog", test_watchdog); - qtest_add_func("/cmsdk-apb-watchdog/watchdog_clock_change", - test_clock_change); + /* 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); - r = g_test_run(); + 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) +{ + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + + 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); + } - return r; + return g_test_run(); } diff --git a/tests/qtest/cpu-plug-test.c b/tests/qtest/cpu-plug-test.c index 7f5dd5f85a7..44d704680b1 100644 --- a/tests/qtest/cpu-plug-test.c +++ b/tests/qtest/cpu-plug-test.c @@ -10,8 +10,8 @@ #include "qemu/osdep.h" #include "libqtest-single.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" struct PlugTestData { char *machine; @@ -156,6 +156,28 @@ static void add_s390x_test_case(const char *mname) g_free(path); } +static void add_loongarch_test_case(const char *mname) +{ + char *path; + PlugTestData *data; + + data = g_new(PlugTestData, 1); + data->machine = g_strdup(mname); + data->cpu_model = "la464"; + data->device_model = g_strdup("la464-loongarch-cpu"); + data->sockets = 1; + data->cores = 3; + data->threads = 1; + data->maxcpus = data->sockets * data->cores * data->threads; + + path = g_strdup_printf("cpu-plug/%s/device-add/%ux%ux%u&maxcpus=%u", + mname, data->sockets, data->cores, + data->threads, data->maxcpus); + qtest_add_data_func_full(path, data, test_plug_with_device_add, + test_data_free); + g_free(path); +} + int main(int argc, char **argv) { const char *arch = qtest_get_arch(); @@ -168,6 +190,8 @@ int main(int argc, char **argv) qtest_cb_for_every_machine(add_pseries_test_case, g_test_quick()); } else if (g_str_equal(arch, "s390x")) { qtest_cb_for_every_machine(add_s390x_test_case, g_test_quick()); + } else if (g_str_equal(arch, "loongarch64")) { + add_loongarch_test_case("virt"); } return g_test_run(); diff --git a/tests/qtest/dbus-display-test.c b/tests/qtest/dbus-display-test.c index 21edaa1e321..f7fc873bfb3 100644 --- a/tests/qtest/dbus-display-test.c +++ b/tests/qtest/dbus-display-test.c @@ -2,9 +2,14 @@ #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* @@ -82,6 +87,7 @@ typedef struct TestDBusConsoleRegister { GThread *thread; GDBusConnection *listener_conn; GDBusObjectManagerServer *server; + bool with_map; } TestDBusConsoleRegister; static gboolean listener_handle_scanout( @@ -94,13 +100,49 @@ static gboolean listener_handle_scanout( GVariant *arg_data, TestDBusConsoleRegister *test) { + 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; @@ -114,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); @@ -135,10 +196,17 @@ test_dbus_console_registered(GObject *source_object, 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 @@ -148,7 +216,7 @@ 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; @@ -156,7 +224,7 @@ test_dbus_display_console(void) g_autoptr(GMainLoop) loop = NULL; QTestState *qts = NULL; int pair[2]; - TestDBusConsoleRegister test; + TestDBusConsoleRegister test = { 0, .with_map = GPOINTER_TO_INT(data) }; #ifdef WIN32 WSAPROTOCOL_INFOW info; g_autoptr(GVariant) listener = NULL; @@ -245,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); @@ -256,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? */ @@ -287,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 5b0ffe43f5f..f84cec51dc2 100644 --- a/tests/qtest/device-introspect-test.c +++ b/tests/qtest/device-introspect-test.c @@ -18,9 +18,9 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "libqtest.h" const char common_args[] = "-nodefaults -machine none"; @@ -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 c6f33153eb4..2707ee59f63 100644 --- a/tests/qtest/device-plug-test.c +++ b/tests/qtest/device-plug-test.c @@ -12,17 +12,8 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" - -static void system_reset(QTestState *qtest) -{ - QDict *resp; - - resp = qtest_qmp(qtest, "{'execute': 'system_reset'}"); - g_assert(qdict_haskey(resp, "return")); - qobject_unref(resp); -} +#include "qobject/qdict.h" +#include "qobject/qstring.h" static void wait_device_deleted_event(QTestState *qtest, const char *id) { @@ -58,7 +49,7 @@ static void process_device_remove(QTestState *qtest, const char *id) * handled, removing the device. */ qtest_qmp_device_del_send(qtest, id); - system_reset(qtest); + qtest_system_reset_nowait(qtest); wait_device_deleted_event(qtest, id); } diff --git a/tests/qtest/dm163-test.c b/tests/qtest/dm163-test.c new file mode 100644 index 00000000000..4c8e654af29 --- /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 8a6f3ac963d..30d9451ddda 100644 --- a/tests/qtest/drive_del-test.c +++ b/tests/qtest/drive_del-test.c @@ -13,8 +13,8 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "libqos/virtio.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" static const char *qvirtio_get_dev_type(void); @@ -154,15 +154,10 @@ static void device_add(QTestState *qts) static void device_del(QTestState *qts, bool and_reset) { - QDict *response; - qtest_qmp_device_del_send(qts, "dev0"); if (and_reset) { - response = qtest_qmp(qts, "{'execute': 'system_reset' }"); - g_assert(response); - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); + qtest_system_reset_nowait(qts); } qtest_qmp_eventwait(qts, "DEVICE_DELETED"); @@ -173,7 +168,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); @@ -192,6 +187,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()); diff --git a/tests/qtest/emc141x-test.c b/tests/qtest/emc141x-test.c index 8c866940916..a24103e2cd8 100644 --- a/tests/qtest/emc141x-test.c +++ b/tests/qtest/emc141x-test.c @@ -10,7 +10,7 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/sensor/emc141x_regs.h" #define EMC1414_TEST_ID "emc1414-test" diff --git a/tests/qtest/erst-test.c b/tests/qtest/erst-test.c index c45bee7f054..36bbe122abf 100644 --- a/tests/qtest/erst-test.c +++ b/tests/qtest/erst-test.c @@ -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); } diff --git a/tests/qtest/es1370-test.c b/tests/qtest/es1370-test.c index 97ab65c4357..8387e74193b 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 5e8fbda9dff..1b37a8a4d25 100644 --- a/tests/qtest/fdc-test.c +++ b/tests/qtest/fdc-test.c @@ -26,7 +26,7 @@ #include "libqtest-single.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define DRIVE_FLOPPY_BLANK \ "-drive if=floppy,file=null-co://,file.read-zeroes=on,format=raw,size=1440k" @@ -552,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/fuzz.c b/tests/qtest/fuzz/fuzz.c index 3bedb81b32b..ca248a51a6c 100644 --- a/tests/qtest/fuzz/fuzz.c +++ b/tests/qtest/fuzz/fuzz.c @@ -17,9 +17,9 @@ #include "qemu/cutils.h" #include "qemu/datadir.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/qtest.h" +#include "system/runstate.h" #include "qemu/main-loop.h" #include "qemu/rcu.h" #include "tests/qtest/libqtest.h" @@ -41,6 +41,7 @@ static FuzzTargetList *fuzz_target_list; static FuzzTarget *fuzz_target; static QTestState *fuzz_qts; +int (*qemu_main)(void); void flush_events(QTestState *s) @@ -207,7 +208,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 21d1362d655..7da0bc3d7eb 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 */ diff --git a/tests/qtest/fuzz/generic_fuzz.c b/tests/qtest/fuzz/generic_fuzz.c index ec842e03c5e..f12080eda49 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 @@ -19,8 +20,8 @@ #include "tests/qtest/libqos/pci-pc.h" #include "fuzz.h" #include "string.h" -#include "exec/memory.h" -#include "exec/ramblock.h" +#include "system/memory.h" +#include "system/ramblock.h" #include "hw/qdev-core.h" #include "hw/pci/pci.h" #include "hw/pci/pci_device.h" @@ -211,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) { @@ -571,7 +572,6 @@ static void op_add_dma_pattern(QTestState *s, pattern p = {a.index, a.stride, len - sizeof(a), data + sizeof(a)}; p.index = a.index % p.len; g_array_append_val(dma_patterns, p); - return; } static void op_clear_dma_patterns(QTestState *s, diff --git a/tests/qtest/fuzz/generic_fuzz_configs.h b/tests/qtest/fuzz/generic_fuzz_configs.h index 50689da6539..ef0ad957127 100644 --- a/tests/qtest/fuzz/generic_fuzz_configs.h +++ b/tests/qtest/fuzz/generic_fuzz_configs.h @@ -106,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", @@ -148,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/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c index e403d373a00..9afe8bf6d8b 100644 --- a/tests/qtest/fuzz/qos_fuzz.c +++ b/tests/qtest/fuzz/qos_fuzz.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/main-loop.h" #include "tests/qtest/libqtest.h" @@ -46,7 +46,7 @@ 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); @@ -180,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/qtest_wrappers.c b/tests/qtest/fuzz/qtest_wrappers.c index 0580f8df860..d7adcbe3fd2 100644 --- a/tests/qtest/fuzz/qtest_wrappers.c +++ b/tests/qtest/fuzz/qtest_wrappers.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "hw/core/cpu.h" -#include "exec/ioport.h" +#include "system/ioport.h" #include "fuzz.h" diff --git a/tests/qtest/fw_cfg-test.c b/tests/qtest/fw_cfg-test.c index 5dc807ba238..e48b34afa59 100644 --- a/tests/qtest/fw_cfg-test.c +++ b/tests/qtest/fw_cfg-test.c @@ -243,12 +243,6 @@ int main(int argc, char **argv) qtest_add_func("fw_cfg/ram_size", test_fw_cfg_ram_size); qtest_add_func("fw_cfg/nographic", test_fw_cfg_nographic); qtest_add_func("fw_cfg/nb_cpus", test_fw_cfg_nb_cpus); -#if 0 - qtest_add_func("fw_cfg/machine_id", test_fw_cfg_machine_id); - qtest_add_func("fw_cfg/kernel", test_fw_cfg_kernel); - qtest_add_func("fw_cfg/initrd", test_fw_cfg_initrd); - qtest_add_func("fw_cfg/boot_device", test_fw_cfg_boot_device); -#endif qtest_add_func("fw_cfg/max_cpus", test_fw_cfg_max_cpus); qtest_add_func("fw_cfg/numa", test_fw_cfg_numa); qtest_add_func("fw_cfg/boot_menu", test_fw_cfg_boot_menu); diff --git a/tests/qtest/hd-geo-test.c b/tests/qtest/hd-geo-test.c index d08bffad91d..41481a5e09b 100644 --- a/tests/qtest/hd-geo-test.c +++ b/tests/qtest/hd-geo-test.c @@ -17,7 +17,7 @@ #include "qemu/osdep.h" #include "qemu/bswap.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "libqtest.h" #include "libqos/fw_cfg.h" #include "libqos/libqos.h" @@ -900,7 +900,6 @@ static void test_override_hot_unplug(TestArgs *args, const char *devid, QTestState *qts; char *joined_args; QFWCFG *fw_cfg; - QDict *response; int i; joined_args = g_strjoinv(" ", args->argv); @@ -913,13 +912,7 @@ static void test_override_hot_unplug(TestArgs *args, const char *devid, /* unplug device an restart */ qtest_qmp_device_del_send(qts, devid); - response = qtest_qmp(qts, - "{ 'execute': 'system_reset', 'arguments': { }}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - qtest_qmp_eventwait(qts, "RESET"); + qtest_system_reset(qts); read_bootdevices(fw_cfg, expected2); @@ -1074,17 +1067,26 @@ 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); @@ -1104,30 +1106,26 @@ int main(int argc, char **argv) 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); - } - + 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/ide-test.c b/tests/qtest/ide-test.c index d6b4f6e36a6..ceee444a9ec 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -29,12 +29,13 @@ #include "libqos/libqos.h" #include "libqos/pci-pc.h" #include "libqos/malloc-pc.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/bswap.h" #include "hw/pci/pci_ids.h" #include "hw/pci/pci_regs.h" -#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 @@ -88,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, @@ -560,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; @@ -1077,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/intel-hda-test.c b/tests/qtest/intel-hda-test.c index d4a8db6fd60..663bb6c4854 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/intel-iommu-test.c b/tests/qtest/intel-iommu-test.c new file mode 100644 index 00000000000..c521b3796eb --- /dev/null +++ b/tests/qtest/intel-iommu-test.c @@ -0,0 +1,64 @@ +/* + * QTest testcase for intel-iommu + * + * Copyright (c) 2024 Intel, Inc. + * + * Author: Zhenzhong Duan + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "hw/i386/intel_iommu_internal.h" + +#define CAP_STAGE_1_FIXED1 (VTD_CAP_FRO | VTD_CAP_NFR | VTD_CAP_ND | \ + VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SLLPS) +#define ECAP_STAGE_1_FIXED1 (VTD_ECAP_QI | VTD_ECAP_IR | VTD_ECAP_IRO | \ + VTD_ECAP_MHMV | VTD_ECAP_SMTS | VTD_ECAP_FLTS) + +static inline uint64_t vtd_reg_readq(QTestState *s, uint64_t offset) +{ + return qtest_readq(s, Q35_HOST_BRIDGE_IOMMU_ADDR + offset); +} + +static void test_intel_iommu_stage_1(void) +{ + uint8_t init_csr[DMAR_REG_SIZE]; /* register values */ + uint8_t post_reset_csr[DMAR_REG_SIZE]; /* register values */ + uint64_t cap, ecap, tmp; + QTestState *s; + + s = qtest_init("-M q35 -device intel-iommu,x-scalable-mode=on,x-flts=on"); + + cap = vtd_reg_readq(s, DMAR_CAP_REG); + g_assert((cap & CAP_STAGE_1_FIXED1) == CAP_STAGE_1_FIXED1); + + tmp = cap & VTD_CAP_SAGAW_MASK; + g_assert(tmp == (VTD_CAP_SAGAW_39bit | VTD_CAP_SAGAW_48bit)); + + tmp = VTD_MGAW_FROM_CAP(cap); + g_assert(tmp == VTD_HOST_AW_48BIT - 1); + + ecap = vtd_reg_readq(s, DMAR_ECAP_REG); + g_assert((ecap & ECAP_STAGE_1_FIXED1) == ECAP_STAGE_1_FIXED1); + + qtest_memread(s, Q35_HOST_BRIDGE_IOMMU_ADDR, init_csr, DMAR_REG_SIZE); + + qobject_unref(qtest_qmp(s, "{ 'execute': 'system_reset' }")); + qtest_qmp_eventwait(s, "RESET"); + + qtest_memread(s, Q35_HOST_BRIDGE_IOMMU_ADDR, post_reset_csr, DMAR_REG_SIZE); + /* Ensure registers are consistent after hard reset */ + g_assert(!memcmp(init_csr, post_reset_csr, DMAR_REG_SIZE)); + + qtest_quit(s); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + qtest_add_func("/q35/intel-iommu/stage-1", test_intel_iommu_stage_1); + + return g_test_run(); +} diff --git a/tests/qtest/ipmi-bt-test.c b/tests/qtest/ipmi-bt-test.c index ed431e34e68..637732fd5aa 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 afc24dd3e46..3186c6ad64b 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/isl_pmbus_vr-test.c b/tests/qtest/isl_pmbus_vr-test.c index 5553ea410ac..1ff840c6b74 100644 --- a/tests/qtest/isl_pmbus_vr-test.c +++ b/tests/qtest/isl_pmbus_vr-test.c @@ -21,8 +21,8 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/bitops.h" #define TEST_ID "isl_pmbus_vr-test" diff --git a/tests/qtest/ivshmem-test.c b/tests/qtest/ivshmem-test.c index 9bf8e78df67..fb45fdeb07b 100644 --- a/tests/qtest/ivshmem-test.c +++ b/tests/qtest/ivshmem-test.c @@ -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 a89cab03c3b..16fe546885a 100644 --- a/tests/qtest/libqmp.c +++ b/tests/qtest/libqmp.c @@ -25,8 +25,8 @@ #include "qemu/cutils.h" #include "qemu/sockets.h" #include "qapi/error.h" -#include "qapi/qmp/json-parser.h" -#include "qapi/qmp/qjson.h" +#include "qobject/json-parser.h" +#include "qobject/qjson.h" #define SOCKET_MAX_FDS 16 diff --git a/tests/qtest/libqmp.h b/tests/qtest/libqmp.h index 3445b753ff4..4a931c93ab2 100644 --- a/tests/qtest/libqmp.h +++ b/tests/qtest/libqmp.h @@ -18,7 +18,7 @@ #ifndef LIBQMP_H #define LIBQMP_H -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" QDict *qmp_fd_receive(int fd); #ifndef _WIN32 diff --git a/tests/qtest/libqos/ahci.c b/tests/qtest/libqos/ahci.c index a2c94c6e060..34a75b7f43b 100644 --- a/tests/qtest/libqos/ahci.c +++ b/tests/qtest/libqos/ahci.c @@ -662,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); @@ -1046,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, @@ -1109,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; diff --git a/tests/qtest/libqos/ahci.h b/tests/qtest/libqos/ahci.h index 48017864bfa..a0487a1557d 100644 --- a/tests/qtest/libqos/ahci.h +++ b/tests/qtest/libqos/ahci.h @@ -599,7 +599,6 @@ void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd); /* Misc */ bool is_atapi(AHCIQState *ahci, uint8_t port); -unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd); /* Command: Macro level execution */ void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, diff --git a/tests/qtest/libqos/arm-imx25-pdk-machine.c b/tests/qtest/libqos/arm-imx25-pdk-machine.c index 8fe128fae86..2d8b7543439 100644 --- a/tests/qtest/libqos/arm-imx25-pdk-machine.c +++ b/tests/qtest/libqos/arm-imx25-pdk-machine.c @@ -23,6 +23,7 @@ #include "libqos-malloc.h" #include "qgraph.h" #include "i2c.h" +#include "hw/i2c/imx_i2c.h" #define ARM_PAGE_SIZE 4096 #define IMX25_PDK_RAM_START 0x80000000 @@ -50,7 +51,7 @@ static void *imx25_pdk_get_driver(void *object, const char *interface) static QOSGraphObject *imx25_pdk_get_device(void *obj, const char *device) { QIMX25PDKMachine *machine = obj; - if (!g_strcmp0(device, "imx.i2c")) { + if (!g_strcmp0(device, TYPE_IMX_I2C)) { return &machine->i2c_1.obj; } @@ -86,7 +87,7 @@ static void imx25_pdk_register_nodes(void) .extra_device_opts = "bus=i2c-bus.0" }; qos_node_create_machine("arm/imx25-pdk", qos_create_machine_arm_imx25_pdk); - qos_node_contains("arm/imx25-pdk", "imx.i2c", &edge, NULL); + qos_node_contains("arm/imx25-pdk", TYPE_IMX_I2C, &edge, NULL); } libqos_init(imx25_pdk_register_nodes); diff --git a/tests/qtest/libqos/arm-n800-machine.c b/tests/qtest/libqos/arm-n800-machine.c deleted file mode 100644 index 4e5afe0164b..00000000000 --- 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/fw_cfg.c b/tests/qtest/libqos/fw_cfg.c index 89f053ccacb..0ab3959171b 100644 --- a/tests/qtest/libqos/fw_cfg.c +++ b/tests/qtest/libqos/fw_cfg.c @@ -14,6 +14,8 @@ #include "qemu/osdep.h" #include "fw_cfg.h" +#include "malloc-pc.h" +#include "libqos-malloc.h" #include "../libqtest.h" #include "qemu/bswap.h" #include "hw/nvram/fw_cfg.h" @@ -60,6 +62,91 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) qtest_writew(fw_cfg->qts, fw_cfg->base, key); } +static void qfw_cfg_dma_transfer(QFWCFG *fw_cfg, QOSState *qs, void *address, + uint32_t length, uint32_t control) +{ + FWCfgDmaAccess access; + uint32_t addr; + uint64_t guest_access_addr; + uint64_t gaddr; + + /* create a data buffer in guest memory */ + gaddr = guest_alloc(&qs->alloc, length); + + if (control & FW_CFG_DMA_CTL_WRITE) { + qtest_bufwrite(fw_cfg->qts, gaddr, address, length); + } + access.address = cpu_to_be64(gaddr); + access.length = cpu_to_be32(length); + access.control = cpu_to_be32(control); + + /* now create a separate buffer in guest memory for 'access' */ + guest_access_addr = guest_alloc(&qs->alloc, sizeof(access)); + qtest_bufwrite(fw_cfg->qts, guest_access_addr, &access, sizeof(access)); + + /* write lower 32 bits of address */ + addr = cpu_to_be32((uint32_t)(uintptr_t)guest_access_addr); + qtest_outl(fw_cfg->qts, fw_cfg->base + 8, addr); + + /* write upper 32 bits of address */ + addr = cpu_to_be32((uint32_t)(uintptr_t)(guest_access_addr >> 32)); + qtest_outl(fw_cfg->qts, fw_cfg->base + 4, addr); + + g_assert(!(be32_to_cpu(access.control) & FW_CFG_DMA_CTL_ERROR)); + + if (control & FW_CFG_DMA_CTL_READ) { + qtest_bufread(fw_cfg->qts, gaddr, address, length); + } + + guest_free(&qs->alloc, guest_access_addr); + guest_free(&qs->alloc, gaddr); +} + +static void qfw_cfg_write_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key, + void *buf, uint32_t len) +{ + qfw_cfg_select(fw_cfg, key); + qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_WRITE); +} + +static void qfw_cfg_read_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key, + void *buf, uint32_t len) +{ + qfw_cfg_select(fw_cfg, key); + qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_READ); +} + +static bool find_pdir_entry(QFWCFG *fw_cfg, const char *filename, + uint16_t *sel, uint32_t *size) +{ + g_autofree unsigned char *filesbuf = NULL; + uint32_t count; + size_t dsize; + FWCfgFile *pdir_entry; + uint32_t i; + bool found = false; + + *size = 0; + *sel = 0; + + qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count)); + count = be32_to_cpu(count); + dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file); + filesbuf = g_malloc(dsize); + qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize); + pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t)); + for (i = 0; i < count; ++i, ++pdir_entry) { + if (!strcmp(pdir_entry->name, filename)) { + *size = be32_to_cpu(pdir_entry->size); + *sel = be16_to_cpu(pdir_entry->select); + found = true; + break; + } + } + + return found; +} + /* * The caller need check the return value. When the return value is * nonzero, it means that some bytes have been transferred. @@ -73,37 +160,106 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) * populated, it has received only a starting slice of the fw_cfg file. */ size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, - void *data, size_t buflen) + void *data, size_t buflen) { - uint32_t count; - uint32_t i; - unsigned char *filesbuf = NULL; - size_t dsize; - FWCfgFile *pdir_entry; size_t filesize = 0; + uint32_t len; + uint16_t sel; - qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count)); - count = be32_to_cpu(count); - dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file); - filesbuf = g_malloc(dsize); - qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize); - pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t)); - for (i = 0; i < count; ++i, ++pdir_entry) { - if (!strcmp(pdir_entry->name, filename)) { - uint32_t len = be32_to_cpu(pdir_entry->size); - uint16_t sel = be16_to_cpu(pdir_entry->select); - filesize = len; - if (len > buflen) { - len = buflen; - } - qfw_cfg_get(fw_cfg, sel, data, len); - break; + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { + filesize = len; + if (len > buflen) { + len = buflen; } + qfw_cfg_get(fw_cfg, sel, data, len); } - g_free(filesbuf); + return filesize; } +/* + * The caller need check the return value. When the return value is + * nonzero, it means that some bytes have been transferred. + * + * If the fw_cfg file in question is smaller than the allocated & passed-in + * buffer, then the first len bytes were read. + * + * If the fw_cfg file in question is larger than the passed-in + * buffer, then the return value explains how much was actually read. + * + * It is illegal to call this function if fw_cfg does not support DMA + * interface. The caller should ensure that DMA is supported before + * calling this function. + * + * Passed QOSState pointer qs must be initialized. qs->alloc must also be + * properly initialized. + */ +size_t qfw_cfg_read_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen) +{ + uint32_t len = 0; + uint16_t sel; + uint32_t id; + + g_assert(qs); + g_assert(filename); + g_assert(data); + g_assert(buflen); + /* check if DMA is supported since we use DMA for read */ + id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); + g_assert(id & FW_CFG_VERSION_DMA); + + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { + if (len > buflen) { + len = buflen; + } + qfw_cfg_read_entry(fw_cfg, qs, sel, data, len); + } + + return len; +} + +/* + * The caller need check the return value. When the return value is + * nonzero, it means that some bytes have been transferred. + * + * If the fw_cfg file in question is smaller than the allocated & passed-in + * buffer, then the buffer has been partially written. + * + * If the fw_cfg file in question is larger than the passed-in + * buffer, then the return value explains how much was actually written. + * + * It is illegal to call this function if fw_cfg does not support DMA + * interface. The caller should ensure that DMA is supported before + * calling this function. + * + * Passed QOSState pointer qs must be initialized. qs->alloc must also be + * properly initialized. + */ +size_t qfw_cfg_write_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen) +{ + uint32_t len = 0; + uint16_t sel; + uint32_t id; + + g_assert(qs); + g_assert(filename); + g_assert(data); + g_assert(buflen); + /* write operation is only valid if DMA is supported */ + id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); + g_assert(id & FW_CFG_VERSION_DMA); + + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { + if (len > buflen) { + len = buflen; + } + qfw_cfg_write_entry(fw_cfg, qs, sel, data, len); + } + return len; +} + static void mm_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len) { uint8_t *ptr = data; diff --git a/tests/qtest/libqos/fw_cfg.h b/tests/qtest/libqos/fw_cfg.h index b0456a15df1..6d6ff09725d 100644 --- a/tests/qtest/libqos/fw_cfg.h +++ b/tests/qtest/libqos/fw_cfg.h @@ -14,6 +14,7 @@ #define LIBQOS_FW_CFG_H #include "../libqtest.h" +#include "libqos.h" typedef struct QFWCFG QFWCFG; @@ -33,7 +34,10 @@ uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key); uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key); size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, void *data, size_t buflen); - +size_t qfw_cfg_write_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen); +size_t qfw_cfg_read_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen); QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base); void mm_fw_cfg_uninit(QFWCFG *fw_cfg); QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base); diff --git a/tests/qtest/libqos/generic-pcihost.c b/tests/qtest/libqos/generic-pcihost.c index 3124b0e46b2..4bbeb5ff508 100644 --- a/tests/qtest/libqos/generic-pcihost.c +++ b/tests/qtest/libqos/generic-pcihost.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "../libqtest.h" #include "generic-pcihost.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/pci/pci_regs.h" #include "qemu/host-utils.h" diff --git a/tests/qtest/libqos/i2c-imx.c b/tests/qtest/libqos/i2c-imx.c index 710cb926d62..6d868e4cc4d 100644 --- a/tests/qtest/libqos/i2c-imx.c +++ b/tests/qtest/libqos/i2c-imx.c @@ -209,8 +209,8 @@ void imx_i2c_init(IMXI2C *s, QTestState *qts, uint64_t addr) static void imx_i2c_register_nodes(void) { - qos_node_create_driver("imx.i2c", NULL); - qos_node_produces("imx.i2c", "i2c-bus"); + qos_node_create_driver(TYPE_IMX_I2C, NULL); + qos_node_produces(TYPE_IMX_I2C, "i2c-bus"); } libqos_init(imx_i2c_register_nodes); diff --git a/tests/qtest/libqos/igb.c b/tests/qtest/libqos/igb.c index a603468beb8..ab3ef6f0c3b 100644 --- a/tests/qtest/libqos/igb.c +++ b/tests/qtest/libqos/igb.c @@ -104,10 +104,15 @@ static void igb_pci_start_hw(QOSGraphObject *obj) 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)); + ldl_le_p(address)); e1000e_macreg_write(&d->e1000e, E1000_RA + 4, E1000_RAH_AV | E1000_RAH_POOL_1 | - le16_to_cpu(*(uint16_t *)(address + 4))); + lduw_le_p(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); diff --git a/tests/qtest/libqos/libqos-malloc.c b/tests/qtest/libqos/libqos-malloc.c index d7566972c40..c90f8f03f4f 100644 --- a/tests/qtest/libqos/libqos-malloc.c +++ b/tests/qtest/libqos/libqos-malloc.c @@ -342,5 +342,4 @@ void migrate_allocator(QGuestAllocator *src, QTAILQ_INIT(src->free); node = mlist_new(src->start, src->end - src->start); QTAILQ_INSERT_HEAD(src->free, node, MLIST_ENTNAME); - return; } diff --git a/tests/qtest/libqos/libqos-spapr.h b/tests/qtest/libqos/libqos-spapr.h index e4483c14f80..a446276416c 100644 --- a/tests/qtest/libqos/libqos-spapr.h +++ b/tests/qtest/libqos/libqos-spapr.h @@ -9,11 +9,4 @@ 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 5c0fa1f7c57..9b49d0d4ddc 100644 --- a/tests/qtest/libqos/libqos.c +++ b/tests/qtest/libqos/libqos.c @@ -2,7 +2,7 @@ #include "../libqtest.h" #include "libqos.h" #include "pci.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" /*** Test Setup & Teardown ***/ @@ -117,13 +117,14 @@ void migrate(QOSState *from, QOSState *to, const char *uri) g_assert(qdict_haskey(sub, "status")); st = qdict_get_str(sub, "status"); - /* "setup", "active", "completed", "failed", "cancelled" */ + /* "setup", "active", "device", "completed", "failed", "cancelled" */ if (strcmp(st, "completed") == 0) { qobject_unref(rsp); break; } if ((strcmp(st, "setup") == 0) || (strcmp(st, "active") == 0) + || (strcmp(st, "device") == 0) || (strcmp(st, "wait-unplug") == 0)) { qobject_unref(rsp); g_usleep(5000); diff --git a/tests/qtest/libqos/loongarch-virt-machine.c b/tests/qtest/libqos/loongarch-virt-machine.c new file mode 100644 index 00000000000..c12089c015d --- /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 90aae42a225..1ddaf7b095b 100644 --- a/tests/qtest/libqos/meson.build +++ b/tests/qtest/libqos/meson.build @@ -32,7 +32,6 @@ libqos_srcs = files( 'i2c-omap.c', 'igb.c', 'sdhci.c', - 'tpci200.c', 'virtio.c', 'virtio-balloon.c', 'virtio-blk.c', @@ -52,7 +51,6 @@ libqos_srcs = files( # 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', @@ -60,14 +58,22 @@ 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 +if config_all_devices.has_key('CONFIG_TPCI200') + libqos_srcs += files('tpci200.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/pci-pc.c b/tests/qtest/libqos/pci-pc.c index 96046287ac6..147009f4f44 100644 --- a/tests/qtest/libqos/pci-pc.c +++ b/tests/qtest/libqos/pci-pc.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "../libqtest.h" #include "pci-pc.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/pci/pci_regs.h" #include "qemu/module.h" diff --git a/tests/qtest/libqos/pci.c b/tests/qtest/libqos/pci.c index b23d72346b6..a59197b9922 100644 --- a/tests/qtest/libqos/pci.c +++ b/tests/qtest/libqos/pci.c @@ -328,8 +328,6 @@ bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry) g_assert(dev->msix_enabled); pba_entry = qpci_io_readl(dev, dev->msix_pba_bar, dev->msix_pba_off + off); - qpci_io_writel(dev, dev->msix_pba_bar, dev->msix_pba_off + off, - pba_entry & ~(1 << bit_n)); return (pba_entry & (1 << bit_n)) != 0; } diff --git a/tests/qtest/libqos/qgraph.h b/tests/qtest/libqos/qgraph.h index 287022a67c1..81fbfdd0e27 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; @@ -355,7 +355,7 @@ void qos_object_start_hw(QOSGraphObject *obj); QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts); /** - * qos_machine_new(): instantiate a new driver node + * qos_driver_new(): instantiate a new driver node * @node: A driver node to be instantiated * @parent: A #QOSGraphObject to be consumed by the new driver node * @alloc: An allocator to be used by the new driver node. diff --git a/tests/qtest/libqos/qos_external.c b/tests/qtest/libqos/qos_external.c index c6bb8bff09e..493ab747de9 100644 --- a/tests/qtest/libqos/qos_external.c +++ b/tests/qtest/libqos/qos_external.c @@ -19,11 +19,11 @@ #include "qemu/osdep.h" #include #include "../libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qbool.h" +#include "qobject/qstring.h" #include "qemu/module.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "libqos-malloc.h" #include "qgraph.h" #include "qgraph_internal.h" diff --git a/tests/qtest/libqos/riscv-iommu.c b/tests/qtest/libqos/riscv-iommu.c new file mode 100644 index 00000000000..01e3b31c0b7 --- /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 00000000000..318db137999 --- /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 00000000000..c4364c9c5d0 --- /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 9e61dd49446..90efa028ef4 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 b8adc8d4b91..6ab4501c6e1 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" : ""; } @@ -556,6 +557,55 @@ void v9fs_rgetattr(P9Req *req, v9fs_attr *attr) v9fs_req_free(req); } +/* + * size[4] Tsetattr tag[2] fid[4] valid[4] mode[4] uid[4] gid[4] size[8] + * atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8] + */ +TSetAttrRes v9fs_tsetattr(TSetAttrOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + + req = v9fs_req_init( + opt.client, 4/*fid*/ + 4/*valid*/ + 4/*mode*/ + 4/*uid*/ + 4/*gid*/ + + 8/*size*/ + 8/*atime_sec*/ + 8/*atime_nsec*/ + 8/*mtime_sec*/ + + 8/*mtime_nsec*/, P9_TSETATTR, opt.tag + ); + v9fs_uint32_write(req, opt.fid); + v9fs_uint32_write(req, (uint32_t) opt.attr.valid); + v9fs_uint32_write(req, opt.attr.mode); + v9fs_uint32_write(req, opt.attr.uid); + v9fs_uint32_write(req, opt.attr.gid); + v9fs_uint64_write(req, opt.attr.size); + v9fs_uint64_write(req, opt.attr.atime_sec); + v9fs_uint64_write(req, opt.attr.atime_nsec); + v9fs_uint64_write(req, opt.attr.mtime_sec); + v9fs_uint64_write(req, opt.attr.mtime_nsec); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rsetattr(req); + } + req = NULL; /* request was freed */ + } + + return (TSetAttrRes) { .req = req }; +} + +/* size[4] Rsetattr tag[2] */ +void v9fs_rsetattr(P9Req *req) +{ + v9fs_req_recv(req, P9_RSETATTR); + v9fs_req_free(req); +} + /* size[4] Treaddir tag[2] fid[4] offset[8] count[4] */ TReadDirRes v9fs_treaddir(TReadDirOpt opt) { diff --git a/tests/qtest/libqos/virtio-9p-client.h b/tests/qtest/libqos/virtio-9p-client.h index 78228eb97d9..e3221a31041 100644 --- a/tests/qtest/libqos/virtio-9p-client.h +++ b/tests/qtest/libqos/virtio-9p-client.h @@ -65,6 +65,16 @@ typedef struct v9fs_attr { #define P9_GETATTR_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */ #define P9_GETATTR_ALL 0x00003fffULL /* Mask for ALL fields */ +#define P9_SETATTR_MODE 0x00000001UL +#define P9_SETATTR_UID 0x00000002UL +#define P9_SETATTR_GID 0x00000004UL +#define P9_SETATTR_SIZE 0x00000008UL +#define P9_SETATTR_ATIME 0x00000010UL +#define P9_SETATTR_MTIME 0x00000020UL +#define P9_SETATTR_CTIME 0x00000040UL +#define P9_SETATTR_ATIME_SET 0x00000080UL +#define P9_SETATTR_MTIME_SET 0x00000100UL + struct V9fsDirent { v9fs_qid qid; uint64_t offset; @@ -182,6 +192,28 @@ typedef struct TGetAttrRes { P9Req *req; } TGetAttrRes; +/* options for 'Tsetattr' 9p request */ +typedef struct TSetAttrOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* file ID of file/dir whose attributes shall be modified (required) */ + uint32_t fid; + /* new attribute values to be set by 9p server */ + v9fs_attr attr; + /* only send Tsetattr request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TSetAttrOpt; + +/* result of 'Tsetattr' 9p request */ +typedef struct TSetAttrRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TSetAttrRes; + /* options for 'Treaddir' 9p request */ typedef struct TReadDirOpt { /* 9P client being used (mandatory) */ @@ -470,6 +502,8 @@ TWalkRes v9fs_twalk(TWalkOpt opt); void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid); TGetAttrRes v9fs_tgetattr(TGetAttrOpt); void v9fs_rgetattr(P9Req *req, v9fs_attr *attr); +TSetAttrRes v9fs_tsetattr(TSetAttrOpt opt); +void v9fs_rsetattr(P9Req *req); TReadDirRes v9fs_treaddir(TReadDirOpt); void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries, struct V9fsDirent **entries); diff --git a/tests/qtest/libqos/virtio-pci-modern.c b/tests/qtest/libqos/virtio-pci-modern.c index 18d118866f0..4e67fcbd5d3 100644 --- a/tests/qtest/libqos/virtio-pci-modern.c +++ b/tests/qtest/libqos/virtio-pci-modern.c @@ -173,13 +173,11 @@ static bool get_config_isr_status(QVirtioDevice *d) static void wait_config_isr_status(QVirtioDevice *d, gint64 timeout_us) { - QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); gint64 start_time = g_get_monotonic_time(); - do { + while (!get_config_isr_status(d)) { g_assert(g_get_monotonic_time() - start_time <= timeout_us); - qtest_clock_step(dev->pdev->bus->qts, 100); - } while (!get_config_isr_status(d)); + } } static void queue_select(QVirtioDevice *d, uint16_t index) diff --git a/tests/qtest/libqos/virtio-pci.c b/tests/qtest/libqos/virtio-pci.c index 485b8f6b7e0..002bf8b8c2d 100644 --- a/tests/qtest/libqos/virtio-pci.c +++ b/tests/qtest/libqos/virtio-pci.c @@ -171,13 +171,11 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) static void qvirtio_pci_wait_config_isr_status(QVirtioDevice *d, gint64 timeout_us) { - QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); gint64 start_time = g_get_monotonic_time(); - do { + while (!qvirtio_pci_get_config_isr_status(d)) { g_assert(g_get_monotonic_time() - start_time <= timeout_us); - qtest_clock_step(dev->pdev->bus->qts, 100); - } while (!qvirtio_pci_get_config_isr_status(d)); + } } static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index) diff --git a/tests/qtest/libqos/virtio-scmi.c b/tests/qtest/libqos/virtio-scmi.c index ce8f4d5c06e..6b5bd4db42f 100644 --- a/tests/qtest/libqos/virtio-scmi.c +++ b/tests/qtest/libqos/virtio-scmi.c @@ -1,7 +1,7 @@ /* * virtio-scmi nodes for testing * - * SPDX-FileCopyrightText: Linaro Ltd + * Copyright (c) Linaro Ltd. * SPDX-FileCopyrightText: Red Hat, Inc. * SPDX-License-Identifier: GPL-2.0-or-later * diff --git a/tests/qtest/libqos/virtio.c b/tests/qtest/libqos/virtio.c index 410513225f4..5a709d0bc59 100644 --- a/tests/qtest/libqos/virtio.c +++ b/tests/qtest/libqos/virtio.c @@ -25,49 +25,63 @@ */ static uint16_t qvirtio_readw(QVirtioDevice *d, QTestState *qts, uint64_t addr) { - uint16_t val = qtest_readw(qts, addr); + uint16_t val; - if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { - val = bswap16(val); + if (d->features & (1ull << VIRTIO_F_VERSION_1)) { + qtest_memread(qts, addr, &val, sizeof(val)); + val = le16_to_cpu(val); + } else { + val = qtest_readw(qts, addr); } + return val; } static uint32_t qvirtio_readl(QVirtioDevice *d, QTestState *qts, uint64_t addr) { - uint32_t val = qtest_readl(qts, addr); + uint32_t val; - if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { - val = bswap32(val); + if (d->features & (1ull << VIRTIO_F_VERSION_1)) { + qtest_memread(qts, addr, &val, sizeof(val)); + val = le32_to_cpu(val); + } else { + val = qtest_readl(qts, addr); } + return val; } static void qvirtio_writew(QVirtioDevice *d, QTestState *qts, uint64_t addr, uint16_t val) { - if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { - val = bswap16(val); + if (d->features & (1ull << VIRTIO_F_VERSION_1)) { + val = cpu_to_le16(val); + qtest_memwrite(qts, addr, &val, sizeof(val)); + } else { + qtest_writew(qts, addr, val); } - qtest_writew(qts, addr, val); } static void qvirtio_writel(QVirtioDevice *d, QTestState *qts, uint64_t addr, uint32_t val) { - if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { - val = bswap32(val); + if (d->features & (1ull << VIRTIO_F_VERSION_1)) { + val = cpu_to_le32(val); + qtest_memwrite(qts, addr, &val, sizeof(val)); + } else { + qtest_writel(qts, addr, val); } - qtest_writel(qts, addr, val); } static void qvirtio_writeq(QVirtioDevice *d, QTestState *qts, uint64_t addr, uint64_t val) { - if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { - val = bswap64(val); + if (d->features & (1ull << VIRTIO_F_VERSION_1)) { + val = cpu_to_le64(val); + qtest_memwrite(qts, addr, &val, sizeof(val)); + } else { + qtest_writeq(qts, addr, val); } - qtest_writeq(qts, addr, val); } uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr) @@ -170,7 +184,6 @@ void qvirtio_wait_queue_isr(QTestState *qts, QVirtioDevice *d, gint64 start_time = g_get_monotonic_time(); for (;;) { - qtest_clock_step(qts, 100); if (d->bus->get_queue_isr_status(d, vq)) { return; } @@ -192,7 +205,6 @@ uint8_t qvirtio_wait_status_byte_no_isr(QTestState *qts, QVirtioDevice *d, uint8_t val; while ((val = qtest_readb(qts, addr)) == 0xff) { - qtest_clock_step(qts, 100); g_assert(!d->bus->get_queue_isr_status(d, vq)); g_assert(g_get_monotonic_time() - start_time <= timeout_us); } @@ -219,14 +231,12 @@ void qvirtio_wait_used_elem(QTestState *qts, QVirtioDevice *d, for (;;) { uint32_t got_desc_idx; - qtest_clock_step(qts, 100); if (d->bus->get_queue_isr_status(d, vq) && qvirtqueue_get_buf(qts, vq, &got_desc_idx, len)) { g_assert_cmpint(got_desc_idx, ==, desc_idx); return; } - g_assert(g_get_monotonic_time() - start_time <= timeout_us); } } @@ -265,7 +275,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 +290,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 +404,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 34b9c14b75b..94526b7f9cc 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -24,16 +24,21 @@ #ifdef __linux__ #include #endif /* __linux__ */ +#ifdef __FreeBSD__ +#include +#endif /* __FreeBSD__ */ #include "libqtest.h" #include "libqmp.h" +#include "qemu/accel.h" #include "qemu/ctype.h" #include "qemu/cutils.h" #include "qemu/sockets.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" +#include "qobject/qbool.h" #define MAX_IRQ 256 @@ -71,6 +76,8 @@ struct QTestState { int fd; int qmp_fd; + int sock; + int qmpsock; pid_t qemu_pid; /* our child QEMU process */ int wstatus; #ifdef _WIN32 @@ -88,6 +95,7 @@ struct QTestState static GHookList abrt_hooks; static void (*sighandler_old)(int); +static bool silence_spawn_log; static int qtest_query_target_endianness(QTestState *s); @@ -210,6 +218,22 @@ static void qtest_check_status(QTestState *s) #endif } +void qtest_system_reset_nowait(QTestState *s) +{ + /* Request the system reset, but do not wait for it to complete */ + qtest_qmp_assert_success(s, "{'execute': 'system_reset' }"); +} + +void qtest_system_reset(QTestState *s) +{ + qtest_system_reset_nowait(s); + /* + * Wait for the RESET event, which is sent once the system reset + * has actually completed. + */ + qtest_qmp_eventwait(s, "RESET"); +} + void qtest_wait_qemu(QTestState *s) { if (s->qemu_pid != -1) { @@ -333,10 +357,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"); @@ -378,7 +409,8 @@ static pid_t qtest_create_process(char *cmd) } #endif /* _WIN32 */ -static QTestState *G_GNUC_PRINTF(1, 2) qtest_spawn_qemu(const char *fmt, ...) +static QTestState *G_GNUC_PRINTF(2, 3) qtest_spawn_qemu(const char *qemu_bin, + const char *fmt, ...) { va_list ap; QTestState *s = g_new0(QTestState, 1); @@ -388,14 +420,15 @@ static QTestState *G_GNUC_PRINTF(1, 2) qtest_spawn_qemu(const char *fmt, ...) g_autoptr(GString) command = g_string_new(""); va_start(ap, fmt); - g_string_append_printf(command, CMD_EXEC "%s %s", - qtest_qemu_binary(), tracearg); + 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); - g_test_message("starting QEMU: %s", command->str); + if (!silence_spawn_log) { + g_test_message("starting QEMU: %s", command->str); + } #ifndef _WIN32 s->qemu_pid = fork(); @@ -414,9 +447,10 @@ static QTestState *G_GNUC_PRINTF(1, 2) qtest_spawn_qemu(const char *fmt, ...) */ prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); #endif /* __linux__ */ - if (!g_setenv("QEMU_AUDIO_DRV", "none", true)) { - exit(1); - } +#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); } @@ -427,17 +461,19 @@ static QTestState *G_GNUC_PRINTF(1, 2) qtest_spawn_qemu(const char *fmt, ...) return s; } -QTestState *qtest_init_without_qmp_handshake(const char *extra_args) +static char *qtest_socket_path(const char *suffix) +{ + return g_strdup_printf("%s/qtest-%d.%s", g_get_tmp_dir(), getpid(), suffix); +} + +static QTestState *qtest_init_internal(const char *qemu_bin, + const char *extra_args, + bool do_connect) { 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()); + g_autofree gchar *socket_path = qtest_socket_path("sock"); + g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp"); /* * It's possible that if an earlier test run crashed it might @@ -452,11 +488,13 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) sock = init_socket(socket_path); qmpsock = init_socket(qmp_socket_path); - s = qtest_spawn_qemu("-qtest unix:%s " + 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, @@ -467,22 +505,19 @@ QTestState *qtest_init_without_qmp_handshake(const char *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); - } - unlink(socket_path); - unlink(qmp_socket_path); - g_free(socket_path); - g_free(qmp_socket_path); - - g_assert(s->fd >= 0 && s->qmp_fd >= 0); - s->rx = g_string_new(""); for (i = 0; i < MAX_IRQ; i++) { s->irq_level[i] = false; } + s->fd = -1; + s->qmp_fd = -1; + s->sock = sock; + s->qmpsock = qmpsock; + if (do_connect) { + qtest_connect(s); + } + /* * Stopping QEMU for debugging is not supported on Windows. * @@ -497,26 +532,71 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) } #endif - /* ask endianness of the target */ + return s; +} - s->big_endian = qtest_query_target_endianness(s); +void qtest_connect(QTestState *s) +{ + g_autofree gchar *socket_path = qtest_socket_path("sock"); + g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp"); - return s; + g_assert(s->sock >= 0 && s->qmpsock >= 0); + s->fd = socket_accept(s->sock); + if (s->fd >= 0) { + s->qmp_fd = socket_accept(s->qmpsock); + } + unlink(socket_path); + unlink(qmp_socket_path); + g_assert(s->fd >= 0 && s->qmp_fd >= 0); + s->sock = s->qmpsock = -1; + /* ask endianness of the target */ + s->big_endian = qtest_query_target_endianness(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); - QDict *greeting; + return qtest_init_internal(qtest_qemu_binary(NULL), extra_args, true); +} +void qtest_qmp_handshake(QTestState *s, QList *capabilities) +{ /* Read the QMP greeting and then do the handshake */ - greeting = qtest_qmp_receive(s); + QDict *greeting = qtest_qmp_receive(s); qobject_unref(greeting); - qobject_unref(qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }")); + if (capabilities) { + qtest_qmp_assert_success(s, + "{ 'execute': 'qmp_capabilities', " + "'arguments': { 'enable': %p } }", + qobject_ref(capabilities)); + } else { + qtest_qmp_assert_success(s, "{ 'execute': 'qmp_capabilities' }"); + } +} + +QTestState *qtest_init_ext(const char *var, const char *extra_args, + QList *capabilities, bool do_connect) +{ + QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args, + do_connect); + + if (do_connect) { + qtest_qmp_handshake(s, capabilities); + } else { + /* + * If the connection is delayed, the capabilities must be set + * at that moment. + */ + assert(!capabilities); + } return s; } +QTestState *qtest_init(const char *extra_args) +{ + return qtest_init_ext(NULL, extra_args, NULL, true); +} + QTestState *qtest_vinitf(const char *fmt, va_list ap) { char *args = g_strdup_vprintf(fmt, ap); @@ -716,12 +796,15 @@ QDict *qtest_qmp_receive(QTestState *s) response, s->eventData)) { /* Stash the event for a later consumption */ s->pending_events = g_list_append(s->pending_events, response); + } else { + qobject_unref(response); } } } QDict *qtest_qmp_receive_dict(QTestState *s) { + g_assert(s->qmp_fd >= 0); return qmp_fd_receive(s->qmp_fd); } @@ -749,12 +832,14 @@ int qtest_socket_server(const char *socket_path) void qtest_qmp_vsend_fds(QTestState *s, int *fds, size_t fds_num, const char *fmt, va_list ap) { + g_assert(s->qmp_fd >= 0); qmp_fd_vsend_fds(s->qmp_fd, fds, fds_num, fmt, ap); } #endif void qtest_qmp_vsend(QTestState *s, const char *fmt, va_list ap) { + g_assert(s->qmp_fd >= 0); qmp_fd_vsend(s->qmp_fd, fmt, ap); } @@ -815,6 +900,7 @@ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) { va_list ap; + g_assert(s->qmp_fd >= 0); va_start(ap, fmt); qmp_fd_vsend_raw(s->qmp_fd, fmt, ap); va_end(ap); @@ -900,7 +986,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) { @@ -918,15 +1004,62 @@ const char *qtest_get_arch(void) return end + 1; } +static bool qtest_qom_has_concrete_type(const char *parent_typename, + const char *child_typename, + QList **cached_list) +{ + QList *list = cached_list ? *cached_list : NULL; + const QListEntry *p; + QObject *qobj; + QString *qstr; + QDict *devinfo; + int idx; + + if (!list) { + QDict *resp; + QDict *args; + QTestState *qts = qtest_init("-machine none"); + + args = qdict_new(); + qdict_put_bool(args, "abstract", false); + qdict_put_str(args, "implements", parent_typename); + + resp = qtest_qmp(qts, "{'execute': 'qom-list-types', 'arguments': %p }", + args); + g_assert(qdict_haskey(resp, "return")); + list = qdict_get_qlist(resp, "return"); + qobject_ref(list); + qobject_unref(resp); + + qtest_quit(qts); + + if (cached_list) { + *cached_list = list; + } + } + + for (p = qlist_first(list), idx = 0; p; p = qlist_next(p), idx++) { + devinfo = qobject_to(QDict, qlist_entry_obj(p)); + g_assert(devinfo); + + qobj = qdict_get(devinfo, "name"); + g_assert(qobj); + qstr = qobject_to(QString, qobj); + g_assert(qstr); + if (g_str_equal(qstring_get_str(qstr), child_typename)) { + return true; + } + } + + return false; +} + bool qtest_has_accel(const char *accel_name) { - if (g_str_equal(accel_name, "tcg")) { -#if defined(CONFIG_TCG) - return true; -#else - return false; -#endif - } else if (g_str_equal(accel_name, "kvm")) { + static QList *list; + g_autofree char *accel_type = NULL; + + if (g_str_equal(accel_name, "kvm")) { int i; const char *arch = qtest_get_arch(); const char *targets[] = { CONFIG_KVM_TARGETS }; @@ -938,11 +1071,12 @@ bool qtest_has_accel(const char *accel_name) } } } - } else { - /* not implemented */ - g_assert_not_reached(); + return false; } - return false; + + accel_type = g_strconcat(accel_name, ACCEL_CLASS_SUFFIX, NULL); + + return qtest_qom_has_concrete_type("accel", accel_type, &list); } bool qtest_get_irq(QTestState *s, int num) @@ -1172,6 +1306,33 @@ uint64_t qtest_rtas_call(QTestState *s, const char *name, return 0; } +static void qtest_rsp_csr(QTestState *s, uint64_t *val) +{ + gchar **args; + uint64_t ret; + int rc; + + args = qtest_rsp_args(s, 3); + + rc = qemu_strtou64(args[1], NULL, 16, &ret); + g_assert(rc == 0); + rc = qemu_strtou64(args[2], NULL, 16, val); + g_assert(rc == 0); + + g_strfreev(args); +} + +uint64_t qtest_csr_call(QTestState *s, const char *name, + uint64_t cpu, int csr, + uint64_t *val) +{ + qtest_sendf(s, "csr %s 0x%"PRIx64" %d 0x%"PRIx64"\n", + name, cpu, csr, *val); + + qtest_rsp_csr(s, val); + return 0; +} + void qtest_add_func(const char *str, void (*fn)(void)) { gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str); @@ -1254,6 +1415,28 @@ void qtest_memset(QTestState *s, uint64_t addr, uint8_t pattern, size_t size) qtest_rsp(s); } +QDict *qtest_vqmp_assert_failure_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, "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) { @@ -1316,6 +1499,17 @@ void qtest_vqmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds, } #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; @@ -1411,13 +1605,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; @@ -1426,11 +1639,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_ext(qemu_var, "-machine none", NULL, true); response = qtest_qmp(qts, "{ 'execute': 'query-machines' }"); g_assert(response); list = qdict_get_qlist(response, "return"); @@ -1461,23 +1685,103 @@ 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_ext(NULL, "-machine none", NULL, true); + 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("xenpvh", machines[i].name)) { + g_str_equal("xenpvh", machines[i].name) || + g_str_equal("vmapple", machines[i].name) || + g_str_equal("nitro-enclave", machines[i].name)) { continue; } if (!skip_old_versioned || @@ -1487,60 +1791,32 @@ 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 (g_str_equal(machine, machines[i].name) || - (machines[i].alias && g_str_equal(machine, machines[i].alias))) { - return true; + if (machines[i].alias && g_str_equal(alias, machines[i].alias)) { + return g_strdup(machines[i].name); } } - return false; + return NULL; } -bool qtest_has_device(const char *device) +bool qtest_has_machine_with_env(const char *var, const char *machine) { - static QList *list; - const QListEntry *p; - QObject *qobj; - QString *qstr; - QDict *devinfo; - int idx; - - if (!list) { - QDict *resp; - QDict *args; - QTestState *qts = qtest_init("-machine none"); - - args = qdict_new(); - qdict_put_bool(args, "abstract", false); - qdict_put_str(args, "implements", "device"); - - resp = qtest_qmp(qts, "{'execute': 'qom-list-types', 'arguments': %p }", - args); - g_assert(qdict_haskey(resp, "return")); - list = qdict_get_qlist(resp, "return"); - qobject_ref(list); - qobject_unref(resp); - - qtest_quit(qts); - } + struct MachInfo *machines; + int i; - for (p = qlist_first(list), idx = 0; p; p = qlist_next(p), idx++) { - devinfo = qobject_to(QDict, qlist_entry_obj(p)); - g_assert(devinfo); + machines = qtest_get_machines(var); - qobj = qdict_get(devinfo, "name"); - g_assert(qobj); - qstr = qobject_to(QString, qobj); - g_assert(qstr); - if (g_str_equal(qstring_get_str(qstr), device)) { + for (i = 0; machines[i].name != NULL; i++) { + if (g_str_equal(machine, machines[i].name) || + (machines[i].alias && g_str_equal(machine, machines[i].alias))) { return true; } } @@ -1548,6 +1824,18 @@ bool qtest_has_device(const char *device) 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; + + return qtest_qom_has_concrete_type("device", device, &list); +} + /* * Generic hot-plugging test via the device_add QMP commands. */ @@ -1708,7 +1996,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; @@ -1722,7 +2014,6 @@ void qtest_client_inproc_recv(void *opaque, const char *str) qts->rx = g_string_new(NULL); } g_string_append(qts->rx, str); - return; } void qtest_qom_set_bool(QTestState *s, const char *path, const char *property, diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index e53e350e3a6..b3f2e7fbefd 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -17,8 +17,9 @@ #ifndef LIBQTEST_H #define LIBQTEST_H -#include "qapi/qmp/qobject.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qobject.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "libqmp.h" typedef struct QTestState QTestState; @@ -55,6 +56,23 @@ QTestState *qtest_vinitf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); */ QTestState *qtest_init(const char *extra_args); +/** + * qtest_init_ext: + * @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. + * @capabilities: list of QMP capabilities (strings) to enable + * @do_connect: connect to qemu monitor and qtest socket. + * + * Like qtest_init(), but use a different environment variable for the + * QEMU binary, allow specify capabilities and skip connecting + * to QEMU monitor. + * + * Returns: #QTestState instance. + */ +QTestState *qtest_init_ext(const char *var, const char *extra_args, + QList *capabilities, bool do_connect); + /** * qtest_init_without_qmp_handshake: * @extra_args: other arguments to pass to QEMU. CAUTION: these @@ -64,6 +82,22 @@ QTestState *qtest_init(const char *extra_args); */ QTestState *qtest_init_without_qmp_handshake(const char *extra_args); +/** + * qtest_connect + * @s: #QTestState instance to connect + * Connect to qemu monitor and qtest socket, after skipping them in + * qtest_init_ext. Does not handshake with the monitor. + */ +void qtest_connect(QTestState *s); + +/** + * qtest_qmp_handshake: + * @s: #QTestState instance to operate on. + * @capabilities: list of QMP capabilities (strings) to enable + * Perform handshake after connecting to qemu monitor. + */ +void qtest_qmp_handshake(QTestState *s, QList *capabilities); + /** * qtest_init_with_serial: * @extra_args: other arguments to pass to QEMU. CAUTION: these @@ -75,6 +109,31 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args); */ QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd); +/** + * qtest_system_reset: + * @s: #QTestState instance to operate on. + * + * Send a "system_reset" command to the QEMU under test, and wait for + * the reset to complete before returning. + */ +void qtest_system_reset(QTestState *s); + +/** + * qtest_system_reset_nowait: + * @s: #QTestState instance to operate on. + * + * Send a "system_reset" command to the QEMU under test, but do not + * wait for the reset to complete before returning. The caller is + * responsible for waiting for either the RESET event or some other + * event of interest to them before proceeding. + * + * This function should only be used if you're specifically testing + * for some other event; in that case you can't use qtest_system_reset() + * because it will read and discard any other QMP events that arrive + * before the RESET event. + */ +void qtest_system_reset_nowait(QTestState *s); + /** * qtest_wait_qemu: * @s: #QTestState instance to operate on. @@ -327,7 +386,7 @@ QDict *qtest_qmp_event_ref(QTestState *s, const char *event); char *qtest_hmp(QTestState *s, const char *fmt, ...) G_GNUC_PRINTF(2, 3); /** - * qtest_hmpv: + * qtest_vhmp: * @s: #QTestState instance to operate on. * @fmt: HMP command to send to QEMU, formats arguments like vsprintf(). * @ap: HMP command arguments @@ -562,6 +621,20 @@ uint64_t qtest_rtas_call(QTestState *s, const char *name, uint32_t nargs, uint64_t args, uint32_t nret, uint64_t ret); +/** + * qtest_csr_call: + * @s: #QTestState instance to operate on. + * @name: name of the command to call. + * @cpu: hart number. + * @csr: CSR number. + * @val: Value for reading/writing. + * + * Call an RISC-V CSR read/write function + */ +uint64_t qtest_csr_call(QTestState *s, const char *name, + uint64_t cpu, int csr, + uint64_t *val); + /** * qtest_bufread: * @s: #QTestState instance to operate on. @@ -810,6 +883,34 @@ void qtest_vqmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds, 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 @@ -838,7 +939,7 @@ void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) #ifndef _WIN32 /** - * qtest_qmp_fd_assert_success_ref: + * qtest_qmp_fds_assert_success_ref: * @qts: QTestState instance to operate on * @fds: the file descriptors to send * @nfds: number of @fds to send @@ -855,7 +956,7 @@ QDict *qtest_qmp_fds_assert_success_ref(QTestState *qts, int *fds, size_t nfds, G_GNUC_PRINTF(4, 5); /** - * qtest_qmp_fd_assert_success: + * qtest_qmp_fds_assert_success: * @qts: QTestState instance to operate on * @fds: the file descriptors to send * @nfds: number of @fds to send @@ -881,6 +982,16 @@ void qtest_qmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds, 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 @@ -889,6 +1000,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 diff --git a/tests/qtest/lsm303dlhc-mag-test.c b/tests/qtest/lsm303dlhc-mag-test.c index 0f64e7fc67d..55ef4594f91 100644 --- a/tests/qtest/lsm303dlhc-mag-test.c +++ b/tests/qtest/lsm303dlhc-mag-test.c @@ -13,7 +13,7 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define LSM303DLHC_MAG_TEST_ID "lsm303dlhc_mag-test" #define LSM303DLHC_MAG_REG_CRA 0x00 diff --git a/tests/qtest/m48t59-test.c b/tests/qtest/m48t59-test.c index 843d2ced8e2..1e39a0e8f07 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 */ @@ -244,11 +247,6 @@ static void base_setup(void) base_year = 1968; base_machine = "SS-5"; use_mmio = true; - } else if (g_str_equal(arch, "ppc") || g_str_equal(arch, "ppc64")) { - base = 0xF0000000; - base_year = 1968; - base_machine = "ref405ep"; - use_mmio = true; } else { g_assert_not_reached(); } @@ -259,11 +257,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 31cc0bfb011..b6a87d27edb 100644 --- a/tests/qtest/machine-none-test.c +++ b/tests/qtest/machine-none-test.c @@ -14,7 +14,7 @@ #include "qemu/cutils.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" struct arch2cpu { @@ -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 0c98d0764cc..5e0878c9239 100644 --- a/tests/qtest/max34451-test.c +++ b/tests/qtest/max34451-test.c @@ -11,13 +11,14 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/bitops.h" #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 1fba07f4ed7..8ad849054fe 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -1,14 +1,22 @@ 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 = [ @@ -38,17 +46,26 @@ qtests_cxl = \ # for the availability of the default NICs in the tests qtests_filter = \ (get_option('default_devices') and slirp.found() ? ['test-netfilter'] : []) + \ - (get_option('default_devices') and targetos != 'windows' ? ['test-filter-mirror'] : []) + \ - (get_option('default_devices') and targetos != 'windows' ? ['test-filter-redirector'] : []) + (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_FW_CFG_DMA') ? ['vmcoreinfo-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'] : []) + \ - (targetos == 'linux' and \ + (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'] : []) + \ @@ -58,6 +75,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'] : []) + \ @@ -71,10 +89,13 @@ 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'] : []) + \ - (targetos != 'windows' and \ + (config_all_devices.has_key('CONFIG_VTD') ? ['intel-iommu-test'] : []) + \ + (host_os != 'windows' and \ config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + \ (config_all_devices.has_key('CONFIG_PCIE_PORT') and \ config_all_devices.has_key('CONFIG_VIRTIO_NET') and \ @@ -86,25 +107,16 @@ qtests_i386 = \ 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 @@ -130,10 +142,15 @@ qtests_alpha = ['boot-serial-test'] + \ qtests_avr = [ 'boot-serial-test' ] -qtests_hppa = ['boot-serial-test'] + \ +qtests_hppa = \ 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', + 'cpu-plug-test'] + qtests_m68k = ['boot-serial-test'] + \ qtests_filter @@ -154,20 +171,23 @@ qtests_mips64el = qtests_mips 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.has_key('CONFIG_TCG') ? ['prom-env-test'] : []) + \ - (config_all.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'] : []) @@ -190,15 +210,30 @@ qtests_npcm7xx = \ 'npcm7xx_timer-test', 'npcm7xx_watchdog_timer-test'] + \ (slirp.found() ? ['npcm7xx_emc-test'] : []) +qtests_npcm8xx = \ + ['npcm_gmac-test'] qtests_aspeed = \ - ['aspeed_hace-test', - 'aspeed_smc-test', - 'aspeed_gpio-test'] + ['aspeed_gpio-test', + 'aspeed_hace-test', + 'aspeed_smc-test'] +qtests_aspeed64 = \ + ['ast2700-gpio-test', + 'ast2700-hace-test', + 'ast2700-smc-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_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 : []) + \ @@ -207,19 +242,25 @@ qtests_arm = \ (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', '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.has_key('CONFIG_TCG') and config_all_devices.has_key('CONFIG_TPM_TIS_SYSBUS') ? \ + (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'] : []) + \ - (config_all_devices.has_key('CONFIG_RASPI') ? ['bcm2835-dma-test'] : []) + \ - (config_all.has_key('CONFIG_TCG') and \ + (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 : []) + \ + (config_all_devices.has_key('CONFIG_NPCM8XX') ? qtests_npcm8xx : []) + \ ['arm-cpu-features', 'numa-test', 'boot-serial-test', @@ -237,15 +278,18 @@ qtests_s390x = \ qtests_riscv32 = \ (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : []) +qtests_riscv64 = ['riscv-csr-test'] + \ + (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', 'es1370-test.c', - 'ipoctal232-test.c', 'lsm303dlhc-mag-test.c', 'isl_pmbus_vr-test.c', 'max34451-test.c', @@ -256,6 +300,7 @@ qos_test_ss.add( 'pca9552-test.c', 'pci-test.c', 'pcnet-test.c', + 'rs5c372-test.c', 'sdhci-test.c', 'spapr-phb-test.c', 'tmp105-test.c', @@ -270,13 +315,17 @@ qos_test_ss.add( 'vmxnet3-test.c', 'igb-test.c', 'ufs-test.c', + 'riscv-iommu-test.c', ) if config_all_devices.has_key('CONFIG_VIRTIO_SERIAL') qos_test_ss.add(files('virtio-serial-test.c')) endif +if config_all_devices.has_key('CONFIG_IP_OCTAL_232') + qos_test_ss.add(files('ipoctal232-test.c')) +endif -if targetos != 'windows' +if host_os != 'windows' qos_test_ss.add(files('e1000e-test.c')) endif if have_virtfs @@ -291,25 +340,45 @@ endif tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c'] -migration_files = [files('migration-helpers.c')] +migration_files = [files( + 'migration/bootfile.c', + 'migration/framework.c', + 'migration/migration-qmp.c', + 'migration/migration-util.c', + 'migration/compression-tests.c', + 'migration/cpr-tests.c', + 'migration/file-tests.c', + 'migration/misc-tests.c', + 'migration/precopy-tests.c', + 'migration/postcopy-tests.c', +)] + +migration_tls_files = [] if gnutls.found() - migration_files += [files('../unit/crypto-tls-psk-helpers.c'), gnutls] + migration_tls_files = [files('migration/tls-tests.c', + '../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_tls_files += [files('../unit/crypto-tls-x509-helpers.c'), tasn1] endif endif qtests = { + 'aspeed_hace-test': files('aspeed-hace-utils.c', 'aspeed_hace-test.c'), + 'aspeed_smc-test': files('aspeed-smc-utils.c', 'aspeed_smc-test.c'), + 'ast2700-hace-test': files('aspeed-hace-utils.c', 'ast2700-hace-test.c'), + 'ast2700-smc-test': files('aspeed-smc-utils.c', 'ast2700-smc-test.c'), 'bios-tables-test': [io, 'boot-sector.c', 'acpi-utils.c', 'tpm-emu.c'], 'cdrom-test': files('boot-sector.c'), - 'dbus-vmstate-test': files('migration-helpers.c') + dbus_vmstate1, + 'dbus-vmstate-test': files('migration/migration-qmp.c', + 'migration/migration-util.c') + dbus_vmstate1, 'erst-test': files('erst-test.c'), 'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'], - 'migration-test': migration_files, + 'migration-test': migration_files + migration_tls_files, 'pxe-test': files('boot-sector.c'), - 'qos-test': [chardev, io, qos_test_ss.apply(config_targetos, strict: false).sources()], + 'pnv-xive2-test': files('pnv-xive2-common.c', 'pnv-xive2-flush-sync.c', + 'pnv-xive2-nvpg_bar.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'], @@ -317,6 +386,7 @@ qtests = { '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': migration_files, 'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'), 'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'), } @@ -324,7 +394,7 @@ qtests = { if vnc.found() gvnc = dependency('gvnc-1.0', method: 'pkg-config', required: false) if gvnc.found() - qtests += {'vnc-display-test': [gvnc]} + qtests += {'vnc-display-test': [gvnc, keymap_targets]} qtests_generic += [ 'vnc-display-test' ] endif endif @@ -342,6 +412,8 @@ foreach dir : target_dirs target_base = dir.split('-')[0] qtest_emulator = emulators['qemu-system-' + target_base] target_qtests = get_variable('qtests_' + target_base, []) + qtests_generic + has_kvm = ('CONFIG_KVM' in config_all_accel and host_os == 'linux' + and cpu == target_base and fs.exists('/dev/kvm')) test_deps = roms qtest_env = environment() @@ -356,6 +428,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 @@ -373,14 +447,21 @@ foreach dir : target_dirs test: executable(test, src, dependencies: deps) } endif + + test_args = ['--tap', '-k'] + + if test == 'migration-test' and has_kvm + test_args += ['--full'] + endif + test('qtest-@0@/@1@'.format(target_base, test), qtest_executables[test], depends: [test_deps, qtest_emulator, emulator_modules], env: qtest_env, - args: ['--tap', '-k'], + args: test_args, 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 2abcad8e31c..505c831f132 100644 --- a/tests/qtest/microbit-test.c +++ b/tests/qtest/microbit-test.c @@ -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 @@ -410,7 +410,7 @@ static void test_nrf51_gpio_detect(void) /* Set pin high */ qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", i, 1); uint32_t actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN); - g_assert_cmpuint(actual, ==, 1 << i); + g_assert_cmphex(actual, ==, 1 << i); /* Check that DETECT is high */ g_assert_true(qtest_get_irq(qts, 0)); @@ -418,7 +418,7 @@ static void test_nrf51_gpio_detect(void) /* Set pin low, check that DETECT goes low. */ qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", i, 0); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN); - g_assert_cmpuint(actual, ==, 0x0); + g_assert_cmphex(actual, ==, 0x0); g_assert_false(qtest_get_irq(qts, 0)); } @@ -434,6 +434,8 @@ static void test_nrf51_gpio_detect(void) 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) diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c deleted file mode 100644 index be00c52d003..00000000000 --- a/tests/qtest/migration-helpers.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * QTest migration helpers - * - * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates - * based on the vhost-user-test.c that is: - * Copyright (c) 2014 Virtual Open Systems Sarl. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qapi/qmp/qjson.h" - -#include "migration-helpers.h" - -/* - * Number of seconds we wait when looking for migration - * status changes, to avoid test suite hanging forever - * when things go wrong. Needs to be higher enough to - * avoid false positives on loaded hosts. - */ -#define MIGRATION_STATUS_WAIT_TIMEOUT 120 - -bool migrate_watch_for_stop(QTestState *who, const char *name, - QDict *event, void *opaque) -{ - bool *seen = opaque; - - if (g_str_equal(name, "STOP")) { - *seen = true; - return true; - } - - return false; -} - -bool migrate_watch_for_resume(QTestState *who, const char *name, - QDict *event, void *opaque) -{ - bool *seen = opaque; - - if (g_str_equal(name, "RESUME")) { - *seen = true; - return true; - } - - return false; -} - -/* - * Send QMP command "migrate". - * Arguments are built from @fmt... (formatted like - * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. - */ -void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) -{ - va_list ap; - QDict *args; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "uri")); - qdict_put_str(args, "uri", uri); - - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate', 'arguments': %p}", args); -} - -/* - * Note: caller is responsible to free the returned object via - * qobject_unref() after use - */ -QDict *migrate_query(QTestState *who) -{ - return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }"); -} - -QDict *migrate_query_not_failed(QTestState *who) -{ - const char *status; - QDict *rsp = migrate_query(who); - status = qdict_get_str(rsp, "status"); - if (g_str_equal(status, "failed")) { - g_printerr("query-migrate shows failed migration: %s\n", - qdict_get_str(rsp, "error-desc")); - } - g_assert(!g_str_equal(status, "failed")); - return rsp; -} - -/* - * Note: caller is responsible to free the returned object via - * g_free() after use - */ -static gchar *migrate_query_status(QTestState *who) -{ - QDict *rsp_return = migrate_query(who); - gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); - - g_assert(status); - qobject_unref(rsp_return); - - return status; -} - -static bool check_migration_status(QTestState *who, const char *goal, - const char **ungoals) -{ - bool ready; - char *current_status; - const char **ungoal; - - current_status = migrate_query_status(who); - ready = strcmp(current_status, goal) == 0; - if (!ungoals) { - g_assert_cmpstr(current_status, !=, "failed"); - /* - * If looking for a state other than completed, - * completion of migration would cause the test to - * hang. - */ - if (strcmp(goal, "completed") != 0) { - g_assert_cmpstr(current_status, !=, "completed"); - } - } else { - for (ungoal = ungoals; *ungoal; ungoal++) { - g_assert_cmpstr(current_status, !=, *ungoal); - } - } - g_free(current_status); - return ready; -} - -void wait_for_migration_status(QTestState *who, - const char *goal, const char **ungoals) -{ - g_test_timer_start(); - while (!check_migration_status(who, goal, ungoals)) { - usleep(1000); - - g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); - } -} - -void wait_for_migration_complete(QTestState *who) -{ - wait_for_migration_status(who, "completed", NULL); -} - -void wait_for_migration_fail(QTestState *from, bool allow_active) -{ - g_test_timer_start(); - QDict *rsp_return; - char *status; - bool failed; - - do { - status = migrate_query_status(from); - bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || - (allow_active && !strcmp(status, "active")); - if (!result) { - fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", - __func__, status, allow_active); - } - g_assert(result); - failed = !strcmp(status, "failed"); - g_free(status); - - g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); - } while (!failed); - - /* Is the machine currently running? */ - 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); -} diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h deleted file mode 100644 index 009e250e906..00000000000 --- a/tests/qtest/migration-helpers.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * QTest migration helpers - * - * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates - * based on the vhost-user-test.c that is: - * Copyright (c) 2014 Virtual Open Systems Sarl. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef MIGRATION_HELPERS_H -#define MIGRATION_HELPERS_H - -#include "libqtest.h" - -bool migrate_watch_for_stop(QTestState *who, const char *name, - QDict *event, void *opaque); -bool migrate_watch_for_resume(QTestState *who, const char *name, - QDict *event, void *opaque); - -G_GNUC_PRINTF(3, 4) -void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...); - -QDict *migrate_query(QTestState *who); -QDict *migrate_query_not_failed(QTestState *who); - -void wait_for_migration_status(QTestState *who, - const char *goal, const char **ungoals); - -void wait_for_migration_complete(QTestState *who); - -void wait_for_migration_fail(QTestState *from, bool allow_active); - -#endif /* MIGRATION_HELPERS_H */ diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 1b43df5ca7c..08936871741 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -11,2957 +11,56 @@ */ #include "qemu/osdep.h" +#include "migration/framework.h" +#include "qemu/module.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 "migration-helpers.h" -#include "tests/migration/migration-test.h" -#ifdef CONFIG_GNUTLS -# include "tests/unit/crypto-tls-psk-helpers.h" -# ifdef CONFIG_TASN1 -# include "tests/unit/crypto-tls-x509-helpers.h" -# endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - -/* For dirty ring test; so far only x86_64 is supported */ -#if defined(__linux__) && defined(HOST_X86_64) -#include "linux/kvm.h" -#endif - -unsigned start_address; -unsigned end_address; -static bool uffd_feature_thread_id; -static bool got_src_stop; -static bool got_dst_resume; - -/* - * An initial 3 MB offset is used as that corresponds - * to ~1 sec of data transfer with our bandwidth setting. - */ -#define MAGIC_OFFSET_BASE (3 * 1024 * 1024) -/* - * A further 1k is added to ensure we're not a multiple - * of TEST_MEM_PAGE_SIZE, thus avoid clash with writes - * from the migration guest workload. - */ -#define MAGIC_OFFSET_SHUFFLE 1024 -#define MAGIC_OFFSET (MAGIC_OFFSET_BASE + MAGIC_OFFSET_SHUFFLE) -#define MAGIC_MARKER 0xFEED12345678CAFEULL - -/* - * Dirtylimit stop working if dirty page rate error - * value less than DIRTYLIMIT_TOLERANCE_RANGE - */ -#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ - -#if defined(__linux__) -#include -#include -#endif - -#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) -#include -#include -#include "qemu/userfaultfd.h" - -static bool ufd_version_check(void) -{ - struct uffdio_api api_struct; - uint64_t ioctl_mask; - - int ufd = uffd_open(O_CLOEXEC); - - if (ufd == -1) { - g_test_message("Skipping test: userfaultfd not available"); - return false; - } - - api_struct.api = UFFD_API; - api_struct.features = 0; - if (ioctl(ufd, UFFDIO_API, &api_struct)) { - g_test_message("Skipping test: UFFDIO_API failed"); - return false; - } - uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID; - - ioctl_mask = (__u64)1 << _UFFDIO_REGISTER | - (__u64)1 << _UFFDIO_UNREGISTER; - if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) { - g_test_message("Skipping test: Missing userfault feature"); - return false; - } - - return true; -} - -#else -static bool ufd_version_check(void) -{ - g_test_message("Skipping test: Userfault not available (builtdtime)"); - return false; -} - -#endif - -static char *tmpfs; - -/* 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/s390x/a-b-bios.h" - -static void init_bootfile(const char *bootpath, void *content, size_t len) -{ - FILE *bootfile = fopen(bootpath, "wb"); - - g_assert_cmpint(fwrite(content, len, 1, bootfile), ==, 1); - fclose(bootfile); -} - -/* - * 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. - */ -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 */ - break; - - case 'B': - /* It's alive! */ - fclose(serialfile); - return; - - case EOF: - started = (strcmp(side, "src_serial") == 0 && - strcmp(arch, "ppc64") == 0) ? 0 : 1; - fseek(serialfile, 0, SEEK_SET); - usleep(1000); - break; - - default: - fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side); - g_assert_not_reached(); - } - } while (true); -} - -/* - * It's tricky to use qemu's migration event capability with qtest, - * events suddenly appearing confuse the qmp()/hmp() responses. - */ - -static int64_t read_ram_property_int(QTestState *who, const char *property) -{ - QDict *rsp_return, *rsp_ram; - int64_t result; - - rsp_return = migrate_query_not_failed(who); - if (!qdict_haskey(rsp_return, "ram")) { - /* Still in setup */ - result = 0; - } else { - rsp_ram = qdict_get_qdict(rsp_return, "ram"); - result = qdict_get_try_int(rsp_ram, property, 0); - } - qobject_unref(rsp_return); - return result; -} - -static int64_t read_migrate_property_int(QTestState *who, const char *property) -{ - QDict *rsp_return; - int64_t result; - - rsp_return = migrate_query_not_failed(who); - result = qdict_get_try_int(rsp_return, property, 0); - qobject_unref(rsp_return); - return result; -} - -static uint64_t get_migration_pass(QTestState *who) -{ - return read_ram_property_int(who, "dirty-sync-count"); -} - -static void read_blocktime(QTestState *who) -{ - QDict *rsp_return; - - rsp_return = migrate_query_not_failed(who); - g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); - qobject_unref(rsp_return); -} - -static void wait_for_migration_pass(QTestState *who) -{ - uint64_t initial_pass = get_migration_pass(who); - uint64_t pass; - - /* Wait for the 1st sync */ - while (!got_src_stop && !initial_pass) { - usleep(1000); - initial_pass = get_migration_pass(who); - } - - do { - usleep(1000); - pass = get_migration_pass(who); - } while (pass == initial_pass && !got_src_stop); -} - -static void check_guests_ram(QTestState *who) -{ - /* Our ASM test will have been incrementing one byte from each page from - * start_address to < end_address in order. This gives us a constraint - * that any page's byte should be equal or less than the previous pages - * byte (mod 256); and they should all be equal except for one transition - * at the point where we meet the incrementer. (We're running this with - * the guest stopped). - */ - unsigned address; - uint8_t first_byte; - uint8_t last_byte; - bool hit_edge = false; - int bad = 0; - - qtest_memread(who, start_address, &first_byte, 1); - last_byte = first_byte; - - for (address = start_address + TEST_MEM_PAGE_SIZE; address < end_address; - address += TEST_MEM_PAGE_SIZE) - { - uint8_t b; - qtest_memread(who, address, &b, 1); - if (b != last_byte) { - if (((b + 1) % 256) == last_byte && !hit_edge) { - /* This is OK, the guest stopped at the point of - * incrementing the previous page but didn't get - * to us yet. - */ - hit_edge = true; - last_byte = b; - } else { - bad++; - if (bad <= 10) { - fprintf(stderr, "Memory content inconsistency at %x" - " first_byte = %x last_byte = %x current = %x" - " hit_edge = %x\n", - address, first_byte, last_byte, b, hit_edge); - } - } - } - } - if (bad >= 10) { - fprintf(stderr, "and in another %d pages", bad - 10); - } - g_assert(bad == 0); -} - -static void cleanup(const char *filename) -{ - g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, 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 = qtest_qmp_assert_success_ref( - who, "{ 'execute': 'query-migrate-parameters' }"); - result = qdict_get_int(rsp, parameter); - qobject_unref(rsp); - return result; -} - -static void migrate_check_parameter_int(QTestState *who, const char *parameter, - long long value) -{ - long long result; - - result = migrate_get_parameter_int(who, parameter); - g_assert_cmpint(result, ==, value); -} - -static void migrate_set_parameter_int(QTestState *who, const char *parameter, - long long value) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %lld } }", - parameter, value); - migrate_check_parameter_int(who, parameter, value); -} - -static char *migrate_get_parameter_str(QTestState *who, - const char *parameter) -{ - QDict *rsp; - char *result; - - rsp = qtest_qmp_assert_success_ref( - who, "{ 'execute': 'query-migrate-parameters' }"); - result = g_strdup(qdict_get_str(rsp, parameter)); - qobject_unref(rsp); - return result; -} - -static void migrate_check_parameter_str(QTestState *who, const char *parameter, - const char *value) -{ - g_autofree char *result = migrate_get_parameter_str(who, parameter); - g_assert_cmpstr(result, ==, value); -} - -static void migrate_set_parameter_str(QTestState *who, const char *parameter, - const char *value) -{ - 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 + 3 mbs bandwidth limit */ - migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000); - migrate_set_parameter_int(who, "downtime-limit", 1); -} - -static void migrate_ensure_converge(QTestState *who) -{ - /* Should converge with 30s downtime + 1 gbs bandwidth limit */ - migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000); - migrate_set_parameter_int(who, "downtime-limit", 30 * 1000); -} - -/* - * Our goal is to ensure that we run a single full migration - * iteration, and also dirty memory, ensuring that at least - * one further iteration is required. - * - * We can't directly synchronize with the start of a migration - * so we have to apply some tricks monitoring memory that is - * transferred. - * - * Initially we set the migration bandwidth to an insanely - * low value, with tiny max downtime too. This basically - * guarantees migration will never complete. - * - * This will result in a test that is unacceptably slow though, - * so we can't let the entire migration pass run at this speed. - * Our intent is to let it run just long enough that we can - * prove data prior to the marker has been transferred *AND* - * also prove this transferred data is dirty again. - * - * Before migration starts, we write a 64-bit magic marker - * into a fixed location in the src VM RAM. - * - * Then watch dst memory until the marker appears. This is - * proof that start_address -> MAGIC_OFFSET_BASE has been - * transferred. - * - * Finally we go back to the source and read a byte just - * before the marker until we see it flip in value. This - * is proof that start_address -> MAGIC_OFFSET_BASE - * is now dirty again. - * - * IOW, we're guaranteed at least a 2nd migration pass - * at this point. - * - * We can now let migration run at full speed to finish - * the test - */ -static void migrate_prepare_for_dirty_mem(QTestState *from) -{ - /* - * The guest workflow iterates from start_address to - * end_address, writing 1 byte every TEST_MEM_PAGE_SIZE - * bytes. - * - * IOW, if we write to mem at a point which is NOT - * a multiple of TEST_MEM_PAGE_SIZE, our write won't - * conflict with the migration workflow. - * - * We put in a marker here, that we'll use to determine - * when the data has been transferred to the dst. - */ - qtest_writeq(from, start_address + MAGIC_OFFSET, MAGIC_MARKER); -} - -static void migrate_wait_for_dirty_mem(QTestState *from, - QTestState *to) -{ - uint64_t watch_address = start_address + MAGIC_OFFSET_BASE; - uint64_t marker_address = start_address + MAGIC_OFFSET; - uint8_t watch_byte; - - /* - * Wait for the MAGIC_MARKER to get transferred, as an - * indicator that a migration pass has made some known - * amount of progress. - */ - do { - usleep(1000 * 10); - } while (qtest_readq(to, marker_address) != MAGIC_MARKER); - - /* - * Now ensure that already transferred bytes are - * dirty again from the guest workload. Note the - * guest byte value will wrap around and by chance - * match the original watch_byte. This is harmless - * as we'll eventually see a different value if we - * keep watching - */ - watch_byte = qtest_readb(from, watch_address); - do { - usleep(1000 * 10); - } while (qtest_readb(from, watch_address) == watch_byte); -} - - -static void migrate_pause(QTestState *who) -{ - qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }"); -} - -static void migrate_continue(QTestState *who, const char *state) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-continue'," - " 'arguments': { 'state': %s } }", - state); -} - -static void migrate_recover(QTestState *who, const char *uri) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-recover', " - " 'id': 'recover-cmd', " - " 'arguments': { 'uri': %s } }", - uri); -} - -static void migrate_cancel(QTestState *who) -{ - qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }"); -} - -static 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); -} - -static void migrate_postcopy_start(QTestState *from, QTestState *to) -{ - qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }"); - - if (!got_src_stop) { - qtest_qmp_eventwait(from, "STOP"); - } - - qtest_qmp_eventwait(to, "RESUME"); -} - -typedef struct { - /* - * QTEST_LOG=1 may override this. When QTEST_LOG=1, we always dump errors - * unconditionally, because it means the user would like to be verbose. - */ - bool hide_stderr; - bool use_shmem; - /* only launch the target process */ - bool only_target; - /* Use dirty ring if true; dirty logging otherwise */ - bool use_dirty_ring; - const char *opts_source; - const char *opts_target; -} MigrateStart; - -/* - * A hook that runs after the src and dst QEMUs have been - * created, but before the migration is started. This can - * be used to set migration parameters and capabilities. - * - * Returns: NULL, or a pointer to opaque state to be - * later passed to the TestMigrateFinishHook - */ -typedef void * (*TestMigrateStartHook)(QTestState *from, - QTestState *to); - -/* - * A hook that runs after the migration has finished, - * regardless of whether it succeeded or failed, but - * before QEMU has terminated (unless it self-terminated - * due to migration error) - * - * @opaque is a pointer to state previously returned - * by the TestMigrateStartHook if any, or NULL. - */ -typedef void (*TestMigrateFinishHook)(QTestState *from, - QTestState *to, - void *opaque); - -typedef struct { - /* Optional: fine tune start parameters */ - MigrateStart start; - - /* Required: the URI for the dst QEMU to listen on */ - const char *listen_uri; - - /* - * Optional: the URI for the src QEMU to connect to - * If NULL, then it will query the dst QEMU for its actual - * listening address and use that as the connect address. - * This allows for dynamically picking a free TCP port. - */ - const char *connect_uri; - - /* Optional: callback to run at start to set migration parameters */ - TestMigrateStartHook start_hook; - /* Optional: callback to run at finish to cleanup */ - TestMigrateFinishHook finish_hook; - - /* - * Optional: normally we expect the migration process to complete. - * - * There can be a variety of reasons and stages in which failure - * can happen during tests. - * - * If a failure is expected to happen at time of establishing - * the connection, then MIG_TEST_FAIL will indicate that the dst - * QEMU is expected to stay running and accept future migration - * connections. - * - * If a failure is expected to happen while processing the - * migration stream, then MIG_TEST_FAIL_DEST_QUIT_ERR will indicate - * that the dst QEMU is expected to quit with non-zero exit status - */ - enum { - /* This test should succeed, the default */ - MIG_TEST_SUCCEED = 0, - /* This test should fail, dest qemu should keep alive */ - MIG_TEST_FAIL, - /* This test should fail, dest qemu should fail with abnormal status */ - MIG_TEST_FAIL_DEST_QUIT_ERR, - } result; - - /* - * Optional: set number of migration passes to wait for, if live==true. - * If zero, then merely wait for a few MB of dirty data - */ - unsigned int iterations; - - /* - * Optional: whether the guest CPUs should be running during a precopy - * migration test. We used to always run with live but it took much - * longer so we reduced live tests to only the ones that have solid - * reason to be tested live-only. For each of the new test cases for - * precopy please provide justifications to use live explicitly (please - * refer to existing ones with live=true), or use live=off by default. - */ - bool live; - - /* Postcopy specific fields */ - void *postcopy_data; - bool postcopy_preempt; -} MigrateCommon; - -static int test_migrate_start(QTestState **from, QTestState **to, - const char *uri, MigrateStart *args) -{ - g_autofree gchar *arch_source = NULL; - g_autofree gchar *arch_target = NULL; - /* options for source and target */ - g_autofree gchar *arch_opts = NULL; - g_autofree gchar *cmd_source = NULL; - g_autofree gchar *cmd_target = NULL; - const gchar *ignore_stderr; - g_autofree char *bootpath = NULL; - g_autofree char *shmem_opts = NULL; - g_autofree char *shmem_path = NULL; - const char *arch = qtest_get_arch(); - const char *memory_size; - - if (args->use_shmem) { - if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { - g_test_skip("/dev/shm is not supported"); - return -1; - } - } - - got_src_stop = false; - got_dst_resume = false; - bootpath = g_strdup_printf("%s/bootsect", tmpfs); - 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_opts = g_strdup_printf("-drive file=%s,format=raw", 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_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) { - memory_size = "256M"; - start_address = PPC_TEST_MEM_START; - end_address = PPC_TEST_MEM_END; - arch_source = g_strdup_printf("-prom-env 'use-nvramrc?=true' -prom-env " - "'nvramrc=hex .\" _\" begin %x %x " - "do i c@ 1 + i c! 1000 +loop .\" B\" 0 " - "until'", end_address, start_address); - arch_opts = g_strdup("-nodefaults -machine vsmt=8"); - } else if (strcmp(arch, "aarch64") == 0) { - init_bootfile(bootpath, aarch64_kernel, sizeof(aarch64_kernel)); - memory_size = "150M"; - arch_opts = g_strdup_printf("-machine virt,gic-version=max -cpu max " - "-kernel %s", bootpath); - start_address = ARM_TEST_MEM_START; - end_address = ARM_TEST_MEM_END; - - g_assert(sizeof(aarch64_kernel) <= ARM_TEST_MAX_KERNEL_SIZE); - } else { - g_assert_not_reached(); - } - - if (!getenv("QTEST_LOG") && args->hide_stderr) { -#ifndef _WIN32 - ignore_stderr = "2>/dev/null"; -#else - /* - * On Windows the QEMU executable is created via CreateProcess() and - * IO redirection does not work, so don't bother adding IO redirection - * to the command line. - */ - ignore_stderr = ""; -#endif - } else { - ignore_stderr = ""; - } - - if (args->use_shmem) { - shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); - shmem_opts = g_strdup_printf( - "-object memory-backend-file,id=mem0,size=%s" - ",mem-path=%s,share=on -numa node,memdev=mem0", - memory_size, shmem_path); - } else { - shmem_path = NULL; - shmem_opts = g_strdup(""); - } - - cmd_source = g_strdup_printf("-accel kvm%s -accel tcg " - "-name source,debug-threads=on " - "-m %s " - "-serial file:%s/src_serial " - "%s %s %s %s %s", - args->use_dirty_ring ? - ",dirty-ring-size=4096" : "", - memory_size, tmpfs, - arch_opts ? arch_opts : "", - arch_source ? arch_source : "", - shmem_opts, - args->opts_source ? args->opts_source : "", - ignore_stderr); - if (!args->only_target) { - *from = qtest_init(cmd_source); - qtest_qmp_set_event_callback(*from, - migrate_watch_for_stop, - &got_src_stop); - } - - cmd_target = g_strdup_printf("-accel kvm%s -accel tcg " - "-name target,debug-threads=on " - "-m %s " - "-serial file:%s/dest_serial " - "-incoming %s " - "%s %s %s %s %s", - args->use_dirty_ring ? - ",dirty-ring-size=4096" : "", - memory_size, tmpfs, uri, - arch_opts ? arch_opts : "", - arch_target ? arch_target : "", - shmem_opts, - args->opts_target ? args->opts_target : "", - ignore_stderr); - *to = qtest_init(cmd_target); - qtest_qmp_set_event_callback(*to, - migrate_watch_for_resume, - &got_dst_resume); - - /* - * Remove shmem file immediately to avoid memory leak in test failed case. - * It's valid because QEMU has already opened this file - */ - if (args->use_shmem) { - unlink(shmem_path); - } - - return 0; -} - -static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest) -{ - unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d; - - qtest_quit(from); - - if (test_dest) { - qtest_memread(to, start_address, &dest_byte_a, 1); - - /* Destination still running, wait for a byte to change */ - do { - qtest_memread(to, start_address, &dest_byte_b, 1); - usleep(1000 * 10); - } while (dest_byte_a == dest_byte_b); - - qtest_qmp_assert_success(to, "{ 'execute' : 'stop'}"); - - /* With it stopped, check nothing changes */ - qtest_memread(to, start_address, &dest_byte_c, 1); - usleep(1000 * 200); - qtest_memread(to, start_address, &dest_byte_d, 1); - g_assert_cmpint(dest_byte_c, ==, dest_byte_d); - - check_guests_ram(to); - } - - qtest_quit(to); - - cleanup("bootsect"); - cleanup("migsocket"); - cleanup("src_serial"); - cleanup("dest_serial"); -} - -#ifdef CONFIG_GNUTLS -struct TestMigrateTLSPSKData { - char *workdir; - char *workdiralt; - char *pskfile; - char *pskfilealt; -}; - -static void * -test_migrate_tls_psk_start_common(QTestState *from, - QTestState *to, - bool mismatch) -{ - struct TestMigrateTLSPSKData *data = - g_new0(struct TestMigrateTLSPSKData, 1); - - data->workdir = g_strdup_printf("%s/tlscredspsk0", tmpfs); - data->pskfile = g_strdup_printf("%s/%s", data->workdir, - QCRYPTO_TLS_CREDS_PSKFILE); - g_mkdir_with_parents(data->workdir, 0700); - test_tls_psk_init(data->pskfile); - - if (mismatch) { - data->workdiralt = g_strdup_printf("%s/tlscredspskalt0", tmpfs); - data->pskfilealt = g_strdup_printf("%s/%s", data->workdiralt, - QCRYPTO_TLS_CREDS_PSKFILE); - g_mkdir_with_parents(data->workdiralt, 0700); - test_tls_psk_init_alt(data->pskfilealt); - } - - qtest_qmp_assert_success(from, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-psk'," - " 'id': 'tlscredspsk0'," - " 'endpoint': 'client'," - " 'dir': %s," - " 'username': 'qemu'} }", - data->workdir); - - 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"); - - return data; -} - -static void * -test_migrate_tls_psk_start_match(QTestState *from, - QTestState *to) -{ - return test_migrate_tls_psk_start_common(from, to, false); -} - -static void * -test_migrate_tls_psk_start_mismatch(QTestState *from, - QTestState *to) -{ - return test_migrate_tls_psk_start_common(from, to, true); -} - -static void -test_migrate_tls_psk_finish(QTestState *from, - QTestState *to, - void *opaque) -{ - struct TestMigrateTLSPSKData *data = opaque; - - test_tls_psk_cleanup(data->pskfile); - if (data->pskfilealt) { - test_tls_psk_cleanup(data->pskfilealt); - } - rmdir(data->workdir); - if (data->workdiralt) { - rmdir(data->workdiralt); - } - - g_free(data->workdiralt); - g_free(data->pskfilealt); - g_free(data->workdir); - g_free(data->pskfile); - g_free(data); -} - -#ifdef CONFIG_TASN1 -typedef struct { - char *workdir; - char *keyfile; - char *cacert; - char *servercert; - char *serverkey; - char *clientcert; - char *clientkey; -} TestMigrateTLSX509Data; - -typedef struct { - bool verifyclient; - bool clientcert; - bool hostileclient; - bool authzclient; - const char *certhostname; - const char *certipaddr; -} TestMigrateTLSX509; - -static void * -test_migrate_tls_x509_start_common(QTestState *from, - QTestState *to, - TestMigrateTLSX509 *args) -{ - TestMigrateTLSX509Data *data = g_new0(TestMigrateTLSX509Data, 1); - - data->workdir = g_strdup_printf("%s/tlscredsx5090", tmpfs); - data->keyfile = g_strdup_printf("%s/key.pem", data->workdir); - - data->cacert = g_strdup_printf("%s/ca-cert.pem", data->workdir); - data->serverkey = g_strdup_printf("%s/server-key.pem", data->workdir); - data->servercert = g_strdup_printf("%s/server-cert.pem", data->workdir); - if (args->clientcert) { - data->clientkey = g_strdup_printf("%s/client-key.pem", data->workdir); - data->clientcert = g_strdup_printf("%s/client-cert.pem", data->workdir); - } - - g_mkdir_with_parents(data->workdir, 0700); - - test_tls_init(data->keyfile); -#ifndef _WIN32 - g_assert(link(data->keyfile, data->serverkey) == 0); -#else - g_assert(CreateHardLink(data->serverkey, data->keyfile, NULL) != 0); -#endif - if (args->clientcert) { -#ifndef _WIN32 - g_assert(link(data->keyfile, data->clientkey) == 0); -#else - g_assert(CreateHardLink(data->clientkey, data->keyfile, NULL) != 0); -#endif - } - - TLS_ROOT_REQ_SIMPLE(cacertreq, data->cacert); - if (args->clientcert) { - TLS_CERT_REQ_SIMPLE_CLIENT(servercertreq, cacertreq, - args->hostileclient ? - QCRYPTO_TLS_TEST_CLIENT_HOSTILE_NAME : - QCRYPTO_TLS_TEST_CLIENT_NAME, - data->clientcert); - } - - TLS_CERT_REQ_SIMPLE_SERVER(clientcertreq, cacertreq, - data->servercert, - args->certhostname, - args->certipaddr); - - 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); - } - - 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) { - 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"); - } - - return data; -} - -/* - * The normal case: match server's cert hostname against - * whatever host we were telling QEMU to connect to (if any) - */ -static void * -test_migrate_tls_x509_start_default_host(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .certipaddr = "127.0.0.1" - }; - return test_migrate_tls_x509_start_common(from, to, &args); -} - -/* - * The unusual case: the server's cert is different from - * the address we're telling QEMU to connect to (if any), - * so we must give QEMU an explicit hostname to validate - */ -static void * -test_migrate_tls_x509_start_override_host(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .certhostname = "qemu.org", - }; - return test_migrate_tls_x509_start_common(from, to, &args); -} - -/* - * The unusual case: the server's cert is different from - * the address we're telling QEMU to connect to, and so we - * expect the client to reject the server - */ -static void * -test_migrate_tls_x509_start_mismatch_host(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .certipaddr = "10.0.0.1", - }; - return test_migrate_tls_x509_start_common(from, to, &args); -} - -static void * -test_migrate_tls_x509_start_friendly_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .authzclient = true, - .certipaddr = "127.0.0.1", - }; - return test_migrate_tls_x509_start_common(from, to, &args); -} - -static void * -test_migrate_tls_x509_start_hostile_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .hostileclient = true, - .authzclient = true, - .certipaddr = "127.0.0.1", - }; - return test_migrate_tls_x509_start_common(from, to, &args); -} - -/* - * The case with no client certificate presented, - * and no server verification - */ -static void * -test_migrate_tls_x509_start_allow_anon_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .certipaddr = "127.0.0.1", - }; - return test_migrate_tls_x509_start_common(from, to, &args); -} - -/* - * The case with no client certificate presented, - * and server verification rejecting - */ -static void * -test_migrate_tls_x509_start_reject_anon_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .certipaddr = "127.0.0.1", - }; - return test_migrate_tls_x509_start_common(from, to, &args); -} - -static void -test_migrate_tls_x509_finish(QTestState *from, - QTestState *to, - void *opaque) -{ - TestMigrateTLSX509Data *data = opaque; - - test_tls_cleanup(data->keyfile); - g_free(data->keyfile); - - unlink(data->cacert); - g_free(data->cacert); - unlink(data->servercert); - g_free(data->servercert); - unlink(data->serverkey); - g_free(data->serverkey); - - if (data->clientcert) { - unlink(data->clientcert); - g_free(data->clientcert); - } - if (data->clientkey) { - unlink(data->clientkey); - g_free(data->clientkey); - } - - rmdir(data->workdir); - g_free(data->workdir); - - g_free(data); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - -static void * -test_migrate_compress_start(QTestState *from, - QTestState *to) -{ - migrate_set_parameter_int(from, "compress-level", 1); - migrate_set_parameter_int(from, "compress-threads", 4); - migrate_set_parameter_bool(from, "compress-wait-thread", true); - migrate_set_parameter_int(to, "decompress-threads", 4); - - migrate_set_capability(from, "compress", true); - migrate_set_capability(to, "compress", true); - - return NULL; -} - -static void * -test_migrate_compress_nowait_start(QTestState *from, - QTestState *to) -{ - migrate_set_parameter_int(from, "compress-level", 9); - migrate_set_parameter_int(from, "compress-threads", 1); - migrate_set_parameter_bool(from, "compress-wait-thread", false); - migrate_set_parameter_int(to, "decompress-threads", 1); - - migrate_set_capability(from, "compress", true); - migrate_set_capability(to, "compress", true); - - return NULL; -} - -static int migrate_postcopy_prepare(QTestState **from_ptr, - QTestState **to_ptr, - MigrateCommon *args) -{ - QTestState *from, *to; - - if (test_migrate_start(&from, &to, "defer", &args->start)) { - return -1; - } - - if (args->start_hook) { - args->postcopy_data = args->start_hook(from, to); - } - - migrate_set_capability(from, "postcopy-ram", true); - migrate_set_capability(to, "postcopy-ram", true); - migrate_set_capability(to, "postcopy-blocktime", true); - - if (args->postcopy_preempt) { - migrate_set_capability(from, "postcopy-preempt", true); - migrate_set_capability(to, "postcopy-preempt", true); - } - - migrate_ensure_non_converge(from); - - migrate_prepare_for_dirty_mem(from); - qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," - " 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}"); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - g_autofree char *uri = migrate_get_socket_address(to, "socket-address"); - migrate_qmp(from, uri, "{}"); - - migrate_wait_for_dirty_mem(from, to); - - *from_ptr = from; - *to_ptr = to; - - return 0; -} - -static void migrate_postcopy_complete(QTestState *from, QTestState *to, - MigrateCommon *args) -{ - wait_for_migration_complete(from); - - /* Make sure we get at least one "B" on destination */ - wait_for_serial("dest_serial"); - - if (uffd_feature_thread_id) { - read_blocktime(to); - } - - if (args->finish_hook) { - args->finish_hook(from, to, args->postcopy_data); - args->postcopy_data = NULL; - } - - test_migrate_end(from, to, true); -} - -static void test_postcopy_common(MigrateCommon *args) -{ - QTestState *from, *to; - - if (migrate_postcopy_prepare(&from, &to, args)) { - return; - } - migrate_postcopy_start(from, to); - migrate_postcopy_complete(from, to, args); -} - -static void test_postcopy(void) -{ - MigrateCommon args = { }; - - test_postcopy_common(&args); -} - -static void test_postcopy_compress(void) -{ - MigrateCommon args = { - .start_hook = test_migrate_compress_start - }; - - test_postcopy_common(&args); -} - -static void test_postcopy_preempt(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - }; - - test_postcopy_common(&args); -} - -#ifdef CONFIG_GNUTLS -static void test_postcopy_tls_psk(void) -{ - MigrateCommon args = { - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, - }; - - test_postcopy_common(&args); -} - -static void test_postcopy_preempt_tls_psk(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, - }; - - test_postcopy_common(&args); -} -#endif - -static void test_postcopy_recovery_common(MigrateCommon *args) -{ - QTestState *from, *to; - g_autofree char *uri = NULL; - - /* Always hide errors for postcopy recover tests since they're expected */ - args->start.hide_stderr = true; - - if (migrate_postcopy_prepare(&from, &to, args)) { - return; - } - - /* Turn postcopy speed down, 4K/s is slow enough on any machines */ - migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096); - - /* Now we start the postcopy */ - migrate_postcopy_start(from, to); - - /* - * Wait until postcopy is really started; we can only run the - * migrate-pause command during a postcopy - */ - wait_for_migration_status(from, "postcopy-active", NULL); - - /* - * Manually stop the postcopy migration. This emulates a network - * failure with the migration socket - */ - migrate_pause(from); - - /* - * Wait for destination side to reach postcopy-paused state. The - * 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 }); - - /* - * Create a new socket to emulate a new channel that is different - * from the broken migration channel; tell the destination to - * listen to the new port - */ - uri = g_strdup_printf("unix:%s/migsocket-recover", tmpfs); - migrate_recover(to, uri); - - /* - * Try to rebuild the migration channel using the resume flag and - * the newly created channel - */ - wait_for_migration_status(from, "postcopy-paused", - (const char * []) { "failed", "active", - "completed", NULL }); - migrate_qmp(from, uri, "{'resume': true}"); - - /* Restore the postcopy bandwidth to unlimited */ - migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); - - migrate_postcopy_complete(from, to, args); -} - -static void test_postcopy_recovery(void) -{ - MigrateCommon args = { }; - - test_postcopy_recovery_common(&args); -} - -static void test_postcopy_recovery_compress(void) -{ - MigrateCommon args = { - .start_hook = test_migrate_compress_start - }; - - test_postcopy_recovery_common(&args); -} - -#ifdef CONFIG_GNUTLS -static void test_postcopy_recovery_tls_psk(void) -{ - MigrateCommon args = { - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, - }; - - test_postcopy_recovery_common(&args); -} -#endif - -static void test_postcopy_preempt_recovery(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - }; - - test_postcopy_recovery_common(&args); -} - -#ifdef CONFIG_GNUTLS -/* This contains preempt+recovery+tls test altogether */ -static void test_postcopy_preempt_all(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, - }; - - test_postcopy_recovery_common(&args); -} - -#endif - -static void test_baddest(void) -{ - MigrateStart args = { - .hide_stderr = true - }; - QTestState *from, *to; - - if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { - return; - } - migrate_qmp(from, "tcp:127.0.0.1:0", "{}"); - wait_for_migration_fail(from, false); - test_migrate_end(from, to, false); -} - -static void test_precopy_common(MigrateCommon *args) -{ - QTestState *from, *to; - void *data_hook = NULL; - - if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { - return; - } - - if (args->start_hook) { - data_hook = args->start_hook(from, to); - } - - /* Wait for the first serial output from the source */ - if (args->result == MIG_TEST_SUCCEED) { - wait_for_serial("src_serial"); - } - - if (args->live) { - migrate_ensure_non_converge(from); - migrate_prepare_for_dirty_mem(from); - } else { - /* - * Testing non-live migration, we allow it to run at - * full speed to ensure short test case duration. - * For tests expected to fail, we don't need to - * change anything. - */ - if (args->result == MIG_TEST_SUCCEED) { - qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); - if (!got_src_stop) { - qtest_qmp_eventwait(from, "STOP"); - } - migrate_ensure_converge(from); - } - } - - if (!args->connect_uri) { - g_autofree char *local_connect_uri = - migrate_get_socket_address(to, "socket-address"); - migrate_qmp(from, local_connect_uri, "{}"); - } else { - migrate_qmp(from, args->connect_uri, "{}"); - } - - - if (args->result != MIG_TEST_SUCCEED) { - bool allow_active = args->result == MIG_TEST_FAIL; - wait_for_migration_fail(from, allow_active); - - if (args->result == MIG_TEST_FAIL_DEST_QUIT_ERR) { - qtest_set_expected_status(to, EXIT_FAILURE); - } - } else { - 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); - - if (!got_src_stop) { - qtest_qmp_eventwait(from, "STOP"); - } - } else { - 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'}"); - } - - if (!got_dst_resume) { - qtest_qmp_eventwait(to, "RESUME"); - } - - wait_for_serial("dest_serial"); - } - - if (args->finish_hook) { - args->finish_hook(from, to, data_hook); - } - - test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); -} - -static void test_precopy_unix_plain(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - 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_dirty_ring(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .start = { - .use_dirty_ring = true, - }, - .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); -} - -#ifdef CONFIG_GNUTLS -static void test_precopy_unix_tls_psk(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, - }; - - test_precopy_common(&args); -} - -#ifdef CONFIG_TASN1 -static void test_precopy_unix_tls_x509_default_host(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .connect_uri = uri, - .listen_uri = uri, - .start_hook = test_migrate_tls_x509_start_default_host, - .finish_hook = test_migrate_tls_x509_finish, - .result = MIG_TEST_FAIL_DEST_QUIT_ERR, - }; - - test_precopy_common(&args); -} - -static void test_precopy_unix_tls_x509_override_host(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = test_migrate_tls_x509_start_override_host, - .finish_hook = test_migrate_tls_x509_finish, - }; - - test_precopy_common(&args); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - -#if 0 -/* Currently upset on aarch64 TCG */ -static void test_ignore_shared(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - QTestState *from, *to; - - if (test_migrate_start(&from, &to, uri, false, true, NULL, NULL)) { - 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_wait_for_dirty_mem(from, to); - - if (!got_src_stop) { - qtest_qmp_eventwait(from, "STOP"); - } - - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - /* Check whether shared RAM has been really skipped */ - g_assert_cmpint(read_ram_property_int(from, "transferred"), <, 1024 * 1024); - - test_migrate_end(from, to, true); -} -#endif - -static void * -test_migrate_xbzrle_start(QTestState *from, - QTestState *to) -{ - migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); - - migrate_set_capability(from, "xbzrle", true); - migrate_set_capability(to, "xbzrle", true); - - return NULL; -} - -static void test_precopy_unix_xbzrle(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = test_migrate_xbzrle_start, - .iterations = 2, - /* - * XBZRLE needs pages to be modified when doing the 2nd+ round - * iteration to have real data pushed to the stream. - */ - .live = true, - }; - - test_precopy_common(&args); -} - -static void test_precopy_unix_compress(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = test_migrate_compress_start, - /* - * Test that no invalid thread state is left over from - * the previous iteration. - */ - .iterations = 2, - /* - * We make sure the compressor can always work well even if guest - * memory is changing. See commit 34ab9e9743 where we used to fix - * a bug when only trigger-able with guest memory changing. - */ - .live = true, - }; - - test_precopy_common(&args); -} - -static void test_precopy_unix_compress_nowait(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = test_migrate_compress_nowait_start, - /* - * Test that no invalid thread state is left over from - * the previous iteration. - */ - .iterations = 2, - /* Same reason for the wait version of precopy compress test */ - .live = true, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_plain(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - }; - - 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) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_psk_mismatch(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_psk_start_mismatch, - .finish_hook = test_migrate_tls_psk_finish, - .result = MIG_TEST_FAIL, - }; - - test_precopy_common(&args); -} - -#ifdef CONFIG_TASN1 -static void test_precopy_tcp_tls_x509_default_host(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_default_host, - .finish_hook = test_migrate_tls_x509_finish, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_override_host(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_override_host, - .finish_hook = test_migrate_tls_x509_finish, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_mismatch_host(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_mismatch_host, - .finish_hook = test_migrate_tls_x509_finish, - .result = MIG_TEST_FAIL_DEST_QUIT_ERR, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_friendly_client(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_friendly_client, - .finish_hook = test_migrate_tls_x509_finish, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_hostile_client(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_hostile_client, - .finish_hook = test_migrate_tls_x509_finish, - .result = MIG_TEST_FAIL, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_allow_anon_client(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_allow_anon_client, - .finish_hook = test_migrate_tls_x509_finish, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_reject_anon_client(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_reject_anon_client, - .finish_hook = test_migrate_tls_x509_finish, - .result = MIG_TEST_FAIL, - }; - - test_precopy_common(&args); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - -#ifndef _WIN32 -static void *test_migrate_fd_start_hook(QTestState *from, - QTestState *to) -{ - int ret; - int pair[2]; - - /* Create two connected sockets for migration */ - ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); - g_assert_cmpint(ret, ==, 0); - - /* Send the 1st socket to the target */ - 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 */ - qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," - " 'arguments': { 'uri': 'fd:fd-mig' }}"); - - /* Send the 2nd socket to the target */ - qtest_qmp_fds_assert_success(from, &pair[1], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - close(pair[1]); - - return NULL; -} - -static void test_migrate_fd_finish_hook(QTestState *from, - QTestState *to, - void *opaque) -{ - QDict *rsp; - const char *error_desc; - - /* 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' }}"); - 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' }}"); - 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) -{ - MigrateCommon args = { - .listen_uri = "defer", - .connect_uri = "fd:fd-mig", - .start_hook = test_migrate_fd_start_hook, - .finish_hook = test_migrate_fd_finish_hook - }; - test_precopy_common(&args); -} -#endif /* _WIN32 */ - -static void do_test_validate_uuid(MigrateStart *args, bool should_fail) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - QTestState *from, *to; - - if (test_migrate_start(&from, &to, uri, args)) { - return; - } - - /* - * UUID validation is at the begin of migration. So, the main process of - * migration is not interesting for us here. Thus, set huge downtime for - * very fast migration. - */ - migrate_set_parameter_int(from, "downtime-limit", 1000000); - migrate_set_capability(from, "validate-uuid", true); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, uri, "{}"); - - if (should_fail) { - qtest_set_expected_status(to, EXIT_FAILURE); - wait_for_migration_fail(from, true); - } else { - wait_for_migration_complete(from); - } - - test_migrate_end(from, to, false); -} - -static void test_validate_uuid(void) -{ - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - .opts_target = "-uuid 11111111-1111-1111-1111-111111111111", - }; - - do_test_validate_uuid(&args, false); -} - -static void test_validate_uuid_error(void) -{ - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", - .hide_stderr = true, - }; - - do_test_validate_uuid(&args, true); -} - -static void test_validate_uuid_src_not_set(void) -{ - MigrateStart args = { - .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", - .hide_stderr = true, - }; - - do_test_validate_uuid(&args, false); -} - -static void test_validate_uuid_dst_not_set(void) -{ - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - .hide_stderr = true, - }; - - do_test_validate_uuid(&args, false); -} - -/* - * The way auto_converge works, we need to do too many passes to - * run this test. Auto_converge logic is only run once every - * three iterations, so: - * - * - 3 iterations without auto_converge enabled - * - 3 iterations with pct = 5 - * - 3 iterations with pct = 30 - * - 3 iterations with pct = 55 - * - 3 iterations with pct = 80 - * - 3 iterations with pct = 95 (max(95, 80 + 25)) - * - * To make things even worse, we need to run the initial stage at - * 3MB/s so we enter autoconverge even when host is (over)loaded. - */ -static void test_migrate_auto_converge(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateStart args = {}; - QTestState *from, *to; - int64_t percentage; - - /* - * We want the test to be stable and as fast as possible. - * 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 = 25, max_pct = 95; - - if (test_migrate_start(&from, &to, uri, &args)) { - return; - } - - migrate_set_capability(from, "auto-converge", true); - migrate_set_parameter_int(from, "cpu-throttle-initial", init_pct); - migrate_set_parameter_int(from, "cpu-throttle-increment", inc_pct); - migrate_set_parameter_int(from, "max-cpu-throttle", max_pct); - - /* - * Set the initial parameters so that the migration could not converge - * without throttling. - */ - migrate_ensure_non_converge(from); - - /* To check remaining size after precopy */ - migrate_set_capability(from, "pause-before-switchover", true); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, uri, "{}"); - - /* Wait for throttling begins */ - percentage = 0; - do { - percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); - if (percentage != 0) { - break; - } - usleep(20); - g_assert_false(got_src_stop); - } while (true); - /* The first percentage of throttling should be at least init_pct */ - g_assert_cmpint(percentage, >=, init_pct); - /* Now, when we tested that throttling works, let it converge */ - migrate_ensure_converge(from); - - /* - * Wait for pre-switchover status to check last throttle percentage - * and remaining. These values will be zeroed later - */ - wait_for_migration_status(from, "pre-switchover", NULL); - - /* The final percentage of throttling shouldn't be greater than max_pct */ - percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); - g_assert_cmpint(percentage, <=, max_pct); - 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 void * -test_migrate_precopy_tcp_multifd_start_common(QTestState *from, - QTestState *to, - const char *method) -{ - migrate_set_parameter_int(from, "multifd-channels", 16); - migrate_set_parameter_int(to, "multifd-channels", 16); - - migrate_set_parameter_str(from, "multifd-compression", method); - migrate_set_parameter_str(to, "multifd-compression", method); - - migrate_set_capability(from, "multifd", true); - migrate_set_capability(to, "multifd", true); - - /* Start incoming migration from the 1st socket */ - qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," - " 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}"); - - return NULL; -} - -static void * -test_migrate_precopy_tcp_multifd_start(QTestState *from, - QTestState *to) -{ - return test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); -} - -static void * -test_migrate_precopy_tcp_multifd_zlib_start(QTestState *from, - QTestState *to) -{ - return test_migrate_precopy_tcp_multifd_start_common(from, to, "zlib"); -} - -#ifdef CONFIG_ZSTD -static void * -test_migrate_precopy_tcp_multifd_zstd_start(QTestState *from, - QTestState *to) -{ - return test_migrate_precopy_tcp_multifd_start_common(from, to, "zstd"); -} -#endif /* CONFIG_ZSTD */ - -static void test_multifd_tcp_none(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_start, - /* - * Multifd is more complicated than most of the features, it - * directly takes guest page buffers when sending, make sure - * everything will work alright even if guest page is changing. - */ - .live = true, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_zlib(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_zlib_start, - }; - test_precopy_common(&args); -} - -#ifdef CONFIG_ZSTD -static void test_multifd_tcp_zstd(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_zstd_start, - }; - test_precopy_common(&args); -} -#endif - -#ifdef CONFIG_GNUTLS -static void * -test_migrate_multifd_tcp_tls_psk_start_match(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_psk_start_match(from, to); -} - -static void * -test_migrate_multifd_tcp_tls_psk_start_mismatch(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_psk_start_mismatch(from, to); -} - -#ifdef CONFIG_TASN1 -static void * -test_migrate_multifd_tls_x509_start_default_host(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_default_host(from, to); -} - -static void * -test_migrate_multifd_tls_x509_start_override_host(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_override_host(from, to); -} - -static void * -test_migrate_multifd_tls_x509_start_mismatch_host(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_mismatch_host(from, to); -} - -static void * -test_migrate_multifd_tls_x509_start_allow_anon_client(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_allow_anon_client(from, to); -} - -static void * -test_migrate_multifd_tls_x509_start_reject_anon_client(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_reject_anon_client(from, to); -} -#endif /* CONFIG_TASN1 */ - -static void test_multifd_tcp_tls_psk_match(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_multifd_tcp_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_psk_mismatch(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .start_hook = test_migrate_multifd_tcp_tls_psk_start_mismatch, - .finish_hook = test_migrate_tls_psk_finish, - .result = MIG_TEST_FAIL, - }; - test_precopy_common(&args); -} - -#ifdef CONFIG_TASN1 -static void test_multifd_tcp_tls_x509_default_host(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_default_host, - .finish_hook = test_migrate_tls_x509_finish, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_override_host(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_override_host, - .finish_hook = test_migrate_tls_x509_finish, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_mismatch_host(void) -{ - /* - * This has different behaviour to the non-multifd case. - * - * In non-multifd case when client aborts due to mismatched - * cert host, the server has already started trying to load - * migration state, and so it exits with I/O failure. - * - * In multifd case when client aborts due to mismatched - * cert host, the server is still waiting for the other - * multifd connections to arrive so hasn't started trying - * to load migration state, and thus just aborts the migration - * without exiting. - */ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_mismatch_host, - .finish_hook = test_migrate_tls_x509_finish, - .result = MIG_TEST_FAIL, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_allow_anon_client(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_allow_anon_client, - .finish_hook = test_migrate_tls_x509_finish, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_reject_anon_client(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_reject_anon_client, - .finish_hook = test_migrate_tls_x509_finish, - .result = MIG_TEST_FAIL, - }; - test_precopy_common(&args); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - -/* - * This test does: - * source target - * migrate_incoming - * migrate - * migrate_cancel - * launch another target - * migrate - * - * And see that it works - */ -static void test_multifd_tcp_cancel(void) -{ - MigrateStart args = { - .hide_stderr = true, - }; - QTestState *from, *to, *to2; - 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); - - migrate_set_capability(from, "multifd", true); - migrate_set_capability(to, "multifd", true); - - /* Start incoming migration from the 1st socket */ - qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," - " 'arguments': { 'uri': '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, uri, "{}"); - - migrate_wait_for_dirty_mem(from, to); - - migrate_cancel(from); - - /* Make sure QEMU process "to" exited */ - qtest_set_expected_status(to, EXIT_FAILURE); - qtest_wait_qemu(to); - - args = (MigrateStart){ - .only_target = true, - }; - - if (test_migrate_start(&from, &to2, "defer", &args)) { - return; - } - - migrate_set_parameter_int(to2, "multifd-channels", 16); - - migrate_set_capability(to2, "multifd", true); - - /* Start incoming migration from the 1st socket */ - qtest_qmp_assert_success(to2, "{ 'execute': 'migrate-incoming'," - " 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}"); - - g_free(uri); - uri = migrate_get_socket_address(to2, "socket-address"); - - wait_for_migration_status(from, "cancelled", NULL); - - migrate_ensure_non_converge(from); - - migrate_qmp(from, uri, "{}"); - - migrate_wait_for_dirty_mem(from, to2); - - migrate_ensure_converge(from); - - if (!got_src_stop) { - qtest_qmp_eventwait(from, "STOP"); - } - qtest_qmp_eventwait(to2, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - test_migrate_end(from, to2, true); -} - -static void calc_dirty_rate(QTestState *who, uint64_t calc_time) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'calc-dirty-rate'," - "'arguments': { " - "'calc-time': %" PRIu64 "," - "'mode': 'dirty-ring' }}", - calc_time); -} - -static QDict *query_dirty_rate(QTestState *who) -{ - return qtest_qmp_assert_success_ref(who, - "{ 'execute': 'query-dirty-rate' }"); -} - -static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'set-vcpu-dirty-limit'," - "'arguments': { " - "'dirty-rate': %" PRIu64 " } }", - dirtyrate); -} - -static void cancel_vcpu_dirty_limit(QTestState *who) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'cancel-vcpu-dirty-limit' }"); -} - -static QDict *query_vcpu_dirty_limit(QTestState *who) -{ - QDict *rsp; - - rsp = qtest_qmp(who, "{ 'execute': 'query-vcpu-dirty-limit' }"); - g_assert(!qdict_haskey(rsp, "error")); - g_assert(qdict_haskey(rsp, "return")); - - return rsp; -} - -static bool calc_dirtyrate_ready(QTestState *who) -{ - QDict *rsp_return; - gchar *status; - - rsp_return = query_dirty_rate(who); - g_assert(rsp_return); - - status = g_strdup(qdict_get_str(rsp_return, "status")); - g_assert(status); - - return g_strcmp0(status, "measuring"); -} - -static void wait_for_calc_dirtyrate_complete(QTestState *who, - int64_t time_s) -{ - int max_try_count = 10000; - usleep(time_s * 1000000); - - while (!calc_dirtyrate_ready(who) && max_try_count--) { - usleep(1000); - } - - /* - * Set the timeout with 10 s(max_try_count * 1000us), - * if dirtyrate measurement not complete, fail test. - */ - g_assert_cmpint(max_try_count, !=, 0); -} - -static int64_t get_dirty_rate(QTestState *who) -{ - QDict *rsp_return; - gchar *status; - QList *rates; - const QListEntry *entry; - QDict *rate; - int64_t dirtyrate; - - rsp_return = query_dirty_rate(who); - g_assert(rsp_return); - - status = g_strdup(qdict_get_str(rsp_return, "status")); - g_assert(status); - g_assert_cmpstr(status, ==, "measured"); - - rates = qdict_get_qlist(rsp_return, "vcpu-dirty-rate"); - g_assert(rates && !qlist_empty(rates)); - - entry = qlist_first(rates); - g_assert(entry); - - rate = qobject_to(QDict, qlist_entry_obj(entry)); - g_assert(rate); - - dirtyrate = qdict_get_try_int(rate, "dirty-rate", -1); - - qobject_unref(rsp_return); - return dirtyrate; -} - -static int64_t get_limit_rate(QTestState *who) -{ - QDict *rsp_return; - QList *rates; - const QListEntry *entry; - QDict *rate; - int64_t dirtyrate; - - rsp_return = query_vcpu_dirty_limit(who); - g_assert(rsp_return); - - rates = qdict_get_qlist(rsp_return, "return"); - g_assert(rates && !qlist_empty(rates)); - - entry = qlist_first(rates); - g_assert(entry); - - rate = qobject_to(QDict, qlist_entry_obj(entry)); - g_assert(rate); - - dirtyrate = qdict_get_try_int(rate, "limit-rate", -1); - - qobject_unref(rsp_return); - return dirtyrate; -} - -static QTestState *dirtylimit_start_vm(void) -{ - QTestState *vm = NULL; - g_autofree gchar *cmd = NULL; - 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)); - - cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 " - "-name dirtylimit-test,debug-threads=on " - "-m 150M -smp 1 " - "-serial file:%s/vm_serial " - "-drive file=%s,format=raw ", - tmpfs, bootpath); - - vm = qtest_init(cmd); - return vm; -} - -static void dirtylimit_stop_vm(QTestState *vm) -{ - qtest_quit(vm); - cleanup("bootsect"); - cleanup("vm_serial"); -} - -static void test_vcpu_dirty_limit(void) +static void parse_args(int *argc_p, char ***argv_p, bool *full_set) { - QTestState *vm; - int64_t origin_rate; - int64_t quota_rate; - int64_t rate ; - int max_try_count = 20; - int hit = 0; - - /* Start vm for vcpu dirtylimit test */ - vm = dirtylimit_start_vm(); - - /* Wait for the first serial output from the vm*/ - wait_for_serial("vm_serial"); - - /* Do dirtyrate measurement with calc time equals 1s */ - calc_dirty_rate(vm, 1); - - /* Sleep calc time and wait for calc dirtyrate complete */ - wait_for_calc_dirtyrate_complete(vm, 1); - - /* Query original dirty page rate */ - origin_rate = get_dirty_rate(vm); - - /* VM booted from bootsect should dirty memory steadily */ - assert(origin_rate != 0); + int argc = *argc_p; + char **argv = *argv_p; + int i, j; - /* Setup quota dirty page rate at half of origin */ - quota_rate = origin_rate / 2; - - /* Set dirtylimit */ - dirtylimit_set_all(vm, quota_rate); - - /* - * Check if set-vcpu-dirty-limit and query-vcpu-dirty-limit - * works literally - */ - g_assert_cmpint(quota_rate, ==, get_limit_rate(vm)); - - /* Sleep a bit to check if it take effect */ - usleep(2000000); - - /* - * Check if dirtylimit take effect realistically, set the - * timeout with 20 s(max_try_count * 1s), if dirtylimit - * doesn't take effect, fail test. - */ - while (--max_try_count) { - calc_dirty_rate(vm, 1); - wait_for_calc_dirtyrate_complete(vm, 1); - rate = get_dirty_rate(vm); - - /* - * Assume hitting if current rate is less - * than quota rate (within accepting error) - */ - if (rate < (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { - hit = 1; - break; + j = 1; + for (i = 1; i < argc; i++) { + if (g_str_equal(argv[i], "--full")) { + *full_set = true; + continue; } - } - - g_assert_cmpint(hit, ==, 1); - - hit = 0; - max_try_count = 20; - - /* Check if dirtylimit cancellation take effect */ - cancel_vcpu_dirty_limit(vm); - while (--max_try_count) { - calc_dirty_rate(vm, 1); - wait_for_calc_dirtyrate_complete(vm, 1); - rate = get_dirty_rate(vm); - - /* - * Assume dirtylimit be canceled if current rate is - * greater than quota rate (within accepting error) - */ - if (rate > (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { - hit = 1; - break; + argv[j++] = argv[i]; + if (i >= j) { + argv[i] = NULL; } } - - g_assert_cmpint(hit, ==, 1); - dirtylimit_stop_vm(vm); -} - -static bool kvm_dirty_ring_supported(void) -{ -#if defined(__linux__) && defined(HOST_X86_64) - int ret, kvm_fd = open("/dev/kvm", O_RDONLY); - - if (kvm_fd < 0) { - return false; - } - - ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING); - close(kvm_fd); - - /* We test with 4096 slots */ - if (ret < 4096) { - return false; - } - - return true; -#else - return false; -#endif + *argc_p = j; } int main(int argc, char **argv) { - bool has_kvm, has_tcg; - bool has_uffd; - const char *arch; - g_autoptr(GError) err = NULL; + MigrationTestEnv *env; int ret; + bool full_set = false; - 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; - } - - has_uffd = ufd_version_check(); - arch = qtest_get_arch(); - - /* - * On ppc64, the test only works with kvm-hv, but not with kvm-pr and TCG - * is touchy due to race conditions on dirty bits (especially on PPC for - * some reason) - */ - if (g_str_equal(arch, "ppc64") && - (!has_kvm || access("/sys/module/kvm_hv", F_OK))) { - g_test_message("Skipping test: kvm_hv not available"); - return g_test_run(); - } - - /* - * 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(); - } - - tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); - if (!tmpfs) { - g_test_message("Can't create temporary directory in %s: %s", - g_get_tmp_dir(), err->message); - } - g_assert(tmpfs); + /* strip the --full option if it's present */ + parse_args(&argc, &argv, &full_set); + g_test_init(&argc, &argv, NULL); + env = migration_get_env(); + env->full_set = full_set; 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); - if (getenv("QEMU_TEST_FLAKY_TESTS")) { - qtest_add_func("/migration/postcopy/compress/plain", - test_postcopy_compress); - qtest_add_func("/migration/postcopy/recovery/compress/plain", - test_postcopy_recovery_compress); - } - } - - 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); - /* - * Compression fails from time to time. - * Put test here but don't enable it until everything is fixed. - */ - if (getenv("QEMU_TEST_FLAKY_TESTS")) { - qtest_add_func("/migration/precopy/unix/compress/wait", - test_precopy_unix_compress); - qtest_add_func("/migration/precopy/unix/compress/nowait", - test_precopy_unix_compress_nowait); - } -#ifdef CONFIG_GNUTLS - qtest_add_func("/migration/precopy/unix/tls/psk", - test_precopy_unix_tls_psk); - - if (has_uffd) { - /* - * NOTE: psk test is enough for postcopy, as other types of TLS - * channels are tested under precopy. Here what we want to test is the - * general postcopy path that has TLS channel enabled. - */ - 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); - } -#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); -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - - qtest_add_func("/migration/precopy/tcp/plain", test_precopy_tcp_plain); - - qtest_add_func("/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); -#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); -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - - /* qtest_add_func("/migration/ignore_shared", test_ignore_shared); */ -#ifndef _WIN32 - qtest_add_func("/migration/fd_proto", test_migrate_fd_proto); -#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); - /* - * See explanation why this test is slow on function definition - */ - if (g_test_slow()) { - qtest_add_func("/migration/auto_converge", test_migrate_auto_converge); - } - qtest_add_func("/migration/multifd/tcp/plain/none", - test_multifd_tcp_none); - /* - * This test is flaky and sometimes fails in CI and otherwise: - * don't run unless user opts in via environment variable. - */ - if (getenv("QEMU_TEST_FLAKY_TESTS")) { - qtest_add_func("/migration/multifd/tcp/plain/cancel", - test_multifd_tcp_cancel); - } - qtest_add_func("/migration/multifd/tcp/plain/zlib", - test_multifd_tcp_zlib); -#ifdef CONFIG_ZSTD - qtest_add_func("/migration/multifd/tcp/plain/zstd", - test_multifd_tcp_zstd); -#endif -#ifdef CONFIG_GNUTLS - qtest_add_func("/migration/multifd/tcp/tls/psk/match", - test_multifd_tcp_tls_psk_match); - qtest_add_func("/migration/multifd/tcp/tls/psk/mismatch", - test_multifd_tcp_tls_psk_mismatch); -#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); -#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_tls(env); + migration_test_add_compression(env); + migration_test_add_postcopy(env); + migration_test_add_file(env); + migration_test_add_precopy(env); + migration_test_add_cpr(env); + migration_test_add_misc(env); ret = g_test_run(); g_assert_cmpint(ret, ==, 0); - ret = rmdir(tmpfs); - if (ret != 0) { - g_test_message("unable to rmdir: path (%s): %s", - tmpfs, strerror(errno)); - } - g_free(tmpfs); + ret = migration_env_clean(env); return ret; } diff --git a/tests/qtest/migration/Makefile b/tests/qtest/migration/Makefile new file mode 100644 index 00000000000..2c5ee287ec4 --- /dev/null +++ b/tests/qtest/migration/Makefile @@ -0,0 +1,40 @@ +# +# Copyright (c) 2018 Red Hat, Inc. 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. +# + +TARGET_LIST = i386 aarch64 s390x ppc64 + +SRC_PATH = ../.. + +.PHONY: help $(TARGET_LIST) +help: + @echo "Create migration guest includes. We generate a binary." + @echo "And then convert that binary to an include file that can be" + @echo "run in a guest." + @echo "Possible operations are:" + @echo + @echo " $(MAKE) clean Remove all intermediate files" + @echo " $(MAKE) target Generate for that target" + @echo " $(MAKE) CROSS_PREFIX=... target" + @echo " Cross-compile than target" + @echo " Possible targets are: $(TARGET_LIST)" + +override define __note +/* This file is automatically generated from the assembly file in + * tests/migration/$@. 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. + */ +endef +export __note + +$(TARGET_LIST): + $(MAKE) CROSS_PREFIX=$(CROSS_PREFIX) -C $@ + +clean: + for target in $(TARGET_LIST); do \ + $(MAKE) -C $$target clean; \ + done diff --git a/tests/migration/aarch64/Makefile b/tests/qtest/migration/aarch64/Makefile similarity index 100% rename from tests/migration/aarch64/Makefile rename to tests/qtest/migration/aarch64/Makefile diff --git a/tests/migration/aarch64/a-b-kernel.S b/tests/qtest/migration/aarch64/a-b-kernel.S similarity index 100% rename from tests/migration/aarch64/a-b-kernel.S rename to tests/qtest/migration/aarch64/a-b-kernel.S diff --git a/tests/migration/aarch64/a-b-kernel.h b/tests/qtest/migration/aarch64/a-b-kernel.h similarity index 100% rename from tests/migration/aarch64/a-b-kernel.h rename to tests/qtest/migration/aarch64/a-b-kernel.h diff --git a/tests/qtest/migration/bootfile.c b/tests/qtest/migration/bootfile.c new file mode 100644 index 00000000000..fac059d11d7 --- /dev/null +++ b/tests/qtest/migration/bootfile.c @@ -0,0 +1,70 @@ +/* + * Guest code setup for migration tests + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" + +/* + * 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 "bootfile.h" +#include "i386/a-b-bootblock.h" +#include "aarch64/a-b-kernel.h" +#include "ppc64/a-b-kernel.h" +#include "s390x/a-b-bios.h" + +static char *bootpath; + +void bootfile_delete(void) +{ + if (!bootpath) { + return; + } + unlink(bootpath); + g_free(bootpath); + bootpath = NULL; +} + +char *bootfile_create(const char *arch, const char *dir, bool suspend_me) +{ + 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); + fclose(bootfile); + + return bootpath; +} diff --git a/tests/qtest/migration/bootfile.h b/tests/qtest/migration/bootfile.h new file mode 100644 index 00000000000..6d6a67386e1 --- /dev/null +++ b/tests/qtest/migration/bootfile.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Red Hat, Inc. 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 BOOTFILE_H +#define BOOTFILE_H + +/* Common */ +#define TEST_MEM_PAGE_SIZE 4096 + +/* x86 */ +#define X86_TEST_MEM_START (1 * 1024 * 1024) +#define X86_TEST_MEM_END (100 * 1024 * 1024) + +/* S390 */ +#define S390_TEST_MEM_START (1 * 1024 * 1024) +#define S390_TEST_MEM_END (100 * 1024 * 1024) + +/* 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) +#define ARM_TEST_MEM_END (0x40000000 + 100 * 1024 * 1024) +#define ARM_MACH_VIRT_UART 0x09000000 +/* AArch64 kernel load address is 0x40080000, and the test memory starts at + * 0x40100000. So the maximum allowable kernel size is 512KB. + */ +#define ARM_TEST_MAX_KERNEL_SIZE (512 * 1024) + +void bootfile_delete(void); +char *bootfile_create(const char *arch, const char *dir, bool suspend_me); + +#endif /* BOOTFILE_H */ diff --git a/tests/qtest/migration/compression-tests.c b/tests/qtest/migration/compression-tests.c new file mode 100644 index 00000000000..b827665b8ec --- /dev/null +++ b/tests/qtest/migration/compression-tests.c @@ -0,0 +1,226 @@ +/* + * QTest testcases for migration compression + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "qemu/module.h" + + +static char *tmpfs; + +#ifdef CONFIG_ZSTD +static void * +migrate_hook_start_precopy_tcp_multifd_zstd(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "multifd-zstd-level", 2); + migrate_set_parameter_int(to, "multifd-zstd-level", 2); + + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zstd"); +} + +static void test_multifd_tcp_zstd(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .start_hook = migrate_hook_start_precopy_tcp_multifd_zstd, + }; + test_precopy_common(&args); +} + +static void test_multifd_postcopy_tcp_zstd(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true, + }, + .start_hook = migrate_hook_start_precopy_tcp_multifd_zstd, + }; + + test_precopy_common(&args); +} +#endif /* CONFIG_ZSTD */ + +#ifdef CONFIG_QATZIP +static void * +migrate_hook_start_precopy_tcp_multifd_qatzip(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "multifd-qatzip-level", 2); + migrate_set_parameter_int(to, "multifd-qatzip-level", 2); + + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qatzip"); +} + +static void test_multifd_tcp_qatzip(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .start_hook = migrate_hook_start_precopy_tcp_multifd_qatzip, + }; + test_precopy_common(&args); +} +#endif + +#ifdef CONFIG_QPL +static void * +migrate_hook_start_precopy_tcp_multifd_qpl(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qpl"); +} + +static void test_multifd_tcp_qpl(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .start_hook = migrate_hook_start_precopy_tcp_multifd_qpl, + }; + test_precopy_common(&args); +} +#endif /* CONFIG_QPL */ + +#ifdef CONFIG_UADK +static void * +migrate_hook_start_precopy_tcp_multifd_uadk(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "uadk"); +} + +static void test_multifd_tcp_uadk(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .start_hook = migrate_hook_start_precopy_tcp_multifd_uadk, + }; + test_precopy_common(&args); +} +#endif /* CONFIG_UADK */ + +static void * +migrate_hook_start_xbzrle(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); + return NULL; +} + +static void test_precopy_unix_xbzrle(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_xbzrle, + .iterations = 2, + .start = { + .caps[MIGRATION_CAPABILITY_XBZRLE] = true, + }, + /* + * 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 * +migrate_hook_start_precopy_tcp_multifd_zlib(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 migrate_hook_start_precopy_tcp_multifd_common(from, to, "zlib"); +} + +static void test_multifd_tcp_zlib(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .start_hook = migrate_hook_start_precopy_tcp_multifd_zlib, + }; + test_precopy_common(&args); +} + +static void migration_test_add_compression_smoke(MigrationTestEnv *env) +{ + migration_test_add("/migration/multifd/tcp/plain/zlib", + test_multifd_tcp_zlib); +} + +void migration_test_add_compression(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add_compression_smoke(env); + + if (!env->full_set) { + return; + } + +#ifdef CONFIG_ZSTD + migration_test_add("/migration/multifd/tcp/plain/zstd", + test_multifd_tcp_zstd); + if (env->has_uffd) { + migration_test_add("/migration/multifd+postcopy/tcp/plain/zstd", + test_multifd_postcopy_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 + + if (g_test_slow()) { + migration_test_add("/migration/precopy/unix/xbzrle", + test_precopy_unix_xbzrle); + } +} diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c new file mode 100644 index 00000000000..5e764a67876 --- /dev/null +++ b/tests/qtest/migration/cpr-tests.c @@ -0,0 +1,136 @@ +/* + * QTest testcases for CPR + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" + + +static char *tmpfs; + +static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to) +{ + migrate_set_parameter_str(from, "mode", "cpr-reboot"); + migrate_set_parameter_str(to, "mode", "cpr-reboot"); + + 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 = migrate_hook_start_mode_reboot, + .start = { + .caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true, + }, + }; + + test_file_common(&args, true); +} + +static void *test_mode_transfer_start(QTestState *from, QTestState *to) +{ + migrate_set_parameter_str(from, "mode", "cpr-transfer"); + return NULL; +} + +/* + * cpr-transfer mode cannot use the target monitor prior to starting the + * migration, and cannot connect synchronously to the monitor, so defer + * the target connection. + */ +static void test_mode_transfer_common(bool incoming_defer) +{ + g_autofree char *cpr_path = g_strdup_printf("%s/cpr.sock", tmpfs); + g_autofree char *mig_path = g_strdup_printf("%s/migsocket", tmpfs); + g_autofree char *uri = g_strdup_printf("unix:%s", mig_path); + g_autofree char *opts_target = NULL; + + const char *opts = "-machine aux-ram-share=on -nodefaults"; + g_autofree const char *cpr_channel = g_strdup_printf( + "cpr,addr.transport=socket,addr.type=unix,addr.path=%s", + cpr_path); + + g_autofree char *connect_channels = g_strdup_printf( + "[ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'unix'," + " 'path': '%s' } } ]", + mig_path); + + /* + * Set up a UNIX domain socket for the CPR channel before + * launching the destination VM, to avoid timing issues + * during connection setup. + */ + int cpr_sockfd = qtest_socket_server(cpr_path); + g_assert(cpr_sockfd >= 0); + + opts_target = g_strdup_printf("-incoming cpr,addr.transport=socket," + "addr.type=fd,addr.str=%d %s", + cpr_sockfd, opts); + MigrateCommon args = { + .start.opts_source = opts, + .start.opts_target = opts_target, + .start.defer_target_connect = true, + .start.memory_backend = "-object memory-backend-memfd,id=pc.ram,size=%s" + " -machine memory-backend=pc.ram", + .listen_uri = incoming_defer ? "defer" : uri, + .connect_channels = connect_channels, + .cpr_channel = cpr_channel, + .start_hook = test_mode_transfer_start, + }; + + test_precopy_common(&args); +} + +static void test_mode_transfer(void) +{ + test_mode_transfer_common(NULL); +} + +static void test_mode_transfer_defer(void) +{ + test_mode_transfer_common(true); +} + +void migration_test_add_cpr(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + /* no tests in the smoke set for now */ + + if (!env->full_set) { + return; + } + + /* + * 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); + } + + if (env->has_kvm) { + migration_test_add("/migration/mode/transfer", test_mode_transfer); + migration_test_add("/migration/mode/transfer/defer", + test_mode_transfer_defer); + } +} diff --git a/tests/qtest/migration/file-tests.c b/tests/qtest/migration/file-tests.c new file mode 100644 index 00000000000..4d78ce08556 --- /dev/null +++ b/tests/qtest/migration/file-tests.c @@ -0,0 +1,341 @@ +/* + * QTest testcases for migration to file + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "qobject/qlist.h" + + +static char *tmpfs; + +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 *migrate_hook_start_file_offset_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, 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 = migrate_hook_start_file_offset_fdset, + }; + + 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_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 = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + }, + }; + + 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 = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + }, + }; + + test_file_common(&args, true); +} + +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 = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + }, + }; + + 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 = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + }, + }; + + test_file_common(&args, true); +} + +static void *migrate_hook_start_multifd_mapped_ram_dio(QTestState *from, + QTestState *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 = migrate_hook_start_multifd_mapped_ram_dio, + .start = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + + 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 migrate_hook_end_multifd_mapped_ram_fdset(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 *migrate_hook_start_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_set_parameter_bool(from, "direct-io", true); + migrate_set_parameter_bool(to, "direct-io", true); + + return NULL; +} + +static void *migrate_hook_start_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); + + 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 = migrate_hook_start_multifd_mapped_ram_fdset, + .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, + .start = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + + 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 = migrate_hook_start_multifd_mapped_ram_fdset_dio, + .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, + .start = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + + 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 migration_test_add_file_smoke(MigrationTestEnv *env) +{ + migration_test_add("/migration/precopy/file", + test_precopy_file); + + migration_test_add("/migration/multifd/file/mapped-ram/dio", + test_multifd_file_mapped_ram_dio); +} + +void migration_test_add_file(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add_file_smoke(env); + + if (!env->full_set) { + return; + } + + 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); + + 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); + +#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 +} diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c new file mode 100644 index 00000000000..407c9023c05 --- /dev/null +++ b/tests/qtest/migration/framework.c @@ -0,0 +1,1066 @@ +/* + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" + +#include "chardev/char.h" +#include "crypto/tlscredspsk.h" +#include "libqtest.h" +#include "migration/bootfile.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "ppc-util.h" +#include "qapi/error.h" +#include "qobject/qjson.h" +#include "qobject/qlist.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/range.h" +#include "qemu/sockets.h" + + +#define QEMU_VM_FILE_MAGIC 0x5145564d +#define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC" +#define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST" +#define MULTIFD_TEST_CHANNELS 4 + +unsigned start_address; +unsigned end_address; +static QTestMigrationState src_state; +static QTestMigrationState dst_state; +static char *tmpfs; + +/* + * 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 + + +/* + * 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 (unless we enabled suspend/resume) + */ +void wait_for_serial(const char *side) +{ + g_autofree char *serialpath = g_strdup_printf("%s/%s", tmpfs, side); + FILE *serialfile = fopen(serialpath, "r"); + + do { + int readvalue = fgetc(serialfile); + + switch (readvalue) { + case 'A': + /* Fine */ + break; + + case 'B': + /* It's alive! */ + fclose(serialfile); + return; + + case EOF: + fseek(serialfile, 0, SEEK_SET); + usleep(1000); + break; + + default: + fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side); + g_assert_not_reached(); + } + } while (true); +} + +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); +} + +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 check_guests_ram(QTestState *who) +{ + /* + * Our ASM test will have been incrementing one byte from each page from + * start_address to < end_address in order. This gives us a constraint + * that any page's byte should be equal or less than the previous pages + * byte (mod 256); and they should all be equal except for one transition + * at the point where we meet the incrementer. (We're running this with + * the guest stopped). + */ + unsigned address; + uint8_t first_byte; + uint8_t last_byte; + bool hit_edge = false; + int bad = 0; + + qtest_memread(who, start_address, &first_byte, 1); + last_byte = first_byte; + + for (address = start_address + TEST_MEM_PAGE_SIZE; address < end_address; + address += TEST_MEM_PAGE_SIZE) + { + uint8_t b; + qtest_memread(who, address, &b, 1); + if (b != last_byte) { + if (((b + 1) % 256) == last_byte && !hit_edge) { + /* + * This is OK, the guest stopped at the point of + * incrementing the previous page but didn't get + * to us yet. + */ + hit_edge = true; + last_byte = b; + } else { + bad++; + if (bad <= 10) { + fprintf(stderr, "Memory content inconsistency at %x" + " first_byte = %x last_byte = %x current = %x" + " hit_edge = %x\n", + address, first_byte, last_byte, b, hit_edge); + } + } + } + } + if (bad >= 10) { + fprintf(stderr, "and in another %d pages", bad - 10); + } + g_assert(bad == 0); +} + +static void cleanup(const char *filename) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, filename); + + unlink(path); +} + +static QList *migrate_start_get_qmp_capabilities(const MigrateStart *args) +{ + QList *capabilities = NULL; + + if (args->oob) { + capabilities = qlist_new(); + qlist_append_str(capabilities, "oob"); + } + return capabilities; +} + +static void migrate_start_set_capabilities(QTestState *from, QTestState *to, + MigrateStart *args) +{ + /* + * MigrationCapability_lookup and MIGRATION_CAPABILITY_ constants + * are from qapi-types-migration.h. + */ + for (uint8_t i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { + if (!args->caps[i]) { + continue; + } + if (from) { + migrate_set_capability(from, + MigrationCapability_lookup.array[i], true); + } + if (to) { + migrate_set_capability(to, + MigrationCapability_lookup.array[i], true); + } + } + + /* + * Always enable migration events. Libvirt always uses it, let's try + * to mimic as closer as that. + */ + migrate_set_capability(from, "events", true); + if (!args->defer_target_connect) { + migrate_set_capability(to, "events", true); + } + + /* + * Default number of channels should be fine for most + * tests. Individual tests can override by calling + * migrate_set_parameter() directly. + */ + if (args->caps[MIGRATION_CAPABILITY_MULTIFD]) { + migrate_set_parameter_int(from, "multifd-channels", + MULTIFD_TEST_CHANNELS); + migrate_set_parameter_int(to, "multifd-channels", + MULTIFD_TEST_CHANNELS); + } + + return; +} + +int migrate_start(QTestState **from, QTestState **to, const char *uri, + MigrateStart *args) +{ + /* 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 *shmem_opts = NULL; + g_autofree char *shmem_path = NULL; + const char *kvm_opts = NULL; + const char *arch = qtest_get_arch(); + const char *memory_size; + const char *machine_alias, *machine_opts = ""; + g_autofree char *machine = NULL; + const char *bootpath; + g_autoptr(QList) capabilities = migrate_start_get_qmp_capabilities(args); + g_autofree char *memory_backend = NULL; + const char *events; + + if (args->use_shmem) { + if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { + g_test_skip("/dev/shm is not supported"); + return -1; + } + } + + dst_state = (QTestMigrationState) { }; + src_state = (QTestMigrationState) { }; + bootpath = bootfile_create(arch, tmpfs, args->suspend_me); + src_state.suspend_me = args->suspend_me; + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + memory_size = "150M"; + + 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")) { + memory_size = "128M"; + 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) { + memory_size = "256M"; + start_address = PPC_TEST_MEM_START; + end_address = PPC_TEST_MEM_END; + 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) { + memory_size = "150M"; + 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; + } else { + g_assert_not_reached(); + } + + if (!getenv("QTEST_LOG") && args->hide_stderr) { +#ifndef _WIN32 + ignore_stderr = "2>/dev/null"; +#else + /* + * On Windows the QEMU executable is created via CreateProcess() and + * IO redirection does not work, so don't bother adding IO redirection + * to the command line. + */ + ignore_stderr = ""; +#endif + } else { + ignore_stderr = ""; + } + + if (args->use_shmem) { + shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); + shmem_opts = g_strdup_printf( + "-object memory-backend-file,id=mem0,size=%s" + ",mem-path=%s,share=on -numa node,memdev=mem0", + memory_size, shmem_path); + } + + if (args->memory_backend) { + memory_backend = g_strdup_printf(args->memory_backend, memory_size); + } else { + memory_backend = g_strdup_printf("-m %s ", memory_size); + } + + 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 " + "%s " + "-serial file:%s/src_serial " + "%s %s %s %s", + kvm_opts ? kvm_opts : "", + machine, machine_opts, + memory_backend, tmpfs, + arch_opts ? arch_opts : "", + shmem_opts ? shmem_opts : "", + args->opts_source ? args->opts_source : "", + ignore_stderr); + if (!args->only_target) { + *from = qtest_init_ext(QEMU_ENV_SRC, cmd_source, capabilities, true); + qtest_qmp_set_event_callback(*from, + migrate_watch_for_events, + &src_state); + } + + /* + * If the monitor connection is deferred, enable events on the command line + * so none are missed. This is for testing only, do not set migration + * options like this in general. + */ + events = args->defer_target_connect ? "-global migration.x-events=on" : ""; + + cmd_target = g_strdup_printf("-accel kvm%s -accel tcg " + "-machine %s,%s " + "-name target,debug-threads=on " + "%s " + "-serial file:%s/dest_serial " + "-incoming %s " + "%s %s %s %s %s", + kvm_opts ? kvm_opts : "", + machine, machine_opts, + memory_backend, tmpfs, uri, + events, + arch_opts ? arch_opts : "", + shmem_opts ? shmem_opts : "", + args->opts_target ? args->opts_target : "", + ignore_stderr); + *to = qtest_init_ext(QEMU_ENV_DST, cmd_target, capabilities, + !args->defer_target_connect); + 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 because QEMU has already opened this file + */ + if (args->use_shmem) { + unlink(shmem_path); + } + + migrate_start_set_capabilities(*from, *to, args); + + return 0; +} + +void migrate_end(QTestState *from, QTestState *to, bool test_dest) +{ + unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d; + + qtest_quit(from); + + if (test_dest) { + qtest_memread(to, start_address, &dest_byte_a, 1); + + /* Destination still running, wait for a byte to change */ + do { + qtest_memread(to, start_address, &dest_byte_b, 1); + usleep(1000 * 10); + } while (dest_byte_a == dest_byte_b); + + qtest_qmp_assert_success(to, "{ 'execute' : 'stop'}"); + + /* With it stopped, check nothing changes */ + qtest_memread(to, start_address, &dest_byte_c, 1); + usleep(1000 * 200); + qtest_memread(to, start_address, &dest_byte_d, 1); + g_assert_cmpint(dest_byte_c, ==, dest_byte_d); + + check_guests_ram(to); + } + + qtest_quit(to); + + cleanup("migsocket"); + cleanup("cpr.sock"); + cleanup("src_serial"); + cleanup("dest_serial"); + cleanup(FILE_TEST_FILENAME); +} + +static int migrate_postcopy_prepare(QTestState **from_ptr, + QTestState **to_ptr, + MigrateCommon *args) +{ + QTestState *from, *to; + + /* set postcopy capabilities */ + args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME] = true; + args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true; + + if (migrate_start(&from, &to, "defer", &args->start)) { + return -1; + } + + if (args->start_hook) { + args->postcopy_data = args->start_hook(from, to); + } + + 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, to, NULL, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to); + + *from_ptr = from; + *to_ptr = to; + + return 0; +} + +static void migrate_postcopy_complete(QTestState *from, QTestState *to, + MigrateCommon *args) +{ + MigrationTestEnv *env = migration_get_env(); + + 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"); + + if (env->uffd_feature_thread_id) { + read_blocktime(to); + } + + if (args->end_hook) { + args->end_hook(from, to, args->postcopy_data); + args->postcopy_data = NULL; + } + + migrate_end(from, to, true); +} + +void test_postcopy_common(MigrateCommon *args) +{ + QTestState *from, *to; + + if (migrate_postcopy_prepare(&from, &to, args)) { + return; + } + migrate_postcopy_start(from, to, &src_state); + migrate_postcopy_complete(from, to, args); +} + +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 +} + +void test_postcopy_recovery_common(MigrateCommon *args) +{ + QTestState *from, *to; + g_autofree char *uri = NULL; + + /* + * Always enable OOB QMP capability for recovery tests, migrate-recover is + * executed out-of-band + */ + args->start.oob = true; + + /* Always hide errors for postcopy recover tests since they're expected */ + args->start.hide_stderr = true; + + if (migrate_postcopy_prepare(&from, &to, args)) { + return; + } + + /* Turn postcopy speed down, 4K/s is slow enough on any machines */ + migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096); + + /* Now we start the postcopy */ + migrate_postcopy_start(from, to, &src_state); + + /* + * Wait until postcopy is really started; we can only run the + * migrate-pause command during a postcopy + */ + wait_for_migration_status(from, "postcopy-active", NULL); + + /* + * Manually stop the postcopy migration. This emulates a network + * failure with the migration socket + */ + migrate_pause(from); + + /* + * Wait for destination side to reach postcopy-paused state. The + * migrate-recover command can only succeed if destination machine + * is in the paused state + */ + 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 + * from the broken migration channel; tell the destination to + * listen to the new port + */ + uri = g_strdup_printf("unix:%s/migsocket-recover", tmpfs); + migrate_recover(to, uri); + + /* + * Try to rebuild the migration channel using the resume flag and + * the newly created channel + */ + migrate_qmp(from, to, uri, NULL, "{'resume': true}"); + + /* Restore the postcopy bandwidth to unlimited */ + migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); + + migrate_postcopy_complete(from, to, args); +} + +void test_precopy_common(MigrateCommon *args) +{ + QTestState *from, *to; + void *data_hook = NULL; + QObject *in_channels = NULL; + QObject *out_channels = NULL; + + g_assert(!args->cpr_channel || args->connect_channels); + + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { + return; + } + + if (args->start_hook) { + data_hook = args->start_hook(from, to); + } + + /* 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->live) { + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); + } else { + /* + * Testing non-live migration, we allow it to run at + * full speed to ensure short test case duration. + * For tests expected to fail, we don't need to + * change anything. + */ + if (args->result == MIG_TEST_SUCCEED) { + qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); + wait_for_stop(from, &src_state); + migrate_ensure_converge(from); + } + } + + /* + * The cpr channel must be included in outgoing channels, but not in + * migrate-incoming channels. + */ + if (args->connect_channels) { + if (args->start.defer_target_connect && + !strcmp(args->listen_uri, "defer")) { + in_channels = qobject_from_json(args->connect_channels, + &error_abort); + } + out_channels = qobject_from_json(args->connect_channels, &error_abort); + + if (args->cpr_channel) { + QList *channels_list = qobject_to(QList, out_channels); + QObject *obj = migrate_str_to_channel(args->cpr_channel); + + qlist_append(channels_list, obj); + } + } + + if (args->result == MIG_TEST_QMP_ERROR) { + migrate_qmp_fail(from, args->connect_uri, out_channels, "{}"); + goto finish; + } + + migrate_qmp(from, to, args->connect_uri, out_channels, "{}"); + + if (args->start.defer_target_connect) { + qtest_connect(to); + qtest_qmp_handshake(to, NULL); + if (!strcmp(args->listen_uri, "defer")) { + migrate_incoming_qmp(to, args->connect_uri, in_channels, "{}"); + } + } + + if (args->result != MIG_TEST_SUCCEED) { + bool allow_active = args->result == MIG_TEST_FAIL; + wait_for_migration_fail(from, allow_active); + + if (args->result == MIG_TEST_FAIL_DEST_QUIT_ERR) { + qtest_set_expected_status(to, EXIT_FAILURE); + } + } else { + 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, &src_state); + 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_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'}"); + } + + wait_for_resume(to, &dst_state); + + if (args->start.suspend_me) { + /* wakeup succeeds only if guest is suspended */ + qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); + } + + wait_for_serial("dest_serial"); + } + +finish: + if (args->end_hook) { + args->end_hook(from, to, data_hook); + } + + 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); +} + +void test_file_common(MigrateCommon *args, bool stop_src) +{ + QTestState *from, *to; + void *data_hook = NULL; + bool check_offset = false; + + if (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, NULL, "{}"); + 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->end_hook) { + args->end_hook(from, to, data_hook); + } + + migrate_end(from, to, args->result == MIG_TEST_SUCCEED); +} + +void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, + QTestState *to, + const char *method) +{ + migrate_set_parameter_str(from, "multifd-compression", method); + migrate_set_parameter_str(to, "multifd-compression", method); + + /* Start incoming migration from the 1st socket */ + migrate_incoming_qmp(to, "tcp:127.0.0.1:0", NULL, "{}"); + + return NULL; +} + +QTestMigrationState *get_src(void) +{ + return &src_state; +} + +MigrationTestEnv *migration_get_env(void) +{ + static MigrationTestEnv *env; + g_autoptr(GError) err = NULL; + + if (env) { + return env; + } + + env = g_new0(MigrationTestEnv, 1); + env->qemu_src = getenv(QEMU_ENV_SRC); + env->qemu_dst = getenv(QEMU_ENV_DST); + + /* + * The default QTEST_QEMU_BINARY must always be provided because + * that is what helpers use to query the accel type and + * architecture. + */ + if (env->qemu_src && env->qemu_dst) { + g_test_message("Only one of %s, %s is allowed", + QEMU_ENV_SRC, QEMU_ENV_DST); + exit(1); + } + + env->has_kvm = qtest_has_accel("kvm"); + env->has_tcg = qtest_has_accel("tcg"); + + if (!env->has_tcg && !env->has_kvm) { + g_test_skip("No KVM or TCG accelerator available"); + return env; + } + + env->has_dirty_ring = kvm_dirty_ring_supported(); + env->has_uffd = ufd_version_check(&env->uffd_feature_thread_id); + env->arch = qtest_get_arch(); + env->is_x86 = !strcmp(env->arch, "i386") || !strcmp(env->arch, "x86_64"); + + env->tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); + if (!env->tmpfs) { + g_test_message("Can't create temporary directory in %s: %s", + g_get_tmp_dir(), err->message); + } + g_assert(env->tmpfs); + + tmpfs = env->tmpfs; + + return env; +} + +int migration_env_clean(MigrationTestEnv *env) +{ + char *tmpfs; + int ret = 0; + + if (!env) { + return ret; + } + + bootfile_delete(); + + tmpfs = env->tmpfs; + ret = rmdir(tmpfs); + if (ret != 0) { + g_test_message("unable to rmdir: path (%s): %s", + tmpfs, strerror(errno)); + } + g_free(tmpfs); + + return ret; +} diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h new file mode 100644 index 00000000000..01e425e64e2 --- /dev/null +++ b/tests/qtest/migration/framework.h @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef TEST_FRAMEWORK_H +#define TEST_FRAMEWORK_H + +#include "libqtest.h" +#include + +#define FILE_TEST_FILENAME "migfile" +#define FILE_TEST_OFFSET 0x1000 +#define FILE_TEST_MARKER 'X' + +typedef struct MigrationTestEnv { + bool has_kvm; + bool has_tcg; + bool has_uffd; + bool uffd_feature_thread_id; + bool has_dirty_ring; + bool is_x86; + bool full_set; + const char *arch; + const char *qemu_src; + const char *qemu_dst; + char *tmpfs; +} MigrationTestEnv; + +MigrationTestEnv *migration_get_env(void); +int migration_env_clean(MigrationTestEnv *env); + +/* + * A hook that runs after the src and dst QEMUs have been + * created, but before the migration is started. This can + * be used to set migration parameters and capabilities. + * + * Returns: NULL, or a pointer to opaque state to be + * later passed to the TestMigrateEndHook + */ +typedef void * (*TestMigrateStartHook)(QTestState *from, + QTestState *to); + +/* + * A hook that runs after the migration has finished, + * regardless of whether it succeeded or failed, but + * before QEMU has terminated (unless it self-terminated + * due to migration error) + * + * @opaque is a pointer to state previously returned + * by the TestMigrateStartHook if any, or NULL. + */ +typedef void (*TestMigrateEndHook)(QTestState *from, + QTestState *to, + void *opaque); + +/* + * 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 + */ +typedef struct { + /* + * QTEST_LOG=1 may override this. When QTEST_LOG=1, we always dump errors + * unconditionally, because it means the user would like to be verbose. + */ + bool hide_stderr; + bool use_shmem; + /* only launch the target process */ + bool only_target; + /* Use dirty ring if true; dirty logging otherwise */ + bool use_dirty_ring; + const char *opts_source; + const char *opts_target; + /* suspend the src before migrating to dest. */ + bool suspend_me; + /* enable OOB QMP capability */ + bool oob; + /* + * Format string for the main memory backend, containing one %s where the + * size is plugged in. If omitted, "-m %s" is used. + */ + const char *memory_backend; + + /* Do not connect to target monitor and qtest sockets in qtest_init */ + bool defer_target_connect; + + /* + * Migration capabilities to be set in both source and + * destination. For unilateral capabilities, use + * migration_set_capabilities(). + */ + bool caps[MIGRATION_CAPABILITY__MAX]; +} MigrateStart; + +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; + +typedef struct { + /* Optional: fine tune start parameters */ + MigrateStart start; + + /* Required: the URI for the dst QEMU to listen on */ + const char *listen_uri; + + /* + * Optional: the URI for the src QEMU to connect to + * If NULL, then it will query the dst QEMU for its actual + * listening address and use that as the connect address. + * This allows for dynamically picking a free TCP port. + */ + const char *connect_uri; + + /* + * Optional: 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: the cpr migration channel, in JSON or dotted keys format */ + const char *cpr_channel; + + /* Optional: callback to run at start to set migration parameters */ + TestMigrateStartHook start_hook; + /* Optional: callback to run at finish to cleanup */ + TestMigrateEndHook end_hook; + + /* + * Optional: normally we expect the migration process to complete. + * + * There can be a variety of reasons and stages in which failure + * can happen during tests. + * + * If a failure is expected to happen at time of establishing + * the connection, then MIG_TEST_FAIL will indicate that the dst + * QEMU is expected to stay running and accept future migration + * connections. + * + * If a failure is expected to happen while processing the + * migration stream, then MIG_TEST_FAIL_DEST_QUIT_ERR will indicate + * that the dst QEMU is expected to quit with non-zero exit status + */ + enum { + /* This test should succeed, the default */ + MIG_TEST_SUCCEED = 0, + /* This test should fail, dest qemu should keep alive */ + MIG_TEST_FAIL, + /* This test should fail, dest qemu should fail with abnormal status */ + MIG_TEST_FAIL_DEST_QUIT_ERR, + /* The QMP command for this migration should fail with an error */ + MIG_TEST_QMP_ERROR, + } result; + + /* + * Optional: set number of migration passes to wait for, if live==true. + * If zero, then merely wait for a few MB of dirty data + */ + unsigned int iterations; + + /* + * Optional: whether the guest CPUs should be running during a precopy + * migration test. We used to always run with live but it took much + * longer so we reduced live tests to only the ones that have solid + * reason to be tested live-only. For each of the new test cases for + * precopy please provide justifications to use live explicitly (please + * refer to existing ones with live=true), or use live=off by default. + */ + bool live; + + /* Postcopy specific fields */ + void *postcopy_data; + PostcopyRecoveryFailStage postcopy_recovery_fail_stage; +} MigrateCommon; + +void wait_for_serial(const char *side); +void migrate_prepare_for_dirty_mem(QTestState *from); +void migrate_wait_for_dirty_mem(QTestState *from, QTestState *to); +int migrate_start(QTestState **from, QTestState **to, const char *uri, + MigrateStart *args); +void migrate_end(QTestState *from, QTestState *to, bool test_dest); + +void test_postcopy_common(MigrateCommon *args); +void test_postcopy_recovery_common(MigrateCommon *args); +void test_precopy_common(MigrateCommon *args); +void test_file_common(MigrateCommon *args, bool stop_src); +void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, + QTestState *to, + const char *method); + +typedef struct QTestMigrationState QTestMigrationState; +QTestMigrationState *get_src(void); + +#ifdef CONFIG_GNUTLS +void migration_test_add_tls(MigrationTestEnv *env); +#else +static inline void migration_test_add_tls(MigrationTestEnv *env) {}; +#endif +void migration_test_add_compression(MigrationTestEnv *env); +void migration_test_add_postcopy(MigrationTestEnv *env); +void migration_test_add_file(MigrationTestEnv *env); +void migration_test_add_precopy(MigrationTestEnv *env); +void migration_test_add_cpr(MigrationTestEnv *env); +void migration_test_add_misc(MigrationTestEnv *env); + +#endif /* TEST_FRAMEWORK_H */ diff --git a/tests/qtest/migration/i386/Makefile b/tests/qtest/migration/i386/Makefile new file mode 100644 index 00000000000..37a72ae353b --- /dev/null +++ b/tests/qtest/migration/i386/Makefile @@ -0,0 +1,23 @@ +# To specify cross compiler prefix, use CROSS_PREFIX= +# $ make CROSS_PREFIX=x86_64-linux-gnu- + +.PHONY: all clean +all: a-b-bootblock.h + +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 + dd if=$< of=$@ bs=256 count=2 skip=124 + +x86.boot: x86.o + $(CROSS_PREFIX)objcopy -O binary $< $@ + +x86.o: a-b-bootblock.S + $(CROSS_PREFIX)gcc -I.. -m32 -march=i486 -c $< -o $@ + +clean: + @rm -rf *.boot *.o *.bootsect diff --git a/tests/qtest/migration/i386/a-b-bootblock.S b/tests/qtest/migration/i386/a-b-bootblock.S new file mode 100644 index 00000000000..6f39eb60511 --- /dev/null +++ b/tests/qtest/migration/i386/a-b-bootblock.S @@ -0,0 +1,145 @@ +# x86 bootblock used in migration test +# repeatedly increments the first byte of each page in a 100MB +# range. +# Outputs an initial 'A' on serial followed by repeated 'B's +# +# Copyright (c) 2016 Red Hat, Inc. 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. +# +# 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 + .file "fill.s" + .text + .globl start + .type start, @function +start: # at 0x7c00 ? + cli + lgdt gdtdesc + mov $1,%eax + mov %eax,%cr0 # Protected mode enable + data32 ljmp $8,$0x7c20 + +.org 0x7c20 +.code32 + # A20 enable - not sure I actually need this + inb $0x92,%al + or $2,%al + outb %al, $0x92 + + # set up DS for the whole of RAM (needed on KVM) + 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: + mov $TEST_MEM_START,%eax +innerloop: + incb (%eax) + add $4096,%eax + cmp $TEST_MEM_END,%eax + jl innerloop + + inc %bl + andb $0x3f,%bl + jnz mainloop + + mov $66,%ax + mov $0x3f8,%dx + outb %al,%dx + + # 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 */ +gdt: + .word 0, 0 + .byte 0, 0, 0, 0 + + /* -- code segment -- + * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity), present + * type = 32bit code execute/read, DPL = 0 + */ + .word 0xFFFF, 0 + .byte 0, 0x9A, 0xCF, 0 + + /* -- data segment -- + * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present + * type = 32 bit data read/write, DPL = 0 + */ + .word 0xFFFF, 0 + .byte 0, 0x92, 0xCF, 0 + +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 + .byte 0xAA diff --git a/tests/qtest/migration/i386/a-b-bootblock.h b/tests/qtest/migration/i386/a-b-bootblock.h new file mode 100644 index 00000000000..c83f8711dbf --- /dev/null +++ b/tests/qtest/migration/i386/a-b-bootblock.h @@ -0,0 +1,61 @@ +/* This file is automatically generated from the assembly file in + * tests/migration/i386. 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 x86_bootsect[] = { + 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, 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c new file mode 100644 index 00000000000..fb59741b2cf --- /dev/null +++ b/tests/qtest/migration/migration-qmp.c @@ -0,0 +1,520 @@ +/* + * QTest QMP helpers for migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "migration-qmp.h" +#include "migration-util.h" +#include "qapi/error.h" +#include "qapi/qapi-types-migration.h" +#include "qapi/qapi-visit-migration.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qlist.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" + +/* + * Number of seconds we wait when looking for migration + * status changes, to avoid test suite hanging forever + * when things go wrong. Needs to be higher enough to + * avoid false positives on loaded hosts. + */ +#define MIGRATION_STATUS_WAIT_TIMEOUT 120 + +/* + * 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); +} + +/* + * Convert a string representing a single channel to an object. + * @str may be in JSON or dotted keys format. + */ +QObject *migrate_str_to_channel(const char *str) +{ + Visitor *v; + MigrationChannel *channel; + QObject *obj; + + /* Create the channel */ + v = qobject_input_visitor_new_str(str, "channel-type", &error_abort); + visit_type_MigrationChannel(v, NULL, &channel, &error_abort); + visit_free(v); + + /* Create the object */ + v = qobject_output_visitor_new(&obj); + visit_type_MigrationChannel(v, NULL, &channel, &error_abort); + visit_complete(v, &obj); + visit_free(v); + + qapi_free_MigrationChannel(channel); + return obj; +} + +void migrate_qmp_fail(QTestState *who, const char *uri, + QObject *channels, const char *fmt, ...) +{ + va_list ap; + QDict *args, *err; + + 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); + } + + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + qdict_put_obj(args, "channels", channels); + } + + err = qtest_qmp_assert_failure_ref( + who, "{ 'execute': 'migrate', 'arguments': %p}", args); + + g_assert(qdict_haskey(err, "desc")); + + qobject_unref(err); +} + +/* + * Send QMP command "migrate". + * Arguments are built from @fmt... (formatted like + * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. + */ +void migrate_qmp(QTestState *who, QTestState *to, const char *uri, + QObject *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) { + QList *channel_list = qobject_to(QList, channels); + migrate_set_ports(to, channel_list); + qdict_put_obj(args, "channels", channels); + } + + 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, QObject *channels, + const char *fmt, ...) +{ + va_list ap; + QDict *args, *rsp; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + if (uri) { + qdict_put_str(args, "uri", uri); + } + + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + qdict_put_obj(args, "channels", channels); + } + + /* 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"); +} + +static bool check_migration_status(QTestState *who, const char *goal, + const char **ungoals) +{ + bool ready; + char *current_status; + const char **ungoal; + + current_status = migrate_query_status(who); + ready = strcmp(current_status, goal) == 0; + if (!ungoals) { + g_assert_cmpstr(current_status, !=, "failed"); + /* + * If looking for a state other than completed, + * completion of migration would cause the test to + * hang. + */ + if (strcmp(goal, "completed") != 0) { + g_assert_cmpstr(current_status, !=, "completed"); + } + } else { + for (ungoal = ungoals; *ungoal; ungoal++) { + g_assert_cmpstr(current_status, !=, *ungoal); + } + } + g_free(current_status); + return ready; +} + +void wait_for_migration_status(QTestState *who, + const char *goal, const char **ungoals) +{ + g_test_timer_start(); + while (!check_migration_status(who, goal, ungoals)) { + usleep(1000); + + g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); + } +} + +void wait_for_migration_complete(QTestState *who) +{ + wait_for_migration_status(who, "completed", NULL); +} + +void wait_for_migration_fail(QTestState *from, bool allow_active) +{ + g_test_timer_start(); + QDict *rsp_return; + char *status; + bool failed; + + do { + status = migrate_query_status(from); + bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || + (allow_active && !strcmp(status, "active")); + if (!result) { + fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", + __func__, status, allow_active); + } + g_assert(result); + failed = !strcmp(status, "failed"); + g_free(status); + + g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); + } while (!failed); + + /* Is the machine currently running? */ + 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); +} + +void wait_for_stop(QTestState *who, QTestMigrationState *state) +{ + if (!state->stop_seen) { + qtest_qmp_eventwait(who, "STOP"); + } +} + +void wait_for_resume(QTestState *who, QTestMigrationState *state) +{ + if (!state->resume_seen) { + qtest_qmp_eventwait(who, "RESUME"); + } +} + +void wait_for_suspend(QTestState *who, QTestMigrationState *state) +{ + if (state->suspend_me && !state->suspend_seen) { + qtest_qmp_eventwait(who, "SUSPEND"); + } +} + +/* + * Note: caller is responsible to free the returned object via + * qobject_unref() after use + */ +QDict *migrate_query(QTestState *who) +{ + return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }"); +} + +QDict *migrate_query_not_failed(QTestState *who) +{ + const char *status; + QDict *rsp = migrate_query(who); + status = qdict_get_str(rsp, "status"); + if (g_str_equal(status, "failed")) { + g_printerr("query-migrate shows failed migration: %s\n", + qdict_get_str(rsp, "error-desc")); + } + g_assert(!g_str_equal(status, "failed")); + return rsp; +} + +/* + * Note: caller is responsible to free the returned object via + * g_free() after use + */ +gchar *migrate_query_status(QTestState *who) +{ + QDict *rsp_return = migrate_query(who); + gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); + + g_assert(status); + qobject_unref(rsp_return); + + return status; +} + +int64_t read_ram_property_int(QTestState *who, const char *property) +{ + QDict *rsp_return, *rsp_ram; + int64_t result; + + rsp_return = migrate_query_not_failed(who); + if (!qdict_haskey(rsp_return, "ram")) { + /* Still in setup */ + result = 0; + } else { + rsp_ram = qdict_get_qdict(rsp_return, "ram"); + result = qdict_get_try_int(rsp_ram, property, 0); + } + qobject_unref(rsp_return); + return result; +} + +int64_t read_migrate_property_int(QTestState *who, const char *property) +{ + QDict *rsp_return; + int64_t result; + + rsp_return = migrate_query_not_failed(who); + result = qdict_get_try_int(rsp_return, property, 0); + qobject_unref(rsp_return); + return result; +} + +uint64_t get_migration_pass(QTestState *who) +{ + return read_ram_property_int(who, "dirty-sync-count"); +} + +void read_blocktime(QTestState *who) +{ + QDict *rsp_return; + + rsp_return = migrate_query_not_failed(who); + g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); + qobject_unref(rsp_return); +} + +/* + * Wait for two changes in the migration pass count, but bail if we stop. + */ +void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state) +{ + uint64_t pass, prev_pass = 0, changes = 0; + + while (changes < 2 && !src_state->stop_seen && !src_state->suspend_seen) { + usleep(1000); + pass = get_migration_pass(who); + changes += (pass != prev_pass); + prev_pass = pass; + } +} + +static long long migrate_get_parameter_int(QTestState *who, + const char *parameter) +{ + QDict *rsp; + long long result; + + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); + result = qdict_get_int(rsp, parameter); + qobject_unref(rsp); + return result; +} + +static void migrate_check_parameter_int(QTestState *who, const char *parameter, + long long value) +{ + long long result; + + result = migrate_get_parameter_int(who, parameter); + g_assert_cmpint(result, ==, value); +} + +void migrate_set_parameter_int(QTestState *who, const char *parameter, + long long value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %lld } }", + parameter, value); + migrate_check_parameter_int(who, parameter, value); +} + +static char *migrate_get_parameter_str(QTestState *who, const char *parameter) +{ + QDict *rsp; + char *result; + + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); + result = g_strdup(qdict_get_str(rsp, parameter)); + qobject_unref(rsp); + return result; +} + +static void migrate_check_parameter_str(QTestState *who, const char *parameter, + const char *value) +{ + g_autofree char *result = migrate_get_parameter_str(who, parameter); + g_assert_cmpstr(result, ==, value); +} + +void migrate_set_parameter_str(QTestState *who, const char *parameter, + const char *value) +{ + 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); +} + +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); +} + +void migrate_ensure_non_converge(QTestState *who) +{ + /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */ + migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000); + migrate_set_parameter_int(who, "downtime-limit", 1); +} + +void migrate_ensure_converge(QTestState *who) +{ + /* Should converge with 30s downtime + 1 gbs bandwidth limit */ + migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000); + migrate_set_parameter_int(who, "downtime-limit", 30 * 1000); +} + +void migrate_pause(QTestState *who) +{ + qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }"); +} + +void migrate_continue(QTestState *who, const char *state) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-continue'," + " 'arguments': { 'state': %s } }", + state); +} + +void migrate_recover(QTestState *who, const char *uri) +{ + qtest_qmp_assert_success(who, + "{ 'exec-oob': 'migrate-recover', " + " 'id': 'recover-cmd', " + " 'arguments': { 'uri': %s } }", + uri); +} + +void migrate_cancel(QTestState *who) +{ + qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }"); +} + +void migrate_postcopy_start(QTestState *from, QTestState *to, + QTestMigrationState *src_state) +{ + qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }"); + + wait_for_stop(from, src_state); + qtest_qmp_eventwait(to, "RESUME"); +} diff --git a/tests/qtest/migration/migration-qmp.h b/tests/qtest/migration/migration-qmp.h new file mode 100644 index 00000000000..faa8181d916 --- /dev/null +++ b/tests/qtest/migration/migration-qmp.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef MIGRATION_QMP_H +#define MIGRATION_QMP_H + +#include "migration-util.h" + +QObject *migrate_str_to_channel(const char *str); + +G_GNUC_PRINTF(4, 5) +void migrate_qmp_fail(QTestState *who, const char *uri, + QObject *channels, const char *fmt, ...); + +G_GNUC_PRINTF(5, 6) +void migrate_qmp(QTestState *who, QTestState *to, const char *uri, + QObject *channels, const char *fmt, ...); + +G_GNUC_PRINTF(4, 5) +void migrate_incoming_qmp(QTestState *who, const char *uri, + QObject *channels, const char *fmt, ...); + +void migration_event_wait(QTestState *s, const char *target); +void migrate_set_capability(QTestState *who, const char *capability, + bool value); +int64_t read_ram_property_int(QTestState *who, const char *property); +void migrate_set_parameter_int(QTestState *who, const char *parameter, + long long value); +void wait_for_stop(QTestState *who, QTestMigrationState *state); +void wait_for_resume(QTestState *who, QTestMigrationState *state); +void wait_for_suspend(QTestState *who, QTestMigrationState *state); +gchar *migrate_query_status(QTestState *who); +int64_t read_migrate_property_int(QTestState *who, const char *property); +uint64_t get_migration_pass(QTestState *who); +void read_blocktime(QTestState *who); +void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state); +void migrate_set_parameter_str(QTestState *who, const char *parameter, + const char *value); +void migrate_set_parameter_bool(QTestState *who, const char *parameter, + int value); +void migrate_ensure_non_converge(QTestState *who); +void migrate_ensure_converge(QTestState *who); +void migrate_pause(QTestState *who); +void migrate_continue(QTestState *who, const char *state); +void migrate_recover(QTestState *who, const char *uri); +void migrate_cancel(QTestState *who); +void migrate_postcopy_start(QTestState *from, QTestState *to, + QTestMigrationState *src_state); + +#endif /* MIGRATION_QMP_H */ diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c new file mode 100644 index 00000000000..642cf50c8d8 --- /dev/null +++ b/tests/qtest/migration/migration-util.c @@ -0,0 +1,398 @@ +/* + * QTest migration utilities + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/ctype.h" +#include "qapi/qapi-visit-sockets.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/error.h" +#include "qobject/qlist.h" +#include "qemu/cutils.h" +#include "qemu/memalign.h" + +#include "migration/bootfile.h" +#include "migration/migration-util.h" + +#if defined(__linux__) +#include +#include +#endif + +/* for uffd_version_check() */ +#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) +#include +#include "qemu/userfaultfd.h" +#endif + +/* For dirty ring test; so far only x86_64 is supported */ +#if defined(__linux__) && defined(HOST_X86_64) +#include "linux/kvm.h" +#endif + + +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("vsock:%s:%s", + addr->u.vsock.cid, + addr->u.vsock.port); + default: + return g_strdup("unknown address type"); + } +} + +static QDict *SocketAddress_to_qdict(SocketAddress *addr) +{ + QDict *dict = qdict_new(); + + 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(); + } + + return dict; +} + +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; +} + +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; +} + +void migrate_set_ports(QTestState *to, QList *channel_list) +{ + g_autoptr(QDict) addr = NULL; + QListEntry *entry; + const char *addr_port = NULL; + + 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") || + strcmp(qdict_get_str(addrdict, "port"), "0")) { + continue; + } + + /* + * Fetch addr only if needed, so tests that are not yet connected to + * the monitor do not query it. Such tests cannot use port=0. + */ + if (!addr) { + addr = migrate_get_connect_qdict(to); + } + + if (qdict_haskey(addr, "port")) { + addr_port = qdict_get_str(addr, "port"); + qdict_put_str(addrdict, "port", addr_port); + } + } +} + +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; +} + +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); + void (*func_full)(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); +} + +static void migration_test_wrapper_full(const void *data) +{ + MigrationTest *test = (MigrationTest *)data; + + g_test_message("Running /%s%s", qtest_get_arch(), test->name); + test->func_full(test->name); +} + +void migration_test_add_suffix(const char *path, const char *suffix, + void (*fn)(void *)) +{ + MigrationTest *test = g_new0(MigrationTest, 1); + + g_assert(g_str_has_suffix(path, "/")); + g_assert(!g_str_has_prefix(suffix, "/")); + + test->func_full = fn; + test->name = g_strconcat(path, suffix, NULL); + + qtest_add_data_func_full(test->name, test, migration_test_wrapper_full, + 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); + memset(buf, 0, len); + + ret = pwrite(fd, buf, len, offset); + unlink(filename); + g_free(buf); + + if (ret < 0) { + return false; + } + + return true; +} +#endif + +#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) +bool ufd_version_check(bool *uffd_feature_thread_id) +{ + struct uffdio_api api_struct; + uint64_t ioctl_mask; + + int ufd = uffd_open(O_CLOEXEC); + + if (ufd == -1) { + g_test_message("Skipping test: userfaultfd not available"); + return false; + } + + api_struct.api = UFFD_API; + api_struct.features = 0; + if (ioctl(ufd, UFFDIO_API, &api_struct)) { + g_test_message("Skipping test: UFFDIO_API failed"); + return false; + } + + if (uffd_feature_thread_id) { + *uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID; + } + + 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; + } + + return true; +} +#else +bool ufd_version_check(bool *uffd_feature_thread_id) +{ + g_test_message("Skipping test: Userfault not available (builtdtime)"); + return false; +} +#endif + +bool kvm_dirty_ring_supported(void) +{ +#if defined(__linux__) && defined(HOST_X86_64) + int ret, kvm_fd = open("/dev/kvm", O_RDONLY); + + if (kvm_fd < 0) { + return false; + } + + ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING); + close(kvm_fd); + + /* We test with 4096 slots */ + if (ret < 4096) { + return false; + } + + return true; +#else + return false; +#endif +} diff --git a/tests/qtest/migration/migration-util.h b/tests/qtest/migration/migration-util.h new file mode 100644 index 00000000000..44815e9c42d --- /dev/null +++ b/tests/qtest/migration/migration-util.h @@ -0,0 +1,59 @@ +/* + * QTest migration helpers + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef MIGRATION_UTIL_H +#define MIGRATION_UTIL_H + +#include "libqtest.h" + +typedef struct QTestMigrationState { + bool stop_seen; + bool resume_seen; + bool suspend_seen; + bool suspend_me; +} QTestMigrationState; + +bool migrate_watch_for_events(QTestState *who, const char *name, + QDict *event, void *opaque); + +QDict *migrate_query(QTestState *who); +QDict *migrate_query_not_failed(QTestState *who); + +void wait_for_migration_status(QTestState *who, + const char *goal, const char **ungoals); + +void wait_for_migration_complete(QTestState *who); + +void wait_for_migration_fail(QTestState *from, bool allow_active); + +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 + +bool ufd_version_check(bool *uffd_feature_thread_id); +bool kvm_dirty_ring_supported(void); +void migration_test_add(const char *path, void (*fn)(void)); +void migration_test_add_suffix(const char *path, const char *suffix, + void (*fn)(void *)); +char *migrate_get_connect_uri(QTestState *who); +void migrate_set_ports(QTestState *to, QList *channel_list); + +#endif /* MIGRATION_UTIL_H */ diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c new file mode 100644 index 00000000000..54995256d81 --- /dev/null +++ b/tests/qtest/migration/misc-tests.c @@ -0,0 +1,297 @@ +/* + * QTest testcases for migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qobject/qjson.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" + +#define ANALYZE_SCRIPT "scripts/analyze-migration.py" + +static char *tmpfs; + +static void test_baddest(void) +{ + MigrateStart args = { + .hide_stderr = true + }; + QTestState *from, *to; + + if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { + return; + } + migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}"); + wait_for_migration_fail(from, false); + 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 (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(); + } + migrate_end(from, to, false); + unlink(file); +} +#endif + +static void test_ignore_shared(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + MigrateStart args = { + .use_shmem = true, + .caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true, + }; + + if (migrate_start(&from, &to, uri, &args)) { + return; + } + + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, to, uri, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to); + + wait_for_stop(from, get_src()); + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + /* Check whether shared RAM has been really skipped */ + g_assert_cmpint( + read_ram_property_int(from, "transferred"), <, 4 * 1024 * 1024); + + migrate_end(from, to, true); +} + +static void do_test_validate_uuid(MigrateStart *args, bool should_fail) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + + if (migrate_start(&from, &to, uri, args)) { + return; + } + + /* + * UUID validation is at the begin of migration. So, the main process of + * migration is not interesting for us here. Thus, set huge downtime for + * very fast migration. + */ + migrate_set_parameter_int(from, "downtime-limit", 1000000); + migrate_set_capability(from, "validate-uuid", true); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, to, uri, NULL, "{}"); + + if (should_fail) { + qtest_set_expected_status(to, EXIT_FAILURE); + wait_for_migration_fail(from, true); + } else { + wait_for_migration_complete(from); + } + + migrate_end(from, to, false); +} + +static void test_validate_uuid(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + .opts_target = "-uuid 11111111-1111-1111-1111-111111111111", + }; + + do_test_validate_uuid(&args, false); +} + +static void test_validate_uuid_error(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", + .hide_stderr = true, + }; + + do_test_validate_uuid(&args, true); +} + +static void test_validate_uuid_src_not_set(void) +{ + MigrateStart args = { + .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", + .hide_stderr = true, + }; + + do_test_validate_uuid(&args, false); +} + +static void test_validate_uuid_dst_not_set(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + .hide_stderr = true, + }; + + do_test_validate_uuid(&args, false); +} + +static void do_test_validate_uri_channel(MigrateCommon *args) +{ + QTestState *from, *to; + QObject *channels; + + if (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. + */ + channels = args->connect_channels ? + qobject_from_json(args->connect_channels, &error_abort) : + NULL; + migrate_qmp_fail(from, args->connect_uri, channels, "{}"); + + 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); +} + +static void migration_test_add_misc_smoke(MigrationTestEnv *env) +{ +#ifndef _WIN32 + migration_test_add("/migration/analyze-script", test_analyze_script); +#endif +} + +void migration_test_add_misc(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add_misc_smoke(env); + + if (!env->full_set) { + return; + } + + migration_test_add("/migration/bad_dest", test_baddest); + + /* + * 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/ignore-shared", test_ignore_shared); + } + + 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); +} diff --git a/tests/qtest/migration/postcopy-tests.c b/tests/qtest/migration/postcopy-tests.c new file mode 100644 index 00000000000..37735258437 --- /dev/null +++ b/tests/qtest/migration/postcopy-tests.c @@ -0,0 +1,149 @@ +/* + * QTest testcases for postcopy migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-util.h" +#include "qobject/qlist.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/range.h" +#include "qemu/sockets.h" + +static void test_postcopy(void) +{ + MigrateCommon args = { }; + + 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 = { + .start = { + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_recovery(void) +{ + MigrateCommon args = { }; + + 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); +} + +static void test_postcopy_preempt_recovery(void) +{ + MigrateCommon args = { + .start = { + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, + }; + + test_postcopy_recovery_common(&args); +} + +static void migration_test_add_postcopy_smoke(MigrationTestEnv *env) +{ + if (env->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); + } +} + +static void test_multifd_postcopy(void) +{ + MigrateCommon args = { + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + + test_postcopy_common(&args); +} + +static void test_multifd_postcopy_preempt(void) +{ + MigrateCommon args = { + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, + }; + + test_postcopy_common(&args); +} + +void migration_test_add_postcopy(MigrationTestEnv *env) +{ + migration_test_add_postcopy_smoke(env); + + if (!env->full_set) { + return; + } + + if (env->has_uffd) { + 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); + + migration_test_add("/migration/multifd+postcopy/plain", + test_multifd_postcopy); + migration_test_add("/migration/multifd+postcopy/preempt/plain", + test_multifd_postcopy_preempt); + if (env->is_x86) { + migration_test_add("/migration/postcopy/suspend", + test_postcopy_suspend); + } + } +} diff --git a/tests/qtest/migration/ppc64/Makefile b/tests/qtest/migration/ppc64/Makefile new file mode 100644 index 00000000000..a3a2d98ac82 --- /dev/null +++ b/tests/qtest/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/qtest/migration/ppc64/a-b-kernel.S b/tests/qtest/migration/ppc64/a-b-kernel.S new file mode 100644 index 00000000000..0613a8d18e1 --- /dev/null +++ b/tests/qtest/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/qtest/migration/ppc64/a-b-kernel.h b/tests/qtest/migration/ppc64/a-b-kernel.h new file mode 100644 index 00000000000..673317efdb5 --- /dev/null +++ b/tests/qtest/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/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c new file mode 100644 index 00000000000..bb38292550c --- /dev/null +++ b/tests/qtest/migration/precopy-tests.c @@ -0,0 +1,1337 @@ +/* + * QTest testcase for precopy migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "chardev/char.h" +#include "crypto/tlscredspsk.h" +#include "libqtest.h" +#include "migration/bootfile.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "ppc-util.h" +#include "qobject/qlist.h" +#include "qapi-types-migration.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/range.h" +#include "qemu/sockets.h" + + +/* + * Dirtylimit stop working if dirty page rate error + * value less than DIRTYLIMIT_TOLERANCE_RANGE + */ +#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ + +static char *tmpfs; + +static void test_precopy_unix_plain(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + 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) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .start = { + .use_dirty_ring = true, + }, + .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); +} + +#ifdef CONFIG_RDMA + +#include + +/* + * During migration over RDMA, it will try to pin portions of guest memory, + * typically exceeding 100MB in this test, while the remainder will be + * transmitted as compressed zero pages. + * + * REQUIRED_MEMLOCK_SZ indicates the minimal mlock size in the current context. + */ +#define REQUIRED_MEMLOCK_SZ (128 << 20) /* 128MB */ + +/* check 'ulimit -l' */ +static bool mlock_check(void) +{ + uid_t uid; + struct rlimit rlim; + + uid = getuid(); + if (uid == 0) { + return true; + } + + if (getrlimit(RLIMIT_MEMLOCK, &rlim) != 0) { + return false; + } + + return rlim.rlim_cur >= REQUIRED_MEMLOCK_SZ; +} + +#define RDMA_MIGRATION_HELPER "scripts/rdma-migration-helper.sh" +static int new_rdma_link(char *buffer, bool ipv6) +{ + char cmd[256]; + bool verbose = g_getenv("QTEST_LOG"); + + snprintf(cmd, sizeof(cmd), "IP_FAMILY=%s %s detect %s", + ipv6 ? "ipv6" : "ipv4", RDMA_MIGRATION_HELPER, + verbose ? "" : "2>/dev/null"); + + FILE *pipe = popen(cmd, "r"); + if (pipe == NULL) { + perror("Failed to run script"); + return -1; + } + + int idx = 0; + while (fgets(buffer + idx, 128 - idx, pipe) != NULL) { + idx += strlen(buffer); + } + + int status = pclose(pipe); + if (status == -1) { + perror("Error reported by pclose()"); + return -1; + } else if (WIFEXITED(status)) { + return WEXITSTATUS(status); + } + + return -1; +} + +static void __test_precopy_rdma_plain(bool ipv6) +{ + char buffer[128] = {}; + + if (!mlock_check()) { + g_test_skip("'ulimit -l' is too small, require >=128M"); + return; + } + + if (new_rdma_link(buffer, ipv6)) { + g_test_skip("No rdma link available\n" + "# To enable the test:\n" + "# Run \'" RDMA_MIGRATION_HELPER " setup\' with root to " + "setup a new rdma/rxe link and rerun the test\n" + "# Optional: run 'scripts/rdma-migration-helper.sh clean' " + "to revert the 'setup'"); + return; + } + + /* + * TODO: query a free port instead of hard code. + * 29200=('R'+'D'+'M'+'A')*100 + **/ + g_autofree char *uri = g_strdup_printf("rdma:%s:29200", buffer); + + MigrateCommon args = { + .listen_uri = uri, + .connect_uri = uri, + }; + + test_precopy_common(&args); +} + +static void test_precopy_rdma_plain(void) +{ + __test_precopy_rdma_plain(false); +} + +static void test_precopy_rdma_plain_ipv6(void) +{ + __test_precopy_rdma_plain(true); +} +#endif + +static void test_precopy_tcp_plain(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_switchover_ack(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start = { + .caps[MIGRATION_CAPABILITY_RETURN_PATH] = true, + .caps[MIGRATION_CAPABILITY_SWITCHOVER_ACK] = true, + }, + /* + * 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); +} + +#ifndef _WIN32 +static void *migrate_hook_start_fd(QTestState *from, + QTestState *to) +{ + int ret; + int pair[2]; + + /* Create two connected sockets for migration */ + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); + g_assert_cmpint(ret, ==, 0); + + /* Send the 1st socket to the target */ + 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 */ + migrate_incoming_qmp(to, "fd:fd-mig", NULL, "{}"); + + /* Send the 2nd socket to the target */ + qtest_qmp_fds_assert_success(from, &pair[1], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + close(pair[1]); + + return NULL; +} + +static void migrate_hook_end_fd(QTestState *from, + QTestState *to, + void *opaque) +{ + QDict *rsp; + const char *error_desc; + + /* 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' }}"); + 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' }}"); + 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_precopy_fd_socket(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .connect_uri = "fd:fd-mig", + .start_hook = migrate_hook_start_fd, + .end_hook = migrate_hook_end_fd, + }; + test_precopy_common(&args); +} + +static void *migrate_hook_start_precopy_fd_file(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_precopy_fd_file(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .connect_uri = "fd:fd-mig", + .start_hook = migrate_hook_start_precopy_fd_file, + .end_hook = migrate_hook_end_fd, + }; + test_file_common(&args, true); +} +#endif /* _WIN32 */ + +/* + * 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_auto_converge(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateStart args = {}; + QTestState *from, *to; + int64_t percentage; + + /* + * We want the test to be stable and as fast as possible. + * 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 = 25, max_pct = 95; + uint64_t prev_dirty_sync_cnt, dirty_sync_cnt; + int max_try_count, hit = 0; + + if (migrate_start(&from, &to, uri, &args)) { + return; + } + + migrate_set_capability(from, "auto-converge", true); + migrate_set_parameter_int(from, "cpu-throttle-initial", init_pct); + migrate_set_parameter_int(from, "cpu-throttle-increment", inc_pct); + migrate_set_parameter_int(from, "max-cpu-throttle", max_pct); + + /* + * Set the initial parameters so that the migration could not converge + * without throttling. + */ + migrate_ensure_non_converge(from); + + /* To check remaining size after precopy */ + migrate_set_capability(from, "pause-before-switchover", true); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, to, uri, NULL, "{}"); + + /* Wait for throttling begins */ + percentage = 0; + do { + percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); + if (percentage != 0) { + break; + } + usleep(20); + g_assert_false(get_src()->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); + } + + 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); + + /* + * Wait for pre-switchover status to check last throttle percentage + * and remaining. These values will be zeroed later + */ + wait_for_migration_status(from, "pre-switchover", NULL); + + /* The final percentage of throttling shouldn't be greater than max_pct */ + percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); + g_assert_cmpint(percentage, <=, max_pct); + migrate_continue(from, "pre-switchover"); + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + migrate_end(from, to, true); +} + +static void * +migrate_hook_start_precopy_tcp_multifd(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); +} + +static void * +migrate_hook_start_precopy_tcp_multifd_zero_page_legacy(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + migrate_set_parameter_str(from, "zero-page-detection", "legacy"); + return NULL; +} + +static void * +migrate_hook_start_precopy_tcp_multifd_no_zero_page(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + migrate_set_parameter_str(from, "zero-page-detection", "none"); + return NULL; +} + +static void test_multifd_tcp_uri_none(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + /* + * 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 = migrate_hook_start_precopy_tcp_multifd_zero_page_legacy, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + /* + * 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 = migrate_hook_start_precopy_tcp_multifd_no_zero_page, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + /* + * 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 = migrate_hook_start_precopy_tcp_multifd, + .live = true, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .connect_channels = ("[ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ]"), + }; + test_precopy_common(&args); +} + +/* + * This test does: + * source target + * migrate_incoming + * migrate + * migrate_cancel + * launch another target + * migrate + * + * And see that it works + */ +static void test_multifd_tcp_cancel(bool postcopy_ram) +{ + MigrateStart args = { + .hide_stderr = true, + }; + QTestState *from, *to, *to2; + + if (migrate_start(&from, &to, "defer", &args)) { + return; + } + + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); + + if (postcopy_ram) { + migrate_set_capability(from, "postcopy-ram", true); + migrate_set_capability(to, "postcopy-ram", true); + } + + migrate_set_parameter_int(from, "multifd-channels", 16); + migrate_set_parameter_int(to, "multifd-channels", 16); + + migrate_set_capability(from, "multifd", true); + migrate_set_capability(to, "multifd", true); + + /* Start incoming migration from the 1st socket */ + migrate_incoming_qmp(to, "tcp:127.0.0.1:0", NULL, "{}"); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, to, NULL, NULL, "{}"); + + 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 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, + }; + + if (migrate_start(&from, &to2, "defer", &args)) { + return; + } + + if (postcopy_ram) { + migrate_set_capability(to2, "postcopy-ram", true); + } + + migrate_set_parameter_int(to2, "multifd-channels", 16); + + migrate_set_capability(to2, "multifd", true); + + /* Start incoming migration from the 1st socket */ + migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", NULL, "{}"); + + migrate_ensure_non_converge(from); + + migrate_qmp(from, to2, NULL, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to2); + + migrate_ensure_converge(from); + + wait_for_stop(from, get_src()); + qtest_qmp_eventwait(to2, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + migrate_end(from, to2, true); +} + +static void test_multifd_precopy_tcp_cancel(void) +{ + test_multifd_tcp_cancel(false); +} + +static void test_multifd_postcopy_tcp_cancel(void) +{ + test_multifd_tcp_cancel(true); +} + +static void test_cancel_src_after_failed(QTestState *from, QTestState *to, + const char *uri, const char *phase) +{ + /* + * No migrate_incoming_qmp() at the start to force source into + * failed state during migrate_qmp(). + */ + + wait_for_serial("src_serial"); + migrate_ensure_converge(from); + + migrate_qmp(from, to, uri, NULL, "{}"); + + migration_event_wait(from, phase); + migrate_cancel(from); + + /* cancelling will not move the migration out of 'failed' */ + + wait_for_migration_status(from, "failed", + (const char * []) { "completed", NULL }); + + /* + * Not waiting for the destination because it never started + * migration. + */ +} + +static void test_cancel_src_after_cancelled(QTestState *from, QTestState *to, + const char *uri, const char *phase) +{ + migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }"); + + wait_for_serial("src_serial"); + migrate_ensure_converge(from); + + migrate_qmp(from, to, uri, NULL, "{}"); + + /* To move to cancelled/cancelling */ + migrate_cancel(from); + migration_event_wait(from, phase); + + /* The migrate_cancel under test */ + migrate_cancel(from); + + wait_for_migration_status(from, "cancelled", + (const char * []) { "completed", NULL }); + + wait_for_migration_status(to, "failed", + (const char * []) { "completed", NULL }); +} + +static void test_cancel_src_after_complete(QTestState *from, QTestState *to, + const char *uri, const char *phase) +{ + migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }"); + + wait_for_serial("src_serial"); + migrate_ensure_converge(from); + + migrate_qmp(from, to, uri, NULL, "{}"); + + migration_event_wait(from, phase); + migrate_cancel(from); + + /* + * qmp_migrate_cancel() exits early if migration is not running + * anymore, the status will not change to cancelled. + */ + wait_for_migration_complete(from); + wait_for_migration_complete(to); +} + +static void test_cancel_src_after_none(QTestState *from, QTestState *to, + const char *uri, const char *phase) +{ + /* + * Test that cancelling without a migration happening does not + * affect subsequent migrations + */ + migrate_cancel(to); + + wait_for_serial("src_serial"); + migrate_cancel(from); + + migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }"); + + migrate_ensure_converge(from); + migrate_qmp(from, to, uri, NULL, "{}"); + + wait_for_migration_complete(from); + wait_for_migration_complete(to); +} + +static void test_cancel_src_pre_switchover(QTestState *from, QTestState *to, + const char *uri, const char *phase) +{ + migrate_set_capability(from, "pause-before-switchover", true); + migrate_set_capability(to, "pause-before-switchover", true); + + migrate_set_capability(from, "multifd", true); + migrate_set_capability(to, "multifd", true); + + migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }"); + + wait_for_serial("src_serial"); + migrate_ensure_converge(from); + + migrate_qmp(from, to, uri, NULL, "{}"); + + migration_event_wait(from, phase); + migrate_cancel(from); + migration_event_wait(from, "cancelling"); + + wait_for_migration_status(from, "cancelled", + (const char * []) { "completed", NULL }); + + wait_for_migration_status(to, "failed", + (const char * []) { "completed", NULL }); +} + +static void test_cancel_src_after_status(void *opaque) +{ + const char *test_path = opaque; + g_autofree char *phase = g_path_get_basename(test_path); + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + MigrateStart args = { + .hide_stderr = true, + }; + + if (migrate_start(&from, &to, "defer", &args)) { + return; + } + + if (g_str_equal(phase, "cancelling") || + g_str_equal(phase, "cancelled")) { + test_cancel_src_after_cancelled(from, to, uri, phase); + + } else if (g_str_equal(phase, "completed")) { + test_cancel_src_after_complete(from, to, uri, phase); + + } else if (g_str_equal(phase, "failed")) { + test_cancel_src_after_failed(from, to, uri, phase); + + } else if (g_str_equal(phase, "none")) { + test_cancel_src_after_none(from, to, uri, phase); + + } else { + /* any state that comes before pre-switchover */ + test_cancel_src_pre_switchover(from, to, uri, phase); + } + + migrate_end(from, to, false); +} + +static void calc_dirty_rate(QTestState *who, uint64_t calc_time) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'calc-dirty-rate'," + "'arguments': { " + "'calc-time': %" PRIu64 "," + "'mode': 'dirty-ring' }}", + calc_time); +} + +static QDict *query_dirty_rate(QTestState *who) +{ + return qtest_qmp_assert_success_ref(who, + "{ 'execute': 'query-dirty-rate' }"); +} + +static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'set-vcpu-dirty-limit'," + "'arguments': { " + "'dirty-rate': %" PRIu64 " } }", + dirtyrate); +} + +static void cancel_vcpu_dirty_limit(QTestState *who) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'cancel-vcpu-dirty-limit' }"); +} + +static QDict *query_vcpu_dirty_limit(QTestState *who) +{ + QDict *rsp; + + rsp = qtest_qmp(who, "{ 'execute': 'query-vcpu-dirty-limit' }"); + g_assert(!qdict_haskey(rsp, "error")); + g_assert(qdict_haskey(rsp, "return")); + + return rsp; +} + +static bool calc_dirtyrate_ready(QTestState *who) +{ + QDict *rsp_return; + const char *status; + bool ready; + + rsp_return = query_dirty_rate(who); + g_assert(rsp_return); + + status = qdict_get_str(rsp_return, "status"); + g_assert(status); + ready = g_strcmp0(status, "measuring"); + qobject_unref(rsp_return); + + return ready; +} + +static void wait_for_calc_dirtyrate_complete(QTestState *who, + int64_t time_s) +{ + int max_try_count = 10000; + usleep(time_s * 1000000); + + while (!calc_dirtyrate_ready(who) && max_try_count--) { + usleep(1000); + } + + /* + * Set the timeout with 10 s(max_try_count * 1000us), + * if dirtyrate measurement not complete, fail test. + */ + g_assert_cmpint(max_try_count, !=, 0); +} + +static int64_t get_dirty_rate(QTestState *who) +{ + QDict *rsp_return; + const char *status; + QList *rates; + const QListEntry *entry; + QDict *rate; + int64_t dirtyrate; + + rsp_return = query_dirty_rate(who); + g_assert(rsp_return); + + status = qdict_get_str(rsp_return, "status"); + g_assert(status); + g_assert_cmpstr(status, ==, "measured"); + + rates = qdict_get_qlist(rsp_return, "vcpu-dirty-rate"); + g_assert(rates && !qlist_empty(rates)); + + entry = qlist_first(rates); + g_assert(entry); + + rate = qobject_to(QDict, qlist_entry_obj(entry)); + g_assert(rate); + + dirtyrate = qdict_get_try_int(rate, "dirty-rate", -1); + + qobject_unref(rsp_return); + return dirtyrate; +} + +static int64_t get_limit_rate(QTestState *who) +{ + QDict *rsp_return; + QList *rates; + const QListEntry *entry; + QDict *rate; + int64_t dirtyrate; + + rsp_return = query_vcpu_dirty_limit(who); + g_assert(rsp_return); + + rates = qdict_get_qlist(rsp_return, "return"); + g_assert(rates && !qlist_empty(rates)); + + entry = qlist_first(rates); + g_assert(entry); + + rate = qobject_to(QDict, qlist_entry_obj(entry)); + g_assert(rate); + + dirtyrate = qdict_get_try_int(rate, "limit-rate", -1); + + qobject_unref(rsp_return); + return dirtyrate; +} + +static QTestState *dirtylimit_start_vm(void) +{ + QTestState *vm = NULL; + g_autofree gchar *cmd = NULL; + const char *bootpath; + + bootpath = bootfile_create(qtest_get_arch(), tmpfs, false); + cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 " + "-name dirtylimit-test,debug-threads=on " + "-m 150M -smp 1 " + "-serial file:%s/vm_serial " + "-drive file=%s,format=raw ", + tmpfs, bootpath); + + vm = qtest_init(cmd); + return vm; +} + +static void dirtylimit_stop_vm(QTestState *vm) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, "vm_serial"); + + qtest_quit(vm); + unlink(path); +} + +static void test_vcpu_dirty_limit(void) +{ + QTestState *vm; + int64_t origin_rate; + int64_t quota_rate; + int64_t rate ; + int max_try_count = 20; + int hit = 0; + + /* Start vm for vcpu dirtylimit test */ + vm = dirtylimit_start_vm(); + + /* Wait for the first serial output from the vm*/ + wait_for_serial("vm_serial"); + + /* Do dirtyrate measurement with calc time equals 1s */ + calc_dirty_rate(vm, 1); + + /* Sleep calc time and wait for calc dirtyrate complete */ + wait_for_calc_dirtyrate_complete(vm, 1); + + /* Query original dirty page rate */ + origin_rate = get_dirty_rate(vm); + + /* VM booted from bootsect should dirty memory steadily */ + assert(origin_rate != 0); + + /* Setup quota dirty page rate at half of origin */ + quota_rate = origin_rate / 2; + + /* Set dirtylimit */ + dirtylimit_set_all(vm, quota_rate); + + /* + * Check if set-vcpu-dirty-limit and query-vcpu-dirty-limit + * works literally + */ + g_assert_cmpint(quota_rate, ==, get_limit_rate(vm)); + + /* Sleep a bit to check if it take effect */ + usleep(2000000); + + /* + * Check if dirtylimit take effect realistically, set the + * timeout with 20 s(max_try_count * 1s), if dirtylimit + * doesn't take effect, fail test. + */ + while (--max_try_count) { + calc_dirty_rate(vm, 1); + wait_for_calc_dirtyrate_complete(vm, 1); + rate = get_dirty_rate(vm); + + /* + * Assume hitting if current rate is less + * than quota rate (within accepting error) + */ + if (rate < (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { + hit = 1; + break; + } + } + + g_assert_cmpint(hit, ==, 1); + + hit = 0; + max_try_count = 20; + + /* Check if dirtylimit cancellation take effect */ + cancel_vcpu_dirty_limit(vm); + while (--max_try_count) { + calc_dirty_rate(vm, 1); + wait_for_calc_dirtyrate_complete(vm, 1); + rate = get_dirty_rate(vm); + + /* + * Assume dirtylimit be canceled if current rate is + * greater than quota rate (within accepting error) + */ + if (rate > (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { + hit = 1; + break; + } + } + + g_assert_cmpint(hit, ==, 1); + dirtylimit_stop_vm(vm); +} + +static void migrate_dirty_limit_wait_showup(QTestState *from, + const int64_t period, + const int64_t value) +{ + /* Enable dirty limit capability */ + migrate_set_capability(from, "dirty-limit", true); + + /* Set dirty limit parameters */ + migrate_set_parameter_int(from, "x-vcpu-dirty-limit-period", period); + migrate_set_parameter_int(from, "vcpu-dirty-limit", value); + + /* Make sure migrate can't converge */ + migrate_ensure_non_converge(from); + + /* To check limit rate after precopy */ + migrate_set_capability(from, "pause-before-switchover", true); + + /* Wait for the serial output from the source */ + wait_for_serial("src_serial"); +} + +/* + * This test does: + * source destination + * start vm + * start incoming vm + * migrate + * wait dirty limit to begin + * cancel migrate + * cancellation check + * restart incoming vm + * migrate + * wait dirty limit to begin + * wait pre-switchover event + * convergence condition check + * + * And see if dirty limit migration works correctly. + * This test case involves many passes, so it runs in slow mode only. + */ +static void test_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 (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(get_src()->stop_seen); + } + + /* Now cancel migrate and wait for dirty limit throttle switch off */ + migrate_cancel(from); + wait_for_migration_status(from, "cancelled", NULL); + + /* destination always fails after cancel */ + migration_event_wait(to, "failed"); + qtest_set_expected_status(to, EXIT_FAILURE); + qtest_quit(to); + + /* 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(get_src()->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 (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(get_src()->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); + + migrate_end(from, to, true); +} + +static void migration_test_add_precopy_smoke(MigrationTestEnv *env) +{ + if (env->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); + } + + migration_test_add("/migration/precopy/unix/plain", + test_precopy_unix_plain); + + migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain); + migration_test_add("/migration/multifd/tcp/uri/plain/none", + test_multifd_tcp_uri_none); + migration_test_add("/migration/multifd/tcp/plain/cancel", + test_multifd_precopy_tcp_cancel); + if (env->has_uffd) { + migration_test_add("/migration/multifd+postcopy/tcp/plain/cancel", + test_multifd_postcopy_tcp_cancel); + } + +#ifdef CONFIG_RDMA + migration_test_add("/migration/precopy/rdma/plain", + test_precopy_rdma_plain); + migration_test_add("/migration/precopy/rdma/plain/ipv6", + test_precopy_rdma_plain_ipv6); +#endif +} + +void migration_test_add_precopy(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add_precopy_smoke(env); + + if (!env->full_set) { + return; + } + + migration_test_add("/migration/precopy/tcp/plain/switchover-ack", + test_precopy_tcp_switchover_ack); + +#ifndef _WIN32 + migration_test_add("/migration/precopy/fd/tcp", + test_precopy_fd_socket); + migration_test_add("/migration/precopy/fd/file", + test_precopy_fd_file); +#endif + + /* + * See explanation why this test is slow on function definition + */ + if (g_test_slow()) { + migration_test_add("/migration/auto_converge", + test_auto_converge); + if (g_str_equal(env->arch, "x86_64") && + env->has_kvm && env->has_dirty_ring) { + migration_test_add("/dirty_limit", + test_dirty_limit); + } + } + 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); + if (g_str_equal(env->arch, "x86_64") + && env->has_kvm && env->has_dirty_ring) { + + 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); + } + } + + /* ensure new status don't go unnoticed */ + assert(MIGRATION_STATUS__MAX == 15); + + for (int i = MIGRATION_STATUS_NONE; i < MIGRATION_STATUS__MAX; i++) { + switch (i) { + case MIGRATION_STATUS_DEVICE: /* happens too fast */ + case MIGRATION_STATUS_WAIT_UNPLUG: /* no support in tests */ + case MIGRATION_STATUS_COLO: /* no support in tests */ + case MIGRATION_STATUS_POSTCOPY_ACTIVE: /* postcopy can't be cancelled */ + case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP: + case MIGRATION_STATUS_POSTCOPY_RECOVER: + continue; + default: + migration_test_add_suffix("/migration/cancel/src/after/", + MigrationStatus_str(i), + test_cancel_src_after_status); + } + } +} diff --git a/tests/migration/s390x/Makefile b/tests/qtest/migration/s390x/Makefile similarity index 100% rename from tests/migration/s390x/Makefile rename to tests/qtest/migration/s390x/Makefile diff --git a/tests/migration/s390x/a-b-bios.c b/tests/qtest/migration/s390x/a-b-bios.c similarity index 78% rename from tests/migration/s390x/a-b-bios.c rename to tests/qtest/migration/s390x/a-b-bios.c index a0327cd153b..ff99a3ef57d 100644 --- a/tests/migration/s390x/a-b-bios.c +++ b/tests/qtest/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/qtest/migration/s390x/a-b-bios.h b/tests/qtest/migration/s390x/a-b-bios.h new file mode 100644 index 00000000000..96103dadbbf --- /dev/null +++ b/tests/qtest/migration/s390x/a-b-bios.h @@ -0,0 +1,279 @@ +/* This file is automatically generated from the a-b-bios.c file in + * tests/migration/s390x. 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 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, 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, 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, + 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 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, 0x0f, + 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, 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, 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, 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, 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, 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, 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, + 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, + 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, 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 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, + 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 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, 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, 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, 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, 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/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c new file mode 100644 index 00000000000..21e9fec87d8 --- /dev/null +++ b/tests/qtest/migration/tls-tests.c @@ -0,0 +1,871 @@ +/* + * QTest testcases for TLS migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "crypto/tlscredspsk.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" + +#include "tests/unit/crypto-tls-psk-helpers.h" +#ifdef CONFIG_TASN1 +# include "tests/unit/crypto-tls-x509-helpers.h" +#endif /* CONFIG_TASN1 */ + + +struct TestMigrateTLSPSKData { + char *workdir; + char *workdiralt; + char *pskfile; + char *pskfilealt; +}; + +static char *tmpfs; + +static void * +migrate_hook_start_tls_psk_common(QTestState *from, + QTestState *to, + bool mismatch) +{ + struct TestMigrateTLSPSKData *data = + g_new0(struct TestMigrateTLSPSKData, 1); + + data->workdir = g_strdup_printf("%s/tlscredspsk0", tmpfs); + data->pskfile = g_strdup_printf("%s/%s", data->workdir, + QCRYPTO_TLS_CREDS_PSKFILE); + g_mkdir_with_parents(data->workdir, 0700); + test_tls_psk_init(data->pskfile); + + if (mismatch) { + data->workdiralt = g_strdup_printf("%s/tlscredspskalt0", tmpfs); + data->pskfilealt = g_strdup_printf("%s/%s", data->workdiralt, + QCRYPTO_TLS_CREDS_PSKFILE); + g_mkdir_with_parents(data->workdiralt, 0700); + test_tls_psk_init_alt(data->pskfilealt); + } + + qtest_qmp_assert_success(from, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-psk'," + " 'id': 'tlscredspsk0'," + " 'endpoint': 'client'," + " 'dir': %s," + " 'username': 'qemu'} }", + data->workdir); + + 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"); + + return data; +} + +static void * +migrate_hook_start_tls_psk_match(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_tls_psk_common(from, to, false); +} + +static void * +migrate_hook_start_tls_psk_mismatch(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_tls_psk_common(from, to, true); +} + +static void +migrate_hook_end_tls_psk(QTestState *from, + QTestState *to, + void *opaque) +{ + struct TestMigrateTLSPSKData *data = opaque; + + test_tls_psk_cleanup(data->pskfile); + if (data->pskfilealt) { + test_tls_psk_cleanup(data->pskfilealt); + } + rmdir(data->workdir); + if (data->workdiralt) { + rmdir(data->workdiralt); + } + + g_free(data->workdiralt); + g_free(data->pskfilealt); + g_free(data->workdir); + g_free(data->pskfile); + g_free(data); +} + +#ifdef CONFIG_TASN1 +typedef struct { + char *workdir; + char *keyfile; + char *cacert; + char *servercert; + char *serverkey; + char *clientcert; + char *clientkey; +} TestMigrateTLSX509Data; + +typedef struct { + bool verifyclient; + bool clientcert; + bool hostileclient; + bool authzclient; + const char *certhostname; + const char *certipaddr; +} TestMigrateTLSX509; + +static void * +migrate_hook_start_tls_x509_common(QTestState *from, + QTestState *to, + TestMigrateTLSX509 *args) +{ + TestMigrateTLSX509Data *data = g_new0(TestMigrateTLSX509Data, 1); + + data->workdir = g_strdup_printf("%s/tlscredsx5090", tmpfs); + data->keyfile = g_strdup_printf("%s/key.pem", data->workdir); + + data->cacert = g_strdup_printf("%s/ca-cert.pem", data->workdir); + data->serverkey = g_strdup_printf("%s/server-key.pem", data->workdir); + data->servercert = g_strdup_printf("%s/server-cert.pem", data->workdir); + if (args->clientcert) { + data->clientkey = g_strdup_printf("%s/client-key.pem", data->workdir); + data->clientcert = g_strdup_printf("%s/client-cert.pem", data->workdir); + } + + g_mkdir_with_parents(data->workdir, 0700); + + test_tls_init(data->keyfile); +#ifndef _WIN32 + g_assert(link(data->keyfile, data->serverkey) == 0); +#else + g_assert(CreateHardLink(data->serverkey, data->keyfile, NULL) != 0); +#endif + if (args->clientcert) { +#ifndef _WIN32 + g_assert(link(data->keyfile, data->clientkey) == 0); +#else + g_assert(CreateHardLink(data->clientkey, data->keyfile, NULL) != 0); +#endif + } + + TLS_ROOT_REQ_SIMPLE(cacertreq, data->cacert); + if (args->clientcert) { + TLS_CERT_REQ_SIMPLE_CLIENT(servercertreq, cacertreq, + args->hostileclient ? + 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); + + 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); + } + + 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) { + 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"); + } + + return data; +} + +/* + * The normal case: match server's cert hostname against + * whatever host we were telling QEMU to connect to (if any) + */ +static void * +migrate_hook_start_tls_x509_default_host(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .certipaddr = "127.0.0.1" + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The unusual case: the server's cert is different from + * the address we're telling QEMU to connect to (if any), + * so we must give QEMU an explicit hostname to validate + */ +static void * +migrate_hook_start_tls_x509_override_host(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .certhostname = "qemu.org", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The unusual case: the server's cert is different from + * the address we're telling QEMU to connect to, and so we + * expect the client to reject the server + */ +static void * +migrate_hook_start_tls_x509_mismatch_host(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .certipaddr = "10.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +static void * +migrate_hook_start_tls_x509_friendly_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .authzclient = true, + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +static void * +migrate_hook_start_tls_x509_hostile_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .hostileclient = true, + .authzclient = true, + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The case with no client certificate presented, + * and no server verification + */ +static void * +migrate_hook_start_tls_x509_allow_anon_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The case with no client certificate presented, + * and server verification rejecting + */ +static void * +migrate_hook_start_tls_x509_reject_anon_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +static void +migrate_hook_end_tls_x509(QTestState *from, + QTestState *to, + void *opaque) +{ + TestMigrateTLSX509Data *data = opaque; + + test_tls_cleanup(data->keyfile); + g_free(data->keyfile); + + unlink(data->cacert); + g_free(data->cacert); + unlink(data->servercert); + g_free(data->servercert); + unlink(data->serverkey); + g_free(data->serverkey); + + if (data->clientcert) { + unlink(data->clientcert); + g_free(data->clientcert); + } + if (data->clientkey) { + unlink(data->clientkey); + g_free(data->clientkey); + } + + rmdir(data->workdir); + g_free(data->workdir); + + g_free(data); +} +#endif /* CONFIG_TASN1 */ + +static void test_postcopy_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_preempt_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + .start = { + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_recovery_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_multifd_postcopy_recovery_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + + test_postcopy_recovery_common(&args); +} + +/* This contains preempt+recovery+tls test altogether */ +static void test_postcopy_preempt_all(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + .start = { + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_multifd_postcopy_preempt_recovery_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_precopy_unix_tls_psk(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_precopy_common(&args); +} + +#ifdef CONFIG_TASN1 +static void test_precopy_unix_tls_x509_default_host(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL_DEST_QUIT_ERR, + }; + + test_precopy_common(&args); +} + +static void test_precopy_unix_tls_x509_override_host(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} +#endif /* CONFIG_TASN1 */ + +static void test_precopy_tcp_tls_psk_match(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_psk_mismatch(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_psk_mismatch, + .end_hook = migrate_hook_end_tls_psk, + .result = MIG_TEST_FAIL, + }; + + test_precopy_common(&args); +} + +#ifdef CONFIG_TASN1 +static void test_precopy_tcp_tls_x509_default_host(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_override_host(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_mismatch_host(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_mismatch_host, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL_DEST_QUIT_ERR, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_friendly_client(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_friendly_client, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_hostile_client(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_hostile_client, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_allow_anon_client(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_allow_anon_client, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_reject_anon_client(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_reject_anon_client, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + + test_precopy_common(&args); +} +#endif /* CONFIG_TASN1 */ + +static void * +migrate_hook_start_multifd_tcp_tls_psk_match(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_psk_match(from, to); +} + +static void * +migrate_hook_start_multifd_tcp_tls_psk_mismatch(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_psk_mismatch(from, to); +} + +#ifdef CONFIG_TASN1 +static void * +migrate_hook_start_multifd_tls_x509_default_host(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_default_host(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_override_host(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_override_host(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_mismatch_host(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_mismatch_host(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_allow_anon_client(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_allow_anon_client(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_reject_anon_client(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_reject_anon_client(from, to); +} +#endif /* CONFIG_TASN1 */ + +static void test_multifd_tcp_tls_psk_match(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tcp_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_psk_mismatch(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tcp_tls_psk_mismatch, + .end_hook = migrate_hook_end_tls_psk, + .result = MIG_TEST_FAIL, + }; + test_precopy_common(&args); +} + +static void test_multifd_postcopy_tcp_tls_psk_match(void) +{ + MigrateCommon args = { + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true, + }, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tcp_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_precopy_common(&args); +} + +#ifdef CONFIG_TASN1 +static void test_multifd_tcp_tls_x509_default_host(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_override_host(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_mismatch_host(void) +{ + /* + * This has different behaviour to the non-multifd case. + * + * In non-multifd case when client aborts due to mismatched + * cert host, the server has already started trying to load + * migration state, and so it exits with I/O failure. + * + * In multifd case when client aborts due to mismatched + * cert host, the server is still waiting for the other + * multifd connections to arrive so hasn't started trying + * to load migration state, and thus just aborts the migration + * without exiting. + */ + MigrateCommon args = { + .start = { + .hide_stderr = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_mismatch_host, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_allow_anon_client(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_allow_anon_client, + .end_hook = migrate_hook_end_tls_x509, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_reject_anon_client(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_reject_anon_client, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + test_precopy_common(&args); +} +#endif /* CONFIG_TASN1 */ + +static void migration_test_add_tls_smoke(MigrationTestEnv *env) +{ + migration_test_add("/migration/precopy/tcp/tls/psk/match", + test_precopy_tcp_tls_psk_match); +} + +void migration_test_add_tls(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add_tls_smoke(env); + + if (!env->full_set) { + return; + } + + migration_test_add("/migration/precopy/unix/tls/psk", + test_precopy_unix_tls_psk); + + if (env->has_uffd) { + /* + * NOTE: psk test is enough for postcopy, as other types of TLS + * channels are tested under precopy. Here what we want to test is the + * general postcopy path that has TLS channel enabled. + */ + migration_test_add("/migration/postcopy/tls/psk", + test_postcopy_tls_psk); + migration_test_add("/migration/postcopy/recovery/tls/psk", + test_postcopy_recovery_tls_psk); + migration_test_add("/migration/postcopy/preempt/tls/psk", + test_postcopy_preempt_tls_psk); + migration_test_add("/migration/postcopy/preempt/recovery/tls/psk", + test_postcopy_preempt_all); + migration_test_add("/migration/multifd+postcopy/recovery/tls/psk", + test_multifd_postcopy_recovery_tls_psk); + migration_test_add( + "/migration/multifd+postcopy/preempt/recovery/tls/psk", + test_multifd_postcopy_preempt_recovery_tls_psk); + } +#ifdef CONFIG_TASN1 + 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 */ + + migration_test_add("/migration/precopy/tcp/tls/psk/mismatch", + test_precopy_tcp_tls_psk_mismatch); +#ifdef CONFIG_TASN1 + 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 */ + + 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); + if (env->has_uffd) { + migration_test_add("/migration/multifd+postcopy/tcp/tls/psk/match", + test_multifd_postcopy_tcp_tls_psk_match); + } +#ifdef CONFIG_TASN1 + 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 */ +} diff --git a/tests/qtest/netdev-socket.c b/tests/qtest/netdev-socket.c index 8eed54801f2..b731af0ad95 100644 --- a/tests/qtest/netdev-socket.c +++ b/tests/qtest/netdev-socket.c @@ -11,7 +11,7 @@ #include #include "../unit/socket-helpers.h" #include "libqtest.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "qemu/sockets.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-sockets.h" @@ -127,7 +127,7 @@ static void test_stream_inet_ipv4(void) "addr.ipv4=on,addr.ipv6=off," "addr.host=127.0.0.1,addr.port=%d", port); - EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0); + 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," @@ -200,11 +200,11 @@ static void test_stream_unix_reconnect(void) "-netdev stream,id=st0,server=true,addr.type=unix," "addr.path=%s", path); - EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0); + EXPECT_STATE(qts0, "st0: index=0,type=stream,listening\r\n", 0); qts1 = qtest_initf("-nodefaults -M none " "-netdev stream,server=false,id=st0,addr.type=unix," - "addr.path=%s,reconnect=1", path); + "addr.path=%s,reconnect-ms=1000", path); wait_stream_connected(qts0, "st0", &addr); g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_UNIX); @@ -250,7 +250,7 @@ static void test_stream_inet_ipv6(void) "addr.ipv4=off,addr.ipv6=on," "addr.host=::1,addr.port=%d", port); - EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0); + 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," @@ -282,7 +282,7 @@ static void test_stream_unix(void) "addr.type=unix,addr.path=%s,", path); - EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0); + EXPECT_STATE(qts0, "st0: index=0,type=stream,listening\r\n", 0); qts1 = qtest_initf("-nodefaults -M none " "-netdev stream,id=st0,server=false," @@ -314,7 +314,7 @@ static void test_stream_unix_abstract(void) "addr.abstract=on", path); - EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0); + EXPECT_STATE(qts0, "st0: index=0,type=stream,listening\r\n", 0); qts1 = qtest_initf("-nodefaults -M none " "-netdev stream,id=st0,server=false," @@ -401,7 +401,7 @@ static void test_dgram_inet(void) qtest_quit(qts0); } -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(CONFIG_DARWIN) static void test_dgram_mcast(void) { QTestState *qts; @@ -414,7 +414,9 @@ static void test_dgram_mcast(void) qtest_quit(qts); } +#endif +#ifndef _WIN32 static void test_dgram_unix(void) { QTestState *qts0, *qts1; @@ -511,7 +513,7 @@ int main(int argc, char **argv) if (has_ipv4) { qtest_add_func("/netdev/stream/inet/ipv4", test_stream_inet_ipv4); qtest_add_func("/netdev/dgram/inet", test_dgram_inet); -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(CONFIG_DARWIN) qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast); #endif } @@ -524,7 +526,7 @@ int main(int argc, char **argv) #ifndef _WIN32 qtest_add_func("/netdev/dgram/unix", test_dgram_unix); #endif - qtest_add_func("/netdev/stream/unix", test_stream_unix); + qtest_add_func("/netdev/stream/unix/oneshot", test_stream_unix); qtest_add_func("/netdev/stream/unix/reconnect", test_stream_unix_reconnect); #ifdef CONFIG_LINUX diff --git a/tests/qtest/npcm7xx_adc-test.c b/tests/qtest/npcm7xx_adc-test.c index 8048044d281..8bc89b8a8b9 100644 --- a/tests/qtest/npcm7xx_adc-test.c +++ b/tests/qtest/npcm7xx_adc-test.c @@ -18,7 +18,7 @@ #include "qemu/bitops.h" #include "qemu/timer.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define REF_HZ (25000000) @@ -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 b046f1d76af..eeedb27ee68 100644 --- a/tests/qtest/npcm7xx_emc-test.c +++ b/tests/qtest/npcm7xx_emc-test.c @@ -16,8 +16,8 @@ #include "qemu/osdep.h" #include "libqos/libqos.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/bitops.h" #include "qemu/iov.h" @@ -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 ea4ca1d106e..052ea876621 100644 --- a/tests/qtest/npcm7xx_pwm-test.c +++ b/tests/qtest/npcm7xx_pwm-test.c @@ -17,8 +17,8 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" static int verbosity_level; @@ -606,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); @@ -618,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); @@ -678,6 +679,7 @@ 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"); @@ -687,8 +689,13 @@ int main(int argc, char **argv) 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 5d68540e520..01f237a8162 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_watchdog_timer-test.c b/tests/qtest/npcm7xx_watchdog_timer-test.c index 4773a673b20..521ea789f18 100644 --- a/tests/qtest/npcm7xx_watchdog_timer-test.c +++ b/tests/qtest/npcm7xx_watchdog_timer-test.c @@ -18,7 +18,7 @@ #include "qemu/timer.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define WTCR_OFFSET 0x1c #define REF_HZ (25000000) @@ -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 00000000000..1317da2cd7c --- /dev/null +++ b/tests/qtest/npcm_gmac-test.c @@ -0,0 +1,343 @@ +/* + * 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/npcm8xx.c */ +static const GMACModule gmac_module_list[] = { + { + .irq = 14, + .base_addr = 0xf0802000 + }, + { + .irq = 15, + .base_addr = 0xf0804000 + }, + { + .irq = 16, + .base_addr = 0xf0806000 + }, + { + .irq = 17, + .base_addr = 0xf0808000 + } +}; + +/* 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); +} + +static uint16_t pcs_read(QTestState *qts, const GMACModule *mod, + NPCMRegister regno) +{ + uint32_t write_value = (regno & 0x3ffe00) >> 9; + qtest_writel(qts, PCS_BASE_ADDRESS + NPCM_PCS_IND_AC_BA, write_value); + uint32_t read_offset = regno & 0x1ff; + return qtest_readl(qts, PCS_BASE_ADDRESS + read_offset); +} + +/* 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 npcm845-evb"); + +#define CHECK_REG32(regno, value) \ + do { \ + g_assert_cmphex(gmac_read(qts, mod, (regno)), ==, (value)); \ + } while (0) + +#define CHECK_REG_PCS(regno, value) \ + do { \ + g_assert_cmphex(pcs_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); + + if (mod->base_addr == 0xf0802000) { + CHECK_REG_PCS(NPCM_PCS_SR_CTL_ID1, 0x699e); + CHECK_REG_PCS(NPCM_PCS_SR_CTL_ID2, 0); + CHECK_REG_PCS(NPCM_PCS_SR_CTL_STS, 0x8000); + + CHECK_REG_PCS(NPCM_PCS_SR_MII_CTRL, 0x1140); + CHECK_REG_PCS(NPCM_PCS_SR_MII_STS, 0x0109); + CHECK_REG_PCS(NPCM_PCS_SR_MII_DEV_ID1, 0x699e); + CHECK_REG_PCS(NPCM_PCS_SR_MII_DEV_ID2, 0x0ced0); + CHECK_REG_PCS(NPCM_PCS_SR_MII_AN_ADV, 0x0020); + CHECK_REG_PCS(NPCM_PCS_SR_MII_LP_BABL, 0); + CHECK_REG_PCS(NPCM_PCS_SR_MII_AN_EXPN, 0); + CHECK_REG_PCS(NPCM_PCS_SR_MII_EXT_STS, 0xc000); + + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_ABL, 0x0003); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR, 0x0038); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_UPR, 0); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR, 0x0038); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_UPR, 0); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR, 0x0058); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_UPR, 0); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR, 0x0048); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_UPR, 0); + + CHECK_REG_PCS(NPCM_PCS_VR_MII_MMD_DIG_CTRL1, 0x2400); + CHECK_REG_PCS(NPCM_PCS_VR_MII_AN_CTRL, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_AN_INTR_STS, 0x000a); + CHECK_REG_PCS(NPCM_PCS_VR_MII_TC, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_DBG_CTRL, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_MCTRL0, 0x899c); + CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_TXTIMER, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_RXTIMER, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_LINK_TIMER_CTRL, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_MCTRL1, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_DIG_STS, 0x0010); + CHECK_REG_PCS(NPCM_PCS_VR_MII_ICG_ERRCNT1, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MISC_STS, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_RX_LSTS, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_BSTCTRL0, 0x00a); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_LVLCTRL0, 0x007f); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_GENCTRL0, 0x0001); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_GENCTRL1, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_STS, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_RX_GENCTRL0, 0x0100); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_RX_GENCTRL1, 0x1100); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0, 0x000e); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MPLL_CTRL0, 0x0100); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MPLL_CTRL1, 0x0032); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MPLL_STS, 0x0001); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MISC_CTRL2, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_LVL_CTRL, 0x0019); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MISC_CTRL0, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MISC_CTRL1, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_DIG_CTRL2, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_DIG_ERRCNT_SEL, 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( + "npcm8xx_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 c5eb13f349f..d657f389474 100644 --- a/tests/qtest/numa-test.c +++ b/tests/qtest/numa-test.c @@ -11,8 +11,8 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" static char *make_cli(const GString *generic_cli, const char *test_cli) { @@ -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 008d189b0fa..5ad6821f7af 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 d80ed93cd3a..74749576923 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/pnv-host-i2c-test.c b/tests/qtest/pnv-host-i2c-test.c new file mode 100644 index 00000000000..51e613ebdcb --- /dev/null +++ b/tests/qtest/pnv-host-i2c-test.c @@ -0,0 +1,489 @@ +/* + * 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]; + send_buf[0] = PCA9554_INPUT; + pnv_i2c_send(dev, send_buf, 1); + pnv_i2c_recv(dev, recv_buf, 1); + return recv_buf[0]; +} + +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 00000000000..600493c425d --- /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=chip0.spi.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 00000000000..bf2bce00567 --- /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 00000000000..2077c05ebc7 --- /dev/null +++ b/tests/qtest/pnv-xive2-common.h @@ -0,0 +1,112 @@ +/* + * 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); +void test_nvpg_bar(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 00000000000..142826bad0f --- /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; + + g_test_message("========================================================="); + g_test_message("Starting cache flush/queue sync injection tests..."); + + for (test_nr = 0; test_nr < sizeof(xive_inject_tests); + test_nr++) { + int op_type = xive_inject_tests[test_nr]; + + g_test_message("Running test %d", 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-nvpg_bar.c b/tests/qtest/pnv-xive2-nvpg_bar.c new file mode 100644 index 00000000000..6ac8d36c821 --- /dev/null +++ b/tests/qtest/pnv-xive2-nvpg_bar.c @@ -0,0 +1,152 @@ +/* + * QTest testcase for PowerNV 10 interrupt controller (xive2) + * - Test NVPG BAR MMIO operations + * + * 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" + +#define NVPG_BACKLOG_OP_SHIFT 10 +#define NVPG_BACKLOG_PRIO_SHIFT 4 + +#define XIVE_PRIORITY_MAX 7 + +enum NVx { + NVP, + NVG, + NVC +}; + +typedef enum { + INCR_STORE = 0b100, + INCR_LOAD = 0b000, + DECR_STORE = 0b101, + DECR_LOAD = 0b001, + READ_x = 0b010, + READ_y = 0b011, +} backlog_op; + +static uint32_t nvpg_backlog_op(QTestState *qts, backlog_op op, + enum NVx type, uint64_t index, + uint8_t priority, uint8_t delta) +{ + uint64_t addr, offset; + uint32_t count = 0; + + switch (type) { + case NVP: + addr = XIVE_NVPG_ADDR + (index << (XIVE_PAGE_SHIFT + 1)); + break; + case NVG: + addr = XIVE_NVPG_ADDR + (index << (XIVE_PAGE_SHIFT + 1)) + + (1 << XIVE_PAGE_SHIFT); + break; + case NVC: + addr = XIVE_NVC_ADDR + (index << XIVE_PAGE_SHIFT); + break; + default: + g_assert_not_reached(); + } + + offset = (op & 0b11) << NVPG_BACKLOG_OP_SHIFT; + offset |= priority << NVPG_BACKLOG_PRIO_SHIFT; + if (op >> 2) { + qtest_writeb(qts, addr + offset, delta); + } else { + count = qtest_readw(qts, addr + offset); + } + return count; +} + +void test_nvpg_bar(QTestState *qts) +{ + uint32_t nvp_target = 0x11; + uint32_t group_target = 0x17; /* size 16 */ + uint32_t vp_irq = 33, group_irq = 47; + uint32_t vp_end = 3, group_end = 97; + uint32_t vp_irq_data = 0x33333333; + uint32_t group_irq_data = 0x66666666; + uint8_t vp_priority = 0, group_priority = 5; + uint32_t vp_count[XIVE_PRIORITY_MAX + 1] = { 0 }; + uint32_t group_count[XIVE_PRIORITY_MAX + 1] = { 0 }; + uint32_t count, delta; + uint8_t i; + + g_test_message("========================================================="); + g_test_message("Testing NVPG BAR operations"); + + set_nvg(qts, group_target, 0); + set_nvp(qts, nvp_target, 0x04); + set_nvp(qts, group_target, 0x04); + + /* + * Setup: trigger a VP-specific interrupt and a group interrupt + * so that the backlog counters are initialized to something else + * than 0 for at least one priority level + */ + set_eas(qts, vp_irq, vp_end, vp_irq_data); + set_end(qts, vp_end, nvp_target, vp_priority, false /* group */); + + set_eas(qts, group_irq, group_end, group_irq_data); + set_end(qts, group_end, group_target, group_priority, true /* group */); + + get_esb(qts, vp_irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); + set_esb(qts, vp_irq, XIVE_TRIGGER_PAGE, 0, 0); + vp_count[vp_priority]++; + + get_esb(qts, group_irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); + set_esb(qts, group_irq, XIVE_TRIGGER_PAGE, 0, 0); + group_count[group_priority]++; + + /* check the initial counters */ + for (i = 0; i <= XIVE_PRIORITY_MAX; i++) { + count = nvpg_backlog_op(qts, READ_x, NVP, nvp_target, i, 0); + g_assert_cmpuint(count, ==, vp_count[i]); + + count = nvpg_backlog_op(qts, READ_y, NVG, group_target, i, 0); + g_assert_cmpuint(count, ==, group_count[i]); + } + + /* do a few ops on the VP. Counter can only be 0 and 1 */ + vp_priority = 2; + delta = 7; + nvpg_backlog_op(qts, INCR_STORE, NVP, nvp_target, vp_priority, delta); + vp_count[vp_priority] = 1; + count = nvpg_backlog_op(qts, INCR_LOAD, NVP, nvp_target, vp_priority, 0); + g_assert_cmpuint(count, ==, vp_count[vp_priority]); + count = nvpg_backlog_op(qts, READ_y, NVP, nvp_target, vp_priority, 0); + g_assert_cmpuint(count, ==, vp_count[vp_priority]); + + count = nvpg_backlog_op(qts, DECR_LOAD, NVP, nvp_target, vp_priority, 0); + g_assert_cmpuint(count, ==, vp_count[vp_priority]); + vp_count[vp_priority] = 0; + nvpg_backlog_op(qts, DECR_STORE, NVP, nvp_target, vp_priority, delta); + count = nvpg_backlog_op(qts, READ_x, NVP, nvp_target, vp_priority, 0); + g_assert_cmpuint(count, ==, vp_count[vp_priority]); + + /* do a few ops on the group */ + group_priority = 2; + delta = 9; + /* can't go negative */ + nvpg_backlog_op(qts, DECR_STORE, NVG, group_target, group_priority, delta); + count = nvpg_backlog_op(qts, READ_y, NVG, group_target, group_priority, 0); + g_assert_cmpuint(count, ==, 0); + nvpg_backlog_op(qts, INCR_STORE, NVG, group_target, group_priority, delta); + group_count[group_priority] += delta; + count = nvpg_backlog_op(qts, INCR_LOAD, NVG, group_target, + group_priority, delta); + g_assert_cmpuint(count, ==, group_count[group_priority]); + group_count[group_priority]++; + + count = nvpg_backlog_op(qts, DECR_LOAD, NVG, group_target, + group_priority, delta); + g_assert_cmpuint(count, ==, group_count[group_priority]); + group_count[group_priority]--; + count = nvpg_backlog_op(qts, READ_x, NVG, group_target, group_priority, 0); + g_assert_cmpuint(count, ==, group_count[group_priority]); +} diff --git a/tests/qtest/pnv-xive2-test.c b/tests/qtest/pnv-xive2-test.c new file mode 100644 index 00000000000..5313d4ef18b --- /dev/null +++ b/tests/qtest/pnv-xive2-test.c @@ -0,0 +1,585 @@ +/* + * QTest testcase for PowerNV 10 interrupt controller (xive2) + * - Test irq to hardware thread + * - Test 'Pull Thread Context to Odd Thread Reporting Line' + * - Test irq to hardware group + * - Test irq to hardware group going through backlog + * - Test irq to pool thread + * + * 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; + + g_test_message("========================================================="); + g_test_message("Testing irq %d to hardware thread %d", 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); +} + +static void test_pool_irq(QTestState *qts) +{ + uint32_t irq = 2; + uint32_t irq_data = 0x600d0d06; + uint32_t end_index = 5; + uint32_t target_pir = 1; + uint32_t target_nvp = 0x100 + target_pir; + uint8_t priority = 5; + uint32_t reg32; + uint16_t reg16; + uint8_t pq, nsr, cppr, ipb; + + g_test_message("========================================================="); + g_test_message("Testing irq %d to pool thread %d", 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); + + /* check TIMA values in the PHYS ring (shared by POOL ring) */ + reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x40); + g_assert_cmphex(cppr, ==, 0xFF); + + /* check TIMA values in the POOL ring */ + reg32 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + ipb = (reg32 >> 8) & 0xFF; + g_assert_cmphex(nsr, ==, 0); + g_assert_cmphex(cppr, ==, 0); + g_assert_cmphex(ipb, ==, 0x80 >> priority); + + /* ack the irq */ + reg16 = get_tima16(qts, target_pir, TM_SPC_ACK_HV_REG); + nsr = reg16 >> 8; + cppr = reg16 & 0xFF; + g_assert_cmphex(nsr, ==, 0x40); + 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)); + + /* check IPB is cleared in the POOL ring */ + reg32 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD0); + ipb = (reg32 >> 8) & 0xFF; + g_assert_cmphex(ipb, ==, 0); + + /* 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; + + g_test_message("========================================================="); + g_test_message("Testing 'Pull Thread Context to Odd Thread Reporting " \ + "Line'"); + + /* 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_hw_group_irq(QTestState *qts) +{ + uint32_t irq = 100; + uint32_t irq_data = 0xdeadbeef; + uint32_t end_index = 23; + uint32_t chosen_one; + uint32_t target_nvp = 0x81; /* group size = 4 */ + uint8_t priority = 6; + uint32_t reg32; + uint16_t reg16; + uint8_t pq, nsr, cppr; + + g_test_message("========================================================="); + g_test_message("Testing irq %d to hardware group of size 4", irq); + + /* irq config */ + set_eas(qts, irq, end_index, irq_data); + set_end(qts, end_index, target_nvp, priority, true /* 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); + + /* find the targeted vCPU */ + for (chosen_one = 0; chosen_one < SMT; chosen_one++) { + reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + if (nsr == 0x82) { + break; + } + } + g_assert_cmphex(chosen_one, <, SMT); + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x82); + g_assert_cmphex(cppr, ==, 0xFF); + + /* ack the irq */ + reg16 = get_tima16(qts, chosen_one, TM_SPC_ACK_HV_REG); + nsr = reg16 >> 8; + cppr = reg16 & 0xFF; + g_assert_cmphex(nsr, ==, 0x82); + 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, chosen_one, TM_QW3_HV_PHYS + TM_CPPR, 0xFF); + reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x00); + g_assert_cmphex(cppr, ==, 0xFF); +} + +static void test_hw_group_irq_backlog(QTestState *qts) +{ + uint32_t irq = 31; + uint32_t irq_data = 0x01234567; + uint32_t end_index = 129; + uint32_t target_nvp = 0x81; /* group size = 4 */ + uint32_t chosen_one = 3; + uint8_t blocking_priority, priority = 3; + uint32_t reg32; + uint16_t reg16; + uint8_t pq, nsr, cppr, lsmfb, i; + + g_test_message("========================================================="); + g_test_message("Testing irq %d to hardware group of size 4 going " \ + "through backlog", + irq); + + /* + * set current priority of all threads in the group to something + * higher than what we're about to trigger + */ + blocking_priority = priority - 1; + for (i = 0; i < SMT; i++) { + set_tima8(qts, i, TM_QW3_HV_PHYS + TM_CPPR, blocking_priority); + } + + /* irq config */ + set_eas(qts, irq, end_index, irq_data); + set_end(qts, end_index, target_nvp, priority, true /* 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); + + /* check no interrupt is pending on the 2 possible targets */ + for (i = 0; i < SMT; i++) { + reg32 = get_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + lsmfb = reg32 & 0xFF; + g_assert_cmphex(nsr, ==, 0x0); + g_assert_cmphex(cppr, ==, blocking_priority); + g_assert_cmphex(lsmfb, ==, priority); + } + + /* lower priority of one thread */ + set_tima8(qts, chosen_one, TM_QW3_HV_PHYS + TM_CPPR, priority + 1); + + /* check backlogged interrupt is presented */ + reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x82); + g_assert_cmphex(cppr, ==, priority + 1); + + /* ack the irq */ + reg16 = get_tima16(qts, chosen_one, TM_SPC_ACK_HV_REG); + nsr = reg16 >> 8; + cppr = reg16 & 0xFF; + g_assert_cmphex(nsr, ==, 0x82); + 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, chosen_one, TM_QW3_HV_PHYS + TM_CPPR, 0xFF); + reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + lsmfb = reg32 & 0xFF; + g_assert_cmphex(nsr, ==, 0x00); + g_assert_cmphex(cppr, ==, 0xFF); + g_assert_cmphex(lsmfb, ==, 0xFF); +} + +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_pool_irq(qts); + + reset_state(qts); + test_hw_group_irq(qts); + + reset_state(qts); + test_hw_group_irq_backlog(qts); + + reset_state(qts); + test_flush_sync_inject(qts); + + reset_state(qts); + test_nvpg_bar(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 8a5ac110377..c814c0f4f5b 100644 --- a/tests/qtest/pnv-xscom-test.c +++ b/tests/qtest/pnv-xscom-test.c @@ -10,66 +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 */ - 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; -} 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, - }, - { - .chip_type = PNV_CHIP_POWER10, - .cpu_model = "POWER10", - .xscom_base = 0x000603fc00000000ull, - .cfam_id = 0x120da04900008000ull, - .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_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; -} +#include "pnv-xscom.h" static uint64_t pnv_xscom_read(QTestState *qts, const PnvChip *chip, uint32_t pcba) diff --git a/tests/qtest/pnv-xscom.h b/tests/qtest/pnv-xscom.h new file mode 100644 index 00000000000..5aa1701ea76 --- /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 00000000000..f68ee935205 --- /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 39ccb59797d..14705105ad8 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 2c05b376ba7..f788a44dbed 100644 --- a/tests/qtest/pvpanic-pci-test.c +++ b/tests/qtest/pvpanic-pci-test.c @@ -13,9 +13,10 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/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,11 +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) { 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); return g_test_run(); } diff --git a/tests/qtest/pvpanic-test.c b/tests/qtest/pvpanic-test.c index 78f1cf8186b..5606baf47be 100644 --- a/tests/qtest/pvpanic-test.c +++ b/tests/qtest/pvpanic-test.c @@ -9,7 +9,8 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/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,11 +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) { 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); return g_test_run(); } diff --git a/tests/qtest/pxe-test.c b/tests/qtest/pxe-test.c index e4b48225a5a..a3f900fbea3 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" diff --git a/tests/qtest/q35-test.c b/tests/qtest/q35-test.c index c922d81bc02..62fff49fc89 100644 --- a/tests/qtest/q35-test.c +++ b/tests/qtest/q35-test.c @@ -14,7 +14,7 @@ #include "libqos/pci.h" #include "libqos/pci-pc.h" #include "hw/pci-host/q35.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define TSEG_SIZE_TEST_GUEST_RAM_MBYTES 128 @@ -83,7 +83,6 @@ static void test_smram_lock(void) { QPCIBus *pcibus; QPCIDevice *pcidev; - QDict *response; QTestState *qts; qts = qtest_init("-M q35"); @@ -107,10 +106,7 @@ static void test_smram_lock(void) g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); /* reset */ - response = qtest_qmp(qts, "{'execute': 'system_reset', 'arguments': {} }"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); + qtest_system_reset(qts); /* check open is settable again */ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false); @@ -194,7 +190,6 @@ static void test_smram_smbase_lock(void) { QPCIBus *pcibus; QPCIDevice *pcidev; - QDict *response; QTestState *qts; int i; @@ -237,10 +232,7 @@ static void test_smram_smbase_lock(void) } /* reset */ - response = qtest_qmp(qts, "{'execute': 'system_reset', 'arguments': {} }"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); + qtest_system_reset(qts); /* check RAM at SMBASE is available after reset */ g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN); @@ -254,41 +246,6 @@ static void test_smram_smbase_lock(void) qtest_quit(qts); } -static void test_without_smram_base(void) -{ - QPCIBus *pcibus; - QPCIDevice *pcidev; - QTestState *qts; - int i; - - qts = qtest_init("-M pc-q35-4.1"); - - pcibus = qpci_new_pc(qts, NULL); - g_assert(pcibus != NULL); - - pcidev = qpci_device_find(pcibus, 0); - g_assert(pcidev != NULL); - - /* check that RAM is accessible */ - qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN); - g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN); - - /* check that writing to 0x9c succeeds */ - for (i = 0; i <= 0xff; i++) { - qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, i); - g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == i); - } - - /* check that RAM is still accessible */ - qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN + 1); - g_assert_cmpint(qtest_readb(qts, SMBASE), ==, (SMRAM_TEST_PATTERN + 1)); - - g_free(pcidev); - qpci_free_pc(pcibus); - - qtest_quit(qts); -} - int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -301,6 +258,6 @@ int main(int argc, char **argv) qtest_add_data_func("/q35/tseg-size/ext/16mb", &tseg_ext_16mb, test_tseg_size); qtest_add_func("/q35/smram/smbase_lock", test_smram_smbase_lock); - qtest_add_func("/q35/smram/legacy_smbase", test_without_smram_base); + return g_test_run(); } diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 73a670e8fa9..040d042810b 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -14,7 +14,7 @@ #include "libqtest.h" #include "qapi/error.h" #include "qapi/qapi-visit-introspect.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" const char common_args[] = "-nodefaults -machine none"; @@ -45,6 +45,7 @@ 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 }, /* Only valid with a USB bus added */ { "x-query-usb", ERROR_CLASS_GENERIC_ERROR }, @@ -99,6 +100,7 @@ static bool query_is_ignored(const char *cmd) /* Success depends on target arch: */ "query-cpu-definitions", /* arm, i386, ppc, s390x */ "query-gic-capabilities", /* arm */ + "query-s390x-cpu-polarization", /* s390x */ /* Success depends on target-specific build configuration: */ "query-pci", /* CONFIG_PCI */ "x-query-virtio", /* CONFIG_VIRTIO */ diff --git a/tests/qtest/qmp-test.c b/tests/qtest/qmp-test.c index 22957fa49c2..edf08867874 100644 --- a/tests/qtest/qmp-test.c +++ b/tests/qtest/qmp-test.c @@ -14,10 +14,10 @@ #include "libqtest.h" #include "qapi/error.h" #include "qapi/qapi-visit-control.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "qapi/qobject-input-visitor.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" const char common_args[] = "-nodefaults -machine none"; diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index d677f87c8e2..27d70bc11ce 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -9,8 +9,8 @@ #include "qemu/osdep.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "qemu/cutils.h" #include "libqtest.h" @@ -88,6 +88,17 @@ static void test_machine(gconstpointer data) qts = qtest_initf("-machine %s", machine); + if (g_test_slow()) { + /* Make sure we can get the machine class properties: */ + g_autofree char *qom_machine = g_strdup_printf("%s-machine", machine); + + response = qtest_qmp(qts, "{ 'execute': 'qom-list-properties'," + " 'arguments': { 'typename': %s } }", + qom_machine); + g_assert(response); + qobject_unref(response); + } + test_properties(qts, "/machine", true); response = qtest_qmp(qts, "{ 'execute': 'quit' }"); diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c index 5da4091ec32..abfd4b9512d 100644 --- a/tests/qtest/qos-test.c +++ b/tests/qtest/qos-test.c @@ -20,7 +20,7 @@ #include #include "libqtest-single.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/module.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-machine.h" @@ -33,7 +33,6 @@ static char *old_path; - /** * qos_set_machines_devices_available(): sets availability of qgraph * machines and devices. @@ -104,8 +103,7 @@ static void restart_qemu_or_continue(char *path) old_path = g_strdup(path); qtest_start(path); } else { /* if cmd line is the same, reset the guest */ - qobject_unref(qmp("{ 'execute': 'system_reset' }")); - qmp_eventwait("RESET"); + qtest_system_reset(global_qtest); } } @@ -191,6 +189,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 +299,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.h b/tests/qtest/qtest_aspeed.h index 235dfaa186a..d35b0c7cba5 100644 --- a/tests/qtest/qtest_aspeed.h +++ b/tests/qtest/qtest_aspeed.h @@ -12,8 +12,6 @@ #ifndef QTEST_ASPEED_H #define QTEST_ASPEED_H -#include - #include "libqtest.h" #define AST2600_ASPEED_I2C_BASE_ADDR 0x1e78a000 diff --git a/tests/qtest/readconfig-test.c b/tests/qtest/readconfig-test.c index 760f974e631..c6f32a4e144 100644 --- a/tests/qtest/readconfig-test.c +++ b/tests/qtest/readconfig-test.c @@ -13,10 +13,10 @@ #include "qapi/qapi-visit-machine.h" #include "qapi/qapi-visit-qom.h" #include "qapi/qapi-visit-ui.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "qapi/qobject-input-visitor.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "qemu/units.h" static QTestState *qtest_init_with_config(const char *cfgdata) diff --git a/tests/qtest/riscv-csr-test.c b/tests/qtest/riscv-csr-test.c new file mode 100644 index 00000000000..ff5c29e6c6f --- /dev/null +++ b/tests/qtest/riscv-csr-test.c @@ -0,0 +1,56 @@ +/* + * QTest testcase for RISC-V CSRs + * + * Copyright (c) 2024 Syntacore. + * + * This program is free software; you can redistribute it and/or modify it + * under the 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 "libqtest.h" + +#define CSR_MVENDORID 0xf11 +#define CSR_MISELECT 0x350 + +static void run_test_csr(void) +{ + uint64_t res; + uint64_t val = 0; + + QTestState *qts = qtest_init("-machine virt -cpu veyron-v1"); + + res = qtest_csr_call(qts, "get_csr", 0, CSR_MVENDORID, &val); + + g_assert_cmpint(res, ==, 0); + g_assert_cmpint(val, ==, 0x61f); + + val = 0xff; + res = qtest_csr_call(qts, "set_csr", 0, CSR_MISELECT, &val); + + g_assert_cmpint(res, ==, 0); + + val = 0; + res = qtest_csr_call(qts, "get_csr", 0, CSR_MISELECT, &val); + + g_assert_cmpint(res, ==, 0); + g_assert_cmpint(val, ==, 0xff); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/cpu/csr", run_test_csr); + + return g_test_run(); +} diff --git a/tests/qtest/riscv-iommu-test.c b/tests/qtest/riscv-iommu-test.c new file mode 100644 index 00000000000..df0c7813ec0 --- /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/rs5c372-test.c b/tests/qtest/rs5c372-test.c new file mode 100644 index 00000000000..0f6a9b68b9f --- /dev/null +++ b/tests/qtest/rs5c372-test.c @@ -0,0 +1,43 @@ +/* + * QTest testcase for the RS5C372 RTC + * + * Copyright (c) 2025 Bernhard Beschow + * + * Based on ds1338-test.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/bcd.h" +#include "libqos/i2c.h" + +#define RS5C372_ADDR 0x32 + +static void rs5c372_read_date(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = obj; + + uint8_t resp[0x10]; + time_t now = time(NULL); + struct tm *utc = gmtime(&now); + + i2c_read_block(i2cdev, 0, resp, sizeof(resp)); + + /* check retrieved time against local time */ + g_assert_cmpuint(from_bcd(resp[5]), == , utc->tm_mday); + g_assert_cmpuint(from_bcd(resp[6]), == , 1 + utc->tm_mon); + g_assert_cmpuint(2000 + from_bcd(resp[7]), == , 1900 + utc->tm_year); +} + +static void rs5c372_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { }; + add_qi2c_address(&opts, &(QI2CAddress) { RS5C372_ADDR }); + + qos_node_create_driver("rs5c372", i2c_device_create); + qos_node_consumes("rs5c372", "i2c-bus", &opts); + qos_add_test("read_date", "rs5c372", rs5c372_read_date, NULL); +} + +libqos_init(rs5c372_register_nodes); diff --git a/tests/qtest/rtl8139-test.c b/tests/qtest/rtl8139-test.c index 4dc0a0d22ed..55f671f2f59 100644 --- a/tests/qtest/rtl8139-test.c +++ b/tests/qtest/rtl8139-test.c @@ -22,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) @@ -46,7 +46,7 @@ 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)); \ + unsigned res = qpci_io_read##len(pcidev, dev_bar, (val)); \ if (verbosity_level >= 2) { \ g_test_message("*%s -> %x", #name, res); \ } \ @@ -57,7 +57,7 @@ static void out_##name(unsigned v) \ if (verbosity_level >= 2) { \ g_test_message("%x -> *%s", v, #name); \ } \ - qpci_io_write##len(dev, dev_bar, (val), v); \ + qpci_io_write##len(pcidev, dev_bar, (val), v); \ } PORT(Timer, l, 0x48) @@ -65,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) { @@ -189,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(); } diff --git a/tests/qtest/sse-timer-test.c b/tests/qtest/sse-timer-test.c index a65d7542d51..fd5635d4c92 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 00000000000..2d21cc666cc --- /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 00000000000..7e39c992fd3 --- /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 00000000000..3c6ea71febf --- /dev/null +++ b/tests/qtest/stm32l4x5_gpio-test.c @@ -0,0 +1,580 @@ +/* + * 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 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); + + qtest_system_reset(global_qtest); + + 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 00000000000..d927c655d13 --- /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 00000000000..376c80e31ca --- /dev/null +++ b/tests/qtest/stm32l4x5_syscfg-test.c @@ -0,0 +1,342 @@ +/* + * 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 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); + + qtest_system_reset(global_qtest); +} + +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); + + qtest_system_reset(global_qtest); +} + +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 00000000000..98a7472307f --- /dev/null +++ b/tests/qtest/stm32l4x5_usart-test.c @@ -0,0 +1,375 @@ +/* + * 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) +{ + 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); + return g_test_run(); +} + diff --git a/tests/qtest/tco-test.c b/tests/qtest/tco-test.c index 0547d411738..20ccefabcbf 100644 --- a/tests/qtest/tco-test.c +++ b/tests/qtest/tco-test.c @@ -12,7 +12,7 @@ #include "libqtest.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/pci/pci_regs.h" #include "hw/southbridge/ich9.h" #include "hw/acpi/ich9.h" diff --git a/tests/qtest/test-filter-mirror.c b/tests/qtest/test-filter-mirror.c index adeada3eb87..723d2c2f291 100644 --- a/tests/qtest/test-filter-mirror.c +++ b/tests/qtest/test-filter-mirror.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/iov.h" #include "qemu/sockets.h" #include "qemu/error-report.h" @@ -61,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); diff --git a/tests/qtest/test-filter-redirector.c b/tests/qtest/test-filter-redirector.c index e72e3b78735..a996a80c1c0 100644 --- a/tests/qtest/test-filter-redirector.c +++ b/tests/qtest/test-filter-redirector.c @@ -52,7 +52,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/iov.h" #include "qemu/sockets.h" #include "qemu/error-report.h" @@ -118,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); @@ -185,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 fc9125f8bba..1b2e07522fe 100644 --- a/tests/qtest/test-hmp.c +++ b/tests/qtest/test-hmp.c @@ -64,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", diff --git a/tests/qtest/test-netfilter.c b/tests/qtest/test-netfilter.c index b09ef7fae98..326d4bd85f1 100644 --- a/tests/qtest/test-netfilter.c +++ b/tests/qtest/test-netfilter.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "libqtest-single.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" /* add a netfilter to a netdev and then remove it */ static void add_one_netfilter(void) diff --git a/tests/qtest/test-x86-cpuid-compat.c b/tests/qtest/test-x86-cpuid-compat.c index b39c9055b30..456e2af6657 100644 --- a/tests/qtest/test-x86-cpuid-compat.c +++ b/tests/qtest/test-x86-cpuid-compat.c @@ -1,8 +1,8 @@ #include "qemu/osdep.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qbool.h" #include "libqtest-single.h" static char *get_cpu0_qom_path(void) @@ -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,16 @@ 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; } 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 +232,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,174 +255,141 @@ 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, - * 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", - "level", 4); - add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/on", - "-machine pc-i440fx-2.3 -cpu Penryn,erms=on", - "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); } - /* - * xlevel doesn't have any feature that triggers auto-level - * code on old machine-types. Just check that the compat code - * is working correctly: - */ - 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", - "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,", - "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", - "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 3678646df52..3b114a50f55 100644 --- a/tests/qtest/tmp105-test.c +++ b/tests/qtest/tmp105-test.c @@ -12,7 +12,7 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/sensor/tmp105_regs.h" #define TMP105_TEST_ID "tmp105-test" @@ -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-emu.c b/tests/qtest/tpm-emu.c index 2bf8ff4c86e..9e4c2005d0e 100644 --- a/tests/qtest/tpm-emu.c +++ b/tests/qtest/tpm-emu.c @@ -16,8 +16,8 @@ #include "backends/tpm/tpm_ioctl.h" #include "io/channel-socket.h" #include "qapi/error.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" #include "tpm-emu.h" void tpm_emu_test_wait_cond(TPMTestState *s) diff --git a/tests/qtest/tpm-emu.h b/tests/qtest/tpm-emu.h index 712cee9b7a5..59c8009f4c5 100644 --- a/tests/qtest/tpm-emu.h +++ b/tests/qtest/tpm-emu.h @@ -21,7 +21,7 @@ #include "qemu/sockets.h" #include "io/channel.h" -#include "sysemu/tpm.h" +#include "system/tpm.h" #include "libqtest.h" struct tpm_hdr { diff --git a/tests/qtest/tpm-tests.c b/tests/qtest/tpm-tests.c index fb94496bbd8..197714f8d99 100644 --- a/tests/qtest/tpm-tests.c +++ b/tests/qtest/tpm-tests.c @@ -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-util.c b/tests/qtest/tpm-util.c index 1c0319e6e70..2cb2dd47961 100644 --- a/tests/qtest/tpm-util.c +++ b/tests/qtest/tpm-util.c @@ -18,7 +18,7 @@ #include "hw/acpi/tpm.h" #include "libqtest.h" #include "tpm-util.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" void tpm_util_crb_transfer(QTestState *s, const unsigned char *req, size_t req_size, diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index ed3dbca1549..4867ccf08a1 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -8,13 +8,14 @@ #include "qemu/osdep.h" #include "qemu/module.h" -#include "qemu/units.h" #include "libqtest.h" #include "libqos/qgraph.h" #include "libqos/pci.h" #include "scsi/constants.h" -#include "include/block/ufs.h" +#include "block/ufs.h" +#include "qemu/bitmap.h" +#define DWORD_BYTE 4 /* Test images sizes in Bytes */ #define TEST_IMAGE_SIZE (64 * 1024 * 1024) /* Timeout for various operations, in seconds. */ @@ -26,6 +27,12 @@ #define UTP_COMMAND_DESCRIPTOR_SIZE 4096 #define UTP_RESPONSE_UPIU_OFFSET 1024 #define UTP_PRDT_UPIU_OFFSET 2048 +#define UTRD_TEST_SLOT 0 +#define UFS_MAX_CMD_DESC 32 +/* Constants for MCQ */ +#define TEST_QID 0 +#define QUEUE_SIZE 32 +#define UFS_MCQ_MAX_QNUM 32 typedef struct QUfs QUfs; @@ -34,12 +41,22 @@ struct QUfs { QPCIDevice dev; QPCIBar bar; - uint64_t utrlba; - uint64_t utmrlba; + DECLARE_BITMAP(cmd_desc_bitmap, UFS_MAX_CMD_DESC); uint64_t cmd_desc_addr; uint64_t data_buffer_addr; bool enabled; + bool support_mcq; + + /* for legacy doorbell mode */ + uint64_t utrlba; + + /* for mcq mode */ + uint32_t maxq; + uint64_t sqlba[UFS_MCQ_MAX_QNUM]; + uint64_t cqlba[UFS_MCQ_MAX_QNUM]; + uint64_t sqdao[UFS_MCQ_MAX_QNUM]; + uint64_t cqdao[UFS_MCQ_MAX_QNUM]; }; static inline uint32_t ufs_rreg(QUfs *ufs, size_t offset) @@ -52,6 +69,24 @@ static inline void ufs_wreg(QUfs *ufs, size_t offset, uint32_t value) qpci_io_writel(&ufs->dev, ufs->bar, offset, value); } +static int alloc_cmd_desc_slot(QUfs *ufs) +{ + int slot = find_first_zero_bit(ufs->cmd_desc_bitmap, UFS_MAX_CMD_DESC); + if (slot == UFS_MAX_CMD_DESC) { + g_assert_not_reached(); + } + set_bit(slot, ufs->cmd_desc_bitmap); + return slot; +} + +static void release_cmd_desc_slot(QUfs *ufs, int slot) +{ + if (!test_bit(slot, ufs->cmd_desc_bitmap)) { + g_assert_not_reached(); + } + clear_bit(slot, ufs->cmd_desc_bitmap); +} + static void ufs_wait_for_irq(QUfs *ufs) { uint64_t end_time; @@ -64,14 +99,11 @@ static void ufs_wait_for_irq(QUfs *ufs) } while (is == 0 && g_get_monotonic_time() < end_time); } -static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr, - uint8_t slot, +static UtpTransferReqDesc ufs_build_req_utrd(uint64_t command_desc_base_addr, 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); @@ -88,80 +120,140 @@ static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr, return req; } -static void ufs_send_nop_out(QUfs *ufs, uint8_t slot, - UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) +static enum UtpOcsCodes +__ufs_send_transfer_request_doorbell(QUfs *ufs, uint8_t lun, + const UtpTransferReqDesc *utrd) { - /* 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 utrd_addr = + ufs->utrlba + UTRD_TEST_SLOT * sizeof(UtpTransferReqDesc); + UtpTransferReqDesc utrd_result; + + qtest_memwrite(ufs->dev.bus->qts, utrd_addr, utrd, sizeof(*utrd)); + + /* Ring the 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)); + + /* Handle completed command */ + qtest_memread(ufs->dev.bus->qts, utrd_addr, &utrd_result, + sizeof(utrd_result)); + return le32_to_cpu(utrd_result.header.dword_2) & 0xf; +} + +static enum UtpOcsCodes +__ufs_send_transfer_request_mcq(QUfs *ufs, uint8_t lun, + const UtpTransferReqDesc *utrd) +{ + uint32_t sqtp = ufs_rreg(ufs, ufs->sqdao[TEST_QID] + 0x4); + uint64_t utrd_addr = ufs->sqlba[TEST_QID] + sqtp; + uint32_t cqhp; + uint64_t cqentry_addr; + UfsCqEntry cqentry; + + qtest_memwrite(ufs->dev.bus->qts, utrd_addr, utrd, sizeof(*utrd)); + + /* Insert a new entry into the submission queue */ + sqtp = ufs_rreg(ufs, ufs->sqdao[TEST_QID] + 0x4); + sqtp = (sqtp + sizeof(UfsSqEntry)) % (QUEUE_SIZE * sizeof(UfsSqEntry)); + ufs_wreg(ufs, ufs->sqdao[TEST_QID] + 0x4, sqtp); + ufs_wait_for_irq(ufs); + g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, CQES)); + ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, CQES, 1)); + + /* Handle the completed command from the completion queue */ + cqhp = ufs_rreg(ufs, ufs->cqdao[TEST_QID]); + cqentry_addr = ufs->cqlba[TEST_QID] + cqhp; + qtest_memread(ufs->dev.bus->qts, cqentry_addr, &cqentry, sizeof(cqentry)); + ufs_wreg(ufs, ufs->cqdao[TEST_QID], cqhp); + + return cqentry.status; +} + +static enum UtpOcsCodes +ufs_send_transfer_request_sync(QUfs *ufs, uint8_t lun, + const UtpTransferReqDesc *utrd) +{ + if (ufs->support_mcq) { + return __ufs_send_transfer_request_mcq(ufs, lun, utrd); + } + + return __ufs_send_transfer_request_doorbell(ufs, lun, utrd); +} + +static enum UtpOcsCodes ufs_send_nop_out(QUfs *ufs, UtpUpiuRsp *rsp_out) +{ + int cmd_desc_slot = alloc_cmd_desc_slot(ufs); uint64_t req_upiu_addr = - ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + ufs->cmd_desc_addr + cmd_desc_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; + req_upiu.header.task_tag = cmd_desc_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)); + /* Build up utp transfer request descriptor */ + UtpTransferReqDesc utrd = + ufs_build_req_utrd(req_upiu_addr, UFS_UTP_NO_DATA_TRANSFER, 0); + + /* Send Transfer Request */ + enum UtpOcsCodes ret = ufs_send_transfer_request_sync(ufs, 0, &utrd); - 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)); + release_cmd_desc_slot(ufs, cmd_desc_slot); + return ret; } -static void ufs_send_query(QUfs *ufs, uint8_t slot, uint8_t query_function, - uint8_t query_opcode, uint8_t idn, uint8_t index, - UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) +static enum UtpOcsCodes ufs_send_query(QUfs *ufs, uint8_t query_function, + uint8_t query_opcode, uint8_t idn, + uint8_t index, uint8_t selector, + uint32_t attr_value, 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); + int cmd_desc_slot = alloc_cmd_desc_slot(ufs); uint64_t req_upiu_addr = - ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + ufs->cmd_desc_addr + cmd_desc_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; + req_upiu.header.task_tag = cmd_desc_slot; /* - * QEMU UFS does not currently support Write descriptor and Write attribute, + * 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 = cpu_to_be32(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)); + /* Build up utp transfer request descriptor */ + UtpTransferReqDesc utrd = + ufs_build_req_utrd(req_upiu_addr, UFS_UTP_NO_DATA_TRANSFER, 0); + + /* Send Transfer Request */ + enum UtpOcsCodes ret = ufs_send_transfer_request_sync(ufs, 0, &utrd); - 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)); + release_cmd_desc_slot(ufs, cmd_desc_slot); + return ret; } -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) +static enum UtpOcsCodes +ufs_send_scsi_command(QUfs *ufs, 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, + UtpUpiuRsp *rsp_out) { /* Build up PRDT */ @@ -171,8 +263,9 @@ static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun, uint8_t flags; uint16_t prd_table_length, i; uint32_t data_direction, data_len; + int cmd_desc_slot = alloc_cmd_desc_slot(ufs); uint64_t req_upiu_addr = - ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + ufs->cmd_desc_addr + cmd_desc_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); @@ -214,36 +307,33 @@ static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun, 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.header.task_tag = cmd_desc_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)); + /* Build up utp transfer request descriptor */ + UtpTransferReqDesc utrd = + ufs_build_req_utrd(req_upiu_addr, data_direction, prd_table_length); + + /* Send Transfer Request */ + enum UtpOcsCodes ret = ufs_send_transfer_request_sync(ufs, lun, &utrd); - 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); } + release_cmd_desc_slot(ufs, cmd_desc_slot); + return ret; } /** @@ -253,10 +343,10 @@ static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun, static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) { uint64_t end_time; - uint32_t nutrs, nutmrs; + uint32_t nutrs; uint32_t hcs, is, ucmdarg2, cap; uint32_t hce = 0, ie = 0; - UtpTransferReqDesc utrd; + enum UtpOcsCodes ocs; UtpUpiuRsp rsp_upiu; ufs->bar = qpci_iomap(&ufs->dev, 0, NULL); @@ -301,9 +391,12 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) 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)); + /* Check MCQ support */ + cap = ufs_rreg(ufs, A_CAP); + ufs->support_mcq = FIELD_EX32(cap, CAP, MCQS); + /* Enable all interrupt functions */ ie = FIELD_DP32(ie, IE, UTRCE, 1); ie = FIELD_DP32(ie, IE, UEE, 1); @@ -316,44 +409,89 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) ie = FIELD_DP32(ie, IE, HCFEE, 1); ie = FIELD_DP32(ie, IE, SBFEE, 1); ie = FIELD_DP32(ie, IE, CEFEE, 1); + if (ufs->support_mcq) { + ie = FIELD_DP32(ie, IE, CQEE, 1); + } ufs_wreg(ufs, A_IE, ie); ufs_wreg(ufs, A_UTRIACR, 0); - /* Enable tranfer 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; + /* Enable transfer request */ ufs->cmd_desc_addr = - guest_alloc(alloc, nutrs * UTP_COMMAND_DESCRIPTOR_SIZE); + guest_alloc(alloc, UFS_MAX_CMD_DESC * 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); + if (ufs->support_mcq) { + uint32_t mcqcap, qid, qcfgptr, mcq_reg_offset; + uint32_t cqattr = 0, sqattr = 0; + + mcqcap = ufs_rreg(ufs, A_MCQCAP); + qcfgptr = FIELD_EX32(mcqcap, MCQCAP, QCFGPTR); + ufs->maxq = FIELD_EX32(mcqcap, MCQCAP, MAXQ) + 1; + for (qid = 0; qid < ufs->maxq; ++qid) { + ufs->sqlba[qid] = + guest_alloc(alloc, QUEUE_SIZE * sizeof(UtpTransferReqDesc)); + ufs->cqlba[qid] = + guest_alloc(alloc, QUEUE_SIZE * sizeof(UtpTransferReqDesc)); + mcq_reg_offset = qcfgptr * 0x200 + qid * 0x40; + + ufs_wreg(ufs, mcq_reg_offset + A_SQLBA, + ufs->sqlba[qid] & 0xffffffff); + ufs_wreg(ufs, mcq_reg_offset + A_SQUBA, ufs->sqlba[qid] >> 32); + ufs_wreg(ufs, mcq_reg_offset + A_CQLBA, + ufs->cqlba[qid] & 0xffffffff); + ufs_wreg(ufs, mcq_reg_offset + A_CQUBA, ufs->cqlba[qid] >> 32); + + /* Enable Completion Queue */ + cqattr = FIELD_DP32(cqattr, CQATTR, CQEN, 1); + cqattr = FIELD_DP32(cqattr, CQATTR, SIZE, + QUEUE_SIZE * sizeof(UtpTransferReqDesc) / + DWORD_BYTE); + ufs_wreg(ufs, mcq_reg_offset + A_CQATTR, cqattr); + + /* Enable Submission Queue */ + sqattr = FIELD_DP32(sqattr, SQATTR, SQEN, 1); + sqattr = FIELD_DP32(sqattr, SQATTR, SIZE, + QUEUE_SIZE * sizeof(UtpTransferReqDesc) / + DWORD_BYTE); + sqattr = FIELD_DP32(sqattr, SQATTR, CQID, qid); + ufs_wreg(ufs, mcq_reg_offset + A_SQATTR, sqattr); + + /* Cache head & tail pointer */ + ufs->sqdao[qid] = ufs_rreg(ufs, mcq_reg_offset + A_SQDAO); + ufs->cqdao[qid] = ufs_rreg(ufs, mcq_reg_offset + A_CQDAO); + } + } else { + nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1; + ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc)); + + ufs_wreg(ufs, A_UTRLBA, ufs->utrlba & 0xffffffff); + ufs_wreg(ufs, A_UTRLBAU, ufs->utrlba >> 32); + ufs_wreg(ufs, A_UTRLRSR, 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); + ocs = ufs_send_nop_out(ufs, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); /* Set fDeviceInit flag via query request */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_SET_FLAG, - UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_SET_FLAG, + UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); /* Wait for device to reset */ end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; do { qtest_clock_step(ufs->dev.bus->qts, 100); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_FLAG, - UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, &utrd, &rsp_upiu); + ocs = + ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_COMMAND_RESULT_SUCCESS); } 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); @@ -364,8 +502,15 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) static void ufs_exit(QUfs *ufs, QGuestAllocator *alloc) { if (ufs->enabled) { - guest_free(alloc, ufs->utrlba); - guest_free(alloc, ufs->utmrlba); + if (ufs->support_mcq) { + for (uint32_t qid = 0; qid < ufs->maxq; ++qid) { + guest_free(alloc, ufs->sqlba[qid]); + guest_free(alloc, ufs->cqlba[qid]); + } + } else { + guest_free(alloc, ufs->utrlba); + } + guest_free(alloc, ufs->cmd_desc_addr); guest_free(alloc, ufs->data_buffer_addr); } @@ -425,25 +570,34 @@ static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc) const uint8_t test_unit_ready_cdb[UFS_CDB_SIZE] = { TEST_UNIT_READY, }; - UtpTransferReqDesc utrd; + const uint8_t request_sense_cdb[UFS_CDB_SIZE] = { + REQUEST_SENSE, + }; + enum UtpOcsCodes ocs; 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); + ocs = ufs_send_scsi_command(ufs, 0, report_luns_cdb, NULL, 0, buf, + sizeof(buf), &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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 */ + ocs = ufs_send_scsi_command(ufs, 0, request_sense_cdb, NULL, 0, buf, + sizeof(buf), &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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); + ocs = ufs_send_scsi_command(ufs, 0, test_unit_ready_cdb, NULL, 0, NULL, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); ufs_exit(ufs, alloc); @@ -473,6 +627,9 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc) 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 @@ -482,39 +639,424 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc) WRITE_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }; uint32_t block_size; - UtpTransferReqDesc utrd; + enum UtpOcsCodes ocs; UtpUpiuRsp rsp_upiu; + const int test_lun = 1; ufs_init(ufs, alloc); + /* Clear Unit Attention */ + ocs = ufs_send_scsi_command(ufs, test_lun, request_sense_cdb, NULL, 0, + read_buf, sizeof(read_buf), &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION); + /* Read capacity */ - ufs_send_scsi_command(ufs, 0, 1, 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); + ocs = ufs_send_scsi_command(ufs, test_lun, read_capacity_cdb, NULL, 0, + read_buf, sizeof(read_buf), &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, - UFS_COMMAND_RESULT_SUCESS); + UFS_COMMAND_RESULT_SUCCESS); block_size = ldl_be_p(&read_buf[8]); g_assert_cmpuint(block_size, ==, 4096); /* Write data */ - memset(write_buf, rand() % 255 + 1, block_size); - ufs_send_scsi_command(ufs, 0, 1, write_cdb, write_buf, block_size, NULL, 0, - &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + memset(write_buf, 0xab, block_size); + ocs = ufs_send_scsi_command(ufs, test_lun, write_cdb, write_buf, block_size, + NULL, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, - UFS_COMMAND_RESULT_SUCESS); + UFS_COMMAND_RESULT_SUCCESS); /* Read data and verify */ - ufs_send_scsi_command(ufs, 0, 1, read_cdb, NULL, 0, read_buf, block_size, - &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_scsi_command(ufs, test_lun, read_cdb, NULL, 0, read_buf, + block_size, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, - UFS_COMMAND_RESULT_SUCESS); + 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; + + enum UtpOcsCodes ocs; + UtpUpiuRsp rsp_upiu; + ufs_init(ufs, alloc); + + /* Read read-only flag */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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 */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_SET_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_CLEAR_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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) */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_PURGE_ENABLE, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_NOT_READABLE); + + /* Write Read-Only Flag (Intended Failure) */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_SET_FLAG, + UFS_QUERY_FLAG_IDN_BUSY_RTC, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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; + + enum UtpOcsCodes ocs; + UtpUpiuRsp rsp_upiu; + ufs_init(ufs, alloc); + + /* Read Readable Attributes*/ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_BOOT_LU_EN, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_BKOPS_STATUS, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_CASE_ROUGH_TEMP, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(160)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(60)); + + /* Write Writable Attributes & Read Again */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0x03, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0x07, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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) */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0x10, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_INVALID_VALUE); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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) */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_SECONDS_PASSED, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_NOT_READABLE); + + /* Write Read-Only Attribute (Intended Error) */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_POWER_MODE, 0, 0, 0x01, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_NOT_WRITEABLE); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_POWER_MODE, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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 */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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; + + enum UtpOcsCodes ocs; + UtpUpiuRsp rsp_upiu; + ufs_init(ufs, alloc); + + /* Write Descriptor is not supported yet */ + + /* Read Device Descriptor */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_DEVICE, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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 */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_UNIT, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_UNIT, 1, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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); + + ocs = + ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT, + UFS_UPIU_RPMB_WLUN, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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 */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_INTERCONNECT, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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 */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_STRING, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_STRING, 1, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_STRING, 4, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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 */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_GEOMETRY, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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 */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_POWER, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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 */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_HEALTH, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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) */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_UNIT, 4, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_INVALID_INDEX); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_STRING, 5, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_INVALID_INDEX); + + /* Invalid Selector (Intended Failure) */ + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_DEVICE, 0, 1, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_INVALID_SELECTOR); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_STRING, 0, 1, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, 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); @@ -556,12 +1098,16 @@ static void ufs_register_nodes(void) 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" + .extra_device_opts = "addr=04.0,id=ufs0" }; - QOSGraphTestOptions io_test_opts = { - .before = ufs_blk_test_setup, - }; + QOSGraphTestOptions io_test_opts = { .before = ufs_blk_test_setup, + .edge.extra_device_opts = + "mcq=false,nutrs=32,nutmrs=8" }; + + QOSGraphTestOptions mcq_test_opts = { .before = ufs_blk_test_setup, + .edge.extra_device_opts = + "mcq=true,mcq-maxq=1" }; add_qpci_address(&edge_opts, &(QPCIAddress){ .devfn = QPCI_DEVFN(4, 0) }); @@ -581,7 +1127,14 @@ static void ufs_register_nodes(void) return; } qos_add_test("init", "ufs", ufstest_init, NULL); - qos_add_test("read-write", "ufs", ufstest_read_write, &io_test_opts); + qos_add_test("legacy-read-write", "ufs", ufstest_read_write, &io_test_opts); + qos_add_test("mcq-read-write", "ufs", ufstest_read_write, &mcq_test_opts); + qos_add_test("query-flag", "ufs", ufstest_query_flag_request, + &io_test_opts); + qos_add_test("query-attribute", "ufs", ufstest_query_attr_request, + &io_test_opts); + qos_add_test("query-desciptor", "ufs", ufstest_query_desc_request, + &io_test_opts); } libqos_init(ufs_register_nodes); diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c index 117b9acd102..ea90d41232e 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++) { diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index d4e437265f6..75cb3e44b21 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -12,7 +12,7 @@ #include "libqtest-single.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/config-file.h" #include "qemu/option.h" #include "qemu/range.h" @@ -20,7 +20,7 @@ #include "chardev/char-fe.h" #include "qemu/memfd.h" #include "qemu/module.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "libqos/libqos.h" #include "libqos/pci-pc.h" #include "libqos/virtio-pci.h" @@ -44,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" @@ -195,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, @@ -228,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; @@ -458,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, @@ -788,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; @@ -899,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 @@ -928,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"); @@ -965,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"); @@ -980,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"); @@ -1022,7 +1043,8 @@ static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc) static uint64_t vu_net_get_features(TestServer *s) { - uint64_t features = 0x1ULL << VHOST_F_LOG_ALL | + uint64_t features = 0x1ULL << VIRTIO_F_VERSION_1 | + 0x1ULL << VHOST_F_LOG_ALL | 0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES; if (s->queues > 1) { @@ -1081,6 +1103,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", diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c index 65e69491e5a..ac38ccf5951 100644 --- a/tests/qtest/virtio-9p-test.c +++ b/tests/qtest/virtio-9p-test.c @@ -20,6 +20,7 @@ #define tversion(...) v9fs_tversion((TVersionOpt) __VA_ARGS__) #define tattach(...) v9fs_tattach((TAttachOpt) __VA_ARGS__) #define tgetattr(...) v9fs_tgetattr((TGetAttrOpt) __VA_ARGS__) +#define tsetattr(...) v9fs_tsetattr((TSetAttrOpt) __VA_ARGS__) #define treaddir(...) v9fs_treaddir((TReadDirOpt) __VA_ARGS__) #define tlopen(...) v9fs_tlopen((TLOpenOpt) __VA_ARGS__) #define twrite(...) v9fs_twrite((TWriteOpt) __VA_ARGS__) @@ -693,9 +694,78 @@ 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); + + /* truncate file to (arbitrarily chosen) size 2001 */ + tsetattr({ + .client = v9p, .fid = fid_file, .attr = (v9fs_attr) { + .valid = P9_SETATTR_SIZE, + .size = 2001 + } + }); + /* truncate apparently succeeded, let's double-check the size */ + tgetattr({ + .client = v9p, .fid = fid_file, .request_mask = P9_GETATTR_BASIC, + .rgetattr.attr = &attr + }); + g_assert_cmpint(attr.size, ==, 2001); +} + +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 +805,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 +817,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 00000000000..ecdd363b06c --- /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-ccw-test.c b/tests/qtest/virtio-ccw-test.c index f4f5858b842..7a5357c2124 100644 --- a/tests/qtest/virtio-ccw-test.c +++ b/tests/qtest/virtio-ccw-test.c @@ -85,7 +85,7 @@ int main(int argc, char **argv) if (qtest_has_device("virtio-rng-ccw")) { qtest_add_func("/virtio/rng/nop", virtio_rng_nop); } - if (qtest_has_device("virtio-rng-ccw")) { + 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); } diff --git a/tests/qtest/virtio-iommu-test.c b/tests/qtest/virtio-iommu-test.c index 068e7a9e6c1..98ffa27912c 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); @@ -105,7 +105,7 @@ static int send_map(QTestState *qts, QVirtioIOMMU *v_iommu, QVirtQueue *vq = v_iommu->vq; uint64_t ro_addr, wr_addr; uint32_t free_head; - struct virtio_iommu_req_map req; + struct virtio_iommu_req_map req = {}; size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail); size_t wr_size = sizeof(struct virtio_iommu_req_tail); struct virtio_iommu_req_tail buffer; @@ -147,7 +147,7 @@ static int send_unmap(QTestState *qts, QVirtioIOMMU *v_iommu, QVirtQueue *vq = v_iommu->vq; uint64_t ro_addr, wr_addr; uint32_t free_head; - struct virtio_iommu_req_unmap req; + struct virtio_iommu_req_unmap req = {}; size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail); size_t wr_size = sizeof(struct virtio_iommu_req_tail); struct virtio_iommu_req_tail buffer; diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c index 4a809590bf7..5baf81c3e6d 100644 --- a/tests/qtest/virtio-net-failover.c +++ b/tests/qtest/virtio-net-failover.c @@ -11,9 +11,11 @@ #include "libqtest.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qjson.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qjson.h" #include "libqos/malloc-pc.h" #include "libqos/virtio-pci.h" #include "hw/pci/pci.h" @@ -485,7 +487,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 +518,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 +567,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 +640,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 +738,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 +755,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 +773,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, NULL, "{}"); resp = get_failover_negociated_event(qts); g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0"); @@ -834,7 +809,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 +863,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 +877,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 +895,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, NULL, "{}"); check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, true, "primary0", MAC_PRIMARY0); @@ -964,7 +928,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 +990,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 +1004,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 +1022,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, NULL, "{}"); check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, false, "primary0", MAC_PRIMARY0); @@ -1102,7 +1055,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 +1155,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 +1244,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 +1343,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 +1435,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 +1531,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 +1562,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 +1681,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 +1701,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 +1725,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 +1747,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, NULL, "{}"); 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 fab5dd8b058..60e5229a3de 100644 --- a/tests/qtest/virtio-net-test.c +++ b/tests/qtest/virtio-net-test.c @@ -11,7 +11,7 @@ #include "libqtest-single.h" #include "qemu/iov.h" #include "qemu/module.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/virtio/virtio-net.h" #include "libqos/qgraph.h" #include "libqos/virtio-net.h" @@ -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"); } diff --git a/tests/qtest/virtio-scsi-test.c b/tests/qtest/virtio-scsi-test.c index ceaa7f2415b..db10d572d0f 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/vmcoreinfo-test.c b/tests/qtest/vmcoreinfo-test.c new file mode 100644 index 00000000000..dcf3b5ae058 --- /dev/null +++ b/tests/qtest/vmcoreinfo-test.c @@ -0,0 +1,90 @@ +/* + * qtest vmcoreinfo test case + * + * Copyright Red Hat. 2025. + * + * Authors: + * Ani Sinha + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "libqos/libqos-pc.h" +#include "libqtest.h" +#include "standard-headers/linux/qemu_fw_cfg.h" +#include "libqos/fw_cfg.h" +#include "qemu/bswap.h" +#include "hw/misc/vmcoreinfo.h" + +static void test_vmcoreinfo_write_basic(void) +{ + QFWCFG *fw_cfg; + QOSState *qs; + FWCfgVMCoreInfo info; + size_t filesize; + uint16_t guest_format; + uint16_t host_format; + uint32_t size; + uint64_t paddr; + + qs = qtest_pc_boot("-device vmcoreinfo"); + fw_cfg = pc_fw_cfg_init(qs->qts); + + memset(&info, 0 , sizeof(info)); + /* read vmcoreinfo and read back the host format */ + filesize = qfw_cfg_read_file(fw_cfg, qs, FW_CFG_VMCOREINFO_FILENAME, + &info, sizeof(info)); + g_assert_cmpint(filesize, ==, sizeof(info)); + + host_format = le16_to_cpu(info.host_format); + g_assert_cmpint(host_format, ==, FW_CFG_VMCOREINFO_FORMAT_ELF); + + memset(&info, 0 , sizeof(info)); + info.guest_format = cpu_to_le16(FW_CFG_VMCOREINFO_FORMAT_ELF); + info.size = cpu_to_le32(1 * MiB); + info.paddr = cpu_to_le64(0xffffff00); + info.host_format = cpu_to_le16(host_format); + + /* write the values to the host */ + filesize = qfw_cfg_write_file(fw_cfg, qs, FW_CFG_VMCOREINFO_FILENAME, + &info, sizeof(info)); + g_assert_cmpint(filesize, ==, sizeof(info)); + + memset(&info, 0 , sizeof(info)); + + /* now read back the values we wrote and compare that they are the same */ + filesize = qfw_cfg_read_file(fw_cfg, qs, FW_CFG_VMCOREINFO_FILENAME, + &info, sizeof(info)); + g_assert_cmpint(filesize, ==, sizeof(info)); + + size = le32_to_cpu(info.size); + paddr = le64_to_cpu(info.paddr); + guest_format = le16_to_cpu(info.guest_format); + + g_assert_cmpint(size, ==, 1 * MiB); + g_assert_cmpint(paddr, ==, 0xffffff00); + g_assert_cmpint(guest_format, ==, FW_CFG_VMCOREINFO_FORMAT_ELF); + + pc_fw_cfg_uninit(fw_cfg); + qtest_shutdown(qs); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { + /* skip for non-x86 */ + exit(EXIT_SUCCESS); + } + + qtest_add_func("vmcoreinfo/basic-write", + test_vmcoreinfo_write_basic); + + return g_test_run(); +} diff --git a/tests/qtest/vmgenid-test.c b/tests/qtest/vmgenid-test.c index 29fee9e7c00..e613374665e 100644 --- a/tests/qtest/vmgenid-test.c +++ b/tests/qtest/vmgenid-test.c @@ -15,7 +15,7 @@ #include "boot-sector.h" #include "acpi-utils.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define VGID_GUID "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87" #define VMGENID_GUID_OFFSET 40 /* allow space for diff --git a/tests/qtest/wdt_ib700-test.c b/tests/qtest/wdt_ib700-test.c index 797288d939f..17547571629 100644 --- a/tests/qtest/wdt_ib700-test.c +++ b/tests/qtest/wdt_ib700-test.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/timer.h" static void qmp_check_no_event(QTestState *s) diff --git a/tests/qtest/xlnx-versal-trng-test.c b/tests/qtest/xlnx-versal-trng-test.c new file mode 100644 index 00000000000..ba86f39d13c --- /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/tcg/Makefile.target b/tests/tcg/Makefile.target index 462289f47cc..95ff76ea44d 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -49,7 +49,7 @@ 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 @@ -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 fragment 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,8 +148,8 @@ 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))) @@ -155,10 +158,11 @@ PLUGINS=$(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c))) # 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,$(MULTIARCH_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 # MULTIARCH_TESTS @@ -171,17 +175,21 @@ 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$(COMMA)callback=true +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) \ + $(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, $<, \ @@ -196,6 +204,10 @@ run-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-%: % diff --git a/tests/tcg/README b/tests/tcg/README index 706bb185b42..6d08ca50dc0 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. - -CRIS -==== -The testsuite for CRIS is in tests/tcg/cris. You can run it -with "make test-cris". +This directory contains various interesting guest binaries for +regression testing the Tiny Code Generator doing system and user-mode +emulation. + +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 b74a2534e38..f7a7d2b800f 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) @@ -39,22 +48,29 @@ memory: CFLAGS+=-DCHECK_UNALIGNED=1 memory-sve: memory.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) -memory-sve: CFLAGS+=-DCHECK_UNALIGNED=1 -march=armv8.1-a+sve -O3 -fno-tree-loop-distribute-patterns +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 +QEMU_EL2_BASE_ARGS=-semihosting-config enable=on,target=native,chardev=output,arg="2" +run-vtimer: QEMU_OPTS=$(QEMU_EL2_MACHINE) $(QEMU_EL2_BASE_ARGS) -kernel + # Simple Record/Replay Test .PHONY: memory-record run-memory-record: memory-record memory @@ -75,10 +91,45 @@ 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) +# This test explicitly checks the output of the pauth operation so we +# must force the use of the QARMA5 algorithm for it. +run-pauth-3: QEMU_BASE_MACHINE=-M virt -cpu max,pauth-qarma5=on -display none else pauth-3: $(call skip-test, "BUILD of $@", "missing compiler support") run-pauth-3: $(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 2efacf9a5a3..16ddcf4f883 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -10,6 +10,7 @@ VPATH += $(AARCH64_SRC) # Base architecture tests AARCH64_TESTS=fcvt pcalign-a64 lse2-fault +AARCH64_TESTS += test-2248 test-2150 fcvt: LDFLAGS+=-lm @@ -19,6 +20,7 @@ run-fcvt: fcvt 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); \ @@ -26,22 +28,23 @@ config-cc.mak: Makefile $(call cc-option,-march=armv8.5-a, CROSS_CC_HAS_ARMV8_5); \ $(call cc-option,-mbranch-protection=standard, CROSS_CC_HAS_ARMV8_BTI); \ $(call cc-option,-march=armv8.5-a+memtag, CROSS_CC_HAS_ARMV8_MTE); \ - $(call cc-option,-Wa$(COMMA)-march=armv9-a+sme, CROSS_AS_HAS_ARMV9_SME)) 3> config-cc.mak + $(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 += -march=armv8.2-a +dcpop: CFLAGS += $(CROSS_CC_HAS_ARMV8_2) endif ifneq ($(CROSS_CC_HAS_ARMV8_5),) AARCH64_TESTS += dcpodp -dcpodp: CFLAGS += -march=armv8.5-a +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 +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]. @@ -53,7 +56,7 @@ endif # 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. @@ -61,29 +64,27 @@ 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),) -AARCH64_TESTS += sme-outprod1 +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 # System Registers Tests AARCH64_TESTS += sysregs -ifneq ($(CROSS_CC_HAS_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 +# Work around compiler false-positive warning, as we do for the 'sha1' test +sha1-vector: CFLAGS=-O3 -Wno-stringop-overread sha1-vector: sha1.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) run-sha1-vector: sha1-vector run-sha1 @@ -100,37 +101,57 @@ sha512-vector: sha512.c TESTS += sha512-vector 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) -TESTS += sha512-sve -endif +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 -ifeq ($(HOST_GDB_SUPPORTS_ARCH),y) +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 99a879af23d..1fada8108d2 100644 --- a/tests/tcg/aarch64/bti-1.c +++ b/tests/tcg/aarch64/bti-1.c @@ -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 8c534c09d78..6a3bd037bcd 100644 --- a/tests/tcg/aarch64/bti-3.c +++ b/tests/tcg/aarch64/bti-3.c @@ -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/fcvt.ref b/tests/tcg/aarch64/fcvt.ref index e7af24dc58a..2726b41063f 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 00000000000..9ad98e7a54c --- /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 ee8d467e59d..a78a3a2514d 100644 --- a/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py +++ b/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py @@ -8,19 +8,10 @@ # 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,26 +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: - # 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 afd8ece98dd..84cdcd4a32e 100644 --- a/tests/tcg/aarch64/gdbstub/test-sve.py +++ b/tests/tcg/aarch64/gdbstub/test-sve.py @@ -6,20 +6,10 @@ # 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,24 +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: - # 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/mte-1.c b/tests/tcg/aarch64/mte-1.c index 88dcd617add..146cad4a049 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-8.c b/tests/tcg/aarch64/mte-8.c new file mode 100644 index 00000000000..808135ba438 --- /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/semicall.h b/tests/tcg/aarch64/semicall.h index 8a3fce35c5f..30d4de9a549 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 00000000000..652c4ea0902 --- /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 00000000000..15f0972d835 --- /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 00000000000..3bfec34914c --- /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-smopa-1.c b/tests/tcg/aarch64/sme-smopa-1.c new file mode 100644 index 00000000000..c62d5e00073 --- /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 00000000000..c9f48c3bfca --- /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 00000000000..ae271c9d87e --- /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 d8eb06abcf2..301e61d0dd4 100644 --- a/tests/tcg/aarch64/sysregs.c +++ b/tests/tcg/aarch64/sysregs.c @@ -126,7 +126,7 @@ int main(void) */ 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(0000,0000,0000,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(f000,0000,ff00,0000)); get_cpu_reg_check_mask(id_aa64mmfr1_el1, _m(0000,f000,0000,0000)); @@ -137,8 +137,8 @@ int main(void) /* 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_mask(SYS_ID_AA64ZFR0_EL1, _m(0ff0,ff0f,00ff,00ff)); - get_cpu_reg_check_mask(SYS_ID_AA64SMFR0_EL1, _m(80f1,00fd,0000,0000)); + 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 501685d0ece..8bfa4e4efc7 100644 --- a/tests/tcg/aarch64/system/boot.S +++ b/tests/tcg/aarch64/system/boot.S @@ -16,6 +16,7 @@ #define semihosting_call hlt 0xf000 #define SYS_WRITEC 0x03 /* character to debug channel */ #define SYS_WRITE0 0x04 /* string to debug channel */ +#define SYS_GET_CMDLINE 0x15 /* get command line */ #define SYS_EXIT 0x18 .align 12 @@ -70,22 +71,172 @@ lower_a32_sync: lower_a32_irq: lower_a32_fiq: lower_a32_serror: + adr x1, .unexp_excp +exit_msg: mov x0, SYS_WRITE0 - adr x1, .error - semihosting_call - mov x0, SYS_EXIT - mov x1, 1 semihosting_call + mov x0, 1 /* EXIT_FAILURE */ + bl _exit /* never returns */ .section .rodata -.error: - .string "Terminated by exception.\n" +.unexp_excp: + .string "Unexpected exception.\n" +.high_el_msg: + .string "Started in lower EL than requested.\n" +.unexp_el0: + .string "Started in invalid EL.\n" + + .align 8 +.get_cmd: + .quad cmdline + .quad 128 .text .align 4 .global __start __start: + /* + * Initialise the stack for whatever EL we are in before + * anything else, we need it to be able to _exit cleanly. + * It's smaller than the stack we pass to the C code but we + * don't need much. + */ + adrp x0, system_stack_end + add x0, x0, :lo12:system_stack_end + mov sp, x0 + + /* + * The test can set the semihosting command line to the target + * EL needed for the test. However if no semihosting args are set we will + * end up with -kernel/-append data (see semihosting_arg_fallback). + * Keep the normalised target in w11. + */ + mov x0, SYS_GET_CMDLINE + adr x1, .get_cmd + semihosting_call + adrp x10, cmdline + add x10, x10, :lo12:cmdline + ldrb w11, [x10] + + /* sanity check, normalise char to EL, clamp to 1 if outside range */ + subs w11, w11, #'0' + b.lt el_default + cmp w11, #3 + b.gt el_default + b 1f + +el_high: + adr x1, .high_el_msg + b exit_msg + +el_default: + mov w11, #1 + +1: + /* Determine current Exception Level */ + mrs x0, CurrentEL + lsr x0, x0, #2 /* CurrentEL[3:2] contains the current EL */ + + /* Are we already in a lower EL than we want? */ + cmp w11, w0 + bgt el_high + + /* Branch based on current EL */ + cmp x0, #3 + b.eq setup_el3 + cmp x0, #2 + b.eq setup_el2 + cmp x0, #1 + b.eq at_testel /* Already at EL1, skip transition */ + + /* Should not be at EL0 - error out */ + adr x1, .unexp_el0 + b exit_msg + +setup_el3: + /* Ensure we trap if we get anything wrong */ + adr x0, vector_table + msr vbar_el3, x0 + + /* Does the test want to be at EL3? */ + cmp w11, #3 + beq at_testel + + /* Configure EL3 to for lower states (EL2 or EL1) */ + mrs x0, scr_el3 + orr x0, x0, #(1 << 10) /* RW = 1: EL2/EL1 execution state is AArch64 */ + orr x0, x0, #(1 << 0) /* NS = 1: Non-secure state */ + msr scr_el3, x0 + + /* + * We need to check if EL2 is actually enabled via ID_AA64PFR0_EL1, + * otherwise we should just jump straight to EL1. + */ + mrs x0, id_aa64pfr0_el1 + ubfx x0, x0, #8, #4 /* Extract EL2 field (bits 11:8) */ + cbz x0, el2_not_present /* If field is 0 no EL2 */ + + + /* Prepare SPSR for exception return to EL2 */ + mov x0, #0x3c9 /* DAIF bits and EL2h mode (9) */ + msr spsr_el3, x0 + + /* Set EL2 entry point */ + adr x0, setup_el2 + msr elr_el3, x0 + + /* Return to EL2 */ + eret + +el2_not_present: + /* Initialize SCTLR_EL1 with reset value */ + msr sctlr_el1, xzr + + /* Set EL1 entry point */ + adr x0, at_testel + msr elr_el3, x0 + + /* Prepare SPSR for exception return to EL1h with interrupts masked */ + mov x0, #0x3c5 /* DAIF bits and EL1h mode (5) */ + msr spsr_el3, x0 + + isb /* Synchronization barrier */ + eret /* Jump to EL1 */ + +setup_el2: + /* Ensure we trap if we get anything wrong */ + adr x0, vector_table + msr vbar_el2, x0 + + /* Does the test want to be at EL2? */ + cmp w11, #2 + beq at_testel + + /* Configure EL2 to allow transition to EL1 */ + mrs x0, hcr_el2 + orr x0, x0, #(1 << 31) /* RW = 1: EL1 execution state is AArch64 */ + msr hcr_el2, x0 + + /* Initialize SCTLR_EL1 with reset value */ + msr sctlr_el1, xzr + + /* Set EL1 entry point */ + adr x0, at_testel + msr elr_el2, x0 + + /* Prepare SPSR for exception return to EL1 */ + mov x0, #(0x5 << 0) /* EL1h (SPx), with interrupts disabled */ + msr spsr_el2, x0 + + /* Return to EL1 */ + eret + + /* + * At the target EL for the test, usually EL1. Note we still + * set everything up as if we were at EL1. + */ +at_testel: /* Installs a table of exception vectors to catch and handle all exceptions by terminating the process with a diagnostic. */ adr x0, vector_table @@ -101,7 +252,7 @@ __start: * maps RAM to the first Gb. The stage2 tables have two 2mb * translation block entries covering a series of adjacent * 4k pages. - */ + */ /* Stage 1 entry: indexed by IA[38:30] */ adr x1, . /* phys address */ @@ -135,6 +286,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. */ /* @@ -188,7 +350,8 @@ __start: orr x0, x0, #(3 << 16) msr cpacr_el1, x0 - /* Setup some stack space and enter the test code. + /* + * Setup some stack space before we enter the test code. * Assume everything except the return value is garbage when we * return, we won't need it. */ @@ -223,6 +386,11 @@ __sys_outc: ret .data + + .align 8 +cmdline: + .space 128, 0 + .align 12 /* Translation table @@ -236,6 +404,10 @@ ttb_stage2: .space 4096, 0 .align 12 +system_stack: + .space 4096, 0 +system_stack_end: + stack: .space 65536, 0 stack_end: diff --git a/tests/tcg/aarch64/system/feat-xs.c b/tests/tcg/aarch64/system/feat-xs.c new file mode 100644 index 00000000000..f310fc837e0 --- /dev/null +++ b/tests/tcg/aarch64/system/feat-xs.c @@ -0,0 +1,27 @@ +/* + * FEAT_XS Test + * + * Copyright (c) 2024 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +int main(void) +{ + uint64_t isar1; + + asm volatile ("mrs %0, id_aa64isar1_el1" : "=r"(isar1)); + if (((isar1 >> 56) & 0xf) < 1) { + ml_printf("FEAT_XS not supported by CPU"); + return 1; + } + /* VMALLE1NXS */ + asm volatile (".inst 0xd508971f"); + /* VMALLE1OSNXS */ + asm volatile (".inst 0xd508911f"); + + return 0; +} diff --git a/tests/tcg/aarch64/system/kernel.ld b/tests/tcg/aarch64/system/kernel.ld index 7b3a76dcbf3..aef043e31db 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 00000000000..b611240a95c --- /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/vtimer.c b/tests/tcg/aarch64/system/vtimer.c new file mode 100644 index 00000000000..7d725eced34 --- /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 00000000000..fb86c119586 --- /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 00000000000..aac2e178368 --- /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 00000000000..84c7e7de716 --- /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_be/Makefile.target b/tests/tcg/aarch64_be/Makefile.target new file mode 100644 index 00000000000..cbe5fa0b2db --- /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 00000000000..a9b2ab45de9 --- /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 09193a62d68..a944102a3ce 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 b94500a7d9c..36d8ed1eaea 100644 --- a/tests/tcg/alpha/Makefile.target +++ b/tests/tcg/alpha/Makefile.target @@ -12,7 +12,7 @@ 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/arm/Makefile.softmmu-target b/tests/tcg/arm/Makefile.softmmu-target index 8b546e2aa3c..b66074b0b43 100644 --- a/tests/tcg/arm/Makefile.softmmu-target +++ b/tests/tcg/arm/Makefile.softmmu-target @@ -3,26 +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 +# Specific Test Rules -TESTS += $(ARM_TESTS) +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 -CFLAGS+=-Wl,--build-id=none -x assembler-with-cpp -LDFLAGS+=-nostdlib -N -static +run-test-armv6m-undef: QEMU_OPTS=-semihosting-config enable=on,target=native,chardev=output -M microbit -kernel -%: %.S %.ld - $(CC) $(CFLAGS) $(ASFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) -T $(ARM_SRC)/$@.ld +ARM_TESTS+=test-armv6m-undef -# Specific Test Rules +# 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) -test-armv6m-undef: EXTRA_CFLAGS+=-mcpu=cortex-m0 -mfloat-abi=soft +.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) -run-test-armv6m-undef: QEMU_OPTS+=-semihosting -M microbit -kernel +EXTRA_RUNS+=run-memory-replay -# We don't currently support the multiarch system tests -undefine MULTIARCH_TESTS +TESTS += $(ARM_TESTS) $(MULTIARCH_TESTS) +EXTRA_RUNS+=$(MULTIARCH_RUNS) diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index 0038cef02c6..6189d7a0e24 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -8,23 +8,22 @@ 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 -test-arm-iwmmxt: test-arm-iwmmxt.S - $(CC) $(CFLAGS) $< -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) @@ -62,7 +61,8 @@ endif ARM_TESTS += commpage # Vector SHA1 -sha1-vector: CFLAGS=-O3 +# Work around compiler false-positive warning, as we do for the 'sha1' test +sha1-vector: CFLAGS=-O3 -Wno-stringop-overread sha1-vector: sha1.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) run-sha1-vector: sha1-vector run-sha1 @@ -79,6 +79,3 @@ sha512-vector: sha512.c ARM_TESTS += sha512-vector TESTS += $(ARM_TESTS) - -# On ARM Linux only supports 4k pages -EXTRA_RUNS+=run-test-mmap-4096 diff --git a/tests/tcg/arm/README b/tests/tcg/arm/README index e6307116e23..aceccc127f7 100644 --- a/tests/tcg/arm/README +++ b/tests/tcg/arm/README @@ -4,8 +4,3 @@ hello-arm --------- A very simple inline assembly, write syscall based hello world - -test-arm-iwmmxt ---------------- - -A simple test case for older iwmmxt extended ARMs diff --git a/tests/tcg/arm/fcvt.c b/tests/tcg/arm/fcvt.c index 7ac47b564e2..ecebbb02470 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 f052b6d7e58..8e007c33454 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 ad8ac51310b..624937c5577 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 00000000000..7d43372c66e --- /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 00000000000..7b3a76dcbf3 --- /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 00000000000..206dd60eed7 --- /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/arm/test-arm-iwmmxt.S b/tests/tcg/arm/test-arm-iwmmxt.S deleted file mode 100644 index d647f9404ae..00000000000 --- a/tests/tcg/arm/test-arm-iwmmxt.S +++ /dev/null @@ -1,49 +0,0 @@ -@ Checks whether iwMMXt is functional. -.code 32 -.globl main - -main: -ldr r0, =data0 -ldr r1, =data1 -ldr r2, =data2 -#ifndef FPA -wldrd wr0, [r0, #0] -wldrd wr1, [r0, #8] -wldrd wr2, [r1, #0] -wldrd wr3, [r1, #8] -wsubb wr2, wr2, wr0 -wsubb wr3, wr3, wr1 -wldrd wr0, [r2, #0] -wldrd wr1, [r2, #8] -waddb wr0, wr0, wr2 -waddb wr1, wr1, wr3 -wstrd wr0, [r2, #0] -wstrd wr1, [r2, #8] -#else -ldfe f0, [r0, #0] -ldfe f1, [r0, #8] -ldfe f2, [r1, #0] -ldfe f3, [r1, #8] -adfdp f2, f2, f0 -adfdp f3, f3, f1 -ldfe f0, [r2, #0] -ldfe f1, [r2, #8] -adfd f0, f0, f2 -adfd f1, f1, f3 -stfe f0, [r2, #0] -stfe f1, [r2, #8] -#endif -mov r0, #1 -mov r1, r2 -mov r2, #0x11 -swi #0x900004 -mov r0, #0 -swi #0x900001 - -.data -data0: -.string "aaaabbbbccccdddd" -data1: -.string "bbbbccccddddeeee" -data2: -.string "hvLLWs\x1fsdrs9\x1fNJ-\n" diff --git a/tests/tcg/cris/.gdbinit b/tests/tcg/cris/.gdbinit deleted file mode 100644 index 5e8c1d32f32..00000000000 --- 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 43587d2769c..00000000000 --- a/tests/tcg/cris/Makefile.target +++ /dev/null @@ -1,62 +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) $<) - -# We don't currently support the multiarch tests -undefine MULTIARCH_TESTS diff --git a/tests/tcg/cris/README b/tests/tcg/cris/README deleted file mode 100644 index 2e65a76f10b..00000000000 --- 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 52ef7a97169..00000000000 --- 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 a00dec02af8..00000000000 --- 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 20ba25d2192..00000000000 --- 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 efece9f538d..00000000000 --- 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 e6f874f9b2b..00000000000 --- 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 7f55cdc1b5e..00000000000 --- 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 09c8355bf8f..00000000000 --- 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 7563494b991..00000000000 --- 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 7f55cdc1b5e..00000000000 --- 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 a947b773c97..00000000000 --- 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 93858863fe5..00000000000 --- 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 55aa7b06077..00000000000 --- 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 61aa1dc32f2..00000000000 --- 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 0a02ae6f7eb..00000000000 --- 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 873a4086c5f..00000000000 --- 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 11929d42020..00000000000 --- 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 c57ffa6fa35..00000000000 --- 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 fb9e5bc905b..00000000000 --- 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 5c50cc5f6ad..00000000000 --- 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 485deb20066..00000000000 --- 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 4e91360273c..00000000000 --- 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 45a7005e24d..00000000000 --- 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 414d3705179..00000000000 --- 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 267c9ba8c01..00000000000 --- 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 e4dde15b32e..00000000000 --- 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 5469141c916..00000000000 --- 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 b30af7a5385..00000000000 --- 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 b237a931752..00000000000 --- 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 87ea5bf8e31..00000000000 --- 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 bd43b838ea9..00000000000 --- 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 10602378731..00000000000 --- 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 9a6150b749e..00000000000 --- 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 9e2ddd7cd00..00000000000 --- 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 18fdbef9b28..00000000000 --- 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 e65ccddfd46..00000000000 --- 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 300cc877422..00000000000 --- 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 da8ec262842..00000000000 --- 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 bbfa633373d..00000000000 --- 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 88489dee319..00000000000 --- 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 53950abd5bd..00000000000 --- 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 b4db595d64b..00000000000 --- 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 eabc9588d42..00000000000 --- 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 7fc11f064dd..00000000000 --- 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 daf0970e4a6..00000000000 --- 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 eef9bdb4fba..00000000000 --- 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 d0d90e12465..00000000000 --- 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 53c8ce6b50d..00000000000 --- 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 7074336e788..00000000000 --- 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 d1889a7a1b7..00000000000 --- 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 7c8487d1a24..00000000000 --- 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 038e539463b..00000000000 --- 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 3ecf475f757..00000000000 --- 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 f379358765b..00000000000 --- 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 a7a1f82a829..00000000000 --- 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 963c4b6f5ea..00000000000 --- 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 33bcf155e56..00000000000 --- 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 c733f036a2b..00000000000 --- 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 ee723a6aa02..00000000000 --- 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 5060edc72d9..00000000000 --- 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 a514c11bc90..00000000000 --- 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 b44fb25933a..00000000000 --- 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 4a8674cc1a2..00000000000 --- 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 e34b5448e25..00000000000 --- 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 e07ea02dd4c..00000000000 --- 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 9e34fa31ab3..00000000000 --- 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 742fbc8915e..00000000000 --- 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 80038b2ab92..00000000000 --- 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 af027d74755..00000000000 --- 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 1644eecc334..00000000000 --- 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 aa1641b2e6b..00000000000 --- 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 08b67b6ef0c..00000000000 --- 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 fc3fb1faa80..00000000000 --- 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 b355ba164fb..00000000000 --- 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 4235e5fc65c..00000000000 --- 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 ed509e27e0e..00000000000 --- 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 d956ab9adec..00000000000 --- 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 aaa5c971159..00000000000 --- 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 45ecd159b3c..00000000000 --- 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 a0c525497d7..00000000000 --- 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 7aada899115..00000000000 --- 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 fb403ba9961..00000000000 --- 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 69caec1bb24..00000000000 --- 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 bf051a6b550..00000000000 --- 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 1aff7622bc2..00000000000 --- 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 b803f0c431e..00000000000 --- 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 35139a0ed9a..00000000000 --- 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 cb890ef1200..00000000000 --- 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 80f2dff6ab2..00000000000 --- 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 251d26eec2e..00000000000 --- 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 5d56189f8ea..00000000000 --- 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 557adee92de..00000000000 --- 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 1f86ea283d4..00000000000 --- 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 3abc3a9ea83..00000000000 --- 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 39fa8d9bacd..00000000000 --- 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 2e2cae51df0..00000000000 --- 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 e36172ed257..00000000000 --- 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 36a9d5d2744..00000000000 --- 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 04f21fe7c4f..00000000000 --- 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 9a68c1e5d76..00000000000 --- 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 20b69b4f60b..00000000000 --- 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 bbbe6c55405..00000000000 --- 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 3dd47bb6730..00000000000 --- 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 87ed2c90b93..e5182c01d8a 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -19,7 +19,7 @@ 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) @@ -51,6 +51,7 @@ 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) @@ -107,6 +108,7 @@ 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 hex_test.h diff --git a/tests/tcg/hexagon/hvx_misc.c b/tests/tcg/hexagon/hvx_misc.c index b45170acd17..90c3733da07 100644 --- a/tests/tcg/hexagon/hvx_misc.c +++ b/tests/tcg/hexagon/hvx_misc.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2021-2023 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 @@ -231,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" @@ -242,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() @@ -460,6 +474,27 @@ static void test_vcombine(void) check_output_w(__LINE__, BUFSIZE); } +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() { init_buffers(); @@ -501,6 +536,8 @@ int main() test_vcombine(); + test_store_new(); + puts(err ? "FAIL" : "PASS"); return err ? 1 : 0; } diff --git a/tests/tcg/hexagon/unaligned_pc.c b/tests/tcg/hexagon/unaligned_pc.c new file mode 100644 index 00000000000..e9dc7cb8b52 --- /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 92bc86a2134..f0b23d312b3 100644 --- a/tests/tcg/hexagon/usr.c +++ b/tests/tcg/hexagon/usr.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2022-2023 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 @@ -1007,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); @@ -1020,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/hppa/Makefile.target b/tests/tcg/hppa/Makefile.target index cdd0d572a78..ea5ae2186df 100644 --- a/tests/tcg/hppa/Makefile.target +++ b/tests/tcg/hppa/Makefile.target @@ -2,9 +2,6 @@ # # 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. diff --git a/tests/tcg/i386/Makefile.softmmu-target b/tests/tcg/i386/Makefile.softmmu-target index 5266f2335aa..4096a1cf31f 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) diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index fdf757c6ce4..f1df40411b0 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -13,7 +13,7 @@ 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 @@ -22,7 +22,7 @@ run-test-i386-sse-exceptions: QEMU_OPTS += -cpu max test-i386-pcmpistri: CFLAGS += -msse4.2 run-test-i386-pcmpistri: QEMU_OPTS += -cpu max -test-i386-bmi2: CFLAGS=-O2 +test-i386-bmi2: CFLAGS=-O2 -fwrapv run-test-i386-bmi2: QEMU_OPTS += -cpu max test-i386-adcox: CFLAGS=-O2 @@ -35,7 +35,7 @@ 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 @@ -71,9 +71,6 @@ endif 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) diff --git a/tests/tcg/i386/system/boot.S b/tests/tcg/i386/system/boot.S index 794c2cb0ad0..28902c400d8 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-avx.c b/tests/tcg/i386/test-avx.c index c39c0e5bce8..80fe363cfc2 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, "gather_mem not expected size"); 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 641a2ef69eb..6063fb2d11d 100755 --- a/tests/tcg/i386/test-avx.py +++ b/tests/tcg/i386/test-avx.py @@ -9,7 +9,7 @@ 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,6 +43,7 @@ 'vPS[LR][AL][WDQ]': 0x3f, 'vPS[RL]LDQ': 0x1f, 'vROUND[PS][SD]': 0x7, + 'SHA1RNDS4': 0x03, 'vSHUFPD': 0x0f, 'vSHUFPS': 0xff, 'vAESKEYGENASSIST': 0xff, diff --git a/tests/tcg/i386/test-flags.c b/tests/tcg/i386/test-flags.c new file mode 100644 index 00000000000..c379e296275 --- /dev/null +++ b/tests/tcg/i386/test-flags.c @@ -0,0 +1,37 @@ +#define _GNU_SOURCE +#include +#include +#include +#include + +volatile unsigned long flags; +volatile unsigned long flags_after; +int *addr; + +void sigsegv(int sig, siginfo_t *info, ucontext_t *uc) +{ + flags = uc->uc_mcontext.gregs[REG_EFL]; + mprotect(addr, 4096, PROT_READ|PROT_WRITE); +} + +int main() +{ + struct sigaction sa = { .sa_handler = (void *)sigsegv, .sa_flags = SA_SIGINFO }; + sigaction(SIGSEGV, &sa, NULL); + + /* fault in the page then protect it */ + addr = mmap (NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); + *addr = 0x1234; + mprotect(addr, 4096, PROT_READ); + + asm("# set flags to all ones \n" + "mov $-1, %%eax \n" + "movq addr, %%rdi \n" + "sahf \n" + "sub %%eax, (%%rdi) \n" + "pushf \n" + "pop flags_after(%%rip) \n" : : : "eax", "edi", "memory"); + + /* OF can have any value before the SUB instruction. */ + assert((flags & 0xff) == 0xd7 && (flags_after & 0x8ff) == 0x17); +} diff --git a/tests/tcg/i386/test-i386-adcox.c b/tests/tcg/i386/test-i386-adcox.c index 16169efff82..a717064adb0 100644 --- a/tests/tcg/i386/test-i386-adcox.c +++ b/tests/tcg/i386/test-i386-adcox.c @@ -29,7 +29,7 @@ void test_adox_adcx(uint32_t in_c, uint32_t in_o, REG adcx_operand, REG adox_ope "adcx %3, %1;" "pushf; pop %0" : "+r" (flags), "+r" (out_adcx), "+r" (out_adox) - : "r" ((REG)-1), "0" (flags), "1" (out_adcx), "2" (out_adox)); + : "r" ((REG) - 1), "0" (flags), "1" (out_adcx), "2" (out_adox)); assert(out_adcx == in_c + adcx_operand - 1); assert(out_adox == in_o + adox_operand - 1); @@ -53,8 +53,8 @@ void test_adcx_adox(uint32_t in_c, uint32_t in_o, REG adcx_operand, REG adox_ope "adcx %3, %1;" "adox %3, %2;" "pushf; pop %0" - : "+r" (flags), "+r" (out_adcx), "+r" (out_adox) - : "r" ((REG)-1), "0" (flags), "1" (out_adcx), "2" (out_adox)); + : "+r"(flags), "+r"(out_adcx), "+r"(out_adox) + : "r" ((REG)-1)); assert(out_adcx == in_c + adcx_operand - 1); assert(out_adox == in_o + adox_operand - 1); diff --git a/tests/tcg/i386/test-i386.c b/tests/tcg/i386/test-i386.c index 864c4e620d5..ce3bf74b5a8 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 c43bf42dd3d..5c0f628e350 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 908f3a8c0fe..6d4a20fde73 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 67eb1c04cea..37a81bafe74 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/kernel.ld b/tests/tcg/loongarch64/system/kernel.ld index f1a7c0168c5..56d8588f1a3 100644 --- a/tests/tcg/loongarch64/system/kernel.ld +++ b/tests/tcg/loongarch64/system/kernel.ld @@ -3,7 +3,7 @@ ENTRY(_start) SECTIONS { /* Linux kernel legacy start address. */ - . = 0x9000000000200000; + . = 0x200000; _text = .; .text : { *(.text) diff --git a/tests/tcg/loongarch64/system/regdef.h b/tests/tcg/loongarch64/system/regdef.h index faa09b2377c..b586b4e86d3 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 1163c7ef034..33f7b1b1275 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 00000000000..20bd8c73329 --- /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 c821d2806a9..af0bf54be98 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 10472b4f585..fb0189c2bb2 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 1a994d5525e..5d17c1706e4 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 4e1cf501afe..38e22d00e36 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 43bddeaf212..45c9cfe18c5 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -42,6 +42,17 @@ munmap-pthread: LDFLAGS+=-pthread vma-pthread: CFLAGS+=-pthread vma-pthread: LDFLAGS+=-pthread +sigreturn-sigmask: CFLAGS+=-pthread +sigreturn-sigmask: LDFLAGS+=-pthread + +# GCC versions 12/13/14/15 at least incorrectly complain about +# "'SHA1Transform' reading 64 bytes from a region of size 0"; see the gcc bug +# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106709 +# Since this is just a standard piece of library code we've borrowed for a +# TCG test case, suppress the warning rather than trying to modify the +# code to work around the compiler. +sha1: CFLAGS+=-Wno-stringop-overread -Wno-unknown-warning-option + # 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),) @@ -51,60 +62,99 @@ run-plugin-vma-pthread-with-%: vma-pthread $(call skip-test, $<, "flaky on CI?") endif -# We define the runner for test-mmap after the individual -# architectures have defined their supported pages sizes. If no -# additional page sizes are defined we only run the default test. - -# 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),) -ifeq ($(HOST_GDB_SUPPORTS_ARCH),y) +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 $(HAVE_GDB_BIN) \ + --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) + +run-gdbstub-late-attach: late-attach + $(call run-test, $@, env LATE_ATTACH_PY=1 $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" --no-suspend \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/late-attach.py, \ + attaching to a running process) + else run-gdbstub-%: - $(call skip-test, "gdbstub test $*", "no guest arch support") -endif -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-proc-mappings 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 run-gdbstub-late-attach # ARM Compatible Semi Hosting Tests # @@ -138,5 +188,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 1d82efc589d..1e2268f4b75 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 8627eee3cf7..f609c01341a 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 00000000000..d1ff1936a7a --- /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 00000000000..80607f04b5d --- /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 0a1f0f93dc5..58d7f8b4c58 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 2e4fa55324d..cb1fdd439e3 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 309f3f4bf10..c42ebe64b9e 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 4888f8641f4..a692e052d5b 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 00000000000..cb6b032b388 --- /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 00000000000..ccce35902fb --- /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 00000000000..72a6e440c08 --- /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 00000000000..5c2fe722088 --- /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 index e222ac94c5a..2d5654d1540 100644 --- a/tests/tcg/multiarch/gdbstub/interrupt.py +++ b/tests/tcg/multiarch/gdbstub/interrupt.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. # @@ -8,19 +8,7 @@ # 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 gdb_exit, main, report def check_interrupt(thread): @@ -59,6 +47,9 @@ 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(): @@ -66,32 +57,4 @@ def run_test(): "thread %d resumes correctly on interrupt" % thread.num) -# -# 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) -if len(gdb.selected_inferior().threads()) == 1: - print("SKIP: set to run on a single thread") - exit(0) - -try: - # 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/late-attach.py b/tests/tcg/multiarch/gdbstub/late-attach.py new file mode 100644 index 00000000000..1d40efb5ec8 --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/late-attach.py @@ -0,0 +1,28 @@ +"""Test attaching GDB to a running process. + +SPDX-License-Identifier: GPL-2.0-or-later +""" +from test_gdbstub import main, report + + +def run_test(): + """Run through the tests one by one""" + try: + phase = gdb.parse_and_eval("phase").string() + except gdb.error: + # Assume the guest did not reach main(). + phase = "start" + + if phase == "start": + gdb.execute("break sigwait") + gdb.execute("continue") + phase = gdb.parse_and_eval("phase").string() + report(phase == "sigwait", "{} == \"sigwait\"".format(phase)) + + gdb.execute("signal SIGUSR1") + + exitcode = int(gdb.parse_and_eval("$_exitcode")) + report(exitcode == 0, "{} == 0".format(exitcode)) + + +main(run_test) diff --git a/tests/tcg/multiarch/gdbstub/memory.py b/tests/tcg/multiarch/gdbstub/memory.py index dd25e722813..532b92e7fb3 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 @@ 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,29 +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: - # 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 00000000000..51082a30e40 --- /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 00000000000..b3d13cb077f --- /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 416728415f9..1ce711a402c 100644 --- a/tests/tcg/multiarch/gdbstub/sha1.py +++ b/tests/tcg/multiarch/gdbstub/sha1.py @@ -7,19 +7,11 @@ # 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,28 +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: - # 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 index 04ec61d2197..6eb6ebf7b17 100644 --- a/tests/tcg/multiarch/gdbstub/test-proc-mappings.py +++ b/tests/tcg/multiarch/gdbstub/test-proc-mappings.py @@ -3,60 +3,20 @@ 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 gdb_exit, main, report def run_test(): """Run through the tests one by one""" - try: - mappings = gdb.execute("info proc mappings", False, True) - except gdb.error as exc: - exc_str = str(exc) - if "Not supported on this target." in exc_str: - # Detect failures due to an outstanding issue with how GDB handles - # the x86_64 QEMU's target.xml, which does not contain the - # definition of orig_rax. Skip the test in this case. - print("SKIP: {}".format(exc_str)) - return - raise + 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") -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: - # 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/multiarch/gdbstub/test-qxfer-auxv-read.py b/tests/tcg/multiarch/gdbstub/test-qxfer-auxv-read.py index 926fa962b77..00c26ab4a95 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 @@ # 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,28 +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: - # 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 00000000000..862596b07a7 --- /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 e57d2a8db8b..4d6b6b9fbe7 100644 --- a/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py +++ b/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py @@ -6,18 +6,8 @@ # 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,28 +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: - # 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/late-attach.c b/tests/tcg/multiarch/late-attach.c new file mode 100644 index 00000000000..20a364034b5 --- /dev/null +++ b/tests/tcg/multiarch/late-attach.c @@ -0,0 +1,41 @@ +/* + * Test attaching GDB to a running process. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include + +static const char *phase = "start"; + +int main(void) +{ + sigset_t set; + int sig; + + assert(sigfillset(&set) == 0); + assert(sigprocmask(SIG_BLOCK, &set, NULL) == 0); + + /* Let GDB know it can send SIGUSR1. */ + phase = "sigwait"; + if (getenv("LATE_ATTACH_PY")) { + assert(sigwait(&set, &sig) == 0); + if (sig != SIGUSR1) { + fprintf(stderr, "Unexpected signal %d\n", sig); + return EXIT_FAILURE; + } + } + + /* Check that the guest does not see host_interrupt_signal. */ + assert(sigpending(&set) == 0); + for (sig = 1; sig < NSIG; sig++) { + if (sigismember(&set, sig)) { + fprintf(stderr, "Unexpected signal %d\n", sig); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/multiarch/libs/float_helpers.c b/tests/tcg/multiarch/libs/float_helpers.c index 4e68d2b6598..fad5fc98933 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 29d0997e687..539fb3b7726 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 00000000000..0ccf7a973a8 --- /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 00000000000..94eaaec371a --- /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 00000000000..a7059aacd9c --- /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/test-vma.c b/tests/tcg/multiarch/linux/test-vma.c similarity index 100% rename from tests/tcg/multiarch/test-vma.c rename to tests/tcg/multiarch/linux/test-vma.c diff --git a/tests/tcg/multiarch/prot-none.c b/tests/tcg/multiarch/prot-none.c new file mode 100644 index 00000000000..dc56aadb3c5 --- /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 00000000000..e6c8ff31ca3 --- /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/sigreturn-sigmask.c b/tests/tcg/multiarch/sigreturn-sigmask.c new file mode 100644 index 00000000000..e6cc904898d --- /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 90810a32b2f..07be001102b 100644 --- a/tests/tcg/multiarch/system/Makefile.softmmu-target +++ b/tests/tcg/multiarch/system/Makefile.softmmu-target @@ -14,13 +14,12 @@ 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),) -ifeq ($(HOST_GDB_SUPPORTS_ARCH),y) +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 \ @@ -29,7 +28,7 @@ run-gdbstub-memory: memory softmmu gdbstub support) run-gdbstub-interrupt: interrupt $(call run-test, $@, $(GDB_SCRIPT) \ - --gdb $(HAVE_GDB_BIN) \ + --gdb $(GDB) \ --qemu $(QEMU) \ --output $<.gdb.out \ --qargs \ @@ -38,7 +37,7 @@ run-gdbstub-interrupt: interrupt softmmu gdbstub support) run-gdbstub-untimely-packet: hello $(call run-test, $@, $(GDB_SCRIPT) \ - --gdb $(HAVE_GDB_BIN) \ + --gdb $(GDB) \ --gdb-args "-ex 'set debug remote 1'" \ --output untimely-packet.gdb.out \ --stderr untimely-packet.gdb.err \ @@ -49,13 +48,26 @@ run-gdbstub-untimely-packet: hello $(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 $*", "no guest arch support") -endif -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 run-gdbstub-interrupt run-gdbstub-untimely-packet +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/memory.c b/tests/tcg/multiarch/system/memory.c index e29786ae559..7508f6b916d 100644 --- a/tests/tcg/multiarch/system/memory.c +++ b/tests/tcg/multiarch/system/memory.c @@ -1,14 +1,14 @@ /* * 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 */ @@ -20,20 +20,28 @@ # 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("."); } @@ -63,12 +71,14 @@ 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++ = BYTE_NEXT(count); - pdot(i); + pdot(i, true); } - ml_printf("done\n"); + + ml_printf("done %d @ %p\n", i, ptr); } /* @@ -91,10 +101,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); } /* @@ -105,9 +116,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) @@ -117,17 +138,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++) { 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) @@ -137,21 +158,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++) { 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; @@ -159,10 +181,10 @@ 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++) { uint64_t b8 = BYTE_NEXT(count), b7 = BYTE_NEXT(count); uint64_t b6 = BYTE_NEXT(count), b5 = BYTE_NEXT(count); @@ -172,10 +194,11 @@ static void init_test_data_u64(int offset) BYTE_SHIFT(b4, 4) | BYTE_SHIFT(b5, 3) | BYTE_SHIFT(b6, 2) | 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) { @@ -194,11 +217,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; } @@ -236,13 +259,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]; @@ -290,17 +314,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) { @@ -357,15 +386,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; } @@ -390,15 +421,15 @@ static bool read_test_data_s16(int offset, bool neg_first) int32_t data = *ptr++; if (neg_first && data < 0) { - pdot(i); + pdot(i, false); } else if (!neg_first && data > 0) { - pdot(i); + 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; } @@ -423,15 +454,15 @@ static bool read_test_data_s32(int offset, bool neg_first) int64_t data = *ptr++; if (neg_first && data < 0) { - pdot(i); + pdot(i, false); } else if (!neg_first && data > 0) { - pdot(i); + 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; } @@ -465,16 +496,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%lx\n", (unsigned long)&test_data[0]); + ml_printf("Test data end: 0x%lx\n", (unsigned long)&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]); @@ -490,6 +528,8 @@ int main(void) ok = do_signed_reads(true); } + ml_printf("Test data read: %lu\n", (unsigned long)test_read_count); + ml_printf("Test data write: %lu\n", (unsigned long)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 00000000000..5b8bbf3ef37 --- /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-plugin-mem-access.c b/tests/tcg/multiarch/test-plugin-mem-access.c new file mode 100644 index 00000000000..057b9aac9f6 --- /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/nios2/10m50-ghrd.ld b/tests/tcg/nios2/10m50-ghrd.ld deleted file mode 100644 index 71cdda450c4..00000000000 --- 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 bc7fd550607..00000000000 --- 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 f6771cbc81a..00000000000 --- 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 c1730692ba2..00000000000 --- 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 6ad4978099d..00000000000 --- 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 79ef69db125..00000000000 --- 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 00000000000..36776dee1e1 --- /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 00000000000..73dde995781 --- /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 00000000000..0c723cb9ed8 --- /dev/null +++ b/tests/tcg/plugins/insn.c @@ -0,0 +1,303 @@ +/* + * 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 { + qemu_plugin_register_vcpu_insn_exec_cb( + insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, NULL); + } + + 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 00000000000..ca4e8883dd2 --- /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, gpointer d) +{ + 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_with_data(counts, addr_order, NULL); + + 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, ®ion); + + 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, &ri->region_address, 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(g_int64_hash, g_int64_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 00000000000..029342282a4 --- /dev/null +++ b/tests/tcg/plugins/meson.build @@ -0,0 +1,23 @@ +t = [] +if get_option('plugins') + foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'reset', '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: win32_qemu_plugin_api_link_flags, + 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: [python, '-c', '']) +endif + +plugin_modules += t diff --git a/tests/tcg/plugins/reset.c b/tests/tcg/plugins/reset.c new file mode 100644 index 00000000000..1be8be2a4b2 --- /dev/null +++ b/tests/tcg/plugins/reset.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025 Linaro Ltd + * + * Test the reset/uninstall cycle of a plugin. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include + +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; +static qemu_plugin_id_t plugin_id; +static bool was_reset; +static bool was_uninstalled; + +static void after_uninstall(qemu_plugin_id_t id) +{ + g_assert(was_reset && !was_uninstalled); + qemu_plugin_outs("uninstall done\n"); + was_uninstalled = true; +} + +static void tb_exec_after_reset(unsigned int vcpu_index, void *userdata) +{ + g_assert(was_reset && !was_uninstalled); + qemu_plugin_uninstall(plugin_id, after_uninstall); +} + +static void tb_trans_after_reset(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + g_assert(was_reset && !was_uninstalled); + qemu_plugin_register_vcpu_tb_exec_cb(tb, tb_exec_after_reset, + QEMU_PLUGIN_CB_NO_REGS, NULL); +} + +static void after_reset(qemu_plugin_id_t id) +{ + g_assert(!was_reset && !was_uninstalled); + qemu_plugin_outs("reset done\n"); + was_reset = true; + qemu_plugin_register_vcpu_tb_trans_cb(id, tb_trans_after_reset); +} + +static void tb_exec_before_reset(unsigned int vcpu_index, void *userdata) +{ + g_assert(!was_reset && !was_uninstalled); + qemu_plugin_reset(plugin_id, after_reset); +} + +static void tb_trans_before_reset(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + g_assert(!was_reset && !was_uninstalled); + qemu_plugin_register_vcpu_tb_exec_cb(tb, tb_exec_before_reset, + QEMU_PLUGIN_CB_NO_REGS, NULL); +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, + int argc, char **argv) +{ + plugin_id = id; + qemu_plugin_register_vcpu_tb_trans_cb(id, tb_trans_before_reset); + return 0; +} + +/* Since we uninstall the plugin, we can't use qemu_plugin_register_atexit_cb, + * so we use destructor attribute instead. */ +static void __attribute__((destructor)) on_plugin_exit(void) +{ + g_assert(was_reset && was_uninstalled); + qemu_plugin_outs("plugin exit\n"); +} diff --git a/tests/tcg/plugins/syscall.c b/tests/tcg/plugins/syscall.c new file mode 100644 index 00000000000..42801f5c863 --- /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, &num); + + if (!entry) { + entry = g_new0(SyscallStats, 1); + entry->num = num; + g_hash_table_insert(statistics, &entry->num, 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, gpointer d) +{ + 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_with_data(entries, comp_func, NULL); + 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(g_int64_hash, g_int64_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 f5e08c7376c..00000000000 --- 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 5721c159f24..0d058b26006 100644 --- a/tests/tcg/ppc64/Makefile.target +++ b/tests/tcg/ppc64/Makefile.target @@ -6,15 +6,32 @@ 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 @@ -38,4 +55,9 @@ 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/vsx_f2i_nan.c b/tests/tcg/ppc64/vsx_f2i_nan.c new file mode 100644 index 00000000000..94b1a4eb02d --- /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 d5b126e5f10..7c1d44d3f43 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 $@ diff --git a/tests/tcg/riscv64/Makefile.target b/tests/tcg/riscv64/Makefile.target index a7e390c384d..4da5b9a3b32 100644 --- a/tests/tcg/riscv64/Makefile.target +++ b/tests/tcg/riscv64/Makefile.target @@ -17,4 +17,4 @@ run-test-aes: QEMU_OPTS += -cpu rv64,zk=on TESTS += test-fcvtmod test-fcvtmod: CFLAGS += -march=rv64imafdc test-fcvtmod: LDFLAGS += -static -run-test-fcvtmod: QEMU_OPTS += -cpu rv64,d=true,Zfa=true +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 f8c88f32dc5..11d0650cb06 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/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target index 1a1f088b280..8cd4667c63b 100644 --- a/tests/tcg/s390x/Makefile.softmmu-target +++ b/tests/tcg/s390x/Makefile.softmmu-target @@ -1,12 +1,13 @@ S390X_SRC=$(SRC_PATH)/tests/tcg/s390x VPATH+=$(S390X_SRC) -QEMU_OPTS=-action panic=exit-failure -nographic -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 +CFLAGS+=-ggdb -O0 -I$(SRC_PATH)/include/hw/s390x/ipl/ LDFLAGS=-nostdlib -static %.o: %.S - $(CC) -march=z13 -m64 -c $< -o $@ + $(CC) -march=z13 -m64 -Wa,--noexecstack -c $< -o $@ %.o: %.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -march=z13 -m64 -c $< -o $@ @@ -25,6 +26,7 @@ ASM_TESTS = \ lpswe-early \ lra \ mc \ + per \ precise-smc-softmmu \ ssm-early \ stosm-early \ @@ -40,8 +42,15 @@ $(ASM_TESTS): LDFLAGS += -Wl,-T$(LINK_SCRIPT) -Wl,--build-id=none $(ASM_TESTS): $(LINK_SCRIPT) TESTS += $(ASM_TESTS) +MULTIARCH_TESTS += mvc-smc 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) +$(MULTIARCH_TESTS): CFLAGS += $(MINILIB_INC) \ + -I$(SRC_PATH)/roms/SLOF/lib/libc/include/ 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 c650aefe5c1..da5fe71a407 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -41,6 +41,14 @@ 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 @@ -66,8 +74,11 @@ $(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 @@ -81,12 +92,12 @@ $(Z15_TESTS): CFLAGS+=-march=z15 -O2 TESTS+=$(Z15_TESTS) endif -ifeq ($(HOST_GDB_SUPPORTS_ARCH),y) +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) @@ -95,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 00000000000..d982f8a651b --- /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/clc.c b/tests/tcg/s390x/clc.c new file mode 100644 index 00000000000..e14189bd75e --- /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/console.c b/tests/tcg/s390x/console.c index d43ce3f44b4..6c26f04949f 100644 --- a/tests/tcg/s390x/console.c +++ b/tests/tcg/s390x/console.c @@ -4,7 +4,10 @@ * * 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) { diff --git a/tests/tcg/s390x/cvb.c b/tests/tcg/s390x/cvb.c new file mode 100644 index 00000000000..e1735f6b81c --- /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 00000000000..d776688985e --- /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/ex-smc.c b/tests/tcg/s390x/ex-smc.c new file mode 100644 index 00000000000..f403640d6ba --- /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/float.h b/tests/tcg/s390x/float.h new file mode 100644 index 00000000000..9d1682b8fc5 --- /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 00000000000..6872f59a7a6 --- /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 ca2bbc0b03e..b6b7b39fc46 100644 --- a/tests/tcg/s390x/gdbstub/test-signals-s390x.py +++ b/tests/tcg/s390x/gdbstub/test-signals-s390x.py @@ -7,19 +7,7 @@ # 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,31 +30,7 @@ def run_test(): gdb.Breakpoint("_exit") gdb.execute("c") status = int(gdb.parse_and_eval("$r2")) - 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) + report(status == 0, "status == 0") -try: - # 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 804705fede9..17210b4e020 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(): @@ -35,26 +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: - # 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/laalg.c b/tests/tcg/s390x/laalg.c new file mode 100644 index 00000000000..797d168bb16 --- /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 00000000000..59712b5e371 --- /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/mvc-smc.c b/tests/tcg/s390x/mvc-smc.c new file mode 100644 index 00000000000..d68f60caa85 --- /dev/null +++ b/tests/tcg/s390x/mvc-smc.c @@ -0,0 +1,82 @@ +/* + * Test modifying code using the MVC instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#define PAGE_SIZE 4096 +#define BR_14_SIZE 2 +#define RWX_OFFSET 2 + +static unsigned char rw[PAGE_SIZE + BR_14_SIZE]; +static unsigned char rwx[RWX_OFFSET + sizeof(rw)] + __attribute__((aligned(PAGE_SIZE))); + +typedef unsigned long (*function_t)(unsigned long); + +static int emit_function(unsigned char *p, int n) +{ + int i = 0, val = 0; + + while (i < n - 2) { + /* aghi %r2,1 */ + p[i++] = 0xa7; + p[i++] = 0x2b; + p[i++] = 0x00; + p[i++] = 0x01; + val++; + } + + /* br %r14 */ + p[i++] = 0x07; + p[i++] = 0xfe; + + return val; +} + +static void memcpy_mvc(void *dest, void *src, unsigned long n) +{ + while (n >= 256) { + asm("mvc 0(256,%[dest]),0(%[src])" + : + : [dest] "a" (dest) + , [src] "a" (src) + : "memory"); + dest += 256; + src += 256; + n -= 256; + } + asm("exrl %[n],0f\n" + "j 1f\n" + "0: mvc 0(1,%[dest]),0(%[src])\n" + "1:" + : + : [dest] "a" (dest) + , [src] "a" (src) + , [n] "a" (n) + : "memory"); +} + +int main(void) +{ + int expected, size; + + /* Create a TB. */ + size = sizeof(rwx) - RWX_OFFSET - 4; + expected = emit_function(rwx + RWX_OFFSET, size); + if (((function_t)(rwx + RWX_OFFSET))(0) != expected) { + return 1; + } + + /* Overwrite the TB. */ + size += 4; + expected = emit_function(rw, size); + memcpy_mvc(rwx + RWX_OFFSET, rw, size); + if (((function_t)(rwx + RWX_OFFSET))(0) != expected) { + return 2; + } + + return 0; +} diff --git a/tests/tcg/s390x/per.S b/tests/tcg/s390x/per.S new file mode 100644 index 00000000000..79e704a6ff6 --- /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 index d534f4e505d..86c340aeef0 100644 --- a/tests/tcg/s390x/pgm-specification-softmmu.S +++ b/tests/tcg/s390x/pgm-specification-softmmu.S @@ -1,5 +1,5 @@ /* - * Common softmmu code for specification exception testing. + * Common system code for specification exception testing. * * SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/tests/tcg/s390x/pgm-specification.mak b/tests/tcg/s390x/pgm-specification.mak index 2999aee26e6..73dc47af0d2 100644 --- a/tests/tcg/s390x/pgm-specification.mak +++ b/tests/tcg/s390x/pgm-specification.mak @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later # List of specification exception tests. -# Shared between the softmmu and the user makefiles. +# Shared between the system and the user makefiles. PGM_SPECIFICATION_TESTS = \ br-odd \ cgrl-unaligned \ diff --git a/tests/tcg/s390x/softmmu.ld b/tests/tcg/s390x/softmmu.ld index ea944eaa3cb..c7a8864407e 100644 --- a/tests/tcg/s390x/softmmu.ld +++ b/tests/tcg/s390x/softmmu.ld @@ -1,5 +1,5 @@ /* - * Linker script for the softmmu test kernels. + * Linker script for the system test kernels. * * SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/tests/tcg/s390x/ts.c b/tests/tcg/s390x/ts.c new file mode 100644 index 00000000000..441faf30d98 --- /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/vfminmax.c b/tests/tcg/s390x/vfminmax.c index 22629df160e..e66285f7625 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; + memcpy(&v1_exp[pos], &v3[pos], float_size); } - xi_exp |= spec[0] == 'X'; - pos += float_size; + } else { + fprintf(stderr, "Unexpected spec: %s\n", spec); + return 1; + } + xi_exp |= spec[0] == 'X'; + pos += float_size; - if ((m5 & 8) || pos == 16) { - ret |= signed_test(test, m4, m5, - v1_exp, xi_exp, v2, v3); - pos = 0; - xi_exp = false; - } + if ((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/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target index 47c39a44b69..7852fa62d88 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 00000000000..ca87fe746ad --- /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 00000000000..b66c854365a --- /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 00000000000..4eceec8634b --- /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 00000000000..a3c2db96e45 --- /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 408dace7839..00000000000 --- 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 2ec0bd36225..258aeb40ae8 100644 --- a/tests/tcg/tricore/Makefile.softmmu-target +++ b/tests/tcg/tricore/Makefile.softmmu-target @@ -9,11 +9,15 @@ CFLAGS = -mtc162 -c -I$(TESTS_PATH) 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 @@ -25,7 +29,7 @@ TESTS += test_muls.asm.tst TESTS += test_boot_to_main.c.tst TESTS += test_context_save_areas.c.tst -QEMU_OPTS += -M tricore_testboard -cpu tc27x -nographic -kernel +QEMU_OPTS += -M tricore_testboard -cpu tc37x -nographic -kernel %.pS: $(ASM_TESTS_PATH)/%.S $(CC) -E -o $@ $< diff --git a/tests/tcg/tricore/asm/macros.h b/tests/tcg/tricore/asm/macros.h index b5087b5c97e..e831f737216 100644 --- a/tests/tcg/tricore/asm/macros.h +++ b/tests/tcg/tricore/asm/macros.h @@ -12,31 +12,31 @@ #define TESTDEV_ADDR 0xf0000000 /* Register definitions */ #define DREG_RS1 %d0 -#define DREG_RS2 %d1 -#define DREG_RS3 %d2 -#define DREG_CALC_RESULT %d3 -#define DREG_CALC_PSW %d4 -#define DREG_CORRECT_PSW %d5 -#define DREG_TEMP_LI %d10 -#define DREG_TEMP %d11 -#define DREG_TEST_NUM %d14 -#define DREG_CORRECT_RESULT %d15 -#define DREG_CORRECT_RESULT_2 %d13 +#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 %e6 -#define EREG_RS1_LO %d6 -#define EREG_RS1_HI %d7 -#define EREG_RS2 %e8 -#define EREG_RS2_LO %d8 -#define EREG_RS2_HI %d9 -#define EREG_CALC_RESULT %e8 -#define EREG_CALC_RESULT_HI %d9 -#define EREG_CALC_RESULT_LO %d8 +#define EREG_RS1 %e0 +#define EREG_RS1_LO %d0 +#define EREG_RS1_HI %d1 +#define EREG_RS2 %e2 +#define EREG_RS2_LO %d2 +#define EREG_RS2_HI %d3 +#define EREG_CALC_RESULT %e6 +#define EREG_CALC_RESULT_LO %d6 +#define EREG_CALC_RESULT_HI %d7 #define EREG_CORRECT_RESULT_LO %d0 #define EREG_CORRECT_RESULT_HI %d1 @@ -46,7 +46,8 @@ test_ ## num: \ code; \ LI(DREG_CORRECT_RESULT, correct) \ mov DREG_TEST_NUM, num; \ - jne testreg, DREG_CORRECT_RESULT, fail \ + jne testreg, DREG_CORRECT_RESULT, fail; \ + mov testreg, 0 #define TEST_CASE_E(num, correct_lo, correct_hi, code...) \ test_ ## num: \ @@ -161,6 +162,30 @@ test_ ## num: \ 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); \ diff --git a/tests/tcg/tricore/asm/test_crcn.S b/tests/tcg/tricore/asm/test_crcn.S new file mode 100644 index 00000000000..51a22722a3f --- /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_ftohp.S b/tests/tcg/tricore/asm/test_ftohp.S new file mode 100644 index 00000000000..9e23141c1e0 --- /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/asm/test_ftou.S b/tests/tcg/tricore/asm/test_ftou.S new file mode 100644 index 00000000000..10f106ad620 --- /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 00000000000..8adc5e5273f --- /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_insert.S b/tests/tcg/tricore/asm/test_insert.S index d5fd2237e18..223d7ce796c 100644 --- a/tests/tcg/tricore/asm/test_insert.S +++ b/tests/tcg/tricore/asm/test_insert.S @@ -6,4 +6,18 @@ _start: # | | | | | | | 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/x86_64/Makefile.softmmu-target b/tests/tcg/x86_64/Makefile.softmmu-target index 1bd763f2e6c..ef6bcb4dc74 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) diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target index e64aab1b81c..be20fc64e88 100644 --- a/tests/tcg/x86_64/Makefile.target +++ b/tests/tcg/x86_64/Makefile.target @@ -8,11 +8,17 @@ 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 +X86_64_TESTS += fma TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64 else TESTS=$(MULTIARCH_TESTS) @@ -23,6 +29,9 @@ 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/cross-modifying-code.c b/tests/tcg/x86_64/cross-modifying-code.c new file mode 100644 index 00000000000..2704df6061c --- /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/fma.c b/tests/tcg/x86_64/fma.c new file mode 100644 index 00000000000..34219614c0a --- /dev/null +++ b/tests/tcg/x86_64/fma.c @@ -0,0 +1,116 @@ +/* + * Test some fused multiply add corner cases. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +/* + * Perform one "n * m + a" operation using the vfmadd insn and return + * the result; on return *mxcsr_p is set to the bottom 6 bits of MXCSR + * (the Flag bits). If ftz is true then we set MXCSR.FTZ while doing + * the operation. + * We print the operation and its results to stdout. + */ +static uint64_t do_fmadd(uint64_t n, uint64_t m, uint64_t a, + bool ftz, uint32_t *mxcsr_p) +{ + uint64_t r; + uint32_t mxcsr = 0; + uint32_t ftz_bit = ftz ? (1 << 15) : 0; + uint32_t saved_mxcsr = 0; + + asm volatile("stmxcsr %[saved_mxcsr]\n" + "stmxcsr %[mxcsr]\n" + "andl $0xffff7fc0, %[mxcsr]\n" + "orl %[ftz_bit], %[mxcsr]\n" + "ldmxcsr %[mxcsr]\n" + "movq %[a], %%xmm0\n" + "movq %[m], %%xmm1\n" + "movq %[n], %%xmm2\n" + /* xmm0 = xmm0 + xmm2 * xmm1 */ + "vfmadd231sd %%xmm1, %%xmm2, %%xmm0\n" + "movq %%xmm0, %[r]\n" + "stmxcsr %[mxcsr]\n" + "ldmxcsr %[saved_mxcsr]\n" + : [r] "=r" (r), [mxcsr] "=m" (mxcsr), + [saved_mxcsr] "=m" (saved_mxcsr) + : [n] "r" (n), [m] "r" (m), [a] "r" (a), + [ftz_bit] "r" (ftz_bit) + : "xmm0", "xmm1", "xmm2"); + *mxcsr_p = mxcsr & 0x3f; + printf("vfmadd132sd 0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64 + " = 0x%" PRIx64 " MXCSR flags 0x%" PRIx32 "\n", + n, m, a, r, *mxcsr_p); + return r; +} + +typedef struct testdata { + /* Input n, m, a */ + uint64_t n; + uint64_t m; + uint64_t a; + bool ftz; + /* Expected result */ + uint64_t expected_r; + /* Expected low 6 bits of MXCSR (the Flag bits) */ + uint32_t expected_mxcsr; +} testdata; + +static testdata tests[] = { + { 0, 0x7ff0000000000000, 0x7ff000000000aaaa, false, /* 0 * Inf + SNaN */ + 0x7ff800000000aaaa, 1 }, /* Should be QNaN and does raise Invalid */ + { 0, 0x7ff0000000000000, 0x7ff800000000aaaa, false, /* 0 * Inf + QNaN */ + 0x7ff800000000aaaa, 0 }, /* Should be QNaN and does *not* raise Invalid */ + /* + * These inputs give a result which is tiny before rounding but which + * becomes non-tiny after rounding. x86 is a "detect tininess after + * rounding" architecture, so it should give a non-denormal result and + * not set the Underflow flag (only the Precision flag for an inexact + * result). + */ + { 0x3fdfffffffffffff, 0x001fffffffffffff, 0x801fffffffffffff, false, + 0x8010000000000000, 0x20 }, + /* + * Flushing of denormal outputs to zero should also happen after + * rounding, so setting FTZ should not affect the result or the flags. + */ + { 0x3fdfffffffffffff, 0x001fffffffffffff, 0x801fffffffffffff, true, + 0x8010000000000000, 0x20 }, /* Enabling FTZ shouldn't change flags */ + /* + * normal * 0 + a denormal. With FTZ disabled this gives an exact + * result (equal to the input denormal) that has consumed the denormal. + */ + { 0x3cc8000000000000, 0x0000000000000000, 0x8008000000000000, false, + 0x8008000000000000, 0x2 }, /* Denormal */ + /* + * With FTZ enabled, this consumes the denormal, returns zero (because + * flushed) and indicates also Underflow and Precision. + */ + { 0x3cc8000000000000, 0x0000000000000000, 0x8008000000000000, true, + 0x8000000000000000, 0x32 }, /* Precision, Underflow, Denormal */ +}; + +int main(void) +{ + bool passed = true; + for (int i = 0; i < ARRAY_SIZE(tests); i++) { + uint32_t mxcsr; + uint64_t r = do_fmadd(tests[i].n, tests[i].m, tests[i].a, + tests[i].ftz, &mxcsr); + if (r != tests[i].expected_r) { + printf("expected result 0x%" PRIx64 "\n", tests[i].expected_r); + passed = false; + } + if (mxcsr != tests[i].expected_mxcsr) { + printf("expected MXCSR flags 0x%x\n", tests[i].expected_mxcsr); + passed = false; + } + } + return passed ? 0 : 1; +} diff --git a/tests/tcg/x86_64/system/boot.S b/tests/tcg/x86_64/system/boot.S index dac9bd534d7..7213aec63b2 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 diff --git a/tests/tcg/x86_64/test-1648.c b/tests/tcg/x86_64/test-1648.c new file mode 100644 index 00000000000..fd0644a8ce2 --- /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 00000000000..aafd037bce2 --- /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 00000000000..456e5332fc2 --- /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 78bf72dfaa4..a29571b367c 100644 --- a/tests/tcg/xtensa/Makefile.softmmu-target +++ b/tests/tcg/xtensa/Makefile.softmmu-target @@ -1,5 +1,5 @@ # -# Xtensa softmmu tests +# Xtensa system tests # CORE=dc232b diff --git a/tests/tcg/xtensa/test_break.S b/tests/tcg/xtensa/test_break.S index 3aa18b5cec3..4c618feb5b1 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 index 4204a96d53c..95d0528c372 100644 --- a/tests/tcg/xtensaeb/Makefile.softmmu-target +++ b/tests/tcg/xtensaeb/Makefile.softmmu-target @@ -1,5 +1,5 @@ # -# Xtensa softmmu tests +# 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 75e444f5dc6..00000000000 --- 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 00000000000..423e482d2f9 --- /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 d9a002a2ef1..b3ef59c27c0 100644 --- a/tests/tsan/suppressions.tsan +++ b/tests/tsan/suppressions.tsan @@ -4,7 +4,6 @@ # 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 between pthread_mutex_init() and diff --git a/tests/uefi-test-tools/Makefile b/tests/uefi-test-tools/Makefile index 0c003f2877f..8ee6fb3571f 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 loongarch64 uefi_binaries := bios-tables-test intermediate_suffixes := .efi .fat .iso.raw @@ -56,7 +56,9 @@ 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_loongarch64_to_uefi = $(subst loongarch64,LOONGARCH64,$(call map_aarch64_to_uefi,$(1))) +map_riscv64_to_uefi = $(subst riscv64,RISCV64,$(call map_loongarch64_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 +72,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 $@ -- $< \ @@ -95,15 +97,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 c8511cd732b..facf8df1fa9 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|LOONGARCH64 BUILD_TARGETS = DEBUG [BuildOptions.IA32] @@ -60,6 +60,14 @@ [LibraryClasses.IA32, LibraryClasses.X64] BaseMemoryLib|MdePkg/Library/BaseMemoryLibRepStr/BaseMemoryLibRepStr.inf + RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf + +[LibraryClasses.RISCV64] + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + +[LibraryClasses.LOONGARCH64] + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + StackCheckLib|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.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 00000000000..8bf48266345 --- /dev/null +++ b/tests/uefi-test-tools/uefi-test-build.config @@ -0,0 +1,62 @@ +[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 + +#################################################################################### +# loongarch64 + +[build.loongarch64] +conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc +plat = UefiTestTools +dest = ./Build +arch = LOONGARCH64 +cpy1 = LOONGARCH64/BiosTablesTest.efi bios-tables-test.loongarch64.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-block-qdict.c b/tests/unit/check-block-qdict.c index 751c58e7373..0036d85cfaa 100644 --- a/tests/unit/check-block-qdict.c +++ b/tests/unit/check-block-qdict.c @@ -9,8 +9,8 @@ #include "qemu/osdep.h" #include "block/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" #include "qapi/error.h" static void qdict_defaults_test(void) diff --git a/tests/unit/check-qdict.c b/tests/unit/check-qdict.c index b5efa859b0b..a1312be30a8 100644 --- a/tests/unit/check-qdict.c +++ b/tests/unit/check-qdict.c @@ -11,9 +11,9 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" /* * Public Interface test-cases diff --git a/tests/unit/check-qjson.c b/tests/unit/check-qjson.c index a89293ce51b..780a3654d08 100644 --- a/tests/unit/check-qjson.c +++ b/tests/unit/check-qjson.c @@ -14,12 +14,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qlit.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qjson.h" +#include "qobject/qlit.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qemu/unicode.h" static QString *from_json_str(const char *jstr, bool single, Error **errp) diff --git a/tests/unit/check-qlist.c b/tests/unit/check-qlist.c index 3cd0ccbf198..1388aeede35 100644 --- a/tests/unit/check-qlist.c +++ b/tests/unit/check-qlist.c @@ -11,8 +11,8 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qlist.h" /* * Public Interface test-cases diff --git a/tests/unit/check-qlit.c b/tests/unit/check-qlit.c index bd6798d9122..ea7a0d9119e 100644 --- a/tests/unit/check-qlit.c +++ b/tests/unit/check-qlit.c @@ -9,12 +9,12 @@ #include "qemu/osdep.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qlit.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qlit.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" static QLitObject qlit = QLIT_QDICT(((QLitDictEntry[]) { { "foo", QLIT_QNUM(42) }, diff --git a/tests/unit/check-qnull.c b/tests/unit/check-qnull.c index 5ceacc65d7f..724a66d0bd2 100644 --- a/tests/unit/check-qnull.c +++ b/tests/unit/check-qnull.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "qapi/error.h" diff --git a/tests/unit/check-qnum.c b/tests/unit/check-qnum.c index bf7fe45bacc..a40120e8d3d 100644 --- a/tests/unit/check-qnum.c +++ b/tests/unit/check-qnum.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qnum.h" /* * Public Interface test-cases diff --git a/tests/unit/check-qobject.c b/tests/unit/check-qobject.c index 022b7c74fe5..ccb25660f26 100644 --- a/tests/unit/check-qobject.c +++ b/tests/unit/check-qobject.c @@ -9,12 +9,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include diff --git a/tests/unit/check-qom-interface.c b/tests/unit/check-qom-interface.c index c99be97ed8b..86ae5f6c3b1 100644 --- a/tests/unit/check-qom-interface.c +++ b/tests/unit/check-qom-interface.c @@ -38,7 +38,7 @@ static const TypeInfo test_if_info = { #define PATTERN 0xFAFBFCFD -static void test_class_init(ObjectClass *oc, void *data) +static void test_class_init(ObjectClass *oc, const void *data) { TestIfClass *tc = TEST_IF_CLASS(oc); @@ -52,7 +52,7 @@ static const TypeInfo direct_impl_info = { .name = TYPE_DIRECT_IMPL, .parent = TYPE_OBJECT, .class_init = test_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TEST_IF }, { } } diff --git a/tests/unit/check-qom-proplist.c b/tests/unit/check-qom-proplist.c index 79d4a8b89d3..ee3c6fb32b1 100644 --- a/tests/unit/check-qom-proplist.c +++ b/tests/unit/check-qom-proplist.c @@ -22,8 +22,8 @@ #include "qapi/error.h" #include "qapi/qobject-input-visitor.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qobject.h" +#include "qobject/qdict.h" +#include "qobject/qobject.h" #include "qom/object.h" #include "qemu/module.h" #include "qemu/option.h" @@ -135,7 +135,7 @@ static void dummy_init(Object *obj) } -static void dummy_class_init(ObjectClass *cls, void *data) +static void dummy_class_init(ObjectClass *cls, const void *data) { object_class_property_add_str(cls, "sv", dummy_get_sv, @@ -164,7 +164,7 @@ static const TypeInfo dummy_info = { .instance_finalize = dummy_finalize, .class_size = sizeof(DummyObjectClass), .class_init = dummy_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } @@ -264,7 +264,7 @@ static void dummy_dev_unparent(Object *obj) object_unparent(OBJECT(dev->bus)); } -static void dummy_dev_class_init(ObjectClass *klass, void *opaque) +static void dummy_dev_class_init(ObjectClass *klass, const void *opaque) { klass->unparent = dummy_dev_unparent; } @@ -288,7 +288,7 @@ static void dummy_bus_unparent(Object *obj) object_unparent(OBJECT(bus->backend)); } -static void dummy_bus_class_init(ObjectClass *klass, void *opaque) +static void dummy_bus_class_init(ObjectClass *klass, const void *opaque) { klass->unparent = dummy_bus_unparent; } @@ -610,7 +610,7 @@ static void test_dummy_delchild(void) static void test_qom_partial_path(void) { Object *root = object_get_objects_root(); - Object *cont1 = container_get(root, "/cont1"); + Object *cont1 = object_property_add_new_container(root, "cont1"); Object *obj1 = object_new(TYPE_DUMMY); Object *obj2a = object_new(TYPE_DUMMY); Object *obj2b = object_new(TYPE_DUMMY); diff --git a/tests/unit/check-qstring.c b/tests/unit/check-qstring.c index bd861f4f8b4..2e6a00570fd 100644 --- a/tests/unit/check-qstring.c +++ b/tests/unit/check-qstring.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" /* * Public Interface test-cases diff --git a/tests/unit/crypto-tls-psk-helpers.c b/tests/unit/crypto-tls-psk-helpers.c index c6cc740772e..36527fd6555 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 e9937f60d86..2daecc416c6 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 247e7160ebd..2a0f7c04fd8 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 0299ef6906c..d5248ae51de 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -18,11 +18,11 @@ tests = { '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': [], @@ -45,11 +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-interval-tree': [], - 'test-xs-node': [qom], + 'test-fifo': [], } if have_system or have_tools @@ -58,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 @@ -95,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 \ - targetos != 'windows' + 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() @@ -113,26 +113,27 @@ if have_block if xts == 'private' tests += {'test-crypto-xts': [crypto, io]} endif - if targetos != 'windows' + if host_os != 'windows' tests += { 'test-image-locking': [testblock], - 'test-nested-aio-poll': [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 + 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': [], @@ -148,7 +149,7 @@ if have_system # are not runnable under TSan due to a known issue. # https://github.com/google/sanitizers/issues/1116 if not get_option('tsan') - if targetos != 'windows' + if host_os != 'windows' tests += { 'test-char': ['socket-helpers.c', qom, io, chardev] } @@ -160,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 @@ -170,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 89521408a1d..00000000000 --- 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 00000000000..fe29c4102ae --- /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.c b/tests/unit/ptimer-test.c index 04b5f4e3d03..08240594bbd 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/socket-helpers.c b/tests/unit/socket-helpers.c index 6de27baee2e..37db24f72a1 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); - close(fd); #ifdef _WIN32 *has_afunix = (fd != (int)INVALID_SOCKET); @@ -168,5 +167,7 @@ void socket_check_afunix_support(bool *has_afunix) *has_afunix = (fd >= 0); #endif - return; + if (*has_afunix) { + close(fd); + } } diff --git a/tests/unit/test-aio-multithread.c b/tests/unit/test-aio-multithread.c index 08d4570ccb1..0ead6bf34ad 100644 --- a/tests/unit/test-aio-multithread.c +++ b/tests/unit/test-aio-multithread.c @@ -305,7 +305,9 @@ static void mcs_mutex_lock(void) prev = qatomic_xchg(&mutex_head, id); if (prev != -1) { qatomic_set(&nodes[prev].next, id); - qemu_futex_wait(&nodes[id].locked, 1); + while (qatomic_read(&nodes[id].locked) == 1) { + qemu_futex_wait(&nodes[id].locked, 1); + } } } @@ -328,7 +330,7 @@ static void mcs_mutex_unlock(void) /* Wake up the next in line. */ next = qatomic_read(&nodes[id].next); nodes[next].locked = 0; - qemu_futex_wake(&nodes[next].locked, 1); + qemu_futex_wake_single(&nodes[next].locked); } static void test_multi_fair_mutex_entry(void *opaque) diff --git a/tests/unit/test-aio.c b/tests/unit/test-aio.c index 71ed31a4db6..e77d86be875 100644 --- a/tests/unit/test-aio.c +++ b/tests/unit/test-aio.c @@ -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, 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) @@ -879,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); @@ -899,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); diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index ccc453c29e4..59c27937259 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "block/block_int.h" #include "block/blockjob_int.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qemu/main-loop.h" #include "iothread.h" @@ -96,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; } @@ -116,7 +116,7 @@ static BlockDriver bdrv_test = { .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) @@ -179,13 +179,7 @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs) 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) @@ -209,17 +203,17 @@ static BlockBackend * no_coroutine_fn test_setup(void) 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(BlockBackend *blk, 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) { BlockDriverState *bs = blk_bs(blk); BlockDriverState *backing = bs->backing->bs; @@ -307,8 +301,14 @@ static void test_drv_cb_co_drain(void) blk_unref(blk); } -static void test_quiesce_common(BlockBackend *blk, 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_quiesce_common(BlockBackend *blk, enum drain_type drain_type, + bool recursive) { BlockDriverState *bs = blk_bs(blk); BlockDriverState *backing = bs->backing->bs; @@ -508,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() */ } /* @@ -554,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; @@ -569,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, @@ -579,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 @@ -593,20 +590,10 @@ 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: co = qemu_coroutine_create(test_iothread_drain_co_entry, &data); @@ -617,9 +604,7 @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) 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); @@ -647,6 +632,8 @@ typedef struct TestBlockJob { BlockDriverState *bs; int run_ret; int prepare_ret; + + /* Accessed with atomics */ bool running; bool should_complete; } TestBlockJob; @@ -682,10 +669,10 @@ static int coroutine_fn test_job_run(Job *job, Error **errp) /* We are running the actual job code past the pause point in * job_co_entry(). */ - s->running = true; + qatomic_set(&s->running, true); job_transition_to_ready(&s->common.job); - while (!s->should_complete) { + while (!qatomic_read(&s->should_complete)) { /* Avoid job_sleep_ns() because it marks the job as !busy. We want to * emulate some actual activity (probably some I/O) here so that drain * has to wait for this activity to stop. */ @@ -700,7 +687,7 @@ static int coroutine_fn test_job_run(Job *job, Error **errp) static void test_job_complete(Job *job, Error **errp) { TestBlockJob *s = container_of(job, TestBlockJob, common.job); - s->should_complete = true; + qatomic_set(&s->should_complete, true); } BlockJobDriver test_job_driver = { @@ -737,8 +724,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); @@ -767,11 +753,11 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, } 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, @@ -780,13 +766,17 @@ 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_drain_all_begin(); + bdrv_graph_wrlock(); block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); switch (result) { case TEST_JOB_SUCCESS: @@ -798,7 +788,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); @@ -806,7 +795,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, /* job_co_entry() is run in the I/O thread, wait for the actual job * code to start (we don't want to catch the job in the pause point in * job_co_entry(). */ - while (!tjob->running) { + while (!qatomic_read(&tjob->running)) { aio_poll(qemu_get_aio_context(), false); } } @@ -814,7 +803,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, WITH_JOB_LOCK_GUARD() { g_assert_cmpint(job->job.pause_count, ==, 0); g_assert_false(job->job.paused); - g_assert_true(tjob->running); + g_assert_true(qatomic_read(&tjob->running)); g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ } @@ -840,7 +829,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, * * paused is reset in the I/O thread, wait for it */ - while (job->job.paused) { + while (job_is_paused(&job->job)) { aio_poll(qemu_get_aio_context(), false); } } @@ -873,7 +862,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, * * paused is reset in the I/O thread, wait for it */ - while (job->job.paused) { + while (job_is_paused(&job->job)) { aio_poll(qemu_get_aio_context(), false); } } @@ -889,12 +878,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); @@ -967,9 +954,14 @@ typedef struct BDRVTestTopState { static void bdrv_test_top_close(BlockDriverState *bs) { BdrvChild *c, *next_c; + + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { bdrv_unref_child(bs, c); } + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); } static int coroutine_fn GRAPH_RDLOCK @@ -1023,9 +1015,15 @@ static void coroutine_fn test_co_delete_by_drain(void *opaque) 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_drain_all_begin(); + bdrv_co_unref_child(bs, c); + bdrv_drain_all_end(); + bdrv_graph_co_rdlock(); } + bdrv_graph_co_rdunlock(); } dbdd->done = true; @@ -1055,8 +1053,12 @@ 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_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); /* This child will be the one to pass to requests through to, and * it will stall until a drain occurs */ @@ -1064,17 +1066,25 @@ 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_drain_all_begin(); + 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(); + bdrv_drain_all_end(); /* 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_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); blk_insert_bs(blk, bs, &error_abort); @@ -1151,31 +1161,37 @@ struct detach_by_parent_data { }; 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_drain_all_begin(); + 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(); + bdrv_drain_all_end(); } -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) { bdrv_inc_in_flight(data->child_b->bs); - detach_indirect_bh(data); + 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; @@ -1212,7 +1228,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; @@ -1258,6 +1274,8 @@ static void test_detach_indirect(bool by_parent_cb) /* Set child relationships */ bdrv_ref(b); bdrv_ref(a); + bdrv_drain_all_begin(); + 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, @@ -1267,6 +1285,8 @@ 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(); + bdrv_drain_all_end(); g_assert_cmpint(parent_a->refcnt, ==, 1); g_assert_cmpint(parent_b->refcnt, ==, 1); @@ -1360,6 +1380,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); @@ -1391,19 +1412,10 @@ static void test_set_aio_context(void) bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, &error_abort); - 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); iothread_join(a); @@ -1413,10 +1425,12 @@ static void test_set_aio_context(void) typedef struct TestDropBackingBlockJob { BlockJob common; - bool should_complete; bool *did_complete; BlockDriverState *detach_also; BlockDriverState *bs; + + /* Accessed with atomics */ + bool should_complete; } TestDropBackingBlockJob; static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp) @@ -1424,7 +1438,7 @@ static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp) TestDropBackingBlockJob *s = container_of(job, TestDropBackingBlockJob, common.job); - while (!s->should_complete) { + while (!qatomic_read(&s->should_complete)) { job_sleep_ns(job, 0); } @@ -1543,7 +1557,7 @@ static void test_blockjob_commit_by_drained_end(void) job_start(&job->common.job); - job->should_complete = true; + qatomic_set(&job->should_complete, true); bdrv_drained_begin(bs_child); g_assert(!job_has_completed); bdrv_drained_end(bs_child); @@ -1559,15 +1573,17 @@ static void test_blockjob_commit_by_drained_end(void) typedef struct TestSimpleBlockJob { BlockJob common; - bool should_complete; bool *did_complete; + + /* Accessed with atomics */ + bool should_complete; } TestSimpleBlockJob; static int coroutine_fn test_simple_job_run(Job *job, Error **errp) { TestSimpleBlockJob *s = container_of(job, TestSimpleBlockJob, common.job); - while (!s->should_complete) { + while (!qatomic_read(&s->should_complete)) { job_sleep_ns(job, 0); } @@ -1593,6 +1609,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) { /* @@ -1682,6 +1699,8 @@ 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_drain_all_begin(); + bdrv_graph_wrlock(); for (i = 0; i < 3; i++) { if (i) { /* Takes the reference to chain[i - 1] */ @@ -1689,6 +1708,8 @@ static void test_drop_intermediate_poll(void) &chain_child_class, BDRV_CHILD_COW, &error_abort); } } + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); job = block_job_create("job", &test_simple_job_driver, NULL, job_node, 0, BLK_PERM_ALL, 0, 0, NULL, NULL, &error_abort); @@ -1699,10 +1720,10 @@ static void test_drop_intermediate_poll(void) job->did_complete = &job_has_completed; job_start(&job->common.job); - job->should_complete = true; + qatomic_set(&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); @@ -1833,6 +1854,8 @@ static void bdrv_replace_test_drain_end(BlockDriverState *bs) { BDRVReplaceTestState *s = bs->opaque; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!s->setup_completed) { return; } @@ -1933,8 +1956,12 @@ static void do_test_replace_child_mid_drain(int old_drain_count, new_child_bs->total_sectors = 1; bdrv_ref(old_child_bs); + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_attach_child(parent_bs, old_child_bs, "child", &child_of_bds, BDRV_CHILD_COW, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); parent_s->setup_completed = true; for (i = 0; i < old_drain_count; i++) { @@ -1963,7 +1990,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) { diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c index 36eed4b4642..7b03ebe4b02 100644 --- a/tests/unit/test-bdrv-graph-mod.c +++ b/tests/unit/test-bdrv-graph-mod.c @@ -22,7 +22,7 @@ #include "qapi/error.h" #include "qemu/main-loop.h" #include "block/block_int.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" static BlockDriver bdrv_pass_through = { .format_name = "pass-through", @@ -137,8 +137,12 @@ static void test_update_perm_tree(void) blk_insert_bs(root, bs, &error_abort); + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_attach_child(filter, bs, "child", &child_of_bds, BDRV_CHILD_DATA, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); ret = bdrv_append(filter, bs, NULL); g_assert_cmpint(ret, <, 0); @@ -202,11 +206,18 @@ static void test_should_update_child(void) bdrv_set_backing_hd(target, bs, &error_abort); + bdrv_drain_all_begin(); + 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_drain_all_end(); 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 +237,19 @@ 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_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_attach_child(top, fl1, "backing", &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); @@ -243,7 +261,13 @@ static void test_parallel_exclusive_write(void) &error_abort); bdrv_replace_node(fl1, fl2, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); + bdrv_drained_end(fl2); + bdrv_drained_end(fl1); + + bdrv_unref(fl1); bdrv_unref(fl2); bdrv_unref(top); } @@ -345,6 +369,8 @@ static void test_parallel_perm_update(void) */ bdrv_ref(base); + bdrv_drain_all_begin(); + 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 +383,14 @@ 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(); + bdrv_drain_all_end(); /* 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 +410,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 +438,13 @@ static void test_append_greedy_filter(void) BlockDriverState *base = no_perm_node("base"); BlockDriverState *fl = exclusive_writer_node("fl1"); + bdrv_drain_all_begin(); + bdrv_graph_wrlock(); bdrv_attach_child(top, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); + bdrv_graph_wrunlock(); + bdrv_drain_all_end(); bdrv_append(fl, base, &error_abort); bdrv_unref(fl); diff --git a/tests/unit/test-block-backend.c b/tests/unit/test-block-backend.c index 2fb1a444bd4..4257b3f8153 100644 --- a/tests/unit/test-block-backend.c +++ b/tests/unit/test-block-backend.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "block/block.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qemu/main-loop.h" diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index d727a5fee84..e26b3be5939 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -26,9 +26,9 @@ #include "block/block.h" #include "block/block_int-global-state.h" #include "block/blockjob_int.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/main-loop.h" #include "iothread.h" @@ -63,7 +63,7 @@ bdrv_test_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, } static int coroutine_fn bdrv_test_co_block_status(BlockDriverState *bs, - bool want_zero, + unsigned int mode, int64_t offset, int64_t count, int64_t *pnum, int64_t *map, BlockDriverState **file) @@ -383,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); } @@ -468,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); } @@ -483,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); @@ -568,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) { @@ -587,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); @@ -646,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); @@ -724,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); @@ -747,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*/ @@ -761,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"); } @@ -774,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); @@ -794,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); @@ -806,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); @@ -825,7 +818,6 @@ static void test_attach_second_node(void) BlockDriverState *bs, *filter; QDict *options; - aio_context_acquire(main_ctx); blk = blk_new(ctx, BLK_PERM_ALL, BLK_PERM_ALL); bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); blk_insert_bs(blk, bs, &error_abort); @@ -835,15 +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); - aio_context_release(main_ctx); g_assert(blk_get_aio_context(blk) == ctx); g_assert(bdrv_get_aio_context(bs) == ctx); g_assert(bdrv_get_aio_context(filter) == ctx); - 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); @@ -857,11 +846,9 @@ static void test_attach_preserve_blk_ctx(void) { IOThread *iothread = iothread_new(); AioContext *ctx = iothread_get_aio_context(iothread); - AioContext *main_ctx = qemu_get_aio_context(); BlockBackend *blk; BlockDriverState *bs; - aio_context_acquire(main_ctx); blk = blk_new(ctx, 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; @@ -870,25 +857,18 @@ static void test_attach_preserve_blk_ctx(void) blk_insert_bs(blk, bs, &error_abort); g_assert(blk_get_aio_context(blk) == ctx); g_assert(bdrv_get_aio_context(bs) == ctx); - aio_context_release(main_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()); /* Re-attach the node */ - aio_context_acquire(main_ctx); blk_insert_bs(blk, bs, &error_abort); - aio_context_release(main_ctx); g_assert(blk_get_aio_context(blk) == ctx); g_assert(bdrv_get_aio_context(bs) == ctx); - aio_context_acquire(ctx); blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); - aio_context_release(ctx); bdrv_unref(bs); blk_unref(blk); } diff --git a/tests/unit/test-blockjob-txn.c b/tests/unit/test-blockjob-txn.c index d3b0bb24bec..118503a8e87 100644 --- a/tests/unit/test-blockjob-txn.c +++ b/tests/unit/test-blockjob-txn.c @@ -14,8 +14,8 @@ #include "qapi/error.h" #include "qemu/main-loop.h" #include "block/blockjob_int.h" -#include "sysemu/block-backend.h" -#include "qapi/qmp/qdict.h" +#include "system/block-backend.h" +#include "qobject/qdict.h" typedef struct { BlockJob common; diff --git a/tests/unit/test-blockjob.c b/tests/unit/test-blockjob.c index a130f6fefba..abdbe4b8350 100644 --- a/tests/unit/test-blockjob.c +++ b/tests/unit/test-blockjob.c @@ -14,8 +14,8 @@ #include "qapi/error.h" #include "qemu/main-loop.h" #include "block/blockjob_int.h" -#include "sysemu/block-backend.h" -#include "qapi/qmp/qdict.h" +#include "system/block-backend.h" +#include "qobject/qdict.h" #include "iothread.h" static const BlockJobDriver test_block_job_driver = { @@ -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,13 +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); - - /* - * This test is flaky and sometimes fails in CI and otherwise: - * don't run unless user opts in via environment variable. - */ - if (getenv("QEMU_TEST_FLAKY_TESTS")) { - 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 649fdf64e19..f30a39f61ff 100644 --- a/tests/unit/test-char.c +++ b/tests/unit/test-char.c @@ -1,15 +1,16 @@ #include "qemu/osdep.h" #include +#include "qapi/error.h" #include "qemu/config-file.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/sockets.h" #include "chardev/char-fe.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "qapi/qapi-commands-char.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qom/qom-qobject.h" #include "io/channel-socket.h" #include "qapi/qobject-input-visitor.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,9 +350,412 @@ 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); } +static void char_hub_test(void) +{ + QemuOpts *opts; + Chardev *hub, *chr1, *chr2, *base; + char *data; + FeHandler h = { 0, false, 0, false, }; + Error *error = NULL; + CharBackend chr_be; + int ret, i; + +#define RB_SIZE 128 + + /* + * Create invalid hub + * 1. Create hub without a 'chardevs.N' defined (expect error) + */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + qemu_opt_set(opts, "backend", "hub", &error_abort); + hub = qemu_chr_new_from_opts(opts, NULL, &error); + g_assert_cmpstr(error_get_pretty(error), ==, + "hub: 'chardevs' list is not defined"); + error_free(error); + error = NULL; + qemu_opts_del(opts); + + /* + * Create invalid hub + * 1. Create chardev with embedded mux: 'mux=on' + * 2. Create hub which refers mux + * 3. Create hub which refers chardev already attached + * to the mux (already in use, expect error) + */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr0", + 1, &error_abort); + qemu_opt_set(opts, "mux", "on", &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", stringify(RB_SIZE), &error_abort); + base = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(base); + qemu_opts_del(opts); + + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + qemu_opt_set(opts, "backend", "hub", &error_abort); + qemu_opt_set(opts, "chardevs.0", "chr0", &error_abort); + hub = qemu_chr_new_from_opts(opts, NULL, &error); + g_assert_cmpstr(error_get_pretty(error), ==, + "hub: multiplexers and hub devices can't be " + "stacked, check chardev 'chr0', chardev should " + "not be a hub device or have 'mux=on' enabled"); + error_free(error); + error = NULL; + qemu_opts_del(opts); + + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + qemu_opt_set(opts, "backend", "hub", &error_abort); + qemu_opt_set(opts, "chardevs.0", "chr0-base", &error_abort); + hub = qemu_chr_new_from_opts(opts, NULL, &error); + g_assert_cmpstr(error_get_pretty(error), ==, + "chardev 'chr0-base' is already in use"); + error_free(error); + error = NULL; + qemu_opts_del(opts); + + /* Finalize chr0 */ + qmp_chardev_remove("chr0", &error_abort); + + /* + * Create invalid hub with more than maximum allowed backends + * 1. Create more than maximum allowed 'chardevs.%d' options for + * hub (expect error) + */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + for (i = 0; i < 10; i++) { + char key[32], val[32]; + + snprintf(key, sizeof(key), "chardevs.%d", i); + snprintf(val, sizeof(val), "chr%d", i); + qemu_opt_set(opts, key, val, &error); + if (error) { + char buf[64]; + + snprintf(buf, sizeof(buf), "Invalid parameter 'chardevs.%d'", i); + g_assert_cmpstr(error_get_pretty(error), ==, buf); + error_free(error); + break; + } + } + g_assert_nonnull(error); + error = NULL; + qemu_opts_del(opts); + + /* + * Create hub with 2 backend chardevs and 1 frontend and perform + * data aggregation + * 1. Create 2 ringbuf backend chardevs + * 2. Create 1 frontend + * 3. Create hub which refers 2 backend chardevs + * 4. Attach hub to a frontend + * 5. Attach hub to a frontend second time (expect error) + * 6. Perform data aggregation + * 7. Remove chr1 ("chr1 is busy", expect error) + * 8. Remove hub0 ("hub0 is busy", expect error); + * 9. Finilize frontend, hub and backend chardevs in correct order + */ + + /* Create first chardev */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr1", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", stringify(RB_SIZE), &error_abort); + chr1 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr1); + qemu_opts_del(opts); + + /* Create second chardev */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr2", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", stringify(RB_SIZE), &error_abort); + chr2 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr2); + qemu_opts_del(opts); + + /* Create hub0 and refer 2 backend chardevs */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + qemu_opt_set(opts, "backend", "hub", &error_abort); + qemu_opt_set(opts, "chardevs.0", "chr1", &error_abort); + qemu_opt_set(opts, "chardevs.1", "chr2", &error_abort); + hub = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(hub); + qemu_opts_del(opts); + + /* Attach hub to a frontend */ + qemu_chr_fe_init(&chr_be, hub, &error_abort); + qemu_chr_fe_set_handlers(&chr_be, + fe_can_read, + fe_read, + fe_event, + NULL, + &h, + NULL, true); + + /* Fails second time */ + qemu_chr_fe_init(&chr_be, hub, &error); + g_assert_cmpstr(error_get_pretty(error), ==, "chardev 'hub0' is already in use"); + error_free(error); + error = NULL; + + /* Write to backend, chr1 */ + base = qemu_chr_find("chr1"); + g_assert_cmpint(qemu_chr_be_can_write(base), !=, 0); + + qemu_chr_be_write(base, (void *)"hello", 6); + g_assert_cmpint(h.read_count, ==, 6); + g_assert_cmpstr(h.read_buf, ==, "hello"); + h.read_count = 0; + + /* Write to backend, chr2 */ + base = qemu_chr_find("chr2"); + g_assert_cmpint(qemu_chr_be_can_write(base), !=, 0); + + qemu_chr_be_write(base, (void *)"olleh", 6); + g_assert_cmpint(h.read_count, ==, 6); + g_assert_cmpstr(h.read_buf, ==, "olleh"); + h.read_count = 0; + + /* Write to frontend, chr_be */ + ret = qemu_chr_fe_write(&chr_be, (void *)"heyhey", 6); + g_assert_cmpint(ret, ==, 6); + + data = qmp_ringbuf_read("chr1", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 6); + g_assert_cmpstr(data, ==, "heyhey"); + g_free(data); + + data = qmp_ringbuf_read("chr2", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 6); + g_assert_cmpstr(data, ==, "heyhey"); + g_free(data); + + /* Can't be removed, depends on hub0 */ + qmp_chardev_remove("chr1", &error); + g_assert_cmpstr(error_get_pretty(error), ==, "Chardev 'chr1' is busy"); + error_free(error); + error = NULL; + + /* Can't be removed, depends on frontend chr_be */ + qmp_chardev_remove("hub0", &error); + g_assert_cmpstr(error_get_pretty(error), ==, "Chardev 'hub0' is busy"); + error_free(error); + error = NULL; + + /* Finalize frontend */ + qemu_chr_fe_deinit(&chr_be, false); + + /* Finalize hub0 */ + qmp_chardev_remove("hub0", &error_abort); + + /* Finalize backend chardevs */ + qmp_chardev_remove("chr1", &error_abort); + qmp_chardev_remove("chr2", &error_abort); + +#ifndef _WIN32 + /* + * Create 3 backend chardevs to simulate EAGAIN and watcher. + * Mainly copied from char_pipe_test(). + * 1. Create 2 ringbuf backend chardevs + * 2. Create 1 pipe backend chardev + * 3. Create 1 frontend + * 4. Create hub which refers 2 backend chardevs + * 5. Attach hub to a frontend + * 6. Perform data aggregation and check watcher + * 7. Finilize frontend, hub and backend chardevs in correct order + */ + { + gchar *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); + gchar *in, *out, *pipe = g_build_filename(tmp_path, "pipe", NULL); + Chardev *chr3; + int fd, len; + char buf[128]; + + in = g_strdup_printf("%s.in", pipe); + if (mkfifo(in, 0600) < 0) { + abort(); + } + out = g_strdup_printf("%s.out", pipe); + if (mkfifo(out, 0600) < 0) { + abort(); + } + + /* Create first chardev */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr1", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", stringify(RB_SIZE), &error_abort); + chr1 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr1); + qemu_opts_del(opts); + + /* Create second chardev */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr2", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", stringify(RB_SIZE), &error_abort); + chr2 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr2); + qemu_opts_del(opts); + + /* Create third chardev */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr3", + 1, &error_abort); + qemu_opt_set(opts, "backend", "pipe", &error_abort); + qemu_opt_set(opts, "path", pipe, &error_abort); + chr3 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr3); + + /* Create hub0 and refer 3 backend chardevs */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + qemu_opt_set(opts, "backend", "hub", &error_abort); + qemu_opt_set(opts, "chardevs.0", "chr1", &error_abort); + qemu_opt_set(opts, "chardevs.1", "chr2", &error_abort); + qemu_opt_set(opts, "chardevs.2", "chr3", &error_abort); + hub = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(hub); + qemu_opts_del(opts); + + /* Attach hub to a frontend */ + qemu_chr_fe_init(&chr_be, hub, &error_abort); + qemu_chr_fe_set_handlers(&chr_be, + fe_can_read, + fe_read, + fe_event, + NULL, + &h, + NULL, true); + + /* Write to frontend, chr_be */ + ret = qemu_chr_fe_write(&chr_be, (void *)"thisis", 6); + g_assert_cmpint(ret, ==, 6); + + data = qmp_ringbuf_read("chr1", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 6); + g_assert_cmpstr(data, ==, "thisis"); + g_free(data); + + data = qmp_ringbuf_read("chr2", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 6); + g_assert_cmpstr(data, ==, "thisis"); + g_free(data); + + fd = open(out, O_RDWR); + ret = read(fd, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, 6); + buf[ret] = 0; + g_assert_cmpstr(buf, ==, "thisis"); + close(fd); + + /* Add watch. 0 indicates no watches if nothing to wait for */ + ret = qemu_chr_fe_add_watch(&chr_be, G_IO_OUT | G_IO_HUP, + NULL, NULL); + g_assert_cmpint(ret, ==, 0); + + /* + * Write to frontend, chr_be, until EAGAIN. Make sure length is + * power of two to fit nicely the whole pipe buffer. + */ + len = 0; + while ((ret = qemu_chr_fe_write(&chr_be, (void *)"thisisit", 8)) + != -1) { + len += ret; + } + g_assert_cmpint(errno, ==, EAGAIN); + + /* Further all writes should cause EAGAIN */ + ret = qemu_chr_fe_write(&chr_be, (void *)"b", 1); + g_assert_cmpint(ret, ==, -1); + g_assert_cmpint(errno, ==, EAGAIN); + + /* + * Add watch. Non 0 indicates we have a blocked chardev, which + * can wakes us up when write is possible. + */ + ret = qemu_chr_fe_add_watch(&chr_be, G_IO_OUT | G_IO_HUP, + NULL, NULL); + g_assert_cmpint(ret, !=, 0); + g_source_remove(ret); + + /* Drain pipe and ring buffers */ + fd = open(out, O_RDWR); + while ((ret = read(fd, buf, MIN(sizeof(buf), len))) != -1 && len > 0) { + len -= ret; + } + close(fd); + + data = qmp_ringbuf_read("chr1", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 128); + g_free(data); + + data = qmp_ringbuf_read("chr2", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 128); + g_free(data); + + /* + * Now we are good to go, first repeat "lost" sequence, which + * was already consumed and drained by the ring buffers, but + * pipe have not recieved that yet. + */ + ret = qemu_chr_fe_write(&chr_be, (void *)"thisisit", 8); + g_assert_cmpint(ret, ==, 8); + + ret = qemu_chr_fe_write(&chr_be, (void *)"streamisrestored", 16); + g_assert_cmpint(ret, ==, 16); + + data = qmp_ringbuf_read("chr1", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 16); + /* Only last 16 bytes, see big comment above */ + g_assert_cmpstr(data, ==, "streamisrestored"); + g_free(data); + + data = qmp_ringbuf_read("chr2", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 16); + /* Only last 16 bytes, see big comment above */ + g_assert_cmpstr(data, ==, "streamisrestored"); + g_free(data); + + fd = open(out, O_RDWR); + ret = read(fd, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, 24); + buf[ret] = 0; + /* Both 8 and 16 bytes */ + g_assert_cmpstr(buf, ==, "thisisitstreamisrestored"); + close(fd); + + g_free(in); + g_free(out); + g_free(tmp_path); + g_free(pipe); + + /* Finalize frontend */ + qemu_chr_fe_deinit(&chr_be, false); + + /* Finalize hub0 */ + qmp_chardev_remove("hub0", &error_abort); + + /* Finalize backend chardevs */ + qmp_chardev_remove("chr1", &error_abort); + qmp_chardev_remove("chr2", &error_abort); + qmp_chardev_remove("chr3", &error_abort); + } +#endif +} static void websock_server_read(void *opaque, const uint8_t *buf, int size) { @@ -556,7 +975,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; @@ -574,7 +993,7 @@ static void char_udp_test_internal(Chardev *reuse_chr, int sock) struct sockaddr_in other; SocketIdleData d = { 0, }; Chardev *chr; - CharBackend *be; + CharBackend stack_be, *be = &stack_be; socklen_t alen = sizeof(other); int ret; char buf[10]; @@ -590,7 +1009,6 @@ static void char_udp_test_internal(Chardev *reuse_chr, int sock) chr = qemu_chr_new("client", tmp, NULL); g_assert_nonnull(chr); - be = g_alloca(sizeof(CharBackend)); qemu_chr_fe_init(be, chr, &error_abort); } @@ -1203,6 +1621,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) { @@ -1383,7 +1825,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); @@ -1461,6 +1903,7 @@ int main(int argc, char **argv) g_test_add_func("/char/invalid", char_invalid_test); g_test_add_func("/char/ringbuf", char_ringbuf_test); g_test_add_func("/char/mux", char_mux_test); + g_test_add_func("/char/hub", char_hub_test); #ifdef _WIN32 g_test_add_func("/char/console/subprocess", char_console_test_subprocess); g_test_add_func("/char/console", char_console_test); @@ -1499,18 +1942,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, \ @@ -1544,6 +1987,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 b0d21d673a0..49d4d9b2512 100644 --- a/tests/unit/test-coroutine.c +++ b/tests/unit/test-coroutine.c @@ -195,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) { @@ -206,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) @@ -218,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) @@ -226,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); @@ -235,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); } @@ -645,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 00a7c180fd6..45e9046bf63 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 4f1f4214dd2..53c2211ba8e 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 347cd5f3d79..3ac7f17b2a0 100644 --- a/tests/unit/test-crypto-block.c +++ b/tests/unit/test-crypto-block.c @@ -39,14 +39,14 @@ #endif static QCryptoBlockCreateOptions qcow_create_opts = { - .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, + .format = QCRYPTO_BLOCK_FORMAT_QCOW, .u.qcow = { .key_secret = (char *)"sec0", }, }; static QCryptoBlockOpenOptions qcow_open_opts = { - .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, + .format = QCRYPTO_BLOCK_FORMAT_QCOW, .u.qcow = { .key_secret = (char *)"sec0", }, @@ -55,7 +55,7 @@ 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 = { .key_secret = (char *)"sec0", }, @@ -64,7 +64,7 @@ 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 = { .key_secret = (char *)"sec0", }, @@ -73,33 +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 = { .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 = { .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 */ @@ -112,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[] = { @@ -128,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 { @@ -141,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, }, @@ -156,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, }, @@ -171,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, }, @@ -283,6 +283,7 @@ static void test_block(gconstpointer opaque) test_block_init_func, test_block_write_func, &header, + 0, &error_abort); g_assert(blk); @@ -302,7 +303,6 @@ static void test_block(gconstpointer opaque) test_block_read_func, &header, 0, - 1, NULL); g_assert(blk == NULL); @@ -311,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); @@ -326,7 +325,6 @@ static void test_block(gconstpointer opaque) test_block_read_func, &header, 0, - 1, &error_abort); g_assert(blk); @@ -362,6 +360,7 @@ test_luks_bad_header(gconstpointer data) test_block_init_func, test_block_write_func, &buf, + 0, &error_abort); g_assert(blk); @@ -382,7 +381,6 @@ test_luks_bad_header(gconstpointer data) test_block_read_func, &buf, 0, - 1, &err); g_assert(!blk); g_assert(err); @@ -574,8 +572,15 @@ 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)) { + g_printerr("# skip unsupported %s\n", + QCryptoHashAlgo_str(test_data[i].hash_alg)); + continue; + } + if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_128, + QCRYPTO_CIPHER_MODE_CBC)) { + g_printerr("# skip unsupported aes-128:cbc\n"); continue; } if (!test_data[i].slow || diff --git a/tests/unit/test-crypto-cipher.c b/tests/unit/test-crypto-cipher.c index d9d9d078ff1..1331d558cf2 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,14 +821,23 @@ 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)); } } - g_test_add_func("/crypto/cipher/null-iv", - test_cipher_null_iv); + if (qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_256, + QCRYPTO_CIPHER_MODE_CBC)) { + g_test_add_func("/crypto/cipher/null-iv", + test_cipher_null_iv); - g_test_add_func("/crypto/cipher/short-plaintext", - test_cipher_short_plaintext); + g_test_add_func("/crypto/cipher/short-plaintext", + test_cipher_short_plaintext); + } else { + g_printerr("# skip unsupported aes-256:cbc\n"); + } return g_test_run(); } diff --git a/tests/unit/test-crypto-hash.c b/tests/unit/test-crypto-hash.c index 1f4abb822b6..8fee1593f94 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 23eb724d942..20c60eb9d8b 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 29630ed348a..bc9ffe34e74 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 43c417f6b43..ddb7244e213 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 ffd13ff70e8..fc32a01747b 100644 --- a/tests/unit/test-crypto-secret.c +++ b/tests/unit/test-crypto-secret.c @@ -22,6 +22,7 @@ #include "crypto/init.h" #include "crypto/secret.h" +#include "crypto/cipher.h" #include "qapi/error.h" #include "qemu/module.h" #if defined(CONFIG_KEYUTILS) && defined(CONFIG_SECRET_KEYRING) @@ -597,18 +598,21 @@ int main(int argc, char **argv) g_test_add_func("/crypto/secret/conv/utf8/base64", test_secret_conv_utf8_base64); - g_test_add_func("/crypto/secret/crypt/raw", - test_secret_crypt_raw); - g_test_add_func("/crypto/secret/crypt/base64", - test_secret_crypt_base64); - g_test_add_func("/crypto/secret/crypt/shortkey", - test_secret_crypt_short_key); - g_test_add_func("/crypto/secret/crypt/shortiv", - test_secret_crypt_short_iv); - g_test_add_func("/crypto/secret/crypt/missingiv", - test_secret_crypt_missing_iv); - g_test_add_func("/crypto/secret/crypt/badiv", - test_secret_crypt_bad_iv); + if (qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_128, + QCRYPTO_CIPHER_MODE_CBC)) { + g_test_add_func("/crypto/secret/crypt/raw", + test_secret_crypt_raw); + g_test_add_func("/crypto/secret/crypt/base64", + test_secret_crypt_base64); + g_test_add_func("/crypto/secret/crypt/shortkey", + test_secret_crypt_short_key); + g_test_add_func("/crypto/secret/crypt/shortiv", + test_secret_crypt_short_iv); + g_test_add_func("/crypto/secret/crypt/missingiv", + test_secret_crypt_missing_iv); + g_test_add_func("/crypto/secret/crypt/badiv", + test_secret_crypt_bad_iv); + } return g_test_run(); } diff --git a/tests/unit/test-crypto-tlssession.c b/tests/unit/test-crypto-tlssession.c index b12e7b68792..554054e9344 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( @@ -136,8 +158,7 @@ static void test_crypto_tls_session_psk(void) rv = qcrypto_tls_session_handshake(serverSess, &error_abort); g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(serverSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { serverShake = true; } } @@ -145,8 +166,7 @@ static void test_crypto_tls_session_psk(void) rv = qcrypto_tls_session_handshake(clientSess, &error_abort); g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(clientSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { clientShake = true; } } @@ -330,8 +350,7 @@ static void test_crypto_tls_session_x509(const void *opaque) rv = qcrypto_tls_session_handshake(serverSess, &error_abort); g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(serverSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { serverShake = true; } } @@ -339,8 +358,7 @@ static void test_crypto_tls_session_x509(const void *opaque) rv = qcrypto_tls_session_handshake(clientSess, &error_abort); g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(clientSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { clientShake = true; } } diff --git a/tests/unit/test-fifo.c b/tests/unit/test-fifo.c new file mode 100644 index 00000000000..14153c41fa0 --- /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-forward-visitor.c b/tests/unit/test-forward-visitor.c index eea8ffc0720..aad1c89f13d 100644 --- a/tests/unit/test-forward-visitor.c +++ b/tests/unit/test-forward-visitor.c @@ -12,8 +12,8 @@ #include "qapi/forward-visitor.h" #include "qapi/qobject-input-visitor.h" #include "qapi/error.h" -#include "qapi/qmp/qobject.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qobject.h" +#include "qobject/qdict.h" #include "test-qapi-visit.h" #include "qemu/keyval.h" diff --git a/tests/unit/test-image-locking.c b/tests/unit/test-image-locking.c index 2624cec6a04..019195f8992 100644 --- a/tests/unit/test-image-locking.c +++ b/tests/unit/test-image-locking.c @@ -26,9 +26,9 @@ #include "qemu/osdep.h" #include "block/block.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/main-loop.h" static BlockBackend *open_image(const char *path, diff --git a/tests/unit/test-io-channel-socket.c b/tests/unit/test-io-channel-socket.c index b964bb202db..dc7be96e9cd 100644 --- a/tests/unit/test-io-channel-socket.c +++ b/tests/unit/test-io-channel-socket.c @@ -506,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" @@ -519,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-task.c b/tests/unit/test-io-task.c index 953a50ae66e..115dba89702 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 6f7623d3107..75bc3be0057 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-keyval.c b/tests/unit/test-keyval.c index 4dc52c7a1a8..c6e8f4fe370 100644 --- a/tests/unit/test-keyval.c +++ b/tests/unit/test-keyval.c @@ -13,9 +13,9 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "test-qapi-visit.h" #include "qemu/cutils.h" diff --git a/tests/unit/test-nested-aio-poll.c b/tests/unit/test-nested-aio-poll.c index db33742af3b..d8fd92c43b3 100644 --- a/tests/unit/test-nested-aio-poll.c +++ b/tests/unit/test-nested-aio-poll.c @@ -30,19 +30,16 @@ typedef struct { static void io_read(EventNotifier *notifier) { - fprintf(stderr, "%s %p\n", __func__, notifier); event_notifier_test_and_clear(notifier); } static bool io_poll_true(void *opaque) { - fprintf(stderr, "%s %p\n", __func__, opaque); return true; } static bool io_poll_false(void *opaque) { - fprintf(stderr, "%s %p\n", __func__, opaque); return false; } @@ -50,8 +47,6 @@ static void io_poll_ready(EventNotifier *notifier) { TestData *td = container_of(notifier, TestData, poll_notifier); - fprintf(stderr, "> %s\n", __func__); - g_assert(!td->nested); td->nested = true; @@ -62,8 +57,6 @@ static void io_poll_ready(EventNotifier *notifier) g_assert(aio_poll(td->ctx, true)); td->nested = false; - - fprintf(stderr, "< %s\n", __func__); } /* dummy_notifier never triggers */ diff --git a/tests/unit/test-qdev-global-props.c b/tests/unit/test-qdev-global-props.c index c8862cac5fe..33062762a67 100644 --- a/tests/unit/test-qdev-global-props.c +++ b/tests/unit/test-qdev-global-props.c @@ -46,13 +46,12 @@ struct MyType { uint32_t prop2; }; -static Property static_props[] = { +static const Property static_props[] = { DEFINE_PROP_UINT32("prop1", MyType, prop1, PROP_DEFAULT), DEFINE_PROP_UINT32("prop2", MyType, prop2, PROP_DEFAULT), - DEFINE_PROP_END_OF_LIST() }; -static void static_prop_class_init(ObjectClass *oc, void *data) +static void static_prop_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -72,6 +71,26 @@ static const TypeInfo subclass_type = { .parent = TYPE_STATIC_PROPS, }; +/* + * Initialize a fake machine, being prepared for future tests. + * + * All the tests later (even if to be run in subprocesses.. which will + * inherit the global states of the parent process) will try to create qdev + * and realize the device. + * + * Realization of such anonymous qdev (with no parent object) requires both + * the machine object and its "unattached" container to be at least present. + */ +static void test_init_machine(void) +{ + /* This is a fake machine - it doesn't need to be a machine object */ + Object *machine = object_property_add_new_container( + object_get_root(), "machine"); + + /* This container must exist for anonymous qdevs to realize() */ + object_property_add_new_container(machine, "unattached"); +} + /* Test simple static property setting to default value */ static void test_static_prop_subprocess(void) { @@ -158,7 +177,7 @@ static void dynamic_instance_init(Object *obj) NULL, NULL); } -static void dynamic_class_init(ObjectClass *oc, void *data) +static void dynamic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -174,7 +193,7 @@ static const TypeInfo dynamic_prop_type = { .class_init = dynamic_class_init, }; -static void hotplug_class_init(ObjectClass *oc, void *data) +static void hotplug_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -190,7 +209,7 @@ static const TypeInfo hotplug_type = { .class_init = hotplug_class_init, }; -static void nohotplug_class_init(ObjectClass *oc, void *data) +static void nohotplug_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -295,6 +314,8 @@ int main(int argc, char **argv) type_register_static(&nohotplug_type); type_register_static(&nondevice_type); + test_init_machine(); + g_test_add_func("/qdev/properties/static/default/subprocess", test_static_prop_subprocess); g_test_add_func("/qdev/properties/static/default", diff --git a/tests/unit/test-qemu-opts.c b/tests/unit/test-qemu-opts.c index 828d40e9283..8d03a69f7cd 100644 --- a/tests/unit/test-qemu-opts.c +++ b/tests/unit/test-qemu-opts.c @@ -12,8 +12,8 @@ #include "qemu/option.h" #include "qemu/option_int.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qemu/config-file.h" diff --git a/tests/unit/test-qga.c b/tests/unit/test-qga.c index 671e83cb86c..587e30c7e48 100644 --- a/tests/unit/test-qga.c +++ b/tests/unit/test-qga.c @@ -5,8 +5,8 @@ #include #include "../qtest/libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" typedef struct { char *test_dir; @@ -332,6 +332,22 @@ static void test_qga_get_fsinfo(gconstpointer fix) } } +static void test_qga_get_load(gconstpointer fix) +{ + const TestFixture *fixture = fix; + g_autoptr(QDict) ret = NULL; + QDict *load; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-load'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + load = qdict_get_qdict(ret, "return"); + g_assert(qdict_haskey(load, "load1m")); + g_assert(qdict_haskey(load, "load5m")); + g_assert(qdict_haskey(load, "load15m")); +} + static void test_qga_get_memory_block_info(gconstpointer fix) { const TestFixture *fixture = fix; @@ -822,7 +838,7 @@ static void test_qga_guest_exec(gconstpointer fix) /* exec 'echo foo bar' */ ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" - " 'path': '/bin/echo', 'arg': [ '-n', '\" test_str \"' ]," + " 'path': 'echo', 'arg': [ '-n', '\" test_str \"' ]," " 'capture-output': true } }"); g_assert_nonnull(ret); qmp_assert_no_error(ret); @@ -883,7 +899,7 @@ static void test_qga_guest_exec_separated(gconstpointer fix) /* exec 'echo foo bar' */ ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" - " 'path': '/bin/bash'," + " '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); @@ -924,7 +940,7 @@ static void test_qga_guest_exec_merged(gconstpointer fix) /* exec 'echo foo bar' */ ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" - " 'path': '/bin/bash'," + " '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); @@ -1105,6 +1121,7 @@ int main(int argc, char **argv) g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus); } g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo); + g_test_add_data_func("/qga/get-load", &fix, test_qga_get_load); g_test_add_data_func("/qga/get-memory-block-info", &fix, test_qga_get_memory_block_info); g_test_add_data_func("/qga/get-memory-blocks", &fix, diff --git a/tests/unit/test-qgraph.c b/tests/unit/test-qgraph.c index 334c76c8e71..ca1d60fc189 100644 --- a/tests/unit/test-qgraph.c +++ b/tests/unit/test-qgraph.c @@ -44,7 +44,6 @@ static void *driverfunct(void *obj, QGuestAllocator *machine, void *arg) static void testfunct(void *obj, void *arg, QGuestAllocator *alloc) { - return; } static void check_interface(const char *interface) diff --git a/tests/unit/test-qmp-cmds.c b/tests/unit/test-qmp-cmds.c index 6d52b4e5d81..ad538868863 100644 --- a/tests/unit/test-qmp-cmds.c +++ b/tests/unit/test-qmp-cmds.c @@ -1,9 +1,9 @@ #include "qemu/osdep.h" #include "qapi/compat-policy.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qapi/error.h" #include "qapi/qobject-input-visitor.h" #include "tests/test-qapi-types.h" diff --git a/tests/unit/test-qmp-event.c b/tests/unit/test-qmp-event.c index 3626d2372f1..2aac27163d8 100644 --- a/tests/unit/test-qmp-event.c +++ b/tests/unit/test-qmp-event.c @@ -15,28 +15,24 @@ #include "qapi/compat-policy.h" #include "qapi/error.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qapi/qmp-event.h" #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; -} - -static void event_prepare(TestEventData *data, - const void *unused) -{ - /* 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); + g_assert(qobject_is_equal(QOBJECT(d), QOBJECT(expected_event))); + qobject_unref(expected_event); + expected_event = NULL; } -static void event_test_add(const char *testpath, - void (*test_func)(TestEventData *data, - const void *user_data)) +static void test_event_a(void) { - 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, &b, "test2"); - g_assert(data->emitted); - qobject_unref(data->expect); + 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", NULL, true, ENUM_ONE_VALUE3); - g_assert(data->emitted); - qobject_unref(data->expect); + 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 024e26c49e9..84bdcdf702e 100644 --- a/tests/unit/test-qobject-input-visitor.c +++ b/tests/unit/test-qobject-input-visitor.c @@ -17,12 +17,12 @@ #include "qapi/qapi-visit-introspect.h" #include "qapi/qobject-input-visitor.h" #include "test-qapi-visit.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" +#include "qobject/qjson.h" #include "test-qapi-introspect.h" #include "qapi/qapi-introspect.h" @@ -720,7 +720,7 @@ static void test_visitor_in_union_in_union(TestInputVisitorData *data, visit_type_TestUnionInUnion(v, NULL, &tmp, &error_abort); g_assert_cmpint(tmp->type, ==, TEST_UNION_ENUM_VALUE_A); - g_assert_cmpint(tmp->u.value_a.type_a, ==, TEST_UNION_ENUMA_VALUE_A1); + g_assert_cmpint(tmp->u.value_a.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); @@ -734,7 +734,7 @@ static void test_visitor_in_union_in_union(TestInputVisitorData *data, visit_type_TestUnionInUnion(v, NULL, &tmp, &error_abort); g_assert_cmpint(tmp->type, ==, TEST_UNION_ENUM_VALUE_A); - g_assert_cmpint(tmp->u.value_a.type_a, ==, TEST_UNION_ENUMA_VALUE_A2); + g_assert_cmpint(tmp->u.value_a.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); diff --git a/tests/unit/test-qobject-output-visitor.c b/tests/unit/test-qobject-output-visitor.c index 1535b3ad17b..407ab9ed505 100644 --- a/tests/unit/test-qobject-output-visitor.c +++ b/tests/unit/test-qobject-output-visitor.c @@ -15,12 +15,12 @@ #include "qapi/error.h" #include "qapi/qobject-output-visitor.h" #include "test-qapi-visit.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" typedef struct TestOutputVisitorData { Visitor *ov; @@ -359,7 +359,7 @@ static void test_visitor_out_union_in_union(TestOutputVisitorData *data, TestUnionInUnion *tmp = g_new0(TestUnionInUnion, 1); tmp->type = TEST_UNION_ENUM_VALUE_A; - tmp->u.value_a.type_a = TEST_UNION_ENUMA_VALUE_A1; + tmp->u.value_a.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"); @@ -377,7 +377,7 @@ static void test_visitor_out_union_in_union(TestOutputVisitorData *data, visitor_reset(data); tmp = g_new0(TestUnionInUnion, 1); tmp->type = TEST_UNION_ENUM_VALUE_A; - tmp->u.value_a.type_a = TEST_UNION_ENUMA_VALUE_A2; + tmp->u.value_a.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; diff --git a/tests/unit/test-replication.c b/tests/unit/test-replication.c index afff908d77a..3aa98e6f565 100644 --- a/tests/unit/test-replication.c +++ b/tests/unit/test-replication.c @@ -11,13 +11,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/option.h" #include "qemu/main-loop.h" #include "block/replication.h" #include "block/block_int.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #define IMG_SIZE (64 * 1024 * 1024) @@ -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 00000000000..4de2d042d14 --- /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 "system/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 f02c79cafd1..71d40834391 100644 --- a/tests/unit/test-seccomp.c +++ b/tests/unit/test-seccomp.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "qemu/config-file.h" #include "qemu/option.h" -#include "sysemu/seccomp.h" +#include "system/seccomp.h" #include "qapi/error.h" #include "qemu/module.h" @@ -229,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) { @@ -256,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 fdc39a846ca..326045ecbb3 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,9 +912,19 @@ 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) +static void machine_base_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -529,27 +934,78 @@ static void machine_base_class_init(ObjectClass *oc, void *data) mc->name = g_strdup(SMP_MACHINE_NAME); } -static void machine_generic_invalid_class_init(ObjectClass *oc, void *data) +static void machine_generic_invalid_class_init(ObjectClass *oc, + const 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, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->smp_props.modules_supported = true; +} + +static void machine_with_dies_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->smp_props.dies_supported = true; } -static void machine_with_dies_class_init(ObjectClass *oc, void *data) +static void machine_with_modules_dies_class_init(ObjectClass *oc, + const 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) +static void machine_with_clusters_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->smp_props.clusters_supported = true; +} + +static void machine_with_books_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->smp_props.books_supported = true; +} + +static void machine_with_drawers_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); + mc->smp_props.drawers_supported = true; +} + +static void machine_with_drawers_books_class_init(ObjectClass *oc, + const 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, const 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) @@ -566,11 +1022,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 +1046,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 +1146,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 +1257,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 +1530,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 +1579,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 1483e53473d..33407b595d3 100644 --- a/tests/unit/test-thread-pool.c +++ b/tests/unit/test-thread-pool.c @@ -43,10 +43,10 @@ static void done_cb(void *opaque, int ret) active--; } -static void test_submit(void) +static void test_submit_no_complete(void) { WorkerTestData data = { .n = 0 }; - thread_pool_submit(worker_cb, &data); + thread_pool_submit_aio(worker_cb, &data, NULL, NULL); while (data.n == 0) { aio_poll(ctx, true); } @@ -236,7 +236,7 @@ int main(int argc, char **argv) ctx = qemu_get_current_aio_context(); g_test_init(&argc, &argv, NULL); - g_test_add_func("/thread-pool/submit", test_submit); + g_test_add_func("/thread-pool/submit-no-complete", test_submit_no_complete); g_test_add_func("/thread-pool/submit-aio", test_submit_aio); g_test_add_func("/thread-pool/submit-co", test_submit_co); g_test_add_func("/thread-pool/submit-many", test_submit_many); diff --git a/tests/unit/test-throttle.c b/tests/unit/test-throttle.c index dc8739c1d6d..dfa61c75ea4 100644 --- a/tests/unit/test-throttle.c +++ b/tests/unit/test-throttle.c @@ -21,7 +21,7 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "block/throttle-groups.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" static AioContext *ctx; static LeakyBucket bkt; @@ -127,13 +127,13 @@ 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 milliseconds of burst * because bkt.level has been leaking during this time. @@ -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]); } } @@ -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-timed-average.c b/tests/unit/test-timed-average.c index 82c92500df8..747ed1ee60e 100644 --- a/tests/unit/test-timed-average.c +++ b/tests/unit/test-timed-average.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "qemu/timed-average.h" /* This is the clock for QEMU_CLOCK_VIRTUAL */ diff --git a/tests/unit/test-util-filemonitor.c b/tests/unit/test-util-filemonitor.c index a22de275955..02e67fc96ac 100644 --- a/tests/unit/test-util-filemonitor.c +++ b/tests/unit/test-util-filemonitor.c @@ -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 63909ccb2b5..ee66d727c38 100644 --- a/tests/unit/test-util-sockets.c +++ b/tests/unit/test-util-sockets.c @@ -326,11 +326,226 @@ 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); } #endif /* CONFIG_LINUX */ +static void inet_parse_test_helper(const char *str, + InetSocketAddress *exp_addr, bool success) +{ + InetSocketAddress addr; + Error *error = NULL; + + int rc = inet_parse(&addr, str, &error); + + if (success) { + if (error) { + error_report_err(error); + } + g_assert_cmpint(rc, ==, 0); + } else { + error_free(error); + g_assert_cmpint(rc, <, 0); + } + if (exp_addr != NULL) { + g_assert_cmpstr(addr.host, ==, exp_addr->host); + g_assert_cmpstr(addr.port, ==, exp_addr->port); + /* Own members: */ + g_assert_cmpint(addr.has_numeric, ==, exp_addr->has_numeric); + g_assert_cmpint(addr.numeric, ==, exp_addr->numeric); + g_assert_cmpint(addr.has_to, ==, exp_addr->has_to); + g_assert_cmpint(addr.to, ==, exp_addr->to); + g_assert_cmpint(addr.has_ipv4, ==, exp_addr->has_ipv4); + g_assert_cmpint(addr.ipv4, ==, exp_addr->ipv4); + g_assert_cmpint(addr.has_ipv6, ==, exp_addr->has_ipv6); + g_assert_cmpint(addr.ipv6, ==, exp_addr->ipv6); + g_assert_cmpint(addr.has_keep_alive, ==, exp_addr->has_keep_alive); + g_assert_cmpint(addr.keep_alive, ==, exp_addr->keep_alive); +#ifdef HAVE_TCP_KEEPCNT + g_assert_cmpint(addr.has_keep_alive_count, ==, + exp_addr->has_keep_alive_count); + g_assert_cmpint(addr.keep_alive_count, ==, + exp_addr->keep_alive_count); +#endif +#ifdef HAVE_TCP_KEEPIDLE + g_assert_cmpint(addr.has_keep_alive_idle, ==, + exp_addr->has_keep_alive_idle); + g_assert_cmpint(addr.keep_alive_idle, ==, + exp_addr->keep_alive_idle); +#endif +#ifdef HAVE_TCP_KEEPINTVL + g_assert_cmpint(addr.has_keep_alive_interval, ==, + exp_addr->has_keep_alive_interval); + g_assert_cmpint(addr.keep_alive_interval, ==, + exp_addr->keep_alive_interval); +#endif +#ifdef HAVE_IPPROTO_MPTCP + g_assert_cmpint(addr.has_mptcp, ==, exp_addr->has_mptcp); + g_assert_cmpint(addr.mptcp, ==, exp_addr->mptcp); +#endif + } + + g_free(addr.host); + g_free(addr.port); +} + +static void test_inet_parse_nohost_good(void) +{ + char host[] = ""; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + }; + inet_parse_test_helper(":5000", &exp_addr, true); +} + +static void test_inet_parse_empty_bad(void) +{ + inet_parse_test_helper("", NULL, false); +} + +static void test_inet_parse_only_colon_bad(void) +{ + inet_parse_test_helper(":", NULL, false); +} + +static void test_inet_parse_ipv4_good(void) +{ + char host[] = "127.0.0.1"; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + }; + inet_parse_test_helper("127.0.0.1:5000", &exp_addr, true); +} + +static void test_inet_parse_ipv4_noport_bad(void) +{ + inet_parse_test_helper("127.0.0.1", NULL, false); +} + +static void test_inet_parse_ipv6_good(void) +{ + char host[] = "::1"; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + }; + inet_parse_test_helper("[::1]:5000", &exp_addr, true); +} + +static void test_inet_parse_ipv6_noend_bad(void) +{ + inet_parse_test_helper("[::1", NULL, false); +} + +static void test_inet_parse_ipv6_noport_bad(void) +{ + inet_parse_test_helper("[::1]:", NULL, false); +} + +static void test_inet_parse_ipv6_empty_bad(void) +{ + inet_parse_test_helper("[]:5000", NULL, false); +} + +static void test_inet_parse_hostname_good(void) +{ + char host[] = "localhost"; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + }; + inet_parse_test_helper("localhost:5000", &exp_addr, true); +} + +static void test_inet_parse_all_options_good(void) +{ + char host[] = "::1"; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + .has_numeric = true, + .numeric = true, + .has_to = true, + .to = 5006, + .has_ipv4 = true, + .ipv4 = false, + .has_ipv6 = true, + .ipv6 = true, + .has_keep_alive = true, + .keep_alive = true, +#ifdef HAVE_TCP_KEEPCNT + .has_keep_alive_count = true, + .keep_alive_count = 10, +#endif +#ifdef HAVE_TCP_KEEPIDLE + .has_keep_alive_idle = true, + .keep_alive_idle = 60, +#endif +#ifdef HAVE_TCP_KEEPINTVL + .has_keep_alive_interval = true, + .keep_alive_interval = 30, +#endif +#ifdef HAVE_IPPROTO_MPTCP + .has_mptcp = true, + .mptcp = false, +#endif + }; + inet_parse_test_helper( + "[::1]:5000,numeric=on,to=5006,ipv4=off,ipv6=on,keep-alive=on" +#ifdef HAVE_TCP_KEEPCNT + ",keep-alive-count=10" +#endif +#ifdef HAVE_TCP_KEEPIDLE + ",keep-alive-idle=60" +#endif +#ifdef HAVE_TCP_KEEPINTVL + ",keep-alive-interval=30" +#endif +#ifdef HAVE_IPPROTO_MPTCP + ",mptcp=off" +#endif + , &exp_addr, true); +} + +static void test_inet_parse_all_implicit_bool_good(void) +{ + char host[] = "::1"; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + .has_numeric = true, + .numeric = true, + .has_to = true, + .to = 5006, + .has_ipv4 = true, + .ipv4 = true, + .has_ipv6 = true, + .ipv6 = true, + .has_keep_alive = true, + .keep_alive = true, +#ifdef HAVE_IPPROTO_MPTCP + .has_mptcp = true, + .mptcp = true, +#endif + }; + inet_parse_test_helper( + "[::1]:5000,numeric,to=5006,ipv4,ipv6,keep-alive" +#ifdef HAVE_IPPROTO_MPTCP + ",mptcp" +#endif + , &exp_addr, true); +} + int main(int argc, char **argv) { bool has_ipv4, has_ipv6; @@ -376,6 +591,31 @@ int main(int argc, char **argv) test_socket_unix_abstract); #endif + g_test_add_func("/util/socket/inet-parse/nohost-good", + test_inet_parse_nohost_good); + g_test_add_func("/util/socket/inet-parse/empty-bad", + test_inet_parse_empty_bad); + g_test_add_func("/util/socket/inet-parse/only-colon-bad", + test_inet_parse_only_colon_bad); + g_test_add_func("/util/socket/inet-parse/ipv4-good", + test_inet_parse_ipv4_good); + g_test_add_func("/util/socket/inet-parse/ipv4-noport-bad", + test_inet_parse_ipv4_noport_bad); + g_test_add_func("/util/socket/inet-parse/ipv6-good", + test_inet_parse_ipv6_good); + g_test_add_func("/util/socket/inet-parse/ipv6-noend-bad", + test_inet_parse_ipv6_noend_bad); + g_test_add_func("/util/socket/inet-parse/ipv6-noport-bad", + test_inet_parse_ipv6_noport_bad); + g_test_add_func("/util/socket/inet-parse/ipv6-empty-bad", + test_inet_parse_ipv6_empty_bad); + g_test_add_func("/util/socket/inet-parse/hostname-good", + test_inet_parse_hostname_good); + g_test_add_func("/util/socket/inet-parse/all-options-good", + test_inet_parse_all_options_good); + g_test_add_func("/util/socket/inet-parse/all-bare-bool-good", + test_inet_parse_all_implicit_bool_good); + end: return g_test_run(); } diff --git a/tests/unit/test-uuid.c b/tests/unit/test-uuid.c index c111de5fc1b..739b91583cf 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 00000000000..a45ec52f421 --- /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 c2056c3eaa8..2d365999fc8 100644 --- a/tests/unit/test-visitor-serialization.c +++ b/tests/unit/test-visitor-serialization.c @@ -16,8 +16,8 @@ #include "test-qapi-visit.h" #include "qapi/error.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qjson.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "qapi/string-input-visitor.h" diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c index 0b7d5ecd683..63f28f26f45 100644 --- a/tests/unit/test-vmstate.c +++ b/tests/unit/test-vmstate.c @@ -24,7 +24,6 @@ #include "qemu/osdep.h" -#include "../migration/migration.h" #include "migration/vmstate.h" #include "migration/qemu-file-types.h" #include "../migration/qemu-file.h" @@ -197,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), @@ -299,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() } @@ -341,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. @@ -412,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), @@ -524,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() } @@ -542,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() @@ -630,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() @@ -685,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() @@ -696,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), @@ -821,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() \ @@ -839,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() \ @@ -915,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, @@ -940,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() } @@ -951,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), @@ -963,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), @@ -1414,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() } @@ -1424,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), @@ -1435,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 bfabc0403a1..00000000000 --- 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 00000000000..55b731ccae5 --- /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-xs-node.c b/tests/unit/test-xs-node.c index ac94e7ed6c2..2f447a73fb8 100644 --- a/tests/unit/test-xs-node.c +++ b/tests/unit/test-xs-node.c @@ -212,7 +212,7 @@ static void compare_tx(gpointer key, gpointer val, gpointer opaque) printf("Comparison failure in TX %u after serdes:\n", tx_id); dump_ref("Original", t1->root, 0); dump_ref("Deserialised", t2->root, 0); - g_assert(0); + g_assert_not_reached(); } g_assert(t1->nr_nodes == t2->nr_nodes); } @@ -257,7 +257,7 @@ static void check_serdes(XenstoreImplState *s) printf("Comparison failure in main tree after serdes:\n"); dump_ref("Original", s->root, 0); dump_ref("Deserialised", s2->root, 0); - g_assert(0); + g_assert_not_reached(); } nr_transactions1 = g_hash_table_size(s->transactions); diff --git a/tests/unit/test-yank.c b/tests/unit/test-yank.c index e6c036a64d9..4acfb2f3f62 100644 --- a/tests/unit/test-yank.c +++ b/tests/unit/test-yank.c @@ -14,7 +14,7 @@ #include "qemu/module.h" #include "qemu/option.h" #include "chardev/char-fe.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "qapi/qapi-commands-char.h" #include "qapi/qapi-types-char.h" diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include index bf12e0fa3c5..14188bba1c6 100644 --- a/tests/vm/Makefile.include +++ b/tests/vm/Makefile.include @@ -45,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" @@ -65,23 +64,24 @@ endif @echo " vm-boot-ssh- - Boot guest and login via ssh" @echo @echo "Special variables:" - @echo " BUILD_TARGET=foo - Override the build target" - @echo " DEBUG=1 - Enable verbose output on host and interactive debugging" - @echo ' EXTRA_CONFIGURE_OPTS="..." - Pass to configure step' - @echo " J=[0..9]* - Override the -jN parameter for make commands" - @echo " LOG_CONSOLE=1 - Log console to file in: ~/.cache/qemu-vm " - @echo " USE_TCG=1 - Use TCG for cross-arch images" - @echo " QEMU=/path/to/qemu - Change path to QEMU binary" + @echo " BUILD_TARGET=foo - Override the build target" + @echo " DEBUG=1 - Enable verbose output on host and interactive debugging" + @echo " ROOT_USER=1 - Login as root user for interactive shell" + @echo ' EXTRA_CONFIGURE_OPTS="..." - Pass to configure step' + @echo " J=[0..9]* - Override the -jN parameter for make commands" + @echo " LOG_CONSOLE=1 - Log console to file in: ~/.cache/qemu-vm " + @echo " USE_TCG=1 - Use TCG for cross-arch images" + @echo " QEMU=/path/to/qemu - Change path to QEMU binary" ifeq ($(HAVE_PYTHON_YAML),yes) - @echo " QEMU_CONFIG=/path/conf.yml - Change path to VM configuration .yml file." + @echo " QEMU_CONFIG=/path/conf.yml - Change path to VM configuration .yml file." else @echo " (install python3-yaml to enable support for yaml file to configure a VM.)" endif - @echo " See conf_example_*.yml for file format details." - @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 output on host and guest commands" + @echo " See conf_example_*.yml for file format details." + @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 output on host and guest commands" vm-build-all: $(addprefix vm-build-, $(IMAGES)) @@ -102,7 +102,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 $*") @@ -142,6 +142,6 @@ vm-boot-ssh-%: $(IMAGES_DIR)/%.img $(VM_VENV) $(if $(EFI_AARCH64),--efi-aarch64 $(EFI_AARCH64)) \ $(if $(LOG_CONSOLE),--log-console) \ --image "$<" \ - --interactive \ + $(if $(ROOT_USER),--interactive-root,-interactive) \ false, \ " VM-BOOT-SSH $*") || true diff --git a/tests/vm/README b/tests/vm/README index f9c04cc0e75..14ac323309e 100644 --- a/tests/vm/README +++ b/tests/vm/README @@ -1 +1 @@ -See docs/devel/testing.rst for help. +See docs/devel/testing/main.rst for help. diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index a97e23b0ce0..9e879e966a3 100644 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -83,7 +83,7 @@ class BaseVM(object): # command to halt the guest, can be overridden by subclasses poweroff = "poweroff" # Time to wait for shutdown to finish. - shutdown_timeout_default = 30 + shutdown_timeout_default = 90 # enable IPv6 networking ipv6 = True # This is the timeout on the wait for console bytes. @@ -312,8 +312,8 @@ def boot(self, img, extra_args=[]): 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" % \ @@ -331,8 +331,8 @@ def console_init(self, timeout = None): 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) @@ -423,6 +423,8 @@ def console_ssh_init(self, prompt, user, pw): 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) @@ -518,8 +520,7 @@ def get_qemu_path(arch, build_path=None): if "QEMU" in os.environ: qemu_path = os.environ["QEMU"] elif build_path: - qemu_path = os.path.join(build_path, arch + "-softmmu") - qemu_path = os.path.join(qemu_path, "qemu-system-" + arch) + qemu_path = os.path.join(build_path, "qemu-system-" + arch) else: # Default is to use system path for qemu. qemu_path = "qemu-system-" + arch @@ -530,7 +531,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): @@ -604,15 +605,18 @@ def get_default_jobs(): 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. ") parser.add_argument("--source-path", default=None, help="Path of source directory, "\ "for finding additional files. ") - parser.add_argument("--interactive", "-I", action="store_true", - help="Interactively run command") + int_ops = parser.add_mutually_exclusive_group() + int_ops.add_argument("--interactive", "-I", action="store_true", + help="Interactively run command") + int_ops.add_argument("--interactive-root", action="store_true", + help="Interactively run command as root") parser.add_argument("--snapshot", "-s", action="store_true", help="run tests with a snapshot") parser.add_argument("--genisoimage", default="genisoimage", @@ -644,9 +648,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) @@ -674,6 +678,8 @@ def main(vmcls, config=None): exitcode = 3 if args.interactive: vm.ssh() + elif args.interactive_root: + vm.ssh_root() if not args.snapshot: vm.graceful_shutdown() diff --git a/tests/vm/centos b/tests/vm/centos deleted file mode 100755 index 097a9ca14d3..00000000000 --- 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 3f58de1e64b..fcf9e08c873 100755 --- a/tests/vm/centos.aarch64 +++ b/tests/vm/centos.aarch64 @@ -25,9 +25,9 @@ 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 "\ + "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, " ), @@ -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 ac51376c82b..74b3b1e520a 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -28,8 +28,8 @@ class FreeBSDVM(basevm.BaseVM): name = "freebsd" arch = "x86_64" - link = "https://download.freebsd.org/releases/CI-IMAGES/13.2-RELEASE/amd64/Latest/FreeBSD-13.2-RELEASE-amd64-BASIC-CI.raw.xz" - csum = "a4fb3b6c7b75dd4d58fb0d75e4caf72844bffe0ca00e66459c028b198ffb3c0e" + 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" BUILD_SCRIPT = """ @@ -38,8 +38,9 @@ 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.9 {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}; """ @@ -107,6 +108,7 @@ class FreeBSDVM(basevm.BaseVM): prompt = "root@freebsd:~ #" self.console_ssh_init(prompt, "root", self._config["root_pass"]) self.console_sshd_config(prompt) + self.console_wait_send(prompt, "service sshd reload\n") # setup virtio-blk #1 (tarfile) self.console_wait(prompt) diff --git a/tests/vm/generated/freebsd.json b/tests/vm/generated/freebsd.json index 2d5895ebed9..c03e1cd5863 100644 --- a/tests/vm/generated/freebsd.json +++ b/tests/vm/generated/freebsd.json @@ -5,7 +5,7 @@ "make": "/usr/local/bin/gmake", "ninja": "/usr/local/bin/ninja", "packaging_command": "pkg", - "pip3": "/usr/local/bin/pip-3.8", + "pip3": "/usr/local/bin/pip", "pkgs": [ "alsa-lib", "bash", @@ -13,7 +13,7 @@ "bzip2", "ca_root_nss", "capstone4", - "ccache", + "ccache4", "cmocka", "ctags", "curl", @@ -29,6 +29,7 @@ "gmake", "gnutls", "gsed", + "gtk-vnc", "gtk3", "json-c", "libepoxy", @@ -51,15 +52,17 @@ "pixman", "pkgconf", "png", - "py39-numpy", - "py39-pillow", - "py39-pip", - "py39-sphinx", - "py39-sphinx_rtd_theme", - "py39-tomli", - "py39-yaml", + "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", @@ -70,6 +73,7 @@ "usbredir", "virglrenderer", "vte3", + "vulkan-tools", "xorriso", "zstd" ], diff --git a/tests/vm/netbsd b/tests/vm/netbsd index c7e3f1e7357..a3f6dd6b3c8 100755 --- a/tests/vm/netbsd +++ b/tests/vm/netbsd @@ -30,8 +30,7 @@ class NetBSDVM(basevm.BaseVM): "git-base", "pkgconf", "xz", - "python310", - "py310-expat", + "python311", "ninja-build", # gnu tools @@ -40,6 +39,9 @@ class NetBSDVM(basevm.BaseVM): "gsed", "gettext-tools", + # libs: basic + "dtc", + # libs: crypto "gnutls", @@ -67,7 +69,8 @@ class NetBSDVM(basevm.BaseVM): mkdir src build; cd src; tar -xf /dev/rld1a; cd ../build - ../src/configure --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 6b4fc29793b..5e4f76f3988 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,10 +99,10 @@ 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") @@ -122,6 +124,7 @@ 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", "c\n") diff --git a/tests/vm/ubuntu.aarch64 b/tests/vm/ubuntu.aarch64 index 666947393bd..eeda281f875 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/tools/ebpf/Makefile.ebpf b/tools/ebpf/Makefile.ebpf index 3391e7ce089..572ca5987ae 100755 --- a/tools/ebpf/Makefile.ebpf +++ b/tools/ebpf/Makefile.ebpf @@ -1,23 +1,24 @@ -OBJS = rss.bpf.o +SKELETONS = rss.bpf.skeleton.h LLVM_STRIP ?= llvm-strip CLANG ?= clang INC_FLAGS = `$(CLANG) -print-file-name=include` EXTRA_CFLAGS ?= -O2 -g -target bpf -all: $(OBJS) +all: $(SKELETONS) .PHONY: clean clean: - rm -f $(OBJS) - rm -f rss.bpf.skeleton.h + 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 $@ $(LLVM_STRIP) -g $@ - bpftool gen skeleton rss.bpf.o > rss.bpf.skeleton.h - cp rss.bpf.skeleton.h ../../ebpf/ + +%.skeleton.h: %.o + bpftool gen skeleton $< > $@ + cp $@ ../../ebpf/ diff --git a/tools/ebpf/rss.bpf.c b/tools/ebpf/rss.bpf.c index 20f227e2acc..c989cb3cd82 100644 --- a/tools/ebpf/rss.bpf.c +++ b/tools/ebpf/rss.bpf.c @@ -81,6 +81,7 @@ struct { __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 { @@ -88,6 +89,7 @@ struct { __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 { @@ -95,6 +97,7 @@ struct { __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, @@ -317,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; @@ -377,18 +380,19 @@ static inline int parse_packet(struct __sk_buff *skb, 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) { @@ -521,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) { @@ -541,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; - } + if (!config || !toe) { + return 0; + } - hash = calculate_rss_hash(skb, config, toe); - if (hash) { - __u32 table_idx = hash % config->indirections_len; - __u16 *queue = 0; + 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); + queue = bpf_map_lookup_elem(&tap_rss_map_indirection_table, + &table_idx); - if (queue) { - return *queue; - } + if (queue) { + return *queue; } - - return config->default_queue; } - return -1; + 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 00000000000..5f19a48cbd2 --- /dev/null +++ b/tools/i386/qemu-vmsr-helper.c @@ -0,0 +1,537 @@ +/* + * 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 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" + */ + return (ebx == 0x756e6547) && (edx == 0x49656e69) && (ecx == 0x6c65746e); +} + +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 00000000000..9a7118639ae --- /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/trace-events b/trace-events index dd318ed1af6..3ec8a6c7202 100644 --- a/trace-events +++ b/trace-events @@ -30,18 +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" - # 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)" diff --git a/trace/control-target.c b/trace/control-target.c index 97f21e476d2..57ceac21084 100644 --- a/trace/control-target.c +++ b/trace/control-target.c @@ -8,9 +8,6 @@ */ #include "qemu/osdep.h" -#include "qemu/lockable.h" -#include "cpu.h" -#include "trace/trace-root.h" #include "trace/control.h" diff --git a/trace/control.c b/trace/control.c index 1a48a7e266d..1c8c50064af 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; @@ -285,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 dfd209edd83..6754bfe052b 100644 --- a/trace/control.h +++ b/trace/control.h @@ -197,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/meson.build b/trace/meson.build index b0d31a67e68..9c42a57a053 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -1,12 +1,10 @@ -system_ss.add(files('trace-hmp-cmds.c')) - -specific_ss.add(files('control-target.c')) +system_ss.add(files('control-target.c', 'trace-hmp-cmds.c')) trace_events_files = [] foreach item : [ '.' ] + trace_events_subdirs + qapi_trace_events if item in qapi_trace_events trace_events_file = item - group_name = item.full_path().split('/')[-1].underscorify() + group_name = fs.name(item).underscorify() else trace_events_file = meson.project_source_root() / item / 'trace-events' group_name = item == '.' ? 'root' : item.underscorify() @@ -59,12 +57,13 @@ foreach item : [ '.' ] + trace_events_subdirs + qapi_trace_events endif endforeach +cat = [ python, '-c', 'import fileinput; [print(line, end="") for line in fileinput.input()]', '@INPUT@' ] trace_events_all = custom_target('trace-events-all', output: 'trace-events-all', input: trace_events_files, - command: [ 'cat', '@INPUT@' ], + command: cat, capture: true, - install: true, + install: get_option('trace_backends') != [ 'nop' ], install_dir: qemu_datadir) if 'ust' in get_option('trace_backends') diff --git a/trace/qmp.c b/trace/qmp.c index 3e3971c6a87..074a27b204f 100644 --- a/trace/qmp.c +++ b/trace/qmp.c @@ -48,7 +48,6 @@ static bool check_events(bool ignore_unavailable, bool is_pattern, } TraceEventInfoList *qmp_trace_event_get_state(const char *name, - bool has_vcpu, int64_t vcpu, Error **errp) { TraceEventInfoList *events = NULL; @@ -86,7 +85,6 @@ 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) { TraceEventIter iter; diff --git a/trace/simple.c b/trace/simple.c index 18af590cf7b..c0aba00cb7f 100644 --- a/trace/simple.c +++ b/trace/simple.c @@ -366,7 +366,7 @@ void st_set_trace_file(const char *file) /* Type cast needed for Windows where getpid() returns an int. */ trace_file_name = g_strdup_printf(CONFIG_TRACE_FILE "-" FMT_pid, (pid_t)getpid()); } else { - trace_file_name = g_strdup_printf("%s", file); + trace_file_name = g_strdup(file); } st_set_trace_file_enabled(saved_enable); diff --git a/trace/trace-hmp-cmds.c b/trace/trace-hmp-cmds.c index 86211fce273..45f4335ff5d 100644 --- a/trace/trace-hmp-cmds.c +++ b/trace/trace-hmp-cmds.c @@ -27,7 +27,7 @@ #include "monitor/monitor.h" #include "qapi/error.h" #include "qapi/qapi-commands-trace.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "trace/control.h" #ifdef CONFIG_TRACE_SIMPLE #include "trace/simple.h" @@ -40,7 +40,7 @@ void hmp_trace_event(Monitor *mon, const QDict *qdict) Error *local_err = NULL; qmp_trace_event_set_state(tp_name, new_state, - true, true, false, 0, &local_err); + true, true, &local_err); if (local_err) { error_report_err(local_err); } @@ -82,7 +82,7 @@ void hmp_info_trace_events(Monitor *mon, const QDict *qdict) name = "*"; } - events = qmp_trace_event_get_state(name, false, 0, &local_err); + events = qmp_trace_event_get_state(name, &local_err); if (local_err) { error_report_err(local_err); return; diff --git a/ui/clipboard.c b/ui/clipboard.c index 3d14bffaf80..ec00a0b8ec7 100644 --- a/ui/clipboard.c +++ b/ui/clipboard.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "system/runstate.h" #include "ui/clipboard.h" #include "trace.h" @@ -7,8 +8,62 @@ static NotifierList clipboard_notifiers = static QemuClipboardInfo *cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT]; +static VMChangeStateEntry *cb_change_state_entry = NULL; + +static bool cb_reset_serial_on_resume = false; + +static const VMStateDescription vmstate_cbcontent = { + .name = "clipboard/content", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(available, QemuClipboardContent), + VMSTATE_BOOL(requested, QemuClipboardContent), + VMSTATE_UINT32(size, QemuClipboardContent), + VMSTATE_VBUFFER_ALLOC_UINT32(data, QemuClipboardContent, 0, 0, size), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_cbinfo = { + .name = "clipboard", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_INT32(selection, QemuClipboardInfo), + VMSTATE_BOOL(has_serial, QemuClipboardInfo), + VMSTATE_UINT32(serial, QemuClipboardInfo), + VMSTATE_STRUCT_ARRAY(types, QemuClipboardInfo, QEMU_CLIPBOARD_TYPE__COUNT, 0, vmstate_cbcontent, QemuClipboardContent), + VMSTATE_END_OF_LIST() + } +}; + +static void qemu_clipboard_change_state(void *opaque, bool running, RunState state) +{ + int i; + + if (!running) { + return; + } + + if (cb_reset_serial_on_resume) { + qemu_clipboard_reset_serial(); + } + + for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) { + if (cbinfo[i]) { + qemu_clipboard_update(cbinfo[i]); + } + } + +} + void qemu_clipboard_peer_register(QemuClipboardPeer *peer) { + if (cb_change_state_entry == NULL) { + cb_change_state_entry = qemu_add_vm_change_state_handler(qemu_clipboard_change_state, NULL); + } + notifier_list_add(&clipboard_notifiers, &peer->notifier); } @@ -65,13 +120,27 @@ 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); - notifier_list_notify(&clipboard_notifiers, ¬ify); + 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); + } + } + + if (runstate_is_running() || runstate_check(RUN_STATE_SUSPENDED)) { + notifier_list_notify(&clipboard_notifiers, ¬ify); + } if (cbinfo[info->selection] != info) { qemu_clipboard_info_unref(cbinfo[info->selection]); @@ -132,6 +201,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,13 +212,20 @@ 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) { info->serial = 0; } } - notifier_list_notify(&clipboard_notifiers, ¬ify); + + if (runstate_is_running() || runstate_check(RUN_STATE_SUSPENDED)) { + notifier_list_notify(&clipboard_notifiers, ¬ify); + } else { + cb_reset_serial_on_resume = true; + } } void qemu_clipboard_set_data(QemuClipboardPeer *peer, @@ -163,9 +241,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 df6d13be38e..23b7a736d70 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" @@ -33,15 +34,15 @@ #include "ui/console.h" #include "ui/input.h" #include "ui/kbd-state.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" -#include "sysemu/runstate-action.h" -#include "sysemu/cpu-throttle.h" +#include "system/system.h" +#include "system/runstate.h" +#include "system/runstate-action.h" +#include "system/cpu-throttle.h" #include "qapi/error.h" #include "qapi/qapi-commands-block.h" #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" -#include "sysemu/blockdev.h" +#include "system/blockdev.h" #include "qemu-version.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" @@ -50,17 +51,8 @@ #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 @@ -81,6 +73,8 @@ int height; } QEMUScreen; +@class QemuCocoaPasteboardTypeOwner; + static void cocoa_update(DisplayChangeListener *dcl, int x, int y, int w, int h); @@ -88,23 +82,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; @@ -112,34 +109,35 @@ static void cocoa_switch(DisplayChangeListener *dcl, static NSInteger cbchangecount = -1; static QemuClipboardInfo *cbinfo; static QemuEvent cbevent; +static QemuCocoaPasteboardTypeOwner *cbowner; -// 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; } @@ -305,36 +303,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 @@ -343,9 +339,9 @@ - (void) raiseAllKeys; 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; } @@ -362,9 +358,31 @@ - (id)initWithFrame:(NSRect)frameRect 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; @@ -378,12 +396,13 @@ - (void) dealloc pixman_image_unref(pixman_image); } - qkbd_state_free(kbd); - if (eventsTap) { CFRelease(eventsTap); } + CGColorSpaceRelease(colorspace); + [cursorLayer release]; + cursor_unref(cursor); [super dealloc]; } @@ -392,44 +411,24 @@ - (BOOL) isOpaque 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 @@ -448,6 +447,72 @@ - (void) unhideCursor [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"); @@ -455,7 +520,7 @@ - (void) drawRect:(NSRect) rect // 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 @@ -481,7 +546,7 @@ - (void) drawRect:(NSRect) rect DIV_ROUND_UP(bitsPerPixel, 8) * 2, //bitsPerComponent bitsPerPixel, //bitsPerPixel stride, //bytesPerRow - CGColorSpaceCreateWithName(kCGColorSpaceSRGB), //colorspace + colorspace, //colorspace kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, //bitmapInfo dataProviderRef, //provider NULL, //decode @@ -497,10 +562,8 @@ - (void) drawRect:(NSRect) rect [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 @@ -513,42 +576,78 @@ - (void) drawRect:(NSRect) rect } } -- (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 { + 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 { - cx = 0; - cy = 0; - cw = screen.width; - ch = screen.height; - cdx = 1.0; - cdy = 1.0; + [[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; @@ -561,9 +660,10 @@ - (void) updateUIInfoLocked 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); @@ -591,6 +691,8 @@ - (void) updateUIInfoLocked dpy_set_ui_info(dcl.con, &info, TRUE); } +#pragma clang diagnostic pop + - (void) updateUIInfo { if (!allow_events) { @@ -605,36 +707,25 @@ - (void) updateUIInfo 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 @@ -643,51 +734,6 @@ - (void) switchSurface:(pixman_image_t *)image } 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 @@ -784,13 +830,14 @@ - (void) handleMonitorInput:(NSEvent *)event } if (keysym) { - qemu_text_console_put_keysym(NULL, 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]; }); } @@ -799,11 +846,8 @@ - (bool) handleEventLocked:(NSEvent *)event { /* 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]; /* @@ -947,7 +991,7 @@ - (bool) handleEventLocked:(NSEvent *)event } break; } - break; + return true; case NSEventTypeKeyDown: keycode = cocoa_keycode_to_qemu([event keyCode]); @@ -967,7 +1011,7 @@ - (bool) handleEventLocked:(NSEvent *)event // 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 @@ -978,12 +1022,12 @@ - (bool) handleEventLocked:(NSEvent *)event } } - 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]); @@ -993,156 +1037,154 @@ - (bool) handleEventLocked:(NSEvent *)event 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]); - } +- (void) handleMouseEvent:(NSEvent *)event button:(InputButton)button down:(bool)down +{ + if (!isMouseGrabbed) { + return; + } + + 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 { - return false; + 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]; } - return true; + + [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 " UC_CTRL_KEY " " UC_ALT_KEY " G to release Mouse)", qemu_name]]; - else - [normalWindow setTitle:@"QEMU - (Press " UC_CTRL_KEY " " UC_ALT_KEY " 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:] @@ -1152,27 +1194,36 @@ - (void) ungrabMouse { 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;} /* @@ -1182,10 +1233,19 @@ - (QEMUScreen) gscreen {return screen;} */ - (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 @@ -1200,7 +1260,6 @@ @interface QemuCocoaAppController : NSObject { } - (void)doToggleFullScreen:(id)sender; -- (void)toggleFullScreen:(id)sender; - (void)showQEMUDoc:(id)sender; - (void)zoomToFit:(id) sender; - (void)displayConsole:(id)sender; @@ -1221,6 +1280,8 @@ - (void)adjustSpeed:(id)sender; @implementation QemuCocoaAppController - (id) init { + NSWindow *window; + COCOA_DEBUG("QemuCocoaAppController: init\n"); self = [super init]; @@ -1234,20 +1295,20 @@ - (id) init } // 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]; @@ -1268,8 +1329,10 @@ - (void) dealloc { COCOA_DEBUG("QemuCocoaAppController: dealloc\n"); - if (cocoaView) - [cocoaView release]; + [cocoaView release]; + [cbowner release]; + cbowner = nil; + [super dealloc]; } @@ -1283,7 +1346,7 @@ - (void)applicationWillTerminate:(NSNotification *)aNotification { COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n"); - with_iothread_lock(^{ + with_bql(^{ shutdown_action = SHUTDOWN_ACTION_POWEROFF; qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI); }); @@ -1313,8 +1376,20 @@ - (void)windowDidChangeScreen:(NSNotification *)notification [cocoaView updateUIInfo]; } +- (void)windowDidEnterFullScreen:(NSNotification *)notification +{ + [cocoaView grabMouse]; +} + +- (void)windowDidExitFullScreen:(NSNotification *)notification +{ + [cocoaView resizeWindow]; + [cocoaView ungrabMouse]; +} + - (void)windowDidResize:(NSNotification *)notification { + [cocoaView updateBounds]; [cocoaView updateUIInfo]; } @@ -1331,6 +1406,14 @@ - (BOOL)windowShouldClose:(id)sender return NO; } +- (NSApplicationPresentationOptions) window:(NSWindow *)window + willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions; + +{ + return (proposedOptions & ~(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)) | + NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar; +} + /* * Called when QEMU goes into the background. Note that * [-NSWindowDelegate windowDidResignKey:] is used here instead of @@ -1350,14 +1433,7 @@ - (void) windowDidResignKey: (NSNotification *)aNotification */ - (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 @@ -1404,10 +1480,20 @@ - (void)showQEMUDoc:(id)sender /* 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]; } } @@ -1415,13 +1501,15 @@ - (void)zoomToFit:(id) sender /* 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]; @@ -1432,7 +1520,7 @@ - (void)pauseQEMU:(id)sender /* Resume running the guest operating system */ - (void)resumeQEMU:(id) sender { - with_iothread_lock(^{ + with_bql(^{ qmp_cont(NULL); }); [sender setEnabled: NO]; @@ -1445,8 +1533,8 @@ - (void)displayPause { /* 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)]; @@ -1462,7 +1550,7 @@ - (void)removePause /* Restarts QEMU */ - (void)restartQEMU:(id)sender { - with_iothread_lock(^{ + with_bql(^{ qmp_system_reset(NULL); }); } @@ -1470,7 +1558,7 @@ - (void)restartQEMU:(id)sender /* Powers down QEMU */ - (void)powerDownQEMU:(id)sender { - with_iothread_lock(^{ + with_bql(^{ qmp_system_powerdown(NULL); }); } @@ -1489,7 +1577,7 @@ - (void)ejectDeviceMedia:(id)sender } __block Error *err = NULL; - with_iothread_lock(^{ + with_bql(^{ qmp_eject([drive cStringUsingEncoding: NSASCIIStringEncoding], NULL, false, false, &err); }); @@ -1524,7 +1612,7 @@ - (void)changeDeviceMedia:(id)sender } __block Error *err = NULL; - with_iothread_lock(^{ + with_bql(^{ qmp_blockdev_change_medium([drive cStringUsingEncoding: NSASCIIStringEncoding], NULL, @@ -1606,7 +1694,7 @@ - (void)adjustSpeed:(id)sender // get the throttle percentage throttle_pct = [sender tag]; - with_iothread_lock(^{ + with_bql(^{ cpu_throttle_set(throttle_pct); }); COCOA_DEBUG("cpu throttling at %d%c\n", cpu_throttle_get_percentage(), '%'); @@ -1671,7 +1759,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]; @@ -1807,6 +1900,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 @@ -1818,7 +1922,7 @@ - (void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSPasteboardType)t return; } - with_iothread_lock(^{ + with_bql(^{ QemuClipboardInfo *info = qemu_clipboard_info_ref(cbinfo); qemu_event_reset(&cbevent); qemu_clipboard_request(info, QEMU_CLIPBOARD_TYPE_TEXT); @@ -1826,9 +1930,9 @@ - (void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSPasteboardType)t 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) { @@ -1844,8 +1948,6 @@ - (void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSPasteboardType)t @end -static QemuCocoaPasteboardTypeOwner *cbowner; - static void cocoa_clipboard_notify(Notifier *notifier, void *data); static void cocoa_clipboard_request(QemuClipboardInfo *info, QemuClipboardType type); @@ -1908,43 +2010,8 @@ static void cocoa_clipboard_request(QemuClipboardInfo *info, } } -/* - * The startup process for the OSX/Cocoa UI is complicated, because - * OSX insists that the UI runs on the initial main thread, and so we - * need to start a second thread which runs the qemu_default_main(): - * in main(): - * in cocoa_display_init(): - * assign cocoa_main to qemu_main - * create application, menus, etc - * in cocoa_main(): - * create qemu-main thread - * enter OSX run loop - */ - -static void *call_qemu_main(void *opaque) -{ - int status; - - COCOA_DEBUG("Second thread: calling qemu_default_main()\n"); - qemu_mutex_lock_iothread(); - status = qemu_default_main(); - qemu_mutex_unlock_iothread(); - COCOA_DEBUG("Second thread: qemu_default_main() returned, exiting\n"); - [cbowner release]; - exit(status); -} - static int cocoa_main(void) { - QemuThread thread; - - COCOA_DEBUG("Entered %s()\n", __func__); - - qemu_mutex_unlock_iothread(); - qemu_thread_create(&thread, "qemu_main", call_qemu_main, - NULL, QEMU_THREAD_DETACHED); - - // Start the main event loop COCOA_DEBUG("Main thread: entering OSX run loop\n"); [NSApp run]; COCOA_DEBUG("Main thread: left OSX run loop, which should never happen\n"); @@ -1961,16 +2028,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]; }); } @@ -1989,7 +2047,6 @@ static void cocoa_switch(DisplayChangeListener *dcl, pixman_image_ref(image); dispatch_async(dispatch_get_main_queue(), ^{ - [cocoaView updateUIInfo]; [cocoaView switchSurface:image]; }); } @@ -1999,18 +2056,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); @@ -2026,14 +2072,27 @@ 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]; COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n"); - qemu_main = cocoa_main; - // Pull this console process up to being a fully-fledged graphical // app with a menubar and Dock icon ProcessSerialNumber psn = { 0, kCurrentProcess }; @@ -2041,26 +2100,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 removable 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]; @@ -2077,14 +2123,45 @@ 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]; qemu_clipboard_peer_register(&cbpeer); [pool release]; + + /* + * The Cocoa UI will run the NSApplication runloop on the main thread + * rather than the default Core Foundation one. + */ + qemu_main = cocoa_main; } static QemuDisplay qemu_display_cocoa = { diff --git a/ui/console-priv.h b/ui/console-priv.h index 88569ed2cc4..43ceb8122f1 100644 --- a/ui/console-priv.h +++ b/ui/console-priv.h @@ -35,7 +35,7 @@ struct QemuConsole { QTAILQ_ENTRY(QemuConsole) next; }; -void qemu_text_console_select(QemuTextConsole *c); +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); diff --git a/ui/console-vc-stubs.c b/ui/console-vc-stubs.c new file mode 100644 index 00000000000..b63e2fb2345 --- /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 index 9c13cc2981b..830842064d6 100644 --- a/ui/console-vc.c +++ b/ui/console-vc.c @@ -42,6 +42,8 @@ enum TTYState { TTY_STATE_NORM, TTY_STATE_ESC, TTY_STATE_CSI, + TTY_STATE_G0, + TTY_STATE_G1, }; typedef struct QemuTextConsole { @@ -88,6 +90,7 @@ struct VCChardev { int esc_params[MAX_ESC_PARAMS]; int nb_esc_params; TextAttributes t_attrib; /* currently active text attributes */ + TextAttributes t_attrib_saved; int x_saved, y_saved; }; typedef struct VCChardev VCChardev; @@ -287,7 +290,7 @@ static void kbd_send_chars(QemuTextConsole *s) const uint8_t *buf; uint32_t size; - buf = fifo8_pop_buf(&s->out_fifo, MIN(len, avail), &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; @@ -615,10 +618,9 @@ static void vc_put_one(VCChardev *vc, int ch) static void vc_respond_str(VCChardev *vc, const char *buf) { - while (*buf) { - vc_put_one(vc, *buf); - buf++; - } + QemuTextConsole *s = vc->console; + + qemu_chr_be_write(s->chr, (const uint8_t *)buf, strlen(buf)); } /* set cursor, checking bounds */ @@ -643,12 +645,119 @@ static void vc_set_cursor(VCChardev *vc, int x, int y) s->y = y; } +/** + * vc_csi_P() - (DCH) deletes one or more characters from the cursor + * position to the right. As characters are deleted, the remaining + * characters between the cursor and right margin move to the + * left. Character attributes move with the characters. + */ +static void vc_csi_P(struct VCChardev *vc, unsigned int nr) +{ + QemuTextConsole *s = vc->console; + TextCell *c1, *c2; + unsigned int x1, x2, y; + unsigned int end, len; + + if (!nr) { + nr = 1; + } + if (nr > s->width - s->x) { + nr = s->width - s->x; + if (!nr) { + return; + } + } + + x1 = s->x; + x2 = s->x + nr; + len = s->width - x2; + if (len) { + y = (s->y_base + s->y) % s->total_height; + c1 = &s->cells[y * s->width + x1]; + c2 = &s->cells[y * s->width + x2]; + memmove(c1, c2, len * sizeof(*c1)); + for (end = x1 + len; x1 < end; x1++) { + vc_update_xy(vc, x1, s->y); + } + } + /* Clear the rest */ + for (; x1 < s->width; x1++) { + vc_clear_xy(vc, x1, s->y); + } +} + +/** + * vc_csi_at() - (ICH) inserts `nr` blank characters with the default + * character attribute. The cursor remains at the beginning of the + * blank characters. Text between the cursor and right margin moves to + * the right. Characters scrolled past the right margin are lost. + */ +static void vc_csi_at(struct VCChardev *vc, unsigned int nr) +{ + QemuTextConsole *s = vc->console; + TextCell *c1, *c2; + unsigned int x1, x2, y; + unsigned int end, len; + + if (!nr) { + nr = 1; + } + if (nr > s->width - s->x) { + nr = s->width - s->x; + if (!nr) { + return; + } + } + + x1 = s->x + nr; + x2 = s->x; + len = s->width - x1; + if (len) { + y = (s->y_base + s->y) % s->total_height; + c1 = &s->cells[y * s->width + x1]; + c2 = &s->cells[y * s->width + x2]; + memmove(c1, c2, len * sizeof(*c1)); + for (end = x1 + len; x1 < end; x1++) { + vc_update_xy(vc, x1, s->y); + } + } + /* Insert blanks */ + for (x1 = s->x; x1 < s->x + nr; x1++) { + vc_clear_xy(vc, x1, s->y); + } +} + +/** + * vc_save_cursor() - saves cursor position and character attributes. + */ +static void vc_save_cursor(VCChardev *vc) +{ + QemuTextConsole *s = vc->console; + + vc->x_saved = s->x; + vc->y_saved = s->y; + vc->t_attrib_saved = vc->t_attrib; +} + +/** + * vc_restore_cursor() - restores cursor position and character + * attributes from saved state. + */ +static void vc_restore_cursor(VCChardev *vc) +{ + QemuTextConsole *s = vc->console; + + s->x = vc->x_saved; + s->y = vc->y_saved; + vc->t_attrib = vc->t_attrib_saved; +} + static void vc_putchar(VCChardev *vc, int ch) { QemuTextConsole *s = vc->console; int i; int x, y; - char response[40]; + g_autofree char *response = NULL; switch(vc->state) { case TTY_STATE_NORM: @@ -694,6 +803,16 @@ static void vc_putchar(VCChardev *vc, int ch) vc->esc_params[i] = 0; vc->nb_esc_params = 0; vc->state = TTY_STATE_CSI; + } else if (ch == '(') { + vc->state = TTY_STATE_G0; + } else if (ch == ')') { + vc->state = TTY_STATE_G1; + } else if (ch == '7') { + vc_save_cursor(vc); + vc->state = TTY_STATE_NORM; + } else if (ch == '8') { + vc_restore_cursor(vc); + vc->state = TTY_STATE_NORM; } else { vc->state = TTY_STATE_NORM; } @@ -810,6 +929,9 @@ static void vc_putchar(VCChardev *vc, int ch) break; } break; + case 'P': + vc_csi_P(vc, vc->esc_params[0]); + break; case 'm': vc_handle_escape(vc); break; @@ -821,22 +943,20 @@ static void vc_putchar(VCChardev *vc, int ch) break; case 6: /* report cursor position */ - sprintf(response, "\033[%d;%dR", - (s->y_base + s->y) % s->total_height + 1, - s->x + 1); + response = g_strdup_printf("\033[%d;%dR", + s->y + 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; + vc_save_cursor(vc); break; case 'u': - /* restore cursor position */ - s->x = vc->x_saved; - s->y = vc->y_saved; + vc_restore_cursor(vc); + break; + case '@': + vc_csi_at(vc, vc->esc_params[0]); break; default: trace_console_putchar_unhandled(ch); @@ -844,6 +964,16 @@ static void vc_putchar(VCChardev *vc, int ch) } break; } + break; + case TTY_STATE_G0: /* set character sets */ + case TTY_STATE_G1: /* set character sets */ + switch (ch) { + case 'B': + /* Latin-1 map */ + break; + } + vc->state = TTY_STATE_NORM; + break; } } @@ -906,7 +1036,7 @@ qemu_text_console_finalize(Object *obj) } static void -qemu_text_console_class_init(ObjectClass *oc, void *data) +qemu_text_console_class_init(ObjectClass *oc, const void *data) { if (!cursor_timer) { cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, cursor_timer_cb, NULL); @@ -935,7 +1065,7 @@ qemu_fixed_text_console_finalize(Object *obj) } static void -qemu_fixed_text_console_class_init(ObjectClass *oc, void *data) +qemu_fixed_text_console_class_init(ObjectClass *oc, const void *data) { } @@ -958,10 +1088,9 @@ static void vc_chr_set_echo(Chardev *chr, bool echo) drv->console->echo = echo; } -void qemu_text_console_select(QemuTextConsole *c) +void qemu_text_console_update_size(QemuTextConsole *c) { dpy_text_resize(QEMU_CONSOLE(c), c->width, c->height); - qemu_text_console_update_cursor(); } static void vc_chr_open(Chardev *chr, @@ -990,8 +1119,8 @@ static void vc_chr_open(Chardev *chr, trace_console_txt_new(width, height); if (width == 0 || height == 0) { s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_TEXT_CONSOLE)); - width = qemu_console_get_width(NULL, 80 * FONT_WIDTH); - height = qemu_console_get_height(NULL, 24 * FONT_HEIGHT); + width = 80 * FONT_WIDTH; + height = 24 * FONT_HEIGHT; } else { s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE)); } @@ -1052,7 +1181,7 @@ static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp) } } -static void char_vc_class_init(ObjectClass *oc, void *data) +static void char_vc_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); @@ -1074,6 +1203,6 @@ 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); + type_register_static(&char_vc_type_info); } } diff --git a/ui/console.c b/ui/console.c index 4a4f19ed33e..2d00828c538 100644 --- a/ui/console.c +++ b/ui/console.c @@ -35,8 +35,9 @@ #include "qemu/option.h" #include "chardev/char.h" #include "trace.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qom/object.h" +#include "qemu/memfd.h" #include "console-priv.h" @@ -49,7 +50,8 @@ typedef struct QemuGraphicConsole { uint32_t head; QEMUCursor *cursor; - int cursor_x, cursor_y, cursor_on; + int cursor_x, cursor_y; + bool cursor_on; } QemuGraphicConsole; typedef QemuConsoleClass QemuGraphicConsoleClass; @@ -66,7 +68,6 @@ struct DisplayState { }; static DisplayState *display_state; -static QemuConsole *active_console; static QTAILQ_HEAD(, QemuConsole) consoles = QTAILQ_HEAD_INITIALIZER(consoles); @@ -135,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; } @@ -209,9 +209,6 @@ 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); } @@ -219,9 +216,6 @@ void graphic_hw_invalidate(QemuConsole *con) 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); } @@ -265,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) { @@ -305,39 +299,8 @@ static void displaychangelistener_display_console(DisplayChangeListener *dcl, } } -void console_select(unsigned int index) -{ - DisplayChangeListener *dcl; - QemuConsole *s; - - trace_console_select(index); - s = qemu_console_lookup_by_index(index); - if (s) { - DisplayState *ds = s->ds; - - active_console = s; - QLIST_FOREACH (dcl, &ds->listeners, next) { - if (dcl->con != NULL) { - continue; - } - displaychangelistener_display_console(dcl, s, NULL); - } - - if (QEMU_IS_TEXT_CONSOLE(s)) { - qemu_text_console_select(QEMU_TEXT_CONSOLE(s)); - } - } -} - void qemu_text_console_put_keysym(QemuTextConsole *s, int keysym) { - if (!s) { - if (!QEMU_IS_TEXT_CONSOLE(active_console)) { - return; - } - s = QEMU_TEXT_CONSOLE(active_console); - } - qemu_text_console_handle_keysym(s, keysym); } @@ -392,11 +355,6 @@ qemu_console_register(QemuConsole *c) { int i; - if (!active_console || (!QEMU_IS_GRAPHIC_CONSOLE(active_console) && - QEMU_IS_GRAPHIC_CONSOLE(c))) { - active_console = c; - } - if (QTAILQ_EMPTY(&consoles)) { c->index = 0; QTAILQ_INSERT_TAIL(&consoles, c, next); @@ -443,7 +401,7 @@ qemu_console_finalize(Object *obj) } static void -qemu_console_class_init(ObjectClass *oc, void *data) +qemu_console_class_init(ObjectClass *oc, const void *data) { } @@ -479,7 +437,7 @@ qemu_graphic_console_prop_get_head(Object *obj, Visitor *v, const char *name, } static void -qemu_graphic_console_class_init(ObjectClass *oc, void *data) +qemu_graphic_console_class_init(ObjectClass *oc, const void *data) { object_class_property_add_link(oc, "device", TYPE_DEVICE, offsetof(QemuGraphicConsole, device), @@ -495,60 +453,26 @@ qemu_graphic_console_init(Object *obj) { } -#ifdef WIN32 -void qemu_displaysurface_win32_set_handle(DisplaySurface *surface, - HANDLE h, uint32_t offset) +void qemu_displaysurface_set_share_handle(DisplaySurface *surface, + qemu_pixman_shareable handle, + uint32_t offset) { - assert(!surface->handle); + assert(surface->share_handle == SHAREABLE_NONE); - surface->handle = h; - surface->handle_offset = offset; -} - -static void -win32_pixman_image_destroy(pixman_image_t *image, void *data) -{ - DisplaySurface *surface = data; - - if (!surface->handle) { - return; - } + surface->share_handle = handle; + surface->share_handle_offset = offset; - assert(surface->handle_offset == 0); - - qemu_win32_map_free( - pixman_image_get_data(surface->image), - surface->handle, - &error_warn - ); } -#endif DisplaySurface *qemu_create_displaysurface(int width, int height) { - DisplaySurface *surface; - void *bits = NULL; -#ifdef WIN32 - HANDLE handle = NULL; -#endif - trace_displaysurface_create(width, height); -#ifdef WIN32 - bits = qemu_win32_map_alloc(width * height * 4, &handle, &error_abort); -#endif - - surface = qemu_create_displaysurface_from( + return qemu_create_displaysurface_from( width, height, PIXMAN_x8r8g8b8, - width * 4, bits + width * 4, NULL ); - surface->flags = QEMU_ALLOCATED_FLAG; - -#ifdef WIN32 - qemu_displaysurface_win32_set_handle(surface, handle, 0); -#endif - return surface; } DisplaySurface *qemu_create_displaysurface_from(int width, int height, @@ -558,15 +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->image = pixman_image_create_bits(format, - width, height, - (void *)data, linesize); - assert(surface->image != NULL); -#ifdef WIN32 - pixman_image_set_destroy_function(surface->image, - win32_pixman_image_destroy, surface); -#endif + 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; } @@ -575,6 +509,7 @@ DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image) DisplaySurface *surface = g_new0(DisplaySurface, 1); trace_displaysurface_create_pixman(surface); + surface->share_handle = SHAREABLE_NONE; surface->image = pixman_image_ref(image); return surface; @@ -584,6 +519,7 @@ DisplaySurface *qemu_create_placeholder_surface(int w, int h, const char *msg) { DisplaySurface *surface = qemu_create_displaysurface(w, h); +#ifdef CONFIG_PIXMAN pixman_color_t bg = QEMU_PIXMAN_COLOR_BLACK; pixman_color_t fg = QEMU_PIXMAN_COLOR_GRAY; pixman_image_t *glyph; @@ -598,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; } @@ -749,8 +686,6 @@ dcl_set_graphic_cursor(DisplayChangeListener *dcl, QemuGraphicConsole *con) void register_displaychangelistener(DisplayChangeListener *dcl) { - QemuConsole *con; - assert(!dcl->ds); trace_displaychangelistener_register(dcl, dcl->ops->dpy_name); @@ -759,13 +694,12 @@ 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); - if (QEMU_IS_GRAPHIC_CONSOLE(con)) { - dcl_set_graphic_cursor(dcl, QEMU_GRAPHIC_CONSOLE(con)); + 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(); } @@ -803,9 +737,6 @@ static void dpy_set_ui_info_timer(void *opaque) bool dpy_ui_info_supported(const QemuConsole *con) { - if (con == NULL) { - con = active_console; - } if (con == NULL) { return false; } @@ -817,19 +748,11 @@ const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con) { assert(dpy_ui_info_supported(con)); - if (con == NULL) { - con = active_console; - } - 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; } @@ -868,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) { @@ -914,7 +837,7 @@ void dpy_gfx_replace_surface(QemuConsole *con, 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, new_surface, surface ? FALSE : TRUE); @@ -968,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) { @@ -986,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) { @@ -1004,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) { @@ -1013,7 +936,7 @@ void dpy_text_resize(QemuConsole *con, int w, int h) } } -void dpy_mouse_set(QemuConsole *c, int x, int y, int on) +void dpy_mouse_set(QemuConsole *c, int x, int y, bool on) { QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c); DisplayState *s = c->ds; @@ -1026,7 +949,7 @@ void dpy_mouse_set(QemuConsole *c, int x, int y, int on) return; } QLIST_FOREACH(dcl, &s->listeners, next) { - if (c != (dcl->con ? dcl->con : active_console)) { + if (c != dcl->con) { continue; } if (dcl->ops->dpy_mouse_set) { @@ -1047,7 +970,7 @@ void dpy_cursor_define(QemuConsole *c, QEMUCursor *cursor) return; } QLIST_FOREACH(dcl, &s->listeners, next) { - if (c != (dcl->con ? dcl->con : active_console)) { + if (c != dcl->con) { continue; } if (dcl->ops->dpy_cursor_define) { @@ -1056,19 +979,6 @@ void dpy_cursor_define(QemuConsole *c, 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) { @@ -1097,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) { @@ -1124,7 +1034,7 @@ void dpy_gl_scanout_texture(QemuConsole *con, 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) { @@ -1146,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) { @@ -1162,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) { @@ -1179,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) { @@ -1195,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) { @@ -1214,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) { @@ -1250,7 +1160,7 @@ DisplayState *init_displaystate(void) * all QemuConsoles are created and the order / numbering * doesn't change any more */ name = g_strdup_printf("console[%d]", con->index); - object_property_add_child(container_get(object_get_root(), "/backend"), + object_property_add_child(object_get_container("backend"), name, OBJECT(con)); g_free(name); } @@ -1323,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; @@ -1401,30 +1323,21 @@ static QemuConsole *qemu_graphic_console_lookup_unused(void) QEMUCursor *qemu_console_get_cursor(QemuConsole *con) { - if (con == NULL) { - con = active_console; - } 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 && QEMU_IS_GRAPHIC_CONSOLE(con); } bool qemu_console_is_fixedsize(QemuConsole *con) { - if (con == NULL) { - con = active_console; - } return con && (QEMU_IS_GRAPHIC_CONSOLE(con) || QEMU_IS_FIXED_TEXT_CONSOLE(con)); } @@ -1434,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; } } @@ -1468,16 +1379,14 @@ char *qemu_console_get_label(QemuConsole *con) bool multihead; dev = DEVICE(c->device); - multihead = qemu_console_is_multihead(dev); + multihead = qemu_graphic_console_is_multihead(c); if (multihead) { return g_strdup_printf("%s.%d", dev->id ? dev->id : object_get_typename(c->device), c->head); } else { - return g_strdup_printf("%s", dev->id ? - dev->id : - object_get_typename(c->device)); + return g_strdup(dev->id ? : object_get_typename(c->device)); } } return g_strdup("VGA"); @@ -1493,17 +1402,11 @@ char *qemu_console_get_label(QemuConsole *con) 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; - } if (con == NULL) { return -1; } @@ -1515,15 +1418,12 @@ uint32_t qemu_console_get_head(QemuConsole *con) 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: @@ -1535,15 +1435,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: @@ -1577,7 +1474,8 @@ void qemu_console_resize(QemuConsole *s, int width, int height) 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; @@ -1677,6 +1575,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; @@ -1695,4 +1608,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"); } diff --git a/ui/curses.c b/ui/curses.c index 8bde8c5cf7c..a39aee87623 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -36,9 +36,9 @@ #include "qemu/module.h" #include "ui/console.h" #include "ui/input.h" -#include "sysemu/sysemu.h" +#include "system/system.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); - - invalidate = 1; + 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; + } 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; - qemu_text_console_put_keysym(NULL, 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/cursor.c b/ui/cursor.c index 29717b3ecb7..6e23244fbe6 100644 --- a/ui/cursor.c +++ b/ui/cursor.c @@ -197,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; @@ -232,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 1d3a7122a11..d05dddaf81b 100644 --- a/ui/dbus-chardev.c +++ b/ui/dbus-chardev.c @@ -106,7 +106,7 @@ dbus_chardev_init(DBusDisplay *dpy) dpy->notifier.notify = dbus_display_on_notify; dbus_display_notifier_add(&dpy->notifier); - object_child_foreach(container_get(object_get_root(), "/chardevs"), + object_child_foreach(object_get_container("chardevs"), dbus_display_chardev_foreach, dpy); } @@ -269,7 +269,7 @@ dbus_chr_parse(QemuOpts *opts, ChardevBackend *backend, } static void -char_dbus_class_init(ObjectClass *oc, void *data) +char_dbus_class_init(ObjectClass *oc, const void *data) { DBusChardevClass *klass = DBUS_CHARDEV_CLASS(oc); ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/ui/dbus-clipboard.c b/ui/dbus-clipboard.c index fe7fcdecb6f..6787a776687 100644 --- a/ui/dbus-clipboard.c +++ b/ui/dbus-clipboard.c @@ -26,7 +26,7 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qom/object_interfaces.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "trace.h" @@ -141,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; @@ -305,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 36f7349585c..85e215ef233 100644 --- a/ui/dbus-console.c +++ b/ui/dbus-console.c @@ -41,7 +41,7 @@ struct _DBusDisplayConsole { DisplayChangeListener dcl; DBusDisplay *display; - GHashTable *listeners; + GPtrArray *listeners; QemuDBusDisplay1Console *iface; QemuDBusDisplay1Keyboard *iface_kbd; @@ -110,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 @@ -139,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; } @@ -154,7 +156,7 @@ dbus_display_console_dispose(GObject *object) 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); @@ -176,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); } @@ -264,16 +266,6 @@ 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); - return DBUS_METHOD_INVOCATION_HANDLED; - } - #ifdef G_OS_WIN32 if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) { return DBUS_METHOD_INVOCATION_HANDLED; @@ -313,10 +305,16 @@ dbus_console_register_listener(DBusDisplayConsole *ddc, #endif ); + GDBusConnectionFlags flags = + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER; +#ifdef WIN32 + flags |= G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; +#endif + listener_conn = g_dbus_connection_new_sync( G_IO_STREAM(socket_conn), guid, - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER, + flags, NULL, NULL, &err); if (err) { error_report("Failed to setup peer connection: %s", err->message); @@ -328,9 +326,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); @@ -386,7 +382,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, @@ -453,7 +449,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, @@ -514,7 +510,7 @@ static void dbus_mouse_update_is_absolute(DBusDisplayConsole *ddc) { g_object_set(ddc->iface_mouse, - "is-absolute", qemu_input_is_absolute(), + "is-absolute", qemu_input_is_absolute(ddc->dcl.con), NULL); } diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml index f0e2fac2127..4a41a7e0f34 100644 --- a/ui/dbus-display1.xml +++ b/ui/dbus-display1.xml @@ -71,7 +71,7 @@ :dbus:iface:`org.qemu.Display1.Listener` interface. --> - + @@ -370,7 +370,7 @@ - + + + + + + + + + + + + + + + + + + + + + + + + @@ -513,6 +561,7 @@ + + + + + + + + + + + + + + + + + + + + + + - + @@ -715,7 +809,7 @@ :dbus:iface:`org.qemu.Display1.AudioInListener` interface. --> - + @@ -724,6 +818,18 @@ + + + + + - + diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 36548a7f520..42875b8eed8 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -24,11 +24,9 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "dbus.h" -#ifdef CONFIG_OPENGL -#include -#endif +#include "glib.h" #ifdef G_OS_UNIX #include #endif @@ -41,6 +39,7 @@ #include "ui/shader.h" #include "ui/egl-helpers.h" #include "ui/egl-context.h" +#include "ui/qemu-pixman.h" #endif #include "trace.h" @@ -62,9 +61,11 @@ struct _DBusDisplayListener { QemuDBusDisplay1Listener *proxy; -#ifdef CONFIG_OPENGL +#ifdef CONFIG_PIXMAN /* Keep track of the damage region */ pixman_region32_t gl_damage; +#else + int gl_damage; #endif DisplayChangeListener dcl; @@ -82,7 +83,14 @@ struct _DBusDisplayListener { #ifdef CONFIG_OPENGL egl_fb fb; #endif +#else /* !WIN32 */ + QemuDBusDisplay1ListenerUnixMap *map_proxy; + QemuDBusDisplay1ListenerUnixScanoutDMABUF2 *scanout_dmabuf_v2_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) @@ -90,11 +98,29 @@ 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); } @@ -263,33 +289,119 @@ static void dbus_call_update_gl(DisplayChangeListener *dcl, } #ifdef CONFIG_GBM -static void dbus_scanout_dmabuf(DisplayChangeListener *dcl, - QemuDmaBuf *dmabuf) +static void dbus_scanout_dmabuf_v1(DBusDisplayListener *ddl, + 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_fds(dmabuf, NULL)[0]; 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_strides(dmabuf, NULL)[0]; + 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); +} + +static void dbus_scanout_dmabuf_v2(DBusDisplayListener *ddl, + QemuDmaBuf *dmabuf) +{ + g_autoptr(GError) err = NULL; + g_autoptr(GUnixFDList) fd_list = NULL; + int i, fd_index[DMABUF_MAX_PLANES], num_fds; + uint32_t x, y, width, height, fourcc, backing_width, backing_height; + GVariant *fd, *offset, *stride, *fd_handles[DMABUF_MAX_PLANES]; + uint64_t modifier; + bool y0_top; + int nfds, noffsets, nstrides; + const int *fds = qemu_dmabuf_get_fds(dmabuf, &nfds); + const uint32_t *offsets = qemu_dmabuf_get_offsets(dmabuf, &noffsets); + const uint32_t *strides = qemu_dmabuf_get_strides(dmabuf, &nstrides); + uint32_t num_planes = qemu_dmabuf_get_num_planes(dmabuf); + + assert(nfds >= num_planes); + assert(noffsets >= num_planes); + assert(nstrides >= num_planes); + + fd_list = g_unix_fd_list_new(); + + for (num_fds = 0; num_fds < num_planes; num_fds++) { + int plane_fd = fds[num_fds]; + + if (plane_fd < 0) { + break; + } + + fd_index[num_fds] = g_unix_fd_list_append(fd_list, plane_fd, &err); + if (fd_index[num_fds] < 0) { + error_report("Failed to setup dmabuf fdlist: %s", err->message); + return; + } + } + + ddl_discard_display_messages(ddl); + + x = qemu_dmabuf_get_x(dmabuf); + y = qemu_dmabuf_get_y(dmabuf); + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + fourcc = qemu_dmabuf_get_fourcc(dmabuf); + backing_width = qemu_dmabuf_get_backing_width(dmabuf); + backing_height = qemu_dmabuf_get_backing_height(dmabuf); + modifier = qemu_dmabuf_get_modifier(dmabuf); + y0_top = qemu_dmabuf_get_y0_top(dmabuf); + + offset = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + offsets, num_planes, sizeof(uint32_t)); + stride = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + strides, num_planes, sizeof(uint32_t)); + + for (i = 0; i < num_fds; i++) { + fd_handles[i] = g_variant_new_handle(fd_index[i]); + } + fd = g_variant_new_array(G_VARIANT_TYPE_HANDLE, fd_handles, num_fds); + + qemu_dbus_display1_listener_unix_scanout_dmabuf2_call_scanout_dmabuf2( + ddl->scanout_dmabuf_v2_proxy, fd, x, y, width, height, offset, stride, + num_planes, fourcc, backing_width, backing_height, modifier, y0_top, + G_DBUS_CALL_FLAGS_NONE, -1, fd_list, NULL, NULL, NULL); +} + +static void dbus_scanout_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf) +{ + DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); + + if (ddl->scanout_dmabuf_v2_proxy) { + dbus_scanout_dmabuf_v2(ddl, dmabuf); + } else { + if (qemu_dmabuf_get_num_planes(dmabuf) > 1) { + g_debug("org.qemu.Display1.Listener.ScanoutDMABUF " + "does not support mutli plane"); + return; + } + dbus_scanout_dmabuf_v1(ddl, dmabuf); + } } #endif /* GBM */ #endif /* OPENGL */ @@ -305,13 +417,13 @@ static bool dbus_scanout_map(DBusDisplayListener *ddl) return true; } - if (!ddl->can_share_map || !ddl->ds->handle) { + if (!ddl->can_share_map || !ddl->ds->share_handle) { return false; } success = DuplicateHandle( GetCurrentProcess(), - ddl->ds->handle, + ddl->ds->share_handle, ddl->peer_process, &target_handle, FILE_MAP_READ | SECTION_QUERY, @@ -323,10 +435,12 @@ static bool dbus_scanout_map(DBusDisplayListener *ddl) 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->handle_offset, + ddl->ds->share_handle_offset, surface_width(ddl->ds), surface_height(ddl->ds), surface_stride(ddl->ds), @@ -384,6 +498,8 @@ dbus_scanout_share_d3d_texture( return false; } + ddl_discard_display_messages(ddl); + qemu_dbus_display1_listener_win32_d3d11_call_scanout_texture2d( ddl->d3d11_proxy, GPOINTER_TO_INT(target_handle), @@ -408,6 +524,51 @@ dbus_scanout_share_d3d_texture( 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 @@ -423,28 +584,23 @@ static void dbus_scanout_texture(DisplayChangeListener *dcl, trace_dbus_scanout_texture(tex_id, backing_y_0_top, backing_width, backing_height, x, y, w, h); #ifdef CONFIG_GBM - QemuDmaBuf dmabuf = { - .width = w, - .height = h, - .y0_top = backing_y_0_top, - .x = x, - .y = y, - .backing_width = backing_width, - .backing_height = backing_height, - }; + g_autoptr(QemuDmaBuf) dmabuf = NULL; + int fd[DMABUF_MAX_PLANES], num_planes; + uint32_t offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES], 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) { - error_report("%s: failed to get fd for texture", __func__); + if (!egl_dmabuf_export_texture(tex_id, fd, (EGLint *)offset, (EGLint *)stride, + (EGLint *)&fourcc, &num_planes, &modifier)) { + error_report("%s: failed to export dmabuf for texture", __func__); return; } + dmabuf = qemu_dmabuf_new(w, h, offset, stride, x, y, backing_width, + backing_height, fourcc, modifier, fd, num_planes, + 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 @@ -473,6 +629,7 @@ static void dbus_cursor_dmabuf(DisplayChangeListener *dcl, DisplaySurface *ds; GVariant *v_data = NULL; egl_fb cursor_fb = EGL_FB_INIT; + uint32_t width, height, texture; if (!dmabuf) { qemu_dbus_display1_listener_call_mouse_set( @@ -481,13 +638,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( @@ -545,6 +708,7 @@ static void dbus_gl_refresh(DisplayChangeListener *dcl) return; } +#ifdef CONFIG_PIXMAN int n_rects = pixman_region32_n_rects(&ddl->gl_damage); for (int i = 0; i < n_rects; i++) { @@ -555,6 +719,13 @@ static void dbus_gl_refresh(DisplayChangeListener *dcl) 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 */ @@ -569,63 +740,45 @@ static void dbus_gl_gfx_update(DisplayChangeListener *dcl, { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); +#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); - - trace_dbus_update(x, y, w, h); - -#ifdef WIN32 - if (dbus_scanout_map(ddl)) { - qemu_dbus_display1_listener_win32_map_call_update_map( - ddl->map_proxy, - x, y, w, h, - G_DBUS_CALL_FLAGS_NONE, - DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); - return; - } -#endif - - if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) { - 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), @@ -640,6 +793,57 @@ 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) @@ -671,7 +875,7 @@ static void dbus_gfx_switch(DisplayChangeListener *dcl, } 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); @@ -685,6 +889,8 @@ static void dbus_cursor_define(DisplayChangeListener *dcl, DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); GVariant *v_data = NULL; + ddl_discard_cursor_messages(ddl); + v_data = g_variant_new_from_data( G_VARIANT_TYPE("ay"), c->data, @@ -751,10 +957,14 @@ dbus_display_listener_dispose(GObject *object) g_clear_object(&ddl->map_proxy); g_clear_object(&ddl->d3d11_proxy); g_clear_pointer(&ddl->peer_process, CloseHandle); -#ifdef CONFIG_OPENGL +#ifdef CONFIG_PIXMAN pixman_region32_fini(&ddl->gl_damage); +#endif +#ifdef CONFIG_OPENGL egl_fb_destroy(&ddl->fb); #endif +#else /* !WIN32 */ + g_clear_object(&ddl->scanout_dmabuf_v2_proxy); #endif G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object); @@ -787,7 +997,7 @@ dbus_display_listener_class_init(DBusDisplayListenerClass *klass) static void dbus_display_listener_init(DBusDisplayListener *ddl) { -#ifdef CONFIG_OPENGL +#ifdef CONFIG_PIXMAN pixman_region32_init(&ddl->gl_damage); #endif } @@ -804,7 +1014,6 @@ dbus_display_listener_get_console(DBusDisplayListener *ddl) return ddl->console; } -#ifdef WIN32 static bool dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface) { @@ -819,6 +1028,7 @@ dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface) return implements; } +#ifdef WIN32 static bool dbus_display_listener_setup_peer_process(DBusDisplayListener *ddl) { @@ -901,10 +1111,11 @@ dbus_display_listener_setup_d3d11(DBusDisplayListener *ddl) static void dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl) { -#ifdef WIN32 g_autoptr(GError) err = NULL; - if (!dbus_display_listener_implements(ddl, "org.qemu.Display1.Listener.Win32.Map")) { +#ifdef WIN32 + if (!dbus_display_listener_implements( + ddl, "org.qemu.Display1.Listener.Win32.Map")) { return; } @@ -925,7 +1136,97 @@ dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl) } 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 void dbus_display_listener_setup_scanout_dmabuf_v2(DBusDisplayListener *ddl) +{ +#ifndef WIN32 + g_autoptr(GError) err = NULL; + + if (!dbus_display_listener_implements( + ddl, "org.qemu.Display1.Listener.Unix.ScanoutDMABUF2")) { + return; + } + ddl->scanout_dmabuf_v2_proxy = + qemu_dbus_display1_listener_unix_scanout_dmabuf2_proxy_new_sync( + ddl->conn, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, + "/org/qemu/Display1/Listener", NULL, &err); + if (!ddl->scanout_dmabuf_v2_proxy) { + g_debug("Failed to setup Unix scanout dmabuf v2 proxy: %s", err->message); + return; + } +#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 * @@ -952,12 +1253,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); + dbus_display_listener_setup_scanout_dmabuf_v2(ddl); con = qemu_console_lookup_by_index(dbus_display_console_get_index(console)); assert(con); diff --git a/ui/dbus.c b/ui/dbus.c index 32f1bbe81ae..dd0336702de 100644 --- a/ui/dbus.c +++ b/ui/dbus.c @@ -28,7 +28,7 @@ #include "qemu/main-loop.h" #include "qemu/option.h" #include "qom/object_interfaces.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "ui/dbus-module.h" #ifdef CONFIG_OPENGL #include "ui/egl-helpers.h" @@ -176,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); } @@ -220,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")) { @@ -318,11 +317,17 @@ dbus_display_add_client(int csock, Error **errp) conn = g_socket_connection_factory_create_connection(socket); dbus_display->add_client_cancellable = g_cancellable_new(); + GDBusConnectionFlags flags = + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER | + G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING; + +#ifdef WIN32 + flags |= G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; +#endif g_dbus_connection_new(G_IO_STREAM(conn), guid, - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER | - G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING, + flags, NULL, dbus_display->add_client_cancellable, dbus_display_add_client_ready, @@ -399,7 +404,7 @@ set_gl_mode(Object *o, int val, Error **errp) } static void -dbus_display_class_init(ObjectClass *oc, void *data) +dbus_display_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); @@ -448,7 +453,7 @@ dbus_vc_parse(QemuOpts *opts, ChardevBackend *backend, } static void -dbus_vc_class_init(ObjectClass *oc, void *data) +dbus_vc_class_init(ObjectClass *oc, const void *data) { DBusVCClass *klass = DBUS_VC_CLASS(oc); ChardevClass *cc = CHARDEV_CLASS(oc); @@ -467,9 +472,9 @@ 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 (mode != DISPLAY_GL_MODE_OFF) { #ifdef CONFIG_OPENGL egl_init(opts->u.dbus.rendernode, mode, &error_fatal); #else @@ -477,13 +482,13 @@ early_dbus_init(DisplayOptions *opts) #endif } - type_register(&dbus_vc_type_info); + type_register_static(&dbus_vc_type_info); } 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"); @@ -509,7 +514,7 @@ static const TypeInfo dbus_display_info = { .instance_init = dbus_display_init, .instance_finalize = dbus_display_finalize, .class_init = dbus_display_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } @@ -519,6 +524,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/dmabuf.c b/ui/dmabuf.c new file mode 100644 index 00000000000..7433a268f0d --- /dev/null +++ b/ui/dmabuf.c @@ -0,0 +1,261 @@ +/* + * 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[DMABUF_MAX_PLANES]; + uint32_t width; + uint32_t height; + uint32_t offset[DMABUF_MAX_PLANES]; + uint32_t stride[DMABUF_MAX_PLANES]; + uint32_t num_planes; + 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, + const uint32_t *offset, const uint32_t *stride, + uint32_t x, uint32_t y, + uint32_t backing_width, uint32_t backing_height, + uint32_t fourcc, uint64_t modifier, + const int32_t *dmabuf_fd, uint32_t num_planes, + bool allow_fences, bool y0_top) { + QemuDmaBuf *dmabuf; + + assert(num_planes > 0 && num_planes <= DMABUF_MAX_PLANES); + + dmabuf = g_new0(QemuDmaBuf, 1); + + dmabuf->width = width; + dmabuf->height = height; + memcpy(dmabuf->offset, offset, num_planes * sizeof(*offset)); + memcpy(dmabuf->stride, stride, num_planes * sizeof(*stride)); + dmabuf->x = x; + dmabuf->y = y; + dmabuf->backing_width = backing_width; + dmabuf->backing_height = backing_height; + dmabuf->fourcc = fourcc; + dmabuf->modifier = modifier; + memcpy(dmabuf->fd, dmabuf_fd, num_planes * sizeof(*dmabuf_fd)); + dmabuf->allow_fences = allow_fences; + dmabuf->y0_top = y0_top; + dmabuf->fence_fd = -1; + dmabuf->num_planes = num_planes; + + return dmabuf; +} + +void qemu_dmabuf_free(QemuDmaBuf *dmabuf) +{ + if (dmabuf == NULL) { + return; + } + + g_free(dmabuf); +} + +const int *qemu_dmabuf_get_fds(QemuDmaBuf *dmabuf, int *nfds) +{ + assert(dmabuf != NULL); + + if (nfds) { + *nfds = ARRAY_SIZE(dmabuf->fd); + } + + return dmabuf->fd; +} + +void qemu_dmabuf_dup_fds(QemuDmaBuf *dmabuf, int *fds, int nfds) +{ + int i; + + assert(dmabuf != NULL); + assert(nfds >= dmabuf->num_planes); + + for (i = 0; i < dmabuf->num_planes; i++) { + fds[i] = dmabuf->fd[i] >= 0 ? dup(dmabuf->fd[i]) : -1; + } +} + +void qemu_dmabuf_close(QemuDmaBuf *dmabuf) +{ + int i; + + assert(dmabuf != NULL); + + for (i = 0; i < dmabuf->num_planes; i++) { + if (dmabuf->fd[i] >= 0) { + close(dmabuf->fd[i]); + dmabuf->fd[i] = -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; +} + +const uint32_t *qemu_dmabuf_get_offsets(QemuDmaBuf *dmabuf, int *noffsets) +{ + assert(dmabuf != NULL); + + if (noffsets) { + *noffsets = ARRAY_SIZE(dmabuf->offset); + } + + return dmabuf->offset; +} + +const uint32_t *qemu_dmabuf_get_strides(QemuDmaBuf *dmabuf, int *nstrides) +{ + assert(dmabuf != NULL); + + if (nstrides) { + *nstrides = ARRAY_SIZE(dmabuf->stride); + } + + return dmabuf->stride; +} + +uint32_t qemu_dmabuf_get_num_planes(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->num_planes; +} + +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; +} diff --git a/ui/egl-context.c b/ui/egl-context.c index 9e0df466f3f..aed3e3ba1f3 100644 --- a/ui/egl-context.c +++ b/ui/egl-context.c @@ -17,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(), diff --git a/ui/egl-headless.c b/ui/egl-headless.c index d5637dadb2d..1f6b845500d 100644 --- a/ui/egl-headless.c +++ b/ui/egl-headless.c @@ -85,29 +85,38 @@ static void egl_scanout_texture(DisplayChangeListener *dcl, 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, NULL); + 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); } @@ -198,7 +207,7 @@ static const DisplayGLCtxOps eglctx_ops = { static void early_egl_headless_init(DisplayOptions *opts) { - DisplayGLMode mode = DISPLAYGL_MODE_ON; + DisplayGLMode mode = DISPLAY_GL_MODE_ON; if (opts->has_gl) { mode = opts->gl; diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 3d19dbe382c..5503a795e4a 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -20,9 +20,10 @@ #include "qemu/error-report.h" #include "ui/console.h" #include "ui/egl-helpers.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "trace.h" +#include "standard-headers/drm/drm_fourcc.h" EGLDisplay *qemu_egl_display; EGLConfig qemu_egl_config; @@ -92,14 +93,18 @@ void egl_fb_destroy(egl_fb *fb) fb->width = 0; fb->height = 0; + fb->x = 0; + fb->y = 0; fb->texture = 0; fb->framebuffer = 0; } -void egl_fb_setup_default(egl_fb *fb, int width, int height) +void egl_fb_setup_default(egl_fb *fb, int width, int height, int x, int y) { fb->width = width; fb->height = height; + fb->x = x; + fb->y = y; fb->framebuffer = 0; /* default framebuffer */ } @@ -144,12 +149,13 @@ void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip) glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->framebuffer); glViewport(0, 0, dst->width, dst->height); + glClear(GL_COLOR_BUFFER_BIT); if (src->dmabuf) { - x1 = src->dmabuf->x; - y1 = src->dmabuf->y; - w = src->dmabuf->width; - h = src->dmabuf->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; @@ -160,7 +166,8 @@ void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip) x2 = x1 + w; glBlitFramebuffer(x1, y1, x2, y2, - 0, 0, dst->width, dst->height, + dst->x, dst->y, + dst->x + dst->width, dst->y + dst->height, GL_COLOR_BUFFER_BIT, GL_LINEAR); } @@ -257,6 +264,11 @@ int egl_rendernode_init(const char *rendernode, DisplayGLMode mode) error_report("egl: EGL_MESA_image_dma_buf_export not supported"); goto err; } + if (!epoxy_has_egl_extension(qemu_egl_display, + "EGL_EXT_image_dma_buf_import_modifiers")) { + error_report("egl: EGL_EXT_image_dma_buf_import_modifiers not supported"); + goto err; + } qemu_egl_rn_ctx = qemu_egl_init_ctx(); if (!qemu_egl_rn_ctx) { @@ -277,63 +289,109 @@ int egl_rendernode_init(const char *rendernode, DisplayGLMode mode) return -1; } -int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc, - EGLuint64KHR *modifier) +bool egl_dmabuf_export_texture(uint32_t tex_id, int *fd, EGLint *offset, + EGLint *stride, EGLint *fourcc, int *num_planes, + EGLuint64KHR *modifier) { EGLImageKHR image; - EGLint num_planes, fd; + EGLuint64KHR modifiers[DMABUF_MAX_PLANES]; image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(unsigned long)tex_id, NULL); if (!image) { - return -1; + return false; } eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc, - &num_planes, modifier); - if (num_planes != 1) { - eglDestroyImageKHR(qemu_egl_display, image); - return -1; - } - eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL); + num_planes, modifiers); + eglExportDMABUFImageMESA(qemu_egl_display, image, fd, stride, offset); eglDestroyImageKHR(qemu_egl_display, image); - return fd; + /* Only first modifier matters. */ + if (modifier) { + *modifier = modifiers[0]; + } + + return true; } void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) { EGLImageKHR image = EGL_NO_IMAGE_KHR; EGLint attrs[64]; - int i = 0; + int i = 0, j; + uint64_t modifier = qemu_dmabuf_get_modifier(dmabuf); + uint32_t texture = qemu_dmabuf_get_texture(dmabuf); + int nfds, noffsets, nstrides; + const int *fds = qemu_dmabuf_get_fds(dmabuf, &nfds); + const uint32_t *offsets = qemu_dmabuf_get_offsets(dmabuf, &noffsets); + const uint32_t *strides = qemu_dmabuf_get_strides(dmabuf, &nstrides); + uint32_t num_planes = qemu_dmabuf_get_num_planes(dmabuf); + + EGLint fd_attrs[] = { + EGL_DMA_BUF_PLANE0_FD_EXT, + EGL_DMA_BUF_PLANE1_FD_EXT, + EGL_DMA_BUF_PLANE2_FD_EXT, + EGL_DMA_BUF_PLANE3_FD_EXT, + }; + EGLint offset_attrs[] = { + EGL_DMA_BUF_PLANE0_OFFSET_EXT, + EGL_DMA_BUF_PLANE1_OFFSET_EXT, + EGL_DMA_BUF_PLANE2_OFFSET_EXT, + EGL_DMA_BUF_PLANE3_OFFSET_EXT, + }; + EGLint stride_attrs[] = { + EGL_DMA_BUF_PLANE0_PITCH_EXT, + EGL_DMA_BUF_PLANE1_PITCH_EXT, + EGL_DMA_BUF_PLANE2_PITCH_EXT, + EGL_DMA_BUF_PLANE3_PITCH_EXT, + }; + EGLint modifier_lo_attrs[] = { + EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, + }; + EGLint modifier_hi_attrs[] = { + EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, + EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, + EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT, + EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT, + }; - if (dmabuf->texture != 0) { + if (texture != 0) { return; } + assert(nfds >= num_planes); + assert(noffsets >= num_planes); + assert(nstrides >= num_planes); + attrs[i++] = EGL_WIDTH; - attrs[i++] = dmabuf->backing_width; + attrs[i++] = qemu_dmabuf_get_backing_width(dmabuf); attrs[i++] = EGL_HEIGHT; - attrs[i++] = dmabuf->backing_height; + attrs[i++] = qemu_dmabuf_get_backing_height(dmabuf); attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT; - attrs[i++] = dmabuf->fourcc; - - attrs[i++] = EGL_DMA_BUF_PLANE0_FD_EXT; - attrs[i++] = dmabuf->fd; - attrs[i++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; - attrs[i++] = dmabuf->stride; - attrs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; - attrs[i++] = 0; -#ifdef EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT - if (dmabuf->modifier) { - attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; - attrs[i++] = (dmabuf->modifier >> 0) & 0xffffffff; - attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; - attrs[i++] = (dmabuf->modifier >> 32) & 0xffffffff; + attrs[i++] = qemu_dmabuf_get_fourcc(dmabuf); + + for (j = 0; j < num_planes; j++) { + attrs[i++] = fd_attrs[j]; + /* fd[1-3] may be -1 if using a joint buffer for all planes */ + attrs[i++] = fds[j] >= 0 ? fds[j] : fds[0]; + attrs[i++] = stride_attrs[j]; + attrs[i++] = strides[j]; + attrs[i++] = offset_attrs[j]; + attrs[i++] = offsets[j]; + if (modifier != DRM_FORMAT_MOD_INVALID) { + attrs[i++] = modifier_lo_attrs[j]; + attrs[i++] = (modifier >> 0) & 0xffffffff; + attrs[i++] = modifier_hi_attrs[j]; + attrs[i++] = (modifier >> 32) & 0xffffffff; + } } -#endif + attrs[i++] = EGL_NONE; image = eglCreateImageKHR(qemu_egl_display, @@ -345,8 +403,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); @@ -356,12 +415,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) @@ -375,18 +437,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); } } @@ -492,7 +558,7 @@ 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) { @@ -522,7 +588,7 @@ static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, 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; } @@ -553,8 +619,8 @@ int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode) int qemu_egl_init_dpy_win32(EGLNativeDisplayType dpy, DisplayGLMode mode) { /* prefer GL ES, as that's what ANGLE supports */ - if (mode == DISPLAYGL_MODE_ON) { - mode = DISPLAYGL_MODE_ES; + if (mode == DISPLAY_GL_MODE_ON) { + mode = DISPLAY_GL_MODE_ES; } if (qemu_egl_init_dpy(dpy, 0, mode) < 0) { @@ -607,7 +673,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; @@ -631,7 +697,7 @@ bool egl_init(const char *rendernode, DisplayGLMode mode, Error **errp) { ERRP_GUARD(); - if (mode == DISPLAYGL_MODE_OFF) { + if (mode == DISPLAY_GL_MODE_OFF) { error_setg(errp, "egl: turning off GL doesn't make sense"); return false; } diff --git a/ui/gtk-clipboard.c b/ui/gtk-clipboard.c index 8d8a636fd1f..65d89ec601d 100644 --- a/ui/gtk-clipboard.c +++ b/ui/gtk-clipboard.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu/main-loop.h" #include "ui/gtk.h" @@ -95,11 +96,13 @@ static void gd_clipboard_update_info(GtkDisplayState *gd, gtk_clipboard_clear(gd->gtkcb[s]); if (targets) { gd->cbowner[s] = true; - gtk_clipboard_set_with_data(gd->gtkcb[s], - targets, n_targets, - gd_clipboard_get_data, - gd_clipboard_clear, - gd); + if (!gtk_clipboard_set_with_data(gd->gtkcb[s], + targets, n_targets, + gd_clipboard_get_data, + gd_clipboard_clear, + gd)) { + warn_report("Failed to set GTK clipboard"); + } gtk_target_table_free(targets, n_targets); } diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index a1060fd80f2..0b787bea25e 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -22,7 +22,7 @@ #include "ui/egl-helpers.h" #include "ui/shader.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout) { @@ -68,38 +68,44 @@ 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, pw, ph, gs; if (!vc->gfx.gls) { return; } window = gtk_widget_get_window(vc->gfx.drawing_area); + gs = gdk_window_get_scale_factor(window); ww = gdk_window_get_width(window); wh = gdk_window_get_height(window); + pw = ww * gs; + ph = wh * gs; 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 gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h); - vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds); - vc->gfx.scale_y = (double)wh / surface_height(vc->gfx.ds); + gd_update_scale(vc, ww, wh, + surface_width(vc->gfx.ds), + surface_height(vc->gfx.ds)); glFlush(); #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); @@ -112,13 +118,14 @@ void gd_egl_draw(VirtualConsole *vc) eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, vc->gfx.esurface, vc->gfx.ectx); - surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh); + surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, pw, ph); surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); eglSwapBuffers(qemu_egl_display, vc->gfx.esurface); - vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds); - vc->gfx.scale_y = (double)wh / surface_height(vc->gfx.ds); + gd_update_scale(vc, ww, wh, + surface_width(vc->gfx.ds), + surface_height(vc->gfx.ds)); glFlush(); } @@ -148,7 +155,9 @@ 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 && vc->gfx.guest_fb.dmabuf->draw_submitted) { + if (vc->gfx.guest_fb.dmabuf && + qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { + gd_egl_draw(vc); return; } @@ -243,6 +252,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); @@ -256,22 +272,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->backing_width, dmabuf->backing_height, - dmabuf->x, dmabuf->y, dmabuf->width, - dmabuf->height, NULL); + 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 @@ -283,15 +307,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->backing_width, dmabuf->backing_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); } @@ -312,7 +340,11 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl, { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GdkWindow *window; - int ww, wh; + int px_offset, py_offset; + int gs; + int pw_widget, ph_widget, pw_surface, ph_surface; + int ww_widget, wh_widget, ww_surface, wh_surface; + int fbw, fbh; if (!vc->gfx.scanout_mode) { return; @@ -325,9 +357,32 @@ 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); - egl_fb_setup_default(&vc->gfx.win_fb, ww, wh); + gs = gdk_window_get_scale_factor(window); + ww_widget = gdk_window_get_width(window); + wh_widget = gdk_window_get_height(window); + fbw = surface_width(vc->gfx.ds); + fbh = surface_height(vc->gfx.ds); + + gd_update_scale(vc, ww_widget, wh_widget, fbw, fbh); + + ww_surface = fbw * vc->gfx.scale_x; + wh_surface = fbh * vc->gfx.scale_y; + pw_widget = ww_widget * gs; + ph_widget = wh_widget * gs; + pw_surface = ww_surface * gs; + ph_surface = wh_surface * gs; + + px_offset = 0; + py_offset = 0; + if (pw_widget > pw_surface) { + px_offset = (pw_widget - pw_surface) / 2; + } + if (ph_widget > ph_surface) { + py_offset = (ph_widget - ph_surface) / 2; + } + + egl_fb_setup_default(&vc->gfx.win_fb, pw_surface, ph_surface, + px_offset, py_offset); if (vc->gfx.cursor_fb.texture) { egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb, vc->gfx.y0_top); @@ -354,9 +409,10 @@ void gd_egl_flush(DisplayChangeListener *dcl, VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GtkWidget *area = vc->gfx.drawing_area; - if (vc->gfx.guest_fb.dmabuf && !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; diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 52dcac161e2..8151cc413cf 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -16,7 +16,7 @@ #include "ui/gtk.h" #include "ui/egl-helpers.h" -#include "sysemu/sysemu.h" +#include "system/system.h" static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, bool scanout) { @@ -42,16 +42,37 @@ void gd_gl_area_draw(VirtualConsole *vc) #ifdef CONFIG_GBM QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; #endif - int ww, wh, ws, y1, y2; + int pw, ph, gs, y1, y2; + int ww, wh; + int ww_surface, wh_surface; + int fbw, fbh; + int wx_offset, wy_offset; if (!vc->gfx.gls) { return; } gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); - ws = gdk_window_get_scale_factor(gtk_widget_get_window(vc->gfx.drawing_area)); - ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area) * ws; - wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area) * ws; + gs = gdk_window_get_scale_factor(gtk_widget_get_window(vc->gfx.drawing_area)); + fbw = surface_width(vc->gfx.ds); + fbh = surface_height(vc->gfx.ds); + ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area); + wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area); + pw = ww * gs; + ph = wh * gs; + + gd_update_scale(vc, ww, wh, fbw, fbh); + + ww_surface = fbw * vc->gfx.scale_x; + wh_surface = fbh * vc->gfx.scale_y; + + wx_offset = wy_offset = 0; + if (ww > ww_surface) { + wx_offset = (ww - ww_surface) / 2; + } + if (wh > wh_surface) { + wy_offset = (wh - wh_surface) / 2; + } if (vc->gfx.scanout_mode) { if (!vc->gfx.guest_fb.framebuffer) { @@ -60,10 +81,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 @@ -71,11 +92,29 @@ void gd_gl_area_draw(VirtualConsole *vc) glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.guest_fb.framebuffer); /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */ - glViewport(0, 0, ww, wh); + if (wx_offset > 0) { + glEnable(GL_SCISSOR_TEST); + glScissor(0, 0, wx_offset * gs, wh * gs); + glClear(GL_COLOR_BUFFER_BIT); + glScissor((ww - wx_offset) * gs, 0, wx_offset * gs, wh * gs); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + } + if (wy_offset > 0) { + glEnable(GL_SCISSOR_TEST); + glScissor(0, 0, ww * gs, wy_offset * gs); + glClear(GL_COLOR_BUFFER_BIT); + glScissor(0, (wh - wy_offset) * gs, ww * gs, wy_offset * gs); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + } + + glViewport(0, 0, pw, ph); y1 = vc->gfx.y0_top ? 0 : vc->gfx.h; y2 = vc->gfx.y0_top ? vc->gfx.h : 0; glBlitFramebuffer(0, y1, vc->gfx.w, y2, - 0, 0, ww, wh, + wx_offset * gs, wy_offset * gs, + (ww - wx_offset) * gs, (wh - wy_offset) * gs, GL_COLOR_BUFFER_BIT, GL_NEAREST); #ifdef CONFIG_GBM if (dmabuf) { @@ -85,9 +124,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); @@ -99,7 +140,7 @@ void gd_gl_area_draw(VirtualConsole *vc) } gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); - surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh); + surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, pw, ph); surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); } } @@ -125,7 +166,9 @@ 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 && vc->gfx.guest_fb.dmabuf->draw_submitted) { + if (vc->gfx.guest_fb.dmabuf && + qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { + gd_gl_area_draw(vc); return; } @@ -285,9 +328,10 @@ 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)); @@ -298,20 +342,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->backing_width, dmabuf->backing_height, - dmabuf->x, dmabuf->y, dmabuf->width, - dmabuf->height, NULL); + 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); + + gd_gl_area_scanout_texture(dcl, texture, y0_top, + backing_width, backing_height, + x, y, width, height, NULL); - if (dmabuf->allow_fences) { + 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 e09f97a86b7..8c4a94c8f6b 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -38,6 +38,7 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +#include "qemu-main.h" #include "ui/console.h" #include "ui/gtk.h" @@ -55,8 +56,8 @@ #include "trace.h" #include "ui/input.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/runstate.h" +#include "system/system.h" #include "keymaps.h" #include "chardev/char.h" #include "qom/object.h" @@ -204,7 +205,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); @@ -386,16 +387,16 @@ static void *gd_win32_get_hwnd(VirtualConsole *vc) /** DisplayState Callbacks **/ static void gd_update(DisplayChangeListener *dcl, - int x, int y, int w, int h) + int fbx, int fby, int fbw, int fbh) { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GdkWindow *win; - int x1, x2, y1, y2; - int mx, my; - int fbw, fbh; - int ww, wh; + int wx1, wx2, wy1, wy2; + int wx_offset, wy_offset; + int ww_surface, wh_surface; + int ww_widget, wh_widget; - trace_gd_update(vc->label, x, y, w, h); + trace_gd_update(vc->label, fbx, fby, fbw, fbh); if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { return; @@ -404,35 +405,36 @@ static void gd_update(DisplayChangeListener *dcl, if (vc->gfx.convert) { pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image, NULL, vc->gfx.convert, - x, y, 0, 0, x, y, w, h); + fbx, fby, 0, 0, fbx, fby, fbw, fbh); } - x1 = floor(x * vc->gfx.scale_x); - y1 = floor(y * vc->gfx.scale_y); + wx1 = floor(fbx * vc->gfx.scale_x); + wy1 = floor(fby * vc->gfx.scale_y); - x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x); - y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y); + wx2 = ceil(fbx * vc->gfx.scale_x + fbw * vc->gfx.scale_x); + wy2 = ceil(fby * vc->gfx.scale_y + fbh * vc->gfx.scale_y); - fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; - fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; + ww_surface = surface_width(vc->gfx.ds) * vc->gfx.scale_x; + wh_surface = surface_height(vc->gfx.ds) * vc->gfx.scale_y; win = gtk_widget_get_window(vc->gfx.drawing_area); if (!win) { return; } - ww = gdk_window_get_width(win); - wh = gdk_window_get_height(win); + ww_widget = gdk_window_get_width(win); + wh_widget = gdk_window_get_height(win); - mx = my = 0; - if (ww > fbw) { - mx = (ww - fbw) / 2; + wx_offset = wy_offset = 0; + if (ww_widget > ww_surface) { + wx_offset = (ww_widget - ww_surface) / 2; } - if (wh > fbh) { - my = (wh - fbh) / 2; + if (wh_widget > wh_surface) { + wy_offset = (wh_widget - wh_surface) / 2; } gtk_widget_queue_draw_area(vc->gfx.drawing_area, - mx + x1, my + y1, (x2 - x1), (y2 - y1)); + wx_offset + wx1, wy_offset + wy1, + (wx2 - wx1), (wy2 - wy1)); } static void gd_refresh(DisplayChangeListener *dcl) @@ -446,14 +448,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 (!gtk_widget_get_realized(vc->gfx.drawing_area) || - qemu_input_is_absolute()) { + qemu_input_is_absolute(dcl->con)) { return; } @@ -596,11 +598,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) **/ @@ -689,7 +695,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); @@ -726,6 +732,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); @@ -735,6 +745,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; @@ -758,8 +772,21 @@ static void gd_resize_event(GtkGLArea *area, gint width, gint height, gpointer *opaque) { VirtualConsole *vc = (void *)opaque; + double pw = width, ph = height; + double sx = vc->gfx.scale_x, sy = vc->gfx.scale_y; + GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(area)); + const int gs = gdk_window_get_scale_factor(window); - gd_set_ui_size(vc, width, height); + if (!vc->s->free_scale && !vc->s->full_screen) { + pw /= sx; + ph /= sy; + } + + /** + * width and height here are in pixel coordinate, so we must divide it + * by global window scale (gs) + */ + gd_set_ui_size(vc, pw / gs, ph / gs); } #endif @@ -787,12 +814,95 @@ void gd_update_monitor_refresh_rate(VirtualConsole *vc, GtkWidget *widget) #endif } +void gd_update_scale(VirtualConsole *vc, int ww, int wh, int fbw, int fbh) +{ + if (!vc) { + return; + } + + if (vc->s->full_screen) { + vc->gfx.scale_x = (double)ww / fbw; + vc->gfx.scale_y = (double)wh / fbh; + } else if (vc->s->free_scale) { + double sx, sy; + + sx = (double)ww / fbw; + sy = (double)wh / fbh; + + vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); + } +} +/** + * DOC: Coordinate handling. + * + * We are coping with sizes and positions in various coordinates and the + * handling of these coordinates is somewhat confusing. It would benefit us + * all if we define these coordinates explicitly and clearly. Besides, it's + * also helpful to follow the same naming convention for variables + * representing values in different coordinates. + * + * I. Definitions + * + * - (guest) buffer coordinate: this is the coordinates that the guest will + * see. The x/y offsets and width/height specified in commands sent by + * guest is basically in buffer coordinate. + * + * - (host) pixel coordinate: this is the coordinate in pixel level on the + * host destop. A window/widget of width 300 in pixel coordinate means it + * occupies 300 pixels horizontally. + * + * - (host) logical window coordinate: the existence of global scaling + * factor in desktop level makes this kind of coordinate play a role. It + * always holds that (logical window size) * (global scale factor) = + * (pixel size). + * + * - global scale factor: this is specified in desktop level and is + * typically invariant during the life cycle of the process. Users with + * high-DPI monitors might set this scale, for example, to 2, in order to + * make the UI look larger. + * + * - zooming scale: this can be freely controlled by the QEMU user to zoom + * in/out the guest content. + * + * II. Representation + * + * We'd like to use consistent representation for variables in different + * coordinates: + * - buffer coordinate: prefix fb + * - pixel coordinate: prefix p + * - logical window coordinate: prefix w + * + * For scales: + * - global scale factor: prefix gs + * - zooming scale: prefix scale/s + * + * Example: fbw, pw, ww for width in different coordinates + * + * III. Equation + * + * - fbw * gs * scale_x = pw + * - pw = gs * ww + * + * Consequently we have + * + * - fbw * scale_x = ww + * + * Example: assuming we are running QEMU on a 3840x2160 screen and have set + * global scaling factor to 2, if the guest buffer size is 1920x1080 and the + * zooming scale is 0.5, then we have: + * - fbw = 1920, fbh = 1080 + * - pw = 1920, ph = 1080 + * - ww = 960, wh = 540 + * A bonus of this configuration is that we can achieve pixel to pixel + * presentation of the guest content. + */ + static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) { VirtualConsole *vc = opaque; GtkDisplayState *s = vc->s; - int mx, my; - int ww, wh; + int wx_offset, wy_offset; + int ww_widget, wh_widget, ww_surface, wh_surface; int fbw, fbh; #if defined(CONFIG_OPENGL) @@ -826,46 +936,37 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) fbw = surface_width(vc->gfx.ds); fbh = surface_height(vc->gfx.ds); - ww = gdk_window_get_width(gtk_widget_get_window(widget)); - wh = gdk_window_get_height(gtk_widget_get_window(widget)); + ww_widget = gdk_window_get_width(gtk_widget_get_window(widget)); + wh_widget = gdk_window_get_height(gtk_widget_get_window(widget)); - if (s->full_screen) { - vc->gfx.scale_x = (double)ww / fbw; - vc->gfx.scale_y = (double)wh / fbh; - } else if (s->free_scale) { - double sx, sy; - - sx = (double)ww / fbw; - sy = (double)wh / fbh; - - vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); - } + gd_update_scale(vc, ww_widget, wh_widget, fbw, fbh); - fbw *= vc->gfx.scale_x; - fbh *= vc->gfx.scale_y; + ww_surface = fbw * vc->gfx.scale_x; + wh_surface = fbh * vc->gfx.scale_y; - mx = my = 0; - if (ww > fbw) { - mx = (ww - fbw) / 2; + wx_offset = wy_offset = 0; + if (ww_widget > ww_surface) { + wx_offset = (ww_widget - ww_surface) / 2; } - if (wh > fbh) { - my = (wh - fbh) / 2; + if (wh_widget > wh_surface) { + wy_offset = (wh_widget - wh_surface) / 2; } - cairo_rectangle(cr, 0, 0, ww, wh); + cairo_rectangle(cr, 0, 0, ww_widget, wh_widget); /* Optionally cut out the inner area where the pixmap will be drawn. This avoids 'flashing' since we're not double-buffering. Note we're using the undocumented behaviour of drawing the rectangle from right to left to cut out the whole */ - cairo_rectangle(cr, mx + fbw, my, - -1 * fbw, fbh); + cairo_rectangle(cr, wx_offset + ww_surface, wy_offset, + -1 * ww_surface, wh_surface); cairo_fill(cr); cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y); cairo_set_source_surface(cr, vc->gfx.surface, - mx / vc->gfx.scale_x, my / vc->gfx.scale_y); + wx_offset / vc->gfx.scale_x, + wy_offset / vc->gfx.scale_y); cairo_paint(cr); return TRUE; @@ -876,62 +977,73 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, { VirtualConsole *vc = opaque; GtkDisplayState *s = vc->s; - int x, y; - int mx, my; - int fbh, fbw; - int ww, wh, ws; + int fbx, fby; + int wx_offset, wy_offset; + int wh_surface, ww_surface; + int ww_widget, wh_widget; if (!vc->gfx.ds) { return TRUE; } - fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; - fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; + ww_surface = surface_width(vc->gfx.ds) * vc->gfx.scale_x; + wh_surface = surface_height(vc->gfx.ds) * vc->gfx.scale_y; + ww_widget = gtk_widget_get_allocated_width(widget); + wh_widget = gtk_widget_get_allocated_height(widget); - ww = gtk_widget_get_allocated_width(widget); - wh = gtk_widget_get_allocated_height(widget); - ws = gtk_widget_get_scale_factor(widget); - - mx = my = 0; - if (ww > fbw) { - mx = (ww - fbw) / 2; + /* + * `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. + */ + wx_offset = wy_offset = 0; + if (ww_widget > ww_surface) { + wx_offset = (ww_widget - ww_surface) / 2; } - if (wh > fbh) { - my = (wh - fbh) / 2; + if (wh_widget > wh_surface) { + wy_offset = (wh_widget - wh_surface) / 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`. + */ + fbx = (motion->x - wx_offset) / vc->gfx.scale_x; + fby = (motion->y - wy_offset) / vc->gfx.scale_y; - if (qemu_input_is_absolute()) { - if (x < 0 || y < 0 || - x >= surface_width(vc->gfx.ds) || - y >= surface_height(vc->gfx.ds)) { + trace_gd_motion_event(ww_widget, wh_widget, + gtk_widget_get_scale_factor(widget), fbx, fby); + + if (qemu_input_is_absolute(vc->gfx.dcl.con)) { + if (fbx < 0 || fby < 0 || + fbx >= surface_width(vc->gfx.ds) || + fby >= surface_height(vc->gfx.ds)) { return TRUE; } - qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x, + qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, fbx, 0, surface_width(vc->gfx.ds)); - qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y, + qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, fby, 0, surface_height(vc->gfx.ds)); qemu_input_event_sync(); } else if (s->last_set && s->ptr_owner == vc) { - qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x); - qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y); + qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, fbx - s->last_x); + qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, fby - s->last_y); qemu_input_event_sync(); } - s->last_x = x; - s->last_y = y; + s->last_x = fbx; + s->last_y = fby; 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); @@ -942,13 +1054,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; } @@ -965,7 +1077,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); @@ -1392,7 +1504,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; } @@ -1737,8 +1849,16 @@ static gboolean gd_configure(GtkWidget *widget, GdkEventConfigure *cfg, gpointer opaque) { VirtualConsole *vc = opaque; + const double sx = vc->gfx.scale_x, sy = vc->gfx.scale_y; + double width = cfg->width, height = cfg->height; + + if (!vc->s->free_scale && !vc->s->full_screen) { + width /= sx; + height /= sy; + } + + gd_set_ui_size(vc, width, height); - gd_set_ui_size(vc, cfg->width, cfg->height); return FALSE; } @@ -1798,7 +1918,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; @@ -1856,7 +1976,7 @@ static void gd_vc_open(Chardev *chr, *be_opened = false; } -static void char_gd_vc_class_init(ObjectClass *oc, void *data) +static void char_gd_vc_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); @@ -1921,8 +2041,7 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, vcd->console = vc; snprintf(buffer, sizeof(buffer), "vc%d", idx); - vc->label = g_strdup_printf("%s", vc->vte.chr->label - ? vc->vte.chr->label : buffer); + vc->label = g_strdup(vc->vte.chr->label ? : buffer); group = gd_vc_menu_init(s, vc, idx, group, view_menu); vc->vte.terminal = vte_terminal_new(); @@ -2363,6 +2482,7 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) GdkDisplay *window_display; GtkIconTheme *theme; char *dir; + int idx; if (!gtkinit) { fprintf(stderr, "gtk initialization failed\n"); @@ -2426,6 +2546,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); @@ -2453,6 +2582,9 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) #ifdef CONFIG_GTK_CLIPBOARD gd_clipboard_init(s); #endif /* CONFIG_GTK_CLIPBOARD */ + + /* GTK's event polling must happen on the main thread. */ + qemu_main = NULL; } static void early_gtk_display_init(DisplayOptions *opts) @@ -2482,7 +2614,7 @@ 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())) { @@ -2498,7 +2630,7 @@ static void early_gtk_display_init(DisplayOptions *opts) #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 } @@ -2508,7 +2640,7 @@ static void early_gtk_display_init(DisplayOptions *opts) keycode_map = gd_get_keymap(&keycode_maplen); #if defined(CONFIG_VTE) - type_register(&char_gd_vc_type_info); + type_register_static(&char_gd_vc_type_info); #endif } @@ -2516,6 +2648,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-barrier.c b/ui/input-barrier.c index 2d57ca70791..9793258aac1 100644 --- a/ui/input-barrier.c +++ b/ui/input-barrier.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qemu/main-loop.h" #include "qemu/sockets.h" #include "qapi/error.h" @@ -696,7 +696,7 @@ static void input_barrier_instance_init(Object *obj) ib->height = 1080; } -static void input_barrier_class_init(ObjectClass *oc, void *data) +static void input_barrier_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); @@ -732,7 +732,7 @@ static const TypeInfo input_barrier_info = { .instance_size = sizeof(InputBarrier), .instance_init = input_barrier_instance_init, .instance_finalize = input_barrier_instance_finalize, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/ui/input-legacy.c b/ui/input-legacy.c index 46ea74e44d6..ca4bccb4113 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-linux.c b/ui/input-linux.c index e572a2e905b..92e1a1aa64a 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -12,7 +12,7 @@ #include "qemu/sockets.h" #include "ui/input.h" #include "qom/object_interfaces.h" -#include "sysemu/iothread.h" +#include "system/iothread.h" #include "block/aio.h" #include @@ -412,7 +412,6 @@ static void input_linux_complete(UserCreatable *uc, Error **errp) err_close: close(il->fd); - return; } static void input_linux_instance_finalize(Object *obj) @@ -495,7 +494,7 @@ static void input_linux_instance_init(Object *obj) { } -static void input_linux_class_init(ObjectClass *oc, void *data) +static void input_linux_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); @@ -523,7 +522,7 @@ static const TypeInfo input_linux_info = { .instance_size = sizeof(InputLinux), .instance_init = input_linux_instance_init, .instance_finalize = input_linux_instance_finalize, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/ui/input.c b/ui/input.c index 1aad64b07c4..147e69c1c3c 100644 --- a/ui/input.c +++ b/ui/input.c @@ -1,16 +1,16 @@ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "qapi/qapi-commands-ui.h" #include "trace.h" #include "ui/input.h" #include "ui/console.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" +#include "system/replay.h" +#include "system/runstate.h" struct QemuInputHandlerState { DeviceState *dev; - QemuInputHandler *handler; + const QemuInputHandler *handler; int id; int events; QemuConsole *con; @@ -46,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; @@ -56,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; } @@ -64,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, @@ -174,37 +174,6 @@ void qmp_input_send_event(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; @@ -340,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) { @@ -494,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); } @@ -583,21 +547,6 @@ void qemu_input_queue_mtt_abs(QemuConsole *src, InputAxis axis, int value, qemu_input_event_send(src, &evt); } -void qemu_input_check_mode_change(void) -{ - static int current_is_absolute; - int is_absolute; - - is_absolute = qemu_input_is_absolute(); - - if (is_absolute != current_is_absolute) { - trace_input_mouse_mode(is_absolute); - notifier_list_notify(&mouse_mode_notifiers, NULL); - } - - current_is_absolute = is_absolute; -} - void qemu_add_mouse_mode_change_notifier(Notifier *notify) { notifier_list_add(&mouse_mode_notifiers, notify); @@ -657,6 +606,6 @@ bool qemu_mouse_set(int index, Error **errp) } qemu_input_handler_activate(s); - qemu_input_check_mode_change(); + notifier_list_notify(&mouse_mode_notifiers, NULL); return true; } diff --git a/ui/kbd-state.c b/ui/kbd-state.c index 62d42a7a22e..52ed28b8a89 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/meson.build b/ui/meson.build index 0a1e8272a32..6371422c460 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -1,13 +1,10 @@ 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 - system_ss.add(png) system_ss.add(files( 'clipboard.c', 'console.c', - 'console-vc.c', 'cursor.c', + 'dmabuf.c', 'input-keymap.c', 'input-legacy.c', 'input-barrier.c', @@ -19,16 +16,16 @@ system_ss.add(files( 'ui-qmp-cmds.c', 'util.c', )) +system_ss.add(when: pixman, if_true: files('console-vc.c'), if_false: files('console-vc-stubs.c')) if dbus_display system_ss.add(files('dbus-module.c')) endif system_ss.add([spice_headers, files('spice-module.c')]) system_ss.add(when: spice_protocol, if_true: files('vdagent.c')) -system_ss.add(when: 'CONFIG_LINUX', if_true: files( - 'input-linux.c', - 'udmabuf.c', -)) +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() @@ -44,9 +41,9 @@ 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')) -system_ss.add_all(when: vnc, if_true: vnc_ss) +system_ss.add_all(when: [vnc, pixman], if_true: vnc_ss) system_ss.add(when: vnc, if_false: files('vnc-stubs.c')) ui_modules = {} @@ -60,8 +57,8 @@ endif 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 @@ -76,7 +73,7 @@ endif if dbus_display dbus_ss = ss.source_set() env = environment() - env.set('TARGETOS', targetos) + env.set('HOST_OS', host_os) xml = custom_target('dbus-display preprocess', input: 'dbus-display1.xml', output: 'dbus-display1.xml', @@ -91,9 +88,7 @@ if dbus_display '--interface-prefix', 'org.qemu.', '--c-namespace', 'QemuDBus', '--generate-c-code', '@BASENAME@']) - dbus_display1_lib = static_library('dbus-display1', dbus_display1, dependencies: gio) - dbus_display1_dep = declare_dependency(link_with: dbus_display1_lib, include_directories: include_directories('.')) - dbus_ss.add(when: [gio, pixman, dbus_display1_dep], + dbus_ss.add(when: gio, if_true: [files( 'dbus-chardev.c', 'dbus-clipboard.c', @@ -101,12 +96,14 @@ if dbus_display 'dbus-error.c', 'dbus-listener.c', 'dbus.c', - ), opengl, gbm]) + ), opengl, gbm, pixman, dbus_display1]) ui_modules += {'dbus' : dbus_ss} endif if gtk.found() - system_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')) @@ -120,8 +117,6 @@ if gtk.found() endif if sdl.found() - system_ss.add(when: 'CONFIG_WIN32', if_true: files('win32-kbd-hook.c')) - sdl_ss = ss.source_set() sdl_ss.add(sdl, sdl_image, pixman, glib, files( 'sdl2-2d.c', @@ -141,12 +136,12 @@ if spice.found() 'spice-display.c' )) ui_modules += {'spice-core' : spice_core_ss} -endif -if spice.found() and gio.found() - spice_ss = ss.source_set() - spice_ss.add(spice, gio, pixman, files('spice-app.c')) - ui_modules += {'spice-app': spice_ss} + if gio.found() + spice_ss = ss.source_set() + spice_ss.add(spice, gio, pixman, files('spice-app.c')) + ui_modules += {'spice-app': spice_ss} + endif endif keymaps = [ diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index be00a96340d..ef4e71da111 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -4,7 +4,9 @@ */ #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" @@ -49,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; @@ -96,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) @@ -123,32 +126,34 @@ uint32_t qemu_pixman_to_drm_format(pixman_format_code_t pixman_format) return 0; } -int qemu_pixman_get_type(int rshift, int gshift, int bshift) +int qemu_pixman_get_type(int rshift, int gshift, int bshift, int endian) { int type = PIXMAN_TYPE_OTHER; + bool native_endian = (endian == G_BYTE_ORDER); if (rshift > gshift && gshift > bshift) { if (bshift == 0) { - type = PIXMAN_TYPE_ARGB; + type = native_endian ? PIXMAN_TYPE_ARGB : PIXMAN_TYPE_BGRA; } else { - type = PIXMAN_TYPE_RGBA; + type = native_endian ? PIXMAN_TYPE_RGBA : PIXMAN_TYPE_ABGR; } } else if (rshift < gshift && gshift < bshift) { if (rshift == 0) { - type = PIXMAN_TYPE_ABGR; + type = native_endian ? PIXMAN_TYPE_ABGR : PIXMAN_TYPE_RGBA; } else { - type = PIXMAN_TYPE_BGRA; + type = native_endian ? PIXMAN_TYPE_BGRA : PIXMAN_TYPE_ARGB; } } return type; } -pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf) +#ifdef CONFIG_PIXMAN +pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf, int endian) { pixman_format_code_t format; int type; - type = qemu_pixman_get_type(pf->rshift, pf->gshift, pf->bshift); + type = qemu_pixman_get_type(pf->rshift, pf->gshift, pf->bshift, endian); format = PIXMAN_FORMAT(pf->bits_per_pixel, type, pf->abits, pf->rbits, pf->gbits, pf->bbits); if (!pixman_format_supported_source(format)) { @@ -156,6 +161,7 @@ pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf) } return format; } +#endif /* * Return true for known-good pixman conversions. @@ -184,6 +190,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) { @@ -209,6 +216,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) { @@ -218,6 +226,7 @@ void qemu_pixman_image_unref(pixman_image_t *image) pixman_image_unref(image); } +#ifdef CONFIG_PIXMAN pixman_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font, unsigned int ch) { @@ -260,3 +269,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 06468cd493e..73052383c2e 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; } diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c index 28d796607c0..3be17d1079a 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); @@ -241,7 +241,7 @@ void sdl2_gl_scanout_flush(DisplayChangeListener *dcl, 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_setup_default(&scon->win_fb, ww, wh, 0, 0); egl_fb_blit(&scon->win_fb, &scon->guest_fb, !scon->y0_top); SDL_GL_SwapWindow(scon->real_window); diff --git a/ui/sdl2-input.c b/ui/sdl2-input.c index b02a89ee7cd..2286df4223d 100644 --- a/ui/sdl2-input.c +++ b/ui/sdl2-input.c @@ -58,3 +58,8 @@ void sdl2_process_key(struct sdl2_console *scon, } } } + +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 178cc054abf..b00e421f7f8 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -29,11 +29,11 @@ #include "ui/console.h" #include "ui/input.h" #include "ui/sdl2.h" -#include "sysemu/runstate.h" -#include "sysemu/runstate-action.h" -#include "sysemu/sysemu.h" -#include "ui/win32-kbd-hook.h" +#include "system/runstate.h" +#include "system/runstate-action.h" +#include "system/system.h" #include "qemu/log.h" +#include "qemu-main.h" static int sdl2_num_outputs; static struct sdl2_console *sdl2_console; @@ -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; @@ -107,7 +107,7 @@ void sdl2_window_create(struct sdl2_console *scon) if (scon->opengl) { const char *driver = "opengl"; - if (scon->opts->gl == DISPLAYGL_MODE_ES) { + if (scon->opts->gl == DISPLAY_GL_MODE_ES) { driver = "opengles2"; } @@ -115,6 +115,7 @@ void sdl2_window_create(struct sdl2_console *scon) 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); @@ -172,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 } } @@ -203,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); } } @@ -214,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); @@ -245,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 { @@ -253,7 +262,6 @@ static void sdl_grab_start(struct sdl2_console *scon) } SDL_SetWindowGrab(scon->real_window, SDL_TRUE); gui_grab = 1; - win32_kbd_set_grab(true); sdl_update_caption(scon); } @@ -261,7 +269,6 @@ static void sdl_grab_end(struct sdl2_console *scon) { SDL_SetWindowGrab(scon->real_window, SDL_FALSE); gui_grab = 0; - win32_kbd_set_grab(false); sdl_show_cursor(scon); sdl_update_caption(scon); } @@ -280,7 +287,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); @@ -311,7 +318,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, @@ -362,30 +369,18 @@ static int get_mod_state(void) } } -static void *sdl2_win32_get_hwnd(struct sdl2_console *scon) -{ -#ifdef CONFIG_WIN32 - SDL_SysWMinfo info; - - SDL_VERSION(&info.version); - if (SDL_GetWindowWMInfo(scon->real_window, &info)) { - return info.info.win.window; - } -#endif - return NULL; -} - 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: @@ -410,15 +405,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) { @@ -431,7 +427,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: @@ -450,14 +446,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); } } @@ -483,7 +479,7 @@ static void handle_textinput(SDL_Event *ev) return; } - if (QEMU_IS_TEXT_CONSOLE(con)) { + 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)); } } @@ -492,14 +488,14 @@ static void handle_mousemotion(SDL_Event *ev) { int max_x, max_y; struct sdl2_console *scon = get_scon_from_window(ev->motion.windowID); + int scr_w, scr_h, surf_w, surf_h, x, y, dx, dy; if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { return; } - if (qemu_input_is_absolute() || absolute_enabled) { - int scr_w, scr_h; - SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); + SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); + if (qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { max_x = scr_w - 1; max_y = scr_h - 1; if (gui_grab && !gui_fullscreen @@ -513,9 +509,14 @@ static void handle_mousemotion(SDL_Event *ev) sdl_grab_start(scon); } } - if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { - sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel, - ev->motion.x, ev->motion.y, ev->motion.state); + surf_w = surface_width(scon->surface); + surf_h = surface_height(scon->surface); + x = (int64_t)ev->motion.x * surf_w / scr_w; + y = (int64_t)ev->motion.y * surf_h / scr_h; + dx = (int64_t)ev->motion.xrel * surf_w / scr_w; + dy = (int64_t)ev->motion.yrel * surf_h / scr_h; + if (gui_grab || qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { + sdl_send_mouse_event(scon, dx, dy, x, y, ev->motion.state); } } @@ -524,13 +525,18 @@ static void handle_mousebutton(SDL_Event *ev) int buttonstate = SDL_GetMouseState(NULL, NULL); SDL_MouseButtonEvent *bev; struct sdl2_console *scon = get_scon_from_window(ev->button.windowID); + int scr_w, scr_h, x, y; if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { return; } bev = &ev->button; - if (!gui_grab && !qemu_input_is_absolute()) { + SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); + x = (int64_t)bev->x * surface_width(scon->surface) / scr_w; + y = (int64_t)bev->y * surface_height(scon->surface) / scr_h; + + 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); @@ -541,7 +547,7 @@ static void handle_mousebutton(SDL_Event *ev) } else { buttonstate &= ~SDL_BUTTON(bev->button); } - sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate); + sdl_send_mouse_event(scon, 0, 0, x, y, buttonstate); } } @@ -597,13 +603,9 @@ static void handle_windowevent(SDL_Event *ev) sdl2_redraw(scon); break; case SDL_WINDOWEVENT_FOCUS_GAINED: - win32_kbd_set_grab(gui_grab); - if (qemu_console_is_graphic(scon->dcl.con)) { - win32_kbd_set_window(sdl2_win32_get_hwnd(scon)); - } /* 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 @@ -616,9 +618,6 @@ static void handle_windowevent(SDL_Event *ev) scon->ignore_hotkeys = get_mod_state(); break; case SDL_WINDOWEVENT_FOCUS_LOST: - if (qemu_console_is_graphic(scon->dcl.con)) { - win32_kbd_set_window(NULL); - } if (gui_grab && !gui_fullscreen) { sdl_grab_end(scon); } @@ -721,7 +720,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); @@ -733,9 +732,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); } } @@ -773,7 +772,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); } } @@ -858,14 +857,12 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) #ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR /* only available since SDL 2.0.8 */ SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); #endif -#ifndef CONFIG_WIN32 - /* QEMU uses its own low level keyboard hook procedure on Windows */ SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1"); -#endif #ifdef SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0"); #endif SDL_SetHint(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, "1"); + SDL_EnableScreenSaver(); memset(&info, 0, sizeof(info)); SDL_VERSION(&info.version); @@ -953,6 +950,9 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) } atexit(sdl_cleanup); + + /* SDL's event polling (in dpy_refresh) must happen on the main thread. */ + qemu_main = NULL; } static QemuDisplay qemu_display_sdl2 = { diff --git a/ui/shader/meson.build b/ui/shader/meson.build index 592bf596b9a..3137e65578e 100644 --- a/ui/shader/meson.build +++ b/ui/shader/meson.build @@ -10,5 +10,6 @@ foreach e : shaders output: output, capture: true, input: files('@0@.@1@'.format(e[0], e[1])), + build_by_default: false, command: [shaderinclude, '@INPUT0@']) endforeach diff --git a/ui/spice-app.c b/ui/spice-app.c index 405fb7f9f54..24f78f305c4 100644 --- a/ui/spice-app.c +++ b/ui/spice-app.c @@ -36,7 +36,7 @@ #include "qapi/error.h" #include "io/channel-command.h" #include "chardev/spice.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qom/object.h" static const char *tmp_dir; @@ -101,7 +101,7 @@ 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) +static void char_vc_class_init(ObjectClass *oc, const void *data) { VCChardevClass *vc = CHARDEV_VC_CLASS(oc); ChardevClass *cc = CHARDEV_CLASS(oc); @@ -173,7 +173,7 @@ static void spice_app_display_early_init(DisplayOptions *opts) exit(1); } - type_register(&char_vc_type_info); + type_register_static(&char_vc_type_info); sock_path = g_strjoin("", app_dir, "/", "spice.sock", NULL); qopts = qemu_opts_create(list, NULL, 0, &error_abort); @@ -220,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 52a59386d74..0326c63becd 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -18,8 +18,8 @@ #include "qemu/osdep.h" #include -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/runstate.h" #include "ui/qemu-spice.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" @@ -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; @@ -217,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) { @@ -260,7 +260,7 @@ static void channel_event(int event, SpiceChannelEventInfo *info) } if (need_lock) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } qapi_free_SpiceServerInfo(server); @@ -568,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, @@ -821,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); @@ -842,7 +840,7 @@ static void qemu_spice_init(void) "incompatible with -spice port/tls-port"); exit(1); } - egl_init(qemu_opt_get(opts, "rendernode"), DISPLAYGL_MODE_ON, &error_fatal); + egl_init(qemu_opt_get(opts, "rendernode"), DISPLAY_GL_MODE_ON, &error_fatal); spice_opengl = 1; } #endif diff --git a/ui/spice-display.c b/ui/spice-display.c index 5cc47bd668f..9c39d2c5c8d 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -28,6 +28,8 @@ #include "ui/spice-display.h" +#include "standard-headers/drm/drm_fourcc.h" + bool spice_opengl; int qemu_spice_rect_is_empty(const QXLRect* r) @@ -254,7 +256,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; @@ -448,7 +450,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); } @@ -476,7 +479,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); } @@ -747,7 +750,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,7 +777,7 @@ static void display_mouse_define(DisplayChangeListener *dcl, 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); } @@ -871,23 +874,48 @@ static void spice_gl_update(DisplayChangeListener *dcl, ssd->gl_updates++; } +static void spice_server_gl_scanout(QXLInstance *qxl, + const int *fd, + uint32_t width, uint32_t height, + const uint32_t *offset, + const uint32_t *stride, + uint32_t num_planes, uint32_t format, + uint64_t modifier, int y_0_top) +{ +#ifdef HAVE_SPICE_QXL_GL_SCANOUT2 + spice_qxl_gl_scanout2(qxl, fd, width, height, offset, stride, + num_planes, format, modifier, y_0_top); +#else + if (num_planes <= 1) { + spice_qxl_gl_scanout(qxl, fd[0], width, height, stride[0], format, y_0_top); + } else { + error_report("SPICE server does not support multi plane GL scanout"); + } +#endif +} + static void spice_gl_switch(DisplayChangeListener *dcl, struct DisplaySurface *new_surface) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); - EGLint stride, fourcc; - int fd; if (ssd->ds) { surface_gl_destroy_texture(ssd->gls, ssd->ds); } ssd->ds = new_surface; if (ssd->ds) { + uint32_t offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES]; + int fd[DMABUF_MAX_PLANES], num_planes, fourcc; + uint64_t modifier; + surface_gl_create_texture(ssd->gls, ssd->ds); - fd = egl_get_fd_for_texture(ssd->ds->texture, - &stride, &fourcc, - NULL); - if (fd < 0) { + if (!egl_dmabuf_export_texture(ssd->ds->texture, + fd, + (EGLint *)offset, + (EGLint *)stride, + &fourcc, + &num_planes, + &modifier)) { surface_gl_destroy_texture(ssd->gls, ssd->ds); return; } @@ -898,10 +926,11 @@ static void spice_gl_switch(DisplayChangeListener *dcl, fourcc); /* note: spice server will close the fd */ - spice_qxl_gl_scanout(&ssd->qxl, fd, - surface_width(ssd->ds), - surface_height(ssd->ds), - stride, fourcc, false); + spice_server_gl_scanout(&ssd->qxl, fd, + surface_width(ssd->ds), + surface_height(ssd->ds), + offset, stride, num_planes, + fourcc, modifier, false); ssd->have_surface = true; ssd->have_scanout = false; @@ -924,7 +953,8 @@ static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl) SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); trace_qemu_spice_gl_scanout_disable(ssd->qxl.id); - spice_qxl_gl_scanout(&ssd->qxl, -1, 0, 0, 0, 0, false); + spice_server_gl_scanout(&ssd->qxl, NULL, 0, 0, NULL, NULL, 0, DRM_FORMAT_INVALID, + DRM_FORMAT_MOD_INVALID, false); qemu_spice_gl_monitor_config(ssd, 0, 0, 0, 0); ssd->have_surface = false; ssd->have_scanout = false; @@ -940,20 +970,23 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, void *d3d_tex2d) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); - EGLint stride = 0, fourcc = 0; - int fd = -1; + EGLint offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES], fourcc = 0; + int fd[DMABUF_MAX_PLANES], num_planes; + uint64_t modifier; assert(tex_id); - fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc, NULL); - if (fd < 0) { - fprintf(stderr, "%s: failed to get fd for texture\n", __func__); + if (!egl_dmabuf_export_texture(tex_id, fd, offset, stride, &fourcc, + &num_planes, &modifier)) { + fprintf(stderr, "%s: failed to export dmabuf for texture\n", __func__); return; } + trace_qemu_spice_gl_scanout_texture(ssd->qxl.id, w, h, fourcc); /* note: spice server will close the fd */ - spice_qxl_gl_scanout(&ssd->qxl, fd, backing_width, backing_height, - stride, fourcc, y_0_top); + spice_server_gl_scanout(&ssd->qxl, fd, backing_width, backing_height, + (uint32_t *)offset, (uint32_t *)stride, num_planes, + fourcc, modifier, y_0_top); qemu_spice_gl_monitor_config(ssd, x, y, w, h); ssd->have_surface = false; ssd->have_scanout = true; @@ -976,6 +1009,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; @@ -984,11 +1018,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); } @@ -1021,11 +1057,11 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); - EGLint stride = 0, fourcc = 0; + EGLint fourcc = 0; bool render_cursor = false; bool y_0_top = false; /* FIXME */ uint64_t cookie; - int fd; + uint32_t width, height, texture; if (!ssd->have_scanout) { return; @@ -1042,54 +1078,80 @@ 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) { + int fds[DMABUF_MAX_PLANES], num_planes; + uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES]; + uint64_t modifier; + + 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); - fd = egl_get_fd_for_texture(ssd->blit_fb.texture, - &stride, &fourcc, NULL); - spice_qxl_gl_scanout(&ssd->qxl, fd, - dmabuf->width, dmabuf->height, - stride, fourcc, false); + width, height); + if (!egl_dmabuf_export_texture(ssd->blit_fb.texture, fds, + (EGLint *)offsets, (EGLint *)strides, + &fourcc, &num_planes, &modifier)) { + fprintf(stderr, + "%s: failed to export dmabuf for texture\n", __func__); + return; + } + + spice_server_gl_scanout(&ssd->qxl, fds, width, height, offsets, strides, + num_planes, fourcc, modifier, false); } } else { - trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id, - dmabuf->width, dmabuf->height); + int fds[DMABUF_MAX_PLANES]; + int noffsets, nstrides; + const uint32_t *offsets = qemu_dmabuf_get_offsets(dmabuf, &noffsets); + const uint32_t *strides = qemu_dmabuf_get_strides(dmabuf, &nstrides); + uint32_t num_planes = qemu_dmabuf_get_num_planes(dmabuf); + + assert(noffsets >= num_planes); + assert(nstrides >= num_planes); + + fourcc = qemu_dmabuf_get_fourcc(dmabuf); + y_0_top = qemu_dmabuf_get_y0_top(dmabuf); + qemu_dmabuf_dup_fds(dmabuf, fds, DMABUF_MAX_PLANES); + + 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_server_gl_scanout(&ssd->qxl, fds, width, height, + offsets, strides, num_planes, + fourcc, + qemu_dmabuf_get_modifier(dmabuf), + 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 bbd502564ed..a5c5d78474e 100644 --- a/ui/spice-input.c +++ b/ui/spice-input.c @@ -224,7 +224,7 @@ static const SpiceTabletInterface tablet_interface = { static void mouse_mode_notifier(Notifier *notifier, void *data) { QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode); - bool is_absolute = qemu_input_is_absolute(); + bool is_absolute = qemu_input_is_absolute(NULL); if (pointer->absolute == is_absolute) { return; diff --git a/ui/trace-events b/ui/trace-events index 76b19a29958..3da0d5e2800 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -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 @@ -92,7 +93,6 @@ input_event_rel(int conidx, const char *axis, int value) "con %d, axis %s, value 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)" @@ -130,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" @@ -157,11 +158,15 @@ 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/ui-hmp-cmds.c b/ui/ui-hmp-cmds.c index c671389473d..980a8bbc518 100644 --- a/ui/ui-hmp-cmds.c +++ b/ui/ui-hmp-cmds.c @@ -21,7 +21,7 @@ #include "monitor/monitor-internal.h" #include "qapi/error.h" #include "qapi/qapi-commands-ui.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/cutils.h" #include "ui/console.h" #include "ui/input.h" @@ -437,6 +437,7 @@ void sendkey_completion(ReadLineState *rs, int nb_args, const char *str) } } +#ifdef CONFIG_PIXMAN void coroutine_fn hmp_screendump(Monitor *mon, const QDict *qdict) { @@ -458,6 +459,7 @@ hmp_screendump(Monitor *mon, const QDict *qdict) end: hmp_handle_error(mon, err); } +#endif void hmp_client_migrate_info(Monitor *mon, const QDict *qdict) { diff --git a/ui/ui-qmp-cmds.c b/ui/ui-qmp-cmds.c index debc07d6783..74fa6c6ec5a 100644 --- a/ui/ui-qmp-cmds.c +++ b/ui/ui-qmp-cmds.c @@ -44,7 +44,8 @@ void qmp_set_password(SetPasswordOptions *opts, Error **errp) 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"); + error_setg(errp, "parameter 'connected' must be 'keep'" + " when 'protocol' is 'vnc'"); return; } /* @@ -195,7 +196,7 @@ void qmp_client_migrate_info(const char *protocol, const char *hostname, } if (!has_port && !has_tls_port) { - error_setg(errp, QERR_MISSING_PARAMETER, "port/tls-port"); + error_setg(errp, "parameter 'port' or 'tls-port' is required"); return; } @@ -212,6 +213,7 @@ void qmp_client_migrate_info(const char *protocol, const char *hostname, error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", "'spice'"); } +#ifdef CONFIG_PIXMAN #ifdef CONFIG_PNG /** * png_save: Take a screenshot as PNG @@ -391,3 +393,4 @@ qmp_screendump(const char *filename, const char *device, } } } +#endif /* CONFIG_PIXMAN */ diff --git a/ui/vdagent.c b/ui/vdagent.c index 00d36a86771..c0746fe5b16 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -6,10 +6,10 @@ #include "qemu/option.h" #include "qemu/units.h" #include "hw/qdev-core.h" -#include "migration/blocker.h" #include "ui/clipboard.h" #include "ui/console.h" #include "ui/input.h" +#include "migration/vmstate.h" #include "trace.h" #include "qapi/qapi-types-char.h" @@ -32,14 +32,12 @@ struct VDAgentChardev { Chardev parent; - /* TODO: migration isn't yet supported */ - Error *migration_blocker; - /* config */ bool mouse; bool clipboard; /* guest vdagent */ + bool connected; uint32_t caps; VDIChunkHeader chunk; uint32_t chunksize; @@ -47,7 +45,7 @@ struct VDAgentChardev { uint32_t msgsize; uint8_t *xbuf; uint32_t xoff, xsize; - Buffer outbuf; + GByteArray *outbuf; /* mouse */ DeviceState mouse_dev; @@ -142,16 +140,16 @@ static void vdagent_send_buf(VDAgentChardev *vd) { uint32_t len; - while (!buffer_empty(&vd->outbuf)) { + while (vd->outbuf->len) { len = qemu_chr_be_can_write(CHARDEV(vd)); if (len == 0) { return; } - if (len > vd->outbuf.offset) { - len = vd->outbuf.offset; + if (len > vd->outbuf->len) { + len = vd->outbuf->len; } - qemu_chr_be_write(CHARDEV(vd), vd->outbuf.buffer, len); - buffer_advance(&vd->outbuf, len); + qemu_chr_be_write(CHARDEV(vd), vd->outbuf->data, len); + g_byte_array_remove_range(vd->outbuf, 0, len); } } @@ -166,7 +164,7 @@ static void vdagent_send_msg(VDAgentChardev *vd, VDAgentMessage *msg) msg->protocol = VD_AGENT_PROTOCOL; - if (vd->outbuf.offset + msgsize > VDAGENT_BUFFER_LIMIT) { + if (vd->outbuf->len + msgsize > VDAGENT_BUFFER_LIMIT) { error_report("buffer full, dropping message"); return; } @@ -177,15 +175,14 @@ static void vdagent_send_msg(VDAgentChardev *vd, VDAgentMessage *msg) if (chunk.size > 1024) { chunk.size = 1024; } - buffer_reserve(&vd->outbuf, sizeof(chunk) + chunk.size); - buffer_append(&vd->outbuf, &chunk, sizeof(chunk)); - buffer_append(&vd->outbuf, msgbuf + msgoff, chunk.size); + g_byte_array_append(vd->outbuf, (void *)&chunk, sizeof(chunk)); + g_byte_array_append(vd->outbuf, msgbuf + msgoff, chunk.size); msgoff += chunk.size; } 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) + @@ -205,6 +202,7 @@ static void vdagent_send_caps(VDAgentChardev *vd) #endif } + caps->request = request; vdagent_send_msg(vd, msg); } @@ -297,7 +295,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, @@ -671,10 +669,6 @@ static void vdagent_chr_open(Chardev *chr, return; #endif - if (migrate_add_blocker(vd->migration_blocker, errp) != 0) { - return; - } - vd->mouse = VDAGENT_MOUSE_DEFAULT; if (cfg->has_mouse) { vd->mouse = cfg->mouse; @@ -693,6 +687,18 @@ static void vdagent_chr_open(Chardev *chr, *be_opened = true; } +static void vdagent_clipboard_peer_register(VDAgentChardev *vd) +{ + if (vd->cbpeer.notifier.notify != NULL) { + return; + } + + vd->cbpeer.name = "vdagent"; + vd->cbpeer.notifier.notify = vdagent_clipboard_notify; + vd->cbpeer.request = vdagent_clipboard_request; + qemu_clipboard_peer_register(&vd->cbpeer); +} + static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg) { VDAgentAnnounceCapabilities *caps = (void *)msg->data; @@ -711,7 +717,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); @@ -719,11 +725,9 @@ 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) { - vd->cbpeer.name = "vdagent"; - vd->cbpeer.notifier.notify = vdagent_clipboard_notify; - vd->cbpeer.request = vdagent_clipboard_request; - qemu_clipboard_peer_register(&vd->cbpeer); + if (have_clipboard(vd)) { + qemu_clipboard_reset_serial(); + vdagent_clipboard_peer_register(vd); } } @@ -856,7 +860,8 @@ static void vdagent_disconnect(VDAgentChardev *vd) { trace_vdagent_disconnect(); - buffer_reset(&vd->outbuf); + vd->connected = false; + g_byte_array_set_size(vd->outbuf, 0); vdagent_reset_bufs(vd); vd->caps = 0; if (vd->mouse_hs) { @@ -872,6 +877,12 @@ 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 (vd->connected == fe_open) { + return; + } + if (!fe_open) { trace_vdagent_close(); vdagent_disconnect(vd); @@ -881,7 +892,8 @@ static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open) return; } - trace_vdagent_open(); + vd->connected = true; + vdagent_send_caps(vd, true); } static void vdagent_chr_parse(QemuOpts *opts, ChardevBackend *backend, @@ -900,7 +912,7 @@ static void vdagent_chr_parse(QemuOpts *opts, ChardevBackend *backend, /* ------------------------------------------------------------------ */ -static void vdagent_chr_class_init(ObjectClass *oc, void *data) +static void vdagent_chr_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); @@ -911,26 +923,163 @@ static void vdagent_chr_class_init(ObjectClass *oc, void *data) cc->chr_accept_input = vdagent_chr_accept_input; } +static int post_load(void *opaque, int version_id) +{ + VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(opaque); + + if (have_mouse(vd) && vd->mouse_hs) { + qemu_input_handler_activate(vd->mouse_hs); + } + + if (have_clipboard(vd)) { + vdagent_clipboard_peer_register(vd); + } + + return 0; +} + +static const VMStateDescription vmstate_chunk = { + .name = "vdagent/chunk", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(port, VDIChunkHeader), + VMSTATE_UINT32(size, VDIChunkHeader), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_vdba = { + .name = "vdagent/bytearray", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(len, GByteArray), + VMSTATE_VBUFFER_ALLOC_UINT32(data, GByteArray, 0, 0, len), + VMSTATE_END_OF_LIST() + } +}; + +struct CBInfoArray { + uint32_t n; + QemuClipboardInfo cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT]; +}; + +static const VMStateDescription vmstate_cbinfo_array = { + .name = "cbinfoarray", + .fields = (const VMStateField[]) { + VMSTATE_UINT32(n, struct CBInfoArray), + VMSTATE_STRUCT_VARRAY_UINT32(cbinfo, struct CBInfoArray, n, + 0, vmstate_cbinfo, QemuClipboardInfo), + VMSTATE_END_OF_LIST() + } +}; + +static int put_cbinfo(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(pv); + struct CBInfoArray cbinfo = {}; + int i; + + if (!have_clipboard(vd)) { + return 0; + } + + for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) { + if (qemu_clipboard_peer_owns(&vd->cbpeer, i)) { + cbinfo.cbinfo[cbinfo.n++] = *qemu_clipboard_info(i); + } + } + + return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc); +} + +static int get_cbinfo(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(pv); + struct CBInfoArray cbinfo = {}; + int i, ret; + + if (!have_clipboard(vd)) { + return 0; + } + + vdagent_clipboard_peer_register(vd); + + ret = vmstate_load_state(f, &vmstate_cbinfo_array, &cbinfo, 0); + if (ret) { + return ret; + } + + for (i = 0; i < cbinfo.n; i++) { + g_autoptr(QemuClipboardInfo) info = + qemu_clipboard_info_new(&vd->cbpeer, cbinfo.cbinfo[i].selection); + /* this will steal clipboard data pointer from cbinfo.types */ + memcpy(info->types, cbinfo.cbinfo[i].types, sizeof(cbinfo.cbinfo[i].types)); + qemu_clipboard_update(info); + } + + return 0; +} + +static const VMStateInfo vmstate_cbinfos = { + .name = "vdagent/cbinfos", + .get = get_cbinfo, + .put = put_cbinfo, +}; + +static const VMStateDescription vmstate_vdagent = { + .name = "vdagent", + .version_id = 0, + .minimum_version_id = 0, + .post_load = post_load, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(connected, VDAgentChardev), + VMSTATE_UINT32(caps, VDAgentChardev), + VMSTATE_STRUCT(chunk, VDAgentChardev, 0, vmstate_chunk, VDIChunkHeader), + VMSTATE_UINT32(chunksize, VDAgentChardev), + VMSTATE_UINT32(msgsize, VDAgentChardev), + VMSTATE_VBUFFER_ALLOC_UINT32(msgbuf, VDAgentChardev, 0, 0, msgsize), + VMSTATE_UINT32(xsize, VDAgentChardev), + VMSTATE_UINT32(xoff, VDAgentChardev), + VMSTATE_VBUFFER_ALLOC_UINT32(xbuf, VDAgentChardev, 0, 0, xsize), + VMSTATE_STRUCT_POINTER(outbuf, VDAgentChardev, vmstate_vdba, GByteArray), + VMSTATE_UINT32(mouse_x, VDAgentChardev), + VMSTATE_UINT32(mouse_y, VDAgentChardev), + VMSTATE_UINT32(mouse_btn, VDAgentChardev), + VMSTATE_UINT32(mouse_display, VDAgentChardev), + VMSTATE_UINT32_ARRAY(last_serial, VDAgentChardev, + QEMU_CLIPBOARD_SELECTION__COUNT), + VMSTATE_UINT32_ARRAY(cbpending, VDAgentChardev, + QEMU_CLIPBOARD_SELECTION__COUNT), + { + .name = "cbinfos", + .info = &vmstate_cbinfos, + .flags = VMS_SINGLE, + }, + VMSTATE_END_OF_LIST() + } +}; + static void vdagent_chr_init(Object *obj) { VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj); - buffer_init(&vd->outbuf, "vdagent-outbuf"); - error_setg(&vd->migration_blocker, - "The vdagent chardev doesn't yet support migration"); + vd->outbuf = g_byte_array_new(); + vmstate_register_any(NULL, &vmstate_vdagent, vd); } static void vdagent_chr_fini(Object *obj) { VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj); - 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); + g_clear_pointer(&vd->outbuf, g_byte_array_unref); } static const TypeInfo vdagent_chr_type_info = { diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index 47fdae5b215..3f4cfc471d5 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 c759be34381..124b6fbd9c2 100644 --- a/ui/vnc-clipboard.c +++ b/ui/vnc-clipboard.c @@ -69,6 +69,11 @@ static uint8_t *inflate_buffer(uint8_t *in, uint32_t in_len, uint32_t *size) } } + *size = stream.total_out; + inflateEnd(&stream); + + return out; + err_end: inflateEnd(&stream); err: diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index 41f559eb837..25c7b2c7889 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -150,7 +150,7 @@ tight_detect_smooth_image24(VncState *vs, int w, int h) * If client is big-endian, color samples begin from the second * byte (offset 1) of a 32-bit pixel value. */ - off = vs->client_be; + off = vs->client_endian == G_BIG_ENDIAN ? 1 : 0; memset(stats, 0, sizeof (stats)); @@ -891,7 +891,7 @@ static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret) buf8 = buf; - if (1 /* FIXME */) { + if (vs->client_endian == G_BYTE_ORDER) { rshift = vs->client_pf.rshift; gshift = vs->client_pf.gshift; bshift = vs->client_pf.bshift; @@ -1001,16 +1001,24 @@ static int send_mono_rect(VncState *vs, int x, int y, break; } case 2: - vnc_write(vs, &bg, 2); - vnc_write(vs, &fg, 2); + { + uint16_t bg16 = bg; + uint16_t fg16 = fg; + vnc_write(vs, &bg16, 2); + vnc_write(vs, &fg16, 2); tight_encode_mono_rect16(vs->tight->tight.buffer, w, h, bg, fg); break; + } default: - vnc_write_u8(vs, bg); - vnc_write_u8(vs, fg); + { + uint8_t bg8 = bg; + uint8_t fg8 = fg; + vnc_write_u8(vs, bg8); + vnc_write_u8(vs, fg8); tight_encode_mono_rect8(vs->tight->tight.buffer, w, h, bg, fg); break; } + } vs->tight->tight.offset = bytes; bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY); diff --git a/ui/vnc-enc-zrle.c b/ui/vnc-enc-zrle.c index bd33b890639..97ec6c7119b 100644 --- a/ui/vnc-enc-zrle.c +++ b/ui/vnc-enc-zrle.c @@ -255,7 +255,7 @@ static void zrle_write_u8(VncState *vs, uint8_t value) static int zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) { - bool be = vs->client_be; + bool be = vs->client_endian == G_BIG_ENDIAN; size_t bytes; int zywrle_level; diff --git a/ui/vnc-enc-zrle.c.inc b/ui/vnc-enc-zrle.c.inc index a8ca37d05ef..2ef7501d521 100644 --- a/ui/vnc-enc-zrle.c.inc +++ b/ui/vnc-enc-zrle.c.inc @@ -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-jobs.c b/ui/vnc-jobs.c index fcca7ec632b..d3486af9e21 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -188,7 +188,7 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) local->lossy_rect = orig->lossy_rect; local->write_pixels = orig->write_pixels; local->client_pf = orig->client_pf; - local->client_be = orig->client_be; + local->client_endian = orig->client_endian; local->tight = orig->tight; local->zlib = orig->zlib; local->hextile = orig->hextile; diff --git a/ui/vnc-palette.c b/ui/vnc-palette.c index dc7c0ba9972..4e88c412f07 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 b4eb3ce718e..a96bc862363 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.c b/ui/vnc.c index 6fd86996a54..e9c30aad62c 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -29,8 +29,8 @@ #include "vnc-jobs.h" #include "trace.h" #include "hw/qdev-core.h" -#include "sysemu/sysemu.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/runstate.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" @@ -146,8 +146,6 @@ static void vnc_init_basic_info(SocketAddress *addr, default: abort(); } - - return; } static void vnc_init_basic_info_from_server_addr(QIOChannelSocket *ioc, @@ -893,7 +891,7 @@ void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) buf[0] = v; break; case 2: - if (vs->client_be) { + if (vs->client_endian == G_BIG_ENDIAN) { buf[0] = v >> 8; buf[1] = v; } else { @@ -903,7 +901,7 @@ void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) break; default: case 4: - if (vs->client_be) { + if (vs->client_endian == G_BIG_ENDIAN) { buf[0] = v >> 24; buf[1] = v >> 16; buf[2] = v >> 8; @@ -981,7 +979,7 @@ 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 ??? */ } @@ -1584,15 +1582,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; @@ -1771,7 +1769,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); @@ -1872,12 +1870,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: @@ -1931,7 +1933,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 */ @@ -1945,88 +1948,88 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) case 0xb8: /* Right ALT */ break; case 0xc8: - qemu_text_console_put_keysym(NULL, QEMU_KEY_UP); + qemu_text_console_put_keysym(con, QEMU_KEY_UP); break; case 0xd0: - qemu_text_console_put_keysym(NULL, QEMU_KEY_DOWN); + qemu_text_console_put_keysym(con, QEMU_KEY_DOWN); break; case 0xcb: - qemu_text_console_put_keysym(NULL, QEMU_KEY_LEFT); + qemu_text_console_put_keysym(con, QEMU_KEY_LEFT); break; case 0xcd: - qemu_text_console_put_keysym(NULL, QEMU_KEY_RIGHT); + qemu_text_console_put_keysym(con, QEMU_KEY_RIGHT); break; case 0xd3: - qemu_text_console_put_keysym(NULL, QEMU_KEY_DELETE); + qemu_text_console_put_keysym(con, QEMU_KEY_DELETE); break; case 0xc7: - qemu_text_console_put_keysym(NULL, QEMU_KEY_HOME); + qemu_text_console_put_keysym(con, QEMU_KEY_HOME); break; case 0xcf: - qemu_text_console_put_keysym(NULL, QEMU_KEY_END); + qemu_text_console_put_keysym(con, QEMU_KEY_END); break; case 0xc9: - qemu_text_console_put_keysym(NULL, QEMU_KEY_PAGEUP); + qemu_text_console_put_keysym(con, QEMU_KEY_PAGEUP); break; case 0xd1: - qemu_text_console_put_keysym(NULL, QEMU_KEY_PAGEDOWN); + qemu_text_console_put_keysym(con, QEMU_KEY_PAGEDOWN); break; case 0x47: - qemu_text_console_put_keysym(NULL, numlock ? '7' : QEMU_KEY_HOME); + qemu_text_console_put_keysym(con, numlock ? '7' : QEMU_KEY_HOME); break; case 0x48: - qemu_text_console_put_keysym(NULL, numlock ? '8' : QEMU_KEY_UP); + qemu_text_console_put_keysym(con, numlock ? '8' : QEMU_KEY_UP); break; case 0x49: - qemu_text_console_put_keysym(NULL, numlock ? '9' : QEMU_KEY_PAGEUP); + qemu_text_console_put_keysym(con, numlock ? '9' : QEMU_KEY_PAGEUP); break; case 0x4b: - qemu_text_console_put_keysym(NULL, numlock ? '4' : QEMU_KEY_LEFT); + qemu_text_console_put_keysym(con, numlock ? '4' : QEMU_KEY_LEFT); break; case 0x4c: - qemu_text_console_put_keysym(NULL, '5'); + qemu_text_console_put_keysym(con, '5'); break; case 0x4d: - qemu_text_console_put_keysym(NULL, numlock ? '6' : QEMU_KEY_RIGHT); + qemu_text_console_put_keysym(con, numlock ? '6' : QEMU_KEY_RIGHT); break; case 0x4f: - qemu_text_console_put_keysym(NULL, numlock ? '1' : QEMU_KEY_END); + qemu_text_console_put_keysym(con, numlock ? '1' : QEMU_KEY_END); break; case 0x50: - qemu_text_console_put_keysym(NULL, numlock ? '2' : QEMU_KEY_DOWN); + qemu_text_console_put_keysym(con, numlock ? '2' : QEMU_KEY_DOWN); break; case 0x51: - qemu_text_console_put_keysym(NULL, numlock ? '3' : QEMU_KEY_PAGEDOWN); + qemu_text_console_put_keysym(con, numlock ? '3' : QEMU_KEY_PAGEDOWN); break; case 0x52: - qemu_text_console_put_keysym(NULL, '0'); + qemu_text_console_put_keysym(con, '0'); break; case 0x53: - qemu_text_console_put_keysym(NULL, numlock ? '.' : QEMU_KEY_DELETE); + qemu_text_console_put_keysym(con, numlock ? '.' : QEMU_KEY_DELETE); break; case 0xb5: - qemu_text_console_put_keysym(NULL, '/'); + qemu_text_console_put_keysym(con, '/'); break; case 0x37: - qemu_text_console_put_keysym(NULL, '*'); + qemu_text_console_put_keysym(con, '*'); break; case 0x4a: - qemu_text_console_put_keysym(NULL, '-'); + qemu_text_console_put_keysym(con, '-'); break; case 0x4e: - qemu_text_console_put_keysym(NULL, '+'); + qemu_text_console_put_keysym(con, '+'); break; case 0x9c: - qemu_text_console_put_keysym(NULL, '\n'); + qemu_text_console_put_keysym(con, '\n'); break; default: if (control) { - qemu_text_console_put_keysym(NULL, sym & 0x1f); + qemu_text_console_put_keysym(con, sym & 0x1f); } else { - qemu_text_console_put_keysym(NULL, sym); + qemu_text_console_put_keysym(con, sym); } break; } @@ -2044,7 +2047,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'; } @@ -2144,16 +2147,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 @@ -2163,54 +2166,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: @@ -2234,7 +2240,8 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) static void set_pixel_conversion(VncState *vs) { - pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf); + pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf, + vs->client_endian); if (fmt == VNC_SERVER_FB_FORMAT) { vs->write_pixels = vnc_write_pixels_copy; @@ -2306,7 +2313,7 @@ static void set_pixel_format(VncState *vs, int bits_per_pixel, vs->client_pf.bits_per_pixel = bits_per_pixel; vs->client_pf.bytes_per_pixel = bits_per_pixel / 8; vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel; - vs->client_be = big_endian_flag; + vs->client_endian = big_endian_flag ? G_BIG_ENDIAN : G_LITTLE_ENDIAN; if (!true_color_flag) { send_color_map(vs); @@ -2442,6 +2449,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."); @@ -2454,7 +2466,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; @@ -2502,6 +2514,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; @@ -2551,7 +2569,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; } @@ -2764,7 +2782,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); @@ -3118,8 +3136,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)); @@ -3367,6 +3385,16 @@ static const DisplayChangeListenerOps dcl_ops = { .dpy_cursor_define = vnc_dpy_cursor_define, }; +static void vmstate_change_handler(void *opaque, bool running, RunState state) +{ + VncDisplay *vd = opaque; + + if (state != RUN_STATE_RUNNING) { + return; + } + update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); +} + void vnc_display_init(const char *id, Error **errp) { VncDisplay *vd; @@ -3403,6 +3431,8 @@ void vnc_display_init(const char *id, Error **errp) vd->dcl.ops = &dcl_ops; register_displaychangelistener(&vd->dcl); vd->kbd = qkbd_state_init(vd->dcl.con); + vd->vmstate_handler_entry = qemu_add_vm_change_state_handler( + &vmstate_change_handler, vd); } @@ -3411,7 +3441,6 @@ static void vnc_display_close(VncDisplay *vd) if (!vd) { return; } - vd->is_unix = false; if (vd->listener) { qio_net_listener_disconnect(vd->listener); @@ -3715,11 +3744,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; @@ -3838,7 +3862,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; @@ -3918,8 +3942,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) { @@ -4050,7 +4072,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; @@ -4172,11 +4194,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"); @@ -4190,7 +4213,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 757fa83044e..b3e07269bb4 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) @@ -168,7 +168,6 @@ struct VncDisplay const char *id; QTAILQ_ENTRY(VncDisplay) next; - bool is_unix; char *password; time_t expires; int auth; @@ -186,6 +185,8 @@ struct VncDisplay #endif AudioState *audio_state; + + VMChangeStateEntry *vmstate_handler_entry; }; typedef struct VncTight { @@ -324,7 +325,7 @@ struct VncState VncWritePixels *write_pixels; PixelFormat client_pf; pixman_format_code_t client_format; - bool client_be; + int client_endian; /* G_LITTLE_ENDIAN or G_BIG_ENDIAN */ CaptureVoiceOut *audio_cap; struct audsettings as; @@ -464,24 +465,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 @@ -597,6 +583,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/win32-kbd-hook.c b/ui/win32-kbd-hook.c index 1ac237db9e9..f448247b19d 100644 --- a/ui/win32-kbd-hook.c +++ b/ui/win32-kbd-hook.c @@ -7,7 +7,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "ui/win32-kbd-hook.h" static Notifier win32_unhook_notifier; diff --git a/util/aio-posix.c b/util/aio-posix.c index 7f2c99729d4..2e0a5dadc4d 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" @@ -27,6 +28,9 @@ /* Stop userspace polling on a handler if it isn't active for some time */ #define POLL_IDLE_INTERVAL_NS (7 * NANOSECONDS_PER_SECOND) +static void adjust_polling_time(AioContext *ctx, AioPolledEvent *poll, + int64_t block_ns); + bool aio_poll_disabled(AioContext *ctx) { return qatomic_read(&ctx->poll_disable_cnt); @@ -391,7 +395,8 @@ static bool aio_dispatch_handler(AioContext *ctx, AioHandler *node) * scanning all handlers with aio_dispatch_handlers(). */ static bool aio_dispatch_ready_handlers(AioContext *ctx, - AioHandlerList *ready_list) + AioHandlerList *ready_list, + int64_t block_ns) { bool progress = false; AioHandler *node; @@ -399,6 +404,14 @@ static bool aio_dispatch_ready_handlers(AioContext *ctx, while ((node = QLIST_FIRST(ready_list))) { QLIST_REMOVE(node, node_ready); progress = aio_dispatch_handler(ctx, node) || progress; + + /* + * Adjust polling time only after aio_dispatch_handler(), which can + * add the handler to ctx->poll_aio_handlers. + */ + if (ctx->poll_max_ns && QLIST_IS_INSERTED(node, node_poll)) { + adjust_polling_time(ctx, &node->poll, block_ns); + } } return progress; @@ -578,13 +591,19 @@ static bool run_poll_handlers(AioContext *ctx, AioHandlerList *ready_list, static bool try_poll_mode(AioContext *ctx, AioHandlerList *ready_list, int64_t *timeout) { + AioHandler *node; int64_t max_ns; if (QLIST_EMPTY_RCU(&ctx->poll_aio_handlers)) { return false; } - max_ns = qemu_soonest_timeout(*timeout, ctx->poll_ns); + max_ns = 0; + QLIST_FOREACH(node, &ctx->poll_aio_handlers, node_poll) { + max_ns = MAX(max_ns, node->poll.ns); + } + max_ns = qemu_soonest_timeout(*timeout, max_ns); + if (max_ns && !ctx->fdmon_ops->need_wait(ctx)) { /* * Enable poll mode. It pairs with the poll_set_started() in @@ -599,6 +618,46 @@ static bool try_poll_mode(AioContext *ctx, AioHandlerList *ready_list, return false; } +static void adjust_polling_time(AioContext *ctx, AioPolledEvent *poll, + int64_t block_ns) +{ + if (block_ns <= poll->ns) { + /* This is the sweet spot, no adjustment needed */ + } else if (block_ns > ctx->poll_max_ns) { + /* We'd have to poll for too long, poll less */ + int64_t old = poll->ns; + + if (ctx->poll_shrink) { + poll->ns /= ctx->poll_shrink; + } else { + poll->ns = 0; + } + + trace_poll_shrink(ctx, old, poll->ns); + } else if (poll->ns < ctx->poll_max_ns && + block_ns < ctx->poll_max_ns) { + /* There is room to grow, poll longer */ + int64_t old = poll->ns; + int64_t grow = ctx->poll_grow; + + if (grow == 0) { + grow = 2; + } + + if (poll->ns) { + poll->ns *= grow; + } else { + poll->ns = 4000; /* start polling at 4 microseconds */ + } + + if (poll->ns > ctx->poll_max_ns) { + poll->ns = ctx->poll_max_ns; + } + + trace_poll_grow(ctx, old, poll->ns); + } +} + bool aio_poll(AioContext *ctx, bool blocking) { AioHandlerList ready_list = QLIST_HEAD_INITIALIZER(ready_list); @@ -606,6 +665,7 @@ bool aio_poll(AioContext *ctx, bool blocking) bool use_notify_me; int64_t timeout; int64_t start = 0; + int64_t block_ns = 0; /* * There cannot be two concurrent aio_poll calls for the same AioContext (or @@ -678,49 +738,13 @@ bool aio_poll(AioContext *ctx, bool blocking) aio_notify_accept(ctx); - /* Adjust polling time */ + /* Calculate blocked time for adaptive polling */ if (ctx->poll_max_ns) { - int64_t block_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start; - - if (block_ns <= ctx->poll_ns) { - /* This is the sweet spot, no adjustment needed */ - } else if (block_ns > ctx->poll_max_ns) { - /* We'd have to poll for too long, poll less */ - int64_t old = ctx->poll_ns; - - if (ctx->poll_shrink) { - ctx->poll_ns /= ctx->poll_shrink; - } else { - ctx->poll_ns = 0; - } - - trace_poll_shrink(ctx, old, ctx->poll_ns); - } else if (ctx->poll_ns < ctx->poll_max_ns && - block_ns < ctx->poll_max_ns) { - /* There is room to grow, poll longer */ - int64_t old = ctx->poll_ns; - int64_t grow = ctx->poll_grow; - - if (grow == 0) { - grow = 2; - } - - if (ctx->poll_ns) { - ctx->poll_ns *= grow; - } else { - ctx->poll_ns = 4000; /* start polling at 4 microseconds */ - } - - if (ctx->poll_ns > ctx->poll_max_ns) { - ctx->poll_ns = ctx->poll_max_ns; - } - - trace_poll_grow(ctx, old, ctx->poll_ns); - } + block_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start; } progress |= aio_bh_poll(ctx); - progress |= aio_dispatch_ready_handlers(ctx, &ready_list); + progress |= aio_dispatch_ready_handlers(ctx, &ready_list, block_ns); aio_free_deleted_handlers(ctx); @@ -766,19 +790,25 @@ void aio_context_use_g_source(AioContext *ctx) void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, int64_t grow, int64_t shrink, Error **errp) { + AioHandler *node; + + qemu_lockcnt_inc(&ctx->list_lock); + QLIST_FOREACH(node, &ctx->aio_handlers, node) { + node->poll.ns = 0; + } + qemu_lockcnt_dec(&ctx->list_lock); + /* No thread synchronization here, it doesn't matter if an incorrect value * is used once. */ ctx->poll_max_ns = max_ns; - ctx->poll_ns = 0; ctx->poll_grow = grow; ctx->poll_shrink = shrink; 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 4264c518be0..82a0201ea48 100644 --- a/util/aio-posix.h +++ b/util/aio-posix.h @@ -38,6 +38,7 @@ struct AioHandler { #endif int64_t poll_idle_timeout; /* when to stop userspace polling */ bool poll_ready; /* has polling detected an event? */ + AioPolledEvent poll; }; /* Add a handler to a ready list */ diff --git a/util/aio-win32.c b/util/aio-win32.c index 948ef47a4d3..6583d5c5f31 100644 --- a/util/aio-win32.c +++ b/util/aio-win32.c @@ -18,6 +18,7 @@ #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" @@ -438,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.c b/util/async.c index 8f90ddc3047..2719c629ae9 100644 --- a/util/async.c +++ b/util/async.c @@ -30,11 +30,12 @@ #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" #include "qemu/coroutine-tls.h" -#include "sysemu/cpu-timers.h" +#include "exec/icount.h" #include "trace.h" /***********************************************************/ @@ -94,13 +95,15 @@ static void aio_bh_enqueue(QEMUBH *bh, unsigned new_flags) } 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() */ @@ -366,7 +369,7 @@ aio_ctx_finalize(GSource *source) QEMUBH *bh; unsigned flags; - thread_pool_free(ctx->thread_pool); + thread_pool_free_aio(ctx->thread_pool); #ifdef CONFIG_LINUX_AIO if (ctx->linux_aio) { @@ -432,10 +435,10 @@ GSource *aio_get_g_source(AioContext *ctx) return &ctx->source; } -ThreadPool *aio_get_thread_pool(AioContext *ctx) +ThreadPoolAio *aio_get_thread_pool(AioContext *ctx) { if (!ctx->thread_pool) { - ctx->thread_pool = thread_pool_new(ctx); + ctx->thread_pool = thread_pool_new_aio(ctx); } return ctx->thread_pool; } @@ -562,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); } } @@ -608,7 +609,6 @@ AioContext *aio_context_new(Error **errp) qemu_rec_mutex_init(&ctx->lock); timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx); - ctx->poll_ns = 0; ctx->poll_max_ns = 0; ctx->poll_grow = 0; ctx->poll_shrink = 0; @@ -707,9 +707,7 @@ void aio_co_enter(AioContext *ctx, 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); } } @@ -723,16 +721,6 @@ void aio_context_unref(AioContext *ctx) g_source_unref(&ctx->source); } -void aio_context_acquire(AioContext *ctx) -{ - qemu_rec_mutex_lock(&ctx->lock); -} - -void aio_context_release(AioContext *ctx) -{ - qemu_rec_mutex_unlock(&ctx->lock); -} - QEMU_DEFINE_STATIC_CO_TLS(AioContext *, my_aiocontext) AioContext *qemu_get_current_aio_context(void) @@ -741,7 +729,7 @@ AioContext *qemu_get_current_aio_context(void) if (ctx) { return ctx; } - if (qemu_mutex_iothread_locked()) { + if (bql_locked()) { /* Possibly in a vCPU thread. */ return qemu_get_aio_context(); } @@ -758,7 +746,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/block-helpers.c b/util/block-helpers.c index c4851432f56..052b4e1476a 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 b53295a529a..838b0825f66 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 3e6a5dfd632..522146dab97 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -26,265 +26,101 @@ #include "qemu/bswap.h" #include "host/cpuinfo.h" -static bool -buffer_zero_int(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; - - 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; - } -} +typedef bool (*biz_accel_fn)(const void *, size_t); -#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) || defined(__SSE2__) -#include - -/* Note that each of these vectorized functions require len >= 64. */ - -static bool __attribute__((target("sse2"))) -buffer_zero_sse2(const void *buf, size_t len) +static bool buffer_is_zero_int_lt256(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(); + uint64_t t; + const uint64_t *p, *e; - /* 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)) { - return false; - } - t = p[-4] | p[-3] | p[-2] | p[-1]; - p += 4; + /* + * 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; } - /* 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; -} - -#ifdef CONFIG_AVX2_OPT -static bool __attribute__((target("sse4"))) -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); + 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); - /* 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; + /* Read 0 to 31 aligned words from the middle. */ + while (p < e) { + t |= *p++; } - - /* 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; } -static bool __attribute__((target("avx2"))) -buffer_zero_avx2(const void *buf, size_t len) +static bool buffer_is_zero_int_ge256(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); - - /* 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); -} -#endif /* CONFIG_AVX2_OPT */ + /* + * 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); -#ifdef CONFIG_AVX512F_OPT -static bool __attribute__((target("avx512f"))) -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); + /* 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 aligned blocks of 256. */ - while (p <= e) { - __builtin_prefetch(p); - if (unlikely(_mm512_test_epi64_mask(t, t))) { + /* + * 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 |= _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); + t = p[0] | p[1] | p[2] | p[3] | p[4] | p[5] | p[6] | p[7]; + p += 8; + } while (p < e - 7); + return t == 0; } -#endif /* CONFIG_AVX512F_OPT */ -/* - * 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_USED 0 -# define INIT_LENGTH 0 -# define INIT_ACCEL buffer_zero_int -#else -# ifndef __SSE2__ -# error "ISA selection confusion" -# endif -# define INIT_USED CPUINFO_SSE2 -# define INIT_LENGTH 64 -# define INIT_ACCEL buffer_zero_sse2 -#endif +#include "host/bufferiszero.c.inc" -static unsigned used_accel = INIT_USED; -static unsigned length_to_accel = INIT_LENGTH; -static bool (*buffer_accel)(const void *, size_t) = INIT_ACCEL; +static biz_accel_fn buffer_is_zero_accel; +static unsigned accel_index; -static unsigned __attribute__((noinline)) -select_accel_cpuinfo(unsigned info) +bool buffer_is_zero_ool(const void *buf, size_t len) { - /* Array is sorted in order of algorithm preference. */ - static const struct { - unsigned bit; - unsigned len; - bool (*fn)(const void *, size_t); - } all[] = { -#ifdef CONFIG_AVX512F_OPT - { CPUINFO_AVX512F, 256, buffer_zero_avx512 }, -#endif -#ifdef CONFIG_AVX2_OPT - { CPUINFO_AVX2, 128, buffer_zero_avx2 }, - { CPUINFO_SSE4, 64, buffer_zero_sse4 }, -#endif - { CPUINFO_SSE2, 64, buffer_zero_sse2 }, - { CPUINFO_ALWAYS, 0, buffer_zero_int }, - }; + 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; + } - for (unsigned i = 0; i < ARRAY_SIZE(all); ++i) { - if (info & all[i].bit) { - length_to_accel = all[i].len; - buffer_accel = all[i].fn; - return all[i].bit; - } + if (likely(len >= 256)) { + return buffer_is_zero_accel(buf, len); } - return 0; + return buffer_is_zero_int_lt256(buf, len); } -#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) -static void __attribute__((constructor)) init_accel(void) +bool buffer_is_zero_ge256(const void *buf, size_t len) { - used_accel = select_accel_cpuinfo(cpuinfo_init()); + return buffer_is_zero_accel(buf, len); } -#endif /* CONFIG_AVX2_OPT */ bool test_buffer_is_zero_next_accel(void) { - /* - * Accumulate the accelerators that we've already tested, and - * remove them from the set to test this round. We'll get back - * a zero from select_accel_cpuinfo when there are no more. - */ - unsigned used = select_accel_cpuinfo(cpuinfo & ~used_accel); - used_accel |= used; - return used; -} - -static bool select_accel_fn(const void *buf, size_t len) -{ - if (likely(len >= length_to_accel)) { - return buffer_accel(buf, len); + if (accel_index != 0) { + buffer_is_zero_accel = accel_table[--accel_index]; + return true; } - 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) +static void __attribute__((constructor)) init_accel(void) { - if (unlikely(len == 0)) { - 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); + accel_index = best_accel(); + buffer_is_zero_accel = accel_table[accel_index]; } diff --git a/util/cacheflush.c b/util/cacheflush.c index a08906155a9..17c58918de6 100644 --- a/util/cacheflush.c +++ b/util/cacheflush.c @@ -229,6 +229,10 @@ static void __attribute__((constructor)) init_cache_info(void) /* Caches are coherent and do not require flushing; symbol inline. */ +#elif defined(EMSCRIPTEN) + +/* Wasm doesn't have executable region of memory. */ + #elif defined(__aarch64__) && !defined(CONFIG_WIN32) /* * For Windows, we use generic implementation of flush_idcache_range, that @@ -279,9 +283,11 @@ void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len) for (p = rw & -dcache_lsize; p < rw + len; p += dcache_lsize) { asm volatile("dc\tcvau, %0" : : "r" (p) : "memory"); } - asm volatile("dsb\tish" : : : "memory"); } + /* DSB unconditionally to ensure any outstanding writes are committed. */ + asm volatile("dsb\tish" : : : "memory"); + /* * If CTR_EL0.DIC is enabled, Instruction cache cleaning to the Point * of Unification is not required for instruction to data coherence. diff --git a/util/chardev_open.c b/util/chardev_open.c new file mode 100644 index 00000000000..f7764297882 --- /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 e2690c5f414..037d6416c4a 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 ddc98fb4f88..8ef603d081e 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-wasm.c b/util/coroutine-wasm.c new file mode 100644 index 00000000000..cb1ec92509c --- /dev/null +++ b/util/coroutine-wasm.c @@ -0,0 +1,127 @@ +/* + * emscripten fiber coroutine initialization code + * based on coroutine-ucontext.c + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2011 Kevin Wolf + * + * 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.0 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/coroutine_int.h" +#include "qemu/coroutine-tls.h" + +#include + +typedef struct { + Coroutine base; + void *stack; + size_t stack_size; + + void *asyncify_stack; + size_t asyncify_stack_size; + + CoroutineAction action; + + emscripten_fiber_t fiber; +} CoroutineEmscripten; + +/** + * Per-thread coroutine bookkeeping + */ +QEMU_DEFINE_STATIC_CO_TLS(Coroutine *, current); +QEMU_DEFINE_STATIC_CO_TLS(CoroutineEmscripten *, leader); +size_t leader_asyncify_stack_size = COROUTINE_STACK_SIZE; + +static void coroutine_trampoline(void *co_) +{ + Coroutine *co = co_; + + while (true) { + co->entry(co->entry_arg); + qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); + } +} + +Coroutine *qemu_coroutine_new(void) +{ + CoroutineEmscripten *co; + + co = g_malloc0(sizeof(*co)); + + co->stack_size = COROUTINE_STACK_SIZE; + co->stack = qemu_alloc_stack(&co->stack_size); + + co->asyncify_stack_size = COROUTINE_STACK_SIZE; + co->asyncify_stack = g_malloc0(co->asyncify_stack_size); + emscripten_fiber_init(&co->fiber, coroutine_trampoline, &co->base, + co->stack, co->stack_size, co->asyncify_stack, + co->asyncify_stack_size); + + return &co->base; +} + +void qemu_coroutine_delete(Coroutine *co_) +{ + CoroutineEmscripten *co = DO_UPCAST(CoroutineEmscripten, base, co_); + + qemu_free_stack(co->stack, co->stack_size); + g_free(co->asyncify_stack); + g_free(co); +} + +CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, + CoroutineAction action) +{ + CoroutineEmscripten *from = DO_UPCAST(CoroutineEmscripten, base, from_); + CoroutineEmscripten *to = DO_UPCAST(CoroutineEmscripten, base, to_); + + set_current(to_); + to->action = action; + emscripten_fiber_swap(&from->fiber, &to->fiber); + return from->action; +} + +Coroutine *qemu_coroutine_self(void) +{ + Coroutine *self = get_current(); + + if (!self) { + CoroutineEmscripten *leaderp = get_leader(); + if (!leaderp) { + leaderp = g_malloc0(sizeof(*leaderp)); + leaderp->asyncify_stack = g_malloc0(leader_asyncify_stack_size); + leaderp->asyncify_stack_size = leader_asyncify_stack_size; + emscripten_fiber_init_from_current_context( + &leaderp->fiber, + leaderp->asyncify_stack, + leaderp->asyncify_stack_size); + leaderp->stack = leaderp->fiber.stack_limit; + leaderp->stack_size = + leaderp->fiber.stack_base - leaderp->fiber.stack_limit; + set_leader(leaderp); + } + self = &leaderp->base; + set_current(self); + } + return self; +} + +bool qemu_in_coroutine(void) +{ + Coroutine *self = get_current(); + + return self && self->caller; +} diff --git a/util/cpuinfo-aarch64.c b/util/cpuinfo-aarch64.c index 7d39f47e3b8..57468890c33 100644 --- a/util/cpuinfo-aarch64.c +++ b/util/cpuinfo-aarch64.c @@ -13,10 +13,22 @@ # 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; @@ -52,16 +64,52 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info = CPUINFO_ALWAYS; -#ifdef CONFIG_LINUX +#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_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; diff --git a/util/cpuinfo-i386.c b/util/cpuinfo-i386.c index b2ed65bb101..c8c8a1b3705 100644 --- a/util/cpuinfo-i386.c +++ b/util/cpuinfo-i386.c @@ -34,11 +34,11 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) if (max >= 1) { __cpuid(1, a, b, c, d); - info |= (d & bit_CMOV ? CPUINFO_CMOV : 0); info |= (d & bit_SSE2 ? CPUINFO_SSE2 : 0); - info |= (c & bit_SSE4_1 ? CPUINFO_SSE4 : 0); + info |= (c & bit_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); diff --git a/util/cpuinfo-loongarch.c b/util/cpuinfo-loongarch.c new file mode 100644 index 00000000000..bb1f7f698bd --- /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 index 1ea3db0ac82..4d3d3aae0bc 100644 --- a/util/cpuinfo-ppc.c +++ b/util/cpuinfo-ppc.c @@ -6,11 +6,21 @@ #include "qemu/osdep.h" #include "host/cpuinfo.h" -#ifdef CONFIG_GETAUXVAL -# include -#else +#ifdef CONFIG_LINUX # include -# include "elf.h" +# 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; @@ -19,16 +29,17 @@ unsigned cpuinfo; unsigned __attribute__((constructor)) cpuinfo_init(void) { unsigned info = cpuinfo; - unsigned long hwcap, hwcap2; if (info) { return info; } - hwcap = qemu_getauxval(AT_HWCAP); - hwcap2 = qemu_getauxval(AT_HWCAP2); info = CPUINFO_ALWAYS; +#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; @@ -40,7 +51,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info |= CPUINFO_V2_06; } - if (hwcap2 & PPC_FEATURE2_HAS_ISEL) { + if (hwcap2 & PPC_FEATURE2_ISEL) { info |= CPUINFO_ISEL; } if (hwcap & PPC_FEATURE_HAS_ALTIVEC) { @@ -53,11 +64,12 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) * always have both anyway, since VSX came with Power7 * and crypto came with Power8. */ - if (hwcap2 & PPC_FEATURE2_HAS_VEC_CRYPTO) { + 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 00000000000..0291b7218af --- /dev/null +++ b/util/cpuinfo-riscv.c @@ -0,0 +1,165 @@ +/* + * 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_ZBS + | 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_zbs) + info |= CPUINFO_ZBS; +#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; + info |= pair.value & RISCV_HWPROBE_EXT_ZBS ? CPUINFO_ZBS : 0; + left &= ~(CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZBS); +#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_ZBS) { + /* Probe for Zbs: bext zero,zero,zero. */ + got_sigill = 0; + asm volatile(".insn r 0x33, 5, 0x24, zero, zero, zero" + : : : "memory"); + info |= got_sigill ? 0 : CPUINFO_ZBS; + left &= ~CPUINFO_ZBS; + } + + 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/cutils.c b/util/cutils.c index 25373198adc..9803f11a596 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -1012,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. */ @@ -1135,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; @@ -1155,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. */ @@ -1195,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 00000000000..037dc0abf0a --- /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 db937c04276..15fdbb109d6 100644 --- a/util/envlist.c +++ b/util/envlist.c @@ -12,9 +12,6 @@ struct envlist { 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. */ @@ -51,72 +48,6 @@ envlist_free(envlist_t *envlist) 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; -} - /* * Sets environment value to envlist in similar manner * than putenv(3). diff --git a/util/error-report.c b/util/error-report.c index 6e44a557321..1b17c11de19 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 } /* diff --git a/util/error.c b/util/error.c index e5e247209a9..daea2142f30 100644 --- a/util/error.c +++ b/util/error.c @@ -15,15 +15,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/error-report.h" - -struct Error -{ - char *msg; - ErrorClass err_class; - const char *src, *func; - int line; - GString *hint; -}; +#include "qapi/error-internal.h" Error *error_abort; Error *error_fatal; @@ -32,8 +24,13 @@ Error *error_warn; static void error_handle(Error **errp, Error *err) { if (errp == &error_abort) { - fprintf(stderr, "Unexpected error in %s() at %s:%d:\n", - err->func, err->src, err->line); + if (err->func) { + fprintf(stderr, "Unexpected error in %s() at %.*s:%d:\n", + err->func, err->src_len, err->src, err->line); + } else { + fprintf(stderr, "Unexpected error at %.*s:%d:\n", + err->src_len, err->src, err->line); + } error_report("%s", error_get_pretty(err)); if (err->hint) { error_printf("%s", err->hint->str); @@ -75,6 +72,7 @@ static void error_setv(Error **errp, g_free(msg); } err->err_class = err_class; + err->src_len = -1; err->src = src; err->line = line; err->func = func; @@ -247,6 +245,17 @@ void warn_report_err(Error *err) error_free(err); } +bool warn_report_err_once_cond(bool *printed, Error *err) +{ + if (*printed) { + error_free(err); + return false; + } + *printed = true; + warn_report_err(err); + return true; +} + void error_reportf_err(Error *err, const char *fmt, ...) { va_list ap; diff --git a/util/event.c b/util/event.c new file mode 100644 index 00000000000..5a8141cd0e4 --- /dev/null +++ b/util/event.c @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/thread.h" + +/* + * Valid transitions: + * - FREE -> SET (qemu_event_set) + * - BUSY -> SET (qemu_event_set) + * - SET -> FREE (qemu_event_reset) + * - FREE -> BUSY (qemu_event_wait) + * + * With futex, the waking and blocking operations follow + * BUSY -> SET and FREE -> BUSY, respectively. + * + * Without futex, BUSY -> SET and FREE -> BUSY never happen. Instead, the waking + * operation follows FREE -> SET and the blocking operation will happen in + * qemu_event_wait() if the event is not SET. + * + * SET->BUSY does not happen (it can be observed from the outside but + * it really is SET->FREE->BUSY). + * + * busy->free provably cannot happen; to enforce it, the set->free transition + * is done with an OR, which becomes a no-op if the event has concurrently + * transitioned to free or busy. + */ + +#define EV_SET 0 +#define EV_FREE 1 +#define EV_BUSY -1 + +void qemu_event_init(QemuEvent *ev, bool init) +{ +#ifndef HAVE_FUTEX + pthread_mutex_init(&ev->lock, NULL); + pthread_cond_init(&ev->cond, NULL); +#endif + + ev->value = (init ? EV_SET : EV_FREE); + ev->initialized = true; +} + +void qemu_event_destroy(QemuEvent *ev) +{ + assert(ev->initialized); + ev->initialized = false; +#ifndef HAVE_FUTEX + pthread_mutex_destroy(&ev->lock); + pthread_cond_destroy(&ev->cond); +#endif +} + +void qemu_event_set(QemuEvent *ev) +{ + assert(ev->initialized); + +#ifdef HAVE_FUTEX + /* + * 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) { + 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_all(ev); + } + } +#else + pthread_mutex_lock(&ev->lock); + /* Pairs with qemu_event_reset()'s load acquire. */ + qatomic_store_release(&ev->value, EV_SET); + pthread_cond_broadcast(&ev->cond); + pthread_mutex_unlock(&ev->lock); +#endif +} + +void qemu_event_reset(QemuEvent *ev) +{ + assert(ev->initialized); + +#ifdef HAVE_FUTEX + /* + * 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(); +#else + /* + * If futexes are not available, there are no EV_FREE->EV_BUSY + * transitions because wakeups are done entirely through the + * condition variable. Since qatomic_set() only writes EV_FREE, + * the load seems useless but in reality, the acquire synchronizes + * with qemu_event_set()'s store release: if qemu_event_reset() + * sees EV_SET here, then the caller will certainly see a + * successful condition and skip qemu_event_wait(): + * + * done = 1; if (done == 0) + * qemu_event_set() { qemu_event_reset() { + * lock(); + * ev->value = EV_SET -----> load ev->value + * ev->value = old value | EV_FREE + * cond_broadcast() + * unlock(); } + * } if (done == 0) + * // qemu_event_wait() not called + */ + qatomic_set(&ev->value, qatomic_load_acquire(&ev->value) | EV_FREE); +#endif +} + +void qemu_event_wait(QemuEvent *ev) +{ + assert(ev->initialized); + +#ifdef HAVE_FUTEX + while (true) { + /* + * 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(). + */ + unsigned value = qatomic_load_acquire(&ev->value); + if (value == EV_SET) { + break; + } + + 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. + * + * 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) { + break; + } + } + + /* + * 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); + } +#else + pthread_mutex_lock(&ev->lock); + while (qatomic_read(&ev->value) != EV_SET) { + pthread_cond_wait(&ev->cond, &ev->lock); + } + pthread_mutex_unlock(&ev->lock); +#endif +} diff --git a/util/fdmon-epoll.c b/util/fdmon-epoll.c index c6413cb18fe..9fb8800dde8 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" diff --git a/util/fdmon-io_uring.c b/util/fdmon-io_uring.c index 16054c5ede3..b0d68bdc44d 100644 --- a/util/fdmon-io_uring.c +++ b/util/fdmon-io_uring.c @@ -180,7 +180,7 @@ 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 diff --git a/util/fifo8.c b/util/fifo8.c index d4d1c135e03..a26da66ad2c 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 2c45f7f1764..7352b9fe53e 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 b124107d61e..0735cd82715 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 9465dda085d..33607d5ff24 100644 --- a/util/guest-random.c +++ b/util/guest-random.c @@ -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) { uint64_t seed; - if (parse_uint_full(optarg, 0, &seed)) { - error_setg(errp, "Invalid seed number: %s", optarg); + 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 6d6e1b595d9..d9a1dabc630 100644 --- a/util/hbitmap.c +++ b/util/hbitmap.c @@ -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 9921114b3c7..f29ffceb746 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 @@ -15,51 +15,103 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/host-utils.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) { - const char *buf = bufptr; - int i, c; + 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 (len > QEMU_HEXDUMP_LINE_BYTES) { - len = QEMU_HEXDUMP_LINE_BYTES; + 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); } - line += snprintf(line, 6, "%04x:", b); - for (i = 0; i < QEMU_HEXDUMP_LINE_BYTES; i++) { - if ((i % 4) == 0) { - *line++ = ' '; + 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 (i < len) { - line += sprintf(line, " %02x", (unsigned char)buf[b + i]); - } else { - line += sprintf(line, " "); + 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)); } - if (ascii) { - *line++ = ' '; - for (i = 0; i < len; i++) { - c = buf[b + i]; - if (c < ' ' || c > '~') { - c = '.'; - } - *line++ = c; + + return str; +} + +static void asciidump_line(char *line, const void *bufptr, size_t len) +{ + const char *buf = bufptr; + + for (size_t i = 0; i < len; i++) { + char c = buf[i]; + + 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 += 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); - 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); + fprintf(fp, "%s: %04zx: %-*s %s\n", + prefix, b, QEMU_HEXDUMP_LINE_WIDTH, str->str, ascii); } } + +void qemu_hexdump_to_buffer(char *restrict buffer, size_t buffer_size, + const uint8_t *restrict data, size_t data_size) +{ + size_t i; + uint64_t required_buffer_size; + bool overflow = umul64_overflow(data_size, 2, &required_buffer_size); + overflow |= uadd64_overflow(required_buffer_size, 1, &required_buffer_size); + assert(!overflow && buffer_size >= required_buffer_size); + + for (i = 0; i < data_size; i++) { + uint8_t val = data[i]; + *(buffer++) = hexdump_nibble(val >> 4); + *(buffer++) = hexdump_nibble(val & 0xf); + } + *buffer = '\0'; +} diff --git a/util/iov.c b/util/iov.c index 7e73948f5e3..f8536f0474a 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 @@ -36,7 +37,6 @@ size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt, offset -= iov[i].iov_len; } } - assert(offset == 0); return done; } @@ -55,7 +55,6 @@ size_t iov_to_buf_full(const struct iovec *iov, const unsigned int iov_cnt, offset -= iov[i].iov_len; } } - assert(offset == 0); return done; } @@ -74,7 +73,6 @@ size_t iov_memset(const struct iovec *iov, const unsigned int iov_cnt, offset -= iov[i].iov_len; } } - assert(offset == 0); return done; } @@ -92,7 +90,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 +101,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 +113,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 +143,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 +200,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; @@ -266,7 +274,6 @@ unsigned iov_copy(struct iovec *dst_iov, unsigned int dst_iov_cnt, bytes -= len; offset = 0; } - assert(offset == 0); return j; } @@ -337,7 +344,6 @@ size_t qemu_iovec_concat_iov(QEMUIOVector *dst, soffset -= src_iov[i].iov_len; } } - assert(soffset == 0); /* offset beyond end of src */ return done; } diff --git a/util/iova-tree.c b/util/iova-tree.c index 536789797e4..5b0c95ff158 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; @@ -280,3 +257,49 @@ void iova_tree_destroy(IOVATree *tree) g_tree_destroy(tree->tree); g_free(tree); } + +static int gpa_tree_compare(gconstpointer a, gconstpointer b, gpointer data) +{ + const DMAMap *m1 = a, *m2 = b; + + if (m1->translated_addr > m2->translated_addr + m2->size) { + return 1; + } + + if (m1->translated_addr + m1->size < m2->translated_addr) { + return -1; + } + + /* Overlapped */ + return 0; +} + +IOVATree *gpa_tree_new(void) +{ + IOVATree *gpa_tree = g_new0(IOVATree, 1); + + gpa_tree->tree = g_tree_new_full(gpa_tree_compare, NULL, g_free, NULL); + + return gpa_tree; +} + +int gpa_tree_insert(IOVATree *tree, const DMAMap *map) +{ + DMAMap *new; + + if (map->translated_addr + map->size < map->translated_addr || + map->perm == IOMMU_NONE) { + return IOVA_ERR_INVALID; + } + + /* We don't allow inserting ranges that overlap with existing ones */ + if (iova_tree_find(tree, map)) { + return IOVA_ERR_OVERLAP; + } + + new = g_new0(DMAMap, 1); + memcpy(new, map, sizeof(*new)); + iova_tree_insert_internal(tree->tree, new); + + return IOVA_OK; +} diff --git a/util/keyval.c b/util/keyval.c index 66a5b4740f1..a70629a4812 100644 --- a/util/keyval.c +++ b/util/keyval.c @@ -91,9 +91,9 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" #include "qemu/cutils.h" #include "qemu/keyval.h" #include "qemu/help_option.h" diff --git a/util/lockcnt.c b/util/lockcnt.c index 5da36946b1b..92c9f8ceca8 100644 --- a/util/lockcnt.c +++ b/util/lockcnt.c @@ -7,14 +7,16 @@ * Paolo Bonzini */ #include "qemu/osdep.h" +#include "qemu/lockcnt.h" #include "qemu/thread.h" #include "qemu/atomic.h" #include "trace.h" -#ifdef CONFIG_LINUX -#include "qemu/futex.h" +#ifdef HAVE_FUTEX -/* On Linux, bits 0-1 are a futex-based lock, bits 2-31 are the counter. +/* + * When futex is available, bits 0-1 are a futex-based lock, bits 2-31 are the + * counter. * For the mutex algorithm see Ulrich Drepper's "Futexes Are Tricky" (ok, * this is not the most relaxing citation I could make...). It is similar * to mutex2 in the paper. @@ -105,7 +107,7 @@ static bool qemu_lockcnt_cmpxchg_or_wait(QemuLockCnt *lockcnt, int *val, static void lockcnt_wake(QemuLockCnt *lockcnt) { trace_lockcnt_futex_wake(lockcnt); - qemu_futex_wake(&lockcnt->count, 1); + qemu_futex_wake_single(&lockcnt->count); } void qemu_lockcnt_inc(QemuLockCnt *lockcnt) diff --git a/util/log.c b/util/log.c index def88a9402b..b87d399e4cb 100644 --- a/util/log.c +++ b/util/log.c @@ -298,6 +298,8 @@ static bool qemu_set_log_internal(const char *filename, bool changed_name, r->fd = logfile; qatomic_rcu_set(&global_file, NULL); call_rcu(r, rcu_close_file, rcu); + } + if (changed_name) { logfile = NULL; } } @@ -464,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", @@ -497,6 +503,8 @@ const QEMULogItem qemu_log_items[] = { "open a separate log file per thread; filename must contain '%d'" }, { CPU_LOG_TB_VPU, "vpu", "include VPU registers in the 'cpu' logging" }, + { LOG_INVALID_MEM, "invalid_mem", + "log invalid memory accesses" }, { 0, NULL, NULL }, }; diff --git a/util/main-loop.c b/util/main-loop.c index 797b640c415..51aeb2432e7 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -26,8 +26,9 @@ #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/timer.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/replay.h" +#include "system/cpu-timers.h" +#include "exec/icount.h" +#include "system/replay.h" #include "qemu/main-loop.h" #include "block/aio.h" #include "block/thread-pool.h" @@ -192,10 +193,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); @@ -215,7 +213,6 @@ static void main_loop_init(EventLoopBase *base, Error **errp) main_loop_update_params(base, errp); mloop = m; - return; } static bool main_loop_can_be_deleted(EventLoopBase *base) @@ -223,7 +220,7 @@ static bool main_loop_can_be_deleted(EventLoopBase *base) return false; } -static void main_loop_class_init(ObjectClass *oc, void *class_data) +static void main_loop_class_init(ObjectClass *oc, const void *class_data) { EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(oc); @@ -302,13 +299,13 @@ static int os_host_main_loop_wait(int64_t timeout) glib_pollfds_fill(&timeout); - qemu_mutex_unlock_iothread(); + bql_unlock(); replay_mutex_unlock(); ret = qemu_poll_ns((GPollFD *)gpollfds->data, gpollfds->len, timeout); replay_mutex_lock(); - qemu_mutex_lock_iothread(); + bql_lock(); glib_pollfds_poll(); @@ -517,7 +514,7 @@ static int os_host_main_loop_wait(int64_t timeout) poll_timeout_ns = qemu_soonest_timeout(poll_timeout_ns, timeout); - qemu_mutex_unlock_iothread(); + bql_unlock(); replay_mutex_unlock(); @@ -525,7 +522,7 @@ static int os_host_main_loop_wait(int64_t timeout) replay_mutex_lock(); - qemu_mutex_lock_iothread(); + bql_lock(); if (g_poll_ret > 0) { for (i = 0; i < w->num; i++) { w->revents[i] = poll_fds[n_poll_fds + i].revents; diff --git a/util/memfd.c b/util/memfd.c index 4a3c07e0bee..07beab174dc 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 @@ void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals, 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)); + } } } @@ -189,17 +194,27 @@ bool qemu_memfd_alloc_check(void) /** * qemu_memfd_check(): * - * Check if host supports memfd. + * Check if host supports memfd. Cache the answer for the common case flags=0. */ bool qemu_memfd_check(unsigned int flags) { #ifdef CONFIG_LINUX - int mfd = memfd_create("test", flags | MFD_CLOEXEC); + int mfd; + static int memfd_check = MEMFD_TODO; + if (!flags && memfd_check != MEMFD_TODO) { + return memfd_check; + } + + mfd = memfd_create("test", flags | MFD_CLOEXEC); if (mfd >= 0) { close(mfd); - return true; } + if (!flags) { + memfd_check = (mfd >= 0) ? MEMFD_OK : MEMFD_KO; + } + return (mfd >= 0); + #endif return false; diff --git a/util/meson.build b/util/meson.build index c4827fd70aa..35029380a37 100644 --- a/util/meson.build +++ b/util/meson.build @@ -3,32 +3,39 @@ util_ss.add(files('thread-context.c'), numa) if not config_host_data.get('CONFIG_ATOMIC64') util_ss.add(files('atomic64.c')) endif -util_ss.add(when: 'CONFIG_POSIX', if_true: files('aio-posix.c')) -util_ss.add(when: 'CONFIG_POSIX', if_true: files('fdmon-poll.c')) -if config_host_data.get('CONFIG_EPOLL_CREATE1') - util_ss.add(files('fdmon-epoll.c')) +if host_os != 'windows' + util_ss.add(files('aio-posix.c')) + util_ss.add(files('fdmon-poll.c')) + if config_host_data.get('CONFIG_EPOLL_CREATE1') + util_ss.add(files('fdmon-epoll.c')) + endif + util_ss.add(files('compatfd.c')) + util_ss.add(files('event_notifier-posix.c')) + if host_os != 'emscripten' + util_ss.add(files('mmap-alloc.c')) + endif + 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, synchronization) 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 -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) if glib_has_gslice util_ss.add(files('qtree.c')) endif +util_ss.add(files('defer-call.c')) util_ss.add(files('envlist.c', 'path.c', 'module.c')) +util_ss.add(files('event.c')) util_ss.add(files('host-utils.c')) util_ss.add(files('bitmap.c', 'bitops.c')) util_ss.add(files('fifo8.c')) @@ -51,12 +58,11 @@ util_ss.add(files('qdist.c')) util_ss.add(files('qht.c')) util_ss.add(files('qsp.c')) util_ss.add(files('range.c')) +util_ss.add(files('reserved-region.c')) util_ss.add(files('stats64.c')) util_ss.add(files('systemd.c')) util_ss.add(files('transactions.c')) -util_ss.add(when: 'CONFIG_POSIX', if_true: files('drm.c')) util_ss.add(files('guest-random.c')) -util_ss.add(files('yank.c')) util_ss.add(files('int128.c')) util_ss.add(files('memalign.c')) util_ss.add(files('interval-tree.c')) @@ -69,7 +75,9 @@ 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 @@ -79,6 +87,8 @@ if have_block or have_ga util_ss.add(files('qemu-coroutine.c', 'qemu-coroutine-lock.c', 'qemu-coroutine-io.c')) util_ss.add(files(f'coroutine-@coroutine_backend@.c')) util_ss.add(files('thread-pool.c', 'qemu-timer.c')) +endif +if have_block or have_ga or have_user util_ss.add(files('qemu-sockets.c')) endif if have_block @@ -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,17 +108,32 @@ 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')) +elif cpu == 's390x' + util_ss.add(files('s390x_pci_mmio.c')) endif diff --git a/util/module.c b/util/module.c index 32e263163c7..1aa2079d013 100644 --- a/util/module.c +++ b/util/module.c @@ -234,7 +234,7 @@ int module_load(const char *prefix, const char *name, Error **errp) search_dir = getenv("QEMU_MODULE_DIR"); if (search_dir != NULL) { - dirs[n_dirs++] = g_strdup_printf("%s", search_dir); + dirs[n_dirs++] = g_strdup(search_dir); } dirs[n_dirs++] = get_relocated_path(CONFIG_QEMU_MODDIR); @@ -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 76bab212ae9..c6e158ffb33 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 e996c4744af..770369831bc 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 } @@ -277,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(const char *name, int flags, mode_t mode) { int ret; @@ -305,7 +319,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) { @@ -314,14 +327,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 @@ -393,21 +399,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 4d583da7ce3..4ff577e5de6 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -31,7 +31,7 @@ #include -#include "sysemu/sysemu.h" +#include "system/system.h" #include "trace.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -42,6 +42,7 @@ #include "qemu/cutils.h" #include "qemu/units.h" #include "qemu/thread-context.h" +#include "qemu/main-loop.h" #ifdef CONFIG_LINUX #include @@ -57,17 +58,22 @@ #include #endif +#include "qemu/memalign.h" #include "qemu/mmap-alloc.h" #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 { @@ -106,6 +112,21 @@ int qemu_get_thread_id(void) #endif } +int qemu_kill_thread(int tid, int sig) +{ +#if defined(__linux__) + return syscall(__NR_tgkill, getpid(), tid, sig); +#elif defined(__FreeBSD__) + return thr_kill2(getpid(), tid, sig); +#elif defined(__NetBSD__) + return _lwp_kill(tid, sig); +#elif defined(__OpenBSD__) + return thrkill(tid, sig, NULL); +#else + return kill(tid, sig); +#endif +} + int qemu_daemon(int nochdir, int noclose) { return daemon(nochdir, noclose); @@ -190,11 +211,21 @@ void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared, const uint32_t qemu_map_flags = (shared ? QEMU_MAP_SHARED : 0) | (noreserve ? QEMU_MAP_NORESERVE : 0); size_t align = QEMU_VMALLOC_ALIGN; +#ifndef EMSCRIPTEN void *ptr = qemu_ram_mmap(-1, size, align, qemu_map_flags, 0); if (ptr == MAP_FAILED) { return NULL; } +#else + /* + * qemu_ram_mmap is not implemented for Emscripten. Use qemu_memalign + * for the anonymous allocation. noreserve is ignored as there is no swap + * space on Emscripten, and shared is ignored as there is no other + * processes on Emscripten. + */ + void *ptr = qemu_memalign(align, size); +#endif if (alignment) { *alignment = align; @@ -207,7 +238,16 @@ void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared, void qemu_anon_ram_free(void *ptr, size_t size) { trace_qemu_anon_ram_free(ptr, size); +#ifndef EMSCRIPTEN qemu_ram_munmap(-1, ptr, size); +#else + /* + * qemu_ram_munmap is not implemented for Emscripten and qemu_memalign + * was used for the allocation. Use the corresponding freeing function + * here. + */ + qemu_vfree(ptr); +#endif } void qemu_socket_set_block(int fd) @@ -258,7 +298,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]); @@ -412,19 +452,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); @@ -432,63 +497,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) @@ -497,15 +603,24 @@ 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; +#ifndef EMSCRIPTEN size_t hpagesize = qemu_fd_getpagesize(fd); +#else + /* + * mmap-alloc.c is excluded from Emscripten build, so qemu_fd_getpagesize + * is unavailable. Fallback to the lower level implementation. + */ + size_t hpagesize = qemu_real_host_page_size(); +#endif 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 @@ -534,16 +649,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) { @@ -555,6 +671,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) @@ -585,7 +702,7 @@ char *qemu_get_pid_name(pid_t pid) void *qemu_alloc_stack(size_t *sz) { - void *ptr, *guardpage; + void *ptr; int flags; #ifdef CONFIG_DEBUG_STACK_USAGE void *ptr2; @@ -618,17 +735,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(); } @@ -742,3 +850,179 @@ 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); + } +} + +int qemu_shm_alloc(size_t size, Error **errp) +{ + g_autoptr(GString) shm_name = g_string_new(NULL); + int fd, oflag, cur_sequence; + static int sequence; + mode_t mode; + + cur_sequence = qatomic_fetch_inc(&sequence); + + /* + * 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; + + /* + * 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-%d", getpid(), + cur_sequence); + + fd = shm_open(shm_name->str, oflag, mode); + if (fd < 0) { + error_setg_errno(errp, errno, + "failed to create POSIX shared memory"); + return -1; + } + + /* + * 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, size) == -1) { + error_setg_errno(errp, errno, + "failed to resize POSIX shared memory to %zu", size); + close(fd); + return -1; + } + + return fd; +} diff --git a/util/oslib-win32.c b/util/oslib-win32.c index 19a0ea7fbe6..b7351634ece 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -264,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(); @@ -274,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) @@ -479,7 +487,7 @@ int qemu_bind_wrap(int sockfd, const struct sockaddr *addr, return ret; } -EXCEPTION_DISPOSITION +QEMU_USED EXCEPTION_DISPOSITION win32_close_exception_handler(struct _EXCEPTION_RECORD *exception_record, void *registration, struct _CONTEXT *context, void *dispatcher) @@ -869,3 +877,9 @@ void qemu_win32_map_free(void *ptr, HANDLE h, Error **errp) } CloseHandle(h); } + +int qemu_shm_alloc(size_t size, Error **errp) +{ + error_setg(errp, "Shared memory is not supported."); + return -1; +} diff --git a/util/qemu-co-shared-resource.c b/util/qemu-co-shared-resource.c index a66cc07e75b..752eb5a1c5f 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 42076efe1ef..d1fc49c507d 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/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/config-file.h" -#include "hw/boards.h" -static QemuOptsList *vm_config_groups[48]; -static QemuOptsList *drive_config_groups[5]; +QemuOptsList *vm_config_groups[48]; +QemuOptsList *drive_config_groups[5]; static QemuOptsList *find_list(QemuOptsList **lists, const char *group, Error **errp) @@ -55,204 +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; - } - - 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; -} - QemuOptsList *qemu_find_opts_err(const char *group, Error **errp) { return find_list(vm_config_groups, group, errp); diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c index 17a88f65053..64d6264fc74 100644 --- a/util/qemu-coroutine.c +++ b/util/qemu-coroutine.c @@ -18,73 +18,210 @@ #include "qemu/atomic.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; + + /* This batch holds up to @COROUTINE_POOL_BATCH_MAX_SIZE coroutines */ + QSLIST_HEAD(, Coroutine) list; + unsigned int size; +} CoroutinePoolBatch; + +typedef QSLIST_HEAD(, CoroutinePoolBatch) CoroutinePool; + +/* Host operating system limit on number of pooled coroutines */ +static unsigned int global_pool_hard_max_size; -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); +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; -static void coroutine_pool_cleanup(Notifier *n, void *value) +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); } -Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque) +static void local_pool_cleanup(Notifier *n, void *value) { - Coroutine *co = NULL; + 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); + } +} - 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); - } +/* 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 (co) { - QSLIST_REMOVE_HEAD(alloc_pool, pool_next); - set_alloc_pool_size(get_alloc_pool_size() - 1); + } + + 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 (IS_ENABLED(CONFIG_COROUTINE_POOL)) { + co = coroutine_pool_get(); + } + if (!co) { co = qemu_coroutine_new(); } @@ -99,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) @@ -223,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 eedd08929b4..770300dff12 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -27,10 +27,10 @@ #include "qapi/error.h" #include "qemu/error-report.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qapi/qmp/qerror.h" #include "qemu/option_int.h" #include "qemu/cutils.h" @@ -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 83e84b11869..4773755fd58 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -30,6 +30,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "qemu/cutils.h" +#include "qemu/option.h" #include "trace.h" #ifndef AI_ADDRCONFIG @@ -44,6 +45,14 @@ # define AI_NUMERICSERV 0 #endif +/* + * On macOS TCP_KEEPIDLE is available under a different name, TCP_KEEPALIVE. + * https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/bsd/man/man4/tcp.4#L172 + */ +#if defined(TCP_KEEPALIVE) && !defined(TCP_KEEPIDLE) +# define TCP_KEEPIDLE TCP_KEEPALIVE +#endif + static int inet_getport(struct addrinfo *e) { @@ -205,6 +214,58 @@ static int try_bind(int socket, InetSocketAddress *saddr, struct addrinfo *e) #endif } +static int inet_set_sockopts(int sock, InetSocketAddress *saddr, Error **errp) +{ + if (saddr->keep_alive) { + int keep_alive = 1; + int ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, + &keep_alive, sizeof(keep_alive)); + + if (ret < 0) { + error_setg_errno(errp, errno, + "Unable to set keep-alive option on socket"); + return -1; + } +#ifdef HAVE_TCP_KEEPCNT + if (saddr->has_keep_alive_count && saddr->keep_alive_count) { + int keep_count = saddr->keep_alive_count; + ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keep_count, + sizeof(keep_count)); + if (ret < 0) { + error_setg_errno(errp, errno, + "Unable to set TCP keep-alive count option on socket"); + return -1; + } + } +#endif +#ifdef HAVE_TCP_KEEPIDLE + if (saddr->has_keep_alive_idle && saddr->keep_alive_idle) { + int keep_idle = saddr->keep_alive_idle; + ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keep_idle, + sizeof(keep_idle)); + if (ret < 0) { + error_setg_errno(errp, errno, + "Unable to set TCP keep-alive idle option on socket"); + return -1; + } + } +#endif +#ifdef HAVE_TCP_KEEPINTVL + if (saddr->has_keep_alive_interval && saddr->keep_alive_interval) { + int keep_interval = saddr->keep_alive_interval; + ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keep_interval, + sizeof(keep_interval)); + if (ret < 0) { + error_setg_errno(errp, errno, + "Unable to set TCP keep-alive interval option on socket"); + return -1; + } + } +#endif + } + return 0; +} + static int inet_listen_saddr(InetSocketAddress *saddr, int port_offset, int num, @@ -220,12 +281,6 @@ static int inet_listen_saddr(InetSocketAddress *saddr, int saved_errno = 0; bool socket_created = false; - if (saddr->keep_alive) { - error_setg(errp, "keep-alive option is not supported for passive " - "sockets"); - return -1; - } - memset(&ai,0, sizeof(ai)); ai.ai_flags = AI_PASSIVE; if (saddr->has_numeric && saddr->numeric) { @@ -287,11 +342,20 @@ static int inet_listen_saddr(InetSocketAddress *saddr, port_min = inet_getport(e); port_max = saddr->has_to ? saddr->to + port_offset : port_min; for (p = port_min; p <= port_max; p++) { + if (slisten >= 0) { + /* + * We have a socket we tried with the previous port. It cannot + * be rebound, we need to close it and create a new one. + */ + close(slisten); + slisten = -1; + } inet_setport(e, p); slisten = create_fast_reuse_socket(e); if (slisten < 0) { - /* First time we expect we might fail to create the socket + /* + * First time we expect we might fail to create the socket * eg if 'e' has AF_INET6 but ipv6 kmod is not loaded. * Later iterations should always succeed if first iteration * worked though, so treat that as fatal. @@ -301,40 +365,41 @@ static int inet_listen_saddr(InetSocketAddress *saddr, } else { error_setg_errno(errp, errno, "Failed to recreate failed listening socket"); - goto listen_failed; + goto fail; } } socket_created = true; rc = try_bind(slisten, saddr, e); if (rc < 0) { - if (errno != EADDRINUSE) { - error_setg_errno(errp, errno, "Failed to bind socket"); - goto listen_failed; - } - } else { - if (!listen(slisten, num)) { - goto listen_ok; + if (errno == EADDRINUSE) { + /* This port is already used, try the next one */ + continue; } - if (errno != EADDRINUSE) { - error_setg_errno(errp, errno, "Failed to listen on socket"); - goto listen_failed; + error_setg_errno(errp, errno, "Failed to bind socket"); + goto fail; + } + if (listen(slisten, num)) { + if (errno == EADDRINUSE) { + /* This port is already used, try the next one */ + continue; } + error_setg_errno(errp, errno, "Failed to listen on socket"); + goto fail; + } + /* We have a listening socket */ + if (inet_set_sockopts(slisten, saddr, errp) < 0) { + goto fail; } - /* Someone else managed to bind to the same port and beat us - * to listen on it! Socket semantics does not allow us to - * recover from this situation, so we need to recreate the - * socket to allow bind attempts for subsequent ports: - */ - close(slisten); - slisten = -1; + freeaddrinfo(res); + return slisten; } } error_setg_errno(errp, errno, socket_created ? "Failed to find an available port" : "Failed to create a socket"); -listen_failed: +fail: saved_errno = errno; if (slisten >= 0) { close(slisten); @@ -342,10 +407,6 @@ static int inet_listen_saddr(InetSocketAddress *saddr, freeaddrinfo(res); errno = saved_errno; return -1; - -listen_ok: - freeaddrinfo(res); - return slisten; } #ifdef _WIN32 @@ -367,7 +428,6 @@ static int inet_connect_addr(const InetSocketAddress *saddr, addr->ai_family); return -1; } - socket_set_fast_reuse(sock); /* connect to peer */ do { @@ -476,16 +536,9 @@ int inet_connect_saddr(InetSocketAddress *saddr, Error **errp) return sock; } - if (saddr->keep_alive) { - int val = 1; - int ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, - &val, sizeof(val)); - - if (ret < 0) { - error_setg_errno(errp, errno, "Unable to set KEEPALIVE"); - close(sock); - return -1; - } + if (inet_set_sockopts(sock, saddr, errp) < 0) { + close(sock); + return -1; } return sock; @@ -592,141 +645,146 @@ static int inet_dgram_saddr(InetSocketAddress *sraddr, return -1; } -/* compatibility wrapper */ -static int inet_parse_flag(const char *flagname, const char *optstr, bool *val, - Error **errp) -{ - char *end; - size_t len; - - end = strstr(optstr, ","); - if (end) { - if (end[1] == ',') { /* Reject 'ipv6=on,,foo' */ - error_setg(errp, "error parsing '%s' flag '%s'", flagname, optstr); - return -1; - } - len = end - optstr; - } else { - len = strlen(optstr); - } - if (len == 0 || (len == 3 && strncmp(optstr, "=on", len) == 0)) { - *val = true; - } else if (len == 4 && strncmp(optstr, "=off", len) == 0) { - *val = false; - } else { - error_setg(errp, "error parsing '%s' flag '%s'", flagname, optstr); - return -1; - } - return 0; -} +static QemuOptsList inet_opts = { + .name = "InetSocketAddress", + .head = QTAILQ_HEAD_INITIALIZER(inet_opts.head), + .implied_opt_name = "addr", + .desc = { + { + .name = "addr", + .type = QEMU_OPT_STRING, + }, + { + .name = "numeric", + .type = QEMU_OPT_BOOL, + }, + { + .name = "to", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "ipv4", + .type = QEMU_OPT_BOOL, + }, + { + .name = "ipv6", + .type = QEMU_OPT_BOOL, + }, + { + .name = "keep-alive", + .type = QEMU_OPT_BOOL, + }, +#ifdef HAVE_TCP_KEEPCNT + { + .name = "keep-alive-count", + .type = QEMU_OPT_NUMBER, + }, +#endif +#ifdef HAVE_TCP_KEEPIDLE + { + .name = "keep-alive-idle", + .type = QEMU_OPT_NUMBER, + }, +#endif +#ifdef HAVE_TCP_KEEPINTVL + { + .name = "keep-alive-interval", + .type = QEMU_OPT_NUMBER, + }, +#endif +#ifdef HAVE_IPPROTO_MPTCP + { + .name = "mptcp", + .type = QEMU_OPT_BOOL, + }, +#endif + { /* end of list */ } + }, +}; int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) { - const char *optstr, *h; - char host[65]; - char port[33]; - int to; - int pos; - char *begin; - + QemuOpts *opts = qemu_opts_parse(&inet_opts, str, true, errp); + if (!opts) { + return -1; + } memset(addr, 0, sizeof(*addr)); /* parse address */ - if (str[0] == ':') { - /* no host given */ - host[0] = '\0'; - if (sscanf(str, ":%32[^,]%n", port, &pos) != 1) { - error_setg(errp, "error parsing port in address '%s'", str); - return -1; - } - } else if (str[0] == '[') { + const char *addr_str = qemu_opt_get(opts, "addr"); + if (!addr_str) { + error_setg(errp, "error parsing address ''"); + return -1; + } + if (str[0] == '[') { /* IPv6 addr */ - if (sscanf(str, "[%64[^]]]:%32[^,]%n", host, port, &pos) != 2) { - error_setg(errp, "error parsing IPv6 address '%s'", str); + const char *ip_end = strstr(addr_str, "]:"); + if (!ip_end || ip_end - addr_str < 2 || strlen(ip_end) < 3) { + error_setg(errp, "error parsing IPv6 address '%s'", addr_str); return -1; } + addr->host = g_strndup(addr_str + 1, ip_end - addr_str - 1); + addr->port = g_strdup(ip_end + 2); } else { - /* hostname or IPv4 addr */ - if (sscanf(str, "%64[^:]:%32[^,]%n", host, port, &pos) != 2) { - error_setg(errp, "error parsing address '%s'", str); + /* no host, hostname or IPv4 addr */ + const char *port = strchr(addr_str, ':'); + if (!port || strlen(port) < 2) { + error_setg(errp, "error parsing address '%s'", addr_str); return -1; } + addr->host = g_strndup(addr_str, port - addr_str); + addr->port = g_strdup(port + 1); } - addr->host = g_strdup(host); - addr->port = g_strdup(port); - /* parse options */ - optstr = str + pos; - h = strstr(optstr, ",to="); - if (h) { - h += 4; - if (sscanf(h, "%d%n", &to, &pos) != 1 || - (h[pos] != '\0' && h[pos] != ',')) { - error_setg(errp, "error parsing to= argument"); - return -1; - } + if (qemu_opt_find(opts, "numeric")) { + addr->has_numeric = true, + addr->numeric = qemu_opt_get_bool(opts, "numeric", false); + } + if (qemu_opt_find(opts, "to")) { addr->has_to = true; - addr->to = to; + addr->to = qemu_opt_get_number(opts, "to", 0); } - begin = strstr(optstr, ",ipv4"); - if (begin) { - if (inet_parse_flag("ipv4", begin + 5, &addr->ipv4, errp) < 0) { - return -1; - } + if (qemu_opt_find(opts, "ipv4")) { addr->has_ipv4 = true; + addr->ipv4 = qemu_opt_get_bool(opts, "ipv4", false); } - begin = strstr(optstr, ",ipv6"); - if (begin) { - if (inet_parse_flag("ipv6", begin + 5, &addr->ipv6, errp) < 0) { - return -1; - } + if (qemu_opt_find(opts, "ipv6")) { addr->has_ipv6 = true; + addr->ipv6 = qemu_opt_get_bool(opts, "ipv6", false); } - begin = strstr(optstr, ",keep-alive"); - if (begin) { - if (inet_parse_flag("keep-alive", begin + strlen(",keep-alive"), - &addr->keep_alive, errp) < 0) - { - return -1; - } + if (qemu_opt_find(opts, "keep-alive")) { addr->has_keep_alive = true; + addr->keep_alive = qemu_opt_get_bool(opts, "keep-alive", false); + } +#ifdef HAVE_TCP_KEEPCNT + if (qemu_opt_find(opts, "keep-alive-count")) { + addr->has_keep_alive_count = true; + addr->keep_alive_count = qemu_opt_get_number(opts, "keep-alive-count", 0); + } +#endif +#ifdef HAVE_TCP_KEEPIDLE + if (qemu_opt_find(opts, "keep-alive-idle")) { + addr->has_keep_alive_idle = true; + addr->keep_alive_idle = qemu_opt_get_number(opts, "keep-alive-idle", 0); } +#endif +#ifdef HAVE_TCP_KEEPINTVL + if (qemu_opt_find(opts, "keep-alive-interval")) { + addr->has_keep_alive_interval = true; + addr->keep_alive_interval = qemu_opt_get_number(opts, "keep-alive-interval", 0); + } +#endif #ifdef HAVE_IPPROTO_MPTCP - begin = strstr(optstr, ",mptcp"); - if (begin) { - if (inet_parse_flag("mptcp", begin + strlen(",mptcp"), - &addr->mptcp, errp) < 0) - { - return -1; - } + if (qemu_opt_find(opts, "mptcp")) { addr->has_mptcp = true; + addr->mptcp = qemu_opt_get_bool(opts, "mptcp", 0); } #endif return 0; } -/** - * 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, @@ -1421,21 +1479,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; @@ -1464,7 +1507,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 b2e26e21205..ba725444ba6 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -317,154 +317,6 @@ void qemu_sem_wait(QemuSemaphore *sem) qemu_mutex_unlock(&sem->mutex); } -#ifdef __linux__ -#include "qemu/futex.h" -#else -static inline void qemu_futex_wake(QemuEvent *ev, int n) -{ - assert(ev->initialized); - pthread_mutex_lock(&ev->lock); - if (n == 1) { - pthread_cond_signal(&ev->cond); - } else { - pthread_cond_broadcast(&ev->cond); - } - pthread_mutex_unlock(&ev->lock); -} - -static inline void qemu_futex_wait(QemuEvent *ev, unsigned val) -{ - assert(ev->initialized); - pthread_mutex_lock(&ev->lock); - if (ev->value == val) { - pthread_cond_wait(&ev->cond, &ev->lock); - } - pthread_mutex_unlock(&ev->lock); -} -#endif - -/* Valid transitions: - * - free->set, when setting the event - * - busy->set, when setting the event, followed by qemu_futex_wake - * - set->free, when resetting the event - * - free->busy, when waiting - * - * set->busy does not happen (it can be observed from the outside but - * it really is set->free->busy). - * - * busy->free provably cannot happen; to enforce it, the set->free transition - * is done with an OR, which becomes a no-op if the event has concurrently - * transitioned to free or busy. - */ - -#define EV_SET 0 -#define EV_FREE 1 -#define EV_BUSY -1 - -void qemu_event_init(QemuEvent *ev, bool init) -{ -#ifndef __linux__ - pthread_mutex_init(&ev->lock, NULL); - pthread_cond_init(&ev->cond, NULL); -#endif - - ev->value = (init ? EV_SET : EV_FREE); - ev->initialized = true; -} - -void qemu_event_destroy(QemuEvent *ev) -{ - assert(ev->initialized); - ev->initialized = false; -#ifndef __linux__ - pthread_mutex_destroy(&ev->lock); - pthread_cond_destroy(&ev->cond); -#endif -} - -void qemu_event_set(QemuEvent *ev) -{ - 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. - */ - smp_mb(); - if (qatomic_read(&ev->value) != EV_SET) { - 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); - } - } -} - -void qemu_event_reset(QemuEvent *ev) -{ - assert(ev->initialized); - - /* - * 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) -{ - unsigned value; - - assert(ev->initialized); - - /* - * 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. - * - * 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); - } -} - static __thread NotifierList thread_exit; /* diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index a7fe3cc345f..ca2e0b512e2 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -231,135 +231,6 @@ void qemu_sem_wait(QemuSemaphore *sem) } } -/* Wrap a Win32 manual-reset event with a fast userspace path. The idea - * is to reset the Win32 event lazily, as part of a test-reset-test-wait - * sequence. Such a sequence is, indeed, how QemuEvents are used by - * RCU and other subsystems! - * - * Valid transitions: - * - free->set, when setting the event - * - busy->set, when setting the event, followed by SetEvent - * - set->free, when resetting the event - * - free->busy, when waiting - * - * set->busy does not happen (it can be observed from the outside but - * it really is set->free->busy). - * - * busy->free provably cannot happen; to enforce it, the set->free transition - * is done with an OR, which becomes a no-op if the event has concurrently - * transitioned to free or busy (and is faster than cmpxchg). - */ - -#define EV_SET 0 -#define EV_FREE 1 -#define EV_BUSY -1 - -void qemu_event_init(QemuEvent *ev, bool init) -{ - /* Manual reset. */ - ev->event = CreateEvent(NULL, TRUE, TRUE, NULL); - ev->value = (init ? EV_SET : EV_FREE); - ev->initialized = true; -} - -void qemu_event_destroy(QemuEvent *ev) -{ - assert(ev->initialized); - ev->initialized = false; - CloseHandle(ev->event); -} - -void qemu_event_set(QemuEvent *ev) -{ - 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. - */ - smp_mb(); - if (qatomic_read(&ev->value) != EV_SET) { - 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); - } - } -} - -void qemu_event_reset(QemuEvent *ev) -{ - assert(ev->initialized); - - /* - * 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) -{ - unsigned value; - - assert(ev->initialized); - - /* - * 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) { - /* - * 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); - - /* - * 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) { - return; - } - } - - /* - * 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); - } -} - struct QemuThreadData { /* Passed to win32_start_routine. */ void *(*start_routine)(void *); diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 6a0de33dd2b..1fb48be281a 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -26,9 +26,10 @@ #include "qemu/main-loop.h" #include "qemu/timer.h" #include "qemu/lockable.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/replay.h" -#include "sysemu/cpus.h" +#include "system/cpu-timers.h" +#include "exec/icount.h" +#include "system/replay.h" +#include "system/cpus.h" #ifdef CONFIG_POSIX #include @@ -182,7 +183,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 +213,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 +287,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) { @@ -419,10 +410,6 @@ static bool timer_mod_ns_locked(QEMUTimerList *timer_list, static void timerlist_rearm(QEMUTimerList *timer_list) { - /* Interrupt execution to force deadline recalculation. */ - if (icount_enabled() && timer_list->clock->type == QEMU_CLOCK_VIRTUAL) { - icount_start_warp_timer(); - } timerlist_notify(timer_list); } @@ -461,7 +448,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) { @@ -645,6 +632,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; @@ -675,3 +667,24 @@ 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; + 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); + + 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); + } + qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + + return clock; +} diff --git a/util/qht.c b/util/qht.c index 92c6b787593..208c2f4b329 100644 --- a/util/qht.c +++ b/util/qht.c @@ -367,7 +367,6 @@ void qht_map_lock_buckets__no_stale(struct qht *ht, struct qht_map **pmap) qht_map_lock_buckets(map); qht_unlock(ht); *pmap = map; - return; } /* diff --git a/util/qsp.c b/util/qsp.c index 2fe3764906c..6b783e2e7f8 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; @@ -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/range.c b/util/range.c index 098d9d2dc0b..f3f40098d54 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 e587bcc4831..fa32c942e4b 100644 --- a/util/rcu.c +++ b/util/rcu.c @@ -283,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(); } @@ -337,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(); } @@ -365,7 +365,7 @@ void drain_call_rcu(void) qatomic_dec(&in_drain_call_rcu); if (locked) { - qemu_mutex_lock_iothread(); + bql_lock(); } } @@ -409,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 494a3d924e6..0f19674f526 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) @@ -405,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) { @@ -418,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); @@ -425,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; @@ -452,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; @@ -480,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: @@ -496,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 00000000000..18f83eb4c65 --- /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/s390x_pci_mmio.c b/util/s390x_pci_mmio.c new file mode 100644 index 00000000000..5ab24fa474c --- /dev/null +++ b/util/s390x_pci_mmio.c @@ -0,0 +1,146 @@ +/* + * s390x PCI MMIO definitions + * + * Copyright 2025 IBM Corp. + * Author(s): Farhan Ali + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include +#include "qemu/s390x_pci_mmio.h" +#include "elf.h" + +union register_pair { + unsigned __int128 pair; + struct { + uint64_t even; + uint64_t odd; + }; +}; + +static bool is_mio_supported; + +static __attribute__((constructor)) void check_is_mio_supported(void) +{ + is_mio_supported = !!(qemu_getauxval(AT_HWCAP) & HWCAP_S390_PCI_MIO); +} + +static uint64_t s390x_pcilgi(const void *ioaddr, size_t len) +{ + union register_pair ioaddr_len = { .even = (uint64_t)ioaddr, + .odd = len }; + uint64_t val; + int cc; + + asm volatile( + /* pcilgi */ + ".insn rre,0xb9d60000,%[val],%[ioaddr_len]\n" + "ipm %[cc]\n" + "srl %[cc],28\n" + : [cc] "=d"(cc), [val] "=d"(val), + [ioaddr_len] "+d"(ioaddr_len.pair) :: "cc"); + + if (cc) { + val = -1ULL; + } + + return val; +} + +static void s390x_pcistgi(void *ioaddr, uint64_t val, size_t len) +{ + union register_pair ioaddr_len = {.even = (uint64_t)ioaddr, .odd = len}; + + asm volatile ( + /* pcistgi */ + ".insn rre,0xb9d40000,%[val],%[ioaddr_len]\n" + : [ioaddr_len] "+d" (ioaddr_len.pair) + : [val] "d" (val) + : "cc", "memory"); +} + +uint8_t s390x_pci_mmio_read_8(const void *ioaddr) +{ + uint8_t val = 0; + + if (is_mio_supported) { + val = s390x_pcilgi(ioaddr, sizeof(val)); + } else { + syscall(__NR_s390_pci_mmio_read, ioaddr, &val, sizeof(val)); + } + return val; +} + +uint16_t s390x_pci_mmio_read_16(const void *ioaddr) +{ + uint16_t val = 0; + + if (is_mio_supported) { + val = s390x_pcilgi(ioaddr, sizeof(val)); + } else { + syscall(__NR_s390_pci_mmio_read, ioaddr, &val, sizeof(val)); + } + return val; +} + +uint32_t s390x_pci_mmio_read_32(const void *ioaddr) +{ + uint32_t val = 0; + + if (is_mio_supported) { + val = s390x_pcilgi(ioaddr, sizeof(val)); + } else { + syscall(__NR_s390_pci_mmio_read, ioaddr, &val, sizeof(val)); + } + return val; +} + +uint64_t s390x_pci_mmio_read_64(const void *ioaddr) +{ + uint64_t val = 0; + + if (is_mio_supported) { + val = s390x_pcilgi(ioaddr, sizeof(val)); + } else { + syscall(__NR_s390_pci_mmio_read, ioaddr, &val, sizeof(val)); + } + return val; +} + +void s390x_pci_mmio_write_8(void *ioaddr, uint8_t val) +{ + if (is_mio_supported) { + s390x_pcistgi(ioaddr, val, sizeof(val)); + } else { + syscall(__NR_s390_pci_mmio_write, ioaddr, &val, sizeof(val)); + } +} + +void s390x_pci_mmio_write_16(void *ioaddr, uint16_t val) +{ + if (is_mio_supported) { + s390x_pcistgi(ioaddr, val, sizeof(val)); + } else { + syscall(__NR_s390_pci_mmio_write, ioaddr, &val, sizeof(val)); + } +} + +void s390x_pci_mmio_write_32(void *ioaddr, uint32_t val) +{ + if (is_mio_supported) { + s390x_pcistgi(ioaddr, val, sizeof(val)); + } else { + syscall(__NR_s390_pci_mmio_write, ioaddr, &val, sizeof(val)); + } +} + +void s390x_pci_mmio_write_64(void *ioaddr, uint64_t val) +{ + if (is_mio_supported) { + s390x_pcistgi(ioaddr, val, sizeof(val)); + } else { + syscall(__NR_s390_pci_mmio_write, ioaddr, &val, sizeof(val)); + } +} diff --git a/util/thread-context.c b/util/thread-context.c index 2bc7883b9e0..0146154fa56 100644 --- a/util/thread-context.c +++ b/util/thread-context.c @@ -273,7 +273,7 @@ static void thread_context_instance_complete(UserCreatable *uc, Error **errp) } } -static void thread_context_class_init(ObjectClass *oc, void *data) +static void thread_context_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); @@ -319,7 +319,7 @@ static const TypeInfo thread_context_info = { .instance_size = sizeof(ThreadContext), .instance_init = thread_context_instance_init, .instance_finalize = thread_context_instance_finalize, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/util/thread-pool.c b/util/thread-pool.c index e3d8292d144..d2ead6b7285 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" @@ -22,9 +23,9 @@ #include "block/thread-pool.h" #include "qemu/main-loop.h" -static void do_spawn_thread(ThreadPool *pool); +static void do_spawn_thread(ThreadPoolAio *pool); -typedef struct ThreadPoolElement ThreadPoolElement; +typedef struct ThreadPoolElementAio ThreadPoolElementAio; enum ThreadState { THREAD_QUEUED, @@ -32,9 +33,9 @@ enum ThreadState { THREAD_DONE, }; -struct ThreadPoolElement { +struct ThreadPoolElementAio { BlockAIOCB common; - ThreadPool *pool; + ThreadPoolAio *pool; ThreadPoolFunc *func; void *arg; @@ -46,13 +47,13 @@ struct ThreadPoolElement { int ret; /* Access to this list is protected by lock. */ - QTAILQ_ENTRY(ThreadPoolElement) reqs; + QTAILQ_ENTRY(ThreadPoolElementAio) reqs; /* This list is only written by the thread pool's mother thread. */ - QLIST_ENTRY(ThreadPoolElement) all; + QLIST_ENTRY(ThreadPoolElementAio) all; }; -struct ThreadPool { +struct ThreadPoolAio { AioContext *ctx; QEMUBH *completion_bh; QemuMutex lock; @@ -61,10 +62,10 @@ struct ThreadPool { QEMUBH *new_thread_bh; /* The following variables are only accessed from one AioContext. */ - QLIST_HEAD(, ThreadPoolElement) head; + QLIST_HEAD(, ThreadPoolElementAio) head; /* The following variables are protected by lock. */ - QTAILQ_HEAD(, ThreadPoolElement) request_list; + QTAILQ_HEAD(, ThreadPoolElementAio) request_list; int cur_threads; int idle_threads; int new_threads; /* backlog of threads we need to create */ @@ -75,14 +76,14 @@ struct ThreadPool { static void *worker_thread(void *opaque) { - ThreadPool *pool = opaque; + ThreadPoolAio *pool = opaque; qemu_mutex_lock(&pool->lock); pool->pending_threads--; do_spawn_thread(pool); while (pool->cur_threads <= pool->max_threads) { - ThreadPoolElement *req; + ThreadPoolElementAio *req; int ret; if (QTAILQ_EMPTY(&pool->request_list)) { @@ -130,7 +131,7 @@ static void *worker_thread(void *opaque) return NULL; } -static void do_spawn_thread(ThreadPool *pool) +static void do_spawn_thread(ThreadPoolAio *pool) { QemuThread t; @@ -147,14 +148,14 @@ static void do_spawn_thread(ThreadPool *pool) static void spawn_thread_bh_fn(void *opaque) { - ThreadPool *pool = opaque; + ThreadPoolAio *pool = opaque; qemu_mutex_lock(&pool->lock); do_spawn_thread(pool); qemu_mutex_unlock(&pool->lock); } -static void spawn_thread(ThreadPool *pool) +static void spawn_thread(ThreadPoolAio *pool) { pool->cur_threads++; pool->new_threads++; @@ -172,8 +173,10 @@ static void spawn_thread(ThreadPool *pool) static void thread_pool_completion_bh(void *opaque) { - ThreadPool *pool = opaque; - ThreadPoolElement *elem, *next; + ThreadPoolAio *pool = opaque; + ThreadPoolElementAio *elem, *next; + + defer_call_begin(); /* cb() may use defer_call() to coalesce work */ restart: QLIST_FOREACH_SAFE(elem, &pool->head, all, next) { @@ -181,8 +184,8 @@ static void thread_pool_completion_bh(void *opaque) continue; } - trace_thread_pool_complete(pool, elem, elem->common.opaque, - elem->ret); + trace_thread_pool_complete_aio(pool, elem, elem->common.opaque, + elem->ret); QLIST_REMOVE(elem, all); if (elem->common.cb) { @@ -208,14 +211,16 @@ static void thread_pool_completion_bh(void *opaque) qemu_aio_unref(elem); } } + + defer_call_end(); } static void thread_pool_cancel(BlockAIOCB *acb) { - ThreadPoolElement *elem = (ThreadPoolElement *)acb; - ThreadPool *pool = elem->pool; + ThreadPoolElementAio *elem = (ThreadPoolElementAio *)acb; + ThreadPoolAio *pool = elem->pool; - trace_thread_pool_cancel(elem, elem->common.opaque); + trace_thread_pool_cancel_aio(elem, elem->common.opaque); QEMU_LOCK_GUARD(&pool->lock); if (elem->state == THREAD_QUEUED) { @@ -228,25 +233,17 @@ 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), + .aiocb_size = sizeof(ThreadPoolElementAio), .cancel_async = thread_pool_cancel, - .get_aio_context = thread_pool_get_aio_context, }; BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, BlockCompletionFunc *cb, void *opaque) { - ThreadPoolElement *req; + ThreadPoolElementAio *req; AioContext *ctx = qemu_get_current_aio_context(); - ThreadPool *pool = aio_get_thread_pool(ctx); + ThreadPoolAio *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()); @@ -259,7 +256,7 @@ BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, QLIST_INSERT_HEAD(&pool->head, req, all); - trace_thread_pool_submit(pool, req, arg); + trace_thread_pool_submit_aio(pool, req, arg); qemu_mutex_lock(&pool->lock); if (pool->idle_threads == 0 && pool->cur_threads < pool->max_threads) { @@ -293,12 +290,7 @@ int coroutine_fn thread_pool_submit_co(ThreadPoolFunc *func, void *arg) return tpc.ret; } -void thread_pool_submit(ThreadPoolFunc *func, void *arg) -{ - thread_pool_submit_aio(func, arg, NULL, NULL); -} - -void thread_pool_update_params(ThreadPool *pool, AioContext *ctx) +void thread_pool_update_params(ThreadPoolAio *pool, AioContext *ctx) { qemu_mutex_lock(&pool->lock); @@ -325,7 +317,7 @@ void thread_pool_update_params(ThreadPool *pool, AioContext *ctx) qemu_mutex_unlock(&pool->lock); } -static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx) +static void thread_pool_init_one(ThreadPoolAio *pool, AioContext *ctx) { if (!ctx) { ctx = qemu_get_aio_context(); @@ -345,14 +337,14 @@ static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx) thread_pool_update_params(pool, ctx); } -ThreadPool *thread_pool_new(AioContext *ctx) +ThreadPoolAio *thread_pool_new_aio(AioContext *ctx) { - ThreadPool *pool = g_new(ThreadPool, 1); + ThreadPoolAio *pool = g_new(ThreadPoolAio, 1); thread_pool_init_one(pool, ctx); return pool; } -void thread_pool_free(ThreadPool *pool) +void thread_pool_free_aio(ThreadPoolAio *pool) { if (!pool) { return; @@ -382,3 +374,122 @@ void thread_pool_free(ThreadPool *pool) qemu_mutex_destroy(&pool->lock); g_free(pool); } + +struct ThreadPool { + GThreadPool *t; + size_t cur_work; + QemuMutex cur_work_lock; + QemuCond all_finished_cond; +}; + +typedef struct { + ThreadPoolFunc *func; + void *opaque; + GDestroyNotify opaque_destroy; +} ThreadPoolElement; + +static void thread_pool_func(gpointer data, gpointer user_data) +{ + ThreadPool *pool = user_data; + g_autofree ThreadPoolElement *el = data; + + el->func(el->opaque); + + if (el->opaque_destroy) { + el->opaque_destroy(el->opaque); + } + + QEMU_LOCK_GUARD(&pool->cur_work_lock); + + assert(pool->cur_work > 0); + pool->cur_work--; + + if (pool->cur_work == 0) { + qemu_cond_signal(&pool->all_finished_cond); + } +} + +ThreadPool *thread_pool_new(void) +{ + ThreadPool *pool = g_new(ThreadPool, 1); + + pool->cur_work = 0; + qemu_mutex_init(&pool->cur_work_lock); + qemu_cond_init(&pool->all_finished_cond); + + pool->t = g_thread_pool_new(thread_pool_func, pool, 0, TRUE, NULL); + /* + * g_thread_pool_new() can only return errors if initial thread(s) + * creation fails but we ask for 0 initial threads above. + */ + assert(pool->t); + + return pool; +} + +void thread_pool_free(ThreadPool *pool) +{ + /* + * With _wait = TRUE this effectively waits for all + * previously submitted work to complete first. + */ + g_thread_pool_free(pool->t, FALSE, TRUE); + + qemu_cond_destroy(&pool->all_finished_cond); + qemu_mutex_destroy(&pool->cur_work_lock); + + g_free(pool); +} + +void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, + void *opaque, GDestroyNotify opaque_destroy) +{ + ThreadPoolElement *el = g_new(ThreadPoolElement, 1); + + el->func = func; + el->opaque = opaque; + el->opaque_destroy = opaque_destroy; + + WITH_QEMU_LOCK_GUARD(&pool->cur_work_lock) { + pool->cur_work++; + } + + /* + * Ignore the return value since this function can only return errors + * if creation of an additional thread fails but even in this case the + * provided work is still getting queued (just for the existing threads). + */ + g_thread_pool_push(pool->t, el, NULL); +} + +void thread_pool_submit_immediate(ThreadPool *pool, ThreadPoolFunc *func, + void *opaque, GDestroyNotify opaque_destroy) +{ + thread_pool_submit(pool, func, opaque, opaque_destroy); + thread_pool_adjust_max_threads_to_work(pool); +} + +void thread_pool_wait(ThreadPool *pool) +{ + QEMU_LOCK_GUARD(&pool->cur_work_lock); + + while (pool->cur_work > 0) { + qemu_cond_wait(&pool->all_finished_cond, + &pool->cur_work_lock); + } +} + +bool thread_pool_set_max_threads(ThreadPool *pool, + int max_threads) +{ + assert(max_threads > 0); + + return g_thread_pool_set_max_threads(pool->t, max_threads, NULL); +} + +bool thread_pool_adjust_max_threads_to_work(ThreadPool *pool) +{ + QEMU_LOCK_GUARD(&pool->cur_work_lock); + + return thread_pool_set_max_threads(pool, pool->cur_work); +} diff --git a/util/throttle.c b/util/throttle.c index 81f247a8d18..9582899da3e 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 2b49d532cee..5b5c22afd88 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 49a4962e188..bd8f25fb592 100644 --- a/util/trace-events +++ b/util/trace-events @@ -14,9 +14,9 @@ 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" -thread_pool_complete(void *pool, void *req, void *opaque, int ret) "pool %p req %p opaque %p ret %d" -thread_pool_cancel(void *req, void *opaque) "req %p opaque %p" +thread_pool_submit_aio(void *pool, void *req, void *opaque) "pool %p req %p opaque %p" +thread_pool_complete_aio(void *pool, void *req, void *opaque, int ret) "pool %p req %p opaque %p ret %d" +thread_pool_cancel_aio(void *req, void *opaque) "req %p opaque %p" # buffer.c buffer_resize(const char *buf, size_t olen, size_t len) "%s: old %zd, new %zd" diff --git a/util/uri.c b/util/uri.c deleted file mode 100644 index dcb33052364..00000000000 --- a/util/uri.c +++ /dev/null @@ -1,2313 +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, see . - * - * 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 fdff4867e8b..2396104f23e 100644 --- a/util/userfaultfd.c +++ b/util/userfaultfd.c @@ -18,7 +18,6 @@ #include #include #include -#include typedef enum { UFFD_UNINITIALIZED = 0, @@ -241,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 @@ -260,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; @@ -274,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 @@ -290,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; @@ -307,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 @@ -321,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; @@ -356,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 b1108dde78d..234619dd5e6 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 f8bab46c68f..fdff042ab46 100644 --- a/util/vfio-helpers.c +++ b/util/vfio-helpers.c @@ -16,7 +16,7 @@ #include "qapi/error.h" #include "exec/ramlist.h" #include "exec/cpu-common.h" -#include "exec/memory.h" +#include "system/memory.h" #include "trace.h" #include "qemu/error-report.h" #include "standard-headers/linux/pci_regs.h" diff --git a/util/vhost-user-server.c b/util/vhost-user-server.c index b4b6bf30a21..b19229074ad 100644 --- a/util/vhost-user-server.c +++ b/util/vhost-user-server.c @@ -65,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]); } @@ -132,8 +144,7 @@ vu_message_read(VuDev *vu_dev, int conn_fd, VhostUserMsg *vmsg) qio_channel_yield(ioc, G_IO_IN); server->in_qio_channel_yield = false; } else { - /* Wait until attached to an AioContext again */ - qemu_coroutine_yield(); + return false; } continue; } else { @@ -201,8 +212,16 @@ 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 (vhost_user_server_has_in_flight(server)) { @@ -278,7 +297,7 @@ 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); @@ -353,11 +372,7 @@ static void vu_accept(QIONetListener *listener, QIOChannelSocket *sioc, qio_channel_set_follow_coroutine_ctx(server->ioc, true); - server->co_trip = qemu_coroutine_create(vu_client_trip, server); - - aio_context_acquire(server->ctx); vhost_user_server_attach_aio_context(server, server->ctx); - aio_context_release(server->ctx); } /* server->ctx acquired by caller */ @@ -413,8 +428,25 @@ void vhost_user_server_attach_aio_context(VuServer *server, AioContext *ctx) NULL, NULL, vu_fd_watch); } - assert(!server->in_qio_channel_yield); - 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 */ diff --git a/util/yank.c b/util/yank.c index abf47c346d1..eaac50539c5 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;